| use std::borrow::Borrow; |
| use std::collections::{BTreeMap, BTreeSet, VecDeque}; |
| use std::fmt; |
| |
| pub struct Graph<N: Clone, E: Clone> { |
| nodes: im_rc::OrdMap<N, im_rc::OrdMap<N, E>>, |
| } |
| |
| impl<N: Eq + Ord + Clone, E: Default + Clone> Graph<N, E> { |
| pub fn new() -> Graph<N, E> { |
| Graph { |
| nodes: im_rc::OrdMap::new(), |
| } |
| } |
| |
| pub fn add(&mut self, node: N) { |
| self.nodes.entry(node).or_insert_with(im_rc::OrdMap::new); |
| } |
| |
| pub fn link(&mut self, node: N, child: N) -> &mut E { |
| self.nodes |
| .entry(node) |
| .or_insert_with(im_rc::OrdMap::new) |
| .entry(child) |
| .or_default() |
| } |
| |
| /// Returns the graph obtained by reversing all edges. |
| pub fn reversed(&self) -> Graph<N, E> { |
| let mut ret = Graph::new(); |
| |
| for n in self.iter() { |
| ret.add(n.clone()); |
| for (m, e) in self.edges(n) { |
| *ret.link(m.clone(), n.clone()) = e.clone(); |
| } |
| } |
| |
| ret |
| } |
| |
| pub fn contains<Q: ?Sized>(&self, k: &Q) -> bool |
| where |
| N: Borrow<Q>, |
| Q: Ord + Eq, |
| { |
| self.nodes.contains_key(k) |
| } |
| |
| pub fn edge(&self, from: &N, to: &N) -> Option<&E> { |
| self.nodes.get(from)?.get(to) |
| } |
| |
| pub fn edges(&self, from: &N) -> impl Iterator<Item = (&N, &E)> + use<'_, N, E> { |
| self.nodes.get(from).into_iter().flat_map(|x| x.iter()) |
| } |
| |
| /// A topological sort of the `Graph` |
| pub fn sort(&self) -> Vec<N> { |
| let mut ret = Vec::new(); |
| let mut marks = BTreeSet::new(); |
| |
| for node in self.nodes.keys() { |
| self.sort_inner_visit(node, &mut ret, &mut marks); |
| } |
| |
| ret |
| } |
| |
| fn sort_inner_visit(&self, node: &N, dst: &mut Vec<N>, marks: &mut BTreeSet<N>) { |
| if !marks.insert(node.clone()) { |
| return; |
| } |
| |
| for child in self.nodes[node].keys() { |
| self.sort_inner_visit(child, dst, marks); |
| } |
| |
| dst.push(node.clone()); |
| } |
| |
| pub fn iter(&self) -> impl Iterator<Item = &N> { |
| self.nodes.keys() |
| } |
| |
| pub fn len(&self) -> usize { |
| self.nodes.len() |
| } |
| |
| /// Checks if there is a path from `from` to `to`. |
| pub fn is_path_from_to<'a>(&'a self, from: &'a N, to: &'a N) -> bool { |
| let mut stack = vec![from]; |
| let mut seen = BTreeSet::new(); |
| seen.insert(from); |
| while let Some(iter) = stack.pop().and_then(|p| self.nodes.get(p)) { |
| for p in iter.keys() { |
| if p == to { |
| return true; |
| } |
| if seen.insert(p) { |
| stack.push(p); |
| } |
| } |
| } |
| false |
| } |
| |
| /// Resolves one of the paths from the given dependent package down to a leaf. |
| /// |
| /// The path return will be the shortest path, or more accurately one of the paths with the shortest length. |
| /// |
| /// Each element contains a node along with an edge except the first one. |
| /// The representation would look like: |
| /// |
| /// (Node0,) -> (Node1, Edge01) -> (Node2, Edge12)... |
| pub fn path_to_bottom<'a>(&'a self, pkg: &'a N) -> Vec<(&'a N, Option<&'a E>)> { |
| self.path_to(pkg, |s, p| s.edges(p)) |
| } |
| |
| /// Resolves one of the paths from the given dependent package up to the root. |
| /// |
| /// The path return will be the shortest path, or more accurately one of the paths with the shortest length. |
| /// |
| /// Each element contains a node along with an edge except the first one. |
| /// The representation would look like: |
| /// |
| /// (Node0,) -> (Node1, Edge01) -> (Node2, Edge12)... |
| pub fn path_to_top<'a>(&'a self, pkg: &'a N) -> Vec<(&'a N, Option<&'a E>)> { |
| self.path_to(pkg, |s, pk| { |
| // Note that this implementation isn't the most robust per se, we'll |
| // likely have to tweak this over time. For now though it works for what |
| // it's used for! |
| s.nodes |
| .iter() |
| .filter_map(|(p, adjacent)| adjacent.get(pk).map(|e| (p, e))) |
| }) |
| } |
| } |
| |
| impl<'s, N: Eq + Ord + Clone + 's, E: Default + Clone + 's> Graph<N, E> { |
| fn path_to<'a, F, I>(&'s self, pkg: &'a N, fn_edge: F) -> Vec<(&'a N, Option<&'a E>)> |
| where |
| I: Iterator<Item = (&'a N, &'a E)>, |
| F: Fn(&'s Self, &'a N) -> I, |
| 'a: 's, |
| { |
| let mut back_link = BTreeMap::new(); |
| let mut queue = VecDeque::from([pkg]); |
| let mut last = pkg; |
| |
| while let Some(p) = queue.pop_front() { |
| last = p; |
| let mut out_edges = true; |
| for (child, edge) in fn_edge(&self, p) { |
| out_edges = false; |
| back_link.entry(child).or_insert_with(|| { |
| queue.push_back(child); |
| (p, edge) |
| }); |
| } |
| if out_edges { |
| break; |
| } |
| } |
| |
| let mut result = Vec::new(); |
| let mut next = last; |
| while let Some((p, e)) = back_link.remove(&next) { |
| result.push((next, Some(e))); |
| next = p; |
| } |
| if result.iter().all(|(n, _)| n != &next) { |
| result.push((next, None)); |
| } |
| result.reverse(); |
| #[cfg(debug_assertions)] |
| { |
| for x in result.windows(2) { |
| let [(n2, _), (n1, Some(e12))] = x else { |
| unreachable!() |
| }; |
| assert!(std::ptr::eq( |
| self.edge(n1, n2).or(self.edge(n2, n1)).unwrap(), |
| *e12 |
| )); |
| } |
| let last = result.last().unwrap().0; |
| let set: Vec<_> = result.iter().map(|(k, _)| k).collect(); |
| if !fn_edge(&self, last) |
| .filter(|(e, _)| !set.contains(&e)) |
| .next() |
| .is_none() |
| { |
| self.print_for_test(); |
| unreachable!("The last element in the path should not have outgoing edges"); |
| } |
| } |
| result |
| } |
| } |
| |
| #[test] |
| fn path_to_case() { |
| let mut new = Graph::new(); |
| new.link(0, 3); |
| new.link(1, 0); |
| new.link(2, 0); |
| new.link(2, 1); |
| assert_eq!( |
| new.path_to_bottom(&2), |
| vec![(&2, None), (&0, Some(&())), (&3, Some(&()))] |
| ); |
| } |
| |
| #[test] |
| fn path_to_self() { |
| // Extracted from #12941 |
| let mut new: Graph<i32, ()> = Graph::new(); |
| new.link(0, 0); |
| assert_eq!(new.path_to_bottom(&0), vec![(&0, Some(&()))]); |
| } |
| |
| #[test] |
| fn reverse() { |
| let mut new: Graph<i32, ()> = Graph::new(); |
| new.link(0, 1); |
| new.link(0, 2); |
| |
| let mut expected: Graph<i32, ()> = Graph::new(); |
| expected.add(0); |
| expected.link(1, 0); |
| expected.link(2, 0); |
| assert_eq!(new.reversed(), expected); |
| } |
| |
| impl<N: Eq + Ord + Clone, E: Default + Clone> Default for Graph<N, E> { |
| fn default() -> Graph<N, E> { |
| Graph::new() |
| } |
| } |
| |
| impl<N: fmt::Display + Eq + Ord + Clone, E: Clone> fmt::Debug for Graph<N, E> { |
| fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { |
| writeln!(fmt, "Graph {{")?; |
| |
| for (n, e) in &self.nodes { |
| writeln!(fmt, " - {}", n)?; |
| |
| for n in e.keys() { |
| writeln!(fmt, " - {}", n)?; |
| } |
| } |
| |
| write!(fmt, "}}")?; |
| |
| Ok(()) |
| } |
| } |
| |
| impl<N: Eq + Ord + Clone, E: Clone> Graph<N, E> { |
| /// Prints the graph for constructing unit tests. |
| /// |
| /// For purposes of graph traversal algorithms the edge values do not matter, |
| /// and the only value of the node we care about is the order it gets compared in. |
| /// This constructs a graph with the same topology but with integer keys and unit edges. |
| #[cfg(debug_assertions)] |
| #[allow(clippy::print_stderr)] |
| fn print_for_test(&self) { |
| // Isolate and print a test case. |
| let names = self |
| .nodes |
| .keys() |
| .chain(self.nodes.values().flat_map(|vs| vs.keys())) |
| .collect::<BTreeSet<_>>() |
| .into_iter() |
| .collect::<Vec<_>>(); |
| let mut new = Graph::new(); |
| for n1 in self.nodes.keys() { |
| let name1 = names.binary_search(&n1).unwrap(); |
| new.add(name1); |
| for n2 in self.nodes[n1].keys() { |
| let name2 = names.binary_search(&n2).unwrap(); |
| *new.link(name1, name2) = (); |
| } |
| } |
| eprintln!("Graph for tests = {new:#?}"); |
| } |
| } |
| |
| impl<N: Eq + Ord + Clone, E: Eq + Clone> PartialEq for Graph<N, E> { |
| fn eq(&self, other: &Graph<N, E>) -> bool { |
| self.nodes.eq(&other.nodes) |
| } |
| } |
| impl<N: Eq + Ord + Clone, E: Eq + Clone> Eq for Graph<N, E> {} |
| |
| impl<N: Eq + Ord + Clone, E: Clone> Clone for Graph<N, E> { |
| fn clone(&self) -> Graph<N, E> { |
| Graph { |
| nodes: self.nodes.clone(), |
| } |
| } |
| } |