1 |
+ |
package Graph::Traversal; |
2 |
+ |
use strict; |
3 |
+ |
local $^W = 1; |
4 |
+ |
use Graph::Base; |
5 |
+ |
use vars qw(@ISA); |
6 |
+ |
@ISA = qw(Graph::Base); |
7 |
+ |
|
8 |
+ |
sub new |
9 |
+ |
{ |
10 |
+ |
my $class = shift; |
11 |
+ |
my $G = shift; |
12 |
+ |
my $S = { G => $G }; |
13 |
+ |
bless $S, $class; |
14 |
+ |
$S->reset(@_); |
15 |
+ |
return $S; |
16 |
+ |
} |
17 |
+ |
|
18 |
+ |
sub reset |
19 |
+ |
{ |
20 |
+ |
my $S = shift; |
21 |
+ |
my $G = $S->{ G }; |
22 |
+ |
|
23 |
+ |
@{ $S->{ pool } }{ $G->vertices } = ( ); |
24 |
+ |
$S->{ active_list } = [ ]; |
25 |
+ |
$S->{ root_list } = [ ]; |
26 |
+ |
$S->{ preorder_list } = [ ]; |
27 |
+ |
$S->{ postorder_list } = [ ]; |
28 |
+ |
$S->{ active_pool } = { }; |
29 |
+ |
$S->{ vertex_found } = { }; |
30 |
+ |
$S->{ vertex_root } = { }; |
31 |
+ |
$S->{ vertex_successors } = { }; |
32 |
+ |
$S->{ param } = { @_ }; |
33 |
+ |
$S->{ when } = 0; |
34 |
+ |
} |
35 |
+ |
|
36 |
+ |
sub _get_next_root_vertex |
37 |
+ |
{ |
38 |
+ |
my $S = shift; |
39 |
+ |
my %param = ( %{ $S->{ param } }, @_ ? %{ $_[0] } : ( )); |
40 |
+ |
my $G = $S->{ G }; |
41 |
+ |
|
42 |
+ |
if ( exists $param{ get_next_root } ) |
43 |
+ |
{ |
44 |
+ |
if ( ref $param{ get_next_root } eq 'CODE' ) |
45 |
+ |
{ |
46 |
+ |
return $param{ get_next_root }->( $S, %param ); # Dynamic. |
47 |
+ |
} |
48 |
+ |
else |
49 |
+ |
{ |
50 |
+ |
my $get_next_root = $param{ get_next_root }; # Static. |
51 |
+ |
|
52 |
+ |
# Use only once. |
53 |
+ |
delete $S->{ param }->{ get_next_root }; |
54 |
+ |
delete $_[0]->{ get_next_root } if @_; |
55 |
+ |
|
56 |
+ |
return $get_next_root; |
57 |
+ |
} |
58 |
+ |
} |
59 |
+ |
else |
60 |
+ |
{ |
61 |
+ |
return $G->largest_out_degree( keys %{ $S->{ pool } } ); |
62 |
+ |
} |
63 |
+ |
} |
64 |
+ |
|
65 |
+ |
sub _mark_vertex_found |
66 |
+ |
{ |
67 |
+ |
my ( $S, $u ) = @_; |
68 |
+ |
|
69 |
+ |
$S->{ vertex_found }->{ $u } = $S->{ when }++; |
70 |
+ |
delete $S->{ pool }->{ $u }; |
71 |
+ |
} |
72 |
+ |
|
73 |
+ |
sub _next_state |
74 |
+ |
{ |
75 |
+ |
my $S = shift; # The current state. |
76 |
+ |
my $G = $S->{ G }; # The current graph. |
77 |
+ |
my %param = ( %{ $S->{ param } }, @_); |
78 |
+ |
my ($u, $v); # The current vertex and its successor. |
79 |
+ |
my $return = 0; # Return when this becomes true. |
80 |
+ |
|
81 |
+ |
until ( $return ) |
82 |
+ |
{ |
83 |
+ |
# Initialize our search when needed. |
84 |
+ |
# (Start up a new tree.) |
85 |
+ |
unless ( @{ $S->{ active_list } } ) |
86 |
+ |
{ |
87 |
+ |
do |
88 |
+ |
{ |
89 |
+ |
$u = $S->_get_next_root_vertex(\%param); |
90 |
+ |
return wantarray ? ( ) : $u unless defined $u; |
91 |
+ |
} while exists $S->{ vertex_found }->{ $u }; |
92 |
+ |
|
93 |
+ |
# A new root vertex found. |
94 |
+ |
push @{ $S->{ active_list } }, $u; |
95 |
+ |
$S->{ active_pool }->{ $u } = 1; |
96 |
+ |
push @{ $S->{ root_list } }, $u; |
97 |
+ |
$S->{ vertex_root }->{ $u } = $#{ $S->{ root_list } }; |
98 |
+ |
} |
99 |
+ |
|
100 |
+ |
# Get the current vertex. |
101 |
+ |
$u = $param{ current }->( $S ); |
102 |
+ |
return wantarray ? () : $u unless defined $u; |
103 |
+ |
|
104 |
+ |
# Record the vertex if necessary. |
105 |
+ |
unless ( exists $S->{ vertex_found }->{ $u } ) |
106 |
+ |
{ |
107 |
+ |
$S->_mark_vertex_found( $u ); |
108 |
+ |
push @{ $S->{ preorder_list } }, $u; |
109 |
+ |
# Time to return? |
110 |
+ |
$return++ if $param{ return_next_preorder }; |
111 |
+ |
} |
112 |
+ |
|
113 |
+ |
# Initialized the list successors if necessary. |
114 |
+ |
$S->{ vertex_successors }->{ $u } = [ $G->successors( $u ) ] |
115 |
+ |
unless exists $S->{ vertex_successors }->{ $u }; |
116 |
+ |
|
117 |
+ |
# Get the next successor vertex. |
118 |
+ |
$v = shift @{ $S->{ vertex_successors }->{ $u } }; |
119 |
+ |
|
120 |
+ |
if ( defined $v ) |
121 |
+ |
{ |
122 |
+ |
# Something to do for each successor? |
123 |
+ |
$param{ successor }->( $u, $v, $S ) |
124 |
+ |
if exists $param{ successor }; |
125 |
+ |
|
126 |
+ |
unless ( exists $S->{ vertex_found }->{ $v } ) |
127 |
+ |
{ |
128 |
+ |
# An unseen successor. |
129 |
+ |
$S->_mark_vertex_found( $v ); |
130 |
+ |
push @{ $S->{ preorder_list } }, $v; |
131 |
+ |
$S->{ vertex_root }->{ $v } = $S->{ vertex_root }->{ $u }; |
132 |
+ |
push @{ $S->{ active_list } }, $v; |
133 |
+ |
$S->{ active_pool }->{ $v } = 1; |
134 |
+ |
|
135 |
+ |
# Something to for each unseen edge? |
136 |
+ |
# For multiedges, triggered only for the first edge. |
137 |
+ |
$param{ unseen_successor }->( $u, $v, $S ) |
138 |
+ |
if exists $param{ unseen_successor }; |
139 |
+ |
} |
140 |
+ |
else |
141 |
+ |
{ |
142 |
+ |
# Something to do for each seen edge? |
143 |
+ |
# For multiedges, triggered for the 2nd, etc, edges. |
144 |
+ |
$param{ seen_successor }->( $u, $v, $S ) |
145 |
+ |
if exists $param{ seen_successor }; |
146 |
+ |
} |
147 |
+ |
|
148 |
+ |
# Time to return? |
149 |
+ |
$return++ if $param{ return_next_edge }; |
150 |
+ |
|
151 |
+ |
} |
152 |
+ |
elsif ( not exists $S->{ vertex_finished }->{ $u } ) |
153 |
+ |
{ |
154 |
+ |
# Finish off with this vertex (we run out of descendants). |
155 |
+ |
$param{ finish }->( $S ); |
156 |
+ |
$S->{ vertex_finished }->{ $u } = $S->{ when }++; |
157 |
+ |
push @{ $S->{ postorder_list } }, $u; |
158 |
+ |
delete $S->{ active_pool }->{ $u }; |
159 |
+ |
|
160 |
+ |
# Time to return? |
161 |
+ |
$return++ if $param{ return_next_postorder }; |
162 |
+ |
} |
163 |
+ |
} |
164 |
+ |
|
165 |
+ |
# Return an edge if so asked. |
166 |
+ |
return ( $u, $v ) if $param{ return_next_edge }; |
167 |
+ |
|
168 |
+ |
# Return a vertex. |
169 |
+ |
return $u; |
170 |
+ |
} |
171 |
+ |
|
172 |
+ |
sub next_preorder |
173 |
+ |
{ |
174 |
+ |
my $S = shift; |
175 |
+ |
$S->_next_state( return_next_preorder => 1, @_ ); |
176 |
+ |
} |
177 |
+ |
|
178 |
+ |
sub next_postorder |
179 |
+ |
{ |
180 |
+ |
my $S = shift; |
181 |
+ |
$S->_next_state( return_next_postorder => 1, @_ ); |
182 |
+ |
} |
183 |
+ |
|
184 |
+ |
sub next_edge |
185 |
+ |
{ |
186 |
+ |
my $S = shift; |
187 |
+ |
$S->_next_state( return_next_edge => 1, @_ ); |
188 |
+ |
} |
189 |
+ |
|
190 |
+ |
sub preorder |
191 |
+ |
{ |
192 |
+ |
my $S = shift; |
193 |
+ |
1 while defined $S->next_preorder; # Process entire graph. |
194 |
+ |
return @{ $S->{ preorder_list } }; |
195 |
+ |
} |
196 |
+ |
|
197 |
+ |
sub postorder |
198 |
+ |
{ |
199 |
+ |
my $S = shift; |
200 |
+ |
1 while defined $S->next_postorder; # Process entire graph. |
201 |
+ |
return @{ $S->{ postorder_list } }; |
202 |
+ |
} |
203 |
+ |
|
204 |
+ |
sub edges |
205 |
+ |
{ |
206 |
+ |
my $S = shift; |
207 |
+ |
my (@E, $u, $v); |
208 |
+ |
push @E, $u, $v while ($u, $v) = $S->next_edge; |
209 |
+ |
return @E; |
210 |
+ |
} |
211 |
+ |
|
212 |
+ |
sub roots |
213 |
+ |
{ |
214 |
+ |
my $S = shift; |
215 |
+ |
|
216 |
+ |
$S->preorder |
217 |
+ |
unless exists $S->{ preorder_list } and |
218 |
+ |
@{ $S->{ preorder_list } } == $S->{ G }->vertices; |
219 |
+ |
return @{ $S->{ root_list } }; |
220 |
+ |
} |
221 |
+ |
|
222 |
+ |
sub vertex_roots |
223 |
+ |
{ |
224 |
+ |
my $S = shift; |
225 |
+ |
my $G = $S->{ G }; |
226 |
+ |
|
227 |
+ |
$S->preorder |
228 |
+ |
unless exists $S->{ preorder_list } and |
229 |
+ |
@{ $S->{ preorder_list } } == $G->vertices; |
230 |
+ |
return |
231 |
+ |
map { ( $_, $S->{ vertex_root }->{ $_ } ) } $G->vertices; |
232 |
+ |
} |
233 |
+ |
|
234 |
+ |
sub DELETE |
235 |
+ |
{ |
236 |
+ |
my $S = shift; |
237 |
+ |
delete $S->{ G }; # Release the graph. |
238 |
+ |
} |
239 |
+ |
|
240 |
+ |
1; |