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


def int_to_roman(n):
    if not isinstance(n, int):
        raise ValueError("Input must be an integer")
    if not 1 <= n <= 3999:
        raise ValueError("Integer must be between 1 and 3999")
    
    mapping = [
        (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 = []
    for value, symbol in mapping:
        while n >= value:
            result.append(symbol)
            n -= value
    return ''.join(result)


def roman_to_int(s):
    if not isinstance(s, str) or len(s) == 0:
        raise ValueError("Input must be a non-empty string")
    
    # Canonical Roman numeral pattern (up to 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.match(pattern, s):
        raise ValueError("Invalid canonical Roman numeral")
    
    roman_map = {
        'I': 1, 'V': 5, 'X': 10, 'L': 50,
        'C': 100, 'D': 500, 'M': 1000
    }
    total = 0
    prev_value = 0
    for ch in reversed(s):
        value = roman_map[ch]
        if value < prev_value:
            total -= value
        else:
            total += value
        prev_value = value
    return total