File Coverage

blib/lib/Tree/Compat/Tree/Nary.pm
Criterion Covered Total %
statement 19 21 90.4
branch n/a
condition n/a
subroutine 7 7 100.0
pod n/a
total 26 28 92.8


line stmt bran cond sub pod time code
1             package Tree::Compat::Tree::Nary;
2              
3 2     2   9 use strict;
  2         4  
  2         56  
4 2     2   10 use warnings;
  2         4  
  2         104  
5              
6             our $VERSION = '1.00';
7              
8             package Tree::Nary;
9              
10             # Some notes:
11             # 1) Tree::Nary has all class methods that take an object as an argument.
12             # Hence, the "sub foo { shift;" idiom.
13             # 2) The children look to be implemented as a linked list, not an array.
14             # This may cause problems in passing the tests.
15              
16             # Set %INC so that require() thinks Tree::Nary has already been loaded
17             $INC{'Tree/Nary.pm'} = $INC{'Tree::Compat::Tree::Nary'};
18              
19 2     2   9 use strict;
  2         8  
  2         44  
20 2     2   7 use warnings;
  2         3  
  2         61  
21              
22 2     2   9 use Scalar::Util qw( blessed weaken );
  2         11  
  2         208  
23 2     2   1762 use Tree;
  2         15117  
  2         27  
24 2     2   1198 use Tree::Binary; # For the in-order traversal constant
  0            
  0            
25              
26             use vars qw($TRUE $FALSE);
27             use vars qw($TRAVERSE_LEAFS $TRAVERSE_NON_LEAFS $TRAVERSE_ALL $TRAVERSE_MASK);
28             use vars qw($IN_ORDER $PRE_ORDER $POST_ORDER $LEVEL_ORDER);
29              
30             # Booleans
31             *TRUE = \1;
32             *FALSE = \0;
33              
34             # Tree traverse flags
35             *TRAVERSE_LEAFS = \(1 << 0); # Only leaf nodes should be visited.
36             *TRAVERSE_NON_LEAFS = \(1 << 1); # Only non-leaf nodes should be visited.
37             *TRAVERSE_ALL = \($TRAVERSE_LEAFS | $TRAVERSE_NON_LEAFS); # All nodes should be visited.
38             *TRAVERSE_MASK = \0x03;
39              
40             # Tree traverse orders
41             *IN_ORDER = \1;
42             *PRE_ORDER = \2;
43             *POST_ORDER = \3;
44             *LEVEL_ORDER = \4;
45              
46             sub new {
47             my $class = shift;
48             my ($data) = @_;
49              
50             my $tree = Tree->new();
51             $tree->error_handler( $tree->DIE );
52              
53             my $self = bless \$tree, $class;
54              
55             $tree->meta->{compat}{object} = $self;
56             weaken( $self );
57              
58             if ( defined $data ) {
59             $tree->set_value( $data );
60             }
61              
62             return $self;
63             }
64              
65             sub REAL_TREE { ${+shift} }
66              
67             sub unlink {
68             }
69              
70             sub is_root { shift;
71             # Check against next and prev too?
72             ${$_[0]}->is_root;
73             }
74              
75             sub is_leaf { shift;
76             ${$_[0]}->is_leaf;
77             }
78              
79             sub _parent {
80             my $parent = ${$_[0]}->parent;
81             return $parent->meta->{compat}{object} if $parent;
82             return;
83             }
84              
85             sub is_ancestor { shift;
86             my ($self, $child) = @_;
87              
88             return unless $self && $child;
89              
90             while ( $child ) {
91             my $parent = $child->_parent;
92              
93             if ( $parent && $parent == $self ) {
94             return 1;
95             }
96              
97             $child = $parent;
98             }
99              
100             return;
101             }
102              
103             sub get_root { shift;
104             ${$_[0]}->root->meta->{compat}{object};
105             }
106              
107             sub depth { shift;
108             ${$_[0]}->depth;
109             }
110              
111             sub reverse_children { shift;
112             my $self = shift;
113             my $tree = ${$self};
114              
115             $tree->add_child( reverse $tree->remove_child( $tree->children ) );
116              
117             return;
118             }
119              
120             sub max_height { shift;
121             ${$_[0]}->height;
122             }
123              
124             sub n_children { shift;
125             scalar ${$_[0]}->children;
126             }
127              
128             sub child_position { shift;
129             my ($self, $child) = @_;
130              
131             if ( !$self || !$child || $self ne $child->_parent ) {
132             return -1;
133             }
134              
135             ${$self}->get_index_for( ${$child} );
136             }
137              
138             sub child_index { shift;
139             my ($self, $data) = @_;
140              
141             return -1 unless defined $self;
142              
143             my @children = ${$self}->children;
144             foreach my $n ( 0 .. $#children ) {
145             if ( $children[$n]->value eq $data ) {
146             return $n;
147             }
148             }
149              
150             return;
151             }
152              
153             sub first_sibling { shift;
154             my ($self) = @_;
155             return unless $self;
156              
157             my $parent = $self->_parent
158             or return $self;
159             return ${$parent}->children( 0 )->meta->{compat}{object};
160             }
161              
162             sub next_sibling { shift;
163             my ($self) = @_;
164             return unless $self;
165              
166             my $tree = ${$self};
167             my $i = $tree->parent->get_index_for( $tree ) + 1;
168             my $num_children = $tree->parent->children;
169             return unless $i <= $num_children;
170             return ${$self}->children( $i )->meta->{compat}{object};
171             }
172              
173             sub prev_sibling { shift;
174             my ($self) = @_;
175             return unless $self;
176              
177             my $tree = ${$self};
178             my $i = $tree->parent->get_index_for( $tree ) - 1;
179             return unless $i >= 0;
180             return ${$self}->children( $i )->meta->{compat}{object};
181             }
182              
183             sub last_sibling { shift;
184             my ($self) = @_;
185             return unless $self;
186              
187             my $parent = $self->_parent
188             or return $self;
189             return ${$parent}->children(
190             scalar ${$parent}->children
191             )->meta->{compat}{object};
192             }
193              
194             sub n_nodes { shift;
195             my ($self, $flags) = @_;
196             return 0 unless $self;
197             return 0 unless $flags <= $TRAVERSE_MASK;
198              
199             ${$self}->size;
200             }
201              
202             sub first_child { shift;
203             return unless $_[0];
204             ${$_[0]}->children( 0 )->meta->{compat}{object};
205             }
206              
207             sub last_child { shift;
208             return unless $_[0];
209             return ${$_[0]}->children( scalar ${$_[0]}->children );
210             }
211              
212             sub nth_child { shift;
213             return unless $_[0];
214             return ${$_[0]}->children( $_[1] );
215             }
216              
217             sub insert { shift;
218             my ($self, $position, $child) = @_;
219              
220             if ( !$self && !defined $child && !$child->is_root ) {
221             return $child;
222             }
223              
224             ${$self}->add_child( { at => $position }, ${$child} );
225             return $child;
226             }
227              
228             sub insert_data {
229             my ($class, $parent, $sibling, $data ) = @_;
230             $class->insert( $parent, $sibling, $class->new( $data ) );
231             }
232              
233             sub insert_before {
234             my $class = shift;
235             my ($self, $sibling, $child) = @_;
236              
237             if ( !$self && !defined $child && !$child->is_root ) {
238             return $child;
239             }
240              
241             if ( defined $sibling ) {
242             if ( $sibling->_parent ne $self ) {
243             return $child;
244             }
245             my $i = $class->child_position( $self, $sibling );
246             ${$self}->add_child( { at => $i }, $child );
247             }
248             else {
249             ${$self}->add_child( ${$child} );
250             }
251              
252             return $child;
253             }
254              
255             sub insert_data_before {
256             my ($class, $parent, $sibling, $data ) = @_;
257             $class->insert_before( $parent, $sibling, $class->new( $data ) );
258             }
259              
260             sub append() {
261             my ($self, $parent, $node) = @_;
262              
263             $self->insert_before($parent, undef, $node);
264             }
265              
266             sub append_data() {
267             my ($self, $parent, $data) = @_;
268              
269             $self->insert_before($parent, undef, $self->new($data));
270             }
271              
272             sub prepend() {
273             my ($self, $parent, $node) = @_;
274              
275             return $node unless $parent;
276              
277             ${$parent}->add_child( { at => 0 }, $node );
278             }
279              
280             sub prepend_data() {
281             my ($self, $parent, $data) = @_;
282              
283             $self->prepend($parent, $self->new($data));
284             }
285              
286             sub traverse {
287             my ($self, $root, $order, $flags, $depth, $funcref, $argref) = @_;
288              
289             unless (
290             $root && $funcref && $order <= $LEVEL_ORDER
291             && $flags < $TRAVERSE_MASK || ($depth == -1 || $depth > 0)
292             ) {
293             return;
294             }
295              
296             my %convert = (
297             $PRE_ORDER => Tree->PRE_ORDER,
298             $POST_ORDER => Tree->POST_ORDER,
299             $LEVEL_ORDER => Tree->LEVEL_ORDER,
300             $IN_ORDER => Tree::Binary->IN_ORDER,
301             );
302              
303             my $traversal = ${$root}->traverse( Tree->PRE_ORDER );
304              
305             while ( my $node = $traversal->() ) {
306             # $depth == 0 cannot happen
307             # -1 will never be greater than $node->depth
308             next if $depth > $node->depth;
309              
310             if ( $node->is_leaf ) {
311             if ($flags & $TRAVERSE_LEAFS) {
312             if ( $funcref->($node->meta->{compat}{object}, $argref) ) {
313             last;
314             }
315             }
316             }
317             else {
318             if ($flags & $TRAVERSE_NON_LEAFS) {
319             if ( $funcref->($node->meta->{compat}{object}, $argref) ) {
320             last;
321             }
322             }
323             }
324             }
325              
326             return;
327             }
328              
329             sub find {
330             my ($self, $root, $order, $flags, $data) = @_;
331              
332             unless ( $root && $order <= $LEVEL_ORDER && $flags <= $TRAVERSE_MASK ) {
333             return;
334             }
335              
336             my $found;
337             $self->traverse(
338             $root, $order, $flags, -1, sub {
339             my $node = shift;
340             if ( $data eq ${$node}->value ) {
341             $found = $node;
342             return 1;
343             }
344              
345             return;
346             },
347             );
348              
349             return $found;
350             }
351              
352             sub find_child {
353             my ($self, $node, $flags, $data) = @_;
354              
355             unless ( $node && $flags <= $TRAVERSE_MASK ) {
356             return;
357             }
358              
359             foreach my $node ( ${node}->children ) {
360             if (
361             $node->is_leaf && $flags & $TRAVERSE_LEAFS
362             || $flags & $TRAVERSE_NON_LEAFS
363             ) {
364             return $node if $node->value eq $data;
365             }
366             }
367             }
368              
369             sub children_foreach {
370             my ($self, $node, $flags, $funcref, $argref) = @_;
371              
372             unless ( $node && $funcref && $flags <= $TRAVERSE_MASK ) {
373             return;
374             }
375              
376             foreach my $node ( ${node}->children ) {
377             if (
378             $node->is_leaf && $flags & $TRAVERSE_LEAFS
379             || $flags & $TRAVERSE_NON_LEAFS
380             ) {
381             return $node if $funcref->( $node->meta->{compat}{object}, $argref);
382             }
383             }
384             }
385              
386             sub tsort {
387             my ($self, $node) = @_;
388              
389             return if $self->is_leaf( $node );
390              
391             my $tree = ${$node};
392             my @children = sort {
393             $b->value cmp $a->value
394             } $tree->remove_child( $tree->children );
395              
396             $tree->add_child( @children );
397             $self->tsort( $_->meta->{compat}{object} ) for @children;
398              
399             return;
400             }
401              
402             sub normalize {
403             my ($self, $node) = @_;
404              
405             return '*' if $self->is_leaf( $node );
406              
407             return '(' . join('',
408             sort map { $self->normalize( $_->meta->{compat}{object} ) } ${$node}->children
409             ) . ')';
410             }
411              
412             sub is_identical {
413             my ($self, $n1, $n2) = @_;
414              
415             my $tree1 = ${$n1};
416             my $tree2 = ${$n2};
417              
418             return if $tree1->value ne $tree2->value;
419              
420             # If they have the same number of children, their leaf-ness has been
421             # checked - a leaf will have 0 children.
422             my @c1 = $tree1->children;
423             my @c2 = $tree2->children;
424             return if @c1 != @c2;
425              
426             for ( 0 .. $#c1 ) {
427             return unless $self->is_identical(
428             map { $_->meta->{compat}{object} } $c1[$_], $c2[$_]
429             );
430             }
431              
432             return 1;
433             }
434              
435             sub has_same_struct {
436             my ($self, $n1, $n2) = @_;
437              
438             return $self->normalize( $n1 ) eq $self->normalize( $n2 );
439             }
440              
441             1;
442             __END__