use logicaffeine_kernel::prelude::StandardLibrary;
use logicaffeine_kernel::{
defeq_for_test, double_check, infer_type, normalize, recheck, BigInt, Context, DoubleCheck,
Literal, Term, Universe,
};
fn g(n: &str) -> Term {
Term::Global(n.to_string())
}
fn v(n: &str) -> Term {
Term::Var(n.to_string())
}
fn app(f: Term, x: Term) -> Term {
Term::App(Box::new(f), Box::new(x))
}
fn apps(f: Term, xs: &[Term]) -> Term {
xs.iter().fold(f, |a, x| app(a, x.clone()))
}
fn lam(p: &str, t: Term, b: Term) -> Term {
Term::Lambda { param: p.to_string(), param_type: Box::new(t), body: Box::new(b) }
}
fn fix(name: &str, body: Term) -> Term {
Term::Fix { name: name.to_string(), body: Box::new(body) }
}
fn match_(d: Term, motive: Term, cases: Vec<Term>) -> Term {
Term::Match { discriminant: Box::new(d), motive: Box::new(motive), cases }
}
fn nat() -> Term {
g("Nat")
}
fn succ(x: Term) -> Term {
app(g("Succ"), x)
}
fn peano(n: u64) -> Term {
(0..n).fold(g("Zero"), |acc, _| succ(acc))
}
fn nat_lit(n: u64) -> Term {
Term::Lit(Literal::Nat(BigInt::from_i64(n as i64)))
}
fn std_ctx() -> Context {
let mut ctx = Context::new();
StandardLibrary::register(&mut ctx);
ctx
}
#[test]
fn nat_literal_is_defeq_to_peano_exhaustively() {
let ctx = std_ctx();
for n in 0u64..64 {
assert!(defeq_for_test(&ctx, &nat_lit(n), &peano(n)), "Nat({n}) ≡ Succ^{n} Zero");
assert!(defeq_for_test(&ctx, &peano(n), &nat_lit(n)), "symmetric at {n}");
assert!(
!defeq_for_test(&ctx, &nat_lit(n), &peano(n + 1)),
"Nat({n}) must NOT equal Succ^{} Zero",
n + 1
);
assert_eq!(infer_type(&ctx, &nat_lit(n)).unwrap(), nat(), "Nat({n}) : Nat");
}
}
#[test]
fn recursor_computes_on_nat_literals_agreeing_with_peano() {
let ctx = std_ctx();
let double = fix(
"rec",
lam(
"n",
nat(),
match_(
v("n"),
lam("_", nat(), nat()),
vec![g("Zero"), lam("k", nat(), succ(succ(app(v("rec"), v("k")))))],
),
),
);
for n in 0u64..24 {
let on_lit = normalize(&ctx, &app(double.clone(), nat_lit(n)));
let on_peano = normalize(&ctx, &app(double.clone(), peano(n)));
assert_eq!(on_lit, on_peano, "double agrees on Nat({n}) and Succ^{n} Zero");
assert!(
defeq_for_test(&ctx, &on_lit, &peano(2 * n)),
"double(Nat({n})) = Succ^{} Zero",
2 * n
);
}
}
#[test]
fn matching_a_nat_literal_selects_the_right_branch() {
let ctx = std_ctx();
let is_zero = lam(
"n",
nat(),
match_(v("n"), lam("_", nat(), g("Bool")), vec![g("true"), lam("_", nat(), g("false"))]),
);
assert_eq!(normalize(&ctx, &app(is_zero.clone(), nat_lit(0))), g("true"), "isZero (Nat 0) = true");
assert_eq!(normalize(&ctx, &app(is_zero.clone(), nat_lit(7))), g("false"), "isZero (Nat 7) = false");
assert_eq!(normalize(&ctx, &app(is_zero.clone(), peano(7))), g("false"), "isZero (Succ^7 Zero) = false");
}
#[test]
fn nat_bridge_is_two_kernel_verified() {
let ctx = std_ctx();
let eq_ty = apps(g("Eq"), &[nat(), nat_lit(3), peano(3)]);
let refl_proof = apps(g("refl"), &[nat(), peano(3)]);
let coerce = app(lam("h", eq_ty, g("Zero")), refl_proof);
assert_eq!(infer_type(&ctx, &coerce).unwrap(), nat(), "the coercion type-checks to Nat");
match double_check(&ctx, &coerce) {
DoubleCheck::Agreed => {}
other => panic!("both kernels must agree via the Nat bridge, got {other:?}"),
}
}
#[test]
fn negative_nat_literal_is_rejected_and_never_loops() {
let ctx = std_ctx();
let neg = Term::Lit(Literal::Nat(BigInt::from_i64(-5)));
assert!(infer_type(&ctx, &neg).is_err(), "main kernel rejects a negative Nat literal");
assert!(recheck(&ctx, &neg).is_err(), "re-checker rejects a negative Nat literal");
let is_zero = lam(
"n",
nat(),
match_(v("n"), lam("_", nat(), g("Bool")), vec![g("true"), lam("_", nat(), g("false"))]),
);
assert_eq!(normalize(&ctx, &app(is_zero, neg.clone())), g("true"), "negative Nat peels to Zero, no ∞ loop");
assert!(!defeq_for_test(&ctx, &neg, &peano(3)), "def_eq terminates on a negative Nat literal");
}
#[test]
fn large_nat_literal_stays_compact_and_computes() {
let ctx = std_ctx();
let huge = Term::Lit(Literal::Nat(BigInt::from_i64(1_000_000_000)));
assert_eq!(infer_type(&ctx, &huge).unwrap(), nat(), "Nat(10^9) : Nat");
let is_zero = lam(
"n",
nat(),
match_(v("n"), lam("_", nat(), g("Bool")), vec![g("true"), lam("_", nat(), g("false"))]),
);
assert_eq!(normalize(&ctx, &app(is_zero, huge)), g("false"), "isZero (Nat 10^9) = false, no unary blowup");
}