ViewVC Help
View File | Revision Log | Show Annotations | Root Listing
root/cvsroot/COMP/SCRAM/src/Graph/Base.pm
Revision: 1.2
Committed: Fri Dec 10 13:41:40 2004 UTC (20 years, 4 months ago) by sashby
Content type: text/plain
Branch: MAIN
CVS Tags: V1_1_7, V1_1_6, V1_1_5, V1_2_0-cand1, V1_1_4, V1_1_3, V1_1_2, V1_1_0_reltag8, V1_1_0_reltag7, V1_1_0_reltag6, V1_1_1, V1_1_0_reltag5, V1_1_0_reltag4, V1_1_0_reltag3, V1_1_0_reltag2, V1_1_0_reltag1, V1_1_0_reltag, V1_0_3-p4, V1_1_0_cand3, V1_1_0_cand2, V1_1_0_cand1, HEAD_SM_071214, forV1_1_0, v103_xml_071106, V1_0_3-p3, V1_0_3-p2, V1_1_0, v110p1, V110p6, V110p5, V110p4, V110p3, before110xmlBRmerge, V110p2, V110p1, V1_0_4p1, V1_0_3-p1, V1_0_3, V1_0_2, V1_0_2_p1, v102p1, V1_0_1, V1_0_0
Branch point for: forBinLess_SCRAM, HEAD_BRANCH_SM_071214, v200branch, v103_with_xml, v103_branch
Changes since 1.1: +373 -0 lines
Log Message:
Merged V1_0 branch to HEAD

File Contents

# Content
1 package Graph::Base;
2 use strict;
3 local $^W = 1;
4 use vars qw(@ISA);
5 require Exporter;
6 @ISA = qw(Exporter);
7
8 sub new
9 {
10 my $class = shift;
11 my $G = { };
12
13 bless $G, $class;
14
15 $G->add_vertices(@_) if @_;
16
17 return $G;
18 }
19
20 sub add_vertices
21 {
22 my ($G, @v) = @_;
23 @{ $G->{ V } }{ @v } = @v;
24
25 return $G;
26 }
27
28 sub add_vertex
29 {
30 my ($G, $v) = @_;
31 return $G->add_vertices($v);
32 }
33
34 sub vertices
35 {
36 my $G = shift;
37 my @V = exists $G->{ V } ? sort values %{ $G->{ V } } : ();
38
39 return @V;
40 }
41
42 sub has_vertex
43 {
44 my ($G, $v) = @_;
45 return exists $G->{V}->{ $v };
46 }
47
48 sub vertex
49 {
50 my ($G, $v) = @_;
51
52 return $G->{ V }->{ $v };
53 }
54
55 sub directed
56 {
57 my ($G, $d) = @_;
58
59 if (defined $d)
60 {
61 if ($d)
62 {
63 my $o = $G->{ D }; # Old directedness.
64
65 $G->{ D } = $d;
66 if (not $o)
67 {
68 my @E = $G->edges;
69
70 while (my ($u, $v) = splice(@E, 0, 2))
71 {
72 $G->add_edge($v, $u);
73 }
74 }
75
76 return bless $G, 'Graph::Directed'; # Re-bless.
77 }
78 else
79 {
80 return $G->undirected(not $d);
81 }
82 }
83
84 return $G->{ D };
85 }
86
87 sub undirected
88 {
89 my ($G, $u) = @_;
90 $G->{ D } = 1 unless defined $G->{ D };
91
92 if (defined $u)
93 {
94 if ($u)
95 {
96 my $o = $G->{ D }; # Old directedness.
97
98 $G->{ D } = not $u;
99 if ($o)
100 {
101 my @E = $G->edges;
102 my %E;
103
104 while (my ($u, $v) = splice(@E, 0, 2))
105 {
106 # Throw away duplicate edges.
107 $G->delete_edge($u, $v) if exists $E{$v}->{$u};
108 $E{$u}->{$v}++;
109 }
110 }
111
112 return bless $G, 'Graph::Undirected'; # Re-bless.
113 }
114 else
115 {
116 return $G->directed(not $u);
117 }
118 }
119
120 return not $G->{ D };
121 }
122
123 sub _union_vertex_set
124 {
125 my ($G, $u, $v) = @_;
126
127 my $su = $G->vertex_set( $u );
128 my $sv = $G->vertex_set( $v );
129 my $ru = $G->{ VertexSetRank }->{ $su };
130 my $rv = $G->{ VertexSetRank }->{ $sv };
131
132 if ( $ru < $rv )
133 { # Union by rank (weight balancing).
134 $G->{ VertexSetParent }->{ $su } = $sv;
135 }
136 else
137 {
138 $G->{ VertexSetParent }->{ $sv } = $su;
139 $G->{ VertexSetRank }->{ $sv }++ if $ru == $rv;
140 }
141 }
142
143 sub vertex_set
144 {
145 my ($G, $v) = @_;
146
147 if ( exists $G->{ VertexSetParent }->{ $v } )
148 {
149 # Path compression.
150 $G->{ VertexSetParent }->{ $v } =
151 $G->vertex_set( $G->{ VertexSetParent }->{ $v } )
152 if $v ne $G->{ VertexSetParent }->{ $v };
153 }
154 else
155 {
156 $G->{ VertexSetParent }->{ $v } = $v;
157 $G->{ VertexSetRank }->{ $v } = 0;
158 }
159 return $G->{ VertexSetParent }->{ $v };
160 }
161
162 sub add_edge
163 {
164 my ($G, $u, $v) = @_;
165
166 $G->add_vertex($u);
167 $G->add_vertex($v);
168 $G->_union_vertex_set( $u, $v );
169 push @{ $G->{ Succ }->{ $u }->{ $v } }, $v;
170 push @{ $G->{ Pred }->{ $v }->{ $u } }, $u;
171 return $G;
172 }
173
174 sub _successors
175 {
176 my ($G, $v) = @_;
177 my @s =
178 defined $G->{ Succ }->{ $v } ?
179 map { @{ $G->{ Succ }->{ $v }->{ $_ } } }
180 sort keys %{ $G->{ Succ }->{ $v } } :
181 ( );
182
183 return @s;
184 }
185
186 sub _predecessors
187 {
188 my ($G, $v) = @_;
189 my @p =
190 defined $G->{ Pred }->{ $v } ?
191 map { @{ $G->{ Pred }->{ $v }->{ $_ } } }
192 sort keys %{ $G->{ Pred }->{ $v } } :
193 ( );
194
195 return @p;
196 }
197
198 sub neighbors
199 {
200 my ($G, $v) = @_;
201 my @n = ($G->_successors($v), $G->_predecessors($v));
202 return @n;
203 }
204
205 use vars '*neighbours';
206 *neighbours = \&neighbors; # Keep both sides of the Atlantic happy.
207
208 sub successors
209 {
210 my ($G, $v) = @_;
211 return $G->directed ? $G->_successors($v) : $G->neighbors($v);
212 }
213
214 sub out_edges
215 {
216 my ($G, $v) = @_;
217 return () unless $G->has_vertex($v);
218
219 my @e = $G->_edges($v, undef);
220
221 return wantarray ? @e : @e / 2;
222 }
223
224 sub edges
225 {
226 my ($G, $u, $v) = @_;
227 return () if defined $v and not $G->has_vertex($v);
228
229 my @e =
230 defined $u ?
231 ( defined $v ?
232 $G->_edges($u, $v) :
233 ($G->in_edges($u), $G->out_edges($u)) ) :
234 $G->_edges;
235 return wantarray ? @e : @e / 2;
236 }
237
238 sub delete_edge
239 {
240 my ($G, $u, $v) = @_;
241 pop @{ $G->{ Succ }->{ $u }->{ $v } };
242 pop @{ $G->{ Pred }->{ $v }->{ $u } };
243
244 delete $G->{ Succ }->{ $u }->{ $v }
245 unless @{ $G->{ Succ }->{ $u }->{ $v } };
246 delete $G->{ Pred }->{ $v }->{ $u }
247 unless @{ $G->{ Pred }->{ $v }->{ $u } };
248
249 delete $G->{ Succ }->{ $u }
250 unless keys %{ $G->{ Succ }->{ $u } };
251 delete $G->{ Pred }->{ $v }
252 unless keys %{ $G->{ Pred }->{ $v } };
253
254 return $G;
255 }
256
257 sub out_degree
258 {
259 my ($G, $v) = @_;
260 return undef unless $G->has_vertex($v);
261
262 if ($G->directed)
263 {
264 if (defined $v)
265 {
266 return scalar $G->out_edges($v);
267 }
268 else
269 {
270 my $out = 0;
271
272 foreach my $v ($G->vertices)
273 {
274 $out += $G->out_degree($v);
275 }
276 return $out;
277 }
278 }
279 else
280 {
281 return scalar $G->edges($v);
282 }
283 }
284
285 sub copy
286 {
287 my $G = shift;
288 my $C = (ref $G)->new($G->vertices);
289
290 if (my @E = $G->edges)
291 {
292 while (my ($u, $v) = splice(@E, 0, 2))
293 {
294 $C->add_edge($u, $v);
295 }
296 }
297
298 $C->directed($G->directed);
299
300 return $C;
301 }
302
303 sub edge_classify
304 {
305 my $G = shift;
306 my $unseen_successor =
307 sub {
308 my ($u, $v, $T) = @_;
309 # Freshly seen successors make for tree edges.
310 push @{ $T->{ edge_class_list } },
311 [ $u, $v, 'tree' ];
312 };
313 my $seen_successor =
314 sub {
315 my ($u, $v, $T) = @_;
316
317 my $class;
318
319 if ( $T->{ G }->directed )
320 {
321 $class = 'cross'; # Default for directed non-tree edges.
322
323 unless ( exists $T->{ vertex_finished }->{ $v } )
324 {
325 $class = 'back';
326 }
327 elsif ( $T->{ vertex_found }->{ $u } <
328 $T->{ vertex_found }->{ $v })
329 {
330 $class = 'forward';
331 }
332 }
333 else
334 {
335 # No cross nor forward edges in
336 # an undirected graph, by definition.
337 $class = 'back';
338 }
339
340 push @{ $T->{ edge_class_list } }, [ $u, $v, $class ];
341 };
342 use Graph::DFS;
343 my $d =
344 Graph::DFS->
345 new( $G,
346 unseen_successor => $unseen_successor,
347 seen_successor => $seen_successor,
348 @_);
349
350 $d->preorder;
351
352 return @{ $d->{ edge_class_list } };
353 }
354
355 sub toposort
356 {
357 my $G = shift;
358 my $d = Graph::DFS->new($G);
359 $d->postorder; # That's it.
360 }
361
362 sub largest_out_degree
363 {
364 my $G = shift;
365 my @R = map { $_->[ 0 ] } # A Schwartzian Transform.
366 sort { $b->[ 1 ] <=> $a->[ 1 ] || $a cmp $b }
367 map { [ $_, $G->out_degree($_) ] }
368 @_;
369
370 return $R[ 0 ];
371 }
372
373 1;