Martin von Zweigbergkdag_walk: delete unused functions74ba3f37 months agoHistory
695 lines · 20.5 KB · Rust
RawDownload
// Copyright 2020 The Jujutsu Authors//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at//// https://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License. //! General-purpose DAG algorithms. use std::collections::HashSet;use std::convert::Infallible;use std::hash::Hash;use std::iter; use itertools::Itertools as _; /// Traverses nodes from `start` in depth-first order.pub fn dfs<T, ID, II, NI>(    start: II,    id_fn: impl Fn(&T) -> ID,    mut neighbors_fn: impl FnMut(&T) -> NI,) -> impl Iterator<Item = T>where    ID: Hash + Eq,    II: IntoIterator<Item = T>,    NI: IntoIterator<Item = T>,{    let neighbors_fn = move |node: &T| to_infallible_iter(neighbors_fn(node));    dfs_ok(to_infallible_iter(start), id_fn, neighbors_fn).map(|Ok(node)| node)} /// Traverses nodes from `start` in depth-first order.////// An `Err` is emitted as a node with no neighbors. Caller may decide to/// short-circuit on it.pub fn dfs_ok<T, ID, E, II, NI>(    start: II,    id_fn: impl Fn(&T) -> ID,    mut neighbors_fn: impl FnMut(&T) -> NI,) -> impl Iterator<Item = Result<T, E>>where    ID: Hash + Eq,    II: IntoIterator<Item = Result<T, E>>,    NI: IntoIterator<Item = Result<T, E>>,{    let mut work: Vec<Result<T, E>> = start.into_iter().collect();    let mut visited: HashSet<ID> = HashSet::new();    iter::from_fn(move || {        loop {            let c = match work.pop() {                Some(Ok(c)) => c,                r @ (Some(Err(_)) | None) => return r,            };            let id = id_fn(&c);            if visited.contains(&id) {                continue;            }            for p in neighbors_fn(&c) {                work.push(p);            }            visited.insert(id);            return Some(Ok(c));        }    })} /// Builds a list of nodes reachable from the `start` where neighbors come/// before the node itself.////// If the graph has cycle, `cycle_fn()` is called with one of the nodes/// involved in the cycle.pub fn topo_order_forward<T, ID, E, II, NI>(    start: II,    id_fn: impl Fn(&T) -> ID,    mut neighbors_fn: impl FnMut(&T) -> NI,    cycle_fn: impl FnOnce(T) -> E,) -> Result<Vec<T>, E>where    ID: Hash + Eq + Clone,    II: IntoIterator<Item = T>,    NI: IntoIterator<Item = T>,{    let neighbors_fn = move |node: &T| to_ok_iter(neighbors_fn(node));    topo_order_forward_ok(to_ok_iter(start), id_fn, neighbors_fn, cycle_fn)} /// Builds a list of `Ok` nodes reachable from the `start` where neighbors come/// before the node itself.////// If `start` or `neighbors_fn()` yields an `Err`, this function terminates and/// returns the error. If the graph has cycle, `cycle_fn()` is called with one/// of the nodes involved in the cycle.pub fn topo_order_forward_ok<T, ID, E, II, NI>(    start: II,    id_fn: impl Fn(&T) -> ID,    mut neighbors_fn: impl FnMut(&T) -> NI,    cycle_fn: impl FnOnce(T) -> E,) -> Result<Vec<T>, E>where    ID: Hash + Eq + Clone,    II: IntoIterator<Item = Result<T, E>>,    NI: IntoIterator<Item = Result<T, E>>,{    let mut stack: Vec<(T, bool)> = start.into_iter().map(|r| Ok((r?, false))).try_collect()?;    let mut visiting = HashSet::new();    let mut emitted = HashSet::new();    let mut result = vec![];    while let Some((node, neighbors_visited)) = stack.pop() {        let id = id_fn(&node);        if emitted.contains(&id) {            continue;        }        if !neighbors_visited {            if !visiting.insert(id.clone()) {                return Err(cycle_fn(node));            }            let neighbors_iter = neighbors_fn(&node).into_iter();            stack.reserve(neighbors_iter.size_hint().0 + 1);            stack.push((node, true));            for neighbor in neighbors_iter {                stack.push((neighbor?, false));            }        } else {            visiting.remove(&id);            emitted.insert(id);            result.push(node);        }    }    Ok(result)} /// Builds a list of nodes reachable from the `start` where neighbors come after/// the node itself.////// If the graph has cycle, `cycle_fn()` is called with one of the nodes/// involved in the cycle.pub fn topo_order_reverse<T, ID, E, II, NI>(    start: II,    id_fn: impl Fn(&T) -> ID,    mut neighbors_fn: impl FnMut(&T) -> NI,    cycle_fn: impl FnOnce(T) -> E,) -> Result<Vec<T>, E>where    ID: Hash + Eq + Clone,    II: IntoIterator<Item = T>,    NI: IntoIterator<Item = T>,{    let neighbors_fn = move |node: &T| to_ok_iter(neighbors_fn(node));    topo_order_reverse_ok(to_ok_iter(start), id_fn, neighbors_fn, cycle_fn)} /// Builds a list of `Ok` nodes reachable from the `start` where neighbors come/// after the node itself.////// If `start` or `neighbors_fn()` yields an `Err`, this function terminates and/// returns the error. If the graph has cycle, `cycle_fn()` is called with one/// of the nodes involved in the cycle.pub fn topo_order_reverse_ok<T, ID, E, II, NI>(    start: II,    id_fn: impl Fn(&T) -> ID,    neighbors_fn: impl FnMut(&T) -> NI,    cycle_fn: impl FnOnce(T) -> E,) -> Result<Vec<T>, E>where    ID: Hash + Eq + Clone,    II: IntoIterator<Item = Result<T, E>>,    NI: IntoIterator<Item = Result<T, E>>,{    let mut result = topo_order_forward_ok(start, id_fn, neighbors_fn, cycle_fn)?;    result.reverse();    Ok(result)} /// Find nodes in the start set that are not reachable from other nodes in the/// start set.pub fn heads<T, ID, II, NI>(    start: II,    id_fn: impl Fn(&T) -> ID,    mut neighbors_fn: impl FnMut(&T) -> NI,) -> HashSet<T>where    T: Hash + Eq + Clone,    ID: Hash + Eq,    II: IntoIterator<Item = T>,    NI: IntoIterator<Item = T>,{    let neighbors_fn = move |node: &T| to_infallible_iter(neighbors_fn(node));    let Ok(node) = heads_ok(to_infallible_iter(start), id_fn, neighbors_fn);    node} /// Finds `Ok` nodes in the start set that are not reachable from other nodes in/// the start set.////// If `start` or `neighbors_fn()` yields an `Err`, this function terminates and/// returns the error.pub fn heads_ok<T, ID, E, II, NI>(    start: II,    id_fn: impl Fn(&T) -> ID,    mut neighbors_fn: impl FnMut(&T) -> NI,) -> Result<HashSet<T>, E>where    T: Hash + Eq + Clone,    ID: Hash + Eq,    II: IntoIterator<Item = Result<T, E>>,    NI: IntoIterator<Item = Result<T, E>>,{    let mut heads: HashSet<T> = start.into_iter().try_collect()?;    // Do a BFS until we have only one item left in the frontier. That frontier must    // have originated from one of the heads, and since there can't be cycles,    // it won't be able to eliminate any other heads.    let mut frontier: Vec<T> = heads.iter().cloned().collect();    let mut visited: HashSet<ID> = heads.iter().map(&id_fn).collect();    let mut root_reached = false;    while frontier.len() > 1 || (!frontier.is_empty() && root_reached) {        frontier = frontier            .iter()            .flat_map(|node| {                let neighbors = neighbors_fn(node).into_iter().collect_vec();                if neighbors.is_empty() {                    root_reached = true;                }                neighbors            })            .try_collect()?;        for node in &frontier {            heads.remove(node);        }        frontier.retain(|node| visited.insert(id_fn(node)));    }    Ok(heads)} fn to_ok_iter<T, E>(iter: impl IntoIterator<Item = T>) -> impl Iterator<Item = Result<T, E>> {    iter.into_iter().map(Ok)} fn to_infallible_iter<T>(    iter: impl IntoIterator<Item = T>,) -> impl Iterator<Item = Result<T, Infallible>> {    to_ok_iter(iter)} #[cfg(test)]mod tests {    use assert_matches::assert_matches;    use maplit::hashmap;    use maplit::hashset;     use super::*;     #[test]    fn test_dfs_ok() {        let neighbors = hashmap! {            'A' => vec![],            'B' => vec![Ok('A'), Err('X')],            'C' => vec![Ok('B')],        };        let id_fn = |node: &char| *node;        let neighbors_fn = |node: &char| neighbors[node].clone();         // Self and neighbor nodes shouldn't be lost at the error.        let nodes = dfs_ok([Ok('C')], id_fn, neighbors_fn).collect_vec();        assert_eq!(nodes, [Ok('C'), Ok('B'), Err('X'), Ok('A')]);    }     #[test]    fn test_topo_order_reverse_linear() {        // This graph:        //  o C        //  o B        //  o A         let neighbors = hashmap! {            'A' => vec![],            'B' => vec!['A'],            'C' => vec!['B'],        };        let id_fn = |node: &char| *node;        let neighbors_fn = |node: &char| neighbors[node].clone();        let cycle_fn = |id| id;         let common = topo_order_reverse(vec!['C'], id_fn, neighbors_fn, cycle_fn).unwrap();        assert_eq!(common, vec!['C', 'B', 'A']);        let common = topo_order_reverse(vec!['C', 'B'], id_fn, neighbors_fn, cycle_fn).unwrap();        assert_eq!(common, vec!['C', 'B', 'A']);        let common = topo_order_reverse(vec!['B', 'C'], id_fn, neighbors_fn, cycle_fn).unwrap();        assert_eq!(common, vec!['C', 'B', 'A']);    }     #[test]    fn test_topo_order_reverse_merge() {        // This graph:        //  o F        //  |\        //  o | E        //  | o D        //  | o C        //  | o B        //  |/        //  o A         let neighbors = hashmap! {            'A' => vec![],            'B' => vec!['A'],            'C' => vec!['B'],            'D' => vec!['C'],            'E' => vec!['A'],            'F' => vec!['E', 'D'],        };        let id_fn = |node: &char| *node;        let neighbors_fn = |node: &char| neighbors[node].clone();        let cycle_fn = |id| id;         let common = topo_order_reverse(vec!['F'], id_fn, neighbors_fn, cycle_fn).unwrap();        assert_eq!(common, vec!['F', 'E', 'D', 'C', 'B', 'A']);        let common =            topo_order_reverse(vec!['F', 'E', 'C'], id_fn, neighbors_fn, cycle_fn).unwrap();        assert_eq!(common, vec!['F', 'D', 'E', 'C', 'B', 'A']);        let common =            topo_order_reverse(vec!['F', 'D', 'E'], id_fn, neighbors_fn, cycle_fn).unwrap();        assert_eq!(common, vec!['F', 'D', 'C', 'B', 'E', 'A']);    }     #[test]    fn test_topo_order_reverse_nested_merges() {        // This graph:        //  o I        //  |\        //  | o H        //  | |\        //  | | o G        //  | o | F        //  | | o E        //  o |/ D        //  | o C        //  o | B        //  |/        //  o A         let neighbors = hashmap! {            'A' => vec![],            'B' => vec!['A'],            'C' => vec!['A'],            'D' => vec!['B'],            'E' => vec!['C'],            'F' => vec!['C'],            'G' => vec!['E'],            'H' => vec!['F', 'G'],            'I' => vec!['D', 'H'],        };        let id_fn = |node: &char| *node;        let neighbors_fn = |node: &char| neighbors[node].clone();        let cycle_fn = |id| id;         let common = topo_order_reverse(vec!['I'], id_fn, neighbors_fn, cycle_fn).unwrap();        assert_eq!(common, vec!['I', 'D', 'B', 'H', 'F', 'G', 'E', 'C', 'A']);    }     #[test]    fn test_topo_order_reverse_nested_merges_bad_order() {        // This graph:        //  o I        //  |\        //  | |\        //  | | |\        //  | | | o h (h > I)        //  | | |/|        //  | | o | G        //  | |/| o f        //  | o |/ e (e > I, G)        //  |/| o D        //  o |/ C        //  | o b (b > D)        //  |/        //  o A         let neighbors = hashmap! {            'A' => vec![],            'b' => vec!['A'],            'C' => vec!['A'],            'D' => vec!['b'],            'e' => vec!['C', 'b'],            'f' => vec!['D'],            'G' => vec!['e', 'D'],            'h' => vec!['G', 'f'],            'I' => vec!['C', 'e', 'G', 'h'],        };        let id_fn = |node: &char| *node;        let neighbors_fn = |node: &char| neighbors[node].clone();        let cycle_fn = |id| id;         let common = topo_order_reverse(vec!['I'], id_fn, neighbors_fn, cycle_fn).unwrap();        assert_eq!(common, vec!['I', 'h', 'G', 'e', 'C', 'f', 'D', 'b', 'A']);    }     #[test]    fn test_topo_order_reverse_merge_bad_fork_order_at_root() {        // This graph:        //  o E        //  |\        //  o | D        //  | o C        //  | o B        //  |/        //  o a (a > D, B)         let neighbors = hashmap! {            'a' => vec![],            'B' => vec!['a'],            'C' => vec!['B'],            'D' => vec!['a'],            'E' => vec!['D', 'C'],        };        let id_fn = |node: &char| *node;        let neighbors_fn = |node: &char| neighbors[node].clone();        let cycle_fn = |id| id;         let common = topo_order_reverse(vec!['E'], id_fn, neighbors_fn, cycle_fn).unwrap();        assert_eq!(common, vec!['E', 'D', 'C', 'B', 'a']);    }     #[test]    fn test_topo_order_reverse_merge_and_linear() {        // This graph:        //  o G        //  |\        //  | o F        //  o | E        //  | o D        //  |/        //  o C        //  o B        //  o A         let neighbors = hashmap! {            'A' => vec![],            'B' => vec!['A'],            'C' => vec!['B'],            'D' => vec!['C'],            'E' => vec!['C'],            'F' => vec!['D'],            'G' => vec!['E', 'F'],        };        let id_fn = |node: &char| *node;        let neighbors_fn = |node: &char| neighbors[node].clone();        let cycle_fn = |id| id;         let common = topo_order_reverse(vec!['G'], id_fn, neighbors_fn, cycle_fn).unwrap();        assert_eq!(common, vec!['G', 'E', 'F', 'D', 'C', 'B', 'A']);    }     #[test]    fn test_topo_order_reverse_merge_and_linear_bad_fork_order() {        // This graph:        //  o G        //  |\        //  o | F        //  o | E        //  | o D        //  |/        //  o c (c > E, D)        //  o B        //  o A         let neighbors = hashmap! {            'A' => vec![],            'B' => vec!['A'],            'c' => vec!['B'],            'D' => vec!['c'],            'E' => vec!['c'],            'F' => vec!['E'],            'G' => vec!['F', 'D'],        };        let id_fn = |node: &char| *node;        let neighbors_fn = |node: &char| neighbors[node].clone();        let cycle_fn = |id| id;         let common = topo_order_reverse(vec!['G'], id_fn, neighbors_fn, cycle_fn).unwrap();        assert_eq!(common, vec!['G', 'F', 'E', 'D', 'c', 'B', 'A']);    }     #[test]    fn test_topo_order_reverse_merge_and_linear_bad_merge_order() {        // This graph:        //  o G        //  |\        //  o | f (f > G)        //  o | e        //  | o d (d > G)        //  |/        //  o C        //  o B

Lines 1–500 of 695.