1use std::collections::HashMap;
67use std::collections::HashSet;
68use std::hash::Hash;
69use std::iter;
70
71use indexmap::IndexMap;
72use indexmap::IndexSet;
73use itertools::Itertools as _;
74use thiserror::Error;
75
76#[derive(Clone, Eq, PartialEq, Debug)]
79pub struct SimpleDirectedGraph<N>
80where
81 N: Clone + Eq + Hash,
82{
83 adj: IndexMap<N, IndexSet<N>>,
89}
90
91impl<N> SimpleDirectedGraph<N>
92where
93 N: Clone + Eq + Hash,
94{
95 pub fn new<EI>(edges: EI) -> Self
97 where
98 EI: IntoIterator<Item = (N, N)>,
99 {
100 let mut adj: IndexMap<N, IndexSet<N>> = IndexMap::new();
101 for (parent, child) in edges {
102 adj.entry(parent).or_default().insert(child.clone());
103 adj.entry(child).or_default();
104 }
105 Self { adj }
106 }
107
108 pub fn nodes(&self) -> impl Iterator<Item = &N> {
110 self.adj.keys()
111 }
112
113 pub fn num_nodes(&self) -> usize {
115 self.adj.len()
116 }
117
118 pub fn edges(&self) -> impl Iterator<Item = (&N, &N)> {
120 self.adj
121 .iter()
122 .flat_map(|(parent, adj_set)| adj_set.iter().map(move |child| (parent, child)))
123 }
124
125 pub fn adjacent_nodes(&self, node: &N) -> Option<impl DoubleEndedIterator<Item = &N>> {
128 self.adj.get(node).map(|adj_set| adj_set.iter())
129 }
130
131 pub fn contains_node(&self, node: &N) -> bool {
133 self.adj.contains_key(node)
134 }
135
136 pub fn get_postorder<'a>(&'a self, start_node: &'a N) -> Vec<&'a N> {
139 post_order(start_node, |&u| self.adjacent_nodes(u).unwrap()).collect_vec()
140 }
141}
142
143#[derive(Clone, Eq, PartialEq, Debug)]
150pub struct FlowGraph<N>
151where
152 N: Clone + Eq + Hash,
153{
154 pub graph: SimpleDirectedGraph<N>,
156 pub start_node: N,
158}
159
160impl<N> FlowGraph<N>
161where
162 N: Clone + Eq + Hash,
163{
164 pub fn new(graph: SimpleDirectedGraph<N>, start_node: N) -> Self {
166 Self { graph, start_node }
167 }
168}
169
170pub struct DominatorFinder<'a, N> {
173 node_to_id: HashMap<&'a N, InternalId>,
176 id_to_node: Vec<&'a N>,
178 immediate_dominators: Vec<InternalId>,
181}
182
183#[derive(Debug, Error, PartialEq)]
185pub enum DominatorFinderError {
186 #[error("The flow graph is invalid: some nodes are unreachable from the start node")]
188 UnreachableNodesInFlowGraph,
189 #[error("Target set must not be empty")]
191 EmptyTargetSet,
192 #[error("Target set contains a node which is not in the flow graph")]
194 UnknownNodeInTargetSet,
195}
196
197type InternalId = usize;
200
201impl<'a, N> DominatorFinder<'a, N>
202where
203 N: Clone + Eq + Hash,
204{
205 pub fn calculate(flow_graph: &'a FlowGraph<N>) -> Result<Self, DominatorFinderError> {
208 let postorder = flow_graph.graph.get_postorder(&flow_graph.start_node);
210 if postorder.len() != flow_graph.graph.num_nodes() {
211 return Err(DominatorFinderError::UnreachableNodesInFlowGraph);
212 }
213
214 let mut node_to_id = HashMap::new();
216 let mut id_to_node = Vec::new();
217 for (index, &node) in postorder.iter().enumerate() {
218 id_to_node.push(node);
219 node_to_id.insert(node, index);
220 }
221
222 let num_nodes = node_to_id.len();
224 let mut rev_adj = vec![vec![]; num_nodes];
225 for (u, v) in flow_graph.graph.edges() {
226 rev_adj[node_to_id[v]].push(node_to_id[u]);
227 }
228
229 let immediate_dominators = Self::calculate_immediate_dominators(&rev_adj);
232
233 Ok(Self {
234 node_to_id,
235 id_to_node,
236 immediate_dominators,
237 })
238 }
239
240 pub fn get_immediate_dominators(&self) -> HashMap<N, N> {
243 self.immediate_dominators
244 .iter()
245 .enumerate()
246 .map(|(index, &idom)| {
247 (
248 self.id_to_node[index].clone(),
249 self.id_to_node[idom].clone(),
250 )
251 })
252 .collect()
253 }
254
255 pub fn find_closest_common_dominator<NI>(
258 &self,
259 target_set: NI,
260 ) -> Result<N, DominatorFinderError>
261 where
262 NI: IntoIterator<Item = N>,
263 {
264 let target_ids: Vec<InternalId> = target_set
266 .into_iter()
267 .map(|node| match self.node_to_id.get(&node) {
268 Some(id) => Ok(*id),
269 None => Err(DominatorFinderError::UnknownNodeInTargetSet),
270 })
271 .try_collect()?;
272 if target_ids.is_empty() {
273 return Err(DominatorFinderError::EmptyTargetSet);
274 }
275
276 let closest_common_dominator =
279 Self::find_lowest_common_ancestor(&target_ids, &self.immediate_dominators);
280
281 Ok(self.id_to_node[closest_common_dominator].clone())
283 }
284
285 fn calculate_immediate_dominators(rev_adj: &[Vec<InternalId>]) -> Vec<InternalId> {
289 let num_nodes = rev_adj.len();
291 let start_node_id = num_nodes - 1;
292
293 let mut immediate_dominators: Vec<InternalId> = vec![usize::MAX; num_nodes];
300 immediate_dominators[start_node_id] = start_node_id;
305
306 loop {
307 let mut changed = false;
313
314 for u in (0..start_node_id).rev() {
316 let mut new_idom = usize::MAX;
317 let preds = &rev_adj[u];
319 for &p in preds {
320 if immediate_dominators[p] == usize::MAX {
321 continue;
323 }
324 if new_idom == usize::MAX {
325 new_idom = p;
329 } else {
330 new_idom = Self::intersect(new_idom, p, &immediate_dominators);
332 }
333 }
334 if new_idom == usize::MAX {
335 continue;
338 }
339 if immediate_dominators[u] != new_idom {
340 immediate_dominators[u] = new_idom;
342 changed = true;
343 }
344 }
345
346 if !changed {
347 break;
349 }
350 }
351
352 immediate_dominators
356 }
357
358 fn intersect(
360 mut b1: InternalId,
361 mut b2: InternalId,
362 immediate_dominators: &[InternalId],
363 ) -> InternalId {
364 while b1 != b2 {
365 while b1 < b2 {
366 b1 = immediate_dominators[b1];
367 }
368 while b2 < b1 {
369 b2 = immediate_dominators[b2];
370 }
371 }
372 b1
373 }
374
375 fn find_lowest_common_ancestor(
377 targets: &[InternalId],
378 immediate_dominators: &[InternalId],
379 ) -> InternalId {
380 targets
381 .iter()
382 .copied()
383 .reduce(|a, b| Self::intersect(a, b, immediate_dominators))
384 .expect("targets must not be empty")
385 }
386}
387
388fn post_order<T, NI>(
390 start_node: T,
391 mut neighbors_fn: impl FnMut(&T) -> NI,
392) -> impl Iterator<Item = T>
393where
394 T: Clone + Hash + Eq,
395 NI: DoubleEndedIterator<Item = T>,
396{
397 let mut stack = vec![(start_node, false)];
398 let mut visited: HashSet<T> = HashSet::new();
399 iter::from_fn(move || {
400 while let Some((node, processed)) = stack.pop() {
401 if processed {
402 return Some(node);
405 }
406 if !visited.insert(node.clone()) {
408 continue;
410 }
411 let neighbors = neighbors_fn(&node);
412 stack.push((node, true));
415 for neighbor in neighbors.rev() {
419 if !visited.contains(&neighbor) {
420 stack.push((neighbor, false));
421 }
422 }
423 }
424 None
425 })
426}
427
428#[cfg(test)]
429mod tests {
430 use maplit::hashmap;
431
432 use super::*;
433
434 #[test]
435 fn test_closest_common_dominator_split() -> Result<(), DominatorFinderError> {
436 let flow_graph = FlowGraph::new(
440 SimpleDirectedGraph::new([("A", "B"), ("A", "C"), ("B", "D"), ("C", "D")]),
441 "A",
442 );
443 let df = DominatorFinder::calculate(&flow_graph)?;
444 assert_eq!(df.find_closest_common_dominator(["A"])?, "A");
445 assert_eq!(df.find_closest_common_dominator(["B"])?, "B");
446 assert_eq!(df.find_closest_common_dominator(["C"])?, "C");
447 assert_eq!(df.find_closest_common_dominator(["D"])?, "D");
448 assert_eq!(df.find_closest_common_dominator(["B", "C"])?, "A");
449 assert_eq!(df.find_closest_common_dominator(["B", "D"])?, "A");
450 assert_eq!(df.find_closest_common_dominator(["B", "C", "D"])?, "A");
451 Ok(())
452 }
453
454 #[test]
455 fn test_closest_common_dominator_linear_chain() -> Result<(), DominatorFinderError> {
456 let flow_graph = FlowGraph::new(
458 SimpleDirectedGraph::new([("A", "B"), ("B", "C"), ("C", "D")]),
459 "A",
460 );
461 let df = DominatorFinder::calculate(&flow_graph)?;
462 assert_eq!(df.find_closest_common_dominator(["A"])?, "A");
463 assert_eq!(df.find_closest_common_dominator(["B"])?, "B");
464 assert_eq!(df.find_closest_common_dominator(["C"])?, "C");
465 assert_eq!(df.find_closest_common_dominator(["D"])?, "D");
466 assert_eq!(df.find_closest_common_dominator(["A", "B"])?, "A");
467 assert_eq!(df.find_closest_common_dominator(["A", "C"])?, "A");
468 assert_eq!(df.find_closest_common_dominator(["A", "D"])?, "A");
469 assert_eq!(df.find_closest_common_dominator(["B", "D"])?, "B");
470 assert_eq!(df.find_closest_common_dominator(["C", "D"])?, "C");
471 assert_eq!(df.find_closest_common_dominator(["A", "B", "C", "D"])?, "A");
472 Ok(())
473 }
474
475 #[test]
476 fn test_closest_common_dominator_classic_diamond() -> Result<(), DominatorFinderError> {
477 let flow_graph = FlowGraph::new(
481 SimpleDirectedGraph::new([("A", "B"), ("A", "C"), ("B", "D"), ("C", "D"), ("D", "E")]),
482 "A",
483 );
484 let df = DominatorFinder::calculate(&flow_graph)?;
485 assert_eq!(df.find_closest_common_dominator(["B", "C"])?, "A");
486 assert_eq!(df.find_closest_common_dominator(["B", "E"])?, "A");
487 assert_eq!(df.find_closest_common_dominator(["D"])?, "D");
488 assert_eq!(df.find_closest_common_dominator(["D", "E"])?, "D");
489 assert_eq!(df.find_closest_common_dominator(["A", "D"])?, "A");
490 Ok(())
491 }
492
493 #[test]
494 fn test_closest_common_dominator_single_node() -> Result<(), DominatorFinderError> {
495 let flow_graph = FlowGraph::new(SimpleDirectedGraph::new([("A", "A")]), "A");
497 let df = DominatorFinder::calculate(&flow_graph)?;
498 assert_eq!(df.find_closest_common_dominator(["A"])?, "A");
499 Ok(())
500 }
501
502 #[test]
503 fn test_invalid_flowgraph() {
504 let flow_graph = FlowGraph::new(
511 SimpleDirectedGraph::new([("A", "B"), ("B", "E"), ("B", "F"), ("C", "D"), ("D", "F")]),
512 "A",
513 );
514 assert_eq!(
515 DominatorFinder::calculate(&flow_graph).err(),
516 Some(DominatorFinderError::UnreachableNodesInFlowGraph)
517 );
518 }
519
520 #[test]
521 fn test_closest_common_dominator_simple_cycle_with_entry() -> Result<(), DominatorFinderError> {
522 let flow_graph = FlowGraph::new(
528 SimpleDirectedGraph::new([("A", "B"), ("B", "C"), ("C", "D"), ("D", "B")]),
529 "A",
530 );
531 let df = DominatorFinder::calculate(&flow_graph)?;
532 assert_eq!(df.find_closest_common_dominator(["A", "B"])?, "A");
533 assert_eq!(df.find_closest_common_dominator(["A", "C"])?, "A");
534 assert_eq!(df.find_closest_common_dominator(["A", "B", "C"])?, "A");
535 assert_eq!(df.find_closest_common_dominator(["B", "C"])?, "B");
536 assert_eq!(df.find_closest_common_dominator(["B", "C", "D"])?, "B");
537 assert_eq!(df.find_closest_common_dominator(["A"])?, "A");
538 assert_eq!(df.find_closest_common_dominator(["B"])?, "B");
539 assert_eq!(df.find_closest_common_dominator(["C"])?, "C");
540 assert_eq!(df.find_closest_common_dominator(["D"])?, "D");
541 Ok(())
542 }
543
544 #[test]
545 fn test_closest_common_dominator_figure_eight_with_bridge() -> Result<(), DominatorFinderError>
546 {
547 let flow_graph = FlowGraph::new(
553 SimpleDirectedGraph::new([
554 ("A", "B"), ("B", "C"),
556 ("C", "D"),
557 ("D", "B"), ("D", "E"), ("E", "F"),
560 ("F", "G"),
561 ("G", "E"), ]),
563 "A",
564 );
565 let df = DominatorFinder::calculate(&flow_graph)?;
566 assert_eq!(df.find_closest_common_dominator(["B", "C"])?, "B");
567 assert_eq!(df.find_closest_common_dominator(["B", "D"])?, "B");
568 assert_eq!(df.find_closest_common_dominator(["B", "E"])?, "B");
569 assert_eq!(df.find_closest_common_dominator(["C", "E"])?, "C");
570 assert_eq!(df.find_closest_common_dominator(["C", "F"])?, "C");
571 assert_eq!(df.find_closest_common_dominator(["D", "E"])?, "D");
572 assert_eq!(df.find_closest_common_dominator(["D", "F"])?, "D");
573 assert_eq!(df.find_closest_common_dominator(["E", "G"])?, "E");
574 assert_eq!(df.find_closest_common_dominator(["F", "G"])?, "F");
575 Ok(())
576 }
577
578 #[test]
579 fn test_closest_common_dominator_figure_eight() -> Result<(), DominatorFinderError> {
580 let flow_graph = FlowGraph::new(
586 SimpleDirectedGraph::new([
587 ("A", "B"), ("B", "C"),
589 ("C", "D"),
590 ("D", "B"), ("D", "E"),
592 ("E", "F"),
593 ("F", "D"), ]),
595 "A",
596 );
597 let df = DominatorFinder::calculate(&flow_graph)?;
598 assert_eq!(df.find_closest_common_dominator(["B", "C"])?, "B");
599 assert_eq!(df.find_closest_common_dominator(["B", "D"])?, "B");
600 assert_eq!(df.find_closest_common_dominator(["B", "E"])?, "B");
601 assert_eq!(df.find_closest_common_dominator(["C", "D"])?, "C");
602 assert_eq!(df.find_closest_common_dominator(["C", "E"])?, "C");
603 assert_eq!(df.find_closest_common_dominator(["C", "F"])?, "C");
604 assert_eq!(df.find_closest_common_dominator(["D", "E"])?, "D");
605 assert_eq!(df.find_closest_common_dominator(["D", "F"])?, "D");
606 assert_eq!(df.find_closest_common_dominator(["E", "F"])?, "E");
607 Ok(())
608 }
609
610 #[test]
611 fn test_closest_common_dominator_entry_cycle_dominance() -> Result<(), DominatorFinderError> {
612 let flow_graph = FlowGraph::new(
616 SimpleDirectedGraph::new([("A", "B"), ("B", "C"), ("C", "B")]),
617 "A",
618 );
619 let df = DominatorFinder::calculate(&flow_graph)?;
620 assert_eq!(df.find_closest_common_dominator(["A", "B"])?, "A");
621 assert_eq!(df.find_closest_common_dominator(["A", "C"])?, "A");
622 assert_eq!(df.find_closest_common_dominator(["B", "C"])?, "B");
623 assert_eq!(df.find_closest_common_dominator(["A", "B", "C"])?, "A");
624 Ok(())
625 }
626
627 #[test]
628 fn test_closest_common_dominator_nested_loops() -> Result<(), DominatorFinderError> {
629 let flow_graph = FlowGraph::new(
637 SimpleDirectedGraph::new([
638 ("A", "B"),
639 ("B", "C"),
640 ("C", "D"),
641 ("C", "E"),
642 ("E", "C"),
643 ("D", "B"),
644 ]),
645 "A",
646 );
647 let df = DominatorFinder::calculate(&flow_graph)?;
648 assert_eq!(df.find_closest_common_dominator(["A", "B"])?, "A");
649 assert_eq!(df.find_closest_common_dominator(["A", "C"])?, "A");
650 assert_eq!(df.find_closest_common_dominator(["B", "C"])?, "B");
651 assert_eq!(df.find_closest_common_dominator(["B", "D"])?, "B");
652 assert_eq!(df.find_closest_common_dominator(["B", "E"])?, "B");
653 assert_eq!(df.find_closest_common_dominator(["C", "D"])?, "C");
654 assert_eq!(df.find_closest_common_dominator(["C", "E"])?, "C");
655 assert_eq!(df.find_closest_common_dominator(["D", "E"])?, "C");
656 assert_eq!(df.find_closest_common_dominator(["B", "C", "D"])?, "B");
657 assert_eq!(df.find_closest_common_dominator(["B", "C", "E"])?, "B");
658 assert_eq!(df.find_closest_common_dominator(["B", "D", "E"])?, "B");
659 assert_eq!(df.find_closest_common_dominator(["C", "D", "E"])?, "C");
660 assert_eq!(df.find_closest_common_dominator(["B", "C", "D", "E"])?, "B");
661 Ok(())
662 }
663
664 #[test]
665 fn test_irreducible_graph_cooper_harvey_kennedy_fig2() -> Result<(), DominatorFinderError> {
666 let graph = SimpleDirectedGraph::new([(1, 2), (2, 1), (3, 2), (4, 1), (5, 4), (5, 3)]);
675 let flow_graph = FlowGraph::new(graph, 5);
676 let df = DominatorFinder::calculate(&flow_graph)?;
677 assert_eq!(
678 df.get_immediate_dominators(),
679 HashMap::from([(1, 5), (2, 5), (3, 5), (4, 5), (5, 5),])
680 );
681 Ok(())
682 }
683
684 #[test]
685 fn test_irreducible_graph_cooper_harvey_kennedy_fig3() -> Result<(), DominatorFinderError> {
686 let graph = SimpleDirectedGraph::new([
695 (1, 2),
696 (2, 1),
697 (2, 3),
698 (3, 2),
699 (5, 1),
700 (4, 2),
701 (4, 3),
702 (6, 5),
703 (6, 4),
704 ]);
705 let flow_graph = FlowGraph::new(graph, 6);
706 let df = DominatorFinder::calculate(&flow_graph)?;
707 assert_eq!(
708 df.get_immediate_dominators(),
709 HashMap::from([(1, 6), (2, 6), (3, 6), (4, 6), (5, 6), (6, 6),])
710 );
711 assert_eq!(df.find_closest_common_dominator([2, 3])?, 6);
712 Ok(())
713 }
714
715 #[test]
716 fn test_dominator_tree_with_three_levels() -> Result<(), DominatorFinderError> {
717 let graph =
732 SimpleDirectedGraph::new([(1, 2), (2, 3), (2, 4), (2, 6), (3, 5), (4, 5), (5, 2)]);
733 let flow_graph = FlowGraph::new(graph, 1);
734 let df = DominatorFinder::calculate(&flow_graph)?;
735 assert_eq!(
736 df.get_immediate_dominators(),
737 HashMap::from([(1, 1), (2, 1), (3, 2), (4, 2), (5, 2), (6, 2),])
738 );
739 assert_eq!(df.find_closest_common_dominator([1, 6])?, 1);
740 assert_eq!(df.find_closest_common_dominator([2, 3])?, 2);
741 assert_eq!(df.find_closest_common_dominator([2, 4])?, 2);
742 assert_eq!(df.find_closest_common_dominator([2, 5])?, 2);
743 assert_eq!(df.find_closest_common_dominator([2, 6])?, 2);
744 assert_eq!(df.find_closest_common_dominator([3, 4])?, 2);
745 assert_eq!(df.find_closest_common_dominator([3, 5])?, 2);
746 assert_eq!(df.find_closest_common_dominator([3, 6])?, 2);
747 assert_eq!(df.find_closest_common_dominator([4, 5])?, 2);
748 assert_eq!(df.find_closest_common_dominator([4, 6])?, 2);
749 assert_eq!(df.find_closest_common_dominator([5, 6])?, 2);
750 assert_eq!(df.find_closest_common_dominator([2, 3, 5])?, 2);
751 assert_eq!(df.find_closest_common_dominator([3, 4, 5])?, 2);
752 assert_eq!(df.find_closest_common_dominator([3, 4, 5, 6])?, 2);
753 Ok(())
754 }
755
756 #[test]
757 fn test_big_graph_fig_18_3() -> Result<(), DominatorFinderError> {
758 let graph = SimpleDirectedGraph::new([
784 (1, 2),
785 (2, 3),
786 (2, 4),
787 (3, 2),
788 (4, 2),
789 (4, 5),
790 (4, 6),
791 (5, 7),
792 (5, 8),
793 (6, 7),
794 (7, 11),
795 (8, 9),
796 (9, 8),
797 (9, 10),
798 (10, 5),
799 (10, 12),
800 (11, 12),
801 ]);
802 let flow_graph = FlowGraph::new(graph, 1);
803 let df = DominatorFinder::calculate(&flow_graph)?;
804 assert_eq!(
805 df.get_immediate_dominators(),
806 HashMap::from([
807 (1, 1),
808 (2, 1),
809 (3, 2),
810 (4, 2),
811 (5, 4),
812 (6, 4),
813 (7, 4),
814 (8, 5),
815 (9, 8),
816 (10, 9),
817 (11, 7),
818 (12, 4)
819 ])
820 );
821 assert_eq!(df.find_closest_common_dominator([6, 3])?, 2);
822 assert_eq!(df.find_closest_common_dominator([11, 9, 12])?, 4);
823 assert_eq!(df.find_closest_common_dominator([11, 9, 5])?, 4);
824 assert_eq!(df.find_closest_common_dominator([11, 10])?, 4);
825 assert_eq!(df.find_closest_common_dominator([10, 11, 12, 3, 6])?, 2);
826 Ok(())
827 }
828
829 #[test]
830 fn test_big_graph_fig_19_8() -> Result<(), DominatorFinderError> {
831 let graph = SimpleDirectedGraph::new([
858 ("A", "B"),
859 ("A", "C"),
860 ("B", "D"),
861 ("B", "G"),
862 ("C", "E"),
863 ("C", "H"),
864 ("D", "F"),
865 ("D", "G"),
866 ("E", "C"),
867 ("E", "H"),
868 ("F", "I"),
869 ("F", "K"),
870 ("G", "J"),
871 ("H", "M"),
872 ("I", "L"),
873 ("J", "I"),
874 ("K", "L"),
875 ("L", "B"),
876 ("L", "M"),
877 ]);
878 let flow_graph = FlowGraph::new(graph, "A");
879 let df = DominatorFinder::calculate(&flow_graph)?;
880 assert_eq!(
881 df.get_immediate_dominators(),
882 HashMap::from([
883 ("A", "A"),
884 ("B", "A"),
885 ("C", "A"),
886 ("D", "B"),
887 ("E", "C"),
888 ("F", "D"),
889 ("G", "B"),
890 ("H", "C"),
891 ("I", "B"),
892 ("J", "G"),
893 ("K", "F"),
894 ("L", "B"),
895 ("M", "A"),
896 ])
897 );
898 assert_eq!(df.find_closest_common_dominator(["K", "L"])?, "B");
899 assert_eq!(df.find_closest_common_dominator(["K", "C"])?, "A");
900 assert_eq!(df.find_closest_common_dominator(["B", "G", "J"])?, "B");
901 Ok(())
902 }
903
904 #[test]
905 fn test_closest_common_dominator_tree() -> Result<(), DominatorFinderError> {
906 let flow_graph = FlowGraph::new(
910 SimpleDirectedGraph::new([("A", "B"), ("B", "C"), ("B", "D"), ("A", "E")]),
911 "A",
912 );
913 let df = DominatorFinder::calculate(&flow_graph)?;
914 assert_eq!(df.find_closest_common_dominator(["B", "C"])?, "B");
915 assert_eq!(df.find_closest_common_dominator(["B", "E"])?, "A");
916 assert_eq!(df.find_closest_common_dominator(["C", "D"])?, "B");
917 assert_eq!(df.find_closest_common_dominator(["C", "E"])?, "A");
918 assert_eq!(df.find_closest_common_dominator(["B", "C", "D"])?, "B");
919 assert_eq!(df.find_closest_common_dominator(["C", "D", "E"])?, "A");
920 Ok(())
921 }
922
923 #[test]
924 fn test_closest_common_dominator_bypassing_path() -> Result<(), DominatorFinderError> {
925 let flow_graph = FlowGraph::new(
930 SimpleDirectedGraph::new([("A", "B"), ("B", "C"), ("C", "D"), ("A", "E"), ("E", "D")]),
931 "A",
932 );
933 let df = DominatorFinder::calculate(&flow_graph)?;
934 assert_eq!(df.find_closest_common_dominator(["B", "C"])?, "B");
935 assert_eq!(df.find_closest_common_dominator(["B", "D"])?, "A");
936 assert_eq!(df.find_closest_common_dominator(["B", "E"])?, "A");
937 assert_eq!(df.find_closest_common_dominator(["C", "D"])?, "A");
938 assert_eq!(df.find_closest_common_dominator(["C", "E"])?, "A");
939 assert_eq!(df.find_closest_common_dominator(["D", "E"])?, "A");
940 assert_eq!(df.find_closest_common_dominator(["B", "C", "D"])?, "A");
941 assert_eq!(df.find_closest_common_dominator(["C", "D", "E"])?, "A");
942 Ok(())
943 }
944
945 #[test]
946 fn test_closest_common_dominator_self_loop_handling() -> Result<(), DominatorFinderError> {
947 let flow_graph = FlowGraph::new(SimpleDirectedGraph::new([("A", "A"), ("A", "B")]), "A");
949 let df = DominatorFinder::calculate(&flow_graph)?;
950 assert_eq!(df.find_closest_common_dominator(["A"])?, "A");
951 Ok(())
952 }
953
954 #[test]
955 fn test_closest_common_dominator_multi_edge() -> Result<(), DominatorFinderError> {
956 let flow_graph = FlowGraph::new(
958 SimpleDirectedGraph::new([
959 ("A", "B"),
960 ("A", "B"), ("B", "C"),
962 ]),
963 "A",
964 );
965 let df = DominatorFinder::calculate(&flow_graph)?;
966 assert_eq!(df.find_closest_common_dominator(["A"])?, "A");
967 assert_eq!(df.find_closest_common_dominator(["B", "C"])?, "B");
968 Ok(())
969 }
970
971 #[test]
972 fn test_closest_common_dominator_invalid_target_set() -> Result<(), DominatorFinderError> {
973 let flow_graph = FlowGraph::new(SimpleDirectedGraph::new([("A", "B")]), "A");
975 let df = DominatorFinder::calculate(&flow_graph)?;
976 assert_eq!(
977 df.find_closest_common_dominator([]),
978 Err(DominatorFinderError::EmptyTargetSet)
979 );
980 Ok(())
981 }
982
983 #[test]
984 fn test_closest_common_dominator_repeated_node() -> Result<(), DominatorFinderError> {
985 let flow_graph = FlowGraph::new(SimpleDirectedGraph::new([("A", "B")]), "A");
987 let df = DominatorFinder::calculate(&flow_graph)?;
988 assert_eq!(df.find_closest_common_dominator(["A", "B", "A", "B"])?, "A");
989 Ok(())
990 }
991
992 #[test]
993 fn test_simple_directed_graph_nodes() {
994 let graph = SimpleDirectedGraph::new([("A", "B"), ("B", "C")]);
995 let nodes = graph.nodes().copied().collect_vec();
996 assert_eq!(nodes, ["A", "B", "C"]);
997
998 let graph = SimpleDirectedGraph::<String>::new([]);
999 let nodes = graph.nodes().cloned().collect_vec();
1000 assert!(nodes.is_empty());
1001 }
1002
1003 #[test]
1004 fn test_simple_directed_graph_edges() {
1005 let graph = SimpleDirectedGraph::new([("A", "B"), ("B", "C"), ("A", "C")]);
1006 let edges = graph.edges().map(|(&u, &v)| (u, v)).collect_vec();
1007 assert_eq!(edges, [("A", "B"), ("A", "C"), ("B", "C")]);
1008
1009 let graph = SimpleDirectedGraph::<String>::new([]);
1010 let edges = graph.edges().collect_vec();
1011 assert!(edges.is_empty());
1012 }
1013
1014 #[test]
1015 fn test_simple_directed_graph_adjacent_nodes() {
1016 let graph = SimpleDirectedGraph::new([("A", "B"), ("A", "C"), ("B", "D")]);
1017 assert_eq!(
1018 graph.adjacent_nodes(&"A").unwrap().copied().collect_vec(),
1019 ["B", "C"]
1020 );
1021 assert_eq!(
1022 graph.adjacent_nodes(&"B").unwrap().copied().collect_vec(),
1023 ["D"]
1024 );
1025 assert!(graph.adjacent_nodes(&"C").unwrap().next().is_none());
1026 assert!(graph.adjacent_nodes(&"Z").is_none());
1027 }
1028
1029 #[test]
1030 fn test_simple_directed_graph_contains_node() {
1031 let graph = SimpleDirectedGraph::new([("A", "B"), ("B", "C")]);
1032 assert!(graph.contains_node(&"A"));
1033 assert!(graph.contains_node(&"B"));
1034 assert!(graph.contains_node(&"C"));
1035 assert!(!graph.contains_node(&"D"));
1036 }
1037
1038 #[test]
1039 fn test_simple_directed_graph_new() {
1040 let graph = SimpleDirectedGraph::new([("A", "B"), ("A", "C"), ("B", "C"), ("A", "B")]);
1041 let nodes = graph.nodes().copied().collect_vec();
1042 assert_eq!(nodes, ["A", "B", "C"]);
1043 let edges = graph.edges().map(|(&u, &v)| (u, v)).collect_vec();
1044 assert_eq!(edges, [("A", "B"), ("A", "C"), ("B", "C")]);
1045
1046 let graph = SimpleDirectedGraph::new([("B", "C"), ("A", "B")]);
1047 let nodes = graph.nodes().copied().collect_vec();
1048 assert_eq!(nodes, ["B", "C", "A"]);
1049 let edges = graph.edges().map(|(&u, &v)| (u, v)).collect_vec();
1050 assert_eq!(edges, [("B", "C"), ("A", "B")]);
1051 }
1052
1053 #[test]
1054 fn test_flow_graph_new() {
1055 let graph = SimpleDirectedGraph::new([("A", "B")]);
1056 let flow_graph = FlowGraph::new(graph.clone(), "A");
1057 assert_eq!(flow_graph.graph, graph);
1058 assert_eq!(flow_graph.start_node, "A");
1059 let flow_graph = FlowGraph::new(graph.clone(), "C");
1060 assert_eq!(flow_graph.graph, graph);
1061 assert_eq!(flow_graph.start_node, "C");
1062 }
1063
1064 #[test]
1065 fn test_post_order() {
1066 let neighbors = hashmap! {
1077 'A' => vec![],
1078 'B' => vec!['A'],
1079 'C' => vec!['B'],
1080 'D' => vec!['C'],
1081 'E' => vec!['A'],
1082 'F' => vec!['E', 'D'],
1083 };
1084 let neighbors_fn = |node: &char| neighbors[node].iter().copied();
1085 assert_eq!(
1086 post_order('F', neighbors_fn).collect_vec(),
1087 ['A', 'E', 'B', 'C', 'D', 'F']
1088 );
1089 assert_eq!(post_order('E', neighbors_fn).collect_vec(), ['A', 'E']);
1090 assert_eq!(
1091 post_order('D', neighbors_fn).collect_vec(),
1092 ['A', 'B', 'C', 'D']
1093 );
1094 assert_eq!(post_order('A', neighbors_fn).collect_vec(), ['A']);
1095
1096 let neighbors = hashmap! {
1111 'A' => vec![],
1112 'B' => vec!['A'],
1113 'C' => vec!['A'],
1114 'D' => vec!['B'],
1115 'E' => vec!['C'],
1116 'F' => vec!['C'],
1117 'G' => vec!['E'],
1118 'H' => vec!['F', 'G'],
1119 'I' => vec!['D', 'H'],
1120 };
1121 let neighbors_fn = |node: &char| neighbors[node].iter().copied();
1122 assert_eq!(
1123 post_order('I', neighbors_fn).collect_vec(),
1124 ['A', 'B', 'D', 'C', 'F', 'E', 'G', 'H', 'I']
1125 );
1126
1127 let neighbors = hashmap! {
1144 'A' => vec![],
1145 'b' => vec!['A'],
1146 'C' => vec!['A'],
1147 'D' => vec!['b'],
1148 'e' => vec!['C', 'b'],
1149 'f' => vec!['D'],
1150 'G' => vec!['e', 'D'],
1151 'h' => vec!['G', 'f'],
1152 'I' => vec!['C', 'e', 'G', 'h'],
1153 };
1154 let neighbors_fn = |node: &char| neighbors[node].iter().copied();
1155 assert_eq!(
1156 post_order('I', neighbors_fn).collect_vec(),
1157 ['A', 'C', 'b', 'e', 'D', 'G', 'f', 'h', 'I']
1158 );
1159
1160 let neighbors = hashmap! {
1172 'A' => vec![],
1173 'B' => vec!['A'],
1174 'C' => vec!['B'],
1175 'D' => vec!['C'],
1176 'E' => vec!['C'],
1177 'F' => vec!['D'],
1178 'G' => vec!['E', 'F'],
1179 };
1180 let neighbors_fn = |node: &char| neighbors[node].iter().copied();
1181 assert_eq!(
1182 post_order('G', neighbors_fn).collect_vec(),
1183 ['A', 'B', 'C', 'E', 'D', 'F', 'G']
1184 );
1185
1186 let neighbors = hashmap! {
1198 'A' => vec![],
1199 'B' => vec!['A'],
1200 'c' => vec!['B'],
1201 'D' => vec!['c'],
1202 'E' => vec!['c'],
1203 'F' => vec!['E'],
1204 'G' => vec!['F', 'D'],
1205 };
1206 let neighbors_fn = |node: &char| neighbors[node].iter().copied();
1207 assert_eq!(
1208 post_order('G', neighbors_fn).collect_vec(),
1209 ['A', 'B', 'c', 'E', 'F', 'D', 'G']
1210 );
1211
1212 let neighbors = hashmap! {
1225 'A' => vec![],
1226 'B' => vec!['A'],
1227 'C' => vec!['B'],
1228 'D' => vec!['A'],
1229 'E' => vec!['A'],
1230 'F' => vec!['E', 'D'],
1231 };
1232 let neighbors_fn = |node: &char| neighbors[node].iter().copied();
1233 assert_eq!(
1234 post_order('F', neighbors_fn).collect_vec(),
1235 ['A', 'E', 'D', 'F']
1236 );
1237 assert_eq!(post_order('C', neighbors_fn).collect_vec(), ['A', 'B', 'C']);
1238
1239 let neighbors = hashmap! {
1247 'A' => vec![],
1248 'B' => vec!['A'],
1249 'C' => vec![],
1250 'D' => vec!['C', 'B'],
1251 };
1252 let neighbors_fn = |node: &char| neighbors[node].iter().copied();
1253 assert_eq!(
1254 post_order('D', neighbors_fn).collect_vec(),
1255 ['C', 'A', 'B', 'D']
1256 );
1257
1258 let neighbors = hashmap! {
1264 'A' => vec!['C'],
1265 'B' => vec!['A'],
1266 'C' => vec!['B'],
1267 };
1268 let neighbors_fn = |node: &char| neighbors[node].iter().copied();
1269 assert_eq!(post_order('C', neighbors_fn).collect_vec(), ['A', 'B', 'C']);
1270 assert_eq!(post_order('B', neighbors_fn).collect_vec(), ['C', 'A', 'B']);
1271 assert_eq!(post_order('A', neighbors_fn).collect_vec(), ['B', 'C', 'A']);
1272 }
1273}