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