use super::sva_to_verify::BoundedExpr;
use logicaffeine_proof::ProofExpr;
#[inline]
fn boxed(e: ProofExpr) -> Box<ProofExpr> {
Box::new(e)
}
fn truth() -> ProofExpr {
let c = ProofExpr::Atom("__bool_const".to_string());
ProofExpr::Or(boxed(c.clone()), boxed(ProofExpr::Not(boxed(c))))
}
fn falsity() -> ProofExpr {
let c = ProofExpr::Atom("__bool_const".to_string());
ProofExpr::And(boxed(c.clone()), boxed(ProofExpr::Not(boxed(c))))
}
pub fn bounded_to_proof(e: &BoundedExpr) -> Option<ProofExpr> {
match e {
BoundedExpr::Var(name) => Some(ProofExpr::Atom(name.clone())),
BoundedExpr::Bool(true) => Some(truth()),
BoundedExpr::Bool(false) => Some(falsity()),
BoundedExpr::Not(p) => Some(ProofExpr::Not(boxed(bounded_to_proof(p)?))),
BoundedExpr::And(p, q) => {
Some(ProofExpr::And(boxed(bounded_to_proof(p)?), boxed(bounded_to_proof(q)?)))
}
BoundedExpr::Or(p, q) => {
Some(ProofExpr::Or(boxed(bounded_to_proof(p)?), boxed(bounded_to_proof(q)?)))
}
BoundedExpr::Implies(p, q) => {
Some(ProofExpr::Implies(boxed(bounded_to_proof(p)?), boxed(bounded_to_proof(q)?)))
}
BoundedExpr::Eq(p, q) => match (bounded_to_proof(p), bounded_to_proof(q)) {
(Some(a), Some(b)) => Some(ProofExpr::Iff(boxed(a), boxed(b))),
_ => super::bitblast::lower_bool(e),
},
_ => super::bitblast::lower_bool(e),
}
}
#[cfg(test)]
mod tests {
use super::*;
use logicaffeine_proof::sat::{prove_equivalence, EquivOutcome};
fn v(s: &str) -> BoundedExpr {
BoundedExpr::Var(s.to_string())
}
fn b(e: BoundedExpr) -> Box<BoundedExpr> {
Box::new(e)
}
#[test]
fn overlapping_implication_proves_equivalent() {
let sva = BoundedExpr::Implies(b(v("req@0")), b(v("ack@0")));
let fol = BoundedExpr::Implies(b(v("req@0")), b(v("ack@0")));
let p = bounded_to_proof(&sva).unwrap();
let q = bounded_to_proof(&fol).unwrap();
assert_eq!(prove_equivalence(&p, &q), EquivOutcome::Equivalent);
}
#[test]
fn implication_vs_consequent_differ() {
let sva = BoundedExpr::Implies(b(v("req@0")), b(v("ack@0")));
let other = v("ack@0");
let p = bounded_to_proof(&sva).unwrap();
let q = bounded_to_proof(&other).unwrap();
match prove_equivalence(&p, &q) {
EquivOutcome::Differ(_) => {}
o => panic!("expected Differ, got {:?}", o),
}
}
#[test]
fn safety_demorgan_equivalent() {
let lhs = BoundedExpr::Not(b(BoundedExpr::And(b(v("a@0")), b(v("b@0")))));
let rhs = BoundedExpr::Or(
b(BoundedExpr::Not(b(v("a@0")))),
b(BoundedExpr::Not(b(v("b@0")))),
);
let p = bounded_to_proof(&lhs).unwrap();
let q = bounded_to_proof(&rhs).unwrap();
assert_eq!(prove_equivalence(&p, &q), EquivOutcome::Equivalent);
}
#[test]
fn boolean_constants_lower_and_differ() {
let t = bounded_to_proof(&BoundedExpr::Bool(true)).unwrap();
let f = bounded_to_proof(&BoundedExpr::Bool(false)).unwrap();
match prove_equivalence(&t, &f) {
EquivOutcome::Differ(_) => {}
o => panic!("True vs False must Differ, got {:?}", o),
}
}
#[test]
fn datapath_returns_none() {
assert!(bounded_to_proof(&BoundedExpr::Int(5)).is_none());
assert!(bounded_to_proof(&BoundedExpr::BitVecVar("data@0".to_string(), 8)).is_none());
let cmp = BoundedExpr::Lt(b(BoundedExpr::Int(1)), b(BoundedExpr::Int(2)));
assert!(bounded_to_proof(&cmp).is_none());
}
}