use crate::domain::Domain;
use crate::variable::Variable;
use super::traits::{Constraint, Revision, VarId};
#[derive(Debug, Clone)]
pub struct ImplicationConstraint<V: Clone + PartialEq + std::fmt::Debug> {
pub(crate) scope: [VarId; 2],
pub(crate) trigger: V,
pub(crate) allowed: Vec<V>,
}
impl<V: Clone + PartialEq + std::fmt::Debug> ImplicationConstraint<V> {
pub fn new(antecedent: VarId, consequent: VarId, trigger: V, allowed: Vec<V>) -> Self {
Self {
scope: [antecedent, consequent],
trigger,
allowed,
}
}
pub(crate) fn check_impl(&self, assignment: &[Option<V>]) -> bool {
let ai = self.scope[0] as usize;
let ci = self.scope[1] as usize;
match (&assignment[ai], &assignment[ci]) {
(Some(a), Some(c)) if *a == self.trigger => self.allowed.contains(c),
_ => true,
}
}
pub(crate) fn revise_impl<D: Domain<Value = V>>(
&self,
vars: &mut [Variable<D>],
depth: usize,
) -> Revision {
let ai = self.scope[0] as usize;
let ci = self.scope[1] as usize;
let antecedent_bound = vars[ai].domain.singleton_value();
let triggered = matches!(antecedent_bound.as_ref(), Some(v) if *v == self.trigger);
if !triggered {
return Revision::Unchanged;
}
let mut changed = false;
let consequent_values: Vec<V> = vars[ci].domain.iter().collect();
for v in &consequent_values {
if !self.allowed.contains(v)
&& vars[ci].prune(v, depth)
{
changed = true;
}
}
if vars[ci].domain.is_empty() {
return Revision::Unsatisfiable;
}
if changed {
Revision::Changed
} else {
Revision::Unchanged
}
}
}
impl<D: Domain> Constraint<D> for ImplicationConstraint<D::Value>
where
D::Value: PartialEq,
{
fn scope(&self) -> &[VarId] {
&self.scope
}
fn check(&self, assignment: &[Option<D::Value>]) -> bool {
self.check_impl(assignment)
}
fn revise(&self, vars: &mut [Variable<D>], depth: usize) -> Revision {
self.revise_impl(vars, depth)
}
}