use super::{DirectedGraph, WithNumNodes, WithStartNode, WithSuccessors};
use rustc_index::bit_set::BitSet;
use rustc_index::vec::IndexVec;
use std::ops::ControlFlow;
#[cfg(test)]
mod tests;
pub fn post_order_from<G: DirectedGraph + WithSuccessors + WithNumNodes>(
graph: &G,
start_node: G::Node,
) -> Vec<G::Node> {
post_order_from_to(graph, start_node, None)
}
pub fn post_order_from_to<G: DirectedGraph + WithSuccessors + WithNumNodes>(
graph: &G,
start_node: G::Node,
end_node: Option<G::Node>,
) -> Vec<G::Node> {
let mut visited: IndexVec<G::Node, bool> = IndexVec::from_elem_n(false, graph.num_nodes());
let mut result: Vec<G::Node> = Vec::with_capacity(graph.num_nodes());
if let Some(end_node) = end_node {
visited[end_node] = true;
}
post_order_walk(graph, start_node, &mut result, &mut visited);
result
}
fn post_order_walk<G: DirectedGraph + WithSuccessors + WithNumNodes>(
graph: &G,
node: G::Node,
result: &mut Vec<G::Node>,
visited: &mut IndexVec<G::Node, bool>,
) {
struct PostOrderFrame<Node, Iter> {
node: Node,
iter: Iter,
}
if visited[node] {
return;
}
let mut stack = vec![PostOrderFrame { node, iter: graph.successors(node) }];
'recurse: while let Some(frame) = stack.last_mut() {
let node = frame.node;
visited[node] = true;
while let Some(successor) = frame.iter.next() {
if !visited[successor] {
stack.push(PostOrderFrame { node: successor, iter: graph.successors(successor) });
continue 'recurse;
}
}
let _ = stack.pop();
result.push(node);
}
}
pub fn reverse_post_order<G: DirectedGraph + WithSuccessors + WithNumNodes>(
graph: &G,
start_node: G::Node,
) -> Vec<G::Node> {
let mut vec = post_order_from(graph, start_node);
vec.reverse();
vec
}
pub struct DepthFirstSearch<'graph, G>
where
G: ?Sized + DirectedGraph + WithNumNodes + WithSuccessors,
{
graph: &'graph G,
stack: Vec<G::Node>,
visited: BitSet<G::Node>,
}
impl<G> DepthFirstSearch<'graph, G>
where
G: ?Sized + DirectedGraph + WithNumNodes + WithSuccessors,
{
pub fn new(graph: &'graph G, start_node: G::Node) -> Self {
Self { graph, stack: vec![start_node], visited: BitSet::new_empty(graph.num_nodes()) }
}
}
impl<G> Iterator for DepthFirstSearch<'_, G>
where
G: ?Sized + DirectedGraph + WithNumNodes + WithSuccessors,
{
type Item = G::Node;
fn next(&mut self) -> Option<G::Node> {
let DepthFirstSearch { stack, visited, graph } = self;
let n = stack.pop()?;
stack.extend(graph.successors(n).filter(|&m| visited.insert(m)));
Some(n)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum NodeStatus {
Visited,
Settled,
}
struct Event<N> {
node: N,
becomes: NodeStatus,
}
pub struct TriColorDepthFirstSearch<'graph, G>
where
G: ?Sized + DirectedGraph + WithNumNodes + WithSuccessors,
{
graph: &'graph G,
stack: Vec<Event<G::Node>>,
visited: BitSet<G::Node>,
settled: BitSet<G::Node>,
}
impl<G> TriColorDepthFirstSearch<'graph, G>
where
G: ?Sized + DirectedGraph + WithNumNodes + WithSuccessors,
{
pub fn new(graph: &'graph G) -> Self {
TriColorDepthFirstSearch {
graph,
stack: vec![],
visited: BitSet::new_empty(graph.num_nodes()),
settled: BitSet::new_empty(graph.num_nodes()),
}
}
pub fn run_from<V>(mut self, root: G::Node, visitor: &mut V) -> Option<V::BreakVal>
where
V: TriColorVisitor<G>,
{
use NodeStatus::{Settled, Visited};
self.stack.push(Event { node: root, becomes: Visited });
loop {
match self.stack.pop()? {
Event { node, becomes: Settled } => {
let not_previously_settled = self.settled.insert(node);
assert!(not_previously_settled, "A node should be settled exactly once");
if let ControlFlow::Break(val) = visitor.node_settled(node) {
return Some(val);
}
}
Event { node, becomes: Visited } => {
let not_previously_visited = self.visited.insert(node);
let prior_status = if not_previously_visited {
None
} else if self.settled.contains(node) {
Some(Settled)
} else {
Some(Visited)
};
if let ControlFlow::Break(val) = visitor.node_examined(node, prior_status) {
return Some(val);
}
if prior_status.is_some() {
continue;
}
self.stack.push(Event { node, becomes: Settled });
for succ in self.graph.successors(node) {
if !visitor.ignore_edge(node, succ) {
self.stack.push(Event { node: succ, becomes: Visited });
}
}
}
}
}
}
}
impl<G> TriColorDepthFirstSearch<'graph, G>
where
G: ?Sized + DirectedGraph + WithNumNodes + WithSuccessors + WithStartNode,
{
pub fn run_from_start<V>(self, visitor: &mut V) -> Option<V::BreakVal>
where
V: TriColorVisitor<G>,
{
let root = self.graph.start_node();
self.run_from(root, visitor)
}
}
pub trait TriColorVisitor<G>
where
G: ?Sized + DirectedGraph,
{
type BreakVal;
fn node_examined(
&mut self,
_node: G::Node,
_prior_status: Option<NodeStatus>,
) -> ControlFlow<Self::BreakVal> {
ControlFlow::CONTINUE
}
fn node_settled(&mut self, _node: G::Node) -> ControlFlow<Self::BreakVal> {
ControlFlow::CONTINUE
}
fn ignore_edge(&mut self, _source: G::Node, _target: G::Node) -> bool {
false
}
}
pub struct CycleDetector;
impl<G> TriColorVisitor<G> for CycleDetector
where
G: ?Sized + DirectedGraph,
{
type BreakVal = ();
fn node_examined(
&mut self,
_node: G::Node,
prior_status: Option<NodeStatus>,
) -> ControlFlow<Self::BreakVal> {
match prior_status {
Some(NodeStatus::Visited) => ControlFlow::BREAK,
_ => ControlFlow::CONTINUE,
}
}
}