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.
395struct 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    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    /// Gets all the values currently cached.
420    fn get_values(&self) -> Vec<Rc<V>> {
421        self.value_to_nodes.keys().cloned().collect_vec()
422    }
423
424    /// Returns the number of distinct values currently cached.
425    fn num_distinct_values(&self) -> usize {
426        self.value_to_nodes.len()
427    }
428
429    /// Returns the value for the given node, computing it if it is not already
430    /// cached.
431    async fn get_or_compute_value(&mut self, node: &N) -> Result<Rc<V>, E> {
432        self.compute_values([node]).await?;
433        Ok(self.node_values.get(node).expect("cached").clone())
434    }
435
436    /// Computes the value of each node, asynchronously and concurrently. Values
437    /// for nodes which were previously evaluated are skipped.
438    async fn compute_values<'a, NI>(&mut self, nodes: NI) -> Result<(), E>
439    where
440        N: 'a,
441        NI: IntoIterator<Item = &'a N>,
442    {
443        // 1. Filter out nodes already in the map and create futures for new nodes.
444        let futures = nodes
445            .into_iter()
446            .filter(|node| !self.node_values.contains_key(node))
447            .map(|node| async {
448                let value = (self.value_fn)(node).await?;
449                Ok((node.clone(), Rc::new(value)))
450            });
451        // 2. Run all new futures concurrently
452        let new_results: Vec<(N, Rc<V>)> = try_join_all(futures).await?;
453        // 3. Insert the new entries into the maps.
454        for (node, value) in new_results {
455            self.node_values.insert(node.clone(), value.clone());
456            self.value_to_nodes.entry(value).or_default().push(node);
457        }
458        Ok(())
459    }
460}
461
462impl<N> FlowGraph<N>
463where
464    N: Clone + Eq + Hash,
465{
466    /// Constructs a new FlowGraph.
467    pub fn new(graph: SimpleDirectedGraph<N>, start_node: N) -> Self {
468        Self { graph, start_node }
469    }
470
471    /// Creates a flow graph of values from a flow graph of nodes.
472    ///
473    /// More precisely, let G be a FlowGraph of nodes with start node S. The
474    /// value flow graph G' is a FlowGraph derived from G. Let v(g) be the
475    /// result of applying value_fn to g. The nodes of G' are the set of values
476    /// v(g), for all g in G. For each edge g1->g2 in G, there is a
477    /// corresponding edge v(g1)->v(g2) in G'. The start node in G' is v(S).
478    ///
479    /// Returns an error if any value_fn invocation fails.
480    pub fn create_value_flow_graph<'a, V>(&self, node_values: &'a HashMap<N, V>) -> FlowGraph<&'a V>
481    where
482        V: Eq + Hash,
483    {
484        let mut edges = vec![];
485        let start_value = node_values.get(&self.start_node).expect("cached");
486        for (parent, children) in &self.graph.adj {
487            let parent_value = node_values.get(parent).expect("cached");
488            for child in children {
489                let child_value = node_values.get(child).expect("cached");
490                edges.push((parent_value, child_value));
491            }
492        }
493        FlowGraph::new(SimpleDirectedGraph::new(edges), start_value)
494    }
495
496    /// Constructs a value flow graph from the given flow graph and value
497    /// function, and finds the closest common dominator value for the
498    /// values of the final nodes. Returns an error if value_fn returns an
499    /// error for any node in the flow graph. `final_nodes` must not be empty.
500    pub async fn find_dominator_value<V, VF, E>(
501        &self,
502        final_nodes: &[N],
503        value_fn: VF,
504    ) -> Result<V, FindDominatorValueError<E>>
505    where
506        V: Hash + Eq + Clone,
507        VF: AsyncFn(&N) -> Result<V, E>,
508    {
509        let mut value_cache = ValueCache::new(value_fn);
510        // First compute the values of all final nodes asynchronously and concurrently.
511        value_cache
512            .compute_values(final_nodes)
513            .await
514            .map_err(|e| FindDominatorValueError::ValueFnError(e))?;
515
516        let final_values = value_cache.get_values();
517        match &*final_values {
518            [] => {
519                return Err(FindDominatorValueError::DominatorFinderError(
520                    DominatorFinderError::EmptyTargetSet,
521                ));
522            }
523            [final_value] => {
524                // Optimization: if all final nodes have the same value, that value is the
525                // closest common dominator. There is no need to build the value flow graph.
526                return Ok(final_value.as_ref().clone());
527            }
528            _ => {}
529        }
530
531        let start_value = value_cache
532            .get_or_compute_value(&self.start_node)
533            .await
534            .map_err(|err| FindDominatorValueError::ValueFnError(err))?;
535
536        if value_cache.num_distinct_values() == final_values.len() {
537            // Optimization: at this point we know that the value of the start node is one
538            // of the final values (because the cardinality of the value set did not
539            // increase when we calculated the value of the start node). In such cases we
540            // know immediately that the closest common dominator is `start_value`.
541            return Ok(start_value.as_ref().clone());
542        }
543
544        // Compute all remaining values.
545        value_cache
546            .compute_values(self.graph.nodes())
547            .await
548            .map_err(|err| FindDominatorValueError::ValueFnError(err))?;
549
550        // NOTE: at this point we could compare the cardinality of the value set versus
551        // the number of nodes: if equal then we know that every node has a
552        // different value, and it is tempting to conclude that the result should be
553        // `start_value` (because the shape of the value flow graph is identical
554        // to the shape of the original flow graph). That is not always correct,
555        // consider this example with start node A and final nodes C and D:
556        //
557        // A(1) -> B(2) -> C(3)
558        //            \--> D(4)
559        //
560        // However, IF start node IS the closest common dominator of the original graph
561        // (it is not in the example above) then the answer would be `start_value`;
562        // so IF we knew that to be true we could skip building the value flow graph and
563        // running the dominator algorithm in the value flow graph.
564
565        let value_flow_graph = self.create_value_flow_graph(&value_cache.node_values);
566        let dominator_finder = DominatorFinder::calculate(&value_flow_graph)?;
567        let dominator_value =
568            dominator_finder.find_closest_common_dominator(final_values.iter())?;
569
570        Ok(dominator_value.as_ref().clone())
571    }
572}
573
574/// Traverses nodes from `start_node` in post-order.
575fn post_order<T, NI>(
576    start_node: T,
577    mut neighbors_fn: impl FnMut(&T) -> NI,
578) -> impl Iterator<Item = T>
579where
580    T: Clone + Hash + Eq,
581    NI: DoubleEndedIterator<Item = T>,
582{
583    let mut stack = vec![(start_node, false)];
584    let mut visited: HashSet<T> = HashSet::new();
585    iter::from_fn(move || {
586        while let Some((node, processed)) = stack.pop() {
587            if processed {
588                // If we marked it as processed, it means its children
589                // were already added to the stack and processed.
590                return Some(node);
591            }
592            // Mark as visited so we don't start a new DFS from here
593            if !visited.insert(node.clone()) {
594                // The node is already visited, continue.
595                continue;
596            }
597            let neighbors = neighbors_fn(&node);
598            // Push the node back onto the stack with processed = true.
599            // It will be popped and yielded AFTER its children.
600            stack.push((node, true));
601            // Push the neighbors onto the stack with processed = false. The neighbors are
602            // added in reverse order, so they are processed in the
603            // original order.
604            for neighbor in neighbors.rev() {
605                if !visited.contains(&neighbor) {
606                    stack.push((neighbor, false));
607                }
608            }
609        }
610        None
611    })
612}
613
614#[cfg(test)]
615mod tests {
616    use maplit::hashmap;
617    use pollster::FutureExt as _;
618
619    use super::*;
620
621    #[test]
622    fn test_closest_common_dominator_split() -> Result<(), DominatorFinderError> {
623        //   /-> B \
624        // A        -> D
625        //   \-> C /
626        let flow_graph = FlowGraph::new(
627            SimpleDirectedGraph::new([("A", "B"), ("A", "C"), ("B", "D"), ("C", "D")]),
628            "A",
629        );
630        let df = DominatorFinder::calculate(&flow_graph)?;
631        assert_eq!(df.find_closest_common_dominator(["A"])?, "A");
632        assert_eq!(df.find_closest_common_dominator(["B"])?, "B");
633        assert_eq!(df.find_closest_common_dominator(["C"])?, "C");
634        assert_eq!(df.find_closest_common_dominator(["D"])?, "D");
635        assert_eq!(df.find_closest_common_dominator(["B", "C"])?, "A");
636        assert_eq!(df.find_closest_common_dominator(["B", "D"])?, "A");
637        assert_eq!(df.find_closest_common_dominator(["B", "C", "D"])?, "A");
638        Ok(())
639    }
640
641    #[test]
642    fn test_closest_common_dominator_linear_chain() -> Result<(), DominatorFinderError> {
643        // A -> B -> C -> D
644        let flow_graph = FlowGraph::new(
645            SimpleDirectedGraph::new([("A", "B"), ("B", "C"), ("C", "D")]),
646            "A",
647        );
648        let df = DominatorFinder::calculate(&flow_graph)?;
649        assert_eq!(df.find_closest_common_dominator(["A"])?, "A");
650        assert_eq!(df.find_closest_common_dominator(["B"])?, "B");
651        assert_eq!(df.find_closest_common_dominator(["C"])?, "C");
652        assert_eq!(df.find_closest_common_dominator(["D"])?, "D");
653        assert_eq!(df.find_closest_common_dominator(["A", "B"])?, "A");
654        assert_eq!(df.find_closest_common_dominator(["A", "C"])?, "A");
655        assert_eq!(df.find_closest_common_dominator(["A", "D"])?, "A");
656        assert_eq!(df.find_closest_common_dominator(["B", "D"])?, "B");
657        assert_eq!(df.find_closest_common_dominator(["C", "D"])?, "C");
658        assert_eq!(df.find_closest_common_dominator(["A", "B", "C", "D"])?, "A");
659        Ok(())
660    }
661
662    #[test]
663    fn test_closest_common_dominator_classic_diamond() -> Result<(), DominatorFinderError> {
664        //      /-> B -\
665        //    A          -> D -> E
666        //      \-> C -/
667        let flow_graph = FlowGraph::new(
668            SimpleDirectedGraph::new([("A", "B"), ("A", "C"), ("B", "D"), ("C", "D"), ("D", "E")]),
669            "A",
670        );
671        let df = DominatorFinder::calculate(&flow_graph)?;
672        assert_eq!(df.find_closest_common_dominator(["B", "C"])?, "A");
673        assert_eq!(df.find_closest_common_dominator(["B", "E"])?, "A");
674        assert_eq!(df.find_closest_common_dominator(["D"])?, "D");
675        assert_eq!(df.find_closest_common_dominator(["D", "E"])?, "D");
676        assert_eq!(df.find_closest_common_dominator(["A", "D"])?, "A");
677        Ok(())
678    }
679
680    #[test]
681    fn test_closest_common_dominator_single_node() -> Result<(), DominatorFinderError> {
682        // A
683        let flow_graph = FlowGraph::new(SimpleDirectedGraph::new([("A", "A")]), "A");
684        let df = DominatorFinder::calculate(&flow_graph)?;
685        assert_eq!(df.find_closest_common_dominator(["A"])?, "A");
686        Ok(())
687    }
688
689    #[test]
690    fn test_invalid_flowgraph() {
691        //       /-> E
692        // A -> B
693        //       \-> F
694        //           ^
695        //           |
696        // C --> D --/
697        let flow_graph = FlowGraph::new(
698            SimpleDirectedGraph::new([("A", "B"), ("B", "E"), ("B", "F"), ("C", "D"), ("D", "F")]),
699            "A",
700        );
701        assert_eq!(
702            DominatorFinder::calculate(&flow_graph).err(),
703            Some(DominatorFinderError::UnreachableNodesInFlowGraph)
704        );
705    }
706
707    #[test]
708    fn test_closest_common_dominator_simple_cycle_with_entry() -> Result<(), DominatorFinderError> {
709        //
710        // A -> B -> C -> D
711        //      ^         |
712        //      |         |
713        //      \--------/
714        let flow_graph = FlowGraph::new(
715            SimpleDirectedGraph::new([("A", "B"), ("B", "C"), ("C", "D"), ("D", "B")]),
716            "A",
717        );
718        let df = DominatorFinder::calculate(&flow_graph)?;
719        assert_eq!(df.find_closest_common_dominator(["A", "B"])?, "A");
720        assert_eq!(df.find_closest_common_dominator(["A", "C"])?, "A");
721        assert_eq!(df.find_closest_common_dominator(["A", "B", "C"])?, "A");
722        assert_eq!(df.find_closest_common_dominator(["B", "C"])?, "B");
723        assert_eq!(df.find_closest_common_dominator(["B", "C", "D"])?, "B");
724        assert_eq!(df.find_closest_common_dominator(["A"])?, "A");
725        assert_eq!(df.find_closest_common_dominator(["B"])?, "B");
726        assert_eq!(df.find_closest_common_dominator(["C"])?, "C");
727        assert_eq!(df.find_closest_common_dominator(["D"])?, "D");
728        Ok(())
729    }
730
731    #[test]
732    fn test_closest_common_dominator_figure_eight_with_bridge() -> Result<(), DominatorFinderError>
733    {
734        //
735        //  A -> B -> C -> D -> E -> F -> G
736        //       ^         |    ^         |
737        //       |         |    |         |
738        //        \_______/      \_______/
739        let flow_graph = FlowGraph::new(
740            SimpleDirectedGraph::new([
741                ("A", "B"), // entry
742                ("B", "C"),
743                ("C", "D"),
744                ("D", "B"), // Loop 1
745                ("D", "E"), // Bridge
746                ("E", "F"),
747                ("F", "G"),
748                ("G", "E"), // Loop 2
749            ]),
750            "A",
751        );
752        let df = DominatorFinder::calculate(&flow_graph)?;
753        assert_eq!(df.find_closest_common_dominator(["B", "C"])?, "B");
754        assert_eq!(df.find_closest_common_dominator(["B", "D"])?, "B");
755        assert_eq!(df.find_closest_common_dominator(["B", "E"])?, "B");
756        assert_eq!(df.find_closest_common_dominator(["C", "E"])?, "C");
757        assert_eq!(df.find_closest_common_dominator(["C", "F"])?, "C");
758        assert_eq!(df.find_closest_common_dominator(["D", "E"])?, "D");
759        assert_eq!(df.find_closest_common_dominator(["D", "F"])?, "D");
760        assert_eq!(df.find_closest_common_dominator(["E", "G"])?, "E");
761        assert_eq!(df.find_closest_common_dominator(["F", "G"])?, "F");
762        Ok(())
763    }
764
765    #[test]
766    fn test_closest_common_dominator_figure_eight() -> Result<(), DominatorFinderError> {
767        //
768        //  A -> B -> C --> D   -> E -> F
769        //       ^         | ^          |
770        //       |         | |          |
771        //        \_______/  \_________/
772        let flow_graph = FlowGraph::new(
773            SimpleDirectedGraph::new([
774                ("A", "B"), // entry
775                ("B", "C"),
776                ("C", "D"),
777                ("D", "B"), // Loop 1
778                ("D", "E"),
779                ("E", "F"),
780                ("F", "D"), // Loop 2
781            ]),
782            "A",
783        );
784        let df = DominatorFinder::calculate(&flow_graph)?;
785        assert_eq!(df.find_closest_common_dominator(["B", "C"])?, "B");
786        assert_eq!(df.find_closest_common_dominator(["B", "D"])?, "B");
787        assert_eq!(df.find_closest_common_dominator(["B", "E"])?, "B");
788        assert_eq!(df.find_closest_common_dominator(["C", "D"])?, "C");
789        assert_eq!(df.find_closest_common_dominator(["C", "E"])?, "C");
790        assert_eq!(df.find_closest_common_dominator(["C", "F"])?, "C");
791        assert_eq!(df.find_closest_common_dominator(["D", "E"])?, "D");
792        assert_eq!(df.find_closest_common_dominator(["D", "F"])?, "D");
793        assert_eq!(df.find_closest_common_dominator(["E", "F"])?, "E");
794        Ok(())
795    }
796
797    #[test]
798    fn test_closest_common_dominator_entry_cycle_dominance() -> Result<(), DominatorFinderError> {
799        // A -> B -> C
800        //      ^    |
801        //      |----/
802        let flow_graph = FlowGraph::new(
803            SimpleDirectedGraph::new([("A", "B"), ("B", "C"), ("C", "B")]),
804            "A",
805        );
806        let df = DominatorFinder::calculate(&flow_graph)?;
807        assert_eq!(df.find_closest_common_dominator(["A", "B"])?, "A");
808        assert_eq!(df.find_closest_common_dominator(["A", "C"])?, "A");
809        assert_eq!(df.find_closest_common_dominator(["B", "C"])?, "B");
810        assert_eq!(df.find_closest_common_dominator(["A", "B", "C"])?, "A");
811        Ok(())
812    }
813
814    #[test]
815    fn test_closest_common_dominator_nested_loops() -> Result<(), DominatorFinderError> {
816        //           /---> E
817        //           |     |
818        //           |     |
819        // A -> B -> C <--/
820        //      ^    |
821        //      |    V
822        //      \----D
823        let flow_graph = FlowGraph::new(
824            SimpleDirectedGraph::new([
825                ("A", "B"),
826                ("B", "C"),
827                ("C", "D"),
828                ("C", "E"),
829                ("E", "C"),
830                ("D", "B"),
831            ]),
832            "A",
833        );
834        let df = DominatorFinder::calculate(&flow_graph)?;
835        assert_eq!(df.find_closest_common_dominator(["A", "B"])?, "A");
836        assert_eq!(df.find_closest_common_dominator(["A", "C"])?, "A");
837        assert_eq!(df.find_closest_common_dominator(["B", "C"])?, "B");
838        assert_eq!(df.find_closest_common_dominator(["B", "D"])?, "B");
839        assert_eq!(df.find_closest_common_dominator(["B", "E"])?, "B");
840        assert_eq!(df.find_closest_common_dominator(["C", "D"])?, "C");
841        assert_eq!(df.find_closest_common_dominator(["C", "E"])?, "C");
842        assert_eq!(df.find_closest_common_dominator(["D", "E"])?, "C");
843        assert_eq!(df.find_closest_common_dominator(["B", "C", "D"])?, "B");
844        assert_eq!(df.find_closest_common_dominator(["B", "C", "E"])?, "B");
845        assert_eq!(df.find_closest_common_dominator(["B", "D", "E"])?, "B");
846        assert_eq!(df.find_closest_common_dominator(["C", "D", "E"])?, "C");
847        assert_eq!(df.find_closest_common_dominator(["B", "C", "D", "E"])?, "B");
848        Ok(())
849    }
850
851    #[test]
852    fn test_irreducible_graph_cooper_harvey_kennedy_fig2() -> Result<(), DominatorFinderError> {
853        //        5
854        //     /    \
855        //    |      |
856        //    V      V
857        //    4      3
858        //    |      |
859        //    V      V
860        //    1 <==> 2
861        let graph = SimpleDirectedGraph::new([(1, 2), (2, 1), (3, 2), (4, 1), (5, 4), (5, 3)]);
862        let flow_graph = FlowGraph::new(graph, 5);
863        let df = DominatorFinder::calculate(&flow_graph)?;
864        assert_eq!(
865            df.get_immediate_dominators(),
866            HashMap::from([(1, 5), (2, 5), (3, 5), (4, 5), (5, 5),])
867        );
868        Ok(())
869    }
870
871    #[test]
872    fn test_irreducible_graph_cooper_harvey_kennedy_fig3() -> Result<(), DominatorFinderError> {
873        //     6
874        //   /   \
875        //  |     |
876        //  v     v
877        //  5     4 --
878        //  |     |    \
879        //  v     v     v
880        //  1 <=> 2 <=> 3
881        let graph = SimpleDirectedGraph::new([
882            (1, 2),
883            (2, 1),
884            (2, 3),
885            (3, 2),
886            (5, 1),
887            (4, 2),
888            (4, 3),
889            (6, 5),
890            (6, 4),
891        ]);
892        let flow_graph = FlowGraph::new(graph, 6);
893        let df = DominatorFinder::calculate(&flow_graph)?;
894        assert_eq!(
895            df.get_immediate_dominators(),
896            HashMap::from([(1, 6), (2, 6), (3, 6), (4, 6), (5, 6), (6, 6),])
897        );
898        assert_eq!(df.find_closest_common_dominator([2, 3])?, 6);
899        Ok(())
900    }
901
902    #[test]
903    fn test_dominator_tree_with_three_levels() -> Result<(), DominatorFinderError> {
904        // Graph taken from https://en.wikipedia.org/wiki/Dominator_(graph_theory)
905        //     1
906        //     |  /---\
907        //     v /     \
908        //     2 <--\    \
909        //    / \    \    \
910        //   /   \    \    \
911        //  |     |    |   |
912        //  v     v    |   |
913        //  3     4    |   |
914        //  |     |    |   |
915        //   \    v    |   v
916        //    --> 5 --/    6
917        //
918        let graph =
919            SimpleDirectedGraph::new([(1, 2), (2, 3), (2, 4), (2, 6), (3, 5), (4, 5), (5, 2)]);
920        let flow_graph = FlowGraph::new(graph, 1);
921        let df = DominatorFinder::calculate(&flow_graph)?;
922        assert_eq!(
923            df.get_immediate_dominators(),
924            HashMap::from([(1, 1), (2, 1), (3, 2), (4, 2), (5, 2), (6, 2),])
925        );
926        assert_eq!(df.find_closest_common_dominator([1, 6])?, 1);
927        assert_eq!(df.find_closest_common_dominator([2, 3])?, 2);
928        assert_eq!(df.find_closest_common_dominator([2, 4])?, 2);
929        assert_eq!(df.find_closest_common_dominator([2, 5])?, 2);
930        assert_eq!(df.find_closest_common_dominator([2, 6])?, 2);
931        assert_eq!(df.find_closest_common_dominator([3, 4])?, 2);
932        assert_eq!(df.find_closest_common_dominator([3, 5])?, 2);
933        assert_eq!(df.find_closest_common_dominator([3, 6])?, 2);
934        assert_eq!(df.find_closest_common_dominator([4, 5])?, 2);
935        assert_eq!(df.find_closest_common_dominator([4, 6])?, 2);
936        assert_eq!(df.find_closest_common_dominator([5, 6])?, 2);
937        assert_eq!(df.find_closest_common_dominator([2, 3, 5])?, 2);
938        assert_eq!(df.find_closest_common_dominator([3, 4, 5])?, 2);
939        assert_eq!(df.find_closest_common_dominator([3, 4, 5, 6])?, 2);
940        Ok(())
941    }
942
943    #[test]
944    fn test_big_graph_fig_18_3() -> Result<(), DominatorFinderError> {
945        // Graph taken from Modern Compiler Implementation in Java,
946        // by Appel and Palsberg, 2004
947        //
948        //           1
949        //           |
950        //           v
951        //      /--> 2 <--\
952        //      |   / \   |
953        //      |  v   v  |
954        //      \- 3   4 -/
955        //             /\
956        //            /  \
957        //           v    v
958        //     /-->  5    6
959        //    /    /   \  /
960        //   /    |     ||
961        //  /     v     vv
962        // |  /-> 8      7
963        // |  |   |      |
964        // |  |   v      v
965        // |  \-- 9      11
966        // |      |      |
967        // |      v      v
968        //  \--- 10 --> 12
969        //
970        let graph = SimpleDirectedGraph::new([
971            (1, 2),
972            (2, 3),
973            (2, 4),
974            (3, 2),
975            (4, 2),
976            (4, 5),
977            (4, 6),
978            (5, 7),
979            (5, 8),
980            (6, 7),
981            (7, 11),
982            (8, 9),
983            (9, 8),
984            (9, 10),
985            (10, 5),
986            (10, 12),
987            (11, 12),
988        ]);
989        let flow_graph = FlowGraph::new(graph, 1);
990        let df = DominatorFinder::calculate(&flow_graph)?;
991        assert_eq!(
992            df.get_immediate_dominators(),
993            HashMap::from([
994                (1, 1),
995                (2, 1),
996                (3, 2),
997                (4, 2),
998                (5, 4),
999                (6, 4),
1000                (7, 4),
1001                (8, 5),
1002                (9, 8),
1003                (10, 9),
1004                (11, 7),
1005                (12, 4)
1006            ])
1007        );
1008        assert_eq!(df.find_closest_common_dominator([6, 3])?, 2);
1009        assert_eq!(df.find_closest_common_dominator([11, 9, 12])?, 4);
1010        assert_eq!(df.find_closest_common_dominator([11, 9, 5])?, 4);
1011        assert_eq!(df.find_closest_common_dominator([11, 10])?, 4);
1012        assert_eq!(df.find_closest_common_dominator([10, 11, 12, 3, 6])?, 2);
1013        Ok(())
1014    }
1015
1016    #[test]
1017    fn test_big_graph_fig_19_8() -> Result<(), DominatorFinderError> {
1018        // Graph taken from Modern Compiler Implementation in Java,
1019        // by Appel and Palsberg, 2004
1020        //
1021        //          /----- A ----\
1022        //         |              |
1023        //         v              v
1024        //  /----> B ---\     /-> C --\
1025        //  |      |    |     |   |   |
1026        //  |      v    |     |   v   |
1027        //  |  /-- D    |     \-- E   |
1028        //  |  |   |    |         |   |
1029        //  |  |   |    |         |   |
1030        //  |  v   |    v         v   v
1031        //  |  F   \--> G           H
1032        //  |  |\       |          /
1033        //  |  | \      v         /
1034        //  |  |  \ /---J        /
1035        //  |  |   X            /
1036        //  |  | /  \          /
1037        //  |  vv    v         |
1038        //  |  I     K         |
1039        //  |  \    /          |
1040        //  |   \  /           |
1041        //  |    vv            v
1042        //  \---- L ---------> M
1043        //
1044        let graph = SimpleDirectedGraph::new([
1045            ("A", "B"),
1046            ("A", "C"),
1047            ("B", "D"),
1048            ("B", "G"),
1049            ("C", "E"),
1050            ("C", "H"),
1051            ("D", "F"),
1052            ("D", "G"),
1053            ("E", "C"),
1054            ("E", "H"),
1055            ("F", "I"),
1056            ("F", "K"),
1057            ("G", "J"),
1058            ("H", "M"),
1059            ("I", "L"),
1060            ("J", "I"),
1061            ("K", "L"),
1062            ("L", "B"),
1063            ("L", "M"),
1064        ]);
1065        let flow_graph = FlowGraph::new(graph, "A");
1066        let df = DominatorFinder::calculate(&flow_graph)?;
1067        assert_eq!(
1068            df.get_immediate_dominators(),
1069            HashMap::from([
1070                ("A", "A"),
1071                ("B", "A"),
1072                ("C", "A"),
1073                ("D", "B"),
1074                ("E", "C"),
1075                ("F", "D"),
1076                ("G", "B"),
1077                ("H", "C"),
1078                ("I", "B"),
1079                ("J", "G"),
1080                ("K", "F"),
1081                ("L", "B"),
1082                ("M", "A"),
1083            ])
1084        );
1085        assert_eq!(df.find_closest_common_dominator(["K", "L"])?, "B");
1086        assert_eq!(df.find_closest_common_dominator(["K", "C"])?, "A");
1087        assert_eq!(df.find_closest_common_dominator(["B", "G", "J"])?, "B");
1088        Ok(())
1089    }
1090
1091    #[test]
1092    fn test_closest_common_dominator_tree() -> Result<(), DominatorFinderError> {
1093        // A -> B -> C
1094        // \     \-> D
1095        //  \------> E
1096        let flow_graph = FlowGraph::new(
1097            SimpleDirectedGraph::new([("A", "B"), ("B", "C"), ("B", "D"), ("A", "E")]),
1098            "A",
1099        );
1100        let df = DominatorFinder::calculate(&flow_graph)?;
1101        assert_eq!(df.find_closest_common_dominator(["B", "C"])?, "B");
1102        assert_eq!(df.find_closest_common_dominator(["B", "E"])?, "A");
1103        assert_eq!(df.find_closest_common_dominator(["C", "D"])?, "B");
1104        assert_eq!(df.find_closest_common_dominator(["C", "E"])?, "A");
1105        assert_eq!(df.find_closest_common_dominator(["B", "C", "D"])?, "B");
1106        assert_eq!(df.find_closest_common_dominator(["C", "D", "E"])?, "A");
1107        Ok(())
1108    }
1109
1110    #[test]
1111    fn test_closest_common_dominator_bypassing_path() -> Result<(), DominatorFinderError> {
1112        // A -> B -> C -> D
1113        // |              ^
1114        // v              |
1115        // E -------------/
1116        let flow_graph = FlowGraph::new(
1117            SimpleDirectedGraph::new([("A", "B"), ("B", "C"), ("C", "D"), ("A", "E"), ("E", "D")]),
1118            "A",
1119        );
1120        let df = DominatorFinder::calculate(&flow_graph)?;
1121        assert_eq!(df.find_closest_common_dominator(["B", "C"])?, "B");
1122        assert_eq!(df.find_closest_common_dominator(["B", "D"])?, "A");
1123        assert_eq!(df.find_closest_common_dominator(["B", "E"])?, "A");
1124        assert_eq!(df.find_closest_common_dominator(["C", "D"])?, "A");
1125        assert_eq!(df.find_closest_common_dominator(["C", "E"])?, "A");
1126        assert_eq!(df.find_closest_common_dominator(["D", "E"])?, "A");
1127        assert_eq!(df.find_closest_common_dominator(["B", "C", "D"])?, "A");
1128        assert_eq!(df.find_closest_common_dominator(["C", "D", "E"])?, "A");
1129        Ok(())
1130    }
1131
1132    #[test]
1133    fn test_closest_common_dominator_self_loop_handling() -> Result<(), DominatorFinderError> {
1134        // A->A (Self loop), A->B
1135        let flow_graph = FlowGraph::new(SimpleDirectedGraph::new([("A", "A"), ("A", "B")]), "A");
1136        let df = DominatorFinder::calculate(&flow_graph)?;
1137        assert_eq!(df.find_closest_common_dominator(["A"])?, "A");
1138        Ok(())
1139    }
1140
1141    #[test]
1142    fn test_closest_common_dominator_multi_edge() -> Result<(), DominatorFinderError> {
1143        // Shape: A->B (x2), B->C.
1144        let flow_graph = FlowGraph::new(
1145            SimpleDirectedGraph::new([
1146                ("A", "B"),
1147                ("A", "B"), // Duplicate edge
1148                ("B", "C"),
1149            ]),
1150            "A",
1151        );
1152        let df = DominatorFinder::calculate(&flow_graph)?;
1153        assert_eq!(df.find_closest_common_dominator(["A"])?, "A");
1154        assert_eq!(df.find_closest_common_dominator(["B", "C"])?, "B");
1155        Ok(())
1156    }
1157
1158    #[test]
1159    fn test_closest_common_dominator_invalid_target_set() -> Result<(), DominatorFinderError> {
1160        // A -> B
1161        let flow_graph = FlowGraph::new(SimpleDirectedGraph::new([("A", "B")]), "A");
1162        let df = DominatorFinder::calculate(&flow_graph)?;
1163        assert_eq!(
1164            df.find_closest_common_dominator([]),
1165            Err(DominatorFinderError::EmptyTargetSet)
1166        );
1167        Ok(())
1168    }
1169
1170    #[test]
1171    fn test_closest_common_dominator_repeated_node() -> Result<(), DominatorFinderError> {
1172        // A -> B
1173        let flow_graph = FlowGraph::new(SimpleDirectedGraph::new([("A", "B")]), "A");
1174        let df = DominatorFinder::calculate(&flow_graph)?;
1175        assert_eq!(df.find_closest_common_dominator(["A", "B", "A", "B"])?, "A");
1176        Ok(())
1177    }
1178
1179    #[test]
1180    fn test_simple_directed_graph_nodes() {
1181        let graph = SimpleDirectedGraph::new([("A", "B"), ("B", "C")]);
1182        let nodes = graph.nodes().copied().collect_vec();
1183        assert_eq!(nodes, ["A", "B", "C"]);
1184
1185        let graph = SimpleDirectedGraph::<String>::new([]);
1186        let nodes = graph.nodes().cloned().collect_vec();
1187        assert!(nodes.is_empty());
1188    }
1189
1190    #[test]
1191    fn test_simple_directed_graph_edges() {
1192        let graph = SimpleDirectedGraph::new([("A", "B"), ("B", "C"), ("A", "C")]);
1193        let edges = graph.edges().map(|(&u, &v)| (u, v)).collect_vec();
1194        assert_eq!(edges, [("A", "B"), ("A", "C"), ("B", "C")]);
1195
1196        let graph = SimpleDirectedGraph::<String>::new([]);
1197        let edges = graph.edges().collect_vec();
1198        assert!(edges.is_empty());
1199    }
1200
1201    #[test]
1202    fn test_simple_directed_graph_adjacent_nodes() {
1203        let graph = SimpleDirectedGraph::new([("A", "B"), ("A", "C"), ("B", "D")]);
1204        assert_eq!(
1205            graph.adjacent_nodes(&"A").unwrap().copied().collect_vec(),
1206            ["B", "C"]
1207        );
1208        assert_eq!(
1209            graph.adjacent_nodes(&"B").unwrap().copied().collect_vec(),
1210            ["D"]
1211        );
1212        assert!(graph.adjacent_nodes(&"C").unwrap().next().is_none());
1213        assert!(graph.adjacent_nodes(&"Z").is_none());
1214    }
1215
1216    #[test]
1217    fn test_simple_directed_graph_contains_node() {
1218        let graph = SimpleDirectedGraph::new([("A", "B"), ("B", "C")]);
1219        assert!(graph.contains_node(&"A"));
1220        assert!(graph.contains_node(&"B"));
1221        assert!(graph.contains_node(&"C"));
1222        assert!(!graph.contains_node(&"D"));
1223    }
1224
1225    #[test]
1226    fn test_simple_directed_graph_new() {
1227        let graph = SimpleDirectedGraph::new([("A", "B"), ("A", "C"), ("B", "C"), ("A", "B")]);
1228        let nodes = graph.nodes().copied().collect_vec();
1229        assert_eq!(nodes, ["A", "B", "C"]);
1230        let edges = graph.edges().map(|(&u, &v)| (u, v)).collect_vec();
1231        assert_eq!(edges, [("A", "B"), ("A", "C"), ("B", "C")]);
1232
1233        let graph = SimpleDirectedGraph::new([("B", "C"), ("A", "B")]);
1234        let nodes = graph.nodes().copied().collect_vec();
1235        assert_eq!(nodes, ["B", "C", "A"]);
1236        let edges = graph.edges().map(|(&u, &v)| (u, v)).collect_vec();
1237        assert_eq!(edges, [("B", "C"), ("A", "B")]);
1238    }
1239
1240    #[test]
1241    fn test_flow_graph_new() {
1242        let graph = SimpleDirectedGraph::new([("A", "B")]);
1243        let flow_graph = FlowGraph::new(graph.clone(), "A");
1244        assert_eq!(flow_graph.graph, graph);
1245        assert_eq!(flow_graph.start_node, "A");
1246        let flow_graph = FlowGraph::new(graph.clone(), "C");
1247        assert_eq!(flow_graph.graph, graph);
1248        assert_eq!(flow_graph.start_node, "C");
1249    }
1250
1251    #[test]
1252    fn test_post_order() {
1253        // This graph:
1254        //  o F
1255        //  |\
1256        //  o | E
1257        //  | o D
1258        //  | o C
1259        //  | o B
1260        //  |/
1261        //  o A
1262
1263        let neighbors = hashmap! {
1264            'A' => vec![],
1265            'B' => vec!['A'],
1266            'C' => vec!['B'],
1267            'D' => vec!['C'],
1268            'E' => vec!['A'],
1269            'F' => vec!['E', 'D'],
1270        };
1271        let neighbors_fn = |node: &char| neighbors[node].iter().copied();
1272        assert_eq!(
1273            post_order('F', neighbors_fn).collect_vec(),
1274            ['A', 'E', 'B', 'C', 'D', 'F']
1275        );
1276        assert_eq!(post_order('E', neighbors_fn).collect_vec(), ['A', 'E']);
1277        assert_eq!(
1278            post_order('D', neighbors_fn).collect_vec(),
1279            ['A', 'B', 'C', 'D']
1280        );
1281        assert_eq!(post_order('A', neighbors_fn).collect_vec(), ['A']);
1282
1283        // This graph:
1284        //  o I
1285        //  |\
1286        //  | o H
1287        //  | |\
1288        //  | | o G
1289        //  | o | F
1290        //  | | o E
1291        //  o |/ D
1292        //  | o C
1293        //  o | B
1294        //  |/
1295        //  o A
1296
1297        let neighbors = hashmap! {
1298            'A' => vec![],
1299            'B' => vec!['A'],
1300            'C' => vec!['A'],
1301            'D' => vec!['B'],
1302            'E' => vec!['C'],
1303            'F' => vec!['C'],
1304            'G' => vec!['E'],
1305            'H' => vec!['F', 'G'],
1306            'I' => vec!['D', 'H'],
1307        };
1308        let neighbors_fn = |node: &char| neighbors[node].iter().copied();
1309        assert_eq!(
1310            post_order('I', neighbors_fn).collect_vec(),
1311            ['A', 'B', 'D', 'C', 'F', 'E', 'G', 'H', 'I']
1312        );
1313
1314        // This graph:
1315        //  o I
1316        //  |\
1317        //  | |\
1318        //  | | |\
1319        //  | | | o h (h > I)
1320        //  | | |/|
1321        //  | | o | G
1322        //  | |/| o f
1323        //  | o |/ e (e > I, G)
1324        //  |/| o D
1325        //  o |/ C
1326        //  | o b (b > D)
1327        //  |/
1328        //  o A
1329
1330        let neighbors = hashmap! {
1331            'A' => vec![],
1332            'b' => vec!['A'],
1333            'C' => vec!['A'],
1334            'D' => vec!['b'],
1335            'e' => vec!['C', 'b'],
1336            'f' => vec!['D'],
1337            'G' => vec!['e', 'D'],
1338            'h' => vec!['G', 'f'],
1339            'I' => vec!['C', 'e', 'G', 'h'],
1340        };
1341        let neighbors_fn = |node: &char| neighbors[node].iter().copied();
1342        assert_eq!(
1343            post_order('I', neighbors_fn).collect_vec(),
1344            ['A', 'C', 'b', 'e', 'D', 'G', 'f', 'h', 'I']
1345        );
1346
1347        // This graph:
1348        //  o G
1349        //  |\
1350        //  | o F
1351        //  o | E
1352        //  | o D
1353        //  |/
1354        //  o C
1355        //  o B
1356        //  o A
1357
1358        let neighbors = hashmap! {
1359            'A' => vec![],
1360            'B' => vec!['A'],
1361            'C' => vec!['B'],
1362            'D' => vec!['C'],
1363            'E' => vec!['C'],
1364            'F' => vec!['D'],
1365            'G' => vec!['E', 'F'],
1366        };
1367        let neighbors_fn = |node: &char| neighbors[node].iter().copied();
1368        assert_eq!(
1369            post_order('G', neighbors_fn).collect_vec(),
1370            ['A', 'B', 'C', 'E', 'D', 'F', 'G']
1371        );
1372
1373        // This graph:
1374        //  o G
1375        //  |\
1376        //  o | F
1377        //  o | E
1378        //  | o D
1379        //  |/
1380        //  o c (c > E, D)
1381        //  o B
1382        //  o A
1383
1384        let neighbors = hashmap! {
1385            'A' => vec![],
1386            'B' => vec!['A'],
1387            'c' => vec!['B'],
1388            'D' => vec!['c'],
1389            'E' => vec!['c'],
1390            'F' => vec!['E'],
1391            'G' => vec!['F', 'D'],
1392        };
1393        let neighbors_fn = |node: &char| neighbors[node].iter().copied();
1394        assert_eq!(
1395            post_order('G', neighbors_fn).collect_vec(),
1396            ['A', 'B', 'c', 'E', 'F', 'D', 'G']
1397        );
1398
1399        // This graph:
1400        //  o F
1401        //  |\
1402        //  o | E
1403        //  | o D
1404        //  | | o C
1405        //  | | |
1406        //  | | o B
1407        //  | |/
1408        //  |/
1409        //  o A
1410
1411        let neighbors = hashmap! {
1412            'A' => vec![],
1413            'B' => vec!['A'],
1414            'C' => vec!['B'],
1415            'D' => vec!['A'],
1416            'E' => vec!['A'],
1417            'F' => vec!['E', 'D'],
1418        };
1419        let neighbors_fn = |node: &char| neighbors[node].iter().copied();
1420        assert_eq!(
1421            post_order('F', neighbors_fn).collect_vec(),
1422            ['A', 'E', 'D', 'F']
1423        );
1424        assert_eq!(post_order('C', neighbors_fn).collect_vec(), ['A', 'B', 'C']);
1425
1426        // This graph:
1427        //  o D
1428        //  | \
1429        //  o | C
1430        //    o B
1431        //    o A
1432
1433        let neighbors = hashmap! {
1434            'A' => vec![],
1435            'B' => vec!['A'],
1436            'C' => vec![],
1437            'D' => vec!['C', 'B'],
1438        };
1439        let neighbors_fn = |node: &char| neighbors[node].iter().copied();
1440        assert_eq!(
1441            post_order('D', neighbors_fn).collect_vec(),
1442            ['C', 'A', 'B', 'D']
1443        );
1444
1445        // This graph:
1446        //  o C
1447        //  o B
1448        //  o A (to C)
1449
1450        let neighbors = hashmap! {
1451            'A' => vec!['C'],
1452            'B' => vec!['A'],
1453            'C' => vec!['B'],
1454        };
1455        let neighbors_fn = |node: &char| neighbors[node].iter().copied();
1456        assert_eq!(post_order('C', neighbors_fn).collect_vec(), ['A', 'B', 'C']);
1457        assert_eq!(post_order('B', neighbors_fn).collect_vec(), ['C', 'A', 'B']);
1458        assert_eq!(post_order('A', neighbors_fn).collect_vec(), ['B', 'C', 'A']);
1459    }
1460
1461    #[test]
1462    fn test_value_flow_graph_new() {
1463        // A(1) -> B(1) -> C(2)
1464        let simple_graph = SimpleDirectedGraph::new([("A", "B"), ("B", "C")]);
1465        let flow_graph = FlowGraph::new(simple_graph, "A");
1466        let node_values = HashMap::from([("A", 1), ("B", 1), ("C", 2)]);
1467        let value_flow_graph = flow_graph.create_value_flow_graph(&node_values);
1468
1469        let expected_value_edges = [(&1, &1), (&1, &2)];
1470        let expected_flow_graph =
1471            FlowGraph::new(SimpleDirectedGraph::new(expected_value_edges), &1);
1472        assert_eq!(value_flow_graph, expected_flow_graph);
1473    }
1474
1475    #[test]
1476    fn test_value_flow_graph_find_dominator_value() {
1477        // A(1) -> B(1) -> C(2) -> D(3)
1478        //          \------------> E(3)
1479        let simple_graph =
1480            SimpleDirectedGraph::new([("A", "B"), ("B", "C"), ("C", "D"), ("B", "E")]);
1481        let flow_graph = FlowGraph::new(simple_graph, "A");
1482        let value_fn = async |node: &&str| match *node {
1483            "A" | "B" => Ok(1),
1484            "C" => Ok(2),
1485            "D" | "E" => Ok(3),
1486            _ => Err("Unknown node".to_string()),
1487        };
1488
1489        // Value graph (* means node has a self-loop):
1490        //   1* -> 2 -> 3
1491        //    \         ^
1492        //     \--------|
1493        assert_eq!(
1494            flow_graph
1495                .find_dominator_value(&["D", "E"], value_fn)
1496                .block_on(),
1497            Ok(3)
1498        );
1499        assert_eq!(
1500            flow_graph
1501                .find_dominator_value(&["C", "D"], value_fn)
1502                .block_on(),
1503            Ok(1)
1504        );
1505        assert_eq!(
1506            flow_graph
1507                .find_dominator_value(&["B", "C"], value_fn)
1508                .block_on(),
1509            Ok(1)
1510        );
1511    }
1512
1513    #[test]
1514    fn test_find_dominator_value_with_distinct_values() {
1515        // A(1) -> B(2) -> C(3) -> D(4)
1516        //          \------------> E(5)
1517        let simple_graph =
1518            SimpleDirectedGraph::new([("A", "B"), ("B", "C"), ("C", "D"), ("B", "E")]);
1519        let flow_graph = FlowGraph::new(simple_graph, "A");
1520        let value_fn = async |node: &&str| match *node {
1521            "A" => Ok(1),
1522            "B" => Ok(2),
1523            "C" => Ok(3),
1524            "D" => Ok(4),
1525            "E" => Ok(5),
1526            _ => Err("Unknown node".to_string()),
1527        };
1528
1529        // Value graph:
1530        // 1 -> 2 -> 3 -> 4
1531        //       \------> 5
1532        assert_eq!(
1533            flow_graph
1534                .find_dominator_value(&["D", "E"], value_fn)
1535                .block_on(),
1536            Ok(2)
1537        );
1538        assert_eq!(
1539            flow_graph
1540                .find_dominator_value(&["C", "D"], value_fn)
1541                .block_on(),
1542            Ok(3)
1543        );
1544        assert_eq!(
1545            flow_graph
1546                .find_dominator_value(&["B", "C"], value_fn)
1547                .block_on(),
1548            Ok(2)
1549        );
1550    }
1551
1552    #[test]
1553    fn test_find_dominator_value_with_invalid_flow_graph() {
1554        // Invalid flow graph: A(1) -> B(1), C(2) -> D(2) (C and D are not reachable
1555        // from A).
1556        let simple_graph = SimpleDirectedGraph::new([("A", "B"), ("C", "D")]);
1557        let flow_graph = FlowGraph::new(simple_graph, "A");
1558        let value_fn = async |node: &&str| match *node {
1559            "A" | "B" => Ok(1),
1560            "C" | "D" => Ok(2),
1561            _ => Err("Unknown node".to_string()),
1562        };
1563        // Todo: the flow_graph is invalid because C and D are not reachable from A, so
1564        // ideally find_dominator_value should return UnreachableNodesInFlowGraph, but
1565        // the optimizations in find_dominator_value currently cause it to
1566        // return the start value. The best way to fix this is to calculate (and store)
1567        // the post-order in FlowGraph::new, that way we could not possibly construct an
1568        // invalid flow graph. This is not a big concern in practice though.
1569        assert_eq!(
1570            flow_graph
1571                .find_dominator_value(&["B", "D"], value_fn)
1572                .block_on(),
1573            Ok(1)
1574        );
1575    }
1576
1577    #[test]
1578    fn test_find_dominator_value_with_unknown_node_in_target_set() {
1579        // Flow graph: A(1) -> B(2).
1580        let simple_graph = SimpleDirectedGraph::new([("A", "B")]);
1581        let flow_graph = FlowGraph::new(simple_graph, "A");
1582        let value_fn = async |node: &&str| match *node {
1583            "A" => Ok(1),
1584            "B" => Ok(2),
1585            "X" => Ok(666),
1586            _ => Err("Unknown node".to_string()),
1587        };
1588        assert_eq!(
1589            flow_graph
1590                .find_dominator_value(&["B", "X"], value_fn)
1591                .block_on(),
1592            Err(FindDominatorValueError::DominatorFinderError(
1593                DominatorFinderError::UnknownNodeInTargetSet
1594            ))
1595        );
1596    }
1597
1598    #[test]
1599    fn test_find_dominator_value_with_unknown_node() {
1600        // Flow graph: A(1) -> B(2).
1601        let simple_graph = SimpleDirectedGraph::new([("A", "B")]);
1602        let flow_graph = FlowGraph::new(simple_graph, "A");
1603        let value_fn = async |node: &&str| match *node {
1604            "A" => Ok(1),
1605            "B" => Ok(2),
1606            _ => Err("Unknown node".to_string()),
1607        };
1608        assert_eq!(
1609            flow_graph
1610                .find_dominator_value(&["B", "X"], value_fn)
1611                .block_on(),
1612            Err(FindDominatorValueError::ValueFnError(
1613                "Unknown node".to_string()
1614            ))
1615        );
1616    }
1617}