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::Counterfactual { antecedent, consequent } => ProofExpr::Counterfactual {
218 antecedent: Box::new(beta_reduce(antecedent)),
219 consequent: Box::new(beta_reduce(consequent)),
220 },
221 ProofExpr::Temporal { operator, body } => ProofExpr::Temporal {
222 operator: operator.clone(),
223 body: Box::new(beta_reduce(body)),
224 },
225 ProofExpr::TemporalBinary { operator, left, right } => ProofExpr::TemporalBinary {
226 operator: operator.clone(),
227 left: Box::new(beta_reduce(left)),
228 right: Box::new(beta_reduce(right)),
229 },
230
231 ProofExpr::Ctor { name, args } => ProofExpr::Ctor {
233 name: name.clone(),
234 args: args.iter().map(beta_reduce).collect(),
235 },
236
237 ProofExpr::Match { scrutinee, arms } => {
239 let reduced_scrutinee = beta_reduce(scrutinee);
240
241 if let ProofExpr::Ctor { name: ctor_name, args: ctor_args } = &reduced_scrutinee {
243 for arm in arms {
244 if &arm.ctor == ctor_name {
245 let mut result = arm.body.clone();
247 for (binding, arg) in arm.bindings.iter().zip(ctor_args.iter()) {
248 result = substitute_expr_for_var(&result, binding, arg);
249 }
250 return beta_reduce(&result);
252 }
253 }
254 }
255
256 ProofExpr::Match {
258 scrutinee: Box::new(reduced_scrutinee),
259 arms: arms.iter().map(|arm| MatchArm {
260 ctor: arm.ctor.clone(),
261 bindings: arm.bindings.clone(),
262 body: beta_reduce(&arm.body),
263 }).collect(),
264 }
265 }
266
267 ProofExpr::Fixpoint { name, body } => ProofExpr::Fixpoint {
269 name: name.clone(),
270 body: Box::new(beta_reduce(body)),
271 },
272
273 ProofExpr::Predicate { .. }
275 | ProofExpr::Identity(_, _)
276 | ProofExpr::Atom(_)
277 | ProofExpr::NeoEvent { .. }
278 | ProofExpr::TypedVar { .. }
279 | ProofExpr::Unsupported(_)
280 | ProofExpr::Hole(_)
281 | ProofExpr::Term(_) => expr.clone(),
282 }
283}
284
285fn free_vars_expr(expr: &ProofExpr, bound: &mut Vec<String>, acc: &mut std::collections::HashSet<String>) {
291 match expr {
292 ProofExpr::Atom(s) => {
293 if !bound.iter().any(|b| b == s) {
294 acc.insert(s.clone());
295 }
296 }
297 ProofExpr::Predicate { args, .. } => {
298 for a in args {
299 free_vars_term(a, bound, acc);
300 }
301 }
302 ProofExpr::Identity(l, r) => {
303 free_vars_term(l, bound, acc);
304 free_vars_term(r, bound, acc);
305 }
306 ProofExpr::And(l, r)
307 | ProofExpr::Or(l, r)
308 | ProofExpr::Implies(l, r)
309 | ProofExpr::Iff(l, r) => {
310 free_vars_expr(l, bound, acc);
311 free_vars_expr(r, bound, acc);
312 }
313 ProofExpr::Not(i) => free_vars_expr(i, bound, acc),
314 ProofExpr::ForAll { variable, body }
315 | ProofExpr::Exists { variable, body }
316 | ProofExpr::Lambda { variable, body } => {
317 bound.push(variable.clone());
318 free_vars_expr(body, bound, acc);
319 bound.pop();
320 }
321 ProofExpr::Modal { body, .. } => free_vars_expr(body, bound, acc),
322 ProofExpr::Counterfactual { antecedent, consequent } => {
323 free_vars_expr(antecedent, bound, acc);
324 free_vars_expr(consequent, bound, acc);
325 }
326 ProofExpr::Temporal { body, .. } => free_vars_expr(body, bound, acc),
327 ProofExpr::TemporalBinary { left, right, .. } => {
328 free_vars_expr(left, bound, acc);
329 free_vars_expr(right, bound, acc);
330 }
331 ProofExpr::App(f, a) => {
332 free_vars_expr(f, bound, acc);
333 free_vars_expr(a, bound, acc);
334 }
335 ProofExpr::NeoEvent { event_var, roles, .. } => {
336 bound.push(event_var.clone());
337 for (_, t) in roles {
338 free_vars_term(t, bound, acc);
339 }
340 bound.pop();
341 }
342 ProofExpr::Ctor { args, .. } => {
343 for a in args {
344 free_vars_expr(a, bound, acc);
345 }
346 }
347 ProofExpr::Match { scrutinee, arms } => {
348 free_vars_expr(scrutinee, bound, acc);
349 for arm in arms {
350 let depth = arm.bindings.len();
351 for b in &arm.bindings {
352 bound.push(b.clone());
353 }
354 free_vars_expr(&arm.body, bound, acc);
355 for _ in 0..depth {
356 bound.pop();
357 }
358 }
359 }
360 ProofExpr::Fixpoint { name, body } => {
361 bound.push(name.clone());
362 free_vars_expr(body, bound, acc);
363 bound.pop();
364 }
365 ProofExpr::TypedVar { name, .. } => {
366 if !bound.iter().any(|b| b == name) {
367 acc.insert(name.clone());
368 }
369 }
370 ProofExpr::Hole(_) | ProofExpr::Unsupported(_) => {}
371 ProofExpr::Term(t) => free_vars_term(t, bound, acc),
372 }
373}
374
375fn free_vars_term(term: &ProofTerm, bound: &[String], acc: &mut std::collections::HashSet<String>) {
376 match term {
377 ProofTerm::Variable(s) | ProofTerm::BoundVarRef(s) => {
378 if !bound.iter().any(|b| b == s) {
379 acc.insert(s.clone());
380 }
381 }
382 ProofTerm::Constant(_) => {}
383 ProofTerm::Function(_, args) | ProofTerm::Group(args) => {
384 for a in args {
385 free_vars_term(a, bound, acc);
386 }
387 }
388 }
389}
390
391fn all_names_expr(expr: &ProofExpr, acc: &mut std::collections::HashSet<String>) {
394 match expr {
395 ProofExpr::Atom(s) => {
396 acc.insert(s.clone());
397 }
398 ProofExpr::Predicate { args, .. } => {
399 for a in args {
400 all_names_term(a, acc);
401 }
402 }
403 ProofExpr::Identity(l, r) => {
404 all_names_term(l, acc);
405 all_names_term(r, acc);
406 }
407 ProofExpr::And(l, r)
408 | ProofExpr::Or(l, r)
409 | ProofExpr::Implies(l, r)
410 | ProofExpr::Iff(l, r) => {
411 all_names_expr(l, acc);
412 all_names_expr(r, acc);
413 }
414 ProofExpr::Not(i) => all_names_expr(i, acc),
415 ProofExpr::ForAll { variable, body }
416 | ProofExpr::Exists { variable, body }
417 | ProofExpr::Lambda { variable, body } => {
418 acc.insert(variable.clone());
419 all_names_expr(body, acc);
420 }
421 ProofExpr::Modal { body, .. } => all_names_expr(body, acc),
422 ProofExpr::Counterfactual { antecedent, consequent } => {
423 all_names_expr(antecedent, acc);
424 all_names_expr(consequent, acc);
425 }
426 ProofExpr::Temporal { body, .. } => all_names_expr(body, acc),
427 ProofExpr::TemporalBinary { left, right, .. } => {
428 all_names_expr(left, acc);
429 all_names_expr(right, acc);
430 }
431 ProofExpr::App(f, a) => {
432 all_names_expr(f, acc);
433 all_names_expr(a, acc);
434 }
435 ProofExpr::NeoEvent { event_var, roles, .. } => {
436 acc.insert(event_var.clone());
437 for (_, t) in roles {
438 all_names_term(t, acc);
439 }
440 }
441 ProofExpr::Ctor { args, .. } => {
442 for a in args {
443 all_names_expr(a, acc);
444 }
445 }
446 ProofExpr::Match { scrutinee, arms } => {
447 all_names_expr(scrutinee, acc);
448 for arm in arms {
449 for b in &arm.bindings {
450 acc.insert(b.clone());
451 }
452 all_names_expr(&arm.body, acc);
453 }
454 }
455 ProofExpr::Fixpoint { name, body } => {
456 acc.insert(name.clone());
457 all_names_expr(body, acc);
458 }
459 ProofExpr::TypedVar { name, .. } => {
460 acc.insert(name.clone());
461 }
462 ProofExpr::Hole(_) | ProofExpr::Unsupported(_) => {}
463 ProofExpr::Term(t) => all_names_term(t, acc),
464 }
465}
466
467fn all_names_term(term: &ProofTerm, acc: &mut std::collections::HashSet<String>) {
468 match term {
469 ProofTerm::Constant(s) | ProofTerm::Variable(s) | ProofTerm::BoundVarRef(s) => {
470 acc.insert(s.clone());
471 }
472 ProofTerm::Function(_, args) | ProofTerm::Group(args) => {
473 for a in args {
474 all_names_term(a, acc);
475 }
476 }
477 }
478}
479
480fn fresh_proof_name(base: &str, avoid: &std::collections::HashSet<String>) -> String {
482 let mut candidate = format!("{}'", base);
483 let mut n: u32 = 0;
484 while avoid.contains(&candidate) {
485 n += 1;
486 candidate = format!("{}'{}", base, n);
487 }
488 candidate
489}
490
491fn alpha_rename_expr(expr: &ProofExpr, from: &str, to: &str) -> ProofExpr {
496 match expr {
497 ProofExpr::Atom(s) if s == from => ProofExpr::Atom(to.to_string()),
498 ProofExpr::Atom(s) => ProofExpr::Atom(s.clone()),
499 ProofExpr::Predicate { name, args, world } => ProofExpr::Predicate {
500 name: name.clone(),
501 args: args.iter().map(|a| alpha_rename_term(a, from, to)).collect(),
502 world: world.clone(),
503 },
504 ProofExpr::Identity(l, r) => ProofExpr::Identity(
505 alpha_rename_term(l, from, to),
506 alpha_rename_term(r, from, to),
507 ),
508 ProofExpr::And(l, r) => ProofExpr::And(
509 Box::new(alpha_rename_expr(l, from, to)),
510 Box::new(alpha_rename_expr(r, from, to)),
511 ),
512 ProofExpr::Or(l, r) => ProofExpr::Or(
513 Box::new(alpha_rename_expr(l, from, to)),
514 Box::new(alpha_rename_expr(r, from, to)),
515 ),
516 ProofExpr::Implies(l, r) => ProofExpr::Implies(
517 Box::new(alpha_rename_expr(l, from, to)),
518 Box::new(alpha_rename_expr(r, from, to)),
519 ),
520 ProofExpr::Iff(l, r) => ProofExpr::Iff(
521 Box::new(alpha_rename_expr(l, from, to)),
522 Box::new(alpha_rename_expr(r, from, to)),
523 ),
524 ProofExpr::Not(i) => ProofExpr::Not(Box::new(alpha_rename_expr(i, from, to))),
525 ProofExpr::ForAll { variable, body } => {
526 if variable == from {
527 expr.clone()
528 } else {
529 ProofExpr::ForAll {
530 variable: variable.clone(),
531 body: Box::new(alpha_rename_expr(body, from, to)),
532 }
533 }
534 }
535 ProofExpr::Exists { variable, body } => {
536 if variable == from {
537 expr.clone()
538 } else {
539 ProofExpr::Exists {
540 variable: variable.clone(),
541 body: Box::new(alpha_rename_expr(body, from, to)),
542 }
543 }
544 }
545 ProofExpr::Lambda { variable, body } => {
546 if variable == from {
547 expr.clone()
548 } else {
549 ProofExpr::Lambda {
550 variable: variable.clone(),
551 body: Box::new(alpha_rename_expr(body, from, to)),
552 }
553 }
554 }
555 ProofExpr::Modal { domain, force, flavor, body } => ProofExpr::Modal {
556 domain: domain.clone(),
557 force: *force,
558 flavor: flavor.clone(),
559 body: Box::new(alpha_rename_expr(body, from, to)),
560 },
561 ProofExpr::Counterfactual { antecedent, consequent } => ProofExpr::Counterfactual {
562 antecedent: Box::new(alpha_rename_expr(antecedent, from, to)),
563 consequent: Box::new(alpha_rename_expr(consequent, from, to)),
564 },
565 ProofExpr::Temporal { operator, body } => ProofExpr::Temporal {
566 operator: operator.clone(),
567 body: Box::new(alpha_rename_expr(body, from, to)),
568 },
569 ProofExpr::TemporalBinary { operator, left, right } => ProofExpr::TemporalBinary {
570 operator: operator.clone(),
571 left: Box::new(alpha_rename_expr(left, from, to)),
572 right: Box::new(alpha_rename_expr(right, from, to)),
573 },
574 ProofExpr::App(f, a) => ProofExpr::App(
575 Box::new(alpha_rename_expr(f, from, to)),
576 Box::new(alpha_rename_expr(a, from, to)),
577 ),
578 ProofExpr::NeoEvent { event_var, verb, roles } => {
579 if event_var == from {
580 expr.clone()
581 } else {
582 ProofExpr::NeoEvent {
583 event_var: event_var.clone(),
584 verb: verb.clone(),
585 roles: roles.iter().map(|(r, t)| (r.clone(), alpha_rename_term(t, from, to))).collect(),
586 }
587 }
588 }
589 ProofExpr::Ctor { name, args } => ProofExpr::Ctor {
590 name: name.clone(),
591 args: args.iter().map(|a| alpha_rename_expr(a, from, to)).collect(),
592 },
593 ProofExpr::Match { scrutinee, arms } => ProofExpr::Match {
594 scrutinee: Box::new(alpha_rename_expr(scrutinee, from, to)),
595 arms: arms.iter().map(|arm| {
596 if arm.bindings.iter().any(|b| b == from) {
597 arm.clone()
598 } else {
599 MatchArm {
600 ctor: arm.ctor.clone(),
601 bindings: arm.bindings.clone(),
602 body: alpha_rename_expr(&arm.body, from, to),
603 }
604 }
605 }).collect(),
606 },
607 ProofExpr::Fixpoint { name, body } => {
608 if name == from {
609 expr.clone()
610 } else {
611 ProofExpr::Fixpoint {
612 name: name.clone(),
613 body: Box::new(alpha_rename_expr(body, from, to)),
614 }
615 }
616 }
617 ProofExpr::TypedVar { name, typename } => {
618 if name == from {
619 ProofExpr::TypedVar { name: to.to_string(), typename: typename.clone() }
620 } else {
621 expr.clone()
622 }
623 }
624 ProofExpr::Hole(_) | ProofExpr::Unsupported(_) => expr.clone(),
625 ProofExpr::Term(t) => ProofExpr::Term(alpha_rename_term(t, from, to)),
626 }
627}
628
629fn alpha_rename_term(term: &ProofTerm, from: &str, to: &str) -> ProofTerm {
630 match term {
631 ProofTerm::Variable(s) if s == from => ProofTerm::Variable(to.to_string()),
632 ProofTerm::BoundVarRef(s) if s == from => ProofTerm::BoundVarRef(to.to_string()),
633 ProofTerm::Variable(s) => ProofTerm::Variable(s.clone()),
634 ProofTerm::BoundVarRef(s) => ProofTerm::BoundVarRef(s.clone()),
635 ProofTerm::Constant(s) => ProofTerm::Constant(s.clone()),
636 ProofTerm::Function(n, args) => {
637 ProofTerm::Function(n.clone(), args.iter().map(|a| alpha_rename_term(a, from, to)).collect())
638 }
639 ProofTerm::Group(args) => {
640 ProofTerm::Group(args.iter().map(|a| alpha_rename_term(a, from, to)).collect())
641 }
642 }
643}
644
645fn rebind_for_subst(
649 variable: &str,
650 inner: &ProofExpr,
651 repl_fvs: &std::collections::HashSet<String>,
652) -> (String, ProofExpr) {
653 if repl_fvs.contains(variable) {
654 let mut avoid = repl_fvs.clone();
655 all_names_expr(inner, &mut avoid);
656 let fresh = fresh_proof_name(variable, &avoid);
657 let renamed = alpha_rename_expr(inner, variable, &fresh);
658 (fresh, renamed)
659 } else {
660 (variable.to_string(), inner.clone())
661 }
662}
663
664fn substitute_expr_for_var(body: &ProofExpr, var: &str, replacement: &ProofExpr) -> ProofExpr {
672 let mut repl_fvs = std::collections::HashSet::new();
673 free_vars_expr(replacement, &mut Vec::new(), &mut repl_fvs);
674 subst_expr_avoiding(body, var, replacement, &repl_fvs)
675}
676
677fn subst_expr_avoiding(
678 body: &ProofExpr,
679 var: &str,
680 replacement: &ProofExpr,
681 repl_fvs: &std::collections::HashSet<String>,
682) -> ProofExpr {
683 match body {
684 ProofExpr::Predicate { name, args, world } => ProofExpr::Predicate {
685 name: name.clone(),
686 args: args.iter().map(|t| substitute_term_for_var(t, var, replacement)).collect(),
687 world: world.clone(),
688 },
689
690 ProofExpr::Identity(l, r) => ProofExpr::Identity(
691 substitute_term_for_var(l, var, replacement),
692 substitute_term_for_var(r, var, replacement),
693 ),
694
695 ProofExpr::Atom(a) => {
696 if a == var {
698 replacement.clone()
699 } else {
700 ProofExpr::Atom(a.clone())
701 }
702 }
703
704 ProofExpr::And(l, r) => ProofExpr::And(
705 Box::new(subst_expr_avoiding(l, var, replacement, repl_fvs)),
706 Box::new(subst_expr_avoiding(r, var, replacement, repl_fvs)),
707 ),
708 ProofExpr::Or(l, r) => ProofExpr::Or(
709 Box::new(subst_expr_avoiding(l, var, replacement, repl_fvs)),
710 Box::new(subst_expr_avoiding(r, var, replacement, repl_fvs)),
711 ),
712 ProofExpr::Implies(l, r) => ProofExpr::Implies(
713 Box::new(subst_expr_avoiding(l, var, replacement, repl_fvs)),
714 Box::new(subst_expr_avoiding(r, var, replacement, repl_fvs)),
715 ),
716 ProofExpr::Iff(l, r) => ProofExpr::Iff(
717 Box::new(subst_expr_avoiding(l, var, replacement, repl_fvs)),
718 Box::new(subst_expr_avoiding(r, var, replacement, repl_fvs)),
719 ),
720 ProofExpr::Not(inner) => ProofExpr::Not(
721 Box::new(subst_expr_avoiding(inner, var, replacement, repl_fvs))
722 ),
723
724 ProofExpr::ForAll { variable, body: inner } => {
727 if variable == var {
728 body.clone()
729 } else {
730 let (v, b) = rebind_for_subst(variable, inner, repl_fvs);
731 ProofExpr::ForAll {
732 variable: v,
733 body: Box::new(subst_expr_avoiding(&b, var, replacement, repl_fvs)),
734 }
735 }
736 }
737 ProofExpr::Exists { variable, body: inner } => {
738 if variable == var {
739 body.clone()
740 } else {
741 let (v, b) = rebind_for_subst(variable, inner, repl_fvs);
742 ProofExpr::Exists {
743 variable: v,
744 body: Box::new(subst_expr_avoiding(&b, var, replacement, repl_fvs)),
745 }
746 }
747 }
748
749 ProofExpr::Lambda { variable, body: inner } => {
750 if variable == var {
751 body.clone()
752 } else {
753 let (v, b) = rebind_for_subst(variable, inner, repl_fvs);
754 ProofExpr::Lambda {
755 variable: v,
756 body: Box::new(subst_expr_avoiding(&b, var, replacement, repl_fvs)),
757 }
758 }
759 }
760
761 ProofExpr::App(f, a) => ProofExpr::App(
762 Box::new(subst_expr_avoiding(f, var, replacement, repl_fvs)),
763 Box::new(subst_expr_avoiding(a, var, replacement, repl_fvs)),
764 ),
765
766 ProofExpr::Modal { domain, force, flavor, body: inner } => ProofExpr::Modal {
767 domain: domain.clone(),
768 force: *force,
769 flavor: flavor.clone(),
770 body: Box::new(subst_expr_avoiding(inner, var, replacement, repl_fvs)),
771 },
772
773 ProofExpr::Counterfactual { antecedent, consequent } => ProofExpr::Counterfactual {
774 antecedent: Box::new(subst_expr_avoiding(antecedent, var, replacement, repl_fvs)),
775 consequent: Box::new(subst_expr_avoiding(consequent, var, replacement, repl_fvs)),
776 },
777
778 ProofExpr::Temporal { operator, body: inner } => ProofExpr::Temporal {
779 operator: operator.clone(),
780 body: Box::new(subst_expr_avoiding(inner, var, replacement, repl_fvs)),
781 },
782
783 ProofExpr::TemporalBinary { operator, left, right } => ProofExpr::TemporalBinary {
784 operator: operator.clone(),
785 left: Box::new(subst_expr_avoiding(left, var, replacement, repl_fvs)),
786 right: Box::new(subst_expr_avoiding(right, var, replacement, repl_fvs)),
787 },
788
789 ProofExpr::NeoEvent { event_var, verb, roles } => {
790 if event_var == var {
791 body.clone()
793 } else if repl_fvs.contains(event_var) {
794 let mut avoid = repl_fvs.clone();
796 for (_, t) in roles {
797 all_names_term(t, &mut avoid);
798 }
799 let fresh = fresh_proof_name(event_var, &avoid);
800 ProofExpr::NeoEvent {
801 event_var: fresh.clone(),
802 verb: verb.clone(),
803 roles: roles
804 .iter()
805 .map(|(r, t)| {
806 let renamed = alpha_rename_term(t, event_var, &fresh);
807 (r.clone(), substitute_term_for_var(&renamed, var, replacement))
808 })
809 .collect(),
810 }
811 } else {
812 ProofExpr::NeoEvent {
813 event_var: event_var.clone(),
814 verb: verb.clone(),
815 roles: roles
816 .iter()
817 .map(|(r, t)| (r.clone(), substitute_term_for_var(t, var, replacement)))
818 .collect(),
819 }
820 }
821 }
822
823 ProofExpr::Ctor { name, args } => ProofExpr::Ctor {
824 name: name.clone(),
825 args: args.iter().map(|a| subst_expr_avoiding(a, var, replacement, repl_fvs)).collect(),
826 },
827
828 ProofExpr::Match { scrutinee, arms } => ProofExpr::Match {
829 scrutinee: Box::new(subst_expr_avoiding(scrutinee, var, replacement, repl_fvs)),
830 arms: arms.iter().map(|arm| {
831 if arm.bindings.iter().any(|b| b == var) {
833 arm.clone()
834 } else {
835 let mut arm_body = arm.body.clone();
838 let mut new_bindings = arm.bindings.clone();
839 let mut avoid = repl_fvs.clone();
840 all_names_expr(&arm_body, &mut avoid);
841 for b in &arm.bindings {
842 avoid.insert(b.clone());
843 }
844 for binding in new_bindings.iter_mut() {
845 if repl_fvs.contains(binding) {
846 let fresh = fresh_proof_name(binding, &avoid);
847 arm_body = alpha_rename_expr(&arm_body, binding, &fresh);
848 avoid.insert(fresh.clone());
849 *binding = fresh;
850 }
851 }
852 MatchArm {
853 ctor: arm.ctor.clone(),
854 bindings: new_bindings,
855 body: subst_expr_avoiding(&arm_body, var, replacement, repl_fvs),
856 }
857 }
858 }).collect(),
859 },
860
861 ProofExpr::Fixpoint { name, body: inner } => {
862 if name == var {
863 body.clone()
864 } else {
865 let (v, b) = rebind_for_subst(name, inner, repl_fvs);
866 ProofExpr::Fixpoint {
867 name: v,
868 body: Box::new(subst_expr_avoiding(&b, var, replacement, repl_fvs)),
869 }
870 }
871 }
872
873 ProofExpr::TypedVar { .. } | ProofExpr::Unsupported(_) => body.clone(),
874
875 ProofExpr::Hole(_) => body.clone(),
877
878 ProofExpr::Term(t) => ProofExpr::Term(substitute_term_for_var(t, var, replacement)),
880 }
881}
882
883fn substitute_term_for_var(term: &ProofTerm, var: &str, replacement: &ProofExpr) -> ProofTerm {
887 match term {
888 ProofTerm::Variable(v) if v == var => {
889 expr_to_term(replacement)
891 }
892 ProofTerm::BoundVarRef(v) if v == var => {
894 expr_to_term(replacement)
895 }
896 ProofTerm::Variable(_) | ProofTerm::Constant(_) | ProofTerm::BoundVarRef(_) => term.clone(),
897 ProofTerm::Function(name, args) => ProofTerm::Function(
898 name.clone(),
899 args.iter().map(|a| substitute_term_for_var(a, var, replacement)).collect(),
900 ),
901 ProofTerm::Group(terms) => ProofTerm::Group(
902 terms.iter().map(|t| substitute_term_for_var(t, var, replacement)).collect(),
903 ),
904 }
905}
906
907fn expr_to_term(expr: &ProofExpr) -> ProofTerm {
912 match expr {
913 ProofExpr::Atom(s) => ProofTerm::Constant(s.clone()),
915
916 ProofExpr::Predicate { name, args, .. } if args.is_empty() => {
918 ProofTerm::Constant(name.clone())
919 }
920
921 ProofExpr::Predicate { name, args, .. } => {
923 ProofTerm::Function(name.clone(), args.clone())
924 }
925
926 ProofExpr::Ctor { name, args } => {
928 ProofTerm::Function(name.clone(), args.iter().map(expr_to_term).collect())
929 }
930
931 ProofExpr::TypedVar { name, .. } => ProofTerm::Variable(name.clone()),
933
934 ProofExpr::Term(t) => t.clone(),
936
937 _ => ProofTerm::Constant(format!("{}", expr)),
939 }
940}
941
942pub fn unify_terms(t1: &ProofTerm, t2: &ProofTerm) -> ProofResult<Substitution> {
989 let mut subst = Substitution::new();
990 unify_terms_with_subst(t1, t2, &mut subst)?;
991 Ok(subst)
992}
993
994fn unify_terms_with_subst(
996 t1: &ProofTerm,
997 t2: &ProofTerm,
998 subst: &mut Substitution,
999) -> ProofResult<()> {
1000 let t1 = apply_subst_to_term(t1, subst);
1002 let t2 = apply_subst_to_term(t2, subst);
1003
1004 match (&t1, &t2) {
1005 (ProofTerm::Constant(c1), ProofTerm::Constant(c2)) if c1 == c2 => Ok(()),
1007
1008 (ProofTerm::Constant(c1), ProofTerm::Constant(c2)) => {
1010 Err(ProofError::SymbolMismatch {
1011 left: c1.clone(),
1012 right: c2.clone(),
1013 })
1014 }
1015
1016 (ProofTerm::Variable(v), t) => {
1018 if let ProofTerm::Variable(v2) = t {
1020 if v == v2 {
1021 return Ok(());
1022 }
1023 }
1024 if occurs(v, t) {
1026 return Err(ProofError::OccursCheck {
1027 variable: v.clone(),
1028 term: t.clone(),
1029 });
1030 }
1031 subst.insert(v.clone(), t.clone());
1032 Ok(())
1033 }
1034
1035 (t, ProofTerm::Variable(v)) => {
1037 if occurs(v, t) {
1039 return Err(ProofError::OccursCheck {
1040 variable: v.clone(),
1041 term: t.clone(),
1042 });
1043 }
1044 subst.insert(v.clone(), t.clone());
1045 Ok(())
1046 }
1047
1048 (ProofTerm::BoundVarRef(v), t) => {
1051 if let ProofTerm::BoundVarRef(v2) = t {
1052 if v == v2 {
1053 return Ok(());
1054 }
1055 }
1056 if occurs(v, t) {
1057 return Err(ProofError::OccursCheck {
1058 variable: v.clone(),
1059 term: t.clone(),
1060 });
1061 }
1062 subst.insert(v.clone(), t.clone());
1063 Ok(())
1064 }
1065
1066 (t, ProofTerm::BoundVarRef(v)) => {
1068 if occurs(v, t) {
1069 return Err(ProofError::OccursCheck {
1070 variable: v.clone(),
1071 term: t.clone(),
1072 });
1073 }
1074 subst.insert(v.clone(), t.clone());
1075 Ok(())
1076 }
1077
1078 (ProofTerm::Function(f1, args1), ProofTerm::Function(f2, args2)) => {
1080 if f1 != f2 {
1081 return Err(ProofError::SymbolMismatch {
1082 left: f1.clone(),
1083 right: f2.clone(),
1084 });
1085 }
1086 if args1.len() != args2.len() {
1087 return Err(ProofError::ArityMismatch {
1088 expected: args1.len(),
1089 found: args2.len(),
1090 });
1091 }
1092 for (a1, a2) in args1.iter().zip(args2.iter()) {
1093 unify_terms_with_subst(a1, a2, subst)?;
1094 }
1095 Ok(())
1096 }
1097
1098 (ProofTerm::Group(g1), ProofTerm::Group(g2)) => {
1100 if g1.len() != g2.len() {
1101 return Err(ProofError::ArityMismatch {
1102 expected: g1.len(),
1103 found: g2.len(),
1104 });
1105 }
1106 for (t1, t2) in g1.iter().zip(g2.iter()) {
1107 unify_terms_with_subst(t1, t2, subst)?;
1108 }
1109 Ok(())
1110 }
1111
1112 _ => Err(ProofError::UnificationFailed {
1114 left: t1,
1115 right: t2,
1116 }),
1117 }
1118}
1119
1120fn occurs(var: &str, term: &ProofTerm) -> bool {
1123 match term {
1124 ProofTerm::Variable(v) => v == var,
1125 ProofTerm::BoundVarRef(v) => v == var, ProofTerm::Constant(_) => false,
1127 ProofTerm::Function(_, args) => args.iter().any(|a| occurs(var, a)),
1128 ProofTerm::Group(terms) => terms.iter().any(|t| occurs(var, t)),
1129 }
1130}
1131
1132pub fn apply_subst_to_term(term: &ProofTerm, subst: &Substitution) -> ProofTerm {
1164 match term {
1165 ProofTerm::Variable(v) => {
1166 if let Some(replacement) = subst.get(v) {
1167 apply_subst_to_term(replacement, subst)
1169 } else {
1170 term.clone()
1171 }
1172 }
1173 ProofTerm::BoundVarRef(v) => {
1175 if let Some(replacement) = subst.get(v) {
1176 apply_subst_to_term(replacement, subst)
1177 } else {
1178 term.clone()
1179 }
1180 }
1181 ProofTerm::Constant(_) => term.clone(),
1182 ProofTerm::Function(name, args) => {
1183 let new_args = args.iter().map(|a| apply_subst_to_term(a, subst)).collect();
1184 ProofTerm::Function(name.clone(), new_args)
1185 }
1186 ProofTerm::Group(terms) => {
1187 let new_terms = terms.iter().map(|t| apply_subst_to_term(t, subst)).collect();
1188 ProofTerm::Group(new_terms)
1189 }
1190 }
1191}
1192
1193pub fn unify_exprs(e1: &ProofExpr, e2: &ProofExpr) -> ProofResult<Substitution> {
1232 let mut subst = Substitution::new();
1233 unify_exprs_with_subst(e1, e2, &mut subst)?;
1234 Ok(subst)
1235}
1236
1237fn unify_exprs_with_subst(
1239 e1: &ProofExpr,
1240 e2: &ProofExpr,
1241 subst: &mut Substitution,
1242) -> ProofResult<()> {
1243 let e1 = beta_reduce(e1);
1246 let e2 = beta_reduce(e2);
1247
1248 match (&e1, &e2) {
1249 (ProofExpr::Atom(a1), ProofExpr::Atom(a2)) if a1 == a2 => Ok(()),
1251
1252 (
1254 ProofExpr::Predicate { name: n1, args: a1, world: w1 },
1255 ProofExpr::Predicate { name: n2, args: a2, world: w2 },
1256 ) => {
1257 if n1 != n2 {
1258 return Err(ProofError::SymbolMismatch {
1259 left: n1.clone(),
1260 right: n2.clone(),
1261 });
1262 }
1263 if a1.len() != a2.len() {
1264 return Err(ProofError::ArityMismatch {
1265 expected: a1.len(),
1266 found: a2.len(),
1267 });
1268 }
1269 match (w1, w2) {
1271 (Some(w1), Some(w2)) if w1 != w2 => {
1272 return Err(ProofError::SymbolMismatch {
1273 left: w1.clone(),
1274 right: w2.clone(),
1275 });
1276 }
1277 _ => {}
1278 }
1279 for (t1, t2) in a1.iter().zip(a2.iter()) {
1281 unify_terms_with_subst(t1, t2, subst)?;
1282 }
1283 Ok(())
1284 }
1285
1286 (ProofExpr::Identity(l1, r1), ProofExpr::Identity(l2, r2)) => {
1288 unify_terms_with_subst(l1, l2, subst)?;
1289 unify_terms_with_subst(r1, r2, subst)?;
1290 Ok(())
1291 }
1292
1293 (ProofExpr::And(l1, r1), ProofExpr::And(l2, r2))
1295 | (ProofExpr::Or(l1, r1), ProofExpr::Or(l2, r2))
1296 | (ProofExpr::Implies(l1, r1), ProofExpr::Implies(l2, r2))
1297 | (ProofExpr::Iff(l1, r1), ProofExpr::Iff(l2, r2)) => {
1298 unify_exprs_with_subst(l1, l2, subst)?;
1299 unify_exprs_with_subst(r1, r2, subst)?;
1300 Ok(())
1301 }
1302
1303 (ProofExpr::Not(inner1), ProofExpr::Not(inner2)) => {
1305 unify_exprs_with_subst(inner1, inner2, subst)
1306 }
1307
1308 (
1311 ProofExpr::ForAll { variable: v1, body: b1 },
1312 ProofExpr::ForAll { variable: v2, body: b2 },
1313 )
1314 | (
1315 ProofExpr::Exists { variable: v1, body: b1 },
1316 ProofExpr::Exists { variable: v2, body: b2 },
1317 ) => {
1318 let fresh = fresh_alpha_constant();
1321
1322 let subst1: Substitution = [(v1.clone(), fresh.clone())].into_iter().collect();
1324 let subst2: Substitution = [(v2.clone(), fresh)].into_iter().collect();
1325
1326 let body1_renamed = apply_subst_to_expr(b1, &subst1);
1328 let body2_renamed = apply_subst_to_expr(b2, &subst2);
1329
1330 unify_exprs_with_subst(&body1_renamed, &body2_renamed, subst)
1332 }
1333
1334 (
1336 ProofExpr::Lambda { variable: v1, body: b1 },
1337 ProofExpr::Lambda { variable: v2, body: b2 },
1338 ) => {
1339 let fresh = fresh_alpha_constant();
1341 let subst1: Substitution = [(v1.clone(), fresh.clone())].into_iter().collect();
1342 let subst2: Substitution = [(v2.clone(), fresh)].into_iter().collect();
1343 let body1_renamed = apply_subst_to_expr(b1, &subst1);
1344 let body2_renamed = apply_subst_to_expr(b2, &subst2);
1345 unify_exprs_with_subst(&body1_renamed, &body2_renamed, subst)
1346 }
1347
1348 (ProofExpr::App(f1, a1), ProofExpr::App(f2, a2)) => {
1350 unify_exprs_with_subst(f1, f2, subst)?;
1351 unify_exprs_with_subst(a1, a2, subst)?;
1352 Ok(())
1353 }
1354
1355 (
1358 ProofExpr::NeoEvent {
1359 event_var: e1,
1360 verb: v1,
1361 roles: r1,
1362 },
1363 ProofExpr::NeoEvent {
1364 event_var: e2,
1365 verb: v2,
1366 roles: r2,
1367 },
1368 ) => {
1369 if v1.to_lowercase() != v2.to_lowercase() {
1371 return Err(ProofError::SymbolMismatch {
1372 left: v1.clone(),
1373 right: v2.clone(),
1374 });
1375 }
1376
1377 if r1.len() != r2.len() {
1379 return Err(ProofError::ArityMismatch {
1380 expected: r1.len(),
1381 found: r2.len(),
1382 });
1383 }
1384
1385 let fresh = fresh_alpha_constant();
1387 let subst1: Substitution = [(e1.clone(), fresh.clone())].into_iter().collect();
1388 let subst2: Substitution = [(e2.clone(), fresh)].into_iter().collect();
1389
1390 for ((role1, term1), (role2, term2)) in r1.iter().zip(r2.iter()) {
1392 if role1 != role2 {
1394 return Err(ProofError::SymbolMismatch {
1395 left: role1.clone(),
1396 right: role2.clone(),
1397 });
1398 }
1399 let t1_renamed = apply_subst_to_term(term1, &subst1);
1401 let t2_renamed = apply_subst_to_term(term2, &subst2);
1402 unify_terms_with_subst(&t1_renamed, &t2_renamed, subst)?;
1403 }
1404 Ok(())
1405 }
1406
1407 (
1410 ProofExpr::Temporal { operator: op1, body: b1 },
1411 ProofExpr::Temporal { operator: op2, body: b2 },
1412 ) => {
1413 if op1 != op2 {
1414 return Err(ProofError::ExprUnificationFailed {
1415 left: e1.clone(),
1416 right: e2.clone(),
1417 });
1418 }
1419 unify_exprs_with_subst(b1, b2, subst)
1420 }
1421
1422 (
1425 ProofExpr::TemporalBinary { operator: op1, left: l1, right: r1 },
1426 ProofExpr::TemporalBinary { operator: op2, left: l2, right: r2 },
1427 ) => {
1428 if op1 != op2 {
1429 return Err(ProofError::ExprUnificationFailed {
1430 left: e1.clone(),
1431 right: e2.clone(),
1432 });
1433 }
1434 unify_exprs_with_subst(l1, l2, subst)?;
1435 unify_exprs_with_subst(r1, r2, subst)
1436 }
1437
1438 _ => Err(ProofError::ExprUnificationFailed {
1440 left: e1.clone(),
1441 right: e2.clone(),
1442 }),
1443 }
1444}
1445
1446pub fn apply_subst_to_expr(expr: &ProofExpr, subst: &Substitution) -> ProofExpr {
1470 match expr {
1471 ProofExpr::Predicate { name, args, world } => ProofExpr::Predicate {
1472 name: name.clone(),
1473 args: args.iter().map(|a| apply_subst_to_term(a, subst)).collect(),
1474 world: world.clone(),
1475 },
1476 ProofExpr::Identity(l, r) => ProofExpr::Identity(
1477 apply_subst_to_term(l, subst),
1478 apply_subst_to_term(r, subst),
1479 ),
1480 ProofExpr::Atom(a) => ProofExpr::Atom(a.clone()),
1481 ProofExpr::And(l, r) => ProofExpr::And(
1482 Box::new(apply_subst_to_expr(l, subst)),
1483 Box::new(apply_subst_to_expr(r, subst)),
1484 ),
1485 ProofExpr::Or(l, r) => ProofExpr::Or(
1486 Box::new(apply_subst_to_expr(l, subst)),
1487 Box::new(apply_subst_to_expr(r, subst)),
1488 ),
1489 ProofExpr::Implies(l, r) => ProofExpr::Implies(
1490 Box::new(apply_subst_to_expr(l, subst)),
1491 Box::new(apply_subst_to_expr(r, subst)),
1492 ),
1493 ProofExpr::Iff(l, r) => ProofExpr::Iff(
1494 Box::new(apply_subst_to_expr(l, subst)),
1495 Box::new(apply_subst_to_expr(r, subst)),
1496 ),
1497 ProofExpr::Not(inner) => ProofExpr::Not(Box::new(apply_subst_to_expr(inner, subst))),
1498 ProofExpr::ForAll { variable, body } => {
1499 let new_variable = match subst.get(variable) {
1501 Some(ProofTerm::Variable(new_name)) => new_name.clone(),
1502 _ => variable.clone(),
1503 };
1504 ProofExpr::ForAll {
1505 variable: new_variable,
1506 body: Box::new(apply_subst_to_expr(body, subst)),
1507 }
1508 }
1509 ProofExpr::Exists { variable, body } => {
1510 let new_variable = match subst.get(variable) {
1512 Some(ProofTerm::Variable(new_name)) => new_name.clone(),
1513 _ => variable.clone(),
1514 };
1515 ProofExpr::Exists {
1516 variable: new_variable,
1517 body: Box::new(apply_subst_to_expr(body, subst)),
1518 }
1519 }
1520 ProofExpr::Modal { domain, force, flavor, body } => ProofExpr::Modal {
1521 domain: domain.clone(),
1522 force: *force,
1523 flavor: flavor.clone(),
1524 body: Box::new(apply_subst_to_expr(body, subst)),
1525 },
1526 ProofExpr::Counterfactual { antecedent, consequent } => ProofExpr::Counterfactual {
1527 antecedent: Box::new(apply_subst_to_expr(antecedent, subst)),
1528 consequent: Box::new(apply_subst_to_expr(consequent, subst)),
1529 },
1530 ProofExpr::Temporal { operator, body } => ProofExpr::Temporal {
1531 operator: operator.clone(),
1532 body: Box::new(apply_subst_to_expr(body, subst)),
1533 },
1534 ProofExpr::TemporalBinary { operator, left, right } => ProofExpr::TemporalBinary {
1535 operator: operator.clone(),
1536 left: Box::new(apply_subst_to_expr(left, subst)),
1537 right: Box::new(apply_subst_to_expr(right, subst)),
1538 },
1539 ProofExpr::Lambda { variable, body } => {
1540 let new_variable = match subst.get(variable) {
1542 Some(ProofTerm::Variable(new_name)) => new_name.clone(),
1543 _ => variable.clone(),
1544 };
1545 ProofExpr::Lambda {
1546 variable: new_variable,
1547 body: Box::new(apply_subst_to_expr(body, subst)),
1548 }
1549 }
1550 ProofExpr::App(f, a) => ProofExpr::App(
1551 Box::new(apply_subst_to_expr(f, subst)),
1552 Box::new(apply_subst_to_expr(a, subst)),
1553 ),
1554 ProofExpr::NeoEvent { event_var, verb, roles } => ProofExpr::NeoEvent {
1555 event_var: event_var.clone(),
1556 verb: verb.clone(),
1557 roles: roles
1558 .iter()
1559 .map(|(r, t)| (r.clone(), apply_subst_to_term(t, subst)))
1560 .collect(),
1561 },
1562 ProofExpr::Ctor { name, args } => ProofExpr::Ctor {
1564 name: name.clone(),
1565 args: args.iter().map(|a| apply_subst_to_expr(a, subst)).collect(),
1566 },
1567 ProofExpr::Match { scrutinee, arms } => ProofExpr::Match {
1568 scrutinee: Box::new(apply_subst_to_expr(scrutinee, subst)),
1569 arms: arms
1570 .iter()
1571 .map(|arm| MatchArm {
1572 ctor: arm.ctor.clone(),
1573 bindings: arm.bindings.clone(),
1574 body: apply_subst_to_expr(&arm.body, subst),
1575 })
1576 .collect(),
1577 },
1578 ProofExpr::Fixpoint { name, body } => ProofExpr::Fixpoint {
1579 name: name.clone(),
1580 body: Box::new(apply_subst_to_expr(body, subst)),
1581 },
1582 ProofExpr::TypedVar { name, typename } => ProofExpr::TypedVar {
1583 name: name.clone(),
1584 typename: typename.clone(),
1585 },
1586 ProofExpr::Unsupported(s) => ProofExpr::Unsupported(s.clone()),
1587 ProofExpr::Hole(name) => ProofExpr::Hole(name.clone()),
1589 ProofExpr::Term(t) => ProofExpr::Term(apply_subst_to_term(t, subst)),
1591 }
1592}
1593
1594pub fn compose_substitutions(s1: Substitution, s2: Substitution) -> Substitution {
1627 let mut result: Substitution = s1
1628 .into_iter()
1629 .map(|(k, v)| (k, apply_subst_to_term(&v, &s2)))
1630 .collect();
1631
1632 for (k, v) in s2 {
1634 result.entry(k).or_insert(v);
1635 }
1636
1637 result
1638}
1639
1640pub fn unify_pattern(lhs: &ProofExpr, rhs: &ProofExpr) -> ProofResult<ExprSubstitution> {
1682 let mut solution = ExprSubstitution::new();
1683 unify_pattern_internal(lhs, rhs, &mut solution)?;
1684 Ok(solution)
1685}
1686
1687fn unify_pattern_internal(
1689 lhs: &ProofExpr,
1690 rhs: &ProofExpr,
1691 solution: &mut ExprSubstitution,
1692) -> ProofResult<()> {
1693 let lhs = beta_reduce(lhs);
1695 let rhs = beta_reduce(rhs);
1696
1697 match &lhs {
1698 ProofExpr::Hole(h) => {
1700 solution.insert(h.clone(), rhs.clone());
1701 Ok(())
1702 }
1703
1704 ProofExpr::App(_, _) => {
1706 let (head, args) = collect_app_args(&lhs);
1708
1709 if let ProofExpr::Hole(h) = head {
1710 let var_args = extract_distinct_vars(&args)?;
1712
1713 check_scope(&rhs, &var_args)?;
1715
1716 let renamed_rhs = rename_vars_to_bound(&rhs, &var_args);
1719 let lambda = build_lambda(var_args, renamed_rhs);
1720 solution.insert(h.clone(), lambda);
1721 Ok(())
1722 } else {
1723 if lhs == rhs {
1725 Ok(())
1726 } else {
1727 Err(ProofError::ExprUnificationFailed {
1728 left: lhs.clone(),
1729 right: rhs.clone(),
1730 })
1731 }
1732 }
1733 }
1734
1735 _ => {
1737 if lhs == rhs {
1738 Ok(())
1739 } else {
1740 Err(ProofError::ExprUnificationFailed {
1741 left: lhs.clone(),
1742 right: rhs.clone(),
1743 })
1744 }
1745 }
1746 }
1747}
1748
1749fn collect_app_args(expr: &ProofExpr) -> (ProofExpr, Vec<ProofExpr>) {
1751 let mut args = Vec::new();
1752 let mut current = expr.clone();
1753
1754 while let ProofExpr::App(func, arg) = current {
1755 args.push(*arg);
1756 current = *func;
1757 }
1758
1759 args.reverse();
1760 (current, args)
1761}
1762
1763fn extract_distinct_vars(args: &[ProofExpr]) -> ProofResult<Vec<String>> {
1766 let mut vars = Vec::new();
1767 for arg in args {
1768 match arg {
1769 ProofExpr::Term(ProofTerm::BoundVarRef(v)) => {
1770 if vars.contains(v) {
1771 return Err(ProofError::PatternNotDistinct(v.clone()));
1772 }
1773 vars.push(v.clone());
1774 }
1775 _ => return Err(ProofError::NotAPattern(arg.clone())),
1776 }
1777 }
1778 Ok(vars)
1779}
1780
1781fn check_scope(expr: &ProofExpr, allowed: &[String]) -> ProofResult<()> {
1783 let free_vars = collect_free_vars(expr);
1784 for var in free_vars {
1785 if !allowed.contains(&var) {
1786 return Err(ProofError::ScopeViolation {
1787 var,
1788 allowed: allowed.to_vec(),
1789 });
1790 }
1791 }
1792 Ok(())
1793}
1794
1795fn collect_free_vars(expr: &ProofExpr) -> Vec<String> {
1797 let mut vars = Vec::new();
1798 collect_free_vars_impl(expr, &mut vars, &mut Vec::new());
1799 vars
1800}
1801
1802fn collect_free_vars_impl(expr: &ProofExpr, vars: &mut Vec<String>, bound: &mut Vec<String>) {
1803 match expr {
1804 ProofExpr::Predicate { args, .. } => {
1805 for arg in args {
1806 collect_free_vars_term(arg, vars, bound);
1807 }
1808 }
1809 ProofExpr::Identity(l, r) => {
1810 collect_free_vars_term(l, vars, bound);
1811 collect_free_vars_term(r, vars, bound);
1812 }
1813 ProofExpr::Atom(s) => {
1814 if !bound.contains(s) && !vars.contains(s) {
1815 vars.push(s.clone());
1816 }
1817 }
1818 ProofExpr::And(l, r)
1819 | ProofExpr::Or(l, r)
1820 | ProofExpr::Implies(l, r)
1821 | ProofExpr::Iff(l, r) => {
1822 collect_free_vars_impl(l, vars, bound);
1823 collect_free_vars_impl(r, vars, bound);
1824 }
1825 ProofExpr::Not(inner) => collect_free_vars_impl(inner, vars, bound),
1826 ProofExpr::ForAll { variable, body }
1827 | ProofExpr::Exists { variable, body }
1828 | ProofExpr::Lambda { variable, body } => {
1829 bound.push(variable.clone());
1830 collect_free_vars_impl(body, vars, bound);
1831 bound.pop();
1832 }
1833 ProofExpr::App(f, a) => {
1834 collect_free_vars_impl(f, vars, bound);
1835 collect_free_vars_impl(a, vars, bound);
1836 }
1837 ProofExpr::Term(t) => collect_free_vars_term(t, vars, bound),
1838 ProofExpr::Hole(_) => {} _ => {} }
1841}
1842
1843fn collect_free_vars_term(term: &ProofTerm, vars: &mut Vec<String>, bound: &[String]) {
1844 match term {
1845 ProofTerm::Variable(v) => {
1846 if !bound.contains(v) && !vars.contains(v) {
1847 vars.push(v.clone());
1848 }
1849 }
1850 ProofTerm::Function(_, args) => {
1851 for arg in args {
1852 collect_free_vars_term(arg, vars, bound);
1853 }
1854 }
1855 ProofTerm::Group(terms) => {
1856 for t in terms {
1857 collect_free_vars_term(t, vars, bound);
1858 }
1859 }
1860 ProofTerm::Constant(_) | ProofTerm::BoundVarRef(_) => {}
1861 }
1862}
1863
1864fn rename_vars_to_bound(expr: &ProofExpr, bound_vars: &[String]) -> ProofExpr {
1867 expr.clone()
1871}
1872
1873fn build_lambda(vars: Vec<String>, body: ProofExpr) -> ProofExpr {
1875 vars.into_iter().rev().fold(body, |acc, var| {
1876 ProofExpr::Lambda {
1877 variable: var,
1878 body: Box::new(acc),
1879 }
1880 })
1881}
1882
1883pub fn match_term_pattern(pattern: &ProofTerm, target: &ProofTerm) -> Option<Substitution> {
1893 let mut subst = Substitution::new();
1894 let mut bound = Vec::new();
1895 if match_term_into(pattern, target, &mut subst, &mut bound) {
1896 Some(subst)
1897 } else {
1898 None
1899 }
1900}
1901
1902pub fn match_expr_pattern(pattern: &ProofExpr, target: &ProofExpr) -> Option<Substitution> {
1908 let mut subst = Substitution::new();
1909 let mut bound = Vec::new();
1910 if match_expr_into(pattern, target, &mut subst, &mut bound) {
1911 Some(subst)
1912 } else {
1913 None
1914 }
1915}
1916
1917fn match_bind(
1920 subst: &mut Substitution,
1921 bound: &[String],
1922 name: &str,
1923 value: &ProofTerm,
1924) -> bool {
1925 if let Some(existing) = subst.get(name) {
1926 return existing == value;
1927 }
1928 if term_mentions_any(value, bound) {
1929 return false;
1930 }
1931 subst.insert(name.to_string(), value.clone());
1932 true
1933}
1934
1935fn term_mentions_any(t: &ProofTerm, names: &[String]) -> bool {
1936 match t {
1937 ProofTerm::Variable(n) | ProofTerm::BoundVarRef(n) => names.iter().any(|b| b == n),
1938 ProofTerm::Constant(_) => false,
1939 ProofTerm::Function(_, args) | ProofTerm::Group(args) => {
1940 args.iter().any(|a| term_mentions_any(a, names))
1941 }
1942 }
1943}
1944
1945fn match_term_into(
1946 pattern: &ProofTerm,
1947 target: &ProofTerm,
1948 subst: &mut Substitution,
1949 bound: &mut Vec<String>,
1950) -> bool {
1951 match pattern {
1952 ProofTerm::Variable(name) => {
1953 if bound.contains(name) {
1956 return matches!(target, ProofTerm::Variable(n) if n == name);
1957 }
1958 match_bind(subst, bound, name, target)
1959 }
1960 ProofTerm::Constant(a) => matches!(target, ProofTerm::Constant(b) if a == b),
1961 ProofTerm::BoundVarRef(a) => matches!(target, ProofTerm::BoundVarRef(b) if a == b),
1962 ProofTerm::Function(name, args) => match target {
1963 ProofTerm::Function(tname, targs) if name == tname && args.len() == targs.len() => {
1964 for (a, b) in args.iter().zip(targs) {
1965 if !match_term_into(a, b, subst, bound) {
1966 return false;
1967 }
1968 }
1969 true
1970 }
1971 _ => false,
1972 },
1973 ProofTerm::Group(args) => match target {
1974 ProofTerm::Group(targs) if args.len() == targs.len() => {
1975 for (a, b) in args.iter().zip(targs) {
1976 if !match_term_into(a, b, subst, bound) {
1977 return false;
1978 }
1979 }
1980 true
1981 }
1982 _ => false,
1983 },
1984 }
1985}
1986
1987fn match_expr_into(
1988 pattern: &ProofExpr,
1989 target: &ProofExpr,
1990 subst: &mut Substitution,
1991 bound: &mut Vec<String>,
1992) -> bool {
1993 match (pattern, target) {
1994 (
1995 ProofExpr::Predicate { name: pn, args: pa, world: pw },
1996 ProofExpr::Predicate { name: tn, args: ta, world: tw },
1997 ) => {
1998 if pn != tn || pa.len() != ta.len() || pw != tw {
1999 return false;
2000 }
2001 for (a, b) in pa.iter().zip(ta) {
2002 if !match_term_into(a, b, subst, bound) {
2003 return false;
2004 }
2005 }
2006 true
2007 }
2008 (ProofExpr::Identity(pl, pr), ProofExpr::Identity(tl, tr)) => {
2009 match_term_into(pl, tl, subst, bound) && match_term_into(pr, tr, subst, bound)
2010 }
2011 (ProofExpr::Atom(a), ProofExpr::Atom(b)) => a == b,
2012 (ProofExpr::And(pl, pr), ProofExpr::And(tl, tr))
2013 | (ProofExpr::Or(pl, pr), ProofExpr::Or(tl, tr))
2014 | (ProofExpr::Implies(pl, pr), ProofExpr::Implies(tl, tr))
2015 | (ProofExpr::Iff(pl, pr), ProofExpr::Iff(tl, tr)) => {
2016 match_expr_into(pl, tl, subst, bound) && match_expr_into(pr, tr, subst, bound)
2017 }
2018 (ProofExpr::Not(p), ProofExpr::Not(t)) => match_expr_into(p, t, subst, bound),
2019 (
2020 ProofExpr::ForAll { variable: pv, body: pb },
2021 ProofExpr::ForAll { variable: tv, body: tb },
2022 )
2023 | (
2024 ProofExpr::Exists { variable: pv, body: pb },
2025 ProofExpr::Exists { variable: tv, body: tb },
2026 ) => {
2027 if pv != tv {
2028 return false;
2029 }
2030 bound.push(pv.clone());
2031 let ok = match_expr_into(pb, tb, subst, bound);
2032 bound.pop();
2033 ok
2034 }
2035 _ => pattern == target,
2038 }
2039}
2040
2041#[cfg(test)]
2042mod tests {
2043 use super::*;
2044
2045 #[test]
2046 fn test_unify_same_constant() {
2047 let t1 = ProofTerm::Constant("a".into());
2048 let t2 = ProofTerm::Constant("a".into());
2049 let result = unify_terms(&t1, &t2);
2050 assert!(result.is_ok());
2051 assert!(result.unwrap().is_empty());
2052 }
2053
2054 #[test]
2055 fn test_unify_different_constants() {
2056 let t1 = ProofTerm::Constant("a".into());
2057 let t2 = ProofTerm::Constant("b".into());
2058 let result = unify_terms(&t1, &t2);
2059 assert!(result.is_err());
2060 }
2061
2062 #[test]
2063 fn test_unify_var_constant() {
2064 let t1 = ProofTerm::Variable("x".into());
2065 let t2 = ProofTerm::Constant("a".into());
2066 let result = unify_terms(&t1, &t2);
2067 assert!(result.is_ok());
2068 let subst = result.unwrap();
2069 assert_eq!(subst.get("x"), Some(&ProofTerm::Constant("a".into())));
2070 }
2071
2072 #[test]
2073 fn test_occurs_check() {
2074 let t1 = ProofTerm::Variable("x".into());
2075 let t2 = ProofTerm::Function("f".into(), vec![ProofTerm::Variable("x".into())]);
2076 let result = unify_terms(&t1, &t2);
2077 assert!(matches!(result, Err(ProofError::OccursCheck { .. })));
2078 }
2079
2080 #[test]
2081 fn test_compose_substitutions() {
2082 let mut s1 = Substitution::new();
2083 s1.insert("x".into(), ProofTerm::Variable("y".into()));
2084
2085 let mut s2 = Substitution::new();
2086 s2.insert("y".into(), ProofTerm::Constant("a".into()));
2087
2088 let composed = compose_substitutions(s1, s2);
2089
2090 assert_eq!(composed.get("x"), Some(&ProofTerm::Constant("a".into())));
2092 assert_eq!(composed.get("y"), Some(&ProofTerm::Constant("a".into())));
2094 }
2095
2096 #[test]
2101 fn test_alpha_equivalence_exists() {
2102 let e1 = ProofExpr::Exists {
2104 variable: "e".to_string(),
2105 body: Box::new(ProofExpr::Predicate {
2106 name: "run".to_string(),
2107 args: vec![ProofTerm::Variable("e".to_string())],
2108 world: None,
2109 }),
2110 };
2111
2112 let e2 = ProofExpr::Exists {
2113 variable: "x".to_string(),
2114 body: Box::new(ProofExpr::Predicate {
2115 name: "run".to_string(),
2116 args: vec![ProofTerm::Variable("x".to_string())],
2117 world: None,
2118 }),
2119 };
2120
2121 let result = unify_exprs(&e1, &e2);
2122 assert!(
2123 result.is_ok(),
2124 "Alpha-equivalent expressions should unify: {:?}",
2125 result
2126 );
2127 }
2128
2129 #[test]
2130 fn test_alpha_equivalence_forall() {
2131 let e1 = ProofExpr::ForAll {
2133 variable: "x".to_string(),
2134 body: Box::new(ProofExpr::Predicate {
2135 name: "mortal".to_string(),
2136 args: vec![ProofTerm::Variable("x".to_string())],
2137 world: None,
2138 }),
2139 };
2140
2141 let e2 = ProofExpr::ForAll {
2142 variable: "y".to_string(),
2143 body: Box::new(ProofExpr::Predicate {
2144 name: "mortal".to_string(),
2145 args: vec![ProofTerm::Variable("y".to_string())],
2146 world: None,
2147 }),
2148 };
2149
2150 let result = unify_exprs(&e1, &e2);
2151 assert!(
2152 result.is_ok(),
2153 "Alpha-equivalent universals should unify: {:?}",
2154 result
2155 );
2156 }
2157
2158 #[test]
2159 fn test_alpha_equivalence_nested() {
2160 let e1 = ProofExpr::Exists {
2162 variable: "e".to_string(),
2163 body: Box::new(ProofExpr::And(
2164 Box::new(ProofExpr::Predicate {
2165 name: "run".to_string(),
2166 args: vec![ProofTerm::Variable("e".to_string())],
2167 world: None,
2168 }),
2169 Box::new(ProofExpr::Predicate {
2170 name: "agent".to_string(),
2171 args: vec![
2172 ProofTerm::Variable("e".to_string()),
2173 ProofTerm::Constant("John".to_string()),
2174 ],
2175 world: None,
2176 }),
2177 )),
2178 };
2179
2180 let e2 = ProofExpr::Exists {
2181 variable: "x".to_string(),
2182 body: Box::new(ProofExpr::And(
2183 Box::new(ProofExpr::Predicate {
2184 name: "run".to_string(),
2185 args: vec![ProofTerm::Variable("x".to_string())],
2186 world: None,
2187 }),
2188 Box::new(ProofExpr::Predicate {
2189 name: "agent".to_string(),
2190 args: vec![
2191 ProofTerm::Variable("x".to_string()),
2192 ProofTerm::Constant("John".to_string()),
2193 ],
2194 world: None,
2195 }),
2196 )),
2197 };
2198
2199 let result = unify_exprs(&e1, &e2);
2200 assert!(
2201 result.is_ok(),
2202 "Nested alpha-equivalent expressions should unify: {:?}",
2203 result
2204 );
2205 }
2206}