1package Graph; 2 3use strict; 4 5BEGIN { 6 if (0) { # SET THIS TO ZERO FOR TESTING AND RELEASES! 7 $SIG{__DIE__ } = \&__carp_confess; 8 $SIG{__WARN__} = \&__carp_confess; 9 } 10 sub __carp_confess { require Carp; Carp::confess(@_) } 11} 12 13use Graph::AdjacencyMap qw(:flags :fields); 14 15use vars qw($VERSION); 16 17$VERSION = '0.94'; 18 19require 5.006; # Weak references are absolutely required. 20 21my $can_deep_copy_Storable = 22 eval 'require Storable; require B::Deparse; $Storable::VERSION >= 2.05 && $B::Deparse::VERSION >= 0.61' && !$@; 23 24sub _can_deep_copy_Storable () { 25 return $can_deep_copy_Storable; 26} 27 28use Graph::AdjacencyMap::Heavy; 29use Graph::AdjacencyMap::Light; 30use Graph::AdjacencyMap::Vertex; 31use Graph::UnionFind; 32use Graph::TransitiveClosure; 33use Graph::Traversal::DFS; 34use Graph::MSTHeapElem; 35use Graph::SPTHeapElem; 36use Graph::Undirected; 37 38use Heap071::Fibonacci; 39use List::Util qw(shuffle first); 40use Scalar::Util qw(weaken); 41 42use Safe; # For deep_copy(). 43 44sub _F () { 0 } # Flags. 45sub _G () { 1 } # Generation. 46sub _V () { 2 } # Vertices. 47sub _E () { 3 } # Edges. 48sub _A () { 4 } # Attributes. 49sub _U () { 5 } # Union-Find. 50sub _S () { 6 } # Successors. 51sub _P () { 7 } # Predecessors. 52 53my $Inf; 54 55BEGIN { 56 local $SIG{FPE}; 57 eval { $Inf = exp(999) } || 58 eval { $Inf = 9**9**9 } || 59 eval { $Inf = 1e+999 } || 60 { $Inf = 1e+99 }; # Close enough for most practical purposes. 61} 62 63sub 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# - _COMPAT02 for user API compatibility with the Graph 0.20xxx series. 72# The vertices are contained in either a "simplemap" 73# (if no hypervertices) 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# - _HYPER for hyperness: a different number of "coordinates" than usual; 78# expects one for vertices and two for edges 79# - _UNORD for unordered coordinates (a set): if _UNORD is not set 80# the coordinates are assumed to be meaningfully ordered 81# - _UNIQ for unique coordinates: if set duplicates are removed, 82# if not, duplicates are assumed to meaningful 83# - _UNORDUNIQ: just a union of _UNORD and UNIQ 84# Vertices are assumed to be _UNORDUNIQ; edges assume none of these flags. 85 86use Graph::Attribute array => _A, map => 'graph'; 87 88sub _COMPAT02 () { 0x00000001 } 89 90sub stringify { 91 my $g = shift; 92 my $u = $g->is_undirected; 93 my $e = $u ? '=' : '-'; 94 my @e = 95 map { 96 my @v = 97 map { 98 ref($_) eq 'ARRAY' ? "[" . join(" ", @$_) . "]" : "$_" 99 } 100 @$_; 101 join($e, $u ? sort { "$a" cmp "$b" } @v : @v) } $g->edges05; 102 my @s = sort { "$a" cmp "$b" } @e; 103 push @s, sort { "$a" cmp "$b" } $g->isolated_vertices; 104 join(",", @s); 105} 106 107sub eq { 108 "$_[0]" eq "$_[1]" 109} 110 111sub ne { 112 "$_[0]" ne "$_[1]" 113} 114 115use overload 116 '""' => \&stringify, 117 'eq' => \&eq, 118 'ne' => \≠ 119 120sub _opt { 121 my ($opt, $flags, %flags) = @_; 122 while (my ($flag, $FLAG) = each %flags) { 123 if (exists $opt->{$flag}) { 124 $$flags |= $FLAG if $opt->{$flag}; 125 delete $opt->{$flag}; 126 } 127 if (exists $opt->{my $non = "non$flag"}) { 128 $$flags &= ~$FLAG if $opt->{$non}; 129 delete $opt->{$non}; 130 } 131 } 132} 133 134sub is_compat02 { 135 my ($g) = @_; 136 $g->[ _F ] & _COMPAT02; 137} 138 139*compat02 = \&is_compat02; 140 141sub has_union_find { 142 my ($g) = @_; 143 ($g->[ _F ] & _UNIONFIND) && defined $g->[ _U ]; 144} 145 146sub _get_union_find { 147 my ($g) = @_; 148 $g->[ _U ]; 149} 150 151sub _opt_get { 152 my ($opt, $key, $var) = @_; 153 if (exists $opt->{$key}) { 154 $$var = $opt->{$key}; 155 delete $opt->{$key}; 156 } 157} 158 159sub _opt_unknown { 160 my ($opt) = @_; 161 if (my @opt = keys %$opt) { 162 my $f = (caller(1))[3]; 163 require Carp; 164 Carp::confess(sprintf 165 "$f: Unknown option%s: @{[map { qq['$_'] } sort @opt]}", 166 @opt > 1 ? 's' : ''); 167 } 168} 169 170sub new { 171 my $class = shift; 172 my $gflags = 0; 173 my $vflags; 174 my $eflags; 175 my %opt = _get_options( \@_ ); 176 177 if (ref $class && $class->isa('Graph')) { 178 no strict 'refs'; 179 for my $c (qw(undirected refvertexed compat02 180 hypervertexed countvertexed multivertexed 181 hyperedged countedged multiedged omniedged 182 __stringified)) { 183# $opt{$c}++ if $class->$c; # 5.00504-incompatible 184 if (&{"Graph::$c"}($class)) { $opt{$c}++ } 185 } 186# $opt{unionfind}++ if $class->has_union_find; # 5.00504-incompatible 187 if (&{"Graph::has_union_find"}($class)) { $opt{unionfind}++ } 188 } 189 190 _opt_get(\%opt, undirected => \$opt{omniedged}); 191 _opt_get(\%opt, omnidirected => \$opt{omniedged}); 192 193 if (exists $opt{directed}) { 194 $opt{omniedged} = !$opt{directed}; 195 delete $opt{directed}; 196 } 197 198 my $vnonomni = 199 $opt{nonomnivertexed} || 200 (exists $opt{omnivertexed} && !$opt{omnivertexed}); 201 my $vnonuniq = 202 $opt{nonuniqvertexed} || 203 (exists $opt{uniqvertexed} && !$opt{uniqvertexed}); 204 205 _opt(\%opt, \$vflags, 206 countvertexed => _COUNT, 207 multivertexed => _MULTI, 208 hypervertexed => _HYPER, 209 omnivertexed => _UNORD, 210 uniqvertexed => _UNIQ, 211 refvertexed => _REF, 212 refvertexed_stringified => _REFSTR , 213 __stringified => _STR, 214 ); 215 216 _opt(\%opt, \$eflags, 217 countedged => _COUNT, 218 multiedged => _MULTI, 219 hyperedged => _HYPER, 220 omniedged => _UNORD, 221 uniqedged => _UNIQ, 222 ); 223 224 _opt(\%opt, \$gflags, 225 compat02 => _COMPAT02, 226 unionfind => _UNIONFIND, 227 ); 228 229 if (exists $opt{vertices_unsorted}) { # Graph 0.20103 compat. 230 my $unsorted = $opt{vertices_unsorted}; 231 delete $opt{vertices_unsorted}; 232 require Carp; 233 Carp::confess("Graph: vertices_unsorted must be true") 234 unless $unsorted; 235 } 236 237 my @V; 238 if ($opt{vertices}) { 239 require Carp; 240 Carp::confess("Graph: vertices should be an array ref") 241 unless ref $opt{vertices} eq 'ARRAY'; 242 @V = @{ $opt{vertices} }; 243 delete $opt{vertices}; 244 } 245 246 my @E; 247 if ($opt{edges}) { 248 unless (ref $opt{edges} eq 'ARRAY') { 249 require Carp; 250 Carp::confess("Graph: edges should be an array ref of array refs"); 251 } 252 @E = @{ $opt{edges} }; 253 delete $opt{edges}; 254 } 255 256 _opt_unknown(\%opt); 257 258 my $uflags; 259 if (defined $vflags) { 260 $uflags = $vflags; 261 $uflags |= _UNORD unless $vnonomni; 262 $uflags |= _UNIQ unless $vnonuniq; 263 } else { 264 $uflags = _UNORDUNIQ; 265 $vflags = 0; 266 } 267 268 if (!($vflags & _HYPER) && ($vflags & _UNORDUNIQ)) { 269 my @but; 270 push @but, 'unordered' if ($vflags & _UNORD); 271 push @but, 'unique' if ($vflags & _UNIQ); 272 require Carp; 273 Carp::confess(sprintf "Graph: not hypervertexed but %s", 274 join(' and ', @but)); 275 } 276 277 unless (defined $eflags) { 278 $eflags = ($gflags & _COMPAT02) ? _COUNT : 0; 279 } 280 281 if (!($vflags & _HYPER) && ($vflags & _UNIQ)) { 282 require Carp; 283 Carp::confess("Graph: not hypervertexed but uniqvertexed"); 284 } 285 286 if (($vflags & _COUNT) && ($vflags & _MULTI)) { 287 require Carp; 288 Carp::confess("Graph: both countvertexed and multivertexed"); 289 } 290 291 if (($eflags & _COUNT) && ($eflags & _MULTI)) { 292 require Carp; 293 Carp::confess("Graph: both countedged and multiedged"); 294 } 295 296 my $g = bless [ ], ref $class || $class; 297 298 $g->[ _F ] = $gflags; 299 $g->[ _G ] = 0; 300 $g->[ _V ] = ($vflags & (_HYPER | _MULTI)) ? 301 Graph::AdjacencyMap::Heavy->_new($uflags, 1) : 302 (($vflags & ~_UNORD) ? 303 Graph::AdjacencyMap::Vertex->_new($uflags, 1) : 304 Graph::AdjacencyMap::Light->_new($g, $uflags, 1)); 305 $g->[ _E ] = (($vflags & _HYPER) || ($eflags & ~_UNORD)) ? 306 Graph::AdjacencyMap::Heavy->_new($eflags, 2) : 307 Graph::AdjacencyMap::Light->_new($g, $eflags, 2); 308 309 $g->add_vertices(@V) if @V; 310 311 if (@E) { 312 for my $e (@E) { 313 unless (ref $e eq 'ARRAY') { 314 require Carp; 315 Carp::confess("Graph: edges should be array refs"); 316 } 317 $g->add_edge(@$e); 318 } 319 } 320 321 if (($gflags & _UNIONFIND)) { 322 $g->[ _U ] = Graph::UnionFind->new; 323 } 324 325 return $g; 326} 327 328sub countvertexed { $_[0]->[ _V ]->_is_COUNT } 329sub multivertexed { $_[0]->[ _V ]->_is_MULTI } 330sub hypervertexed { $_[0]->[ _V ]->_is_HYPER } 331sub omnivertexed { $_[0]->[ _V ]->_is_UNORD } 332sub uniqvertexed { $_[0]->[ _V ]->_is_UNIQ } 333sub refvertexed { $_[0]->[ _V ]->_is_REF } 334sub refvertexed_stringified { $_[0]->[ _V ]->_is_REFSTR } 335sub __stringified { $_[0]->[ _V ]->_is_STR } 336 337sub countedged { $_[0]->[ _E ]->_is_COUNT } 338sub multiedged { $_[0]->[ _E ]->_is_MULTI } 339sub hyperedged { $_[0]->[ _E ]->_is_HYPER } 340sub omniedged { $_[0]->[ _E ]->_is_UNORD } 341sub uniqedged { $_[0]->[ _E ]->_is_UNIQ } 342 343*undirected = \&omniedged; 344*omnidirected = \&omniedged; 345sub directed { ! $_[0]->[ _E ]->_is_UNORD } 346 347*is_directed = \&directed; 348*is_undirected = \&undirected; 349 350*is_countvertexed = \&countvertexed; 351*is_multivertexed = \&multivertexed; 352*is_hypervertexed = \&hypervertexed; 353*is_omnidirected = \&omnidirected; 354*is_uniqvertexed = \&uniqvertexed; 355*is_refvertexed = \&refvertexed; 356*is_refvertexed_stringified = \&refvertexed_stringified; 357 358*is_countedged = \&countedged; 359*is_multiedged = \&multiedged; 360*is_hyperedged = \&hyperedged; 361*is_omniedged = \&omniedged; 362*is_uniqedged = \&uniqedged; 363 364sub _union_find_add_vertex { 365 my ($g, $v) = @_; 366 my $UF = $g->[ _U ]; 367 $UF->add( $g->[ _V ]->_get_path_id( $v ) ); 368} 369 370sub add_vertex { 371 my $g = shift; 372 if ($g->is_multivertexed) { 373 return $g->add_vertex_by_id(@_, _GEN_ID); 374 } 375 my @r; 376 if (@_ > 1) { 377 unless ($g->is_countvertexed || $g->is_hypervertexed) { 378 require Carp; 379 Carp::croak("Graph::add_vertex: use add_vertices for more than one vertex or use hypervertexed"); 380 } 381 for my $v ( @_ ) { 382 if (defined $v) { 383 $g->[ _V ]->set_path( $v ) unless $g->has_vertex( $v ); 384 } else { 385 require Carp; 386 Carp::croak("Graph::add_vertex: undef vertex"); 387 } 388 } 389 } 390 for my $v ( @_ ) { 391 unless (defined $v) { 392 require Carp; 393 Carp::croak("Graph::add_vertex: undef vertex"); 394 } 395 } 396 $g->[ _V ]->set_path( @_ ); 397 $g->[ _G ]++; 398 $g->_union_find_add_vertex( @_ ) if $g->has_union_find; 399 return $g; 400} 401 402sub has_vertex { 403 my $g = shift; 404 my $V = $g->[ _V ]; 405 return exists $V->[ _s ]->{ $_[0] } if ($V->[ _f ] & _LIGHT); 406 $V->has_path( @_ ); 407} 408 409sub vertices05 { 410 my $g = shift; 411 my @v = $g->[ _V ]->paths( @_ ); 412 if (wantarray) { 413 return $g->[ _V ]->_is_HYPER ? 414 @v : map { ref $_ eq 'ARRAY' ? @$_ : $_ } @v; 415 } else { 416 return scalar @v; 417 } 418} 419 420sub vertices { 421 my $g = shift; 422 my @v = $g->vertices05; 423 if ($g->is_compat02) { 424 wantarray ? sort @v : scalar @v; 425 } else { 426 if ($g->is_multivertexed || $g->is_countvertexed) { 427 if (wantarray) { 428 my @V; 429 for my $v ( @v ) { 430 push @V, ($v) x $g->get_vertex_count($v); 431 } 432 return @V; 433 } else { 434 my $V = 0; 435 for my $v ( @v ) { 436 $V += $g->get_vertex_count($v); 437 } 438 return $V; 439 } 440 } else { 441 return @v; 442 } 443 } 444} 445 446*vertices_unsorted = \&vertices_unsorted; # Graph 0.20103 compat. 447 448sub unique_vertices { 449 my $g = shift; 450 my @v = $g->vertices05; 451 if ($g->is_compat02) { 452 wantarray ? sort @v : scalar @v; 453 } else { 454 return @v; 455 } 456} 457 458sub has_vertices { 459 my $g = shift; 460 scalar $g->[ _V ]->has_paths( @_ ); 461} 462 463sub _add_edge { 464 my $g = shift; 465 my $V = $g->[ _V ]; 466 my @e; 467 if (($V->[ _f ]) & _LIGHT) { 468 for my $v ( @_ ) { 469 $g->add_vertex( $v ) unless exists $V->[ _s ]->{ $v }; 470 push @e, $V->[ _s ]->{ $v }; 471 } 472 } else { 473 my $h = $g->[ _V ]->_is_HYPER; 474 for my $v ( @_ ) { 475 my @v = ref $v eq 'ARRAY' && $h ? @$v : $v; 476 $g->add_vertex( @v ) unless $V->has_path( @v ); 477 push @e, $V->_get_path_id( @v ); 478 } 479 } 480 return @e; 481} 482 483sub _union_find_add_edge { 484 my ($g, $u, $v) = @_; 485 $g->[ _U ]->union($u, $v); 486} 487 488sub add_edge { 489 my $g = shift; 490 if ($g->is_multiedged) { 491 unless (@_ == 2 || $g->is_hyperedged) { 492 require Carp; 493 Carp::croak("Graph::add_edge: use add_edges for more than one edge"); 494 } 495 return $g->add_edge_by_id(@_, _GEN_ID); 496 } 497 unless (@_ == 2) { 498 unless ($g->is_hyperedged) { 499 require Carp; 500 Carp::croak("Graph::add_edge: graph is not hyperedged"); 501 } 502 } 503 my @e = $g->_add_edge( @_ ); 504 $g->[ _E ]->set_path( @e ); 505 $g->[ _G ]++; 506 $g->_union_find_add_edge( @e ) if $g->has_union_find; 507 return $g; 508} 509 510sub _vertex_ids { 511 my $g = shift; 512 my $V = $g->[ _V ]; 513 my @e; 514 if (($V->[ _f ] & _LIGHT)) { 515 for my $v ( @_ ) { 516 return () unless exists $V->[ _s ]->{ $v }; 517 push @e, $V->[ _s ]->{ $v }; 518 } 519 } else { 520 my $h = $g->[ _V ]->_is_HYPER; 521 for my $v ( @_ ) { 522 my @v = ref $v eq 'ARRAY' && $h ? @$v : $v; 523 return () unless $V->has_path( @v ); 524 push @e, $V->_get_path_id( @v ); 525 } 526 } 527 return @e; 528} 529 530sub has_edge { 531 my $g = shift; 532 my $E = $g->[ _E ]; 533 my $V = $g->[ _V ]; 534 my @i; 535 if (($V->[ _f ] & _LIGHT) && @_ == 2) { 536 return 0 unless 537 exists $V->[ _s ]->{ $_[0] } && 538 exists $V->[ _s ]->{ $_[1] }; 539 @i = @{ $V->[ _s ] }{ @_[ 0, 1 ] }; 540 } else { 541 @i = $g->_vertex_ids( @_ ); 542 return 0 if @i == 0 && @_; 543 } 544 my $f = $E->[ _f ]; 545 if ($E->[ _a ] == 2 && @i == 2 && !($f & (_HYPER|_REF|_UNIQ))) { # Fast path. 546 @i = sort @i if ($f & _UNORD); 547 return exists $E->[ _s ]->{ $i[0] } && 548 exists $E->[ _s ]->{ $i[0] }->{ $i[1] } ? 1 : 0; 549 } else { 550 return defined $E->_get_path_id( @i ) ? 1 : 0; 551 } 552} 553 554sub edges05 { 555 my $g = shift; 556 my $V = $g->[ _V ]; 557 my @e = $g->[ _E ]->paths( @_ ); 558 wantarray ? 559 map { [ map { my @v = $V->_get_id_path($_); 560 @v == 1 ? $v[0] : [ @v ] } 561 @$_ ] } 562 @e : @e; 563} 564 565sub edges02 { 566 my $g = shift; 567 if (@_ && defined $_[0]) { 568 unless (defined $_[1]) { 569 my @e = $g->edges_at($_[0]); 570 wantarray ? 571 map { @$_ } 572 sort { $a->[0] cmp $b->[0] || $a->[1] cmp $b->[1] } @e 573 : @e; 574 } else { 575 die "edges02: unimplemented option"; 576 } 577 } else { 578 my @e = map { ($_) x $g->get_edge_count(@$_) } $g->edges05( @_ ); 579 wantarray ? 580 map { @$_ } 581 sort { $a->[0] cmp $b->[0] || $a->[1] cmp $b->[1] } @e 582 : @e; 583 } 584} 585 586sub unique_edges { 587 my $g = shift; 588 ($g->is_compat02) ? $g->edges02( @_ ) : $g->edges05( @_ ); 589} 590 591sub edges { 592 my $g = shift; 593 if ($g->is_compat02) { 594 return $g->edges02( @_ ); 595 } else { 596 if ($g->is_multiedged || $g->is_countedged) { 597 if (wantarray) { 598 my @E; 599 for my $e ( $g->edges05 ) { 600 push @E, ($e) x $g->get_edge_count(@$e); 601 } 602 return @E; 603 } else { 604 my $E = 0; 605 for my $e ( $g->edges05 ) { 606 $E += $g->get_edge_count(@$e); 607 } 608 return $E; 609 } 610 } else { 611 return $g->edges05; 612 } 613 } 614} 615 616sub has_edges { 617 my $g = shift; 618 scalar $g->[ _E ]->has_paths( @_ ); 619} 620 621### 622# by_id 623# 624 625sub add_vertex_by_id { 626 my $g = shift; 627 $g->expect_multivertexed; 628 $g->[ _V ]->set_path_by_multi_id( @_ ); 629 $g->[ _G ]++; 630 $g->_union_find_add_vertex( @_ ) if $g->has_union_find; 631 return $g; 632} 633 634sub add_vertex_get_id { 635 my $g = shift; 636 $g->expect_multivertexed; 637 my $id = $g->[ _V ]->set_path_by_multi_id( @_, _GEN_ID ); 638 $g->[ _G ]++; 639 $g->_union_find_add_vertex( @_ ) if $g->has_union_find; 640 return $id; 641} 642 643sub has_vertex_by_id { 644 my $g = shift; 645 $g->expect_multivertexed; 646 $g->[ _V ]->has_path_by_multi_id( @_ ); 647} 648 649sub delete_vertex_by_id { 650 my $g = shift; 651 $g->expect_multivertexed; 652 $g->expect_non_unionfind; 653 my $V = $g->[ _V ]; 654 return unless $V->has_path_by_multi_id( @_ ); 655 # TODO: what to about the edges at this vertex? 656 # If the multiness of this vertex goes to zero, delete the edges? 657 $V->del_path_by_multi_id( @_ ); 658 $g->[ _G ]++; 659 return $g; 660} 661 662sub get_multivertex_ids { 663 my $g = shift; 664 $g->expect_multivertexed; 665 $g->[ _V ]->get_multi_ids( @_ ); 666} 667 668sub add_edge_by_id { 669 my $g = shift; 670 $g->expect_multiedged; 671 my $id = pop; 672 my @e = $g->_add_edge( @_ ); 673 $g->[ _E ]->set_path_by_multi_id( @e, $id ); 674 $g->[ _G ]++; 675 $g->_union_find_add_edge( @e ) if $g->has_union_find; 676 return $g; 677} 678 679sub add_edge_get_id { 680 my $g = shift; 681 $g->expect_multiedged; 682 my @i = $g->_add_edge( @_ ); 683 my $id = $g->[ _E ]->set_path_by_multi_id( @i, _GEN_ID ); 684 $g->_union_find_add_edge( @i ) if $g->has_union_find; 685 $g->[ _G ]++; 686 return $id; 687} 688 689sub has_edge_by_id { 690 my $g = shift; 691 $g->expect_multiedged; 692 my $id = pop; 693 my @i = $g->_vertex_ids( @_ ); 694 return 0 if @i == 0 && @_; 695 $g->[ _E ]->has_path_by_multi_id( @i, $id ); 696} 697 698sub delete_edge_by_id { 699 my $g = shift; 700 $g->expect_multiedged; 701 $g->expect_non_unionfind; 702 my $V = $g->[ _E ]; 703 my $id = pop; 704 my @i = $g->_vertex_ids( @_ ); 705 return unless $V->has_path_by_multi_id( @i, $id ); 706 $V->del_path_by_multi_id( @i, $id ); 707 $g->[ _G ]++; 708 return $g; 709} 710 711sub get_multiedge_ids { 712 my $g = shift; 713 $g->expect_multiedged; 714 my @id = $g->_vertex_ids( @_ ); 715 return unless @id; 716 $g->[ _E ]->get_multi_ids( @id ); 717} 718 719### 720# Neighbourhood. 721# 722 723sub vertices_at { 724 my $g = shift; 725 my $V = $g->[ _V ]; 726 return @_ unless ($V->[ _f ] & _HYPER); 727 my %v; 728 my @i; 729 for my $v ( @_ ) { 730 my $i = $V->_get_path_id( $v ); 731 return unless defined $i; 732 push @i, ( $v{ $v } = $i ); 733 } 734 my $Vi = $V->_ids; 735 my @v; 736 while (my ($i, $v) = each %{ $Vi }) { 737 my %i; 738 my $h = $V->[_f ] & _HYPER; 739 @i{ @i } = @i if @i; # @todo: nonuniq hyper vertices? 740 for my $u (ref $v eq 'ARRAY' && $h ? @$v : $v) { 741 my $j = exists $v{ $u } ? $v{ $u } : ( $v{ $u } = $i ); 742 if (defined $j && exists $i{ $j }) { 743 delete $i{ $j }; 744 unless (keys %i) { 745 push @v, $v; 746 last; 747 } 748 } 749 } 750 } 751 return @v; 752} 753 754sub _edges_at { 755 my $g = shift; 756 my $V = $g->[ _V ]; 757 my $E = $g->[ _E ]; 758 my @e; 759 my $en = 0; 760 my %ev; 761 my $h = $V->[_f ] & _HYPER; 762 for my $v ( $h ? $g->vertices_at( @_ ) : @_ ) { 763 my $vi = $V->_get_path_id( ref $v eq 'ARRAY' && $h ? @$v : $v ); 764 next unless defined $vi; 765 my $Ei = $E->_ids; 766 while (my ($ei, $ev) = each %{ $Ei }) { 767 if (wantarray) { 768 for my $j (@$ev) { 769 push @e, [ $ei, $ev ] 770 if $j == $vi && !$ev{$ei}++; 771 } 772 } else { 773 for my $j (@$ev) { 774 $en++ if $j == $vi; 775 } 776 } 777 } 778 } 779 return wantarray ? @e : $en; 780} 781 782sub _edges { 783 my $g = shift; 784 my $n = pop; 785 my $i = $n == _S ? 0 : -1; # _edges_from() or _edges_to() 786 my $V = $g->[ _V ]; 787 my $E = $g->[ _E ]; 788 my $N = $g->[ $n ]; 789 my $h = $V->[ _f ] & _HYPER; 790 unless (defined $N && $N->[ 0 ] == $g->[ _G ]) { 791 $g->[ $n ]->[ 1 ] = { }; 792 $N = $g->[ $n ]; 793 my $u = $E->[ _f ] & _UNORD; 794 my $Ei = $E->_ids; 795 while (my ($ei, $ev) = each %{ $Ei }) { 796 next unless @$ev; 797 my $e = [ $ei, $ev ]; 798 if ($u) { 799 push @{ $N->[ 1 ]->{ $ev->[ 0] } }, $e; 800 push @{ $N->[ 1 ]->{ $ev->[-1] } }, $e; 801 } else { 802 my $e = [ $ei, $ev ]; 803 push @{ $N->[ 1 ]->{ $ev->[$i] } }, $e; 804 } 805 } 806 $N->[ 0 ] = $g->[ _G ]; 807 } 808 my @e; 809 my @at = $h ? $g->vertices_at( @_ ) : @_; 810 my %at; @at{@at} = (); 811 for my $v ( @at ) { 812 my $vi = $V->_get_path_id( ref $v eq 'ARRAY' && $h ? @$v : $v ); 813 next unless defined $vi && exists $N->[ 1 ]->{ $vi }; 814 push @e, @{ $N->[ 1 ]->{ $vi } }; 815 } 816 if (wantarray && $g->is_undirected) { 817 my @i = map { $V->_get_path_id( $_ ) } @_; 818 for my $e ( @e ) { 819 unless ( $e->[ 1 ]->[ $i ] == $i[ $i ] ) { 820 $e = [ $e->[ 0 ], [ reverse @{ $e->[ 1 ] } ] ]; 821 } 822 } 823 } 824 return @e; 825} 826 827sub _edges_from { 828 push @_, _S; 829 goto &_edges; 830} 831 832sub _edges_to { 833 push @_, _P; 834 goto &_edges; 835} 836 837sub _edges_id_path { 838 my $g = shift; 839 my $V = $g->[ _V ]; 840 [ map { my @v = $V->_get_id_path($_); 841 @v == 1 ? $v[0] : [ @v ] } 842 @{ $_[0]->[1] } ]; 843} 844 845sub edges_at { 846 my $g = shift; 847 map { $g->_edges_id_path($_ ) } $g->_edges_at( @_ ); 848} 849 850sub edges_from { 851 my $g = shift; 852 map { $g->_edges_id_path($_ ) } $g->_edges_from( @_ ); 853} 854 855sub edges_to { 856 my $g = shift; 857 map { $g->_edges_id_path($_ ) } $g->_edges_to( @_ ); 858} 859 860sub successors { 861 my $g = shift; 862 my $E = $g->[ _E ]; 863 ($E->[ _f ] & _LIGHT) ? 864 $E->_successors($g, @_) : 865 Graph::AdjacencyMap::_successors($E, $g, @_); 866} 867 868sub predecessors { 869 my $g = shift; 870 my $E = $g->[ _E ]; 871 ($E->[ _f ] & _LIGHT) ? 872 $E->_predecessors($g, @_) : 873 Graph::AdjacencyMap::_predecessors($E, $g, @_); 874} 875 876sub _all_successors { 877 my $g = shift; 878 my @init = @_; 879 my %todo; 880 @todo{@init} = @init; 881 my %seen; 882 my %init = %todo; 883 my %self; 884 while (keys %todo) { 885 my @todo = values %todo; 886 for my $t (@todo) { 887 $seen{$t} = delete $todo{$t}; 888 for my $s ($g->successors($t)) { 889 $self{$s} = $s if exists $init{$s}; 890 $todo{$s} = $s unless exists $seen{$s}; 891 } 892 } 893 } 894 for my $v (@init) { 895 delete $seen{$v} unless $g->has_edge($v, $v) || $self{$v}; 896 } 897 return values %seen; 898} 899 900sub all_successors { 901 my $g = shift; 902 $g->expect_directed; 903 return $g->_all_successors(@_); 904} 905 906sub _all_predecessors { 907 my $g = shift; 908 my @init = @_; 909 my %todo; 910 @todo{@init} = @init; 911 my %seen; 912 my %init = %todo; 913 my %self; 914 while (keys %todo) { 915 my @todo = values %todo; 916 for my $t (@todo) { 917 $seen{$t} = delete $todo{$t}; 918 for my $p ($g->predecessors($t)) { 919 $self{$p} = $p if exists $init{$p}; 920 $todo{$p} = $p unless exists $seen{$p}; 921 } 922 } 923 } 924 for my $v (@init) { 925 delete $seen{$v} unless $g->has_edge($v, $v) || $self{$v}; 926 } 927 return values %seen; 928} 929 930sub all_predecessors { 931 my $g = shift; 932 $g->expect_directed; 933 return $g->_all_predecessors(@_); 934} 935 936sub neighbours { 937 my $g = shift; 938 my $V = $g->[ _V ]; 939 my @s = map { my @v = @{ $_->[ 1 ] }; shift @v; @v } $g->_edges_from( @_ ); 940 my @p = map { my @v = @{ $_->[ 1 ] }; pop @v; @v } $g->_edges_to ( @_ ); 941 my %n; 942 @n{ @s } = @s; 943 @n{ @p } = @p; 944 map { $V->_get_id_path($_) } keys %n; 945} 946 947*neighbors = \&neighbours; 948 949sub all_neighbours { 950 my $g = shift; 951 my @init = @_; 952 my @v = @init; 953 my %n; 954 my $o = 0; 955 while (1) { 956 my @p = $g->_all_predecessors(@v); 957 my @s = $g->_all_successors(@v); 958 @n{@p} = @p; 959 @n{@s} = @s; 960 @v = values %n; 961 last if @v == $o; # Leave if no growth. 962 $o = @v; 963 } 964 for my $v (@init) { 965 delete $n{$v} unless $g->has_edge($v, $v); 966 } 967 return values %n; 968} 969 970*all_neighbors = \&all_neighbours; 971 972sub all_reachable { 973 my $g = shift; 974 $g->directed ? $g->all_successors(@_) : $g->all_neighbors(@_); 975} 976 977sub delete_edge { 978 my $g = shift; 979 $g->expect_non_unionfind; 980 my @i = $g->_vertex_ids( @_ ); 981 return $g unless @i; 982 my $i = $g->[ _E ]->_get_path_id( @i ); 983 return $g unless defined $i; 984 $g->[ _E ]->_del_id( $i ); 985 $g->[ _G ]++; 986 return $g; 987} 988 989sub delete_vertex { 990 my $g = shift; 991 $g->expect_non_unionfind; 992 my $V = $g->[ _V ]; 993 return $g unless $V->has_path( @_ ); 994 my $E = $g->[ _E ]; 995 for my $e ( $g->_edges_at( @_ ) ) { 996 $E->_del_id( $e->[ 0 ] ); 997 } 998 $V->del_path( @_ ); 999 $g->[ _G ]++; 1000 return $g; 1001} 1002 1003sub get_vertex_count { 1004 my $g = shift; 1005 $g->[ _V ]->_get_path_count( @_ ) || 0; 1006} 1007 1008sub get_edge_count { 1009 my $g = shift; 1010 my @e = $g->_vertex_ids( @_ ); 1011 return 0 unless @e; 1012 $g->[ _E ]->_get_path_count( @e ) || 0; 1013} 1014 1015sub delete_vertices { 1016 my $g = shift; 1017 $g->expect_non_unionfind; 1018 while (@_) { 1019 my $v = shift @_; 1020 $g->delete_vertex($v); 1021 } 1022 return $g; 1023} 1024 1025sub delete_edges { 1026 my $g = shift; 1027 $g->expect_non_unionfind; 1028 while (@_) { 1029 my ($u, $v) = splice @_, 0, 2; 1030 $g->delete_edge($u, $v); 1031 } 1032 return $g; 1033} 1034 1035### 1036# Degrees. 1037# 1038 1039sub _in_degree { 1040 my $g = shift; 1041 return undef unless @_ && $g->has_vertex( @_ ); 1042 my $in = 0; 1043 $in += $g->get_edge_count( @$_ ) for $g->edges_to( @_ ); 1044 return $in; 1045} 1046 1047sub in_degree { 1048 my $g = shift; 1049 $g->_in_degree( @_ ); 1050} 1051 1052sub _out_degree { 1053 my $g = shift; 1054 return undef unless @_ && $g->has_vertex( @_ ); 1055 my $out = 0; 1056 $out += $g->get_edge_count( @$_ ) for $g->edges_from( @_ ); 1057 return $out; 1058} 1059 1060sub out_degree { 1061 my $g = shift; 1062 $g->_out_degree( @_ ); 1063} 1064 1065sub _total_degree { 1066 my $g = shift; 1067 return undef unless @_ && $g->has_vertex( @_ ); 1068 $g->is_undirected ? 1069 $g->_in_degree( @_ ) : 1070 $g-> in_degree( @_ ) - $g-> out_degree( @_ ); 1071} 1072 1073sub degree { 1074 my $g = shift; 1075 if (@_) { 1076 $g->_total_degree( @_ ); 1077 } elsif ($g->is_undirected) { 1078 my $total = 0; 1079 $total += $g->_total_degree( $_ ) for $g->vertices05; 1080 return $total; 1081 } else { 1082 return 0; 1083 } 1084} 1085 1086*vertex_degree = \°ree; 1087 1088sub is_sink_vertex { 1089 my $g = shift; 1090 return 0 unless @_; 1091 $g->successors( @_ ) == 0 && $g->predecessors( @_ ) > 0; 1092} 1093 1094sub is_source_vertex { 1095 my $g = shift; 1096 return 0 unless @_; 1097 $g->predecessors( @_ ) == 0 && $g->successors( @_ ) > 0; 1098} 1099 1100sub is_successorless_vertex { 1101 my $g = shift; 1102 return 0 unless @_; 1103 $g->successors( @_ ) == 0; 1104} 1105 1106sub is_predecessorless_vertex { 1107 my $g = shift; 1108 return 0 unless @_; 1109 $g->predecessors( @_ ) == 0; 1110} 1111 1112sub is_successorful_vertex { 1113 my $g = shift; 1114 return 0 unless @_; 1115 $g->successors( @_ ) > 0; 1116} 1117 1118sub is_predecessorful_vertex { 1119 my $g = shift; 1120 return 0 unless @_; 1121 $g->predecessors( @_ ) > 0; 1122} 1123 1124sub is_isolated_vertex { 1125 my $g = shift; 1126 return 0 unless @_; 1127 $g->predecessors( @_ ) == 0 && $g->successors( @_ ) == 0; 1128} 1129 1130sub is_interior_vertex { 1131 my $g = shift; 1132 return 0 unless @_; 1133 my $p = $g->predecessors( @_ ); 1134 my $s = $g->successors( @_ ); 1135 if ($g->is_self_loop_vertex( @_ )) { 1136 $p--; 1137 $s--; 1138 } 1139 $p > 0 && $s > 0; 1140} 1141 1142sub is_exterior_vertex { 1143 my $g = shift; 1144 return 0 unless @_; 1145 $g->predecessors( @_ ) == 0 || $g->successors( @_ ) == 0; 1146} 1147 1148sub is_self_loop_vertex { 1149 my $g = shift; 1150 return 0 unless @_; 1151 for my $s ( $g->successors( @_ ) ) { 1152 return 1 if $s eq $_[0]; # @todo: multiedges, hypervertices 1153 } 1154 return 0; 1155} 1156 1157sub sink_vertices { 1158 my $g = shift; 1159 grep { $g->is_sink_vertex($_) } $g->vertices05; 1160} 1161 1162sub source_vertices { 1163 my $g = shift; 1164 grep { $g->is_source_vertex($_) } $g->vertices05; 1165} 1166 1167sub successorless_vertices { 1168 my $g = shift; 1169 grep { $g->is_successorless_vertex($_) } $g->vertices05; 1170} 1171 1172sub predecessorless_vertices { 1173 my $g = shift; 1174 grep { $g->is_predecessorless_vertex($_) } $g->vertices05; 1175} 1176 1177sub successorful_vertices { 1178 my $g = shift; 1179 grep { $g->is_successorful_vertex($_) } $g->vertices05; 1180} 1181 1182sub predecessorful_vertices { 1183 my $g = shift; 1184 grep { $g->is_predecessorful_vertex($_) } $g->vertices05; 1185} 1186 1187sub isolated_vertices { 1188 my $g = shift; 1189 grep { $g->is_isolated_vertex($_) } $g->vertices05; 1190} 1191 1192sub interior_vertices { 1193 my $g = shift; 1194 grep { $g->is_interior_vertex($_) } $g->vertices05; 1195} 1196 1197sub exterior_vertices { 1198 my $g = shift; 1199 grep { $g->is_exterior_vertex($_) } $g->vertices05; 1200} 1201 1202sub self_loop_vertices { 1203 my $g = shift; 1204 grep { $g->is_self_loop_vertex($_) } $g->vertices05; 1205} 1206 1207### 1208# Paths and cycles. 1209# 1210 1211sub add_path { 1212 my $g = shift; 1213 my $u = shift; 1214 while (@_) { 1215 my $v = shift; 1216 $g->add_edge($u, $v); 1217 $u = $v; 1218 } 1219 return $g; 1220} 1221 1222sub delete_path { 1223 my $g = shift; 1224 $g->expect_non_unionfind; 1225 my $u = shift; 1226 while (@_) { 1227 my $v = shift; 1228 $g->delete_edge($u, $v); 1229 $u = $v; 1230 } 1231 return $g; 1232} 1233 1234sub has_path { 1235 my $g = shift; 1236 my $u = shift; 1237 while (@_) { 1238 my $v = shift; 1239 return 0 unless $g->has_edge($u, $v); 1240 $u = $v; 1241 } 1242 return $g; 1243} 1244 1245sub add_cycle { 1246 my $g = shift; 1247 $g->add_path(@_, $_[0]); 1248} 1249 1250sub delete_cycle { 1251 my $g = shift; 1252 $g->expect_non_unionfind; 1253 $g->delete_path(@_, $_[0]); 1254} 1255 1256sub has_cycle { 1257 my $g = shift; 1258 @_ ? ($g->has_path(@_, $_[0]) ? 1 : 0) : 0; 1259} 1260 1261sub has_a_cycle { 1262 my $g = shift; 1263 my @r = ( back_edge => \&Graph::Traversal::has_a_cycle ); 1264 push @r, 1265 down_edge => \&Graph::Traversal::has_a_cycle 1266 if $g->is_undirected; 1267 my $t = Graph::Traversal::DFS->new($g, @r, @_); 1268 $t->dfs; 1269 return $t->get_state('has_a_cycle'); 1270} 1271 1272sub find_a_cycle { 1273 my $g = shift; 1274 my @r = ( back_edge => \&Graph::Traversal::find_a_cycle); 1275 push @r, 1276 down_edge => \&Graph::Traversal::find_a_cycle 1277 if $g->is_undirected; 1278 my $t = Graph::Traversal::DFS->new($g, @r, @_); 1279 $t->dfs; 1280 $t->has_state('a_cycle') ? @{ $t->get_state('a_cycle') } : (); 1281} 1282 1283### 1284# Attributes. 1285 1286# Vertex attributes. 1287 1288sub set_vertex_attribute { 1289 my $g = shift; 1290 $g->expect_non_multivertexed; 1291 my $value = pop; 1292 my $attr = pop; 1293 $g->add_vertex( @_ ) unless $g->has_vertex( @_ ); 1294 $g->[ _V ]->_set_path_attr( @_, $attr, $value ); 1295} 1296 1297sub set_vertex_attribute_by_id { 1298 my $g = shift; 1299 $g->expect_multivertexed; 1300 my $value = pop; 1301 my $attr = pop; 1302 $g->add_vertex_by_id( @_ ) unless $g->has_vertex_by_id( @_ ); 1303 $g->[ _V ]->_set_path_attr( @_, $attr, $value ); 1304} 1305 1306sub set_vertex_attributes { 1307 my $g = shift; 1308 $g->expect_non_multivertexed; 1309 my $attr = pop; 1310 $g->add_vertex( @_ ) unless $g->has_vertex( @_ ); 1311 $g->[ _V ]->_set_path_attrs( @_, $attr ); 1312} 1313 1314sub set_vertex_attributes_by_id { 1315 my $g = shift; 1316 $g->expect_multivertexed; 1317 my $attr = pop; 1318 $g->add_vertex_by_id( @_ ) unless $g->has_vertex_by_id( @_ ); 1319 $g->[ _V ]->_set_path_attrs( @_, $attr ); 1320} 1321 1322sub has_vertex_attributes { 1323 my $g = shift; 1324 $g->expect_non_multivertexed; 1325 return 0 unless $g->has_vertex( @_ ); 1326 $g->[ _V ]->_has_path_attrs( @_ ); 1327} 1328 1329sub has_vertex_attributes_by_id { 1330 my $g = shift; 1331 $g->expect_multivertexed; 1332 return 0 unless $g->has_vertex_by_id( @_ ); 1333 $g->[ _V ]->_has_path_attrs( @_ ); 1334} 1335 1336sub has_vertex_attribute { 1337 my $g = shift; 1338 $g->expect_non_multivertexed; 1339 my $attr = pop; 1340 return 0 unless $g->has_vertex( @_ ); 1341 $g->[ _V ]->_has_path_attr( @_, $attr ); 1342} 1343 1344sub has_vertex_attribute_by_id { 1345 my $g = shift; 1346 $g->expect_multivertexed; 1347 my $attr = pop; 1348 return 0 unless $g->has_vertex_by_id( @_ ); 1349 $g->[ _V ]->_has_path_attr( @_, $attr ); 1350} 1351 1352sub get_vertex_attributes { 1353 my $g = shift; 1354 $g->expect_non_multivertexed; 1355 return unless $g->has_vertex( @_ ); 1356 my $a = $g->[ _V ]->_get_path_attrs( @_ ); 1357 ($g->is_compat02) ? (defined $a ? %{ $a } : ()) : $a; 1358} 1359 1360sub get_vertex_attributes_by_id { 1361 my $g = shift; 1362 $g->expect_multivertexed; 1363 return unless $g->has_vertex_by_id( @_ ); 1364 $g->[ _V ]->_get_path_attrs( @_ ); 1365} 1366 1367sub get_vertex_attribute { 1368 my $g = shift; 1369 $g->expect_non_multivertexed; 1370 my $attr = pop; 1371 return unless $g->has_vertex( @_ ); 1372 $g->[ _V ]->_get_path_attr( @_, $attr ); 1373} 1374 1375sub get_vertex_attribute_by_id { 1376 my $g = shift; 1377 $g->expect_multivertexed; 1378 my $attr = pop; 1379 return unless $g->has_vertex_by_id( @_ ); 1380 $g->[ _V ]->_get_path_attr( @_, $attr ); 1381} 1382 1383sub get_vertex_attribute_names { 1384 my $g = shift; 1385 $g->expect_non_multivertexed; 1386 return unless $g->has_vertex( @_ ); 1387 $g->[ _V ]->_get_path_attr_names( @_ ); 1388} 1389 1390sub get_vertex_attribute_names_by_id { 1391 my $g = shift; 1392 $g->expect_multivertexed; 1393 return unless $g->has_vertex_by_id( @_ ); 1394 $g->[ _V ]->_get_path_attr_names( @_ ); 1395} 1396 1397sub get_vertex_attribute_values { 1398 my $g = shift; 1399 $g->expect_non_multivertexed; 1400 return unless $g->has_vertex( @_ ); 1401 $g->[ _V ]->_get_path_attr_values( @_ ); 1402} 1403 1404sub get_vertex_attribute_values_by_id { 1405 my $g = shift; 1406 $g->expect_multivertexed; 1407 return unless $g->has_vertex_by_id( @_ ); 1408 $g->[ _V ]->_get_path_attr_values( @_ ); 1409} 1410 1411sub delete_vertex_attributes { 1412 my $g = shift; 1413 $g->expect_non_multivertexed; 1414 return undef unless $g->has_vertex( @_ ); 1415 $g->[ _V ]->_del_path_attrs( @_ ); 1416} 1417 1418sub delete_vertex_attributes_by_id { 1419 my $g = shift; 1420 $g->expect_multivertexed; 1421 return undef unless $g->has_vertex_by_id( @_ ); 1422 $g->[ _V ]->_del_path_attrs( @_ ); 1423} 1424 1425sub delete_vertex_attribute { 1426 my $g = shift; 1427 $g->expect_non_multivertexed; 1428 my $attr = pop; 1429 return undef unless $g->has_vertex( @_ ); 1430 $g->[ _V ]->_del_path_attr( @_, $attr ); 1431} 1432 1433sub delete_vertex_attribute_by_id { 1434 my $g = shift; 1435 $g->expect_multivertexed; 1436 my $attr = pop; 1437 return undef unless $g->has_vertex_by_id( @_ ); 1438 $g->[ _V ]->_del_path_attr( @_, $attr ); 1439} 1440 1441# Edge attributes. 1442 1443sub _set_edge_attribute { 1444 my $g = shift; 1445 my $value = pop; 1446 my $attr = pop; 1447 my $E = $g->[ _E ]; 1448 my $f = $E->[ _f ]; 1449 my @i; 1450 if ($E->[ _a ] == 2 && @_ == 2 && !($f & (_HYPER|_REF|_UNIQ))) { # Fast path. 1451 @_ = sort @_ if ($f & _UNORD); 1452 my $s = $E->[ _s ]; 1453 $g->add_edge( @_ ) unless exists $s->{ $_[0] } && exists $s->{ $_[0] }->{ $_[1] }; 1454 @i = @{ $g->[ _V ]->[ _s ] }{ @_ }; 1455 } else { 1456 $g->add_edge( @_ ) unless $g->has_edge( @_ ); 1457 @i = $g->_vertex_ids( @_ ); 1458 } 1459 $g->[ _E ]->_set_path_attr( @i, $attr, $value ); 1460} 1461 1462sub set_edge_attribute { 1463 my $g = shift; 1464 $g->expect_non_multiedged; 1465 my $value = pop; 1466 my $attr = pop; 1467 my $E = $g->[ _E ]; 1468 $g->add_edge( @_ ) unless $g->has_edge( @_ ); 1469 $E->_set_path_attr( $g->_vertex_ids( @_ ), $attr, $value ); 1470} 1471 1472sub set_edge_attribute_by_id { 1473 my $g = shift; 1474 $g->expect_multiedged; 1475 my $value = pop; 1476 my $attr = pop; 1477 # $g->add_edge_by_id( @_ ) unless $g->has_edge_by_id( @_ ); 1478 my $id = pop; 1479 $g->[ _E ]->_set_path_attr( $g->_vertex_ids( @_ ), $id, $attr, $value ); 1480} 1481 1482sub set_edge_attributes { 1483 my $g = shift; 1484 $g->expect_non_multiedged; 1485 my $attr = pop; 1486 $g->add_edge( @_ ) unless $g->has_edge( @_ ); 1487 $g->[ _E ]->_set_path_attrs( $g->_vertex_ids( @_ ), $attr ); 1488} 1489 1490sub set_edge_attributes_by_id { 1491 my $g = shift; 1492 $g->expect_multiedged; 1493 my $attr = pop; 1494 $g->add_edge_by_id( @_ ) unless $g->has_edge_by_id( @_ ); 1495 my $id = pop; 1496 $g->[ _E ]->_set_path_attrs( $g->_vertex_ids( @_ ), $id, $attr ); 1497} 1498 1499sub has_edge_attributes { 1500 my $g = shift; 1501 $g->expect_non_multiedged; 1502 return 0 unless $g->has_edge( @_ ); 1503 $g->[ _E ]->_has_path_attrs( $g->_vertex_ids( @_ ) ); 1504} 1505 1506sub has_edge_attributes_by_id { 1507 my $g = shift; 1508 $g->expect_multiedged; 1509 return 0 unless $g->has_edge_by_id( @_ ); 1510 my $id = pop; 1511 $g->[ _E ]->_has_path_attrs( $g->_vertex_ids( @_ ), $id ); 1512} 1513 1514sub has_edge_attribute { 1515 my $g = shift; 1516 $g->expect_non_multiedged; 1517 my $attr = pop; 1518 return 0 unless $g->has_edge( @_ ); 1519 $g->[ _E ]->_has_path_attr( $g->_vertex_ids( @_ ), $attr ); 1520} 1521 1522sub has_edge_attribute_by_id { 1523 my $g = shift; 1524 $g->expect_multiedged; 1525 my $attr = pop; 1526 return 0 unless $g->has_edge_by_id( @_ ); 1527 my $id = pop; 1528 $g->[ _E ]->_has_path_attr( $g->_vertex_ids( @_ ), $id, $attr ); 1529} 1530 1531sub get_edge_attributes { 1532 my $g = shift; 1533 $g->expect_non_multiedged; 1534 return unless $g->has_edge( @_ ); 1535 my $a = $g->[ _E ]->_get_path_attrs( $g->_vertex_ids( @_ ) ); 1536 ($g->is_compat02) ? (defined $a ? %{ $a } : ()) : $a; 1537} 1538 1539sub get_edge_attributes_by_id { 1540 my $g = shift; 1541 $g->expect_multiedged; 1542 return unless $g->has_edge_by_id( @_ ); 1543 my $id = pop; 1544 return $g->[ _E ]->_get_path_attrs( $g->_vertex_ids( @_ ), $id ); 1545} 1546 1547sub _get_edge_attribute { # Fast path; less checks. 1548 my $g = shift; 1549 my $attr = pop; 1550 my $E = $g->[ _E ]; 1551 my $f = $E->[ _f ]; 1552 if ($E->[ _a ] == 2 && @_ == 2 && !($f & (_HYPER|_REF|_UNIQ))) { # Fast path. 1553 @_ = sort @_ if ($f & _UNORD); 1554 my $s = $E->[ _s ]; 1555 return unless exists $s->{ $_[0] } && exists $s->{ $_[0] }->{ $_[1] }; 1556 } else { 1557 return unless $g->has_edge( @_ ); 1558 } 1559 my @i = $g->_vertex_ids( @_ ); 1560 $E->_get_path_attr( @i, $attr ); 1561} 1562 1563sub get_edge_attribute { 1564 my $g = shift; 1565 $g->expect_non_multiedged; 1566 my $attr = pop; 1567 return undef unless $g->has_edge( @_ ); 1568 my @i = $g->_vertex_ids( @_ ); 1569 return undef if @i == 0 && @_; 1570 my $E = $g->[ _E ]; 1571 $E->_get_path_attr( @i, $attr ); 1572} 1573 1574sub get_edge_attribute_by_id { 1575 my $g = shift; 1576 $g->expect_multiedged; 1577 my $attr = pop; 1578 return unless $g->has_edge_by_id( @_ ); 1579 my $id = pop; 1580 $g->[ _E ]->_get_path_attr( $g->_vertex_ids( @_ ), $id, $attr ); 1581} 1582 1583sub get_edge_attribute_names { 1584 my $g = shift; 1585 $g->expect_non_multiedged; 1586 return unless $g->has_edge( @_ ); 1587 $g->[ _E ]->_get_path_attr_names( $g->_vertex_ids( @_ ) ); 1588} 1589 1590sub get_edge_attribute_names_by_id { 1591 my $g = shift; 1592 $g->expect_multiedged; 1593 return unless $g->has_edge_by_id( @_ ); 1594 my $id = pop; 1595 $g->[ _E ]->_get_path_attr_names( $g->_vertex_ids( @_ ), $id ); 1596} 1597 1598sub get_edge_attribute_values { 1599 my $g = shift; 1600 $g->expect_non_multiedged; 1601 return unless $g->has_edge( @_ ); 1602 $g->[ _E ]->_get_path_attr_values( $g->_vertex_ids( @_ ) ); 1603} 1604 1605sub get_edge_attribute_values_by_id { 1606 my $g = shift; 1607 $g->expect_multiedged; 1608 return unless $g->has_edge_by_id( @_ ); 1609 my $id = pop; 1610 $g->[ _E ]->_get_path_attr_values( $g->_vertex_ids( @_ ), $id ); 1611} 1612 1613sub delete_edge_attributes { 1614 my $g = shift; 1615 $g->expect_non_multiedged; 1616 return unless $g->has_edge( @_ ); 1617 $g->[ _E ]->_del_path_attrs( $g->_vertex_ids( @_ ) ); 1618} 1619 1620sub delete_edge_attributes_by_id { 1621 my $g = shift; 1622 $g->expect_multiedged; 1623 return unless $g->has_edge_by_id( @_ ); 1624 my $id = pop; 1625 $g->[ _E ]->_del_path_attrs( $g->_vertex_ids( @_ ), $id ); 1626} 1627 1628sub delete_edge_attribute { 1629 my $g = shift; 1630 $g->expect_non_multiedged; 1631 my $attr = pop; 1632 return unless $g->has_edge( @_ ); 1633 $g->[ _E ]->_del_path_attr( $g->_vertex_ids( @_ ), $attr ); 1634} 1635 1636sub delete_edge_attribute_by_id { 1637 my $g = shift; 1638 $g->expect_multiedged; 1639 my $attr = pop; 1640 return unless $g->has_edge_by_id( @_ ); 1641 my $id = pop; 1642 $g->[ _E ]->_del_path_attr( $g->_vertex_ids( @_ ), $id, $attr ); 1643} 1644 1645### 1646# Compat. 1647# 1648 1649sub vertex { 1650 my $g = shift; 1651 $g->has_vertex( @_ ) ? @_ : undef; 1652} 1653 1654sub out_edges { 1655 my $g = shift; 1656 return unless @_ && $g->has_vertex( @_ ); 1657 my @e = $g->edges_from( @_ ); 1658 wantarray ? map { @$_ } @e : @e; 1659} 1660 1661sub in_edges { 1662 my $g = shift; 1663 return unless @_ && $g->has_vertex( @_ ); 1664 my @e = $g->edges_to( @_ ); 1665 wantarray ? map { @$_ } @e : @e; 1666} 1667 1668sub add_vertices { 1669 my $g = shift; 1670 $g->add_vertex( $_ ) for @_; 1671 return $g; 1672} 1673 1674sub add_edges { 1675 my $g = shift; 1676 while (@_) { 1677 my $u = shift @_; 1678 if (ref $u eq 'ARRAY') { 1679 $g->add_edge( @$u ); 1680 } else { 1681 if (@_) { 1682 my $v = shift @_; 1683 $g->add_edge( $u, $v ); 1684 } else { 1685 require Carp; 1686 Carp::croak("Graph::add_edges: missing end vertex"); 1687 } 1688 } 1689 } 1690 return $g; 1691} 1692 1693### 1694# More constructors. 1695# 1696 1697sub copy { 1698 my $g = shift; 1699 my %opt = _get_options( \@_ ); 1700 1701 my $c = 1702 (ref $g)->new(map { $_ => $g->$_ ? 1 : 0 } 1703 qw(directed 1704 compat02 1705 refvertexed 1706 hypervertexed 1707 countvertexed 1708 multivertexed 1709 hyperedged 1710 countedged 1711 multiedged 1712 omniedged 1713 __stringified)); 1714 for my $v ($g->isolated_vertices) { $c->add_vertex($v) } 1715 for my $e ($g->edges05) { $c->add_edge(@$e) } 1716 1717 return $c; 1718} 1719 1720*copy_graph = \© 1721 1722sub _deep_copy_Storable { 1723 my $g = shift; 1724 my $safe = new Safe; 1725 local $Storable::Deparse = 1; 1726 local $Storable::Eval = sub { $safe->reval($_[0]) }; 1727 return Storable::thaw(Storable::freeze($g)); 1728} 1729 1730sub _deep_copy_DataDumper { 1731 my $g = shift; 1732 my $d = Data::Dumper->new([$g]); 1733 use vars qw($VAR1); 1734 $d->Purity(1)->Terse(1)->Deepcopy(1); 1735 $d->Deparse(1) if $] >= 5.008; 1736 eval $d->Dump; 1737} 1738 1739sub deep_copy { 1740 if (_can_deep_copy_Storable()) { 1741 return _deep_copy_Storable(@_); 1742 } else { 1743 return _deep_copy_DataDumper(@_); 1744 } 1745} 1746 1747*deep_copy_graph = \&deep_copy; 1748 1749sub transpose_edge { 1750 my $g = shift; 1751 if ($g->is_directed) { 1752 return undef unless $g->has_edge( @_ ); 1753 my $c = $g->get_edge_count( @_ ); 1754 my $a = $g->get_edge_attributes( @_ ); 1755 my @e = reverse @_; 1756 $g->delete_edge( @_ ) unless $g->has_edge( @e ); 1757 $g->add_edge( @e ) for 1..$c; 1758 $g->set_edge_attributes(@e, $a) if $a; 1759 } 1760 return $g; 1761} 1762 1763sub transpose_graph { 1764 my $g = shift; 1765 my $t = $g->copy; 1766 if ($t->directed) { 1767 for my $e ($t->edges05) { 1768 $t->transpose_edge(@$e); 1769 } 1770 } 1771 return $t; 1772} 1773 1774*transpose = \&transpose_graph; 1775 1776sub complete_graph { 1777 my $g = shift; 1778 my $c = $g->new( directed => $g->directed ); 1779 my @v = $g->vertices05; 1780 for (my $i = 0; $i <= $#v; $i++ ) { 1781 for (my $j = 0; $j <= $#v; $j++ ) { 1782 next if $i >= $j; 1783 if ($g->is_undirected) { 1784 $c->add_edge($v[$i], $v[$j]); 1785 } else { 1786 $c->add_edge($v[$i], $v[$j]); 1787 $c->add_edge($v[$j], $v[$i]); 1788 } 1789 } 1790 } 1791 return $c; 1792} 1793 1794*complement = \&complement_graph; 1795 1796sub complement_graph { 1797 my $g = shift; 1798 my $c = $g->new( directed => $g->directed ); 1799 my @v = $g->vertices05; 1800 for (my $i = 0; $i <= $#v; $i++ ) { 1801 for (my $j = 0; $j <= $#v; $j++ ) { 1802 next if $i >= $j; 1803 if ($g->is_undirected) { 1804 $c->add_edge($v[$i], $v[$j]) 1805 unless $g->has_edge($v[$i], $v[$j]); 1806 } else { 1807 $c->add_edge($v[$i], $v[$j]) 1808 unless $g->has_edge($v[$i], $v[$j]); 1809 $c->add_edge($v[$j], $v[$i]) 1810 unless $g->has_edge($v[$j], $v[$i]); 1811 } 1812 } 1813 } 1814 return $c; 1815} 1816 1817*complete = \&complete_graph; 1818 1819### 1820# Transitivity. 1821# 1822 1823sub is_transitive { 1824 my $g = shift; 1825 Graph::TransitiveClosure::is_transitive($g); 1826} 1827 1828### 1829# Weighted vertices. 1830# 1831 1832my $defattr = 'weight'; 1833 1834sub _defattr { 1835 return $defattr; 1836} 1837 1838sub add_weighted_vertex { 1839 my $g = shift; 1840 $g->expect_non_multivertexed; 1841 my $w = pop; 1842 $g->add_vertex(@_); 1843 $g->set_vertex_attribute(@_, $defattr, $w); 1844} 1845 1846sub add_weighted_vertices { 1847 my $g = shift; 1848 $g->expect_non_multivertexed; 1849 while (@_) { 1850 my ($v, $w) = splice @_, 0, 2; 1851 $g->add_vertex($v); 1852 $g->set_vertex_attribute($v, $defattr, $w); 1853 } 1854} 1855 1856sub get_vertex_weight { 1857 my $g = shift; 1858 $g->expect_non_multivertexed; 1859 $g->get_vertex_attribute(@_, $defattr); 1860} 1861 1862sub has_vertex_weight { 1863 my $g = shift; 1864 $g->expect_non_multivertexed; 1865 $g->has_vertex_attribute(@_, $defattr); 1866} 1867 1868sub set_vertex_weight { 1869 my $g = shift; 1870 $g->expect_non_multivertexed; 1871 my $w = pop; 1872 $g->set_vertex_attribute(@_, $defattr, $w); 1873} 1874 1875sub delete_vertex_weight { 1876 my $g = shift; 1877 $g->expect_non_multivertexed; 1878 $g->delete_vertex_attribute(@_, $defattr); 1879} 1880 1881sub add_weighted_vertex_by_id { 1882 my $g = shift; 1883 $g->expect_multivertexed; 1884 my $w = pop; 1885 $g->add_vertex_by_id(@_); 1886 $g->set_vertex_attribute_by_id(@_, $defattr, $w); 1887} 1888 1889sub add_weighted_vertices_by_id { 1890 my $g = shift; 1891 $g->expect_multivertexed; 1892 my $id = pop; 1893 while (@_) { 1894 my ($v, $w) = splice @_, 0, 2; 1895 $g->add_vertex_by_id($v, $id); 1896 $g->set_vertex_attribute_by_id($v, $id, $defattr, $w); 1897 } 1898} 1899 1900sub get_vertex_weight_by_id { 1901 my $g = shift; 1902 $g->expect_multivertexed; 1903 $g->get_vertex_attribute_by_id(@_, $defattr); 1904} 1905 1906sub has_vertex_weight_by_id { 1907 my $g = shift; 1908 $g->expect_multivertexed; 1909 $g->has_vertex_attribute_by_id(@_, $defattr); 1910} 1911 1912sub set_vertex_weight_by_id { 1913 my $g = shift; 1914 $g->expect_multivertexed; 1915 my $w = pop; 1916 $g->set_vertex_attribute_by_id(@_, $defattr, $w); 1917} 1918 1919sub delete_vertex_weight_by_id { 1920 my $g = shift; 1921 $g->expect_multivertexed; 1922 $g->delete_vertex_attribute_by_id(@_, $defattr); 1923} 1924 1925### 1926# Weighted edges. 1927# 1928 1929sub add_weighted_edge { 1930 my $g = shift; 1931 $g->expect_non_multiedged; 1932 if ($g->is_compat02) { 1933 my $w = splice @_, 1, 1; 1934 $g->add_edge(@_); 1935 $g->set_edge_attribute(@_, $defattr, $w); 1936 } else { 1937 my $w = pop; 1938 $g->add_edge(@_); 1939 $g->set_edge_attribute(@_, $defattr, $w); 1940 } 1941} 1942 1943sub add_weighted_edges { 1944 my $g = shift; 1945 $g->expect_non_multiedged; 1946 if ($g->is_compat02) { 1947 while (@_) { 1948 my ($u, $w, $v) = splice @_, 0, 3; 1949 $g->add_edge($u, $v); 1950 $g->set_edge_attribute($u, $v, $defattr, $w); 1951 } 1952 } else { 1953 while (@_) { 1954 my ($u, $v, $w) = splice @_, 0, 3; 1955 $g->add_edge($u, $v); 1956 $g->set_edge_attribute($u, $v, $defattr, $w); 1957 } 1958 } 1959} 1960 1961sub add_weighted_edges_by_id { 1962 my $g = shift; 1963 $g->expect_multiedged; 1964 my $id = pop; 1965 while (@_) { 1966 my ($u, $v, $w) = splice @_, 0, 3; 1967 $g->add_edge_by_id($u, $v, $id); 1968 $g->set_edge_attribute_by_id($u, $v, $id, $defattr, $w); 1969 } 1970} 1971 1972sub add_weighted_path { 1973 my $g = shift; 1974 $g->expect_non_multiedged; 1975 my $u = shift; 1976 while (@_) { 1977 my ($w, $v) = splice @_, 0, 2; 1978 $g->add_edge($u, $v); 1979 $g->set_edge_attribute($u, $v, $defattr, $w); 1980 $u = $v; 1981 } 1982} 1983 1984sub get_edge_weight { 1985 my $g = shift; 1986 $g->expect_non_multiedged; 1987 $g->get_edge_attribute(@_, $defattr); 1988} 1989 1990sub has_edge_weight { 1991 my $g = shift; 1992 $g->expect_non_multiedged; 1993 $g->has_edge_attribute(@_, $defattr); 1994} 1995 1996sub set_edge_weight { 1997 my $g = shift; 1998 $g->expect_non_multiedged; 1999 my $w = pop; 2000 $g->set_edge_attribute(@_, $defattr, $w); 2001} 2002 2003sub delete_edge_weight { 2004 my $g = shift; 2005 $g->expect_non_multiedged; 2006 $g->delete_edge_attribute(@_, $defattr); 2007} 2008 2009sub add_weighted_edge_by_id { 2010 my $g = shift; 2011 $g->expect_multiedged; 2012 if ($g->is_compat02) { 2013 my $w = splice @_, 1, 1; 2014 $g->add_edge_by_id(@_); 2015 $g->set_edge_attribute_by_id(@_, $defattr, $w); 2016 } else { 2017 my $w = pop; 2018 $g->add_edge_by_id(@_); 2019 $g->set_edge_attribute_by_id(@_, $defattr, $w); 2020 } 2021} 2022 2023sub add_weighted_path_by_id { 2024 my $g = shift; 2025 $g->expect_multiedged; 2026 my $id = pop; 2027 my $u = shift; 2028 while (@_) { 2029 my ($w, $v) = splice @_, 0, 2; 2030 $g->add_edge_by_id($u, $v, $id); 2031 $g->set_edge_attribute_by_id($u, $v, $id, $defattr, $w); 2032 $u = $v; 2033 } 2034} 2035 2036sub get_edge_weight_by_id { 2037 my $g = shift; 2038 $g->expect_multiedged; 2039 $g->get_edge_attribute_by_id(@_, $defattr); 2040} 2041 2042sub has_edge_weight_by_id { 2043 my $g = shift; 2044 $g->expect_multiedged; 2045 $g->has_edge_attribute_by_id(@_, $defattr); 2046} 2047 2048sub set_edge_weight_by_id { 2049 my $g = shift; 2050 $g->expect_multiedged; 2051 my $w = pop; 2052 $g->set_edge_attribute_by_id(@_, $defattr, $w); 2053} 2054 2055sub delete_edge_weight_by_id { 2056 my $g = shift; 2057 $g->expect_multiedged; 2058 $g->delete_edge_attribute_by_id(@_, $defattr); 2059} 2060 2061### 2062# Error helpers. 2063# 2064 2065my %expected; 2066@expected{qw(directed undirected acyclic)} = qw(undirected directed cyclic); 2067 2068sub _expected { 2069 my $exp = shift; 2070 my $got = @_ ? shift : $expected{$exp}; 2071 $got = defined $got ? ", got $got" : ""; 2072 if (my @caller2 = caller(2)) { 2073 die "$caller2[3]: expected $exp graph$got, at $caller2[1] line $caller2[2].\n"; 2074 } else { 2075 my @caller1 = caller(1); 2076 die "$caller1[3]: expected $exp graph$got, at $caller1[1] line $caller1[2].\n"; 2077 } 2078} 2079 2080sub expect_undirected { 2081 my $g = shift; 2082 _expected('undirected') unless $g->is_undirected; 2083} 2084 2085sub expect_directed { 2086 my $g = shift; 2087 _expected('directed') unless $g->is_directed; 2088} 2089 2090sub expect_acyclic { 2091 my $g = shift; 2092 _expected('acyclic') unless $g->is_acyclic; 2093} 2094 2095sub expect_dag { 2096 my $g = shift; 2097 my @got; 2098 push @got, 'undirected' unless $g->is_directed; 2099 push @got, 'cyclic' unless $g->is_acyclic; 2100 _expected('directed acyclic', "@got") if @got; 2101} 2102 2103sub expect_multivertexed { 2104 my $g = shift; 2105 _expected('multivertexed') unless $g->is_multivertexed; 2106} 2107 2108sub expect_non_multivertexed { 2109 my $g = shift; 2110 _expected('non-multivertexed') if $g->is_multivertexed; 2111} 2112 2113sub expect_non_multiedged { 2114 my $g = shift; 2115 _expected('non-multiedged') if $g->is_multiedged; 2116} 2117 2118sub expect_multiedged { 2119 my $g = shift; 2120 _expected('multiedged') unless $g->is_multiedged; 2121} 2122 2123sub expect_non_unionfind { 2124 my $g = shift; 2125 _expected('non-unionfind') if $g->has_union_find; 2126} 2127 2128sub _get_options { 2129 my @caller = caller(1); 2130 unless (@_ == 1 && ref $_[0] eq 'ARRAY') { 2131 die "$caller[3]: internal error: should be called with only one array ref argument, at $caller[1] line $caller[2].\n"; 2132 } 2133 my @opt = @{ $_[0] }; 2134 unless (@opt % 2 == 0) { 2135 die "$caller[3]: expected an options hash, got a non-even number of arguments, at $caller[1] line $caller[2].\n"; 2136 } 2137 return @opt; 2138} 2139 2140### 2141# Random constructors and accessors. 2142# 2143 2144sub __fisher_yates_shuffle (@) { 2145 # From perlfaq4, but modified to be non-modifying. 2146 my @a = @_; 2147 my $i = @a; 2148 while ($i--) { 2149 my $j = int rand ($i+1); 2150 @a[$i,$j] = @a[$j,$i]; 2151 } 2152 return @a; 2153} 2154 2155BEGIN { 2156 sub _shuffle(@); 2157 # Workaround for the Perl bug [perl #32383] where -d:Dprof and 2158 # List::Util::shuffle do not like each other: if any debugging 2159 # (-d) flags are on, fall back to our own Fisher-Yates shuffle. 2160 # The bug was fixed by perl changes #26054 and #26062, which 2161 # went to Perl 5.9.3. If someone tests this with a pre-5.9.3 2162 # bleadperl that calls itself 5.9.3 but doesn't yet have the 2163 # patches, oh, well. 2164 *_shuffle = $^P && $] < 5.009003 ? 2165 \&__fisher_yates_shuffle : \&List::Util::shuffle; 2166} 2167 2168sub random_graph { 2169 my $class = (@_ % 2) == 0 ? 'Graph' : shift; 2170 my %opt = _get_options( \@_ ); 2171 my $random_edge; 2172 unless (exists $opt{vertices} && defined $opt{vertices}) { 2173 require Carp; 2174 Carp::croak("Graph::random_graph: argument 'vertices' missing or undef"); 2175 } 2176 if (exists $opt{random_seed}) { 2177 srand($opt{random_seed}); 2178 delete $opt{random_seed}; 2179 } 2180 if (exists $opt{random_edge}) { 2181 $random_edge = $opt{random_edge}; 2182 delete $opt{random_edge}; 2183 } 2184 my @V; 2185 if (my $ref = ref $opt{vertices}) { 2186 if ($ref eq 'ARRAY') { 2187 @V = @{ $opt{vertices} }; 2188 } else { 2189 Carp::croak("Graph::random_graph: argument 'vertices' illegal"); 2190 } 2191 } else { 2192 @V = 0..($opt{vertices} - 1); 2193 } 2194 delete $opt{vertices}; 2195 my $V = @V; 2196 my $C = $V * ($V - 1) / 2; 2197 my $E; 2198 if (exists $opt{edges} && exists $opt{edges_fill}) { 2199 Carp::croak("Graph::random_graph: both arguments 'edges' and 'edges_fill' specified"); 2200 } 2201 $E = exists $opt{edges_fill} ? $opt{edges_fill} * $C : $opt{edges}; 2202 delete $opt{edges}; 2203 delete $opt{edges_fill}; 2204 my $g = $class->new(%opt); 2205 $g->add_vertices(@V); 2206 return $g if $V < 2; 2207 $C *= 2 if $g->directed; 2208 $E = $C / 2 unless defined $E; 2209 $E = int($E + 0.5); 2210 my $p = $E / $C; 2211 $random_edge = sub { $p } unless defined $random_edge; 2212 # print "V = $V, E = $E, C = $C, p = $p\n"; 2213 if ($p > 1.0 && !($g->countedged || $g->multiedged)) { 2214 require Carp; 2215 Carp::croak("Graph::random_graph: needs to be countedged or multiedged ($E > $C)"); 2216 } 2217 my @V1 = @V; 2218 my @V2 = @V; 2219 # Shuffle the vertex lists so that the pairs at 2220 # the beginning of the lists are not more likely. 2221 @V1 = _shuffle @V1; 2222 @V2 = _shuffle @V2; 2223 LOOP: 2224 while ($E) { 2225 for my $v1 (@V1) { 2226 for my $v2 (@V2) { 2227 next if $v1 eq $v2; # TODO: allow self-loops? 2228 my $q = $random_edge->($g, $v1, $v2, $p); 2229 if ($q && ($q == 1 || rand() <= $q) && 2230 !$g->has_edge($v1, $v2)) { 2231 $g->add_edge($v1, $v2); 2232 $E--; 2233 last LOOP unless $E; 2234 } 2235 } 2236 } 2237 } 2238 return $g; 2239} 2240 2241sub random_vertex { 2242 my $g = shift; 2243 my @V = $g->vertices05; 2244 @V[rand @V]; 2245} 2246 2247sub random_edge { 2248 my $g = shift; 2249 my @E = $g->edges05; 2250 @E[rand @E]; 2251} 2252 2253sub random_successor { 2254 my ($g, $v) = @_; 2255 my @S = $g->successors($v); 2256 @S[rand @S]; 2257} 2258 2259sub random_predecessor { 2260 my ($g, $v) = @_; 2261 my @P = $g->predecessors($v); 2262 @P[rand @P]; 2263} 2264 2265### 2266# Algorithms. 2267# 2268 2269my $MST_comparator = sub { ($_[0] || 0) <=> ($_[1] || 0) }; 2270 2271sub _MST_attr { 2272 my $attr = shift; 2273 my $attribute = 2274 exists $attr->{attribute} ? 2275 $attr->{attribute} : $defattr; 2276 my $comparator = 2277 exists $attr->{comparator} ? 2278 $attr->{comparator} : $MST_comparator; 2279 return ($attribute, $comparator); 2280} 2281 2282sub _MST_edges { 2283 my ($g, $attr) = @_; 2284 my ($attribute, $comparator) = _MST_attr($attr); 2285 map { $_->[1] } 2286 sort { $comparator->($a->[0], $b->[0], $a->[1], $b->[1]) } 2287 map { [ $g->get_edge_attribute(@$_, $attribute), $_ ] } 2288 $g->edges05; 2289} 2290 2291sub MST_Kruskal { 2292 my ($g, %attr) = @_; 2293 2294 $g->expect_undirected; 2295 2296 my $MST = Graph::Undirected->new; 2297 2298 my $UF = Graph::UnionFind->new; 2299 for my $v ($g->vertices05) { $UF->add($v) } 2300 2301 for my $e ($g->_MST_edges(\%attr)) { 2302 my ($u, $v) = @$e; # TODO: hyperedges 2303 my $t0 = $UF->find( $u ); 2304 my $t1 = $UF->find( $v ); 2305 unless ($t0 eq $t1) { 2306 $UF->union($u, $v); 2307 $MST->add_edge($u, $v); 2308 } 2309 } 2310 2311 return $MST; 2312} 2313 2314sub _MST_add { 2315 my ($g, $h, $HF, $r, $attr, $unseen) = @_; 2316 for my $s ( grep { exists $unseen->{ $_ } } $g->successors( $r ) ) { 2317 $HF->add( Graph::MSTHeapElem->new( $r, $s, $g->get_edge_attribute( $r, $s, $attr ) ) ); 2318 } 2319} 2320 2321sub _next_alphabetic { shift; (sort keys %{ $_[0] })[0] } 2322sub _next_numeric { shift; (sort { $a <=> $b } keys %{ $_[0] })[0] } 2323sub _next_random { shift; (values %{ $_[0] })[ rand keys %{ $_[0] } ] } 2324 2325sub _root_opt { 2326 my $g = shift; 2327 my %opt = @_ == 1 ? ( first_root => $_[0] ) : _get_options( \@_ ); 2328 my %unseen; 2329 my @unseen = $g->vertices05; 2330 @unseen{ @unseen } = @unseen; 2331 @unseen = _shuffle @unseen; 2332 my $r; 2333 if (exists $opt{ start }) { 2334 $opt{ first_root } = $opt{ start }; 2335 $opt{ next_root } = undef; 2336 } 2337 if (exists $opt{ get_next_root }) { 2338 $opt{ next_root } = $opt{ get_next_root }; # Graph 0.201 compat. 2339 } 2340 if (exists $opt{ first_root }) { 2341 if (ref $opt{ first_root } eq 'CODE') { 2342 $r = $opt{ first_root }->( $g, \%unseen ); 2343 } else { 2344 $r = $opt{ first_root }; 2345 } 2346 } else { 2347 $r = shift @unseen; 2348 } 2349 my $next = 2350 exists $opt{ next_root } ? 2351 $opt{ next_root } : 2352 $opt{ next_alphabetic } ? 2353 \&_next_alphabetic : 2354 $opt{ next_numeric } ? \&_next_numeric : 2355 \&_next_random; 2356 my $code = ref $next eq 'CODE'; 2357 my $attr = exists $opt{ attribute } ? $opt{ attribute } : $defattr; 2358 return ( \%opt, \%unseen, \@unseen, $r, $next, $code, $attr ); 2359} 2360 2361sub _heap_walk { 2362 my ($g, $h, $add, $etc) = splice @_, 0, 4; # Leave %opt in @_. 2363 2364 my ($opt, $unseenh, $unseena, $r, $next, $code, $attr) = $g->_root_opt(@_); 2365 my $HF = Heap071::Fibonacci->new; 2366 2367 while (defined $r) { 2368 # print "r = $r\n"; 2369 $add->($g, $h, $HF, $r, $attr, $unseenh, $etc); 2370 delete $unseenh->{ $r }; 2371 while (defined $HF->top) { 2372 my $t = $HF->extract_top; 2373 # use Data::Dumper; print "t = ", Dumper($t); 2374 if (defined $t) { 2375 my ($u, $v, $w) = $t->val; 2376 # print "extracted top: $u $v $w\n"; 2377 if (exists $unseenh->{ $v }) { 2378 $h->set_edge_attribute($u, $v, $attr, $w); 2379 delete $unseenh->{ $v }; 2380 $add->($g, $h, $HF, $v, $attr, $unseenh, $etc); 2381 } 2382 } 2383 } 2384 return $h unless defined $next; 2385 $r = $code ? $next->( $g, $unseenh ) : shift @$unseena; 2386 } 2387 2388 return $h; 2389} 2390 2391sub MST_Prim { 2392 my $g = shift; 2393 $g->expect_undirected; 2394 $g->_heap_walk(Graph::Undirected->new(), \&_MST_add, undef, @_); 2395} 2396 2397*MST_Dijkstra = \&MST_Prim; 2398 2399*minimum_spanning_tree = \&MST_Prim; 2400 2401### 2402# Cycle detection. 2403# 2404 2405*is_cyclic = \&has_a_cycle; 2406 2407sub is_acyclic { 2408 my $g = shift; 2409 return !$g->is_cyclic; 2410} 2411 2412sub is_dag { 2413 my $g = shift; 2414 return $g->is_directed && $g->is_acyclic ? 1 : 0; 2415} 2416 2417*is_directed_acyclic_graph = \&is_dag; 2418 2419### 2420# Backward compat. 2421# 2422 2423sub average_degree { 2424 my $g = shift; 2425 my $V = $g->vertices05; 2426 2427 return $V ? $g->degree / $V : 0; 2428} 2429 2430sub density_limits { 2431 my $g = shift; 2432 2433 my $V = $g->vertices05; 2434 my $M = $V * ($V - 1); 2435 2436 $M /= 2 if $g->is_undirected; 2437 2438 return ( 0.25 * $M, 0.75 * $M, $M ); 2439} 2440 2441sub density { 2442 my $g = shift; 2443 my ($sparse, $dense, $complete) = $g->density_limits; 2444 2445 return $complete ? $g->edges / $complete : 0; 2446} 2447 2448### 2449# Attribute backward compat 2450# 2451 2452sub _attr02_012 { 2453 my ($g, $op, $ga, $va, $ea) = splice @_, 0, 5; 2454 if ($g->is_compat02) { 2455 if (@_ == 0) { return $ga->( $g ) } 2456 elsif (@_ == 1) { return $va->( $g, @_ ) } 2457 elsif (@_ == 2) { return $ea->( $g, @_ ) } 2458 else { 2459 die sprintf "$op: wrong number of arguments (%d)", scalar @_; 2460 } 2461 } else { 2462 die "$op: not a compat02 graph" 2463 } 2464} 2465 2466sub _attr02_123 { 2467 my ($g, $op, $ga, $va, $ea) = splice @_, 0, 5; 2468 if ($g->is_compat02) { 2469 if (@_ == 1) { return $ga->( $g, @_ ) } 2470 elsif (@_ == 2) { return $va->( $g, @_[1, 0] ) } 2471 elsif (@_ == 3) { return $ea->( $g, @_[1, 2, 0] ) } 2472 else { 2473 die sprintf "$op: wrong number of arguments (%d)", scalar @_; 2474 } 2475 } else { 2476 die "$op: not a compat02 graph" 2477 } 2478} 2479 2480sub _attr02_234 { 2481 my ($g, $op, $ga, $va, $ea) = splice @_, 0, 5; 2482 if ($g->is_compat02) { 2483 if (@_ == 2) { return $ga->( $g, @_ ) } 2484 elsif (@_ == 3) { return $va->( $g, @_[1, 0, 2] ) } 2485 elsif (@_ == 4) { return $ea->( $g, @_[1, 2, 0, 3] ) } 2486 else { 2487 die sprintf "$op: wrong number of arguments (%d)", scalar @_; 2488 } 2489 } else { 2490 die "$op: not a compat02 graph"; 2491 } 2492} 2493 2494sub set_attribute { 2495 my $g = shift; 2496 $g->_attr02_234('set_attribute', 2497 \&Graph::set_graph_attribute, 2498 \&Graph::set_vertex_attribute, 2499 \&Graph::set_edge_attribute, 2500 @_); 2501 2502} 2503 2504sub set_attributes { 2505 my $g = shift; 2506 my $a = pop; 2507 $g->_attr02_123('set_attributes', 2508 \&Graph::set_graph_attributes, 2509 \&Graph::set_vertex_attributes, 2510 \&Graph::set_edge_attributes, 2511 $a, @_); 2512 2513} 2514 2515sub get_attribute { 2516 my $g = shift; 2517 $g->_attr02_123('get_attribute', 2518 \&Graph::get_graph_attribute, 2519 \&Graph::get_vertex_attribute, 2520 \&Graph::get_edge_attribute, 2521 @_); 2522 2523} 2524 2525sub get_attributes { 2526 my $g = shift; 2527 $g->_attr02_012('get_attributes', 2528 \&Graph::get_graph_attributes, 2529 \&Graph::get_vertex_attributes, 2530 \&Graph::get_edge_attributes, 2531 @_); 2532 2533} 2534 2535sub has_attribute { 2536 my $g = shift; 2537 return 0 unless @_; 2538 $g->_attr02_123('has_attribute', 2539 \&Graph::has_graph_attribute, 2540 \&Graph::has_vertex_attribute, 2541 \&Graph::get_edge_attribute, 2542 @_); 2543 2544} 2545 2546sub has_attributes { 2547 my $g = shift; 2548 $g->_attr02_012('has_attributes', 2549 \&Graph::has_graph_attributes, 2550 \&Graph::has_vertex_attributes, 2551 \&Graph::has_edge_attributes, 2552 @_); 2553 2554} 2555 2556sub delete_attribute { 2557 my $g = shift; 2558 $g->_attr02_123('delete_attribute', 2559 \&Graph::delete_graph_attribute, 2560 \&Graph::delete_vertex_attribute, 2561 \&Graph::delete_edge_attribute, 2562 @_); 2563 2564} 2565 2566sub delete_attributes { 2567 my $g = shift; 2568 $g->_attr02_012('delete_attributes', 2569 \&Graph::delete_graph_attributes, 2570 \&Graph::delete_vertex_attributes, 2571 \&Graph::delete_edge_attributes, 2572 @_); 2573 2574} 2575 2576### 2577# Simple DFS uses. 2578# 2579 2580sub topological_sort { 2581 my $g = shift; 2582 my %opt = _get_options( \@_ ); 2583 my $eic = $opt{ empty_if_cyclic }; 2584 my $hac; 2585 if ($eic) { 2586 $hac = $g->has_a_cycle; 2587 } else { 2588 $g->expect_dag; 2589 } 2590 delete $opt{ empty_if_cyclic }; 2591 my $t = Graph::Traversal::DFS->new($g, %opt); 2592 my @s = $t->dfs; 2593 $hac ? () : reverse @s; 2594} 2595 2596*toposort = \&topological_sort; 2597 2598sub undirected_copy { 2599 my $g = shift; 2600 2601 $g->expect_directed; 2602 2603 my $c = Graph::Undirected->new; 2604 for my $v ($g->isolated_vertices) { # TODO: if iv ... 2605 $c->add_vertex($v); 2606 } 2607 for my $e ($g->edges05) { 2608 $c->add_edge(@$e); 2609 } 2610 return $c; 2611} 2612 2613*undirected_copy_graph = \&undirected_copy; 2614 2615sub directed_copy { 2616 my $g = shift; 2617 $g->expect_undirected; 2618 my $c = Graph::Directed->new; 2619 for my $v ($g->isolated_vertices) { # TODO: if iv ... 2620 $c->add_vertex($v); 2621 } 2622 for my $e ($g->edges05) { 2623 my @e = @$e; 2624 $c->add_edge(@e); 2625 $c->add_edge(reverse @e); 2626 } 2627 return $c; 2628} 2629 2630*directed_copy_graph = \&directed_copy; 2631 2632### 2633# Cache or not. 2634# 2635 2636my %_cache_type = 2637 ( 2638 'connectivity' => '_ccc', 2639 'strong_connectivity' => '_scc', 2640 'biconnectivity' => '_bcc', 2641 'SPT_Dijkstra' => '_spt_di', 2642 'SPT_Bellman_Ford' => '_spt_bf', 2643 ); 2644 2645sub _check_cache { 2646 my ($g, $type, $code) = splice @_, 0, 3; 2647 my $c = $_cache_type{$type}; 2648 if (defined $c) { 2649 my $a = $g->get_graph_attribute($c); 2650 unless (defined $a && $a->[ 0 ] == $g->[ _G ]) { 2651 $a->[ 0 ] = $g->[ _G ]; 2652 $a->[ 1 ] = $code->( $g, @_ ); 2653 $g->set_graph_attribute($c, $a); 2654 } 2655 return $a->[ 1 ]; 2656 } else { 2657 Carp::croak("Graph: unknown cache type '$type'"); 2658 } 2659} 2660 2661sub _clear_cache { 2662 my ($g, $type) = @_; 2663 my $c = $_cache_type{$type}; 2664 if (defined $c) { 2665 $g->delete_graph_attribute($c); 2666 } else { 2667 Carp::croak("Graph: unknown cache type '$type'"); 2668 } 2669} 2670 2671sub connectivity_clear_cache { 2672 my $g = shift; 2673 _clear_cache($g, 'connectivity'); 2674} 2675 2676sub strong_connectivity_clear_cache { 2677 my $g = shift; 2678 _clear_cache($g, 'strong_connectivity'); 2679} 2680 2681sub biconnectivity_clear_cache { 2682 my $g = shift; 2683 _clear_cache($g, 'biconnectivity'); 2684} 2685 2686sub SPT_Dijkstra_clear_cache { 2687 my $g = shift; 2688 _clear_cache($g, 'SPT_Dijkstra'); 2689 $g->delete_graph_attribute('SPT_Dijkstra_first_root'); 2690} 2691 2692sub SPT_Bellman_Ford_clear_cache { 2693 my $g = shift; 2694 _clear_cache($g, 'SPT_Bellman_Ford'); 2695} 2696 2697### 2698# Connected components. 2699# 2700 2701sub _connected_components_compute { 2702 my $g = shift; 2703 my %cce; 2704 my %cci; 2705 my $cc = 0; 2706 if ($g->has_union_find) { 2707 my $UF = $g->_get_union_find(); 2708 my $V = $g->[ _V ]; 2709 my %icce; # Isolated vertices. 2710 my %icci; 2711 my $icc = 0; 2712 for my $v ( $g->unique_vertices ) { 2713 $cc = $UF->find( $V->_get_path_id( $v ) ); 2714 if (defined $cc) { 2715 $cce{ $v } = $cc; 2716 push @{ $cci{ $cc } }, $v; 2717 } else { 2718 $icce{ $v } = $icc; 2719 push @{ $icci{ $icc } }, $v; 2720 $icc++; 2721 } 2722 } 2723 if ($icc) { 2724 @cce{ keys %icce } = values %icce; 2725 @cci{ keys %icci } = values %icci; 2726 } 2727 } else { 2728 my @u = $g->unique_vertices; 2729 my %r; @r{ @u } = @u; 2730 my $froot = sub { 2731 (each %r)[1]; 2732 }; 2733 my $nroot = sub { 2734 $cc++ if keys %r; 2735 (each %r)[1]; 2736 }; 2737 my $t = Graph::Traversal::DFS->new($g, 2738 first_root => $froot, 2739 next_root => $nroot, 2740 pre => sub { 2741 my ($v, $t) = @_; 2742 $cce{ $v } = $cc; 2743 push @{ $cci{ $cc } }, $v; 2744 delete $r{ $v }; 2745 }, 2746 @_); 2747 $t->dfs; 2748 } 2749 return [ \%cce, \%cci ]; 2750} 2751 2752sub _connected_components { 2753 my $g = shift; 2754 my $ccc = _check_cache($g, 'connectivity', 2755 \&_connected_components_compute, @_); 2756 return @{ $ccc }; 2757} 2758 2759sub connected_component_by_vertex { 2760 my ($g, $v) = @_; 2761 $g->expect_undirected; 2762 my ($CCE, $CCI) = $g->_connected_components(); 2763 return $CCE->{ $v }; 2764} 2765 2766sub connected_component_by_index { 2767 my ($g, $i) = @_; 2768 $g->expect_undirected; 2769 my ($CCE, $CCI) = $g->_connected_components(); 2770 return defined $CCI->{ $i } ? @{ $CCI->{ $i } } : ( ); 2771} 2772 2773sub connected_components { 2774 my $g = shift; 2775 $g->expect_undirected; 2776 my ($CCE, $CCI) = $g->_connected_components(); 2777 return values %{ $CCI }; 2778} 2779 2780sub same_connected_components { 2781 my $g = shift; 2782 $g->expect_undirected; 2783 if ($g->has_union_find) { 2784 my $UF = $g->_get_union_find(); 2785 my $V = $g->[ _V ]; 2786 my $u = shift; 2787 my $c = $UF->find( $V->_get_path_id ( $u ) ); 2788 my $d; 2789 for my $v ( @_) { 2790 return 0 2791 unless defined($d = $UF->find( $V->_get_path_id( $v ) )) && 2792 $d eq $c; 2793 } 2794 return 1; 2795 } else { 2796 my ($CCE, $CCI) = $g->_connected_components(); 2797 my $u = shift; 2798 my $c = $CCE->{ $u }; 2799 for my $v ( @_) { 2800 return 0 2801 unless defined $CCE->{ $v } && 2802 $CCE->{ $v } eq $c; 2803 } 2804 return 1; 2805 } 2806} 2807 2808my $super_component = sub { join("+", sort @_) }; 2809 2810sub connected_graph { 2811 my ($g, %opt) = @_; 2812 $g->expect_undirected; 2813 my $cg = Graph->new(undirected => 1); 2814 if ($g->has_union_find && $g->vertices == 1) { 2815 # TODO: super_component? 2816 $cg->add_vertices($g->vertices); 2817 } else { 2818 my $sc_cb = 2819 exists $opt{super_component} ? 2820 $opt{super_component} : $super_component; 2821 for my $cc ( $g->connected_components() ) { 2822 my $sc = $sc_cb->(@$cc); 2823 $cg->add_vertex($sc); 2824 $cg->set_vertex_attribute($sc, 'subvertices', [ @$cc ]); 2825 } 2826 } 2827 return $cg; 2828} 2829 2830sub is_connected { 2831 my $g = shift; 2832 $g->expect_undirected; 2833 my ($CCE, $CCI) = $g->_connected_components(); 2834 return keys %{ $CCI } == 1; 2835} 2836 2837sub is_weakly_connected { 2838 my $g = shift; 2839 $g->expect_directed; 2840 $g->undirected_copy->is_connected(@_); 2841} 2842 2843*weakly_connected = \&is_weakly_connected; 2844 2845sub weakly_connected_components { 2846 my $g = shift; 2847 $g->expect_directed; 2848 $g->undirected_copy->connected_components(@_); 2849} 2850 2851sub weakly_connected_component_by_vertex { 2852 my $g = shift; 2853 $g->expect_directed; 2854 $g->undirected_copy->connected_component_by_vertex(@_); 2855} 2856 2857sub weakly_connected_component_by_index { 2858 my $g = shift; 2859 $g->expect_directed; 2860 $g->undirected_copy->connected_component_by_index(@_); 2861} 2862 2863sub same_weakly_connected_components { 2864 my $g = shift; 2865 $g->expect_directed; 2866 $g->undirected_copy->same_connected_components(@_); 2867} 2868 2869sub weakly_connected_graph { 2870 my $g = shift; 2871 $g->expect_directed; 2872 $g->undirected_copy->connected_graph(@_); 2873} 2874 2875sub _strongly_connected_components_compute { 2876 my $g = shift; 2877 my $t = Graph::Traversal::DFS->new($g); 2878 my @d = reverse $t->dfs; 2879 my @c; 2880 my $h = $g->transpose_graph; 2881 my $u = 2882 Graph::Traversal::DFS->new($h, 2883 next_root => sub { 2884 my ($t, $u) = @_; 2885 my $root; 2886 while (defined($root = shift @d)) { 2887 last if exists $u->{ $root }; 2888 } 2889 if (defined $root) { 2890 push @c, []; 2891 return $root; 2892 } else { 2893 return; 2894 } 2895 }, 2896 pre => sub { 2897 my ($v, $t) = @_; 2898 push @{ $c[-1] }, $v; 2899 }, 2900 @_); 2901 $u->dfs; 2902 return \@c; 2903} 2904 2905sub _strongly_connected_components { 2906 my $g = shift; 2907 my $type = 'strong_connectivity'; 2908 my $scc = _check_cache($g, $type, 2909 \&_strongly_connected_components_compute, @_); 2910 return defined $scc ? @$scc : ( ); 2911} 2912 2913sub strongly_connected_components { 2914 my $g = shift; 2915 $g->expect_directed; 2916 $g->_strongly_connected_components(@_); 2917} 2918 2919sub strongly_connected_component_by_vertex { 2920 my $g = shift; 2921 my $v = shift; 2922 $g->expect_directed; 2923 my @scc = $g->_strongly_connected_components( next_alphabetic => 1, @_ ); 2924 for (my $i = 0; $i <= $#scc; $i++) { 2925 for (my $j = 0; $j <= $#{ $scc[$i] }; $j++) { 2926 return $i if $scc[$i]->[$j] eq $v; 2927 } 2928 } 2929 return; 2930} 2931 2932sub strongly_connected_component_by_index { 2933 my $g = shift; 2934 my $i = shift; 2935 $g->expect_directed; 2936 my $c = ( $g->_strongly_connected_components(@_) )[ $i ]; 2937 return defined $c ? @{ $c } : (); 2938} 2939 2940sub same_strongly_connected_components { 2941 my $g = shift; 2942 $g->expect_directed; 2943 my @scc = $g->_strongly_connected_components( next_alphabetic => 1, @_ ); 2944 my @i; 2945 while (@_) { 2946 my $v = shift; 2947 for (my $i = 0; $i <= $#scc; $i++) { 2948 for (my $j = 0; $j <= $#{ $scc[$i] }; $j++) { 2949 if ($scc[$i]->[$j] eq $v) { 2950 push @i, $i; 2951 return 0 if @i > 1 && $i[-1] ne $i[0]; 2952 } 2953 } 2954 } 2955 } 2956 return 1; 2957} 2958 2959sub is_strongly_connected { 2960 my $g = shift; 2961 $g->expect_directed; 2962 my $t = Graph::Traversal::DFS->new($g); 2963 my @d = reverse $t->dfs; 2964 my @c; 2965 my $h = $g->transpose; 2966 my $u = 2967 Graph::Traversal::DFS->new($h, 2968 next_root => sub { 2969 my ($t, $u) = @_; 2970 my $root; 2971 while (defined($root = shift @d)) { 2972 last if exists $u->{ $root }; 2973 } 2974 if (defined $root) { 2975 unless (@{ $t->{ roots } }) { 2976 push @c, []; 2977 return $root; 2978 } else { 2979 $t->terminate; 2980 return; 2981 } 2982 } else { 2983 return; 2984 } 2985 }, 2986 pre => sub { 2987 my ($v, $t) = @_; 2988 push @{ $c[-1] }, $v; 2989 }, 2990 @_); 2991 $u->dfs; 2992 return @{ $u->{ roots } } == 1 && keys %{ $u->{ unseen } } == 0; 2993} 2994 2995*strongly_connected = \&is_strongly_connected; 2996 2997sub strongly_connected_graph { 2998 my $g = shift; 2999 my %attr = @_; 3000 3001 $g->expect_directed; 3002 3003 my $t = Graph::Traversal::DFS->new($g); 3004 my @d = reverse $t->dfs; 3005 my @c; 3006 my $h = $g->transpose; 3007 my $u = 3008 Graph::Traversal::DFS->new($h, 3009 next_root => sub { 3010 my ($t, $u) = @_; 3011 my $root; 3012 while (defined($root = shift @d)) { 3013 last if exists $u->{ $root }; 3014 } 3015 if (defined $root) { 3016 push @c, []; 3017 return $root; 3018 } else { 3019 return; 3020 } 3021 }, 3022 pre => sub { 3023 my ($v, $t) = @_; 3024 push @{ $c[-1] }, $v; 3025 } 3026 ); 3027 3028 $u->dfs; 3029 3030 my $sc_cb; 3031 my $hv_cb; 3032 3033 _opt_get(\%attr, super_component => \$sc_cb); 3034 _opt_get(\%attr, hypervertex => \$hv_cb); 3035 _opt_unknown(\%attr); 3036 3037 if (defined $hv_cb && !defined $sc_cb) { 3038 $sc_cb = sub { $hv_cb->( [ @_ ] ) }; 3039 } 3040 unless (defined $sc_cb) { 3041 $sc_cb = $super_component; 3042 } 3043 3044 my $s = Graph->new; 3045 3046 my %c; 3047 my @s; 3048 for (my $i = 0; $i < @c; $i++) { 3049 my $c = $c[$i]; 3050 $s->add_vertex( $s[$i] = $sc_cb->(@$c) ); 3051 $s->set_vertex_attribute($s[$i], 'subvertices', [ @$c ]); 3052 for my $v (@$c) { 3053 $c{$v} = $i; 3054 } 3055 } 3056 3057 my $n = @c; 3058 for my $v ($g->vertices) { 3059 unless (exists $c{$v}) { 3060 $c{$v} = $n; 3061 $s[$n] = $v; 3062 $n++; 3063 } 3064 } 3065 3066 for my $e ($g->edges05) { 3067 my ($u, $v) = @$e; # @TODO: hyperedges 3068 unless ($c{$u} == $c{$v}) { 3069 my ($p, $q) = ( $s[ $c{ $u } ], $s[ $c{ $v } ] ); 3070 $s->add_edge($p, $q) unless $s->has_edge($p, $q); 3071 } 3072 } 3073 3074 if (my @i = $g->isolated_vertices) { 3075 $s->add_vertices(map { $s[ $c{ $_ } ] } @i); 3076 } 3077 3078 return $s; 3079} 3080 3081### 3082# Biconnectivity. 3083# 3084 3085sub _make_bcc { 3086 my ($S, $v, $c) = @_; 3087 my %b; 3088 while (@$S) { 3089 my $t = pop @$S; 3090 $b{ $t } = $t; 3091 last if $t eq $v; 3092 } 3093 return [ values %b, $c ]; 3094} 3095 3096sub _biconnectivity_compute { 3097 my $g = shift; 3098 my ($opt, $unseenh, $unseena, $r, $next, $code, $attr) = 3099 $g->_root_opt(@_); 3100 return () unless defined $r; 3101 my %P; 3102 my %I; 3103 for my $v ($g->vertices) { 3104 $I{ $v } = 0; 3105 } 3106 $I{ $r } = 1; 3107 my %U; 3108 my %S; # Self-loops. 3109 for my $e ($g->edges) { 3110 my ($u, $v) = @$e; 3111 $U{ $u }{ $v } = 0; 3112 $U{ $v }{ $u } = 0; 3113 $S{ $u } = 1 if $u eq $v; 3114 } 3115 my $i = 1; 3116 my $v = $r; 3117 my %AP; 3118 my %L = ( $r => 1 ); 3119 my @S = ( $r ); 3120 my %A; 3121 my @V = $g->vertices; 3122 3123 # print "V : @V\n"; 3124 # print "r : $r\n"; 3125 3126 my %T; @T{ @V } = @V; 3127 3128 for my $w (@V) { 3129 my @s = $g->successors( $w ); 3130 if (@s) { 3131 @s = grep { $_ eq $w ? ( delete $T{ $w }, 0 ) : 1 } @s; 3132 @{ $A{ $w } }{ @s } = @s; 3133 } elsif ($g->predecessors( $w ) == 0) { 3134 delete $T{ $w }; 3135 if ($w eq $r) { 3136 delete $I { $r }; 3137 $r = $v = each %T; 3138 if (defined $r) { 3139 %L = ( $r => 1 ); 3140 @S = ( $r ); 3141 $I{ $r } = 1; 3142 # print "r : $r\n"; 3143 } 3144 } 3145 } 3146 } 3147 3148 # use Data::Dumper; 3149 # print "T : ", Dumper(\%T); 3150 # print "A : ", Dumper(\%A); 3151 3152 my %V2BC; 3153 my @BR; 3154 my @BC; 3155 3156 my @C; 3157 my $Avok; 3158 3159 while (keys %T) { 3160 # print "T = ", Dumper(\%T); 3161 do { 3162 my $w; 3163 do { 3164 my @w = _shuffle values %{ $A{ $v } }; 3165 # print "w = @w\n"; 3166 $w = first { !$U{ $v }{ $_ } } @w; 3167 if (defined $w) { 3168 # print "w = $w\n"; 3169 $U{ $v }{ $w }++; 3170 $U{ $w }{ $v }++; 3171 if ($I{ $w } == 0) { 3172 $P{ $w } = $v; 3173 $i++; 3174 $I{ $w } = $i; 3175 $L{ $w } = $i; 3176 push @S, $w; 3177 $v = $w; 3178 } else { 3179 $L{ $v } = $I{ $w } if $I{ $w } < $L{ $v }; 3180 } 3181 } 3182 } while (defined $w); 3183 # print "U = ", Dumper(\%U); 3184 # print "P = ", Dumper(\%P); 3185 # print "L = ", Dumper(\%L); 3186 if (!defined $P{ $v }) { 3187 # Do nothing. 3188 } elsif ($P{ $v } ne $r) { 3189 if ($L{ $v } < $I{ $P{ $v } }) { 3190 $L{ $P{ $v } } = $L{ $v } if $L{ $v } < $L{ $P{ $v } }; 3191 } else { 3192 $AP{ $P{ $v } } = $P{ $v }; 3193 push @C, _make_bcc(\@S, $v, $P{ $v } ); 3194 } 3195 } else { 3196 my $e; 3197 for my $w (_shuffle keys %{ $A{ $r } }) { 3198 # print "w = $w\n"; 3199 unless ($U{ $r }{ $w }) { 3200 $e = $r; 3201 # print "e = $e\n"; 3202 last; 3203 } 3204 } 3205 $AP{ $e } = $e if defined $e; 3206 push @C, _make_bcc(\@S, $v, $r); 3207 } 3208 # print "AP = ", Dumper(\%AP); 3209 # print "C = ", Dumper(\@C); 3210 # print "L = ", Dumper(\%L); 3211 $v = defined $P{ $v } ? $P{ $v } : $r; 3212 # print "v = $v\n"; 3213 $Avok = 0; 3214 if (defined $v) { 3215 if (keys %{ $A{ $v } }) { 3216 if (!exists $P{ $v }) { 3217 for my $w (keys %{ $A{ $v } }) { 3218 $Avok++ if $U{ $v }{ $w }; 3219 } 3220 # print "Avok/1 = $Avok\n"; 3221 $Avok = 0 unless $Avok == keys %{ $A{ $v } }; 3222 # print "Avok/2 = $Avok\n"; 3223 } 3224 } else { 3225 $Avok = 1; 3226 # print "Avok/3 = $Avok\n"; 3227 } 3228 } 3229 } until ($Avok); 3230 3231 last if @C == 0 && !exists $S{$v}; 3232 3233 for (my $i = 0; $i < @C; $i++) { 3234 for my $v (@{ $C[ $i ]}) { 3235 $V2BC{ $v }{ $i }++; 3236 delete $T{ $v }; 3237 } 3238 } 3239 3240 for (my $i = 0; $i < @C; $i++) { 3241 if (@{ $C[ $i ] } == 2) { 3242 push @BR, $C[ $i ]; 3243 } else { 3244 push @BC, $C[ $i ]; 3245 } 3246 } 3247 3248 if (keys %T) { 3249 $r = $v = each %T; 3250 } 3251 } 3252 3253 return [ [values %AP], \@BC, \@BR, \%V2BC ]; 3254} 3255 3256sub biconnectivity { 3257 my $g = shift; 3258 $g->expect_undirected; 3259 my $bcc = _check_cache($g, 'biconnectivity', 3260 \&_biconnectivity_compute, @_); 3261 return defined $bcc ? @$bcc : ( ); 3262} 3263 3264sub is_biconnected { 3265 my $g = shift; 3266 my ($ap, $bc) = ($g->biconnectivity(@_))[0, 1]; 3267 return defined $ap ? @$ap == 0 && $g->vertices >= 3 : undef; 3268} 3269 3270sub is_edge_connected { 3271 my $g = shift; 3272 my ($br) = ($g->biconnectivity(@_))[2]; 3273 return defined $br ? @$br == 0 && $g->edges : undef; 3274} 3275 3276sub is_edge_separable { 3277 my $g = shift; 3278 my $c = $g->is_edge_connected; 3279 defined $c ? !$c && $g->edges : undef; 3280} 3281 3282sub articulation_points { 3283 my $g = shift; 3284 my ($ap) = ($g->biconnectivity(@_))[0]; 3285 return defined $ap ? @$ap : (); 3286} 3287 3288*cut_vertices = \&articulation_points; 3289 3290sub biconnected_components { 3291 my $g = shift; 3292 my ($bc) = ($g->biconnectivity(@_))[1]; 3293 return defined $bc ? @$bc : (); 3294} 3295 3296sub biconnected_component_by_index { 3297 my $g = shift; 3298 my $i = shift; 3299 my ($bc) = ($g->biconnectivity(@_))[1]; 3300 return defined $bc ? $bc->[ $i ] : undef; 3301} 3302 3303sub biconnected_component_by_vertex { 3304 my $g = shift; 3305 my $v = shift; 3306 my ($v2bc) = ($g->biconnectivity(@_))[3]; 3307 return defined $v2bc->{ $v } ? keys %{ $v2bc->{ $v } } : (); 3308} 3309 3310sub same_biconnected_components { 3311 my $g = shift; 3312 my $u = shift; 3313 my @u = $g->biconnected_component_by_vertex($u, @_); 3314 return 0 unless @u; 3315 my %ubc; @ubc{ @u } = (); 3316 while (@_) { 3317 my $v = shift; 3318 my @v = $g->biconnected_component_by_vertex($v); 3319 if (@v) { 3320 my %vbc; @vbc{ @v } = (); 3321 my $vi; 3322 for my $ui (keys %ubc) { 3323 if (exists $vbc{ $ui }) { 3324 $vi = $ui; 3325 last; 3326 } 3327 } 3328 return 0 unless defined $vi; 3329 } 3330 } 3331 return 1; 3332} 3333 3334sub biconnected_graph { 3335 my ($g, %opt) = @_; 3336 my ($bc, $v2bc) = ($g->biconnectivity, %opt)[1, 3]; 3337 my $bcg = Graph::Undirected->new; 3338 my $sc_cb = 3339 exists $opt{super_component} ? 3340 $opt{super_component} : $super_component; 3341 for my $c (@$bc) { 3342 $bcg->add_vertex(my $s = $sc_cb->(@$c)); 3343 $bcg->set_vertex_attribute($s, 'subvertices', [ @$c ]); 3344 } 3345 my %k; 3346 for my $i (0..$#$bc) { 3347 my @u = @{ $bc->[ $i ] }; 3348 my %i; @i{ @u } = (); 3349 for my $j (0..$#$bc) { 3350 if ($i > $j) { 3351 my @v = @{ $bc->[ $j ] }; 3352 my %j; @j{ @v } = (); 3353 for my $u (@u) { 3354 if (exists $j{ $u }) { 3355 unless ($k{ $i }{ $j }++) { 3356 $bcg->add_edge($sc_cb->(@{$bc->[$i]}), 3357 $sc_cb->(@{$bc->[$j]})); 3358 } 3359 last; 3360 } 3361 } 3362 } 3363 } 3364 } 3365 return $bcg; 3366} 3367 3368sub bridges { 3369 my $g = shift; 3370 my ($br) = ($g->biconnectivity(@_))[2]; 3371 return defined $br ? @$br : (); 3372} 3373 3374### 3375# SPT. 3376# 3377 3378sub _SPT_add { 3379 my ($g, $h, $HF, $r, $attr, $unseen, $etc) = @_; 3380 my $etc_r = $etc->{ $r } || 0; 3381 for my $s ( grep { exists $unseen->{ $_ } } $g->successors( $r ) ) { 3382 my $t = $g->get_edge_attribute( $r, $s, $attr ); 3383 $t = 1 unless defined $t; 3384 if ($t < 0) { 3385 require Carp; 3386 Carp::croak("Graph::SPT_Dijkstra: edge $r-$s is negative ($t)"); 3387 } 3388 if (!defined($etc->{ $s }) || ($etc_r + $t) < $etc->{ $s }) { 3389 my $etc_s = $etc->{ $s } || 0; 3390 $etc->{ $s } = $etc_r + $t; 3391 # print "$r - $s : setting $s to $etc->{ $s } ($etc_r, $etc_s)\n"; 3392 $h->set_vertex_attribute( $s, $attr, $etc->{ $s }); 3393 $h->set_vertex_attribute( $s, 'p', $r ); 3394 $HF->add( Graph::SPTHeapElem->new($r, $s, $etc->{ $s }) ); 3395 } 3396 } 3397} 3398 3399sub _SPT_Dijkstra_compute { 3400} 3401 3402sub SPT_Dijkstra { 3403 my $g = shift; 3404 my %opt = @_ == 1 ? (first_root => $_[0]) : @_; 3405 my $first_root = $opt{ first_root }; 3406 unless (defined $first_root) { 3407 $opt{ first_root } = $first_root = $g->random_vertex(); 3408 } 3409 my $spt_di = $g->get_graph_attribute('_spt_di'); 3410 unless (defined $spt_di && exists $spt_di->{ $first_root } && $spt_di->{ $first_root }->[ 0 ] == $g->[ _G ]) { 3411 my %etc; 3412 my $sptg = $g->_heap_walk($g->new, \&_SPT_add, \%etc, %opt); 3413 $spt_di->{ $first_root } = [ $g->[ _G ], $sptg ]; 3414 $g->set_graph_attribute('_spt_di', $spt_di); 3415 } 3416 3417 my $spt = $spt_di->{ $first_root }->[ 1 ]; 3418 3419 $spt->set_graph_attribute('SPT_Dijkstra_root', $first_root); 3420 3421 return $spt; 3422} 3423 3424*SSSP_Dijkstra = \&SPT_Dijkstra; 3425 3426*single_source_shortest_paths = \&SPT_Dijkstra; 3427 3428sub SP_Dijkstra { 3429 my ($g, $u, $v) = @_; 3430 my $sptg = $g->SPT_Dijkstra(first_root => $u); 3431 my @path = ($v); 3432 my %seen; 3433 my $V = $g->vertices; 3434 my $p; 3435 while (defined($p = $sptg->get_vertex_attribute($v, 'p'))) { 3436 last if exists $seen{$p}; 3437 push @path, $p; 3438 $v = $p; 3439 $seen{$p}++; 3440 last if keys %seen == $V || $u eq $v; 3441 } 3442 @path = () if @path && $path[-1] ne $u; 3443 return reverse @path; 3444} 3445 3446sub __SPT_Bellman_Ford { 3447 my ($g, $u, $v, $attr, $d, $p, $c0, $c1) = @_; 3448 return unless $c0->{ $u }; 3449 my $w = $g->get_edge_attribute($u, $v, $attr); 3450 $w = 1 unless defined $w; 3451 if (defined $d->{ $v }) { 3452 if (defined $d->{ $u }) { 3453 if ($d->{ $v } > $d->{ $u } + $w) { 3454 $d->{ $v } = $d->{ $u } + $w; 3455 $p->{ $v } = $u; 3456 $c1->{ $v }++; 3457 } 3458 } # else !defined $d->{ $u } && defined $d->{ $v } 3459 } else { 3460 if (defined $d->{ $u }) { 3461 # defined $d->{ $u } && !defined $d->{ $v } 3462 $d->{ $v } = $d->{ $u } + $w; 3463 $p->{ $v } = $u; 3464 $c1->{ $v }++; 3465 } # else !defined $d->{ $u } && !defined $d->{ $v } 3466 } 3467} 3468 3469sub _SPT_Bellman_Ford { 3470 my ($g, $opt, $unseenh, $unseena, $r, $next, $code, $attr) = @_; 3471 my %d; 3472 return unless defined $r; 3473 $d{ $r } = 0; 3474 my %p; 3475 my $V = $g->vertices; 3476 my %c0; # Changed during the last iteration? 3477 $c0{ $r }++; 3478 for (my $i = 0; $i < $V; $i++) { 3479 my %c1; 3480 for my $e ($g->edges) { 3481 my ($u, $v) = @$e; 3482 __SPT_Bellman_Ford($g, $u, $v, $attr, \%d, \%p, \%c0, \%c1); 3483 if ($g->undirected) { 3484 __SPT_Bellman_Ford($g, $v, $u, $attr, \%d, \%p, \%c0, \%c1); 3485 } 3486 } 3487 %c0 = %c1 unless $i == $V - 1; 3488 } 3489 3490 for my $e ($g->edges) { 3491 my ($u, $v) = @$e; 3492 if (defined $d{ $u } && defined $d{ $v }) { 3493 my $d = $g->get_edge_attribute($u, $v, $attr); 3494 if (defined $d && $d{ $v } > $d{ $u } + $d) { 3495 require Carp; 3496 Carp::croak("Graph::SPT_Bellman_Ford: negative cycle exists"); 3497 } 3498 } 3499 } 3500 3501 return (\%p, \%d); 3502} 3503 3504sub _SPT_Bellman_Ford_compute { 3505} 3506 3507sub SPT_Bellman_Ford { 3508 my $g = shift; 3509 3510 my ($opt, $unseenh, $unseena, $r, $next, $code, $attr) = $g->_root_opt(@_); 3511 3512 unless (defined $r) { 3513 $r = $g->random_vertex(); 3514 return unless defined $r; 3515 } 3516 3517 my $spt_bf = $g->get_graph_attribute('_spt_bf'); 3518 unless (defined $spt_bf && 3519 exists $spt_bf->{ $r } && $spt_bf->{ $r }->[ 0 ] == $g->[ _G ]) { 3520 my ($p, $d) = 3521 $g->_SPT_Bellman_Ford($opt, $unseenh, $unseena, 3522 $r, $next, $code, $attr); 3523 my $h = $g->new; 3524 for my $v (keys %$p) { 3525 my $u = $p->{ $v }; 3526 $h->add_edge( $u, $v ); 3527 $h->set_edge_attribute( $u, $v, $attr, 3528 $g->get_edge_attribute($u, $v, $attr)); 3529 $h->set_vertex_attribute( $v, $attr, $d->{ $v } ); 3530 $h->set_vertex_attribute( $v, 'p', $u ); 3531 } 3532 $spt_bf->{ $r } = [ $g->[ _G ], $h ]; 3533 $g->set_graph_attribute('_spt_bf', $spt_bf); 3534 } 3535 3536 my $spt = $spt_bf->{ $r }->[ 1 ]; 3537 3538 $spt->set_graph_attribute('SPT_Bellman_Ford_root', $r); 3539 3540 return $spt; 3541} 3542 3543*SSSP_Bellman_Ford = \&SPT_Bellman_Ford; 3544 3545sub SP_Bellman_Ford { 3546 my ($g, $u, $v) = @_; 3547 my $sptg = $g->SPT_Bellman_Ford(first_root => $u); 3548 my @path = ($v); 3549 my %seen; 3550 my $V = $g->vertices; 3551 my $p; 3552 while (defined($p = $sptg->get_vertex_attribute($v, 'p'))) { 3553 last if exists $seen{$p}; 3554 push @path, $p; 3555 $v = $p; 3556 $seen{$p}++; 3557 last if keys %seen == $V; 3558 } 3559 # @path = () if @path && "$path[-1]" ne "$u"; 3560 return reverse @path; 3561} 3562 3563### 3564# Transitive Closure. 3565# 3566 3567sub TransitiveClosure_Floyd_Warshall { 3568 my $self = shift; 3569 my $class = ref $self || $self; 3570 $self = shift unless ref $self; 3571 bless Graph::TransitiveClosure->new($self, @_), $class; 3572} 3573 3574*transitive_closure = \&TransitiveClosure_Floyd_Warshall; 3575 3576sub APSP_Floyd_Warshall { 3577 my $self = shift; 3578 my $class = ref $self || $self; 3579 $self = shift unless ref $self; 3580 bless Graph::TransitiveClosure->new($self, path => 1, @_), $class; 3581} 3582 3583*all_pairs_shortest_paths = \&APSP_Floyd_Warshall; 3584 3585sub _transitive_closure_matrix_compute { 3586} 3587 3588sub transitive_closure_matrix { 3589 my $g = shift; 3590 my $tcm = $g->get_graph_attribute('_tcm'); 3591 if (defined $tcm) { 3592 if (ref $tcm eq 'ARRAY') { # YECHHH! 3593 if ($tcm->[ 0 ] == $g->[ _G ]) { 3594 $tcm = $tcm->[ 1 ]; 3595 } else { 3596 undef $tcm; 3597 } 3598 } 3599 } 3600 unless (defined $tcm) { 3601 my $apsp = $g->APSP_Floyd_Warshall(@_); 3602 $tcm = $apsp->get_graph_attribute('_tcm'); 3603 $g->set_graph_attribute('_tcm', [ $g->[ _G ], $tcm ]); 3604 } 3605 3606 return $tcm; 3607} 3608 3609sub path_length { 3610 my $g = shift; 3611 my $tcm = $g->transitive_closure_matrix; 3612 $tcm->path_length(@_); 3613} 3614 3615sub path_predecessor { 3616 my $g = shift; 3617 my $tcm = $g->transitive_closure_matrix; 3618 $tcm->path_predecessor(@_); 3619} 3620 3621sub path_vertices { 3622 my $g = shift; 3623 my $tcm = $g->transitive_closure_matrix; 3624 $tcm->path_vertices(@_); 3625} 3626 3627sub is_reachable { 3628 my $g = shift; 3629 my $tcm = $g->transitive_closure_matrix; 3630 $tcm->is_reachable(@_); 3631} 3632 3633sub for_shortest_paths { 3634 my $g = shift; 3635 my $c = shift; 3636 my $t = $g->transitive_closure_matrix; 3637 my @v = $g->vertices; 3638 my $n = 0; 3639 for my $u (@v) { 3640 for my $v (@v) { 3641 next unless $t->is_reachable($u, $v); 3642 $n++; 3643 $c->($t, $u, $v, $n); 3644 } 3645 } 3646 return $n; 3647} 3648 3649sub _minmax_path { 3650 my $g = shift; 3651 my $min; 3652 my $max; 3653 my $minp; 3654 my $maxp; 3655 $g->for_shortest_paths(sub { 3656 my ($t, $u, $v, $n) = @_; 3657 my $l = $t->path_length($u, $v); 3658 return unless defined $l; 3659 my $p; 3660 if ($u ne $v && (!defined $max || $l > $max)) { 3661 $max = $l; 3662 $maxp = $p = [ $t->path_vertices($u, $v) ]; 3663 } 3664 if ($u ne $v && (!defined $min || $l < $min)) { 3665 $min = $l; 3666 $minp = $p || [ $t->path_vertices($u, $v) ]; 3667 } 3668 }); 3669 return ($min, $max, $minp, $maxp); 3670} 3671 3672sub diameter { 3673 my $g = shift; 3674 my ($min, $max, $minp, $maxp) = $g->_minmax_path(@_); 3675 return defined $maxp ? (wantarray ? @$maxp : $max) : undef; 3676} 3677 3678*graph_diameter = \&diameter; 3679 3680sub longest_path { 3681 my ($g, $u, $v) = @_; 3682 my $t = $g->transitive_closure_matrix; 3683 if (defined $u) { 3684 if (defined $v) { 3685 return wantarray ? 3686 $t->path_vertices($u, $v) : $t->path_length($u, $v); 3687 } else { 3688 my $max; 3689 my @max; 3690 for my $v ($g->vertices) { 3691 next if $u eq $v; 3692 my $l = $t->path_length($u, $v); 3693 if (defined $l && (!defined $max || $l > $max)) { 3694 $max = $l; 3695 @max = $t->path_vertices($u, $v); 3696 } 3697 } 3698 return wantarray ? @max : $max; 3699 } 3700 } else { 3701 if (defined $v) { 3702 my $max; 3703 my @max; 3704 for my $u ($g->vertices) { 3705 next if $u eq $v; 3706 my $l = $t->path_length($u, $v); 3707 if (defined $l && (!defined $max || $l > $max)) { 3708 $max = $l; 3709 @max = $t->path_vertices($u, $v); 3710 } 3711 } 3712 return wantarray ? @max : @max - 1; 3713 } else { 3714 my ($min, $max, $minp, $maxp) = $g->_minmax_path(@_); 3715 return defined $maxp ? (wantarray ? @$maxp : $max) : undef; 3716 } 3717 } 3718} 3719 3720sub vertex_eccentricity { 3721 my ($g, $u) = @_; 3722 $g->expect_undirected; 3723 if ($g->is_connected) { 3724 my $max; 3725 for my $v ($g->vertices) { 3726 next if $u eq $v; 3727 my $l = $g->path_length($u, $v); 3728 if (defined $l && (!defined $max || $l > $max)) { 3729 $max = $l; 3730 } 3731 } 3732 return $max; 3733 } else { 3734 return Infinity(); 3735 } 3736} 3737 3738sub shortest_path { 3739 my ($g, $u, $v) = @_; 3740 $g->expect_undirected; 3741 my $t = $g->transitive_closure_matrix; 3742 if (defined $u) { 3743 if (defined $v) { 3744 return wantarray ? 3745 $t->path_vertices($u, $v) : $t->path_length($u, $v); 3746 } else { 3747 my $min; 3748 my @min; 3749 for my $v ($g->vertices) { 3750 next if $u eq $v; 3751 my $l = $t->path_length($u, $v); 3752 if (defined $l && (!defined $min || $l < $min)) { 3753 $min = $l; 3754 @min = $t->path_vertices($u, $v); 3755 } 3756 } 3757 return wantarray ? @min : $min; 3758 } 3759 } else { 3760 if (defined $v) { 3761 my $min; 3762 my @min; 3763 for my $u ($g->vertices) { 3764 next if $u eq $v; 3765 my $l = $t->path_length($u, $v); 3766 if (defined $l && (!defined $min || $l < $min)) { 3767 $min = $l; 3768 @min = $t->path_vertices($u, $v); 3769 } 3770 } 3771 return wantarray ? @min : $min; 3772 } else { 3773 my ($min, $max, $minp, $maxp) = $g->_minmax_path(@_); 3774 return defined $minp ? (wantarray ? @$minp : $min) : undef; 3775 } 3776 } 3777} 3778 3779sub radius { 3780 my $g = shift; 3781 $g->expect_undirected; 3782 my ($center, $radius) = (undef, Infinity()); 3783 for my $v ($g->vertices) { 3784 my $x = $g->vertex_eccentricity($v); 3785 ($center, $radius) = ($v, $x) if defined $x && $x < $radius; 3786 } 3787 return $radius; 3788} 3789 3790sub center_vertices { 3791 my ($g, $delta) = @_; 3792 $g->expect_undirected; 3793 $delta = 0 unless defined $delta; 3794 $delta = abs($delta); 3795 my @c; 3796 my $r = $g->radius; 3797 if (defined $r) { 3798 for my $v ($g->vertices) { 3799 my $e = $g->vertex_eccentricity($v); 3800 next unless defined $e; 3801 push @c, $v if abs($e - $r) <= $delta; 3802 } 3803 } 3804 return @c; 3805} 3806 3807*centre_vertices = \¢er_vertices; 3808 3809sub average_path_length { 3810 my $g = shift; 3811 my @A = @_; 3812 my $d = 0; 3813 my $m = 0; 3814 my $n = $g->for_shortest_paths(sub { 3815 my ($t, $u, $v, $n) = @_; 3816 my $l = $t->path_length($u, $v); 3817 if ($l) { 3818 my $c = @A == 0 || 3819 (@A == 1 && $u eq $A[0]) || 3820 ((@A == 2) && 3821 (defined $A[0] && 3822 $u eq $A[0]) || 3823 (defined $A[1] && 3824 $v eq $A[1])); 3825 if ($c) { 3826 $d += $l; 3827 $m++; 3828 } 3829 } 3830 }); 3831 return $m ? $d / $m : undef; 3832} 3833 3834### 3835# Simple tests. 3836# 3837 3838sub is_multi_graph { 3839 my $g = shift; 3840 return 0 unless $g->is_multiedged || $g->is_countedged; 3841 my $multiedges = 0; 3842 for my $e ($g->edges05) { 3843 my ($u, @v) = @$e; 3844 for my $v (@v) { 3845 return 0 if $u eq $v; 3846 } 3847 $multiedges++ if $g->get_edge_count(@$e) > 1; 3848 } 3849 return $multiedges; 3850} 3851 3852sub is_simple_graph { 3853 my $g = shift; 3854 return 1 unless $g->is_countedged || $g->is_multiedged; 3855 for my $e ($g->edges05) { 3856 return 0 if $g->get_edge_count(@$e) > 1; 3857 } 3858 return 1; 3859} 3860 3861sub is_pseudo_graph { 3862 my $g = shift; 3863 my $m = $g->is_countedged || $g->is_multiedged; 3864 for my $e ($g->edges05) { 3865 my ($u, @v) = @$e; 3866 for my $v (@v) { 3867 return 1 if $u eq $v; 3868 } 3869 return 1 if $m && $g->get_edge_count($u, @v) > 1; 3870 } 3871 return 0; 3872} 3873 3874### 3875# Rough isomorphism guess. 3876# 3877 3878my %_factorial = (0 => 1, 1 => 1); 3879 3880sub __factorial { 3881 my $n = shift; 3882 for (my $i = 2; $i <= $n; $i++) { 3883 next if exists $_factorial{$i}; 3884 $_factorial{$i} = $i * $_factorial{$i - 1}; 3885 } 3886 $_factorial{$n}; 3887} 3888 3889sub _factorial { 3890 my $n = int(shift); 3891 if ($n < 0) { 3892 require Carp; 3893 Carp::croak("factorial of a negative number"); 3894 } 3895 __factorial($n) unless exists $_factorial{$n}; 3896 return $_factorial{$n}; 3897} 3898 3899sub could_be_isomorphic { 3900 my ($g0, $g1) = @_; 3901 return 0 unless $g0->vertices == $g1->vertices; 3902 return 0 unless $g0->edges05 == $g1->edges05; 3903 my %d0; 3904 for my $v0 ($g0->vertices) { 3905 $d0{ $g0->in_degree($v0) }{ $g0->out_degree($v0) }++ 3906 } 3907 my %d1; 3908 for my $v1 ($g1->vertices) { 3909 $d1{ $g1->in_degree($v1) }{ $g1->out_degree($v1) }++ 3910 } 3911 return 0 unless keys %d0 == keys %d1; 3912 for my $da (keys %d0) { 3913 return 0 3914 unless exists $d1{$da} && 3915 keys %{ $d0{$da} } == keys %{ $d1{$da} }; 3916 for my $db (keys %{ $d0{$da} }) { 3917 return 0 3918 unless exists $d1{$da}{$db} && 3919 $d0{$da}{$db} == $d1{$da}{$db}; 3920 } 3921 } 3922 for my $da (keys %d0) { 3923 for my $db (keys %{ $d0{$da} }) { 3924 return 0 unless $d1{$da}{$db} == $d0{$da}{$db}; 3925 } 3926 delete $d1{$da}; 3927 } 3928 return 0 unless keys %d1 == 0; 3929 my $f = 1; 3930 for my $da (keys %d0) { 3931 for my $db (keys %{ $d0{$da} }) { 3932 $f *= _factorial(abs($d0{$da}{$db})); 3933 } 3934 } 3935 return $f; 3936} 3937 3938### 3939# Analysis functions. 3940 3941sub subgraph_by_radius 3942{ 3943 my ($g, $n, $rad) = @_; 3944 3945 return unless defined $n && defined $rad && $rad >= 0; 3946 3947 my $r = (ref $g)->new; 3948 3949 if ($rad == 0) { 3950 return $r->add_vertex($n); 3951 } 3952 3953 my %h; 3954 $h{1} = [ [ $n, $g->successors($n) ] ]; 3955 for my $i (1..$rad) { 3956 $h{$i+1} = []; 3957 for my $arr (@{ $h{$i} }) { 3958 my ($p, @succ) = @{ $arr }; 3959 for my $s (@succ) { 3960 $r->add_edge($p, $s); 3961 push(@{ $h{$i+1} }, [$s, $g->successors($s)]) if $i < $rad; 3962 } 3963 } 3964 } 3965 3966 return $r; 3967} 3968 3969sub clustering_coefficient { 3970 my ($g) = @_; 3971 my %clustering; 3972 3973 my $gamma = 0; 3974 3975 for my $n ($g->vertices()) { 3976 my $gamma_v = 0; 3977 my @neigh = $g->successors($n); 3978 my %c; 3979 for my $u (@neigh) { 3980 for my $v (@neigh) { 3981 if (!$c{"$u-$v"} && $g->has_edge($u, $v)) { 3982 $gamma_v++; 3983 $c{"$u-$v"} = 1; 3984 $c{"$v-$u"} = 1; 3985 } 3986 } 3987 } 3988 if (@neigh > 1) { 3989 $clustering{$n} = $gamma_v/(@neigh * (@neigh - 1) / 2); 3990 $gamma += $gamma_v/(@neigh * (@neigh - 1) / 2); 3991 } else { 3992 $clustering{$n} = 0; 3993 } 3994 } 3995 3996 $gamma /= $g->vertices(); 3997 3998 return wantarray ? ($gamma, %clustering) : $gamma; 3999} 4000 4001sub betweenness { 4002 my $g = shift; 4003 4004 my @V = $g->vertices(); 4005 4006 my %Cb; # C_b{w} = 0 4007 4008 $Cb{$_} = 0 for @V; 4009 4010 for my $s (@V) { 4011 my @S; # stack (unshift, shift) 4012 4013 my %P; # P{w} = empty list 4014 $P{$_} = [] for @V; 4015 4016 my %sigma; # \sigma{t} = 0 4017 $sigma{$_} = 0 for @V; 4018 $sigma{$s} = 1; 4019 4020 my %d; # d{t} = -1; 4021 $d{$_} = -1 for @V; 4022 $d{$s} = 0; 4023 4024 my @Q; # queue (push, shift) 4025 push @Q, $s; 4026 4027 while (@Q) { 4028 my $v = shift @Q; 4029 unshift @S, $v; 4030 for my $w ($g->successors($v)) { 4031 # w found for first time 4032 if ($d{$w} < 0) { 4033 push @Q, $w; 4034 $d{$w} = $d{$v} + 1; 4035 } 4036 # Shortest path to w via v 4037 if ($d{$w} == $d{$v} + 1) { 4038 $sigma{$w} += $sigma{$v}; 4039 push @{ $P{$w} }, $v; 4040 } 4041 } 4042 } 4043 4044 my %delta; 4045 $delta{$_} = 0 for @V; 4046 4047 while (@S) { 4048 my $w = shift @S; 4049 for my $v (@{ $P{$w} }) { 4050 $delta{$v} += $sigma{$v}/$sigma{$w} * (1 + $delta{$w}); 4051 } 4052 if ($w ne $s) { 4053 $Cb{$w} += $delta{$w}; 4054 } 4055 } 4056 } 4057 4058 return %Cb; 4059} 4060 4061### 4062# Debugging. 4063# 4064 4065sub _dump { 4066 require Data::Dumper; 4067 my $d = Data::Dumper->new([$_[0]],[ref $_[0]]); 4068 defined wantarray ? $d->Dump : print $d->Dump; 4069} 4070 40711; 4072