CODE-0004 · grok-composer-2.5-fast (default)
_VALUES = {
    "I": 1,
    "V": 5,
    "X": 10,
    "L": 50,
    "C": 100,
    "D": 500,
    "M": 1000,
}

_SUBTRACTIVE = {("I", "V"), ("I", "X"), ("X", "L"), ("X", "C"), ("C", "D"), ("C", "M")}

_INT_TO_ROMAN_PAIRS = (
    (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"),
)


def int_to_roman(n: int) -> str:
    if not isinstance(n, int) or n < 1 or n > 3999:
        raise ValueError("integer must be in range 1..3999")

    parts: list[str] = []
    remaining = n
    for value, numeral in _INT_TO_ROMAN_PAIRS:
        while remaining >= value:
            parts.append(numeral)
            remaining -= value
    return "".join(parts)


def _parse_roman_value(s: str) -> int:
    total = 0
    i = 0
    length = len(s)
    while i < length:
        current = s[i]
        if current not in _VALUES:
            raise ValueError("invalid Roman numeral")

        if i + 1 < length:
            nxt = s[i + 1]
            if nxt not in _VALUES:
                raise ValueError("invalid Roman numeral")
            if _VALUES[current] < _VALUES[nxt]:
                pair = (current, nxt)
                if pair not in _SUBTRACTIVE:
                    raise ValueError("invalid Roman numeral")
                if current in "VLD":
                    raise ValueError("invalid Roman numeral")
                total += _VALUES[nxt] - _VALUES[current]
                i += 2
                continue

        if current in "VLD":
            if i + 1 < length and s[i + 1] == current:
                raise ValueError("invalid Roman numeral")

        if current == "I":
            j = i
            while j < length and s[j] == "I":
                j += 1
            if j - i > 3:
                raise ValueError("invalid Roman numeral")
        elif current == "X":
            j = i
            while j < length and s[j] == "X":
                j += 1
            if j - i > 3:
                raise ValueError("invalid Roman numeral")
        elif current == "C":
            j = i
            while j < length and s[j] == "C":
                j += 1
            if j - i > 3:
                raise ValueError("invalid Roman numeral")
        elif current == "M":
            j = i
            while j < length and s[j] == "M":
                j += 1
            if j - i > 3:
                raise ValueError("invalid Roman numeral")

        total += _VALUES[current]
        i += 1

    return total


def roman_to_int(s: str) -> int:
    if not s:
        raise ValueError("invalid Roman numeral")

    value = _parse_roman_value(s)
    if value < 1 or value > 3999:
        raise ValueError("invalid Roman numeral")

    if int_to_roman(value) != s:
        raise ValueError("invalid Roman numeral")

    return value