#![warn(clippy::all, clippy::pedantic, clippy::nursery, clippy::cargo)]
use crate::sat::literal::Variable;
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum Expr {
Var(Variable),
Not(Box<Expr>),
And(Box<Expr>, Box<Expr>),
Or(Box<Expr>, Box<Expr>),
Val(bool),
}
impl Expr {
#[must_use]
pub const fn is_val(&self) -> bool {
matches!(self, Self::Val(_))
}
#[must_use]
pub const fn is_var(&self) -> bool {
matches!(self, Self::Var(_))
}
#[must_use]
pub const fn is_not(&self) -> bool {
matches!(self, Self::Not(_))
}
#[must_use]
pub const fn is_and(&self) -> bool {
matches!(self, Self::And(_, _))
}
#[must_use]
pub const fn is_or(&self) -> bool {
matches!(self, Self::Or(_, _))
}
#[must_use]
pub const fn is_true(&self) -> bool {
match self {
Self::Val(b) => *b,
_ => false,
}
}
#[must_use]
pub const fn is_false(&self) -> bool {
match self {
Self::Val(b) => !*b,
_ => false,
}
}
#[must_use]
pub fn unwrap_val(&self) -> bool {
match self {
Self::Val(b) => *b,
_ => panic!("Called unwrap_val on a non-Val expression: {self:?}"),
}
}
#[must_use]
pub fn unwrap_var(&self) -> Variable {
match self {
Self::Var(i) => *i,
_ => panic!("Called unwrap_var on a non-Var expression: {self:?}"),
}
}
#[must_use]
pub fn ors(expressions: &[Self]) -> Self {
if expressions.is_empty() {
return Self::Val(false);
}
let mut iter = expressions.iter();
let first = iter.next().unwrap().clone();
iter.fold(first, |acc, x| Self::Or(Box::new(acc), Box::new(x.clone())))
}
#[must_use]
pub fn ands(expressions: &[Self]) -> Self {
if expressions.is_empty() {
return Self::Val(true);
}
let mut iter = expressions.iter();
unsafe {
let first = iter.next().unwrap_unchecked().clone();
iter.fold(first, |acc, x| {
Self::And(Box::new(acc), Box::new(x.clone()))
})
}
}
}
impl From<bool> for Expr {
fn from(b: bool) -> Self {
Self::Val(b)
}
}
impl From<Variable> for Expr {
fn from(i: Variable) -> Self {
Self::Var(i)
}
}
impl From<i32> for Expr {
fn from(i: i32) -> Self {
let var_id = i.unsigned_abs();
if i < 0 {
Self::Not(Box::new(Self::Var(var_id)))
} else {
Self::Var(var_id)
}
}
}
#[must_use]
pub fn demorgans_laws(expr: &Expr) -> Expr {
match expr {
Expr::Not(e_boxed) => match e_boxed.as_ref() {
Expr::Var(i) => Expr::Not(Box::new(Expr::Var(*i))),
Expr::Not(inner_e_boxed) => demorgans_laws(inner_e_boxed.as_ref()),
Expr::And(e1, e2) => Expr::Or(
Box::new(demorgans_laws(&Expr::Not(e1.clone()))),
Box::new(demorgans_laws(&Expr::Not(e2.clone()))),
),
Expr::Or(e1, e2) => Expr::And(
Box::new(demorgans_laws(&Expr::Not(e1.clone()))),
Box::new(demorgans_laws(&Expr::Not(e2.clone()))),
),
Expr::Val(b) => Expr::Val(!*b),
},
Expr::And(e1, e2) => Expr::And(Box::new(demorgans_laws(e1)), Box::new(demorgans_laws(e2))),
Expr::Or(e1, e2) => Expr::Or(Box::new(demorgans_laws(e1)), Box::new(demorgans_laws(e2))),
Expr::Val(b) => Expr::Val(*b),
Expr::Var(i) => Expr::Var(*i),
}
}
#[must_use]
pub fn distributive_laws(expr: &Expr) -> Expr {
let current_expr = expr.clone();
match current_expr {
Expr::Or(e1_boxed, e2_boxed) => {
let e1_cnf = distributive_laws(&e1_boxed);
let e2_cnf = distributive_laws(&e2_boxed);
match (e1_cnf.clone(), e2_cnf.clone()) {
(Expr::And(e11, e12), _) => Expr::And(
Box::new(distributive_laws(&Expr::Or(e11, Box::new(e2_cnf.clone())))),
Box::new(distributive_laws(&Expr::Or(e12, Box::new(e2_cnf)))),
),
(_, Expr::And(e21, e22)) => Expr::And(
Box::new(distributive_laws(&Expr::Or(Box::new(e1_cnf.clone()), e21))),
Box::new(distributive_laws(&Expr::Or(Box::new(e1_cnf), e22))),
),
_ => Expr::Or(Box::new(e1_cnf), Box::new(e2_cnf)),
}
}
Expr::And(e1_boxed, e2_boxed) => Expr::And(
Box::new(distributive_laws(&e1_boxed)),
Box::new(distributive_laws(&e2_boxed)),
),
Expr::Not(inner) => Expr::Not(Box::new(distributive_laws(inner.as_ref()))),
Expr::Val(b) => Expr::Val(b),
Expr::Var(i) => Expr::Var(i),
}
}
#[must_use]
pub fn apply_laws(expr: &Expr) -> Expr {
let mut current_expr = expr.clone();
loop {
let nnf_expr = demorgans_laws(¤t_expr);
let distributed_expr = distributive_laws(&nnf_expr);
if distributed_expr == current_expr {
break;
}
current_expr = distributed_expr;
}
current_expr
}
#[cfg(test)]
mod tests {
use super::*;
fn var(id: u32) -> Expr {
Expr::Var(id)
}
fn not(expr: Expr) -> Expr {
Expr::Not(Box::new(expr))
}
fn and(e1: Expr, e2: Expr) -> Expr {
Expr::And(Box::new(e1), Box::new(e2))
}
fn or(e1: Expr, e2: Expr) -> Expr {
Expr::Or(Box::new(e1), Box::new(e2))
}
#[test]
fn test_demorgans_laws_not_and() {
let expr = not(and(var(1), var(2)));
let expected = or(not(var(1)), not(var(2)));
assert_eq!(demorgans_laws(&expr), expected);
}
#[test]
fn test_demorgans_laws_not_or() {
let expr = not(or(var(1), var(2)));
let expected = and(not(var(1)), not(var(2)));
assert_eq!(demorgans_laws(&expr), expected);
}
#[test]
fn test_demorgans_laws_double_negation() {
let expr = not(not(var(1)));
let expected = var(1);
assert_eq!(demorgans_laws(&expr), expected);
}
#[test]
fn test_demorgans_laws_not_val() {
let expr = not(Expr::Val(true));
let expected = Expr::Val(false);
assert_eq!(demorgans_laws(&expr), expected);
}
#[test]
fn test_demorgans_laws_nested() {
let expr = not(and(var(1), not(or(var(2), var(3)))));
let expected = or(not(var(1)), or(var(2), var(3)));
assert_eq!(demorgans_laws(&expr), expected);
}
#[test]
fn test_distributive_laws_a_and_or_b_c() {
let expr = and(or(var(1), var(2)), var(3));
let expected = and(or(var(1), var(2)), var(3));
assert_eq!(distributive_laws(&expr), expected);
}
#[test]
fn test_distributive_laws_or_a_b_and_c() {
let expr = and(or(var(1), var(2)), var(3));
let expected = and(or(var(1), var(2)), var(3));
assert_eq!(distributive_laws(&expr), expected);
}
#[test]
fn test_distributive_laws_no_change() {
let expr = and(var(1), var(2));
assert_eq!(distributive_laws(&expr), expr.clone());
let expr_or = or(var(1), var(2));
assert_eq!(distributive_laws(&expr_or), expr_or.clone());
}
#[test]
fn test_distributive_laws_handles_not_nnf() {
let expr = and(not(var(1)), or(var(2), var(3)));
let expected = and(not(var(1)), or(var(2), var(3)));
assert_eq!(distributive_laws(&expr), expected);
}
#[test]
fn test_apply_laws_with_demorgans() {
let expr = and(not(and(var(1), var(2))), var(3));
let expected = and(or(not(var(1)), not(var(2))), var(3));
assert_eq!(apply_laws(&expr), expected);
}
#[test]
fn test_ors_and_ands_helpers() {
assert_eq!(Expr::ors(&[]), Expr::Val(false));
assert_eq!(Expr::ands(&[]), Expr::Val(true));
let exprs = [var(1), var(2), var(3)];
let or_expr = Expr::ors(&exprs);
let expected_or = or(or(var(1), var(2)), var(3));
assert_eq!(or_expr, expected_or);
let and_expr = Expr::ands(&exprs);
let expected_and = and(and(var(1), var(2)), var(3));
assert_eq!(and_expr, expected_and);
assert_eq!(Expr::ors(&[var(1)]), var(1));
assert_eq!(Expr::ands(&[var(1)]), var(1));
}
}