1use std::collections::VecDeque;
17
18use crate::engine::BackwardChainer;
19use crate::unify::{apply_subst_to_expr, unify_exprs, Substitution};
20use crate::verify::{check_derivation, VerifiedProof};
21use crate::{
22 DerivationTree, InductionArg, InductionCase, InferenceRule, ProofExpr, ProofTerm,
23};
24
25#[derive(Debug, Clone)]
34pub struct Hyp {
35 pub name: String,
36 pub prop: ProofExpr,
37 proof: DerivationTree,
38}
39
40#[derive(Debug, Clone)]
46pub struct CtorSpec {
47 pub constructor: String,
49 pub recursive: Vec<bool>,
51}
52
53#[derive(Debug, Clone)]
55pub struct Goal {
56 pub hyps: Vec<Hyp>,
58 pub target: ProofExpr,
60}
61
62#[derive(Debug, Clone, PartialEq)]
64pub enum TacticError {
65 NoOpenGoals,
67 DoesNotApply(String),
69 NoSuchHypothesis(String),
71 AutoFailed,
73 GoalsRemain(usize),
75}
76
77enum Child {
79 Closed(DerivationTree),
80 Sub(Goal),
81}
82
83#[derive(Clone)]
85enum Node {
86 Hole(Goal),
88 Done(DerivationTree),
90 Filled {
92 conclusion: ProofExpr,
93 rule: InferenceRule,
94 children: Vec<usize>,
95 },
96}
97
98#[derive(Clone)]
104pub struct ProofState {
105 premises: Vec<ProofExpr>,
106 goal: ProofExpr,
107 nodes: Vec<Node>,
108 root: usize,
109 open: VecDeque<usize>,
110 fresh: usize,
111}
112
113fn replace_in_term(t: &ProofTerm, from: &ProofTerm, to: &ProofTerm) -> ProofTerm {
115 if t == from {
116 return to.clone();
117 }
118 match t {
119 ProofTerm::Function(n, args) => ProofTerm::Function(
120 n.clone(),
121 args.iter().map(|a| replace_in_term(a, from, to)).collect(),
122 ),
123 ProofTerm::Group(args) => {
124 ProofTerm::Group(args.iter().map(|a| replace_in_term(a, from, to)).collect())
125 }
126 other => other.clone(),
127 }
128}
129
130fn replace_in_expr(e: &ProofExpr, from: &ProofTerm, to: &ProofTerm) -> ProofExpr {
133 let re = |x: &ProofExpr| Box::new(replace_in_expr(x, from, to));
134 let rt = |x: &ProofTerm| replace_in_term(x, from, to);
135 match e {
136 ProofExpr::Predicate { name, args, world } => ProofExpr::Predicate {
137 name: name.clone(),
138 args: args.iter().map(rt).collect(),
139 world: world.clone(),
140 },
141 ProofExpr::Identity(l, r) => ProofExpr::Identity(rt(l), rt(r)),
142 ProofExpr::Atom(s) => ProofExpr::Atom(s.clone()),
143 ProofExpr::And(l, r) => ProofExpr::And(re(l), re(r)),
144 ProofExpr::Or(l, r) => ProofExpr::Or(re(l), re(r)),
145 ProofExpr::Implies(l, r) => ProofExpr::Implies(re(l), re(r)),
146 ProofExpr::Iff(l, r) => ProofExpr::Iff(re(l), re(r)),
147 ProofExpr::Not(p) => ProofExpr::Not(re(p)),
148 ProofExpr::ForAll { variable, body } => {
149 ProofExpr::ForAll { variable: variable.clone(), body: re(body) }
150 }
151 ProofExpr::Exists { variable, body } => {
152 ProofExpr::Exists { variable: variable.clone(), body: re(body) }
153 }
154 other => other.clone(),
155 }
156}
157
158fn eigen_to_var_tree(tree: &DerivationTree, name: &str) -> DerivationTree {
162 let from = ProofTerm::Constant(name.to_string());
163 let to = ProofTerm::Variable(name.to_string());
164 let rule = match &tree.rule {
165 InferenceRule::Rewrite { from: rf, to: rt } => InferenceRule::Rewrite {
166 from: replace_in_term(rf, &from, &to),
167 to: replace_in_term(rt, &from, &to),
168 },
169 other => other.clone(),
170 };
171 DerivationTree {
172 conclusion: replace_in_expr(&tree.conclusion, &from, &to),
173 rule,
174 premises: tree.premises.iter().map(|p| eigen_to_var_tree(p, name)).collect(),
175 depth: tree.depth,
176 substitution: tree.substitution.clone(),
177 }
178}
179
180fn direct_hyp(name: String, prop: ProofExpr) -> Hyp {
184 let proof = DerivationTree::leaf(prop.clone(), InferenceRule::PremiseMatch);
185 Hyp { name, prop, proof }
186}
187
188impl ProofState {
189 pub fn start(premises: Vec<ProofExpr>, goal: ProofExpr) -> Self {
191 Self::start_with_names(premises, &[], goal)
192 }
193
194 pub fn start_with_names(
199 premises: Vec<ProofExpr>,
200 names: &[Option<String>],
201 goal: ProofExpr,
202 ) -> Self {
203 let hyps = premises
204 .iter()
205 .enumerate()
206 .map(|(i, p)| {
207 let name = names
208 .get(i)
209 .and_then(|n| n.as_ref())
210 .cloned()
211 .unwrap_or_else(|| format!("hp{i}"));
212 direct_hyp(name, p.clone())
213 })
214 .collect();
215 let root_goal = Goal { hyps, target: goal.clone() };
216 ProofState {
217 premises,
218 goal,
219 nodes: vec![Node::Hole(root_goal)],
220 root: 0,
221 open: VecDeque::from([0]),
222 fresh: 0,
223 }
224 }
225
226 fn fresh_witness(&mut self) -> String {
229 self.fresh += 1;
230 format!("W{}", self.fresh)
231 }
232
233 fn set_focused_goal(&mut self, goal: Goal) {
236 if let Some(&idx) = self.open.front() {
237 self.nodes[idx] = Node::Hole(goal);
238 }
239 }
240
241 pub fn focus(&self) -> Option<&Goal> {
243 let idx = *self.open.front()?;
244 match &self.nodes[idx] {
245 Node::Hole(g) => Some(g),
246 _ => None,
247 }
248 }
249
250 pub fn open_goals(&self) -> usize {
252 self.open.len()
253 }
254
255 pub fn focused_target(&self) -> Option<&ProofExpr> {
258 self.focus().map(|g| &g.target)
259 }
260
261 pub fn scope_simp_set(&self) -> crate::simp::SimpSet {
265 let mut set = crate::simp::SimpSet::new();
266 if let Some(g) = self.focus() {
267 for h in &g.hyps {
268 set.register_lemma(&h.name, &h.prop);
269 }
270 }
271 set
272 }
273
274 fn focused_goal(&self) -> Result<Goal, TacticError> {
275 self.focus().cloned().ok_or(TacticError::NoOpenGoals)
276 }
277
278 fn refine(&mut self, conclusion: ProofExpr, rule: InferenceRule, children: Vec<Child>) {
281 let idx = self.open.pop_front().expect("a focused goal");
282 let mut child_indices = Vec::with_capacity(children.len());
283 let mut new_holes = Vec::new();
284 for child in children {
285 let cidx = self.nodes.len();
286 match child {
287 Child::Closed(tree) => self.nodes.push(Node::Done(tree)),
288 Child::Sub(goal) => {
289 self.nodes.push(Node::Hole(goal));
290 new_holes.push(cidx);
291 }
292 }
293 child_indices.push(cidx);
294 }
295 for &h in new_holes.iter().rev() {
297 self.open.push_front(h);
298 }
299 self.nodes[idx] = Node::Filled { conclusion, rule, children: child_indices };
300 }
301
302 fn close(&mut self, tree: DerivationTree) {
304 let idx = self.open.pop_front().expect("a focused goal");
305 self.nodes[idx] = Node::Done(tree);
306 }
307
308 pub fn intro(&mut self, name: &str) -> Result<&mut Self, TacticError> {
314 let g = self.focused_goal()?;
315 match &g.target {
316 ProofExpr::Implies(p, q) => {
317 let mut hyps = g.hyps.clone();
318 hyps.push(direct_hyp(name.to_string(), (**p).clone()));
319 let sub = Goal { hyps, target: (**q).clone() };
320 self.refine(g.target.clone(), InferenceRule::ImpliesIntro, vec![Child::Sub(sub)]);
321 Ok(self)
322 }
323 ProofExpr::ForAll { variable, body } => {
324 let (conclusion, renamed) = if name == variable {
330 (g.target.clone(), (**body).clone())
331 } else {
332 let mut subst = Substitution::new();
333 subst.insert(variable.clone(), ProofTerm::Variable(name.to_string()));
334 let renamed = apply_subst_to_expr(body, &subst);
335 let conclusion = ProofExpr::ForAll {
336 variable: name.to_string(),
337 body: Box::new(renamed.clone()),
338 };
339 (conclusion, renamed)
340 };
341 let sub = Goal { hyps: g.hyps.clone(), target: renamed };
342 self.refine(
343 conclusion,
344 InferenceRule::UniversalIntro {
345 variable: name.to_string(),
346 var_type: "Entity".to_string(),
347 },
348 vec![Child::Sub(sub)],
349 );
350 Ok(self)
351 }
352 other => Err(TacticError::DoesNotApply(format!(
353 "intro expects → or ∀, got {other:?}"
354 ))),
355 }
356 }
357
358 pub fn assumption(&mut self) -> Result<&mut Self, TacticError> {
362 let g = self.focused_goal()?;
363 if let Some(h) = g.hyps.iter().find(|h| h.prop == g.target) {
364 let proof = h.proof.clone();
365 self.close(proof);
366 Ok(self)
367 } else {
368 Err(TacticError::NoSuchHypothesis(format!(
369 "no hypothesis proves {:?}",
370 g.target
371 )))
372 }
373 }
374
375 pub fn exact(&mut self, name: &str) -> Result<&mut Self, TacticError> {
378 let g = self.focused_goal()?;
379 match g.hyps.iter().find(|h| h.name == name) {
380 Some(h) if h.prop == g.target => {
381 let proof = h.proof.clone();
382 self.close(proof);
383 Ok(self)
384 }
385 Some(h) => Err(TacticError::DoesNotApply(format!(
386 "hypothesis {name} : {:?} does not prove {:?}",
387 h.prop, g.target
388 ))),
389 None => Err(TacticError::NoSuchHypothesis(name.to_string())),
390 }
391 }
392
393 pub fn split(&mut self) -> Result<&mut Self, TacticError> {
396 let g = self.focused_goal()?;
397 match &g.target {
398 ProofExpr::And(a, b) => {
399 let ga = Goal { hyps: g.hyps.clone(), target: (**a).clone() };
400 let gb = Goal { hyps: g.hyps.clone(), target: (**b).clone() };
401 self.refine(
402 g.target.clone(),
403 InferenceRule::ConjunctionIntro,
404 vec![Child::Sub(ga), Child::Sub(gb)],
405 );
406 Ok(self)
407 }
408 other => Err(TacticError::DoesNotApply(format!("split expects ∧, got {other:?}"))),
409 }
410 }
411
412 pub fn left(&mut self) -> Result<&mut Self, TacticError> {
414 self.disjunct(true)
415 }
416
417 pub fn right(&mut self) -> Result<&mut Self, TacticError> {
419 self.disjunct(false)
420 }
421
422 fn disjunct(&mut self, take_left: bool) -> Result<&mut Self, TacticError> {
423 let g = self.focused_goal()?;
424 match &g.target {
425 ProofExpr::Or(a, b) => {
426 let chosen = if take_left { (**a).clone() } else { (**b).clone() };
427 let sub = Goal { hyps: g.hyps.clone(), target: chosen };
428 self.refine(
429 g.target.clone(),
430 InferenceRule::DisjunctionIntro,
431 vec![Child::Sub(sub)],
432 );
433 Ok(self)
434 }
435 other => Err(TacticError::DoesNotApply(format!(
436 "left/right expects ∨, got {other:?}"
437 ))),
438 }
439 }
440
441 pub fn exists(&mut self, witness: ProofTerm) -> Result<&mut Self, TacticError> {
444 let g = self.focused_goal()?;
445 match &g.target {
446 ProofExpr::Exists { variable, body } => {
447 let mut subst = Substitution::new();
448 subst.insert(variable.clone(), witness.clone());
449 let instantiated = apply_subst_to_expr(body, &subst);
450 let sub = Goal { hyps: g.hyps.clone(), target: instantiated };
451 self.refine(
452 g.target.clone(),
453 InferenceRule::ExistentialIntro {
454 witness: format!("{witness}"),
455 witness_type: "Entity".to_string(),
456 },
457 vec![Child::Sub(sub)],
458 );
459 Ok(self)
460 }
461 other => Err(TacticError::DoesNotApply(format!("exists expects ∃, got {other:?}"))),
462 }
463 }
464
465 pub fn apply(&mut self, rule: &ProofExpr) -> Result<&mut Self, TacticError> {
468 let g = self.focused_goal()?;
469 if let ProofExpr::Implies(ant, con) = rule {
470 if unify_exprs(&g.target, con).is_ok() {
471 let rule_leaf = DerivationTree::leaf(rule.clone(), InferenceRule::PremiseMatch);
472 let sub = Goal { hyps: g.hyps.clone(), target: (**ant).clone() };
473 self.refine(
474 g.target.clone(),
475 InferenceRule::ModusPonens,
476 vec![Child::Closed(rule_leaf), Child::Sub(sub)],
477 );
478 return Ok(self);
479 }
480 }
481 Err(TacticError::DoesNotApply(format!(
482 "apply: {rule:?} is not an implication whose conclusion is the goal"
483 )))
484 }
485
486 pub fn cases(&mut self, name: &str) -> Result<&mut Self, TacticError> {
496 let g = self.focused_goal()?;
497 let hyp = g
498 .hyps
499 .iter()
500 .find(|h| h.name == name)
501 .cloned()
502 .ok_or_else(|| TacticError::NoSuchHypothesis(name.to_string()))?;
503 match &hyp.prop {
504 ProofExpr::And(a, b) => {
505 let proj_a = DerivationTree::new(
506 (**a).clone(),
507 InferenceRule::ConjunctionElim,
508 vec![hyp.proof.clone()],
509 );
510 let proj_b = DerivationTree::new(
511 (**b).clone(),
512 InferenceRule::ConjunctionElim,
513 vec![hyp.proof.clone()],
514 );
515 let mut hyps = g.hyps.clone();
516 hyps.push(Hyp { name: format!("{name}_1"), prop: (**a).clone(), proof: proj_a });
517 hyps.push(Hyp { name: format!("{name}_2"), prop: (**b).clone(), proof: proj_b });
518 self.set_focused_goal(Goal { hyps, target: g.target.clone() });
519 Ok(self)
520 }
521 ProofExpr::Or(a, b) => {
522 let mut ha = g.hyps.clone();
523 ha.push(direct_hyp(format!("{name}_l"), (**a).clone()));
524 let ga = Goal { hyps: ha, target: g.target.clone() };
525 let mut hb = g.hyps.clone();
526 hb.push(direct_hyp(format!("{name}_r"), (**b).clone()));
527 let gb = Goal { hyps: hb, target: g.target.clone() };
528 self.refine(
529 g.target.clone(),
530 InferenceRule::DisjunctionCases,
531 vec![Child::Closed(hyp.proof.clone()), Child::Sub(ga), Child::Sub(gb)],
532 );
533 Ok(self)
534 }
535 ProofExpr::Exists { variable, body } => {
536 let witness = self.fresh_witness();
537 let mut subst = Substitution::new();
538 subst.insert(variable.clone(), ProofTerm::Constant(witness.clone()));
539 let phi_c = apply_subst_to_expr(body, &subst);
540 let mut hyps = g.hyps.clone();
541 hyps.push(direct_hyp(format!("{name}_w"), phi_c));
542 let gbody = Goal { hyps, target: g.target.clone() };
543 self.refine(
544 g.target.clone(),
545 InferenceRule::ExistentialElim { witness },
546 vec![Child::Closed(hyp.proof.clone()), Child::Sub(gbody)],
547 );
548 Ok(self)
549 }
550 other => Err(TacticError::DoesNotApply(format!(
551 "cases expects ∧/∨/∃ hypothesis, got {other:?}"
552 ))),
553 }
554 }
555
556 pub fn induction(&mut self) -> Result<&mut Self, TacticError> {
562 let g = self.focused_goal()?;
563 let (variable, body) = match &g.target {
564 ProofExpr::ForAll { variable, body } => (variable.clone(), (**body).clone()),
565 other => {
566 return Err(TacticError::DoesNotApply(format!(
567 "induction expects a ∀ goal, got {other:?}"
568 )))
569 }
570 };
571 self.fresh += 1;
572 let step_var = format!("K{}", self.fresh);
578 let subst_to = |term: ProofTerm| {
579 let mut s = Substitution::new();
580 s.insert(variable.clone(), term);
581 apply_subst_to_expr(&body, &s)
582 };
583 let base_target = subst_to(ProofTerm::Constant("Zero".to_string()));
584 let succ_k =
585 ProofTerm::Function("Succ".to_string(), vec![ProofTerm::Constant(step_var.clone())]);
586 let step_target = subst_to(succ_k);
587 let ih_prop = subst_to(ProofTerm::Constant(step_var.clone()));
588
589 let base_goal = Goal { hyps: g.hyps.clone(), target: base_target };
590 let mut step_hyps = vec![direct_hyp("ih".to_string(), ih_prop)];
594 step_hyps.extend(g.hyps.clone());
595 let step_goal = Goal { hyps: step_hyps, target: step_target };
596
597 self.refine(
598 g.target.clone(),
599 InferenceRule::StructuralInduction {
600 variable,
601 ind_type: "Nat".to_string(),
602 step_var,
603 },
604 vec![Child::Sub(base_goal), Child::Sub(step_goal)],
605 );
606 Ok(self)
607 }
608
609 pub fn induction_over(
624 &mut self,
625 ind_type: &str,
626 ctors: Vec<CtorSpec>,
627 ) -> Result<&mut Self, TacticError> {
628 let g = self.focused_goal()?;
629 let (variable, body) = match &g.target {
630 ProofExpr::ForAll { variable, body } => (variable.clone(), (**body).clone()),
631 other => {
632 return Err(TacticError::DoesNotApply(format!(
633 "induction expects a ∀ goal, got {other:?}"
634 )))
635 }
636 };
637 let subst_to = |term: ProofTerm| {
638 let mut s = Substitution::new();
639 s.insert(variable.clone(), term);
640 apply_subst_to_expr(&body, &s)
641 };
642
643 let mut cases = Vec::with_capacity(ctors.len());
644 let mut children = Vec::with_capacity(ctors.len());
645 for ctor in &ctors {
646 let arg_names: Vec<String> = ctor
648 .recursive
649 .iter()
650 .map(|_| {
651 self.fresh += 1;
652 format!("K{}", self.fresh)
653 })
654 .collect();
655
656 let ctor_term = if arg_names.is_empty() {
659 ProofTerm::Constant(ctor.constructor.clone())
660 } else {
661 ProofTerm::Function(
662 ctor.constructor.clone(),
663 arg_names.iter().map(|n| ProofTerm::Constant(n.clone())).collect(),
664 )
665 };
666 let case_target = subst_to(ctor_term);
667
668 let mut hyps = Vec::new();
670 for (arg, &is_rec) in arg_names.iter().zip(ctor.recursive.iter()) {
671 if is_rec {
672 let ih_prop = subst_to(ProofTerm::Constant(arg.clone()));
673 hyps.push(direct_hyp(format!("ih_{arg}"), ih_prop));
674 }
675 }
676 hyps.extend(g.hyps.clone());
677 children.push(Child::Sub(Goal { hyps, target: case_target }));
678
679 cases.push(InductionCase {
680 constructor: ctor.constructor.clone(),
681 args: arg_names
682 .iter()
683 .zip(ctor.recursive.iter())
684 .map(|(name, &recursive)| InductionArg { name: name.clone(), recursive })
685 .collect(),
686 });
687 }
688
689 self.refine(
690 g.target.clone(),
691 InferenceRule::InductionScheme { variable, ind_type: ind_type.to_string(), cases },
692 children,
693 );
694 Ok(self)
695 }
696
697 pub fn rewrite(&mut self, name: &str) -> Result<&mut Self, TacticError> {
703 let g = self.focused_goal()?;
704 let hyp = g
705 .hyps
706 .iter()
707 .find(|h| h.name == name)
708 .cloned()
709 .ok_or_else(|| TacticError::NoSuchHypothesis(name.to_string()))?;
710 let (lhs, rhs) = match &hyp.prop {
711 ProofExpr::Identity(l, r) => (l.clone(), r.clone()),
712 other => {
713 return Err(TacticError::DoesNotApply(format!(
714 "rewrite expects an equality hypothesis, got {other:?}"
715 )))
716 }
717 };
718 let subgoal_target = replace_in_expr(&g.target, &lhs, &rhs);
719 if subgoal_target == g.target {
720 return Err(TacticError::DoesNotApply(format!(
721 "rewrite: {lhs} does not occur in the goal"
722 )));
723 }
724 let eq_sym = DerivationTree::new(
726 ProofExpr::Identity(rhs.clone(), lhs.clone()),
727 InferenceRule::EqualitySymmetry,
728 vec![hyp.proof.clone()],
729 );
730 let sub = Goal { hyps: g.hyps.clone(), target: subgoal_target };
731 self.refine(
732 g.target.clone(),
733 InferenceRule::Rewrite { from: rhs, to: lhs },
734 vec![Child::Closed(eq_sym), Child::Sub(sub)],
735 );
736 Ok(self)
737 }
738
739 pub fn simp(&mut self, set: &crate::simp::SimpSet) -> Result<&mut Self, TacticError> {
751 const BUDGET: usize = 1000;
752 let mut progress = false;
753 let mut seen: std::collections::HashSet<String> = std::collections::HashSet::new();
754 seen.insert(self.focused_goal()?.target.to_string());
755
756 for _ in 0..BUDGET {
757 let g = self.focused_goal()?;
758 let hyp_view: Vec<(&ProofExpr, &DerivationTree)> =
759 g.hyps.iter().map(|h| (&h.prop, &h.proof)).collect();
760
761 let step = set
763 .find_term_step(&g.target, &hyp_view)
764 .or_else(|| crate::simp::find_ground_fold(&g.target));
765 if let Some(step) = step {
766 let new_target = replace_in_expr(&g.target, &step.from, &step.to);
767 if new_target == g.target || !seen.insert(new_target.to_string()) {
768 break;
769 }
770 let eq_sym = DerivationTree::new(
773 ProofExpr::Identity(step.to.clone(), step.from.clone()),
774 InferenceRule::EqualitySymmetry,
775 vec![step.eq],
776 );
777 let sub = Goal { hyps: g.hyps.clone(), target: new_target };
778 self.refine(
779 g.target.clone(),
780 InferenceRule::Rewrite { from: step.to, to: step.from },
781 vec![Child::Closed(eq_sym), Child::Sub(sub)],
782 );
783 progress = true;
784 continue;
785 }
786
787 if let Some(iff) = set.find_iff_step(&g.target, &hyp_view) {
790 if !seen.insert(iff.rhs.to_string()) {
791 break;
792 }
793 let sub = Goal { hyps: g.hyps.clone(), target: iff.rhs };
794 self.refine(
795 g.target.clone(),
796 InferenceRule::ModusPonens,
797 vec![Child::Closed(iff.imp), Child::Sub(sub)],
798 );
799 progress = true;
800 continue;
801 }
802
803 break;
804 }
805
806 let g = self.focused_goal()?;
808 if let ProofExpr::Identity(l, r) = &g.target {
809 if l == r {
810 self.close(DerivationTree::leaf(
811 g.target.clone(),
812 InferenceRule::Reflexivity,
813 ));
814 return Ok(self);
815 }
816 }
817 if self.assumption().is_ok() {
818 return Ok(self);
819 }
820 if progress {
821 Ok(self)
822 } else {
823 Err(TacticError::DoesNotApply(
824 "simp: no rule rewrites the goal and it is not closable".to_string(),
825 ))
826 }
827 }
828
829 pub fn decide(&mut self) -> Result<&mut Self, TacticError> {
834 let g = self.focused_goal()?;
835 match crate::decide::decide_expr(&g.target) {
836 Some(tree) => {
837 self.close(tree);
838 Ok(self)
839 }
840 None => Err(TacticError::DoesNotApply(
841 "decide: not a closed decidable goal (or it is false)".to_string(),
842 )),
843 }
844 }
845
846 pub fn omega(&mut self) -> Result<&mut Self, TacticError> {
853 let g = self.focused_goal()?;
854 if !crate::omega_solve::has_arith_facts(
855 &g.hyps.iter().map(|h| (h.prop.clone(), h.proof.clone())).collect::<Vec<_>>(),
856 ) {
857 return Err(TacticError::DoesNotApply(
858 "omega: no arithmetic (≤ / <) hypotheses in scope".to_string(),
859 ));
860 }
861 let known: Vec<(ProofExpr, DerivationTree)> =
862 g.hyps.iter().map(|h| (h.prop.clone(), h.proof.clone())).collect();
863 match crate::omega_solve::omega_close(&known) {
864 Some(bot) => {
865 let tree = DerivationTree::new(
867 g.target.clone(),
868 InferenceRule::ExFalso,
869 vec![bot],
870 );
871 self.close(tree);
872 Ok(self)
873 }
874 None => Err(TacticError::DoesNotApply(
875 "omega: the integer hypotheses are satisfiable (no refutation)".to_string(),
876 )),
877 }
878 }
879
880 pub fn crush(&mut self) -> Result<&mut Self, TacticError> {
885 let g = self.focused_goal()?;
886 let premises: Vec<ProofExpr> = g.hyps.iter().map(|h| h.prop.clone()).collect();
887 match crate::crush::crush_prove(&premises, &g.target) {
888 Some(tree) => {
889 self.close(tree);
890 Ok(self)
891 }
892 None => Err(TacticError::DoesNotApply(
893 "crush: could not close the goal by e-matching + congruence".to_string(),
894 )),
895 }
896 }
897
898 pub fn auto(&mut self) -> Result<&mut Self, TacticError> {
902 let g = self.focused_goal()?;
903 let mut engine = BackwardChainer::new();
904 for h in &g.hyps {
905 engine.add_axiom(h.prop.clone());
906 }
907 match engine.prove(g.target.clone()) {
908 Ok(tree) => {
909 self.close(tree);
910 Ok(self)
911 }
912 Err(_) => Err(TacticError::AutoFailed),
913 }
914 }
915
916 fn assemble(&self, idx: usize) -> Result<DerivationTree, TacticError> {
917 match &self.nodes[idx] {
918 Node::Done(tree) => Ok(tree.clone()),
919 Node::Filled { conclusion, rule, children } => {
920 let mut kids = children
921 .iter()
922 .map(|&c| self.assemble(c))
923 .collect::<Result<Vec<_>, _>>()?;
924 if let InferenceRule::StructuralInduction { step_var, .. } = rule {
927 if kids.len() == 2 {
928 kids[1] = eigen_to_var_tree(&kids[1], step_var);
929 }
930 }
931 if let InferenceRule::InductionScheme { cases, .. } = rule {
935 for (kid, case) in kids.iter_mut().zip(cases.iter()) {
936 for arg in &case.args {
937 *kid = eigen_to_var_tree(kid, &arg.name);
938 }
939 }
940 }
941 Ok(DerivationTree::new(conclusion.clone(), rule.clone(), kids))
942 }
943 Node::Hole(_) => Err(TacticError::GoalsRemain(1)),
944 }
945 }
946
947 pub fn assembled(&self) -> Option<DerivationTree> {
949 if self.open.is_empty() {
950 self.assemble(self.root).ok()
951 } else {
952 None
953 }
954 }
955
956 pub fn run(&mut self, t: &Tactic) -> Result<&mut Self, TacticError> {
959 t(self)?;
960 Ok(self)
961 }
962
963 pub fn qed(&self) -> Result<VerifiedProof, TacticError> {
967 if !self.open.is_empty() {
968 return Err(TacticError::GoalsRemain(self.open.len()));
969 }
970 let tree = self.assemble(self.root)?;
971 Ok(check_derivation(&self.premises, &self.goal, tree))
972 }
973}
974
975pub type Tactic = Box<dyn Fn(&mut ProofState) -> Result<(), TacticError>>;
979
980pub mod combinators {
985 use super::{ProofState, ProofTerm, Tactic, TacticError};
986
987 pub fn intro(name: &str) -> Tactic {
989 let name = name.to_string();
990 Box::new(move |st: &mut ProofState| st.intro(&name).map(|_| ()))
991 }
992 pub fn assumption() -> Tactic {
994 Box::new(|st: &mut ProofState| st.assumption().map(|_| ()))
995 }
996 pub fn simp() -> Tactic {
999 Box::new(|st: &mut ProofState| {
1000 let set = st.scope_simp_set();
1001 st.simp(&set).map(|_| ())
1002 })
1003 }
1004 pub fn decide() -> Tactic {
1006 Box::new(|st: &mut ProofState| st.decide().map(|_| ()))
1007 }
1008 pub fn omega() -> Tactic {
1010 Box::new(|st: &mut ProofState| st.omega().map(|_| ()))
1011 }
1012 pub fn crush() -> Tactic {
1014 Box::new(|st: &mut ProofState| st.crush().map(|_| ()))
1015 }
1016 pub fn exact(name: &str) -> Tactic {
1018 let name = name.to_string();
1019 Box::new(move |st: &mut ProofState| st.exact(&name).map(|_| ()))
1020 }
1021 pub fn split() -> Tactic {
1023 Box::new(|st: &mut ProofState| st.split().map(|_| ()))
1024 }
1025 pub fn left() -> Tactic {
1027 Box::new(|st: &mut ProofState| st.left().map(|_| ()))
1028 }
1029 pub fn right() -> Tactic {
1031 Box::new(|st: &mut ProofState| st.right().map(|_| ()))
1032 }
1033 pub fn exists(witness: ProofTerm) -> Tactic {
1035 Box::new(move |st: &mut ProofState| st.exists(witness.clone()).map(|_| ()))
1036 }
1037 pub fn cases(name: &str) -> Tactic {
1039 let name = name.to_string();
1040 Box::new(move |st: &mut ProofState| st.cases(&name).map(|_| ()))
1041 }
1042 pub fn rewrite(name: &str) -> Tactic {
1044 let name = name.to_string();
1045 Box::new(move |st: &mut ProofState| st.rewrite(&name).map(|_| ()))
1046 }
1047 pub fn induction() -> Tactic {
1049 Box::new(|st: &mut ProofState| st.induction().map(|_| ()))
1050 }
1051 pub fn induction_over(ind_type: &str, ctors: Vec<super::CtorSpec>) -> Tactic {
1053 let ind_type = ind_type.to_string();
1054 Box::new(move |st: &mut ProofState| {
1055 st.induction_over(&ind_type, ctors.clone()).map(|_| ())
1056 })
1057 }
1058 pub fn auto() -> Tactic {
1060 Box::new(|st: &mut ProofState| st.auto().map(|_| ()))
1061 }
1062
1063 pub fn seq(tactics: Vec<Tactic>) -> Tactic {
1066 Box::new(move |st: &mut ProofState| {
1067 for t in &tactics {
1068 t(st)?;
1069 }
1070 Ok(())
1071 })
1072 }
1073
1074 pub fn first(tactics: Vec<Tactic>) -> Tactic {
1077 Box::new(move |st: &mut ProofState| {
1078 for t in &tactics {
1079 let mut trial = st.clone();
1080 if t(&mut trial).is_ok() {
1081 *st = trial;
1082 return Ok(());
1083 }
1084 }
1085 Err(TacticError::DoesNotApply("first: no alternative applied".to_string()))
1086 })
1087 }
1088
1089 pub fn try_(t: Tactic) -> Tactic {
1092 Box::new(move |st: &mut ProofState| {
1093 let mut trial = st.clone();
1094 if t(&mut trial).is_ok() {
1095 *st = trial;
1096 }
1097 Ok(())
1098 })
1099 }
1100
1101 pub fn repeat(t: Tactic) -> Tactic {
1105 Box::new(move |st: &mut ProofState| {
1106 for _ in 0..100_000 {
1107 let mut trial = st.clone();
1108 if t(&mut trial).is_ok() {
1109 *st = trial;
1110 } else {
1111 break;
1112 }
1113 if st.open.is_empty() {
1114 break;
1115 }
1116 }
1117 Ok(())
1118 })
1119 }
1120
1121 pub fn all_goals(t: Tactic) -> Tactic {
1125 Box::new(move |st: &mut ProofState| {
1126 let current: Vec<usize> = st.open.iter().copied().collect();
1127 let mut new_open = std::collections::VecDeque::new();
1128 for goal_idx in current {
1129 st.open = std::collections::VecDeque::from([goal_idx]);
1130 t(st)?;
1131 new_open.extend(st.open.iter().copied());
1132 }
1133 st.open = new_open;
1134 Ok(())
1135 })
1136 }
1137
1138 pub fn then_all(t1: Tactic, t2: Tactic) -> Tactic {
1140 let t2 = std::rc::Rc::new(t2);
1141 Box::new(move |st: &mut ProofState| {
1142 t1(st)?;
1143 let t2 = t2.clone();
1144 all_goals(Box::new(move |s| t2(s)))(st)
1145 })
1146 }
1147}