graph6_rs/
directed.rs

1use super::{GraphConversion, IOError};
2use crate::{
3    utils::{fill_bitvector, get_size},
4    WriteGraph,
5};
6
7/// Creates a directed graph from a graph6 representation
8#[derive(Debug)]
9pub struct DiGraph {
10    pub bit_vec: Vec<usize>,
11    pub n: usize,
12}
13impl DiGraph {
14    /// Creates a new DiGraph from a graph6 representation string
15    ///
16    /// # Arguments
17    /// * `repr` - A graph6 representation string
18    ///
19    /// # Errors
20    /// Returns an error if the graph6 representation is invalid (i.e. missing digraph header '&')
21    ///
22    /// # Examples
23    /// ```
24    /// use graph6_rs::DiGraph;
25    /// let graph = DiGraph::from_d6("&AG").unwrap();
26    /// assert_eq!(graph.n, 2);
27    /// assert_eq!(graph.bit_vec, &[0, 0, 1, 0]);
28    /// ```
29    pub fn from_d6(repr: &str) -> Result<Self, IOError> {
30        let bytes = repr.as_bytes();
31        Self::valid_digraph(bytes)?;
32        let n = get_size(bytes, 1)?;
33        let Some(bit_vec) = Self::build_bitvector(bytes, n) else {
34            return Err(IOError::NonCanonicalEncoding);
35        };
36        Ok(Self { bit_vec, n })
37    }
38
39    /// Creates a new DiGraph from a flattened adjacency matrix
40    ///
41    /// # Arguments
42    /// * `adj` - A flattened adjacency matrix
43    ///
44    /// # Errors
45    /// Returns an error if the adjacency matrix is invalid (i.e. not square)
46    ///
47    /// # Examples
48    /// ```
49    /// use graph6_rs::DiGraph;
50    /// let graph = DiGraph::from_adj(&[0, 0, 1, 0]).unwrap();
51    /// assert_eq!(graph.n, 2);
52    /// assert_eq!(graph.bit_vec, &[0, 0, 1, 0]);
53    /// ```
54    pub fn from_adj(adj: &[usize]) -> Result<Self, IOError> {
55        let n2 = adj.len();
56        let n = (n2 as f64).sqrt() as usize;
57        if n * n != n2 {
58            return Err(IOError::InvalidAdjacencyMatrix);
59        }
60        let bit_vec = adj.to_vec();
61        Ok(Self { bit_vec, n })
62    }
63
64    /// Validates graph6 directed representation
65    fn valid_digraph(repr: &[u8]) -> Result<bool, IOError> {
66        if repr[0] == b'&' {
67            Ok(true)
68        } else {
69            Err(IOError::InvalidDigraphHeader)
70        }
71    }
72
73    /// Iteratores through the bytes and builds a bitvector
74    /// representing the adjaceny matrix of the graph
75    fn build_bitvector(bytes: &[u8], n: usize) -> Option<Vec<usize>> {
76        let bv_len = n * n;
77        let bit_vec = fill_bitvector(bytes, bv_len, 2);
78        bit_vec
79    }
80}
81
82impl GraphConversion for DiGraph {
83    fn bit_vec(&self) -> &[usize] {
84        &self.bit_vec
85    }
86
87    fn size(&self) -> usize {
88        self.n
89    }
90
91    fn is_directed(&self) -> bool {
92        true
93    }
94}
95
96impl WriteGraph for DiGraph {}
97
98#[cfg(test)]
99mod testing {
100    use crate::WriteGraph;
101
102    use super::GraphConversion;
103
104    #[test]
105    fn test_header() {
106        let repr = b"&AG";
107        assert!(super::DiGraph::valid_digraph(repr).is_ok());
108    }
109
110    #[test]
111    fn test_invalid_header() {
112        let repr = b"AG";
113        assert!(super::DiGraph::valid_digraph(repr).is_err());
114    }
115
116    #[test]
117    fn test_from_adj() {
118        let adj = &[0, 0, 1, 0];
119        let graph = super::DiGraph::from_adj(adj).unwrap();
120        assert_eq!(graph.size(), 2);
121        assert_eq!(graph.bit_vec(), vec![0, 0, 1, 0]);
122        assert_eq!(graph.write_graph(), "&AG");
123    }
124
125    #[test]
126    fn test_from_nonsquare_adj() {
127        let adj = &[0, 0, 1, 0, 1];
128        let graph = super::DiGraph::from_adj(adj);
129        assert!(graph.is_err());
130    }
131
132    #[test]
133    /// Adjacency matrix:
134    /// 0 0
135    /// 1 0
136    fn test_bitvector_n2() {
137        let repr = "&AG";
138        let graph = super::DiGraph::from_d6(repr).unwrap();
139        assert_eq!(graph.size(), 2);
140        assert_eq!(graph.bit_vec(), vec![0, 0, 1, 0]);
141    }
142
143    #[test]
144    /// Adjacency matrix:
145    /// 0 1 1
146    /// 1 0 1
147    /// 1 1 0
148    fn test_bitvector_n3() {
149        let repr = r"&B\o";
150        let graph = super::DiGraph::from_d6(repr).unwrap();
151        assert_eq!(graph.size(), 3);
152        assert_eq!(graph.bit_vec(), vec![0, 1, 1, 1, 0, 1, 1, 1, 0]);
153    }
154
155    #[test]
156    /// Adjacency matrix:
157    /// 0 1 1 1
158    /// 1 0 1 1
159    /// 1 1 0 1
160    /// 1 1 1 0
161    fn test_bitvector_n4() {
162        let repr = r"&C]|w";
163        let graph = super::DiGraph::from_d6(repr).unwrap();
164        assert_eq!(graph.size(), 4);
165        assert_eq!(
166            graph.bit_vec(),
167            vec![0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0]
168        );
169    }
170
171    #[test]
172    fn test_init_invalid_n2() {
173        let repr = "AG";
174        let graph = super::DiGraph::from_d6(repr);
175        assert!(graph.is_err());
176    }
177
178    #[test]
179    fn test_to_adjacency() {
180        let repr = r"&C]|w";
181        let graph = super::DiGraph::from_d6(repr).unwrap();
182        let adj = graph.to_adjmat();
183        assert_eq!(adj, "0 1 1 1\n1 0 1 1\n1 1 0 1\n1 1 1 0\n");
184    }
185
186    #[test]
187    fn test_to_dot() {
188        let repr = r"&AG";
189        let graph = super::DiGraph::from_d6(repr).unwrap();
190        let dot = graph.to_dot(None);
191        assert_eq!(dot, "digraph {\n1 -> 0;\n}");
192    }
193
194    #[test]
195    fn test_to_dot_with_id() {
196        let repr = r"&AG";
197        let graph = super::DiGraph::from_d6(repr).unwrap();
198        let dot = graph.to_dot(Some(1));
199        assert_eq!(dot, "digraph graph_1 {\n1 -> 0;\n}");
200    }
201
202    #[test]
203    fn test_to_net() {
204        let repr = r"&AG";
205        let graph = super::DiGraph::from_d6(repr).unwrap();
206        let net = graph.to_net();
207        assert_eq!(net, "*Vertices 2\n1 \"0\"\n2 \"1\"\n*Arcs\n2 1\n");
208    }
209
210    #[test]
211    fn test_to_flat() {
212        let repr = r"&AG";
213        let graph = super::DiGraph::from_d6(repr).unwrap();
214        let flat = graph.to_flat();
215        assert_eq!(flat, "0010");
216    }
217
218    #[test]
219    fn test_write_n2() {
220        let repr = r"&AG";
221        let graph = super::DiGraph::from_d6(repr).unwrap();
222        let graph6 = graph.write_graph();
223        assert_eq!(graph6, repr);
224    }
225
226    #[test]
227    fn test_write_n3() {
228        let repr = r"&B\o";
229        let graph = super::DiGraph::from_d6(repr).unwrap();
230        let graph6 = graph.write_graph();
231        assert_eq!(graph6, repr);
232    }
233
234    #[test]
235    fn test_write_n4() {
236        let repr = r"&C]|w";
237        let graph = super::DiGraph::from_d6(repr).unwrap();
238        let graph6 = graph.write_graph();
239        assert_eq!(graph6, repr);
240    }
241}