1use crate::chc::PredId;
13use oxiz_core::TermId;
14use rustc_hash::FxHashSet;
15use smallvec::SmallVec;
16use std::cmp::Ordering;
17use std::collections::BinaryHeap;
18
19pub const INFTY_LEVEL: u32 = u32::MAX;
21
22#[inline]
24pub fn is_infty_level(level: u32) -> bool {
25 level == INFTY_LEVEL
26}
27
28#[derive(Debug, Clone)]
30pub struct Lemma {
31 pub id: LemmaId,
33 pub formula: TermId,
35 level: u32,
37 init_level: u32,
39 bumped: u16,
41 weakness: u16,
43 external: bool,
45 blocked: bool,
47 background: bool,
49}
50
51impl Lemma {
52 pub fn new(id: LemmaId, formula: TermId, level: u32) -> Self {
54 Self {
55 id,
56 formula,
57 level,
58 init_level: level,
59 bumped: 0,
60 weakness: 0,
61 external: false,
62 blocked: false,
63 background: false,
64 }
65 }
66
67 #[inline]
69 #[must_use]
70 pub fn is_inductive(&self) -> bool {
71 is_infty_level(self.level)
72 }
73
74 #[inline]
76 #[must_use]
77 pub fn level(&self) -> u32 {
78 self.level
79 }
80
81 #[inline]
83 #[must_use]
84 pub fn init_level(&self) -> u32 {
85 self.init_level
86 }
87
88 #[inline]
90 pub fn set_level(&mut self, level: u32) {
91 self.level = level;
92 }
93
94 #[inline]
96 pub fn bump(&mut self) {
97 self.bumped = self.bumped.saturating_add(1);
98 }
99
100 #[inline]
102 #[must_use]
103 pub fn bumped(&self) -> u16 {
104 self.bumped
105 }
106
107 #[inline]
109 #[must_use]
110 pub fn weakness(&self) -> u16 {
111 self.weakness
112 }
113
114 #[inline]
116 pub fn set_weakness(&mut self, weakness: u16) {
117 self.weakness = weakness;
118 }
119
120 #[inline]
122 #[must_use]
123 pub fn is_external(&self) -> bool {
124 self.external
125 }
126
127 #[inline]
129 pub fn set_external(&mut self, external: bool) {
130 self.external = external;
131 }
132
133 #[inline]
135 #[must_use]
136 pub fn is_blocked(&self) -> bool {
137 self.blocked
138 }
139
140 #[inline]
142 pub fn set_blocked(&mut self, blocked: bool) {
143 self.blocked = blocked;
144 }
145
146 #[inline]
148 #[must_use]
149 pub fn is_background(&self) -> bool {
150 self.background
151 }
152
153 #[inline]
155 pub fn set_background(&mut self, background: bool) {
156 self.background = background;
157 }
158}
159
160#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
162pub struct LemmaId(pub u32);
163
164impl LemmaId {
165 #[must_use]
167 pub const fn new(id: u32) -> Self {
168 Self(id)
169 }
170
171 #[must_use]
173 pub const fn raw(self) -> u32 {
174 self.0
175 }
176}
177
178impl PartialEq for Lemma {
180 fn eq(&self, other: &Self) -> bool {
181 self.id == other.id
182 }
183}
184
185impl Eq for Lemma {}
186
187impl PartialOrd for Lemma {
188 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
189 Some(self.cmp(other))
190 }
191}
192
193impl Ord for Lemma {
194 fn cmp(&self, other: &Self) -> Ordering {
195 self.level
196 .cmp(&other.level)
197 .then_with(|| self.id.cmp(&other.id))
198 }
199}
200
201#[derive(Debug)]
203pub struct PredicateFrames {
204 pub pred: PredId,
206 lemmas: Vec<Lemma>,
208 active: FxHashSet<LemmaId>,
210 bg_invariants: SmallVec<[LemmaId; 4]>,
212 num_frames: u32,
214 next_lemma_id: u32,
216 sorted: bool,
218}
219
220impl PredicateFrames {
221 pub fn new(pred: PredId) -> Self {
223 Self {
224 pred,
225 lemmas: Vec::new(),
226 active: FxHashSet::default(),
227 bg_invariants: SmallVec::new(),
228 num_frames: 1, next_lemma_id: 0,
230 sorted: true,
231 }
232 }
233
234 #[inline]
236 #[must_use]
237 pub fn num_frames(&self) -> u32 {
238 self.num_frames
239 }
240
241 pub fn add_frame(&mut self) {
243 self.num_frames += 1;
244 }
245
246 pub fn add_lemma(&mut self, formula: TermId, level: u32) -> LemmaId {
248 let id = LemmaId(self.next_lemma_id);
249 self.next_lemma_id += 1;
250
251 let lemma = Lemma::new(id, formula, level);
252 self.lemmas.push(lemma);
253 self.active.insert(id);
254 self.sorted = false;
255
256 id
257 }
258
259 pub fn add_background(&mut self, formula: TermId) -> LemmaId {
261 let id = self.add_lemma(formula, INFTY_LEVEL);
262 if let Some(lemma) = self.get_lemma_mut(id) {
263 lemma.set_background(true);
264 }
265 self.bg_invariants.push(id);
266 id
267 }
268
269 #[must_use]
271 pub fn get_lemma(&self, id: LemmaId) -> Option<&Lemma> {
272 self.lemmas.get(id.0 as usize)
273 }
274
275 pub fn get_lemma_mut(&mut self, id: LemmaId) -> Option<&mut Lemma> {
277 self.lemmas.get_mut(id.0 as usize)
278 }
279
280 #[inline]
282 pub fn active_lemmas(&self) -> impl Iterator<Item = &Lemma> {
283 self.active.iter().filter_map(|&id| self.get_lemma(id))
284 }
285
286 pub fn lemmas_at_level(&self, level: u32) -> impl Iterator<Item = &Lemma> {
288 self.active_lemmas().filter(move |l| l.level() == level)
289 }
290
291 pub fn lemmas_geq_level(&self, level: u32) -> impl Iterator<Item = &Lemma> {
293 self.active_lemmas().filter(move |l| l.level() >= level)
294 }
295
296 #[inline]
298 pub fn inductive_lemmas(&self) -> impl Iterator<Item = &Lemma> {
299 self.active_lemmas().filter(|l| l.is_inductive())
300 }
301
302 pub fn background_invariants(&self) -> impl Iterator<Item = &Lemma> {
304 self.bg_invariants
305 .iter()
306 .filter_map(|&id| self.get_lemma(id))
307 }
308
309 pub fn propagate(&mut self, id: LemmaId, new_level: u32) -> bool {
311 if let Some(lemma) = self.get_lemma_mut(id)
312 && new_level > lemma.level()
313 {
314 lemma.set_level(new_level);
315 self.sorted = false;
316 return true;
317 }
318 false
319 }
320
321 pub fn propagate_to_infinity(&mut self, from_level: u32) {
323 for lemma in &mut self.lemmas {
324 if self.active.contains(&lemma.id) && lemma.level() >= from_level {
325 lemma.set_level(INFTY_LEVEL);
326 }
327 }
328 self.sorted = false;
329 }
330
331 pub fn propagate_level(&mut self, level: u32) -> bool {
334 let mut all_propagated = true;
335 let lemma_ids: Vec<_> = self.lemmas_at_level(level).map(|l| l.id).collect();
336
337 for id in lemma_ids {
338 if !self.propagate(id, level + 1) {
339 all_propagated = false;
340 }
341 }
342
343 all_propagated
344 }
345
346 pub fn deactivate(&mut self, id: LemmaId) {
348 self.active.remove(&id);
349 }
350
351 #[must_use]
353 pub fn is_active(&self, id: LemmaId) -> bool {
354 self.active.contains(&id)
355 }
356
357 #[must_use]
359 pub fn num_active_lemmas(&self) -> usize {
360 self.active.len()
361 }
362
363 #[must_use]
365 pub fn num_inductive(&self) -> usize {
366 self.inductive_lemmas().count()
367 }
368
369 pub fn remove_subsumed_syntactic(&mut self) -> usize {
372 let mut to_deactivate = Vec::new();
373
374 let active_lemmas: Vec<_> = self
376 .active
377 .iter()
378 .filter_map(|&id| self.get_lemma(id).map(|l| (id, l.formula)))
379 .collect();
380
381 for i in 0..active_lemmas.len() {
383 for j in (i + 1)..active_lemmas.len() {
384 if active_lemmas[i].1 == active_lemmas[j].1 {
385 let lemma_i = self.get_lemma(active_lemmas[i].0);
387 let lemma_j = self.get_lemma(active_lemmas[j].0);
388
389 if let (Some(li), Some(lj)) = (lemma_i, lemma_j) {
390 if li.level() > lj.level() {
391 to_deactivate.push(active_lemmas[i].0);
392 } else {
393 to_deactivate.push(active_lemmas[j].0);
394 }
395 }
396 }
397 }
398 }
399
400 to_deactivate.sort_unstable_by_key(|id| id.0);
402 to_deactivate.dedup();
403
404 let count = to_deactivate.len();
406 for id in to_deactivate {
407 self.deactivate(id);
408 }
409
410 count
411 }
412
413 pub fn sort_lemmas(&mut self) {
415 if !self.sorted {
416 self.sorted = true;
419 }
420 }
421
422 pub fn clear(&mut self) {
424 self.lemmas.clear();
425 self.active.clear();
426 self.bg_invariants.clear();
427 self.num_frames = 1;
428 self.next_lemma_id = 0;
429 self.sorted = true;
430 }
431
432 pub fn compress(&mut self, keep_above_level: u32) -> usize {
436 let mut removed = 0;
437
438 let to_remove: Vec<_> = self
440 .active
441 .iter()
442 .filter_map(|&id| {
443 self.get_lemma(id).and_then(|l| {
444 if l.level() < keep_above_level && !l.is_inductive() {
445 Some(id)
446 } else {
447 None
448 }
449 })
450 })
451 .collect();
452
453 for id in to_remove {
455 self.deactivate(id);
456 removed += 1;
457 }
458
459 removed
460 }
461
462 #[must_use]
464 pub fn memory_stats(&self) -> (usize, usize, usize) {
465 (
466 self.lemmas.len(), self.active.len(), self.bg_invariants.len(), )
470 }
471}
472
473#[derive(Debug)]
475pub struct FrameManager {
476 frames: rustc_hash::FxHashMap<PredId, PredicateFrames>,
478 current_level: u32,
480}
481
482impl Default for FrameManager {
483 fn default() -> Self {
484 Self::new()
485 }
486}
487
488impl FrameManager {
489 pub fn new() -> Self {
491 Self {
492 frames: rustc_hash::FxHashMap::default(),
493 current_level: 0,
494 }
495 }
496
497 pub fn get_or_create(&mut self, pred: PredId) -> &mut PredicateFrames {
499 self.frames
500 .entry(pred)
501 .or_insert_with(|| PredicateFrames::new(pred))
502 }
503
504 #[must_use]
506 pub fn get(&self, pred: PredId) -> Option<&PredicateFrames> {
507 self.frames.get(&pred)
508 }
509
510 pub fn get_mut(&mut self, pred: PredId) -> Option<&mut PredicateFrames> {
512 self.frames.get_mut(&pred)
513 }
514
515 #[inline]
517 #[must_use]
518 pub fn current_level(&self) -> u32 {
519 self.current_level
520 }
521
522 pub fn next_level(&mut self) {
524 self.current_level += 1;
525 for frames in self.frames.values_mut() {
526 frames.add_frame();
527 }
528 }
529
530 pub fn add_lemma(&mut self, pred: PredId, formula: TermId, level: u32) -> LemmaId {
532 self.get_or_create(pred).add_lemma(formula, level)
533 }
534
535 #[must_use]
537 pub fn is_fixpoint(&self) -> bool {
538 let level = self.current_level;
539 for frames in self.frames.values() {
540 if frames.lemmas_at_level(level).any(|l| !l.is_inductive()) {
542 return false;
543 }
544 }
545 true
546 }
547
548 pub fn propagate(&mut self) -> bool {
551 for level in 1..=self.current_level {
553 let mut all_propagated = true;
554
555 for frames in self.frames.values_mut() {
556 if !frames.propagate_level(level) {
557 all_propagated = false;
558 }
559 }
560
561 if all_propagated && level == self.current_level {
563 for frames in self.frames.values_mut() {
565 frames.propagate_to_infinity(level);
566 }
567 return true;
568 }
569 }
570
571 false
572 }
573
574 pub fn compress(&mut self, keep_above_level: u32) -> usize {
577 let mut total_removed = 0;
578 for frames in self.frames.values_mut() {
579 total_removed += frames.compress(keep_above_level);
580 }
581 total_removed
582 }
583
584 #[must_use]
586 pub fn total_memory_stats(&self) -> (usize, usize, usize) {
587 let mut total_lemmas = 0;
588 let mut total_active = 0;
589 let mut total_bg = 0;
590
591 for frames in self.frames.values() {
592 let (lemmas, active, bg) = frames.memory_stats();
593 total_lemmas += lemmas;
594 total_active += active;
595 total_bg += bg;
596 }
597
598 (total_lemmas, total_active, total_bg)
599 }
600
601 pub fn reset(&mut self) {
603 for frames in self.frames.values_mut() {
604 frames.clear();
605 }
606 self.current_level = 0;
607 }
608
609 pub fn stats(&self) -> FrameStats {
611 let mut total_lemmas = 0;
612 let mut total_inductive = 0;
613 let mut max_level = 0;
614
615 for frames in self.frames.values() {
616 total_lemmas += frames.num_active_lemmas();
617 total_inductive += frames.num_inductive();
618 max_level = max_level.max(frames.num_frames());
619 }
620
621 FrameStats {
622 total_lemmas,
623 total_inductive,
624 num_predicates: self.frames.len(),
625 max_level,
626 current_level: self.current_level,
627 }
628 }
629}
630
631#[derive(Debug, Clone)]
633pub struct FrameStats {
634 pub total_lemmas: usize,
636 pub total_inductive: usize,
638 pub num_predicates: usize,
640 pub max_level: u32,
642 pub current_level: u32,
644}
645
646#[derive(Debug)]
648pub struct LemmaQueue {
649 heap: BinaryHeap<std::cmp::Reverse<(u32, LemmaId, PredId)>>,
650}
651
652impl Default for LemmaQueue {
653 fn default() -> Self {
654 Self::new()
655 }
656}
657
658impl LemmaQueue {
659 pub fn new() -> Self {
661 Self {
662 heap: BinaryHeap::new(),
663 }
664 }
665
666 pub fn push(&mut self, level: u32, lemma: LemmaId, pred: PredId) {
668 self.heap.push(std::cmp::Reverse((level, lemma, pred)));
669 }
670
671 pub fn pop(&mut self) -> Option<(u32, LemmaId, PredId)> {
673 self.heap.pop().map(|r| r.0)
674 }
675
676 #[must_use]
678 pub fn is_empty(&self) -> bool {
679 self.heap.is_empty()
680 }
681
682 #[must_use]
684 pub fn len(&self) -> usize {
685 self.heap.len()
686 }
687
688 pub fn clear(&mut self) {
690 self.heap.clear();
691 }
692}
693
694#[cfg(test)]
695mod tests {
696 use super::*;
697
698 #[test]
699 fn test_lemma_creation() {
700 let id = LemmaId::new(0);
701 let formula = oxiz_core::TermId::new(42);
702 let lemma = Lemma::new(id, formula, 1);
703
704 assert_eq!(lemma.level(), 1);
705 assert_eq!(lemma.init_level(), 1);
706 assert!(!lemma.is_inductive());
707 }
708
709 #[test]
710 fn test_inductive_lemma() {
711 let id = LemmaId::new(0);
712 let formula = oxiz_core::TermId::new(42);
713 let mut lemma = Lemma::new(id, formula, 1);
714
715 lemma.set_level(INFTY_LEVEL);
716 assert!(lemma.is_inductive());
717 }
718
719 #[test]
720 fn test_predicate_frames() {
721 let pred = PredId::new(0);
722 let mut frames = PredicateFrames::new(pred);
723
724 let f1 = oxiz_core::TermId::new(1);
725 let f2 = oxiz_core::TermId::new(2);
726
727 let id1 = frames.add_lemma(f1, 1);
728 let id2 = frames.add_lemma(f2, 2);
729
730 assert_eq!(frames.num_active_lemmas(), 2);
731 assert_eq!(frames.lemmas_at_level(1).count(), 1);
732 assert_eq!(frames.lemmas_at_level(2).count(), 1);
733
734 assert!(frames.propagate(id1, 2));
736 assert_eq!(frames.lemmas_at_level(2).count(), 2);
737 assert_eq!(frames.lemmas_at_level(1).count(), 0);
738
739 frames.deactivate(id2);
741 assert!(!frames.is_active(id2));
742 assert_eq!(frames.num_active_lemmas(), 1);
743 }
744
745 #[test]
746 fn test_frame_manager() {
747 let mut manager = FrameManager::new();
748 let pred = PredId::new(0);
749
750 let f1 = oxiz_core::TermId::new(1);
751 manager.add_lemma(pred, f1, 0);
752
753 assert_eq!(manager.current_level(), 0);
754
755 manager.next_level();
756 assert_eq!(manager.current_level(), 1);
757
758 let stats = manager.stats();
759 assert_eq!(stats.total_lemmas, 1);
760 assert_eq!(stats.num_predicates, 1);
761 }
762
763 #[test]
764 fn test_lemma_queue() {
765 let mut queue = LemmaQueue::new();
766
767 queue.push(3, LemmaId::new(0), PredId::new(0));
768 queue.push(1, LemmaId::new(1), PredId::new(0));
769 queue.push(2, LemmaId::new(2), PredId::new(0));
770
771 let (level, _, _) = queue.pop().expect("collection should not be empty");
773 assert_eq!(level, 1);
774
775 let (level, _, _) = queue.pop().expect("collection should not be empty");
776 assert_eq!(level, 2);
777
778 let (level, _, _) = queue.pop().expect("collection should not be empty");
779 assert_eq!(level, 3);
780
781 assert!(queue.is_empty());
782 }
783
784 #[test]
785 fn test_propagate_to_infinity() {
786 let pred = PredId::new(0);
787 let mut frames = PredicateFrames::new(pred);
788
789 let f1 = oxiz_core::TermId::new(1);
790 let f2 = oxiz_core::TermId::new(2);
791 let f3 = oxiz_core::TermId::new(3);
792
793 frames.add_lemma(f1, 1);
794 frames.add_lemma(f2, 2);
795 frames.add_lemma(f3, 3);
796
797 frames.propagate_to_infinity(2);
799
800 assert_eq!(frames.lemmas_at_level(1).count(), 1);
801 assert_eq!(frames.inductive_lemmas().count(), 2);
802 }
803}