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   60 use strict;
  11         21  
  11         331  
4 11     11   90 use warnings;
  11         41  
  11         23962  
5              
6             # $SIG{__DIE__ } = \&Graph::__carp_confess;
7             # $SIG{__WARN__} = \&Graph::__carp_confess;
8              
9             sub reset {
10 104     104 0 216 my $self = shift;
11 104         2426 require Set::Object;
12 104         26297 $self->{ unseen } = Set::Object->new($self->{ graph }->vertices);
13 104         354 $self->{ seen } = Set::Object->new;
14 104         202 $self->{ order } = [ ];
15 104         191 $self->{ preorder } = [ ];
16 104         151 $self->{ postorder } = [ ];
17 104         252 $self->{ roots } = [ ];
18 104         309 $self->{ tree } = Graph->new(directed => $self->{ graph }->directed);
19 104         236 delete $self->{ terminate };
20             }
21              
22             sub _see {
23 765     765   959 my $self = shift;
24 765         1390 $self->see;
25             }
26              
27             sub has_a_cycle {
28 8     8 0 21 my ($u, $v, $t, $s) = @_;
29 8         15 $s->{ has_a_cycle } = 1;
30 8         26 $t->terminate;
31             }
32              
33             sub find_a_cycle {
34 5     5 0 12 my ($u, $v, $t, $s) = @_;
35 5         12 my @cycle = ( $u );
36 5 100       17 push @cycle, $v unless $u eq $v;
37 5         9 my $path = $t->{ order };
38 5 100       22 if (@$path) {
39 4         8 my $i = $#$path;
40 4   66     20 while ($i >= 0 && $path->[ $i ] ne $v) { $i-- }
  3         9  
41 4 50       10 if ($i >= 0) {
42 4         8 unshift @cycle, @{ $path }[ $i+1 .. $#$path ];
  4         10  
43             }
44             }
45 5         12 $s->{ a_cycle } = \@cycle;
46 5         12 $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 10938 my ($class, $g, %attr) = @_;
62 103 100 66     726 Graph::__carp_confess("Graph::Traversal: first argument is not a Graph")
63             unless ref $g && $g->isa('Graph');
64 102         380 my $self = bless { graph => $g, state => { } }, $class;
65 102         331 $self->reset;
66 102 100       259 if (exists $attr{ start }) {
67 1         2 $attr{ first_root } = delete $attr{ start };
68 1         2 $attr{ next_root } = undef;
69             }
70 102         496 my @found_known = grep exists $attr{$_}, @EXTRACT_CONFIG;
71 102         309 @$self{@found_known} = delete @attr{@found_known};
72             $self->{ seen_edge } = $attr{ seen_edge }
73 102 50 33     251 if exists $attr{ seen_edge } and ($g->multiedged || $g->countedged);
      66        
74 102 50       219 $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       275 $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       327 if !exists $self->{ first_root };
    100          
83 102 100       238 $self->{ next_successor } = $default_next if !exists $self->{ next_successor };
84 102 100       234 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       39 $self->{ down_edge } = $has_a_cycle if $g->is_undirected;
89             }
90 102 100       200 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       18 $self->{ down_edge } = $find_a_cycle if $g->is_undirected;
95             }
96 102         219 $self->{ add } = \&add_order;
97 102         207 $self->{ see } = \&_see;
98 102         275 delete @attr{@KNOWN_CONFIG};
99 102         259 Graph::_opt_unknown(\%attr);
100 101         244 return $self;
101             }
102              
103             sub terminate {
104 15     15 0 23 my $self = shift;
105 15         32 $self->{ terminate } = 1;
106             }
107              
108             sub add_order {
109 775     775 0 1136 my ($self, @next) = @_;
110 775         1069 push @{ $self->{ order } }, @next;
  775         1447  
111             }
112              
113             sub visit {
114 775     775 0 1246 my ($self, @next) = @_;
115 775         2471 $self->{ unseen }->remove(@next);
116 775         1856 $self->{ seen }->insert(@next);
117 775         1590 $self->{ add }->( $self, @next );
118 775 100       1568 return unless my $p = $self->{ pre };
119 420         987 $p->( $_, $self ) for @next;
120             }
121              
122             sub visit_preorder {
123 775     775 0 1355 my ($self, @next) = @_;
124 775         837 push @{ $self->{ preorder } }, @next;
  775         1326  
125 775         1974 $self->{ preordern }->{ $_ } = $self->{ preorderi }++ for @next;
126 775         1385 $self->visit( @next );
127             }
128              
129             sub visit_postorder {
130 765     765 0 1045 my ($self) = @_;
131 765         1280 my @post = reverse $self->{ see }->( $self );
132 765         927 push @{ $self->{ postorder } }, @post;
  765         1335  
133 765         2079 $self->{ postordern }->{ $_ } = $self->{ postorderi }++ for @post;
134 765 100       1473 if (my $p = $self->{ post }) {
135 65         130 $p->( $_, $self ) for @post;
136             }
137 765 100 100     1801 return unless (my $p = $self->{ post_edge }) and defined(my $u = $self->current);
138 9         26 $p->( $u, $_, $self, $self->{ state }) for @post;
139             }
140              
141             sub _callbacks {
142 755     755   1411 my ($self, $current, @all) = @_;
143 755 100       1379 return unless @all;
144 691         923 my $nontree = $self->{ non_tree_edge };
145 691         779 my $back = $self->{ back_edge };
146 691         788 my $down = $self->{ down_edge };
147 691         786 my $cross = $self->{ cross_edge };
148 691         786 my $seen = $self->{ seen_edge };
149 691   66     2306 my $bdc = defined $back || defined $down || defined $cross;
150 691 100 100     3549 return unless (defined $nontree || $bdc || defined $seen);
      66        
151 46         57 my $u = $current;
152 46         64 my $preu = $self->{ preordern }->{ $u };
153 46         62 my $postu = $self->{ postordern }->{ $u };
154 46         71 for my $v ( @all ) {
155 59 50 66     146 if (!$self->{tree}->has_edge($u, $v) && (defined $nontree || $bdc) &&
      66        
      33        
156             exists $self->{ seen }->{ $v }) {
157 25 100       871 $nontree->( $u, $v, $self, $self->{ state }) if $nontree;
158 25 50       56 if ($bdc) {
159 25         36 my $postv = $self->{ postordern }->{ $v };
160 25 100 100     104 if ($back &&
      66        
161             (!defined $postv || $postv >= $postu)) {
162 14         40 $back ->( $u, $v, $self, $self->{ state });
163             } else {
164 11         18 my $prev = $self->{ preordern }->{ $v };
165 11 100 100     46 if ($down && $prev > $preu) {
    100 66        
166 1         4 $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 59 100       314 next if !$seen;
174 1         4 my $c = $self->graph->get_edge_count($u, $v);
175 1         6 $seen->( $u, $v, $self, $self->{ state } ) while $c-- > 1;
176             }
177             }
178              
179             sub next {
180 914     914 0 1148 my $self = shift;
181 914 100       1510 return undef if $self->{ terminate };
182 906         994 my @next;
183 906         1686 while ($self->seeing) {
184 1269         2671 my $current = $self->current;
185 1269         2788 my $next = Set::Object->new($self->{ graph }->successors($current));
186 1269         3969 my @all = $next->members;
187 1269         3090 $next = $next->difference($self->{seen});
188 1269 100       30479 if ($next->size) {
189 504         2662 @next = $self->{ next_successor }->( $self, { map +($_=>$_), $next->members } );
190 504         2324 $self->{ tree }->add_edges(map [$current, $_], @next);
191 504 100       2309 last unless my $p = $self->{ pre_edge };
192 9         23 $p->($current, $_, $self, $self->{ state }) for @next;
193 9         57 last;
194             } else {
195 765         1316 $self->visit_postorder;
196             }
197 765 100       1282 return undef if $self->{ terminate };
198 755         1268 $self->_callbacks($current, @all);
199             }
200 896 100       1645 unless (@next) {
201 392 100 66     422 if (!@{ $self->{ roots } } and defined(my $first = $self->{ first_root })) {
  392         1064  
202 105 100       392 return unless @next = ref $first eq 'CODE'
    100          
203             ? $first->( $self, { map +($_=>$_), $self->unseen } )
204             : $first;
205             }
206 388 100 100     1268 return if !@next and !$self->{ next_root };
207 382 100 100     1448 return if !@next and !(@next = $self->{ next_root }->( $self, { map +($_=>$_), $self->unseen } ));
208 271 50       1620 return if $self->{ seen }->contains($next[0]); # Sanity check.
209 271         1250 push @{ $self->{ roots } }, $next[0];
  271         552  
210             }
211 775 50       2185 $self->visit_preorder( @next ) if @next;
212 775         2311 return $next[0];
213             }
214              
215             sub _order {
216 137     137   338 my ($self, $order) = @_;
217 137         319 1 while defined $self->next;
218 137         196 @{ $self->{ $order } };
  137         811  
219             }
220              
221             sub preorder {
222 19     19 1 73 my $self = shift;
223 19         50 $self->_order( 'preorder' );
224             }
225              
226             sub postorder {
227 118     118 1 246 my $self = shift;
228 118         275 $self->_order( 'postorder' );
229             }
230              
231             sub unseen {
232 400     400 1 50920 my $self = shift;
233 400 100       513 $self->{ unseen }->${ wantarray ? \'members' : \'size' };
  400         6415  
234             }
235              
236             sub seen {
237 37     37 1 68 my $self = shift;
238 37 100       57 $self->{ seen }->${ wantarray ? \'members' : \'size' };
  37         366  
239             }
240              
241             sub seeing {
242 1681     1681 1 2079 my $self = shift;
243 1681         1885 @{ $self->{ order } };
  1681         3507  
244             }
245              
246             sub roots {
247 16     16 0 29 my $self = shift;
248 16         30 @{ $self->{ roots } };
  16         107  
249             }
250              
251             sub is_root {
252 9     9 0 25 my ($self, $v) = @_;
253 9         10 for my $u (@{ $self->{ roots } }) {
  9         20  
254 11 100       36 return 1 if $u eq $v;
255             }
256 5         18 return 0;
257             }
258              
259             sub tree {
260 1     1 1 6942 my $self = shift;
261 1         6 $self->{ tree };
262             }
263              
264             sub graph {
265 3     3 0 2174 my $self = shift;
266 3         12 $self->{ graph };
267             }
268              
269             sub vertex_by_postorder {
270 4     4 1 9 my ($self, $i) = @_;
271 4 50       24 exists $self->{ postorder } && $self->{ postorder }->[ $i ];
272             }
273              
274             sub postorder_by_vertex {
275 4     4 1 9 my ($self, $v) = @_;
276 4 50       23 exists $self->{ postordern } && $self->{ postordern }->{ $v };
277             }
278              
279             sub postorder_vertices {
280 1     1 1 2635 my ($self, $v) = @_;
281 1 50       4 exists $self->{ postordern } ? %{ $self->{ postordern } } : ();
  1         70  
282             }
283              
284             sub vertex_by_preorder {
285 4     4 1 9 my ($self, $i) = @_;
286 4 50       22 exists $self->{ preorder } && $self->{ preorder }->[ $i ];
287             }
288              
289             sub preorder_by_vertex {
290 4     4 1 8 my ($self, $v) = @_;
291 4 50       22 exists $self->{ preordern } && $self->{ preordern }->{ $v };
292             }
293              
294             sub preorder_vertices {
295 1     1 1 4 my ($self, $v) = @_;
296 1 50       3 exists $self->{ preordern } ? %{ $self->{ preordern } } : ();
  1         101  
297             }
298              
299             sub has_state {
300 5     5 1 779 my ($self, $var) = @_;
301 5 100       48 exists $self->{ state } && exists $self->{ state }->{ $var };
302             }
303              
304             sub get_state {
305 22     22 1 49 my ($self, $var) = @_;
306 22 100       307 exists $self->{ state } ? $self->{ state }->{ $var } : undef;
307             }
308              
309             sub set_state {
310 1     1 1 4 my ($self, $var, $val) = @_;
311 1         4 $self->{ state }->{ $var } = $val;
312 1         3 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         6  
319 1         4 return 1;
320             }
321              
322             1;
323             __END__