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   77 use strict;
  11         24  
  11         406  
4 11     11   137 use warnings;
  11         21  
  11         30164  
5              
6             # $SIG{__DIE__ } = \&Graph::__carp_confess;
7             # $SIG{__WARN__} = \&Graph::__carp_confess;
8              
9             sub reset {
10 104     104 0 200 my $self = shift;
11 104         2982 require Set::Object;
12 104         31689 $self->{ unseen } = Set::Object->new($self->{ graph }->vertices);
13 104         389 $self->{ seen } = Set::Object->new;
14 104         224 $self->{ order } = [ ];
15 104         213 $self->{ preorder } = [ ];
16 104         194 $self->{ postorder } = [ ];
17 104         298 $self->{ roots } = [ ];
18 104         366 $self->{ tree } = Graph->new(directed => $self->{ graph }->directed);
19 104         249 delete $self->{ terminate };
20             }
21              
22             sub _see {
23 779     779   1060 my $self = shift;
24 779         1660 $self->see;
25             }
26              
27             sub has_a_cycle {
28 8     8 0 32 my ($u, $v, $t, $s) = @_;
29 8         34 $s->{ has_a_cycle } = 1;
30 8         32 $t->terminate;
31             }
32              
33             sub find_a_cycle {
34 5     5 0 12 my ($u, $v, $t, $s) = @_;
35 5         27 my @cycle = ( $u );
36 5 100       18 push @cycle, $v unless $u eq $v;
37 5         10 my $path = $t->{ order };
38 5 100       24 if (@$path) {
39 4         9 my $i = $#$path;
40 4   66     29 while ($i >= 0 && $path->[ $i ] ne $v) { $i-- }
  3         10  
41 4 50       11 if ($i >= 0) {
42 4         9 unshift @cycle, @{ $path }[ $i+1 .. $#$path ];
  4         13  
43             }
44             }
45 5         12 $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 103     103 0 11045 my ($class, $g, %attr) = @_;
62 103 100 66     799 Graph::__carp_confess("Graph::Traversal: first argument is not a Graph")
63             unless ref $g && $g->isa('Graph');
64 102         395 my $self = bless { graph => $g, state => { } }, $class;
65 102         379 $self->reset;
66 102 100       305 if (exists $attr{ start }) {
67 1         3 $attr{ first_root } = delete $attr{ start };
68 1         2 $attr{ next_root } = undef;
69             }
70 102         544 my @found_known = grep exists $attr{$_}, @EXTRACT_CONFIG;
71 102         345 @$self{@found_known} = delete @attr{@found_known};
72             $self->{ seen_edge } = $attr{ seen_edge }
73 102 50 33     269 if exists $attr{ seen_edge } and ($g->multiedged || $g->countedged);
      66        
74 102 50       228 $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       347 $attr{ next_numeric } ? \&Graph::_next_numeric :
    100          
78             \&Graph::_next_random;
79 102 100       277 $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       312 if !exists $self->{ first_root };
    100          
83 102 100       252 $self->{ next_successor } = $default_next if !exists $self->{ next_successor };
84 102 100       241 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       50 $attr{ has_a_cycle } : \&has_a_cycle;
88 17 100       44 $self->{ down_edge } = $has_a_cycle if $g->is_undirected;
89             }
90 102 100       214 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       10 $attr{ find_a_cycle } : \&find_a_cycle;
94 3 100       20 $self->{ down_edge } = $find_a_cycle if $g->is_undirected;
95             }
96 102         217 $self->{ add } = \&add_order;
97 102         217 $self->{ see } = \&_see;
98 102         277 delete @attr{@KNOWN_CONFIG};
99 102         298 Graph::_opt_unknown(\%attr);
100 101         302 return $self;
101             }
102              
103             sub terminate {
104 15     15 0 26 my $self = shift;
105 15         43 $self->{ terminate } = 1;
106             }
107              
108             sub add_order {
109 789     789 0 1399 my ($self, @next) = @_;
110 789         1023 push @{ $self->{ order } }, @next;
  789         1901  
111             }
112              
113             sub visit {
114 789     789 0 1529 my ($self, @next) = @_;
115 789         2724 $self->{ unseen }->remove(@next);
116 789         2434 $self->{ seen }->insert(@next);
117 789         1929 $self->{ add }->( $self, @next );
118 789 100       1904 return unless my $p = $self->{ pre };
119 427         1243 $p->( $_, $self ) for @next;
120             }
121              
122             sub visit_preorder {
123 789     789 0 1692 my ($self, @next) = @_;
124 789         1015 push @{ $self->{ preorder } }, @next;
  789         1627  
125 789         2412 $self->{ preordern }->{ $_ } = $self->{ preorderi }++ for @next;
126 789         1680 $self->visit( @next );
127             }
128              
129             sub visit_postorder {
130 779     779 0 1230 my ($self) = @_;
131 779         1604 my @post = reverse $self->{ see }->( $self );
132 779         1110 push @{ $self->{ postorder } }, @post;
  779         1784  
133 779         2859 $self->{ postordern }->{ $_ } = $self->{ postorderi }++ for @post;
134 779 100       1744 if (my $p = $self->{ post }) {
135 65         159 $p->( $_, $self ) for @post;
136             }
137 779 100 100     2208 return unless (my $p = $self->{ post_edge }) and defined(my $u = $self->current);
138 9         29 $p->( $u, $_, $self, $self->{ state }) for @post;
139             }
140              
141             sub _callbacks {
142 769     769   1730 my ($self, $current, @all) = @_;
143 769 100       1723 return unless @all;
144 705         1103 my $nontree = $self->{ non_tree_edge };
145 705         998 my $back = $self->{ back_edge };
146 705         944 my $down = $self->{ down_edge };
147 705         1032 my $cross = $self->{ cross_edge };
148 705         949 my $seen = $self->{ seen_edge };
149 705   66     2821 my $bdc = defined $back || defined $down || defined $cross;
150 705 100 100     4423 return unless (defined $nontree || $bdc || defined $seen);
      66        
151 46         81 my $u = $current;
152 46         77 my $preu = $self->{ preordern }->{ $u };
153 46         89 my $postu = $self->{ postordern }->{ $u };
154 46         77 for my $v ( @all ) {
155 58 50 66     184 if (!$self->{tree}->has_edge($u, $v) && (defined $nontree || $bdc) &&
      66        
      33        
156             exists $self->{ seen }->{ $v }) {
157 28 100       1017 $nontree->( $u, $v, $self, $self->{ state }) if $nontree;
158 28 50       62 if ($bdc) {
159 28         50 my $postv = $self->{ postordern }->{ $v };
160 28 100 100     144 if ($back &&
      66        
161             (!defined $postv || $postv >= $postu)) {
162 14         46 $back ->( $u, $v, $self, $self->{ state });
163             } else {
164 14         26 my $prev = $self->{ preordern }->{ $v };
165 14 100 100     73 if ($down && $prev > $preu) {
    100 66        
166 1         4 $down ->( $u, $v, $self, $self->{ state });
167             } elsif ($cross && $prev < $preu) {
168 1         3 $cross->( $u, $v, $self, $self->{ state });
169             }
170             }
171             }
172             }
173 58 100       374 next if !$seen;
174 1         5 my $c = $self->graph->get_edge_count($u, $v);
175 1         8 $seen->( $u, $v, $self, $self->{ state } ) while $c-- > 1;
176             }
177             }
178              
179             sub next {
180 928     928 0 1431 my $self = shift;
181 928 100       1873 return undef if $self->{ terminate };
182 920         1138 my @next;
183 920         1738 while ($self->seeing) {
184 1306         3146 my $current = $self->current;
185 1306         3547 my $next = Set::Object->new($self->{ graph }->successors($current));
186 1306         5068 my @all = $next->members;
187 1306         3888 $next = $next->difference($self->{seen});
188 1306 100       38930 if ($next->size) {
189 527         3163 @next = $self->{ next_successor }->( $self, { map +($_=>$_), $next->members } );
190 527         3044 $self->{ tree }->add_edges(map [$current, $_], @next);
191 527 100       3023 last unless my $p = $self->{ pre_edge };
192 9         32 $p->($current, $_, $self, $self->{ state }) for @next;
193 9         73 last;
194             } else {
195 779         1716 $self->visit_postorder;
196             }
197 779 100       1615 return undef if $self->{ terminate };
198 769         1700 $self->_callbacks($current, @all);
199             }
200 910 100       2041 unless (@next) {
201 383 100 66     482 if (!@{ $self->{ roots } } and defined(my $first = $self->{ first_root })) {
  383         1251  
202 105 100       433 return unless @next = ref $first eq 'CODE'
    100          
203             ? $first->( $self, { map +($_=>$_), $self->unseen } )
204             : $first;
205             }
206 379 100 100     1511 return if !@next and !$self->{ next_root };
207 373 100 100     1020 return if !@next and !(@next = $self->{ next_root }->( $self, { map +($_=>$_), $self->unseen } ));
208 262 50       1933 return if $self->{ seen }->contains($next[0]); # Sanity check.
209 262         1600 push @{ $self->{ roots } }, $next[0];
  262         651  
210             }
211 789 50       2743 $self->visit_preorder( @next ) if @next;
212 789         2823 return $next[0];
213             }
214              
215             sub _order {
216 137     137   258 my ($self, $order) = @_;
217 137         321 1 while defined $self->next;
218 137         246 @{ $self->{ $order } };
  137         1019  
219             }
220              
221             sub preorder {
222 19     19 1 80 my $self = shift;
223 19         53 $self->_order( 'preorder' );
224             }
225              
226             sub postorder {
227 118     118 1 293 my $self = shift;
228 118         283 $self->_order( 'postorder' );
229             }
230              
231             sub unseen {
232 391     391 1 52183 my $self = shift;
233 391 100       564 $self->{ unseen }->${ wantarray ? \'members' : \'size' };
  391         7832  
234             }
235              
236             sub seen {
237 37     37 1 73 my $self = shift;
238 37 100       70 $self->{ seen }->${ wantarray ? \'members' : \'size' };
  37         424  
239             }
240              
241             sub seeing {
242 1709     1709 1 2351 my $self = shift;
243 1709         2082 @{ $self->{ order } };
  1709         4129  
244             }
245              
246             sub roots {
247 16     16 0 33 my $self = shift;
248 16         25 @{ $self->{ roots } };
  16         127  
249             }
250              
251             sub is_root {
252 9     9 0 22 my ($self, $v) = @_;
253 9         17 for my $u (@{ $self->{ roots } }) {
  9         21  
254 11 100       42 return 1 if $u eq $v;
255             }
256 5         24 return 0;
257             }
258              
259             sub tree {
260 1     1 1 6810 my $self = shift;
261 1         6 $self->{ tree };
262             }
263              
264             sub graph {
265 3     3 0 2258 my $self = shift;
266 3         14 $self->{ graph };
267             }
268              
269             sub vertex_by_postorder {
270 4     4 1 11 my ($self, $i) = @_;
271 4 50       28 exists $self->{ postorder } && $self->{ postorder }->[ $i ];
272             }
273              
274             sub postorder_by_vertex {
275 4     4 1 9 my ($self, $v) = @_;
276 4 50       28 exists $self->{ postordern } && $self->{ postordern }->{ $v };
277             }
278              
279             sub postorder_vertices {
280 1     1 1 2542 my ($self, $v) = @_;
281 1 50       4 exists $self->{ postordern } ? %{ $self->{ postordern } } : ();
  1         100  
282             }
283              
284             sub vertex_by_preorder {
285 4     4 1 10 my ($self, $i) = @_;
286 4 50       28 exists $self->{ preorder } && $self->{ preorder }->[ $i ];
287             }
288              
289             sub preorder_by_vertex {
290 4     4 1 11 my ($self, $v) = @_;
291 4 50       28 exists $self->{ preordern } && $self->{ preordern }->{ $v };
292             }
293              
294             sub preorder_vertices {
295 1     1 1 4 my ($self, $v) = @_;
296 1 50       5 exists $self->{ preordern } ? %{ $self->{ preordern } } : ();
  1         131  
297             }
298              
299             sub has_state {
300 5     5 1 831 my ($self, $var) = @_;
301 5 100       40 exists $self->{ state } && exists $self->{ state }->{ $var };
302             }
303              
304             sub get_state {
305 22     22 1 56 my ($self, $var) = @_;
306 22 100       359 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         6 return 1;
313             }
314              
315             sub delete_state {
316 1     1 1 5 my ($self, $var) = @_;
317 1         3 delete $self->{ state }->{ $var };
318 1 50       3 delete $self->{ state } unless keys %{ $self->{ state } };
  1         7  
319 1         6 return 1;
320             }
321              
322             1;
323             __END__