use crate::prelude::*;
#[test]
fn test_clean_solution_api_demo() {
let mut m = Model::default();
let x = m.int(5, 10);
let y = m.int(0, 5);
m.new(x.ge(int(7)));
m.new(y.le(int(3)));
let result = m.solve();
assert!(result.is_ok());
if let Ok(solution) = result {
let x_val = solution.get_int(x);
let y_val = solution.get_int(y);
let x_alt = solution[x].as_int().unwrap();
let y_alt = solution[y].as_int().unwrap();
let x_safe = solution.try_get_int(x).expect("x should be an integer");
let y_safe = solution.try_get_int(y).expect("y should be an integer");
assert_eq!(x_val, x_alt);
assert_eq!(x_val, x_safe);
assert_eq!(y_val, y_alt);
assert_eq!(y_val, y_safe);
assert!(x_val >= 7);
assert!(y_val <= 3);
}
println!("✓ Comprehensive Phase 3 + Clean API demonstration complete!");
}
#[test]
fn test_automatic_type_inference() {
let mut m = Model::default();
let x = m.int(1, 10);
let y = m.float(0.0, 5.0);
m.new(x.ge(int(5)));
m.new(y.le(float(3.0)));
let result = m.solve();
assert!(result.is_ok());
if let Ok(solution) = result {
let x_val: i32 = solution.get(x); let y_val: f64 = solution.get(y);
let x_as_int: i32 = solution.get(x);
let y_as_float: f64 = solution.get(y);
let x_opt: Option<i32> = solution.get(x); let y_opt: Option<f64> = solution.get(y);
fn process_int(val: i32) -> i32 { val * 2 }
fn process_float(val: f64) -> f64 { val * val }
let x_processed = process_int(solution.get(x)); let y_processed = process_float(solution.get(y));
assert!(x_val >= 5);
assert!(y_val <= 3.0);
assert_eq!(x_as_int, x_val);
assert_eq!(y_as_float, y_val);
assert_eq!(x_opt, Some(x_val));
assert_eq!(y_opt, Some(y_val));
assert_eq!(x_processed, x_val * 2);
assert_eq!(y_processed, y_val * y_val);
println!("✨ Type inference works!");
println!(" x: i32 = {} (inferred from annotation)", x_val);
println!(" y: f64 = {} (inferred from annotation)", y_val);
println!(" x_processed: i32 = {} (inferred from function)", x_processed);
println!(" x_opt: Option<i32> = {:?} (inferred from type)", x_opt);
}
println!("✓ Automatic type inference test complete!");
}
#[test]
fn test_phase3_boolean_logic_with_clean_api() {
let mut m = Model::default();
let x = m.int(0, 10);
let y = m.int(0, 10);
let c1 = x.ge(int(5)); let c2 = y.le(int(8));
let combined = c1.and(c2);
m.new(combined);
let result = m.solve();
assert!(result.is_ok());
if let Ok(solution) = result {
let x_val = solution.get_int(x);
let y_val = solution.get_int(y);
assert!(x_val >= 5);
assert!(y_val <= 8);
}
println!("✓ Phase 3 boolean logic with clean API works!");
}
#[test]
fn test_constraint_or_with_clean_api() {
let mut m = Model::default();
let x = m.int(0, 10);
let c1 = x.eq(int(2));
let c2 = x.eq(int(8));
let combined = c1.or(c2);
m.new(combined);
let result = m.solve();
match &result {
Ok(solution) => {
let x_val = solution[x].as_int().unwrap();
assert!(x_val == 2 || x_val == 8);
println!("✓ OR constraints with clean API work! x = {}", x_val);
}
Err(e) => {
println!("❌ OR constraint failed: {:?}", e);
panic!("Expected success but got error: {:?}", e);
}
}
}
#[test]
fn test_constraint_vec_operations() {
let mut m = Model::default();
let x = m.int(0, 10);
let y = m.int(0, 10);
let z = m.int(0, 10);
let constraints = vec![
x.ge(int(3)), y.le(int(7)), z.eq(int(5)), ];
if let Some(combined) = constraints.and_all() {
m.new(combined);
}
let result = m.solve();
assert!(result.is_ok());
if let Ok(solution) = result {
let x_val = solution.get_int(x); let y_val = solution[y].as_int().unwrap(); let z_val = solution.try_get_int(z).unwrap();
assert!(x_val >= 3);
assert!(y_val <= 7);
assert_eq!(z_val, 5);
}
println!("✓ Constraint vector operations with clean API work!");
}
#[test]
fn test_model_post_methods() {
let mut m = Model::default();
let x = m.int(0, 10);
let y = m.int(0, 10);
let constraints = vec![
x.ge(int(4)),
y.le(int(6)),
];
let prop_ids = m.postall(constraints);
assert_eq!(prop_ids.len(), 2);
let result = m.solve();
assert!(result.is_ok());
if let Ok(solution) = result {
let x_val = solution.get_int(x);
let y_val = solution.get_int(y);
assert!(x_val >= 4);
assert!(y_val <= 6);
}
println!("✓ Model post methods with clean API work!");
}
#[test]
fn test_comprehensive_clean_api_features() {
let mut m = Model::default();
let x = m.int(1, 10);
let y = m.int(1, 10);
m.c(x).add(y).ge(int(8));
m.c(x).mul(int(2)).le(y.add(int(6)));
let vars = vec![x, y];
m.alldiff(&vars);
let result = m.solve();
assert!(result.is_ok());
if let Ok(solution) = result {
println!("Values using different clean approaches:");
let x_direct = solution.get_int(x);
let y_direct = solution.get_int(y);
println!(" Direct: x={}, y={}", x_direct, y_direct);
let x_index = solution[x].as_int().unwrap();
let y_index = solution[y].as_int().unwrap();
println!(" Indexing: x={}, y={}", x_index, y_index);
let x_safe = solution.try_get_int(x).unwrap();
let y_safe = solution.try_get_int(y).unwrap();
println!(" Safe: x={}, y={}", x_safe, y_safe);
assert_eq!(x_direct, x_index);
assert_eq!(x_direct, x_safe);
assert_eq!(y_direct, y_index);
assert_eq!(y_direct, y_safe);
assert!(x_direct + y_direct >= 8);
assert!(x_direct * 2 <= y_direct + 6);
assert_ne!(x_direct, y_direct); }
println!("✓ Comprehensive clean API features work perfectly!");
}
#[test]
fn test_safe_constraint_building_no_panics() {
let mut m = Model::default();
let x = m.int(0, 10);
let y = m.int(0, 10);
fn build_constraint_safe(var: VarId, op: &str, value: i32) -> Option<Constraint> {
match op {
"eq" => Some(var.eq(int(value))),
"gt" => Some(var.gt(int(value))),
"lt" => Some(var.lt(int(value))),
"ge" => Some(var.ge(int(value))),
"le" => Some(var.le(int(value))),
_ => None }
}
let constraint_specs = vec![
(x, "ge", 3), (y, "le", 7), (x, "invalid", 5), (y, "bad_op", 2), ];
let mut successful_constraints = 0;
let mut failed_constraints = 0;
for (var, op, value) in constraint_specs {
match build_constraint_safe(var, op, value) {
Some(constraint) => {
m.new(constraint);
successful_constraints += 1;
}
None => {
failed_constraints += 1;
println!("Warning: Unknown operator '{}', skipping constraint", op);
}
}
}
assert_eq!(successful_constraints, 2);
assert_eq!(failed_constraints, 2);
let result = m.solve();
assert!(result.is_ok());
if let Ok(solution) = result {
let x_val = solution.get_int(x);
let y_val = solution.get_int(y);
assert!(x_val >= 3);
assert!(y_val <= 7);
}
println!("✓ Safe constraint building - no panics, graceful error handling!");
}
#[test]
fn test_all_different_constraint() {
let mut m = Model::default();
let vars: Vec<_> = (0..3).map(|_| m.int(1, 3)).collect();
m.alldiff(&vars);
let result = m.solve();
if result.is_err() {
eprintln!("Solve failed with error: {:?}", result.as_ref().unwrap_err());
}
assert!(result.is_ok());
if let Ok(solution) = result {
let values: Vec<i32> = vars.iter().map(|&v| solution.get_int(v)).collect();
for i in 0..values.len() {
for j in i+1..values.len() {
assert_ne!(values[i], values[j], "Values should all be different");
}
}
for &value in &values {
assert!(value >= 1 && value <= 3);
}
}
println!("✓ All different constraint test passed!");
}
#[test]
fn test_all_equal_constraint() {
let mut m = Model::default();
let vars: Vec<_> = (0..3).map(|_| m.int(1, 10)).collect();
m.alleq(&vars);
m.new(vars[0].ge(5));
let result = m.solve();
assert!(result.is_ok());
if let Ok(solution) = result {
let values: Vec<i32> = vars.iter().map(|&v| solution.get_int(v)).collect();
let first_value = values[0];
for &value in &values {
assert_eq!(value, first_value, "All values should be equal");
}
assert!(first_value >= 5);
}
println!("✓ All equal constraint test passed!");
}
#[test]
fn test_element_constraint() {
let mut m = Model::default();
let array: Vec<_> = (0..3).map(|i| m.int(i * 10, i * 10)).collect(); let index = m.int(0, 2);
let value = m.int(0, 20);
m.elem(&array, index, value);
let result = m.solve();
assert!(result.is_ok());
if let Ok(solution) = result {
let idx = solution.get_int(index) as usize;
let val = solution.get_int(value);
let array_val = solution.get_int(array[idx]);
assert_eq!(array_val, val, "array[index] should equal value");
assert!(idx < array.len());
}
println!("✓ Element constraint test passed!");
}
#[test]
fn test_count_constraint() {
let mut m = Model::default();
let vars: Vec<_> = (0..5).map(|_| m.int(1, 3)).collect();
let count_result = m.int(0, 5);
let target = m.int(2, 2);
m.count(&vars, target, count_result);
m.new(count_result.eq(2));
let result = m.solve();
assert!(result.is_ok());
if let Ok(solution) = result {
let values: Vec<i32> = vars.iter().map(|&v| solution.get_int(v)).collect();
let count = solution.get_int(count_result);
let actual_count = values.iter().filter(|&&v| v == 2).count();
assert_eq!(count, 2, "Count should be exactly 2");
assert_eq!(actual_count, 2, "Actual count should match");
}
println!("✓ Count constraint test passed!");
}
#[test]
fn test_cardinality_constraints() {
let mut m = Model::default();
let x = m.int(0, 100);
let y = m.int(0, 100);
let z = m.int(0, 100);
m.betw(x, 10, 20);
m.atmost(y, 50);
m.atleast(z, 75);
let result = m.solve();
assert!(result.is_ok());
if let Ok(solution) = result {
let x_val = solution.get_int(x);
let y_val = solution.get_int(y);
let z_val = solution.get_int(z);
assert!(x_val >= 10 && x_val <= 20, "x should be between 10 and 20");
assert!(y_val <= 50, "y should be at most 50");
assert!(z_val >= 75, "z should be at least 75");
}
println!("✓ Cardinality constraints test passed!");
}
#[test]
fn test_global_cardinality_constraint() {
let mut m = Model::default();
let vars: Vec<_> = (0..3).map(|_| m.int(1, 3)).collect();
println!("Created {} variables with domains [1,3]", vars.len());
let values = [1, 2, 3];
let counts: Vec<_> = (0..3).map(|_| m.int(0, 3)).collect();
let gcc_props = m.gcc(&vars, &values, &counts);
println!("Created {} global cardinality constraints", gcc_props.len());
println!("GCC PropIds: {:?}", gcc_props);
let eq1 = m.new(counts[0].eq(1)); let eq2 = m.new(counts[1].eq(1)); let eq3 = m.new(counts[2].eq(1)); println!("Posted equality constraints: {:?}, {:?}, {:?}", eq1, eq2, eq3);
let result = m.solve();
println!("Solve result: {:?}", result.is_ok());
assert!(result.is_ok());
if let Ok(solution) = result {
let var_values: Vec<i32> = vars.iter().map(|&v| solution.get_int(v)).collect();
let count_values: Vec<i32> = counts.iter().map(|&c| solution.get_int(c)).collect();
println!("Variable values: {:?}", var_values);
println!("Count values: {:?}", count_values);
for (i, &target_value) in values.iter().enumerate() {
let actual_count = var_values.iter().filter(|&&v| v == target_value).count() as i32;
let constraint_count = count_values[i];
println!("Value {}: actual_count={}, constraint_count={}", target_value, actual_count, constraint_count);
assert_eq!(actual_count, constraint_count,
"Count of value {} should match constraint", target_value);
}
assert_eq!(count_values[0], 1, "Should have exactly 1 one");
assert_eq!(count_values[1], 1, "Should have exactly 1 two");
assert_eq!(count_values[2], 1, "Should have exactly 1 three");
}
println!("✓ 3-value count constraint test passed!");
}
#[test]
fn test_combined_global_constraints() {
let mut m = Model::default();
let tasks: Vec<_> = (0..3).map(|_| m.int(1, 5)).collect(); let resources: Vec<_> = (0..3).map(|_| m.int(1, 3)).collect();
m.alldiff(&tasks);
let resource_counts: Vec<_> = (0..3).map(|_| m.int(0, 3)).collect();
m.gcc(&resources, &[1, 2, 3], &resource_counts);
let result = m.solve();
assert!(result.is_ok());
if let Ok(solution) = result {
let task_times: Vec<i32> = tasks.iter().map(|&t| solution.get_int(t)).collect();
let resource_counts_vals: Vec<i32> = resource_counts.iter().map(|&c| solution.get_int(c)).collect();
for i in 0..task_times.len() {
for j in i+1..task_times.len() {
assert_ne!(task_times[i], task_times[j], "Task times should be different");
}
}
let total_usage: i32 = resource_counts_vals.iter().sum();
assert_eq!(total_usage, 3, "Total resource usage should equal number of tasks");
}
println!("✓ Combined global constraints test passed!");
}