use lpsolve::prelude::*;
#[derive(Debug)]
struct Food {
name: &'static str,
cost: f64,
calories: f64,
protein: f64,
calcium: f64,
iron: f64,
}
#[derive(Debug)]
struct NutritionalRequirement {
name: &'static str,
minimum: f64,
maximum: Option<f64>,
}
fn main() -> Result<()> {
println!("=== Diet Problem ===\n");
let foods = vec![
Food { name: "Bread", cost: 2.0, calories: 100.0, protein: 4.0, calcium: 2.0, iron: 1.0 },
Food { name: "Milk", cost: 3.0, calories: 120.0, protein: 8.0, calcium: 15.0, iron: 0.5 },
Food { name: "Eggs", cost: 4.0, calories: 150.0, protein: 12.0, calcium: 2.0, iron: 2.0 },
Food { name: "Meat", cost: 8.0, calories: 200.0, protein: 20.0, calcium: 1.0, iron: 3.0 },
Food { name: "Vegetables", cost: 1.5, calories: 50.0, protein: 2.0, calcium: 4.0, iron: 1.5 },
];
let requirements = vec![
NutritionalRequirement { name: "Calories", minimum: 2000.0, maximum: Some(2500.0) },
NutritionalRequirement { name: "Protein", minimum: 50.0, maximum: None },
NutritionalRequirement { name: "Calcium", minimum: 100.0, maximum: None },
NutritionalRequirement { name: "Iron", minimum: 10.0, maximum: Some(20.0) },
];
let n_foods = foods.len() as i32;
let costs: Vec<f64> = foods.iter().map(|f| f.cost).collect();
let calories: Vec<f64> = foods.iter().map(|f| f.calories).collect();
let protein: Vec<f64> = foods.iter().map(|f| f.protein).collect();
let calcium: Vec<f64> = foods.iter().map(|f| f.calcium).collect();
let iron: Vec<f64> = foods.iter().map(|f| f.iron).collect();
let names: Vec<&str> = foods.iter().map(|f| f.name).collect();
let solution = Problem::builder()
.name("Diet Optimization")?
.cols(n_foods)
.min(&costs)
.named_constraint("Min Calories", &calories, Ge, requirements[0].minimum)?
.named_constraint("Max Calories", &calories, Le, requirements[0].maximum.unwrap())?
.named_constraint("Min Protein", &protein, Ge, requirements[1].minimum)?
.named_constraint("Min Calcium", &calcium, Ge, requirements[2].minimum)?
.named_constraint("Min Iron", &iron, Ge, requirements[3].minimum)?
.named_constraint("Max Iron", &iron, Le, requirements[3].maximum.unwrap())?
.bounds_all(0.0, 10.0) .variable_names(&names)?
.typical_lp()
.solve()?;
if solution.is_optimal() {
println!("✓ Found optimal diet!");
println!(" Total daily cost: ${:.2}\n", solution.objective_value());
println!(" Food quantities (units per day):");
let mut total_nutrients = vec![0.0; 4];
for (i, food) in foods.iter().enumerate() {
if let Some(quantity) = solution.variable((i + 1) as i32) {
if quantity > 0.01 { println!(" {:<12} {:.2} units @ ${:.2}/unit = ${:.2}",
food.name, quantity, food.cost, quantity * food.cost);
total_nutrients[0] += quantity * food.calories;
total_nutrients[1] += quantity * food.protein;
total_nutrients[2] += quantity * food.calcium;
total_nutrients[3] += quantity * food.iron;
}
}
}
println!("\n Nutritional content:");
println!(" Calories: {:.1} (required: {:.0}-{:.0})",
total_nutrients[0], requirements[0].minimum,
requirements[0].maximum.unwrap_or(f64::INFINITY));
println!(" Protein: {:.1}g (required: >= {:.0}g)",
total_nutrients[1], requirements[1].minimum);
println!(" Calcium: {:.1}mg (required: >= {:.0}mg)",
total_nutrients[2], requirements[2].minimum);
println!(" Iron: {:.1}mg (required: {:.0}-{:.0}mg)",
total_nutrients[3], requirements[3].minimum,
requirements[3].maximum.unwrap_or(f64::INFINITY));
if let Some(duals) = solution.dual_values() {
println!("\n Shadow prices (marginal cost of constraints):");
let constraint_names = [
"Min Calories", "Max Calories", "Min Protein",
"Min Calcium", "Min Iron", "Max Iron"
];
for (i, name) in constraint_names.iter().enumerate() {
if i < duals.len() && duals[i].abs() > 0.001 {
println!(" {}: ${:.4}", name, duals[i].abs());
}
}
}
println!("\n Solver statistics:");
println!(" Iterations: {}", solution.iterations());
println!(" Time: {:.4}s", solution.time_elapsed());
} else {
println!("✗ Could not find optimal diet: {:?}", solution.status());
if solution.status() == Infeasible {
println!("\n The nutritional requirements might be impossible to meet");
println!(" with the given foods. Try:");
println!(" - Relaxing some constraints");
println!(" - Adding more food options");
println!(" - Adjusting the bounds");
}
}
Ok(())
}