File Coverage

blib/lib/Tree/Ops.pm
Criterion Covered Total %
statement 431 436 98.8
branch 140 172 81.4
condition 28 40 70.0
subroutine 104 104 100.0
pod 75 76 98.6
total 778 828 93.9


line stmt bran cond sub pod time code
1             #!/usr/bin/perl -I/home/phil/perl/cpan/DataTableText/lib
2             #-------------------------------------------------------------------------------
3             # Tree operations
4             # Philip R Brenan at gmail dot com, Appa Apps Ltd Inc., 2020
5             #-------------------------------------------------------------------------------
6             # podDocumentation
7             package Tree::Ops;
8             our $VERSION = 20200725;
9             require v5.26;
10 1     1   1242 use warnings FATAL => qw(all);
  1         8  
  1         37  
11 1     1   9 use strict;
  1         2  
  1         33  
12 1     1   5 use Carp;
  1         2  
  1         95  
13 1     1   563 use Data::Dump qw(dump);
  1         8065  
  1         62  
14 1     1   3985 use Data::Table::Text qw(:all);
  1         148412  
  1         4370  
15 1     1   35 use feature qw(current_sub say);
  1         6  
  1         250  
16 1     1   1112 use experimental qw(smartmatch);
  1         4780  
  1         6  
17              
18             my $logFile = q(/home/phil/z/z/z/zzz.txt); # Log printed results if developing
19              
20             #D1 Build # Create a tree. There is no implicit ordering applied to the tree, the relationships between parents and children within the tree are as established by the user and can be reorganized at will using the methods in this module.
21              
22             sub new(;$$) #S Create a new child optionally recording the specified key or value.
23 263     263 1 809 {my ($key, $value) = @_; # Key, value
24 263         706 genHash(__PACKAGE__, # Child in the tree.
25             children => [], # Children of this child.
26             key => $key, # Key for this child - any thing that can be compared with the L operator.
27             value => $value, # Value for this child.
28             parent => undef, # Parent for this child.
29             lastChild => undef, # Last active child chain - enables us to find the currently open scope from the start if the tree.
30             );
31             }
32              
33             sub activeScope($) # Locate the active scope in a tree.
34 426     426 1 592 {my ($tree) = @_; # Tree
35 426         518 my $active; # Latest active child
36 426         832 for(my $l = $tree; $l; $l = $l->lastChild) {$active = $l} # Skip down edge of parse tree to deepest active child.
  1331         24384  
37 426         2065 $active
38             }
39              
40             sub setParentOfChild($$) #P Set the parent of a child and return the child.
41 227     227 1 395 {my ($child, $parent) = @_; # Child, parent
42 227         3655 $child->parent = $parent; # Parent child
43 227         1182 $child
44             }
45              
46             sub open($;$$) # Add a child and make it the currently active scope into which new children will be added.
47 213     213 1 359 {my ($tree, $key, $value) = @_; # Tree, key, value to be recorded in the interior child being opened
48 213         377 my $parent = activeScope $tree; # Active parent
49 213         466 my $child = new $key, $value; # New child
50 213         16940 push $parent->children->@*, $child; # Place new child last under parent
51 213         4279 $parent->lastChild = $child; # Make child active
52 213         950 setParentOfChild $child, $parent # Parent child
53             }
54              
55             sub close($) # Close the current scope returning to the previous scope.
56 210     210 1 328 {my ($tree) = @_; # Tree
57 210         320 my $parent = activeScope $tree; # Locate active scope
58 210 100       3433 delete $parent->parent->{lastChild} if $parent->parent; # Close scope
59 210         4795 $parent
60             }
61              
62             sub single($;$$) # Add one child in the current scope.
63 129     129 1 241 {my ($tree, $key, $value) = @_; # Tree, key, value to be recorded in the child being created
64 129         289 $tree->open($key, $value); # Open scope
65 129         255 $tree->close; # Close scope immediately
66             }
67              
68             sub include($$) # Include the specified tree in the currently open scope.
69 1     1 1 5 {my ($tree, $include) = @_; # Tree being built, tree to include
70 1         4 my $parent = activeScope $tree; # Active parent
71 1         18 my $n = new $include->key, $include->value; # New intermediate child
72 1         136 $n->children = $include->children; # Include children
73 1         37 $n->parent = $parent; # Parent new node
74 1         24 $parent->putLast($n) # Include node
75             }
76              
77             sub fromLetters($) # Create a tree from a string of letters returning the children created in alphabetic order - useful for testing.
78 20     20 1 53 {my ($letters) = @_; # String of letters and ( ).
79 20         59 my $t = new(my $s = 'a');
80 20         1493 my @l = split //, $letters;
81              
82 20         38 my @c; # Last letter seen
83 20         89 for my $l(split(//, $letters), '') # Each letter
84 371         582 {my $c = shift @c; # Last letter
85 371 50       782 if ($l eq '(') {$t->open ($c) if $c} # Open new scope
  77 100       197  
    100          
86 77 100       234 elsif ($l eq ')') {$t->single($c) if $c; $t->close} # Close scope
  77         154  
87 217 100       451 else {$t->single($c) if $c; @c = $l} # Save current letter as last letter
  217         524  
88             }
89              
90 20         95 sort {$a->key cmp $b->key} $t->by # Sorted results
  514         10169  
91             }
92              
93             #D1 Navigation # Navigate through a tree.
94              
95             sub first($) # Get the first child under the specified parent.
96 84     84 1 256 {my ($parent) = @_; # Parent
97 84         1418 $parent->children->[0]
98             }
99              
100             sub last($) # Get the last child under the specified parent.
101 68     68 1 168 {my ($parent) = @_; # Parent
102 68         1116 $parent->children->[-1]
103             }
104              
105             sub indexOfChildInParent($) #P Get the index of a child within the specified parent.
106 152     152 1 282 {my ($child) = @_; # Child
107 152 50       2575 return undef unless my $parent = $child->parent; # Parent
108 152         2959 my $c = $parent->children; # Siblings
109 152 100       820 for(keys @$c) {return $_ if $$c[$_] == $child} # Locate child and return index
  295         1440  
110             undef # Root has no index
111 0         0 }
112              
113             sub next($) # Get the next sibling following the specified child.
114 54     54 1 103 {my ($child) = @_; # Child
115 54 100       880 return undef unless my $parent = $child->parent; # Parent
116 50         998 my $c = $parent->children; # Siblings
117 50 100 66     377 return undef if @$c == 0 or $$c[-1] == $child; # No next child
118 49         106 $$c[+1 + indexOfChildInParent $child] # Next child
119             }
120              
121             sub prev($) # Get the previous sibling of the specified child.
122 64     64 1 134 {my ($child) = @_; # Child
123 64 100       1069 return undef unless my $parent = $child->parent; # Parent
124 56         1082 my $c = $parent->children; # Siblings
125 56 100 66     424 return undef if @$c == 0 or $$c[0] == $child; # No previous child
126 55         130 $$c[-1 + indexOfChildInParent $child] # Previous child
127             }
128              
129             sub firstMost($) # Return the first most descendant child in the tree starting at this parent or else return B if this parent has no children.
130 19     19 1 89 {my ($parent) = @_; # Parent
131 19         37 my $f;
132 19         38 for(my $p = $parent; $p; $p = $p->first) {$f = $p} # Go first most
  44         188  
133 19         180 $f
134             }
135              
136             sub nextMost($) # Return the next child with no children, i.e. the next leaf of the tree, else return B if there is no such child.
137 20     20 1 48 {my ($child) = @_; # Current leaf
138 20 100       344 return firstMost $child if $child->children->@*; # First most child if we are not starting on a child with no children - i.e. on a leaf.
139 9         50 my $p = $child; # Traverse upwards and then right
140 9         20 $p = $p->parent while $p->isLast; # Traverse upwards
141 9 100       68 return undef unless $p = $p->next; # Traverse right else we are at the root
142 7         18 firstMost $p # First most child
143             }
144              
145             sub prevMost($) # Return the previous child with no children, i.e. the previous leaf of the tree, else return B if there is no such child.
146 21     21 1 49 {my ($child) = @_; # Current leaf
147 21         35 my $p = $child; # Traverse upwards and then left
148 21         45 $p = $p->parent while $p->isFirst; # Traverse upwards
149 21 100       166 return undef unless $p = $p->prev; # Traverse left else we are at the root
150 15         35 lastMost $p # Last most child
151             }
152              
153             sub lastMost($) # Return the last most descendant child in the tree starting at this parent or else return B if this parent has no children.
154 17     17 1 30 {my ($parent) = @_; # Parent
155 17         24 my $f;
156 17         42 for(my $p = $parent; $p; $p = $p->last) {$f = $p} # Go last most
  32         124  
157 17         188 $f
158             }
159              
160             sub topMost($) # Return the top most parent in the tree containing the specified child.
161 1     1 1 4 {my ($child) = @_; # Child
162 1 100       5 for(my $p = $child; $p;) {return $p unless my $q = $p->parent; $p = $q} # Go up
  4         67  
  3         17  
163 0         0 confess "Child required";
164             }
165              
166             sub mostRecentCommonAncestor($$) # Find the most recent common ancestor of the specified children.
167 2     2 1 8 {my ($first, $second) = @_; # First child, second child
168 2 50       7 return $first if $first == $second; # Same first and second child
169 2         7 my @f = context $first; # Context of first child
170 2         5 my @s = context $second; # Context of second child
171 2   33     4 my $c; $c = pop @f, pop @s while @f and @s and $f[-1] == $s[-1]; # Remove common ancestors
  2   66     25  
172 2         11 $c
173             }
174              
175             sub go($@) # Return the child at the end of the path starting at the specified parent. A path is a list of zero based children numbers. Return B if the path is not valid.
176 10     10 1 30 {my ($parent, @path) = @_; # Parent, list of zero based children numbers
177 10         15 my $p = $parent; # Start
178 10 50       12 my $q; defined($q = $p->children->[$_]) ? $p = $q : return undef for @path; # Down # Same first and second child
  10         157  
179 10         381 $p
180             }
181              
182             #D1 Location # Verify the current location.
183              
184             sub context($) # Get the context of the current child.
185 21     21 1 34 {my ($child) = @_; # Child
186 21         31 my @c; # Context
187 21         44 for(my $c = $child; $c; $c = $c->parent) {push @c, $c} # Walk up
  88         1679  
188             @c
189 21         147 }
190              
191             sub isFirst($) # Return the specified child if that child is first under its parent, else return B.
192 80     80 1 691 {my ($child) = @_; # Child
193 80 100       1317 return undef unless my $parent = $child->parent; # Parent
194 72 100       1438 $parent->children->[0] == $child ? $child : undef # There will be at least one child
195             }
196              
197             sub isLast($) # Return the specified child if that child is last under its parent, else return B.
198 64     64 1 565 {my ($child) = @_; # Child
199 64 100       1020 return undef unless my $parent = $child->parent; # Parent
200 60         1172 my $c = $parent->children;
201 60 100       1106 $parent->children->[-1] == $child ? $child : undef # There will be at least one child
202             }
203              
204             sub isTop($) # Return the specified parent if that parent is the top most parent in the tree.
205 2     2 1 6 {my ($parent) = @_; # Parent
206 2 100       38 $parent->parent ? undef : $parent
207             }
208              
209             sub singleChildOfParent($) # Return the only child of this parent if the parent has an only child, else B
210 1     1 1 3 {my ($parent) = @_; # Parent
211 1 50       18 $parent->children->@* == 1 ? $parent->children->[0] : undef # Return only child if it exists
212             }
213              
214             sub empty($) # Return the specified parent if it has no children else B
215 2     2 1 5 {my ($parent) = @_; # Parent
216 2 100       37 $parent->children->@* == 0 ? $parent : undef
217             }
218              
219             #D1 Put # Insert children into a tree.
220              
221             sub putFirst($$) # Place a new child first under the specified parent and return the child.
222 4     4 1 77 {my ($parent, $child) = @_; # Parent, child
223 4         69 unshift $parent->children->@*, $child; # Place child
224 4         28 setParentOfChild $child, $parent # Parent child
225             }
226              
227             sub putLast($$) # Place a new child last under the specified parent and return the child.
228 6     6 1 93 {my ($parent, $child) = @_; # Parent, child
229 6         106 push $parent->children->@*, $child; # Place child
230 6         41 setParentOfChild $child, $parent # Parent child
231             }
232              
233             sub putNext($$) # Place a new child after the specified child.
234 2     2 1 114 {my ($child, $new) = @_; # Existing child, new child
235 2 50       8 return undef unless defined(my $i = indexOfChildInParent $child); # Locate child within parent
236 2         43 splice $child->parent->children->@*, $i, 1, $child, $new; # Place new child
237 2         87 setParentOfChild $new, $child->parent # Parent child
238             }
239              
240             sub putPrev($$) # Place a new child before the specified child.
241 2     2 1 90 {my ($child, $new) = @_; # Child, new child
242 2 50       6 return undef unless defined(my $i = indexOfChildInParent($child)); # Locate child within parent
243 2         46 splice $child->parent->children->@*, $i, 1, $new, $child; # Place new child
244 2         104 setParentOfChild $new, $child->parent # Parent child
245             }
246              
247             #D1 Steps # Move the start or end of a scope forwards or backwards as suggested by Alex Monroe.
248              
249             sub step($) # Make the first child of the specified parent the parents previous sibling and return the parent. In effect this moves the start of the parent one step forwards.
250 1     1 1 3 {my ($parent) = @_; # Parent
251 1 50       4 return undef unless my $f = $parent->first; # First child
252 1         11 putPrev $parent, cut $f; # Place first child
253 1         17 $parent
254             }
255              
256             sub stepEnd($) # Make the next sibling of the specified parent the parents last child and return the parent. In effect this moves the end of the parent one step forwards.
257 3     3 1 9 {my ($parent) = @_; # Parent
258 3 50       10 return undef unless my $n = $parent->next; # Next sibling
259 3         12 putLast $parent, cut $n; # Place next sibling as first child
260 3         23 $parent
261             }
262              
263             sub stepBack # Make the previous sibling of the specified parent the parents first child and return the parent. In effect this moves the start of the parent one step backwards.
264 2     2 1 9 {my ($parent) = @_; # Parent
265 2 50       17 return undef unless my $p = $parent->prev; # Previous sibling
266 2         15 putFirst $parent, cut $p; # Place previous sibling as first child
267 2         33 $parent
268             }
269              
270             sub stepEndBack # Make the last child of the specified parent the parents next sibling and return the parent. In effect this moves the end of the parent one step backwards.
271 1     1 1 7 {my ($parent) = @_; # Parent
272 1 50       3 return undef unless my $l = $parent->last; # Last child sibling
273 1         25 putNext $parent, cut $l; # Place last child as first sibling
274 1         16 $parent
275             }
276              
277             #D1 Edit # Edit a tree in situ.
278              
279             sub cut($) # Cut out a child and all its content and children, return it ready for reinsertion else where.
280 10     10 1 21 {my ($child) = @_; # Child
281 10 50       175 return $child unless my $parent = $child->parent; # The whole tree
282 10         201 splice $parent->children->@*, indexOfChildInParent($child), 1; # Remove child
283 10         64 $child
284             }
285              
286             sub dup($) # Duplicate a specified parent and all its descendants returning the root of the resulting tree.
287 1     1 1 4 {my ($parent) = @_; # Parent
288              
289             sub # Duplicate a child
290 8     8   101 {my ($old) = @_; # Existing child
291 8         194 my $new = new $old->key, $old->value; # New child
292 8         633 push $new->children->@*, __SUB__->($_) for $old->children->@*; # Duplicate children of child
293 8         100 $new
294 1         7 }->($parent) # Start duplication at parent
295             }
296              
297             sub transcribe($) # Duplicate a specified parent and all its descendants recording the mapping in a temporary {transcribed} field in the tree being transcribed. Returns the root parent of the tree being duplicated.
298 1     1 1 3 {my ($parent) = @_; # Parent
299              
300             sub # Duplicate a child
301 8     8   125 {my ($old) = @_; # Existing child
302 8         136 my $new = new $old->key, $old->value; # New child
303 8         498 $old->{transcribedTo} = $new; # To where we went
304 8         11 $new->{transcribedFrom} = $old; # From where we came
305 8         156 push $new->children->@*, __SUB__->($_) for $old->children->@*; # Duplicate children of child and record transcription
306 8         113 $new
307 1         8 }->($parent) # Start duplication at parent
308             }
309              
310             sub unwrap($) # Unwrap the specified child and return that child.
311 5     5 1 40 {my ($child) = @_; # Child
312 5 50       13 return undef unless defined(my $i = indexOfChildInParent $child); # Locate child within parent
313 5         86 my $parent = $child->parent; # Parent
314 5         96 $_->parent = $parent for $child->children->@*; # Reparent unwrapped children of child
315 5         154 delete $child ->{parent}; # Remove parent of unwrapped child
316 5         82 splice $parent->children->@*, $i, 1, $child->children->@*; # Remove child
317 5         94 $parent
318             }
319              
320             sub wrap($;$$) # Wrap the specified child with a new parent and return the new parent optionally setting its L[key] and L[value].
321 5     5 1 115 {my ($child, $key, $value) = @_; # Child to wrap, optional key, optional value
322 5 50       12 return undef unless defined(my $i = indexOfChildInParent $child); # Locate child within existing parent
323 5         87 my $parent = $child->parent; # Existing parent
324 5         24 my $new = new $key, $value; # Create new parent
325 5         410 $new->parent = $parent; # Parent new parent
326 5         121 $new->children = [$child]; # Set children for new parent
327 5         115 splice $parent->children->@*, $i, 1, $new; # Place new parent in existing parent
328 5         121 $child->parent = $new # Reparent child to new parent
329             }
330              
331             sub wrapChildren($;$$) # Wrap the children of the specified parent with a new intermediate parent that becomes the child of the specified parent, optionally setting the L[key] and the L[value] for the new parent. Return the new parent.
332 1     1 1 4 {my ($parent, $key, $value) = @_; # Child to wrap, optional key for new wrapping parent, optional value for new wrapping parent
333 1         3 my $new = new $key, $value; # Create new parent
334 1         90 $new->children = $parent->children; # Move children;
335 1         27 $parent->children = [$new]; # Grand parent
336 1         22 $new->parent = $parent; # Parent new parent
337 1         20 $_->parent = $new for $new->children->@*; # Reparent new children
338 1         64 $new # New parent
339             }
340              
341             sub merge($) # Unwrap the children of the specified parent with the whose L[key] fields L that of their parent. Returns the specified parent regardless.
342 1     1 1 5 {my ($parent) = @_; # Merging parent
343 1         20 for my $c($parent->children->@*) # Children of parent
344 4 100       80 {unwrap $c if $c->key ~~ $parent->key; # Unwrap child if like parent
345             }
346             $parent
347 1         22 }
348              
349             sub mergeLikePrev($) # Merge the preceding sibling of the specified child if that sibling exists and the L[key] data of the two siblings L. Returns the specified child regardless. From a proposal made by Micaela Monroe.
350 1     1 1 9 {my ($child) = @_; # Child
351 1 50       4 return $child unless my $prev = $child->prev; # No merge possible if child is first
352 1         4 $child->putFirst($prev->cut)->unwrap # Children to be merged
353             }
354              
355             sub mergeLikeNext($) # Merge the following sibling of the specified child if that sibling exists and the L[key] data of the two siblings L. Returns the specified child regardless. From a proposal made by Micaela Monroe.
356 1     1 1 9 {my ($child) = @_; # Child
357 1 50       5 return $child unless my $next = $child->next; # No merge possible if child is last
358 1         4 $child->putLast($next->cut)->unwrap # Children to be merged
359             }
360              
361             sub split($) # Make the specified parent a grandparent of each of its children by interposing a copy of the specified parent between the specified parent and each of its children. Return the specified parent.
362 1     1 1 5 {my ($parent) = @_; # Parent to make into a grand parent
363 1         20 wrap $_, $parent->key for $parent->children->@*; # Grandparent each child
364 1         25 $parent
365             }
366              
367             #D1 Traverse # Traverse a tree.
368              
369             sub by($;$) # Traverse a tree in post-order to process each child with the specified sub and return an array of the results of processing each child. If no sub sub is specified, the children are returned in tree order.
370 24     24 1 57 {my ($tree, $sub) = @_; # Tree, optional sub to process each child
371 24   100 239   177 $sub //= sub{@_}; # Default sub
  239         501  
372              
373 24         35 my @r; # Results
374             sub # Traverse
375 262     262   3096 {my ($child) = @_; # Child
376 262         4323 __SUB__->($_) for $child->children->@*; # Children of child
377 262         2852 push @r, &$sub($child); # Process child saving result
378 24         101 }->($tree); # Start at root of tree
379              
380             @r
381 24         793 }
382              
383             sub select($$) # Select matching children in a tree in post-order. A child can be selected via named value, array of values, a hash of values, a regular expression or a sub reference.
384 7     7 1 51 {my ($tree, $select) = @_; # Tree, method to select a child
385 7         16 my $ref = ref $select; # Selector type
386             my $sel = # Selection method
387 10     10   44 $ref =~ m(array)i ? sub{grep{$_[0] eq $_} @$select} : # Array
  20         46  
388 10     10   55 $ref =~ m(hash)i ? sub{$$select{$_[0]}} : # Hash
389 17     17   160 $ref =~ m(exp)i ? sub{$_[0] =~ m($select)} : # Regular expression
390 17     17   311 $ref =~ m(code)i ? sub{&$select($_[0])} : # Sub
391 7 100   7   68 sub{$_[0] eq $select}; # Scalar
  7 100       39  
    100          
    100          
392 7         13 my @s; # Selection
393              
394             sub # Traverse
395 61     61   344 {my ($child) = @_; # Child
396 61 100       993 push @s, $child if &$sel($child->key); # Select child if it matches
397 61         1011 __SUB__->($_) for $child->children->@*; # Each child
398 7         31 }->($tree); # Start at root
399              
400             @s
401 7         181 }
402              
403             #D1 Partitions # Various partitions of the tree
404              
405             sub leaves($) # The set of all children without further children, i.e. each leaf of the tree.
406 2     2 1 5 {my ($tree) = @_; # Tree
407 2         4 my @leaves; # Leaves
408             sub # Traverse
409 20     20   25 {my ($child) = @_; # Child
410 20 100       327 if (my @c = $child->children->@*) # Children of child
411 11         81 {__SUB__->($_) for @c; # Process children of child
412             }
413             else
414 9         60 {push @leaves, $child; # Save leaf
415             }
416 2         14 }->($tree); # Start at root of tree
417              
418             @leaves
419 2         19 }
420              
421             sub parentsOrdered($$$) #P The set of all parents in the tree, i.e. each non leaf of the tree, i.e the interior of the tree in the specified order.
422 7     7 1 13 {my ($tree, $preorder, $reverse) = @_; # Tree, pre-order if true else post-order, reversed if true
423 7         11 my @parents; # Parents
424             sub # Traverse
425 73     73   183 {my ($child) = @_; # Child
426 73 100       1193 if (my @c = $child->children->@*) # Children of child
427 36 100       205 {@c = reverse @c if $reverse; # Reverse if requested
428 36 100       59 push @parents, $child if $preorder; # Pre-order
429 36         101 __SUB__->($_) for @c; # Process children of child
430 36 100       177 push @parents, $child unless $preorder; # Post-order
431             }
432 7         40 }->($tree); # Start at root of tree
433              
434             @parents
435 7         69 }
436              
437             sub parentsPreOrder($) # The set of all parents in the tree, i.e. each non leaf of the tree, i.e the interior of the tree in normal pre-order.
438 1     1 1 3 {my ($tree) = @_; # Tree
439 1         5 parentsOrdered($tree, 1, 0);
440             }
441              
442             sub parentsPostOrder($) # The set of all parents in the tree, i.e. each non leaf of the tree, i.e the interior of the tree in normal post-order.
443 4     4 1 9 {my ($tree) = @_; # Tree
444 4         10 parentsOrdered($tree, 0, 0);
445             }
446              
447             sub parentsReversePreOrder($) # The set of all parents in the tree, i.e. each non leaf of the tree, i.e the interior of the tree in reverse pre-order.
448 1     1 1 4 {my ($tree) = @_; # Tree
449 1         4 parentsOrdered($tree, 1, 1);
450             }
451              
452             sub parentsReversePostOrder($) # The set of all parents in the tree, i.e. each non leaf of the tree, i.e the interior of the tree in reverse post-order.
453 1     1 1 3 {my ($tree) = @_; # Tree
454 1         3 &parentsOrdered($tree, 0, 1);
455             }
456              
457             sub parents($) # The set of all parents in the tree, i.e. each non leaf of the tree, i.e the interior of the tree in normal post-order.
458 2     2 1 6 {my ($tree) = @_; # Tree
459 2         6 &parentsPostOrder(@_);
460             }
461              
462             #D1 Order # Check the order and relative position of children in a tree.
463              
464             sub above($$) # Return the first child if it is above the second child else return B.
465 4     4 1 11 {my ($first, $second) = @_; # First child, second child
466 4 50       11 return undef if $first == $second; # A child cannot be above itself
467 4         41 my @f = context $first; # Context of first child
468 4         10 my @s = context $second; # Context of second child
469 4   66     51 pop @f, pop @s while @f and @s and $f[-1] == $s[-1]; # Find first different ancestor
      100        
470 4 100       28 !@f ? $first : undef # First is above second if the ancestors of first are also ancestors of second
471             }
472              
473             sub below($$) # Return the first child if it is below the second child else return B.
474 2     2 1 7 {my ($first, $second) = @_; # First child, second child
475 2 100       10 above($second, $first) ? $first : undef
476             }
477              
478             sub after($$) # Return the first child if it occurs strictly after the second child in the tree or else B if the first child is L[above], L[below] or L[before] the second child.
479 4     4 1 8 {my ($first, $second) = @_; # First child, second child
480 4         11 my @f = context $first; # Context of first child
481 4         10 my @s = context $second; # Context of second child
482 4   66     50 pop @f, pop @s while @f and @s and $f[-1] == $s[-1]; # Find first different ancestor
      100        
483 4 100 66     27 return undef unless @f and @s; # Not strictly after
484 2 50       6 indexOfChildInParent($f[-1]) > indexOfChildInParent($s[-1]) ? $first : undef # First child relative to second child at first common ancestor
485             }
486              
487             sub before($$) # Return the first child if it occurs strictly before the second child in the tree or else B if the first child is L[above], L[below] or L[after] the second child.
488 2     2 1 7 {my ($first, $second) = @_; # First child, second child
489 2 100       5 after($second, $first) ? $first : undef
490             }
491              
492             #D1 Paths # Find paths between nodes
493              
494             sub path($) # Return the list of zero based child indexes for the path from the root of the tree containing the specified child to the specified child for use by the L[go] method.
495 1     1 1 4 {my ($child) = @_; # Child
496 1         2 my @p; # Path
497 1         24 for(my $p = $child; my $q = $p->parent; $p = $q) # Go up
498 3         20 {unshift @p, indexOfChildInParent $p # Record path
499             }
500             @p
501 1         22 }
502              
503             sub pathFrom($$) # Return the list of zero based child indexes for the path from the specified ancestor to the specified child for use by the L[go] method else confess if the ancestor is not, in fact, an ancestor.
504 9     9 1 19 {my ($child, $ancestor) = @_; # Child, ancestor
505 9 100       41 return () if $child == $ancestor; # Easy case
506 8         43 my @p; # Path
507 8         148 for(my $p = $child; my $q = $p->parent; $p = $q) # Go up
508 15         102 {unshift @p, indexOfChildInParent $p; # Record path
509 15 100       283 return @p if $q == $ancestor; # Stop at ancestor
510             }
511 0         0 confess "Not an ancestor"
512             }
513              
514             sub siblingsBefore($) # Return a list of siblings before the specified child.
515 1     1 1 4 {my ($child) = @_; # Child
516 1 50       18 return () unless my $parent = $child->parent; # Parent
517 1         25 my @c = $parent->children->@*; # Children
518 1         7 my $i = indexOfChildInParent $child; # Our position
519 1         24 @c[0..$i-1]
520             }
521              
522             sub siblingsAfter($) # Return a list of siblings after the specified child.
523 1     1 1 4 {my ($child) = @_; # Child
524 1 50       19 return () unless my $parent = $child->parent; # Parent
525 1         24 my @c = $parent->children->@*; # Children
526 1         8 my $i = indexOfChildInParent $child; # Our position
527 1         9 @c[$i+1..$#c]
528             }
529              
530             sub siblingsStrictlyBetween($$) # Return a list of the siblings strictly between two children of the same parent else return B.
531 2     2 1 7 {my ($start, $finish) = @_; # Start child, finish child
532 2 50       35 return () unless my $parent = $start->parent; # Parent
533 2 100       83 confess "Must be siblings" unless $parent == $finish->parent; # Check both children have the same parent
534 1         23 my @c = $parent->children->@*; # All siblings
535 1   66     15 shift @c while @c and $c[0] != $start; # Remove all siblings up to the start child
536 1   66     7 pop @c while @c and $c[-1] != $finish; # Remove all siblings after the finish child
537 1 50       3 shift @c; pop @c if @c; # Remove first and last child to make range strictly between
  1         10  
538             @c # Siblings strictly between start and finish
539 1         7 }
540              
541             sub lineage($$) # Return the path from the specified child to the specified ancestor else return B if the child is not a descendant of the ancestor.
542 2     2 1 6 {my ($child, $ancestor) = @_; # Child, ancestor
543 2         5 my @p; # Path
544 2         8 for(my $p = $child; $p; $p = $p->parent) # Go up
545 8         34 {push @p, $p; # Record path
546 8 100       121 last if $p == $ancestor # Stop if we encounter the specified ancestor
547             }
548 2 100 66     36 return @p if !@p or $p[-1] == $ancestor; # Found the ancestor
549             undef # No such ancestor
550 1         6 }
551              
552             sub nextPreOrderPath($) # Return a list of children visited between the specified child and the next child in pre-order.
553 22     22 1 38 {my ($start) = @_; # The child at the start of the path
554 22 100       358 return ($start->first) if $start->children->@*; # First child if possible
555 13         81 my $p = $start; # Traverse upwards and then right
556 13         15 my @p; # Path
557 13         37 push @p, $p = $p->parent while $p->isLast; # Traverse upwards
558 13 100       94 $p->next ? (@p, $p->next) : () # Traverse right else we are at the root
559             }
560              
561             sub nextPostOrderPath($) # Return a list of children visited between the specified child and the next child in post-order.
562 22     22 1 44 {my ($start) = @_; # The child at the start of the path
563 22         29 my $p = $start; # Traverse upwards and then right, then first most
564 22         29 my @p; # Path
565 22 100       360 if (!$p->parent) # Starting at the root which is last in a post order traversal
566 2         16 {push @p, $p while $p = $p->first;
567             return @p
568 2         54 }
569 20 100       124 return (@p, $p->parent) if $p->isLast; # Traverse upwards
570 11 50       75 if (my $q = $p->next) # Traverse right
571 11         26 {for( ; $q; $q = $q->first) {push @p, $q} # Traverse first most
  13         40  
572             return @p
573 11         236 }
574 0         0 ($p) # Back at the root
575             }
576              
577             sub prevPostOrderPath($) # Return a list of children visited between the specified child and the previous child in post-order.
578 22     22 1 41 {my ($start) = @_; # The child at the start of the path
579 22 100       372 return ($start->last) if $start->children->@*; # Last child if possible
580 13         66 my $p = $start; # Traverse upwards and then left
581 13         19 my @p; # Path
582 13         27 push @p, $p = $p->parent while $p->isFirst; # Traverse upwards
583 13 100       91 $p->prev ? (@p, $p->prev) : () # Traverse left else we are at the root
584             }
585              
586             sub prevPreOrderPath($) # Return a list of children visited between the specified child and the previous child in pre-order.
587 22     22 1 39 {my ($start) = @_; # The child at the start of the path
588 22         31 my $p = $start; # Traverse upwards and then left, then last most
589 22         28 my @p; # Path
590 22 100       363 if (!$p->parent) # Starting at the root which is last in a post order traversal
591 2         17 {push @p, $p while $p = $p->last;
592             return @p
593 2         53 }
594 20 100       128 return (@p, $p->parent) if $p->isFirst; # Traverse upwards
595 11 50       85 if (my $q = $p->prev) # Traverse left
596 11         27 {for( ; $q; $q = $q->last) {push @p, $q} # Traverse last most
  18         65  
597             return @p
598 11         232 }
599 0         0 ($p) # Back at the root
600             }
601              
602             #D1 Print # Print a tree.
603              
604             sub printTree($$$$) #P String representation as a horizontal tree.
605 37     37 1 72 {my ($tree, $print, $preorder, $reverse) = @_; # Tree, optional print method, pre-order, reverse
606 37         55 my @s; # String representation
607              
608             sub # Print a child
609 355     355   579 {my ($child, $depth) = @_; # Child, depth
610 355         5984 my $key = $child->key; # Key
611 355         6628 my $value = $child->value; # Value
612 355 50       1905 my $k = join '', ' ' x $depth, $print ? &$print($key) : $key; # Print key
613 355 50       699 my $v = !defined($value) ? '' : ref($value) ? dump($value) : $value; # Print value
    100          
614 355 100       975 push @s, [$k, $v] if $preorder;
615 355 100       6114 my @c = $child->children->@*; @c = reverse @c if $reverse;
  355         1759  
616 355         1054 __SUB__->($_, $depth+1) for @c; # Print children of child
617 355 100       923 push @s, [$k, $v] unless $preorder;
618 37         267 }->($tree, 0); # Print root
619              
620 37         453 my $r = formatTableBasic [[qw(Key Value)], @s]; # Print tree
621 37 50       15853 owf($logFile, $r) if -e $logFile; # Log the result if requested
622 37         409 $r
623             }
624              
625             sub printPreOrder($;$) # Print tree in normal pre-order.
626 34     34 1 63 {my ($tree, $print) = @_; # Tree, optional print method
627 34         77 printTree($tree, $print, 1, 0);
628             }
629              
630             sub printPostOrder($;$) # Print tree in normal post-order.
631 1     1 1 4 {my ($tree, $print) = @_; # Tree, optional print method
632 1         5 printTree($tree, $print, 0, 0);
633             }
634              
635             sub printReversePreOrder($;$) # Print tree in reverse pre-order
636 1     1 1 5 {my ($tree, $print) = @_; # Tree, optional print method
637 1         4 printTree($tree, $print, 1, 1);
638             }
639              
640             sub printReversePostOrder($;$) # Print tree in reverse post-order
641 1     1 1 4 {my ($tree, $print) = @_; # Tree, optional print method
642 1         4 printTree($tree, $print, 0, 1);
643             }
644              
645             sub print($;$) # Print tree in normal pre-order.
646 33     33 1 74 {my ($tree, $print) = @_; # Tree, optional print method
647 33         435 &printPreOrder(@_);
648             }
649              
650             sub brackets($;$$) # Bracketed string representation of a tree.
651 7     7 1 18 {my ($tree, $print, $separator) = @_; # Tree, optional print method, optional child separator
652 7   50     37 my $t = $separator // ''; # Default child separator
653             sub # Print a child
654 62     62   92 {my ($child) = @_; # Child
655 62         1018 my $key = $child->key; # Key
656 62 50       272 my $p = $print ? &$print($key) : $key; # Printed child
657 62         980 my $c = $child->children; # Children of child
658 62 100       406 return $p unless @$c; # Return child immediately if no children to format
659 31         61 join '', $p, '(', join($t, map {__SUB__->($_)} @$c), ')' # String representation
  55         130  
660 7         43 }->($tree) # Print root
661             }
662              
663             sub xml($;$) # Print a tree as as xml.
664 1     1 1 4 {my ($tree, $print) = @_; # Tree, optional print method
665             sub # Print a child
666 12     12   21 {my ($child) = @_; # Child
667 12         202 my $key = $child->key; # Key
668 12 50       57 my $p = $print ? &$print($key) : $key; # Printed child
669 12         193 my $c = $child->children; # Children of child
670 12 100       111 return "<$p/>" unless @$c; # Singleton
671 6         16 join '', "<$p>", (map {__SUB__->($_)} @$c), "" # String representation
  11         30  
672 1         9 }->($tree) # Print root
673             }
674              
675             #D1 Data Structures # Data structures use by this package.
676              
677             #D0
678             #-------------------------------------------------------------------------------
679             # Export
680             #-------------------------------------------------------------------------------
681              
682 1     1   8356 use Exporter qw(import);
  1         7  
  1         67  
683              
684 1     1   9 use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
  1         3  
  1         891  
685              
686             @ISA = qw(Exporter);
687             @EXPORT_OK = qw(
688             );
689             %EXPORT_TAGS = (all=>[@EXPORT, @EXPORT_OK]);
690              
691             # podDocumentation
692              
693             =pod
694              
695             =encoding utf-8
696              
697             =head1 Name
698              
699             Tree::Ops - Tree operations.
700              
701             =head1 Synopsis
702              
703             Create a tree:
704              
705             my $a = Tree::Ops::new 'a', 'A';
706              
707             for(1..2)
708             {$a->open ('b', "B$_");
709             $a->single('c', "C$_");
710             $a->close;
711             }
712             $a->single ('d', 'D');
713             $a->single ('e', 'E');
714              
715             Print it:
716              
717             is_deeply $a->print, <
718             Key Value
719             a A
720             b B1
721             c C1
722             b B2
723             c C2
724             d D
725             e E
726             END
727              
728             Navigate through the tree:
729              
730             is_deeply $a->lastMost->prev->prev->first->key, 'c';
731             is_deeply $a->first->next->last->parent->first->value, 'C2';
732              
733             Traverse the tree:
734              
735             is_deeply [map{$_->value} $a->by], [qw(C1 B1 C2 B2 D E A)];
736              
737             Select items from the tree:
738              
739             is_deeply [map{$_->value} $a->select('b')], [qw(B1 B2)];
740             is_deeply [map{$_->value} $a->select(qr(b|c))], [qw(B1 C1 B2 C2)];
741             is_deeply [map{$_->value} $a->select(sub{$_[0] eq 'd'})], [qw(D)];
742              
743             Reorganize the tree:
744              
745             $a->first->next->stepEnd->stepEnd->first->next->stepBack;
746             is_deeply $a->print, <
747             Key Value
748             a A
749             b B1
750             c C1
751             b B2
752             d D
753             c C2
754             e E
755             END
756              
757             =head1 Description
758              
759             Tree operations.
760              
761              
762             Version 20200725.
763              
764              
765             The following sections describe the methods in each functional area of this
766             module. For an alphabetic listing of all methods by name see L.
767              
768              
769              
770             =head1 Build
771              
772             Create a tree. There is no implicit ordering applied to the tree, the relationships between parents and children within the tree are as established by the user and can be reorganized at will using the methods in this module.
773              
774             =head2 new($key, $value)
775              
776             Create a new child optionally recording the specified key or value.
777              
778             Parameter Description
779             1 $key Key
780             2 $value Value
781              
782             B
783              
784              
785            
786             my $a = Tree::Ops::new 'a', 'A'; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
787              
788             for(1..2)
789             {$a->open ('b', "B$_");
790             $a->single('c', "C$_");
791             ok $a->activeScope->key eq 'b';
792             $a->close;
793             }
794             $a->single ('d', 'D');
795             $a->single ('e', 'E');
796             is_deeply $a->print, <
797             Key Value
798             a A
799             b B1
800             c C1
801             b B2
802             c C2
803             d D
804             e E
805             END
806            
807             is_deeply [map{$_->value} $a->by], [qw(C1 B1 C2 B2 D E A)];
808            
809             is_deeply $a->lastMost->prev->prev->first->key, 'c';
810             is_deeply $a->first->next->last->parent->first->value, 'C2';
811            
812             is_deeply [map{$_->value} $a->select('b')], [qw(B1 B2)];
813             is_deeply [map{$_->value} $a->select(qr(b|c))], [qw(B1 C1 B2 C2)];
814             is_deeply [map{$_->value} $a->select(sub{$_[0] eq 'd'})], [qw(D)];
815            
816             $a->first->next->stepEnd->stepEnd->first->next->stepBack;
817             is_deeply $a->print, <
818             Key Value
819             a A
820             b B1
821             c C1
822             b B2
823             d D
824             c C2
825             e E
826             END
827            
828              
829             This is a static method and so should either be imported or invoked as:
830              
831             Tree::Ops::new
832              
833              
834             =head2 activeScope($tree)
835              
836             Locate the active scope in a tree.
837              
838             Parameter Description
839             1 $tree Tree
840              
841             B
842              
843              
844             my $a = Tree::Ops::new 'a', 'A';
845             for(1..2)
846             {$a->open ('b', "B$_");
847             $a->single('c', "C$_");
848            
849             ok $a->activeScope->key eq 'b'; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
850              
851             $a->close;
852             }
853             $a->single ('d', 'D');
854             $a->single ('e', 'E');
855             is_deeply $a->print, <
856             Key Value
857             a A
858             b B1
859             c C1
860             b B2
861             c C2
862             d D
863             e E
864             END
865            
866             is_deeply [map{$_->value} $a->by], [qw(C1 B1 C2 B2 D E A)];
867            
868             is_deeply $a->lastMost->prev->prev->first->key, 'c';
869             is_deeply $a->first->next->last->parent->first->value, 'C2';
870            
871             is_deeply [map{$_->value} $a->select('b')], [qw(B1 B2)];
872             is_deeply [map{$_->value} $a->select(qr(b|c))], [qw(B1 C1 B2 C2)];
873             is_deeply [map{$_->value} $a->select(sub{$_[0] eq 'd'})], [qw(D)];
874            
875             $a->first->next->stepEnd->stepEnd->first->next->stepBack;
876             is_deeply $a->print, <
877             Key Value
878             a A
879             b B1
880             c C1
881             b B2
882             d D
883             c C2
884             e E
885             END
886            
887              
888             =head2 open($tree, $key, $value)
889              
890             Add a child and make it the currently active scope into which new children will be added.
891              
892             Parameter Description
893             1 $tree Tree
894             2 $key Key
895             3 $value Value to be recorded in the interior child being opened
896              
897             B
898              
899              
900             my $a = Tree::Ops::new 'a', 'A';
901             for(1..2)
902            
903             {$a->open ('b', "B$_"); # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
904              
905             $a->single('c', "C$_");
906             ok $a->activeScope->key eq 'b';
907             $a->close;
908             }
909             $a->single ('d', 'D');
910             $a->single ('e', 'E');
911             is_deeply $a->print, <
912             Key Value
913             a A
914             b B1
915             c C1
916             b B2
917             c C2
918             d D
919             e E
920             END
921            
922             is_deeply [map{$_->value} $a->by], [qw(C1 B1 C2 B2 D E A)];
923            
924             is_deeply $a->lastMost->prev->prev->first->key, 'c';
925             is_deeply $a->first->next->last->parent->first->value, 'C2';
926            
927             is_deeply [map{$_->value} $a->select('b')], [qw(B1 B2)];
928             is_deeply [map{$_->value} $a->select(qr(b|c))], [qw(B1 C1 B2 C2)];
929             is_deeply [map{$_->value} $a->select(sub{$_[0] eq 'd'})], [qw(D)];
930            
931             $a->first->next->stepEnd->stepEnd->first->next->stepBack;
932             is_deeply $a->print, <
933             Key Value
934             a A
935             b B1
936             c C1
937             b B2
938             d D
939             c C2
940             e E
941             END
942            
943              
944             =head2 close($tree)
945              
946             Close the current scope returning to the previous scope.
947              
948             Parameter Description
949             1 $tree Tree
950              
951             B
952              
953              
954             my $a = Tree::Ops::new 'a', 'A';
955             for(1..2)
956             {$a->open ('b', "B$_");
957             $a->single('c', "C$_");
958             ok $a->activeScope->key eq 'b';
959            
960             $a->close; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
961              
962             }
963             $a->single ('d', 'D');
964             $a->single ('e', 'E');
965             is_deeply $a->print, <
966             Key Value
967             a A
968             b B1
969             c C1
970             b B2
971             c C2
972             d D
973             e E
974             END
975            
976             is_deeply [map{$_->value} $a->by], [qw(C1 B1 C2 B2 D E A)];
977            
978             is_deeply $a->lastMost->prev->prev->first->key, 'c';
979             is_deeply $a->first->next->last->parent->first->value, 'C2';
980            
981             is_deeply [map{$_->value} $a->select('b')], [qw(B1 B2)];
982             is_deeply [map{$_->value} $a->select(qr(b|c))], [qw(B1 C1 B2 C2)];
983             is_deeply [map{$_->value} $a->select(sub{$_[0] eq 'd'})], [qw(D)];
984            
985             $a->first->next->stepEnd->stepEnd->first->next->stepBack;
986             is_deeply $a->print, <
987             Key Value
988             a A
989             b B1
990             c C1
991             b B2
992             d D
993             c C2
994             e E
995             END
996            
997              
998             =head2 single($tree, $key, $value)
999              
1000             Add one child in the current scope.
1001              
1002             Parameter Description
1003             1 $tree Tree
1004             2 $key Key
1005             3 $value Value to be recorded in the child being created
1006              
1007             B
1008              
1009              
1010             my $a = Tree::Ops::new 'a', 'A';
1011             for(1..2)
1012             {$a->open ('b', "B$_");
1013            
1014             $a->single('c', "C$_"); # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1015              
1016             ok $a->activeScope->key eq 'b';
1017             $a->close;
1018             }
1019            
1020             $a->single ('d', 'D'); # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1021              
1022            
1023             $a->single ('e', 'E'); # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1024              
1025             is_deeply $a->print, <
1026             Key Value
1027             a A
1028             b B1
1029             c C1
1030             b B2
1031             c C2
1032             d D
1033             e E
1034             END
1035            
1036             is_deeply [map{$_->value} $a->by], [qw(C1 B1 C2 B2 D E A)];
1037            
1038             is_deeply $a->lastMost->prev->prev->first->key, 'c';
1039             is_deeply $a->first->next->last->parent->first->value, 'C2';
1040            
1041             is_deeply [map{$_->value} $a->select('b')], [qw(B1 B2)];
1042             is_deeply [map{$_->value} $a->select(qr(b|c))], [qw(B1 C1 B2 C2)];
1043             is_deeply [map{$_->value} $a->select(sub{$_[0] eq 'd'})], [qw(D)];
1044            
1045             $a->first->next->stepEnd->stepEnd->first->next->stepBack;
1046             is_deeply $a->print, <
1047             Key Value
1048             a A
1049             b B1
1050             c C1
1051             b B2
1052             d D
1053             c C2
1054             e E
1055             END
1056            
1057              
1058             =head2 include($tree, $include)
1059              
1060             Include the specified tree in the currently open scope.
1061              
1062             Parameter Description
1063             1 $tree Tree being built
1064             2 $include Tree to include
1065              
1066             B
1067              
1068              
1069             my ($i) = fromLetters 'b(cd)';
1070            
1071             my $a = Tree::Ops::new 'A';
1072             $a->open ('B');
1073            
1074             $a->include($i); # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1075              
1076             $a->close;
1077            
1078             is_deeply $a->print, <
1079             Key Value
1080             A
1081             B
1082             a
1083             b
1084             c
1085             d
1086             END
1087            
1088              
1089             =head2 fromLetters($letters)
1090              
1091             Create a tree from a string of letters returning the children created in alphabetic order - useful for testing.
1092              
1093             Parameter Description
1094             1 $letters String of letters and ( ).
1095              
1096             B
1097              
1098              
1099            
1100             my ($a) = fromLetters(q(bc(d)e)); # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1101              
1102            
1103             is_deeply $a->print, <
1104             Key Value
1105             a
1106             b
1107             c
1108             d
1109             e
1110             END
1111            
1112              
1113             =head1 Navigation
1114              
1115             Navigate through a tree.
1116              
1117             =head2 first($parent)
1118              
1119             Get the first child under the specified parent.
1120              
1121             Parameter Description
1122             1 $parent Parent
1123              
1124             B
1125              
1126              
1127             my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j) = fromLetters 'b(c)d(efgh(i(j)))';
1128             is_deeply $c->parent, $b;
1129            
1130             is_deeply $a->first, $b; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1131              
1132             is_deeply $a->last, $d;
1133             is_deeply $e->next, $f;
1134             is_deeply $f->prev, $e;
1135            
1136              
1137             =head2 last($parent)
1138              
1139             Get the last child under the specified parent.
1140              
1141             Parameter Description
1142             1 $parent Parent
1143              
1144             B
1145              
1146              
1147             my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j) = fromLetters 'b(c)d(efgh(i(j)))';
1148             is_deeply $c->parent, $b;
1149             is_deeply $a->first, $b;
1150            
1151             is_deeply $a->last, $d; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1152              
1153             is_deeply $e->next, $f;
1154             is_deeply $f->prev, $e;
1155            
1156              
1157             =head2 next($child)
1158              
1159             Get the next sibling following the specified child.
1160              
1161             Parameter Description
1162             1 $child Child
1163              
1164             B
1165              
1166              
1167             my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j) = fromLetters 'b(c)d(efgh(i(j)))';
1168             is_deeply $c->parent, $b;
1169             is_deeply $a->first, $b;
1170             is_deeply $a->last, $d;
1171            
1172             is_deeply $e->next, $f; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1173              
1174             is_deeply $f->prev, $e;
1175            
1176              
1177             =head2 prev($child)
1178              
1179             Get the previous sibling of the specified child.
1180              
1181             Parameter Description
1182             1 $child Child
1183              
1184             B
1185              
1186              
1187             my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j) = fromLetters 'b(c)d(efgh(i(j)))';
1188             is_deeply $c->parent, $b;
1189             is_deeply $a->first, $b;
1190             is_deeply $a->last, $d;
1191             is_deeply $e->next, $f;
1192            
1193             is_deeply $f->prev, $e; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1194              
1195            
1196              
1197             =head2 firstMost($parent)
1198              
1199             Return the first most descendant child in the tree starting at this parent or else return B if this parent has no children.
1200              
1201             Parameter Description
1202             1 $parent Parent
1203              
1204             B
1205              
1206              
1207             my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $x, $y) =
1208             fromLetters 'b(c)y(x)d(efgh(i(j)))';
1209            
1210             is_deeply $a->print, <
1211             Key Value
1212             a
1213             b
1214             c
1215             y
1216             x
1217             d
1218             e
1219             f
1220             g
1221             h
1222             i
1223             j
1224             END
1225            
1226             is_deeply $a->xml,
1227             '';
1228            
1229             is_deeply [$c, $x, $e, $f, $g, $j], [$a->leaves];
1230             is_deeply [$a, $b, $y, $d, $h, $i], [$a->parentsPreOrder];
1231             is_deeply [$b, $y, $i, $h, $d, $a], [$a->parentsPostOrder];
1232             is_deeply [$a->parents], [$a->parentsPostOrder];
1233            
1234             is_deeply [$a, $d, $h, $i, $y, $b], [$a->parentsReversePreOrder];
1235             is_deeply [$i, $h, $d, $y, $b, $a], [$a->parentsReversePostOrder];
1236            
1237             ok !$j->parents;
1238            
1239             ok $a->lastMost == $j;
1240             ok !$a->prevMost;
1241             ok $j->prevMost == $g;
1242             ok $i->prevMost == $g;
1243             ok $h->prevMost == $g;
1244             ok $g->prevMost == $f;
1245             ok $f->prevMost == $e;
1246             ok $e->prevMost == $x;
1247             ok $d->prevMost == $x;
1248             ok $x->prevMost == $c;
1249             ok $y->prevMost == $c;
1250             ok !$c->prevMost;
1251             ok !$b->prevMost;
1252             ok !$a->prevMost;
1253            
1254            
1255             ok $a->firstMost == $c; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1256              
1257             ok $a->nextMost == $c;
1258             ok $b->nextMost == $c;
1259             ok $c->nextMost == $x;
1260             ok $y->nextMost == $x;
1261             ok $x->nextMost == $e;
1262             ok $d->nextMost == $e;
1263             ok $e->nextMost == $f;
1264             ok $f->nextMost == $g;
1265             ok $g->nextMost == $j;
1266             ok $h->nextMost == $j;
1267             ok $i->nextMost == $j;
1268             ok !$j->nextMost;
1269            
1270             ok $i->topMost == $a;
1271            
1272              
1273             =head2 nextMost($child)
1274              
1275             Return the next child with no children, i.e. the next leaf of the tree, else return B if there is no such child.
1276              
1277             Parameter Description
1278             1 $child Current leaf
1279              
1280             B
1281              
1282              
1283             my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $x, $y) =
1284             fromLetters 'b(c)y(x)d(efgh(i(j)))';
1285            
1286             is_deeply $a->print, <
1287             Key Value
1288             a
1289             b
1290             c
1291             y
1292             x
1293             d
1294             e
1295             f
1296             g
1297             h
1298             i
1299             j
1300             END
1301            
1302             is_deeply $a->xml,
1303             '';
1304            
1305             is_deeply [$c, $x, $e, $f, $g, $j], [$a->leaves];
1306             is_deeply [$a, $b, $y, $d, $h, $i], [$a->parentsPreOrder];
1307             is_deeply [$b, $y, $i, $h, $d, $a], [$a->parentsPostOrder];
1308             is_deeply [$a->parents], [$a->parentsPostOrder];
1309            
1310             is_deeply [$a, $d, $h, $i, $y, $b], [$a->parentsReversePreOrder];
1311             is_deeply [$i, $h, $d, $y, $b, $a], [$a->parentsReversePostOrder];
1312            
1313             ok !$j->parents;
1314            
1315             ok $a->lastMost == $j;
1316             ok !$a->prevMost;
1317             ok $j->prevMost == $g;
1318             ok $i->prevMost == $g;
1319             ok $h->prevMost == $g;
1320             ok $g->prevMost == $f;
1321             ok $f->prevMost == $e;
1322             ok $e->prevMost == $x;
1323             ok $d->prevMost == $x;
1324             ok $x->prevMost == $c;
1325             ok $y->prevMost == $c;
1326             ok !$c->prevMost;
1327             ok !$b->prevMost;
1328             ok !$a->prevMost;
1329            
1330             ok $a->firstMost == $c;
1331            
1332             ok $a->nextMost == $c; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1333              
1334            
1335             ok $b->nextMost == $c; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1336              
1337            
1338             ok $c->nextMost == $x; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1339              
1340            
1341             ok $y->nextMost == $x; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1342              
1343            
1344             ok $x->nextMost == $e; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1345              
1346            
1347             ok $d->nextMost == $e; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1348              
1349            
1350             ok $e->nextMost == $f; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1351              
1352            
1353             ok $f->nextMost == $g; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1354              
1355            
1356             ok $g->nextMost == $j; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1357              
1358            
1359             ok $h->nextMost == $j; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1360              
1361            
1362             ok $i->nextMost == $j; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1363              
1364            
1365             ok !$j->nextMost; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1366              
1367            
1368             ok $i->topMost == $a;
1369            
1370              
1371             =head2 prevMost($child)
1372              
1373             Return the previous child with no children, i.e. the previous leaf of the tree, else return B if there is no such child.
1374              
1375             Parameter Description
1376             1 $child Current leaf
1377              
1378             B
1379              
1380              
1381             my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $x, $y) =
1382             fromLetters 'b(c)y(x)d(efgh(i(j)))';
1383            
1384             is_deeply $a->print, <
1385             Key Value
1386             a
1387             b
1388             c
1389             y
1390             x
1391             d
1392             e
1393             f
1394             g
1395             h
1396             i
1397             j
1398             END
1399            
1400             is_deeply $a->xml,
1401             '';
1402            
1403             is_deeply [$c, $x, $e, $f, $g, $j], [$a->leaves];
1404             is_deeply [$a, $b, $y, $d, $h, $i], [$a->parentsPreOrder];
1405             is_deeply [$b, $y, $i, $h, $d, $a], [$a->parentsPostOrder];
1406             is_deeply [$a->parents], [$a->parentsPostOrder];
1407            
1408             is_deeply [$a, $d, $h, $i, $y, $b], [$a->parentsReversePreOrder];
1409             is_deeply [$i, $h, $d, $y, $b, $a], [$a->parentsReversePostOrder];
1410            
1411             ok !$j->parents;
1412            
1413             ok $a->lastMost == $j;
1414            
1415             ok !$a->prevMost; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1416              
1417            
1418             ok $j->prevMost == $g; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1419              
1420            
1421             ok $i->prevMost == $g; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1422              
1423            
1424             ok $h->prevMost == $g; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1425              
1426            
1427             ok $g->prevMost == $f; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1428              
1429            
1430             ok $f->prevMost == $e; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1431              
1432            
1433             ok $e->prevMost == $x; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1434              
1435            
1436             ok $d->prevMost == $x; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1437              
1438            
1439             ok $x->prevMost == $c; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1440              
1441            
1442             ok $y->prevMost == $c; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1443              
1444            
1445             ok !$c->prevMost; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1446              
1447            
1448             ok !$b->prevMost; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1449              
1450            
1451             ok !$a->prevMost; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1452              
1453            
1454             ok $a->firstMost == $c;
1455             ok $a->nextMost == $c;
1456             ok $b->nextMost == $c;
1457             ok $c->nextMost == $x;
1458             ok $y->nextMost == $x;
1459             ok $x->nextMost == $e;
1460             ok $d->nextMost == $e;
1461             ok $e->nextMost == $f;
1462             ok $f->nextMost == $g;
1463             ok $g->nextMost == $j;
1464             ok $h->nextMost == $j;
1465             ok $i->nextMost == $j;
1466             ok !$j->nextMost;
1467            
1468             ok $i->topMost == $a;
1469            
1470              
1471             =head2 lastMost($parent)
1472              
1473             Return the last most descendant child in the tree starting at this parent or else return B if this parent has no children.
1474              
1475             Parameter Description
1476             1 $parent Parent
1477              
1478             B
1479              
1480              
1481             my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $x, $y) =
1482             fromLetters 'b(c)y(x)d(efgh(i(j)))';
1483            
1484             is_deeply $a->print, <
1485             Key Value
1486             a
1487             b
1488             c
1489             y
1490             x
1491             d
1492             e
1493             f
1494             g
1495             h
1496             i
1497             j
1498             END
1499            
1500             is_deeply $a->xml,
1501             '';
1502            
1503             is_deeply [$c, $x, $e, $f, $g, $j], [$a->leaves];
1504             is_deeply [$a, $b, $y, $d, $h, $i], [$a->parentsPreOrder];
1505             is_deeply [$b, $y, $i, $h, $d, $a], [$a->parentsPostOrder];
1506             is_deeply [$a->parents], [$a->parentsPostOrder];
1507            
1508             is_deeply [$a, $d, $h, $i, $y, $b], [$a->parentsReversePreOrder];
1509             is_deeply [$i, $h, $d, $y, $b, $a], [$a->parentsReversePostOrder];
1510            
1511             ok !$j->parents;
1512            
1513            
1514             ok $a->lastMost == $j; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1515              
1516             ok !$a->prevMost;
1517             ok $j->prevMost == $g;
1518             ok $i->prevMost == $g;
1519             ok $h->prevMost == $g;
1520             ok $g->prevMost == $f;
1521             ok $f->prevMost == $e;
1522             ok $e->prevMost == $x;
1523             ok $d->prevMost == $x;
1524             ok $x->prevMost == $c;
1525             ok $y->prevMost == $c;
1526             ok !$c->prevMost;
1527             ok !$b->prevMost;
1528             ok !$a->prevMost;
1529            
1530             ok $a->firstMost == $c;
1531             ok $a->nextMost == $c;
1532             ok $b->nextMost == $c;
1533             ok $c->nextMost == $x;
1534             ok $y->nextMost == $x;
1535             ok $x->nextMost == $e;
1536             ok $d->nextMost == $e;
1537             ok $e->nextMost == $f;
1538             ok $f->nextMost == $g;
1539             ok $g->nextMost == $j;
1540             ok $h->nextMost == $j;
1541             ok $i->nextMost == $j;
1542             ok !$j->nextMost;
1543            
1544             ok $i->topMost == $a;
1545            
1546              
1547             =head2 topMost($child)
1548              
1549             Return the top most parent in the tree containing the specified child.
1550              
1551             Parameter Description
1552             1 $child Child
1553              
1554             B
1555              
1556              
1557             my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $x, $y) =
1558             fromLetters 'b(c)y(x)d(efgh(i(j)))';
1559            
1560             is_deeply $a->print, <
1561             Key Value
1562             a
1563             b
1564             c
1565             y
1566             x
1567             d
1568             e
1569             f
1570             g
1571             h
1572             i
1573             j
1574             END
1575            
1576             is_deeply $a->xml,
1577             '';
1578            
1579             is_deeply [$c, $x, $e, $f, $g, $j], [$a->leaves];
1580             is_deeply [$a, $b, $y, $d, $h, $i], [$a->parentsPreOrder];
1581             is_deeply [$b, $y, $i, $h, $d, $a], [$a->parentsPostOrder];
1582             is_deeply [$a->parents], [$a->parentsPostOrder];
1583            
1584             is_deeply [$a, $d, $h, $i, $y, $b], [$a->parentsReversePreOrder];
1585             is_deeply [$i, $h, $d, $y, $b, $a], [$a->parentsReversePostOrder];
1586            
1587             ok !$j->parents;
1588            
1589             ok $a->lastMost == $j;
1590             ok !$a->prevMost;
1591             ok $j->prevMost == $g;
1592             ok $i->prevMost == $g;
1593             ok $h->prevMost == $g;
1594             ok $g->prevMost == $f;
1595             ok $f->prevMost == $e;
1596             ok $e->prevMost == $x;
1597             ok $d->prevMost == $x;
1598             ok $x->prevMost == $c;
1599             ok $y->prevMost == $c;
1600             ok !$c->prevMost;
1601             ok !$b->prevMost;
1602             ok !$a->prevMost;
1603            
1604             ok $a->firstMost == $c;
1605             ok $a->nextMost == $c;
1606             ok $b->nextMost == $c;
1607             ok $c->nextMost == $x;
1608             ok $y->nextMost == $x;
1609             ok $x->nextMost == $e;
1610             ok $d->nextMost == $e;
1611             ok $e->nextMost == $f;
1612             ok $f->nextMost == $g;
1613             ok $g->nextMost == $j;
1614             ok $h->nextMost == $j;
1615             ok $i->nextMost == $j;
1616             ok !$j->nextMost;
1617            
1618            
1619             ok $i->topMost == $a; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1620              
1621            
1622              
1623             =head2 mostRecentCommonAncestor($first, $second)
1624              
1625             Find the most recent common ancestor of the specified children.
1626              
1627             Parameter Description
1628             1 $first First child
1629             2 $second Second child
1630              
1631             B
1632              
1633              
1634             my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $k) =
1635             fromLetters 'b(c(d(e))f(g(h)i)j)k';
1636            
1637             is_deeply $a->print, <
1638             Key Value
1639             a
1640             b
1641             c
1642             d
1643             e
1644             f
1645             g
1646             h
1647             i
1648             j
1649             k
1650             END
1651            
1652            
1653             ok $e->mostRecentCommonAncestor($h) == $b; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1654              
1655            
1656             ok $e->mostRecentCommonAncestor($k) == $a; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1657              
1658            
1659              
1660             =head2 go($parent, @path)
1661              
1662             Return the child at the end of the path starting at the specified parent. A path is a list of zero based children numbers. Return B if the path is not valid.
1663              
1664             Parameter Description
1665             1 $parent Parent
1666             2 @path List of zero based children numbers
1667              
1668             B
1669              
1670              
1671             my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j) = fromLetters 'b(cd(e(fg)h)i)j';
1672            
1673             is_deeply $a->print, <
1674             Key Value
1675             a
1676             b
1677             c
1678             d
1679             e
1680             f
1681             g
1682             h
1683             i
1684             j
1685             END
1686            
1687            
1688             ok $a->go(0,1,0,1) == $g; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1689              
1690            
1691             ok $d->go(0,0) == $f; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1692              
1693            
1694             is_deeply [$e->path], [0,1,0];
1695             is_deeply [$g->pathFrom($d)], [0,1];
1696            
1697             is_deeply $b->dup->print, <
1698             Key Value
1699             b
1700             c
1701             d
1702             e
1703             f
1704             g
1705             h
1706             i
1707             END
1708            
1709             my $B = $b->transcribe;
1710            
1711             $b->by(sub
1712             {my ($c) = @_;
1713             my @path = $c->pathFrom($b);
1714            
1715             my $C = $B->go(@path); # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1716              
1717             is_deeply $c->key, $C->key;
1718             is_deeply $c->{transcribedTo}, $C;
1719             is_deeply $C->{transcribedFrom}, $c;
1720             });
1721            
1722             is_deeply $B->print, <
1723             Key Value
1724             b
1725             c
1726             d
1727             e
1728             f
1729             g
1730             h
1731             i
1732             END
1733            
1734              
1735             =head1 Location
1736              
1737             Verify the current location.
1738              
1739             =head2 context($child)
1740              
1741             Get the context of the current child.
1742              
1743             Parameter Description
1744             1 $child Child
1745              
1746             B
1747              
1748              
1749             my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $s, $t, $x, $y, $z) =
1750             fromLetters 'b(c)y(x)z(st)d(efgh(i(j))))';
1751            
1752            
1753             is_deeply [$x->context], [$x, $y, $a]; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1754              
1755            
1756             is_deeply join(' ', $a->by(sub{$_[0]->key})), "c b x y s t z e f g j i h d a";
1757             is_deeply join(' ', map{$_->key} $a->by), "c b x y s t z e f g j i h d a";
1758            
1759             is_deeply $a->print, <
1760             Key Value
1761             a
1762             b
1763             c
1764             y
1765             x
1766             z
1767             s
1768             t
1769             d
1770             e
1771             f
1772             g
1773             h
1774             i
1775             j
1776             END
1777            
1778             $z->cut;
1779             is_deeply $a->print, <
1780             Key Value
1781             a
1782             b
1783             c
1784             y
1785             x
1786             d
1787             e
1788             f
1789             g
1790             h
1791             i
1792             j
1793             END
1794            
1795              
1796             =head2 isFirst($child)
1797              
1798             Return the specified child if that child is first under its parent, else return B.
1799              
1800             Parameter Description
1801             1 $child Child
1802              
1803             B
1804              
1805              
1806             my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j) = fromLetters 'b(c)d(efgh(i(j)))';
1807            
1808             is_deeply $a->print, <
1809             Key Value
1810             a
1811             b
1812             c
1813             d
1814             e
1815             f
1816             g
1817             h
1818             i
1819             j
1820             END
1821            
1822             is_deeply $b->singleChildOfParent, $c;
1823            
1824             is_deeply $e->isFirst, $e; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1825              
1826            
1827             ok !$f->isFirst; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1828              
1829             ok !$g->isLast;
1830             is_deeply $h->isLast, $h;
1831             ok $j->empty;
1832             ok !$i->empty;
1833             ok $a->isTop;
1834             ok !$b->isTop;
1835            
1836              
1837             =head2 isLast($child)
1838              
1839             Return the specified child if that child is last under its parent, else return B.
1840              
1841             Parameter Description
1842             1 $child Child
1843              
1844             B
1845              
1846              
1847             my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j) = fromLetters 'b(c)d(efgh(i(j)))';
1848            
1849             is_deeply $a->print, <
1850             Key Value
1851             a
1852             b
1853             c
1854             d
1855             e
1856             f
1857             g
1858             h
1859             i
1860             j
1861             END
1862            
1863             is_deeply $b->singleChildOfParent, $c;
1864             is_deeply $e->isFirst, $e;
1865             ok !$f->isFirst;
1866            
1867             ok !$g->isLast; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1868              
1869            
1870             is_deeply $h->isLast, $h; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1871              
1872             ok $j->empty;
1873             ok !$i->empty;
1874             ok $a->isTop;
1875             ok !$b->isTop;
1876            
1877              
1878             =head2 isTop($parent)
1879              
1880             Return the specified parent if that parent is the top most parent in the tree.
1881              
1882             Parameter Description
1883             1 $parent Parent
1884              
1885             B
1886              
1887              
1888             my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j) = fromLetters 'b(c)d(efgh(i(j)))';
1889            
1890             is_deeply $a->print, <
1891             Key Value
1892             a
1893             b
1894             c
1895             d
1896             e
1897             f
1898             g
1899             h
1900             i
1901             j
1902             END
1903            
1904             is_deeply $b->singleChildOfParent, $c;
1905             is_deeply $e->isFirst, $e;
1906             ok !$f->isFirst;
1907             ok !$g->isLast;
1908             is_deeply $h->isLast, $h;
1909             ok $j->empty;
1910             ok !$i->empty;
1911            
1912             ok $a->isTop; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1913              
1914            
1915             ok !$b->isTop; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1916              
1917            
1918              
1919             =head2 singleChildOfParent($parent)
1920              
1921             Return the only child of this parent if the parent has an only child, else B
1922              
1923             Parameter Description
1924             1 $parent Parent
1925              
1926             B
1927              
1928              
1929             my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j) = fromLetters 'b(c)d(efgh(i(j)))';
1930            
1931             is_deeply $a->print, <
1932             Key Value
1933             a
1934             b
1935             c
1936             d
1937             e
1938             f
1939             g
1940             h
1941             i
1942             j
1943             END
1944            
1945            
1946             is_deeply $b->singleChildOfParent, $c; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1947              
1948             is_deeply $e->isFirst, $e;
1949             ok !$f->isFirst;
1950             ok !$g->isLast;
1951             is_deeply $h->isLast, $h;
1952             ok $j->empty;
1953             ok !$i->empty;
1954             ok $a->isTop;
1955             ok !$b->isTop;
1956            
1957              
1958             =head2 empty($parent)
1959              
1960             Return the specified parent if it has no children else B
1961              
1962             Parameter Description
1963             1 $parent Parent
1964              
1965             B
1966              
1967              
1968             my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j) = fromLetters 'b(c)d(efgh(i(j)))';
1969            
1970             is_deeply $a->print, <
1971             Key Value
1972             a
1973             b
1974             c
1975             d
1976             e
1977             f
1978             g
1979             h
1980             i
1981             j
1982             END
1983            
1984             is_deeply $b->singleChildOfParent, $c;
1985             is_deeply $e->isFirst, $e;
1986             ok !$f->isFirst;
1987             ok !$g->isLast;
1988             is_deeply $h->isLast, $h;
1989            
1990             ok $j->empty; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1991              
1992            
1993             ok !$i->empty; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1994              
1995             ok $a->isTop;
1996             ok !$b->isTop;
1997            
1998              
1999             =head1 Put
2000              
2001             Insert children into a tree.
2002              
2003             =head2 putFirst($parent, $child)
2004              
2005             Place a new child first under the specified parent and return the child.
2006              
2007             Parameter Description
2008             1 $parent Parent
2009             2 $child Child
2010              
2011             B
2012              
2013              
2014             my ($a, $b, $c, $d, $e) = fromLetters 'b(c)d(e)';
2015            
2016             is_deeply $a->print, <
2017             Key Value
2018             a
2019             b
2020             c
2021             d
2022             e
2023             END
2024            
2025             my $z = $b->putNext(new 'z');
2026             is_deeply $a->print, <
2027             Key Value
2028             a
2029             b
2030             c
2031             z
2032             d
2033             e
2034             END
2035            
2036             my $y = $d->putPrev(new 'y');
2037             is_deeply $a->print, <
2038             Key Value
2039             a
2040             b
2041             c
2042             z
2043             y
2044             d
2045             e
2046             END
2047            
2048             $z->putLast(new 't');
2049             is_deeply $a->print, <
2050             Key Value
2051             a
2052             b
2053             c
2054             z
2055             t
2056             y
2057             d
2058             e
2059             END
2060            
2061            
2062             $z->putFirst(new 's'); # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
2063              
2064             is_deeply $a->print, <
2065             Key Value
2066             a
2067             b
2068             c
2069             z
2070             s
2071             t
2072             y
2073             d
2074             e
2075             END
2076            
2077              
2078             =head2 putLast($parent, $child)
2079              
2080             Place a new child last under the specified parent and return the child.
2081              
2082             Parameter Description
2083             1 $parent Parent
2084             2 $child Child
2085              
2086             B
2087              
2088              
2089             my ($a, $b, $c, $d, $e) = fromLetters 'b(c)d(e)';
2090            
2091             is_deeply $a->print, <
2092             Key Value
2093             a
2094             b
2095             c
2096             d
2097             e
2098             END
2099            
2100             my $z = $b->putNext(new 'z');
2101             is_deeply $a->print, <
2102             Key Value
2103             a
2104             b
2105             c
2106             z
2107             d
2108             e
2109             END
2110            
2111             my $y = $d->putPrev(new 'y');
2112             is_deeply $a->print, <
2113             Key Value
2114             a
2115             b
2116             c
2117             z
2118             y
2119             d
2120             e
2121             END
2122            
2123            
2124             $z->putLast(new 't'); # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
2125              
2126             is_deeply $a->print, <
2127             Key Value
2128             a
2129             b
2130             c
2131             z
2132             t
2133             y
2134             d
2135             e
2136             END
2137            
2138             $z->putFirst(new 's');
2139             is_deeply $a->print, <
2140             Key Value
2141             a
2142             b
2143             c
2144             z
2145             s
2146             t
2147             y
2148             d
2149             e
2150             END
2151            
2152              
2153             =head2 putNext($child, $new)
2154              
2155             Place a new child after the specified child.
2156              
2157             Parameter Description
2158             1 $child Existing child
2159             2 $new New child
2160              
2161             B
2162              
2163              
2164             my ($a, $b, $c, $d, $e) = fromLetters 'b(c)d(e)';
2165            
2166             is_deeply $a->print, <
2167             Key Value
2168             a
2169             b
2170             c
2171             d
2172             e
2173             END
2174            
2175            
2176             my $z = $b->putNext(new 'z'); # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
2177              
2178             is_deeply $a->print, <
2179             Key Value
2180             a
2181             b
2182             c
2183             z
2184             d
2185             e
2186             END
2187            
2188             my $y = $d->putPrev(new 'y');
2189             is_deeply $a->print, <
2190             Key Value
2191             a
2192             b
2193             c
2194             z
2195             y
2196             d
2197             e
2198             END
2199            
2200             $z->putLast(new 't');
2201             is_deeply $a->print, <
2202             Key Value
2203             a
2204             b
2205             c
2206             z
2207             t
2208             y
2209             d
2210             e
2211             END
2212            
2213             $z->putFirst(new 's');
2214             is_deeply $a->print, <
2215             Key Value
2216             a
2217             b
2218             c
2219             z
2220             s
2221             t
2222             y
2223             d
2224             e
2225             END
2226            
2227              
2228             =head2 putPrev($child, $new)
2229              
2230             Place a new child before the specified child.
2231              
2232             Parameter Description
2233             1 $child Child
2234             2 $new New child
2235              
2236             B
2237              
2238              
2239             my ($a, $b, $c, $d, $e) = fromLetters 'b(c)d(e)';
2240            
2241             is_deeply $a->print, <
2242             Key Value
2243             a
2244             b
2245             c
2246             d
2247             e
2248             END
2249            
2250             my $z = $b->putNext(new 'z');
2251             is_deeply $a->print, <
2252             Key Value
2253             a
2254             b
2255             c
2256             z
2257             d
2258             e
2259             END
2260            
2261            
2262             my $y = $d->putPrev(new 'y'); # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
2263              
2264             is_deeply $a->print, <
2265             Key Value
2266             a
2267             b
2268             c
2269             z
2270             y
2271             d
2272             e
2273             END
2274            
2275             $z->putLast(new 't');
2276             is_deeply $a->print, <
2277             Key Value
2278             a
2279             b
2280             c
2281             z
2282             t
2283             y
2284             d
2285             e
2286             END
2287            
2288             $z->putFirst(new 's');
2289             is_deeply $a->print, <
2290             Key Value
2291             a
2292             b
2293             c
2294             z
2295             s
2296             t
2297             y
2298             d
2299             e
2300             END
2301            
2302              
2303             =head1 Steps
2304              
2305             Move the start or end of a scope forwards or backwards as suggested by Alex Monroe.
2306              
2307             =head2 step($parent)
2308              
2309             Make the first child of the specified parent the parents previous sibling and return the parent. In effect this moves the start of the parent one step forwards.
2310              
2311             Parameter Description
2312             1 $parent Parent
2313              
2314             B
2315              
2316              
2317             my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j) = fromLetters 'b(c)d(efgh(i(j)))';
2318            
2319             is_deeply $a->brackets, 'a(b(c)d(efgh(i(j))))';
2320            
2321            
2322             $d->step; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
2323              
2324             is_deeply $a->brackets, 'a(b(c)ed(fgh(i(j))))';
2325            
2326            
2327             $d->stepBack; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
2328              
2329             is_deeply $a->brackets, 'a(b(c)d(efgh(i(j))))';
2330            
2331            
2332             $b->stepEnd; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
2333              
2334             is_deeply $a->brackets, 'a(b(cd(efgh(i(j)))))';
2335            
2336            
2337             $b->stepEndBack; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
2338              
2339             is_deeply $a->brackets, 'a(b(c)d(efgh(i(j))))';
2340            
2341              
2342             =head2 stepEnd($parent)
2343              
2344             Make the next sibling of the specified parent the parents last child and return the parent. In effect this moves the end of the parent one step forwards.
2345              
2346             Parameter Description
2347             1 $parent Parent
2348              
2349             B
2350              
2351              
2352             my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j) = fromLetters 'b(c)d(efgh(i(j)))';
2353            
2354             is_deeply $a->brackets, 'a(b(c)d(efgh(i(j))))';
2355            
2356             $d->step;
2357             is_deeply $a->brackets, 'a(b(c)ed(fgh(i(j))))';
2358            
2359             $d->stepBack;
2360             is_deeply $a->brackets, 'a(b(c)d(efgh(i(j))))';
2361            
2362            
2363             $b->stepEnd; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
2364              
2365             is_deeply $a->brackets, 'a(b(cd(efgh(i(j)))))';
2366            
2367            
2368             $b->stepEndBack; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
2369              
2370             is_deeply $a->brackets, 'a(b(c)d(efgh(i(j))))';
2371            
2372              
2373             =head2 stepBack()
2374              
2375             Make the previous sibling of the specified parent the parents first child and return the parent. In effect this moves the start of the parent one step backwards.
2376              
2377              
2378             B
2379              
2380              
2381             my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j) = fromLetters 'b(c)d(efgh(i(j)))';
2382            
2383             is_deeply $a->brackets, 'a(b(c)d(efgh(i(j))))';
2384            
2385             $d->step;
2386             is_deeply $a->brackets, 'a(b(c)ed(fgh(i(j))))';
2387            
2388            
2389             $d->stepBack; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
2390              
2391             is_deeply $a->brackets, 'a(b(c)d(efgh(i(j))))';
2392            
2393             $b->stepEnd;
2394             is_deeply $a->brackets, 'a(b(cd(efgh(i(j)))))';
2395            
2396             $b->stepEndBack;
2397             is_deeply $a->brackets, 'a(b(c)d(efgh(i(j))))';
2398            
2399              
2400             =head2 stepEndBack()
2401              
2402             Make the last child of the specified parent the parents next sibling and return the parent. In effect this moves the end of the parent one step backwards.
2403              
2404              
2405             B
2406              
2407              
2408             my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j) = fromLetters 'b(c)d(efgh(i(j)))';
2409            
2410             is_deeply $a->brackets, 'a(b(c)d(efgh(i(j))))';
2411            
2412             $d->step;
2413             is_deeply $a->brackets, 'a(b(c)ed(fgh(i(j))))';
2414            
2415             $d->stepBack;
2416             is_deeply $a->brackets, 'a(b(c)d(efgh(i(j))))';
2417            
2418             $b->stepEnd;
2419             is_deeply $a->brackets, 'a(b(cd(efgh(i(j)))))';
2420            
2421            
2422             $b->stepEndBack; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
2423              
2424             is_deeply $a->brackets, 'a(b(c)d(efgh(i(j))))';
2425            
2426              
2427             =head1 Edit
2428              
2429             Edit a tree in situ.
2430              
2431             =head2 cut($child)
2432              
2433             Cut out a child and all its content and children, return it ready for reinsertion else where.
2434              
2435             Parameter Description
2436             1 $child Child
2437              
2438             B
2439              
2440              
2441             my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $s, $t, $x, $y, $z) =
2442             fromLetters 'b(c)y(x)z(st)d(efgh(i(j))))';
2443            
2444             is_deeply [$x->context], [$x, $y, $a];
2445            
2446             is_deeply join(' ', $a->by(sub{$_[0]->key})), "c b x y s t z e f g j i h d a";
2447             is_deeply join(' ', map{$_->key} $a->by), "c b x y s t z e f g j i h d a";
2448            
2449             is_deeply $a->print, <
2450             Key Value
2451             a
2452             b
2453             c
2454             y
2455             x
2456             z
2457             s
2458             t
2459             d
2460             e
2461             f
2462             g
2463             h
2464             i
2465             j
2466             END
2467            
2468            
2469             $z->cut; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
2470              
2471             is_deeply $a->print, <
2472             Key Value
2473             a
2474             b
2475             c
2476             y
2477             x
2478             d
2479             e
2480             f
2481             g
2482             h
2483             i
2484             j
2485             END
2486            
2487              
2488             =head2 dup($parent)
2489              
2490             Duplicate a specified parent and all its descendants returning the root of the resulting tree.
2491              
2492             Parameter Description
2493             1 $parent Parent
2494              
2495             B
2496              
2497              
2498             my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j) = fromLetters 'b(cd(e(fg)h)i)j';
2499            
2500             is_deeply $a->print, <
2501             Key Value
2502             a
2503             b
2504             c
2505             d
2506             e
2507             f
2508             g
2509             h
2510             i
2511             j
2512             END
2513            
2514             ok $a->go(0,1,0,1) == $g;
2515             ok $d->go(0,0) == $f;
2516            
2517             is_deeply [$e->path], [0,1,0];
2518             is_deeply [$g->pathFrom($d)], [0,1];
2519            
2520            
2521             is_deeply $b->dup->print, <
2522              
2523             Key Value
2524             b
2525             c
2526             d
2527             e
2528             f
2529             g
2530             h
2531             i
2532             END
2533            
2534             my $B = $b->transcribe;
2535            
2536             $b->by(sub
2537             {my ($c) = @_;
2538             my @path = $c->pathFrom($b);
2539             my $C = $B->go(@path);
2540             is_deeply $c->key, $C->key;
2541             is_deeply $c->{transcribedTo}, $C;
2542             is_deeply $C->{transcribedFrom}, $c;
2543             });
2544            
2545             is_deeply $B->print, <
2546             Key Value
2547             b
2548             c
2549             d
2550             e
2551             f
2552             g
2553             h
2554             i
2555             END
2556            
2557              
2558             =head2 transcribe($parent)
2559              
2560             Duplicate a specified parent and all its descendants recording the mapping in a temporary {transcribed} field in the tree being transcribed. Returns the root parent of the tree being duplicated.
2561              
2562             Parameter Description
2563             1 $parent Parent
2564              
2565             B
2566              
2567              
2568             my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j) = fromLetters 'b(cd(e(fg)h)i)j';
2569            
2570             is_deeply $a->print, <
2571             Key Value
2572             a
2573             b
2574             c
2575             d
2576             e
2577             f
2578             g
2579             h
2580             i
2581             j
2582             END
2583            
2584             ok $a->go(0,1,0,1) == $g;
2585             ok $d->go(0,0) == $f;
2586            
2587             is_deeply [$e->path], [0,1,0];
2588             is_deeply [$g->pathFrom($d)], [0,1];
2589            
2590             is_deeply $b->dup->print, <
2591             Key Value
2592             b
2593             c
2594             d
2595             e
2596             f
2597             g
2598             h
2599             i
2600             END
2601            
2602            
2603             my $B = $b->transcribe; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
2604              
2605            
2606             $b->by(sub
2607             {my ($c) = @_;
2608             my @path = $c->pathFrom($b);
2609             my $C = $B->go(@path);
2610             is_deeply $c->key, $C->key;
2611            
2612             is_deeply $c->{transcribedTo}, $C; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
2613              
2614            
2615             is_deeply $C->{transcribedFrom}, $c; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
2616              
2617             });
2618            
2619             is_deeply $B->print, <
2620             Key Value
2621             b
2622             c
2623             d
2624             e
2625             f
2626             g
2627             h
2628             i
2629             END
2630            
2631              
2632             =head2 unwrap($child)
2633              
2634             Unwrap the specified child and return that child.
2635              
2636             Parameter Description
2637             1 $child Child
2638              
2639             B
2640              
2641              
2642             my ($a, $b, $c, $d, $e, $f, $g) = fromLetters 'b(c(de)f)g';
2643            
2644             is_deeply $a->print, <
2645             Key Value
2646             a
2647             b
2648             c
2649             d
2650             e
2651             f
2652             g
2653             END
2654            
2655             $c->wrap('z');
2656            
2657             is_deeply $a->print, <
2658             Key Value
2659             a
2660             b
2661             z
2662             c
2663             d
2664             e
2665             f
2666             g
2667             END
2668            
2669            
2670             $c->parent->unwrap; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
2671              
2672            
2673             is_deeply $a->print, <
2674             Key Value
2675             a
2676             b
2677             c
2678             d
2679             e
2680             f
2681             g
2682             END
2683            
2684             $c->wrapChildren("Z");
2685            
2686             is_deeply $a->print, <
2687             Key Value
2688             a
2689             b
2690             c
2691             Z
2692             d
2693             e
2694             f
2695             g
2696             END
2697            
2698              
2699             =head2 wrap($child, $key, $value)
2700              
2701             Wrap the specified child with a new parent and return the new parent optionally setting its L and L.
2702              
2703             Parameter Description
2704             1 $child Child to wrap
2705             2 $key Optional key
2706             3 $value Optional value
2707              
2708             B
2709              
2710              
2711             my ($a, $b, $c, $d, $e, $f, $g) = fromLetters 'b(c(de)f)g';
2712            
2713             is_deeply $a->print, <
2714             Key Value
2715             a
2716             b
2717             c
2718             d
2719             e
2720             f
2721             g
2722             END
2723            
2724            
2725             $c->wrap('z'); # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
2726              
2727            
2728             is_deeply $a->print, <
2729             Key Value
2730             a
2731             b
2732             z
2733             c
2734             d
2735             e
2736             f
2737             g
2738             END
2739            
2740            
2741             $c->parent->unwrap; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
2742              
2743            
2744             is_deeply $a->print, <
2745             Key Value
2746             a
2747             b
2748             c
2749             d
2750             e
2751             f
2752             g
2753             END
2754            
2755            
2756             $c->wrapChildren("Z"); # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
2757              
2758            
2759             is_deeply $a->print, <
2760             Key Value
2761             a
2762             b
2763             c
2764             Z
2765             d
2766             e
2767             f
2768             g
2769             END
2770            
2771              
2772             =head2 wrapChildren($parent, $key, $value)
2773              
2774             Wrap the children of the specified parent with a new intermediate parent that becomes the child of the specified parent, optionally setting the L and the L for the new parent. Return the new parent.
2775              
2776             Parameter Description
2777             1 $parent Child to wrap
2778             2 $key Optional key for new wrapping parent
2779             3 $value Optional value for new wrapping parent
2780              
2781             B
2782              
2783              
2784             my ($a, $b, $c, $d, $e, $f, $g) = fromLetters 'b(c(de)f)g';
2785            
2786             is_deeply $a->print, <
2787             Key Value
2788             a
2789             b
2790             c
2791             d
2792             e
2793             f
2794             g
2795             END
2796            
2797             $c->wrap('z');
2798            
2799             is_deeply $a->print, <
2800             Key Value
2801             a
2802             b
2803             z
2804             c
2805             d
2806             e
2807             f
2808             g
2809             END
2810            
2811             $c->parent->unwrap;
2812            
2813             is_deeply $a->print, <
2814             Key Value
2815             a
2816             b
2817             c
2818             d
2819             e
2820             f
2821             g
2822             END
2823            
2824            
2825             $c->wrapChildren("Z"); # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
2826              
2827            
2828             is_deeply $a->print, <
2829             Key Value
2830             a
2831             b
2832             c
2833             Z
2834             d
2835             e
2836             f
2837             g
2838             END
2839            
2840              
2841             =head2 merge($parent)
2842              
2843             Unwrap the children of the specified parent with the whose L fields L that of their parent. Returns the specified parent regardless.
2844              
2845             Parameter Description
2846             1 $parent Merging parent
2847              
2848             B
2849              
2850              
2851             my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j) = fromLetters 'b(c)d(efgh(i(j)))';
2852            
2853             is_deeply $a->print, <
2854             Key Value
2855             a
2856             b
2857             c
2858             d
2859             e
2860             f
2861             g
2862             h
2863             i
2864             j
2865             END
2866            
2867             $d->split;
2868             is_deeply $a->print, <
2869             Key Value
2870             a
2871             b
2872             c
2873             d
2874             d
2875             e
2876             d
2877             f
2878             d
2879             g
2880             d
2881             h
2882             i
2883             j
2884             END
2885            
2886            
2887             $f->parent->mergeLikePrev; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
2888              
2889             is_deeply $a->print, <
2890             Key Value
2891             a
2892             b
2893             c
2894             d
2895             d
2896             e
2897             f
2898             d
2899             g
2900             d
2901             h
2902             i
2903             j
2904             END
2905            
2906            
2907             $g->parent->mergeLikeNext; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
2908              
2909             is_deeply $a->print, <
2910             Key Value
2911             a
2912             b
2913             c
2914             d
2915             d
2916             e
2917             f
2918             d
2919             g
2920             h
2921             i
2922             j
2923             END
2924            
2925            
2926             $d->merge; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
2927              
2928             is_deeply $a->print, <
2929             Key Value
2930             a
2931             b
2932             c
2933             d
2934             e
2935             f
2936             g
2937             h
2938             i
2939             j
2940             END
2941            
2942              
2943             =head2 mergeLikePrev($child)
2944              
2945             Merge the preceding sibling of the specified child if that sibling exists and the L data of the two siblings L. Returns the specified child regardless. From a proposal made by Micaela Monroe.
2946              
2947             Parameter Description
2948             1 $child Child
2949              
2950             B
2951              
2952              
2953             my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j) = fromLetters 'b(c)d(efgh(i(j)))';
2954            
2955             is_deeply $a->print, <
2956             Key Value
2957             a
2958             b
2959             c
2960             d
2961             e
2962             f
2963             g
2964             h
2965             i
2966             j
2967             END
2968            
2969             $d->split;
2970             is_deeply $a->print, <
2971             Key Value
2972             a
2973             b
2974             c
2975             d
2976             d
2977             e
2978             d
2979             f
2980             d
2981             g
2982             d
2983             h
2984             i
2985             j
2986             END
2987            
2988            
2989             $f->parent->mergeLikePrev; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
2990              
2991             is_deeply $a->print, <
2992             Key Value
2993             a
2994             b
2995             c
2996             d
2997             d
2998             e
2999             f
3000             d
3001             g
3002             d
3003             h
3004             i
3005             j
3006             END
3007            
3008             $g->parent->mergeLikeNext;
3009             is_deeply $a->print, <
3010             Key Value
3011             a
3012             b
3013             c
3014             d
3015             d
3016             e
3017             f
3018             d
3019             g
3020             h
3021             i
3022             j
3023             END
3024            
3025             $d->merge;
3026             is_deeply $a->print, <
3027             Key Value
3028             a
3029             b
3030             c
3031             d
3032             e
3033             f
3034             g
3035             h
3036             i
3037             j
3038             END
3039            
3040              
3041             =head2 mergeLikeNext($child)
3042              
3043             Merge the following sibling of the specified child if that sibling exists and the L data of the two siblings L. Returns the specified child regardless. From a proposal made by Micaela Monroe.
3044              
3045             Parameter Description
3046             1 $child Child
3047              
3048             B
3049              
3050              
3051             my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j) = fromLetters 'b(c)d(efgh(i(j)))';
3052            
3053             is_deeply $a->print, <
3054             Key Value
3055             a
3056             b
3057             c
3058             d
3059             e
3060             f
3061             g
3062             h
3063             i
3064             j
3065             END
3066            
3067             $d->split;
3068             is_deeply $a->print, <
3069             Key Value
3070             a
3071             b
3072             c
3073             d
3074             d
3075             e
3076             d
3077             f
3078             d
3079             g
3080             d
3081             h
3082             i
3083             j
3084             END
3085            
3086             $f->parent->mergeLikePrev;
3087             is_deeply $a->print, <
3088             Key Value
3089             a
3090             b
3091             c
3092             d
3093             d
3094             e
3095             f
3096             d
3097             g
3098             d
3099             h
3100             i
3101             j
3102             END
3103            
3104            
3105             $g->parent->mergeLikeNext; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
3106              
3107             is_deeply $a->print, <
3108             Key Value
3109             a
3110             b
3111             c
3112             d
3113             d
3114             e
3115             f
3116             d
3117             g
3118             h
3119             i
3120             j
3121             END
3122            
3123             $d->merge;
3124             is_deeply $a->print, <
3125             Key Value
3126             a
3127             b
3128             c
3129             d
3130             e
3131             f
3132             g
3133             h
3134             i
3135             j
3136             END
3137            
3138              
3139             =head2 split($parent)
3140              
3141             Make the specified parent a grandparent of each of its children by interposing a copy of the specified parent between the specified parent and each of its children. Return the specified parent.
3142              
3143             Parameter Description
3144             1 $parent Parent to make into a grand parent
3145              
3146             B
3147              
3148              
3149             my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j) = fromLetters 'b(c)d(efgh(i(j)))';
3150            
3151             is_deeply $a->print, <
3152             Key Value
3153             a
3154             b
3155             c
3156             d
3157             e
3158             f
3159             g
3160             h
3161             i
3162             j
3163             END
3164            
3165            
3166             $d->split; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
3167              
3168             is_deeply $a->print, <
3169             Key Value
3170             a
3171             b
3172             c
3173             d
3174             d
3175             e
3176             d
3177             f
3178             d
3179             g
3180             d
3181             h
3182             i
3183             j
3184             END
3185            
3186             $f->parent->mergeLikePrev;
3187             is_deeply $a->print, <
3188             Key Value
3189             a
3190             b
3191             c
3192             d
3193             d
3194             e
3195             f
3196             d
3197             g
3198             d
3199             h
3200             i
3201             j
3202             END
3203            
3204             $g->parent->mergeLikeNext;
3205             is_deeply $a->print, <
3206             Key Value
3207             a
3208             b
3209             c
3210             d
3211             d
3212             e
3213             f
3214             d
3215             g
3216             h
3217             i
3218             j
3219             END
3220            
3221             $d->merge;
3222             is_deeply $a->print, <
3223             Key Value
3224             a
3225             b
3226             c
3227             d
3228             e
3229             f
3230             g
3231             h
3232             i
3233             j
3234             END
3235            
3236              
3237             =head1 Traverse
3238              
3239             Traverse a tree.
3240              
3241             =head2 by($tree, $sub)
3242              
3243             Traverse a tree in post-order to process each child with the specified sub and return an array of the results of processing each child. If no sub sub is specified, the children are returned in tree order.
3244              
3245             Parameter Description
3246             1 $tree Tree
3247             2 $sub Optional sub to process each child
3248              
3249             B
3250              
3251              
3252             my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $s, $t, $x, $y, $z) =
3253             fromLetters 'b(c)y(x)z(st)d(efgh(i(j))))';
3254            
3255             is_deeply [$x->context], [$x, $y, $a];
3256            
3257            
3258             is_deeply join(' ', $a->by(sub{$_[0]->key})), "c b x y s t z e f g j i h d a"; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
3259              
3260            
3261             is_deeply join(' ', map{$_->key} $a->by), "c b x y s t z e f g j i h d a"; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
3262              
3263            
3264             is_deeply $a->print, <
3265             Key Value
3266             a
3267             b
3268             c
3269             y
3270             x
3271             z
3272             s
3273             t
3274             d
3275             e
3276             f
3277             g
3278             h
3279             i
3280             j
3281             END
3282            
3283             $z->cut;
3284             is_deeply $a->print, <
3285             Key Value
3286             a
3287             b
3288             c
3289             y
3290             x
3291             d
3292             e
3293             f
3294             g
3295             h
3296             i
3297             j
3298             END
3299            
3300              
3301             =head2 select($tree, $select)
3302              
3303             Select matching children in a tree in post-order. A child can be selected via named value, array of values, a hash of values, a regular expression or a sub reference.
3304              
3305             Parameter Description
3306             1 $tree Tree
3307             2 $select Method to select a child
3308              
3309             B
3310              
3311              
3312             my $a = Tree::Ops::new 'a', 'A';
3313             for(1..2)
3314             {$a->open ('b', "B$_");
3315             $a->single('c', "C$_");
3316             ok $a->activeScope->key eq 'b';
3317             $a->close;
3318             }
3319             $a->single ('d', 'D');
3320             $a->single ('e', 'E');
3321             is_deeply $a->print, <
3322             Key Value
3323             a A
3324             b B1
3325             c C1
3326             b B2
3327             c C2
3328             d D
3329             e E
3330             END
3331            
3332             is_deeply [map{$_->value} $a->by], [qw(C1 B1 C2 B2 D E A)];
3333            
3334             is_deeply $a->lastMost->prev->prev->first->key, 'c';
3335             is_deeply $a->first->next->last->parent->first->value, 'C2';
3336            
3337            
3338             is_deeply [map{$_->value} $a->select('b')], [qw(B1 B2)]; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
3339              
3340            
3341             is_deeply [map{$_->value} $a->select(qr(b|c))], [qw(B1 C1 B2 C2)]; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
3342              
3343            
3344             is_deeply [map{$_->value} $a->select(sub{$_[0] eq 'd'})], [qw(D)]; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
3345              
3346            
3347             $a->first->next->stepEnd->stepEnd->first->next->stepBack;
3348             is_deeply $a->print, <
3349             Key Value
3350             a A
3351             b B1
3352             c C1
3353             b B2
3354             d D
3355             c C2
3356             e E
3357             END
3358            
3359              
3360             =head1 Partitions
3361              
3362             Various partitions of the tree
3363              
3364             =head2 leaves($tree)
3365              
3366             The set of all children without further children, i.e. each leaf of the tree.
3367              
3368             Parameter Description
3369             1 $tree Tree
3370              
3371             B
3372              
3373              
3374             my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $x, $y) =
3375             fromLetters 'b(c)y(x)d(efgh(i(j)))';
3376            
3377             is_deeply $a->print, <
3378             Key Value
3379             a
3380             b
3381             c
3382             y
3383             x
3384             d
3385             e
3386             f
3387             g
3388             h
3389             i
3390             j
3391             END
3392            
3393             is_deeply $a->xml,
3394             '';
3395            
3396            
3397             is_deeply [$c, $x, $e, $f, $g, $j], [$a->leaves]; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
3398              
3399             is_deeply [$a, $b, $y, $d, $h, $i], [$a->parentsPreOrder];
3400             is_deeply [$b, $y, $i, $h, $d, $a], [$a->parentsPostOrder];
3401             is_deeply [$a->parents], [$a->parentsPostOrder];
3402            
3403             is_deeply [$a, $d, $h, $i, $y, $b], [$a->parentsReversePreOrder];
3404             is_deeply [$i, $h, $d, $y, $b, $a], [$a->parentsReversePostOrder];
3405            
3406             ok !$j->parents;
3407            
3408             ok $a->lastMost == $j;
3409             ok !$a->prevMost;
3410             ok $j->prevMost == $g;
3411             ok $i->prevMost == $g;
3412             ok $h->prevMost == $g;
3413             ok $g->prevMost == $f;
3414             ok $f->prevMost == $e;
3415             ok $e->prevMost == $x;
3416             ok $d->prevMost == $x;
3417             ok $x->prevMost == $c;
3418             ok $y->prevMost == $c;
3419             ok !$c->prevMost;
3420             ok !$b->prevMost;
3421             ok !$a->prevMost;
3422            
3423             ok $a->firstMost == $c;
3424             ok $a->nextMost == $c;
3425             ok $b->nextMost == $c;
3426             ok $c->nextMost == $x;
3427             ok $y->nextMost == $x;
3428             ok $x->nextMost == $e;
3429             ok $d->nextMost == $e;
3430             ok $e->nextMost == $f;
3431             ok $f->nextMost == $g;
3432             ok $g->nextMost == $j;
3433             ok $h->nextMost == $j;
3434             ok $i->nextMost == $j;
3435             ok !$j->nextMost;
3436            
3437             ok $i->topMost == $a;
3438            
3439              
3440             =head2 parentsPreOrder($tree)
3441              
3442             The set of all parents in the tree, i.e. each non leaf of the tree, i.e the interior of the tree in normal pre-order.
3443              
3444             Parameter Description
3445             1 $tree Tree
3446              
3447             B
3448              
3449              
3450             my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $x, $y) =
3451             fromLetters 'b(c)y(x)d(efgh(i(j)))';
3452            
3453             is_deeply $a->print, <
3454             Key Value
3455             a
3456             b
3457             c
3458             y
3459             x
3460             d
3461             e
3462             f
3463             g
3464             h
3465             i
3466             j
3467             END
3468            
3469             is_deeply $a->xml,
3470             '';
3471            
3472             is_deeply [$c, $x, $e, $f, $g, $j], [$a->leaves];
3473            
3474             is_deeply [$a, $b, $y, $d, $h, $i], [$a->parentsPreOrder]; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
3475              
3476             is_deeply [$b, $y, $i, $h, $d, $a], [$a->parentsPostOrder];
3477             is_deeply [$a->parents], [$a->parentsPostOrder];
3478            
3479             is_deeply [$a, $d, $h, $i, $y, $b], [$a->parentsReversePreOrder];
3480             is_deeply [$i, $h, $d, $y, $b, $a], [$a->parentsReversePostOrder];
3481            
3482             ok !$j->parents;
3483            
3484             ok $a->lastMost == $j;
3485             ok !$a->prevMost;
3486             ok $j->prevMost == $g;
3487             ok $i->prevMost == $g;
3488             ok $h->prevMost == $g;
3489             ok $g->prevMost == $f;
3490             ok $f->prevMost == $e;
3491             ok $e->prevMost == $x;
3492             ok $d->prevMost == $x;
3493             ok $x->prevMost == $c;
3494             ok $y->prevMost == $c;
3495             ok !$c->prevMost;
3496             ok !$b->prevMost;
3497             ok !$a->prevMost;
3498            
3499             ok $a->firstMost == $c;
3500             ok $a->nextMost == $c;
3501             ok $b->nextMost == $c;
3502             ok $c->nextMost == $x;
3503             ok $y->nextMost == $x;
3504             ok $x->nextMost == $e;
3505             ok $d->nextMost == $e;
3506             ok $e->nextMost == $f;
3507             ok $f->nextMost == $g;
3508             ok $g->nextMost == $j;
3509             ok $h->nextMost == $j;
3510             ok $i->nextMost == $j;
3511             ok !$j->nextMost;
3512            
3513             ok $i->topMost == $a;
3514            
3515              
3516             =head2 parentsPostOrder($tree)
3517              
3518             The set of all parents in the tree, i.e. each non leaf of the tree, i.e the interior of the tree in normal post-order.
3519              
3520             Parameter Description
3521             1 $tree Tree
3522              
3523             B
3524              
3525              
3526             my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $x, $y) =
3527             fromLetters 'b(c)y(x)d(efgh(i(j)))';
3528            
3529             is_deeply $a->print, <
3530             Key Value
3531             a
3532             b
3533             c
3534             y
3535             x
3536             d
3537             e
3538             f
3539             g
3540             h
3541             i
3542             j
3543             END
3544            
3545             is_deeply $a->xml,
3546             '';
3547            
3548             is_deeply [$c, $x, $e, $f, $g, $j], [$a->leaves];
3549             is_deeply [$a, $b, $y, $d, $h, $i], [$a->parentsPreOrder];
3550            
3551             is_deeply [$b, $y, $i, $h, $d, $a], [$a->parentsPostOrder]; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
3552              
3553            
3554             is_deeply [$a->parents], [$a->parentsPostOrder]; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
3555              
3556            
3557             is_deeply [$a, $d, $h, $i, $y, $b], [$a->parentsReversePreOrder];
3558             is_deeply [$i, $h, $d, $y, $b, $a], [$a->parentsReversePostOrder];
3559            
3560             ok !$j->parents;
3561            
3562             ok $a->lastMost == $j;
3563             ok !$a->prevMost;
3564             ok $j->prevMost == $g;
3565             ok $i->prevMost == $g;
3566             ok $h->prevMost == $g;
3567             ok $g->prevMost == $f;
3568             ok $f->prevMost == $e;
3569             ok $e->prevMost == $x;
3570             ok $d->prevMost == $x;
3571             ok $x->prevMost == $c;
3572             ok $y->prevMost == $c;
3573             ok !$c->prevMost;
3574             ok !$b->prevMost;
3575             ok !$a->prevMost;
3576            
3577             ok $a->firstMost == $c;
3578             ok $a->nextMost == $c;
3579             ok $b->nextMost == $c;
3580             ok $c->nextMost == $x;
3581             ok $y->nextMost == $x;
3582             ok $x->nextMost == $e;
3583             ok $d->nextMost == $e;
3584             ok $e->nextMost == $f;
3585             ok $f->nextMost == $g;
3586             ok $g->nextMost == $j;
3587             ok $h->nextMost == $j;
3588             ok $i->nextMost == $j;
3589             ok !$j->nextMost;
3590            
3591             ok $i->topMost == $a;
3592            
3593              
3594             =head2 parentsReversePreOrder($tree)
3595              
3596             The set of all parents in the tree, i.e. each non leaf of the tree, i.e the interior of the tree in reverse pre-order.
3597              
3598             Parameter Description
3599             1 $tree Tree
3600              
3601             B
3602              
3603              
3604             my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $x, $y) =
3605             fromLetters 'b(c)y(x)d(efgh(i(j)))';
3606            
3607             is_deeply $a->print, <
3608             Key Value
3609             a
3610             b
3611             c
3612             y
3613             x
3614             d
3615             e
3616             f
3617             g
3618             h
3619             i
3620             j
3621             END
3622            
3623             is_deeply $a->xml,
3624             '';
3625            
3626             is_deeply [$c, $x, $e, $f, $g, $j], [$a->leaves];
3627             is_deeply [$a, $b, $y, $d, $h, $i], [$a->parentsPreOrder];
3628             is_deeply [$b, $y, $i, $h, $d, $a], [$a->parentsPostOrder];
3629             is_deeply [$a->parents], [$a->parentsPostOrder];
3630            
3631            
3632             is_deeply [$a, $d, $h, $i, $y, $b], [$a->parentsReversePreOrder]; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
3633              
3634             is_deeply [$i, $h, $d, $y, $b, $a], [$a->parentsReversePostOrder];
3635            
3636             ok !$j->parents;
3637            
3638             ok $a->lastMost == $j;
3639             ok !$a->prevMost;
3640             ok $j->prevMost == $g;
3641             ok $i->prevMost == $g;
3642             ok $h->prevMost == $g;
3643             ok $g->prevMost == $f;
3644             ok $f->prevMost == $e;
3645             ok $e->prevMost == $x;
3646             ok $d->prevMost == $x;
3647             ok $x->prevMost == $c;
3648             ok $y->prevMost == $c;
3649             ok !$c->prevMost;
3650             ok !$b->prevMost;
3651             ok !$a->prevMost;
3652            
3653             ok $a->firstMost == $c;
3654             ok $a->nextMost == $c;
3655             ok $b->nextMost == $c;
3656             ok $c->nextMost == $x;
3657             ok $y->nextMost == $x;
3658             ok $x->nextMost == $e;
3659             ok $d->nextMost == $e;
3660             ok $e->nextMost == $f;
3661             ok $f->nextMost == $g;
3662             ok $g->nextMost == $j;
3663             ok $h->nextMost == $j;
3664             ok $i->nextMost == $j;
3665             ok !$j->nextMost;
3666            
3667             ok $i->topMost == $a;
3668            
3669              
3670             =head2 parentsReversePostOrder($tree)
3671              
3672             The set of all parents in the tree, i.e. each non leaf of the tree, i.e the interior of the tree in reverse post-order.
3673              
3674             Parameter Description
3675             1 $tree Tree
3676              
3677             B
3678              
3679              
3680             my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $x, $y) =
3681             fromLetters 'b(c)y(x)d(efgh(i(j)))';
3682            
3683             is_deeply $a->print, <
3684             Key Value
3685             a
3686             b
3687             c
3688             y
3689             x
3690             d
3691             e
3692             f
3693             g
3694             h
3695             i
3696             j
3697             END
3698            
3699             is_deeply $a->xml,
3700             '';
3701            
3702             is_deeply [$c, $x, $e, $f, $g, $j], [$a->leaves];
3703             is_deeply [$a, $b, $y, $d, $h, $i], [$a->parentsPreOrder];
3704             is_deeply [$b, $y, $i, $h, $d, $a], [$a->parentsPostOrder];
3705             is_deeply [$a->parents], [$a->parentsPostOrder];
3706            
3707             is_deeply [$a, $d, $h, $i, $y, $b], [$a->parentsReversePreOrder];
3708            
3709             is_deeply [$i, $h, $d, $y, $b, $a], [$a->parentsReversePostOrder]; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
3710              
3711            
3712             ok !$j->parents;
3713            
3714             ok $a->lastMost == $j;
3715             ok !$a->prevMost;
3716             ok $j->prevMost == $g;
3717             ok $i->prevMost == $g;
3718             ok $h->prevMost == $g;
3719             ok $g->prevMost == $f;
3720             ok $f->prevMost == $e;
3721             ok $e->prevMost == $x;
3722             ok $d->prevMost == $x;
3723             ok $x->prevMost == $c;
3724             ok $y->prevMost == $c;
3725             ok !$c->prevMost;
3726             ok !$b->prevMost;
3727             ok !$a->prevMost;
3728            
3729             ok $a->firstMost == $c;
3730             ok $a->nextMost == $c;
3731             ok $b->nextMost == $c;
3732             ok $c->nextMost == $x;
3733             ok $y->nextMost == $x;
3734             ok $x->nextMost == $e;
3735             ok $d->nextMost == $e;
3736             ok $e->nextMost == $f;
3737             ok $f->nextMost == $g;
3738             ok $g->nextMost == $j;
3739             ok $h->nextMost == $j;
3740             ok $i->nextMost == $j;
3741             ok !$j->nextMost;
3742            
3743             ok $i->topMost == $a;
3744            
3745              
3746             =head2 parents($tree)
3747              
3748             The set of all parents in the tree, i.e. each non leaf of the tree, i.e the interior of the tree in normal post-order.
3749              
3750             Parameter Description
3751             1 $tree Tree
3752              
3753             B
3754              
3755              
3756             my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $x, $y) =
3757             fromLetters 'b(c)y(x)d(efgh(i(j)))';
3758            
3759             is_deeply $a->print, <
3760             Key Value
3761             a
3762             b
3763             c
3764             y
3765             x
3766             d
3767             e
3768             f
3769             g
3770             h
3771             i
3772             j
3773             END
3774            
3775             is_deeply $a->xml,
3776             '';
3777            
3778             is_deeply [$c, $x, $e, $f, $g, $j], [$a->leaves];
3779            
3780             is_deeply [$a, $b, $y, $d, $h, $i], [$a->parentsPreOrder]; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
3781              
3782            
3783             is_deeply [$b, $y, $i, $h, $d, $a], [$a->parentsPostOrder]; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
3784              
3785            
3786             is_deeply [$a->parents], [$a->parentsPostOrder]; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
3787              
3788            
3789            
3790             is_deeply [$a, $d, $h, $i, $y, $b], [$a->parentsReversePreOrder]; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
3791              
3792            
3793             is_deeply [$i, $h, $d, $y, $b, $a], [$a->parentsReversePostOrder]; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
3794              
3795            
3796            
3797             ok !$j->parents; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
3798              
3799            
3800             ok $a->lastMost == $j;
3801             ok !$a->prevMost;
3802             ok $j->prevMost == $g;
3803             ok $i->prevMost == $g;
3804             ok $h->prevMost == $g;
3805             ok $g->prevMost == $f;
3806             ok $f->prevMost == $e;
3807             ok $e->prevMost == $x;
3808             ok $d->prevMost == $x;
3809             ok $x->prevMost == $c;
3810             ok $y->prevMost == $c;
3811             ok !$c->prevMost;
3812             ok !$b->prevMost;
3813             ok !$a->prevMost;
3814            
3815             ok $a->firstMost == $c;
3816             ok $a->nextMost == $c;
3817             ok $b->nextMost == $c;
3818             ok $c->nextMost == $x;
3819             ok $y->nextMost == $x;
3820             ok $x->nextMost == $e;
3821             ok $d->nextMost == $e;
3822             ok $e->nextMost == $f;
3823             ok $f->nextMost == $g;
3824             ok $g->nextMost == $j;
3825             ok $h->nextMost == $j;
3826             ok $i->nextMost == $j;
3827             ok !$j->nextMost;
3828            
3829             ok $i->topMost == $a;
3830            
3831              
3832             =head1 Order
3833              
3834             Check the order and relative position of children in a tree.
3835              
3836             =head2 above($first, $second)
3837              
3838             Return the first child if it is above the second child else return B.
3839              
3840             Parameter Description
3841             1 $first First child
3842             2 $second Second child
3843              
3844             B
3845              
3846              
3847             my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $k, $l, $m, $n) =
3848             fromLetters('b(c(d(efgh(i(j)k)l)m)n');
3849            
3850             is_deeply $a->print, <
3851             Key Value
3852             a
3853             b
3854             c
3855             d
3856             e
3857             f
3858             g
3859             h
3860             i
3861             j
3862             k
3863             l
3864             m
3865             n
3866             END
3867            
3868            
3869             ok $c->above($j) == $c; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
3870              
3871            
3872             ok !$m->above($j); # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
3873              
3874            
3875             ok $i->below($b) == $i;
3876             ok !$i->below($n);
3877            
3878             ok $n->after($e) == $n;
3879             ok !$k->after($c);
3880            
3881             ok $c->before($n) == $c;
3882             ok !$c->before($m);
3883            
3884             is_deeply [map{$_->key} $j->lineage($d)], [qw(j i h d)];
3885             ok !$d->lineage($m);
3886            
3887              
3888             =head2 below($first, $second)
3889              
3890             Return the first child if it is below the second child else return B.
3891              
3892             Parameter Description
3893             1 $first First child
3894             2 $second Second child
3895              
3896             B
3897              
3898              
3899             my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $k, $l, $m, $n) =
3900             fromLetters('b(c(d(efgh(i(j)k)l)m)n');
3901            
3902             is_deeply $a->print, <
3903             Key Value
3904             a
3905             b
3906             c
3907             d
3908             e
3909             f
3910             g
3911             h
3912             i
3913             j
3914             k
3915             l
3916             m
3917             n
3918             END
3919            
3920             ok $c->above($j) == $c;
3921             ok !$m->above($j);
3922            
3923            
3924             ok $i->below($b) == $i; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
3925              
3926            
3927             ok !$i->below($n); # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
3928              
3929            
3930             ok $n->after($e) == $n;
3931             ok !$k->after($c);
3932            
3933             ok $c->before($n) == $c;
3934             ok !$c->before($m);
3935            
3936             is_deeply [map{$_->key} $j->lineage($d)], [qw(j i h d)];
3937             ok !$d->lineage($m);
3938            
3939              
3940             =head2 after($first, $second)
3941              
3942             Return the first child if it occurs strictly after the second child in the tree or else B if the first child is L, L or L the second child.
3943              
3944             Parameter Description
3945             1 $first First child
3946             2 $second Second child
3947              
3948             B
3949              
3950              
3951             my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $k, $l, $m, $n) =
3952             fromLetters('b(c(d(efgh(i(j)k)l)m)n');
3953            
3954             is_deeply $a->print, <
3955             Key Value
3956             a
3957             b
3958             c
3959             d
3960             e
3961             f
3962             g
3963             h
3964             i
3965             j
3966             k
3967             l
3968             m
3969             n
3970             END
3971            
3972             ok $c->above($j) == $c;
3973             ok !$m->above($j);
3974            
3975             ok $i->below($b) == $i;
3976             ok !$i->below($n);
3977            
3978            
3979             ok $n->after($e) == $n; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
3980              
3981            
3982             ok !$k->after($c); # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
3983              
3984            
3985             ok $c->before($n) == $c;
3986             ok !$c->before($m);
3987            
3988             is_deeply [map{$_->key} $j->lineage($d)], [qw(j i h d)];
3989             ok !$d->lineage($m);
3990            
3991              
3992             =head2 before($first, $second)
3993              
3994             Return the first child if it occurs strictly before the second child in the tree or else B if the first child is L, L or L the second child.
3995              
3996             Parameter Description
3997             1 $first First child
3998             2 $second Second child
3999              
4000             B
4001              
4002              
4003             my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $k, $l, $m, $n) =
4004             fromLetters('b(c(d(efgh(i(j)k)l)m)n');
4005            
4006             is_deeply $a->print, <
4007             Key Value
4008             a
4009             b
4010             c
4011             d
4012             e
4013             f
4014             g
4015             h
4016             i
4017             j
4018             k
4019             l
4020             m
4021             n
4022             END
4023            
4024             ok $c->above($j) == $c;
4025             ok !$m->above($j);
4026            
4027             ok $i->below($b) == $i;
4028             ok !$i->below($n);
4029            
4030             ok $n->after($e) == $n;
4031             ok !$k->after($c);
4032            
4033            
4034             ok $c->before($n) == $c; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
4035              
4036            
4037             ok !$c->before($m); # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
4038              
4039            
4040             is_deeply [map{$_->key} $j->lineage($d)], [qw(j i h d)];
4041             ok !$d->lineage($m);
4042            
4043              
4044             =head1 Paths
4045              
4046             Find paths between nodes
4047              
4048             =head2 path($child)
4049              
4050             Return the list of zero based child indexes for the path from the root of the tree containing the specified child to the specified child for use by the L method.
4051              
4052             Parameter Description
4053             1 $child Child
4054              
4055             B
4056              
4057              
4058             my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j) = fromLetters 'b(cd(e(fg)h)i)j';
4059            
4060             is_deeply $a->print, <
4061             Key Value
4062             a
4063             b
4064             c
4065             d
4066             e
4067             f
4068             g
4069             h
4070             i
4071             j
4072             END
4073            
4074             ok $a->go(0,1,0,1) == $g;
4075             ok $d->go(0,0) == $f;
4076            
4077            
4078             is_deeply [$e->path], [0,1,0]; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
4079              
4080            
4081             is_deeply [$g->pathFrom($d)], [0,1]; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
4082              
4083            
4084             is_deeply $b->dup->print, <
4085             Key Value
4086             b
4087             c
4088             d
4089             e
4090             f
4091             g
4092             h
4093             i
4094             END
4095            
4096             my $B = $b->transcribe;
4097            
4098             $b->by(sub
4099             {my ($c) = @_;
4100            
4101             my @path = $c->pathFrom($b); # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
4102              
4103            
4104             my $C = $B->go(@path); # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
4105              
4106             is_deeply $c->key, $C->key;
4107             is_deeply $c->{transcribedTo}, $C;
4108             is_deeply $C->{transcribedFrom}, $c;
4109             });
4110            
4111             is_deeply $B->print, <
4112             Key Value
4113             b
4114             c
4115             d
4116             e
4117             f
4118             g
4119             h
4120             i
4121             END
4122            
4123              
4124             =head2 pathFrom($child, $ancestor)
4125              
4126             Return the list of zero based child indexes for the path from the specified ancestor to the specified child for use by the L method else confess if the ancestor is not, in fact, an ancestor.
4127              
4128             Parameter Description
4129             1 $child Child
4130             2 $ancestor Ancestor
4131              
4132             B
4133              
4134              
4135             my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j) = fromLetters 'b(cd(e(fg)h)i)j';
4136            
4137             is_deeply $a->print, <
4138             Key Value
4139             a
4140             b
4141             c
4142             d
4143             e
4144             f
4145             g
4146             h
4147             i
4148             j
4149             END
4150            
4151             ok $a->go(0,1,0,1) == $g;
4152             ok $d->go(0,0) == $f;
4153            
4154             is_deeply [$e->path], [0,1,0];
4155            
4156             is_deeply [$g->pathFrom($d)], [0,1]; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
4157              
4158            
4159             is_deeply $b->dup->print, <
4160             Key Value
4161             b
4162             c
4163             d
4164             e
4165             f
4166             g
4167             h
4168             i
4169             END
4170            
4171             my $B = $b->transcribe;
4172            
4173             $b->by(sub
4174             {my ($c) = @_;
4175            
4176             my @path = $c->pathFrom($b); # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
4177              
4178             my $C = $B->go(@path);
4179             is_deeply $c->key, $C->key;
4180             is_deeply $c->{transcribedTo}, $C;
4181             is_deeply $C->{transcribedFrom}, $c;
4182             });
4183            
4184             is_deeply $B->print, <
4185             Key Value
4186             b
4187             c
4188             d
4189             e
4190             f
4191             g
4192             h
4193             i
4194             END
4195            
4196              
4197             =head2 siblingsBefore($child)
4198              
4199             Return a list of siblings before the specified child.
4200              
4201             Parameter Description
4202             1 $child Child
4203              
4204             B
4205              
4206              
4207             my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j) = fromLetters 'b(cde(f)ghi)j';
4208             is_deeply $a->print, <
4209             Key Value
4210             a
4211             b
4212             c
4213             d
4214             e
4215             f
4216             g
4217             h
4218             i
4219             j
4220             END
4221            
4222             is_deeply [$d->siblingsStrictlyBetween($h)], [$e, $g];
4223             is_deeply [$d->siblingsAfter], [$e, $g, $h, $i];
4224            
4225             is_deeply [$g->siblingsBefore], [$c, $d, $e]; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
4226              
4227             eval {$e->siblingsStrictlyBetween($f)};
4228             ok $@ =~ m(Must be siblings);
4229            
4230              
4231             =head2 siblingsAfter($child)
4232              
4233             Return a list of siblings after the specified child.
4234              
4235             Parameter Description
4236             1 $child Child
4237              
4238             B
4239              
4240              
4241             my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j) = fromLetters 'b(cde(f)ghi)j';
4242             is_deeply $a->print, <
4243             Key Value
4244             a
4245             b
4246             c
4247             d
4248             e
4249             f
4250             g
4251             h
4252             i
4253             j
4254             END
4255            
4256             is_deeply [$d->siblingsStrictlyBetween($h)], [$e, $g];
4257            
4258             is_deeply [$d->siblingsAfter], [$e, $g, $h, $i]; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
4259              
4260             is_deeply [$g->siblingsBefore], [$c, $d, $e];
4261             eval {$e->siblingsStrictlyBetween($f)};
4262             ok $@ =~ m(Must be siblings);
4263            
4264              
4265             =head2 siblingsStrictlyBetween($start, $finish)
4266              
4267             Return a list of the siblings strictly between two children of the same parent else return B.
4268              
4269             Parameter Description
4270             1 $start Start child
4271             2 $finish Finish child
4272              
4273             B
4274              
4275              
4276             my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j) = fromLetters 'b(cde(f)ghi)j';
4277             is_deeply $a->print, <
4278             Key Value
4279             a
4280             b
4281             c
4282             d
4283             e
4284             f
4285             g
4286             h
4287             i
4288             j
4289             END
4290            
4291            
4292             is_deeply [$d->siblingsStrictlyBetween($h)], [$e, $g]; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
4293              
4294             is_deeply [$d->siblingsAfter], [$e, $g, $h, $i];
4295             is_deeply [$g->siblingsBefore], [$c, $d, $e];
4296            
4297             eval {$e->siblingsStrictlyBetween($f)}; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
4298              
4299             ok $@ =~ m(Must be siblings);
4300            
4301              
4302             =head2 lineage($child, $ancestor)
4303              
4304             Return the path from the specified child to the specified ancestor else return B if the child is not a descendant of the ancestor.
4305              
4306             Parameter Description
4307             1 $child Child
4308             2 $ancestor Ancestor
4309              
4310             B
4311              
4312              
4313             my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $k, $l, $m, $n) =
4314             fromLetters('b(c(d(efgh(i(j)k)l)m)n');
4315            
4316             is_deeply $a->print, <
4317             Key Value
4318             a
4319             b
4320             c
4321             d
4322             e
4323             f
4324             g
4325             h
4326             i
4327             j
4328             k
4329             l
4330             m
4331             n
4332             END
4333            
4334             ok $c->above($j) == $c;
4335             ok !$m->above($j);
4336            
4337             ok $i->below($b) == $i;
4338             ok !$i->below($n);
4339            
4340             ok $n->after($e) == $n;
4341             ok !$k->after($c);
4342            
4343             ok $c->before($n) == $c;
4344             ok !$c->before($m);
4345            
4346            
4347             is_deeply [map{$_->key} $j->lineage($d)], [qw(j i h d)]; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
4348              
4349            
4350             ok !$d->lineage($m); # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
4351              
4352            
4353              
4354             =head2 nextPreOrderPath($start)
4355              
4356             Return a list of children visited between the specified child and the next child in pre-order.
4357              
4358             Parameter Description
4359             1 $start The child at the start of the path
4360              
4361             B
4362              
4363              
4364             my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $k, $l, $m, $n, $o, $p, $q, $r) =
4365             fromLetters 'b(c(d(e(fg)hi(j(kl)m)n)op)q)r';
4366             my @p = [$a];
4367            
4368             for(1..99)
4369            
4370             {my @n = $p[-1][-1]->nextPreOrderPath; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
4371              
4372             last unless @n;
4373             push @p, [@n];
4374             }
4375            
4376             is_deeply $a->print, <
4377             Key Value
4378             a
4379             b
4380             c
4381             d
4382             e
4383             f
4384             g
4385             h
4386             i
4387             j
4388             k
4389             l
4390             m
4391             n
4392             o
4393             p
4394             q
4395             r
4396             END
4397            
4398             my @pre = map{[map{$_->key} @$_]} @p;
4399             is_deeply scalar(@pre), scalar(['a'..'r']->@*);
4400             is_deeply [@pre],
4401             [["a"],
4402             ["b"],
4403             ["c"],
4404             ["d"],
4405             ["e"],
4406             ["f"],
4407             ["g"],
4408             ["e", "h"],
4409             ["i"],
4410             ["j"],
4411             ["k"],
4412             ["l"],
4413             ["j", "m"],
4414             ["i", "n"],
4415             ["d", "o"],
4416             ["p"],
4417             ["c", "q"],
4418             ["b", "r"]];
4419            
4420              
4421             =head2 nextPostOrderPath($start)
4422              
4423             Return a list of children visited between the specified child and the next child in post-order.
4424              
4425             Parameter Description
4426             1 $start The child at the start of the path
4427              
4428             B
4429              
4430              
4431             my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $k, $l, $m, $n, $o, $p, $q, $r) =
4432             fromLetters 'b(c(d(e(fg)hi(j(kl)m)n)op)q)r';
4433            
4434             my @n = $a;
4435             my @p;
4436             for(1..99)
4437            
4438             {@n = $n[-1]->nextPostOrderPath; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
4439              
4440             last unless @n;
4441             push @p, [@n];
4442             last if $n[-1] == $a;
4443             }
4444            
4445             is_deeply $a->print, <
4446             Key Value
4447             a
4448             b
4449             c
4450             d
4451             e
4452             f
4453             g
4454             h
4455             i
4456             j
4457             k
4458             l
4459             m
4460             n
4461             o
4462             p
4463             q
4464             r
4465             END
4466            
4467             my @post = map{[map{$_->key} @$_]} @p;
4468             is_deeply scalar(@post), scalar(['a'..'r']->@*);
4469             is_deeply [@post],
4470             [["b" .. "f"],
4471             ["g"],
4472             ["e"],
4473             ["h"],
4474             ["i", "j", "k"],
4475             ["l"],
4476             ["j"],
4477             ["m"],
4478             ["i"],
4479             ["n"],
4480             ["d"],
4481             ["o"],
4482             ["p"],
4483             ["c"],
4484             ["q"],
4485             ["b"],
4486             ["r"],
4487             ["a"]];
4488            
4489              
4490             =head2 prevPostOrderPath($start)
4491              
4492             Return a list of children visited between the specified child and the previous child in post-order.
4493              
4494             Parameter Description
4495             1 $start The child at the start of the path
4496              
4497             B
4498              
4499              
4500             my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $k, $l, $m, $n, $o, $p, $q, $r) =
4501             fromLetters 'b(c(d(e(fg)hi(j(kl)m)n)op)q)r';
4502             my @p = [$a];
4503            
4504             for(1..99)
4505            
4506             {my @n = $p[-1][-1]->prevPostOrderPath; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
4507              
4508             last unless @n;
4509             push @p, [@n];
4510             }
4511            
4512             is_deeply $a->print, <
4513             Key Value
4514             a
4515             b
4516             c
4517             d
4518             e
4519             f
4520             g
4521             h
4522             i
4523             j
4524             k
4525             l
4526             m
4527             n
4528             o
4529             p
4530             q
4531             r
4532             END
4533            
4534             my @post = map{[map{$_->key} @$_]} @p;
4535             is_deeply scalar(@post), scalar(['a'..'r']->@*);
4536             is_deeply [@post],
4537             [["a"],
4538             ["r"],
4539             ["b"],
4540             ["q"],
4541             ["c"],
4542             ["p"],
4543             ["o"],
4544             ["d"],
4545             ["n"],
4546             ["i"],
4547             ["m"],
4548             ["j"],
4549             ["l"],
4550             ["k"],
4551             ["j", "i", "h"],
4552             ["e"],
4553             ["g"],
4554             ["f"]];
4555            
4556              
4557             =head2 prevPreOrderPath($start)
4558              
4559             Return a list of children visited between the specified child and the previous child in pre-order.
4560              
4561             Parameter Description
4562             1 $start The child at the start of the path
4563              
4564             B
4565              
4566              
4567             my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $k, $l, $m, $n, $o, $p, $q, $r) =
4568             fromLetters 'b(c(d(e(fg)hi(j(kl)m)n)op)q)r';
4569            
4570             my @n = $a;
4571             my @p;
4572             for(1..99)
4573            
4574             {@n = $n[-1]->prevPreOrderPath; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
4575              
4576             last unless @n;
4577             push @p, [@n];
4578             last if $n[-1] == $a;
4579             }
4580            
4581             is_deeply $a->print, <
4582             Key Value
4583             a
4584             b
4585             c
4586             d
4587             e
4588             f
4589             g
4590             h
4591             i
4592             j
4593             k
4594             l
4595             m
4596             n
4597             o
4598             p
4599             q
4600             r
4601             END
4602            
4603             my @pre = map{[map{$_->key} @$_]} @p;
4604             is_deeply scalar(@pre), scalar(['a'..'r']->@*);
4605             is_deeply [@pre],
4606             [["r"],
4607             ["b", "q"],
4608             ["c", "p"],
4609             ["o"],
4610             ["d", "n"],
4611             ["i", "m"],
4612             ["j", "l"],
4613             ["k"],
4614             ["j"],
4615             ["i"],
4616             ["h"],
4617             ["e", "g"],
4618             ["f"],
4619             ["e"],
4620             ["d"],
4621             ["c"],
4622             ["b"],
4623             ["a"]];
4624            
4625              
4626             =head1 Print
4627              
4628             Print a tree.
4629              
4630             =head2 printPreOrder($tree, $print)
4631              
4632             Print tree in normal pre-order.
4633              
4634             Parameter Description
4635             1 $tree Tree
4636             2 $print Optional print method
4637              
4638             B
4639              
4640              
4641             my ($a, $b, $c, $d) = fromLetters 'b(c)d';
4642             my sub test(@) {join ' ', map{join '', $_->key} @_}
4643            
4644            
4645             is_deeply $a->printPreOrder, <
4646              
4647             Key Value
4648             a
4649             b
4650             c
4651             d
4652             END
4653            
4654             is_deeply test($a->nextPreOrderPath), 'b';
4655             is_deeply test($b->nextPreOrderPath), 'c';
4656             is_deeply test($c->nextPreOrderPath), 'b d';
4657             is_deeply test($d->nextPreOrderPath), '';
4658            
4659             is_deeply $a->printPostOrder, <
4660             Key Value
4661             c
4662             b
4663             d
4664             a
4665             END
4666            
4667             is_deeply test($a->nextPostOrderPath), 'b c';
4668             is_deeply test($c->nextPostOrderPath), 'b';
4669             is_deeply test($b->nextPostOrderPath), 'd';
4670             is_deeply test($d->nextPostOrderPath), 'a';
4671            
4672             is_deeply $a->printReversePreOrder, <
4673             Key Value
4674             a
4675             d
4676             b
4677             c
4678             END
4679             is_deeply test($a->prevPreOrderPath), 'd';
4680             is_deeply test($d->prevPreOrderPath), 'b c';
4681             is_deeply test($c->prevPreOrderPath), 'b';
4682             is_deeply test($b->prevPreOrderPath), 'a';
4683            
4684             is_deeply $a->printReversePostOrder, <
4685             Key Value
4686             d
4687             c
4688             b
4689             a
4690             END
4691            
4692             is_deeply test($a->prevPostOrderPath), 'd';
4693             is_deeply test($d->prevPostOrderPath), 'b';
4694             is_deeply test($b->prevPostOrderPath), 'c';
4695             is_deeply test($c->prevPostOrderPath), '';
4696            
4697              
4698             =head2 printPostOrder($tree, $print)
4699              
4700             Print tree in normal post-order.
4701              
4702             Parameter Description
4703             1 $tree Tree
4704             2 $print Optional print method
4705              
4706             B
4707              
4708              
4709             my ($a, $b, $c, $d) = fromLetters 'b(c)d';
4710             my sub test(@) {join ' ', map{join '', $_->key} @_}
4711            
4712             is_deeply $a->printPreOrder, <
4713             Key Value
4714             a
4715             b
4716             c
4717             d
4718             END
4719            
4720             is_deeply test($a->nextPreOrderPath), 'b';
4721             is_deeply test($b->nextPreOrderPath), 'c';
4722             is_deeply test($c->nextPreOrderPath), 'b d';
4723             is_deeply test($d->nextPreOrderPath), '';
4724            
4725            
4726             is_deeply $a->printPostOrder, <
4727              
4728             Key Value
4729             c
4730             b
4731             d
4732             a
4733             END
4734            
4735             is_deeply test($a->nextPostOrderPath), 'b c';
4736             is_deeply test($c->nextPostOrderPath), 'b';
4737             is_deeply test($b->nextPostOrderPath), 'd';
4738             is_deeply test($d->nextPostOrderPath), 'a';
4739            
4740             is_deeply $a->printReversePreOrder, <
4741             Key Value
4742             a
4743             d
4744             b
4745             c
4746             END
4747             is_deeply test($a->prevPreOrderPath), 'd';
4748             is_deeply test($d->prevPreOrderPath), 'b c';
4749             is_deeply test($c->prevPreOrderPath), 'b';
4750             is_deeply test($b->prevPreOrderPath), 'a';
4751            
4752             is_deeply $a->printReversePostOrder, <
4753             Key Value
4754             d
4755             c
4756             b
4757             a
4758             END
4759            
4760             is_deeply test($a->prevPostOrderPath), 'd';
4761             is_deeply test($d->prevPostOrderPath), 'b';
4762             is_deeply test($b->prevPostOrderPath), 'c';
4763             is_deeply test($c->prevPostOrderPath), '';
4764            
4765              
4766             =head2 printReversePreOrder($tree, $print)
4767              
4768             Print tree in reverse pre-order
4769              
4770             Parameter Description
4771             1 $tree Tree
4772             2 $print Optional print method
4773              
4774             B
4775              
4776              
4777             my ($a, $b, $c, $d) = fromLetters 'b(c)d';
4778             my sub test(@) {join ' ', map{join '', $_->key} @_}
4779            
4780             is_deeply $a->printPreOrder, <
4781             Key Value
4782             a
4783             b
4784             c
4785             d
4786             END
4787            
4788             is_deeply test($a->nextPreOrderPath), 'b';
4789             is_deeply test($b->nextPreOrderPath), 'c';
4790             is_deeply test($c->nextPreOrderPath), 'b d';
4791             is_deeply test($d->nextPreOrderPath), '';
4792            
4793             is_deeply $a->printPostOrder, <
4794             Key Value
4795             c
4796             b
4797             d
4798             a
4799             END
4800            
4801             is_deeply test($a->nextPostOrderPath), 'b c';
4802             is_deeply test($c->nextPostOrderPath), 'b';
4803             is_deeply test($b->nextPostOrderPath), 'd';
4804             is_deeply test($d->nextPostOrderPath), 'a';
4805            
4806            
4807             is_deeply $a->printReversePreOrder, <
4808              
4809             Key Value
4810             a
4811             d
4812             b
4813             c
4814             END
4815             is_deeply test($a->prevPreOrderPath), 'd';
4816             is_deeply test($d->prevPreOrderPath), 'b c';
4817             is_deeply test($c->prevPreOrderPath), 'b';
4818             is_deeply test($b->prevPreOrderPath), 'a';
4819            
4820             is_deeply $a->printReversePostOrder, <
4821             Key Value
4822             d
4823             c
4824             b
4825             a
4826             END
4827            
4828             is_deeply test($a->prevPostOrderPath), 'd';
4829             is_deeply test($d->prevPostOrderPath), 'b';
4830             is_deeply test($b->prevPostOrderPath), 'c';
4831             is_deeply test($c->prevPostOrderPath), '';
4832            
4833              
4834             =head2 printReversePostOrder($tree, $print)
4835              
4836             Print tree in reverse post-order
4837              
4838             Parameter Description
4839             1 $tree Tree
4840             2 $print Optional print method
4841              
4842             B
4843              
4844              
4845             my ($a, $b, $c, $d) = fromLetters 'b(c)d';
4846             my sub test(@) {join ' ', map{join '', $_->key} @_}
4847            
4848             is_deeply $a->printPreOrder, <
4849             Key Value
4850             a
4851             b
4852             c
4853             d
4854             END
4855            
4856             is_deeply test($a->nextPreOrderPath), 'b';
4857             is_deeply test($b->nextPreOrderPath), 'c';
4858             is_deeply test($c->nextPreOrderPath), 'b d';
4859             is_deeply test($d->nextPreOrderPath), '';
4860            
4861             is_deeply $a->printPostOrder, <
4862             Key Value
4863             c
4864             b
4865             d
4866             a
4867             END
4868            
4869             is_deeply test($a->nextPostOrderPath), 'b c';
4870             is_deeply test($c->nextPostOrderPath), 'b';
4871             is_deeply test($b->nextPostOrderPath), 'd';
4872             is_deeply test($d->nextPostOrderPath), 'a';
4873            
4874             is_deeply $a->printReversePreOrder, <
4875             Key Value
4876             a
4877             d
4878             b
4879             c
4880             END
4881             is_deeply test($a->prevPreOrderPath), 'd';
4882             is_deeply test($d->prevPreOrderPath), 'b c';
4883             is_deeply test($c->prevPreOrderPath), 'b';
4884             is_deeply test($b->prevPreOrderPath), 'a';
4885            
4886            
4887             is_deeply $a->printReversePostOrder, <
4888              
4889             Key Value
4890             d
4891             c
4892             b
4893             a
4894             END
4895            
4896             is_deeply test($a->prevPostOrderPath), 'd';
4897             is_deeply test($d->prevPostOrderPath), 'b';
4898             is_deeply test($b->prevPostOrderPath), 'c';
4899             is_deeply test($c->prevPostOrderPath), '';
4900            
4901              
4902             =head2 print($tree, $print)
4903              
4904             Print tree in normal pre-order.
4905              
4906             Parameter Description
4907             1 $tree Tree
4908             2 $print Optional print method
4909              
4910             B
4911              
4912              
4913             my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $x, $y) =
4914             fromLetters 'b(c)y(x)d(efgh(i(j)))';
4915            
4916            
4917             is_deeply $a->print, <
4918              
4919             Key Value
4920             a
4921             b
4922             c
4923             y
4924             x
4925             d
4926             e
4927             f
4928             g
4929             h
4930             i
4931             j
4932             END
4933            
4934             is_deeply $a->xml,
4935             '';
4936            
4937             is_deeply [$c, $x, $e, $f, $g, $j], [$a->leaves];
4938             is_deeply [$a, $b, $y, $d, $h, $i], [$a->parentsPreOrder];
4939             is_deeply [$b, $y, $i, $h, $d, $a], [$a->parentsPostOrder];
4940             is_deeply [$a->parents], [$a->parentsPostOrder];
4941            
4942             is_deeply [$a, $d, $h, $i, $y, $b], [$a->parentsReversePreOrder];
4943             is_deeply [$i, $h, $d, $y, $b, $a], [$a->parentsReversePostOrder];
4944            
4945             ok !$j->parents;
4946            
4947             ok $a->lastMost == $j;
4948             ok !$a->prevMost;
4949             ok $j->prevMost == $g;
4950             ok $i->prevMost == $g;
4951             ok $h->prevMost == $g;
4952             ok $g->prevMost == $f;
4953             ok $f->prevMost == $e;
4954             ok $e->prevMost == $x;
4955             ok $d->prevMost == $x;
4956             ok $x->prevMost == $c;
4957             ok $y->prevMost == $c;
4958             ok !$c->prevMost;
4959             ok !$b->prevMost;
4960             ok !$a->prevMost;
4961            
4962             ok $a->firstMost == $c;
4963             ok $a->nextMost == $c;
4964             ok $b->nextMost == $c;
4965             ok $c->nextMost == $x;
4966             ok $y->nextMost == $x;
4967             ok $x->nextMost == $e;
4968             ok $d->nextMost == $e;
4969             ok $e->nextMost == $f;
4970             ok $f->nextMost == $g;
4971             ok $g->nextMost == $j;
4972             ok $h->nextMost == $j;
4973             ok $i->nextMost == $j;
4974             ok !$j->nextMost;
4975            
4976             ok $i->topMost == $a;
4977            
4978              
4979             =head2 brackets($tree, $print, $separator)
4980              
4981             Bracketed string representation of a tree.
4982              
4983             Parameter Description
4984             1 $tree Tree
4985             2 $print Optional print method
4986             3 $separator Optional child separator
4987              
4988             B
4989              
4990              
4991             my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $x, $y) =
4992             fromLetters 'b(c)y(x)d(efgh(i(j)))';
4993            
4994             is_deeply $a->print, <
4995             Key Value
4996             a
4997             b
4998             c
4999             y
5000             x
5001             d
5002             e
5003             f
5004             g
5005             h
5006             i
5007             j
5008             END
5009            
5010             is_deeply $a->xml,
5011             '';
5012            
5013             is_deeply [$c, $x, $e, $f, $g, $j], [$a->leaves];
5014             is_deeply [$a, $b, $y, $d, $h, $i], [$a->parentsPreOrder];
5015             is_deeply [$b, $y, $i, $h, $d, $a], [$a->parentsPostOrder];
5016             is_deeply [$a->parents], [$a->parentsPostOrder];
5017            
5018             is_deeply [$a, $d, $h, $i, $y, $b], [$a->parentsReversePreOrder];
5019             is_deeply [$i, $h, $d, $y, $b, $a], [$a->parentsReversePostOrder];
5020            
5021             ok !$j->parents;
5022            
5023             ok $a->lastMost == $j;
5024             ok !$a->prevMost;
5025             ok $j->prevMost == $g;
5026             ok $i->prevMost == $g;
5027             ok $h->prevMost == $g;
5028             ok $g->prevMost == $f;
5029             ok $f->prevMost == $e;
5030             ok $e->prevMost == $x;
5031             ok $d->prevMost == $x;
5032             ok $x->prevMost == $c;
5033             ok $y->prevMost == $c;
5034             ok !$c->prevMost;
5035             ok !$b->prevMost;
5036             ok !$a->prevMost;
5037            
5038             ok $a->firstMost == $c;
5039             ok $a->nextMost == $c;
5040             ok $b->nextMost == $c;
5041             ok $c->nextMost == $x;
5042             ok $y->nextMost == $x;
5043             ok $x->nextMost == $e;
5044             ok $d->nextMost == $e;
5045             ok $e->nextMost == $f;
5046             ok $f->nextMost == $g;
5047             ok $g->nextMost == $j;
5048             ok $h->nextMost == $j;
5049             ok $i->nextMost == $j;
5050             ok !$j->nextMost;
5051            
5052             ok $i->topMost == $a;
5053            
5054              
5055             =head2 xml($tree, $print)
5056              
5057             Print a tree as as xml.
5058              
5059             Parameter Description
5060             1 $tree Tree
5061             2 $print Optional print method
5062              
5063             B
5064              
5065              
5066             my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $x, $y) =
5067             fromLetters 'b(c)y(x)d(efgh(i(j)))';
5068            
5069             is_deeply $a->print, <
5070             Key Value
5071             a
5072             b
5073             c
5074             y
5075             x
5076             d
5077             e
5078             f
5079             g
5080             h
5081             i
5082             j
5083             END
5084            
5085            
5086             is_deeply $a->xml, # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
5087              
5088             '';
5089            
5090             is_deeply [$c, $x, $e, $f, $g, $j], [$a->leaves];
5091             is_deeply [$a, $b, $y, $d, $h, $i], [$a->parentsPreOrder];
5092             is_deeply [$b, $y, $i, $h, $d, $a], [$a->parentsPostOrder];
5093             is_deeply [$a->parents], [$a->parentsPostOrder];
5094            
5095             is_deeply [$a, $d, $h, $i, $y, $b], [$a->parentsReversePreOrder];
5096             is_deeply [$i, $h, $d, $y, $b, $a], [$a->parentsReversePostOrder];
5097            
5098             ok !$j->parents;
5099            
5100             ok $a->lastMost == $j;
5101             ok !$a->prevMost;
5102             ok $j->prevMost == $g;
5103             ok $i->prevMost == $g;
5104             ok $h->prevMost == $g;
5105             ok $g->prevMost == $f;
5106             ok $f->prevMost == $e;
5107             ok $e->prevMost == $x;
5108             ok $d->prevMost == $x;
5109             ok $x->prevMost == $c;
5110             ok $y->prevMost == $c;
5111             ok !$c->prevMost;
5112             ok !$b->prevMost;
5113             ok !$a->prevMost;
5114            
5115             ok $a->firstMost == $c;
5116             ok $a->nextMost == $c;
5117             ok $b->nextMost == $c;
5118             ok $c->nextMost == $x;
5119             ok $y->nextMost == $x;
5120             ok $x->nextMost == $e;
5121             ok $d->nextMost == $e;
5122             ok $e->nextMost == $f;
5123             ok $f->nextMost == $g;
5124             ok $g->nextMost == $j;
5125             ok $h->nextMost == $j;
5126             ok $i->nextMost == $j;
5127             ok !$j->nextMost;
5128            
5129             ok $i->topMost == $a;
5130            
5131              
5132             =head1 Data Structures
5133              
5134             Data structures use by this package.
5135              
5136              
5137             =head2 Tree::Ops Definition
5138              
5139              
5140             Child in the tree.
5141              
5142              
5143              
5144              
5145             =head3 Output fields
5146              
5147              
5148             =head4 children
5149              
5150             Children of this child.
5151              
5152             =head4 key
5153              
5154             Key for this child - any thing that can be compared with the L operator.
5155              
5156             =head4 lastChild
5157              
5158             Last active child chain - enables us to find the currently open scope from the start if the tree.
5159              
5160             =head4 parent
5161              
5162             Parent for this child.
5163              
5164             =head4 value
5165              
5166             Value for this child.
5167              
5168              
5169              
5170             =head1 Private Methods
5171              
5172             =head2 setParentOfChild($child, $parent)
5173              
5174             Set the parent of a child and return the child.
5175              
5176             Parameter Description
5177             1 $child Child
5178             2 $parent Parent
5179              
5180             =head2 indexOfChildInParent($child)
5181              
5182             Get the index of a child within the specified parent.
5183              
5184             Parameter Description
5185             1 $child Child
5186              
5187             =head2 parentsOrdered($tree, $preorder, $reverse)
5188              
5189             The set of all parents in the tree, i.e. each non leaf of the tree, i.e the interior of the tree in the specified order.
5190              
5191             Parameter Description
5192             1 $tree Tree
5193             2 $preorder Pre-order if true else post-order
5194             3 $reverse Reversed if true
5195              
5196             =head2 printTree($tree, $print, $preorder, $reverse)
5197              
5198             String representation as a horizontal tree.
5199              
5200             Parameter Description
5201             1 $tree Tree
5202             2 $print Optional print method
5203             3 $preorder Pre-order
5204             4 $reverse Reverse
5205              
5206              
5207             =head1 Index
5208              
5209              
5210             1 L - Return the first child if it is above the second child else return B.
5211              
5212             2 L - Locate the active scope in a tree.
5213              
5214             3 L - Return the first child if it occurs strictly after the second child in the tree or else B if the first child is L, L or L the second child.
5215              
5216             4 L - Return the first child if it occurs strictly before the second child in the tree or else B if the first child is L, L or L the second child.
5217              
5218             5 L - Return the first child if it is below the second child else return B.
5219              
5220             6 L - Bracketed string representation of a tree.
5221              
5222             7 L - Traverse a tree in post-order to process each child with the specified sub and return an array of the results of processing each child.
5223              
5224             8 L - Close the current scope returning to the previous scope.
5225              
5226             9 L - Get the context of the current child.
5227              
5228             10 L - Cut out a child and all its content and children, return it ready for reinsertion else where.
5229              
5230             11 L - Duplicate a specified parent and all its descendants returning the root of the resulting tree.
5231              
5232             12 L - Return the specified parent if it has no children else B
5233              
5234             13 L - Get the first child under the specified parent.
5235              
5236             14 L - Return the first most descendant child in the tree starting at this parent or else return B if this parent has no children.
5237              
5238             15 L - Create a tree from a string of letters returning the children created in alphabetic order - useful for testing.
5239              
5240             16 L - Return the child at the end of the path starting at the specified parent.
5241              
5242             17 L - Include the specified tree in the currently open scope.
5243              
5244             18 L - Get the index of a child within the specified parent.
5245              
5246             19 L - Return the specified child if that child is first under its parent, else return B.
5247              
5248             20 L - Return the specified child if that child is last under its parent, else return B.
5249              
5250             21 L - Return the specified parent if that parent is the top most parent in the tree.
5251              
5252             22 L - Get the last child under the specified parent.
5253              
5254             23 L - Return the last most descendant child in the tree starting at this parent or else return B if this parent has no children.
5255              
5256             24 L - The set of all children without further children, i.
5257              
5258             25 L - Return the path from the specified child to the specified ancestor else return B if the child is not a descendant of the ancestor.
5259              
5260             26 L - Unwrap the children of the specified parent with the whose L fields L that of their parent.
5261              
5262             27 L - Merge the following sibling of the specified child if that sibling exists and the L data of the two siblings L.
5263              
5264             28 L - Merge the preceding sibling of the specified child if that sibling exists and the L data of the two siblings L.
5265              
5266             29 L - Find the most recent common ancestor of the specified children.
5267              
5268             30 L - Create a new child optionally recording the specified key or value.
5269              
5270             31 L - Get the next sibling following the specified child.
5271              
5272             32 L - Return the next child with no children, i.
5273              
5274             33 L - Return a list of children visited between the specified child and the next child in post-order.
5275              
5276             34 L - Return a list of children visited between the specified child and the next child in pre-order.
5277              
5278             35 L - Add a child and make it the currently active scope into which new children will be added.
5279              
5280             36 L - The set of all parents in the tree, i.
5281              
5282             37 L - The set of all parents in the tree, i.
5283              
5284             38 L - The set of all parents in the tree, i.
5285              
5286             39 L - The set of all parents in the tree, i.
5287              
5288             40 L - The set of all parents in the tree, i.
5289              
5290             41 L - The set of all parents in the tree, i.
5291              
5292             42 L - Return the list of zero based child indexes for the path from the root of the tree containing the specified child to the specified child for use by the L method.
5293              
5294             43 L - Return the list of zero based child indexes for the path from the specified ancestor to the specified child for use by the L method else confess if the ancestor is not, in fact, an ancestor.
5295              
5296             44 L - Get the previous sibling of the specified child.
5297              
5298             45 L - Return the previous child with no children, i.
5299              
5300             46 L - Return a list of children visited between the specified child and the previous child in post-order.
5301              
5302             47 L - Return a list of children visited between the specified child and the previous child in pre-order.
5303              
5304             48 L - Print tree in normal pre-order.
5305              
5306             49 L - Print tree in normal post-order.
5307              
5308             50 L - Print tree in normal pre-order.
5309              
5310             51 L - Print tree in reverse post-order
5311              
5312             52 L - Print tree in reverse pre-order
5313              
5314             53 L - String representation as a horizontal tree.
5315              
5316             54 L - Place a new child first under the specified parent and return the child.
5317              
5318             55 L - Place a new child last under the specified parent and return the child.
5319              
5320             56 L - Place a new child after the specified child.
5321              
5322             57 L - Place a new child before the specified child.
5323              
5324             58 L - Select matching children in a tree in post-order.
5325              
5326             59 L - Set the parent of a child and return the child.
5327              
5328             60 L - Return a list of siblings after the specified child.
5329              
5330             61 L - Return a list of siblings before the specified child.
5331              
5332             62 L - Return a list of the siblings strictly between two children of the same parent else return B.
5333              
5334             63 L - Add one child in the current scope.
5335              
5336             64 L - Return the only child of this parent if the parent has an only child, else B
5337              
5338             65 L - Make the specified parent a grandparent of each of its children by interposing a copy of the specified parent between the specified parent and each of its children.
5339              
5340             66 L - Make the first child of the specified parent the parents previous sibling and return the parent.
5341              
5342             67 L - Make the previous sibling of the specified parent the parents first child and return the parent.
5343              
5344             68 L - Make the next sibling of the specified parent the parents last child and return the parent.
5345              
5346             69 L - Make the last child of the specified parent the parents next sibling and return the parent.
5347              
5348             70 L - Return the top most parent in the tree containing the specified child.
5349              
5350             71 L - Duplicate a specified parent and all its descendants recording the mapping in a temporary {transcribed} field in the tree being transcribed.
5351              
5352             72 L - Unwrap the specified child and return that child.
5353              
5354             73 L - Wrap the specified child with a new parent and return the new parent optionally setting its L and L.
5355              
5356             74 L - Wrap the children of the specified parent with a new intermediate parent that becomes the child of the specified parent, optionally setting the L and the L for the new parent.
5357              
5358             75 L - Print a tree as as xml.
5359              
5360             =head1 Installation
5361              
5362             This module is written in 100% Pure Perl and, thus, it is easy to read,
5363             comprehend, use, modify and install via B:
5364              
5365             sudo cpan install Tree::Ops
5366              
5367             =head1 Author
5368              
5369             L
5370              
5371             L
5372              
5373             =head1 Copyright
5374              
5375             Copyright (c) 2016-2019 Philip R Brenan.
5376              
5377             This module is free software. It may be used, redistributed and/or modified
5378             under the same terms as Perl itself.
5379              
5380             =cut
5381              
5382              
5383              
5384             # Tests and documentation
5385              
5386             sub test
5387 1     1 0 9 {my $p = __PACKAGE__;
5388 1         11 binmode($_, ":utf8") for *STDOUT, *STDERR;
5389 1 50       81 return if eval "eof(${p}::DATA)";
5390 1         60 my $s = eval "join('', <${p}::DATA>)";
5391 1 50       10 $@ and die $@;
5392 1     1   8 eval $s;
  1     1   3  
  1     1   39  
  1     1   5  
  1         3  
  1         32  
  1         5  
  1         3  
  1         19  
  1         869  
  1         70709  
  1         10  
  1         97  
5393 1 50       12 $@ and die $@;
5394 1         170 1
5395             }
5396              
5397             test unless caller;
5398              
5399             1;
5400             # podDocumentation
5401             __DATA__