1
use merc_utilities::MercError;
2

            
3
use crate::Slot;
4

            
5
/// A Linear Process Specification trait.
6
///
7
/// An [`LPS`] describes a transition system implicitly by giving an initial
8
/// state vector and a collection of condition action effect summands. The same
9
/// abstraction also covers similar state-space generators, such as PBESs in SRF
10
/// form.
11
pub trait LPS {
12
    /// The type of the values stored at each position of a state vector.
13
    ///
14
    /// The value must be a [`Slot`] so state vectors can be stored compactly in
15
    /// the discovered set's hash-consed sequence forest.
16
    type Value: Slot;
17

            
18
    /// The label produced for each enumerated transition.
19
    type Label: Clone;
20

            
21
    /// Metadata reported once per discovered state to the exploration caller.
22
    ///
23
    /// Plain LPSs typically use `()`.
24
    type StateInfo;
25

            
26
    /// A single condition action effect summand of the LPS.
27
    type Summand: Summand<Value = Self::Value, Label = Self::Label>;
28

            
29
    /// Returns the initial state vector of the LPS.
30
    fn initial_state(&self) -> Vec<Self::Value>;
31

            
32
    /// Returns the summands that together define the transition relation.
33
    fn summands(&self) -> &[Self::Summand];
34

            
35
    /// Creates a fresh per-thread enumeration context for driving the summands.
36
    ///
37
    /// Each worker thread owns exactly one context; it holds the enumeration
38
    /// backend and the reusable scratch buffers. The LPS definition itself stays
39
    /// immutable and shareable by `&self`, so a single LPS can drive several
40
    /// contexts concurrently. The returned context is threaded through both
41
    /// [`LPS::prepare`] and [`Summand::enumerate`].
42
    fn create_context(&self) -> <Self::Summand as Summand>::Context;
43

            
44
    /// Prepares `context` for enumerating transitions from `state` and returns
45
    /// the indices of the summands that must be explored from `state`. Can be
46
    /// reused across all summands.
47
    ///
48
    /// The returned iterator lets implementations restrict enumeration to the
49
    /// relevant summands; implementations with no such restriction return all
50
    /// summand indices. The returned iterator may borrow both `self` and
51
    /// `state`, so it must be fully consumed before `state` is mutated again.
52
    fn prepare<'a>(
53
        &'a self,
54
        context: &mut <Self::Summand as Summand>::Context,
55
        state: &'a [Self::Value],
56
    ) -> impl Iterator<Item = usize> + 'a;
57

            
58
    /// Returns the state-level metadata for the given source `state`.
59
    fn state_info(&self, state: &[Self::Value]) -> Self::StateInfo;
60
}
61

            
62
impl<P: LPS> LPS for &P {
63
    type Value = P::Value;
64
    type Label = P::Label;
65
    type StateInfo = P::StateInfo;
66
    type Summand = P::Summand;
67

            
68
    fn initial_state(&self) -> Vec<Self::Value> {
69
        (**self).initial_state()
70
    }
71

            
72
    fn summands(&self) -> &[Self::Summand] {
73
        (**self).summands()
74
    }
75

            
76
    fn create_context(&self) -> <Self::Summand as Summand>::Context {
77
        (**self).create_context()
78
    }
79

            
80
    fn prepare<'a>(
81
        &'a self,
82
        context: &mut <Self::Summand as Summand>::Context,
83
        state: &'a [Self::Value],
84
    ) -> impl Iterator<Item = usize> + 'a {
85
        (**self).prepare(context, state)
86
    }
87

            
88
    fn state_info(&self, state: &[Self::Value]) -> Self::StateInfo {
89
        (**self).state_info(state)
90
    }
91
}
92

            
93
/// A condition action effect summand of an [`LPS`].
94
///
95
/// A summand represents a guarded transition: given a current state, it
96
/// reports each outgoing transition (action label and next state vector) it
97
/// produces by invoking the `report` callback. Implementations are free to
98
/// short-circuit when the callback returns an error.
99
pub trait Summand {
100
    /// The state vector element type, matching [`LPS::Value`].
101
    type Value;
102

            
103
    /// The action label type, matching [`LPS::Label`].
104
    type Label;
105

            
106
    /// Enumeration mutates this context instead of any shared `&self` state, so
107
    /// the same `&Summand` can be driven from several threads at once as long
108
    /// as each thread owns a distinct context.
109
    ///
110
    /// Contexts are produced by [`LPS::create_context`] rather than constructed
111
    /// directly, so a context can own backend state that depends on the LPS
112
    /// definition.
113
    type Context;
114

            
115
    /// Enumerate every outgoing transition produced by this summand from the
116
    /// state vector `state`.
117
    ///
118
    /// For each transition, `report(label, next_state)` is invoked exactly once
119
    /// with borrowed values.
120
    fn enumerate<F>(&self, context: &mut Self::Context, state: &[Self::Value], report: F) -> Result<(), MercError>
121
    where
122
        F: FnMut(&Self::Label, &[Self::Value]) -> Result<(), MercError>;
123

            
124
    /// Returns the indices into the state vector whose values fully determine
125
    /// this summand's enumeration result. Used as the cache
126
    /// key by [`crate::CacheLPS`].
127
    ///
128
    /// This is a *correctness* contract under caching, not a hint: when the
129
    /// summand is cached, two source states that agree on these positions are
130
    /// assumed to enumerate identical results, so any position that influences
131
    /// the guard or the written values must be listed.
132
    fn read_positions(&self) -> &[usize];
133

            
134
    /// Returns the indices into the state vector that this summand may change.
135
    /// Every position *not* in this set is passed through unchanged from the
136
    /// source state to each enumerated next state.
137
    ///
138
    /// This is also a *correctness* contract under caching.
139
    fn write_positions(&self) -> &[usize];
140
}