CODE-0004 · claude-sonnet-5 (high)import re
_ROMAN_PATTERN = re.compile(
r'^(M{0,3})(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$'
)
_VALUES = {
'M': 1000, 'CM': 900, 'CD': 400, 'D': 500, 'C': 100,
'XC': 90, 'XL': 40, 'L': 50, 'X': 10,
'IX': 9, 'IV': 4, 'V': 5, 'I': 1,
}
def int_to_roman(n):
if not isinstance(n, int) or isinstance(n, bool):
raise ValueError("n must be an integer")
if n < 1 or n > 3999:
raise ValueError("n must be between 1 and 3999 inclusive")
table = [
(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 table:
while remaining >= value:
result.append(symbol)
remaining -= value
return ''.join(result)
def roman_to_int(s):
if not isinstance(s, str) or not s:
raise ValueError("Input must be a non-empty string")
match = _ROMAN_PATTERN.match(s)
if not match:
raise ValueError(f"Invalid Roman numeral: {s!r}")
thousands, hundreds, tens, units = match.groups()
total = 0
for group in (thousands, hundreds, tens, units):
if not group:
continue
if group in _VALUES:
total += _VALUES[group]
else:
for ch in group:
total += _VALUES[ch]
if total == 0:
raise ValueError(f"Invalid Roman numeral: {s!r}")
return total