# Complete GUROBIPY implementation

import gurobipy as gp
from gurobipy import GRB

def candidate_poll_optimization():
    """Optimize resource allocation for maximizing support rate in a political campaign."""
    
    # 1. MODEL & DATA SETUP
    model = gp.Model("candidate_poll")
    
    # Data from the problem context
    poll_sources = [1, 2, 3]
    support_rates = {1: 0.55, 2: 0.75, 3: 0.6}
    min_effort = {1: 5.0, 2: 10.0, 3: 7.5}
    max_effort = {1: 20.0, 2: 30.0, 3: 25.0}
    total_budget = 50.0  # Example budget constraint
    
    # CRITICAL: Validate array lengths before loops
    assert len(support_rates) == len(min_effort) == len(max_effort) == len(poll_sources), "Array length mismatch"
    
    # 2. VARIABLES
    effort = {i: model.addVar(vtype=GRB.CONTINUOUS, name=f"effort_{i}", lb=min_effort[i], ub=max_effort[i]) 
              for i in poll_sources}
    
    # 3. OBJECTIVE FUNCTION
    model.setObjective(gp.quicksum(support_rates[i] * effort[i] for i in poll_sources), GRB.MAXIMIZE)
    
    # 4. CONSTRAINTS - CORRECT SYNTAX PATTERNS
    
    # Total effort constraint (budget constraint)
    model.addConstr(gp.quicksum(effort[i] for i in poll_sources) <= total_budget, name="total_budget")
    
    # 5. SOLVING & RESULTS
    model.optimize()
    
    if model.status == GRB.OPTIMAL:
        print(f"Optimal value: {model.objVal}")
        for i in poll_sources:
            if effort[i].x > 1e-6:
                print(f"effort[{i}] = {effort[i].x:.3f}")
    elif model.status == GRB.INFEASIBLE:
        print("Problem is infeasible")
    elif model.status == GRB.UNBOUNDED:
        print("Problem is unbounded")
    
    return model

# Run the optimization
candidate_poll_optimization()