#!/usr/bin/env python3
"""
Gurobipy 12.0.2 Cheat Sheet - Common Patterns & Syntax
Use as in-context example for LLM code generation
"""

import gurobipy as gp
from gurobipy import GRB

def optimization_example():
    """Essential Gurobipy patterns - covers 90% of use cases"""
    
    # 1. MODEL & DATA SETUP
    model = gp.Model("example")
    n_items = 5
    costs = [10, 15, 12, 8, 25]
    demands = [20, 30, 15, 40, 35]
    
    # CRITICAL: Validate array lengths before loops
    assert len(costs) == len(demands) == n_items, "Array length mismatch"
    
    # 2. VARIABLES
    # Single variables
    total = model.addVar(vtype=GRB.CONTINUOUS, name="total", lb=0)
    
    # Variable dictionaries (most common)
    x = {i: model.addVar(vtype=GRB.CONTINUOUS, name=f"x_{i}", lb=0) 
         for i in range(n_items)}
    
    y = {i: model.addVar(vtype=GRB.BINARY, name=f"y_{i}") 
         for i in range(n_items)}
    
    # Variable arrays using addVars
    z = model.addVars(n_items, vtype=GRB.INTEGER, name="z", lb=0, ub=10)
    
    # 3. OBJECTIVE FUNCTION
    # Use gp.quicksum for sums (not Python sum())
    model.setObjective(gp.quicksum(costs[i] * x[i] for i in range(n_items)), GRB.MINIMIZE)
    
    # 4. CONSTRAINTS - CORRECT SYNTAX PATTERNS
    
    # Pattern A: Single constraint with comparison operators (RECOMMENDED)
    model.addConstr(gp.quicksum(x[i] for i in range(n_items)) <= 100, name="total_limit")
    
    # Pattern B: Individual constraints in loop
    for i in range(n_items):
        model.addConstr(x[i] <= demands[i] * y[i], name=f"link_{i}")
    
    # Pattern C: Batch constraints using addConstrs
    model.addConstrs((x[i] >= 0.1 * demands[i] for i in range(n_items)), name="min_prod")
    
    # Pattern D: Complex expressions
    for i in range(n_items):
        expr = gp.quicksum(costs[j] * x[j] for j in range(i+1))
        model.addConstr(expr <= 50, name=f"partial_sum_{i}")
    
    # 5. SOLVING & RESULTS
    model.optimize()
    
    if model.status == GRB.OPTIMAL:
        print(f"Optimal value: {model.objVal}")
        for i in range(n_items):
            if x[i].x > 1e-6:
                print(f"x[{i}] = {x[i].x:.3f}")
    elif model.status == GRB.INFEASIBLE:
        print("Problem is infeasible")
    elif model.status == GRB.UNBOUNDED:
        print("Problem is unbounded")
    
    return model

# SYNTAX RULES TO FOLLOW:
"""
CORRECT CONSTRAINT SYNTAX:
model.addConstr(expr <= rhs, name="constraint_name")
model.addConstr(expr >= rhs, name="constraint_name") 
model.addConstr(expr == rhs, name="constraint_name")

CORRECT SUMMATION:
gp.quicksum(coef[i] * var[i] for i in range(n))
gp.quicksum(var[i] for i in indices if condition)

ARRAY SAFETY:
assert len(array1) == len(array2) == expected_len
safe_range = range(min(len(array1), len(array2)))

WRONG SYNTAX (causes errors):
model.addConstr(expr, GRB.LESS_EQUAL, rhs)  # Old syntax
model.addConstr(expr, GRB.LESS_EQUAL, rhs, name)  # Wrong args
sum(...)  # Use gp.quicksum() instead
"""

