1
#![forbid(unsafe_code)]
2

            
3
use std::borrow::Borrow;
4
use std::collections::HashMap;
5
use std::fmt;
6
use std::hash::Hash;
7

            
8
use crate::LabelIndex;
9
use crate::LabelledTransitionSystem;
10
use crate::StateIndex;
11
use crate::TransitionLabel;
12

            
13
/// This is the same as [`crate::LtsBuilder`], but optimized for speed rather than memory usage.
14
/// So it does not use the byte compression for the transitions since somehow permuting and
15
/// sorting these take a long time (probably due to cache misses).
16
///
17
/// Perhaps that implementation can be made more efficient in the future, but for now
18
/// this works well enough.
19
pub struct LtsBuilderFast<L> {
20
    transitions: Vec<(StateIndex, LabelIndex, StateIndex)>,
21

            
22
    // This is used to keep track of the label to index mapping.
23
    labels_index: HashMap<L, LabelIndex>,
24
    labels: Vec<L>,
25

            
26
    /// The hidden labels that should be mapped to the hidden action.
27
    hidden_labels: Vec<String>,
28

            
29
    /// The number of states (derived from the transitions).
30
    num_of_states: usize,
31
}
32

            
33
impl<L: TransitionLabel> LtsBuilderFast<L> {
34
    /// Initializes a new empty builder.
35
6114
    pub fn new(labels: Vec<L>, hidden_labels: Vec<String>) -> Self {
36
6114
        Self::with_capacity(labels, hidden_labels, 0)
37
6114
    }
38

            
39
    /// Initializes the builder with pre-allocated capacity for states and transitions.
40
18114
    pub fn with_capacity(mut labels: Vec<L>, hidden_labels: Vec<String>, num_of_transitions: usize) -> Self {
41
        // Remove duplicates from the labels.
42
18114
        labels.sort();
43
18114
        labels.dedup();
44

            
45
        // Introduce the fixed 0 indexed tau label.
46
110970
        if let Some(tau_pos) = labels.iter().position(|l| l.is_tau_label()) {
47
18014
            labels.swap(0, tau_pos);
48
18014
        } else {
49
100
            labels.insert(0, L::tau_label());
50
100
        }
51

            
52
        // Ensure that all hidden labels are mapped to the tau action.
53
18114
        let mut labels_index = HashMap::new();
54
18114
        labels_index.insert(labels[0].clone(), LabelIndex::new(0));
55
121270
        for (index, label) in labels.iter().enumerate() {
56
121270
            if hidden_labels.iter().any(|l| label.matches_label(l)) {
57
                labels_index.insert(label.clone(), LabelIndex::new(0)); // Map hidden labels to tau
58
121270
            } else {
59
121270
                labels_index.insert(label.clone(), LabelIndex::new(index));
60
121270
            }
61
        }
62

            
63
18114
        Self {
64
18114
            transitions: Vec::with_capacity(num_of_transitions),
65
18114
            labels_index,
66
18114
            labels,
67
18114
            hidden_labels,
68
18114
            num_of_states: 0,
69
18114
        }
70
18114
    }
71

            
72
    /// Adds a transition to the builder.
73
2807048
    pub fn add_transition<Q>(&mut self, from: StateIndex, label: &Q, to: StateIndex)
74
2807048
    where
75
2807048
        L: Borrow<Q>,
76
2807048
        Q: ToOwned<Owned = L> + Eq + Hash,
77
    {
78
2807048
        let label_index = if let Some(&index) = self.labels_index.get(label) {
79
2807048
            index
80
        } else {
81
            // Label was not yet added, so add it to the labels and the index.
82
            let label = label.to_owned();
83
            let index = if self.hidden_labels.iter().any(|l| label.matches_label(l)) {
84
                LabelIndex::new(0) // Map hidden labels to tau
85
            } else {
86
                let idx = LabelIndex::new(self.labels.len());
87
                self.labels.push(label.clone());
88
                idx
89
            };
90
            self.labels_index.insert(label, index);
91
            index
92
        };
93

            
94
2807048
        self.transitions.push((from, label_index, to));
95

            
96
        // Update the number of states.
97
2807048
        self.num_of_states = self.num_of_states.max(from.value() + 1).max(to.value() + 1);
98
2807048
    }
99

            
100
    /// Finalizes the builder and returns the constructed labelled transition system.
101
18014
    pub fn finish(&mut self, initial_state: StateIndex, remove_duplicates: bool) -> LabelledTransitionSystem<L> {
102
18014
        if remove_duplicates {
103
18014
            self.remove_duplicates();
104
18014
        }
105

            
106
18014
        LabelledTransitionSystem::new(
107
18014
            initial_state,
108
18014
            Some(self.num_of_states),
109
36028
            || self.iter(),
110
18014
            self.labels.clone(),
111
        )
112
18014
    }
113

            
114
    /// Returns the number of transitions added to the builder.
115
    pub fn num_of_transitions(&self) -> usize {
116
        self.transitions.len()
117
    }
118

            
119
    /// Returns the number of states that the builder currently found.
120
4900
    pub fn num_of_states(&self) -> usize {
121
4900
        self.num_of_states
122
4900
    }
123

            
124
    /// Sets the number of states to at least the given number. All states without transitions
125
    /// will simply become deadlock states.
126
14129
    pub fn require_num_of_states(&mut self, num_states: usize) {
127
14129
        if num_states > self.num_of_states {
128
4299
            self.num_of_states = num_states;
129
9830
        }
130
14129
    }
131

            
132
    /// Removes duplicated transitions from the added transitions.
133
18114
    fn remove_duplicates(&mut self) {
134
18114
        self.transitions.sort();
135
18114
        self.transitions.dedup();
136
18114
    }
137

            
138
    /// Returns an iterator over all transitions as (from, label, to) tuples.
139
36128
    pub fn iter(&self) -> impl Iterator<Item = (StateIndex, LabelIndex, StateIndex)> {
140
36128
        self.transitions.iter().cloned()
141
36128
    }
142
}
143

            
144
impl<Label: TransitionLabel> fmt::Debug for LtsBuilderFast<Label> {
145
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
146
        writeln!(f, "Transitions:")?;
147
        for (from, label, to) in self.iter() {
148
            writeln!(f, "    {:?} --[{:?}]-> {:?}", from, label, to)?;
149
        }
150
        Ok(())
151
    }
152
}
153

            
154
#[cfg(test)]
155
mod tests {
156
    use itertools::Itertools;
157
    use rand::RngExt;
158

            
159
    use merc_utilities::random_test;
160

            
161
    use crate::LabelIndex;
162
    use crate::LtsBuilderFast;
163
    use crate::StateIndex;
164

            
165
    #[test]
166
1
    fn test_random_remove_duplicates() {
167
100
        random_test(100, |rng| {
168
100
            let labels = vec!["a".to_string(), "b".to_string(), "c".to_string()];
169
100
            let mut builder = LtsBuilderFast::new(labels.clone(), Vec::new());
170

            
171
464
            for _ in 0..rng.random_range(0..10) {
172
464
                let from = StateIndex::new(rng.random_range(0..10));
173
464
                let label = LabelIndex::new(rng.random_range(0..2));
174
464
                let to = StateIndex::new(rng.random_range(0..10));
175
464
                builder.add_transition(from, &labels[label], to);
176
464
            }
177

            
178
100
            builder.remove_duplicates();
179

            
180
100
            let transitions = builder.iter().collect::<Vec<_>>();
181
100
            debug_assert!(
182
100
                transitions.iter().all_unique(),
183
                "Transitions should be unique after removing duplicates"
184
            );
185
100
        });
186
1
    }
187
}