1
use log::trace;
2

            
3
use crate::LTS;
4
use crate::LabelIndex;
5
use crate::LabelledTransitionSystem;
6
use crate::LtsBuilder;
7
use crate::LtsBuilderFast;
8
use crate::StateIndex;
9
use crate::TransitionLabel;
10

            
11
/// Performs a reachability analysis on the given LTS using a depth-first search
12
/// from the initial state.
13
///
14
/// Only follows transitions for which `filter` returns `true`.
15
/// Calls `visit` for every newly reached state, including the start state.
16
384896
pub fn reachability<P, F, L: LTS>(lts: &L, state: StateIndex, mut filter: F, mut visit: P)
17
384896
where
18
384896
    P: FnMut(StateIndex),
19
384896
    F: FnMut(LabelIndex) -> bool,
20
{
21
384896
    let mut reachable = vec![false; lts.num_of_states()];
22
384896
    visit(state);
23
384896
    reachable[state] = true;
24

            
25
384896
    let mut stack = vec![state];
26

            
27
1347662
    while let Some(state) = stack.pop() {
28
962766
        debug_assert!(reachable[state], "State {} must already be marked as reachable", state);
29
962766
        trace!("Visiting {}", state);
30

            
31
4278632
        for transition in lts.outgoing_transitions(state) {
32
4278632
            if filter(transition.label) && !reachable[transition.to] {
33
577870
                trace!("Transition -[{}]-> {}", transition.label, transition.to);
34
577870
                reachable[transition.to] = true;
35
577870
                visit(transition.to);
36
577870
                stack.push(transition.to);
37
3700762
            }
38
        }
39
    }
40

            
41
384896
    trace!("Finished reachability");
42
384896
}
43

            
44
/// Returns the number of states reachable from the given state of the LTS.
45
400
pub fn num_reachable_states<L: LTS>(lts: &L, state: StateIndex) -> usize {
46
400
    let mut count = 0;
47
400
    reachability(lts, state, |_| true, |_| count += 1);
48
400
    count
49
400
}
50

            
51
/// Returns a new LTS containing only the states and transitions reachable from
52
/// the initial state of `lts`.
53
pub fn reachable_lts<L: LTS>(lts: &L) -> LabelledTransitionSystem<L::Label>
54
where
55
    L::Label: TransitionLabel,
56
{
57
    let initial = lts.initial_state_index();
58

            
59
    // Collect reachable states in discovery order.
60
    let mut old_to_new: Vec<Option<StateIndex>> = vec![None; lts.num_of_states()];
61
    let mut new_to_old: Vec<StateIndex> = Vec::new();
62

            
63
    reachability(
64
        lts,
65
        initial,
66
        |_| true,
67
        |state| {
68
            let new_index = StateIndex::new(new_to_old.len());
69
            old_to_new[state] = Some(new_index);
70
            new_to_old.push(state);
71
        },
72
    );
73

            
74
    let num_reachable = new_to_old.len();
75

            
76
    // Use LtsBuilderFast to collect the reachable transitions with renumbered state indices.
77
    let labels = lts.labels().to_vec();
78
    let mut builder = LtsBuilderFast::new(labels, vec![]);
79
    builder.require_num_of_states(num_reachable);
80

            
81
    for &old_state in &new_to_old {
82
        let new_from = old_to_new[old_state].expect("reachable state must have a mapping");
83
        for t in lts.outgoing_transitions(old_state) {
84
            if let Some(new_to) = old_to_new[t.to] {
85
                let label = &lts.labels()[t.label.value()];
86
                builder
87
                    .add_transition(new_from, label, new_to)
88
                    .expect("transition should be valid");
89
            }
90
        }
91
    }
92

            
93
    builder.finish(StateIndex::new(0), false)
94
}