//! Classic diet problem
//!
//! Find the minimum cost diet that meets nutritional requirements.
//!
//! Foods available:
//! - Bread: $0.20/loaf, 4g protein, 1000mg calcium per loaf
//! - Milk: $0.35/quart, 8g protein, 1200mg calcium per quart
//! - Cheese: $0.80/lb, 20g protein, 800mg calcium per lb
//!
//! Requirements:
//! - At least 30g protein per day
//! - At least 1500mg calcium per day
//! - At least 1 loaf of bread (for carbohydrates)

use ockham::prelude::*;

fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
    println!("=== Diet Problem Example ===\n");

    let problem = ProblemBuilder::new()
        .name("Minimum Cost Diet")
        .add_non_negative_variable("bread")? // loaves
        .add_non_negative_variable("milk")? // quarts
        .add_non_negative_variable("cheese")? // lbs
        // Protein constraint: 4*bread + 8*milk + 20*cheese >= 30
        .add_named_constraint(
            "protein",
            vec![4.0, 8.0, 20.0],
            ConstraintType::GreaterEqual,
            30.0,
        )?
        // Calcium constraint: 1000*bread + 1200*milk + 800*cheese >= 1500
        .add_named_constraint(
            "calcium",
            vec![1000.0, 1200.0, 800.0],
            ConstraintType::GreaterEqual,
            1500.0,
        )?
        // Minimum bread constraint: bread >= 1
        .add_named_constraint(
            "min_bread",
            vec![1.0, 0.0, 0.0],
            ConstraintType::GreaterEqual,
            1.0,
        )?
        // Objective: minimize cost 0.20*bread + 0.35*milk + 0.80*cheese
        .named_objective("cost", vec![0.20, 0.35, 0.80], ObjectiveType::Minimize)?
        .build()?;

    println!("Problem formulation:");
    println!("{}", problem);

    // Show problem statistics
    let stats = problem.statistics();
    println!("{}", stats);

    // Validate the problem
    let validator = Validator::new();
    let validation_result = validator.validate(&problem);
    if !validation_result.warnings.is_empty() {
        println!("\nValidation warnings:");
        for warning in &validation_result.warnings {
            println!("  - {}", warning);
        }
    }

    // Solve the problem
    println!("\nSolving...");
    let mut solver =
        SimplexSolver::new().with_pivot_rule(PivotRule::MostNegative);

    let solution = solver.solve(&problem)?;

    println!("\nSolution:");
    println!("{}", solution);

    if solution.is_optimal() {
        let bread = solution.variables[0];
        let milk = solution.variables[1];
        let cheese = solution.variables[2];

        println!("\nOptimal diet:");
        println!("  Bread: {:.3} loaves", bread);
        println!("  Milk: {:.3} quarts", milk);
        println!("  Cheese: {:.3} lbs", cheese);
        println!("  Total cost: ${:.2} per day", solution.objective_value);

        println!("\nNutritional content:");
        let protein = 4.0 * bread + 8.0 * milk + 20.0 * cheese;
        let calcium = 1000.0 * bread + 1200.0 * milk + 800.0 * cheese;

        println!("  Protein: {:.1}g (required: 30g)", protein);
        println!("  Calcium: {:.0}mg (required: 1500mg)", calcium);

        println!("\nCost breakdown:");
        println!("  Bread: ${:.2}", 0.20 * bread);
        println!("  Milk: ${:.2}", 0.35 * milk);
        println!("  Cheese: ${:.2}", 0.80 * cheese);

        // Check if constraints are satisfied
        println!("\nConstraint verification:");
        println!(
            "  Protein constraint: {:.1} >= 30? {}",
            protein,
            protein >= 30.0 - 1e-6
        );
        println!(
            "  Calcium constraint: {:.0} >= 1500? {}",
            calcium,
            calcium >= 1500.0 - 1e-6
        );
        println!(
            "  Minimum bread: {:.3} >= 1? {}",
            bread,
            bread >= 1.0 - 1e-6
        );
    }

    Ok(())
}
