use csp_solver::domain::CostFiniteDomain;
use csp_solver::domain::traits::{CostDomain, Domain};
use csp_solver::ordering::Ordering;
use csp_solver::{Csp, OptimizationMode, Pruning, SolveConfig};
#[test]
fn cost_lookup_present() {
let domain = CostFiniteDomain::new(vec![10, 20, 30], vec![1.0, 2.0, 3.0]);
assert_eq!(domain.cost(&20), 2.0);
assert_eq!(domain.cost(&10), 1.0);
assert_eq!(domain.cost(&30), 3.0);
}
#[test]
fn cost_lookup_unsorted_input() {
let domain = CostFiniteDomain::new(vec![30, 10, 20], vec![3.0, 1.0, 2.0]);
assert_eq!(domain.cost(&10), 1.0);
assert_eq!(domain.cost(&20), 2.0);
assert_eq!(domain.cost(&30), 3.0);
assert_eq!(domain.values(), vec![10, 20, 30]);
}
#[test]
fn cost_lookup_with_negative_sentinel() {
let domain = CostFiniteDomain::new(vec![-1, 0, 1, 2], vec![100.0, 4.2, 1.1, 7.8]);
assert_eq!(domain.cost(&-1), 100.0);
assert_eq!(domain.cost(&0), 4.2);
assert_eq!(domain.cost(&1), 1.1);
assert_eq!(domain.cost(&2), 7.8);
assert_eq!(domain.values(), vec![-1, 0, 1, 2]);
}
#[test]
fn min_cost_after_remove() {
let mut domain = CostFiniteDomain::new(vec![10, 20, 30], vec![3.0, 1.0, 2.0]);
assert_eq!(domain.min_cost(), 1.0);
assert!(domain.remove(&20));
assert_eq!(domain.min_cost(), 2.0);
assert!(domain.remove(&30));
assert_eq!(domain.min_cost(), 3.0);
}
#[test]
fn domain_delegation() {
let mut domain = CostFiniteDomain::new(vec![1, 2, 3], vec![0.5, 1.5, 2.5]);
assert_eq!(domain.size(), 3);
assert!(domain.contains(&1));
assert!(domain.contains(&2));
assert!(domain.contains(&3));
assert!(!domain.contains(&4));
let iter_vals: Vec<i32> = domain.iter().collect();
assert_eq!(iter_vals, domain.values());
assert_eq!(domain.values(), vec![1, 2, 3]);
assert!(domain.remove(&2));
assert_eq!(domain.size(), 2);
assert!(!domain.contains(&2));
assert!(!domain.remove(&2));
domain.add(&2);
assert_eq!(domain.size(), 3);
assert!(domain.contains(&2));
assert_eq!(domain.cost(&2), 1.5);
domain.add(&99);
assert!(domain.contains(&99));
assert_eq!(domain.cost(&99), f64::INFINITY);
}
#[test]
fn solve_optimized_end_to_end() {
let mut csp: Csp<CostFiniteDomain> = Csp::new();
let domain = CostFiniteDomain::new(vec![10, 20, 30], vec![3.0, 1.0, 2.0]);
csp.add_variable(domain.clone());
csp.add_variable(domain.clone());
csp.add_variable(domain);
csp.finalize();
let config = SolveConfig {
pruning: Pruning::ForwardChecking,
ordering: Ordering::Chronological,
max_solutions: 1,
optimization_mode: OptimizationMode::MinimizeCost,
..Default::default()
};
let solutions = csp.solve_optimized(&config);
assert_eq!(solutions.len(), 1);
assert_eq!(solutions[0], vec![20, 20, 20]);
let total: f64 = solutions[0].iter().map(domain_cost).sum();
assert_eq!(total, 3.0);
}
#[test]
fn min_cost_cache_invalidation() {
let mut d = CostFiniteDomain::new(vec![10, 20, 30], vec![3.0, 1.0, 2.0]);
assert_eq!(d.min_cost(), 1.0); assert_eq!(d.min_cost(), 1.0); d.remove(&20); assert_eq!(d.min_cost(), 2.0); d.add(&20); assert_eq!(d.min_cost(), 1.0); }
fn domain_cost(val: &i32) -> f64 {
match val {
10 => 3.0,
20 => 1.0,
30 => 2.0,
_ => panic!("unexpected value {val}"),
}
}