1
//! Authors: Maurice Laveaux and Sjef van Loo
2

            
3
use std::io::Read;
4
use std::io::Write;
5

            
6
use itertools::Itertools;
7
use log::info;
8
use regex::Regex;
9
use streaming_iterator::StreamingIterator;
10
use thiserror::Error;
11

            
12
use merc_io::LineIterator;
13
use merc_io::TimeProgress;
14
use merc_utilities::MercError;
15

            
16
use crate::ParityGame;
17
use crate::ParityGameBuilder;
18
use crate::Player;
19
use crate::Priority;
20
use crate::VertexIndex;
21
use crate::PG;
22

            
23
#[derive(Error, Debug)]
24
pub enum IOError {
25
    #[error("Invalid .pg header {0}")]
26
    InvalidHeader(&'static str),
27

            
28
    #[error("Invalid line {0}")]
29
    InvalidLine(&'static str),
30
}
31

            
32
/// Reads a parity game in textual PGSolver `.pg` format from the given reader.
33
///
34
/// # Details
35
///
36
/// The format starts with a header, followed by the vertices
37
///
38
/// `parity <num_of_vertices>;`
39
/// `<index> <priority> <owner> <outgoing_vertex>, <outgoing_vertex>, ...;`
40
101
pub fn read_pg<R: Read>(reader: R) -> Result<ParityGame, MercError> {
41
101
    info!("Reading parity game in .pg format...");
42

            
43
101
    let mut lines = LineIterator::new(reader);
44
101
    lines.advance();
45
101
    let header = lines
46
101
        .get()
47
101
        .ok_or(IOError::InvalidHeader("The first line should be the header"))?;
48

            
49
    // Read the header
50
101
    let header_regex = Regex::new(r#"parity\s+([0-9]+)\s*;"#).expect("Regex compilation should not fail");
51

            
52
101
    let (_, [num_of_vertices_txt]) = header_regex
53
101
        .captures(header)
54
101
        .ok_or(IOError::InvalidHeader("does not match parity <num_of_vertices>;"))?
55
101
        .extract();
56

            
57
101
    let num_of_vertices: usize = num_of_vertices_txt.parse()?;
58
101
    let progress = TimeProgress::new(
59
        |(amount, total): (usize, usize)| info!("Read {} vertices ({}%)...", amount, amount * 100 / total),
60
        1,
61
    );
62

            
63
    // Collect the data in a builder and remove duplicate edges at the end.
64
101
    let mut builder = ParityGameBuilder::with_capacity(VertexIndex::new(0), num_of_vertices);
65
101
    if num_of_vertices > 0 {
66
101
        builder.add_vertex(VertexIndex::new(num_of_vertices - 1), Player::Even, Priority::new(0));
67
101
    }
68

            
69
101
    let mut vertex_count = 0;
70
8103
    while let Some(line) = lines.next() {
71
        // Parse the line: <index> <priority> <owner> <outgoing_vertex>, <outgoing_vertex>, ...;
72
8002
        let mut parts = line.split_whitespace();
73

            
74
8002
        let index: usize = parts
75
8002
            .next()
76
8002
            .ok_or(IOError::InvalidLine("Expected at least <index> ...;"))?
77
8002
            .parse()?;
78
8002
        let vertex_priority: usize = parts
79
8002
            .next()
80
8002
            .ok_or(IOError::InvalidLine("Expected at least <index> <priority> ...;"))?
81
8002
            .parse()?;
82
8002
        let vertex_owner: u8 = parts
83
8002
            .next()
84
8002
            .ok_or(IOError::InvalidLine(
85
8002
                "Expected at least <index> <priority> <owner> ...;",
86
8002
            ))?
87
8002
            .parse()?;
88

            
89
8002
        let vertex = VertexIndex::new(index);
90
8002
        builder.add_vertex(vertex, Player::from_index(vertex_owner), Priority::new(vertex_priority));
91

            
92
14749
        for successors in parts {
93
            // Parse successors (remaining parts, removing trailing semicolon)
94
14749
            for successor in successors
95
14749
                .trim_end_matches(';')
96
14749
                .split(',')
97
21496
                .filter(|s| !s.trim().is_empty())
98
14749
                .map(|s| s.trim().parse())
99
            {
100
14749
                let successor = successor?;
101
14749
                builder.add_edge(vertex, VertexIndex::new(successor));
102
            }
103
        }
104

            
105
8002
        progress.print((vertex_count + 1, num_of_vertices));
106
8002
        vertex_count += 1;
107
    }
108

            
109
101
    Ok(builder.finish(true, true))
110
101
}
111

            
112
/// Writes the given parity game to the given writer in .pg format.
113
100
pub fn write_pg<W: Write, G: PG>(mut writer: W, game: &G) -> Result<(), MercError> {
114
100
    info!("Writing parity game to .pg format...");
115

            
116
100
    let progress = TimeProgress::new(
117
        |(index, total): (usize, usize)| info!("Wrote {} vertices ({}%)...", index, index * 100 / total),
118
        1,
119
    );
120

            
121
100
    writeln!(writer, "parity {};", game.num_of_vertices())?;
122
5000
    for v in game.iter_vertices() {
123
5000
        let prio = game.priority(v);
124
5000
        let owner = game.owner(v).to_index();
125

            
126
5000
        write!(writer, "{} {} {} ", v.value(), prio.value(), owner)?;
127
5000
        write!(
128
5000
            writer,
129
            "{}",
130
10781
            game.outgoing_edges(v).map(|edge| edge.to().value()).format(", ")
131
        )?;
132
5000
        writeln!(writer, ";")?;
133
5000
        progress.print((v.value() + 1, game.num_of_vertices()));
134
    }
135

            
136
100
    Ok(())
137
100
}
138

            
139
#[cfg(test)]
140
mod tests {
141
    use merc_utilities::random_test;
142

            
143
    use crate::random_parity_game;
144

            
145
    use super::*;
146

            
147
    #[test]
148
    #[cfg_attr(miri, ignore)]
149
1
    fn test_read_pg() {
150
1
        let parity_game = read_pg(include_bytes!("../../../../examples/vpg/example.pg") as &[u8]).unwrap();
151
1
        assert_eq!(parity_game.num_of_vertices(), 3002);
152
1
        assert_eq!(parity_game.num_of_edges(), 3968);
153
1
    }
154

            
155
    #[test]
156
    #[cfg_attr(miri, ignore)] // Test is too slow under miri
157
1
    fn test_random_io_pg() {
158
100
        random_test(100, |rng| {
159
100
            let game = random_parity_game(rng, true, 50, 10, 5);
160

            
161
100
            let mut buffer = Vec::new();
162
100
            write_pg(&mut buffer, &game).unwrap();
163

            
164
100
            let read_game = read_pg(buffer.as_slice()).unwrap();
165
100
            assert_eq!(game.num_of_vertices(), read_game.num_of_vertices());
166
100
            assert_eq!(game.num_of_edges(), read_game.num_of_edges());
167

            
168
5000
            for vertex in game.iter_vertices() {
169
10781
                let original_successors: Vec<_> = game.outgoing_edges(vertex).map(|e| e.to()).collect();
170
10781
                let read_successors: Vec<_> = read_game.outgoing_edges(vertex).map(|e| e.to()).collect();
171
5000
                assert_eq!(original_successors.len(), read_successors.len());
172
5000
                assert_eq!(
173
5000
                    original_successors.into_iter().sorted().collect::<Vec<_>>(),
174
5000
                    read_successors.into_iter().sorted().collect::<Vec<_>>()
175
                );
176
            }
177
100
        });
178
1
    }
179
}