From a3a8749f54c3ca3578fa4b0668ed78fe9d45137e Mon Sep 17 00:00:00 2001 From: Bert Peters Date: Sat, 13 Feb 2021 20:48:34 +0100 Subject: [PATCH] Implement very simple digraph with cycle detection --- .gitignore | 2 + Cargo.toml | 9 +++++ src/lib.rs | 116 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 127 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 src/lib.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..96ef6c0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..5dcaeb9 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "tracing-mutex" +version = "0.1.0" +authors = ["Bert Peters "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..5b18f0d --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,116 @@ +use std::collections::HashMap; +use std::collections::HashSet; + +#[derive(Clone, Default, Debug)] +struct DiGraph { + in_edges: HashMap>, + out_edges: HashMap>, +} + +impl DiGraph { + fn add_node(&mut self, node: usize) -> (&mut Vec, &mut Vec) { + let in_edges = self.in_edges.entry(node).or_default(); + let out_edges = self.out_edges.entry(node).or_default(); + + (in_edges, out_edges) + } + + pub fn remove_node(&mut self, node: usize) -> bool { + match self.out_edges.remove(&node) { + None => false, + Some(out_edges) => { + for other in out_edges { + self.in_edges + .get_mut(&other) + .unwrap() + .retain(|c| c != &node); + } + + for other in self.in_edges.remove(&node).unwrap() { + self.out_edges + .get_mut(&other) + .unwrap() + .retain(|c| c != &node); + } + + true + } + } + } + + pub fn add_edge(&mut self, from: usize, to: usize) -> bool { + if from == to { + return false; + } + + let (_, out_edges) = self.add_node(from); + + out_edges.push(to); + + let (in_edges, _) = self.add_node(to); + + // No need for existence check assuming the datastructure is consistent + in_edges.push(from); + + true + } + + pub fn has_cycles(&self) -> bool { + let mut marks = HashSet::new(); + let mut temp = HashSet::new(); + + self.out_edges + .keys() + .copied() + .any(|node| !self.visit(node, &mut marks, &mut temp)) + } + + fn visit(&self, node: usize, marks: &mut HashSet, temp: &mut HashSet) -> bool { + if marks.contains(&node) { + return true; + } + + if !temp.insert(node) { + return false; + } + + if self.out_edges[&node] + .iter() + .copied() + .any(|node| !self.visit(node, marks, temp)) + { + return false; + } + + temp.remove(&node); + + marks.insert(node); + + true + } +} + +#[cfg(test)] +mod tests { + use crate::DiGraph; + + #[test] + fn test_digraph() { + let mut graph = DiGraph::default(); + + graph.add_edge(1, 2); + graph.add_edge(2, 3); + graph.add_edge(3, 4); + graph.add_edge(5, 2); + + assert!(!graph.has_cycles()); + + graph.add_edge(4, 2); + + assert!(graph.has_cycles()); + + assert!(graph.remove_node(4)); + + assert!(!graph.has_cycles()) + } +}