use std::cmp::Ord;
use std::collections::HashMap;
use std::fmt::Debug;
use std::hash::Hash;
use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Not};
use simplify;
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum Expr<T>
where
T: Clone + Debug + Eq + Hash,
{
Terminal(T),
Const(bool),
Not(Box<Expr<T>>),
And(Box<Expr<T>>, Box<Expr<T>>),
Or(Box<Expr<T>>, Box<Expr<T>>),
}
impl<T> Expr<T>
where
T: Clone + Debug + Eq + Hash,
{
pub fn is_terminal(&self) -> bool {
match self {
&Expr::Terminal(_) => true,
_ => false,
}
}
pub fn is_const(&self) -> bool {
match self {
&Expr::Const(_) => true,
_ => false,
}
}
pub fn is_not(&self) -> bool {
match self {
&Expr::Not(_) => true,
_ => false,
}
}
pub fn is_and(&self) -> bool {
match self {
&Expr::And(_, _) => true,
_ => false,
}
}
pub fn is_or(&self) -> bool {
match self {
&Expr::Or(_, _) => true,
_ => false,
}
}
pub fn not(e: Expr<T>) -> Expr<T> {
Expr::Not(Box::new(e))
}
pub fn and(e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
Expr::And(Box::new(e1), Box::new(e2))
}
pub fn or(e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
Expr::Or(Box::new(e1), Box::new(e2))
}
pub fn xor(e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
let nand = !(e1.clone() & e2.clone());
let or = e1 | e2;
nand & or
}
pub fn evaluate(&self, vals: &HashMap<T, bool>) -> bool {
match self {
&Expr::Terminal(ref t) => *vals.get(t).unwrap_or(&false),
&Expr::Const(val) => val,
&Expr::And(ref a, ref b) => a.evaluate(vals) && b.evaluate(vals),
&Expr::Or(ref a, ref b) => a.evaluate(vals) || b.evaluate(vals),
&Expr::Not(ref x) => !x.evaluate(vals),
}
}
pub fn evaluate_with<F>(&self, f: F) -> bool
where
F: Fn(&T) -> bool,
{
self.evaluate_with1(&f)
}
fn evaluate_with1<F>(&self, f: &F) -> bool
where
F: Fn(&T) -> bool,
{
match self {
Expr::Terminal(t) => f(t),
Expr::Const(val) => *val,
Expr::And(a, b) => a.evaluate_with1(f) && b.evaluate_with1(f),
Expr::Or(a, b) => a.evaluate_with1(f) || b.evaluate_with1(f),
Expr::Not(x) => !x.evaluate_with1(f),
}
}
pub fn simplify_via_laws(self) -> Expr<T> {
simplify::simplify_via_laws(self)
}
pub fn simplify_via_bdd(self) -> Expr<T> {
simplify::simplify_via_bdd(self)
}
pub fn map<F, R>(&self, f: F) -> Expr<R>
where
F: Fn(&T) -> R,
R: Clone + Debug + Eq + Hash,
{
self.map1(&f)
}
fn map1<F, R>(&self, f: &F) -> Expr<R>
where
F: Fn(&T) -> R,
R: Clone + Debug + Eq + Hash,
{
match self {
&Expr::Terminal(ref t) => Expr::Terminal(f(t)),
&Expr::Const(val) => Expr::Const(val),
&Expr::Not(ref n) => Expr::not(n.map1(f)),
&Expr::And(ref a, ref b) => Expr::and(a.map1(f), b.map1(f)),
&Expr::Or(ref a, ref b) => Expr::or(a.map1(f), b.map1(f)),
}
}
}
impl<T> Not for Expr<T>
where
T: Clone + Debug + Eq + Hash,
{
type Output = Self;
fn not(self) -> Self::Output {
Self::not(self)
}
}
impl<T> BitAnd<Expr<T>> for Expr<T>
where
T: Clone + Debug + Eq + Hash,
{
type Output = Self;
fn bitand(self, rhs: Expr<T>) -> Self::Output {
Self::and(self, rhs)
}
}
impl<T> BitAndAssign<Expr<T>> for Expr<T>
where
T: Clone + Debug + Eq + Hash,
{
fn bitand_assign(&mut self, rhs: Expr<T>) {
*self = Self::and(self.clone(), rhs);
}
}
impl<T> BitOr<Expr<T>> for Expr<T>
where
T: Clone + Debug + Eq + Hash,
{
type Output = Self;
fn bitor(self, rhs: Expr<T>) -> Self::Output {
Self::or(self, rhs)
}
}
impl<T> BitOrAssign<Expr<T>> for Expr<T>
where
T: Clone + Debug + Eq + Hash,
{
fn bitor_assign(&mut self, rhs: Expr<T>) {
*self = Self::or(self.clone(), rhs);
}
}
impl<T> BitXor<Expr<T>> for Expr<T>
where
T: Clone + Debug + Eq + Hash,
{
type Output = Self;
fn bitxor(self, rhs: Expr<T>) -> Self::Output {
Self::xor(self, rhs)
}
}
impl<T> BitXorAssign<Expr<T>> for Expr<T>
where
T: Clone + Debug + Eq + Hash,
{
fn bitxor_assign(&mut self, rhs: Expr<T>) {
*self = Self::xor(self.clone(), rhs);
}
}