blob: 3acaf776967d1431e76eece7a508c62df1362c49 [file] [log] [blame]
use crate::visit::IntoNeighbors;
use crate::visit::{VisitMap, Visitable};
/// Strictly monotonically increasing event time for a depth first search.
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Default, Hash)]
pub struct Time(pub usize);
/// A depth first search (DFS) visitor event.
#[derive(Copy, Clone, Debug)]
pub enum DfsEvent<N> {
Discover(N, Time),
/// An edge of the tree formed by the traversal.
TreeEdge(N, N),
/// An edge to an already visited node.
BackEdge(N, N),
/// A cross or forward edge.
///
/// For an edge *(u, v)*, if the discover time of *v* is greater than *u*,
/// then it is a forward edge, else a cross edge.
CrossForwardEdge(N, N),
/// All edges from a node have been reported.
Finish(N, Time),
}
/// Return if the expression is a break value, execute the provided statement
/// if it is a prune value.
macro_rules! try_control {
($e:expr, $p:stmt) => {
match $e {
x => {
if x.should_break() {
return x;
} else if x.should_prune() {
$p
}
}
}
};
}
/// Control flow for `depth_first_search` callbacks.
#[derive(Copy, Clone, Debug)]
pub enum Control<B> {
/// Continue the DFS traversal as normal.
Continue,
/// Prune the current node from the DFS traversal. No more edges from this
/// node will be reported to the callback. A `DfsEvent::Finish` for this
/// node will still be reported. This can be returned in response to any
/// `DfsEvent`, except `Finish`, which will panic.
Prune,
/// Stop the DFS traversal and return the provided value.
Break(B),
}
impl<B> Control<B> {
pub fn breaking() -> Control<()> {
Control::Break(())
}
/// Get the value in `Control::Break(_)`, if present.
pub fn break_value(self) -> Option<B> {
match self {
Control::Continue | Control::Prune => None,
Control::Break(b) => Some(b),
}
}
}
/// Control flow for callbacks.
///
/// The empty return value `()` is equivalent to continue.
pub trait ControlFlow {
fn continuing() -> Self;
fn should_break(&self) -> bool;
fn should_prune(&self) -> bool;
}
impl ControlFlow for () {
fn continuing() {}
#[inline]
fn should_break(&self) -> bool {
false
}
#[inline]
fn should_prune(&self) -> bool {
false
}
}
impl<B> ControlFlow for Control<B> {
fn continuing() -> Self {
Control::Continue
}
fn should_break(&self) -> bool {
if let Control::Break(_) = *self {
true
} else {
false
}
}
fn should_prune(&self) -> bool {
match *self {
Control::Prune => true,
Control::Continue | Control::Break(_) => false,
}
}
}
impl<C: ControlFlow, E> ControlFlow for Result<C, E> {
fn continuing() -> Self {
Ok(C::continuing())
}
fn should_break(&self) -> bool {
if let Ok(ref c) = *self {
c.should_break()
} else {
true
}
}
fn should_prune(&self) -> bool {
if let Ok(ref c) = *self {
c.should_prune()
} else {
false
}
}
}
/// The default is `Continue`.
impl<B> Default for Control<B> {
fn default() -> Self {
Control::Continue
}
}
/// A recursive depth first search.
///
/// Starting points are the nodes in the iterator `starts` (specify just one
/// start vertex *x* by using `Some(x)`).
///
/// The traversal emits discovery and finish events for each reachable vertex,
/// and edge classification of each reachable edge. `visitor` is called for each
/// event, see [`DfsEvent`][de] for possible values.
///
/// If the return value of the visitor is simply `()`, the visit runs until it
/// is finished. If the return value is a `Control<B>`, it can be used to
/// change the control flow of the search. `Control::Break` will stop
/// the visit early, returning the contained value from the function.
/// `Control::Prune` will stop traversing any additional edges from the current
/// node and proceed immediately to the `Finish` event.
///
/// ***Panics** if you attempt to prune a node from its `Finish` event.
///
/// [de]: enum.DfsEvent.html
///
/// # Example
///
/// Find a path from vertex 0 to 5, and exit the visit as soon as we reach
/// the goal vertex.
///
/// ```
/// use petgraph::prelude::*;
/// use petgraph::graph::node_index as n;
/// use petgraph::visit::depth_first_search;
/// use petgraph::visit::{DfsEvent, Control};
///
/// let gr: Graph<(), ()> = Graph::from_edges(&[
/// (0, 1), (0, 2), (0, 3),
/// (1, 3),
/// (2, 3), (2, 4),
/// (4, 0), (4, 5),
/// ]);
///
/// // record each predecessor, mapping node → node
/// let mut predecessor = vec![NodeIndex::end(); gr.node_count()];
/// let start = n(0);
/// let goal = n(5);
/// depth_first_search(&gr, Some(start), |event| {
/// if let DfsEvent::TreeEdge(u, v) = event {
/// predecessor[v.index()] = u;
/// if v == goal {
/// return Control::Break(v);
/// }
/// }
/// Control::Continue
/// });
///
/// let mut next = goal;
/// let mut path = vec![next];
/// while next != start {
/// let pred = predecessor[next.index()];
/// path.push(pred);
/// next = pred;
/// }
/// path.reverse();
/// assert_eq!(&path, &[n(0), n(2), n(4), n(5)]);
/// ```
pub fn depth_first_search<G, I, F, C>(graph: G, starts: I, mut visitor: F) -> C
where
G: IntoNeighbors + Visitable,
I: IntoIterator<Item = G::NodeId>,
F: FnMut(DfsEvent<G::NodeId>) -> C,
C: ControlFlow,
{
let time = &mut Time(0);
let discovered = &mut graph.visit_map();
let finished = &mut graph.visit_map();
for start in starts {
try_control!(
dfs_visitor(graph, start, &mut visitor, discovered, finished, time),
unreachable!()
);
}
C::continuing()
}
fn dfs_visitor<G, F, C>(
graph: G,
u: G::NodeId,
visitor: &mut F,
discovered: &mut G::Map,
finished: &mut G::Map,
time: &mut Time,
) -> C
where
G: IntoNeighbors + Visitable,
F: FnMut(DfsEvent<G::NodeId>) -> C,
C: ControlFlow,
{
if !discovered.visit(u) {
return C::continuing();
}
'prune: loop {
try_control!(
visitor(DfsEvent::Discover(u, time_post_inc(time))),
break 'prune
);
for v in graph.neighbors(u) {
if !discovered.is_visited(&v) {
try_control!(visitor(DfsEvent::TreeEdge(u, v)), continue);
try_control!(
dfs_visitor(graph, v, visitor, discovered, finished, time),
unreachable!()
);
} else if !finished.is_visited(&v) {
try_control!(visitor(DfsEvent::BackEdge(u, v)), continue);
} else {
try_control!(visitor(DfsEvent::CrossForwardEdge(u, v)), continue);
}
}
break;
}
let first_finish = finished.visit(u);
debug_assert!(first_finish);
try_control!(
visitor(DfsEvent::Finish(u, time_post_inc(time))),
panic!("Pruning on the `DfsEvent::Finish` is not supported!")
);
C::continuing()
}
fn time_post_inc(x: &mut Time) -> Time {
let v = *x;
x.0 += 1;
v
}