use std::error::Error;
use float_eq::assert_float_eq;
use good_lp::{
Expression, ProblemVariables, Solution, SolverModel, Variable, default_solver, variable,
};
#[cfg(target_arch = "wasm32")]
use wasm_bindgen_test::*;
struct Product {
needed_fuel: f64,
needed_time: f64,
value: f64,
}
fn resource_consumed<'a, F>(
products: impl IntoIterator<Item = (&'a Product, &'a Variable)>,
consumption: F,
) -> Expression
where
F: Fn(&Product) -> f64,
{
products
.into_iter()
.map(|(product, amount)| consumption(product) * *amount)
.sum()
}
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn no_macro_example() -> Result<(), Box<dyn Error>> {
let available_fuel = 5.;
let available_time = 3.;
let products = [
Product {
needed_fuel: 1.,
needed_time: 1.,
value: 10.,
},
Product {
needed_fuel: 2.,
needed_time: 1.,
value: 11.,
},
];
let mut vars = ProblemVariables::new();
let amounts: Vec<Variable> = products
.iter()
.map(|_| vars.add(variable().min(0)))
.collect();
let objective: Expression = products
.iter()
.zip(amounts.iter())
.map(|(p, amount)| p.value * *amount)
.sum();
let mut model = vars.maximise(objective.clone()).using(default_solver);
model.add_constraint(
resource_consumed(products.iter().zip(&amounts), |p| p.needed_fuel).leq(available_fuel),
);
model.add_constraint(
resource_consumed(products.iter().zip(&amounts), |p| p.needed_time).leq(available_time),
);
let solution = model.solve()?;
println!(
"produce {:.0} units of product 1 and {:.0} units of product 2",
solution.value(amounts[0]),
solution.value(amounts[1])
);
println!("total profit: {}", solution.eval(&objective));
assert_float_eq!(solution.value(amounts[0]), 1., abs <= 1e-8);
assert_float_eq!(solution.value(amounts[1]), 2., abs <= 1e-8);
Ok(())
}