# Complete GUROBIPY implementation - Retry Attempt 4

import gurobipy as gp
from gurobipy import GRB

def restaurant_optimization():
    """Optimize student visits to restaurants for maximum satisfaction."""
    
    # 1. MODEL & DATA SETUP
    model = gp.Model("restaurant_optimization")
    
    # Data from the problem statement
    restaurants = [1, 2, 3]
    students = [101, 102, 103]
    
    ratings = {1: 4.5, 2: 3.8, 3: 5.0}
    budgets = {101: 120, 102: 180, 103: 150}
    costs = {(101, 1): 12.0, (102, 2): 15.0, (103, 3): 20.0}
    
    # Validate data lengths
    assert len(ratings) == len(restaurants), "Ratings length mismatch"
    assert len(budgets) == len(students), "Budgets length mismatch"
    assert len(costs) == len(students) * len(restaurants), "Costs length mismatch"
    
    # 2. VARIABLES
    # Decision variables: number of visits each student makes to each restaurant
    visits = model.addVars(students, restaurants, vtype=GRB.INTEGER, name="visits", lb=0)
    
    # 3. OBJECTIVE FUNCTION
    # Maximize total satisfaction score
    model.setObjective(gp.quicksum(ratings[j] * visits[i, j] for i in students for j in restaurants), GRB.MAXIMIZE)
    
    # 4. CONSTRAINTS
    # Budget constraints for each student
    for i in students:
        model.addConstr(gp.quicksum(costs[i, j] * visits[i, j] for j in restaurants if (i, j) in costs) <= budgets[i], name=f"budget_{i}")
    
    # 5. SOLVING & RESULTS
    model.optimize()
    
    if model.status == GRB.OPTIMAL:
        print(f"Optimal value: {model.objVal}")
        for i in students:
            for j in restaurants:
                if visits[i, j].x > 1e-6:
                    print(f"Student {i} visits Restaurant {j}: {visits[i, j].x:.0f} times")
    elif model.status == GRB.INFEASIBLE:
        print("Problem is infeasible")
    elif model.status == GRB.UNBOUNDED:
        print("Problem is unbounded")
    
    return model

# Run the optimization
restaurant_optimization()