petgraph/algo/
dominators.rs1use std::collections::{HashMap, HashSet};
16use std::hash::Hash;
17
18use visit::{DfsPostOrder, GraphBase, IntoNeighbors, Visitable, Walker};
19
20#[derive(Debug, Clone)]
22pub struct Dominators<N>
23 where N: Copy + Eq + Hash
24{
25 root: N,
26 dominators: HashMap<N, N>,
27}
28
29impl<N> Dominators<N>
30 where N: Copy + Eq + Hash
31{
32 pub fn root(&self) -> N {
34 self.root
35 }
36
37 pub fn immediate_dominator(&self, node: N) -> Option<N> {
42 if node == self.root {
43 None
44 } else {
45 self.dominators.get(&node).cloned()
46 }
47 }
48
49 pub fn strict_dominators(&self, node: N) -> Option<DominatorsIter<N>> {
54 if self.dominators.contains_key(&node) {
55 Some(DominatorsIter {
56 dominators: self,
57 node: self.immediate_dominator(node),
58 })
59 } else {
60 None
61 }
62 }
63
64 pub fn dominators(&self, node: N) -> Option<DominatorsIter<N>> {
70 if self.dominators.contains_key(&node) {
71 Some(DominatorsIter {
72 dominators: self,
73 node: Some(node),
74 })
75 } else {
76 None
77 }
78 }
79}
80
81pub struct DominatorsIter<'a, N>
83 where N: 'a + Copy + Eq + Hash
84{
85 dominators: &'a Dominators<N>,
86 node: Option<N>,
87}
88
89impl<'a, N> Iterator for DominatorsIter<'a, N>
90 where N: 'a + Copy + Eq + Hash
91{
92 type Item = N;
93
94 fn next(&mut self) -> Option<Self::Item> {
95 let next = self.node.take();
96 if let Some(next) = next {
97 self.node = self.dominators.immediate_dominator(next);
98 }
99 next
100 }
101}
102
103const UNDEFINED: usize = ::std::usize::MAX;
106
107pub fn simple_fast<G>(graph: G, root: G::NodeId) -> Dominators<G::NodeId>
117 where G: IntoNeighbors + Visitable,
118 <G as GraphBase>::NodeId: Eq + Hash
119{
120 let (post_order, predecessor_sets) = simple_fast_post_order(graph, root);
121 let length = post_order.len();
122 debug_assert!(length > 0);
123 debug_assert!(post_order.last() == Some(&root));
124
125 let node_to_post_order_idx: HashMap<_, _> = post_order.iter()
132 .enumerate()
133 .map(|(idx, &node)| (node, idx))
134 .collect();
135
136 let idx_to_predecessor_vec =
139 predecessor_sets_to_idx_vecs(&post_order, &node_to_post_order_idx, predecessor_sets);
140
141 let mut dominators = vec![UNDEFINED; length];
142 dominators[length - 1] = length - 1;
143
144 let mut changed = true;
145 while changed {
146 changed = false;
147
148 for idx in (0..length - 1).rev() {
151 debug_assert!(post_order[idx] != root);
152
153 let new_idom_idx = {
158 let mut predecessors =
159 idx_to_predecessor_vec[idx].iter().filter(|&&p| dominators[p] != UNDEFINED);
160 let new_idom_idx = predecessors.next()
161 .expect("Because the root is initialized to dominate itself, and is the \
162 first node in every path, there must exist a predecessor to this \
163 node that also has a dominator");
164 predecessors.fold(*new_idom_idx, |new_idom_idx, &predecessor_idx| {
165 intersect(&dominators, new_idom_idx, predecessor_idx)
166 })
167 };
168
169 debug_assert!(new_idom_idx < length);
170
171 if new_idom_idx != dominators[idx] {
172 dominators[idx] = new_idom_idx;
173 changed = true;
174 }
175 }
176 }
177
178 debug_assert!(!dominators.iter().any(|&dom| dom == UNDEFINED));
181
182 Dominators {
183 root: root,
184 dominators: dominators.into_iter()
185 .enumerate()
186 .map(|(idx, dom_idx)| (post_order[idx], post_order[dom_idx]))
187 .collect(),
188 }
189}
190
191fn intersect(dominators: &[usize], mut finger1: usize, mut finger2: usize) -> usize {
192 while finger1 != finger2 {
193 if finger1 < finger2 {
194 finger1 = dominators[finger1];
195 } else if finger2 < finger1 {
196 finger2 = dominators[finger2];
197 }
198 }
199 finger1
200}
201
202fn predecessor_sets_to_idx_vecs<N>(post_order: &[N],
203 node_to_post_order_idx: &HashMap<N, usize>,
204 mut predecessor_sets: HashMap<N, HashSet<N>>)
205 -> Vec<Vec<usize>>
206 where N: Copy + Eq + Hash
207{
208 post_order.iter()
209 .map(|node| {
210 predecessor_sets.remove(node)
211 .map(|predecessors| {
212 predecessors.into_iter()
213 .map(|p| *node_to_post_order_idx.get(&p).unwrap())
214 .collect()
215 })
216 .unwrap_or_else(Vec::new)
217 })
218 .collect()
219}
220
221fn simple_fast_post_order<G>(graph: G,
222 root: G::NodeId)
223 -> (Vec<G::NodeId>, HashMap<G::NodeId, HashSet<G::NodeId>>)
224 where G: IntoNeighbors + Visitable,
225 <G as GraphBase>::NodeId: Eq + Hash
226{
227 let mut post_order = vec![];
228 let mut predecessor_sets = HashMap::new();
229
230 for node in DfsPostOrder::new(graph, root).iter(graph) {
231 post_order.push(node);
232
233 for successor in graph.neighbors(node) {
234 predecessor_sets.entry(successor)
235 .or_insert_with(HashSet::new)
236 .insert(node);
237 }
238 }
239
240 (post_order, predecessor_sets)
241}
242
243#[cfg(test)]
244mod tests {
245 use super::*;
246
247 #[test]
248 fn test_iter_dominators() {
249 let doms: Dominators<u32> = Dominators {
250 root: 0,
251 dominators: [(2, 1), (1, 0), (0, 0)]
252 .iter()
253 .cloned()
254 .collect(),
255 };
256
257 let all_doms: Vec<_> = doms.dominators(2).unwrap().collect();
258 assert_eq!(vec![2, 1, 0], all_doms);
259
260 assert_eq!(None::<()>, doms.dominators(99).map(|_| unreachable!()));
261
262 let strict_doms: Vec<_> = doms.strict_dominators(2).unwrap().collect();
263 assert_eq!(vec![1, 0], strict_doms);
264
265 assert_eq!(None::<()>, doms.strict_dominators(99).map(|_| unreachable!()));
266 }
267}