1use std::fmt;
22use std::io::{self, Write};
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
26pub struct TheoryStepId(pub u32);
27
28impl fmt::Display for TheoryStepId {
29 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
30 write!(f, "t{}", self.0)
31 }
32}
33
34#[derive(Debug, Clone, PartialEq, Eq)]
36pub struct ProofTerm(pub String);
37
38impl fmt::Display for ProofTerm {
39 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40 write!(f, "{}", self.0)
41 }
42}
43
44impl<S: Into<String>> From<S> for ProofTerm {
45 fn from(s: S) -> Self {
46 ProofTerm(s.into())
47 }
48}
49
50#[derive(Debug, Clone, PartialEq, Eq)]
52pub enum TheoryRule {
53 Refl,
56 Symm,
58 Trans,
60 Cong,
62 FuncEq,
64 Distinct,
66
67 LaGeneric,
70 LaMult,
72 LaAdd,
74 LaTighten,
76 LaDiv,
78 LaTotality,
80 LaDiseq,
82
83 BvBlastEq,
86 BvExtract,
88 BvConcat,
90 BvZeroExtend,
92 BvSignExtend,
94 BvBitwise,
96 BvArith,
98 BvCompare,
100 BvOverflow,
102
103 ArrReadWrite1,
106 ArrReadWrite2,
108 ArrExt,
110 ArrConst,
112
113 ForallElim,
116 ExistsIntro,
118 Skolemize,
120 QuantInst,
122 AlphaEquiv,
124
125 TheoryConflict,
128 TheoryProp,
130 IteElim,
132 BoolSimp,
134 Custom(String),
136}
137
138impl fmt::Display for TheoryRule {
139 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
140 match self {
141 Self::Refl => write!(f, "refl"),
142 Self::Symm => write!(f, "symm"),
143 Self::Trans => write!(f, "trans"),
144 Self::Cong => write!(f, "cong"),
145 Self::FuncEq => write!(f, "func_eq"),
146 Self::Distinct => write!(f, "distinct"),
147 Self::LaGeneric => write!(f, "la_generic"),
148 Self::LaMult => write!(f, "la_mult"),
149 Self::LaAdd => write!(f, "la_add"),
150 Self::LaTighten => write!(f, "la_tighten"),
151 Self::LaDiv => write!(f, "la_div"),
152 Self::LaTotality => write!(f, "la_totality"),
153 Self::LaDiseq => write!(f, "la_diseq"),
154 Self::BvBlastEq => write!(f, "bv_blast_eq"),
155 Self::BvExtract => write!(f, "bv_extract"),
156 Self::BvConcat => write!(f, "bv_concat"),
157 Self::BvZeroExtend => write!(f, "bv_zero_extend"),
158 Self::BvSignExtend => write!(f, "bv_sign_extend"),
159 Self::BvBitwise => write!(f, "bv_bitwise"),
160 Self::BvArith => write!(f, "bv_arith"),
161 Self::BvCompare => write!(f, "bv_compare"),
162 Self::BvOverflow => write!(f, "bv_overflow"),
163 Self::ArrReadWrite1 => write!(f, "arr_read_write_1"),
164 Self::ArrReadWrite2 => write!(f, "arr_read_write_2"),
165 Self::ArrExt => write!(f, "arr_ext"),
166 Self::ArrConst => write!(f, "arr_const"),
167 Self::ForallElim => write!(f, "forall_elim"),
168 Self::ExistsIntro => write!(f, "exists_intro"),
169 Self::Skolemize => write!(f, "skolemize"),
170 Self::QuantInst => write!(f, "quant_inst"),
171 Self::AlphaEquiv => write!(f, "alpha_equiv"),
172 Self::TheoryConflict => write!(f, "theory_conflict"),
173 Self::TheoryProp => write!(f, "theory_prop"),
174 Self::IteElim => write!(f, "ite_elim"),
175 Self::BoolSimp => write!(f, "bool_simp"),
176 Self::Custom(name) => write!(f, "{}", name),
177 }
178 }
179}
180
181#[derive(Debug, Clone)]
183pub struct TheoryStep {
184 pub id: TheoryStepId,
186 pub rule: TheoryRule,
188 pub premises: Vec<TheoryStepId>,
190 pub args: Vec<ProofTerm>,
192 pub conclusion: ProofTerm,
194}
195
196impl fmt::Display for TheoryStep {
197 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
198 write!(f, "{}: {} [", self.id, self.conclusion)?;
199 if !self.premises.is_empty() {
200 for (i, premise) in self.premises.iter().enumerate() {
201 if i > 0 {
202 write!(f, ", ")?;
203 }
204 write!(f, "{}", premise)?;
205 }
206 write!(f, " | ")?;
207 }
208 write!(f, "{}", self.rule)?;
209 for arg in &self.args {
210 write!(f, " {}", arg)?;
211 }
212 write!(f, "]")
213 }
214}
215
216#[derive(Debug, Default)]
218pub struct TheoryProof {
219 steps: Vec<TheoryStep>,
221 next_id: u32,
223}
224
225impl TheoryProof {
226 #[must_use]
228 pub fn new() -> Self {
229 Self {
230 steps: Vec::new(),
231 next_id: 0,
232 }
233 }
234
235 fn alloc_id(&mut self) -> TheoryStepId {
237 let id = TheoryStepId(self.next_id);
238 self.next_id += 1;
239 id
240 }
241
242 pub fn add_axiom(
244 &mut self,
245 rule: TheoryRule,
246 conclusion: impl Into<ProofTerm>,
247 ) -> TheoryStepId {
248 let id = self.alloc_id();
249 self.steps.push(TheoryStep {
250 id,
251 rule,
252 premises: Vec::new(),
253 args: Vec::new(),
254 conclusion: conclusion.into(),
255 });
256 id
257 }
258
259 pub fn add_step(
261 &mut self,
262 rule: TheoryRule,
263 premises: Vec<TheoryStepId>,
264 conclusion: impl Into<ProofTerm>,
265 ) -> TheoryStepId {
266 let id = self.alloc_id();
267 self.steps.push(TheoryStep {
268 id,
269 rule,
270 premises,
271 args: Vec::new(),
272 conclusion: conclusion.into(),
273 });
274 id
275 }
276
277 pub fn add_step_with_args(
279 &mut self,
280 rule: TheoryRule,
281 premises: Vec<TheoryStepId>,
282 args: Vec<ProofTerm>,
283 conclusion: impl Into<ProofTerm>,
284 ) -> TheoryStepId {
285 let id = self.alloc_id();
286 self.steps.push(TheoryStep {
287 id,
288 rule,
289 premises,
290 args,
291 conclusion: conclusion.into(),
292 });
293 id
294 }
295
296 pub fn refl(&mut self, term: impl Into<ProofTerm>) -> TheoryStepId {
300 let t = term.into();
301 let conclusion = format!("(= {} {})", t.0, t.0);
302 self.add_axiom(TheoryRule::Refl, conclusion)
303 }
304
305 pub fn symm(
307 &mut self,
308 premise: TheoryStepId,
309 t1: impl Into<ProofTerm>,
310 t2: impl Into<ProofTerm>,
311 ) -> TheoryStepId {
312 let conclusion = format!("(= {} {})", t2.into().0, t1.into().0);
313 self.add_step(TheoryRule::Symm, vec![premise], conclusion)
314 }
315
316 pub fn trans(
318 &mut self,
319 p1: TheoryStepId,
320 p2: TheoryStepId,
321 t1: impl Into<ProofTerm>,
322 t3: impl Into<ProofTerm>,
323 ) -> TheoryStepId {
324 let conclusion = format!("(= {} {})", t1.into().0, t3.into().0);
325 self.add_step(TheoryRule::Trans, vec![p1, p2], conclusion)
326 }
327
328 pub fn cong(
330 &mut self,
331 premises: Vec<TheoryStepId>,
332 func: impl Into<ProofTerm>,
333 args1: &[ProofTerm],
334 args2: &[ProofTerm],
335 ) -> TheoryStepId {
336 let f = func.into();
337 let lhs = if args1.is_empty() {
338 f.0.clone()
339 } else {
340 format!(
341 "({} {})",
342 f.0,
343 args1
344 .iter()
345 .map(|a| a.0.as_str())
346 .collect::<Vec<_>>()
347 .join(" ")
348 )
349 };
350 let rhs = if args2.is_empty() {
351 f.0.clone()
352 } else {
353 format!(
354 "({} {})",
355 f.0,
356 args2
357 .iter()
358 .map(|a| a.0.as_str())
359 .collect::<Vec<_>>()
360 .join(" ")
361 )
362 };
363 let conclusion = format!("(= {} {})", lhs, rhs);
364 self.add_step(TheoryRule::Cong, premises, conclusion)
365 }
366
367 pub fn farkas(
371 &mut self,
372 premises: Vec<TheoryStepId>,
373 coefficients: &[ProofTerm],
374 ) -> TheoryStepId {
375 let conclusion = "false".to_string();
376 self.add_step_with_args(
377 TheoryRule::LaGeneric,
378 premises,
379 coefficients.to_vec(),
380 conclusion,
381 )
382 }
383
384 pub fn la_add(
386 &mut self,
387 p1: TheoryStepId,
388 p2: TheoryStepId,
389 conclusion: impl Into<ProofTerm>,
390 ) -> TheoryStepId {
391 self.add_step(TheoryRule::LaAdd, vec![p1, p2], conclusion)
392 }
393
394 pub fn la_mult(
396 &mut self,
397 premise: TheoryStepId,
398 coefficient: impl Into<ProofTerm>,
399 conclusion: impl Into<ProofTerm>,
400 ) -> TheoryStepId {
401 self.add_step_with_args(
402 TheoryRule::LaMult,
403 vec![premise],
404 vec![coefficient.into()],
405 conclusion,
406 )
407 }
408
409 pub fn read_write_same(
413 &mut self,
414 array: impl Into<ProofTerm>,
415 index: impl Into<ProofTerm>,
416 value: impl Into<ProofTerm>,
417 ) -> TheoryStepId {
418 let a = array.into();
419 let i = index.into();
420 let v = value.into();
421 let conclusion = format!(
422 "(= (select (store {} {} {}) {}) {})",
423 a.0, i.0, v.0, i.0, v.0
424 );
425 self.add_axiom(TheoryRule::ArrReadWrite1, conclusion)
426 }
427
428 pub fn read_write_diff(
430 &mut self,
431 premise: TheoryStepId,
432 array: impl Into<ProofTerm>,
433 i: impl Into<ProofTerm>,
434 j: impl Into<ProofTerm>,
435 v: impl Into<ProofTerm>,
436 ) -> TheoryStepId {
437 let a = array.into();
438 let idx_i = i.into();
439 let idx_j = j.into();
440 let val = v.into();
441 let conclusion = format!(
442 "(= (select (store {} {} {}) {}) (select {} {}))",
443 a.0, idx_i.0, val.0, idx_j.0, a.0, idx_j.0
444 );
445 self.add_step(TheoryRule::ArrReadWrite2, vec![premise], conclusion)
446 }
447
448 pub fn bv_blast_eq(
452 &mut self,
453 premises: Vec<TheoryStepId>,
454 conclusion: impl Into<ProofTerm>,
455 ) -> TheoryStepId {
456 self.add_step(TheoryRule::BvBlastEq, premises, conclusion)
457 }
458
459 pub fn forall_elim(
463 &mut self,
464 premise: TheoryStepId,
465 variable: impl Into<ProofTerm>,
466 instantiation: impl Into<ProofTerm>,
467 conclusion: impl Into<ProofTerm>,
468 ) -> TheoryStepId {
469 self.add_step_with_args(
470 TheoryRule::ForallElim,
471 vec![premise],
472 vec![variable.into(), instantiation.into()],
473 conclusion,
474 )
475 }
476
477 pub fn exists_intro(
479 &mut self,
480 premise: TheoryStepId,
481 witness: impl Into<ProofTerm>,
482 conclusion: impl Into<ProofTerm>,
483 ) -> TheoryStepId {
484 self.add_step_with_args(
485 TheoryRule::ExistsIntro,
486 vec![premise],
487 vec![witness.into()],
488 conclusion,
489 )
490 }
491
492 pub fn skolemize(
494 &mut self,
495 premise: TheoryStepId,
496 skolem_constant: impl Into<ProofTerm>,
497 conclusion: impl Into<ProofTerm>,
498 ) -> TheoryStepId {
499 self.add_step_with_args(
500 TheoryRule::Skolemize,
501 vec![premise],
502 vec![skolem_constant.into()],
503 conclusion,
504 )
505 }
506
507 pub fn quant_inst(
509 &mut self,
510 quantified_formula: TheoryStepId,
511 pattern: impl Into<ProofTerm>,
512 instantiations: Vec<ProofTerm>,
513 conclusion: impl Into<ProofTerm>,
514 ) -> TheoryStepId {
515 let mut args = vec![pattern.into()];
516 args.extend(instantiations);
517 self.add_step_with_args(
518 TheoryRule::QuantInst,
519 vec![quantified_formula],
520 args,
521 conclusion,
522 )
523 }
524
525 #[must_use]
529 pub fn len(&self) -> usize {
530 self.steps.len()
531 }
532
533 #[must_use]
535 pub fn is_empty(&self) -> bool {
536 self.steps.is_empty()
537 }
538
539 #[must_use]
541 pub fn steps(&self) -> &[TheoryStep] {
542 &self.steps
543 }
544
545 #[must_use]
547 pub fn get_step(&self, id: TheoryStepId) -> Option<&TheoryStep> {
548 self.steps.get(id.0 as usize)
549 }
550
551 pub fn clear(&mut self) {
553 self.steps.clear();
554 self.next_id = 0;
555 }
556
557 pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
559 writeln!(writer, "; Theory proof generated by OxiZ")?;
560 writeln!(writer)?;
561
562 for step in &self.steps {
563 writeln!(writer, "{}", step)?;
564 }
565
566 Ok(())
567 }
568
569 #[must_use]
571 pub fn to_string_repr(&self) -> String {
572 let mut buf = Vec::new();
573 self.write(&mut buf)
574 .expect("writing to Vec should not fail");
575 String::from_utf8(buf).expect("output is UTF-8")
576 }
577}
578
579#[derive(Debug, Default)]
581pub struct EufProofRecorder {
582 proof: TheoryProof,
584 equality_steps: rustc_hash::FxHashMap<(u32, u32), TheoryStepId>,
586}
587
588impl EufProofRecorder {
589 #[must_use]
591 pub fn new() -> Self {
592 Self {
593 proof: TheoryProof::new(),
594 equality_steps: rustc_hash::FxHashMap::default(),
595 }
596 }
597
598 pub fn record_equality(&mut self, a: u32, b: u32, term: impl Into<ProofTerm>) -> TheoryStepId {
600 let id = self
601 .proof
602 .add_axiom(TheoryRule::Custom("assert".to_string()), term);
603 self.equality_steps.insert((a.min(b), a.max(b)), id);
604 id
605 }
606
607 pub fn record_congruence(
609 &mut self,
610 func: impl Into<ProofTerm>,
611 arg_equalities: Vec<TheoryStepId>,
612 result: impl Into<ProofTerm>,
613 ) -> TheoryStepId {
614 self.proof
615 .add_step_with_args(TheoryRule::Cong, arg_equalities, vec![func.into()], result)
616 }
617
618 pub fn record_transitivity(
620 &mut self,
621 steps: Vec<TheoryStepId>,
622 conclusion: impl Into<ProofTerm>,
623 ) -> TheoryStepId {
624 self.proof.add_step(TheoryRule::Trans, steps, conclusion)
625 }
626
627 #[must_use]
629 pub fn proof(&self) -> &TheoryProof {
630 &self.proof
631 }
632
633 pub fn take_proof(&mut self) -> TheoryProof {
635 std::mem::take(&mut self.proof)
636 }
637}
638
639#[derive(Debug, Default)]
641pub struct ArithProofRecorder {
642 proof: TheoryProof,
644 constraint_steps: Vec<TheoryStepId>,
646}
647
648impl ArithProofRecorder {
649 #[must_use]
651 pub fn new() -> Self {
652 Self {
653 proof: TheoryProof::new(),
654 constraint_steps: Vec::new(),
655 }
656 }
657
658 pub fn record_bound(&mut self, constraint: impl Into<ProofTerm>) -> TheoryStepId {
660 let id = self
661 .proof
662 .add_axiom(TheoryRule::Custom("bound".to_string()), constraint);
663 self.constraint_steps.push(id);
664 id
665 }
666
667 pub fn record_farkas_conflict(&mut self, reasons: &[u32]) -> TheoryStepId {
669 let premises: Vec<TheoryStepId> = reasons
670 .iter()
671 .filter_map(|&r| self.constraint_steps.get(r as usize).copied())
672 .collect();
673 self.proof
674 .add_step(TheoryRule::LaGeneric, premises, "false")
675 }
676
677 #[must_use]
679 pub fn proof(&self) -> &TheoryProof {
680 &self.proof
681 }
682
683 pub fn take_proof(&mut self) -> TheoryProof {
685 std::mem::take(&mut self.proof)
686 }
687}
688
689#[derive(Debug, Default)]
691pub struct ArrayProofRecorder {
692 proof: TheoryProof,
694}
695
696impl ArrayProofRecorder {
697 #[must_use]
699 pub fn new() -> Self {
700 Self {
701 proof: TheoryProof::new(),
702 }
703 }
704
705 pub fn record_select_store_same(
707 &mut self,
708 array: impl Into<ProofTerm>,
709 index: impl Into<ProofTerm>,
710 value: impl Into<ProofTerm>,
711 ) -> TheoryStepId {
712 self.proof.read_write_same(array, index, value)
713 }
714
715 pub fn record_select_store_diff(
717 &mut self,
718 neq_proof: TheoryStepId,
719 array: impl Into<ProofTerm>,
720 i: impl Into<ProofTerm>,
721 j: impl Into<ProofTerm>,
722 v: impl Into<ProofTerm>,
723 ) -> TheoryStepId {
724 self.proof.read_write_diff(neq_proof, array, i, j, v)
725 }
726
727 #[must_use]
729 pub fn proof(&self) -> &TheoryProof {
730 &self.proof
731 }
732
733 pub fn take_proof(&mut self) -> TheoryProof {
735 std::mem::take(&mut self.proof)
736 }
737}
738
739pub trait TheoryProofProducer {
741 fn enable_proof(&mut self);
743
744 fn disable_proof(&mut self);
746
747 fn proof_enabled(&self) -> bool;
749
750 fn get_theory_proof(&self) -> Option<&TheoryProof>;
752
753 fn take_theory_proof(&mut self) -> Option<TheoryProof>;
755}
756
757#[cfg(test)]
758mod tests {
759 use super::*;
760
761 #[test]
762 fn test_theory_step_id_display() {
763 let id = TheoryStepId(42);
764 assert_eq!(format!("{}", id), "t42");
765 }
766
767 #[test]
768 fn test_theory_rule_display() {
769 assert_eq!(format!("{}", TheoryRule::Refl), "refl");
770 assert_eq!(format!("{}", TheoryRule::Trans), "trans");
771 assert_eq!(format!("{}", TheoryRule::LaGeneric), "la_generic");
772 assert_eq!(format!("{}", TheoryRule::ArrReadWrite1), "arr_read_write_1");
773 }
774
775 #[test]
776 fn test_theory_proof_reflexivity() {
777 let mut proof = TheoryProof::new();
778
779 let step = proof.refl("x");
780 assert_eq!(step.0, 0);
781
782 let s = proof.get_step(step).expect("test operation should succeed");
783 assert_eq!(s.conclusion.0, "(= x x)");
784 assert_eq!(s.rule, TheoryRule::Refl);
785 assert!(s.premises.is_empty());
786 }
787
788 #[test]
789 fn test_theory_proof_transitivity() {
790 let mut proof = TheoryProof::new();
791
792 let s1 = proof.add_axiom(TheoryRule::Custom("assert".into()), "(= a b)");
794
795 let s2 = proof.add_axiom(TheoryRule::Custom("assert".into()), "(= b c)");
797
798 let s3 = proof.trans(s1, s2, "a", "c");
800
801 let step = proof.get_step(s3).expect("test operation should succeed");
802 assert_eq!(step.conclusion.0, "(= a c)");
803 assert_eq!(step.rule, TheoryRule::Trans);
804 assert_eq!(step.premises.len(), 2);
805 }
806
807 #[test]
808 fn test_theory_proof_farkas() {
809 let mut proof = TheoryProof::new();
810
811 let s1 = proof.add_axiom(TheoryRule::Custom("bound".into()), "(>= x 10)");
813
814 let s2 = proof.add_axiom(TheoryRule::Custom("bound".into()), "(<= x 5)");
816
817 let s3 = proof.farkas(
819 vec![s1, s2],
820 &[ProofTerm("1".into()), ProofTerm("1".into())],
821 );
822
823 let step = proof.get_step(s3).expect("test operation should succeed");
824 assert_eq!(step.conclusion.0, "false");
825 assert_eq!(step.rule, TheoryRule::LaGeneric);
826 }
827
828 #[test]
829 fn test_theory_proof_array_read_write() {
830 let mut proof = TheoryProof::new();
831
832 let step = proof.read_write_same("a", "i", "v");
833
834 let s = proof.get_step(step).expect("test operation should succeed");
835 assert!(s.conclusion.0.contains("select"));
836 assert!(s.conclusion.0.contains("store"));
837 assert_eq!(s.rule, TheoryRule::ArrReadWrite1);
838 }
839
840 #[test]
841 fn test_euf_proof_recorder() {
842 let mut recorder = EufProofRecorder::new();
843
844 let s1 = recorder.record_equality(0, 1, "(= a b)");
845 let s2 = recorder.record_equality(1, 2, "(= b c)");
846 let _s3 = recorder.record_transitivity(vec![s1, s2], "(= a c)");
847
848 assert_eq!(recorder.proof().len(), 3);
849 }
850
851 #[test]
852 fn test_arith_proof_recorder() {
853 let mut recorder = ArithProofRecorder::new();
854
855 recorder.record_bound("(>= x 10)");
856 recorder.record_bound("(<= x 5)");
857 let conflict = recorder.record_farkas_conflict(&[0, 1]);
858
859 let step = recorder
860 .proof()
861 .get_step(conflict)
862 .expect("test operation should succeed");
863 assert_eq!(step.conclusion.0, "false");
864 }
865
866 #[test]
867 fn test_theory_proof_clear() {
868 let mut proof = TheoryProof::new();
869 proof.refl("x");
870 proof.refl("y");
871
872 assert_eq!(proof.len(), 2);
873 proof.clear();
874 assert!(proof.is_empty());
875 }
876
877 #[test]
878 fn test_theory_proof_write() {
879 let mut proof = TheoryProof::new();
880 proof.refl("x");
881
882 let output = proof.to_string_repr();
883 assert!(output.contains("Theory proof"));
884 assert!(output.contains("(= x x)"));
885 }
886
887 #[test]
888 fn test_quantifier_forall_elim() {
889 let mut proof = TheoryProof::new();
890
891 let forall_step = proof.add_axiom(TheoryRule::Custom("axiom".into()), "(forall x (P x))");
893
894 let inst_step = proof.forall_elim(forall_step, "x", "5", "(P 5)");
896
897 let step = proof
898 .get_step(inst_step)
899 .expect("test operation should succeed");
900 assert_eq!(step.rule, TheoryRule::ForallElim);
901 assert_eq!(step.premises.len(), 1);
902 assert_eq!(step.args.len(), 2);
903 }
904
905 #[test]
906 fn test_quantifier_exists_intro() {
907 let mut proof = TheoryProof::new();
908
909 let concrete = proof.add_axiom(TheoryRule::Custom("axiom".into()), "(P 5)");
911
912 let exists_step = proof.exists_intro(concrete, "5", "(exists x (P x))");
914
915 let step = proof
916 .get_step(exists_step)
917 .expect("test operation should succeed");
918 assert_eq!(step.rule, TheoryRule::ExistsIntro);
919 assert_eq!(step.args.len(), 1);
920 }
921
922 #[test]
923 fn test_quantifier_skolemization() {
924 let mut proof = TheoryProof::new();
925
926 let exists_step = proof.add_axiom(TheoryRule::Custom("axiom".into()), "(exists x (P x))");
928
929 let skolem_step = proof.skolemize(exists_step, "sk_1", "(P sk_1)");
931
932 let step = proof
933 .get_step(skolem_step)
934 .expect("test operation should succeed");
935 assert_eq!(step.rule, TheoryRule::Skolemize);
936 assert!(step.conclusion.0.contains("sk_1"));
937 }
938
939 #[test]
940 fn test_quantifier_inst_pattern() {
941 let mut proof = TheoryProof::new();
942
943 let forall_step = proof.add_axiom(
945 TheoryRule::Custom("axiom".into()),
946 "(forall x (=> (P x) (Q x)))",
947 );
948
949 let inst_step = proof.quant_inst(
951 forall_step,
952 "(P x)",
953 vec![ProofTerm("a".into())],
954 "(=> (P a) (Q a))",
955 );
956
957 let step = proof
958 .get_step(inst_step)
959 .expect("test operation should succeed");
960 assert_eq!(step.rule, TheoryRule::QuantInst);
961 assert!(!step.args.is_empty());
962 }
963
964 #[test]
965 fn test_quantifier_rule_display() {
966 assert_eq!(format!("{}", TheoryRule::ForallElim), "forall_elim");
967 assert_eq!(format!("{}", TheoryRule::ExistsIntro), "exists_intro");
968 assert_eq!(format!("{}", TheoryRule::Skolemize), "skolemize");
969 assert_eq!(format!("{}", TheoryRule::QuantInst), "quant_inst");
970 }
971}