use crate::context::Context;
use crate::omega;
use crate::term::{Literal, Term, Universe};
use crate::type_checker::substitute;
pub fn normalize(ctx: &Context, term: &Term) -> Term {
let mut current = term.clone();
let mut fuel = 10000;
loop {
if fuel == 0 {
return current;
}
fuel -= 1;
let reduced = reduce_step(ctx, ¤t);
if reduced == current {
return current;
}
current = reduced;
}
}
fn reduce_step(ctx: &Context, term: &Term) -> Term {
match term {
Term::Lit(_) => term.clone(),
Term::App(func, arg) => {
if let Some(result) = try_primitive_reduce(func, arg) {
return result;
}
if let Some(result) = try_reflection_reduce(ctx, func, arg) {
return result;
}
match func.as_ref() {
Term::Lambda { param, body, .. } => {
substitute(body, param, arg)
}
Term::Fix { name, body } => {
if is_constructor_form(ctx, arg) {
let fix_term = Term::Fix {
name: name.clone(),
body: body.clone(),
};
let unfolded = substitute(body, name, &fix_term);
Term::App(Box::new(unfolded), arg.clone())
} else {
let reduced_arg = reduce_step(ctx, arg);
if reduced_arg != **arg {
Term::App(func.clone(), Box::new(reduced_arg))
} else {
term.clone()
}
}
}
Term::App(_, _) => {
let reduced_func = reduce_step(ctx, func);
if reduced_func != **func {
Term::App(Box::new(reduced_func), arg.clone())
} else {
let reduced_arg = reduce_step(ctx, arg);
Term::App(func.clone(), Box::new(reduced_arg))
}
}
_ => {
let reduced_func = reduce_step(ctx, func);
if reduced_func != **func {
Term::App(Box::new(reduced_func), arg.clone())
} else {
let reduced_arg = reduce_step(ctx, arg);
Term::App(func.clone(), Box::new(reduced_arg))
}
}
}
}
Term::Match {
discriminant,
motive,
cases,
} => {
if let Some((ctor_idx, args)) = extract_constructor(ctx, discriminant) {
let case = &cases[ctor_idx];
let mut result = case.clone();
for arg in args {
result = Term::App(Box::new(result), Box::new(arg));
}
reduce_step(ctx, &result)
} else {
let reduced_disc = reduce_step(ctx, discriminant);
if reduced_disc != **discriminant {
Term::Match {
discriminant: Box::new(reduced_disc),
motive: motive.clone(),
cases: cases.clone(),
}
} else {
term.clone()
}
}
}
Term::Lambda {
param,
param_type,
body,
} => {
let reduced_param_type = reduce_step(ctx, param_type);
let reduced_body = reduce_step(ctx, body);
if reduced_param_type != **param_type || reduced_body != **body {
Term::Lambda {
param: param.clone(),
param_type: Box::new(reduced_param_type),
body: Box::new(reduced_body),
}
} else {
term.clone()
}
}
Term::Pi {
param,
param_type,
body_type,
} => {
let reduced_param_type = reduce_step(ctx, param_type);
let reduced_body_type = reduce_step(ctx, body_type);
if reduced_param_type != **param_type || reduced_body_type != **body_type {
Term::Pi {
param: param.clone(),
param_type: Box::new(reduced_param_type),
body_type: Box::new(reduced_body_type),
}
} else {
term.clone()
}
}
Term::Fix { name, body } => {
let reduced_body = reduce_step(ctx, body);
if reduced_body != **body {
Term::Fix {
name: name.clone(),
body: Box::new(reduced_body),
}
} else {
term.clone()
}
}
Term::Sort(_) | Term::Var(_) | Term::Hole => term.clone(),
Term::Global(name) => {
if let Some(body) = ctx.get_definition_body(name) {
body.clone()
} else {
term.clone()
}
}
}
}
fn is_constructor_form(ctx: &Context, term: &Term) -> bool {
extract_constructor(ctx, term).is_some()
}
fn extract_constructor(ctx: &Context, term: &Term) -> Option<(usize, Vec<Term>)> {
let mut args = Vec::new();
let mut current = term;
while let Term::App(func, arg) = current {
args.push((**arg).clone());
current = func;
}
args.reverse();
if let Term::Global(name) = current {
if let Some(inductive) = ctx.constructor_inductive(name) {
let ctors = ctx.get_constructors(inductive);
for (idx, (ctor_name, ctor_type)) in ctors.iter().enumerate() {
if *ctor_name == name {
let num_type_params = count_type_params(ctor_type);
let value_args = if num_type_params < args.len() {
args[num_type_params..].to_vec()
} else {
vec![]
};
return Some((idx, value_args));
}
}
}
}
None
}
fn count_type_params(ty: &Term) -> usize {
let mut count = 0;
let mut current = ty;
while let Term::Pi { param_type, body_type, .. } = current {
if is_sort(param_type) {
count += 1;
current = body_type;
} else {
break;
}
}
count
}
fn is_sort(term: &Term) -> bool {
matches!(term, Term::Sort(_))
}
fn try_primitive_reduce(func: &Term, arg: &Term) -> Option<Term> {
if let Term::App(op_term, x) = func {
if let Term::Global(op_name) = op_term.as_ref() {
if let (Term::Lit(Literal::Int(x_val)), Term::Lit(Literal::Int(y_val))) =
(x.as_ref(), arg)
{
let result = match op_name.as_str() {
"add" => x_val.checked_add(*y_val)?,
"sub" => x_val.checked_sub(*y_val)?,
"mul" => x_val.checked_mul(*y_val)?,
"div" => x_val.checked_div(*y_val)?,
"mod" => x_val.checked_rem(*y_val)?,
_ => return None,
};
return Some(Term::Lit(Literal::Int(result)));
}
}
}
None
}
fn try_reflection_reduce(ctx: &Context, func: &Term, arg: &Term) -> Option<Term> {
if let Term::Global(op_name) = func {
match op_name.as_str() {
"syn_size" => {
let norm_arg = normalize(ctx, arg);
return try_syn_size_reduce(ctx, &norm_arg);
}
"syn_max_var" => {
let norm_arg = normalize(ctx, arg);
return try_syn_max_var_reduce(ctx, &norm_arg, 0);
}
"syn_step" => {
let norm_arg = normalize(ctx, arg);
return try_syn_step_reduce(ctx, &norm_arg);
}
"syn_quote" => {
let norm_arg = normalize(ctx, arg);
return try_syn_quote_reduce(ctx, &norm_arg);
}
"syn_diag" => {
let norm_arg = normalize(ctx, arg);
return try_syn_diag_reduce(ctx, &norm_arg);
}
"concludes" => {
let norm_arg = normalize(ctx, arg);
return try_concludes_reduce(ctx, &norm_arg);
}
"try_refl" => {
let norm_arg = normalize(ctx, arg);
return try_try_refl_reduce(ctx, &norm_arg);
}
"tact_fail" => {
return Some(make_error_derivation());
}
"try_compute" => {
let norm_arg = normalize(ctx, arg);
return Some(Term::App(
Box::new(Term::Global("DCompute".to_string())),
Box::new(norm_arg),
));
}
"try_ring" => {
let norm_arg = normalize(ctx, arg);
return try_try_ring_reduce(ctx, &norm_arg);
}
"try_lia" => {
let norm_arg = normalize(ctx, arg);
return try_try_lia_reduce(ctx, &norm_arg);
}
"try_cc" => {
let norm_arg = normalize(ctx, arg);
return try_try_cc_reduce(ctx, &norm_arg);
}
"try_simp" => {
let norm_arg = normalize(ctx, arg);
return try_try_simp_reduce(ctx, &norm_arg);
}
"try_omega" => {
let norm_arg = normalize(ctx, arg);
return try_try_omega_reduce(ctx, &norm_arg);
}
"try_auto" => {
let norm_arg = normalize(ctx, arg);
return try_try_auto_reduce(ctx, &norm_arg);
}
"try_bitblast" => {
let norm_arg = normalize(ctx, arg);
return try_try_bitblast_reduce(ctx, &norm_arg);
}
"try_tabulate" => {
let norm_arg = normalize(ctx, arg);
return try_try_tabulate_reduce(ctx, &norm_arg);
}
"try_hw_auto" => {
let norm_arg = normalize(ctx, arg);
return try_try_hw_auto_reduce(ctx, &norm_arg);
}
"try_inversion" => {
let norm_arg = normalize(ctx, arg);
return try_try_inversion_reduce(ctx, &norm_arg);
}
"induction_num_cases" => {
let norm_arg = normalize(ctx, arg);
return try_induction_num_cases_reduce(ctx, &norm_arg);
}
_ => {}
}
}
if let Term::App(partial2, cutoff) = func {
if let Term::App(partial1, amount) = partial2.as_ref() {
if let Term::Global(op_name) = partial1.as_ref() {
if op_name == "syn_lift" {
if let (Term::Lit(Literal::Int(amt)), Term::Lit(Literal::Int(cut))) =
(amount.as_ref(), cutoff.as_ref())
{
let norm_term = normalize(ctx, arg);
return try_syn_lift_reduce(ctx, *amt, *cut, &norm_term);
}
}
}
}
}
if let Term::App(partial2, index) = func {
if let Term::App(partial1, replacement) = partial2.as_ref() {
if let Term::Global(op_name) = partial1.as_ref() {
if op_name == "syn_subst" {
if let Term::Lit(Literal::Int(idx)) = index.as_ref() {
let norm_replacement = normalize(ctx, replacement);
let norm_term = normalize(ctx, arg);
return try_syn_subst_reduce(ctx, &norm_replacement, *idx, &norm_term);
}
}
}
}
}
if let Term::App(partial1, body) = func {
if let Term::Global(op_name) = partial1.as_ref() {
if op_name == "syn_beta" {
let norm_body = normalize(ctx, body);
let norm_arg = normalize(ctx, arg);
return try_syn_beta_reduce(ctx, &norm_body, &norm_arg);
}
}
}
if let Term::App(partial1, context) = func {
if let Term::Global(op_name) = partial1.as_ref() {
if op_name == "try_cong" {
let norm_context = normalize(ctx, context);
let norm_proof = normalize(ctx, arg);
return Some(Term::App(
Box::new(Term::App(
Box::new(Term::Global("DCong".to_string())),
Box::new(norm_context),
)),
Box::new(norm_proof),
));
}
}
}
if let Term::App(partial1, eq_proof) = func {
if let Term::Global(op_name) = partial1.as_ref() {
if op_name == "try_rewrite" {
let norm_eq_proof = normalize(ctx, eq_proof);
let norm_goal = normalize(ctx, arg);
return try_try_rewrite_reduce(ctx, &norm_eq_proof, &norm_goal, false);
}
if op_name == "try_rewrite_rev" {
let norm_eq_proof = normalize(ctx, eq_proof);
let norm_goal = normalize(ctx, arg);
return try_try_rewrite_reduce(ctx, &norm_eq_proof, &norm_goal, true);
}
}
}
if let Term::App(partial1, fuel_term) = func {
if let Term::Global(op_name) = partial1.as_ref() {
if op_name == "syn_eval" {
if let Term::Lit(Literal::Int(fuel)) = fuel_term.as_ref() {
let norm_term = normalize(ctx, arg);
return try_syn_eval_reduce(ctx, *fuel, &norm_term);
}
}
}
}
if let Term::App(partial1, ind_type) = func {
if let Term::Global(op_name) = partial1.as_ref() {
if op_name == "induction_base_goal" {
let norm_ind_type = normalize(ctx, ind_type);
let norm_motive = normalize(ctx, arg);
return try_induction_base_goal_reduce(ctx, &norm_ind_type, &norm_motive);
}
}
}
if let Term::App(partial1, t2) = func {
if let Term::App(combinator, t1) = partial1.as_ref() {
if let Term::Global(name) = combinator.as_ref() {
if name == "tact_orelse" {
return try_tact_orelse_reduce(ctx, t1, t2, arg);
}
if name == "tact_then" {
return try_tact_then_reduce(ctx, t1, t2, arg);
}
}
}
}
if let Term::App(combinator, t) = func {
if let Term::Global(name) = combinator.as_ref() {
if name == "tact_try" {
return try_tact_try_reduce(ctx, t, arg);
}
if name == "tact_repeat" {
return try_tact_repeat_reduce(ctx, t, arg);
}
if name == "tact_solve" {
return try_tact_solve_reduce(ctx, t, arg);
}
if name == "tact_first" {
return try_tact_first_reduce(ctx, t, arg);
}
}
}
if let Term::App(partial1, motive) = func {
if let Term::App(combinator, ind_type) = partial1.as_ref() {
if let Term::Global(name) = combinator.as_ref() {
if name == "induction_step_goal" {
let norm_ind_type = normalize(ctx, ind_type);
let norm_motive = normalize(ctx, motive);
let norm_idx = normalize(ctx, arg);
return try_induction_step_goal_reduce(ctx, &norm_ind_type, &norm_motive, &norm_idx);
}
}
}
}
if let Term::App(partial1, motive) = func {
if let Term::App(combinator, ind_type) = partial1.as_ref() {
if let Term::Global(name) = combinator.as_ref() {
if name == "try_induction" {
let norm_ind_type = normalize(ctx, ind_type);
let norm_motive = normalize(ctx, motive);
let norm_cases = normalize(ctx, arg);
return try_try_induction_reduce(ctx, &norm_ind_type, &norm_motive, &norm_cases);
}
}
}
}
if let Term::App(partial1, motive) = func {
if let Term::App(combinator, ind_type) = partial1.as_ref() {
if let Term::Global(name) = combinator.as_ref() {
if name == "try_destruct" {
let norm_ind_type = normalize(ctx, ind_type);
let norm_motive = normalize(ctx, motive);
let norm_cases = normalize(ctx, arg);
return try_try_destruct_reduce(ctx, &norm_ind_type, &norm_motive, &norm_cases);
}
}
}
}
if let Term::App(partial1, hyp_proof) = func {
if let Term::App(combinator, hyp_name) = partial1.as_ref() {
if let Term::Global(name) = combinator.as_ref() {
if name == "try_apply" {
let norm_hyp_name = normalize(ctx, hyp_name);
let norm_hyp_proof = normalize(ctx, hyp_proof);
let norm_goal = normalize(ctx, arg);
return try_try_apply_reduce(ctx, &norm_hyp_name, &norm_hyp_proof, &norm_goal);
}
}
}
}
None
}
fn try_syn_size_reduce(ctx: &Context, term: &Term) -> Option<Term> {
if let Term::App(ctor_term, _inner_arg) = term {
if let Term::Global(ctor_name) = ctor_term.as_ref() {
match ctor_name.as_str() {
"SVar" | "SGlobal" | "SSort" | "SLit" | "SName" => {
return Some(Term::Lit(Literal::Int(1)));
}
_ => {}
}
}
if let Term::App(inner, a) = ctor_term.as_ref() {
if let Term::Global(ctor_name) = inner.as_ref() {
match ctor_name.as_str() {
"SApp" | "SLam" | "SPi" => {
let a_size = try_syn_size_reduce(ctx, a)?;
let b_size = try_syn_size_reduce(ctx, _inner_arg)?;
if let (Term::Lit(Literal::Int(a_n)), Term::Lit(Literal::Int(b_n))) =
(a_size, b_size)
{
return Some(Term::Lit(Literal::Int(1 + a_n + b_n)));
}
}
_ => {}
}
}
}
}
None
}
fn try_syn_max_var_reduce(ctx: &Context, term: &Term, depth: i64) -> Option<Term> {
if let Term::App(ctor_term, inner_arg) = term {
if let Term::Global(ctor_name) = ctor_term.as_ref() {
match ctor_name.as_str() {
"SVar" => {
if let Term::Lit(Literal::Int(k)) = inner_arg.as_ref() {
if *k >= depth {
return Some(Term::Lit(Literal::Int(*k - depth)));
} else {
return Some(Term::Lit(Literal::Int(-1)));
}
}
}
"SGlobal" | "SSort" | "SLit" | "SName" => {
return Some(Term::Lit(Literal::Int(-1)));
}
_ => {}
}
}
if let Term::App(inner, a) = ctor_term.as_ref() {
if let Term::Global(ctor_name) = inner.as_ref() {
match ctor_name.as_str() {
"SApp" => {
let a_max = try_syn_max_var_reduce(ctx, a, depth)?;
let b_max = try_syn_max_var_reduce(ctx, inner_arg, depth)?;
if let (Term::Lit(Literal::Int(a_n)), Term::Lit(Literal::Int(b_n))) =
(a_max, b_max)
{
return Some(Term::Lit(Literal::Int(a_n.max(b_n))));
}
}
"SLam" | "SPi" => {
let a_max = try_syn_max_var_reduce(ctx, a, depth)?;
let b_max = try_syn_max_var_reduce(ctx, inner_arg, depth + 1)?;
if let (Term::Lit(Literal::Int(a_n)), Term::Lit(Literal::Int(b_n))) =
(a_max, b_max)
{
return Some(Term::Lit(Literal::Int(a_n.max(b_n))));
}
}
_ => {}
}
}
}
}
None
}
fn try_syn_lift_reduce(ctx: &Context, amount: i64, cutoff: i64, term: &Term) -> Option<Term> {
if let Term::App(ctor_term, inner_arg) = term {
if let Term::Global(ctor_name) = ctor_term.as_ref() {
match ctor_name.as_str() {
"SVar" => {
if let Term::Lit(Literal::Int(k)) = inner_arg.as_ref() {
if *k >= cutoff {
return Some(Term::App(
Box::new(Term::Global("SVar".to_string())),
Box::new(Term::Lit(Literal::Int(*k + amount))),
));
} else {
return Some(term.clone());
}
}
}
"SGlobal" | "SSort" | "SLit" | "SName" => {
return Some(term.clone());
}
_ => {}
}
}
if let Term::App(inner, a) = ctor_term.as_ref() {
if let Term::Global(ctor_name) = inner.as_ref() {
match ctor_name.as_str() {
"SApp" => {
let a_lifted = try_syn_lift_reduce(ctx, amount, cutoff, a)?;
let b_lifted = try_syn_lift_reduce(ctx, amount, cutoff, inner_arg)?;
return Some(Term::App(
Box::new(Term::App(
Box::new(Term::Global("SApp".to_string())),
Box::new(a_lifted),
)),
Box::new(b_lifted),
));
}
"SLam" | "SPi" => {
let param_lifted = try_syn_lift_reduce(ctx, amount, cutoff, a)?;
let body_lifted =
try_syn_lift_reduce(ctx, amount, cutoff + 1, inner_arg)?;
return Some(Term::App(
Box::new(Term::App(
Box::new(Term::Global(ctor_name.clone())),
Box::new(param_lifted),
)),
Box::new(body_lifted),
));
}
_ => {}
}
}
}
}
None
}
fn try_syn_subst_reduce(
ctx: &Context,
replacement: &Term,
index: i64,
term: &Term,
) -> Option<Term> {
if let Term::App(ctor_term, inner_arg) = term {
if let Term::Global(ctor_name) = ctor_term.as_ref() {
match ctor_name.as_str() {
"SVar" => {
if let Term::Lit(Literal::Int(k)) = inner_arg.as_ref() {
if *k == index {
return Some(replacement.clone());
} else {
return Some(term.clone());
}
}
}
"SGlobal" | "SSort" | "SLit" | "SName" => {
return Some(term.clone());
}
_ => {}
}
}
if let Term::App(inner, a) = ctor_term.as_ref() {
if let Term::Global(ctor_name) = inner.as_ref() {
match ctor_name.as_str() {
"SApp" => {
let a_subst = try_syn_subst_reduce(ctx, replacement, index, a)?;
let b_subst = try_syn_subst_reduce(ctx, replacement, index, inner_arg)?;
return Some(Term::App(
Box::new(Term::App(
Box::new(Term::Global("SApp".to_string())),
Box::new(a_subst),
)),
Box::new(b_subst),
));
}
"SLam" | "SPi" => {
let param_subst = try_syn_subst_reduce(ctx, replacement, index, a)?;
let lifted_replacement = try_syn_lift_reduce(ctx, 1, 0, replacement)?;
let body_subst = try_syn_subst_reduce(
ctx,
&lifted_replacement,
index + 1,
inner_arg,
)?;
return Some(Term::App(
Box::new(Term::App(
Box::new(Term::Global(ctor_name.clone())),
Box::new(param_subst),
)),
Box::new(body_subst),
));
}
_ => {}
}
}
}
}
None
}
fn try_syn_beta_reduce(ctx: &Context, body: &Term, arg: &Term) -> Option<Term> {
try_syn_subst_reduce(ctx, arg, 0, body)
}
fn try_syn_arith_reduce(func: &Term, arg: &Term) -> Option<Term> {
if let Term::App(inner_ctor, n_term) = func {
if let Term::App(sapp_ctor, op_term) = inner_ctor.as_ref() {
if let Term::Global(ctor_name) = sapp_ctor.as_ref() {
if ctor_name != "SApp" {
return None;
}
} else {
return None;
}
let op = if let Term::App(sname_ctor, op_str) = op_term.as_ref() {
if let Term::Global(name) = sname_ctor.as_ref() {
if name == "SName" {
if let Term::Lit(Literal::Text(op)) = op_str.as_ref() {
op.clone()
} else {
return None;
}
} else {
return None;
}
} else {
return None;
}
} else {
return None;
};
let n = if let Term::App(slit_ctor, n_val) = n_term.as_ref() {
if let Term::Global(name) = slit_ctor.as_ref() {
if name == "SLit" {
if let Term::Lit(Literal::Int(n)) = n_val.as_ref() {
*n
} else {
return None;
}
} else {
return None;
}
} else {
return None;
}
} else {
return None;
};
let m = if let Term::App(slit_ctor, m_val) = arg {
if let Term::Global(name) = slit_ctor.as_ref() {
if name == "SLit" {
if let Term::Lit(Literal::Int(m)) = m_val.as_ref() {
*m
} else {
return None;
}
} else {
return None;
}
} else {
return None;
}
} else {
return None;
};
let result = match op.as_str() {
"add" => n.checked_add(m),
"sub" => n.checked_sub(m),
"mul" => n.checked_mul(m),
"div" => {
if m == 0 {
return None; }
n.checked_div(m)
}
"mod" => {
if m == 0 {
return None; }
n.checked_rem(m)
}
_ => None,
};
if let Some(r) = result {
return Some(Term::App(
Box::new(Term::Global("SLit".to_string())),
Box::new(Term::Lit(Literal::Int(r))),
));
}
}
}
None
}
fn try_syn_step_reduce(ctx: &Context, term: &Term) -> Option<Term> {
if let Term::App(ctor_term, arg) = term {
if let Term::App(inner, func) = ctor_term.as_ref() {
if let Term::Global(ctor_name) = inner.as_ref() {
if ctor_name == "SApp" {
if let Some(result) = try_syn_arith_reduce(func.as_ref(), arg.as_ref()) {
return Some(result);
}
if let Term::App(lam_inner, body) = func.as_ref() {
if let Term::App(lam_ctor, _param_type) = lam_inner.as_ref() {
if let Term::Global(lam_name) = lam_ctor.as_ref() {
if lam_name == "SLam" {
return try_syn_beta_reduce(ctx, body.as_ref(), arg.as_ref());
}
}
}
}
{
let full_app = term; if let Some(kernel_term) = syntax_to_term(full_app) {
let normalized = normalize(ctx, &kernel_term);
if normalized != kernel_term {
if let Some(result_syntax) = term_to_syntax(&normalized) {
return Some(result_syntax);
}
}
}
}
if let Some(stepped_func) = try_syn_step_reduce(ctx, func.as_ref()) {
if &stepped_func != func.as_ref() {
return Some(Term::App(
Box::new(Term::App(
Box::new(Term::Global("SApp".to_string())),
Box::new(stepped_func),
)),
Box::new(arg.as_ref().clone()),
));
}
}
if let Some(stepped_arg) = try_syn_step_reduce(ctx, arg.as_ref()) {
if &stepped_arg != arg.as_ref() {
return Some(Term::App(
Box::new(Term::App(
Box::new(Term::Global("SApp".to_string())),
Box::new(func.as_ref().clone()),
)),
Box::new(stepped_arg),
));
}
}
return Some(term.clone());
}
}
}
}
Some(term.clone())
}
fn try_syn_eval_reduce(ctx: &Context, fuel: i64, term: &Term) -> Option<Term> {
if fuel <= 0 {
return Some(term.clone());
}
let stepped = try_syn_step_reduce(ctx, term)?;
if &stepped == term {
return Some(term.clone());
}
try_syn_eval_reduce(ctx, fuel - 1, &stepped)
}
#[allow(dead_code)]
fn try_syn_quote_reduce(ctx: &Context, term: &Term) -> Option<Term> {
fn sname_app(name: &str, arg: Term) -> Term {
Term::App(
Box::new(Term::App(
Box::new(Term::Global("SApp".to_string())),
Box::new(Term::App(
Box::new(Term::Global("SName".to_string())),
Box::new(Term::Lit(Literal::Text(name.to_string()))),
)),
)),
Box::new(arg),
)
}
fn slit(n: i64) -> Term {
Term::App(
Box::new(Term::Global("SLit".to_string())),
Box::new(Term::Lit(Literal::Int(n))),
)
}
fn sname(s: &str) -> Term {
Term::App(
Box::new(Term::Global("SName".to_string())),
Box::new(Term::Lit(Literal::Text(s.to_string()))),
)
}
fn sname_app2(name: &str, a: Term, b: Term) -> Term {
Term::App(
Box::new(Term::App(
Box::new(Term::Global("SApp".to_string())),
Box::new(sname_app(name, a)),
)),
Box::new(b),
)
}
if let Term::App(ctor_term, inner_arg) = term {
if let Term::Global(ctor_name) = ctor_term.as_ref() {
match ctor_name.as_str() {
"SVar" => {
if let Term::Lit(Literal::Int(n)) = inner_arg.as_ref() {
return Some(sname_app("SVar", slit(*n)));
}
}
"SGlobal" => {
if let Term::Lit(Literal::Int(n)) = inner_arg.as_ref() {
return Some(sname_app("SGlobal", slit(*n)));
}
}
"SSort" => {
let quoted_univ = quote_univ(inner_arg)?;
return Some(sname_app("SSort", quoted_univ));
}
"SLit" => {
if let Term::Lit(Literal::Int(n)) = inner_arg.as_ref() {
return Some(sname_app("SLit", slit(*n)));
}
}
"SName" => {
return Some(term.clone());
}
_ => {}
}
}
if let Term::App(inner, a) = ctor_term.as_ref() {
if let Term::Global(ctor_name) = inner.as_ref() {
match ctor_name.as_str() {
"SApp" | "SLam" | "SPi" => {
let quoted_a = try_syn_quote_reduce(ctx, a)?;
let quoted_b = try_syn_quote_reduce(ctx, inner_arg)?;
return Some(sname_app2(ctor_name, quoted_a, quoted_b));
}
_ => {}
}
}
}
}
None
}
fn quote_univ(term: &Term) -> Option<Term> {
fn sname(s: &str) -> Term {
Term::App(
Box::new(Term::Global("SName".to_string())),
Box::new(Term::Lit(Literal::Text(s.to_string()))),
)
}
fn slit(n: i64) -> Term {
Term::App(
Box::new(Term::Global("SLit".to_string())),
Box::new(Term::Lit(Literal::Int(n))),
)
}
fn sname_app(name: &str, arg: Term) -> Term {
Term::App(
Box::new(Term::App(
Box::new(Term::Global("SApp".to_string())),
Box::new(sname(name)),
)),
Box::new(arg),
)
}
if let Term::Global(name) = term {
if name == "UProp" {
return Some(sname("UProp"));
}
}
if let Term::App(ctor, arg) = term {
if let Term::Global(name) = ctor.as_ref() {
if name == "UType" {
if let Term::Lit(Literal::Int(n)) = arg.as_ref() {
return Some(sname_app("UType", slit(*n)));
}
}
}
}
None
}
fn try_syn_diag_reduce(ctx: &Context, term: &Term) -> Option<Term> {
let quoted = try_syn_quote_reduce(ctx, term)?;
try_syn_subst_reduce(ctx, "ed, 0, term)
}
fn try_concludes_reduce(ctx: &Context, deriv: &Term) -> Option<Term> {
if let Term::App(ctor_term, p) = deriv {
if let Term::Global(ctor_name) = ctor_term.as_ref() {
if ctor_name == "DAxiom" {
return Some(p.as_ref().clone());
}
if ctor_name == "DUnivIntro" {
let inner_conc = try_concludes_reduce(ctx, p)?;
let lifted = try_syn_lift_reduce(ctx, 1, 0, &inner_conc)?;
return Some(make_forall_syntax(&lifted));
}
if ctor_name == "DCompute" {
return try_dcompute_conclude(ctx, p);
}
if ctor_name == "DRingSolve" {
return try_dring_solve_conclude(ctx, p);
}
if ctor_name == "DLiaSolve" {
return try_dlia_solve_conclude(ctx, p);
}
if ctor_name == "DccSolve" {
return try_dcc_solve_conclude(ctx, p);
}
if ctor_name == "DSimpSolve" {
return try_dsimp_solve_conclude(ctx, p);
}
if ctor_name == "DOmegaSolve" {
return try_domega_solve_conclude(ctx, p);
}
if ctor_name == "DAutoSolve" {
return try_dauto_solve_conclude(ctx, p);
}
if ctor_name == "DBitblastSolve" {
return try_dbitblast_solve_conclude(ctx, p);
}
if ctor_name == "DTabulateSolve" {
return try_dtabulate_solve_conclude(ctx, p);
}
if ctor_name == "DHwAutoSolve" {
return try_dhw_auto_solve_conclude(ctx, p);
}
if ctor_name == "DInversion" {
return try_dinversion_conclude(ctx, p);
}
}
}
if let Term::App(partial1, new_goal) = deriv {
if let Term::App(partial2, old_goal) = partial1.as_ref() {
if let Term::App(ctor_term, eq_proof) = partial2.as_ref() {
if let Term::Global(ctor_name) = ctor_term.as_ref() {
if ctor_name == "DRewrite" {
return try_drewrite_conclude(ctx, eq_proof, old_goal, new_goal);
}
}
}
}
}
if let Term::App(partial1, cases) = deriv {
if let Term::App(partial2, motive) = partial1.as_ref() {
if let Term::App(ctor_term, ind_type) = partial2.as_ref() {
if let Term::Global(ctor_name) = ctor_term.as_ref() {
if ctor_name == "DDestruct" {
return try_ddestruct_conclude(ctx, ind_type, motive, cases);
}
}
}
}
}
if let Term::App(partial1, new_goal) = deriv {
if let Term::App(partial2, old_goal) = partial1.as_ref() {
if let Term::App(partial3, hyp_proof) = partial2.as_ref() {
if let Term::App(ctor_term, hyp_name) = partial3.as_ref() {
if let Term::Global(ctor_name) = ctor_term.as_ref() {
if ctor_name == "DApply" {
return try_dapply_conclude(ctx, hyp_name, hyp_proof, old_goal, new_goal);
}
}
}
}
}
}
if let Term::App(partial, a) = deriv {
if let Term::App(ctor_term, t) = partial.as_ref() {
if let Term::Global(ctor_name) = ctor_term.as_ref() {
if ctor_name == "DRefl" {
return Some(make_eq_syntax(t.as_ref(), a.as_ref()));
}
if ctor_name == "DCong" {
return try_dcong_conclude(ctx, t, a);
}
}
}
}
if let Term::App(partial, d_ant) = deriv {
if let Term::App(ctor_term, d_impl) = partial.as_ref() {
if let Term::Global(ctor_name) = ctor_term.as_ref() {
if ctor_name == "DModusPonens" {
let impl_conc = try_concludes_reduce(ctx, d_impl)?;
let ant_conc = try_concludes_reduce(ctx, d_ant)?;
if let Some((a, b)) = extract_implication(&impl_conc) {
if syntax_equal(&ant_conc, &a) {
return Some(b);
}
}
return Some(make_sname_error());
}
if ctor_name == "DUnivElim" {
let conc = try_concludes_reduce(ctx, d_impl)?;
if let Some(body) = extract_forall_body(&conc) {
return try_syn_subst_reduce(ctx, d_ant, 0, &body);
}
return Some(make_sname_error());
}
}
}
}
if let Term::App(partial1, step) = deriv {
if let Term::App(partial2, base) = partial1.as_ref() {
if let Term::App(ctor_term, motive) = partial2.as_ref() {
if let Term::Global(ctor_name) = ctor_term.as_ref() {
if ctor_name == "DInduction" {
return try_dinduction_reduce(ctx, motive, base, step);
}
}
}
}
}
if let Term::App(partial1, cases) = deriv {
if let Term::App(partial2, motive) = partial1.as_ref() {
if let Term::App(ctor_term, ind_type) = partial2.as_ref() {
if let Term::Global(ctor_name) = ctor_term.as_ref() {
if ctor_name == "DElim" {
return try_delim_conclude(ctx, ind_type, motive, cases);
}
}
}
}
}
None
}
fn extract_implication(term: &Term) -> Option<(Term, Term)> {
if let Term::App(outer, b) = term {
if let Term::App(sapp_outer, x) = outer.as_ref() {
if let Term::Global(ctor) = sapp_outer.as_ref() {
if ctor == "SApp" {
if let Term::App(inner, a) = x.as_ref() {
if let Term::App(sapp_inner, sname_implies) = inner.as_ref() {
if let Term::Global(ctor2) = sapp_inner.as_ref() {
if ctor2 == "SApp" {
if let Term::App(sname, text) = sname_implies.as_ref() {
if let Term::Global(sname_ctor) = sname.as_ref() {
if sname_ctor == "SName" {
if let Term::Lit(Literal::Text(s)) = text.as_ref() {
if s == "Implies" || s == "implies" {
return Some((
a.as_ref().clone(),
b.as_ref().clone(),
));
}
}
}
}
}
}
}
}
}
}
}
}
}
None
}
fn extract_implications(term: &Term) -> Option<(Vec<Term>, Term)> {
let mut hyps = Vec::new();
let mut current = term.clone();
while let Some((hyp, rest)) = extract_implication(¤t) {
hyps.push(hyp);
current = rest;
}
if hyps.is_empty() {
None
} else {
Some((hyps, current))
}
}
fn extract_forall_body(term: &Term) -> Option<Term> {
if let Term::App(outer, lam) = term {
if let Term::App(sapp_outer, x) = outer.as_ref() {
if let Term::Global(ctor) = sapp_outer.as_ref() {
if ctor == "SApp" {
if let Term::App(sname, text) = x.as_ref() {
if let Term::Global(sname_ctor) = sname.as_ref() {
if sname_ctor == "SName" {
if let Term::Lit(Literal::Text(s)) = text.as_ref() {
if s == "Forall" {
return extract_slam_body(lam);
}
}
}
}
}
if let Term::App(inner, _t) = x.as_ref() {
if let Term::App(sapp_inner, sname_forall) = inner.as_ref() {
if let Term::Global(ctor2) = sapp_inner.as_ref() {
if ctor2 == "SApp" {
if let Term::App(sname, text) = sname_forall.as_ref() {
if let Term::Global(sname_ctor) = sname.as_ref() {
if sname_ctor == "SName" {
if let Term::Lit(Literal::Text(s)) = text.as_ref() {
if s == "Forall" {
return extract_slam_body(lam);
}
}
}
}
}
}
}
}
}
}
}
}
}
None
}
fn extract_slam_body(term: &Term) -> Option<Term> {
if let Term::App(inner, body) = term {
if let Term::App(slam, _t) = inner.as_ref() {
if let Term::Global(name) = slam.as_ref() {
if name == "SLam" {
return Some(body.as_ref().clone());
}
}
}
}
None
}
fn syntax_equal(a: &Term, b: &Term) -> bool {
a == b
}
fn make_sname_error() -> Term {
Term::App(
Box::new(Term::Global("SName".to_string())),
Box::new(Term::Lit(Literal::Text("Error".to_string()))),
)
}
fn make_sname(s: &str) -> Term {
Term::App(
Box::new(Term::Global("SName".to_string())),
Box::new(Term::Lit(Literal::Text(s.to_string()))),
)
}
fn make_slit(n: i64) -> Term {
Term::App(
Box::new(Term::Global("SLit".to_string())),
Box::new(Term::Lit(Literal::Int(n))),
)
}
fn make_sapp(f: Term, x: Term) -> Term {
Term::App(
Box::new(Term::App(
Box::new(Term::Global("SApp".to_string())),
Box::new(f),
)),
Box::new(x),
)
}
fn make_spi(a: Term, b: Term) -> Term {
Term::App(
Box::new(Term::App(
Box::new(Term::Global("SPi".to_string())),
Box::new(a),
)),
Box::new(b),
)
}
fn make_slam(a: Term, b: Term) -> Term {
Term::App(
Box::new(Term::App(
Box::new(Term::Global("SLam".to_string())),
Box::new(a),
)),
Box::new(b),
)
}
fn make_ssort(u: Term) -> Term {
Term::App(
Box::new(Term::Global("SSort".to_string())),
Box::new(u),
)
}
fn make_svar(n: i64) -> Term {
Term::App(
Box::new(Term::Global("SVar".to_string())),
Box::new(Term::Lit(Literal::Int(n))),
)
}
fn term_to_syntax(term: &Term) -> Option<Term> {
match term {
Term::Global(name) => Some(make_sname(name)),
Term::Var(name) => {
Some(make_sname(name))
}
Term::App(f, x) => {
let sf = term_to_syntax(f)?;
let sx = term_to_syntax(x)?;
Some(make_sapp(sf, sx))
}
Term::Pi { param_type, body_type, .. } => {
let sp = term_to_syntax(param_type)?;
let sb = term_to_syntax(body_type)?;
Some(make_spi(sp, sb))
}
Term::Lambda { param_type, body, .. } => {
let sp = term_to_syntax(param_type)?;
let sb = term_to_syntax(body)?;
Some(make_slam(sp, sb))
}
Term::Sort(Universe::Type(n)) => {
let u = Term::App(
Box::new(Term::Global("UType".to_string())),
Box::new(Term::Lit(Literal::Int(*n as i64))),
);
Some(make_ssort(u))
}
Term::Sort(Universe::Prop) => {
let u = Term::Global("UProp".to_string());
Some(make_ssort(u))
}
Term::Lit(Literal::Int(n)) => Some(make_slit(*n)),
Term::Lit(Literal::Text(s)) => Some(make_sname(s)),
Term::Lit(Literal::Float(_))
| Term::Lit(Literal::Duration(_))
| Term::Lit(Literal::Date(_))
| Term::Lit(Literal::Moment(_)) => None,
Term::Match { .. } | Term::Fix { .. } | Term::Hole => None,
}
}
fn make_hint_derivation(hint_name: &str, goal: &Term) -> Term {
let hint_marker = make_sapp(make_sname("Hint"), make_sname(hint_name));
Term::App(
Box::new(Term::Global("DAutoSolve".to_string())),
Box::new(goal.clone()),
)
}
fn try_apply_hint(ctx: &Context, hint_name: &str, hint_type: &Term, goal: &Term) -> Option<Term> {
let hint_syntax = term_to_syntax(hint_type)?;
let norm_hint = normalize(ctx, &hint_syntax);
let norm_goal = normalize(ctx, goal);
if syntax_equal(&norm_hint, &norm_goal) {
return Some(make_hint_derivation(hint_name, goal));
}
if let Term::App(outer, q) = &hint_syntax {
if let Term::App(pi_ctor, p) = outer.as_ref() {
if let Term::Global(name) = pi_ctor.as_ref() {
if name == "SPi" {
let norm_q = normalize(ctx, q);
if syntax_equal(&norm_q, &norm_goal) {
}
}
}
}
}
None
}
fn make_forall_syntax(body: &Term) -> Term {
let type0 = Term::App(
Box::new(Term::Global("SSort".to_string())),
Box::new(Term::App(
Box::new(Term::Global("UType".to_string())),
Box::new(Term::Lit(Literal::Int(0))),
)),
);
let slam = Term::App(
Box::new(Term::App(
Box::new(Term::Global("SLam".to_string())),
Box::new(type0.clone()),
)),
Box::new(body.clone()),
);
Term::App(
Box::new(Term::App(
Box::new(Term::Global("SApp".to_string())),
Box::new(Term::App(
Box::new(Term::App(
Box::new(Term::Global("SApp".to_string())),
Box::new(Term::App(
Box::new(Term::Global("SName".to_string())),
Box::new(Term::Lit(Literal::Text("Forall".to_string()))),
)),
)),
Box::new(type0),
)),
)),
Box::new(slam),
)
}
fn make_eq_syntax(type_s: &Term, term: &Term) -> Term {
let eq_name = Term::App(
Box::new(Term::Global("SName".to_string())),
Box::new(Term::Lit(Literal::Text("Eq".to_string()))),
);
let app1 = Term::App(
Box::new(Term::App(
Box::new(Term::Global("SApp".to_string())),
Box::new(eq_name),
)),
Box::new(type_s.clone()),
);
let app2 = Term::App(
Box::new(Term::App(
Box::new(Term::Global("SApp".to_string())),
Box::new(app1),
)),
Box::new(term.clone()),
);
Term::App(
Box::new(Term::App(
Box::new(Term::Global("SApp".to_string())),
Box::new(app2),
)),
Box::new(term.clone()),
)
}
fn extract_eq(term: &Term) -> Option<(Term, Term, Term)> {
if let Term::App(outer, b) = term {
if let Term::App(sapp_outer, x) = outer.as_ref() {
if let Term::Global(ctor) = sapp_outer.as_ref() {
if ctor == "SApp" {
if let Term::App(inner, a) = x.as_ref() {
if let Term::App(sapp_inner, y) = inner.as_ref() {
if let Term::Global(ctor2) = sapp_inner.as_ref() {
if ctor2 == "SApp" {
if let Term::App(inner2, t) = y.as_ref() {
if let Term::App(sapp_inner2, sname_eq) = inner2.as_ref() {
if let Term::Global(ctor3) = sapp_inner2.as_ref() {
if ctor3 == "SApp" {
if let Term::App(sname, text) = sname_eq.as_ref()
{
if let Term::Global(sname_ctor) =
sname.as_ref()
{
if sname_ctor == "SName" {
if let Term::Lit(Literal::Text(s)) =
text.as_ref()
{
if s == "Eq" {
return Some((
t.as_ref().clone(),
a.as_ref().clone(),
b.as_ref().clone(),
));
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
None
}
fn make_drefl(type_s: &Term, term: &Term) -> Term {
let drefl = Term::Global("DRefl".to_string());
let app1 = Term::App(Box::new(drefl), Box::new(type_s.clone()));
Term::App(Box::new(app1), Box::new(term.clone()))
}
fn make_error_derivation() -> Term {
let daxiom = Term::Global("DAxiom".to_string());
let error = make_sname_error();
Term::App(Box::new(daxiom), Box::new(error))
}
fn make_daxiom(goal: &Term) -> Term {
let daxiom = Term::Global("DAxiom".to_string());
Term::App(Box::new(daxiom), Box::new(goal.clone()))
}
fn try_try_refl_reduce(ctx: &Context, goal: &Term) -> Option<Term> {
let norm_goal = normalize(ctx, goal);
if let Some((type_s, left, right)) = extract_eq(&norm_goal) {
if syntax_equal(&left, &right) {
return Some(make_drefl(&type_s, &left));
}
}
Some(make_error_derivation())
}
use crate::ring;
use crate::lia;
use crate::cc;
use crate::simp;
fn try_try_ring_reduce(ctx: &Context, goal: &Term) -> Option<Term> {
let norm_goal = normalize(ctx, goal);
if let Some((type_s, left, right)) = extract_eq(&norm_goal) {
if !is_ring_type(&type_s) {
return Some(make_error_derivation());
}
let poly_left = match ring::reify(&left) {
Ok(p) => p,
Err(_) => return Some(make_error_derivation()),
};
let poly_right = match ring::reify(&right) {
Ok(p) => p,
Err(_) => return Some(make_error_derivation()),
};
if poly_left.canonical_eq(&poly_right) {
return Some(Term::App(
Box::new(Term::Global("DRingSolve".to_string())),
Box::new(norm_goal),
));
}
}
Some(make_error_derivation())
}
fn try_dring_solve_conclude(ctx: &Context, goal: &Term) -> Option<Term> {
let norm_goal = normalize(ctx, goal);
if let Some((type_s, left, right)) = extract_eq(&norm_goal) {
if !is_ring_type(&type_s) {
return Some(make_sname_error());
}
let poly_left = match ring::reify(&left) {
Ok(p) => p,
Err(_) => return Some(make_sname_error()),
};
let poly_right = match ring::reify(&right) {
Ok(p) => p,
Err(_) => return Some(make_sname_error()),
};
if poly_left.canonical_eq(&poly_right) {
return Some(norm_goal);
}
}
Some(make_sname_error())
}
fn is_ring_type(type_term: &Term) -> bool {
if let Some(name) = extract_sname_from_syntax(type_term) {
return name == "Int" || name == "Nat";
}
false
}
fn extract_sname_from_syntax(term: &Term) -> Option<String> {
if let Term::App(ctor, arg) = term {
if let Term::Global(name) = ctor.as_ref() {
if name == "SName" {
if let Term::Lit(Literal::Text(s)) = arg.as_ref() {
return Some(s.clone());
}
}
}
}
None
}
fn try_try_lia_reduce(ctx: &Context, goal: &Term) -> Option<Term> {
let norm_goal = normalize(ctx, goal);
if let Some((rel, lhs_term, rhs_term)) = lia::extract_comparison(&norm_goal) {
let lhs = match lia::reify_linear(&lhs_term) {
Ok(l) => l,
Err(_) => return Some(make_error_derivation()),
};
let rhs = match lia::reify_linear(&rhs_term) {
Ok(r) => r,
Err(_) => return Some(make_error_derivation()),
};
if let Some(negated) = lia::goal_to_negated_constraint(&rel, &lhs, &rhs) {
if lia::fourier_motzkin_unsat(&[negated]) {
return Some(Term::App(
Box::new(Term::Global("DLiaSolve".to_string())),
Box::new(norm_goal),
));
}
}
}
Some(make_error_derivation())
}
fn try_dlia_solve_conclude(ctx: &Context, goal: &Term) -> Option<Term> {
let norm_goal = normalize(ctx, goal);
if let Some((rel, lhs_term, rhs_term)) = lia::extract_comparison(&norm_goal) {
let lhs = match lia::reify_linear(&lhs_term) {
Ok(l) => l,
Err(_) => return Some(make_sname_error()),
};
let rhs = match lia::reify_linear(&rhs_term) {
Ok(r) => r,
Err(_) => return Some(make_sname_error()),
};
if let Some(negated) = lia::goal_to_negated_constraint(&rel, &lhs, &rhs) {
if lia::fourier_motzkin_unsat(&[negated]) {
return Some(norm_goal);
}
}
}
Some(make_sname_error())
}
fn try_try_cc_reduce(ctx: &Context, goal: &Term) -> Option<Term> {
let norm_goal = normalize(ctx, goal);
if cc::check_goal(&norm_goal) {
return Some(Term::App(
Box::new(Term::Global("DccSolve".to_string())),
Box::new(norm_goal),
));
}
Some(make_error_derivation())
}
fn try_dcc_solve_conclude(ctx: &Context, goal: &Term) -> Option<Term> {
let norm_goal = normalize(ctx, goal);
if cc::check_goal(&norm_goal) {
return Some(norm_goal);
}
Some(make_sname_error())
}
fn try_try_simp_reduce(ctx: &Context, goal: &Term) -> Option<Term> {
let norm_goal = normalize(ctx, goal);
if simp::check_goal(&norm_goal) {
return Some(Term::App(
Box::new(Term::Global("DSimpSolve".to_string())),
Box::new(norm_goal),
));
}
Some(make_error_derivation())
}
fn try_dsimp_solve_conclude(ctx: &Context, goal: &Term) -> Option<Term> {
let norm_goal = normalize(ctx, goal);
if simp::check_goal(&norm_goal) {
return Some(norm_goal);
}
Some(make_sname_error())
}
fn try_try_omega_reduce(ctx: &Context, goal: &Term) -> Option<Term> {
let norm_goal = normalize(ctx, goal);
if let Some((hyps, conclusion)) = extract_implications(&norm_goal) {
let mut constraints = Vec::new();
for hyp in &hyps {
if let Some((rel, lhs_term, rhs_term)) = omega::extract_comparison(hyp) {
if let (Some(lhs), Some(rhs)) =
(omega::reify_int_linear(&lhs_term), omega::reify_int_linear(&rhs_term))
{
match rel.as_str() {
"Lt" | "lt" => {
let mut expr = lhs.sub(&rhs);
expr.constant += 1;
constraints.push(omega::IntConstraint { expr, strict: false });
}
"Le" | "le" => {
constraints.push(omega::IntConstraint {
expr: lhs.sub(&rhs),
strict: false,
});
}
"Gt" | "gt" => {
let mut expr = rhs.sub(&lhs);
expr.constant += 1;
constraints.push(omega::IntConstraint { expr, strict: false });
}
"Ge" | "ge" => {
constraints.push(omega::IntConstraint {
expr: rhs.sub(&lhs),
strict: false,
});
}
_ => {}
}
}
}
}
if let Some((rel, lhs_term, rhs_term)) = omega::extract_comparison(&conclusion) {
if let (Some(lhs), Some(rhs)) =
(omega::reify_int_linear(&lhs_term), omega::reify_int_linear(&rhs_term))
{
if let Some(neg_constraint) = omega::goal_to_negated_constraint(&rel, &lhs, &rhs) {
let mut all_constraints = constraints;
all_constraints.push(neg_constraint);
if omega::omega_unsat(&all_constraints) {
return Some(Term::App(
Box::new(Term::Global("DOmegaSolve".to_string())),
Box::new(norm_goal),
));
}
}
}
}
return Some(make_error_derivation());
}
if let Some((rel, lhs_term, rhs_term)) = omega::extract_comparison(&norm_goal) {
if let (Some(lhs), Some(rhs)) =
(omega::reify_int_linear(&lhs_term), omega::reify_int_linear(&rhs_term))
{
if let Some(constraint) = omega::goal_to_negated_constraint(&rel, &lhs, &rhs) {
if omega::omega_unsat(&[constraint]) {
return Some(Term::App(
Box::new(Term::Global("DOmegaSolve".to_string())),
Box::new(norm_goal),
));
}
}
}
}
Some(make_error_derivation())
}
fn try_domega_solve_conclude(ctx: &Context, goal: &Term) -> Option<Term> {
let norm_goal = normalize(ctx, goal);
if let Some((hyps, conclusion)) = extract_implications(&norm_goal) {
let mut constraints = Vec::new();
for hyp in &hyps {
if let Some((rel, lhs_term, rhs_term)) = omega::extract_comparison(hyp) {
if let (Some(lhs), Some(rhs)) =
(omega::reify_int_linear(&lhs_term), omega::reify_int_linear(&rhs_term))
{
match rel.as_str() {
"Lt" | "lt" => {
let mut expr = lhs.sub(&rhs);
expr.constant += 1;
constraints.push(omega::IntConstraint { expr, strict: false });
}
"Le" | "le" => {
constraints.push(omega::IntConstraint {
expr: lhs.sub(&rhs),
strict: false,
});
}
"Gt" | "gt" => {
let mut expr = rhs.sub(&lhs);
expr.constant += 1;
constraints.push(omega::IntConstraint { expr, strict: false });
}
"Ge" | "ge" => {
constraints.push(omega::IntConstraint {
expr: rhs.sub(&lhs),
strict: false,
});
}
_ => {}
}
}
}
}
if let Some((rel, lhs_term, rhs_term)) = omega::extract_comparison(&conclusion) {
if let (Some(lhs), Some(rhs)) =
(omega::reify_int_linear(&lhs_term), omega::reify_int_linear(&rhs_term))
{
if let Some(neg_constraint) = omega::goal_to_negated_constraint(&rel, &lhs, &rhs) {
let mut all_constraints = constraints;
all_constraints.push(neg_constraint);
if omega::omega_unsat(&all_constraints) {
return Some(norm_goal);
}
}
}
}
return Some(make_sname_error());
}
if let Some((rel, lhs_term, rhs_term)) = omega::extract_comparison(&norm_goal) {
if let (Some(lhs), Some(rhs)) =
(omega::reify_int_linear(&lhs_term), omega::reify_int_linear(&rhs_term))
{
if let Some(constraint) = omega::goal_to_negated_constraint(&rel, &lhs, &rhs) {
if omega::omega_unsat(&[constraint]) {
return Some(norm_goal);
}
}
}
}
Some(make_sname_error())
}
fn is_error_derivation(term: &Term) -> bool {
if let Term::App(ctor, arg) = term {
if let Term::Global(name) = ctor.as_ref() {
if name == "DAxiom" {
if let Term::App(sname, inner) = arg.as_ref() {
if let Term::Global(sn) = sname.as_ref() {
if sn == "SName" {
if let Term::Lit(Literal::Text(s)) = inner.as_ref() {
return s == "Error";
}
}
if sn == "SApp" {
return true; }
}
}
if let Term::Global(sn) = arg.as_ref() {
return sn == "Error";
}
return true; }
}
}
false
}
fn try_try_bitblast_reduce(ctx: &Context, goal: &Term) -> Option<Term> {
let norm_goal = normalize(ctx, goal);
if let Some((type_s, left, right)) = extract_eq_syntax_parts(&norm_goal) {
if !is_bit_type(&type_s) {
return Some(make_error_derivation());
}
let fuel = 1000;
let left_eval = try_syn_eval_reduce(ctx, fuel, &left)?;
let right_eval = try_syn_eval_reduce(ctx, fuel, &right)?;
if syntax_equal(&left_eval, &right_eval) {
return Some(Term::App(
Box::new(Term::Global("DBitblastSolve".to_string())),
Box::new(norm_goal),
));
}
}
Some(make_error_derivation())
}
fn syntax_to_term(syntax: &Term) -> Option<Term> {
if let Term::App(ctor, inner) = syntax {
if let Term::Global(name) = ctor.as_ref() {
match name.as_str() {
"SName" => {
if let Term::Lit(Literal::Text(s)) = inner.as_ref() {
return Some(Term::Global(s.clone()));
}
}
"SLit" => {
if let Term::Lit(Literal::Int(n)) = inner.as_ref() {
return Some(Term::Lit(Literal::Int(*n)));
}
}
"SVar" => {
if let Term::Lit(Literal::Int(n)) = inner.as_ref() {
return Some(Term::Var(format!("v{}", n)));
}
}
"SGlobal" => {
if let Term::Lit(Literal::Int(n)) = inner.as_ref() {
return Some(Term::Var(format!("g{}", n)));
}
}
_ => {}
}
}
}
if let Term::App(outer, second) = syntax {
if let Term::App(sctor, first) = outer.as_ref() {
if let Term::Global(name) = sctor.as_ref() {
match name.as_str() {
"SApp" => {
let f = syntax_to_term(first)?;
let x = syntax_to_term(second)?;
return Some(Term::App(Box::new(f), Box::new(x)));
}
"SLam" => {
let param_type = syntax_to_term(first)?;
let body = syntax_to_term(second)?;
return Some(Term::Lambda {
param: "_".to_string(),
param_type: Box::new(param_type),
body: Box::new(body),
});
}
"SPi" => {
let param_type = syntax_to_term(first)?;
let body_type = syntax_to_term(second)?;
return Some(Term::Pi {
param: "_".to_string(),
param_type: Box::new(param_type),
body_type: Box::new(body_type),
});
}
_ => {}
}
}
}
}
if let Term::App(outer, third) = syntax {
if let Term::App(mid, second) = outer.as_ref() {
if let Term::App(inner, first) = mid.as_ref() {
if let Term::Global(name) = inner.as_ref() {
if name == "SMatch" {
let disc = syntax_to_term(first)?;
let motive = syntax_to_term(second)?;
let _ = third;
return Some(Term::Match {
discriminant: Box::new(disc),
motive: Box::new(motive),
cases: vec![],
});
}
}
}
}
}
None
}
fn is_bit_type(term: &Term) -> bool {
if let Term::App(ctor, inner) = term {
if let Term::Global(name) = ctor.as_ref() {
if name == "SName" {
if let Term::Lit(Literal::Text(s)) = inner.as_ref() {
return s == "Bit";
}
}
}
}
false
}
fn syntax_subst_var(term: &Term, idx: i64, replacement: &Term) -> Term {
if let Term::App(ctor, inner) = term {
if let Term::Global(name) = ctor.as_ref() {
if name == "SVar" {
if let Term::Lit(Literal::Int(n)) = inner.as_ref() {
if *n == idx {
return replacement.clone();
} else if *n > idx {
return make_svar(*n - 1);
} else {
return term.clone();
}
}
}
}
}
if let Term::App(outer, second) = term {
if let Term::App(sctor, first) = outer.as_ref() {
if let Term::Global(name) = sctor.as_ref() {
match name.as_str() {
"SApp" => {
let f = syntax_subst_var(first, idx, replacement);
let x = syntax_subst_var(second, idx, replacement);
return make_sapp(f, x);
}
"SPi" => {
let param_t = syntax_subst_var(first, idx, replacement);
let body = syntax_subst_var(second, idx + 1, replacement);
return make_spi(param_t, body);
}
"SLam" => {
let param_t = syntax_subst_var(first, idx, replacement);
let body = syntax_subst_var(second, idx + 1, replacement);
return make_slam(param_t, body);
}
_ => {}
}
}
}
}
term.clone()
}
fn try_try_tabulate_reduce(ctx: &Context, goal: &Term) -> Option<Term> {
let norm_goal = normalize(ctx, goal);
if let Some((param_type, body)) = extract_spi(&norm_goal) {
if !is_bit_type(¶m_type) {
return Some(make_error_derivation());
}
let body_b0 = syntax_subst_var(&body, 0, &make_sname("B0"));
let body_b1 = syntax_subst_var(&body, 0, &make_sname("B1"));
let ok_b0 = tabulate_verify_case(ctx, &body_b0);
let ok_b1 = tabulate_verify_case(ctx, &body_b1);
if ok_b0 && ok_b1 {
return Some(Term::App(
Box::new(Term::Global("DTabulateSolve".to_string())),
Box::new(norm_goal),
));
}
return Some(make_error_derivation());
}
Some(make_error_derivation())
}
fn tabulate_verify_case(ctx: &Context, case: &Term) -> bool {
let norm = normalize(ctx, case);
if let Some((param_type, _)) = extract_spi(&norm) {
if is_bit_type(¶m_type) {
if let Some(result) = try_try_tabulate_reduce(ctx, &norm) {
return !is_error_derivation(&result);
}
}
return false;
}
if let Some((type_s, left, right)) = extract_eq_syntax_parts(&norm) {
let fuel = 1000;
if let (Some(left_eval), Some(right_eval)) = (
try_syn_eval_reduce(ctx, fuel, &left),
try_syn_eval_reduce(ctx, fuel, &right),
) {
return syntax_equal(&left_eval, &right_eval);
}
if let Some(full_eval) = try_syn_eval_reduce(ctx, fuel, &norm) {
if is_sname_with_value(&full_eval, "True") {
return true;
}
}
return false;
}
let fuel = 1000;
if let Some(eval) = try_syn_eval_reduce(ctx, fuel, &norm) {
if is_sname_with_value(&eval, "True") {
return true;
}
}
false
}
fn is_sname_with_value(term: &Term, value: &str) -> bool {
if let Term::App(ctor, inner) = term {
if let Term::Global(name) = ctor.as_ref() {
if name == "SName" {
if let Term::Lit(Literal::Text(s)) = inner.as_ref() {
return s == value;
}
}
}
}
false
}
fn try_dbitblast_solve_conclude(ctx: &Context, goal: &Term) -> Option<Term> {
let norm_goal = normalize(ctx, goal);
if let Some((type_s, left, right)) = extract_eq_syntax_parts(&norm_goal) {
if is_bit_type(&type_s) {
let fuel = 1000;
let left_eval = try_syn_eval_reduce(ctx, fuel, &left)?;
let right_eval = try_syn_eval_reduce(ctx, fuel, &right)?;
if syntax_equal(&left_eval, &right_eval) {
return Some(norm_goal);
}
}
}
None
}
fn try_dtabulate_solve_conclude(ctx: &Context, goal: &Term) -> Option<Term> {
let norm_goal = normalize(ctx, goal);
if let Some((param_type, _)) = extract_spi(&norm_goal) {
if is_bit_type(¶m_type) {
if let Some(result) = try_try_tabulate_reduce(ctx, &norm_goal) {
if !is_error_derivation(&result) {
return Some(norm_goal);
}
}
}
}
None
}
fn try_dhw_auto_solve_conclude(ctx: &Context, goal: &Term) -> Option<Term> {
if let Some(result) = try_dbitblast_solve_conclude(ctx, goal) {
return Some(result);
}
try_dauto_solve_conclude(ctx, goal)
}
fn try_try_hw_auto_reduce(ctx: &Context, goal: &Term) -> Option<Term> {
let norm_goal = normalize(ctx, goal);
if let Some(result) = try_try_bitblast_reduce(ctx, &norm_goal) {
if !is_error_derivation(&result) {
return Some(result);
}
}
if let Some(result) = try_try_tabulate_reduce(ctx, &norm_goal) {
if !is_error_derivation(&result) {
return Some(result);
}
}
try_try_auto_reduce(ctx, &norm_goal)
}
fn try_try_auto_reduce(ctx: &Context, goal: &Term) -> Option<Term> {
let norm_goal = normalize(ctx, goal);
if let Term::App(ctor, inner) = &norm_goal {
if let Term::Global(name) = ctor.as_ref() {
if name == "SName" {
if let Term::Lit(Literal::Text(s)) = inner.as_ref() {
if s == "True" {
return Some(Term::App(
Box::new(Term::Global("DAutoSolve".to_string())),
Box::new(norm_goal),
));
}
if s == "False" {
return Some(make_error_derivation());
}
}
}
}
}
if let Some(result) = try_try_simp_reduce(ctx, &norm_goal) {
if !is_error_derivation(&result) {
return Some(result);
}
}
if let Some(result) = try_try_ring_reduce(ctx, &norm_goal) {
if !is_error_derivation(&result) {
return Some(result);
}
}
if let Some(result) = try_try_cc_reduce(ctx, &norm_goal) {
if !is_error_derivation(&result) {
return Some(result);
}
}
if let Some(result) = try_try_omega_reduce(ctx, &norm_goal) {
if !is_error_derivation(&result) {
return Some(result);
}
}
if let Some(result) = try_try_lia_reduce(ctx, &norm_goal) {
if !is_error_derivation(&result) {
return Some(result);
}
}
for hint_name in ctx.get_hints() {
if let Some(hint_type) = ctx.get_global(hint_name) {
if let Some(result) = try_apply_hint(ctx, hint_name, hint_type, &norm_goal) {
return Some(result);
}
}
}
Some(make_error_derivation())
}
fn try_dauto_solve_conclude(ctx: &Context, goal: &Term) -> Option<Term> {
let norm_goal = normalize(ctx, goal);
if let Term::App(ctor, inner) = &norm_goal {
if let Term::Global(name) = ctor.as_ref() {
if name == "SName" {
if let Term::Lit(Literal::Text(s)) = inner.as_ref() {
if s == "True" {
return Some(norm_goal.clone());
}
}
}
}
}
if let Some(result) = try_try_simp_reduce(ctx, &norm_goal) {
if !is_error_derivation(&result) {
return Some(norm_goal.clone());
}
}
if let Some(result) = try_try_ring_reduce(ctx, &norm_goal) {
if !is_error_derivation(&result) {
return Some(norm_goal.clone());
}
}
if let Some(result) = try_try_cc_reduce(ctx, &norm_goal) {
if !is_error_derivation(&result) {
return Some(norm_goal.clone());
}
}
if let Some(result) = try_try_omega_reduce(ctx, &norm_goal) {
if !is_error_derivation(&result) {
return Some(norm_goal.clone());
}
}
if let Some(result) = try_try_lia_reduce(ctx, &norm_goal) {
if !is_error_derivation(&result) {
return Some(norm_goal.clone());
}
}
for hint_name in ctx.get_hints() {
if let Some(hint_type) = ctx.get_global(hint_name) {
if try_apply_hint(ctx, hint_name, hint_type, &norm_goal).is_some() {
return Some(norm_goal);
}
}
}
Some(make_sname_error())
}
fn try_induction_num_cases_reduce(ctx: &Context, ind_type: &Term) -> Option<Term> {
let ind_name = match extract_inductive_name_from_syntax(ind_type) {
Some(name) => name,
None => {
return Some(Term::Global("Zero".to_string()));
}
};
let constructors = ctx.get_constructors(&ind_name);
let num_ctors = constructors.len();
let mut result = Term::Global("Zero".to_string());
for _ in 0..num_ctors {
result = Term::App(
Box::new(Term::Global("Succ".to_string())),
Box::new(result),
);
}
Some(result)
}
fn try_induction_base_goal_reduce(
ctx: &Context,
ind_type: &Term,
motive: &Term,
) -> Option<Term> {
let ind_name = match extract_inductive_name_from_syntax(ind_type) {
Some(name) => name,
None => return Some(make_sname_error()),
};
let constructors = ctx.get_constructors(&ind_name);
if constructors.is_empty() {
return Some(make_sname_error());
}
let motive_body = match extract_slam_body(motive) {
Some(body) => body,
None => return Some(make_sname_error()),
};
let (ctor_name, _) = constructors[0];
build_case_expected(ctx, ctor_name, &constructors, &motive_body, ind_type)
}
fn try_induction_step_goal_reduce(
ctx: &Context,
ind_type: &Term,
motive: &Term,
ctor_idx: &Term,
) -> Option<Term> {
let ind_name = match extract_inductive_name_from_syntax(ind_type) {
Some(name) => name,
None => return Some(make_sname_error()),
};
let constructors = ctx.get_constructors(&ind_name);
if constructors.is_empty() {
return Some(make_sname_error());
}
let idx = nat_to_usize(ctor_idx)?;
if idx >= constructors.len() {
return Some(make_sname_error());
}
let motive_body = match extract_slam_body(motive) {
Some(body) => body,
None => return Some(make_sname_error()),
};
let (ctor_name, _) = constructors[idx];
build_case_expected(ctx, ctor_name, &constructors, &motive_body, ind_type)
}
fn nat_to_usize(term: &Term) -> Option<usize> {
match term {
Term::Global(name) if name == "Zero" => Some(0),
Term::App(succ, inner) => {
if let Term::Global(name) = succ.as_ref() {
if name == "Succ" {
return nat_to_usize(inner).map(|n| n + 1);
}
}
None
}
_ => None,
}
}
fn try_try_induction_reduce(
ctx: &Context,
ind_type: &Term,
motive: &Term,
cases: &Term,
) -> Option<Term> {
let ind_name = match extract_inductive_name_from_syntax(ind_type) {
Some(name) => name,
None => return Some(make_error_derivation()),
};
let constructors = ctx.get_constructors(&ind_name);
if constructors.is_empty() {
return Some(make_error_derivation());
}
let case_proofs = match extract_case_proofs(cases) {
Some(proofs) => proofs,
None => return Some(make_error_derivation()),
};
if case_proofs.len() != constructors.len() {
return Some(make_error_derivation());
}
Some(Term::App(
Box::new(Term::App(
Box::new(Term::App(
Box::new(Term::Global("DElim".to_string())),
Box::new(ind_type.clone()),
)),
Box::new(motive.clone()),
)),
Box::new(cases.clone()),
))
}
fn try_dinduction_reduce(
ctx: &Context,
motive: &Term,
base: &Term,
step: &Term,
) -> Option<Term> {
let norm_motive = normalize(ctx, motive);
let norm_base = normalize(ctx, base);
let norm_step = normalize(ctx, step);
let motive_body = match extract_slam_body(&norm_motive) {
Some(body) => body,
None => return Some(make_sname_error()),
};
let zero = make_sname("Zero");
let expected_base = match try_syn_subst_reduce(ctx, &zero, 0, &motive_body) {
Some(b) => b,
None => return Some(make_sname_error()),
};
let base_conc = match try_concludes_reduce(ctx, &norm_base) {
Some(c) => c,
None => return Some(make_sname_error()),
};
if !syntax_equal(&base_conc, &expected_base) {
return Some(make_sname_error());
}
let expected_step = match build_induction_step_formula(ctx, &motive_body) {
Some(s) => s,
None => return Some(make_sname_error()),
};
let step_conc = match try_concludes_reduce(ctx, &norm_step) {
Some(c) => c,
None => return Some(make_sname_error()),
};
if !syntax_equal(&step_conc, &expected_step) {
return Some(make_sname_error());
}
Some(make_forall_nat_syntax(&norm_motive))
}
fn build_induction_step_formula(ctx: &Context, motive_body: &Term) -> Option<Term> {
let p_k = motive_body.clone();
let succ_var = Term::App(
Box::new(Term::App(
Box::new(Term::Global("SApp".to_string())),
Box::new(make_sname("Succ")),
)),
Box::new(Term::App(
Box::new(Term::Global("SVar".to_string())),
Box::new(Term::Lit(Literal::Int(0))),
)),
);
let p_succ_k = try_syn_subst_reduce(ctx, &succ_var, 0, motive_body)?;
let implies_body = make_implies_syntax(&p_k, &p_succ_k);
let slam = Term::App(
Box::new(Term::App(
Box::new(Term::Global("SLam".to_string())),
Box::new(make_sname("Nat")),
)),
Box::new(implies_body),
);
Some(make_forall_syntax_with_type(&make_sname("Nat"), &slam))
}
fn make_implies_syntax(a: &Term, b: &Term) -> Term {
let app1 = Term::App(
Box::new(Term::App(
Box::new(Term::Global("SApp".to_string())),
Box::new(make_sname("Implies")),
)),
Box::new(a.clone()),
);
Term::App(
Box::new(Term::App(
Box::new(Term::Global("SApp".to_string())),
Box::new(app1),
)),
Box::new(b.clone()),
)
}
fn make_forall_nat_syntax(motive: &Term) -> Term {
make_forall_syntax_with_type(&make_sname("Nat"), motive)
}
fn make_forall_syntax_with_type(type_s: &Term, body: &Term) -> Term {
let app1 = Term::App(
Box::new(Term::App(
Box::new(Term::Global("SApp".to_string())),
Box::new(make_sname("Forall")),
)),
Box::new(type_s.clone()),
);
Term::App(
Box::new(Term::App(
Box::new(Term::Global("SApp".to_string())),
Box::new(app1),
)),
Box::new(body.clone()),
)
}
fn try_dcompute_conclude(ctx: &Context, goal: &Term) -> Option<Term> {
let norm_goal = normalize(ctx, goal);
let parts = extract_eq_syntax_parts(&norm_goal);
if parts.is_none() {
return Some(make_sname_error());
}
let (_, a, b) = parts.unwrap();
let fuel = 1000;
let a_eval = match try_syn_eval_reduce(ctx, fuel, &a) {
Some(e) => e,
None => return Some(make_sname_error()),
};
let b_eval = match try_syn_eval_reduce(ctx, fuel, &b) {
Some(e) => e,
None => return Some(make_sname_error()),
};
if syntax_equal(&a_eval, &b_eval) {
Some(norm_goal)
} else {
Some(make_sname_error())
}
}
fn extract_eq_syntax_parts(term: &Term) -> Option<(Term, Term, Term)> {
if let Term::App(partial2, b) = term {
if let Term::App(sapp2, inner2) = partial2.as_ref() {
if let Term::Global(sapp2_name) = sapp2.as_ref() {
if sapp2_name != "SApp" {
return None;
}
} else {
return None;
}
if let Term::App(partial1, a) = inner2.as_ref() {
if let Term::App(sapp1, inner1) = partial1.as_ref() {
if let Term::Global(sapp1_name) = sapp1.as_ref() {
if sapp1_name != "SApp" {
return None;
}
} else {
return None;
}
if let Term::App(eq_t, t) = inner1.as_ref() {
if let Term::App(sapp0, eq_sname) = eq_t.as_ref() {
if let Term::Global(sapp0_name) = sapp0.as_ref() {
if sapp0_name != "SApp" {
return None;
}
} else {
return None;
}
if let Term::App(sname_ctor, eq_str) = eq_sname.as_ref() {
if let Term::Global(ctor) = sname_ctor.as_ref() {
if ctor == "SName" {
if let Term::Lit(Literal::Text(s)) = eq_str.as_ref() {
if s == "Eq" {
return Some((
t.as_ref().clone(),
a.as_ref().clone(),
b.as_ref().clone(),
));
}
}
}
}
}
}
}
}
}
}
}
None
}
fn try_tact_orelse_reduce(
ctx: &Context,
t1: &Term,
t2: &Term,
goal: &Term,
) -> Option<Term> {
let norm_goal = normalize(ctx, goal);
let d1_app = Term::App(Box::new(t1.clone()), Box::new(norm_goal.clone()));
let d1 = normalize(ctx, &d1_app);
if let Some(conc1) = try_concludes_reduce(ctx, &d1) {
if is_error_syntax(&conc1) {
let d2_app = Term::App(Box::new(t2.clone()), Box::new(norm_goal));
return Some(normalize(ctx, &d2_app));
} else {
return Some(d1);
}
}
Some(make_error_derivation())
}
fn is_error_syntax(term: &Term) -> bool {
if let Term::App(ctor, arg) = term {
if let Term::Global(name) = ctor.as_ref() {
if name == "SName" {
if let Term::Lit(Literal::Text(s)) = arg.as_ref() {
return s == "Error";
}
}
}
}
false
}
fn try_tact_try_reduce(ctx: &Context, t: &Term, goal: &Term) -> Option<Term> {
let norm_goal = normalize(ctx, goal);
let d_app = Term::App(Box::new(t.clone()), Box::new(norm_goal.clone()));
let d = normalize(ctx, &d_app);
if let Some(conc) = try_concludes_reduce(ctx, &d) {
if is_error_syntax(&conc) {
return Some(make_daxiom(&norm_goal));
} else {
return Some(d);
}
}
Some(make_daxiom(&norm_goal))
}
fn try_tact_repeat_reduce(ctx: &Context, t: &Term, goal: &Term) -> Option<Term> {
const MAX_ITERATIONS: usize = 100;
let norm_goal = normalize(ctx, goal);
let mut current_goal = norm_goal.clone();
let mut last_successful_deriv: Option<Term> = None;
for _ in 0..MAX_ITERATIONS {
let d_app = Term::App(Box::new(t.clone()), Box::new(current_goal.clone()));
let d = normalize(ctx, &d_app);
if let Some(conc) = try_concludes_reduce(ctx, &d) {
if is_error_syntax(&conc) {
break;
}
if syntax_equal(&conc, ¤t_goal) {
break;
}
current_goal = conc;
last_successful_deriv = Some(d);
} else {
break;
}
}
last_successful_deriv.or_else(|| Some(make_daxiom(&norm_goal)))
}
fn try_tact_then_reduce(
ctx: &Context,
t1: &Term,
t2: &Term,
goal: &Term,
) -> Option<Term> {
let norm_goal = normalize(ctx, goal);
let d1_app = Term::App(Box::new(t1.clone()), Box::new(norm_goal.clone()));
let d1 = normalize(ctx, &d1_app);
if let Some(conc1) = try_concludes_reduce(ctx, &d1) {
if is_error_syntax(&conc1) {
return Some(make_error_derivation());
}
let d2_app = Term::App(Box::new(t2.clone()), Box::new(conc1));
let d2 = normalize(ctx, &d2_app);
return Some(d2);
}
Some(make_error_derivation())
}
fn try_tact_first_reduce(ctx: &Context, tactics: &Term, goal: &Term) -> Option<Term> {
let norm_goal = normalize(ctx, goal);
let tactic_vec = extract_tlist(tactics)?;
for tactic in tactic_vec {
let d_app = Term::App(Box::new(tactic), Box::new(norm_goal.clone()));
let d = normalize(ctx, &d_app);
if let Some(conc) = try_concludes_reduce(ctx, &d) {
if !is_error_syntax(&conc) {
return Some(d);
}
}
}
Some(make_error_derivation())
}
fn extract_tlist(term: &Term) -> Option<Vec<Term>> {
let mut result = Vec::new();
let mut current = term.clone();
loop {
match ¤t {
Term::App(tnil, _type) => {
if let Term::Global(name) = tnil.as_ref() {
if name == "TNil" {
break;
}
}
if let Term::App(partial2, tail) = ¤t {
if let Term::App(partial1, head) = partial2.as_ref() {
if let Term::App(tcons, _type) = partial1.as_ref() {
if let Term::Global(name) = tcons.as_ref() {
if name == "TCons" {
result.push(head.as_ref().clone());
current = tail.as_ref().clone();
continue;
}
}
}
}
}
return None;
}
Term::Global(name) if name == "TNil" => {
break;
}
_ => {
return None;
}
}
}
Some(result)
}
fn try_tact_solve_reduce(ctx: &Context, t: &Term, goal: &Term) -> Option<Term> {
let norm_goal = normalize(ctx, goal);
let d_app = Term::App(Box::new(t.clone()), Box::new(norm_goal.clone()));
let d = normalize(ctx, &d_app);
if let Some(conc) = try_concludes_reduce(ctx, &d) {
if is_error_syntax(&conc) {
return Some(make_error_derivation());
}
return Some(d);
}
Some(make_error_derivation())
}
fn try_dcong_conclude(ctx: &Context, context: &Term, eq_proof: &Term) -> Option<Term> {
let eq_conc = try_concludes_reduce(ctx, eq_proof)?;
let parts = extract_eq_syntax_parts(&eq_conc);
if parts.is_none() {
return Some(make_sname_error());
}
let (_type_term, lhs, rhs) = parts.unwrap();
let norm_context = normalize(ctx, context);
let slam_parts = extract_slam_parts(&norm_context);
if slam_parts.is_none() {
return Some(make_sname_error());
}
let (param_type, body) = slam_parts.unwrap();
let fa = try_syn_subst_reduce(ctx, &lhs, 0, &body)?;
let fb = try_syn_subst_reduce(ctx, &rhs, 0, &body)?;
Some(make_eq_syntax_three(¶m_type, &fa, &fb))
}
fn extract_slam_parts(term: &Term) -> Option<(Term, Term)> {
if let Term::App(inner, body) = term {
if let Term::App(slam_ctor, param_type) = inner.as_ref() {
if let Term::Global(name) = slam_ctor.as_ref() {
if name == "SLam" {
return Some((param_type.as_ref().clone(), body.as_ref().clone()));
}
}
}
}
None
}
fn make_eq_syntax_three(type_s: &Term, a: &Term, b: &Term) -> Term {
let eq_name = Term::App(
Box::new(Term::Global("SName".to_string())),
Box::new(Term::Lit(Literal::Text("Eq".to_string()))),
);
let app1 = Term::App(
Box::new(Term::App(
Box::new(Term::Global("SApp".to_string())),
Box::new(eq_name),
)),
Box::new(type_s.clone()),
);
let app2 = Term::App(
Box::new(Term::App(
Box::new(Term::Global("SApp".to_string())),
Box::new(app1),
)),
Box::new(a.clone()),
);
Term::App(
Box::new(Term::App(
Box::new(Term::Global("SApp".to_string())),
Box::new(app2),
)),
Box::new(b.clone()),
)
}
fn try_delim_conclude(
ctx: &Context,
ind_type: &Term,
motive: &Term,
cases: &Term,
) -> Option<Term> {
let norm_ind_type = normalize(ctx, ind_type);
let norm_motive = normalize(ctx, motive);
let norm_cases = normalize(ctx, cases);
let ind_name = match extract_inductive_name_from_syntax(&norm_ind_type) {
Some(name) => name,
None => return Some(make_sname_error()),
};
let constructors = ctx.get_constructors(&ind_name);
if constructors.is_empty() {
return Some(make_sname_error());
}
let case_proofs = match extract_case_proofs(&norm_cases) {
Some(proofs) => proofs,
None => return Some(make_sname_error()),
};
if case_proofs.len() != constructors.len() {
return Some(make_sname_error());
}
let motive_body = match extract_slam_body(&norm_motive) {
Some(body) => body,
None => return Some(make_sname_error()),
};
for (i, (ctor_name, _ctor_type)) in constructors.iter().enumerate() {
let case_proof = &case_proofs[i];
let case_conc = match try_concludes_reduce(ctx, case_proof) {
Some(c) => c,
None => return Some(make_sname_error()),
};
let expected = match build_case_expected(ctx, ctor_name, &constructors, &motive_body, &norm_ind_type) {
Some(e) => e,
None => return Some(make_sname_error()),
};
if !syntax_equal(&case_conc, &expected) {
return Some(make_sname_error());
}
}
Some(make_forall_syntax_generic(&norm_ind_type, &norm_motive))
}
fn extract_inductive_name_from_syntax(term: &Term) -> Option<String> {
if let Term::App(sname, text) = term {
if let Term::Global(ctor) = sname.as_ref() {
if ctor == "SName" {
if let Term::Lit(Literal::Text(name)) = text.as_ref() {
return Some(name.clone());
}
}
}
}
if let Term::App(inner, _arg) = term {
if let Term::App(sapp, func) = inner.as_ref() {
if let Term::Global(ctor) = sapp.as_ref() {
if ctor == "SApp" {
return extract_inductive_name_from_syntax(func);
}
}
}
}
None
}
fn extract_case_proofs(term: &Term) -> Option<Vec<Term>> {
let mut proofs = Vec::new();
let mut current = term;
loop {
if let Term::Global(name) = current {
if name == "DCaseEnd" {
return Some(proofs);
}
}
if let Term::App(inner, tail) = current {
if let Term::App(dcase, head) = inner.as_ref() {
if let Term::Global(name) = dcase.as_ref() {
if name == "DCase" {
proofs.push(head.as_ref().clone());
current = tail.as_ref();
continue;
}
}
}
}
return None;
}
}
fn build_case_expected(
ctx: &Context,
ctor_name: &str,
_constructors: &[(&str, &Term)],
motive_body: &Term,
ind_type: &Term,
) -> Option<Term> {
let ind_name = extract_inductive_name_from_syntax(ind_type)?;
if ind_name == "Nat" {
if ctor_name == "Zero" {
let zero = make_sname("Zero");
return try_syn_subst_reduce(ctx, &zero, 0, motive_body);
} else if ctor_name == "Succ" {
return build_induction_step_formula(ctx, motive_body);
}
}
let ctor_syntax = make_sname(ctor_name);
let ctor_applied = apply_type_args_to_ctor(&ctor_syntax, ind_type);
if let Some(ctor_ty) = ctx.get_global(ctor_name) {
if is_recursive_constructor(ctx, ctor_ty, &ind_name, ind_type) {
return build_recursive_case_formula(ctx, ctor_name, ctor_ty, motive_body, ind_type, &ind_name);
}
}
try_syn_subst_reduce(ctx, &ctor_applied, 0, motive_body)
}
fn apply_type_args_to_ctor(ctor: &Term, ind_type: &Term) -> Term {
let args = extract_type_args(ind_type);
if args.is_empty() {
return ctor.clone();
}
args.iter().fold(ctor.clone(), |acc, arg| {
Term::App(
Box::new(Term::App(
Box::new(Term::Global("SApp".to_string())),
Box::new(acc),
)),
Box::new(arg.clone()),
)
})
}
fn extract_type_args(term: &Term) -> Vec<Term> {
let mut args = Vec::new();
let mut current = term;
loop {
if let Term::App(inner, arg) = current {
if let Term::App(sapp, func) = inner.as_ref() {
if let Term::Global(ctor) = sapp.as_ref() {
if ctor == "SApp" {
args.push(arg.as_ref().clone());
current = func.as_ref();
continue;
}
}
}
}
break;
}
args.reverse();
args
}
fn make_forall_syntax_generic(ind_type: &Term, motive: &Term) -> Term {
let forall_type = Term::App(
Box::new(Term::App(
Box::new(Term::Global("SApp".to_string())),
Box::new(make_sname("Forall")),
)),
Box::new(ind_type.clone()),
);
Term::App(
Box::new(Term::App(
Box::new(Term::Global("SApp".to_string())),
Box::new(forall_type),
)),
Box::new(motive.clone()),
)
}
fn is_recursive_constructor(
_ctx: &Context,
ctor_ty: &Term,
ind_name: &str,
_ind_type: &Term,
) -> bool {
fn contains_inductive(term: &Term, ind_name: &str) -> bool {
match term {
Term::Global(name) => name == ind_name,
Term::App(f, a) => {
contains_inductive(f, ind_name) || contains_inductive(a, ind_name)
}
Term::Pi { param_type, body_type, .. } => {
contains_inductive(param_type, ind_name) || contains_inductive(body_type, ind_name)
}
Term::Lambda { param_type, body, .. } => {
contains_inductive(param_type, ind_name) || contains_inductive(body, ind_name)
}
_ => false,
}
}
fn check_params(term: &Term, ind_name: &str) -> bool {
match term {
Term::Pi { param_type, body_type, .. } => {
if contains_inductive(param_type, ind_name) {
return true;
}
check_params(body_type, ind_name)
}
_ => false,
}
}
check_params(ctor_ty, ind_name)
}
fn build_recursive_case_formula(
ctx: &Context,
ctor_name: &str,
ctor_ty: &Term,
motive_body: &Term,
ind_type: &Term,
ind_name: &str,
) -> Option<Term> {
let type_args = extract_type_args(ind_type);
let args = collect_ctor_args(ctor_ty, ind_name, &type_args);
if args.is_empty() {
let ctor_applied = apply_type_args_to_ctor(&make_sname(ctor_name), ind_type);
return try_syn_subst_reduce(ctx, &ctor_applied, 0, motive_body);
}
let mut ctor_app = apply_type_args_to_ctor(&make_sname(ctor_name), ind_type);
for (i, _) in args.iter().enumerate() {
let idx = (args.len() - 1 - i) as i64;
let var = Term::App(
Box::new(Term::Global("SVar".to_string())),
Box::new(Term::Lit(Literal::Int(idx))),
);
ctor_app = Term::App(
Box::new(Term::App(
Box::new(Term::Global("SApp".to_string())),
Box::new(ctor_app),
)),
Box::new(var),
);
}
let p_ctor = try_syn_subst_reduce(ctx, &ctor_app, 0, motive_body)?;
let mut body = p_ctor;
for (i, (arg_ty, is_recursive)) in args.iter().enumerate().rev() {
if *is_recursive {
let idx = (args.len() - 1 - i) as i64;
let var = Term::App(
Box::new(Term::Global("SVar".to_string())),
Box::new(Term::Lit(Literal::Int(idx))),
);
let p_arg = try_syn_subst_reduce(ctx, &var, 0, motive_body)?;
body = make_implies_syntax(&p_arg, &body);
}
let _ = (i, arg_ty); }
for (arg_ty, _) in args.iter().rev() {
let slam = Term::App(
Box::new(Term::App(
Box::new(Term::Global("SLam".to_string())),
Box::new(arg_ty.clone()),
)),
Box::new(body.clone()),
);
body = make_forall_syntax_with_type(arg_ty, &slam);
}
Some(body)
}
fn collect_ctor_args(ctor_ty: &Term, ind_name: &str, type_args: &[Term]) -> Vec<(Term, bool)> {
let mut args = Vec::new();
let mut current = ctor_ty;
let mut skip_count = type_args.len();
loop {
match current {
Term::Pi { param_type, body_type, .. } => {
if skip_count > 0 {
skip_count -= 1;
} else {
let is_recursive = contains_inductive_term(param_type, ind_name);
let arg_ty_syntax = kernel_type_to_syntax(param_type);
args.push((arg_ty_syntax, is_recursive));
}
current = body_type;
}
_ => break,
}
}
args
}
fn contains_inductive_term(term: &Term, ind_name: &str) -> bool {
match term {
Term::Global(name) => name == ind_name,
Term::App(f, a) => {
contains_inductive_term(f, ind_name) || contains_inductive_term(a, ind_name)
}
Term::Pi { param_type, body_type, .. } => {
contains_inductive_term(param_type, ind_name) || contains_inductive_term(body_type, ind_name)
}
Term::Lambda { param_type, body, .. } => {
contains_inductive_term(param_type, ind_name) || contains_inductive_term(body, ind_name)
}
_ => false,
}
}
fn kernel_type_to_syntax(term: &Term) -> Term {
match term {
Term::Global(name) => make_sname(name),
Term::Var(name) => make_sname(name), Term::App(f, a) => {
let f_syn = kernel_type_to_syntax(f);
let a_syn = kernel_type_to_syntax(a);
Term::App(
Box::new(Term::App(
Box::new(Term::Global("SApp".to_string())),
Box::new(f_syn),
)),
Box::new(a_syn),
)
}
Term::Pi { param, param_type, body_type } => {
let pt_syn = kernel_type_to_syntax(param_type);
let bt_syn = kernel_type_to_syntax(body_type);
Term::App(
Box::new(Term::App(
Box::new(Term::Global("SPi".to_string())),
Box::new(pt_syn),
)),
Box::new(bt_syn),
)
}
Term::Sort(univ) => {
Term::App(
Box::new(Term::Global("SSort".to_string())),
Box::new(univ_to_syntax(univ)),
)
}
Term::Lit(lit) => {
Term::App(
Box::new(Term::Global("SLit".to_string())),
Box::new(Term::Lit(lit.clone())),
)
}
_ => {
make_sname("Unknown")
}
}
}
fn univ_to_syntax(univ: &crate::term::Universe) -> Term {
match univ {
crate::term::Universe::Prop => Term::Global("UProp".to_string()),
crate::term::Universe::Type(n) => Term::App(
Box::new(Term::Global("UType".to_string())),
Box::new(Term::Lit(Literal::Int(*n as i64))),
),
}
}
fn try_try_inversion_reduce(ctx: &Context, goal: &Term) -> Option<Term> {
let (ind_name, hyp_args) = match extract_applied_inductive_from_syntax(goal) {
Some((name, args)) => (name, args),
None => return Some(make_error_derivation()),
};
if !ctx.is_inductive(&ind_name) {
return Some(make_error_derivation());
}
let constructors = ctx.get_constructors(&ind_name);
let mut any_possible = false;
for (_ctor_name, ctor_type) in constructors.iter() {
if can_constructor_match_args(ctx, ctor_type, &hyp_args, &ind_name) {
any_possible = true;
break;
}
}
if any_possible {
return Some(make_error_derivation());
}
Some(Term::App(
Box::new(Term::Global("DInversion".to_string())),
Box::new(goal.clone()),
))
}
fn try_dinversion_conclude(ctx: &Context, hyp_type: &Term) -> Option<Term> {
let norm_hyp = normalize(ctx, hyp_type);
let (ind_name, hyp_args) = match extract_applied_inductive_from_syntax(&norm_hyp) {
Some((name, args)) => (name, args),
None => return Some(make_sname_error()),
};
if !ctx.is_inductive(&ind_name) {
return Some(make_sname_error());
}
let constructors = ctx.get_constructors(&ind_name);
for (_ctor_name, ctor_type) in constructors.iter() {
if can_constructor_match_args(ctx, ctor_type, &hyp_args, &ind_name) {
return Some(make_sname_error());
}
}
Some(make_sname("False"))
}
fn extract_applied_inductive_from_syntax(term: &Term) -> Option<(String, Vec<Term>)> {
if let Term::App(ctor, text) = term {
if let Term::Global(ctor_name) = ctor.as_ref() {
if ctor_name == "SName" {
if let Term::Lit(Literal::Text(name)) = text.as_ref() {
return Some((name.clone(), vec![]));
}
}
}
}
if let Term::App(inner, arg) = term {
if let Term::App(sapp_ctor, func) = inner.as_ref() {
if let Term::Global(ctor_name) = sapp_ctor.as_ref() {
if ctor_name == "SApp" {
let (name, mut args) = extract_applied_inductive_from_syntax(func)?;
args.push(arg.as_ref().clone());
return Some((name, args));
}
}
}
}
None
}
fn can_constructor_match_args(
ctx: &Context,
ctor_type: &Term,
hyp_args: &[Term],
ind_name: &str,
) -> bool {
let (result, pattern_vars) = decompose_ctor_type_with_vars(ctor_type);
let result_args = match extract_applied_inductive_from_syntax(&kernel_type_to_syntax(&result)) {
Some((name, args)) if name == *ind_name => args,
_ => return false,
};
if result_args.len() != hyp_args.len() {
return false;
}
let mut bindings: std::collections::HashMap<String, Term> = std::collections::HashMap::new();
for (pattern, concrete) in result_args.iter().zip(hyp_args.iter()) {
if !can_unify_syntax_terms_with_bindings(ctx, pattern, concrete, &pattern_vars, &mut bindings) {
return false;
}
}
true
}
fn decompose_ctor_type_with_vars(ty: &Term) -> (Term, Vec<String>) {
let mut vars = Vec::new();
let mut current = ty;
loop {
match current {
Term::Pi { param, body_type, .. } => {
vars.push(param.clone());
current = body_type;
}
_ => break,
}
}
(current.clone(), vars)
}
fn can_unify_syntax_terms_with_bindings(
ctx: &Context,
pattern: &Term,
concrete: &Term,
pattern_vars: &[String],
bindings: &mut std::collections::HashMap<String, Term>,
) -> bool {
if let Term::App(ctor, _idx) = pattern {
if let Term::Global(name) = ctor.as_ref() {
if name == "SVar" {
return true;
}
}
}
if let Term::App(ctor1, text1) = pattern {
if let Term::Global(n1) = ctor1.as_ref() {
if n1 == "SName" {
if let Term::Lit(Literal::Text(var_name)) = text1.as_ref() {
if pattern_vars.contains(var_name) {
if let Some(existing) = bindings.get(var_name) {
return syntax_terms_equal(existing, concrete);
} else {
bindings.insert(var_name.clone(), concrete.clone());
return true;
}
}
}
if let Term::App(ctor2, text2) = concrete {
if let Term::Global(n2) = ctor2.as_ref() {
if n2 == "SName" {
return text1 == text2;
}
}
}
return false;
}
}
}
if let (Term::App(inner1, arg1), Term::App(inner2, arg2)) = (pattern, concrete) {
if let (Term::App(sapp1, func1), Term::App(sapp2, func2)) =
(inner1.as_ref(), inner2.as_ref())
{
if let (Term::Global(n1), Term::Global(n2)) = (sapp1.as_ref(), sapp2.as_ref()) {
if n1 == "SApp" && n2 == "SApp" {
return can_unify_syntax_terms_with_bindings(ctx, func1, func2, pattern_vars, bindings)
&& can_unify_syntax_terms_with_bindings(ctx, arg1.as_ref(), arg2.as_ref(), pattern_vars, bindings);
}
}
}
}
if let (Term::App(ctor1, lit1), Term::App(ctor2, lit2)) = (pattern, concrete) {
if let (Term::Global(n1), Term::Global(n2)) = (ctor1.as_ref(), ctor2.as_ref()) {
if n1 == "SLit" && n2 == "SLit" {
return lit1 == lit2;
}
}
}
pattern == concrete
}
fn syntax_terms_equal(a: &Term, b: &Term) -> bool {
match (a, b) {
(Term::App(f1, x1), Term::App(f2, x2)) => {
syntax_terms_equal(f1, f2) && syntax_terms_equal(x1, x2)
}
(Term::Global(n1), Term::Global(n2)) => n1 == n2,
(Term::Lit(l1), Term::Lit(l2)) => l1 == l2,
_ => a == b,
}
}
fn extract_eq_components_from_syntax(term: &Term) -> Option<(Term, Term, Term)> {
let (eq_a_x, y) = extract_sapp(term)?;
let (eq_a, x) = extract_sapp(&eq_a_x)?;
let (eq, a) = extract_sapp(&eq_a)?;
let eq_name = extract_sname(&eq)?;
if eq_name != "Eq" {
return None;
}
Some((a, x, y))
}
fn extract_sapp(term: &Term) -> Option<(Term, Term)> {
if let Term::App(inner, x) = term {
if let Term::App(sapp_ctor, f) = inner.as_ref() {
if let Term::Global(ctor_name) = sapp_ctor.as_ref() {
if ctor_name == "SApp" {
return Some((f.as_ref().clone(), x.as_ref().clone()));
}
}
}
}
None
}
fn extract_sname(term: &Term) -> Option<String> {
if let Term::App(ctor, text) = term {
if let Term::Global(ctor_name) = ctor.as_ref() {
if ctor_name == "SName" {
if let Term::Lit(Literal::Text(name)) = text.as_ref() {
return Some(name.clone());
}
}
}
}
None
}
fn contains_subterm_syntax(term: &Term, target: &Term) -> bool {
if syntax_equal(term, target) {
return true;
}
if let Some((f, x)) = extract_sapp(term) {
if contains_subterm_syntax(&f, target) || contains_subterm_syntax(&x, target) {
return true;
}
}
if let Some((t, body)) = extract_slam(term) {
if contains_subterm_syntax(&t, target) || contains_subterm_syntax(&body, target) {
return true;
}
}
if let Some((t, body)) = extract_spi(term) {
if contains_subterm_syntax(&t, target) || contains_subterm_syntax(&body, target) {
return true;
}
}
false
}
fn extract_slam(term: &Term) -> Option<(Term, Term)> {
if let Term::App(inner, body) = term {
if let Term::App(slam_ctor, t) = inner.as_ref() {
if let Term::Global(ctor_name) = slam_ctor.as_ref() {
if ctor_name == "SLam" {
return Some((t.as_ref().clone(), body.as_ref().clone()));
}
}
}
}
None
}
fn extract_spi(term: &Term) -> Option<(Term, Term)> {
if let Term::App(inner, body) = term {
if let Term::App(spi_ctor, t) = inner.as_ref() {
if let Term::Global(ctor_name) = spi_ctor.as_ref() {
if ctor_name == "SPi" {
return Some((t.as_ref().clone(), body.as_ref().clone()));
}
}
}
}
None
}
fn replace_first_subterm_syntax(term: &Term, target: &Term, replacement: &Term) -> Option<Term> {
if syntax_equal(term, target) {
return Some(replacement.clone());
}
if let Some((f, x)) = extract_sapp(term) {
if let Some(new_f) = replace_first_subterm_syntax(&f, target, replacement) {
return Some(make_sapp(new_f, x));
}
if let Some(new_x) = replace_first_subterm_syntax(&x, target, replacement) {
return Some(make_sapp(f, new_x));
}
}
if let Some((t, body)) = extract_slam(term) {
if let Some(new_t) = replace_first_subterm_syntax(&t, target, replacement) {
return Some(make_slam(new_t, body));
}
if let Some(new_body) = replace_first_subterm_syntax(&body, target, replacement) {
return Some(make_slam(t, new_body));
}
}
if let Some((t, body)) = extract_spi(term) {
if let Some(new_t) = replace_first_subterm_syntax(&t, target, replacement) {
return Some(make_spi(new_t, body));
}
if let Some(new_body) = replace_first_subterm_syntax(&body, target, replacement) {
return Some(make_spi(t, new_body));
}
}
None
}
fn try_try_rewrite_reduce(
ctx: &Context,
eq_proof: &Term,
goal: &Term,
reverse: bool,
) -> Option<Term> {
let eq_conclusion = try_concludes_reduce(ctx, eq_proof)?;
let (ty, lhs, rhs) = match extract_eq_components_from_syntax(&eq_conclusion) {
Some(components) => components,
None => return Some(make_error_derivation()),
};
let (target, replacement) = if reverse { (rhs, lhs) } else { (lhs, rhs) };
if !contains_subterm_syntax(goal, &target) {
return Some(make_error_derivation());
}
let new_goal = match replace_first_subterm_syntax(goal, &target, &replacement) {
Some(ng) => ng,
None => return Some(make_error_derivation()),
};
Some(Term::App(
Box::new(Term::App(
Box::new(Term::App(
Box::new(Term::Global("DRewrite".to_string())),
Box::new(eq_proof.clone()),
)),
Box::new(goal.clone()),
)),
Box::new(new_goal),
))
}
fn try_drewrite_conclude(
ctx: &Context,
eq_proof: &Term,
old_goal: &Term,
new_goal: &Term,
) -> Option<Term> {
let eq_conclusion = try_concludes_reduce(ctx, eq_proof)?;
let (_ty, lhs, rhs) = match extract_eq_components_from_syntax(&eq_conclusion) {
Some(components) => components,
None => return Some(make_sname_error()),
};
if let Some(computed_new) = replace_first_subterm_syntax(old_goal, &lhs, &rhs) {
if syntax_equal(&computed_new, new_goal) {
return Some(new_goal.clone());
}
}
if let Some(computed_new) = replace_first_subterm_syntax(old_goal, &rhs, &lhs) {
if syntax_equal(&computed_new, new_goal) {
return Some(new_goal.clone());
}
}
Some(make_sname_error())
}
fn try_try_destruct_reduce(
ctx: &Context,
ind_type: &Term,
motive: &Term,
cases: &Term,
) -> Option<Term> {
Some(Term::App(
Box::new(Term::App(
Box::new(Term::App(
Box::new(Term::Global("DDestruct".to_string())),
Box::new(ind_type.clone()),
)),
Box::new(motive.clone()),
)),
Box::new(cases.clone()),
))
}
fn try_ddestruct_conclude(
ctx: &Context,
ind_type: &Term,
motive: &Term,
cases: &Term,
) -> Option<Term> {
let ind_name = extract_inductive_name_from_syntax(ind_type)?;
if !ctx.is_inductive(&ind_name) {
return Some(make_sname_error());
}
let constructors = ctx.get_constructors(&ind_name);
let case_proofs = match extract_case_proofs(cases) {
Some(proofs) => proofs,
None => return Some(make_sname_error()),
};
if case_proofs.len() != constructors.len() {
return Some(make_sname_error());
}
Some(make_forall_syntax_with_type(ind_type, motive))
}
fn try_try_apply_reduce(
ctx: &Context,
hyp_name: &Term,
hyp_proof: &Term,
goal: &Term,
) -> Option<Term> {
let hyp_conclusion = try_concludes_reduce(ctx, hyp_proof)?;
if let Some((antecedent, consequent)) = extract_spi(&hyp_conclusion) {
if syntax_equal(&consequent, goal) {
return Some(Term::App(
Box::new(Term::App(
Box::new(Term::App(
Box::new(Term::App(
Box::new(Term::Global("DApply".to_string())),
Box::new(hyp_name.clone()),
)),
Box::new(hyp_proof.clone()),
)),
Box::new(goal.clone()),
)),
Box::new(antecedent),
));
}
}
if let Some(forall_body) = extract_forall_body(&hyp_conclusion) {
return Some(Term::App(
Box::new(Term::App(
Box::new(Term::App(
Box::new(Term::App(
Box::new(Term::Global("DApply".to_string())),
Box::new(hyp_name.clone()),
)),
Box::new(hyp_proof.clone()),
)),
Box::new(goal.clone()),
)),
Box::new(make_sname("True")),
));
}
if syntax_equal(&hyp_conclusion, goal) {
return Some(Term::App(
Box::new(Term::App(
Box::new(Term::App(
Box::new(Term::App(
Box::new(Term::Global("DApply".to_string())),
Box::new(hyp_name.clone()),
)),
Box::new(hyp_proof.clone()),
)),
Box::new(goal.clone()),
)),
Box::new(make_sname("True")),
));
}
Some(make_error_derivation())
}
fn try_dapply_conclude(
ctx: &Context,
hyp_name: &Term,
hyp_proof: &Term,
old_goal: &Term,
new_goal: &Term,
) -> Option<Term> {
let hyp_conclusion = try_concludes_reduce(ctx, hyp_proof)?;
if let Some((antecedent, consequent)) = extract_spi(&hyp_conclusion) {
if syntax_equal(&consequent, old_goal) {
if syntax_equal(&antecedent, new_goal) || extract_sname(new_goal) == Some("True".to_string()) {
return Some(new_goal.clone());
}
}
}
if let Some(_forall_body) = extract_forall_body(&hyp_conclusion) {
if extract_sname(new_goal) == Some("True".to_string()) {
return Some(new_goal.clone());
}
}
if syntax_equal(&hyp_conclusion, old_goal) {
if extract_sname(new_goal) == Some("True".to_string()) {
return Some(new_goal.clone());
}
}
Some(make_sname_error())
}