graph6_rs/
undirected.rs

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