1use crate::precedence::Precedence;
12use crate::semantics::{perform_among, settle};
13use expectation_model::{Condition, ExpId, Expectation, SectionId, Suite, VarId};
14use std::collections::{BTreeSet, HashMap};
15
16#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
17pub enum Node {
18 Start,
19 Exp(ExpId),
20}
21
22#[derive(Clone, Debug)]
24pub struct Route {
25 pub start: Condition,
26 pub steps: Vec<ExpId>,
27}
28
29#[derive(Clone, Copy, Debug)]
30pub struct Limits {
31 pub depth: u32,
32 pub states: usize,
33}
34
35impl Default for Limits {
36 fn default() -> Self {
37 Limits { depth: 24, states: 5000 }
38 }
39}
40
41#[derive(Debug, Default)]
42pub struct Flow {
43 pub level: Vec<Option<u32>>,
45 pub edges: BTreeSet<(Node, ExpId)>,
47 pub routes: Vec<Option<Route>>,
49 pub states: usize,
50 pub truncated: bool,
52}
53
54impl Flow {
55 pub fn parents(&self, e: ExpId) -> impl Iterator<Item = Node> + '_ {
56 self.edges.iter().filter(move |(_, c)| *c == e).map(|(p, _)| *p)
57 }
58
59 pub fn children(&self, n: Node) -> impl Iterator<Item = ExpId> + '_ {
60 self.edges.iter().filter(move |(p, _)| *p == n).map(|(_, c)| *c)
61 }
62
63 fn record(&mut self, e: ExpId, level: u32, from: Node, route: impl FnOnce() -> Route) {
64 let i = e.0 as usize;
65 match self.level[i] {
66 None => {
67 self.level[i] = Some(level);
68 self.routes[i] = Some(route());
69 self.edges.insert((from, e));
70 }
71 Some(l) if l == level => {
72 self.edges.insert((from, e));
73 }
74 _ => {}
75 }
76 }
77}
78
79struct State {
80 cond: Condition,
81 section: SectionId,
83 level: u32,
84 parent: Option<usize>,
85 via: Option<ExpId>,
86 start: Condition,
88 touched: BTreeSet<VarId>,
90}
91
92impl State {
93 fn from(&self) -> Node {
94 self.via.map_or(Node::Start, Node::Exp)
95 }
96}
97
98fn steps_to(states: &[State], mut i: usize) -> Vec<ExpId> {
99 let mut out = vec![];
100 while let (Some(p), Some(v)) = (states[i].parent, states[i].via) {
101 out.push(v);
102 i = p;
103 }
104 out.reverse();
105 out
106}
107
108pub fn in_scope(suite: &Suite, section: SectionId) -> Vec<bool> {
111 let mut scope = vec![false; suite.expectations.len()];
112 let chain = suite.chain(section);
113 let top = chain.get(1).unwrap_or(&chain[0]).id;
114 for s in suite.subtree(top) {
115 for &f in &suite.section(s).features {
116 for &e in &suite.feature(f).expectations {
117 scope[e.0 as usize] = true;
118 }
119 }
120 }
121 scope
122}
123
124pub fn explore(suite: &Suite, prec: &Precedence, roots: &[(Condition, SectionId)], limits: Limits) -> Flow {
127 let n = suite.expectations.len();
128 let mut flow = Flow { level: vec![None; n], routes: vec![None; n], ..Flow::default() };
129 let mut states: Vec<State> = vec![];
130 let mut seen: HashMap<Condition, usize> = HashMap::new();
131
132 let mut scopes: HashMap<SectionId, Vec<bool>> = HashMap::new();
133 for (root, section) in roots {
134 let scope = scopes.entry(*section).or_insert_with(|| in_scope(suite, *section));
135 let (cond, invariants) = settle(suite, prec, root, &[]);
136 if !cond.is_satisfiable() || seen.contains_key(&cond) {
137 continue;
138 }
139 for inv in invariants.into_iter().filter(|e| scope[e.0 as usize]) {
140 flow.record(inv, 0, Node::Start, || Route { start: root.clone(), steps: vec![] });
141 }
142 seen.insert(cond.clone(), states.len());
143 let section = *section;
144 states.push(State {
145 cond,
146 section,
147 level: 0,
148 parent: None,
149 via: None,
150 start: root.clone(),
151 touched: BTreeSet::new(),
152 });
153 }
154
155 let transitions: Vec<&Expectation> = suite.transitions().collect();
156 let companions = companions(&transitions);
157 let mut i = 0;
158 while i < states.len() {
159 let scope = &scopes[&states[i].section];
160 for (t, e) in transitions.iter().enumerate() {
161 let s = &states[i];
162 if !scope[e.id.0 as usize] || !s.cond.overlaps(&e.given) {
163 continue;
164 }
165 let pre = s.cond.and(&e.given);
168 let out = perform_among(suite, prec, &pre, &e.trigger, companions[t].iter().map(|&c| transitions[c]));
169 if !out.fired.contains(&e.id) {
170 continue;
171 }
172 let assumed = s.cond.unproven(&e.given).restrict(e.given.vars().filter(|v| !s.touched.contains(v)));
173 let start = s.start.and(&assumed);
174 let route = || {
175 let mut steps = steps_to(&states, i);
176 steps.push(e.id);
177 Route { start: start.clone(), steps }
178 };
179 let (level, from) = (s.level, s.from());
180 for &f in &out.fired {
181 flow.record(f, level, from, route);
182 }
183 if !out.after.is_satisfiable() {
184 continue;
185 }
186 for &inv in out.invariants.iter().filter(|e| scope[e.0 as usize]) {
187 flow.record(inv, level + 1, Node::Exp(e.id), route);
188 }
189 if seen.contains_key(&out.after) {
190 continue;
191 }
192 if level + 1 > limits.depth || states.len() >= limits.states {
193 flow.truncated = true;
194 continue;
195 }
196 let mut touched = states[i].touched.clone();
197 touched.extend(e.trigger.changes.vars());
198 for &f in &out.fired {
199 touched.extend(suite.exp(f).sets());
200 }
201 seen.insert(out.after.clone(), states.len());
202 let section = states[i].section;
203 states.push(State {
204 cond: out.after,
205 section,
206 level: level + 1,
207 parent: Some(i),
208 via: Some(e.id),
209 start,
210 touched,
211 });
212 }
213 i += 1;
214 }
215 flow.states = states.len();
216 flow
217}
218
219fn companions(transitions: &[&Expectation]) -> Vec<Vec<usize>> {
221 transitions
222 .iter()
223 .map(|e| (0..transitions.len()).filter(|&c| e.trigger.may_fire(&transitions[c].trigger)).collect())
224 .collect()
225}
226
227pub fn route_to(suite: &Suite, prec: &Precedence, from: &Condition, goal: &Condition, limits: Limits) -> Option<Route> {
236 use std::cmp::Reverse;
237 use std::collections::BinaryHeap;
238
239 let (start, _) = settle(suite, prec, from, &[]);
242 let mut relevant: BTreeSet<VarId> =
243 goal.iter().filter(|(v, g)| !start.get(*v).is_some_and(|x| x.within(g))).map(|(v, _)| v).collect();
244 loop {
245 let before = relevant.len();
246 for e in suite.transitions() {
247 if e.sets().any(|v| relevant.contains(&v)) {
248 relevant.extend(e.given.vars().chain(e.trigger.changes.vars()));
249 }
250 }
251 if relevant.len() == before {
252 break;
253 }
254 }
255 let transitions: Vec<&Expectation> =
256 suite.transitions().filter(|e| e.sets().any(|v| relevant.contains(&v))).collect();
257 let companions = companions(&transitions);
258 let wrong = |c: &Condition| goal.iter().filter(|(v, g)| !c.get(*v).is_some_and(|x| x.within(g))).count() as u32;
259
260 let mut states: Vec<(Condition, Option<usize>, Option<ExpId>, u32)> = vec![(start.clone(), None, None, 0)];
262 let mut seen: HashMap<Condition, usize> = HashMap::from([(start.clone(), 0)]);
263 let mut queue = BinaryHeap::from([(Reverse(wrong(&start)), Reverse(0usize))]);
264 while let Some((_, Reverse(i))) = queue.pop() {
266 if states[i].0.implies(goal) {
267 let mut steps = vec![];
268 let mut at = i;
269 while let (Some(p), Some(v)) = (states[at].1, states[at].2) {
270 steps.push(v);
271 at = p;
272 }
273 steps.reverse();
274 let replay = |steps: &[ExpId]| {
276 let mut state = start.clone();
277 for &id in steps {
278 let t = transitions.iter().position(|e| e.id == id)?;
279 let e = transitions[t];
280 if !state.implies(&e.given) {
281 return None;
282 }
283 let candidates = companions[t].iter().map(|&c| transitions[c]);
284 let out = perform_among(suite, prec, &state, &e.trigger, candidates);
285 if !out.fired.contains(&e.id) || !out.uncertain.is_empty() || !out.after.is_satisfiable() {
286 return None;
287 }
288 state = out.after;
289 }
290 Some(state)
291 };
292 let mut k = 0;
293 while k < steps.len() {
294 let mut fewer = steps.clone();
295 fewer.remove(k);
296 if replay(&fewer).is_some_and(|end| end.implies(goal)) {
297 steps = fewer;
298 } else {
299 k += 1;
300 }
301 }
302 let mut swapped = true;
305 while swapped {
306 swapped = false;
307 for k in 1..steps.len() {
308 if steps[k] < steps[k - 1] {
309 let mut other = steps.clone();
310 other.swap(k - 1, k);
311 if replay(&other).is_some_and(|end| end.implies(goal)) {
312 steps = other;
313 swapped = true;
314 }
315 }
316 }
317 }
318 return Some(Route { start: from.clone(), steps });
319 }
320 let depth = states[i].3;
321 if depth >= limits.depth {
322 continue;
323 }
324 for (t, e) in transitions.iter().enumerate() {
325 if states.len() >= limits.states || !states[i].0.implies(&e.given) {
327 continue;
328 }
329 let out =
330 perform_among(suite, prec, &states[i].0, &e.trigger, companions[t].iter().map(|&c| transitions[c]));
331 if !out.fired.contains(&e.id) || !out.uncertain.is_empty() || !out.after.is_satisfiable() {
332 continue;
333 }
334 if !seen.contains_key(&out.after) {
335 let n = states.len();
336 seen.insert(out.after.clone(), n);
337 queue.push((Reverse(2 * (depth + 1) + wrong(&out.after)), Reverse(n)));
339 states.push((out.after, Some(i), Some(e.id), depth + 1));
340 }
341 }
342 }
343 None
344}