Skip to main content

jj_lib/
graph_dominators.rs

1// Copyright 2026 The Jujutsu Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Generic implementation of the "closest common dominator" algorithm for
16//! directed graphs.
17//!
18//! Generic implementation of the Common Dominator algorithm for directed
19//! graphs, using the Cooper-Harvey-Kennedy iterative algorithm. Loosely
20//! speaking the algorithm finds the "choke point" for a set of nodes S in a
21//! directed graph (going from the "entry" node to nodes in S), closest to S.
22//!
23//! Dominance:
24//!
25//! * A flow graph is a directed graph with a designated entry node.
26//! * A node z is said to dominate a node n if all paths from the entry node to
27//!   n must go through z. Every node dominates itself, and the entry node
28//!   dominates all nodes.
29//! * A node can have one or more dominators.
30//! * A node z strictly dominates n if z dominates n and z != n.
31//! * The immediate dominator of a node n is the dominator of n that doesn't
32//!   strictly dominate any other strict dominators of n. Informally it is the
33//!   "closest" choke point on all paths from the entry node to n.
34//! * Let S be a subset of the nodes in the graph. The intersection of the
35//!   dominators of each node in S is the set of common dominators of S.
36//! * The closest common dominator of S is the common dominator of S that
37//!   doesn't strictly dominate any other common dominator of S. Informally, it
38//!   is the choke point closest to S such that all paths from the entry node to
39//!   S must go through it.
40//!
41//! Dominator Tree:
42//!
43//! For any flow graph G there is a corresponding dominator tree defined as
44//! follows:
45//! * The nodes of the dominator tree are the same as the nodes of G
46//! * The root of the dominator tree is the entry node of G
47//! * In the dominator tree, the children of a node are the nodes it immediately
48//!   dominates
49//!
50//! The closest common dominator of S is the Lowest Common Ancestor (LCA)
51//! of S in the graph's dominator tree.
52//!
53//! This implementation constructs the Dominator Tree by first determining
54//! the Immediate Dominator for every node (using the standard iterative
55//! algorithm), and then calculating the LCA for the set S. See:
56//!
57//! * <http://www.hipersoft.rice.edu/grads/publications/dom14.pdf>
58//! * <https://en.wikipedia.org/wiki/Dominator_(graph_theory)>
59//!
60//! The running time is O(V+E+|S|*V)in the worst case, the space complexity is
61//! O(V+E), where V is the number of nodes and E is the number of edges. In
62//! practice the algorithm is fast and efficient for typical use cases because
63//! the number of nodes that dominate any given node is typically small, and
64//! the dominator tree is typically shallow.
65
66use std::collections::HashMap;
67use std::collections::HashSet;
68use std::hash::Hash;
69use std::iter;
70use std::rc::Rc;
71
72use futures::future::try_join_all;
73use indexmap::IndexMap;
74use indexmap::IndexSet;
75use itertools::Itertools as _;
76use thiserror::Error;
77
78/// An immutable directed graph with nodes of type N and a minimal interface for
79/// iterating over nodes and their adjacent nodes.
80#[derive(Clone, Eq, PartialEq, Debug)]
81pub struct SimpleDirectedGraph<N>
82where
83    N: Clone + Eq + Hash,
84{
85    /// The adjacency map of the graph. Each key is a node, and the
86    /// corresponding value is the set of adjacent nodes (i.e., the children of
87    /// the key node). The adjacency map is in canonical form: for every
88    /// u->v edge, there is an entry in adj with key v (even if v has no
89    /// outgoing edges).
90    adj: IndexMap<N, IndexSet<N>>,
91}
92
93impl<N> SimpleDirectedGraph<N>
94where
95    N: Clone + Eq + Hash,
96{
97    /// Constructs a new SimpleDirectedGraph from a list of edges.
98    pub fn new<EI>(edges: EI) -> Self
99    where
100        EI: IntoIterator<Item = (N, N)>,
101    {
102        let mut adj: IndexMap<N, IndexSet<N>> = IndexMap::new();
103        for (parent, child) in edges {
104            adj.entry(parent).or_default().insert(child.clone());
105            adj.entry(child).or_default();
106        }
107        Self { adj }
108    }
109
110    /// Returns the nodes in this graph.
111    pub fn nodes(&self) -> impl Iterator<Item = &N> {
112        self.adj.keys()
113    }
114
115    /// Returns the nodes in this graph.
116    pub fn num_nodes(&self) -> usize {
117        self.adj.len()
118    }
119
120    /// Returns the edges in this graph.
121    pub fn edges(&self) -> impl Iterator<Item = (&N, &N)> {
122        self.adj
123            .iter()
124            .flat_map(|(parent, adj_set)| adj_set.iter().map(move |child| (parent, child)))
125    }
126
127    /// Returns the adjacent nodes for the given node, or None if the node is
128    /// not in the graph.
129    pub fn adjacent_nodes(&self, node: &N) -> Option<impl DoubleEndedIterator<Item = &N>> {
130        self.adj.get(node).map(|adj_set| adj_set.iter())
131    }
132
133    /// Returns true if this graph contains the given node.
134    pub fn contains_node(&self, node: &N) -> bool {
135        self.adj.contains_key(node)
136    }
137
138    /// Returns a postorder traversal of the nodes in this graph starting from
139    /// the given node.
140    pub fn get_postorder<'a>(&'a self, start_node: &'a N) -> Vec<&'a N> {
141        post_order(start_node, |&u| self.adjacent_nodes(u).unwrap()).collect_vec()
142    }
143}
144
145/// A FlowGraph is a directed graph with a designated start node.
146///
147/// Any node in the graph can be the start node. There are no reachability
148/// requirements whatsoever: some nodes may be unreachable from the start node,
149/// the start node could have incoming edges, the graph could be disconnected,
150/// etc.
151#[derive(Clone, Eq, PartialEq, Debug)]
152pub struct FlowGraph<N>
153where
154    N: Clone + Eq + Hash,
155{
156    /// The graph.
157    pub graph: SimpleDirectedGraph<N>,
158    /// The start node.
159    pub start_node: N,
160}
161
162/// Calculates the dominators in a flow graph. Also has a method for finding the
163/// closest common dominator of a set of nodes.
164pub struct DominatorFinder<'a, N> {
165    /// Map from nodes to integers in [0, N-1] range, in postorder (the start
166    /// node has index N-1).
167    node_to_id: HashMap<&'a N, InternalId>,
168    /// The inverse of node_to_id.
169    id_to_node: Vec<&'a N>,
170    /// The immediate dominator for each node (by index). NOTE: the immediate
171    /// dominator of the start node is itself.
172    immediate_dominators: Vec<InternalId>,
173}
174
175/// Errors that can occur while finding dominators.
176#[derive(Debug, Error, PartialEq)]
177pub enum DominatorFinderError {
178    /// The flow graph is invalid.
179    #[error("The flow graph is invalid: some nodes are unreachable from the start node")]
180    UnreachableNodesInFlowGraph,
181    /// The target set is empty.
182    #[error("Target set must not be empty")]
183    EmptyTargetSet,
184    /// The target set is invalid.
185    #[error("Target set contains a node which is not in the flow graph")]
186    UnknownNodeInTargetSet,
187}
188
189/// The dominator algorithm assigns consecutive numeric IDs to nodes, for
190/// efficiency reasons. We use this type alias for clarity.
191type InternalId = usize;
192
193impl<'a, N> DominatorFinder<'a, N>
194where
195    N: Clone + Eq + Hash,
196{
197    /// Constructs a new DominatorFinder. Returns an error if the flow graph is
198    /// invalid: e.g. if some node is unreachable from the start node.
199    pub fn calculate(flow_graph: &'a FlowGraph<N>) -> Result<Self, DominatorFinderError> {
200        // Get postorder traversal of the graph starting from the start node.
201        let postorder = flow_graph.graph.get_postorder(&flow_graph.start_node);
202        if postorder.len() != flow_graph.graph.num_nodes() {
203            return Err(DominatorFinderError::UnreachableNodesInFlowGraph);
204        }
205
206        // Map generic types to integer IDs
207        let mut node_to_id = HashMap::new();
208        let mut id_to_node = Vec::new();
209        for (index, &node) in postorder.iter().enumerate() {
210            id_to_node.push(node);
211            node_to_id.insert(node, index);
212        }
213
214        // Build graph using internal IDs.
215        let num_nodes = node_to_id.len();
216        let mut rev_adj = vec![vec![]; num_nodes];
217        for (u, v) in flow_graph.graph.edges() {
218            rev_adj[node_to_id[v]].push(node_to_id[u]);
219        }
220
221        // Find the immediate dominators for each node using the Cooper-Harvey-Kennedy
222        // iterative algorithm.
223        let immediate_dominators = Self::calculate_immediate_dominators(&rev_adj);
224
225        Ok(Self {
226            node_to_id,
227            id_to_node,
228            immediate_dominators,
229        })
230    }
231
232    /// Returns a map from each node to its immediate dominator. NOTE: the
233    /// immediate dominator of the start node is itself.
234    pub fn get_immediate_dominators(&self) -> HashMap<N, N> {
235        self.immediate_dominators
236            .iter()
237            .enumerate()
238            .map(|(index, &idom)| {
239                (
240                    self.id_to_node[index].clone(),
241                    self.id_to_node[idom].clone(),
242                )
243            })
244            .collect()
245    }
246
247    /// Finds the closest common dominator for the given flow graph and set of
248    /// nodes S (target_set).
249    pub fn find_closest_common_dominator<NI>(
250        &self,
251        target_set: NI,
252    ) -> Result<N, DominatorFinderError>
253    where
254        NI: IntoIterator<Item = N>,
255    {
256        // Convert generic target_set to internal IDs
257        let target_ids: Vec<InternalId> = target_set
258            .into_iter()
259            .map(|node| match self.node_to_id.get(&node) {
260                Some(id) => Ok(*id),
261                None => Err(DominatorFinderError::UnknownNodeInTargetSet),
262            })
263            .try_collect()?;
264        if target_ids.is_empty() {
265            return Err(DominatorFinderError::EmptyTargetSet);
266        }
267
268        // The closest common dominator of a set of nodes is the lowest common ancestor
269        // of those nodes in the dominator tree.
270        let closest_common_dominator =
271            Self::find_lowest_common_ancestor(&target_ids, &self.immediate_dominators);
272
273        // Map the internal ID back to generic type N.
274        Ok(self.id_to_node[closest_common_dominator].clone())
275    }
276
277    // Applies the Cooper-Harvey-Kennedy iterative algorithm to find the immediate
278    // dominators for each node in the graph.
279    // See http://www.hipersoft.rice.edu/grads/publications/dom14.pdf for details on how this function works.
280    fn calculate_immediate_dominators(rev_adj: &[Vec<InternalId>]) -> Vec<InternalId> {
281        // Step 1: Compute Dominators on Reverse Graph
282        let num_nodes = rev_adj.len();
283        let start_node_id = num_nodes - 1;
284
285        // We hold the immediate dominator for each node in the following vector, in
286        // index position (the kth entry is the immediate dominator of the node with ID
287        // k). We initialize the immediate dominator of every node to usize::MAX to
288        // represent that those nodes are not processed yet. Once a node is
289        // processed, its immediate dominator is guaranteed to be a valid node
290        // index.
291        let mut immediate_dominators: Vec<InternalId> = vec![usize::MAX; num_nodes];
292        // NOTE: technically speaking the immediate dominator is NOT defined for the
293        // start node, but it is convenient for the algorithm to set it to itself; this
294        // is consistent with the literature and specifically with
295        // Cooper-Harvey-Kennedy.
296        immediate_dominators[start_node_id] = start_node_id;
297
298        loop {
299            // Each iteration of the loop processes all nodes in reverse postorder, trying
300            // to improve the immediate dominator for each node. The loop continues until we
301            // have an iteration where no immediate dominator is changed. Note that the
302            // entries in immediate_dominators are only guaranteed to be correct when the
303            // loop terminates.
304            let mut changed = false;
305
306            // Iterate in reverse postorder, skipping the start node.
307            for u in (0..start_node_id).rev() {
308                let mut new_idom = usize::MAX;
309                // Process predecessors (nodes that flow INTO u).
310                let preds = &rev_adj[u];
311                for &p in preds {
312                    if immediate_dominators[p] == usize::MAX {
313                        // Skip predecessors that have not been processed yet.
314                        continue;
315                    }
316                    if new_idom == usize::MAX {
317                        // This is the first predecessor of u that has been processed so far. We use
318                        // it as the starting point for finding the new "improved" immediate
319                        // dominator for u.
320                        new_idom = p;
321                    } else {
322                        // "Intersect" the current new_idom with p's idom.
323                        new_idom = Self::intersect(new_idom, p, &immediate_dominators);
324                    }
325                }
326                if new_idom == usize::MAX {
327                    // None of the predecessors of u have been processed yet. That's fine, we will
328                    // try again of the next iteration of the outer loop.
329                    continue;
330                }
331                if immediate_dominators[u] != new_idom {
332                    // We "improved" the immediate dominator for u!
333                    immediate_dominators[u] = new_idom;
334                    changed = true;
335                }
336            }
337
338            if !changed {
339                // We reached the fixed point. We are done.
340                break;
341            }
342        }
343
344        // At this point we know the immediate dominator of every node, but we keep the
345        // Option wrapper so that we can use the intersect function during
346        // find_lowest_common_ancestor.
347        immediate_dominators
348    }
349
350    // See http://www.hipersoft.rice.edu/grads/publications/dom14.pdf for details on how this function works.
351    fn intersect(
352        mut b1: InternalId,
353        mut b2: InternalId,
354        immediate_dominators: &[InternalId],
355    ) -> InternalId {
356        while b1 != b2 {
357            while b1 < b2 {
358                b1 = immediate_dominators[b1];
359            }
360            while b2 < b1 {
361                b2 = immediate_dominators[b2];
362            }
363        }
364        b1
365    }
366
367    // See http://www.hipersoft.rice.edu/grads/publications/dom14.pdf for details on how this function works.
368    fn find_lowest_common_ancestor(
369        targets: &[InternalId],
370        immediate_dominators: &[InternalId],
371    ) -> InternalId {
372        targets
373            .iter()
374            .copied()
375            .reduce(|a, b| Self::intersect(a, b, immediate_dominators))
376            .expect("targets must not be empty")
377    }
378}
379
380/// Errors that can occur while finding a dominator value (i.e. a dominator in a
381/// value flow graph).
382#[derive(Debug, Error, PartialEq)]
383pub enum FindDominatorValueError<E> {
384    /// An error occurred while computing the value of a node.
385    #[error(transparent)]
386    ValueFnError(E),
387    /// An error occurred while finding the dominator.
388    #[error(transparent)]
389    DominatorFinderError(#[from] DominatorFinderError),
390}
391
392/// Helper struct for constructing a value flow graph. It caches the results
393/// of applying value_fn to nodes, and also keeps track of the mapping from
394/// values to nodes and nodes to values.
395pub struct ValueCache<N, V, VF> {
396    /// The function that emits values.
397    value_fn: VF,
398    /// Maps nodes to their corresponding values.
399    node_values: HashMap<N, Rc<V>>,
400    /// Maps values to the nodes that have that value.
401    value_to_nodes: HashMap<Rc<V>, Vec<N>>,
402}
403
404impl<N, V, VF, E> ValueCache<N, V, VF>
405where
406    N: Hash + Eq + Clone,
407    V: Hash + Eq,
408    VF: AsyncFn(&N) -> Result<V, E>,
409{
410    /// Creates a new ValueCache that uses the given function to get values.
411    pub fn new(value_fn: VF) -> Self {
412        Self {
413            value_fn,
414            node_values: HashMap::new(),
415            value_to_nodes: HashMap::new(),
416        }
417    }
418
419    /// Returns the value for the given node, computing it if it is not already
420    /// cached.
421    pub async fn get_value(&mut self, node: &N) -> Result<Rc<V>, E> {
422        let values = self.get_values([node]).await?;
423        assert_eq!(values.len(), 1);
424        Ok(values[0].clone())
425    }
426
427    /// Returns the nodes that have the given value.
428    pub fn get_nodes_for_value(&self, value: &Rc<V>) -> Option<&Vec<N>> {
429        self.value_to_nodes.get(value)
430    }
431
432    /// Returns the value of the given nodes, computing them (if they are not
433    /// already cached) asynchronously and concurrently. The result will have
434    /// the same cardinality as `nodes`, and may contain duplicate values
435    /// (e.g. if multiple nodes have the same value, or if
436    /// there are repeated nodes). The order of the result is not specified
437    /// (any order is possible).
438    async fn get_values<'a, NI>(&mut self, nodes: NI) -> Result<Vec<Rc<V>>, E>
439    where
440        N: 'a,
441        NI: IntoIterator<Item = &'a N>,
442    {
443        let mut values = vec![];
444        let mut futures = vec![];
445
446        // 1. Filter out nodes already in the map and create futures for new nodes.
447        for node in nodes {
448            match self.node_values.get(node) {
449                Some(value) => {
450                    values.push(value.clone());
451                }
452                None => {
453                    // This node is not cached, we will compute its value.
454                    futures.push(async {
455                        let value = (self.value_fn)(node).await?;
456                        Ok((node.clone(), Rc::new(value)))
457                    });
458                }
459            }
460        }
461        // 2. Run all new futures concurrently
462        let new_results: Vec<(N, Rc<V>)> = try_join_all(futures).await?;
463        // 3. Insert the new entries into the maps.
464        for (node, value) in new_results {
465            self.node_values.insert(node.clone(), value.clone());
466            self.value_to_nodes
467                .entry(value.clone())
468                .or_default()
469                .push(node);
470            values.push(value);
471        }
472        Ok(values)
473    }
474}
475
476impl<N> FlowGraph<N>
477where
478    N: Clone + Eq + Hash,
479{
480    /// Constructs a new FlowGraph.
481    pub fn new(graph: SimpleDirectedGraph<N>, start_node: N) -> Self {
482        Self { graph, start_node }
483    }
484
485    /// Creates a flow graph of values from a flow graph of nodes.
486    ///
487    /// More precisely, let G be a FlowGraph of nodes with start node S. The
488    /// value flow graph G' is a FlowGraph derived from G. Let v(g) be the
489    /// result of applying value_fn to g. The nodes of G' are the set of values
490    /// v(g), for all g in G. For each edge g1->g2 in G, there is a
491    /// corresponding edge v(g1)->v(g2) in G'. The start node in G' is v(S).
492    ///
493    /// Returns an error if any value_fn invocation fails.
494    pub fn create_value_flow_graph<'a, V>(&self, node_values: &'a HashMap<N, V>) -> FlowGraph<&'a V>
495    where
496        V: Eq + Hash,
497    {
498        let mut edges = vec![];
499        let start_value = node_values.get(&self.start_node).expect("cached");
500        for (parent, children) in &self.graph.adj {
501            let parent_value = node_values.get(parent).expect("cached");
502            for child in children {
503                let child_value = node_values.get(child).expect("cached");
504                edges.push((parent_value, child_value));
505            }
506        }
507        FlowGraph::new(SimpleDirectedGraph::new(edges), start_value)
508    }
509
510    /// Constructs a value flow graph from the given flow graph and value
511    /// function, and finds the closest common dominator value for the
512    /// values of the final nodes. Returns an error if value_fn returns an
513    /// error for any node in the flow graph. `final_nodes` must not be empty.
514    pub async fn find_dominator_value<'a, NI, V, VF, E>(
515        &'a self,
516        final_nodes: NI,
517        value_fn: VF,
518    ) -> Result<V, FindDominatorValueError<E>>
519    where
520        NI: IntoIterator<Item = &'a N>,
521        V: Hash + Eq + Clone,
522        VF: AsyncFn(&N) -> Result<V, E>,
523    {
524        let mut value_cache = ValueCache::new(value_fn);
525        let value_rc = self
526            .find_dominator_value_with_value_cache(final_nodes, &mut value_cache)
527            .await?;
528        Ok((*value_rc).clone())
529    }
530
531    /// Constructs a value flow graph from the given flow graph and value_cache,
532    /// and finds the closest common dominator value (in the value flow graph)
533    /// of the values of the final nodes.
534    ///
535    /// Returns an error if final_nodes is empty or if value_cache returns an
536    /// error.
537    pub async fn find_dominator_value_with_value_cache<'a, NI, V, VF, E>(
538        &'a self,
539        final_nodes: NI,
540        value_cache: &mut ValueCache<N, V, VF>,
541    ) -> Result<Rc<V>, FindDominatorValueError<E>>
542    where
543        NI: IntoIterator<Item = &'a N>,
544        V: Hash + Eq,
545        VF: AsyncFn(&N) -> Result<V, E>,
546    {
547        // First compute the values of all final nodes asynchronously and concurrently.
548        let final_values = value_cache
549            .get_values(final_nodes)
550            .await
551            .map_err(|e| FindDominatorValueError::ValueFnError(e))?;
552
553        match &*final_values {
554            [] => {
555                return Err(FindDominatorValueError::DominatorFinderError(
556                    DominatorFinderError::EmptyTargetSet,
557                ));
558            }
559            [final_value] => {
560                // Optimization: if all final nodes have the same value, that value is the
561                // closest common dominator. There is no need to build the value flow graph.
562                return Ok(final_value.clone());
563            }
564            _ => {}
565        }
566
567        let start_value = value_cache
568            .get_value(&self.start_node)
569            .await
570            .map_err(|err| FindDominatorValueError::ValueFnError(err))?;
571        if final_values.contains(&start_value) {
572            return Ok(Rc::clone(&start_value));
573        }
574
575        // Compute all remaining values.
576        value_cache
577            .get_values(self.graph.nodes())
578            .await
579            .map_err(|err| FindDominatorValueError::ValueFnError(err))?;
580
581        // NOTE: at this point we could compare the cardinality of the value set versus
582        // the number of nodes: if equal then we know that every node has a
583        // different value, and it is tempting to conclude that the result should be
584        // `start_value` (because the shape of the value flow graph is identical
585        // to the shape of the original flow graph). That is not always correct,
586        // consider this example with start node A and final nodes C and D:
587        //
588        // A(1) -> B(2) -> C(3)
589        //            \--> D(4)
590        //
591        // However, IF start node IS the closest common dominator of the original graph
592        // (it is not in the example above) then the answer would be `start_value`;
593        // so IF we knew that to be true we could skip building the value flow graph and
594        // running the dominator algorithm in the value flow graph.
595
596        let value_flow_graph = self.create_value_flow_graph(&value_cache.node_values);
597        let dominator_finder = DominatorFinder::calculate(&value_flow_graph)?;
598        let dominator_value =
599            dominator_finder.find_closest_common_dominator(final_values.iter())?;
600
601        Ok(Rc::clone(dominator_value))
602    }
603}
604
605/// Traverses nodes from `start_node` in post-order.
606fn post_order<T, NI>(
607    start_node: T,
608    mut neighbors_fn: impl FnMut(&T) -> NI,
609) -> impl Iterator<Item = T>
610where
611    T: Clone + Hash + Eq,
612    NI: DoubleEndedIterator<Item = T>,
613{
614    let mut stack = vec![(start_node, false)];
615    let mut visited: HashSet<T> = HashSet::new();
616    iter::from_fn(move || {
617        while let Some((node, processed)) = stack.pop() {
618            if processed {
619                // If we marked it as processed, it means its children
620                // were already added to the stack and processed.
621                return Some(node);
622            }
623            // Mark as visited so we don't start a new DFS from here
624            if !visited.insert(node.clone()) {
625                // The node is already visited, continue.
626                continue;
627            }
628            let neighbors = neighbors_fn(&node);
629            // Push the node back onto the stack with processed = true.
630            // It will be popped and yielded AFTER its children.
631            stack.push((node, true));
632            // Push the neighbors onto the stack with processed = false. The neighbors are
633            // added in reverse order, so they are processed in the
634            // original order.
635            for neighbor in neighbors.rev() {
636                if !visited.contains(&neighbor) {
637                    stack.push((neighbor, false));
638                }
639            }
640        }
641        None
642    })
643}
644
645#[cfg(test)]
646mod tests {
647    use maplit::hashmap;
648    use pollster::FutureExt as _;
649
650    use super::*;
651
652    #[test]
653    fn test_closest_common_dominator_split() -> Result<(), DominatorFinderError> {
654        //   /-> B \
655        // A        -> D
656        //   \-> C /
657        let flow_graph = FlowGraph::new(
658            SimpleDirectedGraph::new([("A", "B"), ("A", "C"), ("B", "D"), ("C", "D")]),
659            "A",
660        );
661        let df = DominatorFinder::calculate(&flow_graph)?;
662        assert_eq!(df.find_closest_common_dominator(["A"])?, "A");
663        assert_eq!(df.find_closest_common_dominator(["B"])?, "B");
664        assert_eq!(df.find_closest_common_dominator(["C"])?, "C");
665        assert_eq!(df.find_closest_common_dominator(["D"])?, "D");
666        assert_eq!(df.find_closest_common_dominator(["B", "C"])?, "A");
667        assert_eq!(df.find_closest_common_dominator(["B", "D"])?, "A");
668        assert_eq!(df.find_closest_common_dominator(["B", "C", "D"])?, "A");
669        Ok(())
670    }
671
672    #[test]
673    fn test_closest_common_dominator_linear_chain() -> Result<(), DominatorFinderError> {
674        // A -> B -> C -> D
675        let flow_graph = FlowGraph::new(
676            SimpleDirectedGraph::new([("A", "B"), ("B", "C"), ("C", "D")]),
677            "A",
678        );
679        let df = DominatorFinder::calculate(&flow_graph)?;
680        assert_eq!(df.find_closest_common_dominator(["A"])?, "A");
681        assert_eq!(df.find_closest_common_dominator(["B"])?, "B");
682        assert_eq!(df.find_closest_common_dominator(["C"])?, "C");
683        assert_eq!(df.find_closest_common_dominator(["D"])?, "D");
684        assert_eq!(df.find_closest_common_dominator(["A", "B"])?, "A");
685        assert_eq!(df.find_closest_common_dominator(["A", "C"])?, "A");
686        assert_eq!(df.find_closest_common_dominator(["A", "D"])?, "A");
687        assert_eq!(df.find_closest_common_dominator(["B", "D"])?, "B");
688        assert_eq!(df.find_closest_common_dominator(["C", "D"])?, "C");
689        assert_eq!(df.find_closest_common_dominator(["A", "B", "C", "D"])?, "A");
690        Ok(())
691    }
692
693    #[test]
694    fn test_closest_common_dominator_classic_diamond() -> Result<(), DominatorFinderError> {
695        //      /-> B -\
696        //    A          -> D -> E
697        //      \-> C -/
698        let flow_graph = FlowGraph::new(
699            SimpleDirectedGraph::new([("A", "B"), ("A", "C"), ("B", "D"), ("C", "D"), ("D", "E")]),
700            "A",
701        );
702        let df = DominatorFinder::calculate(&flow_graph)?;
703        assert_eq!(df.find_closest_common_dominator(["B", "C"])?, "A");
704        assert_eq!(df.find_closest_common_dominator(["B", "E"])?, "A");
705        assert_eq!(df.find_closest_common_dominator(["D"])?, "D");
706        assert_eq!(df.find_closest_common_dominator(["D", "E"])?, "D");
707        assert_eq!(df.find_closest_common_dominator(["A", "D"])?, "A");
708        Ok(())
709    }
710
711    #[test]
712    fn test_closest_common_dominator_single_node() -> Result<(), DominatorFinderError> {
713        // A
714        let flow_graph = FlowGraph::new(SimpleDirectedGraph::new([("A", "A")]), "A");
715        let df = DominatorFinder::calculate(&flow_graph)?;
716        assert_eq!(df.find_closest_common_dominator(["A"])?, "A");
717        Ok(())
718    }
719
720    #[test]
721    fn test_invalid_flowgraph() {
722        //       /-> E
723        // A -> B
724        //       \-> F
725        //           ^
726        //           |
727        // C --> D --/
728        let flow_graph = FlowGraph::new(
729            SimpleDirectedGraph::new([("A", "B"), ("B", "E"), ("B", "F"), ("C", "D"), ("D", "F")]),
730            "A",
731        );
732        assert_eq!(
733            DominatorFinder::calculate(&flow_graph).err(),
734            Some(DominatorFinderError::UnreachableNodesInFlowGraph)
735        );
736    }
737
738    #[test]
739    fn test_closest_common_dominator_simple_cycle_with_entry() -> Result<(), DominatorFinderError> {
740        //
741        // A -> B -> C -> D
742        //      ^         |
743        //      |         |
744        //      \--------/
745        let flow_graph = FlowGraph::new(
746            SimpleDirectedGraph::new([("A", "B"), ("B", "C"), ("C", "D"), ("D", "B")]),
747            "A",
748        );
749        let df = DominatorFinder::calculate(&flow_graph)?;
750        assert_eq!(df.find_closest_common_dominator(["A", "B"])?, "A");
751        assert_eq!(df.find_closest_common_dominator(["A", "C"])?, "A");
752        assert_eq!(df.find_closest_common_dominator(["A", "B", "C"])?, "A");
753        assert_eq!(df.find_closest_common_dominator(["B", "C"])?, "B");
754        assert_eq!(df.find_closest_common_dominator(["B", "C", "D"])?, "B");
755        assert_eq!(df.find_closest_common_dominator(["A"])?, "A");
756        assert_eq!(df.find_closest_common_dominator(["B"])?, "B");
757        assert_eq!(df.find_closest_common_dominator(["C"])?, "C");
758        assert_eq!(df.find_closest_common_dominator(["D"])?, "D");
759        Ok(())
760    }
761
762    #[test]
763    fn test_closest_common_dominator_figure_eight_with_bridge() -> Result<(), DominatorFinderError>
764    {
765        //
766        //  A -> B -> C -> D -> E -> F -> G
767        //       ^         |    ^         |
768        //       |         |    |         |
769        //        \_______/      \_______/
770        let flow_graph = FlowGraph::new(
771            SimpleDirectedGraph::new([
772                ("A", "B"), // entry
773                ("B", "C"),
774                ("C", "D"),
775                ("D", "B"), // Loop 1
776                ("D", "E"), // Bridge
777                ("E", "F"),
778                ("F", "G"),
779                ("G", "E"), // Loop 2
780            ]),
781            "A",
782        );
783        let df = DominatorFinder::calculate(&flow_graph)?;
784        assert_eq!(df.find_closest_common_dominator(["B", "C"])?, "B");
785        assert_eq!(df.find_closest_common_dominator(["B", "D"])?, "B");
786        assert_eq!(df.find_closest_common_dominator(["B", "E"])?, "B");
787        assert_eq!(df.find_closest_common_dominator(["C", "E"])?, "C");
788        assert_eq!(df.find_closest_common_dominator(["C", "F"])?, "C");
789        assert_eq!(df.find_closest_common_dominator(["D", "E"])?, "D");
790        assert_eq!(df.find_closest_common_dominator(["D", "F"])?, "D");
791        assert_eq!(df.find_closest_common_dominator(["E", "G"])?, "E");
792        assert_eq!(df.find_closest_common_dominator(["F", "G"])?, "F");
793        Ok(())
794    }
795
796    #[test]
797    fn test_closest_common_dominator_figure_eight() -> Result<(), DominatorFinderError> {
798        //
799        //  A -> B -> C --> D   -> E -> F
800        //       ^         | ^          |
801        //       |         | |          |
802        //        \_______/  \_________/
803        let flow_graph = FlowGraph::new(
804            SimpleDirectedGraph::new([
805                ("A", "B"), // entry
806                ("B", "C"),
807                ("C", "D"),
808                ("D", "B"), // Loop 1
809                ("D", "E"),
810                ("E", "F"),
811                ("F", "D"), // Loop 2
812            ]),
813            "A",
814        );
815        let df = DominatorFinder::calculate(&flow_graph)?;
816        assert_eq!(df.find_closest_common_dominator(["B", "C"])?, "B");
817        assert_eq!(df.find_closest_common_dominator(["B", "D"])?, "B");
818        assert_eq!(df.find_closest_common_dominator(["B", "E"])?, "B");
819        assert_eq!(df.find_closest_common_dominator(["C", "D"])?, "C");
820        assert_eq!(df.find_closest_common_dominator(["C", "E"])?, "C");
821        assert_eq!(df.find_closest_common_dominator(["C", "F"])?, "C");
822        assert_eq!(df.find_closest_common_dominator(["D", "E"])?, "D");
823        assert_eq!(df.find_closest_common_dominator(["D", "F"])?, "D");
824        assert_eq!(df.find_closest_common_dominator(["E", "F"])?, "E");
825        Ok(())
826    }
827
828    #[test]
829    fn test_closest_common_dominator_entry_cycle_dominance() -> Result<(), DominatorFinderError> {
830        // A -> B -> C
831        //      ^    |
832        //      |----/
833        let flow_graph = FlowGraph::new(
834            SimpleDirectedGraph::new([("A", "B"), ("B", "C"), ("C", "B")]),
835            "A",
836        );
837        let df = DominatorFinder::calculate(&flow_graph)?;
838        assert_eq!(df.find_closest_common_dominator(["A", "B"])?, "A");
839        assert_eq!(df.find_closest_common_dominator(["A", "C"])?, "A");
840        assert_eq!(df.find_closest_common_dominator(["B", "C"])?, "B");
841        assert_eq!(df.find_closest_common_dominator(["A", "B", "C"])?, "A");
842        Ok(())
843    }
844
845    #[test]
846    fn test_closest_common_dominator_nested_loops() -> Result<(), DominatorFinderError> {
847        //           /---> E
848        //           |     |
849        //           |     |
850        // A -> B -> C <--/
851        //      ^    |
852        //      |    V
853        //      \----D
854        let flow_graph = FlowGraph::new(
855            SimpleDirectedGraph::new([
856                ("A", "B"),
857                ("B", "C"),
858                ("C", "D"),
859                ("C", "E"),
860                ("E", "C"),
861                ("D", "B"),
862            ]),
863            "A",
864        );
865        let df = DominatorFinder::calculate(&flow_graph)?;
866        assert_eq!(df.find_closest_common_dominator(["A", "B"])?, "A");
867        assert_eq!(df.find_closest_common_dominator(["A", "C"])?, "A");
868        assert_eq!(df.find_closest_common_dominator(["B", "C"])?, "B");
869        assert_eq!(df.find_closest_common_dominator(["B", "D"])?, "B");
870        assert_eq!(df.find_closest_common_dominator(["B", "E"])?, "B");
871        assert_eq!(df.find_closest_common_dominator(["C", "D"])?, "C");
872        assert_eq!(df.find_closest_common_dominator(["C", "E"])?, "C");
873        assert_eq!(df.find_closest_common_dominator(["D", "E"])?, "C");
874        assert_eq!(df.find_closest_common_dominator(["B", "C", "D"])?, "B");
875        assert_eq!(df.find_closest_common_dominator(["B", "C", "E"])?, "B");
876        assert_eq!(df.find_closest_common_dominator(["B", "D", "E"])?, "B");
877        assert_eq!(df.find_closest_common_dominator(["C", "D", "E"])?, "C");
878        assert_eq!(df.find_closest_common_dominator(["B", "C", "D", "E"])?, "B");
879        Ok(())
880    }
881
882    #[test]
883    fn test_irreducible_graph_cooper_harvey_kennedy_fig2() -> Result<(), DominatorFinderError> {
884        //        5
885        //     /    \
886        //    |      |
887        //    V      V
888        //    4      3
889        //    |      |
890        //    V      V
891        //    1 <==> 2
892        let graph = SimpleDirectedGraph::new([(1, 2), (2, 1), (3, 2), (4, 1), (5, 4), (5, 3)]);
893        let flow_graph = FlowGraph::new(graph, 5);
894        let df = DominatorFinder::calculate(&flow_graph)?;
895        assert_eq!(
896            df.get_immediate_dominators(),
897            HashMap::from([(1, 5), (2, 5), (3, 5), (4, 5), (5, 5),])
898        );
899        Ok(())
900    }
901
902    #[test]
903    fn test_irreducible_graph_cooper_harvey_kennedy_fig3() -> Result<(), DominatorFinderError> {
904        //     6
905        //   /   \
906        //  |     |
907        //  v     v
908        //  5     4 --
909        //  |     |    \
910        //  v     v     v
911        //  1 <=> 2 <=> 3
912        let graph = SimpleDirectedGraph::new([
913            (1, 2),
914            (2, 1),
915            (2, 3),
916            (3, 2),
917            (5, 1),
918            (4, 2),
919            (4, 3),
920            (6, 5),
921            (6, 4),
922        ]);
923        let flow_graph = FlowGraph::new(graph, 6);
924        let df = DominatorFinder::calculate(&flow_graph)?;
925        assert_eq!(
926            df.get_immediate_dominators(),
927            HashMap::from([(1, 6), (2, 6), (3, 6), (4, 6), (5, 6), (6, 6),])
928        );
929        assert_eq!(df.find_closest_common_dominator([2, 3])?, 6);
930        Ok(())
931    }
932
933    #[test]
934    fn test_dominator_tree_with_three_levels() -> Result<(), DominatorFinderError> {
935        // Graph taken from https://en.wikipedia.org/wiki/Dominator_(graph_theory)
936        //     1
937        //     |  /---\
938        //     v /     \
939        //     2 <--\    \
940        //    / \    \    \
941        //   /   \    \    \
942        //  |     |    |   |
943        //  v     v    |   |
944        //  3     4    |   |
945        //  |     |    |   |
946        //   \    v    |   v
947        //    --> 5 --/    6
948        //
949        let graph =
950            SimpleDirectedGraph::new([(1, 2), (2, 3), (2, 4), (2, 6), (3, 5), (4, 5), (5, 2)]);
951        let flow_graph = FlowGraph::new(graph, 1);
952        let df = DominatorFinder::calculate(&flow_graph)?;
953        assert_eq!(
954            df.get_immediate_dominators(),
955            HashMap::from([(1, 1), (2, 1), (3, 2), (4, 2), (5, 2), (6, 2),])
956        );
957        assert_eq!(df.find_closest_common_dominator([1, 6])?, 1);
958        assert_eq!(df.find_closest_common_dominator([2, 3])?, 2);
959        assert_eq!(df.find_closest_common_dominator([2, 4])?, 2);
960        assert_eq!(df.find_closest_common_dominator([2, 5])?, 2);
961        assert_eq!(df.find_closest_common_dominator([2, 6])?, 2);
962        assert_eq!(df.find_closest_common_dominator([3, 4])?, 2);
963        assert_eq!(df.find_closest_common_dominator([3, 5])?, 2);
964        assert_eq!(df.find_closest_common_dominator([3, 6])?, 2);
965        assert_eq!(df.find_closest_common_dominator([4, 5])?, 2);
966        assert_eq!(df.find_closest_common_dominator([4, 6])?, 2);
967        assert_eq!(df.find_closest_common_dominator([5, 6])?, 2);
968        assert_eq!(df.find_closest_common_dominator([2, 3, 5])?, 2);
969        assert_eq!(df.find_closest_common_dominator([3, 4, 5])?, 2);
970        assert_eq!(df.find_closest_common_dominator([3, 4, 5, 6])?, 2);
971        Ok(())
972    }
973
974    #[test]
975    fn test_big_graph_fig_18_3() -> Result<(), DominatorFinderError> {
976        // Graph taken from Modern Compiler Implementation in Java,
977        // by Appel and Palsberg, 2004
978        //
979        //           1
980        //           |
981        //           v
982        //      /--> 2 <--\
983        //      |   / \   |
984        //      |  v   v  |
985        //      \- 3   4 -/
986        //             /\
987        //            /  \
988        //           v    v
989        //     /-->  5    6
990        //    /    /   \  /
991        //   /    |     ||
992        //  /     v     vv
993        // |  /-> 8      7
994        // |  |   |      |
995        // |  |   v      v
996        // |  \-- 9      11
997        // |      |      |
998        // |      v      v
999        //  \--- 10 --> 12
1000        //
1001        let graph = SimpleDirectedGraph::new([
1002            (1, 2),
1003            (2, 3),
1004            (2, 4),
1005            (3, 2),
1006            (4, 2),
1007            (4, 5),
1008            (4, 6),
1009            (5, 7),
1010            (5, 8),
1011            (6, 7),
1012            (7, 11),
1013            (8, 9),
1014            (9, 8),
1015            (9, 10),
1016            (10, 5),
1017            (10, 12),
1018            (11, 12),
1019        ]);
1020        let flow_graph = FlowGraph::new(graph, 1);
1021        let df = DominatorFinder::calculate(&flow_graph)?;
1022        assert_eq!(
1023            df.get_immediate_dominators(),
1024            HashMap::from([
1025                (1, 1),
1026                (2, 1),
1027                (3, 2),
1028                (4, 2),
1029                (5, 4),
1030                (6, 4),
1031                (7, 4),
1032                (8, 5),
1033                (9, 8),
1034                (10, 9),
1035                (11, 7),
1036                (12, 4)
1037            ])
1038        );
1039        assert_eq!(df.find_closest_common_dominator([6, 3])?, 2);
1040        assert_eq!(df.find_closest_common_dominator([11, 9, 12])?, 4);
1041        assert_eq!(df.find_closest_common_dominator([11, 9, 5])?, 4);
1042        assert_eq!(df.find_closest_common_dominator([11, 10])?, 4);
1043        assert_eq!(df.find_closest_common_dominator([10, 11, 12, 3, 6])?, 2);
1044        Ok(())
1045    }
1046
1047    #[test]
1048    fn test_big_graph_fig_19_8() -> Result<(), DominatorFinderError> {
1049        // Graph taken from Modern Compiler Implementation in Java,
1050        // by Appel and Palsberg, 2004
1051        //
1052        //          /----- A ----\
1053        //         |              |
1054        //         v              v
1055        //  /----> B ---\     /-> C --\
1056        //  |      |    |     |   |   |
1057        //  |      v    |     |   v   |
1058        //  |  /-- D    |     \-- E   |
1059        //  |  |   |    |         |   |
1060        //  |  |   |    |         |   |
1061        //  |  v   |    v         v   v
1062        //  |  F   \--> G           H
1063        //  |  |\       |          /
1064        //  |  | \      v         /
1065        //  |  |  \ /---J        /
1066        //  |  |   X            /
1067        //  |  | /  \          /
1068        //  |  vv    v         |
1069        //  |  I     K         |
1070        //  |  \    /          |
1071        //  |   \  /           |
1072        //  |    vv            v
1073        //  \---- L ---------> M
1074        //
1075        let graph = SimpleDirectedGraph::new([
1076            ("A", "B"),
1077            ("A", "C"),
1078            ("B", "D"),
1079            ("B", "G"),
1080            ("C", "E"),
1081            ("C", "H"),
1082            ("D", "F"),
1083            ("D", "G"),
1084            ("E", "C"),
1085            ("E", "H"),
1086            ("F", "I"),
1087            ("F", "K"),
1088            ("G", "J"),
1089            ("H", "M"),
1090            ("I", "L"),
1091            ("J", "I"),
1092            ("K", "L"),
1093            ("L", "B"),
1094            ("L", "M"),
1095        ]);
1096        let flow_graph = FlowGraph::new(graph, "A");
1097        let df = DominatorFinder::calculate(&flow_graph)?;
1098        assert_eq!(
1099            df.get_immediate_dominators(),
1100            HashMap::from([
1101                ("A", "A"),
1102                ("B", "A"),
1103                ("C", "A"),
1104                ("D", "B"),
1105                ("E", "C"),
1106                ("F", "D"),
1107                ("G", "B"),
1108                ("H", "C"),
1109                ("I", "B"),
1110                ("J", "G"),
1111                ("K", "F"),
1112                ("L", "B"),
1113                ("M", "A"),
1114            ])
1115        );
1116        assert_eq!(df.find_closest_common_dominator(["K", "L"])?, "B");
1117        assert_eq!(df.find_closest_common_dominator(["K", "C"])?, "A");
1118        assert_eq!(df.find_closest_common_dominator(["B", "G", "J"])?, "B");
1119        Ok(())
1120    }
1121
1122    #[test]
1123    fn test_closest_common_dominator_tree() -> Result<(), DominatorFinderError> {
1124        // A -> B -> C
1125        // \     \-> D
1126        //  \------> E
1127        let flow_graph = FlowGraph::new(
1128            SimpleDirectedGraph::new([("A", "B"), ("B", "C"), ("B", "D"), ("A", "E")]),
1129            "A",
1130        );
1131        let df = DominatorFinder::calculate(&flow_graph)?;
1132        assert_eq!(df.find_closest_common_dominator(["B", "C"])?, "B");
1133        assert_eq!(df.find_closest_common_dominator(["B", "E"])?, "A");
1134        assert_eq!(df.find_closest_common_dominator(["C", "D"])?, "B");
1135        assert_eq!(df.find_closest_common_dominator(["C", "E"])?, "A");
1136        assert_eq!(df.find_closest_common_dominator(["B", "C", "D"])?, "B");
1137        assert_eq!(df.find_closest_common_dominator(["C", "D", "E"])?, "A");
1138        Ok(())
1139    }
1140
1141    #[test]
1142    fn test_closest_common_dominator_bypassing_path() -> Result<(), DominatorFinderError> {
1143        // A -> B -> C -> D
1144        // |              ^
1145        // v              |
1146        // E -------------/
1147        let flow_graph = FlowGraph::new(
1148            SimpleDirectedGraph::new([("A", "B"), ("B", "C"), ("C", "D"), ("A", "E"), ("E", "D")]),
1149            "A",
1150        );
1151        let df = DominatorFinder::calculate(&flow_graph)?;
1152        assert_eq!(df.find_closest_common_dominator(["B", "C"])?, "B");
1153        assert_eq!(df.find_closest_common_dominator(["B", "D"])?, "A");
1154        assert_eq!(df.find_closest_common_dominator(["B", "E"])?, "A");
1155        assert_eq!(df.find_closest_common_dominator(["C", "D"])?, "A");
1156        assert_eq!(df.find_closest_common_dominator(["C", "E"])?, "A");
1157        assert_eq!(df.find_closest_common_dominator(["D", "E"])?, "A");
1158        assert_eq!(df.find_closest_common_dominator(["B", "C", "D"])?, "A");
1159        assert_eq!(df.find_closest_common_dominator(["C", "D", "E"])?, "A");
1160        Ok(())
1161    }
1162
1163    #[test]
1164    fn test_closest_common_dominator_self_loop_handling() -> Result<(), DominatorFinderError> {
1165        // A->A (Self loop), A->B
1166        let flow_graph = FlowGraph::new(SimpleDirectedGraph::new([("A", "A"), ("A", "B")]), "A");
1167        let df = DominatorFinder::calculate(&flow_graph)?;
1168        assert_eq!(df.find_closest_common_dominator(["A"])?, "A");
1169        Ok(())
1170    }
1171
1172    #[test]
1173    fn test_closest_common_dominator_multi_edge() -> Result<(), DominatorFinderError> {
1174        // Shape: A->B (x2), B->C.
1175        let flow_graph = FlowGraph::new(
1176            SimpleDirectedGraph::new([
1177                ("A", "B"),
1178                ("A", "B"), // Duplicate edge
1179                ("B", "C"),
1180            ]),
1181            "A",
1182        );
1183        let df = DominatorFinder::calculate(&flow_graph)?;
1184        assert_eq!(df.find_closest_common_dominator(["A"])?, "A");
1185        assert_eq!(df.find_closest_common_dominator(["B", "C"])?, "B");
1186        Ok(())
1187    }
1188
1189    #[test]
1190    fn test_closest_common_dominator_invalid_target_set() -> Result<(), DominatorFinderError> {
1191        // A -> B
1192        let flow_graph = FlowGraph::new(SimpleDirectedGraph::new([("A", "B")]), "A");
1193        let df = DominatorFinder::calculate(&flow_graph)?;
1194        assert_eq!(
1195            df.find_closest_common_dominator([]),
1196            Err(DominatorFinderError::EmptyTargetSet)
1197        );
1198        Ok(())
1199    }
1200
1201    #[test]
1202    fn test_closest_common_dominator_repeated_node() -> Result<(), DominatorFinderError> {
1203        // A -> B
1204        let flow_graph = FlowGraph::new(SimpleDirectedGraph::new([("A", "B")]), "A");
1205        let df = DominatorFinder::calculate(&flow_graph)?;
1206        assert_eq!(df.find_closest_common_dominator(["A", "B", "A", "B"])?, "A");
1207        Ok(())
1208    }
1209
1210    #[test]
1211    fn test_simple_directed_graph_nodes() {
1212        let graph = SimpleDirectedGraph::new([("A", "B"), ("B", "C")]);
1213        let nodes = graph.nodes().copied().collect_vec();
1214        assert_eq!(nodes, ["A", "B", "C"]);
1215
1216        let graph = SimpleDirectedGraph::<String>::new([]);
1217        let nodes = graph.nodes().cloned().collect_vec();
1218        assert!(nodes.is_empty());
1219    }
1220
1221    #[test]
1222    fn test_simple_directed_graph_edges() {
1223        let graph = SimpleDirectedGraph::new([("A", "B"), ("B", "C"), ("A", "C")]);
1224        let edges = graph.edges().map(|(&u, &v)| (u, v)).collect_vec();
1225        assert_eq!(edges, [("A", "B"), ("A", "C"), ("B", "C")]);
1226
1227        let graph = SimpleDirectedGraph::<String>::new([]);
1228        let edges = graph.edges().collect_vec();
1229        assert!(edges.is_empty());
1230    }
1231
1232    #[test]
1233    fn test_simple_directed_graph_adjacent_nodes() {
1234        let graph = SimpleDirectedGraph::new([("A", "B"), ("A", "C"), ("B", "D")]);
1235        assert_eq!(
1236            graph.adjacent_nodes(&"A").unwrap().copied().collect_vec(),
1237            ["B", "C"]
1238        );
1239        assert_eq!(
1240            graph.adjacent_nodes(&"B").unwrap().copied().collect_vec(),
1241            ["D"]
1242        );
1243        assert!(graph.adjacent_nodes(&"C").unwrap().next().is_none());
1244        assert!(graph.adjacent_nodes(&"Z").is_none());
1245    }
1246
1247    #[test]
1248    fn test_simple_directed_graph_contains_node() {
1249        let graph = SimpleDirectedGraph::new([("A", "B"), ("B", "C")]);
1250        assert!(graph.contains_node(&"A"));
1251        assert!(graph.contains_node(&"B"));
1252        assert!(graph.contains_node(&"C"));
1253        assert!(!graph.contains_node(&"D"));
1254    }
1255
1256    #[test]
1257    fn test_simple_directed_graph_new() {
1258        let graph = SimpleDirectedGraph::new([("A", "B"), ("A", "C"), ("B", "C"), ("A", "B")]);
1259        let nodes = graph.nodes().copied().collect_vec();
1260        assert_eq!(nodes, ["A", "B", "C"]);
1261        let edges = graph.edges().map(|(&u, &v)| (u, v)).collect_vec();
1262        assert_eq!(edges, [("A", "B"), ("A", "C"), ("B", "C")]);
1263
1264        let graph = SimpleDirectedGraph::new([("B", "C"), ("A", "B")]);
1265        let nodes = graph.nodes().copied().collect_vec();
1266        assert_eq!(nodes, ["B", "C", "A"]);
1267        let edges = graph.edges().map(|(&u, &v)| (u, v)).collect_vec();
1268        assert_eq!(edges, [("B", "C"), ("A", "B")]);
1269    }
1270
1271    #[test]
1272    fn test_flow_graph_new() {
1273        let graph = SimpleDirectedGraph::new([("A", "B")]);
1274        let flow_graph = FlowGraph::new(graph.clone(), "A");
1275        assert_eq!(flow_graph.graph, graph);
1276        assert_eq!(flow_graph.start_node, "A");
1277        let flow_graph = FlowGraph::new(graph.clone(), "C");
1278        assert_eq!(flow_graph.graph, graph);
1279        assert_eq!(flow_graph.start_node, "C");
1280    }
1281
1282    #[test]
1283    fn test_post_order() {
1284        // This graph:
1285        //  o F
1286        //  |\
1287        //  o | E
1288        //  | o D
1289        //  | o C
1290        //  | o B
1291        //  |/
1292        //  o A
1293
1294        let neighbors = hashmap! {
1295            'A' => vec![],
1296            'B' => vec!['A'],
1297            'C' => vec!['B'],
1298            'D' => vec!['C'],
1299            'E' => vec!['A'],
1300            'F' => vec!['E', 'D'],
1301        };
1302        let neighbors_fn = |node: &char| neighbors[node].iter().copied();
1303        assert_eq!(
1304            post_order('F', neighbors_fn).collect_vec(),
1305            ['A', 'E', 'B', 'C', 'D', 'F']
1306        );
1307        assert_eq!(post_order('E', neighbors_fn).collect_vec(), ['A', 'E']);
1308        assert_eq!(
1309            post_order('D', neighbors_fn).collect_vec(),
1310            ['A', 'B', 'C', 'D']
1311        );
1312        assert_eq!(post_order('A', neighbors_fn).collect_vec(), ['A']);
1313
1314        // This graph:
1315        //  o I
1316        //  |\
1317        //  | o H
1318        //  | |\
1319        //  | | o G
1320        //  | o | F
1321        //  | | o E
1322        //  o |/ D
1323        //  | o C
1324        //  o | B
1325        //  |/
1326        //  o A
1327
1328        let neighbors = hashmap! {
1329            'A' => vec![],
1330            'B' => vec!['A'],
1331            'C' => vec!['A'],
1332            'D' => vec!['B'],
1333            'E' => vec!['C'],
1334            'F' => vec!['C'],
1335            'G' => vec!['E'],
1336            'H' => vec!['F', 'G'],
1337            'I' => vec!['D', 'H'],
1338        };
1339        let neighbors_fn = |node: &char| neighbors[node].iter().copied();
1340        assert_eq!(
1341            post_order('I', neighbors_fn).collect_vec(),
1342            ['A', 'B', 'D', 'C', 'F', 'E', 'G', 'H', 'I']
1343        );
1344
1345        // This graph:
1346        //  o I
1347        //  |\
1348        //  | |\
1349        //  | | |\
1350        //  | | | o h (h > I)
1351        //  | | |/|
1352        //  | | o | G
1353        //  | |/| o f
1354        //  | o |/ e (e > I, G)
1355        //  |/| o D
1356        //  o |/ C
1357        //  | o b (b > D)
1358        //  |/
1359        //  o A
1360
1361        let neighbors = hashmap! {
1362            'A' => vec![],
1363            'b' => vec!['A'],
1364            'C' => vec!['A'],
1365            'D' => vec!['b'],
1366            'e' => vec!['C', 'b'],
1367            'f' => vec!['D'],
1368            'G' => vec!['e', 'D'],
1369            'h' => vec!['G', 'f'],
1370            'I' => vec!['C', 'e', 'G', 'h'],
1371        };
1372        let neighbors_fn = |node: &char| neighbors[node].iter().copied();
1373        assert_eq!(
1374            post_order('I', neighbors_fn).collect_vec(),
1375            ['A', 'C', 'b', 'e', 'D', 'G', 'f', 'h', 'I']
1376        );
1377
1378        // This graph:
1379        //  o G
1380        //  |\
1381        //  | o F
1382        //  o | E
1383        //  | o D
1384        //  |/
1385        //  o C
1386        //  o B
1387        //  o A
1388
1389        let neighbors = hashmap! {
1390            'A' => vec![],
1391            'B' => vec!['A'],
1392            'C' => vec!['B'],
1393            'D' => vec!['C'],
1394            'E' => vec!['C'],
1395            'F' => vec!['D'],
1396            'G' => vec!['E', 'F'],
1397        };
1398        let neighbors_fn = |node: &char| neighbors[node].iter().copied();
1399        assert_eq!(
1400            post_order('G', neighbors_fn).collect_vec(),
1401            ['A', 'B', 'C', 'E', 'D', 'F', 'G']
1402        );
1403
1404        // This graph:
1405        //  o G
1406        //  |\
1407        //  o | F
1408        //  o | E
1409        //  | o D
1410        //  |/
1411        //  o c (c > E, D)
1412        //  o B
1413        //  o A
1414
1415        let neighbors = hashmap! {
1416            'A' => vec![],
1417            'B' => vec!['A'],
1418            'c' => vec!['B'],
1419            'D' => vec!['c'],
1420            'E' => vec!['c'],
1421            'F' => vec!['E'],
1422            'G' => vec!['F', 'D'],
1423        };
1424        let neighbors_fn = |node: &char| neighbors[node].iter().copied();
1425        assert_eq!(
1426            post_order('G', neighbors_fn).collect_vec(),
1427            ['A', 'B', 'c', 'E', 'F', 'D', 'G']
1428        );
1429
1430        // This graph:
1431        //  o F
1432        //  |\
1433        //  o | E
1434        //  | o D
1435        //  | | o C
1436        //  | | |
1437        //  | | o B
1438        //  | |/
1439        //  |/
1440        //  o A
1441
1442        let neighbors = hashmap! {
1443            'A' => vec![],
1444            'B' => vec!['A'],
1445            'C' => vec!['B'],
1446            'D' => vec!['A'],
1447            'E' => vec!['A'],
1448            'F' => vec!['E', 'D'],
1449        };
1450        let neighbors_fn = |node: &char| neighbors[node].iter().copied();
1451        assert_eq!(
1452            post_order('F', neighbors_fn).collect_vec(),
1453            ['A', 'E', 'D', 'F']
1454        );
1455        assert_eq!(post_order('C', neighbors_fn).collect_vec(), ['A', 'B', 'C']);
1456
1457        // This graph:
1458        //  o D
1459        //  | \
1460        //  o | C
1461        //    o B
1462        //    o A
1463
1464        let neighbors = hashmap! {
1465            'A' => vec![],
1466            'B' => vec!['A'],
1467            'C' => vec![],
1468            'D' => vec!['C', 'B'],
1469        };
1470        let neighbors_fn = |node: &char| neighbors[node].iter().copied();
1471        assert_eq!(
1472            post_order('D', neighbors_fn).collect_vec(),
1473            ['C', 'A', 'B', 'D']
1474        );
1475
1476        // This graph:
1477        //  o C
1478        //  o B
1479        //  o A (to C)
1480
1481        let neighbors = hashmap! {
1482            'A' => vec!['C'],
1483            'B' => vec!['A'],
1484            'C' => vec!['B'],
1485        };
1486        let neighbors_fn = |node: &char| neighbors[node].iter().copied();
1487        assert_eq!(post_order('C', neighbors_fn).collect_vec(), ['A', 'B', 'C']);
1488        assert_eq!(post_order('B', neighbors_fn).collect_vec(), ['C', 'A', 'B']);
1489        assert_eq!(post_order('A', neighbors_fn).collect_vec(), ['B', 'C', 'A']);
1490    }
1491
1492    #[test]
1493    fn test_value_flow_graph_new() {
1494        // A(1) -> B(1) -> C(2)
1495        let simple_graph = SimpleDirectedGraph::new([("A", "B"), ("B", "C")]);
1496        let flow_graph = FlowGraph::new(simple_graph, "A");
1497        let node_values = HashMap::from([("A", 1), ("B", 1), ("C", 2)]);
1498        let value_flow_graph = flow_graph.create_value_flow_graph(&node_values);
1499
1500        let expected_value_edges = [(&1, &1), (&1, &2)];
1501        let expected_flow_graph =
1502            FlowGraph::new(SimpleDirectedGraph::new(expected_value_edges), &1);
1503        assert_eq!(value_flow_graph, expected_flow_graph);
1504    }
1505
1506    #[test]
1507    fn test_value_flow_graph_find_dominator_value() {
1508        // A(1) -> B(1) -> C(2) -> D(3)
1509        //          \------------> E(3)
1510        let simple_graph =
1511            SimpleDirectedGraph::new([("A", "B"), ("B", "C"), ("C", "D"), ("B", "E")]);
1512        let flow_graph = FlowGraph::new(simple_graph, "A");
1513        let value_fn = async |node: &&str| match *node {
1514            "A" | "B" => Ok(1),
1515            "C" => Ok(2),
1516            "D" | "E" => Ok(3),
1517            _ => Err("Unknown node".to_string()),
1518        };
1519
1520        // Value graph (* means node has a self-loop):
1521        //   1* -> 2 -> 3
1522        //    \         ^
1523        //     \--------|
1524        assert_eq!(
1525            flow_graph
1526                .find_dominator_value(&["D", "E"], value_fn)
1527                .block_on(),
1528            Ok(3)
1529        );
1530        assert_eq!(
1531            flow_graph
1532                .find_dominator_value(&["C", "D"], value_fn)
1533                .block_on(),
1534            Ok(1)
1535        );
1536        assert_eq!(
1537            flow_graph
1538                .find_dominator_value(&["B", "C"], value_fn)
1539                .block_on(),
1540            Ok(1)
1541        );
1542    }
1543
1544    #[test]
1545    fn test_find_dominator_value_with_distinct_values() {
1546        // A(1) -> B(2) -> C(3) -> D(4)
1547        //          \------------> E(5)
1548        let simple_graph =
1549            SimpleDirectedGraph::new([("A", "B"), ("B", "C"), ("C", "D"), ("B", "E")]);
1550        let flow_graph = FlowGraph::new(simple_graph, "A");
1551        let value_fn = async |node: &&str| match *node {
1552            "A" => Ok(1),
1553            "B" => Ok(2),
1554            "C" => Ok(3),
1555            "D" => Ok(4),
1556            "E" => Ok(5),
1557            _ => Err("Unknown node".to_string()),
1558        };
1559
1560        // Value graph:
1561        // 1 -> 2 -> 3 -> 4
1562        //       \------> 5
1563        assert_eq!(
1564            flow_graph
1565                .find_dominator_value(&["D", "E"], value_fn)
1566                .block_on(),
1567            Ok(2)
1568        );
1569        assert_eq!(
1570            flow_graph
1571                .find_dominator_value(&["C", "D"], value_fn)
1572                .block_on(),
1573            Ok(3)
1574        );
1575        assert_eq!(
1576            flow_graph
1577                .find_dominator_value(&["B", "C"], value_fn)
1578                .block_on(),
1579            Ok(2)
1580        );
1581    }
1582
1583    #[test]
1584    fn test_find_dominator_value_with_invalid_flow_graph() {
1585        // Invalid flow graph: A(1) -> B(1), C(2) -> D(2) (C and D are not reachable
1586        // from A).
1587        let simple_graph = SimpleDirectedGraph::new([("A", "B"), ("C", "D")]);
1588        let flow_graph = FlowGraph::new(simple_graph, "A");
1589        let value_fn = async |node: &&str| match *node {
1590            "A" | "B" => Ok(1),
1591            "C" | "D" => Ok(2),
1592            _ => Err("Unknown node".to_string()),
1593        };
1594        // Todo: the flow_graph is invalid because C and D are not reachable from A, so
1595        // ideally find_dominator_value should return UnreachableNodesInFlowGraph, but
1596        // the optimizations in find_dominator_value currently cause it to
1597        // return the start value. The best way to fix this is to calculate (and store)
1598        // the post-order in FlowGraph::new, that way we could not possibly construct an
1599        // invalid flow graph. This is not a big concern in practice though.
1600        assert_eq!(
1601            flow_graph
1602                .find_dominator_value(&["B", "D"], value_fn)
1603                .block_on(),
1604            Ok(1)
1605        );
1606    }
1607
1608    #[test]
1609    fn test_find_dominator_value_with_unknown_node_in_target_set() {
1610        // Flow graph: A(1) -> B(2).
1611        let simple_graph = SimpleDirectedGraph::new([("A", "B")]);
1612        let flow_graph = FlowGraph::new(simple_graph, "A");
1613        let value_fn = async |node: &&str| match *node {
1614            "A" => Ok(1),
1615            "B" => Ok(2),
1616            "X" => Ok(666),
1617            _ => Err("Unknown node".to_string()),
1618        };
1619        assert_eq!(
1620            flow_graph
1621                .find_dominator_value(&["B", "X"], value_fn)
1622                .block_on(),
1623            Err(FindDominatorValueError::DominatorFinderError(
1624                DominatorFinderError::UnknownNodeInTargetSet
1625            ))
1626        );
1627    }
1628
1629    #[test]
1630    fn test_find_dominator_value_with_unknown_node() {
1631        // Flow graph: A(1) -> B(2).
1632        let simple_graph = SimpleDirectedGraph::new([("A", "B")]);
1633        let flow_graph = FlowGraph::new(simple_graph, "A");
1634        let value_fn = async |node: &&str| match *node {
1635            "A" => Ok(1),
1636            "B" => Ok(2),
1637            _ => Err("Unknown node".to_string()),
1638        };
1639        assert_eq!(
1640            flow_graph
1641                .find_dominator_value(&["B", "X"], value_fn)
1642                .block_on(),
1643            Err(FindDominatorValueError::ValueFnError(
1644                "Unknown node".to_string()
1645            ))
1646        );
1647    }
1648}