1
use log::debug;
2
use log::info;
3
use log::trace;
4
use merc_ldd::height;
5
use merc_lts::LtsBuilder;
6
use rustc_hash::FxBuildHasher;
7

            
8
use merc_collections::IndexedSet;
9
use merc_io::LargeFormatter;
10
use merc_io::TimeProgress;
11
use merc_ldd::Storage;
12
use merc_ldd::iterators::iter;
13
use merc_ldd::len;
14
use merc_lts::StateIndex;
15
use merc_utilities::MercError;
16
use rustc_hash::FxHashSet;
17
use streaming_iterator::StreamingIterator;
18

            
19
use crate::SymbolicLTS;
20
use crate::TransitionGroup;
21

            
22
/// Converts a symbolic LTS to an explicit LTS.
23
///
24
/// # Details
25
///
26
/// This basically applies the symbolic transitions to every state in the state
27
/// space, and constructs the explicit LTS.
28
1
pub fn convert_symbolic_lts<B: LtsBuilder<String>>(
29
1
    storage: &mut Storage,
30
1
    output: &mut B,
31
1
    lts: &impl SymbolicLTS,
32
1
) -> Result<B::LTS, MercError> {
33
10
    for group in lts.transition_groups() {
34
10
        if group.action_label_index().is_none() {
35
            return Err("Cannot convert a symbolic LTS with transition groups without action labels".into());
36
10
        }
37
    }
38

            
39
    // Compute for every read and write index its position in the transition vector.
40
1
    let mut read_positions = Vec::new();
41
1
    let mut write_positions = Vec::new();
42

            
43
10
    for group in lts.transition_groups() {
44
10
        let (rpos, wpos) = compute_positions(group);
45
10

            
46
10
        read_positions.push(rpos);
47
10
        write_positions.push(wpos);
48
10
    }
49

            
50
10
    for (group_index, group) in lts.transition_groups().iter().enumerate() {
51
10
        debug!("Transition group {}: {:?}", group_index, group,);
52

            
53
10
        debug!("  Read indices: {:?}", read_positions[group_index]);
54
10
        debug!("  Write indices: {:?}", write_positions[group_index]);
55
    }
56

            
57
    // Total number of states for progress reporting.
58
1
    let total_number_of_states = len(storage, lts.states());
59
1
    info!(
60
        "Converting symbolic LTS to explicit LTS with {} states",
61
        LargeFormatter(total_number_of_states)
62
    );
63

            
64
1
    let state_progress = TimeProgress::new(
65
        move |number_of_states| {
66
            info!(
67
                "Added {} states to discovered ({}%)",
68
                LargeFormatter(number_of_states),
69
                number_of_states * 100 / total_number_of_states
70
            );
71
        },
72
        1,
73
    );
74

            
75
    // All states have been explored, so add them to the discovered set immediately.
76
1
    let mut discovered: IndexedSet<Vec<u32>, FxBuildHasher> = IndexedSet::new();
77
1
    let mut state_iter = iter(storage, lts.states());
78
75
    while let Some(state) = state_iter.next() { 
79
74
        let (_, inserted) = discovered.insert(state.clone());
80
74
        debug_assert!(inserted, "State space contains duplicate states");
81
74
        state_progress.print(discovered.len())
82
    }
83

            
84
    // Total number of states for progress reporting.
85
1
    let progress = TimeProgress::new(
86
        move |(number_of_states, number_of_transitions)| {
87
            info!(
88
                "Explored {} states and {} transitions ({}%)",
89
                LargeFormatter(number_of_states),
90
                LargeFormatter(number_of_transitions),
91
                number_of_states * 100 / total_number_of_states
92
            );
93
        },
94
        1,
95
    );
96

            
97
    // Keep track of outgoing transitions to avoid duplicates.
98
1
    let mut outgoing = FxHashSet::default();
99

            
100
    // Avoid reallocations.
101
1
    let mut target = vec![0u32; height(storage, lts.states())];
102

            
103
1
    let mut state_iter = iter(storage, lts.states());
104
1
    let mut index: usize = 0;
105

            
106
75
    while let Some(state) = state_iter.next() {
107
        // Find the index of this state, it was already added before.
108
74
        let state_index = discovered
109
74
            .index(state)
110
74
            .ok_or("Found state that was not in the state set")?;
111

            
112
        // Apply every transition group to this state.
113
740
        for (group_index, group) in lts.transition_groups().iter().enumerate() {
114
740
            let mut iter_transitions = iter(storage, group.relation());
115
3848
            'skip: while let Some(transition) = iter_transitions.next() {
116
                // Try to match the read parameters of this vector.
117
4636
                for (index, i) in group.read_indices().iter().enumerate() {
118
4636
                    if state[*i as usize] != transition[read_positions[group_index][index]] {
119
                        // If they do not match, skip this transition.
120
3016
                        continue 'skip;
121
1620
                    }
122
                }
123

            
124
                // Apply the transition writes to the state vector.
125
92
                target.clone_from_slice(state);
126
92
                trace!("transition {:?}", transition);
127
332
                for (index, i) in group.write_indices().iter().enumerate() {
128
332
                    target[*i as usize] = transition[write_positions[group_index][index]];
129
332
                }
130

            
131
                // Find the action label.
132
92
                let label = &lts.action_labels()[transition[group
133
92
                    .action_label_index()
134
92
                    .ok_or("Transition vector should at least have the action label")?]
135
                    as usize];
136

            
137
                // Find the target state index.
138
92
                let target_index = discovered
139
92
                    .index(&target)
140
92
                    .ok_or("Found state that was not in the state set")?;
141
92
                if outgoing.insert((*state_index, *target_index)) {     
142
92
                    trace!(
143
                        " Found transition in {group_index} from {:?} to {:?} with label {:?}",
144
                        state, target, label
145
                    );
146

            
147
92
                    output.add_transition(StateIndex::new(*state_index), label, StateIndex::new(*target_index))?;
148
                }
149
            }
150
        }
151

            
152
74
        progress.print((index, output.num_of_transitions()));
153
74
        index += 1;
154

            
155
        // Clear the outgoing set for the next state.
156
74
        outgoing.clear();
157
    }
158

            
159
    // Find the initial state.
160
1
    let mut state_iter = iter(storage, lts.initial_state());
161
1
    let initial_state = state_iter
162
1
        .next()
163
1
        .ok_or("Symbolic LTS has no initial state")?;
164

            
165
1
    let initial_state_index = discovered
166
1
        .index(initial_state)
167
1
        .ok_or("Initial state was not found in the discovered state set")?;
168

            
169
1
    output.finish(StateIndex::new(*initial_state_index))
170
1
}
171

            
172
/// Computes the positions of the read and write indices in the transition vector.
173
10
fn compute_positions(group: &impl TransitionGroup) -> (Vec<usize>, Vec<usize>) {
174
    // Ensure indices are non-decreasing; merge relies on sorted inputs.
175
10
    debug_assert!(group.read_indices().is_sorted(), "read_indices must be sorted");
176
10
    debug_assert!(group.write_indices().is_sorted(), "write_indices must be sorted");
177

            
178
10
    let mut rpos = Vec::with_capacity(group.read_indices().len());
179
10
    let mut wpos = Vec::with_capacity(group.write_indices().len());
180

            
181
10
    let mut ri = 0usize;
182
10
    let mut wi = 0usize;
183
10
    let mut index = 0usize;
184

            
185
60
    while ri < group.read_indices().len() && wi < group.write_indices().len() {
186
50
        if group.read_indices()[ri] <= group.write_indices()[wi] {
187
28
            rpos.push(index);
188
28
            ri += 1;
189
28
        } else {
190
22
            wpos.push(index);
191
22
            wi += 1;
192
22
        }
193
50
        index += 1;
194
    }
195

            
196
11
    while ri < group.read_indices().len() {
197
1
        rpos.push(index);
198
1
        ri += 1;
199
1
        index += 1;
200
1
    }
201

            
202
24
    while wi < group.write_indices().len() {
203
14
        wpos.push(index);
204
14
        wi += 1;
205
14
        index += 1;
206
14
    }
207
10
    (rpos, wpos)
208
10
}
209

            
210
#[cfg(test)]
211
mod tests {
212
    use merc_ldd::Storage;
213
    use merc_lts::LTS;
214
    use merc_lts::LtsBuilderMem;
215
    use merc_utilities::test_logger;
216

            
217
    use crate::convert_symbolic_lts;
218
    use crate::read_symbolic_lts;
219

            
220
    #[test]
221
1
    fn test_convert_symbolic_lts() {
222
1
        test_logger();
223

            
224
1
        let input = include_bytes!("../../../examples/lts/abp.sym");
225

            
226
1
        let mut storage = Storage::new();
227
1
        let symbolic_lts = read_symbolic_lts(&mut storage, &input[..]).unwrap();
228

            
229
1
        let mut builder = LtsBuilderMem::new(Vec::new(), Vec::new());
230
1
        let lts = convert_symbolic_lts(&mut storage, &mut builder, &symbolic_lts).unwrap();
231

            
232
1
        debug_assert_eq!(lts.num_of_states(), 74);
233
1
        debug_assert_eq!(lts.num_of_transitions(), 92);
234
1
    }
235
}