CODE-0004 · deepseek-v4-flash (default)
import re

def int_to_roman(n):
    if not (1 <= n <= 3999):
        raise ValueError("Integer out of range (1..3999)")
    value_symbol_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')
    ]
    result = []
    remaining = n
    for value, symbol in value_symbol_pairs:
        while remaining >= value:
            result.append(symbol)
            remaining -= value
    return ''.join(result)

def roman_to_int(s):
    if not isinstance(s, str) or s == '':
        raise ValueError("Empty string or invalid input")
    # Validate canonical Roman numeral (1..3999)
    pattern = r'^M{0,3}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$'
    if not re.fullmatch(pattern, s):
        raise ValueError("Malformed or non-canonical Roman numeral")
    value_map = {
        'I': 1,
        'V': 5,
        'X': 10,
        'L': 50,
        'C': 100,
        'D': 500,
        'M': 1000
    }
    total = 0
    prev_value = 0
    # Process from right to left to handle subtractive notation
    for char in reversed(s):
        cur_value = value_map[char]
        if cur_value < prev_value:
            total -= cur_value
        else:
            total += cur_value
        prev_value = cur_value
    return total