File Coverage

blib/lib/Tree/RB.pm
Criterion Covered Total %
statement 227 286 79.3
branch 112 170 65.8
condition 29 50 58.0
subroutine 33 37 89.1
pod 11 11 100.0
total 412 554 74.3


line stmt bran cond sub pod time code
1             package Tree::RB;
2              
3 6     6   257681 use strict;
  6         46  
  6         158  
4 6     6   32 use Carp;
  6         9  
  6         360  
5              
6 6     6   1704 use Tree::RB::Node qw[set_color color_of parent_of left_of right_of];
  6         16  
  6         365  
7 6     6   36 use Tree::RB::Node::_Constants;
  6         12  
  6         352  
8 6     6   34 use vars qw( $VERSION @EXPORT_OK );
  6         8  
  6         450  
9             $VERSION = '0.500006';
10             $VERSION = eval $VERSION;
11              
12             require Exporter;
13             *import = \&Exporter::import;
14             @EXPORT_OK = qw[LUEQUAL LUGTEQ LULTEQ LUGREAT LULESS LUNEXT LUPREV];
15              
16 6         22 use enum qw{
17             LUEQUAL
18             LUGTEQ
19             LULTEQ
20             LUGREAT
21             LULESS
22             LUNEXT
23             LUPREV
24 6     6   30 };
  6         12  
25              
26             # object slots
27 6         26 use enum qw{
28             ROOT
29             CMP
30             SIZE
31             HASH_ITER
32             HASH_SEEK_ARG
33 6     6   3034 };
  6         12  
34              
35             # Node and hash Iteration
36              
37             sub _mk_iter {
38 12   50 12   43 my $start_fn = shift || 'min';
39 12   50     26 my $next_fn = shift || 'successor';
40             return sub {
41 30     30   675 my $self = shift;
42 30         44 my $key = shift;
43 30         37 my $node;
44             my $iter = sub {
45 73 100   73   113 if($node) {
46 45         94 $node = $node->$next_fn;
47             }
48             else {
49 28 100       46 if(defined $key) {
50             # seek to $key
51 16 100       44 (undef, $node) = $self->lookup(
52             $key,
53             $next_fn eq 'successor' ? LUGTEQ : LULTEQ
54             );
55             }
56             else {
57 12         36 $node = $self->$start_fn;
58             }
59             }
60 73         180 return $node;
61 30         111 };
62 30         152 return bless($iter => 'Tree::RB::Iterator');
63 12         51 };
64             }
65              
66 73     73   4388 *Tree::RB::Iterator::next = sub { $_[0]->() };
67              
68             *iter = _mk_iter(qw/min successor/);
69             *rev_iter = _mk_iter(qw/max predecessor/);
70              
71             sub hseek {
72 10     10 1 3810 my $self = shift;
73 10         14 my $arg = shift;
74 10 50       22 defined $arg or croak("Can't seek to an undefined key");
75 10         11 my %args;
76 10 100       20 if(ref $arg eq 'HASH') {
77 3         9 %args = %$arg;
78             }
79             else {
80 7         12 $args{-key} = $arg;
81             }
82            
83 10 50 66     25 if(@_ && exists $args{-key}) {
84 3         6 my $arg = shift;
85 3 50       6 if(ref $arg eq 'HASH') {
86 3         12 %args = (%$arg, %args);
87             }
88             }
89 10 100       23 if(! exists $args{-key}) {
90 1 50       4 defined $args{'-reverse'} or croak("Expected option '-reverse' is undefined");
91             }
92 10         19 $self->[HASH_SEEK_ARG] = \%args;
93 10 100       20 if($self->[HASH_ITER]) {
94 9         15 $self->_reset_hash_iter;
95             }
96             }
97              
98             sub _reset_hash_iter {
99 20     20   28 my $self = shift;
100 20 100       38 if($self->[HASH_SEEK_ARG]) {
101 12 100       22 my $iter = ($self->[HASH_SEEK_ARG]{'-reverse'} ? 'rev_iter' : 'iter');
102 12         23 $self->[HASH_ITER] = $self->$iter($self->[HASH_SEEK_ARG]{'-key'});
103             }
104             else {
105 8         18 $self->[HASH_ITER] = $self->iter;
106             }
107             }
108              
109             sub FIRSTKEY {
110 11     11   829 my $self = shift;
111 11         26 $self->_reset_hash_iter;
112              
113 11 100       28 my $node = $self->[HASH_ITER]->next
114             or return;
115 8         29 return $node->[_KEY];
116             }
117              
118             sub NEXTKEY {
119 39     39   2068 my $self = shift;
120              
121 39 100       52 my $node = $self->[HASH_ITER]->next
122             or return;
123 32         84 return $node->[_KEY];
124             }
125              
126             sub new {
127 7     7 1 4406 my ($class, $cmp) = @_;
128 7         22 my $obj = [];
129 7         20 $obj->[SIZE] = 0;
130 7 100       25 if($cmp) {
131 1 50       5 ref $cmp eq 'CODE'
132             or croak('Invalid arg: codref expected');
133 1         2 $obj->[CMP] = $cmp;
134             }
135 7         29 return bless $obj => $class;
136             }
137              
138             *TIEHASH = \&new;
139              
140 8 100   8   1998 sub DESTROY { $_[0]->[ROOT]->DESTROY if $_[0]->[ROOT] }
141              
142             sub CLEAR {
143 4     4   464 my $self = shift;
144 4 100       17 if($self->[ROOT]) {
145 1         5 $self->[ROOT]->DESTROY;
146 1         3 undef $self->[ROOT];
147 1         6 undef $self->[HASH_ITER];
148 1         4 $self->[SIZE] = 0;
149             }
150             }
151              
152             sub UNTIE {
153 2     2   4 my $self = shift;
154 2         5 $self->DESTROY;
155 2         11 undef @$self;
156             }
157              
158             sub resort {
159 0     0 1 0 my $self = $_[0];
160 0         0 my $cmp = $_[1];
161 0 0 0     0 ref $cmp eq 'CODE'
162             or croak sprintf(q[Arg of type coderef required; got %s], ref $cmp || 'undef');
163              
164 0         0 my $new_tree = __PACKAGE__->new($cmp);
165 0     0   0 $self->[ROOT]->strip(sub { $new_tree->put($_[0]) });
  0         0  
166 0         0 $new_tree->put(delete $self->[ROOT]);
167 0         0 $_[0] = $new_tree;
168             }
169              
170 1     1 1 5 sub root { $_[0]->[ROOT] }
171 6     6 1 311 sub size { $_[0]->[SIZE] }
172              
173             *SCALAR = \&size;
174              
175             sub min {
176 18     18 1 58 my $self = shift;
177 18 100       61 return undef unless $self->[ROOT];
178 14         39 return $self->[ROOT]->min;
179             }
180              
181             sub max {
182 10     10 1 22 my $self = shift;
183 10 50       31 return undef unless $self->[ROOT];
184 10         34 return $self->[ROOT]->max;
185             }
186              
187             sub lookup {
188 51     51 1 2042 my $self = shift;
189 51         85 my $key = shift;
190 51 50       106 defined $key
191             or croak("Can't use undefined value as key");
192 51   100     138 my $mode = shift || LUEQUAL;
193 51         72 my $cmp = $self->[CMP];
194              
195 51         67 my $y;
196 51 100       135 my $x = $self->[ROOT]
197             or return;
198 48         58 my $next_child;
199 48         90 while($x) {
200 116         135 $y = $x;
201 116 50       230 if($cmp ? $cmp->($key, $x->[_KEY]) == 0
    100          
202             : $key eq $x->[_KEY]) {
203             # found it!
204 30 50 33     152 if($mode == LUGREAT || $mode == LUNEXT) {
    50 33        
205 0         0 $x = $x->successor;
206             }
207             elsif($mode == LULESS || $mode == LUPREV) {
208 0         0 $x = $x->predecessor;
209             }
210             return wantarray
211 30 100       130 ? ($x->[_VAL], $x)
212             : $x->[_VAL];
213             }
214 86 50       144 if($cmp ? $cmp->($key, $x->[_KEY]) < 0
    100          
215             : $key lt $x->[_KEY]) {
216 41         46 $next_child = _LEFT;
217             }
218             else {
219 45         54 $next_child = _RIGHT;
220             }
221 86         130 $x = $x->[$next_child];
222             }
223             # Didn't find it :(
224 18 100 100     69 if($mode == LUGTEQ || $mode == LUGREAT) {
    50 33        
225 10 100       20 if($next_child == _LEFT) {
226 5 100       19 return wantarray ? ($y->[_VAL], $y) : $y->[_VAL];
227             }
228             else {
229 5 100       16 my $next = $y->successor
230             or return;
231 2 50       9 return wantarray ? ($next->[_VAL], $next) : $next->[_VAL];
232             }
233             }
234             elsif($mode == LULTEQ || $mode == LULESS) {
235 8 100       18 if($next_child == _RIGHT) {
236 3 100       15 return wantarray ? ($y->[_VAL], $y) : $y->[_VAL];
237             }
238             else {
239 5 100       14 my $next = $y->predecessor
240             or return;
241 2 100       11 return wantarray ? ($next->[_VAL], $next) : $next->[_VAL];
242             }
243             }
244 0         0 return;
245             }
246              
247             *FETCH = \&lookup;
248             *get = \&lookup;
249              
250             sub nth {
251 8     8 1 18 my ($self, $i) = @_;
252              
253 8 50       43 $i =~ /^-?\d+$/
254             or croak('Integer index expected');
255 8 100       19 if ($i < 0) {
256 4         6 $i += $self->[SIZE];
257             }
258 8 50 33     32 if ($i < 0 || $i >= $self->[SIZE]) {
259 0         0 return;
260             }
261              
262 8         11 my ($node, $next, $moves);
263 8 100       21 if ($i > $self->[SIZE] / 2) {
264 4         10 $node = $self->max;
265 4         8 $next = 'predecessor';
266 4         7 $moves = $self->[SIZE] - $i - 1;
267             }
268             else {
269 4         10 $node = $self->min;
270 4         6 $next = 'successor';
271 4         6 $moves = $i;
272             }
273              
274 8         10 my $count = 0;
275 8         15 while ($count != $moves) {
276 4         10 $node = $node->$next;
277 4         7 ++$count;
278             }
279 8         24 return $node;
280             }
281              
282             sub EXISTS {
283 2     2   14 my $self = shift;
284 2         6 my $key = shift;
285 2         10 return defined $self->lookup($key);
286             }
287              
288             sub put {
289 41     41 1 1180 my $self = shift;
290 41         78 my $key_or_node = shift;
291 41 50       87 defined $key_or_node
292             or croak("Can't use undefined value as key or node");
293 41         61 my $val = shift;
294              
295 41         67 my $cmp = $self->[CMP];
296 41 50       144 my $z = (ref $key_or_node eq 'Tree::RB::Node')
297             ? $key_or_node
298             : Tree::RB::Node->new($key_or_node => $val);
299              
300 41         56 my $y;
301 41         58 my $x = $self->[ROOT];
302 41         124 while($x) {
303 56         70 $y = $x;
304             # Handle case of inserting node with duplicate key.
305 56 100       128 if($cmp ? $cmp->($z->[_KEY], $x->[_KEY]) == 0
    100          
306             : $z->[_KEY] eq $x->[_KEY])
307             {
308 1         1 my $old_val = $x->[_VAL];
309 1         2 $x->[_VAL] = $z->[_VAL];
310 1         4 return $old_val;
311             }
312              
313 55 100       126 if($cmp ? $cmp->($z->[_KEY], $x->[_KEY]) < 0
    100          
314             : $z->[_KEY] lt $x->[_KEY])
315             {
316 26         51 $x = $x->[_LEFT];
317             }
318             else {
319 29         62 $x = $x->[_RIGHT];
320             }
321             }
322             # insert new node
323 40         70 $z->[_PARENT] = $y;
324 40 100       69 if(not defined $y) {
325 8         17 $self->[ROOT] = $z;
326             }
327             else {
328 32 100       72 if($cmp ? $cmp->($z->[_KEY], $y->[_KEY]) < 0
    100          
329             : $z->[_KEY] lt $y->[_KEY])
330             {
331 17         28 $y->[_LEFT] = $z;
332             }
333             else {
334 15         26 $y->[_RIGHT] = $z;
335             }
336             }
337 40         90 $self->_fix_after_insertion($z);
338 40         57 $self->[SIZE]++;
339 40         103 return;
340             }
341              
342             *STORE = \&put;
343              
344             sub _fix_after_insertion {
345 40     40   90 my $self = shift;
346 40 50       88 my $x = shift or croak('Missing arg: node');
347              
348 40         55 $x->[_COLOR] = RED;
349 40   100     149 while($x != $self->[ROOT] && $x->[_PARENT][_COLOR] == RED) {
350 7         16 my ($child, $rotate1, $rotate2);
351 7 100 50     38 if(($x->[_PARENT] || 0) == ($x->[_PARENT][_PARENT][_LEFT] || 0)) {
      50        
352 1         3 ($child, $rotate1, $rotate2) = (_RIGHT, '_left_rotate', '_right_rotate');
353             }
354             else {
355 6         19 ($child, $rotate1, $rotate2) = (_LEFT, '_right_rotate', '_left_rotate');
356             }
357 7         14 my $y = $x->[_PARENT][_PARENT][$child];
358              
359 7 50 33     40 if($y && $y->[_COLOR] == RED) {
360 7         14 $x->[_PARENT][_COLOR] = BLACK;
361 7         13 $y->[_COLOR] = BLACK;
362 7         13 $x->[_PARENT][_PARENT][_COLOR] = RED;
363 7         22 $x = $x->[_PARENT][_PARENT];
364             }
365             else {
366 0 0 0     0 if($x == ($x->[_PARENT][$child] || 0)) {
367 0         0 $x = $x->[_PARENT];
368 0         0 $self->$rotate1($x);
369             }
370 0         0 $x->[_PARENT][_COLOR] = BLACK;
371 0         0 $x->[_PARENT][_PARENT][_COLOR] = RED;
372 0         0 $self->$rotate2($x->[_PARENT][_PARENT]);
373             }
374             }
375 40         75 $self->[ROOT][_COLOR] = BLACK;
376             }
377              
378             sub delete {
379 7     7 1 32 my ($self, $key_or_node) = @_;
380 7 50       24 defined $key_or_node
381             or croak("Can't use undefined value as key or node");
382              
383 7 50       34 my $z = (ref $key_or_node eq 'Tree::RB::Node')
384             ? $key_or_node
385             : ($self->lookup($key_or_node))[1];
386 7 50       26 return unless $z;
387              
388 7         14 my $y;
389 7 100 100     40 if($z->[_LEFT] && $z->[_RIGHT]) {
390             # (Notes kindly provided by Christopher Gurnee)
391             # When deleting a node 'z' which has two children from a binary search tree, the
392             # typical algorithm is to delete the successor node 'y' instead (which is
393             # guaranteed to have at most one child), and then to overwrite the key/values of
394             # node 'z' (which is still in the tree) with the key/values (which we don't want
395             # to lose) from the now-deleted successor node 'y'.
396              
397             # Since we need to return the deleted item, it's not good enough to overwrite the
398             # key/values of node 'z' with those of node 'y'. Instead we swap them so we can
399             # return the deleted values.
400              
401 1         7 $y = $z->successor;
402 1         6 ($z->[_KEY], $y->[_KEY]) = ($y->[_KEY], $z->[_KEY]);
403 1         4 ($z->[_VAL], $y->[_VAL]) = ($y->[_VAL], $z->[_VAL]);
404             }
405             else {
406 6         14 $y = $z;
407             }
408              
409             # splice out $y
410 7   100     33 my $x = $y->[_LEFT] || $y->[_RIGHT];
411 7 100       26 if(defined $x) {
    100          
412 4         11 $x->[_PARENT] = $y->[_PARENT];
413 4 50       15 if(! defined $y->[_PARENT]) {
    100          
414 0         0 $self->[ROOT] = $x;
415             }
416             elsif($y == $y->[_PARENT][_LEFT]) {
417 2         6 $y->[_PARENT][_LEFT] = $x;
418             }
419             else {
420 2         6 $y->[_PARENT][_RIGHT] = $x;
421             }
422             # Null out links so they are OK to use by _fix_after_deletion
423 4         11 delete @{$y}[_PARENT, _LEFT, _RIGHT];
  4         14  
424              
425             # Fix replacement
426 4 50       15 if($y->[_COLOR] == BLACK) {
427 4         14 $self->_fix_after_deletion($x);
428             }
429             }
430             elsif(! defined $y->[_PARENT]) {
431             # return if we are the only node
432 2         34 delete $self->[ROOT];
433             }
434             else {
435             # No children. Use self as phantom replacement and unlink
436 1 50       6 if($y->[_COLOR] == BLACK) {
437 1         4 $self->_fix_after_deletion($y);
438             }
439 1 50       5 if(defined $y->[_PARENT]) {
440 6     6   14021 no warnings 'uninitialized';
  6         13  
  6         1245  
441 1 50       7 if($y == $y->[_PARENT][_LEFT]) {
    50          
442 0         0 delete $y->[_PARENT][_LEFT];
443             }
444             elsif($y == $y->[_PARENT][_RIGHT]) {
445 1         4 delete $y->[_PARENT][_RIGHT];
446             }
447 1         3 delete $y->[_PARENT];
448             }
449             }
450 7         19 $self->[SIZE]--;
451 7         35 return $y;
452             }
453              
454             *DELETE = \&delete;
455              
456             sub _fix_after_deletion {
457 5     5   12 my $self = shift;
458 5 50       23 my $x = shift or croak('Missing arg: node');
459              
460 5   100     30 while($x != $self->[ROOT] && color_of($x) == BLACK) {
461 1         4 my ($child1, $child2, $rotate1, $rotate2);
462 6     6   46 no warnings 'uninitialized';
  6         12  
  6         501  
463 1 50       6 if($x == left_of(parent_of($x))) {
464 0         0 ($child1, $child2, $rotate1, $rotate2) =
465             (\&right_of, \&left_of, '_left_rotate', '_right_rotate');
466             }
467             else {
468 1         7 ($child1, $child2, $rotate1, $rotate2) =
469             (\&left_of, \&right_of, '_right_rotate', '_left_rotate');
470             }
471 6     6   36 use warnings;
  6         11  
  6         2903  
472              
473 1         5 my $w = $child1->(parent_of($x));
474 1 50       4 if(color_of($w) == RED) {
475 0         0 set_color($w, BLACK);
476 0         0 set_color(parent_of($x), RED);
477 0         0 $self->$rotate1(parent_of($x));
478 0         0 $w = right_of(parent_of($x));
479             }
480 1 50 33     5 if(color_of($child2->($w)) == BLACK &&
481             color_of($child1->($w)) == BLACK) {
482 1         5 set_color($w, RED);
483 1         5 $x = parent_of($x);
484             }
485             else {
486 0 0       0 if(color_of($child1->($w)) == BLACK) {
487 0         0 set_color($child2->($w), BLACK);
488 0         0 set_color($w, RED);
489 0         0 $self->$rotate2($w);
490 0         0 $w = $child1->(parent_of($x));
491             }
492 0         0 set_color($w, color_of(parent_of($x)));
493 0         0 set_color(parent_of($x), BLACK);
494 0         0 set_color($child1->($w), BLACK);
495 0         0 $self->$rotate1(parent_of($x));
496 0         0 $x = $self->[ROOT];
497             }
498             }
499 5         20 set_color($x, BLACK);
500             }
501              
502             sub _left_rotate {
503 0     0     my $self = shift;
504 0 0         my $x = shift or croak('Missing arg: node');
505              
506 0 0         my $y = $x->[_RIGHT]
507             or return;
508 0           $x->[_RIGHT] = $y->[_LEFT];
509 0 0         if($y->[_LEFT]) {
510 0           $y->[_LEFT]->[_PARENT] = $x;
511             }
512 0           $y->[_PARENT] = $x->[_PARENT];
513 0 0         if(not defined $x->[_PARENT]) {
514 0           $self->[ROOT] = $y;
515             }
516             else {
517 0 0         $x == $x->[_PARENT]->[_LEFT]
518             ? $x->[_PARENT]->[_LEFT] = $y
519             : $x->[_PARENT]->[_RIGHT] = $y;
520             }
521 0           $y->[_LEFT] = $x;
522 0           $x->[_PARENT] = $y;
523             }
524              
525             sub _right_rotate {
526 0     0     my $self = shift;
527 0 0         my $y = shift or croak('Missing arg: node');
528              
529 0 0         my $x = $y->[_LEFT]
530             or return;
531 0           $y->[_LEFT] = $x->[_RIGHT];
532 0 0         if($x->[_RIGHT]) {
533 0           $x->[_RIGHT]->[_PARENT] = $y
534             }
535 0           $x->[_PARENT] = $y->[_PARENT];
536 0 0         if(not defined $y->[_PARENT]) {
537 0           $self->[ROOT] = $x;
538             }
539             else {
540 0 0         $y == $y->[_PARENT]->[_RIGHT]
541             ? $y->[_PARENT]->[_RIGHT] = $x
542             : $y->[_PARENT]->[_LEFT] = $x;
543             }
544 0           $x->[_RIGHT] = $y;
545 0           $y->[_PARENT] = $x;
546             }
547              
548             1; # Magic true value required at end of module
549             __END__