1
use std::borrow::Borrow;
2
use std::collections::HashMap;
3
use std::fmt;
4
use std::hash::Hash;
5

            
6
use crate::LabelIndex;
7
use crate::LabelledTransitionSystem;
8
use crate::StateIndex;
9
use crate::TransitionLabel;
10

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

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

            
24
    /// The number of states (derived from the transitions).
25
    num_of_states: usize,
26
}
27

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

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

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

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

            
58
8900
        Self {
59
8900
            transitions: Vec::with_capacity(num_of_transitions),
60
8900
            labels_index,
61
8900
            labels,
62
8900
            num_of_states: 0,
63
8900
        }
64
8900
    }
65

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

            
81
649736
        self.transitions.push((from, label_index, to));
82

            
83
        // Update the number of states.
84
649736
        self.num_of_states = self.num_of_states.max(from.value() + 1).max(to.value() + 1);
85
649736
    }
86

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

            
93
8800
        LabelledTransitionSystem::new(
94
8800
            initial_state,
95
8800
            Some(self.num_of_states),
96
17600
            || self.iter(),
97
8800
            self.labels.clone(),
98
        )
99
8800
    }
100

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

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

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

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

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

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

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

            
145
    use itertools::Itertools;
146
    use rand::Rng;
147

            
148
    use merc_utilities::random_test;
149

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

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

            
163
100
            builder.remove_duplicates();
164

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