1use std::cmp::Ordering;
16use std::collections::{hash_map::Iter, HashMap, HashSet};
17use std::hash::Hash;
18
19use crate::visit::{DfsPostOrder, GraphBase, IntoNeighbors, Visitable, Walker};
20
21#[derive(Debug, Clone)]
23pub struct Dominators<N>
24where
25 N: Copy + Eq + Hash,
26{
27 root: N,
28 dominators: HashMap<N, N>,
29}
30
31impl<N> Dominators<N>
32where
33 N: Copy + Eq + Hash,
34{
35 pub fn root(&self) -> N {
37 self.root
38 }
39
40 pub fn immediate_dominator(&self, node: N) -> Option<N> {
45 if node == self.root {
46 None
47 } else {
48 self.dominators.get(&node).cloned()
49 }
50 }
51
52 pub fn strict_dominators(&self, node: N) -> Option<DominatorsIter<N>> {
57 if self.dominators.contains_key(&node) {
58 Some(DominatorsIter {
59 dominators: self,
60 node: self.immediate_dominator(node),
61 })
62 } else {
63 None
64 }
65 }
66
67 pub fn dominators(&self, node: N) -> Option<DominatorsIter<N>> {
73 if self.dominators.contains_key(&node) {
74 Some(DominatorsIter {
75 dominators: self,
76 node: Some(node),
77 })
78 } else {
79 None
80 }
81 }
82
83 pub fn immediately_dominated_by(&self, node: N) -> DominatedByIter<N> {
86 DominatedByIter {
87 iter: self.dominators.iter(),
88 node,
89 }
90 }
91}
92
93#[derive(Debug, Clone)]
95pub struct DominatorsIter<'a, N>
96where
97 N: 'a + Copy + Eq + Hash,
98{
99 dominators: &'a Dominators<N>,
100 node: Option<N>,
101}
102
103impl<'a, N> Iterator for DominatorsIter<'a, N>
104where
105 N: 'a + Copy + Eq + Hash,
106{
107 type Item = N;
108
109 fn next(&mut self) -> Option<Self::Item> {
110 let next = self.node.take();
111 if let Some(next) = next {
112 self.node = self.dominators.immediate_dominator(next);
113 }
114 next
115 }
116}
117
118#[derive(Debug, Clone)]
120pub struct DominatedByIter<'a, N>
121where
122 N: 'a + Copy + Eq + Hash,
123{
124 iter: Iter<'a, N, N>,
125 node: N,
126}
127
128impl<'a, N> Iterator for DominatedByIter<'a, N>
129where
130 N: 'a + Copy + Eq + Hash,
131{
132 type Item = N;
133
134 fn next(&mut self) -> Option<Self::Item> {
135 for (dominator, dominated) in self.iter.by_ref() {
136 if dominated == &self.node && dominated != dominator {
139 return Some(*dominator);
140 }
141 }
142 None
143 }
144 fn size_hint(&self) -> (usize, Option<usize>) {
145 let (_, upper) = self.iter.size_hint();
146 (0, upper)
147 }
148}
149
150const UNDEFINED: usize = ::std::usize::MAX;
153
154pub fn simple_fast<G>(graph: G, root: G::NodeId) -> Dominators<G::NodeId>
164where
165 G: IntoNeighbors + Visitable,
166 <G as GraphBase>::NodeId: Eq + Hash,
167{
168 let (post_order, predecessor_sets) = simple_fast_post_order(graph, root);
169 let length = post_order.len();
170 debug_assert!(length > 0);
171 debug_assert!(post_order.last() == Some(&root));
172
173 let node_to_post_order_idx: HashMap<_, _> = post_order
180 .iter()
181 .enumerate()
182 .map(|(idx, &node)| (node, idx))
183 .collect();
184
185 let idx_to_predecessor_vec =
188 predecessor_sets_to_idx_vecs(&post_order, &node_to_post_order_idx, predecessor_sets);
189
190 let mut dominators = vec![UNDEFINED; length];
191 dominators[length - 1] = length - 1;
192
193 let mut changed = true;
194 while changed {
195 changed = false;
196
197 for idx in (0..length - 1).rev() {
200 debug_assert!(post_order[idx] != root);
201
202 let new_idom_idx = {
207 let mut predecessors = idx_to_predecessor_vec[idx]
208 .iter()
209 .filter(|&&p| dominators[p] != UNDEFINED);
210 let new_idom_idx = predecessors.next().expect(
211 "Because the root is initialized to dominate itself, and is the \
212 first node in every path, there must exist a predecessor to this \
213 node that also has a dominator",
214 );
215 predecessors.fold(*new_idom_idx, |new_idom_idx, &predecessor_idx| {
216 intersect(&dominators, new_idom_idx, predecessor_idx)
217 })
218 };
219
220 debug_assert!(new_idom_idx < length);
221
222 if new_idom_idx != dominators[idx] {
223 dominators[idx] = new_idom_idx;
224 changed = true;
225 }
226 }
227 }
228
229 debug_assert!(!dominators.iter().any(|&dom| dom == UNDEFINED));
232
233 Dominators {
234 root,
235 dominators: dominators
236 .into_iter()
237 .enumerate()
238 .map(|(idx, dom_idx)| (post_order[idx], post_order[dom_idx]))
239 .collect(),
240 }
241}
242
243fn intersect(dominators: &[usize], mut finger1: usize, mut finger2: usize) -> usize {
244 loop {
245 match finger1.cmp(&finger2) {
246 Ordering::Less => finger1 = dominators[finger1],
247 Ordering::Greater => finger2 = dominators[finger2],
248 Ordering::Equal => return finger1,
249 }
250 }
251}
252
253fn predecessor_sets_to_idx_vecs<N>(
254 post_order: &[N],
255 node_to_post_order_idx: &HashMap<N, usize>,
256 mut predecessor_sets: HashMap<N, HashSet<N>>,
257) -> Vec<Vec<usize>>
258where
259 N: Copy + Eq + Hash,
260{
261 post_order
262 .iter()
263 .map(|node| {
264 predecessor_sets
265 .remove(node)
266 .map(|predecessors| {
267 predecessors
268 .into_iter()
269 .map(|p| *node_to_post_order_idx.get(&p).unwrap())
270 .collect()
271 })
272 .unwrap_or_default()
273 })
274 .collect()
275}
276
277type PredecessorSets<NodeId> = HashMap<NodeId, HashSet<NodeId>>;
278
279fn simple_fast_post_order<G>(
280 graph: G,
281 root: G::NodeId,
282) -> (Vec<G::NodeId>, PredecessorSets<G::NodeId>)
283where
284 G: IntoNeighbors + Visitable,
285 <G as GraphBase>::NodeId: Eq + Hash,
286{
287 let mut post_order = vec![];
288 let mut predecessor_sets = HashMap::new();
289
290 for node in DfsPostOrder::new(graph, root).iter(graph) {
291 post_order.push(node);
292
293 for successor in graph.neighbors(node) {
294 predecessor_sets
295 .entry(successor)
296 .or_insert_with(HashSet::new)
297 .insert(node);
298 }
299 }
300
301 (post_order, predecessor_sets)
302}
303
304#[cfg(test)]
305mod tests {
306 use super::*;
307
308 #[test]
309 fn test_iter_dominators() {
310 let doms: Dominators<u32> = Dominators {
311 root: 0,
312 dominators: [(2, 1), (1, 0), (0, 0)].iter().cloned().collect(),
313 };
314
315 let all_doms: Vec<_> = doms.dominators(2).unwrap().collect();
316 assert_eq!(vec![2, 1, 0], all_doms);
317
318 assert_eq!(None::<()>, doms.dominators(99).map(|_| unreachable!()));
319
320 let strict_doms: Vec<_> = doms.strict_dominators(2).unwrap().collect();
321 assert_eq!(vec![1, 0], strict_doms);
322
323 assert_eq!(
324 None::<()>,
325 doms.strict_dominators(99).map(|_| unreachable!())
326 );
327
328 let dom_by: Vec<_> = doms.immediately_dominated_by(1).collect();
329 assert_eq!(vec![2], dom_by);
330 assert_eq!(None, doms.immediately_dominated_by(99).next());
331 assert_eq!(1, doms.immediately_dominated_by(0).count());
332 }
333}