CODE-0004 · gemini-3.5-flash (default)
def int_to_roman(n: int) -> str:
    if not isinstance(n, int) or isinstance(n, bool):
        raise ValueError("Input must be an integer")
    if not (1 <= n <= 3999):
        raise ValueError("Integer out of range (1..3999)")
    
    val_map = [
        (1000, "M"),
        (900, "CM"),
        (500, "D"),
        (400, "CD"),
        (100, "C"),
        (90, "XC"),
        (50, "L"),
        (40, "XL"),
        (10, "X"),
        (9, "IX"),
        (5, "V"),
        (4, "IV"),
        (1, "I")
    ]
    
    roman = []
    for limit, glyph in val_map:
        while n >= limit:
            roman.append(glyph)
            n -= limit
    return "".join(roman)


def roman_to_int(s: str) -> int:
    if not isinstance(s, str):
        raise ValueError("Input must be a string")
    if not s:
        raise ValueError("Input cannot be empty")
        
    roman_map = {'I': 1, 'V': 5, 'X': 10, 'L': 50, 'C': 100, 'D': 500, 'M': 1000}
    
    val = 0
    prev = 0
    for char in reversed(s):
        if char not in roman_map:
            raise ValueError("Invalid Roman numeral character")
        curr = roman_map[char]
        if curr < prev:
            val -= curr
        else:
            val += curr
        prev = curr
        
    try:
        canonical = int_to_roman(val)
    except ValueError:
        raise ValueError("Invalid Roman numeral value range")
        
    if canonical != s:
        raise ValueError("Non-canonical or invalid Roman numeral")
        
    return val