use crate::prelude::*;
#[derive(Debug, Clone)]
struct TarjanState {
index: Option<usize>,
lowlink: usize,
on_stack: bool,
}
impl Default for TarjanState {
fn default() -> Self {
Self {
index: None,
lowlink: 0,
on_stack: false,
}
}
}
pub fn tarjan<G: Graph>(graph: G) -> impl Iterator<Item = Box<[G::NodeIx]>> {
let mut sccs = Vec::new();
let mut node_states = graph.init_node_map(|_, _| TarjanState::default());
let mut stack = Vec::new();
let mut index_counter = 0usize;
for node_ix in graph.node_indices() {
if node_states[node_ix].index.is_none() {
visit(
&graph,
node_ix,
&mut node_states,
&mut stack,
&mut index_counter,
&mut sccs,
);
}
}
sccs.into_iter()
}
fn visit<G: Graph>(
graph: &G,
node: G::NodeIx,
node_states: &mut impl crate::Mapping<G::NodeIx, TarjanState>,
stack: &mut Vec<G::NodeIx>,
index_counter: &mut usize,
sccs: &mut Vec<Box<[G::NodeIx]>>,
) {
node_states[node].index = Some(*index_counter);
node_states[node].lowlink = *index_counter;
*index_counter += 1;
stack.push(node.clone());
node_states[node].on_stack = true;
for successor in graph.outgoing_edge_indices(node) {
let [_, to_node] = graph.endpoints(successor);
if node_states[to_node].index.is_none() {
visit(graph, to_node, node_states, stack, index_counter, sccs);
node_states[node].lowlink = node_states[node].lowlink.min(node_states[to_node].lowlink);
} else if node_states[to_node].on_stack {
node_states[node].lowlink = node_states[node]
.lowlink
.min(node_states[to_node].index.unwrap());
}
}
if node_states[node].lowlink == node_states[node].index.unwrap() {
let mut scc_nodes = Vec::new();
loop {
let w = stack.pop().expect("Stack should not be empty");
node_states[w.clone()].on_stack = false;
scc_nodes.push(w.clone());
if std::ptr::eq(&w as *const _, &node as *const _)
|| format!("{:?}", w) == format!("{:?}", node)
{
break;
}
}
sccs.push(scc_nodes.into_boxed_slice());
}
}