#!/usr/bin/env python3
"""
Gurobipy 12.0.2 Implementation for Restaurant Placement Optimization
"""

import gurobipy as gp
from gurobipy import GRB

def restaurant_optimization():
    """Optimize restaurant placement to maximize customer satisfaction."""
    
    # 1. MODEL & DATA SETUP
    model = gp.Model("restaurant_placement")
    
    # Data from the problem
    ratings = [4.5, 3.8, 4.2, 3.9, 4.1]
    regions = ['North', 'South', 'East', 'West', 'North']
    cities = range(len(ratings))
    unique_regions = list(set(regions))
    
    # CRITICAL: Validate array lengths before loops
    assert len(ratings) == len(regions), "Array length mismatch"
    
    # 2. VARIABLES
    # Binary decision variables for each city
    x = {i: model.addVar(vtype=GRB.BINARY, name=f"x_{i}") for i in cities}
    
    # 3. OBJECTIVE FUNCTION
    # Maximize total customer satisfaction
    model.setObjective(gp.quicksum(ratings[i] * x[i] for i in cities), GRB.MAXIMIZE)
    
    # 4. CONSTRAINTS
    
    # Total budget constraint: sum of x_i <= 5
    model.addConstr(gp.quicksum(x[i] for i in cities) <= 5, name="total_budget")
    
    # Regional distribution constraint: at least 1 restaurant per region
    for r in unique_regions:
        model.addConstr(gp.quicksum(x[i] for i in cities if regions[i] == r) >= 1, name=f"region_{r}")
    
    # City-level placement constraint: no more than 2 restaurants per city
    for i in cities:
        model.addConstr(x[i] <= 2, name=f"city_limit_{i}")
    
    # 5. SOLVING & RESULTS
    model.optimize()
    
    if model.status == GRB.OPTIMAL:
        print(f"Optimal value: {model.objVal}")
        for i in cities:
            if x[i].x > 0:
                print(f"City {i} selected: {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__":
    restaurant_optimization()