#!/usr/bin/env python3
"""
Gurobipy 12.0.2 Implementation for Bodybuilder Team Selection Problem
"""

import gurobipy as gp
from gurobipy import GRB

def bodybuilder_optimization():
    """Optimization model for selecting bodybuilders to maximize performance score."""
    
    # 1. MODEL & DATA SETUP
    model = gp.Model("bodybuilder_selection")
    
    # Data from the database
    snatch_scores = [150.5, 160.0, 170.5]
    clean_jerk_scores = [200.0, 210.5, 220.0]
    heights = [175.0, 180.0, 185.0]
    weights = [90.0, 95.0, 100.0]
    
    # Number of bodybuilders
    n_bodybuilders = len(snatch_scores)
    
    # CRITICAL: Validate array lengths before loops
    assert len(snatch_scores) == len(clean_jerk_scores) == len(heights) == len(weights) == n_bodybuilders, "Array length mismatch"
    
    # 2. VARIABLES
    # Binary decision variables for selecting bodybuilders
    x = {i: model.addVar(vtype=GRB.BINARY, name=f"x_{i}") for i in range(n_bodybuilders)}
    
    # 3. OBJECTIVE FUNCTION
    # Maximize the total performance score
    total_score = gp.quicksum((snatch_scores[i] + clean_jerk_scores[i]) * x[i] for i in range(n_bodybuilders))
    model.setObjective(total_score, GRB.MAXIMIZE)
    
    # 4. CONSTRAINTS
    
    # Team Size Limit: Total number of bodybuilders selected <= 5
    model.addConstr(gp.quicksum(x[i] for i in range(n_bodybuilders)) <= 5, name="team_size_limit")
    
    # Minimum Average Height: Average height of selected bodybuilders >= 170 cm
    model.addConstr(gp.quicksum(heights[i] * x[i] for i in range(n_bodybuilders)) >= 170 * gp.quicksum(x[i] for i in range(n_bodybuilders)), name="min_avg_height")
    
    # Maximum Average Weight: Average weight of selected bodybuilders <= 100 kg
    model.addConstr(gp.quicksum(weights[i] * x[i] for i in range(n_bodybuilders)) <= 100 * gp.quicksum(x[i] for i in range(n_bodybuilders)), name="max_avg_weight")
    
    # 5. SOLVING & RESULTS
    model.optimize()
    
    if model.status == GRB.OPTIMAL:
        print(f"Optimal value: {model.objVal}")
        for i in range(n_bodybuilders):
            if x[i].x > 0.5:
                print(f"Bodybuilder {i} is selected.")
    elif model.status == GRB.INFEASIBLE:
        print("Problem is infeasible")
    elif model.status == GRB.UNBOUNDED:
        print("Problem is unbounded")
    
    return model

# Execute the optimization
if __name__ == "__main__":
    bodybuilder_optimization()