1use crate::context::Context;
44use crate::omega;
45use crate::term::{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::App(func, arg) => {
82 if let Some(result) = try_primitive_reduce(func, arg) {
84 return result;
85 }
86
87 if let Some(result) = try_reflection_reduce(ctx, func, arg) {
89 return result;
90 }
91
92 match func.as_ref() {
94 Term::Lambda { param, body, .. } => {
95 substitute(body, param, arg)
97 }
98 Term::Fix { name, body } => {
101 if is_constructor_form(ctx, arg) {
102 let fix_term = Term::Fix {
104 name: name.clone(),
105 body: body.clone(),
106 };
107 let unfolded = substitute(body, name, &fix_term);
108 Term::App(Box::new(unfolded), arg.clone())
109 } else {
110 let reduced_arg = reduce_step(ctx, arg);
112 if reduced_arg != **arg {
113 Term::App(func.clone(), Box::new(reduced_arg))
114 } else {
115 term.clone()
116 }
117 }
118 }
119 Term::App(_, _) => {
121 let reduced_func = reduce_step(ctx, func);
122 if reduced_func != **func {
123 Term::App(Box::new(reduced_func), arg.clone())
124 } else {
125 let reduced_arg = reduce_step(ctx, arg);
127 Term::App(func.clone(), Box::new(reduced_arg))
128 }
129 }
130 _ => {
132 let reduced_func = reduce_step(ctx, func);
133 if reduced_func != **func {
134 Term::App(Box::new(reduced_func), arg.clone())
135 } else {
136 let reduced_arg = reduce_step(ctx, arg);
138 Term::App(func.clone(), Box::new(reduced_arg))
139 }
140 }
141 }
142 }
143
144 Term::Match {
146 discriminant,
147 motive,
148 cases,
149 } => {
150 if let Some((ctor_idx, args)) = extract_constructor(ctx, discriminant) {
151 let case = &cases[ctor_idx];
153 let mut result = case.clone();
155 for arg in args {
156 result = Term::App(Box::new(result), Box::new(arg));
157 }
158 reduce_step(ctx, &result)
160 } else {
161 let reduced_disc = reduce_step(ctx, discriminant);
163 if reduced_disc != **discriminant {
164 Term::Match {
165 discriminant: Box::new(reduced_disc),
166 motive: motive.clone(),
167 cases: cases.clone(),
168 }
169 } else {
170 term.clone()
171 }
172 }
173 }
174
175 Term::Lambda {
177 param,
178 param_type,
179 body,
180 } => {
181 let reduced_param_type = reduce_step(ctx, param_type);
182 let reduced_body = reduce_step(ctx, body);
183 if reduced_param_type != **param_type || reduced_body != **body {
184 Term::Lambda {
185 param: param.clone(),
186 param_type: Box::new(reduced_param_type),
187 body: Box::new(reduced_body),
188 }
189 } else {
190 term.clone()
191 }
192 }
193
194 Term::Pi {
196 param,
197 param_type,
198 body_type,
199 } => {
200 let reduced_param_type = reduce_step(ctx, param_type);
201 let reduced_body_type = reduce_step(ctx, body_type);
202 if reduced_param_type != **param_type || reduced_body_type != **body_type {
203 Term::Pi {
204 param: param.clone(),
205 param_type: Box::new(reduced_param_type),
206 body_type: Box::new(reduced_body_type),
207 }
208 } else {
209 term.clone()
210 }
211 }
212
213 Term::Fix { name, body } => {
215 let reduced_body = reduce_step(ctx, body);
216 if reduced_body != **body {
217 Term::Fix {
218 name: name.clone(),
219 body: Box::new(reduced_body),
220 }
221 } else {
222 term.clone()
223 }
224 }
225
226 Term::Sort(_) | Term::Var(_) | Term::Hole => term.clone(),
228
229 Term::Global(name) => {
231 if let Some(body) = ctx.get_definition_body(name) {
232 body.clone()
234 } else {
235 term.clone()
237 }
238 }
239 }
240}
241
242fn is_constructor_form(ctx: &Context, term: &Term) -> bool {
244 extract_constructor(ctx, term).is_some()
245}
246
247fn extract_constructor(ctx: &Context, term: &Term) -> Option<(usize, Vec<Term>)> {
254 let mut args = Vec::new();
255 let mut current = term;
256
257 while let Term::App(func, arg) = current {
259 args.push((**arg).clone());
260 current = func;
261 }
262 args.reverse();
263
264 if let Term::Global(name) = current {
266 if let Some(inductive) = ctx.constructor_inductive(name) {
267 let ctors = ctx.get_constructors(inductive);
268 for (idx, (ctor_name, ctor_type)) in ctors.iter().enumerate() {
269 if *ctor_name == name {
270 let num_type_params = count_type_params(ctor_type);
272
273 let value_args = if num_type_params < args.len() {
275 args[num_type_params..].to_vec()
276 } else {
277 vec![]
278 };
279
280 return Some((idx, value_args));
281 }
282 }
283 }
284 }
285 None
286}
287
288fn count_type_params(ty: &Term) -> usize {
293 let mut count = 0;
294 let mut current = ty;
295
296 while let Term::Pi { param_type, body_type, .. } = current {
297 if is_sort(param_type) {
298 count += 1;
299 current = body_type;
300 } else {
301 break;
302 }
303 }
304
305 count
306}
307
308fn is_sort(term: &Term) -> bool {
310 matches!(term, Term::Sort(_))
311}
312
313fn try_primitive_reduce(func: &Term, arg: &Term) -> Option<Term> {
318 if let Term::App(op_term, x) = func {
320 if let Term::Global(op_name) = op_term.as_ref() {
321 if let (Term::Lit(Literal::Int(x_val)), Term::Lit(Literal::Int(y_val))) =
322 (x.as_ref(), arg)
323 {
324 let result = match op_name.as_str() {
325 "add" => x_val.checked_add(*y_val)?,
326 "sub" => x_val.checked_sub(*y_val)?,
327 "mul" => x_val.checked_mul(*y_val)?,
328 "div" => x_val.checked_div(*y_val)?,
329 "mod" => x_val.checked_rem(*y_val)?,
330 _ => return None,
331 };
332 return Some(Term::Lit(Literal::Int(result)));
333 }
334 }
335 }
336 None
337}
338
339fn try_reflection_reduce(ctx: &Context, func: &Term, arg: &Term) -> Option<Term> {
350 if let Term::Global(op_name) = func {
352 match op_name.as_str() {
353 "syn_size" => {
354 let norm_arg = normalize(ctx, arg);
356 return try_syn_size_reduce(ctx, &norm_arg);
357 }
358 "syn_max_var" => {
359 let norm_arg = normalize(ctx, arg);
361 return try_syn_max_var_reduce(ctx, &norm_arg, 0);
362 }
363 "syn_step" => {
364 let norm_arg = normalize(ctx, arg);
366 return try_syn_step_reduce(ctx, &norm_arg);
367 }
368 "syn_quote" => {
369 let norm_arg = normalize(ctx, arg);
371 return try_syn_quote_reduce(ctx, &norm_arg);
372 }
373 "syn_diag" => {
374 let norm_arg = normalize(ctx, arg);
376 return try_syn_diag_reduce(ctx, &norm_arg);
377 }
378 "concludes" => {
379 let norm_arg = normalize(ctx, arg);
381 return try_concludes_reduce(ctx, &norm_arg);
382 }
383 "try_refl" => {
384 let norm_arg = normalize(ctx, arg);
386 return try_try_refl_reduce(ctx, &norm_arg);
387 }
388 "tact_fail" => {
389 return Some(make_error_derivation());
391 }
392 "try_compute" => {
393 let norm_arg = normalize(ctx, arg);
396 return Some(Term::App(
397 Box::new(Term::Global("DCompute".to_string())),
398 Box::new(norm_arg),
399 ));
400 }
401 "try_ring" => {
402 let norm_arg = normalize(ctx, arg);
404 return try_try_ring_reduce(ctx, &norm_arg);
405 }
406 "try_lia" => {
407 let norm_arg = normalize(ctx, arg);
409 return try_try_lia_reduce(ctx, &norm_arg);
410 }
411 "try_cc" => {
412 let norm_arg = normalize(ctx, arg);
414 return try_try_cc_reduce(ctx, &norm_arg);
415 }
416 "try_simp" => {
417 let norm_arg = normalize(ctx, arg);
419 return try_try_simp_reduce(ctx, &norm_arg);
420 }
421 "try_omega" => {
422 let norm_arg = normalize(ctx, arg);
424 return try_try_omega_reduce(ctx, &norm_arg);
425 }
426 "try_auto" => {
427 let norm_arg = normalize(ctx, arg);
429 return try_try_auto_reduce(ctx, &norm_arg);
430 }
431 "try_inversion" => {
432 let norm_arg = normalize(ctx, arg);
434 return try_try_inversion_reduce(ctx, &norm_arg);
435 }
436 "induction_num_cases" => {
437 let norm_arg = normalize(ctx, arg);
439 return try_induction_num_cases_reduce(ctx, &norm_arg);
440 }
441 _ => {}
442 }
443 }
444
445 if let Term::App(partial2, cutoff) = func {
452 if let Term::App(partial1, amount) = partial2.as_ref() {
453 if let Term::Global(op_name) = partial1.as_ref() {
454 if op_name == "syn_lift" {
455 if let (Term::Lit(Literal::Int(amt)), Term::Lit(Literal::Int(cut))) =
456 (amount.as_ref(), cutoff.as_ref())
457 {
458 let norm_term = normalize(ctx, arg);
459 return try_syn_lift_reduce(ctx, *amt, *cut, &norm_term);
460 }
461 }
462 }
463 }
464 }
465
466 if let Term::App(partial2, index) = func {
469 if let Term::App(partial1, replacement) = partial2.as_ref() {
470 if let Term::Global(op_name) = partial1.as_ref() {
471 if op_name == "syn_subst" {
472 if let Term::Lit(Literal::Int(idx)) = index.as_ref() {
473 let norm_replacement = normalize(ctx, replacement);
474 let norm_term = normalize(ctx, arg);
475 return try_syn_subst_reduce(ctx, &norm_replacement, *idx, &norm_term);
476 }
477 }
478 }
479 }
480 }
481
482 if let Term::App(partial1, body) = func {
485 if let Term::Global(op_name) = partial1.as_ref() {
486 if op_name == "syn_beta" {
487 let norm_body = normalize(ctx, body);
488 let norm_arg = normalize(ctx, arg);
489 return try_syn_beta_reduce(ctx, &norm_body, &norm_arg);
490 }
491 }
492 }
493
494 if let Term::App(partial1, context) = func {
498 if let Term::Global(op_name) = partial1.as_ref() {
499 if op_name == "try_cong" {
500 let norm_context = normalize(ctx, context);
501 let norm_proof = normalize(ctx, arg);
502 return Some(Term::App(
503 Box::new(Term::App(
504 Box::new(Term::Global("DCong".to_string())),
505 Box::new(norm_context),
506 )),
507 Box::new(norm_proof),
508 ));
509 }
510 }
511 }
512
513 if let Term::App(partial1, eq_proof) = func {
517 if let Term::Global(op_name) = partial1.as_ref() {
518 if op_name == "try_rewrite" {
519 let norm_eq_proof = normalize(ctx, eq_proof);
520 let norm_goal = normalize(ctx, arg);
521 return try_try_rewrite_reduce(ctx, &norm_eq_proof, &norm_goal, false);
522 }
523 if op_name == "try_rewrite_rev" {
524 let norm_eq_proof = normalize(ctx, eq_proof);
525 let norm_goal = normalize(ctx, arg);
526 return try_try_rewrite_reduce(ctx, &norm_eq_proof, &norm_goal, true);
527 }
528 }
529 }
530
531 if let Term::App(partial1, fuel_term) = func {
534 if let Term::Global(op_name) = partial1.as_ref() {
535 if op_name == "syn_eval" {
536 if let Term::Lit(Literal::Int(fuel)) = fuel_term.as_ref() {
537 let norm_term = normalize(ctx, arg);
538 return try_syn_eval_reduce(ctx, *fuel, &norm_term);
539 }
540 }
541 }
542 }
543
544 if let Term::App(partial1, ind_type) = func {
547 if let Term::Global(op_name) = partial1.as_ref() {
548 if op_name == "induction_base_goal" {
549 let norm_ind_type = normalize(ctx, ind_type);
550 let norm_motive = normalize(ctx, arg);
551 return try_induction_base_goal_reduce(ctx, &norm_ind_type, &norm_motive);
552 }
553 }
554 }
555
556 if let Term::App(partial1, t2) = func {
560 if let Term::App(combinator, t1) = partial1.as_ref() {
561 if let Term::Global(name) = combinator.as_ref() {
562 if name == "tact_orelse" {
563 return try_tact_orelse_reduce(ctx, t1, t2, arg);
564 }
565 if name == "tact_then" {
567 return try_tact_then_reduce(ctx, t1, t2, arg);
568 }
569 }
570 }
571 }
572
573 if let Term::App(combinator, t) = func {
576 if let Term::Global(name) = combinator.as_ref() {
577 if name == "tact_try" {
578 return try_tact_try_reduce(ctx, t, arg);
579 }
580 if name == "tact_repeat" {
582 return try_tact_repeat_reduce(ctx, t, arg);
583 }
584 if name == "tact_solve" {
586 return try_tact_solve_reduce(ctx, t, arg);
587 }
588 if name == "tact_first" {
590 return try_tact_first_reduce(ctx, t, arg);
591 }
592 }
593 }
594
595 if let Term::App(partial1, motive) = func {
599 if let Term::App(combinator, ind_type) = partial1.as_ref() {
600 if let Term::Global(name) = combinator.as_ref() {
601 if name == "induction_step_goal" {
602 let norm_ind_type = normalize(ctx, ind_type);
603 let norm_motive = normalize(ctx, motive);
604 let norm_idx = normalize(ctx, arg);
605 return try_induction_step_goal_reduce(ctx, &norm_ind_type, &norm_motive, &norm_idx);
606 }
607 }
608 }
609 }
610
611 if let Term::App(partial1, motive) = func {
615 if let Term::App(combinator, ind_type) = partial1.as_ref() {
616 if let Term::Global(name) = combinator.as_ref() {
617 if name == "try_induction" {
618 let norm_ind_type = normalize(ctx, ind_type);
619 let norm_motive = normalize(ctx, motive);
620 let norm_cases = normalize(ctx, arg);
621 return try_try_induction_reduce(ctx, &norm_ind_type, &norm_motive, &norm_cases);
622 }
623 }
624 }
625 }
626
627 if let Term::App(partial1, motive) = func {
631 if let Term::App(combinator, ind_type) = partial1.as_ref() {
632 if let Term::Global(name) = combinator.as_ref() {
633 if name == "try_destruct" {
634 let norm_ind_type = normalize(ctx, ind_type);
635 let norm_motive = normalize(ctx, motive);
636 let norm_cases = normalize(ctx, arg);
637 return try_try_destruct_reduce(ctx, &norm_ind_type, &norm_motive, &norm_cases);
638 }
639 }
640 }
641 }
642
643 if let Term::App(partial1, hyp_proof) = func {
647 if let Term::App(combinator, hyp_name) = partial1.as_ref() {
648 if let Term::Global(name) = combinator.as_ref() {
649 if name == "try_apply" {
650 let norm_hyp_name = normalize(ctx, hyp_name);
651 let norm_hyp_proof = normalize(ctx, hyp_proof);
652 let norm_goal = normalize(ctx, arg);
653 return try_try_apply_reduce(ctx, &norm_hyp_name, &norm_hyp_proof, &norm_goal);
654 }
655 }
656 }
657 }
658
659 None
660}
661
662fn try_syn_size_reduce(ctx: &Context, term: &Term) -> Option<Term> {
671 if let Term::App(ctor_term, _inner_arg) = term {
677 if let Term::Global(ctor_name) = ctor_term.as_ref() {
678 match ctor_name.as_str() {
679 "SVar" | "SGlobal" | "SSort" | "SLit" | "SName" => {
680 return Some(Term::Lit(Literal::Int(1)));
681 }
682 _ => {}
683 }
684 }
685
686 if let Term::App(inner, a) = ctor_term.as_ref() {
688 if let Term::Global(ctor_name) = inner.as_ref() {
689 match ctor_name.as_str() {
690 "SApp" | "SLam" | "SPi" => {
691 let a_size = try_syn_size_reduce(ctx, a)?;
693 let b_size = try_syn_size_reduce(ctx, _inner_arg)?;
694
695 if let (Term::Lit(Literal::Int(a_n)), Term::Lit(Literal::Int(b_n))) =
696 (a_size, b_size)
697 {
698 return Some(Term::Lit(Literal::Int(1 + a_n + b_n)));
699 }
700 }
701 _ => {}
702 }
703 }
704 }
705 }
706
707 None
708}
709
710fn try_syn_max_var_reduce(ctx: &Context, term: &Term, depth: i64) -> Option<Term> {
720 if let Term::App(ctor_term, inner_arg) = term {
722 if let Term::Global(ctor_name) = ctor_term.as_ref() {
723 match ctor_name.as_str() {
724 "SVar" => {
725 if let Term::Lit(Literal::Int(k)) = inner_arg.as_ref() {
727 if *k >= depth {
728 return Some(Term::Lit(Literal::Int(*k - depth)));
730 } else {
731 return Some(Term::Lit(Literal::Int(-1)));
733 }
734 }
735 }
736 "SGlobal" | "SSort" | "SLit" | "SName" => {
737 return Some(Term::Lit(Literal::Int(-1)));
739 }
740 _ => {}
741 }
742 }
743
744 if let Term::App(inner, a) = ctor_term.as_ref() {
746 if let Term::Global(ctor_name) = inner.as_ref() {
747 match ctor_name.as_str() {
748 "SApp" => {
749 let a_max = try_syn_max_var_reduce(ctx, a, depth)?;
751 let b_max = try_syn_max_var_reduce(ctx, inner_arg, depth)?;
752
753 if let (Term::Lit(Literal::Int(a_n)), Term::Lit(Literal::Int(b_n))) =
754 (a_max, b_max)
755 {
756 return Some(Term::Lit(Literal::Int(a_n.max(b_n))));
757 }
758 }
759 "SLam" | "SPi" => {
760 let a_max = try_syn_max_var_reduce(ctx, a, depth)?;
763 let b_max = try_syn_max_var_reduce(ctx, inner_arg, depth + 1)?;
764
765 if let (Term::Lit(Literal::Int(a_n)), Term::Lit(Literal::Int(b_n))) =
766 (a_max, b_max)
767 {
768 return Some(Term::Lit(Literal::Int(a_n.max(b_n))));
769 }
770 }
771 _ => {}
772 }
773 }
774 }
775 }
776
777 None
778}
779
780fn try_syn_lift_reduce(ctx: &Context, amount: i64, cutoff: i64, term: &Term) -> Option<Term> {
790 if let Term::App(ctor_term, inner_arg) = term {
792 if let Term::Global(ctor_name) = ctor_term.as_ref() {
793 match ctor_name.as_str() {
794 "SVar" => {
795 if let Term::Lit(Literal::Int(k)) = inner_arg.as_ref() {
796 if *k >= cutoff {
797 return Some(Term::App(
799 Box::new(Term::Global("SVar".to_string())),
800 Box::new(Term::Lit(Literal::Int(*k + amount))),
801 ));
802 } else {
803 return Some(term.clone());
805 }
806 }
807 }
808 "SGlobal" | "SSort" | "SLit" | "SName" => {
809 return Some(term.clone());
811 }
812 _ => {}
813 }
814 }
815
816 if let Term::App(inner, a) = ctor_term.as_ref() {
818 if let Term::Global(ctor_name) = inner.as_ref() {
819 match ctor_name.as_str() {
820 "SApp" => {
821 let a_lifted = try_syn_lift_reduce(ctx, amount, cutoff, a)?;
823 let b_lifted = try_syn_lift_reduce(ctx, amount, cutoff, inner_arg)?;
824 return Some(Term::App(
825 Box::new(Term::App(
826 Box::new(Term::Global("SApp".to_string())),
827 Box::new(a_lifted),
828 )),
829 Box::new(b_lifted),
830 ));
831 }
832 "SLam" | "SPi" => {
833 let param_lifted = try_syn_lift_reduce(ctx, amount, cutoff, a)?;
835 let body_lifted =
836 try_syn_lift_reduce(ctx, amount, cutoff + 1, inner_arg)?;
837 return Some(Term::App(
838 Box::new(Term::App(
839 Box::new(Term::Global(ctor_name.clone())),
840 Box::new(param_lifted),
841 )),
842 Box::new(body_lifted),
843 ));
844 }
845 _ => {}
846 }
847 }
848 }
849 }
850
851 None
852}
853
854fn try_syn_subst_reduce(
861 ctx: &Context,
862 replacement: &Term,
863 index: i64,
864 term: &Term,
865) -> Option<Term> {
866 if let Term::App(ctor_term, inner_arg) = term {
868 if let Term::Global(ctor_name) = ctor_term.as_ref() {
869 match ctor_name.as_str() {
870 "SVar" => {
871 if let Term::Lit(Literal::Int(k)) = inner_arg.as_ref() {
872 if *k == index {
873 return Some(replacement.clone());
875 } else {
876 return Some(term.clone());
878 }
879 }
880 }
881 "SGlobal" | "SSort" | "SLit" | "SName" => {
882 return Some(term.clone());
884 }
885 _ => {}
886 }
887 }
888
889 if let Term::App(inner, a) = ctor_term.as_ref() {
891 if let Term::Global(ctor_name) = inner.as_ref() {
892 match ctor_name.as_str() {
893 "SApp" => {
894 let a_subst = try_syn_subst_reduce(ctx, replacement, index, a)?;
896 let b_subst = try_syn_subst_reduce(ctx, replacement, index, inner_arg)?;
897 return Some(Term::App(
898 Box::new(Term::App(
899 Box::new(Term::Global("SApp".to_string())),
900 Box::new(a_subst),
901 )),
902 Box::new(b_subst),
903 ));
904 }
905 "SLam" | "SPi" => {
906 let param_subst = try_syn_subst_reduce(ctx, replacement, index, a)?;
909
910 let lifted_replacement = try_syn_lift_reduce(ctx, 1, 0, replacement)?;
912 let body_subst = try_syn_subst_reduce(
913 ctx,
914 &lifted_replacement,
915 index + 1,
916 inner_arg,
917 )?;
918
919 return Some(Term::App(
920 Box::new(Term::App(
921 Box::new(Term::Global(ctor_name.clone())),
922 Box::new(param_subst),
923 )),
924 Box::new(body_subst),
925 ));
926 }
927 _ => {}
928 }
929 }
930 }
931 }
932
933 None
934}
935
936fn try_syn_beta_reduce(ctx: &Context, body: &Term, arg: &Term) -> Option<Term> {
944 try_syn_subst_reduce(ctx, arg, 0, body)
946}
947
948fn try_syn_arith_reduce(func: &Term, arg: &Term) -> Option<Term> {
953 if let Term::App(inner_ctor, n_term) = func {
956 if let Term::App(sapp_ctor, op_term) = inner_ctor.as_ref() {
957 if let Term::Global(ctor_name) = sapp_ctor.as_ref() {
959 if ctor_name != "SApp" {
960 return None;
961 }
962 } else {
963 return None;
964 }
965
966 let op = if let Term::App(sname_ctor, op_str) = op_term.as_ref() {
968 if let Term::Global(name) = sname_ctor.as_ref() {
969 if name == "SName" {
970 if let Term::Lit(Literal::Text(op)) = op_str.as_ref() {
971 op.clone()
972 } else {
973 return None;
974 }
975 } else {
976 return None;
977 }
978 } else {
979 return None;
980 }
981 } else {
982 return None;
983 };
984
985 let n = if let Term::App(slit_ctor, n_val) = n_term.as_ref() {
987 if let Term::Global(name) = slit_ctor.as_ref() {
988 if name == "SLit" {
989 if let Term::Lit(Literal::Int(n)) = n_val.as_ref() {
990 *n
991 } else {
992 return None;
993 }
994 } else {
995 return None;
996 }
997 } else {
998 return None;
999 }
1000 } else {
1001 return None;
1002 };
1003
1004 let m = if let Term::App(slit_ctor, m_val) = arg {
1006 if let Term::Global(name) = slit_ctor.as_ref() {
1007 if name == "SLit" {
1008 if let Term::Lit(Literal::Int(m)) = m_val.as_ref() {
1009 *m
1010 } else {
1011 return None;
1012 }
1013 } else {
1014 return None;
1015 }
1016 } else {
1017 return None;
1018 }
1019 } else {
1020 return None;
1021 };
1022
1023 let result = match op.as_str() {
1025 "add" => n.checked_add(m),
1026 "sub" => n.checked_sub(m),
1027 "mul" => n.checked_mul(m),
1028 "div" => {
1029 if m == 0 {
1030 return None; }
1032 n.checked_div(m)
1033 }
1034 "mod" => {
1035 if m == 0 {
1036 return None; }
1038 n.checked_rem(m)
1039 }
1040 _ => None,
1041 };
1042
1043 if let Some(r) = result {
1045 return Some(Term::App(
1046 Box::new(Term::Global("SLit".to_string())),
1047 Box::new(Term::Lit(Literal::Int(r))),
1048 ));
1049 }
1050 }
1051 }
1052
1053 None
1054}
1055
1056fn try_syn_step_reduce(ctx: &Context, term: &Term) -> Option<Term> {
1063 if let Term::App(ctor_term, arg) = term {
1065 if let Term::App(inner, func) = ctor_term.as_ref() {
1066 if let Term::Global(ctor_name) = inner.as_ref() {
1067 if ctor_name == "SApp" {
1068 if let Some(result) = try_syn_arith_reduce(func.as_ref(), arg.as_ref()) {
1071 return Some(result);
1072 }
1073
1074 if let Term::App(lam_inner, body) = func.as_ref() {
1076 if let Term::App(lam_ctor, _param_type) = lam_inner.as_ref() {
1077 if let Term::Global(lam_name) = lam_ctor.as_ref() {
1078 if lam_name == "SLam" {
1079 return try_syn_beta_reduce(ctx, body.as_ref(), arg.as_ref());
1081 }
1082 }
1083 }
1084 }
1085
1086 if let Some(stepped_func) = try_syn_step_reduce(ctx, func.as_ref()) {
1088 if &stepped_func != func.as_ref() {
1090 return Some(Term::App(
1092 Box::new(Term::App(
1093 Box::new(Term::Global("SApp".to_string())),
1094 Box::new(stepped_func),
1095 )),
1096 Box::new(arg.as_ref().clone()),
1097 ));
1098 }
1099 }
1100
1101 if let Some(stepped_arg) = try_syn_step_reduce(ctx, arg.as_ref()) {
1103 if &stepped_arg != arg.as_ref() {
1104 return Some(Term::App(
1106 Box::new(Term::App(
1107 Box::new(Term::Global("SApp".to_string())),
1108 Box::new(func.as_ref().clone()),
1109 )),
1110 Box::new(stepped_arg),
1111 ));
1112 }
1113 }
1114
1115 return Some(term.clone());
1117 }
1118 }
1119 }
1120 }
1121
1122 Some(term.clone())
1125}
1126
1127fn try_syn_eval_reduce(ctx: &Context, fuel: i64, term: &Term) -> Option<Term> {
1137 if fuel <= 0 {
1138 return Some(term.clone());
1139 }
1140
1141 let stepped = try_syn_step_reduce(ctx, term)?;
1143
1144 if &stepped == term {
1146 return Some(term.clone());
1147 }
1148
1149 try_syn_eval_reduce(ctx, fuel - 1, &stepped)
1151}
1152
1153#[allow(dead_code)]
1169fn try_syn_quote_reduce(ctx: &Context, term: &Term) -> Option<Term> {
1170 fn sname_app(name: &str, arg: Term) -> Term {
1172 Term::App(
1173 Box::new(Term::App(
1174 Box::new(Term::Global("SApp".to_string())),
1175 Box::new(Term::App(
1176 Box::new(Term::Global("SName".to_string())),
1177 Box::new(Term::Lit(Literal::Text(name.to_string()))),
1178 )),
1179 )),
1180 Box::new(arg),
1181 )
1182 }
1183
1184 fn slit(n: i64) -> Term {
1186 Term::App(
1187 Box::new(Term::Global("SLit".to_string())),
1188 Box::new(Term::Lit(Literal::Int(n))),
1189 )
1190 }
1191
1192 fn sname(s: &str) -> Term {
1194 Term::App(
1195 Box::new(Term::Global("SName".to_string())),
1196 Box::new(Term::Lit(Literal::Text(s.to_string()))),
1197 )
1198 }
1199
1200 fn sname_app2(name: &str, a: Term, b: Term) -> Term {
1202 Term::App(
1203 Box::new(Term::App(
1204 Box::new(Term::Global("SApp".to_string())),
1205 Box::new(sname_app(name, a)),
1206 )),
1207 Box::new(b),
1208 )
1209 }
1210
1211 if let Term::App(ctor_term, inner_arg) = term {
1213 if let Term::Global(ctor_name) = ctor_term.as_ref() {
1214 match ctor_name.as_str() {
1215 "SVar" => {
1216 if let Term::Lit(Literal::Int(n)) = inner_arg.as_ref() {
1217 return Some(sname_app("SVar", slit(*n)));
1218 }
1219 }
1220 "SGlobal" => {
1221 if let Term::Lit(Literal::Int(n)) = inner_arg.as_ref() {
1222 return Some(sname_app("SGlobal", slit(*n)));
1223 }
1224 }
1225 "SSort" => {
1226 let quoted_univ = quote_univ(inner_arg)?;
1228 return Some(sname_app("SSort", quoted_univ));
1229 }
1230 "SLit" => {
1231 if let Term::Lit(Literal::Int(n)) = inner_arg.as_ref() {
1232 return Some(sname_app("SLit", slit(*n)));
1233 }
1234 }
1235 "SName" => {
1236 return Some(term.clone());
1238 }
1239 _ => {}
1240 }
1241 }
1242
1243 if let Term::App(inner, a) = ctor_term.as_ref() {
1245 if let Term::Global(ctor_name) = inner.as_ref() {
1246 match ctor_name.as_str() {
1247 "SApp" | "SLam" | "SPi" => {
1248 let quoted_a = try_syn_quote_reduce(ctx, a)?;
1249 let quoted_b = try_syn_quote_reduce(ctx, inner_arg)?;
1250 return Some(sname_app2(ctor_name, quoted_a, quoted_b));
1251 }
1252 _ => {}
1253 }
1254 }
1255 }
1256 }
1257
1258 None
1259}
1260
1261fn quote_univ(term: &Term) -> Option<Term> {
1266 fn sname(s: &str) -> Term {
1267 Term::App(
1268 Box::new(Term::Global("SName".to_string())),
1269 Box::new(Term::Lit(Literal::Text(s.to_string()))),
1270 )
1271 }
1272
1273 fn slit(n: i64) -> Term {
1274 Term::App(
1275 Box::new(Term::Global("SLit".to_string())),
1276 Box::new(Term::Lit(Literal::Int(n))),
1277 )
1278 }
1279
1280 fn sname_app(name: &str, arg: Term) -> Term {
1281 Term::App(
1282 Box::new(Term::App(
1283 Box::new(Term::Global("SApp".to_string())),
1284 Box::new(sname(name)),
1285 )),
1286 Box::new(arg),
1287 )
1288 }
1289
1290 if let Term::Global(name) = term {
1291 if name == "UProp" {
1292 return Some(sname("UProp"));
1293 }
1294 }
1295
1296 if let Term::App(ctor, arg) = term {
1297 if let Term::Global(name) = ctor.as_ref() {
1298 if name == "UType" {
1299 if let Term::Lit(Literal::Int(n)) = arg.as_ref() {
1300 return Some(sname_app("UType", slit(*n)));
1301 }
1302 }
1303 }
1304 }
1305
1306 None
1307}
1308
1309fn try_syn_diag_reduce(ctx: &Context, term: &Term) -> Option<Term> {
1318 let quoted = try_syn_quote_reduce(ctx, term)?;
1320
1321 try_syn_subst_reduce(ctx, "ed, 0, term)
1323}
1324
1325fn try_concludes_reduce(ctx: &Context, deriv: &Term) -> Option<Term> {
1338 if let Term::App(ctor_term, p) = deriv {
1340 if let Term::Global(ctor_name) = ctor_term.as_ref() {
1341 if ctor_name == "DAxiom" {
1342 return Some(p.as_ref().clone());
1343 }
1344 if ctor_name == "DUnivIntro" {
1345 let inner_conc = try_concludes_reduce(ctx, p)?;
1347 let lifted = try_syn_lift_reduce(ctx, 1, 0, &inner_conc)?;
1349 return Some(make_forall_syntax(&lifted));
1350 }
1351 if ctor_name == "DCompute" {
1352 return try_dcompute_conclude(ctx, p);
1354 }
1355 if ctor_name == "DRingSolve" {
1356 return try_dring_solve_conclude(ctx, p);
1358 }
1359 if ctor_name == "DLiaSolve" {
1360 return try_dlia_solve_conclude(ctx, p);
1362 }
1363 if ctor_name == "DccSolve" {
1364 return try_dcc_solve_conclude(ctx, p);
1366 }
1367 if ctor_name == "DSimpSolve" {
1368 return try_dsimp_solve_conclude(ctx, p);
1370 }
1371 if ctor_name == "DOmegaSolve" {
1372 return try_domega_solve_conclude(ctx, p);
1374 }
1375 if ctor_name == "DAutoSolve" {
1376 return try_dauto_solve_conclude(ctx, p);
1378 }
1379 if ctor_name == "DInversion" {
1380 return try_dinversion_conclude(ctx, p);
1382 }
1383 }
1384 }
1385
1386 if let Term::App(partial1, new_goal) = deriv {
1389 if let Term::App(partial2, old_goal) = partial1.as_ref() {
1390 if let Term::App(ctor_term, eq_proof) = partial2.as_ref() {
1391 if let Term::Global(ctor_name) = ctor_term.as_ref() {
1392 if ctor_name == "DRewrite" {
1393 return try_drewrite_conclude(ctx, eq_proof, old_goal, new_goal);
1394 }
1395 }
1396 }
1397 }
1398 }
1399
1400 if let Term::App(partial1, cases) = deriv {
1403 if let Term::App(partial2, motive) = partial1.as_ref() {
1404 if let Term::App(ctor_term, ind_type) = partial2.as_ref() {
1405 if let Term::Global(ctor_name) = ctor_term.as_ref() {
1406 if ctor_name == "DDestruct" {
1407 return try_ddestruct_conclude(ctx, ind_type, motive, cases);
1408 }
1409 }
1410 }
1411 }
1412 }
1413
1414 if let Term::App(partial1, new_goal) = deriv {
1417 if let Term::App(partial2, old_goal) = partial1.as_ref() {
1418 if let Term::App(partial3, hyp_proof) = partial2.as_ref() {
1419 if let Term::App(ctor_term, hyp_name) = partial3.as_ref() {
1420 if let Term::Global(ctor_name) = ctor_term.as_ref() {
1421 if ctor_name == "DApply" {
1422 return try_dapply_conclude(ctx, hyp_name, hyp_proof, old_goal, new_goal);
1423 }
1424 }
1425 }
1426 }
1427 }
1428 }
1429
1430 if let Term::App(partial, a) = deriv {
1432 if let Term::App(ctor_term, t) = partial.as_ref() {
1433 if let Term::Global(ctor_name) = ctor_term.as_ref() {
1434 if ctor_name == "DRefl" {
1435 return Some(make_eq_syntax(t.as_ref(), a.as_ref()));
1436 }
1437 if ctor_name == "DCong" {
1438 return try_dcong_conclude(ctx, t, a);
1441 }
1442 }
1443 }
1444 }
1445
1446 if let Term::App(partial, d_ant) = deriv {
1448 if let Term::App(ctor_term, d_impl) = partial.as_ref() {
1449 if let Term::Global(ctor_name) = ctor_term.as_ref() {
1450 if ctor_name == "DModusPonens" {
1451 let impl_conc = try_concludes_reduce(ctx, d_impl)?;
1453 let ant_conc = try_concludes_reduce(ctx, d_ant)?;
1454
1455 if let Some((a, b)) = extract_implication(&impl_conc) {
1457 if syntax_equal(&ant_conc, &a) {
1459 return Some(b);
1460 }
1461 }
1462 return Some(make_sname_error());
1464 }
1465 if ctor_name == "DUnivElim" {
1466 let conc = try_concludes_reduce(ctx, d_impl)?;
1468 if let Some(body) = extract_forall_body(&conc) {
1469 return try_syn_subst_reduce(ctx, d_ant, 0, &body);
1471 }
1472 return Some(make_sname_error());
1474 }
1475 }
1476 }
1477 }
1478
1479 if let Term::App(partial1, step) = deriv {
1482 if let Term::App(partial2, base) = partial1.as_ref() {
1483 if let Term::App(ctor_term, motive) = partial2.as_ref() {
1484 if let Term::Global(ctor_name) = ctor_term.as_ref() {
1485 if ctor_name == "DInduction" {
1486 return try_dinduction_reduce(ctx, motive, base, step);
1487 }
1488 }
1489 }
1490 }
1491 }
1492
1493 if let Term::App(partial1, cases) = deriv {
1496 if let Term::App(partial2, motive) = partial1.as_ref() {
1497 if let Term::App(ctor_term, ind_type) = partial2.as_ref() {
1498 if let Term::Global(ctor_name) = ctor_term.as_ref() {
1499 if ctor_name == "DElim" {
1500 return try_delim_conclude(ctx, ind_type, motive, cases);
1501 }
1502 }
1503 }
1504 }
1505 }
1506
1507 None
1508}
1509
1510fn extract_implication(term: &Term) -> Option<(Term, Term)> {
1517 if let Term::App(outer, b) = term {
1519 if let Term::App(sapp_outer, x) = outer.as_ref() {
1520 if let Term::Global(ctor) = sapp_outer.as_ref() {
1521 if ctor == "SApp" {
1522 if let Term::App(inner, a) = x.as_ref() {
1524 if let Term::App(sapp_inner, sname_implies) = inner.as_ref() {
1525 if let Term::Global(ctor2) = sapp_inner.as_ref() {
1526 if ctor2 == "SApp" {
1527 if let Term::App(sname, text) = sname_implies.as_ref() {
1529 if let Term::Global(sname_ctor) = sname.as_ref() {
1530 if sname_ctor == "SName" {
1531 if let Term::Lit(Literal::Text(s)) = text.as_ref() {
1532 if s == "Implies" || s == "implies" {
1533 return Some((
1534 a.as_ref().clone(),
1535 b.as_ref().clone(),
1536 ));
1537 }
1538 }
1539 }
1540 }
1541 }
1542 }
1543 }
1544 }
1545 }
1546 }
1547 }
1548 }
1549 }
1550 None
1551}
1552
1553fn extract_implications(term: &Term) -> Option<(Vec<Term>, Term)> {
1558 let mut hyps = Vec::new();
1559 let mut current = term.clone();
1560
1561 while let Some((hyp, rest)) = extract_implication(¤t) {
1562 hyps.push(hyp);
1563 current = rest;
1564 }
1565
1566 if hyps.is_empty() {
1567 None
1568 } else {
1569 Some((hyps, current))
1570 }
1571}
1572
1573fn extract_forall_body(term: &Term) -> Option<Term> {
1580 if let Term::App(outer, lam) = term {
1582 if let Term::App(sapp_outer, x) = outer.as_ref() {
1583 if let Term::Global(ctor) = sapp_outer.as_ref() {
1584 if ctor == "SApp" {
1585 if let Term::App(sname, text) = x.as_ref() {
1587 if let Term::Global(sname_ctor) = sname.as_ref() {
1588 if sname_ctor == "SName" {
1589 if let Term::Lit(Literal::Text(s)) = text.as_ref() {
1590 if s == "Forall" {
1591 return extract_slam_body(lam);
1593 }
1594 }
1595 }
1596 }
1597 }
1598
1599 if let Term::App(inner, _t) = x.as_ref() {
1601 if let Term::App(sapp_inner, sname_forall) = inner.as_ref() {
1602 if let Term::Global(ctor2) = sapp_inner.as_ref() {
1603 if ctor2 == "SApp" {
1604 if let Term::App(sname, text) = sname_forall.as_ref() {
1605 if let Term::Global(sname_ctor) = sname.as_ref() {
1606 if sname_ctor == "SName" {
1607 if let Term::Lit(Literal::Text(s)) = text.as_ref() {
1608 if s == "Forall" {
1609 return extract_slam_body(lam);
1610 }
1611 }
1612 }
1613 }
1614 }
1615 }
1616 }
1617 }
1618 }
1619 }
1620 }
1621 }
1622 }
1623 None
1624}
1625
1626fn extract_slam_body(term: &Term) -> Option<Term> {
1628 if let Term::App(inner, body) = term {
1629 if let Term::App(slam, _t) = inner.as_ref() {
1630 if let Term::Global(name) = slam.as_ref() {
1631 if name == "SLam" {
1632 return Some(body.as_ref().clone());
1633 }
1634 }
1635 }
1636 }
1637 None
1638}
1639
1640fn syntax_equal(a: &Term, b: &Term) -> bool {
1642 a == b
1643}
1644
1645fn make_sname_error() -> Term {
1647 Term::App(
1648 Box::new(Term::Global("SName".to_string())),
1649 Box::new(Term::Lit(Literal::Text("Error".to_string()))),
1650 )
1651}
1652
1653fn make_sname(s: &str) -> Term {
1655 Term::App(
1656 Box::new(Term::Global("SName".to_string())),
1657 Box::new(Term::Lit(Literal::Text(s.to_string()))),
1658 )
1659}
1660
1661fn make_slit(n: i64) -> Term {
1663 Term::App(
1664 Box::new(Term::Global("SLit".to_string())),
1665 Box::new(Term::Lit(Literal::Int(n))),
1666 )
1667}
1668
1669fn make_sapp(f: Term, x: Term) -> Term {
1671 Term::App(
1672 Box::new(Term::App(
1673 Box::new(Term::Global("SApp".to_string())),
1674 Box::new(f),
1675 )),
1676 Box::new(x),
1677 )
1678}
1679
1680fn make_spi(a: Term, b: Term) -> Term {
1682 Term::App(
1683 Box::new(Term::App(
1684 Box::new(Term::Global("SPi".to_string())),
1685 Box::new(a),
1686 )),
1687 Box::new(b),
1688 )
1689}
1690
1691fn make_slam(a: Term, b: Term) -> Term {
1693 Term::App(
1694 Box::new(Term::App(
1695 Box::new(Term::Global("SLam".to_string())),
1696 Box::new(a),
1697 )),
1698 Box::new(b),
1699 )
1700}
1701
1702fn make_ssort(u: Term) -> Term {
1704 Term::App(
1705 Box::new(Term::Global("SSort".to_string())),
1706 Box::new(u),
1707 )
1708}
1709
1710fn make_svar(n: i64) -> Term {
1712 Term::App(
1713 Box::new(Term::Global("SVar".to_string())),
1714 Box::new(Term::Lit(Literal::Int(n))),
1715 )
1716}
1717
1718fn term_to_syntax(term: &Term) -> Option<Term> {
1731 match term {
1732 Term::Global(name) => Some(make_sname(name)),
1733
1734 Term::Var(name) => {
1735 Some(make_sname(name))
1737 }
1738
1739 Term::App(f, x) => {
1740 let sf = term_to_syntax(f)?;
1741 let sx = term_to_syntax(x)?;
1742 Some(make_sapp(sf, sx))
1743 }
1744
1745 Term::Pi { param_type, body_type, .. } => {
1746 let sp = term_to_syntax(param_type)?;
1747 let sb = term_to_syntax(body_type)?;
1748 Some(make_spi(sp, sb))
1749 }
1750
1751 Term::Lambda { param_type, body, .. } => {
1752 let sp = term_to_syntax(param_type)?;
1753 let sb = term_to_syntax(body)?;
1754 Some(make_slam(sp, sb))
1755 }
1756
1757 Term::Sort(Universe::Type(n)) => {
1758 let u = Term::App(
1759 Box::new(Term::Global("UType".to_string())),
1760 Box::new(Term::Lit(Literal::Int(*n as i64))),
1761 );
1762 Some(make_ssort(u))
1763 }
1764
1765 Term::Sort(Universe::Prop) => {
1766 let u = Term::Global("UProp".to_string());
1767 Some(make_ssort(u))
1768 }
1769
1770 Term::Lit(Literal::Int(n)) => Some(make_slit(*n)),
1771
1772 Term::Lit(Literal::Text(s)) => Some(make_sname(s)),
1773
1774 Term::Lit(Literal::Float(_)) => None,
1776
1777 Term::Match { .. } | Term::Fix { .. } | Term::Hole => None,
1779 }
1780}
1781
1782fn make_hint_derivation(hint_name: &str, goal: &Term) -> Term {
1786 let hint_marker = make_sapp(make_sname("Hint"), make_sname(hint_name));
1789
1790 Term::App(
1793 Box::new(Term::Global("DAutoSolve".to_string())),
1794 Box::new(goal.clone()),
1795 )
1796}
1797
1798fn try_apply_hint(ctx: &Context, hint_name: &str, hint_type: &Term, goal: &Term) -> Option<Term> {
1803 let hint_syntax = term_to_syntax(hint_type)?;
1805
1806 let norm_hint = normalize(ctx, &hint_syntax);
1808 let norm_goal = normalize(ctx, goal);
1809
1810 if syntax_equal(&norm_hint, &norm_goal) {
1812 return Some(make_hint_derivation(hint_name, goal));
1813 }
1814
1815 if let Term::App(outer, q) = &hint_syntax {
1818 if let Term::App(pi_ctor, p) = outer.as_ref() {
1819 if let Term::Global(name) = pi_ctor.as_ref() {
1820 if name == "SPi" {
1821 let norm_q = normalize(ctx, q);
1823 if syntax_equal(&norm_q, &norm_goal) {
1824 }
1828 }
1829 }
1830 }
1831 }
1832
1833 None
1834}
1835
1836fn make_forall_syntax(body: &Term) -> Term {
1838 let type0 = Term::App(
1839 Box::new(Term::Global("SSort".to_string())),
1840 Box::new(Term::App(
1841 Box::new(Term::Global("UType".to_string())),
1842 Box::new(Term::Lit(Literal::Int(0))),
1843 )),
1844 );
1845
1846 let slam = Term::App(
1848 Box::new(Term::App(
1849 Box::new(Term::Global("SLam".to_string())),
1850 Box::new(type0.clone()),
1851 )),
1852 Box::new(body.clone()),
1853 );
1854
1855 Term::App(
1857 Box::new(Term::App(
1858 Box::new(Term::Global("SApp".to_string())),
1859 Box::new(Term::App(
1860 Box::new(Term::App(
1861 Box::new(Term::Global("SApp".to_string())),
1862 Box::new(Term::App(
1863 Box::new(Term::Global("SName".to_string())),
1864 Box::new(Term::Lit(Literal::Text("Forall".to_string()))),
1865 )),
1866 )),
1867 Box::new(type0),
1868 )),
1869 )),
1870 Box::new(slam),
1871 )
1872}
1873
1874fn make_eq_syntax(type_s: &Term, term: &Term) -> Term {
1882 let eq_name = Term::App(
1883 Box::new(Term::Global("SName".to_string())),
1884 Box::new(Term::Lit(Literal::Text("Eq".to_string()))),
1885 );
1886
1887 let app1 = Term::App(
1889 Box::new(Term::App(
1890 Box::new(Term::Global("SApp".to_string())),
1891 Box::new(eq_name),
1892 )),
1893 Box::new(type_s.clone()),
1894 );
1895
1896 let app2 = Term::App(
1898 Box::new(Term::App(
1899 Box::new(Term::Global("SApp".to_string())),
1900 Box::new(app1),
1901 )),
1902 Box::new(term.clone()),
1903 );
1904
1905 Term::App(
1907 Box::new(Term::App(
1908 Box::new(Term::Global("SApp".to_string())),
1909 Box::new(app2),
1910 )),
1911 Box::new(term.clone()),
1912 )
1913}
1914
1915fn extract_eq(term: &Term) -> Option<(Term, Term, Term)> {
1919 if let Term::App(outer, b) = term {
1921 if let Term::App(sapp_outer, x) = outer.as_ref() {
1922 if let Term::Global(ctor) = sapp_outer.as_ref() {
1923 if ctor == "SApp" {
1924 if let Term::App(inner, a) = x.as_ref() {
1926 if let Term::App(sapp_inner, y) = inner.as_ref() {
1927 if let Term::Global(ctor2) = sapp_inner.as_ref() {
1928 if ctor2 == "SApp" {
1929 if let Term::App(inner2, t) = y.as_ref() {
1931 if let Term::App(sapp_inner2, sname_eq) = inner2.as_ref() {
1932 if let Term::Global(ctor3) = sapp_inner2.as_ref() {
1933 if ctor3 == "SApp" {
1934 if let Term::App(sname, text) = sname_eq.as_ref()
1936 {
1937 if let Term::Global(sname_ctor) =
1938 sname.as_ref()
1939 {
1940 if sname_ctor == "SName" {
1941 if let Term::Lit(Literal::Text(s)) =
1942 text.as_ref()
1943 {
1944 if s == "Eq" {
1945 return Some((
1946 t.as_ref().clone(),
1947 a.as_ref().clone(),
1948 b.as_ref().clone(),
1949 ));
1950 }
1951 }
1952 }
1953 }
1954 }
1955 }
1956 }
1957 }
1958 }
1959 }
1960 }
1961 }
1962 }
1963 }
1964 }
1965 }
1966 }
1967 None
1968}
1969
1970fn make_drefl(type_s: &Term, term: &Term) -> Term {
1972 let drefl = Term::Global("DRefl".to_string());
1973 let app1 = Term::App(Box::new(drefl), Box::new(type_s.clone()));
1974 Term::App(Box::new(app1), Box::new(term.clone()))
1975}
1976
1977fn make_error_derivation() -> Term {
1979 let daxiom = Term::Global("DAxiom".to_string());
1980 let error = make_sname_error();
1981 Term::App(Box::new(daxiom), Box::new(error))
1982}
1983
1984fn make_daxiom(goal: &Term) -> Term {
1986 let daxiom = Term::Global("DAxiom".to_string());
1987 Term::App(Box::new(daxiom), Box::new(goal.clone()))
1988}
1989
1990fn try_try_refl_reduce(ctx: &Context, goal: &Term) -> Option<Term> {
1996 let norm_goal = normalize(ctx, goal);
1998
1999 if let Some((type_s, left, right)) = extract_eq(&norm_goal) {
2001 if syntax_equal(&left, &right) {
2003 return Some(make_drefl(&type_s, &left));
2005 }
2006 }
2007
2008 Some(make_error_derivation())
2010}
2011
2012use crate::ring;
2017use crate::lia;
2018use crate::cc;
2019use crate::simp;
2020
2021fn try_try_ring_reduce(ctx: &Context, goal: &Term) -> Option<Term> {
2029 let norm_goal = normalize(ctx, goal);
2031
2032 if let Some((type_s, left, right)) = extract_eq(&norm_goal) {
2034 if !is_ring_type(&type_s) {
2036 return Some(make_error_derivation());
2037 }
2038
2039 let poly_left = match ring::reify(&left) {
2041 Ok(p) => p,
2042 Err(_) => return Some(make_error_derivation()),
2043 };
2044 let poly_right = match ring::reify(&right) {
2045 Ok(p) => p,
2046 Err(_) => return Some(make_error_derivation()),
2047 };
2048
2049 if poly_left.canonical_eq(&poly_right) {
2051 return Some(Term::App(
2053 Box::new(Term::Global("DRingSolve".to_string())),
2054 Box::new(norm_goal),
2055 ));
2056 }
2057 }
2058
2059 Some(make_error_derivation())
2061}
2062
2063fn try_dring_solve_conclude(ctx: &Context, goal: &Term) -> Option<Term> {
2067 let norm_goal = normalize(ctx, goal);
2068
2069 if let Some((type_s, left, right)) = extract_eq(&norm_goal) {
2071 if !is_ring_type(&type_s) {
2073 return Some(make_sname_error());
2074 }
2075
2076 let poly_left = match ring::reify(&left) {
2078 Ok(p) => p,
2079 Err(_) => return Some(make_sname_error()),
2080 };
2081 let poly_right = match ring::reify(&right) {
2082 Ok(p) => p,
2083 Err(_) => return Some(make_sname_error()),
2084 };
2085
2086 if poly_left.canonical_eq(&poly_right) {
2087 return Some(norm_goal);
2088 }
2089 }
2090
2091 Some(make_sname_error())
2092}
2093
2094fn is_ring_type(type_term: &Term) -> bool {
2096 if let Some(name) = extract_sname_from_syntax(type_term) {
2097 return name == "Int" || name == "Nat";
2098 }
2099 false
2100}
2101
2102fn extract_sname_from_syntax(term: &Term) -> Option<String> {
2104 if let Term::App(ctor, arg) = term {
2105 if let Term::Global(name) = ctor.as_ref() {
2106 if name == "SName" {
2107 if let Term::Lit(Literal::Text(s)) = arg.as_ref() {
2108 return Some(s.clone());
2109 }
2110 }
2111 }
2112 }
2113 None
2114}
2115
2116fn try_try_lia_reduce(ctx: &Context, goal: &Term) -> Option<Term> {
2128 let norm_goal = normalize(ctx, goal);
2130
2131 if let Some((rel, lhs_term, rhs_term)) = lia::extract_comparison(&norm_goal) {
2133 let lhs = match lia::reify_linear(&lhs_term) {
2135 Ok(l) => l,
2136 Err(_) => return Some(make_error_derivation()),
2137 };
2138 let rhs = match lia::reify_linear(&rhs_term) {
2139 Ok(r) => r,
2140 Err(_) => return Some(make_error_derivation()),
2141 };
2142
2143 if let Some(negated) = lia::goal_to_negated_constraint(&rel, &lhs, &rhs) {
2145 if lia::fourier_motzkin_unsat(&[negated]) {
2147 return Some(Term::App(
2149 Box::new(Term::Global("DLiaSolve".to_string())),
2150 Box::new(norm_goal),
2151 ));
2152 }
2153 }
2154 }
2155
2156 Some(make_error_derivation())
2158}
2159
2160fn try_dlia_solve_conclude(ctx: &Context, goal: &Term) -> Option<Term> {
2164 let norm_goal = normalize(ctx, goal);
2165
2166 if let Some((rel, lhs_term, rhs_term)) = lia::extract_comparison(&norm_goal) {
2168 let lhs = match lia::reify_linear(&lhs_term) {
2170 Ok(l) => l,
2171 Err(_) => return Some(make_sname_error()),
2172 };
2173 let rhs = match lia::reify_linear(&rhs_term) {
2174 Ok(r) => r,
2175 Err(_) => return Some(make_sname_error()),
2176 };
2177
2178 if let Some(negated) = lia::goal_to_negated_constraint(&rel, &lhs, &rhs) {
2180 if lia::fourier_motzkin_unsat(&[negated]) {
2181 return Some(norm_goal);
2182 }
2183 }
2184 }
2185
2186 Some(make_sname_error())
2187}
2188
2189fn try_try_cc_reduce(ctx: &Context, goal: &Term) -> Option<Term> {
2201 let norm_goal = normalize(ctx, goal);
2203
2204 if cc::check_goal(&norm_goal) {
2208 return Some(Term::App(
2210 Box::new(Term::Global("DccSolve".to_string())),
2211 Box::new(norm_goal),
2212 ));
2213 }
2214
2215 Some(make_error_derivation())
2217}
2218
2219fn try_dcc_solve_conclude(ctx: &Context, goal: &Term) -> Option<Term> {
2223 let norm_goal = normalize(ctx, goal);
2224
2225 if cc::check_goal(&norm_goal) {
2227 return Some(norm_goal);
2228 }
2229
2230 Some(make_sname_error())
2231}
2232
2233fn try_try_simp_reduce(ctx: &Context, goal: &Term) -> Option<Term> {
2243 let norm_goal = normalize(ctx, goal);
2245
2246 if simp::check_goal(&norm_goal) {
2251 return Some(Term::App(
2253 Box::new(Term::Global("DSimpSolve".to_string())),
2254 Box::new(norm_goal),
2255 ));
2256 }
2257
2258 Some(make_error_derivation())
2260}
2261
2262fn try_dsimp_solve_conclude(ctx: &Context, goal: &Term) -> Option<Term> {
2266 let norm_goal = normalize(ctx, goal);
2267
2268 if simp::check_goal(&norm_goal) {
2270 return Some(norm_goal);
2271 }
2272
2273 Some(make_sname_error())
2274}
2275
2276fn try_try_omega_reduce(ctx: &Context, goal: &Term) -> Option<Term> {
2293 let norm_goal = normalize(ctx, goal);
2294
2295 if let Some((hyps, conclusion)) = extract_implications(&norm_goal) {
2297 let mut constraints = Vec::new();
2299
2300 for hyp in &hyps {
2301 if let Some((rel, lhs_term, rhs_term)) = omega::extract_comparison(hyp) {
2303 if let (Some(lhs), Some(rhs)) =
2304 (omega::reify_int_linear(&lhs_term), omega::reify_int_linear(&rhs_term))
2305 {
2306 match rel.as_str() {
2310 "Lt" | "lt" => {
2311 let mut expr = lhs.sub(&rhs);
2313 expr.constant += 1;
2314 constraints.push(omega::IntConstraint { expr, strict: false });
2315 }
2316 "Le" | "le" => {
2317 constraints.push(omega::IntConstraint {
2319 expr: lhs.sub(&rhs),
2320 strict: false,
2321 });
2322 }
2323 "Gt" | "gt" => {
2324 let mut expr = rhs.sub(&lhs);
2326 expr.constant += 1;
2327 constraints.push(omega::IntConstraint { expr, strict: false });
2328 }
2329 "Ge" | "ge" => {
2330 constraints.push(omega::IntConstraint {
2332 expr: rhs.sub(&lhs),
2333 strict: false,
2334 });
2335 }
2336 _ => {}
2337 }
2338 }
2339 }
2340 }
2341
2342 if let Some((rel, lhs_term, rhs_term)) = omega::extract_comparison(&conclusion) {
2344 if let (Some(lhs), Some(rhs)) =
2345 (omega::reify_int_linear(&lhs_term), omega::reify_int_linear(&rhs_term))
2346 {
2347 if let Some(neg_constraint) = omega::goal_to_negated_constraint(&rel, &lhs, &rhs) {
2349 let mut all_constraints = constraints;
2350 all_constraints.push(neg_constraint);
2351
2352 if omega::omega_unsat(&all_constraints) {
2353 return Some(Term::App(
2354 Box::new(Term::Global("DOmegaSolve".to_string())),
2355 Box::new(norm_goal),
2356 ));
2357 }
2358 }
2359 }
2360 }
2361
2362 return Some(make_error_derivation());
2364 }
2365
2366 if let Some((rel, lhs_term, rhs_term)) = omega::extract_comparison(&norm_goal) {
2368 if let (Some(lhs), Some(rhs)) =
2369 (omega::reify_int_linear(&lhs_term), omega::reify_int_linear(&rhs_term))
2370 {
2371 if let Some(constraint) = omega::goal_to_negated_constraint(&rel, &lhs, &rhs) {
2372 if omega::omega_unsat(&[constraint]) {
2373 return Some(Term::App(
2374 Box::new(Term::Global("DOmegaSolve".to_string())),
2375 Box::new(norm_goal),
2376 ));
2377 }
2378 }
2379 }
2380 }
2381
2382 Some(make_error_derivation())
2383}
2384
2385fn try_domega_solve_conclude(ctx: &Context, goal: &Term) -> Option<Term> {
2389 let norm_goal = normalize(ctx, goal);
2390
2391 if let Some((hyps, conclusion)) = extract_implications(&norm_goal) {
2394 let mut constraints = Vec::new();
2395
2396 for hyp in &hyps {
2397 if let Some((rel, lhs_term, rhs_term)) = omega::extract_comparison(hyp) {
2398 if let (Some(lhs), Some(rhs)) =
2399 (omega::reify_int_linear(&lhs_term), omega::reify_int_linear(&rhs_term))
2400 {
2401 match rel.as_str() {
2402 "Lt" | "lt" => {
2403 let mut expr = lhs.sub(&rhs);
2404 expr.constant += 1;
2405 constraints.push(omega::IntConstraint { expr, strict: false });
2406 }
2407 "Le" | "le" => {
2408 constraints.push(omega::IntConstraint {
2409 expr: lhs.sub(&rhs),
2410 strict: false,
2411 });
2412 }
2413 "Gt" | "gt" => {
2414 let mut expr = rhs.sub(&lhs);
2415 expr.constant += 1;
2416 constraints.push(omega::IntConstraint { expr, strict: false });
2417 }
2418 "Ge" | "ge" => {
2419 constraints.push(omega::IntConstraint {
2420 expr: rhs.sub(&lhs),
2421 strict: false,
2422 });
2423 }
2424 _ => {}
2425 }
2426 }
2427 }
2428 }
2429
2430 if let Some((rel, lhs_term, rhs_term)) = omega::extract_comparison(&conclusion) {
2431 if let (Some(lhs), Some(rhs)) =
2432 (omega::reify_int_linear(&lhs_term), omega::reify_int_linear(&rhs_term))
2433 {
2434 if let Some(neg_constraint) = omega::goal_to_negated_constraint(&rel, &lhs, &rhs) {
2435 let mut all_constraints = constraints;
2436 all_constraints.push(neg_constraint);
2437
2438 if omega::omega_unsat(&all_constraints) {
2439 return Some(norm_goal);
2440 }
2441 }
2442 }
2443 }
2444
2445 return Some(make_sname_error());
2446 }
2447
2448 if let Some((rel, lhs_term, rhs_term)) = omega::extract_comparison(&norm_goal) {
2450 if let (Some(lhs), Some(rhs)) =
2451 (omega::reify_int_linear(&lhs_term), omega::reify_int_linear(&rhs_term))
2452 {
2453 if let Some(constraint) = omega::goal_to_negated_constraint(&rel, &lhs, &rhs) {
2454 if omega::omega_unsat(&[constraint]) {
2455 return Some(norm_goal);
2456 }
2457 }
2458 }
2459 }
2460
2461 Some(make_sname_error())
2462}
2463
2464fn is_error_derivation(term: &Term) -> bool {
2472 if let Term::App(ctor, arg) = term {
2474 if let Term::Global(name) = ctor.as_ref() {
2475 if name == "DAxiom" {
2476 if let Term::App(sname, inner) = arg.as_ref() {
2478 if let Term::Global(sn) = sname.as_ref() {
2479 if sn == "SName" {
2480 if let Term::Lit(Literal::Text(s)) = inner.as_ref() {
2481 return s == "Error";
2482 }
2483 }
2484 if sn == "SApp" {
2485 return true; }
2488 }
2489 }
2490 if let Term::Global(sn) = arg.as_ref() {
2492 return sn == "Error";
2494 }
2495 return true; }
2497 }
2498 }
2499 false
2500}
2501
2502fn try_try_auto_reduce(ctx: &Context, goal: &Term) -> Option<Term> {
2507 let norm_goal = normalize(ctx, goal);
2508
2509 if let Term::App(ctor, inner) = &norm_goal {
2512 if let Term::Global(name) = ctor.as_ref() {
2513 if name == "SName" {
2514 if let Term::Lit(Literal::Text(s)) = inner.as_ref() {
2515 if s == "True" {
2516 return Some(Term::App(
2518 Box::new(Term::Global("DAutoSolve".to_string())),
2519 Box::new(norm_goal),
2520 ));
2521 }
2522 if s == "False" {
2523 return Some(make_error_derivation());
2525 }
2526 }
2527 }
2528 }
2529 }
2530
2531 if let Some(result) = try_try_simp_reduce(ctx, &norm_goal) {
2533 if !is_error_derivation(&result) {
2534 return Some(result);
2535 }
2536 }
2537
2538 if let Some(result) = try_try_ring_reduce(ctx, &norm_goal) {
2540 if !is_error_derivation(&result) {
2541 return Some(result);
2542 }
2543 }
2544
2545 if let Some(result) = try_try_cc_reduce(ctx, &norm_goal) {
2547 if !is_error_derivation(&result) {
2548 return Some(result);
2549 }
2550 }
2551
2552 if let Some(result) = try_try_omega_reduce(ctx, &norm_goal) {
2554 if !is_error_derivation(&result) {
2555 return Some(result);
2556 }
2557 }
2558
2559 if let Some(result) = try_try_lia_reduce(ctx, &norm_goal) {
2561 if !is_error_derivation(&result) {
2562 return Some(result);
2563 }
2564 }
2565
2566 for hint_name in ctx.get_hints() {
2568 if let Some(hint_type) = ctx.get_global(hint_name) {
2569 if let Some(result) = try_apply_hint(ctx, hint_name, hint_type, &norm_goal) {
2570 return Some(result);
2571 }
2572 }
2573 }
2574
2575 Some(make_error_derivation())
2577}
2578
2579fn try_dauto_solve_conclude(ctx: &Context, goal: &Term) -> Option<Term> {
2581 let norm_goal = normalize(ctx, goal);
2582
2583 if let Term::App(ctor, inner) = &norm_goal {
2585 if let Term::Global(name) = ctor.as_ref() {
2586 if name == "SName" {
2587 if let Term::Lit(Literal::Text(s)) = inner.as_ref() {
2588 if s == "True" {
2589 return Some(norm_goal.clone());
2590 }
2591 }
2592 }
2593 }
2594 }
2595
2596 if let Some(result) = try_try_simp_reduce(ctx, &norm_goal) {
2598 if !is_error_derivation(&result) {
2599 return Some(norm_goal.clone());
2600 }
2601 }
2602 if let Some(result) = try_try_ring_reduce(ctx, &norm_goal) {
2603 if !is_error_derivation(&result) {
2604 return Some(norm_goal.clone());
2605 }
2606 }
2607 if let Some(result) = try_try_cc_reduce(ctx, &norm_goal) {
2608 if !is_error_derivation(&result) {
2609 return Some(norm_goal.clone());
2610 }
2611 }
2612 if let Some(result) = try_try_omega_reduce(ctx, &norm_goal) {
2613 if !is_error_derivation(&result) {
2614 return Some(norm_goal.clone());
2615 }
2616 }
2617 if let Some(result) = try_try_lia_reduce(ctx, &norm_goal) {
2618 if !is_error_derivation(&result) {
2619 return Some(norm_goal.clone());
2620 }
2621 }
2622
2623 for hint_name in ctx.get_hints() {
2625 if let Some(hint_type) = ctx.get_global(hint_name) {
2626 if try_apply_hint(ctx, hint_name, hint_type, &norm_goal).is_some() {
2627 return Some(norm_goal);
2628 }
2629 }
2630 }
2631
2632 Some(make_sname_error())
2633}
2634
2635fn try_induction_num_cases_reduce(ctx: &Context, ind_type: &Term) -> Option<Term> {
2645 let ind_name = match extract_inductive_name_from_syntax(ind_type) {
2647 Some(name) => name,
2648 None => {
2649 return Some(Term::Global("Zero".to_string()));
2651 }
2652 };
2653
2654 let constructors = ctx.get_constructors(&ind_name);
2656 let num_ctors = constructors.len();
2657
2658 let mut result = Term::Global("Zero".to_string());
2660 for _ in 0..num_ctors {
2661 result = Term::App(
2662 Box::new(Term::Global("Succ".to_string())),
2663 Box::new(result),
2664 );
2665 }
2666
2667 Some(result)
2668}
2669
2670fn try_induction_base_goal_reduce(
2674 ctx: &Context,
2675 ind_type: &Term,
2676 motive: &Term,
2677) -> Option<Term> {
2678 let ind_name = match extract_inductive_name_from_syntax(ind_type) {
2680 Some(name) => name,
2681 None => return Some(make_sname_error()),
2682 };
2683
2684 let constructors = ctx.get_constructors(&ind_name);
2686 if constructors.is_empty() {
2687 return Some(make_sname_error());
2688 }
2689
2690 let motive_body = match extract_slam_body(motive) {
2692 Some(body) => body,
2693 None => return Some(make_sname_error()),
2694 };
2695
2696 let (ctor_name, _) = constructors[0];
2698 build_case_expected(ctx, ctor_name, &constructors, &motive_body, ind_type)
2699}
2700
2701fn try_induction_step_goal_reduce(
2705 ctx: &Context,
2706 ind_type: &Term,
2707 motive: &Term,
2708 ctor_idx: &Term,
2709) -> Option<Term> {
2710 let ind_name = match extract_inductive_name_from_syntax(ind_type) {
2712 Some(name) => name,
2713 None => return Some(make_sname_error()),
2714 };
2715
2716 let constructors = ctx.get_constructors(&ind_name);
2718 if constructors.is_empty() {
2719 return Some(make_sname_error());
2720 }
2721
2722 let idx = nat_to_usize(ctor_idx)?;
2724 if idx >= constructors.len() {
2725 return Some(make_sname_error());
2726 }
2727
2728 let motive_body = match extract_slam_body(motive) {
2730 Some(body) => body,
2731 None => return Some(make_sname_error()),
2732 };
2733
2734 let (ctor_name, _) = constructors[idx];
2736 build_case_expected(ctx, ctor_name, &constructors, &motive_body, ind_type)
2737}
2738
2739fn nat_to_usize(term: &Term) -> Option<usize> {
2745 match term {
2746 Term::Global(name) if name == "Zero" => Some(0),
2747 Term::App(succ, inner) => {
2748 if let Term::Global(name) = succ.as_ref() {
2749 if name == "Succ" {
2750 return nat_to_usize(inner).map(|n| n + 1);
2751 }
2752 }
2753 None
2754 }
2755 _ => None,
2756 }
2757}
2758
2759fn try_try_induction_reduce(
2765 ctx: &Context,
2766 ind_type: &Term,
2767 motive: &Term,
2768 cases: &Term,
2769) -> Option<Term> {
2770 let ind_name = match extract_inductive_name_from_syntax(ind_type) {
2772 Some(name) => name,
2773 None => return Some(make_error_derivation()),
2774 };
2775
2776 let constructors = ctx.get_constructors(&ind_name);
2778 if constructors.is_empty() {
2779 return Some(make_error_derivation());
2780 }
2781
2782 let case_proofs = match extract_case_proofs(cases) {
2784 Some(proofs) => proofs,
2785 None => return Some(make_error_derivation()),
2786 };
2787
2788 if case_proofs.len() != constructors.len() {
2790 return Some(make_error_derivation());
2791 }
2792
2793 Some(Term::App(
2795 Box::new(Term::App(
2796 Box::new(Term::App(
2797 Box::new(Term::Global("DElim".to_string())),
2798 Box::new(ind_type.clone()),
2799 )),
2800 Box::new(motive.clone()),
2801 )),
2802 Box::new(cases.clone()),
2803 ))
2804}
2805
2806fn try_dinduction_reduce(
2821 ctx: &Context,
2822 motive: &Term,
2823 base: &Term,
2824 step: &Term,
2825) -> Option<Term> {
2826 let norm_motive = normalize(ctx, motive);
2828 let norm_base = normalize(ctx, base);
2829 let norm_step = normalize(ctx, step);
2830
2831 let motive_body = match extract_slam_body(&norm_motive) {
2833 Some(body) => body,
2834 None => return Some(make_sname_error()),
2835 };
2836
2837 let zero = make_sname("Zero");
2839 let expected_base = match try_syn_subst_reduce(ctx, &zero, 0, &motive_body) {
2840 Some(b) => b,
2841 None => return Some(make_sname_error()),
2842 };
2843
2844 let base_conc = match try_concludes_reduce(ctx, &norm_base) {
2846 Some(c) => c,
2847 None => return Some(make_sname_error()),
2848 };
2849
2850 if !syntax_equal(&base_conc, &expected_base) {
2852 return Some(make_sname_error());
2853 }
2854
2855 let expected_step = match build_induction_step_formula(ctx, &motive_body) {
2857 Some(s) => s,
2858 None => return Some(make_sname_error()),
2859 };
2860
2861 let step_conc = match try_concludes_reduce(ctx, &norm_step) {
2863 Some(c) => c,
2864 None => return Some(make_sname_error()),
2865 };
2866
2867 if !syntax_equal(&step_conc, &expected_step) {
2869 return Some(make_sname_error());
2870 }
2871
2872 Some(make_forall_nat_syntax(&norm_motive))
2874}
2875
2876fn build_induction_step_formula(ctx: &Context, motive_body: &Term) -> Option<Term> {
2881 let p_k = motive_body.clone();
2883
2884 let succ_var = Term::App(
2886 Box::new(Term::App(
2887 Box::new(Term::Global("SApp".to_string())),
2888 Box::new(make_sname("Succ")),
2889 )),
2890 Box::new(Term::App(
2891 Box::new(Term::Global("SVar".to_string())),
2892 Box::new(Term::Lit(Literal::Int(0))),
2893 )),
2894 );
2895 let p_succ_k = try_syn_subst_reduce(ctx, &succ_var, 0, motive_body)?;
2896
2897 let implies_body = make_implies_syntax(&p_k, &p_succ_k);
2899
2900 let slam = Term::App(
2902 Box::new(Term::App(
2903 Box::new(Term::Global("SLam".to_string())),
2904 Box::new(make_sname("Nat")),
2905 )),
2906 Box::new(implies_body),
2907 );
2908
2909 Some(make_forall_syntax_with_type(&make_sname("Nat"), &slam))
2911}
2912
2913fn make_implies_syntax(a: &Term, b: &Term) -> Term {
2915 let app1 = Term::App(
2917 Box::new(Term::App(
2918 Box::new(Term::Global("SApp".to_string())),
2919 Box::new(make_sname("Implies")),
2920 )),
2921 Box::new(a.clone()),
2922 );
2923
2924 Term::App(
2926 Box::new(Term::App(
2927 Box::new(Term::Global("SApp".to_string())),
2928 Box::new(app1),
2929 )),
2930 Box::new(b.clone()),
2931 )
2932}
2933
2934fn make_forall_nat_syntax(motive: &Term) -> Term {
2936 make_forall_syntax_with_type(&make_sname("Nat"), motive)
2937}
2938
2939fn make_forall_syntax_with_type(type_s: &Term, body: &Term) -> Term {
2941 let app1 = Term::App(
2943 Box::new(Term::App(
2944 Box::new(Term::Global("SApp".to_string())),
2945 Box::new(make_sname("Forall")),
2946 )),
2947 Box::new(type_s.clone()),
2948 );
2949
2950 Term::App(
2952 Box::new(Term::App(
2953 Box::new(Term::Global("SApp".to_string())),
2954 Box::new(app1),
2955 )),
2956 Box::new(body.clone()),
2957 )
2958}
2959
2960fn try_dcompute_conclude(ctx: &Context, goal: &Term) -> Option<Term> {
2974 let norm_goal = normalize(ctx, goal);
2975
2976 let parts = extract_eq_syntax_parts(&norm_goal);
2979 if parts.is_none() {
2980 return Some(make_sname_error());
2982 }
2983 let (_, a, b) = parts.unwrap();
2984
2985 let fuel = 1000;
2987 let a_eval = match try_syn_eval_reduce(ctx, fuel, &a) {
2988 Some(e) => e,
2989 None => return Some(make_sname_error()),
2990 };
2991 let b_eval = match try_syn_eval_reduce(ctx, fuel, &b) {
2992 Some(e) => e,
2993 None => return Some(make_sname_error()),
2994 };
2995
2996 if syntax_equal(&a_eval, &b_eval) {
2998 Some(norm_goal)
2999 } else {
3000 Some(make_sname_error())
3001 }
3002}
3003
3004fn extract_eq_syntax_parts(term: &Term) -> Option<(Term, Term, Term)> {
3008 if let Term::App(partial2, b) = term {
3011 if let Term::App(sapp2, inner2) = partial2.as_ref() {
3012 if let Term::Global(sapp2_name) = sapp2.as_ref() {
3013 if sapp2_name != "SApp" {
3014 return None;
3015 }
3016 } else {
3017 return None;
3018 }
3019
3020 if let Term::App(partial1, a) = inner2.as_ref() {
3021 if let Term::App(sapp1, inner1) = partial1.as_ref() {
3022 if let Term::Global(sapp1_name) = sapp1.as_ref() {
3023 if sapp1_name != "SApp" {
3024 return None;
3025 }
3026 } else {
3027 return None;
3028 }
3029
3030 if let Term::App(eq_t, t) = inner1.as_ref() {
3031 if let Term::App(sapp0, eq_sname) = eq_t.as_ref() {
3032 if let Term::Global(sapp0_name) = sapp0.as_ref() {
3033 if sapp0_name != "SApp" {
3034 return None;
3035 }
3036 } else {
3037 return None;
3038 }
3039
3040 if let Term::App(sname_ctor, eq_str) = eq_sname.as_ref() {
3042 if let Term::Global(ctor) = sname_ctor.as_ref() {
3043 if ctor == "SName" {
3044 if let Term::Lit(Literal::Text(s)) = eq_str.as_ref() {
3045 if s == "Eq" {
3046 return Some((
3047 t.as_ref().clone(),
3048 a.as_ref().clone(),
3049 b.as_ref().clone(),
3050 ));
3051 }
3052 }
3053 }
3054 }
3055 }
3056 }
3057 }
3058 }
3059 }
3060 }
3061 }
3062 None
3063}
3064
3065fn try_tact_orelse_reduce(
3075 ctx: &Context,
3076 t1: &Term,
3077 t2: &Term,
3078 goal: &Term,
3079) -> Option<Term> {
3080 let norm_goal = normalize(ctx, goal);
3081
3082 let d1_app = Term::App(Box::new(t1.clone()), Box::new(norm_goal.clone()));
3084 let d1 = normalize(ctx, &d1_app);
3085
3086 if let Some(conc1) = try_concludes_reduce(ctx, &d1) {
3088 if is_error_syntax(&conc1) {
3089 let d2_app = Term::App(Box::new(t2.clone()), Box::new(norm_goal));
3091 return Some(normalize(ctx, &d2_app));
3092 } else {
3093 return Some(d1);
3095 }
3096 }
3097
3098 Some(make_error_derivation())
3100}
3101
3102fn is_error_syntax(term: &Term) -> bool {
3104 if let Term::App(ctor, arg) = term {
3106 if let Term::Global(name) = ctor.as_ref() {
3107 if name == "SName" {
3108 if let Term::Lit(Literal::Text(s)) = arg.as_ref() {
3109 return s == "Error";
3110 }
3111 }
3112 }
3113 }
3114 false
3115}
3116
3117fn try_tact_try_reduce(ctx: &Context, t: &Term, goal: &Term) -> Option<Term> {
3123 let norm_goal = normalize(ctx, goal);
3124
3125 let d_app = Term::App(Box::new(t.clone()), Box::new(norm_goal.clone()));
3127 let d = normalize(ctx, &d_app);
3128
3129 if let Some(conc) = try_concludes_reduce(ctx, &d) {
3131 if is_error_syntax(&conc) {
3132 return Some(make_daxiom(&norm_goal));
3134 } else {
3135 return Some(d);
3137 }
3138 }
3139
3140 Some(make_daxiom(&norm_goal))
3142}
3143
3144fn try_tact_repeat_reduce(ctx: &Context, t: &Term, goal: &Term) -> Option<Term> {
3149 const MAX_ITERATIONS: usize = 100;
3150
3151 let norm_goal = normalize(ctx, goal);
3152 let mut current_goal = norm_goal.clone();
3153 let mut last_successful_deriv: Option<Term> = None;
3154
3155 for _ in 0..MAX_ITERATIONS {
3156 let d_app = Term::App(Box::new(t.clone()), Box::new(current_goal.clone()));
3158 let d = normalize(ctx, &d_app);
3159
3160 if let Some(conc) = try_concludes_reduce(ctx, &d) {
3162 if is_error_syntax(&conc) {
3163 break;
3165 }
3166
3167 if syntax_equal(&conc, ¤t_goal) {
3169 break;
3171 }
3172
3173 current_goal = conc;
3175 last_successful_deriv = Some(d);
3176 } else {
3177 break;
3179 }
3180 }
3181
3182 last_successful_deriv.or_else(|| Some(make_daxiom(&norm_goal)))
3184}
3185
3186fn try_tact_then_reduce(
3192 ctx: &Context,
3193 t1: &Term,
3194 t2: &Term,
3195 goal: &Term,
3196) -> Option<Term> {
3197 let norm_goal = normalize(ctx, goal);
3198
3199 let d1_app = Term::App(Box::new(t1.clone()), Box::new(norm_goal.clone()));
3201 let d1 = normalize(ctx, &d1_app);
3202
3203 if let Some(conc1) = try_concludes_reduce(ctx, &d1) {
3205 if is_error_syntax(&conc1) {
3206 return Some(make_error_derivation());
3208 }
3209
3210 let d2_app = Term::App(Box::new(t2.clone()), Box::new(conc1));
3212 let d2 = normalize(ctx, &d2_app);
3213
3214 return Some(d2);
3216 }
3217
3218 Some(make_error_derivation())
3220}
3221
3222fn try_tact_first_reduce(ctx: &Context, tactics: &Term, goal: &Term) -> Option<Term> {
3227 let norm_goal = normalize(ctx, goal);
3228
3229 let tactic_vec = extract_tlist(tactics)?;
3231
3232 for tactic in tactic_vec {
3233 let d_app = Term::App(Box::new(tactic), Box::new(norm_goal.clone()));
3235 let d = normalize(ctx, &d_app);
3236
3237 if let Some(conc) = try_concludes_reduce(ctx, &d) {
3239 if !is_error_syntax(&conc) {
3240 return Some(d);
3242 }
3243 }
3245 }
3246
3247 Some(make_error_derivation())
3249}
3250
3251fn extract_tlist(term: &Term) -> Option<Vec<Term>> {
3258 let mut result = Vec::new();
3259 let mut current = term.clone();
3260
3261 loop {
3262 match ¤t {
3263 Term::App(tnil, _type) => {
3265 if let Term::Global(name) = tnil.as_ref() {
3266 if name == "TNil" {
3267 break;
3269 }
3270 }
3271 if let Term::App(partial2, tail) = ¤t {
3273 if let Term::App(partial1, head) = partial2.as_ref() {
3274 if let Term::App(tcons, _type) = partial1.as_ref() {
3275 if let Term::Global(name) = tcons.as_ref() {
3276 if name == "TCons" {
3277 result.push(head.as_ref().clone());
3278 current = tail.as_ref().clone();
3279 continue;
3280 }
3281 }
3282 }
3283 }
3284 }
3285 return None;
3287 }
3288 Term::Global(name) if name == "TNil" => {
3290 break;
3291 }
3292 _ => {
3293 return None;
3295 }
3296 }
3297 }
3298
3299 Some(result)
3300}
3301
3302fn try_tact_solve_reduce(ctx: &Context, t: &Term, goal: &Term) -> Option<Term> {
3308 let norm_goal = normalize(ctx, goal);
3309
3310 let d_app = Term::App(Box::new(t.clone()), Box::new(norm_goal.clone()));
3312 let d = normalize(ctx, &d_app);
3313
3314 if let Some(conc) = try_concludes_reduce(ctx, &d) {
3316 if is_error_syntax(&conc) {
3317 return Some(make_error_derivation());
3319 }
3320 return Some(d);
3322 }
3323
3324 Some(make_error_derivation())
3326}
3327
3328fn try_dcong_conclude(ctx: &Context, context: &Term, eq_proof: &Term) -> Option<Term> {
3339 let eq_conc = try_concludes_reduce(ctx, eq_proof)?;
3341
3342 let parts = extract_eq_syntax_parts(&eq_conc);
3344 if parts.is_none() {
3345 return Some(make_sname_error());
3347 }
3348 let (_type_term, lhs, rhs) = parts.unwrap();
3349
3350 let norm_context = normalize(ctx, context);
3352
3353 let slam_parts = extract_slam_parts(&norm_context);
3355 if slam_parts.is_none() {
3356 return Some(make_sname_error());
3358 }
3359 let (param_type, body) = slam_parts.unwrap();
3360
3361 let fa = try_syn_subst_reduce(ctx, &lhs, 0, &body)?;
3363 let fb = try_syn_subst_reduce(ctx, &rhs, 0, &body)?;
3364
3365 Some(make_eq_syntax_three(¶m_type, &fa, &fb))
3367}
3368
3369fn extract_slam_parts(term: &Term) -> Option<(Term, Term)> {
3373 if let Term::App(inner, body) = term {
3374 if let Term::App(slam_ctor, param_type) = inner.as_ref() {
3375 if let Term::Global(name) = slam_ctor.as_ref() {
3376 if name == "SLam" {
3377 return Some((param_type.as_ref().clone(), body.as_ref().clone()));
3378 }
3379 }
3380 }
3381 }
3382 None
3383}
3384
3385fn make_eq_syntax_three(type_s: &Term, a: &Term, b: &Term) -> Term {
3389 let eq_name = Term::App(
3390 Box::new(Term::Global("SName".to_string())),
3391 Box::new(Term::Lit(Literal::Text("Eq".to_string()))),
3392 );
3393
3394 let app1 = Term::App(
3396 Box::new(Term::App(
3397 Box::new(Term::Global("SApp".to_string())),
3398 Box::new(eq_name),
3399 )),
3400 Box::new(type_s.clone()),
3401 );
3402
3403 let app2 = Term::App(
3405 Box::new(Term::App(
3406 Box::new(Term::Global("SApp".to_string())),
3407 Box::new(app1),
3408 )),
3409 Box::new(a.clone()),
3410 );
3411
3412 Term::App(
3414 Box::new(Term::App(
3415 Box::new(Term::Global("SApp".to_string())),
3416 Box::new(app2),
3417 )),
3418 Box::new(b.clone()),
3419 )
3420}
3421
3422fn try_delim_conclude(
3438 ctx: &Context,
3439 ind_type: &Term,
3440 motive: &Term,
3441 cases: &Term,
3442) -> Option<Term> {
3443 let norm_ind_type = normalize(ctx, ind_type);
3445 let norm_motive = normalize(ctx, motive);
3446 let norm_cases = normalize(ctx, cases);
3447
3448 let ind_name = match extract_inductive_name_from_syntax(&norm_ind_type) {
3450 Some(name) => name,
3451 None => return Some(make_sname_error()),
3452 };
3453
3454 let constructors = ctx.get_constructors(&ind_name);
3456 if constructors.is_empty() {
3457 return Some(make_sname_error());
3459 }
3460
3461 let case_proofs = match extract_case_proofs(&norm_cases) {
3463 Some(proofs) => proofs,
3464 None => return Some(make_sname_error()),
3465 };
3466
3467 if case_proofs.len() != constructors.len() {
3469 return Some(make_sname_error());
3470 }
3471
3472 let motive_body = match extract_slam_body(&norm_motive) {
3474 Some(body) => body,
3475 None => return Some(make_sname_error()),
3476 };
3477
3478 for (i, (ctor_name, _ctor_type)) in constructors.iter().enumerate() {
3480 let case_proof = &case_proofs[i];
3481
3482 let case_conc = match try_concludes_reduce(ctx, case_proof) {
3484 Some(c) => c,
3485 None => return Some(make_sname_error()),
3486 };
3487
3488 let expected = match build_case_expected(ctx, ctor_name, &constructors, &motive_body, &norm_ind_type) {
3492 Some(e) => e,
3493 None => return Some(make_sname_error()),
3494 };
3495
3496 if !syntax_equal(&case_conc, &expected) {
3498 return Some(make_sname_error());
3499 }
3500 }
3501
3502 Some(make_forall_syntax_generic(&norm_ind_type, &norm_motive))
3504}
3505
3506fn extract_inductive_name_from_syntax(term: &Term) -> Option<String> {
3512 if let Term::App(sname, text) = term {
3514 if let Term::Global(ctor) = sname.as_ref() {
3515 if ctor == "SName" {
3516 if let Term::Lit(Literal::Text(name)) = text.as_ref() {
3517 return Some(name.clone());
3518 }
3519 }
3520 }
3521 }
3522
3523 if let Term::App(inner, _arg) = term {
3525 if let Term::App(sapp, func) = inner.as_ref() {
3526 if let Term::Global(ctor) = sapp.as_ref() {
3527 if ctor == "SApp" {
3528 return extract_inductive_name_from_syntax(func);
3530 }
3531 }
3532 }
3533 }
3534
3535 None
3536}
3537
3538fn extract_case_proofs(term: &Term) -> Option<Vec<Term>> {
3542 let mut proofs = Vec::new();
3543 let mut current = term;
3544
3545 loop {
3546 if let Term::Global(name) = current {
3548 if name == "DCaseEnd" {
3549 return Some(proofs);
3550 }
3551 }
3552
3553 if let Term::App(inner, tail) = current {
3555 if let Term::App(dcase, head) = inner.as_ref() {
3556 if let Term::Global(name) = dcase.as_ref() {
3557 if name == "DCase" {
3558 proofs.push(head.as_ref().clone());
3559 current = tail.as_ref();
3560 continue;
3561 }
3562 }
3563 }
3564 }
3565
3566 return None;
3568 }
3569}
3570
3571fn build_case_expected(
3576 ctx: &Context,
3577 ctor_name: &str,
3578 _constructors: &[(&str, &Term)],
3579 motive_body: &Term,
3580 ind_type: &Term,
3581) -> Option<Term> {
3582 let ind_name = extract_inductive_name_from_syntax(ind_type)?;
3584
3585 if ind_name == "Nat" {
3587 if ctor_name == "Zero" {
3588 let zero = make_sname("Zero");
3590 return try_syn_subst_reduce(ctx, &zero, 0, motive_body);
3591 } else if ctor_name == "Succ" {
3592 return build_induction_step_formula(ctx, motive_body);
3595 }
3596 }
3597
3598 let ctor_syntax = make_sname(ctor_name);
3601
3602 let ctor_applied = apply_type_args_to_ctor(&ctor_syntax, ind_type);
3605
3606 if let Some(ctor_ty) = ctx.get_global(ctor_name) {
3608 if is_recursive_constructor(ctx, ctor_ty, &ind_name, ind_type) {
3610 return build_recursive_case_formula(ctx, ctor_name, ctor_ty, motive_body, ind_type, &ind_name);
3612 }
3613 }
3614
3615 try_syn_subst_reduce(ctx, &ctor_applied, 0, motive_body)
3617}
3618
3619fn apply_type_args_to_ctor(ctor: &Term, ind_type: &Term) -> Term {
3624 let args = extract_type_args(ind_type);
3626
3627 if args.is_empty() {
3628 return ctor.clone();
3629 }
3630
3631 args.iter().fold(ctor.clone(), |acc, arg| {
3633 Term::App(
3634 Box::new(Term::App(
3635 Box::new(Term::Global("SApp".to_string())),
3636 Box::new(acc),
3637 )),
3638 Box::new(arg.clone()),
3639 )
3640 })
3641}
3642
3643fn extract_type_args(term: &Term) -> Vec<Term> {
3649 let mut args = Vec::new();
3650 let mut current = term;
3651
3652 loop {
3654 if let Term::App(inner, arg) = current {
3655 if let Term::App(sapp, func) = inner.as_ref() {
3656 if let Term::Global(ctor) = sapp.as_ref() {
3657 if ctor == "SApp" {
3658 args.push(arg.as_ref().clone());
3659 current = func.as_ref();
3660 continue;
3661 }
3662 }
3663 }
3664 }
3665 break;
3666 }
3667
3668 args.reverse();
3670 args
3671}
3672
3673fn make_forall_syntax_generic(ind_type: &Term, motive: &Term) -> Term {
3677 let forall_type = Term::App(
3679 Box::new(Term::App(
3680 Box::new(Term::Global("SApp".to_string())),
3681 Box::new(make_sname("Forall")),
3682 )),
3683 Box::new(ind_type.clone()),
3684 );
3685
3686 Term::App(
3688 Box::new(Term::App(
3689 Box::new(Term::Global("SApp".to_string())),
3690 Box::new(forall_type),
3691 )),
3692 Box::new(motive.clone()),
3693 )
3694}
3695
3696fn is_recursive_constructor(
3698 _ctx: &Context,
3699 ctor_ty: &Term,
3700 ind_name: &str,
3701 _ind_type: &Term,
3702) -> bool {
3703 fn contains_inductive(term: &Term, ind_name: &str) -> bool {
3708 match term {
3709 Term::Global(name) => name == ind_name,
3710 Term::App(f, a) => {
3711 contains_inductive(f, ind_name) || contains_inductive(a, ind_name)
3712 }
3713 Term::Pi { param_type, body_type, .. } => {
3714 contains_inductive(param_type, ind_name) || contains_inductive(body_type, ind_name)
3715 }
3716 Term::Lambda { param_type, body, .. } => {
3717 contains_inductive(param_type, ind_name) || contains_inductive(body, ind_name)
3718 }
3719 _ => false,
3720 }
3721 }
3722
3723 fn check_params(term: &Term, ind_name: &str) -> bool {
3725 match term {
3726 Term::Pi { param_type, body_type, .. } => {
3727 if contains_inductive(param_type, ind_name) {
3729 return true;
3730 }
3731 check_params(body_type, ind_name)
3733 }
3734 _ => false,
3735 }
3736 }
3737
3738 check_params(ctor_ty, ind_name)
3739}
3740
3741fn build_recursive_case_formula(
3747 ctx: &Context,
3748 ctor_name: &str,
3749 ctor_ty: &Term,
3750 motive_body: &Term,
3751 ind_type: &Term,
3752 ind_name: &str,
3753) -> Option<Term> {
3754 let type_args = extract_type_args(ind_type);
3756
3757 let args = collect_ctor_args(ctor_ty, ind_name, &type_args);
3759
3760 if args.is_empty() {
3761 let ctor_applied = apply_type_args_to_ctor(&make_sname(ctor_name), ind_type);
3763 return try_syn_subst_reduce(ctx, &ctor_applied, 0, motive_body);
3764 }
3765
3766 let mut ctor_app = apply_type_args_to_ctor(&make_sname(ctor_name), ind_type);
3774 for (i, _) in args.iter().enumerate() {
3775 let idx = (args.len() - 1 - i) as i64;
3777 let var = Term::App(
3778 Box::new(Term::Global("SVar".to_string())),
3779 Box::new(Term::Lit(Literal::Int(idx))),
3780 );
3781 ctor_app = Term::App(
3782 Box::new(Term::App(
3783 Box::new(Term::Global("SApp".to_string())),
3784 Box::new(ctor_app),
3785 )),
3786 Box::new(var),
3787 );
3788 }
3789
3790 let p_ctor = try_syn_subst_reduce(ctx, &ctor_app, 0, motive_body)?;
3792
3793 let mut body = p_ctor;
3795 for (i, (arg_ty, is_recursive)) in args.iter().enumerate().rev() {
3796 if *is_recursive {
3797 let idx = (args.len() - 1 - i) as i64;
3800 let var = Term::App(
3801 Box::new(Term::Global("SVar".to_string())),
3802 Box::new(Term::Lit(Literal::Int(idx))),
3803 );
3804 let p_arg = try_syn_subst_reduce(ctx, &var, 0, motive_body)?;
3805 body = make_implies_syntax(&p_arg, &body);
3806 }
3807 let _ = (i, arg_ty); }
3810
3811 for (arg_ty, _) in args.iter().rev() {
3813 let slam = Term::App(
3815 Box::new(Term::App(
3816 Box::new(Term::Global("SLam".to_string())),
3817 Box::new(arg_ty.clone()),
3818 )),
3819 Box::new(body.clone()),
3820 );
3821 body = make_forall_syntax_with_type(arg_ty, &slam);
3823 }
3824
3825 Some(body)
3826}
3827
3828fn collect_ctor_args(ctor_ty: &Term, ind_name: &str, type_args: &[Term]) -> Vec<(Term, bool)> {
3831 let mut args = Vec::new();
3832 let mut current = ctor_ty;
3833 let mut skip_count = type_args.len();
3834
3835 loop {
3836 match current {
3837 Term::Pi { param_type, body_type, .. } => {
3838 if skip_count > 0 {
3839 skip_count -= 1;
3841 } else {
3842 let is_recursive = contains_inductive_term(param_type, ind_name);
3844 let arg_ty_syntax = kernel_type_to_syntax(param_type);
3846 args.push((arg_ty_syntax, is_recursive));
3847 }
3848 current = body_type;
3849 }
3850 _ => break,
3851 }
3852 }
3853
3854 args
3855}
3856
3857fn contains_inductive_term(term: &Term, ind_name: &str) -> bool {
3859 match term {
3860 Term::Global(name) => name == ind_name,
3861 Term::App(f, a) => {
3862 contains_inductive_term(f, ind_name) || contains_inductive_term(a, ind_name)
3863 }
3864 Term::Pi { param_type, body_type, .. } => {
3865 contains_inductive_term(param_type, ind_name) || contains_inductive_term(body_type, ind_name)
3866 }
3867 Term::Lambda { param_type, body, .. } => {
3868 contains_inductive_term(param_type, ind_name) || contains_inductive_term(body, ind_name)
3869 }
3870 _ => false,
3871 }
3872}
3873
3874fn kernel_type_to_syntax(term: &Term) -> Term {
3876 match term {
3877 Term::Global(name) => make_sname(name),
3878 Term::Var(name) => make_sname(name), Term::App(f, a) => {
3880 let f_syn = kernel_type_to_syntax(f);
3881 let a_syn = kernel_type_to_syntax(a);
3882 Term::App(
3884 Box::new(Term::App(
3885 Box::new(Term::Global("SApp".to_string())),
3886 Box::new(f_syn),
3887 )),
3888 Box::new(a_syn),
3889 )
3890 }
3891 Term::Pi { param, param_type, body_type } => {
3892 let pt_syn = kernel_type_to_syntax(param_type);
3893 let bt_syn = kernel_type_to_syntax(body_type);
3894 Term::App(
3896 Box::new(Term::App(
3897 Box::new(Term::Global("SPi".to_string())),
3898 Box::new(pt_syn),
3899 )),
3900 Box::new(bt_syn),
3901 )
3902 }
3903 Term::Sort(univ) => {
3904 Term::App(
3906 Box::new(Term::Global("SSort".to_string())),
3907 Box::new(univ_to_syntax(univ)),
3908 )
3909 }
3910 Term::Lit(lit) => {
3911 Term::App(
3913 Box::new(Term::Global("SLit".to_string())),
3914 Box::new(Term::Lit(lit.clone())),
3915 )
3916 }
3917 _ => {
3918 make_sname("Unknown")
3920 }
3921 }
3922}
3923
3924fn univ_to_syntax(univ: &crate::term::Universe) -> Term {
3926 match univ {
3927 crate::term::Universe::Prop => Term::Global("UProp".to_string()),
3928 crate::term::Universe::Type(n) => Term::App(
3929 Box::new(Term::Global("UType".to_string())),
3930 Box::new(Term::Lit(Literal::Int(*n as i64))),
3931 ),
3932 }
3933}
3934
3935fn try_try_inversion_reduce(ctx: &Context, goal: &Term) -> Option<Term> {
3944 let (ind_name, hyp_args) = match extract_applied_inductive_from_syntax(goal) {
3946 Some((name, args)) => (name, args),
3947 None => return Some(make_error_derivation()),
3948 };
3949
3950 if !ctx.is_inductive(&ind_name) {
3952 return Some(make_error_derivation());
3954 }
3955
3956 let constructors = ctx.get_constructors(&ind_name);
3958
3959 let mut any_possible = false;
3961 for (_ctor_name, ctor_type) in constructors.iter() {
3962 if can_constructor_match_args(ctx, ctor_type, &hyp_args, &ind_name) {
3963 any_possible = true;
3964 break;
3965 }
3966 }
3967
3968 if any_possible {
3969 return Some(make_error_derivation());
3971 }
3972
3973 Some(Term::App(
3975 Box::new(Term::Global("DInversion".to_string())),
3976 Box::new(goal.clone()),
3977 ))
3978}
3979
3980fn try_dinversion_conclude(ctx: &Context, hyp_type: &Term) -> Option<Term> {
3982 let norm_hyp = normalize(ctx, hyp_type);
3983
3984 let (ind_name, hyp_args) = match extract_applied_inductive_from_syntax(&norm_hyp) {
3985 Some((name, args)) => (name, args),
3986 None => return Some(make_sname_error()),
3987 };
3988
3989 if !ctx.is_inductive(&ind_name) {
3991 return Some(make_sname_error());
3992 }
3993
3994 let constructors = ctx.get_constructors(&ind_name);
3995
3996 for (_ctor_name, ctor_type) in constructors.iter() {
3998 if can_constructor_match_args(ctx, ctor_type, &hyp_args, &ind_name) {
3999 return Some(make_sname_error());
4000 }
4001 }
4002
4003 Some(make_sname("False"))
4005}
4006
4007fn extract_applied_inductive_from_syntax(term: &Term) -> Option<(String, Vec<Term>)> {
4012 if let Term::App(ctor, text) = term {
4014 if let Term::Global(ctor_name) = ctor.as_ref() {
4015 if ctor_name == "SName" {
4016 if let Term::Lit(Literal::Text(name)) = text.as_ref() {
4017 return Some((name.clone(), vec![]));
4018 }
4019 }
4020 }
4021 }
4022
4023 if let Term::App(inner, arg) = term {
4025 if let Term::App(sapp_ctor, func) = inner.as_ref() {
4026 if let Term::Global(ctor_name) = sapp_ctor.as_ref() {
4027 if ctor_name == "SApp" {
4028 let (name, mut args) = extract_applied_inductive_from_syntax(func)?;
4030 args.push(arg.as_ref().clone());
4031 return Some((name, args));
4032 }
4033 }
4034 }
4035 }
4036
4037 None
4038}
4039
4040fn can_constructor_match_args(
4049 ctx: &Context,
4050 ctor_type: &Term,
4051 hyp_args: &[Term],
4052 ind_name: &str,
4053) -> bool {
4054 let (result, pattern_vars) = decompose_ctor_type_with_vars(ctor_type);
4056
4057 let result_args = match extract_applied_inductive_from_syntax(&kernel_type_to_syntax(&result)) {
4059 Some((name, args)) if name == *ind_name => args,
4060 _ => return false,
4061 };
4062
4063 if result_args.len() != hyp_args.len() {
4065 return false;
4066 }
4067
4068 let mut bindings: std::collections::HashMap<String, Term> = std::collections::HashMap::new();
4070
4071 for (pattern, concrete) in result_args.iter().zip(hyp_args.iter()) {
4072 if !can_unify_syntax_terms_with_bindings(ctx, pattern, concrete, &pattern_vars, &mut bindings) {
4073 return false;
4074 }
4075 }
4076
4077 true
4081}
4082
4083fn decompose_ctor_type_with_vars(ty: &Term) -> (Term, Vec<String>) {
4089 let mut vars = Vec::new();
4090 let mut current = ty;
4091 loop {
4092 match current {
4093 Term::Pi { param, body_type, .. } => {
4094 vars.push(param.clone());
4095 current = body_type;
4096 }
4097 _ => break,
4098 }
4099 }
4100 (current.clone(), vars)
4101}
4102
4103fn can_unify_syntax_terms_with_bindings(
4110 ctx: &Context,
4111 pattern: &Term,
4112 concrete: &Term,
4113 pattern_vars: &[String],
4114 bindings: &mut std::collections::HashMap<String, Term>,
4115) -> bool {
4116 if let Term::App(ctor, _idx) = pattern {
4118 if let Term::Global(name) = ctor.as_ref() {
4119 if name == "SVar" {
4120 return true;
4121 }
4122 }
4123 }
4124
4125 if let Term::App(ctor1, text1) = pattern {
4127 if let Term::Global(n1) = ctor1.as_ref() {
4128 if n1 == "SName" {
4129 if let Term::Lit(Literal::Text(var_name)) = text1.as_ref() {
4130 if pattern_vars.contains(var_name) {
4132 if let Some(existing) = bindings.get(var_name) {
4134 return syntax_terms_equal(existing, concrete);
4136 } else {
4137 bindings.insert(var_name.clone(), concrete.clone());
4139 return true;
4140 }
4141 }
4142 }
4143 if let Term::App(ctor2, text2) = concrete {
4145 if let Term::Global(n2) = ctor2.as_ref() {
4146 if n2 == "SName" {
4147 return text1 == text2;
4148 }
4149 }
4150 }
4151 return false;
4152 }
4153 }
4154 }
4155
4156 if let (Term::App(inner1, arg1), Term::App(inner2, arg2)) = (pattern, concrete) {
4158 if let (Term::App(sapp1, func1), Term::App(sapp2, func2)) =
4159 (inner1.as_ref(), inner2.as_ref())
4160 {
4161 if let (Term::Global(n1), Term::Global(n2)) = (sapp1.as_ref(), sapp2.as_ref()) {
4162 if n1 == "SApp" && n2 == "SApp" {
4163 return can_unify_syntax_terms_with_bindings(ctx, func1, func2, pattern_vars, bindings)
4164 && can_unify_syntax_terms_with_bindings(ctx, arg1.as_ref(), arg2.as_ref(), pattern_vars, bindings);
4165 }
4166 }
4167 }
4168 }
4169
4170 if let (Term::App(ctor1, lit1), Term::App(ctor2, lit2)) = (pattern, concrete) {
4172 if let (Term::Global(n1), Term::Global(n2)) = (ctor1.as_ref(), ctor2.as_ref()) {
4173 if n1 == "SLit" && n2 == "SLit" {
4174 return lit1 == lit2;
4175 }
4176 }
4177 }
4178
4179 pattern == concrete
4181}
4182
4183fn syntax_terms_equal(a: &Term, b: &Term) -> bool {
4185 match (a, b) {
4186 (Term::App(f1, x1), Term::App(f2, x2)) => {
4187 syntax_terms_equal(f1, f2) && syntax_terms_equal(x1, x2)
4188 }
4189 (Term::Global(n1), Term::Global(n2)) => n1 == n2,
4190 (Term::Lit(l1), Term::Lit(l2)) => l1 == l2,
4191 _ => a == b,
4192 }
4193}
4194
4195fn extract_eq_components_from_syntax(term: &Term) -> Option<(Term, Term, Term)> {
4203 let (eq_a_x, y) = extract_sapp(term)?;
4208
4209 let (eq_a, x) = extract_sapp(&eq_a_x)?;
4211
4212 let (eq, a) = extract_sapp(&eq_a)?;
4214
4215 let eq_name = extract_sname(&eq)?;
4217 if eq_name != "Eq" {
4218 return None;
4219 }
4220
4221 Some((a, x, y))
4222}
4223
4224fn extract_sapp(term: &Term) -> Option<(Term, Term)> {
4226 if let Term::App(inner, x) = term {
4228 if let Term::App(sapp_ctor, f) = inner.as_ref() {
4229 if let Term::Global(ctor_name) = sapp_ctor.as_ref() {
4230 if ctor_name == "SApp" {
4231 return Some((f.as_ref().clone(), x.as_ref().clone()));
4232 }
4233 }
4234 }
4235 }
4236 None
4237}
4238
4239fn extract_sname(term: &Term) -> Option<String> {
4241 if let Term::App(ctor, text) = term {
4242 if let Term::Global(ctor_name) = ctor.as_ref() {
4243 if ctor_name == "SName" {
4244 if let Term::Lit(Literal::Text(name)) = text.as_ref() {
4245 return Some(name.clone());
4246 }
4247 }
4248 }
4249 }
4250 None
4251}
4252
4253fn contains_subterm_syntax(term: &Term, target: &Term) -> bool {
4255 if syntax_equal(term, target) {
4256 return true;
4257 }
4258
4259 if let Some((f, x)) = extract_sapp(term) {
4261 if contains_subterm_syntax(&f, target) || contains_subterm_syntax(&x, target) {
4262 return true;
4263 }
4264 }
4265
4266 if let Some((t, body)) = extract_slam(term) {
4268 if contains_subterm_syntax(&t, target) || contains_subterm_syntax(&body, target) {
4269 return true;
4270 }
4271 }
4272
4273 if let Some((t, body)) = extract_spi(term) {
4275 if contains_subterm_syntax(&t, target) || contains_subterm_syntax(&body, target) {
4276 return true;
4277 }
4278 }
4279
4280 false
4281}
4282
4283fn extract_slam(term: &Term) -> Option<(Term, Term)> {
4285 if let Term::App(inner, body) = term {
4286 if let Term::App(slam_ctor, t) = inner.as_ref() {
4287 if let Term::Global(ctor_name) = slam_ctor.as_ref() {
4288 if ctor_name == "SLam" {
4289 return Some((t.as_ref().clone(), body.as_ref().clone()));
4290 }
4291 }
4292 }
4293 }
4294 None
4295}
4296
4297fn extract_spi(term: &Term) -> Option<(Term, Term)> {
4299 if let Term::App(inner, body) = term {
4300 if let Term::App(spi_ctor, t) = inner.as_ref() {
4301 if let Term::Global(ctor_name) = spi_ctor.as_ref() {
4302 if ctor_name == "SPi" {
4303 return Some((t.as_ref().clone(), body.as_ref().clone()));
4304 }
4305 }
4306 }
4307 }
4308 None
4309}
4310
4311fn replace_first_subterm_syntax(term: &Term, target: &Term, replacement: &Term) -> Option<Term> {
4313 if syntax_equal(term, target) {
4315 return Some(replacement.clone());
4316 }
4317
4318 if let Some((f, x)) = extract_sapp(term) {
4320 if let Some(new_f) = replace_first_subterm_syntax(&f, target, replacement) {
4322 return Some(make_sapp(new_f, x));
4323 }
4324 if let Some(new_x) = replace_first_subterm_syntax(&x, target, replacement) {
4326 return Some(make_sapp(f, new_x));
4327 }
4328 }
4329
4330 if let Some((t, body)) = extract_slam(term) {
4332 if let Some(new_t) = replace_first_subterm_syntax(&t, target, replacement) {
4333 return Some(make_slam(new_t, body));
4334 }
4335 if let Some(new_body) = replace_first_subterm_syntax(&body, target, replacement) {
4336 return Some(make_slam(t, new_body));
4337 }
4338 }
4339
4340 if let Some((t, body)) = extract_spi(term) {
4342 if let Some(new_t) = replace_first_subterm_syntax(&t, target, replacement) {
4343 return Some(make_spi(new_t, body));
4344 }
4345 if let Some(new_body) = replace_first_subterm_syntax(&body, target, replacement) {
4346 return Some(make_spi(t, new_body));
4347 }
4348 }
4349
4350 None
4352}
4353
4354fn try_try_rewrite_reduce(
4357 ctx: &Context,
4358 eq_proof: &Term,
4359 goal: &Term,
4360 reverse: bool,
4361) -> Option<Term> {
4362 let eq_conclusion = try_concludes_reduce(ctx, eq_proof)?;
4364
4365 let (ty, lhs, rhs) = match extract_eq_components_from_syntax(&eq_conclusion) {
4367 Some(components) => components,
4368 None => return Some(make_error_derivation()),
4369 };
4370
4371 let (target, replacement) = if reverse { (rhs, lhs) } else { (lhs, rhs) };
4373
4374 if !contains_subterm_syntax(goal, &target) {
4376 return Some(make_error_derivation());
4377 }
4378
4379 let new_goal = match replace_first_subterm_syntax(goal, &target, &replacement) {
4381 Some(ng) => ng,
4382 None => return Some(make_error_derivation()),
4383 };
4384
4385 Some(Term::App(
4387 Box::new(Term::App(
4388 Box::new(Term::App(
4389 Box::new(Term::Global("DRewrite".to_string())),
4390 Box::new(eq_proof.clone()),
4391 )),
4392 Box::new(goal.clone()),
4393 )),
4394 Box::new(new_goal),
4395 ))
4396}
4397
4398fn try_drewrite_conclude(
4400 ctx: &Context,
4401 eq_proof: &Term,
4402 old_goal: &Term,
4403 new_goal: &Term,
4404) -> Option<Term> {
4405 let eq_conclusion = try_concludes_reduce(ctx, eq_proof)?;
4407
4408 let (_ty, lhs, rhs) = match extract_eq_components_from_syntax(&eq_conclusion) {
4410 Some(components) => components,
4411 None => return Some(make_sname_error()),
4412 };
4413
4414 if let Some(computed_new) = replace_first_subterm_syntax(old_goal, &lhs, &rhs) {
4417 if syntax_equal(&computed_new, new_goal) {
4418 return Some(new_goal.clone());
4419 }
4420 }
4421
4422 if let Some(computed_new) = replace_first_subterm_syntax(old_goal, &rhs, &lhs) {
4424 if syntax_equal(&computed_new, new_goal) {
4425 return Some(new_goal.clone());
4426 }
4427 }
4428
4429 Some(make_sname_error())
4431}
4432
4433fn try_try_destruct_reduce(
4435 ctx: &Context,
4436 ind_type: &Term,
4437 motive: &Term,
4438 cases: &Term,
4439) -> Option<Term> {
4440 Some(Term::App(
4447 Box::new(Term::App(
4448 Box::new(Term::App(
4449 Box::new(Term::Global("DDestruct".to_string())),
4450 Box::new(ind_type.clone()),
4451 )),
4452 Box::new(motive.clone()),
4453 )),
4454 Box::new(cases.clone()),
4455 ))
4456}
4457
4458fn try_ddestruct_conclude(
4460 ctx: &Context,
4461 ind_type: &Term,
4462 motive: &Term,
4463 cases: &Term,
4464) -> Option<Term> {
4465 let ind_name = extract_inductive_name_from_syntax(ind_type)?;
4470
4471 if !ctx.is_inductive(&ind_name) {
4473 return Some(make_sname_error());
4474 }
4475
4476 let constructors = ctx.get_constructors(&ind_name);
4477
4478 let case_proofs = match extract_case_proofs(cases) {
4480 Some(proofs) => proofs,
4481 None => return Some(make_sname_error()),
4482 };
4483
4484 if case_proofs.len() != constructors.len() {
4486 return Some(make_sname_error());
4487 }
4488
4489 Some(make_forall_syntax_with_type(ind_type, motive))
4495}
4496
4497fn try_try_apply_reduce(
4499 ctx: &Context,
4500 hyp_name: &Term,
4501 hyp_proof: &Term,
4502 goal: &Term,
4503) -> Option<Term> {
4504 let hyp_conclusion = try_concludes_reduce(ctx, hyp_proof)?;
4506
4507 if let Some((antecedent, consequent)) = extract_spi(&hyp_conclusion) {
4509 if syntax_equal(&consequent, goal) {
4511 return Some(Term::App(
4513 Box::new(Term::App(
4514 Box::new(Term::App(
4515 Box::new(Term::App(
4516 Box::new(Term::Global("DApply".to_string())),
4517 Box::new(hyp_name.clone()),
4518 )),
4519 Box::new(hyp_proof.clone()),
4520 )),
4521 Box::new(goal.clone()),
4522 )),
4523 Box::new(antecedent),
4524 ));
4525 }
4526 }
4527
4528 if let Some(forall_body) = extract_forall_body(&hyp_conclusion) {
4530 return Some(Term::App(
4536 Box::new(Term::App(
4537 Box::new(Term::App(
4538 Box::new(Term::App(
4539 Box::new(Term::Global("DApply".to_string())),
4540 Box::new(hyp_name.clone()),
4541 )),
4542 Box::new(hyp_proof.clone()),
4543 )),
4544 Box::new(goal.clone()),
4545 )),
4546 Box::new(make_sname("True")),
4547 ));
4548 }
4549
4550 if syntax_equal(&hyp_conclusion, goal) {
4552 return Some(Term::App(
4553 Box::new(Term::App(
4554 Box::new(Term::App(
4555 Box::new(Term::App(
4556 Box::new(Term::Global("DApply".to_string())),
4557 Box::new(hyp_name.clone()),
4558 )),
4559 Box::new(hyp_proof.clone()),
4560 )),
4561 Box::new(goal.clone()),
4562 )),
4563 Box::new(make_sname("True")),
4564 ));
4565 }
4566
4567 Some(make_error_derivation())
4569}
4570
4571fn try_dapply_conclude(
4573 ctx: &Context,
4574 hyp_name: &Term,
4575 hyp_proof: &Term,
4576 old_goal: &Term,
4577 new_goal: &Term,
4578) -> Option<Term> {
4579 let hyp_conclusion = try_concludes_reduce(ctx, hyp_proof)?;
4581
4582 if let Some((antecedent, consequent)) = extract_spi(&hyp_conclusion) {
4585 if syntax_equal(&consequent, old_goal) {
4586 if syntax_equal(&antecedent, new_goal) || extract_sname(new_goal) == Some("True".to_string()) {
4587 return Some(new_goal.clone());
4588 }
4589 }
4590 }
4591
4592 if let Some(_forall_body) = extract_forall_body(&hyp_conclusion) {
4594 if extract_sname(new_goal) == Some("True".to_string()) {
4596 return Some(new_goal.clone());
4597 }
4598 }
4599
4600 if syntax_equal(&hyp_conclusion, old_goal) {
4602 if extract_sname(new_goal) == Some("True".to_string()) {
4603 return Some(new_goal.clone());
4604 }
4605 }
4606
4607 Some(make_sname_error())
4609}
4610