//! This file implements a simple labelled graph without extra //! functionalities. //! //! Neither the method //! [`find_children_with_label`][crate::LabelGraph::find_children_with_label] //! nor the method [`labels_of`][crate::LabelGraph::labels_of] are //! implemented efficiently. use super::*; use crate::VLabelGraph; #[derive(Debug, Clone)] struct SLNode { children: Box<[(T, usize)]>, label: S, } impl Default for SLNode { fn default() -> Self { Self { children: Box::new([]), label: Default::default(), } } } impl SLNode { #[allow(dead_code)] fn new(children: Box<[(T, usize)]>, label: S) -> Self { Self { children, label } } fn into_builder(self) -> SLBNode { let label = self.label; let children = self.children.into_vec(); SLBNode { children, label } } fn to_builder(&self) -> SLBNode { let label = self.label; let children = self.children.to_vec(); SLBNode { children, label } } } /// Simple Labelled Graph. #[derive(Debug, Clone)] pub struct SLGraph { nodes: Box<[SLNode]>, } impl SLGraph { /// Return a builder. #[inline] pub fn builder(self) -> SLGBuilder { SLGBuilder { nodes: as IntoIterator>::into_iter(self.nodes) .map(SLNode::into_builder) .collect(), } } /// Return a builder from a reference. #[inline] pub fn builder_ref(&self) -> SLGBuilder { SLGBuilder { nodes: self.nodes.iter().map(SLNode::to_builder).collect(), } } } impl Default for SLGraph { #[inline] fn default() -> Self { let nodes = Box::new([]); Self { nodes } } } /// An iterator delegation for children of nodes of `SLGraph`. #[derive(Debug, Clone)] pub struct ChildrenIter<'a, T: GraphLabel> { iter: std::iter::Copied>, } impl<'a, T: GraphLabel> ChildrenIter<'a, T> { fn new(iter: std::iter::Copied>) -> Self { Self { iter } } } impl<'a, T: GraphLabel> Iterator for ChildrenIter<'a, T> { type Item = usize; #[inline] fn next(&mut self) -> Option { self.iter.next().map(|(_, to)| to) } #[inline] fn size_hint(&self) -> (usize, Option) { self.iter.size_hint() } } impl<'a, T: GraphLabel> ExactSizeIterator for ChildrenIter<'a, T> { #[inline] fn len(&self) -> usize { self.iter.len() } } impl Graph for SLGraph { // Not using a boxed pointer is supposed to save some allocations. type Iter<'a> = ChildrenIter<'a, T> where T: 'a, S:'a; #[inline] fn is_empty(&self) -> bool { self.nodes.is_empty() } #[inline] fn nodes_len(&self) -> usize { self.nodes.len() } #[inline] fn children_of(&self, node_id: usize) -> Result, Error> { match self.nodes.get(node_id) { Some(node) => Ok(ChildrenIter::new(node.children.iter().copied())), None => Err(Error::IndexOutOfBounds(node_id, self.nodes.len())), } } #[inline] /// Return the number of "children" of a node, or an error if the /// node is not a member of the graph. /// /// This counts edges with different labels as different edges. fn degree(&self, node_id: usize) -> Result { self.nodes .get(node_id) .ok_or(Error::IndexOutOfBounds(node_id, self.nodes.len())) .map(|node| node.children.len()) } #[inline] fn is_empty_node(&self, node_id: usize) -> Result { self.nodes .get(node_id) .ok_or(Error::IndexOutOfBounds(node_id, self.nodes.len())) .map(|node| node.children.is_empty()) } fn has_edge(&self, source: usize, target: usize) -> Result { match self.nodes.get(source) { Some(source_node) => { if self.nodes.get(target).is_none() { Err(Error::IndexOutOfBounds(target, self.nodes.len())) } else { for (_, to) in source_node.children.iter() { if *to == target { return Ok(true); } } Ok(false) } } None => Err(Error::IndexOutOfBounds(source, self.nodes.len())), } } fn print_viz(&self, filename: &str) -> Result<(), std::io::Error> { let filename = format!("output/{filename}"); let preamble = "digraph nfa { fontname=\"Helvetica,Arial,sans-serif\" node [fontname=\"Helvetica,Arial,sans-serif\", ordering=out] edge [fontname=\"Helvetica,Arial,sans-serif\"] rankdir=LR;\n"; let mut post = String::new(); for (node_id, node_label) in self.nodes.iter().map(|node| &node.label).enumerate() { post.push_str(&format!(" {node_id} [label = \"{node_label}\"]\n")); } for (source, target) in self.edges() { for label in self.edge_label(source, target).unwrap() { post.push_str(&format!(" {source} -> {target} [label = \"{label}\"]\n")); } } post.push_str("}\n"); let result = format!("{preamble}{post}"); if std::fs::metadata(&filename).is_ok() { std::fs::remove_file(&filename)?; } let mut file = std::fs::File::options() .write(true) .create(true) .truncate(true) .open(&filename)?; use std::io::Write; file.write_all(result.as_bytes()) } #[inline] fn replace_by_builder(&mut self, builder: impl Builder) { let new_graph = builder.build(); self.nodes = new_graph.nodes; } } /// An iterator delegation for children, and labels of children, of /// nodes of `SLGraph`. #[derive(Debug, Clone)] pub struct VLChildrenIter<'a, S: GraphLabel, T: GraphLabel> { iter: std::iter::Copied>, graph: &'a SLGraph, } impl<'a, S: GraphLabel, T: GraphLabel> VLChildrenIter<'a, S, T> { fn new( iter: std::iter::Copied>, graph: &'a SLGraph, ) -> Self { Self { iter, graph } } } impl<'a, S: GraphLabel, T: GraphLabel> Iterator for VLChildrenIter<'a, S, T> { type Item = (S, usize); #[inline] fn next(&mut self) -> Option { self.iter.next().and_then(|(_, to)| { let label = self.graph.nodes.get(to)?.label; Some((label, to)) }) } #[inline] fn size_hint(&self) -> (usize, Option) { self.iter.size_hint() } } impl<'a, S: GraphLabel, T: GraphLabel> ExactSizeIterator for VLChildrenIter<'a, S, T> { #[inline] fn len(&self) -> usize { self.iter.len() } } impl VLabelGraph for SLGraph { type TupleIter<'a> = VLChildrenIter<'a, S, T> where Self: 'a, T:'a, S: 'a; fn vertex_label(&self, node_id: usize) -> Result, Error> { Ok(Some( self.nodes .get(node_id) .ok_or(Error::IndexOutOfBounds(node_id, self.nodes_len()))? .label, )) } fn verlabren_of(&self, node_id: usize) -> Result, Error> { let iter = self .nodes .get(node_id) .ok_or(Error::IndexOutOfBounds(node_id, self.nodes.len()))? .children .iter() .copied(); Ok(Self::TupleIter::new(iter, self)) } } /// An iterator delegation for children of nodes of `SLGraph`. /// /// This iterates through labels and targets of edges simultaneously. #[derive(Debug, Clone)] pub struct ELChildrenIter<'a, T: GraphLabel> { iter: std::iter::Copied>, } impl<'a, T: GraphLabel> ELChildrenIter<'a, T> { fn new(iter: std::iter::Copied>) -> Self { Self { iter } } } impl<'a, T: GraphLabel> Iterator for ELChildrenIter<'a, T> { type Item = (T, usize); #[inline] fn next(&mut self) -> Option { self.iter.next() } #[inline] fn size_hint(&self) -> (usize, Option) { self.iter.size_hint() } } impl<'a, T: GraphLabel> ExactSizeIterator for ELChildrenIter<'a, T> { #[inline] fn len(&self) -> usize { self.iter.len() } } impl LabelGraph for SLGraph { type Iter<'a> = std::iter::Empty where Self:'a, T:'a; type LabelIter<'a> = std::iter::Empty<(&'a T, >::Iter<'a>)> where Self: 'a, T: 'a; type TupleIter<'a> = ELChildrenIter<'a, T> where Self: 'a, T: 'a; type EdgeLabelIter<'a> = std::option::IntoIter where Self: 'a, T: 'a; fn tuples_of(&self, node_id: usize) -> Result, Error> { self.nodes .get(node_id) .ok_or(Error::IndexOutOfBounds(node_id, self.nodes.len())) .map(|node| ELChildrenIter::new(node.children.iter().copied())) } fn edge_label(&self, source: usize, target: usize) -> Result, Error> { let nodes_len = self.nodes.len(); if let Some(source_node) = self.nodes.get(source) { if !(0..nodes_len).contains(&target) { return Err(Error::IndexOutOfBounds(target, nodes_len)); } for (label, end) in source_node.children.iter() { if *end == target { return Ok(Some(*label).into_iter()); } } Ok(None.into_iter()) } else { Err(Error::IndexOutOfBounds(source, nodes_len)) } } fn find_children_with_label( &self, _node_id: usize, _label: &T, ) -> Result<>::Iter<'_>, Error> { unimplemented!("Unsupported operation") } fn labels_of(&self, _node_id: usize) -> Result, Error> { unimplemented!("Unsupported operation") } #[inline] fn has_edge_label(&self, node_id: usize, label: &T, target: usize) -> Result { let nodes_len = self.nodes.len(); match self.nodes.get(node_id) { Some(node) => { if target >= nodes_len { return Err(Error::IndexOutOfBounds(target, nodes_len)); } for (edge_label, end) in node.children.iter() { if *edge_label == *label && *end == target { return Ok(true); } } Ok(false) } None => Err(Error::IndexOutOfBounds(node_id, nodes_len)), } } } impl SLGBuilder { /// Add a new node with `edges` and `label`. /// /// If an edge from `edges` points to a non-existent node, return /// an error. pub fn extend( &mut self, label: S, edges: impl IntoIterator, ) -> Result { let mut children = Vec::new(); for (label, to) in edges { if !self.has_node(to) { return Err(Error::IndexOutOfBounds(to, self.nodes.len())); } children.push((label, to)); } let new_node = SLBNode::new(children, label); let new_index = self.nodes_len(); self.nodes.push(new_node); Ok(new_index) } } // Builder for this implementation of graph /// The type of nodes for builders. #[derive(Debug, Clone)] struct SLBNode { children: Vec<(T, usize)>, label: S, } impl SLBNode { fn new(children: Vec<(T, usize)>, label: S) -> Self { Self { children, label } } fn into_boxed(self) -> SLNode { let label = self.label; let children = self.children.into_boxed_slice(); SLNode { children, label } } } /// A builder for simple labelled graphs. #[derive(Debug, Clone)] pub struct SLGBuilder { nodes: Vec>, } impl Default for SLGBuilder { fn default() -> Self { Self { nodes: Vec::new() } } } impl Graph for SLGBuilder { // Not using a boxed pointer is supposed to save some allocations. type Iter<'a> = ChildrenIter<'a, T> where T: 'a, S:'a; #[inline] fn is_empty(&self) -> bool { self.nodes.is_empty() } #[inline] fn nodes_len(&self) -> usize { self.nodes.len() } #[inline] fn children_of(&self, node_id: usize) -> Result, Error> { match self.nodes.get(node_id) { Some(node) => Ok(ChildrenIter::new(node.children.iter().copied())), None => Err(Error::IndexOutOfBounds(node_id, self.nodes.len())), } } #[inline] /// Return the number of "children" of a node, or an error if the /// node is not a member of the graph. /// /// This counts edges with different labels as different edges. fn degree(&self, node_id: usize) -> Result { self.nodes .get(node_id) .ok_or(Error::IndexOutOfBounds(node_id, self.nodes.len())) .map(|node| node.children.len()) } #[inline] fn is_empty_node(&self, node_id: usize) -> Result { self.nodes .get(node_id) .ok_or(Error::IndexOutOfBounds(node_id, self.nodes.len())) .map(|node| node.children.is_empty()) } fn has_edge(&self, source: usize, target: usize) -> Result { match self.nodes.get(source) { Some(source_node) => { if self.nodes.get(target).is_none() { Err(Error::IndexOutOfBounds(target, self.nodes.len())) } else { for (_, to) in source_node.children.iter() { if *to == target { return Ok(true); } } Ok(false) } } None => Err(Error::IndexOutOfBounds(source, self.nodes.len())), } } fn print_viz(&self, filename: &str) -> Result<(), std::io::Error> { let filename = format!("output/{filename}"); let preamble = "digraph nfa { fontname=\"Helvetica,Arial,sans-serif\" node [fontname=\"Helvetica,Arial,sans-serif\", ordering=out] edge [fontname=\"Helvetica,Arial,sans-serif\"] rankdir=LR;\n"; let mut post = String::new(); for (node_id, node_label) in self.nodes.iter().map(|node| &node.label).enumerate() { post.push_str(&format!(" {node_id} [label = \"{node_label}\"]\n")); } for (source, target) in self.edges() { for label in self.edge_label(source, target).unwrap() { post.push_str(&format!(" {source} -> {target} [label = \"{label}\"]\n")); } } post.push_str("}\n"); let result = format!("{preamble}{post}"); if std::fs::metadata(&filename).is_ok() { std::fs::remove_file(&filename)?; } let mut file = std::fs::File::options() .write(true) .create(true) .truncate(true) .open(&filename)?; use std::io::Write; file.write_all(result.as_bytes()) } #[inline] fn replace_by_builder(&mut self, _builder: impl Builder) { unimplemented!() } } /// An iterator delegation for children, and labels of children, of /// nodes of `SLGBuilder`. #[derive(Debug, Clone)] pub struct VLBChildrenIter<'a, S: GraphLabel, T: GraphLabel> { iter: std::iter::Copied>, builder: &'a SLGBuilder, } impl<'a, S: GraphLabel, T: GraphLabel> VLBChildrenIter<'a, S, T> { fn new( iter: std::iter::Copied>, builder: &'a SLGBuilder, ) -> Self { Self { iter, builder } } } impl<'a, S: GraphLabel, T: GraphLabel> Iterator for VLBChildrenIter<'a, S, T> { type Item = (S, usize); #[inline] fn next(&mut self) -> Option { self.iter.next().and_then(|(_, to)| { let label = self.builder.nodes.get(to)?.label; Some((label, to)) }) } #[inline] fn size_hint(&self) -> (usize, Option) { self.iter.size_hint() } } impl<'a, S: GraphLabel, T: GraphLabel> ExactSizeIterator for VLBChildrenIter<'a, S, T> { #[inline] fn len(&self) -> usize { self.iter.len() } } impl VLabelGraph for SLGBuilder { type TupleIter<'a> = VLBChildrenIter<'a, S,T> where Self: 'a, T:'a, S: 'a; fn vertex_label(&self, node_id: usize) -> Result, Error> { Ok(Some( self.nodes .get(node_id) .ok_or(Error::IndexOutOfBounds(node_id, self.nodes_len()))? .label, )) } fn verlabren_of(&self, node_id: usize) -> Result, Error> { let iter = self .nodes .get(node_id) .ok_or(Error::IndexOutOfBounds(node_id, self.nodes.len()))? .children .iter() .copied(); Ok(Self::TupleIter::new(iter, self)) } } impl LabelGraph for SLGBuilder { type Iter<'a> = std::iter::Empty where Self:'a, T:'a; type LabelIter<'a> = std::iter::Empty<(&'a T, >::Iter<'a>)> where Self: 'a, T: 'a; type TupleIter<'a> = ELChildrenIter<'a,T> where Self: 'a, T: 'a; type EdgeLabelIter<'a> = std::option::IntoIter where Self: 'a, T: 'a; fn tuples_of(&self, node_id: usize) -> Result, Error> { self.nodes .get(node_id) .ok_or(Error::IndexOutOfBounds(node_id, self.nodes.len())) .map(|node| ELChildrenIter::new(node.children.iter().copied())) } fn edge_label(&self, source: usize, target: usize) -> Result, Error> { let nodes_len = self.nodes.len(); if let Some(source_node) = self.nodes.get(source) { if !(0..nodes_len).contains(&target) { return Err(Error::IndexOutOfBounds(target, nodes_len)); } for (label, end) in source_node.children.iter() { if *end == target { return Ok(Some(*label).into_iter()); } } Ok(None.into_iter()) } else { Err(Error::IndexOutOfBounds(source, nodes_len)) } } fn find_children_with_label( &self, _node_id: usize, _label: &T, ) -> Result<>::Iter<'_>, Error> { unimplemented!("Unsupported operation") } fn labels_of(&self, _node_id: usize) -> Result, Error> { unimplemented!("Unsupported operation") } #[inline] fn has_edge_label(&self, node_id: usize, label: &T, target: usize) -> Result { let nodes_len = self.nodes.len(); match self.nodes.get(node_id) { Some(node) => { if target >= nodes_len { return Err(Error::IndexOutOfBounds(target, nodes_len)); } for (edge_label, end) in node.children.iter() { if *edge_label == *label && *end == target { return Ok(true); } } Ok(false) } None => Err(Error::IndexOutOfBounds(node_id, nodes_len)), } } } impl SLGBuilder { /// Return a builder with a fixed capacity. #[inline] pub fn with_capacity(size: usize) -> Self { SLGBuilder { nodes: Vec::with_capacity(size), } } /// Add a vertex with a given label. #[inline] pub fn add_vertex(&mut self, label: S) -> usize { self.nodes.push(SLBNode::new(Vec::new(), label)); self.nodes.len() - 1 } /// Add vertices with an iterator of labels. #[inline] pub fn add_vertices(&mut self, labels: impl Iterator) { self.nodes .extend(labels.map(|label| SLBNode::new(Vec::new(), label))); } /// Add an edge from the source to the target. pub fn add_edge(&mut self, source: usize, target: usize, label: T) -> Result<(), Error> { let nodes_len = self.nodes.len(); let source_node = self .nodes .get_mut(source) .ok_or(Error::IndexOutOfBounds(source, nodes_len))?; if !(0..nodes_len).contains(&target) { return Err(Error::IndexOutOfBounds(target, nodes_len)); } source_node.children.push((label, target)); Ok(()) } /// Remove an edge from the source to the target. /// /// Since some graphs are labelled, the users are allowed to pass /// a predicate to determine if an edge from the source to the /// target should be removed. pub fn remove_edge( &mut self, source: usize, target: usize, mut predicate: F, ) -> Result<(), Error> where F: FnMut(T) -> bool, { let nodes_len = self.nodes.len(); let source_node = self .nodes .get_mut(source) .ok_or(Error::IndexOutOfBounds(source, nodes_len))?; if !(0..nodes_len).contains(&target) { return Err(Error::IndexOutOfBounds(target, nodes_len)); } source_node .children .retain(|(label, end)| !(*end == target && predicate(*label))); Ok(()) } /// Convert the builder into a graph using a reference. /// /// This is similar to [`build`][Builder::build], but takes an /// immutable reference of the builder, so that the builder can /// still be used later on. pub fn build_ref(&self) -> SLGraph { let nodes = self .nodes .clone() .into_iter() .map(SLBNode::into_boxed) .collect::>() .into_boxed_slice(); SLGraph { nodes } } /// Convert the builder into a graph. /// /// This is the purpose of having a builder. pub fn build(self) -> SLGraph { let nodes = self .nodes .into_iter() .map(SLBNode::into_boxed) .collect::>() .into_boxed_slice(); SLGraph { nodes } } } #[cfg(test)] mod simple_test { use super::*; use std::collections::BTreeSet as Set; macro_rules! set { () => { Set::::default() }; ($($num:literal),*) => { { let mut set: Set = Set::default(); $(set.insert($num);)* set } }; } // macro_rules! map { // () => { Map::>::default() }; // ($(($key:literal, $value:expr)),*) => { // { // let mut map: Map> = Map::default(); // $(map.insert($key, $value);)* // map // } // }; // } #[test] fn test_graph_apis() -> Result<(), Error> { let mut builder: SLGBuilder = Default::default(); // testing empty graph assert!(builder.is_empty()); // testing adding an empty node assert_eq!(builder.extend(0, std::iter::empty())?, 0); // testing nodes_len assert_eq!(builder.nodes_len(), 1); // testing extension assert_eq!(builder.extend(1, [(0, 0)].iter().copied())?, 1); assert_eq!(builder.extend(2, [(1, 0), (1, 1)].iter().copied())?, 2); assert_eq!(builder.extend(3, [(3, 0), (3, 2)].iter().copied())?, 3); assert_eq!(builder.extend(4, [(1, 1), (1, 2)].iter().copied())?, 4); assert_eq!( builder.extend(5, [(2, 1), (3, 2), (2, 3)].iter().copied())?, 5 ); // testing adding a duplicated edge set assert_eq!( builder.extend(6, [(2, 1), (2, 3), (3, 2)].iter().copied())?, 6 ); assert_eq!(builder.extend(7, [(3, 2), (3, 0)].iter().copied())?, 7); let graph = builder.build(); // ensuring the correct length assert_eq!(graph.nodes_len(), 8); // testing children_of assert_eq!(graph.children_of(5)?.collect::>(), set!(1, 3, 2)); // testing find_children_with_label assert!(std::panic::catch_unwind(|| { graph.find_children_with_label(5, &2) }).is_err()); // testing edge_label assert_eq!(graph.edge_label(5, 2)?.collect::>(), set!(3)); assert!(matches!( graph.edge_label(8, 2), Err(Error::IndexOutOfBounds(8, 8)) )); // testing degree assert_eq!(graph.degree(4)?, 2); // testing is_empty_node assert!(graph.is_empty_node(0)?); assert!(!graph.is_empty_node(1)?); // testing has_edge assert!(graph.has_edge(3, 2)?); assert!(!graph.has_edge(3, 1)?); assert!(matches!( graph.has_edge(3, 8), Err(Error::IndexOutOfBounds(8, 8)) )); // testing labels_of assert!(std::panic::catch_unwind(|| { graph.labels_of(5) }).is_err()); Ok(()) } } #[cfg(test)] mod test_slgraph_builder { use super::*; #[test] fn test_builder() -> Result<(), Box> { let mut builder = SLGBuilder::::default(); // Add five nodes builder.add_vertices(0..5); // Link each node to its successor and link the last node with // the first one to form a cycle. for i in 0..5 { builder.add_edge(i, if i < 4 { i + 1 } else { 0 }, i)?; } // Remove the link from the last node to the first node. builder.remove_edge(4, 0, |_| true)?; // build a graph let graph = builder.build(); println!("final graph: {graph:?}"); Ok(()) } #[test] fn test_errors() -> Result<(), Box> { let mut builder = SLGBuilder::::default(); // Add five nodes builder.add_vertices(0..5); // Errors in add_edge assert!(matches!( builder.add_edge(0, 5, 0), Err(Error::IndexOutOfBounds(5, 5)) )); assert!(matches!( builder.add_edge(10, 5, 0), Err(Error::IndexOutOfBounds(10, 5)) )); assert!(matches!( builder.add_edge(10, 50, 0), Err(Error::IndexOutOfBounds(10, 5)) )); // Errors in remove_edge assert!(matches!( builder.remove_edge(0, 5, |_| true), Err(Error::IndexOutOfBounds(5, 5)) )); assert!(matches!( builder.remove_edge(10, 5, |_| true), Err(Error::IndexOutOfBounds(10, 5)) )); assert!(matches!( builder.remove_edge(10, 50, |_| true), Err(Error::IndexOutOfBounds(10, 5)) )); assert!(matches!(builder.remove_edge(0, 1, |_| true), Ok(()),)); Ok(()) } }