PageRenderTime 24ms CodeModel.GetById 15ms app.highlight 6ms RepoModel.GetById 2ms app.codeStats 0ms

/Doc/tools/roman.py

http://unladen-swallow.googlecode.com/
Python | 80 lines | 78 code | 1 blank | 1 comment | 1 complexity | 9c65b77db9b9eaff722ff9383213fb86 MD5 | raw file
 1"""Convert to and from Roman numerals"""
 2
 3__author__ = "Mark Pilgrim (f8dy@diveintopython.org)"
 4__version__ = "1.4"
 5__date__ = "8 August 2001"
 6__copyright__ = """Copyright (c) 2001 Mark Pilgrim
 7
 8This program is part of "Dive Into Python", a free Python tutorial for
 9experienced programmers.  Visit http://diveintopython.org/ for the
10latest version.
11
12This program is free software; you can redistribute it and/or modify
13it under the terms of the Python 2.1.1 license, available at
14http://www.python.org/2.1.1/license.html
15"""
16
17import re
18
19#Define exceptions
20class RomanError(Exception): pass
21class OutOfRangeError(RomanError): pass
22class NotIntegerError(RomanError): pass
23class InvalidRomanNumeralError(RomanError): pass
24
25#Define digit mapping
26romanNumeralMap = (('M',  1000),
27                   ('CM', 900),
28                   ('D',  500),
29                   ('CD', 400),
30                   ('C',  100),
31                   ('XC', 90),
32                   ('L',  50),
33                   ('XL', 40),
34                   ('X',  10),
35                   ('IX', 9),
36                   ('V',  5),
37                   ('IV', 4),
38                   ('I',  1))
39
40def toRoman(n):
41    """convert integer to Roman numeral"""
42    if not (0 < n < 5000):
43        raise OutOfRangeError, "number out of range (must be 1..4999)"
44    if int(n) <> n:
45        raise NotIntegerError, "decimals can not be converted"
46
47    result = ""
48    for numeral, integer in romanNumeralMap:
49        while n >= integer:
50            result += numeral
51            n -= integer
52    return result
53
54#Define pattern to detect valid Roman numerals
55romanNumeralPattern = re.compile("""
56    ^                   # beginning of string
57    M{0,4}              # thousands - 0 to 4 M's
58    (CM|CD|D?C{0,3})    # hundreds - 900 (CM), 400 (CD), 0-300 (0 to 3 C's),
59                        #            or 500-800 (D, followed by 0 to 3 C's)
60    (XC|XL|L?X{0,3})    # tens - 90 (XC), 40 (XL), 0-30 (0 to 3 X's),
61                        #        or 50-80 (L, followed by 0 to 3 X's)
62    (IX|IV|V?I{0,3})    # ones - 9 (IX), 4 (IV), 0-3 (0 to 3 I's),
63                        #        or 5-8 (V, followed by 0 to 3 I's)
64    $                   # end of string
65    """ ,re.VERBOSE)
66
67def fromRoman(s):
68    """convert Roman numeral to integer"""
69    if not s:
70        raise InvalidRomanNumeralError, 'Input can not be blank'
71    if not romanNumeralPattern.search(s):
72        raise InvalidRomanNumeralError, 'Invalid Roman numeral: %s' % s
73
74    result = 0
75    index = 0
76    for numeral, integer in romanNumeralMap:
77        while s[index:index+len(numeral)] == numeral:
78            result += integer
79            index += len(numeral)
80    return result