use logicaffeine_kernel::prelude::StandardLibrary;
use logicaffeine_kernel::{infer_type, is_subtype, Context, Term};
use logicaffeine_proof::certifier::{certify, CertificationContext};
use logicaffeine_proof::{DerivationTree, InferenceRule, ProofExpr, ProofTerm};
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 pi(param: &str, ty: Term, body: Term) -> Term {
Term::Pi { param: param.to_string(), param_type: Box::new(ty), body_type: Box::new(body) }
}
fn kvar(n: &str) -> Term {
Term::Var(n.to_string())
}
fn nat() -> Term {
g("Nat")
}
fn node() -> Term {
g("Entity") }
fn keq(a: Term, b: Term) -> Term {
app(app(app(g("Eq"), node()), a), b)
}
fn kmark(x: Term) -> Term {
app(g("mark"), x)
}
fn kpath(n: Term) -> Term {
app(g("path"), n)
}
fn klvl(n: Term) -> Term {
app(g("lvl"), n)
}
fn kesucc(x: Term) -> Term {
app(g("esucc"), x)
}
fn ksucc(n: Term) -> Term {
app(g("Succ"), n)
}
fn id(a: ProofTerm, b: ProofTerm) -> ProofExpr {
ProofExpr::Identity(a, b)
}
fn pmark(x: ProofTerm) -> ProofTerm {
ProofTerm::Function("mark".to_string(), vec![x])
}
fn ppath(n: ProofTerm) -> ProofTerm {
ProofTerm::Function("path".to_string(), vec![n])
}
fn plvl(n: ProofTerm) -> ProofTerm {
ProofTerm::Function("lvl".to_string(), vec![n])
}
fn pesucc(x: ProofTerm) -> ProofTerm {
ProofTerm::Function("esucc".to_string(), vec![x])
}
fn psucc(n: ProofTerm) -> ProofTerm {
ProofTerm::Function("Succ".to_string(), vec![n])
}
fn pk() -> ProofTerm {
ProofTerm::Variable("k".to_string())
}
fn pn() -> ProofTerm {
ProofTerm::Variable("n".to_string())
}
fn forall(v: &str, body: ProofExpr) -> ProofExpr {
ProofExpr::ForAll { variable: v.to_string(), body: Box::new(body) }
}
fn named(name: &str) -> DerivationTree {
DerivationTree::leaf(ProofExpr::Atom(name.to_string()), InferenceRule::PremiseMatch)
}
fn konig_context() -> Context {
let mut ctx = Context::new();
StandardLibrary::register(&mut ctx);
ctx.add_declaration("mark", pi("_", node(), node()));
ctx.add_declaration("path", pi("_", nat(), node()));
ctx.add_declaration("lvl", pi("_", nat(), node()));
ctx.add_declaration("esucc", pi("_", node(), node()));
ctx.add_declaration("mp0", keq(kmark(kpath(g("Zero"))), klvl(g("Zero"))));
ctx.add_declaration(
"mpS",
pi("k", nat(), keq(kmark(kpath(ksucc(kvar("k")))), kesucc(kmark(kpath(kvar("k")))))),
);
ctx.add_declaration(
"lvlS",
pi("k", nat(), keq(klvl(ksucc(kvar("k"))), kesucc(klvl(kvar("k"))))),
);
ctx
}
fn konig_step() -> DerivationTree {
let ui_mps = DerivationTree::new(
id(pmark(ppath(psucc(pk()))), pesucc(pmark(ppath(pk())))),
InferenceRule::UniversalInst("k".to_string()),
vec![named("mpS")],
);
let ih = DerivationTree::leaf(id(pmark(ppath(pk())), plvl(pk())), InferenceRule::PremiseMatch);
let rewritten = DerivationTree::new(
id(pmark(ppath(psucc(pk()))), pesucc(plvl(pk()))),
InferenceRule::Rewrite { from: pmark(ppath(pk())), to: plvl(pk()) },
vec![ih, ui_mps],
);
let ui_lvls = DerivationTree::new(
id(plvl(psucc(pk())), pesucc(plvl(pk()))),
InferenceRule::UniversalInst("k".to_string()),
vec![named("lvlS")],
);
let lvls_flip = DerivationTree::new(
id(pesucc(plvl(pk())), plvl(psucc(pk()))),
InferenceRule::EqualitySymmetry,
vec![ui_lvls],
);
DerivationTree::new(
id(pmark(ppath(psucc(pk()))), plvl(psucc(pk()))),
InferenceRule::EqualityTransitivity,
vec![rewritten, lvls_flip],
)
}
#[test]
fn the_konig_path_is_level_faithful_at_every_depth_kernel_certified() {
let ctx = konig_context();
let tree = DerivationTree::new(
forall("n", id(pmark(ppath(pn())), plvl(pn()))),
InferenceRule::StructuralInduction {
variable: "n".to_string(),
ind_type: "Nat".to_string(),
step_var: "k".to_string(),
},
vec![named("mp0"), konig_step()],
);
let cert_ctx = CertificationContext::new(&ctx);
let term = certify(&tree, &cert_ctx).expect("the ∀n König induction certifies to a Fix/Match term");
let inferred =
infer_type(&ctx, &term).expect("the certified König term must type-check in the kernel");
assert!(matches!(inferred, Term::Pi { .. }), "the certified term is universally quantified");
let goal = pi("n", nat(), keq(kmark(kpath(kvar("n"))), klvl(kvar("n"))));
assert!(
is_subtype(&ctx, &inferred, &goal),
"the inferred type is exactly ∀n:Nat. Eq Entity (mark (path n)) (lvl n): {inferred} ⊄ {goal}"
);
}
#[test]
fn a_base_that_mismatches_p_of_zero_fails_the_kernel_type_check() {
let mut ctx = Context::new();
StandardLibrary::register(&mut ctx);
ctx.add_declaration("mark", pi("_", node(), node()));
ctx.add_declaration("path", pi("_", nat(), node()));
ctx.add_declaration("lvl", pi("_", nat(), node()));
ctx.add_declaration("esucc", pi("_", node(), node()));
ctx.add_declaration("mp0", keq(kmark(kpath(g("Zero"))), klvl(ksucc(g("Zero")))));
ctx.add_declaration(
"mpS",
pi("k", nat(), keq(kmark(kpath(ksucc(kvar("k")))), kesucc(kmark(kpath(kvar("k")))))),
);
ctx.add_declaration("lvlS", pi("k", nat(), keq(klvl(ksucc(kvar("k"))), kesucc(klvl(kvar("k"))))));
let tree = DerivationTree::new(
forall("n", id(pmark(ppath(pn())), plvl(pn()))),
InferenceRule::StructuralInduction {
variable: "n".to_string(),
ind_type: "Nat".to_string(),
step_var: "k".to_string(),
},
vec![named("mp0"), konig_step()],
);
let cert_ctx = CertificationContext::new(&ctx);
let verified = matches!(
certify(&tree, &cert_ctx).and_then(|term| infer_type(&ctx, &term)),
Ok(Term::Pi { .. })
);
assert!(!verified, "an off-by-one base must NOT yield a kernel-checked ∀n theorem");
}
#[test]
fn skipping_the_induction_hypothesis_fails_the_kernel_type_check() {
let ctx = konig_context();
let ui_mps = DerivationTree::new(
id(pmark(ppath(psucc(pk()))), pesucc(pmark(ppath(pk())))),
InferenceRule::UniversalInst("k".to_string()),
vec![named("mpS")],
);
let ui_lvls = DerivationTree::new(
id(plvl(psucc(pk())), pesucc(plvl(pk()))),
InferenceRule::UniversalInst("k".to_string()),
vec![named("lvlS")],
);
let lvls_flip = DerivationTree::new(
id(pesucc(plvl(pk())), plvl(psucc(pk()))),
InferenceRule::EqualitySymmetry,
vec![ui_lvls],
);
let broken_step = DerivationTree::new(
id(pmark(ppath(psucc(pk()))), plvl(psucc(pk()))),
InferenceRule::EqualityTransitivity,
vec![ui_mps, lvls_flip],
);
let tree = DerivationTree::new(
forall("n", id(pmark(ppath(pn())), plvl(pn()))),
InferenceRule::StructuralInduction {
variable: "n".to_string(),
ind_type: "Nat".to_string(),
step_var: "k".to_string(),
},
vec![named("mp0"), broken_step],
);
let cert_ctx = CertificationContext::new(&ctx);
let verified = matches!(
certify(&tree, &cert_ctx).and_then(|term| infer_type(&ctx, &term)),
Ok(Term::Pi { .. })
);
assert!(!verified, "a step that skips the IH rewrite must NOT yield a kernel-checked ∀n theorem");
}