use crate::{
ctx::Ctx,
env::Env,
reduce::{def_eq, nf, whnf},
stdlib::{eq_refl, nat, nat_zero, EQ_ID, NAT_ID, NAT_NEXT, NAT_ZERO},
term::Term,
};
pub fn omega_solve(env: &Env, ctx: &Ctx, goal: &Term) -> Option<Term> {
if let Some(proof) = try_rfl(env, ctx, goal) {
return Some(proof);
}
if let Term::Ind(id, params) = nf(env, ctx, goal.clone()) {
if id == EQ_ID && params.len() == 3 {
let ty = ¶ms[0];
let lhs = ¶ms[1];
let rhs = ¶ms[2];
if let Some(proof) = try_arith_laws(env, ctx, ty, lhs, rhs) {
return Some(proof);
}
}
}
None
}
pub fn try_rfl(env: &Env, ctx: &Ctx, goal: &Term) -> Option<Term> {
let reduced = nf(env, ctx, goal.clone());
if let Term::Ind(id, ref params) = reduced {
if id == EQ_ID && params.len() == 3 {
if def_eq(env, ctx, ¶ms[1], ¶ms[2]) {
return Some(eq_refl(params[0].clone(), params[1].clone()));
}
}
}
None
}
fn try_arith_laws(env: &Env, ctx: &Ctx, ty: &Term, lhs: &Term, rhs: &Term) -> Option<Term> {
if !def_eq(env, ctx, ty, &nat()) {
return None;
}
let lhs_nf = nf(env, ctx, lhs.clone());
let rhs_nf = nf(env, ctx, rhs.clone());
if def_eq(env, ctx, &lhs_nf, &rhs_nf) {
return Some(eq_refl(nat(), lhs_nf));
}
if let (Some(lhs_addends), Some(rhs_addends)) = (
extract_add_terms(&lhs_nf),
extract_add_terms(&rhs_nf),
) {
if addends_equal(env, ctx, &lhs_addends, &rhs_addends) {
}
}
None
}
fn extract_add_terms(t: &Term) -> Option<Vec<Term>> {
match t {
Term::Elim(id, _, cases, target) if *id == NAT_ID && cases.len() == 2 => {
let mut addends = vec![*target.clone()];
addends.push(cases[0].clone()); Some(addends)
}
_ => {
Some(vec![t.clone()])
}
}
}
fn addends_equal(env: &Env, ctx: &Ctx, a: &[Term], b: &[Term]) -> bool {
if a.len() != b.len() { return false; }
let mut used = vec![false; b.len()];
'outer: for ta in a {
for (j, tb) in b.iter().enumerate() {
if !used[j] && def_eq(env, ctx, ta, tb) {
used[j] = true;
continue 'outer;
}
}
return false;
}
true
}
pub fn eval_nat(env: &Env, ctx: &Ctx, t: &Term) -> Option<u64> {
match whnf(env, ctx, t.clone()) {
Term::Ctor(id, k, args) if id == NAT_ID => {
if k == NAT_ZERO && args.is_empty() {
Some(0)
} else if k == NAT_NEXT && args.len() == 1 {
eval_nat(env, ctx, &args[0]).map(|n| n + 1)
} else {
None
}
}
_ => None,
}
}
pub fn build_nat(n: u64) -> Term {
let mut t = nat_zero();
for _ in 0..n { t = crate::stdlib::nat_next(t); }
t
}
pub fn eval_add_expr(env: &Env, ctx: &Ctx, t: &Term) -> Option<u64> {
match whnf(env, ctx, t.clone()) {
Term::Ctor(id, k, ref args) if id == NAT_ID => {
if k == NAT_ZERO { Some(0) }
else if k == NAT_NEXT && args.len() == 1 {
eval_add_expr(env, ctx, &args[0]).map(|n| n + 1)
} else { None }
}
Term::Elim(id, _, ref cases, ref target) if id == NAT_ID && cases.len() == 2 => {
let n = eval_add_expr(env, ctx, target)?;
let m = eval_add_expr(env, ctx, &cases[0])?;
Some(n + m)
}
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::stdlib::{nat_add, nat_lit, std_env};
fn ctx() -> Ctx { vec![] }
#[test]
fn omega_concrete_eq() {
let env = std_env();
let goal = Term::Ind(EQ_ID, vec![nat(), nat_lit(3), nat_lit(3)]);
assert!(omega_solve(&env, &ctx(), &goal).is_some());
}
#[test]
fn omega_add_concrete() {
let env = std_env();
let lhs = nat_add(nat_lit(2), nat_lit(3));
let rhs = nat_lit(5);
let goal = Term::Ind(EQ_ID, vec![nat(), lhs, rhs]);
assert!(omega_solve(&env, &ctx(), &goal).is_some());
}
#[test]
fn eval_nat_zero() {
let env = std_env();
assert_eq!(eval_nat(&env, &ctx(), &nat_zero()), Some(0));
}
#[test]
fn eval_nat_lit() {
let env = std_env();
for n in 0..=5u64 {
assert_eq!(eval_nat(&env, &ctx(), &nat_lit(n)), Some(n));
}
}
}