File Coverage

blib/lib/Tree/RB.pm
Criterion Covered Total %
statement 223 285 78.2
branch 112 170 65.8
condition 29 50 58.0
subroutine 33 37 89.1
pod 11 11 100.0
total 408 553 73.7


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