File Coverage

blib/lib/Graph.pm
Criterion Covered Total %
statement 1561 1572 99.8
branch 622 720 86.3
condition 206 256 80.4
subroutine 307 307 100.0
pod 195 195 100.0
total 2891 3050 95.0


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