6 if (0) { # SET THIS TO ZERO FOR TESTING AND RELEASES!
7 $SIG{__DIE__
} = \
&__carp_confess
;
8 $SIG{__WARN__
} = \
&__carp_confess
;
10 sub __carp_confess
{ require Carp
; Carp
::confess
(@_) }
13 use Graph
::AdjacencyMap
qw(:flags :fields);
15 use vars
qw($VERSION);
19 require 5.006; # Weak references are absolutely required.
21 use Graph::AdjacencyMap::Heavy;
22 use Graph::AdjacencyMap::Light;
23 use Graph::AdjacencyMap::Vertex;
25 use Graph::TransitiveClosure;
26 use Graph::Traversal::DFS;
27 use Graph::MSTHeapElem;
28 use Graph::SPTHeapElem;
29 use Graph::Undirected;
31 use Heap071::Fibonacci;
32 use List::Util qw(shuffle first);
33 use Scalar
::Util
qw(weaken);
35 sub _F
() { 0 } # Flags.
36 sub _G
() { 1 } # Generation.
37 sub _V
() { 2 } # Vertices.
38 sub _E
() { 3 } # Edges.
39 sub _A
() { 4 } # Attributes.
40 sub _U
() { 5 } # Union-Find.
46 eval { $Inf = exp(999) } ||
47 eval { $Inf = 9**9**9 } ||
48 eval { $Inf = 1e+999 } ||
49 { $Inf = 1e+99 }; # Close enough for most practical purposes.
52 sub Infinity
() { $Inf }
54 # Graphs are blessed array references.
55 # - The first element contains the flags.
56 # - The second element is the vertices.
57 # - The third element is the edges.
58 # - The fourth element is the attributes of the whole graph.
59 # The defined flags for Graph are:
60 # - _COMPAT02 for user API compatibility with the Graph 0.20xxx series.
61 # The vertices are contained in either a "simplemap"
62 # (if no hypervertices) or in a "map".
63 # The edges are always in a "map".
64 # The defined flags for maps are:
65 # - _COUNT for countedness: more than one instance
66 # - _HYPER for hyperness: a different number of "coordinates" than usual;
67 # expects one for vertices and two for edges
68 # - _UNORD for unordered coordinates (a set): if _UNORD is not set
69 # the coordinates are assumed to be meaningfully ordered
70 # - _UNIQ for unique coordinates: if set duplicates are removed,
71 # if not, duplicates are assumed to meaningful
72 # - _UNORDUNIQ: just a union of _UNORD and UNIQ
73 # Vertices are assumed to be _UNORDUNIQ; edges assume none of these flags.
75 use Graph
::Attribute array
=> _A
, map => 'graph';
77 sub _COMPAT02
() { 0x00000001 }
81 my $o = $g->is_undirected;
82 my $e = $o ?
'=' : '-';
87 ref($_) eq 'ARRAY' ?
"[" . join(" ", @
$_) . "]" : "$_"
90 join($e, $o ?
sort { "$a" cmp "$b" } @v : @v) } $g->edges05;
91 my @s = sort { "$a" cmp "$b" } @e;
92 push @s, sort { "$a" cmp "$b" } $g->isolated_vertices;
110 my ($opt, $flags, %flags) = @_;
111 while (my ($flag, $FLAG) = each %flags) {
112 if (exists $opt->{$flag}) {
113 $$flags |= $FLAG if $opt->{$flag};
114 delete $opt->{$flag};
116 if (exists $opt->{my $non = "non$flag"}) {
117 $$flags &= ~$FLAG if $opt->{$non};
125 $g->[ _F
] & _COMPAT02
;
128 *compat02
= \
&is_compat02
;
132 ($g->[ _F
] & _UNIONFIND
) && defined $g->[ _U
];
135 sub _get_union_find
{
141 my ($opt, $key, $var) = @_;
142 if (exists $opt->{$key}) {
143 $$var = $opt->{$key};
150 if (my @opt = keys %$opt) {
151 my $f = (caller(1))[3];
153 Carp
::confess
(sprintf
154 "$f: Unknown option%s: @{[map { qq['$_'] } sort @opt]}",
155 @opt > 1 ?
's' : '');
164 my %opt = _get_options
( \
@_ );
166 if (ref $class && $class->isa('Graph')) {
168 for my $c (qw(undirected refvertexed compat02
169 hypervertexed countvertexed multivertexed
170 hyperedged countedged multiedged omniedged)) {
171 # $opt{$c}++ if $class->$c; # 5.00504-incompatible
172 if (&{"Graph::$c"}($class)) { $opt{$c}++ }
174 # $opt{unionfind}++ if $class->has_union_find; # 5.00504-incompatible
175 if (&{"Graph::has_union_find"}($class)) { $opt{unionfind
}++ }
178 _opt_get
(\
%opt, undirected
=> \
$opt{omniedged
});
179 _opt_get
(\
%opt, omnidirected
=> \
$opt{omniedged
});
181 if (exists $opt{directed
}) {
182 $opt{omniedged
} = !$opt{directed
};
183 delete $opt{directed
};
187 $opt{nonomnivertexed
} ||
188 (exists $opt{omnivertexed
} && !$opt{omnivertexed
});
190 $opt{nonuniqvertexed
} ||
191 (exists $opt{uniqvertexed
} && !$opt{uniqvertexed
});
193 _opt
(\
%opt, \
$vflags,
194 countvertexed
=> _COUNT
,
195 multivertexed
=> _MULTI
,
196 hypervertexed
=> _HYPER
,
197 omnivertexed
=> _UNORD
,
198 uniqvertexed
=> _UNIQ
,
202 _opt
(\
%opt, \
$eflags,
203 countedged
=> _COUNT
,
204 multiedged
=> _MULTI
,
205 hyperedged
=> _HYPER
,
210 _opt
(\
%opt, \
$gflags,
211 compat02
=> _COMPAT02
,
212 unionfind
=> _UNIONFIND
,
215 if (exists $opt{vertices_unsorted
}) { # Graph 0.20103 compat.
216 my $unsorted = $opt{vertices_unsorted
};
217 delete $opt{vertices_unsorted
};
219 Carp
::confess
("Graph: vertices_unsorted must be true")
224 if ($opt{vertices
}) {
226 Carp
::confess
("Graph: vertices should be an array ref")
227 unless ref $opt{vertices
} eq 'ARRAY';
228 @V = @
{ $opt{vertices
} };
229 delete $opt{vertices
};
234 unless (ref $opt{edges
} eq 'ARRAY') {
236 Carp
::confess
("Graph: edges should be an array ref of array refs");
238 @E = @
{ $opt{edges
} };
245 if (defined $vflags) {
247 $uflags |= _UNORD
unless $vnonomni;
248 $uflags |= _UNIQ
unless $vnonuniq;
250 $uflags = _UNORDUNIQ
;
254 if (!($vflags & _HYPER
) && ($vflags & _UNORDUNIQ
)) {
256 push @but, 'unordered' if ($vflags & _UNORD
);
257 push @but, 'unique' if ($vflags & _UNIQ
);
259 Carp
::confess
(sprintf "Graph: not hypervertexed but %s",
260 join(' and ', @but));
263 unless (defined $eflags) {
264 $eflags = ($gflags & _COMPAT02
) ? _COUNT
: 0;
267 if (!($vflags & _HYPER
) && ($vflags & _UNIQ
)) {
269 Carp
::confess
("Graph: not hypervertexed but uniqvertexed");
272 if (($vflags & _COUNT
) && ($vflags & _MULTI
)) {
274 Carp
::confess
("Graph: both countvertexed and multivertexed");
277 if (($eflags & _COUNT
) && ($eflags & _MULTI
)) {
279 Carp
::confess
("Graph: both countedged and multiedged");
282 my $g = bless [ ], ref $class || $class;
284 $g->[ _F
] = $gflags;
286 $g->[ _V
] = ($vflags & (_HYPER
| _MULTI
)) ?
287 Graph
::AdjacencyMap
::Heavy
->_new($uflags, 1) :
288 (($vflags & ~_UNORD
) ?
289 Graph
::AdjacencyMap
::Vertex
->_new($uflags, 1) :
290 Graph
::AdjacencyMap
::Light
->_new($g, $uflags, 1));
291 $g->[ _E
] = (($vflags & _HYPER
) || ($eflags & ~_UNORD
)) ?
292 Graph
::AdjacencyMap
::Heavy
->_new($eflags, 2) :
293 Graph
::AdjacencyMap
::Light
->_new($g, $eflags, 2);
295 $g->add_vertices(@V) if @V;
299 unless (ref $e eq 'ARRAY') {
301 Carp
::confess
("Graph: edges should be array refs");
307 if (($gflags & _UNIONFIND
)) {
308 $g->[ _U
] = Graph
::UnionFind
->new;
314 sub countvertexed
{ $_[0]->[ _V
]->_is_COUNT }
315 sub multivertexed
{ $_[0]->[ _V
]->_is_MULTI }
316 sub hypervertexed
{ $_[0]->[ _V
]->_is_HYPER }
317 sub omnivertexed
{ $_[0]->[ _V
]->_is_UNORD }
318 sub uniqvertexed
{ $_[0]->[ _V
]->_is_UNIQ }
319 sub refvertexed
{ $_[0]->[ _V
]->_is_REF }
321 sub countedged
{ $_[0]->[ _E
]->_is_COUNT }
322 sub multiedged
{ $_[0]->[ _E
]->_is_MULTI }
323 sub hyperedged
{ $_[0]->[ _E
]->_is_HYPER }
324 sub omniedged
{ $_[0]->[ _E
]->_is_UNORD }
325 sub uniqedged
{ $_[0]->[ _E
]->_is_UNIQ }
327 *undirected
= \
&omniedged
;
328 *omnidirected
= \
&omniedged
;
329 sub directed
{ ! $_[0]->[ _E
]->_is_UNORD }
331 *is_directed
= \
&directed
;
332 *is_undirected
= \
&undirected
;
334 *is_countvertexed
= \
&countvertexed
;
335 *is_multivertexed
= \
&multivertexed
;
336 *is_hypervertexed
= \
&hypervertexed
;
337 *is_omnidirected
= \
&omnidirected
;
338 *is_uniqvertexed
= \
&uniqvertexed
;
339 *is_refvertexed
= \
&refvertexed
;
341 *is_countedged
= \
&countedged
;
342 *is_multiedged
= \
&multiedged
;
343 *is_hyperedged
= \
&hyperedged
;
344 *is_omniedged
= \
&omniedged
;
345 *is_uniqedged
= \
&uniqedged
;
347 sub _union_find_add_vertex
{
350 $UF->add( $g->[ _V
]->_get_path_id( $v ) );
355 if ($g->is_multivertexed) {
356 return $g->add_vertex_by_id(@_, _GEN_ID
);
360 unless ($g->is_countvertexed || $g->is_hypervertexed) {
362 Carp
::croak
("Graph::add_vertex: use add_vertices for more than one vertex or use hypervertexed");
366 $g->[ _V
]->set_path( $v ) unless $g->has_vertex( $v );
369 Carp
::croak
("Graph::add_vertex: undef vertex");
374 unless (defined $v) {
376 Carp
::croak
("Graph::add_vertex: undef vertex");
379 $g->[ _V
]->set_path( @_ );
381 $g->_union_find_add_vertex( @_ ) if $g->has_union_find;
388 return exists $V->[ _s
]->{ $_[0] } if ($V->[ _f
] & _LIGHT
);
394 my @v = $g->[ _V
]->paths( @_ );
396 return $g->[ _V
]->_is_HYPER ?
397 @v : map { ref $_ eq 'ARRAY' ? @
$_ : $_ } @v;
405 my @v = $g->vertices05;
406 if ($g->is_compat02) {
407 wantarray ?
sort @v : scalar @v;
409 if ($g->is_multivertexed || $g->is_countvertexed) {
413 push @V, ($v) x
$g->get_vertex_count($v);
419 $V += $g->get_vertex_count($v);
429 *vertices_unsorted
= \
&vertices_unsorted
; # Graph 0.20103 compat.
431 sub unique_vertices
{
433 my @v = $g->vertices05;
434 if ($g->is_compat02) {
435 wantarray ?
sort @v : scalar @v;
443 scalar $g->[ _V
]->has_paths( @_ );
450 if (($V->[ _f
]) & _LIGHT
) {
452 $g->add_vertex( $v ) unless exists $V->[ _s
]->{ $v };
453 push @e, $V->[ _s
]->{ $v };
456 my $h = $g->[ _V
]->_is_HYPER;
458 my @v = ref $v eq 'ARRAY' && $h ? @
$v : $v;
459 $g->add_vertex( @v ) unless $V->has_path( @v );
460 push @e, $V->_get_path_id( @v );
466 sub _union_find_add_edge
{
467 my ($g, $u, $v) = @_;
468 $g->[ _U
]->union($u, $v);
473 if ($g->is_multiedged) {
474 unless (@_ == 2 || $g->is_hyperedged) {
476 Carp
::croak
("Graph::add_edge: use add_edges for more than one edge");
478 return $g->add_edge_by_id(@_, _GEN_ID
);
481 unless ($g->is_hyperedged) {
483 Carp
::croak
("Graph::add_edge: graph is not hyperedged");
486 my @e = $g->_add_edge( @_ );
487 $g->[ _E
]->set_path( @e );
489 $g->_union_find_add_edge( @e ) if $g->has_union_find;
497 if (($V->[ _f
] & _LIGHT
)) {
499 return () unless exists $V->[ _s
]->{ $v };
500 push @e, $V->[ _s
]->{ $v };
503 my $h = $g->[ _V
]->_is_HYPER;
505 my @v = ref $v eq 'ARRAY' && $h ? @
$v : $v;
506 return () unless $V->has_path( @v );
507 push @e, $V->_get_path_id( @v );
518 if (($V->[ _f
] & _LIGHT
) && @_ == 2) {
520 exists $V->[ _s
]->{ $_[0] } &&
521 exists $V->[ _s
]->{ $_[1] };
522 @i = @
{ $V->[ _s
] }{ @_[ 0, 1 ] };
524 @i = $g->_vertex_ids( @_ );
525 return 0 if @i == 0 && @_;
528 if ($E->[ _a
] == 2 && @i == 2 && !($f & (_HYPER
|_REF
|_UNIQ
))) { # Fast path.
529 @i = sort @i if ($f & _UNORD
);
530 return exists $E->[ _s
]->{ $i[0] } &&
531 exists $E->[ _s
]->{ $i[0] }->{ $i[1] } ?
1 : 0;
533 return defined $E->_get_path_id( @i ) ?
1 : 0;
540 my @e = $g->[ _E
]->paths( @_ );
542 map { [ map { my @v = $V->_get_id_path($_);
543 @v == 1 ?
$v[0] : [ @v ] }
550 if (@_ && defined $_[0]) {
551 unless (defined $_[1]) {
552 my @e = $g->edges_at($_[0]);
555 sort { $a->[0] cmp $b->[0] || $a->[1] cmp $b->[1] } @e
558 die "edges02: unimplemented option";
561 my @e = map { ($_) x
$g->get_edge_count(@
$_) } $g->edges05( @_ );
564 sort { $a->[0] cmp $b->[0] || $a->[1] cmp $b->[1] } @e
571 ($g->is_compat02) ?
$g->edges02( @_ ) : $g->edges05( @_ );
576 if ($g->is_compat02) {
577 return $g->edges02( @_ );
579 if ($g->is_multiedged || $g->is_countedged) {
582 for my $e ( $g->edges05 ) {
583 push @E, ($e) x
$g->get_edge_count(@
$e);
588 for my $e ( $g->edges05 ) {
589 $E += $g->get_edge_count(@
$e);
601 scalar $g->[ _E
]->has_paths( @_ );
608 sub add_vertex_by_id
{
610 $g->expect_multivertexed;
611 $g->[ _V
]->set_path_by_multi_id( @_ );
613 $g->_union_find_add_vertex( @_ ) if $g->has_union_find;
617 sub add_vertex_get_id
{
619 $g->expect_multivertexed;
620 my $id = $g->[ _V
]->set_path_by_multi_id( @_, _GEN_ID
);
622 $g->_union_find_add_vertex( @_ ) if $g->has_union_find;
626 sub has_vertex_by_id
{
628 $g->expect_multivertexed;
629 $g->[ _V
]->has_path_by_multi_id( @_ );
632 sub delete_vertex_by_id
{
634 $g->expect_multivertexed;
636 return unless $V->has_path_by_multi_id( @_ );
637 # TODO: what to about the edges at this vertex?
638 # If the multiness of this vertex goes to zero, delete the edges?
639 $V->del_path_by_multi_id( @_ );
644 sub get_multivertex_ids
{
646 $g->expect_multivertexed;
647 $g->[ _V
]->get_multi_ids( @_ );
652 $g->expect_multiedged;
654 my @e = $g->_add_edge( @_ );
655 $g->[ _E
]->set_path( @e, $id );
657 $g->_union_find_add_edge( @e ) if $g->has_union_find;
661 sub add_edge_get_id
{
663 $g->expect_multiedged;
664 my @i = $g->_add_edge( @_ );
665 my $id = $g->[ _E
]->set_path_by_multi_id( @i, _GEN_ID
);
666 $g->_union_find_add_edge( @i ) if $g->has_union_find;
673 $g->expect_multiedged;
675 my @i = $g->_vertex_ids( @_ );
676 return 0 if @i == 0 && @_;
677 $g->[ _E
]->has_path_by_multi_id( @i, $id );
680 sub delete_edge_by_id
{
682 $g->expect_multiedged;
685 my @i = $g->_vertex_ids( @_ );
686 return unless $V->has_path_by_multi_id( @i, $id );
687 $V->del_path_by_multi_id( @i, $id );
692 sub get_multiedge_ids
{
694 $g->expect_multiedged;
695 my @id = $g->_vertex_ids( @_ );
697 $g->[ _E
]->get_multi_ids( @id );
707 return @_ unless ($V->[ _f
] & _HYPER
);
711 my $i = $V->_get_path_id( $v );
712 return unless defined $i;
713 push @i, ( $v{ $v } = $i );
717 while (my ($i, $v) = each %{ $Vi }) {
719 my $h = $V->[_f
] & _HYPER
;
720 @i{ @i } = @i if @i; # @todo: nonuniq hyper vertices?
721 for my $u (ref $v eq 'ARRAY' && $h ? @
$v : $v) {
722 my $j = exists $v{ $u } ?
$v{ $u } : ( $v{ $u } = $i );
723 if (defined $j && exists $i{ $j }) {
742 my $h = $V->[_f
] & _HYPER
;
743 for my $v ( $h ?
$g->vertices_at( @_ ) : @_ ) {
744 my $vi = $V->_get_path_id( ref $v eq 'ARRAY' && $h ? @
$v : $v );
745 next unless defined $vi;
747 while (my ($ei, $ev) = each %{ $Ei }) {
750 push @e, [ $ei, $ev ]
751 if $j == $vi && !$ev{$ei}++;
760 return wantarray ?
@e : $en;
768 my $o = $E->[ _f
] & _UNORD
;
771 my $h = $V->[_f
] & _HYPER
;
772 for my $v ( $h ?
$g->vertices_at( @_ ) : @_ ) {
773 my $vi = $V->_get_path_id( ref $v eq 'ARRAY' && $h ? @
$v : $v );
774 next unless defined $vi;
778 while (my ($ei, $ev) = each %{ $Ei }) {
780 push @e, [ $ei, $ev ]
781 if ($ev->[0] == $vi || $ev->[-1] == $vi) && !$ev{$ei}++;
784 while (my ($ei, $ev) = each %{ $Ei }) {
786 push @e, [ $ei, $ev ]
787 if $ev->[0] == $vi && !$ev{$ei}++;
792 while (my ($ei, $ev) = each %{ $Ei }) {
794 $en++ if ($ev->[0] == $vi || $ev->[-1] == $vi);
797 while (my ($ei, $ev) = each %{ $Ei }) {
799 $en++ if $ev->[0] == $vi;
804 if (wantarray && $g->is_undirected) {
805 my @i = map { $V->_get_path_id( $_ ) } @_;
807 unless ( $e->[ 1 ]->[ 0 ] == $i[ 0 ] ) { # @todo
808 $e = [ $e->[ 0 ], [ reverse @
{ $e->[ 1 ] } ] ];
812 return wantarray ?
@e : $en;
820 my $o = $E->[ _f
] & _UNORD
;
823 my $h = $V->[_f
] & _HYPER
;
824 for my $v ( $h ?
$g->vertices_at( @_ ) : @_ ) {
825 my $vi = $V->_get_path_id( ref $v eq 'ARRAY' && $h ? @
$v : $v );
826 next unless defined $vi;
830 while (my ($ei, $ev) = each %{ $Ei }) {
832 push @e, [ $ei, $ev ]
833 if ($ev->[-1] == $vi || $ev->[0] == $vi) && !$ev{$ei}++;
836 while (my ($ei, $ev) = each %{ $Ei }) {
838 push @e, [ $ei, $ev ]
839 if $ev->[-1] == $vi && !$ev{$ei}++;
844 while (my ($ei, $ev) = each %{ $Ei }) {
846 $en++ if $ev->[-1] == $vi || $ev->[0] == $vi;
849 while (my ($ei, $ev) = each %{ $Ei }) {
851 $en++ if $ev->[-1] == $vi;
856 if (wantarray && $g->is_undirected) {
857 my @i = map { $V->_get_path_id( $_ ) } @_;
859 unless ( $e->[ 1 ]->[ -1 ] == $i[ -1 ] ) { # @todo
860 $e = [ $e->[ 0 ], [ reverse @
{ $e->[ 1 ] } ] ];
864 return wantarray ?
@e : $en;
870 [ map { my @v = $V->_get_id_path($_);
871 @v == 1 ?
$v[0] : [ @v ] }
877 map { $g->_edges_id_path($_ ) } $g->_edges_at( @_ );
882 map { $g->_edges_id_path($_ ) } $g->_edges_from( @_ );
887 map { $g->_edges_id_path($_ ) } $g->_edges_to( @_ );
893 ($E->[ _f
] & _LIGHT
) ?
894 $E->_successors($g, @_) :
895 Graph
::AdjacencyMap
::_successors
($E, $g, @_);
901 ($E->[ _f
] & _LIGHT
) ?
902 $E->_predecessors($g, @_) :
903 Graph
::AdjacencyMap
::_predecessors
($E, $g, @_);
909 my @s = map { my @v = @
{ $_->[ 1 ] }; shift @v; @v } $g->_edges_from( @_ );
910 my @p = map { my @v = @
{ $_->[ 1 ] }; pop @v; @v } $g->_edges_to ( @_ );
914 map { $V->_get_id_path($_) } keys %n;
917 *neighbors
= \
&neighbours
;
921 my @i = $g->_vertex_ids( @_ );
923 my $i = $g->[ _E
]->_get_path_id( @i );
924 return $g unless defined $i;
925 $g->[ _E
]->_del_id( $i );
933 return $g unless $V->has_path( @_ );
935 for my $e ( $g->_edges_at( @_ ) ) {
936 $E->_del_id( $e->[ 0 ] );
943 sub get_vertex_count
{
945 $g->[ _V
]->_get_path_count( @_ ) || 0;
950 my @e = $g->_vertex_ids( @_ );
952 $g->[ _E
]->_get_path_count( @e ) || 0;
955 sub delete_vertices
{
959 $g->delete_vertex($v);
967 my ($u, $v) = splice @_, 0, 2;
968 $g->delete_edge($u, $v);
979 return undef unless @_ && $g->has_vertex( @_ );
980 my $in = $g->is_undirected && $g->is_self_loop_vertex( @_ ) ?
1 : 0;
981 $in += $g->get_edge_count( @
$_ ) for $g->edges_to( @_ );
987 $g->_in_degree( @_ );
992 return undef unless @_ && $g->has_vertex( @_ );
993 my $out = $g->is_undirected && $g->is_self_loop_vertex( @_ ) ?
1 : 0;
994 $out += $g->get_edge_count( @
$_ ) for $g->edges_from( @_ );
1000 $g->_out_degree( @_ );
1005 return undef unless @_ && $g->has_vertex( @_ );
1007 $g->_in_degree( @_ ) :
1008 $g-> in_degree
( @_ ) - $g-> out_degree
( @_ );
1014 $g->_total_degree( @_ );
1016 if ($g->is_undirected) {
1018 $total += $g->_total_degree( $_ ) for $g->vertices05;
1026 *vertex_degree
= \
°ree
;
1028 sub is_sink_vertex
{
1031 $g->successors( @_ ) == 0 && $g->predecessors( @_ ) > 0;
1034 sub is_source_vertex
{
1037 $g->predecessors( @_ ) == 0 && $g->successors( @_ ) > 0;
1040 sub is_successorless_vertex
{
1043 $g->successors( @_ ) == 0;
1046 sub is_predecessorless_vertex
{
1049 $g->predecessors( @_ ) == 0;
1052 sub is_successorful_vertex
{
1055 $g->successors( @_ ) > 0;
1058 sub is_predecessorful_vertex
{
1061 $g->predecessors( @_ ) > 0;
1064 sub is_isolated_vertex
{
1067 $g->predecessors( @_ ) == 0 && $g->successors( @_ ) == 0;
1070 sub is_interior_vertex
{
1073 my $p = $g->predecessors( @_ );
1074 my $s = $g->successors( @_ );
1075 if ($g->is_self_loop_vertex( @_ )) {
1082 sub is_exterior_vertex
{
1085 $g->predecessors( @_ ) == 0 || $g->successors( @_ ) == 0;
1088 sub is_self_loop_vertex
{
1091 for my $s ( $g->successors( @_ ) ) {
1092 return 1 if $s eq $_[0]; # @todo: hypervertices
1099 grep { $g->is_sink_vertex($_) } $g->vertices05;
1102 sub source_vertices
{
1104 grep { $g->is_source_vertex($_) } $g->vertices05;
1107 sub successorless_vertices
{
1109 grep { $g->is_successorless_vertex($_) } $g->vertices05;
1112 sub predecessorless_vertices
{
1114 grep { $g->is_predecessorless_vertex($_) } $g->vertices05;
1117 sub successorful_vertices
{
1119 grep { $g->is_successorful_vertex($_) } $g->vertices05;
1122 sub predecessorful_vertices
{
1124 grep { $g->is_predecessorful_vertex($_) } $g->vertices05;
1127 sub isolated_vertices
{
1129 grep { $g->is_isolated_vertex($_) } $g->vertices05;
1132 sub interior_vertices
{
1134 grep { $g->is_interior_vertex($_) } $g->vertices05;
1137 sub exterior_vertices
{
1139 grep { $g->is_exterior_vertex($_) } $g->vertices05;
1142 sub self_loop_vertices
{
1144 grep { $g->is_self_loop_vertex($_) } $g->vertices05;
1156 $g->add_edge($u, $v);
1167 $g->delete_edge($u, $v);
1178 return 0 unless $g->has_edge($u, $v);
1186 $g->add_path(@_, $_[0]);
1191 $g->delete_path(@_, $_[0]);
1196 @_ ?
($g->has_path(@_, $_[0]) ?
1 : 0) : 0;
1201 my @r = ( back_edge
=> \
&Graph
::Traversal
::has_a_cycle
);
1203 down_edge
=> \
&Graph
::Traversal
::has_a_cycle
1204 if $g->is_undirected;
1205 my $t = Graph
::Traversal
::DFS
->new($g, @r, @_);
1207 return $t->get_state('has_a_cycle');
1212 my @r = ( back_edge
=> \
&Graph
::Traversal
::find_a_cycle
);
1214 down_edge
=> \
&Graph
::Traversal
::find_a_cycle
1215 if $g->is_undirected;
1216 my $t = Graph
::Traversal
::DFS
->new($g, @r, @_);
1218 $t->has_state('a_cycle') ? @
{ $t->get_state('a_cycle') } : ();
1224 # Vertex attributes.
1226 sub set_vertex_attribute
{
1228 $g->expect_non_multivertexed;
1231 $g->add_vertex( @_ ) unless $g->has_vertex( @_ );
1232 $g->[ _V
]->_set_path_attr( @_, $attr, $value );
1235 sub set_vertex_attribute_by_id
{
1237 $g->expect_multivertexed;
1240 $g->add_vertex_by_id( @_ ) unless $g->has_vertex_by_id( @_ );
1241 $g->[ _V
]->_set_path_attr( @_, $attr, $value );
1244 sub set_vertex_attributes
{
1246 $g->expect_non_multivertexed;
1248 $g->add_vertex( @_ ) unless $g->has_vertex( @_ );
1249 $g->[ _V
]->_set_path_attrs( @_, $attr );
1252 sub set_vertex_attributes_by_id
{
1254 $g->expect_multivertexed;
1256 $g->add_vertex_by_id( @_ ) unless $g->has_vertex_by_id( @_ );
1257 $g->[ _V
]->_set_path_attrs( @_, $attr );
1260 sub has_vertex_attributes
{
1262 $g->expect_non_multivertexed;
1263 return 0 unless $g->has_vertex( @_ );
1264 $g->[ _V
]->_has_path_attrs( @_ );
1267 sub has_vertex_attributes_by_id
{
1269 $g->expect_multivertexed;
1270 return 0 unless $g->has_vertex_by_id( @_ );
1271 $g->[ _V
]->_has_path_attrs( @_ );
1274 sub has_vertex_attribute
{
1276 $g->expect_non_multivertexed;
1278 return 0 unless $g->has_vertex( @_ );
1279 $g->[ _V
]->_has_path_attr( @_, $attr );
1282 sub has_vertex_attribute_by_id
{
1284 $g->expect_multivertexed;
1286 return 0 unless $g->has_vertex_by_id( @_ );
1287 $g->[ _V
]->_has_path_attr( @_, $attr );
1290 sub get_vertex_attributes
{
1292 $g->expect_non_multivertexed;
1293 return unless $g->has_vertex( @_ );
1294 my $a = $g->[ _V
]->_get_path_attrs( @_ );
1295 ($g->is_compat02) ?
(defined $a ?
%{ $a } : ()) : $a;
1298 sub get_vertex_attributes_by_id
{
1300 $g->expect_multivertexed;
1301 return unless $g->has_vertex_by_id( @_ );
1302 $g->[ _V
]->_get_path_attrs( @_ );
1305 sub get_vertex_attribute
{
1307 $g->expect_non_multivertexed;
1309 return unless $g->has_vertex( @_ );
1310 $g->[ _V
]->_get_path_attr( @_, $attr );
1313 sub get_vertex_attribute_by_id
{
1315 $g->expect_multivertexed;
1317 return unless $g->has_vertex_by_id( @_ );
1318 $g->[ _V
]->_get_path_attr( @_, $attr );
1321 sub get_vertex_attribute_names
{
1323 $g->expect_non_multivertexed;
1324 return unless $g->has_vertex( @_ );
1325 $g->[ _V
]->_get_path_attr_names( @_ );
1328 sub get_vertex_attribute_names_by_id
{
1330 $g->expect_multivertexed;
1331 return unless $g->has_vertex_by_id( @_ );
1332 $g->[ _V
]->_get_path_attr_names( @_ );
1335 sub get_vertex_attribute_values
{
1337 $g->expect_non_multivertexed;
1338 return unless $g->has_vertex( @_ );
1339 $g->[ _V
]->_get_path_attr_values( @_ );
1342 sub get_vertex_attribute_values_by_id
{
1344 $g->expect_multivertexed;
1345 return unless $g->has_vertex_by_id( @_ );
1346 $g->[ _V
]->_get_path_attr_values( @_ );
1349 sub delete_vertex_attributes
{
1351 $g->expect_non_multivertexed;
1352 return undef unless $g->has_vertex( @_ );
1353 $g->[ _V
]->_del_path_attrs( @_ );
1356 sub delete_vertex_attributes_by_id
{
1358 $g->expect_multivertexed;
1359 return undef unless $g->has_vertex_by_id( @_ );
1360 $g->[ _V
]->_del_path_attrs( @_ );
1363 sub delete_vertex_attribute
{
1365 $g->expect_non_multivertexed;
1367 return undef unless $g->has_vertex( @_ );
1368 $g->[ _V
]->_del_path_attr( @_, $attr );
1371 sub delete_vertex_attribute_by_id
{
1373 $g->expect_multivertexed;
1375 return undef unless $g->has_vertex_by_id( @_ );
1376 $g->[ _V
]->_del_path_attr( @_, $attr );
1381 sub _set_edge_attribute
{
1388 if ($E->[ _a
] == 2 && @_ == 2 && !($f & (_HYPER
|_REF
|_UNIQ
))) { # Fast path.
1389 @_ = sort @_ if ($f & _UNORD
);
1391 $g->add_edge( @_ ) unless exists $s->{ $_[0] } && exists $s->{ $_[0] }->{ $_[1] };
1392 @i = @
{ $g->[ _V
]->[ _s
] }{ @_ };
1394 $g->add_edge( @_ ) unless $g->has_edge( @_ );
1395 @i = $g->_vertex_ids( @_ );
1397 $g->[ _E
]->_set_path_attr( @i, $attr, $value );
1400 sub set_edge_attribute
{
1402 $g->expect_non_multiedged;
1406 $g->add_edge( @_ ) unless $g->has_edge( @_ );
1407 $E->_set_path_attr( $g->_vertex_ids( @_ ), $attr, $value );
1410 sub set_edge_attribute_by_id
{
1412 $g->expect_multiedged;
1415 # $g->add_edge_by_id( @_ ) unless $g->has_edge_by_id( @_ );
1417 $g->[ _E
]->_set_path_attr( $g->_vertex_ids( @_ ), $id, $attr, $value );
1420 sub set_edge_attributes
{
1422 $g->expect_non_multiedged;
1424 $g->add_edge( @_ ) unless $g->has_edge( @_ );
1425 $g->[ _E
]->_set_path_attrs( $g->_vertex_ids( @_ ), $attr );
1428 sub set_edge_attributes_by_id
{
1430 $g->expect_multiedged;
1432 $g->add_edge_by_id( @_ ) unless $g->has_edge_by_id( @_ );
1434 $g->[ _E
]->_set_path_attrs( $g->_vertex_ids( @_ ), $id, $attr );
1437 sub has_edge_attributes
{
1439 $g->expect_non_multiedged;
1440 return 0 unless $g->has_edge( @_ );
1441 $g->[ _E
]->_has_path_attrs( $g->_vertex_ids( @_ ) );
1444 sub has_edge_attributes_by_id
{
1446 $g->expect_multiedged;
1447 return 0 unless $g->has_edge_by_id( @_ );
1449 $g->[ _E
]->_has_path_attrs( $g->_vertex_ids( @_ ), $id );
1452 sub has_edge_attribute
{
1454 $g->expect_non_multiedged;
1456 return 0 unless $g->has_edge( @_ );
1457 $g->[ _E
]->_has_path_attr( $g->_vertex_ids( @_ ), $attr );
1460 sub has_edge_attribute_by_id
{
1462 $g->expect_multiedged;
1464 return 0 unless $g->has_edge_by_id( @_ );
1466 $g->[ _E
]->_has_path_attr( $g->_vertex_ids( @_ ), $id, $attr );
1469 sub get_edge_attributes
{
1471 $g->expect_non_multiedged;
1472 return unless $g->has_edge( @_ );
1473 my $a = $g->[ _E
]->_get_path_attrs( $g->_vertex_ids( @_ ) );
1474 ($g->is_compat02) ?
(defined $a ?
%{ $a } : ()) : $a;
1477 sub get_edge_attributes_by_id
{
1479 $g->expect_multiedged;
1480 return unless $g->has_edge_by_id( @_ );
1482 return $g->[ _E
]->_get_path_attrs( $g->_vertex_ids( @_ ), $id );
1485 sub _get_edge_attribute
{ # Fast path; less checks.
1490 if ($E->[ _a
] == 2 && @_ == 2 && !($f & (_HYPER
|_REF
|_UNIQ
))) { # Fast path.
1491 @_ = sort @_ if ($f & _UNORD
);
1493 return unless exists $s->{ $_[0] } && exists $s->{ $_[0] }->{ $_[1] };
1495 return unless $g->has_edge( @_ );
1497 my @i = $g->_vertex_ids( @_ );
1498 $E->_get_path_attr( @i, $attr );
1501 sub get_edge_attribute
{
1503 $g->expect_non_multiedged;
1505 return undef unless $g->has_edge( @_ );
1506 my @i = $g->_vertex_ids( @_ );
1507 return undef if @i == 0 && @_;
1509 $E->_get_path_attr( @i, $attr );
1512 sub get_edge_attribute_by_id
{
1514 $g->expect_multiedged;
1516 return unless $g->has_edge_by_id( @_ );
1518 $g->[ _E
]->_get_path_attr( $g->_vertex_ids( @_ ), $id, $attr );
1521 sub get_edge_attribute_names
{
1523 $g->expect_non_multiedged;
1524 return unless $g->has_edge( @_ );
1525 $g->[ _E
]->_get_path_attr_names( $g->_vertex_ids( @_ ) );
1528 sub get_edge_attribute_names_by_id
{
1530 $g->expect_multiedged;
1531 return unless $g->has_edge_by_id( @_ );
1533 $g->[ _E
]->_get_path_attr_names( $g->_vertex_ids( @_ ), $id );
1536 sub get_edge_attribute_values
{
1538 $g->expect_non_multiedged;
1539 return unless $g->has_edge( @_ );
1540 $g->[ _E
]->_get_path_attr_values( $g->_vertex_ids( @_ ) );
1543 sub get_edge_attribute_values_by_id
{
1545 $g->expect_multiedged;
1546 return unless $g->has_edge_by_id( @_ );
1548 $g->[ _E
]->_get_path_attr_values( $g->_vertex_ids( @_ ), $id );
1551 sub delete_edge_attributes
{
1553 $g->expect_non_multiedged;
1554 return unless $g->has_edge( @_ );
1555 $g->[ _E
]->_del_path_attrs( $g->_vertex_ids( @_ ) );
1558 sub delete_edge_attributes_by_id
{
1560 $g->expect_multiedged;
1561 return unless $g->has_edge_by_id( @_ );
1563 $g->[ _E
]->_del_path_attrs( $g->_vertex_ids( @_ ), $id );
1566 sub delete_edge_attribute
{
1568 $g->expect_non_multiedged;
1570 return unless $g->has_edge( @_ );
1571 $g->[ _E
]->_del_path_attr( $g->_vertex_ids( @_ ), $attr );
1574 sub delete_edge_attribute_by_id
{
1576 $g->expect_multiedged;
1578 return unless $g->has_edge_by_id( @_ );
1580 $g->[ _E
]->_del_path_attr( $g->_vertex_ids( @_ ), $id, $attr );
1589 $g->has_vertex( @_ ) ?
@_ : undef;
1594 return unless @_ && $g->has_vertex( @_ );
1595 my @e = $g->edges_from( @_ );
1596 wantarray ?
map { @
$_ } @e : @e;
1601 return unless @_ && $g->has_vertex( @_ );
1602 my @e = $g->edges_to( @_ );
1603 wantarray ?
map { @
$_ } @e : @e;
1608 $g->add_vertex( $_ ) for @_;
1615 if (ref $u eq 'ARRAY') {
1616 $g->add_edge( @
$u );
1620 $g->add_edge( $u, $v );
1623 Carp
::croak
("Graph::add_edges: missing end vertex");
1630 # More constructors.
1635 my %opt = _get_options
( \
@_ );
1637 my $c = (ref $g)->new(directed
=> $g->directed ?
1 : 0,
1638 compat02
=> $g->compat02 ?
1 : 0);
1639 for my $v ($g->isolated_vertices) { $c->add_vertex($v) }
1640 for my $e ($g->edges05) { $c->add_edge(@
$e) }
1644 *copy_graph
= \
©
;
1647 require Data
::Dumper
;
1649 my $d = Data
::Dumper
->new([$g]);
1651 $d->Purity(1)->Terse(1)->Deepcopy(1);
1652 $d->Deparse(1) if $] >= 5.008;
1656 *deep_copy_graph = \&deep_copy;
1658 sub transpose_edge {
1660 if ($g->is_directed) {
1661 return undef unless $g->has_edge( @_ );
1662 my $c = $g->get_edge_count( @_ );
1663 my $a = $g->get_edge_attributes( @_ );
1665 $g->delete_edge( @_ ) unless $g->has_edge( @e );
1666 $g->add_edge( @e ) for 1..$c;
1667 $g->set_edge_attributes(@e, $a) if $a;
1672 sub transpose_graph {
1676 for my $e ($t->edges05) {
1677 $t->transpose_edge(@$e);
1683 *transpose = \&transpose_graph;
1685 sub complete_graph {
1687 my $c = $g->new( directed => $g->directed );
1688 my @v = $g->vertices05;
1689 for (my $i = 0; $i <= $#v; $i++ ) {
1690 for (my $j = 0; $j <= $#v; $j++ ) {
1692 if ($g->is_undirected) {
1693 $c->add_edge($v[$i], $v[$j]);
1695 $c->add_edge($v[$i], $v[$j]);
1696 $c->add_edge($v[$j], $v[$i]);
1703 *complement = \&complement_graph;
1705 sub complement_graph {
1707 my $c = $g->new( directed => $g->directed );
1708 my @v = $g->vertices05;
1709 for (my $i = 0; $i <= $#v; $i++ ) {
1710 for (my $j = 0; $j <= $#v; $j++ ) {
1712 if ($g->is_undirected) {
1713 $c->add_edge($v[$i], $v[$j])
1714 unless $g->has_edge($v[$i], $v[$j]);
1716 $c->add_edge($v[$i], $v[$j])
1717 unless $g->has_edge($v[$i], $v[$j]);
1718 $c->add_edge($v[$j], $v[$i])
1719 unless $g->has_edge($v[$j], $v[$i]);
1726 *complete = \&complete_graph;
1734 Graph::TransitiveClosure::is_transitive($g);
1738 # Weighted vertices.
1741 my $defattr = 'weight';
1747 sub add_weighted_vertex {
1749 $g->expect_non_multivertexed;
1752 $g->set_vertex_attribute(@_, $defattr, $w);
1755 sub add_weighted_vertices {
1757 $g->expect_non_multivertexed;
1759 my ($v, $w) = splice @_, 0, 2;
1761 $g->set_vertex_attribute($v, $defattr, $w);
1765 sub get_vertex_weight {
1767 $g->expect_non_multivertexed;
1768 $g->get_vertex_attribute(@_, $defattr);
1771 sub has_vertex_weight {
1773 $g->expect_non_multivertexed;
1774 $g->has_vertex_attribute(@_, $defattr);
1777 sub set_vertex_weight {
1779 $g->expect_non_multivertexed;
1781 $g->set_vertex_attribute(@_, $defattr, $w);
1784 sub delete_vertex_weight {
1786 $g->expect_non_multivertexed;
1787 $g->delete_vertex_attribute(@_, $defattr);
1790 sub add_weighted_vertex_by_id {
1792 $g->expect_multivertexed;
1794 $g->add_vertex_by_id(@_);
1795 $g->set_vertex_attribute_by_id(@_, $defattr, $w);
1798 sub add_weighted_vertices_by_id {
1800 $g->expect_multivertexed;
1803 my ($v, $w) = splice @_, 0, 2;
1804 $g->add_vertex_by_id($v, $id);
1805 $g->set_vertex_attribute_by_id($v, $id, $defattr, $w);
1809 sub get_vertex_weight_by_id {
1811 $g->expect_multivertexed;
1812 $g->get_vertex_attribute_by_id(@_, $defattr);
1815 sub has_vertex_weight_by_id {
1817 $g->expect_multivertexed;
1818 $g->has_vertex_attribute_by_id(@_, $defattr);
1821 sub set_vertex_weight_by_id {
1823 $g->expect_multivertexed;
1825 $g->set_vertex_attribute_by_id(@_, $defattr, $w);
1828 sub delete_vertex_weight_by_id {
1830 $g->expect_multivertexed;
1831 $g->delete_vertex_attribute_by_id(@_, $defattr);
1838 sub add_weighted_edge {
1840 $g->expect_non_multiedged;
1841 if ($g->is_compat02) {
1842 my $w = splice @_, 1, 1;
1844 $g->set_edge_attribute(@_, $defattr, $w);
1848 $g->set_edge_attribute(@_, $defattr, $w);
1852 sub add_weighted_edges {
1854 $g->expect_non_multiedged;
1855 if ($g->is_compat02) {
1857 my ($u, $w, $v) = splice @_, 0, 3;
1858 $g->add_edge($u, $v);
1859 $g->set_edge_attribute($u, $v, $defattr, $w);
1863 my ($u, $v, $w) = splice @_, 0, 3;
1864 $g->add_edge($u, $v);
1865 $g->set_edge_attribute($u, $v, $defattr, $w);
1870 sub add_weighted_edges_by_id {
1872 $g->expect_multiedged;
1875 my ($u, $v, $w) = splice @_, 0, 3;
1876 $g->add_edge_by_id($u, $v, $id);
1877 $g->set_edge_attribute_by_id($u, $v, $id, $defattr, $w);
1881 sub add_weighted_path {
1883 $g->expect_non_multiedged;
1886 my ($w, $v) = splice @_, 0, 2;
1887 $g->add_edge($u, $v);
1888 $g->set_edge_attribute($u, $v, $defattr, $w);
1893 sub get_edge_weight {
1895 $g->expect_non_multiedged;
1896 $g->get_edge_attribute(@_, $defattr);
1899 sub has_edge_weight {
1901 $g->expect_non_multiedged;
1902 $g->has_edge_attribute(@_, $defattr);
1905 sub set_edge_weight {
1907 $g->expect_non_multiedged;
1909 $g->set_edge_attribute(@_, $defattr, $w);
1912 sub delete_edge_weight {
1914 $g->expect_non_multiedged;
1915 $g->delete_edge_attribute(@_, $defattr);
1918 sub add_weighted_edge_by_id {
1920 $g->expect_multiedged;
1921 if ($g->is_compat02) {
1922 my $w = splice @_, 1, 1;
1923 $g->add_edge_by_id(@_);
1924 $g->set_edge_attribute_by_id(@_, $defattr, $w);
1927 $g->add_edge_by_id(@_);
1928 $g->set_edge_attribute_by_id(@_, $defattr, $w);
1932 sub add_weighted_path_by_id {
1934 $g->expect_multiedged;
1938 my ($w, $v) = splice @_, 0, 2;
1939 $g->add_edge_by_id($u, $v, $id);
1940 $g->set_edge_attribute_by_id($u, $v, $id, $defattr, $w);
1945 sub get_edge_weight_by_id {
1947 $g->expect_multiedged;
1948 $g->get_edge_attribute_by_id(@_, $defattr);
1951 sub has_edge_weight_by_id {
1953 $g->expect_multiedged;
1954 $g->has_edge_attribute_by_id(@_, $defattr);
1957 sub set_edge_weight_by_id {
1959 $g->expect_multiedged;
1961 $g->set_edge_attribute_by_id(@_, $defattr, $w);
1964 sub delete_edge_weight_by_id {
1966 $g->expect_multiedged;
1967 $g->delete_edge_attribute_by_id(@_, $defattr);
1975 @expected{qw(directed undirected acyclic)} = qw(undirected directed cyclic);
1979 my $got = @_ ?
shift : $expected{$exp};
1980 $got = defined $got ?
", got $got" : "";
1981 if (my @caller2 = caller(2)) {
1982 die "$caller2[3]: expected $exp graph$got, at $caller2[1] line $caller2[2].\n";
1984 my @caller1 = caller(1);
1985 die "$caller1[3]: expected $exp graph$got, at $caller1[1] line $caller1[2].\n";
1989 sub expect_undirected
{
1991 _expected
('undirected') unless $g->is_undirected;
1994 sub expect_directed
{
1996 _expected
('directed') unless $g->is_directed;
1999 sub expect_acyclic
{
2001 _expected
('acyclic') unless $g->is_acyclic;
2007 push @got, 'undirected' unless $g->is_directed;
2008 push @got, 'cyclic' unless $g->is_acyclic;
2009 _expected
('directed acyclic', "@got") if @got;
2012 sub expect_multivertexed
{
2014 _expected
('multivertexed') unless $g->is_multivertexed;
2017 sub expect_non_multivertexed
{
2019 _expected
('non-multivertexed') if $g->is_multivertexed;
2022 sub expect_non_multiedged
{
2024 _expected
('non-multiedged') if $g->is_multiedged;
2027 sub expect_multiedged
{
2029 _expected
('multiedged') unless $g->is_multiedged;
2033 my @caller = caller(1);
2034 unless (@_ == 1 && ref $_[0] eq 'ARRAY') {
2035 die "$caller[3]: internal error: should be called with only one array ref argument, at $caller[1] line $caller[2].\n";
2037 my @opt = @
{ $_[0] };
2038 unless (@opt % 2 == 0) {
2039 die "$caller[3]: expected an options hash, got a non-even number of arguments, at $caller[1] line $caller[2].\n";
2045 # Random constructors and accessors.
2048 sub __fisher_yates_shuffle
(@
) {
2049 # From perlfaq4, but modified to be non-modifying.
2053 my $j = int rand ($i+1);
2054 @a[$i,$j] = @a[$j,$i];
2061 # Workaround for the Perl bug [perl #32383] where -d:Dprof and
2062 # List::Util::shuffle do not like each other: if any debugging
2063 # (-d) flags are on, fall back to our own Fisher-Yates shuffle.
2064 # The bug was fixed by perl changes #26054 and #26062, which
2065 # went to Perl 5.9.3. If someone tests this with a pre-5.9.3
2066 # bleadperl that calls itself 5.9.3 but doesn't yet have the
2067 # patches, oh, well.
2068 *_shuffle
= $^P
&& $] < 5.009003 ?
2069 \
&__fisher_yates_shuffle
: \
&List
::Util
::shuffle
;
2073 my $class = (@_ % 2) == 0 ?
'Graph' : shift;
2074 my %opt = _get_options
( \
@_ );
2076 unless (exists $opt{vertices
} && defined $opt{vertices
}) {
2078 Carp
::croak
("Graph::random_graph: argument 'vertices' missing or undef");
2080 if (exists $opt{random_seed
}) {
2081 srand($opt{random_seed
});
2082 delete $opt{random_seed
};
2084 if (exists $opt{random_edge
}) {
2085 $random_edge = $opt{random_edge
};
2086 delete $opt{random_edge
};
2089 if (my $ref = ref $opt{vertices
}) {
2090 if ($ref eq 'ARRAY') {
2091 @V = @
{ $opt{vertices
} };
2093 Carp
::croak
("Graph::random_graph: argument 'vertices' illegal");
2096 @V = 0..($opt{vertices
} - 1);
2098 delete $opt{vertices
};
2100 my $C = $V * ($V - 1) / 2;
2102 if (exists $opt{edges
} && exists $opt{edges_fill
}) {
2103 Carp
::croak
("Graph::random_graph: both arguments 'edges' and 'edges_fill' specified");
2105 $E = exists $opt{edges_fill
} ?
$opt{edges_fill
} * $C : $opt{edges
};
2107 delete $opt{edges_fill
};
2108 my $g = $class->new(%opt);
2109 $g->add_vertices(@V);
2110 return $g if $V < 2;
2111 $C *= 2 if $g->directed;
2112 $E = $C / 2 unless defined $E;
2115 $random_edge = sub { $p } unless defined $random_edge;
2116 # print "V = $V, E = $E, C = $C, p = $p\n";
2117 if ($p > 1.0 && !($g->countedged || $g->multiedged)) {
2119 Carp
::croak
("Graph::random_graph: needs to be countedged or multiedged ($E > $C)");
2123 # Shuffle the vertex lists so that the pairs at
2124 # the beginning of the lists are not more likely.
2131 next if $v1 eq $v2; # TODO: allow self-loops?
2132 my $q = $random_edge->($g, $v1, $v2, $p);
2133 if ($q && ($q == 1 || rand() <= $q) &&
2134 !$g->has_edge($v1, $v2)) {
2135 $g->add_edge($v1, $v2);
2137 last LOOP
unless $E;
2147 my @V = $g->vertices05;
2153 my @E = $g->edges05;
2157 sub random_successor
{
2159 my @S = $g->successors($v);
2163 sub random_predecessor
{
2165 my @P = $g->predecessors($v);
2173 my $MST_comparator = sub { ($_[0] || 0) <=> ($_[1] || 0) };
2178 exists $attr->{attribute
} ?
2179 $attr->{attribute
} : $defattr;
2181 exists $attr->{comparator
} ?
2182 $attr->{comparator
} : $MST_comparator;
2183 return ($attribute, $comparator);
2187 my ($g, $attr) = @_;
2188 my ($attribute, $comparator) = _MST_attr
($attr);
2190 sort { $comparator->($a->[0], $b->[0], $a->[1], $b->[1]) }
2191 map { [ $g->get_edge_attribute(@
$_, $attribute), $_ ] }
2196 my ($g, %attr) = @_;
2198 $g->expect_undirected;
2200 my $MST = Graph
::Undirected
->new;
2202 my $UF = Graph
::UnionFind
->new;
2203 for my $v ($g->vertices05) { $UF->add($v) }
2205 for my $e ($g->_MST_edges(\
%attr)) {
2206 my ($u, $v) = @
$e; # TODO: hyperedges
2207 my $t0 = $UF->find( $u );
2208 my $t1 = $UF->find( $v );
2209 unless ($t0 eq $t1) {
2211 $MST->add_edge($u, $v);
2219 my ($g, $h, $HF, $r, $attr, $unseen) = @_;
2220 for my $s ( grep { exists $unseen->{ $_ } } $g->successors( $r ) ) {
2221 $HF->add( Graph
::MSTHeapElem
->new( $r, $s, $g->get_edge_attribute( $r, $s, $attr ) ) );
2225 sub _next_alphabetic
{ shift; (sort keys %{ $_[0] })[0] }
2226 sub _next_numeric
{ shift; (sort { $a <=> $b } keys %{ $_[0] })[0] }
2227 sub _next_random
{ shift; (values %{ $_[0] })[ rand keys %{ $_[0] } ] }
2231 my %opt = @_ == 1 ?
( first_root
=> $_[0] ) : _get_options
( \
@_ );
2233 my @unseen = $g->vertices05;
2234 @unseen{ @unseen } = @unseen;
2235 @unseen = _shuffle
@unseen;
2237 if (exists $opt{ start
}) {
2238 $opt{ first_root
} = $opt{ start
};
2239 $opt{ next_root
} = undef;
2241 if (exists $opt{ get_next_root
}) {
2242 $opt{ next_root
} = $opt{ get_next_root
}; # Graph 0.201 compat.
2244 if (exists $opt{ first_root
}) {
2245 if (ref $opt{ first_root
} eq 'CODE') {
2246 $r = $opt{ first_root
}->( $g, \
%unseen );
2248 $r = $opt{ first_root
};
2254 exists $opt{ next_root
} ?
2256 $opt{ next_alphabetic
} ?
2257 \
&_next_alphabetic
:
2258 $opt{ next_numeric
} ? \
&_next_numeric
:
2260 my $code = ref $next eq 'CODE';
2261 my $attr = exists $opt{ attribute
} ?
$opt{ attribute
} : $defattr;
2262 return ( \
%opt, \
%unseen, \
@unseen, $r, $next, $code, $attr );
2266 my ($g, $h, $add, $etc) = splice @_, 0, 4; # Leave %opt in @_.
2268 my ($opt, $unseenh, $unseena, $r, $next, $code, $attr) = $g->_root_opt(@_);
2269 my $HF = Heap071
::Fibonacci
->new;
2271 while (defined $r) {
2273 $add->($g, $h, $HF, $r, $attr, $unseenh, $etc);
2274 delete $unseenh->{ $r };
2275 while (defined $HF->top) {
2276 my $t = $HF->extract_top;
2277 # use Data::Dumper; print "t = ", Dumper($t);
2279 my ($u, $v, $w) = $t->val;
2280 # print "extracted top: $u $v $w\n";
2281 if (exists $unseenh->{ $v }) {
2282 $h->set_edge_attribute($u, $v, $attr, $w);
2283 delete $unseenh->{ $v };
2284 $add->($g, $h, $HF, $v, $attr, $unseenh, $etc);
2288 return $h unless defined $next;
2289 $r = $code ?
$next->( $g, $unseenh ) : shift @
$unseena;
2297 $g->expect_undirected;
2298 $g->_heap_walk(Graph
::Undirected
->new(), \
&_MST_add
, undef, @_);
2301 *MST_Dijkstra
= \
&MST_Prim
;
2303 *minimum_spanning_tree
= \
&MST_Prim
;
2309 *is_cyclic
= \
&has_a_cycle
;
2313 return !$g->is_cyclic;
2318 return $g->is_directed && $g->is_acyclic ?
1 : 0;
2321 *is_directed_acyclic_graph
= \
&is_dag
;
2327 sub average_degree
{
2329 my $V = $g->vertices05;
2331 return $V ?
$g->degree / $V : 0;
2334 sub density_limits
{
2337 my $V = $g->vertices05;
2338 my $M = $V * ($V - 1);
2340 $M /= 2 if $g->is_undirected;
2342 return ( 0.25 * $M, 0.75 * $M, $M );
2347 my ($sparse, $dense, $complete) = $g->density_limits;
2349 return $complete ?
$g->edges / $complete : 0;
2353 # Attribute backward compat
2357 my ($g, $op, $ga, $va, $ea) = splice @_, 0, 5;
2358 if ($g->is_compat02) {
2359 if (@_ == 0) { return $ga->( $g ) }
2360 elsif (@_ == 1) { return $va->( $g, @_ ) }
2361 elsif (@_ == 2) { return $ea->( $g, @_ ) }
2363 die sprintf "$op: wrong number of arguments (%d)", scalar @_;
2366 die "$op: not a compat02 graph"
2371 my ($g, $op, $ga, $va, $ea) = splice @_, 0, 5;
2372 if ($g->is_compat02) {
2373 if (@_ == 1) { return $ga->( $g, @_ ) }
2374 elsif (@_ == 2) { return $va->( $g, @_[1, 0] ) }
2375 elsif (@_ == 3) { return $ea->( $g, @_[1, 2, 0] ) }
2377 die sprintf "$op: wrong number of arguments (%d)", scalar @_;
2380 die "$op: not a compat02 graph"
2385 my ($g, $op, $ga, $va, $ea) = splice @_, 0, 5;
2386 if ($g->is_compat02) {
2387 if (@_ == 2) { return $ga->( $g, @_ ) }
2388 elsif (@_ == 3) { return $va->( $g, @_[1, 0, 2] ) }
2389 elsif (@_ == 4) { return $ea->( $g, @_[1, 2, 0, 3] ) }
2391 die sprintf "$op: wrong number of arguments (%d)", scalar @_;
2394 die "$op: not a compat02 graph";
2400 $g->_attr02_234('set_attribute',
2401 \
&Graph
::set_graph_attribute
,
2402 \
&Graph
::set_vertex_attribute
,
2403 \
&Graph
::set_edge_attribute
,
2408 sub set_attributes
{
2411 $g->_attr02_123('set_attributes',
2412 \
&Graph
::set_graph_attributes
,
2413 \
&Graph
::set_vertex_attributes
,
2414 \
&Graph
::set_edge_attributes
,
2421 $g->_attr02_123('get_attribute',
2422 \
&Graph
::get_graph_attribute
,
2423 \
&Graph
::get_vertex_attribute
,
2424 \
&Graph
::get_edge_attribute
,
2429 sub get_attributes
{
2431 $g->_attr02_012('get_attributes',
2432 \
&Graph
::get_graph_attributes
,
2433 \
&Graph
::get_vertex_attributes
,
2434 \
&Graph
::get_edge_attributes
,
2442 $g->_attr02_123('has_attribute',
2443 \
&Graph
::has_graph_attribute
,
2444 \
&Graph
::has_vertex_attribute
,
2445 \
&Graph
::get_edge_attribute
,
2450 sub has_attributes
{
2452 $g->_attr02_012('has_attributes',
2453 \
&Graph
::has_graph_attributes
,
2454 \
&Graph
::has_vertex_attributes
,
2455 \
&Graph
::has_edge_attributes
,
2460 sub delete_attribute
{
2462 $g->_attr02_123('delete_attribute',
2463 \
&Graph
::delete_graph_attribute
,
2464 \
&Graph
::delete_vertex_attribute
,
2465 \
&Graph
::delete_edge_attribute
,
2470 sub delete_attributes
{
2472 $g->_attr02_012('delete_attributes',
2473 \
&Graph
::delete_graph_attributes
,
2474 \
&Graph
::delete_vertex_attributes
,
2475 \
&Graph
::delete_edge_attributes
,
2484 sub topological_sort
{
2486 my %opt = _get_options
( \
@_ );
2487 my $eic = $opt{ empty_if_cyclic
};
2490 $hac = $g->has_a_cycle;
2494 delete $opt{ empty_if_cyclic
};
2495 my $t = Graph
::Traversal
::DFS
->new($g, %opt);
2497 $hac ?
() : reverse @s;
2500 *toposort
= \
&topological_sort
;
2502 sub undirected_copy
{
2505 $g->expect_directed;
2507 my $c = Graph
::Undirected
->new;
2508 for my $v ($g->isolated_vertices) { # TODO: if iv ...
2511 for my $e ($g->edges05) {
2517 *undirected_copy_graph
= \
&undirected_copy
;
2521 $g->expect_undirected;
2522 my $c = Graph
::Directed
->new;
2523 for my $v ($g->isolated_vertices) { # TODO: if iv ...
2526 for my $e ($g->edges05) {
2529 $c->add_edge(reverse @e);
2534 *directed_copy_graph
= \
&directed_copy
;
2542 'connectivity' => '_ccc',
2543 'strong_connectivity' => '_scc',
2544 'biconnectivity' => '_bcc',
2545 'SPT_Dijkstra' => '_spt_di',
2546 'SPT_Bellman_Ford' => '_spt_bf',
2550 my ($g, $type, $code) = splice @_, 0, 3;
2551 my $c = $_cache_type{$type};
2553 my $a = $g->get_graph_attribute($c);
2554 unless (defined $a && $a->[ 0 ] == $g->[ _G
]) {
2555 $a->[ 0 ] = $g->[ _G
];
2556 $a->[ 1 ] = $code->( $g, @_ );
2557 $g->set_graph_attribute($c, $a);
2561 Carp
::croak
("Graph: unknown cache type '$type'");
2566 my ($g, $type) = @_;
2567 my $c = $_cache_type{$type};
2569 $g->delete_graph_attribute($c);
2571 Carp
::croak
("Graph: unknown cache type '$type'");
2575 sub connectivity_clear_cache
{
2577 _clear_cache
($g, 'connectivity');
2580 sub strong_connectivity_clear_cache
{
2582 _clear_cache
($g, 'strong_connectivity');
2585 sub biconnectivity_clear_cache
{
2587 _clear_cache
($g, 'biconnectivity');
2590 sub SPT_Dijkstra_clear_cache
{
2592 _clear_cache
($g, 'SPT_Dijkstra');
2593 $g->delete_graph_attribute('SPT_Dijkstra_first_root');
2596 sub SPT_Bellman_Ford_clear_cache
{
2598 _clear_cache
($g, 'SPT_Bellman_Ford');
2602 # Connected components.
2605 sub _connected_components_compute
{
2610 if ($g->has_union_find) {
2611 my $UF = $g->_get_union_find();
2613 my %icce; # Isolated vertices.
2616 for my $v ( $g->unique_vertices ) {
2617 $cc = $UF->find( $V->_get_path_id( $v ) );
2620 push @
{ $cci{ $cc } }, $v;
2623 push @
{ $icci{ $icc } }, $v;
2628 @cce{ keys %icce } = values %icce;
2629 @cci{ keys %icci } = values %icci;
2632 my @u = $g->unique_vertices;
2633 my %r; @r{ @u } = @u;
2641 my $t = Graph
::Traversal
::DFS
->new($g,
2642 first_root
=> $froot,
2643 next_root
=> $nroot,
2647 push @
{ $cci{ $cc } }, $v;
2653 return [ \
%cce, \
%cci ];
2656 sub _connected_components
{
2658 my $ccc = _check_cache
($g, 'connectivity',
2659 \
&_connected_components_compute
, @_);
2663 sub connected_component_by_vertex
{
2665 $g->expect_undirected;
2666 my ($CCE, $CCI) = $g->_connected_components();
2667 return $CCE->{ $v };
2670 sub connected_component_by_index
{
2672 $g->expect_undirected;
2673 my ($CCE, $CCI) = $g->_connected_components();
2674 return defined $CCI->{ $i } ? @
{ $CCI->{ $i } } : ( );
2677 sub connected_components
{
2679 $g->expect_undirected;
2680 my ($CCE, $CCI) = $g->_connected_components();
2681 return values %{ $CCI };
2684 sub same_connected_components
{
2686 $g->expect_undirected;
2687 if ($g->has_union_find) {
2688 my $UF = $g->_get_union_find();
2691 my $c = $UF->find( $V->_get_path_id ( $u ) );
2695 unless defined($d = $UF->find( $V->_get_path_id( $v ) )) &&
2700 my ($CCE, $CCI) = $g->_connected_components();
2702 my $c = $CCE->{ $u };
2705 unless defined $CCE->{ $v } &&
2712 my $super_component = sub { join("+", sort @_) };
2714 sub connected_graph
{
2716 $g->expect_undirected;
2717 my $cg = Graph
->new(undirected
=> 1);
2718 if ($g->has_union_find && $g->vertices == 1) {
2719 # TODO: super_component?
2720 $cg->add_vertices($g->vertices);
2723 exists $opt{super_component
} ?
2724 $opt{super_component
} : $super_component;
2725 for my $cc ( $g->connected_components() ) {
2726 my $sc = $sc_cb->(@
$cc);
2727 $cg->add_vertex($sc);
2728 $cg->set_vertex_attribute($sc, 'subvertices', [ @
$cc ]);
2736 $g->expect_undirected;
2737 my ($CCE, $CCI) = $g->_connected_components();
2738 return keys %{ $CCI } == 1;
2741 sub is_weakly_connected
{
2743 $g->expect_directed;
2744 $g->undirected_copy->is_connected(@_);
2747 *weakly_connected
= \
&is_weakly_connected
;
2749 sub weakly_connected_components
{
2751 $g->expect_directed;
2752 $g->undirected_copy->connected_components(@_);
2755 sub weakly_connected_component_by_vertex
{
2757 $g->expect_directed;
2758 $g->undirected_copy->connected_component_by_vertex(@_);
2761 sub weakly_connected_component_by_index
{
2763 $g->expect_directed;
2764 $g->undirected_copy->connected_component_by_index(@_);
2767 sub same_weakly_connected_components
{
2769 $g->expect_directed;
2770 $g->undirected_copy->same_connected_components(@_);
2773 sub weakly_connected_graph
{
2775 $g->expect_directed;
2776 $g->undirected_copy->connected_graph(@_);
2779 sub _strongly_connected_components_compute
{
2781 my $t = Graph
::Traversal
::DFS
->new($g);
2782 my @d = reverse $t->dfs;
2784 my $h = $g->transpose_graph;
2786 Graph
::Traversal
::DFS
->new($h,
2790 while (defined($root = shift @d)) {
2791 last if exists $u->{ $root };
2793 if (defined $root) {
2802 push @
{ $c[-1] }, $v;
2809 sub _strongly_connected_components
{
2811 my $scc = _check_cache
($g, 'strong_connectivity',
2812 \
&_strongly_connected_components_compute
, @_);
2813 return defined $scc ? @
$scc : ( );
2816 sub strongly_connected_components
{
2818 $g->expect_directed;
2819 $g->_strongly_connected_components(@_);
2822 sub strongly_connected_component_by_vertex
{
2825 $g->expect_directed;
2826 my @scc = $g->_strongly_connected_components( next_alphabetic
=> 1, @_ );
2827 for (my $i = 0; $i <= $#scc; $i++) {
2828 for (my $j = 0; $j <= $#{ $scc[$i] }; $j++) {
2829 return $i if $scc[$i]->[$j] eq $v;
2835 sub strongly_connected_component_by_index
{
2838 $g->expect_directed;
2839 my $c = ( $g->_strongly_connected_components(@_) )[ $i ];
2840 return defined $c ? @
{ $c } : ();
2843 sub same_strongly_connected_components
{
2845 $g->expect_directed;
2846 my @scc = $g->_strongly_connected_components( next_alphabetic
=> 1, @_ );
2850 for (my $i = 0; $i <= $#scc; $i++) {
2851 for (my $j = 0; $j <= $#{ $scc[$i] }; $j++) {
2852 if ($scc[$i]->[$j] eq $v) {
2854 return 0 if @i > 1 && $i[-1] ne $i[0];
2862 sub is_strongly_connected
{
2864 $g->expect_directed;
2865 my $t = Graph
::Traversal
::DFS
->new($g);
2866 my @d = reverse $t->dfs;
2868 my $h = $g->transpose;
2870 Graph
::Traversal
::DFS
->new($h,
2874 while (defined($root = shift @d)) {
2875 last if exists $u->{ $root };
2877 if (defined $root) {
2878 unless (@
{ $t->{ roots
} }) {
2891 push @
{ $c[-1] }, $v;
2895 return @
{ $u->{ roots
} } == 1 && keys %{ $u->{ unseen
} } == 0;
2898 *strongly_connected
= \
&is_strongly_connected
;
2900 sub strongly_connected_graph
{
2904 $g->expect_directed;
2906 my $t = Graph
::Traversal
::DFS
->new($g);
2907 my @d = reverse $t->dfs;
2909 my $h = $g->transpose;
2911 Graph
::Traversal
::DFS
->new($h,
2915 while (defined($root = shift @d)) {
2916 last if exists $u->{ $root };
2918 if (defined $root) {
2927 push @
{ $c[-1] }, $v;
2936 _opt_get
(\
%attr, super_component
=> \
$sc_cb);
2937 _opt_get
(\
%attr, hypervertex
=> \
$hv_cb);
2938 _opt_unknown
(\
%attr);
2940 if (defined $hv_cb && !defined $sc_cb) {
2941 $sc_cb = sub { $hv_cb->( [ @_ ] ) };
2943 unless (defined $sc_cb) {
2944 $sc_cb = $super_component;
2951 for (my $i = 0; $i < @c; $i++) {
2953 $s->add_vertex( $s[$i] = $sc_cb->(@
$c) );
2954 $s->set_vertex_attribute($s[$i], 'subvertices', [ @
$c ]);
2961 for my $v ($g->vertices) {
2962 unless (exists $c{$v}) {
2969 for my $e ($g->edges05) {
2970 my ($u, $v) = @
$e; # @TODO: hyperedges
2971 unless ($c{$u} == $c{$v}) {
2972 my ($p, $q) = ( $s[ $c{ $u } ], $s[ $c{ $v } ] );
2973 $s->add_edge($p, $q) unless $s->has_edge($p, $q);
2977 if (my @i = $g->isolated_vertices) {
2978 $s->add_vertices(map { $s[ $c{ $_ } ] } @i);
2989 my ($S, $v, $c) = @_;
2996 return [ values %b, $c ];
2999 sub _biconnectivity_compute
{
3001 my ($opt, $unseenh, $unseena, $r, $next, $code, $attr) =
3003 return () unless defined $r;
3006 for my $v ($g->vertices) {
3011 my %S; # Self-loops.
3012 for my $e ($g->edges) {
3016 $S{ $u } = 1 if $u eq $v;
3021 my %L = ( $r => 1 );
3024 my @V = $g->vertices;
3029 my %T; @T{ @V } = @V;
3032 my @s = $g->successors( $w );
3034 @s = grep { $_ eq $w ?
( delete $T{ $w }, 0 ) : 1 } @s;
3035 @
{ $A{ $w } }{ @s } = @s;
3036 } elsif ($g->predecessors( $w ) == 0) {
3052 # print "T : ", Dumper(\%T);
3053 # print "A : ", Dumper(\%A);
3063 # print "T = ", Dumper(\%T);
3067 my @w = _shuffle
values %{ $A{ $v } };
3069 $w = first
{ !$U{ $v }{ $_ } } @w;
3074 if ($I{ $w } == 0) {
3082 $L{ $v } = $I{ $w } if $I{ $w } < $L{ $v };
3085 } while (defined $w);
3086 # print "U = ", Dumper(\%U);
3087 # print "P = ", Dumper(\%P);
3088 # print "L = ", Dumper(\%L);
3089 if (!defined $P{ $v }) {
3091 } elsif ($P{ $v } ne $r) {
3092 if ($L{ $v } < $I{ $P{ $v } }) {
3093 $L{ $P{ $v } } = $L{ $v } if $L{ $v } < $L{ $P{ $v } };
3095 $AP{ $P{ $v } } = $P{ $v };
3096 push @C, _make_bcc
(\
@S, $v, $P{ $v } );
3100 for my $w (_shuffle
keys %{ $A{ $r } }) {
3102 unless ($U{ $r }{ $w }) {
3108 $AP{ $e } = $e if defined $e;
3109 push @C, _make_bcc
(\
@S, $v, $r);
3111 # print "AP = ", Dumper(\%AP);
3112 # print "C = ", Dumper(\@C);
3113 # print "L = ", Dumper(\%L);
3114 $v = defined $P{ $v } ?
$P{ $v } : $r;
3118 if (keys %{ $A{ $v } }) {
3119 if (!exists $P{ $v }) {
3120 for my $w (keys %{ $A{ $v } }) {
3121 $Avok++ if $U{ $v }{ $w };
3123 # print "Avok/1 = $Avok\n";
3124 $Avok = 0 unless $Avok == keys %{ $A{ $v } };
3125 # print "Avok/2 = $Avok\n";
3129 # print "Avok/3 = $Avok\n";
3134 last if @C == 0 && !exists $S{$v};
3136 for (my $i = 0; $i < @C; $i++) {
3137 for my $v (@
{ $C[ $i ]}) {
3138 $V2BC{ $v }{ $i }++;
3143 for (my $i = 0; $i < @C; $i++) {
3144 if (@
{ $C[ $i ] } == 2) {
3156 return [ [values %AP], \
@BC, \
@BR, \
%V2BC ];
3159 sub biconnectivity
{
3161 $g->expect_undirected;
3162 my $bcc = _check_cache
($g, 'biconnectivity',
3163 \
&_biconnectivity_compute
, @_);
3164 return defined $bcc ? @
$bcc : ( );
3167 sub is_biconnected
{
3169 my ($ap, $bc) = ($g->biconnectivity(@_))[0, 1];
3170 return defined $ap ? @
$ap == 0 && $g->vertices >= 3 : undef;
3173 sub is_edge_connected
{
3175 my ($br) = ($g->biconnectivity(@_))[2];
3176 return defined $br ? @
$br == 0 && $g->edges : undef;
3179 sub is_edge_separable
{
3181 my $c = $g->is_edge_connected;
3182 defined $c ?
!$c && $g->edges : undef;
3185 sub articulation_points
{
3187 my ($ap) = ($g->biconnectivity(@_))[0];
3188 return defined $ap ? @
$ap : ();
3191 *cut_vertices
= \
&articulation_points
;
3193 sub biconnected_components
{
3195 my ($bc) = ($g->biconnectivity(@_))[1];
3196 return defined $bc ? @
$bc : ();
3199 sub biconnected_component_by_index
{
3202 my ($bc) = ($g->biconnectivity(@_))[1];
3203 return defined $bc ?
$bc->[ $i ] : undef;
3206 sub biconnected_component_by_vertex
{
3209 my ($v2bc) = ($g->biconnectivity(@_))[3];
3210 return defined $v2bc->{ $v } ?
keys %{ $v2bc->{ $v } } : ();
3213 sub same_biconnected_components
{
3216 my @u = $g->biconnected_component_by_vertex($u, @_);
3218 my %ubc; @ubc{ @u } = ();
3221 my @v = $g->biconnected_component_by_vertex($v);
3223 my %vbc; @vbc{ @v } = ();
3225 for my $ui (keys %ubc) {
3226 if (exists $vbc{ $ui }) {
3231 return 0 unless defined $vi;
3237 sub biconnected_graph
{
3239 my ($bc, $v2bc) = ($g->biconnectivity, %opt)[1, 3];
3240 my $bcg = Graph
::Undirected
->new;
3242 exists $opt{super_component
} ?
3243 $opt{super_component
} : $super_component;
3245 $bcg->add_vertex(my $s = $sc_cb->(@
$c));
3246 $bcg->set_vertex_attribute($s, 'subvertices', [ @
$c ]);
3249 for my $i (0..$#$bc) {
3250 my @u = @
{ $bc->[ $i ] };
3251 my %i; @i{ @u } = ();
3252 for my $j (0..$#$bc) {
3254 my @v = @
{ $bc->[ $j ] };
3255 my %j; @j{ @v } = ();
3257 if (exists $j{ $u }) {
3258 unless ($k{ $i }{ $j }++) {
3259 $bcg->add_edge($sc_cb->(@
{$bc->[$i]}),
3260 $sc_cb->(@
{$bc->[$j]}));
3273 my ($br) = ($g->biconnectivity(@_))[2];
3274 return defined $br ? @
$br : ();
3282 my ($g, $h, $HF, $r, $attr, $unseen, $etc) = @_;
3283 my $etc_r = $etc->{ $r } || 0;
3284 for my $s ( grep { exists $unseen->{ $_ } } $g->successors( $r ) ) {
3285 my $t = $g->get_edge_attribute( $r, $s, $attr );
3286 $t = 1 unless defined $t;
3289 Carp
::croak
("Graph::SPT_Dijkstra: edge $r-$s is negative ($t)");
3291 if (!defined($etc->{ $s }) || ($etc_r + $t) < $etc->{ $s }) {
3292 my $etc_s = $etc->{ $s } || 0;
3293 $etc->{ $s } = $etc_r + $t;
3294 # print "$r - $s : setting $s to $etc->{ $s } ($etc_r, $etc_s)\n";
3295 $h->set_vertex_attribute( $s, $attr, $etc->{ $s });
3296 $h->set_vertex_attribute( $s, 'p', $r );
3297 $HF->add( Graph
::SPTHeapElem
->new($r, $s, $etc->{ $s }) );
3302 sub _SPT_Dijkstra_compute
{
3307 my %opt = @_ == 1 ?
(first_root
=> $_[0]) : @_;
3308 my $first_root = $opt{ first_root
};
3309 unless (defined $first_root) {
3310 $opt{ first_root
} = $first_root = $g->random_vertex();
3312 my $spt_di = $g->get_graph_attribute('_spt_di');
3313 unless (defined $spt_di && exists $spt_di->{ $first_root } && $spt_di->{ $first_root }->[ 0 ] == $g->[ _G
]) {
3315 my $sptg = $g->_heap_walk($g->new, \
&_SPT_add
, \
%etc, %opt);
3316 $spt_di->{ $first_root } = [ $g->[ _G
], $sptg ];
3317 $g->set_graph_attribute('_spt_di', $spt_di);
3320 my $spt = $spt_di->{ $first_root }->[ 1 ];
3322 $spt->set_graph_attribute('SPT_Dijkstra_root', $first_root);
3327 *SSSP_Dijkstra
= \
&SPT_Dijkstra
;
3329 *single_source_shortest_paths
= \
&SPT_Dijkstra
;
3332 my ($g, $u, $v) = @_;
3333 my $sptg = $g->SPT_Dijkstra(first_root
=> $u);
3336 my $V = $g->vertices;
3338 while (defined($p = $sptg->get_vertex_attribute($v, 'p'))) {
3339 last if exists $seen{$p};
3343 last if keys %seen == $V || $u eq $v;
3345 @path = () if @path && $path[-1] ne $u;
3346 return reverse @path;
3349 sub __SPT_Bellman_Ford
{
3350 my ($g, $u, $v, $attr, $d, $p, $c0, $c1) = @_;
3351 return unless $c0->{ $u };
3352 my $w = $g->get_edge_attribute($u, $v, $attr);
3353 $w = 1 unless defined $w;
3354 if (defined $d->{ $v }) {
3355 if (defined $d->{ $u }) {
3356 if ($d->{ $v } > $d->{ $u } + $w) {
3357 $d->{ $v } = $d->{ $u } + $w;
3361 } # else !defined $d->{ $u } && defined $d->{ $v }
3363 if (defined $d->{ $u }) {
3364 # defined $d->{ $u } && !defined $d->{ $v }
3365 $d->{ $v } = $d->{ $u } + $w;
3368 } # else !defined $d->{ $u } && !defined $d->{ $v }
3372 sub _SPT_Bellman_Ford
{
3373 my ($g, $opt, $unseenh, $unseena, $r, $next, $code, $attr) = @_;
3375 return unless defined $r;
3378 my $V = $g->vertices;
3379 my %c0; # Changed during the last iteration?
3381 for (my $i = 0; $i < $V; $i++) {
3383 for my $e ($g->edges) {
3385 __SPT_Bellman_Ford
($g, $u, $v, $attr, \
%d, \
%p, \
%c0, \
%c1);
3386 if ($g->undirected) {
3387 __SPT_Bellman_Ford
($g, $v, $u, $attr, \
%d, \
%p, \
%c0, \
%c1);
3390 %c0 = %c1 unless $i == $V - 1;
3393 for my $e ($g->edges) {
3395 if (defined $d{ $u } && defined $d{ $v }) {
3396 my $d = $g->get_edge_attribute($u, $v, $attr);
3397 if (defined $d && $d{ $v } > $d{ $u } + $d) {
3399 Carp
::croak
("Graph::SPT_Bellman_Ford: negative cycle exists");
3407 sub _SPT_Bellman_Ford_compute
{
3410 sub SPT_Bellman_Ford
{
3413 my ($opt, $unseenh, $unseena, $r, $next, $code, $attr) = $g->_root_opt(@_);
3415 unless (defined $r) {
3416 $r = $g->random_vertex();
3417 return unless defined $r;
3420 my $spt_bf = $g->get_graph_attribute('_spt_bf');
3421 unless (defined $spt_bf &&
3422 exists $spt_bf->{ $r } && $spt_bf->{ $r }->[ 0 ] == $g->[ _G
]) {
3424 $g->_SPT_Bellman_Ford($opt, $unseenh, $unseena,
3425 $r, $next, $code, $attr);
3427 for my $v (keys %$p) {
3429 $h->add_edge( $u, $v );
3430 $h->set_edge_attribute( $u, $v, $attr,
3431 $g->get_edge_attribute($u, $v, $attr));
3432 $h->set_vertex_attribute( $v, $attr, $d->{ $v } );
3433 $h->set_vertex_attribute( $v, 'p', $u );
3435 $spt_bf->{ $r } = [ $g->[ _G
], $h ];
3436 $g->set_graph_attribute('_spt_bf', $spt_bf);
3439 my $spt = $spt_bf->{ $r }->[ 1 ];
3441 $spt->set_graph_attribute('SPT_Bellman_Ford_root', $r);
3446 *SSSP_Bellman_Ford
= \
&SPT_Bellman_Ford
;
3448 sub SP_Bellman_Ford
{
3449 my ($g, $u, $v) = @_;
3450 my $sptg = $g->SPT_Bellman_Ford(first_root
=> $u);
3453 my $V = $g->vertices;
3455 while (defined($p = $sptg->get_vertex_attribute($v, 'p'))) {
3456 last if exists $seen{$p};
3460 last if keys %seen == $V;
3462 # @path = () if @path && "$path[-1]" ne "$u";
3463 return reverse @path;
3467 # Transitive Closure.
3470 sub TransitiveClosure_Floyd_Warshall
{
3472 my $class = ref $self || $self;
3473 $self = shift unless ref $self;
3474 bless Graph
::TransitiveClosure
->new($self, @_), $class;
3477 *transitive_closure
= \
&TransitiveClosure_Floyd_Warshall
;
3479 sub APSP_Floyd_Warshall
{
3481 my $class = ref $self || $self;
3482 $self = shift unless ref $self;
3483 bless Graph
::TransitiveClosure
->new($self, path
=> 1, @_), $class;
3486 *all_pairs_shortest_paths
= \
&APSP_Floyd_Warshall
;
3488 sub _transitive_closure_matrix_compute
{
3491 sub transitive_closure_matrix
{
3493 my $tcm = $g->get_graph_attribute('_tcm');
3495 if (ref $tcm eq 'ARRAY') { # YECHHH!
3496 if ($tcm->[ 0 ] == $g->[ _G
]) {
3503 unless (defined $tcm) {
3504 my $apsp = $g->APSP_Floyd_Warshall(@_);
3505 $tcm = $apsp->get_graph_attribute('_tcm');
3506 $g->set_graph_attribute('_tcm', [ $g->[ _G
], $tcm ]);
3514 my $tcm = $g->transitive_closure_matrix;
3515 $tcm->path_length(@_);
3518 sub path_predecessor
{
3520 my $tcm = $g->transitive_closure_matrix;
3521 $tcm->path_predecessor(@_);
3526 my $tcm = $g->transitive_closure_matrix;
3527 $tcm->path_vertices(@_);
3532 my $tcm = $g->transitive_closure_matrix;
3533 $tcm->is_reachable(@_);
3536 sub for_shortest_paths
{
3539 my $t = $g->transitive_closure_matrix;
3540 my @v = $g->vertices;
3544 next unless $t->is_reachable($u, $v);
3546 $c->($t, $u, $v, $n);
3558 $g->for_shortest_paths(sub {
3559 my ($t, $u, $v, $n) = @_;
3560 my $l = $t->path_length($u, $v);
3561 return unless defined $l;
3563 if ($u ne $v && (!defined $max || $l > $max)) {
3565 $maxp = $p = [ $t->path_vertices($u, $v) ];
3567 if ($u ne $v && (!defined $min || $l < $min)) {
3569 $minp = $p || [ $t->path_vertices($u, $v) ];
3572 return ($min, $max, $minp, $maxp);
3577 my ($min, $max, $minp, $maxp) = $g->_minmax_path(@_);
3578 return defined $maxp ?
(wantarray ? @
$maxp : $max) : undef;
3581 *graph_diameter
= \
&diameter
;
3584 my ($g, $u, $v) = @_;
3585 my $t = $g->transitive_closure_matrix;
3589 $t->path_vertices($u, $v) : $t->path_length($u, $v);
3593 for my $v ($g->vertices) {
3595 my $l = $t->path_length($u, $v);
3596 if (defined $l && (!defined $max || $l > $max)) {
3598 @max = $t->path_vertices($u, $v);
3601 return wantarray ?
@max : $max;
3607 for my $u ($g->vertices) {
3609 my $l = $t->path_length($u, $v);
3610 if (defined $l && (!defined $max || $l > $max)) {
3612 @max = $t->path_vertices($u, $v);
3615 return wantarray ?
@max : @max - 1;
3617 my ($min, $max, $minp, $maxp) = $g->_minmax_path(@_);
3618 return defined $maxp ?
(wantarray ? @
$maxp : $max) : undef;
3623 sub vertex_eccentricity
{
3625 $g->expect_undirected;
3626 if ($g->is_connected) {
3628 for my $v ($g->vertices) {
3630 my $l = $g->path_length($u, $v);
3631 if (defined $l && (!defined $max || $l > $max)) {
3642 my ($g, $u, $v) = @_;
3643 $g->expect_undirected;
3644 my $t = $g->transitive_closure_matrix;
3648 $t->path_vertices($u, $v) : $t->path_length($u, $v);
3652 for my $v ($g->vertices) {
3654 my $l = $t->path_length($u, $v);
3655 if (defined $l && (!defined $min || $l < $min)) {
3657 @min = $t->path_vertices($u, $v);
3660 return wantarray ?
@min : $min;
3666 for my $u ($g->vertices) {
3668 my $l = $t->path_length($u, $v);
3669 if (defined $l && (!defined $min || $l < $min)) {
3671 @min = $t->path_vertices($u, $v);
3674 return wantarray ?
@min : $min;
3676 my ($min, $max, $minp, $maxp) = $g->_minmax_path(@_);
3677 return defined $minp ?
(wantarray ? @
$minp : $min) : undef;
3684 $g->expect_undirected;
3685 my ($center, $radius) = (undef, Infinity
());
3686 for my $v ($g->vertices) {
3687 my $x = $g->vertex_eccentricity($v);
3688 ($center, $radius) = ($v, $x) if defined $x && $x < $radius;
3693 sub center_vertices
{
3694 my ($g, $delta) = @_;
3695 $g->expect_undirected;
3696 $delta = 0 unless defined $delta;
3697 $delta = abs($delta);
3701 for my $v ($g->vertices) {
3702 my $e = $g->vertex_eccentricity($v);
3703 next unless defined $e;
3704 push @c, $v if abs($e - $r) <= $delta;
3710 *centre_vertices
= \
¢er_vertices
;
3712 sub average_path_length
{
3717 my $n = $g->for_shortest_paths(sub {
3718 my ($t, $u, $v, $n) = @_;
3719 my $l = $t->path_length($u, $v);
3722 (@A == 1 && $u eq $A[0]) ||
3734 return $m ?
$d / $m : undef;
3741 sub is_multi_graph
{
3743 return 0 unless $g->is_multiedged || $g->is_countedged;
3745 for my $e ($g->edges05) {
3748 return 0 if $u eq $v;
3750 $multiedges++ if $g->get_edge_count(@
$e) > 1;
3755 sub is_simple_graph
{
3757 return 1 unless $g->is_countedged || $g->is_multiedged;
3758 for my $e ($g->edges05) {
3759 return 0 if $g->get_edge_count(@
$e) > 1;
3764 sub is_pseudo_graph
{
3766 my $m = $g->is_countedged || $g->is_multiedged;
3767 for my $e ($g->edges05) {
3770 return 1 if $u eq $v;
3772 return 1 if $m && $g->get_edge_count($u, @v) > 1;
3778 # Rough isomorphism guess.
3781 my %_factorial = (0 => 1, 1 => 1);
3785 for (my $i = 2; $i <= $n; $i++) {
3786 next if exists $_factorial{$i};
3787 $_factorial{$i} = $i * $_factorial{$i - 1};
3796 Carp
::croak
("factorial of a negative number");
3798 __factorial
($n) unless exists $_factorial{$n};
3799 return $_factorial{$n};
3802 sub could_be_isomorphic
{
3804 return 0 unless $g0->vertices == $g1->vertices;
3805 return 0 unless $g0->edges05 == $g1->edges05;
3807 for my $v0 ($g0->vertices) {
3808 $d0{ $g0->in_degree($v0) }{ $g0->out_degree($v0) }++
3811 for my $v1 ($g1->vertices) {
3812 $d1{ $g1->in_degree($v1) }{ $g1->out_degree($v1) }++
3814 return 0 unless keys %d0 == keys %d1;
3815 for my $da (keys %d0) {
3817 unless exists $d1{$da} &&
3818 keys %{ $d0{$da} } == keys %{ $d1{$da} };
3819 for my $db (keys %{ $d0{$da} }) {
3821 unless exists $d1{$da}{$db} &&
3822 $d0{$da}{$db} == $d1{$da}{$db};
3825 for my $da (keys %d0) {
3826 for my $db (keys %{ $d0{$da} }) {
3827 return 0 unless $d1{$da}{$db} == $d0{$da}{$db};
3831 return 0 unless keys %d1 == 0;
3833 for my $da (keys %d0) {
3834 for my $db (keys %{ $d0{$da} }) {
3835 $f *= _factorial
(abs($d0{$da}{$db}));
3846 require Data
::Dumper
;
3847 my $d = Data
::Dumper
->new([$_[0]],[ref $_[0]]);
3848 defined wantarray ?
$d->Dump : print $d->Dump;