use selen::prelude::*;
use selen::core::SolverError;
#[test]
fn test_float_lin_eq_simple() {
let mut m = Model::default();
let x = m.float(0.0, 10.0);
let y = m.float(0.0, 10.0);
m.lin_eq(&[1.0, 1.0], &[x, y], 7.5);
m.new(x.eq(3.0));
let solution = m.solve().expect("Should find solution");
if let (Val::ValF(x_val), Val::ValF(y_val)) = (solution[x], solution[y]) {
assert!((x_val - 3.0).abs() < 1e-6);
assert!((y_val - 4.5).abs() < 1e-6); assert!((x_val + y_val - 7.5).abs() < 1e-6);
} else {
panic!("Expected float values");
}
}
#[test]
fn test_float_lin_eq_with_coefficients() {
let mut m = Model::default();
let x = m.float(0.0, 10.0);
let y = m.float(0.0, 10.0);
m.lin_eq(&[2.5, 3.7], &[x, y], 18.5);
m.new(x.eq(2.0));
let solution = m.solve().expect("Should find solution");
if let (Val::ValF(x_val), Val::ValF(y_val)) = (solution[x], solution[y]) {
assert!((x_val - 2.0).abs() < 1e-6);
let expected_y = (18.5 - 2.5 * x_val) / 3.7;
assert!((y_val - expected_y).abs() < 1e-6);
assert!((2.5 * x_val + 3.7 * y_val - 18.5).abs() < 1e-5);
} else {
panic!("Expected float values");
}
}
#[test]
fn test_float_lin_eq_negative_coefficient() {
let mut m = Model::default();
let x = m.float(0.0, 10.0);
let y = m.float(0.0, 10.0);
m.lin_eq(&[5.0, -2.0], &[x, y], 6.0);
m.new(x.eq(4.0));
let solution = m.solve().expect("Should find solution");
if let (Val::ValF(x_val), Val::ValF(y_val)) = (solution[x], solution[y]) {
assert!((x_val - 4.0).abs() < 1e-6);
assert!((y_val - 7.0).abs() < 1e-6); assert!((5.0 * x_val - 2.0 * y_val - 6.0).abs() < 1e-6);
} else {
panic!("Expected float values");
}
}
#[test]
fn test_float_lin_eq_three_variables() {
let mut m = Model::default();
let x = m.float(0.0, 10.0);
let y = m.float(0.0, 10.0);
let z = m.float(0.0, 10.0);
m.lin_eq(&[2.0, 3.0, -1.0], &[x, y, z], 10.0);
m.new(x.eq(2.0));
m.new(y.eq(3.0));
let solution = m.solve().expect("Should find solution");
if let (Val::ValF(x_val), Val::ValF(y_val), Val::ValF(z_val)) =
(solution[x], solution[y], solution[z]) {
assert!((x_val - 2.0).abs() < 1e-6);
assert!((y_val - 3.0).abs() < 1e-6);
assert!((z_val - 3.0).abs() < 1e-6); assert!((2.0 * x_val + 3.0 * y_val - z_val - 10.0).abs() < 1e-6);
} else {
panic!("Expected float values");
}
}
#[test]
fn test_float_lin_le_simple() {
let mut m = Model::default();
let x = m.float(0.0, 10.0);
let y = m.float(0.0, 10.0);
m.lin_le(&[1.0, 1.0], &[x, y], 10.5);
m.new(x.eq(8.0));
let solution = m.solve().expect("Should find solution");
if let (Val::ValF(x_val), Val::ValF(y_val)) = (solution[x], solution[y]) {
assert!((x_val - 8.0).abs() < 1e-6);
assert!(y_val <= 2.5 + 1e-6);
assert!(x_val + y_val <= 10.5 + 1e-6);
} else {
panic!("Expected float values");
}
}
#[test]
fn test_float_lin_le_with_coefficients() {
let mut m = Model::default();
let x = m.float(0.0, 10.0);
let y = m.float(0.0, 10.0);
m.lin_le(&[2.0, 3.0], &[x, y], 20.0);
m.new(x.eq(5.0));
let solution = m.solve().expect("Should find solution");
if let (Val::ValF(x_val), Val::ValF(y_val)) = (solution[x], solution[y]) {
assert!((x_val - 5.0).abs() < 1e-6);
assert!(y_val <= 10.0/3.0 + 1e-6);
assert!(2.0 * x_val + 3.0 * y_val <= 20.0 + 1e-6);
} else {
panic!("Expected float values");
}
}
#[test]
fn test_float_lin_le_negative_coefficient() {
let mut m = Model::default();
let x = m.float(0.0, 10.0);
let y = m.float(0.0, 10.0);
m.lin_le(&[1.0, -1.0], &[x, y], 5.0);
m.new(x.eq(8.0));
let solution = m.solve().expect("Should find solution");
if let (Val::ValF(x_val), Val::ValF(y_val)) = (solution[x], solution[y]) {
assert!((x_val - 8.0).abs() < 1e-6);
assert!(y_val >= 3.0 - 1e-6);
assert!(x_val - y_val <= 5.0 + 1e-6);
} else {
panic!("Expected float values");
}
}
#[test]
fn test_float_lin_ne_simple() {
let mut m = Model::default();
let x = m.float(0.0, 10.0);
let y = m.float(0.0, 10.0);
m.lin_ne(&[1.0, 1.0], &[x, y], 5.0);
m.new(x.eq(2.0));
let solution = m.solve().expect("Should find solution");
if let (Val::ValF(x_val), Val::ValF(y_val)) = (solution[x], solution[y]) {
assert!((x_val - 2.0).abs() < 1e-6);
assert!((y_val - 3.0).abs() > 1e-6);
assert!((x_val + y_val - 5.0).abs() > 1e-6);
} else {
panic!("Expected float values");
}
}
#[test]
fn test_float_lin_ne_with_coefficients() {
let mut m = Model::default();
let x = m.float(0.0, 10.0);
let y = m.float(0.0, 10.0);
m.lin_ne(&[2.0, 3.0], &[x, y], 12.0);
m.new(x.eq(3.0));
let solution = m.solve().expect("Should find solution");
if let (Val::ValF(x_val), Val::ValF(y_val)) = (solution[x], solution[y]) {
assert!((x_val - 3.0).abs() < 1e-6);
assert!((y_val - 2.0).abs() > 1e-6);
assert!((2.0 * x_val + 3.0 * y_val - 12.0).abs() > 1e-6);
} else {
panic!("Expected float values");
}
}
#[test]
fn test_float_lin_eq_single_variable() {
let mut m = Model::default();
let x = m.float(0.0, 10.0);
m.lin_eq(&[3.5], &[x], 7.0);
let solution = m.solve().expect("Should find solution");
if let Val::ValF(x_val) = solution[x] {
assert!((x_val - 2.0).abs() < 1e-6); assert!((3.5 * x_val - 7.0).abs() < 1e-6);
} else {
panic!("Expected float value");
}
}
#[test]
fn test_float_lin_le_single_variable() {
let mut m = Model::default();
let x = m.float(0.0, 10.0);
m.lin_le(&[2.0], &[x], 10.0);
let solution = m.solve().expect("Should find solution");
if let Val::ValF(x_val) = solution[x] {
assert!(x_val <= 5.0 + 1e-6);
assert!(2.0 * x_val <= 10.0 + 1e-6);
} else {
panic!("Expected float value");
}
}
#[test]
fn test_float_lin_eq_infeasible() {
let mut m = Model::default();
let x = m.float(0.0, 5.0);
let y = m.float(0.0, 5.0);
m.lin_eq(&[1.0, 1.0], &[x, y], 20.0);
let result = m.solve();
assert!(result.is_err(), "Should not find solution");
}
#[test]
fn test_float_lin_eq_mismatched_lengths() {
let mut m = Model::default();
let x = m.float(0.0, 10.0);
let y = m.float(0.0, 10.0);
m.lin_eq(&[1.0, 2.0, 3.0], &[x, y], 10.0);
let result = m.solve();
assert!(result.is_err(), "Should return error for mismatched lengths");
if let Err(e) = result {
match e {
SolverError::InvalidConstraint { message, .. } => {
assert!(message.contains("coefficients and variables must have same length"),
"Error message should mention length mismatch: {}", message);
}
_ => panic!("Expected InvalidConstraint error, got: {:?}", e),
}
}
}
#[test]
fn test_float_lin_le_mismatched_lengths() {
let mut m = Model::default();
let x = m.float(0.0, 10.0);
let y = m.float(0.0, 10.0);
m.lin_le(&[1.0], &[x, y], 10.0);
let result = m.solve();
assert!(result.is_err(), "Should return error for mismatched lengths");
if let Err(e) = result {
match e {
SolverError::InvalidConstraint { message, .. } => {
assert!(message.contains("coefficients and variables must have same length"),
"Error message should mention length mismatch: {}", message);
}
_ => panic!("Expected InvalidConstraint error, got: {:?}", e),
}
}
}
#[test]
fn test_loan_example() {
let mut m = Model::default();
let principal = m.float(1000.0, 10000.0);
let interest = m.float(0.0, 1000.0);
let payment = m.float(0.0, 5000.0);
let balance = m.float(0.0, 15000.0);
m.lin_eq(&[1.05, 1.03, -1.0, -1.0],
&[principal, interest, payment, balance],
0.0);
m.new(principal.eq(5000.0)); m.new(interest.eq(250.0)); m.new(payment.eq(1000.0));
let solution = m.solve().expect("Should find solution");
if let Val::ValF(balance_val) = solution[balance] {
let expected_balance = 1.05 * 5000.0 + 1.03 * 250.0 - 1000.0;
assert!((balance_val - expected_balance).abs() < 1e-6);
} else {
panic!("Expected float value for balance");
}
}
#[test]
fn test_float_lin_eq_reif_true() {
let mut m = Model::default();
let x = m.float(0.0, 10.0);
let y = m.float(0.0, 10.0);
let b = m.bool();
m.lin_eq_reif(&[2.0, 3.0], &[x, y], 12.0, b);
m.new(x.eq(3.0));
m.new(y.eq(2.0));
let solution = m.solve().expect("Should find solution");
assert_eq!(solution[b], Val::ValI(1), "b should be 1 when constraint holds");
}
#[test]
fn test_float_lin_eq_reif_false() {
let mut m = Model::default();
let x = m.float(0.0, 10.0);
let y = m.float(0.0, 10.0);
let b = m.bool();
m.lin_eq_reif(&[2.0, 3.0], &[x, y], 12.0, b);
m.new(x.eq(1.0));
m.new(y.eq(1.0));
let solution = m.solve().expect("Should find solution");
assert_eq!(solution[b], Val::ValI(0), "b should be 0 when constraint doesn't hold");
}
#[test]
fn test_float_lin_eq_reif_force_true() {
let mut m = Model::default();
let x = m.float(0.0, 10.0);
let y = m.float(0.0, 10.0);
let b = m.bool();
m.lin_eq_reif(&[1.0, 1.0], &[x, y], 7.5, b);
m.new(b.eq(1));
m.new(x.eq(3.0));
let solution = m.solve().expect("Should find solution");
if let Val::ValF(y_val) = solution[y] {
assert!((y_val - 4.5).abs() < 1e-6, "y should be 4.5 when b=1 and x=3.0");
}
}
#[test]
fn test_float_lin_eq_reif_force_false() {
let mut m = Model::default();
let x = m.float(2.0, 4.0);
let y = m.float(2.0, 4.0);
let b = m.bool();
m.lin_eq_reif(&[1.0, 1.0], &[x, y], 5.0, b);
m.new(b.eq(0));
let solution = m.solve().expect("Should find solution");
if let (Val::ValF(x_val), Val::ValF(y_val)) = (solution[x], solution[y]) {
assert!((x_val + y_val - 5.0).abs() > 1e-6, "x + y should not equal 5.0 when b=0");
}
}
#[test]
fn test_float_lin_le_reif_true() {
let mut m = Model::default();
let x = m.float(0.0, 10.0);
let y = m.float(0.0, 10.0);
let b = m.bool();
m.lin_le_reif(&[1.0, 1.0], &[x, y], 10.0, b);
m.new(x.eq(4.0));
m.new(y.eq(5.0));
let solution = m.solve().expect("Should find solution");
assert_eq!(solution[b], Val::ValI(1), "b should be 1 when constraint holds");
}
#[test]
fn test_float_lin_le_reif_false() {
let mut m = Model::default();
let x = m.float(0.0, 20.0);
let y = m.float(0.0, 20.0);
let b = m.bool();
m.lin_le_reif(&[1.0, 1.0], &[x, y], 10.0, b);
m.new(x.eq(8.0));
m.new(y.eq(5.0));
let solution = m.solve().expect("Should find solution");
assert_eq!(solution[b], Val::ValI(0), "b should be 0 when constraint doesn't hold");
}
#[test]
fn test_float_lin_le_reif_force_true() {
let mut m = Model::default();
let x = m.float(0.0, 10.0);
let y = m.float(0.0, 10.0);
let b = m.bool();
m.lin_le_reif(&[2.0, 3.0], &[x, y], 20.0, b);
m.new(b.eq(1));
m.new(x.eq(5.0));
let solution = m.solve().expect("Should find solution");
if let (Val::ValF(x_val), Val::ValF(y_val)) = (solution[x], solution[y]) {
assert!(2.0 * x_val + 3.0 * y_val <= 20.0 + 1e-6,
"2*x + 3*y should be ≤ 20.0 when b=1");
}
}
#[test]
fn test_float_lin_ne_reif_true() {
let mut m = Model::default();
let x = m.float(0.0, 10.0);
let y = m.float(0.0, 10.0);
let b = m.bool();
m.lin_ne_reif(&[1.0, 1.0], &[x, y], 5.0, b);
m.new(x.eq(2.0));
m.new(y.eq(1.0));
let solution = m.solve().expect("Should find solution");
assert_eq!(solution[b], Val::ValI(1), "b should be 1 when constraint holds");
}
#[test]
fn test_float_lin_ne_reif_false() {
let mut m = Model::default();
let x = m.float(0.0, 10.0);
let y = m.float(0.0, 10.0);
let b = m.bool();
m.lin_ne_reif(&[1.0, 1.0], &[x, y], 5.0, b);
m.new(x.eq(2.0));
m.new(y.eq(3.0));
let solution = m.solve().expect("Should find solution");
assert_eq!(solution[b], Val::ValI(0), "b should be 0 when values are equal");
}
#[test]
fn test_float_lin_ne_reif_force_true() {
let mut m = Model::default();
let x = m.float(0.0, 10.0);
let y = m.float(0.0, 10.0);
let b = m.bool();
m.lin_ne_reif(&[2.0, 3.0], &[x, y], 12.0, b);
m.new(b.eq(1));
m.new(x.eq(3.0));
let solution = m.solve().expect("Should find solution");
if let (Val::ValF(x_val), Val::ValF(y_val)) = (solution[x], solution[y]) {
assert!((2.0 * x_val + 3.0 * y_val - 12.0).abs() > 1e-6,
"2*x + 3*y should not equal 12.0 when b=1");
}
}