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   106173 use strict;
  6         14  
  6         248  
4 6     6   29 use Carp;
  6         10  
  6         457  
5            
6 6     6   2552 use Tree::RB::Node qw[set_color color_of parent_of left_of right_of];
  6         14  
  6         468  
7 6     6   31 use Tree::RB::Node::_Constants;
  6         6  
  6         395  
8 6     6   28 use vars qw( $VERSION @EXPORT_OK );
  6         6  
  6         522  
9             $VERSION = '0.500_005';
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         26 use enum qw{
17             LUEQUAL
18             LUGTEQ
19             LULTEQ
20             LUGREAT
21             LULESS
22             LUNEXT
23             LUPREV
24 6     6   52 };
  6         8  
25            
26             # object slots
27 6         18 use enum qw{
28             ROOT
29             CMP
30             SIZE
31             HASH_ITER
32             HASH_SEEK_ARG
33 6     6   3073 };
  6         11  
34            
35             # Node and hash Iteration
36            
37             sub _mk_iter {
38 12   50 12   37 my $start_fn = shift || 'min';
39 12   50     40 my $next_fn = shift || 'successor';
40             return sub {
41 30     30   1023 my $self = shift;
42 30         30 my $key = shift;
43 30         23 my $node;
44             my $iter = sub {
45 73 100   73   104 if($node) {
46 45         105 $node = $node->$next_fn;
47             }
48             else {
49 28 100       41 if(defined $key) {
50             # seek to $key
51 16 100       50 (undef, $node) = $self->lookup(
52             $key,
53             $next_fn eq 'successor' ? LUGTEQ : LULTEQ
54             );
55             }
56             else {
57 12         29 $node = $self->$start_fn;
58             }
59             }
60 73         215 return $node;
61 30         105 };
62 30         110 return bless($iter => 'Tree::RB::Iterator');
63 12         45 };
64             }
65            
66 73     73   6914 *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 2706 my $self = shift;
73 10         11 my $arg = shift;
74 10 50       22 defined $arg or croak("Can't seek to an undefined key");
75 10         9 my %args;
76 10 100       19 if(ref $arg eq 'HASH') {
77 3         10 %args = %$arg;
78             }
79             else {
80 7         17 $args{-key} = $arg;
81             }
82            
83 10 100 66     35 if(@_ && exists $args{-key}) {
84 3         3 my $arg = shift;
85 3 50       8 if(ref $arg eq 'HASH') {
86 3         15 %args = (%$arg, %args);
87             }
88             }
89 10 100       20 if(! exists $args{-key}) {
90 1 50       3 defined $args{'-reverse'} or croak("Expected option '-reverse' is undefined");
91             }
92 10         16 $self->[HASH_SEEK_ARG] = \%args;
93 10 100       27 if($self->[HASH_ITER]) {
94 9         13 $self->_reset_hash_iter;
95             }
96             }
97            
98             sub _reset_hash_iter {
99 20     20   22 my $self = shift;
100 20 100       37 if($self->[HASH_SEEK_ARG]) {
101 12 100       26 my $iter = ($self->[HASH_SEEK_ARG]{'-reverse'} ? 'rev_iter' : 'iter');
102 12         29 $self->[HASH_ITER] = $self->$iter($self->[HASH_SEEK_ARG]{'-key'});
103             }
104             else {
105 8         14 $self->[HASH_ITER] = $self->iter;
106             }
107             }
108            
109             sub FIRSTKEY {
110 11     11   520 my $self = shift;
111 11         17 $self->_reset_hash_iter;
112            
113 11 100       42 my $node = $self->[HASH_ITER]->next
114             or return;
115 8         25 return $node->[_KEY];
116             }
117            
118             sub NEXTKEY {
119 39     39   1941 my $self = shift;
120            
121 39 100       65 my $node = $self->[HASH_ITER]->next
122             or return;
123 32         82 return $node->[_KEY];
124             }
125            
126             sub new {
127 7     7 1 4878 my ($class, $cmp) = @_;
128 7         16 my $obj = [];
129 7         15 $obj->[SIZE] = 0;
130 7 100       21 if($cmp) {
131 1 50       4 ref $cmp eq 'CODE'
132             or croak('Invalid arg: codref expected');
133 1         2 $obj->[CMP] = $cmp;
134             }
135 7         27 return bless $obj => $class;
136             }
137            
138             *TIEHASH = \&new;
139            
140 8 100   8   1391 sub DESTROY { $_[0]->[ROOT]->DESTROY if $_[0]->[ROOT] }
141            
142             sub CLEAR {
143 4     4   291 my $self = shift;
144 4 100       21 if($self->[ROOT]) {
145 1         5 $self->[ROOT]->DESTROY;
146 1         2 undef $self->[ROOT];
147 1         2 undef $self->[HASH_ITER];
148 1         8 $self->[SIZE] = 0;
149             }
150             }
151            
152             sub UNTIE {
153 2     2   3 my $self = shift;
154 2         5 $self->DESTROY;
155 2         12 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 638 sub size { $_[0]->[SIZE] }
172            
173             *SCALAR = \&size;
174            
175             sub min {
176 18     18 1 20 my $self = shift;
177 18 100       51 return undef unless $self->[ROOT];
178 14         42 return $self->[ROOT]->min;
179             }
180            
181             sub max {
182 10     10 1 15 my $self = shift;
183 10 50       27 return undef unless $self->[ROOT];
184 10         34 return $self->[ROOT]->max;
185             }
186            
187             sub lookup {
188 51     51 1 1541 my $self = shift;
189 51         45 my $key = shift;
190 51 50       101 defined $key
191             or croak("Can't use undefined value as key");
192 51   100     116 my $mode = shift || LUEQUAL;
193 51         50 my $cmp = $self->[CMP];
194            
195 51         47 my $y;
196 51 100       91 my $x = $self->[ROOT]
197             or return;
198 48         39 my $next_child;
199 48         84 while($x) {
200 116         83 $y = $x;
201 116 50       237 if($cmp ? $cmp->($key, $x->[_KEY]) == 0
    100          
202             : $key eq $x->[_KEY]) {
203             # found it!
204 30 50 33     189 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       112 ? ($x->[_VAL], $x)
212             : $x->[_VAL];
213             }
214 86 50       148 if($cmp ? $cmp->($key, $x->[_KEY]) < 0
    100          
215             : $key lt $x->[_KEY]) {
216 41         32 $next_child = _LEFT;
217             }
218             else {
219 45         43 $next_child = _RIGHT;
220             }
221 86         136 $x = $x->[$next_child];
222             }
223             # Didn't find it :(
224 18 100 100     101 if($mode == LUGTEQ || $mode == LUGREAT) {
    50 33        
225 10 100       16 if($next_child == _LEFT) {
226 5 100       21 return wantarray ? ($y->[_VAL], $y) : $y->[_VAL];
227             }
228             else {
229 5 100       17 my $next = $y->successor
230             or return;
231 2 50       8 return wantarray ? ($next->[_VAL], $next) : $next->[_VAL];
232             }
233             }
234             elsif($mode == LULTEQ || $mode == LULESS) {
235 8 100       16 if($next_child == _RIGHT) {
236 3 100       17 return wantarray ? ($y->[_VAL], $y) : $y->[_VAL];
237             }
238             else {
239 5 100       14 my $next = $y->predecessor
240             or return;
241 2 100       13 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 12 my ($self, $i) = @_;
252            
253 8 50       36 $i =~ /^-?\d+$/
254             or croak('Integer index expected');
255 8 100       14 if ($i < 0) {
256 4         6 $i += $self->[SIZE];
257             }
258 8 50 33     34 if ($i < 0 || $i >= $self->[SIZE]) {
259 0         0 return;
260             }
261            
262 8         8 my ($node, $next, $moves);
263 8 100       19 if ($i > $self->[SIZE] / 2) {
264 4         9 $node = $self->max;
265 4         5 $next = 'predecessor';
266 4         7 $moves = $self->[SIZE] - $i - 1;
267             }
268             else {
269 4         7 $node = $self->min;
270 4         5 $next = 'successor';
271 4         4 $moves = $i;
272             }
273            
274 8         5 my $count = 0;
275 8         16 while ($count != $moves) {
276 4         10 $node = $node->$next;
277 4         8 ++$count;
278             }
279 8         21 return $node;
280             }
281            
282             sub EXISTS {
283 2     2   7 my $self = shift;
284 2         3 my $key = shift;
285 2         4 return defined $self->lookup($key);
286             }
287            
288             sub put {
289 40     40 1 748 my $self = shift;
290 40         39 my $key_or_node = shift;
291 40 50       72 defined $key_or_node
292             or croak("Can't use undefined value as key or node");
293 40         39 my $val = shift;
294            
295 40         43 my $cmp = $self->[CMP];
296 40 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 40         42 my $y;
301 40         39 my $x = $self->[ROOT];
302 40         78 while($x) {
303 53         54 $y = $x;
304             # Handle case of inserting node with duplicate key.
305 53 100       116 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       159 if($cmp ? $cmp->($z->[_KEY], $x->[_KEY]) < 0
    100          
314             : $z->[_KEY] lt $x->[_KEY])
315             {
316 25         52 $x = $x->[_LEFT];
317             }
318             else {
319 28         54 $x = $x->[_RIGHT];
320             }
321             }
322             # insert new node
323 40         63 $z->[_PARENT] = $y;
324 40 100       61 if(not defined $y) {
325 8         12 $self->[ROOT] = $z;
326             }
327             else {
328 32 100       73 if($cmp ? $cmp->($z->[_KEY], $y->[_KEY]) < 0
    100          
329             : $z->[_KEY] lt $y->[_KEY])
330             {
331 17         30 $y->[_LEFT] = $z;
332             }
333             else {
334 15         27 $y->[_RIGHT] = $z;
335             }
336             }
337 40         73 $self->_fix_after_insertion($z);
338 40         93 $self->[SIZE]++;
339             }
340            
341             *STORE = \&put;
342            
343             sub _fix_after_insertion {
344 40     40   41 my $self = shift;
345 40 50       64 my $x = shift or croak('Missing arg: node');
346            
347 40         48 $x->[_COLOR] = RED;
348 40   100     168 while($x != $self->[ROOT] && $x->[_PARENT][_COLOR] == RED) {
349 7         9 my ($child, $rotate1, $rotate2);
350 7 100 50     51 if(($x->[_PARENT] || 0) == ($x->[_PARENT][_PARENT][_LEFT] || 0)) {
      50        
351 1         2 ($child, $rotate1, $rotate2) = (_RIGHT, '_left_rotate', '_right_rotate');
352             }
353             else {
354 6         14 ($child, $rotate1, $rotate2) = (_LEFT, '_right_rotate', '_left_rotate');
355             }
356 7         15 my $y = $x->[_PARENT][_PARENT][$child];
357            
358 7 50 33     42 if($y && $y->[_COLOR] == RED) {
359 7         11 $x->[_PARENT][_COLOR] = BLACK;
360 7         11 $y->[_COLOR] = BLACK;
361 7         11 $x->[_PARENT][_PARENT][_COLOR] = RED;
362 7         24 $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         66 $self->[ROOT][_COLOR] = BLACK;
375             }
376            
377             sub delete {
378 7     7 1 17 my ($self, $key_or_node) = @_;
379 7 50       19 defined $key_or_node
380             or croak("Can't use undefined value as key or node");
381            
382 7 50       22 my $z = (ref $key_or_node eq 'Tree::RB::Node')
383             ? $key_or_node
384             : ($self->lookup($key_or_node))[1];
385 7 50       15 return unless $z;
386            
387 7         6 my $y;
388 7 100 100     23 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     54 my $x = $y->[_LEFT] || $y->[_RIGHT];
410 7 100       19 if(defined $x) {
    100          
411 4         7 $x->[_PARENT] = $y->[_PARENT];
412 4 50       13 if(! defined $y->[_PARENT]) {
    100          
413 0         0 $self->[ROOT] = $x;
414             }
415             elsif($y == $y->[_PARENT][_LEFT]) {
416 2         2 $y->[_PARENT][_LEFT] = $x;
417             }
418             else {
419 2         3 $y->[_PARENT][_RIGHT] = $x;
420             }
421             # Null out links so they are OK to use by _fix_after_deletion
422 4         5 delete @{$y}[_PARENT, _LEFT, _RIGHT];
  4         7  
423            
424             # Fix replacement
425 4 50       9 if($y->[_COLOR] == BLACK) {
426 4         9 $self->_fix_after_deletion($x);
427             }
428             }
429             elsif(! defined $y->[_PARENT]) {
430             # return if we are the only node
431 2         4 delete $self->[ROOT];
432             }
433             else {
434             # No children. Use self as phantom replacement and unlink
435 1 50       3 if($y->[_COLOR] == BLACK) {
436 1         2 $self->_fix_after_deletion($y);
437             }
438 1 50       6 if(defined $y->[_PARENT]) {
439 6     6   14836 no warnings 'uninitialized';
  6         12  
  6         1045  
440 1 50       4 if($y == $y->[_PARENT][_LEFT]) {
    50          
441 0         0 delete $y->[_PARENT][_LEFT];
442             }
443             elsif($y == $y->[_PARENT][_RIGHT]) {
444 1         2 delete $y->[_PARENT][_RIGHT];
445             }
446 1         1 delete $y->[_PARENT];
447             }
448             }
449 7         9 $self->[SIZE]--;
450 7         24 return $y;
451             }
452            
453             *DELETE = \&delete;
454            
455             sub _fix_after_deletion {
456 5     5   6 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         1 my ($child1, $child2, $rotate1, $rotate2);
461 6     6   35 no warnings 'uninitialized';
  6         8  
  6         519  
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   30 use warnings;
  6         9  
  6         3159  
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         8 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         15 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__