use std::fmt;
use std::fmt::Write;
use oxidd::util::AllocResult;
use oxidd::BooleanFunction;
use oxidd::BooleanFunctionQuant;
use oxidd::ManagerRef;
use oxidd::VarNo;
pub type Var = u32;
pub type VarSet = u32;
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum Prop {
False,
True,
Var(Var),
Not(Box<Prop>),
And(Box<Prop>, Box<Prop>),
Or(Box<Prop>, Box<Prop>),
Xor(Box<Prop>, Box<Prop>),
Equiv(Box<Prop>, Box<Prop>),
Nand(Box<Prop>, Box<Prop>),
Nor(Box<Prop>, Box<Prop>),
Imp(Box<Prop>, Box<Prop>),
ImpStrict(Box<Prop>, Box<Prop>),
Ite(Box<Prop>, Box<Prop>, Box<Prop>),
Restrict(VarSet, VarSet, Box<Prop>),
Exists(VarSet, Box<Prop>),
Forall(VarSet, Box<Prop>),
Unique(VarSet, Box<Prop>),
}
impl From<bool> for Prop {
fn from(value: bool) -> Self {
if value {
Prop::True
} else {
Prop::False
}
}
}
impl fmt::Display for Prop {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use Prop::*;
match self {
False => f.write_str("⊥"),
True => f.write_str("⊤"),
Var(i) => write!(f, "x{i}"),
Not(p) => write!(f, "(¬{p})"),
And(p, q) => write!(f, "({p} ∧ {q})"),
Or(p, q) => write!(f, "({p} ∨ {q})"),
Xor(p, q) => write!(f, "({p} ⊕ {q})"),
Equiv(p, q) => write!(f, "({p} ↔ {q})"),
Nand(p, q) => write!(f, "({p} ⊼ {q})"),
Nor(p, q) => write!(f, "({p} ⊽ {q})"),
Imp(p, q) => write!(f, "({p} → {q})"),
ImpStrict(p, q) => write!(f, "({p} < {q})"),
Ite(p, q, r) => write!(f, "ite({p}, {q}, {r})"),
Restrict(positive, negative, p) => {
write!(f, "({p}|{positive:b},{negative:b})")
}
Exists(vars, p) => write!(f, "∃ {vars:b}. {p}"),
Forall(vars, p) => write!(f, "∀ {vars:b}. {p}"),
Unique(vars, p) => write!(f, "∃! {vars:b}. {p}"),
}
}
}
#[derive(Clone, Copy, Debug)]
struct Diff {
expected: bool,
actual: bool,
args: u32,
}
#[derive(Clone)]
pub struct Error<F: fmt::Display> {
formula: F,
nvars: u32,
diffs: Vec<Diff>,
}
impl<F: fmt::Display> fmt::Debug for Error<F> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "differences for formula {}", self.formula)?;
for diff in &self.diffs {
f.write_str(" ")?;
for var in 0..self.nvars {
if var % 4 == 0 {
f.write_char(' ')?;
}
let c = if diff.args & (1 << var) != 0 {
'1'
} else {
'0'
};
f.write_char(c)?;
}
writeln!(f, " expected: {}, got: {}", diff.expected, diff.actual)?;
}
Ok(())
}
}
impl Prop {
fn eval(&self, args: u32) -> bool {
use Prop::*;
fn quant_inner<const Q: u8>(vars: u32, args: u32, p: &Prop) -> bool {
if vars == 0 {
p.eval(args)
} else {
let trailing = vars.trailing_zeros();
let v = 1 << trailing;
let vars = vars & !v;
match Q {
0 => {
quant_inner::<Q>(vars, args | v, p) || quant_inner::<Q>(vars, args & !v, p)
}
1 => {
quant_inner::<Q>(vars, args | v, p) && quant_inner::<Q>(vars, args & !v, p)
}
2 => quant_inner::<Q>(vars, args | v, p) ^ quant_inner::<Q>(vars, args & !v, p),
_ => unreachable!(),
}
}
}
match self {
False => false,
True => true,
Var(i) => args & (1 << *i) != 0,
Not(p) => !p.eval(args),
And(p, q) => p.eval(args) && q.eval(args),
Or(p, q) => p.eval(args) || q.eval(args),
Xor(p, q) => p.eval(args) ^ q.eval(args),
Equiv(p, q) => p.eval(args) == q.eval(args),
Nand(p, q) => !(p.eval(args) && q.eval(args)),
Nor(p, q) => !(p.eval(args) || q.eval(args)),
Imp(p, q) => !p.eval(args) || q.eval(args),
ImpStrict(p, q) => !p.eval(args) && q.eval(args),
Ite(i, t, e) => {
if i.eval(args) {
t.eval(args)
} else {
e.eval(args)
}
}
Restrict(positive, negative, p) => p.eval((args | positive) & !negative),
Exists(vars, p) => quant_inner::<0>(*vars, args, p),
Forall(vars, p) => quant_inner::<1>(*vars, args, p),
Unique(vars, p) => quant_inner::<2>(*vars, args, p),
}
}
fn cons<B: BooleanFunction>(&self, manager: &B::ManagerRef, vars: &[B]) -> AllocResult<B> {
use Prop::*;
match self {
False => Ok(manager.with_manager_shared(|m| B::f(m))),
True => Ok(manager.with_manager_shared(|m| B::t(m))),
Var(i) => Ok(vars[*i as usize].clone()),
Not(p) => p.cons(manager, vars)?.not(),
And(p, q) => p.cons(manager, vars)?.and(&q.cons(manager, vars)?),
Or(p, q) => p.cons(manager, vars)?.or(&q.cons(manager, vars)?),
Xor(p, q) => p.cons(manager, vars)?.xor(&q.cons(manager, vars)?),
Equiv(p, q) => p.cons(manager, vars)?.equiv(&q.cons(manager, vars)?),
Nand(p, q) => p.cons(manager, vars)?.nand(&q.cons(manager, vars)?),
Nor(p, q) => p.cons(manager, vars)?.nor(&q.cons(manager, vars)?),
Imp(p, q) => p.cons(manager, vars)?.imp(&q.cons(manager, vars)?),
ImpStrict(p, q) => p.cons(manager, vars)?.imp_strict(&q.cons(manager, vars)?),
Ite(i, t, e) => i
.cons(manager, vars)?
.ite(&t.cons(manager, vars)?, &e.cons(manager, vars)?),
Restrict(..) | Exists(..) | Forall(..) | Unique(..) => {
panic!("cons cannot construct quantification formulas, use `cons_q()` instead")
}
}
}
fn cons_q<B: BooleanFunctionQuant>(
&self,
manager: &B::ManagerRef,
vars: &[B],
) -> AllocResult<B> {
use Prop::*;
match self {
False => Ok(manager.with_manager_shared(|m| B::f(m))),
True => Ok(manager.with_manager_shared(|m| B::t(m))),
Var(i) => Ok(vars[*i as usize].clone()),
Not(p) => p.cons_q(manager, vars)?.not(),
And(p, q) => p.cons_q(manager, vars)?.and(&q.cons_q(manager, vars)?),
Or(p, q) => p.cons_q(manager, vars)?.or(&q.cons_q(manager, vars)?),
Xor(p, q) => p.cons_q(manager, vars)?.xor(&q.cons_q(manager, vars)?),
Equiv(p, q) => p.cons_q(manager, vars)?.equiv(&q.cons_q(manager, vars)?),
Nand(p, q) => p.cons_q(manager, vars)?.nand(&q.cons_q(manager, vars)?),
Nor(p, q) => p.cons_q(manager, vars)?.nor(&q.cons_q(manager, vars)?),
Imp(p, q) => p.cons_q(manager, vars)?.imp(&q.cons_q(manager, vars)?),
ImpStrict(p, q) => p
.cons_q(manager, vars)?
.imp_strict(&q.cons_q(manager, vars)?),
Ite(i, t, e) => i
.cons_q(manager, vars)?
.ite(&t.cons_q(manager, vars)?, &e.cons_q(manager, vars)?),
Restrict(mut pos, mut neg, p) => {
debug_assert_eq!(pos & neg, 0);
let prop = p.cons_q(manager, vars)?;
let mut v_dd = manager.with_manager_shared(|m| B::t(m));
let mut i = 0;
while pos | neg != 0 {
if pos & 1 != 0 {
v_dd = v_dd.and(&vars[i])?;
} else if neg & 1 != 0 {
v_dd = v_dd.and(&vars[i].not()?)?;
}
i += 1;
pos >>= 1;
neg >>= 1;
}
prop.restrict(&v_dd)
}
Exists(mut v, p) | Forall(mut v, p) | Unique(mut v, p) => {
let prop = p.cons_q(manager, vars)?;
let mut v_dd = manager.with_manager_shared(|m| B::t(m));
let mut i = 0;
while v != 0 {
if v & 1 != 0 {
v_dd = v_dd.and(&vars[i])?;
}
i += 1;
v >>= 1;
}
match self {
Exists(..) => prop.exists(&v_dd),
Forall(..) => prop.forall(&v_dd),
Unique(..) => prop.unique(&v_dd),
_ => unreachable!(),
}
}
}
}
pub fn build_and_check<B: BooleanFunction>(
&self,
manager: &B::ManagerRef,
vars: &[B],
) -> Result<(), Error<&Self>> {
assert!(
vars.len() < 31,
"Too many variables for exhaustive checking"
);
let end = 1u32 << vars.len();
let f = self.cons(manager, vars).expect("out of memory");
let mut diffs = Vec::new();
for args in 0..end {
let expected = self.eval(args);
let actual = f.eval((0..vars.len() as VarNo).map(|i| (i, args & (1 << i) != 0)));
if expected != actual {
diffs.push(Diff {
expected,
actual,
args,
})
}
}
if diffs.is_empty() {
Ok(())
} else {
Err(Error {
formula: self,
nvars: vars.len() as u32,
diffs,
})
}
}
pub fn build_and_check_quant<B: BooleanFunctionQuant>(
&self,
manager: &B::ManagerRef,
vars: &[B],
) -> Result<(), Error<&Self>> {
assert!(
vars.len() < 31,
"Too many variables for exhaustive checking"
);
let end = 1u32 << vars.len();
let f = self.cons_q(manager, vars).expect("out of memory");
let mut diffs = Vec::new();
for args in 0..end {
let expected = self.eval(args);
let actual = f.eval((0..vars.len() as VarNo).map(|i| (i, args & (1 << i) != 0)));
if expected != actual {
diffs.push(Diff {
expected,
actual,
args,
})
}
}
if diffs.is_empty() {
Ok(())
} else {
Err(Error {
formula: self,
nvars: vars.len() as u32,
diffs,
})
}
}
pub fn next<const QUANT: bool>(&mut self, depth: u32, nvars: u32) -> bool {
use Prop::*;
match self {
False => *self = True,
True => {
if nvars > 0 {
*self = Var(0);
} else if depth > 0 {
*self = Not(Box::new(False));
} else {
return false;
}
}
Var(i) => {
if *i + 1 < nvars {
*self = Var(*i + 1);
} else if depth > 0 {
*self = Not(Box::new(False));
} else {
return false;
}
}
Not(p) => {
if !p.next::<QUANT>(depth - 1, nvars) {
*self = And(Box::new(False), Box::new(False));
}
}
And(p, q) => {
if !q.next::<QUANT>(depth - 1, nvars) {
**q = False;
if !p.next::<QUANT>(depth - 1, nvars) {
*self = Or(Box::new(False), Box::new(False));
}
}
}
Or(p, q) => {
if !q.next::<QUANT>(depth - 1, nvars) {
**q = False;
if !p.next::<QUANT>(depth - 1, nvars) {
*self = Xor(Box::new(False), Box::new(False));
}
}
}
Xor(p, q) => {
if !q.next::<QUANT>(depth - 1, nvars) {
**q = False;
if !p.next::<QUANT>(depth - 1, nvars) {
*self = Equiv(Box::new(False), Box::new(False));
}
}
}
Equiv(p, q) => {
if !q.next::<QUANT>(depth - 1, nvars) {
**q = False;
if !p.next::<QUANT>(depth - 1, nvars) {
*self = Nand(Box::new(False), Box::new(False));
}
}
}
Nand(p, q) => {
if !q.next::<QUANT>(depth - 1, nvars) {
**q = False;
if !p.next::<QUANT>(depth - 1, nvars) {
*self = Nor(Box::new(False), Box::new(False));
}
}
}
Nor(p, q) => {
if !q.next::<QUANT>(depth - 1, nvars) {
**q = False;
if !p.next::<QUANT>(depth - 1, nvars) {
*self = Imp(Box::new(False), Box::new(False));
}
}
}
Imp(p, q) => {
if !q.next::<QUANT>(depth - 1, nvars) {
**q = False;
if !p.next::<QUANT>(depth - 1, nvars) {
*self = ImpStrict(Box::new(False), Box::new(False));
}
}
}
ImpStrict(p, q) => {
if !q.next::<QUANT>(depth - 1, nvars) {
**q = False;
if !p.next::<QUANT>(depth - 1, nvars) {
*self = Ite(Box::new(False), Box::new(False), Box::new(False));
}
}
}
Ite(i, t, e) => {
if !e.next::<QUANT>(depth - 1, nvars) {
**e = False;
if !t.next::<QUANT>(depth - 1, nvars) {
**t = False;
if !i.next::<QUANT>(depth - 1, nvars) {
if !QUANT {
return false;
}
*self = Restrict(0, 0, Box::new(False));
}
}
}
}
Restrict(positive, negative, p) => {
if *positive < (1 << nvars) - 1 {
*positive += 1;
loop {
let both = *positive & *negative;
if both == 0 {
return true;
}
let trailing = both.trailing_zeros();
if (1 << trailing) > (1 << nvars) - 1 - *positive {
break; }
*positive += 1 << trailing;
}
}
*positive = 0;
if *negative < (1 << nvars) - 1 {
*negative += 1;
return true;
}
*negative = 0;
if !p.next::<QUANT>(depth - 1, nvars) {
*self = Exists(0, Box::new(False))
}
}
Exists(v, p) => {
if *v < (1 << nvars) - 1 {
*v += 1;
return true;
}
*v = 0;
if !p.next::<QUANT>(depth - 1, nvars) {
*self = Forall(0, Box::new(False))
}
}
Forall(v, p) => {
if *v < (1 << nvars) - 1 {
*v += 1;
return true;
}
*v = 0;
if !p.next::<QUANT>(depth - 1, nvars) {
*self = Unique(0, Box::new(False))
}
}
Unique(v, p) => {
if *v < (1 << nvars) - 1 {
*v += 1;
return true;
}
*v = 0;
if !p.next::<QUANT>(depth - 1, nvars) {
return false;
}
}
}
true
}
}