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(_))
1776 | Term::Lit(Literal::Duration(_))
1777 | Term::Lit(Literal::Date(_))
1778 | Term::Lit(Literal::Moment(_)) => None,
1779
1780 Term::Match { .. } | Term::Fix { .. } | Term::Hole => None,
1782 }
1783}
1784
1785fn make_hint_derivation(hint_name: &str, goal: &Term) -> Term {
1789 let hint_marker = make_sapp(make_sname("Hint"), make_sname(hint_name));
1792
1793 Term::App(
1796 Box::new(Term::Global("DAutoSolve".to_string())),
1797 Box::new(goal.clone()),
1798 )
1799}
1800
1801fn try_apply_hint(ctx: &Context, hint_name: &str, hint_type: &Term, goal: &Term) -> Option<Term> {
1806 let hint_syntax = term_to_syntax(hint_type)?;
1808
1809 let norm_hint = normalize(ctx, &hint_syntax);
1811 let norm_goal = normalize(ctx, goal);
1812
1813 if syntax_equal(&norm_hint, &norm_goal) {
1815 return Some(make_hint_derivation(hint_name, goal));
1816 }
1817
1818 if let Term::App(outer, q) = &hint_syntax {
1821 if let Term::App(pi_ctor, p) = outer.as_ref() {
1822 if let Term::Global(name) = pi_ctor.as_ref() {
1823 if name == "SPi" {
1824 let norm_q = normalize(ctx, q);
1826 if syntax_equal(&norm_q, &norm_goal) {
1827 }
1831 }
1832 }
1833 }
1834 }
1835
1836 None
1837}
1838
1839fn make_forall_syntax(body: &Term) -> Term {
1841 let type0 = Term::App(
1842 Box::new(Term::Global("SSort".to_string())),
1843 Box::new(Term::App(
1844 Box::new(Term::Global("UType".to_string())),
1845 Box::new(Term::Lit(Literal::Int(0))),
1846 )),
1847 );
1848
1849 let slam = Term::App(
1851 Box::new(Term::App(
1852 Box::new(Term::Global("SLam".to_string())),
1853 Box::new(type0.clone()),
1854 )),
1855 Box::new(body.clone()),
1856 );
1857
1858 Term::App(
1860 Box::new(Term::App(
1861 Box::new(Term::Global("SApp".to_string())),
1862 Box::new(Term::App(
1863 Box::new(Term::App(
1864 Box::new(Term::Global("SApp".to_string())),
1865 Box::new(Term::App(
1866 Box::new(Term::Global("SName".to_string())),
1867 Box::new(Term::Lit(Literal::Text("Forall".to_string()))),
1868 )),
1869 )),
1870 Box::new(type0),
1871 )),
1872 )),
1873 Box::new(slam),
1874 )
1875}
1876
1877fn make_eq_syntax(type_s: &Term, term: &Term) -> Term {
1885 let eq_name = Term::App(
1886 Box::new(Term::Global("SName".to_string())),
1887 Box::new(Term::Lit(Literal::Text("Eq".to_string()))),
1888 );
1889
1890 let app1 = Term::App(
1892 Box::new(Term::App(
1893 Box::new(Term::Global("SApp".to_string())),
1894 Box::new(eq_name),
1895 )),
1896 Box::new(type_s.clone()),
1897 );
1898
1899 let app2 = Term::App(
1901 Box::new(Term::App(
1902 Box::new(Term::Global("SApp".to_string())),
1903 Box::new(app1),
1904 )),
1905 Box::new(term.clone()),
1906 );
1907
1908 Term::App(
1910 Box::new(Term::App(
1911 Box::new(Term::Global("SApp".to_string())),
1912 Box::new(app2),
1913 )),
1914 Box::new(term.clone()),
1915 )
1916}
1917
1918fn extract_eq(term: &Term) -> Option<(Term, Term, Term)> {
1922 if let Term::App(outer, b) = term {
1924 if let Term::App(sapp_outer, x) = outer.as_ref() {
1925 if let Term::Global(ctor) = sapp_outer.as_ref() {
1926 if ctor == "SApp" {
1927 if let Term::App(inner, a) = x.as_ref() {
1929 if let Term::App(sapp_inner, y) = inner.as_ref() {
1930 if let Term::Global(ctor2) = sapp_inner.as_ref() {
1931 if ctor2 == "SApp" {
1932 if let Term::App(inner2, t) = y.as_ref() {
1934 if let Term::App(sapp_inner2, sname_eq) = inner2.as_ref() {
1935 if let Term::Global(ctor3) = sapp_inner2.as_ref() {
1936 if ctor3 == "SApp" {
1937 if let Term::App(sname, text) = sname_eq.as_ref()
1939 {
1940 if let Term::Global(sname_ctor) =
1941 sname.as_ref()
1942 {
1943 if sname_ctor == "SName" {
1944 if let Term::Lit(Literal::Text(s)) =
1945 text.as_ref()
1946 {
1947 if s == "Eq" {
1948 return Some((
1949 t.as_ref().clone(),
1950 a.as_ref().clone(),
1951 b.as_ref().clone(),
1952 ));
1953 }
1954 }
1955 }
1956 }
1957 }
1958 }
1959 }
1960 }
1961 }
1962 }
1963 }
1964 }
1965 }
1966 }
1967 }
1968 }
1969 }
1970 None
1971}
1972
1973fn make_drefl(type_s: &Term, term: &Term) -> Term {
1975 let drefl = Term::Global("DRefl".to_string());
1976 let app1 = Term::App(Box::new(drefl), Box::new(type_s.clone()));
1977 Term::App(Box::new(app1), Box::new(term.clone()))
1978}
1979
1980fn make_error_derivation() -> Term {
1982 let daxiom = Term::Global("DAxiom".to_string());
1983 let error = make_sname_error();
1984 Term::App(Box::new(daxiom), Box::new(error))
1985}
1986
1987fn make_daxiom(goal: &Term) -> Term {
1989 let daxiom = Term::Global("DAxiom".to_string());
1990 Term::App(Box::new(daxiom), Box::new(goal.clone()))
1991}
1992
1993fn try_try_refl_reduce(ctx: &Context, goal: &Term) -> Option<Term> {
1999 let norm_goal = normalize(ctx, goal);
2001
2002 if let Some((type_s, left, right)) = extract_eq(&norm_goal) {
2004 if syntax_equal(&left, &right) {
2006 return Some(make_drefl(&type_s, &left));
2008 }
2009 }
2010
2011 Some(make_error_derivation())
2013}
2014
2015use crate::ring;
2020use crate::lia;
2021use crate::cc;
2022use crate::simp;
2023
2024fn try_try_ring_reduce(ctx: &Context, goal: &Term) -> Option<Term> {
2032 let norm_goal = normalize(ctx, goal);
2034
2035 if let Some((type_s, left, right)) = extract_eq(&norm_goal) {
2037 if !is_ring_type(&type_s) {
2039 return Some(make_error_derivation());
2040 }
2041
2042 let poly_left = match ring::reify(&left) {
2044 Ok(p) => p,
2045 Err(_) => return Some(make_error_derivation()),
2046 };
2047 let poly_right = match ring::reify(&right) {
2048 Ok(p) => p,
2049 Err(_) => return Some(make_error_derivation()),
2050 };
2051
2052 if poly_left.canonical_eq(&poly_right) {
2054 return Some(Term::App(
2056 Box::new(Term::Global("DRingSolve".to_string())),
2057 Box::new(norm_goal),
2058 ));
2059 }
2060 }
2061
2062 Some(make_error_derivation())
2064}
2065
2066fn try_dring_solve_conclude(ctx: &Context, goal: &Term) -> Option<Term> {
2070 let norm_goal = normalize(ctx, goal);
2071
2072 if let Some((type_s, left, right)) = extract_eq(&norm_goal) {
2074 if !is_ring_type(&type_s) {
2076 return Some(make_sname_error());
2077 }
2078
2079 let poly_left = match ring::reify(&left) {
2081 Ok(p) => p,
2082 Err(_) => return Some(make_sname_error()),
2083 };
2084 let poly_right = match ring::reify(&right) {
2085 Ok(p) => p,
2086 Err(_) => return Some(make_sname_error()),
2087 };
2088
2089 if poly_left.canonical_eq(&poly_right) {
2090 return Some(norm_goal);
2091 }
2092 }
2093
2094 Some(make_sname_error())
2095}
2096
2097fn is_ring_type(type_term: &Term) -> bool {
2099 if let Some(name) = extract_sname_from_syntax(type_term) {
2100 return name == "Int" || name == "Nat";
2101 }
2102 false
2103}
2104
2105fn extract_sname_from_syntax(term: &Term) -> Option<String> {
2107 if let Term::App(ctor, arg) = term {
2108 if let Term::Global(name) = ctor.as_ref() {
2109 if name == "SName" {
2110 if let Term::Lit(Literal::Text(s)) = arg.as_ref() {
2111 return Some(s.clone());
2112 }
2113 }
2114 }
2115 }
2116 None
2117}
2118
2119fn try_try_lia_reduce(ctx: &Context, goal: &Term) -> Option<Term> {
2131 let norm_goal = normalize(ctx, goal);
2133
2134 if let Some((rel, lhs_term, rhs_term)) = lia::extract_comparison(&norm_goal) {
2136 let lhs = match lia::reify_linear(&lhs_term) {
2138 Ok(l) => l,
2139 Err(_) => return Some(make_error_derivation()),
2140 };
2141 let rhs = match lia::reify_linear(&rhs_term) {
2142 Ok(r) => r,
2143 Err(_) => return Some(make_error_derivation()),
2144 };
2145
2146 if let Some(negated) = lia::goal_to_negated_constraint(&rel, &lhs, &rhs) {
2148 if lia::fourier_motzkin_unsat(&[negated]) {
2150 return Some(Term::App(
2152 Box::new(Term::Global("DLiaSolve".to_string())),
2153 Box::new(norm_goal),
2154 ));
2155 }
2156 }
2157 }
2158
2159 Some(make_error_derivation())
2161}
2162
2163fn try_dlia_solve_conclude(ctx: &Context, goal: &Term) -> Option<Term> {
2167 let norm_goal = normalize(ctx, goal);
2168
2169 if let Some((rel, lhs_term, rhs_term)) = lia::extract_comparison(&norm_goal) {
2171 let lhs = match lia::reify_linear(&lhs_term) {
2173 Ok(l) => l,
2174 Err(_) => return Some(make_sname_error()),
2175 };
2176 let rhs = match lia::reify_linear(&rhs_term) {
2177 Ok(r) => r,
2178 Err(_) => return Some(make_sname_error()),
2179 };
2180
2181 if let Some(negated) = lia::goal_to_negated_constraint(&rel, &lhs, &rhs) {
2183 if lia::fourier_motzkin_unsat(&[negated]) {
2184 return Some(norm_goal);
2185 }
2186 }
2187 }
2188
2189 Some(make_sname_error())
2190}
2191
2192fn try_try_cc_reduce(ctx: &Context, goal: &Term) -> Option<Term> {
2204 let norm_goal = normalize(ctx, goal);
2206
2207 if cc::check_goal(&norm_goal) {
2211 return Some(Term::App(
2213 Box::new(Term::Global("DccSolve".to_string())),
2214 Box::new(norm_goal),
2215 ));
2216 }
2217
2218 Some(make_error_derivation())
2220}
2221
2222fn try_dcc_solve_conclude(ctx: &Context, goal: &Term) -> Option<Term> {
2226 let norm_goal = normalize(ctx, goal);
2227
2228 if cc::check_goal(&norm_goal) {
2230 return Some(norm_goal);
2231 }
2232
2233 Some(make_sname_error())
2234}
2235
2236fn try_try_simp_reduce(ctx: &Context, goal: &Term) -> Option<Term> {
2246 let norm_goal = normalize(ctx, goal);
2248
2249 if simp::check_goal(&norm_goal) {
2254 return Some(Term::App(
2256 Box::new(Term::Global("DSimpSolve".to_string())),
2257 Box::new(norm_goal),
2258 ));
2259 }
2260
2261 Some(make_error_derivation())
2263}
2264
2265fn try_dsimp_solve_conclude(ctx: &Context, goal: &Term) -> Option<Term> {
2269 let norm_goal = normalize(ctx, goal);
2270
2271 if simp::check_goal(&norm_goal) {
2273 return Some(norm_goal);
2274 }
2275
2276 Some(make_sname_error())
2277}
2278
2279fn try_try_omega_reduce(ctx: &Context, goal: &Term) -> Option<Term> {
2296 let norm_goal = normalize(ctx, goal);
2297
2298 if let Some((hyps, conclusion)) = extract_implications(&norm_goal) {
2300 let mut constraints = Vec::new();
2302
2303 for hyp in &hyps {
2304 if let Some((rel, lhs_term, rhs_term)) = omega::extract_comparison(hyp) {
2306 if let (Some(lhs), Some(rhs)) =
2307 (omega::reify_int_linear(&lhs_term), omega::reify_int_linear(&rhs_term))
2308 {
2309 match rel.as_str() {
2313 "Lt" | "lt" => {
2314 let mut expr = lhs.sub(&rhs);
2316 expr.constant += 1;
2317 constraints.push(omega::IntConstraint { expr, strict: false });
2318 }
2319 "Le" | "le" => {
2320 constraints.push(omega::IntConstraint {
2322 expr: lhs.sub(&rhs),
2323 strict: false,
2324 });
2325 }
2326 "Gt" | "gt" => {
2327 let mut expr = rhs.sub(&lhs);
2329 expr.constant += 1;
2330 constraints.push(omega::IntConstraint { expr, strict: false });
2331 }
2332 "Ge" | "ge" => {
2333 constraints.push(omega::IntConstraint {
2335 expr: rhs.sub(&lhs),
2336 strict: false,
2337 });
2338 }
2339 _ => {}
2340 }
2341 }
2342 }
2343 }
2344
2345 if let Some((rel, lhs_term, rhs_term)) = omega::extract_comparison(&conclusion) {
2347 if let (Some(lhs), Some(rhs)) =
2348 (omega::reify_int_linear(&lhs_term), omega::reify_int_linear(&rhs_term))
2349 {
2350 if let Some(neg_constraint) = omega::goal_to_negated_constraint(&rel, &lhs, &rhs) {
2352 let mut all_constraints = constraints;
2353 all_constraints.push(neg_constraint);
2354
2355 if omega::omega_unsat(&all_constraints) {
2356 return Some(Term::App(
2357 Box::new(Term::Global("DOmegaSolve".to_string())),
2358 Box::new(norm_goal),
2359 ));
2360 }
2361 }
2362 }
2363 }
2364
2365 return Some(make_error_derivation());
2367 }
2368
2369 if let Some((rel, lhs_term, rhs_term)) = omega::extract_comparison(&norm_goal) {
2371 if let (Some(lhs), Some(rhs)) =
2372 (omega::reify_int_linear(&lhs_term), omega::reify_int_linear(&rhs_term))
2373 {
2374 if let Some(constraint) = omega::goal_to_negated_constraint(&rel, &lhs, &rhs) {
2375 if omega::omega_unsat(&[constraint]) {
2376 return Some(Term::App(
2377 Box::new(Term::Global("DOmegaSolve".to_string())),
2378 Box::new(norm_goal),
2379 ));
2380 }
2381 }
2382 }
2383 }
2384
2385 Some(make_error_derivation())
2386}
2387
2388fn try_domega_solve_conclude(ctx: &Context, goal: &Term) -> Option<Term> {
2392 let norm_goal = normalize(ctx, goal);
2393
2394 if let Some((hyps, conclusion)) = extract_implications(&norm_goal) {
2397 let mut constraints = Vec::new();
2398
2399 for hyp in &hyps {
2400 if let Some((rel, lhs_term, rhs_term)) = omega::extract_comparison(hyp) {
2401 if let (Some(lhs), Some(rhs)) =
2402 (omega::reify_int_linear(&lhs_term), omega::reify_int_linear(&rhs_term))
2403 {
2404 match rel.as_str() {
2405 "Lt" | "lt" => {
2406 let mut expr = lhs.sub(&rhs);
2407 expr.constant += 1;
2408 constraints.push(omega::IntConstraint { expr, strict: false });
2409 }
2410 "Le" | "le" => {
2411 constraints.push(omega::IntConstraint {
2412 expr: lhs.sub(&rhs),
2413 strict: false,
2414 });
2415 }
2416 "Gt" | "gt" => {
2417 let mut expr = rhs.sub(&lhs);
2418 expr.constant += 1;
2419 constraints.push(omega::IntConstraint { expr, strict: false });
2420 }
2421 "Ge" | "ge" => {
2422 constraints.push(omega::IntConstraint {
2423 expr: rhs.sub(&lhs),
2424 strict: false,
2425 });
2426 }
2427 _ => {}
2428 }
2429 }
2430 }
2431 }
2432
2433 if let Some((rel, lhs_term, rhs_term)) = omega::extract_comparison(&conclusion) {
2434 if let (Some(lhs), Some(rhs)) =
2435 (omega::reify_int_linear(&lhs_term), omega::reify_int_linear(&rhs_term))
2436 {
2437 if let Some(neg_constraint) = omega::goal_to_negated_constraint(&rel, &lhs, &rhs) {
2438 let mut all_constraints = constraints;
2439 all_constraints.push(neg_constraint);
2440
2441 if omega::omega_unsat(&all_constraints) {
2442 return Some(norm_goal);
2443 }
2444 }
2445 }
2446 }
2447
2448 return Some(make_sname_error());
2449 }
2450
2451 if let Some((rel, lhs_term, rhs_term)) = omega::extract_comparison(&norm_goal) {
2453 if let (Some(lhs), Some(rhs)) =
2454 (omega::reify_int_linear(&lhs_term), omega::reify_int_linear(&rhs_term))
2455 {
2456 if let Some(constraint) = omega::goal_to_negated_constraint(&rel, &lhs, &rhs) {
2457 if omega::omega_unsat(&[constraint]) {
2458 return Some(norm_goal);
2459 }
2460 }
2461 }
2462 }
2463
2464 Some(make_sname_error())
2465}
2466
2467fn is_error_derivation(term: &Term) -> bool {
2475 if let Term::App(ctor, arg) = term {
2477 if let Term::Global(name) = ctor.as_ref() {
2478 if name == "DAxiom" {
2479 if let Term::App(sname, inner) = arg.as_ref() {
2481 if let Term::Global(sn) = sname.as_ref() {
2482 if sn == "SName" {
2483 if let Term::Lit(Literal::Text(s)) = inner.as_ref() {
2484 return s == "Error";
2485 }
2486 }
2487 if sn == "SApp" {
2488 return true; }
2491 }
2492 }
2493 if let Term::Global(sn) = arg.as_ref() {
2495 return sn == "Error";
2497 }
2498 return true; }
2500 }
2501 }
2502 false
2503}
2504
2505fn try_try_auto_reduce(ctx: &Context, goal: &Term) -> Option<Term> {
2510 let norm_goal = normalize(ctx, goal);
2511
2512 if let Term::App(ctor, inner) = &norm_goal {
2515 if let Term::Global(name) = ctor.as_ref() {
2516 if name == "SName" {
2517 if let Term::Lit(Literal::Text(s)) = inner.as_ref() {
2518 if s == "True" {
2519 return Some(Term::App(
2521 Box::new(Term::Global("DAutoSolve".to_string())),
2522 Box::new(norm_goal),
2523 ));
2524 }
2525 if s == "False" {
2526 return Some(make_error_derivation());
2528 }
2529 }
2530 }
2531 }
2532 }
2533
2534 if let Some(result) = try_try_simp_reduce(ctx, &norm_goal) {
2536 if !is_error_derivation(&result) {
2537 return Some(result);
2538 }
2539 }
2540
2541 if let Some(result) = try_try_ring_reduce(ctx, &norm_goal) {
2543 if !is_error_derivation(&result) {
2544 return Some(result);
2545 }
2546 }
2547
2548 if let Some(result) = try_try_cc_reduce(ctx, &norm_goal) {
2550 if !is_error_derivation(&result) {
2551 return Some(result);
2552 }
2553 }
2554
2555 if let Some(result) = try_try_omega_reduce(ctx, &norm_goal) {
2557 if !is_error_derivation(&result) {
2558 return Some(result);
2559 }
2560 }
2561
2562 if let Some(result) = try_try_lia_reduce(ctx, &norm_goal) {
2564 if !is_error_derivation(&result) {
2565 return Some(result);
2566 }
2567 }
2568
2569 for hint_name in ctx.get_hints() {
2571 if let Some(hint_type) = ctx.get_global(hint_name) {
2572 if let Some(result) = try_apply_hint(ctx, hint_name, hint_type, &norm_goal) {
2573 return Some(result);
2574 }
2575 }
2576 }
2577
2578 Some(make_error_derivation())
2580}
2581
2582fn try_dauto_solve_conclude(ctx: &Context, goal: &Term) -> Option<Term> {
2584 let norm_goal = normalize(ctx, goal);
2585
2586 if let Term::App(ctor, inner) = &norm_goal {
2588 if let Term::Global(name) = ctor.as_ref() {
2589 if name == "SName" {
2590 if let Term::Lit(Literal::Text(s)) = inner.as_ref() {
2591 if s == "True" {
2592 return Some(norm_goal.clone());
2593 }
2594 }
2595 }
2596 }
2597 }
2598
2599 if let Some(result) = try_try_simp_reduce(ctx, &norm_goal) {
2601 if !is_error_derivation(&result) {
2602 return Some(norm_goal.clone());
2603 }
2604 }
2605 if let Some(result) = try_try_ring_reduce(ctx, &norm_goal) {
2606 if !is_error_derivation(&result) {
2607 return Some(norm_goal.clone());
2608 }
2609 }
2610 if let Some(result) = try_try_cc_reduce(ctx, &norm_goal) {
2611 if !is_error_derivation(&result) {
2612 return Some(norm_goal.clone());
2613 }
2614 }
2615 if let Some(result) = try_try_omega_reduce(ctx, &norm_goal) {
2616 if !is_error_derivation(&result) {
2617 return Some(norm_goal.clone());
2618 }
2619 }
2620 if let Some(result) = try_try_lia_reduce(ctx, &norm_goal) {
2621 if !is_error_derivation(&result) {
2622 return Some(norm_goal.clone());
2623 }
2624 }
2625
2626 for hint_name in ctx.get_hints() {
2628 if let Some(hint_type) = ctx.get_global(hint_name) {
2629 if try_apply_hint(ctx, hint_name, hint_type, &norm_goal).is_some() {
2630 return Some(norm_goal);
2631 }
2632 }
2633 }
2634
2635 Some(make_sname_error())
2636}
2637
2638fn try_induction_num_cases_reduce(ctx: &Context, ind_type: &Term) -> Option<Term> {
2648 let ind_name = match extract_inductive_name_from_syntax(ind_type) {
2650 Some(name) => name,
2651 None => {
2652 return Some(Term::Global("Zero".to_string()));
2654 }
2655 };
2656
2657 let constructors = ctx.get_constructors(&ind_name);
2659 let num_ctors = constructors.len();
2660
2661 let mut result = Term::Global("Zero".to_string());
2663 for _ in 0..num_ctors {
2664 result = Term::App(
2665 Box::new(Term::Global("Succ".to_string())),
2666 Box::new(result),
2667 );
2668 }
2669
2670 Some(result)
2671}
2672
2673fn try_induction_base_goal_reduce(
2677 ctx: &Context,
2678 ind_type: &Term,
2679 motive: &Term,
2680) -> Option<Term> {
2681 let ind_name = match extract_inductive_name_from_syntax(ind_type) {
2683 Some(name) => name,
2684 None => return Some(make_sname_error()),
2685 };
2686
2687 let constructors = ctx.get_constructors(&ind_name);
2689 if constructors.is_empty() {
2690 return Some(make_sname_error());
2691 }
2692
2693 let motive_body = match extract_slam_body(motive) {
2695 Some(body) => body,
2696 None => return Some(make_sname_error()),
2697 };
2698
2699 let (ctor_name, _) = constructors[0];
2701 build_case_expected(ctx, ctor_name, &constructors, &motive_body, ind_type)
2702}
2703
2704fn try_induction_step_goal_reduce(
2708 ctx: &Context,
2709 ind_type: &Term,
2710 motive: &Term,
2711 ctor_idx: &Term,
2712) -> Option<Term> {
2713 let ind_name = match extract_inductive_name_from_syntax(ind_type) {
2715 Some(name) => name,
2716 None => return Some(make_sname_error()),
2717 };
2718
2719 let constructors = ctx.get_constructors(&ind_name);
2721 if constructors.is_empty() {
2722 return Some(make_sname_error());
2723 }
2724
2725 let idx = nat_to_usize(ctor_idx)?;
2727 if idx >= constructors.len() {
2728 return Some(make_sname_error());
2729 }
2730
2731 let motive_body = match extract_slam_body(motive) {
2733 Some(body) => body,
2734 None => return Some(make_sname_error()),
2735 };
2736
2737 let (ctor_name, _) = constructors[idx];
2739 build_case_expected(ctx, ctor_name, &constructors, &motive_body, ind_type)
2740}
2741
2742fn nat_to_usize(term: &Term) -> Option<usize> {
2748 match term {
2749 Term::Global(name) if name == "Zero" => Some(0),
2750 Term::App(succ, inner) => {
2751 if let Term::Global(name) = succ.as_ref() {
2752 if name == "Succ" {
2753 return nat_to_usize(inner).map(|n| n + 1);
2754 }
2755 }
2756 None
2757 }
2758 _ => None,
2759 }
2760}
2761
2762fn try_try_induction_reduce(
2768 ctx: &Context,
2769 ind_type: &Term,
2770 motive: &Term,
2771 cases: &Term,
2772) -> Option<Term> {
2773 let ind_name = match extract_inductive_name_from_syntax(ind_type) {
2775 Some(name) => name,
2776 None => return Some(make_error_derivation()),
2777 };
2778
2779 let constructors = ctx.get_constructors(&ind_name);
2781 if constructors.is_empty() {
2782 return Some(make_error_derivation());
2783 }
2784
2785 let case_proofs = match extract_case_proofs(cases) {
2787 Some(proofs) => proofs,
2788 None => return Some(make_error_derivation()),
2789 };
2790
2791 if case_proofs.len() != constructors.len() {
2793 return Some(make_error_derivation());
2794 }
2795
2796 Some(Term::App(
2798 Box::new(Term::App(
2799 Box::new(Term::App(
2800 Box::new(Term::Global("DElim".to_string())),
2801 Box::new(ind_type.clone()),
2802 )),
2803 Box::new(motive.clone()),
2804 )),
2805 Box::new(cases.clone()),
2806 ))
2807}
2808
2809fn try_dinduction_reduce(
2824 ctx: &Context,
2825 motive: &Term,
2826 base: &Term,
2827 step: &Term,
2828) -> Option<Term> {
2829 let norm_motive = normalize(ctx, motive);
2831 let norm_base = normalize(ctx, base);
2832 let norm_step = normalize(ctx, step);
2833
2834 let motive_body = match extract_slam_body(&norm_motive) {
2836 Some(body) => body,
2837 None => return Some(make_sname_error()),
2838 };
2839
2840 let zero = make_sname("Zero");
2842 let expected_base = match try_syn_subst_reduce(ctx, &zero, 0, &motive_body) {
2843 Some(b) => b,
2844 None => return Some(make_sname_error()),
2845 };
2846
2847 let base_conc = match try_concludes_reduce(ctx, &norm_base) {
2849 Some(c) => c,
2850 None => return Some(make_sname_error()),
2851 };
2852
2853 if !syntax_equal(&base_conc, &expected_base) {
2855 return Some(make_sname_error());
2856 }
2857
2858 let expected_step = match build_induction_step_formula(ctx, &motive_body) {
2860 Some(s) => s,
2861 None => return Some(make_sname_error()),
2862 };
2863
2864 let step_conc = match try_concludes_reduce(ctx, &norm_step) {
2866 Some(c) => c,
2867 None => return Some(make_sname_error()),
2868 };
2869
2870 if !syntax_equal(&step_conc, &expected_step) {
2872 return Some(make_sname_error());
2873 }
2874
2875 Some(make_forall_nat_syntax(&norm_motive))
2877}
2878
2879fn build_induction_step_formula(ctx: &Context, motive_body: &Term) -> Option<Term> {
2884 let p_k = motive_body.clone();
2886
2887 let succ_var = Term::App(
2889 Box::new(Term::App(
2890 Box::new(Term::Global("SApp".to_string())),
2891 Box::new(make_sname("Succ")),
2892 )),
2893 Box::new(Term::App(
2894 Box::new(Term::Global("SVar".to_string())),
2895 Box::new(Term::Lit(Literal::Int(0))),
2896 )),
2897 );
2898 let p_succ_k = try_syn_subst_reduce(ctx, &succ_var, 0, motive_body)?;
2899
2900 let implies_body = make_implies_syntax(&p_k, &p_succ_k);
2902
2903 let slam = Term::App(
2905 Box::new(Term::App(
2906 Box::new(Term::Global("SLam".to_string())),
2907 Box::new(make_sname("Nat")),
2908 )),
2909 Box::new(implies_body),
2910 );
2911
2912 Some(make_forall_syntax_with_type(&make_sname("Nat"), &slam))
2914}
2915
2916fn make_implies_syntax(a: &Term, b: &Term) -> Term {
2918 let app1 = Term::App(
2920 Box::new(Term::App(
2921 Box::new(Term::Global("SApp".to_string())),
2922 Box::new(make_sname("Implies")),
2923 )),
2924 Box::new(a.clone()),
2925 );
2926
2927 Term::App(
2929 Box::new(Term::App(
2930 Box::new(Term::Global("SApp".to_string())),
2931 Box::new(app1),
2932 )),
2933 Box::new(b.clone()),
2934 )
2935}
2936
2937fn make_forall_nat_syntax(motive: &Term) -> Term {
2939 make_forall_syntax_with_type(&make_sname("Nat"), motive)
2940}
2941
2942fn make_forall_syntax_with_type(type_s: &Term, body: &Term) -> Term {
2944 let app1 = Term::App(
2946 Box::new(Term::App(
2947 Box::new(Term::Global("SApp".to_string())),
2948 Box::new(make_sname("Forall")),
2949 )),
2950 Box::new(type_s.clone()),
2951 );
2952
2953 Term::App(
2955 Box::new(Term::App(
2956 Box::new(Term::Global("SApp".to_string())),
2957 Box::new(app1),
2958 )),
2959 Box::new(body.clone()),
2960 )
2961}
2962
2963fn try_dcompute_conclude(ctx: &Context, goal: &Term) -> Option<Term> {
2977 let norm_goal = normalize(ctx, goal);
2978
2979 let parts = extract_eq_syntax_parts(&norm_goal);
2982 if parts.is_none() {
2983 return Some(make_sname_error());
2985 }
2986 let (_, a, b) = parts.unwrap();
2987
2988 let fuel = 1000;
2990 let a_eval = match try_syn_eval_reduce(ctx, fuel, &a) {
2991 Some(e) => e,
2992 None => return Some(make_sname_error()),
2993 };
2994 let b_eval = match try_syn_eval_reduce(ctx, fuel, &b) {
2995 Some(e) => e,
2996 None => return Some(make_sname_error()),
2997 };
2998
2999 if syntax_equal(&a_eval, &b_eval) {
3001 Some(norm_goal)
3002 } else {
3003 Some(make_sname_error())
3004 }
3005}
3006
3007fn extract_eq_syntax_parts(term: &Term) -> Option<(Term, Term, Term)> {
3011 if let Term::App(partial2, b) = term {
3014 if let Term::App(sapp2, inner2) = partial2.as_ref() {
3015 if let Term::Global(sapp2_name) = sapp2.as_ref() {
3016 if sapp2_name != "SApp" {
3017 return None;
3018 }
3019 } else {
3020 return None;
3021 }
3022
3023 if let Term::App(partial1, a) = inner2.as_ref() {
3024 if let Term::App(sapp1, inner1) = partial1.as_ref() {
3025 if let Term::Global(sapp1_name) = sapp1.as_ref() {
3026 if sapp1_name != "SApp" {
3027 return None;
3028 }
3029 } else {
3030 return None;
3031 }
3032
3033 if let Term::App(eq_t, t) = inner1.as_ref() {
3034 if let Term::App(sapp0, eq_sname) = eq_t.as_ref() {
3035 if let Term::Global(sapp0_name) = sapp0.as_ref() {
3036 if sapp0_name != "SApp" {
3037 return None;
3038 }
3039 } else {
3040 return None;
3041 }
3042
3043 if let Term::App(sname_ctor, eq_str) = eq_sname.as_ref() {
3045 if let Term::Global(ctor) = sname_ctor.as_ref() {
3046 if ctor == "SName" {
3047 if let Term::Lit(Literal::Text(s)) = eq_str.as_ref() {
3048 if s == "Eq" {
3049 return Some((
3050 t.as_ref().clone(),
3051 a.as_ref().clone(),
3052 b.as_ref().clone(),
3053 ));
3054 }
3055 }
3056 }
3057 }
3058 }
3059 }
3060 }
3061 }
3062 }
3063 }
3064 }
3065 None
3066}
3067
3068fn try_tact_orelse_reduce(
3078 ctx: &Context,
3079 t1: &Term,
3080 t2: &Term,
3081 goal: &Term,
3082) -> Option<Term> {
3083 let norm_goal = normalize(ctx, goal);
3084
3085 let d1_app = Term::App(Box::new(t1.clone()), Box::new(norm_goal.clone()));
3087 let d1 = normalize(ctx, &d1_app);
3088
3089 if let Some(conc1) = try_concludes_reduce(ctx, &d1) {
3091 if is_error_syntax(&conc1) {
3092 let d2_app = Term::App(Box::new(t2.clone()), Box::new(norm_goal));
3094 return Some(normalize(ctx, &d2_app));
3095 } else {
3096 return Some(d1);
3098 }
3099 }
3100
3101 Some(make_error_derivation())
3103}
3104
3105fn is_error_syntax(term: &Term) -> bool {
3107 if let Term::App(ctor, arg) = term {
3109 if let Term::Global(name) = ctor.as_ref() {
3110 if name == "SName" {
3111 if let Term::Lit(Literal::Text(s)) = arg.as_ref() {
3112 return s == "Error";
3113 }
3114 }
3115 }
3116 }
3117 false
3118}
3119
3120fn try_tact_try_reduce(ctx: &Context, t: &Term, goal: &Term) -> Option<Term> {
3126 let norm_goal = normalize(ctx, goal);
3127
3128 let d_app = Term::App(Box::new(t.clone()), Box::new(norm_goal.clone()));
3130 let d = normalize(ctx, &d_app);
3131
3132 if let Some(conc) = try_concludes_reduce(ctx, &d) {
3134 if is_error_syntax(&conc) {
3135 return Some(make_daxiom(&norm_goal));
3137 } else {
3138 return Some(d);
3140 }
3141 }
3142
3143 Some(make_daxiom(&norm_goal))
3145}
3146
3147fn try_tact_repeat_reduce(ctx: &Context, t: &Term, goal: &Term) -> Option<Term> {
3152 const MAX_ITERATIONS: usize = 100;
3153
3154 let norm_goal = normalize(ctx, goal);
3155 let mut current_goal = norm_goal.clone();
3156 let mut last_successful_deriv: Option<Term> = None;
3157
3158 for _ in 0..MAX_ITERATIONS {
3159 let d_app = Term::App(Box::new(t.clone()), Box::new(current_goal.clone()));
3161 let d = normalize(ctx, &d_app);
3162
3163 if let Some(conc) = try_concludes_reduce(ctx, &d) {
3165 if is_error_syntax(&conc) {
3166 break;
3168 }
3169
3170 if syntax_equal(&conc, ¤t_goal) {
3172 break;
3174 }
3175
3176 current_goal = conc;
3178 last_successful_deriv = Some(d);
3179 } else {
3180 break;
3182 }
3183 }
3184
3185 last_successful_deriv.or_else(|| Some(make_daxiom(&norm_goal)))
3187}
3188
3189fn try_tact_then_reduce(
3195 ctx: &Context,
3196 t1: &Term,
3197 t2: &Term,
3198 goal: &Term,
3199) -> Option<Term> {
3200 let norm_goal = normalize(ctx, goal);
3201
3202 let d1_app = Term::App(Box::new(t1.clone()), Box::new(norm_goal.clone()));
3204 let d1 = normalize(ctx, &d1_app);
3205
3206 if let Some(conc1) = try_concludes_reduce(ctx, &d1) {
3208 if is_error_syntax(&conc1) {
3209 return Some(make_error_derivation());
3211 }
3212
3213 let d2_app = Term::App(Box::new(t2.clone()), Box::new(conc1));
3215 let d2 = normalize(ctx, &d2_app);
3216
3217 return Some(d2);
3219 }
3220
3221 Some(make_error_derivation())
3223}
3224
3225fn try_tact_first_reduce(ctx: &Context, tactics: &Term, goal: &Term) -> Option<Term> {
3230 let norm_goal = normalize(ctx, goal);
3231
3232 let tactic_vec = extract_tlist(tactics)?;
3234
3235 for tactic in tactic_vec {
3236 let d_app = Term::App(Box::new(tactic), Box::new(norm_goal.clone()));
3238 let d = normalize(ctx, &d_app);
3239
3240 if let Some(conc) = try_concludes_reduce(ctx, &d) {
3242 if !is_error_syntax(&conc) {
3243 return Some(d);
3245 }
3246 }
3248 }
3249
3250 Some(make_error_derivation())
3252}
3253
3254fn extract_tlist(term: &Term) -> Option<Vec<Term>> {
3261 let mut result = Vec::new();
3262 let mut current = term.clone();
3263
3264 loop {
3265 match ¤t {
3266 Term::App(tnil, _type) => {
3268 if let Term::Global(name) = tnil.as_ref() {
3269 if name == "TNil" {
3270 break;
3272 }
3273 }
3274 if let Term::App(partial2, tail) = ¤t {
3276 if let Term::App(partial1, head) = partial2.as_ref() {
3277 if let Term::App(tcons, _type) = partial1.as_ref() {
3278 if let Term::Global(name) = tcons.as_ref() {
3279 if name == "TCons" {
3280 result.push(head.as_ref().clone());
3281 current = tail.as_ref().clone();
3282 continue;
3283 }
3284 }
3285 }
3286 }
3287 }
3288 return None;
3290 }
3291 Term::Global(name) if name == "TNil" => {
3293 break;
3294 }
3295 _ => {
3296 return None;
3298 }
3299 }
3300 }
3301
3302 Some(result)
3303}
3304
3305fn try_tact_solve_reduce(ctx: &Context, t: &Term, goal: &Term) -> Option<Term> {
3311 let norm_goal = normalize(ctx, goal);
3312
3313 let d_app = Term::App(Box::new(t.clone()), Box::new(norm_goal.clone()));
3315 let d = normalize(ctx, &d_app);
3316
3317 if let Some(conc) = try_concludes_reduce(ctx, &d) {
3319 if is_error_syntax(&conc) {
3320 return Some(make_error_derivation());
3322 }
3323 return Some(d);
3325 }
3326
3327 Some(make_error_derivation())
3329}
3330
3331fn try_dcong_conclude(ctx: &Context, context: &Term, eq_proof: &Term) -> Option<Term> {
3342 let eq_conc = try_concludes_reduce(ctx, eq_proof)?;
3344
3345 let parts = extract_eq_syntax_parts(&eq_conc);
3347 if parts.is_none() {
3348 return Some(make_sname_error());
3350 }
3351 let (_type_term, lhs, rhs) = parts.unwrap();
3352
3353 let norm_context = normalize(ctx, context);
3355
3356 let slam_parts = extract_slam_parts(&norm_context);
3358 if slam_parts.is_none() {
3359 return Some(make_sname_error());
3361 }
3362 let (param_type, body) = slam_parts.unwrap();
3363
3364 let fa = try_syn_subst_reduce(ctx, &lhs, 0, &body)?;
3366 let fb = try_syn_subst_reduce(ctx, &rhs, 0, &body)?;
3367
3368 Some(make_eq_syntax_three(¶m_type, &fa, &fb))
3370}
3371
3372fn extract_slam_parts(term: &Term) -> Option<(Term, Term)> {
3376 if let Term::App(inner, body) = term {
3377 if let Term::App(slam_ctor, param_type) = inner.as_ref() {
3378 if let Term::Global(name) = slam_ctor.as_ref() {
3379 if name == "SLam" {
3380 return Some((param_type.as_ref().clone(), body.as_ref().clone()));
3381 }
3382 }
3383 }
3384 }
3385 None
3386}
3387
3388fn make_eq_syntax_three(type_s: &Term, a: &Term, b: &Term) -> Term {
3392 let eq_name = Term::App(
3393 Box::new(Term::Global("SName".to_string())),
3394 Box::new(Term::Lit(Literal::Text("Eq".to_string()))),
3395 );
3396
3397 let app1 = Term::App(
3399 Box::new(Term::App(
3400 Box::new(Term::Global("SApp".to_string())),
3401 Box::new(eq_name),
3402 )),
3403 Box::new(type_s.clone()),
3404 );
3405
3406 let app2 = Term::App(
3408 Box::new(Term::App(
3409 Box::new(Term::Global("SApp".to_string())),
3410 Box::new(app1),
3411 )),
3412 Box::new(a.clone()),
3413 );
3414
3415 Term::App(
3417 Box::new(Term::App(
3418 Box::new(Term::Global("SApp".to_string())),
3419 Box::new(app2),
3420 )),
3421 Box::new(b.clone()),
3422 )
3423}
3424
3425fn try_delim_conclude(
3441 ctx: &Context,
3442 ind_type: &Term,
3443 motive: &Term,
3444 cases: &Term,
3445) -> Option<Term> {
3446 let norm_ind_type = normalize(ctx, ind_type);
3448 let norm_motive = normalize(ctx, motive);
3449 let norm_cases = normalize(ctx, cases);
3450
3451 let ind_name = match extract_inductive_name_from_syntax(&norm_ind_type) {
3453 Some(name) => name,
3454 None => return Some(make_sname_error()),
3455 };
3456
3457 let constructors = ctx.get_constructors(&ind_name);
3459 if constructors.is_empty() {
3460 return Some(make_sname_error());
3462 }
3463
3464 let case_proofs = match extract_case_proofs(&norm_cases) {
3466 Some(proofs) => proofs,
3467 None => return Some(make_sname_error()),
3468 };
3469
3470 if case_proofs.len() != constructors.len() {
3472 return Some(make_sname_error());
3473 }
3474
3475 let motive_body = match extract_slam_body(&norm_motive) {
3477 Some(body) => body,
3478 None => return Some(make_sname_error()),
3479 };
3480
3481 for (i, (ctor_name, _ctor_type)) in constructors.iter().enumerate() {
3483 let case_proof = &case_proofs[i];
3484
3485 let case_conc = match try_concludes_reduce(ctx, case_proof) {
3487 Some(c) => c,
3488 None => return Some(make_sname_error()),
3489 };
3490
3491 let expected = match build_case_expected(ctx, ctor_name, &constructors, &motive_body, &norm_ind_type) {
3495 Some(e) => e,
3496 None => return Some(make_sname_error()),
3497 };
3498
3499 if !syntax_equal(&case_conc, &expected) {
3501 return Some(make_sname_error());
3502 }
3503 }
3504
3505 Some(make_forall_syntax_generic(&norm_ind_type, &norm_motive))
3507}
3508
3509fn extract_inductive_name_from_syntax(term: &Term) -> Option<String> {
3515 if let Term::App(sname, text) = term {
3517 if let Term::Global(ctor) = sname.as_ref() {
3518 if ctor == "SName" {
3519 if let Term::Lit(Literal::Text(name)) = text.as_ref() {
3520 return Some(name.clone());
3521 }
3522 }
3523 }
3524 }
3525
3526 if let Term::App(inner, _arg) = term {
3528 if let Term::App(sapp, func) = inner.as_ref() {
3529 if let Term::Global(ctor) = sapp.as_ref() {
3530 if ctor == "SApp" {
3531 return extract_inductive_name_from_syntax(func);
3533 }
3534 }
3535 }
3536 }
3537
3538 None
3539}
3540
3541fn extract_case_proofs(term: &Term) -> Option<Vec<Term>> {
3545 let mut proofs = Vec::new();
3546 let mut current = term;
3547
3548 loop {
3549 if let Term::Global(name) = current {
3551 if name == "DCaseEnd" {
3552 return Some(proofs);
3553 }
3554 }
3555
3556 if let Term::App(inner, tail) = current {
3558 if let Term::App(dcase, head) = inner.as_ref() {
3559 if let Term::Global(name) = dcase.as_ref() {
3560 if name == "DCase" {
3561 proofs.push(head.as_ref().clone());
3562 current = tail.as_ref();
3563 continue;
3564 }
3565 }
3566 }
3567 }
3568
3569 return None;
3571 }
3572}
3573
3574fn build_case_expected(
3579 ctx: &Context,
3580 ctor_name: &str,
3581 _constructors: &[(&str, &Term)],
3582 motive_body: &Term,
3583 ind_type: &Term,
3584) -> Option<Term> {
3585 let ind_name = extract_inductive_name_from_syntax(ind_type)?;
3587
3588 if ind_name == "Nat" {
3590 if ctor_name == "Zero" {
3591 let zero = make_sname("Zero");
3593 return try_syn_subst_reduce(ctx, &zero, 0, motive_body);
3594 } else if ctor_name == "Succ" {
3595 return build_induction_step_formula(ctx, motive_body);
3598 }
3599 }
3600
3601 let ctor_syntax = make_sname(ctor_name);
3604
3605 let ctor_applied = apply_type_args_to_ctor(&ctor_syntax, ind_type);
3608
3609 if let Some(ctor_ty) = ctx.get_global(ctor_name) {
3611 if is_recursive_constructor(ctx, ctor_ty, &ind_name, ind_type) {
3613 return build_recursive_case_formula(ctx, ctor_name, ctor_ty, motive_body, ind_type, &ind_name);
3615 }
3616 }
3617
3618 try_syn_subst_reduce(ctx, &ctor_applied, 0, motive_body)
3620}
3621
3622fn apply_type_args_to_ctor(ctor: &Term, ind_type: &Term) -> Term {
3627 let args = extract_type_args(ind_type);
3629
3630 if args.is_empty() {
3631 return ctor.clone();
3632 }
3633
3634 args.iter().fold(ctor.clone(), |acc, arg| {
3636 Term::App(
3637 Box::new(Term::App(
3638 Box::new(Term::Global("SApp".to_string())),
3639 Box::new(acc),
3640 )),
3641 Box::new(arg.clone()),
3642 )
3643 })
3644}
3645
3646fn extract_type_args(term: &Term) -> Vec<Term> {
3652 let mut args = Vec::new();
3653 let mut current = term;
3654
3655 loop {
3657 if let Term::App(inner, arg) = current {
3658 if let Term::App(sapp, func) = inner.as_ref() {
3659 if let Term::Global(ctor) = sapp.as_ref() {
3660 if ctor == "SApp" {
3661 args.push(arg.as_ref().clone());
3662 current = func.as_ref();
3663 continue;
3664 }
3665 }
3666 }
3667 }
3668 break;
3669 }
3670
3671 args.reverse();
3673 args
3674}
3675
3676fn make_forall_syntax_generic(ind_type: &Term, motive: &Term) -> Term {
3680 let forall_type = Term::App(
3682 Box::new(Term::App(
3683 Box::new(Term::Global("SApp".to_string())),
3684 Box::new(make_sname("Forall")),
3685 )),
3686 Box::new(ind_type.clone()),
3687 );
3688
3689 Term::App(
3691 Box::new(Term::App(
3692 Box::new(Term::Global("SApp".to_string())),
3693 Box::new(forall_type),
3694 )),
3695 Box::new(motive.clone()),
3696 )
3697}
3698
3699fn is_recursive_constructor(
3701 _ctx: &Context,
3702 ctor_ty: &Term,
3703 ind_name: &str,
3704 _ind_type: &Term,
3705) -> bool {
3706 fn contains_inductive(term: &Term, ind_name: &str) -> bool {
3711 match term {
3712 Term::Global(name) => name == ind_name,
3713 Term::App(f, a) => {
3714 contains_inductive(f, ind_name) || contains_inductive(a, ind_name)
3715 }
3716 Term::Pi { param_type, body_type, .. } => {
3717 contains_inductive(param_type, ind_name) || contains_inductive(body_type, ind_name)
3718 }
3719 Term::Lambda { param_type, body, .. } => {
3720 contains_inductive(param_type, ind_name) || contains_inductive(body, ind_name)
3721 }
3722 _ => false,
3723 }
3724 }
3725
3726 fn check_params(term: &Term, ind_name: &str) -> bool {
3728 match term {
3729 Term::Pi { param_type, body_type, .. } => {
3730 if contains_inductive(param_type, ind_name) {
3732 return true;
3733 }
3734 check_params(body_type, ind_name)
3736 }
3737 _ => false,
3738 }
3739 }
3740
3741 check_params(ctor_ty, ind_name)
3742}
3743
3744fn build_recursive_case_formula(
3750 ctx: &Context,
3751 ctor_name: &str,
3752 ctor_ty: &Term,
3753 motive_body: &Term,
3754 ind_type: &Term,
3755 ind_name: &str,
3756) -> Option<Term> {
3757 let type_args = extract_type_args(ind_type);
3759
3760 let args = collect_ctor_args(ctor_ty, ind_name, &type_args);
3762
3763 if args.is_empty() {
3764 let ctor_applied = apply_type_args_to_ctor(&make_sname(ctor_name), ind_type);
3766 return try_syn_subst_reduce(ctx, &ctor_applied, 0, motive_body);
3767 }
3768
3769 let mut ctor_app = apply_type_args_to_ctor(&make_sname(ctor_name), ind_type);
3777 for (i, _) in args.iter().enumerate() {
3778 let idx = (args.len() - 1 - i) as i64;
3780 let var = Term::App(
3781 Box::new(Term::Global("SVar".to_string())),
3782 Box::new(Term::Lit(Literal::Int(idx))),
3783 );
3784 ctor_app = Term::App(
3785 Box::new(Term::App(
3786 Box::new(Term::Global("SApp".to_string())),
3787 Box::new(ctor_app),
3788 )),
3789 Box::new(var),
3790 );
3791 }
3792
3793 let p_ctor = try_syn_subst_reduce(ctx, &ctor_app, 0, motive_body)?;
3795
3796 let mut body = p_ctor;
3798 for (i, (arg_ty, is_recursive)) in args.iter().enumerate().rev() {
3799 if *is_recursive {
3800 let idx = (args.len() - 1 - i) as i64;
3803 let var = Term::App(
3804 Box::new(Term::Global("SVar".to_string())),
3805 Box::new(Term::Lit(Literal::Int(idx))),
3806 );
3807 let p_arg = try_syn_subst_reduce(ctx, &var, 0, motive_body)?;
3808 body = make_implies_syntax(&p_arg, &body);
3809 }
3810 let _ = (i, arg_ty); }
3813
3814 for (arg_ty, _) in args.iter().rev() {
3816 let slam = Term::App(
3818 Box::new(Term::App(
3819 Box::new(Term::Global("SLam".to_string())),
3820 Box::new(arg_ty.clone()),
3821 )),
3822 Box::new(body.clone()),
3823 );
3824 body = make_forall_syntax_with_type(arg_ty, &slam);
3826 }
3827
3828 Some(body)
3829}
3830
3831fn collect_ctor_args(ctor_ty: &Term, ind_name: &str, type_args: &[Term]) -> Vec<(Term, bool)> {
3834 let mut args = Vec::new();
3835 let mut current = ctor_ty;
3836 let mut skip_count = type_args.len();
3837
3838 loop {
3839 match current {
3840 Term::Pi { param_type, body_type, .. } => {
3841 if skip_count > 0 {
3842 skip_count -= 1;
3844 } else {
3845 let is_recursive = contains_inductive_term(param_type, ind_name);
3847 let arg_ty_syntax = kernel_type_to_syntax(param_type);
3849 args.push((arg_ty_syntax, is_recursive));
3850 }
3851 current = body_type;
3852 }
3853 _ => break,
3854 }
3855 }
3856
3857 args
3858}
3859
3860fn contains_inductive_term(term: &Term, ind_name: &str) -> bool {
3862 match term {
3863 Term::Global(name) => name == ind_name,
3864 Term::App(f, a) => {
3865 contains_inductive_term(f, ind_name) || contains_inductive_term(a, ind_name)
3866 }
3867 Term::Pi { param_type, body_type, .. } => {
3868 contains_inductive_term(param_type, ind_name) || contains_inductive_term(body_type, ind_name)
3869 }
3870 Term::Lambda { param_type, body, .. } => {
3871 contains_inductive_term(param_type, ind_name) || contains_inductive_term(body, ind_name)
3872 }
3873 _ => false,
3874 }
3875}
3876
3877fn kernel_type_to_syntax(term: &Term) -> Term {
3879 match term {
3880 Term::Global(name) => make_sname(name),
3881 Term::Var(name) => make_sname(name), Term::App(f, a) => {
3883 let f_syn = kernel_type_to_syntax(f);
3884 let a_syn = kernel_type_to_syntax(a);
3885 Term::App(
3887 Box::new(Term::App(
3888 Box::new(Term::Global("SApp".to_string())),
3889 Box::new(f_syn),
3890 )),
3891 Box::new(a_syn),
3892 )
3893 }
3894 Term::Pi { param, param_type, body_type } => {
3895 let pt_syn = kernel_type_to_syntax(param_type);
3896 let bt_syn = kernel_type_to_syntax(body_type);
3897 Term::App(
3899 Box::new(Term::App(
3900 Box::new(Term::Global("SPi".to_string())),
3901 Box::new(pt_syn),
3902 )),
3903 Box::new(bt_syn),
3904 )
3905 }
3906 Term::Sort(univ) => {
3907 Term::App(
3909 Box::new(Term::Global("SSort".to_string())),
3910 Box::new(univ_to_syntax(univ)),
3911 )
3912 }
3913 Term::Lit(lit) => {
3914 Term::App(
3916 Box::new(Term::Global("SLit".to_string())),
3917 Box::new(Term::Lit(lit.clone())),
3918 )
3919 }
3920 _ => {
3921 make_sname("Unknown")
3923 }
3924 }
3925}
3926
3927fn univ_to_syntax(univ: &crate::term::Universe) -> Term {
3929 match univ {
3930 crate::term::Universe::Prop => Term::Global("UProp".to_string()),
3931 crate::term::Universe::Type(n) => Term::App(
3932 Box::new(Term::Global("UType".to_string())),
3933 Box::new(Term::Lit(Literal::Int(*n as i64))),
3934 ),
3935 }
3936}
3937
3938fn try_try_inversion_reduce(ctx: &Context, goal: &Term) -> Option<Term> {
3947 let (ind_name, hyp_args) = match extract_applied_inductive_from_syntax(goal) {
3949 Some((name, args)) => (name, args),
3950 None => return Some(make_error_derivation()),
3951 };
3952
3953 if !ctx.is_inductive(&ind_name) {
3955 return Some(make_error_derivation());
3957 }
3958
3959 let constructors = ctx.get_constructors(&ind_name);
3961
3962 let mut any_possible = false;
3964 for (_ctor_name, ctor_type) in constructors.iter() {
3965 if can_constructor_match_args(ctx, ctor_type, &hyp_args, &ind_name) {
3966 any_possible = true;
3967 break;
3968 }
3969 }
3970
3971 if any_possible {
3972 return Some(make_error_derivation());
3974 }
3975
3976 Some(Term::App(
3978 Box::new(Term::Global("DInversion".to_string())),
3979 Box::new(goal.clone()),
3980 ))
3981}
3982
3983fn try_dinversion_conclude(ctx: &Context, hyp_type: &Term) -> Option<Term> {
3985 let norm_hyp = normalize(ctx, hyp_type);
3986
3987 let (ind_name, hyp_args) = match extract_applied_inductive_from_syntax(&norm_hyp) {
3988 Some((name, args)) => (name, args),
3989 None => return Some(make_sname_error()),
3990 };
3991
3992 if !ctx.is_inductive(&ind_name) {
3994 return Some(make_sname_error());
3995 }
3996
3997 let constructors = ctx.get_constructors(&ind_name);
3998
3999 for (_ctor_name, ctor_type) in constructors.iter() {
4001 if can_constructor_match_args(ctx, ctor_type, &hyp_args, &ind_name) {
4002 return Some(make_sname_error());
4003 }
4004 }
4005
4006 Some(make_sname("False"))
4008}
4009
4010fn extract_applied_inductive_from_syntax(term: &Term) -> Option<(String, Vec<Term>)> {
4015 if let Term::App(ctor, text) = term {
4017 if let Term::Global(ctor_name) = ctor.as_ref() {
4018 if ctor_name == "SName" {
4019 if let Term::Lit(Literal::Text(name)) = text.as_ref() {
4020 return Some((name.clone(), vec![]));
4021 }
4022 }
4023 }
4024 }
4025
4026 if let Term::App(inner, arg) = term {
4028 if let Term::App(sapp_ctor, func) = inner.as_ref() {
4029 if let Term::Global(ctor_name) = sapp_ctor.as_ref() {
4030 if ctor_name == "SApp" {
4031 let (name, mut args) = extract_applied_inductive_from_syntax(func)?;
4033 args.push(arg.as_ref().clone());
4034 return Some((name, args));
4035 }
4036 }
4037 }
4038 }
4039
4040 None
4041}
4042
4043fn can_constructor_match_args(
4052 ctx: &Context,
4053 ctor_type: &Term,
4054 hyp_args: &[Term],
4055 ind_name: &str,
4056) -> bool {
4057 let (result, pattern_vars) = decompose_ctor_type_with_vars(ctor_type);
4059
4060 let result_args = match extract_applied_inductive_from_syntax(&kernel_type_to_syntax(&result)) {
4062 Some((name, args)) if name == *ind_name => args,
4063 _ => return false,
4064 };
4065
4066 if result_args.len() != hyp_args.len() {
4068 return false;
4069 }
4070
4071 let mut bindings: std::collections::HashMap<String, Term> = std::collections::HashMap::new();
4073
4074 for (pattern, concrete) in result_args.iter().zip(hyp_args.iter()) {
4075 if !can_unify_syntax_terms_with_bindings(ctx, pattern, concrete, &pattern_vars, &mut bindings) {
4076 return false;
4077 }
4078 }
4079
4080 true
4084}
4085
4086fn decompose_ctor_type_with_vars(ty: &Term) -> (Term, Vec<String>) {
4092 let mut vars = Vec::new();
4093 let mut current = ty;
4094 loop {
4095 match current {
4096 Term::Pi { param, body_type, .. } => {
4097 vars.push(param.clone());
4098 current = body_type;
4099 }
4100 _ => break,
4101 }
4102 }
4103 (current.clone(), vars)
4104}
4105
4106fn can_unify_syntax_terms_with_bindings(
4113 ctx: &Context,
4114 pattern: &Term,
4115 concrete: &Term,
4116 pattern_vars: &[String],
4117 bindings: &mut std::collections::HashMap<String, Term>,
4118) -> bool {
4119 if let Term::App(ctor, _idx) = pattern {
4121 if let Term::Global(name) = ctor.as_ref() {
4122 if name == "SVar" {
4123 return true;
4124 }
4125 }
4126 }
4127
4128 if let Term::App(ctor1, text1) = pattern {
4130 if let Term::Global(n1) = ctor1.as_ref() {
4131 if n1 == "SName" {
4132 if let Term::Lit(Literal::Text(var_name)) = text1.as_ref() {
4133 if pattern_vars.contains(var_name) {
4135 if let Some(existing) = bindings.get(var_name) {
4137 return syntax_terms_equal(existing, concrete);
4139 } else {
4140 bindings.insert(var_name.clone(), concrete.clone());
4142 return true;
4143 }
4144 }
4145 }
4146 if let Term::App(ctor2, text2) = concrete {
4148 if let Term::Global(n2) = ctor2.as_ref() {
4149 if n2 == "SName" {
4150 return text1 == text2;
4151 }
4152 }
4153 }
4154 return false;
4155 }
4156 }
4157 }
4158
4159 if let (Term::App(inner1, arg1), Term::App(inner2, arg2)) = (pattern, concrete) {
4161 if let (Term::App(sapp1, func1), Term::App(sapp2, func2)) =
4162 (inner1.as_ref(), inner2.as_ref())
4163 {
4164 if let (Term::Global(n1), Term::Global(n2)) = (sapp1.as_ref(), sapp2.as_ref()) {
4165 if n1 == "SApp" && n2 == "SApp" {
4166 return can_unify_syntax_terms_with_bindings(ctx, func1, func2, pattern_vars, bindings)
4167 && can_unify_syntax_terms_with_bindings(ctx, arg1.as_ref(), arg2.as_ref(), pattern_vars, bindings);
4168 }
4169 }
4170 }
4171 }
4172
4173 if let (Term::App(ctor1, lit1), Term::App(ctor2, lit2)) = (pattern, concrete) {
4175 if let (Term::Global(n1), Term::Global(n2)) = (ctor1.as_ref(), ctor2.as_ref()) {
4176 if n1 == "SLit" && n2 == "SLit" {
4177 return lit1 == lit2;
4178 }
4179 }
4180 }
4181
4182 pattern == concrete
4184}
4185
4186fn syntax_terms_equal(a: &Term, b: &Term) -> bool {
4188 match (a, b) {
4189 (Term::App(f1, x1), Term::App(f2, x2)) => {
4190 syntax_terms_equal(f1, f2) && syntax_terms_equal(x1, x2)
4191 }
4192 (Term::Global(n1), Term::Global(n2)) => n1 == n2,
4193 (Term::Lit(l1), Term::Lit(l2)) => l1 == l2,
4194 _ => a == b,
4195 }
4196}
4197
4198fn extract_eq_components_from_syntax(term: &Term) -> Option<(Term, Term, Term)> {
4206 let (eq_a_x, y) = extract_sapp(term)?;
4211
4212 let (eq_a, x) = extract_sapp(&eq_a_x)?;
4214
4215 let (eq, a) = extract_sapp(&eq_a)?;
4217
4218 let eq_name = extract_sname(&eq)?;
4220 if eq_name != "Eq" {
4221 return None;
4222 }
4223
4224 Some((a, x, y))
4225}
4226
4227fn extract_sapp(term: &Term) -> Option<(Term, Term)> {
4229 if let Term::App(inner, x) = term {
4231 if let Term::App(sapp_ctor, f) = inner.as_ref() {
4232 if let Term::Global(ctor_name) = sapp_ctor.as_ref() {
4233 if ctor_name == "SApp" {
4234 return Some((f.as_ref().clone(), x.as_ref().clone()));
4235 }
4236 }
4237 }
4238 }
4239 None
4240}
4241
4242fn extract_sname(term: &Term) -> Option<String> {
4244 if let Term::App(ctor, text) = term {
4245 if let Term::Global(ctor_name) = ctor.as_ref() {
4246 if ctor_name == "SName" {
4247 if let Term::Lit(Literal::Text(name)) = text.as_ref() {
4248 return Some(name.clone());
4249 }
4250 }
4251 }
4252 }
4253 None
4254}
4255
4256fn contains_subterm_syntax(term: &Term, target: &Term) -> bool {
4258 if syntax_equal(term, target) {
4259 return true;
4260 }
4261
4262 if let Some((f, x)) = extract_sapp(term) {
4264 if contains_subterm_syntax(&f, target) || contains_subterm_syntax(&x, target) {
4265 return true;
4266 }
4267 }
4268
4269 if let Some((t, body)) = extract_slam(term) {
4271 if contains_subterm_syntax(&t, target) || contains_subterm_syntax(&body, target) {
4272 return true;
4273 }
4274 }
4275
4276 if let Some((t, body)) = extract_spi(term) {
4278 if contains_subterm_syntax(&t, target) || contains_subterm_syntax(&body, target) {
4279 return true;
4280 }
4281 }
4282
4283 false
4284}
4285
4286fn extract_slam(term: &Term) -> Option<(Term, Term)> {
4288 if let Term::App(inner, body) = term {
4289 if let Term::App(slam_ctor, t) = inner.as_ref() {
4290 if let Term::Global(ctor_name) = slam_ctor.as_ref() {
4291 if ctor_name == "SLam" {
4292 return Some((t.as_ref().clone(), body.as_ref().clone()));
4293 }
4294 }
4295 }
4296 }
4297 None
4298}
4299
4300fn extract_spi(term: &Term) -> Option<(Term, Term)> {
4302 if let Term::App(inner, body) = term {
4303 if let Term::App(spi_ctor, t) = inner.as_ref() {
4304 if let Term::Global(ctor_name) = spi_ctor.as_ref() {
4305 if ctor_name == "SPi" {
4306 return Some((t.as_ref().clone(), body.as_ref().clone()));
4307 }
4308 }
4309 }
4310 }
4311 None
4312}
4313
4314fn replace_first_subterm_syntax(term: &Term, target: &Term, replacement: &Term) -> Option<Term> {
4316 if syntax_equal(term, target) {
4318 return Some(replacement.clone());
4319 }
4320
4321 if let Some((f, x)) = extract_sapp(term) {
4323 if let Some(new_f) = replace_first_subterm_syntax(&f, target, replacement) {
4325 return Some(make_sapp(new_f, x));
4326 }
4327 if let Some(new_x) = replace_first_subterm_syntax(&x, target, replacement) {
4329 return Some(make_sapp(f, new_x));
4330 }
4331 }
4332
4333 if let Some((t, body)) = extract_slam(term) {
4335 if let Some(new_t) = replace_first_subterm_syntax(&t, target, replacement) {
4336 return Some(make_slam(new_t, body));
4337 }
4338 if let Some(new_body) = replace_first_subterm_syntax(&body, target, replacement) {
4339 return Some(make_slam(t, new_body));
4340 }
4341 }
4342
4343 if let Some((t, body)) = extract_spi(term) {
4345 if let Some(new_t) = replace_first_subterm_syntax(&t, target, replacement) {
4346 return Some(make_spi(new_t, body));
4347 }
4348 if let Some(new_body) = replace_first_subterm_syntax(&body, target, replacement) {
4349 return Some(make_spi(t, new_body));
4350 }
4351 }
4352
4353 None
4355}
4356
4357fn try_try_rewrite_reduce(
4360 ctx: &Context,
4361 eq_proof: &Term,
4362 goal: &Term,
4363 reverse: bool,
4364) -> Option<Term> {
4365 let eq_conclusion = try_concludes_reduce(ctx, eq_proof)?;
4367
4368 let (ty, lhs, rhs) = match extract_eq_components_from_syntax(&eq_conclusion) {
4370 Some(components) => components,
4371 None => return Some(make_error_derivation()),
4372 };
4373
4374 let (target, replacement) = if reverse { (rhs, lhs) } else { (lhs, rhs) };
4376
4377 if !contains_subterm_syntax(goal, &target) {
4379 return Some(make_error_derivation());
4380 }
4381
4382 let new_goal = match replace_first_subterm_syntax(goal, &target, &replacement) {
4384 Some(ng) => ng,
4385 None => return Some(make_error_derivation()),
4386 };
4387
4388 Some(Term::App(
4390 Box::new(Term::App(
4391 Box::new(Term::App(
4392 Box::new(Term::Global("DRewrite".to_string())),
4393 Box::new(eq_proof.clone()),
4394 )),
4395 Box::new(goal.clone()),
4396 )),
4397 Box::new(new_goal),
4398 ))
4399}
4400
4401fn try_drewrite_conclude(
4403 ctx: &Context,
4404 eq_proof: &Term,
4405 old_goal: &Term,
4406 new_goal: &Term,
4407) -> Option<Term> {
4408 let eq_conclusion = try_concludes_reduce(ctx, eq_proof)?;
4410
4411 let (_ty, lhs, rhs) = match extract_eq_components_from_syntax(&eq_conclusion) {
4413 Some(components) => components,
4414 None => return Some(make_sname_error()),
4415 };
4416
4417 if let Some(computed_new) = replace_first_subterm_syntax(old_goal, &lhs, &rhs) {
4420 if syntax_equal(&computed_new, new_goal) {
4421 return Some(new_goal.clone());
4422 }
4423 }
4424
4425 if let Some(computed_new) = replace_first_subterm_syntax(old_goal, &rhs, &lhs) {
4427 if syntax_equal(&computed_new, new_goal) {
4428 return Some(new_goal.clone());
4429 }
4430 }
4431
4432 Some(make_sname_error())
4434}
4435
4436fn try_try_destruct_reduce(
4438 ctx: &Context,
4439 ind_type: &Term,
4440 motive: &Term,
4441 cases: &Term,
4442) -> Option<Term> {
4443 Some(Term::App(
4450 Box::new(Term::App(
4451 Box::new(Term::App(
4452 Box::new(Term::Global("DDestruct".to_string())),
4453 Box::new(ind_type.clone()),
4454 )),
4455 Box::new(motive.clone()),
4456 )),
4457 Box::new(cases.clone()),
4458 ))
4459}
4460
4461fn try_ddestruct_conclude(
4463 ctx: &Context,
4464 ind_type: &Term,
4465 motive: &Term,
4466 cases: &Term,
4467) -> Option<Term> {
4468 let ind_name = extract_inductive_name_from_syntax(ind_type)?;
4473
4474 if !ctx.is_inductive(&ind_name) {
4476 return Some(make_sname_error());
4477 }
4478
4479 let constructors = ctx.get_constructors(&ind_name);
4480
4481 let case_proofs = match extract_case_proofs(cases) {
4483 Some(proofs) => proofs,
4484 None => return Some(make_sname_error()),
4485 };
4486
4487 if case_proofs.len() != constructors.len() {
4489 return Some(make_sname_error());
4490 }
4491
4492 Some(make_forall_syntax_with_type(ind_type, motive))
4498}
4499
4500fn try_try_apply_reduce(
4502 ctx: &Context,
4503 hyp_name: &Term,
4504 hyp_proof: &Term,
4505 goal: &Term,
4506) -> Option<Term> {
4507 let hyp_conclusion = try_concludes_reduce(ctx, hyp_proof)?;
4509
4510 if let Some((antecedent, consequent)) = extract_spi(&hyp_conclusion) {
4512 if syntax_equal(&consequent, goal) {
4514 return Some(Term::App(
4516 Box::new(Term::App(
4517 Box::new(Term::App(
4518 Box::new(Term::App(
4519 Box::new(Term::Global("DApply".to_string())),
4520 Box::new(hyp_name.clone()),
4521 )),
4522 Box::new(hyp_proof.clone()),
4523 )),
4524 Box::new(goal.clone()),
4525 )),
4526 Box::new(antecedent),
4527 ));
4528 }
4529 }
4530
4531 if let Some(forall_body) = extract_forall_body(&hyp_conclusion) {
4533 return Some(Term::App(
4539 Box::new(Term::App(
4540 Box::new(Term::App(
4541 Box::new(Term::App(
4542 Box::new(Term::Global("DApply".to_string())),
4543 Box::new(hyp_name.clone()),
4544 )),
4545 Box::new(hyp_proof.clone()),
4546 )),
4547 Box::new(goal.clone()),
4548 )),
4549 Box::new(make_sname("True")),
4550 ));
4551 }
4552
4553 if syntax_equal(&hyp_conclusion, goal) {
4555 return Some(Term::App(
4556 Box::new(Term::App(
4557 Box::new(Term::App(
4558 Box::new(Term::App(
4559 Box::new(Term::Global("DApply".to_string())),
4560 Box::new(hyp_name.clone()),
4561 )),
4562 Box::new(hyp_proof.clone()),
4563 )),
4564 Box::new(goal.clone()),
4565 )),
4566 Box::new(make_sname("True")),
4567 ));
4568 }
4569
4570 Some(make_error_derivation())
4572}
4573
4574fn try_dapply_conclude(
4576 ctx: &Context,
4577 hyp_name: &Term,
4578 hyp_proof: &Term,
4579 old_goal: &Term,
4580 new_goal: &Term,
4581) -> Option<Term> {
4582 let hyp_conclusion = try_concludes_reduce(ctx, hyp_proof)?;
4584
4585 if let Some((antecedent, consequent)) = extract_spi(&hyp_conclusion) {
4588 if syntax_equal(&consequent, old_goal) {
4589 if syntax_equal(&antecedent, new_goal) || extract_sname(new_goal) == Some("True".to_string()) {
4590 return Some(new_goal.clone());
4591 }
4592 }
4593 }
4594
4595 if let Some(_forall_body) = extract_forall_body(&hyp_conclusion) {
4597 if extract_sname(new_goal) == Some("True".to_string()) {
4599 return Some(new_goal.clone());
4600 }
4601 }
4602
4603 if syntax_equal(&hyp_conclusion, old_goal) {
4605 if extract_sname(new_goal) == Some("True".to_string()) {
4606 return Some(new_goal.clone());
4607 }
4608 }
4609
4610 Some(make_sname_error())
4612}
4613