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
/// immediatly 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
102
    pub fn new(labels: Vec<L>, hidden_labels: Vec<String>) -> Self {
76
102
        Self::with_capacity(labels, hidden_labels, 0, 0, 0)
77
102
    }
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
333
    pub fn with_capacity(
82
333
        mut labels: Vec<L>,
83
333
        hidden_labels: Vec<String>,
84
333
        num_of_labels: usize,
85
333
        num_of_states: usize,
86
333
        num_of_transitions: usize,
87
333
    ) -> Self {
88
        // Remove duplicates from the labels.
89
333
        labels.sort();
90
333
        labels.dedup();
91

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

            
99
        // Ensure that all hidden labels are mapped to the tau action.
100
333
        let mut labels_index = HashMap::new();
101
333
        labels_index.insert(L::tau_label(), LabelIndex::new(0));
102
333
        for (index, label) in labels.iter().enumerate() {
103
333
            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
333
            } else {
106
333
                labels_index.insert(label.clone(), LabelIndex::new(index));
107
333
            }
108
        }
109

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

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

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

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

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

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

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

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

            
183
284911
    fn num_of_transitions(&self) -> usize {
184
284911
        self.transition_from.len()
185
284911
    }
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
}