1use logicaffeine_kernel::{Context, KernelError, KernelResult, Term};
13
14use crate::{DerivationTree, InferenceRule, ProofExpr, ProofTerm};
15
16struct InductionState {
23 fix_name: String,
25 step_var: String,
27 ih_conclusion: ProofExpr,
29}
30
31pub struct CertificationContext<'a> {
63 kernel_ctx: &'a Context,
64 locals: Vec<String>,
66 induction_state: Option<InductionState>,
68}
69
70impl<'a> CertificationContext<'a> {
71 pub fn new(kernel_ctx: &'a Context) -> Self {
72 Self {
73 kernel_ctx,
74 locals: Vec::new(),
75 induction_state: None,
76 }
77 }
78
79 fn with_local(&self, name: &str) -> Self {
81 let mut new_locals = self.locals.clone();
82 new_locals.push(name.to_string());
83 Self {
84 kernel_ctx: self.kernel_ctx,
85 locals: new_locals,
86 induction_state: self.induction_state.clone(),
87 }
88 }
89
90 fn with_induction(&self, fix_name: &str, step_var: &str, ih: ProofExpr) -> Self {
92 Self {
93 kernel_ctx: self.kernel_ctx,
94 locals: self.locals.clone(),
95 induction_state: Some(InductionState {
96 fix_name: fix_name.to_string(),
97 step_var: step_var.to_string(),
98 ih_conclusion: ih,
99 }),
100 }
101 }
102
103 fn is_local(&self, name: &str) -> bool {
105 self.locals.iter().any(|n| n == name)
106 }
107
108 fn get_ih_term(&self, conclusion: &ProofExpr) -> Option<Term> {
111 if let Some(state) = &self.induction_state {
112 if conclusions_match(conclusion, &state.ih_conclusion) {
113 return Some(Term::App(
115 Box::new(Term::Var(state.fix_name.clone())),
116 Box::new(Term::Var(state.step_var.clone())),
117 ));
118 }
119 }
120 None
121 }
122}
123
124impl Clone for InductionState {
125 fn clone(&self) -> Self {
126 Self {
127 fix_name: self.fix_name.clone(),
128 step_var: self.step_var.clone(),
129 ih_conclusion: self.ih_conclusion.clone(),
130 }
131 }
132}
133
134fn conclusions_match(a: &ProofExpr, b: &ProofExpr) -> bool {
137 a == b
138}
139
140pub fn certify(tree: &DerivationTree, ctx: &CertificationContext) -> KernelResult<Term> {
180 match &tree.rule {
181 InferenceRule::Axiom | InferenceRule::PremiseMatch => {
183 certify_hypothesis(&tree.conclusion, ctx)
184 }
185
186 InferenceRule::ModusPonens => {
189 if tree.premises.len() != 2 {
190 return Err(KernelError::CertificationError(
191 "ModusPonens requires exactly 2 premises".to_string(),
192 ));
193 }
194 let impl_term = certify(&tree.premises[0], ctx)?;
195 let arg_term = certify(&tree.premises[1], ctx)?;
196 Ok(Term::App(Box::new(impl_term), Box::new(arg_term)))
197 }
198
199 InferenceRule::ConjunctionIntro => {
202 if tree.premises.len() != 2 {
203 return Err(KernelError::CertificationError(
204 "ConjunctionIntro requires exactly 2 premises".to_string(),
205 ));
206 }
207 let (p_type, q_type) = extract_and_types(&tree.conclusion)?;
208 let p_term = certify(&tree.premises[0], ctx)?;
209 let q_term = certify(&tree.premises[1], ctx)?;
210
211 let conj = Term::Global("conj".to_string());
213 let applied = Term::App(
214 Box::new(Term::App(
215 Box::new(Term::App(
216 Box::new(Term::App(Box::new(conj), Box::new(p_type))),
217 Box::new(q_type),
218 )),
219 Box::new(p_term),
220 )),
221 Box::new(q_term),
222 );
223 Ok(applied)
224 }
225
226 InferenceRule::UniversalInst(witness) => {
229 if tree.premises.len() != 1 {
230 return Err(KernelError::CertificationError(
231 "UniversalInst requires exactly 1 premise".to_string(),
232 ));
233 }
234 let forall_proof = certify(&tree.premises[0], ctx)?;
235 let witness_term = if ctx.is_local(witness) {
237 Term::Var(witness.clone())
238 } else {
239 Term::Global(witness.clone())
240 };
241 Ok(Term::App(Box::new(forall_proof), Box::new(witness_term)))
242 }
243
244 InferenceRule::UniversalIntro { variable, var_type } => {
247 if tree.premises.len() != 1 {
248 return Err(KernelError::CertificationError(
249 "UniversalIntro requires exactly 1 premise".to_string(),
250 ));
251 }
252
253 let type_term = Term::Global(var_type.clone());
255
256 let extended_ctx = ctx.with_local(variable);
258 let body_term = certify(&tree.premises[0], &extended_ctx)?;
259
260 Ok(Term::Lambda {
262 param: variable.clone(),
263 param_type: Box::new(type_term),
264 body: Box::new(body_term),
265 })
266 }
267
268 InferenceRule::StructuralInduction {
271 variable: var_name,
272 ind_type,
273 step_var,
274 } => {
275 if tree.premises.len() != 2 {
276 return Err(KernelError::CertificationError(
277 "StructuralInduction requires exactly 2 premises (base, step)".to_string(),
278 ));
279 }
280
281 let motive_body = extract_motive_body(&tree.conclusion, var_name)?;
283
284 let fix_name = format!("rec_{}", var_name);
286
287 let base_term = certify(&tree.premises[0], ctx)?;
290
291 let ih_conclusion = compute_ih_conclusion(&tree.conclusion, var_name, step_var)?;
294
295 let step_ctx = ctx
299 .with_local(step_var)
300 .with_induction(&fix_name, step_var, ih_conclusion);
301
302 let step_body = certify(&tree.premises[1], &step_ctx)?;
303
304 let step_term = Term::Lambda {
306 param: step_var.clone(),
307 param_type: Box::new(Term::Global(ind_type.clone())),
308 body: Box::new(step_body),
309 };
310
311 let match_term = Term::Match {
315 discriminant: Box::new(Term::Var(var_name.clone())),
316 motive: Box::new(build_motive(ind_type, &motive_body, var_name)),
317 cases: vec![base_term, step_term],
318 };
319
320 let lambda_term = Term::Lambda {
323 param: var_name.clone(),
324 param_type: Box::new(Term::Global(ind_type.clone())),
325 body: Box::new(match_term),
326 };
327
328 Ok(Term::Fix {
331 name: fix_name,
332 body: Box::new(lambda_term),
333 })
334 }
335
336 InferenceRule::ExistentialIntro {
339 witness: witness_str,
340 witness_type,
341 } => {
342 if tree.premises.len() != 1 {
343 return Err(KernelError::CertificationError(
344 "ExistentialIntro requires exactly 1 premise".to_string(),
345 ));
346 }
347
348 let (variable, body) = match &tree.conclusion {
350 ProofExpr::Exists { variable, body } => (variable.clone(), body.as_ref().clone()),
351 _ => {
352 return Err(KernelError::CertificationError(
353 "ExistentialIntro conclusion must be Exists".to_string(),
354 ))
355 }
356 };
357
358 let witness_term = if ctx.is_local(witness_str) {
360 Term::Var(witness_str.clone())
361 } else {
362 Term::Global(witness_str.clone())
363 };
364
365 let proof_term = certify(&tree.premises[0], ctx)?;
367
368 let type_a = Term::Global(witness_type.clone());
370
371 let predicate = match &body {
375 ProofExpr::Predicate { name, args, .. }
377 if args.len() == 1
378 && matches!(&args[0], ProofTerm::Variable(v) if v == &variable) =>
379 {
380 Term::Global(name.clone())
381 }
382 _ => {
384 let body_type = proof_expr_to_type(&body)?;
385 Term::Lambda {
386 param: variable.clone(),
387 param_type: Box::new(type_a.clone()),
388 body: Box::new(body_type),
389 }
390 }
391 };
392
393 let witness_ctor = Term::Global("witness".to_string());
396
397 let applied = Term::App(
398 Box::new(Term::App(
399 Box::new(Term::App(
400 Box::new(Term::App(Box::new(witness_ctor), Box::new(type_a))),
401 Box::new(predicate),
402 )),
403 Box::new(witness_term),
404 )),
405 Box::new(proof_term),
406 );
407
408 Ok(applied)
409 }
410
411 InferenceRule::Rewrite { from, to } => {
415 if tree.premises.len() != 2 {
416 return Err(KernelError::CertificationError(
417 "Rewrite requires exactly 2 premises (equality, source)".to_string(),
418 ));
419 }
420
421 let eq_proof = certify(&tree.premises[0], ctx)?;
423 let source_proof = certify(&tree.premises[1], ctx)?;
424
425 let from_term = proof_term_to_kernel_term(from)?;
427 let to_term = proof_term_to_kernel_term(to)?;
428
429 let predicate = build_equality_predicate(&tree.conclusion, to)?;
432
433 let eq_rec = Term::Global("Eq_rec".to_string());
436 let entity = Term::Global("Entity".to_string());
437
438 let applied = Term::App(
439 Box::new(Term::App(
440 Box::new(Term::App(
441 Box::new(Term::App(
442 Box::new(Term::App(
443 Box::new(Term::App(Box::new(eq_rec), Box::new(entity))),
444 Box::new(from_term),
445 )),
446 Box::new(predicate),
447 )),
448 Box::new(source_proof),
449 )),
450 Box::new(to_term),
451 )),
452 Box::new(eq_proof),
453 );
454
455 Ok(applied)
456 }
457
458 InferenceRule::EqualitySymmetry => {
461 if tree.premises.len() != 1 {
462 return Err(KernelError::CertificationError(
463 "EqualitySymmetry requires exactly 1 premise".to_string(),
464 ));
465 }
466
467 let premise_proof = certify(&tree.premises[0], ctx)?;
468
469 let (x, y) = match &tree.premises[0].conclusion {
471 ProofExpr::Identity(l, r) => {
472 (proof_term_to_kernel_term(l)?, proof_term_to_kernel_term(r)?)
473 }
474 _ => {
475 return Err(KernelError::CertificationError(
476 "EqualitySymmetry premise must be an Identity".to_string(),
477 ))
478 }
479 };
480
481 let eq_sym = Term::Global("Eq_sym".to_string());
484 let entity = Term::Global("Entity".to_string());
485
486 Ok(Term::App(
487 Box::new(Term::App(
488 Box::new(Term::App(
489 Box::new(Term::App(Box::new(eq_sym), Box::new(entity))),
490 Box::new(x),
491 )),
492 Box::new(y),
493 )),
494 Box::new(premise_proof),
495 ))
496 }
497
498 InferenceRule::EqualityTransitivity => {
501 if tree.premises.len() != 2 {
502 return Err(KernelError::CertificationError(
503 "EqualityTransitivity requires exactly 2 premises".to_string(),
504 ));
505 }
506
507 let proof1 = certify(&tree.premises[0], ctx)?;
508 let proof2 = certify(&tree.premises[1], ctx)?;
509
510 let (x, y) = match &tree.premises[0].conclusion {
512 ProofExpr::Identity(l, r) => {
513 (proof_term_to_kernel_term(l)?, proof_term_to_kernel_term(r)?)
514 }
515 _ => {
516 return Err(KernelError::CertificationError(
517 "EqualityTransitivity first premise must be Identity".to_string(),
518 ))
519 }
520 };
521
522 let z = match &tree.premises[1].conclusion {
524 ProofExpr::Identity(_, r) => proof_term_to_kernel_term(r)?,
525 _ => {
526 return Err(KernelError::CertificationError(
527 "EqualityTransitivity second premise must be Identity".to_string(),
528 ))
529 }
530 };
531
532 let eq_trans = Term::Global("Eq_trans".to_string());
535 let entity = Term::Global("Entity".to_string());
536
537 Ok(Term::App(
538 Box::new(Term::App(
539 Box::new(Term::App(
540 Box::new(Term::App(
541 Box::new(Term::App(
542 Box::new(Term::App(Box::new(eq_trans), Box::new(entity))),
543 Box::new(x),
544 )),
545 Box::new(y),
546 )),
547 Box::new(z),
548 )),
549 Box::new(proof1),
550 )),
551 Box::new(proof2),
552 ))
553 }
554
555 rule => Err(KernelError::CertificationError(format!(
557 "Certification not implemented for {:?}",
558 rule
559 ))),
560 }
561}
562
563fn build_equality_predicate(goal: &ProofExpr, replace_term: &ProofTerm) -> KernelResult<Term> {
569 if let ProofExpr::Predicate { name, args, .. } = goal {
572 if args.len() == 1 && &args[0] == replace_term {
573 return Ok(Term::Global(name.clone()));
574 }
575 }
576
577 let goal_type = proof_expr_to_type(goal)?;
579 let param_name = "_eq_var".to_string();
580 let substituted = substitute_term_in_kernel(
581 &goal_type,
582 &proof_term_to_kernel_term(replace_term)?,
583 &Term::Var(param_name.clone()),
584 );
585
586 Ok(Term::Lambda {
587 param: param_name,
588 param_type: Box::new(Term::Global("Entity".to_string())),
589 body: Box::new(substituted),
590 })
591}
592
593fn substitute_term_in_kernel(term: &Term, from: &Term, to: &Term) -> Term {
595 if term == from {
596 return to.clone();
597 }
598 match term {
599 Term::App(f, a) => Term::App(
600 Box::new(substitute_term_in_kernel(f, from, to)),
601 Box::new(substitute_term_in_kernel(a, from, to)),
602 ),
603 Term::Pi {
604 param,
605 param_type,
606 body_type,
607 } => Term::Pi {
608 param: param.clone(),
609 param_type: Box::new(substitute_term_in_kernel(param_type, from, to)),
610 body_type: Box::new(substitute_term_in_kernel(body_type, from, to)),
611 },
612 Term::Lambda {
613 param,
614 param_type,
615 body,
616 } => Term::Lambda {
617 param: param.clone(),
618 param_type: Box::new(substitute_term_in_kernel(param_type, from, to)),
619 body: Box::new(substitute_term_in_kernel(body, from, to)),
620 },
621 Term::Match {
622 discriminant,
623 motive,
624 cases,
625 } => Term::Match {
626 discriminant: Box::new(substitute_term_in_kernel(discriminant, from, to)),
627 motive: Box::new(substitute_term_in_kernel(motive, from, to)),
628 cases: cases
629 .iter()
630 .map(|c| substitute_term_in_kernel(c, from, to))
631 .collect(),
632 },
633 Term::Fix { name, body } => Term::Fix {
634 name: name.clone(),
635 body: Box::new(substitute_term_in_kernel(body, from, to)),
636 },
637 other => other.clone(),
638 }
639}
640
641fn certify_hypothesis(conclusion: &ProofExpr, ctx: &CertificationContext) -> KernelResult<Term> {
643 if let Some(ih_term) = ctx.get_ih_term(conclusion) {
645 return Ok(ih_term);
646 }
647
648 match conclusion {
649 ProofExpr::Atom(name) => {
650 if ctx.is_local(name) {
652 return Ok(Term::Var(name.clone()));
653 }
654 if ctx.kernel_ctx.get_global(name).is_some() {
656 Ok(Term::Global(name.clone()))
657 } else {
658 Err(KernelError::CertificationError(format!(
659 "Unknown hypothesis: {}",
660 name
661 )))
662 }
663 }
664 ProofExpr::Predicate { name, args, .. } => {
666 let mut target_type = Term::Global(name.clone());
668 for arg in args {
669 let arg_term = proof_term_to_kernel_term(arg)?;
670 target_type = Term::App(Box::new(target_type), Box::new(arg_term));
671 }
672
673 for (decl_name, decl_type) in ctx.kernel_ctx.iter_declarations() {
675 if types_structurally_match(&target_type, decl_type) {
676 return Ok(Term::Global(decl_name.to_string()));
677 }
678 }
679
680 Err(KernelError::CertificationError(format!(
681 "Cannot find hypothesis with type: {:?}",
682 conclusion
683 )))
684 }
685 ProofExpr::ForAll { .. } | ProofExpr::Implies(_, _) | ProofExpr::Identity(_, _) => {
687 let target_type = proof_expr_to_type(conclusion)?;
689
690 for (name, decl_type) in ctx.kernel_ctx.iter_declarations() {
692 if types_structurally_match(&target_type, decl_type) {
693 return Ok(Term::Global(name.to_string()));
694 }
695 }
696
697 Err(KernelError::CertificationError(format!(
698 "Cannot find hypothesis with type matching: {:?}",
699 conclusion
700 )))
701 }
702 _ => Err(KernelError::CertificationError(format!(
703 "Cannot certify hypothesis: {:?}",
704 conclusion
705 ))),
706 }
707}
708
709fn types_structurally_match(a: &Term, b: &Term) -> bool {
712 types_alpha_equiv(a, b, &mut Vec::new())
714}
715
716fn types_alpha_equiv(a: &Term, b: &Term, bindings: &mut Vec<(String, String)>) -> bool {
719 match (a, b) {
720 (Term::Sort(u1), Term::Sort(u2)) => u1 == u2,
721 (Term::Var(v1), Term::Var(v2)) => {
722 for (bound_a, bound_b) in bindings.iter().rev() {
724 if v1 == bound_a {
725 return v2 == bound_b;
726 }
727 if v2 == bound_b {
728 return false; }
730 }
731 v1 == v2
733 }
734 (Term::Global(g1), Term::Global(g2)) => g1 == g2,
735 (Term::App(f1, a1), Term::App(f2, a2)) => {
736 types_alpha_equiv(f1, f2, bindings) && types_alpha_equiv(a1, a2, bindings)
737 }
738 (
739 Term::Pi {
740 param: p1,
741 param_type: pt1,
742 body_type: bt1,
743 },
744 Term::Pi {
745 param: p2,
746 param_type: pt2,
747 body_type: bt2,
748 },
749 ) => {
750 if !types_alpha_equiv(pt1, pt2, bindings) {
752 return false;
753 }
754 bindings.push((p1.clone(), p2.clone()));
756 let result = types_alpha_equiv(bt1, bt2, bindings);
757 bindings.pop();
758 result
759 }
760 (
761 Term::Lambda {
762 param: p1,
763 param_type: pt1,
764 body: b1,
765 },
766 Term::Lambda {
767 param: p2,
768 param_type: pt2,
769 body: b2,
770 },
771 ) => {
772 if !types_alpha_equiv(pt1, pt2, bindings) {
774 return false;
775 }
776 bindings.push((p1.clone(), p2.clone()));
778 let result = types_alpha_equiv(b1, b2, bindings);
779 bindings.pop();
780 result
781 }
782 _ => false,
783 }
784}
785
786fn extract_and_types(conclusion: &ProofExpr) -> KernelResult<(Term, Term)> {
788 match conclusion {
789 ProofExpr::And(p, q) => {
790 let p_term = proof_expr_to_type(p)?;
791 let q_term = proof_expr_to_type(q)?;
792 Ok((p_term, q_term))
793 }
794 _ => Err(KernelError::CertificationError(format!(
795 "Expected And, got {:?}",
796 conclusion
797 ))),
798 }
799}
800
801fn proof_expr_to_type(expr: &ProofExpr) -> KernelResult<Term> {
803 match expr {
804 ProofExpr::Atom(name) => Ok(Term::Global(name.clone())),
805 ProofExpr::And(p, q) => Ok(Term::App(
806 Box::new(Term::App(
807 Box::new(Term::Global("And".to_string())),
808 Box::new(proof_expr_to_type(p)?),
809 )),
810 Box::new(proof_expr_to_type(q)?),
811 )),
812 ProofExpr::Or(p, q) => Ok(Term::App(
813 Box::new(Term::App(
814 Box::new(Term::Global("Or".to_string())),
815 Box::new(proof_expr_to_type(p)?),
816 )),
817 Box::new(proof_expr_to_type(q)?),
818 )),
819 ProofExpr::Implies(p, q) => Ok(Term::Pi {
820 param: "_".to_string(),
821 param_type: Box::new(proof_expr_to_type(p)?),
822 body_type: Box::new(proof_expr_to_type(q)?),
823 }),
824 ProofExpr::ForAll { variable, body } => {
826 let body_type = proof_expr_to_type(body)?;
827 Ok(Term::Pi {
828 param: variable.clone(),
829 param_type: Box::new(Term::Global("Entity".to_string())),
830 body_type: Box::new(body_type),
831 })
832 }
833 ProofExpr::Predicate { name, args, .. } => {
835 let mut result = Term::Global(name.clone());
836 for arg in args {
837 let arg_term = proof_term_to_kernel_term(arg)?;
838 result = Term::App(Box::new(result), Box::new(arg_term));
839 }
840 Ok(result)
841 }
842 ProofExpr::Identity(l, r) => {
844 let l_term = proof_term_to_kernel_term(l)?;
845 let r_term = proof_term_to_kernel_term(r)?;
846 Ok(Term::App(
847 Box::new(Term::App(
848 Box::new(Term::App(
849 Box::new(Term::Global("Eq".to_string())),
850 Box::new(Term::Global("Entity".to_string())),
851 )),
852 Box::new(l_term),
853 )),
854 Box::new(r_term),
855 ))
856 }
857 ProofExpr::Exists { variable, body } => {
860 let var_type = Term::Global("Nat".to_string()); let body_type = proof_expr_to_type(body)?;
862
863 let predicate = Term::Lambda {
865 param: variable.clone(),
866 param_type: Box::new(var_type.clone()),
867 body: Box::new(body_type),
868 };
869
870 Ok(Term::App(
871 Box::new(Term::App(
872 Box::new(Term::Global("Ex".to_string())),
873 Box::new(var_type),
874 )),
875 Box::new(predicate),
876 ))
877 }
878 _ => Err(KernelError::CertificationError(format!(
879 "Cannot convert {:?} to kernel type",
880 expr
881 ))),
882 }
883}
884
885fn proof_term_to_kernel_term(term: &ProofTerm) -> KernelResult<Term> {
890 match term {
891 ProofTerm::Constant(name) => Ok(Term::Global(name.clone())),
892 ProofTerm::Variable(name) => Ok(Term::Var(name.clone())),
893 ProofTerm::BoundVarRef(name) => Ok(Term::Var(name.clone())),
894 ProofTerm::Function(name, args) => {
895 let mut result = Term::Global(name.clone());
897 for arg in args {
898 let arg_term = proof_term_to_kernel_term(arg)?;
899 result = Term::App(Box::new(result), Box::new(arg_term));
900 }
901 Ok(result)
902 }
903 ProofTerm::Group(_) => Err(KernelError::CertificationError(
904 "Cannot convert Group to kernel term".to_string(),
905 )),
906 }
907}
908
909fn extract_motive_body(conclusion: &ProofExpr, var_name: &str) -> KernelResult<Term> {
916 match conclusion {
917 ProofExpr::ForAll { variable, body } if variable == var_name => {
918 proof_expr_to_type(body)
920 }
921 _ => Err(KernelError::CertificationError(format!(
922 "StructuralInduction conclusion must be ForAll over {}, got {:?}",
923 var_name, conclusion
924 ))),
925 }
926}
927
928fn compute_ih_conclusion(
931 conclusion: &ProofExpr,
932 orig_var: &str,
933 step_var: &str,
934) -> KernelResult<ProofExpr> {
935 match conclusion {
936 ProofExpr::ForAll { body, .. } => Ok(substitute_var_in_expr(body, orig_var, step_var)),
937 _ => Err(KernelError::CertificationError(
938 "Expected ForAll for IH computation".to_string(),
939 )),
940 }
941}
942
943fn build_motive(ind_type: &str, result_type: &Term, motive_param: &str) -> Term {
947 Term::Lambda {
948 param: motive_param.to_string(),
949 param_type: Box::new(Term::Global(ind_type.to_string())),
950 body: Box::new(result_type.clone()),
951 }
952}
953
954fn substitute_var_in_expr(expr: &ProofExpr, from: &str, to: &str) -> ProofExpr {
956 match expr {
957 ProofExpr::Atom(s) if s == from => ProofExpr::Atom(to.to_string()),
958 ProofExpr::Atom(s) => ProofExpr::Atom(s.clone()),
959
960 ProofExpr::TypedVar { name, typename } => ProofExpr::TypedVar {
961 name: if name == from {
962 to.to_string()
963 } else {
964 name.clone()
965 },
966 typename: typename.clone(),
967 },
968
969 ProofExpr::Predicate { name, args, world } => ProofExpr::Predicate {
970 name: name.clone(),
971 args: args
972 .iter()
973 .map(|a| substitute_var_in_term(a, from, to))
974 .collect(),
975 world: world.clone(),
976 },
977
978 ProofExpr::Identity(l, r) => ProofExpr::Identity(
979 substitute_var_in_term(l, from, to),
980 substitute_var_in_term(r, from, to),
981 ),
982
983 ProofExpr::And(l, r) => ProofExpr::And(
984 Box::new(substitute_var_in_expr(l, from, to)),
985 Box::new(substitute_var_in_expr(r, from, to)),
986 ),
987
988 ProofExpr::Or(l, r) => ProofExpr::Or(
989 Box::new(substitute_var_in_expr(l, from, to)),
990 Box::new(substitute_var_in_expr(r, from, to)),
991 ),
992
993 ProofExpr::Implies(l, r) => ProofExpr::Implies(
994 Box::new(substitute_var_in_expr(l, from, to)),
995 Box::new(substitute_var_in_expr(r, from, to)),
996 ),
997
998 ProofExpr::Iff(l, r) => ProofExpr::Iff(
999 Box::new(substitute_var_in_expr(l, from, to)),
1000 Box::new(substitute_var_in_expr(r, from, to)),
1001 ),
1002
1003 ProofExpr::Not(inner) => {
1004 ProofExpr::Not(Box::new(substitute_var_in_expr(inner, from, to)))
1005 }
1006
1007 ProofExpr::ForAll { variable, body } if variable != from => ProofExpr::ForAll {
1009 variable: variable.clone(),
1010 body: Box::new(substitute_var_in_expr(body, from, to)),
1011 },
1012
1013 ProofExpr::Exists { variable, body } if variable != from => ProofExpr::Exists {
1014 variable: variable.clone(),
1015 body: Box::new(substitute_var_in_expr(body, from, to)),
1016 },
1017
1018 ProofExpr::Lambda { variable, body } if variable != from => ProofExpr::Lambda {
1019 variable: variable.clone(),
1020 body: Box::new(substitute_var_in_expr(body, from, to)),
1021 },
1022
1023 ProofExpr::App(f, a) => ProofExpr::App(
1024 Box::new(substitute_var_in_expr(f, from, to)),
1025 Box::new(substitute_var_in_expr(a, from, to)),
1026 ),
1027
1028 ProofExpr::Term(t) => ProofExpr::Term(substitute_var_in_term(t, from, to)),
1029
1030 ProofExpr::Ctor { name, args } => ProofExpr::Ctor {
1031 name: name.clone(),
1032 args: args
1033 .iter()
1034 .map(|a| substitute_var_in_expr(a, from, to))
1035 .collect(),
1036 },
1037
1038 other => other.clone(),
1040 }
1041}
1042
1043fn substitute_var_in_term(term: &ProofTerm, from: &str, to: &str) -> ProofTerm {
1045 match term {
1046 ProofTerm::Variable(s) if s == from => ProofTerm::Variable(to.to_string()),
1047 ProofTerm::Variable(s) => ProofTerm::Variable(s.clone()),
1048 ProofTerm::Constant(s) => ProofTerm::Constant(s.clone()),
1049 ProofTerm::BoundVarRef(s) if s == from => ProofTerm::BoundVarRef(to.to_string()),
1050 ProofTerm::BoundVarRef(s) => ProofTerm::BoundVarRef(s.clone()),
1051 ProofTerm::Function(name, args) => ProofTerm::Function(
1052 name.clone(),
1053 args.iter()
1054 .map(|a| substitute_var_in_term(a, from, to))
1055 .collect(),
1056 ),
1057 ProofTerm::Group(terms) => ProofTerm::Group(
1058 terms
1059 .iter()
1060 .map(|t| substitute_var_in_term(t, from, to))
1061 .collect(),
1062 ),
1063 }
1064}