use std::collections::VecDeque;
use crate::constraint::VarId;
#[derive(Debug, Clone)]
pub struct NogoodStore<V: Clone + PartialEq> {
max_length: usize,
max_entries: usize,
store: VecDeque<Vec<(VarId, V)>>,
var_index: Vec<Vec<usize>>,
}
impl<V: Clone + PartialEq> NogoodStore<V> {
pub fn new(max_length: usize, max_entries: usize, num_vars: usize) -> Self {
Self {
max_length,
max_entries,
store: VecDeque::new(),
var_index: vec![Vec::new(); num_vars],
}
}
pub fn record(&mut self, conflict: &[(VarId, V)]) {
if conflict.len() > self.max_length {
return;
}
if self.store.len() >= self.max_entries {
self.evict_oldest();
}
let idx = self.store.len();
self.store.push_back(conflict.to_vec());
for &(var, _) in conflict {
let v = var as usize;
if v < self.var_index.len() {
self.var_index[v].push(idx);
}
}
}
pub fn is_nogood(&self, var: VarId, val: &V, assignment: &[Option<V>]) -> bool {
let v = var as usize;
if v >= self.var_index.len() {
return false;
}
for &ng_idx in &self.var_index[v] {
let nogood = &self.store[ng_idx];
let has_pair = nogood.iter().any(|(nv, nval)| *nv == var && nval == val);
if !has_pair {
continue;
}
let all_match = nogood.iter().all(|(nv, nval)| {
if *nv == var {
true
} else {
match &assignment[*nv as usize] {
Some(av) => av == nval,
None => false, }
}
});
if all_match {
return true;
}
}
false
}
pub fn clear(&mut self) {
self.store.clear();
for idx_list in &mut self.var_index {
idx_list.clear();
}
}
pub fn len(&self) -> usize {
self.store.len()
}
pub fn is_empty(&self) -> bool {
self.store.is_empty()
}
fn evict_oldest(&mut self) {
if let Some(evicted) = self.store.pop_front() {
self.rebuild_var_index_after_eviction(&evicted);
}
}
fn rebuild_var_index_after_eviction(&mut self, _evicted: &[(VarId, V)]) {
for idx_list in &mut self.var_index {
idx_list.clear();
}
for (ng_idx, nogood) in self.store.iter().enumerate() {
for &(var, _) in nogood {
let v = var as usize;
if v < self.var_index.len() {
self.var_index[v].push(ng_idx);
}
}
}
}
}