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 number of states (derived from the transitions).
27
    num_of_states: usize,
28
}
29

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

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

            
42
        // Introduce the fixed 0 indexed tau label.
43
46700
        if let Some(tau_pos) = labels.iter().position(|l| l.is_tau_label()) {
44
10200
            labels.swap(0, tau_pos);
45
10200
        } else {
46
100
            labels.insert(0, L::tau_label());
47
100
        }
48

            
49
        // Ensure that all hidden labels are mapped to the tau action.
50
10300
        let mut labels_index = HashMap::new();
51
10300
        labels_index.insert(L::tau_label(), LabelIndex::new(0));
52
47000
        for (index, label) in labels.iter().enumerate() {
53
47000
            if hidden_labels.iter().any(|l| label.matches_label(l)) {
54
                labels_index.insert(label.clone(), LabelIndex::new(0)); // Map hidden labels to tau
55
47000
            } else {
56
47000
                labels_index.insert(label.clone(), LabelIndex::new(index));
57
47000
            }
58
        }
59

            
60
10300
        Self {
61
10300
            transitions: Vec::with_capacity(num_of_transitions),
62
10300
            labels_index,
63
10300
            labels,
64
10300
            num_of_states: 0,
65
10300
        }
66
10300
    }
67

            
68
    /// Adds a transition to the builder.
69
618127
    pub fn add_transition<Q>(&mut self, from: StateIndex, label: &Q, to: StateIndex)
70
618127
    where
71
618127
        L: Borrow<Q>,
72
618127
        Q: ToOwned<Owned = L> + Eq + Hash,
73
    {
74
618127
        let label_index = if let Some(&index) = self.labels_index.get(label) {
75
618127
            index
76
        } else {
77
            let index = LabelIndex::new(self.labels.len());
78
            self.labels_index.insert(label.to_owned(), index);
79
            self.labels.push(label.to_owned());
80
            index
81
        };
82

            
83
618127
        self.transitions.push((from, label_index, to));
84

            
85
        // Update the number of states.
86
618127
        self.num_of_states = self.num_of_states.max(from.value() + 1).max(to.value() + 1);
87
618127
    }
88

            
89
    /// Finalizes the builder and returns the constructed labelled transition system.
90
10200
    pub fn finish(&mut self, initial_state: StateIndex, remove_duplicates: bool) -> LabelledTransitionSystem<L> {
91
10200
        if remove_duplicates {
92
10200
            self.remove_duplicates();
93
10200
        }
94

            
95
10200
        LabelledTransitionSystem::new(
96
10200
            initial_state,
97
10200
            Some(self.num_of_states),
98
20400
            || self.iter(),
99
10200
            self.labels.clone(),
100
        )
101
10200
    }
102

            
103
    /// Returns the number of transitions added to the builder.
104
    pub fn num_of_transitions(&self) -> usize {
105
        self.transitions.len()
106
    }
107

            
108
    /// Returns the number of states that the builder currently found.
109
8900
    pub fn num_of_states(&self) -> usize {
110
8900
        self.num_of_states
111
8900
    }
112

            
113
    /// Sets the number of states to at least the given number. All states without transitions
114
    /// will simply become deadlock states.
115
1849
    pub fn require_num_of_states(&mut self, num_states: usize) {
116
1849
        if num_states > self.num_of_states {
117
677
            self.num_of_states = num_states;
118
1319
        }
119
1849
    }
120

            
121
    /// Removes duplicated transitions from the added transitions.
122
10300
    fn remove_duplicates(&mut self) {
123
10300
        self.transitions.sort();
124
10300
        self.transitions.dedup();
125
10300
    }
126

            
127
    /// Returns an iterator over all transitions as (from, label, to) tuples.
128
20500
    pub fn iter(&self) -> impl Iterator<Item = (StateIndex, LabelIndex, StateIndex)> {
129
20500
        self.transitions.iter().cloned()
130
20500
    }
131
}
132

            
133
impl<Label: TransitionLabel> fmt::Debug for LtsBuilderFast<Label> {
134
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
135
        writeln!(f, "Transitions:")?;
136
        for (from, label, to) in self.iter() {
137
            writeln!(f, "    {:?} --[{:?}]-> {:?}", from, label, to)?;
138
        }
139
        Ok(())
140
    }
141
}
142

            
143
#[cfg(test)]
144
mod tests {
145
    use super::*;
146

            
147
    use itertools::Itertools;
148
    use rand::Rng;
149

            
150
    use merc_utilities::random_test;
151

            
152
    #[test]
153
1
    fn test_random_remove_duplicates() {
154
100
        random_test(100, |rng| {
155
100
            let labels = vec!["a".to_string(), "b".to_string(), "c".to_string()];
156
100
            let mut builder = LtsBuilderFast::new(labels.clone(), Vec::new());
157

            
158
417
            for _ in 0..rng.random_range(0..10) {
159
417
                let from = StateIndex::new(rng.random_range(0..10));
160
417
                let label = LabelIndex::new(rng.random_range(0..2));
161
417
                let to = StateIndex::new(rng.random_range(0..10));
162
417
                builder.add_transition(from, &labels[label], to);
163
417
            }
164

            
165
100
            builder.remove_duplicates();
166

            
167
100
            let transitions = builder.iter().collect::<Vec<_>>();
168
100
            debug_assert!(
169
100
                transitions.iter().all_unique(),
170
                "Transitions should be unique after removing duplicates"
171
            );
172
100
        });
173
1
    }
174
}