1
use rand::Rng;
2

            
3
use crate::LabelledTransitionSystem;
4
use crate::LtsBuilderFast;
5
use crate::StateIndex;
6
use crate::TransitionLabel;
7
use crate::product_lts;
8

            
9
/// Generates a random LTS with the desired number of states, labels and out
10
/// degree by composing three smaller random LTSs using the synchronous product.
11
/// This is often a more realistic structure than fully random LTSs, but
12
/// otherwise see [`random_lts_monolithic`].
13
1500
pub fn random_lts(
14
1500
    rng: &mut impl Rng,
15
1500
    num_of_states: usize,
16
1500
    num_of_labels: u32,
17
1500
    outdegree: usize,
18
1500
) -> LabelledTransitionSystem<String> {
19
1500
    let components: Vec<LabelledTransitionSystem<String>> = (0..3)
20
4500
        .map(|_| random_lts_monolithic(rng, num_of_states, num_of_labels, outdegree))
21
1500
        .collect();
22

            
23
    // Synchronize on some of the labels.
24
1500
    let synchronized_labels: Vec<String> = (1..num_of_labels.min(3))
25
3000
        .map(|i| {
26
3000
            char::from_digit(i, 36)
27
3000
                .expect("Radix is less than 37, so should not panic")
28
3000
                .to_string()
29
3000
        })
30
1500
        .collect();
31

            
32
1500
    components
33
1500
        .into_iter()
34
3000
        .reduce(|acc, lts| product_lts(&acc, &lts, Some(synchronized_labels.clone())))
35
1500
        .expect("At least one component should be present")
36
1500
}
37

            
38
/// Generates a monolithic LTS with the desired number of states, labels, out
39
/// degree and in degree for all the states. Uses the given TransitionLabel type
40
/// to generate the transition labels.
41
4700
pub fn random_lts_monolithic<L: TransitionLabel>(
42
4700
    rng: &mut impl Rng,
43
4700
    num_of_states: usize,
44
4700
    num_of_labels: u32,
45
4700
    outdegree: usize,
46
4700
) -> LabelledTransitionSystem<L> {
47
4700
    assert!(
48
4700
        num_of_labels < 26,
49
        "Too many labels requested, we only support alphabetic labels."
50
    );
51

            
52
    // Introduce lower case letters for the labels.
53
4700
    let mut labels: Vec<L> = Vec::new();
54
4700
    labels.push(L::tau_label()); // The initial hidden label, assumed to be index 0.
55
19300
    for i in 0..(num_of_labels - 1) {
56
19300
        labels.push(L::from_index(i as usize));
57
19300
    }
58

            
59
4700
    let mut builder = LtsBuilderFast::with_capacity(labels.clone(), Vec::new(), num_of_states);
60

            
61
59600
    for state_index in 0..num_of_states {
62
        // Introduce outgoing transitions for this state based on the desired out degree.
63
243174
        for _ in 0..rng.random_range(0..outdegree) {
64
240284
            // Pick a random label and state.
65
240284
            let label = rng.random_range(0..num_of_labels);
66
240284
            let to = rng.random_range(0..num_of_states);
67
240284

            
68
240284
            builder.add_transition(
69
240284
                StateIndex::new(state_index),
70
240284
                &labels[label as usize],
71
240284
                StateIndex::new(to),
72
240284
            );
73
240284
        }
74
    }
75

            
76
4700
    if builder.num_of_states() == 0 {
77
41
        // Ensure there is at least one state (otherwise it would be an LTS without initial state).
78
41
        builder.require_num_of_states(1);
79
4659
    }
80

            
81
4700
    builder.finish(StateIndex::new(0), true)
82
4700
}
83

            
84
#[cfg(test)]
85
mod tests {
86
    use super::*;
87

            
88
    use test_log::test;
89

            
90
    use merc_utilities::random_test;
91

            
92
    #[test]
93
    fn random_lts_test() {
94
100
        random_test(100, |rng| {
95
            // This test only checks the assertions of an LTS internally.
96
100
            let _lts = random_lts(rng, 10, 3, 3);
97
100
        });
98
    }
99
}