graph6/
lib.rs

1//! # graph6
2//!
3//! `graph6` is a library for converting between strings in graph6 format and adjacency matrices.
4
5/// Converts a string in graph6 format to its adjacency matrix, and returns the matrix and the graph size.
6///
7/// The returned adjacency matrix is a vector with elements stored in row-major order. Note, this function only supports graphs with less than 63 vertices.
8///
9/// # Panics
10///
11/// Panics if the graph has more than 62 vertices.
12///
13/// # Example
14///
15/// ```
16/// let graph = String::from("BW");
17/// let (adjacency_matrix, size) = graph6::string_to_adjacency_matrix(&graph);
18///
19/// assert_eq!(adjacency_matrix, vec![0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0]);
20/// assert_eq!(size, 3);
21/// ```
22pub fn string_to_adjacency_matrix(graph_str: &str) -> (Vec<f32>, usize) {
23    let bytes = graph_str.as_bytes();
24
25    let size = size_from_bytes(bytes).unwrap_or_else(|err| {
26        panic!("{}", err);
27    });
28
29    let mut bit_vec: Vec<u8> = Vec::new();
30    bytes.iter().skip(1).map(|byte| byte - 63).for_each(|byte| {
31        for shift in (0..6).rev() {
32            if (byte & 1 << shift) > 0 {
33                bit_vec.push(1);
34            } else {
35                bit_vec.push(0);
36            }
37        }
38    });
39
40    let adjusted_bit_vec_len = bit_vec.len() - (bit_vec.len() - (size * (size - 1)) / 2);
41
42    let mut bit_vec_iter = bit_vec[0..adjusted_bit_vec_len].iter();
43
44    let mut adjacency_matrix = vec![0.0; size * size];
45    for i in 1..size {
46        for j in 0..i {
47            if *(bit_vec_iter.next().unwrap()) == 1 {
48                adjacency_matrix[i * size + j] = 1.0;
49                adjacency_matrix[j * size + i] = 1.0;
50            }
51        }
52    }
53
54    (adjacency_matrix, size)
55}
56
57/// Converts an adjacency matrix to its string representation in graph6 format.
58///
59/// The adjacency matrix must be a vector with elements stored in row-major order.
60///
61/// # Panics
62///
63/// Panics if `size` is greater than 258,047.
64///
65/// # Example
66///
67/// ```
68/// let adjacency_matrix = vec![0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0];
69/// let size = 3;
70/// let graph = graph6::adjacency_matrix_to_string(&adjacency_matrix, size);
71///
72/// assert_eq!(graph, String::from("BW"));
73/// ```
74pub fn adjacency_matrix_to_string(adjacency_matrix: &[f32], size: usize) -> String {
75    let mut size_bytes = size_to_bytes(size).unwrap_or_else(|err| {
76        panic!("{}", err);
77    });
78
79    let mut bit_vec = Vec::new();
80    for i in 1..size {
81        for j in 0..i {
82            if adjacency_matrix[i * size + j] == 1.0 {
83                bit_vec.push(1);
84            } else {
85                bit_vec.push(0);
86            }
87        }
88    }
89
90    let mut bytes = bit_vec_to_bytes(bit_vec);
91
92    size_bytes.append(&mut bytes);
93
94    unsafe { String::from_utf8_unchecked(size_bytes) }
95}
96
97/// Converts an integer to its bit vector representation.
98fn int_to_bit_vec(mut x: usize) -> Vec<u8> {
99    let mut bit_vec = Vec::new();
100
101    while x != 0 {
102        if x & 1 == 1 {
103            bit_vec.push(1);
104        } else {
105            bit_vec.push(0);
106        }
107
108        x >>= 1;
109    }
110
111    bit_vec.reverse();
112
113    bit_vec
114}
115
116/// Converts a bit vector to a vector of "bytes" (each element of the vector is a group of six bits).
117fn bit_vec_to_bytes(mut bit_vec: Vec<u8>) -> Vec<u8> {
118    // The length of bit_vec must be a multiple of six. If it isn't, bit_vec is padded with zeroes to round its length to the next multiple of six.
119    let next_multiple_of_six = bit_vec.len() - 1 - (bit_vec.len() - 1) % 6 + 6;
120    for _ in 0..(next_multiple_of_six - bit_vec.len()) {
121        bit_vec.push(0);
122    }
123
124    let mut bytes = Vec::new();
125    let mut bit_vec_iter = bit_vec.iter();
126
127    for _ in 0..(bit_vec.len() / 6) {
128        let mut byte: u8 = 0;
129        for shift in (0..6).rev() {
130            // Since the number of loop iterations is equal to the length of bit_vec, the call to unwrap will never panic.
131            byte |= bit_vec_iter.next().unwrap() << shift;
132        }
133        bytes.push(byte);
134    }
135
136    bytes.iter().map(|byte| byte + 63).collect()
137}
138
139/// Returns the size of a graph given its string representation.
140fn size_from_bytes(bytes: &[u8]) -> Result<usize, &'static str> {
141    // The first byte is 126 for graphs with more than 62 vertices.
142    if bytes[0] == 126 {
143        return Err("size is greater than 62");
144    }
145
146    Ok((bytes[0] - 63) as usize)
147}
148
149/// Converts an integer, i.e., the size of a graph, to its representation in bytes.
150fn size_to_bytes(size: usize) -> Result<Vec<u8>, &'static str> {
151    match size {
152        s if s <= 62 => Ok(vec![size as u8 + 63]),
153        s if s <= 258047 => {
154            let mut size_bytes = vec![126];
155            size_bytes.append(&mut bit_vec_to_bytes(int_to_bit_vec(size)));
156            Ok(size_bytes)
157        }
158        _ => Err("size is greater than 258,047"),
159    }
160}
161
162#[cfg(test)]
163mod tests {
164    use super::*;
165
166    #[test]
167    fn size_four_graph_to_adjacency_matrix() {
168        let graph_str = String::from("C]");
169
170        let (adjacency_matrix, size) = string_to_adjacency_matrix(&graph_str);
171
172        assert_eq!(
173            adjacency_matrix,
174            vec![0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0]
175        );
176        assert_eq!(size, 4);
177    }
178
179    #[test]
180    fn size_120000_to_bytes() {
181        let size = 120000;
182        let bytes = vec![126, 121, 101, 63];
183
184        assert_eq!(size_to_bytes(size).unwrap(), bytes);
185    }
186}