#!/usr/bin/env python3
"""
Gurobipy 12.0.2 Implementation for Dormitory Assignment Problem
"""

import gurobipy as gp
from gurobipy import GRB

def dorm_assignment_optimization():
    """Optimize dormitory assignment to minimize total distance traveled by students."""
    
    # 1. MODEL & DATA SETUP
    model = gp.Model("dorm_assignment")
    
    # Example data (replace with actual database queries)
    students = [1, 2, 3]
    dorms = [1, 2]
    
    # DistanceMatrix data
    distance_matrix = {
        (1, 1): 12.5,
        (1, 2): 18.3,
        (2, 1): 15.0,
        (2, 2): 10.2,
        (3, 1): 20.1,
        (3, 2): 14.7
    }
    
    # GenderInfo data
    gender_info = {
        (1, 1): 'Male',
        (2, 2): 'Female',
        (3, 1): 'Male'
    }
    
    # Dorm data
    dorm_capacity = {
        1: 100,
        2: 150
    }
    
    dorm_gender = {
        1: 'Male',
        2: 'Female'
    }
    
    # CRITICAL: Validate array lengths before loops
    assert len(students) > 0, "No students provided"
    assert len(dorms) > 0, "No dorms provided"
    
    # 2. VARIABLES
    # Binary decision variables: x[s, d] = 1 if student s is assigned to dorm d
    x = model.addVars(students, dorms, vtype=GRB.BINARY, name="x")
    
    # 3. OBJECTIVE FUNCTION
    # Minimize total distance traveled by all students
    model.setObjective(gp.quicksum(distance_matrix[s, d] * x[s, d] for s in students for d in dorms), GRB.MINIMIZE)
    
    # 4. CONSTRAINTS
    
    # Single Assignment: Each student must be assigned to exactly one dorm
    for s in students:
        model.addConstr(gp.quicksum(x[s, d] for d in dorms) == 1, name=f"single_assignment_{s}")
    
    # Dorm Capacity: Number of students assigned to a dorm cannot exceed its capacity
    for d in dorms:
        model.addConstr(gp.quicksum(x[s, d] for s in students) <= dorm_capacity[d], name=f"capacity_{d}")
    
    # Gender Matching: A student can only be assigned to a dorm if the dorm's gender matches the student's gender
    for s in students:
        for d in dorms:
            if (s, d) in gender_info and gender_info[s, d] != dorm_gender[d]:
                model.addConstr(x[s, d] == 0, name=f"gender_matching_{s}_{d}")
    
    # 5. SOLVING & RESULTS
    model.optimize()
    
    if model.status == GRB.OPTIMAL:
        print(f"Optimal value: {model.objVal}")
        for s in students:
            for d in dorms:
                if x[s, d].x > 0.5:
                    print(f"Student {s} assigned to Dorm {d}")
    elif model.status == GRB.INFEASIBLE:
        print("Problem is infeasible")
    elif model.status == GRB.UNBOUNDED:
        print("Problem is unbounded")
    
    return model

# Run the optimization
if __name__ == "__main__":
    dorm_assignment_optimization()