solution_022.py

#!/usr/bin/python3
# ===================================================================
# Converting Roman Numerals To Decimal
#
# From: stackoverflow.com/questions/9073150/
#       converting-roman-numerals-to-decimal
#
# I found this code on the web. I really like the recursive
# approach to solving the problem. (Note: It was originally
# written in javascript.)
#
# ===================================================================
# This code does not recognize the following error situation:
#
#   A letter cannot be repeated more than 3 times, and only powers
#   of 10 can be repeated. IIII converts to 4 but is not
#   allowed by the rules for roman numerals.
#
#   Perhaps check for bad input before trying to convert?
#     re.search("IIII")
#     re.search("XXXX")
#     re.search("CCCC")
#     re.search("LL")
#     re.search("CC")
#     re.search("DD")
#
# ===================================================================
# Rules for writing roman numerals
#   (From: www.cuemath.com/numbers/roman-numerals/)
#
#   a. The letters I,X,C can be repeated three time in succession.
#      L, V, D cannot be repeated or the number is considered to
#      be invalid.
#   b. If a lower value digit is written to the left of a higher
#      value digit, it is subtracted
#   c. If a lower value digit is written to the right of a higher
#      value, it is added
#   d.Only I,X and C can be used as subtractive numerals
#
# ===================================================================

import re
import sys

# -------------------------------------------------------------------
# ---- convert roman numerals to decimal (recursive function)
# ---- input must be all caps
# -------------------------------------------------------------------

def roman_to_integer(nstr):

    print(f'*** roman_to_integer({nstr})')

    if nstr == '':
        return 0
    if re.match("^M",nstr):
        return 1000 + roman_to_integer(nstr[1:])
    if re.match("^CM",nstr):
        return 900  + roman_to_integer(nstr[2:])
    if re.match("^D",nstr):
        return 500  + roman_to_integer(nstr[1:])
    if re.match("^CD",nstr):
        return 400  + roman_to_integer(nstr[2:])
    if re.match("^C",nstr):
        return 100  + roman_to_integer(nstr[1:])
    if re.match("^XC",nstr):
        return 90   + roman_to_integer(nstr[2:])
    if re.match("^L",nstr):
        return 50   + roman_to_integer(nstr[1:])
    if re.match("^XL",nstr):
        return 40   + roman_to_integer(nstr[2:])
    if re.match("^X",nstr):
        return 10   + roman_to_integer(nstr[1:])
    if re.match("^IX",nstr):
        return 9    + roman_to_integer(nstr[2:])
    if re.match("^V",nstr):
        return 5    + roman_to_integer(nstr[1:])
    if re.match("^IV",nstr):
        return 4    + roman_to_integer(nstr[2:])
    if re.match("^I",nstr):
        return 1    + roman_to_integer(nstr[1:])

    print('error: something bad happened with ({nstr})')
    sys.exit()

# -------------------------------------------------------------------
# ---- main
# -------------------------------------------------------------------

if __name__ == '__main__':

    import user_interface as ui

    while(True):

       ui.clear_screen()

       print()
       s = ui.get_user_input('Enter roman number: ')

       if not s:
           break

       n = roman_to_integer(s.upper())

       print()
       if n > 0:
           print(f'roman number is {n} decimal')
       else:
           print(f'bad roman number input ({s})')

       ui.pause()