File Coverage

blib/lib/Math/PartialOrder/Base.pm
Criterion Covered Total %
statement 198 496 39.9
branch 105 312 33.6
condition 49 141 34.7
subroutine 40 99 40.4
pod 55 68 80.8
total 447 1116 40.0


line stmt bran cond sub pod time code
1             # -*- Mode: Perl -*-
2              
3             #
4             # Copyright (c) 2001, Bryan Jurish. All rights reserved.
5             #
6             # This package is free software. You may redistribute it
7             # and/or modify it under the same terms as Perl itself.
8             #
9              
10             ###############################################################
11             #
12             # File: Math::PartialOrder::Base.pm
13             # Author: Bryan Jurish
14             #
15             # Description: Abstract base class for partial orders
16             #
17             ###############################################################
18              
19             package Math::PartialOrder::Base;
20             # System modules
21             require 5.6.0; # for those handy 'our' variables...
22 7     7   34 use Carp qw(:DEFAULT cluck);
  7         12  
  7         57401  
23             require Exporter;
24             # 3rd party exstensions
25             # user extension modules
26             @ISA = qw(Exporter);
27             @EXPORT = qw();
28             @EXPORT_OK = (
29             qw(&_subsumes_trivial &_psubsumes_trivial),
30             qw(&_subsumes_user &_psubsumes_user),
31             qw(&_lub_trivial &_glb_trivial),
32             qw(&_binop_user &_lub_user &_glb_user),
33             qw($TYPE_NONE $TYPE_TOP),
34             );
35             %EXPORT_TAGS =
36             (
37             trivialities => [qw(&_subsumes_trivial &_psubsumes_trivial),
38             qw(&_lub_trivial &_glb_trivial)],
39             userhooks => [qw(&_subsumes_user &_psubsumes_user),
40             qw(&_binop_user &_lub_user &_glb_user)],
41             typevars => [qw($TYPE_TOP)]
42             );
43              
44             ###############################################################
45             # Package-global variables
46             ###############################################################
47             our $VERSION = 0.01;
48              
49             our $WANT_USER_HOOKS = 1;
50              
51             our $VERBOSE = 1;
52             our $TYPE_TOP = '__TOP__';
53             *TYPE_NONE = \$TYPE_TOP;
54              
55             ###############################################################
56             # Constructor
57             # + Hierarchy->new()
58             # + Hierarchy->new( att1 => val1, ..., attN => valN )
59             ###############################################################
60              
61             # $h->new(), $h->new(\%args)
62             sub new ($;$) {
63 0     0 1 0 my $proto = shift;
64 0 0       0 if (ref($proto)) {
65 0         0 return bless %$proto, ref($proto);
66             }
67 0   0     0 my $self = shift || {};
68 0         0 bless $self, $proto;
69 0 0       0 $self->initialize(@_) if ($self->can('initialize'));
70 0         0 return $self;
71             }
72              
73             sub clone ($) {
74 5     5 1 1830 my $h = shift;
75 5         48 my $h2 = ref($h)->new();
76 5         33 $h2->assign($h);
77 5         24 return $h2;
78             }
79              
80 6     6 1 21 sub compiled ($;$) { return 0; }
81              
82 3     3 1 12 sub compile ($) { return 1; }
83              
84              
85             ###############################################################
86             # Operations
87             # + most of these are not defined here, they're just wrappers
88             # for the functions we'll want
89             ###############################################################
90             sub warn_abstract ($) {
91 0 0 0 0 0 0 if ($^W && $VERBOSE) {
92 0         0 my $warn =
93             "Attempt to find non-existant method `$_[0]' via Math::PartialOrder::Base.\n"
94             ." > (Did you forget to implement an abstract method?)";
95 0 0       0 if ($VERBOSE > 1) { cluck($warn); }
  0         0  
96 0         0 else { carp($warn); }
97             }
98             }
99              
100             ###############################################################
101             # Hierarchy Manipulation/Information
102             ###############################################################
103 0     0 1 0 sub root ($;$) { warn_abstract('root'); }
104 0     0 1 0 sub add ($$@) { warn_abstract('add'); }
105 0     0 1 0 sub move ($$@) { warn_abstract('move'); }
106 0     0 1 0 sub remove ($@) { warn_abstract('remove'); }
107              
108             sub add_parents ($$;@) {
109 6     6 1 20 my ($self, $type, @parents) = @_;
110 6         31 return $self->move($type,
111             $self->parents($type),
112 6         34 grep { !$self->has_parent($type,$_) } @parents);
113             }
114             sub replace ($$$) {
115 0     0 1 0 my ($h, $old, $new) = @_;
116 0         0 $h->add($new,$h->parents($old));
117 0         0 foreach ($h->children($old)) { $h->add_parents($_,$new); }
  0         0  
118 0         0 $h->remove($old);
119 0         0 return $h;
120             }
121              
122             sub ensure_types ($@) {
123 543     543 1 671 my $self = shift;
124 543 100       1140 if (@_) {
125 520         1249 foreach (@_) {
126 592 100       1572 $self->add($_, $self->root) unless $self->has_type($_);
127             }
128 520         2220 return @_;
129             }
130 23         62 return ($self->root);
131             }
132              
133             sub clear ($) {
134 0     0 1 0 $_[0]->remove($_[0]->types);
135 0         0 %{$_[0]->_hattributes} = ();
  0         0  
136             }
137              
138             sub assign ($$) {
139 26     26 1 48 my ($h1,$h2) = @_;
140 26         102 $h1->clear();
141 26         87 $h1->replace($h1->root,$h2->root);
142 26         42 my ($t,$attrs,$newattrs);
143 26         155 foreach $t ($h2->types) {
144 187         600 $h1->add($t, $h2->parents($t));
145 187         678 $attrs = $h2->_attributes($t);
146 187 50 33     573 if (defined($attrs) && %$attrs) {
147 0         0 $newattrs = {};
148 0         0 %$newattrs = %$attrs;
149 0         0 $h1->_attributes($t, $newattrs);
150             }
151             }
152 26         56 %{$h1->_hattributes} = %{$h2->_hattributes};
  26         74  
  26         89  
153 26         126 return $h1;
154             }
155              
156             # $h1->merge($h2,$h3,...)
157             sub merge ($@) {
158 25     25 1 44 my $h1 = shift;
159 25         40 my ($h2,$t,$attrs,$a,@parents);
160 25         74 while ($h2 = shift) {
161 25         32 %{$h1->_hattributes} = (%{$h1->_hattributes}, %{$h2->_hattributes});
  25         59  
  25         88  
  25         115  
162 25         85 foreach $t ($h2->types) {
163 179 50       613 unless ($h1->has_type($t)) {
164             # new type for $h1
165 0         0 $h1->add($t, $h2->parents($t));
166             } else {
167             # just add any parents this type didn't have before
168 179         726 @parents = grep { !$h1->has_parent($t,$_) } $h2->parents($t);
  176         483  
169 179 50       498 $h1->add_parents($t, @parents) if (@parents);
170             }
171             # merge attributes
172 179         446 $attrs = $h2->get_attributes($t);
173 179 50       629 if (defined($attrs)) {
174 0         0 foreach $a (keys(%$attrs)) {
175 0         0 $h1->set_attribute($t,$a,$h2->get_attribute($t,$a));
176             }
177             }
178             }
179             }
180 25         81 return $h1;
181             }
182              
183              
184              
185             ###############################################################
186             # Hierarchy Information
187             ###############################################################
188 47     47 1 151 sub size ($) { return scalar($_[0]->types); }
189 5     5 1 18 sub leaves ($) { return grep { !$_[0]->children($_) } $_[0]->types(); }
  20         71  
190             sub has_type ($$) {
191             return
192 0         0 defined($_[1])
193 0         0 ? grep { $_ eq $_[1] } $_[0]->types()
194 0 0   0 1 0 : grep { !defined($_) } $_[0]->types();
195             }
196             sub has_types ($$@) {
197 426     426 1 756 my $h = shift;
198 426         630 foreach (@_) {
199 852 100       1887 return '' unless ($h->has_type($_));
200             }
201 422         2500 return 1;
202             }
203              
204 0     0 1 0 sub types ($) { warn_abstract('types'); }
205              
206             sub is_equal ($$) {
207 60     60 1 131 my ($h1,$h2) = @_;
208 60 100       326 return 1 if ($h1 eq $h2); # object-equality
209 50         82 my (%alltypes, %allparents, $parent, $type);
210 50         192 @alltypes{($h1->types,
211             $h2->types)} = ($h1->types,
212             $h2->types);
213 50         263 foreach $type (values(%alltypes)) {
214 374 100 66     1002 return 0 unless ($h1->has_type($type) && $h2->has_type($type));
215 370         860 %allparents = ();
216 370         1184 @allparents{($h1->parents($type),
217             $h2->parents($type))} = ($h1->parents($type),
218             $h2->parents($type));
219 370         1131 foreach $parent (values(%allparents)) {
220 370 100 66     1108 return 0 unless ($h1->has_parent($type, $parent) &&
221             $h2->has_parent($type, $parent));
222             }
223             }
224 44         363 return 1;
225             }
226              
227             # $bool = $h->is_circular
228             # --iterative version
229             *is_circular = \&is_circular_iter;
230             *is_cyclic = \&is_circular_iter;
231             sub is_circular_iter ($) {
232             return
233 10     10 0 192 $_[0]->iterate_strata($_[0]->can('children'),
234             \&_is_circular_callback,
235             {size=>$_[0]->size, return=>''});
236             }
237             sub _is_circular_callback ($$$) {
238 50 100   50   124 return 1 if ($_[2]->{step} > $_[2]->{size});
239 45         50 return undef;
240             }
241             # --logical version : assumes non-iterative has_ancestor...
242             #*is_circular = \&is_circular_log;
243             sub is_circular_log ($) {
244 0 0   0 0 0 foreach ($_[0]->types) { return 1 if ($_[0]->has_ancestor($_,$_)); }
  0         0  
245 0         0 return '';
246             }
247              
248             # $bool = $h->is_deterministic
249 10     10 1 71 sub is_deterministic ($) { return !$_[0]->get_nondet_pair; }
250             # ($t1,$t2) = $h->get_nondet_pair
251             sub get_nondet_pair ($) {
252 10     10 1 14 my $h = shift;
253 10         27 my @types = $h->types;
254 10         14 my (@lubs,$i,$j);
255 10         39 for ($i = 0; $i <= $#types; ++$i) {
256 27         64 for ($j = $i+1; $j <= $#types; ++$j) {
257 48         135 @lubs = $h->_lub($types[$i],$types[$j]);
258 48 100 100     696 return (@types[$i,$j]) if (@lubs && scalar(@lubs) > 1);
259             }
260             }
261 5         25 return qw();
262             }
263              
264             # $bool = $h->is_treelike
265 10     10 0 206 sub is_treelike ($) { return !defined($_[0]->get_multiparent_type); }
266             # $type = $h->get_multiparent_type
267             sub get_multiparent_type ($) {
268 10     10 1 14 my $h = shift;
269 10         26 foreach ($h->types) {
270 34 100       36 return $_ if (scalar(@{[ $h->parents($_) ]}) > 1);
  34         76  
271             }
272 5         21 return undef;
273             }
274              
275              
276 0     0 1 0 sub parents ($$) { warn_abstract('parents'); }
277 0     0 1 0 sub children ($$) { warn_abstract('children'); }
278              
279             sub ancestors ($$) {
280 0     0 1 0 return @{$_[0]->iterate_cp_step
  0         0  
281             (\&_ancestors_callback, { start => $_[1], return => [] })};
282             }
283             # callback($h,$t,$args)
284             sub _ancestors_callback ($$$) {
285 0     0   0 push(@{$_[2]->{return}}, $_[0]->parents($_[1]));
  0         0  
286 0         0 return undef;
287             }
288             sub descendants ($$) {
289 0     0 1 0 return @{$_[0]->iterate_pc_step
  0         0  
290             (\&_descendants_callback,
291             { start => $_[1], return => [] })};
292             }
293             # callback($h,$t,$args)
294             sub _descendants_callback ($$$) {
295 0     0   0 push(@{$_[2]->{return}}, $_[0]->children($_[1]));
  0         0  
296 0         0 return undef;
297             }
298              
299             # $h->has_parent($t,$p)
300             sub has_parent ($$$) {
301             return
302             ($_[0]->has_types($_[1],$_[2])
303             and
304 0   0 0 1 0 grep { $_ eq $_[2] } $_[0]->parents($_[1]));
305             }
306             # $h->has_child($t,$p)
307             sub has_child ($$$) {
308 0         0 ($_[0]->has_types($_[1],$_[2])
309             and
310 0 0   0 1 0 grep { $_ eq $_[2] } $_[0]->children($_[1]));
311             }
312              
313             sub has_ancestor ($$$) {
314             return
315 20   66 20 1 209 defined($_[1]) && defined($_[2]) &&
316             $_[0]->has_type($_[1]) &&
317             $_[0]->has_type($_[2]) &&
318             $_[0]->iterate_cp_step(\&_type_search_callback,
319             {
320             start => [$_[0]->parents($_[1])],
321             find => $_[2],
322             return => ''
323             });
324             }
325             # callback($h,$t,$args)
326             sub _type_search_callback ($$$) {
327 69 100   69   164 return $_[2]->{find} eq $_[1] ? 1 : undef;
328             }
329              
330 1     1 1 4 sub has_descendant ($$$) { return $_[0]->has_ancestor($_[2],$_[1]); }
331              
332             # comparison aliases
333             *le = \&subsumes;
334             *lt = \&psubsumes;
335             *ge = \&extends;
336             *gt = \&pextends;
337             *cmp = \&compare;
338              
339 0     0 1 0 sub extends ($$$) { return $_[0]->subsumes($_[2],$_[1]); }
340              
341             *properly_extends = \&pextends;
342 0     0 0 0 sub pextends ($$$) { return $_[0]->psubsumes($_[2],$_[1]); }
343              
344             sub subsumes ($$$) {
345 45     45 1 488 my ($a);
346             return
347             # easy answers
348 45 100 66     99 (defined($a = _subsumes_trivial($_[1],$_[2]))
    100 100        
349             ? $a
350             # user-defined subsumption
351             : ($WANT_USER_HOOKS && defined($a = _subsumes_user($_[1],$_[2]))
352             ? $a
353             # consult hierarchy (if we can)
354             : (ref($_[0])
355             and $_[0]->has_types($_[1],$_[2])
356             and $_[0]->has_ancestor($_[2],$_[1]))));
357             }
358              
359             sub _subsumes_trivial ($$) {
360             return
361             (# undef subsumes everything
362 45 50   45   295 !defined($_[0]) ? 1
    50          
    100          
    100          
    100          
363             : (# nothing else subsumes undef
364             !defined($_[1]) ? 0
365             : (# object-identity counts as subsumption
366             $_[0] eq $_[1] ? 1
367             : (# everything subsumes $TYPE_TOP
368             $_[1] eq $TYPE_TOP ? 1
369             : (# nothing else subsumes $TYPE_TOP
370             $_[0] eq $TYPE_TOP ? 0
371             : # ... can't do it the easy way
372             undef)))));
373             }
374              
375             # _subsumes_user($t1,$t2)
376             # user-defined subsumption
377             sub _subsumes_user ($$) {
378             return
379             (# object-oriented user-defined subsumption
380 10         29 UNIVERSAL::can($_[0], 'subsumes')
381             ? (1,$_[0]->subsumes($_[1]))
382             : (# functional user-defined subsumption
383             ref($_[0]) && ref($_[0]) eq 'CODE'
384 25 100 66 25   293 ? &{$_[0]}('subsumes',$_[1])
    50          
385             : # ... nope
386             undef));
387             }
388              
389              
390             *properly_subsumes = \&psubsumes;
391             sub psubsumes ($$$) {
392 0     0 0 0 my ($ans);
393             return
394             (# easy answers
395 0 0 0     0 defined($ans = _psubsumes_trivial($_[1],$_[2])) ? $ans
    0 0        
396             : (# user-defined subsumption
397             $WANT_USER_HOOKS && defined($ans = _psubsumes_user($_[1],$_[2])) ? $ans
398             : (# consult hierarchy
399             ref($_[0])
400             and $_[0]->has_types($_[1],$_[2])
401             and $_[0]->has_ancestor($_[2],$_[1]))));
402             }
403             *_properly_subsumes_trivial = \&_psubsumes_trivial;
404             sub _psubsumes_trivial ($$) {
405             return
406 0 0   0   0 (defined($_[0])
    0          
    0          
    0          
    0          
    0          
407             ? (# nothing defined p-subsumes undef
408             !defined($_[1]) ? 0
409             : (# TOP p-subsumes nothing
410             $_[0] eq $TYPE_TOP ? 0
411             : (# everything else p-subsumes TOP
412             $_[1] eq $TYPE_TOP ? 1
413             : (# nothing p-subsumes itself
414             $_[0] eq $_[1] ? 0
415             : # ... can't do it this way
416             undef))))
417             : (# undef subsumes all and only defined values
418             defined($_[1]) ? 1 : 0));
419             }
420              
421             # non-method: _psubsumes_user($t1,$t2)
422             sub _psubsumes_user ($$) {
423             return
424             (# object-oriented user-defined proper subsumption
425 0         0 UNIVERSAL::can($_[0], 'properly_subsumes')
426             ? $_[0]->properly_subsumes($_[1])
427             : (# functional user-defined p-subsumption
428             ref($_[0]) && ref($_[0]) eq 'CODE'
429 0 0 0 0   0 ? &{$_[0]}('properly_subsumes',$_[1])
    0          
430             # nope
431             : undef));
432             }
433             *_properly_subsumes = \&_psubsumes;
434             sub _psubsumes ($$$) {
435             return
436             # check hierarchy structure
437 0     0   0 $_[0]->has_ancestor($_[2],$_[1]);
438             }
439              
440              
441              
442             #####################################################################
443             # Sorting/Comparison
444             #####################################################################
445             sub compare ($$$) {
446 0 0 0 0 1 0 return 0 if (# object-equality is easy
      0        
      0        
      0        
447             (defined($_[1]) and defined($_[2]) and $_[1] eq $_[2])
448             or
449             # so is undef
450             (!defined($_[1]) and !defined($_[2])));
451 0 0       0 return -1 if ($_[0]->properly_subsumes($_[1],$_[2]));
452 0 0       0 return 1 if ($_[0]->properly_subsumes($_[2],$_[1]));
453 0         0 return undef; # incomparable
454             }
455             sub _compare ($$$) {
456 8 50   8   37 return undef unless ($_[0]->has_types($_[1],$_[2])); # sanity check
457 8 100 33     139 return 0 if (# object-equality is easy
      66        
      33        
      66        
458             (defined($_[1]) and defined($_[2]) and $_[1] eq $_[2])
459             or
460             # so is undef
461             (!defined($_[1]) and !defined($_[2])));
462 5 100       26 return 1 if ($_[0]->has_ancestor($_[1],$_[2]));
463 3 50       57 return -1 if ($_[0]->has_ancestor($_[2],$_[1]));
464 3         45 return undef; # incomparable
465             }
466              
467              
468             # @min = $h->min(@types)
469             sub min ($@) {
470 0     0 1 0 my $self = shift;
471 0         0 my ($t1,$t2,@results);
472             MIN_T1:
473 0         0 foreach $t1 (@_) {
474 0         0 foreach $t2 (@_) {
475             #next unless ($self->has_type($t1)); # sanity check -- not needed with 'extends'
476 0 0       0 next MIN_T1 if ($self->properly_extends($t1,$t2));
477             }
478 0         0 push(@results,$t1);
479             }
480 0         0 return @results;
481             }
482             # @max = $h->max(@types)
483             sub max ($@) {
484 0     0 1 0 my $self = shift;
485 0         0 my ($t1,$t2,@results);
486             MAX_T1:
487 0         0 foreach $t1 (@_) {
488             #next unless ($self->has_type($t1)); # sanity check -- not needed w/ 'subsumes'
489 0         0 foreach $t2 (@_) {
490 0 0       0 next MAX_T1 if ($self->properly_subsumes($t1,$t2));
491             }
492 0         0 push(@results,$t1);
493             }
494 0         0 return @results;
495             }
496              
497              
498              
499             # @min = $h->min_extending($base,@types)
500             # (logical version)
501             *min_extending = \&_min_extending_log;
502             sub _min_extending_log ($$@) {
503 0     0   0 my $h = shift;
504 0         0 my $base = shift;
505 0         0 return $h->min(grep { $h->extends($_,$base) } @_);
  0         0  
506             }
507              
508             # @max = $h->max_subsuming($base,@types)
509             # (logical version)
510             *max_subsuming = \&_max_subsuming_log;
511             sub _max_subsuming_log ($$@) {
512 0     0   0 my $h = shift;
513 0         0 my $base = shift;
514 0         0 return $h->max(grep { $h->subsumes($_,$base) } @_);
  0         0  
515             }
516              
517              
518             # --- iterative version
519             *subsort = \&_subsort_it;
520             sub _subsort_it ($@) {
521 0     0   0 my $h = shift;
522 0         0 my %find = map { $_ => $_ } @_;
  0         0  
523 0         0 my %found = ();
524 0         0 my %found2step = ();
525 0         0 $h->iterate_strata($h->can('children'),
526             \&_subsort_callback,
527             {
528             find => \%find,
529             found => \%found,
530             found2step => \%found2step,
531             maxstep => $h->size,
532             });
533             return
534 0         0 @found{sort { $found2step{$a} <=> $found2step{$b} } keys(%found)},
  0         0  
535             values(%find);
536             }
537             # callback($hi,$type,$args)
538             sub _subsort_callback ($$$) {
539 0 0   0   0 return '' if ($_[2]->{step} > $_[2]->{maxstep});
540 0 0       0 if (exists($_[2]->{find}{$_[1]})) {
    0          
541 0         0 $_[2]->{found2step}{$_[1]} = $_[2]->{step};
542 0         0 $_[2]->{found}{$_[1]} = $_[1];
543 0         0 delete($_[2]->{find}{$_[1]});
544             } elsif (exists($_[2]->{found}{$_[1]})) {
545 0         0 $_[2]->{found2step}{$_[1]} = $_[2]->{step};
546             }
547 0         0 return undef;
548             }
549              
550             # logical version -- nice if we've got a fast 'has_ancestor'
551             #*subsort = \&_subsort_log;
552             sub _subsort_log ($@) {
553 0     0   0 my $h = shift;
554 0         0 my ($i,$j,$cmp);
555 0         0 for ($i = 0; $i < $#_; ++$i) {
556 0         0 for ($j = $i+1; $j <= $#_; ++$j) {
557 0         0 $cmp = $h->compare($_[$i],$_[$j]);
558 0 0 0     0 next if (!$cmp || $cmp < 0);
559 0         0 @_[$i,$j] = @_[$j,$i];
560             }
561             }
562 0         0 return @_;
563             }
564              
565              
566             sub stratasort ($@) {
567 0     0 1 0 my $h = shift;
568 0 0       0 my %find = map { defined($_) ? ($_ => $_) : qw() } @_;
  0         0  
569 0         0 my $stratah = $h->get_strata(@_);
570 0         0 my @strata = ();
571 0         0 my $last = [];
572 0         0 foreach (@_) {
573 0 0 0     0 if (defined($_) && exists($stratah->{$_})) {
574 0 0       0 unless (exists($strata[$stratah->{$_}])) {
575 0         0 $strata[$stratah->{$_}] = [$_];
576             } else {
577 0         0 push(@{$strata[$stratah->{$_}]}, $_);
  0         0  
578             }
579             } else {
580 0         0 push(@$last, $_);
581             }
582             }
583 0 0       0 push(@strata, $last) if (@$last);
584 0         0 return grep { $_ } @strata;
  0         0  
585             }
586              
587              
588             # iterative version
589             *get_strata = \&_get_strata_it;
590             sub _get_strata_it ($@) {
591             return
592 0     0   0 $_[0]->iterate_strata
593             ($_[0]->can('children'),
594             \&_get_strata_callback,
595             {
596             maxstrat => scalar($_[0]->types),
597             laststep => {},
598             return => {},
599             });
600             }
601             # callback($h,$type,$args)
602             sub _get_strata_callback ($$$) {
603             # circular hierarchy -- bail out
604 0 0   0   0 return $_[2]->{return} if ($_[2]->{step} >= $_[2]->{maxstrat});
605              
606 0 0 0     0 if (!exists($_[2]->{laststep}{$_[1]}) ||
607             $_[2]->{laststep}{$_[1]} < $_[2]->{step})
608             {
609             # ... we need to move it to this virtual step-stratum
610 0         0 $_[2]->{return}{$_[1]} = $_[2]->{step};
611             }
612              
613 0         0 $_[2]->{laststep}{$_[1]} = $_[2]->{step}; # keep this for *all* types
614 0         0 return undef;
615             }
616              
617             # --- assumes we have a fast has_ancestor()
618             # : pretty but slow
619             #*get_strata = \&_get_strata_log;
620             sub _get_strata_log ($@) {
621 0     0   0 my $h = shift;
622 0         0 my @types = grep { $h->has_type($_) } @_;
  0         0  
623 0         0 my %strata = ( map { $_ => 0 } @types );
  0         0  
624 0         0 my ($cmp,$i,$j);
625 0         0 my $changed = 1;
626 0         0 my $step = 1;
627              
628 0         0 while ($changed) {
629 0 0       0 last if ($step > scalar(@_));
630 0         0 $changed = 0;
631              
632 0         0 for ($i = 0; $i < $#types; ++$i) {
633 0 0       0 next unless ($h->has_type($types[$i]));
634              
635 0         0 for ($j = $i+1; $j <= $#types; ++$j) {
636 0 0       0 next unless ($h->has_type($types[$j]));
637              
638 0         0 $cmp = $h->_compare($types[$i],$types[$j]);
639 0 0       0 next unless ($cmp);
640              
641 0 0       0 if ($cmp < 0) {
    0          
642 0 0       0 next if ($strata{$types[$i]} < $strata{$types[$j]});
643 0         0 $changed = 1;
644 0         0 $strata{$types[$j]} = $strata{$types[$i]} + 1;
645             }
646             elsif ($cmp > 0) {
647 0 0       0 next if ($strata{$types[$i]} > $strata{$types[$j]});
648 0         0 $changed = 1;
649 0         0 $strata{$types[$i]} = $strata{$types[$j]} + 1;
650             }
651             }
652             }
653             }
654 0         0 return \%strata;
655             }
656              
657             ###############################################################
658             # Type Operations
659             ###############################################################
660              
661             # $h->warn_nondet($op,$t1,$t2,$default,@warnings)
662             # --> sets '$!'
663             sub warn_nondet ($$$$$@) {
664 0 0 0 0 0 0 if ($^W && $VERBOSE) {
665 0         0 my ($h,$op,$t1,$t2,$default) = @_;
666 0 0 0     0 my @warnings =
    0          
    0          
667             ("Warning: unsupported deterministic operation in non-ccpo hierarchy\n",
668             " > Class: ", (ref($h)||$h), "\n",
669             " > Operation: $op(", (defined($t1) ? "'$t1'" : "undef",
670             defined($t2) ? ",'$t2'" : ",undef"), ")\n",
671             " > Defaults to: ", defined($default) ? "'$default'" : 'undef', "\n",
672             " >");
673 0 0       0 if ($VERBOSE > 1) { cluck(@warnings); }
  0         0  
674 0         0 else { carp(@warnings); }
675             }
676             }
677              
678             #--------------------------------------------------------------
679             # njoin : deterministc n-ary lub()
680             sub njoin ($@) {
681 0     0 1 0 my $h = shift;
682 0         0 my $val = shift;
683 0         0 my (@lubs);
684 0         0 foreach (@_) {
685 0         0 @lubs = $h->lub($val,$_);
686 0 0       0 return $TYPE_TOP unless (@lubs);
687 0 0       0 $h->warn_nondet('lub', $val, $_, $lubs[0]) if (scalar(@lubs) > 1);
688 0         0 $val = $lubs[0];
689             }
690 0         0 return $val;
691             }
692              
693             #--------------------------------------------------------------
694             # type_join : deterministic n-ary lub(), defined types only
695             sub type_join ($@) {
696 0     0 1 0 my $h = shift;
697 0 0       0 return $TYPE_TOP unless ($h->has_types(@_)); # sanity check
698 0         0 my $val = shift;
699 0         0 my (@lubs);
700 0         0 foreach (@_) {
701 0         0 @lubs = $h->_lub($val,$_);
702 0 0       0 return $TYPE_TOP unless (@lubs);
703 0 0       0 $h->warn_nondet('_lub', $val, $_, $lubs[0]) if (scalar(@lubs) > 1);
704 0         0 $val = $lubs[0];
705             }
706 0         0 return $val;
707             }
708              
709              
710             #--------------------------------------------------------------
711             # nmeet : deterministic n-ary glb()
712             sub nmeet ($@) {
713 0     0 1 0 my $h = shift;
714 0         0 my $val = shift;
715 0         0 my (@glbs);
716 0         0 foreach (@_) {
717 0         0 @glbs = $h->glb($val,$_);
718 0 0       0 return undef unless (@glbs);
719 0 0       0 $h->warn_nondet('glb', $val, $_, $glbs[0]) if (scalar(@glbs) > 1);
720 0         0 $val = $glbs[0];
721             }
722 0         0 return $val;
723             }
724             #--------------------------------------------------------------
725             # type_meet : deterministic n-ary glb(), types only
726             sub type_meet ($@) {
727 0     0 1 0 my $h = shift;
728 0 0       0 return undef unless ($h->has_types(@_));
729 0         0 my $val = shift;
730 0         0 my (@glbs);
731 0         0 foreach (@_) {
732 0         0 @glbs = $h->_glb($val,$_);
733 0 0       0 return undef unless (@glbs);
734 0 0       0 $h->warn_nondet('_glb', $val, $_, $glbs[0]) if (scalar(@glbs) > 1);
735 0         0 $val = $glbs[0];
736             }
737 0         0 return $val;
738             }
739              
740              
741              
742             #--------------------------------------------------------------
743             # lub : least upper bounds
744             #--------------------------------------------------------------
745             *least_upper_bounds = \&lub;
746             sub lub ($$$) {
747 35     35 0 41 my ($l);
748             return
749             (# get the easy answers
750 35 50 66     70 defined($l = _lub_trivial($_[1],$_[2])) ? @$l
    100 33        
    100          
751             : (# user hooks
752             ($WANT_USER_HOOKS && defined($l = _lub_user($_[1],$_[2]))) ? @$l
753             : (# are we an instance with these types?
754             (ref($_[0]) && $_[0]->has_types($_[1],$_[2]))
755             # ... then do the lookup
756             ? ($_[0]->_lub($_[1],$_[2]))
757             : # ... guess not
758             qw())));
759             }
760              
761             # lub: iterative version
762             *_lub = \&_lub_iter;
763             sub _lub_iter ($$$) {
764             return
765 29     29   30 values(%{$_[0]->_get_bounds_iter($_[0]->can('children'),
  29         239  
766             -1,
767             $_[1], $_[2],
768             {$_[1]=>$_[1]},
769             {$_[2]=>$_[2]})});
770             }
771              
772             # non-method -- easy answers for lub()
773             # $listref_or_undef = _lub_trivial($t1,$t2)
774             sub _lub_trivial ($$) {
775             return
776             (# X * undef = X
777 35 100 33 35   276 !defined($_[1]) ? [$_[0]]
    50          
    100          
    100          
778             : (# undef * X = X
779             !defined($_[0]) ? [$_[1]]
780             : (# X * top = top * X = top
781             ($_[0] eq $TYPE_TOP || $_[1] eq $TYPE_TOP) ? [$TYPE_TOP]
782             : (# X * X = X
783             $_[0] eq $_[1] ? [$_[0]]
784             : # ... can't do it the easy way
785             undef))));
786             }
787              
788             # non-method -- user hooks for lub()
789             # $listref_or_undef = _lub_user($t1,$t2)
790 20     20   44 sub _lub_user ($$) { return Math::PartialOrder::Base::_binop_user('lub',$_[0],$_[1]); }
791              
792             # $listref_or_undef = binop_user($op,$t1,$t2)
793             sub _binop_user ($$$) {
794             return
795             (# delegate to $t1 (func)
796 10         26 ref($_[1]) && ref($_[1]) eq 'CODE'
797 0         0 ? [&{$_[1]}($_[0],$_[2])]
798             : (# delegate to $t2 (func)
799             ref($_[2]) && ref($_[2]) eq 'CODE'
800 15         62 ? [&{$_[2]}($_[0],$_[1])]
801             : (# delegate to $t1 (oop)
802             UNIVERSAL::can($_[1], $_[0])
803 5         20 ? [&{UNIVERSAL::can($_[1],$_[0])}($_[1],$_[2])]
804             : (# delegate to $t2 (oop)
805             UNIVERSAL::can($_[2], $_[0])
806 45 100 66 45   436 ? [&{UNIVERSAL::can($_[2],$_[0])}($_[2],$_[1])]
    100 33        
    50          
    100          
807             : # ... nope
808             undef))));
809             }
810              
811             # $_[1] : STOP $_[0] :STOP 'lub' STOP
812              
813             # lub: logical version
814             #*_lub = \&_lub_log;
815             sub _lub_log ($$$) {
816 0     0   0 my ($cmp);
817 0 0       0 if (defined($cmp = $_[0]->compare($_[1],$_[2]))) {
818             # more easy answers
819 0 0       0 return $_[2] if ($cmp <= 0);
820 0         0 return $_[1];
821             }
822             # do the lookup
823 0         0 return $_[0]->mcd_log($_[1],$_[2]);
824             }
825              
826             #--------------------------------------------------------------
827             # mcd : minimal common descendants
828             #--------------------------------------------------------------
829             #*mcd= \&mcd_log;
830             *mcd = \&mcd_iter;
831             *minimal_common_descendants = \&mcd;
832              
833             # iterative version
834             sub mcd_iter ($$$) {
835 0 0   0 0 0 return qw() unless ($_[0]->has_types($_[1],$_[2]));
836             return
837 0         0 values(%{$_[0]->_get_bounds_iter($_[0]->can('children'),
  0         0  
838             -1, $_[1], $_[2])});
839             }
840              
841              
842             #--------------------------------------------------------------
843             # _get_bounds_iter: abstract iterative bound-getting method
844             #
845             # iterative version:
846             # lub:test = has_ancestor
847             # lub:next = children
848             # glb:test = has_descendant
849             # glb:next = parents
850             #--------------------------------------------------------------
851              
852             # $h->get_bounds_iter(\&next,$cmpkeep,$t1,$t2)
853             # $h->get_bounds_iter(\&next,$cmpkeep,$t1,$t2,$t1hash,$t2hash)
854             # --> $cmpkeep is -1 to keep minimal, 1 to keep maximal
855             # i.e. min={ x \in solns | y \in solns and !_compare(x,y) or (_compare(x,y) == cmpkeep }
856             sub _get_bounds_iter ($&$$$;$$) {
857 32     32   45 my $self = shift;
858 32         35 my $next = shift;
859 32         30 my $cmpkeep = shift;
860 32         50 my @t1q = (shift);
861 32         45 my @t2q = (shift);
862 32   50     65 my $t1set = shift || {};
863 32   50     54 my $t2set = shift || {};
864 32         45 my %solns = ();
865 32         37 my %q = ();
866 32         75 my $step = $self->size;
867 32         38 my ($e,@next,$cmp,$want);
868 32   100     161 while ((@t1q || @t2q) && $step >= 0) {
      66        
869 59         62 --$step;
870 59         69 %q = ();
871 59         73 foreach $e (@t1q) {
872 69         95 $t1set->{$e} = $e;
873 69 100       127 if (exists($t2set->{$e})) {
874 13         15 $want = 1;
875 13         43 foreach (values(%solns)) {
876 0         0 $cmp = $self->_compare($e,$_);
877 0 0       0 next if (!$cmp);
878 0 0       0 if ($cmp != $cmpkeep) {
879 0         0 $want = 0;
880 0         0 last;
881             }
882 0         0 delete($solns{$_});
883             }
884 13 50       38 $solns{$e} = $e if ($want);
885             } else {
886 56         169 @next = &$next($self,$e);
887 56         143 @q{@next} = @next;
888             }
889             }
890 59         116 @t1q = values(%q);
891 59         74 %q = ();
892 59         73 foreach $e (@t2q) {
893 66         96 $t2set->{$e} = $e;
894 66 100       118 if (exists($t1set->{$e})) {
895 18         21 $want = 1;
896 18         41 foreach (values(%solns)) {
897 8         54 $cmp = $self->_compare($e,$_);
898 8 100       33 next if (!$cmp);
899 2 50       4 if ($cmp != $cmpkeep) {
900 2         3 $want = 0;
901 2         2 last;
902             }
903 0         0 delete($solns{$_});
904             }
905 18 100       51 $solns{$e} = $e if ($want);
906             } else {
907 48         109 @next = &$next($self,$e);
908 48         126 @q{@next} = @next;
909             }
910             }
911 59         340 @t2q = values(%q);
912             }
913 32         186 return \%solns;
914             }
915              
916              
917             # mcd($h,$t1,$t2): logical version
918             # *mcd = \&mcd_log;
919             sub mcd_log ($$$) {
920             # get intersection of descendants
921 0     0 0 0 my (@t1descs,%t1hash);
922 0         0 @t1descs = $_[0]->descendants($_[1]);
923 0         0 @t1hash{@t1descs} = @t1descs;
924             # delegate the gruntwork to min()
925             return
926 0         0 $_[0]->min(grep { exists($t1hash{$_}) } $_[0]->descendants($_[2]));
  0         0  
927             }
928              
929              
930             #--------------------------------------------------------------
931             # glb : greatest lower bounds
932             #--------------------------------------------------------------
933             *greatest_lower_bounds = \&glb;
934              
935             sub glb ($$$) {
936 35     35 0 40 my (@l);
937             return
938             (# get the easy answers
939 35 100 66     76 defined($l = _glb_trivial($_[1],$_[2])) ? @$l
    100 66        
    100          
940             : (# user hooks
941             ($WANT_USER_HOOKS && defined($l = _glb_user($_[1],$_[2]))) ? @$l
942             : (# are we an instance with these types?
943             (ref($_[0]) && $_[0]->has_types($_[1],$_[2]))
944             # ... then do the lookup
945             ? ($_[0]->_glb($_[1],$_[2]))
946             : # ... guess not
947             undef)));
948             }
949              
950             # non-method -- easy answers for glb()
951             # $listref_or_undef = _lub_trivial($t1,$t2)
952             sub _glb_trivial ($$) {
953             return
954             (# X / undef = undef / X = undef
955 35   66 35   274 !defined($_[0]) or !defined($_[1]) ? [undef]
956             : (# top / X = X
957             $_[0] eq $TYPE_TOP ? [$_[1]]
958             : (# X / top = X
959             $_[1] eq $TYPE_TOP ? [$_[0]]
960             : # ... can't do it the easy way
961             undef)));
962             }
963              
964             # non-method -- user hooks for glb()
965             # $listref_or_undef = _glb_user($t1,$t2)
966 25     25   52 sub _glb_user ($$) { return Math::PartialOrder::Base::_binop_user('glb',$_[0],$_[1]); }
967              
968             # glb: logical version
969             #*_glb = \&_glb_log;
970             sub _glb_log ($$$) {
971 0     0   0 my ($cmp);
972 0 0       0 if (defined($cmp = $_[0]->compare($_[1],$_[2]))) {
973             # more easy answers
974 0 0       0 return $_[2] if ($cmp >= 0);
975 0         0 return $_[1];
976             }
977             # do the lookup
978 0         0 return $_[0]->mca_log($_[1],$_[2]);
979             }
980              
981              
982             # glb: iterative version
983             *_glb = \&_glb_iter;
984             sub _glb_iter ($$$) {
985             return
986 3     3   6 values(%{$_[0]->_get_bounds_iter($_[0]->can('parents'),
  3         25  
987             1,
988             $_[1], $_[2],
989             {$_[1]=>$_[1]},
990             {$_[2]=>$_[2]})});
991             }
992              
993              
994             #--------------------------------------------------------------
995             # mca : maximal common ancestors
996             #--------------------------------------------------------------
997             *mca = \&mca_iter;
998             #*mca = \&mca_log;
999             *maximal_common_ancestors = \&mca;
1000              
1001             # iterative version
1002             sub mca_iter ($$$) {
1003 0     0 0 0 return values(%{$_[0]->_get_bounds_iter($_[0]->can('parents'),
  0         0  
1004             1,
1005             $_[1], $_[2])});
1006             }
1007              
1008             # mca: logical version
1009             # *mca = \&mca_log;
1010             sub mca_log ($$$) {
1011             # get intersection of ancestors
1012 0     0 0 0 my (@t1descs,%t1hash);
1013 0         0 @t1descs = $_[0]->ancestors($_[1]);
1014 0         0 @t1hash{@t1descs} = @t1descs;
1015             # delegate the gruntwork to max()
1016             return
1017 0         0 $_[0]->max(grep { exists($t1hash{$_}) } $_[0]->ancestors($_[2]));
  0         0  
1018             }
1019              
1020              
1021             #####################################################################
1022             # User-Defined Attributes
1023             #####################################################################
1024              
1025 179     179 1 678 sub get_attributes ($$) { return $_[0]->_attributes($_[1]); }
1026             sub get_attribute ($$$) {
1027 0     0 1 0 my ($self,$type,$attr) = @_;
1028 0         0 my ($tattrs);
1029             return
1030 0 0       0 defined($tattrs = $self->_attributes($type))
1031             ? $tattrs->{$attr}
1032             : undef;
1033             }
1034             sub set_attribute ($$$$) {
1035 0     0 1 0 my ($self,$type,$attr,$val) = @_;
1036 0         0 my ($tattrs);
1037 0 0       0 if (defined($tattrs = $self->_attributes($type))) {
1038 0         0 return $tattrs->{$attr} = $val;
1039             }
1040             # need new attributes
1041 0         0 $self->_attributes($type, {$attr => $val});
1042 0         0 return $val;
1043             }
1044             sub _attributes ($$;$) {
1045 0     0   0 my $self = shift;
1046 0 0 0     0 return undef unless (ref($self) && $self =~ /=HASH\(/);
1047 0         0 my $type = shift;
1048 0         0 my $attr = shift;
1049 0 0       0 if (@_) {
1050             # set attributes
1051 0         0 return $self->{attributes}->{$type} = shift;
1052             }
1053             # get attributes
1054 0 0 0     0 if (exists($self->{attributes}) && exists($self->{attributes}->{$type}))
1055             {
1056 0         0 return $self->{attributes}->{$type};
1057             }
1058 0         0 return undef;
1059             }
1060              
1061             # _hattributes(), _hattributes($attrs)
1062             # --> automagic creation!
1063             *_hattrs = \&_hattributes;
1064             sub _hattributes ($;$) {
1065 140 100   140   374 if (scalar(@_) == 1) {
1066 105 100 66     384 return $_[0]->_attributes->{$_[0]} = {} unless
1067             (defined($_[0]->_attributes) &&
1068             exists($_[0]->_attributes->{$_[0]}));
1069 99         325 return $_[0]->_attributes->{$_[0]};
1070             }
1071 35         119 return $_[0]->_attributes->{$_[0]} = $_[1];
1072             }
1073              
1074             # $val = $h->get_hattribute($a)
1075             sub get_hattribute ($$) {
1076 0 0   0 1 0 return defined($_[1]) ? $_[0]->_hattributes->{$_[1]} : undef;
1077             }
1078             # $val = $h->set_hattribute($a,$v)
1079             sub set_hattribute ($$;$) {
1080             return
1081 0 0   0 1 0 defined($_[1])
    0          
1082             ? defined($_[2])
1083             ? $_[0]->_hattributes->{$_[1]} = $_[2]
1084             : delete($_[0]->_hattributes->{$_[1]})
1085             : undef;
1086             }
1087              
1088              
1089             #####################################################################
1090             # Iteration Utilitiles
1091             #####################################################################
1092              
1093             sub iterate ($&&;$) {
1094 0     0 1 0 my ($self,$next,$callback,$args) = @_;
1095 0 0 0     0 return undef unless (defined($next) && defined($callback));
1096 0         0 my ($t,$r);
1097 0         0 my @q = defined($args->{start})
1098             ? ref($args->{start})
1099 0 0       0 ? @{$args->{start}}
    0          
1100             : $args->{start}
1101             : ($self->root);
1102 0 0       0 return undef unless ($self->has_types(@q));
1103              
1104 0         0 while (@q) {
1105 0         0 $t = shift(@q);
1106 0 0       0 $r = &$callback($self, $t, $args) if (defined($callback));
1107 0 0       0 return $r if (defined($r));
1108 0         0 push(@q, &$next($self,$t,$args));
1109             }
1110 0         0 return $args->{return};
1111             }
1112              
1113              
1114             sub iterate_step ($&&;$) {
1115 26     26 1 54 my ($self,$next,$callback,$args) = @_;
1116 26 50       47 return undef unless (defined($next));
1117              
1118 20         48 my @q = defined($args->{start})
1119             ? ref($args->{start})
1120 26 100       80 ? @{$args->{start}}
    50          
1121             : ($args->{start})
1122             : ($self->root);
1123 26 50       69 return undef unless ($self->has_types(@q));
1124              
1125 26 50       75 my $visited =
1126             defined($args->{visited})
1127             ? $args->{visited}
1128             : ($args->{visited} = {});
1129              
1130 26         33 my ($t,$r,%qh,@next);
1131 26         60 @qh{@q} = @q;
1132 26 50       77 $args->{step} = 0 unless (defined($args->{step}));
1133 26         48 while (%qh) {
1134 61         110 @q = values(%qh);
1135 61         90 %qh = ();
1136 61         122 while (defined($t = shift(@q))) {
1137 98 100       186 next if (exists($visited->{$t}));
1138 96         123 $visited->{$t} = undef;
1139 96 50       239 $r = &$callback($self, $t, $args) if (defined($callback));
1140 96 100       234 return $r if (defined($r));
1141 85         173 @next = grep { !exists($visited->{$_}) } &$next($self,$t);
  71         187  
1142 85         247 @qh{@next} = @next;
1143             }
1144 50         93 ++$args->{step};
1145             }
1146 15         118 return $args->{return};
1147             }
1148              
1149              
1150             sub iterate_tracking ($&&;$) {
1151 0     0 1 0 my ($self,$next,$callback,$args) = @_;
1152 0 0 0     0 return undef unless (defined($next) && defined($callback));
1153              
1154 0         0 my @q = defined($args->{start})
1155             ? ref($args->{start})
1156 0 0       0 ? @{$args->{start}}
    0          
1157             : ($args->{start})
1158             : ($self->root);
1159              
1160 0 0       0 my $ignore =
1161             defined($args->{ignore})
1162             ? $args->{ignore}
1163             : ($args->{ignore} = {});
1164              
1165 0 0       0 my $prev =
1166             defined($args->{prev})
1167             ? $args->{prev}
1168             : ($args->{prev} = {});
1169              
1170 0         0 my ($t,$r,%qh,@next);
1171 0         0 @qh{@q} = @q;
1172 0 0       0 $args->{step} = 0 unless (defined($args->{step}));
1173              
1174 0         0 while (%qh) {
1175 0         0 @q = values(%qh);
1176 0         0 %qh = ();
1177 0         0 while (defined($t = shift(@q))) {
1178 0 0       0 next if (exists($ignore->{$t}));
1179              
1180 0         0 $r = &$callback($self, $t, $args);
1181 0 0       0 return $r if (defined($r));
1182              
1183 0 0       0 next if (exists($ignore->{$t}));
1184              
1185 0         0 @next = &$next($self,$t,$args);
1186              
1187 0         0 foreach (@next) { $prev->{$_}{$t} = undef; }
  0         0  
1188 0         0 @next = grep { !exists($ignore->{$_}) } @next;
  0         0  
1189 0         0 @qh{@next} = @next;
1190             }
1191 0         0 ++$args->{step};
1192             }
1193 0         0 return $args->{return};
1194             }
1195              
1196             sub iterate_strata ($&&;$) {
1197 10     10 1 19 my ($self,$next,$callback,$args) = @_;
1198              
1199 10 50 33     47 return undef unless (defined($next) && defined($callback));
1200              
1201 0         0 my @q = defined($args->{start})
1202             ? ref($args->{start})
1203 10 0       36 ? @{$args->{start}}
    50          
1204             : ($args->{start})
1205             : ($self->root);
1206              
1207 10 50       29 my $prev =
1208             defined($args->{prev})
1209             ? $args->{prev}
1210             : ($args->{prev} = {});
1211              
1212 10         13 my ($t,$r,%qh,@next);
1213 10         22 @qh{@q} = @q;
1214 10 50       29 $args->{step} = 0 unless (defined($args->{step}));
1215              
1216 10         25 while (%qh) {
1217 40         60 @q = values(%qh);
1218 40         57 %qh = ();
1219 40         73 while (defined($t = shift(@q))) {
1220 50         82 $r = &$callback($self, $t, $args);
1221 50 100       112 return $r if (defined($r));
1222              
1223 45         93 @next = &$next($self,$t,$args);
1224              
1225 45         77 foreach (@next) {
1226 40 50 66     133 if (!defined($prev->{$_}{$t}) || $prev->{$_}{$t} < $args->{step}) {
1227 40         96 $prev->{$_}{$t} = $args->{step}
1228             }
1229             }
1230 45         140 @qh{@next} = @next;
1231             }
1232 35         68 ++$args->{step};
1233             }
1234 5         24 return $args->{return};
1235             }
1236              
1237             sub iterate_pc ($&;$) {
1238 0 0   0 1 0 return $_[0]->iterate
1239             (UNIVERSAL::can($_[0], 'children'),
1240             $_[1],
1241             defined($_[2]) ? $_[2] : {});
1242             }
1243              
1244             sub iterate_pc_step ($&;$) {
1245 0 0   0 1 0 return $_[0]->iterate_step
1246             (UNIVERSAL::can($_[0], 'children'),
1247             $_[1],
1248             defined($_[2]) ? $_[2] : {});
1249             }
1250              
1251             sub iterate_cp ($&;$) {
1252 0 0 0 0 1 0 $_[2]->{start} = [$_[0]->leaves] if
1253             (defined($_[2]) && !defined($_[2]->{start}));
1254 0 0       0 return $_[0]->iterate
1255             (UNIVERSAL::can($_[0], 'parents'),
1256             $_[1],
1257             defined($_[2]) ? $_[2] : {});
1258             }
1259              
1260             sub iterate_cp_step ($&;$) {
1261 26 50 33 26 1 115 $_[2]->{start} = [$_[0]->leaves] if
1262             (defined($_[2]) && !defined($_[2]->{start}));
1263 26 50       162 return $_[0]->iterate_step
1264             (UNIVERSAL::can($_[0], 'parents'),
1265             $_[1],
1266             defined($_[2]) ? $_[2] : {});
1267             }
1268              
1269              
1270             #####################################################################
1271             # Miscellaneous
1272             #####################################################################
1273              
1274             sub dump ($) {
1275 0     0 1   eval "use Data::Dumper";
1276 0           return Data::Dumper->Dump([$_[0]], [$_[0]]);
1277             }
1278              
1279              
1280              
1281             1;
1282             __END__