use crate::compat::FxHashSet;
use std::cmp;
#[derive(Debug, Clone, Eq, Hash, PartialEq)]
pub struct SccExit {
pub exit: usize,
pub from: usize,
pub to: usize,
}
impl SccExit {
pub fn new(from: usize, to: usize) -> Self {
SccExit {
exit: from,
from,
to,
}
}
}
#[derive(Debug, Clone)]
pub struct SccInfo {
pub enter: usize,
pub nodes: FxHashSet<usize>,
pub exits: FxHashSet<SccExit>,
pub backedges: Vec<(usize, usize)>,
pub child_sccs: Vec<usize>,
}
impl SccInfo {
pub fn new(enter: usize) -> Self {
SccInfo {
enter,
nodes: FxHashSet::default(),
exits: FxHashSet::default(),
backedges: Vec::new(),
child_sccs: Vec::new(),
}
}
pub fn is_trivial(&self) -> bool {
self.nodes.is_empty() && self.backedges.is_empty()
}
pub fn enter(&self) -> usize {
self.enter
}
}
pub trait Scc {
fn find_scc(&mut self) {
if self.get_size() == 0 {
return;
}
self.find_scc_from(0);
}
fn find_scc_from(&mut self, start: usize) {
if start >= self.get_size() {
return;
}
let mut stack = Vec::new();
let mut instack = FxHashSet::<usize>::default();
let mut dfn = vec![0; self.get_size()];
let mut low = vec![0; self.get_size()];
let mut time = 1;
self.tarjan(
start,
&mut stack,
&mut instack,
&mut dfn,
&mut low,
&mut time,
);
}
fn on_scc_found(&mut self, root: usize, scc_components: &[usize]);
fn get_next(&mut self, root: usize) -> FxHashSet<usize>;
fn get_size(&mut self) -> usize;
fn tarjan(
&mut self,
index: usize,
stack: &mut Vec<usize>,
instack: &mut FxHashSet<usize>,
dfn: &mut Vec<usize>,
low: &mut Vec<usize>,
time: &mut usize,
) {
dfn[index] = *time;
low[index] = *time;
*time += 1;
stack.push(index);
instack.insert(index);
let size = self.get_size();
let nexts = self.get_next(index);
for next in nexts {
if next >= size {
continue;
}
if dfn[next] == 0 {
self.tarjan(next, stack, instack, dfn, low, time);
low[index] = cmp::min(low[index], low[next]);
} else if instack.contains(&next) {
low[index] = cmp::min(low[index], dfn[next]);
}
}
if dfn[index] == low[index] {
let mut component = vec![index];
while let Some(top) = stack.pop() {
instack.remove(&top);
if top == index {
break;
}
component.push(top);
}
self.on_scc_found(index, &component);
}
}
}