File Coverage

blib/lib/Math/PartialOrder/Std.pm
Criterion Covered Total %
statement 139 215 64.6
branch 40 92 43.4
condition 13 31 41.9
subroutine 23 30 76.6
pod 17 17 100.0
total 232 385 60.2


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::Std.pm
13             # Author: Bryan Jurish
14             #
15             # Description: standard iterative PartialOrder class
16             #
17             ###############################################################
18              
19             package Math::PartialOrder::Std;
20             # System modules
21 7     7   37366 use Carp;
  7         18  
  7         512  
22             #require Exporter;
23             # 3rd party exstensions
24             # user extension modules
25 7     7   4009 use Math::PartialOrder::Base;
  7         21  
  7         19119  
26             @ISA = qw(Math::PartialOrder::Base);
27             @EXPORT = qw();
28             @EXPORT_OK = qw();
29              
30             our $VERSION = 0.01;
31              
32             ###############################################################
33             # Initialization
34             # + object structure:
35             # {
36             # types => {type1=>type1, ... }
37             # root => scalar
38             # parents => { type1 => {p1a=>p1a, c1b=>c1b,... }, ... }
39             # children => { type1 => {c1a=>c1a, c1b=>c1b,... }, ... }
40             # attributes => { type1 => { attr1.1 => val1.1, ... }, ... }
41             # }
42             ###############################################################
43              
44             #----------------------------------------------------------------------
45             # new( {root=>$r} )
46             # + initialization routine: returns the object
47             #----------------------------------------------------------------------
48             sub new ($;$) {
49 11     11 1 627296 my $class = shift;
50 11         20 my $args = shift;
51 11         81 my $self = bless {
52             types => {},
53             root => '',
54             parents => {},
55             children => {},
56             attrs => {}
57             }, $class;
58             # root node
59 11   100     99 $self->_root($args->{root}||'BOTTOM');
60              
61             # hierarchy attributes
62 11         71 $self->_hattributes({});
63              
64 11         35 return $self;
65             }
66              
67             ###############################################################
68             # Hierarchy Information
69             # information
70             ###############################################################
71             # @types = $h->types()
72 218     218 1 248 sub types ($) { return values(%{$_[0]->{types}}); }
  218         1611  
73              
74             #--------------------------------------------------------------
75             # $bool = $h->has_type($t)
76 1464   66 1464 1 11377 sub has_type ($$) { return (defined($_[1]) && exists($_[0]->{types}{$_[1]})); }
77              
78             # $bool = $h->has_types(@types)
79             sub has_types ($@) {
80 69     69 1 182 my $types = shift->{types};
81 129 100 66     1166 grep {
82 69         89 return '' unless (defined($_) && exists($types->{$_}));
83             } @_;
84 63         330 return 1;
85             }
86              
87             #--------------------------------------------------------------
88             # @ps = $h->parents($typ)
89             sub parents ($$) {
90             return
91 1296         5894 (defined($_[1]) && exists($_[0]->{parents}{$_[1]})
92 1296 50 33 1296 1 6540 ? values(%{$_[0]->{parents}{$_[1]}})
93             : qw());
94             }
95              
96             #--------------------------------------------------------------
97             # @cs = $h->children($typ)
98             sub children ($$) {
99             return
100 61         253 (defined($_[1]) && exists($_[0]->{children}{$_[1]})
101 146 100 66 146 1 744 ? values(%{$_[0]->{children}{$_[1]}})
102             : qw());
103             }
104              
105             #--------------------------------------------------------------
106             # $bool = $h->has_parent($kid,$prt);
107             sub has_parent ($$) {
108             return
109 525 50 33 525 1 5357 (defined($_[1]) && exists($_[0]->{parents}{$_[1]})
110             ? exists($_[0]->{parents}{$_[1]}{$_[2]})
111             : '');
112             }
113              
114             #--------------------------------------------------------------
115             # $bool = $h->has_child($prt,$kid);
116             sub has_child ($$) {
117             return
118 3 50 33 3 1 31 (defined($_[1]) && exists($_[0]->{parents}{$_[1]})
119             ? exists($_[0]->{parents}{$_[1]}{$_[2]})
120             : '');
121             }
122              
123             #--------------------------------------------------------------
124             # @ancs = $h->ancestors($typ)
125 3     3 1 6 sub ancestors ($$) { return values(%{$_[0]->_ancestors($_[1])}); }
  3         17  
126              
127             #--------------------------------------------------------------
128             # @dscs = $h->descendants($typ)
129 3     3 1 5 sub descendants ($$) { return values(%{$_[0]->_ancestors($_[1])}); }
  3         7  
130              
131             #--------------------------------------------------------------
132             # $bool = has_ancestor($t1,$t2) : inherited
133              
134             #--------------------------------------------------------------
135             # $bool = has_descendant($t1,$t2) : inherited
136              
137              
138             ###############################################################
139             # Additional Information
140             ###############################################################
141              
142             # $hashref = $h->_ancestors($t)
143             sub _ancestors ($$) {
144             return
145 6 50   6   16 ($_[0]->has_type($_[1])
146             ? $_[0]->iterate_cp_step(\&_ancestors_callback,
147             {
148             start => $_[1],
149             return => {}
150             })
151             : {});
152             }
153             sub _ancestors_callback ($$$) {
154 27     27   54 @{$_[2]->{return}}{$_[0]->parents($_[1])} = $_[0]->parents($_[1]);
  27         65  
155 27         167 return undef;
156             }
157              
158             # $hashref = $h->_descendants($t)
159             sub _descendants ($$) {
160 0 0   0   0 ($_[0]->has_type($_[1])
161             ? $_[0]->iterate_pc_step(\&_descendants_callback,
162             {
163             start => $_[1],
164             return => {}
165             })
166             : {});
167             }
168             sub _descendants_callback ($$$) {
169 0     0   0 @{$_[2]->{return}}{$_[0]->children($_[1])} = $_[0]->children($_[1]);
  0         0  
170 0         0 return undef;
171             }
172              
173             # end information
174              
175              
176             ###############################################################
177             # Hierarchy Manipulation
178             ###############################################################
179              
180             #--------------------------------------------------------------
181             # add($type,@parents)
182             sub add ($$@) {
183 338     338 1 574 my $self = shift;
184 338         385 my $type = shift;
185              
186             # sanity checks
187 338 50       696 unless (defined($type)) {
188 0         0 carp("cannot add undefined type");
189 0         0 return $self;
190             }
191 338 100       820 return $self->move($type, @_) if ($self->has_type($type));
192              
193             # add this type
194 273         1732 $self->{types}{$type} = $type;
195              
196             # ensure parents are well-defined & well-placed
197 273         900 @_ = $self->ensure_types(@_);
198              
199             # set parents-relation for new $type
200 273 50       1456 if (@_) {
201 273         645 $self->{parents}{$type} = {};
202 273         438 @{$self->{parents}{$type}}{@_} = @_;
  273         798  
203             }
204             # set children-relation for new $type
205 273         567 my $kids = $self->{children};
206 273         428 foreach (@_) { $kids->{$_}{$type} = $type; }
  312         1048  
207 273         939 return $self;
208             }
209              
210             #--------------------------------------------------------------
211             # sub add_parents($h,$type,@parents) : INHERITED
212              
213             #--------------------------------------------------------------
214             # replace($old,$new)
215             sub replace ($$$) {
216 24     24 1 50 my ($h, $old, $new) = @_;
217 24 50       67 return $h->add($new,$h->root) unless ($h->has_type($old));
218 24 50 33     132 unless (defined($old) && defined($new)) {
219 0         0 carp("cannot add undefined type");
220 0         0 return $h;
221             }
222 24         55 $h->{types}{$new} = $new;
223 24         51 foreach (qw(parents children attrs)) {
224 72 100       238 if (exists($h->{$_}{$old})) {
225 48         135 $h->{$_}{$new} = $h->{$_}{$old};
226 48         136 delete($h->{$_}{$old});
227             } else {
228 24         75 delete($h->{$_}{$new});
229             }
230             }
231 24 100       1993 if ($old eq $h->{root}) { $h->_root($new); }
  21         75  
232 24         75 return $h;
233             }
234              
235              
236              
237             #--------------------------------------------------------------
238             # $move($type,@newps)
239             sub move ($$@) {
240 95     95 1 124 my $self = shift;
241 95         119 my $type = shift;
242              
243             # sanity check: type existence
244 95 50       183 return $self->add($type, @_) unless ($self->has_type($type));
245             # sanity check: root node
246 95 100       285 if ($type eq $self->{root}) {
247 18 50       50 if (@_) { croak("Cannot move hierarchy root '$type'"); }
  0         0  
248 18         62 else { return $self; }
249             }
250              
251             # ensure parents are well-defined & well-placed
252 77         231 @_ = $self->ensure_types(@_);
253              
254 77         139 my $kids = $self->{children};
255 77         145 my $prts = $self->{parents};
256 77 50       527 if (exists($prts->{$type})) {
257             # adjust old child-relations for moved $type
258 77         96 foreach (values(%{$prts->{$type}})) {
  77         280  
259 80         262 delete($kids->{$_}{$type});
260             }
261             }
262              
263             # add new child-relations for moved $type
264 77         147 foreach (@_) { $kids->{$_}{$type} = $type; }
  86         242  
265              
266             # adjust parent-relation for moved $type
267 77         129 %{$prts->{$type}} = map { ($_ => $_) } @_;
  77         288  
  86         209  
268              
269 77         393 return $self
270             }
271              
272             #--------------------------------------------------------------
273             # BUG (fixed): deleting an intermediate type orphans its descendants
274             sub remove ($@) {
275 3     3 1 3 my $self = shift;
276              
277 3 50 0     6 @_ = # sanity check
      33        
278             grep {
279 3         4 $self->has_type($_) &&
280             ($_ ne $self->root ||
281             (carp("attempt to remove hierarchy root!") && 0))
282             } @_;
283              
284 3 50       6 return $self unless (@_); # not really deleting anything
285              
286 3         4 delete(@{$self->{types}}{@_});
  3         6  
287              
288 3         3 my ($kids,$parents,$deleted);
289 3         5 foreach $deleted (@_) {
290             # adopt orphans
291 3         3 $kids = $self->{children}{$deleted};
292 3         5 $parents = $self->{parents}{$deleted};
293 3         6 foreach (values(%$kids)) { # $_ is an orphaned child
294 3 50       8 $self->{parents}{$_} = {} unless (exists($self->{parents}{$_}));
295 3         5 @{$self->{parents}{$_}}{values(%$parents)} = values(%$parents);
  3         6  
296 3         3 delete(@{$self->{parents}{$_}}{@_});
  3         9  
297             }
298 3         5 foreach (values(%$parents)) { # $_ is an adopting grandparent
299 3 50       10 $self->{children}{$_} = {} unless (exists($self->{children}{$_}));
300 3         5 @{$self->{children}{$_}}{values(%$kids)} = values(%$kids);
  3         7  
301 3         4 delete(@{$self->{children}{$_}}{@_});
  3         9  
302             }
303             }
304             # delete inheritance information for deleted types
305 3         3 delete(@{$self->{parents}}{@_});
  3         3  
306 3         4 delete(@{$self->{children}}{@_});
  3         4  
307              
308 3         12 return $self;
309             }
310              
311             #--------------------------------------------------------------
312             # $h1->assign($h2)
313             sub assign ($$) {
314 18     18 1 1324 my ($h1,$h2) = @_;
315 18 50       275 return $h1->SUPER::assign($h2) unless ($h2->isa($h1));
316 0         0 $h1->clear();
317 0         0 %{$h1->{types}} = %{$h2->{types}};
  0         0  
  0         0  
318 0         0 $h1->_root($h2->{root});
319 0         0 foreach (values(%{$h1->{types}})) {
  0         0  
320 0         0 %{$h1->{parents}{$_}} = %{$h2->{parents}{$_}};
  0         0  
  0         0  
321 0         0 %{$h1->{children}{$_}} = %{$h2->{children}{$_}};
  0         0  
  0         0  
322             }
323             # assign attributes
324 0         0 %{$h1->_attributes} = %{$h2->_attributes};
  0         0  
  0         0  
325 0         0 %{$h1->_hattributes} = %{$h2->_hattributes};
  0         0  
  0         0  
326 0         0 delete($h1->_attributes->{$h2});
327 0         0 return $h1;
328             }
329              
330              
331              
332             #--------------------------------------------------------------
333             # $h1->merge($h2,...)
334             sub merge ($@) {
335 15     15 1 29 my $h1 = shift;
336              
337 15         20 my ($a2);
338 15         52 while ($h2 = shift) {
339 15 50       144 unless ($h2->isa($h1)) {
340 15         89 $h1->SUPER::merge($h2);
341 15         47 next;
342             }
343              
344             # add all types
345 0         0 @{$h1->{types}}{$h2->types} = $h2->types;
  0         0  
346              
347             # adopt $h2->root under $h1->root if they differ
348 0 0       0 $h1->move($h2->{root}) unless ($h1->{root} eq $h2->{root});
349              
350             # merge/override hierarchy-attributes
351 0         0 %{$h1->_hattributes} = (%$h1->_hattributes, %{$h2->_hattributes});
  0         0  
  0         0  
352              
353             # merge in all parent-child relations and attributes
354 0         0 foreach (values(%{$h2->{types}})) {
  0         0  
355              
356             # parents
357 0         0 @{$h1->{parents}{$_}}{$h2->parents($_)} = $h2->parents($_);
  0         0  
358              
359             # children
360 0         0 @{$h1->{children}{$_}}{$h2->children($_)} = $h2->children($_);
  0         0  
361              
362             # attributes
363 0 0 0     0 if (defined($a2 = $h2->_attributes($_)) && %$a2) {
364 0         0 @{$h1->{attrs}{$_}}{keys(%$a2)} = values(%$a2);
  0         0  
365             }
366             }
367             }
368 15         60 return $h1;
369             }
370              
371             #--------------------------------------------------------------
372             # $h->clear
373             sub clear ($) {
374 21     21 1 34 my $self = shift;
375 21         28 %{$self->{types}} = ();
  21         88  
376 21         64 %{$self->{parents}} = ();
  21         106  
377 21         31 %{$self->{children}} = ();
  21         87  
378 21         29 %{$self->{attributes}} = ();
  21         43  
379              
380 21         83 my $hattrs = $self->_hattributes; # save this reference!
381 21         41 %{$self->{attrs}} = ();
  21         49  
382 21         38 %$hattrs = ();
383 21         62 $self->_hattributes($hattrs);
384              
385              
386             # make sure we still have the root type!
387 21         66 $self->_root($self->root);
388              
389 21         85 return $self;
390             }
391              
392             # end manipulation
393              
394              
395              
396             ###############################################################
397             # additional sorting / comparison
398             ###############################################################
399              
400             # $h->_minimize($hashref)
401             sub _minimize ($$) {
402 0     0   0 my ($self,$hash) = @_;
403 0         0 my ($t1,$t2);
404 0         0 my @members = values(%$hash);
405             MINIMIZE_T1:
406 0         0 foreach $t1 (@members) {
407 0 0       0 next unless (exists($hash->{$t1}));
408 0 0       0 unless ($self->has_type($t1)) {
409             # sanity check
410 0         0 delete($hash->{$t1});
411 0         0 next;
412             }
413 0         0 foreach $t2 (@members) {
414 0 0       0 next unless (exists($hash->{$t2}));
415 0 0       0 if ($self->has_ancestor($t1,$t2)) {
416 0         0 delete($hash->{$t1});
417 0         0 next MINIMIZE_T1;
418             }
419             }
420             }
421 0         0 return $hash;
422             }
423              
424             # $h->_maximize($hashref)
425             sub _maximize ($$) {
426 0     0   0 my ($self,$hash) = @_;
427 0         0 my ($t1,$t2);
428 0         0 my @members = values(%$hash);
429             MAXIMIZE_T1:
430 0         0 foreach $t1 (@members) {
431 0 0       0 next unless (exists($hash->{$t1}));
432 0 0       0 unless ($self->has_type($t1)) {
433             # sanity check
434 0         0 delete($hash->{$t1});
435 0         0 next;
436             }
437 0         0 foreach $t2 (@members) {
438 0 0       0 next unless (exists($hash->{$t2}));
439 0 0       0 if ($self->has_descendant($t1,$t2)) {
440 0         0 delete($hash->{$t1});
441 0         0 next MAXIMIZE_T1;
442             }
443             }
444             }
445 0         0 return $set;
446             }
447              
448             # end sorting/comparison
449              
450              
451             ###############################################################
452             # unsorted
453             ###############################################################
454              
455             ###############################################################
456             # Type operations
457             ###############################################################
458              
459             #--------------------------------------------------------------
460             # lub : inherited
461              
462             #--------------------------------------------------------------
463             # glb : inherited
464              
465              
466              
467             ###############################################################
468             # Accessors/manipulators
469             ###############################################################
470 0     0   0 sub _types ($) { return $_[0]->{types}; }
471              
472             sub _root ($;$) {
473 135     135   189 my $self = shift;
474 135 100       510 return $self->{root} unless (@_);
475 75         117 my $root = shift(@_);
476 75 50       321 $root = 'BOTTOM' unless (defined($root));
477 75         215 $self->{types}{$root} = $root;
478 75 100       285 $self->{parents}{$root} = {} unless (defined($self->{parents}{$root}));
479 75 100       256 $self->{children}{$root} = {} unless (defined($self->{children}{$root}));
480 75         202 return $self->{root} = $root;
481             }
482             *root = \&_root;
483              
484             # \%prts_by_type = $h->_parents()
485             # \%type_prts = $h->_parents($type)
486             sub _parents ($;$) {
487             return
488 0 0   0   0 (exists($_[1])
    0          
489             ? (exists($_[0]->{parents}{$_[1]})
490             ? $_[0]->{parents}{$_[1]}
491             : undef)
492             : $_[0]->{parents});
493             }
494              
495             # \%kids_by_type = $h->_children()
496             # \%type_kids = $h->children($type)
497             sub _children ($;$) {
498             return
499 0 0   0   0 (exists($_[1])
    0          
500             ? (exists($_[0]->{children}{$_[1]})
501             ? $_[0]->{children}{$_[1]}
502             : undef)
503             : $_[0]->{children});
504             }
505              
506              
507             # * C<_attributes()>, C<_attributes($type)>, C<_attributes($type, $hashref)>
508             sub _attributes ($;$$) {
509             return
510 630 0   630   3624 (exists($_[1])
    50          
    50          
    100          
511             ? (defined($_[1])
512             ? (exists($_[2])
513             ? (defined($_[2])
514             ? $_[0]->{attrs}{$_[1]} = $_[2]
515             : delete($_[0]->{attrs}{$_[1]}))
516             : $_[0]->{attrs}{$_[1]})
517             : undef)
518             : $_[0]->{attrs});
519             }
520              
521              
522             1;
523             __END__