File Coverage

blib/lib/Graph.pm
Criterion Covered Total %
statement 1564 1575 99.8
branch 624 722 86.4
condition 209 259 80.6
subroutine 307 307 100.0
pod 195 195 100.0
total 2899 3058 95.0


line stmt bran cond sub pod time code
1             package Graph;
2              
3 80     80   3357583 use strict;
  80         673  
  80         2362  
4 80     80   453 use warnings;
  80         193  
  80         3640  
5 80 50   80   6997 BEGIN { warnings->unimport('recursion') if $ENV{GRAPH_ALLOW_RECURSION} }
6              
7 20     20   116 sub __carp_confess { require Carp; Carp::confess(@_) }
  20         4006  
8             BEGIN {
9 80     80   2238 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   36240 use Graph::AdjacencyMap qw(:flags :fields);
  80         236  
  80         42147  
16              
17             our $VERSION = '0.9726';
18              
19             require 5.006; # Weak references are absolutely required.
20              
21             my @GRAPH_PROPS_COPIED = qw(
22             undirected refvertexed countvertexed multivertexed __stringified
23             hyperedged countedged multiedged
24             );
25             my $_empty_array = [];
26 1     1   7 sub _empty_array () { $_empty_array }
27              
28             my $can_deep_copy_Storable;
29             sub _can_deep_copy_Storable () {
30 20 100   20   1174 return $can_deep_copy_Storable if defined $can_deep_copy_Storable;
31 4 50       18 return $can_deep_copy_Storable = 0 if $] < 5.010; # no :load tag Safe 5.8
32 4         9 eval {
33 4         2804 require Storable;
34 4         13654 require B::Deparse;
35 4         78 Storable->VERSION(2.05);
36 4         86 B::Deparse->VERSION(0.61);
37             };
38 4         42 $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   652 if ($] >= 5.022) {
52 80         4790 $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 110 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   33565 use Graph::Attribute array => _A, map => 'graph';
  80         194  
  80         2518  
83              
84             sub stringify {
85 697     697 1 31732 my ($u, $h) = (&is_undirected, &is_hyperedged);
86 697 100       1895 my $e = $u ? '=' : '-';
87             my @edges = map join($e,
88 3150         10169 $u ? sort { "$a" cmp "$b" } @$_ :
89 697 100       1396 $h ? map '['.join(",", sort { "$a" cmp "$b" } @$_).']', @$_ :
  8 100       32  
90             @$_), &_edges05;
91 697         4694 my @s = sort @edges;
92 697         1574 push @s, sort { "$a" cmp "$b" } &isolated_vertices;
  106         344  
93 697         9141 join(",", @s);
94             }
95              
96             sub eq {
97 298     298 1 101832 "$_[0]" eq "$_[1]"
98             }
99              
100             sub boolify {
101 276     276 1 27717 1; # Important for empty graphs: they stringify to "", which is false.
102             }
103              
104             sub ne {
105 11     11 1 3716 "$_[0]" ne "$_[1]"
106             }
107              
108             use overload
109 80         592 '""' => \&stringify,
110             'bool' => \&boolify,
111             'eq' => \&eq,
112 80     80   94794 'ne' => \≠
  80         80830  
113              
114             sub _opt {
115 2691     2691   7823 my ($opt, $flags, %flags) = @_;
116 2691         7294 while (my ($flag, $FLAG) = each %flags) {
117 8073 100       14315 $$flags |= $FLAG if delete $opt->{$flag};
118 8073 50       26164 $$flags &= ~$FLAG if delete $opt->{"non$flag"};
119             }
120             }
121              
122             sub _opt_get {
123 6     6   12 my ($opt, $key, $var) = @_;
124 6 100       18 return if !exists $opt->{$key};
125 1         4 $$var = delete $opt->{$key};
126             }
127              
128             sub _opt_unknown {
129 1228     1228   2054 my ($opt) = @_;
130 1228 100       3861 return unless my @opt = keys %$opt;
131 6 100       20 __carp_confess sprintf
132 6         56 "@{[(caller(1))[3]]}: Unknown option%s: @{[map qq['$_'], sort @opt]}",
  6         80  
133             @opt > 1 ? 's' : '';
134             }
135              
136             sub _opt_from_existing {
137 106     106   236 my ($g) = @_;
138 106         155 my %existing;
139 106         391 $existing{$_}++ for grep $g->$_, @GRAPH_PROPS_COPIED;
140 106 50       275 $existing{unionfind}++ if $g->has_union_find;
141 106         423 %existing;
142             }
143              
144             sub _opt_to_vflags {
145 897     897   1948 my ($vflags, $opt) = (0, @_);
146 897         2815 _opt($opt, \$vflags,
147             countvertexed => _COUNT,
148             multivertexed => _MULTI,
149             refvertexed => _REF,
150             refvertexed_stringified => _REFSTR ,
151             __stringified => _STR,
152             );
153 897         1861 $vflags;
154             }
155              
156             sub _opt_to_eflags {
157 897     897   1729 my ($eflags, $opt) = (0, @_);
158 897 100       2347 $opt->{undirected} = !delete $opt->{directed} if exists $opt->{directed};
159 897         2528 _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 182049 my ($class, @args) = @_;
169 897         1652 my $gflags = 0;
170 897         2472 my %opt = _get_options( \@args );
171              
172 897 100 66     3239 %opt = (_opt_from_existing($class), %opt) # allow overrides
173             if ref $class && $class->isa('Graph');
174              
175 897         2173 my $vflags = _opt_to_vflags(\%opt);
176 897         2200 my ($eflags, $is_hyper) = _opt_to_eflags(\%opt);
177              
178 897         2942 _opt(\%opt, \$gflags,
179             unionfind => _UNIONFIND,
180             );
181              
182 897         1525 my @V;
183 897 100       2073 if ($opt{vertices}) {
184             __carp_confess "Graph: vertices should be an array ref"
185 95 50       343 if ref $opt{vertices} ne 'ARRAY';
186 95         153 @V = @{ delete $opt{vertices} };
  95         298  
187             }
188              
189 897         1295 my @E;
190 897 100       1860 if ($opt{edges}) {
191             __carp_confess "Graph: edges should be an array ref of array refs"
192 23 50       71 if ref $opt{edges} ne 'ARRAY';
193 23         38 @E = @{ delete $opt{edges} };
  23         51  
194             }
195              
196 897         2307 _opt_unknown(\%opt);
197              
198 894 100 100     2594 __carp_confess "Graph: both countvertexed and multivertexed"
199             if ($vflags & _COUNT) && ($vflags & _MULTI);
200              
201 893 100 100     2236 __carp_confess "Graph: both countedged and multiedged"
202             if ($eflags & _COUNT) && ($eflags & _MULTI);
203              
204 892   66     3376 my $g = bless [ ], ref $class || $class;
205              
206 892         5854 $g->[ _F ] = $gflags;
207 892         1436 $g->[ _G ] = 0;
208 892         2091 $g->[ _V ] = _make_v($vflags);
209 892         1977 $g->[ _E ] = _make_e($is_hyper, $eflags);
210              
211 892 100       2517 $g->add_vertices(@V) if @V;
212              
213 892 50       2196 __carp_confess "Graph: edges should be array refs"
214             if grep ref $_ ne 'ARRAY', @E;
215 892         2755 $g->add_edges(@E);
216              
217 892 100       1908 $g->[ _U ] = do { require Graph::UnionFind; Graph::UnionFind->new }
  5         1955  
  5         33  
218             if $gflags & _UNIONFIND;
219              
220 892         4186 return $g;
221             }
222              
223             sub _make_v {
224 892     892   1571 my ($vflags) = @_;
225 892 100       2513 $vflags ? _am_heavy($vflags, 1) : _am_light($vflags, 1);
226             }
227              
228             sub _make_e {
229 892     892   1784 my ($is_hyper, $eflags) = @_;
230 892 100 100     3860 ($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   44156 require Graph::AdjacencyMap::Light;
237 1621         5163 Graph::AdjacencyMap::Light->_new(@_);
238             }
239              
240             sub _am_heavy {
241 163     163   548 Graph::AdjacencyMap->_new(@_);
242             }
243              
244 1546     1546 1 11172 sub countvertexed { $_[0]->[ _V ]->_is_COUNT }
245 5558     5558 1 15715 sub multivertexed { $_[0]->[ _V ]->_is_MULTI }
246 146     146 1 596 sub refvertexed { $_[0]->[ _V ]->_is_REF }
247 1     1 1 20 sub refvertexed_stringified { $_[0]->[ _V ]->_is_REFSTR }
248 140     140   396 sub __stringified { $_[0]->[ _V ]->_is_STR }
249              
250 575     575 1 4062 sub countedged { $_[0]->[ _E ]->_is_COUNT }
251 38005     38005 1 95176 sub multiedged { $_[0]->[ _E ]->_is_MULTI }
252 79403     79403 1 193858 sub hyperedged { !$_[0]->[ _E ]->[ _arity ] }
253 47486     47486 1 107916 sub undirected { $_[0]->[ _E ]->_is_UNORD }
254              
255 22155     22155 1 407646 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 20959     20959 1 34515 sub has_union_find { $_[0]->[ _U ] }
270              
271             sub add_vertex {
272 2101 100   2101 1 22421 __carp_confess "Graph::add_vertex: use add_vertices for more than one vertex" if @_ != 2;
273 2096 100       5488 __carp_confess "Graph::add_vertex: undef vertex" if grep !defined, @_;
274 2095         4366 goto &add_vertices;
275             }
276              
277             sub has_vertex {
278 2586     2586 1 57033 my $g = $_[0];
279 2586         3605 my $V = $g->[ _V ];
280 2586 100       6036 return defined $V->has_path($_[1]) if ($V->[ _f ] & _REF);
281 2124         6338 exists $V->[ _pi ]->{ $_[1] };
282             }
283              
284             sub _vertices05 {
285 2575     2575   3576 my $g = $_[0];
286 2575         6856 $g->[ _V ]->paths;
287             }
288              
289             sub vertices {
290 1405     1405 1 39402 my $g = $_[0];
291 1405         2264 my @v = &_vertices05;
292 1405 100 100     2883 return @v if !(&is_multivertexed || &is_countvertexed);
293 14 100       38 return map +(($_) x $g->get_vertex_count($_)), @v if wantarray;
294 12         20 my $V = 0;
295 12         34 $V += $g->get_vertex_count($_) for @v;
296 12         50 return $V;
297             }
298              
299             *unique_vertices = \&_vertices05;
300              
301             sub has_vertices {
302 22     22 1 3849 my $g = shift;
303 22         79 scalar $g->[ _V ]->has_any_paths;
304             }
305              
306             sub add_edge {
307 15560 100   15560 1 67605 &expect_hyperedged, &expect_undirected if @_ != 3;
308 15558         46910 $_[0]->add_edges([ @_[1..$#_] ]);
309             }
310              
311             sub _vertex_ids_ensure {
312 69     69   132 push @_, 1;
313 69         155 goto &_vertex_ids_maybe_ensure;
314             }
315              
316             sub _vertex_ids_ensure_multi {
317 56     56   104 my $id = pop;
318 56         106 my @i = &_vertex_ids_ensure;
319 56         123 push @_, $id;
320 56 50       184 @i ? (@i, $id) : ();
321             }
322              
323             sub _vertex_ids {
324 38137     38137   53312 push @_, 0;
325 38137         60412 goto &_vertex_ids_maybe_ensure;
326             }
327              
328             sub _vertex_ids_multi {
329 285     285   527 my $id = pop;
330 285         468 my @i = &_vertex_ids;
331 285         493 push @_, $id;
332 285 100       753 @i ? (@i, $id) : ();
333             }
334              
335             sub _vertex_ids_maybe_ensure {
336 38206     38206   48310 my $ensure = pop;
337 38206         74619 my ($g, @args) = @_;
338 38206 50       93787 __carp_confess "Graph: given undefined vertex" if grep !defined, @args;
339 38206         52768 my $V = $g->[ _V ];
340 38206   100     53345 my $deep = &is_hyperedged && &is_directed;
341 38206 100 100     112973 return $V->get_ids_by_paths(\@args, $ensure, $deep) if ($V->[ _f ] & _REF) or $deep;
342 37862         48191 my $pi = $V->[ _pi ];
343 37862         83126 my @non_exist = grep !exists $pi->{ $_ }, @args;
344 37862 100 100     107912 return if !$ensure and @non_exist;
345 36569 100       62002 $V->get_ids_by_paths(\@non_exist, 1) if @non_exist;
346 36569         103067 @$pi{ @args };
347             }
348              
349             sub has_edge {
350 20001     20001 1 75913 my $g = $_[0];
351 20001         26832 my $E = $g->[ _E ];
352 20001         34413 my ($Ef, $Ea) = @$E[ _f, _arity ];
353 20001 100 100     64183 return 0 if $Ea and @_ != $Ea + 1;
354 19997         30170 my $directed = &is_directed;
355 19997   100     30692 my $deep = &is_hyperedged && $directed;
356 19997 100       31002 return 0 if (my @i = &_vertex_ids) != @_ - 1;
357 18586 50       32507 return defined $E->has_path($directed ? \@i : [ map [ sort @$_ ], @i ]) if $deep;
    100          
358 18575 100       50104 @i = sort @i if !$directed;
359 18575         79121 exists $E->[ _pi ]{ "@i" };
360             }
361              
362             sub any_edge {
363 22     22 1 1375 my ($g, @args) = @_;
364 22         39 my $E = $g->[ _E ];
365 22         32 my $V = $g->[ _V ];
366 22 100       113 return 0 if (my @i = $V->get_ids_by_paths(\@args)) != @args;
367 16         58 $E->has_successor(@i);
368             }
369              
370             sub _edges05 {
371 1308     1308   2347 my $g = $_[0];
372 1308         3567 my @e = $g->[ _E ]->paths;
373 1308 100       3199 return @e if !wantarray;
374 1253   100     2733 $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 3312 my $g = $_[0];
381 367         671 my @e = &_edges05;
382 367 100 100     1028 return @e if !(&is_multiedged || &is_countedged);
383 28 100       125 return map +(($_) x $g->get_edge_count(@$_)), @e if wantarray;
384 14         27 my $E = 0;
385 14         44 $E += $g->get_edge_count(@$_) for @e;
386 14         68 return $E;
387             }
388              
389             sub has_edges {
390 7     7 1 888 scalar $_[0]->[ _E ]->has_any_paths;
391             }
392              
393             ###
394             # by_id
395             #
396              
397             sub add_vertex_by_id {
398 14     14 1 316 &expect_multivertexed;
399 13         32 my ($g, $v, $id) = @_;
400 13         25 my $V = $g->[ _V ];
401 13 100       46 return $g if $V->has_path_by_multi_id( my @args = ($v, $id) );
402 12         42 my ($i) = $V->set_path_by_multi_id( @args );
403 12 50       29 $g->[ _U ]->add($i) if &has_union_find;
404 12         24 $g->[ _G ]++;
405 12         27 return $g;
406             }
407              
408             sub add_vertex_get_id {
409 6     6 1 3583 &expect_multivertexed;
410 6         18 my ($g, $v) = @_;
411 6         16 my ($i, $multi_id) = $g->[ _V ]->set_path_by_multi_id( $v, _GEN_ID );
412 6 50       66 $g->[ _U ]->add($i) if &has_union_find;
413 6         43 $g->[ _G ]++;
414 6         28 return $multi_id;
415             }
416              
417             sub has_vertex_by_id {
418 100     100 1 3898 &expect_multivertexed;
419 99         195 my ($g, $v, $id) = @_;
420 99         251 $g->[ _V ]->has_path_by_multi_id( $v, $id );
421             }
422              
423             sub delete_vertex_by_id {
424 4     4 1 537 &expect_multivertexed;
425 3         9 &expect_non_unionfind;
426 3         7 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         8 $g->[ _V ]->del_path_by_multi_id( $v, $id );
431 2         3 $g->[ _G ]++;
432 2         12 return $g;
433             }
434              
435             sub get_multivertex_ids {
436 14     14 1 2893 &expect_multivertexed;
437 13         24 my $g = shift;
438 13         43 $g->[ _V ]->get_multi_ids( @_ );
439             }
440              
441             sub add_edge_by_id {
442 57     57 1 170 &expect_multiedged;
443 56         99 my $g = $_[0];
444 56         111 my @i = &_vertex_ids_ensure_multi;
445 56         97 my $id = pop @i;
446 56 100       128 @i = sort @i if &is_undirected;
447 56         198 $g->[ _E ]->set_path_by_multi_id( \@i, $id );
448 56         105 $g->[ _G ]++;
449 56 100       128 $g->[ _U ]->union(\@i) if &has_union_find;
450 56         144 return $g;
451             }
452              
453             sub add_edge_get_id {
454 13     13 1 2887 &expect_multiedged;
455 13         24 my $g = $_[0];
456 13         22 my @i = &_vertex_ids_ensure;
457 13 100       30 @i = sort @i if &is_undirected;
458 13         47 my (undef, $id) = $g->[ _E ]->set_path_by_multi_id( \@i, _GEN_ID );
459 13         27 $g->[ _G ]++;
460 13 50       28 $g->[ _U ]->union(\@i) if &has_union_find;
461 13         48 return $id;
462             }
463              
464             sub has_edge_by_id {
465 153     153 1 1573 &expect_multiedged;
466 152         244 my $g = $_[0];
467 152         260 my @i = &_vertex_ids_multi;
468 152 100       377 return 0 if @i < @_ - 2;
469 134         209 my $id = pop @i;
470 134 100       222 @i = sort @i if &is_undirected;
471 134         330 $g->[ _E ]->has_path_by_multi_id( \@i, $id );
472             }
473              
474             sub delete_edge_by_id {
475 5     5 1 588 &expect_multiedged;
476 4         11 &expect_non_unionfind;
477 4         8 my $g = $_[0];
478 4         7 my $E = $g->[ _E ];
479 4         8 my @i = &_vertex_ids_multi;
480 4 50       20 return if @i < @_ - 2;
481 4         6 my $id = pop @i;
482 4 50       12 @i = sort @i if &is_undirected;
483 4 100       14 return unless $E->has_path_by_multi_id( my @args = (\@i, $id) );
484 3         18 $E->del_path_by_multi_id( @args );
485 3         7 $g->[ _G ]++;
486 3         12 return $g;
487             }
488              
489             sub get_multiedge_ids {
490 35     35 1 3046 &expect_multiedged;
491 34 50       76 return unless @_-1 == (my @i = &_vertex_ids);
492 34         133 $_[0]->[ _E ]->get_multi_ids( \@i );
493             }
494              
495             ###
496             # Neighbourhood.
497             #
498              
499             sub _edges_at {
500 189 100   189   734 goto &_edges_from if &is_undirected;
501 110         4180 require Set::Object;
502 110 100       46491 Set::Object->new(&_edges_from, &_edges_to)->${ wantarray ? \'members' : \'size' };
  110         671  
503             }
504              
505             sub _edges_from {
506 476     476   1035 my ($g, @args) = @_;
507 476         973 my ($V, $E) = @$g[ _V, _E ];
508 476 100 100     919 return if (my @i = $V->get_ids_by_paths(\@args, &is_hyperedged && &is_directed)) != @args;
509 472         1439 $E->paths_from(@i);
510             }
511              
512             sub _edges_to {
513 326 50   326   546 goto &_edges_from if &is_undirected;
514 326         726 my ($g, @args) = @_;
515 326         614 my ($V, $E) = @$g[ _V, _E ];
516 326 100 66     620 return if (my @i = $V->get_ids_by_paths(\@args, &is_hyperedged && &is_directed)) != @args;
517 323         916 $E->paths_to(@i);
518             }
519              
520             sub edges_at {
521 12 100   12 1 7270 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 8394 goto &_edges_from if !wantarray;
527 287   66     533 $_[0]->[ _V ]->get_paths_by_ids([ &_edges_from ], &is_hyperedged && &is_directed);
528             }
529              
530             sub edges_to {
531 246 100   246 1 5805 goto &edges_from if &is_undirected;
532 216 50       410 goto &_edges_to if !wantarray;
533 216   66     352 $_[0]->[ _V ]->get_paths_by_ids([ &_edges_to ], &is_hyperedged && &is_directed);
534             }
535              
536             sub successors {
537 25617     25617 1 48060 my ($g, @args) = @_;
538 25617         46755 my ($V, $E) = @$g[ _V, _E ];
539 25617 100       56491 return if (my @i = $V->get_ids_by_paths(\@args)) != @args;
540 25611         59297 my @v = $E->successors(@i);
541 25611 100       69599 return @v if !wantarray;
542 21512         58298 map @$_, $V->get_paths_by_ids([ \@v ]);
543             }
544              
545             sub predecessors {
546 5057 100   5057 1 11341 goto &successors if &is_undirected;
547 1598         3184 my ($g, @args) = @_;
548 1598         2919 my ($V, $E) = @$g[ _V, _E ];
549 1598 100       3787 return if (my @i = $V->get_ids_by_paths(\@args)) != @args;
550 1594         3889 my @v = $E->predecessors(@i);
551 1594 100       7779 return @v if !wantarray;
552 195         545 map @$_, $V->get_paths_by_ids([ \@v ]);
553             }
554              
555             sub _cessors_by_radius {
556 166     166   397 my ($radius, $method, $self_only_if_loop) = splice @_, -3, 3;
557 166         334 my ($g, @v) = @_;
558 166         693 require Set::Object;
559 166         1111 my ($init, $next) = map Set::Object->new(@v), 1..2;
560 166 100       561 my $self = Set::Object->new(grep $g->has_edge($_, $_), @v) if $self_only_if_loop;
561 166         632 my ($got, $found) = map Set::Object->new, 1..2;
562 166   100     550 while (!defined $radius or $radius-- > 0) {
563 335         1265 $found->insert($g->$method($next->members));
564 335         915 $next = $found->difference($got);
565 335 100       9241 last if $next->is_null; # Leave if no new found.
566 211         794 $got->insert($next->members);
567 211         726 $found->clear;
568             }
569 166 100       395 $got->remove($init->difference($self)->members) if $self_only_if_loop;
570 166 100       2164 $got->${ wantarray ? \'members' : \'size' };
  166         2030  
571             }
572              
573             sub all_successors {
574 37     37 1 5684 &expect_directed;
575 37         93 push @_, undef, 'successors', 0;
576 37         89 goto &_cessors_by_radius;
577             }
578              
579             sub successors_by_radius {
580 9     9 1 25 &expect_directed;
581 9         23 push @_, 'successors', 0;
582 9         17 goto &_cessors_by_radius;
583             }
584              
585             sub all_predecessors {
586 18     18 1 5531 &expect_directed;
587 18         48 push @_, undef, 'predecessors', 0;
588 18         45 goto &_cessors_by_radius;
589             }
590              
591             sub predecessors_by_radius {
592 22     22 1 7599 &expect_directed;
593 22         50 push @_, 'predecessors', 0;
594 22         59 goto &_cessors_by_radius;
595             }
596              
597             sub neighbours_by_radius {
598 26     26 1 6451 push @_, 'neighbours', 1;
599 26         71 goto &_cessors_by_radius;
600             }
601             *neighbors_by_radius = \&neighbours_by_radius;
602              
603             sub neighbours {
604 219     219 1 8855 require Set::Object;
605 219         389 my $s = Set::Object->new(&successors);
606 219 100       546 $s->insert(&predecessors) if &is_directed;
607 219 100       354 $s->${ wantarray ? \'members' : \'size' };
  219         1423  
608             }
609             *neighbors = \&neighbours;
610              
611             sub all_neighbours {
612 54     54 1 11417 push @_, undef, 'neighbours', 1;
613 54         147 goto &_cessors_by_radius;
614             }
615             *all_neighbors = \&all_neighbours;
616              
617             sub all_reachable {
618 36 100   36 1 11202 &directed ? goto &all_successors : goto &all_neighbors;
619             }
620              
621             sub reachable_by_radius {
622 17 100   17 1 31 &directed ? goto &successors_by_radius : goto &neighbors_by_radius;
623             }
624              
625             sub delete_edge {
626 452     452 1 3294 &expect_non_unionfind;
627 451         642 my $g = $_[0];
628 451 100       648 return $g if (my @i = &_vertex_ids) != @_ - 1;
629 446 100       742 @i = sort @i if &is_undirected;
630 446 100 100     1492 return $g unless @i and $g->[ _E ]->del_path( \@i );
631 437         750 $g->[ _G ]++;
632 437         697 return $g;
633             }
634              
635             sub delete_vertex {
636 184     184 1 5517 &expect_non_unionfind;
637 184         314 my $g = $_[0];
638 184 100       463 return $g if @_ != 2;
639 183         275 my $V = $g->[ _V ];
640 183 100       609 return $g unless defined $V->has_path($_[1]);
641             # TODO: _edges_at is excruciatingly slow (rt.cpan.org 92427)
642 177         378 my $E = $g->[ _E ];
643 177         365 $E->del_path( $_ ) for &_edges_at;
644 177         808 $V->del_path($_[1]);
645 177         353 $g->[ _G ]++;
646 177         681 return $g;
647             }
648              
649             sub get_vertex_count {
650 63     63 1 11228 my $g = shift;
651 63         564 $g->[ _V ]->_get_path_count( @_ );
652             }
653              
654             sub get_edge_count {
655 980     980 1 9674 my $g = $_[0];
656 980 100       1448 return 0 if (my @i = &_vertex_ids) != @_ - 1;
657 964 100       1582 @i = sort @i if &is_undirected;
658 964         2333 $g->[ _E ]->_get_path_count( \@i );
659             }
660              
661             sub delete_vertices {
662 6     6 1 16 &expect_non_unionfind;
663 6         10 my $g = shift;
664 6         14 while (@_) {
665 8         15 my $v = shift @_;
666 8         17 $g->delete_vertex($v);
667             }
668 6         15 return $g;
669             }
670              
671             sub delete_edges {
672 6     6 1 456 &expect_non_unionfind;
673 6         11 my $g = shift;
674 6         16 while (@_) {
675 8         25 my ($u, $v) = splice @_, 0, 2;
676 8         18 $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 357 my $g = $_[0];
687 232 50 33     522 return undef unless @_ > 1 && &has_vertex;
688 232         360 my $in = 0;
689 232         389 $in += $g->get_edge_count( @$_ ) for &edges_to;
690 232 100 100     446 $in++ if &is_undirected and &is_self_loop_vertex;
691 232         671 return $in;
692             }
693              
694             sub out_degree {
695 208     208 1 325 my $g = $_[0];
696 208 50 33     459 return undef unless @_ > 1 && &has_vertex;
697 208         369 my $out = 0;
698 208         321 $out += $g->get_edge_count( @$_ ) for &edges_from;
699 208 100 100     386 $out++ if &is_undirected and &is_self_loop_vertex;
700 208         686 return $out;
701             }
702              
703             sub _total_degree {
704 42 50 33 42   121 return undef unless @_ > 1 && &has_vertex;
705 42 100       82 &is_undirected ? &in_degree : &in_degree - &out_degree;
706             }
707              
708             sub degree {
709 38 100   38 1 143 goto &_total_degree if @_ > 1;
710 2 100       6 return 0 if &is_directed;
711 1         3 my $g = $_[0];
712 1         3 my $total = 0;
713 1         4 $total += $g->_total_degree( $_ ) for &_vertices05;
714 1         5 return $total;
715             }
716              
717             *vertex_degree = \°ree;
718              
719             sub is_sink_vertex {
720 36 50   36 1 87 return 0 unless @_ > 1;
721 36 100       51 &successors == 0 && &predecessors > 0;
722             }
723              
724             sub is_source_vertex {
725 36 50   36 1 79 return 0 unless @_ > 1;
726 36 100       61 &predecessors == 0 && &successors > 0;
727             }
728              
729             sub is_successorless_vertex {
730 36 50   36 1 6298 return 0 unless @_ > 1;
731 36         68 &successors == 0;
732             }
733              
734             sub is_predecessorless_vertex {
735 36 50   36 1 6245 return 0 unless @_ > 1;
736 36         90 &predecessors == 0;
737             }
738              
739             sub is_successorful_vertex {
740 36 50   36 1 6316 return 0 unless @_ > 1;
741 36         72 &successors > 0;
742             }
743              
744             sub is_predecessorful_vertex {
745 36 50   36 1 6314 return 0 unless @_ > 1;
746 36         62 &predecessors > 0;
747             }
748              
749             sub is_isolated_vertex {
750 4690 50   4690 1 10067 return 0 unless @_ > 1;
751 4690 100       7123 &predecessors == 0 && &successors == 0;
752             }
753              
754             sub is_interior_vertex {
755 36 50   36 1 75 return 0 unless @_ > 1;
756 36         59 my $s = &successors;
757 36 100       61 $s-- if my $isl = &is_self_loop_vertex;
758 36 100       115 return 0 if $s == 0;
759 23 100       35 return $s > 0 if &is_undirected;
760 8         16 my $p = &predecessors;
761 8 100       17 $p-- if $isl;
762 8         28 $p > 0;
763             }
764              
765             sub is_exterior_vertex {
766 36 50   36 1 75 return 0 unless @_ > 1;
767 36 100       57 &predecessors == 0 || &successors == 0;
768             }
769              
770             sub is_self_loop_vertex {
771 108 50   108 1 262 return 0 unless @_ > 1;
772 108 100       171 return 1 if grep $_ eq $_[1], &successors; # @todo: multiedges
773 86         290 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   456288 no strict 'refs';
  80         232  
  80         58154  
789             (my $m = $p) =~ s/^is_(.*)ex$/${1}ices/;
790 782     782   11573 *$m = sub { my $g = $_[0]; grep $g->$p($_), &_vertices05 };
  782         1387  
791             }
792              
793             ###
794             # Paths and cycles.
795             #
796              
797             sub add_path {
798 126     126 1 564 my $g = shift;
799 126         196 my $u = shift;
800 126         176 my @edges;
801 126         267 while (@_) {
802 317         470 my $v = shift;
803 317         540 push @edges, [ $u, $v ];
804 317         598 $u = $v;
805             }
806 126         344 $g->add_edges(@edges);
807 126         357 return $g;
808             }
809              
810             sub delete_path {
811 4     4 1 18 &expect_non_unionfind;
812 4         6 my $g = shift;
813 4         9 my $u = shift;
814 4         13 while (@_) {
815 10         15 my $v = shift;
816 10         25 $g->delete_edge($u, $v);
817 10         23 $u = $v;
818             }
819 4         9 return $g;
820             }
821              
822             sub has_path {
823 20     20 1 884 my $g = shift;
824 20         38 my $u = shift;
825 20         48 while (@_) {
826 43         97 my $v = shift;
827 43 100       82 return 0 unless $g->has_edge($u, $v);
828 30         70 $u = $v;
829             }
830 7         34 return $g;
831             }
832              
833             sub add_cycle {
834 39     39 1 377 push @_, $_[1];
835 39         103 goto &add_path;
836             }
837              
838             sub delete_cycle {
839 2     2 1 7 &expect_non_unionfind;
840 2         4 push @_, $_[1];
841 2         7 goto &delete_path;
842             }
843              
844             sub has_cycle {
845 9 100   9 1 848 return 0 if @_ == 1;
846 8         18 push @_, $_[1];
847 8         21 goto &has_path;
848             }
849              
850             *has_this_cycle = \&has_cycle;
851              
852             sub has_a_cycle {
853 17     17 1 622 my $g = shift;
854 17         933 require Graph::Traversal::DFS;
855 17         72 my $t = Graph::Traversal::DFS->new($g, has_a_cycle => 1, @_);
856 17         52 $t->dfs;
857 17         57 return $t->get_state('has_a_cycle');
858             }
859              
860             sub find_a_cycle {
861 2     2 1 22 require Graph::Traversal::DFS;
862 2         8 my @r = ( back_edge => \&Graph::Traversal::find_a_cycle);
863 2 100       7 push @r,
864             down_edge => \&Graph::Traversal::find_a_cycle
865             if &is_undirected;
866 2         6 my $g = shift;
867 2         22 my $t = Graph::Traversal::DFS->new($g, @r, @_);
868 2         14 $t->dfs;
869 2 50       15 $t->has_state('a_cycle') ? @{ $t->get_state('a_cycle') } : ();
  2         21  
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   744 no strict 'refs';
  80         220  
  80         35801  
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 17889     17889   60142 &$expect_non; push @_, 0, $entity, $offset, $args_non, $is_vertex; goto &$func;
  17887         45908  
  17887         41540  
903             };
904             *{$m.'_by_id'} = sub {
905 206     206   4538 &$expect_yes; push @_, 1, $entity, $offset, $args_yes, $is_vertex; goto &$func;
  206         628  
  206         590  
906             };
907             }
908             }
909              
910             sub _munge_args {
911 18049     18049   34482 my ($is_vertex, $is_multi, $is_undirected, @args) = @_;
912 18049 100 100     56092 return \@args if !$is_vertex and !$is_undirected and !$is_multi;
      100        
913 15928 100 100     81176 return [ sort @args ] if !$is_vertex and !$is_multi;
914 1673 100       4032 return @args if $is_vertex;
915 129         222 my $id = pop @args;
916 129 100       409 ($is_undirected ? [ sort @args ] : \@args, $id);
917             }
918              
919             sub _set_attribute {
920 4691     4691   11440 my ($is_multi, $entity, $offset, $args, $is_vertex) = splice @_, -5, 5;
921 4691         7369 my $value = pop;
922 4691         6598 my $attr = pop;
923 80     80   711 no strict 'refs';
  80         164  
  80         15883  
924 4691 100       6205 &{ 'add_' . $entity . ($is_multi ? '_by_id' : '') } unless &{ 'has_' . $entity . ($is_multi ? '_by_id' : '') };
  4633 100       15282  
  4691 100       14467  
925 4691 100       13257 my @args = ($entity eq 'edge') ? &$args : @_[1..$#_];
926 4691         8397 @args = _munge_args($is_vertex, $is_multi, &is_undirected, @args);
927 4691         12885 $_[0]->[ $offset ]->_set_path_attr( @args, $attr, $value );
928             }
929              
930             sub _set_attributes {
931 1113     1113   2555 my ($is_multi, $entity, $offset, $args, $is_vertex) = splice @_, -5, 5;
932 1113         1720 my $attr = pop;
933 80     80   599 no strict 'refs';
  80         162  
  80         14215  
934 1113 100       1370 &{ 'add_' . $entity . ($is_multi ? '_by_id' : '') } unless &{ 'has_' . $entity . ($is_multi ? '_by_id' : '') };
  1025 100       2947  
  1113 100       3404  
935 1113 100       3462 my @args = ($entity eq 'edge') ? &$args : @_[1..$#_];
936 1113         1934 @args = _munge_args($is_vertex, $is_multi, &is_undirected, @args);
937 1113         2812 $_[0]->[ $offset ]->_set_path_attrs( @args, $attr );
938             }
939              
940             sub _has_attributes {
941 40     40   115 my ($is_multi, $entity, $offset, $args, $is_vertex) = splice @_, -5, 5;
942 80     80   618 no strict 'refs';
  80         206  
  80         13314  
943 40 100       73 return 0 unless &{ 'has_' . $entity . ($is_multi ? '_by_id' : '') };
  40 50       177  
944 40 100       149 my @args = ($entity eq 'edge') ? &$args : @_[1..$#_];
945 40         105 @args = _munge_args($is_vertex, $is_multi, &is_undirected, @args);
946 40         125 $_[0]->[ $offset ]->_has_path_attrs( @args );
947             }
948              
949             sub _has_attribute {
950 24     24   91 my ($is_multi, $entity, $offset, $args, $is_vertex) = splice @_, -5, 5;
951 24         64 my $attr = pop;
952 80     80   635 no strict 'refs';
  80         238  
  80         12053  
953 24 100       40 return 0 unless &{ 'has_' . $entity . ($is_multi ? '_by_id' : '') };
  24 100       122  
954 20 100       95 my @args = ($entity eq 'edge') ? &$args : @_[1..$#_];
955 20         136 @args = _munge_args($is_vertex, $is_multi, &is_undirected, @args);
956 20         81 $_[0]->[ $offset ]->_has_path_attr( @args, $attr );
957             }
958              
959             sub _get_attributes {
960 636     636   1508 my ($is_multi, $entity, $offset, $args, $is_vertex) = splice @_, -5, 5;
961 80     80   624 no strict 'refs';
  80         157  
  80         12819  
962 636 100       931 return undef unless &{ 'has_' . $entity . ($is_multi ? '_by_id' : '') };
  636 100       1884  
963 634 100       1540 my @args = ($entity eq 'edge') ? &$args : @_[1..$#_];
964 634         1146 @args = _munge_args($is_vertex, $is_multi, &is_undirected, @args);
965 634         1708 scalar $_[0]->[ $offset ]->_get_path_attrs( @args );
966             }
967              
968             sub _get_attribute {
969 11545     11545   26959 my ($is_multi, $entity, $offset, $args, $is_vertex) = splice @_, -5, 5;
970 80     80   638 no strict 'refs';
  80         218  
  80         14699  
971 11545         18140 my $attr = pop;
972 11545 100       13811 return undef unless &{ 'has_' . $entity . ($is_multi ? '_by_id' : '') };
  11545 100       32414  
973 11507 100       25917 my @args = ($entity eq 'edge') ? &$args : @_[1..$#_];
974 11507         19126 @args = _munge_args($is_vertex, $is_multi, &is_undirected, @args);
975 11507         30018 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         204  
  80         12151  
981 12 100       28 return unless &{ 'has_' . $entity . ($is_multi ? '_by_id' : '') };
  12 50       54  
982 12 100       53 my @args = ($entity eq 'edge') ? &$args : @_[1..$#_];
983 12         32 @args = _munge_args($is_vertex, $is_multi, &is_undirected, @args);
984 12         51 $_[0]->[ $offset ]->_get_path_attr_names( @args );
985             }
986              
987             sub _get_attribute_values {
988 12     12   41 my ($is_multi, $entity, $offset, $args, $is_vertex) = splice @_, -5, 5;
989 80     80   593 no strict 'refs';
  80         199  
  80         12096  
990 12 100       27 return unless &{ 'has_' . $entity . ($is_multi ? '_by_id' : '') };
  12 50       55  
991 12 100       51 my @args = ($entity eq 'edge') ? &$args : @_[1..$#_];
992 12         31 @args = _munge_args($is_vertex, $is_multi, &is_undirected, @args);
993 12         45 $_[0]->[ $offset ]->_get_path_attr_values( @args );
994             }
995              
996             sub _delete_attributes {
997 8     8   31 my ($is_multi, $entity, $offset, $args, $is_vertex) = splice @_, -5, 5;
998 80     80   579 no strict 'refs';
  80         259  
  80         12954  
999 8 100       17 return undef unless &{ 'has_' . $entity . ($is_multi ? '_by_id' : '') };
  8 50       44  
1000 8 100       42 my @args = ($entity eq 'edge') ? &$args : @_[1..$#_];
1001 8         22 @args = _munge_args($is_vertex, $is_multi, &is_undirected, @args);
1002 8         35 $_[0]->[ $offset ]->_del_path_attrs( @args );
1003             }
1004              
1005             sub _delete_attribute {
1006 12     12   56 my ($is_multi, $entity, $offset, $args, $is_vertex) = splice @_, -5, 5;
1007 12         32 my $attr = pop;
1008 80     80   590 no strict 'refs';
  80         195  
  80         87808  
1009 12 100       24 return undef unless &{ 'has_' . $entity . ($is_multi ? '_by_id' : '') };
  12 50       68  
1010 12 100       66 my @args = ($entity eq 'edge') ? &$args : @_[1..$#_];
1011 12         34 @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 7093 my ($g, @v) = @_;
1017 2268 100       3656 if (&is_multivertexed) {
1018 2         14 $g->add_vertex_by_id($_, _GEN_ID) for @v;
1019 2         6 return $g;
1020             }
1021 2266         5946 my @i = $g->[ _V ]->set_paths(@v);
1022 2266         3461 $g->[ _G ]++;
1023 2266 100       3625 return $g if !&has_union_find;
1024 5         24 $g->[ _U ]->add(@i);
1025 5         11 $g;
1026             }
1027              
1028             sub add_edges {
1029 17809     17809 1 34396 my ($g, @args) = @_;
1030 17809         22251 my @edges;
1031 17809         35347 while (defined(my $u = shift @args)) {
1032 32181 100       82683 push @edges, ref $u eq 'ARRAY' ? $u : @args ? [ $u, shift @args ]
    100          
1033             : __carp_confess "Graph::add_edges: missing end vertex";
1034             }
1035 17808 100       28683 if (&is_multiedged) {
1036 52         144 $g->add_edge_by_id(@$_, _GEN_ID) for @edges;
1037 52         152 return $g;
1038             }
1039 17756         26957 my $uf = &has_union_find;
1040 17756   100     27855 my $deep = &is_hyperedged && &is_directed;
1041 17756 100       54687 my @paths = $g->[ _V ]->get_ids_by_paths(\@edges, 1, 1 + ($deep ? 1 : 0));
1042 17756 100       30498 @paths = map [ sort @$_ ], @paths if &is_undirected;
1043 17756         50654 $g->[ _E ]->set_paths( @paths );
1044 17756 100       31239 $uf->union(@paths) if $uf;
1045 17756         24276 $g->[ _G ]++;
1046 17756         48695 return $g;
1047             }
1048              
1049             sub rename_vertex {
1050 24     24 1 90 my $g = shift;
1051 24         68 $g->[ _V ]->rename_path(@_);
1052 24         49 return $g;
1053             }
1054              
1055             sub rename_vertices {
1056 3     3 1 1087 my ($g, $code) = @_;
1057 3         4 my %seen;
1058             $g->rename_vertex($_, $code->($_))
1059 3         12 for grep !$seen{$_}++, $g->[ _V ]->paths;
1060 3         9 return $g;
1061             }
1062              
1063             sub as_hashes {
1064 11     11 1 11294 my ($g) = @_;
1065 11         26 my (%v, %e, @e);
1066 11         33 my ($is_hyper, $is_directed)= (&is_hyperedged, &is_directed);
1067 11 100       31 if (&is_multivertexed) {
1068 2         10 for my $v ($g->unique_vertices) {
1069 4   50     11 $v{$v} = {
1070             map +($_ => $g->get_vertex_attributes_by_id($v, $_) || {}),
1071             $g->get_multivertex_ids($v)
1072             };
1073             }
1074             } else {
1075 9   100     42 %v = map +($_ => $g->get_vertex_attributes($_) || {}), $g->unique_vertices;
1076             }
1077 11         43 my $multi_e = &is_multiedged;
1078 11         61 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     119 : %{ $g->get_edge_attributes(@$e)||{} }
  40 100       87  
1084             };
1085 53 100       147 if ($is_hyper) {
1086 12         31 my %h = (attributes => $edge_attr);
1087 12 100       24 if ($is_directed) {
1088 8         20 @h{qw(predecessors successors)} = @$e;
1089             } else {
1090 4         9 $h{vertices} = $e;
1091             }
1092 12         33 push @e, \%h;
1093             } else {
1094 41         92 $e{ $e->[0] }{ $e->[1] } = $edge_attr;
1095 41 100       102 $e{ $e->[1] }{ $e->[0] } = $edge_attr if !$is_directed;
1096             }
1097             }
1098 11 100       157 ( \%v, $is_hyper ? \@e : \%e );
1099             }
1100              
1101             sub ingest {
1102 3     3 1 1507 my ($g, $g2) = @_;
1103 3         16 for my $v ($g2->vertices) {
1104 12 100       32 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         26 $g->set_vertex_attributes($v, $g2->get_vertex_attributes($v));
1109             }
1110 12 100       27 if (&is_multiedged) {
1111 6         18 for my $e ($g2->edges_from($v)) {
1112             $g->set_edge_attributes_by_id(@$e, $_, $g2->get_edge_attributes_by_id(@$e, $_))
1113 4         16 for $g2->get_multiedge_ids(@$e);
1114             }
1115             } else {
1116             $g->set_edge_attributes(@$_, $g2->get_edge_attributes(@$_))
1117 6         18 for $g2->edges_from($v);
1118             }
1119             }
1120 3         25 $g;
1121             }
1122              
1123             ###
1124             # More constructors.
1125             #
1126              
1127             sub copy {
1128 34     34 1 892 my ($g, @args) = @_;
1129 34         102 my %opt = _get_options( \@args );
1130 80     80   667 no strict 'refs';
  80         158  
  80         24402  
1131 34 100       153 my $c = (ref $g)->new(map +($_ => &$_ ? 1 : 0), @GRAPH_PROPS_COPIED);
1132 34         143 $c->add_vertices(&isolated_vertices);
1133 34         79 $c->add_edges(&_edges05);
1134 34         225 return $c;
1135             }
1136              
1137             *copy_graph = \©
1138              
1139             sub _deep_copy_best {
1140 19 50   19   1363 _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         2209 require Safe; # For deep_copy().
1147 20         152828 my $safe = Safe->new;
1148 20         20715 $safe->permit(qw/:load/);
1149 20         174 local $Storable::Deparse = 1;
1150 20     3   101 local $Storable::Eval = sub { $safe->reval($_[0]) };
  3         4633  
1151 20         126 return Storable::thaw(Storable::freeze($g));
1152             }
1153              
1154             sub _deep_copy_DataDumper {
1155 1     1   7 my $g = shift;
1156 1         618 require Data::Dumper;
1157 1         6714 my $d = Data::Dumper->new([$g]);
1158 80     80   698 use vars qw($VAR1);
  80         164  
  80         237129  
1159 1         32 $d->Purity(1)->Terse(1)->Deepcopy(1);
1160 1 50       26 $d->Deparse(1) if $] >= 5.008;
1161 1     1   8 eval $d->Dump;
  1     1   1649  
  1         3  
  1         57  
  1         8  
  1         3  
  1         53  
1162             }
1163              
1164             sub deep_copy {
1165 17     17 1 791 local $. = $.;
1166 17         51 my $g2 = _deep_copy_best(@_);
1167 17 100       12229 $g2->[ _V ]->reindex if grep ref, &_vertices05;
1168 17         144 $g2;
1169             }
1170              
1171             *deep_copy_graph = \&deep_copy;
1172              
1173             sub transpose_edge {
1174 491     491 1 725 my $g = $_[0];
1175 491 50       816 return $g if !&is_directed;
1176 491 50       944 return undef unless &has_edge;
1177 491         952 my $c = &get_edge_count;
1178 491         1093 my $a = &get_edge_attributes;
1179 491         1320 my @e = reverse @_[1..$#_];
1180 491 100       1041 &delete_edge unless $g->has_edge( @e );
1181 491         1864 $g->add_edges(map \@e, 1..$c);
1182 491 50       889 $g->set_edge_attributes(@e, $a) if $a;
1183 491         1338 return $g;
1184             }
1185              
1186             sub transpose_graph {
1187 20     20 1 62 my $t = ©
1188 20 100       50 return $t if !&directed;
1189 17         50 $t->transpose_edge(@$_) for &_edges05;
1190 17         302 return $t;
1191             }
1192              
1193             *transpose = \&transpose_graph;
1194              
1195             sub complete_graph {
1196 9     9 1 715 my $directed = &is_directed;
1197 9         21 my $c = &new;
1198 9         21 my @v = &_vertices05;
1199 9         15 my @edges;
1200 9         24 for (my $i = $#v; $i >= 0; $i-- ) {
1201 20 100       109 push @edges, map +([$v[$i], $v[$_]], $directed ? [$v[$_], $v[$i]] : ()),
1202             0..$i - 1;
1203             }
1204 9         20 $c->add_edges(@edges);
1205 9         49 return $c;
1206             }
1207              
1208             *complement = \&complement_graph;
1209              
1210             sub complement_graph {
1211 5     5 1 495 my $c = &complete_graph;
1212 5         20 $c->delete_edge(@$_) for &edges;
1213 5         42 return $c;
1214             }
1215              
1216             *complete = \&complete_graph;
1217              
1218             sub subgraph {
1219 23     23 1 85 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         751 require Set::Object;
1223 23         7912 my $s = $g->new;
1224 23         89 my @u = grep $g->has_vertex($_), @$src;
1225 23 100       110 my $v = Set::Object->new($dst ? grep $g->has_vertex($_), @$dst : @u);
1226 23 100       92 $s->add_vertices(@u, $dst ? $v->members : ());
1227 23         41 my $directed = &is_directed;
1228 23 100       43 if ($directed) {
1229 12         28 $s->add_edges(grep $v->contains($_->[1]), $g->edges_from(@u));
1230             } else {
1231 11 100       83 my $valid = $dst ? $v + Set::Object->new(@u) : $v;
1232 11   100     101 $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         207 return $s;
1239             }
1240              
1241             ###
1242             # Transitivity.
1243             #
1244              
1245             sub is_transitive {
1246 4     4 1 63 my $g = shift;
1247 4         727 require Graph::TransitiveClosure;
1248 4         25 Graph::TransitiveClosure::is_transitive($g);
1249             }
1250              
1251             ###
1252             # Weighted vertices.
1253             #
1254              
1255             my $defattr = 'weight';
1256              
1257             sub _defattr {
1258 149     149   344 return $defattr;
1259             }
1260              
1261             sub add_weighted_vertex {
1262 1     1 1 5 &expect_non_multivertexed;
1263 1         5 push @_, $defattr, pop;
1264 1         5 goto &set_vertex_attribute;
1265             }
1266              
1267             sub add_weighted_vertices {
1268 1     1 1 5 &expect_non_multivertexed;
1269 1         2 my $g = shift;
1270 1         5 while (@_) {
1271 2         8 my ($v, $w) = splice @_, 0, 2;
1272 2         6 $g->set_vertex_attribute($v, $defattr, $w);
1273             }
1274             }
1275              
1276             sub get_vertex_weight {
1277 5     5 1 14 &expect_non_multivertexed;
1278 5         13 push @_, $defattr;
1279 5         17 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         11 goto &has_vertex_attribute;
1286             }
1287              
1288             sub set_vertex_weight {
1289 1     1 1 5 &expect_non_multivertexed;
1290 1         4 push @_, $defattr, pop;
1291 1         4 goto &set_vertex_attribute;
1292             }
1293              
1294             sub delete_vertex_weight {
1295 1     1 1 4 &expect_non_multivertexed;
1296 1         4 push @_, $defattr;
1297 1         5 goto &delete_vertex_attribute;
1298             }
1299              
1300             sub add_weighted_vertex_by_id {
1301 1     1 1 5 &expect_multivertexed;
1302 1         4 push @_, $defattr, pop;
1303 1         5 goto &set_vertex_attribute_by_id;
1304             }
1305              
1306             sub add_weighted_vertices_by_id {
1307 1     1 1 5 &expect_multivertexed;
1308 1         2 my $g = shift;
1309 1         3 my $id = pop;
1310 1         5 while (@_) {
1311 2         7 my ($v, $w) = splice @_, 0, 2;
1312 2         20 $g->add_vertex_by_id($v, $id);
1313 2         6 $g->set_vertex_attribute_by_id($v, $id, $defattr, $w);
1314             }
1315             }
1316              
1317             sub get_vertex_weight_by_id {
1318 5     5 1 15 &expect_multivertexed;
1319 5         12 push @_, $defattr;
1320 5         16 goto &get_vertex_attribute_by_id;
1321             }
1322              
1323             sub has_vertex_weight_by_id {
1324 3     3 1 11 &expect_multivertexed;
1325 3         9 push @_, $defattr;
1326 3         12 goto &has_vertex_attribute_by_id;
1327             }
1328              
1329             sub set_vertex_weight_by_id {
1330 1     1 1 647 &expect_multivertexed;
1331 1         4 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 4 &expect_multivertexed;
1337 1         3 push @_, $defattr;
1338 1         5 goto &delete_vertex_attribute_by_id;
1339             }
1340              
1341             ###
1342             # Weighted edges.
1343             #
1344              
1345             sub add_weighted_edge {
1346 2581     2581 1 13522 &expect_non_multiedged;
1347 2581         4828 push @_, $defattr, pop;
1348 2581         5742 goto &set_edge_attribute;
1349             }
1350              
1351             sub add_weighted_edges {
1352 3     3 1 44 &expect_non_multiedged;
1353 3         6 my $g = shift;
1354 3         12 while (@_) {
1355 14         76 my ($u, $v, $w) = splice @_, 0, 3;
1356 14         35 $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         5 while (@_) {
1365 2         6 my ($u, $v, $w) = splice @_, 0, 3;
1366 2         6 $g->set_edge_attribute_by_id($u, $v, $id, $defattr, $w);
1367             }
1368             }
1369              
1370             sub add_weighted_path {
1371 6     6 1 382 &expect_non_multiedged;
1372 6         15 my $g = shift;
1373 6         13 my $u = shift;
1374 6         29 while (@_) {
1375 22         61 my ($w, $v) = splice @_, 0, 2;
1376 22         76 $g->set_edge_attribute($u, $v, $defattr, $w);
1377 22         68 $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         23 goto &get_edge_attribute;
1385             }
1386              
1387             sub has_edge_weight {
1388 3     3 1 10 &expect_non_multiedged;
1389 3         7 push @_, $defattr;
1390 3         11 goto &has_edge_attribute;
1391             }
1392              
1393             sub set_edge_weight {
1394 3     3 1 748 &expect_non_multiedged;
1395 3         12 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         5 push @_, $defattr;
1402 1         5 goto &delete_edge_attribute;
1403             }
1404              
1405             sub add_weighted_edge_by_id {
1406 6     6 1 42 &expect_multiedged;
1407 6         18 push @_, $defattr, pop;
1408 6         19 goto &set_edge_attribute_by_id;
1409             }
1410              
1411             sub add_weighted_path_by_id {
1412 3     3 1 18 &expect_multiedged;
1413 3         6 my $g = shift;
1414 3         5 my $id = pop;
1415 3         6 my $u = shift;
1416 3         10 while (@_) {
1417 6         15 my ($w, $v) = splice @_, 0, 2;
1418 6         18 $g->set_edge_attribute_by_id($u, $v, $id, $defattr, $w);
1419 6         22 $u = $v;
1420             }
1421             }
1422              
1423             sub get_edge_weight_by_id {
1424 8     8 1 29 &expect_multiedged;
1425 8         20 push @_, $defattr;
1426 8         23 goto &get_edge_attribute_by_id;
1427             }
1428              
1429             sub has_edge_weight_by_id {
1430 3     3 1 11 &expect_multiedged;
1431 3         9 push @_, $defattr;
1432 3         11 goto &has_edge_attribute_by_id;
1433             }
1434              
1435             sub set_edge_weight_by_id {
1436 1     1 1 696 &expect_multiedged;
1437 1         5 push @_, $defattr, pop;
1438 1         4 goto &set_edge_attribute_by_id;
1439             }
1440              
1441             sub delete_edge_weight_by_id {
1442 1     1 1 4 &expect_multiedged;
1443 1         4 push @_, $defattr;
1444 1         4 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   92 my $exp = shift;
1456 43 100       166 my $got = @_ ? shift : $expected{$exp};
1457 43 100       141 $got = defined $got ? ", got $got" : "";
1458 43 50       340 if (my @caller2 = caller(2)) {
1459 43         354 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 18 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 1147 100   1147 1 2844 _expected('undirected') unless &is_undirected;
1475             }
1476              
1477             sub expect_directed {
1478 261 100   261 1 1583 _expected('directed') unless &is_directed;
1479             }
1480              
1481             sub expect_acyclic {
1482 3 100   3 1 1640 _expected('acyclic') unless &is_acyclic;
1483             }
1484              
1485             sub expect_dag {
1486 7     7 1 1700 my @got;
1487 7 100       17 push @got, 'undirected' unless &is_directed;
1488 7 100       19 push @got, 'cyclic' unless &is_acyclic;
1489 7 100       40 _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 417 _expected('multivertexed') unless &is_multivertexed;
1498             }
1499             *expect_multivertex = \&expect_multivertexed;
1500              
1501             sub expect_non_multivertexed {
1502 1491 100   1491 1 2289 _expected('non-multivertexed') if &is_multivertexed;
1503             }
1504             *expect_non_multivertex = \&expect_non_multivertexed;
1505              
1506             sub expect_non_multiedged {
1507 19014 100   19014 1 29629 _expected('non-multiedged') if &is_multiedged;
1508             }
1509             *expect_non_multiedge = \&expect_non_multiedged;
1510              
1511             sub expect_multiedged {
1512 416 100   416 1 634 _expected('multiedged') unless &is_multiedged;
1513             }
1514             *expect_multiedge = \&expect_multiedged;
1515              
1516             sub expect_non_unionfind {
1517 661 100   661 1 1215 _expected('non-unionfind') if &has_union_find;
1518             }
1519              
1520             sub _get_options {
1521 1101     1101   10397 my @caller = caller(1);
1522 1101 100 100     6635 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         1829 my @opt = @{ $_[0] };
  1098         2459  
1526 1098 50       3106 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         4202 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   596 my @a = @_;
1539 1         3 my $i = @a;
1540 1         4 while ($i--) {
1541 3         8 my $j = int rand ($i+1);
1542 3         9 @a[$i,$j] = @a[$j,$i];
1543             }
1544 1         10 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   1352 \&__fisher_yates_shuffle : do { require List::Util; \&List::Util::shuffle };
  80         558  
  80         179435  
1558             }
1559              
1560             sub random_graph {
1561 14 100   14 1 9916 my $class = (@_ % 2) == 0 ? 'Graph' : shift;
1562 14         56 my %opt = _get_options( \@_ );
1563             __carp_confess "Graph::random_graph: argument 'vertices' missing or undef"
1564 14 100       50 unless defined $opt{vertices};
1565 12 100       38 srand delete $opt{random_seed} if exists $opt{random_seed};
1566 12 100       35 my $random_edge = delete $opt{random_edge} if exists $opt{random_edge};
1567 12         18 my @V;
1568 12 100       81 if (my $ref = ref $opt{vertices}) {
1569 1 50       7 __carp_confess "Graph::random_graph: argument 'vertices' illegal"
1570             if $ref ne 'ARRAY';
1571 1         4 @V = @{ $opt{vertices} };
  1         51  
1572             } else {
1573 11         37 @V = 0..($opt{vertices} - 1);
1574             }
1575 12         25 delete $opt{vertices};
1576 12         19 my $V = @V;
1577 12         32 my $C = $V * ($V - 1) / 2;
1578 12         18 my $E;
1579             __carp_confess "Graph::random_graph: both arguments 'edges' and 'edges_fill' specified"
1580 12 50 66     42 if exists $opt{edges} && exists $opt{edges_fill};
1581 12 100       27 $E = exists $opt{edges_fill} ? $opt{edges_fill} * $C : $opt{edges};
1582 12         22 delete $opt{edges};
1583 12         17 delete $opt{edges_fill};
1584 12         43 my $g = $class->new(%opt);
1585 12         66 $g->add_vertices(@V);
1586 12 50       29 return $g if $V < 2;
1587 12 100       28 $C *= 2 if my $is_directed = $g->directed;
1588 12 100       42 $E = $C / 2 unless defined $E;
1589 12         28 $E = int($E + 0.5);
1590 12         23 my $p = $E / $C;
1591 12 100   11992   38 $random_edge = sub { $p } unless defined $random_edge;
  11992         15800  
1592             # print "V = $V, E = $E, C = $C, p = $p\n";
1593 12 50 0     44 __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         21 my (%v1_v2, @edges);
1598 12         71 my @V1 = _shuffle @V;
1599 12         38 my @V2 = _shuffle @V;
1600             LOOP:
1601 12         25 while ($E) {
1602 21         34 for my $v1 (@V1) {
1603 280         390 for my $v2 (@V2) {
1604 12768 100       21450 next if $v1 eq $v2; # TODO: allow self-loops?
1605 12496         16457 my $q = $random_edge->($g, $v1, $v2, $p);
1606 12496 100 66     58159 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         9944 $v1_v2{$v1}{$v2} = undef;
1610 6027         14228 push @edges, [ $v1, $v2 ];
1611 6027         7372 $E--;
1612 6027 100       10924 last LOOP unless $E;
1613             }
1614             }
1615             }
1616             }
1617 12         263 $g->add_edges(@edges);
1618             }
1619              
1620             sub random_vertex {
1621 128     128 1 39171 my @V = &_vertices05;
1622 128         565 @V[rand @V];
1623             }
1624              
1625             sub random_edge {
1626 31     31 1 15116 my @E = &_edges05;
1627 31         167 @E[rand @E];
1628             }
1629              
1630             sub random_successor {
1631 53     53 1 165 my @S = &successors;
1632 53         190 @S[rand @S];
1633             }
1634              
1635             sub random_predecessor {
1636 46     46 1 156 my @P = &predecessors;
1637 46         174 @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   80 my $attr = shift;
1648             my $attribute =
1649             exists $attr->{attribute} ?
1650 23 50       62 $attr->{attribute} : $defattr;
1651             my $comparator =
1652             exists $attr->{comparator} ?
1653 23 50       60 $attr->{comparator} : $MST_comparator;
1654 23         59 return ($attribute, $comparator);
1655             }
1656              
1657             sub _MST_edges {
1658 23     23   77 my ($g, $attr) = @_;
1659 23         65 my ($attribute, $comparator) = _MST_attr($attr);
1660             map $_->[1],
1661 23         80 sort { $comparator->($a->[0], $b->[0], $a->[1], $b->[1]) }
  1673         2362  
1662             map [ $g->get_edge_attribute(@$_, $attribute), $_ ],
1663             &_edges05;
1664             }
1665              
1666             sub MST_Kruskal {
1667 24     24 1 504 &expect_undirected;
1668 23         57 my ($g, %attr) = @_;
1669 23         1069 require Graph::UnionFind;
1670              
1671 23         96 my $MST = Graph->new(directed => 0);
1672              
1673 23         122 my $UF = Graph::UnionFind->new;
1674 23         56 $UF->add(&_vertices05);
1675              
1676 23         97 my @edges;
1677 23         114 for my $e ($g->_MST_edges(\%attr)) {
1678 1606         3160 my ($u, $v) = @$e; # TODO: hyperedges
1679 1606 100       2950 next if $UF->same( @$e );
1680 454         1384 $UF->union([$u, $v]);
1681 454         1241 push @edges, [ $u, $v ];
1682             }
1683 23         747 $MST->add_edges(@edges);
1684              
1685 23         359 return $MST;
1686             }
1687              
1688             sub _MST_add {
1689 926     926   2001 my ($g, $h, $HF, $r, $attr, $unseen) = @_;
1690             $HF->add( Graph::MSTHeapElem->new( $r, $_, $g->get_edge_attribute( $r, $_, $attr ) ) )
1691 926         2082 for grep exists $unseen->{ $_ }, $g->successors( $r );
1692             }
1693              
1694 244     244   402 sub _next_alphabetic { shift; (sort keys %{ $_[0] })[0] }
  244         357  
  244         1130  
1695 5     5   9 sub _next_numeric { shift; (sort { $a <=> $b } keys %{ $_[0] })[0] }
  5         7  
  9         25  
  5         38  
1696 541     541   864 sub _next_random { shift; (values %{ $_[0] })[ rand keys %{ $_[0] } ] }
  541         731  
  541         2057  
  541         2154  
1697              
1698             sub _root_opt {
1699 150     150   364 my ($g, @args) = @_;
1700 150 100       518 my %opt = @args == 1 ? ( first_root => $args[0] ) : _get_options( \@args );
1701 150         256 my %unseen;
1702 150         405 my @unseen = $g->_vertices05;
1703 150         1855 @unseen{ @unseen } = @unseen;
1704 150         1460 @unseen = _shuffle @unseen;
1705 150         251 my $r;
1706 150 100       381 if (exists $opt{ start }) {
1707 1         4 $opt{ first_root } = delete $opt{ start };
1708 1         2 $opt{ next_root } = undef;
1709             }
1710 150 100       310 if (exists $opt{ first_root }) {
1711 107 100       226 if (ref $opt{ first_root } eq 'CODE') {
1712 1         5 $r = $opt{ first_root }->( $g, \%unseen );
1713             } else {
1714 106         172 $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       543 \&_next_numeric :
    50          
    100          
1726             \&_next_random;
1727 150         318 my $code = ref $next eq 'CODE';
1728 150 50       320 my $attr = exists $opt{ attribute } ? $opt{ attribute } : $defattr;
1729 150         629 return ( \%opt, \%unseen, \@unseen, $r, $next, $code, $attr );
1730             }
1731              
1732             sub _heap_walk {
1733 83     83   292 my ($g, $h, $add, $etc, $opt, $unseenh, $unseena, $r, $next, $code, $attr) = @_;
1734 83         4567 require Heap::Fibonacci;
1735 83         17169 my $HF = Heap::Fibonacci->new;
1736 83         818 while (defined $r) {
1737             # print "r = $r\n";
1738 105         306 $add->($g, $h, $HF, $r, $attr, $unseenh, $etc);
1739 105         841 delete $unseenh->{ $r };
1740 105         285 while (defined $HF->top) {
1741 4088         28778 my $t = $HF->extract_top;
1742             # use Data::Dumper; print "t = ", Dumper($t);
1743 4088 50       24812 if (defined $t) {
1744 4088         8234 my ($u, $v, $w) = $t->val;
1745             # print "extracted top: $u $v $w\n";
1746 4088 100       11320 if (exists $unseenh->{ $v }) {
1747 1894         4595 $h->set_edge_attribute($u, $v, $attr, $w);
1748 1894         4959 delete $unseenh->{ $v };
1749 1894         4202 $add->($g, $h, $HF, $v, $attr, $unseenh, $etc);
1750             }
1751             }
1752             }
1753 104 100       752 return $h unless defined $next;
1754 103 50       327 $r = $code ? $next->( $g, $unseenh ) : shift @$unseena;
1755 103 100       304 last unless defined $r;
1756             }
1757 81         285 return $h;
1758             }
1759              
1760             sub MST_Prim {
1761 43     43 1 17688 &expect_undirected;
1762 42         1099 require Graph::MSTHeapElem;
1763 42         208 $_[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 30 !&is_cyclic;
1778             }
1779              
1780             sub is_dag {
1781 5 100 100 5 1 1420 &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 592 my $g = shift;
1792 5         16 my %opt = _get_options( \@_ );
1793 5         12 my $eic = delete $opt{ empty_if_cyclic };
1794 5         7 my $hac;
1795 5 100       12 if ($eic) {
1796 1         9 $hac = $g->has_a_cycle;
1797             } else {
1798 4         19 $g->expect_dag;
1799             }
1800 2         12 require Graph::Traversal::DFS;
1801 2         9 my $t = Graph::Traversal::DFS->new($g, %opt);
1802 2         7 my @s = $t->dfs;
1803 2 100       31 $hac ? () : reverse @s;
1804             }
1805              
1806             *toposort = \&topological_sort;
1807              
1808             sub _undirected_copy_compute {
1809 12     12   45 Graph->new(directed => 0, vertices => [&isolated_vertices], edges => [&_edges05]);
1810             }
1811              
1812             sub undirected_copy {
1813 63     63 1 128 &expect_directed;
1814 63         181 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 12 &expect_undirected;
1821 3         7 my @edges = &_edges05;
1822 3         13 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   721 no strict 'refs';
  80         194  
  80         662298  
1845             my @attr = @{ $_cache_type{$t} };
1846 228     228   134518 *{$t."_clear_cache"} = sub { $_[0]->delete_graph_attribute($_) for @attr };
1847             }
1848              
1849             sub _check_cache {
1850 2247     2247   4610 my ($g, $type, $extra_vals, $code, @args) = @_;
1851 2247         3659 my $c = $_cache_type{$type};
1852 2247 50       4149 __carp_confess "Graph: unknown cache type '$type'" if !defined $c;
1853 2247         3564 my ($main_key, @extra_keys) = @$c;
1854 2247 50       4551 __carp_confess "Graph: wrong number of extra values (@extra_keys) vs (@$extra_vals)" if @extra_keys != @$extra_vals;
1855 2247         5489 my $a = $g->get_graph_attribute($main_key);
1856 2247 50 66     7535 __carp_confess "$c attribute set to unexpected value $a"
1857             if defined $a and ref $a ne 'ARRAY';
1858 2247 100 100     6378 unless (defined $a && $a->[ 0 ] == $g->[ _G ]) {
1859 423         1159 $g->set_graph_attribute($main_key, $a = [ $g->[ _G ], $code->( $g, @args ) ]);
1860             }
1861 2245         3294 my $i = -1;
1862             my $extra_invalid = grep {
1863 2245         3293 my $v = $a->[ 1 ]->get_graph_attribute($_);
  106         283  
1864 106         172 $i++; # here so still incremented even if short-cut
1865 106 50       484 !defined $v or $v ne $extra_vals->[$i];
1866             } @extra_keys;
1867 2245 100       3853 if ($extra_invalid) {
1868 29         83 $g->set_graph_attribute($main_key, $a = [ $g->[ _G ], $code->( $g, @args ) ]);
1869             }
1870 2245         9280 return $a->[ 1 ];
1871             }
1872              
1873             ###
1874             # Connected components.
1875             #
1876              
1877             sub _connected_components_compute {
1878 40     40   80 my $g = $_[0];
1879 40         71 my %v2c;
1880             my @c;
1881 40 100       110 return [ [], {} ] unless my @v = $g->unique_vertices;
1882 35 100       76 if (my $UF = &has_union_find) {
1883 9         14 my $V = $g->[ _V ];
1884 9         24 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         29 for (my $i = 0; $i <= $#v; $i++) {
1888 20         32 my $cc = $cc[$i];
1889 20 50       39 __carp_confess "connected_component union-find did not have vertex '$v[$i]', please report"
1890             if !defined $cc;
1891 20 100       42 $cc2counter{$cc} = $counter++ if !exists $cc2counter{$cc};
1892 20         33 my $ci = $cc2counter{$cc};
1893 20         36 $v2c{ $v[$i] } = $ci;
1894 20         25 push @{ $c[$ci] }, $v[$i];
  20         74  
1895             }
1896             } else {
1897 26         2368 require Graph::Traversal::DFS;
1898 26         55 my %r; @r{ @v } = @v;
  26         112  
1899 26         56 @c = [];
1900             my $t = Graph::Traversal::DFS->new(
1901             $g,
1902 26     26   152 first_root => sub { (each %r)[1] },
1903 34 100   34   128 next_root => sub { push @c, [] if keys %r; (each %r)[1]; },
  34         238  
1904             pre => sub {
1905 97     97   189 my ($v, $t) = @_;
1906 97         191 $v2c{ $v } = $#c;
1907 97         158 push @{ $c[-1] }, $v;
  97         193  
1908 97         359 delete $r{ $v };
1909             },
1910 26         293 @_[1..$#_]
1911             );
1912 26         88 $t->dfs;
1913             }
1914 35         260 return [ \@c, \%v2c ];
1915             }
1916              
1917             sub _connected_components {
1918 384     384   839 my $ccc = _check_cache($_[0], 'connectivity', [],
1919             \&_connected_components_compute);
1920 384         560 return @{ $ccc };
  384         1283  
1921             }
1922              
1923             sub connected_component_by_vertex {
1924 82     82 1 12739 &expect_undirected;
1925 81         140 (&_connected_components)[1]->{ $_[1] };
1926             }
1927              
1928             sub connected_component_by_index {
1929 58     58 1 16076 &expect_undirected;
1930 57         103 my $value = (&_connected_components)[0]->[$_[1]];
1931 57 50       130 $value ? @{ $value || _empty_array } : ();
  41 100       194  
1932             }
1933              
1934             sub connected_components {
1935 41     41 1 721 &expect_undirected;
1936 40         71 @{ (&_connected_components)[0] };
  40         76  
1937             }
1938              
1939             sub same_connected_components {
1940 29     29 1 18189 &expect_undirected;
1941 28         120 my ($g, @args) = @_;
1942 28         74 my @components;
1943 28 100       52 if (my $UF = &has_union_find) {
1944 14         27 my @ids = &_vertex_ids;
1945 14 100       43 return 0 if @ids != @args;
1946 10         32 @components = $UF->find(@ids);
1947             } else {
1948 14         21 @components = @{ (&_connected_components)[1] }{ @args };
  14         27  
1949             }
1950 24 100       119 return 0 if grep !defined, @components;
1951 20         97 require List::Util;
1952 20         163 List::Util::uniq( @components ) == 1;
1953             }
1954              
1955 40     40   160 sub _super_component { join("+", sort @_) }
1956              
1957             sub connected_graph {
1958 21     21 1 583 &expect_undirected;
1959 20         40 my ($g, %opt) = @_;
1960 20         55 my $cg = Graph->new(undirected => 1);
1961 20 100 100     43 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     67 my $sc_cb = $opt{super_component} || \&_super_component;
1966             $cg->set_vertex_attribute(scalar $sc_cb->(@$_), 'subvertices', $_)
1967 18         45 for $g->connected_components;
1968             }
1969 20         93 return $cg;
1970             }
1971              
1972             sub is_connected {
1973 197     197 1 1039 &expect_undirected;
1974 192         244 return @{ (&_connected_components)[0] } == 1;
  192         363  
1975             }
1976              
1977             sub is_weakly_connected {
1978 10     10 1 2422 &expect_directed;
1979 9         39 splice @_, 0, 1, &undirected_copy;
1980 9         33 goto &is_connected;
1981             }
1982              
1983             *weakly_connected = \&is_weakly_connected;
1984              
1985             sub weakly_connected_components {
1986 6     6 1 549 &expect_directed;
1987 5         12 splice @_, 0, 1, &undirected_copy;
1988 5         14 goto &connected_components;
1989             }
1990              
1991             sub weakly_connected_component_by_vertex {
1992 21     21 1 4471 &expect_directed;
1993 20         43 splice @_, 0, 1, &undirected_copy;
1994 20         53 goto &connected_component_by_vertex;
1995             }
1996              
1997             sub weakly_connected_component_by_index {
1998 15     15 1 5730 &expect_directed;
1999 14         29 splice @_, 0, 1, &undirected_copy;
2000 14         61 goto &connected_component_by_index;
2001             }
2002              
2003             sub same_weakly_connected_components {
2004 8     8 1 6408 &expect_directed;
2005 7         28 splice @_, 0, 1, &undirected_copy;
2006 7         22 goto &same_connected_components;
2007             }
2008              
2009             sub weakly_connected_graph {
2010 6     6 1 534 &expect_directed;
2011 5         15 splice @_, 0, 1, &undirected_copy;
2012 5         14 goto &connected_graph;
2013             }
2014              
2015             sub _strongly_connected_components_compute {
2016 14     14   27 my $g = $_[0];
2017 14         1112 require Graph::Traversal::DFS;
2018 14         69 require List::Util;
2019 14         73 my $t = Graph::Traversal::DFS->new($g);
2020 14         90 my @d = reverse $t->dfs;
2021 14         49 my @c;
2022             my %v2c;
2023             my $u = Graph::Traversal::DFS->new(
2024             $g->transpose_graph,
2025             next_root => sub {
2026 134     134   345 my ($t, $u) = @_;
2027             return if !defined(my $root = List::Util::first(
2028 3135         4013 sub { exists $u->{$_} }, @d
2029 134 100       857 ));
2030 120         403 push @c, [];
2031 120         498 return $root;
2032             },
2033             pre => sub {
2034 251     251   421 my ($v, $t) = @_;
2035 251         295 push @{ $c[-1] }, $v;
  251         502  
2036 251         867 $v2c{$v} = $#c;
2037             },
2038 14         56 next_alphabetic => 1,
2039             );
2040 14         65 $u->dfs;
2041 14         1047 return [ \@c, \%v2c ];
2042             }
2043              
2044             sub _strongly_connected_components_v2c {
2045 12     12   21 &_strongly_connected_components->[1];
2046             }
2047              
2048             sub _strongly_connected_components_arrays {
2049 18     18   28 @{ &_strongly_connected_components->[0] };
  18         44  
2050             }
2051              
2052             sub _strongly_connected_components {
2053 40     40   138 _check_cache($_[0], 'strong_connectivity', [],
2054             \&_strongly_connected_components_compute);
2055             }
2056              
2057             sub strongly_connected_components {
2058 19     19 1 228 &expect_directed;
2059 18         71 goto &_strongly_connected_components_arrays;
2060             }
2061              
2062             sub strongly_connected_component_by_vertex {
2063 5     5 1 572 &expect_directed;
2064 4         9 &_strongly_connected_components_v2c->{$_[1]};
2065             }
2066              
2067             sub strongly_connected_component_by_index {
2068 6     6 1 2630 &expect_directed;
2069 5         13 my $i = $_[1];
2070 5 100       10 return if !defined(my $c = &_strongly_connected_components->[0][ $i ]);
2071 4         28 @$c;
2072             }
2073              
2074             sub same_strongly_connected_components {
2075 8     8 1 2683 &expect_directed;
2076 8         20 my ($g, @args) = @_;
2077 8         39 require Set::Object;
2078 8         16 Set::Object->new(@{ &_strongly_connected_components_v2c }{@args})->size <= 1;
  8         13  
2079             }
2080              
2081             sub is_strongly_connected {
2082 4     4 1 11 &strongly_connected_components == 1;
2083             }
2084              
2085             *strongly_connected = \&is_strongly_connected;
2086              
2087             sub strongly_connected_graph {
2088 6     6 1 9373 &expect_directed;
2089 6         16 my ($g, %attr) = @_;
2090 6         16 my $sc_cb = \&_super_component;
2091 6         17 _opt_get(\%attr, super_component => \$sc_cb);
2092 6         15 _opt_unknown(\%attr);
2093 5         8 my ($c, $v2c) = @{ &_strongly_connected_components };
  5         9  
2094 5         13 my $s = Graph->new;
2095 5         17 my @s = map $sc_cb->(@$_), @$c;
2096 5         43 $s->set_vertex_attribute($s[$_], 'subvertices', $c->[$_]) for 0..$#$c;
2097 5         24 require List::Util;
2098 5         11 $s->add_edges(map [@s[ @$v2c{ @$_ } ]], grep List::Util::uniq( @$v2c{ @$_ } ) > 1, &_edges05);
2099 5         49 return $s;
2100             }
2101              
2102             ###
2103             # Biconnectivity.
2104             #
2105              
2106             sub _biconnectivity_out {
2107 14895     14895   25324 my ($state, $u, $v) = @_;
2108 14895         17349 my @BC;
2109 14895         18438 while (@{$state->{stack}}) {
  16819         29037  
2110 16819         21205 push @BC, my $e = pop @{$state->{stack}};
  16819         28392  
2111 16819 100 66     51464 last if $e->[0] eq $u && $e->[1] eq $v;
2112             }
2113 14895 50       28099 push @{$state->{BC}}, \@BC if @BC;
  14895         35051  
2114             }
2115              
2116             sub _biconnectivity_dfs {
2117 17536     17536   26935 my ($g, $u, $state) = @_;
2118 17536         38469 $state->{low}{$u} = $state->{num}{$u} = $state->{dfs}++;
2119 17536         31239 for my $v ($g->successors($u)) {
2120 35248 100 100     128941 if (!exists $state->{num}{$v}) {
    100 100        
2121 15912         19423 push @{$state->{stack}}, [$u, $v];
  15912         31943  
2122 15912         31431 $state->{pred}{$v} = $u;
2123 15912         30252 $state->{succ}{$u}{$v}++;
2124 15912         31113 _biconnectivity_dfs($g, $v, $state);
2125 15912         32428 $state->{low}{$u} = List::Util::min(@{ $state->{low} }{$u, $v});
  15912         41408  
2126             _biconnectivity_out($state, $u, $v)
2127 15912 100       40775 if $state->{low}{$v} >= $state->{num}{$u};
2128             } elsif (defined $state->{pred}{$u} &&
2129             $state->{pred}{$u} ne $v &&
2130             $state->{num}{$v} < $state->{num}{$u}) {
2131 907         1247 push @{$state->{stack}}, [$u, $v];
  907         2174  
2132 907         2723 $state->{low}{$u} = List::Util::min($state->{low}{$u}, $state->{num}{$v});
2133             }
2134             }
2135             }
2136              
2137             sub _biconnectivity_compute {
2138 252     252   1446 require List::Util;
2139 252         461 my ($g) = @_;
2140 252         672 my %state = (BC=>[], dfs=>0);
2141 252         563 my @u = $g->vertices;
2142 252         613 for my $u (@u) {
2143 17536 100       30012 next if exists $state{num}->{$u};
2144 1624         3832 _biconnectivity_dfs($g, $u, \%state);
2145 1624 100       3939 push @{$state{BC}}, delete $state{stack} if @{ $state{stack} || _empty_array };
  0 50       0  
  1624         4586  
2146             }
2147              
2148             # Mark the components each vertex belongs to.
2149 252         498 my ($bci, %v2bc, %bc2v) = 0;
2150 252         360 for my $bc (@{$state{BC}}) {
  252         456  
2151 14895         50995 $v2bc{$_}{$bci} = undef for map @$_, @$bc;
2152 14895         20970 $bci++;
2153             }
2154              
2155             # Any isolated vertices get each their own component.
2156 252         3938 $v2bc{$_}{$bci++} = undef for grep !exists $v2bc{$_}, @u;
2157              
2158             # build vector now we know how big to make it
2159 252         967 my ($Z, %v2bc_vec, @ap) = "\0" x (($bci + 7) / 8);
2160 252         5119 @v2bc_vec{@u} = ($Z) x @u;
2161 252         583 for my $v (@u) {
2162 17536         19779 my @components = keys %{ $v2bc{$v} };
  17536         38733  
2163 17536         68529 vec($v2bc_vec{$v}, $_, 1) = 1 for @components;
2164 17536         50364 $bc2v{$_}{$v}{$_} = undef for @components;
2165             # Articulation points / cut vertices are the vertices
2166             # which belong to more than one component.
2167 17536 100       36929 push @ap, $v if @components > 1;
2168             }
2169              
2170             # Bridges / cut edges are the components of two vertices.
2171 252         21194 my @br = grep @$_ == 2, map [keys %$_], values %bc2v;
2172              
2173             # Create the subgraph components.
2174 252         1348 my @sg = map [ List::Util::uniq( map @$_, @$_ ) ], @{$state{BC}};
  252         32687  
2175 252         20010 return [ \@ap, \@sg, \@br, \%v2bc, \%v2bc_vec, $Z ];
2176             }
2177              
2178             sub biconnectivity {
2179 435     435 1 159926 &expect_undirected;
2180 434 50       657 @{ _check_cache($_[0], 'biconnectivity', [],
  434         1730  
2181             \&_biconnectivity_compute, @_[1..$#_]) || _empty_array };
2182             }
2183              
2184             sub is_biconnected {
2185 13 100   13 1 25613 &edges >= 2 ? @{ (&biconnectivity)[0] } == 0 : undef ;
  10         24  
2186             }
2187              
2188             sub is_edge_connected {
2189 13 100   13 1 34 &edges >= 2 ? @{ (&biconnectivity)[2] } == 0 : undef;
  10         20  
2190             }
2191              
2192             sub is_edge_separable {
2193 13 100   13 1 33 &edges >= 2 ? @{ (&biconnectivity)[2] } > 0 : undef;
  10         22  
2194             }
2195              
2196             sub articulation_points {
2197 248     248 1 2190 @{ (&biconnectivity)[0] };
  248         468  
2198             }
2199              
2200             *cut_vertices = \&articulation_points;
2201              
2202             sub biconnected_components {
2203 14     14 1 1957 @{ (&biconnectivity)[1] };
  14         26  
2204             }
2205              
2206             sub biconnected_component_by_index {
2207 16     16 1 8810 my ($i) = splice @_, 1, 1;
2208 16         21 (&biconnectivity)[1]->[ $i ];
2209             }
2210              
2211             sub biconnected_component_by_vertex {
2212 2     2 1 7 my ($v) = splice @_, 1, 1;
2213 2         4 my $v2bc = (&biconnectivity)[3];
2214 2         6 splice @_, 1, 0, $v;
2215 2 50       10 return defined $v2bc->{ $v } ? keys %{ $v2bc->{ $v } } : ();
  2         10  
2216             }
2217              
2218             sub same_biconnected_components {
2219 5     5 1 462 my ($v2bc, $Z) = (&biconnectivity)[4,5];
2220 5 50       34 return 0 if grep !defined, my @vecs = @$v2bc{ @_[1..$#_] };
2221 5         11 my $accumulator = $vecs[0];
2222 5         20 $accumulator &= $_ for @vecs[1..$#vecs]; # accumulate 0s -> all in same
2223 5         29 $accumulator ne $Z;
2224             }
2225              
2226             sub biconnected_graph {
2227 1     1 1 6 my ($g, %opt) = @_;
2228 1         4 my $bc = (&biconnectivity)[1];
2229 1         6 my $bcg = Graph->new(directed => 0);
2230 1   50     8 my $sc_cb = $opt{super_component} || \&_super_component;
2231 1         5 my @s = map $sc_cb->(@$_), @$bc;
2232 1         10 $bcg->set_vertex_attribute($s[$_], 'subvertices', $bc->[$_]) for 0..$#$bc;
2233 1         2 my @edges;
2234 1         5 for my $i (0..$#$bc) {
2235 5         6 my @u = @{ $bc->[ $i ] };
  5         13  
2236 5         10 for my $j (0..$i-1) {
2237 10         13 my %j; @j{ @{ $bc->[ $j ] } } = ();
  10         14  
  10         27  
2238 10 100       47 next if !grep exists $j{ $_ }, @u;
2239 4         19 push @edges, [ @s[$i, $j] ];
2240             }
2241             }
2242 1         11 $bcg->add_edges(@edges);
2243 1         6 return $bcg;
2244             }
2245              
2246             sub bridges {
2247 23 50   23 1 22402 @{ (&biconnectivity)[2] || _empty_array };
  23         55  
2248             }
2249              
2250             ###
2251             # SPT.
2252             #
2253              
2254             sub _SPT_add {
2255 1073     1073   2147 my ($g, $h, $HF, $r, $attr, $unseen, $etc) = @_;
2256 1073   100     2705 my $etc_r = $etc->{ $r } || 0;
2257 1073         2406 for my $s ( grep exists $unseen->{ $_ }, $g->successors( $r ) ) {
2258 5253         11642 my $t = $g->get_edge_attribute( $r, $s, $attr );
2259 5253 100       9910 $t = 1 unless defined $t;
2260 5253 100       9241 __carp_confess "Graph::SPT_Dijkstra: edge $r-$s is negative ($t)"
2261             if $t < 0;
2262 5252 100 100     21890 if (!defined($etc->{ $s }) || ($etc_r + $t) < $etc->{ $s }) {
2263 1016   100     2458 my $etc_s = $etc->{ $s } || 0;
2264 1016         2537 $etc->{ $s } = $etc_r + $t;
2265             # print "$r - $s : setting $s to $etc->{ $s } ($etc_r, $etc_s)\n";
2266 1016         3935 $h->set_vertex_attributes($s, { $attr=>$etc->{ $s }, 'p', $r });
2267 1016         2933 $HF->add( Graph::SPTHeapElem->new($r, $s, $etc->{ $s }) );
2268             }
2269             }
2270             }
2271              
2272             sub _SPT_Dijkstra_compute {
2273 41     41   3351 require Graph::SPTHeapElem;
2274 41         176 my $sptg = $_[0]->_heap_walk($_[0]->new, \&_SPT_add, {}, @_[1..$#_]);
2275 40         1238 $sptg->set_graph_attribute('SPT_Dijkstra_root', $_[4]);
2276 40         130 $sptg;
2277             }
2278              
2279             sub SPT_Dijkstra {
2280 81     81 1 185 my $g = $_[0];
2281 81         166 my @args = &_root_opt;
2282 81         282 _check_cache($g, 'SPT_Dijkstra', [$args[3]],
2283             \&_SPT_Dijkstra_compute, @args);
2284             }
2285              
2286             *SSSP_Dijkstra = \&SPT_Dijkstra;
2287              
2288             *single_source_shortest_paths = \&SPT_Dijkstra;
2289              
2290             sub SP_Dijkstra {
2291 68     68 1 33502 my ($g, $u, $v) = @_;
2292 68         169 my $sptg = $g->SPT_Dijkstra(first_root => $u);
2293 68         155 my @path = ($v);
2294 68         323 require Set::Object;
2295 68         246 my $seen = Set::Object->new;
2296 68         167 my $V = $g->vertices;
2297 68         110 my $p;
2298 68         216 while (defined($p = $sptg->get_vertex_attribute($v, 'p'))) {
2299 82 50       230 last if $seen->contains($p);
2300 82         485 push @path, $p;
2301 82         118 $v = $p;
2302 82         263 $seen->insert($p);
2303 82 100 66     410 last if $seen->size == $V || $u eq $v;
2304             }
2305 68 100 66     466 return if !@path or $path[-1] ne $u;
2306 27         370 return reverse @path;
2307             }
2308              
2309             sub __SPT_Bellman_Ford {
2310 2118     2118   3337 my ($g, $u, $v, $attr, $d, $p, $c0, $c1) = @_;
2311 2118 100       4127 return unless $c0->{ $u };
2312 215         384 my $w = $g->get_edge_attribute($u, $v, $attr);
2313 215 100       422 $w = 1 unless defined $w;
2314 215 100       388 if (defined $d->{ $v }) {
2315 141 50       258 if (defined $d->{ $u }) {
2316 141 100       413 if ($d->{ $v } > $d->{ $u } + $w) {
2317 14         25 $d->{ $v } = $d->{ $u } + $w;
2318 14         21 $p->{ $v } = $u;
2319 14         31 $c1->{ $v }++;
2320             }
2321             } # else !defined $d->{ $u } && defined $d->{ $v }
2322             } else {
2323 74 50       142 if (defined $d->{ $u }) {
2324             # defined $d->{ $u } && !defined $d->{ $v }
2325 74         174 $d->{ $v } = $d->{ $u } + $w;
2326 74         124 $p->{ $v } = $u;
2327 74         158 $c1->{ $v }++;
2328             } # else !defined $d->{ $u } && !defined $d->{ $v }
2329             }
2330             }
2331              
2332             sub _SPT_Bellman_Ford {
2333 11     11   31 my ($g, $opt, $unseenh, $unseena, $r, $next, $code, $attr) = @_;
2334 11         17 my %d;
2335 11 50       20 return unless defined $r;
2336 11         37 $d{ $r } = 0;
2337 11         12 my %p;
2338 11         41 my $V = $g->vertices;
2339 11         18 my %c0; # Changed during the last iteration?
2340 11         23 $c0{ $r }++;
2341 11         34 for (my $i = 0; $i < $V; $i++) {
2342 89         117 my %c1;
2343 89         178 for my $e ($g->edges) {
2344 1546         2347 my ($u, $v) = @$e;
2345 1546         2963 __SPT_Bellman_Ford($g, $u, $v, $attr, \%d, \%p, \%c0, \%c1);
2346 1546 100       2377 __SPT_Bellman_Ford($g, $v, $u, $attr, \%d, \%p, \%c0, \%c1)
2347             if $g->undirected;
2348             }
2349 89 100       538 %c0 = %c1 unless $i == $V - 1;
2350             }
2351              
2352 11         22 for my $e ($g->edges) {
2353 161         314 my ($u, $v) = @$e;
2354 161 100 66     492 if (defined $d{ $u } && defined $d{ $v }) {
2355 148         258 my $d = $g->get_edge_attribute($u, $v, $attr);
2356             __carp_confess "Graph::SPT_Bellman_Ford: negative cycle exists"
2357 148 100 100     488 if defined $d && $d{ $v } > $d{ $u } + $d;
2358             }
2359             }
2360              
2361 10         57 return (\%p, \%d);
2362             }
2363              
2364             sub _SPT_Bellman_Ford_compute {
2365 11     11   35 my ($g, @args) = @_;
2366 11         45 my ($p, $d) = $g->_SPT_Bellman_Ford(@args);
2367 10         27 my $h = $g->new;
2368 10         38 for my $v (keys %$p) {
2369 69         118 my $u = $p->{ $v };
2370 69         181 $h->set_edge_attribute( $u, $v, $args[6],
2371             $g->get_edge_attribute($u, $v, $args[6]));
2372 69         301 $h->set_vertex_attributes( $v, { $args[6], $d->{ $v }, p => $u } );
2373             }
2374 10         56 $h->set_graph_attribute('SPT_Bellman_Ford_root', $args[3]);
2375 10         71 $h;
2376             }
2377              
2378             sub SPT_Bellman_Ford {
2379 27     27 1 3355 my @args = &_root_opt;
2380 27         93 _check_cache($_[0], 'SPT_Bellman_Ford', [$args[3]],
2381             \&_SPT_Bellman_Ford_compute, @args);
2382             }
2383              
2384             *SSSP_Bellman_Ford = \&SPT_Bellman_Ford;
2385              
2386             sub SP_Bellman_Ford {
2387 18     18 1 47 my ($g, $u, $v) = @_;
2388 18         39 my $sptg = $g->SPT_Bellman_Ford(first_root => $u);
2389 18         38 my @path = ($v);
2390 18         91 require Set::Object;
2391 18         64 my $seen = Set::Object->new;
2392 18         41 my $V = $g->vertices;
2393 18         41 my $p;
2394 18         44 while (defined($p = $sptg->get_vertex_attribute($v, 'p'))) {
2395 30 50       76 last if $seen->contains($p);
2396 30         144 push @path, $p;
2397 30         43 $v = $p;
2398 30         97 $seen->insert($p);
2399 30 50       92 last if $seen->size == $V;
2400             }
2401             # @path = () if @path && "$path[-1]" ne "$u";
2402 18         239 return reverse @path;
2403             }
2404              
2405             ###
2406             # Transitive Closure.
2407             #
2408              
2409             sub TransitiveClosure_Floyd_Warshall {
2410 19     19 1 2067 my $self = shift;
2411 19         513 require Graph::TransitiveClosure;
2412 19         95 Graph::TransitiveClosure->new($self, @_);
2413             }
2414              
2415             *transitive_closure = \&TransitiveClosure_Floyd_Warshall;
2416              
2417             sub APSP_Floyd_Warshall {
2418 37     37 1 6689 my $self = shift;
2419 37         1626 require Graph::TransitiveClosure;
2420 37         226 Graph::TransitiveClosure->new($self, path => 1, @_);
2421             }
2422              
2423             *all_pairs_shortest_paths = \&APSP_Floyd_Warshall;
2424              
2425             sub _transitive_closure_matrix_compute {
2426 22     22   59 &APSP_Floyd_Warshall->transitive_closure_matrix;
2427             }
2428              
2429             sub transitive_closure_matrix {
2430 1143     1143 1 2933 _check_cache($_[0], 'transitive_closure_matrix', [],
2431             \&_transitive_closure_matrix_compute, @_[1..$#_]);
2432             }
2433              
2434             sub path_length {
2435 1532     1532 1 17740 shift->transitive_closure_matrix->path_length(@_);
2436             }
2437              
2438             sub path_successor {
2439 27     27 1 100 shift->transitive_closure_matrix->path_successor(@_);
2440             }
2441              
2442             sub path_vertices {
2443 205     205 1 78037 shift->transitive_closure_matrix->path_vertices(@_);
2444             }
2445              
2446             sub all_paths {
2447 25     25 1 11584 shift->transitive_closure_matrix->all_paths(@_);
2448             }
2449              
2450             sub is_reachable {
2451 12070     12070 1 488937 shift->transitive_closure_matrix->is_reachable(@_);
2452             }
2453              
2454             sub for_shortest_paths {
2455 34     34 1 46 my $g = shift;
2456 34         45 my $c = shift;
2457 34         64 my $t = $g->transitive_closure_matrix;
2458 34         94 my @v = $g->vertices;
2459 34         56 my $n = 0;
2460 34         67 for my $u (@v) {
2461 183         454 $c->($t, $u, $_, ++$n) for grep $t->is_reachable($u, $_), @v;
2462             }
2463 34         82 return $n;
2464             }
2465              
2466             sub _minmax_path {
2467 25     25   34 my $g = shift;
2468 25         72 my $min;
2469             my $max;
2470 25         0 my $minp;
2471 25         0 my $maxp;
2472             $g->for_shortest_paths(sub {
2473 628     628   1018 my ($t, $u, $v, $n) = @_;
2474 628         1204 my $l = $t->path_length($u, $v);
2475 628 50       1102 return unless defined $l;
2476 628         695 my $p;
2477 628 100 100     1991 if ($u ne $v && (!defined $max || $l > $max)) {
      100        
2478 50         70 $max = $l;
2479 50         123 $maxp = $p = [ $t->path_vertices($u, $v) ];
2480             }
2481 628 100 100     2460 if ($u ne $v && (!defined $min || $l < $min)) {
      100        
2482 18         25 $min = $l;
2483 18   100     58 $minp = $p || [ $t->path_vertices($u, $v) ];
2484             }
2485 25         153 });
2486 25         197 return ($min, $max, $minp, $maxp);
2487             }
2488              
2489             sub diameter {
2490 15     15 1 37 my $g = shift;
2491 15         36 my ($min, $max, $minp, $maxp) = $g->_minmax_path(@_);
2492 15 50       109 return defined $maxp ? (wantarray ? @$maxp : $max) : undef;
    100          
2493             }
2494              
2495             *graph_diameter = \&diameter;
2496              
2497             sub longest_path {
2498 5     5 1 19 my ($g, $u, $v) = @_;
2499 5         12 my $t = $g->transitive_closure_matrix;
2500 5 100       26 if (defined $u) {
2501 2 50       15 return wantarray ? $t->path_vertices($u, $v) : $t->path_length($u, $v)
    100          
2502             if defined $v;
2503 1         2 my $max;
2504             my @max;
2505 1         6 for my $v (grep $u ne $_, $g->vertices) {
2506 9         21 my $l = $t->path_length($u, $v);
2507 9 100 100     42 next if !(defined $l && (!defined $max || $l > $max));
      66        
2508 3         5 $max = $l;
2509 3         7 @max = $t->path_vertices($u, $v);
2510             }
2511 1 50       12 return wantarray ? @max : $max;
2512             }
2513 3 100       8 if (defined $v) {
2514 1         2 my $max;
2515             my @max;
2516 1         4 for my $u (grep $_ ne $v, $g->vertices) {
2517 9         18 my $l = $t->path_length($u, $v);
2518 9 100 100     43 next if !(defined $l && (!defined $max || $l > $max));
      66        
2519 2         3 $max = $l;
2520 2         5 @max = $t->path_vertices($u, $v);
2521             }
2522 1 50       10 return wantarray ? @max : @max - 1;
2523             }
2524 2         7 my ($min, $max, $minp, $maxp) = $g->_minmax_path(@_);
2525 2 50       32 return defined $maxp ? (wantarray ? @$maxp : $max) : undef;
    50          
2526             }
2527              
2528             sub vertex_eccentricity {
2529 165     165 1 366 &expect_undirected;
2530 165         272 my ($g, $u) = @_;
2531 165 100       266 return Infinity() if !&is_connected;
2532 158         250 my $max;
2533 158         307 for my $v (grep $u ne $_, $g->vertices) {
2534 1095         1926 my $l = $g->path_length($u, $v);
2535 1095 100 100     4129 next if !(defined $l && (!defined $max || $l > $max));
      66        
2536 366         537 $max = $l;
2537             }
2538 158 100       525 return defined $max ? $max : Infinity();
2539             }
2540              
2541             sub shortest_path {
2542 11     11 1 33 &expect_undirected;
2543 11         29 my ($g, $u, $v) = @_;
2544 11         25 my $t = $g->transitive_closure_matrix;
2545 11 100       39 if (defined $u) {
2546 2 50       11 return wantarray ? $t->path_vertices($u, $v) : $t->path_length($u, $v)
    100          
2547             if defined $v;
2548 1         3 my $min;
2549             my @min;
2550 1         6 for my $v (grep $u ne $_, $g->vertices) {
2551 9         19 my $l = $t->path_length($u, $v);
2552 9 100 66     39 next if !(defined $l && (!defined $min || $l < $min));
      33        
2553 1         3 $min = $l;
2554 1         4 @min = $t->path_vertices($u, $v);
2555             }
2556             # print "min/1 = @min\n";
2557 1 50       14 return wantarray ? @min : $min;
2558             }
2559 9 100       20 if (defined $v) {
2560 1         4 my $min;
2561             my @min;
2562 1         6 for my $u (grep $_ ne $v, $g->vertices) {
2563 9         19 my $l = $t->path_length($u, $v);
2564 9 100 100     43 next if !(defined $l && (!defined $min || $l < $min));
      66        
2565 3         6 $min = $l;
2566 3         10 @min = $t->path_vertices($u, $v);
2567             }
2568             # print "min/2 = @min\n";
2569 1 50       14 return wantarray ? @min : $min;
2570             }
2571 8         25 my ($min, $max, $minp, $maxp) = $g->_minmax_path(@_);
2572 8 100       45 return if !defined $minp;
2573 2 50       15 wantarray ? @$minp : $min;
2574             }
2575              
2576             sub radius {
2577 17     17 1 56 &expect_undirected;
2578 17         37 my $g = shift;
2579 17         38 my ($center, $radius) = (undef, Infinity());
2580 17         48 for my $v ($g->vertices) {
2581 89         177 my $x = $g->vertex_eccentricity($v);
2582 89 100 66     346 ($center, $radius) = ($v, $x) if defined $x && $x < $radius;
2583             }
2584 17         69 return $radius;
2585             }
2586              
2587             sub center_vertices {
2588 10     10 1 1130 &expect_undirected;
2589 10         24 my ($g, $delta) = @_;
2590 10 100       24 $delta = 0 unless defined $delta;
2591 10         19 $delta = abs($delta);
2592 10         16 my @c;
2593 10         19 my $Inf = Infinity();
2594 10         33 my $r = $g->radius;
2595 10 100 66     68 if (defined $r && $r != $Inf) {
2596 7         19 for my $v ($g->vertices) {
2597 53         109 my $e = $g->vertex_eccentricity($v);
2598 53 50 33     172 next unless defined $e && $e != $Inf;
2599 53 100       123 push @c, $v if abs($e - $r) <= $delta;
2600             }
2601             }
2602 10         74 return @c;
2603             }
2604              
2605             *centre_vertices = \¢er_vertices;
2606              
2607             sub average_path_length {
2608 9     9 1 1102 my $g = shift;
2609 9         21 my @A = @_;
2610 9         14 my $d = 0;
2611 9         13 my $m = 0;
2612             $g->for_shortest_paths(sub {
2613 809     809   1261 my ($t, $u, $v, $n) = @_;
2614 809 100       1447 return unless my $l = $t->path_length($u, $v);
2615 726 100 100     1985 return if defined $A[0] && $u ne $A[0];
2616 308 100 100     792 return if defined $A[1] && $v ne $A[1];
2617 145         177 $d += $l;
2618 145         256 $m++;
2619 9         60 });
2620 9 100       95 return $m ? $d / $m : undef;
2621             }
2622              
2623             ###
2624             # Simple tests.
2625             #
2626              
2627             sub is_multi_graph {
2628 32 100 100 32 1 75 return 0 unless &is_multiedged || &is_countedged;
2629 16         26 my $g = $_[0];
2630 16         24 my $multiedges = 0;
2631 16         31 for my $e (&_edges05) {
2632 14         30 my ($u, @v) = @$e;
2633 14 100       75 return 0 if grep $u eq $_, @v;
2634 6 100       15 $multiedges++ if $g->get_edge_count(@$e) > 1;
2635             }
2636 8         39 return $multiedges;
2637             }
2638              
2639             sub is_simple_graph {
2640 32 100 100 32 1 79 return 1 unless &is_multiedged || &is_countedged;
2641 16         29 my $g = $_[0];
2642 16 100       30 return 0 if grep $g->get_edge_count(@$_) > 1, &_edges05;
2643 12         74 return 1;
2644             }
2645              
2646             sub is_pseudo_graph {
2647 32   100 32 1 68 my $m = &is_countedged || &is_multiedged;
2648 32         52 my $g = $_[0];
2649 32         73 for my $e (&_edges05) {
2650 28         63 my ($u, @v) = @$e;
2651 28 100       237 return 1 if grep $u eq $_, @v;
2652 12 100 100     43 return 1 if $m && $g->get_edge_count($u, @v) > 1;
2653             }
2654 14         81 return 0;
2655             }
2656              
2657             ###
2658             # Rough isomorphism guess.
2659             #
2660              
2661             my %_factorial = (0 => 1, 1 => 1);
2662              
2663             sub __factorial {
2664 4     4   6 my $n = shift;
2665 4         10 for (my $i = 2; $i <= $n; $i++) {
2666 14 100       48 next if exists $_factorial{$i};
2667 7         45 $_factorial{$i} = $i * $_factorial{$i - 1};
2668             }
2669 4         10 $_factorial{$n};
2670             }
2671              
2672             sub _factorial {
2673 39     39   50 my $n = int(shift);
2674 39 50       65 __carp_confess "factorial of a negative number" if $n < 0;
2675 39 100       77 __factorial($n) unless exists $_factorial{$n};
2676 39         79 return $_factorial{$n};
2677             }
2678              
2679             sub could_be_isomorphic {
2680 31     31 1 77 my ($g0, $g1) = @_;
2681 31 100       59 return 0 unless &vertices == $g1->vertices;
2682 23 100       48 return 0 unless &_edges05 == $g1->_edges05;
2683 17         25 my %d0;
2684 17         39 $d0{ $g0->in_degree($_) }{ $g0->out_degree($_) }++ for &vertices;
2685 17         32 my %d1;
2686 17         36 $d1{ $g1->in_degree($_) }{ $g1->out_degree($_) }++ for $g1->vertices;
2687 17 50       56 return 0 unless keys %d0 == keys %d1;
2688 17         42 for my $da (keys %d0) {
2689             return 0
2690             unless exists $d1{$da} &&
2691 31 50 33     70 keys %{ $d0{$da} } == keys %{ $d1{$da} };
  31         59  
  31         80  
2692             return 0
2693             if grep !(exists $d1{$da}{$_} && $d0{$da}{$_} == $d1{$da}{$_}),
2694 31 100 66     41 keys %{ $d0{$da} };
  31         192  
2695             }
2696 13         28 for my $da (keys %d0) {
2697 27 50       34 return 0 if grep $d1{$da}{$_} != $d0{$da}{$_}, keys %{ $d0{$da} };
  27         74  
2698 27         67 delete $d1{$da};
2699             }
2700 13 50       28 return 0 unless keys %d1 == 0;
2701 13         19 my $f = 1;
2702 13         22 for my $da (keys %d0) {
2703 27         33 $f *= _factorial(abs($d0{$da}{$_})) for keys %{ $d0{$da} };
  27         71  
2704             }
2705 13         106 return $f;
2706             }
2707              
2708             ###
2709             # Analysis functions.
2710              
2711             sub subgraph_by_radius {
2712 17     17 1 66 $_[0]->subgraph([ @_[1..$#_-1], &reachable_by_radius ]);
2713             }
2714              
2715             sub clustering_coefficient {
2716 2     2 1 14 my ($g) = @_;
2717 2 100       5 return unless my @v = $g->vertices;
2718 1         8 require Set::Object;
2719 1         1 my %clustering;
2720              
2721 1         3 my $gamma = 0;
2722              
2723 1         5 for my $n (@v) {
2724 15         26 my $gamma_v = 0;
2725 15         31 my @neigh = $g->successors($n);
2726 15         50 my $c = Set::Object->new;
2727 15         28 for my $u (@neigh) {
2728 29   100     91 for my $v (grep +(!$c->contains("$u-$_") && $g->has_edge($u, $_)), @neigh) {
2729 15         32 $gamma_v++;
2730 15         57 $c->insert("$u-$v");
2731 15         50 $c->insert("$v-$u");
2732             }
2733             }
2734 15 100       33 if (@neigh > 1) {
2735 9         33 $clustering{$n} = $gamma_v/(@neigh * (@neigh - 1) / 2);
2736 9         40 $gamma += $gamma_v/(@neigh * (@neigh - 1) / 2);
2737             } else {
2738 6         25 $clustering{$n} = 0;
2739             }
2740             }
2741              
2742 1         11 $gamma /= @v;
2743              
2744 1 50       18 return wantarray ? ($gamma, %clustering) : $gamma;
2745             }
2746              
2747             sub betweenness {
2748 1     1 1 1999 my $g = shift;
2749              
2750 1         7 my @V = $g->vertices();
2751              
2752 1         2 my %Cb; # C_b{w} = 0
2753              
2754 1         7 @Cb{@V} = ();
2755              
2756 1         4 for my $s (@V) {
2757 15         29 my @S; # stack (unshift, shift)
2758              
2759             my %P; # P{w} = empty list
2760 15         102 $P{$_} = [] for @V;
2761              
2762 15         18 my %sigma; # \sigma{t} = 0
2763 15         74 $sigma{$_} = 0 for @V;
2764 15         20 $sigma{$s} = 1;
2765              
2766 15         18 my %d; # d{t} = -1;
2767 15         74 $d{$_} = -1 for @V;
2768 15         23 $d{$s} = 0;
2769              
2770 15         19 my @Q; # queue (push, shift)
2771 15         28 push @Q, $s;
2772              
2773 15         54 while (@Q) {
2774 172         264 my $v = shift @Q;
2775 172         282 unshift @S, $v;
2776 172         321 for my $w ($g->successors($v)) {
2777             # w found for first time
2778 341 100       664 if ($d{$w} < 0) {
2779 157         277 push @Q, $w;
2780 157         224 $d{$w} = $d{$v} + 1;
2781             }
2782             # Shortest path to w via v
2783 341 100       754 if ($d{$w} == $d{$v} + 1) {
2784 173         220 $sigma{$w} += $sigma{$v};
2785 173         196 push @{ $P{$w} }, $v;
  173         452  
2786             }
2787             }
2788             }
2789              
2790 15         22 my %delta;
2791 15         97 $delta{$_} = 0 for @V;
2792              
2793 15         53 while (@S) {
2794 172         225 my $w = shift @S;
2795             $delta{$_} += $sigma{$_}/$sigma{$w} * (1 + $delta{$w})
2796 172         201 for @{ $P{$w} };
  172         403  
2797 172 100       493 $Cb{$w} += $delta{$w} if $w ne $s;
2798             }
2799             }
2800              
2801 1         13 return %Cb;
2802             }
2803              
2804             1;