use crate::fx::FxHashSet;
use crate::graph::vec_graph::VecGraph;
use crate::graph::{DirectedGraph, GraphSuccessors, WithNumEdges, WithNumNodes, WithSuccessors};
use rustc_index::vec::{Idx, IndexVec};
use std::ops::Range;
#[cfg(test)]
mod tests;
pub struct Sccs<N: Idx, S: Idx> {
scc_indices: IndexVec<N, S>,
scc_data: SccData<S>,
}
struct SccData<S: Idx> {
ranges: IndexVec<S, Range<usize>>,
all_successors: Vec<S>,
}
impl<N: Idx, S: Idx> Sccs<N, S> {
pub fn new(graph: &(impl DirectedGraph<Node = N> + WithNumNodes + WithSuccessors)) -> Self {
SccsConstruction::construct(graph)
}
pub fn num_sccs(&self) -> usize {
self.scc_data.len()
}
pub fn all_sccs(&self) -> impl Iterator<Item = S> {
(0..self.scc_data.len()).map(S::new)
}
pub fn scc(&self, r: N) -> S {
self.scc_indices[r]
}
pub fn successors(&self, scc: S) -> &[S] {
self.scc_data.successors(scc)
}
pub fn reverse(&self) -> VecGraph<S> {
VecGraph::new(
self.num_sccs(),
self.all_sccs()
.flat_map(|source| {
self.successors(source).iter().map(move |&target| (target, source))
})
.collect(),
)
}
}
impl<N: Idx, S: Idx> DirectedGraph for Sccs<N, S> {
type Node = S;
}
impl<N: Idx, S: Idx> WithNumNodes for Sccs<N, S> {
fn num_nodes(&self) -> usize {
self.num_sccs()
}
}
impl<N: Idx, S: Idx> WithNumEdges for Sccs<N, S> {
fn num_edges(&self) -> usize {
self.scc_data.all_successors.len()
}
}
impl<N: Idx, S: Idx> GraphSuccessors<'graph> for Sccs<N, S> {
type Item = S;
type Iter = std::iter::Cloned<std::slice::Iter<'graph, S>>;
}
impl<N: Idx, S: Idx> WithSuccessors for Sccs<N, S> {
fn successors<'graph>(&'graph self, node: S) -> <Self as GraphSuccessors<'graph>>::Iter {
self.successors(node).iter().cloned()
}
}
impl<S: Idx> SccData<S> {
fn len(&self) -> usize {
self.ranges.len()
}
fn successors(&self, scc: S) -> &[S] {
let range = &self.ranges[scc];
&self.all_successors[range.start..range.end]
}
fn create_scc(&mut self, successors: impl IntoIterator<Item = S>) -> S {
let all_successors_start = self.all_successors.len();
self.all_successors.extend(successors);
let all_successors_end = self.all_successors.len();
debug!(
"create_scc({:?}) successors={:?}",
self.ranges.len(),
&self.all_successors[all_successors_start..all_successors_end],
);
self.ranges.push(all_successors_start..all_successors_end)
}
}
struct SccsConstruction<'c, G: DirectedGraph + WithNumNodes + WithSuccessors, S: Idx> {
graph: &'c G,
node_states: IndexVec<G::Node, NodeState<G::Node, S>>,
node_stack: Vec<G::Node>,
successors_stack: Vec<S>,
duplicate_set: FxHashSet<S>,
scc_data: SccData<S>,
}
#[derive(Copy, Clone, Debug)]
enum NodeState<N, S> {
NotVisited,
BeingVisited { depth: usize },
InCycle { scc_index: S },
InCycleWith { parent: N },
}
#[derive(Copy, Clone, Debug)]
enum WalkReturn<S> {
Cycle { min_depth: usize },
Complete { scc_index: S },
}
impl<'c, G, S> SccsConstruction<'c, G, S>
where
G: DirectedGraph + WithNumNodes + WithSuccessors,
S: Idx,
{
fn construct(graph: &'c G) -> Sccs<G::Node, S> {
let num_nodes = graph.num_nodes();
let mut this = Self {
graph,
node_states: IndexVec::from_elem_n(NodeState::NotVisited, num_nodes),
node_stack: Vec::with_capacity(num_nodes),
successors_stack: Vec::new(),
scc_data: SccData { ranges: IndexVec::new(), all_successors: Vec::new() },
duplicate_set: FxHashSet::default(),
};
let scc_indices = (0..num_nodes)
.map(G::Node::new)
.map(|node| match this.walk_node(0, node) {
WalkReturn::Complete { scc_index } => scc_index,
WalkReturn::Cycle { min_depth } => {
panic!("`walk_node(0, {:?})` returned cycle with depth {:?}", node, min_depth)
}
})
.collect();
Sccs { scc_indices, scc_data: this.scc_data }
}
fn walk_node(&mut self, depth: usize, node: G::Node) -> WalkReturn<S> {
debug!("walk_node(depth = {:?}, node = {:?})", depth, node);
match self.find_state(node) {
NodeState::InCycle { scc_index } => WalkReturn::Complete { scc_index },
NodeState::BeingVisited { depth: min_depth } => WalkReturn::Cycle { min_depth },
NodeState::NotVisited => self.walk_unvisited_node(depth, node),
NodeState::InCycleWith { parent } => panic!(
"`find_state` returned `InCycleWith({:?})`, which ought to be impossible",
parent
),
}
}
fn find_state(&mut self, r: G::Node) -> NodeState<G::Node, S> {
debug!("find_state(r = {:?} in state {:?})", r, self.node_states[r]);
match self.node_states[r] {
NodeState::InCycle { scc_index } => NodeState::InCycle { scc_index },
NodeState::BeingVisited { depth } => NodeState::BeingVisited { depth },
NodeState::NotVisited => NodeState::NotVisited,
NodeState::InCycleWith { parent } => {
let parent_state = self.find_state(parent);
debug!("find_state: parent_state = {:?}", parent_state);
match parent_state {
NodeState::InCycle { .. } => {
self.node_states[r] = parent_state;
parent_state
}
NodeState::BeingVisited { depth } => {
self.node_states[r] =
NodeState::InCycleWith { parent: self.node_stack[depth] };
parent_state
}
NodeState::NotVisited | NodeState::InCycleWith { .. } => {
panic!("invalid parent state: {:?}", parent_state)
}
}
}
}
}
fn walk_unvisited_node(&mut self, depth: usize, node: G::Node) -> WalkReturn<S> {
debug!("walk_unvisited_node(depth = {:?}, node = {:?})", depth, node);
debug_assert!(match self.node_states[node] {
NodeState::NotVisited => true,
_ => false,
});
self.node_states[node] = NodeState::BeingVisited { depth };
self.node_stack.push(node);
let mut min_depth = depth;
let mut min_cycle_root = node;
let successors_len = self.successors_stack.len();
for successor_node in self.graph.successors(node) {
debug!("walk_unvisited_node: node = {:?} successor_ode = {:?}", node, successor_node);
match self.walk_node(depth + 1, successor_node) {
WalkReturn::Cycle { min_depth: successor_min_depth } => {
assert!(successor_min_depth <= depth);
if successor_min_depth < min_depth {
debug!(
"walk_unvisited_node: node = {:?} successor_min_depth = {:?}",
node, successor_min_depth
);
min_depth = successor_min_depth;
min_cycle_root = successor_node;
}
}
WalkReturn::Complete { scc_index: successor_scc_index } => {
debug!(
"walk_unvisited_node: node = {:?} successor_scc_index = {:?}",
node, successor_scc_index
);
self.successors_stack.push(successor_scc_index);
}
}
}
let r = self.node_stack.pop();
debug_assert_eq!(r, Some(node));
if min_depth == depth {
let deduplicated_successors = {
let duplicate_set = &mut self.duplicate_set;
duplicate_set.clear();
self.successors_stack
.drain(successors_len..)
.filter(move |&i| duplicate_set.insert(i))
};
let scc_index = self.scc_data.create_scc(deduplicated_successors);
self.node_states[node] = NodeState::InCycle { scc_index };
WalkReturn::Complete { scc_index }
} else {
self.node_states[node] = NodeState::InCycleWith { parent: min_cycle_root };
WalkReturn::Cycle { min_depth }
}
}
}