use oxilean_kernel::Node;
use oxilean_kernel::{BinderInfo, Declaration, EnvError, Environment, Expr, Level, Literal, 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 pi(name: &str, dom: Expr, body: Expr) -> Expr {
Expr::Pi(
BinderInfo::Default,
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 int_const() -> Expr {
cst("Int")
}
fn forall1_int(body: Expr) -> Expr {
pi("a", int_const(), body)
}
fn forall2_int(body: Expr) -> Expr {
pi("a", int_const(), pi("b", int_const(), body))
}
fn forall3_int(body: Expr) -> Expr {
pi(
"a",
int_const(),
pi("b", int_const(), pi("c", int_const(), body)),
)
}
fn int_eq_expr(a: Expr, b: Expr) -> Expr {
let eq_const = Expr::Const(Name::str("Eq"), vec![Level::succ(Level::zero())]);
app(app(app(eq_const, int_const()), a), b)
}
fn int_zero() -> Expr {
app(cst("Int.ofNat"), Expr::Lit(Literal::nat(0)))
}
fn int_one() -> Expr {
app(cst("Int.ofNat"), Expr::Lit(Literal::nat(1)))
}
fn ty_add_comm() -> Expr {
forall2_int(int_eq_expr(
app2(cst("Int.add"), bvar(1), bvar(0)),
app2(cst("Int.add"), bvar(0), bvar(1)),
))
}
fn ty_add_assoc() -> Expr {
forall3_int(int_eq_expr(
app2(
cst("Int.add"),
app2(cst("Int.add"), bvar(2), bvar(1)),
bvar(0),
),
app2(
cst("Int.add"),
bvar(2),
app2(cst("Int.add"), bvar(1), bvar(0)),
),
))
}
fn ty_mul_comm() -> Expr {
forall2_int(int_eq_expr(
app2(cst("Int.mul"), bvar(1), bvar(0)),
app2(cst("Int.mul"), bvar(0), bvar(1)),
))
}
fn ty_mul_assoc() -> Expr {
forall3_int(int_eq_expr(
app2(
cst("Int.mul"),
app2(cst("Int.mul"), bvar(2), bvar(1)),
bvar(0),
),
app2(
cst("Int.mul"),
bvar(2),
app2(cst("Int.mul"), bvar(1), bvar(0)),
),
))
}
fn ty_left_distrib() -> Expr {
forall3_int(int_eq_expr(
app2(
cst("Int.mul"),
bvar(2),
app2(cst("Int.add"), bvar(1), bvar(0)),
),
app2(
cst("Int.add"),
app2(cst("Int.mul"), bvar(2), bvar(1)),
app2(cst("Int.mul"), bvar(2), bvar(0)),
),
))
}
fn ty_right_distrib() -> Expr {
forall3_int(int_eq_expr(
app2(
cst("Int.mul"),
app2(cst("Int.add"), bvar(2), bvar(1)),
bvar(0),
),
app2(
cst("Int.add"),
app2(cst("Int.mul"), bvar(2), bvar(0)),
app2(cst("Int.mul"), bvar(1), bvar(0)),
),
))
}
fn ty_add_zero() -> Expr {
forall1_int(int_eq_expr(
app2(cst("Int.add"), bvar(0), int_zero()),
bvar(0),
))
}
fn ty_zero_add() -> Expr {
forall1_int(int_eq_expr(
app2(cst("Int.add"), int_zero(), bvar(0)),
bvar(0),
))
}
fn ty_mul_one() -> Expr {
forall1_int(int_eq_expr(
app2(cst("Int.mul"), bvar(0), int_one()),
bvar(0),
))
}
fn ty_one_mul() -> Expr {
forall1_int(int_eq_expr(
app2(cst("Int.mul"), int_one(), bvar(0)),
bvar(0),
))
}
fn ty_mul_zero() -> Expr {
forall1_int(int_eq_expr(
app2(cst("Int.mul"), bvar(0), int_zero()),
int_zero(),
))
}
fn ty_zero_mul() -> Expr {
forall1_int(int_eq_expr(
app2(cst("Int.mul"), int_zero(), bvar(0)),
int_zero(),
))
}
fn ty_neg_add_cancel() -> Expr {
forall1_int(int_eq_expr(
app2(cst("Int.add"), app(cst("Int.neg"), bvar(0)), bvar(0)),
int_zero(),
))
}
fn ty_add_neg_cancel() -> Expr {
forall1_int(int_eq_expr(
app2(cst("Int.add"), bvar(0), app(cst("Int.neg"), bvar(0))),
int_zero(),
))
}
fn ty_mul_neg() -> Expr {
forall2_int(int_eq_expr(
app2(cst("Int.mul"), bvar(1), app(cst("Int.neg"), bvar(0))),
app(cst("Int.neg"), app2(cst("Int.mul"), bvar(1), bvar(0))),
))
}
pub fn register_polyrith_helper(env: &mut Environment) -> Result<(), EnvError> {
let lemmas: &[(&str, Expr)] = &[
("Int.add_comm", ty_add_comm()),
("Int.add_assoc", ty_add_assoc()),
("Int.mul_comm", ty_mul_comm()),
("Int.mul_assoc", ty_mul_assoc()),
("Int.left_distrib", ty_left_distrib()),
("Int.right_distrib", ty_right_distrib()),
("Int.add_zero", ty_add_zero()),
("Int.zero_add", ty_zero_add()),
("Int.mul_one", ty_mul_one()),
("Int.one_mul", ty_one_mul()),
("Int.mul_zero", ty_mul_zero()),
("Int.zero_mul", ty_zero_mul()),
("Int.neg_add_cancel", ty_neg_add_cancel()),
("Int.add_neg_cancel", ty_add_neg_cancel()),
("Int.mul_neg", ty_mul_neg()),
];
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 POLYRITH_LEMMAS: &[&str] = &[
"Int.add_comm",
"Int.add_assoc",
"Int.mul_comm",
"Int.mul_assoc",
"Int.left_distrib",
"Int.right_distrib",
"Int.add_zero",
"Int.zero_add",
"Int.mul_one",
"Int.one_mul",
"Int.mul_zero",
"Int.zero_mul",
"Int.neg_add_cancel",
"Int.add_neg_cancel",
"Int.mul_neg",
];
#[test]
fn test_register_polyrith_helper_success() {
let mut env = Environment::new();
register_polyrith_helper(&mut env).expect("registration should succeed");
}
#[test]
fn test_all_polyrith_lemmas_present() {
let mut env = Environment::new();
register_polyrith_helper(&mut env).expect("registration should succeed");
for name in POLYRITH_LEMMAS {
assert!(
env.contains(&Name::str(*name)),
"lemma {} should be registered",
name
);
}
}
#[test]
fn test_duplicate_registration_is_idempotent() {
let mut env = Environment::new();
register_polyrith_helper(&mut env).expect("first registration");
register_polyrith_helper(&mut env).expect("second registration should succeed");
for name in POLYRITH_LEMMAS {
assert!(
env.contains(&Name::str(*name)),
"lemma {} should still be registered after second call",
name
);
}
}
#[test]
fn test_polyrith_lemma_count_exact() {
let mut env = Environment::new();
register_polyrith_helper(&mut env).expect("registration should succeed");
assert_eq!(
env.len(),
POLYRITH_LEMMAS.len(),
"exactly {} polyrith lemmas expected",
POLYRITH_LEMMAS.len()
);
}
#[test]
fn test_add_comm_is_pi() {
let ty = ty_add_comm();
assert!(
matches!(ty, Expr::Pi(_, _, _, _)),
"add_comm type should be a Pi"
);
}
#[test]
fn test_mul_comm_is_pi() {
let ty = ty_mul_comm();
assert!(
matches!(ty, Expr::Pi(_, _, _, _)),
"mul_comm type should be a Pi"
);
}
#[test]
fn test_add_assoc_is_pi() {
let ty = ty_add_assoc();
assert!(
matches!(ty, Expr::Pi(_, _, _, _)),
"add_assoc type should be a Pi"
);
}
#[test]
fn test_mul_assoc_is_pi() {
let ty = ty_mul_assoc();
assert!(
matches!(ty, Expr::Pi(_, _, _, _)),
"mul_assoc type should be a Pi"
);
}
#[test]
fn test_left_distrib_is_pi() {
let ty = ty_left_distrib();
assert!(
matches!(ty, Expr::Pi(_, _, _, _)),
"left_distrib type should be a Pi"
);
}
#[test]
fn test_right_distrib_is_pi() {
let ty = ty_right_distrib();
assert!(
matches!(ty, Expr::Pi(_, _, _, _)),
"right_distrib type should be a Pi"
);
}
#[test]
fn test_add_zero_is_pi() {
let ty = ty_add_zero();
assert!(
matches!(ty, Expr::Pi(_, _, _, _)),
"add_zero type should be a Pi"
);
}
#[test]
fn test_mul_one_is_pi() {
let ty = ty_mul_one();
assert!(
matches!(ty, Expr::Pi(_, _, _, _)),
"mul_one type should be a Pi"
);
}
#[test]
fn test_neg_add_cancel_registered() {
let mut env = Environment::new();
register_polyrith_helper(&mut env).expect("registration should succeed");
assert!(
env.contains(&Name::str("Int.neg_add_cancel")),
"Int.neg_add_cancel should be registered"
);
}
#[test]
fn test_add_neg_cancel_registered() {
let mut env = Environment::new();
register_polyrith_helper(&mut env).expect("registration should succeed");
assert!(
env.contains(&Name::str("Int.add_neg_cancel")),
"Int.add_neg_cancel should be registered"
);
}
#[test]
fn test_mul_neg_registered() {
let mut env = Environment::new();
register_polyrith_helper(&mut env).expect("registration should succeed");
assert!(
env.contains(&Name::str("Int.mul_neg")),
"Int.mul_neg should be registered"
);
}
#[test]
fn test_coexistence_with_preregistered_lemmas() {
let mut env = Environment::new();
env.add(Declaration::Axiom {
name: Name::str("Int.add_comm"),
univ_params: vec![],
ty: ty_add_comm(),
})
.expect("pre-registration should succeed");
register_polyrith_helper(&mut env).expect("registration with pre-existing should succeed");
for name in POLYRITH_LEMMAS {
assert!(
env.contains(&Name::str(*name)),
"lemma {} should be present after coexistence test",
name
);
}
}
#[test]
fn test_env_find_after_registration() {
let mut env = Environment::new();
register_polyrith_helper(&mut env).expect("registration should succeed");
assert!(env.find(&Name::str("Int.add_comm")).is_some());
assert!(env.find(&Name::str("Int.mul_assoc")).is_some());
assert!(env.find(&Name::str("Int.left_distrib")).is_some());
assert!(env.find(&Name::str("Int.mul_neg")).is_some());
}
#[test]
fn test_idempotent_count_stays_15() {
let mut env = Environment::new();
register_polyrith_helper(&mut env).expect("first registration");
register_polyrith_helper(&mut env).expect("second registration");
assert_eq!(env.len(), 15, "expected exactly 15 polyrith lemmas");
}
}