1
#![forbid(unsafe_code)]
2

            
3
use merc_collections::BlockIndex;
4
use merc_lts::LTS;
5
use merc_lts::LabelledTransitionSystem;
6
use merc_lts::LtsBuilderFast;
7
use merc_lts::StateIndex;
8

            
9
use crate::BlockPartition;
10
use crate::Partition;
11

            
12
/// Returns a new LTS based on the given partition.
13
///
14
/// The naive version will add the transitions of all states in the block to the quotient LTS.
15
1200
pub fn quotient_lts_naive<L: LTS>(
16
1200
    lts: &L,
17
1200
    partition: &impl Partition,
18
1200
    eliminate_tau_loops: bool,
19
1200
) -> LabelledTransitionSystem<L::Label> {
20
    // Introduce the transitions based on the block numbers, the number of blocks is a decent approximation for the number of transitions.
21
1200
    let mut builder = LtsBuilderFast::with_capacity(
22
1200
        lts.labels().into(),
23
1200
        Vec::new(),
24
1200
        partition.num_of_blocks(), // We expect one transition per state.
25
    );
26

            
27
9606
    for state_index in lts.iter_states() {
28
19020
        for transition in lts.outgoing_transitions(state_index) {
29
19020
            let block = partition.block_number(state_index);
30
19020
            let to_block = partition.block_number(transition.to);
31

            
32
            // If we eliminate tau loops then check if the 'to' and 'from' end up in the same block
33
19020
            if !(eliminate_tau_loops && lts.is_hidden_label(transition.label) && block == to_block) {
34
17793
                debug_assert!(
35
17793
                    partition.block_number(state_index) < partition.num_of_blocks(),
36
                    "Quotienting assumes that the block numbers do not exceed the number of blocks"
37
                );
38

            
39
17793
                builder.add_transition(
40
17793
                    StateIndex::new(block.value()),
41
17793
                    &lts.labels()[transition.label],
42
17793
                    StateIndex::new(to_block.value()),
43
                );
44
1227
            }
45
        }
46
    }
47

            
48
1200
    builder.require_num_of_states(partition.num_of_blocks());
49
1200
    builder.finish(
50
1200
        StateIndex::new(partition.block_number(lts.initial_state_index()).value()),
51
        true,
52
    )
53
1200
}
54

            
55
/// Optimised implementation for block partitions.
56
///
57
/// Chooses a single state in the block as representative. If BRANCHING then the chosen state is a bottom state.
58
100
pub fn quotient_lts_block<L: LTS, const BRANCHING: bool>(
59
100
    lts: &L,
60
100
    partition: &BlockPartition,
61
100
) -> LabelledTransitionSystem<L::Label> {
62
100
    let mut builder = LtsBuilderFast::new(lts.labels().into(), Vec::new());
63

            
64
1569
    for block in (0..partition.num_of_blocks()).map(BlockIndex::new) {
65
        // Pick any state in the block
66
1569
        let mut candidate = if let Some(state) = partition.iter_block(block).next() {
67
1569
            state
68
        } else {
69
            panic!("Blocks in the partition should not be empty {}", block);
70
        };
71

            
72
1569
        if BRANCHING {
73
            // DFS into a bottom state.
74
            let mut found = false;
75
            while !found {
76
                found = true;
77

            
78
                if let Some(trans) = lts
79
                    .outgoing_transitions(candidate)
80
                    .find(|trans| lts.is_hidden_label(trans.label) && partition.block_number(trans.to) == block)
81
                {
82
                    found = false;
83
                    candidate = trans.to;
84
                }
85
            }
86
1569
        }
87

            
88
        // Add all transitions from the representative state.
89
4214
        for transition in lts.outgoing_transitions(candidate) {
90
4214
            if BRANCHING {
91
                // Candidate is a bottom state, so add all transitions.
92
                debug_assert!(
93
                    !(lts.is_hidden_label(transition.label) && partition.block_number(transition.to) == block),
94
                    "This state is not bottom {}",
95
                    block
96
                );
97
4214
            }
98

            
99
4214
            builder.add_transition(
100
4214
                StateIndex::new(*block),
101
4214
                &lts.labels()[transition.label],
102
4214
                StateIndex::new(*partition.block_number(transition.to)),
103
            );
104
        }
105
    }
106

            
107
100
    builder.require_num_of_states(partition.num_of_blocks());
108
100
    builder.finish(
109
100
        StateIndex::new(partition.block_number(lts.initial_state_index()).value()),
110
        true,
111
    )
112
100
}