1
use std::fmt;
2

            
3
/// Represents a dependency graph between variables used in symbolic transition relations.
4
pub struct DependencyGraph {
5
    /// The list of relations in the dependency graph.
6
    relations: Vec<Relation>,
7

            
8
    /// The number of vertices
9
    num_of_vertices: usize,
10
}
11

            
12
impl DependencyGraph {
13
    /// Creates a new dependency graph from the given relations.
14
1
    pub fn new(relations: Vec<Relation>) -> Self {
15
1
        let num_of_vertices = relations
16
1
            .iter()
17
10
            .flat_map(|rel| rel.read_vars.iter().chain(rel.write_vars.iter()))
18
1
            .copied()
19
1
            .max()
20
1
            .map_or(0, |max_index| max_index + 1);
21

            
22
1
        DependencyGraph {
23
1
            relations,
24
1
            num_of_vertices,
25
1
        }
26
1
    }
27

            
28
    /// Returns the number of vertices in the dependency graph.
29
    pub fn num_of_vertices(&self) -> usize {
30
        self.num_of_vertices
31
    }
32

            
33
    /// Number of relations in the dependency graph.
34
    pub fn num_of_relations(&self) -> usize {
35
        self.relations.len()
36
    }
37

            
38
    /// Returns an iterator over the relations in the dependency graph.
39
    pub fn relations(&self) -> impl Iterator<Item = &Relation> {
40
        self.relations.iter()
41
    }
42
}
43

            
44
impl fmt::Debug for DependencyGraph {
45
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
46
        writeln!(f, "DependencyGraph with {} vertices:", self.num_of_vertices)?;
47
        for (i, relation) in self.relations.iter().enumerate() {
48
            writeln!(f, "  {}: {:?}", i, relation)?;
49
        }
50
        Ok(())
51
    }
52
}
53

            
54
/// A single relation in the dependency graph containing read and write
55
/// dependencies onto variables, given by their indices.
56
pub struct Relation {
57
    read_vars: Vec<usize>,
58
    write_vars: Vec<usize>,
59
}
60

            
61
impl Relation {
62
    /// Create a new relation or hyper-edge.
63
    pub fn new(read_vars: Vec<usize>, write_vars: Vec<usize>) -> Self {
64
        Relation {
65
            read_vars,
66
            write_vars,
67
        }
68
    }
69

            
70
    /// Returns an iterator over the read variables in this relation.
71
    pub fn read_vars(&self) -> impl Iterator<Item = usize> + '_ {
72
        self.read_vars.iter().copied()
73
    }
74

            
75
    /// Returns an iterator over the write variables in this relation.
76
    pub fn write_vars(&self) -> impl Iterator<Item = usize> + '_ {
77
        self.write_vars.iter().copied()
78
    }
79
}
80

            
81
impl fmt::Debug for Relation {
82
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
83
        write!(f, "{:?} -> {:?}", self.read_vars, self.write_vars)
84
    }
85
}
86

            
87
/// Parses a dependency graph as output by
88
/// [lpreach](https://mcrl2.org/web/user_manual/tools/release/lpsreach.html) and
89
/// [pbessolvesymbolic](https://mcrl2.org/web/user_manual/tools/release/pbessolvesymbolic.html)
90
/// flag `--info`.
91
1
pub fn parse_compacted_dependency_graph(input: &str) -> DependencyGraph {
92
1
    let mut relations = Vec::new();
93

            
94
10
    for line in input.lines() {
95
        // Keep only pattern characters, ignoring indices/whitespace
96
131
        let pattern: Vec<char> = line.chars().filter(|c| matches!(c, '+' | '-' | 'r' | 'w')).collect();
97

            
98
10
        if pattern.is_empty() {
99
            continue;
100
10
        }
101

            
102
10
        let mut read_vars = Vec::new();
103
10
        let mut write_vars = Vec::new();
104

            
105
110
        for (col, ch) in pattern.into_iter().enumerate() {
106
110
            match ch {
107
26
                '+' => {
108
26
                    read_vars.push(col);
109
26
                    write_vars.push(col);
110
26
                }
111
3
                'r' => read_vars.push(col),
112
10
                'w' => write_vars.push(col),
113
71
                '-' => {}
114
                _ => {}
115
            }
116
        }
117

            
118
10
        relations.push(Relation { read_vars, write_vars });
119
    }
120

            
121
1
    DependencyGraph::new(relations)
122
1
}
123

            
124
#[cfg(test)]
125
mod tests {
126
    use crate::parse_compacted_dependency_graph;
127

            
128
    #[test]
129
1
    fn test_parse_abp_dependency_graph() {
130
1
        let input = "1 +w---------
131
1
2 ---+++-----
132
1
3 ------++---
133
1
4 --------++-
134
1
5 ------+w+w+
135
1
6 ---+ww--+w-
136
1
7 ---+++--+wr
137
1
8 +-----+w---
138
1
9 +rr+ww-----
139
1
10 +++---++---";
140

            
141
1
        let graph = parse_compacted_dependency_graph(input);
142

            
143
1
        assert_eq!(graph.relations.len(), 10);
144
1
    }
145
}