cairo_lang_utils/graph_algos/
strongly_connected_components.rs1use core::hash::Hash;
4
5use super::graph_node::GraphNode;
6use crate::unordered_hash_map::UnorderedHashMap;
7
8#[cfg(test)]
9#[path = "strongly_connected_components_test.rs"]
10mod strongly_connected_components_test;
11
12pub trait ComputeScc: GraphNode {
14 fn compute_scc(&self) -> Vec<Self::NodeId>;
15}
16
17#[derive(Default, PartialEq, Eq, Hash, Clone)]
20struct SccAlgoNode<Node: GraphNode> {
21 node: Node,
23 index: u32,
26 lowlink: u32,
28 on_stack: bool,
30}
31
32struct SccAlgoContext<Node: GraphNode> {
34 next_index: u32,
36 stack: Vec<Node::NodeId>,
38 known_nodes: UnorderedHashMap<Node::NodeId, SccAlgoNode<Node>>,
40 target_node_id: Node::NodeId,
42 result: Vec<Node::NodeId>,
44}
45impl<Node: GraphNode> SccAlgoContext<Node> {
46 fn new(target_node_id: Node::NodeId) -> Self {
47 SccAlgoContext::<Node> {
48 next_index: 0,
49 stack: Vec::new(),
50 known_nodes: UnorderedHashMap::default(),
51 target_node_id,
52 result: Vec::new(),
53 }
54 }
55}
56
57pub fn compute_scc<Node: GraphNode>(root: &Node) -> Vec<Node::NodeId> {
59 let mut ctx = SccAlgoContext::new(root.get_id());
60 compute_scc_recursive(&mut ctx, root);
61 ctx.result
62}
63
64fn compute_scc_recursive<Node: GraphNode>(ctx: &mut SccAlgoContext<Node>, current_node: &Node) {
66 let mut current_wrapper_node = SccAlgoNode {
67 node: current_node.clone(),
68 index: ctx.next_index,
69 lowlink: ctx.next_index,
70 on_stack: true,
71 };
72 let current_node_id = current_node.get_id();
73 ctx.known_nodes.insert(current_node_id.clone(), current_wrapper_node.clone());
74 ctx.next_index += 1;
75 ctx.stack.push(current_node_id.clone());
76
77 for neighbor in current_node.get_neighbors() {
78 let neighbor_id = neighbor.get_id();
79 match ctx.known_nodes.get(&neighbor_id) {
80 None => {
81 compute_scc_recursive(ctx, &neighbor);
83 current_wrapper_node.lowlink = core::cmp::min(
85 current_wrapper_node.lowlink,
86 ctx.known_nodes[&neighbor_id].lowlink,
87 );
88 }
89 Some(neighbor_node) => {
90 if ctx.known_nodes[&neighbor_id].on_stack {
91 current_wrapper_node.lowlink =
93 core::cmp::min(current_wrapper_node.lowlink, neighbor_node.index);
94 } else {
95 continue;
98 }
99 }
100 };
101
102 ctx.known_nodes.insert(current_node_id.clone(), current_wrapper_node.clone());
104 }
105
106 if current_wrapper_node.lowlink != current_wrapper_node.index {
107 return;
109 }
110
111 let mut scc = Vec::new();
115 while let Some(other_node_id) = ctx.stack.pop() {
116 let other_node = ctx.known_nodes.get_mut(&other_node_id).unwrap();
117 other_node.on_stack = false;
118 scc.push(other_node_id.clone());
119
120 if other_node_id == current_node_id {
122 break;
123 }
124 }
125
126 if current_node_id == ctx.target_node_id {
129 ctx.result = scc;
130 }
131}