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 merc_collections::ByteCompressedVec;
9
use merc_collections::CompressedEntry;
10
use merc_utilities::MercError;
11

            
12
use crate::LabelIndex;
13
use crate::LabelledTransitionSystem;
14
use crate::StateIndex;
15
use crate::TransitionLabel;
16

            
17
/// A trait for building labelled transition systems incrementally.
18
///
19
/// # Details
20
///
21
/// Depending on the implementation this can be done in a memory efficient way,
22
/// or in a way that is optimized for speed. Alternatively, the resulting LTS is
23
/// immediately written to disk. The builder accumulates transitions using
24
/// `add_transition`, and once all transitions have been added, the labelled
25
/// transition system can be constructed with `finish`. An initial state can
26
/// also be specified during finalization.
27
pub trait LtsBuilder<L: TransitionLabel> {
28
    /// The result type of the builder once finalized.
29
    type LTS;
30

            
31
    /// Adds a transition to the builder. For efficiency reasons, we can use
32
    /// another type `Q` for the label.
33
    fn add_transition<Q>(&mut self, from: StateIndex, label: &Q, to: StateIndex) -> Result<(), MercError>
34
    where
35
        L: Borrow<Q>,
36
        Q: ?Sized + ToOwned<Owned = L> + Eq + Hash;
37

            
38
    /// Finalizes the builder and returns the constructed labelled transition system.
39
    fn finish(&mut self, initial_state: StateIndex) -> Result<Self::LTS, MercError>;
40

            
41
    /// Returns the number of transitions added to the builder.
42
    fn num_of_transitions(&self) -> usize;
43

            
44
    /// Returns the number of states added to the builder.
45
    fn num_of_states(&self) -> usize;
46
}
47

            
48
/// This struct helps in building a labelled transition system by accumulating
49
/// transitions in a memory efficient way.
50
///
51
/// # Details
52
///
53
/// Transitions can be added with `add_transition`, and once all transitions
54
/// have been added, the labelled transition system can be constructed with
55
/// `finish`. An initial state can also be specified during finalization.
56
///
57
pub struct LtsBuilderMem<L> {
58
    transition_from: ByteCompressedVec<StateIndex>,
59
    transition_labels: ByteCompressedVec<LabelIndex>,
60
    transition_to: ByteCompressedVec<StateIndex>,
61

            
62
    // This is used to keep track of the label to index mapping.
63
    labels_index: HashMap<L, LabelIndex>,
64
    labels: Vec<L>,
65

            
66
    /// The hidden labels that should be mapped to the hidden action.
67
    hidden_labels: Vec<String>,
68

            
69
    /// The number of states (derived from the transitions).
70
    num_of_states: usize,
71
}
72

            
73
impl<L: TransitionLabel> LtsBuilderMem<L> {
74
    /// Initializes a new empty builder.
75
503
    pub fn new(labels: Vec<L>, hidden_labels: Vec<String>) -> Self {
76
503
        Self::with_capacity(labels, hidden_labels, 0, 0, 0)
77
503
    }
78

            
79
    /// Initializes the builder with pre-allocated capacity for states and transitions. The number of labels
80
    /// can be used when labels are added dynamically.
81
5225
    pub fn with_capacity(
82
5225
        mut labels: Vec<L>,
83
5225
        hidden_labels: Vec<String>,
84
5225
        num_of_labels: usize,
85
5225
        num_of_states: usize,
86
5225
        num_of_transitions: usize,
87
5225
    ) -> Self {
88
        // Remove duplicates from the labels.
89
5225
        labels.sort();
90
5225
        labels.dedup();
91

            
92
        // Introduce the fixed 0-indexed tau label.
93
5225
        if let Some(tau_pos) = labels.iter().position(|l| l.is_tau_label()) {
94
            labels.swap(0, tau_pos);
95
5225
        } else {
96
5225
            labels.insert(0, L::tau_label());
97
5225
        }
98

            
99
        // Ensure that all hidden labels are mapped to the tau action.
100
5225
        let mut labels_index = HashMap::new();
101
5225
        labels_index.insert(L::tau_label(), LabelIndex::new(0));
102
5225
        for (index, label) in labels.iter().enumerate() {
103
5225
            if hidden_labels.iter().any(|l| label.matches_label(l)) {
104
                labels_index.insert(label.clone(), LabelIndex::new(0)); // Map hidden labels to tau
105
5225
            } else {
106
5225
                labels_index.insert(label.clone(), LabelIndex::new(index));
107
5225
            }
108
        }
109

            
110
5225
        Self {
111
5225
            transition_from: ByteCompressedVec::with_capacity(num_of_transitions, num_of_states.bytes_required()),
112
5225
            transition_labels: ByteCompressedVec::with_capacity(
113
5225
                num_of_transitions,
114
5225
                num_of_labels.max(labels.len()).bytes_required(),
115
5225
            ),
116
5225
            transition_to: ByteCompressedVec::with_capacity(num_of_transitions, num_of_states.bytes_required()),
117
5225
            labels_index,
118
5225
            labels,
119
5225
            hidden_labels,
120
5225
            num_of_states: 0,
121
5225
        }
122
5225
    }
123

            
124
    /// Ensures that the builder has at least the given number of states.
125
300
    pub fn require_num_of_states(&mut self, num_of_states: usize) {
126
300
        if num_of_states > self.num_of_states {
127
300
            self.num_of_states = num_of_states;
128
300
        }
129
300
    }
130

            
131
    /// Returns an iterator over all transitions as (from, label, to) tuples.
132
10450
    fn iter(&self) -> impl Iterator<Item = (StateIndex, LabelIndex, StateIndex)> {
133
10450
        self.transition_from
134
10450
            .iter()
135
10450
            .zip(self.transition_labels.iter())
136
10450
            .zip(self.transition_to.iter())
137
22755640
            .map(|((from, label), to)| (from, label, to))
138
10450
    }
139
}
140

            
141
impl<L: TransitionLabel> LtsBuilder<L> for LtsBuilderMem<L> {
142
    type LTS = LabelledTransitionSystem<L>;
143

            
144
1240231
    fn add_transition<Q>(&mut self, from: StateIndex, label: &Q, to: StateIndex) -> Result<(), MercError>
145
1240231
    where
146
1240231
        L: Borrow<Q>,
147
1240231
        Q: ?Sized + ToOwned<Owned = L> + Eq + Hash,
148
    {
149
1240231
        let label_index = if let Some(&index) = self.labels_index.get(label) {
150
1237108
            index
151
        } else {
152
            // Label was not yet added, so add it to the labels and the index.
153
3123
            let label = label.to_owned();
154
3123
            let index = if self.hidden_labels.iter().any(|l| label.matches_label(l)) {
155
164
                LabelIndex::new(0) // Map hidden labels to tau
156
            } else {
157
2959
                let idx = LabelIndex::new(self.labels.len());
158
2959
                self.labels.push(label.clone());
159
2959
                idx
160
            };
161
3123
            self.labels_index.insert(label, index);
162
3123
            index
163
        };
164

            
165
1240231
        self.transition_from.push(from);
166
1240231
        self.transition_labels.push(label_index);
167
1240231
        self.transition_to.push(to);
168

            
169
        // Update the number of states.
170
1240231
        self.num_of_states = self.num_of_states.max(from.value() + 1).max(to.value() + 1);
171
1240231
        Ok(())
172
1240231
    }
173

            
174
5225
    fn finish(&mut self, initial_state: StateIndex) -> Result<Self::LTS, MercError> {
175
5225
        Ok(LabelledTransitionSystem::new(
176
5225
            initial_state,
177
5225
            Some(self.num_of_states),
178
10450
            || self.iter(),
179
5225
            self.labels.clone(),
180
        ))
181
5225
    }
182

            
183
976352
    fn num_of_transitions(&self) -> usize {
184
976352
        self.transition_from.len()
185
976352
    }
186

            
187
    fn num_of_states(&self) -> usize {
188
        self.num_of_states
189
    }
190
}
191

            
192
impl<Label: TransitionLabel> fmt::Debug for LtsBuilderMem<Label> {
193
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
194
        writeln!(f, "Transitions:")?;
195
        for (from, label, to) in self.iter() {
196
            writeln!(f, "    {:?} --[{:?}]-> {:?}", from, label, to)?;
197
        }
198
        Ok(())
199
    }
200
}