1package Graph;
2
3use strict;
4
5BEGIN {
6    if (0) { # SET THIS TO ZERO FOR TESTING AND RELEASES!
7	$SIG{__DIE__ } = \&__carp_confess;
8	$SIG{__WARN__} = \&__carp_confess;
9    }
10    sub __carp_confess { require Carp; Carp::confess(@_) }
11}
12
13use Graph::AdjacencyMap qw(:flags :fields);
14
15use vars qw($VERSION);
16
17$VERSION = '0.94';
18
19require 5.006; # Weak references are absolutely required.
20
21my $can_deep_copy_Storable =
22    eval 'require Storable; require B::Deparse; $Storable::VERSION >= 2.05 && $B::Deparse::VERSION >= 0.61' && !$@;
23
24sub _can_deep_copy_Storable () {
25    return $can_deep_copy_Storable;
26}
27
28use Graph::AdjacencyMap::Heavy;
29use Graph::AdjacencyMap::Light;
30use Graph::AdjacencyMap::Vertex;
31use Graph::UnionFind;
32use Graph::TransitiveClosure;
33use Graph::Traversal::DFS;
34use Graph::MSTHeapElem;
35use Graph::SPTHeapElem;
36use Graph::Undirected;
37
38use Heap071::Fibonacci;
39use List::Util qw(shuffle first);
40use Scalar::Util qw(weaken);
41
42use Safe;  # For deep_copy().
43
44sub _F () { 0 } # Flags.
45sub _G () { 1 } # Generation.
46sub _V () { 2 } # Vertices.
47sub _E () { 3 } # Edges.
48sub _A () { 4 } # Attributes.
49sub _U () { 5 } # Union-Find.
50sub _S () { 6 } # Successors.
51sub _P () { 7 } # Predecessors.
52
53my $Inf;
54
55BEGIN {
56    local $SIG{FPE};
57    eval { $Inf = exp(999) } ||
58	eval { $Inf = 9**9**9 } ||
59	    eval { $Inf = 1e+999 } ||
60		{ $Inf = 1e+99 };  # Close enough for most practical purposes.
61}
62
63sub Infinity () { $Inf }
64
65# Graphs are blessed array references.
66# - The first element contains the flags.
67# - The second element is the vertices.
68# - The third element is the edges.
69# - The fourth element is the attributes of the whole graph.
70# The defined flags for Graph are:
71# - _COMPAT02 for user API compatibility with the Graph 0.20xxx series.
72# The vertices are contained in either a "simplemap"
73# (if no hypervertices) or in a "map".
74# The edges are always in a "map".
75# The defined flags for maps are:
76# - _COUNT for countedness: more than one instance
77# - _HYPER for hyperness: a different number of "coordinates" than usual;
78#   expects one for vertices and two for edges
79# - _UNORD for unordered coordinates (a set): if _UNORD is not set
80#   the coordinates are assumed to be meaningfully ordered
81# - _UNIQ for unique coordinates: if set duplicates are removed,
82#   if not, duplicates are assumed to meaningful
83# - _UNORDUNIQ: just a union of _UNORD and UNIQ
84# Vertices are assumed to be _UNORDUNIQ; edges assume none of these flags.
85
86use Graph::Attribute array => _A, map => 'graph';
87
88sub _COMPAT02 () { 0x00000001 }
89
90sub stringify {
91    my $g = shift;
92    my $u = $g->is_undirected;
93    my $e = $u ? '=' : '-';
94    my @e =
95	map {
96	    my @v =
97		map {
98		    ref($_) eq 'ARRAY' ? "[" . join(" ", @$_) . "]" : "$_"
99		}
100	    @$_;
101	    join($e, $u ? sort { "$a" cmp "$b" } @v : @v) } $g->edges05;
102    my @s = sort { "$a" cmp "$b" } @e;
103    push @s, sort { "$a" cmp "$b" } $g->isolated_vertices;
104    join(",", @s);
105}
106
107sub eq {
108    "$_[0]" eq "$_[1]"
109}
110
111sub ne {
112    "$_[0]" ne "$_[1]"
113}
114
115use overload
116    '""' => \&stringify,
117    'eq' => \&eq,
118    'ne' => \≠
119
120sub _opt {
121    my ($opt, $flags, %flags) = @_;
122    while (my ($flag, $FLAG) = each %flags) {
123	if (exists $opt->{$flag}) {
124	    $$flags |= $FLAG if $opt->{$flag};
125	    delete $opt->{$flag};
126	}
127	if (exists $opt->{my $non = "non$flag"}) {
128	    $$flags &= ~$FLAG if $opt->{$non};
129	    delete $opt->{$non};
130	}
131    }
132}
133
134sub is_compat02 {
135    my ($g) = @_;
136    $g->[ _F ] & _COMPAT02;
137}
138
139*compat02 = \&is_compat02;
140
141sub has_union_find {
142    my ($g) = @_;
143    ($g->[ _F ] & _UNIONFIND) && defined $g->[ _U ];
144}
145
146sub _get_union_find {
147    my ($g) = @_;
148    $g->[ _U ];
149}
150
151sub _opt_get {
152    my ($opt, $key, $var) = @_;
153    if (exists $opt->{$key}) {
154	$$var = $opt->{$key};
155	delete $opt->{$key};
156    }
157}
158
159sub _opt_unknown {
160    my ($opt) = @_;
161    if (my @opt = keys %$opt) {
162	my $f = (caller(1))[3];
163	require Carp;
164	Carp::confess(sprintf
165		      "$f: Unknown option%s: @{[map { qq['$_'] } sort @opt]}",
166		      @opt > 1 ? 's' : '');
167    }
168}
169
170sub new {
171    my $class = shift;
172    my $gflags = 0;
173    my $vflags;
174    my $eflags;
175    my %opt = _get_options( \@_ );
176
177    if (ref $class && $class->isa('Graph')) {
178	no strict 'refs';
179        for my $c (qw(undirected refvertexed compat02
180                      hypervertexed countvertexed multivertexed
181                      hyperedged countedged multiedged omniedged
182		      __stringified)) {
183#            $opt{$c}++ if $class->$c; # 5.00504-incompatible
184	    if (&{"Graph::$c"}($class)) { $opt{$c}++ }
185        }
186#        $opt{unionfind}++ if $class->has_union_find; # 5.00504-incompatible
187	if (&{"Graph::has_union_find"}($class)) { $opt{unionfind}++ }
188    }
189
190    _opt_get(\%opt, undirected   => \$opt{omniedged});
191    _opt_get(\%opt, omnidirected => \$opt{omniedged});
192
193    if (exists $opt{directed}) {
194	$opt{omniedged} = !$opt{directed};
195	delete $opt{directed};
196    }
197
198    my $vnonomni =
199	$opt{nonomnivertexed} ||
200	    (exists $opt{omnivertexed} && !$opt{omnivertexed});
201    my $vnonuniq =
202	$opt{nonuniqvertexed} ||
203	    (exists $opt{uniqvertexed} && !$opt{uniqvertexed});
204
205    _opt(\%opt, \$vflags,
206	 countvertexed	=> _COUNT,
207	 multivertexed	=> _MULTI,
208	 hypervertexed	=> _HYPER,
209	 omnivertexed	=> _UNORD,
210	 uniqvertexed	=> _UNIQ,
211	 refvertexed	=> _REF,
212	 refvertexed_stringified => _REFSTR ,
213	 __stringified => _STR,
214	);
215
216    _opt(\%opt, \$eflags,
217	 countedged	=> _COUNT,
218	 multiedged	=> _MULTI,
219	 hyperedged	=> _HYPER,
220	 omniedged	=> _UNORD,
221	 uniqedged	=> _UNIQ,
222	);
223
224    _opt(\%opt, \$gflags,
225	 compat02      => _COMPAT02,
226	 unionfind     => _UNIONFIND,
227	);
228
229    if (exists $opt{vertices_unsorted}) { # Graph 0.20103 compat.
230	my $unsorted = $opt{vertices_unsorted};
231	delete $opt{vertices_unsorted};
232	require Carp;
233	Carp::confess("Graph: vertices_unsorted must be true")
234	    unless $unsorted;
235    }
236
237    my @V;
238    if ($opt{vertices}) {
239	require Carp;
240	Carp::confess("Graph: vertices should be an array ref")
241	    unless ref $opt{vertices} eq 'ARRAY';
242	@V = @{ $opt{vertices} };
243	delete $opt{vertices};
244    }
245
246    my @E;
247    if ($opt{edges}) {
248	unless (ref $opt{edges} eq 'ARRAY') {
249	    require Carp;
250	    Carp::confess("Graph: edges should be an array ref of array refs");
251	}
252	@E = @{ $opt{edges} };
253	delete $opt{edges};
254    }
255
256    _opt_unknown(\%opt);
257
258    my $uflags;
259    if (defined $vflags) {
260	$uflags = $vflags;
261	$uflags |= _UNORD unless $vnonomni;
262	$uflags |= _UNIQ  unless $vnonuniq;
263    } else {
264	$uflags = _UNORDUNIQ;
265	$vflags = 0;
266    }
267
268    if (!($vflags & _HYPER) && ($vflags & _UNORDUNIQ)) {
269	my @but;
270	push @but, 'unordered' if ($vflags & _UNORD);
271	push @but, 'unique'    if ($vflags & _UNIQ);
272	require Carp;
273	Carp::confess(sprintf "Graph: not hypervertexed but %s",
274		      join(' and ', @but));
275    }
276
277    unless (defined $eflags) {
278	$eflags = ($gflags & _COMPAT02) ? _COUNT : 0;
279    }
280
281    if (!($vflags & _HYPER) && ($vflags & _UNIQ)) {
282	require Carp;
283	Carp::confess("Graph: not hypervertexed but uniqvertexed");
284    }
285
286    if (($vflags & _COUNT) && ($vflags & _MULTI)) {
287	require Carp;
288	Carp::confess("Graph: both countvertexed and multivertexed");
289    }
290
291    if (($eflags & _COUNT) && ($eflags & _MULTI)) {
292	require Carp;
293	Carp::confess("Graph: both countedged and multiedged");
294    }
295
296    my $g = bless [ ], ref $class || $class;
297
298    $g->[ _F ] = $gflags;
299    $g->[ _G ] = 0;
300    $g->[ _V ] = ($vflags & (_HYPER | _MULTI)) ?
301	Graph::AdjacencyMap::Heavy->_new($uflags, 1) :
302	    (($vflags & ~_UNORD) ?
303	     Graph::AdjacencyMap::Vertex->_new($uflags, 1) :
304	     Graph::AdjacencyMap::Light->_new($g, $uflags, 1));
305    $g->[ _E ] = (($vflags & _HYPER) || ($eflags & ~_UNORD)) ?
306	Graph::AdjacencyMap::Heavy->_new($eflags, 2) :
307	    Graph::AdjacencyMap::Light->_new($g, $eflags, 2);
308
309    $g->add_vertices(@V) if @V;
310
311    if (@E) {
312	for my $e (@E) {
313	    unless (ref $e eq 'ARRAY') {
314		require Carp;
315		Carp::confess("Graph: edges should be array refs");
316	    }
317	    $g->add_edge(@$e);
318	}
319    }
320
321    if (($gflags & _UNIONFIND)) {
322	$g->[ _U ] = Graph::UnionFind->new;
323    }
324
325    return $g;
326}
327
328sub countvertexed { $_[0]->[ _V ]->_is_COUNT }
329sub multivertexed { $_[0]->[ _V ]->_is_MULTI }
330sub hypervertexed { $_[0]->[ _V ]->_is_HYPER }
331sub omnivertexed  { $_[0]->[ _V ]->_is_UNORD }
332sub uniqvertexed  { $_[0]->[ _V ]->_is_UNIQ  }
333sub refvertexed   { $_[0]->[ _V ]->_is_REF   }
334sub refvertexed_stringified { $_[0]->[ _V ]->_is_REFSTR }
335sub __stringified { $_[0]->[ _V ]->_is_STR   }
336
337sub countedged    { $_[0]->[ _E ]->_is_COUNT }
338sub multiedged    { $_[0]->[ _E ]->_is_MULTI }
339sub hyperedged    { $_[0]->[ _E ]->_is_HYPER }
340sub omniedged     { $_[0]->[ _E ]->_is_UNORD }
341sub uniqedged     { $_[0]->[ _E ]->_is_UNIQ  }
342
343*undirected   = \&omniedged;
344*omnidirected = \&omniedged;
345sub directed { ! $_[0]->[ _E ]->_is_UNORD }
346
347*is_directed      = \&directed;
348*is_undirected    = \&undirected;
349
350*is_countvertexed = \&countvertexed;
351*is_multivertexed = \&multivertexed;
352*is_hypervertexed = \&hypervertexed;
353*is_omnidirected  = \&omnidirected;
354*is_uniqvertexed  = \&uniqvertexed;
355*is_refvertexed   = \&refvertexed;
356*is_refvertexed_stringified = \&refvertexed_stringified;
357
358*is_countedged    = \&countedged;
359*is_multiedged    = \&multiedged;
360*is_hyperedged    = \&hyperedged;
361*is_omniedged     = \&omniedged;
362*is_uniqedged     = \&uniqedged;
363
364sub _union_find_add_vertex {
365    my ($g, $v) = @_;
366    my $UF = $g->[ _U ];
367    $UF->add( $g->[ _V ]->_get_path_id( $v ) );
368}
369
370sub add_vertex {
371    my $g = shift;
372    if ($g->is_multivertexed) {
373	return $g->add_vertex_by_id(@_, _GEN_ID);
374    }
375    my @r;
376    if (@_ > 1) {
377	unless ($g->is_countvertexed || $g->is_hypervertexed) {
378	    require Carp;
379	    Carp::croak("Graph::add_vertex: use add_vertices for more than one vertex or use hypervertexed");
380	}
381	for my $v ( @_ ) {
382	    if (defined $v) {
383		$g->[ _V ]->set_path( $v ) unless $g->has_vertex( $v );
384	    } else {
385		require Carp;
386		Carp::croak("Graph::add_vertex: undef vertex");
387	    }
388	}
389    }
390    for my $v ( @_ ) {
391	unless (defined $v) {
392	    require Carp;
393	    Carp::croak("Graph::add_vertex: undef vertex");
394	}
395    }
396    $g->[ _V ]->set_path( @_ );
397    $g->[ _G ]++;
398    $g->_union_find_add_vertex( @_ ) if $g->has_union_find;
399    return $g;
400}
401
402sub has_vertex {
403    my $g = shift;
404    my $V = $g->[ _V ];
405    return exists $V->[ _s ]->{ $_[0] } if ($V->[ _f ] & _LIGHT);
406    $V->has_path( @_ );
407}
408
409sub vertices05 {
410    my $g = shift;
411    my @v = $g->[ _V ]->paths( @_ );
412    if (wantarray) {
413	return $g->[ _V ]->_is_HYPER ?
414	    @v : map { ref $_ eq 'ARRAY' ? @$_ : $_ } @v;
415    } else {
416	return scalar @v;
417    }
418}
419
420sub vertices {
421    my $g = shift;
422    my @v = $g->vertices05;
423    if ($g->is_compat02) {
424        wantarray ? sort @v : scalar @v;
425    } else {
426	if ($g->is_multivertexed || $g->is_countvertexed) {
427	    if (wantarray) {
428		my @V;
429		for my $v ( @v ) {
430		    push @V, ($v) x $g->get_vertex_count($v);
431		}
432		return @V;
433	    } else {
434		my $V = 0;
435		for my $v ( @v ) {
436		    $V += $g->get_vertex_count($v);
437		}
438		return $V;
439	    }
440	} else {
441	    return @v;
442	}
443    }
444}
445
446*vertices_unsorted = \&vertices_unsorted; # Graph 0.20103 compat.
447
448sub unique_vertices {
449    my $g = shift;
450    my @v = $g->vertices05;
451    if ($g->is_compat02) {
452        wantarray ? sort @v : scalar @v;
453    } else {
454	return @v;
455    }
456}
457
458sub has_vertices {
459    my $g = shift;
460    scalar $g->[ _V ]->has_paths( @_ );
461}
462
463sub _add_edge {
464    my $g = shift;
465    my $V = $g->[ _V ];
466    my @e;
467    if (($V->[ _f ]) & _LIGHT) {
468	for my $v ( @_ ) {
469	    $g->add_vertex( $v ) unless exists $V->[ _s ]->{ $v };
470	    push @e, $V->[ _s ]->{ $v };
471	}
472    } else {
473	my $h = $g->[ _V ]->_is_HYPER;
474	for my $v ( @_ ) {
475	    my @v = ref $v eq 'ARRAY' && $h ? @$v : $v;
476	    $g->add_vertex( @v ) unless $V->has_path( @v );
477	    push @e, $V->_get_path_id( @v );
478	}
479    }
480    return @e;
481}
482
483sub _union_find_add_edge {
484    my ($g, $u, $v) = @_;
485    $g->[ _U ]->union($u, $v);
486}
487
488sub add_edge {
489    my $g = shift;
490    if ($g->is_multiedged) {
491	unless (@_ == 2 || $g->is_hyperedged) {
492	    require Carp;
493	    Carp::croak("Graph::add_edge: use add_edges for more than one edge");
494	}
495	return $g->add_edge_by_id(@_, _GEN_ID);
496    }
497    unless (@_ == 2) {
498	unless ($g->is_hyperedged) {
499	    require Carp;
500	    Carp::croak("Graph::add_edge: graph is not hyperedged");
501	}
502    }
503    my @e = $g->_add_edge( @_ );
504    $g->[ _E ]->set_path( @e );
505    $g->[ _G ]++;
506    $g->_union_find_add_edge( @e ) if $g->has_union_find;
507    return $g;
508}
509
510sub _vertex_ids {
511    my $g = shift;
512    my $V = $g->[ _V ];
513    my @e;
514    if (($V->[ _f ] & _LIGHT)) {
515	for my $v ( @_ ) {
516	    return () unless exists $V->[ _s ]->{ $v };
517	    push @e, $V->[ _s ]->{ $v };
518	}
519    } else {
520	my $h = $g->[ _V ]->_is_HYPER;
521	for my $v ( @_ ) {
522	    my @v = ref $v eq 'ARRAY' && $h ? @$v : $v;
523	    return () unless $V->has_path( @v );
524	    push @e, $V->_get_path_id( @v );
525	}
526    }
527    return @e;
528}
529
530sub has_edge {
531    my $g = shift;
532    my $E = $g->[ _E ];
533    my $V = $g->[ _V ];
534    my @i;
535    if (($V->[ _f ] & _LIGHT) && @_ == 2) {
536	return 0 unless
537	    exists $V->[ _s ]->{ $_[0] } &&
538	    exists $V->[ _s ]->{ $_[1] };
539	@i = @{ $V->[ _s ] }{ @_[ 0, 1 ] };
540    } else {
541	@i = $g->_vertex_ids( @_ );
542	return 0 if @i == 0 && @_;
543    }
544    my $f = $E->[ _f ];
545    if ($E->[ _a ] == 2 && @i == 2 && !($f & (_HYPER|_REF|_UNIQ))) { # Fast path.
546	@i = sort @i if ($f & _UNORD);
547	return exists $E->[ _s ]->{ $i[0] } &&
548	       exists $E->[ _s ]->{ $i[0] }->{ $i[1] } ? 1 : 0;
549    } else {
550	return defined $E->_get_path_id( @i ) ? 1 : 0;
551    }
552}
553
554sub edges05 {
555    my $g = shift;
556    my $V = $g->[ _V ];
557    my @e = $g->[ _E ]->paths( @_ );
558    wantarray ?
559	map { [ map { my @v = $V->_get_id_path($_);
560		      @v == 1 ? $v[0] : [ @v ] }
561		@$_ ] }
562            @e : @e;
563}
564
565sub edges02 {
566    my $g = shift;
567    if (@_ && defined $_[0]) {
568	unless (defined $_[1]) {
569	    my @e = $g->edges_at($_[0]);
570	    wantarray ?
571		map { @$_ }
572                    sort { $a->[0] cmp $b->[0] || $a->[1] cmp $b->[1] } @e
573                : @e;
574	} else {
575	    die "edges02: unimplemented option";
576	}
577    } else {
578	my @e = map { ($_) x $g->get_edge_count(@$_) } $g->edges05( @_ );
579	wantarray ?
580          map { @$_ }
581              sort { $a->[0] cmp $b->[0] || $a->[1] cmp $b->[1] } @e
582          : @e;
583    }
584}
585
586sub unique_edges {
587    my $g = shift;
588    ($g->is_compat02) ? $g->edges02( @_ ) : $g->edges05( @_ );
589}
590
591sub edges {
592    my $g = shift;
593    if ($g->is_compat02) {
594	return $g->edges02( @_ );
595    } else {
596	if ($g->is_multiedged || $g->is_countedged) {
597	    if (wantarray) {
598		my @E;
599		for my $e ( $g->edges05 ) {
600		    push @E, ($e) x $g->get_edge_count(@$e);
601		}
602		return @E;
603	    } else {
604		my $E = 0;
605		for my $e ( $g->edges05 ) {
606		    $E += $g->get_edge_count(@$e);
607		}
608		return $E;
609	    }
610	} else {
611	    return $g->edges05;
612	}
613    }
614}
615
616sub has_edges {
617    my $g = shift;
618    scalar $g->[ _E ]->has_paths( @_ );
619}
620
621###
622# by_id
623#
624
625sub add_vertex_by_id {
626    my $g = shift;
627    $g->expect_multivertexed;
628    $g->[ _V ]->set_path_by_multi_id( @_ );
629    $g->[ _G ]++;
630    $g->_union_find_add_vertex( @_ ) if $g->has_union_find;
631    return $g;
632}
633
634sub add_vertex_get_id {
635    my $g = shift;
636    $g->expect_multivertexed;
637    my $id = $g->[ _V ]->set_path_by_multi_id( @_, _GEN_ID );
638    $g->[ _G ]++;
639    $g->_union_find_add_vertex( @_ ) if $g->has_union_find;
640    return $id;
641}
642
643sub has_vertex_by_id {
644    my $g = shift;
645    $g->expect_multivertexed;
646    $g->[ _V ]->has_path_by_multi_id( @_ );
647}
648
649sub delete_vertex_by_id {
650    my $g = shift;
651    $g->expect_multivertexed;
652    $g->expect_non_unionfind;
653    my $V = $g->[ _V ];
654    return unless $V->has_path_by_multi_id( @_ );
655    # TODO: what to about the edges at this vertex?
656    # If the multiness of this vertex goes to zero, delete the edges?
657    $V->del_path_by_multi_id( @_ );
658    $g->[ _G ]++;
659    return $g;
660}
661
662sub get_multivertex_ids {
663    my $g = shift;
664    $g->expect_multivertexed;
665    $g->[ _V ]->get_multi_ids( @_ );
666}
667
668sub add_edge_by_id {
669    my $g = shift;
670    $g->expect_multiedged;
671    my $id = pop;
672    my @e = $g->_add_edge( @_ );
673    $g->[ _E ]->set_path_by_multi_id( @e, $id );
674    $g->[ _G ]++;
675    $g->_union_find_add_edge( @e ) if $g->has_union_find;
676    return $g;
677}
678
679sub add_edge_get_id {
680    my $g = shift;
681    $g->expect_multiedged;
682    my @i = $g->_add_edge( @_ );
683    my $id = $g->[ _E ]->set_path_by_multi_id( @i, _GEN_ID );
684    $g->_union_find_add_edge( @i ) if $g->has_union_find;
685    $g->[ _G ]++;
686    return $id;
687}
688
689sub has_edge_by_id {
690    my $g = shift;
691    $g->expect_multiedged;
692    my $id = pop;
693    my @i = $g->_vertex_ids( @_ );
694    return 0 if @i == 0 && @_;
695    $g->[ _E ]->has_path_by_multi_id( @i, $id );
696}
697
698sub delete_edge_by_id {
699    my $g = shift;
700    $g->expect_multiedged;
701    $g->expect_non_unionfind;
702    my $V = $g->[ _E ];
703    my $id = pop;
704    my @i = $g->_vertex_ids( @_ );
705    return unless $V->has_path_by_multi_id( @i, $id );
706    $V->del_path_by_multi_id( @i, $id );
707    $g->[ _G ]++;
708    return $g;
709}
710
711sub get_multiedge_ids {
712    my $g = shift;
713    $g->expect_multiedged;
714    my @id = $g->_vertex_ids( @_ );
715    return unless @id;
716    $g->[ _E ]->get_multi_ids( @id );
717}
718
719###
720# Neighbourhood.
721#
722
723sub vertices_at {
724    my $g = shift;
725    my $V = $g->[ _V ];
726    return @_ unless ($V->[ _f ] & _HYPER);
727    my %v;
728    my @i;
729    for my $v ( @_ ) {
730	my $i = $V->_get_path_id( $v );
731	return unless defined $i;
732	push @i, ( $v{ $v } = $i );
733    }
734    my $Vi = $V->_ids;
735    my @v;
736    while (my ($i, $v) = each %{ $Vi }) {
737	my %i;
738	my $h = $V->[_f ] & _HYPER;
739	@i{ @i } = @i if @i; # @todo: nonuniq hyper vertices?
740	for my $u (ref $v eq 'ARRAY' && $h ? @$v : $v) {
741	    my $j = exists $v{ $u } ? $v{ $u } : ( $v{ $u } = $i );
742	    if (defined $j && exists $i{ $j }) {
743		delete $i{ $j };
744		unless (keys %i) {
745		    push @v, $v;
746		    last;
747		}
748	    }
749	}
750    }
751    return @v;
752}
753
754sub _edges_at {
755    my $g = shift;
756    my $V = $g->[ _V ];
757    my $E = $g->[ _E ];
758    my @e;
759    my $en = 0;
760    my %ev;
761    my $h = $V->[_f ] & _HYPER;
762    for my $v ( $h ? $g->vertices_at( @_ ) : @_ ) {
763	my $vi = $V->_get_path_id( ref $v eq 'ARRAY' && $h ? @$v : $v );
764	next unless defined $vi;
765	my $Ei = $E->_ids;
766	while (my ($ei, $ev) = each %{ $Ei }) {
767	    if (wantarray) {
768		for my $j (@$ev) {
769		    push @e, [ $ei, $ev ]
770			if $j == $vi && !$ev{$ei}++;
771		}
772	    } else {
773		for my $j (@$ev) {
774		    $en++ if $j == $vi;
775		}
776	    }
777	}
778    }
779    return wantarray ? @e : $en;
780}
781
782sub _edges {
783    my $g = shift;
784    my $n = pop;
785    my $i = $n == _S ? 0 : -1;  # _edges_from() or _edges_to()
786    my $V = $g->[ _V ];
787    my $E = $g->[ _E ];
788    my $N = $g->[ $n ];
789    my $h = $V->[ _f ] & _HYPER;
790    unless (defined $N && $N->[ 0 ] == $g->[ _G ]) {
791	$g->[ $n ]->[ 1 ] = { };
792	$N = $g->[ $n ];
793	my $u = $E->[ _f ] & _UNORD;
794	my $Ei = $E->_ids;
795	while (my ($ei, $ev) = each %{ $Ei }) {
796	    next unless @$ev;
797	    my $e = [ $ei, $ev ];
798	    if ($u) {
799		push @{ $N->[ 1 ]->{ $ev->[ 0] } }, $e;
800		push @{ $N->[ 1 ]->{ $ev->[-1] } }, $e;
801	    } else {
802		my $e = [ $ei, $ev ];
803		push @{ $N->[ 1 ]->{ $ev->[$i] } }, $e;
804	    }
805	}
806	$N->[ 0 ] = $g->[ _G ];
807    }
808    my @e;
809    my @at = $h ? $g->vertices_at( @_ ) : @_;
810    my %at; @at{@at} = ();
811    for my $v ( @at ) {
812	my $vi = $V->_get_path_id( ref $v eq 'ARRAY' && $h ? @$v : $v );
813	next unless defined $vi && exists $N->[ 1 ]->{ $vi };
814	push @e, @{ $N->[ 1 ]->{ $vi } };
815    }
816    if (wantarray && $g->is_undirected) {
817	my @i = map { $V->_get_path_id( $_ ) } @_;
818	for my $e ( @e ) {
819	    unless ( $e->[ 1 ]->[ $i ] == $i[ $i ] ) {
820		$e = [ $e->[ 0 ], [ reverse @{ $e->[ 1 ] } ] ];
821	    }
822	}
823    }
824    return @e;
825}
826
827sub _edges_from {
828    push @_, _S;
829    goto &_edges;
830}
831
832sub _edges_to {
833    push @_, _P;
834    goto &_edges;
835}
836
837sub _edges_id_path {
838    my $g = shift;
839    my $V  = $g->[ _V ];
840    [ map { my @v = $V->_get_id_path($_);
841	    @v == 1 ? $v[0] : [ @v ] }
842          @{ $_[0]->[1] } ];
843}
844
845sub edges_at {
846    my $g = shift;
847    map { $g->_edges_id_path($_ ) } $g->_edges_at( @_ );
848}
849
850sub edges_from {
851    my $g = shift;
852    map { $g->_edges_id_path($_ ) } $g->_edges_from( @_ );
853}
854
855sub edges_to {
856    my $g = shift;
857    map { $g->_edges_id_path($_ ) } $g->_edges_to( @_ );
858}
859
860sub successors {
861    my $g = shift;
862    my $E = $g->[ _E ];
863    ($E->[ _f ] & _LIGHT) ?
864	$E->_successors($g, @_) :
865	Graph::AdjacencyMap::_successors($E, $g, @_);
866}
867
868sub predecessors {
869    my $g = shift;
870    my $E = $g->[ _E ];
871    ($E->[ _f ] & _LIGHT) ?
872	$E->_predecessors($g, @_) :
873	Graph::AdjacencyMap::_predecessors($E, $g, @_);
874}
875
876sub _all_successors {
877    my $g = shift;
878    my @init = @_;
879    my %todo;
880    @todo{@init} = @init;
881    my %seen;
882    my %init = %todo;
883    my %self;
884    while (keys %todo) {
885      my @todo = values %todo;
886      for my $t (@todo) {
887	$seen{$t} = delete $todo{$t};
888	for my $s ($g->successors($t)) {
889	  $self{$s} = $s if exists $init{$s};
890	  $todo{$s} = $s unless exists $seen{$s};
891	}
892      }
893    }
894    for my $v (@init) {
895      delete $seen{$v} unless $g->has_edge($v, $v) || $self{$v};
896    }
897    return values %seen;
898}
899
900sub all_successors {
901    my $g = shift;
902    $g->expect_directed;
903    return $g->_all_successors(@_);
904}
905
906sub _all_predecessors {
907    my $g = shift;
908    my @init = @_;
909    my %todo;
910    @todo{@init} = @init;
911    my %seen;
912    my %init = %todo;
913    my %self;
914    while (keys %todo) {
915      my @todo = values %todo;
916      for my $t (@todo) {
917	$seen{$t} = delete $todo{$t};
918	for my $p ($g->predecessors($t)) {
919	  $self{$p} = $p if exists $init{$p};
920	  $todo{$p} = $p unless exists $seen{$p};
921	}
922      }
923    }
924    for my $v (@init) {
925      delete $seen{$v} unless $g->has_edge($v, $v) || $self{$v};
926    }
927    return values %seen;
928}
929
930sub all_predecessors {
931    my $g = shift;
932    $g->expect_directed;
933    return $g->_all_predecessors(@_);
934}
935
936sub neighbours {
937    my $g = shift;
938    my $V  = $g->[ _V ];
939    my @s = map { my @v = @{ $_->[ 1 ] }; shift @v; @v } $g->_edges_from( @_ );
940    my @p = map { my @v = @{ $_->[ 1 ] }; pop   @v; @v } $g->_edges_to  ( @_ );
941    my %n;
942    @n{ @s } = @s;
943    @n{ @p } = @p;
944    map { $V->_get_id_path($_) } keys %n;
945}
946
947*neighbors = \&neighbours;
948
949sub all_neighbours {
950    my $g = shift;
951    my @init = @_;
952    my @v = @init;
953    my %n;
954    my $o = 0;
955    while (1) {
956      my @p = $g->_all_predecessors(@v);
957      my @s = $g->_all_successors(@v);
958      @n{@p} = @p;
959      @n{@s} = @s;
960      @v = values %n;
961      last if @v == $o;  # Leave if no growth.
962      $o = @v;
963    }
964    for my $v (@init) {
965      delete $n{$v} unless $g->has_edge($v, $v);
966    }
967    return values %n;
968}
969
970*all_neighbors = \&all_neighbours;
971
972sub all_reachable {
973    my $g = shift;
974    $g->directed ? $g->all_successors(@_) : $g->all_neighbors(@_);
975}
976
977sub delete_edge {
978    my $g = shift;
979    $g->expect_non_unionfind;
980    my @i = $g->_vertex_ids( @_ );
981    return $g unless @i;
982    my $i = $g->[ _E ]->_get_path_id( @i );
983    return $g unless defined $i;
984    $g->[ _E ]->_del_id( $i );
985    $g->[ _G ]++;
986    return $g;
987}
988
989sub delete_vertex {
990    my $g = shift;
991    $g->expect_non_unionfind;
992    my $V = $g->[ _V ];
993    return $g unless $V->has_path( @_ );
994    my $E = $g->[ _E ];
995    for my $e ( $g->_edges_at( @_ ) ) {
996	$E->_del_id( $e->[ 0 ] );
997    }
998    $V->del_path( @_ );
999    $g->[ _G ]++;
1000    return $g;
1001}
1002
1003sub get_vertex_count {
1004    my $g = shift;
1005    $g->[ _V ]->_get_path_count( @_ ) || 0;
1006}
1007
1008sub get_edge_count {
1009    my $g = shift;
1010    my @e = $g->_vertex_ids( @_ );
1011    return 0 unless @e;
1012    $g->[ _E ]->_get_path_count( @e ) || 0;
1013}
1014
1015sub delete_vertices {
1016    my $g = shift;
1017    $g->expect_non_unionfind;
1018    while (@_) {
1019	my $v = shift @_;
1020	$g->delete_vertex($v);
1021    }
1022    return $g;
1023}
1024
1025sub delete_edges {
1026    my $g = shift;
1027    $g->expect_non_unionfind;
1028    while (@_) {
1029	my ($u, $v) = splice @_, 0, 2;
1030	$g->delete_edge($u, $v);
1031    }
1032    return $g;
1033}
1034
1035###
1036# Degrees.
1037#
1038
1039sub _in_degree {
1040    my $g = shift;
1041    return undef unless @_ && $g->has_vertex( @_ );
1042    my $in = 0;
1043    $in += $g->get_edge_count( @$_ ) for $g->edges_to( @_ );
1044    return $in;
1045}
1046
1047sub in_degree {
1048    my $g = shift;
1049    $g->_in_degree( @_ );
1050}
1051
1052sub _out_degree {
1053    my $g = shift;
1054    return undef unless @_ && $g->has_vertex( @_ );
1055    my $out = 0;
1056    $out += $g->get_edge_count( @$_ ) for $g->edges_from( @_ );
1057    return $out;
1058}
1059
1060sub out_degree {
1061    my $g = shift;
1062    $g->_out_degree( @_ );
1063}
1064
1065sub _total_degree {
1066    my $g = shift;
1067    return undef unless @_ && $g->has_vertex( @_ );
1068    $g->is_undirected ?
1069	$g->_in_degree( @_ ) :
1070	$g-> in_degree( @_ ) - $g-> out_degree( @_ );
1071}
1072
1073sub degree {
1074    my $g = shift;
1075    if (@_) {
1076	$g->_total_degree( @_ );
1077    } elsif ($g->is_undirected) {
1078	my $total = 0;
1079	$total += $g->_total_degree( $_ ) for $g->vertices05;
1080	return $total;
1081    } else {
1082	return 0;
1083    }
1084}
1085
1086*vertex_degree = \&degree;
1087
1088sub is_sink_vertex {
1089    my $g = shift;
1090    return 0 unless @_;
1091    $g->successors( @_ ) == 0 && $g->predecessors( @_ ) > 0;
1092}
1093
1094sub is_source_vertex {
1095    my $g = shift;
1096    return 0 unless @_;
1097    $g->predecessors( @_ ) == 0 && $g->successors( @_ ) > 0;
1098}
1099
1100sub is_successorless_vertex {
1101    my $g = shift;
1102    return 0 unless @_;
1103    $g->successors( @_ ) == 0;
1104}
1105
1106sub is_predecessorless_vertex {
1107    my $g = shift;
1108    return 0 unless @_;
1109    $g->predecessors( @_ ) == 0;
1110}
1111
1112sub is_successorful_vertex {
1113    my $g = shift;
1114    return 0 unless @_;
1115    $g->successors( @_ ) > 0;
1116}
1117
1118sub is_predecessorful_vertex {
1119    my $g = shift;
1120    return 0 unless @_;
1121    $g->predecessors( @_ ) > 0;
1122}
1123
1124sub is_isolated_vertex {
1125    my $g = shift;
1126    return 0 unless @_;
1127    $g->predecessors( @_ ) == 0 && $g->successors( @_ ) == 0;
1128}
1129
1130sub is_interior_vertex {
1131    my $g = shift;
1132    return 0 unless @_;
1133    my $p = $g->predecessors( @_ );
1134    my $s = $g->successors( @_ );
1135    if ($g->is_self_loop_vertex( @_ )) {
1136	$p--;
1137	$s--;
1138    }
1139    $p > 0 && $s > 0;
1140}
1141
1142sub is_exterior_vertex {
1143    my $g = shift;
1144    return 0 unless @_;
1145    $g->predecessors( @_ ) == 0 || $g->successors( @_ ) == 0;
1146}
1147
1148sub is_self_loop_vertex {
1149    my $g = shift;
1150    return 0 unless @_;
1151    for my $s ( $g->successors( @_ ) ) {
1152	return 1 if $s eq $_[0]; # @todo: multiedges, hypervertices
1153    }
1154    return 0;
1155}
1156
1157sub sink_vertices {
1158    my $g = shift;
1159    grep { $g->is_sink_vertex($_) } $g->vertices05;
1160}
1161
1162sub source_vertices {
1163    my $g = shift;
1164    grep { $g->is_source_vertex($_) } $g->vertices05;
1165}
1166
1167sub successorless_vertices {
1168    my $g = shift;
1169    grep { $g->is_successorless_vertex($_) } $g->vertices05;
1170}
1171
1172sub predecessorless_vertices {
1173    my $g = shift;
1174    grep { $g->is_predecessorless_vertex($_) } $g->vertices05;
1175}
1176
1177sub successorful_vertices {
1178    my $g = shift;
1179    grep { $g->is_successorful_vertex($_) } $g->vertices05;
1180}
1181
1182sub predecessorful_vertices {
1183    my $g = shift;
1184    grep { $g->is_predecessorful_vertex($_) } $g->vertices05;
1185}
1186
1187sub isolated_vertices {
1188    my $g = shift;
1189    grep { $g->is_isolated_vertex($_) } $g->vertices05;
1190}
1191
1192sub interior_vertices {
1193    my $g = shift;
1194    grep { $g->is_interior_vertex($_) } $g->vertices05;
1195}
1196
1197sub exterior_vertices {
1198    my $g = shift;
1199    grep { $g->is_exterior_vertex($_) } $g->vertices05;
1200}
1201
1202sub self_loop_vertices {
1203    my $g = shift;
1204    grep { $g->is_self_loop_vertex($_) } $g->vertices05;
1205}
1206
1207###
1208# Paths and cycles.
1209#
1210
1211sub add_path {
1212    my $g = shift;
1213    my $u = shift;
1214    while (@_) {
1215	my $v = shift;
1216	$g->add_edge($u, $v);
1217	$u = $v;
1218    }
1219    return $g;
1220}
1221
1222sub delete_path {
1223    my $g = shift;
1224    $g->expect_non_unionfind;
1225    my $u = shift;
1226    while (@_) {
1227	my $v = shift;
1228	$g->delete_edge($u, $v);
1229	$u = $v;
1230    }
1231    return $g;
1232}
1233
1234sub has_path {
1235    my $g = shift;
1236    my $u = shift;
1237    while (@_) {
1238	my $v = shift;
1239	return 0 unless $g->has_edge($u, $v);
1240	$u = $v;
1241    }
1242    return $g;
1243}
1244
1245sub add_cycle {
1246    my $g = shift;
1247    $g->add_path(@_, $_[0]);
1248}
1249
1250sub delete_cycle {
1251    my $g = shift;
1252    $g->expect_non_unionfind;
1253    $g->delete_path(@_, $_[0]);
1254}
1255
1256sub has_cycle {
1257    my $g = shift;
1258    @_ ? ($g->has_path(@_, $_[0]) ? 1 : 0) : 0;
1259}
1260
1261sub has_a_cycle {
1262    my $g = shift;
1263    my @r = ( back_edge => \&Graph::Traversal::has_a_cycle );
1264    push @r,
1265      down_edge => \&Graph::Traversal::has_a_cycle
1266       if $g->is_undirected;
1267    my $t = Graph::Traversal::DFS->new($g, @r, @_);
1268    $t->dfs;
1269    return $t->get_state('has_a_cycle');
1270}
1271
1272sub find_a_cycle {
1273    my $g = shift;
1274    my @r = ( back_edge => \&Graph::Traversal::find_a_cycle);
1275    push @r,
1276      down_edge => \&Graph::Traversal::find_a_cycle
1277	if $g->is_undirected;
1278    my $t = Graph::Traversal::DFS->new($g, @r, @_);
1279    $t->dfs;
1280    $t->has_state('a_cycle') ? @{ $t->get_state('a_cycle') } : ();
1281}
1282
1283###
1284# Attributes.
1285
1286# Vertex attributes.
1287
1288sub set_vertex_attribute {
1289    my $g = shift;
1290    $g->expect_non_multivertexed;
1291    my $value = pop;
1292    my $attr  = pop;
1293    $g->add_vertex( @_ ) unless $g->has_vertex( @_ );
1294    $g->[ _V ]->_set_path_attr( @_, $attr, $value );
1295}
1296
1297sub set_vertex_attribute_by_id {
1298    my $g = shift;
1299    $g->expect_multivertexed;
1300    my $value = pop;
1301    my $attr  = pop;
1302    $g->add_vertex_by_id( @_ ) unless $g->has_vertex_by_id( @_ );
1303    $g->[ _V ]->_set_path_attr( @_, $attr, $value );
1304}
1305
1306sub set_vertex_attributes {
1307    my $g = shift;
1308    $g->expect_non_multivertexed;
1309    my $attr = pop;
1310    $g->add_vertex( @_ ) unless $g->has_vertex( @_ );
1311    $g->[ _V ]->_set_path_attrs( @_, $attr );
1312}
1313
1314sub set_vertex_attributes_by_id {
1315    my $g = shift;
1316    $g->expect_multivertexed;
1317    my $attr = pop;
1318    $g->add_vertex_by_id( @_ ) unless $g->has_vertex_by_id( @_ );
1319    $g->[ _V ]->_set_path_attrs( @_, $attr );
1320}
1321
1322sub has_vertex_attributes {
1323    my $g = shift;
1324    $g->expect_non_multivertexed;
1325    return 0 unless $g->has_vertex( @_ );
1326    $g->[ _V ]->_has_path_attrs( @_ );
1327}
1328
1329sub has_vertex_attributes_by_id {
1330    my $g = shift;
1331    $g->expect_multivertexed;
1332    return 0 unless $g->has_vertex_by_id( @_ );
1333    $g->[ _V ]->_has_path_attrs( @_ );
1334}
1335
1336sub has_vertex_attribute {
1337    my $g = shift;
1338    $g->expect_non_multivertexed;
1339    my $attr = pop;
1340    return 0 unless $g->has_vertex( @_ );
1341    $g->[ _V ]->_has_path_attr( @_, $attr );
1342}
1343
1344sub has_vertex_attribute_by_id {
1345    my $g = shift;
1346    $g->expect_multivertexed;
1347    my $attr = pop;
1348    return 0 unless $g->has_vertex_by_id( @_ );
1349    $g->[ _V ]->_has_path_attr( @_, $attr );
1350}
1351
1352sub get_vertex_attributes {
1353    my $g = shift;
1354    $g->expect_non_multivertexed;
1355    return unless $g->has_vertex( @_ );
1356    my $a = $g->[ _V ]->_get_path_attrs( @_ );
1357    ($g->is_compat02) ? (defined $a ? %{ $a } : ()) : $a;
1358}
1359
1360sub get_vertex_attributes_by_id {
1361    my $g = shift;
1362    $g->expect_multivertexed;
1363    return unless $g->has_vertex_by_id( @_ );
1364    $g->[ _V ]->_get_path_attrs( @_ );
1365}
1366
1367sub get_vertex_attribute {
1368    my $g = shift;
1369    $g->expect_non_multivertexed;
1370    my $attr = pop;
1371    return unless $g->has_vertex( @_ );
1372    $g->[ _V ]->_get_path_attr( @_, $attr );
1373}
1374
1375sub get_vertex_attribute_by_id {
1376    my $g = shift;
1377    $g->expect_multivertexed;
1378    my $attr = pop;
1379    return unless $g->has_vertex_by_id( @_ );
1380    $g->[ _V ]->_get_path_attr( @_, $attr );
1381}
1382
1383sub get_vertex_attribute_names {
1384    my $g = shift;
1385    $g->expect_non_multivertexed;
1386    return unless $g->has_vertex( @_ );
1387    $g->[ _V ]->_get_path_attr_names( @_ );
1388}
1389
1390sub get_vertex_attribute_names_by_id {
1391    my $g = shift;
1392    $g->expect_multivertexed;
1393    return unless $g->has_vertex_by_id( @_ );
1394    $g->[ _V ]->_get_path_attr_names( @_ );
1395}
1396
1397sub get_vertex_attribute_values {
1398    my $g = shift;
1399    $g->expect_non_multivertexed;
1400    return unless $g->has_vertex( @_ );
1401    $g->[ _V ]->_get_path_attr_values( @_ );
1402}
1403
1404sub get_vertex_attribute_values_by_id {
1405    my $g = shift;
1406    $g->expect_multivertexed;
1407    return unless $g->has_vertex_by_id( @_ );
1408    $g->[ _V ]->_get_path_attr_values( @_ );
1409}
1410
1411sub delete_vertex_attributes {
1412    my $g = shift;
1413    $g->expect_non_multivertexed;
1414    return undef unless $g->has_vertex( @_ );
1415    $g->[ _V ]->_del_path_attrs( @_ );
1416}
1417
1418sub delete_vertex_attributes_by_id {
1419    my $g = shift;
1420    $g->expect_multivertexed;
1421    return undef unless $g->has_vertex_by_id( @_ );
1422    $g->[ _V ]->_del_path_attrs( @_ );
1423}
1424
1425sub delete_vertex_attribute {
1426    my $g = shift;
1427    $g->expect_non_multivertexed;
1428    my $attr = pop;
1429    return undef unless $g->has_vertex( @_ );
1430    $g->[ _V ]->_del_path_attr( @_, $attr );
1431}
1432
1433sub delete_vertex_attribute_by_id {
1434    my $g = shift;
1435    $g->expect_multivertexed;
1436    my $attr = pop;
1437    return undef unless $g->has_vertex_by_id( @_ );
1438    $g->[ _V ]->_del_path_attr( @_, $attr );
1439}
1440
1441# Edge attributes.
1442
1443sub _set_edge_attribute {
1444    my $g = shift;
1445    my $value = pop;
1446    my $attr  = pop;
1447    my $E = $g->[ _E ];
1448    my $f = $E->[ _f ];
1449    my @i;
1450    if ($E->[ _a ] == 2 && @_ == 2 && !($f & (_HYPER|_REF|_UNIQ))) { # Fast path.
1451	@_ = sort @_ if ($f & _UNORD);
1452	my $s = $E->[ _s ];
1453	$g->add_edge( @_ ) unless exists $s->{ $_[0] } && exists $s->{ $_[0] }->{ $_[1] };
1454	@i = @{ $g->[ _V ]->[ _s ] }{ @_ };
1455    } else {
1456	$g->add_edge( @_ ) unless $g->has_edge( @_ );
1457	@i = $g->_vertex_ids( @_ );
1458    }
1459    $g->[ _E ]->_set_path_attr( @i, $attr, $value );
1460}
1461
1462sub set_edge_attribute {
1463    my $g = shift;
1464    $g->expect_non_multiedged;
1465    my $value = pop;
1466    my $attr  = pop;
1467    my $E = $g->[ _E ];
1468    $g->add_edge( @_ ) unless $g->has_edge( @_ );
1469    $E->_set_path_attr( $g->_vertex_ids( @_ ), $attr, $value );
1470}
1471
1472sub set_edge_attribute_by_id {
1473    my $g = shift;
1474    $g->expect_multiedged;
1475    my $value = pop;
1476    my $attr  = pop;
1477    # $g->add_edge_by_id( @_ ) unless $g->has_edge_by_id( @_ );
1478    my $id = pop;
1479    $g->[ _E ]->_set_path_attr( $g->_vertex_ids( @_ ), $id, $attr, $value );
1480}
1481
1482sub set_edge_attributes {
1483    my $g = shift;
1484    $g->expect_non_multiedged;
1485    my $attr = pop;
1486    $g->add_edge( @_ ) unless $g->has_edge( @_ );
1487    $g->[ _E ]->_set_path_attrs( $g->_vertex_ids( @_ ), $attr );
1488}
1489
1490sub set_edge_attributes_by_id {
1491    my $g = shift;
1492    $g->expect_multiedged;
1493    my $attr = pop;
1494    $g->add_edge_by_id( @_ ) unless $g->has_edge_by_id( @_ );
1495    my $id = pop;
1496    $g->[ _E ]->_set_path_attrs( $g->_vertex_ids( @_ ), $id, $attr );
1497}
1498
1499sub has_edge_attributes {
1500    my $g = shift;
1501    $g->expect_non_multiedged;
1502    return 0 unless $g->has_edge( @_ );
1503    $g->[ _E ]->_has_path_attrs( $g->_vertex_ids( @_ ) );
1504}
1505
1506sub has_edge_attributes_by_id {
1507    my $g = shift;
1508    $g->expect_multiedged;
1509    return 0 unless $g->has_edge_by_id( @_ );
1510    my $id = pop;
1511    $g->[ _E ]->_has_path_attrs( $g->_vertex_ids( @_ ), $id );
1512}
1513
1514sub has_edge_attribute {
1515    my $g = shift;
1516    $g->expect_non_multiedged;
1517    my $attr = pop;
1518    return 0 unless $g->has_edge( @_ );
1519    $g->[ _E ]->_has_path_attr( $g->_vertex_ids( @_ ), $attr );
1520}
1521
1522sub has_edge_attribute_by_id {
1523    my $g = shift;
1524    $g->expect_multiedged;
1525    my $attr = pop;
1526    return 0 unless $g->has_edge_by_id( @_ );
1527    my $id = pop;
1528    $g->[ _E ]->_has_path_attr( $g->_vertex_ids( @_ ), $id, $attr );
1529}
1530
1531sub get_edge_attributes {
1532    my $g = shift;
1533    $g->expect_non_multiedged;
1534    return unless $g->has_edge( @_ );
1535    my $a = $g->[ _E ]->_get_path_attrs( $g->_vertex_ids( @_ ) );
1536    ($g->is_compat02) ? (defined $a ? %{ $a } : ()) : $a;
1537}
1538
1539sub get_edge_attributes_by_id {
1540    my $g = shift;
1541    $g->expect_multiedged;
1542    return unless $g->has_edge_by_id( @_ );
1543    my $id = pop;
1544    return $g->[ _E ]->_get_path_attrs( $g->_vertex_ids( @_ ), $id );
1545}
1546
1547sub _get_edge_attribute { # Fast path; less checks.
1548    my $g = shift;
1549    my $attr = pop;
1550    my $E = $g->[ _E ];
1551    my $f = $E->[ _f ];
1552    if ($E->[ _a ] == 2 && @_ == 2 && !($f & (_HYPER|_REF|_UNIQ))) { # Fast path.
1553	@_ = sort @_ if ($f & _UNORD);
1554	my $s = $E->[ _s ];
1555	return unless exists $s->{ $_[0] } && exists $s->{ $_[0] }->{ $_[1] };
1556    } else {
1557	return unless $g->has_edge( @_ );
1558    }
1559    my @i = $g->_vertex_ids( @_ );
1560    $E->_get_path_attr( @i, $attr );
1561}
1562
1563sub get_edge_attribute {
1564    my $g = shift;
1565    $g->expect_non_multiedged;
1566    my $attr = pop;
1567    return undef unless $g->has_edge( @_ );
1568    my @i = $g->_vertex_ids( @_ );
1569    return undef if @i == 0 && @_;
1570    my $E = $g->[ _E ];
1571    $E->_get_path_attr( @i, $attr );
1572}
1573
1574sub get_edge_attribute_by_id {
1575    my $g = shift;
1576    $g->expect_multiedged;
1577    my $attr = pop;
1578    return unless $g->has_edge_by_id( @_ );
1579    my $id = pop;
1580    $g->[ _E ]->_get_path_attr( $g->_vertex_ids( @_ ), $id, $attr );
1581}
1582
1583sub get_edge_attribute_names {
1584    my $g = shift;
1585    $g->expect_non_multiedged;
1586    return unless $g->has_edge( @_ );
1587    $g->[ _E ]->_get_path_attr_names( $g->_vertex_ids( @_ ) );
1588}
1589
1590sub get_edge_attribute_names_by_id {
1591    my $g = shift;
1592    $g->expect_multiedged;
1593    return unless $g->has_edge_by_id( @_ );
1594    my $id = pop;
1595    $g->[ _E ]->_get_path_attr_names( $g->_vertex_ids( @_ ), $id );
1596}
1597
1598sub get_edge_attribute_values {
1599    my $g = shift;
1600    $g->expect_non_multiedged;
1601    return unless $g->has_edge( @_ );
1602    $g->[ _E ]->_get_path_attr_values( $g->_vertex_ids( @_ ) );
1603}
1604
1605sub get_edge_attribute_values_by_id {
1606    my $g = shift;
1607    $g->expect_multiedged;
1608    return unless $g->has_edge_by_id( @_ );
1609    my $id = pop;
1610    $g->[ _E ]->_get_path_attr_values( $g->_vertex_ids( @_ ), $id );
1611}
1612
1613sub delete_edge_attributes {
1614    my $g = shift;
1615    $g->expect_non_multiedged;
1616    return unless $g->has_edge( @_ );
1617    $g->[ _E ]->_del_path_attrs( $g->_vertex_ids( @_ ) );
1618}
1619
1620sub delete_edge_attributes_by_id {
1621    my $g = shift;
1622    $g->expect_multiedged;
1623    return unless $g->has_edge_by_id( @_ );
1624    my $id = pop;
1625    $g->[ _E ]->_del_path_attrs( $g->_vertex_ids( @_ ), $id );
1626}
1627
1628sub delete_edge_attribute {
1629    my $g = shift;
1630    $g->expect_non_multiedged;
1631    my $attr = pop;
1632    return unless $g->has_edge( @_ );
1633    $g->[ _E ]->_del_path_attr( $g->_vertex_ids( @_ ), $attr );
1634}
1635
1636sub delete_edge_attribute_by_id {
1637    my $g = shift;
1638    $g->expect_multiedged;
1639    my $attr = pop;
1640    return unless $g->has_edge_by_id( @_ );
1641    my $id = pop;
1642    $g->[ _E ]->_del_path_attr( $g->_vertex_ids( @_ ), $id, $attr );
1643}
1644
1645###
1646# Compat.
1647#
1648
1649sub vertex {
1650    my $g = shift;
1651    $g->has_vertex( @_ ) ? @_ : undef;
1652}
1653
1654sub out_edges {
1655    my $g = shift;
1656    return unless @_ && $g->has_vertex( @_ );
1657    my @e = $g->edges_from( @_ );
1658    wantarray ? map { @$_ } @e : @e;
1659}
1660
1661sub in_edges {
1662    my $g = shift;
1663    return unless @_ && $g->has_vertex( @_ );
1664    my @e = $g->edges_to( @_ );
1665    wantarray ? map { @$_ } @e : @e;
1666}
1667
1668sub add_vertices {
1669    my $g = shift;
1670    $g->add_vertex( $_ ) for @_;
1671    return $g;
1672}
1673
1674sub add_edges {
1675    my $g = shift;
1676    while (@_) {
1677	my $u = shift @_;
1678	if (ref $u eq 'ARRAY') {
1679	    $g->add_edge( @$u );
1680	} else {
1681	    if (@_) {
1682		my $v = shift @_;
1683		$g->add_edge( $u, $v );
1684	    } else {
1685		require Carp;
1686		Carp::croak("Graph::add_edges: missing end vertex");
1687	    }
1688	}
1689    }
1690    return $g;
1691}
1692
1693###
1694# More constructors.
1695#
1696
1697sub copy {
1698    my $g = shift;
1699    my %opt = _get_options( \@_ );
1700
1701    my $c =
1702	(ref $g)->new(map { $_ => $g->$_ ? 1 : 0 }
1703		      qw(directed
1704			 compat02
1705			 refvertexed
1706			 hypervertexed
1707			 countvertexed
1708			 multivertexed
1709			 hyperedged
1710			 countedged
1711			 multiedged
1712			 omniedged
1713		         __stringified));
1714    for my $v ($g->isolated_vertices) { $c->add_vertex($v) }
1715    for my $e ($g->edges05)           { $c->add_edge(@$e)  }
1716
1717    return $c;
1718}
1719
1720*copy_graph = \©
1721
1722sub _deep_copy_Storable {
1723    my $g = shift;
1724    my $safe = new Safe;
1725    local $Storable::Deparse = 1;
1726    local $Storable::Eval = sub { $safe->reval($_[0]) };
1727    return Storable::thaw(Storable::freeze($g));
1728}
1729
1730sub _deep_copy_DataDumper {
1731    my $g = shift;
1732    my $d = Data::Dumper->new([$g]);
1733    use vars qw($VAR1);
1734    $d->Purity(1)->Terse(1)->Deepcopy(1);
1735    $d->Deparse(1) if $] >= 5.008;
1736    eval $d->Dump;
1737}
1738
1739sub deep_copy {
1740    if (_can_deep_copy_Storable()) {
1741	return _deep_copy_Storable(@_);
1742    } else {
1743	return _deep_copy_DataDumper(@_);
1744    }
1745}
1746
1747*deep_copy_graph = \&deep_copy;
1748
1749sub transpose_edge {
1750    my $g = shift;
1751    if ($g->is_directed) {
1752	return undef unless $g->has_edge( @_ );
1753	my $c = $g->get_edge_count( @_ );
1754	my $a = $g->get_edge_attributes( @_ );
1755	my @e = reverse @_;
1756	$g->delete_edge( @_ ) unless $g->has_edge( @e );
1757	$g->add_edge( @e ) for 1..$c;
1758	$g->set_edge_attributes(@e, $a) if $a;
1759    }
1760    return $g;
1761}
1762
1763sub transpose_graph {
1764    my $g = shift;
1765    my $t = $g->copy;
1766    if ($t->directed) {
1767	for my $e ($t->edges05) {
1768	    $t->transpose_edge(@$e);
1769	}
1770    }
1771    return $t;
1772}
1773
1774*transpose = \&transpose_graph;
1775
1776sub complete_graph {
1777    my $g = shift;
1778    my $c = $g->new( directed => $g->directed );
1779    my @v = $g->vertices05;
1780    for (my $i = 0; $i <= $#v; $i++ ) {
1781	for (my $j = 0; $j <= $#v; $j++ ) {
1782	    next if $i >= $j;
1783	    if ($g->is_undirected) {
1784		$c->add_edge($v[$i], $v[$j]);
1785	    } else {
1786		$c->add_edge($v[$i], $v[$j]);
1787		$c->add_edge($v[$j], $v[$i]);
1788	    }
1789	}
1790    }
1791    return $c;
1792}
1793
1794*complement = \&complement_graph;
1795
1796sub complement_graph {
1797    my $g = shift;
1798    my $c = $g->new( directed => $g->directed );
1799    my @v = $g->vertices05;
1800    for (my $i = 0; $i <= $#v; $i++ ) {
1801	for (my $j = 0; $j <= $#v; $j++ ) {
1802	    next if $i >= $j;
1803	    if ($g->is_undirected) {
1804		$c->add_edge($v[$i], $v[$j])
1805		    unless $g->has_edge($v[$i], $v[$j]);
1806	    } else {
1807		$c->add_edge($v[$i], $v[$j])
1808		    unless $g->has_edge($v[$i], $v[$j]);
1809		$c->add_edge($v[$j], $v[$i])
1810		    unless $g->has_edge($v[$j], $v[$i]);
1811	    }
1812	}
1813    }
1814    return $c;
1815}
1816
1817*complete = \&complete_graph;
1818
1819###
1820# Transitivity.
1821#
1822
1823sub is_transitive {
1824    my $g = shift;
1825    Graph::TransitiveClosure::is_transitive($g);
1826}
1827
1828###
1829# Weighted vertices.
1830#
1831
1832my $defattr = 'weight';
1833
1834sub _defattr {
1835    return $defattr;
1836}
1837
1838sub add_weighted_vertex {
1839    my $g = shift;
1840    $g->expect_non_multivertexed;
1841    my $w = pop;
1842    $g->add_vertex(@_);
1843    $g->set_vertex_attribute(@_, $defattr, $w);
1844}
1845
1846sub add_weighted_vertices {
1847    my $g = shift;
1848    $g->expect_non_multivertexed;
1849    while (@_) {
1850	my ($v, $w) = splice @_, 0, 2;
1851	$g->add_vertex($v);
1852	$g->set_vertex_attribute($v, $defattr, $w);
1853    }
1854}
1855
1856sub get_vertex_weight {
1857    my $g = shift;
1858    $g->expect_non_multivertexed;
1859    $g->get_vertex_attribute(@_, $defattr);
1860}
1861
1862sub has_vertex_weight {
1863    my $g = shift;
1864    $g->expect_non_multivertexed;
1865    $g->has_vertex_attribute(@_, $defattr);
1866}
1867
1868sub set_vertex_weight {
1869    my $g = shift;
1870    $g->expect_non_multivertexed;
1871    my $w = pop;
1872    $g->set_vertex_attribute(@_, $defattr, $w);
1873}
1874
1875sub delete_vertex_weight {
1876    my $g = shift;
1877    $g->expect_non_multivertexed;
1878    $g->delete_vertex_attribute(@_, $defattr);
1879}
1880
1881sub add_weighted_vertex_by_id {
1882    my $g = shift;
1883    $g->expect_multivertexed;
1884    my $w = pop;
1885    $g->add_vertex_by_id(@_);
1886    $g->set_vertex_attribute_by_id(@_, $defattr, $w);
1887}
1888
1889sub add_weighted_vertices_by_id {
1890    my $g = shift;
1891    $g->expect_multivertexed;
1892    my $id = pop;
1893    while (@_) {
1894	my ($v, $w) = splice @_, 0, 2;
1895	$g->add_vertex_by_id($v, $id);
1896	$g->set_vertex_attribute_by_id($v, $id, $defattr, $w);
1897    }
1898}
1899
1900sub get_vertex_weight_by_id {
1901    my $g = shift;
1902    $g->expect_multivertexed;
1903    $g->get_vertex_attribute_by_id(@_, $defattr);
1904}
1905
1906sub has_vertex_weight_by_id {
1907    my $g = shift;
1908    $g->expect_multivertexed;
1909    $g->has_vertex_attribute_by_id(@_, $defattr);
1910}
1911
1912sub set_vertex_weight_by_id {
1913    my $g = shift;
1914    $g->expect_multivertexed;
1915    my $w = pop;
1916    $g->set_vertex_attribute_by_id(@_, $defattr, $w);
1917}
1918
1919sub delete_vertex_weight_by_id {
1920    my $g = shift;
1921    $g->expect_multivertexed;
1922    $g->delete_vertex_attribute_by_id(@_, $defattr);
1923}
1924
1925###
1926# Weighted edges.
1927#
1928
1929sub add_weighted_edge {
1930    my $g = shift;
1931    $g->expect_non_multiedged;
1932    if ($g->is_compat02) {
1933	my $w = splice @_, 1, 1;
1934	$g->add_edge(@_);
1935	$g->set_edge_attribute(@_, $defattr, $w);
1936    } else {
1937	my $w = pop;
1938	$g->add_edge(@_);
1939	$g->set_edge_attribute(@_, $defattr, $w);
1940    }
1941}
1942
1943sub add_weighted_edges {
1944    my $g = shift;
1945    $g->expect_non_multiedged;
1946    if ($g->is_compat02) {
1947	while (@_) {
1948	    my ($u, $w, $v) = splice @_, 0, 3;
1949	    $g->add_edge($u, $v);
1950	    $g->set_edge_attribute($u, $v, $defattr, $w);
1951	}
1952    } else {
1953	while (@_) {
1954	    my ($u, $v, $w) = splice @_, 0, 3;
1955	    $g->add_edge($u, $v);
1956	    $g->set_edge_attribute($u, $v, $defattr, $w);
1957	}
1958    }
1959}
1960
1961sub add_weighted_edges_by_id {
1962    my $g = shift;
1963    $g->expect_multiedged;
1964    my $id = pop;
1965    while (@_) {
1966	my ($u, $v, $w) = splice @_, 0, 3;
1967	$g->add_edge_by_id($u, $v, $id);
1968	$g->set_edge_attribute_by_id($u, $v, $id, $defattr, $w);
1969    }
1970}
1971
1972sub add_weighted_path {
1973    my $g = shift;
1974    $g->expect_non_multiedged;
1975    my $u = shift;
1976    while (@_) {
1977	my ($w, $v) = splice @_, 0, 2;
1978	$g->add_edge($u, $v);
1979	$g->set_edge_attribute($u, $v, $defattr, $w);
1980	$u = $v;
1981    }
1982}
1983
1984sub get_edge_weight {
1985    my $g = shift;
1986    $g->expect_non_multiedged;
1987    $g->get_edge_attribute(@_, $defattr);
1988}
1989
1990sub has_edge_weight {
1991    my $g = shift;
1992    $g->expect_non_multiedged;
1993    $g->has_edge_attribute(@_, $defattr);
1994}
1995
1996sub set_edge_weight {
1997    my $g = shift;
1998    $g->expect_non_multiedged;
1999    my $w = pop;
2000    $g->set_edge_attribute(@_, $defattr, $w);
2001}
2002
2003sub delete_edge_weight {
2004    my $g = shift;
2005    $g->expect_non_multiedged;
2006    $g->delete_edge_attribute(@_, $defattr);
2007}
2008
2009sub add_weighted_edge_by_id {
2010    my $g = shift;
2011    $g->expect_multiedged;
2012    if ($g->is_compat02) {
2013	my $w = splice @_, 1, 1;
2014	$g->add_edge_by_id(@_);
2015	$g->set_edge_attribute_by_id(@_, $defattr, $w);
2016    } else {
2017	my $w = pop;
2018	$g->add_edge_by_id(@_);
2019	$g->set_edge_attribute_by_id(@_, $defattr, $w);
2020    }
2021}
2022
2023sub add_weighted_path_by_id {
2024    my $g = shift;
2025    $g->expect_multiedged;
2026    my $id = pop;
2027    my $u = shift;
2028    while (@_) {
2029	my ($w, $v) = splice @_, 0, 2;
2030	$g->add_edge_by_id($u, $v, $id);
2031	$g->set_edge_attribute_by_id($u, $v, $id, $defattr, $w);
2032	$u = $v;
2033    }
2034}
2035
2036sub get_edge_weight_by_id {
2037    my $g = shift;
2038    $g->expect_multiedged;
2039    $g->get_edge_attribute_by_id(@_, $defattr);
2040}
2041
2042sub has_edge_weight_by_id {
2043    my $g = shift;
2044    $g->expect_multiedged;
2045    $g->has_edge_attribute_by_id(@_, $defattr);
2046}
2047
2048sub set_edge_weight_by_id {
2049    my $g = shift;
2050    $g->expect_multiedged;
2051    my $w = pop;
2052    $g->set_edge_attribute_by_id(@_, $defattr, $w);
2053}
2054
2055sub delete_edge_weight_by_id {
2056    my $g = shift;
2057    $g->expect_multiedged;
2058    $g->delete_edge_attribute_by_id(@_, $defattr);
2059}
2060
2061###
2062# Error helpers.
2063#
2064
2065my %expected;
2066@expected{qw(directed undirected acyclic)} = qw(undirected directed cyclic);
2067
2068sub _expected {
2069    my $exp = shift;
2070    my $got = @_ ? shift : $expected{$exp};
2071    $got = defined $got ? ", got $got" : "";
2072    if (my @caller2 = caller(2)) {
2073	die "$caller2[3]: expected $exp graph$got, at $caller2[1] line $caller2[2].\n";
2074    } else {
2075	my @caller1 = caller(1);
2076	die "$caller1[3]: expected $exp graph$got, at $caller1[1] line $caller1[2].\n";
2077    }
2078}
2079
2080sub expect_undirected {
2081    my $g = shift;
2082    _expected('undirected') unless $g->is_undirected;
2083}
2084
2085sub expect_directed {
2086    my $g = shift;
2087    _expected('directed') unless $g->is_directed;
2088}
2089
2090sub expect_acyclic {
2091    my $g = shift;
2092    _expected('acyclic') unless $g->is_acyclic;
2093}
2094
2095sub expect_dag {
2096    my $g = shift;
2097    my @got;
2098    push @got, 'undirected' unless $g->is_directed;
2099    push @got, 'cyclic'     unless $g->is_acyclic;
2100    _expected('directed acyclic', "@got") if @got;
2101}
2102
2103sub expect_multivertexed {
2104    my $g = shift;
2105    _expected('multivertexed') unless $g->is_multivertexed;
2106}
2107
2108sub expect_non_multivertexed {
2109    my $g = shift;
2110    _expected('non-multivertexed') if $g->is_multivertexed;
2111}
2112
2113sub expect_non_multiedged {
2114    my $g = shift;
2115    _expected('non-multiedged') if $g->is_multiedged;
2116}
2117
2118sub expect_multiedged {
2119    my $g = shift;
2120    _expected('multiedged') unless $g->is_multiedged;
2121}
2122
2123sub expect_non_unionfind {
2124    my $g = shift;
2125    _expected('non-unionfind') if $g->has_union_find;
2126}
2127
2128sub _get_options {
2129    my @caller = caller(1);
2130    unless (@_ == 1 && ref $_[0] eq 'ARRAY') {
2131	die "$caller[3]: internal error: should be called with only one array ref argument, at $caller[1] line $caller[2].\n";
2132    }
2133    my @opt = @{ $_[0] };
2134    unless (@opt  % 2 == 0) {
2135	die "$caller[3]: expected an options hash, got a non-even number of arguments, at $caller[1] line $caller[2].\n";
2136    }
2137    return @opt;
2138}
2139
2140###
2141# Random constructors and accessors.
2142#
2143
2144sub __fisher_yates_shuffle (@) {
2145    # From perlfaq4, but modified to be non-modifying.
2146    my @a = @_;
2147    my $i = @a;
2148    while ($i--) {
2149	my $j = int rand ($i+1);
2150	@a[$i,$j] = @a[$j,$i];
2151    }
2152    return @a;
2153}
2154
2155BEGIN {
2156    sub _shuffle(@);
2157    # Workaround for the Perl bug [perl #32383] where -d:Dprof and
2158    # List::Util::shuffle do not like each other: if any debugging
2159    # (-d) flags are on, fall back to our own Fisher-Yates shuffle.
2160    # The bug was fixed by perl changes #26054 and #26062, which
2161    # went to Perl 5.9.3.  If someone tests this with a pre-5.9.3
2162    # bleadperl that calls itself 5.9.3 but doesn't yet have the
2163    # patches, oh, well.
2164    *_shuffle = $^P && $] < 5.009003 ?
2165	\&__fisher_yates_shuffle : \&List::Util::shuffle;
2166}
2167
2168sub random_graph {
2169    my $class = (@_ % 2) == 0 ? 'Graph' : shift;
2170    my %opt = _get_options( \@_ );
2171    my $random_edge;
2172    unless (exists $opt{vertices} && defined $opt{vertices}) {
2173	require Carp;
2174	Carp::croak("Graph::random_graph: argument 'vertices' missing or undef");
2175    }
2176    if (exists $opt{random_seed}) {
2177	srand($opt{random_seed});
2178	delete $opt{random_seed};
2179    }
2180    if (exists $opt{random_edge}) {
2181	$random_edge = $opt{random_edge};
2182	delete $opt{random_edge};
2183    }
2184    my @V;
2185    if (my $ref = ref $opt{vertices}) {
2186	if ($ref eq 'ARRAY') {
2187	    @V = @{ $opt{vertices} };
2188	} else {
2189	    Carp::croak("Graph::random_graph: argument 'vertices' illegal");
2190	}
2191    } else {
2192	@V = 0..($opt{vertices} - 1);
2193    }
2194    delete $opt{vertices};
2195    my $V = @V;
2196    my $C = $V * ($V - 1) / 2;
2197    my $E;
2198    if (exists $opt{edges} && exists $opt{edges_fill}) {
2199	Carp::croak("Graph::random_graph: both arguments 'edges' and 'edges_fill' specified");
2200    }
2201    $E = exists $opt{edges_fill} ? $opt{edges_fill} * $C : $opt{edges};
2202    delete $opt{edges};
2203    delete $opt{edges_fill};
2204    my $g = $class->new(%opt);
2205    $g->add_vertices(@V);
2206    return $g if $V < 2;
2207    $C *= 2 if $g->directed;
2208    $E = $C / 2 unless defined $E;
2209    $E = int($E + 0.5);
2210    my $p = $E / $C;
2211    $random_edge = sub { $p } unless defined $random_edge;
2212    # print "V = $V, E = $E, C = $C, p = $p\n";
2213    if ($p > 1.0 && !($g->countedged || $g->multiedged)) {
2214	require Carp;
2215	Carp::croak("Graph::random_graph: needs to be countedged or multiedged ($E > $C)");
2216    }
2217    my @V1 = @V;
2218    my @V2 = @V;
2219    # Shuffle the vertex lists so that the pairs at
2220    # the beginning of the lists are not more likely.
2221    @V1 = _shuffle @V1;
2222    @V2 = _shuffle @V2;
2223 LOOP:
2224    while ($E) {
2225	for my $v1 (@V1) {
2226	    for my $v2 (@V2) {
2227		next if $v1 eq $v2; # TODO: allow self-loops?
2228		my $q = $random_edge->($g, $v1, $v2, $p);
2229		if ($q && ($q == 1 || rand() <= $q) &&
2230		    !$g->has_edge($v1, $v2)) {
2231		    $g->add_edge($v1, $v2);
2232		    $E--;
2233		    last LOOP unless $E;
2234		}
2235	    }
2236	}
2237    }
2238    return $g;
2239}
2240
2241sub random_vertex {
2242    my $g = shift;
2243    my @V = $g->vertices05;
2244    @V[rand @V];
2245}
2246
2247sub random_edge {
2248    my $g = shift;
2249    my @E = $g->edges05;
2250    @E[rand @E];
2251}
2252
2253sub random_successor {
2254    my ($g, $v) = @_;
2255    my @S = $g->successors($v);
2256    @S[rand @S];
2257}
2258
2259sub random_predecessor {
2260    my ($g, $v) = @_;
2261    my @P = $g->predecessors($v);
2262    @P[rand @P];
2263}
2264
2265###
2266# Algorithms.
2267#
2268
2269my $MST_comparator = sub { ($_[0] || 0) <=> ($_[1] || 0) };
2270
2271sub _MST_attr {
2272    my $attr = shift;
2273    my $attribute =
2274	exists $attr->{attribute}  ?
2275	    $attr->{attribute}  : $defattr;
2276    my $comparator =
2277	exists $attr->{comparator} ?
2278	    $attr->{comparator} : $MST_comparator;
2279    return ($attribute, $comparator);
2280}
2281
2282sub _MST_edges {
2283    my ($g, $attr) = @_;
2284    my ($attribute, $comparator) = _MST_attr($attr);
2285    map { $_->[1] }
2286        sort { $comparator->($a->[0], $b->[0], $a->[1], $b->[1]) }
2287             map { [ $g->get_edge_attribute(@$_, $attribute), $_ ] }
2288                 $g->edges05;
2289}
2290
2291sub MST_Kruskal {
2292    my ($g, %attr) = @_;
2293
2294    $g->expect_undirected;
2295
2296    my $MST = Graph::Undirected->new;
2297
2298    my $UF  = Graph::UnionFind->new;
2299    for my $v ($g->vertices05) { $UF->add($v) }
2300
2301    for my $e ($g->_MST_edges(\%attr)) {
2302	my ($u, $v) = @$e; # TODO: hyperedges
2303	my $t0 = $UF->find( $u );
2304	my $t1 = $UF->find( $v );
2305	unless ($t0 eq $t1) {
2306	    $UF->union($u, $v);
2307	    $MST->add_edge($u, $v);
2308	}
2309    }
2310
2311    return $MST;
2312}
2313
2314sub _MST_add {
2315    my ($g, $h, $HF, $r, $attr, $unseen) = @_;
2316    for my $s ( grep { exists $unseen->{ $_ } } $g->successors( $r ) ) {
2317	$HF->add( Graph::MSTHeapElem->new( $r, $s, $g->get_edge_attribute( $r, $s, $attr ) ) );
2318    }
2319}
2320
2321sub _next_alphabetic { shift; (sort               keys %{ $_[0] })[0] }
2322sub _next_numeric    { shift; (sort { $a <=> $b } keys %{ $_[0] })[0] }
2323sub _next_random     { shift; (values %{ $_[0] })[ rand keys %{ $_[0] } ] }
2324
2325sub _root_opt {
2326    my $g = shift;
2327    my %opt = @_ == 1 ? ( first_root => $_[0] ) : _get_options( \@_ );
2328    my %unseen;
2329    my @unseen = $g->vertices05;
2330    @unseen{ @unseen } = @unseen;
2331    @unseen = _shuffle @unseen;
2332    my $r;
2333    if (exists $opt{ start }) {
2334	$opt{ first_root } = $opt{ start };
2335	$opt{ next_root  } = undef;
2336    }
2337    if (exists $opt{ get_next_root }) {
2338	$opt{ next_root  } = $opt{ get_next_root }; # Graph 0.201 compat.
2339    }
2340    if (exists $opt{ first_root }) {
2341	if (ref $opt{ first_root } eq 'CODE') {
2342	    $r = $opt{ first_root }->( $g, \%unseen );
2343	} else {
2344	    $r = $opt{ first_root };
2345	}
2346    } else {
2347	$r = shift @unseen;
2348    }
2349    my $next =
2350	exists $opt{ next_root } ?
2351	    $opt{ next_root } :
2352		$opt{ next_alphabetic } ?
2353		    \&_next_alphabetic :
2354			$opt{ next_numeric } ? \&_next_numeric :
2355			    \&_next_random;
2356    my $code = ref $next eq 'CODE';
2357    my $attr = exists $opt{ attribute } ? $opt{ attribute } : $defattr;
2358    return ( \%opt, \%unseen, \@unseen, $r, $next, $code, $attr );
2359}
2360
2361sub _heap_walk {
2362    my ($g, $h, $add, $etc) = splice @_, 0, 4; # Leave %opt in @_.
2363
2364    my ($opt, $unseenh, $unseena, $r, $next, $code, $attr) = $g->_root_opt(@_);
2365    my $HF = Heap071::Fibonacci->new;
2366
2367    while (defined $r) {
2368	# print "r = $r\n";
2369	$add->($g, $h, $HF, $r, $attr, $unseenh, $etc);
2370	delete $unseenh->{ $r };
2371	while (defined $HF->top) {
2372	    my $t = $HF->extract_top;
2373	    # use Data::Dumper; print "t = ", Dumper($t);
2374	    if (defined $t) {
2375		my ($u, $v, $w) = $t->val;
2376		# print "extracted top: $u $v $w\n";
2377		if (exists $unseenh->{ $v }) {
2378		    $h->set_edge_attribute($u, $v, $attr, $w);
2379		    delete $unseenh->{ $v };
2380		    $add->($g, $h, $HF, $v, $attr, $unseenh, $etc);
2381		}
2382	    }
2383	}
2384	return $h unless defined $next;
2385	$r = $code ? $next->( $g, $unseenh ) : shift @$unseena;
2386    }
2387
2388    return $h;
2389}
2390
2391sub MST_Prim {
2392    my $g = shift;
2393    $g->expect_undirected;
2394    $g->_heap_walk(Graph::Undirected->new(), \&_MST_add, undef, @_);
2395}
2396
2397*MST_Dijkstra = \&MST_Prim;
2398
2399*minimum_spanning_tree = \&MST_Prim;
2400
2401###
2402# Cycle detection.
2403#
2404
2405*is_cyclic = \&has_a_cycle;
2406
2407sub is_acyclic {
2408    my $g = shift;
2409    return !$g->is_cyclic;
2410}
2411
2412sub is_dag {
2413    my $g = shift;
2414    return $g->is_directed && $g->is_acyclic ? 1 : 0;
2415}
2416
2417*is_directed_acyclic_graph = \&is_dag;
2418
2419###
2420# Backward compat.
2421#
2422
2423sub average_degree {
2424    my $g = shift;
2425    my $V = $g->vertices05;
2426
2427    return $V ? $g->degree / $V : 0;
2428}
2429
2430sub density_limits {
2431    my $g = shift;
2432
2433    my $V = $g->vertices05;
2434    my $M = $V * ($V - 1);
2435
2436    $M /= 2 if $g->is_undirected;
2437
2438    return ( 0.25 * $M, 0.75 * $M, $M );
2439}
2440
2441sub density {
2442    my $g = shift;
2443    my ($sparse, $dense, $complete) = $g->density_limits;
2444
2445    return $complete ? $g->edges / $complete : 0;
2446}
2447
2448###
2449# Attribute backward compat
2450#
2451
2452sub _attr02_012 {
2453    my ($g, $op, $ga, $va, $ea) = splice @_, 0, 5;
2454    if ($g->is_compat02) {
2455	if    (@_ == 0) { return $ga->( $g ) }
2456	elsif (@_ == 1) { return $va->( $g, @_ ) }
2457	elsif (@_ == 2) { return $ea->( $g, @_ ) }
2458	else {
2459	    die sprintf "$op: wrong number of arguments (%d)", scalar @_;
2460	}
2461    } else {
2462	die "$op: not a compat02 graph"
2463    }
2464}
2465
2466sub _attr02_123 {
2467    my ($g, $op, $ga, $va, $ea) = splice @_, 0, 5;
2468    if ($g->is_compat02) {
2469	if    (@_ == 1) { return $ga->( $g, @_ ) }
2470	elsif (@_ == 2) { return $va->( $g, @_[1, 0] ) }
2471	elsif (@_ == 3) { return $ea->( $g, @_[1, 2, 0] ) }
2472	else {
2473	    die sprintf "$op: wrong number of arguments (%d)", scalar @_;
2474	}
2475    } else {
2476	die "$op: not a compat02 graph"
2477    }
2478}
2479
2480sub _attr02_234 {
2481    my ($g, $op, $ga, $va, $ea) = splice @_, 0, 5;
2482    if ($g->is_compat02) {
2483	if    (@_ == 2) { return $ga->( $g, @_ ) }
2484	elsif (@_ == 3) { return $va->( $g, @_[1, 0, 2] ) }
2485	elsif (@_ == 4) { return $ea->( $g, @_[1, 2, 0, 3] ) }
2486	else {
2487	    die sprintf "$op: wrong number of arguments (%d)", scalar @_;
2488	}
2489    } else {
2490	die "$op: not a compat02 graph";
2491    }
2492}
2493
2494sub set_attribute {
2495    my $g = shift;
2496    $g->_attr02_234('set_attribute',
2497		    \&Graph::set_graph_attribute,
2498		    \&Graph::set_vertex_attribute,
2499		    \&Graph::set_edge_attribute,
2500		    @_);
2501
2502}
2503
2504sub set_attributes {
2505    my $g = shift;
2506    my $a = pop;
2507    $g->_attr02_123('set_attributes',
2508		    \&Graph::set_graph_attributes,
2509		    \&Graph::set_vertex_attributes,
2510		    \&Graph::set_edge_attributes,
2511		    $a, @_);
2512
2513}
2514
2515sub get_attribute {
2516    my $g = shift;
2517    $g->_attr02_123('get_attribute',
2518		    \&Graph::get_graph_attribute,
2519		    \&Graph::get_vertex_attribute,
2520		    \&Graph::get_edge_attribute,
2521		    @_);
2522
2523}
2524
2525sub get_attributes {
2526    my $g = shift;
2527    $g->_attr02_012('get_attributes',
2528		    \&Graph::get_graph_attributes,
2529		    \&Graph::get_vertex_attributes,
2530		    \&Graph::get_edge_attributes,
2531		    @_);
2532
2533}
2534
2535sub has_attribute {
2536    my $g = shift;
2537    return 0 unless @_;
2538    $g->_attr02_123('has_attribute',
2539		    \&Graph::has_graph_attribute,
2540		    \&Graph::has_vertex_attribute,
2541		    \&Graph::get_edge_attribute,
2542		    @_);
2543
2544}
2545
2546sub has_attributes {
2547    my $g = shift;
2548    $g->_attr02_012('has_attributes',
2549		    \&Graph::has_graph_attributes,
2550		    \&Graph::has_vertex_attributes,
2551		    \&Graph::has_edge_attributes,
2552		    @_);
2553
2554}
2555
2556sub delete_attribute {
2557    my $g = shift;
2558    $g->_attr02_123('delete_attribute',
2559		    \&Graph::delete_graph_attribute,
2560		    \&Graph::delete_vertex_attribute,
2561		    \&Graph::delete_edge_attribute,
2562		    @_);
2563
2564}
2565
2566sub delete_attributes {
2567    my $g = shift;
2568    $g->_attr02_012('delete_attributes',
2569		    \&Graph::delete_graph_attributes,
2570		    \&Graph::delete_vertex_attributes,
2571		    \&Graph::delete_edge_attributes,
2572		    @_);
2573
2574}
2575
2576###
2577# Simple DFS uses.
2578#
2579
2580sub topological_sort {
2581    my $g = shift;
2582    my %opt = _get_options( \@_ );
2583    my $eic = $opt{ empty_if_cyclic };
2584    my $hac;
2585    if ($eic) {
2586	$hac = $g->has_a_cycle;
2587    } else {
2588	$g->expect_dag;
2589    }
2590    delete $opt{ empty_if_cyclic };
2591    my $t = Graph::Traversal::DFS->new($g, %opt);
2592    my @s = $t->dfs;
2593    $hac ? () : reverse @s;
2594}
2595
2596*toposort = \&topological_sort;
2597
2598sub undirected_copy {
2599    my $g = shift;
2600
2601    $g->expect_directed;
2602
2603    my $c = Graph::Undirected->new;
2604    for my $v ($g->isolated_vertices) { # TODO: if iv ...
2605	$c->add_vertex($v);
2606    }
2607    for my $e ($g->edges05) {
2608	$c->add_edge(@$e);
2609    }
2610    return $c;
2611}
2612
2613*undirected_copy_graph = \&undirected_copy;
2614
2615sub directed_copy {
2616    my $g = shift;
2617    $g->expect_undirected;
2618    my $c = Graph::Directed->new;
2619    for my $v ($g->isolated_vertices) { # TODO: if iv ...
2620	$c->add_vertex($v);
2621    }
2622    for my $e ($g->edges05) {
2623	my @e = @$e;
2624	$c->add_edge(@e);
2625	$c->add_edge(reverse @e);
2626    }
2627    return $c;
2628}
2629
2630*directed_copy_graph = \&directed_copy;
2631
2632###
2633# Cache or not.
2634#
2635
2636my %_cache_type =
2637    (
2638     'connectivity'        => '_ccc',
2639     'strong_connectivity' => '_scc',
2640     'biconnectivity'      => '_bcc',
2641     'SPT_Dijkstra'        => '_spt_di',
2642     'SPT_Bellman_Ford'    => '_spt_bf',
2643    );
2644
2645sub _check_cache {
2646    my ($g, $type, $code) = splice @_, 0, 3;
2647    my $c = $_cache_type{$type};
2648    if (defined $c) {
2649	my $a = $g->get_graph_attribute($c);
2650	unless (defined $a && $a->[ 0 ] == $g->[ _G ]) {
2651	    $a->[ 0 ] = $g->[ _G ];
2652	    $a->[ 1 ] = $code->( $g, @_ );
2653	    $g->set_graph_attribute($c, $a);
2654	}
2655	return $a->[ 1 ];
2656    } else {
2657	Carp::croak("Graph: unknown cache type '$type'");
2658    }
2659}
2660
2661sub _clear_cache {
2662    my ($g, $type) = @_;
2663    my $c = $_cache_type{$type};
2664    if (defined $c) {
2665	$g->delete_graph_attribute($c);
2666    } else {
2667	Carp::croak("Graph: unknown cache type '$type'");
2668    }
2669}
2670
2671sub connectivity_clear_cache {
2672    my $g = shift;
2673    _clear_cache($g, 'connectivity');
2674}
2675
2676sub strong_connectivity_clear_cache {
2677    my $g = shift;
2678    _clear_cache($g, 'strong_connectivity');
2679}
2680
2681sub biconnectivity_clear_cache {
2682    my $g = shift;
2683    _clear_cache($g, 'biconnectivity');
2684}
2685
2686sub SPT_Dijkstra_clear_cache {
2687    my $g = shift;
2688    _clear_cache($g, 'SPT_Dijkstra');
2689    $g->delete_graph_attribute('SPT_Dijkstra_first_root');
2690}
2691
2692sub SPT_Bellman_Ford_clear_cache {
2693    my $g = shift;
2694    _clear_cache($g, 'SPT_Bellman_Ford');
2695}
2696
2697###
2698# Connected components.
2699#
2700
2701sub _connected_components_compute {
2702    my $g = shift;
2703    my %cce;
2704    my %cci;
2705    my $cc = 0;
2706    if ($g->has_union_find) {
2707	my $UF = $g->_get_union_find();
2708	my $V  = $g->[ _V ];
2709	my %icce; # Isolated vertices.
2710	my %icci;
2711	my $icc = 0;
2712	for my $v ( $g->unique_vertices ) {
2713	    $cc = $UF->find( $V->_get_path_id( $v ) );
2714	    if (defined $cc) {
2715		$cce{ $v } = $cc;
2716		push @{ $cci{ $cc } }, $v;
2717	    } else {
2718		$icce{ $v } = $icc;
2719		push @{ $icci{ $icc } }, $v;
2720		$icc++;
2721	    }
2722	}
2723	if ($icc) {
2724	    @cce{ keys %icce } = values %icce;
2725	    @cci{ keys %icci } = values %icci;
2726	}
2727    } else {
2728	my @u = $g->unique_vertices;
2729	my %r; @r{ @u } = @u;
2730	my $froot = sub {
2731	    (each %r)[1];
2732	};
2733	my $nroot = sub {
2734	    $cc++ if keys %r;
2735	    (each %r)[1];
2736	};
2737	my $t = Graph::Traversal::DFS->new($g,
2738					   first_root => $froot,
2739					   next_root  => $nroot,
2740					   pre => sub {
2741					       my ($v, $t) = @_;
2742					       $cce{ $v } = $cc;
2743					       push @{ $cci{ $cc } }, $v;
2744					       delete $r{ $v };
2745					   },
2746					   @_);
2747	$t->dfs;
2748    }
2749    return [ \%cce, \%cci ];
2750}
2751
2752sub _connected_components {
2753    my $g = shift;
2754    my $ccc = _check_cache($g, 'connectivity',
2755			   \&_connected_components_compute, @_);
2756    return @{ $ccc };
2757}
2758
2759sub connected_component_by_vertex {
2760    my ($g, $v) = @_;
2761    $g->expect_undirected;
2762    my ($CCE, $CCI) = $g->_connected_components();
2763    return $CCE->{ $v };
2764}
2765
2766sub connected_component_by_index {
2767    my ($g, $i) = @_;
2768    $g->expect_undirected;
2769    my ($CCE, $CCI) = $g->_connected_components();
2770    return defined $CCI->{ $i } ? @{ $CCI->{ $i } } : ( );
2771}
2772
2773sub connected_components {
2774    my $g = shift;
2775    $g->expect_undirected;
2776    my ($CCE, $CCI) = $g->_connected_components();
2777    return values %{ $CCI };
2778}
2779
2780sub same_connected_components {
2781    my $g = shift;
2782    $g->expect_undirected;
2783    if ($g->has_union_find) {
2784	my $UF = $g->_get_union_find();
2785	my $V  = $g->[ _V ];
2786	my $u = shift;
2787	my $c = $UF->find( $V->_get_path_id ( $u ) );
2788	my $d;
2789	for my $v ( @_) {
2790	    return 0
2791		unless defined($d = $UF->find( $V->_get_path_id( $v ) )) &&
2792		       $d eq $c;
2793	}
2794	return 1;
2795    } else {
2796	my ($CCE, $CCI) = $g->_connected_components();
2797	my $u = shift;
2798	my $c = $CCE->{ $u };
2799	for my $v ( @_) {
2800	    return 0
2801		unless defined $CCE->{ $v } &&
2802		       $CCE->{ $v } eq $c;
2803	}
2804	return 1;
2805    }
2806}
2807
2808my $super_component = sub { join("+", sort @_) };
2809
2810sub connected_graph {
2811    my ($g, %opt) = @_;
2812    $g->expect_undirected;
2813    my $cg = Graph->new(undirected => 1);
2814    if ($g->has_union_find && $g->vertices == 1) {
2815	# TODO: super_component?
2816	$cg->add_vertices($g->vertices);
2817    } else {
2818	my $sc_cb =
2819	    exists $opt{super_component} ?
2820		$opt{super_component} : $super_component;
2821	for my $cc ( $g->connected_components() ) {
2822	    my $sc = $sc_cb->(@$cc);
2823	    $cg->add_vertex($sc);
2824	    $cg->set_vertex_attribute($sc, 'subvertices', [ @$cc ]);
2825	}
2826    }
2827    return $cg;
2828}
2829
2830sub is_connected {
2831    my $g = shift;
2832    $g->expect_undirected;
2833    my ($CCE, $CCI) = $g->_connected_components();
2834    return keys %{ $CCI } == 1;
2835}
2836
2837sub is_weakly_connected {
2838    my $g = shift;
2839    $g->expect_directed;
2840    $g->undirected_copy->is_connected(@_);
2841}
2842
2843*weakly_connected = \&is_weakly_connected;
2844
2845sub weakly_connected_components {
2846    my $g = shift;
2847    $g->expect_directed;
2848    $g->undirected_copy->connected_components(@_);
2849}
2850
2851sub weakly_connected_component_by_vertex {
2852    my $g = shift;
2853    $g->expect_directed;
2854    $g->undirected_copy->connected_component_by_vertex(@_);
2855}
2856
2857sub weakly_connected_component_by_index {
2858    my $g = shift;
2859    $g->expect_directed;
2860    $g->undirected_copy->connected_component_by_index(@_);
2861}
2862
2863sub same_weakly_connected_components {
2864    my $g = shift;
2865    $g->expect_directed;
2866    $g->undirected_copy->same_connected_components(@_);
2867}
2868
2869sub weakly_connected_graph {
2870    my $g = shift;
2871    $g->expect_directed;
2872    $g->undirected_copy->connected_graph(@_);
2873}
2874
2875sub _strongly_connected_components_compute {
2876    my $g = shift;
2877    my $t = Graph::Traversal::DFS->new($g);
2878    my @d = reverse $t->dfs;
2879    my @c;
2880    my $h = $g->transpose_graph;
2881    my $u =
2882	Graph::Traversal::DFS->new($h,
2883				   next_root => sub {
2884				       my ($t, $u) = @_;
2885				       my $root;
2886				       while (defined($root = shift @d)) {
2887					   last if exists $u->{ $root };
2888				       }
2889				       if (defined $root) {
2890					   push @c, [];
2891					   return $root;
2892				       } else {
2893					   return;
2894				       }
2895				   },
2896				   pre => sub {
2897				       my ($v, $t) = @_;
2898				       push @{ $c[-1] }, $v;
2899				   },
2900				   @_);
2901    $u->dfs;
2902    return \@c;
2903}
2904
2905sub _strongly_connected_components {
2906    my $g = shift;
2907    my $type = 'strong_connectivity';
2908    my $scc = _check_cache($g, $type,
2909			   \&_strongly_connected_components_compute, @_);
2910    return defined $scc ? @$scc : ( );
2911}
2912
2913sub strongly_connected_components {
2914    my $g = shift;
2915    $g->expect_directed;
2916    $g->_strongly_connected_components(@_);
2917}
2918
2919sub strongly_connected_component_by_vertex {
2920    my $g = shift;
2921    my $v = shift;
2922    $g->expect_directed;
2923    my @scc = $g->_strongly_connected_components( next_alphabetic => 1, @_ );
2924    for (my $i = 0; $i <= $#scc; $i++) {
2925	for (my $j = 0; $j <= $#{ $scc[$i] }; $j++) {
2926	    return $i if $scc[$i]->[$j] eq $v;
2927	}
2928    }
2929    return;
2930}
2931
2932sub strongly_connected_component_by_index {
2933    my $g = shift;
2934    my $i = shift;
2935    $g->expect_directed;
2936    my $c = ( $g->_strongly_connected_components(@_) )[ $i ];
2937    return defined $c ? @{ $c } : ();
2938}
2939
2940sub same_strongly_connected_components {
2941    my $g = shift;
2942    $g->expect_directed;
2943    my @scc = $g->_strongly_connected_components( next_alphabetic => 1, @_ );
2944    my @i;
2945    while (@_) {
2946	my $v = shift;
2947	for (my $i = 0; $i <= $#scc; $i++) {
2948	    for (my $j = 0; $j <= $#{ $scc[$i] }; $j++) {
2949		if ($scc[$i]->[$j] eq $v) {
2950		    push @i, $i;
2951		    return 0 if @i > 1 && $i[-1] ne $i[0];
2952		}
2953	    }
2954	}
2955    }
2956    return 1;
2957}
2958
2959sub is_strongly_connected {
2960    my $g = shift;
2961    $g->expect_directed;
2962    my $t = Graph::Traversal::DFS->new($g);
2963    my @d = reverse $t->dfs;
2964    my @c;
2965    my $h = $g->transpose;
2966    my $u =
2967	Graph::Traversal::DFS->new($h,
2968				   next_root => sub {
2969				       my ($t, $u) = @_;
2970				       my $root;
2971				       while (defined($root = shift @d)) {
2972					   last if exists $u->{ $root };
2973				       }
2974				       if (defined $root) {
2975					   unless (@{ $t->{ roots } }) {
2976					       push @c, [];
2977					       return $root;
2978					   } else {
2979					       $t->terminate;
2980					       return;
2981					   }
2982				       } else {
2983					   return;
2984				       }
2985				   },
2986				   pre => sub {
2987				       my ($v, $t) = @_;
2988				       push @{ $c[-1] }, $v;
2989				   },
2990				   @_);
2991    $u->dfs;
2992    return @{ $u->{ roots } } == 1 && keys %{ $u->{ unseen } } == 0;
2993}
2994
2995*strongly_connected = \&is_strongly_connected;
2996
2997sub strongly_connected_graph {
2998    my $g = shift;
2999    my %attr = @_;
3000
3001    $g->expect_directed;
3002
3003    my $t = Graph::Traversal::DFS->new($g);
3004    my @d = reverse $t->dfs;
3005    my @c;
3006    my $h = $g->transpose;
3007    my $u =
3008	Graph::Traversal::DFS->new($h,
3009				   next_root => sub {
3010				       my ($t, $u) = @_;
3011				       my $root;
3012				       while (defined($root = shift @d)) {
3013					   last if exists $u->{ $root };
3014				       }
3015				       if (defined $root) {
3016					   push @c, [];
3017					   return $root;
3018				       } else {
3019					   return;
3020				       }
3021				   },
3022				   pre => sub {
3023				       my ($v, $t) = @_;
3024				       push @{ $c[-1] }, $v;
3025				   }
3026				   );
3027
3028    $u->dfs;
3029
3030    my $sc_cb;
3031    my $hv_cb;
3032
3033    _opt_get(\%attr, super_component => \$sc_cb);
3034    _opt_get(\%attr, hypervertex => \$hv_cb);
3035    _opt_unknown(\%attr);
3036
3037    if (defined $hv_cb && !defined $sc_cb) {
3038	$sc_cb = sub { $hv_cb->( [ @_ ] ) };
3039    }
3040    unless (defined $sc_cb) {
3041	$sc_cb = $super_component;
3042    }
3043
3044    my $s = Graph->new;
3045
3046    my %c;
3047    my @s;
3048    for (my $i = 0; $i <  @c; $i++) {
3049	my $c = $c[$i];
3050	$s->add_vertex( $s[$i] = $sc_cb->(@$c) );
3051	$s->set_vertex_attribute($s[$i], 'subvertices', [ @$c ]);
3052	for my $v (@$c) {
3053	    $c{$v} = $i;
3054	}
3055    }
3056
3057    my $n = @c;
3058    for my $v ($g->vertices) {
3059	unless (exists $c{$v}) {
3060	    $c{$v} = $n;
3061	    $s[$n] = $v;
3062	    $n++;
3063	}
3064    }
3065
3066    for my $e ($g->edges05) {
3067	my ($u, $v) = @$e; # @TODO: hyperedges
3068	unless ($c{$u} == $c{$v}) {
3069	    my ($p, $q) = ( $s[ $c{ $u } ], $s[ $c{ $v } ] );
3070	    $s->add_edge($p, $q) unless $s->has_edge($p, $q);
3071	}
3072    }
3073
3074    if (my @i = $g->isolated_vertices) {
3075	$s->add_vertices(map { $s[ $c{ $_ } ] } @i);
3076    }
3077
3078    return $s;
3079}
3080
3081###
3082# Biconnectivity.
3083#
3084
3085sub _make_bcc {
3086    my ($S, $v, $c) = @_;
3087    my %b;
3088    while (@$S) {
3089	my $t = pop @$S;
3090	$b{ $t } = $t;
3091	last if $t eq $v;
3092    }
3093    return [ values %b, $c ];
3094}
3095
3096sub _biconnectivity_compute {
3097    my $g = shift;
3098    my ($opt, $unseenh, $unseena, $r, $next, $code, $attr) =
3099	$g->_root_opt(@_);
3100    return () unless defined $r;
3101    my %P;
3102    my %I;
3103    for my $v ($g->vertices) {
3104	$I{ $v } = 0;
3105    }
3106    $I{ $r } = 1;
3107    my %U;
3108    my %S; # Self-loops.
3109    for my $e ($g->edges) {
3110	my ($u, $v) = @$e;
3111	$U{ $u }{ $v } = 0;
3112	$U{ $v }{ $u } = 0;
3113	$S{ $u } = 1 if $u eq $v;
3114    }
3115    my $i = 1;
3116    my $v = $r;
3117    my %AP;
3118    my %L = ( $r => 1 );
3119    my @S = ( $r );
3120    my %A;
3121    my @V = $g->vertices;
3122
3123    # print "V : @V\n";
3124    # print "r : $r\n";
3125
3126    my %T; @T{ @V } = @V;
3127
3128    for my $w (@V) {
3129	my @s = $g->successors( $w );
3130	if (@s) {
3131	    @s = grep { $_ eq $w ? ( delete $T{ $w }, 0 ) : 1 } @s;
3132	    @{ $A{ $w } }{ @s } = @s;
3133	} elsif ($g->predecessors( $w ) == 0) {
3134	    delete $T{ $w };
3135	    if ($w eq $r) {
3136		delete $I { $r };
3137		$r = $v = each %T;
3138		if (defined $r) {
3139		    %L = ( $r => 1 );
3140		    @S = ( $r );
3141		    $I{ $r } = 1;
3142		    # print "r : $r\n";
3143		}
3144	    }
3145	}
3146    }
3147
3148    # use Data::Dumper;
3149    # print "T : ", Dumper(\%T);
3150    # print "A : ", Dumper(\%A);
3151
3152    my %V2BC;
3153    my @BR;
3154    my @BC;
3155
3156    my @C;
3157    my $Avok;
3158
3159    while (keys %T) {
3160	# print "T = ", Dumper(\%T);
3161	do {
3162	    my $w;
3163	    do {
3164		my @w = _shuffle values %{ $A{ $v } };
3165		# print "w = @w\n";
3166		$w = first { !$U{ $v }{ $_ } } @w;
3167		if (defined $w) {
3168		    # print "w = $w\n";
3169		    $U{ $v }{ $w }++;
3170		    $U{ $w }{ $v }++;
3171		    if ($I{ $w } == 0) {
3172			$P{ $w } = $v;
3173			$i++;
3174			$I{ $w } = $i;
3175			$L{ $w } = $i;
3176			push @S, $w;
3177			$v = $w;
3178		    } else {
3179			$L{ $v } = $I{ $w } if $I{ $w } < $L{ $v };
3180		    }
3181		}
3182	    } while (defined $w);
3183	    # print "U = ", Dumper(\%U);
3184	    # print "P = ", Dumper(\%P);
3185	    # print "L = ", Dumper(\%L);
3186	    if (!defined $P{ $v }) {
3187		# Do nothing.
3188	    } elsif ($P{ $v } ne $r) {
3189		if ($L{ $v } < $I{ $P{ $v } }) {
3190		    $L{ $P{ $v } } = $L{ $v } if $L{ $v } < $L{ $P{ $v } };
3191		} else {
3192		    $AP{ $P{ $v } } = $P{ $v };
3193		    push @C, _make_bcc(\@S, $v, $P{ $v } );
3194		}
3195	    } else {
3196		my $e;
3197		for my $w (_shuffle keys %{ $A{ $r } }) {
3198		    # print "w = $w\n";
3199		    unless ($U{ $r }{ $w }) {
3200			$e = $r;
3201			# print "e = $e\n";
3202			last;
3203		    }
3204		}
3205		$AP{ $e } = $e if defined $e;
3206		push @C, _make_bcc(\@S, $v, $r);
3207	    }
3208	    # print "AP = ", Dumper(\%AP);
3209	    # print "C  = ", Dumper(\@C);
3210	    # print "L  = ", Dumper(\%L);
3211	    $v = defined $P{ $v } ? $P{ $v } : $r;
3212	    # print "v = $v\n";
3213	    $Avok = 0;
3214	    if (defined $v) {
3215		if (keys %{ $A{ $v } }) {
3216		    if (!exists $P{ $v }) {
3217			for my $w (keys %{ $A{ $v } }) {
3218			    $Avok++ if $U{ $v }{ $w };
3219			}
3220			# print "Avok/1 = $Avok\n";
3221			$Avok = 0 unless $Avok == keys %{ $A{ $v } };
3222			# print "Avok/2 = $Avok\n";
3223		    }
3224		} else {
3225		    $Avok = 1;
3226		    # print "Avok/3 = $Avok\n";
3227		}
3228	    }
3229	} until ($Avok);
3230
3231	last if @C == 0 && !exists $S{$v};
3232
3233	for (my $i = 0; $i < @C; $i++) {
3234	    for my $v (@{ $C[ $i ]}) {
3235		$V2BC{ $v }{ $i }++;
3236		delete $T{ $v };
3237	    }
3238	}
3239
3240	for (my $i = 0; $i < @C; $i++) {
3241	    if (@{ $C[ $i ] } == 2) {
3242		push @BR, $C[ $i ];
3243	    } else {
3244		push @BC, $C[ $i ];
3245	    }
3246	}
3247
3248	if (keys %T) {
3249	    $r = $v = each %T;
3250	}
3251    }
3252
3253    return [ [values %AP], \@BC, \@BR, \%V2BC ];
3254}
3255
3256sub biconnectivity {
3257    my $g = shift;
3258    $g->expect_undirected;
3259    my $bcc = _check_cache($g, 'biconnectivity',
3260			   \&_biconnectivity_compute, @_);
3261    return defined $bcc ? @$bcc : ( );
3262}
3263
3264sub is_biconnected {
3265    my $g = shift;
3266    my ($ap, $bc) = ($g->biconnectivity(@_))[0, 1];
3267    return defined $ap ? @$ap == 0 && $g->vertices >= 3 : undef;
3268}
3269
3270sub is_edge_connected {
3271    my $g = shift;
3272    my ($br) = ($g->biconnectivity(@_))[2];
3273    return defined $br ? @$br == 0 && $g->edges : undef;
3274}
3275
3276sub is_edge_separable {
3277    my $g = shift;
3278    my $c = $g->is_edge_connected;
3279    defined $c ? !$c && $g->edges : undef;
3280}
3281
3282sub articulation_points {
3283    my $g = shift;
3284    my ($ap) = ($g->biconnectivity(@_))[0];
3285    return defined $ap ? @$ap : ();
3286}
3287
3288*cut_vertices = \&articulation_points;
3289
3290sub biconnected_components {
3291    my $g = shift;
3292    my ($bc) = ($g->biconnectivity(@_))[1];
3293    return defined $bc ? @$bc : ();
3294}
3295
3296sub biconnected_component_by_index {
3297    my $g = shift;
3298    my $i = shift;
3299    my ($bc) = ($g->biconnectivity(@_))[1];
3300    return defined $bc ? $bc->[ $i ] : undef;
3301}
3302
3303sub biconnected_component_by_vertex {
3304    my $g = shift;
3305    my $v = shift;
3306    my ($v2bc) = ($g->biconnectivity(@_))[3];
3307    return defined $v2bc->{ $v } ? keys %{ $v2bc->{ $v } } : ();
3308}
3309
3310sub same_biconnected_components {
3311    my $g = shift;
3312    my $u = shift;
3313    my @u = $g->biconnected_component_by_vertex($u, @_);
3314    return 0 unless @u;
3315    my %ubc; @ubc{ @u } = ();
3316    while (@_) {
3317	my $v = shift;
3318	my @v = $g->biconnected_component_by_vertex($v);
3319	if (@v) {
3320	    my %vbc; @vbc{ @v } = ();
3321	    my $vi;
3322	    for my $ui (keys %ubc) {
3323		if (exists $vbc{ $ui }) {
3324		    $vi = $ui;
3325		    last;
3326		}
3327	    }
3328	    return 0 unless defined $vi;
3329	}
3330    }
3331    return 1;
3332}
3333
3334sub biconnected_graph {
3335    my ($g, %opt) = @_;
3336    my ($bc, $v2bc) = ($g->biconnectivity, %opt)[1, 3];
3337    my $bcg = Graph::Undirected->new;
3338    my $sc_cb =
3339	exists $opt{super_component} ?
3340	    $opt{super_component} : $super_component;
3341    for my $c (@$bc) {
3342	$bcg->add_vertex(my $s = $sc_cb->(@$c));
3343	$bcg->set_vertex_attribute($s, 'subvertices', [ @$c ]);
3344    }
3345    my %k;
3346    for my $i (0..$#$bc) {
3347	my @u = @{ $bc->[ $i ] };
3348	my %i; @i{ @u } = ();
3349	for my $j (0..$#$bc) {
3350	    if ($i > $j) {
3351		my @v = @{ $bc->[ $j ] };
3352		my %j; @j{ @v } = ();
3353		for my $u (@u) {
3354		    if (exists $j{ $u }) {
3355			unless ($k{ $i }{ $j }++) {
3356			    $bcg->add_edge($sc_cb->(@{$bc->[$i]}),
3357					   $sc_cb->(@{$bc->[$j]}));
3358			}
3359			last;
3360		    }
3361		}
3362	    }
3363	}
3364    }
3365    return $bcg;
3366}
3367
3368sub bridges {
3369    my $g = shift;
3370    my ($br) = ($g->biconnectivity(@_))[2];
3371    return defined $br ? @$br : ();
3372}
3373
3374###
3375# SPT.
3376#
3377
3378sub _SPT_add {
3379    my ($g, $h, $HF, $r, $attr, $unseen, $etc) = @_;
3380    my $etc_r = $etc->{ $r } || 0;
3381    for my $s ( grep { exists $unseen->{ $_ } } $g->successors( $r ) ) {
3382	my $t = $g->get_edge_attribute( $r, $s, $attr );
3383	$t = 1 unless defined $t;
3384	if ($t < 0) {
3385	    require Carp;
3386	    Carp::croak("Graph::SPT_Dijkstra: edge $r-$s is negative ($t)");
3387	}
3388	if (!defined($etc->{ $s }) || ($etc_r + $t) < $etc->{ $s }) {
3389	    my $etc_s = $etc->{ $s } || 0;
3390	    $etc->{ $s } = $etc_r + $t;
3391	    # print "$r - $s : setting $s to $etc->{ $s } ($etc_r, $etc_s)\n";
3392	    $h->set_vertex_attribute( $s, $attr, $etc->{ $s });
3393	    $h->set_vertex_attribute( $s, 'p', $r );
3394	    $HF->add( Graph::SPTHeapElem->new($r, $s, $etc->{ $s }) );
3395	}
3396    }
3397}
3398
3399sub _SPT_Dijkstra_compute {
3400}
3401
3402sub SPT_Dijkstra {
3403    my $g = shift;
3404    my %opt = @_ == 1 ? (first_root => $_[0]) : @_;
3405    my $first_root = $opt{ first_root };
3406    unless (defined $first_root) {
3407	$opt{ first_root } = $first_root = $g->random_vertex();
3408    }
3409    my $spt_di = $g->get_graph_attribute('_spt_di');
3410    unless (defined $spt_di && exists $spt_di->{ $first_root } && $spt_di->{ $first_root }->[ 0 ] == $g->[ _G ]) {
3411	my %etc;
3412	my $sptg = $g->_heap_walk($g->new, \&_SPT_add, \%etc, %opt);
3413	$spt_di->{ $first_root } = [ $g->[ _G ], $sptg ];
3414	$g->set_graph_attribute('_spt_di', $spt_di);
3415    }
3416
3417    my $spt = $spt_di->{ $first_root }->[ 1 ];
3418
3419    $spt->set_graph_attribute('SPT_Dijkstra_root', $first_root);
3420
3421    return $spt;
3422}
3423
3424*SSSP_Dijkstra = \&SPT_Dijkstra;
3425
3426*single_source_shortest_paths = \&SPT_Dijkstra;
3427
3428sub SP_Dijkstra {
3429    my ($g, $u, $v) = @_;
3430    my $sptg = $g->SPT_Dijkstra(first_root => $u);
3431    my @path = ($v);
3432    my %seen;
3433    my $V = $g->vertices;
3434    my $p;
3435    while (defined($p = $sptg->get_vertex_attribute($v, 'p'))) {
3436	last if exists $seen{$p};
3437	push @path, $p;
3438	$v = $p;
3439	$seen{$p}++;
3440	last if keys %seen == $V || $u eq $v;
3441    }
3442    @path = () if @path && $path[-1] ne $u;
3443    return reverse @path;
3444}
3445
3446sub __SPT_Bellman_Ford {
3447    my ($g, $u, $v, $attr, $d, $p, $c0, $c1) = @_;
3448    return unless $c0->{ $u };
3449    my $w = $g->get_edge_attribute($u, $v, $attr);
3450    $w = 1 unless defined $w;
3451    if (defined $d->{ $v }) {
3452	if (defined $d->{ $u }) {
3453	    if ($d->{ $v } > $d->{ $u } + $w) {
3454		$d->{ $v } = $d->{ $u } + $w;
3455		$p->{ $v } = $u;
3456		$c1->{ $v }++;
3457	    }
3458	} # else !defined $d->{ $u } &&  defined $d->{ $v }
3459    } else {
3460	if (defined $d->{ $u }) {
3461	    #  defined $d->{ $u } && !defined $d->{ $v }
3462	    $d->{ $v } = $d->{ $u } + $w;
3463	    $p->{ $v } = $u;
3464	    $c1->{ $v }++;
3465	} # else !defined $d->{ $u } && !defined $d->{ $v }
3466    }
3467}
3468
3469sub _SPT_Bellman_Ford {
3470    my ($g, $opt, $unseenh, $unseena, $r, $next, $code, $attr) = @_;
3471    my %d;
3472    return unless defined $r;
3473    $d{ $r } = 0;
3474    my %p;
3475    my $V = $g->vertices;
3476    my %c0; # Changed during the last iteration?
3477    $c0{ $r }++;
3478    for (my $i = 0; $i < $V; $i++) {
3479	my %c1;
3480	for my $e ($g->edges) {
3481	    my ($u, $v) = @$e;
3482	    __SPT_Bellman_Ford($g, $u, $v, $attr, \%d, \%p, \%c0, \%c1);
3483	    if ($g->undirected) {
3484		__SPT_Bellman_Ford($g, $v, $u, $attr, \%d, \%p, \%c0, \%c1);
3485	    }
3486	}
3487	%c0 = %c1 unless $i == $V - 1;
3488    }
3489
3490    for my $e ($g->edges) {
3491	my ($u, $v) = @$e;
3492	if (defined $d{ $u } && defined $d{ $v }) {
3493	    my $d = $g->get_edge_attribute($u, $v, $attr);
3494	    if (defined $d && $d{ $v } > $d{ $u } + $d) {
3495		require Carp;
3496		Carp::croak("Graph::SPT_Bellman_Ford: negative cycle exists");
3497	    }
3498	}
3499    }
3500
3501    return (\%p, \%d);
3502}
3503
3504sub _SPT_Bellman_Ford_compute {
3505}
3506
3507sub SPT_Bellman_Ford {
3508    my $g = shift;
3509
3510    my ($opt, $unseenh, $unseena, $r, $next, $code, $attr) = $g->_root_opt(@_);
3511
3512    unless (defined $r) {
3513	$r = $g->random_vertex();
3514	return unless defined $r;
3515    }
3516
3517    my $spt_bf = $g->get_graph_attribute('_spt_bf');
3518    unless (defined $spt_bf &&
3519	    exists $spt_bf->{ $r } && $spt_bf->{ $r }->[ 0 ] == $g->[ _G ]) {
3520	my ($p, $d) =
3521	    $g->_SPT_Bellman_Ford($opt, $unseenh, $unseena,
3522				  $r, $next, $code, $attr);
3523	my $h = $g->new;
3524	for my $v (keys %$p) {
3525	    my $u = $p->{ $v };
3526	    $h->add_edge( $u, $v );
3527	    $h->set_edge_attribute( $u, $v, $attr,
3528				    $g->get_edge_attribute($u, $v, $attr));
3529	    $h->set_vertex_attribute( $v, $attr, $d->{ $v } );
3530	    $h->set_vertex_attribute( $v, 'p', $u );
3531	}
3532	$spt_bf->{ $r } = [ $g->[ _G ], $h ];
3533	$g->set_graph_attribute('_spt_bf', $spt_bf);
3534    }
3535
3536    my $spt = $spt_bf->{ $r }->[ 1 ];
3537
3538    $spt->set_graph_attribute('SPT_Bellman_Ford_root', $r);
3539
3540    return $spt;
3541}
3542
3543*SSSP_Bellman_Ford = \&SPT_Bellman_Ford;
3544
3545sub SP_Bellman_Ford {
3546    my ($g, $u, $v) = @_;
3547    my $sptg = $g->SPT_Bellman_Ford(first_root => $u);
3548    my @path = ($v);
3549    my %seen;
3550    my $V = $g->vertices;
3551    my $p;
3552    while (defined($p = $sptg->get_vertex_attribute($v, 'p'))) {
3553	last if exists $seen{$p};
3554	push @path, $p;
3555	$v = $p;
3556	$seen{$p}++;
3557	last if keys %seen == $V;
3558    }
3559    # @path = () if @path && "$path[-1]" ne "$u";
3560    return reverse @path;
3561}
3562
3563###
3564# Transitive Closure.
3565#
3566
3567sub TransitiveClosure_Floyd_Warshall {
3568    my $self = shift;
3569    my $class = ref $self || $self;
3570    $self = shift unless ref $self;
3571    bless Graph::TransitiveClosure->new($self, @_), $class;
3572}
3573
3574*transitive_closure = \&TransitiveClosure_Floyd_Warshall;
3575
3576sub APSP_Floyd_Warshall {
3577    my $self = shift;
3578    my $class = ref $self || $self;
3579    $self = shift unless ref $self;
3580    bless Graph::TransitiveClosure->new($self, path => 1, @_), $class;
3581}
3582
3583*all_pairs_shortest_paths = \&APSP_Floyd_Warshall;
3584
3585sub _transitive_closure_matrix_compute {
3586}
3587
3588sub transitive_closure_matrix {
3589    my $g = shift;
3590    my $tcm = $g->get_graph_attribute('_tcm');
3591    if (defined $tcm) {
3592	if (ref $tcm eq 'ARRAY') { # YECHHH!
3593	    if ($tcm->[ 0 ] == $g->[ _G ]) {
3594		$tcm = $tcm->[ 1 ];
3595	    } else {
3596		undef $tcm;
3597	    }
3598	}
3599    }
3600    unless (defined $tcm) {
3601	my $apsp = $g->APSP_Floyd_Warshall(@_);
3602	$tcm = $apsp->get_graph_attribute('_tcm');
3603	$g->set_graph_attribute('_tcm', [ $g->[ _G ], $tcm ]);
3604    }
3605
3606    return $tcm;
3607}
3608
3609sub path_length {
3610    my $g = shift;
3611    my $tcm = $g->transitive_closure_matrix;
3612    $tcm->path_length(@_);
3613}
3614
3615sub path_predecessor {
3616    my $g = shift;
3617    my $tcm = $g->transitive_closure_matrix;
3618    $tcm->path_predecessor(@_);
3619}
3620
3621sub path_vertices {
3622    my $g = shift;
3623    my $tcm = $g->transitive_closure_matrix;
3624    $tcm->path_vertices(@_);
3625}
3626
3627sub is_reachable {
3628    my $g = shift;
3629    my $tcm = $g->transitive_closure_matrix;
3630    $tcm->is_reachable(@_);
3631}
3632
3633sub for_shortest_paths {
3634    my $g = shift;
3635    my $c = shift;
3636    my $t = $g->transitive_closure_matrix;
3637    my @v = $g->vertices;
3638    my $n = 0;
3639    for my $u (@v) {
3640	for my $v (@v) {
3641	    next unless $t->is_reachable($u, $v);
3642	    $n++;
3643	    $c->($t, $u, $v, $n);
3644	}
3645    }
3646    return $n;
3647}
3648
3649sub _minmax_path {
3650    my $g = shift;
3651    my $min;
3652    my $max;
3653    my $minp;
3654    my $maxp;
3655    $g->for_shortest_paths(sub {
3656			       my ($t, $u, $v, $n) = @_;
3657			       my $l = $t->path_length($u, $v);
3658			       return unless defined $l;
3659			       my $p;
3660			       if ($u ne $v && (!defined $max || $l > $max)) {
3661				   $max = $l;
3662				   $maxp = $p = [ $t->path_vertices($u, $v) ];
3663			       }
3664			       if ($u ne $v && (!defined $min || $l < $min)) {
3665				   $min = $l;
3666				   $minp = $p || [ $t->path_vertices($u, $v) ];
3667			       }
3668			   });
3669    return ($min, $max, $minp, $maxp);
3670}
3671
3672sub diameter {
3673    my $g = shift;
3674    my ($min, $max, $minp, $maxp) = $g->_minmax_path(@_);
3675    return defined $maxp ? (wantarray ? @$maxp : $max) : undef;
3676}
3677
3678*graph_diameter = \&diameter;
3679
3680sub longest_path {
3681    my ($g, $u, $v) = @_;
3682    my $t = $g->transitive_closure_matrix;
3683    if (defined $u) {
3684	if (defined $v) {
3685	    return wantarray ?
3686		$t->path_vertices($u, $v) : $t->path_length($u, $v);
3687	} else {
3688	    my $max;
3689	    my @max;
3690	    for my $v ($g->vertices) {
3691		next if $u eq $v;
3692		my $l = $t->path_length($u, $v);
3693		if (defined $l && (!defined $max || $l > $max)) {
3694		    $max = $l;
3695		    @max = $t->path_vertices($u, $v);
3696		}
3697	    }
3698	    return wantarray ? @max : $max;
3699	}
3700    } else {
3701	if (defined $v) {
3702	    my $max;
3703	    my @max;
3704	    for my $u ($g->vertices) {
3705		next if $u eq $v;
3706		my $l = $t->path_length($u, $v);
3707		if (defined $l && (!defined $max || $l > $max)) {
3708		    $max = $l;
3709		    @max = $t->path_vertices($u, $v);
3710		}
3711	    }
3712	    return wantarray ? @max : @max - 1;
3713	} else {
3714	    my ($min, $max, $minp, $maxp) = $g->_minmax_path(@_);
3715	    return defined $maxp ? (wantarray ? @$maxp : $max) : undef;
3716	}
3717    }
3718}
3719
3720sub vertex_eccentricity {
3721    my ($g, $u) = @_;
3722    $g->expect_undirected;
3723    if ($g->is_connected) {
3724	my $max;
3725	for my $v ($g->vertices) {
3726	    next if $u eq $v;
3727	    my $l = $g->path_length($u, $v);
3728	    if (defined $l && (!defined $max || $l > $max)) {
3729		$max = $l;
3730	    }
3731	}
3732	return $max;
3733    } else {
3734	return Infinity();
3735    }
3736}
3737
3738sub shortest_path {
3739    my ($g, $u, $v) = @_;
3740    $g->expect_undirected;
3741    my $t = $g->transitive_closure_matrix;
3742    if (defined $u) {
3743	if (defined $v) {
3744	    return wantarray ?
3745		$t->path_vertices($u, $v) : $t->path_length($u, $v);
3746	} else {
3747	    my $min;
3748	    my @min;
3749	    for my $v ($g->vertices) {
3750		next if $u eq $v;
3751		my $l = $t->path_length($u, $v);
3752		if (defined $l && (!defined $min || $l < $min)) {
3753		    $min = $l;
3754		    @min = $t->path_vertices($u, $v);
3755		}
3756	    }
3757	    return wantarray ? @min : $min;
3758	}
3759    } else {
3760	if (defined $v) {
3761	    my $min;
3762	    my @min;
3763	    for my $u ($g->vertices) {
3764		next if $u eq $v;
3765		my $l = $t->path_length($u, $v);
3766		if (defined $l && (!defined $min || $l < $min)) {
3767		    $min = $l;
3768		    @min = $t->path_vertices($u, $v);
3769		}
3770	    }
3771	    return wantarray ? @min : $min;
3772	} else {
3773	    my ($min, $max, $minp, $maxp) = $g->_minmax_path(@_);
3774	    return defined $minp ? (wantarray ? @$minp : $min) : undef;
3775	}
3776    }
3777}
3778
3779sub radius {
3780    my $g = shift;
3781    $g->expect_undirected;
3782    my ($center, $radius) = (undef, Infinity());
3783    for my $v ($g->vertices) {
3784	my $x = $g->vertex_eccentricity($v);
3785	($center, $radius) = ($v, $x) if defined $x && $x < $radius;
3786    }
3787    return $radius;
3788}
3789
3790sub center_vertices {
3791    my ($g, $delta) = @_;
3792    $g->expect_undirected;
3793    $delta = 0 unless defined $delta;
3794    $delta = abs($delta);
3795    my @c;
3796    my $r = $g->radius;
3797    if (defined $r) {
3798	for my $v ($g->vertices) {
3799	    my $e = $g->vertex_eccentricity($v);
3800	    next unless defined $e;
3801	    push @c, $v if abs($e - $r) <= $delta;
3802	}
3803    }
3804    return @c;
3805}
3806
3807*centre_vertices = \&center_vertices;
3808
3809sub average_path_length {
3810    my $g = shift;
3811    my @A = @_;
3812    my $d = 0;
3813    my $m = 0;
3814    my $n = $g->for_shortest_paths(sub {
3815				       my ($t, $u, $v, $n) = @_;
3816				       my $l = $t->path_length($u, $v);
3817				       if ($l) {
3818					   my $c = @A == 0 ||
3819					       (@A == 1 && $u eq $A[0]) ||
3820						   ((@A == 2) &&
3821						    (defined $A[0] &&
3822						     $u eq $A[0]) ||
3823						    (defined $A[1] &&
3824						     $v eq $A[1]));
3825					   if ($c) {
3826					       $d += $l;
3827					       $m++;
3828					   }
3829				       }
3830				   });
3831    return $m ? $d / $m : undef;
3832}
3833
3834###
3835# Simple tests.
3836#
3837
3838sub is_multi_graph {
3839    my $g = shift;
3840    return 0 unless $g->is_multiedged || $g->is_countedged;
3841    my $multiedges = 0;
3842    for my $e ($g->edges05) {
3843	my ($u, @v) = @$e;
3844	for my $v (@v) {
3845	    return 0 if $u eq $v;
3846	}
3847	$multiedges++ if $g->get_edge_count(@$e) > 1;
3848    }
3849    return $multiedges;
3850}
3851
3852sub is_simple_graph {
3853    my $g = shift;
3854    return 1 unless $g->is_countedged || $g->is_multiedged;
3855    for my $e ($g->edges05) {
3856	return 0 if $g->get_edge_count(@$e) > 1;
3857    }
3858    return 1;
3859}
3860
3861sub is_pseudo_graph {
3862    my $g = shift;
3863    my $m = $g->is_countedged || $g->is_multiedged;
3864    for my $e ($g->edges05) {
3865	my ($u, @v) = @$e;
3866	for my $v (@v) {
3867	    return 1 if $u eq $v;
3868	}
3869	return 1 if $m && $g->get_edge_count($u, @v) > 1;
3870    }
3871    return 0;
3872}
3873
3874###
3875# Rough isomorphism guess.
3876#
3877
3878my %_factorial = (0 => 1, 1 => 1);
3879
3880sub __factorial {
3881    my $n = shift;
3882    for (my $i = 2; $i <= $n; $i++) {
3883	next if exists $_factorial{$i};
3884	$_factorial{$i} = $i * $_factorial{$i - 1};
3885    }
3886    $_factorial{$n};
3887}
3888
3889sub _factorial {
3890    my $n = int(shift);
3891    if ($n < 0) {
3892	require Carp;
3893	Carp::croak("factorial of a negative number");
3894    }
3895    __factorial($n) unless exists $_factorial{$n};
3896    return $_factorial{$n};
3897}
3898
3899sub could_be_isomorphic {
3900    my ($g0, $g1) = @_;
3901    return 0 unless $g0->vertices == $g1->vertices;
3902    return 0 unless $g0->edges05  == $g1->edges05;
3903    my %d0;
3904    for my $v0 ($g0->vertices) {
3905	$d0{ $g0->in_degree($v0) }{ $g0->out_degree($v0) }++
3906    }
3907    my %d1;
3908    for my $v1 ($g1->vertices) {
3909	$d1{ $g1->in_degree($v1) }{ $g1->out_degree($v1) }++
3910    }
3911    return 0 unless keys %d0 == keys %d1;
3912    for my $da (keys %d0) {
3913	return 0
3914	    unless exists $d1{$da} &&
3915		   keys %{ $d0{$da} } == keys %{ $d1{$da} };
3916	for my $db (keys %{ $d0{$da} }) {
3917	    return 0
3918		unless exists $d1{$da}{$db} &&
3919		       $d0{$da}{$db} == $d1{$da}{$db};
3920	}
3921    }
3922    for my $da (keys %d0) {
3923	for my $db (keys %{ $d0{$da} }) {
3924	    return 0 unless $d1{$da}{$db} == $d0{$da}{$db};
3925	}
3926	delete $d1{$da};
3927    }
3928    return 0 unless keys %d1 == 0;
3929    my $f = 1;
3930    for my $da (keys %d0) {
3931	for my $db (keys %{ $d0{$da} }) {
3932	    $f *= _factorial(abs($d0{$da}{$db}));
3933	}
3934    }
3935    return $f;
3936}
3937
3938###
3939# Analysis functions.
3940
3941sub subgraph_by_radius
3942{
3943    my ($g, $n, $rad) = @_;
3944
3945    return unless defined $n && defined $rad && $rad >= 0;
3946
3947    my $r = (ref $g)->new;
3948
3949    if ($rad == 0) {
3950	return $r->add_vertex($n);
3951    }
3952
3953    my %h;
3954    $h{1} = [ [ $n, $g->successors($n) ] ];
3955    for my $i (1..$rad) {
3956	$h{$i+1} = [];
3957	for my $arr (@{ $h{$i} }) {
3958	    my ($p, @succ) = @{ $arr };
3959	    for my $s (@succ) {
3960		$r->add_edge($p, $s);
3961		push(@{ $h{$i+1} }, [$s, $g->successors($s)]) if $i < $rad;
3962	    }
3963	}
3964    }
3965
3966    return $r;
3967}
3968
3969sub clustering_coefficient {
3970    my ($g) = @_;
3971    my %clustering;
3972
3973    my $gamma = 0;
3974
3975    for my $n ($g->vertices()) {
3976	my $gamma_v = 0;
3977	my @neigh = $g->successors($n);
3978	my %c;
3979	for my $u (@neigh) {
3980	    for my $v (@neigh) {
3981		if (!$c{"$u-$v"} && $g->has_edge($u, $v)) {
3982		    $gamma_v++;
3983		    $c{"$u-$v"} = 1;
3984		    $c{"$v-$u"} = 1;
3985		}
3986	    }
3987	}
3988	if (@neigh > 1) {
3989	    $clustering{$n} = $gamma_v/(@neigh * (@neigh - 1) / 2);
3990	    $gamma += $gamma_v/(@neigh * (@neigh - 1) / 2);
3991	} else {
3992	    $clustering{$n} = 0;
3993	}
3994    }
3995
3996    $gamma /= $g->vertices();
3997
3998    return wantarray ? ($gamma, %clustering) : $gamma;
3999}
4000
4001sub betweenness {
4002    my $g = shift;
4003
4004    my @V = $g->vertices();
4005
4006    my %Cb; # C_b{w} = 0
4007
4008    $Cb{$_} = 0 for @V;
4009
4010    for my $s (@V) {
4011	my @S; # stack (unshift, shift)
4012
4013	my %P; # P{w} = empty list
4014	$P{$_} = [] for @V;
4015
4016	my %sigma; # \sigma{t} = 0
4017	$sigma{$_} = 0 for @V;
4018	$sigma{$s} = 1;
4019
4020	my %d; # d{t} = -1;
4021	$d{$_} = -1 for @V;
4022	$d{$s} = 0;
4023
4024	my @Q; # queue (push, shift)
4025	push @Q, $s;
4026
4027	while (@Q) {
4028	    my $v = shift @Q;
4029	    unshift @S, $v;
4030	    for my $w ($g->successors($v)) {
4031		# w found for first time
4032		if ($d{$w} < 0) {
4033		    push @Q, $w;
4034		    $d{$w} = $d{$v} + 1;
4035		}
4036		# Shortest path to w via v
4037		if ($d{$w} == $d{$v} + 1) {
4038		    $sigma{$w} += $sigma{$v};
4039		    push @{ $P{$w} }, $v;
4040		}
4041	    }
4042	}
4043
4044	my %delta;
4045	$delta{$_} = 0 for @V;
4046
4047	while (@S) {
4048	    my $w = shift @S;
4049	    for my $v (@{ $P{$w} }) {
4050		$delta{$v} += $sigma{$v}/$sigma{$w} * (1 + $delta{$w});
4051	    }
4052	    if ($w ne $s) {
4053		$Cb{$w} += $delta{$w};
4054	    }
4055	}
4056    }
4057
4058    return %Cb;
4059}
4060
4061###
4062# Debugging.
4063#
4064
4065sub _dump {
4066    require Data::Dumper;
4067    my $d = Data::Dumper->new([$_[0]],[ref $_[0]]);
4068    defined wantarray ? $d->Dump : print $d->Dump;
4069}
4070
40711;
4072