smaller vspaces
[librarian.git] / librarian / util.py
1 # Functions to convert between integers and Roman numerals. Doctest examples included.
2 # by Paul Winkler 
3 # http://code.activestate.com/recipes/81611-roman-numerals/
4 # PSFL (GPL compatible)
5 import os
6
7
8 def int_to_roman(input):
9     """
10     Convert an integer to Roman numerals.
11
12     Examples:
13     >>> int_to_roman(0)
14     Traceback (most recent call last):
15     ValueError: Argument must be between 1 and 3999
16
17     >>> int_to_roman(-1)
18     Traceback (most recent call last):
19     ValueError: Argument must be between 1 and 3999
20
21     >>> int_to_roman(1.5)
22     Traceback (most recent call last):
23     TypeError: expected integer, got <type 'float'>
24
25     >>> for i in range(1, 21): print int_to_roman(i)
26     ...
27     I
28     II
29     III
30     IV
31     V
32     VI
33     VII
34     VIII
35     IX
36     X
37     XI
38     XII
39     XIII
40     XIV
41     XV
42     XVI
43     XVII
44     XVIII
45     XIX
46     XX
47     >>> print int_to_roman(2000)
48     MM
49     >>> print int_to_roman(1999)
50     MCMXCIX
51     """
52     if type(input) != type(1):
53         raise TypeError, "expected integer, got %s" % type(input)
54     if not 0 < input < 4000:
55         raise ValueError, "Argument must be between 1 and 3999"    
56     ints = (1000, 900,  500, 400, 100,  90, 50,  40, 10,  9,    5,  4,    1)
57     nums = ('M',  'CM', 'D', 'CD','C', 'XC','L','XL','X','IX','V','IV','I')
58     result = ""
59     for i in range(len(ints)):
60         count = int(input / ints[i])
61         result += nums[i] * count
62         input -= ints[i] * count
63     return result
64
65 def roman_to_int(input):
66     """
67     Convert a roman numeral to an integer.
68     
69     >>> r = range(1, 4000)
70     >>> nums = [int_to_roman(i) for i in r]
71     >>> ints = [roman_to_int(n) for n in nums]
72     >>> print r == ints
73     1
74
75     >>> roman_to_int('VVVIV')
76     Traceback (most recent call last):
77      ...
78     ValueError: input is not a valid roman numeral: VVVIV
79     >>> roman_to_int(1)
80     Traceback (most recent call last):
81      ...
82     TypeError: expected string, got <type 'int'>
83     >>> roman_to_int('a')
84     Traceback (most recent call last):
85      ...
86     ValueError: input is not a valid roman numeral: A
87     >>> roman_to_int('IL')
88     Traceback (most recent call last):
89      ...
90     ValueError: input is not a valid roman numeral: IL
91     """
92     if type(input) != type(""):
93         raise TypeError, "expected string, got %s" % type(input)
94     input = input.upper()
95     nums = ['M', 'D', 'C', 'L', 'X', 'V', 'I']
96     ints = [1000, 500, 100, 50,  10,  5,    1]
97     places = []
98     for c in input:
99         if not c in nums:
100             raise ValueError, "input is not a valid roman numeral: %s" % input
101     for i in range(len(input)):
102         c = input[i]
103         value = ints[nums.index(c)]
104         # If the next place holds a larger number, this value is negative.
105         try:
106             nextvalue = ints[nums.index(input[i +1])]
107             if nextvalue > value:
108                 value *= -1
109         except IndexError:
110             # there is no next place.
111             pass
112         places.append(value)
113     sum = 0
114     for n in places: sum += n
115     # Easiest test for validity...
116     if int_to_roman(sum) == input:
117         return sum
118     else:
119         raise ValueError, 'input is not a valid roman numeral: %s' % input
120
121
122 def makedirs(path):
123     if not os.path.isdir(path):
124         os.makedirs(path)