File Coverage

blib/lib/Tree/Multi.pm
Criterion Covered Total %
statement 420 447 93.9
branch 195 270 72.2
condition 35 48 72.9
subroutine 54 56 96.4
pod 34 36 94.4
total 738 857 86.1


line stmt bran cond sub pod time code
1             #!/usr/bin/perl -I/home/phil/perl/cpan/DataTableText/lib/ -I.
2             #-------------------------------------------------------------------------------
3             # Multi-way tree in Pure Perl with an even or odd number of keys per node.
4             # Philip R Brenan at appaapps dot com, Appa Apps Ltd Inc., 2021
5             #-------------------------------------------------------------------------------
6             # podDocumentation
7             package Tree::Multi;
8             our $VERSION = "20210607";
9 1     1   1815 use warnings FATAL => qw(all);
  1         8  
  1         42  
10 1     1   5 use strict;
  1         2  
  1         36  
11 1     1   6 use Carp qw(confess cluck);
  1         2  
  1         103  
12 1     1   1306 use Data::Dump qw(dump pp);
  1         7906  
  1         66  
13 1     1   5509 use Data::Table::Text qw(:all);
  1         145678  
  1         1726  
14 1     1   13 use feature qw(say current_sub);
  1         3  
  1         6161  
15              
16             our $numberOfKeysPerNode = 3; # Number of keys per node which can be localized because it is ours. The number of keys can be even or odd.
17              
18             #D1 Multi-way Tree # Create and use a multi-way tree.
19              
20             sub new() #P Create a new multi-way tree node.
21 254934     254934 1 7333984 {my () = @_; # Key, $data, parent node, index of link from parent node
22 254934         694918 genHash(__PACKAGE__, # Multi tree node
23             up => undef, # Parent node
24             keys => [], # Array of key items for this node
25             data => [], # Data corresponding to each key
26             node => [], # Child nodes
27             );
28             }
29              
30             sub minimumNumberOfKeys() #P Minimum number of keys per node.
31 1149745     1149745 1 4010052 {int(($numberOfKeysPerNode - 1) / 2)
32             }
33              
34             sub maximumNumberOfKeys() #P Maximum number of keys per node.
35 2763899     2763899 1 13553290 {$numberOfKeysPerNode
36             }
37              
38             sub maximumNumberOfNodes() #P Maximum number of children per parent.
39 126319     126319 1 340489 {$numberOfKeysPerNode + 1
40             }
41              
42             sub full($) #P Confirm that a node is full.
43 0     0 1 0 {my ($tree) = @_; # Tree
44 0 0       0 @_ == 1 or confess;
45 0         0 my $n = $tree->keys->@*;
46 0 0       0 $n <= maximumNumberOfKeys or confess "Keys";
47 0         0 $n == maximumNumberOfKeys
48             }
49              
50             sub halfFull($) #P Confirm that a node is half full.
51 1149745     1149745 1 2222979 {my ($tree) = @_; # Tree
52 1149745 50       1976861 @_ == 1 or confess;
53 1149745         17832051 my $n = $tree->keys->@*;
54 1149745 50       4927410 $n <= maximumNumberOfKeys+1 or confess "Keys";
55 1149745         1771699 $n == minimumNumberOfKeys
56             }
57              
58             sub root($) # Return the root node of a tree.
59 83     83 1 237 {my ($tree) = @_; # Tree
60 83 50       294 confess unless $tree;
61 83         1594 for(; $tree->up; $tree = $tree->up) {}
62 83         774 $tree
63             }
64              
65             sub leaf($) # Confirm that the tree is a leaf.
66 908406     908406 1 1350714 {my ($tree) = @_; # Tree
67 908406 50       1607969 @_ == 1 or confess;
68 908406         14124529 !scalar $tree->node->@* # No children so it must be a leaf
69             }
70              
71             sub reUp($$) #P Reconnect the children to their new parent.
72 118478     118478 1 574913 {my ($tree, $children) = @_; # Tree, children
73 118478 50       236132 @_ > 0 or confess;
74 118478 50       1841422 $tree->keys->@* <= maximumNumberOfKeys or confess "Keys";
75 118478         1940441 $_->up = $tree for @$children; # Connect child to parent
76             }
77              
78             sub splitFullNode($) #P Split a node if it is full.
79 1128164     1128164 1 1682688 {my ($node) = @_; # Node to split
80 1128164 50       1957833 @_ == 1 or confess;
81              
82 1128164 100       17684080 return unless $node->keys->@* == maximumNumberOfKeys; # Only split full nodes
83              
84 126319   66     1991181 my ($p, $l, $r) = ($node->up // $node, new, new); # New child nodes
85 126319         8014126 $l->up = $r->up = $p; # Connect children to parent
86              
87 126319         2497011 my @k = $node->keys->@*;
88 126319         2411354 my @d = $node->data->@*;
89              
90 126319         600791 my $N = int maximumNumberOfNodes / 2; # Split points
91 126319 100       218779 my $n = maximumNumberOfKeys % 2 == 0 ? $N - 1 : $N - 2;
92              
93 126319         2310073 $l->keys = [@k[0..$n]]; # Split keys
94 126319         2401764 $l->data = [@d[0..$n]]; # Split data
95 126319         2411153 $r->keys = [@k[$n+2..$#k]];
96 126319         2340246 $r->data = [@d[$n+2..$#k]];
97              
98 126319 100       2239713 if (my @n = $node->node->@*) # Not a leaf node
99 39018         823013 {reUp $l, $l->node = [@n[0 ..$n+1]];
100 39018         1673879 reUp $r, $r->node = [@n[$n+2..$#n ]];
101             }
102              
103 126319 100       1372858 if ($p != $node) # Not a root node
104 118477         1836624 {my @n = $p->node->@*; # Insert new nodes in parent known to be not full
105 118477         645588 for my $i(keys @n)
106 220024 100       488984 {if ($n[$i] == $node)
107 118477         1901510 {splice $p->keys->@*, $i, 0, $k[$n+1];
108 118477         2354976 splice $p->data->@*, $i, 0, $d[$n+1];
109 118477         2253129 splice $p->node->@*, $i, 1, $l, $r;
110 118477         1094401 return;
111             }
112             }
113 0         0 confess "Should not happen";
114             }
115             else # Root node
116 7842         135090 {$node->keys = [$k[$n+1]];
117 7842         160819 $node->data = [$d[$n+1]];
118 7842         149310 $node->node = [$l, $r];
119             }
120             }
121              
122             sub findAndSplit($$) #P Find a key in a tree splitting full nodes along the path to the key.
123 234992     234992 1 404146 {my ($root, $key) = @_; # Root of tree, key
124 234992 50       449309 @_ == 2 or confess;
125              
126 234992         312954 my $tree = $root; # Start at the root
127              
128 234992         442547 for(0..999) # Step down through the tree
129 893222         1831143 {splitFullNode $tree; # Split any full nodes encountered
130 893222 50       13754982 confess unless my @k = $tree->keys->@*; # We should have at least one key in the tree because we do a special case insert for an empty tree
131              
132 893222 100       5334691 if ($key < $k[0]) # Less than smallest key in node
133 341773 100       5326263 {return (-1, $tree, 0) unless my $n = $tree->node->[0];
134 257913         1175426 $tree = $n;
135 257913         501535 next;
136             }
137              
138 551449 100       972870 if ($key > $k[-1]) # Greater than largest key in node
139 331651 100       5156090 {return (+1, $tree, $#k) unless my $n = $tree->node->[-1];
140 242728         1119700 $tree = $n;
141 242728         462747 next;
142             }
143              
144 219798         460564 for my $i(keys @k) # Search the keys in this node as greater than least key and less than largest key
145 548737         797530 {my $s = $key <=> $k[$i]; # Compare key
146 548737 100       1214486 if ($s == 0) # Found key
    100          
147 50         153 {return (0, $tree, $i);
148             }
149             elsif ($s < 0) # Less than current key
150 219748 100       3575955 {return (-1, $tree, $i) unless my $n = $tree->node->[$i]; # Step through if possible
151 157589         767966 $tree = $n; # Step
152 157589         378589 last;
153             }
154             }
155             }
156              
157 0         0 confess "Should not happen";
158             }
159              
160             sub find($$) # Find a key in a tree returning its associated data or undef if the key does not exist.
161 481951     481951 1 876456 {my ($root, $key) = @_; # Root of tree, key
162 481951 50       967279 @_ == 2 or confess;
163              
164 481951         611883 my $tree = $root; # Start at the root
165              
166 481951         862487 for(0..999) # Step down through the tree
167 1682140 100       26305324 {return undef unless my @k = $tree->keys->@*; # Empty node
168              
169 1679918 100       9987857 if ($key < $k[0]) # Less than smallest key in node
170 581049 100       8953268 {return undef unless $tree = $tree->node->[0];
171 485407         2426302 next;
172             }
173              
174 1098869 100       1836508 if ($key > $k[-1]) # Greater than largest key in node
175 561478 100       8711454 {return undef unless $tree = $tree->node->[-1];
176 454918         2304110 next;
177             }
178              
179 537391         1059776 for my $i(keys @k) # Search the keys in this node
180 1108466         1536565 {my $s = $key <=> $k[$i]; # Compare key
181 1108466 100       5422915 return $tree->data->[$i] if $s == 0; # Found key
182 866978 100       1546965 if ($s < 0) # Less than current key
183 295903 100       4798134 {return undef unless $tree = $tree->node->[$i];
184 259864         1484486 last;
185             }
186             }
187             }
188 0         0 confess "Should not happen";
189             }
190              
191             sub indexInParent($) #P Get the index of a node in its parent.
192 411111     411111 1 661230 {my ($tree) = @_; # Tree
193 411111 50       731542 @_ == 1 or confess; confess unless my $p = $tree->up;
  411111 50       6568999  
194              
195 411111 100       7489013 my @n = $p->node->@*; for my $i(keys @n) {return $i if $n[$i] == $tree}
  411111         2166618  
  987293         2578857  
196 0         0 confess "Should not happen";
197             }
198              
199             sub fillFromLeftOrRight($$) #P Fill a node from the specified sibling.
200 71973     71973 1 137272 {my ($node, $dir) = @_; # Node to fill, node to fill from 0 for left or 1 for right
201 71973 50       145474 @_ == 2 or confess;
202              
203 71973 50       1187618 confess unless my $p = $node->up; # Parent of leaf
204 71973         375685 my $i = indexInParent $node; # Index of leaf in parent
205              
206 71973 100       161234 if ($dir) # Fill from right
207 9260 50       158935 {$i < $p->node->@* - 1 or confess; # Cannot fill from right
208 9260         185068 my $r = $p->node->[$i+1]; # Right sibling
209 9260         176114 push $node->keys->@*, $p->keys->[$i]; $p->keys->[$i] = shift $r->keys->@*; # Transfer key
  9260         201158  
210 9260         199647 push $node->data->@*, $p->data->[$i]; $p->data->[$i] = shift $r->data->@*; # Transfer data
  9260         196767  
211 9260 100       66499 if (!leaf $node) # Transfer node if not a leaf
212 4637         96300 {push $node->node->@*, shift $r->node->@*;
213 4637         100603 $node->node->[-1]->up = $node;
214             }
215             }
216             else # Fill from left
217 62713 50       141312 {$i > 0 or confess; # Cannot fill from left
218 62713         104392 my $I = $i-1;
219 62713         1025006 my $n = $p->node->[$I]; # Left sibling
220 62713         1137343 my $k = $p->keys; my $d = $p->data;
  62713         1112551  
221 62713         1101813 unshift $node->keys->@*, $k->[$I]; $k->[$I] = pop $n->keys->@*; # Transfer key
  62713         1195897  
222 62713         1153333 unshift $node->data->@*, $d->[$I]; $d->[$I] = pop $n->data->@*; # Transfer data
  62713         1149793  
223 62713 100       327274 if (!leaf $node) # Transfer node if not a leaf
224 22719         447511 {unshift $node->node->@*, pop $n->node->@*;
225 22719         472091 $node->node->[0]->up = $node;
226             }
227             }
228             }
229              
230             sub mergeWithLeftOrRight($$) #P Merge two adjacent nodes.
231 116670     116670 1 239336 {my ($n, $dir) = @_; # Node to merge into, node to merge is on right if 1 else left
232 116670 50       252973 @_ == 2 or confess;
233              
234 116670 50       188975 confess unless halfFull($n); # Confirm leaf is half full
235 116670 50       1917578 confess unless my $p = $n->up; # Parent of leaf
236 116670 50 66     573399 confess if halfFull($p) and $p->up; # Parent must have more than the minimum number of keys because we need to remove one unless it is the root of the tree
237              
238 116670         266902 my $i = indexInParent $n; # Index of leaf in parent
239              
240 116670 100       264940 if ($dir) # Merge with right hand sibling
241 22720 50       370667 {$i < $p->node->@* - 1 or confess; # Cannot fill from right
242 22720         125813 my $I = $i+1;
243 22720         364751 my $r = $p->node->[$I]; # Leaf on right
244 22720 50       111137 confess unless halfFull($r); # Confirm right leaf is half full
245 22720         374652 push $n->keys->@*, splice($p->keys->@*, $i, 1), $r->keys->@*; # Transfer keys
246 22720         566341 push $n->data->@*, splice($p->data->@*, $i, 1), $r->data->@*; # Transfer data
247 22720 100       233753 if (!leaf $n) # Children of merged node
248 7264         146402 {push $n->node->@*, $r->node->@*; # Children of merged node
249 7264         153285 reUp $n, $r->node; # Update parent of children of right node
250             }
251 22720         567835 splice $p->node->@*, $I, 1; # Remove link from parent to right child
252             }
253             else # Merge with left hand sibling
254 93950 50       191362 {$i > 0 or confess; # Cannot fill from left
255 93950         146493 my $I = $i-1;
256 93950         1562684 my $l = $p->node->[$I]; # Node on left
257 93950 50       425357 confess unless halfFull($l); # Confirm right leaf is half full
258 93950         1548828 unshift $n->keys->@*, $l->keys->@*, splice $p->keys->@*, $I, 1; # Transfer keys
259 93950         2298565 unshift $n->data->@*, $l->data->@*, splice $p->data->@*, $I, 1; # Transfer data
260 93950 100       858185 if (!leaf $n) # Children of merged node
261 25553         513619 {unshift $n->node->@*, $l->node->@*; # Children of merged node
262 25553         531963 reUp $n, $l->node; # Update parent of children of left node
263             }
264 93950         2245808 splice $p->node->@*, $I, 1; # Remove link from parent to left child
265             }
266             }
267              
268             sub merge($) #P Merge the current node with its sibling.
269 188643     188643 1 326701 {my ($tree) = @_; # Tree
270 188643 100       347655 if (my $i = indexInParent $tree) # Merge with left node
271 156663         2573520 {my $l = $tree->up->node->[$i-1]; # Left node
272 156663 50       3160324 if (halfFull(my $r = $tree))
273 156663 100       298106 {$l->halfFull ? mergeWithLeftOrRight $r, 0 : fillFromLeftOrRight $r, 0; # Merge as left and right nodes are half full
274             }
275             }
276             else
277 31980         526197 {my $r = $tree->up->node->[1]; # Right node
278 31980 50       646983 if (halfFull(my $l = $tree))
279 31980 100       61530 {halfFull($r) ? mergeWithLeftOrRight $l, 1 : fillFromLeftOrRight $l, 1; # Merge as left and right nodes are half full
280             }
281             }
282             }
283              
284             sub mergeOrFill($) #P Make a node larger than a half node.
285 401559     401559 1 1541221 {my ($tree) = @_; # Tree
286 401559 50       771639 @_ == 1 or confess;
287              
288 401559 100       704512 return unless halfFull($tree); # No need to merge of if not a half node
289 196268 50       3186569 confess unless my $p = $tree->up; # Parent exists
290              
291 196268 100 100     3550180 if ($p->up) # Merge or fill parent which is not the root
    100 100        
292 165991         823065 {__SUB__->($p);
293 165991         1427690 merge($tree);
294             }
295             elsif ($p->keys->@* == 1 and halfFull(my $l = $p->node->[0]) # Parent is the root and it only has one key - merge into the child if possible
296             and halfFull(my $r = $p->node->[1]))
297 7625         132793 {$p->keys = $tree->keys = [$l->keys->@*, $p->keys->@*, $r->keys->@*]; # Merge in place to retain addressability
298 7625         226362 $p->data = $tree->data = [$l->data->@*, $p->data->@*, $r->data->@*];
299 7625         203282 $p->node = $tree->node = [$l->node->@*, $r->node->@*];
300              
301 7625         186695 reUp $p, $p->node; # Reconnect children to parent
302             }
303             else # Parent is the root but it has too may keys to merge into both sibling so merge with a sibling if possible
304 22652         437040 {merge($tree);
305             }
306             }
307              
308             sub leftMost($) # Return the left most node below the specified one.
309 81055     81055 1 684645 {my ($tree) = @_; # Tree
310 81055         176298 for(0..999) # Step down through tree
311 132204 100       367913 {return $tree if leaf $tree; # We are on a leaf so we have arrived at the left most node
312 51149         991823 $tree = $tree->node->[0]; # Go left
313             }
314 0         0 confess "Should not happen";
315             }
316              
317             sub rightMost($) # Return the right most node below the specified one.
318 44937     44937 1 242709 {my ($tree) = @_; # Tree
319 44937         108541 for(0..999) # Step down through tree
320 68935 100       200796 {return $tree if leaf $tree; # We are on a leaf so we have arrived at the left most node
321 23998         467684 $tree = $tree->node->[-1]; # Go right
322             }
323 0         0 confess "Should not happen";
324             }
325              
326             sub height($) # Return the height of the tree.
327 476     476 1 824 {my ($tree) = @_; # Tree
328 476         942 for my $n(0..999) # Step down through tree
329 2291 100       8559 {if (leaf $tree) # We are on a leaf
330 476 100       8897 {return $n + 1 if $tree->keys->@*; # We are in a partially full leaf
331 5         38 return $n; # We are on the root and it is empty
332             }
333 1815         35695 $tree = $tree->node->[0];
334             }
335 0         0 confess "Should not happen";
336             }
337              
338             sub depth($) # Return the depth of a node within a tree.
339 25     25 1 159 {my ($tree) = @_; # Tree
340 25 100 100     408 return 0 if !$tree->up and !$tree->keys->@*; # We are at the root and it is empty
341 24         217 for my $n(1..999) # Step down through tree
342 54 100       884 {return $n unless $tree->up; # We are at the root
343 30         524 $tree = $tree->up;
344             }
345 0         0 confess "Should not happen";
346             }
347              
348             sub deleteLeafKey($$) #P Delete a key in a leaf.
349 240493     240493 1 585125 {my ($tree, $i) = @_; # Tree, index to delete at
350 240493 50       476696 @_ == 2 or confess;
351 240493 50       377315 confess "Not a leaf" unless leaf $tree;
352 240493         4567274 my $key = $tree->keys->[$i];
353 240493 100       4409448 mergeOrFill $tree if $tree->up; # Merge and fill unless we are on the root and the root is a leaf
354 240493         4644806 my $k = $tree->keys;
355 240493         1136582 for my $j(keys @$k) # Search for key to delete
356 495185 100       1044261 {if ($$k[$j] == $key)
357 240493         3815611 {splice $tree->keys->@*, $j, 1; # Remove keys
358 240493         4457098 splice $tree->data->@*, $j, 1; # Remove data
359 240493         1134081 return;
360             }
361             }
362             }
363              
364             sub deleteKey($$) #P Delete a key.
365 240493     240493 1 426431 {my ($tree, $i) = @_; # Tree, index to delete at
366 240493 50       486440 @_ == 2 or confess;
367 240493 100       467811 if (leaf $tree) # Delete from a leaf
    100          
368 132259         798919 {deleteLeafKey($tree, $i);
369             }
370             elsif ($i > 0) # Delete from a node
371 43850         890058 {my $l = rightMost $tree->node->[$i]; # Find previous node
372 43850         896849 splice $tree->keys->@*, $i, 1, $l->keys->[-1];
373 43850         950693 splice $tree->data->@*, $i, 1, $l->data->[-1];
374 43850         926807 deleteLeafKey $l, -1 + scalar $l->keys->@*; # Remove leaf key
375             }
376             else # Delete from a node
377 64384         1288073 {my $r = leftMost $tree->node->[1]; # Find previous node
378 64384         1319221 splice $tree->keys->@*, 0, 1, $r->keys->[0];
379 64384         1386949 splice $tree->data->@*, 0, 1, $r->data->[0];
380 64384         478635 deleteLeafKey $r, 0; # Remove leaf key
381             }
382             }
383              
384             sub delete($$) # Find a key in a tree, delete it and return any associated data.
385 240493     240493 1 443699 {my ($root, $key) = @_; # Tree root, key
386 240493 50       456031 @_ == 2 or confess;
387              
388 240493         319634 my $tree = $root;
389 240493         429114 for (0..999)
390 759868         13167282 {my $k = $tree->keys;
391              
392 759868 100       3541981 if ($key < $$k[0]) # Less than smallest key in node
    100          
393 206183 50       3147783 {return undef unless $tree = $tree->node->[0];
394             }
395             elsif ($key > $$k[-1]) # Greater than largest key in node
396 195379 50       3019563 {return undef unless $tree = $tree->node->[-1];
397             }
398             else
399 358306         736631 {for my $i(keys @$k) # Search the keys in this node
400 658373 100       1472232 {if ((my $s = $key <=> $$k[$i]) == 0) # Delete found key
    100          
401 240493         3773638 {my $d = $tree->data->[$i]; # Save data
402 240493         1170181 deleteKey $tree, $i; # Delete the key
403 240493         552848 return $d; # Return data associated with key
404             }
405             elsif ($s < 0) # Less than current key
406 117813 50       1902355 {return undef unless $tree = $tree->node->[$i];
407 117813         623176 last;
408             }
409             }
410             }
411             }
412 0         0 confess "Should not happen";
413             }
414              
415             sub insert($$$) # Insert the specified key and data into a tree.
416 243487     243487 1 492841 {my ($tree, $key, $data) = @_; # Tree, key, data
417 243487 50       485563 @_ == 3 or confess;
418              
419 243487 100 100     3913742 if (!(my $n = $tree->keys->@*)) # Empty tree
    100          
420 2294         53131 {push $tree->keys->@*, $key;
421 2294         48471 push $tree->data->@*, $data;
422 2294         13915 return $tree;
423             }
424             elsif ($n < maximumNumberOfKeys and $tree->node->@* == 0) # Node is root with no children and room for one more key
425 6201         125873 {my $k = $tree->keys;
426 6201         31525 for my $i(reverse keys @$k) # Each key - in reverse due to the preponderance of already sorted data
427 10095 50       38391 {if ((my $s = $key <=> $$k[$i]) == 0) # Key already present
    100          
428 0         0 {$tree->data->[$i]= $data;
429 0         0 return;
430             }
431             elsif ($s > 0) # Insert before greatest smaller key
432 3881         7385 {my $I = $i + 1;
433 3881         62792 splice $tree->keys->@*, $I, 0, $key;
434 3881         74226 splice $tree->data->@*, $I, 0, $data;
435 3881         24205 return;
436             }
437             }
438 2320         39559 unshift $tree->keys->@*, $key; # Insert the key at the start of the block because it is less than all the other keys in the block
439 2320         49685 unshift $tree->data->@*, $data;
440             }
441             else # Insert node
442 234992         1363923 {my ($compare, $node, $index) = findAndSplit $tree, $key; # Check for existing key
443              
444 234992 100       1634714 if ($compare == 0) # Found an equal key whose data we can update
445 50         846 {$node->data->[$index] = $data;
446             }
447             else # We have room for the insert
448 234942 100       480212 {++$index if $compare > 0; # Position at which to insert new key
449 234942         3755729 splice $node->keys->@*, $index, 0, $key;
450 234942         4449849 splice $node->data->@*, $index, 0, $data;
451 234942         1165749 splitFullNode $node # Split if the leaf has got too big
452             }
453             }
454             }
455              
456             sub iterator($) # Make an iterator for a tree.
457 260     260 1 656 {my ($tree) = @_; # Tree
458 260 50       809 @_ == 1 or confess;
459 260         1159 my $i = genHash(__PACKAGE__.'::Iterator', # Iterator
460             tree => $tree, # Tree we are iterating over
461             node => $tree, # Current node within tree
462             pos => undef, # Current position within node
463             key => undef, # Key at this position
464             data => undef, # Data at this position
465             count => 0, # Counter
466             more => 1, # Iteration not yet finished
467             );
468 260         24859 $i->next; # First element if any
469 260         11229 $i # Iterator
470             }
471              
472             sub Tree::Multi::Iterator::next($) # Find the next key.
473 33460     33460   156929 {my ($iter) = @_; # Iterator
474 33460 50       67883 @_ >= 1 or confess;
475 33460 50       515487 confess unless my $C = $iter->node; # Current node required
476              
477 33460         622249 ++$iter->count; # Count the calls to the iterator
478              
479             my $new = sub # Load iterator with latest position
480 33200     33200   209653 {my ($node, $pos) = @_; # Parameters
481 33200         524189 $iter->node = $node;
482 33200   100     638737 $iter->pos = $pos //= 0;
483 33200         580870 $iter->key = $node->keys->[$pos];
484 33200         1126063 $iter->data = $node->data->[$pos]
485 33460         192656 };
486              
487 33460     260   72352 my $done = sub {$iter->more = undef}; # The tree has been completely traversed
  260         4242  
488              
489 33460 100       543762 if (!defined($iter->pos)) # Initial descent
490 260         5609 {my $l = $C->node->[0];
491 260 50       1978 return $l ? &$new($l->leftMost) : $C->keys->@* ? &$new($C) : &$done; # Start node or done if empty tree
    100          
492             }
493              
494             my $up = sub # Iterate up to next node that has not been visited
495 16663     16663   319411 {for(my $n = $C; my $p = $n->up; $n = $n->up)
496 31928         443507 {my $i = indexInParent $n;
497 31928 100       516208 return &$new($p, $i) if $i < $p->keys->@*;
498             }
499 260         6243 &$done # No nodes not visited
500 33200         195884 };
501              
502 33200         540703 my $i = ++$iter->pos;
503 33200 100       140159 if (leaf $C) # Leaf
504 16797 100       327087 {$i < $C->keys->@* ? &$new($C, $i) : &$up;
505             }
506             else # Node
507 16403 50       317700 {$i < $C->node->@* ? &$new($C->node->[$i]->leftMost) : &$up;
508             }
509             }
510              
511             sub reverseIterator($) # Create a reverse iterator for a tree.
512 65     65 1 196 {my ($tree) = @_; # Tree
513 65 50       402 @_ == 1 or confess;
514 65         308 my $i = genHash(__PACKAGE__.'::ReverseIterator', # Iterator
515             tree => root($tree), # Tree we are iterating over
516             node => $tree, # Current node within tree
517             pos => undef, # Current position within node
518             key => undef, # Key at this position
519             data => undef, # Data at this position
520             count => 0, # Counter
521             less => 1, # Iteration not yet finished
522             );
523 65         7552 $i->prev; # Last element if any
524 65         2970 $i # Iterator
525             }
526              
527             sub Tree::Multi::ReverseIterator::prev($) # Find the previous key.
528 2210     2210   10143 {my ($iter) = @_; # Iterator
529 2210 50       4788 @_ >= 1 or confess;
530 2210 50       35229 confess unless my $C = $iter->node; # Current node required
531              
532 2210         41187 ++$iter->count; # Count the calls to the iterator
533              
534             my $new = sub # Load iterator with latest position
535 2145     2145   9334 {my ($node, $pos) = @_; # Parameters
536 2145         36312 $iter->node = $node;
537 2145   100     41208 $iter->pos = $pos //= ($node->keys->@* - 1);
538 2145         56144 $iter->key = $node->keys->[$pos];
539 2145         72594 $iter->data = $node->data->[$pos]
540 2210         12919 };
541              
542 2210     65   5197 my $done = sub {$iter->less = undef}; # The tree has been completely traversed
  65         1122  
543              
544 2210 100       35716 if (!defined($iter->pos)) # Initial descent
545 65         1411 {my $l = $C->node->[-1];
546 65 50       765 return $l ? &$new($l->rightMost) : $C->keys->@* ? &$new($C) : &$done; # Start node or done if empty tree
    100          
547 0         0 return;
548             }
549              
550             my $up = sub # Iterate up to next node that has not been visited
551 1088     1088   17578 {for(my $n = $C; my $p = $n->up; $n = $n->up)
552 1897         10740 {my $i = indexInParent $n;
553 1897 100       18011 return &$new($p, $i-1) if $i > 0;
554             }
555 65         437 &$done # No nodes not visited
556 2145         12668 };
557              
558 2145         34461 my $i = $iter->pos;
559 2145 100       9542 if (leaf $C) # Leaf
560 1122 100       6544 {$i > 0 ? &$new($C, $i-1) : &$up;
561             }
562             else # Node
563 1023 50       20476 {$i >= 0 ? &$new($C->node->[$i]->rightMost) : &$up
564             }
565             }
566              
567             sub flat($@) # Print the keys in a tree from left right to make it easier to visualize the structure of the tree.
568 2     2 1 7 {my ($tree, @title) = @_; # Tree, title
569 2 50       6 confess unless $tree;
570 2         6 my @s; # Print
571             my $D; # Deepest
572 2         20 for(my $i = iterator root $tree; $i->more; $i->next) # Traverse tree
573 16         807 {my $d = depth $i->node;
574 16 100 100     113 $D = $d unless $D and $D > $d;
575 16   100     40 $s[$d] //= '';
576 16         292 $s[$d] .= " ".$i->key; # Add key at appropriate depth
577 16         81 my $l = length $s[$d];
578 16         35 for my $j(0..$D) # Pad all strings to the current position
579 64   100     145 {my $s = $s[$j] //= '';
580 64 100       206 $s[$j] = substr($s.(' 'x999), 0, $l) if length($s) < $l;
581             }
582             }
583 2         33 for my $i(keys @s) # Clean up trailing blanks so that tests are not affected by spurious white space mismatches
584 8         18 {$s[$i] =~ s/\s+\n/\n/gs;
585 8         37 $s[$i] =~ s/\s+\Z//gs;
586             }
587 2 50       7 unshift @s, join(' ', @title) if @title; # Add title
588 2         17 join "\n", @s, '';
589             }
590              
591             sub print($;$) # Print the keys in a tree optionally marking the active key.
592 4     4 1 13 {my ($tree, $i) = @_; # Tree, optional index of active key
593 4 50       12 confess unless $tree;
594 4         8 my @s; # Print
595              
596             my $print = sub # Print a node
597 112     112   223 {my ($t, $in) = @_;
598 112 50 33     1832 return unless $t and $t->keys and $t->keys->@*;
      33        
599              
600 112         2791 my @t = (' 'x$in); # Print keys staring the active key if known
601 112         1763 for my $j(keys $t->keys->@*)
602 557         8938 {push @t, $t->keys->[$j];
603 557 0 33     2723 push @t, '<=' if defined($i) and $i == $j and $tree == $t;
      33        
604             }
605 112         374 push @s, join ' ', @t; # Details of one node
606              
607 112 50       1778 if (my $nodes = $t->node) # Each key
608 112         790 {__SUB__->($_, $in+1) for $nodes->@*;
609             }
610 4         40 };
611              
612 4         19 &$print(root($tree), 0); # Print tree
613              
614 4         119 join "\n", @s, ''
615             }
616              
617             sub size($) # Count the number of keys in a tree.
618 11     11 1 38 {my ($tree) = @_; # Tree
619 11 50       31 @_ == 1 or confess;
620 11         19 my $n = 0; # Print
621              
622             my $count = sub # Print a node
623 55     55   95 {my ($t) = @_;
624 55 100 33     896 return unless $t and $t->keys and my @k = $t->keys->@*;
      66        
625 54         1268 $n += @k;
626 54 50       854 if (my $nodes = $t->node) # Each key
627 54         323 {__SUB__->($_) for $nodes->@*;
628             }
629 11         52 };
630              
631 11         31 &$count(root $tree); # Count nodes in tree
632              
633 11         121 $n;
634             }
635              
636             #d
637             #-------------------------------------------------------------------------------
638             # Export - eeee
639             #-------------------------------------------------------------------------------
640              
641 1     1   10 use Exporter qw(import);
  1         2  
  1         36  
642              
643 1     1   5 use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
  1         2  
  1         399  
644              
645             @ISA = qw(Exporter);
646             @EXPORT = qw();
647             @EXPORT_OK = qw(
648             );
649             %EXPORT_TAGS = (all=>[@EXPORT, @EXPORT_OK]);
650              
651             # podDocumentation
652             =pod
653              
654             =encoding utf-8
655              
656             =head1 Name
657              
658             Tree::Multi - Multi-way tree in Pure Perl with an even or odd number of keys per node.
659              
660             =head1 Synopsis
661              
662             Construct and query a multi-way tree in B<100%> Pure Perl with a choice of an
663             odd or an even numbers of keys per node:
664              
665             local $Tree::Multi::numberOfKeysPerNode = 4; # Number of keys per node - can be even
666              
667             my $t = Tree::Multi::new; # Construct tree
668             $t->insert($_, 2 * $_) for reverse 1..32; # Load tree in reverse
669              
670             is_deeply $t->print, <
671             15 21 27
672             3 6 9 12
673             1 2
674             4 5
675             7 8
676             10 11
677             13 14
678             18
679             16 17
680             19 20
681             24
682             22 23
683             25 26
684             30
685             28 29
686             31 32
687             END
688              
689             ok $t->height == 3; # Height
690              
691             ok $t->find (16) == 32; # Find by key
692             $t->delete(16); # Delete a key
693             ok !$t->find (16); # Key no longer present
694              
695              
696             ok $t->find (17) == 34; # Find by key
697             my @k;
698             for(my $i = $t->iterator; $i->more; $i->next) # Iterator
699             {push @k, $i->key unless $i->key == 17;
700             }
701              
702             $t->delete($_) for @k; # Delete
703              
704             ok $t->find(17) == 34 && $t->size == 1; # Size
705              
706             =head1 Description
707              
708             Multi-way tree in Pure Perl with an even or odd number of keys per node.
709              
710              
711             Version "20210605".
712              
713              
714             The following sections describe the methods in each functional area of this
715             module. For an alphabetic listing of all methods by name see L.
716              
717              
718              
719             =head1 Multi-way Tree
720              
721             Create and use a multi-way tree.
722              
723             =head2 root($tree)
724              
725             Return the root node of a tree.
726              
727             Parameter Description
728             1 $tree Tree
729              
730             B
731              
732              
733             local $numberOfKeysPerNode = 3; my $N = 13; my $t = new;
734              
735             for my $n(1..$N)
736             {$t->insert($n, $n);
737             }
738              
739             is_deeply $t->leftMost ->keys, [1, 2];
740             is_deeply $t->rightMost->keys, [13];
741             ok $t->leftMost ->leaf;
742             ok $t->rightMost->leaf;
743              
744             ok $t->root == $t; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
745              
746              
747             ok T($t, <
748             6
749             3
750             1 2
751             4 5
752             9 12
753             7 8
754             10 11
755             13
756             END
757              
758              
759             =head2 leaf($tree)
760              
761             Confirm that the tree is a leaf.
762              
763             Parameter Description
764             1 $tree Tree
765              
766             B
767              
768              
769             local $numberOfKeysPerNode = 3; my $N = 13; my $t = new;
770              
771             for my $n(1..$N)
772             {$t->insert($n, $n);
773             }
774              
775             is_deeply $t->leftMost ->keys, [1, 2];
776             is_deeply $t->rightMost->keys, [13];
777              
778             ok $t->leftMost ->leaf; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
779              
780              
781             ok $t->rightMost->leaf; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
782              
783             ok $t->root == $t;
784              
785             ok T($t, <
786             6
787             3
788             1 2
789             4 5
790             9 12
791             7 8
792             10 11
793             13
794             END
795              
796              
797             =head2 find($root, $key)
798              
799             Find a key in a tree returning its associated data or undef if the key does not exist.
800              
801             Parameter Description
802             1 $root Root of tree
803             2 $key Key
804              
805             B
806              
807              
808             local $Tree::Multi::numberOfKeysPerNode = 4; # Number of keys per node - can be even
809              
810             my $t = Tree::Multi::new; # Construct tree
811             $t->insert($_, 2 * $_) for reverse 1..32; # Load tree in reverse
812              
813             is_deeply $t->print, <
814             15 21 27
815             3 6 9 12
816             1 2
817             4 5
818             7 8
819             10 11
820             13 14
821             18
822             16 17
823             19 20
824             24
825             22 23
826             25 26
827             30
828             28 29
829             31 32
830             END
831              
832             ok $t->size == 32; # Size
833             ok $t->height == 3; # Height
834             ok $t->delete(16) == 2 * 16; # Delete a key
835              
836             ok !$t->find (16); # Key no longer present # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
837              
838              
839             ok $t->find (17) == 34; # Find by key # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
840              
841              
842             my @k;
843             for(my $i = $t->iterator; $i->more; $i->next) # Iterator
844             {push @k, $i->key unless $i->key == 17;
845             }
846              
847             ok $t->delete($_) == 2 * $_ for @k; # Delete
848              
849              
850             ok $t->find(17) == 34 && $t->size == 1; # Size # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
851              
852              
853              
854             =head2 leftMost($tree)
855              
856             Return the left most node below the specified one.
857              
858             Parameter Description
859             1 $tree Tree
860              
861             B
862              
863              
864             local $numberOfKeysPerNode = 3; my $N = 13; my $t = new;
865              
866             for my $n(1..$N)
867             {$t->insert($n, $n);
868             }
869              
870              
871             is_deeply $t->leftMost ->keys, [1, 2]; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
872              
873             is_deeply $t->rightMost->keys, [13];
874              
875             ok $t->leftMost ->leaf; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
876              
877             ok $t->rightMost->leaf;
878             ok $t->root == $t;
879              
880             ok T($t, <
881             6
882             3
883             1 2
884             4 5
885             9 12
886             7 8
887             10 11
888             13
889             END
890              
891              
892             =head2 rightMost($tree)
893              
894             Return the right most node below the specified one.
895              
896             Parameter Description
897             1 $tree Tree
898              
899             B
900              
901              
902             local $numberOfKeysPerNode = 3; my $N = 13; my $t = new;
903              
904             for my $n(1..$N)
905             {$t->insert($n, $n);
906             }
907              
908             is_deeply $t->leftMost ->keys, [1, 2];
909              
910             is_deeply $t->rightMost->keys, [13]; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
911              
912             ok $t->leftMost ->leaf;
913              
914             ok $t->rightMost->leaf; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
915              
916             ok $t->root == $t;
917              
918             ok T($t, <
919             6
920             3
921             1 2
922             4 5
923             9 12
924             7 8
925             10 11
926             13
927             END
928              
929              
930             =head2 height($tree)
931              
932             Return the height of the tree.
933              
934             Parameter Description
935             1 $tree Tree
936              
937             B
938              
939              
940             local $Tree::Multi::numberOfKeysPerNode = 3;
941              
942             my $t = new; ok $t->height == 0; ok $t->leftMost->depth == 0; ok $t->size == 0; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
943              
944              
945             $t->insert(1, 1); ok $t->height == 1; ok $t->leftMost->depth == 1; ok $t->size == 1; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
946              
947              
948             $t->insert(2, 2); ok $t->height == 1; ok $t->leftMost->depth == 1; ok $t->size == 2; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
949              
950              
951             $t->insert(3, 3); ok $t->height == 1; ok $t->leftMost->depth == 1; ok $t->size == 3; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
952              
953              
954             $t->insert(4, 4); ok $t->height == 2; ok $t->leftMost->depth == 2; ok $t->size == 4; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
955              
956              
957             $t->insert(5, 5); ok $t->height == 2; ok $t->leftMost->depth == 2; ok $t->size == 5; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
958              
959              
960             $t->insert(6, 6); ok $t->height == 2; ok $t->leftMost->depth == 2; ok $t->size == 6; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
961              
962              
963             $t->insert(7, 7); ok $t->height == 2; ok $t->leftMost->depth == 2; ok $t->size == 7; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
964              
965              
966             $t->insert(8, 8); ok $t->height == 2; ok $t->leftMost->depth == 2; ok $t->size == 8; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
967              
968              
969             is_deeply $t->flat, <flat if $develop;
970              
971             3 6
972             1 2 4 5 7 8
973             END
974              
975              
976              
977             =head2 depth($tree)
978              
979             Return the depth of a node within a tree.
980              
981             Parameter Description
982             1 $tree Tree
983              
984             B
985              
986              
987             local $Tree::Multi::numberOfKeysPerNode = 3;
988              
989             my $t = new; ok $t->height == 0; ok $t->leftMost->depth == 0; ok $t->size == 0; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
990              
991              
992             $t->insert(1, 1); ok $t->height == 1; ok $t->leftMost->depth == 1; ok $t->size == 1; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
993              
994              
995             $t->insert(2, 2); ok $t->height == 1; ok $t->leftMost->depth == 1; ok $t->size == 2; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
996              
997              
998             $t->insert(3, 3); ok $t->height == 1; ok $t->leftMost->depth == 1; ok $t->size == 3; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
999              
1000              
1001             $t->insert(4, 4); ok $t->height == 2; ok $t->leftMost->depth == 2; ok $t->size == 4; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1002              
1003              
1004             $t->insert(5, 5); ok $t->height == 2; ok $t->leftMost->depth == 2; ok $t->size == 5; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1005              
1006              
1007             $t->insert(6, 6); ok $t->height == 2; ok $t->leftMost->depth == 2; ok $t->size == 6; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1008              
1009              
1010             $t->insert(7, 7); ok $t->height == 2; ok $t->leftMost->depth == 2; ok $t->size == 7; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1011              
1012              
1013             $t->insert(8, 8); ok $t->height == 2; ok $t->leftMost->depth == 2; ok $t->size == 8; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1014              
1015              
1016             is_deeply $t->flat, <flat if $develop;
1017              
1018             3 6
1019             1 2 4 5 7 8
1020             END
1021              
1022              
1023              
1024             =head2 delete($root, $key)
1025              
1026             Find a key in a tree, delete it and return any associated data.
1027              
1028             Parameter Description
1029             1 $root Tree root
1030             2 $key Key
1031              
1032             B
1033              
1034              
1035             local $Tree::Multi::numberOfKeysPerNode = 4; # Number of keys per node - can be even
1036              
1037             my $t = Tree::Multi::new; # Construct tree
1038             $t->insert($_, 2 * $_) for reverse 1..32; # Load tree in reverse
1039              
1040             is_deeply $t->print, <
1041             15 21 27
1042             3 6 9 12
1043             1 2
1044             4 5
1045             7 8
1046             10 11
1047             13 14
1048             18
1049             16 17
1050             19 20
1051             24
1052             22 23
1053             25 26
1054             30
1055             28 29
1056             31 32
1057             END
1058              
1059             ok $t->size == 32; # Size
1060             ok $t->height == 3; # Height
1061              
1062             ok $t->delete(16) == 2 * 16; # Delete a key # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1063              
1064             ok !$t->find (16); # Key no longer present
1065             ok $t->find (17) == 34; # Find by key
1066              
1067             my @k;
1068             for(my $i = $t->iterator; $i->more; $i->next) # Iterator
1069             {push @k, $i->key unless $i->key == 17;
1070             }
1071              
1072              
1073             ok $t->delete($_) == 2 * $_ for @k; # Delete # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1074              
1075              
1076             ok $t->find(17) == 34 && $t->size == 1; # Size
1077              
1078              
1079             =head2 insert($tree, $key, $data)
1080              
1081             Insert the specified key and data into a tree.
1082              
1083             Parameter Description
1084             1 $tree Tree
1085             2 $key Key
1086             3 $data Data
1087              
1088             B
1089              
1090              
1091             local $Tree::Multi::numberOfKeysPerNode = 4; # Number of keys per node - can be even
1092              
1093             my $t = Tree::Multi::new; # Construct tree
1094              
1095             $t->insert($_, 2 * $_) for reverse 1..32; # Load tree in reverse # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1096              
1097              
1098             is_deeply $t->print, <
1099             15 21 27
1100             3 6 9 12
1101             1 2
1102             4 5
1103             7 8
1104             10 11
1105             13 14
1106             18
1107             16 17
1108             19 20
1109             24
1110             22 23
1111             25 26
1112             30
1113             28 29
1114             31 32
1115             END
1116              
1117             ok $t->size == 32; # Size
1118             ok $t->height == 3; # Height
1119             ok $t->delete(16) == 2 * 16; # Delete a key
1120             ok !$t->find (16); # Key no longer present
1121             ok $t->find (17) == 34; # Find by key
1122              
1123             my @k;
1124             for(my $i = $t->iterator; $i->more; $i->next) # Iterator
1125             {push @k, $i->key unless $i->key == 17;
1126             }
1127              
1128             ok $t->delete($_) == 2 * $_ for @k; # Delete
1129              
1130             ok $t->find(17) == 34 && $t->size == 1; # Size
1131              
1132              
1133             =head2 iterator($tree)
1134              
1135             Make an iterator for a tree.
1136              
1137             Parameter Description
1138             1 $tree Tree
1139              
1140             B
1141              
1142              
1143             local $numberOfKeysPerNode = 3; my $N = 256; my $e = 0; my $t = new;
1144              
1145             for my $n(0..$N)
1146             {$t->insert($n, $n);
1147              
1148             my @n; for(my $i = $t->iterator; $i->more; $i->next) {push @n, $i->key} # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1149              
1150             ++$e unless dump(\@n) eq dump [0..$n];
1151             }
1152              
1153             is_deeply $e, 0;
1154              
1155             local $Tree::Multi::numberOfKeysPerNode = 4; # Number of keys per node - can be even
1156              
1157             my $t = Tree::Multi::new; # Construct tree
1158             $t->insert($_, 2 * $_) for reverse 1..32; # Load tree in reverse
1159              
1160             is_deeply $t->print, <
1161             15 21 27
1162             3 6 9 12
1163             1 2
1164             4 5
1165             7 8
1166             10 11
1167             13 14
1168             18
1169             16 17
1170             19 20
1171             24
1172             22 23
1173             25 26
1174             30
1175             28 29
1176             31 32
1177             END
1178              
1179             ok $t->size == 32; # Size
1180             ok $t->height == 3; # Height
1181             ok $t->delete(16) == 2 * 16; # Delete a key
1182             ok !$t->find (16); # Key no longer present
1183             ok $t->find (17) == 34; # Find by key
1184              
1185             my @k;
1186              
1187             for(my $i = $t->iterator; $i->more; $i->next) # Iterator # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1188              
1189             {push @k, $i->key unless $i->key == 17;
1190             }
1191              
1192             ok $t->delete($_) == 2 * $_ for @k; # Delete
1193              
1194             ok $t->find(17) == 34 && $t->size == 1; # Size
1195              
1196              
1197             =head2 Tree::Multi::Iterator::next($iter)
1198              
1199             Find the next key.
1200              
1201             Parameter Description
1202             1 $iter Iterator
1203              
1204             B
1205              
1206              
1207             local $numberOfKeysPerNode = 3; my $N = 256; my $e = 0; my $t = new;
1208              
1209             for my $n(0..$N)
1210             {$t->insert($n, $n);
1211             my @n; for(my $i = $t->iterator; $i->more; $i->next) {push @n, $i->key}
1212             ++$e unless dump(\@n) eq dump [0..$n];
1213             }
1214              
1215             is_deeply $e, 0;
1216              
1217              
1218             =head2 reverseIterator($tree)
1219              
1220             Create a reverse iterator for a tree.
1221              
1222             Parameter Description
1223             1 $tree Tree
1224              
1225             B
1226              
1227              
1228             local $numberOfKeysPerNode = 3; my $N = 64; my $e = 0;
1229              
1230             for my $n(0..$N)
1231             {my $t = new;
1232             for my $i(0..$n)
1233             {$t->insert($i, $i);
1234             }
1235             my @n;
1236              
1237             for(my $i = $t->reverseIterator; $i->less; $i->prev) # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1238              
1239             {push @n, $i->key;
1240             }
1241             ++$e unless dump(\@n) eq dump [reverse 0..$n];
1242             }
1243              
1244             is_deeply $e, 0;
1245              
1246              
1247             =head2 Tree::Multi::ReverseIterator::prev($iter)
1248              
1249             Find the previous key.
1250              
1251             Parameter Description
1252             1 $iter Iterator
1253              
1254             B
1255              
1256              
1257             local $numberOfKeysPerNode = 3; my $N = 64; my $e = 0;
1258              
1259             for my $n(0..$N)
1260             {my $t = new;
1261             for my $i(0..$n)
1262             {$t->insert($i, $i);
1263             }
1264             my @n;
1265             for(my $i = $t->reverseIterator; $i->less; $i->prev)
1266             {push @n, $i->key;
1267             }
1268             ++$e unless dump(\@n) eq dump [reverse 0..$n];
1269             }
1270              
1271             is_deeply $e, 0;
1272              
1273              
1274             =head2 flat($tree, @title)
1275              
1276             Print the keys in a tree from left right to make it easier to visualize the structure of the tree.
1277              
1278             Parameter Description
1279             1 $tree Tree
1280             2 @title Title
1281              
1282             B
1283              
1284              
1285             local $Tree::Multi::numberOfKeysPerNode = 3;
1286             my $t = new; ok $t->height == 0; ok $t->leftMost->depth == 0; ok $t->size == 0;
1287             $t->insert(1, 1); ok $t->height == 1; ok $t->leftMost->depth == 1; ok $t->size == 1;
1288             $t->insert(2, 2); ok $t->height == 1; ok $t->leftMost->depth == 1; ok $t->size == 2;
1289             $t->insert(3, 3); ok $t->height == 1; ok $t->leftMost->depth == 1; ok $t->size == 3;
1290             $t->insert(4, 4); ok $t->height == 2; ok $t->leftMost->depth == 2; ok $t->size == 4;
1291             $t->insert(5, 5); ok $t->height == 2; ok $t->leftMost->depth == 2; ok $t->size == 5;
1292             $t->insert(6, 6); ok $t->height == 2; ok $t->leftMost->depth == 2; ok $t->size == 6;
1293             $t->insert(7, 7); ok $t->height == 2; ok $t->leftMost->depth == 2; ok $t->size == 7;
1294             $t->insert(8, 8); ok $t->height == 2; ok $t->leftMost->depth == 2; ok $t->size == 8;
1295              
1296              
1297             is_deeply $t->flat, <flat if $develop; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1298              
1299              
1300             3 6
1301             1 2 4 5 7 8
1302             END
1303              
1304              
1305              
1306             =head2 print($tree, $i)
1307              
1308             Print the keys in a tree optionally marking the active key.
1309              
1310             Parameter Description
1311             1 $tree Tree
1312             2 $i Optional index of active key
1313              
1314             B
1315              
1316              
1317             local $Tree::Multi::numberOfKeysPerNode = 4; # Number of keys per node - can be even
1318              
1319             my $t = Tree::Multi::new; # Construct tree
1320             $t->insert($_, 2 * $_) for reverse 1..32; # Load tree in reverse
1321              
1322              
1323             is_deeply $t->print, <
1324              
1325             15 21 27
1326             3 6 9 12
1327             1 2
1328             4 5
1329             7 8
1330             10 11
1331             13 14
1332             18
1333             16 17
1334             19 20
1335             24
1336             22 23
1337             25 26
1338             30
1339             28 29
1340             31 32
1341             END
1342              
1343             ok $t->size == 32; # Size
1344             ok $t->height == 3; # Height
1345             ok $t->delete(16) == 2 * 16; # Delete a key
1346             ok !$t->find (16); # Key no longer present
1347             ok $t->find (17) == 34; # Find by key
1348              
1349             my @k;
1350             for(my $i = $t->iterator; $i->more; $i->next) # Iterator
1351             {push @k, $i->key unless $i->key == 17;
1352             }
1353              
1354             ok $t->delete($_) == 2 * $_ for @k; # Delete
1355              
1356             ok $t->find(17) == 34 && $t->size == 1; # Size
1357              
1358              
1359             =head2 size($tree)
1360              
1361             Count the number of keys in a tree.
1362              
1363             Parameter Description
1364             1 $tree Tree
1365              
1366             B
1367              
1368              
1369             local $Tree::Multi::numberOfKeysPerNode = 3;
1370              
1371             my $t = new; ok $t->height == 0; ok $t->leftMost->depth == 0; ok $t->size == 0; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1372              
1373              
1374             $t->insert(1, 1); ok $t->height == 1; ok $t->leftMost->depth == 1; ok $t->size == 1; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1375              
1376              
1377             $t->insert(2, 2); ok $t->height == 1; ok $t->leftMost->depth == 1; ok $t->size == 2; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1378              
1379              
1380             $t->insert(3, 3); ok $t->height == 1; ok $t->leftMost->depth == 1; ok $t->size == 3; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1381              
1382              
1383             $t->insert(4, 4); ok $t->height == 2; ok $t->leftMost->depth == 2; ok $t->size == 4; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1384              
1385              
1386             $t->insert(5, 5); ok $t->height == 2; ok $t->leftMost->depth == 2; ok $t->size == 5; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1387              
1388              
1389             $t->insert(6, 6); ok $t->height == 2; ok $t->leftMost->depth == 2; ok $t->size == 6; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1390              
1391              
1392             $t->insert(7, 7); ok $t->height == 2; ok $t->leftMost->depth == 2; ok $t->size == 7; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1393              
1394              
1395             $t->insert(8, 8); ok $t->height == 2; ok $t->leftMost->depth == 2; ok $t->size == 8; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1396              
1397              
1398             is_deeply $t->flat, <flat if $develop;
1399              
1400             3 6
1401             1 2 4 5 7 8
1402             END
1403              
1404              
1405              
1406              
1407             =head2 Tree::Multi Definition
1408              
1409              
1410             Iterator
1411              
1412              
1413              
1414              
1415             =head3 Output fields
1416              
1417              
1418             =head4 count
1419              
1420             Counter
1421              
1422             =head4 data
1423              
1424             Data at this position
1425              
1426             =head4 key
1427              
1428             Key at this position
1429              
1430             =head4 keys
1431              
1432             Array of key items for this node
1433              
1434             =head4 less
1435              
1436             Iteration not yet finished
1437              
1438             =head4 more
1439              
1440             Iteration not yet finished
1441              
1442             =head4 node
1443              
1444             Current node within tree
1445              
1446             =head4 number
1447              
1448             Number of the node for debugging purposes
1449              
1450             =head4 pos
1451              
1452             Current position within node
1453              
1454             =head4 tree
1455              
1456             Tree we are iterating over
1457              
1458             =head4 up
1459              
1460             Parent node
1461              
1462              
1463              
1464             =head1 Private Methods
1465              
1466             =head2 new()
1467              
1468             Create a new multi-way tree node.
1469              
1470              
1471             B
1472              
1473              
1474             local $Tree::Multi::numberOfKeysPerNode = 4; # Number of keys per node - can be even
1475              
1476              
1477             my $t = Tree::Multi::new; # Construct tree # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1478              
1479             $t->insert($_, 2 * $_) for reverse 1..32; # Load tree in reverse
1480              
1481             is_deeply $t->print, <
1482             15 21 27
1483             3 6 9 12
1484             1 2
1485             4 5
1486             7 8
1487             10 11
1488             13 14
1489             18
1490             16 17
1491             19 20
1492             24
1493             22 23
1494             25 26
1495             30
1496             28 29
1497             31 32
1498             END
1499              
1500             ok $t->size == 32; # Size
1501             ok $t->height == 3; # Height
1502             ok $t->delete(16) == 2 * 16; # Delete a key
1503             ok !$t->find (16); # Key no longer present
1504             ok $t->find (17) == 34; # Find by key
1505              
1506             my @k;
1507             for(my $i = $t->iterator; $i->more; $i->next) # Iterator
1508             {push @k, $i->key unless $i->key == 17;
1509             }
1510              
1511             ok $t->delete($_) == 2 * $_ for @k; # Delete
1512              
1513             ok $t->find(17) == 34 && $t->size == 1; # Size
1514              
1515              
1516             =head2 minimumNumberOfKeys()
1517              
1518             Minimum number of keys per node.
1519              
1520              
1521             =head2 maximumNumberOfKeys()
1522              
1523             Maximum number of keys per node.
1524              
1525              
1526             =head2 maximumNumberOfNodes()
1527              
1528             Maximum number of children per parent.
1529              
1530              
1531             =head2 full($tree)
1532              
1533             Confirm that a node is full.
1534              
1535             Parameter Description
1536             1 $tree Tree
1537              
1538             =head2 halfFull($tree)
1539              
1540             Confirm that a node is half full.
1541              
1542             Parameter Description
1543             1 $tree Tree
1544              
1545             =head2 separate(@k)
1546              
1547             Return ([lower], center, [upper]) keys from an array.
1548              
1549             Parameter Description
1550             1 @k Array to split
1551              
1552             =head2 separateKeys($node)
1553              
1554             Return ([lower], center, [upper]) keys.
1555              
1556             Parameter Description
1557             1 $node Node to split
1558              
1559             =head2 separateData($node)
1560              
1561             Return ([lower], center, [upper]) data.
1562              
1563             Parameter Description
1564             1 $node Node to split
1565              
1566             =head2 separateNode($node)
1567              
1568             Return ([lower], [upper]) children.
1569              
1570             Parameter Description
1571             1 $node Node to split
1572              
1573             =head2 reUp($tree, @children)
1574              
1575             Reconnect the children to their new parent.
1576              
1577             Parameter Description
1578             1 $tree Tree
1579             2 @children Children
1580              
1581             =head2 splitFullNode($node)
1582              
1583             Split a node, that is not a leaf, if it is full.
1584              
1585             Parameter Description
1586             1 $node Node to split
1587              
1588             =head2 findAndSplit($root, $key)
1589              
1590             Find a key in a tree splitting full nodes along the path to the key.
1591              
1592             Parameter Description
1593             1 $root Root of tree
1594             2 $key Key
1595              
1596             =head2 indexInParent($tree)
1597              
1598             Get the index of a node in its parent.
1599              
1600             Parameter Description
1601             1 $tree Tree
1602              
1603             =head2 fillFromLeftOrRight($node, $dir)
1604              
1605             Fill a node from the specified sibling.
1606              
1607             Parameter Description
1608             1 $node Node to fill
1609             2 $dir Node to fill from 0 for left or 1 for right
1610              
1611             =head2 mergeWithLeftOrRight($n, $dir)
1612              
1613             Merge two adjacent nodes.
1614              
1615             Parameter Description
1616             1 $n Node to merge into
1617             2 $dir Node to merge is on right if 1 else left
1618              
1619             =head2 merge($tree)
1620              
1621             Merge the current node with its sibling.
1622              
1623             Parameter Description
1624             1 $tree Tree
1625              
1626             =head2 mergeOrFill($tree)
1627              
1628             Make a node larger than a half node.
1629              
1630             Parameter Description
1631             1 $tree Tree
1632              
1633             =head2 deleteLeafKey($tree, $i)
1634              
1635             Delete a key in a leaf.
1636              
1637             Parameter Description
1638             1 $tree Tree
1639             2 $i Index to delete at
1640              
1641             =head2 deleteKey($tree, $i)
1642              
1643             Delete a key.
1644              
1645             Parameter Description
1646             1 $tree Tree
1647             2 $i Index to delete at
1648              
1649             =head2 T($tree, $expected)
1650              
1651             Write a result to the log file
1652              
1653             Parameter Description
1654             1 $tree Tree
1655             2 $expected Expected print
1656              
1657             =head2 disordered($n, $N)
1658              
1659             Disordered but stable insertions
1660              
1661             Parameter Description
1662             1 $n Keys per node
1663             2 $N Nodes
1664              
1665             =head2 disorderedCheck($t, $n, $N)
1666              
1667             Check disordered insertions
1668              
1669             Parameter Description
1670             1 $t Tree to check
1671             2 $n Keys per node
1672             3 $N Nodes
1673              
1674             =head2 randomCheck($n, $N, $T)
1675              
1676             Random insertions
1677              
1678             Parameter Description
1679             1 $n Keys per node
1680             2 $N Log 10 nodes
1681             3 $T Log 10 number of tests
1682              
1683              
1684             =head1 Index
1685              
1686              
1687             1 L - Find a key in a tree, delete it and return any associated data.
1688              
1689             2 L - Delete a key.
1690              
1691             3 L - Delete a key in a leaf.
1692              
1693             4 L - Return the depth of a node within a tree.
1694              
1695             5 L - Disordered but stable insertions
1696              
1697             6 L - Check disordered insertions
1698              
1699             7 L - Fill a node from the specified sibling.
1700              
1701             8 L - Find a key in a tree returning its associated data or undef if the key does not exist.
1702              
1703             9 L - Find a key in a tree splitting full nodes along the path to the key.
1704              
1705             10 L - Print the keys in a tree from left right to make it easier to visualize the structure of the tree.
1706              
1707             11 L - Confirm that a node is full.
1708              
1709             12 L - Confirm that a node is half full.
1710              
1711             13 L - Return the height of the tree.
1712              
1713             14 L - Get the index of a node in its parent.
1714              
1715             15 L - Insert the specified key and data into a tree.
1716              
1717             16 L - Make an iterator for a tree.
1718              
1719             17 L - Confirm that the tree is a leaf.
1720              
1721             18 L - Return the left most node below the specified one.
1722              
1723             19 L - Maximum number of keys per node.
1724              
1725             20 L - Maximum number of children per parent.
1726              
1727             21 L - Merge the current node with its sibling.
1728              
1729             22 L - Make a node larger than a half node.
1730              
1731             23 L - Merge two adjacent nodes.
1732              
1733             24 L - Minimum number of keys per node.
1734              
1735             25 L - Create a new multi-way tree node.
1736              
1737             26 L - Print the keys in a tree optionally marking the active key.
1738              
1739             27 L - Random insertions
1740              
1741             28 L - Reconnect the children to their new parent.
1742              
1743             29 L - Create a reverse iterator for a tree.
1744              
1745             30 L - Return the right most node below the specified one.
1746              
1747             31 L - Return the root node of a tree.
1748              
1749             32 L - Return ([lower], center, [upper]) keys from an array.
1750              
1751             33 L - Return ([lower], center, [upper]) data.
1752              
1753             34 L - Return ([lower], center, [upper]) keys.
1754              
1755             35 L - Return ([lower], [upper]) children.
1756              
1757             36 L - Count the number of keys in a tree.
1758              
1759             37 L - Split a node, that is not a leaf, if it is full.
1760              
1761             38 L - Write a result to the log file
1762              
1763             39 L - Find the next key.
1764              
1765             40 L - Find the previous key.
1766              
1767             =head1 Installation
1768              
1769             This module is written in 100% Pure Perl and, thus, it is easy to read,
1770             comprehend, use, modify and install via B:
1771              
1772             sudo cpan install Tree::Multi
1773              
1774             =head1 Author
1775              
1776             L
1777              
1778             L
1779              
1780             =head1 Copyright
1781              
1782             Copyright (c) 2016-2021 Philip R Brenan.
1783              
1784             This module is free software. It may be used, redistributed and/or modified
1785             under the same terms as Perl itself.
1786              
1787             =cut
1788              
1789              
1790              
1791             # Tests and documentation
1792              
1793             sub test
1794 1     1 0 86 {my $p = __PACKAGE__;
1795 1         11 binmode($_, ":utf8") for *STDOUT, *STDERR;
1796 1 50       65 return if eval "eof(${p}::DATA)";
1797 0         0 my $s = eval "join('', <${p}::DATA>)";
1798 0 0       0 $@ and die $@;
1799 0         0 eval $s;
1800 0 0       0 $@ and die $@;
1801 0         0 1
1802             }
1803              
1804             test unless caller;
1805              
1806             1;
1807             # podDocumentation
1808             #__DATA__
1809 1     1   9 use Time::HiRes qw(time);
  1         2  
  1         11  
1810 1     1   1983 use Test::Most;
  1         86878  
  1         6  
1811              
1812             my $develop = -e q(/home/phil/); # Developing
1813             my $logFile = q(/home/phil/perl/cpan/TreeMulti/lib/Tree/zzzLog.txt); # Log file
1814              
1815             my $localTest = ((caller(1))[0]//'Tree::Multi') eq "Tree::Multi"; # Local testing mode
1816              
1817             Test::More->builder->output("/dev/null") if $localTest; # Reduce number of confirmation messages during testing
1818              
1819             if ($^O =~ m(bsd|linux)i) # Supported systems
1820             {plan tests => 83;
1821             }
1822             else
1823             {plan skip_all =>qq(Not supported on: $^O);
1824             }
1825              
1826             bail_on_fail; # Stop if any tests fails
1827              
1828             sub T($$;$) #P Print a tree to the log file and check it against the expected result
1829 6     6 1 22 {my ($tree, $expected, $flat) = @_; # Tree, expected print, optionally print in flat mode if true
1830 6 50       25 confess unless ref($tree);
1831 6 100       39 my $got = $flat ? $tree->flat : $tree->print;
1832 6 50       56 return $got eq $expected unless $develop;
1833 0         0 my $s = &showGotVersusWanted($got, $expected);
1834 0 0       0 return 1 unless $s;
1835 0         0 owf($logFile, $got);
1836 0         0 confess "$s\n";
1837             }
1838              
1839             sub F($$) #P Print a tree flatly to the log file and check its result
1840 0     0 0 0 {&T(@_, 1);
1841             }
1842              
1843             sub disordered($$) #P Disordered but stable insertions
1844 2     2 1 5 {my ($n, $N) = @_; # Keys per node, nodes
1845 2         5 local $numberOfKeysPerNode = $n;
1846              
1847 2         4 my $t = new;
1848 2         139 my @t = map{$_ = scalar reverse $_; s/\A0+//r} 1..$N;
  512         721  
  512         856  
1849 2         40 $t->insert($_, 2 * $_) for @t;
1850 2         53 $t # Tree built from disordered but stable insertions
1851             }
1852              
1853             sub disorderedCheck($$$) #P Check disordered insertions
1854 2     2 1 6 {my ($t, $n, $N) = @_; # Tree to check, keys per node, Nodes
1855              
1856 2         13 my %t = map {$_=>2*$_} map{$_ = scalar reverse $_; s/\A0+//r} 1..$N;
  512         1262  
  512         733  
  512         807  
1857              
1858 2         84 my $e = 0;
1859 2         10 my $h = $t->height;
1860 2         95 for my $k(sort {reverse($a) cmp reverse($b)} keys %t)
  3035         4435  
1861 462 50       3105 {++$e unless $t->find($k) == $t{$k}; $t->delete($k); delete $t{$k};
  462         3364  
  462         835  
1862 462 50       1032 ++$e if defined $t->find($k);
1863 462 50       3462 ++$e if $t->height > $h;
1864             }
1865 2 50       125 ++$e unless $t->height == 0;
1866              
1867 2         20 !$e; # No errors
1868             }
1869              
1870             sub randomCheck($$$) #P Random insertions
1871 6     6 1 17 {my ($n, $N, $T) = @_; # Keys per node, log 10 nodes, log 10 number of tests
1872 6         17 local $numberOfKeysPerNode = $n;
1873 6         12 my $e = 0;
1874              
1875 6         25 for(1..10**$T) # Each test
1876 2220         112592 {my %t = map {$_=>2*$_} 1..10**$N;
  240000         455493  
1877 2220         28637 my $t = new; $t->insert($_, $t{$_}) for keys %t;
  2220         214485  
1878              
1879 2220         55624 for my $k(keys %t) # Delete each key in test
1880 240000 50       1689018 {++$e unless $t->find($k) == $t{$k}; $t->delete($k); delete $t{$k};
  240000         1847171  
  240000         454597  
1881 240000 50       544684 ++$e if defined $t->find($k);
1882             }
1883             }
1884              
1885 6         821 !$e; # No errors
1886             }
1887              
1888             my $start = time; # Tests
1889              
1890             eval {goto latest} if !caller(0) and -e "/home/phil"; # Go to latest test if specified
1891              
1892             if (1) { # Odd number of keys
1893             my $t = new;
1894             $t = disordered( 3, 256);
1895             ok disorderedCheck($t, 3, 256);
1896             }
1897              
1898             if (1) { # Even number of keys
1899             my $t = new;
1900             $t = disordered( 4, 256);
1901             ok disorderedCheck($t, 4, 256);
1902             }
1903              
1904             if (1) {
1905             local $numberOfKeysPerNode = 15;
1906              
1907             my $t = new; my $N = 256;
1908              
1909             $t->insert($_, 2 * $_) for 1..$N;
1910              
1911             ok T($t, <
1912             64 128 192
1913             8 16 24 32 40 48 56
1914             1 2 3 4 5 6 7
1915             9 10 11 12 13 14 15
1916             17 18 19 20 21 22 23
1917             25 26 27 28 29 30 31
1918             33 34 35 36 37 38 39
1919             41 42 43 44 45 46 47
1920             49 50 51 52 53 54 55
1921             57 58 59 60 61 62 63
1922             72 80 88 96 104 112 120
1923             65 66 67 68 69 70 71
1924             73 74 75 76 77 78 79
1925             81 82 83 84 85 86 87
1926             89 90 91 92 93 94 95
1927             97 98 99 100 101 102 103
1928             105 106 107 108 109 110 111
1929             113 114 115 116 117 118 119
1930             121 122 123 124 125 126 127
1931             136 144 152 160 168 176 184
1932             129 130 131 132 133 134 135
1933             137 138 139 140 141 142 143
1934             145 146 147 148 149 150 151
1935             153 154 155 156 157 158 159
1936             161 162 163 164 165 166 167
1937             169 170 171 172 173 174 175
1938             177 178 179 180 181 182 183
1939             185 186 187 188 189 190 191
1940             200 208 216 224 232 240 248
1941             193 194 195 196 197 198 199
1942             201 202 203 204 205 206 207
1943             209 210 211 212 213 214 215
1944             217 218 219 220 221 222 223
1945             225 226 227 228 229 230 231
1946             233 234 235 236 237 238 239
1947             241 242 243 244 245 246 247
1948             249 250 251 252 253 254 255 256
1949             END
1950              
1951             if (1)
1952             {my $n = 0;
1953             for my $i(1..$N)
1954             {my $ii = $t->find($i);
1955             ++$n if $t->find($i) eq 2 * $i;
1956             }
1957             ok $n == $N;
1958             }
1959             }
1960              
1961             if (1) { # Large number of keys per node
1962             local $numberOfKeysPerNode = 15;
1963              
1964             my $t = new; my $N = 256;
1965              
1966             my @t = reverse map{scalar reverse; s/\A0+//r} 1..$N;
1967             $t->insert($_, 2 * $_) for @t;
1968              
1969             ok T($t, <
1970             65 129 193
1971             9 17 25 33 41 49 57
1972             1 2 3 4 5 6 7 8
1973             10 11 12 13 14 15 16
1974             18 19 20 21 22 23 24
1975             26 27 28 29 30 31 32
1976             34 35 36 37 38 39 40
1977             42 43 44 45 46 47 48
1978             50 51 52 53 54 55 56
1979             58 59 60 61 62 63 64
1980             73 81 89 97 105 113 121
1981             66 67 68 69 70 71 72
1982             74 75 76 77 78 79 80
1983             82 83 84 85 86 87 88
1984             90 91 92 93 94 95 96
1985             98 99 100 101 102 103 104
1986             106 107 108 109 110 111 112
1987             114 115 116 117 118 119 120
1988             122 123 124 125 126 127 128
1989             137 145 153 161 169 177 185
1990             130 131 132 133 134 135 136
1991             138 139 140 141 142 143 144
1992             146 147 148 149 150 151 152
1993             154 155 156 157 158 159 160
1994             162 163 164 165 166 167 168
1995             170 171 172 173 174 175 176
1996             178 179 180 181 182 183 184
1997             186 187 188 189 190 191 192
1998             201 209 217 225 233 241 249
1999             194 195 196 197 198 199 200
2000             202 203 204 205 206 207 208
2001             210 211 212 213 214 215 216
2002             218 219 220 221 222 223 224
2003             226 227 228 229 230 231 232
2004             234 235 236 237 238 239 240
2005             242 243 244 245 246 247 248
2006             250 251 252 253 254 255 256
2007             END
2008              
2009             if (1)
2010             {my $n = 0;
2011             for my $i(@t)
2012             {my $ii = $t->find($i);
2013             ++$n if $t->find($i) eq 2 * $i;
2014             }
2015             ok $n == $N;
2016             }
2017             }
2018              
2019             if (1) { #Titerator #TTree::Multi::Iterator::next #TTree::Multi::Iterator::more
2020             local $numberOfKeysPerNode = 3; my $N = 256; my $e = 0; my $t = new;
2021              
2022             for my $n(0..$N)
2023             {$t->insert($n, $n);
2024             my @n; for(my $i = $t->iterator; $i->more; $i->next) {push @n, $i->key}
2025             ++$e unless dump(\@n) eq dump [0..$n];
2026             }
2027              
2028             is_deeply $e, 0;
2029             }
2030              
2031             if (1) { #TleftMost #TrightMost #Tleaf #Troot
2032             local $numberOfKeysPerNode = 3; my $N = 13; my $t = new;
2033              
2034             for my $n(1..$N)
2035             {$t->insert($n, $n);
2036             }
2037              
2038             ok T($t, <
2039             4 8
2040             2
2041             1
2042             3
2043             6
2044             5
2045             7
2046             10 12
2047             9
2048             11
2049             13
2050             END
2051              
2052             is_deeply $t->leftMost ->keys, [1];
2053             is_deeply $t->rightMost->keys, [13];
2054             ok $t->leftMost ->leaf;
2055             ok $t->rightMost->leaf;
2056             ok $t->root == $t;
2057             }
2058              
2059             if (1) { #TreverseIterator #TTree::Multi::ReverseIterator::prev #TTree::Multi::ReverseIterator::less
2060             local $numberOfKeysPerNode = 3; my $N = 64; my $e = 0;
2061              
2062             for my $n(0..$N)
2063             {my $t = new;
2064             for my $i(0..$n)
2065             {$t->insert($i, $i);
2066             }
2067             my @n;
2068             for(my $i = $t->reverseIterator; $i->less; $i->prev)
2069             {push @n, $i->key;
2070             }
2071             ++$e unless dump(\@n) eq dump [reverse 0..$n];
2072             }
2073              
2074             is_deeply $e, 0;
2075             }
2076              
2077             if (1) { #Theight #Tdepth #Tsize #Tflat
2078             local $Tree::Multi::numberOfKeysPerNode = 3;
2079             my $t = new; ok $t->height == 0; ok $t->leftMost->depth == 0; ok $t->size == 0;
2080             $t->insert(1, 1); ok $t->height == 1; ok $t->leftMost->depth == 1; ok $t->size == 1;
2081             $t->insert(2, 2); ok $t->height == 1; ok $t->leftMost->depth == 1; ok $t->size == 2;
2082             $t->insert(3, 3); ok $t->height == 1; ok $t->leftMost->depth == 1; ok $t->size == 3;
2083             $t->insert(4, 4); ok $t->height == 2; ok $t->leftMost->depth == 2; ok $t->size == 4;
2084             $t->insert(5, 5); ok $t->height == 2; ok $t->leftMost->depth == 2; ok $t->size == 5;
2085             $t->insert(6, 6); ok $t->height == 2; ok $t->leftMost->depth == 2; ok $t->size == 6;
2086             $t->insert(7, 7); ok $t->height == 2; ok $t->leftMost->depth == 2; ok $t->size == 7;
2087             $t->insert(8, 8); ok $t->height == 3; ok $t->leftMost->depth == 3; ok $t->size == 8;
2088              
2089             T($t, <
2090              
2091             4
2092             2 6
2093             1 3 5 7 8
2094             END
2095              
2096             }
2097              
2098             ok &randomCheck(3, $develop ? (2, 1) : (2, 3)); # Randomize and check against a Perl hash
2099             ok &randomCheck(4, $develop ? (2, 1) : (2, 3));
2100             ok &randomCheck(5, $develop ? (2, 1) : (2, 2));
2101             ok &randomCheck(6, $develop ? (2, 1) : (2, 2));
2102             ok &randomCheck(7, $develop ? (2, 1) : (3, 1));
2103             ok &randomCheck(8, $develop ? (2, 1) : (3, 1));
2104              
2105             if (1) { # Synopsis #Tnew #Tinsert #Tfind #Tdelete #Tprint #Titerator
2106             local $Tree::Multi::numberOfKeysPerNode = 4; # Number of keys per node - can be even
2107              
2108             my $t = Tree::Multi::new; # Construct tree
2109             $t->insert($_, 2 * $_) for reverse 1..32; # Load tree in reverse
2110              
2111             T($t, <
2112             17 25
2113             9 13
2114             3 5 7
2115             1 2
2116             4
2117             6
2118             8
2119             11
2120             10
2121             12
2122             15
2123             14
2124             16
2125             21
2126             19
2127             18
2128             20
2129             23
2130             22
2131             24
2132             29
2133             27
2134             26
2135             28
2136             31
2137             30
2138             32
2139             END
2140              
2141             ok $t->size == 32; # Size
2142             ok $t->height == 4; # Height
2143             ok $t->delete(16) == 2 * 16; # Delete a key
2144             ok !$t->find (16); # Key no longer present
2145             ok $t->find (17) == 34; # Find by key
2146              
2147             my @k;
2148             for(my $i = $t->iterator; $i->more; $i->next) # Iterator
2149             {push @k, $i->key unless $i->key == 17;
2150             }
2151              
2152             ok $t->delete($_) == 2 * $_ for @k; # Delete
2153              
2154             ok $t->find(17) == 34 && $t->size == 1; # Size
2155             }
2156              
2157             #latest:;
2158             if (1) { # Synopsis #Tnew #Tinsert #Tfind #Tdelete #Tprint #Titerator
2159             local $Tree::Multi::numberOfKeysPerNode = 3; # Number of keys per node - can be even
2160              
2161             my $t = Tree::Multi::new; # Construct tree
2162             $t->insert($_, $_) for 1..8;
2163              
2164             T($t, <
2165              
2166             4
2167             2 6
2168             1 3 5 7 8
2169             END
2170             }
2171              
2172             lll "Success:", sprintf("%5.2f seconds", time - $start);