use std::collections::HashMap;
use crate::constraints::gac_sparseset::SparseSetGAC;
use crate::constraints::gac_bitset::BitSetGAC;
use crate::variables::domain::bitset_domain::{BitSetDomain, MAX_BITSET_DOMAIN_SIZE};
use crate::variables::domain::sparse_set::SparseSet;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Variable(pub usize);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Value(pub i32);
#[derive(Debug, Clone)]
pub struct GACStats {
pub total_variables: usize,
pub assigned_variables: usize,
pub total_domain_size: usize,
pub min_domain_size: usize,
pub max_domain_size: usize,
}
#[derive(Debug, Clone)]
pub enum DomainType {
BitSet(BitSetDomain),
SparseSet(SparseSet),
}
impl DomainType {
pub fn new(min_val: i32, max_val: i32) -> Self {
let domain_size = (max_val - min_val + 1) as usize;
if domain_size <= MAX_BITSET_DOMAIN_SIZE {
Self::BitSet(BitSetDomain::new(min_val, max_val))
} else {
Self::SparseSet(SparseSet::new(min_val, max_val))
}
}
pub fn new_from_values(values: Vec<i32>) -> Self {
if values.is_empty() {
return Self::SparseSet(SparseSet::new_from_values(values));
}
let min_val = *values.iter().min().unwrap();
let max_val = *values.iter().max().unwrap();
let range_size = (max_val - min_val + 1) as usize;
if range_size <= MAX_BITSET_DOMAIN_SIZE && values.len() > range_size / 2 {
Self::BitSet(BitSetDomain::new_from_values(values))
} else {
Self::SparseSet(SparseSet::new_from_values(values))
}
}
pub fn contains(&self, val: i32) -> bool {
match self {
Self::BitSet(bitset) => bitset.contains(val),
Self::SparseSet(sparse) => sparse.contains(val),
}
}
#[inline(always)]
pub fn contains_fast(&self, val: i32) -> bool {
match self {
Self::BitSet(bitset) => bitset.contains(val),
Self::SparseSet(sparse) => sparse.contains(val),
}
}
pub fn intersects_range(&self, min_val: i32, max_val: i32) -> bool {
match self {
Self::BitSet(bitset) => {
for val in min_val..=max_val {
if bitset.contains(val) {
return true;
}
}
false
}
Self::SparseSet(sparse) => {
sparse.iter().any(|val| val >= min_val && val <= max_val)
}
}
}
pub fn size(&self) -> usize {
match self {
Self::BitSet(bitset) => bitset.size(),
Self::SparseSet(sparse) => sparse.size(),
}
}
pub fn is_empty(&self) -> bool {
match self {
Self::BitSet(bitset) => bitset.is_empty(),
Self::SparseSet(sparse) => sparse.is_empty(),
}
}
pub fn is_fixed(&self) -> bool {
match self {
Self::BitSet(bitset) => bitset.is_fixed(),
Self::SparseSet(sparse) => sparse.is_fixed(),
}
}
pub fn min(&self) -> i32 {
match self {
Self::BitSet(bitset) => bitset.min().unwrap_or(0),
Self::SparseSet(sparse) => sparse.min(),
}
}
pub fn max(&self) -> i32 {
match self {
Self::BitSet(bitset) => bitset.max().unwrap_or(0),
Self::SparseSet(sparse) => sparse.max(),
}
}
pub fn remove(&mut self, val: i32) -> bool {
match self {
Self::BitSet(bitset) => bitset.remove(val),
Self::SparseSet(sparse) => sparse.remove(val),
}
}
pub fn iter(&self) -> Box<dyn Iterator<Item = i32> + '_> {
match self {
Self::BitSet(bitset) => Box::new(bitset.iter()),
Self::SparseSet(sparse) => Box::new(sparse.iter()),
}
}
}
#[derive(Debug, Clone)]
pub struct BipartiteGraph {
pub var_domains: HashMap<Variable, DomainType>,
pub value_vars: HashMap<Value, Vec<Variable>>,
}
impl Default for BipartiteGraph {
fn default() -> Self {
Self::new()
}
}
impl BipartiteGraph {
pub fn new() -> Self {
Self::with_capacity(16)
}
pub fn with_capacity(capacity: usize) -> Self {
Self {
var_domains: HashMap::with_capacity(capacity),
value_vars: HashMap::with_capacity(capacity * 4),
}
}
pub fn add_variable(&mut self, var: Variable, domain: Vec<i32>) {
let optimal_domain = DomainType::new_from_values(domain.clone());
self.var_domains.insert(var, optimal_domain);
for value in domain {
let val = Value(value);
self.value_vars.entry(val).or_default().push(var);
}
}
pub fn add_variable_range(&mut self, var: Variable, min_val: i32, max_val: i32) {
let optimal_domain = DomainType::new(min_val, max_val);
self.var_domains.insert(var, optimal_domain);
for val in min_val..=max_val {
self.value_vars.entry(Value(val)).or_default().push(var);
}
}
pub fn remove_value(&mut self, var: Variable, val: Value) -> bool {
let mut changed = false;
if let Some(domain) = self.var_domains.get_mut(&var) {
if domain.remove(val.0) {
changed = true;
}
}
if let Some(vars) = self.value_vars.get_mut(&val) {
if let Some(pos) = vars.iter().position(|&v| v == var) {
vars.swap_remove(pos);
changed = true;
}
}
changed
}
pub fn variables(&self) -> impl Iterator<Item = Variable> + '_ {
self.var_domains.keys().copied()
}
pub fn values(&self) -> impl Iterator<Item = Value> + '_ {
self.value_vars.keys().copied()
}
pub fn domain_size(&self, var: Variable) -> usize {
self.var_domains.get(&var).map_or(0, |d| d.size())
}
pub fn contains_value(&self, var: Variable, val: Value) -> bool {
self.var_domains.get(&var).map_or(false, |d| d.contains_fast(val.0))
}
pub fn domain(&self, var: Variable) -> Vec<Value> {
if let Some(domain) = self.var_domains.get(&var) {
domain.iter().map(|val| Value(val)).collect()
} else {
Vec::new()
}
}
pub fn domain_iter(&self, var: Variable) -> Box<dyn Iterator<Item = Value> + '_> {
if let Some(domain) = self.var_domains.get(&var) {
Box::new(domain.iter().map(|val| Value(val)))
} else {
Box::new(std::iter::empty())
}
}
pub fn domain_min(&self, var: Variable) -> Option<Value> {
self.var_domains.get(&var)
.filter(|d| !d.is_empty())
.map(|d| Value(d.min()))
}
pub fn domain_max(&self, var: Variable) -> Option<Value> {
self.var_domains.get(&var)
.filter(|d| !d.is_empty())
.map(|d| Value(d.max()))
}
pub fn is_assigned(&self, var: Variable) -> bool {
self.var_domains.get(&var).map_or(false, |d| d.is_fixed())
}
pub fn assigned_value(&self, var: Variable) -> Option<Value> {
let domain = self.var_domains.get(&var)?;
if domain.is_fixed() {
Some(Value(domain.min()))
} else {
None
}
}
pub fn intersect_domains(&self, var1: Variable, var2: Variable) -> Vec<Value> {
let (domain1, domain2) = match (self.var_domains.get(&var1), self.var_domains.get(&var2)) {
(Some(d1), Some(d2)) => (d1, d2),
_ => return Vec::new(),
};
match (domain1, domain2) {
(DomainType::BitSet(b1), DomainType::BitSet(b2)) => {
b1.iter()
.filter(|&val| b2.contains(val))
.map(Value)
.collect()
}
(DomainType::SparseSet(s1), DomainType::SparseSet(s2)) => {
let (smaller, larger) = if s1.size() <= s2.size() { (s1, s2) } else { (s2, s1) };
smaller.iter()
.filter(|&val| larger.contains(val))
.map(Value)
.collect()
}
_ => {
let (smaller, larger) = if domain1.size() <= domain2.size() {
(domain1, domain2)
} else {
(domain2, domain1)
};
smaller.iter()
.filter(|&val| larger.contains(val))
.map(Value)
.collect()
}
}
}
pub fn union_domains(&self, var1: Variable, var2: Variable) -> Vec<Value> {
let (domain1, domain2) = match (self.var_domains.get(&var1), self.var_domains.get(&var2)) {
(Some(d1), Some(d2)) => (d1, d2),
_ => return Vec::new(),
};
match (domain1, domain2) {
(DomainType::BitSet(b1), DomainType::BitSet(b2)) => {
let mut result: Vec<Value> = b1.iter().map(Value).collect();
result.extend(b2.iter().filter(|&val| !b1.contains(val)).map(Value));
result
}
_ => {
use std::collections::HashSet;
let mut result_set = HashSet::with_capacity(domain1.size() + domain2.size());
for val in domain1.iter() {
result_set.insert(Value(val));
}
for val in domain2.iter() {
result_set.insert(Value(val));
}
result_set.into_iter().collect()
}
}
}
pub fn domains_overlap(&self, var1: Variable, var2: Variable) -> bool {
let (domain1, domain2) = match (self.var_domains.get(&var1), self.var_domains.get(&var2)) {
(Some(d1), Some(d2)) => (d1, d2),
_ => return false,
};
match (domain1, domain2) {
(DomainType::BitSet(b1), DomainType::BitSet(b2)) => {
b1.iter().any(|val| b2.contains(val))
}
(DomainType::SparseSet(s1), DomainType::SparseSet(s2)) => {
let (smaller, larger) = if s1.size() <= s2.size() { (s1, s2) } else { (s2, s1) };
smaller.iter().any(|val| larger.contains(val))
}
_ => {
let (smaller, larger) = if domain1.size() <= domain2.size() {
(domain1, domain2)
} else {
(domain2, domain1)
};
smaller.iter().any(|val| larger.contains(val))
}
}
}
pub fn remove_values_bulk(&mut self, var: Variable, values_to_remove: &[i32]) -> bool {
if let Some(domain) = self.var_domains.get_mut(&var) {
let mut changed = false;
match domain {
DomainType::BitSet(bitset) => {
for &val in values_to_remove {
if bitset.remove(val) {
changed = true;
if let Some(vars) = self.value_vars.get_mut(&Value(val)) {
if let Some(pos) = vars.iter().position(|&v| v == var) {
vars.swap_remove(pos);
}
}
}
}
}
DomainType::SparseSet(sparse) => {
for &val in values_to_remove {
if sparse.remove(val) {
changed = true;
if let Some(vars) = self.value_vars.get_mut(&Value(val)) {
if let Some(pos) = vars.iter().position(|&v| v == var) {
vars.swap_remove(pos);
}
}
}
}
}
}
changed
} else {
false
}
}
}
#[derive(Debug)]
pub struct Matching {
pub var_to_val: HashMap<Variable, Value>,
pub val_to_var: HashMap<Value, Variable>,
}
impl Default for Matching {
fn default() -> Self {
Self::new()
}
}
impl Matching {
pub fn new() -> Self {
Self {
var_to_val: HashMap::with_capacity(16),
val_to_var: HashMap::with_capacity(16),
}
}
pub fn find_maximum_matching(graph: &BipartiteGraph) -> Self {
let mut matching = Self::new();
for var in graph.variables() {
if graph.is_assigned(var) {
if let Some(val) = graph.assigned_value(var) {
if !matching.val_to_var.contains_key(&val) {
matching.add_edge(var, val);
}
}
}
}
for var in graph.variables() {
if !matching.var_to_val.contains_key(&var) {
Self::find_augmenting_path(graph, &mut matching, var);
}
}
matching
}
fn add_edge(&mut self, var: Variable, val: Value) {
self.var_to_val.insert(var, val);
self.val_to_var.insert(val, var);
}
fn remove_edge(&mut self, var: Variable, val: Value) {
self.var_to_val.remove(&var);
self.val_to_var.remove(&val);
}
fn find_augmenting_path(graph: &BipartiteGraph, matching: &mut Self, start_var: Variable) -> bool {
let num_vars = graph.var_domains.len();
let num_vals = graph.value_vars.len();
if num_vars <= 64 && num_vals <= 128 {
Self::find_augmenting_path_bitset(graph, matching, start_var)
} else {
Self::find_augmenting_path_hashmap(graph, matching, start_var)
}
}
fn find_augmenting_path_bitset(graph: &BipartiteGraph, matching: &mut Self, start_var: Variable) -> bool {
use std::collections::VecDeque;
let mut queue: VecDeque<Variable> = VecDeque::new();
let mut visited_vars = 0u64; let mut visited_vals = 0u128; let mut parent_var: HashMap<Variable, Value> = HashMap::with_capacity(16);
let mut parent_val: HashMap<Value, Variable> = HashMap::with_capacity(16);
queue.push_back(start_var);
visited_vars |= 1u64 << start_var.0;
while let Some(current_var) = queue.pop_front() {
for val_int in graph.domain_iter(current_var) {
let val_bit = 1u128 << (val_int.0 as usize);
if (visited_vals & val_bit) != 0 {
continue;
}
visited_vals |= val_bit;
parent_val.insert(val_int, current_var);
if !matching.val_to_var.contains_key(&val_int) {
Self::apply_augmenting_path(matching, start_var, val_int, &parent_var, &parent_val);
return true;
}
if let Some(&matched_var) = matching.val_to_var.get(&val_int) {
let var_bit = 1u64 << matched_var.0;
if (visited_vars & var_bit) == 0 {
visited_vars |= var_bit;
parent_var.insert(matched_var, val_int);
queue.push_back(matched_var);
}
}
}
}
false
}
fn find_augmenting_path_hashmap(graph: &BipartiteGraph, matching: &mut Self, start_var: Variable) -> bool {
use std::collections::{VecDeque, HashSet};
let mut queue = VecDeque::new();
let mut visited_vars = HashSet::with_capacity(32);
let mut visited_vals = HashSet::with_capacity(64);
let mut parent_var: HashMap<Variable, Value> = HashMap::with_capacity(32);
let mut parent_val: HashMap<Value, Variable> = HashMap::with_capacity(32);
queue.push_back(start_var);
visited_vars.insert(start_var);
while let Some(current_var) = queue.pop_front() {
for val_int in graph.domain_iter(current_var) {
if visited_vals.contains(&val_int) {
continue;
}
visited_vals.insert(val_int);
parent_val.insert(val_int, current_var);
if !matching.val_to_var.contains_key(&val_int) {
Self::apply_augmenting_path(matching, start_var, val_int, &parent_var, &parent_val);
return true;
}
if let Some(&matched_var) = matching.val_to_var.get(&val_int) {
if !visited_vars.contains(&matched_var) {
visited_vars.insert(matched_var);
parent_var.insert(matched_var, val_int);
queue.push_back(matched_var);
}
}
}
}
false
}
fn apply_augmenting_path(
matching: &mut Self,
start_var: Variable,
end_val: Value,
parent_var: &HashMap<Variable, Value>,
parent_val: &HashMap<Value, Variable>,
) {
let mut current_val = end_val;
let mut current_var = start_var;
loop {
matching.add_edge(current_var, current_val);
if let Some(&prev_val) = parent_var.get(¤t_var) {
if let Some(&prev_var) = parent_val.get(&prev_val) {
matching.remove_edge(prev_var, prev_val);
current_var = prev_var;
current_val = prev_val;
} else {
break;
}
} else {
break;
}
}
}
pub fn is_complete(&self, graph: &BipartiteGraph) -> bool {
self.var_to_val.len() == graph.var_domains.len()
}
}
pub struct HybridGAC {
bitset_vars: HashMap<Variable, ()>,
sparseset_vars: HashMap<Variable, ()>,
bitset_gac: BitSetGAC,
sparseset_gac: SparseSetGAC,
}
impl Default for HybridGAC {
fn default() -> Self {
Self::new()
}
}
impl HybridGAC {
pub fn new() -> Self {
Self {
bitset_vars: HashMap::new(),
sparseset_vars: HashMap::new(),
bitset_gac: BitSetGAC::new(),
sparseset_gac: SparseSetGAC::new(),
}
}
pub fn add_variable(&mut self, var: Variable, min_val: i32, max_val: i32) -> Result<(), String> {
if min_val > max_val {
return Err(format!("Invalid range: min_val ({}) > max_val ({})", min_val, max_val));
}
let domain_size = (max_val - min_val + 1) as usize;
if domain_size <= MAX_BITSET_DOMAIN_SIZE {
self.bitset_gac.add_variable(var, min_val, max_val);
self.bitset_vars.insert(var, ());
} else {
self.sparseset_gac.add_variable(var, min_val, max_val);
self.sparseset_vars.insert(var, ());
}
Ok(())
}
pub fn add_variable_with_values(&mut self, var: Variable, values: Vec<i32>) -> Result<(), String> {
if values.is_empty() {
return Err("Cannot create domain from empty values".to_string());
}
let min_val = *values.iter().min().unwrap();
let max_val = *values.iter().max().unwrap();
let universe_size = (max_val - min_val + 1) as usize;
if universe_size <= MAX_BITSET_DOMAIN_SIZE {
self.bitset_gac.add_variable_with_values(var, values);
self.bitset_vars.insert(var, ());
} else {
self.sparseset_gac.add_variable_with_values(var, values);
self.sparseset_vars.insert(var, ());
}
Ok(())
}
pub fn remove_value(&mut self, var: Variable, val: i32) -> bool {
if self.bitset_vars.contains_key(&var) {
self.bitset_gac.remove_value(var, val)
} else if self.sparseset_vars.contains_key(&var) {
self.sparseset_gac.remove_value(var, val)
} else {
false
}
}
pub fn assign_variable(&mut self, var: Variable, val: i32) -> bool {
if self.bitset_vars.contains_key(&var) {
self.bitset_gac.assign_variable(var, val)
} else if self.sparseset_vars.contains_key(&var) {
self.sparseset_gac.assign_variable(var, val)
} else {
false
}
}
pub fn remove_above(&mut self, var: Variable, threshold: i32) -> bool {
if self.bitset_vars.contains_key(&var) {
self.bitset_gac.remove_above(var, threshold)
} else if self.sparseset_vars.contains_key(&var) {
self.sparseset_gac.remove_above(var, threshold)
} else {
false
}
}
pub fn remove_below(&mut self, var: Variable, threshold: i32) -> bool {
if self.bitset_vars.contains_key(&var) {
self.bitset_gac.remove_below(var, threshold)
} else if self.sparseset_vars.contains_key(&var) {
self.sparseset_gac.remove_below(var, threshold)
} else {
false
}
}
pub fn get_domain_values(&self, var: Variable) -> Vec<i32> {
if self.bitset_vars.contains_key(&var) {
self.bitset_gac.get_domain_values(var)
} else if self.sparseset_vars.contains_key(&var) {
self.sparseset_gac.get_domain_values(var)
} else {
Vec::new()
}
}
pub fn domain_size(&self, var: Variable) -> usize {
if self.bitset_vars.contains_key(&var) {
self.bitset_gac.domain_size(var)
} else if self.sparseset_vars.contains_key(&var) {
self.sparseset_gac.domains.get(&var).map_or(0, |d| d.size())
} else {
0
}
}
pub fn is_assigned(&self, var: Variable) -> bool {
if self.bitset_vars.contains_key(&var) {
self.bitset_gac.is_assigned(var)
} else if self.sparseset_vars.contains_key(&var) {
self.sparseset_gac.domains.get(&var).map_or(false, |d| d.is_fixed())
} else {
false
}
}
pub fn assigned_value(&self, var: Variable) -> Option<i32> {
if self.bitset_vars.contains_key(&var) {
self.bitset_gac.assigned_value(var)
} else if self.sparseset_vars.contains_key(&var) {
let domain = self.sparseset_gac.domains.get(&var)?;
if domain.is_fixed() {
Some(domain.min())
} else {
None
}
} else {
None
}
}
pub fn is_inconsistent(&self, var: Variable) -> bool {
if self.bitset_vars.contains_key(&var) {
self.bitset_gac.is_inconsistent(var)
} else if self.sparseset_vars.contains_key(&var) {
self.sparseset_gac.domains.get(&var).map_or(true, |d| d.is_empty())
} else {
true
}
}
pub fn get_bounds(&self, var: Variable) -> Option<(i32, i32)> {
if self.bitset_vars.contains_key(&var) {
let bounds = self.bitset_gac.get_bounds(var);
bounds
} else if self.sparseset_vars.contains_key(&var) {
let domain = self.sparseset_gac.domains.get(&var)?;
if !domain.is_empty() {
let bounds = Some((domain.min(), domain.max()));
bounds
} else {
None
}
} else {
None
}
}
pub fn propagate_alldiff(&mut self, variables: &[Variable]) -> (bool, bool) {
if variables.is_empty() {
return (false, true); }
let bitset_vars: Vec<Variable> = variables.iter()
.filter(|var| self.bitset_vars.contains_key(var))
.copied()
.collect();
let sparseset_vars: Vec<Variable> = variables.iter()
.filter(|var| self.sparseset_vars.contains_key(var))
.copied()
.collect();
let mut changed = false;
if !bitset_vars.is_empty() {
let (bitset_changed, bitset_consistent) = self.bitset_gac.propagate_alldiff(&bitset_vars);
if !bitset_consistent {
return (changed, false); }
changed |= bitset_changed;
}
if !sparseset_vars.is_empty() {
let (sparse_changed, sparse_consistent) = self.propagate_sparseset_alldiff(&sparseset_vars);
if !sparse_consistent {
return (changed, false); }
changed |= sparse_changed;
}
if !bitset_vars.is_empty() && !sparseset_vars.is_empty() {
let (cross_changed, cross_consistent) = self.cross_propagate_alldiff(&bitset_vars, &sparseset_vars);
if !cross_consistent {
return (changed, false); }
changed |= cross_changed;
}
(changed, true)
}
fn propagate_sparseset_alldiff(&mut self, variables: &[Variable]) -> (bool, bool) {
let mut changed = false;
let assigned_values: Vec<(Variable, i32)> = variables
.iter()
.filter_map(|&var| {
let domain = self.sparseset_gac.domains.get(&var)?;
if domain.is_fixed() {
Some((var, domain.min()))
} else {
None
}
})
.collect();
for (assigned_var, assigned_val) in assigned_values {
for &var in variables {
if var != assigned_var {
if self.sparseset_gac.remove_value(var, assigned_val) {
changed = true;
if self.sparseset_gac.domains.get(&var).unwrap().is_empty() {
return (changed, false); }
}
}
}
}
(changed, true)
}
fn cross_propagate_alldiff(&mut self, bitset_vars: &[Variable], sparseset_vars: &[Variable]) -> (bool, bool) {
let mut changed = false;
let bitset_assigned: Vec<i32> = bitset_vars
.iter()
.filter_map(|&var| self.bitset_gac.assigned_value(var))
.collect();
for assigned_val in &bitset_assigned {
for &var in sparseset_vars {
if self.sparseset_gac.remove_value(var, *assigned_val) {
changed = true;
if self.sparseset_gac.domains.get(&var).unwrap().is_empty() {
return (changed, false); }
}
}
}
let sparseset_assigned: Vec<i32> = sparseset_vars
.iter()
.filter_map(|&var| {
let domain = self.sparseset_gac.domains.get(&var)?;
if domain.is_fixed() {
Some(domain.min())
} else {
None
}
})
.collect();
for assigned_val in &sparseset_assigned {
for &var in bitset_vars {
if self.bitset_gac.remove_value(var, *assigned_val) {
changed = true;
if self.bitset_gac.is_inconsistent(var) {
return (changed, false); }
}
}
}
(changed, true)
}
pub fn variables(&self) -> impl Iterator<Item = Variable> + '_ {
self.bitset_vars.keys().chain(self.sparseset_vars.keys()).copied()
}
pub fn get_stats(&self) -> (usize, usize) {
(self.bitset_vars.len(), self.sparseset_vars.len())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_hybrid_gac_small_domains() {
let mut gac = HybridGAC::new();
gac.add_variable(Variable(0), 1, 10).unwrap();
gac.add_variable(Variable(1), 1, 10).unwrap();
assert!(gac.bitset_vars.contains_key(&Variable(0)));
assert!(gac.bitset_vars.contains_key(&Variable(1)));
let (bitset_count, sparseset_count) = gac.get_stats();
assert_eq!(bitset_count, 2);
assert_eq!(sparseset_count, 0);
}
#[test]
fn test_hybrid_gac_large_domains() {
let mut gac = HybridGAC::new();
gac.add_variable(Variable(0), 1, 150).unwrap();
gac.add_variable(Variable(1), 1, 200).unwrap();
assert!(gac.sparseset_vars.contains_key(&Variable(0)));
assert!(gac.sparseset_vars.contains_key(&Variable(1)));
let (bitset_count, sparseset_count) = gac.get_stats();
assert_eq!(bitset_count, 0);
assert_eq!(sparseset_count, 2);
}
#[test]
fn test_hybrid_gac_mixed() {
let mut gac = HybridGAC::new();
gac.add_variable(Variable(0), 1, 64).unwrap(); gac.add_variable(Variable(1), 1, 150).unwrap();
assert!(gac.bitset_vars.contains_key(&Variable(0)));
assert!(gac.sparseset_vars.contains_key(&Variable(1)));
let (bitset_count, sparseset_count) = gac.get_stats();
assert_eq!(bitset_count, 1);
assert_eq!(sparseset_count, 1);
}
#[test]
fn test_hybrid_alldiff_propagation() {
let mut gac = HybridGAC::new();
gac.add_variable(Variable(0), 1, 5).unwrap(); gac.add_variable(Variable(1), 1, 5).unwrap(); gac.add_variable(Variable(2), 1, 100).unwrap();
gac.assign_variable(Variable(0), 3);
let (changed, consistent) = gac.propagate_alldiff(&[Variable(0), Variable(1), Variable(2)]);
assert!(consistent);
assert!(changed);
let domain1 = gac.get_domain_values(Variable(1));
let domain2 = gac.get_domain_values(Variable(2));
assert!(!domain1.contains(&3));
assert!(!domain2.contains(&3));
}
#[test]
fn test_domain_type_automatic_selection() {
let small_domain = DomainType::new(1, 10);
match small_domain {
DomainType::BitSet(_) => {}, DomainType::SparseSet(_) => panic!("Small domain should use BitSet"),
}
let large_domain = DomainType::new(1, 200);
match large_domain {
DomainType::SparseSet(_) => {}, DomainType::BitSet(_) => panic!("Large domain should use SparseSet"),
}
}
#[test]
fn test_domain_type_operations() {
let mut domain = DomainType::new(1, 5);
assert_eq!(domain.size(), 5);
assert!(domain.contains(1));
assert!(domain.contains(5));
assert!(!domain.contains(0));
assert!(!domain.contains(6));
assert!(!domain.is_empty());
assert!(!domain.is_fixed());
assert!(domain.remove(3));
assert!(!domain.contains(3));
assert_eq!(domain.size(), 4);
assert!(!domain.remove(3));
assert_eq!(domain.min(), 1);
assert_eq!(domain.max(), 5);
}
#[test]
fn test_bipartite_graph_hybrid() {
let mut graph = BipartiteGraph::new();
graph.add_variable_range(Variable(0), 1, 3);
graph.add_variable_range(Variable(1), 1, 150);
assert_eq!(graph.variables().count(), 2);
assert_eq!(graph.domain_size(Variable(0)), 3);
assert_eq!(graph.domain_size(Variable(1)), 150);
assert!(graph.contains_value(Variable(0), Value(1)));
assert!(graph.contains_value(Variable(1), Value(1)));
assert!(graph.contains_value(Variable(1), Value(150)));
assert!(!graph.contains_value(Variable(0), Value(4)));
assert_eq!(graph.domain_min(Variable(0)), Some(Value(1)));
assert_eq!(graph.domain_max(Variable(0)), Some(Value(3)));
assert_eq!(graph.domain_min(Variable(1)), Some(Value(1)));
assert_eq!(graph.domain_max(Variable(1)), Some(Value(150)));
assert!(graph.remove_value(Variable(0), Value(2)));
assert_eq!(graph.domain_size(Variable(0)), 2);
assert!(!graph.contains_value(Variable(0), Value(2)));
assert!(graph.remove_value(Variable(1), Value(75)));
assert_eq!(graph.domain_size(Variable(1)), 149);
assert!(!graph.contains_value(Variable(1), Value(75)));
}
#[test]
fn test_matching_hybrid() {
let mut graph = BipartiteGraph::new();
graph.add_variable(Variable(0), vec![1, 2]);
graph.add_variable(Variable(1), vec![2, 3]);
graph.add_variable_range(Variable(2), 1, 200);
let matching = Matching::find_maximum_matching(&graph);
assert!(matching.is_complete(&graph));
assert_eq!(matching.var_to_val.len(), 3);
assert_eq!(matching.val_to_var.len(), 3);
}
#[test]
fn test_domain_type_from_values() {
let dense_small = DomainType::new_from_values(vec![1, 2, 3, 4, 5]);
match dense_small {
DomainType::BitSet(_) => {}, DomainType::SparseSet(_) => panic!("Dense small domain should use BitSet"),
}
let sparse_small = DomainType::new_from_values(vec![1, 100]);
match sparse_small {
DomainType::SparseSet(_) => {}, DomainType::BitSet(_) => panic!("Sparse domain should use SparseSet"),
}
let large_values: Vec<i32> = (1..=200).collect();
let large_domain = DomainType::new_from_values(large_values);
match large_domain {
DomainType::SparseSet(_) => {}, DomainType::BitSet(_) => panic!("Large domain should use SparseSet"),
}
}
}