use crate::domain::Domain;
use crate::variable::Variable;
use super::traits::{Constraint, Revision, VarId};
#[derive(Debug)]
pub struct AllDifferentExcept<V: Clone + PartialEq + std::fmt::Debug> {
pub(crate) scope: Vec<VarId>,
pub(crate) sentinel: V,
}
impl<V: Clone + PartialEq + std::fmt::Debug> AllDifferentExcept<V> {
pub fn new(vars: Vec<VarId>, sentinel: V) -> Self {
Self { scope: vars, sentinel }
}
pub(crate) fn check_impl(&self, assignment: &[Option<V>]) -> bool {
let assigned: Vec<&V> = self
.scope
.iter()
.filter_map(|&v| assignment[v as usize].as_ref())
.filter(|val| **val != self.sentinel)
.collect();
for i in 0..assigned.len() {
for j in (i + 1)..assigned.len() {
if assigned[i] == assigned[j] {
return false;
}
}
}
true
}
pub(crate) fn revise_impl<D>(&self, vars: &mut [Variable<D>], depth: usize) -> Revision
where
D: Domain<Value = V>,
{
if self.scope.len() >= 4 {
return crate::solver::gac_alldiff_except::propagate_gac_alldiff_except(
&self.scope,
&self.sentinel,
vars,
depth,
);
}
let mut changed = false;
let singletons: Vec<(VarId, D::Value)> = self
.scope
.iter()
.filter_map(|&v| vars[v as usize].domain.singleton_value().map(|val| (v, val)))
.filter(|(_, val)| *val != self.sentinel)
.collect();
for (sv, sval) in &singletons {
for &other in &self.scope {
if other == *sv { continue; }
if vars[other as usize].prune(sval, depth) { changed = true; }
if vars[other as usize].domain.is_empty() { return Revision::Unsatisfiable; }
}
}
if changed { Revision::Changed } else { Revision::Unchanged }
}
}
impl<D> Constraint<D> for AllDifferentExcept<D::Value>
where
D: Domain,
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)
}
}