package Graph::AdjacencyMap::Light; # THIS IS INTERNAL IMPLEMENTATION ONLY, NOT TO BE USED DIRECTLY. # THE INTERFACE IS HARD TO USE AND GOING TO STAY THAT WAY AND # ALMOST GUARANTEED TO CHANGE OR GO AWAY IN FUTURE RELEASES. use strict; use Graph::AdjacencyMap qw(:flags :fields); use base 'Graph::AdjacencyMap'; use Scalar::Util qw(weaken); use Graph::AdjacencyMap::Heavy; use Graph::AdjacencyMap::Vertex; sub _V () { 2 } # Graph::_V sub _E () { 3 } # Graph::_E sub _F () { 0 } # Graph::_F sub _new { my ($class, $graph, $flags, $arity) = @_; my $m = bless [ ], $class; $m->[ _n ] = 0; $m->[ _f ] = $flags | _LIGHT; $m->[ _a ] = $arity; $m->[ _i ] = { }; $m->[ _s ] = { }; $m->[ _p ] = { }; $m->[ _g ] = $graph; weaken $m->[ _g ]; # So that DESTROY finds us earlier. return $m; } sub set_path { my $m = shift; my ($n, $f, $a, $i, $s, $p) = @$m; if ($a == 2) { @_ = sort @_ if ($f & _UNORD); } my $e0 = shift; if ($a == 2) { my $e1 = shift; unless (exists $s->{ $e0 } && exists $s->{ $e0 }->{ $e1 }) { $n = $m->[ _n ]++; $i->{ $n } = [ $e0, $e1 ]; $s->{ $e0 }->{ $e1 } = $n; $p->{ $e1 }->{ $e0 } = $n; } } else { unless (exists $s->{ $e0 }) { $n = $m->[ _n ]++; $s->{ $e0 } = $n; $i->{ $n } = $e0; } } } sub has_path { my $m = shift; my ($n, $f, $a, $i, $s) = @$m; return 0 unless $a == @_; my $e; if ($a == 2) { @_ = sort @_ if ($f & _UNORD); $e = shift; return 0 unless exists $s->{ $e }; $s = $s->{ $e }; } $e = shift; exists $s->{ $e }; } sub _get_path_id { my $m = shift; my ($n, $f, $a, $i, $s) = @$m; return undef unless $a == @_; my $e; if ($a == 2) { @_ = sort @_ if ($f & _UNORD); $e = shift; return undef unless exists $s->{ $e }; $s = $s->{ $e }; } $e = shift; $s->{ $e }; } sub _get_path_count { my $m = shift; my ($n, $f, $a, $i, $s) = @$m; my $e; if (@_ == 2) { @_ = sort @_ if ($f & _UNORD); $e = shift; return undef unless exists $s->{ $e }; $s = $s->{ $e }; } $e = shift; return exists $s->{ $e } ? 1 : 0; } sub has_paths { my $m = shift; my ($n, $f, $a, $i, $s) = @$m; keys %$s; } sub paths { my $m = shift; my ($n, $f, $a, $i) = @$m; if (defined $i) { my ($k, $v) = each %$i; if (ref $v) { return values %{ $i }; } else { return map { [ $_ ] } values %{ $i }; } } else { return ( ); } } sub _get_id_path { my $m = shift; my ($n, $f, $a, $i) = @$m; my $p = $i->{ $_[ 0 ] }; defined $p ? ( ref $p eq 'ARRAY' ? @$p : $p ) : ( ); } sub del_path { my $m = shift; my ($n, $f, $a, $i, $s, $p) = @$m; if (@_ == 2) { @_ = sort @_ if ($f & _UNORD); my $e0 = shift; return 0 unless exists $s->{ $e0 }; my $e1 = shift; if (defined($n = $s->{ $e0 }->{ $e1 })) { delete $i->{ $n }; delete $s->{ $e0 }->{ $e1 }; delete $p->{ $e1 }->{ $e0 }; delete $s->{ $e0 } unless keys %{ $s->{ $e0 } }; delete $p->{ $e1 } unless keys %{ $p->{ $e1 } }; return 1; } } else { my $e = shift; if (defined($n = $s->{ $e })) { delete $i->{ $n }; delete $s->{ $e }; return 1; } } return 0; } sub __successors { my $E = shift; return wantarray ? () : 0 unless defined $E->[ _s ]; my $g = shift; my $V = $g->[ _V ]; return wantarray ? () : 0 unless defined $V && defined $V->[ _s ]; # my $i = $V->_get_path_id( $_[0] ); my $i = ($V->[ _f ] & _LIGHT) ? $V->[ _s ]->{ $_[0] } : $V->_get_path_id( $_[0] ); return wantarray ? () : 0 unless defined $i && defined $E->[ _s ]->{ $i }; return keys %{ $E->[ _s ]->{ $i } }; } sub _successors { my $E = shift; my $g = shift; my @s = $E->__successors($g, @_); if (($E->[ _f ] & _UNORD)) { push @s, $E->__predecessors($g, @_); my %s; @s{ @s } = (); @s = keys %s; } my $V = $g->[ _V ]; return wantarray ? map { $V->[ _i ]->{ $_ } } @s : @s; } sub __predecessors { my $E = shift; return wantarray ? () : 0 unless defined $E->[ _p ]; my $g = shift; my $V = $g->[ _V ]; return wantarray ? () : 0 unless defined $V && defined $V->[ _s ]; # my $i = $V->_get_path_id( $_[0] ); my $i = ($V->[ _f ] & _LIGHT) ? $V->[ _s ]->{ $_[0] } : $V->_get_path_id( $_[0] ); return wantarray ? () : 0 unless defined $i && defined $E->[ _p ]->{ $i }; return keys %{ $E->[ _p ]->{ $i } }; } sub _predecessors { my $E = shift; my $g = shift; my @p = $E->__predecessors($g, @_); if ($E->[ _f ] & _UNORD) { push @p, $E->__successors($g, @_); my %p; @p{ @p } = (); @p = keys %p; } my $V = $g->[ _V ]; return wantarray ? map { $V->[ _i ]->{ $_ } } @p : @p; } sub __attr { # Major magic takes place here: we rebless the appropriate 'light' # map into a more complex map and then redispatch the method. my $m = $_[0]; my ($n, $f, $a, $i, $s, $p, $g) = @$m; my ($k, $v) = each %$i; my @V = @{ $g->[ _V ] }; my @E = $g->edges; # TODO: Both these (ZZZ) lines are mysteriously needed! # ZZZ: an example of failing tests is t/51_edge_attributes.t. if (ref $v eq 'ARRAY') { # Edges, then. # print "Reedging.\n"; @E = $g->edges; # TODO: Both these (ZZZ) lines are mysteriously needed! $g->[ _E ] = $m = Graph::AdjacencyMap::Heavy->_new($f, 2); $g->add_edges( @E ); } else { # print "Revertexing.\n"; $m = Graph::AdjacencyMap::Vertex->_new(($f & ~_LIGHT), 1); $m->[ _n ] = $V[ _n ]; $m->[ _i ] = $V[ _i ]; $m->[ _s ] = $V[ _s ]; $m->[ _p ] = $V[ _p ]; $g->[ _V ] = $m; } $_[0] = $m; goto &{ ref($m) . "::__attr" }; # Redispatch. } sub _is_COUNT () { 0 } sub _is_MULTI () { 0 } sub _is_HYPER () { 0 } sub _is_UNIQ () { 0 } sub _is_REF () { 0 } 1;