File Coverage

blib/lib/Graph/Traversal.pm
Criterion Covered Total %
statement 194 194 100.0
branch 93 110 84.5
condition 40 54 74.0
subroutine 34 34 100.0
pod 16 29 55.1
total 377 421 89.5


line stmt bran cond sub pod time code
1             package Graph::Traversal;
2              
3 11     11   101 use strict;
  11         21  
  11         380  
4 11     11   95 use warnings;
  11         18  
  11         28094  
5              
6             # $SIG{__DIE__ } = \&Graph::__carp_confess;
7             # $SIG{__WARN__} = \&Graph::__carp_confess;
8              
9             sub reset {
10 104     104 0 164 my $self = shift;
11 104         2840 require Set::Object;
12 104         30613 $self->{ unseen } = Set::Object->new($self->{ graph }->vertices);
13 104         391 $self->{ seen } = Set::Object->new;
14 104         225 $self->{ order } = [ ];
15 104         180 $self->{ preorder } = [ ];
16 104         206 $self->{ postorder } = [ ];
17 104         255 $self->{ roots } = [ ];
18 104         327 $self->{ tree } = Graph->new(directed => $self->{ graph }->directed);
19 104         240 delete $self->{ terminate };
20             }
21              
22             sub _see {
23 771     771   1185 my $self = shift;
24 771         1637 $self->see;
25             }
26              
27             sub has_a_cycle {
28 8     8 0 26 my ($u, $v, $t, $s) = @_;
29 8         19 $s->{ has_a_cycle } = 1;
30 8         26 $t->terminate;
31             }
32              
33             sub find_a_cycle {
34 5     5 0 25 my ($u, $v, $t, $s) = @_;
35 5         10 my @cycle = ( $u );
36 5 100       19 push @cycle, $v unless $u eq $v;
37 5         8 my $path = $t->{ order };
38 5 100       13 if (@$path) {
39 4         8 my $i = $#$path;
40 4   66     20 while ($i >= 0 && $path->[ $i ] ne $v) { $i-- }
  3         10  
41 4 50       22 if ($i >= 0) {
42 4         13 unshift @cycle, @{ $path }[ $i+1 .. $#$path ];
  4         9  
43             }
44             }
45 5         13 $s->{ a_cycle } = \@cycle;
46 5         24 $t->terminate;
47             }
48              
49             my @KNOWN_CONFIG = qw(
50             tree_edge seen_edge
51             next_alphabetic next_numeric next_random
52             has_a_cycle find_a_cycle
53             );
54             my @EXTRACT_CONFIG = qw(
55             pre post pre_vertex post_vertex
56             pre_edge post_edge back_edge down_edge cross_edge non_tree_edge
57             first_root next_root next_successor
58             );
59              
60             sub new {
61 103     103 0 11502 my ($class, $g, %attr) = @_;
62 103 100 66     761 Graph::__carp_confess("Graph::Traversal: first argument is not a Graph")
63             unless ref $g && $g->isa('Graph');
64 102         384 my $self = bless { graph => $g, state => { } }, $class;
65 102         327 $self->reset;
66 102 100       291 if (exists $attr{ start }) {
67 1         4 $attr{ first_root } = delete $attr{ start };
68 1         3 $attr{ next_root } = undef;
69             }
70 102         518 my @found_known = grep exists $attr{$_}, @EXTRACT_CONFIG;
71 102         341 @$self{@found_known} = delete @attr{@found_known};
72             $self->{ seen_edge } = $attr{ seen_edge }
73 102 50 33     254 if exists $attr{ seen_edge } and ($g->multiedged || $g->countedged);
      66        
74 102 50       224 $self->{ pre_edge } = $attr{ tree_edge } if exists $attr{ tree_edge };
75             my $default_next =
76             $attr{ next_alphabetic } ? \&Graph::_next_alphabetic :
77 102 100       352 $attr{ next_numeric } ? \&Graph::_next_numeric :
    100          
78             \&Graph::_next_random;
79 102 100       278 $self->{ next_root } = $default_next if !exists $self->{ next_root };
80             $self->{ first_root } =
81             exists $self->{ next_root } ? $self->{ next_root } : $default_next
82 102 50       298 if !exists $self->{ first_root };
    100          
83 102 100       255 $self->{ next_successor } = $default_next if !exists $self->{ next_successor };
84 102 100       235 if (exists $attr{ has_a_cycle }) {
85             $self->{ back_edge } = my $has_a_cycle =
86             ref $attr{ has_a_cycle } eq 'CODE' ?
87 17 50       48 $attr{ has_a_cycle } : \&has_a_cycle;
88 17 100       46 $self->{ down_edge } = $has_a_cycle if $g->is_undirected;
89             }
90 102 100       219 if (exists $attr{ find_a_cycle }) {
91             $self->{ back_edge } = my $find_a_cycle =
92             ref $attr{ find_a_cycle } eq 'CODE' ?
93 3 50       12 $attr{ find_a_cycle } : \&find_a_cycle;
94 3 100       16 $self->{ down_edge } = $find_a_cycle if $g->is_undirected;
95             }
96 102         200 $self->{ add } = \&add_order;
97 102         202 $self->{ see } = \&_see;
98 102         284 delete @attr{@KNOWN_CONFIG};
99 102         288 Graph::_opt_unknown(\%attr);
100 101         296 return $self;
101             }
102              
103             sub terminate {
104 15     15 0 27 my $self = shift;
105 15         43 $self->{ terminate } = 1;
106             }
107              
108             sub add_order {
109 782     782 0 1420 my ($self, @next) = @_;
110 782         1018 push @{ $self->{ order } }, @next;
  782         1747  
111             }
112              
113             sub visit {
114 782     782 0 1479 my ($self, @next) = @_;
115 782         2574 $self->{ unseen }->remove(@next);
116 782         2371 $self->{ seen }->insert(@next);
117 782         1893 $self->{ add }->( $self, @next );
118 782 100       1975 return unless my $p = $self->{ pre };
119 423         1169 $p->( $_, $self ) for @next;
120             }
121              
122             sub visit_preorder {
123 782     782 0 1621 my ($self, @next) = @_;
124 782         1002 push @{ $self->{ preorder } }, @next;
  782         1633  
125 782         2518 $self->{ preordern }->{ $_ } = $self->{ preorderi }++ for @next;
126 782         1542 $self->visit( @next );
127             }
128              
129             sub visit_postorder {
130 771     771 0 1219 my ($self) = @_;
131 771         1505 my @post = reverse $self->{ see }->( $self );
132 771         1095 push @{ $self->{ postorder } }, @post;
  771         1571  
133 771         2473 $self->{ postordern }->{ $_ } = $self->{ postorderi }++ for @post;
134 771 100       1706 if (my $p = $self->{ post }) {
135 65         156 $p->( $_, $self ) for @post;
136             }
137 771 100 100     2176 return unless (my $p = $self->{ post_edge }) and defined(my $u = $self->current);
138 9         30 $p->( $u, $_, $self, $self->{ state }) for @post;
139             }
140              
141             sub _callbacks {
142 761     761   1697 my ($self, $current, @all) = @_;
143 761 100       1592 return unless @all;
144 693         1049 my $nontree = $self->{ non_tree_edge };
145 693         931 my $back = $self->{ back_edge };
146 693         939 my $down = $self->{ down_edge };
147 693         926 my $cross = $self->{ cross_edge };
148 693         979 my $seen = $self->{ seen_edge };
149 693   66     2730 my $bdc = defined $back || defined $down || defined $cross;
150 693 100 100     4141 return unless (defined $nontree || $bdc || defined $seen);
      66        
151 46         65 my $u = $current;
152 46         77 my $preu = $self->{ preordern }->{ $u };
153 46         74 my $postu = $self->{ postordern }->{ $u };
154 46         83 for my $v ( @all ) {
155 58 50 66     176 if (!$self->{tree}->has_edge($u, $v) && (defined $nontree || $bdc) &&
      66        
      33        
156             exists $self->{ seen }->{ $v }) {
157 24 100       822 $nontree->( $u, $v, $self, $self->{ state }) if $nontree;
158 24 50       64 if ($bdc) {
159 24         43 my $postv = $self->{ postordern }->{ $v };
160 24 100 100     141 if ($back &&
      66        
161             (!defined $postv || $postv >= $postu)) {
162 14         39 $back ->( $u, $v, $self, $self->{ state });
163             } else {
164 10         20 my $prev = $self->{ preordern }->{ $v };
165 10 100 100     44 if ($down && $prev > $preu) {
    100 66        
166 1         3 $down ->( $u, $v, $self, $self->{ state });
167             } elsif ($cross && $prev < $preu) {
168 1         4 $cross->( $u, $v, $self, $self->{ state });
169             }
170             }
171             }
172             }
173 58 100       362 next if !$seen;
174 1         17 my $c = $self->graph->get_edge_count($u, $v);
175 1         14 $seen->( $u, $v, $self, $self->{ state } ) while $c-- > 1;
176             }
177             }
178              
179             sub next {
180 921     921 0 1414 my $self = shift;
181 921 100       1800 return undef if $self->{ terminate };
182 913         1144 my @next;
183 913         1669 while ($self->seeing) {
184 1289         2959 my $current = $self->current;
185 1289         3479 my $next = Set::Object->new($self->{ graph }->successors($current));
186 1289         4997 my @all = $next->members;
187 1289         3652 $next = $next->difference($self->{seen});
188 1289 100       37253 if ($next->size) {
189 518         3055 @next = $self->{ next_successor }->( $self, { map +($_=>$_), $next->members } );
190 518         3254 $self->{ tree }->add_edges(map [$current, $_], @next);
191 518 100       2846 last unless my $p = $self->{ pre_edge };
192 9         30 $p->($current, $_, $self, $self->{ state }) for @next;
193 9         66 last;
194             } else {
195 771         1662 $self->visit_postorder;
196             }
197 771 100       1503 return undef if $self->{ terminate };
198 761         1568 $self->_callbacks($current, @all);
199             }
200 903 100       1996 unless (@next) {
201 385 100 66     507 if (!@{ $self->{ roots } } and defined(my $first = $self->{ first_root })) {
  385         1142  
202 105 100       411 return unless @next = ref $first eq 'CODE'
    100          
203             ? $first->( $self, { map +($_=>$_), $self->unseen } )
204             : $first;
205             }
206 381 100 100     1528 return if !@next and !$self->{ next_root };
207 375 100 100     985 return if !@next and !(@next = $self->{ next_root }->( $self, { map +($_=>$_), $self->unseen } ));
208 264 50       1786 return if $self->{ seen }->contains($next[0]); # Sanity check.
209 264         1372 push @{ $self->{ roots } }, $next[0];
  264         653  
210             }
211 782 50       2662 $self->visit_preorder( @next ) if @next;
212 782         2791 return $next[0];
213             }
214              
215             sub _order {
216 137     137   271 my ($self, $order) = @_;
217 137         309 1 while defined $self->next;
218 137         227 @{ $self->{ $order } };
  137         958  
219             }
220              
221             sub preorder {
222 19     19 1 72 my $self = shift;
223 19         53 $self->_order( 'preorder' );
224             }
225              
226             sub postorder {
227 118     118 1 273 my $self = shift;
228 118         295 $self->_order( 'postorder' );
229             }
230              
231             sub unseen {
232 393     393 1 56129 my $self = shift;
233 393 100       576 $self->{ unseen }->${ wantarray ? \'members' : \'size' };
  393         7069  
234             }
235              
236             sub seen {
237 37     37 1 77 my $self = shift;
238 37 100       71 $self->{ seen }->${ wantarray ? \'members' : \'size' };
  37         418  
239             }
240              
241             sub seeing {
242 1694     1694 1 2389 my $self = shift;
243 1694         2239 @{ $self->{ order } };
  1694         3903  
244             }
245              
246             sub roots {
247 16     16 0 33 my $self = shift;
248 16         24 @{ $self->{ roots } };
  16         107  
249             }
250              
251             sub is_root {
252 9     9 0 22 my ($self, $v) = @_;
253 9         15 for my $u (@{ $self->{ roots } }) {
  9         22  
254 11 100       43 return 1 if $u eq $v;
255             }
256 5         21 return 0;
257             }
258              
259             sub tree {
260 1     1 1 6652 my $self = shift;
261 1         5 $self->{ tree };
262             }
263              
264             sub graph {
265 3     3 0 2327 my $self = shift;
266 3         16 $self->{ graph };
267             }
268              
269             sub vertex_by_postorder {
270 4     4 1 13 my ($self, $i) = @_;
271 4 50       28 exists $self->{ postorder } && $self->{ postorder }->[ $i ];
272             }
273              
274             sub postorder_by_vertex {
275 4     4 1 11 my ($self, $v) = @_;
276 4 50       27 exists $self->{ postordern } && $self->{ postordern }->{ $v };
277             }
278              
279             sub postorder_vertices {
280 1     1 1 2514 my ($self, $v) = @_;
281 1 50       5 exists $self->{ postordern } ? %{ $self->{ postordern } } : ();
  1         79  
282             }
283              
284             sub vertex_by_preorder {
285 4     4 1 9 my ($self, $i) = @_;
286 4 50       28 exists $self->{ preorder } && $self->{ preorder }->[ $i ];
287             }
288              
289             sub preorder_by_vertex {
290 4     4 1 10 my ($self, $v) = @_;
291 4 50       30 exists $self->{ preordern } && $self->{ preordern }->{ $v };
292             }
293              
294             sub preorder_vertices {
295 1     1 1 5 my ($self, $v) = @_;
296 1 50       4 exists $self->{ preordern } ? %{ $self->{ preordern } } : ();
  1         96  
297             }
298              
299             sub has_state {
300 5     5 1 751 my ($self, $var) = @_;
301 5 100       50 exists $self->{ state } && exists $self->{ state }->{ $var };
302             }
303              
304             sub get_state {
305 22     22 1 53 my ($self, $var) = @_;
306 22 100       357 exists $self->{ state } ? $self->{ state }->{ $var } : undef;
307             }
308              
309             sub set_state {
310 1     1 1 5 my ($self, $var, $val) = @_;
311 1         4 $self->{ state }->{ $var } = $val;
312 1         4 return 1;
313             }
314              
315             sub delete_state {
316 1     1 1 4 my ($self, $var) = @_;
317 1         3 delete $self->{ state }->{ $var };
318 1 50       2 delete $self->{ state } unless keys %{ $self->{ state } };
  1         7  
319 1         5 return 1;
320             }
321              
322             1;
323             __END__