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: 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_lowlink = ctx.next_index;
let current_node_id = current_node.get_id();
ctx.known_nodes.insert(
current_node_id.clone(),
SccAlgoNode {
node: current_node.clone(),
index: ctx.next_index,
lowlink: current_lowlink,
on_stack: true,
},
);
ctx.next_index += 1;
ctx.stack.push(current_node_id.clone());
let mut is_scc_root = true;
for neighbor in current_node.get_neighbors() {
let neighbor_id = neighbor.get_id();
let lowlink_candidate = match ctx.known_nodes.get(&neighbor_id) {
None => {
compute_scc_recursive(ctx, &neighbor);
ctx.known_nodes[&neighbor_id].lowlink
}
Some(neighbor_node) => {
if !neighbor_node.on_stack {
continue;
}
neighbor_node.index
}
};
if lowlink_candidate < current_lowlink {
current_lowlink = lowlink_candidate;
ctx.known_nodes.get_mut(¤t_node_id).unwrap().lowlink = current_lowlink;
is_scc_root = false;
}
}
if !is_scc_root {
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;
let is_root = other_node_id == current_node_id;
scc.push(other_node_id);
if is_root {
break;
}
}
if current_node_id == ctx.target_node_id {
ctx.result = scc;
}
}