1use std::collections::HashSet;
19
20use crate::engine::cert_congruence_path;
21use crate::unify::{apply_subst_to_expr, match_term_pattern, Substitution};
22use crate::{DerivationTree, InferenceRule, ProofExpr, ProofTerm};
23
24pub(crate) fn crush_prove(premises: &[ProofExpr], goal: &ProofExpr) -> Option<DerivationTree> {
28 let mut known: Vec<(ProofExpr, DerivationTree)> = Vec::new();
30 let mut eq_lemmas: Vec<&ProofExpr> = Vec::new();
31 for prem in premises {
32 if is_forall_eq_lemma(prem) {
33 eq_lemmas.push(prem);
34 } else {
35 known.push((prem.clone(), DerivationTree::leaf(prem.clone(), InferenceRule::PremiseMatch)));
36 }
37 }
38
39 let mut universe: Vec<ProofTerm> = Vec::new();
41 for (prop, _) in &known {
42 collect_expr_terms(prop, &mut universe);
43 }
44 collect_expr_terms(goal, &mut universe);
45 dedup_terms(&mut universe);
46
47 for lemma in &eq_lemmas {
49 let (params, lhs, _rhs) = peel_eq_lemma(lemma).expect("checked by is_forall_eq_lemma");
50 let mut fired: HashSet<String> = HashSet::new();
51 for g in &universe {
52 let Some(subst) = match_term_pattern(&lhs, g) else { continue };
53 if !params.iter().all(|p| subst.contains_key(p)) {
54 continue; }
56 let key = format!("{g}");
57 if !fired.insert(key) {
58 continue;
59 }
60 if let Some(inst) = instantiate_eq_lemma(lemma, ¶ms, &subst) {
61 known.push(inst);
62 }
63 }
64 }
65
66 match goal {
68 ProofExpr::Identity(a, b) => cert_congruence_path(a, b, &known),
69 ProofExpr::Predicate { name, args, world } => {
70 close_predicate(name, args, world.as_deref(), &known)
71 }
72 _ => None,
73 }
74}
75
76fn close_predicate(
80 name: &str,
81 goal_args: &[ProofTerm],
82 world: Option<&str>,
83 known: &[(ProofExpr, DerivationTree)],
84) -> Option<DerivationTree> {
85 let pred = |args: &[ProofTerm]| ProofExpr::Predicate {
86 name: name.to_string(),
87 args: args.to_vec(),
88 world: world.map(str::to_string),
89 };
90 for (prop, atom_proof) in known {
91 let ProofExpr::Predicate { name: kn, args: kargs, world: kw } = prop else { continue };
92 if kn != name || kargs.len() != goal_args.len() || kw.as_deref() != world {
93 continue;
94 }
95 let mut cur = kargs.clone();
97 let mut proof = atom_proof.clone();
98 let mut ok = true;
99 for i in 0..goal_args.len() {
100 if cur[i] == goal_args[i] {
101 continue;
102 }
103 let Some(eq) = cert_congruence_path(&cur[i], &goal_args[i], known) else {
105 ok = false;
106 break;
107 };
108 let mut next = cur.clone();
109 next[i] = goal_args[i].clone();
110 proof = DerivationTree::new(
113 pred(&next),
114 InferenceRule::Rewrite { from: cur[i].clone(), to: goal_args[i].clone() },
115 vec![eq, proof],
116 );
117 cur = next;
118 }
119 if ok && cur == goal_args {
120 return Some(proof);
121 }
122 }
123 None
124}
125
126fn is_forall_eq_lemma(e: &ProofExpr) -> bool {
128 peel_eq_lemma(e).is_some()
129}
130
131fn peel_eq_lemma(e: &ProofExpr) -> Option<(Vec<String>, ProofTerm, ProofTerm)> {
134 let mut params = Vec::new();
135 let mut body = e;
136 while let ProofExpr::ForAll { variable, body: inner } = body {
137 params.push(variable.clone());
138 body = inner;
139 }
140 if params.is_empty() {
141 return None;
142 }
143 match body {
144 ProofExpr::Identity(l, r) => Some((params, l.clone(), r.clone())),
145 _ => None,
146 }
147}
148
149fn instantiate_eq_lemma(
152 lemma: &ProofExpr,
153 params: &[String],
154 subst: &Substitution,
155) -> Option<(ProofExpr, DerivationTree)> {
156 let mut tree = DerivationTree::leaf(lemma.clone(), InferenceRule::PremiseMatch);
157 let mut expr = lemma.clone();
158 for param in params {
159 let witness = subst.get(param)?.clone();
160 let ProofExpr::ForAll { variable, body } = expr else { return None };
161 debug_assert_eq!(&variable, param);
162 let mut single = Substitution::new();
163 single.insert(param.clone(), witness.clone());
164 let inst = apply_subst_to_expr(&body, &single);
165 tree = DerivationTree::new(
166 inst.clone(),
167 InferenceRule::UniversalInstTerm(witness),
168 vec![tree],
169 );
170 expr = inst;
171 }
172 Some((expr, tree))
173}
174
175fn collect_expr_terms(e: &ProofExpr, out: &mut Vec<ProofTerm>) {
176 match e {
177 ProofExpr::Predicate { args, .. } => {
178 for a in args {
179 collect_term(a, out);
180 }
181 }
182 ProofExpr::Identity(l, r) => {
183 collect_term(l, out);
184 collect_term(r, out);
185 }
186 ProofExpr::And(l, r)
187 | ProofExpr::Or(l, r)
188 | ProofExpr::Implies(l, r)
189 | ProofExpr::Iff(l, r) => {
190 collect_expr_terms(l, out);
191 collect_expr_terms(r, out);
192 }
193 ProofExpr::Not(p) => collect_expr_terms(p, out),
194 ProofExpr::ForAll { body, .. } | ProofExpr::Exists { body, .. } => {
195 collect_expr_terms(body, out)
196 }
197 _ => {}
198 }
199}
200
201fn collect_term(t: &ProofTerm, out: &mut Vec<ProofTerm>) {
202 out.push(t.clone());
203 if let ProofTerm::Function(_, args) | ProofTerm::Group(args) = t {
204 for a in args {
205 collect_term(a, out);
206 }
207 }
208}
209
210fn dedup_terms(v: &mut Vec<ProofTerm>) {
211 let mut seen = HashSet::new();
212 v.retain(|t| seen.insert(format!("{t}")));
213}