1use crate::discrimination::DiscTree;
18use crate::unify::{apply_subst_to_expr, match_expr_pattern, match_term_pattern, Substitution};
19use crate::{DerivationTree, InferenceRule, ProofExpr, ProofTerm};
20
21enum RuleCore {
24 TermEq { lhs: ProofTerm, rhs: ProofTerm },
25 PropIff { lhs: ProofExpr, rhs: ProofExpr },
26}
27
28struct CompiledRule {
32 #[allow(dead_code)]
33 name: String,
34 params: Vec<String>,
35 conds: Vec<ProofExpr>,
36 core: RuleCore,
37 lemma: ProofExpr,
38 base: DerivationTree,
39}
40
41#[derive(Default)]
43pub struct SimpSet {
44 rules: Vec<CompiledRule>,
45 term_index: DiscTree<usize>,
46 iff_index: DiscTree<usize>,
47}
48
49pub(crate) struct RewriteStep {
51 pub eq: DerivationTree,
52 pub from: ProofTerm,
53 pub to: ProofTerm,
54}
55
56pub(crate) struct IffStep {
59 pub imp: DerivationTree,
60 pub rhs: ProofExpr,
61}
62
63impl SimpSet {
64 pub fn new() -> Self {
65 Self::default()
66 }
67
68 pub fn len(&self) -> usize {
69 self.rules.len()
70 }
71
72 pub fn is_empty(&self) -> bool {
73 self.rules.is_empty()
74 }
75
76 pub fn register_lemma(&mut self, name: &str, lemma: &ProofExpr) -> bool {
84 let mut params: Vec<String> = Vec::new();
86 let mut body = lemma;
87 while let ProofExpr::ForAll { variable, body: inner } = body {
88 if params.contains(variable) {
89 return false; }
91 params.push(variable.clone());
92 body = inner;
93 }
94 let mut conds: Vec<ProofExpr> = Vec::new();
96 while let ProofExpr::Implies(c, rest) = body {
97 conds.push((**c).clone());
98 body = rest;
99 }
100 let core = match body {
101 ProofExpr::Identity(l, r) => {
102 if matches!(l, ProofTerm::Variable(_)) {
103 return false; }
105 RuleCore::TermEq { lhs: l.clone(), rhs: r.clone() }
106 }
107 ProofExpr::Iff(l, r) => {
108 RuleCore::PropIff { lhs: (**l).clone(), rhs: (**r).clone() }
109 }
110 _ => return false,
111 };
112 let lhs_vars = match &core {
115 RuleCore::TermEq { lhs, .. } => term_vars(lhs),
116 RuleCore::PropIff { lhs, .. } => expr_vars(lhs),
117 };
118 let rhs_vars = match &core {
119 RuleCore::TermEq { rhs, .. } => term_vars(rhs),
120 RuleCore::PropIff { rhs, .. } => expr_vars(rhs),
121 };
122 if !rhs_vars.iter().all(|v| lhs_vars.contains(v)) {
123 return false;
124 }
125 for c in &conds {
126 if !expr_vars(c).iter().all(|v| lhs_vars.contains(v)) {
127 return false;
128 }
129 }
130 if !params.iter().all(|p| lhs_vars.contains(p)) {
132 return false;
133 }
134
135 let idx = self.rules.len();
136 match &core {
137 RuleCore::TermEq { lhs, .. } => self.term_index.insert_term(lhs, idx),
138 RuleCore::PropIff { lhs, .. } => self.iff_index.insert_expr(lhs, idx),
139 }
140 self.rules.push(CompiledRule {
141 name: name.to_string(),
142 params,
143 conds,
144 core,
145 lemma: lemma.clone(),
146 base: DerivationTree::leaf(lemma.clone(), InferenceRule::PremiseMatch),
147 });
148 true
149 }
150
151 pub(crate) fn find_term_step(
155 &self,
156 goal: &ProofExpr,
157 hyps: &[(&ProofExpr, &DerivationTree)],
158 ) -> Option<RewriteStep> {
159 let mut subterms = Vec::new();
160 collect_goal_terms(goal, &mut subterms);
161 for t in subterms {
162 let mut cand: Vec<usize> = self.term_index.candidates_term(t).into_iter().copied().collect();
163 cand.sort_unstable();
164 cand.dedup();
165 for idx in cand {
166 if let Some(step) = self.try_term_rule(idx, t, hyps) {
167 return Some(step);
168 }
169 }
170 }
171 None
172 }
173
174 pub(crate) fn find_iff_step(
177 &self,
178 goal: &ProofExpr,
179 hyps: &[(&ProofExpr, &DerivationTree)],
180 ) -> Option<IffStep> {
181 let mut cand: Vec<usize> = self.iff_index.candidates_expr(goal).into_iter().copied().collect();
182 cand.sort_unstable();
183 cand.dedup();
184 for idx in cand {
185 let rule = &self.rules[idx];
186 let RuleCore::PropIff { lhs, .. } = &rule.core else { continue };
187 let Some(subst) = match_expr_pattern(lhs, goal) else { continue };
188 let Some((tree, concl)) = instantiate_rule(rule, &subst, hyps) else { continue };
189 let ProofExpr::Iff(l, r) = &concl else { continue };
190 if l.as_ref() != goal {
191 continue;
192 }
193 let imp = DerivationTree::new(
195 ProofExpr::Implies(r.clone(), l.clone()),
196 InferenceRule::ConjunctionElim,
197 vec![tree],
198 );
199 return Some(IffStep { imp, rhs: (**r).clone() });
200 }
201 None
202 }
203
204 fn try_term_rule(
205 &self,
206 idx: usize,
207 t: &ProofTerm,
208 hyps: &[(&ProofExpr, &DerivationTree)],
209 ) -> Option<RewriteStep> {
210 let rule = &self.rules[idx];
211 let RuleCore::TermEq { lhs, rhs } = &rule.core else { return None };
212 let subst = match_term_pattern(lhs, t)?;
213 let (tree, concl) = instantiate_rule(rule, &subst, hyps)?;
214 let ProofExpr::Identity(from, to) = concl else { return None };
215 if &from != t {
216 return None;
217 }
218 if from == to {
220 return None;
221 }
222 Some(RewriteStep { eq: tree, from, to })
223 }
224}
225
226fn instantiate_rule(
231 rule: &CompiledRule,
232 subst: &Substitution,
233 hyps: &[(&ProofExpr, &DerivationTree)],
234) -> Option<(DerivationTree, ProofExpr)> {
235 let mut tree = rule.base.clone();
236 let mut expr = rule.lemma.clone();
237 for param in &rule.params {
238 let witness = subst.get(param)?.clone();
239 let ProofExpr::ForAll { variable, body } = expr else { return None };
240 debug_assert_eq!(&variable, param);
241 let mut single = Substitution::new();
242 single.insert(param.clone(), witness.clone());
243 let inst = apply_subst_to_expr(&body, &single);
244 tree = DerivationTree::new(
245 inst.clone(),
246 InferenceRule::UniversalInstTerm(witness),
247 vec![tree],
248 );
249 expr = inst;
250 }
251 for _ in 0..rule.conds.len() {
252 let ProofExpr::Implies(cond, rest) = expr else { return None };
253 let cond_proof = discharge_condition(&cond, hyps)?;
254 tree = DerivationTree::new(
255 (*rest).clone(),
256 InferenceRule::ModusPonens,
257 vec![tree, cond_proof],
258 );
259 expr = *rest;
260 }
261 Some((tree, expr))
262}
263
264fn discharge_condition(
268 cond: &ProofExpr,
269 hyps: &[(&ProofExpr, &DerivationTree)],
270) -> Option<DerivationTree> {
271 if let Some((_, proof)) = hyps.iter().find(|(prop, _)| *prop == cond) {
272 return Some((*proof).clone());
273 }
274 if let ProofExpr::Identity(l, r) = cond {
275 if ground_int(l).is_some() && ground_int(r).is_some() {
276 return Some(DerivationTree::leaf(cond.clone(), InferenceRule::ArithDecision));
277 }
278 }
279 None
280}
281
282pub(crate) fn find_ground_fold(goal: &ProofExpr) -> Option<RewriteStep> {
286 let mut subterms = Vec::new();
287 collect_goal_terms(goal, &mut subterms);
288 for t in subterms {
289 let ProofTerm::Function(op, args) = t else { continue };
290 if args.len() != 2 {
291 continue;
292 }
293 let (Some(a), Some(b)) = (ground_int(&args[0]), ground_int(&args[1])) else {
294 continue;
295 };
296 let result = match op.as_str() {
297 "add" => a.checked_add(b),
298 "sub" => a.checked_sub(b),
299 "mul" => a.checked_mul(b),
300 _ => None,
301 }?;
302 let to = ProofTerm::Constant(result.to_string());
303 if &to == t {
304 continue;
305 }
306 let eq = DerivationTree::leaf(
307 ProofExpr::Identity(t.clone(), to.clone()),
308 InferenceRule::ArithDecision,
309 );
310 return Some(RewriteStep { eq, from: t.clone(), to });
311 }
312 None
313}
314
315fn ground_int(t: &ProofTerm) -> Option<i64> {
316 match t {
317 ProofTerm::Constant(s) => s.parse::<i64>().ok(),
318 _ => None,
319 }
320}
321
322fn collect_goal_terms<'a>(e: &'a ProofExpr, out: &mut Vec<&'a ProofTerm>) {
324 fn walk_term<'a>(t: &'a ProofTerm, out: &mut Vec<&'a ProofTerm>) {
325 match t {
326 ProofTerm::Function(_, args) | ProofTerm::Group(args) => {
327 for a in args {
328 walk_term(a, out);
329 }
330 }
331 _ => {}
332 }
333 if !out.contains(&t) {
334 out.push(t);
335 }
336 }
337 match e {
338 ProofExpr::Predicate { args, .. } => {
339 for a in args {
340 walk_term(a, out);
341 }
342 }
343 ProofExpr::Identity(l, r) => {
344 walk_term(l, out);
345 walk_term(r, out);
346 }
347 ProofExpr::And(l, r)
348 | ProofExpr::Or(l, r)
349 | ProofExpr::Implies(l, r)
350 | ProofExpr::Iff(l, r) => {
351 collect_goal_terms(l, out);
352 collect_goal_terms(r, out);
353 }
354 ProofExpr::Not(p) => collect_goal_terms(p, out),
355 ProofExpr::ForAll { body, .. } | ProofExpr::Exists { body, .. } => {
356 collect_goal_terms(body, out)
357 }
358 _ => {}
359 }
360}
361
362fn term_vars(t: &ProofTerm) -> Vec<String> {
363 let mut out = Vec::new();
364 collect_term_vars(t, &mut out);
365 out
366}
367
368fn collect_term_vars(t: &ProofTerm, out: &mut Vec<String>) {
369 match t {
370 ProofTerm::Variable(n) => {
371 if !out.contains(n) {
372 out.push(n.clone());
373 }
374 }
375 ProofTerm::Function(_, args) | ProofTerm::Group(args) => {
376 for a in args {
377 collect_term_vars(a, out);
378 }
379 }
380 _ => {}
381 }
382}
383
384fn expr_vars(e: &ProofExpr) -> Vec<String> {
385 let mut out = Vec::new();
386 collect_expr_vars(e, &mut out);
387 out
388}
389
390fn collect_expr_vars(e: &ProofExpr, out: &mut Vec<String>) {
391 match e {
392 ProofExpr::Predicate { args, .. } => {
393 for a in args {
394 collect_term_vars(a, out);
395 }
396 }
397 ProofExpr::Identity(l, r) => {
398 collect_term_vars(l, out);
399 collect_term_vars(r, out);
400 }
401 ProofExpr::And(l, r)
402 | ProofExpr::Or(l, r)
403 | ProofExpr::Implies(l, r)
404 | ProofExpr::Iff(l, r) => {
405 collect_expr_vars(l, out);
406 collect_expr_vars(r, out);
407 }
408 ProofExpr::Not(p) => collect_expr_vars(p, out),
409 ProofExpr::ForAll { variable, body } | ProofExpr::Exists { variable, body } => {
410 let mut inner = Vec::new();
411 collect_expr_vars(body, &mut inner);
412 for v in inner {
413 if v != *variable && !out.contains(&v) {
414 out.push(v);
415 }
416 }
417 }
418 _ => {}
419 }
420}
421