sub
reset
{
my
$self
=
shift
;
$self
->{ unseen } = Set::Object->new(
$self
->{ graph }->vertices);
$self
->{ seen } = Set::Object->new;
$self
->{ order } = [ ];
$self
->{ preorder } = [ ];
$self
->{ postorder } = [ ];
$self
->{ roots } = [ ];
$self
->{ tree } = Graph->new(
directed
=>
$self
->{ graph }->directed);
delete
$self
->{ terminate };
}
sub
_see {
my
$self
=
shift
;
$self
->see;
}
sub
has_a_cycle {
my
(
$u
,
$v
,
$t
,
$s
) =
@_
;
$s
->{ has_a_cycle } = 1;
$t
->terminate;
}
sub
find_a_cycle {
my
(
$u
,
$v
,
$t
,
$s
) =
@_
;
my
@cycle
= (
$u
);
push
@cycle
,
$v
unless
$u
eq
$v
;
my
$path
=
$t
->{ order };
if
(
@$path
) {
my
$i
=
$#$path
;
while
(
$i
>= 0 &&
$path
->[
$i
] ne
$v
) {
$i
-- }
if
(
$i
>= 0) {
unshift
@cycle
, @{
$path
}[
$i
+1 ..
$#$path
];
}
}
$s
->{ a_cycle } = \
@cycle
;
$t
->terminate;
}
my
@KNOWN_CONFIG
=
qw(
tree_edge seen_edge
next_alphabetic next_numeric next_random
has_a_cycle find_a_cycle
)
;
my
@EXTRACT_CONFIG
=
qw(
pre post pre_vertex post_vertex
pre_edge post_edge back_edge down_edge cross_edge non_tree_edge
first_root next_root next_successor
)
;
sub
new {
my
(
$class
,
$g
,
%attr
) =
@_
;
Graph::__carp_confess(
"Graph::Traversal: first argument is not a Graph"
)
unless
ref
$g
&&
$g
->isa(
'Graph'
);
my
$self
=
bless
{
graph
=>
$g
,
state
=> { } },
$class
;
$self
->
reset
;
if
(
exists
$attr
{ start }) {
$attr
{ first_root } =
delete
$attr
{ start };
$attr
{ next_root } =
undef
;
}
my
@found_known
=
grep
exists
$attr
{
$_
},
@EXTRACT_CONFIG
;
@$self
{
@found_known
} =
delete
@attr
{
@found_known
};
$self
->{ seen_edge } =
$attr
{ seen_edge }
if
exists
$attr
{ seen_edge } and (
$g
->multiedged ||
$g
->countedged);
$self
->{ pre_edge } =
$attr
{ tree_edge }
if
exists
$attr
{ tree_edge };
my
$default_next
=
$attr
{ next_alphabetic } ? \
&Graph::_next_alphabetic
:
$attr
{ next_numeric } ? \
&Graph::_next_numeric
:
\
&Graph::_next_random
;
$self
->{ next_root } =
$default_next
if
!
exists
$self
->{ next_root };
$self
->{ first_root } =
$self
->{ next_root }
if
!
exists
$self
->{ first_root };
$self
->{ next_successor } =
$default_next
if
!
exists
$self
->{ next_successor };
if
(
exists
$attr
{ has_a_cycle }) {
$self
->{ back_edge } =
my
$has_a_cycle
=
ref
$attr
{ has_a_cycle } eq
'CODE'
?
$attr
{ has_a_cycle } : \
&has_a_cycle
;
$self
->{ down_edge } =
$has_a_cycle
if
$g
->is_undirected;
}
if
(
exists
$attr
{ find_a_cycle }) {
$self
->{ back_edge } =
my
$find_a_cycle
=
ref
$attr
{ find_a_cycle } eq
'CODE'
?
$attr
{ find_a_cycle } : \
&find_a_cycle
;
$self
->{ down_edge } =
$find_a_cycle
if
$g
->is_undirected;
}
$self
->{ add } = \
&add_order
;
$self
->{ see } = \
&_see
;
delete
@attr
{
@KNOWN_CONFIG
};
Graph::_opt_unknown(\
%attr
);
return
$self
;
}
sub
terminate {
my
$self
=
shift
;
$self
->{ terminate } = 1;
}
sub
add_order {
my
(
$self
,
@next
) =
@_
;
push
@{
$self
->{ order } },
@next
;
}
sub
visit {
my
(
$self
,
@next
) =
@_
;
$self
->{ unseen }->remove(
@next
);
$self
->{ seen }->insert(
@next
);
$self
->{ add }->(
$self
,
@next
);
return
unless
my
$p
=
$self
->{ pre };
$p
->(
$_
,
$self
)
for
@next
;
}
sub
visit_preorder {
my
(
$self
,
@next
) =
@_
;
push
@{
$self
->{ preorder } },
@next
;
$self
->{ preordern }->{
$_
} =
$self
->{ preorderi }++
for
@next
;
$self
->visit(
@next
);
}
sub
visit_postorder {
my
(
$self
) =
@_
;
my
@post
=
reverse
$self
->{ see }->(
$self
);
push
@{
$self
->{ postorder } },
@post
;
$self
->{ postordern }->{
$_
} =
$self
->{ postorderi }++
for
@post
;
if
(
my
$p
=
$self
->{ post }) {
$p
->(
$_
,
$self
)
for
@post
;
}
return
unless
(
my
$p
=
$self
->{ post_edge }) and
defined
(
my
$u
=
$self
->current);
$p
->(
$u
,
$_
,
$self
,
$self
->{ state })
for
@post
;
}
sub
_callbacks {
my
(
$self
,
$current
,
@all
) =
@_
;
return
unless
@all
;
my
$nontree
=
$self
->{ non_tree_edge };
my
$back
=
$self
->{ back_edge };
my
$down
=
$self
->{ down_edge };
my
$cross
=
$self
->{ cross_edge };
my
$seen
=
$self
->{ seen_edge };
my
$bdc
=
defined
$back
||
defined
$down
||
defined
$cross
;
return
unless
(
defined
$nontree
||
$bdc
||
defined
$seen
);
my
$u
=
$current
;
my
$preu
=
$self
->{ preordern }->{
$u
};
my
$postu
=
$self
->{ postordern }->{
$u
};
for
my
$v
(
@all
) {
if
(!
$self
->{tree}->has_edge(
$u
,
$v
) && (
defined
$nontree
||
$bdc
) &&
exists
$self
->{ seen }->{
$v
}) {
$nontree
->(
$u
,
$v
,
$self
,
$self
->{ state })
if
$nontree
;
if
(
$bdc
) {
my
$postv
=
$self
->{ postordern }->{
$v
};
if
(
$back
&&
(!
defined
$postv
||
$postv
>=
$postu
)) {
$back
->(
$u
,
$v
,
$self
,
$self
->{ state });
}
else
{
my
$prev
=
$self
->{ preordern }->{
$v
};
if
(
$down
&&
$prev
>
$preu
) {
$down
->(
$u
,
$v
,
$self
,
$self
->{ state });
}
elsif
(
$cross
&&
$prev
<
$preu
) {
$cross
->(
$u
,
$v
,
$self
,
$self
->{ state });
}
}
}
}
next
if
!
$seen
;
my
$c
=
$self
->graph->get_edge_count(
$u
,
$v
);
$seen
->(
$u
,
$v
,
$self
,
$self
->{ state } )
while
$c
-- > 1;
}
}
sub
next
{
my
$self
=
shift
;
return
undef
if
$self
->{ terminate };
my
@next
;
while
(
$self
->seeing) {
my
$current
=
$self
->current;
my
$next
= Set::Object->new(
$self
->{ graph }->successors(
$current
));
my
@all
=
$next
->members;
$next
=
$next
->difference(
$self
->{seen});
if
(
$next
->size) {
@next
=
$self
->{ next_successor }->(
$self
, {
map
+(
$_
=>
$_
),
$next
->members } );
$self
->{ tree }->add_edges(
map
[
$current
,
$_
],
@next
);
last
unless
my
$p
=
$self
->{ pre_edge };
$p
->(
$current
,
$_
,
$self
,
$self
->{ state })
for
@next
;
last
;
}
else
{
$self
->visit_postorder;
}
return
undef
if
$self
->{ terminate };
$self
->_callbacks(
$current
,
@all
);
}
unless
(
@next
) {
if
(!@{
$self
->{ roots } } and
defined
(
my
$first
=
$self
->{ first_root })) {
return
unless
@next
=
ref
$first
eq
'CODE'
?
$first
->(
$self
, {
map
+(
$_
=>
$_
),
$self
->unseen } )
:
$first
;
}
return
if
!
@next
and !
$self
->{ next_root };
return
if
!
@next
and !(
@next
=
$self
->{ next_root }->(
$self
, {
map
+(
$_
=>
$_
),
$self
->unseen } ));
return
if
!
defined
$next
[0] or
$self
->{ seen }->contains(
$next
[0]);
push
@{
$self
->{ roots } },
$next
[0];
}
$self
->visit_preorder(
@next
)
if
@next
;
return
$next
[0];
}
sub
_order {
my
(
$self
,
$order
) =
@_
;
1
while
defined
$self
->
next
;
@{
$self
->{
$order
} };
}
sub
preorder {
my
$self
=
shift
;
$self
->_order(
'preorder'
);
}
sub
postorder {
my
$self
=
shift
;
$self
->_order(
'postorder'
);
}
sub
unseen {
my
$self
=
shift
;
$self
->{ unseen }->${
wantarray
? \
'members'
: \
'size'
};
}
sub
seen {
my
$self
=
shift
;
$self
->{ seen }->${
wantarray
? \
'members'
: \
'size'
};
}
sub
seeing {
my
$self
=
shift
;
@{
$self
->{ order } };
}
sub
roots {
my
$self
=
shift
;
@{
$self
->{ roots } };
}
sub
is_root {
my
(
$self
,
$v
) =
@_
;
for
my
$u
(@{
$self
->{ roots } }) {
return
1
if
$u
eq
$v
;
}
return
0;
}
sub
tree {
my
$self
=
shift
;
$self
->{ tree };
}
sub
graph {
my
$self
=
shift
;
$self
->{ graph };
}
sub
vertex_by_postorder {
my
(
$self
,
$i
) =
@_
;
exists
$self
->{ postorder } &&
$self
->{ postorder }->[
$i
];
}
sub
postorder_by_vertex {
my
(
$self
,
$v
) =
@_
;
exists
$self
->{ postordern } &&
$self
->{ postordern }->{
$v
};
}
sub
postorder_vertices {
my
(
$self
,
$v
) =
@_
;
exists
$self
->{ postordern } ? %{
$self
->{ postordern } } : ();
}
sub
vertex_by_preorder {
my
(
$self
,
$i
) =
@_
;
exists
$self
->{ preorder } &&
$self
->{ preorder }->[
$i
];
}
sub
preorder_by_vertex {
my
(
$self
,
$v
) =
@_
;
exists
$self
->{ preordern } &&
$self
->{ preordern }->{
$v
};
}
sub
preorder_vertices {
my
(
$self
,
$v
) =
@_
;
exists
$self
->{ preordern } ? %{
$self
->{ preordern } } : ();
}
sub
has_state {
my
(
$self
,
$var
) =
@_
;
exists
$self
->{ state } &&
exists
$self
->{ state }->{
$var
};
}
sub
get_state {
my
(
$self
,
$var
) =
@_
;
exists
$self
->{ state } ?
$self
->{ state }->{
$var
} :
undef
;
}
sub
set_state {
my
(
$self
,
$var
,
$val
) =
@_
;
$self
->{ state }->{
$var
} =
$val
;
return
1;
}
sub
delete_state {
my
(
$self
,
$var
) =
@_
;
delete
$self
->{ state }->{
$var
};
delete
$self
->{ state }
unless
keys
%{
$self
->{ state } };
return
1;
}
1;