use std::collections::HashMap;
use logicaffeine_kernel::Universe;
fn u(name: &str) -> Universe {
Universe::Var(name.to_string())
}
fn t(n: u32) -> Universe {
Universe::Type(n)
}
fn prop() -> Universe {
Universe::Prop
}
fn succ(l: Universe) -> Universe {
l.succ()
}
fn max(a: Universe, b: Universe) -> Universe {
a.max(&b)
}
fn subst(pairs: &[(&str, Universe)]) -> HashMap<String, Universe> {
pairs.iter().map(|(k, v)| (k.to_string(), v.clone())).collect()
}
#[test]
fn concrete_succ_and_max_unchanged() {
assert!(succ(t(0)).equiv(&t(1)), "succ(Type 0) = Type 1");
assert!(succ(prop()).equiv(&t(1)), "succ(Prop) = Type 1 (the kernel's convention)");
assert!(max(t(1), t(2)).equiv(&t(2)));
assert!(max(prop(), t(0)).equiv(&t(0)), "Prop is dominated by Type 0");
}
#[test]
fn concrete_cumulativity_unchanged() {
assert!(prop().is_subtype_of(&t(0)), "Prop ≤ Type 0");
assert!(prop().is_subtype_of(&prop()), "Prop ≤ Prop");
assert!(t(0).is_subtype_of(&t(1)), "Type 0 ≤ Type 1");
assert!(!t(1).is_subtype_of(&t(0)), "Type 1 ≰ Type 0");
assert!(!t(0).is_subtype_of(&prop()), "Type 0 ≰ Prop");
}
#[test]
fn variable_equality_is_by_name() {
assert!(u("a").equiv(&u("a")), "u ≡ u");
assert!(!u("a").equiv(&u("b")), "u ≢ v");
assert!(!u("a").equiv(&t(0)), "u ≢ Type 0 (a variable is not a concrete level)");
}
#[test]
fn the_algebra_collapses_in_normalization() {
assert!(max(u("a"), u("a")).equiv(&u("a")), "max(u,u) ≡ u");
assert!(max(u("a"), u("b")).equiv(&max(u("b"), u("a"))), "max(u,v) ≡ max(v,u)");
assert!(max(succ(u("a")), u("a")).equiv(&succ(u("a"))), "max(u+1, u) ≡ u+1");
assert!(succ(succ(t(0))).equiv(&t(2)));
assert!(!max(u("a"), t(0)).equiv(&u("a")), "max(u, Type 0) ≢ u (differ at u = Prop)");
assert!(
max(max(u("a"), u("b")), u("c")).equiv(&max(u("a"), max(u("b"), u("c")))),
"max is associative"
);
}
#[test]
fn variable_cumulativity_holds_only_when_universally_true() {
assert!(u("a").is_subtype_of(&u("a")), "u ≤ u");
assert!(prop().is_subtype_of(&u("a")), "Prop ≤ u (Prop is the bottom level)");
assert!(u("a").is_subtype_of(&succ(u("a"))), "u ≤ u+1");
assert!(!t(0).is_subtype_of(&u("a")), "Type 0 ≰ u (fails at u := Prop)");
assert!(!u("a").is_subtype_of(&u("b")), "u ≰ v (distinct variables)");
assert!(!t(1).is_subtype_of(&u("a")), "Type 1 ≰ u (fails at u := Prop)");
assert!(!succ(u("a")).is_subtype_of(&u("a")), "u+1 ≰ u");
assert!(!u("a").is_subtype_of(&prop()), "u ≰ Prop (u could be Type 0)");
}
#[test]
fn max_is_the_least_upper_bound() {
assert!(u("a").is_subtype_of(&max(u("a"), u("b"))), "u ≤ max(u,v)");
assert!(u("b").is_subtype_of(&max(u("a"), u("b"))), "v ≤ max(u,v)");
assert!(max(u("a"), u("b")).is_subtype_of(&max(u("b"), u("a"))));
assert!(!max(u("a"), u("b")).is_subtype_of(&u("a")), "max(u,v) ≰ u");
}
#[test]
fn substitution_instantiates_variables() {
assert!(u("a").substitute(&subst(&[("a", t(0))])).equiv(&t(0)), "u[a:=Type0] = Type 0");
assert!(
succ(u("a")).substitute(&subst(&[("a", t(1))])).equiv(&t(2)),
"(u+1)[u:=Type1] = Type 2"
);
assert!(
max(u("a"), t(5)).substitute(&subst(&[("a", t(0))])).equiv(&t(5)),
"max(u,Type5)[u:=Type0] = Type 5"
);
assert!(
u("a").substitute(&subst(&[("b", t(0))])).equiv(&u("a")),
"unrelated substitution is identity"
);
assert!(
max(u("a"), u("b")).substitute(&subst(&[("a", u("b"))])).equiv(&u("b")),
"max(u,v)[u:=v] ≡ v"
);
}