Last active
August 29, 2015 14:28
-
-
Save khardix/44d4b16f5fb1d1291a81 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
# -*- coding: utf-8 -*- | |
ROMAN_NUMBERS = { | |
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' | |
} | |
def to_roman(value): | |
"""Convert integer value to roman numeral string. | |
Keyword arguments: | |
value -- Converted value. | |
Returns: string | |
""" | |
value = int(value) | |
if value <= 0: | |
raise ValueError("Value too small (<= 0)") | |
steps = sorted(ROMAN_NUMBERS.keys()) | |
output = "" | |
while value > 0: | |
step = steps[-1] | |
if step <= value: # Substract and convert | |
value -= step | |
output += ROMAN_NUMBERS[step] | |
else: # Move to lesser one | |
steps.pop() | |
return output | |
def from_roman(value): | |
"""Convert string of roman numerals to number. | |
Keyword arguments: | |
value -- String representing roman numeral. | |
Returns: integer, or None if the input is empty string | |
""" | |
# Backward conversion | |
SYMBOLS = {symbol: value for value, symbol in ROMAN_NUMBERS.items()} | |
SYM_MAX = max([len(symbol) for symbol in SYMBOLS.keys()]) | |
output = 0 | |
remains = value.strip() | |
while len(remains) > 0: | |
if remains.startswith(tuple(SYMBOLS.keys())): | |
for l in range(SYM_MAX, 0, -1): # find the prefix, start from longest | |
if remains[:l] in SYMBOLS: | |
output += SYMBOLS[remains[:l]] | |
remains = remains[l:] | |
break | |
else: | |
raise ValueError("Invalid roman numeral") | |
if output == 0: | |
return None | |
else: | |
return output | |
if __name__ == '__main__': | |
from sys import stdin | |
from argparse import ArgumentParser, SUPPRESS | |
def words(istream=stdin): | |
"""Yields words from input stream.""" | |
for line in istream: | |
for word in line.split(): yield word | |
parser = ArgumentParser(description="Converts numbers to/from roman numerals.") | |
conversions = parser.add_mutually_exclusive_group(required=False) | |
conversions.add_argument('-t', '--to-roman', dest="number", nargs='*', type=int, default=SUPPRESS, | |
help="Convert integer to roman number" | |
) | |
conversions.add_argument('-f', '--from-roman', dest="roman", nargs='*', type=str, default=SUPPRESS, | |
help="Convert roman literal to integer" | |
) | |
arguments = vars(parser.parse_args()) | |
# Source - an iterable which produces words to be converted | |
# Converter - actual converting function | |
if "number" in arguments: # To roman | |
(source, converter) = (arguments["number"], to_roman) | |
elif "roman" in arguments: # To integer | |
(source, converter) = (arguments["roman"], from_roman) | |
else: # Default - try to roman | |
(source, converter) = (None, to_roman) | |
# Detect "empty" input and switch to stdin | |
if source is None or len(source) == 0: source = words(stdin) | |
try: | |
for value in source: print(converter(value)) | |
except ValueError as err: | |
raise SystemExit("Error: " + str(err)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment