weavatrix_graph/algo/
dominators.rs1use crate::IndexGraphView;
2use crate::Vec;
3
4type Adjacency<Node> = Vec<Vec<Node>>;
5
6#[derive(Debug, Clone)]
7pub struct Dominators<Node> {
8 root: Node,
9 reachable: Vec<Node>,
10 immediate: Vec<(Node, Node)>,
11}
12
13impl<Node> Dominators<Node>
14where
15 Node: Copy + Eq,
16{
17 #[must_use]
18 pub const fn root(&self) -> Node {
19 self.root
20 }
21
22 #[must_use]
23 pub fn reachable_nodes(&self) -> &[Node] {
24 &self.reachable
25 }
26
27 #[must_use]
28 pub fn immediate_dominator(&self, node: Node) -> Option<Node> {
29 (node != self.root).then(|| {
30 self.immediate
31 .iter()
32 .find_map(|&(candidate, parent)| (candidate == node).then_some(parent))
33 })?
34 }
35
36 pub fn immediate_dominators(&self) -> impl Iterator<Item = (Node, Node)> + '_ {
37 self.immediate.iter().copied()
38 }
39
40 pub fn dominators(&self, node: Node) -> Option<DominatorsIter<'_, Node>> {
41 self.is_reachable(node).then_some(DominatorsIter {
42 result: self,
43 next: Some(node),
44 })
45 }
46
47 pub fn strict_dominators(&self, node: Node) -> Option<DominatorsIter<'_, Node>> {
48 let mut result = self.dominators(node)?;
49 result.next();
50 Some(result)
51 }
52
53 #[must_use]
54 pub fn immediately_dominated_by(&self, node: Node) -> Vec<Node> {
55 self.reachable
56 .iter()
57 .copied()
58 .filter(|candidate| self.immediate_dominator(*candidate) == Some(node))
59 .collect()
60 }
61
62 #[must_use]
63 pub fn dominates(&self, dominator: Node, node: Node) -> bool {
64 self.dominators(node)
65 .is_some_and(|mut chain| chain.any(|candidate| candidate == dominator))
66 }
67
68 #[must_use]
69 pub fn is_reachable(&self, node: Node) -> bool {
70 node == self.root
71 || self
72 .immediate
73 .iter()
74 .any(|&(candidate, _)| candidate == node)
75 }
76}
77
78pub struct DominatorsIter<'result, Node> {
79 result: &'result Dominators<Node>,
80 next: Option<Node>,
81}
82
83impl<Node> Iterator for DominatorsIter<'_, Node>
84where
85 Node: Copy + Eq,
86{
87 type Item = Node;
88
89 fn next(&mut self) -> Option<Self::Item> {
90 let node = self.next?;
91 self.next = self.result.immediate_dominator(node);
92 Some(node)
93 }
94}
95
96#[must_use]
97pub fn dominators<G>(graph: &G, root: G::Node) -> Option<Dominators<G::Node>>
98where
99 G: IndexGraphView,
100{
101 dominators_filtered(graph, root, |_| true)
102}
103
104#[must_use]
105pub fn dominators_filtered<G, F>(
106 graph: &G,
107 root: G::Node,
108 allows_edge: F,
109) -> Option<Dominators<G::Node>>
110where
111 G: IndexGraphView,
112 F: Fn(G::Edge) -> bool,
113{
114 if !graph.contains_node(root) {
115 return None;
116 }
117 let (adjacency, predecessors) = adjacency_pair(graph, &allows_edge);
118 let postorder = reachable_postorder::<G>(root, &adjacency);
119 let reachable = postorder.iter().rev().copied().collect::<Vec<_>>();
120 let mut position = vec![None; graph.node_bound()];
121 for (index, &node) in reachable.iter().enumerate() {
122 position[G::node_slot(node)] = Some(index);
123 }
124 let mut immediate = vec![None; reachable.len()];
125 immediate[0] = Some(0);
126 let mut changed = true;
127 while changed {
128 changed = false;
129 for index in 1..reachable.len() {
130 let node = reachable[index];
131 let candidate = predecessors[G::node_slot(node)]
132 .iter()
133 .filter_map(|predecessor| position[G::node_slot(*predecessor)])
134 .find(|predecessor| immediate[*predecessor].is_some());
135 let Some(mut new_idom) = candidate else {
136 continue;
137 };
138 for predecessor in predecessors[G::node_slot(node)]
139 .iter()
140 .filter_map(|predecessor| position[G::node_slot(*predecessor)])
141 .filter(|predecessor| immediate[*predecessor].is_some())
142 {
143 new_idom = intersect(predecessor, new_idom, &immediate)?;
144 }
145 if immediate[index] != Some(new_idom) {
146 immediate[index] = Some(new_idom);
147 changed = true;
148 }
149 }
150 }
151 let immediate = reachable
152 .iter()
153 .copied()
154 .enumerate()
155 .skip(1)
156 .filter_map(|(index, node)| immediate[index].map(|parent| (node, reachable[parent])))
157 .collect();
158 Some(Dominators {
159 root,
160 reachable,
161 immediate,
162 })
163}
164
165fn adjacency_pair<G, F>(graph: &G, allows_edge: &F) -> (Adjacency<G::Node>, Adjacency<G::Node>)
166where
167 G: IndexGraphView,
168 F: Fn(G::Edge) -> bool,
169{
170 let mut adjacency = vec![Vec::new(); graph.node_bound()];
171 let mut predecessors = vec![Vec::new(); graph.node_bound()];
172 for (edge, endpoints) in graph.edge_references() {
173 if allows_edge(edge) {
174 adjacency[G::node_slot(endpoints.source())].push(endpoints.target());
175 predecessors[G::node_slot(endpoints.target())].push(endpoints.source());
176 }
177 }
178 (adjacency, predecessors)
179}
180
181fn reachable_postorder<G>(root: G::Node, adjacency: &[Vec<G::Node>]) -> Vec<G::Node>
182where
183 G: IndexGraphView,
184{
185 let mut seen = vec![false; adjacency.len()];
186 let mut order = Vec::new();
187 let mut stack = vec![(root, 0_usize)];
188 seen[G::node_slot(root)] = true;
189 while let Some((node, next)) = stack.last_mut() {
190 let Some(&target) = adjacency[G::node_slot(*node)].get(*next) else {
191 order.push(*node);
192 stack.pop();
193 continue;
194 };
195 *next += 1;
196 let slot = G::node_slot(target);
197 if !seen[slot] {
198 seen[slot] = true;
199 stack.push((target, 0));
200 }
201 }
202 order
203}
204
205fn intersect(mut left: usize, mut right: usize, immediate: &[Option<usize>]) -> Option<usize> {
206 while left != right {
207 while left > right {
208 left = immediate.get(left).copied().flatten()?;
209 }
210 while right > left {
211 right = immediate.get(right).copied().flatten()?;
212 }
213 }
214 Some(left)
215}