lpsolve 1.0.1

High-level lpsolve wrapper
Documentation
use lpsolve::*;

/// Smoke test: Basic LP problem with maximize
#[test]
fn test_basic_lp_maximize() {
    // Pre-allocate 2 rows to avoid potential issues with add_constraint on 0-row problems
    let mut lp = Problem::new(2, 2).expect("Failed to create problem");

    // Maximize: 30*x1 + 50*x2
    lp.set_maximize();
    let mut obj = vec![0.0, 30.0, 50.0];
    lp.set_objective_function(&mut obj).expect("Failed to set objective");

    // Set row 1: 2*x1 + 3*x2 <= 100
    lp.set_row(1, &mut [0.0, 2.0, 3.0]).expect("Failed to set first row");
    lp.set_rh(1, 100.0).expect("Failed to set RHS 1");
    lp.set_constraint_type(1, ConstraintType::Le).expect("Failed to set constraint type 1");

    // Set row 2: x1 + 2*x2 <= 60
    lp.set_row(2, &mut [0.0, 1.0, 2.0]).expect("Failed to set second row");
    lp.set_rh(2, 60.0).expect("Failed to set RHS 2");
    lp.set_constraint_type(2, ConstraintType::Le).expect("Failed to set constraint type 2");

    // Solve
    let status = lp.solve();
    assert_eq!(status, SolveStatus::Optimal);

    // Check we got a reasonable solution
    let mut vars = vec![0.0; 2];
    lp.get_solution_variables(&mut vars).expect("Failed to get variables");
    let objective = lp.get_objective();
    assert!(objective > 1500.0 && objective < 1700.0);
}

/// Smoke test: Basic LP problem with minimize
#[test]
fn test_basic_lp_minimize() {
    let mut lp = Problem::new(0, 2).expect("Failed to create problem");

    // Minimize: x1 + 2*x2
    lp.set_minimize();
    lp.set_objective_function(&mut [0.0, 1.0, 2.0]).expect("Failed to set objective");

    // Constraint: x1 + x2 >= 5
    lp.add_constraint(&mut [0.0, 1.0, 1.0], 5.0, ConstraintType::Ge)
        .expect("Failed to add constraint");

    let status = lp.solve();
    assert_eq!(status, SolveStatus::Optimal);

    let objective = lp.get_objective();
    assert!(objective >= 5.0);
}

/// Smoke test: Integer programming using builder pattern with binary
#[test]
fn test_integer_programming() {
    let mut lp = Problem::new(0, 3).expect("Failed to create problem");

    // Maximize value with binary variables (0-1 knapsack)
    lp.set_maximize();
    lp.set_objective_function(&mut [0.0, 12.0, 10.0, 8.0])
        .expect("Failed to set objective");

    // Weight constraint
    lp.add_constraint(&mut [0.0, 2.0, 1.0, 1.0], 3.0, ConstraintType::Le)
        .expect("Failed to add constraint");

    // Binary variables using the method that works in examples
    lp.set_binary(1, true).expect("Failed to set binary 1");
    lp.set_binary(2, true).expect("Failed to set binary 2");
    lp.set_binary(3, true).expect("Failed to set binary 3");

    let status = lp.solve();
    assert_eq!(status, SolveStatus::Optimal);

    // Check solution is actually binary
    let mut vars = vec![0.0; 3];
    lp.get_solution_variables(&mut vars).expect("Failed to get variables");
    for &v in vars.iter() {
        assert!(v == 0.0 || v == 1.0);
    }
}

/// Smoke test: Infeasible problem detection
#[test]
fn test_infeasible_detection() {
    let mut lp = Problem::new(0, 2).expect("Failed to create problem");

    lp.set_minimize();
    lp.set_objective_function(&mut [0.0, 1.0, 1.0]).expect("Failed to set objective");

    // Conflicting constraints: x1 + x2 >= 10 AND x1 + x2 <= 5
    lp.add_constraint(&mut [0.0, 1.0, 1.0], 10.0, ConstraintType::Ge)
        .expect("Failed to add first constraint");
    lp.add_constraint(&mut [0.0, 1.0, 1.0], 5.0, ConstraintType::Le)
        .expect("Failed to add second constraint");

    let status = lp.solve();
    assert_eq!(status, SolveStatus::Infeasible);
}