From 6d9c687d701bffbf100b0f7a3e90e6760dd32b54 Mon Sep 17 00:00:00 2001
From: Leon Stichternath <leon@stichternath.net>
Date: Fri, 14 May 2021 02:02:54 +0200
Subject: [PATCH] implemented component methods and kernel

---
 src/graph/graph.rs      | 77 +++++++++++++++++++++++++++++++++++++++-
 src/kernel/component.rs | 78 +++++++++++++++++++++++++++++++++++++++++
 src/kernel/mod.rs       |  1 +
 3 files changed, 155 insertions(+), 1 deletion(-)
 create mode 100644 src/kernel/component.rs

diff --git a/src/graph/graph.rs b/src/graph/graph.rs
index 66854b9..16e9318 100644
--- a/src/graph/graph.rs
+++ b/src/graph/graph.rs
@@ -185,12 +185,51 @@ impl Graph {
         non_common.remove(&b);
         non_common
     }
+
+    /// Returns all nodes of the component that `node` is in.
+    pub fn component_of_node(&self, node: Node) -> FxHashSet<Node> {
+        let mut component = crate::new_fxhashset(0);
+        let mut nodes_to_check = vec![node];
+
+        while let Some(node) = nodes_to_check.pop() {
+            component.insert(node);
+            nodes_to_check.extend(self.neighbors(node).difference(&component));
+        }
+
+        component
+    }
+
+    /// Returns all seperated components of the graph.
+    pub fn find_all_components(&self) -> Vec<FxHashSet<Node>> {
+        let mut components = Vec::with_capacity(0);
+
+        for node in self.nodes() {
+            if components
+                .iter()
+                .any(|component: &FxHashSet<_>| component.contains(&node))
+            {
+                continue;
+            }
+            components.push(self.component_of_node(node));
+        }
+
+        components
+    }
+
+    /// Returns `true` iff `component` is complete, as in all nodes in `component` are neighboring.
+    ///
+    /// This function assumes that `component` is a valid component of the graph.
+    pub fn is_component_complete(&self, component: &FxHashSet<Node>) -> bool {
+        component
+            .iter()
+            .all(|&node| self.neighbors(node).len() == component.len() - 1)
+    }
 }
 
 #[cfg(test)]
 mod tests {
-
     use super::Graph;
+    use rustc_hash::FxHashSet;
 
     #[test]
     fn with_capacity() {
@@ -621,4 +660,40 @@ mod tests {
         assert_eq!(non_common_13.len(), 0);
         assert_eq!(non_common_23.len(), 0);
     }
+    #[test]
+    fn component_of_node_one() {
+        let mut graph = Graph::with_capacity(0);
+        graph.add_edge(0, 1);
+        graph.add_edge(0, 2);
+        graph.add_edge(0, 3);
+        graph.add_edge(1, 4);
+        graph.add_edge(2, 4);
+
+        let correct_component = (0..=4).collect::<FxHashSet<_>>();
+        let components = (0..=4).map(|node| graph.component_of_node(node));
+
+        for component in components {
+            assert_eq!(component, correct_component);
+        }
+    }
+    #[test]
+    fn component_of_node_two() {
+        let mut graph = Graph::with_capacity(0);
+        graph.add_edge(0, 1);
+        graph.add_edge(0, 2);
+        graph.add_edge(0, 3);
+        graph.add_edge(4, 5);
+        graph.add_edge(4, 6);
+
+        let correct_component_0 = (0..=3).collect::<FxHashSet<_>>();
+        let correct_component_1 = (4..=6).collect::<FxHashSet<_>>();
+        let components_0 = (0..=3).map(|node| graph.component_of_node(node));
+        let components_1 = (4..=6).map(|node| graph.component_of_node(node));
+        for component_0 in components_0 {
+            assert_eq!(component_0, correct_component_0);
+        }
+        for component_1 in components_1 {
+            assert_eq!(component_1, correct_component_1);
+        }
+    }
 }
diff --git a/src/kernel/component.rs b/src/kernel/component.rs
new file mode 100644
index 0000000..f9b6576
--- /dev/null
+++ b/src/kernel/component.rs
@@ -0,0 +1,78 @@
+use crate::{context::CepContext, CepResult};
+
+impl CepContext {
+    /// This kernel will delete all seperate complete components.
+    ///
+    /// This graph
+    /// ```text
+    /// o---o  o--o--o
+    ///  \ /      | /
+    ///   o       o
+    /// ```
+    /// will be turned into this graph.
+    /// ```text
+    ///        o--o--o
+    ///           | /
+    ///           o
+    /// ```
+    pub fn kernel_complete_components(&mut self) -> CepResult<&mut Self> {
+        for component in self.graph().find_all_components() {
+            if !self.graph().is_component_complete(&component) {
+                continue;
+            }
+            for node in component {
+                self.graph_mut().remove_node(node);
+            }
+        }
+        Ok(self)
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use crate::{context::CepContext, graph::Graph};
+
+    #[test]
+    fn kernel_complete_components_nothing() {
+        let mut graph = Graph::with_capacity(0);
+        graph.add_edge(0, 1);
+        graph.add_edge(0, 2);
+        graph.add_edge(3, 4);
+        graph.add_edge(4, 5);
+
+        let mut context = CepContext::new(graph, 0);
+        context.kernel_complete_components().unwrap();
+
+        assert!(context.permanent_edges().is_empty());
+        assert!(context.forbidden_edges().is_empty());
+        assert!(context.edits().is_empty());
+        for node in 0..=5 {
+            assert!(context.graph().node_exists(node));
+        }
+    }
+    #[test]
+    fn kernel_complete_components() {
+        let mut graph = Graph::with_capacity(0);
+        graph.add_edge(0, 1);
+        graph.add_edge(0, 2);
+        graph.add_edge(1, 2);
+        graph.add_edge(3, 4);
+        graph.add_edge(4, 5);
+        graph.add_node(6);
+
+        let mut context = CepContext::new(graph, 0);
+        context.kernel_complete_components().unwrap();
+
+        assert!(context.permanent_edges().is_empty());
+        assert!(context.forbidden_edges().is_empty());
+        assert!(context.edits().is_empty());
+
+        assert!(!context.graph().node_exists(0));
+        assert!(!context.graph().node_exists(1));
+        assert!(!context.graph().node_exists(2));
+        assert!(context.graph().node_exists(3));
+        assert!(context.graph().node_exists(4));
+        assert!(context.graph().node_exists(5));
+        assert!(!context.graph().node_exists(6));
+    }
+}
diff --git a/src/kernel/mod.rs b/src/kernel/mod.rs
index 8ce8b6e..456ea49 100644
--- a/src/kernel/mod.rs
+++ b/src/kernel/mod.rs
@@ -1 +1,2 @@
 mod bridge;
+mod component;
-- 
GitLab