use std::fmt;
use std::ops::{BitAnd, BitOr, BitXor, Not};
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum Formula {
Const(bool),
Var(String),
Not(Box<Formula>),
And(Box<Formula>, Box<Formula>),
Or(Box<Formula>, Box<Formula>),
Implies(Box<Formula>, Box<Formula>),
Iff(Box<Formula>, Box<Formula>),
Xor(Box<Formula>, Box<Formula>),
}
impl Formula {
pub fn var(name: impl Into<String>) -> Self {
Formula::Var(name.into())
}
pub fn constant(value: bool) -> Self {
Formula::Const(value)
}
pub fn and(self, other: Formula) -> Self {
Formula::And(Box::new(self), Box::new(other))
}
pub fn or(self, other: Formula) -> Self {
Formula::Or(Box::new(self), Box::new(other))
}
pub fn implies(self, other: Formula) -> Self {
Formula::Implies(Box::new(self), Box::new(other))
}
pub fn iff(self, other: Formula) -> Self {
Formula::Iff(Box::new(self), Box::new(other))
}
pub fn xor(self, other: Formula) -> Self {
Formula::Xor(Box::new(self), Box::new(other))
}
pub fn evaluate<F: Fn(&str) -> bool>(&self, value: &F) -> bool {
match self {
Formula::Const(c) => *c,
Formula::Var(name) => value(name),
Formula::Not(f) => !f.evaluate(value),
Formula::And(f, g) => f.evaluate(value) && g.evaluate(value),
Formula::Or(f, g) => f.evaluate(value) || g.evaluate(value),
Formula::Implies(f, g) => !f.evaluate(value) || g.evaluate(value),
Formula::Iff(f, g) => f.evaluate(value) == g.evaluate(value),
Formula::Xor(f, g) => f.evaluate(value) != g.evaluate(value),
}
}
pub fn variables(&self) -> Vec<&str> {
let mut out = Vec::new();
self.collect_variables(&mut out);
out
}
fn collect_variables<'a>(&'a self, out: &mut Vec<&'a str>) {
match self {
Formula::Const(_) => {}
Formula::Var(name) => {
if !out.contains(&name.as_str()) {
out.push(name);
}
}
Formula::Not(f) => f.collect_variables(out),
Formula::And(f, g)
| Formula::Or(f, g)
| Formula::Implies(f, g)
| Formula::Iff(f, g)
| Formula::Xor(f, g) => {
f.collect_variables(out);
g.collect_variables(out);
}
}
}
}
impl Not for Formula {
type Output = Formula;
fn not(self) -> Formula {
Formula::Not(Box::new(self))
}
}
impl BitAnd for Formula {
type Output = Formula;
fn bitand(self, other: Formula) -> Formula {
self.and(other)
}
}
impl BitOr for Formula {
type Output = Formula;
fn bitor(self, other: Formula) -> Formula {
self.or(other)
}
}
impl BitXor for Formula {
type Output = Formula;
fn bitxor(self, other: Formula) -> Formula {
self.xor(other)
}
}
impl fmt::Display for Formula {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let (op, l, r) = match self {
Formula::Const(true) => return write!(f, "T"),
Formula::Const(false) => return write!(f, "F"),
Formula::Var(name) => return write!(f, "{name}"),
Formula::Not(g) => return write!(f, "!{g}"),
Formula::And(l, r) => ("&", l, r),
Formula::Or(l, r) => ("|", l, r),
Formula::Implies(l, r) => ("->", l, r),
Formula::Iff(l, r) => ("<->", l, r),
Formula::Xor(l, r) => ("^", l, r),
};
write!(f, "({l} {op} {r})")
}
}