1use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
44use std::hash::{BuildHasherDefault, Hasher};
45
46use super::ir::{Automaton, LeafId, ThreadStateId, Transition};
47use super::property::{Prop, PropEnv, PropId, PropertyIr};
48use super::trace::{Frame, Matches, read_local};
49
50const INF: u64 = u64::MAX;
52const MAX_FRAMES: usize = 1 << 20;
55const MAX_MEMO: usize = 1 << 16;
57
58#[derive(Default, Clone, Copy)]
59struct Fx(u64);
60
61impl Fx {
62 #[inline]
63 fn add(&mut self, w: u64) {
64 self.0 = (self.0.rotate_left(5) ^ w).wrapping_mul(0x517c_c1b7_2722_0a95);
65 }
66}
67
68impl Hasher for Fx {
69 #[inline]
70 fn finish(&self) -> u64 {
71 self.0
72 }
73 #[inline]
74 fn write(&mut self, bytes: &[u8]) {
75 for &b in bytes {
76 self.add(u64::from(b));
77 }
78 }
79 #[inline]
80 fn write_u8(&mut self, i: u8) {
81 self.add(u64::from(i));
82 }
83 #[inline]
84 fn write_u32(&mut self, i: u32) {
85 self.add(u64::from(i));
86 }
87 #[inline]
88 fn write_u64(&mut self, i: u64) {
89 self.add(i);
90 }
91 #[inline]
92 fn write_i64(&mut self, i: i64) {
93 self.add(i as u64);
94 }
95 #[inline]
96 fn write_usize(&mut self, i: usize) {
97 self.add(i as u64);
98 }
99}
100
101type ByStart = BTreeMap<u64, smallvec::SmallVec<[(u32, u64, u32, bool); 1]>>;
104type FxMap<K, V> = HashMap<K, V, BuildHasherDefault<Fx>>;
105type FxSet<K> = HashSet<K, BuildHasherDefault<Fx>>;
106
107#[derive(Clone, Copy, PartialEq, Eq, Debug)]
109struct Rng {
110 state: u32,
111 frame: u32,
112 lo: u64,
113 hi: u64,
114}
115
116impl Rng {
117 fn same_key(&self, o: &Rng) -> bool {
118 self.state == o.state && self.frame == o.frame
119 }
120
121 fn joins(&self, o: &Rng) -> bool {
123 self.lo <= o.hi.saturating_add(1) && o.lo <= self.hi.saturating_add(1)
124 }
125}
126
127fn add_range(pool: &mut Vec<Rng>, mut a: Rng) {
130 loop {
131 let hit = pool.iter().position(|e| e.same_key(&a) && e.joins(&a));
132 match hit {
133 Some(i) => {
134 let e = pool.swap_remove(i);
135 a.lo = a.lo.min(e.lo);
136 a.hi = a.hi.max(e.hi);
137 }
138 None => break,
139 }
140 }
141 pool.push(a);
142}
143
144#[derive(Clone)]
146struct Frames {
147 list: Vec<Frame>,
148 ids: FxMap<Frame, u32>,
149 empty: u32,
151}
152
153impl Frames {
154 fn new() -> Self {
155 let mut f = Frames {
156 list: Vec::new(),
157 ids: FxMap::default(),
158 empty: 0,
159 };
160 f.empty = f.intern_owned(Vec::new());
161 f
162 }
163
164 fn len(&self) -> usize {
165 self.list.len()
166 }
167
168 fn get(&self, id: u32) -> &Frame {
169 &self.list[id as usize]
170 }
171
172 fn intern(&mut self, f: &[Option<i64>]) -> u32 {
173 if f.is_empty() {
174 return self.empty;
175 }
176 if let Some(&id) = self.ids.get(f) {
177 return id;
178 }
179 self.intern_owned(f.to_vec())
180 }
181
182 fn intern_owned(&mut self, f: Frame) -> u32 {
183 if let Some(&id) = self.ids.get(&f) {
184 return id;
185 }
186 let id = self.list.len() as u32;
187 self.list.push(f.clone());
188 self.ids.insert(f, id);
189 id
190 }
191}
192
193#[derive(Clone)]
195struct Seq {
196 processed: u64,
198 pool: Vec<Rng>,
200 matches: Vec<(u64, u32)>,
203 false_at: u64,
206 done: bool,
209 poisoned: bool,
211}
212
213#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
215enum Want {
216 Exists,
218 All,
220 ExtraCuts,
223 ExtraCutsAll,
226}
227
228#[derive(Clone, PartialEq, Eq, Hash)]
230struct MemoKey {
231 sig: Vec<(u32, u32, u64, u64)>,
233 k: u32,
235 top: bool,
236 want: Want,
237}
238
239#[derive(Clone)]
240enum Found {
241 Bool(bool),
242 List(Vec<(u32, u32)>),
244}
245
246#[derive(Clone)]
249struct Core {
250 frames: Frames,
251 memo: FxMap<MemoKey, Found>,
252 span: u32,
253 exact: bool,
256 stack: Vec<(u32, u32)>,
257 seen: FxSet<(u32, u32)>,
258 adds: Vec<Rng>,
259 hits: Vec<u32>,
260 sig_buf: Vec<(u32, u32, u64, u64)>,
263}
264
265fn one_letter_guard(au: &Automaton, entry: ThreadStateId) -> Option<LeafId> {
270 let [edge] = au.states[entry.0 as usize].edges.as_slice() else {
271 return None;
272 };
273 let guard = edge.guard?;
274 if !matches!(edge.label, Transition::Epsilon) {
275 return None;
276 }
277 let [end] = au.states[edge.target.0 as usize].edges.as_slice() else {
278 return None;
279 };
280 (end.guard.is_none() && matches!(end.label, Transition::Match { .. })).then_some(guard)
281}
282
283type Real<'a> = Option<(&'a dyn PropEnv, u64)>;
284
285impl Core {
286 fn process_tick(
292 &mut self,
293 au: &Automaton,
294 real: Real<'_>,
295 letter: Option<bool>,
296 t: u64,
297 pool: &mut Vec<Rng>,
298 out: &mut Vec<(u64, u32)>,
299 ) -> Result<bool, ()> {
300 self.stack.clear();
301 self.seen.clear();
302 self.adds.clear();
303 self.hits.clear();
304 for r in pool.iter_mut() {
305 debug_assert!(r.lo >= t);
306 if r.lo == t {
307 self.stack.push((r.state, r.frame));
308 r.lo += 1;
309 }
310 }
311 pool.retain(|r| r.lo <= r.hi);
312 while let Some((s, f)) = self.stack.pop() {
313 if !self.seen.insert((s, f)) {
314 continue;
315 }
316 for edge in &au.states[s as usize].edges {
317 if let Some(g) = edge.guard {
318 let holds = match (letter, real) {
319 (Some(top), _) => top,
320 (None, Some((env, base))) => {
321 env.value(g, (t - base) as u32, self.frames.get(f).as_slice())
322 .map_err(|_| ())?
323 != 0
324 }
325 (None, None) => return Err(()),
326 };
327 if !holds {
328 continue;
329 }
330 }
331 let target = edge.target.0;
332 match &edge.label {
333 Transition::Epsilon => self.stack.push((target, f)),
334 Transition::Delay { min, max } => {
335 let hi = match max {
336 Some(m) => t + u64::from(*m),
337 None => INF,
338 };
339 if *min == 0 {
340 self.stack.push((target, f));
341 if hi > t {
342 self.adds.push(Rng {
343 state: target,
344 frame: f,
345 lo: t + 1,
346 hi,
347 });
348 }
349 } else {
350 let lo = t + u64::from(*min);
351 if lo <= hi {
352 self.adds.push(Rng {
353 state: target,
354 frame: f,
355 lo,
356 hi,
357 });
358 }
359 }
360 }
361 Transition::Match { .. } => self.hits.push(f),
362 Transition::LocalVarWrite { var, value } => {
363 let raw = match (letter, real) {
364 (Some(_), _) => 0,
365 (None, Some((env, base))) => env
366 .value(*value, (t - base) as u32, self.frames.get(f).as_slice())
367 .map_err(|_| ())?,
368 (None, None) => return Err(()),
369 };
370 let v = au.local_var(*var).cast(raw);
371 let mut f2 = self.frames.get(f).clone();
372 f2[var.0 as usize] = Some(v);
373 let id = self.frames.intern_owned(f2);
374 self.stack.push((target, id));
375 }
376 Transition::LocalVarCopy { dst, src } => {
377 let src_frame = self.frames.get(f);
378 let mut f2 = src_frame.clone();
379 super::wide::copy_local(
382 &mut f2,
383 *dst,
384 au.local_var(*dst),
385 src_frame,
386 *src,
387 au.local_var(*src),
388 );
389 let id = self.frames.intern_owned(f2);
390 self.stack.push((target, id));
391 }
392 Transition::LocalVarStep { var, delta } => {
393 let decl = au.local_var(*var);
394 let mut f2 = self.frames.get(f).clone();
395 if decl.is_wide() {
396 super::wide::step_local(&mut f2, *var, decl, *delta).ok_or(())?;
397 } else {
398 let cur = read_local(self.frames.get(f), *var).map_err(|_| ())?;
399 f2[var.0 as usize] = Some(decl.cast(cur.wrapping_add(*delta)));
400 }
401 let id = self.frames.intern_owned(f2);
402 self.stack.push((target, id));
403 }
404 _ => return Err(()),
406 }
407 }
408 }
409 let mut adds = std::mem::take(&mut self.adds);
411 for a in adds.drain(..) {
412 add_range(pool, a);
413 }
414 self.adds = adds;
415 let hit = !self.hits.is_empty();
416 if hit {
417 self.hits.sort_unstable();
418 self.hits.dedup();
419 out.extend(self.hits.iter().map(|&f| (t, f)));
420 }
421 Ok(hit)
422 }
423
424 fn tail_run(
428 &mut self,
429 au: &Automaton,
430 pool: &[Rng],
431 r: u64,
432 k: u32,
433 top: bool,
434 exists: bool,
435 ) -> Result<Found, ()> {
436 let h = r + u64::from(k) + u64::from(self.span) + 1;
437 let mut pool: Vec<Rng> = pool.iter().filter(|x| x.lo < h).copied().collect();
438 let mut found: Vec<(u64, u32)> = Vec::new();
439 let stop_early = exists && !self.exact;
440 let mut any = false;
441 let mut t = r;
442 while let Some(next) = pool.iter().map(|x| x.lo).min() {
443 t = t.max(next);
444 if t >= h {
445 break;
446 }
447 let letter = if t - r < u64::from(k) { false } else { top };
448 let hit = self.process_tick(au, None, Some(letter), t, &mut pool, &mut found)?;
449 if hit {
450 any = true;
451 if stop_early {
452 return Ok(Found::Bool(true));
453 }
454 }
455 t += 1;
456 }
457 if exists {
458 return Ok(Found::Bool(any));
459 }
460 Ok(Found::List(
461 found
462 .into_iter()
463 .map(|(t, f)| ((t - r) as u32, f))
464 .collect(),
465 ))
466 }
467
468 fn tail(
470 &mut self,
471 au: &Automaton,
472 pool: &[Rng],
473 r: u64,
474 k: u32,
475 top: bool,
476 want: Want,
477 ) -> Result<Found, ()> {
478 if pool.is_empty() {
479 return Ok(match want {
480 Want::All => Found::List(Vec::new()),
481 Want::Exists => Found::Bool(false),
482 Want::ExtraCuts | Want::ExtraCutsAll => Found::Bool(false),
484 });
485 }
486 let h = r + u64::from(k) + u64::from(self.span) + 1;
487 let mut sig = std::mem::take(&mut self.sig_buf);
488 sig.clear();
489 sig.extend(
490 pool.iter()
491 .filter(|x| x.lo < h)
492 .map(|x| (x.state, x.frame, x.lo - r, x.hi.min(h - 1) - r)),
493 );
494 sig.sort_unstable();
495 let key = MemoKey { sig, k, top, want };
496 if let Some(v) = self.memo.get(&key) {
497 let v = v.clone();
498 self.sig_buf = key.sig;
499 return Ok(v);
500 }
501 let res = match want {
502 Want::Exists => self.tail_run(au, pool, r, k, top, true)?,
503 Want::All => self.tail_run(au, pool, r, k, top, false)?,
504 Want::ExtraCuts | Want::ExtraCutsAll => {
505 let mut all = true;
506 for kk in 1..=self.span + 1 {
507 match self.tail_run(au, pool, r, kk, true, true)? {
508 Found::Bool(true) => {}
509 _ => {
510 all = false;
511 if want == Want::ExtraCuts {
512 break;
513 }
514 }
515 }
516 }
517 Found::Bool(all)
518 }
519 };
520 if self.memo.len() >= MAX_MEMO {
521 self.memo.clear();
522 }
523 self.memo.insert(key, res.clone());
524 Ok(res)
525 }
526
527 fn advance(
529 &mut self,
530 au: &Automaton,
531 real: (&dyn PropEnv, u64),
532 seq: &mut Seq,
533 to: u64,
534 ) -> Result<(), ()> {
535 while seq.processed < to {
536 let Some(next) = seq.pool.iter().map(|x| x.lo).min() else {
537 seq.processed = to;
538 break;
539 };
540 if next > seq.processed {
541 seq.processed = next.min(to);
542 continue;
543 }
544 let t = seq.processed;
545 self.process_tick(au, Some(real), None, t, &mut seq.pool, &mut seq.matches)?;
546 seq.processed = t + 1;
547 }
548 Ok(())
549 }
550
551 fn advance_weak(
554 &mut self,
555 au: &Automaton,
556 real: (&dyn PropEnv, u64),
557 seq: &mut Seq,
558 to: u64,
559 ) -> Result<(), ()> {
560 while seq.processed < to && seq.false_at == INF && !seq.done {
561 let t = seq.processed;
562 self.process_tick(au, Some(real), None, t, &mut seq.pool, &mut seq.matches)?;
563 seq.processed = t + 1;
564 let n = t + 1;
565 let has = !seq.matches.is_empty();
566 if has && !self.exact {
567 seq.done = true;
568 seq.pool.clear();
569 seq.processed = to;
570 break;
571 }
572 let ran = if seq.pool.is_empty() {
573 false
574 } else {
575 matches!(
576 self.tail(au, &seq.pool, n, 0, true, Want::Exists)?,
577 Found::Bool(true)
578 )
579 };
580 if !has && !ran {
581 seq.false_at = n;
582 }
583 }
584 Ok(())
585 }
586}
587
588#[derive(Clone)]
590pub struct Accel {
591 base: u64,
593 off: bool,
594 seqs: FxMap<(u32, u64, u32, bool), Seq>,
595 by_start: ByStart,
596 core: Core,
597 reach: Vec<Option<u32>>,
600 fins: Vec<Fin>,
603 memo_ok: bool,
606}
607
608#[derive(Clone, Default)]
619struct Fin {
620 next: u64,
622 blocked: bool,
625 keep_ones: bool,
626 keep_zeros: bool,
627 ones: BTreeSet<u64>,
628 zeros: BTreeSet<u64>,
629}
630
631impl Fin {
632 fn evict(&mut self, base: u64) {
633 if self.next < base {
634 self.next = base;
635 self.blocked = false;
636 }
637 if self.ones.first().is_some_and(|&t| t < base) {
638 self.ones = self.ones.split_off(&base);
639 }
640 if self.zeros.first().is_some_and(|&t| t < base) {
641 self.zeros = self.zeros.split_off(&base);
642 }
643 }
644}
645
646fn seq_reach(au: &Automaton, entry: u32) -> Option<u32> {
651 let n = au.states.len();
652 let mut color = vec![0u8; n];
653 let mut best = vec![0u32; n];
654 let mut stack = vec![(entry as usize, 0usize)];
655 *color.get_mut(entry as usize)? = 1;
656 while let Some(&(s, i)) = stack.last() {
657 let edges = &au.states[s].edges;
658 if let Some(e) = edges.get(i) {
659 stack.last_mut()?.1 += 1;
660 if matches!(e.label, Transition::Match { .. }) {
661 continue;
663 }
664 let t = e.target.0 as usize;
665 match *color.get(t)? {
666 1 => return None,
667 0 => {
668 color[t] = 1;
669 stack.push((t, 0));
670 }
671 _ => {}
672 }
673 } else {
674 let mut longest = 0u32;
675 for e in edges {
676 let w = match e.label {
677 Transition::Match { .. } => continue,
678 Transition::Delay { min, max } => max?.max(min),
679 _ => 0,
680 };
681 longest = longest.max(w.checked_add(best[e.target.0 as usize])?);
682 }
683 if longest > MAX_REACH {
684 return None;
685 }
686 best[s] = longest;
687 color[s] = 2;
688 stack.pop();
689 }
690 }
691 Some(best[entry as usize] + 1)
692}
693
694const MAX_REACH: u32 = 1 << 16;
696
697fn reach_table(ir: &PropertyIr) -> Vec<Option<u32>> {
703 let au = &ir.automaton;
704 let mut seqs: FxMap<u32, Option<u32>> = FxMap::default();
705 let mut out: Vec<Option<u32>> = Vec::with_capacity(ir.props.len());
706 for prop in &ir.props {
707 let get = |out: &[Option<u32>], id: PropId| out.get(id.0 as usize).copied().flatten();
708 let mut seq = |entry: ThreadStateId| {
709 *seqs
710 .entry(entry.0)
711 .or_insert_with(|| seq_reach(au, entry.0))
712 };
713 let r = match prop {
714 Prop::Seq { seq: s, .. } => seq(s.entry),
715 Prop::Not(p) | Prop::Clocked { p, .. } => get(&out, *p),
716 Prop::And(a, b) | Prop::Or(a, b) => {
717 get(&out, *a).zip(get(&out, *b)).map(|(a, b)| a.max(b))
718 }
719 Prop::If { then, els, .. } => {
720 let e = els.map_or(Some(0), |e| get(&out, e));
721 get(&out, *then).zip(e).map(|(a, b)| a.max(b))
722 }
723 Prop::Case { arms, default, .. } => {
724 let mut r = default.map_or(Some(0), |d| get(&out, d));
725 for arm in arms {
726 r = r.zip(get(&out, arm.body)).map(|(a, b)| a.max(b));
727 }
728 r
729 }
730 Prop::Implication {
731 ante,
732 cons,
733 overlapped,
734 } => seq(ante.entry)
735 .zip(get(&out, *cons))
736 .map(|(a, c)| a.saturating_add(c).saturating_add(u32::from(!*overlapped))),
737 Prop::Nexttime { p, n, .. } => get(&out, *p).map(|r| r.saturating_add(*n)),
738 Prop::Always { p, hi, .. } | Prop::Eventually { p, hi, .. } => {
739 hi.zip(get(&out, *p)).map(|(h, r)| h.saturating_add(r))
740 }
741 Prop::Until { .. } | Prop::Abort { .. } | Prop::Let { .. } | Prop::Call { .. } => None,
742 };
743 out.push(r.filter(|&r| r <= MAX_REACH));
744 }
745 out
746}
747
748pub fn automaton_span(au: &Automaton) -> u32 {
751 1 + au
752 .states
753 .iter()
754 .flat_map(|s| &s.edges)
755 .map(|e| match e.label {
756 Transition::Delay { min, max } => max.unwrap_or(min).max(1),
757 _ => 1,
758 })
759 .sum::<u32>()
760}
761
762impl Accel {
763 pub fn new(
770 ir: &PropertyIr,
771 n_clocks: usize,
772 has_calls: bool,
773 locals_read: bool,
774 ) -> Option<Accel> {
775 let au = &ir.automaton;
776 if n_clocks > 1
777 || has_calls
778 || !au.end_points.is_empty()
779 || au.first_match_groups != 0
780 || ir
781 .props
782 .iter()
783 .any(|p| matches!(p, Prop::Abort { .. } | Prop::Call { .. }))
784 {
785 return None;
786 }
787 let mut exact = locals_read;
788 let mut reads = |l: LeafId| Some(l) == ir.disable;
789 for e in au.states.iter().flat_map(|s| &s.edges) {
790 if e.guard.is_some_and(&mut reads) {
791 return None;
792 }
793 match &e.label {
794 Transition::Epsilon
795 | Transition::Delay { .. }
796 | Transition::Match { .. }
797 | Transition::LocalVarCopy { .. } => {}
798 Transition::LocalVarWrite { value, .. } => {
799 if reads(*value) {
800 return None;
801 }
802 }
803 Transition::LocalVarStep { .. } => exact = true,
804 _ => return None,
805 }
806 }
807 let reads_disable = ir.props.iter().any(|p| match p {
812 Prop::If { cond, .. } => reads(*cond),
813 Prop::Case { selector, arms, .. } => {
814 reads(*selector) || arms.iter().any(|a| a.labels.iter().any(|&l| reads(l)))
815 }
816 _ => false,
817 });
818 let memo_ok = !exact && !reads_disable;
819 let mut fins = Vec::new();
820 if memo_ok {
821 fins.resize_with(ir.props.len(), Fin::default);
822 for p in &ir.props {
823 match p {
824 Prop::Always { p, .. } => fins[p.0 as usize].keep_zeros = true,
825 Prop::Eventually { p, .. } => fins[p.0 as usize].keep_ones = true,
826 Prop::Until { p, q, .. } => {
827 fins[p.0 as usize].keep_zeros = true;
828 fins[q.0 as usize].keep_ones = true;
829 }
830 _ => {}
831 }
832 }
833 }
834 Some(Accel {
835 reach: if memo_ok { reach_table(ir) } else { Vec::new() },
836 fins,
837 memo_ok,
838 base: 0,
839 off: false,
840 seqs: FxMap::default(),
841 by_start: BTreeMap::new(),
842 core: Core {
843 frames: Frames::new(),
844 memo: FxMap::default(),
845 span: automaton_span(au),
846 exact,
847 stack: Vec::new(),
848 seen: FxSet::default(),
849 adds: Vec::new(),
850 hits: Vec::new(),
851 sig_buf: Vec::new(),
852 },
853 })
854 }
855
856 pub fn set_base(&mut self, base: u64) {
859 self.base = base;
860 }
861
862 pub fn evict_before(&mut self, base: u64) {
864 for fin in &mut self.fins {
865 fin.evict(base);
866 }
867 if self.by_start.is_empty() || self.by_start.keys().next().is_none_or(|&s| s >= base) {
868 return;
869 }
870 while let Some(old) = self.by_start.first_entry()
872 && *old.key() < base
873 {
874 for key in old.remove() {
875 self.seqs.remove(&key);
876 }
877 }
878 }
879
880 pub fn base(&self) -> u64 {
882 self.base
883 }
884
885 pub fn final_reach(&self, id: PropId) -> Option<u32> {
889 if self.off || !self.memo_ok {
890 return None;
891 }
892 self.reach.get(id.0 as usize).copied().flatten()
893 }
894
895 pub fn final_next(&mut self, id: PropId) -> (u64, bool) {
899 let base = self.base;
900 let fin = &mut self.fins[id.0 as usize];
901 if fin.next < base {
902 fin.next = base;
903 fin.blocked = false;
904 }
905 (fin.next, fin.blocked)
906 }
907
908 pub fn final_record(&mut self, id: PropId, at: u64, verdict: Option<bool>) {
911 let fin = &mut self.fins[id.0 as usize];
912 debug_assert_eq!(fin.next, at);
913 match verdict {
914 Some(true) => {
915 if fin.keep_ones {
916 fin.ones.insert(at);
917 }
918 }
919 Some(false) => {
920 if fin.keep_zeros {
921 fin.zeros.insert(at);
922 }
923 }
924 None => {
925 fin.blocked = true;
926 return;
927 }
928 }
929 fin.next = at + 1;
930 }
931
932 pub fn final_first(&self, id: PropId, v: bool, from: u64) -> Option<u64> {
937 let fin = &self.fins[id.0 as usize];
938 let set = if v { &fin.ones } else { &fin.zeros };
939 set.range(from..).next().copied()
940 }
941
942 fn disable(&mut self) {
943 self.off = true;
944 self.seqs.clear();
945 self.by_start.clear();
946 for fin in &mut self.fins {
947 fin.ones.clear();
948 fin.zeros.clear();
949 }
950 self.core.memo.clear();
951 self.core.frames = Frames::new();
952 }
953
954 fn prep(
956 &mut self,
957 env: &dyn PropEnv,
958 real: u32,
959 pos: u32,
960 frame: &Frame,
961 ) -> Option<(u64, u64, u32)> {
962 if self.off || pos > real || env.timeline().is_some() {
963 return None;
964 }
965 if self.core.frames.len() > MAX_FRAMES {
966 self.disable();
967 return None;
968 }
969 let fid = self.core.frames.intern(frame);
970 Some((self.base + u64::from(real), self.base + u64::from(pos), fid))
971 }
972
973 #[allow(clippy::too_many_arguments)]
976 fn state<'a>(
977 seqs: &'a mut FxMap<(u32, u64, u32, bool), Seq>,
978 by_start: &mut ByStart,
979 core: &mut Core,
980 au: &Automaton,
981 env: &dyn PropEnv,
982 base: u64,
983 key: (u32, u64, u32, bool),
984 r: u64,
985 ) -> Option<&'a mut Seq> {
986 let (entry, start, fid, weak) = key;
987 let seq = seqs.entry(key).or_insert_with(|| {
988 by_start.entry(start).or_default().push(key);
989 Seq {
990 processed: start,
991 pool: vec![Rng {
992 state: entry,
993 frame: fid,
994 lo: start,
995 hi: start,
996 }],
997 matches: Vec::new(),
998 false_at: INF,
999 done: false,
1000 poisoned: false,
1001 }
1002 });
1003 if seq.poisoned || seq.processed > r {
1004 return None;
1005 }
1006 let res = if weak {
1007 core.advance_weak(au, (env, base), seq, r)
1008 } else {
1009 core.advance(au, (env, base), seq, r)
1010 };
1011 if res.is_err() {
1012 seq.poisoned = true;
1013 return None;
1014 }
1015 Some(seq)
1016 }
1017
1018 #[allow(clippy::too_many_arguments)]
1022 pub fn matches(
1023 &mut self,
1024 au: &Automaton,
1025 env: &dyn PropEnv,
1026 entry: ThreadStateId,
1027 real: u32,
1028 top: bool,
1029 pos: u32,
1030 frame: &Frame,
1031 ) -> Option<Matches> {
1032 let (r, start, fid) = self.prep(env, real, pos, frame)?;
1033 if start < r
1034 && let Some(g) = one_letter_guard(au, entry)
1035 {
1036 let held = env.value(g, pos, frame.as_slice()).ok()? != 0;
1037 let mut one = Matches::new();
1038 if held {
1039 one.push((pos, frame.clone()));
1040 }
1041 return Some(one);
1042 }
1043 let mut all: Vec<(u64, u32)>;
1044 if start == r {
1045 let pool = [Rng {
1046 state: entry.0,
1047 frame: fid,
1048 lo: r,
1049 hi: r,
1050 }];
1051 let found = self.core.tail(au, &pool, r, 0, top, Want::All).ok()?;
1052 let Found::List(l) = found else { return None };
1053 all = l.into_iter().map(|(o, f)| (r + u64::from(o), f)).collect();
1054 } else {
1055 let key = (entry.0, start, fid, false);
1056 let seq = Self::state(
1057 &mut self.seqs,
1058 &mut self.by_start,
1059 &mut self.core,
1060 au,
1061 env,
1062 self.base,
1063 key,
1064 r,
1065 )?;
1066 let found = self.core.tail(au, &seq.pool, r, 0, top, Want::All).ok()?;
1067 let Found::List(l) = found else { return None };
1068 all = seq.matches.clone();
1069 all.extend(l.into_iter().map(|(o, f)| (r + u64::from(o), f)));
1070 }
1071 let frames = &self.core.frames;
1072 all.sort_unstable_by(|a, b| {
1073 a.0.cmp(&b.0)
1074 .then_with(|| frames.get(a.1).cmp(frames.get(b.1)))
1075 });
1076 all.dedup();
1077 Some(
1078 all.into_iter()
1079 .map(|(e, f)| ((e - self.base) as u32, frames.get(f).clone()))
1080 .collect(),
1081 )
1082 }
1083
1084 #[allow(clippy::too_many_arguments)]
1086 pub fn strong(
1087 &mut self,
1088 au: &Automaton,
1089 env: &dyn PropEnv,
1090 entry: ThreadStateId,
1091 real: u32,
1092 top: bool,
1093 pos: u32,
1094 frame: &Frame,
1095 ) -> Option<bool> {
1096 let (r, start, fid) = self.prep(env, real, pos, frame)?;
1097 if start < r
1098 && let Some(g) = one_letter_guard(au, entry)
1099 {
1100 return Some(env.value(g, pos, frame.as_slice()).ok()? != 0);
1101 }
1102 if start == r {
1103 let pool = [Rng {
1104 state: entry.0,
1105 frame: fid,
1106 lo: r,
1107 hi: r,
1108 }];
1109 return match self.core.tail(au, &pool, r, 0, top, Want::Exists).ok()? {
1110 Found::Bool(b) => Some(b),
1111 Found::List(_) => None,
1112 };
1113 }
1114 let key = (entry.0, start, fid, false);
1115 let seq = Self::state(
1116 &mut self.seqs,
1117 &mut self.by_start,
1118 &mut self.core,
1119 au,
1120 env,
1121 self.base,
1122 key,
1123 r,
1124 )?;
1125 let has = !seq.matches.is_empty();
1126 if has && !self.core.exact {
1127 return Some(true);
1128 }
1129 match self
1130 .core
1131 .tail(au, &seq.pool, r, 0, top, Want::Exists)
1132 .ok()?
1133 {
1134 Found::Bool(b) => Some(has || b),
1135 Found::List(_) => None,
1136 }
1137 }
1138
1139 #[allow(clippy::too_many_arguments)]
1142 pub fn weak(
1143 &mut self,
1144 au: &Automaton,
1145 env: &dyn PropEnv,
1146 entry: ThreadStateId,
1147 real: u32,
1148 top: bool,
1149 pos: u32,
1150 frame: &Frame,
1151 ) -> Option<bool> {
1152 let (r, start, fid) = self.prep(env, real, pos, frame)?;
1153 if start == r {
1154 let pool = [Rng {
1155 state: entry.0,
1156 frame: fid,
1157 lo: r,
1158 hi: r,
1159 }];
1160 let want = if top { Want::Exists } else { Want::ExtraCuts };
1161 return match self.core.tail(au, &pool, r, 0, true, want).ok()? {
1162 Found::Bool(b) => Some(b),
1163 Found::List(_) => None,
1164 };
1165 }
1166 let key = (entry.0, start, fid, true);
1167 let seq = Self::state(
1168 &mut self.seqs,
1169 &mut self.by_start,
1170 &mut self.core,
1171 au,
1172 env,
1173 self.base,
1174 key,
1175 r,
1176 )?;
1177 if seq.false_at <= r {
1178 return Some(false);
1179 }
1180 if top {
1181 return Some(true);
1182 }
1183 let has = !seq.matches.is_empty() || seq.done;
1184 if has && !self.core.exact {
1185 return Some(true);
1186 }
1187 let want = if has {
1189 Want::ExtraCutsAll
1190 } else {
1191 Want::ExtraCuts
1192 };
1193 match self.core.tail(au, &seq.pool, r, 0, true, want).ok()? {
1194 Found::Bool(b) => Some(has || b),
1195 Found::List(_) => None,
1196 }
1197 }
1198}