# Complete GUROBIPY implementation

import gurobipy as gp
from gurobipy import GRB

def optimize_train_allocation():
    """Optimize train allocation to minimize operational costs."""
    
    # 1. MODEL & DATA SETUP
    model = gp.Model("railway_optimization")
    
    # Data from the problem statement
    operational_costs = [120.0, 180.0, 140.0]
    capacities = [12, 18, 15]
    minimum_trains_required = [3, 4, 5]
    total_trains_available = 30
    n_railways = len(operational_costs)
    
    # CRITICAL: Validate array lengths before loops
    assert len(operational_costs) == len(capacities) == len(minimum_trains_required) == n_railways, "Array length mismatch"
    
    # 2. VARIABLES
    # Integer variables for the number of trains allocated to each railway
    x = model.addVars(n_railways, vtype=GRB.INTEGER, name="x", lb=0)
    
    # 3. OBJECTIVE FUNCTION
    # Minimize the total operational cost
    model.setObjective(gp.quicksum(operational_costs[i] * x[i] for i in range(n_railways)), GRB.MINIMIZE)
    
    # 4. CONSTRAINTS - CORRECT SYNTAX PATTERNS
    
    # Total train availability constraint
    model.addConstr(gp.quicksum(x[i] for i in range(n_railways)) <= total_trains_available, name="total_trains")
    
    # Railway capacity constraints
    for i in range(n_railways):
        model.addConstr(x[i] <= capacities[i], name=f"capacity_{i}")
    
    # Minimum train requirement constraints
    for i in range(n_railways):
        model.addConstr(x[i] >= minimum_trains_required[i], name=f"min_trains_{i}")
    
    # 5. SOLVING & RESULTS
    model.optimize()
    
    if model.status == GRB.OPTIMAL:
        print(f"Optimal value: {model.objVal}")
        for i in range(n_railways):
            print(f"x[{i+1}] = {x[i].x:.0f}")
    elif model.status == GRB.INFEASIBLE:
        print("Problem is infeasible")
    elif model.status == GRB.UNBOUNDED:
        print("Problem is unbounded")
    
    return model

# Run the optimization
optimize_train_allocation()