Rubykuby

romannumerals.py

Feb 9th, 2014
364
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 4.41 KB | None | 0 0
  1. #    This program is free software: you can redistribute it and/or modify
  2. #    it under the terms of the GNU General Public License as published by
  3. #    the Free Software Foundation, either version 3 of the License, or
  4. #    (at your option) any later version.
  5.  
  6. #    This program is distributed in the hope that it will be useful,
  7. #    but WITHOUT ANY WARRANTY; without even the implied warranty of
  8. #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  9. #    GNU General Public License for more details.
  10.  
  11. #    You should have received a copy of the GNU General Public License
  12. #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  13.  
  14. # Global dictionary entry
  15. NUMERALS = {'I':1,
  16.             'V':5,
  17.             'X':10,
  18.             'L':50,
  19.             'C':100,
  20.             'D':500,
  21.             'M':1000}
  22.  
  23. def verify_combo(numeral, first, second):
  24.     """Compares two values for a ratio. The maximal ratio between two numbers in
  25.    the Roman numeral system, it turns out, is 0.1. If this condition is not
  26.    met, throw an exception.
  27.    """
  28.    
  29.     if first / second >= 0.1:
  30.         pass
  31.     else:
  32.         raise ValueError("{} is not a valid Roman numeral.".format(numeral))
  33.  
  34. def is_combo(first, second):
  35.     """In case of Roman numeral combos (e.g., IV), the second value is always
  36.    bigger than the first. This function tests against that.
  37.    """
  38.    
  39.     if first < second:
  40.         return True
  41.     else:
  42.         return False
  43.  
  44. def compare(numeral, index):
  45.     """Compares preceeding, current and succeeding numeral digits.
  46.    Returns current digit if no combo is present.
  47.    Returns combo value of current digit and succeeding digit are a combo.
  48.    Returns 0 if current digit and preceeding digit are a combo.
  49.    """
  50.  
  51.     # Initialise integer values.
  52.     try:
  53.         previous = NUMERALS[numeral[index-1]]
  54.         current = NUMERALS[numeral[index]]
  55.         next = NUMERALS[numeral[index+1]]
  56.     except IndexError:
  57.         pass
  58.  
  59.     if index != len(numeral)-1 and is_combo(current, next):
  60.         verify_combo(numeral, current, next)
  61.         return next - current
  62.     elif index > 0 and is_combo(previous, current):
  63.         return 0
  64.     else:
  65.         return current
  66.  
  67. def numeral_to_decimal(numeral):
  68.     """Converts a numeral string value to a decimal int value."""
  69.     decimal = 0
  70.  
  71.     # Loop through every possible letter in the numeral value.
  72.     # Add returned value from compare() to decimal.
  73.     for letter in range(len(numeral)):
  74.         decimal += compare(numeral, letter)
  75.  
  76.     return decimal
  77.  
  78. def decimal_to_numeral(decimal):
  79.     """Converts a decimal int value to a numeral string value."""
  80.     numeral = ""
  81.     numeral_tuples = []
  82.  
  83.     # Generate a tuple of tuples that is ordered (numeral digit, decimal value).
  84.     for letter in sorted(NUMERALS, key=NUMERALS.get, reverse=True):
  85.         numeral_tuples.append((letter, NUMERALS[letter]))
  86.  
  87.     # For every possible numeral digit.
  88.     for i in range(len(NUMERALS)):
  89.         # Perform below operations until decimal is smaler than the
  90.         # corresponding value of the letter.
  91.         while decimal >= numeral_tuples[i][1]:
  92.             # The magic happens below. Undocumentable.
  93.             if str(decimal).startswith('9')\
  94.             and str(numeral_tuples[i][1]).startswith('5'):
  95.                 break
  96.             elif str(decimal).startswith('4'):
  97.                 decimal -= numeral_tuples[i-1][1] * 0.8
  98.                 numeral += numeral_tuples[i][0] + numeral_tuples[i-1][0]
  99.             elif str(decimal).startswith('9'):
  100.                 decimal -= numeral_tuples[i-2][1] * 0.9
  101.                 numeral += numeral_tuples[i][0] + numeral_tuples[i-2][0]
  102.             else:
  103.                 decimal -= numeral_tuples[i][1]
  104.                 numeral += numeral_tuples[i][0]
  105.  
  106.     return numeral
  107.  
  108. def main():
  109.     # Test results.
  110.     print(numeral_to_decimal("MCMLIV"))
  111.     print()
  112.     print(numeral_to_decimal("MMVIII"))
  113.     print()
  114.     print(numeral_to_decimal("MCMXC"))
  115.     print()
  116.     print(numeral_to_decimal("MCDIV"))
  117.     #print()
  118.     #print(numeral_to_decimal("IM"))
  119.     print()
  120.     print(numeral_to_decimal(decimal_to_numeral(114)))
  121.     print()
  122.     print(decimal_to_numeral(numeral_to_decimal("MCMLIV")))
  123.     print()
  124.     print(decimal_to_numeral(numeral_to_decimal("MMVIII")))
  125.     print()
  126.     print(numeral_to_decimal(decimal_to_numeral(444)))
  127.  
  128. if __name__ == "__main__":
  129.     main()
Advertisement
Add Comment
Please, Sign In to add comment