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) -> Vec<usize> {
72        let bv_len = n * (n - 1) / 2;
73        let bit_vec = fill_bitvector(bytes, bv_len, 1);
74        Self::fill_from_triangle(&bit_vec, n)
75    }
76
77    /// Fills the adjacency bitvector from an upper triangle
78    fn fill_from_triangle(tri: &[usize], n: usize) -> Vec<usize> {
79        let mut bit_vec = vec![0; n * n];
80        let mut tri_iter = tri.iter();
81        for i in 1..n {
82            for j in 0..i {
83                let idx = i * n + j;
84                let jdx = j * n + i;
85                let val = *tri_iter.next().unwrap();
86                bit_vec[idx] = val;
87                bit_vec[jdx] = val;
88            }
89        }
90        bit_vec
91    }
92}
93impl GraphConversion for Graph {
94    /// Returns the bitvector representation of the graph
95    fn bit_vec(&self) -> &[usize] {
96        &self.bit_vec
97    }
98
99    /// Returns the number of vertices in the graph
100    fn size(&self) -> usize {
101        self.n
102    }
103
104    /// Returns true if the graph is directed
105    fn is_directed(&self) -> bool {
106        false
107    }
108}
109impl WriteGraph for Graph {}
110
111#[cfg(test)]
112mod testing {
113    use super::{Graph, GraphConversion, WriteGraph};
114
115    #[test]
116    fn test_graph_n2() {
117        let graph = Graph::from_g6("A_").unwrap();
118        assert_eq!(graph.size(), 2);
119        assert_eq!(graph.bit_vec(), &[0, 1, 1, 0]);
120    }
121
122    #[test]
123    fn test_graph_n2_empty() {
124        let graph = Graph::from_g6("A?").unwrap();
125        assert_eq!(graph.size(), 2);
126        assert_eq!(graph.bit_vec(), &[0, 0, 0, 0]);
127    }
128
129    #[test]
130    fn test_graph_n3() {
131        let graph = Graph::from_g6("Bw").unwrap();
132        assert_eq!(graph.size(), 3);
133        assert_eq!(graph.bit_vec(), &[0, 1, 1, 1, 0, 1, 1, 1, 0]);
134    }
135
136    #[test]
137    fn test_graph_n4() {
138        let graph = Graph::from_g6("C~").unwrap();
139        assert_eq!(graph.size(), 4);
140        assert_eq!(
141            graph.bit_vec(),
142            &[0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0]
143        );
144    }
145
146    #[test]
147    fn test_to_adjacency() {
148        let graph = Graph::from_g6("A_").unwrap();
149        let adj = graph.to_adjmat();
150        assert_eq!(adj, "0 1\n1 0\n");
151    }
152
153    #[test]
154    fn test_to_dot() {
155        let graph = Graph::from_g6("A_").unwrap();
156        let dot = graph.to_dot(None);
157        assert_eq!(dot, "graph {\n0 -- 1;\n}");
158    }
159
160    #[test]
161    fn test_to_dot_with_label() {
162        let graph = Graph::from_g6("A_").unwrap();
163        let dot = graph.to_dot(Some(1));
164        assert_eq!(dot, "graph graph_1 {\n0 -- 1;\n}");
165    }
166
167    #[test]
168    fn test_to_net() {
169        let repr = r"A_";
170        let graph = Graph::from_g6(repr).unwrap();
171        let net = graph.to_net();
172        assert_eq!(net, "*Vertices 2\n1 \"0\"\n2 \"1\"\n*Arcs\n1 2\n2 1\n");
173    }
174
175    #[test]
176    fn test_to_flat() {
177        let repr = r"A_";
178        let graph = Graph::from_g6(repr).unwrap();
179        let flat = graph.to_flat();
180        assert_eq!(flat, "0110");
181    }
182
183    #[test]
184    fn test_write_n2() {
185        let repr = r"A_";
186        let graph = Graph::from_g6(repr).unwrap();
187        let g6 = graph.write_graph();
188        assert_eq!(g6, repr);
189    }
190
191    #[test]
192    fn test_write_n3() {
193        let repr = r"Bw";
194        let graph = Graph::from_g6(repr).unwrap();
195        let g6 = graph.write_graph();
196        assert_eq!(g6, repr);
197    }
198
199    #[test]
200    fn test_write_n4() {
201        let repr = r"C~";
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_from_adj() {
209        let adj = &[0, 0, 1, 0];
210        let graph = Graph::from_adj(adj).unwrap();
211        assert_eq!(graph.size(), 2);
212        assert_eq!(graph.bit_vec(), &[0, 1, 1, 0]);
213        assert_eq!(graph.write_graph(), "A_");
214    }
215
216    #[test]
217    fn test_from_nonsquare_adj() {
218        let adj = &[0, 0, 1, 0, 1];
219        let graph = Graph::from_adj(adj);
220        assert!(graph.is_err());
221    }
222}