1use std::collections::HashMap;
34use std::sync::atomic::{AtomicUsize, Ordering};
35
36use crate::error::{ProofError, ProofResult};
37use crate::{MatchArm, ProofExpr, ProofTerm};
38
39pub type Substitution = HashMap<String, ProofTerm>;
58
59pub type ExprSubstitution = HashMap<String, ProofExpr>;
76
77static ALPHA_COUNTER: AtomicUsize = AtomicUsize::new(0);
83
84fn fresh_alpha_constant() -> ProofTerm {
87 let id = ALPHA_COUNTER.fetch_add(1, Ordering::SeqCst);
88 ProofTerm::Constant(format!("#α{}", id))
89}
90
91fn is_constructor_form(expr: &ProofExpr) -> bool {
98 matches!(expr, ProofExpr::Ctor { .. })
99}
100
101pub fn beta_reduce(expr: &ProofExpr) -> ProofExpr {
139 match expr {
140 ProofExpr::App(func, arg) => {
142 let func_reduced = beta_reduce(func);
144 let arg_reduced = beta_reduce(arg);
145
146 match func_reduced {
147 ProofExpr::Lambda { variable, body } => {
149 let result = substitute_expr_for_var(&body, &variable, &arg_reduced);
150 beta_reduce(&result)
152 }
153
154 ProofExpr::Fixpoint { ref name, ref body } if is_constructor_form(&arg_reduced) => {
157 let fix_expr = ProofExpr::Fixpoint {
159 name: name.clone(),
160 body: body.clone(),
161 };
162 let unfolded = substitute_expr_for_var(body, name, &fix_expr);
163 let applied = ProofExpr::App(Box::new(unfolded), Box::new(arg_reduced));
165 beta_reduce(&applied)
166 }
167
168 _ => {
169 ProofExpr::App(Box::new(func_reduced), Box::new(arg_reduced))
171 }
172 }
173 }
174
175 ProofExpr::And(l, r) => ProofExpr::And(
177 Box::new(beta_reduce(l)),
178 Box::new(beta_reduce(r)),
179 ),
180 ProofExpr::Or(l, r) => ProofExpr::Or(
181 Box::new(beta_reduce(l)),
182 Box::new(beta_reduce(r)),
183 ),
184 ProofExpr::Implies(l, r) => ProofExpr::Implies(
185 Box::new(beta_reduce(l)),
186 Box::new(beta_reduce(r)),
187 ),
188 ProofExpr::Iff(l, r) => ProofExpr::Iff(
189 Box::new(beta_reduce(l)),
190 Box::new(beta_reduce(r)),
191 ),
192 ProofExpr::Not(inner) => ProofExpr::Not(Box::new(beta_reduce(inner))),
193
194 ProofExpr::ForAll { variable, body } => ProofExpr::ForAll {
196 variable: variable.clone(),
197 body: Box::new(beta_reduce(body)),
198 },
199 ProofExpr::Exists { variable, body } => ProofExpr::Exists {
200 variable: variable.clone(),
201 body: Box::new(beta_reduce(body)),
202 },
203
204 ProofExpr::Lambda { variable, body } => ProofExpr::Lambda {
206 variable: variable.clone(),
207 body: Box::new(beta_reduce(body)),
208 },
209
210 ProofExpr::Modal { domain, force, flavor, body } => ProofExpr::Modal {
212 domain: domain.clone(),
213 force: *force,
214 flavor: flavor.clone(),
215 body: Box::new(beta_reduce(body)),
216 },
217 ProofExpr::Temporal { operator, body } => ProofExpr::Temporal {
218 operator: operator.clone(),
219 body: Box::new(beta_reduce(body)),
220 },
221 ProofExpr::TemporalBinary { operator, left, right } => ProofExpr::TemporalBinary {
222 operator: operator.clone(),
223 left: Box::new(beta_reduce(left)),
224 right: Box::new(beta_reduce(right)),
225 },
226
227 ProofExpr::Ctor { name, args } => ProofExpr::Ctor {
229 name: name.clone(),
230 args: args.iter().map(beta_reduce).collect(),
231 },
232
233 ProofExpr::Match { scrutinee, arms } => {
235 let reduced_scrutinee = beta_reduce(scrutinee);
236
237 if let ProofExpr::Ctor { name: ctor_name, args: ctor_args } = &reduced_scrutinee {
239 for arm in arms {
240 if &arm.ctor == ctor_name {
241 let mut result = arm.body.clone();
243 for (binding, arg) in arm.bindings.iter().zip(ctor_args.iter()) {
244 result = substitute_expr_for_var(&result, binding, arg);
245 }
246 return beta_reduce(&result);
248 }
249 }
250 }
251
252 ProofExpr::Match {
254 scrutinee: Box::new(reduced_scrutinee),
255 arms: arms.iter().map(|arm| MatchArm {
256 ctor: arm.ctor.clone(),
257 bindings: arm.bindings.clone(),
258 body: beta_reduce(&arm.body),
259 }).collect(),
260 }
261 }
262
263 ProofExpr::Fixpoint { name, body } => ProofExpr::Fixpoint {
265 name: name.clone(),
266 body: Box::new(beta_reduce(body)),
267 },
268
269 ProofExpr::Predicate { .. }
271 | ProofExpr::Identity(_, _)
272 | ProofExpr::Atom(_)
273 | ProofExpr::NeoEvent { .. }
274 | ProofExpr::TypedVar { .. }
275 | ProofExpr::Unsupported(_)
276 | ProofExpr::Hole(_)
277 | ProofExpr::Term(_) => expr.clone(),
278 }
279}
280
281fn substitute_expr_for_var(body: &ProofExpr, var: &str, replacement: &ProofExpr) -> ProofExpr {
286 match body {
287 ProofExpr::Predicate { name, args, world } => ProofExpr::Predicate {
288 name: name.clone(),
289 args: args.iter().map(|t| substitute_term_for_var(t, var, replacement)).collect(),
290 world: world.clone(),
291 },
292
293 ProofExpr::Identity(l, r) => ProofExpr::Identity(
294 substitute_term_for_var(l, var, replacement),
295 substitute_term_for_var(r, var, replacement),
296 ),
297
298 ProofExpr::Atom(a) => {
299 if a == var {
301 replacement.clone()
302 } else {
303 ProofExpr::Atom(a.clone())
304 }
305 }
306
307 ProofExpr::And(l, r) => ProofExpr::And(
308 Box::new(substitute_expr_for_var(l, var, replacement)),
309 Box::new(substitute_expr_for_var(r, var, replacement)),
310 ),
311 ProofExpr::Or(l, r) => ProofExpr::Or(
312 Box::new(substitute_expr_for_var(l, var, replacement)),
313 Box::new(substitute_expr_for_var(r, var, replacement)),
314 ),
315 ProofExpr::Implies(l, r) => ProofExpr::Implies(
316 Box::new(substitute_expr_for_var(l, var, replacement)),
317 Box::new(substitute_expr_for_var(r, var, replacement)),
318 ),
319 ProofExpr::Iff(l, r) => ProofExpr::Iff(
320 Box::new(substitute_expr_for_var(l, var, replacement)),
321 Box::new(substitute_expr_for_var(r, var, replacement)),
322 ),
323 ProofExpr::Not(inner) => ProofExpr::Not(
324 Box::new(substitute_expr_for_var(inner, var, replacement))
325 ),
326
327 ProofExpr::ForAll { variable, body: inner } => {
329 if variable == var {
330 body.clone()
332 } else {
333 ProofExpr::ForAll {
334 variable: variable.clone(),
335 body: Box::new(substitute_expr_for_var(inner, var, replacement)),
336 }
337 }
338 }
339 ProofExpr::Exists { variable, body: inner } => {
340 if variable == var {
341 body.clone()
342 } else {
343 ProofExpr::Exists {
344 variable: variable.clone(),
345 body: Box::new(substitute_expr_for_var(inner, var, replacement)),
346 }
347 }
348 }
349
350 ProofExpr::Lambda { variable, body: inner } => {
352 if variable == var {
353 body.clone()
354 } else {
355 ProofExpr::Lambda {
356 variable: variable.clone(),
357 body: Box::new(substitute_expr_for_var(inner, var, replacement)),
358 }
359 }
360 }
361
362 ProofExpr::App(f, a) => ProofExpr::App(
363 Box::new(substitute_expr_for_var(f, var, replacement)),
364 Box::new(substitute_expr_for_var(a, var, replacement)),
365 ),
366
367 ProofExpr::Modal { domain, force, flavor, body: inner } => ProofExpr::Modal {
368 domain: domain.clone(),
369 force: *force,
370 flavor: flavor.clone(),
371 body: Box::new(substitute_expr_for_var(inner, var, replacement)),
372 },
373
374 ProofExpr::Temporal { operator, body: inner } => ProofExpr::Temporal {
375 operator: operator.clone(),
376 body: Box::new(substitute_expr_for_var(inner, var, replacement)),
377 },
378
379 ProofExpr::TemporalBinary { operator, left, right } => ProofExpr::TemporalBinary {
380 operator: operator.clone(),
381 left: Box::new(substitute_expr_for_var(left, var, replacement)),
382 right: Box::new(substitute_expr_for_var(right, var, replacement)),
383 },
384
385 ProofExpr::NeoEvent { event_var, verb, roles } => {
386 ProofExpr::NeoEvent {
388 event_var: event_var.clone(),
389 verb: verb.clone(),
390 roles: roles.iter().map(|(r, t)| {
391 (r.clone(), substitute_term_for_var(t, var, replacement))
392 }).collect(),
393 }
394 }
395
396 ProofExpr::Ctor { name, args } => ProofExpr::Ctor {
397 name: name.clone(),
398 args: args.iter().map(|a| substitute_expr_for_var(a, var, replacement)).collect(),
399 },
400
401 ProofExpr::Match { scrutinee, arms } => ProofExpr::Match {
402 scrutinee: Box::new(substitute_expr_for_var(scrutinee, var, replacement)),
403 arms: arms.iter().map(|arm| {
404 if arm.bindings.contains(&var.to_string()) {
406 arm.clone()
407 } else {
408 MatchArm {
409 ctor: arm.ctor.clone(),
410 bindings: arm.bindings.clone(),
411 body: substitute_expr_for_var(&arm.body, var, replacement),
412 }
413 }
414 }).collect(),
415 },
416
417 ProofExpr::Fixpoint { name, body: inner } => {
418 if name == var {
419 body.clone()
420 } else {
421 ProofExpr::Fixpoint {
422 name: name.clone(),
423 body: Box::new(substitute_expr_for_var(inner, var, replacement)),
424 }
425 }
426 }
427
428 ProofExpr::TypedVar { .. } | ProofExpr::Unsupported(_) => body.clone(),
429
430 ProofExpr::Hole(_) => body.clone(),
432
433 ProofExpr::Term(t) => ProofExpr::Term(substitute_term_for_var(t, var, replacement)),
435 }
436}
437
438fn substitute_term_for_var(term: &ProofTerm, var: &str, replacement: &ProofExpr) -> ProofTerm {
442 match term {
443 ProofTerm::Variable(v) if v == var => {
444 expr_to_term(replacement)
446 }
447 ProofTerm::BoundVarRef(v) if v == var => {
449 expr_to_term(replacement)
450 }
451 ProofTerm::Variable(_) | ProofTerm::Constant(_) | ProofTerm::BoundVarRef(_) => term.clone(),
452 ProofTerm::Function(name, args) => ProofTerm::Function(
453 name.clone(),
454 args.iter().map(|a| substitute_term_for_var(a, var, replacement)).collect(),
455 ),
456 ProofTerm::Group(terms) => ProofTerm::Group(
457 terms.iter().map(|t| substitute_term_for_var(t, var, replacement)).collect(),
458 ),
459 }
460}
461
462fn expr_to_term(expr: &ProofExpr) -> ProofTerm {
467 match expr {
468 ProofExpr::Atom(s) => ProofTerm::Constant(s.clone()),
470
471 ProofExpr::Predicate { name, args, .. } if args.is_empty() => {
473 ProofTerm::Constant(name.clone())
474 }
475
476 ProofExpr::Predicate { name, args, .. } => {
478 ProofTerm::Function(name.clone(), args.clone())
479 }
480
481 ProofExpr::Ctor { name, args } => {
483 ProofTerm::Function(name.clone(), args.iter().map(expr_to_term).collect())
484 }
485
486 ProofExpr::TypedVar { name, .. } => ProofTerm::Variable(name.clone()),
488
489 ProofExpr::Term(t) => t.clone(),
491
492 _ => ProofTerm::Constant(format!("{}", expr)),
494 }
495}
496
497pub fn unify_terms(t1: &ProofTerm, t2: &ProofTerm) -> ProofResult<Substitution> {
544 let mut subst = Substitution::new();
545 unify_terms_with_subst(t1, t2, &mut subst)?;
546 Ok(subst)
547}
548
549fn unify_terms_with_subst(
551 t1: &ProofTerm,
552 t2: &ProofTerm,
553 subst: &mut Substitution,
554) -> ProofResult<()> {
555 let t1 = apply_subst_to_term(t1, subst);
557 let t2 = apply_subst_to_term(t2, subst);
558
559 match (&t1, &t2) {
560 (ProofTerm::Constant(c1), ProofTerm::Constant(c2)) if c1 == c2 => Ok(()),
562
563 (ProofTerm::Constant(c1), ProofTerm::Constant(c2)) => {
565 Err(ProofError::SymbolMismatch {
566 left: c1.clone(),
567 right: c2.clone(),
568 })
569 }
570
571 (ProofTerm::Variable(v), t) => {
573 if let ProofTerm::Variable(v2) = t {
575 if v == v2 {
576 return Ok(());
577 }
578 }
579 if occurs(v, t) {
581 return Err(ProofError::OccursCheck {
582 variable: v.clone(),
583 term: t.clone(),
584 });
585 }
586 subst.insert(v.clone(), t.clone());
587 Ok(())
588 }
589
590 (t, ProofTerm::Variable(v)) => {
592 if occurs(v, t) {
594 return Err(ProofError::OccursCheck {
595 variable: v.clone(),
596 term: t.clone(),
597 });
598 }
599 subst.insert(v.clone(), t.clone());
600 Ok(())
601 }
602
603 (ProofTerm::BoundVarRef(v), t) => {
606 if let ProofTerm::BoundVarRef(v2) = t {
607 if v == v2 {
608 return Ok(());
609 }
610 }
611 if occurs(v, t) {
612 return Err(ProofError::OccursCheck {
613 variable: v.clone(),
614 term: t.clone(),
615 });
616 }
617 subst.insert(v.clone(), t.clone());
618 Ok(())
619 }
620
621 (t, ProofTerm::BoundVarRef(v)) => {
623 if occurs(v, t) {
624 return Err(ProofError::OccursCheck {
625 variable: v.clone(),
626 term: t.clone(),
627 });
628 }
629 subst.insert(v.clone(), t.clone());
630 Ok(())
631 }
632
633 (ProofTerm::Function(f1, args1), ProofTerm::Function(f2, args2)) => {
635 if f1 != f2 {
636 return Err(ProofError::SymbolMismatch {
637 left: f1.clone(),
638 right: f2.clone(),
639 });
640 }
641 if args1.len() != args2.len() {
642 return Err(ProofError::ArityMismatch {
643 expected: args1.len(),
644 found: args2.len(),
645 });
646 }
647 for (a1, a2) in args1.iter().zip(args2.iter()) {
648 unify_terms_with_subst(a1, a2, subst)?;
649 }
650 Ok(())
651 }
652
653 (ProofTerm::Group(g1), ProofTerm::Group(g2)) => {
655 if g1.len() != g2.len() {
656 return Err(ProofError::ArityMismatch {
657 expected: g1.len(),
658 found: g2.len(),
659 });
660 }
661 for (t1, t2) in g1.iter().zip(g2.iter()) {
662 unify_terms_with_subst(t1, t2, subst)?;
663 }
664 Ok(())
665 }
666
667 _ => Err(ProofError::UnificationFailed {
669 left: t1,
670 right: t2,
671 }),
672 }
673}
674
675fn occurs(var: &str, term: &ProofTerm) -> bool {
678 match term {
679 ProofTerm::Variable(v) => v == var,
680 ProofTerm::BoundVarRef(v) => v == var, ProofTerm::Constant(_) => false,
682 ProofTerm::Function(_, args) => args.iter().any(|a| occurs(var, a)),
683 ProofTerm::Group(terms) => terms.iter().any(|t| occurs(var, t)),
684 }
685}
686
687pub fn apply_subst_to_term(term: &ProofTerm, subst: &Substitution) -> ProofTerm {
719 match term {
720 ProofTerm::Variable(v) => {
721 if let Some(replacement) = subst.get(v) {
722 apply_subst_to_term(replacement, subst)
724 } else {
725 term.clone()
726 }
727 }
728 ProofTerm::BoundVarRef(v) => {
730 if let Some(replacement) = subst.get(v) {
731 apply_subst_to_term(replacement, subst)
732 } else {
733 term.clone()
734 }
735 }
736 ProofTerm::Constant(_) => term.clone(),
737 ProofTerm::Function(name, args) => {
738 let new_args = args.iter().map(|a| apply_subst_to_term(a, subst)).collect();
739 ProofTerm::Function(name.clone(), new_args)
740 }
741 ProofTerm::Group(terms) => {
742 let new_terms = terms.iter().map(|t| apply_subst_to_term(t, subst)).collect();
743 ProofTerm::Group(new_terms)
744 }
745 }
746}
747
748pub fn unify_exprs(e1: &ProofExpr, e2: &ProofExpr) -> ProofResult<Substitution> {
787 let mut subst = Substitution::new();
788 unify_exprs_with_subst(e1, e2, &mut subst)?;
789 Ok(subst)
790}
791
792fn unify_exprs_with_subst(
794 e1: &ProofExpr,
795 e2: &ProofExpr,
796 subst: &mut Substitution,
797) -> ProofResult<()> {
798 let e1 = beta_reduce(e1);
801 let e2 = beta_reduce(e2);
802
803 match (&e1, &e2) {
804 (ProofExpr::Atom(a1), ProofExpr::Atom(a2)) if a1 == a2 => Ok(()),
806
807 (
809 ProofExpr::Predicate { name: n1, args: a1, world: w1 },
810 ProofExpr::Predicate { name: n2, args: a2, world: w2 },
811 ) => {
812 if n1 != n2 {
813 return Err(ProofError::SymbolMismatch {
814 left: n1.clone(),
815 right: n2.clone(),
816 });
817 }
818 if a1.len() != a2.len() {
819 return Err(ProofError::ArityMismatch {
820 expected: a1.len(),
821 found: a2.len(),
822 });
823 }
824 match (w1, w2) {
826 (Some(w1), Some(w2)) if w1 != w2 => {
827 return Err(ProofError::SymbolMismatch {
828 left: w1.clone(),
829 right: w2.clone(),
830 });
831 }
832 _ => {}
833 }
834 for (t1, t2) in a1.iter().zip(a2.iter()) {
836 unify_terms_with_subst(t1, t2, subst)?;
837 }
838 Ok(())
839 }
840
841 (ProofExpr::Identity(l1, r1), ProofExpr::Identity(l2, r2)) => {
843 unify_terms_with_subst(l1, l2, subst)?;
844 unify_terms_with_subst(r1, r2, subst)?;
845 Ok(())
846 }
847
848 (ProofExpr::And(l1, r1), ProofExpr::And(l2, r2))
850 | (ProofExpr::Or(l1, r1), ProofExpr::Or(l2, r2))
851 | (ProofExpr::Implies(l1, r1), ProofExpr::Implies(l2, r2))
852 | (ProofExpr::Iff(l1, r1), ProofExpr::Iff(l2, r2)) => {
853 unify_exprs_with_subst(l1, l2, subst)?;
854 unify_exprs_with_subst(r1, r2, subst)?;
855 Ok(())
856 }
857
858 (ProofExpr::Not(inner1), ProofExpr::Not(inner2)) => {
860 unify_exprs_with_subst(inner1, inner2, subst)
861 }
862
863 (
866 ProofExpr::ForAll { variable: v1, body: b1 },
867 ProofExpr::ForAll { variable: v2, body: b2 },
868 )
869 | (
870 ProofExpr::Exists { variable: v1, body: b1 },
871 ProofExpr::Exists { variable: v2, body: b2 },
872 ) => {
873 let fresh = fresh_alpha_constant();
876
877 let subst1: Substitution = [(v1.clone(), fresh.clone())].into_iter().collect();
879 let subst2: Substitution = [(v2.clone(), fresh)].into_iter().collect();
880
881 let body1_renamed = apply_subst_to_expr(b1, &subst1);
883 let body2_renamed = apply_subst_to_expr(b2, &subst2);
884
885 unify_exprs_with_subst(&body1_renamed, &body2_renamed, subst)
887 }
888
889 (
891 ProofExpr::Lambda { variable: v1, body: b1 },
892 ProofExpr::Lambda { variable: v2, body: b2 },
893 ) => {
894 let fresh = fresh_alpha_constant();
896 let subst1: Substitution = [(v1.clone(), fresh.clone())].into_iter().collect();
897 let subst2: Substitution = [(v2.clone(), fresh)].into_iter().collect();
898 let body1_renamed = apply_subst_to_expr(b1, &subst1);
899 let body2_renamed = apply_subst_to_expr(b2, &subst2);
900 unify_exprs_with_subst(&body1_renamed, &body2_renamed, subst)
901 }
902
903 (ProofExpr::App(f1, a1), ProofExpr::App(f2, a2)) => {
905 unify_exprs_with_subst(f1, f2, subst)?;
906 unify_exprs_with_subst(a1, a2, subst)?;
907 Ok(())
908 }
909
910 (
913 ProofExpr::NeoEvent {
914 event_var: e1,
915 verb: v1,
916 roles: r1,
917 },
918 ProofExpr::NeoEvent {
919 event_var: e2,
920 verb: v2,
921 roles: r2,
922 },
923 ) => {
924 if v1.to_lowercase() != v2.to_lowercase() {
926 return Err(ProofError::SymbolMismatch {
927 left: v1.clone(),
928 right: v2.clone(),
929 });
930 }
931
932 if r1.len() != r2.len() {
934 return Err(ProofError::ArityMismatch {
935 expected: r1.len(),
936 found: r2.len(),
937 });
938 }
939
940 let fresh = fresh_alpha_constant();
942 let subst1: Substitution = [(e1.clone(), fresh.clone())].into_iter().collect();
943 let subst2: Substitution = [(e2.clone(), fresh)].into_iter().collect();
944
945 for ((role1, term1), (role2, term2)) in r1.iter().zip(r2.iter()) {
947 if role1 != role2 {
949 return Err(ProofError::SymbolMismatch {
950 left: role1.clone(),
951 right: role2.clone(),
952 });
953 }
954 let t1_renamed = apply_subst_to_term(term1, &subst1);
956 let t2_renamed = apply_subst_to_term(term2, &subst2);
957 unify_terms_with_subst(&t1_renamed, &t2_renamed, subst)?;
958 }
959 Ok(())
960 }
961
962 (
965 ProofExpr::Temporal { operator: op1, body: b1 },
966 ProofExpr::Temporal { operator: op2, body: b2 },
967 ) => {
968 if op1 != op2 {
969 return Err(ProofError::ExprUnificationFailed {
970 left: e1.clone(),
971 right: e2.clone(),
972 });
973 }
974 unify_exprs_with_subst(b1, b2, subst)
975 }
976
977 (
980 ProofExpr::TemporalBinary { operator: op1, left: l1, right: r1 },
981 ProofExpr::TemporalBinary { operator: op2, left: l2, right: r2 },
982 ) => {
983 if op1 != op2 {
984 return Err(ProofError::ExprUnificationFailed {
985 left: e1.clone(),
986 right: e2.clone(),
987 });
988 }
989 unify_exprs_with_subst(l1, l2, subst)?;
990 unify_exprs_with_subst(r1, r2, subst)
991 }
992
993 _ => Err(ProofError::ExprUnificationFailed {
995 left: e1.clone(),
996 right: e2.clone(),
997 }),
998 }
999}
1000
1001pub fn apply_subst_to_expr(expr: &ProofExpr, subst: &Substitution) -> ProofExpr {
1025 match expr {
1026 ProofExpr::Predicate { name, args, world } => ProofExpr::Predicate {
1027 name: name.clone(),
1028 args: args.iter().map(|a| apply_subst_to_term(a, subst)).collect(),
1029 world: world.clone(),
1030 },
1031 ProofExpr::Identity(l, r) => ProofExpr::Identity(
1032 apply_subst_to_term(l, subst),
1033 apply_subst_to_term(r, subst),
1034 ),
1035 ProofExpr::Atom(a) => ProofExpr::Atom(a.clone()),
1036 ProofExpr::And(l, r) => ProofExpr::And(
1037 Box::new(apply_subst_to_expr(l, subst)),
1038 Box::new(apply_subst_to_expr(r, subst)),
1039 ),
1040 ProofExpr::Or(l, r) => ProofExpr::Or(
1041 Box::new(apply_subst_to_expr(l, subst)),
1042 Box::new(apply_subst_to_expr(r, subst)),
1043 ),
1044 ProofExpr::Implies(l, r) => ProofExpr::Implies(
1045 Box::new(apply_subst_to_expr(l, subst)),
1046 Box::new(apply_subst_to_expr(r, subst)),
1047 ),
1048 ProofExpr::Iff(l, r) => ProofExpr::Iff(
1049 Box::new(apply_subst_to_expr(l, subst)),
1050 Box::new(apply_subst_to_expr(r, subst)),
1051 ),
1052 ProofExpr::Not(inner) => ProofExpr::Not(Box::new(apply_subst_to_expr(inner, subst))),
1053 ProofExpr::ForAll { variable, body } => {
1054 let new_variable = match subst.get(variable) {
1056 Some(ProofTerm::Variable(new_name)) => new_name.clone(),
1057 _ => variable.clone(),
1058 };
1059 ProofExpr::ForAll {
1060 variable: new_variable,
1061 body: Box::new(apply_subst_to_expr(body, subst)),
1062 }
1063 }
1064 ProofExpr::Exists { variable, body } => {
1065 let new_variable = match subst.get(variable) {
1067 Some(ProofTerm::Variable(new_name)) => new_name.clone(),
1068 _ => variable.clone(),
1069 };
1070 ProofExpr::Exists {
1071 variable: new_variable,
1072 body: Box::new(apply_subst_to_expr(body, subst)),
1073 }
1074 }
1075 ProofExpr::Modal { domain, force, flavor, body } => ProofExpr::Modal {
1076 domain: domain.clone(),
1077 force: *force,
1078 flavor: flavor.clone(),
1079 body: Box::new(apply_subst_to_expr(body, subst)),
1080 },
1081 ProofExpr::Temporal { operator, body } => ProofExpr::Temporal {
1082 operator: operator.clone(),
1083 body: Box::new(apply_subst_to_expr(body, subst)),
1084 },
1085 ProofExpr::TemporalBinary { operator, left, right } => ProofExpr::TemporalBinary {
1086 operator: operator.clone(),
1087 left: Box::new(apply_subst_to_expr(left, subst)),
1088 right: Box::new(apply_subst_to_expr(right, subst)),
1089 },
1090 ProofExpr::Lambda { variable, body } => {
1091 let new_variable = match subst.get(variable) {
1093 Some(ProofTerm::Variable(new_name)) => new_name.clone(),
1094 _ => variable.clone(),
1095 };
1096 ProofExpr::Lambda {
1097 variable: new_variable,
1098 body: Box::new(apply_subst_to_expr(body, subst)),
1099 }
1100 }
1101 ProofExpr::App(f, a) => ProofExpr::App(
1102 Box::new(apply_subst_to_expr(f, subst)),
1103 Box::new(apply_subst_to_expr(a, subst)),
1104 ),
1105 ProofExpr::NeoEvent { event_var, verb, roles } => ProofExpr::NeoEvent {
1106 event_var: event_var.clone(),
1107 verb: verb.clone(),
1108 roles: roles
1109 .iter()
1110 .map(|(r, t)| (r.clone(), apply_subst_to_term(t, subst)))
1111 .collect(),
1112 },
1113 ProofExpr::Ctor { name, args } => ProofExpr::Ctor {
1115 name: name.clone(),
1116 args: args.iter().map(|a| apply_subst_to_expr(a, subst)).collect(),
1117 },
1118 ProofExpr::Match { scrutinee, arms } => ProofExpr::Match {
1119 scrutinee: Box::new(apply_subst_to_expr(scrutinee, subst)),
1120 arms: arms
1121 .iter()
1122 .map(|arm| MatchArm {
1123 ctor: arm.ctor.clone(),
1124 bindings: arm.bindings.clone(),
1125 body: apply_subst_to_expr(&arm.body, subst),
1126 })
1127 .collect(),
1128 },
1129 ProofExpr::Fixpoint { name, body } => ProofExpr::Fixpoint {
1130 name: name.clone(),
1131 body: Box::new(apply_subst_to_expr(body, subst)),
1132 },
1133 ProofExpr::TypedVar { name, typename } => ProofExpr::TypedVar {
1134 name: name.clone(),
1135 typename: typename.clone(),
1136 },
1137 ProofExpr::Unsupported(s) => ProofExpr::Unsupported(s.clone()),
1138 ProofExpr::Hole(name) => ProofExpr::Hole(name.clone()),
1140 ProofExpr::Term(t) => ProofExpr::Term(apply_subst_to_term(t, subst)),
1142 }
1143}
1144
1145pub fn compose_substitutions(s1: Substitution, s2: Substitution) -> Substitution {
1178 let mut result: Substitution = s1
1179 .into_iter()
1180 .map(|(k, v)| (k, apply_subst_to_term(&v, &s2)))
1181 .collect();
1182
1183 for (k, v) in s2 {
1185 result.entry(k).or_insert(v);
1186 }
1187
1188 result
1189}
1190
1191pub fn unify_pattern(lhs: &ProofExpr, rhs: &ProofExpr) -> ProofResult<ExprSubstitution> {
1233 let mut solution = ExprSubstitution::new();
1234 unify_pattern_internal(lhs, rhs, &mut solution)?;
1235 Ok(solution)
1236}
1237
1238fn unify_pattern_internal(
1240 lhs: &ProofExpr,
1241 rhs: &ProofExpr,
1242 solution: &mut ExprSubstitution,
1243) -> ProofResult<()> {
1244 let lhs = beta_reduce(lhs);
1246 let rhs = beta_reduce(rhs);
1247
1248 match &lhs {
1249 ProofExpr::Hole(h) => {
1251 solution.insert(h.clone(), rhs.clone());
1252 Ok(())
1253 }
1254
1255 ProofExpr::App(_, _) => {
1257 let (head, args) = collect_app_args(&lhs);
1259
1260 if let ProofExpr::Hole(h) = head {
1261 let var_args = extract_distinct_vars(&args)?;
1263
1264 check_scope(&rhs, &var_args)?;
1266
1267 let renamed_rhs = rename_vars_to_bound(&rhs, &var_args);
1270 let lambda = build_lambda(var_args, renamed_rhs);
1271 solution.insert(h.clone(), lambda);
1272 Ok(())
1273 } else {
1274 if lhs == rhs {
1276 Ok(())
1277 } else {
1278 Err(ProofError::ExprUnificationFailed {
1279 left: lhs.clone(),
1280 right: rhs.clone(),
1281 })
1282 }
1283 }
1284 }
1285
1286 _ => {
1288 if lhs == rhs {
1289 Ok(())
1290 } else {
1291 Err(ProofError::ExprUnificationFailed {
1292 left: lhs.clone(),
1293 right: rhs.clone(),
1294 })
1295 }
1296 }
1297 }
1298}
1299
1300fn collect_app_args(expr: &ProofExpr) -> (ProofExpr, Vec<ProofExpr>) {
1302 let mut args = Vec::new();
1303 let mut current = expr.clone();
1304
1305 while let ProofExpr::App(func, arg) = current {
1306 args.push(*arg);
1307 current = *func;
1308 }
1309
1310 args.reverse();
1311 (current, args)
1312}
1313
1314fn extract_distinct_vars(args: &[ProofExpr]) -> ProofResult<Vec<String>> {
1317 let mut vars = Vec::new();
1318 for arg in args {
1319 match arg {
1320 ProofExpr::Term(ProofTerm::BoundVarRef(v)) => {
1321 if vars.contains(v) {
1322 return Err(ProofError::PatternNotDistinct(v.clone()));
1323 }
1324 vars.push(v.clone());
1325 }
1326 _ => return Err(ProofError::NotAPattern(arg.clone())),
1327 }
1328 }
1329 Ok(vars)
1330}
1331
1332fn check_scope(expr: &ProofExpr, allowed: &[String]) -> ProofResult<()> {
1334 let free_vars = collect_free_vars(expr);
1335 for var in free_vars {
1336 if !allowed.contains(&var) {
1337 return Err(ProofError::ScopeViolation {
1338 var,
1339 allowed: allowed.to_vec(),
1340 });
1341 }
1342 }
1343 Ok(())
1344}
1345
1346fn collect_free_vars(expr: &ProofExpr) -> Vec<String> {
1348 let mut vars = Vec::new();
1349 collect_free_vars_impl(expr, &mut vars, &mut Vec::new());
1350 vars
1351}
1352
1353fn collect_free_vars_impl(expr: &ProofExpr, vars: &mut Vec<String>, bound: &mut Vec<String>) {
1354 match expr {
1355 ProofExpr::Predicate { args, .. } => {
1356 for arg in args {
1357 collect_free_vars_term(arg, vars, bound);
1358 }
1359 }
1360 ProofExpr::Identity(l, r) => {
1361 collect_free_vars_term(l, vars, bound);
1362 collect_free_vars_term(r, vars, bound);
1363 }
1364 ProofExpr::Atom(s) => {
1365 if !bound.contains(s) && !vars.contains(s) {
1366 vars.push(s.clone());
1367 }
1368 }
1369 ProofExpr::And(l, r)
1370 | ProofExpr::Or(l, r)
1371 | ProofExpr::Implies(l, r)
1372 | ProofExpr::Iff(l, r) => {
1373 collect_free_vars_impl(l, vars, bound);
1374 collect_free_vars_impl(r, vars, bound);
1375 }
1376 ProofExpr::Not(inner) => collect_free_vars_impl(inner, vars, bound),
1377 ProofExpr::ForAll { variable, body }
1378 | ProofExpr::Exists { variable, body }
1379 | ProofExpr::Lambda { variable, body } => {
1380 bound.push(variable.clone());
1381 collect_free_vars_impl(body, vars, bound);
1382 bound.pop();
1383 }
1384 ProofExpr::App(f, a) => {
1385 collect_free_vars_impl(f, vars, bound);
1386 collect_free_vars_impl(a, vars, bound);
1387 }
1388 ProofExpr::Term(t) => collect_free_vars_term(t, vars, bound),
1389 ProofExpr::Hole(_) => {} _ => {} }
1392}
1393
1394fn collect_free_vars_term(term: &ProofTerm, vars: &mut Vec<String>, bound: &[String]) {
1395 match term {
1396 ProofTerm::Variable(v) => {
1397 if !bound.contains(v) && !vars.contains(v) {
1398 vars.push(v.clone());
1399 }
1400 }
1401 ProofTerm::Function(_, args) => {
1402 for arg in args {
1403 collect_free_vars_term(arg, vars, bound);
1404 }
1405 }
1406 ProofTerm::Group(terms) => {
1407 for t in terms {
1408 collect_free_vars_term(t, vars, bound);
1409 }
1410 }
1411 ProofTerm::Constant(_) | ProofTerm::BoundVarRef(_) => {}
1412 }
1413}
1414
1415fn rename_vars_to_bound(expr: &ProofExpr, bound_vars: &[String]) -> ProofExpr {
1418 expr.clone()
1422}
1423
1424fn build_lambda(vars: Vec<String>, body: ProofExpr) -> ProofExpr {
1426 vars.into_iter().rev().fold(body, |acc, var| {
1427 ProofExpr::Lambda {
1428 variable: var,
1429 body: Box::new(acc),
1430 }
1431 })
1432}
1433
1434#[cfg(test)]
1435mod tests {
1436 use super::*;
1437
1438 #[test]
1439 fn test_unify_same_constant() {
1440 let t1 = ProofTerm::Constant("a".into());
1441 let t2 = ProofTerm::Constant("a".into());
1442 let result = unify_terms(&t1, &t2);
1443 assert!(result.is_ok());
1444 assert!(result.unwrap().is_empty());
1445 }
1446
1447 #[test]
1448 fn test_unify_different_constants() {
1449 let t1 = ProofTerm::Constant("a".into());
1450 let t2 = ProofTerm::Constant("b".into());
1451 let result = unify_terms(&t1, &t2);
1452 assert!(result.is_err());
1453 }
1454
1455 #[test]
1456 fn test_unify_var_constant() {
1457 let t1 = ProofTerm::Variable("x".into());
1458 let t2 = ProofTerm::Constant("a".into());
1459 let result = unify_terms(&t1, &t2);
1460 assert!(result.is_ok());
1461 let subst = result.unwrap();
1462 assert_eq!(subst.get("x"), Some(&ProofTerm::Constant("a".into())));
1463 }
1464
1465 #[test]
1466 fn test_occurs_check() {
1467 let t1 = ProofTerm::Variable("x".into());
1468 let t2 = ProofTerm::Function("f".into(), vec![ProofTerm::Variable("x".into())]);
1469 let result = unify_terms(&t1, &t2);
1470 assert!(matches!(result, Err(ProofError::OccursCheck { .. })));
1471 }
1472
1473 #[test]
1474 fn test_compose_substitutions() {
1475 let mut s1 = Substitution::new();
1476 s1.insert("x".into(), ProofTerm::Variable("y".into()));
1477
1478 let mut s2 = Substitution::new();
1479 s2.insert("y".into(), ProofTerm::Constant("a".into()));
1480
1481 let composed = compose_substitutions(s1, s2);
1482
1483 assert_eq!(composed.get("x"), Some(&ProofTerm::Constant("a".into())));
1485 assert_eq!(composed.get("y"), Some(&ProofTerm::Constant("a".into())));
1487 }
1488
1489 #[test]
1494 fn test_alpha_equivalence_exists() {
1495 let e1 = ProofExpr::Exists {
1497 variable: "e".to_string(),
1498 body: Box::new(ProofExpr::Predicate {
1499 name: "run".to_string(),
1500 args: vec![ProofTerm::Variable("e".to_string())],
1501 world: None,
1502 }),
1503 };
1504
1505 let e2 = ProofExpr::Exists {
1506 variable: "x".to_string(),
1507 body: Box::new(ProofExpr::Predicate {
1508 name: "run".to_string(),
1509 args: vec![ProofTerm::Variable("x".to_string())],
1510 world: None,
1511 }),
1512 };
1513
1514 let result = unify_exprs(&e1, &e2);
1515 assert!(
1516 result.is_ok(),
1517 "Alpha-equivalent expressions should unify: {:?}",
1518 result
1519 );
1520 }
1521
1522 #[test]
1523 fn test_alpha_equivalence_forall() {
1524 let e1 = ProofExpr::ForAll {
1526 variable: "x".to_string(),
1527 body: Box::new(ProofExpr::Predicate {
1528 name: "mortal".to_string(),
1529 args: vec![ProofTerm::Variable("x".to_string())],
1530 world: None,
1531 }),
1532 };
1533
1534 let e2 = ProofExpr::ForAll {
1535 variable: "y".to_string(),
1536 body: Box::new(ProofExpr::Predicate {
1537 name: "mortal".to_string(),
1538 args: vec![ProofTerm::Variable("y".to_string())],
1539 world: None,
1540 }),
1541 };
1542
1543 let result = unify_exprs(&e1, &e2);
1544 assert!(
1545 result.is_ok(),
1546 "Alpha-equivalent universals should unify: {:?}",
1547 result
1548 );
1549 }
1550
1551 #[test]
1552 fn test_alpha_equivalence_nested() {
1553 let e1 = ProofExpr::Exists {
1555 variable: "e".to_string(),
1556 body: Box::new(ProofExpr::And(
1557 Box::new(ProofExpr::Predicate {
1558 name: "run".to_string(),
1559 args: vec![ProofTerm::Variable("e".to_string())],
1560 world: None,
1561 }),
1562 Box::new(ProofExpr::Predicate {
1563 name: "agent".to_string(),
1564 args: vec![
1565 ProofTerm::Variable("e".to_string()),
1566 ProofTerm::Constant("John".to_string()),
1567 ],
1568 world: None,
1569 }),
1570 )),
1571 };
1572
1573 let e2 = ProofExpr::Exists {
1574 variable: "x".to_string(),
1575 body: Box::new(ProofExpr::And(
1576 Box::new(ProofExpr::Predicate {
1577 name: "run".to_string(),
1578 args: vec![ProofTerm::Variable("x".to_string())],
1579 world: None,
1580 }),
1581 Box::new(ProofExpr::Predicate {
1582 name: "agent".to_string(),
1583 args: vec![
1584 ProofTerm::Variable("x".to_string()),
1585 ProofTerm::Constant("John".to_string()),
1586 ],
1587 world: None,
1588 }),
1589 )),
1590 };
1591
1592 let result = unify_exprs(&e1, &e2);
1593 assert!(
1594 result.is_ok(),
1595 "Nested alpha-equivalent expressions should unify: {:?}",
1596 result
1597 );
1598 }
1599}