diff options
author | Guido van Rossum <guido@python.org> | 2007-08-16 23:48:43 (GMT) |
---|---|---|
committer | Guido van Rossum <guido@python.org> | 2007-08-16 23:48:43 (GMT) |
commit | af554a0e17ceb0e6a3cc0c07e9cf6db2f80c1ad9 (patch) | |
tree | 7719b2787d82dd1cc7afad76ad989fc9b1f1504f /Doc/tools | |
parent | 10c17ba299513184e39c85fc5c3ecaf2fd13187e (diff) | |
download | cpython-af554a0e17ceb0e6a3cc0c07e9cf6db2f80c1ad9.zip cpython-af554a0e17ceb0e6a3cc0c07e9cf6db2f80c1ad9.tar.gz cpython-af554a0e17ceb0e6a3cc0c07e9cf6db2f80c1ad9.tar.bz2 |
First merge from the trunk straight into the py3k branch. I'm not
using the message generated by svnmerge, because it contains a lot of
stuff about the Doc tree, which I'm not merging this time due to the
way the Doc tree was initially added. I am however adding roman.py
which was added later to Doc/tools. I'll try to diff the two Doc
trees separately to see if there's stuff I missed.
Diffstat (limited to 'Doc/tools')
-rw-r--r-- | Doc/tools/roman.py | 80 |
1 files changed, 80 insertions, 0 deletions
diff --git a/Doc/tools/roman.py b/Doc/tools/roman.py new file mode 100644 index 0000000..33f6db7 --- /dev/null +++ b/Doc/tools/roman.py @@ -0,0 +1,80 @@ +"""Convert to and from Roman numerals""" + +__author__ = "Mark Pilgrim (f8dy@diveintopython.org)" +__version__ = "1.4" +__date__ = "8 August 2001" +__copyright__ = """Copyright (c) 2001 Mark Pilgrim + +This program is part of "Dive Into Python", a free Python tutorial for +experienced programmers. Visit http://diveintopython.org/ for the +latest version. + +This program is free software; you can redistribute it and/or modify +it under the terms of the Python 2.1.1 license, available at +http://www.python.org/2.1.1/license.html +""" + +import re + +#Define exceptions +class RomanError(Exception): pass +class OutOfRangeError(RomanError): pass +class NotIntegerError(RomanError): pass +class InvalidRomanNumeralError(RomanError): pass + +#Define digit mapping +romanNumeralMap = (('M', 1000), + ('CM', 900), + ('D', 500), + ('CD', 400), + ('C', 100), + ('XC', 90), + ('L', 50), + ('XL', 40), + ('X', 10), + ('IX', 9), + ('V', 5), + ('IV', 4), + ('I', 1)) + +def toRoman(n): + """convert integer to Roman numeral""" + if not (0 < n < 5000): + raise OutOfRangeError, "number out of range (must be 1..4999)" + if int(n) <> n: + raise NotIntegerError, "decimals can not be converted" + + result = "" + for numeral, integer in romanNumeralMap: + while n >= integer: + result += numeral + n -= integer + return result + +#Define pattern to detect valid Roman numerals +romanNumeralPattern = re.compile(""" + ^ # beginning of string + M{0,4} # thousands - 0 to 4 M's + (CM|CD|D?C{0,3}) # hundreds - 900 (CM), 400 (CD), 0-300 (0 to 3 C's), + # or 500-800 (D, followed by 0 to 3 C's) + (XC|XL|L?X{0,3}) # tens - 90 (XC), 40 (XL), 0-30 (0 to 3 X's), + # or 50-80 (L, followed by 0 to 3 X's) + (IX|IV|V?I{0,3}) # ones - 9 (IX), 4 (IV), 0-3 (0 to 3 I's), + # or 5-8 (V, followed by 0 to 3 I's) + $ # end of string + """ ,re.VERBOSE) + +def fromRoman(s): + """convert Roman numeral to integer""" + if not s: + raise InvalidRomanNumeralError, 'Input can not be blank' + if not romanNumeralPattern.search(s): + raise InvalidRomanNumeralError, 'Invalid Roman numeral: %s' % s + + result = 0 + index = 0 + for numeral, integer in romanNumeralMap: + while s[index:index+len(numeral)] == numeral: + result += integer + index += len(numeral) + return result |