use core::hash::Hash;
use super::graph_node::GraphNode;
use crate::unordered_hash_map::UnorderedHashMap;
#[cfg(test)]
#[path = "strongly_connected_components_test.rs"]
mod strongly_connected_components_test;
pub trait ComputeScc
where
Self: GraphNode,
{
fn compute_scc(&self) -> Vec<Self::NodeId>;
}
#[derive(Default, PartialEq, Eq, Hash, Clone)]
struct SccAlgoNode<Node: GraphNode> {
node: Node,
index: u32,
lowlink: u32,
on_stack: bool,
}
struct SccAlgoContext<Node: GraphNode> {
next_index: u32,
stack: Vec<Node::NodeId>,
known_nodes: UnorderedHashMap<Node::NodeId, SccAlgoNode<Node>>,
target_node_id: Node::NodeId,
result: Vec<Node::NodeId>,
}
impl<Node: GraphNode> SccAlgoContext<Node> {
fn new(target_node_id: Node::NodeId) -> Self {
SccAlgoContext::<Node> {
next_index: 0,
stack: Vec::new(),
known_nodes: UnorderedHashMap::default(),
target_node_id,
result: Vec::new(),
}
}
}
pub fn compute_scc<Node: GraphNode>(root: &Node) -> Vec<Node::NodeId> {
let mut ctx = SccAlgoContext::new(root.get_id());
compute_scc_recursive(&mut ctx, root);
ctx.result
}
fn compute_scc_recursive<Node: GraphNode>(ctx: &mut SccAlgoContext<Node>, current_node: &Node) {
let mut current_wrapper_node = SccAlgoNode {
node: current_node.clone(),
index: ctx.next_index,
lowlink: ctx.next_index,
on_stack: true,
};
let current_node_id = current_node.get_id();
ctx.known_nodes.insert(current_node_id.clone(), current_wrapper_node.clone());
ctx.next_index += 1;
ctx.stack.push(current_node_id.clone());
for neighbor in current_node.get_neighbors() {
let neighbor_id = neighbor.get_id();
match ctx.known_nodes.get(&neighbor_id) {
None => {
compute_scc_recursive(ctx, &neighbor);
current_wrapper_node.lowlink = std::cmp::min(
current_wrapper_node.lowlink,
ctx.known_nodes[&neighbor_id].lowlink,
);
}
Some(neighbor_node) => {
if ctx.known_nodes[&neighbor_id].on_stack {
current_wrapper_node.lowlink =
std::cmp::min(current_wrapper_node.lowlink, neighbor_node.index);
} else {
continue;
}
}
};
ctx.known_nodes.insert(current_node_id.clone(), current_wrapper_node.clone());
}
if current_wrapper_node.lowlink != current_wrapper_node.index {
return;
}
let mut scc = Vec::new();
while let Some(other_node_id) = ctx.stack.pop() {
let other_node = ctx.known_nodes.get_mut(&other_node_id).unwrap();
other_node.on_stack = false;
scc.push(other_node_id.clone());
if other_node_id == current_node_id {
break;
}
}
if current_node_id == ctx.target_node_id {
ctx.result = scc;
}
}