use crate::variables::{VarId, Val};
use crate::model::Model;
use std::ops::{BitAnd, BitOr, Not};
#[doc(hidden)]
#[derive(Debug, Clone)]
pub struct BoolExpr {
pub(crate) operation: BoolOperation,
}
#[derive(Debug, Clone)]
pub(crate) enum BoolOperation {
Var(VarId),
And(Box<BoolExpr>, Box<BoolExpr>),
Or(Box<BoolExpr>, Box<BoolExpr>),
Not(Box<BoolExpr>),
}
impl BoolExpr {
pub fn apply_to(self, model: &mut Model) -> VarId {
match self.operation {
BoolOperation::Var(var_id) => var_id,
BoolOperation::And(left, right) => {
let left_var = left.apply_to(model);
let right_var = right.apply_to(model);
let result = model.bool();
let mut vars = Vec::with_capacity(2);
vars.push(left_var);
vars.push(right_var);
model.props.bool_and(vars, result);
result
}
BoolOperation::Or(left, right) => {
let left_var = left.apply_to(model);
let right_var = right.apply_to(model);
let result = model.bool();
let vars = vec![left_var, right_var];
model.props.bool_or(vars, result);
result
}
BoolOperation::Not(operand) => {
let operand_var = operand.apply_to(model);
let result = model.bool();
model.props.bool_not(operand_var, result);
result
}
}
}
pub fn must_be_true(self, model: &mut Model) {
let result_var = self.apply_to(model);
let _ = model.props.equals(result_var, Val::ValI(1));
}
pub fn must_be_false(self, model: &mut Model) {
let result_var = self.apply_to(model);
let _ = model.props.equals(result_var, Val::ValI(0));
}
}
impl From<VarId> for BoolExpr {
fn from(var_id: VarId) -> Self {
BoolExpr {
operation: BoolOperation::Var(var_id)
}
}
}
impl BitAnd for VarId {
type Output = BoolExpr;
fn bitand(self, rhs: Self) -> Self::Output {
BoolExpr {
operation: BoolOperation::And(
Box::new(BoolExpr::from(self)),
Box::new(BoolExpr::from(rhs))
)
}
}
}
impl BitOr for VarId {
type Output = BoolExpr;
fn bitor(self, rhs: Self) -> Self::Output {
BoolExpr {
operation: BoolOperation::Or(
Box::new(BoolExpr::from(self)),
Box::new(BoolExpr::from(rhs))
)
}
}
}
impl Not for VarId {
type Output = BoolExpr;
fn not(self) -> Self::Output {
BoolExpr {
operation: BoolOperation::Not(
Box::new(BoolExpr::from(self))
)
}
}
}
impl BitAnd for BoolExpr {
type Output = BoolExpr;
fn bitand(self, rhs: Self) -> Self::Output {
BoolExpr {
operation: BoolOperation::And(Box::new(self), Box::new(rhs))
}
}
}
impl BitAnd<BoolExpr> for VarId {
type Output = BoolExpr;
fn bitand(self, rhs: BoolExpr) -> Self::Output {
BoolExpr {
operation: BoolOperation::And(
Box::new(BoolExpr::from(self)),
Box::new(rhs)
)
}
}
}
impl BitAnd<VarId> for BoolExpr {
type Output = BoolExpr;
fn bitand(self, rhs: VarId) -> Self::Output {
BoolExpr {
operation: BoolOperation::And(
Box::new(self),
Box::new(BoolExpr::from(rhs))
)
}
}
}
impl BitOr for BoolExpr {
type Output = BoolExpr;
fn bitor(self, rhs: Self) -> Self::Output {
BoolExpr {
operation: BoolOperation::Or(Box::new(self), Box::new(rhs))
}
}
}
impl BitOr<BoolExpr> for VarId {
type Output = BoolExpr;
fn bitor(self, rhs: BoolExpr) -> Self::Output {
BoolExpr {
operation: BoolOperation::Or(
Box::new(BoolExpr::from(self)),
Box::new(rhs)
)
}
}
}
impl BitOr<VarId> for BoolExpr {
type Output = BoolExpr;
fn bitor(self, rhs: VarId) -> Self::Output {
BoolExpr {
operation: BoolOperation::Or(
Box::new(self),
Box::new(BoolExpr::from(rhs))
)
}
}
}
impl Not for BoolExpr {
type Output = BoolExpr;
fn not(self) -> Self::Output {
BoolExpr {
operation: BoolOperation::Not(Box::new(self))
}
}
}
#[doc(hidden)]
pub trait BooleanModel {
fn post_true(&mut self, expr: BoolExpr);
fn post_false(&mut self, expr: BoolExpr);
}
impl BooleanModel for Model {
fn post_true(&mut self, expr: BoolExpr) {
expr.must_be_true(self);
}
fn post_false(&mut self, expr: BoolExpr) {
expr.must_be_false(self);
}
}
#[cfg(test)]
mod tests {
use crate::prelude::*;
#[test]
fn test_bitwise_boolean_operators() {
let mut m = Model::default();
let a = m.bool();
let b = m.bool();
let c = m.bool();
m.new(a.eq(1)); m.new(b.eq(0)); m.new(c.eq(0));
let solution = m.solve().unwrap();
let a_val = if let crate::variables::Val::ValI(v) = solution[a] { v } else { 0 };
let b_val = if let crate::variables::Val::ValI(v) = solution[b] { v } else { 0 };
let c_val = if let crate::variables::Val::ValI(v) = solution[c] { v } else { 0 };
assert_eq!(a_val, 1);
assert_eq!(b_val, 0);
assert_eq!(c_val, 0);
}
}