use crate::constraint::{Constraint, PropagationResult};
use crate::model::domain::TrailedDomains;
use crate::model::variable::VariableId;
use std::collections::{HashMap, HashSet};
#[derive(Debug, Clone)]
pub struct AllowedValues {
var: VariableId,
allowed: HashSet<i64>,
scope: [VariableId; 1],
}
impl AllowedValues {
pub fn new(var: VariableId, allowed_values: impl IntoIterator<Item = i64>) -> Self {
let allowed: HashSet<i64> = allowed_values.into_iter().collect();
Self {
var,
allowed,
scope: [var],
}
}
}
impl Constraint for AllowedValues {
fn name(&self) -> &str {
"AllowedValues"
}
fn scope(&self) -> &[VariableId] {
&self.scope
}
fn is_satisfied(&self, assignment: &HashMap<VariableId, i64>) -> bool {
match assignment.get(&self.var) {
Some(&val) => self.allowed.contains(&val),
None => true,
}
}
fn propagate(&self, domains: &mut TrailedDomains) -> PropagationResult {
let mut changed = false;
if let Some(domain) = domains.get_mut(&self.var) {
let current_values = domain.values();
for val in current_values {
if !self.allowed.contains(&val) && domain.remove(val) {
changed = true;
}
}
if domain.is_empty() {
return PropagationResult::Conflict;
}
}
PropagationResult::Success { changed }
}
}
#[derive(Debug, Clone)]
pub struct ForbiddenValues {
var: VariableId,
forbidden: HashSet<i64>,
scope: [VariableId; 1],
}
impl ForbiddenValues {
pub fn new(var: VariableId, forbidden_values: impl IntoIterator<Item = i64>) -> Self {
let forbidden: HashSet<i64> = forbidden_values.into_iter().collect();
Self {
var,
forbidden,
scope: [var],
}
}
}
impl Constraint for ForbiddenValues {
fn name(&self) -> &str {
"ForbiddenValues"
}
fn scope(&self) -> &[VariableId] {
&self.scope
}
fn is_satisfied(&self, assignment: &HashMap<VariableId, i64>) -> bool {
match assignment.get(&self.var) {
Some(&val) => !self.forbidden.contains(&val),
None => true,
}
}
fn propagate(&self, domains: &mut TrailedDomains) -> PropagationResult {
let mut changed = false;
if let Some(domain) = domains.get_mut(&self.var) {
for &val in &self.forbidden {
if domain.remove(val) {
changed = true;
}
}
if domain.is_empty() {
return PropagationResult::Conflict;
}
}
PropagationResult::Success { changed }
}
}