use oxilean_kernel::Node;
use oxilean_kernel::{BinderInfo, Declaration, EnvError, Environment, Expr, Level, Name};
fn app(f: Expr, a: Expr) -> Expr {
Expr::App(Node::new(f), Node::new(a))
}
fn app2(f: Expr, a: Expr, b: Expr) -> Expr {
app(app(f, a), b)
}
fn app3(f: Expr, a: Expr, b: Expr, c: Expr) -> Expr {
app(app2(f, a, b), c)
}
fn pi_default(name: &str, dom: Expr, body: Expr) -> Expr {
Expr::Pi(
BinderInfo::Default,
Name::str(name),
Node::new(dom),
Node::new(body),
)
}
fn pi_implicit(name: &str, dom: Expr, body: Expr) -> Expr {
Expr::Pi(
BinderInfo::Implicit,
Name::str(name),
Node::new(dom),
Node::new(body),
)
}
fn cst(s: &str) -> Expr {
Expr::Const(Name::str(s), vec![])
}
fn bvar(n: u32) -> Expr {
Expr::BVar(n)
}
fn prop() -> Expr {
Expr::Sort(Level::zero())
}
fn sort1() -> Expr {
Expr::Sort(Level::succ(Level::zero()))
}
fn eq_u1() -> Expr {
Expr::Const(Name::str("Eq"), vec![Level::succ(Level::zero())])
}
fn eq_app(alpha: Expr, a: Expr, b: Expr) -> Expr {
app3(eq_u1(), alpha, a, b)
}
fn heq_u1() -> Expr {
Expr::Const(Name::str("HEq"), vec![Level::succ(Level::zero())])
}
fn eq_prop() -> Expr {
Expr::Const(Name::str("Eq"), vec![Level::zero()])
}
fn eq_true_expr(p: Expr) -> Expr {
app3(eq_prop(), prop(), p, cst("True"))
}
fn eq_false_expr(p: Expr) -> Expr {
app3(eq_prop(), prop(), p, cst("False"))
}
fn ty_eq_symm() -> Expr {
pi_implicit(
"α",
sort1(),
pi_implicit(
"a",
bvar(0),
pi_implicit(
"b",
bvar(1),
pi_default(
"h",
eq_app(bvar(2), bvar(1), bvar(0)),
eq_app(bvar(3), bvar(1), bvar(2)),
),
),
),
)
}
fn ty_eq_trans() -> Expr {
pi_implicit(
"α",
sort1(),
pi_implicit(
"a",
bvar(0),
pi_implicit(
"b",
bvar(1),
pi_implicit(
"c",
bvar(2),
pi_default(
"h1",
eq_app(bvar(3), bvar(2), bvar(1)),
pi_default(
"h2",
eq_app(bvar(4), bvar(2), bvar(1)),
eq_app(bvar(5), bvar(4), bvar(2)),
),
),
),
),
),
)
}
fn ty_congr_arg() -> Expr {
pi_implicit(
"α",
sort1(),
pi_implicit(
"β",
sort1(),
pi_implicit(
"a",
bvar(1),
pi_implicit(
"b",
bvar(2),
pi_default(
"f",
pi_default("_", bvar(3), bvar(3)),
pi_default(
"h",
eq_app(bvar(4), bvar(3), bvar(2)),
eq_app(bvar(4), app(bvar(1), bvar(3)), app(bvar(1), bvar(2))),
),
),
),
),
),
)
}
fn ty_ne_symm() -> Expr {
pi_implicit(
"α",
sort1(),
pi_implicit(
"a",
bvar(0),
pi_implicit(
"b",
bvar(1),
pi_default(
"h",
pi_default("_", eq_app(bvar(2), bvar(1), bvar(0)), cst("False")),
pi_default("_", eq_app(bvar(3), bvar(1), bvar(2)), cst("False")),
),
),
),
)
}
fn ty_eq_of_heq() -> Expr {
pi_implicit(
"α",
sort1(),
pi_implicit(
"a",
bvar(0),
pi_implicit(
"b",
bvar(1),
pi_default(
"h",
app(app(app(app(heq_u1(), bvar(2)), bvar(1)), bvar(2)), bvar(0)),
eq_app(bvar(3), bvar(2), bvar(1)),
),
),
),
)
}
fn ty_eq_subst() -> Expr {
pi_implicit(
"α",
sort1(),
pi_implicit(
"a",
bvar(0),
pi_implicit(
"b",
bvar(1),
pi_default(
"p",
pi_default("_", bvar(2), prop()),
pi_default(
"h",
eq_app(bvar(3), bvar(2), bvar(1)),
pi_default(
"ha",
app(bvar(1), bvar(3)),
app(bvar(2), bvar(3)),
),
),
),
),
),
)
}
fn ty_congr() -> Expr {
pi_implicit(
"α",
sort1(),
pi_implicit(
"β",
sort1(),
pi_implicit(
"f",
pi_default("_", bvar(1), bvar(1)),
pi_implicit(
"g",
pi_default("_", bvar(2), bvar(2)),
pi_implicit(
"a",
bvar(3),
pi_implicit(
"b",
bvar(4),
pi_default(
"hf",
eq_app(pi_default("_", bvar(5), bvar(5)), bvar(3), bvar(2)),
pi_default(
"ha",
eq_app(bvar(6), bvar(2), bvar(1)),
eq_app(bvar(6), app(bvar(5), bvar(3)), app(bvar(4), bvar(2))),
),
),
),
),
),
),
),
)
}
fn ty_eq_true_intro() -> Expr {
pi_implicit("p", prop(), pi_default("h", bvar(0), eq_true_expr(bvar(1))))
}
fn ty_eq_false_intro() -> Expr {
pi_implicit(
"p",
prop(),
pi_default(
"h",
pi_default("_", bvar(0), cst("False")),
eq_false_expr(bvar(1)),
),
)
}
pub fn register_cc_helper(env: &mut Environment) -> Result<(), EnvError> {
let lemmas: &[(&str, Expr)] = &[
("Eq.symm", ty_eq_symm()),
("Eq.trans", ty_eq_trans()),
("congrArg", ty_congr_arg()),
("Ne.symm", ty_ne_symm()),
("eq_of_heq", ty_eq_of_heq()),
("Eq.subst", ty_eq_subst()),
("congr", ty_congr()),
("eq_true_intro", ty_eq_true_intro()),
("eq_false_intro", ty_eq_false_intro()),
];
for (name, ty) in lemmas {
let n = Name::str(*name);
if env.contains(&n) {
continue;
}
env.add(Declaration::Axiom {
name: n,
univ_params: vec![],
ty: ty.clone(),
})?;
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
const CC_LEMMAS: &[&str] = &[
"Eq.symm",
"Eq.trans",
"congrArg",
"Ne.symm",
"eq_of_heq",
"Eq.subst",
"congr",
"eq_true_intro",
"eq_false_intro",
];
#[test]
fn test_register_cc_helper_success() {
let mut env = Environment::new();
register_cc_helper(&mut env).expect("registration should succeed");
}
#[test]
fn test_all_lemmas_present() {
let mut env = Environment::new();
register_cc_helper(&mut env).expect("registration should succeed");
for name in CC_LEMMAS {
assert!(
env.contains(&Name::str(*name)),
"lemma {} should be registered",
name
);
}
}
#[test]
fn test_idempotent_registration() {
let mut env = Environment::new();
register_cc_helper(&mut env).expect("first registration");
register_cc_helper(&mut env).expect("second registration should succeed");
for name in CC_LEMMAS {
assert!(
env.contains(&Name::str(*name)),
"lemma {} should still be registered after second call",
name
);
}
}
#[test]
fn test_lemma_count_in_fresh_env() {
let mut env = Environment::new();
register_cc_helper(&mut env).expect("registration should succeed");
assert_eq!(env.len(), CC_LEMMAS.len(), "expected 9 cc lemmas");
}
#[test]
fn test_lemma_count_is_9() {
assert_eq!(CC_LEMMAS.len(), 9, "CC_LEMMAS should list 9 entries");
}
#[test]
fn test_eq_symm_present() {
let mut env = Environment::new();
register_cc_helper(&mut env).expect("registration should succeed");
assert!(
env.contains(&Name::str("Eq.symm")),
"Eq.symm should be registered"
);
}
#[test]
fn test_ne_symm_present() {
let mut env = Environment::new();
register_cc_helper(&mut env).expect("registration should succeed");
assert!(
env.contains(&Name::str("Ne.symm")),
"Ne.symm should be registered"
);
}
#[test]
fn test_eq_of_heq_present() {
let mut env = Environment::new();
register_cc_helper(&mut env).expect("registration should succeed");
assert!(
env.contains(&Name::str("eq_of_heq")),
"eq_of_heq should be registered"
);
}
#[test]
fn test_eq_symm_type_starts_with_implicit_pi() {
let ty = ty_eq_symm();
assert!(
matches!(ty, Expr::Pi(BinderInfo::Implicit, _, _, _)),
"Eq.symm type should start with an implicit Pi binder ({{α : Sort 1}})"
);
}
#[test]
fn test_ne_symm_type_starts_with_implicit_pi() {
let ty = ty_ne_symm();
assert!(
matches!(ty, Expr::Pi(BinderInfo::Implicit, _, _, _)),
"Ne.symm type should start with an implicit Pi binder ({{α : Sort 1}})"
);
}
#[test]
fn test_eq_of_heq_type_starts_with_implicit_pi() {
let ty = ty_eq_of_heq();
assert!(
matches!(ty, Expr::Pi(BinderInfo::Implicit, _, _, _)),
"eq_of_heq type should start with an implicit Pi binder ({{α : Sort 1}})"
);
}
#[test]
fn test_eq_true_intro_type_starts_with_implicit_pi() {
let ty = ty_eq_true_intro();
assert!(
matches!(ty, Expr::Pi(BinderInfo::Implicit, _, _, _)),
"eq_true_intro type should start with an implicit Pi binder ({{p : Prop}})"
);
}
#[test]
fn test_eq_false_intro_type_starts_with_implicit_pi() {
let ty = ty_eq_false_intro();
assert!(
matches!(ty, Expr::Pi(BinderInfo::Implicit, _, _, _)),
"eq_false_intro type should start with an implicit Pi binder ({{p : Prop}})"
);
}
#[test]
fn test_idempotent_count_stable() {
let mut env = Environment::new();
register_cc_helper(&mut env).expect("first registration");
let count_after_first = env.len();
register_cc_helper(&mut env).expect("second registration should succeed");
assert_eq!(
env.len(),
count_after_first,
"second registration must not add any new entries"
);
}
#[test]
fn test_coexistence_with_preregistered() {
let mut env = Environment::new();
let pre_existing = &[
("Eq.symm", ty_eq_symm()),
("Eq.trans", ty_eq_trans()),
("congrArg", ty_congr_arg()),
];
for (name, ty) in pre_existing {
env.add(Declaration::Axiom {
name: Name::str(*name),
univ_params: vec![],
ty: ty.clone(),
})
.expect("pre-registration should succeed");
}
register_cc_helper(&mut env).expect("registration with pre-existing should succeed");
for name in CC_LEMMAS {
assert!(
env.contains(&Name::str(*name)),
"lemma {} should be present after coexistence test",
name
);
}
assert_eq!(env.len(), 9, "total should be 9 after coexistence test");
}
#[test]
fn test_env_find_after_registration() {
let mut env = Environment::new();
register_cc_helper(&mut env).expect("registration should succeed");
assert!(env.find(&Name::str("Eq.symm")).is_some());
assert!(env.find(&Name::str("Ne.symm")).is_some());
assert!(env.find(&Name::str("eq_of_heq")).is_some());
assert!(env.find(&Name::str("congr")).is_some());
assert!(env.find(&Name::str("eq_true_intro")).is_some());
assert!(env.find(&Name::str("eq_false_intro")).is_some());
}
}