File Coverage

blib/lib/Graph.pm
Criterion Covered Total %
statement 1569 1580 99.8
branch 624 722 86.4
condition 209 259 80.6
subroutine 307 307 100.0
pod 195 195 100.0
total 2904 3063 95.0


line stmt bran cond sub pod time code
1             package Graph;
2              
3 80     80   3459465 use strict;
  80         676  
  80         2487  
4 80     80   477 use warnings;
  80         171  
  80         3888  
5 80 50   80   7873 BEGIN { warnings->unimport('recursion') if $ENV{GRAPH_ALLOW_RECURSION} }
6              
7 20     20   132 sub __carp_confess { require Carp; Carp::confess(@_) }
  20         4049  
8             BEGIN {
9 80     80   2395 if (0) { # SET THIS TO ZERO FOR TESTING AND RELEASES!
10             $SIG{__DIE__ } = \&__carp_confess;
11             $SIG{__WARN__} = \&__carp_confess;
12             }
13             }
14              
15 80     80   38605 use Graph::AdjacencyMap qw(:flags :fields);
  80         228  
  80         42519  
16              
17             our $VERSION = '0.9727';
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 3     3   14 sub _empty_array () { $_empty_array }
27              
28             my $can_deep_copy_Storable;
29             sub _can_deep_copy_Storable () {
30 20 100   20   1153 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         18 eval {
33 4         2850 require Storable;
34 4         14061 require B::Deparse;
35 4         66 Storable->VERSION(2.05);
36 4         62 B::Deparse->VERSION(0.61);
37             };
38 4         55 $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 80 50   80   750 if ($] >= 5.022) {
52 80         4998 $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 111 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 80     80   34528 use Graph::Attribute array => _A, map => 'graph';
  80         202  
  80         4303  
83              
84             sub stringify {
85 625     625 1 40734 my ($u, $h) = (&is_undirected, &is_hyperedged);
86 625 100       1669 my $e = $u ? '=' : '-';
87             my @edges = map join($e,
88 3150         10758 $u ? sort { "$a" cmp "$b" } @$_ :
89 625 100       1253 $h ? map '['.join(",", sort { "$a" cmp "$b" } @$_).']', @$_ :
  8 100       31  
90             @$_), &_edges05;
91 625         4977 my @s = sort @edges;
92 625         1472 push @s, sort { "$a" cmp "$b" } &isolated_vertices;
  107         324  
93 625         9039 join(",", @s);
94             }
95              
96             sub eq {
97 298     298 1 107265 "$_[0]" eq "$_[1]"
98             }
99              
100             sub boolify {
101 276     276 1 27554 1; # Important for empty graphs: they stringify to "", which is false.
102             }
103              
104             sub ne {
105 11     11 1 3817 "$_[0]" ne "$_[1]"
106             }
107              
108             use overload
109 80         578 '""' => \&stringify,
110             'bool' => \&boolify,
111             'eq' => \&eq,
112 80     80   97694 'ne' => \≠
  80         84196  
113              
114             sub _opt {
115 2691     2691   7754 my ($opt, $flags, %flags) = @_;
116 2691         7418 while (my ($flag, $FLAG) = each %flags) {
117 8073 100       14277 $$flags |= $FLAG if delete $opt->{$flag};
118 8073 50       26334 $$flags &= ~$FLAG if delete $opt->{"non$flag"};
119             }
120             }
121              
122             sub _opt_get {
123 6     6   14 my ($opt, $key, $var) = @_;
124 6 100       17 return if !exists $opt->{$key};
125 1         9 $$var = delete $opt->{$key};
126             }
127              
128             sub _opt_unknown {
129 1228     1228   2093 my ($opt) = @_;
130 1228 100       3730 return unless my @opt = keys %$opt;
131 6 100       14 __carp_confess sprintf
132 6         52 "@{[(caller(1))[3]]}: Unknown option%s: @{[map qq['$_'], sort @opt]}",
  6         88  
133             @opt > 1 ? 's' : '';
134             }
135              
136             sub _opt_from_existing {
137 106     106   253 my ($g) = @_;
138 106         169 my %existing;
139 106         417 $existing{$_}++ for grep $g->$_, @GRAPH_PROPS_COPIED;
140 106 50       271 $existing{unionfind}++ if $g->has_union_find;
141 106         419 %existing;
142             }
143              
144             sub _opt_to_vflags {
145 897     897   1947 my ($vflags, $opt) = (0, @_);
146 897         2833 _opt($opt, \$vflags,
147             countvertexed => _COUNT,
148             multivertexed => _MULTI,
149             refvertexed => _REF,
150             refvertexed_stringified => _REFSTR ,
151             __stringified => _STR,
152             );
153 897         1852 $vflags;
154             }
155              
156             sub _opt_to_eflags {
157 897     897   1691 my ($eflags, $opt) = (0, @_);
158 897 100       2371 $opt->{undirected} = !delete $opt->{directed} if exists $opt->{directed};
159 897         2415 _opt($opt, \$eflags,
160             countedged => _COUNT,
161             multiedged => _MULTI,
162             undirected => _UNORD,
163             );
164 897         2411 ($eflags, delete $opt->{hyperedged});
165             }
166              
167             sub new {
168 897     897 1 206464 my ($class, @args) = @_;
169 897         1496 my $gflags = 0;
170 897         2443 my %opt = _get_options( \@args );
171              
172 897 100 66     3142 %opt = (_opt_from_existing($class), %opt) # allow overrides
173             if ref $class && $class->isa('Graph');
174              
175 897         2243 my $vflags = _opt_to_vflags(\%opt);
176 897         2186 my ($eflags, $is_hyper) = _opt_to_eflags(\%opt);
177              
178 897         2538 _opt(\%opt, \$gflags,
179             unionfind => _UNIONFIND,
180             );
181              
182 897         1485 my @V;
183 897 100       2107 if ($opt{vertices}) {
184             __carp_confess "Graph: vertices should be an array ref"
185 95 50       325 if ref $opt{vertices} ne 'ARRAY';
186 95         155 @V = @{ delete $opt{vertices} };
  95         297  
187             }
188              
189 897         1250 my @E;
190 897 100       1862 if ($opt{edges}) {
191             __carp_confess "Graph: edges should be an array ref of array refs"
192 23 50       83 if ref $opt{edges} ne 'ARRAY';
193 23         35 @E = @{ delete $opt{edges} };
  23         53  
194             }
195              
196 897         2325 _opt_unknown(\%opt);
197              
198 894 100 100     2469 __carp_confess "Graph: both countvertexed and multivertexed"
199             if ($vflags & _COUNT) && ($vflags & _MULTI);
200              
201 893 100 100     2198 __carp_confess "Graph: both countedged and multiedged"
202             if ($eflags & _COUNT) && ($eflags & _MULTI);
203              
204 892   66     3568 my $g = bless [ ], ref $class || $class;
205              
206 892         6141 $g->[ _F ] = $gflags;
207 892         1399 $g->[ _G ] = 0;
208 892         1983 $g->[ _V ] = _make_v($vflags);
209 892         1965 $g->[ _E ] = _make_e($is_hyper, $eflags);
210              
211 892 100       2467 $g->add_vertices(@V) if @V;
212              
213 892 50       2786 __carp_confess "Graph: edges should be array refs"
214             if grep ref $_ ne 'ARRAY', @E;
215 892         2724 $g->add_edges(@E);
216              
217 892 100       1910 $g->[ _U ] = do { require Graph::UnionFind; Graph::UnionFind->new }
  5         2049  
  5         37  
218             if $gflags & _UNIONFIND;
219              
220 892         4326 return $g;
221             }
222              
223             sub _make_v {
224 892     892   1518 my ($vflags) = @_;
225 892 100       2535 $vflags ? _am_heavy($vflags, 1) : _am_light($vflags, 1);
226             }
227              
228             sub _make_e {
229 892     892   1752 my ($is_hyper, $eflags) = @_;
230 892 100 100     3973 ($is_hyper or $eflags & ~_UNORD) ?
    100          
231             _am_heavy($eflags, $is_hyper ? 0 : 2) :
232             _am_light($eflags, 2);
233             }
234              
235             sub _am_light {
236 1621     1621   45361 require Graph::AdjacencyMap::Light;
237 1621         5236 Graph::AdjacencyMap::Light->_new(@_);
238             }
239              
240             sub _am_heavy {
241 163     163   550 Graph::AdjacencyMap->_new(@_);
242             }
243              
244 1294     1294 1 8514 sub countvertexed { $_[0]->[ _V ]->_is_COUNT }
245 5307     5307 1 14860 sub multivertexed { $_[0]->[ _V ]->_is_MULTI }
246 146     146 1 612 sub refvertexed { $_[0]->[ _V ]->_is_REF }
247 1     1 1 8 sub refvertexed_stringified { $_[0]->[ _V ]->_is_REFSTR }
248 140     140   420 sub __stringified { $_[0]->[ _V ]->_is_STR }
249              
250 575     575 1 5941 sub countedged { $_[0]->[ _E ]->_is_COUNT }
251 38000     38000 1 98889 sub multiedged { $_[0]->[ _E ]->_is_MULTI }
252 79221     79221 1 186024 sub hyperedged { !$_[0]->[ _E ]->[ _arity ] }
253 47459     47459 1 106693 sub undirected { $_[0]->[ _E ]->_is_UNORD }
254              
255 22130     22130 1 422427 sub directed { ! $_[0]->[ _E ]->_is_UNORD }
256              
257             *is_directed = \&directed;
258             *is_undirected = \&undirected;
259              
260             *is_countvertexed = \&countvertexed;
261             *is_multivertexed = \&multivertexed;
262             *is_refvertexed = \&refvertexed;
263             *is_refvertexed_stringified = \&refvertexed_stringified;
264              
265             *is_countedged = \&countedged;
266             *is_multiedged = \&multiedged;
267             *is_hyperedged = \&hyperedged;
268              
269 20974     20974 1 34809 sub has_union_find { $_[0]->[ _U ] }
270              
271             sub add_vertex {
272 2101 100   2101 1 23095 __carp_confess "Graph::add_vertex: use add_vertices for more than one vertex" if @_ != 2;
273 2096 100       5785 __carp_confess "Graph::add_vertex: undef vertex" if grep !defined, @_;
274 2095         4462 goto &add_vertices;
275             }
276              
277             sub has_vertex {
278 2659     2659 1 40523 my $g = $_[0];
279 2659         3730 my $V = $g->[ _V ];
280 2659 100       6122 return defined $V->has_path($_[1]) if ($V->[ _f ] & _REF);
281 2125         6240 exists $V->[ _pi ]->{ $_[1] };
282             }
283              
284             sub _vertices05 {
285 2249     2249   3197 my $g = $_[0];
286 2249         6247 $g->[ _V ]->paths;
287             }
288              
289             sub vertices {
290 1153     1153 1 38356 my $g = $_[0];
291 1153         1993 my @v = &_vertices05;
292 1153 100 100     2315 return @v if !(&is_multivertexed || &is_countvertexed);
293 14 100       42 return map +(($_) x $g->get_vertex_count($_)), @v if wantarray;
294 12         15 my $V = 0;
295 12         40 $V += $g->get_vertex_count($_) for @v;
296 12         55 return $V;
297             }
298              
299             *unique_vertices = \&_vertices05;
300              
301             sub has_vertices {
302 22     22 1 3859 my $g = shift;
303 22         86 scalar $g->[ _V ]->has_any_paths;
304             }
305              
306             sub add_edge {
307 15555 100   15555 1 67346 &expect_hyperedged, &expect_undirected if @_ != 3;
308 15553         46957 $_[0]->add_edges([ @_[1..$#_] ]);
309             }
310              
311             sub _vertex_ids_ensure {
312 69     69   127 push @_, 1;
313 69         149 goto &_vertex_ids_maybe_ensure;
314             }
315              
316             sub _vertex_ids_ensure_multi {
317 56     56   103 my $id = pop;
318 56         101 my @i = &_vertex_ids_ensure;
319 56         120 push @_, $id;
320 56 50       184 @i ? (@i, $id) : ();
321             }
322              
323             sub _vertex_ids {
324 38120     38120   53968 push @_, 0;
325 38120         61343 goto &_vertex_ids_maybe_ensure;
326             }
327              
328             sub _vertex_ids_multi {
329 285     285   565 my $id = pop;
330 285         473 my @i = &_vertex_ids;
331 285         510 push @_, $id;
332 285 100       830 @i ? (@i, $id) : ();
333             }
334              
335             sub _vertex_ids_maybe_ensure {
336 38189     38189   49691 my $ensure = pop;
337 38189         74722 my ($g, @args) = @_;
338 38189 50       96722 __carp_confess "Graph: given undefined vertex" if grep !defined, @args;
339 38189         54209 my $V = $g->[ _V ];
340 38189   100     53969 my $deep = &is_hyperedged && &is_directed;
341 38189 100 100     114027 return $V->get_ids_by_paths(\@args, $ensure, $deep) if ($V->[ _f ] & _REF) or $deep;
342 37845         47676 my $pi = $V->[ _pi ];
343 37845         84578 my @non_exist = grep !exists $pi->{ $_ }, @args;
344 37845 100 100     107136 return if !$ensure and @non_exist;
345 36543 100       61590 $V->get_ids_by_paths(\@non_exist, 1) if @non_exist;
346 36543         105233 @$pi{ @args };
347             }
348              
349             sub has_edge {
350 19981     19981 1 91573 my $g = $_[0];
351 19981         26977 my $E = $g->[ _E ];
352 19981         33651 my ($Ef, $Ea) = @$E[ _f, _arity ];
353 19981 100 100     66044 return 0 if $Ea and @_ != $Ea + 1;
354 19977         30871 my $directed = &is_directed;
355 19977   100     32973 my $deep = &is_hyperedged && $directed;
356 19977 100       31145 return 0 if (my @i = &_vertex_ids) != @_ - 1;
357 18557 50       33309 return defined $E->has_path($directed ? \@i : [ map [ sort @$_ ], @i ]) if $deep;
    100          
358 18546 100       52851 @i = sort @i if !$directed;
359 18546         81918 exists $E->[ _pi ]{ "@i" };
360             }
361              
362             sub any_edge {
363 22     22 1 1358 my ($g, @args) = @_;
364 22         39 my $E = $g->[ _E ];
365 22         33 my $V = $g->[ _V ];
366 22 100       95 return 0 if (my @i = $V->get_ids_by_paths(\@args)) != @args;
367 16         59 $E->has_successor(@i);
368             }
369              
370             sub _edges05 {
371 1236     1236   1836 my $g = $_[0];
372 1236         3499 my @e = $g->[ _E ]->paths;
373 1236 100       3114 return @e if !wantarray;
374 1181   100     2592 $g->[ _V ]->get_paths_by_ids(\@e, &is_hyperedged && &is_directed);
375             }
376              
377             *unique_edges = \&_edges05;
378              
379             sub edges {
380 367     367 1 3303 my $g = $_[0];
381 367         1089 my @e = &_edges05;
382 367 100 100     1466 return @e if !(&is_multiedged || &is_countedged);
383 28 100       143 return map +(($_) x $g->get_edge_count(@$_)), @e if wantarray;
384 14         25 my $E = 0;
385 14         46 $E += $g->get_edge_count(@$_) for @e;
386 14         70 return $E;
387             }
388              
389             sub has_edges {
390 7     7 1 857 scalar $_[0]->[ _E ]->has_any_paths;
391             }
392              
393             ###
394             # by_id
395             #
396              
397             sub add_vertex_by_id {
398 14     14 1 311 &expect_multivertexed;
399 13         28 my ($g, $v, $id) = @_;
400 13         22 my $V = $g->[ _V ];
401 13 100       42 return $g if $V->has_path_by_multi_id( my @args = ($v, $id) );
402 12         39 my ($i) = $V->set_path_by_multi_id( @args );
403 12 50       27 $g->[ _U ]->add($i) if &has_union_find;
404 12         22 $g->[ _G ]++;
405 12         36 return $g;
406             }
407              
408             sub add_vertex_get_id {
409 6     6 1 3707 &expect_multivertexed;
410 6         16 my ($g, $v) = @_;
411 6         17 my ($i, $multi_id) = $g->[ _V ]->set_path_by_multi_id( $v, _GEN_ID );
412 6 50       56 $g->[ _U ]->add($i) if &has_union_find;
413 6         38 $g->[ _G ]++;
414 6         21 return $multi_id;
415             }
416              
417             sub has_vertex_by_id {
418 100     100 1 3784 &expect_multivertexed;
419 99         235 my ($g, $v, $id) = @_;
420 99         289 $g->[ _V ]->has_path_by_multi_id( $v, $id );
421             }
422              
423             sub delete_vertex_by_id {
424 4     4 1 535 &expect_multivertexed;
425 3         8 &expect_non_unionfind;
426 3         9 my ($g, $v, $id) = @_;
427 3 100       5 return $g unless &has_vertex_by_id;
428             # TODO: what to about the edges at this vertex?
429             # If the multiness of this vertex goes to zero, delete the edges?
430 2         7 $g->[ _V ]->del_path_by_multi_id( $v, $id );
431 2         3 $g->[ _G ]++;
432 2         7 return $g;
433             }
434              
435             sub get_multivertex_ids {
436 14     14 1 2776 &expect_multivertexed;
437 13         23 my $g = shift;
438 13         41 $g->[ _V ]->get_multi_ids( @_ );
439             }
440              
441             sub add_edge_by_id {
442 57     57 1 159 &expect_multiedged;
443 56         98 my $g = $_[0];
444 56         110 my @i = &_vertex_ids_ensure_multi;
445 56         107 my $id = pop @i;
446 56 100       100 @i = sort @i if &is_undirected;
447 56         200 $g->[ _E ]->set_path_by_multi_id( \@i, $id );
448 56         101 $g->[ _G ]++;
449 56 100       118 $g->[ _U ]->union(\@i) if &has_union_find;
450 56         134 return $g;
451             }
452              
453             sub add_edge_get_id {
454 13     13 1 2914 &expect_multiedged;
455 13         24 my $g = $_[0];
456 13         27 my @i = &_vertex_ids_ensure;
457 13 100       26 @i = sort @i if &is_undirected;
458 13         39 my (undef, $id) = $g->[ _E ]->set_path_by_multi_id( \@i, _GEN_ID );
459 13         25 $g->[ _G ]++;
460 13 50       24 $g->[ _U ]->union(\@i) if &has_union_find;
461 13         44 return $id;
462             }
463              
464             sub has_edge_by_id {
465 153     153 1 1573 &expect_multiedged;
466 152         234 my $g = $_[0];
467 152         270 my @i = &_vertex_ids_multi;
468 152 100       383 return 0 if @i < @_ - 2;
469 134         184 my $id = pop @i;
470 134 100       219 @i = sort @i if &is_undirected;
471 134         401 $g->[ _E ]->has_path_by_multi_id( \@i, $id );
472             }
473              
474             sub delete_edge_by_id {
475 5     5 1 566 &expect_multiedged;
476 4         12 &expect_non_unionfind;
477 4         7 my $g = $_[0];
478 4         7 my $E = $g->[ _E ];
479 4         7 my @i = &_vertex_ids_multi;
480 4 50       17 return if @i < @_ - 2;
481 4         7 my $id = pop @i;
482 4 50       8 @i = sort @i if &is_undirected;
483 4 100       16 return unless $E->has_path_by_multi_id( my @args = (\@i, $id) );
484 3         22 $E->del_path_by_multi_id( @args );
485 3         7 $g->[ _G ]++;
486 3         20 return $g;
487             }
488              
489             sub get_multiedge_ids {
490 35     35 1 3050 &expect_multiedged;
491 34 50       92 return unless @_-1 == (my @i = &_vertex_ids);
492 34         122 $_[0]->[ _E ]->get_multi_ids( \@i );
493             }
494              
495             ###
496             # Neighbourhood.
497             #
498              
499             sub _edges_at {
500 189 100   189   434 goto &_edges_from if &is_undirected;
501 110         4227 require Set::Object;
502 110 100       47550 Set::Object->new(&_edges_from, &_edges_to)->${ wantarray ? \'members' : \'size' };
  110         635  
503             }
504              
505             sub _edges_from {
506 476     476   1074 my ($g, @args) = @_;
507 476         907 my ($V, $E) = @$g[ _V, _E ];
508 476 100 100     920 return if (my @i = $V->get_ids_by_paths(\@args, &is_hyperedged && &is_directed)) != @args;
509 472         1384 $E->paths_from(@i);
510             }
511              
512             sub _edges_to {
513 326 50   326   572 goto &_edges_from if &is_undirected;
514 326         716 my ($g, @args) = @_;
515 326         640 my ($V, $E) = @$g[ _V, _E ];
516 326 100 66     593 return if (my @i = $V->get_ids_by_paths(\@args, &is_hyperedged && &is_directed)) != @args;
517 323         913 $E->paths_to(@i);
518             }
519              
520             sub edges_at {
521 12 100   12 1 7252 goto &_edges_at if !wantarray;
522 11   100     29 $_[0]->[ _V ]->get_paths_by_ids([ &_edges_at ], &is_hyperedged && &is_directed);
523             }
524              
525             sub edges_from {
526 287 50   287 1 6859 goto &_edges_from if !wantarray;
527 287   66     543 $_[0]->[ _V ]->get_paths_by_ids([ &_edges_from ], &is_hyperedged && &is_directed);
528             }
529              
530             sub edges_to {
531 246 100   246 1 5776 goto &edges_from if &is_undirected;
532 216 50       428 goto &_edges_to if !wantarray;
533 216   66     368 $_[0]->[ _V ]->get_paths_by_ids([ &_edges_to ], &is_hyperedged && &is_directed);
534             }
535              
536             sub successors {
537 8090     8090 1 21151 my ($g, @args) = @_;
538 8090         16706 my ($V, $E) = @$g[ _V, _E ];
539 8090 100       19668 return if (my @i = $V->get_ids_by_paths(\@args)) != @args;
540 8084         19849 my @v = $E->successors(@i);
541 8084 100       31490 return @v if !wantarray;
542 3989         12656 map @$_, $V->get_paths_by_ids([ \@v ]);
543             }
544              
545             sub predecessors {
546 5063 100   5063 1 12553 goto &successors if &is_undirected;
547 1604         3349 my ($g, @args) = @_;
548 1604         2849 my ($V, $E) = @$g[ _V, _E ];
549 1604 100       3642 return if (my @i = $V->get_ids_by_paths(\@args)) != @args;
550 1600         3963 my @v = $E->predecessors(@i);
551 1600 100       8070 return @v if !wantarray;
552 197         570 map @$_, $V->get_paths_by_ids([ \@v ]);
553             }
554              
555             sub _cessors_by_radius {
556 166     166   410 my ($radius, $method, $self_only_if_loop) = splice @_, -3, 3;
557 166         344 my ($g, @v) = @_;
558 166         714 require Set::Object;
559 166         1226 my ($init, $next) = map Set::Object->new(@v), 1..2;
560 166 100       524 my $self = Set::Object->new(grep $g->has_edge($_, $_), @v) if $self_only_if_loop;
561 166         621 my ($got, $found) = map Set::Object->new, 1..2;
562 166   100     563 while (!defined $radius or $radius-- > 0) {
563 335         1182 $found->insert($g->$method($next->members));
564 335         933 $next = $found->difference($got);
565 335 100       9409 last if $next->is_null; # Leave if no new found.
566 211         745 $got->insert($next->members);
567 211         762 $found->clear;
568             }
569 166 100       399 $got->remove($init->difference($self)->members) if $self_only_if_loop;
570 166 100       2174 $got->${ wantarray ? \'members' : \'size' };
  166         1900  
571             }
572              
573             sub all_successors {
574 37     37 1 7134 &expect_directed;
575 37         90 push @_, undef, 'successors', 0;
576 37         98 goto &_cessors_by_radius;
577             }
578              
579             sub successors_by_radius {
580 9     9 1 22 &expect_directed;
581 9         27 push @_, 'successors', 0;
582 9         16 goto &_cessors_by_radius;
583             }
584              
585             sub all_predecessors {
586 18     18 1 7031 &expect_directed;
587 18         47 push @_, undef, 'predecessors', 0;
588 18         51 goto &_cessors_by_radius;
589             }
590              
591             sub predecessors_by_radius {
592 22     22 1 10203 &expect_directed;
593 22         53 push @_, 'predecessors', 0;
594 22         63 goto &_cessors_by_radius;
595             }
596              
597             sub neighbours_by_radius {
598 26     26 1 8084 push @_, 'neighbours', 1;
599 26         77 goto &_cessors_by_radius;
600             }
601             *neighbors_by_radius = \&neighbours_by_radius;
602              
603             sub neighbours {
604 219     219 1 10496 require Set::Object;
605 219         411 my $s = Set::Object->new(&successors);
606 219 100       626 $s->insert(&predecessors) if &is_directed;
607 219 100       379 $s->${ wantarray ? \'members' : \'size' };
  219         1408  
608             }
609             *neighbors = \&neighbours;
610              
611             sub all_neighbours {
612 54     54 1 14438 push @_, undef, 'neighbours', 1;
613 54         148 goto &_cessors_by_radius;
614             }
615             *all_neighbors = \&all_neighbours;
616              
617             sub all_reachable {
618 36 100   36 1 13915 &directed ? goto &all_successors : goto &all_neighbors;
619             }
620              
621             sub reachable_by_radius {
622 17 100   17 1 30 &directed ? goto &successors_by_radius : goto &neighbors_by_radius;
623             }
624              
625             sub delete_edge {
626 468     468 1 3485 &expect_non_unionfind;
627 467         629 my $g = $_[0];
628 467 100       660 return $g if (my @i = &_vertex_ids) != @_ - 1;
629 462 100       748 @i = sort @i if &is_undirected;
630 462 100 100     1615 return $g unless @i and $g->[ _E ]->del_path( \@i );
631 453         789 $g->[ _G ]++;
632 453         772 return $g;
633             }
634              
635             sub delete_vertex {
636 184     184 1 36194 &expect_non_unionfind;
637 184         290 my $g = $_[0];
638 184 100       419 return $g if @_ != 2;
639 183         268 my $V = $g->[ _V ];
640 183 100       615 return $g unless defined $V->has_path($_[1]);
641             # TODO: _edges_at is excruciatingly slow (rt.cpan.org 92427)
642 177         385 my $E = $g->[ _E ];
643 177         346 $E->del_path( $_ ) for &_edges_at;
644 177         792 $V->del_path($_[1]);
645 177         270 $g->[ _G ]++;
646 177         625 return $g;
647             }
648              
649             sub get_vertex_count {
650 63     63 1 11207 my $g = shift;
651 63         230 $g->[ _V ]->_get_path_count( @_ );
652             }
653              
654             sub get_edge_count {
655 975     975 1 10101 my $g = $_[0];
656 975 100       1479 return 0 if (my @i = &_vertex_ids) != @_ - 1;
657 959 100       1585 @i = sort @i if &is_undirected;
658 959         2457 $g->[ _E ]->_get_path_count( \@i );
659             }
660              
661             sub delete_vertices {
662 6     6 1 18 &expect_non_unionfind;
663 6         11 my $g = shift;
664 6         13 while (@_) {
665 8         17 my $v = shift @_;
666 8         17 $g->delete_vertex($v);
667             }
668 6         12 return $g;
669             }
670              
671             sub delete_edges {
672 6     6 1 446 &expect_non_unionfind;
673 6         11 my $g = shift;
674 6         15 while (@_) {
675 8         24 my ($u, $v) = splice @_, 0, 2;
676 8         17 $g->delete_edge($u, $v);
677             }
678 6         15 return $g;
679             }
680              
681             ###
682             # Degrees.
683             #
684              
685             sub in_degree {
686 232     232 1 330 my $g = $_[0];
687 232 50 33     593 return undef unless @_ > 1 && &has_vertex;
688 232         355 my $in = 0;
689 232         383 $in += $g->get_edge_count( @$_ ) for &edges_to;
690 232 100 100     460 $in++ if &is_undirected and &is_self_loop_vertex;
691 232         704 return $in;
692             }
693              
694             sub out_degree {
695 208     208 1 311 my $g = $_[0];
696 208 50 33     488 return undef unless @_ > 1 && &has_vertex;
697 208         308 my $out = 0;
698 208         304 $out += $g->get_edge_count( @$_ ) for &edges_from;
699 208 100 100     400 $out++ if &is_undirected and &is_self_loop_vertex;
700 208         741 return $out;
701             }
702              
703             sub _total_degree {
704 42 50 33 42   178 return undef unless @_ > 1 && &has_vertex;
705 42 100       87 &is_undirected ? &in_degree : &in_degree - &out_degree;
706             }
707              
708             sub degree {
709 38 100   38 1 149 goto &_total_degree if @_ > 1;
710 2 100       6 return 0 if &is_directed;
711 1         5 my $g = $_[0];
712 1         2 my $total = 0;
713 1         4 $total += $g->_total_degree( $_ ) for &_vertices05;
714 1         8 return $total;
715             }
716              
717             *vertex_degree = \°ree;
718              
719             sub is_sink_vertex {
720 36 50   36 1 76 return 0 unless @_ > 1;
721 36 100       52 &successors == 0 && &predecessors > 0;
722             }
723              
724             sub is_source_vertex {
725 36 50   36 1 74 return 0 unless @_ > 1;
726 36 100       61 &predecessors == 0 && &successors > 0;
727             }
728              
729             sub is_successorless_vertex {
730 36 50   36 1 7885 return 0 unless @_ > 1;
731 36         69 &successors == 0;
732             }
733              
734             sub is_predecessorless_vertex {
735 36 50   36 1 8022 return 0 unless @_ > 1;
736 36         70 &predecessors == 0;
737             }
738              
739             sub is_successorful_vertex {
740 36 50   36 1 7904 return 0 unless @_ > 1;
741 36         66 &successors > 0;
742             }
743              
744             sub is_predecessorful_vertex {
745 36 50   36 1 7968 return 0 unless @_ > 1;
746 36         91 &predecessors > 0;
747             }
748              
749             sub is_isolated_vertex {
750 4694 50   4694 1 10005 return 0 unless @_ > 1;
751 4694 100       7051 &predecessors == 0 && &successors == 0;
752             }
753              
754             sub is_interior_vertex {
755 36 50   36 1 75 return 0 unless @_ > 1;
756 36         50 my $s = &successors;
757 36 100       56 $s-- if my $isl = &is_self_loop_vertex;
758 36 100       114 return 0 if $s == 0;
759 23 100       35 return $s > 0 if &is_undirected;
760 8         14 my $p = &predecessors;
761 8 100       17 $p-- if $isl;
762 8         26 $p > 0;
763             }
764              
765             sub is_exterior_vertex {
766 36 50   36 1 78 return 0 unless @_ > 1;
767 36 100       47 &predecessors == 0 || &successors == 0;
768             }
769              
770             sub is_self_loop_vertex {
771 108 50   108 1 225 return 0 unless @_ > 1;
772 108 100       162 return 1 if grep $_ eq $_[1], &successors; # @todo: multiedges
773 86         279 return 0;
774             }
775              
776             for my $p (qw(
777             is_sink_vertex
778             is_source_vertex
779             is_successorless_vertex
780             is_predecessorless_vertex
781             is_successorful_vertex
782             is_predecessorful_vertex
783             is_isolated_vertex
784             is_interior_vertex
785             is_exterior_vertex
786             is_self_loop_vertex
787             )) {
788 80     80   473161 no strict 'refs';
  80         261  
  80         60226  
789             (my $m = $p) =~ s/^is_(.*)ex$/${1}ices/;
790 710     710   12405 *$m = sub { my $g = $_[0]; grep $g->$p($_), &_vertices05 };
  710         1370  
791             }
792              
793             ###
794             # Paths and cycles.
795             #
796              
797             sub add_path {
798 126     126 1 585 my $g = shift;
799 126         196 my $u = shift;
800 126         176 my @edges;
801 126         293 while (@_) {
802 317         416 my $v = shift;
803 317         553 push @edges, [ $u, $v ];
804 317         593 $u = $v;
805             }
806 126         339 $g->add_edges(@edges);
807 126         412 return $g;
808             }
809              
810             sub delete_path {
811 4     4 1 33 &expect_non_unionfind;
812 4         9 my $g = shift;
813 4         7 my $u = shift;
814 4         11 while (@_) {
815 10         15 my $v = shift;
816 10         28 $g->delete_edge($u, $v);
817 10         26 $u = $v;
818             }
819 4         10 return $g;
820             }
821              
822             sub has_path {
823 20     20 1 822 my $g = shift;
824 20         35 my $u = shift;
825 20         43 while (@_) {
826 43         114 my $v = shift;
827 43 100       93 return 0 unless $g->has_edge($u, $v);
828 30         72 $u = $v;
829             }
830 7         48 return $g;
831             }
832              
833             sub add_cycle {
834 39     39 1 287 push @_, $_[1];
835 39         105 goto &add_path;
836             }
837              
838             sub delete_cycle {
839 2     2 1 7 &expect_non_unionfind;
840 2         15 push @_, $_[1];
841 2         7 goto &delete_path;
842             }
843              
844             sub has_cycle {
845 9 100   9 1 939 return 0 if @_ == 1;
846 8         23 push @_, $_[1];
847 8         26 goto &has_path;
848             }
849              
850             *has_this_cycle = \&has_cycle;
851              
852             sub has_a_cycle {
853 17     17 1 617 my $g = shift;
854 17         939 require Graph::Traversal::DFS;
855 17         76 my $t = Graph::Traversal::DFS->new($g, has_a_cycle => 1, @_);
856 17         50 $t->dfs;
857 17         66 return $t->get_state('has_a_cycle');
858             }
859              
860             sub find_a_cycle {
861 2     2 1 23 require Graph::Traversal::DFS;
862 2         10 my @r = ( back_edge => \&Graph::Traversal::find_a_cycle);
863 2 100       8 push @r,
864             down_edge => \&Graph::Traversal::find_a_cycle
865             if &is_undirected;
866 2         5 my $g = shift;
867 2         12 my $t = Graph::Traversal::DFS->new($g, @r, @_);
868 2         17 $t->dfs;
869 2 50       29 $t->has_state('a_cycle') ? @{ $t->get_state('a_cycle') } : ();
  2         42  
870             }
871              
872             ###
873             # Attributes.
874              
875             my @generic_methods = (
876             [ 'set_attribute', \&_set_attribute ],
877             [ 'set_attributes', \&_set_attributes ],
878             [ 'has_attributes', \&_has_attributes ],
879             [ 'has_attribute', \&_has_attribute ],
880             [ 'get_attributes', \&_get_attributes ],
881             [ 'get_attribute', \&_get_attribute ],
882             [ 'get_attribute_names', \&_get_attribute_names ],
883             [ 'get_attribute_values', \&_get_attribute_values ],
884             [ 'delete_attributes', \&_delete_attributes ],
885             [ 'delete_attribute', \&_delete_attribute ],
886             );
887             my %entity2offset = (vertex => _V, edge => _E);
888             my %entity2args = (edge => '_vertex_ids');
889             for my $entity (qw(vertex edge)) {
890 80     80   794 no strict 'refs';
  80         256  
  80         36637  
891             my $expect_non = \&{ "expect_non_multi${entity}" };
892             my $expect_yes = \&{ "expect_multi${entity}" };
893             my $args_non = \&{ $entity2args{$entity} } if $entity2args{$entity};
894             my $args_yes = \&{ $entity2args{$entity}.'_multi' } if $entity2args{$entity};
895             my $offset = $entity2offset{$entity};
896             for my $t (@generic_methods) {
897             my ($raw, $func) = @$t;
898             my ($first, $rest) = ($raw =~ /^(\w+?)_(.+)/);
899             my $m = join '_', $first, $entity, $rest;
900             my $is_vertex = $entity eq 'vertex';
901             *$m = sub {
902 17882     17882   62391 &$expect_non; push @_, 0, $entity, $offset, $args_non, $is_vertex; goto &$func;
  17880         46256  
  17880         44201  
903             };
904             *{$m.'_by_id'} = sub {
905 206     206   4702 &$expect_yes; push @_, 1, $entity, $offset, $args_yes, $is_vertex; goto &$func;
  206         610  
  206         610  
906             };
907             }
908             }
909              
910             sub _munge_args {
911 18042     18042   36233 my ($is_vertex, $is_multi, $is_undirected, @args) = @_;
912 18042 100 100     54833 return \@args if !$is_vertex and !$is_undirected and !$is_multi;
      100        
913 15927 100 100     80845 return [ sort @args ] if !$is_vertex and !$is_multi;
914 1674 100       4186 return @args if $is_vertex;
915 129         169 my $id = pop @args;
916 129 100       418 ($is_undirected ? [ sort @args ] : \@args, $id);
917             }
918              
919             sub _set_attribute {
920 4695     4695   11247 my ($is_multi, $entity, $offset, $args, $is_vertex) = splice @_, -5, 5;
921 4695         7347 my $value = pop;
922 4695         6245 my $attr = pop;
923 80     80   674 no strict 'refs';
  80         182  
  80         16214  
924 4695 100       6418 &{ 'add_' . $entity . ($is_multi ? '_by_id' : '') } unless &{ 'has_' . $entity . ($is_multi ? '_by_id' : '') };
  4628 100       15101  
  4695 100       15319  
925 4695 100       13962 my @args = ($entity eq 'edge') ? &$args : @_[1..$#_];
926 4695         8663 @args = _munge_args($is_vertex, $is_multi, &is_undirected, @args);
927 4695         13436 $_[0]->[ $offset ]->_set_path_attr( @args, $attr, $value );
928             }
929              
930             sub _set_attributes {
931 1114     1114   2824 my ($is_multi, $entity, $offset, $args, $is_vertex) = splice @_, -5, 5;
932 1114         1814 my $attr = pop;
933 80     80   632 no strict 'refs';
  80         219  
  80         14987  
934 1114 100       1437 &{ 'add_' . $entity . ($is_multi ? '_by_id' : '') } unless &{ 'has_' . $entity . ($is_multi ? '_by_id' : '') };
  1025 100       3080  
  1114 100       3481  
935 1114 100       3392 my @args = ($entity eq 'edge') ? &$args : @_[1..$#_];
936 1114         1935 @args = _munge_args($is_vertex, $is_multi, &is_undirected, @args);
937 1114         2863 $_[0]->[ $offset ]->_set_path_attrs( @args, $attr );
938             }
939              
940             sub _has_attributes {
941 40     40   121 my ($is_multi, $entity, $offset, $args, $is_vertex) = splice @_, -5, 5;
942 80     80   586 no strict 'refs';
  80         175  
  80         14010  
943 40 100       61 return 0 unless &{ 'has_' . $entity . ($is_multi ? '_by_id' : '') };
  40 50       168  
944 40 100       147 my @args = ($entity eq 'edge') ? &$args : @_[1..$#_];
945 40         76 @args = _munge_args($is_vertex, $is_multi, &is_undirected, @args);
946 40         120 $_[0]->[ $offset ]->_has_path_attrs( @args );
947             }
948              
949             sub _has_attribute {
950 24     24   83 my ($is_multi, $entity, $offset, $args, $is_vertex) = splice @_, -5, 5;
951 24         56 my $attr = pop;
952 80     80   637 no strict 'refs';
  80         207  
  80         12695  
953 24 100       38 return 0 unless &{ 'has_' . $entity . ($is_multi ? '_by_id' : '') };
  24 100       106  
954 20 100       89 my @args = ($entity eq 'edge') ? &$args : @_[1..$#_];
955 20         123 @args = _munge_args($is_vertex, $is_multi, &is_undirected, @args);
956 20         91 $_[0]->[ $offset ]->_has_path_attr( @args, $attr );
957             }
958              
959             sub _get_attributes {
960 631     631   1514 my ($is_multi, $entity, $offset, $args, $is_vertex) = splice @_, -5, 5;
961 80     80   629 no strict 'refs';
  80         204  
  80         13672  
962 631 100       898 return undef unless &{ 'has_' . $entity . ($is_multi ? '_by_id' : '') };
  631 100       1921  
963 629 100       1619 my @args = ($entity eq 'edge') ? &$args : @_[1..$#_];
964 629         1177 @args = _munge_args($is_vertex, $is_multi, &is_undirected, @args);
965 629         1681 scalar $_[0]->[ $offset ]->_get_path_attrs( @args );
966             }
967              
968             sub _get_attribute {
969 11538     11538   27611 my ($is_multi, $entity, $offset, $args, $is_vertex) = splice @_, -5, 5;
970 80     80   663 no strict 'refs';
  80         236  
  80         14756  
971 11538         18269 my $attr = pop;
972 11538 100       14041 return undef unless &{ 'has_' . $entity . ($is_multi ? '_by_id' : '') };
  11538 100       33677  
973 11500 100       27302 my @args = ($entity eq 'edge') ? &$args : @_[1..$#_];
974 11500         18951 @args = _munge_args($is_vertex, $is_multi, &is_undirected, @args);
975 11500         30678 scalar $_[0]->[ $offset ]->_get_path_attr( @args, $attr );
976             }
977              
978             sub _get_attribute_names {
979 12     12   46 my ($is_multi, $entity, $offset, $args, $is_vertex) = splice @_, -5, 5;
980 80     80   643 no strict 'refs';
  80         221  
  80         12485  
981 12 100       25 return unless &{ 'has_' . $entity . ($is_multi ? '_by_id' : '') };
  12 50       51  
982 12 100       57 my @args = ($entity eq 'edge') ? &$args : @_[1..$#_];
983 12         33 @args = _munge_args($is_vertex, $is_multi, &is_undirected, @args);
984 12         42 $_[0]->[ $offset ]->_get_path_attr_names( @args );
985             }
986              
987             sub _get_attribute_values {
988 12     12   42 my ($is_multi, $entity, $offset, $args, $is_vertex) = splice @_, -5, 5;
989 80     80   639 no strict 'refs';
  80         224  
  80         12971  
990 12 100       22 return unless &{ 'has_' . $entity . ($is_multi ? '_by_id' : '') };
  12 50       52  
991 12 100       53 my @args = ($entity eq 'edge') ? &$args : @_[1..$#_];
992 12         35 @args = _munge_args($is_vertex, $is_multi, &is_undirected, @args);
993 12         57 $_[0]->[ $offset ]->_get_path_attr_values( @args );
994             }
995              
996             sub _delete_attributes {
997 8     8   44 my ($is_multi, $entity, $offset, $args, $is_vertex) = splice @_, -5, 5;
998 80     80   627 no strict 'refs';
  80         191  
  80         13345  
999 8 100       19 return undef unless &{ 'has_' . $entity . ($is_multi ? '_by_id' : '') };
  8 50       43  
1000 8 100       54 my @args = ($entity eq 'edge') ? &$args : @_[1..$#_];
1001 8         24 @args = _munge_args($is_vertex, $is_multi, &is_undirected, @args);
1002 8         29 $_[0]->[ $offset ]->_del_path_attrs( @args );
1003             }
1004              
1005             sub _delete_attribute {
1006 12     12   60 my ($is_multi, $entity, $offset, $args, $is_vertex) = splice @_, -5, 5;
1007 12         27 my $attr = pop;
1008 80     80   680 no strict 'refs';
  80         178  
  80         90283  
1009 12 100       20 return undef unless &{ 'has_' . $entity . ($is_multi ? '_by_id' : '') };
  12 50       65  
1010 12 100       55 my @args = ($entity eq 'edge') ? &$args : @_[1..$#_];
1011 12         29 @args = _munge_args($is_vertex, $is_multi, &is_undirected, @args);
1012 12         53 $_[0]->[ $offset ]->_del_path_attr( @args, $attr );
1013             }
1014              
1015             sub add_vertices {
1016 2268     2268 1 6776 my ($g, @v) = @_;
1017 2268 100       3812 if (&is_multivertexed) {
1018 2         7 $g->add_vertex_by_id($_, _GEN_ID) for @v;
1019 2         6 return $g;
1020             }
1021 2266         5938 my @i = $g->[ _V ]->set_paths(@v);
1022 2266         3545 $g->[ _G ]++;
1023 2266 100       3778 return $g if !&has_union_find;
1024 5         23 $g->[ _U ]->add(@i);
1025 5         12 $g;
1026             }
1027              
1028             sub add_edges {
1029 17808     17808 1 34814 my ($g, @args) = @_;
1030 17808         21688 my @edges;
1031 17808         37265 while (defined(my $u = shift @args)) {
1032 32566 100       86107 push @edges, ref $u eq 'ARRAY' ? $u : @args ? [ $u, shift @args ]
    100          
1033             : __carp_confess "Graph::add_edges: missing end vertex";
1034             }
1035 17807 100       29740 if (&is_multiedged) {
1036 52         144 $g->add_edge_by_id(@$_, _GEN_ID) for @edges;
1037 52         156 return $g;
1038             }
1039 17755         27512 my $uf = &has_union_find;
1040 17755   100     26789 my $deep = &is_hyperedged && &is_directed;
1041 17755 100       54529 my @paths = $g->[ _V ]->get_ids_by_paths(\@edges, 1, 1 + ($deep ? 1 : 0));
1042 17755 100       32125 @paths = map [ sort @$_ ], @paths if &is_undirected;
1043 17755         51148 $g->[ _E ]->set_paths( @paths );
1044 17755 100       30411 $uf->union(@paths) if $uf;
1045 17755         24555 $g->[ _G ]++;
1046 17755         45955 return $g;
1047             }
1048              
1049             sub rename_vertex {
1050 24     24 1 79 my $g = shift;
1051 24         58 $g->[ _V ]->rename_path(@_);
1052 24         53 return $g;
1053             }
1054              
1055             sub rename_vertices {
1056 3     3 1 1496 my ($g, $code) = @_;
1057 3         5 my %seen;
1058             $g->rename_vertex($_, $code->($_))
1059 3         11 for grep !$seen{$_}++, $g->[ _V ]->paths;
1060 3         16 return $g;
1061             }
1062              
1063             sub as_hashes {
1064 11     11 1 11631 my ($g) = @_;
1065 11         80 my (%v, %e, @e);
1066 11         34 my ($is_hyper, $is_directed)= (&is_hyperedged, &is_directed);
1067 11 100       44 if (&is_multivertexed) {
1068 2         9 for my $v ($g->unique_vertices) {
1069 4   50     13 $v{$v} = {
1070             map +($_ => $g->get_vertex_attributes_by_id($v, $_) || {}),
1071             $g->get_multivertex_ids($v)
1072             };
1073             }
1074             } else {
1075 9   100     39 %v = map +($_ => $g->get_vertex_attributes($_) || {}), $g->unique_vertices;
1076             }
1077 11         37 my $multi_e = &is_multiedged;
1078 11         44 for my $e ($g->edges) {
1079             my $edge_attr = {
1080             $multi_e
1081             ? map +($_ => $g->get_edge_attributes_by_id(@$e, $_) || {}),
1082             $g->get_multiedge_ids(@$e)
1083 53 100 100     109 : %{ $g->get_edge_attributes(@$e)||{} }
  40 100       81  
1084             };
1085 53 100       120 if ($is_hyper) {
1086 12         32 my %h = (attributes => $edge_attr);
1087 12 100       27 if ($is_directed) {
1088 8         25 @h{qw(predecessors successors)} = @$e;
1089             } else {
1090 4         6 $h{vertices} = $e;
1091             }
1092 12         30 push @e, \%h;
1093             } else {
1094 41         92 $e{ $e->[0] }{ $e->[1] } = $edge_attr;
1095 41 100       99 $e{ $e->[1] }{ $e->[0] } = $edge_attr if !$is_directed;
1096             }
1097             }
1098 11 100       145 ( \%v, $is_hyper ? \@e : \%e );
1099             }
1100              
1101             sub ingest {
1102 3     3 1 1556 my ($g, $g2) = @_;
1103 3         18 for my $v ($g2->vertices) {
1104 12 100       44 if (&is_multivertexed) {
1105             $g->set_vertex_attributes_by_id($v, $_, $g2->get_vertex_attributes_by_id($v, $_))
1106 4         12 for $g2->get_multivertex_ids($v);
1107             } else {
1108 8         25 $g->set_vertex_attributes($v, $g2->get_vertex_attributes($v));
1109             }
1110 12 100       37 if (&is_multiedged) {
1111 6         15 for my $e ($g2->edges_from($v)) {
1112             $g->set_edge_attributes_by_id(@$e, $_, $g2->get_edge_attributes_by_id(@$e, $_))
1113 4         12 for $g2->get_multiedge_ids(@$e);
1114             }
1115             } else {
1116             $g->set_edge_attributes(@$_, $g2->get_edge_attributes(@$_))
1117 6         20 for $g2->edges_from($v);
1118             }
1119             }
1120 3         20 $g;
1121             }
1122              
1123             ###
1124             # More constructors.
1125             #
1126              
1127             sub copy {
1128 34     34 1 682 my ($g, @args) = @_;
1129 34         147 my %opt = _get_options( \@args );
1130 80     80   700 no strict 'refs';
  80         180  
  80         25157  
1131 34 100       153 my $c = (ref $g)->new(map +($_ => &$_ ? 1 : 0), @GRAPH_PROPS_COPIED);
1132 34         145 $c->add_vertices(&isolated_vertices);
1133 34         111 $c->add_edges(&_edges05);
1134 34         259 return $c;
1135             }
1136              
1137             *copy_graph = \©
1138              
1139             sub _deep_copy_best {
1140 19 50   19   1112 _can_deep_copy_Storable()
1141             ? _deep_copy_Storable(@_) : _deep_copy_DataDumper(@_);
1142             }
1143              
1144             sub _deep_copy_Storable {
1145 20     20   43 my $g = shift;
1146 20         2255 require Safe; # For deep_copy().
1147 20         154552 my $safe = Safe->new;
1148 20         21405 $safe->permit(qw/:load/);
1149 20         184 local $Storable::Deparse = 1;
1150 20     3   104 local $Storable::Eval = sub { $safe->reval($_[0]) };
  3         4849  
1151 20         79 return Storable::thaw(Storable::freeze($g));
1152             }
1153              
1154             sub _deep_copy_DataDumper {
1155 1     1   19 my $g = shift;
1156 1         706 require Data::Dumper;
1157 1         7646 my $d = Data::Dumper->new([$g]);
1158 80     80   654 use vars qw($VAR1);
  80         179  
  80         247789  
1159 1         33 $d->Purity(1)->Terse(1)->Deepcopy(1);
1160 1 50       25 $d->Deparse(1) if $] >= 5.008;
1161 1     1   10 eval $d->Dump;
  1     1   1571  
  1         10  
  1         63  
  1         9  
  1         7  
  1         67  
1162             }
1163              
1164             sub deep_copy {
1165 17     17 1 1096 local $. = $.;
1166 17         49 my $g2 = _deep_copy_best(@_);
1167 17 100       12445 $g2->[ _V ]->reindex if grep ref, &_vertices05;
1168 17         154 $g2;
1169             }
1170              
1171             *deep_copy_graph = \&deep_copy;
1172              
1173             sub transpose_edge {
1174 486     486 1 646 my $g = $_[0];
1175 486 50       785 return $g if !&is_directed;
1176 486 50       872 return undef unless &has_edge;
1177 486         880 my $c = &get_edge_count;
1178 486         1023 my $a = &get_edge_attributes;
1179 486         1294 my @e = reverse @_[1..$#_];
1180 486 100       962 &delete_edge unless $g->has_edge( @e );
1181 486         1836 $g->add_edges(map \@e, 1..$c);
1182 486 50       918 $g->set_edge_attributes(@e, $a) if $a;
1183 486         1355 return $g;
1184             }
1185              
1186             sub transpose_graph {
1187 20     20 1 55 my $t = ©
1188 20 100       55 return $t if !&directed;
1189 17         63 $t->transpose_edge(@$_) for &_edges05;
1190 17         318 return $t;
1191             }
1192              
1193             *transpose = \&transpose_graph;
1194              
1195             sub complete_graph {
1196 9     9 1 717 my $directed = &is_directed;
1197 9         18 my $c = &new;
1198 9         22 my @v = &_vertices05;
1199 9         15 my @edges;
1200 9         25 for (my $i = $#v; $i >= 0; $i-- ) {
1201 20 100       101 push @edges, map +([$v[$i], $v[$_]], $directed ? [$v[$_], $v[$i]] : ()),
1202             0..$i - 1;
1203             }
1204 9         21 $c->add_edges(@edges);
1205 9         39 return $c;
1206             }
1207              
1208             *complement = \&complement_graph;
1209              
1210             sub complement_graph {
1211 5     5 1 528 my $c = &complete_graph;
1212 5         21 $c->delete_edge(@$_) for &edges;
1213 5         30 return $c;
1214             }
1215              
1216             *complete = \&complete_graph;
1217              
1218             sub subgraph {
1219 23     23 1 101 my ($g, $src, $dst) = @_;
1220 23 50 66     104 __carp_confess "Graph::subgraph: need src and dst array references"
      66        
1221             unless ref $src eq 'ARRAY' && (!defined($dst) or ref $dst eq 'ARRAY');
1222 23         734 require Set::Object;
1223 23         8071 my $s = $g->new;
1224 23         63 my @u = grep $g->has_vertex($_), @$src;
1225 23 100       118 my $v = Set::Object->new($dst ? grep $g->has_vertex($_), @$dst : @u);
1226 23 100       88 $s->add_vertices(@u, $dst ? $v->members : ());
1227 23         36 my $directed = &is_directed;
1228 23 100       37 if ($directed) {
1229 12         35 $s->add_edges(grep $v->contains($_->[1]), $g->edges_from(@u));
1230             } else {
1231 11 100       74 my $valid = $dst ? $v + Set::Object->new(@u) : $v;
1232 11   100     104 $s->add_edges(
1233             grep +($v->contains($_->[0]) || $v->contains($_->[1])) &&
1234             ($valid->contains($_->[0]) && $valid->contains($_->[1])),
1235             $g->edges_from(@u)
1236             );
1237             }
1238 23         289 return $s;
1239             }
1240              
1241             ###
1242             # Transitivity.
1243             #
1244              
1245             sub is_transitive {
1246 4     4 1 43 my $g = shift;
1247 4         791 require Graph::TransitiveClosure;
1248 4         16 Graph::TransitiveClosure::is_transitive($g);
1249             }
1250              
1251             ###
1252             # Weighted vertices.
1253             #
1254              
1255             my $defattr = 'weight';
1256              
1257             sub _defattr {
1258 149     149   345 return $defattr;
1259             }
1260              
1261             sub add_weighted_vertex {
1262 1     1 1 4 &expect_non_multivertexed;
1263 1         5 push @_, $defattr, pop;
1264 1         4 goto &set_vertex_attribute;
1265             }
1266              
1267             sub add_weighted_vertices {
1268 1     1 1 4 &expect_non_multivertexed;
1269 1         3 my $g = shift;
1270 1         4 while (@_) {
1271 2         8 my ($v, $w) = splice @_, 0, 2;
1272 2         5 $g->set_vertex_attribute($v, $defattr, $w);
1273             }
1274             }
1275              
1276             sub get_vertex_weight {
1277 5     5 1 15 &expect_non_multivertexed;
1278 5         12 push @_, $defattr;
1279 5         16 goto &get_vertex_attribute;
1280             }
1281              
1282             sub has_vertex_weight {
1283 3     3 1 11 &expect_non_multivertexed;
1284 3         7 push @_, $defattr;
1285 3         23 goto &has_vertex_attribute;
1286             }
1287              
1288             sub set_vertex_weight {
1289 1     1 1 4 &expect_non_multivertexed;
1290 1         3 push @_, $defattr, pop;
1291 1         4 goto &set_vertex_attribute;
1292             }
1293              
1294             sub delete_vertex_weight {
1295 1     1 1 5 &expect_non_multivertexed;
1296 1         3 push @_, $defattr;
1297 1         3 goto &delete_vertex_attribute;
1298             }
1299              
1300             sub add_weighted_vertex_by_id {
1301 1     1 1 6 &expect_multivertexed;
1302 1         6 push @_, $defattr, pop;
1303 1         4 goto &set_vertex_attribute_by_id;
1304             }
1305              
1306             sub add_weighted_vertices_by_id {
1307 1     1 1 5 &expect_multivertexed;
1308 1         6 my $g = shift;
1309 1         5 my $id = pop;
1310 1         3 while (@_) {
1311 2         8 my ($v, $w) = splice @_, 0, 2;
1312 2         18 $g->add_vertex_by_id($v, $id);
1313 2         16 $g->set_vertex_attribute_by_id($v, $id, $defattr, $w);
1314             }
1315             }
1316              
1317             sub get_vertex_weight_by_id {
1318 5     5 1 20 &expect_multivertexed;
1319 5         14 push @_, $defattr;
1320 5         15 goto &get_vertex_attribute_by_id;
1321             }
1322              
1323             sub has_vertex_weight_by_id {
1324 3     3 1 9 &expect_multivertexed;
1325 3         9 push @_, $defattr;
1326 3         10 goto &has_vertex_attribute_by_id;
1327             }
1328              
1329             sub set_vertex_weight_by_id {
1330 1     1 1 649 &expect_multivertexed;
1331 1         3 push @_, $defattr, pop;
1332 1         5 goto &set_vertex_attribute_by_id;
1333             }
1334              
1335             sub delete_vertex_weight_by_id {
1336 1     1 1 5 &expect_multivertexed;
1337 1         5 push @_, $defattr;
1338 1         4 goto &delete_vertex_attribute_by_id;
1339             }
1340              
1341             ###
1342             # Weighted edges.
1343             #
1344              
1345             sub add_weighted_edge {
1346 2585     2585 1 14073 &expect_non_multiedged;
1347 2585         5071 push @_, $defattr, pop;
1348 2585         5793 goto &set_edge_attribute;
1349             }
1350              
1351             sub add_weighted_edges {
1352 3     3 1 43 &expect_non_multiedged;
1353 3         7 my $g = shift;
1354 3         15 while (@_) {
1355 14         45 my ($u, $v, $w) = splice @_, 0, 3;
1356 14         30 $g->set_edge_attribute($u, $v, $defattr, $w);
1357             }
1358             }
1359              
1360             sub add_weighted_edges_by_id {
1361 1     1 1 4 &expect_multiedged;
1362 1         3 my $g = shift;
1363 1         2 my $id = pop;
1364 1         4 while (@_) {
1365 2         8 my ($u, $v, $w) = splice @_, 0, 3;
1366 2         4 $g->set_edge_attribute_by_id($u, $v, $id, $defattr, $w);
1367             }
1368             }
1369              
1370             sub add_weighted_path {
1371 6     6 1 381 &expect_non_multiedged;
1372 6         14 my $g = shift;
1373 6         14 my $u = shift;
1374 6         17 while (@_) {
1375 22         56 my ($w, $v) = splice @_, 0, 2;
1376 22         76 $g->set_edge_attribute($u, $v, $defattr, $w);
1377 22         69 $u = $v;
1378             }
1379             }
1380              
1381             sub get_edge_weight {
1382 7     7 1 19 &expect_non_multiedged;
1383 7         17 push @_, $defattr;
1384 7         22 goto &get_edge_attribute;
1385             }
1386              
1387             sub has_edge_weight {
1388 3     3 1 9 &expect_non_multiedged;
1389 3         8 push @_, $defattr;
1390 3         9 goto &has_edge_attribute;
1391             }
1392              
1393             sub set_edge_weight {
1394 3     3 1 688 &expect_non_multiedged;
1395 3         14 push @_, $defattr, pop;
1396 3         12 goto &set_edge_attribute;
1397             }
1398              
1399             sub delete_edge_weight {
1400 1     1 1 5 &expect_non_multiedged;
1401 1         6 push @_, $defattr;
1402 1         4 goto &delete_edge_attribute;
1403             }
1404              
1405             sub add_weighted_edge_by_id {
1406 6     6 1 38 &expect_multiedged;
1407 6         22 push @_, $defattr, pop;
1408 6         20 goto &set_edge_attribute_by_id;
1409             }
1410              
1411             sub add_weighted_path_by_id {
1412 3     3 1 20 &expect_multiedged;
1413 3         6 my $g = shift;
1414 3         5 my $id = pop;
1415 3         5 my $u = shift;
1416 3         9 while (@_) {
1417 6         16 my ($w, $v) = splice @_, 0, 2;
1418 6         16 $g->set_edge_attribute_by_id($u, $v, $id, $defattr, $w);
1419 6         18 $u = $v;
1420             }
1421             }
1422              
1423             sub get_edge_weight_by_id {
1424 8     8 1 36 &expect_multiedged;
1425 8         23 push @_, $defattr;
1426 8         26 goto &get_edge_attribute_by_id;
1427             }
1428              
1429             sub has_edge_weight_by_id {
1430 3     3 1 10 &expect_multiedged;
1431 3         11 push @_, $defattr;
1432 3         17 goto &has_edge_attribute_by_id;
1433             }
1434              
1435             sub set_edge_weight_by_id {
1436 1     1 1 720 &expect_multiedged;
1437 1         5 push @_, $defattr, pop;
1438 1         6 goto &set_edge_attribute_by_id;
1439             }
1440              
1441             sub delete_edge_weight_by_id {
1442 1     1 1 4 &expect_multiedged;
1443 1         5 push @_, $defattr;
1444 1         6 goto &delete_edge_attribute_by_id;
1445             }
1446              
1447             ###
1448             # Error helpers.
1449             #
1450              
1451             my %expected;
1452             @expected{qw(directed undirected acyclic)} = qw(undirected directed cyclic);
1453              
1454             sub _expected {
1455 43     43   89 my $exp = shift;
1456 43 100       151 my $got = @_ ? shift : $expected{$exp};
1457 43 100       154 $got = defined $got ? ", got $got" : "";
1458 43 50       351 if (my @caller2 = caller(2)) {
1459 43         372 die "$caller2[3]: expected $exp graph$got, at $caller2[1] line $caller2[2].\n";
1460             } else {
1461 0         0 my @caller1 = caller(1); # uncoverable statement
1462 0         0 die "$caller1[3]: expected $exp graph$got, at $caller1[1] line $caller1[2].\n"; # uncoverable statement
1463             }
1464             }
1465              
1466             sub expect_no_args {
1467 10     10 1 17 my $g = shift;
1468 10 50       29 return unless @_;
1469 0         0 my @caller1 = caller(1); # uncoverable statement
1470 0         0 die "$caller1[3]: expected no arguments, got " . scalar @_ . ", at $caller1[1] line $caller1[2]\n"; # uncoverable statement
1471             }
1472              
1473             sub expect_undirected {
1474 1183 100   1183 1 3207 _expected('undirected') unless &is_undirected;
1475             }
1476              
1477             sub expect_directed {
1478 261 100   261 1 1821 _expected('directed') unless &is_directed;
1479             }
1480              
1481             sub expect_acyclic {
1482 3 100   3 1 2044 _expected('acyclic') unless &is_acyclic;
1483             }
1484              
1485             sub expect_dag {
1486 7     7 1 2089 my @got;
1487 7 100       19 push @got, 'undirected' unless &is_directed;
1488 7 100       24 push @got, 'cyclic' unless &is_acyclic;
1489 7 100       63 _expected('directed acyclic', "@got") if @got;
1490             }
1491              
1492             sub expect_hyperedged {
1493 11 100   11 1 28 _expected('hyperedged') unless &is_hyperedged;
1494             }
1495              
1496             sub expect_multivertexed {
1497 226 100   226 1 371 _expected('multivertexed') unless &is_multivertexed;
1498             }
1499             *expect_multivertex = \&expect_multivertexed;
1500              
1501             sub expect_non_multivertexed {
1502 1492 100   1492 1 2416 _expected('non-multivertexed') if &is_multivertexed;
1503             }
1504             *expect_non_multivertex = \&expect_non_multivertexed;
1505              
1506             sub expect_non_multiedged {
1507 19010 100   19010 1 29025 _expected('non-multiedged') if &is_multiedged;
1508             }
1509             *expect_non_multiedge = \&expect_non_multiedged;
1510              
1511             sub expect_multiedged {
1512 416 100   416 1 654 _expected('multiedged') unless &is_multiedged;
1513             }
1514             *expect_multiedge = \&expect_multiedged;
1515              
1516             sub expect_non_unionfind {
1517 677 100   677 1 1202 _expected('non-unionfind') if &has_union_find;
1518             }
1519              
1520             sub _get_options {
1521 1101     1101   10605 my @caller = caller(1);
1522 1101 100 100     7168 unless (@_ == 1 && ref $_[0] eq 'ARRAY') {
1523 3         21 die "$caller[3]: internal error: should be called with only one array ref argument, at $caller[1] line $caller[2].\n";
1524             }
1525 1098         1807 my @opt = @{ $_[0] };
  1098         2516  
1526 1098 50       3119 unless (@opt % 2 == 0) {
1527 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
1528             }
1529 1098         4189 return @opt;
1530             }
1531              
1532             ###
1533             # Random constructors and accessors.
1534             #
1535              
1536             sub __fisher_yates_shuffle (@) {
1537             # From perlfaq4, but modified to be non-modifying.
1538 1     1   772 my @a = @_;
1539 1         3 my $i = @a;
1540 1         5 while ($i--) {
1541 3         8 my $j = int rand ($i+1);
1542 3         11 @a[$i,$j] = @a[$j,$i];
1543             }
1544 1         11 return @a;
1545             }
1546              
1547             BEGIN {
1548             sub _shuffle(@);
1549             # Workaround for the Perl bug [perl #32383] where -d:Dprof and
1550             # List::Util::shuffle do not like each other: if any debugging
1551             # (-d) flags are on, fall back to our own Fisher-Yates shuffle.
1552             # The bug was fixed by perl changes #26054 and #26062, which
1553             # went to Perl 5.9.3. If someone tests this with a pre-5.9.3
1554             # bleadperl that calls itself 5.9.3 but doesn't yet have the
1555             # patches, oh, well.
1556             *_shuffle = $^P && $] < 5.009003 ?
1557 80 50 33 80   1417 \&__fisher_yates_shuffle : do { require List::Util; \&List::Util::shuffle };
  80         638  
  80         182900  
1558             }
1559              
1560             sub random_graph {
1561 14 100   14 1 11010 my $class = (@_ % 2) == 0 ? 'Graph' : shift;
1562 14         41 my %opt = _get_options( \@_ );
1563             __carp_confess "Graph::random_graph: argument 'vertices' missing or undef"
1564 14 100       93 unless defined $opt{vertices};
1565 12 100       37 srand delete $opt{random_seed} if exists $opt{random_seed};
1566 12 100       32 my $random_edge = delete $opt{random_edge} if exists $opt{random_edge};
1567 12         22 my @V;
1568 12 100       85 if (my $ref = ref $opt{vertices}) {
1569 1 50       8 __carp_confess "Graph::random_graph: argument 'vertices' illegal"
1570             if $ref ne 'ARRAY';
1571 1         3 @V = @{ $opt{vertices} };
  1         36  
1572             } else {
1573 11         40 @V = 0..($opt{vertices} - 1);
1574             }
1575 12         24 delete $opt{vertices};
1576 12         19 my $V = @V;
1577 12         28 my $C = $V * ($V - 1) / 2;
1578 12         19 my $E;
1579             __carp_confess "Graph::random_graph: both arguments 'edges' and 'edges_fill' specified"
1580 12 50 66     27 if exists $opt{edges} && exists $opt{edges_fill};
1581 12 100       35 $E = exists $opt{edges_fill} ? $opt{edges_fill} * $C : $opt{edges};
1582 12         15 delete $opt{edges};
1583 12         20 delete $opt{edges_fill};
1584 12         35 my $g = $class->new(%opt);
1585 12         37 $g->add_vertices(@V);
1586 12 50       28 return $g if $V < 2;
1587 12 100       28 $C *= 2 if my $is_directed = $g->directed;
1588 12 100       35 $E = $C / 2 unless defined $E;
1589 12         25 $E = int($E + 0.5);
1590 12         23 my $p = $E / $C;
1591 12 100   12069   44 $random_edge = sub { $p } unless defined $random_edge;
  12069         15734  
1592             # print "V = $V, E = $E, C = $C, p = $p\n";
1593 12 50 0     32 __carp_confess "Graph::random_graph: needs to be countedged or multiedged ($E > $C)"
      33        
1594             if $p > 1.0 && !($g->countedged || $g->multiedged);
1595             # Shuffle the vertex lists so that the pairs at
1596             # the beginning of the lists are not more likely.
1597 12         17 my (%v1_v2, @edges);
1598 12         56 my @V1 = _shuffle @V;
1599 12         37 my @V2 = _shuffle @V;
1600             LOOP:
1601 12         32 while ($E) {
1602 21         37 for my $v1 (@V1) {
1603 280         390 for my $v2 (@V2) {
1604 12848 100       19575 next if $v1 eq $v2; # TODO: allow self-loops?
1605 12573         16471 my $q = $random_edge->($g, $v1, $v2, $p);
1606 12573 100 66     59318 if ($q && ($q == 1 || rand() <= $q) &&
    100 100        
      100        
      100        
1607             !exists $v1_v2{$v1}{$v2} &&
1608             ($is_directed ? 1 : !exists $v1_v2{$v2}{$v1})) {
1609 6027         9895 $v1_v2{$v1}{$v2} = undef;
1610 6027         14557 push @edges, [ $v1, $v2 ];
1611 6027         7408 $E--;
1612 6027 100       10522 last LOOP unless $E;
1613             }
1614             }
1615             }
1616             }
1617 12         239 $g->add_edges(@edges);
1618             }
1619              
1620             sub random_vertex {
1621 126     126 1 51050 my @V = &_vertices05;
1622 126         599 @V[rand @V];
1623             }
1624              
1625             sub random_edge {
1626 31     31 1 19071 my @E = &_edges05;
1627 31         173 @E[rand @E];
1628             }
1629              
1630             sub random_successor {
1631 49     49 1 161 my @S = &successors;
1632 49         187 @S[rand @S];
1633             }
1634              
1635             sub random_predecessor {
1636 48     48 1 150 my @P = &predecessors;
1637 48         193 @P[rand @P];
1638             }
1639              
1640             ###
1641             # Algorithms.
1642             #
1643              
1644             my $MST_comparator = sub { ($_[0] || 0) <=> ($_[1] || 0) };
1645              
1646             sub _MST_attr {
1647 23     23   37 my $attr = shift;
1648             my $attribute =
1649             exists $attr->{attribute} ?
1650 23 50       79 $attr->{attribute} : $defattr;
1651             my $comparator =
1652             exists $attr->{comparator} ?
1653 23 50       54 $attr->{comparator} : $MST_comparator;
1654 23         64 return ($attribute, $comparator);
1655             }
1656              
1657             sub _MST_edges {
1658 23     23   59 my ($g, $attr) = @_;
1659 23         64 my ($attribute, $comparator) = _MST_attr($attr);
1660             map $_->[1],
1661 23         60 sort { $comparator->($a->[0], $b->[0], $a->[1], $b->[1]) }
  1672         2294  
1662             map [ $g->get_edge_attribute(@$_, $attribute), $_ ],
1663             &_edges05;
1664             }
1665              
1666             sub MST_Kruskal {
1667 24     24 1 559 &expect_undirected;
1668 23         69 my ($g, %attr) = @_;
1669 23         1020 require Graph::UnionFind;
1670              
1671 23         97 my $MST = Graph->new(directed => 0);
1672              
1673 23         147 my $UF = Graph::UnionFind->new;
1674 23         60 $UF->add(&_vertices05);
1675              
1676 23         82 my @edges;
1677 23         115 for my $e ($g->_MST_edges(\%attr)) {
1678 1606         2991 my ($u, $v) = @$e; # TODO: hyperedges
1679 1606 100       2956 next if $UF->same( @$e );
1680 454         1472 $UF->union([$u, $v]);
1681 454         1232 push @edges, [ $u, $v ];
1682             }
1683 23         626 $MST->add_edges(@edges);
1684              
1685 23         300 return $MST;
1686             }
1687              
1688             sub _MST_add {
1689 926     926   2104 my ($g, $h, $HF, $r, $attr, $unseen) = @_;
1690             $HF->add( Graph::MSTHeapElem->new( $r, $_, $g->get_edge_attribute( $r, $_, $attr ) ) )
1691 926         2319 for grep exists $unseen->{ $_ }, $g->successors( $r );
1692             }
1693              
1694 246     246   411 sub _next_alphabetic { shift; (sort keys %{ $_[0] })[0] }
  246         334  
  246         1171  
1695 5     5   7 sub _next_numeric { shift; (sort { $a <=> $b } keys %{ $_[0] })[0] }
  5         8  
  9         23  
  5         25  
1696 544     544   906 sub _next_random { shift; (values %{ $_[0] })[ rand keys %{ $_[0] } ] }
  544         699  
  544         2124  
  544         2247  
1697              
1698             sub _root_opt {
1699 150     150   411 my ($g, @args) = @_;
1700 150 100       525 my %opt = @args == 1 ? ( first_root => $args[0] ) : _get_options( \@args );
1701 150         242 my %unseen;
1702 150         470 my @unseen = $g->_vertices05;
1703 150         1865 @unseen{ @unseen } = @unseen;
1704 150         1475 @unseen = _shuffle @unseen;
1705 150         248 my $r;
1706 150 100       409 if (exists $opt{ start }) {
1707 1         3 $opt{ first_root } = delete $opt{ start };
1708 1         5 $opt{ next_root } = undef;
1709             }
1710 150 100       288 if (exists $opt{ first_root }) {
1711 107 100       246 if (ref $opt{ first_root } eq 'CODE') {
1712 1         7 $r = $opt{ first_root }->( $g, \%unseen );
1713             } else {
1714 106         163 $r = $opt{ first_root };
1715             }
1716             } else {
1717 43         93 $r = shift @unseen;
1718             }
1719             my $next =
1720             exists $opt{ next_root } ?
1721             $opt{ next_root } :
1722             $opt{ next_alphabetic } ?
1723             \&_next_alphabetic :
1724             $opt{ next_numeric } ?
1725 150 50       527 \&_next_numeric :
    50          
    100          
1726             \&_next_random;
1727 150         303 my $code = ref $next eq 'CODE';
1728 150 50       332 my $attr = exists $opt{ attribute } ? $opt{ attribute } : $defattr;
1729 150         618 return ( \%opt, \%unseen, \@unseen, $r, $next, $code, $attr );
1730             }
1731              
1732             sub _heap_walk {
1733 83     83   353 my ($g, $h, $add, $etc, $opt, $unseenh, $unseena, $r, $next, $code, $attr) = @_;
1734 83         4177 require Heap::Fibonacci;
1735 83         17918 my $HF = Heap::Fibonacci->new;
1736 83         850 while (defined $r) {
1737             # print "r = $r\n";
1738 105         303 $add->($g, $h, $HF, $r, $attr, $unseenh, $etc);
1739 105         874 delete $unseenh->{ $r };
1740 105         383 while (defined $HF->top) {
1741 4087         29289 my $t = $HF->extract_top;
1742             # use Data::Dumper; print "t = ", Dumper($t);
1743 4087 50       24903 if (defined $t) {
1744 4087         7756 my ($u, $v, $w) = $t->val;
1745             # print "extracted top: $u $v $w\n";
1746 4087 100       11687 if (exists $unseenh->{ $v }) {
1747 1894         4892 $h->set_edge_attribute($u, $v, $attr, $w);
1748 1894         4914 delete $unseenh->{ $v };
1749 1894         4331 $add->($g, $h, $HF, $v, $attr, $unseenh, $etc);
1750             }
1751             }
1752             }
1753 104 100       778 return $h unless defined $next;
1754 103 50       333 $r = $code ? $next->( $g, $unseenh ) : shift @$unseena;
1755 103 100       278 last unless defined $r;
1756             }
1757 81         294 return $h;
1758             }
1759              
1760             sub MST_Prim {
1761 43     43 1 23124 &expect_undirected;
1762 42         1106 require Graph::MSTHeapElem;
1763 42         220 $_[0]->_heap_walk(Graph->new(directed => 0), \&_MST_add, undef, &_root_opt);
1764             }
1765              
1766             *MST_Dijkstra = \&MST_Prim;
1767              
1768             *minimum_spanning_tree = \&MST_Prim;
1769              
1770             ###
1771             # Cycle detection.
1772             #
1773              
1774             *is_cyclic = \&has_a_cycle;
1775              
1776             sub is_acyclic {
1777 13     13 1 32 !&is_cyclic;
1778             }
1779              
1780             sub is_dag {
1781 5 100 100 5 1 1182 &is_directed && &is_acyclic ? 1 : 0;
1782             }
1783              
1784             *is_directed_acyclic_graph = \&is_dag;
1785              
1786             ###
1787             # Simple DFS uses.
1788             #
1789              
1790             sub topological_sort {
1791 5     5 1 625 my $g = shift;
1792 5         16 my %opt = _get_options( \@_ );
1793 5         16 my $eic = delete $opt{ empty_if_cyclic };
1794 5         8 my $hac;
1795 5 100       12 if ($eic) {
1796 1         10 $hac = $g->has_a_cycle;
1797             } else {
1798 4         19 $g->expect_dag;
1799             }
1800 2         12 require Graph::Traversal::DFS;
1801 2         13 my $t = Graph::Traversal::DFS->new($g, %opt);
1802 2         10 my @s = $t->dfs;
1803 2 100       32 $hac ? () : reverse @s;
1804             }
1805              
1806             *toposort = \&topological_sort;
1807              
1808             sub _undirected_copy_compute {
1809 12     12   42 Graph->new(directed => 0, vertices => [&isolated_vertices], edges => [&_edges05]);
1810             }
1811              
1812             sub undirected_copy {
1813 63     63 1 132 &expect_directed;
1814 63         178 return _check_cache($_[0], 'undirected_copy', [], \&_undirected_copy_compute);
1815             }
1816              
1817             *undirected_copy_graph = \&undirected_copy;
1818              
1819             sub directed_copy {
1820 3     3 1 11 &expect_undirected;
1821 3         6 my @edges = &_edges05;
1822 3         12 Graph->new(directed => 1, vertices => [&isolated_vertices],
1823             edges => [@edges, map [reverse @$_], @edges]);
1824             }
1825              
1826             *directed_copy_graph = \&directed_copy;
1827              
1828             ###
1829             # Cache or not.
1830             #
1831              
1832             my %_cache_type =
1833             (
1834             'connectivity' => ['_ccc'],
1835             'strong_connectivity' => ['_scc'],
1836             'biconnectivity' => ['_bcc'],
1837             'SPT_Dijkstra' => ['_spt_di', 'SPT_Dijkstra_root'],
1838             'SPT_Bellman_Ford' => ['_spt_bf', 'SPT_Bellman_Ford_root'],
1839             'undirected_copy' => ['_undirected'],
1840             'transitive_closure_matrix' => ['_tcm'],
1841             );
1842              
1843             for my $t (keys %_cache_type) {
1844 80     80   786 no strict 'refs';
  80         226  
  80         692330  
1845             my @attr = @{ $_cache_type{$t} };
1846 228     228   142447 *{$t."_clear_cache"} = sub { $_[0]->delete_graph_attribute($_) for @attr };
1847             }
1848              
1849             sub _check_cache {
1850 2283     2283   4733 my ($g, $type, $extra_vals, $code, @args) = @_;
1851 2283         3756 my $c = $_cache_type{$type};
1852 2283 50       4202 __carp_confess "Graph: unknown cache type '$type'" if !defined $c;
1853 2283         3745 my ($main_key, @extra_keys) = @$c;
1854 2283 50       4792 __carp_confess "Graph: wrong number of extra values (@extra_keys) vs (@$extra_vals)" if @extra_keys != @$extra_vals;
1855 2283         5588 my $a = $g->get_graph_attribute($main_key);
1856 2283 50 66     8000 __carp_confess "$c attribute set to unexpected value $a"
1857             if defined $a and ref $a ne 'ARRAY';
1858 2283 100 100     6370 unless (defined $a && $a->[ 0 ] == $g->[ _G ]) {
1859 459         1234 $g->set_graph_attribute($main_key, $a = [ $g->[ _G ], $code->( $g, @args ) ]);
1860             }
1861 2281         3335 my $i = -1;
1862             my $extra_invalid = grep {
1863 2281         3476 my $v = $a->[ 1 ]->get_graph_attribute($_);
  106         256  
1864 106         149 $i++; # here so still incremented even if short-cut
1865 106 50       534 !defined $v or $v ne $extra_vals->[$i];
1866             } @extra_keys;
1867 2281 100       4173 if ($extra_invalid) {
1868 29         84 $g->set_graph_attribute($main_key, $a = [ $g->[ _G ], $code->( $g, @args ) ]);
1869             }
1870 2281         9922 return $a->[ 1 ];
1871             }
1872              
1873             ###
1874             # Connected components.
1875             #
1876              
1877             sub _connected_components_compute {
1878 40     40   63 my $g = $_[0];
1879 40         72 my %v2c;
1880             my @c;
1881 40 100       120 return [ [], {} ] unless my @v = $g->unique_vertices;
1882 35 100       75 if (my $UF = &has_union_find) {
1883 9         15 my $V = $g->[ _V ];
1884 9         28 my @ids = $V->get_ids_by_paths(\@v, 0);
1885 9         17 my ($counter, %cc2counter) = 0;
1886 9         26 my @cc = $UF->find(@ids);
1887 9         30 for (my $i = 0; $i <= $#v; $i++) {
1888 20         30 my $cc = $cc[$i];
1889 20 50       37 __carp_confess "connected_component union-find did not have vertex '$v[$i]', please report"
1890             if !defined $cc;
1891 20 100       44 $cc2counter{$cc} = $counter++ if !exists $cc2counter{$cc};
1892 20         27 my $ci = $cc2counter{$cc};
1893 20         34 $v2c{ $v[$i] } = $ci;
1894 20         24 push @{ $c[$ci] }, $v[$i];
  20         64  
1895             }
1896             } else {
1897 26         2426 require Graph::Traversal::DFS;
1898 26         56 my %r; @r{ @v } = @v;
  26         106  
1899 26         62 @c = [];
1900             my $t = Graph::Traversal::DFS->new(
1901             $g,
1902 26     26   165 first_root => sub { (each %r)[1] },
1903 34 100   34   121 next_root => sub { push @c, [] if keys %r; (each %r)[1]; },
  34         215  
1904             pre => sub {
1905 97     97   185 my ($v, $t) = @_;
1906 97         192 $v2c{ $v } = $#c;
1907 97         163 push @{ $c[-1] }, $v;
  97         192  
1908 97         310 delete $r{ $v };
1909             },
1910 26         308 @_[1..$#_]
1911             );
1912 26         128 $t->dfs;
1913             }
1914 35         261 return [ \@c, \%v2c ];
1915             }
1916              
1917             sub _connected_components {
1918 384     384   936 my $ccc = _check_cache($_[0], 'connectivity', [],
1919             \&_connected_components_compute);
1920 384         547 return @{ $ccc };
  384         1414  
1921             }
1922              
1923             sub connected_component_by_vertex {
1924 82     82 1 12279 &expect_undirected;
1925 81         186 (&_connected_components)[1]->{ $_[1] };
1926             }
1927              
1928             sub connected_component_by_index {
1929 58     58 1 16234 &expect_undirected;
1930 57         104 my $value = (&_connected_components)[0]->[$_[1]];
1931 57 50       144 $value ? @{ $value || _empty_array } : ();
  41 100       202  
1932             }
1933              
1934             sub connected_components {
1935 41     41 1 696 &expect_undirected;
1936 40         59 @{ (&_connected_components)[0] };
  40         91  
1937             }
1938              
1939             sub same_connected_components {
1940 29     29 1 18545 &expect_undirected;
1941 28         125 my ($g, @args) = @_;
1942 28         98 my @components;
1943 28 100       61 if (my $UF = &has_union_find) {
1944 14         29 my @ids = &_vertex_ids;
1945 14 100       44 return 0 if @ids != @args;
1946 10         35 @components = $UF->find(@ids);
1947             } else {
1948 14         21 @components = @{ (&_connected_components)[1] }{ @args };
  14         27  
1949             }
1950 24 100       100 return 0 if grep !defined, @components;
1951 20         119 require List::Util;
1952 20         153 List::Util::uniq( @components ) == 1;
1953             }
1954              
1955 40     40   164 sub _super_component { join("+", sort @_) }
1956              
1957             sub connected_graph {
1958 21     21 1 586 &expect_undirected;
1959 20         39 my ($g, %opt) = @_;
1960 20         64 my $cg = Graph->new(undirected => 1);
1961 20 100 100     51 if ($g->has_union_find && $g->vertices == 1) {
1962             # TODO: super_component?
1963 2         6 $cg->add_vertices($g->vertices);
1964             } else {
1965 18   50     61 my $sc_cb = $opt{super_component} || \&_super_component;
1966             $cg->set_vertex_attribute(scalar $sc_cb->(@$_), 'subvertices', $_)
1967 18         37 for $g->connected_components;
1968             }
1969 20         115 return $cg;
1970             }
1971              
1972             sub is_connected {
1973 197     197 1 1005 &expect_undirected;
1974 192         275 return @{ (&_connected_components)[0] } == 1;
  192         329  
1975             }
1976              
1977             sub is_weakly_connected {
1978 10     10 1 2503 &expect_directed;
1979 9         43 splice @_, 0, 1, &undirected_copy;
1980 9         26 goto &is_connected;
1981             }
1982              
1983             *weakly_connected = \&is_weakly_connected;
1984              
1985             sub weakly_connected_components {
1986 6     6 1 560 &expect_directed;
1987 5         13 splice @_, 0, 1, &undirected_copy;
1988 5         15 goto &connected_components;
1989             }
1990              
1991             sub weakly_connected_component_by_vertex {
1992 21     21 1 4495 &expect_directed;
1993 20         44 splice @_, 0, 1, &undirected_copy;
1994 20         56 goto &connected_component_by_vertex;
1995             }
1996              
1997             sub weakly_connected_component_by_index {
1998 15     15 1 5864 &expect_directed;
1999 14         29 splice @_, 0, 1, &undirected_copy;
2000 14         40 goto &connected_component_by_index;
2001             }
2002              
2003             sub same_weakly_connected_components {
2004 8     8 1 6420 &expect_directed;
2005 7         17 splice @_, 0, 1, &undirected_copy;
2006 7         24 goto &same_connected_components;
2007             }
2008              
2009             sub weakly_connected_graph {
2010 6     6 1 560 &expect_directed;
2011 5         14 splice @_, 0, 1, &undirected_copy;
2012 5         16 goto &connected_graph;
2013             }
2014              
2015             sub _strongly_connected_components_compute {
2016 14     14   24 my $g = $_[0];
2017 14         1192 require Graph::Traversal::DFS;
2018 14         69 require List::Util;
2019 14         89 my $t = Graph::Traversal::DFS->new($g);
2020 14         57 my @d = reverse $t->dfs;
2021 14         39 my @c;
2022             my %v2c;
2023             my $u = Graph::Traversal::DFS->new(
2024             $g->transpose_graph,
2025             next_root => sub {
2026 136     136   342 my ($t, $u) = @_;
2027             return if !defined(my $root = List::Util::first(
2028 3345         4126 sub { exists $u->{$_} }, @d
2029 136 100       844 ));
2030 122         445 push @c, [];
2031 122         545 return $root;
2032             },
2033             pre => sub {
2034 255     255   479 my ($v, $t) = @_;
2035 255         355 push @{ $c[-1] }, $v;
  255         499  
2036 255         851 $v2c{$v} = $#c;
2037             },
2038 14         69 next_alphabetic => 1,
2039             );
2040 14         75 $u->dfs;
2041 14         1308 return [ \@c, \%v2c ];
2042             }
2043              
2044             sub _strongly_connected_components_v2c {
2045 12     12   19 &_strongly_connected_components->[1];
2046             }
2047              
2048             sub _strongly_connected_components_arrays {
2049 18     18   31 @{ &_strongly_connected_components->[0] };
  18         38  
2050             }
2051              
2052             sub _strongly_connected_components {
2053 40     40   127 _check_cache($_[0], 'strong_connectivity', [],
2054             \&_strongly_connected_components_compute);
2055             }
2056              
2057             sub strongly_connected_components {
2058 19     19 1 217 &expect_directed;
2059 18         66 goto &_strongly_connected_components_arrays;
2060             }
2061              
2062             sub strongly_connected_component_by_vertex {
2063 5     5 1 680 &expect_directed;
2064 4         10 &_strongly_connected_components_v2c->{$_[1]};
2065             }
2066              
2067             sub strongly_connected_component_by_index {
2068 6     6 1 2777 &expect_directed;
2069 5         11 my $i = $_[1];
2070 5 100       42 return if !defined(my $c = &_strongly_connected_components->[0][ $i ]);
2071 4         34 @$c;
2072             }
2073              
2074             sub same_strongly_connected_components {
2075 8     8 1 3239 &expect_directed;
2076 8         24 my ($g, @args) = @_;
2077 8         57 require Set::Object;
2078 8         14 Set::Object->new(@{ &_strongly_connected_components_v2c }{@args})->size <= 1;
  8         16  
2079             }
2080              
2081             sub is_strongly_connected {
2082 4     4 1 10 &strongly_connected_components == 1;
2083             }
2084              
2085             *strongly_connected = \&is_strongly_connected;
2086              
2087             sub strongly_connected_graph {
2088 6     6 1 9541 &expect_directed;
2089 6         21 my ($g, %attr) = @_;
2090 6         13 my $sc_cb = \&_super_component;
2091 6         19 _opt_get(\%attr, super_component => \$sc_cb);
2092 6         14 _opt_unknown(\%attr);
2093 5         8 my ($c, $v2c) = @{ &_strongly_connected_components };
  5         11  
2094 5         16 my $s = Graph->new;
2095 5         26 my @s = map $sc_cb->(@$_), @$c;
2096 5         47 $s->set_vertex_attribute($s[$_], 'subvertices', $c->[$_]) for 0..$#$c;
2097 5         24 require List::Util;
2098 5         13 $s->add_edges(map [@s[ @$v2c{ @$_ } ]], grep List::Util::uniq( @$v2c{ @$_ } ) > 1, &_edges05);
2099 5         39 return $s;
2100             }
2101              
2102             ###
2103             # Biconnectivity.
2104             #
2105              
2106             sub _biconnectivity_out {
2107 14931     14931   26323 my ($state, $u, $v) = @_;
2108 14931         17215 my @BC;
2109 14931         17049 while (@{$state->{stack}}) {
  16855         29595  
2110 16855         22017 push @BC, my $e = pop @{$state->{stack}};
  16855         28482  
2111 16855 100 66     51579 last if $e->[0] eq $u && $e->[1] eq $v;
2112             }
2113 14931 50       26699 push @{$state->{BC}}, \@BC if @BC;
  14931         33432  
2114             }
2115              
2116             sub _biconnectivity_dfs {
2117 17608     17608   27968 my ($E, $u, $state) = @_;
2118 17608         36361 $state->{low}{$u} = $state->{num}{$u} = $state->{dfs}++;
2119 17608         36930 for my $v ($E->successors($u)) {
2120 35320 100 100     125450 if (!exists $state->{num}{$v}) {
    100 100        
2121 15948         18731 push @{$state->{stack}}, [$u, $v];
  15948         36693  
2122 15948         32886 $state->{pred}{$v} = $u;
2123 15948         31435 _biconnectivity_dfs($E, $v, $state);
2124 15948         25419 $state->{low}{$u} = List::Util::min(@{ $state->{low} }{$u, $v});
  15948         37528  
2125             _biconnectivity_out($state, $u, $v)
2126 15948 100       38587 if $state->{low}{$v} >= $state->{num}{$u};
2127             } elsif (defined $state->{pred}{$u} &&
2128             $state->{pred}{$u} ne $v &&
2129             $state->{num}{$v} < $state->{num}{$u}) {
2130 907         1168 push @{$state->{stack}}, [$u, $v];
  907         2332  
2131 907         2688 $state->{low}{$u} = List::Util::min($state->{low}{$u}, $state->{num}{$v});
2132             }
2133             }
2134             }
2135              
2136             sub _biconnectivity_compute {
2137 288     288   1575 require List::Util;
2138 288         566 my ($g) = @_;
2139 288         549 my ($V, $E) = @$g[ _V, _E ];
2140 288         807 my %state = (BC=>[], dfs=>0);
2141 288         877 my @u = $V->ids;
2142 288         630 for my $u (@u) {
2143 17608 100       34020 next if exists $state{num}->{$u};
2144 1660         3555 _biconnectivity_dfs($E, $u, \%state);
2145 1660 100       2376 push @{$state{BC}}, delete $state{stack} if @{ $state{stack} || _empty_array };
  0 50       0  
  1660         5105  
2146             }
2147              
2148             # Mark the components each vertex belongs to.
2149 288         649 my ($bci, %v2bc, %bc2v) = 0;
2150 288         351 for my $bc (@{$state{BC}}) {
  288         530  
2151 14931         52324 $v2bc{$_}{$bci} = undef for map @$_, @$bc;
2152 14931         23141 $bci++;
2153             }
2154              
2155             # Any isolated vertices get each their own component.
2156 288         3941 $v2bc{$_}{$bci++} = undef for grep !exists $v2bc{$_}, @u;
2157              
2158             # build vector now we know how big to make it
2159 288         1063 my ($Z, %v2bc_vec, @ap) = "\0" x (($bci + 7) / 8);
2160 288         5180 @v2bc_vec{@u} = ($Z) x @u;
2161 288         572 for my $v (@u) {
2162 17608         21526 my @components = keys %{ $v2bc{$v} };
  17608         38368  
2163 17608         64372 vec($v2bc_vec{$v}, $_, 1) = 1 for @components;
2164 17608         51530 $bc2v{$_}{$v}{$_} = undef for @components;
2165             # Articulation points / cut vertices are the vertices
2166             # which belong to more than one component.
2167 17608 100       38456 push @ap, $v if @components > 1;
2168             }
2169              
2170             # Bridges / cut edges are the components of two vertices.
2171 288         16989 my @br = grep @$_ == 2, map [keys %$_], values %bc2v;
2172              
2173             # Create the subgraph components.
2174 288         1173 my @sg = map [ List::Util::uniq( map @$_, @$_ ) ], @{$state{BC}};
  288         33518  
2175 288         1798 my ($apdeep, $sgv, $brv) = $V->get_paths_by_ids([[\@ap], \@sg, \@br], 1);
2176 288         27954 return [ @$apdeep, $sgv, $brv, \%v2bc, \%v2bc_vec, $Z ];
2177             }
2178              
2179             sub biconnectivity {
2180 471     471 1 163041 &expect_undirected;
2181 470 50       645 @{ _check_cache($_[0], 'biconnectivity', [],
  470         1974  
2182             \&_biconnectivity_compute, @_[1..$#_]) || _empty_array };
2183             }
2184              
2185             sub is_biconnected {
2186 13 100   13 1 26275 &edges >= 2 ? @{ (&biconnectivity)[0] } == 0 : undef ;
  10         22  
2187             }
2188              
2189             sub is_edge_connected {
2190 13 100   13 1 40 &edges >= 2 ? @{ (&biconnectivity)[2] } == 0 : undef;
  10         39  
2191             }
2192              
2193             sub is_edge_separable {
2194 13 100   13 1 37 &edges >= 2 ? @{ (&biconnectivity)[2] } > 0 : undef;
  10         22  
2195             }
2196              
2197             sub articulation_points {
2198 248     248 1 1828 @{ (&biconnectivity)[0] };
  248         552  
2199             }
2200              
2201             *cut_vertices = \&articulation_points;
2202              
2203             sub biconnected_components {
2204 14     14 1 2002 @{ (&biconnectivity)[1] };
  14         27  
2205             }
2206              
2207             sub biconnected_component_by_index {
2208 16     16 1 8963 my ($i) = splice @_, 1, 1;
2209 16         24 (&biconnectivity)[1]->[ $i ];
2210             }
2211              
2212             sub biconnected_component_by_vertex {
2213 2     2 1 9 my ($v) = splice @_, 1, 1;
2214 2         6 my $v2bc = (&biconnectivity)[3];
2215 2         6 splice @_, 1, 0, $v;
2216 2         5 my $V = $_[0]->[ _V ];
2217 2         8 ($v) = $V->get_ids_by_paths([$v]);
2218 2 50       13 return defined $v2bc->{ $v } ? keys %{ $v2bc->{ $v } } : ();
  2         10  
2219             }
2220              
2221             sub same_biconnected_components {
2222 5     5 1 493 my ($v2bc, $Z) = (&biconnectivity)[4,5];
2223 5         12 my $V = $_[0]->[ _V ];
2224 5         23 my @vs = $V->get_ids_by_paths([@_[1..$#_]]);
2225 5 50       30 return 0 if grep !defined, my @vecs = @$v2bc{ @vs };
2226 5         9 my $accumulator = $vecs[0];
2227 5         49 $accumulator &= $_ for @vecs[1..$#vecs]; # accumulate 0s -> all in same
2228 5         32 $accumulator ne $Z;
2229             }
2230              
2231             sub biconnected_graph {
2232 1     1 1 5 my ($g, %opt) = @_;
2233 1         5 my $bc = (&biconnectivity)[1];
2234 1         5 my $bcg = Graph->new(directed => 0);
2235 1   50     11 my $sc_cb = $opt{super_component} || \&_super_component;
2236 1         7 my @s = map $sc_cb->(@$_), @$bc;
2237 1         22 $bcg->set_vertex_attribute($s[$_], 'subvertices', $bc->[$_]) for 0..$#$bc;
2238 1         3 my @edges;
2239 1         17 for my $i (0..$#$bc) {
2240 5         36 my @u = @{ $bc->[ $i ] };
  5         22  
2241 5         13 for my $j (0..$i-1) {
2242 10         11 my %j; @j{ @{ $bc->[ $j ] } } = ();
  10         13  
  10         23  
2243 10 100       28 next if !grep exists $j{ $_ }, @u;
2244 4         16 push @edges, [ @s[$i, $j] ];
2245             }
2246             }
2247 1         4 $bcg->add_edges(@edges);
2248 1         9 return $bcg;
2249             }
2250              
2251             sub bridges {
2252 59 50   59 1 26714 @{ (&biconnectivity)[2] || _empty_array };
  59         136  
2253             }
2254              
2255             ###
2256             # SPT.
2257             #
2258              
2259             sub _SPT_add {
2260 1073     1073   2181 my ($g, $h, $HF, $r, $attr, $unseen, $etc) = @_;
2261 1073   100     2738 my $etc_r = $etc->{ $r } || 0;
2262 1073         2357 for my $s ( grep exists $unseen->{ $_ }, $g->successors( $r ) ) {
2263 5253         12050 my $t = $g->get_edge_attribute( $r, $s, $attr );
2264 5253 100       9791 $t = 1 unless defined $t;
2265 5253 100       9382 __carp_confess "Graph::SPT_Dijkstra: edge $r-$s is negative ($t)"
2266             if $t < 0;
2267 5252 100 100     22084 if (!defined($etc->{ $s }) || ($etc_r + $t) < $etc->{ $s }) {
2268 1017   100     2589 my $etc_s = $etc->{ $s } || 0;
2269 1017         2786 $etc->{ $s } = $etc_r + $t;
2270             # print "$r - $s : setting $s to $etc->{ $s } ($etc_r, $etc_s)\n";
2271 1017         3904 $h->set_vertex_attributes($s, { $attr=>$etc->{ $s }, 'p', $r });
2272 1017         3147 $HF->add( Graph::SPTHeapElem->new($r, $s, $etc->{ $s }) );
2273             }
2274             }
2275             }
2276              
2277             sub _SPT_Dijkstra_compute {
2278 41     41   3435 require Graph::SPTHeapElem;
2279 41         164 my $sptg = $_[0]->_heap_walk($_[0]->new, \&_SPT_add, {}, @_[1..$#_]);
2280 40         1262 $sptg->set_graph_attribute('SPT_Dijkstra_root', $_[4]);
2281 40         128 $sptg;
2282             }
2283              
2284             sub SPT_Dijkstra {
2285 81     81 1 173 my $g = $_[0];
2286 81         183 my @args = &_root_opt;
2287 81         283 _check_cache($g, 'SPT_Dijkstra', [$args[3]],
2288             \&_SPT_Dijkstra_compute, @args);
2289             }
2290              
2291             *SSSP_Dijkstra = \&SPT_Dijkstra;
2292              
2293             *single_source_shortest_paths = \&SPT_Dijkstra;
2294              
2295             sub SP_Dijkstra {
2296 68     68 1 26131 my ($g, $u, $v) = @_;
2297 68         189 my $sptg = $g->SPT_Dijkstra(first_root => $u);
2298 68         156 my @path = ($v);
2299 68         317 require Set::Object;
2300 68         256 my $seen = Set::Object->new;
2301 68         174 my $V = $g->vertices;
2302 68         123 my $p;
2303 68         194 while (defined($p = $sptg->get_vertex_attribute($v, 'p'))) {
2304 82 50       233 last if $seen->contains($p);
2305 82         435 push @path, $p;
2306 82         118 $v = $p;
2307 82         248 $seen->insert($p);
2308 82 100 66     421 last if $seen->size == $V || $u eq $v;
2309             }
2310 68 100 66     418 return if !@path or $path[-1] ne $u;
2311 27         319 return reverse @path;
2312             }
2313              
2314             sub __SPT_Bellman_Ford {
2315 2118     2118   3552 my ($g, $u, $v, $attr, $d, $p, $c0, $c1) = @_;
2316 2118 100       4268 return unless $c0->{ $u };
2317 215         402 my $w = $g->get_edge_attribute($u, $v, $attr);
2318 215 100       461 $w = 1 unless defined $w;
2319 215 100       419 if (defined $d->{ $v }) {
2320 141 50       285 if (defined $d->{ $u }) {
2321 141 100       407 if ($d->{ $v } > $d->{ $u } + $w) {
2322 14         27 $d->{ $v } = $d->{ $u } + $w;
2323 14         23 $p->{ $v } = $u;
2324 14         31 $c1->{ $v }++;
2325             }
2326             } # else !defined $d->{ $u } && defined $d->{ $v }
2327             } else {
2328 74 50       147 if (defined $d->{ $u }) {
2329             # defined $d->{ $u } && !defined $d->{ $v }
2330 74         175 $d->{ $v } = $d->{ $u } + $w;
2331 74         124 $p->{ $v } = $u;
2332 74         172 $c1->{ $v }++;
2333             } # else !defined $d->{ $u } && !defined $d->{ $v }
2334             }
2335             }
2336              
2337             sub _SPT_Bellman_Ford {
2338 11     11   32 my ($g, $opt, $unseenh, $unseena, $r, $next, $code, $attr) = @_;
2339 11         18 my %d;
2340 11 50       45 return unless defined $r;
2341 11         23 $d{ $r } = 0;
2342 11         16 my %p;
2343 11         33 my $V = $g->vertices;
2344 11         20 my %c0; # Changed during the last iteration?
2345 11         25 $c0{ $r }++;
2346 11         32 for (my $i = 0; $i < $V; $i++) {
2347 89         119 my %c1;
2348 89         168 for my $e ($g->edges) {
2349 1546         2548 my ($u, $v) = @$e;
2350 1546         3027 __SPT_Bellman_Ford($g, $u, $v, $attr, \%d, \%p, \%c0, \%c1);
2351 1546 100       2542 __SPT_Bellman_Ford($g, $v, $u, $attr, \%d, \%p, \%c0, \%c1)
2352             if $g->undirected;
2353             }
2354 89 100       575 %c0 = %c1 unless $i == $V - 1;
2355             }
2356              
2357 11         26 for my $e ($g->edges) {
2358 161         323 my ($u, $v) = @$e;
2359 161 100 66     542 if (defined $d{ $u } && defined $d{ $v }) {
2360 148         274 my $d = $g->get_edge_attribute($u, $v, $attr);
2361             __carp_confess "Graph::SPT_Bellman_Ford: negative cycle exists"
2362 148 100 100     523 if defined $d && $d{ $v } > $d{ $u } + $d;
2363             }
2364             }
2365              
2366 10         66 return (\%p, \%d);
2367             }
2368              
2369             sub _SPT_Bellman_Ford_compute {
2370 11     11   29 my ($g, @args) = @_;
2371 11         35 my ($p, $d) = $g->_SPT_Bellman_Ford(@args);
2372 10         30 my $h = $g->new;
2373 10         58 for my $v (keys %$p) {
2374 69         117 my $u = $p->{ $v };
2375 69         215 $h->set_edge_attribute( $u, $v, $args[6],
2376             $g->get_edge_attribute($u, $v, $args[6]));
2377 69         358 $h->set_vertex_attributes( $v, { $args[6], $d->{ $v }, p => $u } );
2378             }
2379 10         76 $h->set_graph_attribute('SPT_Bellman_Ford_root', $args[3]);
2380 10         67 $h;
2381             }
2382              
2383             sub SPT_Bellman_Ford {
2384 27     27 1 3223 my @args = &_root_opt;
2385 27         103 _check_cache($_[0], 'SPT_Bellman_Ford', [$args[3]],
2386             \&_SPT_Bellman_Ford_compute, @args);
2387             }
2388              
2389             *SSSP_Bellman_Ford = \&SPT_Bellman_Ford;
2390              
2391             sub SP_Bellman_Ford {
2392 18     18 1 44 my ($g, $u, $v) = @_;
2393 18         44 my $sptg = $g->SPT_Bellman_Ford(first_root => $u);
2394 18         69 my @path = ($v);
2395 18         90 require Set::Object;
2396 18         76 my $seen = Set::Object->new;
2397 18         43 my $V = $g->vertices;
2398 18         32 my $p;
2399 18         61 while (defined($p = $sptg->get_vertex_attribute($v, 'p'))) {
2400 30 50       81 last if $seen->contains($p);
2401 30         149 push @path, $p;
2402 30         43 $v = $p;
2403 30         91 $seen->insert($p);
2404 30 50       89 last if $seen->size == $V;
2405             }
2406             # @path = () if @path && "$path[-1]" ne "$u";
2407 18         266 return reverse @path;
2408             }
2409              
2410             ###
2411             # Transitive Closure.
2412             #
2413              
2414             sub TransitiveClosure_Floyd_Warshall {
2415 19     19 1 2104 my $self = shift;
2416 19         545 require Graph::TransitiveClosure;
2417 19         95 Graph::TransitiveClosure->new($self, @_);
2418             }
2419              
2420             *transitive_closure = \&TransitiveClosure_Floyd_Warshall;
2421              
2422             sub APSP_Floyd_Warshall {
2423 37     37 1 3028 my $self = shift;
2424 37         1662 require Graph::TransitiveClosure;
2425 37         201 Graph::TransitiveClosure->new($self, path => 1, @_);
2426             }
2427              
2428             *all_pairs_shortest_paths = \&APSP_Floyd_Warshall;
2429              
2430             sub _transitive_closure_matrix_compute {
2431 22     22   47 &APSP_Floyd_Warshall->transitive_closure_matrix;
2432             }
2433              
2434             sub transitive_closure_matrix {
2435 1143     1143 1 3074 _check_cache($_[0], 'transitive_closure_matrix', [],
2436             \&_transitive_closure_matrix_compute, @_[1..$#_]);
2437             }
2438              
2439             sub path_length {
2440 1532     1532 1 21794 shift->transitive_closure_matrix->path_length(@_);
2441             }
2442              
2443             sub path_successor {
2444 27     27 1 91 shift->transitive_closure_matrix->path_successor(@_);
2445             }
2446              
2447             sub path_vertices {
2448 205     205 1 80225 shift->transitive_closure_matrix->path_vertices(@_);
2449             }
2450              
2451             sub all_paths {
2452 25     25 1 14689 shift->transitive_closure_matrix->all_paths(@_);
2453             }
2454              
2455             sub is_reachable {
2456 12724     12724 1 526273 shift->transitive_closure_matrix->is_reachable(@_);
2457             }
2458              
2459             sub for_shortest_paths {
2460 34     34 1 52 my $g = shift;
2461 34         46 my $c = shift;
2462 34         83 my $t = $g->transitive_closure_matrix;
2463 34         91 my @v = $g->vertices;
2464 34         48 my $n = 0;
2465 34         69 for my $u (@v) {
2466 183         477 $c->($t, $u, $_, ++$n) for grep $t->is_reachable($u, $_), @v;
2467             }
2468 34         67 return $n;
2469             }
2470              
2471             sub _minmax_path {
2472 25     25   38 my $g = shift;
2473 25         71 my $min;
2474             my $max;
2475 25         0 my $minp;
2476 25         0 my $maxp;
2477             $g->for_shortest_paths(sub {
2478 628     628   1091 my ($t, $u, $v, $n) = @_;
2479 628         1305 my $l = $t->path_length($u, $v);
2480 628 50       1075 return unless defined $l;
2481 628         705 my $p;
2482 628 100 100     1996 if ($u ne $v && (!defined $max || $l > $max)) {
      100        
2483 50         63 $max = $l;
2484 50         119 $maxp = $p = [ $t->path_vertices($u, $v) ];
2485             }
2486 628 100 100     2427 if ($u ne $v && (!defined $min || $l < $min)) {
      100        
2487 18         23 $min = $l;
2488 18   100     88 $minp = $p || [ $t->path_vertices($u, $v) ];
2489             }
2490 25         156 });
2491 25         188 return ($min, $max, $minp, $maxp);
2492             }
2493              
2494             sub diameter {
2495 15     15 1 47 my $g = shift;
2496 15         43 my ($min, $max, $minp, $maxp) = $g->_minmax_path(@_);
2497 15 50       117 return defined $maxp ? (wantarray ? @$maxp : $max) : undef;
    100          
2498             }
2499              
2500             *graph_diameter = \&diameter;
2501              
2502             sub longest_path {
2503 5     5 1 18 my ($g, $u, $v) = @_;
2504 5         13 my $t = $g->transitive_closure_matrix;
2505 5 100       17 if (defined $u) {
2506 2 50       11 return wantarray ? $t->path_vertices($u, $v) : $t->path_length($u, $v)
    100          
2507             if defined $v;
2508 1         2 my $max;
2509             my @max;
2510 1         4 for my $v (grep $u ne $_, $g->vertices) {
2511 9         21 my $l = $t->path_length($u, $v);
2512 9 100 100     42 next if !(defined $l && (!defined $max || $l > $max));
      66        
2513 3         4 $max = $l;
2514 3         10 @max = $t->path_vertices($u, $v);
2515             }
2516 1 50       11 return wantarray ? @max : $max;
2517             }
2518 3 100       8 if (defined $v) {
2519 1         2 my $max;
2520             my @max;
2521 1         4 for my $u (grep $_ ne $v, $g->vertices) {
2522 9         21 my $l = $t->path_length($u, $v);
2523 9 100 100     58 next if !(defined $l && (!defined $max || $l > $max));
      66        
2524 2         4 $max = $l;
2525 2         5 @max = $t->path_vertices($u, $v);
2526             }
2527 1 50       8 return wantarray ? @max : @max - 1;
2528             }
2529 2         7 my ($min, $max, $minp, $maxp) = $g->_minmax_path(@_);
2530 2 50       21 return defined $maxp ? (wantarray ? @$maxp : $max) : undef;
    50          
2531             }
2532              
2533             sub vertex_eccentricity {
2534 165     165 1 394 &expect_undirected;
2535 165         305 my ($g, $u) = @_;
2536 165 100       293 return Infinity() if !&is_connected;
2537 158         238 my $max;
2538 158         337 for my $v (grep $u ne $_, $g->vertices) {
2539 1095         1922 my $l = $g->path_length($u, $v);
2540 1095 100 100     4218 next if !(defined $l && (!defined $max || $l > $max));
      66        
2541 366         568 $max = $l;
2542             }
2543 158 100       468 return defined $max ? $max : Infinity();
2544             }
2545              
2546             sub shortest_path {
2547 11     11 1 40 &expect_undirected;
2548 11         25 my ($g, $u, $v) = @_;
2549 11         27 my $t = $g->transitive_closure_matrix;
2550 11 100       29 if (defined $u) {
2551 2 50       12 return wantarray ? $t->path_vertices($u, $v) : $t->path_length($u, $v)
    100          
2552             if defined $v;
2553 1         9 my $min;
2554             my @min;
2555 1         3 for my $v (grep $u ne $_, $g->vertices) {
2556 9         20 my $l = $t->path_length($u, $v);
2557 9 100 66     44 next if !(defined $l && (!defined $min || $l < $min));
      33        
2558 1         2 $min = $l;
2559 1         4 @min = $t->path_vertices($u, $v);
2560             }
2561             # print "min/1 = @min\n";
2562 1 50       9 return wantarray ? @min : $min;
2563             }
2564 9 100       18 if (defined $v) {
2565 1         3 my $min;
2566             my @min;
2567 1         3 for my $u (grep $_ ne $v, $g->vertices) {
2568 9         20 my $l = $t->path_length($u, $v);
2569 9 100 100     39 next if !(defined $l && (!defined $min || $l < $min));
      66        
2570 3         5 $min = $l;
2571 3         8 @min = $t->path_vertices($u, $v);
2572             }
2573             # print "min/2 = @min\n";
2574 1 50       8 return wantarray ? @min : $min;
2575             }
2576 8         20 my ($min, $max, $minp, $maxp) = $g->_minmax_path(@_);
2577 8 100       49 return if !defined $minp;
2578 2 50       22 wantarray ? @$minp : $min;
2579             }
2580              
2581             sub radius {
2582 17     17 1 52 &expect_undirected;
2583 17         36 my $g = shift;
2584 17         44 my ($center, $radius) = (undef, Infinity());
2585 17         43 for my $v ($g->vertices) {
2586 89         195 my $x = $g->vertex_eccentricity($v);
2587 89 100 66     335 ($center, $radius) = ($v, $x) if defined $x && $x < $radius;
2588             }
2589 17         69 return $radius;
2590             }
2591              
2592             sub center_vertices {
2593 10     10 1 1454 &expect_undirected;
2594 10         28 my ($g, $delta) = @_;
2595 10 100       31 $delta = 0 unless defined $delta;
2596 10         19 $delta = abs($delta);
2597 10         20 my @c;
2598 10         24 my $Inf = Infinity();
2599 10         31 my $r = $g->radius;
2600 10 100 66     63 if (defined $r && $r != $Inf) {
2601 7         17 for my $v ($g->vertices) {
2602 53         106 my $e = $g->vertex_eccentricity($v);
2603 53 50 33     205 next unless defined $e && $e != $Inf;
2604 53 100       137 push @c, $v if abs($e - $r) <= $delta;
2605             }
2606             }
2607 10         75 return @c;
2608             }
2609              
2610             *centre_vertices = \¢er_vertices;
2611              
2612             sub average_path_length {
2613 9     9 1 1451 my $g = shift;
2614 9         26 my @A = @_;
2615 9         15 my $d = 0;
2616 9         10 my $m = 0;
2617             $g->for_shortest_paths(sub {
2618 809     809   1298 my ($t, $u, $v, $n) = @_;
2619 809 100       1451 return unless my $l = $t->path_length($u, $v);
2620 726 100 100     2040 return if defined $A[0] && $u ne $A[0];
2621 308 100 100     812 return if defined $A[1] && $v ne $A[1];
2622 145         175 $d += $l;
2623 145         294 $m++;
2624 9         61 });
2625 9 100       112 return $m ? $d / $m : undef;
2626             }
2627              
2628             ###
2629             # Simple tests.
2630             #
2631              
2632             sub is_multi_graph {
2633 32 100 100 32 1 83 return 0 unless &is_multiedged || &is_countedged;
2634 16         26 my $g = $_[0];
2635 16         22 my $multiedges = 0;
2636 16         30 for my $e (&_edges05) {
2637 14         33 my ($u, @v) = @$e;
2638 14 100       105 return 0 if grep $u eq $_, @v;
2639 6 100       16 $multiedges++ if $g->get_edge_count(@$e) > 1;
2640             }
2641 8         79 return $multiedges;
2642             }
2643              
2644             sub is_simple_graph {
2645 32 100 100 32 1 75 return 1 unless &is_multiedged || &is_countedged;
2646 16         27 my $g = $_[0];
2647 16 100       28 return 0 if grep $g->get_edge_count(@$_) > 1, &_edges05;
2648 12         51 return 1;
2649             }
2650              
2651             sub is_pseudo_graph {
2652 32   100 32 1 72 my $m = &is_countedged || &is_multiedged;
2653 32         47 my $g = $_[0];
2654 32         69 for my $e (&_edges05) {
2655 28         62 my ($u, @v) = @$e;
2656 28 100       221 return 1 if grep $u eq $_, @v;
2657 12 100 100     42 return 1 if $m && $g->get_edge_count($u, @v) > 1;
2658             }
2659 14         64 return 0;
2660             }
2661              
2662             ###
2663             # Rough isomorphism guess.
2664             #
2665              
2666             my %_factorial = (0 => 1, 1 => 1);
2667              
2668             sub __factorial {
2669 4     4   9 my $n = shift;
2670 4         20 for (my $i = 2; $i <= $n; $i++) {
2671 14 100       31 next if exists $_factorial{$i};
2672 7         26 $_factorial{$i} = $i * $_factorial{$i - 1};
2673             }
2674 4         7 $_factorial{$n};
2675             }
2676              
2677             sub _factorial {
2678 39     39   57 my $n = int(shift);
2679 39 50       77 __carp_confess "factorial of a negative number" if $n < 0;
2680 39 100       77 __factorial($n) unless exists $_factorial{$n};
2681 39         85 return $_factorial{$n};
2682             }
2683              
2684             sub could_be_isomorphic {
2685 31     31 1 82 my ($g0, $g1) = @_;
2686 31 100       64 return 0 unless &vertices == $g1->vertices;
2687 23 100       47 return 0 unless &_edges05 == $g1->_edges05;
2688 17         24 my %d0;
2689 17         30 $d0{ $g0->in_degree($_) }{ $g0->out_degree($_) }++ for &vertices;
2690 17         35 my %d1;
2691 17         37 $d1{ $g1->in_degree($_) }{ $g1->out_degree($_) }++ for $g1->vertices;
2692 17 50       60 return 0 unless keys %d0 == keys %d1;
2693 17         49 for my $da (keys %d0) {
2694             return 0
2695             unless exists $d1{$da} &&
2696 31 50 33     68 keys %{ $d0{$da} } == keys %{ $d1{$da} };
  31         60  
  31         83  
2697             return 0
2698             if grep !(exists $d1{$da}{$_} && $d0{$da}{$_} == $d1{$da}{$_}),
2699 31 100 66     42 keys %{ $d0{$da} };
  31         184  
2700             }
2701 13         32 for my $da (keys %d0) {
2702 27 50       31 return 0 if grep $d1{$da}{$_} != $d0{$da}{$_}, keys %{ $d0{$da} };
  27         74  
2703 27         66 delete $d1{$da};
2704             }
2705 13 50       30 return 0 unless keys %d1 == 0;
2706 13         21 my $f = 1;
2707 13         22 for my $da (keys %d0) {
2708 27         33 $f *= _factorial(abs($d0{$da}{$_})) for keys %{ $d0{$da} };
  27         72  
2709             }
2710 13         83 return $f;
2711             }
2712              
2713             ###
2714             # Analysis functions.
2715              
2716             sub subgraph_by_radius {
2717 17     17 1 72 $_[0]->subgraph([ @_[1..$#_-1], &reachable_by_radius ]);
2718             }
2719              
2720             sub clustering_coefficient {
2721 2     2 1 10 my ($g) = @_;
2722 2 100       6 return unless my @v = $g->vertices;
2723 1         6 require Set::Object;
2724 1         5 my %clustering;
2725              
2726 1         2 my $gamma = 0;
2727              
2728 1         8 for my $n (@v) {
2729 15         24 my $gamma_v = 0;
2730 15         30 my @neigh = $g->successors($n);
2731 15         50 my $c = Set::Object->new;
2732 15         28 for my $u (@neigh) {
2733 29   100     88 for my $v (grep +(!$c->contains("$u-$_") && $g->has_edge($u, $_)), @neigh) {
2734 15         20 $gamma_v++;
2735 15         87 $c->insert("$u-$v");
2736 15         53 $c->insert("$v-$u");
2737             }
2738             }
2739 15 100       32 if (@neigh > 1) {
2740 9         29 $clustering{$n} = $gamma_v/(@neigh * (@neigh - 1) / 2);
2741 9         40 $gamma += $gamma_v/(@neigh * (@neigh - 1) / 2);
2742             } else {
2743 6         23 $clustering{$n} = 0;
2744             }
2745             }
2746              
2747 1         5 $gamma /= @v;
2748              
2749 1 50       16 return wantarray ? ($gamma, %clustering) : $gamma;
2750             }
2751              
2752             sub betweenness {
2753 1     1 1 2578 my $g = shift;
2754              
2755 1         5 my @V = $g->vertices();
2756              
2757 1         3 my %Cb; # C_b{w} = 0
2758              
2759 1         8 @Cb{@V} = ();
2760              
2761 1         4 for my $s (@V) {
2762 15         23 my @S; # stack (unshift, shift)
2763              
2764             my %P; # P{w} = empty list
2765 15         104 $P{$_} = [] for @V;
2766              
2767 15         20 my %sigma; # \sigma{t} = 0
2768 15         72 $sigma{$_} = 0 for @V;
2769 15         19 $sigma{$s} = 1;
2770              
2771 15         15 my %d; # d{t} = -1;
2772 15         85 $d{$_} = -1 for @V;
2773 15         21 $d{$s} = 0;
2774              
2775 15         19 my @Q; # queue (push, shift)
2776 15         27 push @Q, $s;
2777              
2778 15         24 while (@Q) {
2779 172         268 my $v = shift @Q;
2780 172         271 unshift @S, $v;
2781 172         289 for my $w ($g->successors($v)) {
2782             # w found for first time
2783 341 100       709 if ($d{$w} < 0) {
2784 157         282 push @Q, $w;
2785 157         254 $d{$w} = $d{$v} + 1;
2786             }
2787             # Shortest path to w via v
2788 341 100       733 if ($d{$w} == $d{$v} + 1) {
2789 173         227 $sigma{$w} += $sigma{$v};
2790 173         195 push @{ $P{$w} }, $v;
  173         484  
2791             }
2792             }
2793             }
2794              
2795 15         23 my %delta;
2796 15         97 $delta{$_} = 0 for @V;
2797              
2798 15         30 while (@S) {
2799 172         226 my $w = shift @S;
2800             $delta{$_} += $sigma{$_}/$sigma{$w} * (1 + $delta{$w})
2801 172         191 for @{ $P{$w} };
  172         406  
2802 172 100       488 $Cb{$w} += $delta{$w} if $w ne $s;
2803             }
2804             }
2805              
2806 1         13 return %Cb;
2807             }
2808              
2809             1;