Version 0.99.03
[nasm/avx512.git] / perllib / Graph / Traversal.pm
blobedfc5b192a2d0ff1b26186a2cd1a610e7340b9f4
1 package Graph::Traversal;
3 use strict;
5 # $SIG{__DIE__ } = sub { use Carp; confess };
6 # $SIG{__WARN__} = sub { use Carp; confess };
8 sub DEBUG () { 0 }
10 sub reset {
11 my $self = shift;
12 $self->{ unseen } = { map { $_ => $_ } $self->{ graph }->vertices };
13 $self->{ seen } = { };
14 $self->{ order } = [ ];
15 $self->{ preorder } = [ ];
16 $self->{ postorder } = [ ];
17 $self->{ roots } = [ ];
18 $self->{ tree } =
19 Graph->new( directed => $self->{ graph }->directed );
20 delete $self->{ terminate };
23 my $see = sub {
24 my $self = shift;
25 $self->see;
28 my $see_active = sub {
29 my $self = shift;
30 delete @{ $self->{ active } }{ $self->see };
33 sub has_a_cycle {
34 my ($u, $v, $t, $s) = @_;
35 $s->{ has_a_cycle } = 1;
36 $t->terminate;
39 sub find_a_cycle {
40 my ($u, $v, $t, $s) = @_;
41 my @cycle = ( $u );
42 push @cycle, $v unless $u eq $v;
43 my $path = $t->{ order };
44 if (@$path) {
45 my $i = $#$path;
46 while ($i >= 0 && $path->[ $i ] ne $v) { $i-- }
47 if ($i >= 0) {
48 unshift @cycle, @{ $path }[ $i+1 .. $#$path ];
51 $s->{ a_cycle } = \@cycle;
52 $t->terminate;
55 sub configure {
56 my ($self, %attr) = @_;
57 $self->{ pre } = $attr{ pre } if exists $attr{ pre };
58 $self->{ post } = $attr{ post } if exists $attr{ post };
59 $self->{ pre_vertex } = $attr{ pre_vertex } if exists $attr{ pre_vertex };
60 $self->{ post_vertex } = $attr{ post_vertex } if exists $attr{ post_vertex };
61 $self->{ pre_edge } = $attr{ pre_edge } if exists $attr{ pre_edge };
62 $self->{ post_edge } = $attr{ post_edge } if exists $attr{ post_edge };
63 if (exists $attr{ successor }) { # Graph 0.201 compatibility.
64 $self->{ tree_edge } = $self->{ non_tree_edge } = $attr{ successor };
66 if (exists $attr{ unseen_successor }) {
67 if (exists $self->{ tree_edge }) { # Graph 0.201 compatibility.
68 my $old_tree_edge = $self->{ tree_edge };
69 $self->{ tree_edge } = sub {
70 $old_tree_edge->( @_ );
71 $attr{ unseen_successor }->( @_ );
73 } else {
74 $self->{ tree_edge } = $attr{ unseen_successor };
77 if ($self->graph->multiedged || $self->graph->countedged) {
78 $self->{ seen_edge } = $attr{ seen_edge } if exists $attr{ seen_edge };
79 if (exists $attr{ seen_successor }) { # Graph 0.201 compatibility.
80 $self->{ seen_edge } = $attr{ seen_edge };
83 $self->{ non_tree_edge } = $attr{ non_tree_edge } if exists $attr{ non_tree_edge };
84 $self->{ pre_edge } = $attr{ tree_edge } if exists $attr{ tree_edge };
85 $self->{ back_edge } = $attr{ back_edge } if exists $attr{ back_edge };
86 $self->{ down_edge } = $attr{ down_edge } if exists $attr{ down_edge };
87 $self->{ cross_edge } = $attr{ cross_edge } if exists $attr{ cross_edge };
88 if (exists $attr{ start }) {
89 $attr{ first_root } = $attr{ start };
90 $attr{ next_root } = undef;
92 if (exists $attr{ get_next_root }) {
93 $attr{ next_root } = $attr{ get_next_root }; # Graph 0.201 compat.
95 $self->{ next_root } =
96 exists $attr{ next_root } ?
97 $attr{ next_root } :
98 $attr{ next_alphabetic } ?
99 \&Graph::_next_alphabetic :
100 $attr{ next_numeric } ?
101 \&Graph::_next_numeric :
102 \&Graph::_next_random;
103 $self->{ first_root } =
104 exists $attr{ first_root } ?
105 $attr{ first_root } :
106 exists $attr{ next_root } ?
107 $attr{ next_root } :
108 $attr{ next_alphabetic } ?
109 \&Graph::_next_alphabetic :
110 $attr{ next_numeric } ?
111 \&Graph::_next_numeric :
112 \&Graph::_next_random;
113 $self->{ next_successor } =
114 exists $attr{ next_successor } ?
115 $attr{ next_successor } :
116 $attr{ next_alphabetic } ?
117 \&Graph::_next_alphabetic :
118 $attr{ next_numeric } ?
119 \&Graph::_next_numeric :
120 \&Graph::_next_random;
121 if (exists $attr{ has_a_cycle }) {
122 my $has_a_cycle =
123 ref $attr{ has_a_cycle } eq 'CODE' ?
124 $attr{ has_a_cycle } : \&has_a_cycle;
125 $self->{ back_edge } = $has_a_cycle;
126 if ($self->{ graph }->is_undirected) {
127 $self->{ down_edge } = $has_a_cycle;
130 if (exists $attr{ find_a_cycle }) {
131 my $find_a_cycle =
132 ref $attr{ find_a_cycle } eq 'CODE' ?
133 $attr{ find_a_cycle } : \&find_a_cycle;
134 $self->{ back_edge } = $find_a_cycle;
135 if ($self->{ graph }->is_undirected) {
136 $self->{ down_edge } = $find_a_cycle;
139 $self->{ add } = \&add_order;
140 $self->{ see } = $see;
141 delete @attr{ qw(
142 pre post pre_edge post_edge
143 successor unseen_successor seen_successor
144 tree_edge non_tree_edge
145 back_edge down_edge cross_edge seen_edge
146 start get_next_root
147 next_root next_alphabetic next_numeric next_random next_successor
148 first_root
149 has_a_cycle find_a_cycle
150 ) };
151 if (keys %attr) {
152 require Carp;
153 my @attr = sort keys %attr;
154 Carp::croak(sprintf "Graph::Traversal: unknown attribute%s @{[map { qq['$_'] } @attr]}\n", @attr == 1 ? '' : 's');
158 sub new {
159 my $class = shift;
160 my $g = shift;
161 unless (ref $g && $g->isa('Graph')) {
162 require Carp;
163 Carp::croak("Graph::Traversal: first argument is not a Graph");
165 my $self = { graph => $g, state => { } };
166 bless $self, $class;
167 $self->reset;
168 $self->configure( @_ );
169 return $self;
172 sub terminate {
173 my $self = shift;
174 $self->{ terminate } = 1;
177 sub add_order {
178 my ($self, @next) = @_;
179 push @{ $self->{ order } }, @next;
182 sub visit {
183 my ($self, @next) = @_;
184 delete @{ $self->{ unseen } }{ @next };
185 print "unseen = @{[sort keys %{$self->{unseen}}]}\n" if DEBUG;
186 @{ $self->{ seen } }{ @next } = @next;
187 print "seen = @{[sort keys %{$self->{seen}}]}\n" if DEBUG;
188 $self->{ add }->( $self, @next );
189 print "order = @{$self->{order}}\n" if DEBUG;
190 if (exists $self->{ pre }) {
191 my $p = $self->{ pre };
192 for my $v (@next) {
193 $p->( $v, $self );
198 sub visit_preorder {
199 my ($self, @next) = @_;
200 push @{ $self->{ preorder } }, @next;
201 for my $v (@next) {
202 $self->{ preordern }->{ $v } = $self->{ preorderi }++;
204 print "preorder = @{$self->{preorder}}\n" if DEBUG;
205 $self->visit( @next );
208 sub visit_postorder {
209 my ($self) = @_;
210 my @post = reverse $self->{ see }->( $self );
211 push @{ $self->{ postorder } }, @post;
212 for my $v (@post) {
213 $self->{ postordern }->{ $v } = $self->{ postorderi }++;
215 print "postorder = @{$self->{postorder}}\n" if DEBUG;
216 if (exists $self->{ post }) {
217 my $p = $self->{ post };
218 for my $v (@post) {
219 $p->( $v, $self ) ;
222 if (exists $self->{ post_edge }) {
223 my $p = $self->{ post_edge };
224 my $u = $self->current;
225 if (defined $u) {
226 for my $v (@post) {
227 $p->( $u, $v, $self, $self->{ state });
233 sub _callbacks {
234 my ($self, $current, @all) = @_;
235 return unless @all;
236 my $nontree = $self->{ non_tree_edge };
237 my $back = $self->{ back_edge };
238 my $down = $self->{ down_edge };
239 my $cross = $self->{ cross_edge };
240 my $seen = $self->{ seen_edge };
241 my $bdc = defined $back || defined $down || defined $cross;
242 if (defined $nontree || $bdc || defined $seen) {
243 my $u = $current;
244 my $preu = $self->{ preordern }->{ $u };
245 my $postu = $self->{ postordern }->{ $u };
246 for my $v ( @all ) {
247 my $e = $self->{ tree }->has_edge( $u, $v );
248 if ( !$e && (defined $nontree || $bdc) ) {
249 if ( exists $self->{ seen }->{ $v }) {
250 $nontree->( $u, $v, $self, $self->{ state })
251 if $nontree;
252 if ($bdc) {
253 my $postv = $self->{ postordern }->{ $v };
254 if ($back &&
255 (!defined $postv || $postv >= $postu)) {
256 $back ->( $u, $v, $self, $self->{ state });
257 } else {
258 my $prev = $self->{ preordern }->{ $v };
259 if ($down && $prev > $preu) {
260 $down ->( $u, $v, $self, $self->{ state });
261 } elsif ($cross && $prev < $preu) {
262 $cross->( $u, $v, $self, $self->{ state });
268 if ($seen) {
269 my $c = $self->graph->get_edge_count($u, $v);
270 while ($c-- > 1) {
271 $seen->( $u, $v, $self, $self->{ state } );
278 sub next {
279 my $self = shift;
280 return undef if $self->{ terminate };
281 my @next;
282 while ($self->seeing) {
283 my $current = $self->current;
284 print "current = $current\n" if DEBUG;
285 @next = $self->{ graph }->successors( $current );
286 print "next.0 - @next\n" if DEBUG;
287 my %next; @next{ @next } = @next;
288 # delete $next{ $current };
289 print "next.1 - @next\n" if DEBUG;
290 @next = keys %next;
291 my @all = @next;
292 print "all = @all\n" if DEBUG;
293 delete @next{ $self->seen };
294 @next = keys %next;
295 print "next.2 - @next\n" if DEBUG;
296 if (@next) {
297 @next = $self->{ next_successor }->( $self, \%next );
298 print "next.3 - @next\n" if DEBUG;
299 for my $v (@next) {
300 $self->{ tree }->add_edge( $current, $v );
302 if (exists $self->{ pre_edge }) {
303 my $p = $self->{ pre_edge };
304 my $u = $self->current;
305 for my $v (@next) {
306 $p->( $u, $v, $self, $self->{ state });
309 last;
310 } else {
311 $self->visit_postorder;
313 return undef if $self->{ terminate };
314 $self->_callbacks($current, @all);
315 # delete $next{ $current };
317 print "next.4 - @next\n" if DEBUG;
318 unless (@next) {
319 unless ( @{ $self->{ roots } } ) {
320 my $first = $self->{ first_root };
321 if (defined $first) {
322 @next =
323 ref $first eq 'CODE' ?
324 $self->{ first_root }->( $self, $self->{ unseen } ) :
325 $first;
326 return unless @next;
329 unless (@next) {
330 return unless defined $self->{ next_root };
331 return unless @next =
332 $self->{ next_root }->( $self, $self->{ unseen } );
334 return if exists $self->{ seen }->{ $next[0] }; # Sanity check.
335 print "next.5 - @next\n" if DEBUG;
336 push @{ $self->{ roots } }, $next[0];
338 print "next.6 - @next\n" if DEBUG;
339 if (@next) {
340 $self->visit_preorder( @next );
342 return $next[0];
345 sub _order {
346 my ($self, $order) = @_;
347 1 while defined $self->next;
348 my $wantarray = wantarray;
349 if ($wantarray) {
350 @{ $self->{ $order } };
351 } elsif (defined $wantarray) {
352 shift @{ $self->{ $order } };
356 sub preorder {
357 my $self = shift;
358 $self->_order( 'preorder' );
361 sub postorder {
362 my $self = shift;
363 $self->_order( 'postorder' );
366 sub unseen {
367 my $self = shift;
368 values %{ $self->{ unseen } };
371 sub seen {
372 my $self = shift;
373 values %{ $self->{ seen } };
376 sub seeing {
377 my $self = shift;
378 @{ $self->{ order } };
381 sub roots {
382 my $self = shift;
383 @{ $self->{ roots } };
386 sub is_root {
387 my ($self, $v) = @_;
388 for my $u (@{ $self->{ roots } }) {
389 return 1 if $u eq $v;
391 return 0;
394 sub tree {
395 my $self = shift;
396 $self->{ tree };
399 sub graph {
400 my $self = shift;
401 $self->{ graph };
404 sub vertex_by_postorder {
405 my ($self, $i) = @_;
406 exists $self->{ postorder } && $self->{ postorder }->[ $i ];
409 sub postorder_by_vertex {
410 my ($self, $v) = @_;
411 exists $self->{ postordern } && $self->{ postordern }->{ $v };
414 sub postorder_vertices {
415 my ($self, $v) = @_;
416 exists $self->{ postordern } ? %{ $self->{ postordern } } : ();
419 sub vertex_by_preorder {
420 my ($self, $i) = @_;
421 exists $self->{ preorder } && $self->{ preorder }->[ $i ];
424 sub preorder_by_vertex {
425 my ($self, $v) = @_;
426 exists $self->{ preordern } && $self->{ preordern }->{ $v };
429 sub preorder_vertices {
430 my ($self, $v) = @_;
431 exists $self->{ preordern } ? %{ $self->{ preordern } } : ();
434 sub has_state {
435 my ($self, $var) = @_;
436 exists $self->{ state } && exists $self->{ state }->{ $var };
439 sub get_state {
440 my ($self, $var) = @_;
441 exists $self->{ state } ? $self->{ state }->{ $var } : undef;
444 sub set_state {
445 my ($self, $var, $val) = @_;
446 $self->{ state }->{ $var } = $val;
447 return 1;
450 sub delete_state {
451 my ($self, $var) = @_;
452 delete $self->{ state }->{ $var };
453 delete $self->{ state } unless keys %{ $self->{ state } };
454 return 1;
458 __END__
459 =pod
461 =head1 NAME
463 Graph::Traversal - traverse graphs
465 =head1 SYNOPSIS
467 Don't use Graph::Traversal directly, use Graph::Traversal::DFS
468 or Graph::Traversal::BFS instead.
470 use Graph;
471 my $g = Graph->new;
472 $g->add_edge(...);
473 use Graph::Traversal::...;
474 my $t = Graph::Traversal::...->new(%opt);
475 $t->...
477 =head1 DESCRIPTION
479 You can control how the graph is traversed by the various callback
480 parameters in the C<%opt>. In the parameters descriptions below the
481 $u and $v are vertices, and the $self is the traversal object itself.
483 =head2 Callback parameters
485 The following callback parameters are available:
487 =over 4
489 =item tree_edge
491 Called when traversing an edge that belongs to the traversal tree.
492 Called with arguments ($u, $v, $self).
494 =item non_tree_edge
496 Called when an edge is met which either leads back to the traversal tree
497 (either a C<back_edge>, a C<down_edge>, or a C<cross_edge>).
498 Called with arguments ($u, $v, $self).
500 =item pre_edge
502 Called for edges in preorder.
503 Called with arguments ($u, $v, $self).
505 =item post_edge
507 Called for edges in postorder.
508 Called with arguments ($u, $v, $self).
510 =item back_edge
512 Called for back edges.
513 Called with arguments ($u, $v, $self).
515 =item down_edge
517 Called for down edges.
518 Called with arguments ($u, $v, $self).
520 =item cross_edge
522 Called for cross edges.
523 Called with arguments ($u, $v, $self).
525 =item pre
527 =item pre_vertex
529 Called for vertices in preorder.
530 Called with arguments ($v, $self).
532 =item post
534 =item post_vertex
536 Called for vertices in postorder.
537 Called with arguments ($v, $self).
539 =item first_root
541 Called when choosing the first root (start) vertex for traversal.
542 Called with arguments ($self, $unseen) where $unseen is a hash
543 reference with the unseen vertices as keys.
545 =item next_root
547 Called when choosing the next root (after the first one) vertex for
548 traversal (useful when the graph is not connected). Called with
549 arguments ($self, $unseen) where $unseen is a hash reference with
550 the unseen vertices as keys. If you want only the first reachable
551 subgraph to be processed, set the next_root to C<undef>.
553 =item start
555 Identical to defining C<first_root> and undefining C<next_root>.
557 =item next_alphabetic
559 Set this to true if you want the vertices to be processed in
560 alphabetic order (and leave first_root/next_root undefined).
562 =item next_numeric
564 Set this to true if you want the vertices to be processed in
565 numeric order (and leave first_root/next_root undefined).
567 =item next_successor
569 Called when choosing the next vertex to visit. Called with arguments
570 ($self, $next) where $next is a hash reference with the possible
571 next vertices as keys. Use this to provide a custom ordering for
572 choosing vertices, as opposed to C<next_numeric> or C<next_alphabetic>.
574 =back
576 The parameters C<first_root> and C<next_successor> have a 'hierarchy'
577 of how they are determined: if they have been explicitly defined, use
578 that value. If not, use the value of C<next_alphabetic>, if that has
579 been defined. If not, use the value of C<next_numeric>, if that has
580 been defined. If not, the next vertex to be visited is chose randomly.
582 =head2 Methods
584 The following methods are available:
586 =over 4
588 =item unseen
590 Return the unseen vertices in random order.
592 =item seen
594 Return the seen vertices in random order.
596 =item seeing
598 Return the active fringe vertices in random order.
600 =item preorder
602 Return the vertices in preorder traversal order.
604 =item postorder
606 Return the vertices in postorder traversal order.
608 =item vertex_by_preorder
610 $v = $t->vertex_by_preorder($i)
612 Return the ith (0..$V-1) vertex by preorder.
614 =item preorder_by_vertex
616 $i = $t->preorder_by_vertex($v)
618 Return the preorder index (0..$V-1) by vertex.
620 =item vertex_by_postorder
622 $v = $t->vertex_by_postorder($i)
624 Return the ith (0..$V-1) vertex by postorder.
626 =item postorder_by_vertex
628 $i = $t->postorder_by_vertex($v)
630 Return the postorder index (0..$V-1) by vertex.
632 =item preorder_vertices
634 Return a hash with the vertices as the keys and their preorder indices
635 as the values.
637 =item postorder_vertices
639 Return a hash with the vertices as the keys and their postorder
640 indices as the values.
642 =item tree
644 Return the traversal tree as a graph.
646 =item has_state
648 $t->has_state('s')
650 Test whether the traversal has state 's' attached to it.
652 =item get_state
654 $t->get_state('s')
656 Get the state 's' attached to the traversal (C<undef> if none).
658 =item set_state
660 $t->set_state('s', $s)
662 Set the state 's' attached to the traversal.
664 =item delete_state
666 $t->delete_state('s')
668 Delete the state 's' from the traversal.
670 =back
672 =head2 Backward compatibility
674 The following parameters are for backward compatibility to Graph 0.2xx:
676 =over 4
678 =item get_next_root
680 Like C<next_root>.
682 =item successor
684 Identical to having C<tree_edge> both C<non_tree_edge> defined
685 to be the same.
687 =item unseen_successor
689 Like C<tree_edge>.
691 =item seen_successor
693 Like C<seed_edge>.
695 =back
697 =head2 Special callbacks
699 If in a callback you call the special C<terminate> method,
700 the traversal is terminated, no more vertices are traversed.
702 =head1 SEE ALSO
704 L<Graph::Traversal::DFS>, L<Graph::Traversal::BFS>
706 =head1 AUTHOR AND COPYRIGHT
708 Jarkko Hietaniemi F<jhi@iki.fi>
710 =head1 LICENSE
712 This module is licensed under the same terms as Perl itself.
714 =cut