Skip to content

Instantly share code, notes, and snippets.

@dmtucker
Created April 25, 2020 03:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dmtucker/12b72c30eb9600eb95cf610ddfd9f3a0 to your computer and use it in GitHub Desktop.
Save dmtucker/12b72c30eb9600eb95cf610ddfd9f3a0 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python3
ROMAN_LEGEND = {
'I': 1,
'V': 5,
'X': 10,
'L': 50,
'C': 100,
'D': 500,
'M': 1000,
}
LEGEND_ROMAN = {v: k for k, v in ROMAN_LEGEND.items()}
def _subtractive(roman, i):
return any(ROMAN_LEGEND[roman[j]] > ROMAN_LEGEND[roman[i]] for j in range(i + 1, len(roman)))
def roman_to_decimal(roman):
roman_values = [ROMAN_LEGEND[c] for c in roman]
##return sum(roman_values) # naive, misses e.g. IX, XC, etc.
#new_romans = []
#for i, x in enumerate(roman_values):
# new_romans.append(x)
# try:
# if roman_values[i + 1] > x:
# new_romans[i] = -x
# except IndexError:
# break
#return sum(new_romans) # almost, misses alternative forms e.g. XIIX, IIXX, etc.
return sum(-x if _subtractive(roman, i) else x for i, x in enumerate(roman_values))
def decimal_to_roman(decimal):
roman = ''
for key in sorted(LEGEND_ROMAN.keys(), reverse=True):
while decimal >= key:
roman += LEGEND_ROMAN[key]
decimal -= key
return roman # naive, does not use the subtractive form
ROMAN_DECIMALS = {
'I': 1,
'V': 5,
'VI': 6,
'IV': 4,
'X': 10,
'IX': 9,
'XI': 11,
'XV': 15,
'L': 50,
'C': 100,
'XC': 90,
'XCIX': 99,
'CM': 900,
'MDCCLXXVI': 1776,
'MCMIII': 1903,
'MCMX': 1910,
'MCMLIV': 1954,
'MCMXC': 1990,
'MMXIV': 2014,
}
ROMAN_ALTERNATIVES = {
'IIII': 4,
'VIIII': 9,
'XIIX': 18,
'IIXX': 18,
'IC': 99,
'MDCDIII': 1903,
'MDCCCCX': 1910,
}
def test_roman_to_decimal():
#assert all(roman_to_decimal(roman) == decimal for roman, decimal in ROMAN_DECIMALS.items())
for roman, decimal in ROMAN_DECIMALS.items():
assert roman_to_decimal(roman) == decimal
def test_roman_to_decimal_alt():
#assert all(roman_to_decimal(roman) == decimal for roman, decimal in ROMAN_ALTERNATIVES.items())
for roman, decimal in ROMAN_ALTERNATIVES.items():
assert roman_to_decimal(roman) == decimal
def test_decimal_to_roman():
#assert all(decimal_to_roman(decimal) == roman for roman, decimal in ROMAN_DECIMALS.items())
for roman, decimal in ROMAN_DECIMALS.items():
assert decimal_to_roman(decimal) == roman
# It doesn't make sense to test alternative forms against test_decimal_to_roman, which should return the canonical form.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment