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