use symplex::prelude::*;
fn logic_ctx() -> Context {
static CTX: std::sync::OnceLock<Context> = std::sync::OnceLock::new();
CTX.get_or_init(Context::new).clone()
}
fn bool_true() -> BoolEx {
let ctx = logic_ctx();
ctx.int(1).gt(&ctx.int(0))
}
fn bool_false() -> BoolEx {
let ctx = logic_ctx();
ctx.int(0).gt(&ctx.int(1))
}
fn eval_str(b: &BoolEx) -> String {
format!("{}", b.eval())
}
#[test]
fn xor_truth_table() {
let t = bool_true();
let f = bool_false();
assert_eq!(eval_str(&t.xor(&t)), "False", "xor(T, T)");
assert_eq!(eval_str(&t.xor(&f)), "True", "xor(T, F)");
assert_eq!(eval_str(&f.xor(&t)), "True", "xor(F, T)");
assert_eq!(eval_str(&f.xor(&f)), "False", "xor(F, F)");
}
#[test]
fn xor_is_commutative() {
let ctx = Context::new();
let a = ctx.int(5).gt(&ctx.int(0)); let b = ctx.int(5).lt(&ctx.int(0));
assert_eq!(eval_str(&a.xor(&b)), eval_str(&b.xor(&a)));
}
#[test]
fn implies_truth_table() {
let t = bool_true();
let f = bool_false();
assert_eq!(eval_str(&t.implies(&t)), "True", "T → T");
assert_eq!(eval_str(&t.implies(&f)), "False", "T → F");
assert_eq!(eval_str(&f.implies(&t)), "True", "F → T");
assert_eq!(eval_str(&f.implies(&f)), "True", "F → F");
}
#[test]
fn implies_false_antecedent_always_true() {
let f = bool_false();
let t = bool_true();
assert_eq!(eval_str(&f.implies(&t)), "True");
assert_eq!(eval_str(&f.implies(&f)), "True");
}
#[test]
fn equivalent_truth_table() {
let t = bool_true();
let f = bool_false();
assert_eq!(eval_str(&t.equivalent(&t)), "True", "T ↔ T");
assert_eq!(eval_str(&t.equivalent(&f)), "False", "T ↔ F");
assert_eq!(eval_str(&f.equivalent(&t)), "False", "F ↔ T");
assert_eq!(eval_str(&f.equivalent(&f)), "True", "F ↔ F");
}
#[test]
fn equivalent_is_commutative() {
let t = bool_true();
let f = bool_false();
assert_eq!(eval_str(&t.equivalent(&f)), eval_str(&f.equivalent(&t)),);
}
#[test]
fn nand_truth_table() {
let t = bool_true();
let f = bool_false();
assert_eq!(eval_str(&t.nand(&t)), "False", "nand(T, T)");
assert_eq!(eval_str(&t.nand(&f)), "True", "nand(T, F)");
assert_eq!(eval_str(&f.nand(&t)), "True", "nand(F, T)");
assert_eq!(eval_str(&f.nand(&f)), "True", "nand(F, F)");
}
#[test]
fn nor_truth_table() {
let t = bool_true();
let f = bool_false();
assert_eq!(eval_str(&t.nor(&t)), "False", "nor(T, T)");
assert_eq!(eval_str(&t.nor(&f)), "False", "nor(T, F)");
assert_eq!(eval_str(&f.nor(&t)), "False", "nor(F, T)");
assert_eq!(eval_str(&f.nor(&f)), "True", "nor(F, F)");
}
#[test]
fn ite_selects_then_when_true() {
let cond = bool_true();
let then_ = bool_true();
let else_ = bool_false();
assert_eq!(eval_str(&cond.ite(&then_, &else_)), "True");
}
#[test]
fn ite_selects_else_when_false() {
let cond = bool_false();
let then_ = bool_true();
let else_ = bool_false();
assert_eq!(eval_str(&cond.ite(&then_, &else_)), "False");
}
#[test]
fn ite_all_combinations() {
let t = bool_true();
let f = bool_false();
assert_eq!(eval_str(&t.ite(&t, &f)), "True", "ite(T, T, F)");
assert_eq!(eval_str(&t.ite(&f, &t)), "False", "ite(T, F, T)");
assert_eq!(eval_str(&t.ite(&t, &t)), "True", "ite(T, T, T)");
assert_eq!(eval_str(&t.ite(&f, &f)), "False", "ite(T, F, F)");
assert_eq!(eval_str(&f.ite(&t, &f)), "False", "ite(F, T, F)");
assert_eq!(eval_str(&f.ite(&f, &t)), "True", "ite(F, F, T)");
assert_eq!(eval_str(&f.ite(&t, &t)), "True", "ite(F, T, T)");
assert_eq!(eval_str(&f.ite(&f, &f)), "False", "ite(F, F, F)");
}
#[test]
fn xor_equivalent_are_complementary() {
let pairs: Vec<(BoolEx, BoolEx)> = vec![
(bool_true(), bool_true()),
(bool_true(), bool_false()),
(bool_false(), bool_true()),
(bool_false(), bool_false()),
];
for (a, b) in &pairs {
let xor_val = eval_str(&a.xor(b));
let not_equiv = eval_str(&a.equivalent(b).not());
assert_eq!(xor_val, not_equiv, "xor(a,b) should equal ¬equivalent(a,b)");
}
}
#[test]
fn nand_from_not_and() {
let pairs: Vec<(BoolEx, BoolEx)> = vec![
(bool_true(), bool_true()),
(bool_true(), bool_false()),
(bool_false(), bool_true()),
(bool_false(), bool_false()),
];
for (a, b) in &pairs {
let via_method = eval_str(&a.nand(b));
let via_manual = eval_str(&a.and(b).not());
assert_eq!(via_method, via_manual, "nand should equal not-and");
}
}