1use crate::context::Context;
44use crate::omega;
45use crate::term::{int_lit, lit_bigint, Literal, Term, Universe};
46use crate::type_checker::substitute;
47
48pub fn normalize(ctx: &Context, term: &Term) -> Term {
53 let mut current = term.clone();
54 let mut fuel = 10000; loop {
57 if fuel == 0 {
58 return current;
60 }
61 fuel -= 1;
62
63 let reduced = reduce_step(ctx, ¤t);
64 if reduced == current {
65 return current;
66 }
67 current = reduced;
68 }
69}
70
71fn reduce_step(ctx: &Context, term: &Term) -> Term {
76 match term {
77 Term::Lit(_) => term.clone(),
79
80 Term::Const { name, levels } => {
83 if let Some((params, _ty, body)) = ctx.get_universe_poly(name) {
84 if params.len() == levels.len() {
85 let subst: std::collections::HashMap<String, crate::term::Universe> =
86 params.iter().cloned().zip(levels.iter().cloned()).collect();
87 return crate::term::instantiate_universes(body, &subst);
88 }
89 }
90 term.clone()
91 }
92
93 Term::App(func, arg) => {
95 if let Some(result) = try_primitive_reduce(func, arg) {
97 return result;
98 }
99
100 if let Some(result) = try_reflection_reduce(ctx, func, arg) {
102 return result;
103 }
104
105 if let Term::Global(name) = func.as_ref() {
110 if name == "reduceBool" {
111 if let Some(b) = crate::eval::eval_bool(ctx, arg) {
112 return Term::Global(if b { "true" } else { "false" }.to_string());
113 }
114 }
115 }
116
117 if let Some(result) = try_quot_lift_reduce(func, arg) {
119 return result;
120 }
121
122 match func.as_ref() {
124 Term::Lambda { param, body, .. } => {
125 substitute(body, param, arg)
127 }
128 Term::Fix { name, body } => {
131 if is_constructor_form(ctx, arg) {
132 let fix_term = Term::Fix {
134 name: name.clone(),
135 body: body.clone(),
136 };
137 let unfolded = substitute(body, name, &fix_term);
138 Term::App(Box::new(unfolded), arg.clone())
139 } else {
140 let reduced_arg = reduce_step(ctx, arg);
142 if reduced_arg != **arg {
143 Term::App(func.clone(), Box::new(reduced_arg))
144 } else {
145 term.clone()
146 }
147 }
148 }
149 Term::MutualFix { defs, index } => {
154 if is_constructor_form(ctx, arg) {
155 let mut unfolded = defs[*index].1.clone();
156 for (j, (name_j, _)) in defs.iter().enumerate() {
157 let proj = Term::MutualFix { defs: defs.clone(), index: j };
158 unfolded = substitute(&unfolded, name_j, &proj);
159 }
160 Term::App(Box::new(unfolded), arg.clone())
161 } else {
162 let reduced_arg = reduce_step(ctx, arg);
163 if reduced_arg != **arg {
164 Term::App(func.clone(), Box::new(reduced_arg))
165 } else {
166 term.clone()
167 }
168 }
169 }
170 Term::App(_, _) => {
172 let reduced_func = reduce_step(ctx, func);
173 if reduced_func != **func {
174 Term::App(Box::new(reduced_func), arg.clone())
175 } else {
176 let reduced_arg = reduce_step(ctx, arg);
178 Term::App(func.clone(), Box::new(reduced_arg))
179 }
180 }
181 _ => {
183 let reduced_func = reduce_step(ctx, func);
184 if reduced_func != **func {
185 Term::App(Box::new(reduced_func), arg.clone())
186 } else {
187 let reduced_arg = reduce_step(ctx, arg);
189 Term::App(func.clone(), Box::new(reduced_arg))
190 }
191 }
192 }
193 }
194
195 Term::Match {
197 discriminant,
198 motive,
199 cases,
200 } => {
201 if let Some((ctor_idx, args)) = extract_constructor(ctx, discriminant) {
202 let case = &cases[ctor_idx];
204 let mut result = case.clone();
206 for arg in args {
207 result = Term::App(Box::new(result), Box::new(arg));
208 }
209 reduce_step(ctx, &result)
211 } else {
212 let reduced_disc = reduce_step(ctx, discriminant);
214 if reduced_disc != **discriminant {
215 Term::Match {
216 discriminant: Box::new(reduced_disc),
217 motive: motive.clone(),
218 cases: cases.clone(),
219 }
220 } else {
221 term.clone()
222 }
223 }
224 }
225
226 Term::Lambda {
228 param,
229 param_type,
230 body,
231 } => {
232 let reduced_param_type = reduce_step(ctx, param_type);
233 let reduced_body = reduce_step(ctx, body);
234 if reduced_param_type != **param_type || reduced_body != **body {
235 Term::Lambda {
236 param: param.clone(),
237 param_type: Box::new(reduced_param_type),
238 body: Box::new(reduced_body),
239 }
240 } else {
241 term.clone()
242 }
243 }
244
245 Term::Pi {
247 param,
248 param_type,
249 body_type,
250 } => {
251 let reduced_param_type = reduce_step(ctx, param_type);
252 let reduced_body_type = reduce_step(ctx, body_type);
253 if reduced_param_type != **param_type || reduced_body_type != **body_type {
254 Term::Pi {
255 param: param.clone(),
256 param_type: Box::new(reduced_param_type),
257 body_type: Box::new(reduced_body_type),
258 }
259 } else {
260 term.clone()
261 }
262 }
263
264 Term::Fix { name, body } => {
266 let reduced_body = reduce_step(ctx, body);
267 if reduced_body != **body {
268 Term::Fix {
269 name: name.clone(),
270 body: Box::new(reduced_body),
271 }
272 } else {
273 term.clone()
274 }
275 }
276
277 Term::MutualFix { defs, index } => {
280 let mut new_defs = Vec::with_capacity(defs.len());
281 let mut changed = false;
282 for (n, b) in defs {
283 let rb = reduce_step(ctx, b);
284 if rb != *b {
285 changed = true;
286 }
287 new_defs.push((n.clone(), rb));
288 }
289 if changed {
290 Term::MutualFix { defs: new_defs, index: *index }
291 } else {
292 term.clone()
293 }
294 }
295
296 Term::Let { name, value, body, .. } => {
299 crate::type_checker::substitute(body, name, value)
300 }
301
302 Term::Sort(_) | Term::Var(_) | Term::Hole => term.clone(),
304
305 Term::Global(name) => {
307 if let Some(body) = ctx.get_definition_body(name) {
308 body.clone()
310 } else {
311 term.clone()
313 }
314 }
315 }
316}
317
318fn is_constructor_form(ctx: &Context, term: &Term) -> bool {
320 extract_constructor(ctx, term).is_some()
321}
322
323fn extract_constructor(ctx: &Context, term: &Term) -> Option<(usize, Vec<Term>)> {
330 if let Term::Lit(Literal::Nat(n)) = term {
334 let ctors = ctx.get_constructors("Nat");
335 let idx_of = |name: &str| ctors.iter().position(|(c, _)| *c == name);
336 return if *n <= logicaffeine_base::BigInt::from_i64(0) {
340 idx_of("Zero").map(|i| (i, Vec::new()))
341 } else {
342 let pred = n.sub(&logicaffeine_base::BigInt::from_i64(1));
343 idx_of("Succ").map(|i| (i, vec![Term::Lit(Literal::Nat(pred))]))
344 };
345 }
346
347 let mut args = Vec::new();
348 let mut current = term;
349
350 while let Term::App(func, arg) = current {
352 args.push((**arg).clone());
353 current = func;
354 }
355 args.reverse();
356
357 if let Term::Global(name) = current {
359 if let Some(inductive) = ctx.constructor_inductive(name) {
360 let ctors = ctx.get_constructors(inductive);
361 for (idx, (ctor_name, ctor_type)) in ctors.iter().enumerate() {
362 if *ctor_name == name {
363 let num_type_params = ctx
370 .inductive_declared_params(inductive)
371 .unwrap_or_else(|| count_type_params(ctor_type));
372
373 let value_args = if num_type_params < args.len() {
375 args[num_type_params..].to_vec()
376 } else {
377 vec![]
378 };
379
380 return Some((idx, value_args));
381 }
382 }
383 }
384 }
385 None
386}
387
388fn count_type_params(ty: &Term) -> usize {
393 let mut count = 0;
394 let mut current = ty;
395
396 while let Term::Pi { param_type, body_type, .. } = current {
397 if is_sort(param_type) {
398 count += 1;
399 current = body_type;
400 } else {
401 break;
402 }
403 }
404
405 count
406}
407
408fn is_sort(term: &Term) -> bool {
410 matches!(term, Term::Sort(_))
411}
412
413fn app_spine(t: &Term) -> (&Term, Vec<&Term>) {
415 let mut args = Vec::new();
416 let mut cur = t;
417 while let Term::App(f, a) = cur {
418 args.push(a.as_ref());
419 cur = f.as_ref();
420 }
421 args.reverse();
422 (cur, args)
423}
424
425fn try_quot_lift_reduce(func: &Term, arg: &Term) -> Option<Term> {
430 let (fh, fargs) = app_spine(func);
431 if !matches!(fh, Term::Global(n) if n == "Quot_lift") || fargs.len() != 5 {
432 return None;
433 }
434 let (qh, qargs) = app_spine(arg);
435 if !matches!(qh, Term::Global(n) if n == "Quot_mk") || qargs.len() != 3 {
436 return None;
437 }
438 Some(Term::App(Box::new(fargs[3].clone()), Box::new(qargs[2].clone())))
439}
440
441fn try_primitive_reduce(func: &Term, arg: &Term) -> Option<Term> {
446 if let Term::App(op_term, x) = func {
448 if let Term::Global(op_name) = op_term.as_ref() {
449 if let (Term::Lit(xl), Term::Lit(yl)) = (x.as_ref(), arg) {
450 let bool_term =
451 |b: bool| Term::Global(if b { "true" } else { "false" }.to_string());
452
453 if let (Literal::Int(a), Literal::Int(b)) = (xl, yl) {
457 let fast = match op_name.as_str() {
458 "add" => a.checked_add(*b),
459 "sub" => a.checked_sub(*b),
460 "mul" => a.checked_mul(*b),
461 "div" => a.checked_div(*b),
462 "mod" => a.checked_rem(*b),
463 _ => None,
464 };
465 if let Some(r) = fast {
466 return Some(Term::Lit(Literal::Int(r)));
467 }
468 let fast_bool = match op_name.as_str() {
472 "le" => Some(a <= b),
473 "lt" => Some(a < b),
474 "ge" => Some(a >= b),
475 "gt" => Some(a > b),
476 _ => None,
477 };
478 if let Some(bl) = fast_bool {
479 return Some(bool_term(bl));
480 }
481 }
482
483 if let (Some(xb), Some(yb)) = (lit_bigint(xl), lit_bigint(yl)) {
489 let big = match op_name.as_str() {
490 "add" => Some(xb.add(&yb)),
491 "sub" => Some(xb.sub(&yb)),
492 "mul" => Some(xb.mul(&yb)),
493 "div" => xb.div_rem(&yb).map(|(q, _)| q),
494 "mod" => xb.div_rem(&yb).map(|(_, r)| r),
495 _ => None,
496 };
497 if let Some(r) = big {
498 return Some(Term::Lit(int_lit(r)));
499 }
500 let bool_result = match op_name.as_str() {
501 "le" => Some(xb <= yb),
502 "lt" => Some(xb < yb),
503 "ge" => Some(xb >= yb),
504 "gt" => Some(xb > yb),
505 _ => None,
506 };
507 if let Some(b) = bool_result {
508 return Some(bool_term(b));
509 }
510 }
511 }
512 }
513 }
514 None
515}
516
517fn try_reflection_reduce(ctx: &Context, func: &Term, arg: &Term) -> Option<Term> {
528 if let Term::Global(op_name) = func {
530 match op_name.as_str() {
531 "syn_size" => {
532 let norm_arg = normalize(ctx, arg);
534 return try_syn_size_reduce(ctx, &norm_arg);
535 }
536 "syn_max_var" => {
537 let norm_arg = normalize(ctx, arg);
539 return try_syn_max_var_reduce(ctx, &norm_arg, 0);
540 }
541 "syn_step" => {
542 let norm_arg = normalize(ctx, arg);
544 return try_syn_step_reduce(ctx, &norm_arg);
545 }
546 "syn_quote" => {
547 let norm_arg = normalize(ctx, arg);
549 return try_syn_quote_reduce(ctx, &norm_arg);
550 }
551 "syn_diag" => {
552 let norm_arg = normalize(ctx, arg);
554 return try_syn_diag_reduce(ctx, &norm_arg);
555 }
556 "concludes" => {
557 let norm_arg = normalize(ctx, arg);
559 return try_concludes_reduce(ctx, &norm_arg);
560 }
561 "try_refl" => {
562 let norm_arg = normalize(ctx, arg);
564 return try_try_refl_reduce(ctx, &norm_arg);
565 }
566 "tact_fail" => {
567 return Some(make_error_derivation());
569 }
570 "try_compute" => {
571 let norm_arg = normalize(ctx, arg);
574 return Some(Term::App(
575 Box::new(Term::Global("DCompute".to_string())),
576 Box::new(norm_arg),
577 ));
578 }
579 "try_ring" => {
580 let norm_arg = normalize(ctx, arg);
582 return try_try_ring_reduce(ctx, &norm_arg);
583 }
584 "try_lia" => {
585 let norm_arg = normalize(ctx, arg);
587 return try_try_lia_reduce(ctx, &norm_arg);
588 }
589 "try_cc" => {
590 let norm_arg = normalize(ctx, arg);
592 return try_try_cc_reduce(ctx, &norm_arg);
593 }
594 "try_simp" => {
595 let norm_arg = normalize(ctx, arg);
597 return try_try_simp_reduce(ctx, &norm_arg);
598 }
599 "try_omega" => {
600 let norm_arg = normalize(ctx, arg);
602 return try_try_omega_reduce(ctx, &norm_arg);
603 }
604 "try_auto" => {
605 let norm_arg = normalize(ctx, arg);
607 return try_try_auto_reduce(ctx, &norm_arg);
608 }
609 "try_bitblast" => {
610 let norm_arg = normalize(ctx, arg);
612 return try_try_bitblast_reduce(ctx, &norm_arg);
613 }
614 "try_tabulate" => {
615 let norm_arg = normalize(ctx, arg);
617 return try_try_tabulate_reduce(ctx, &norm_arg);
618 }
619 "try_hw_auto" => {
620 let norm_arg = normalize(ctx, arg);
622 return try_try_hw_auto_reduce(ctx, &norm_arg);
623 }
624 "try_inversion" => {
625 let norm_arg = normalize(ctx, arg);
627 return try_try_inversion_reduce(ctx, &norm_arg);
628 }
629 "induction_num_cases" => {
630 let norm_arg = normalize(ctx, arg);
632 return try_induction_num_cases_reduce(ctx, &norm_arg);
633 }
634 _ => {}
635 }
636 }
637
638 if let Term::App(partial2, cutoff) = func {
645 if let Term::App(partial1, amount) = partial2.as_ref() {
646 if let Term::Global(op_name) = partial1.as_ref() {
647 if op_name == "syn_lift" {
648 if let (Term::Lit(Literal::Int(amt)), Term::Lit(Literal::Int(cut))) =
649 (amount.as_ref(), cutoff.as_ref())
650 {
651 let norm_term = normalize(ctx, arg);
652 return try_syn_lift_reduce(ctx, *amt, *cut, &norm_term);
653 }
654 }
655 }
656 }
657 }
658
659 if let Term::App(partial2, index) = func {
662 if let Term::App(partial1, replacement) = partial2.as_ref() {
663 if let Term::Global(op_name) = partial1.as_ref() {
664 if op_name == "syn_subst" {
665 if let Term::Lit(Literal::Int(idx)) = index.as_ref() {
666 let norm_replacement = normalize(ctx, replacement);
667 let norm_term = normalize(ctx, arg);
668 return try_syn_subst_reduce(ctx, &norm_replacement, *idx, &norm_term);
669 }
670 }
671 }
672 }
673 }
674
675 if let Term::App(partial1, body) = func {
678 if let Term::Global(op_name) = partial1.as_ref() {
679 if op_name == "syn_beta" {
680 let norm_body = normalize(ctx, body);
681 let norm_arg = normalize(ctx, arg);
682 return try_syn_beta_reduce(ctx, &norm_body, &norm_arg);
683 }
684 }
685 }
686
687 if let Term::App(partial1, context) = func {
691 if let Term::Global(op_name) = partial1.as_ref() {
692 if op_name == "try_cong" {
693 let norm_context = normalize(ctx, context);
694 let norm_proof = normalize(ctx, arg);
695 return Some(Term::App(
696 Box::new(Term::App(
697 Box::new(Term::Global("DCong".to_string())),
698 Box::new(norm_context),
699 )),
700 Box::new(norm_proof),
701 ));
702 }
703 }
704 }
705
706 if let Term::App(partial1, eq_proof) = func {
710 if let Term::Global(op_name) = partial1.as_ref() {
711 if op_name == "try_rewrite" {
712 let norm_eq_proof = normalize(ctx, eq_proof);
713 let norm_goal = normalize(ctx, arg);
714 return try_try_rewrite_reduce(ctx, &norm_eq_proof, &norm_goal, false);
715 }
716 if op_name == "try_rewrite_rev" {
717 let norm_eq_proof = normalize(ctx, eq_proof);
718 let norm_goal = normalize(ctx, arg);
719 return try_try_rewrite_reduce(ctx, &norm_eq_proof, &norm_goal, true);
720 }
721 }
722 }
723
724 if let Term::App(partial1, fuel_term) = func {
727 if let Term::Global(op_name) = partial1.as_ref() {
728 if op_name == "syn_eval" {
729 if let Term::Lit(Literal::Int(fuel)) = fuel_term.as_ref() {
730 let norm_term = normalize(ctx, arg);
731 return try_syn_eval_reduce(ctx, *fuel, &norm_term);
732 }
733 }
734 }
735 }
736
737 if let Term::App(partial1, ind_type) = func {
740 if let Term::Global(op_name) = partial1.as_ref() {
741 if op_name == "induction_base_goal" {
742 let norm_ind_type = normalize(ctx, ind_type);
743 let norm_motive = normalize(ctx, arg);
744 return try_induction_base_goal_reduce(ctx, &norm_ind_type, &norm_motive);
745 }
746 }
747 }
748
749 if let Term::App(partial1, t2) = func {
753 if let Term::App(combinator, t1) = partial1.as_ref() {
754 if let Term::Global(name) = combinator.as_ref() {
755 if name == "tact_orelse" {
756 return try_tact_orelse_reduce(ctx, t1, t2, arg);
757 }
758 if name == "tact_then" {
760 return try_tact_then_reduce(ctx, t1, t2, arg);
761 }
762 }
763 }
764 }
765
766 if let Term::App(combinator, t) = func {
769 if let Term::Global(name) = combinator.as_ref() {
770 if name == "tact_try" {
771 return try_tact_try_reduce(ctx, t, arg);
772 }
773 if name == "tact_repeat" {
775 return try_tact_repeat_reduce(ctx, t, arg);
776 }
777 if name == "tact_solve" {
779 return try_tact_solve_reduce(ctx, t, arg);
780 }
781 if name == "tact_first" {
783 return try_tact_first_reduce(ctx, t, arg);
784 }
785 }
786 }
787
788 if let Term::App(partial1, motive) = func {
792 if let Term::App(combinator, ind_type) = partial1.as_ref() {
793 if let Term::Global(name) = combinator.as_ref() {
794 if name == "induction_step_goal" {
795 let norm_ind_type = normalize(ctx, ind_type);
796 let norm_motive = normalize(ctx, motive);
797 let norm_idx = normalize(ctx, arg);
798 return try_induction_step_goal_reduce(ctx, &norm_ind_type, &norm_motive, &norm_idx);
799 }
800 }
801 }
802 }
803
804 if let Term::App(partial1, motive) = func {
808 if let Term::App(combinator, ind_type) = partial1.as_ref() {
809 if let Term::Global(name) = combinator.as_ref() {
810 if name == "try_induction" {
811 let norm_ind_type = normalize(ctx, ind_type);
812 let norm_motive = normalize(ctx, motive);
813 let norm_cases = normalize(ctx, arg);
814 return try_try_induction_reduce(ctx, &norm_ind_type, &norm_motive, &norm_cases);
815 }
816 }
817 }
818 }
819
820 if let Term::App(partial1, motive) = func {
824 if let Term::App(combinator, ind_type) = partial1.as_ref() {
825 if let Term::Global(name) = combinator.as_ref() {
826 if name == "try_destruct" {
827 let norm_ind_type = normalize(ctx, ind_type);
828 let norm_motive = normalize(ctx, motive);
829 let norm_cases = normalize(ctx, arg);
830 return try_try_destruct_reduce(ctx, &norm_ind_type, &norm_motive, &norm_cases);
831 }
832 }
833 }
834 }
835
836 if let Term::App(partial1, hyp_proof) = func {
840 if let Term::App(combinator, hyp_name) = partial1.as_ref() {
841 if let Term::Global(name) = combinator.as_ref() {
842 if name == "try_apply" {
843 let norm_hyp_name = normalize(ctx, hyp_name);
844 let norm_hyp_proof = normalize(ctx, hyp_proof);
845 let norm_goal = normalize(ctx, arg);
846 return try_try_apply_reduce(ctx, &norm_hyp_name, &norm_hyp_proof, &norm_goal);
847 }
848 }
849 }
850 }
851
852 None
853}
854
855fn try_syn_size_reduce(ctx: &Context, term: &Term) -> Option<Term> {
864 if let Term::App(ctor_term, _inner_arg) = term {
870 if let Term::Global(ctor_name) = ctor_term.as_ref() {
871 match ctor_name.as_str() {
872 "SVar" | "SGlobal" | "SSort" | "SLit" | "SName" => {
873 return Some(Term::Lit(Literal::Int(1)));
874 }
875 _ => {}
876 }
877 }
878
879 if let Term::App(inner, a) = ctor_term.as_ref() {
881 if let Term::Global(ctor_name) = inner.as_ref() {
882 match ctor_name.as_str() {
883 "SApp" | "SLam" | "SPi" => {
884 let a_size = try_syn_size_reduce(ctx, a)?;
886 let b_size = try_syn_size_reduce(ctx, _inner_arg)?;
887
888 if let (Term::Lit(Literal::Int(a_n)), Term::Lit(Literal::Int(b_n))) =
889 (a_size, b_size)
890 {
891 return Some(Term::Lit(Literal::Int(1 + a_n + b_n)));
892 }
893 }
894 _ => {}
895 }
896 }
897 }
898 }
899
900 None
901}
902
903fn try_syn_max_var_reduce(ctx: &Context, term: &Term, depth: i64) -> Option<Term> {
913 if let Term::App(ctor_term, inner_arg) = term {
915 if let Term::Global(ctor_name) = ctor_term.as_ref() {
916 match ctor_name.as_str() {
917 "SVar" => {
918 if let Term::Lit(Literal::Int(k)) = inner_arg.as_ref() {
920 if *k >= depth {
921 return Some(Term::Lit(Literal::Int(*k - depth)));
923 } else {
924 return Some(Term::Lit(Literal::Int(-1)));
926 }
927 }
928 }
929 "SGlobal" | "SSort" | "SLit" | "SName" => {
930 return Some(Term::Lit(Literal::Int(-1)));
932 }
933 _ => {}
934 }
935 }
936
937 if let Term::App(inner, a) = ctor_term.as_ref() {
939 if let Term::Global(ctor_name) = inner.as_ref() {
940 match ctor_name.as_str() {
941 "SApp" => {
942 let a_max = try_syn_max_var_reduce(ctx, a, depth)?;
944 let b_max = try_syn_max_var_reduce(ctx, inner_arg, depth)?;
945
946 if let (Term::Lit(Literal::Int(a_n)), Term::Lit(Literal::Int(b_n))) =
947 (a_max, b_max)
948 {
949 return Some(Term::Lit(Literal::Int(a_n.max(b_n))));
950 }
951 }
952 "SLam" | "SPi" => {
953 let a_max = try_syn_max_var_reduce(ctx, a, depth)?;
956 let b_max = try_syn_max_var_reduce(ctx, inner_arg, depth + 1)?;
957
958 if let (Term::Lit(Literal::Int(a_n)), Term::Lit(Literal::Int(b_n))) =
959 (a_max, b_max)
960 {
961 return Some(Term::Lit(Literal::Int(a_n.max(b_n))));
962 }
963 }
964 _ => {}
965 }
966 }
967 }
968 }
969
970 None
971}
972
973fn try_syn_lift_reduce(ctx: &Context, amount: i64, cutoff: i64, term: &Term) -> Option<Term> {
983 if let Term::App(ctor_term, inner_arg) = term {
985 if let Term::Global(ctor_name) = ctor_term.as_ref() {
986 match ctor_name.as_str() {
987 "SVar" => {
988 if let Term::Lit(Literal::Int(k)) = inner_arg.as_ref() {
989 if *k >= cutoff {
990 return Some(Term::App(
992 Box::new(Term::Global("SVar".to_string())),
993 Box::new(Term::Lit(Literal::Int(*k + amount))),
994 ));
995 } else {
996 return Some(term.clone());
998 }
999 }
1000 }
1001 "SGlobal" | "SSort" | "SLit" | "SName" => {
1002 return Some(term.clone());
1004 }
1005 _ => {}
1006 }
1007 }
1008
1009 if let Term::App(inner, a) = ctor_term.as_ref() {
1011 if let Term::Global(ctor_name) = inner.as_ref() {
1012 match ctor_name.as_str() {
1013 "SApp" => {
1014 let a_lifted = try_syn_lift_reduce(ctx, amount, cutoff, a)?;
1016 let b_lifted = try_syn_lift_reduce(ctx, amount, cutoff, inner_arg)?;
1017 return Some(Term::App(
1018 Box::new(Term::App(
1019 Box::new(Term::Global("SApp".to_string())),
1020 Box::new(a_lifted),
1021 )),
1022 Box::new(b_lifted),
1023 ));
1024 }
1025 "SLam" | "SPi" => {
1026 let param_lifted = try_syn_lift_reduce(ctx, amount, cutoff, a)?;
1028 let body_lifted =
1029 try_syn_lift_reduce(ctx, amount, cutoff + 1, inner_arg)?;
1030 return Some(Term::App(
1031 Box::new(Term::App(
1032 Box::new(Term::Global(ctor_name.clone())),
1033 Box::new(param_lifted),
1034 )),
1035 Box::new(body_lifted),
1036 ));
1037 }
1038 _ => {}
1039 }
1040 }
1041 }
1042 }
1043
1044 None
1045}
1046
1047fn try_syn_subst_reduce(
1054 ctx: &Context,
1055 replacement: &Term,
1056 index: i64,
1057 term: &Term,
1058) -> Option<Term> {
1059 syn_subst_impl(ctx, replacement, index, term, false)
1062}
1063
1064fn syn_subst_impl(
1072 ctx: &Context,
1073 replacement: &Term,
1074 index: i64,
1075 term: &Term,
1076 decrement: bool,
1077) -> Option<Term> {
1078 if let Term::App(ctor_term, inner_arg) = term {
1080 if let Term::Global(ctor_name) = ctor_term.as_ref() {
1081 match ctor_name.as_str() {
1082 "SVar" => {
1083 if let Term::Lit(Literal::Int(k)) = inner_arg.as_ref() {
1084 if *k == index {
1085 return Some(replacement.clone());
1087 } else if decrement && *k > index {
1088 return Some(Term::App(
1091 Box::new(Term::Global("SVar".to_string())),
1092 Box::new(Term::Lit(Literal::Int(*k - 1))),
1093 ));
1094 } else {
1095 return Some(term.clone());
1097 }
1098 }
1099 }
1100 "SGlobal" | "SSort" | "SLit" | "SName" => {
1101 return Some(term.clone());
1103 }
1104 _ => {}
1105 }
1106 }
1107
1108 if let Term::App(inner, a) = ctor_term.as_ref() {
1110 if let Term::Global(ctor_name) = inner.as_ref() {
1111 match ctor_name.as_str() {
1112 "SApp" => {
1113 let a_subst = syn_subst_impl(ctx, replacement, index, a, decrement)?;
1115 let b_subst = syn_subst_impl(ctx, replacement, index, inner_arg, decrement)?;
1116 return Some(Term::App(
1117 Box::new(Term::App(
1118 Box::new(Term::Global("SApp".to_string())),
1119 Box::new(a_subst),
1120 )),
1121 Box::new(b_subst),
1122 ));
1123 }
1124 "SLam" | "SPi" => {
1125 let param_subst = syn_subst_impl(ctx, replacement, index, a, decrement)?;
1128
1129 let lifted_replacement = try_syn_lift_reduce(ctx, 1, 0, replacement)?;
1131 let body_subst = syn_subst_impl(
1132 ctx,
1133 &lifted_replacement,
1134 index + 1,
1135 inner_arg,
1136 decrement,
1137 )?;
1138
1139 return Some(Term::App(
1140 Box::new(Term::App(
1141 Box::new(Term::Global(ctor_name.clone())),
1142 Box::new(param_subst),
1143 )),
1144 Box::new(body_subst),
1145 ));
1146 }
1147 _ => {}
1148 }
1149 }
1150 }
1151 }
1152
1153 None
1154}
1155
1156fn try_syn_beta_reduce(ctx: &Context, body: &Term, arg: &Term) -> Option<Term> {
1171 syn_subst_impl(ctx, arg, 0, body, true)
1172}
1173
1174fn try_syn_arith_reduce(func: &Term, arg: &Term) -> Option<Term> {
1179 if let Term::App(inner_ctor, n_term) = func {
1182 if let Term::App(sapp_ctor, op_term) = inner_ctor.as_ref() {
1183 if let Term::Global(ctor_name) = sapp_ctor.as_ref() {
1185 if ctor_name != "SApp" {
1186 return None;
1187 }
1188 } else {
1189 return None;
1190 }
1191
1192 let op = if let Term::App(sname_ctor, op_str) = op_term.as_ref() {
1194 if let Term::Global(name) = sname_ctor.as_ref() {
1195 if name == "SName" {
1196 if let Term::Lit(Literal::Text(op)) = op_str.as_ref() {
1197 op.clone()
1198 } else {
1199 return None;
1200 }
1201 } else {
1202 return None;
1203 }
1204 } else {
1205 return None;
1206 }
1207 } else {
1208 return None;
1209 };
1210
1211 let n = if let Term::App(slit_ctor, n_val) = n_term.as_ref() {
1213 if let Term::Global(name) = slit_ctor.as_ref() {
1214 if name == "SLit" {
1215 if let Term::Lit(Literal::Int(n)) = n_val.as_ref() {
1216 *n
1217 } else {
1218 return None;
1219 }
1220 } else {
1221 return None;
1222 }
1223 } else {
1224 return None;
1225 }
1226 } else {
1227 return None;
1228 };
1229
1230 let m = if let Term::App(slit_ctor, m_val) = arg {
1232 if let Term::Global(name) = slit_ctor.as_ref() {
1233 if name == "SLit" {
1234 if let Term::Lit(Literal::Int(m)) = m_val.as_ref() {
1235 *m
1236 } else {
1237 return None;
1238 }
1239 } else {
1240 return None;
1241 }
1242 } else {
1243 return None;
1244 }
1245 } else {
1246 return None;
1247 };
1248
1249 let result = match op.as_str() {
1251 "add" => n.checked_add(m),
1252 "sub" => n.checked_sub(m),
1253 "mul" => n.checked_mul(m),
1254 "div" => {
1255 if m == 0 {
1256 return None; }
1258 n.checked_div(m)
1259 }
1260 "mod" => {
1261 if m == 0 {
1262 return None; }
1264 n.checked_rem(m)
1265 }
1266 _ => None,
1267 };
1268
1269 if let Some(r) = result {
1271 return Some(Term::App(
1272 Box::new(Term::Global("SLit".to_string())),
1273 Box::new(Term::Lit(Literal::Int(r))),
1274 ));
1275 }
1276 }
1277 }
1278
1279 None
1280}
1281
1282fn try_syn_step_reduce(ctx: &Context, term: &Term) -> Option<Term> {
1289 if let Term::App(ctor_term, arg) = term {
1291 if let Term::App(inner, func) = ctor_term.as_ref() {
1292 if let Term::Global(ctor_name) = inner.as_ref() {
1293 if ctor_name == "SApp" {
1294 if let Some(result) = try_syn_arith_reduce(func.as_ref(), arg.as_ref()) {
1297 return Some(result);
1298 }
1299
1300 if let Term::App(lam_inner, body) = func.as_ref() {
1302 if let Term::App(lam_ctor, _param_type) = lam_inner.as_ref() {
1303 if let Term::Global(lam_name) = lam_ctor.as_ref() {
1304 if lam_name == "SLam" {
1305 return try_syn_beta_reduce(ctx, body.as_ref(), arg.as_ref());
1307 }
1308 }
1309 }
1310 }
1311
1312 {
1319 let full_app = term; if let Some(kernel_term) = syntax_to_term(full_app) {
1321 let normalized = normalize(ctx, &kernel_term);
1322 if normalized != kernel_term {
1323 if let Some(result_syntax) = term_to_syntax(&normalized) {
1324 return Some(result_syntax);
1325 }
1326 }
1327 }
1328 }
1329
1330 if let Some(stepped_func) = try_syn_step_reduce(ctx, func.as_ref()) {
1332 if &stepped_func != func.as_ref() {
1334 return Some(Term::App(
1336 Box::new(Term::App(
1337 Box::new(Term::Global("SApp".to_string())),
1338 Box::new(stepped_func),
1339 )),
1340 Box::new(arg.as_ref().clone()),
1341 ));
1342 }
1343 }
1344
1345 if let Some(stepped_arg) = try_syn_step_reduce(ctx, arg.as_ref()) {
1347 if &stepped_arg != arg.as_ref() {
1348 return Some(Term::App(
1350 Box::new(Term::App(
1351 Box::new(Term::Global("SApp".to_string())),
1352 Box::new(func.as_ref().clone()),
1353 )),
1354 Box::new(stepped_arg),
1355 ));
1356 }
1357 }
1358
1359 return Some(term.clone());
1361 }
1362 }
1363 }
1364 }
1365
1366 Some(term.clone())
1369}
1370
1371fn try_syn_eval_reduce(ctx: &Context, fuel: i64, term: &Term) -> Option<Term> {
1381 if fuel <= 0 {
1382 return Some(term.clone());
1383 }
1384
1385 let stepped = try_syn_step_reduce(ctx, term)?;
1387
1388 if &stepped == term {
1390 return Some(term.clone());
1391 }
1392
1393 try_syn_eval_reduce(ctx, fuel - 1, &stepped)
1395}
1396
1397#[allow(dead_code)]
1413fn try_syn_quote_reduce(ctx: &Context, term: &Term) -> Option<Term> {
1414 fn sname_app(name: &str, arg: Term) -> Term {
1416 Term::App(
1417 Box::new(Term::App(
1418 Box::new(Term::Global("SApp".to_string())),
1419 Box::new(Term::App(
1420 Box::new(Term::Global("SName".to_string())),
1421 Box::new(Term::Lit(Literal::Text(name.to_string()))),
1422 )),
1423 )),
1424 Box::new(arg),
1425 )
1426 }
1427
1428 fn slit(n: i64) -> Term {
1430 Term::App(
1431 Box::new(Term::Global("SLit".to_string())),
1432 Box::new(Term::Lit(Literal::Int(n))),
1433 )
1434 }
1435
1436 fn sname(s: &str) -> Term {
1438 Term::App(
1439 Box::new(Term::Global("SName".to_string())),
1440 Box::new(Term::Lit(Literal::Text(s.to_string()))),
1441 )
1442 }
1443
1444 fn sname_app2(name: &str, a: Term, b: Term) -> Term {
1446 Term::App(
1447 Box::new(Term::App(
1448 Box::new(Term::Global("SApp".to_string())),
1449 Box::new(sname_app(name, a)),
1450 )),
1451 Box::new(b),
1452 )
1453 }
1454
1455 if let Term::App(ctor_term, inner_arg) = term {
1457 if let Term::Global(ctor_name) = ctor_term.as_ref() {
1458 match ctor_name.as_str() {
1459 "SVar" => {
1460 if let Term::Lit(Literal::Int(n)) = inner_arg.as_ref() {
1461 return Some(sname_app("SVar", slit(*n)));
1462 }
1463 }
1464 "SGlobal" => {
1465 if let Term::Lit(Literal::Int(n)) = inner_arg.as_ref() {
1466 return Some(sname_app("SGlobal", slit(*n)));
1467 }
1468 }
1469 "SSort" => {
1470 let quoted_univ = quote_univ(inner_arg)?;
1472 return Some(sname_app("SSort", quoted_univ));
1473 }
1474 "SLit" => {
1475 if let Term::Lit(Literal::Int(n)) = inner_arg.as_ref() {
1476 return Some(sname_app("SLit", slit(*n)));
1477 }
1478 }
1479 "SName" => {
1480 return Some(term.clone());
1482 }
1483 _ => {}
1484 }
1485 }
1486
1487 if let Term::App(inner, a) = ctor_term.as_ref() {
1489 if let Term::Global(ctor_name) = inner.as_ref() {
1490 match ctor_name.as_str() {
1491 "SApp" | "SLam" | "SPi" => {
1492 let quoted_a = try_syn_quote_reduce(ctx, a)?;
1493 let quoted_b = try_syn_quote_reduce(ctx, inner_arg)?;
1494 return Some(sname_app2(ctor_name, quoted_a, quoted_b));
1495 }
1496 _ => {}
1497 }
1498 }
1499 }
1500 }
1501
1502 None
1503}
1504
1505fn quote_univ(term: &Term) -> Option<Term> {
1510 fn sname(s: &str) -> Term {
1511 Term::App(
1512 Box::new(Term::Global("SName".to_string())),
1513 Box::new(Term::Lit(Literal::Text(s.to_string()))),
1514 )
1515 }
1516
1517 fn slit(n: i64) -> Term {
1518 Term::App(
1519 Box::new(Term::Global("SLit".to_string())),
1520 Box::new(Term::Lit(Literal::Int(n))),
1521 )
1522 }
1523
1524 fn sname_app(name: &str, arg: Term) -> Term {
1525 Term::App(
1526 Box::new(Term::App(
1527 Box::new(Term::Global("SApp".to_string())),
1528 Box::new(sname(name)),
1529 )),
1530 Box::new(arg),
1531 )
1532 }
1533
1534 if let Term::Global(name) = term {
1535 if name == "UProp" {
1536 return Some(sname("UProp"));
1537 }
1538 }
1539
1540 if let Term::App(ctor, arg) = term {
1541 if let Term::Global(name) = ctor.as_ref() {
1542 if name == "UType" {
1543 if let Term::Lit(Literal::Int(n)) = arg.as_ref() {
1544 return Some(sname_app("UType", slit(*n)));
1545 }
1546 }
1547 }
1548 }
1549
1550 None
1551}
1552
1553fn try_syn_diag_reduce(ctx: &Context, term: &Term) -> Option<Term> {
1562 let quoted = try_syn_quote_reduce(ctx, term)?;
1564
1565 try_syn_subst_reduce(ctx, "ed, 0, term)
1567}
1568
1569fn try_concludes_reduce(ctx: &Context, deriv: &Term) -> Option<Term> {
1582 if let Term::App(ctor_term, p) = deriv {
1584 if let Term::Global(ctor_name) = ctor_term.as_ref() {
1585 if ctor_name == "DAxiom" {
1586 return Some(p.as_ref().clone());
1587 }
1588 if ctor_name == "DUnivIntro" {
1589 let inner_conc = try_concludes_reduce(ctx, p)?;
1591 let lifted = try_syn_lift_reduce(ctx, 1, 0, &inner_conc)?;
1593 return Some(make_forall_syntax(&lifted));
1594 }
1595 if ctor_name == "DCompute" {
1596 return try_dcompute_conclude(ctx, p);
1598 }
1599 if ctor_name == "DRingSolve" {
1600 return try_dring_solve_conclude(ctx, p);
1602 }
1603 if ctor_name == "DLiaSolve" {
1604 return try_dlia_solve_conclude(ctx, p);
1606 }
1607 if ctor_name == "DccSolve" {
1608 return try_dcc_solve_conclude(ctx, p);
1610 }
1611 if ctor_name == "DSimpSolve" {
1612 return try_dsimp_solve_conclude(ctx, p);
1614 }
1615 if ctor_name == "DOmegaSolve" {
1616 return try_domega_solve_conclude(ctx, p);
1618 }
1619 if ctor_name == "DAutoSolve" {
1620 return try_dauto_solve_conclude(ctx, p);
1622 }
1623 if ctor_name == "DBitblastSolve" {
1624 return try_dbitblast_solve_conclude(ctx, p);
1626 }
1627 if ctor_name == "DTabulateSolve" {
1628 return try_dtabulate_solve_conclude(ctx, p);
1630 }
1631 if ctor_name == "DHwAutoSolve" {
1632 return try_dhw_auto_solve_conclude(ctx, p);
1634 }
1635 if ctor_name == "DInversion" {
1636 return try_dinversion_conclude(ctx, p);
1638 }
1639 }
1640 }
1641
1642 if let Term::App(partial1, new_goal) = deriv {
1645 if let Term::App(partial2, old_goal) = partial1.as_ref() {
1646 if let Term::App(ctor_term, eq_proof) = partial2.as_ref() {
1647 if let Term::Global(ctor_name) = ctor_term.as_ref() {
1648 if ctor_name == "DRewrite" {
1649 return try_drewrite_conclude(ctx, eq_proof, old_goal, new_goal);
1650 }
1651 }
1652 }
1653 }
1654 }
1655
1656 if let Term::App(partial1, cases) = deriv {
1659 if let Term::App(partial2, motive) = partial1.as_ref() {
1660 if let Term::App(ctor_term, ind_type) = partial2.as_ref() {
1661 if let Term::Global(ctor_name) = ctor_term.as_ref() {
1662 if ctor_name == "DDestruct" {
1663 return try_ddestruct_conclude(ctx, ind_type, motive, cases);
1664 }
1665 }
1666 }
1667 }
1668 }
1669
1670 if let Term::App(partial1, new_goal) = deriv {
1673 if let Term::App(partial2, old_goal) = partial1.as_ref() {
1674 if let Term::App(partial3, hyp_proof) = partial2.as_ref() {
1675 if let Term::App(ctor_term, hyp_name) = partial3.as_ref() {
1676 if let Term::Global(ctor_name) = ctor_term.as_ref() {
1677 if ctor_name == "DApply" {
1678 return try_dapply_conclude(ctx, hyp_name, hyp_proof, old_goal, new_goal);
1679 }
1680 }
1681 }
1682 }
1683 }
1684 }
1685
1686 if let Term::App(partial, a) = deriv {
1688 if let Term::App(ctor_term, t) = partial.as_ref() {
1689 if let Term::Global(ctor_name) = ctor_term.as_ref() {
1690 if ctor_name == "DRefl" {
1691 return Some(make_eq_syntax(t.as_ref(), a.as_ref()));
1692 }
1693 if ctor_name == "DCong" {
1694 return try_dcong_conclude(ctx, t, a);
1697 }
1698 }
1699 }
1700 }
1701
1702 if let Term::App(partial, d_ant) = deriv {
1704 if let Term::App(ctor_term, d_impl) = partial.as_ref() {
1705 if let Term::Global(ctor_name) = ctor_term.as_ref() {
1706 if ctor_name == "DModusPonens" {
1707 let impl_conc = try_concludes_reduce(ctx, d_impl)?;
1709 let ant_conc = try_concludes_reduce(ctx, d_ant)?;
1710
1711 if let Some((a, b)) = extract_implication(&impl_conc) {
1713 if syntax_equal(&ant_conc, &a) {
1715 return Some(b);
1716 }
1717 }
1718 return Some(make_sname_error());
1720 }
1721 if ctor_name == "DUnivElim" {
1722 let conc = try_concludes_reduce(ctx, d_impl)?;
1724 if let Some(body) = extract_forall_body(&conc) {
1725 return try_syn_subst_reduce(ctx, d_ant, 0, &body);
1727 }
1728 return Some(make_sname_error());
1730 }
1731 }
1732 }
1733 }
1734
1735 if let Term::App(partial1, step) = deriv {
1738 if let Term::App(partial2, base) = partial1.as_ref() {
1739 if let Term::App(ctor_term, motive) = partial2.as_ref() {
1740 if let Term::Global(ctor_name) = ctor_term.as_ref() {
1741 if ctor_name == "DInduction" {
1742 return try_dinduction_reduce(ctx, motive, base, step);
1743 }
1744 }
1745 }
1746 }
1747 }
1748
1749 if let Term::App(partial1, cases) = deriv {
1752 if let Term::App(partial2, motive) = partial1.as_ref() {
1753 if let Term::App(ctor_term, ind_type) = partial2.as_ref() {
1754 if let Term::Global(ctor_name) = ctor_term.as_ref() {
1755 if ctor_name == "DElim" {
1756 return try_delim_conclude(ctx, ind_type, motive, cases);
1757 }
1758 }
1759 }
1760 }
1761 }
1762
1763 None
1764}
1765
1766fn extract_implication(term: &Term) -> Option<(Term, Term)> {
1773 if let Term::App(outer, b) = term {
1775 if let Term::App(sapp_outer, x) = outer.as_ref() {
1776 if let Term::Global(ctor) = sapp_outer.as_ref() {
1777 if ctor == "SApp" {
1778 if let Term::App(inner, a) = x.as_ref() {
1780 if let Term::App(sapp_inner, sname_implies) = inner.as_ref() {
1781 if let Term::Global(ctor2) = sapp_inner.as_ref() {
1782 if ctor2 == "SApp" {
1783 if let Term::App(sname, text) = sname_implies.as_ref() {
1785 if let Term::Global(sname_ctor) = sname.as_ref() {
1786 if sname_ctor == "SName" {
1787 if let Term::Lit(Literal::Text(s)) = text.as_ref() {
1788 if s == "Implies" || s == "implies" {
1789 return Some((
1790 a.as_ref().clone(),
1791 b.as_ref().clone(),
1792 ));
1793 }
1794 }
1795 }
1796 }
1797 }
1798 }
1799 }
1800 }
1801 }
1802 }
1803 }
1804 }
1805 }
1806 None
1807}
1808
1809fn extract_implications(term: &Term) -> Option<(Vec<Term>, Term)> {
1814 let mut hyps = Vec::new();
1815 let mut current = term.clone();
1816
1817 while let Some((hyp, rest)) = extract_implication(¤t) {
1818 hyps.push(hyp);
1819 current = rest;
1820 }
1821
1822 if hyps.is_empty() {
1823 None
1824 } else {
1825 Some((hyps, current))
1826 }
1827}
1828
1829fn extract_forall_body(term: &Term) -> Option<Term> {
1836 if let Term::App(outer, lam) = term {
1838 if let Term::App(sapp_outer, x) = outer.as_ref() {
1839 if let Term::Global(ctor) = sapp_outer.as_ref() {
1840 if ctor == "SApp" {
1841 if let Term::App(sname, text) = x.as_ref() {
1843 if let Term::Global(sname_ctor) = sname.as_ref() {
1844 if sname_ctor == "SName" {
1845 if let Term::Lit(Literal::Text(s)) = text.as_ref() {
1846 if s == "Forall" {
1847 return extract_slam_body(lam);
1849 }
1850 }
1851 }
1852 }
1853 }
1854
1855 if let Term::App(inner, _t) = x.as_ref() {
1857 if let Term::App(sapp_inner, sname_forall) = inner.as_ref() {
1858 if let Term::Global(ctor2) = sapp_inner.as_ref() {
1859 if ctor2 == "SApp" {
1860 if let Term::App(sname, text) = sname_forall.as_ref() {
1861 if let Term::Global(sname_ctor) = sname.as_ref() {
1862 if sname_ctor == "SName" {
1863 if let Term::Lit(Literal::Text(s)) = text.as_ref() {
1864 if s == "Forall" {
1865 return extract_slam_body(lam);
1866 }
1867 }
1868 }
1869 }
1870 }
1871 }
1872 }
1873 }
1874 }
1875 }
1876 }
1877 }
1878 }
1879 None
1880}
1881
1882fn extract_slam_body(term: &Term) -> Option<Term> {
1884 if let Term::App(inner, body) = term {
1885 if let Term::App(slam, _t) = inner.as_ref() {
1886 if let Term::Global(name) = slam.as_ref() {
1887 if name == "SLam" {
1888 return Some(body.as_ref().clone());
1889 }
1890 }
1891 }
1892 }
1893 None
1894}
1895
1896fn syntax_equal(a: &Term, b: &Term) -> bool {
1898 a == b
1899}
1900
1901fn make_sname_error() -> Term {
1903 Term::App(
1904 Box::new(Term::Global("SName".to_string())),
1905 Box::new(Term::Lit(Literal::Text("Error".to_string()))),
1906 )
1907}
1908
1909fn make_sname(s: &str) -> Term {
1911 Term::App(
1912 Box::new(Term::Global("SName".to_string())),
1913 Box::new(Term::Lit(Literal::Text(s.to_string()))),
1914 )
1915}
1916
1917fn make_slit(n: i64) -> Term {
1919 Term::App(
1920 Box::new(Term::Global("SLit".to_string())),
1921 Box::new(Term::Lit(Literal::Int(n))),
1922 )
1923}
1924
1925fn make_sapp(f: Term, x: Term) -> Term {
1927 Term::App(
1928 Box::new(Term::App(
1929 Box::new(Term::Global("SApp".to_string())),
1930 Box::new(f),
1931 )),
1932 Box::new(x),
1933 )
1934}
1935
1936fn make_spi(a: Term, b: Term) -> Term {
1938 Term::App(
1939 Box::new(Term::App(
1940 Box::new(Term::Global("SPi".to_string())),
1941 Box::new(a),
1942 )),
1943 Box::new(b),
1944 )
1945}
1946
1947fn make_slam(a: Term, b: Term) -> Term {
1949 Term::App(
1950 Box::new(Term::App(
1951 Box::new(Term::Global("SLam".to_string())),
1952 Box::new(a),
1953 )),
1954 Box::new(b),
1955 )
1956}
1957
1958fn make_ssort(u: Term) -> Term {
1960 Term::App(
1961 Box::new(Term::Global("SSort".to_string())),
1962 Box::new(u),
1963 )
1964}
1965
1966fn make_svar(n: i64) -> Term {
1968 Term::App(
1969 Box::new(Term::Global("SVar".to_string())),
1970 Box::new(Term::Lit(Literal::Int(n))),
1971 )
1972}
1973
1974fn term_to_syntax(term: &Term) -> Option<Term> {
1987 match term {
1988 Term::Global(name) => Some(make_sname(name)),
1989
1990 Term::Const { name, .. } => Some(make_sname(name)),
1991
1992 Term::Var(name) => {
1993 Some(make_sname(name))
1995 }
1996
1997 Term::App(f, x) => {
1998 let sf = term_to_syntax(f)?;
1999 let sx = term_to_syntax(x)?;
2000 Some(make_sapp(sf, sx))
2001 }
2002
2003 Term::Pi { param_type, body_type, .. } => {
2004 let sp = term_to_syntax(param_type)?;
2005 let sb = term_to_syntax(body_type)?;
2006 Some(make_spi(sp, sb))
2007 }
2008
2009 Term::Lambda { param_type, body, .. } => {
2010 let sp = term_to_syntax(param_type)?;
2011 let sb = term_to_syntax(body)?;
2012 Some(make_slam(sp, sb))
2013 }
2014
2015 Term::Sort(univ) => Some(make_ssort(univ_to_syntax(univ))),
2016
2017 Term::Lit(Literal::Int(n)) => Some(make_slit(*n)),
2018
2019 Term::Lit(Literal::Text(s)) => Some(make_sname(s)),
2020
2021 Term::Lit(Literal::Float(_))
2023 | Term::Lit(Literal::Duration(_))
2024 | Term::Lit(Literal::Date(_))
2025 | Term::Lit(Literal::Moment(_))
2026 | Term::Lit(Literal::BigInt(_))
2028 | Term::Lit(Literal::Nat(_)) => None,
2029
2030 Term::Match { .. } | Term::Fix { .. } | Term::MutualFix { .. } | Term::Let { .. }
2032 | Term::Hole => None,
2033 }
2034}
2035
2036fn make_hint_derivation(hint_name: &str, goal: &Term) -> Term {
2040 let hint_marker = make_sapp(make_sname("Hint"), make_sname(hint_name));
2043
2044 Term::App(
2047 Box::new(Term::Global("DAutoSolve".to_string())),
2048 Box::new(goal.clone()),
2049 )
2050}
2051
2052fn try_apply_hint(ctx: &Context, hint_name: &str, hint_type: &Term, goal: &Term) -> Option<Term> {
2057 let hint_syntax = term_to_syntax(hint_type)?;
2059
2060 let norm_hint = normalize(ctx, &hint_syntax);
2062 let norm_goal = normalize(ctx, goal);
2063
2064 if syntax_equal(&norm_hint, &norm_goal) {
2066 return Some(make_hint_derivation(hint_name, goal));
2067 }
2068
2069 if let Term::App(outer, q) = &hint_syntax {
2072 if let Term::App(pi_ctor, p) = outer.as_ref() {
2073 if let Term::Global(name) = pi_ctor.as_ref() {
2074 if name == "SPi" {
2075 let norm_q = normalize(ctx, q);
2077 if syntax_equal(&norm_q, &norm_goal) {
2078 }
2082 }
2083 }
2084 }
2085 }
2086
2087 None
2088}
2089
2090fn make_forall_syntax(body: &Term) -> Term {
2092 let type0 = Term::App(
2093 Box::new(Term::Global("SSort".to_string())),
2094 Box::new(Term::App(
2095 Box::new(Term::Global("UType".to_string())),
2096 Box::new(Term::Lit(Literal::Int(0))),
2097 )),
2098 );
2099
2100 let slam = Term::App(
2102 Box::new(Term::App(
2103 Box::new(Term::Global("SLam".to_string())),
2104 Box::new(type0.clone()),
2105 )),
2106 Box::new(body.clone()),
2107 );
2108
2109 Term::App(
2111 Box::new(Term::App(
2112 Box::new(Term::Global("SApp".to_string())),
2113 Box::new(Term::App(
2114 Box::new(Term::App(
2115 Box::new(Term::Global("SApp".to_string())),
2116 Box::new(Term::App(
2117 Box::new(Term::Global("SName".to_string())),
2118 Box::new(Term::Lit(Literal::Text("Forall".to_string()))),
2119 )),
2120 )),
2121 Box::new(type0),
2122 )),
2123 )),
2124 Box::new(slam),
2125 )
2126}
2127
2128fn make_eq_syntax(type_s: &Term, term: &Term) -> Term {
2136 let eq_name = Term::App(
2137 Box::new(Term::Global("SName".to_string())),
2138 Box::new(Term::Lit(Literal::Text("Eq".to_string()))),
2139 );
2140
2141 let app1 = Term::App(
2143 Box::new(Term::App(
2144 Box::new(Term::Global("SApp".to_string())),
2145 Box::new(eq_name),
2146 )),
2147 Box::new(type_s.clone()),
2148 );
2149
2150 let app2 = Term::App(
2152 Box::new(Term::App(
2153 Box::new(Term::Global("SApp".to_string())),
2154 Box::new(app1),
2155 )),
2156 Box::new(term.clone()),
2157 );
2158
2159 Term::App(
2161 Box::new(Term::App(
2162 Box::new(Term::Global("SApp".to_string())),
2163 Box::new(app2),
2164 )),
2165 Box::new(term.clone()),
2166 )
2167}
2168
2169fn extract_eq(term: &Term) -> Option<(Term, Term, Term)> {
2173 if let Term::App(outer, b) = term {
2175 if let Term::App(sapp_outer, x) = outer.as_ref() {
2176 if let Term::Global(ctor) = sapp_outer.as_ref() {
2177 if ctor == "SApp" {
2178 if let Term::App(inner, a) = x.as_ref() {
2180 if let Term::App(sapp_inner, y) = inner.as_ref() {
2181 if let Term::Global(ctor2) = sapp_inner.as_ref() {
2182 if ctor2 == "SApp" {
2183 if let Term::App(inner2, t) = y.as_ref() {
2185 if let Term::App(sapp_inner2, sname_eq) = inner2.as_ref() {
2186 if let Term::Global(ctor3) = sapp_inner2.as_ref() {
2187 if ctor3 == "SApp" {
2188 if let Term::App(sname, text) = sname_eq.as_ref()
2190 {
2191 if let Term::Global(sname_ctor) =
2192 sname.as_ref()
2193 {
2194 if sname_ctor == "SName" {
2195 if let Term::Lit(Literal::Text(s)) =
2196 text.as_ref()
2197 {
2198 if s == "Eq" {
2199 return Some((
2200 t.as_ref().clone(),
2201 a.as_ref().clone(),
2202 b.as_ref().clone(),
2203 ));
2204 }
2205 }
2206 }
2207 }
2208 }
2209 }
2210 }
2211 }
2212 }
2213 }
2214 }
2215 }
2216 }
2217 }
2218 }
2219 }
2220 }
2221 None
2222}
2223
2224fn make_drefl(type_s: &Term, term: &Term) -> Term {
2226 let drefl = Term::Global("DRefl".to_string());
2227 let app1 = Term::App(Box::new(drefl), Box::new(type_s.clone()));
2228 Term::App(Box::new(app1), Box::new(term.clone()))
2229}
2230
2231fn make_error_derivation() -> Term {
2233 let daxiom = Term::Global("DAxiom".to_string());
2234 let error = make_sname_error();
2235 Term::App(Box::new(daxiom), Box::new(error))
2236}
2237
2238fn make_daxiom(goal: &Term) -> Term {
2240 let daxiom = Term::Global("DAxiom".to_string());
2241 Term::App(Box::new(daxiom), Box::new(goal.clone()))
2242}
2243
2244fn try_try_refl_reduce(ctx: &Context, goal: &Term) -> Option<Term> {
2250 let norm_goal = normalize(ctx, goal);
2252
2253 if let Some((type_s, left, right)) = extract_eq(&norm_goal) {
2255 if syntax_equal(&left, &right) {
2257 return Some(make_drefl(&type_s, &left));
2259 }
2260 }
2261
2262 Some(make_error_derivation())
2264}
2265
2266use crate::ring;
2271use crate::lia;
2272use crate::cc;
2273use crate::simp;
2274
2275fn try_try_ring_reduce(ctx: &Context, goal: &Term) -> Option<Term> {
2283 let norm_goal = normalize(ctx, goal);
2285
2286 if let Some((type_s, left, right)) = extract_eq(&norm_goal) {
2288 if !is_ring_type(&type_s) {
2290 return Some(make_error_derivation());
2291 }
2292
2293 let mut vars = crate::reify::VarInterner::new();
2296 let poly_left = match ring::reify(&left, &mut vars) {
2297 Ok(p) => p,
2298 Err(_) => return Some(make_error_derivation()),
2299 };
2300 let poly_right = match ring::reify(&right, &mut vars) {
2301 Ok(p) => p,
2302 Err(_) => return Some(make_error_derivation()),
2303 };
2304
2305 if poly_left.canonical_eq(&poly_right) {
2307 return Some(Term::App(
2309 Box::new(Term::Global("DRingSolve".to_string())),
2310 Box::new(norm_goal),
2311 ));
2312 }
2313 }
2314
2315 Some(make_error_derivation())
2317}
2318
2319fn try_dring_solve_conclude(ctx: &Context, goal: &Term) -> Option<Term> {
2323 let norm_goal = normalize(ctx, goal);
2324
2325 if let Some((type_s, left, right)) = extract_eq(&norm_goal) {
2327 if !is_ring_type(&type_s) {
2329 return Some(make_sname_error());
2330 }
2331
2332 let mut vars = crate::reify::VarInterner::new();
2334 let poly_left = match ring::reify(&left, &mut vars) {
2335 Ok(p) => p,
2336 Err(_) => return Some(make_sname_error()),
2337 };
2338 let poly_right = match ring::reify(&right, &mut vars) {
2339 Ok(p) => p,
2340 Err(_) => return Some(make_sname_error()),
2341 };
2342
2343 if poly_left.canonical_eq(&poly_right) {
2344 return Some(norm_goal);
2345 }
2346 }
2347
2348 Some(make_sname_error())
2349}
2350
2351fn is_ring_type(type_term: &Term) -> bool {
2353 if let Some(name) = extract_sname_from_syntax(type_term) {
2354 return name == "Int" || name == "Nat";
2355 }
2356 false
2357}
2358
2359fn extract_sname_from_syntax(term: &Term) -> Option<String> {
2361 if let Term::App(ctor, arg) = term {
2362 if let Term::Global(name) = ctor.as_ref() {
2363 if name == "SName" {
2364 if let Term::Lit(Literal::Text(s)) = arg.as_ref() {
2365 return Some(s.clone());
2366 }
2367 }
2368 }
2369 }
2370 None
2371}
2372
2373fn try_try_lia_reduce(ctx: &Context, goal: &Term) -> Option<Term> {
2385 let norm_goal = normalize(ctx, goal);
2387
2388 if let Some((rel, lhs_term, rhs_term)) = lia::extract_comparison(&norm_goal) {
2390 let mut vars = crate::reify::VarInterner::new();
2392 let lhs = match lia::reify_linear(&lhs_term, &mut vars) {
2393 Ok(l) => l,
2394 Err(_) => return Some(make_error_derivation()),
2395 };
2396 let rhs = match lia::reify_linear(&rhs_term, &mut vars) {
2397 Ok(r) => r,
2398 Err(_) => return Some(make_error_derivation()),
2399 };
2400
2401 if let Some(negated) = lia::goal_to_negated_constraint(&rel, &lhs, &rhs) {
2403 if lia::fourier_motzkin_unsat(&[negated]) {
2405 return Some(Term::App(
2407 Box::new(Term::Global("DLiaSolve".to_string())),
2408 Box::new(norm_goal),
2409 ));
2410 }
2411 }
2412 }
2413
2414 Some(make_error_derivation())
2416}
2417
2418fn try_dlia_solve_conclude(ctx: &Context, goal: &Term) -> Option<Term> {
2422 let norm_goal = normalize(ctx, goal);
2423
2424 if let Some((rel, lhs_term, rhs_term)) = lia::extract_comparison(&norm_goal) {
2426 let mut vars = crate::reify::VarInterner::new();
2428 let lhs = match lia::reify_linear(&lhs_term, &mut vars) {
2429 Ok(l) => l,
2430 Err(_) => return Some(make_sname_error()),
2431 };
2432 let rhs = match lia::reify_linear(&rhs_term, &mut vars) {
2433 Ok(r) => r,
2434 Err(_) => return Some(make_sname_error()),
2435 };
2436
2437 if let Some(negated) = lia::goal_to_negated_constraint(&rel, &lhs, &rhs) {
2439 if lia::fourier_motzkin_unsat(&[negated]) {
2440 return Some(norm_goal);
2441 }
2442 }
2443 }
2444
2445 Some(make_sname_error())
2446}
2447
2448fn try_try_cc_reduce(ctx: &Context, goal: &Term) -> Option<Term> {
2460 let norm_goal = normalize(ctx, goal);
2462
2463 if cc::check_goal(&norm_goal) {
2467 return Some(Term::App(
2469 Box::new(Term::Global("DccSolve".to_string())),
2470 Box::new(norm_goal),
2471 ));
2472 }
2473
2474 Some(make_error_derivation())
2476}
2477
2478fn try_dcc_solve_conclude(ctx: &Context, goal: &Term) -> Option<Term> {
2482 let norm_goal = normalize(ctx, goal);
2483
2484 if cc::check_goal(&norm_goal) {
2486 return Some(norm_goal);
2487 }
2488
2489 Some(make_sname_error())
2490}
2491
2492fn try_try_simp_reduce(ctx: &Context, goal: &Term) -> Option<Term> {
2502 let norm_goal = normalize(ctx, goal);
2504
2505 if simp::check_goal(&norm_goal) {
2510 return Some(Term::App(
2512 Box::new(Term::Global("DSimpSolve".to_string())),
2513 Box::new(norm_goal),
2514 ));
2515 }
2516
2517 Some(make_error_derivation())
2519}
2520
2521fn try_dsimp_solve_conclude(ctx: &Context, goal: &Term) -> Option<Term> {
2525 let norm_goal = normalize(ctx, goal);
2526
2527 if simp::check_goal(&norm_goal) {
2529 return Some(norm_goal);
2530 }
2531
2532 Some(make_sname_error())
2533}
2534
2535fn try_try_omega_reduce(ctx: &Context, goal: &Term) -> Option<Term> {
2552 let norm_goal = normalize(ctx, goal);
2553
2554 if let Some((hyps, conclusion)) = extract_implications(&norm_goal) {
2556 let mut vars = crate::reify::VarInterner::new();
2559 let constraints = omega_hyp_constraints(&hyps, &mut vars);
2560
2561 if let Some((rel, lhs_term, rhs_term)) = omega::extract_comparison(&conclusion) {
2563 if let (Some(lhs), Some(rhs)) = (
2564 omega::reify_int_linear(&lhs_term, &mut vars),
2565 omega::reify_int_linear(&rhs_term, &mut vars),
2566 ) {
2567 if let Some(neg_constraint) = omega::goal_to_negated_constraint(&rel, &lhs, &rhs) {
2569 let mut all_constraints = constraints;
2570 all_constraints.push(neg_constraint);
2571
2572 if omega::omega_unsat(&all_constraints) {
2573 return Some(Term::App(
2574 Box::new(Term::Global("DOmegaSolve".to_string())),
2575 Box::new(norm_goal),
2576 ));
2577 }
2578 }
2579 }
2580 }
2581
2582 return Some(make_error_derivation());
2584 }
2585
2586 if let Some((rel, lhs_term, rhs_term)) = omega::extract_comparison(&norm_goal) {
2588 let mut vars = crate::reify::VarInterner::new();
2589 if let (Some(lhs), Some(rhs)) = (
2590 omega::reify_int_linear(&lhs_term, &mut vars),
2591 omega::reify_int_linear(&rhs_term, &mut vars),
2592 ) {
2593 if let Some(constraint) = omega::goal_to_negated_constraint(&rel, &lhs, &rhs) {
2594 if omega::omega_unsat(&[constraint]) {
2595 return Some(Term::App(
2596 Box::new(Term::Global("DOmegaSolve".to_string())),
2597 Box::new(norm_goal),
2598 ));
2599 }
2600 }
2601 }
2602 }
2603
2604 Some(make_error_derivation())
2605}
2606
2607fn omega_hyp_constraints(
2612 hyps: &[Term],
2613 vars: &mut crate::reify::VarInterner,
2614) -> Vec<omega::IntConstraint> {
2615 let one = omega::IntExpr::constant(1);
2616 let mut constraints = Vec::new();
2617 for hyp in hyps {
2618 if let Some((rel, lhs_term, rhs_term)) = omega::extract_comparison(hyp) {
2619 if let (Some(lhs), Some(rhs)) = (
2620 omega::reify_int_linear(&lhs_term, vars),
2621 omega::reify_int_linear(&rhs_term, vars),
2622 ) {
2623 match rel.as_str() {
2624 "Lt" | "lt" => {
2625 constraints.push(omega::IntConstraint {
2627 expr: lhs.sub(&rhs).add(&one),
2628 strict: false,
2629 });
2630 }
2631 "Le" | "le" => {
2632 constraints.push(omega::IntConstraint {
2634 expr: lhs.sub(&rhs),
2635 strict: false,
2636 });
2637 }
2638 "Gt" | "gt" => {
2639 constraints.push(omega::IntConstraint {
2641 expr: rhs.sub(&lhs).add(&one),
2642 strict: false,
2643 });
2644 }
2645 "Ge" | "ge" => {
2646 constraints.push(omega::IntConstraint {
2648 expr: rhs.sub(&lhs),
2649 strict: false,
2650 });
2651 }
2652 _ => {}
2653 }
2654 }
2655 }
2656 }
2657 constraints
2658}
2659
2660fn try_domega_solve_conclude(ctx: &Context, goal: &Term) -> Option<Term> {
2664 let norm_goal = normalize(ctx, goal);
2665
2666 if let Some((hyps, conclusion)) = extract_implications(&norm_goal) {
2669 let mut vars = crate::reify::VarInterner::new();
2670 let constraints = omega_hyp_constraints(&hyps, &mut vars);
2671
2672 if let Some((rel, lhs_term, rhs_term)) = omega::extract_comparison(&conclusion) {
2673 if let (Some(lhs), Some(rhs)) = (
2674 omega::reify_int_linear(&lhs_term, &mut vars),
2675 omega::reify_int_linear(&rhs_term, &mut vars),
2676 ) {
2677 if let Some(neg_constraint) = omega::goal_to_negated_constraint(&rel, &lhs, &rhs) {
2678 let mut all_constraints = constraints;
2679 all_constraints.push(neg_constraint);
2680
2681 if omega::omega_unsat(&all_constraints) {
2682 return Some(norm_goal);
2683 }
2684 }
2685 }
2686 }
2687
2688 return Some(make_sname_error());
2689 }
2690
2691 if let Some((rel, lhs_term, rhs_term)) = omega::extract_comparison(&norm_goal) {
2693 let mut vars = crate::reify::VarInterner::new();
2694 if let (Some(lhs), Some(rhs)) = (
2695 omega::reify_int_linear(&lhs_term, &mut vars),
2696 omega::reify_int_linear(&rhs_term, &mut vars),
2697 ) {
2698 if let Some(constraint) = omega::goal_to_negated_constraint(&rel, &lhs, &rhs) {
2699 if omega::omega_unsat(&[constraint]) {
2700 return Some(norm_goal);
2701 }
2702 }
2703 }
2704 }
2705
2706 Some(make_sname_error())
2707}
2708
2709fn is_error_derivation(term: &Term) -> bool {
2717 if let Term::App(ctor, arg) = term {
2719 if let Term::Global(name) = ctor.as_ref() {
2720 if name == "DAxiom" {
2721 if let Term::App(sname, inner) = arg.as_ref() {
2723 if let Term::Global(sn) = sname.as_ref() {
2724 if sn == "SName" {
2725 if let Term::Lit(Literal::Text(s)) = inner.as_ref() {
2726 return s == "Error";
2727 }
2728 }
2729 if sn == "SApp" {
2730 return true; }
2733 }
2734 }
2735 if let Term::Global(sn) = arg.as_ref() {
2737 return sn == "Error";
2739 }
2740 return true; }
2742 }
2743 }
2744 false
2745}
2746
2747fn try_try_bitblast_reduce(ctx: &Context, goal: &Term) -> Option<Term> {
2760 let norm_goal = normalize(ctx, goal);
2761
2762 if let Some((type_s, left, right)) = extract_eq_syntax_parts(&norm_goal) {
2764 if !is_bit_type(&type_s) {
2766 return Some(make_error_derivation());
2767 }
2768
2769 let fuel = 1000;
2774 let left_eval = try_syn_eval_reduce(ctx, fuel, &left)?;
2775 let right_eval = try_syn_eval_reduce(ctx, fuel, &right)?;
2776
2777 if syntax_equal(&left_eval, &right_eval) {
2778 return Some(Term::App(
2779 Box::new(Term::Global("DBitblastSolve".to_string())),
2780 Box::new(norm_goal),
2781 ));
2782 }
2783 }
2784
2785 Some(make_error_derivation())
2786}
2787
2788fn syntax_to_term(syntax: &Term) -> Option<Term> {
2797 if let Term::App(ctor, inner) = syntax {
2798 if let Term::Global(name) = ctor.as_ref() {
2799 match name.as_str() {
2800 "SName" => {
2801 if let Term::Lit(Literal::Text(s)) = inner.as_ref() {
2802 return Some(Term::Global(s.clone()));
2803 }
2804 }
2805 "SLit" => {
2806 if let Term::Lit(Literal::Int(n)) = inner.as_ref() {
2807 return Some(Term::Lit(Literal::Int(*n)));
2808 }
2809 }
2810 "SVar" => {
2811 if let Term::Lit(Literal::Int(n)) = inner.as_ref() {
2812 return Some(Term::Var(format!("v{}", n)));
2813 }
2814 }
2815 "SGlobal" => {
2816 if let Term::Lit(Literal::Int(n)) = inner.as_ref() {
2817 return Some(Term::Var(format!("g{}", n)));
2818 }
2819 }
2820 _ => {}
2821 }
2822 }
2823 }
2824
2825 if let Term::App(outer, second) = syntax {
2827 if let Term::App(sctor, first) = outer.as_ref() {
2828 if let Term::Global(name) = sctor.as_ref() {
2829 match name.as_str() {
2830 "SApp" => {
2831 let f = syntax_to_term(first)?;
2832 let x = syntax_to_term(second)?;
2833 return Some(Term::App(Box::new(f), Box::new(x)));
2834 }
2835 "SLam" => {
2836 let param_type = syntax_to_term(first)?;
2837 let body = syntax_to_term(second)?;
2838 return Some(Term::Lambda {
2839 param: "_".to_string(),
2840 param_type: Box::new(param_type),
2841 body: Box::new(body),
2842 });
2843 }
2844 "SPi" => {
2845 let param_type = syntax_to_term(first)?;
2846 let body_type = syntax_to_term(second)?;
2847 return Some(Term::Pi {
2848 param: "_".to_string(),
2849 param_type: Box::new(param_type),
2850 body_type: Box::new(body_type),
2851 });
2852 }
2853 _ => {}
2854 }
2855 }
2856 }
2857 }
2858
2859 if let Term::App(outer, third) = syntax {
2862 if let Term::App(mid, second) = outer.as_ref() {
2863 if let Term::App(inner, first) = mid.as_ref() {
2864 if let Term::Global(name) = inner.as_ref() {
2865 if name == "SMatch" {
2866 let disc = syntax_to_term(first)?;
2867 let motive = syntax_to_term(second)?;
2868 let _ = third;
2870 return Some(Term::Match {
2871 discriminant: Box::new(disc),
2872 motive: Box::new(motive),
2873 cases: vec![],
2874 });
2875 }
2876 }
2877 }
2878 }
2879 }
2880
2881 None
2882}
2883
2884fn is_bit_type(term: &Term) -> bool {
2886 if let Term::App(ctor, inner) = term {
2888 if let Term::Global(name) = ctor.as_ref() {
2889 if name == "SName" {
2890 if let Term::Lit(Literal::Text(s)) = inner.as_ref() {
2891 return s == "Bit";
2892 }
2893 }
2894 }
2895 }
2896 false
2897}
2898
2899fn syntax_subst_var(term: &Term, idx: i64, replacement: &Term) -> Term {
2902 if let Term::App(ctor, inner) = term {
2904 if let Term::Global(name) = ctor.as_ref() {
2905 if name == "SVar" {
2906 if let Term::Lit(Literal::Int(n)) = inner.as_ref() {
2907 if *n == idx {
2908 return replacement.clone();
2909 } else if *n > idx {
2910 return make_svar(*n - 1);
2912 } else {
2913 return term.clone();
2914 }
2915 }
2916 }
2917 }
2918 }
2919
2920 if let Term::App(outer, second) = term {
2922 if let Term::App(sctor, first) = outer.as_ref() {
2923 if let Term::Global(name) = sctor.as_ref() {
2924 match name.as_str() {
2925 "SApp" => {
2926 let f = syntax_subst_var(first, idx, replacement);
2927 let x = syntax_subst_var(second, idx, replacement);
2928 return make_sapp(f, x);
2929 }
2930 "SPi" => {
2931 let param_t = syntax_subst_var(first, idx, replacement);
2933 let body = syntax_subst_var(second, idx + 1, replacement);
2934 return make_spi(param_t, body);
2935 }
2936 "SLam" => {
2937 let param_t = syntax_subst_var(first, idx, replacement);
2938 let body = syntax_subst_var(second, idx + 1, replacement);
2939 return make_slam(param_t, body);
2940 }
2941 _ => {}
2942 }
2943 }
2944 }
2945 }
2946
2947 term.clone()
2949}
2950
2951fn try_try_tabulate_reduce(ctx: &Context, goal: &Term) -> Option<Term> {
2962 let norm_goal = normalize(ctx, goal);
2963
2964 if let Some((param_type, body)) = extract_spi(&norm_goal) {
2966 if !is_bit_type(¶m_type) {
2968 return Some(make_error_derivation());
2969 }
2970
2971 let body_b0 = syntax_subst_var(&body, 0, &make_sname("B0"));
2973 let body_b1 = syntax_subst_var(&body, 0, &make_sname("B1"));
2974
2975 let ok_b0 = tabulate_verify_case(ctx, &body_b0);
2977 let ok_b1 = tabulate_verify_case(ctx, &body_b1);
2978
2979 if ok_b0 && ok_b1 {
2980 return Some(Term::App(
2981 Box::new(Term::Global("DTabulateSolve".to_string())),
2982 Box::new(norm_goal),
2983 ));
2984 }
2985
2986 return Some(make_error_derivation());
2987 }
2988
2989 Some(make_error_derivation())
2991}
2992
2993fn tabulate_verify_case(ctx: &Context, case: &Term) -> bool {
2998 let norm = normalize(ctx, case);
2999
3000 if let Some((param_type, _)) = extract_spi(&norm) {
3002 if is_bit_type(¶m_type) {
3003 if let Some(result) = try_try_tabulate_reduce(ctx, &norm) {
3004 return !is_error_derivation(&result);
3005 }
3006 }
3007 return false;
3008 }
3009
3010 if let Some((type_s, left, right)) = extract_eq_syntax_parts(&norm) {
3012 let fuel = 1000;
3013 if let (Some(left_eval), Some(right_eval)) = (
3014 try_syn_eval_reduce(ctx, fuel, &left),
3015 try_syn_eval_reduce(ctx, fuel, &right),
3016 ) {
3017 return syntax_equal(&left_eval, &right_eval);
3018 }
3019 if let Some(full_eval) = try_syn_eval_reduce(ctx, fuel, &norm) {
3021 if is_sname_with_value(&full_eval, "True") {
3022 return true;
3023 }
3024 }
3025 return false;
3026 }
3027
3028 let fuel = 1000;
3030 if let Some(eval) = try_syn_eval_reduce(ctx, fuel, &norm) {
3031 if is_sname_with_value(&eval, "True") {
3032 return true;
3033 }
3034 }
3035
3036 false
3037}
3038
3039fn is_sname_with_value(term: &Term, value: &str) -> bool {
3041 if let Term::App(ctor, inner) = term {
3042 if let Term::Global(name) = ctor.as_ref() {
3043 if name == "SName" {
3044 if let Term::Lit(Literal::Text(s)) = inner.as_ref() {
3045 return s == value;
3046 }
3047 }
3048 }
3049 }
3050 false
3051}
3052
3053fn try_dbitblast_solve_conclude(ctx: &Context, goal: &Term) -> Option<Term> {
3055 let norm_goal = normalize(ctx, goal);
3056 if let Some((type_s, left, right)) = extract_eq_syntax_parts(&norm_goal) {
3057 if is_bit_type(&type_s) {
3058 let fuel = 1000;
3059 let left_eval = try_syn_eval_reduce(ctx, fuel, &left)?;
3060 let right_eval = try_syn_eval_reduce(ctx, fuel, &right)?;
3061 if syntax_equal(&left_eval, &right_eval) {
3062 return Some(norm_goal);
3063 }
3064 }
3065 }
3066 None
3067}
3068
3069fn try_dtabulate_solve_conclude(ctx: &Context, goal: &Term) -> Option<Term> {
3071 let norm_goal = normalize(ctx, goal);
3072 if let Some((param_type, _)) = extract_spi(&norm_goal) {
3074 if is_bit_type(¶m_type) {
3075 if let Some(result) = try_try_tabulate_reduce(ctx, &norm_goal) {
3076 if !is_error_derivation(&result) {
3077 return Some(norm_goal);
3078 }
3079 }
3080 }
3081 }
3082 None
3083}
3084
3085fn try_dhw_auto_solve_conclude(ctx: &Context, goal: &Term) -> Option<Term> {
3087 if let Some(result) = try_dbitblast_solve_conclude(ctx, goal) {
3089 return Some(result);
3090 }
3091 try_dauto_solve_conclude(ctx, goal)
3093}
3094
3095fn try_try_hw_auto_reduce(ctx: &Context, goal: &Term) -> Option<Term> {
3097 let norm_goal = normalize(ctx, goal);
3098
3099 if let Some(result) = try_try_bitblast_reduce(ctx, &norm_goal) {
3101 if !is_error_derivation(&result) {
3102 return Some(result);
3103 }
3104 }
3105
3106 if let Some(result) = try_try_tabulate_reduce(ctx, &norm_goal) {
3108 if !is_error_derivation(&result) {
3109 return Some(result);
3110 }
3111 }
3112
3113 try_try_auto_reduce(ctx, &norm_goal)
3115}
3116
3117fn try_try_auto_reduce(ctx: &Context, goal: &Term) -> Option<Term> {
3118 let norm_goal = normalize(ctx, goal);
3119
3120 if let Term::App(ctor, inner) = &norm_goal {
3123 if let Term::Global(name) = ctor.as_ref() {
3124 if name == "SName" {
3125 if let Term::Lit(Literal::Text(s)) = inner.as_ref() {
3126 if s == "True" {
3127 return Some(Term::App(
3129 Box::new(Term::Global("DAutoSolve".to_string())),
3130 Box::new(norm_goal),
3131 ));
3132 }
3133 if s == "False" {
3134 return Some(make_error_derivation());
3136 }
3137 }
3138 }
3139 }
3140 }
3141
3142 if let Some(result) = try_try_simp_reduce(ctx, &norm_goal) {
3144 if !is_error_derivation(&result) {
3145 return Some(result);
3146 }
3147 }
3148
3149 if let Some(result) = try_try_ring_reduce(ctx, &norm_goal) {
3151 if !is_error_derivation(&result) {
3152 return Some(result);
3153 }
3154 }
3155
3156 if let Some(result) = try_try_cc_reduce(ctx, &norm_goal) {
3158 if !is_error_derivation(&result) {
3159 return Some(result);
3160 }
3161 }
3162
3163 if let Some(result) = try_try_omega_reduce(ctx, &norm_goal) {
3165 if !is_error_derivation(&result) {
3166 return Some(result);
3167 }
3168 }
3169
3170 if let Some(result) = try_try_lia_reduce(ctx, &norm_goal) {
3172 if !is_error_derivation(&result) {
3173 return Some(result);
3174 }
3175 }
3176
3177 for hint_name in ctx.get_hints() {
3179 if let Some(hint_type) = ctx.get_global(hint_name) {
3180 if let Some(result) = try_apply_hint(ctx, hint_name, hint_type, &norm_goal) {
3181 return Some(result);
3182 }
3183 }
3184 }
3185
3186 Some(make_error_derivation())
3188}
3189
3190fn try_dauto_solve_conclude(ctx: &Context, goal: &Term) -> Option<Term> {
3192 let norm_goal = normalize(ctx, goal);
3193
3194 if let Term::App(ctor, inner) = &norm_goal {
3196 if let Term::Global(name) = ctor.as_ref() {
3197 if name == "SName" {
3198 if let Term::Lit(Literal::Text(s)) = inner.as_ref() {
3199 if s == "True" {
3200 return Some(norm_goal.clone());
3201 }
3202 }
3203 }
3204 }
3205 }
3206
3207 if let Some(result) = try_try_simp_reduce(ctx, &norm_goal) {
3209 if !is_error_derivation(&result) {
3210 return Some(norm_goal.clone());
3211 }
3212 }
3213 if let Some(result) = try_try_ring_reduce(ctx, &norm_goal) {
3214 if !is_error_derivation(&result) {
3215 return Some(norm_goal.clone());
3216 }
3217 }
3218 if let Some(result) = try_try_cc_reduce(ctx, &norm_goal) {
3219 if !is_error_derivation(&result) {
3220 return Some(norm_goal.clone());
3221 }
3222 }
3223 if let Some(result) = try_try_omega_reduce(ctx, &norm_goal) {
3224 if !is_error_derivation(&result) {
3225 return Some(norm_goal.clone());
3226 }
3227 }
3228 if let Some(result) = try_try_lia_reduce(ctx, &norm_goal) {
3229 if !is_error_derivation(&result) {
3230 return Some(norm_goal.clone());
3231 }
3232 }
3233
3234 for hint_name in ctx.get_hints() {
3236 if let Some(hint_type) = ctx.get_global(hint_name) {
3237 if try_apply_hint(ctx, hint_name, hint_type, &norm_goal).is_some() {
3238 return Some(norm_goal);
3239 }
3240 }
3241 }
3242
3243 Some(make_sname_error())
3244}
3245
3246fn try_induction_num_cases_reduce(ctx: &Context, ind_type: &Term) -> Option<Term> {
3256 let ind_name = match extract_inductive_name_from_syntax(ind_type) {
3258 Some(name) => name,
3259 None => {
3260 return Some(Term::Global("Zero".to_string()));
3262 }
3263 };
3264
3265 let constructors = ctx.get_constructors(&ind_name);
3267 let num_ctors = constructors.len();
3268
3269 let mut result = Term::Global("Zero".to_string());
3271 for _ in 0..num_ctors {
3272 result = Term::App(
3273 Box::new(Term::Global("Succ".to_string())),
3274 Box::new(result),
3275 );
3276 }
3277
3278 Some(result)
3279}
3280
3281fn try_induction_base_goal_reduce(
3285 ctx: &Context,
3286 ind_type: &Term,
3287 motive: &Term,
3288) -> Option<Term> {
3289 let ind_name = match extract_inductive_name_from_syntax(ind_type) {
3291 Some(name) => name,
3292 None => return Some(make_sname_error()),
3293 };
3294
3295 let constructors = ctx.get_constructors(&ind_name);
3297 if constructors.is_empty() {
3298 return Some(make_sname_error());
3299 }
3300
3301 let motive_body = match extract_slam_body(motive) {
3303 Some(body) => body,
3304 None => return Some(make_sname_error()),
3305 };
3306
3307 let (ctor_name, _) = constructors[0];
3309 build_case_expected(ctx, ctor_name, &constructors, &motive_body, ind_type)
3310}
3311
3312fn try_induction_step_goal_reduce(
3316 ctx: &Context,
3317 ind_type: &Term,
3318 motive: &Term,
3319 ctor_idx: &Term,
3320) -> Option<Term> {
3321 let ind_name = match extract_inductive_name_from_syntax(ind_type) {
3323 Some(name) => name,
3324 None => return Some(make_sname_error()),
3325 };
3326
3327 let constructors = ctx.get_constructors(&ind_name);
3329 if constructors.is_empty() {
3330 return Some(make_sname_error());
3331 }
3332
3333 let idx = nat_to_usize(ctor_idx)?;
3335 if idx >= constructors.len() {
3336 return Some(make_sname_error());
3337 }
3338
3339 let motive_body = match extract_slam_body(motive) {
3341 Some(body) => body,
3342 None => return Some(make_sname_error()),
3343 };
3344
3345 let (ctor_name, _) = constructors[idx];
3347 build_case_expected(ctx, ctor_name, &constructors, &motive_body, ind_type)
3348}
3349
3350fn nat_to_usize(term: &Term) -> Option<usize> {
3356 match term {
3357 Term::Global(name) if name == "Zero" => Some(0),
3358 Term::App(succ, inner) => {
3359 if let Term::Global(name) = succ.as_ref() {
3360 if name == "Succ" {
3361 return nat_to_usize(inner).map(|n| n + 1);
3362 }
3363 }
3364 None
3365 }
3366 _ => None,
3367 }
3368}
3369
3370fn try_try_induction_reduce(
3376 ctx: &Context,
3377 ind_type: &Term,
3378 motive: &Term,
3379 cases: &Term,
3380) -> Option<Term> {
3381 let ind_name = match extract_inductive_name_from_syntax(ind_type) {
3383 Some(name) => name,
3384 None => return Some(make_error_derivation()),
3385 };
3386
3387 let constructors = ctx.get_constructors(&ind_name);
3389 if constructors.is_empty() {
3390 return Some(make_error_derivation());
3391 }
3392
3393 let case_proofs = match extract_case_proofs(cases) {
3395 Some(proofs) => proofs,
3396 None => return Some(make_error_derivation()),
3397 };
3398
3399 if case_proofs.len() != constructors.len() {
3401 return Some(make_error_derivation());
3402 }
3403
3404 Some(Term::App(
3406 Box::new(Term::App(
3407 Box::new(Term::App(
3408 Box::new(Term::Global("DElim".to_string())),
3409 Box::new(ind_type.clone()),
3410 )),
3411 Box::new(motive.clone()),
3412 )),
3413 Box::new(cases.clone()),
3414 ))
3415}
3416
3417fn try_dinduction_reduce(
3432 ctx: &Context,
3433 motive: &Term,
3434 base: &Term,
3435 step: &Term,
3436) -> Option<Term> {
3437 let norm_motive = normalize(ctx, motive);
3439 let norm_base = normalize(ctx, base);
3440 let norm_step = normalize(ctx, step);
3441
3442 let motive_body = match extract_slam_body(&norm_motive) {
3444 Some(body) => body,
3445 None => return Some(make_sname_error()),
3446 };
3447
3448 let zero = make_sname("Zero");
3450 let expected_base = match try_syn_subst_reduce(ctx, &zero, 0, &motive_body) {
3451 Some(b) => b,
3452 None => return Some(make_sname_error()),
3453 };
3454
3455 let base_conc = match try_concludes_reduce(ctx, &norm_base) {
3457 Some(c) => c,
3458 None => return Some(make_sname_error()),
3459 };
3460
3461 if !syntax_equal(&base_conc, &expected_base) {
3463 return Some(make_sname_error());
3464 }
3465
3466 let expected_step = match build_induction_step_formula(ctx, &motive_body) {
3468 Some(s) => s,
3469 None => return Some(make_sname_error()),
3470 };
3471
3472 let step_conc = match try_concludes_reduce(ctx, &norm_step) {
3474 Some(c) => c,
3475 None => return Some(make_sname_error()),
3476 };
3477
3478 if !syntax_equal(&step_conc, &expected_step) {
3480 return Some(make_sname_error());
3481 }
3482
3483 Some(make_forall_nat_syntax(&norm_motive))
3485}
3486
3487fn build_induction_step_formula(ctx: &Context, motive_body: &Term) -> Option<Term> {
3492 let p_k = motive_body.clone();
3494
3495 let succ_var = Term::App(
3497 Box::new(Term::App(
3498 Box::new(Term::Global("SApp".to_string())),
3499 Box::new(make_sname("Succ")),
3500 )),
3501 Box::new(Term::App(
3502 Box::new(Term::Global("SVar".to_string())),
3503 Box::new(Term::Lit(Literal::Int(0))),
3504 )),
3505 );
3506 let p_succ_k = try_syn_subst_reduce(ctx, &succ_var, 0, motive_body)?;
3507
3508 let implies_body = make_implies_syntax(&p_k, &p_succ_k);
3510
3511 let slam = Term::App(
3513 Box::new(Term::App(
3514 Box::new(Term::Global("SLam".to_string())),
3515 Box::new(make_sname("Nat")),
3516 )),
3517 Box::new(implies_body),
3518 );
3519
3520 Some(make_forall_syntax_with_type(&make_sname("Nat"), &slam))
3522}
3523
3524fn make_implies_syntax(a: &Term, b: &Term) -> Term {
3526 let app1 = Term::App(
3528 Box::new(Term::App(
3529 Box::new(Term::Global("SApp".to_string())),
3530 Box::new(make_sname("Implies")),
3531 )),
3532 Box::new(a.clone()),
3533 );
3534
3535 Term::App(
3537 Box::new(Term::App(
3538 Box::new(Term::Global("SApp".to_string())),
3539 Box::new(app1),
3540 )),
3541 Box::new(b.clone()),
3542 )
3543}
3544
3545fn make_forall_nat_syntax(motive: &Term) -> Term {
3547 make_forall_syntax_with_type(&make_sname("Nat"), motive)
3548}
3549
3550fn make_forall_syntax_with_type(type_s: &Term, body: &Term) -> Term {
3552 let app1 = Term::App(
3554 Box::new(Term::App(
3555 Box::new(Term::Global("SApp".to_string())),
3556 Box::new(make_sname("Forall")),
3557 )),
3558 Box::new(type_s.clone()),
3559 );
3560
3561 Term::App(
3563 Box::new(Term::App(
3564 Box::new(Term::Global("SApp".to_string())),
3565 Box::new(app1),
3566 )),
3567 Box::new(body.clone()),
3568 )
3569}
3570
3571fn try_dcompute_conclude(ctx: &Context, goal: &Term) -> Option<Term> {
3585 let norm_goal = normalize(ctx, goal);
3586
3587 let parts = extract_eq_syntax_parts(&norm_goal);
3590 if parts.is_none() {
3591 return Some(make_sname_error());
3593 }
3594 let (_, a, b) = parts.unwrap();
3595
3596 let fuel = 1000;
3598 let a_eval = match try_syn_eval_reduce(ctx, fuel, &a) {
3599 Some(e) => e,
3600 None => return Some(make_sname_error()),
3601 };
3602 let b_eval = match try_syn_eval_reduce(ctx, fuel, &b) {
3603 Some(e) => e,
3604 None => return Some(make_sname_error()),
3605 };
3606
3607 if syntax_equal(&a_eval, &b_eval) {
3609 Some(norm_goal)
3610 } else {
3611 Some(make_sname_error())
3612 }
3613}
3614
3615fn extract_eq_syntax_parts(term: &Term) -> Option<(Term, Term, Term)> {
3619 if let Term::App(partial2, b) = term {
3622 if let Term::App(sapp2, inner2) = partial2.as_ref() {
3623 if let Term::Global(sapp2_name) = sapp2.as_ref() {
3624 if sapp2_name != "SApp" {
3625 return None;
3626 }
3627 } else {
3628 return None;
3629 }
3630
3631 if let Term::App(partial1, a) = inner2.as_ref() {
3632 if let Term::App(sapp1, inner1) = partial1.as_ref() {
3633 if let Term::Global(sapp1_name) = sapp1.as_ref() {
3634 if sapp1_name != "SApp" {
3635 return None;
3636 }
3637 } else {
3638 return None;
3639 }
3640
3641 if let Term::App(eq_t, t) = inner1.as_ref() {
3642 if let Term::App(sapp0, eq_sname) = eq_t.as_ref() {
3643 if let Term::Global(sapp0_name) = sapp0.as_ref() {
3644 if sapp0_name != "SApp" {
3645 return None;
3646 }
3647 } else {
3648 return None;
3649 }
3650
3651 if let Term::App(sname_ctor, eq_str) = eq_sname.as_ref() {
3653 if let Term::Global(ctor) = sname_ctor.as_ref() {
3654 if ctor == "SName" {
3655 if let Term::Lit(Literal::Text(s)) = eq_str.as_ref() {
3656 if s == "Eq" {
3657 return Some((
3658 t.as_ref().clone(),
3659 a.as_ref().clone(),
3660 b.as_ref().clone(),
3661 ));
3662 }
3663 }
3664 }
3665 }
3666 }
3667 }
3668 }
3669 }
3670 }
3671 }
3672 }
3673 None
3674}
3675
3676fn try_tact_orelse_reduce(
3686 ctx: &Context,
3687 t1: &Term,
3688 t2: &Term,
3689 goal: &Term,
3690) -> Option<Term> {
3691 let norm_goal = normalize(ctx, goal);
3692
3693 let d1_app = Term::App(Box::new(t1.clone()), Box::new(norm_goal.clone()));
3695 let d1 = normalize(ctx, &d1_app);
3696
3697 if let Some(conc1) = try_concludes_reduce(ctx, &d1) {
3699 if is_error_syntax(&conc1) {
3700 let d2_app = Term::App(Box::new(t2.clone()), Box::new(norm_goal));
3702 return Some(normalize(ctx, &d2_app));
3703 } else {
3704 return Some(d1);
3706 }
3707 }
3708
3709 Some(make_error_derivation())
3711}
3712
3713fn is_error_syntax(term: &Term) -> bool {
3715 if let Term::App(ctor, arg) = term {
3717 if let Term::Global(name) = ctor.as_ref() {
3718 if name == "SName" {
3719 if let Term::Lit(Literal::Text(s)) = arg.as_ref() {
3720 return s == "Error";
3721 }
3722 }
3723 }
3724 }
3725 false
3726}
3727
3728fn try_tact_try_reduce(ctx: &Context, t: &Term, goal: &Term) -> Option<Term> {
3734 let norm_goal = normalize(ctx, goal);
3735
3736 let d_app = Term::App(Box::new(t.clone()), Box::new(norm_goal.clone()));
3738 let d = normalize(ctx, &d_app);
3739
3740 if let Some(conc) = try_concludes_reduce(ctx, &d) {
3742 if is_error_syntax(&conc) {
3743 return Some(make_daxiom(&norm_goal));
3745 } else {
3746 return Some(d);
3748 }
3749 }
3750
3751 Some(make_daxiom(&norm_goal))
3753}
3754
3755fn try_tact_repeat_reduce(ctx: &Context, t: &Term, goal: &Term) -> Option<Term> {
3760 const MAX_ITERATIONS: usize = 100;
3761
3762 let norm_goal = normalize(ctx, goal);
3763 let mut current_goal = norm_goal.clone();
3764 let mut last_successful_deriv: Option<Term> = None;
3765
3766 for _ in 0..MAX_ITERATIONS {
3767 let d_app = Term::App(Box::new(t.clone()), Box::new(current_goal.clone()));
3769 let d = normalize(ctx, &d_app);
3770
3771 if let Some(conc) = try_concludes_reduce(ctx, &d) {
3773 if is_error_syntax(&conc) {
3774 break;
3776 }
3777
3778 if syntax_equal(&conc, ¤t_goal) {
3780 break;
3782 }
3783
3784 current_goal = conc;
3786 last_successful_deriv = Some(d);
3787 } else {
3788 break;
3790 }
3791 }
3792
3793 last_successful_deriv.or_else(|| Some(make_daxiom(&norm_goal)))
3795}
3796
3797fn try_tact_then_reduce(
3803 ctx: &Context,
3804 t1: &Term,
3805 t2: &Term,
3806 goal: &Term,
3807) -> Option<Term> {
3808 let norm_goal = normalize(ctx, goal);
3809
3810 let d1_app = Term::App(Box::new(t1.clone()), Box::new(norm_goal.clone()));
3812 let d1 = normalize(ctx, &d1_app);
3813
3814 if let Some(conc1) = try_concludes_reduce(ctx, &d1) {
3816 if is_error_syntax(&conc1) {
3817 return Some(make_error_derivation());
3819 }
3820
3821 let d2_app = Term::App(Box::new(t2.clone()), Box::new(conc1));
3823 let d2 = normalize(ctx, &d2_app);
3824
3825 return Some(d2);
3827 }
3828
3829 Some(make_error_derivation())
3831}
3832
3833fn try_tact_first_reduce(ctx: &Context, tactics: &Term, goal: &Term) -> Option<Term> {
3838 let norm_goal = normalize(ctx, goal);
3839
3840 let tactic_vec = extract_tlist(tactics)?;
3842
3843 for tactic in tactic_vec {
3844 let d_app = Term::App(Box::new(tactic), Box::new(norm_goal.clone()));
3846 let d = normalize(ctx, &d_app);
3847
3848 if let Some(conc) = try_concludes_reduce(ctx, &d) {
3850 if !is_error_syntax(&conc) {
3851 return Some(d);
3853 }
3854 }
3856 }
3857
3858 Some(make_error_derivation())
3860}
3861
3862fn extract_tlist(term: &Term) -> Option<Vec<Term>> {
3869 let mut result = Vec::new();
3870 let mut current = term.clone();
3871
3872 loop {
3873 match ¤t {
3874 Term::App(tnil, _type) => {
3876 if let Term::Global(name) = tnil.as_ref() {
3877 if name == "TNil" {
3878 break;
3880 }
3881 }
3882 if let Term::App(partial2, tail) = ¤t {
3884 if let Term::App(partial1, head) = partial2.as_ref() {
3885 if let Term::App(tcons, _type) = partial1.as_ref() {
3886 if let Term::Global(name) = tcons.as_ref() {
3887 if name == "TCons" {
3888 result.push(head.as_ref().clone());
3889 current = tail.as_ref().clone();
3890 continue;
3891 }
3892 }
3893 }
3894 }
3895 }
3896 return None;
3898 }
3899 Term::Global(name) if name == "TNil" => {
3901 break;
3902 }
3903 _ => {
3904 return None;
3906 }
3907 }
3908 }
3909
3910 Some(result)
3911}
3912
3913fn try_tact_solve_reduce(ctx: &Context, t: &Term, goal: &Term) -> Option<Term> {
3919 let norm_goal = normalize(ctx, goal);
3920
3921 let d_app = Term::App(Box::new(t.clone()), Box::new(norm_goal.clone()));
3923 let d = normalize(ctx, &d_app);
3924
3925 if let Some(conc) = try_concludes_reduce(ctx, &d) {
3927 if is_error_syntax(&conc) {
3928 return Some(make_error_derivation());
3930 }
3931 return Some(d);
3933 }
3934
3935 Some(make_error_derivation())
3937}
3938
3939fn try_dcong_conclude(ctx: &Context, context: &Term, eq_proof: &Term) -> Option<Term> {
3950 let eq_conc = try_concludes_reduce(ctx, eq_proof)?;
3952
3953 let parts = extract_eq_syntax_parts(&eq_conc);
3955 if parts.is_none() {
3956 return Some(make_sname_error());
3958 }
3959 let (_type_term, lhs, rhs) = parts.unwrap();
3960
3961 let norm_context = normalize(ctx, context);
3963
3964 let slam_parts = extract_slam_parts(&norm_context);
3966 if slam_parts.is_none() {
3967 return Some(make_sname_error());
3969 }
3970 let (param_type, body) = slam_parts.unwrap();
3971
3972 let fa = try_syn_subst_reduce(ctx, &lhs, 0, &body)?;
3974 let fb = try_syn_subst_reduce(ctx, &rhs, 0, &body)?;
3975
3976 Some(make_eq_syntax_three(¶m_type, &fa, &fb))
3978}
3979
3980fn extract_slam_parts(term: &Term) -> Option<(Term, Term)> {
3984 if let Term::App(inner, body) = term {
3985 if let Term::App(slam_ctor, param_type) = inner.as_ref() {
3986 if let Term::Global(name) = slam_ctor.as_ref() {
3987 if name == "SLam" {
3988 return Some((param_type.as_ref().clone(), body.as_ref().clone()));
3989 }
3990 }
3991 }
3992 }
3993 None
3994}
3995
3996fn make_eq_syntax_three(type_s: &Term, a: &Term, b: &Term) -> Term {
4000 let eq_name = Term::App(
4001 Box::new(Term::Global("SName".to_string())),
4002 Box::new(Term::Lit(Literal::Text("Eq".to_string()))),
4003 );
4004
4005 let app1 = Term::App(
4007 Box::new(Term::App(
4008 Box::new(Term::Global("SApp".to_string())),
4009 Box::new(eq_name),
4010 )),
4011 Box::new(type_s.clone()),
4012 );
4013
4014 let app2 = Term::App(
4016 Box::new(Term::App(
4017 Box::new(Term::Global("SApp".to_string())),
4018 Box::new(app1),
4019 )),
4020 Box::new(a.clone()),
4021 );
4022
4023 Term::App(
4025 Box::new(Term::App(
4026 Box::new(Term::Global("SApp".to_string())),
4027 Box::new(app2),
4028 )),
4029 Box::new(b.clone()),
4030 )
4031}
4032
4033fn try_delim_conclude(
4049 ctx: &Context,
4050 ind_type: &Term,
4051 motive: &Term,
4052 cases: &Term,
4053) -> Option<Term> {
4054 let norm_ind_type = normalize(ctx, ind_type);
4056 let norm_motive = normalize(ctx, motive);
4057 let norm_cases = normalize(ctx, cases);
4058
4059 let ind_name = match extract_inductive_name_from_syntax(&norm_ind_type) {
4061 Some(name) => name,
4062 None => return Some(make_sname_error()),
4063 };
4064
4065 let constructors = ctx.get_constructors(&ind_name);
4067 if constructors.is_empty() {
4068 return Some(make_sname_error());
4070 }
4071
4072 let case_proofs = match extract_case_proofs(&norm_cases) {
4074 Some(proofs) => proofs,
4075 None => return Some(make_sname_error()),
4076 };
4077
4078 if case_proofs.len() != constructors.len() {
4080 return Some(make_sname_error());
4081 }
4082
4083 let motive_body = match extract_slam_body(&norm_motive) {
4085 Some(body) => body,
4086 None => return Some(make_sname_error()),
4087 };
4088
4089 for (i, (ctor_name, _ctor_type)) in constructors.iter().enumerate() {
4091 let case_proof = &case_proofs[i];
4092
4093 let case_conc = match try_concludes_reduce(ctx, case_proof) {
4095 Some(c) => c,
4096 None => return Some(make_sname_error()),
4097 };
4098
4099 let expected = match build_case_expected(ctx, ctor_name, &constructors, &motive_body, &norm_ind_type) {
4103 Some(e) => e,
4104 None => return Some(make_sname_error()),
4105 };
4106
4107 if !syntax_equal(&case_conc, &expected) {
4109 return Some(make_sname_error());
4110 }
4111 }
4112
4113 Some(make_forall_syntax_generic(&norm_ind_type, &norm_motive))
4115}
4116
4117fn extract_inductive_name_from_syntax(term: &Term) -> Option<String> {
4123 if let Term::App(sname, text) = term {
4125 if let Term::Global(ctor) = sname.as_ref() {
4126 if ctor == "SName" {
4127 if let Term::Lit(Literal::Text(name)) = text.as_ref() {
4128 return Some(name.clone());
4129 }
4130 }
4131 }
4132 }
4133
4134 if let Term::App(inner, _arg) = term {
4136 if let Term::App(sapp, func) = inner.as_ref() {
4137 if let Term::Global(ctor) = sapp.as_ref() {
4138 if ctor == "SApp" {
4139 return extract_inductive_name_from_syntax(func);
4141 }
4142 }
4143 }
4144 }
4145
4146 None
4147}
4148
4149fn extract_case_proofs(term: &Term) -> Option<Vec<Term>> {
4153 let mut proofs = Vec::new();
4154 let mut current = term;
4155
4156 loop {
4157 if let Term::Global(name) = current {
4159 if name == "DCaseEnd" {
4160 return Some(proofs);
4161 }
4162 }
4163
4164 if let Term::App(inner, tail) = current {
4166 if let Term::App(dcase, head) = inner.as_ref() {
4167 if let Term::Global(name) = dcase.as_ref() {
4168 if name == "DCase" {
4169 proofs.push(head.as_ref().clone());
4170 current = tail.as_ref();
4171 continue;
4172 }
4173 }
4174 }
4175 }
4176
4177 return None;
4179 }
4180}
4181
4182fn build_case_expected(
4187 ctx: &Context,
4188 ctor_name: &str,
4189 _constructors: &[(&str, &Term)],
4190 motive_body: &Term,
4191 ind_type: &Term,
4192) -> Option<Term> {
4193 let ind_name = extract_inductive_name_from_syntax(ind_type)?;
4195
4196 if ind_name == "Nat" {
4198 if ctor_name == "Zero" {
4199 let zero = make_sname("Zero");
4201 return try_syn_subst_reduce(ctx, &zero, 0, motive_body);
4202 } else if ctor_name == "Succ" {
4203 return build_induction_step_formula(ctx, motive_body);
4206 }
4207 }
4208
4209 let ctor_syntax = make_sname(ctor_name);
4212
4213 let ctor_applied = apply_type_args_to_ctor(&ctor_syntax, ind_type);
4216
4217 if let Some(ctor_ty) = ctx.get_global(ctor_name) {
4219 if is_recursive_constructor(ctx, ctor_ty, &ind_name, ind_type) {
4221 return build_recursive_case_formula(ctx, ctor_name, ctor_ty, motive_body, ind_type, &ind_name);
4223 }
4224 }
4225
4226 try_syn_subst_reduce(ctx, &ctor_applied, 0, motive_body)
4228}
4229
4230fn apply_type_args_to_ctor(ctor: &Term, ind_type: &Term) -> Term {
4235 let args = extract_type_args(ind_type);
4237
4238 if args.is_empty() {
4239 return ctor.clone();
4240 }
4241
4242 args.iter().fold(ctor.clone(), |acc, arg| {
4244 Term::App(
4245 Box::new(Term::App(
4246 Box::new(Term::Global("SApp".to_string())),
4247 Box::new(acc),
4248 )),
4249 Box::new(arg.clone()),
4250 )
4251 })
4252}
4253
4254fn extract_type_args(term: &Term) -> Vec<Term> {
4260 let mut args = Vec::new();
4261 let mut current = term;
4262
4263 loop {
4265 if let Term::App(inner, arg) = current {
4266 if let Term::App(sapp, func) = inner.as_ref() {
4267 if let Term::Global(ctor) = sapp.as_ref() {
4268 if ctor == "SApp" {
4269 args.push(arg.as_ref().clone());
4270 current = func.as_ref();
4271 continue;
4272 }
4273 }
4274 }
4275 }
4276 break;
4277 }
4278
4279 args.reverse();
4281 args
4282}
4283
4284fn make_forall_syntax_generic(ind_type: &Term, motive: &Term) -> Term {
4288 let forall_type = Term::App(
4290 Box::new(Term::App(
4291 Box::new(Term::Global("SApp".to_string())),
4292 Box::new(make_sname("Forall")),
4293 )),
4294 Box::new(ind_type.clone()),
4295 );
4296
4297 Term::App(
4299 Box::new(Term::App(
4300 Box::new(Term::Global("SApp".to_string())),
4301 Box::new(forall_type),
4302 )),
4303 Box::new(motive.clone()),
4304 )
4305}
4306
4307fn is_recursive_constructor(
4309 _ctx: &Context,
4310 ctor_ty: &Term,
4311 ind_name: &str,
4312 _ind_type: &Term,
4313) -> bool {
4314 fn contains_inductive(term: &Term, ind_name: &str) -> bool {
4319 match term {
4320 Term::Global(name) => name == ind_name,
4321 Term::App(f, a) => {
4322 contains_inductive(f, ind_name) || contains_inductive(a, ind_name)
4323 }
4324 Term::Pi { param_type, body_type, .. } => {
4325 contains_inductive(param_type, ind_name) || contains_inductive(body_type, ind_name)
4326 }
4327 Term::Lambda { param_type, body, .. } => {
4328 contains_inductive(param_type, ind_name) || contains_inductive(body, ind_name)
4329 }
4330 _ => false,
4331 }
4332 }
4333
4334 fn check_params(term: &Term, ind_name: &str) -> bool {
4336 match term {
4337 Term::Pi { param_type, body_type, .. } => {
4338 if contains_inductive(param_type, ind_name) {
4340 return true;
4341 }
4342 check_params(body_type, ind_name)
4344 }
4345 _ => false,
4346 }
4347 }
4348
4349 check_params(ctor_ty, ind_name)
4350}
4351
4352fn build_recursive_case_formula(
4358 ctx: &Context,
4359 ctor_name: &str,
4360 ctor_ty: &Term,
4361 motive_body: &Term,
4362 ind_type: &Term,
4363 ind_name: &str,
4364) -> Option<Term> {
4365 let type_args = extract_type_args(ind_type);
4367
4368 let args = collect_ctor_args(ctor_ty, ind_name, &type_args);
4370
4371 if args.is_empty() {
4372 let ctor_applied = apply_type_args_to_ctor(&make_sname(ctor_name), ind_type);
4374 return try_syn_subst_reduce(ctx, &ctor_applied, 0, motive_body);
4375 }
4376
4377 let mut ctor_app = apply_type_args_to_ctor(&make_sname(ctor_name), ind_type);
4385 for (i, _) in args.iter().enumerate() {
4386 let idx = (args.len() - 1 - i) as i64;
4388 let var = Term::App(
4389 Box::new(Term::Global("SVar".to_string())),
4390 Box::new(Term::Lit(Literal::Int(idx))),
4391 );
4392 ctor_app = Term::App(
4393 Box::new(Term::App(
4394 Box::new(Term::Global("SApp".to_string())),
4395 Box::new(ctor_app),
4396 )),
4397 Box::new(var),
4398 );
4399 }
4400
4401 let p_ctor = try_syn_subst_reduce(ctx, &ctor_app, 0, motive_body)?;
4403
4404 let mut body = p_ctor;
4406 for (i, (arg_ty, is_recursive)) in args.iter().enumerate().rev() {
4407 if *is_recursive {
4408 let idx = (args.len() - 1 - i) as i64;
4411 let var = Term::App(
4412 Box::new(Term::Global("SVar".to_string())),
4413 Box::new(Term::Lit(Literal::Int(idx))),
4414 );
4415 let p_arg = try_syn_subst_reduce(ctx, &var, 0, motive_body)?;
4416 body = make_implies_syntax(&p_arg, &body);
4417 }
4418 let _ = (i, arg_ty); }
4421
4422 for (arg_ty, _) in args.iter().rev() {
4424 let slam = Term::App(
4426 Box::new(Term::App(
4427 Box::new(Term::Global("SLam".to_string())),
4428 Box::new(arg_ty.clone()),
4429 )),
4430 Box::new(body.clone()),
4431 );
4432 body = make_forall_syntax_with_type(arg_ty, &slam);
4434 }
4435
4436 Some(body)
4437}
4438
4439fn collect_ctor_args(ctor_ty: &Term, ind_name: &str, type_args: &[Term]) -> Vec<(Term, bool)> {
4442 let mut args = Vec::new();
4443 let mut current = ctor_ty;
4444 let mut skip_count = type_args.len();
4445
4446 loop {
4447 match current {
4448 Term::Pi { param_type, body_type, .. } => {
4449 if skip_count > 0 {
4450 skip_count -= 1;
4452 } else {
4453 let is_recursive = contains_inductive_term(param_type, ind_name);
4455 let arg_ty_syntax = kernel_type_to_syntax(param_type);
4457 args.push((arg_ty_syntax, is_recursive));
4458 }
4459 current = body_type;
4460 }
4461 _ => break,
4462 }
4463 }
4464
4465 args
4466}
4467
4468fn contains_inductive_term(term: &Term, ind_name: &str) -> bool {
4470 match term {
4471 Term::Global(name) => name == ind_name,
4472 Term::App(f, a) => {
4473 contains_inductive_term(f, ind_name) || contains_inductive_term(a, ind_name)
4474 }
4475 Term::Pi { param_type, body_type, .. } => {
4476 contains_inductive_term(param_type, ind_name) || contains_inductive_term(body_type, ind_name)
4477 }
4478 Term::Lambda { param_type, body, .. } => {
4479 contains_inductive_term(param_type, ind_name) || contains_inductive_term(body, ind_name)
4480 }
4481 _ => false,
4482 }
4483}
4484
4485fn kernel_type_to_syntax(term: &Term) -> Term {
4487 match term {
4488 Term::Global(name) => make_sname(name),
4489 Term::Var(name) => make_sname(name), Term::App(f, a) => {
4491 let f_syn = kernel_type_to_syntax(f);
4492 let a_syn = kernel_type_to_syntax(a);
4493 Term::App(
4495 Box::new(Term::App(
4496 Box::new(Term::Global("SApp".to_string())),
4497 Box::new(f_syn),
4498 )),
4499 Box::new(a_syn),
4500 )
4501 }
4502 Term::Pi { param, param_type, body_type } => {
4503 let pt_syn = kernel_type_to_syntax(param_type);
4504 let bt_syn = kernel_type_to_syntax(body_type);
4505 Term::App(
4507 Box::new(Term::App(
4508 Box::new(Term::Global("SPi".to_string())),
4509 Box::new(pt_syn),
4510 )),
4511 Box::new(bt_syn),
4512 )
4513 }
4514 Term::Sort(univ) => {
4515 Term::App(
4517 Box::new(Term::Global("SSort".to_string())),
4518 Box::new(univ_to_syntax(univ)),
4519 )
4520 }
4521 Term::Lit(lit) => {
4522 Term::App(
4524 Box::new(Term::Global("SLit".to_string())),
4525 Box::new(Term::Lit(lit.clone())),
4526 )
4527 }
4528 _ => {
4529 make_sname("Unknown")
4531 }
4532 }
4533}
4534
4535fn univ_to_syntax(univ: &crate::term::Universe) -> Term {
4537 use crate::term::Universe;
4538 match univ {
4539 Universe::SProp => Term::Global("USProp".to_string()),
4540 Universe::Prop => Term::Global("UProp".to_string()),
4541 Universe::Type(n) => Term::App(
4542 Box::new(Term::Global("UType".to_string())),
4543 Box::new(Term::Lit(Literal::Int(*n as i64))),
4544 ),
4545 Universe::Var(v) => Term::App(
4547 Box::new(Term::Global("UVar".to_string())),
4548 Box::new(Term::Global(v.clone())),
4549 ),
4550 Universe::Succ(l) => Term::App(
4551 Box::new(Term::Global("USucc".to_string())),
4552 Box::new(univ_to_syntax(l)),
4553 ),
4554 Universe::Max(a, b) => Term::App(
4555 Box::new(Term::App(
4556 Box::new(Term::Global("UMax".to_string())),
4557 Box::new(univ_to_syntax(a)),
4558 )),
4559 Box::new(univ_to_syntax(b)),
4560 ),
4561 Universe::IMax(a, b) => Term::App(
4562 Box::new(Term::App(
4563 Box::new(Term::Global("UIMax".to_string())),
4564 Box::new(univ_to_syntax(a)),
4565 )),
4566 Box::new(univ_to_syntax(b)),
4567 ),
4568 }
4569}
4570
4571fn try_try_inversion_reduce(ctx: &Context, goal: &Term) -> Option<Term> {
4580 let (ind_name, hyp_args) = match extract_applied_inductive_from_syntax(goal) {
4582 Some((name, args)) => (name, args),
4583 None => return Some(make_error_derivation()),
4584 };
4585
4586 if !ctx.is_inductive(&ind_name) {
4588 return Some(make_error_derivation());
4590 }
4591
4592 let constructors = ctx.get_constructors(&ind_name);
4594
4595 let mut any_possible = false;
4597 for (_ctor_name, ctor_type) in constructors.iter() {
4598 if can_constructor_match_args(ctx, ctor_type, &hyp_args, &ind_name) {
4599 any_possible = true;
4600 break;
4601 }
4602 }
4603
4604 if any_possible {
4605 return Some(make_error_derivation());
4607 }
4608
4609 Some(Term::App(
4611 Box::new(Term::Global("DInversion".to_string())),
4612 Box::new(goal.clone()),
4613 ))
4614}
4615
4616fn try_dinversion_conclude(ctx: &Context, hyp_type: &Term) -> Option<Term> {
4618 let norm_hyp = normalize(ctx, hyp_type);
4619
4620 let (ind_name, hyp_args) = match extract_applied_inductive_from_syntax(&norm_hyp) {
4621 Some((name, args)) => (name, args),
4622 None => return Some(make_sname_error()),
4623 };
4624
4625 if !ctx.is_inductive(&ind_name) {
4627 return Some(make_sname_error());
4628 }
4629
4630 let constructors = ctx.get_constructors(&ind_name);
4631
4632 for (_ctor_name, ctor_type) in constructors.iter() {
4634 if can_constructor_match_args(ctx, ctor_type, &hyp_args, &ind_name) {
4635 return Some(make_sname_error());
4636 }
4637 }
4638
4639 Some(make_sname("False"))
4641}
4642
4643fn extract_applied_inductive_from_syntax(term: &Term) -> Option<(String, Vec<Term>)> {
4648 if let Term::App(ctor, text) = term {
4650 if let Term::Global(ctor_name) = ctor.as_ref() {
4651 if ctor_name == "SName" {
4652 if let Term::Lit(Literal::Text(name)) = text.as_ref() {
4653 return Some((name.clone(), vec![]));
4654 }
4655 }
4656 }
4657 }
4658
4659 if let Term::App(inner, arg) = term {
4661 if let Term::App(sapp_ctor, func) = inner.as_ref() {
4662 if let Term::Global(ctor_name) = sapp_ctor.as_ref() {
4663 if ctor_name == "SApp" {
4664 let (name, mut args) = extract_applied_inductive_from_syntax(func)?;
4666 args.push(arg.as_ref().clone());
4667 return Some((name, args));
4668 }
4669 }
4670 }
4671 }
4672
4673 None
4674}
4675
4676fn can_constructor_match_args(
4685 ctx: &Context,
4686 ctor_type: &Term,
4687 hyp_args: &[Term],
4688 ind_name: &str,
4689) -> bool {
4690 let (result, pattern_vars) = decompose_ctor_type_with_vars(ctor_type);
4692
4693 let result_args = match extract_applied_inductive_from_syntax(&kernel_type_to_syntax(&result)) {
4695 Some((name, args)) if name == *ind_name => args,
4696 _ => return false,
4697 };
4698
4699 if result_args.len() != hyp_args.len() {
4701 return false;
4702 }
4703
4704 let mut bindings: std::collections::HashMap<String, Term> = std::collections::HashMap::new();
4706
4707 for (pattern, concrete) in result_args.iter().zip(hyp_args.iter()) {
4708 if !can_unify_syntax_terms_with_bindings(ctx, pattern, concrete, &pattern_vars, &mut bindings) {
4709 return false;
4710 }
4711 }
4712
4713 true
4717}
4718
4719fn decompose_ctor_type_with_vars(ty: &Term) -> (Term, Vec<String>) {
4725 let mut vars = Vec::new();
4726 let mut current = ty;
4727 loop {
4728 match current {
4729 Term::Pi { param, body_type, .. } => {
4730 vars.push(param.clone());
4731 current = body_type;
4732 }
4733 _ => break,
4734 }
4735 }
4736 (current.clone(), vars)
4737}
4738
4739fn can_unify_syntax_terms_with_bindings(
4746 ctx: &Context,
4747 pattern: &Term,
4748 concrete: &Term,
4749 pattern_vars: &[String],
4750 bindings: &mut std::collections::HashMap<String, Term>,
4751) -> bool {
4752 if let Term::App(ctor, _idx) = pattern {
4754 if let Term::Global(name) = ctor.as_ref() {
4755 if name == "SVar" {
4756 return true;
4757 }
4758 }
4759 }
4760
4761 if let Term::App(ctor1, text1) = pattern {
4763 if let Term::Global(n1) = ctor1.as_ref() {
4764 if n1 == "SName" {
4765 if let Term::Lit(Literal::Text(var_name)) = text1.as_ref() {
4766 if pattern_vars.contains(var_name) {
4768 if let Some(existing) = bindings.get(var_name) {
4770 return syntax_terms_equal(existing, concrete);
4772 } else {
4773 bindings.insert(var_name.clone(), concrete.clone());
4775 return true;
4776 }
4777 }
4778 }
4779 if let Term::App(ctor2, text2) = concrete {
4781 if let Term::Global(n2) = ctor2.as_ref() {
4782 if n2 == "SName" {
4783 return text1 == text2;
4784 }
4785 }
4786 }
4787 return false;
4788 }
4789 }
4790 }
4791
4792 if let (Term::App(inner1, arg1), Term::App(inner2, arg2)) = (pattern, concrete) {
4794 if let (Term::App(sapp1, func1), Term::App(sapp2, func2)) =
4795 (inner1.as_ref(), inner2.as_ref())
4796 {
4797 if let (Term::Global(n1), Term::Global(n2)) = (sapp1.as_ref(), sapp2.as_ref()) {
4798 if n1 == "SApp" && n2 == "SApp" {
4799 return can_unify_syntax_terms_with_bindings(ctx, func1, func2, pattern_vars, bindings)
4800 && can_unify_syntax_terms_with_bindings(ctx, arg1.as_ref(), arg2.as_ref(), pattern_vars, bindings);
4801 }
4802 }
4803 }
4804 }
4805
4806 if let (Term::App(ctor1, lit1), Term::App(ctor2, lit2)) = (pattern, concrete) {
4808 if let (Term::Global(n1), Term::Global(n2)) = (ctor1.as_ref(), ctor2.as_ref()) {
4809 if n1 == "SLit" && n2 == "SLit" {
4810 return lit1 == lit2;
4811 }
4812 }
4813 }
4814
4815 pattern == concrete
4817}
4818
4819fn syntax_terms_equal(a: &Term, b: &Term) -> bool {
4821 match (a, b) {
4822 (Term::App(f1, x1), Term::App(f2, x2)) => {
4823 syntax_terms_equal(f1, f2) && syntax_terms_equal(x1, x2)
4824 }
4825 (Term::Global(n1), Term::Global(n2)) => n1 == n2,
4826 (Term::Lit(l1), Term::Lit(l2)) => l1 == l2,
4827 _ => a == b,
4828 }
4829}
4830
4831fn extract_eq_components_from_syntax(term: &Term) -> Option<(Term, Term, Term)> {
4839 let (eq_a_x, y) = extract_sapp(term)?;
4844
4845 let (eq_a, x) = extract_sapp(&eq_a_x)?;
4847
4848 let (eq, a) = extract_sapp(&eq_a)?;
4850
4851 let eq_name = extract_sname(&eq)?;
4853 if eq_name != "Eq" {
4854 return None;
4855 }
4856
4857 Some((a, x, y))
4858}
4859
4860fn extract_sapp(term: &Term) -> Option<(Term, Term)> {
4862 if let Term::App(inner, x) = term {
4864 if let Term::App(sapp_ctor, f) = inner.as_ref() {
4865 if let Term::Global(ctor_name) = sapp_ctor.as_ref() {
4866 if ctor_name == "SApp" {
4867 return Some((f.as_ref().clone(), x.as_ref().clone()));
4868 }
4869 }
4870 }
4871 }
4872 None
4873}
4874
4875fn extract_sname(term: &Term) -> Option<String> {
4877 if let Term::App(ctor, text) = term {
4878 if let Term::Global(ctor_name) = ctor.as_ref() {
4879 if ctor_name == "SName" {
4880 if let Term::Lit(Literal::Text(name)) = text.as_ref() {
4881 return Some(name.clone());
4882 }
4883 }
4884 }
4885 }
4886 None
4887}
4888
4889fn contains_subterm_syntax(term: &Term, target: &Term) -> bool {
4891 if syntax_equal(term, target) {
4892 return true;
4893 }
4894
4895 if let Some((f, x)) = extract_sapp(term) {
4897 if contains_subterm_syntax(&f, target) || contains_subterm_syntax(&x, target) {
4898 return true;
4899 }
4900 }
4901
4902 if let Some((t, body)) = extract_slam(term) {
4904 if contains_subterm_syntax(&t, target) || contains_subterm_syntax(&body, target) {
4905 return true;
4906 }
4907 }
4908
4909 if let Some((t, body)) = extract_spi(term) {
4911 if contains_subterm_syntax(&t, target) || contains_subterm_syntax(&body, target) {
4912 return true;
4913 }
4914 }
4915
4916 false
4917}
4918
4919fn extract_slam(term: &Term) -> Option<(Term, Term)> {
4921 if let Term::App(inner, body) = term {
4922 if let Term::App(slam_ctor, t) = inner.as_ref() {
4923 if let Term::Global(ctor_name) = slam_ctor.as_ref() {
4924 if ctor_name == "SLam" {
4925 return Some((t.as_ref().clone(), body.as_ref().clone()));
4926 }
4927 }
4928 }
4929 }
4930 None
4931}
4932
4933fn extract_spi(term: &Term) -> Option<(Term, Term)> {
4935 if let Term::App(inner, body) = term {
4936 if let Term::App(spi_ctor, t) = inner.as_ref() {
4937 if let Term::Global(ctor_name) = spi_ctor.as_ref() {
4938 if ctor_name == "SPi" {
4939 return Some((t.as_ref().clone(), body.as_ref().clone()));
4940 }
4941 }
4942 }
4943 }
4944 None
4945}
4946
4947fn replace_first_subterm_syntax(term: &Term, target: &Term, replacement: &Term) -> Option<Term> {
4949 if syntax_equal(term, target) {
4951 return Some(replacement.clone());
4952 }
4953
4954 if let Some((f, x)) = extract_sapp(term) {
4956 if let Some(new_f) = replace_first_subterm_syntax(&f, target, replacement) {
4958 return Some(make_sapp(new_f, x));
4959 }
4960 if let Some(new_x) = replace_first_subterm_syntax(&x, target, replacement) {
4962 return Some(make_sapp(f, new_x));
4963 }
4964 }
4965
4966 if let Some((t, body)) = extract_slam(term) {
4968 if let Some(new_t) = replace_first_subterm_syntax(&t, target, replacement) {
4969 return Some(make_slam(new_t, body));
4970 }
4971 if let Some(new_body) = replace_first_subterm_syntax(&body, target, replacement) {
4972 return Some(make_slam(t, new_body));
4973 }
4974 }
4975
4976 if let Some((t, body)) = extract_spi(term) {
4978 if let Some(new_t) = replace_first_subterm_syntax(&t, target, replacement) {
4979 return Some(make_spi(new_t, body));
4980 }
4981 if let Some(new_body) = replace_first_subterm_syntax(&body, target, replacement) {
4982 return Some(make_spi(t, new_body));
4983 }
4984 }
4985
4986 None
4988}
4989
4990fn try_try_rewrite_reduce(
4993 ctx: &Context,
4994 eq_proof: &Term,
4995 goal: &Term,
4996 reverse: bool,
4997) -> Option<Term> {
4998 let eq_conclusion = try_concludes_reduce(ctx, eq_proof)?;
5000
5001 let (ty, lhs, rhs) = match extract_eq_components_from_syntax(&eq_conclusion) {
5003 Some(components) => components,
5004 None => return Some(make_error_derivation()),
5005 };
5006
5007 let (target, replacement) = if reverse { (rhs, lhs) } else { (lhs, rhs) };
5009
5010 if !contains_subterm_syntax(goal, &target) {
5012 return Some(make_error_derivation());
5013 }
5014
5015 let new_goal = match replace_first_subterm_syntax(goal, &target, &replacement) {
5017 Some(ng) => ng,
5018 None => return Some(make_error_derivation()),
5019 };
5020
5021 Some(Term::App(
5023 Box::new(Term::App(
5024 Box::new(Term::App(
5025 Box::new(Term::Global("DRewrite".to_string())),
5026 Box::new(eq_proof.clone()),
5027 )),
5028 Box::new(goal.clone()),
5029 )),
5030 Box::new(new_goal),
5031 ))
5032}
5033
5034fn try_drewrite_conclude(
5036 ctx: &Context,
5037 eq_proof: &Term,
5038 old_goal: &Term,
5039 new_goal: &Term,
5040) -> Option<Term> {
5041 let eq_conclusion = try_concludes_reduce(ctx, eq_proof)?;
5043
5044 let (_ty, lhs, rhs) = match extract_eq_components_from_syntax(&eq_conclusion) {
5046 Some(components) => components,
5047 None => return Some(make_sname_error()),
5048 };
5049
5050 if let Some(computed_new) = replace_first_subterm_syntax(old_goal, &lhs, &rhs) {
5053 if syntax_equal(&computed_new, new_goal) {
5054 return Some(new_goal.clone());
5055 }
5056 }
5057
5058 if let Some(computed_new) = replace_first_subterm_syntax(old_goal, &rhs, &lhs) {
5060 if syntax_equal(&computed_new, new_goal) {
5061 return Some(new_goal.clone());
5062 }
5063 }
5064
5065 Some(make_sname_error())
5067}
5068
5069fn try_try_destruct_reduce(
5071 ctx: &Context,
5072 ind_type: &Term,
5073 motive: &Term,
5074 cases: &Term,
5075) -> Option<Term> {
5076 Some(Term::App(
5083 Box::new(Term::App(
5084 Box::new(Term::App(
5085 Box::new(Term::Global("DDestruct".to_string())),
5086 Box::new(ind_type.clone()),
5087 )),
5088 Box::new(motive.clone()),
5089 )),
5090 Box::new(cases.clone()),
5091 ))
5092}
5093
5094fn try_ddestruct_conclude(
5096 ctx: &Context,
5097 ind_type: &Term,
5098 motive: &Term,
5099 cases: &Term,
5100) -> Option<Term> {
5101 let ind_name = extract_inductive_name_from_syntax(ind_type)?;
5106
5107 if !ctx.is_inductive(&ind_name) {
5109 return Some(make_sname_error());
5110 }
5111
5112 let constructors = ctx.get_constructors(&ind_name);
5113
5114 let case_proofs = match extract_case_proofs(cases) {
5116 Some(proofs) => proofs,
5117 None => return Some(make_sname_error()),
5118 };
5119
5120 if case_proofs.len() != constructors.len() {
5122 return Some(make_sname_error());
5123 }
5124
5125 Some(make_forall_syntax_with_type(ind_type, motive))
5131}
5132
5133fn try_try_apply_reduce(
5135 ctx: &Context,
5136 hyp_name: &Term,
5137 hyp_proof: &Term,
5138 goal: &Term,
5139) -> Option<Term> {
5140 let hyp_conclusion = try_concludes_reduce(ctx, hyp_proof)?;
5142
5143 if let Some((antecedent, consequent)) = extract_spi(&hyp_conclusion) {
5145 if syntax_equal(&consequent, goal) {
5147 return Some(Term::App(
5149 Box::new(Term::App(
5150 Box::new(Term::App(
5151 Box::new(Term::App(
5152 Box::new(Term::Global("DApply".to_string())),
5153 Box::new(hyp_name.clone()),
5154 )),
5155 Box::new(hyp_proof.clone()),
5156 )),
5157 Box::new(goal.clone()),
5158 )),
5159 Box::new(antecedent),
5160 ));
5161 }
5162 }
5163
5164 if let Some(forall_body) = extract_forall_body(&hyp_conclusion) {
5166 return Some(Term::App(
5172 Box::new(Term::App(
5173 Box::new(Term::App(
5174 Box::new(Term::App(
5175 Box::new(Term::Global("DApply".to_string())),
5176 Box::new(hyp_name.clone()),
5177 )),
5178 Box::new(hyp_proof.clone()),
5179 )),
5180 Box::new(goal.clone()),
5181 )),
5182 Box::new(make_sname("True")),
5183 ));
5184 }
5185
5186 if syntax_equal(&hyp_conclusion, goal) {
5188 return Some(Term::App(
5189 Box::new(Term::App(
5190 Box::new(Term::App(
5191 Box::new(Term::App(
5192 Box::new(Term::Global("DApply".to_string())),
5193 Box::new(hyp_name.clone()),
5194 )),
5195 Box::new(hyp_proof.clone()),
5196 )),
5197 Box::new(goal.clone()),
5198 )),
5199 Box::new(make_sname("True")),
5200 ));
5201 }
5202
5203 Some(make_error_derivation())
5205}
5206
5207fn try_dapply_conclude(
5209 ctx: &Context,
5210 hyp_name: &Term,
5211 hyp_proof: &Term,
5212 old_goal: &Term,
5213 new_goal: &Term,
5214) -> Option<Term> {
5215 let hyp_conclusion = try_concludes_reduce(ctx, hyp_proof)?;
5217
5218 if let Some((antecedent, consequent)) = extract_spi(&hyp_conclusion) {
5221 if syntax_equal(&consequent, old_goal) {
5222 if syntax_equal(&antecedent, new_goal) || extract_sname(new_goal) == Some("True".to_string()) {
5223 return Some(new_goal.clone());
5224 }
5225 }
5226 }
5227
5228 if let Some(_forall_body) = extract_forall_body(&hyp_conclusion) {
5230 if extract_sname(new_goal) == Some("True".to_string()) {
5232 return Some(new_goal.clone());
5233 }
5234 }
5235
5236 if syntax_equal(&hyp_conclusion, old_goal) {
5238 if extract_sname(new_goal) == Some("True".to_string()) {
5239 return Some(new_goal.clone());
5240 }
5241 }
5242
5243 Some(make_sname_error())
5245}
5246