use logicaffeine_proof::verify::prove_certify_check;
use logicaffeine_proof::{ProofExpr, ProofTerm};
fn k(n: &str) -> ProofTerm {
ProofTerm::Constant(n.to_string())
}
fn v(n: &str) -> ProofTerm {
ProofTerm::Variable(n.to_string())
}
fn p(n: &str, a: Vec<ProofTerm>) -> ProofExpr {
ProofExpr::Predicate { name: n.to_string(), args: a, world: None }
}
fn and(l: ProofExpr, r: ProofExpr) -> ProofExpr {
ProofExpr::And(Box::new(l), Box::new(r))
}
fn implies(l: ProofExpr, r: ProofExpr) -> ProofExpr {
ProofExpr::Implies(Box::new(l), Box::new(r))
}
fn not_(e: ProofExpr) -> ProofExpr {
ProofExpr::Not(Box::new(e))
}
fn forall(var: &str, body: ProofExpr) -> ProofExpr {
ProofExpr::ForAll { variable: var.to_string(), body: Box::new(body) }
}
fn eq(l: ProofTerm, r: ProofTerm) -> ProofExpr {
ProofExpr::Identity(l, r)
}
fn iff(l: ProofExpr, r: ProofExpr) -> ProofExpr {
ProofExpr::Iff(Box::new(l), Box::new(r))
}
#[test]
fn equality_reflexivity() {
let r = prove_certify_check(&[], &eq(k("A"), k("A")));
assert!(r.verified, "A = A should hold: {:?}", r.verification_error);
}
#[test]
fn equality_symmetry() {
let r = prove_certify_check(&[eq(k("A"), k("B"))], &eq(k("B"), k("A")));
assert!(r.verified, "A = B ⊢ B = A: {:?}", r.verification_error);
}
#[test]
fn equality_transitivity() {
let r = prove_certify_check(
&[eq(k("A"), k("B")), eq(k("B"), k("C"))],
&eq(k("A"), k("C")),
);
assert!(r.verified, "A = B, B = C ⊢ A = C: {:?}", r.verification_error);
}
#[test]
fn proof_by_cases() {
let pp = p("sunny", vec![k("A")]);
let qq = p("rainy", vec![k("A")]);
let rr = p("outside", vec![k("A")]);
let premises = [
ProofExpr::Or(Box::new(pp.clone()), Box::new(qq.clone())),
implies(pp.clone(), rr.clone()),
implies(qq.clone(), rr.clone()),
];
let r = prove_certify_check(&premises, &rr);
assert!(
r.verified,
"case analysis should conclude R: {:?}",
r.verification_error
);
}
#[test]
fn contradiction_ex_falso() {
let premises = [
p("hot", vec![k("A")]),
not_(p("hot", vec![k("A")])),
];
let goal = p("blue", vec![k("B")]);
let r = prove_certify_check(&premises, &goal);
assert!(
r.verified,
"a contradiction should prove anything: {:?}",
r.verification_error
);
}
#[test]
fn biconditional_forward() {
let premises = [
iff(p("rains", vec![k("A")]), p("wet", vec![k("A")])),
p("rains", vec![k("A")]),
];
let goal = p("wet", vec![k("A")]);
let r = prove_certify_check(&premises, &goal);
assert!(r.verified, "P ↔ Q, P ⊢ Q: {:?}", r.verification_error);
}
#[test]
fn implication_introduction_reflexive() {
let goal = implies(p("rains", vec![k("A")]), p("rains", vec![k("A")]));
let r = prove_certify_check(&[], &goal);
assert!(r.verified, "⊢ P → P: {:?}", r.verification_error);
}
#[test]
fn implication_introduction_weakening() {
let q = p("wet", vec![k("A")]);
let goal = implies(p("rains", vec![k("A")]), q.clone());
let r = prove_certify_check(&[q], &goal);
assert!(r.verified, "Q ⊢ P → Q: {:?}", r.verification_error);
}
#[test]
fn universal_introduction() {
let goal = forall(
"x",
implies(p("loved", vec![v("x")]), p("loved", vec![v("x")])),
);
let r = prove_certify_check(&[], &goal);
assert!(r.verified, "∀x.(loved(x) → loved(x)): {:?}", r.verification_error);
}
#[test]
fn universal_introduction_rejects_unsound_generalization() {
let premises = [p("man", vec![k("Socrates")])];
let goal = forall("x", p("man", vec![v("x")]));
let r = prove_certify_check(&premises, &goal);
assert!(
!r.verified,
"∀x.man(x) must NOT follow from man(Socrates)"
);
}
#[test]
fn biconditional_introduction_reflexive() {
let pp = p("rains", vec![k("A")]);
let goal = iff(pp.clone(), pp);
let r = prove_certify_check(&[], &goal);
assert!(r.verified, "⊢ P ↔ P: {:?}", r.verification_error);
}
#[test]
fn biconditional_introduction_from_directions() {
let pp = p("rains", vec![k("A")]);
let qq = p("wet", vec![k("A")]);
let premises = [
implies(pp.clone(), qq.clone()),
implies(qq.clone(), pp.clone()),
];
let goal = iff(pp, qq);
let r = prove_certify_check(&premises, &goal);
assert!(r.verified, "P→Q, Q→P ⊢ P↔Q: {:?}", r.verification_error);
}
#[test]
fn double_negation_introduction() {
let pp = p("rains", vec![k("A")]);
let goal = not_(not_(pp.clone()));
let r = prove_certify_check(&[pp], &goal);
assert!(r.verified, "P ⊢ ¬¬P: {:?}", r.verification_error);
}
#[test]
fn classical_double_negation_elimination() {
let pp = p("rains", vec![k("A")]);
let premises = [not_(not_(pp.clone()))];
let goal = pp;
let r = prove_certify_check(&premises, &goal);
assert!(r.verified, "¬¬P ⊢ P (classical): {:?}", r.verification_error);
}
#[test]
fn transitivity_chain() {
let trans = forall(
"x",
forall(
"y",
forall(
"z",
implies(
and(
p("less", vec![v("x"), v("y")]),
p("less", vec![v("y"), v("z")]),
),
p("less", vec![v("x"), v("z")]),
),
),
),
);
let premises = [
trans,
p("less", vec![k("A"), k("B")]),
p("less", vec![k("B"), k("C")]),
];
let goal = p("less", vec![k("A"), k("C")]);
let r = prove_certify_check(&premises, &goal);
assert!(
r.verified,
"transitivity should chain to less(A, C): {:?}",
r.verification_error
);
}
#[test]
fn equality_substitution() {
let premises = [
ProofExpr::Identity(k("A"), k("B")),
p("big", vec![k("A")]),
];
let goal = p("big", vec![k("B")]);
let r = prove_certify_check(&premises, &goal);
assert!(
r.verified,
"substitution of equals should give big(B): {:?}",
r.verification_error
);
}
#[test]
fn modus_tollens() {
let premises = [
implies(p("rains", vec![k("A")]), p("wet", vec![k("A")])),
not_(p("wet", vec![k("A")])),
];
let goal = not_(p("rains", vec![k("A")]));
let r = prove_certify_check(&premises, &goal);
assert!(
r.verified,
"modus tollens should give ¬rains(A): {:?}",
r.verification_error
);
}