use std::{
mem,
num::NonZero,
ops::{Add, Mul, Not, Sub},
};
use crate::{
IntVal,
actions::{
BoolInspectionActions, BoolPropagationActions, BoolSimplificationActions, IntPropCond,
PropagationActions,
},
constraints::{Conflict, ReasonBuilder},
model::{
AdvRef, Advisor, ConRef, Model,
decision::Decision,
expressions::BoolFormula,
resolved::Resolved,
view::{DefaultView, View, private},
},
solver::{IntLitMeaning, activation_list::ActivationAction},
};
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[non_exhaustive]
pub enum BoolView {
Decision(Decision<bool>),
Const(bool),
IntEq(Decision<IntVal>, IntVal),
IntGreaterEq(Decision<IntVal>, IntVal),
IntLess(Decision<IntVal>, IntVal),
IntNotEq(Decision<IntVal>, IntVal),
}
impl Resolved<View<bool>> {
pub(crate) fn fix(
self,
ctx: &mut Model,
val: bool,
reason: impl ReasonBuilder<Model>,
) -> Result<(), Conflict<View<bool>>> {
let lit = if val { self.0 } else { !self.0 };
Resolved(lit).require(ctx, reason)
}
pub(crate) fn require(
self,
ctx: &mut Model,
reason: impl ReasonBuilder<Model>,
) -> Result<(), Conflict<View<bool>>> {
use BoolView::*;
match self.0.0 {
Decision(l) => {
let def = &mut ctx.bool_vars[l.idx()];
debug_assert!(def.alias.is_none());
def.alias = Some(View(Const(!l.is_negated())));
ctx.bool_events.push(l.var());
}
Const(c) => c.require(ctx, reason)?,
IntEq(iv, val) => iv.resolve_alias(ctx).fix(ctx, val, reason)?,
IntGreaterEq(iv, val) => iv.resolve_alias(ctx).tighten_min(ctx, val, reason)?,
IntLess(iv, val) => iv.resolve_alias(ctx).tighten_max(ctx, val - 1, reason)?,
IntNotEq(iv, val) => iv.resolve_alias(ctx).remove_val(ctx, val, reason)?,
};
Ok(())
}
pub(crate) fn unify(
self,
ctx: &mut Model,
other: Resolved<View<bool>>,
) -> Result<(), Conflict<View<bool>>> {
use BoolView::*;
let x = self.0;
let y = other.0;
match (x.0, y.0) {
(x, y) if x == y => Ok(()),
(Decision(xl), Decision(yl)) if xl.var() == yl.var() => {
Err(ctx.declare_conflict([x, y]))
}
(Const(x), Const(y)) if x != y => Err(ctx.declare_conflict([])),
(x, Const(b)) | (Const(b), x) => Resolved(View::<bool>(x)).fix(ctx, b, []),
(Decision(x), y) | (y, Decision(x)) => {
let (x, y) = if let Decision(y) = y {
if x.0.var() > y.0.var() {
(x, View(Decision(y)))
} else {
(y, View(Decision(x)))
}
} else {
(x, View(y))
};
let store = &mut ctx.bool_vars[x.idx()];
debug_assert_eq!(store.alias, None);
store.alias = Some(if x.is_negated() { !y } else { y });
let constraints = mem::take(&mut store.constraints);
match y.0 {
Decision(lit) => {
ctx.bool_vars[lit.idx()].constraints.extend(constraints);
}
IntEq(j, _) | IntGreaterEq(j, _) | IntLess(j, _) | IntNotEq(j, _) => {
for act in constraints {
let event = if matches!(y.0, IntEq(_, _) | IntNotEq(_, _)) {
IntPropCond::Domain
} else {
IntPropCond::Bounds
};
match ActivationAction::<AdvRef, ConRef>::from(act) {
ActivationAction::Advise(adv) => {
let def: &mut Advisor = &mut ctx.advisors[adv.index()];
def.condition = Some(match y.0 {
IntEq(_, v) => IntLitMeaning::Eq(v),
IntGreaterEq(_, v) => IntLitMeaning::GreaterEq(v),
IntLess(_, v) => IntLitMeaning::Less(v),
IntNotEq(_, v) => IntLitMeaning::NotEq(v),
_ => unreachable!(),
});
ctx.int_vars[j.idx()]
.constraints
.add(ActivationAction::Advise(adv), event);
}
me @ ActivationAction::Enqueue(_) => {
ctx.int_vars[j.idx()].constraints.add(me, event);
}
}
}
}
Const(_) => unreachable!(),
};
Ok(())
}
(x, y) => {
let x = BoolFormula::Atom(View(x));
let y = BoolFormula::Atom(View(y));
ctx.post_constraint_internal(BoolFormula::Equiv(vec![x, y]));
Ok(())
}
}
}
}
impl BoolInspectionActions<Model> for Resolved<View<bool>> {
fn val(&self, _ctx: &Model) -> Option<bool> {
use BoolView::*;
match self.0.0 {
Const(b) => Some(b),
_ => None,
}
}
}
impl Add<IntVal> for View<bool> {
type Output = View<IntVal>;
fn add(self, rhs: IntVal) -> Self::Output {
let me: View<IntVal> = self.into();
me + rhs
}
}
impl BoolInspectionActions<Model> for View<bool> {
fn val(&self, ctx: &Model) -> Option<bool> {
self.resolve_alias(ctx).val(ctx)
}
}
impl BoolPropagationActions<Model> for View<bool> {
fn fix(
&self,
ctx: &mut Model,
val: bool,
reason: impl ReasonBuilder<Model>,
) -> Result<(), Conflict<View<bool>>> {
self.resolve_alias(ctx).fix(ctx, val, reason)
}
fn require(
&self,
ctx: &mut Model,
reason: impl ReasonBuilder<Model>,
) -> Result<(), Conflict<View<bool>>> {
self.resolve_alias(ctx).require(ctx, reason)
}
}
impl BoolSimplificationActions<Model> for View<bool> {
fn unify(&self, ctx: &mut Model, other: impl Into<Self>) -> Result<(), Conflict<View<bool>>> {
let other = other.into().resolve_alias(ctx);
self.resolve_alias(ctx).unify(ctx, other)
}
}
impl From<Decision<bool>> for View<bool> {
fn from(decision: Decision<bool>) -> Self {
View(BoolView::Decision(decision))
}
}
impl From<Resolved<View<bool>>> for View<bool> {
fn from(value: Resolved<View<bool>>) -> Self {
value.into_inner()
}
}
impl From<bool> for View<bool> {
fn from(v: bool) -> Self {
View(BoolView::Const(v))
}
}
impl Mul<IntVal> for View<bool> {
type Output = View<IntVal>;
fn mul(self, rhs: IntVal) -> Self::Output {
let me: View<IntVal> = self.into();
me * rhs
}
}
impl Mul<NonZero<IntVal>> for View<bool> {
type Output = View<IntVal>;
fn mul(self, rhs: NonZero<IntVal>) -> Self::Output {
let me: View<IntVal> = self.into();
me * rhs
}
}
impl Not for View<bool> {
type Output = Self;
fn not(self) -> Self::Output {
use BoolView::*;
View(match self.0 {
Decision(l) => Decision(!l),
Const(b) => Const(!b),
IntEq(v, i) => IntNotEq(v, i),
IntGreaterEq(v, i) => IntLess(v, i),
IntLess(v, i) => IntGreaterEq(v, i),
IntNotEq(v, i) => IntEq(v, i),
})
}
}
impl Sub<IntVal> for View<bool> {
type Output = View<IntVal>;
fn sub(self, rhs: IntVal) -> Self::Output {
self + -rhs
}
}
impl DefaultView for bool {
type View = BoolView;
}
impl private::Sealed for bool {}