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 number of states (derived from the transitions).
67
    num_of_states: usize,
68
}
69

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

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

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

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

            
107
321
        Self {
108
321
            transition_from: ByteCompressedVec::with_capacity(num_of_transitions, num_of_states.bytes_required()),
109
321
            transition_labels: ByteCompressedVec::with_capacity(
110
321
                num_of_transitions,
111
321
                num_of_labels.max(labels.len()).bytes_required(),
112
321
            ),
113
321
            transition_to: ByteCompressedVec::with_capacity(num_of_transitions, num_of_states.bytes_required()),
114
321
            labels_index,
115
321
            labels,
116
321
            num_of_states: 0,
117
321
        }
118
321
    }
119

            
120
    /// Ensures that the builder has at least the given number of states.
121
    pub fn require_num_of_states(&mut self, num_of_states: usize) {
122
        if num_of_states > self.num_of_states {
123
            self.num_of_states = num_of_states;
124
        }
125
    }
126

            
127
    /// Returns an iterator over all transitions as (from, label, to) tuples.
128
642
    fn iter(&self) -> impl Iterator<Item = (StateIndex, LabelIndex, StateIndex)> {
129
642
        self.transition_from
130
642
            .iter()
131
642
            .zip(self.transition_labels.iter())
132
642
            .zip(self.transition_to.iter())
133
570392
            .map(|((from, label), to)| (from, label, to))
134
642
    }
135
}
136

            
137
impl<L: TransitionLabel> LtsBuilder<L> for LtsBuilderMem<L> {
138
    type LTS = LabelledTransitionSystem<L>;
139

            
140
284920
    fn add_transition<Q>(&mut self, from: StateIndex, label: &Q, to: StateIndex) -> Result<(), MercError>
141
284920
    where
142
284920
        L: Borrow<Q>,
143
284920
        Q: ?Sized + ToOwned<Owned = L> + Eq + Hash,
144
    {
145
284920
        let label_index = if let Some(&index) = self.labels_index.get(label) {
146
283927
            index
147
        } else {
148
993
            let index = LabelIndex::new(self.labels.len());
149
993
            self.labels_index.insert(label.to_owned(), index);
150
993
            self.labels.push(label.to_owned());
151
993
            index
152
        };
153

            
154
284920
        self.transition_from.push(from);
155
284920
        self.transition_labels.push(label_index);
156
284920
        self.transition_to.push(to);
157

            
158
        // Update the number of states.
159
284920
        self.num_of_states = self.num_of_states.max(from.value() + 1).max(to.value() + 1);
160
284920
        Ok(())
161
284920
    }
162

            
163
321
    fn finish(&mut self, initial_state: StateIndex) -> Result<Self::LTS, MercError> {
164
321
        Ok(LabelledTransitionSystem::new(
165
321
            initial_state,
166
321
            Some(self.num_of_states),
167
642
            || self.iter(),
168
321
            self.labels.clone(),
169
        ))
170
321
    }
171

            
172
285124
    fn num_of_transitions(&self) -> usize {
173
285124
        self.transition_from.len()
174
285124
    }
175

            
176
    fn num_of_states(&self) -> usize {
177
        self.num_of_states
178
    }
179
}
180

            
181
impl<Label: TransitionLabel> fmt::Debug for LtsBuilderMem<Label> {
182
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
183
        writeln!(f, "Transitions:")?;
184
        for (from, label, to) in self.iter() {
185
            writeln!(f, "    {:?} --[{:?}]-> {:?}", from, label, to)?;
186
        }
187
        Ok(())
188
    }
189
}