use logicaffeine_kernel::prelude::StandardLibrary;
use logicaffeine_kernel::{
derive_recursor, double_check, infer_type, is_subtype, normalize, Context, DoubleCheck, Term,
};
fn g(n: &str) -> Term {
Term::Global(n.to_string())
}
fn app(f: Term, x: Term) -> Term {
Term::App(Box::new(f), Box::new(x))
}
fn lam(p: &str, ty: Term, body: Term) -> Term {
Term::Lambda { param: p.to_string(), param_type: Box::new(ty), body: Box::new(body) }
}
fn std_ctx() -> Context {
let mut ctx = Context::new();
StandardLibrary::register(&mut ctx);
ctx
}
fn not_false() -> Term {
lam("x", g("False"), Term::Var("x".to_string()))
}
fn refl(ty: Term, x: Term) -> Term {
app(app(g("refl"), ty), x)
}
#[test]
fn decidable_layer_is_registered_and_well_typed() {
let ctx = std_ctx();
for name in ["Decidable", "isTrue", "isFalse", "Decidable_rec", "decide", "of_decide_eq_true"] {
assert!(
infer_type(&ctx, &g(name)).is_ok(),
"{name} must be registered and well-typed: {:?}",
infer_type(&ctx, &g(name))
);
}
}
#[test]
fn decide_computes_true_and_false() {
let ctx = std_ctx();
let dt = app(app(g("decide"), g("True")), app(app(g("isTrue"), g("True")), g("I")));
assert!(infer_type(&ctx, &dt).is_ok(), "decide True (isTrue …) must type-check");
assert_eq!(normalize(&ctx, &dt), g("true"), "decide of isTrue must compute to true");
let df = app(
app(g("decide"), g("False")),
app(app(g("isFalse"), g("False")), not_false()),
);
assert!(infer_type(&ctx, &df).is_ok(), "decide False (isFalse …) must type-check");
assert_eq!(normalize(&ctx, &df), g("false"), "decide of isFalse must compute to false");
}
#[test]
fn decidable_recursor_and_of_decide_are_derived_two_kernel_verified() {
let ctx = std_ctx();
let (_ty, rec) = derive_recursor(&ctx, "Decidable").expect("Decidable.rec derives");
assert_eq!(double_check(&ctx, &rec), DoubleCheck::Agreed, "Decidable_rec two-kernel");
assert!(ctx.is_definition("of_decide_eq_true"), "of_decide_eq_true must be derived, not an axiom");
let body = ctx.get_definition_body("of_decide_eq_true").expect("has a body").clone();
assert_eq!(double_check(&ctx, &body), DoubleCheck::Agreed, "of_decide_eq_true two-kernel");
let decl = ctx.get_definition_type("of_decide_eq_true").expect("has a type").clone();
let got = infer_type(&ctx, &body).expect("of_decide_eq_true body well-typed");
assert!(is_subtype(&ctx, &got, &decl), "of_decide_eq_true body must inhabit its declared type");
}
#[test]
fn of_decide_proves_a_true_decidable_proposition() {
let ctx = std_ctx();
let inst = app(app(g("isTrue"), g("True")), g("I"));
let proof = app(app(app(g("of_decide_eq_true"), g("True")), inst), refl(g("Bool"), g("true")));
let ty = infer_type(&ctx, &proof).expect("decide proof must type-check");
assert_eq!(normalize(&ctx, &ty), g("True"), "the decide proof must have type True");
}
fn dec_eq_bool(a: Term, b: Term) -> Term {
app(app(g("decEqBool"), a), b)
}
#[test]
fn dec_eq_bool_is_derived_and_two_kernel_verified() {
let ctx = std_ctx();
assert!(infer_type(&ctx, &g("decEqBool")).is_ok(), "decEqBool must be well-typed");
assert!(ctx.is_definition("decEqBool"), "decEqBool is a derived definition");
let body = ctx.get_definition_body("decEqBool").expect("has a body").clone();
assert_eq!(double_check(&ctx, &body), DoubleCheck::Agreed, "decEqBool two-kernel verified");
let decl = ctx.get_definition_type("decEqBool").expect("has a type").clone();
let got = infer_type(&ctx, &body).expect("decEqBool body well-typed");
assert!(is_subtype(&ctx, &got, &decl), "decEqBool body inhabits its declared type");
}
#[test]
fn dec_eq_bool_computes_the_verdict() {
let ctx = std_ctx();
let same = app(
app(g("decide"), app(app(app(g("Eq"), g("Bool")), g("true")), g("true"))),
dec_eq_bool(g("true"), g("true")),
);
assert_eq!(normalize(&ctx, &same), g("true"), "true = true decides true");
let diff = app(
app(g("decide"), app(app(app(g("Eq"), g("Bool")), g("true")), g("false"))),
dec_eq_bool(g("true"), g("false")),
);
assert_eq!(normalize(&ctx, &diff), g("false"), "true = false decides false");
}
#[test]
fn decide_proves_a_true_bool_equality() {
let ctx = std_ctx();
let prop = app(app(app(g("Eq"), g("Bool")), g("true")), g("true"));
let proof = app(
app(app(g("of_decide_eq_true"), prop.clone()), dec_eq_bool(g("true"), g("true"))),
refl(g("Bool"), g("true")),
);
let ty = infer_type(&ctx, &proof).expect("decide must prove true = true");
assert!(is_subtype(&ctx, &ty, &prop), "the proof has type Eq Bool true true");
}
#[test]
fn decide_is_fail_closed_on_a_false_bool_equality() {
let ctx = std_ctx();
let prop = app(app(app(g("Eq"), g("Bool")), g("true")), g("false"));
let bogus = app(
app(app(g("of_decide_eq_true"), prop), dec_eq_bool(g("true"), g("false"))),
refl(g("Bool"), g("true")),
);
assert!(infer_type(&ctx, &bogus).is_err(), "decide must NOT prove true = false");
}
fn nat_lit(n: usize) -> Term {
let mut t = g("Zero");
for _ in 0..n {
t = app(g("Succ"), t);
}
t
}
fn eqn(a: Term, b: Term) -> Term {
app(app(app(g("Eq"), g("Nat")), a), b)
}
fn dec_eq_nat(a: Term, b: Term) -> Term {
app(app(g("decEqNat"), a), b)
}
#[test]
fn dec_eq_nat_is_derived_and_two_kernel_verified() {
let ctx = std_ctx();
for name in ["decEqNat", "nat_zs_ne", "nat_sz_ne", "succ_cong", "succ_inj"] {
assert!(infer_type(&ctx, &g(name)).is_ok(), "{name} must be well-typed: {:?}", infer_type(&ctx, &g(name)));
assert!(ctx.is_definition(name), "{name} must be a derived definition (no axiom)");
let body = ctx.get_definition_body(name).unwrap_or_else(|| panic!("{name} body")).clone();
assert_eq!(double_check(&ctx, &body), DoubleCheck::Agreed, "{name} two-kernel verified");
let decl = ctx.get_definition_type(name).unwrap_or_else(|| panic!("{name} type")).clone();
let got = infer_type(&ctx, &body).unwrap_or_else(|_| panic!("{name} body type"));
assert!(is_subtype(&ctx, &got, &decl), "{name} body inhabits its declared type");
}
}
#[test]
fn dec_eq_nat_computes_the_verdict() {
let ctx = std_ctx();
let same = app(app(g("decide"), eqn(nat_lit(2), nat_lit(2))), dec_eq_nat(nat_lit(2), nat_lit(2)));
assert_eq!(normalize(&ctx, &same), g("true"), "2 = 2 decides true");
let diff = app(app(g("decide"), eqn(nat_lit(2), nat_lit(3))), dec_eq_nat(nat_lit(2), nat_lit(3)));
assert_eq!(normalize(&ctx, &diff), g("false"), "2 = 3 decides false");
}
#[test]
fn decide_proves_arithmetic_equality() {
let ctx = std_ctx();
let prop = eqn(nat_lit(2), nat_lit(2));
let proof = app(
app(app(g("of_decide_eq_true"), prop.clone()), dec_eq_nat(nat_lit(2), nat_lit(2))),
refl(g("Bool"), g("true")),
);
let ty = infer_type(&ctx, &proof).expect("decide must prove 2 = 2");
assert!(is_subtype(&ctx, &ty, &prop), "the proof has type Eq Nat 2 2");
}
#[test]
fn decide_is_fail_closed_on_false_arithmetic() {
let ctx = std_ctx();
let bogus = app(
app(app(g("of_decide_eq_true"), eqn(nat_lit(2), nat_lit(3))), dec_eq_nat(nat_lit(2), nat_lit(3))),
refl(g("Bool"), g("true")),
);
assert!(infer_type(&ctx, &bogus).is_err(), "decide must NOT prove 2 = 3");
}
#[test]
fn decide_is_fail_closed_on_a_false_proposition() {
let ctx = std_ctx();
let inst = app(app(g("isFalse"), g("False")), not_false());
let bogus = app(app(app(g("of_decide_eq_true"), g("False")), inst), refl(g("Bool"), g("true")));
assert!(
infer_type(&ctx, &bogus).is_err(),
"decide must NOT be able to prove False: the refl witness cannot type-check against \
Eq Bool false true"
);
}