snark_tool/service/io/
reader_g6.rs

1use std::str::Chars;
2
3use crate::graph::graph::{Graph, GraphConstructor};
4use crate::service::io::error::ReadError;
5use crate::service::io::reader::GraphFileReader;
6use std::io::{BufRead, BufReader};
7use std::marker::PhantomData;
8use std::{fs, io, result};
9
10pub const BIAS: u8 = 63;
11pub const SMALLN: u64 = 62;
12const WRONG_FORMAT: &str = "Wrong g6 format";
13
14type Result<T> = result::Result<T, ReadError>;
15
16pub struct G6Reader<'a, G>
17where
18    G: Graph,
19{
20    lines: io::Lines<BufReader<&'a fs::File>>,
21    _ph: PhantomData<G>,
22}
23
24impl<'a, G> GraphFileReader<'a, G> for G6Reader<'a, G>
25where
26    G: Graph + GraphConstructor,
27{
28    fn new(file: &'a fs::File) -> Self {
29        G6Reader {
30            lines: io::BufReader::new(file).lines(),
31            _ph: PhantomData,
32        }
33    }
34
35    fn next(&mut self) -> Option<Result<G>> {
36        let line_opt = self.lines.next();
37        match line_opt {
38            None => {
39                // warn - file contains less graphs than specified to work with
40                return None;
41            }
42            Some(line) => {
43                if let Ok(line_str) = line {
44                    if line_str.trim().is_empty() {
45                        return self.next();
46                    }
47                    let graph = G6Reader::read_graph(line_str);
48                    return Some(graph);
49                } else {
50                    // skip error lines?
51                    return self.next();
52                }
53            }
54        }
55    }
56}
57
58impl<'a, G> G6Reader<'a, G>
59where
60    G: Graph + GraphConstructor,
61{
62    pub fn read_graph(source: impl AsRef<str>) -> Result<G> {
63        let mut iterator = source.as_ref().chars();
64        let size = get_graph_size(&mut iterator);
65        let graph = G6Reader::create_graph(&mut iterator, size? as u32)?;
66        Ok(graph)
67    }
68
69    fn create_graph(iterator: &mut Chars, size: u32) -> Result<G> {
70        let vertices = size as usize;
71        let edges = (size * 3 / 2) as usize;
72        let mut graph = G::with_capacity(vertices, edges);
73        let mut char = iterator.next();
74        let mut position = Position { row: 0, column: 1 };
75        while char != None {
76            let char_num = G6Reader::<G>::extract_char(char.unwrap())?;
77            let bits = format!("{:b}", char_num);
78            for _i in 0..(6 - bits.len()) {
79                position.increment();
80            }
81            for char in bits.chars() {
82                if char == '1' {
83                    graph.add_edge(position.row, position.column);
84                }
85                position.increment();
86            }
87            char = iterator.next();
88        }
89        Ok(graph)
90    }
91
92    fn extract_char(char: char) -> Result<u8> {
93        let char_num = char as u8;
94        if char_num < BIAS || char_num > BIAS * 2 {
95            return Err(ReadError {
96                message: format!(
97                    "{} `{}` is not allowed character",
98                    WRONG_FORMAT, char_num as char
99                ),
100            });
101        }
102        Ok(char_num - BIAS)
103    }
104}
105
106#[derive(Debug)]
107pub struct Position {
108    pub row: usize,
109    pub column: usize,
110}
111
112impl Position {
113    pub fn increment(&mut self) {
114        if (self.row + 1) == self.column {
115            self.column += 1;
116            self.row = 0;
117        } else {
118            self.row += 1;
119        }
120    }
121}
122
123pub fn get_graph_size(iterator: &mut Chars) -> Result<u64> {
124    let mut char = iterator.next();
125    if char == Some(':') || char == Some('&') {
126        char = iterator.next();
127    }
128
129    if char.is_none() {
130        return Err(ReadError {
131            message: "".to_string(),
132        });
133    }
134
135    let mut size = (char.unwrap() as u64) - BIAS as u64;
136    if size > SMALLN {
137        char = iterator.next();
138        if char.is_none() {
139            return Err(ReadError {
140                message: "".to_string(),
141            });
142        }
143        size = (char.unwrap() as u64) - BIAS as u64;
144
145        if size > SMALLN {
146            char = iterator.next();
147            if char.is_none() {
148                return Err(ReadError {
149                    message: "".to_string(),
150                });
151            }
152            size = (char.unwrap() as u64) - BIAS as u64;
153            size = append_char_binary_to_size(size, iterator)?;
154            size = append_char_binary_to_size(size, iterator)?;
155            size = append_char_binary_to_size(size, iterator)?;
156            size = append_char_binary_to_size(size, iterator)?;
157            size = append_char_binary_to_size(size, iterator)?;
158        } else {
159            size = append_char_binary_to_size(size, iterator)?;
160            size = append_char_binary_to_size(size, iterator)?;
161        }
162    }
163    Ok(size)
164}
165
166fn append_char_binary_to_size(mut size: u64, iterator: &mut Chars) -> Result<u64> {
167    let char = iterator.next();
168    if char.is_none() {
169        return Err(ReadError {
170            message: "".to_string(),
171        });
172    }
173    size = (size << 6) | ((char.unwrap() as u64) - BIAS as u64);
174    Ok(size)
175}