File Coverage

blib/lib/Graph/Traversal.pm
Criterion Covered Total %
statement 194 194 100.0
branch 94 108 87.0
condition 41 57 71.9
subroutine 34 34 100.0
pod 16 29 55.1
total 379 422 89.8


line stmt bran cond sub pod time code
1             package Graph::Traversal;
2              
3 13     13   80 use strict;
  13         22  
  13         470  
4 13     13   108 use warnings;
  13         24  
  13         40830  
5              
6             # $SIG{__DIE__ } = \&Graph::__carp_confess;
7             # $SIG{__WARN__} = \&Graph::__carp_confess;
8              
9             sub reset {
10 130     130 0 274 my $self = shift;
11 130         3370 require Set::Object;
12 130         33056 $self->{ unseen } = Set::Object->new($self->{ graph }->vertices);
13 130         745 $self->{ seen } = Set::Object->new;
14 130         350 $self->{ order } = [ ];
15 130         505 $self->{ preorder } = [ ];
16 130         480 $self->{ postorder } = [ ];
17 130         338 $self->{ roots } = [ ];
18 130         631 $self->{ tree } = Graph->new(directed => $self->{ graph }->directed);
19 130         359 delete $self->{ terminate };
20             }
21              
22             sub _see {
23 1304     1304   2524 my $self = shift;
24 1304         4537 $self->see;
25             }
26              
27             sub has_a_cycle {
28 8     8 0 30 my ($u, $v, $t, $s) = @_;
29 8         28 $s->{ has_a_cycle } = 1;
30 8         37 $t->terminate;
31             }
32              
33             sub find_a_cycle {
34 5     5 0 15 my ($u, $v, $t, $s) = @_;
35 5         20 my @cycle = ( $u );
36 5 100       18 push @cycle, $v unless $u eq $v;
37 5         11 my $path = $t->{ order };
38 5 100       56 if (@$path) {
39 4         9 my $i = $#$path;
40 4   66     25 while ($i >= 0 && $path->[ $i ] ne $v) { $i-- }
  3         13  
41 4 50       13 if ($i >= 0) {
42 4         14 unshift @cycle, @{ $path }[ $i+1 .. $#$path ];
  4         68  
43             }
44             }
45 5         17 $s->{ a_cycle } = \@cycle;
46 5         16 $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 129     129 0 33627 my ($class, $g, %attr) = @_;
62 129 100 66     1380 Graph::__carp_confess("Graph::Traversal: first argument is not a Graph")
63             unless ref $g && $g->isa('Graph');
64 128         748 my $self = bless { graph => $g, state => { } }, $class;
65 128         615 $self->reset;
66 128 100       538 if (exists $attr{ start }) {
67 1         6 $attr{ first_root } = delete $attr{ start };
68 1         3 $attr{ next_root } = undef;
69             }
70 128         1054 my @found_known = grep exists $attr{$_}, @EXTRACT_CONFIG;
71 128         627 @$self{@found_known} = delete @attr{@found_known};
72             $self->{ seen_edge } = $attr{ seen_edge }
73 128 50 33     436 if exists $attr{ seen_edge } and ($g->multiedged || $g->countedged);
      66        
74 128 100       478 $self->{ pre_edge } = $attr{ tree_edge } if exists $attr{ tree_edge };
75             my $default_next =
76             $attr{ next_alphabetic } ? \&Graph::_next_alphabetic :
77 128 100       837 $attr{ next_numeric } ? \&Graph::_next_numeric :
    100          
78             \&Graph::_next_random;
79 128 100       620 $self->{ next_root } = $default_next if !exists $self->{ next_root };
80             $self->{ first_root } = $self->{ next_root }
81 128 100       529 if !exists $self->{ first_root };
82 128 100       644 $self->{ next_successor } = $default_next if !exists $self->{ next_successor };
83 128 100       384 if (exists $attr{ has_a_cycle }) {
84             $self->{ back_edge } = my $has_a_cycle =
85             ref $attr{ has_a_cycle } eq 'CODE' ?
86 17 50       104 $attr{ has_a_cycle } : \&has_a_cycle;
87 17 100       93 $self->{ down_edge } = $has_a_cycle if $g->is_undirected;
88             }
89 128 100       384 if (exists $attr{ find_a_cycle }) {
90             $self->{ back_edge } = my $find_a_cycle =
91             ref $attr{ find_a_cycle } eq 'CODE' ?
92 3 50       18 $attr{ find_a_cycle } : \&find_a_cycle;
93 3 100       25 $self->{ down_edge } = $find_a_cycle if $g->is_undirected;
94             }
95 128         469 $self->{ add } = \&add_order;
96 128         430 $self->{ see } = \&_see;
97 128         546 delete @attr{@KNOWN_CONFIG};
98 128         496 Graph::_opt_unknown(\%attr);
99 127         466 return $self;
100             }
101              
102             sub terminate {
103 15     15 0 32 my $self = shift;
104 15         53 $self->{ terminate } = 1;
105             }
106              
107             sub add_order {
108 1316     1316 0 2831 my ($self, @next) = @_;
109 1316         2023 push @{ $self->{ order } }, @next;
  1316         3959  
110             }
111              
112             sub visit {
113 1316     1316 0 2624 my ($self, @next) = @_;
114 1316         7439 $self->{ unseen }->remove(@next);
115 1316         4901 $self->{ seen }->insert(@next);
116 1316         3812 $self->{ add }->( $self, @next );
117 1316 100       4216 return unless my $p = $self->{ pre };
118 535         1918 $p->( $_, $self ) for @next;
119             }
120              
121             sub visit_preorder {
122 1316     1316 0 3551 my ($self, @next) = @_;
123 1316         1954 push @{ $self->{ preorder } }, @next;
  1316         3467  
124 1316         5251 $self->{ preordern }->{ $_ } = $self->{ preorderi }++ for @next;
125 1316         3354 $self->visit( @next );
126             }
127              
128             sub visit_postorder {
129 1304     1304 0 3147 my ($self) = @_;
130 1304         4290 my @post = reverse $self->{ see }->( $self );
131 1304         2490 push @{ $self->{ postorder } }, @post;
  1304         4095  
132 1304         6645 $self->{ postordern }->{ $_ } = $self->{ postorderi }++ for @post;
133 1304 100       5302 if (my $p = $self->{ post }) {
134 65         221 $p->( $_, $self ) for @post;
135             }
136 1304 100 100     5640 return unless (my $p = $self->{ post_edge }) and defined(my $u = $self->current);
137 9         41 $p->( $u, $_, $self, $self->{ state }) for @post;
138             }
139              
140             sub _callbacks {
141 1294     1294   11279 my ($self, $current, @all) = @_;
142 1294 100       3701 return unless @all;
143 1221         2851 my $nontree = $self->{ non_tree_edge };
144 1221         2420 my $back = $self->{ back_edge };
145 1221         3999 my $down = $self->{ down_edge };
146 1221         2185 my $cross = $self->{ cross_edge };
147 1221         2367 my $seen = $self->{ seen_edge };
148 1221   66     7200 my $bdc = defined $back || defined $down || defined $cross;
149 1221 100 100     8104 return unless (defined $nontree || $bdc || defined $seen);
      66        
150 560         1030 my $u = $current;
151 560         1787 my $preu = $self->{ preordern }->{ $u };
152 560         1389 my $postu = $self->{ postordern }->{ $u };
153 560         1359 for my $v ( @all ) {
154 32360 50 66     95190 if (!$self->{tree}->has_edge($u, $v) && (defined $nontree || $bdc) &&
      66        
      33        
155             exists $self->{ seen }->{ $v }) {
156 31339 100       1085842 $nontree->( $u, $v, $self, $self->{ state }) if $nontree;
157 31339 100       76208 if ($bdc) {
158 23         62 my $postv = $self->{ postordern }->{ $v };
159 23 100 100     155 if ($back &&
      66        
160             (!defined $postv || $postv >= $postu)) {
161 14         62 $back ->( $u, $v, $self, $self->{ state });
162             } else {
163 9         24 my $prev = $self->{ preordern }->{ $v };
164 9 100 100     81 if ($down && $prev > $preu) {
    100 66        
165 1         5 $down ->( $u, $v, $self, $self->{ state });
166             } elsif ($cross && $prev < $preu) {
167 1         5 $cross->( $u, $v, $self, $self->{ state });
168             }
169             }
170             }
171             }
172 32360 100       160809 next if !$seen;
173 1         6 my $c = $self->graph->get_edge_count($u, $v);
174 1         10 $seen->( $u, $v, $self, $self->{ state } ) while $c-- > 1;
175             }
176             }
177              
178             sub next {
179 1481     1481 0 2462 my $self = shift;
180 1481 100       4106 return undef if $self->{ terminate };
181 1473         2382 my @next;
182 1473         3552 while ($self->seeing) {
183 2322         9010 my $current = $self->current;
184 2322         9719 my $next = Set::Object->new($self->{ graph }->successors($current));
185 2322         40594 my @all = $next->members;
186 2322         10779 $next = $next->difference($self->{seen});
187 2322 100       252126 if ($next->size) {
188 1018         25439 @next = $self->{ next_successor }->( $self, { map +($_=>$_), $next->members } );
189 1018         12666 $self->{ tree }->add_edges(map [$current, $_], @next);
190 1018 100       6750 last unless my $p = $self->{ pre_edge };
191 414         1599 $p->($current, $_, $self, $self->{ state }) for @next;
192 414         7726 last;
193             } else {
194 1304         6196 $self->visit_postorder;
195             }
196 1304 100       3841 return undef if $self->{ terminate };
197 1294         4653 $self->_callbacks($current, @all);
198             }
199 1463 100       3863 unless (@next) {
200 445 100 66     724 if (!@{ $self->{ roots } } and defined(my $first = $self->{ first_root })) {
  445         1926  
201 131 100       719 return unless @next = ref $first eq 'CODE'
    100          
202             ? $first->( $self, { map +($_=>$_), $self->unseen } )
203             : $first;
204             }
205 436 100 100     2411 return if !@next and !$self->{ next_root };
206 430 100 100     1622 return if !@next and !(@next = $self->{ next_root }->( $self, { map +($_=>$_), $self->unseen } ));
207 298 50 33     4298 return if !defined $next[0] or $self->{ seen }->contains($next[0]); # Sanity check.
208 298         2393 push @{ $self->{ roots } }, $next[0];
  298         1076  
209             }
210 1316 50       5547 $self->visit_preorder( @next ) if @next;
211 1316         5954 return $next[0];
212             }
213              
214             sub _order {
215 163     163   485 my ($self, $order) = @_;
216 163         608 1 while defined $self->next;
217 163         357 @{ $self->{ $order } };
  163         1652  
218             }
219              
220             sub preorder {
221 19     19 1 110 my $self = shift;
222 19         77 $self->_order( 'preorder' );
223             }
224              
225             sub postorder {
226 144     144 1 462 my $self = shift;
227 144         575 $self->_order( 'postorder' );
228             }
229              
230             sub unseen {
231 453     453 1 106174 my $self = shift;
232 453 100       908 $self->{ unseen }->${ wantarray ? \'members' : \'size' };
  453         13560  
233             }
234              
235             sub seen {
236 37     37 1 112 my $self = shift;
237 37 100       115 $self->{ seen }->${ wantarray ? \'members' : \'size' };
  37         672  
238             }
239              
240             sub seeing {
241 2787     2787 1 5273 my $self = shift;
242 2787         4780 @{ $self->{ order } };
  2787         9008  
243             }
244              
245             sub roots {
246 16     16 0 54 my $self = shift;
247 16         39 @{ $self->{ roots } };
  16         258  
248             }
249              
250             sub is_root {
251 9     9 0 26 my ($self, $v) = @_;
252 9         18 for my $u (@{ $self->{ roots } }) {
  9         24  
253 11 100       52 return 1 if $u eq $v;
254             }
255 5         18 return 0;
256             }
257              
258             sub tree {
259 1     1 1 12001 my $self = shift;
260 1         8 $self->{ tree };
261             }
262              
263             sub graph {
264 3     3 0 4829 my $self = shift;
265 3         19 $self->{ graph };
266             }
267              
268             sub vertex_by_postorder {
269 4     4 1 13 my ($self, $i) = @_;
270 4 50       39 exists $self->{ postorder } && $self->{ postorder }->[ $i ];
271             }
272              
273             sub postorder_by_vertex {
274 4     4 1 13 my ($self, $v) = @_;
275 4 50       39 exists $self->{ postordern } && $self->{ postordern }->{ $v };
276             }
277              
278             sub postorder_vertices {
279 1     1 1 4425 my ($self, $v) = @_;
280 1 50       7 exists $self->{ postordern } ? %{ $self->{ postordern } } : ();
  1         10  
281             }
282              
283             sub vertex_by_preorder {
284 4     4 1 16 my ($self, $i) = @_;
285 4 50       39 exists $self->{ preorder } && $self->{ preorder }->[ $i ];
286             }
287              
288             sub preorder_by_vertex {
289 4     4 1 14 my ($self, $v) = @_;
290 4 50       41 exists $self->{ preordern } && $self->{ preordern }->{ $v };
291             }
292              
293             sub preorder_vertices {
294 1     1 1 4 my ($self, $v) = @_;
295 1 50       6 exists $self->{ preordern } ? %{ $self->{ preordern } } : ();
  1         11  
296             }
297              
298             sub has_state {
299 5     5 1 992 my ($self, $var) = @_;
300 5 100       46 exists $self->{ state } && exists $self->{ state }->{ $var };
301             }
302              
303             sub get_state {
304 22     22 1 69 my ($self, $var) = @_;
305 22 100       539 exists $self->{ state } ? $self->{ state }->{ $var } : undef;
306             }
307              
308             sub set_state {
309 1     1 1 5 my ($self, $var, $val) = @_;
310 1         4 $self->{ state }->{ $var } = $val;
311 1         6 return 1;
312             }
313              
314             sub delete_state {
315 1     1 1 4 my ($self, $var) = @_;
316 1         4 delete $self->{ state }->{ $var };
317 1 50       3 delete $self->{ state } unless keys %{ $self->{ state } };
  1         7  
318 1         5 return 1;
319             }
320              
321             1;
322             __END__