#!/usr/bin/env python3
"""
Gurobipy 12.0.2 Implementation for Orchestra Optimization Problem
"""

import gurobipy as gp
from gurobipy import GRB

def orchestra_optimization():
    """Optimize the number of performances for each orchestra to maximize total attendance."""
    
    # 1. MODEL & DATA SETUP
    model = gp.Model("orchestra_optimization")
    
    # Data from the problem
    orchestras = [1, 2, 3]
    attendance = {1: 500, 2: 600, 3: 450}
    min_performances = {1: 1, 2: 2, 3: 1}
    max_performances = {1: 10, 2: 12, 3: 8}
    total_conductor_availability = 300  # Sum of conductor availability
    
    # CRITICAL: Validate array lengths before loops
    assert len(orchestras) == len(attendance) == len(min_performances) == len(max_performances), "Array length mismatch"
    
    # 2. VARIABLES
    x = {i: model.addVar(vtype=GRB.INTEGER, name=f"x_{i}", lb=min_performances[i], ub=max_performances[i]) 
         for i in orchestras}
    
    # 3. OBJECTIVE FUNCTION
    model.setObjective(gp.quicksum(attendance[i] * x[i] for i in orchestras), GRB.MAXIMIZE)
    
    # 4. CONSTRAINTS
    
    # Conductor Availability Constraint
    model.addConstr(gp.quicksum(x[i] for i in orchestras) <= total_conductor_availability, name="conductor_availability")
    
    # 5. SOLVING & RESULTS
    model.optimize()
    
    if model.status == GRB.OPTIMAL:
        print(f"Optimal value: {model.objVal}")
        for i in orchestras:
            print(f"x[{i}] = {x[i].x}")
    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__":
    orchestra_optimization()