File Coverage

blib/lib/Graph.pm
Criterion Covered Total %
statement 1794 1810 99.5
branch 674 808 83.4
condition 204 255 80.0
subroutine 341 341 100.0
pod 247 247 100.0
total 3260 3461 94.4


line stmt bran cond sub pod time code
1             package Graph;
2              
3 84     84   6163463 use strict;
  84         174  
  84         3632  
4 84     84   415 use warnings;
  84         174  
  84         7376  
5 84 50   84   8318 BEGIN { warnings->unimport('recursion') if $ENV{GRAPH_ALLOW_RECURSION} }
6              
7 20     20   139 sub __carp_confess { require Carp; Carp::confess(@_) }
  20         6090  
8             BEGIN {
9 84     84   2873 if (0) { # SET THIS TO ZERO FOR TESTING AND RELEASES!
10             $SIG{__DIE__ } = \&__carp_confess;
11             $SIG{__WARN__} = \&__carp_confess;
12             }
13             }
14              
15 84     84   45039 use Graph::AdjacencyMap qw(:flags :fields);
  84         299  
  84         51416  
16              
17             our $VERSION = '0.9735';
18              
19             require 5.006; # Weak references are absolutely required.
20              
21             my @GRAPH_PROPS_COPIED = qw(
22             undirected refvertexed countvertexed multivertexed __stringified
23             hyperedged countedged multiedged
24             );
25             my $_empty_array = [];
26 1     1   7 sub _empty_array () { $_empty_array }
27              
28             my $can_deep_copy_Storable;
29             sub _can_deep_copy_Storable () {
30 25 100   25   1109 return $can_deep_copy_Storable if defined $can_deep_copy_Storable;
31 4 50       15 return $can_deep_copy_Storable = 0 if $] < 5.010; # no :load tag Safe 5.8
32 4         7 eval {
33 4         22 require Storable;
34 4         13 require B::Deparse;
35 4         125 Storable->VERSION(2.05);
36 4         122 B::Deparse->VERSION(0.61);
37             };
38 4         26 $can_deep_copy_Storable = !$@;
39             }
40              
41             sub _F () { 0 } # Flags.
42             sub _G () { 1 } # Generation.
43             sub _V () { 2 } # Vertices.
44             sub _E () { 3 } # Edges.
45             sub _A () { 4 } # Attributes.
46             sub _U () { 5 } # Union-Find.
47              
48             my $Inf;
49              
50             BEGIN {
51 84 50   84   716 if ($] >= 5.022) {
52 84         5988 $Inf = eval '+"Inf"'; # uncoverable statement
53             } else {
54 0         0 local $SIG{FPE}; # uncoverable statement
55 0         0 eval { $Inf = exp(999) } || # uncoverable statement
56 0         0 eval { $Inf = 9**9**9 } || # uncoverable statement
57 0 0 0     0 eval { $Inf = 1e+999 } || # uncoverable statement
  0   0     0  
58             { $Inf = 1e+99 }; # uncoverable statement
59             # Close enough for most practical purposes.
60             }
61             }
62              
63 44     44 1 135 sub 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             # - unionfind
72             # The vertices are contained in a "simplemap"
73             # (if no attributes) 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             # expects one for vertices and two for edges
78             # - _UNORD for unordered coordinates (a set): if _UNORD is not set
79             # the coordinates are assumed to be meaningfully ordered
80             # Vertices and edges assume none of these flags.
81              
82 84     84   48027 use Graph::Attribute array => _A, map => 'graph';
  84         265  
  84         588  
83              
84             sub stringify {
85 645     645 1 45818 my ($u, $h) = (&is_undirected, &is_hyperedged);
86 645 100       2134 my $e = $u ? '=' : '-';
87             my @edges = map join($e,
88 3155         11880 $u ? sort { "$a" cmp "$b" } @$_ :
89 645 100       1670 $h ? map '['.join(",", sort { "$a" cmp "$b" } @$_).']', @$_ :
  8 100       38  
90             @$_), &_edges05;
91 645         6291 my @s = sort @edges;
92 645         1927 push @s, sort { "$a" cmp "$b" } &isolated_vertices;
  107         366  
93 645         6640 join(",", @s);
94             }
95              
96             sub eq {
97 318     318 1 187158 "$_[0]" eq "$_[1]"
98             }
99              
100             sub boolify {
101 280     280 1 33062 1; # Important for empty graphs: they stringify to "", which is false.
102             }
103              
104             sub ne {
105 11     11 1 5963 "$_[0]" ne "$_[1]"
106             }
107              
108             use overload
109 84         688 '""' => \&stringify,
110             'bool' => \&boolify,
111             'eq' => \&eq,
112 84     84   60284 'ne' => \≠
  84         166739  
113              
114             sub _opt {
115 6864     6864   26202 my ($opt, $flags, %flags) = @_;
116 6864         22660 while (my ($flag, $FLAG) = each %flags) {
117 20592 100       44116 $$flags |= $FLAG if delete $opt->{$flag};
118 20592 50       77169 $$flags &= ~$FLAG if delete $opt->{"non$flag"};
119             }
120             }
121              
122             sub _opt_get {
123 6     6   19 my ($opt, $key, $var) = @_;
124 6 100       25 return if !exists $opt->{$key};
125 1         4 $$var = delete $opt->{$key};
126             }
127              
128             sub _opt_unknown {
129 2645     2645   5530 my ($opt) = @_;
130 2645 100       10175 return unless my @opt = keys %$opt;
131 6 100       16 __carp_confess sprintf
132 6         58 "@{[(caller(1))[3]]}: Unknown option%s: @{[map qq['$_'], sort @opt]}",
  6         91  
133             @opt > 1 ? 's' : '';
134             }
135              
136             sub _opt_from_existing {
137 1496     1496   3179 my ($g) = @_;
138 1496         2540 my %existing;
139 1496         5699 $existing{$_}++ for grep $g->$_, @GRAPH_PROPS_COPIED;
140 1496 100       3776 $existing{unionfind}++ if $g->has_union_find;
141 1496         8728 %existing;
142             }
143              
144             sub _opt_to_vflags {
145 2288     2288   5436 my ($vflags, $opt) = (0, @_);
146 2288         8417 _opt($opt, \$vflags,
147             countvertexed => _COUNT,
148             multivertexed => _MULTI,
149             refvertexed => _REF,
150             refvertexed_stringified => _REFSTR ,
151             __stringified => _STR,
152             );
153 2288         5685 $vflags;
154             }
155              
156             sub _opt_to_eflags {
157 2288     2288   4933 my ($eflags, $opt) = (0, @_);
158 2288 100       8800 $opt->{undirected} = !delete $opt->{directed} if exists $opt->{directed};
159 2288         7534 _opt($opt, \$eflags,
160             countedged => _COUNT,
161             multiedged => _MULTI,
162             undirected => _UNORD,
163             );
164 2288         7027 ($eflags, delete $opt->{hyperedged});
165             }
166              
167             sub new {
168 2288     2288 1 14639380 my ($class, @args) = @_;
169 2288         4251 my $gflags = 0;
170 2288         8120 my %opt = _get_options( \@args );
171              
172 2288 100 66     15257 %opt = (_opt_from_existing($class), %opt) # allow overrides
173             if ref $class && $class->isa('Graph');
174              
175 2288         7578 my $vflags = _opt_to_vflags(\%opt);
176 2288         6321 my ($eflags, $is_hyper) = _opt_to_eflags(\%opt);
177              
178 2288         7243 _opt(\%opt, \$gflags,
179             unionfind => _UNIONFIND,
180             );
181              
182 2288         4559 my @V;
183 2288 100       11155 if ($opt{vertices}) {
184             __carp_confess "Graph: vertices should be an array ref"
185 80 50       358 if ref $opt{vertices} ne 'ARRAY';
186 80         146 @V = @{ delete $opt{vertices} };
  80         413  
187             }
188              
189 2288         3952 my @E;
190 2288 100       6016 if ($opt{edges}) {
191             __carp_confess "Graph: edges should be an array ref of array refs"
192 8 50       39 if ref $opt{edges} ne 'ARRAY';
193 8         16 @E = @{ delete $opt{edges} };
  8         24  
194 8 50       49 __carp_confess "Graph: edges should be array refs"
195             if grep ref $_ ne 'ARRAY', @E;
196             }
197              
198 2288         7296 _opt_unknown(\%opt);
199              
200 2285 100 100     8871 __carp_confess "Graph: both countvertexed and multivertexed"
201             if ($vflags & _COUNT) && ($vflags & _MULTI);
202              
203 2284 100 100     7367 __carp_confess "Graph: both countedged and multiedged"
204             if ($eflags & _COUNT) && ($eflags & _MULTI);
205              
206 2283   66     10707 my $g = bless [ ], ref $class || $class;
207 2283         12721 $g->[ _F ] = $gflags;
208 2283         4292 $g->[ _G ] = 0;
209 2283         5582 $g->[ _V ] = _make_v($vflags);
210 2283         5876 $g->[ _E ] = _make_e($is_hyper, $eflags);
211 2283 100       6648 $g->[ _U ] = do { require Graph::UnionFind; Graph::UnionFind->new }
  6         2483  
  6         48  
212             if $gflags & _UNIONFIND;
213 2283 100       5756 $g->add_vertices(@V) if @V;
214 2283 100       5178 $g->add_edges(@E) if @E;
215              
216 2283         10478 return $g;
217             }
218              
219             sub _make_v {
220 2283     2283   5118 my ($vflags) = @_;
221 2283 100       8955 $vflags ? _am_heavy($vflags, 1) : _am_light($vflags, 1);
222             }
223              
224             sub _make_e {
225 2283     2283   5794 my ($is_hyper, $eflags) = @_;
226 2283 100 100     12657 ($is_hyper or $eflags & ~_UNORD) ?
    100          
227             _am_heavy($eflags, $is_hyper ? 0 : 2) :
228             _am_light($eflags, 2);
229             }
230              
231             sub _am_light {
232 4342     4342   86858 require Graph::AdjacencyMap::Light;
233 4342         19450 Graph::AdjacencyMap::Light->_new(@_);
234             }
235              
236             sub _am_heavy {
237 224     224   1146 Graph::AdjacencyMap->_new(@_);
238             }
239              
240 3871     3871 1 22230 sub countvertexed { $_[0]->[ _V ]->_is_COUNT }
241 9573     9573 1 57973 sub multivertexed { $_[0]->[ _V ]->_is_MULTI }
242 1516     1516 1 6105 sub refvertexed { $_[0]->[ _V ]->_is_REF }
243 1     1 1 7 sub refvertexed_stringified { $_[0]->[ _V ]->_is_REFSTR }
244 1496     1496   4442 sub __stringified { $_[0]->[ _V ]->_is_STR }
245              
246 1938     1938 1 10068 sub countedged { $_[0]->[ _E ]->_is_COUNT }
247 68895     68895 1 678214 sub multiedged { $_[0]->[ _E ]->_is_MULTI }
248 184030     184030 1 503660 sub hyperedged { !$_[0]->[ _E ]->[ _arity ] }
249 70861     70861 1 227419 sub undirected { $_[0]->[ _E ]->_is_UNORD }
250              
251 72578     72578 1 708177 sub directed { ! $_[0]->[ _E ]->_is_UNORD }
252              
253             *is_directed = \&directed;
254             *is_undirected = \&undirected;
255              
256             *is_countvertexed = \&countvertexed;
257             *is_multivertexed = \&multivertexed;
258             *is_refvertexed = \&refvertexed;
259             *is_refvertexed_stringified = \&refvertexed_stringified;
260              
261             *is_countedged = \&countedged;
262             *is_multiedged = \&multiedged;
263             *is_hyperedged = \&hyperedged;
264              
265 46762     46762 1 112460 sub has_union_find { $_[0]->[ _U ] }
266              
267             sub add_vertex {
268 2122 100   2122 1 33192 __carp_confess "Graph::add_vertex: use add_vertices for more than one vertex" if @_ != 2;
269 2117 100       6352 __carp_confess "Graph::add_vertex: undef vertex" if grep !defined, @_;
270 2116         5202 goto &add_vertices;
271             }
272              
273             sub has_vertex {
274 12025     12025 1 72475 my $g = $_[0];
275 12025         18356 my $V = $g->[ _V ];
276 12025 100       27008 return defined $V->has_path($_[1]) if ($V->[ _f ] & _REF);
277 11491         64719 exists $V->[ _pi ]->{ $_[1] };
278             }
279              
280             sub _vertices05 {
281 3541     3541   5486 my $g = $_[0];
282 3541         11251 $g->[ _V ]->paths;
283             }
284              
285             sub vertices {
286 2359     2359 1 67204 my $g = $_[0];
287 2359         4436 my @v = &_vertices05;
288 2359 100 100     5010 return @v if !(&is_multivertexed || &is_countvertexed);
289 13 100       32 return map +(($_) x $g->get_vertex_count($_)), @v if wantarray;
290 12         15 my $V = 0;
291 12         40 $V += $g->get_vertex_count($_) for @v;
292 12         53 return $V;
293             }
294              
295             *unique_vertices = \&_vertices05;
296              
297             sub has_vertices {
298 22     22 1 7077 my $g = shift;
299 22         114 scalar $g->[ _V ]->has_any_paths;
300             }
301              
302             sub add_edge {
303 37710 100   37710 1 131430 &expect_hyperedged, &expect_undirected if @_ != 3;
304 37708         126832 $_[0]->add_edges([ @_[1..$#_] ]);
305             }
306              
307             sub _vertex_ids_ensure {
308 127     127   268 push @_, 1;
309 127         266 goto &_vertex_ids_maybe_ensure;
310             }
311              
312             sub _vertex_ids_ensure_multi {
313 114     114   190 my $id = pop;
314 114         988 my @i = &_vertex_ids_ensure;
315 114         232 push @_, $id;
316 114 50       455 @i ? (@i, $id) : ();
317             }
318              
319             sub _vertex_ids {
320 77433     77433   172925 push @_, 0;
321 77433         161041 goto &_vertex_ids_maybe_ensure;
322             }
323              
324             sub _vertex_ids_multi {
325 635     635   1039 my $id = pop;
326 635         1050 my @i = &_vertex_ids;
327 635         1109 push @_, $id;
328 635 100       7138 @i ? (@i, $id) : ();
329             }
330              
331             sub _vertex_ids_maybe_ensure {
332 77560     77560   124454 my $ensure = pop;
333 77560         210495 my ($g, @args) = @_;
334 77560 50       237961 __carp_confess "Graph: given undefined vertex" if grep !defined, @args;
335 77560         131400 my $V = $g->[ _V ];
336 77560   100     140391 my $deep = &is_hyperedged && &is_directed;
337 77560 100 100     330892 return $V->get_ids_by_paths(\@args, $ensure, $deep) if ($V->[ _f ] & _REF) or $deep;
338 77216         125144 my $pi = $V->[ _pi ];
339 77216         238183 my @non_exist = grep !exists $pi->{ $_ }, @args;
340 77216 100 100     287082 return if !$ensure and @non_exist;
341 75911 100       149373 $V->get_ids_by_paths(\@non_exist, 1) if @non_exist;
342 75911         671105 @$pi{ @args };
343             }
344              
345             sub has_edge {
346 58522     58522 1 165570 my $g = $_[0];
347 58522         100259 my $E = $g->[ _E ];
348 58522         157656 my ($Ef, $Ea) = @$E[ _f, _arity ];
349 58522 100 100     287270 return 0 if $Ea and @_ != $Ea + 1;
350 58518         112790 my $directed = &is_directed;
351 58518   100     121019 my $deep = &is_hyperedged && $directed;
352 58518 100       112400 return 0 if (my @i = &_vertex_ids) != @_ - 1;
353 57096 50       128679 return defined $E->has_path($directed ? \@i : [ map [ sort @$_ ], @i ]) if $deep;
    100          
354 57085 100       225772 @i = sort @i if !$directed;
355 57085         793595 exists $E->[ _pi ]{ "@i" };
356             }
357              
358             sub any_edge {
359 22     22 1 1412 my ($g, @args) = @_;
360 22         41 my $E = $g->[ _E ];
361 22         40 my $V = $g->[ _V ];
362 22 100       87 return 0 if (my @i = $V->get_ids_by_paths(\@args)) != @args;
363 16         69 $E->has_successor(@i);
364             }
365              
366             sub _edges05 {
367 1311     1311   2417 my $g = $_[0];
368 1311         4665 my @e = $g->[ _E ]->paths;
369 1311 100       3476 return @e if !wantarray;
370 1256   100     3134 $g->[ _V ]->get_paths_by_ids(\@e, &is_hyperedged && &is_directed);
371             }
372              
373             *unique_edges = \&_edges05;
374              
375             sub edges {
376 385     385 1 6298 my $g = $_[0];
377 385         869 my @e = &_edges05;
378 385 100 100     1298 return @e if !(&is_multiedged || &is_countedged);
379 53 100       299 return map +(($_) x $g->get_edge_count(@$_)), @e if wantarray;
380 14         21 my $E = 0;
381 14         58 $E += $g->get_edge_count(@$_) for @e;
382 14         108 return $E;
383             }
384              
385             sub has_edges {
386 7     7 1 479 scalar $_[0]->[ _E ]->has_any_paths;
387             }
388              
389             ###
390             # by_id
391             #
392              
393             sub add_vertex_by_id {
394 45     45 1 473 &expect_multivertexed;
395 44         102 my ($g, $v, $id) = @_;
396 44         91 my $V = $g->[ _V ];
397 44 100       160 return $g if $V->has_path_by_multi_id( my @args = ($v, $id) );
398 43         118 my ($i) = $V->set_path_by_multi_id( @args );
399 43 50       106 $g->[ _U ]->add($i) if &has_union_find;
400 43         109 $g->[ _G ]++;
401 43         533 return $g;
402             }
403              
404             sub add_vertex_get_id {
405 6     6 1 6854 &expect_multivertexed;
406 6         39 my ($g, $v) = @_;
407 6         23 my ($i, $multi_id) = $g->[ _V ]->set_path_by_multi_id( $v, _GEN_ID );
408 6 50       44 $g->[ _U ]->add($i) if &has_union_find;
409 6         15 $g->[ _G ]++;
410 6         23 return $multi_id;
411             }
412              
413             sub has_vertex_by_id {
414 177     177 1 5716 &expect_multivertexed;
415 176         456 my ($g, $v, $id) = @_;
416 176         539 $g->[ _V ]->has_path_by_multi_id( $v, $id );
417             }
418              
419             sub delete_vertex_by_id {
420 8     8 1 867 &expect_multivertexed;
421 7         23 &expect_non_unionfind;
422 7         21 my ($g, $v, $id) = @_;
423 7 100       21 return $g unless &has_vertex_by_id;
424 6 100       24 if ($g->[ _V ]->get_multi_ids( $v ) == 1) {
425             # only incarnation, zap edges
426 3         15 my @i = &_vertex_ids_multi;
427 3         8 pop @i; # the id
428 3         9 my $E = $g->[ _E ];
429 3         19 my @edges = $E->paths_from(@i);
430 3 50       11 push @edges, $E->paths_to(@i) if !&is_undirected;
431 3         19 $E->del_path( $_ ) for @edges;
432             }
433 6         31 $g->[ _V ]->del_path_by_multi_id( $v, $id );
434 6         11 $g->[ _G ]++;
435 6         37 return $g;
436             }
437              
438             sub get_multivertex_ids {
439 105     105 1 5568 &expect_multivertexed;
440 104         177 my $g = shift;
441 104         309 $g->[ _V ]->get_multi_ids( @_ );
442             }
443              
444             sub add_edge_by_id {
445 115     115 1 417 &expect_multiedged;
446 114         207 my $g = $_[0];
447 114         257 my @i = &_vertex_ids_ensure_multi;
448 114         305 my $id = pop @i;
449 114 100       240 @i = sort @i if &is_undirected;
450 114         492 $g->[ _E ]->set_path_by_multi_id( \@i, $id );
451 114         243 $g->[ _G ]++;
452 114 100       290 $g->[ _U ]->union(\@i) if &has_union_find;
453 114         1363 return $g;
454             }
455              
456             sub add_edge_get_id {
457 13     13 1 5867 &expect_multiedged;
458 13         29 my $g = $_[0];
459 13         34 my @i = &_vertex_ids_ensure;
460 13 100       34 @i = sort @i if &is_undirected;
461 13         69 my (undef, $id) = $g->[ _E ]->set_path_by_multi_id( \@i, _GEN_ID );
462 13         38 $g->[ _G ]++;
463 13 50       69 $g->[ _U ]->union(\@i) if &has_union_find;
464 13         75 return $id;
465             }
466              
467             sub has_edge_by_id {
468 326     326 1 2699 &expect_multiedged;
469 325         494 my $g = $_[0];
470 325         640 my @i = &_vertex_ids_multi;
471 325 100       1113 return 0 if @i < @_ - 2;
472 306         490 my $id = pop @i;
473 306 100       529 @i = sort @i if &is_undirected;
474 306         846 $g->[ _E ]->has_path_by_multi_id( \@i, $id );
475             }
476              
477             sub delete_edge_by_id {
478 6     6 1 983 &expect_multiedged;
479 5         17 &expect_non_unionfind;
480 5         9 my $g = $_[0];
481 5         12 my $E = $g->[ _E ];
482 5         15 my @i = &_vertex_ids_multi;
483 5 50       18 return if @i < @_ - 2;
484 5         20 my $id = pop @i;
485 5 50       12 @i = sort @i if &is_undirected;
486 5 100       33 return unless $E->has_path_by_multi_id( my @args = (\@i, $id) );
487 4         21 $E->del_path_by_multi_id( @args );
488 4         9 $g->[ _G ]++;
489 4         34 return $g;
490             }
491              
492             sub get_multiedge_ids {
493 198     198 1 5312 &expect_multiedged;
494 197 50       474 return unless @_-1 == (my @i = &_vertex_ids);
495 197         682 $_[0]->[ _E ]->get_multi_ids( \@i );
496             }
497              
498             ###
499             # Neighbourhood.
500             #
501              
502             sub _edges_at {
503 195 100   195   491 goto &_edges_from if &is_undirected;
504 115         4319 require Set::Object;
505 115 100       45043 Set::Object->new(&_edges_from, &_edges_to)->${ wantarray ? \'members' : \'size' };
  115         829  
506             }
507              
508             sub _edges_from {
509 1669     1669   5747 my ($g, @args) = @_;
510 1669         4009 my ($V, $E) = @$g[ _V, _E ];
511 1669 100 100     4422 return if (my @i = $V->get_ids_by_paths(\@args, &is_hyperedged && &is_directed)) != @args;
512 1665         6274 $E->paths_from(@i);
513             }
514              
515             sub _edges_to {
516 331 50   331   523 goto &_edges_from if &is_undirected;
517 331         658 my ($g, @args) = @_;
518 331         779 my ($V, $E) = @$g[ _V, _E ];
519 331 100 66     674 return if (my @i = $V->get_ids_by_paths(\@args, &is_hyperedged && &is_directed)) != @args;
520 328         847 $E->paths_to(@i);
521             }
522              
523             sub edges_at {
524 12 100   12 1 9546 goto &_edges_at if !wantarray;
525 11   100     37 $_[0]->[ _V ]->get_paths_by_ids([ &_edges_at ], &is_hyperedged && &is_directed);
526             }
527              
528             sub edges_from {
529 1474 50   1474 1 13823 goto &_edges_from if !wantarray;
530 1474   66     3224 $_[0]->[ _V ]->get_paths_by_ids([ &_edges_from ], &is_hyperedged && &is_directed);
531             }
532              
533             sub edges_to {
534 246 100   246 1 12384 goto &edges_from if &is_undirected;
535 216 50       365 goto &_edges_to if !wantarray;
536 216   66     320 $_[0]->[ _V ]->get_paths_by_ids([ &_edges_to ], &is_hyperedged && &is_directed);
537             }
538              
539             sub successors {
540 18486     18486 1 44642 my ($g, @args) = @_;
541 18486         35557 my ($V, $E) = @$g[ _V, _E ];
542 18486 100       49296 return if (my @i = $V->get_ids_by_paths(\@args)) != @args;
543 18480         44623 my @v = $E->successors(@i);
544 18480 100       64171 return @v if !wantarray;
545 14417         43146 map @$_, $V->get_paths_by_ids([ \@v ]);
546             }
547              
548             sub predecessors {
549 4808 100   4808 1 14335 goto &successors if &is_undirected;
550 1378         3004 my ($g, @args) = @_;
551 1378         2771 my ($V, $E) = @$g[ _V, _E ];
552 1378 100       3685 return if (my @i = $V->get_ids_by_paths(\@args)) != @args;
553 1374         3705 my @v = $E->predecessors(@i);
554 1374 100       8529 return @v if !wantarray;
555 199         531 map @$_, $V->get_paths_by_ids([ \@v ]);
556             }
557              
558             sub _cessors_by_radius {
559 166     166   469 my ($radius, $method, $self_only_if_loop) = splice @_, -3, 3;
560 166         393 my ($g, @v) = @_;
561 166         944 require Set::Object;
562 166         1301 my ($init, $next) = map Set::Object->new(@v), 1..2;
563 166 100       534 my $self = $self_only_if_loop ? Set::Object->new(grep $g->has_edge($_, $_), @v) : undef;
564 166         667 my ($got, $found) = map Set::Object->new, 1..2;
565 166   100     553 while (!defined $radius or $radius-- > 0) {
566 335         1455 $found->insert($g->$method($next->members));
567 335         1100 $next = $found->difference($got);
568 335 100       10039 last if $next->is_null; # Leave if no new found.
569 211         1013 $got->insert($next->members);
570 211         1115 $found->clear;
571             }
572 166 100       456 $got->remove($init->difference($self)->members) if $self_only_if_loop;
573 166 100       2237 $got->${ wantarray ? \'members' : \'size' };
  166         2322  
574             }
575              
576             sub all_successors {
577 37     37 1 7434 &expect_directed;
578 37         94 push @_, undef, 'successors', 0;
579 37         79 goto &_cessors_by_radius;
580             }
581              
582             sub successors_by_radius {
583 9     9 1 17 &expect_directed;
584 9         22 push @_, 'successors', 0;
585 9         13 goto &_cessors_by_radius;
586             }
587              
588             sub all_predecessors {
589 18     18 1 8283 &expect_directed;
590 18         47 push @_, undef, 'predecessors', 0;
591 18         53 goto &_cessors_by_radius;
592             }
593              
594             sub predecessors_by_radius {
595 22     22 1 10416 &expect_directed;
596 22         54 push @_, 'predecessors', 0;
597 22         60 goto &_cessors_by_radius;
598             }
599              
600             sub neighbours_by_radius {
601 26     26 1 13285 push @_, 'neighbours', 1;
602 26         103 goto &_cessors_by_radius;
603             }
604             *neighbors_by_radius = \&neighbours_by_radius;
605              
606             sub neighbours {
607 9616     9616 1 44151 require Set::Object;
608 9616         24912 my $s = Set::Object->new(&successors);
609 9616 100       23761 $s->insert(&predecessors) if &is_directed;
610 9616 100       19689 $s->${ wantarray ? \'members' : \'size' };
  9616         50054  
611             }
612             *neighbors = \&neighbours;
613              
614             sub all_neighbours {
615 54     54 1 15804 push @_, undef, 'neighbours', 1;
616 54         223 goto &_cessors_by_radius;
617             }
618             *all_neighbors = \&all_neighbours;
619              
620             sub all_reachable {
621 36 100   36 1 16551 &directed ? goto &all_successors : goto &all_neighbors;
622             }
623              
624             sub reachable_by_radius {
625 17 100   17 1 33 &directed ? goto &successors_by_radius : goto &neighbors_by_radius;
626             }
627              
628             sub delete_edge {
629 465     465 1 3822 &expect_non_unionfind;
630 464         729 my $g = $_[0];
631 464 100       868 return $g if (my @i = &_vertex_ids) != @_ - 1;
632 459 100       802 @i = sort @i if &is_undirected;
633 459 100 100     1904 return $g unless @i and $g->[ _E ]->del_path( \@i );
634 450         955 $g->[ _G ]++;
635 450         958 return $g;
636             }
637              
638             sub delete_vertex {
639 190     190 1 40186 &expect_non_unionfind;
640 190         373 my $g = $_[0];
641 190 100       539 return $g if @_ != 2;
642 189         302 my $V = $g->[ _V ];
643 189 100       654 return $g unless defined $V->has_path($_[1]);
644             # TODO: _edges_at is excruciatingly slow (rt.cpan.org 92427)
645 183         316 my $E = $g->[ _E ];
646 183         437 $E->del_path( $_ ) for &_edges_at;
647 183         787 $V->del_path($_[1]);
648 183         343 $g->[ _G ]++;
649 183         644 return $g;
650             }
651              
652             sub get_vertex_count {
653 60     60 1 19313 my $g = shift;
654 60         820 $g->[ _V ]->_get_path_count( @_ );
655             }
656              
657             sub get_edge_count {
658 1188     1188 1 19560 my $g = $_[0];
659 1188 100       2022 return 0 if (my @i = &_vertex_ids) != @_ - 1;
660 1172 100       1963 @i = sort @i if &is_undirected;
661 1172         3297 $g->[ _E ]->_get_path_count( \@i );
662             }
663              
664             sub delete_vertices {
665 8     8 1 74 &expect_non_unionfind;
666 8         14 my $g = shift;
667 8         28 while (@_) {
668 12         26 my $v = shift @_;
669 12         37 $g->delete_vertex($v);
670             }
671 8         23 return $g;
672             }
673              
674             sub delete_edges {
675 8     8 1 508 &expect_non_unionfind;
676 8         19 my $g = shift;
677 8         31 while (@_) {
678 10         40 my ($u, $v) = splice @_, 0, 2;
679 10         36 $g->delete_edge($u, $v);
680             }
681 8         21 return $g;
682             }
683              
684             ###
685             # Degrees.
686             #
687              
688             sub in_degree {
689 232     232 1 247 my $g = $_[0];
690 232 50 33     434 return undef unless @_ > 1 && &has_vertex;
691 232         271 my $in = 0;
692 232         288 $in += $g->get_edge_count( @$_ ) for &edges_to;
693 232 100 100     343 $in++ if &is_undirected and &is_self_loop_vertex;
694 232         583 return $in;
695             }
696              
697             sub out_degree {
698 208     208 1 236 my $g = $_[0];
699 208 50 33     386 return undef unless @_ > 1 && &has_vertex;
700 208         231 my $out = 0;
701 208         320 $out += $g->get_edge_count( @$_ ) for &edges_from;
702 208 100 100     356 $out++ if &is_undirected and &is_self_loop_vertex;
703 208         589 return $out;
704             }
705              
706             sub _total_degree {
707 42 50 33 42   95 return undef unless @_ > 1 && &has_vertex;
708 42 100       66 &is_undirected ? &in_degree : &in_degree - &out_degree;
709             }
710              
711             sub degree {
712 38 100   38 1 146 goto &_total_degree if @_ > 1;
713 2 100       4 return 0 if &is_directed;
714 1         2 my $g = $_[0];
715 1         2 my $total = 0;
716 1         2 $total += $g->_total_degree( $_ ) for &_vertices05;
717 1         4 return $total;
718             }
719              
720             *vertex_degree = \°ree;
721              
722             sub is_sink_vertex {
723 36 50   36 1 63 return 0 unless @_ > 1;
724 36 100       55 &successors == 0 && &predecessors > 0;
725             }
726              
727             sub is_source_vertex {
728 36 50   36 1 81 return 0 unless @_ > 1;
729 36 100       56 &predecessors == 0 && &successors > 0;
730             }
731              
732             sub is_successorless_vertex {
733 36 50   36 1 13783 return 0 unless @_ > 1;
734 36         79 &successors == 0;
735             }
736              
737             sub is_predecessorless_vertex {
738 36 50   36 1 13308 return 0 unless @_ > 1;
739 36         101 &predecessors == 0;
740             }
741              
742             sub is_successorful_vertex {
743 36 50   36 1 15289 return 0 unless @_ > 1;
744 36         101 &successors > 0;
745             }
746              
747             sub is_predecessorful_vertex {
748 36 50   36 1 10761 return 0 unless @_ > 1;
749 36         75 &predecessors > 0;
750             }
751              
752             sub is_isolated_vertex {
753 4437 50   4437 1 10101 return 0 unless @_ > 1;
754 4437 100       8026 &predecessors == 0 && &successors == 0;
755             }
756              
757             sub is_interior_vertex {
758 36 50   36 1 121 return 0 unless @_ > 1;
759 36         58 my $s = &successors;
760 36 100       60 $s-- if my $isl = &is_self_loop_vertex;
761 36 100       125 return 0 if $s == 0;
762 23 100       41 return $s > 0 if &is_undirected;
763 8         14 my $p = &predecessors;
764 8 100       16 $p-- if $isl;
765 8         25 $p > 0;
766             }
767              
768             sub is_exterior_vertex {
769 36 50   36 1 68 return 0 unless @_ > 1;
770 36 100       56 &predecessors == 0 || &successors == 0;
771             }
772              
773             sub is_self_loop_vertex {
774 108 50   108 1 180 return 0 unless @_ > 1;
775 108 100       156 return 1 if grep $_ eq $_[1], &successors; # @todo: multiedges
776 86         259 return 0;
777             }
778              
779             for my $p (qw(
780             is_sink_vertex
781             is_source_vertex
782             is_successorless_vertex
783             is_predecessorless_vertex
784             is_successorful_vertex
785             is_predecessorful_vertex
786             is_isolated_vertex
787             is_interior_vertex
788             is_exterior_vertex
789             is_self_loop_vertex
790             )) {
791 84     84   742033 no strict 'refs';
  84         178  
  84         76765  
792             (my $m = $p) =~ s/^is_(.*)ex$/${1}ices/;
793 681     681   15975 *$m = sub { my $g = $_[0]; grep $g->$p($_), &_vertices05 };
  681         1633  
794             }
795              
796             ###
797             # Paths and cycles.
798             #
799              
800             sub add_path {
801 140     140 1 662 my $g = shift;
802 140         287 my $u = shift;
803 140         224 my @edges;
804 140         436 while (@_) {
805 438         662 my $v = shift;
806 438         882 push @edges, [ $u, $v ];
807 438         960 $u = $v;
808             }
809 140         592 $g->add_edges(@edges);
810 140         558 return $g;
811             }
812              
813             sub delete_path {
814 4     4 1 16 &expect_non_unionfind;
815 4         6 my $g = shift;
816 4         6 my $u = shift;
817 4         10 while (@_) {
818 10         16 my $v = shift;
819 10         33 $g->delete_edge($u, $v);
820 10         27 $u = $v;
821             }
822 4         12 return $g;
823             }
824              
825             sub has_path {
826 20     20 1 890 my $g = shift;
827 20         37 my $u = shift;
828 20         54 while (@_) {
829 43         67 my $v = shift;
830 43 100       103 return 0 unless $g->has_edge($u, $v);
831 30         77 $u = $v;
832             }
833 7         39 return $g;
834             }
835              
836             sub add_cycle {
837 52     52 1 1360 push @_, $_[1];
838 52         193 goto &add_path;
839             }
840              
841             sub delete_cycle {
842 2     2 1 7 &expect_non_unionfind;
843 2         6 push @_, $_[1];
844 2         8 goto &delete_path;
845             }
846              
847             sub has_cycle {
848 9 100   9 1 1012 return 0 if @_ == 1;
849 8         21 push @_, $_[1];
850 8         53 goto &has_path;
851             }
852              
853             *has_this_cycle = \&has_cycle;
854              
855             sub has_a_cycle {
856 17     17 1 1392 my $g = shift;
857 17         1294 require Graph::Traversal::DFS;
858 17         155 my $t = Graph::Traversal::DFS->new($g, has_a_cycle => 1, @_);
859 17         88 $t->dfs;
860 17         88 return $t->get_state('has_a_cycle');
861             }
862              
863             sub find_a_cycle {
864 2     2 1 28 require Graph::Traversal::DFS;
865 2         9 my @r = ( back_edge => \&Graph::Traversal::find_a_cycle);
866 2 100       8 push @r,
867             down_edge => \&Graph::Traversal::find_a_cycle
868             if &is_undirected;
869 2         4 my $g = shift;
870 2         18 my $t = Graph::Traversal::DFS->new($g, @r, @_);
871 2         12 $t->dfs;
872 2 50       22 $t->has_state('a_cycle') ? @{ $t->get_state('a_cycle') } : ();
  2         13  
873             }
874              
875             ###
876             # Attributes.
877              
878             my @generic_methods = (
879             [ 'set_attribute', 'my (\$attr, \$value) = splice \@_, -2; &$add unless &$has;',
880             '\$_[0]->[ $offset ]->_set_path_attr( \@args, \$attr, \$value );' ],
881             [ 'set_attributes', 'my \$attr = pop; &$add unless &$has;',
882             '\$_[0]->[ $offset ]->_set_path_attrs( \@args, \$attr );', ],
883             [ 'has_attributes', 'return 0 unless &$has;',
884             '\$_[0]->[ $offset ]->_has_path_attrs( \@args );', ],
885             [ 'has_attribute', 'my \$attr = pop; return 0 unless &$has;',
886             '\$_[0]->[ $offset ]->_has_path_attr( \@args, \$attr );', ],
887             [ 'get_attributes', 'return undef unless &$has;',
888             'scalar \$_[0]->[ $offset ]->_get_path_attrs( \@args );', ],
889             [ 'get_attribute', 'my \$attr = pop; return undef unless &$has;',
890             'scalar \$_[0]->[ $offset ]->_get_path_attr( \@args, \$attr );', ],
891             [ 'get_attribute_names', 'return unless &$has;',
892             '\$_[0]->[ $offset ]->_get_path_attr_names( \@args );', ],
893             [ 'get_attribute_values', 'return unless &$has;',
894             '\$_[0]->[ $offset ]->_get_path_attr_values( \@args );', ],
895             [ 'delete_attributes', 'return undef unless &$has;',
896             '\$_[0]->[ $offset ]->_del_path_attrs( \@args );', ],
897             [ 'delete_attribute', 'my \$attr = pop; return undef unless &$has;',
898             '\$_[0]->[ $offset ]->_del_path_attr( \@args, \$attr );', ],
899             );
900             my %entity2offset = (vertex => '_V', edge => '_E');
901             my %entity2args = (edge => '&_vertex_ids');
902             my $template_mid = 'my \@args = @{[ $args || "\@_[1..\$#_]" ]};$munge';
903             for my $entity (qw(vertex edge)) {
904 84     84   1770 no strict 'refs';
  84         251  
  84         192416  
905             my $has_base = 'has_' . $entity;
906             my $add_base = 'add_' . $entity;
907             my $offset = $entity2offset{$entity};
908             for my $t (@generic_methods) {
909             my ($raw, $t1, $t2) = @$t;
910             my ($first, $rest) = ($raw =~ /^(\w+?)_(.+)/);
911             my $is_vertex = $entity eq 'vertex';
912             my $m = join '_', $first, $entity, $rest;
913             my ($args, $munge, $has, $add) = ($entity2args{$entity}, $is_vertex ? '' : "\n\@args = &is_undirected ? [sort \@args] : [\@args];", $has_base, $add_base);
914             my $func_text = "qq{sub $m {\n&expect_non_multi$entity;\n$t1\n$template_mid\n$t2\n}}\n"; #warn "$m:\n$func_text\n";
915             my $tv2 = eval $func_text; #warn "$m v2:\n$tv2\n";
916 3 50   3 1 13 eval $tv2; die if $@;
  3 50   2 1 10  
  3 50   3 1 12  
  3 50   2 1 14  
  3 50   11285 1 16  
  3 50   3 1 27  
  2 100   3 1 7  
  2 100   539 1 7  
  2 50   287 1 6  
  2 50   3 1 7  
  2 50   3 1 11  
  3 50   63 1 12  
  3 100   6 1 10  
  3 100   10 1 10  
  3 100   6 1 17  
  3 50   10 1 24  
  2 50   4625 1 11  
  2 100   5 1 8  
  2 100   59 1 14  
  2 50   1117 1 14  
  11285 50       158056  
  11285 50       23702  
  11285 100       24477  
  11224 50       27873  
  11224 100       60101  
  11224 100       44683  
  3 100       14  
  3 100       9  
  3 100       11  
  3 100       10  
  3         17  
  3         15  
  3         10  
  3         10  
  3         10  
  3         18  
  539         2427  
  539         1572  
  538         1631  
  538         1501  
  538         2171  
  287         50028  
  287         715  
  287         643  
  280         1045  
  280         1059  
  3         14  
  3         11  
  3         15  
  3         22  
  3         12  
  3         17  
  3         19  
  3         23  
  63         979  
  63         153  
  62         238  
  62         183  
  6         284  
  6         12  
  6         15  
  5         15  
  5         17  
  5         27  
  10         1390  
  10         24  
  10         47  
  10         21  
  10         37  
  6         360  
  6         14  
  6         15  
  5         20  
  5         24  
  10         1510  
  10         26  
  10         38  
  10         39  
  4625         16114  
  4624         45531  
  4624         12198  
  4624         18608  
  4624         12875  
  4624         18603  
  5         74  
  5         12  
  5         19  
  5         20  
  5         20  
  5         34  
  59         1033  
  58         218  
  58         154  
  58         280  
  58         371  
  1117         6559  
  1117         2419  
  1117         2581  
  1117         3950  
  1117         3919  
917             $m .= '_by_id';
918             ($args, $munge, $has, $add) = ($entity2args{$entity} && "$entity2args{$entity}_multi", $is_vertex ? '' : "\n\@args = (&is_undirected ? [sort \@args[0..\$#args-1]] : [\@args[0..\$#args-1]], \$args[-1]);", $has_base.'_by_id', $add_base.'_by_id');
919             $func_text = "qq{sub $m {\n&expect_multi$entity;\n$t1\n$template_mid\n$t2\n}}\n"; #warn "$m:\n$func_text\n";
920             $tv2 = eval $func_text; #warn "$m v2:\n$tv2\n";
921 3 50   3 1 10 eval $tv2; die if $@;
  3 50   2 1 5  
  3 50   3 1 6  
  3 50   2 1 8  
  3 50   129 1 7  
  3 50   3 1 27  
  2 50   3 1 7  
  2 100   85 1 5  
  2 50   17 1 4  
  2 50   3 1 4  
  2 50   3 1 7  
  3 50   78 1 16  
  3 50   6 1 10  
  3 100   10 1 9  
  3 50   6 1 17  
  3 50   10 1 18  
  2 50   43 1 25  
  2 50   19 1 7  
  2 100   12 1 11  
  2 50   15 1 11  
  129 50       4180  
  129 50       259  
  129 100       275  
  129 50       363  
  129 100       311  
  129 100       434  
  3 100       46  
  3 100       6  
  3 100       7  
  3 100       4  
  3         9  
  3         42  
  3         5  
  3         7  
  3         6  
  3         7  
  85         812  
  85         220  
  85         289  
  85         231  
  85         314  
  17         87  
  17         54  
  17         72  
  17         111  
  17         78  
  3         98  
  3         10  
  3         15  
  3         16  
  3         104  
  3         11  
  3         18  
  3         12  
  78         259  
  78         200  
  78         280  
  78         275  
  6         269  
  6         9  
  6         11  
  5         11  
  5         10  
  5         14  
  10         1464  
  10         16  
  10         21  
  10         16  
  10         25  
  6         27  
  6         18  
  6         20  
  5         29  
  5         21  
  10         108  
  10         34  
  10         47  
  10         61  
  43         2206  
  43         124  
  43         84  
  43         127  
  43         91  
  43         146  
  19         151  
  19         40  
  19         44  
  19         61  
  19         58  
  19         72  
  12         2494  
  12         53  
  12         45  
  12         59  
  12         55  
  15         53  
  15         34  
  15         39  
  15         63  
  15         59  
922             }
923             }
924              
925             sub get_edge_attribute_all {
926 5740     5740 1 15876 my ($g, $u, $v, $name) = @_;
927 5740 50       11153 die "no attribute name given" if !defined $name;
928 5740 100       11302 grep defined(),
929             &is_multiedged ? (map $g->get_edge_attribute_by_id($u, $v, $_, $name),
930             $g->get_multiedge_ids($u, $v))
931             : $g->get_edge_attribute($u, $v, $name);
932             }
933              
934             sub add_vertices {
935 3524     3524 1 14790 my ($g, @v) = @_;
936 3524 100       7075 if (&is_multivertexed) {
937 1         6 $g->add_vertex_by_id($_, _GEN_ID) for @v;
938 1         4 return $g;
939             }
940 3523         11642 my @i = $g->[ _V ]->set_paths(@v);
941 3523         10933 $g->[ _G ]++;
942 3523 100       7008 return $g if !&has_union_find;
943 5         23 $g->[ _U ]->add(@i);
944 5         12 $g;
945             }
946              
947             sub add_edges {
948 40817     40817 1 250880 my ($g, @args) = @_;
949 40817         60036 my @edges;
950 40817         107577 while (defined(my $u = shift @args)) {
951 58271 100       211470 push @edges, ref $u eq 'ARRAY' ? $u : @args ? [ $u, shift @args ]
    100          
952             : __carp_confess "Graph::add_edges: missing end vertex";
953             }
954 40816 100       80239 if (&is_multiedged) {
955 25         145 $g->add_edge_by_id(@$_, _GEN_ID) for @edges;
956 25         176 return $g;
957             }
958 40791         72058 my $uf = &has_union_find;
959 40791   100     65660 my $deep = &is_hyperedged && &is_directed;
960 40791 100       152218 my @paths = $g->[ _V ]->get_ids_by_paths(\@edges, 1, 1 + ($deep ? 1 : 0));
961 40791 100       78757 @paths = map [ sort @$_ ], @paths if &is_undirected;
962 40791         133125 $g->[ _E ]->set_paths( @paths );
963 40791 100       84867 $uf->union(@paths) if $uf;
964 40791         72946 $g->[ _G ]++;
965 40791         245822 return $g;
966             }
967              
968             sub add_edges_by_id {
969 4     4 1 10 &expect_multiedged;
970 4         9 my ($g, $id) = (shift, pop);
971 4         8 my @edges;
972 4         13 while (defined(my $u = shift @_)) {
973 8 0       29 push @edges, ref $u eq 'ARRAY' ? $u : @_ ? [ $u, shift @_ ]
    50          
974             : __carp_confess "Graph::add_edges: missing end vertex";
975             }
976 4         19 $g->add_edge_by_id(@$_, $id) for @edges;
977 4         9 return $g;
978             }
979              
980             sub rename_vertex {
981 24     24 1 117 my $g = shift;
982 24         105 $g->[ _V ]->rename_path(@_);
983 24         65 return $g;
984             }
985              
986             sub rename_vertices {
987 3     3 1 1976 my ($g, $code) = @_;
988 3         7 my %seen;
989             $g->rename_vertex($_, $code->($_))
990 3         21 for grep !$seen{$_}++, $g->[ _V ]->paths;
991 3         21 return $g;
992             }
993              
994             sub filter_vertices {
995 3     3 1 1613 my ($g, $code) = @_;
996 3         31 my @v = &_vertices05;
997 3 100       16 if (&is_multivertexed) {
998 1         6 for my $v (@v) {
999 7         55 $g->delete_vertex_by_id($v, $_) for
1000             grep !$code->($g, $v, $_), $g->get_multivertex_ids($v);
1001             }
1002             } else {
1003 2         12 $g->delete_vertices(grep !$code->($g, $_), @v);
1004             }
1005 3         16 $g;
1006             }
1007              
1008             sub filter_edges {
1009 3     3 1 1645 my ($g, $code) = @_;
1010 3         16 my @e = &_edges05;
1011 3 100       15 if (&is_multiedged) {
1012 1         5 for my $e (@e) {
1013 6         48 $g->delete_edge_by_id(@$e, $_) for
1014             grep !$code->($g, @$e, $_), $g->get_multiedge_ids(@$e);
1015             }
1016             } else {
1017 2         13 $g->delete_edges(map @$_, grep !$code->($g, @$_), @e);
1018             }
1019 3         19 $g;
1020             }
1021              
1022             sub as_hashes {
1023 22     22 1 16661 my ($g) = @_;
1024 22         47 my (%v, %e, @e);
1025 22         60 my ($is_hyper, $is_directed) = (&is_hyperedged, &is_directed);
1026 22 100       62 if (&is_multivertexed) {
1027 13         49 for my $v ($g->unique_vertices) {
1028 59   50     167 $v{$v} = {
1029             map +($_ => $g->get_vertex_attributes_by_id($v, $_) || {}),
1030             $g->get_multivertex_ids($v)
1031             };
1032             }
1033             } else {
1034 9   100     47 %v = map +($_ => $g->get_vertex_attributes($_) || {}), $g->unique_vertices;
1035             }
1036 22         76 my $multi_e = &is_multiedged;
1037 22         122 for my $e ($g->edges) {
1038             my $edge_attr = {
1039             $multi_e
1040             ? map +($_ => $g->get_edge_attributes_by_id(@$e, $_) || {}),
1041             $g->get_multiedge_ids(@$e)
1042 105 100 100     330 : %{ $g->get_edge_attributes(@$e)||{} }
  40 100       1043  
1043             };
1044 105 100       330 if ($is_hyper) {
1045 12         36 my %h = (attributes => $edge_attr);
1046 12 100       27 if ($is_directed) {
1047 8         24 @h{qw(predecessors successors)} = @$e;
1048             } else {
1049 4         10 $h{vertices} = $e;
1050             }
1051 12         33 push @e, \%h;
1052             } else {
1053 93         298 $e{ $e->[0] }{ $e->[1] } = $edge_attr;
1054 93 100       391 $e{ $e->[1] }{ $e->[0] } = $edge_attr if !$is_directed;
1055             }
1056             }
1057 22 100       349 ( \%v, $is_hyper ? \@e : \%e );
1058             }
1059              
1060             sub ingest {
1061 3     3 1 1405 my ($g, $g2) = @_;
1062 3         47 _copy_vertices($g2, $g, 1);
1063 3         25 _copy_edges($g2, $g, 1);
1064 3         11 $g;
1065             }
1066              
1067             ###
1068             # More constructors.
1069             #
1070              
1071             sub copy {
1072 52     52 1 3232 my ($g, @args) = @_;
1073 52         188 my $c = $g->new(@args);
1074 52         354 _copy_vertices($g, $c);
1075 52         224 _copy_edges($g, $c);
1076 52         257 return $c;
1077             }
1078              
1079             *copy_graph = \©
1080              
1081             sub _deep_copy_best {
1082 24 50   24   1536 _can_deep_copy_Storable()
1083             ? _deep_copy_Storable(@_) : _deep_copy_DataDumper(@_);
1084             }
1085              
1086             sub _deep_copy_Storable {
1087 25     25   85 my $g = shift;
1088 25         2721 require Safe; # For deep_copy().
1089 25         41509 my $safe = Safe->new;
1090 25         30112 $safe->permit(qw/:load/);
1091 25         244 local $Storable::Deparse = 1;
1092 25     3   133 local $Storable::Eval = sub { $safe->reval($_[0]) };
  3         5947  
1093 25         151 return Storable::thaw(Storable::freeze($g));
1094             }
1095              
1096             sub _deep_copy_DataDumper {
1097 1     1   5 my $g = shift;
1098 1         538 require Data::Dumper;
1099 1         6297 my $d = Data::Dumper->new([$g]);
1100 84     84   1030 use vars qw($VAR1);
  84         174  
  84         333287  
1101 1         30 $d->Purity(1)->Terse(1)->Deepcopy(1);
1102 1 50       26 $d->Deparse(1) if $] >= 5.008;
1103 1     1   8 eval $d->Dump;
  1     1   1444  
  1         2  
  1         64  
  1         5  
  1         1  
  1         34  
1104             }
1105              
1106             sub deep_copy {
1107 17     17 1 1073 local $. = $.;
1108 17         61 my $g2 = _deep_copy_best(@_);
1109 17 100       18798 $g2->[ _V ]->reindex if grep ref, &_vertices05;
1110 17         231 $g2;
1111             }
1112              
1113             *deep_copy_graph = \&deep_copy;
1114              
1115             sub transpose_edge {
1116 493     493 1 795 my $g = $_[0];
1117 493 50       857 return $g if !&is_directed;
1118 493 50       995 return undef unless &has_edge;
1119 493         1217 my $c = &get_edge_count;
1120 493         19145 my $a = &get_edge_attributes;
1121 493         1737 my @e = reverse @_[1..$#_];
1122 493 100       1318 &delete_edge unless $g->has_edge( @e );
1123 493         2432 $g->add_edges(map \@e, 1..$c);
1124 493 50       1131 $g->set_edge_attributes(@e, $a) if $a;
1125 493         2165 return $g;
1126             }
1127              
1128             sub transpose_graph {
1129 20     20 1 78 my $t = ©
1130 20 100       51 return $t if !&directed;
1131 17         79 $t->transpose_edge(@$_) for &_edges05;
1132 17         598 return $t;
1133             }
1134              
1135             *transpose = \&transpose_graph;
1136              
1137             sub complete_graph {
1138 9     9 1 874 my $directed = &is_directed;
1139 9         25 my $c = &new;
1140 9         26 my @v = &_vertices05;
1141 9         18 my @edges;
1142 9         31 for (my $i = $#v; $i >= 0; $i-- ) {
1143 20 100       130 push @edges, map +([$v[$i], $v[$_]], $directed ? [$v[$_], $v[$i]] : ()),
1144             0..$i - 1;
1145             }
1146 9         32 $c->add_edges(@edges);
1147 9         45 return $c;
1148             }
1149              
1150             sub max_cliques {
1151 3     3 1 31 my ($g) = @_;
1152 3         13 &expect_undirected;
1153 3         13 $g->bron_kerbosch_pivot([], [$g->vertices], [], \ my @cliques);
1154 3 100       57 return wantarray ? @cliques : \@cliques
1155             }
1156              
1157             sub bron_kerbosch_pivot {
1158 67     67 1 169 my ($g, $r, $p, $x, $max_cliques) = @_;
1159 67 100 100     376 if (! @$p && ! @$x && @$r) {
      66        
1160 22         84 push @$max_cliques, [@$r];
1161 22         70 return;
1162             }
1163 45         102 my $pivot = (@$p, @$x)[0];
1164 45         248 for my $v (my @p = @$p) {
1165 77 100       238 next if $g->has_edge($pivot, $v);
1166             $g->bron_kerbosch_pivot(
1167             [@$r, $v],
1168 216         360 [grep { my $w = $_; grep $_ eq $w, @$p } $g->neighbours($v)],
  216         659  
1169 64         273 [grep { my $w = $_; grep $_ eq $w, @$x } $g->neighbours($v)],
  216         332  
  216         654  
1170             $max_cliques);
1171 64         417 @$p = grep $_ ne $v, @$p;
1172 64         211 push @$x, $v;
1173             }
1174             }
1175              
1176             *complement = \&complement_graph;
1177              
1178             sub complement_graph {
1179 5     5 1 1125 my $c = &complete_graph;
1180 5         16 $c->delete_edge(@$_) for &edges;
1181 5         26 return $c;
1182             }
1183              
1184             *complete = \&complete_graph;
1185              
1186             sub subgraph {
1187 1222     1222 1 3207 my ($g, $src, $dst) = @_;
1188 1222 50 66     6787 __carp_confess "Graph::subgraph: need src and dst array references"
      66        
1189             unless ref $src eq 'ARRAY' && (!defined($dst) or ref $dst eq 'ARRAY');
1190 1222         6391 require Set::Object;
1191 1222         10607 my $s = $g->new;
1192 1222         6146 my @u = grep $g->has_vertex($_), @$src;
1193 1222 100       13711 my $v = Set::Object->new($dst ? grep $g->has_vertex($_), @$dst : @u);
1194 1222 100       4966 $s->add_vertices(@u, $dst ? $v->members : ());
1195 1222         2891 my $directed = &is_directed;
1196 1222 100       2985 if ($directed) {
1197 12         25 $s->add_edges(grep $v->contains($_->[1]), $g->edges_from(@u));
1198             } else {
1199 1210 100       2601 my $valid = $dst ? $v + Set::Object->new(@u) : $v;
1200 1210   100     4118 $s->add_edges(
1201             grep +($v->contains($_->[0]) || $v->contains($_->[1])) &&
1202             ($valid->contains($_->[0]) && $valid->contains($_->[1])),
1203             $g->edges_from(@u)
1204             );
1205             }
1206 1222         34191 return $s;
1207             }
1208              
1209             ###
1210             # Transitivity.
1211             #
1212              
1213             sub is_transitive {
1214 4     4 1 30 my $g = shift;
1215 4         910 require Graph::TransitiveClosure;
1216 4         19 Graph::TransitiveClosure::is_transitive($g);
1217             }
1218              
1219             ###
1220             # Weighted vertices.
1221             #
1222              
1223             my $defattr = 'weight';
1224              
1225             sub _defattr {
1226 149     149   396 return $defattr;
1227             }
1228              
1229             sub add_weighted_vertex {
1230 1     1 1 4 &expect_non_multivertexed;
1231 1         5 push @_, $defattr, pop;
1232 1         43 goto &set_vertex_attribute;
1233             }
1234              
1235             sub add_weighted_vertices {
1236 1     1 1 6 &expect_non_multivertexed;
1237 1         3 my $g = shift;
1238 1         5 while (@_) {
1239 2         8 my ($v, $w) = splice @_, 0, 2;
1240 2         64 $g->set_vertex_attribute($v, $defattr, $w);
1241             }
1242             }
1243              
1244             sub get_vertex_weight {
1245 5     5 1 20 &expect_non_multivertexed;
1246 5         19 push @_, $defattr;
1247 5         206 goto &get_vertex_attribute;
1248             }
1249              
1250             sub has_vertex_weight {
1251 3     3 1 8 &expect_non_multivertexed;
1252 3         6 push @_, $defattr;
1253 3         70 goto &has_vertex_attribute;
1254             }
1255              
1256             sub set_vertex_weight {
1257 1     1 1 7 &expect_non_multivertexed;
1258 1         6 push @_, $defattr, pop;
1259 1         38 goto &set_vertex_attribute;
1260             }
1261              
1262             sub delete_vertex_weight {
1263 1     1 1 5 &expect_non_multivertexed;
1264 1         2 push @_, $defattr;
1265 1         26 goto &delete_vertex_attribute;
1266             }
1267              
1268             sub add_weighted_vertex_by_id {
1269 1     1 1 5 &expect_multivertexed;
1270 1         4 push @_, $defattr, pop;
1271 1         40 goto &set_vertex_attribute_by_id;
1272             }
1273              
1274             sub add_weighted_vertices_by_id {
1275 1     1 1 5 &expect_multivertexed;
1276 1         2 my $g = shift;
1277 1         3 my $id = pop;
1278 1         4 while (@_) {
1279 2         8 my ($v, $w) = splice @_, 0, 2;
1280 2         10 $g->add_vertex_by_id($v, $id);
1281 2         72 $g->set_vertex_attribute_by_id($v, $id, $defattr, $w);
1282             }
1283             }
1284              
1285             sub get_vertex_weight_by_id {
1286 5     5 1 18 &expect_multivertexed;
1287 5         18 push @_, $defattr;
1288 5         217 goto &get_vertex_attribute_by_id;
1289             }
1290              
1291             sub has_vertex_weight_by_id {
1292 3     3 1 12 &expect_multivertexed;
1293 3         11 push @_, $defattr;
1294 3         116 goto &has_vertex_attribute_by_id;
1295             }
1296              
1297             sub set_vertex_weight_by_id {
1298 1     1 1 2713 &expect_multivertexed;
1299 1         4 push @_, $defattr, pop;
1300 1         42 goto &set_vertex_attribute_by_id;
1301             }
1302              
1303             sub delete_vertex_weight_by_id {
1304 1     1 1 5 &expect_multivertexed;
1305 1         4 push @_, $defattr;
1306 1         39 goto &delete_vertex_attribute_by_id;
1307             }
1308              
1309             ###
1310             # Weighted edges.
1311             #
1312              
1313             sub add_weighted_edge {
1314 2590     2590 1 19145 &expect_non_multiedged;
1315 2590         6385 push @_, $defattr, pop;
1316 2590         68118 goto &set_edge_attribute;
1317             }
1318              
1319             sub add_weighted_edges {
1320 3     3 1 30 &expect_non_multiedged;
1321 3         6 my $g = shift;
1322 3         11 while (@_) {
1323 14         43 my ($u, $v, $w) = splice @_, 0, 3;
1324 14         292 $g->set_edge_attribute($u, $v, $defattr, $w);
1325             }
1326             }
1327              
1328             sub add_weighted_edges_by_id {
1329 1     1 1 4 &expect_multiedged;
1330 1         2 my $g = shift;
1331 1         1 my $id = pop;
1332 1         60 while (@_) {
1333 2         6 my ($u, $v, $w) = splice @_, 0, 3;
1334 2         37 $g->set_edge_attribute_by_id($u, $v, $id, $defattr, $w);
1335             }
1336             }
1337              
1338             sub add_weighted_path {
1339 6     6 1 321 &expect_non_multiedged;
1340 6         11 my $g = shift;
1341 6         13 my $u = shift;
1342 6         34 while (@_) {
1343 22         58 my ($w, $v) = splice @_, 0, 2;
1344 22         494 $g->set_edge_attribute($u, $v, $defattr, $w);
1345 22         70 $u = $v;
1346             }
1347             }
1348              
1349             sub get_edge_weight {
1350 7     7 1 20 &expect_non_multiedged;
1351 7         15 push @_, $defattr;
1352 7         160 goto &get_edge_attribute;
1353             }
1354              
1355             sub has_edge_weight {
1356 3     3 1 9 &expect_non_multiedged;
1357 3         9 push @_, $defattr;
1358 3         80 goto &has_edge_attribute;
1359             }
1360              
1361             sub set_edge_weight {
1362 3     3 1 655 &expect_non_multiedged;
1363 3         11 push @_, $defattr, pop;
1364 3         104 goto &set_edge_attribute;
1365             }
1366              
1367             sub delete_edge_weight {
1368 1     1 1 4 &expect_non_multiedged;
1369 1         4 push @_, $defattr;
1370 1         23 goto &delete_edge_attribute;
1371             }
1372              
1373             sub add_weighted_edge_by_id {
1374 17     17 1 96 &expect_multiedged;
1375 17         35 push @_, $defattr, pop;
1376 17         324 goto &set_edge_attribute_by_id;
1377             }
1378              
1379             sub add_path_by_id {
1380 4     4 1 34 &expect_multiedged;
1381 4         10 my ($g, $u, $id) = (shift, shift, pop);
1382 4         8 my @edges;
1383 4         11 while (@_) {
1384 8         13 my $v = shift;
1385 8         18 push @edges, [ $u, $v ];
1386 8         21 $u = $v;
1387             }
1388 4         30 $g->add_edges_by_id(@edges, $id);
1389 4         13 return $g;
1390             }
1391              
1392             sub add_weighted_path_by_id {
1393 3     3 1 21 &expect_multiedged;
1394 3         9 my ($g, $u, $id) = (shift, shift, pop);
1395 3         9 while (@_) {
1396 6         35 my ($w, $v) = splice @_, 0, 2;
1397 6         172 $g->set_edge_attribute_by_id($u, $v, $id, $defattr, $w);
1398 6         23 $u = $v;
1399             }
1400             }
1401              
1402             sub get_edge_weight_by_id {
1403 8     8 1 25 &expect_multiedged;
1404 8         12 push @_, $defattr;
1405 8         137 goto &get_edge_attribute_by_id;
1406             }
1407              
1408             sub has_edge_weight_by_id {
1409 3     3 1 7 &expect_multiedged;
1410 3         5 push @_, $defattr;
1411 3         58 goto &has_edge_attribute_by_id;
1412             }
1413              
1414             sub set_edge_weight_by_id {
1415 3     3 1 4381 &expect_multiedged;
1416 3         10 push @_, $defattr, pop;
1417 3         115 goto &set_edge_attribute_by_id;
1418             }
1419              
1420             sub delete_edge_weight_by_id {
1421 1     1 1 3 &expect_multiedged;
1422 1         14 push @_, $defattr;
1423 1         20 goto &delete_edge_attribute_by_id;
1424             }
1425              
1426             ###
1427             # Error helpers.
1428             #
1429              
1430             my %expected;
1431             @expected{qw(directed undirected acyclic)} = qw(undirected directed cyclic);
1432              
1433             sub _expected {
1434 43     43   111 my $exp = shift;
1435 43 100       185 my $got = @_ ? shift : $expected{$exp};
1436 43 100       157 $got = defined $got ? ", got $got" : "";
1437 43 50       564 if (my @caller2 = caller(2)) {
1438 43         480 die "$caller2[3]: expected $exp graph$got, at $caller2[1] line $caller2[2].\n";
1439             } else {
1440 0         0 my @caller1 = caller(1); # uncoverable statement
1441 0         0 die "$caller1[3]: expected $exp graph$got, at $caller1[1] line $caller1[2].\n"; # uncoverable statement
1442             }
1443             }
1444              
1445             sub expect_no_args {
1446 10     10 1 20 my $g = shift;
1447 10 50       41 return unless @_;
1448 0         0 my @caller1 = caller(1); # uncoverable statement
1449 0         0 die "$caller1[3]: expected no arguments, got " . scalar @_ . ", at $caller1[1] line $caller1[2]\n"; # uncoverable statement
1450             }
1451              
1452             sub expect_undirected {
1453 1212 100   1212 1 4658 _expected('undirected') unless &is_undirected;
1454             }
1455              
1456             sub expect_directed {
1457 219 100   219 1 3290 _expected('directed') unless &is_directed;
1458             }
1459              
1460             sub expect_acyclic {
1461 3 100   3 1 4418 _expected('acyclic') unless &is_acyclic;
1462             }
1463              
1464             sub expect_dag {
1465 7     7 1 3908 my @got;
1466 7 100       47 push @got, 'undirected' unless &is_directed;
1467 7 100       26 push @got, 'cyclic' unless &is_acyclic;
1468 7 100       153 _expected('directed acyclic', "@got") if @got;
1469             }
1470              
1471             sub expect_hyperedged {
1472 11 100   11 1 32 _expected('hyperedged') unless &is_hyperedged;
1473             }
1474              
1475             sub expect_multivertexed {
1476 502 100   502 1 1018 _expected('multivertexed') unless &is_multivertexed;
1477             }
1478             *expect_multivertex = \&expect_multivertexed;
1479              
1480             sub expect_non_multivertexed {
1481 1565 100   1565 1 3887 _expected('non-multivertexed') if &is_multivertexed;
1482             }
1483             *expect_non_multivertex = \&expect_non_multivertexed;
1484              
1485             sub expect_non_multiedged {
1486 19094 100   19094 1 42064 _expected('non-multiedged') if &is_multiedged;
1487             }
1488             *expect_non_multiedge = \&expect_non_multiedged;
1489              
1490             sub expect_multiedged {
1491 1005 100   1005 1 1910 _expected('multiedged') unless &is_multiedged;
1492             }
1493             *expect_multiedge = \&expect_multiedged;
1494              
1495             sub expect_non_unionfind {
1496 689 100   689 1 1371 _expected('non-unionfind') if &has_union_find;
1497             }
1498              
1499             sub _get_options {
1500 2472     2472   18889 my @caller = caller(1);
1501 2472 100 100     16330 unless (@_ == 1 && ref $_[0] eq 'ARRAY') {
1502 3         25 die "$caller[3]: internal error: should be called with only one array ref argument, at $caller[1] line $caller[2].\n";
1503             }
1504 2469         4264 my @opt = @{ $_[0] };
  2469         7102  
1505 2469 50       9037 unless (@opt % 2 == 0) {
1506 0         0 die "$caller[3]: expected an options hash, got a non-even number of arguments, at $caller[1] line $caller[2].\n"; # uncoverable statement
1507             }
1508 2469         12375 return @opt;
1509             }
1510              
1511             ###
1512             # Random constructors and accessors.
1513             #
1514              
1515             sub __fisher_yates_shuffle (@) {
1516             # From perlfaq4, but modified to be non-modifying.
1517 1     1   1398 my @a = @_;
1518 1         4 my $i = @a;
1519 1         6 while ($i--) {
1520 3         12 my $j = int rand ($i+1);
1521 3         11 @a[$i,$j] = @a[$j,$i];
1522             }
1523 1         20 return @a;
1524             }
1525              
1526             BEGIN {
1527             sub _shuffle(@);
1528             # Workaround for the Perl bug [perl #32383] where -d:Dprof and
1529             # List::Util::shuffle do not like each other: if any debugging
1530             # (-d) flags are on, fall back to our own Fisher-Yates shuffle.
1531             # The bug was fixed by perl changes #26054 and #26062, which
1532             # went to Perl 5.9.3. If someone tests this with a pre-5.9.3
1533             # bleadperl that calls itself 5.9.3 but doesn't yet have the
1534             # patches, oh, well.
1535             *_shuffle = $^P && $] < 5.009003 ?
1536 84 50 33 84   1433 \&__fisher_yates_shuffle : do { require List::Util; \&List::Util::shuffle };
  84         654  
  84         334884  
1537             }
1538              
1539             sub random_graph {
1540 14 100   14 1 15116 my $class = (@_ % 2) == 0 ? 'Graph' : shift;
1541 14         66 my %opt = _get_options( \@_ );
1542             __carp_confess "Graph::random_graph: argument 'vertices' missing or undef"
1543 14 100       66 unless defined $opt{vertices};
1544             __carp_confess "Graph::random_graph: both arguments 'edges' and 'edges_fill' specified"
1545 12 50 66     50 if exists $opt{edges} && exists $opt{edges_fill};
1546 12 100       56 srand delete $opt{random_seed} if exists $opt{random_seed};
1547 12         29 my $random_edge = delete $opt{random_edge};
1548 12         21 my @V;
1549 12 100       44 if (my $ref = ref $opt{vertices}) {
1550 1 50       4 __carp_confess "Graph::random_graph: argument 'vertices' illegal"
1551             if $ref ne 'ARRAY';
1552 1         2 @V = @{ $opt{vertices} };
  1         4  
1553             } else {
1554 11         60 @V = 0..($opt{vertices} - 1);
1555             }
1556 12         28 delete $opt{vertices};
1557 12         23 my $V = @V;
1558 12         61 my $C = $V * ($V - 1) / 2;
1559 12 100       40 my $E = exists $opt{edges_fill} ? $opt{edges_fill} * $C : $opt{edges};
1560 12         50 delete @opt{qw(edges edges_fill)};
1561 12         59 my $g = $class->new(%opt);
1562 12         68 $g->add_vertices(@V);
1563 12 50       45 return $g if $V < 2;
1564 12 100       45 $C *= 2 if $g->directed;
1565 12 100       45 $E = $C / 2 unless defined $E;
1566 12         39 $E = int($E + 0.5);
1567 12         25 my $p = $E / $C;
1568 12 100   13629   82 $random_edge = sub { $p } unless defined $random_edge;
  13629         21104  
1569 12 50 0     100 __carp_confess "Graph::random_graph: needs to be countedged or multiedged ($E > $C)"
      33        
1570             if $p > 1.0 && !($g->countedged || $g->multiedged);
1571             # Shuffle the vertex lists so that the pairs at
1572             # the beginning of the lists are not more likely.
1573 12         208 my @V1 = _shuffle @V;
1574 12         58 my @V2 = _shuffle @V;
1575             LOOP:
1576 12         35 while ($E) {
1577 23         47 for my $v1 (@V1) {
1578 333         700 for my $v2 (@V2) {
1579 14460 100       31407 next if $v1 eq $v2; # TODO: allow self-loops?
1580 14133         24851 my $q = $random_edge->($g, $v1, $v2, $p);
1581 14133 100 66     75500 if ($q && ($q == 1 || rand() <= $q) &&
      100        
      100        
1582             !$g->has_edge($v1, $v2)) {
1583 6027         15871 $g->add_edge($v1, $v2);
1584 6027         12114 $E--;
1585 6027 100       14653 last LOOP unless $E;
1586             }
1587             }
1588             }
1589             }
1590 12         3639 $g;
1591             }
1592              
1593             sub random_vertex {
1594 125     125 1 47390 my @V = &_vertices05;
1595 125         578 @V[rand @V];
1596             }
1597              
1598             sub random_edge {
1599 31     31 1 17455 my @E = &_edges05;
1600 31         178 @E[rand @E];
1601             }
1602              
1603             sub random_successor {
1604 46     46 1 140 my @S = &successors;
1605 46         155 @S[rand @S];
1606             }
1607              
1608             sub random_predecessor {
1609 50     50 1 142 my @P = &predecessors;
1610 50         189 @P[rand @P];
1611             }
1612              
1613             ###
1614             # Algorithms.
1615             #
1616              
1617             my $MST_comparator = sub { ($_[0] || 0) <=> ($_[1] || 0) };
1618              
1619             sub _MST_attr {
1620 23     23   42 my $attr = shift;
1621             my $attribute =
1622             exists $attr->{attribute} ?
1623 23 50       80 $attr->{attribute} : $defattr;
1624             my $comparator =
1625             exists $attr->{comparator} ?
1626 23 50       68 $attr->{comparator} : $MST_comparator;
1627 23         98 return ($attribute, $comparator);
1628             }
1629              
1630             sub _MST_edges {
1631 23     23   55 my ($g, $attr) = @_;
1632 23         73 my ($attribute, $comparator) = _MST_attr($attr);
1633             map $_->[1],
1634 23         74 sort { $comparator->($a->[0], $b->[0], $a->[1], $b->[1]) }
  1678         3154  
1635             map [ $g->get_edge_attribute(@$_, $attribute), $_ ],
1636             &_edges05;
1637             }
1638              
1639             sub MST_Kruskal {
1640 24     24 1 747 &expect_undirected;
1641 23         60 my ($g, %attr) = @_;
1642 23         1260 require Graph::UnionFind;
1643              
1644 23         135 my $MST = Graph->new(directed => 0);
1645              
1646 23         140 my $UF = Graph::UnionFind->new;
1647 23         65 $UF->add(&_vertices05);
1648              
1649 23         115 my @edges;
1650 23         127 for my $e ($g->_MST_edges(\%attr)) {
1651 1612         3806 my ($u, $v) = @$e; # TODO: hyperedges
1652 1612 100       4018 next if $UF->same( @$e );
1653 454         1709 $UF->union([$u, $v]);
1654 454         1591 push @edges, [ $u, $v ];
1655             }
1656 23         1167 $MST->add_edges(@edges);
1657              
1658 23         631 return $MST;
1659             }
1660              
1661             sub _MST_add {
1662 926     926   2941 my ($g, $h, $HF, $r, $attr, $unseen) = @_;
1663             $HF->add( Graph::MSTHeapElem->new( $r, $_, $g->get_edge_attribute( $r, $_, $attr ) ) )
1664 926         11856 for grep exists $unseen->{ $_ }, $g->successors( $r );
1665             }
1666              
1667 242     242   461 sub _next_alphabetic { shift; (sort keys %{ $_[0] })[0] }
  242         453  
  242         1347  
1668 5     5   11 sub _next_numeric { shift; (sort { $a <=> $b } keys %{ $_[0] })[0] }
  5         11  
  9         32  
  5         33  
1669 1089     1089   2857 sub _next_random { shift; (values %{ $_[0] })[ rand keys %{ $_[0] } ] }
  1089         1749  
  1089         5967  
  1089         4203  
1670              
1671             sub _root_opt {
1672 164     164   468 my ($g, @args) = @_;
1673 164 100       819 my %opt = @args == 1 ? ( first_root => $args[0] ) : _get_options( \@args );
1674 164         379 my %unseen;
1675 164         715 my @unseen = $g->_vertices05;
1676 164         19613 @unseen{ @unseen } = @unseen;
1677 164         4792 @unseen = _shuffle @unseen;
1678 164         299 my $r;
1679 164 100       585 if (exists $opt{ start }) {
1680 1         3 $opt{ first_root } = delete $opt{ start };
1681 1         3 $opt{ next_root } = undef;
1682             }
1683 164 100       420 if (exists $opt{ first_root }) {
1684 121 100       307 if (ref $opt{ first_root } eq 'CODE') {
1685 1         5 $r = $opt{ first_root }->( $g, \%unseen );
1686             } else {
1687 120         252 $r = $opt{ first_root };
1688             }
1689             } else {
1690 43         110 $r = shift @unseen;
1691             }
1692             my $next =
1693             exists $opt{ next_root } ?
1694             $opt{ next_root } :
1695             $opt{ next_alphabetic } ?
1696             \&_next_alphabetic :
1697             $opt{ next_numeric } ?
1698 164 50       872 \&_next_numeric :
    50          
    100          
1699             \&_next_random;
1700 164         443 my $code = ref $next eq 'CODE';
1701 164 50       436 my $attr = exists $opt{ attribute } ? $opt{ attribute } : $defattr;
1702 164         868 return ( \%opt, \%unseen, \@unseen, $r, $next, $code, $attr );
1703             }
1704              
1705             sub _heap_walk {
1706 86     86   1789 my ($g, $h, $add, $etc, $opt, $unseenh, $unseena, $r, $next, $code, $attr) = @_;
1707 86         6469 require Heap::Fibonacci;
1708 86         23425 my $HF = Heap::Fibonacci->new;
1709 86         1061 while (defined $r) {
1710             # print "r = $r\n";
1711 108         418 $add->($g, $h, $HF, $r, $attr, $unseenh, $etc);
1712 108         1118 delete $unseenh->{ $r };
1713 108         404 while (defined $HF->top) {
1714 4124         38620 my $t = $HF->extract_top;
1715             # use Data::Dumper; print "t = ", Dumper($t);
1716 4124 50       39933 if (defined $t) {
1717 4124         10505 my ($u, $v, $w) = $t->val;
1718             # print "extracted top: $u $v $w\n";
1719 4124 100       16016 if (exists $unseenh->{ $v }) {
1720 1909         62784 $h->set_edge_attribute($u, $v, $attr, $w);
1721 1909         5966 delete $unseenh->{ $v };
1722 1909         6353 $add->($g, $h, $HF, $v, $attr, $unseenh, $etc);
1723             }
1724             }
1725             }
1726 107 100       929 return $h unless defined $next;
1727 106 50       475 $r = $code ? $next->( $g, $unseenh ) : shift @$unseena;
1728 106 100       389 last unless defined $r;
1729             }
1730 84         385 return $h;
1731             }
1732              
1733             sub MST_Prim {
1734 43     43 1 22845 &expect_undirected;
1735 42         1372 require Graph::MSTHeapElem;
1736 42         278 $_[0]->_heap_walk(Graph->new(directed => 0), \&_MST_add, undef, &_root_opt);
1737             }
1738              
1739             *MST_Dijkstra = \&MST_Prim;
1740              
1741             *minimum_spanning_tree = \&MST_Prim;
1742              
1743             ###
1744             # Cycle detection.
1745             #
1746              
1747             *is_cyclic = \&has_a_cycle;
1748              
1749             sub is_acyclic {
1750 13     13 1 50 !&is_cyclic;
1751             }
1752              
1753             sub is_dag {
1754 5 100 100 5 1 2341 &is_directed && &is_acyclic ? 1 : 0;
1755             }
1756              
1757             *is_directed_acyclic_graph = \&is_dag;
1758              
1759             ###
1760             # Simple DFS uses.
1761             #
1762              
1763             sub topological_sort {
1764 5     5 1 1076 my $g = shift;
1765 5         28 my %opt = _get_options( \@_ );
1766 5         18 my $eic = delete $opt{ empty_if_cyclic };
1767 5         12 my $hac;
1768 5 100       23 if ($eic) {
1769 1         15 $hac = $g->has_a_cycle;
1770             } else {
1771 4         27 $g->expect_dag;
1772             }
1773 2         17 require Graph::Traversal::DFS;
1774 2         15 my $t = Graph::Traversal::DFS->new($g, %opt);
1775 2         11 my @s = $t->dfs;
1776 2 100       56 $hac ? () : reverse @s;
1777             }
1778              
1779             *toposort = \&topological_sort;
1780              
1781             sub _copy_vertices {
1782 83     83   199 my ($g, $gc, $attr_too) = @_;
1783 83 100       168 if (&is_multivertexed) {
1784 10         34 for my $v (&_vertices05) {
1785 33 100       78 if ($attr_too) {
1786             $gc->set_vertex_attributes_by_id($v, $_, $g->get_vertex_attributes_by_id($v, $_))
1787 13         42 for $g->get_multivertex_ids($v);
1788             } else {
1789 20         58 $gc->add_vertex_by_id($v, $_) for $g->get_multivertex_ids($v);
1790             }
1791             }
1792             } else {
1793 73 100       173 if ($attr_too) {
1794 2         36 $gc->set_vertex_attributes($_, $g->get_vertex_attributes($_)) for &_vertices05;
1795             } else {
1796 71         184 $gc->add_vertices(&_vertices05);
1797             }
1798             }
1799             }
1800              
1801             sub _copy_edges {
1802 83     83   201 my ($g, $gc, $attr_too, $mirror) = @_;
1803 83         250 my @edges = &_edges05;
1804 83 100       261 if (&is_multiedged) {
1805 10         30 for my $e (@edges) {
1806 28         110 for my $id ($g->get_multiedge_ids(@$e)) {
1807 28 100       80 if ($attr_too) {
1808 12         355 $gc->set_edge_attributes_by_id(@$e, $id, $g->get_edge_attributes_by_id(@$e, $id));
1809 12 100       219 $gc->set_edge_attributes_by_id(reverse(@$e), $id, $g->get_edge_attributes_by_id(@$e, $id)) if $mirror;
1810             } else {
1811 16         56 $gc->add_edge_by_id(@$e, $id);
1812 16 100       67 $gc->add_edge_by_id(reverse(@$e), $id) if $mirror;
1813             }
1814             }
1815             }
1816             } else {
1817 73 100       179 if ($attr_too) {
1818             $gc->set_edge_attributes(@$_, $g->get_edge_attributes(@$_))
1819 2         67 for @edges;
1820 2 50       9 if ($mirror) {
1821             $gc->set_edge_attributes(reverse(@$_), $g->get_edge_attributes(@$_))
1822 0         0 for @edges;
1823             }
1824             } else {
1825 71 100       355 $gc->add_edges(@edges, !$mirror ? () : map [reverse @$_], @edges);
1826             }
1827             }
1828             }
1829              
1830             sub undirected_copy {
1831 20     20 1 364 &expect_directed;
1832 20         115 my $gc = $_[0]->new(undirected=>1);
1833 20         82 _copy_vertices($_[0], $gc);
1834 20         62 _copy_edges($_[0], $gc);
1835 20         81 $gc;
1836             }
1837              
1838             *undirected_copy_graph = \&undirected_copy;
1839              
1840             sub undirected_copy_attributes {
1841 1     1 1 5 &expect_directed;
1842 1         5 my $gc = $_[0]->new(undirected=>1);
1843 1         10 $gc->set_graph_attributes($_[0]->get_graph_attributes);
1844 1         5 _copy_vertices($_[0], $gc, 1);
1845 1         6 _copy_edges($_[0], $gc, 1);
1846 1         6 $gc;
1847             }
1848              
1849             sub directed_copy {
1850 6     6 1 19 &expect_undirected;
1851 6         21 my $gc = $_[0]->new(undirected=>0);
1852 6         19 _copy_vertices($_[0], $gc);
1853 6         18 _copy_edges($_[0], $gc, 0, 1);
1854 6         26 $gc;
1855             }
1856              
1857             *directed_copy_graph = \&directed_copy;
1858              
1859             sub directed_copy_attributes {
1860 1     1 1 219 &expect_undirected;
1861 1         6 my $gc = $_[0]->new(directed=>1);
1862 1         10 $gc->set_graph_attributes($_[0]->get_graph_attributes);
1863 1         6 _copy_vertices($_[0], $gc, 1);
1864 1         7 _copy_edges($_[0], $gc, 1, 1);
1865 1         6 $gc;
1866             }
1867              
1868             sub is_bipartite {
1869 14     14 1 193 &expect_undirected;
1870 14         38 my ($g) = @_;
1871 14         29 my $is_bipartite = 1;
1872 14         32 my %colors;
1873             my $operations = {
1874             tree_edge => sub {
1875 405     405   1124 my( $seen, $unseen ) = @_;
1876 405         2189 ( $seen, $unseen ) = sort { exists $colors{$b} <=> exists $colors{$a} } ( $seen, $unseen );
  810         1610  
1877 405   100     1224 $colors{$seen} ||= -1;
1878 405         1431 $colors{$unseen} = -$colors{$seen};
1879             },
1880             non_tree_edge => sub {
1881 30884 100   30884   133564 $is_bipartite = '' if $colors{$_[0]} == $colors{$_[1]};
1882             },
1883 14         160 };
1884 14         519 require Graph::Traversal::DFS;
1885 14         104 Graph::Traversal::DFS->new( $g, %$operations )->dfs;
1886 14         21617 return $is_bipartite;
1887             }
1888              
1889             sub is_planar {
1890 8     8 1 83 &expect_undirected;
1891 8         18 my ($g) = @_;
1892 8         33 my @paths_at = map [], 1..$g->vertices;
1893 8         46 my $path_graph = Graph->new(undirected => 1);
1894 8         27 my ($n, $d, %order) = (0, 0);
1895             my $operations = {
1896             pre => sub {
1897 98     98   177 $order{$_[0]} = $n;
1898 98         230 $n++;
1899             },
1900             non_tree_edge => sub {
1901 432     432   1156 my( $i, $j ) = sort map { $order{$_} } @_[0..1];
  864         2808  
1902 432         816 for (@{$paths_at[$i]}) { # for all crossed paths
  432         1063  
1903 15562         35879 $path_graph->add_edge( $_, $d );
1904             }
1905 432         1600 for ($i+1..$j-1) {
1906 3332         3926 push @{$paths_at[$_]}, $d;
  3332         6406  
1907             }
1908 432         1347 $d++;
1909             },
1910 8         112 };
1911 8         530 require Graph::Traversal::DFS;
1912 8         67 Graph::Traversal::DFS->new( $g, %$operations )->dfs;
1913 8         424 return $path_graph->is_bipartite;
1914             }
1915              
1916             ###
1917             # Cache or not.
1918             #
1919              
1920             my %_cache_type =
1921             (
1922             'connectivity' => ['_ccc'],
1923             'strong_connectivity' => ['_scc'],
1924             'weak_connectivity_undirected_graph' => ['_wcug'],
1925             'biconnectivity' => ['_bcc'],
1926             'SPT_Dijkstra' => ['_spt_di', 'SPT_Dijkstra_root'],
1927             'SPT_Bellman_Ford' => ['_spt_bf', 'SPT_Bellman_Ford_root'],
1928             'transitive_closure_matrix' => ['_tcm'],
1929             );
1930              
1931             for my $t (keys %_cache_type) {
1932 84     84   834 no strict 'refs';
  84         166  
  84         910311  
1933             my @attr = @{ $_cache_type{$t} };
1934 228     228   256379 *{$t."_clear_cache"} = sub { $_[0]->delete_graph_attribute($_) for @attr };
1935             }
1936              
1937             sub _check_cache {
1938 2285     2285   6108 my ($g, $type, $extra_vals, $code, @args) = @_;
1939 2285         5050 my $c = $_cache_type{$type};
1940 2285 50       5510 __carp_confess "Graph: unknown cache type '$type'" if !defined $c;
1941 2285         5173 my ($main_key, @extra_keys) = @$c;
1942 2285 50       5619 __carp_confess "Graph: wrong number of extra values (@extra_keys) vs (@$extra_vals)" if @extra_keys != @$extra_vals;
1943 2285         8152 my $a = $g->get_graph_attribute($main_key);
1944 2285 50 66     10082 __carp_confess "$c attribute set to unexpected value $a"
1945             if defined $a and ref $a ne 'ARRAY';
1946 2285 100 100     8732 unless (defined $a && $a->[ 0 ] == $g->[ _G ]) {
1947 459         1771 $g->set_graph_attribute($main_key, $a = [ $g->[ _G ], $code->( $g, @args ) ]);
1948             }
1949 2283         4192 my $i = -1;
1950             my $extra_invalid = grep {
1951 2283         4574 my $v = $a->[ 1 ]->get_graph_attribute($_);
  120         364  
1952 120         327 $i++; # here so still incremented even if short-cut
1953 120 50       750 !defined $v or $v ne $extra_vals->[$i];
1954             } @extra_keys;
1955 2283 100       5634 if ($extra_invalid) {
1956 31         113 $g->set_graph_attribute($main_key, $a = [ $g->[ _G ], $code->( $g, @args ) ]);
1957             }
1958 2283         14883 return $a->[ 1 ];
1959             }
1960              
1961             ###
1962             # Connected components.
1963             #
1964              
1965             sub _connected_components_compute {
1966 45     45   75 my $g = $_[0];
1967 45         85 my %v2c;
1968             my @c;
1969 45 100       149 return [ [], {} ] unless my @v = $g->unique_vertices;
1970 39 100       100 if (my $UF = &has_union_find) {
1971 9         17 my $V = $g->[ _V ];
1972 9         37 my @ids = $V->get_ids_by_paths(\@v, 0);
1973 9         22 my ($counter, %cc2counter) = 0;
1974 9         51 my @cc = $UF->find(@ids);
1975 9         70 for (my $i = 0; $i <= $#v; $i++) {
1976 20         27 my $cc = $cc[$i];
1977 20 50       34 __carp_confess "connected_component union-find did not have vertex '$v[$i]', please report"
1978             if !defined $cc;
1979 20 100       44 $cc2counter{$cc} = $counter++ if !exists $cc2counter{$cc};
1980 20         27 my $ci = $cc2counter{$cc};
1981 20         33 $v2c{ $v[$i] } = $ci;
1982 20         23 push @{ $c[$ci] }, $v[$i];
  20         82  
1983             }
1984             } else {
1985 30         4153 require Graph::Traversal::DFS;
1986 30         66 my %r; @r{ @v } = @v;
  30         164  
1987 30         85 @c = [];
1988             my $t = Graph::Traversal::DFS->new(
1989             $g,
1990 30     30   183 first_root => sub { (each %r)[1] },
1991 40 100   40   151 next_root => sub { push @c, [] if keys %r; (each %r)[1]; },
  40         263  
1992             pre => sub {
1993 106     106   229 my ($v, $t) = @_;
1994 106         239 $v2c{ $v } = $#c;
1995 106         165 push @{ $c[-1] }, $v;
  106         276  
1996 106         387 delete $r{ $v };
1997             },
1998 30         532 @_[1..$#_]
1999             );
2000 30         156 $t->dfs;
2001             }
2002 39         421 return [ \@c, \%v2c ];
2003             }
2004              
2005             sub _connected_components {
2006 384     384   1097 my $ccc = _check_cache($_[0], 'connectivity', [],
2007             \&_connected_components_compute);
2008 384         640 return @{ $ccc };
  384         1669  
2009             }
2010              
2011             sub connected_component_by_vertex {
2012 82     82 1 12045 &expect_undirected;
2013 81         151 (&_connected_components)[1]->{ $_[1] };
2014             }
2015              
2016             sub connected_component_by_index {
2017 58     58 1 18563 &expect_undirected;
2018 57         107 my $value = (&_connected_components)[0]->[$_[1]];
2019 57 50       148 $value ? @{ $value || _empty_array } : ();
  41 100       203  
2020             }
2021              
2022             sub connected_components {
2023 41     41 1 995 &expect_undirected;
2024 40         51 @{ (&_connected_components)[0] };
  40         72  
2025             }
2026              
2027             sub same_connected_components {
2028 29     29 1 22571 &expect_undirected;
2029 28         83 my ($g, @args) = @_;
2030 28         43 my @components;
2031 28 100       70 if (my $UF = &has_union_find) {
2032 14         37 my @ids = &_vertex_ids;
2033 14 100       65 return 0 if @ids != @args;
2034 10         39 @components = $UF->find(@ids);
2035             } else {
2036 14         25 @components = @{ (&_connected_components)[1] }{ @args };
  14         32  
2037             }
2038 24 100       141 return 0 if grep !defined, @components;
2039 20         138 require List::Util;
2040 20         242 List::Util::uniq( @components ) == 1;
2041             }
2042              
2043 40     40   713 sub _super_component { join("+", sort @_) }
2044              
2045             sub connected_graph {
2046 21     21 1 1136 &expect_undirected;
2047 20         54 my ($g, %opt) = @_;
2048 20         108 my $cg = Graph->new(undirected => 1);
2049 20 100 100     59 if ($g->has_union_find && $g->vertices == 1) {
2050             # TODO: super_component?
2051 2         7 $cg->add_vertices($g->vertices);
2052             } else {
2053 18   50     84 my $sc_cb = $opt{super_component} || \&_super_component;
2054             $cg->set_vertex_attribute(scalar $sc_cb->(@$_), 'subvertices', $_)
2055 18         84 for $g->connected_components;
2056             }
2057 20         85 return $cg;
2058             }
2059              
2060             sub is_connected {
2061 197     197 1 1492 &expect_undirected;
2062 192         294 return @{ (&_connected_components)[0] } == 1;
  192         445  
2063             }
2064              
2065             sub is_weakly_connected {
2066 10     10 1 3739 &expect_directed;
2067 9         33 splice @_, 0, 1, &undirected_copy;
2068 9         29 goto &is_connected;
2069             }
2070              
2071             *weakly_connected = \&is_weakly_connected;
2072              
2073             # because recreating undirected copy every time has different hash ordering
2074             # so weakly_connected_component_by_index etc would be unstable
2075             sub _weakly_connected_undir_graph {
2076 51     51   171 _check_cache($_[0], 'weak_connectivity_undirected_graph', [],
2077             \&undirected_copy);
2078             }
2079              
2080             sub weakly_connected_components {
2081 6     6 1 889 &expect_directed;
2082 5         15 splice @_, 0, 1, &_weakly_connected_undir_graph;
2083 5         20 goto &connected_components;
2084             }
2085              
2086             sub weakly_connected_component_by_vertex {
2087 21     21 1 6944 &expect_directed;
2088 20         52 splice @_, 0, 1, &_weakly_connected_undir_graph;
2089 20         72 goto &connected_component_by_vertex;
2090             }
2091              
2092             sub weakly_connected_component_by_index {
2093 15     15 1 8423 &expect_directed;
2094 14         28 splice @_, 0, 1, &_weakly_connected_undir_graph;
2095 14         40 goto &connected_component_by_index;
2096             }
2097              
2098             sub same_weakly_connected_components {
2099 8     8 1 12158 &expect_directed;
2100 7         22 splice @_, 0, 1, &_weakly_connected_undir_graph;
2101 7         30 goto &same_connected_components;
2102             }
2103              
2104             sub weakly_connected_graph {
2105 6     6 1 860 &expect_directed;
2106 5         14 splice @_, 0, 1, &_weakly_connected_undir_graph;
2107 5         21 goto &connected_graph;
2108             }
2109              
2110             sub _strongly_connected_components_compute {
2111 14     14   27 my $g = $_[0];
2112 14         1507 require Graph::Traversal::DFS;
2113 14         73 require List::Util;
2114 14         119 my $t = Graph::Traversal::DFS->new($g);
2115 14         89 my @d = reverse $t->dfs;
2116 14         32 my @c;
2117             my %v2c;
2118             my $u = Graph::Traversal::DFS->new(
2119             $g->transpose_graph,
2120             next_root => sub {
2121 141     141   453 my ($t, $u) = @_;
2122             return if !defined(my $root = List::Util::first(
2123 3463         9299 sub { exists $u->{$_} }, @d
2124 141 100       1164 ));
2125 127         632 push @c, [];
2126 127         779 return $root;
2127             },
2128             pre => sub {
2129 256     256   611 my ($v, $t) = @_;
2130 256         384 push @{ $c[-1] }, $v;
  256         701  
2131 256         1106 $v2c{$v} = $#c;
2132             },
2133 14         95 next_alphabetic => 1,
2134             );
2135 14         78 $u->dfs;
2136 14         1924 return [ \@c, \%v2c ];
2137             }
2138              
2139             sub _strongly_connected_components_v2c {
2140 12     12   21 &_strongly_connected_components->[1];
2141             }
2142              
2143             sub _strongly_connected_components_arrays {
2144 18     18   33 @{ &_strongly_connected_components->[0] };
  18         39  
2145             }
2146              
2147             sub _strongly_connected_components {
2148 40     40   178 _check_cache($_[0], 'strong_connectivity', [],
2149             \&_strongly_connected_components_compute);
2150             }
2151              
2152             sub strongly_connected_components {
2153 19     19 1 278 &expect_directed;
2154 18         144 goto &_strongly_connected_components_arrays;
2155             }
2156              
2157             sub strongly_connected_component_by_vertex {
2158 5     5 1 675 &expect_directed;
2159 4         12 &_strongly_connected_components_v2c->{$_[1]};
2160             }
2161              
2162             sub strongly_connected_component_by_index {
2163 6     6 1 9209 &expect_directed;
2164 5         8 my $i = $_[1];
2165 5 100       9 return if !defined(my $c = &_strongly_connected_components->[0][ $i ]);
2166 4         42 @$c;
2167             }
2168              
2169             sub same_strongly_connected_components {
2170 8     8 1 4828 &expect_directed;
2171 8         29 my ($g, @args) = @_;
2172 8         62 require Set::Object;
2173 8         17 Set::Object->new(@{ &_strongly_connected_components_v2c }{@args})->size <= 1;
  8         17  
2174             }
2175              
2176             sub is_strongly_connected {
2177 4     4 1 16 &strongly_connected_components == 1;
2178             }
2179              
2180             *strongly_connected = \&is_strongly_connected;
2181              
2182             sub strongly_connected_graph {
2183 6     6 1 17293 &expect_directed;
2184 6         23 my ($g, %attr) = @_;
2185 6         21 my $sc_cb = \&_super_component;
2186 6         29 _opt_get(\%attr, super_component => \$sc_cb);
2187 6         21 _opt_unknown(\%attr);
2188 5         8 my ($c, $v2c) = @{ &_strongly_connected_components };
  5         14  
2189 5         23 my $s = Graph->new;
2190 5         30 my @s = map $sc_cb->(@$_), @$c;
2191 5         262 $s->set_vertex_attribute($s[$_], 'subvertices', $c->[$_]) for 0..$#$c;
2192 5         41 require List::Util;
2193 5         38 $s->add_edges(map [@s[ @$v2c{ @$_ } ]], grep List::Util::uniq( @$v2c{ @$_ } ) > 1, &_edges05);
2194 5         52 return $s;
2195             }
2196              
2197             ###
2198             # Biconnectivity.
2199             #
2200              
2201             sub _biconnectivity_out {
2202 14931     14931   29095 my ($state, $u, $v) = @_;
2203 14931         19540 my @BC;
2204 14931         19217 while (@{$state->{stack}}) {
  16855         34746  
2205 16855         23593 push @BC, my $e = pop @{$state->{stack}};
  16855         35836  
2206 16855 100 66     63189 last if $e->[0] eq $u && $e->[1] eq $v;
2207             }
2208 14931 50       33361 push @{$state->{BC}}, \@BC if @BC;
  14931         41592  
2209             }
2210              
2211             sub _biconnectivity_dfs {
2212 17608     17608   38265 my ($E, $u, $state) = @_;
2213 17608         47202 $state->{low}{$u} = $state->{num}{$u} = $state->{dfs}++;
2214 17608         46581 for my $v ($E->successors($u)) {
2215 35320 100 100     146209 if (!exists $state->{num}{$v}) {
    100 100        
2216 15948         21275 push @{$state->{stack}}, [$u, $v];
  15948         44797  
2217 15948         40547 $state->{pred}{$v} = $u;
2218 15948         38071 _biconnectivity_dfs($E, $v, $state);
2219 15948         26527 $state->{low}{$u} = List::Util::min(@{ $state->{low} }{$u, $v});
  15948         44911  
2220             _biconnectivity_out($state, $u, $v)
2221 15948 100       45423 if $state->{low}{$v} >= $state->{num}{$u};
2222             } elsif (defined $state->{pred}{$u} &&
2223             $state->{pred}{$u} ne $v &&
2224             $state->{num}{$v} < $state->{num}{$u}) {
2225 907         1459 push @{$state->{stack}}, [$u, $v];
  907         2846  
2226 907         3185 $state->{low}{$u} = List::Util::min($state->{low}{$u}, $state->{num}{$v});
2227             }
2228             }
2229             }
2230              
2231             sub _biconnectivity_compute {
2232 288     288   2424 require List::Util;
2233 288         788 my ($g) = @_;
2234 288         904 my ($V, $E) = @$g[ _V, _E ];
2235 288         1215 my %state = (BC=>[], dfs=>0);
2236 288         1355 my @u = $V->ids;
2237 288         849 for my $u (@u) {
2238 17608 100       43886 next if exists $state{num}->{$u};
2239 1660         4473 _biconnectivity_dfs($E, $u, \%state);
2240 1660 100       3349 push @{$state{BC}}, delete $state{stack} if @{ $state{stack} || _empty_array };
  0 50       0  
  1660         6195  
2241             }
2242              
2243             # Mark the components each vertex belongs to.
2244 288         962 my ($bci, %v2bc, %bc2v) = 0;
2245 288         470 for my $bc (@{$state{BC}}) {
  288         806  
2246 14931         67984 $v2bc{$_}{$bci} = undef for map @$_, @$bc;
2247 14931         24524 $bci++;
2248             }
2249              
2250             # Any isolated vertices get each their own component.
2251 288         5714 $v2bc{$_}{$bci++} = undef for grep !exists $v2bc{$_}, @u;
2252              
2253             # build vector now we know how big to make it
2254 288         1549 my ($Z, %v2bc_vec, @ap) = "\0" x (($bci + 7) / 8);
2255 288         8166 @v2bc_vec{@u} = ($Z) x @u;
2256 288         900 for my $v (@u) {
2257 17608         22225 my @components = keys %{ $v2bc{$v} };
  17608         40325  
2258 17608         68715 vec($v2bc_vec{$v}, $_, 1) = 1 for @components;
2259 17608         60648 $bc2v{$_}{$v}{$_} = undef for @components;
2260             # Articulation points / cut vertices are the vertices
2261             # which belong to more than one component.
2262 17608 100       44234 push @ap, $v if @components > 1;
2263             }
2264              
2265             # Bridges / cut edges are the components of two vertices.
2266 288         36998 my @br = grep @$_ == 2, map [keys %$_], values %bc2v;
2267              
2268             # Create the subgraph components.
2269 288         1597 my @sg = map [ List::Util::uniq( map @$_, @$_ ) ], @{$state{BC}};
  288         47007  
2270 288         2799 my ($apdeep, $sgv, $brv) = $V->get_paths_by_ids([[\@ap], \@sg, \@br], 1);
2271 288         40024 return [ @$apdeep, $sgv, $brv, \%v2bc, \%v2bc_vec, $Z ];
2272             }
2273              
2274             sub biconnectivity {
2275 471     471 1 230579 &expect_undirected;
2276 470 50       767 @{ _check_cache($_[0], 'biconnectivity', [],
  470         2738  
2277             \&_biconnectivity_compute, @_[1..$#_]) || _empty_array };
2278             }
2279              
2280             sub is_biconnected {
2281 13 100   13 1 31997 &edges >= 2 ? @{ (&biconnectivity)[0] } == 0 : undef ;
  10         32  
2282             }
2283              
2284             sub is_edge_connected {
2285 13 100   13 1 43 &edges >= 2 ? @{ (&biconnectivity)[2] } == 0 : undef;
  10         32  
2286             }
2287              
2288             sub is_edge_separable {
2289 13 100   13 1 56 &edges >= 2 ? @{ (&biconnectivity)[2] } > 0 : undef;
  10         47  
2290             }
2291              
2292             sub articulation_points {
2293 248     248 1 2765 @{ (&biconnectivity)[0] };
  248         794  
2294             }
2295              
2296             *cut_vertices = \&articulation_points;
2297              
2298             sub biconnected_components {
2299 14     14 1 1991 @{ (&biconnectivity)[1] };
  14         37  
2300             }
2301              
2302             sub biconnected_component_by_index {
2303 16     16 1 8362 my ($i) = splice @_, 1, 1;
2304 16         15 (&biconnectivity)[1]->[ $i ];
2305             }
2306              
2307             sub biconnected_component_by_vertex {
2308 2     2 1 4 my ($v) = splice @_, 1, 1;
2309 2         5 my $v2bc = (&biconnectivity)[3];
2310 2         5 splice @_, 1, 0, $v;
2311 2         3 my $V = $_[0]->[ _V ];
2312 2         5 ($v) = $V->get_ids_by_paths([$v]);
2313 2 50       6 return defined $v2bc->{ $v } ? keys %{ $v2bc->{ $v } } : ();
  2         8  
2314             }
2315              
2316             sub same_biconnected_components {
2317 5     5 1 288 my ($v2bc, $Z) = (&biconnectivity)[4,5];
2318 5         8 my $V = $_[0]->[ _V ];
2319 5         14 my @vs = $V->get_ids_by_paths([@_[1..$#_]]);
2320 5 50       24 return 0 if grep !defined, my @vecs = @$v2bc{ @vs };
2321 5         8 my $accumulator = $vecs[0];
2322 5         13 $accumulator &= $_ for @vecs[1..$#vecs]; # accumulate 0s -> all in same
2323 5         24 $accumulator ne $Z;
2324             }
2325              
2326             sub biconnected_graph {
2327 1     1 1 95 my ($g, %opt) = @_;
2328 1         5 my $bc = (&biconnectivity)[1];
2329 1         8 my $bcg = Graph->new(directed => 0);
2330 1   50     8 my $sc_cb = $opt{super_component} || \&_super_component;
2331 1         5 my @s = map $sc_cb->(@$_), @$bc;
2332 1         30 $bcg->set_vertex_attribute($s[$_], 'subvertices', $bc->[$_]) for 0..$#$bc;
2333 1         2 my @edges;
2334 1         2 for my $i (0..$#$bc) {
2335 5         5 my @u = @{ $bc->[ $i ] };
  5         9  
2336 5         7 for my $j (0..$i-1) {
2337 10         8 my %j; @j{ @{ $bc->[ $j ] } } = ();
  10         11  
  10         16  
2338 10 100       22 next if !grep exists $j{ $_ }, @u;
2339 4         11 push @edges, [ @s[$i, $j] ];
2340             }
2341             }
2342 1         4 $bcg->add_edges(@edges);
2343 1         5 return $bcg;
2344             }
2345              
2346             sub bridges {
2347 59 50   59 1 24549 @{ (&biconnectivity)[2] || _empty_array };
  59         240  
2348             }
2349              
2350             ###
2351             # SPT.
2352             #
2353              
2354             sub _SPT_add {
2355 1091     1091   3003 my ($g, $h, $HF, $r, $attr, $unseen, $etc) = @_;
2356 1091   100     3681 my $etc_r = $etc->{ $r } || 0;
2357 1091         3866 for my $s ( grep exists $unseen->{ $_ }, $g->successors( $r ) ) {
2358 5278         15511 my ($t) = sort {$a<=>$b} $g->get_edge_attribute_all($r, $s, $attr);
  0         0  
2359 5278 100       10395 $t = 1 unless defined $t;
2360 5278 100       10881 __carp_confess "Graph::SPT_Dijkstra: edge $r-$s is negative ($t)"
2361             if $t < 0;
2362 5277 100 100     33061 if (!defined($etc->{ $s }) || ($etc_r + $t) < $etc->{ $s }) {
2363 1034   100     3033 my $etc_s = $etc->{ $s } || 0;
2364 1034         2527 $etc->{ $s } = $etc_r + $t;
2365             # print "$r - $s : setting $s to $etc->{ $s } ($etc_r, $etc_s)\n";
2366 1034         23900 $h->set_vertex_attributes($s, { $attr=>$etc->{ $s }, 'p', $r });
2367 1034         4310 $HF->add( Graph::SPTHeapElem->new($r, $s, $etc->{ $s }) );
2368             }
2369             }
2370             }
2371              
2372             sub _SPT_Dijkstra_compute {
2373 44     44   5863 require Graph::SPTHeapElem;
2374 44         260 my $sptg = $_[0]->_heap_walk($_[0]->new(multiedged=>0), \&_SPT_add, {}, @_[1..$#_]);
2375 43         1978 $sptg->set_graph_attribute('SPT_Dijkstra_root', $_[4]);
2376 43         276 $sptg;
2377             }
2378              
2379             sub SPT_Dijkstra {
2380 88     88 1 2003 my $g = $_[0];
2381 88         271 my @args = &_root_opt;
2382 88         410 _check_cache($g, 'SPT_Dijkstra', [$args[3]],
2383             \&_SPT_Dijkstra_compute, @args);
2384             }
2385              
2386             *SSSP_Dijkstra = \&SPT_Dijkstra;
2387              
2388             *single_source_shortest_paths = \&SPT_Dijkstra;
2389              
2390             sub SP_Dijkstra {
2391 74     74 1 60543 my ($g, $u, $v) = @_;
2392 74         248 my $sptg = $g->SPT_Dijkstra(first_root => $u);
2393 74         472 my @path = ($v);
2394 74         434 require Set::Object;
2395 74         324 my $seen = Set::Object->new;
2396 74         214 my $V = $g->vertices;
2397 74         130 my $p;
2398 74         2549 while (defined($p = $sptg->get_vertex_attribute($v, 'p'))) {
2399 92 50       377 last if $seen->contains($p);
2400 92         568 push @path, $p;
2401 92         152 $v = $p;
2402 92         333 $seen->insert($p);
2403 92 100 66     2092 last if $seen->size == $V || $u eq $v;
2404             }
2405 74 100 66     592 return if !@path or $path[-1] ne $u;
2406 33         545 return reverse @path;
2407             }
2408              
2409             sub __SPT_Bellman_Ford {
2410 2184     2184   4973 my ($g, $u, $v, $attr, $d, $p, $c0, $c1) = @_;
2411 2184 100       6167 return unless $c0->{ $u };
2412 226         603 my ($w) = sort {$a<=>$b} $g->get_edge_attribute_all($u, $v, $attr);
  0         0  
2413 226 100       586 $w = 1 unless defined $w;
2414 226 100       578 if (defined $d->{ $v }) {
2415 147 50       442 if (defined $d->{ $u }) {
2416 147 100       745 if ($d->{ $v } > $d->{ $u } + $w) {
2417 15         25 $d->{ $v } = $d->{ $u } + $w;
2418 15         24 $p->{ $v } = $u;
2419 15         34 $c1->{ $v }++;
2420             }
2421             } # else !defined $d->{ $u } && defined $d->{ $v }
2422             } else {
2423 79 50       297 if (defined $d->{ $u }) {
2424             # defined $d->{ $u } && !defined $d->{ $v }
2425 79         226 $d->{ $v } = $d->{ $u } + $w;
2426 79         220 $p->{ $v } = $u;
2427 79         231 $c1->{ $v }++;
2428             } # else !defined $d->{ $u } && !defined $d->{ $v }
2429             }
2430             }
2431              
2432             sub _SPT_Bellman_Ford {
2433 12     12   46 my ($g, $opt, $unseenh, $unseena, $r, $next, $code, $attr) = @_;
2434 12         21 my %d;
2435 12 50       34 return unless defined $r;
2436 12         39 $d{ $r } = 0;
2437 12         20 my %p;
2438 12         53 my $V = $g->vertices;
2439 12         22 my %c0; # Changed during the last iteration?
2440 12         44 $c0{ $r }++;
2441 12         55 for (my $i = 0; $i < $V; $i++) {
2442 95         143 my %c1;
2443 95         252 for my $e ($g->edges) {
2444 1612         3408 my ($u, $v) = @$e;
2445 1612         3837 __SPT_Bellman_Ford($g, $u, $v, $attr, \%d, \%p, \%c0, \%c1);
2446 1612 100       3413 __SPT_Bellman_Ford($g, $v, $u, $attr, \%d, \%p, \%c0, \%c1)
2447             if $g->undirected;
2448             }
2449 95 100       1143 %c0 = %c1 unless $i == $V - 1;
2450             }
2451              
2452 12         37 for my $e ($g->edges) {
2453 172         374 my ($u, $v) = @$e;
2454 172 100 66     840 if (defined $d{ $u } && defined $d{ $v }) {
2455 159         456 my ($d) = sort {$a<=>$b} $g->get_edge_attribute_all($u, $v, $attr);
  0         0  
2456             __carp_confess "Graph::SPT_Bellman_Ford: negative cycle exists"
2457 159 100 100     719 if defined $d && $d{ $v } > $d{ $u } + $d;
2458             }
2459             }
2460              
2461 11         107 return (\%p, \%d);
2462             }
2463              
2464             sub _SPT_Bellman_Ford_compute {
2465 12     12   213 my ($g, @args) = @_;
2466 12         90 my ($p, $d) = $g->_SPT_Bellman_Ford(@args);
2467 11         58 my $h = $g->new(multiedged=>0);
2468 11         69 for my $v (keys %$p) {
2469 74         186 my $u = $p->{ $v };
2470 74         224 my ($w) = sort {$a<=>$b} $g->get_edge_attribute_all($u, $v, $args[6]);
  0         0  
2471 74         1847 $h->set_edge_attribute( $u, $v, $args[6], $w);
2472 74         2062 $h->set_vertex_attributes( $v, { $args[6], $d->{ $v }, p => $u } );
2473             }
2474 11         98 $h->set_graph_attribute('SPT_Bellman_Ford_root', $args[3]);
2475 11         134 $h;
2476             }
2477              
2478             sub SPT_Bellman_Ford {
2479 34     34 1 2084 my @args = &_root_opt;
2480 34         177 _check_cache($_[0], 'SPT_Bellman_Ford', [$args[3]],
2481             \&_SPT_Bellman_Ford_compute, @args);
2482             }
2483              
2484             *SSSP_Bellman_Ford = \&SPT_Bellman_Ford;
2485              
2486             sub SP_Bellman_Ford {
2487 24     24 1 23048 my ($g, $u, $v) = @_;
2488 24         80 my $sptg = $g->SPT_Bellman_Ford(first_root => $u);
2489 24         67 my @path = ($v);
2490 24         162 require Set::Object;
2491 24         145 my $seen = Set::Object->new;
2492 24         71 my $V = $g->vertices;
2493 24         39 my $p;
2494 24         820 while (defined($p = $sptg->get_vertex_attribute($v, 'p'))) {
2495 40 50       154 last if $seen->contains($p);
2496 40         244 push @path, $p;
2497 40         55 $v = $p;
2498 40         132 $seen->insert($p);
2499 40 50       895 last if $seen->size == $V;
2500             }
2501             # @path = () if @path && "$path[-1]" ne "$u";
2502 24         335 return reverse @path;
2503             }
2504              
2505             ###
2506             # Transitive Closure.
2507             #
2508              
2509             sub TransitiveClosure_Floyd_Warshall {
2510 19     19 1 3897 my $self = shift;
2511 19         816 require Graph::TransitiveClosure;
2512 19         121 Graph::TransitiveClosure->new($self, @_);
2513             }
2514              
2515             *transitive_closure = \&TransitiveClosure_Floyd_Warshall;
2516              
2517             sub APSP_Floyd_Warshall {
2518 37     37 1 3107 my $self = shift;
2519 37         1876 require Graph::TransitiveClosure;
2520 37         246 Graph::TransitiveClosure->new($self, path => 1, @_);
2521             }
2522              
2523             *all_pairs_shortest_paths = \&APSP_Floyd_Warshall;
2524              
2525             sub _transitive_closure_matrix_compute {
2526 22     22   65 &APSP_Floyd_Warshall->transitive_closure_matrix;
2527             }
2528              
2529             sub transitive_closure_matrix {
2530 1143     1143 1 4340 _check_cache($_[0], 'transitive_closure_matrix', [],
2531             \&_transitive_closure_matrix_compute, @_[1..$#_]);
2532             }
2533              
2534             sub path_length {
2535 1532     1532 1 23550 shift->transitive_closure_matrix->path_length(@_);
2536             }
2537              
2538             sub path_successor {
2539 27     27 1 127 shift->transitive_closure_matrix->path_successor(@_);
2540             }
2541              
2542             sub path_vertices {
2543 205     205 1 123547 shift->transitive_closure_matrix->path_vertices(@_);
2544             }
2545              
2546             sub all_paths {
2547 25     25 1 11266 shift->transitive_closure_matrix->all_paths(@_);
2548             }
2549              
2550             sub is_reachable {
2551 13157     13157 1 713074 shift->transitive_closure_matrix->is_reachable(@_);
2552             }
2553              
2554             sub for_shortest_paths {
2555 34     34 1 62 my $g = shift;
2556 34         55 my $c = shift;
2557 34         91 my $t = $g->transitive_closure_matrix;
2558 34         118 my @v = $g->vertices;
2559 34         88 my $n = 0;
2560 34         111 for my $u (@v) {
2561 183         643 $c->($t, $u, $_, ++$n) for grep $t->is_reachable($u, $_), @v;
2562             }
2563 34         106 return $n;
2564             }
2565              
2566             sub _minmax_path {
2567 25     25   56 my $g = shift;
2568 25         88 my $min;
2569             my $max;
2570 25         0 my $minp;
2571 25         0 my $maxp;
2572             $g->for_shortest_paths(sub {
2573 628     628   1160 my ($t, $u, $v, $n) = @_;
2574 628         1331 my $l = $t->path_length($u, $v);
2575 628 50       1182 return unless defined $l;
2576 628         834 my $p;
2577 628 100 100     2363 if ($u ne $v && (!defined $max || $l > $max)) {
      100        
2578 50         107 $max = $l;
2579 50         133 $maxp = $p = [ $t->path_vertices($u, $v) ];
2580             }
2581 628 100 100     2827 if ($u ne $v && (!defined $min || $l < $min)) {
      100        
2582 18         29 $min = $l;
2583 18   100     78 $minp = $p || [ $t->path_vertices($u, $v) ];
2584             }
2585 25         219 });
2586 25         288 return ($min, $max, $minp, $maxp);
2587             }
2588              
2589             sub diameter {
2590 15     15 1 51 my $g = shift;
2591 15         53 my ($min, $max, $minp, $maxp) = $g->_minmax_path(@_);
2592 15 50       152 return defined $maxp ? (wantarray ? @$maxp : $max) : undef;
    100          
2593             }
2594              
2595             *graph_diameter = \&diameter;
2596              
2597             sub longest_path {
2598 5     5 1 18 my ($g, $u, $v) = @_;
2599 5         17 my $t = $g->transitive_closure_matrix;
2600 5 100       19 if (defined $u) {
2601 2 50       23 return wantarray ? $t->path_vertices($u, $v) : $t->path_length($u, $v)
    100          
2602             if defined $v;
2603 1         3 my $max;
2604             my @max;
2605 1         5 for my $v (grep $u ne $_, $g->vertices) {
2606 9         26 my $l = $t->path_length($u, $v);
2607 9 100 100     50 next if !(defined $l && (!defined $max || $l > $max));
      66        
2608 3         5 $max = $l;
2609 3         10 @max = $t->path_vertices($u, $v);
2610             }
2611 1 50       10 return wantarray ? @max : $max;
2612             }
2613 3 100       9 if (defined $v) {
2614 1         4 my $max;
2615             my @max;
2616 1         5 for my $u (grep $_ ne $v, $g->vertices) {
2617 9         26 my $l = $t->path_length($u, $v);
2618 9 100 100     53 next if !(defined $l && (!defined $max || $l > $max));
      66        
2619 2         5 $max = $l;
2620 2         6 @max = $t->path_vertices($u, $v);
2621             }
2622 1 50       13 return wantarray ? @max : @max - 1;
2623             }
2624 2         8 my ($min, $max, $minp, $maxp) = $g->_minmax_path(@_);
2625 2 50       19 return defined $maxp ? (wantarray ? @$maxp : $max) : undef;
    50          
2626             }
2627              
2628             sub vertex_eccentricity {
2629 165     165 1 520 &expect_undirected;
2630 165         661 my ($g, $u) = @_;
2631 165 100       346 return Infinity() if !&is_connected;
2632 158         281 my $max;
2633 158         454 for my $v (grep $u ne $_, $g->vertices) {
2634 1095         2649 my $l = $g->path_length($u, $v);
2635 1095 100 100     5472 next if !(defined $l && (!defined $max || $l > $max));
      66        
2636 366         714 $max = $l;
2637             }
2638 158 100       688 return defined $max ? $max : Infinity();
2639             }
2640              
2641             sub shortest_path {
2642 11     11 1 41 &expect_undirected;
2643 11         31 my ($g, $u, $v) = @_;
2644 11         30 my $t = $g->transitive_closure_matrix;
2645 11 100       36 if (defined $u) {
2646 2 50       14 return wantarray ? $t->path_vertices($u, $v) : $t->path_length($u, $v)
    100          
2647             if defined $v;
2648 1         4 my $min;
2649             my @min;
2650 1         5 for my $v (grep $u ne $_, $g->vertices) {
2651 9         27 my $l = $t->path_length($u, $v);
2652 9 100 66     53 next if !(defined $l && (!defined $min || $l < $min));
      33        
2653 1         3 $min = $l;
2654 1         5 @min = $t->path_vertices($u, $v);
2655             }
2656             # print "min/1 = @min\n";
2657 1 50       11 return wantarray ? @min : $min;
2658             }
2659 9 100       23 if (defined $v) {
2660 1         4 my $min;
2661             my @min;
2662 1         6 for my $u (grep $_ ne $v, $g->vertices) {
2663 9         26 my $l = $t->path_length($u, $v);
2664 9 100 100     58 next if !(defined $l && (!defined $min || $l < $min));
      66        
2665 3         7 $min = $l;
2666 3         10 @min = $t->path_vertices($u, $v);
2667             }
2668             # print "min/2 = @min\n";
2669 1 50       11 return wantarray ? @min : $min;
2670             }
2671 8         24 my ($min, $max, $minp, $maxp) = $g->_minmax_path(@_);
2672 8 100       52 return if !defined $minp;
2673 2 50       22 wantarray ? @$minp : $min;
2674             }
2675              
2676             sub radius {
2677 17     17 1 61 &expect_undirected;
2678 17         33 my $g = shift;
2679 17         50 my ($center, $radius) = (undef, Infinity());
2680 17         54 for my $v ($g->vertices) {
2681 89         232 my $x = $g->vertex_eccentricity($v);
2682 89 100 66     422 ($center, $radius) = ($v, $x) if defined $x && $x < $radius;
2683             }
2684 17         79 return $radius;
2685             }
2686              
2687             sub center_vertices {
2688 10     10 1 2102 &expect_undirected;
2689 10         31 my ($g, $delta) = @_;
2690 10 100       36 $delta = 0 unless defined $delta;
2691 10         24 $delta = abs($delta);
2692 10         18 my @c;
2693 10         35 my $Inf = Infinity();
2694 10         40 my $r = $g->radius;
2695 10 100 66     167 if (defined $r && $r != $Inf) {
2696 7         28 for my $v ($g->vertices) {
2697 53         159 my $e = $g->vertex_eccentricity($v);
2698 53 50 33     233 next unless defined $e && $e != $Inf;
2699 53 100       170 push @c, $v if abs($e - $r) <= $delta;
2700             }
2701             }
2702 10         84 return @c;
2703             }
2704              
2705             *centre_vertices = \¢er_vertices;
2706              
2707             sub average_path_length {
2708 9     9 1 2264 my $g = shift;
2709 9         28 my @A = @_;
2710 9         21 my $d = 0;
2711 9         16 my $m = 0;
2712             $g->for_shortest_paths(sub {
2713 809     809   1833 my ($t, $u, $v, $n) = @_;
2714 809 100       2261 return unless my $l = $t->path_length($u, $v);
2715 726 100 100     2863 return if defined $A[0] && $u ne $A[0];
2716 308 100 100     1098 return if defined $A[1] && $v ne $A[1];
2717 145         236 $d += $l;
2718 145         370 $m++;
2719 9         79 });
2720 9 100       135 return $m ? $d / $m : undef;
2721             }
2722              
2723             ###
2724             # Simple tests.
2725             #
2726              
2727             sub is_multi_graph {
2728 32 100 100 32 1 102 return 0 unless &is_multiedged || &is_countedged;
2729 16         35 my $g = $_[0];
2730 16         28 my $multiedges = 0;
2731 16         38 for my $e (&_edges05) {
2732 14         39 my ($u, @v) = @$e;
2733 14 100       88 return 0 if grep $u eq $_, @v;
2734 6 100       27 $multiedges++ if $g->get_edge_count(@$e) > 1;
2735             }
2736 8         47 return $multiedges;
2737             }
2738              
2739             sub is_simple_graph {
2740 32 100 100 32 1 125 return 1 unless &is_multiedged || &is_countedged;
2741 16         30 my $g = $_[0];
2742 16 100       45 return 0 if grep $g->get_edge_count(@$_) > 1, &_edges05;
2743 12         72 return 1;
2744             }
2745              
2746             sub is_pseudo_graph {
2747 32   100 32 1 115 my $m = &is_countedged || &is_multiedged;
2748 32         63 my $g = $_[0];
2749 32         95 for my $e (&_edges05) {
2750 28         82 my ($u, @v) = @$e;
2751 28 100       172 return 1 if grep $u eq $_, @v;
2752 12 100 100     56 return 1 if $m && $g->get_edge_count($u, @v) > 1;
2753             }
2754 14         79 return 0;
2755             }
2756              
2757             ###
2758             # Rough isomorphism guess.
2759             #
2760              
2761             my %_factorial = (0 => 1, 1 => 1);
2762              
2763             sub __factorial {
2764 4     4   6 my $n = shift;
2765 4         14 for (my $i = 2; $i <= $n; $i++) {
2766 14 100       29 next if exists $_factorial{$i};
2767 7         29 $_factorial{$i} = $i * $_factorial{$i - 1};
2768             }
2769 4         21 $_factorial{$n};
2770             }
2771              
2772             sub _factorial {
2773 39     39   41 my $n = int(shift);
2774 39 50       53 __carp_confess "factorial of a negative number" if $n < 0;
2775 39 100       75 __factorial($n) unless exists $_factorial{$n};
2776 39         88 return $_factorial{$n};
2777             }
2778              
2779             sub could_be_isomorphic {
2780 31     31 1 67 my ($g0, $g1) = @_;
2781 31 100       49 return 0 unless &vertices == $g1->vertices;
2782 23 100       47 return 0 unless &_edges05 == $g1->_edges05;
2783 17         21 my %d0;
2784 17         26 $d0{ $g0->in_degree($_) }{ $g0->out_degree($_) }++ for &vertices;
2785 17         26 my %d1;
2786 17         36 $d1{ $g1->in_degree($_) }{ $g1->out_degree($_) }++ for $g1->vertices;
2787 17 50       57 return 0 unless keys %d0 == keys %d1;
2788 17         49 for my $da (keys %d0) {
2789             return 0
2790             unless exists $d1{$da} &&
2791 31 50 33     55 keys %{ $d0{$da} } == keys %{ $d1{$da} };
  31         38  
  31         71  
2792             return 0
2793             if grep !(exists $d1{$da}{$_} && $d0{$da}{$_} == $d1{$da}{$_}),
2794 31 100 66     33 keys %{ $d0{$da} };
  31         168  
2795             }
2796 13         41 for my $da (keys %d0) {
2797 27 50       29 return 0 if grep $d1{$da}{$_} != $d0{$da}{$_}, keys %{ $d0{$da} };
  27         60  
2798 27         77 delete $d1{$da};
2799             }
2800 13 50       33 return 0 unless keys %d1 == 0;
2801 13         14 my $f = 1;
2802 13         19 for my $da (keys %d0) {
2803 27         26 $f *= _factorial(abs($d0{$da}{$_})) for keys %{ $d0{$da} };
  27         67  
2804             }
2805 13         101 return $f;
2806             }
2807              
2808             ###
2809             # Analysis functions.
2810              
2811             sub subgraph_by_radius {
2812 17     17 1 75 $_[0]->subgraph([ @_[1..$#_-1], &reachable_by_radius ]);
2813             }
2814              
2815             sub clustering_coefficient {
2816 2     2 1 26 my ($g) = @_;
2817 2 100       15 return unless my @v = $g->vertices;
2818 1         13 require Set::Object;
2819 1         3 my %clustering;
2820              
2821 1         17 my $gamma = 0;
2822              
2823 1         5 for my $n (@v) {
2824 15         25 my $gamma_v = 0;
2825 15         43 my @neigh = $g->successors($n);
2826 15         61 my $c = Set::Object->new;
2827 15         33 for my $u (@neigh) {
2828 29   100     110 for my $v (grep +(!$c->contains("$u-$_") && $g->has_edge($u, $_)), @neigh) {
2829 15         40 $gamma_v++;
2830 15         66 $c->insert("$u-$v");
2831 15         55 $c->insert("$v-$u");
2832             }
2833             }
2834 15 100       34 if (@neigh > 1) {
2835 9         39 $clustering{$n} = $gamma_v/(@neigh * (@neigh - 1) / 2);
2836 9         60 $gamma += $gamma_v/(@neigh * (@neigh - 1) / 2);
2837             } else {
2838 6         41 $clustering{$n} = 0;
2839             }
2840             }
2841              
2842 1         4 $gamma /= @v;
2843              
2844 1 50       28 return wantarray ? ($gamma, %clustering) : $gamma;
2845             }
2846              
2847             sub betweenness {
2848 1     1 1 3033 my $g = shift;
2849              
2850 1         8 my @V = $g->vertices();
2851              
2852 1         2 my %Cb; # C_b{w} = 0
2853              
2854 1         11 @Cb{@V} = ();
2855              
2856 1         3 for my $s (@V) {
2857 15         35 my @S; # stack (unshift, shift)
2858              
2859             my %P; # P{w} = empty list
2860 15         130 $P{$_} = [] for @V;
2861              
2862 15         22 my %sigma; # \sigma{t} = 0
2863 15         109 $sigma{$_} = 0 for @V;
2864 15         30 $sigma{$s} = 1;
2865              
2866 15         22 my %d; # d{t} = -1;
2867 15         103 $d{$_} = -1 for @V;
2868 15         44 $d{$s} = 0;
2869              
2870 15         25 my @Q; # queue (push, shift)
2871 15         32 push @Q, $s;
2872              
2873 15         30 while (@Q) {
2874 172         298 my $v = shift @Q;
2875 172         3864 unshift @S, $v;
2876 172         347 for my $w ($g->successors($v)) {
2877             # w found for first time
2878 341 100       1411 if ($d{$w} < 0) {
2879 157         258 push @Q, $w;
2880 157         253 $d{$w} = $d{$v} + 1;
2881             }
2882             # Shortest path to w via v
2883 341 100       865 if ($d{$w} == $d{$v} + 1) {
2884 173         268 $sigma{$w} += $sigma{$v};
2885 173         255 push @{ $P{$w} }, $v;
  173         541  
2886             }
2887             }
2888             }
2889              
2890 15         24 my %delta;
2891 15         159 $delta{$_} = 0 for @V;
2892              
2893 15         40 while (@S) {
2894 172         241 my $w = shift @S;
2895             $delta{$_} += $sigma{$_}/$sigma{$w} * (1 + $delta{$w})
2896 172         203 for @{ $P{$w} };
  172         3166  
2897 172 100       572 $Cb{$w} += $delta{$w} if $w ne $s;
2898             }
2899             }
2900              
2901 1         35 return %Cb;
2902             }
2903              
2904             sub connected_subgraphs {
2905 3     3 1 32 my $g = shift;
2906 3         15 require Set::Object;
2907 3         16 my @subgraphs = ( [ map { Set::Object->new($_) } $g->vertices ] );
  24         90  
2908 3         13 for (2..scalar $g->vertices) {
2909 21         41 my %seen;
2910 21         37 for my $subgraph (@{$subgraphs[-1]}) {
  21         56  
2911 1196         5794 for my $neighbour ((Set::Object->new( map { $g->neighbours($_) } $subgraph->members ) - $subgraph)->members) {
  9269         19418  
2912 4674         79067 my $new_subgraph = Set::Object->new($subgraph->members, $neighbour);
2913 4674         15926 my $key = join '|', @$new_subgraph;
2914 4674 100       685659 next if exists $seen{$key};
2915 1175         4831 $seen{$key} = $new_subgraph;
2916             }
2917             }
2918 21         816 push @subgraphs, [values %seen];
2919             }
2920 3         12 return map { $g->subgraph([$_->members]) } map { @$_ } @subgraphs;
  1199         12114  
  24         6183  
2921             }
2922              
2923             1;