Drop lots of legacy code. Support Python 3.7-3.11.
[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 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)  # doctest: +IGNORE_EXCEPTION_DETAIL
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 not isinstance(input, int):
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',
58             'I')
59     result = ""
60     for i in range(len(ints)):
61         count = int(input / ints[i])
62         result += nums[i] * count
63         input -= ints[i] * count
64     return result
65
66
67 def roman_to_int(input):
68     """
69     Convert a roman numeral to an integer.
70
71     >>> r = list(range(1, 4000))
72     >>> nums = [int_to_roman(i) for i in r]
73     >>> ints = [roman_to_int(n) for n in nums]
74     >>> print(r == ints)
75     1
76
77     >>> roman_to_int('VVVIV')
78     Traceback (most recent call last):
79      ...
80     ValueError: input is not a valid roman numeral: VVVIV
81     >>> roman_to_int(1)  # doctest: +IGNORE_EXCEPTION_DETAIL
82     Traceback (most recent call last):
83      ...
84     TypeError: expected string, got <type 'int'>
85     >>> roman_to_int('a')
86     Traceback (most recent call last):
87      ...
88     ValueError: input is not a valid roman numeral: A
89     >>> roman_to_int('IL')
90     Traceback (most recent call last):
91      ...
92     ValueError: input is not a valid roman numeral: IL
93     """
94     if not isinstance(input, str):
95         raise TypeError("expected string, got %s" % type(input))
96     input = input.upper()
97     nums = ['M', 'D', 'C', 'L', 'X', 'V', 'I']
98     ints = [1000, 500, 100, 50,  10,  5,    1]
99     places = []
100     for c in input:
101         if c not in nums:
102             raise ValueError("input is not a valid roman numeral: %s" % input)
103     for i in range(len(input)):
104         c = input[i]
105         value = ints[nums.index(c)]
106         # If the next place holds a larger number, this value is negative.
107         try:
108             nextvalue = ints[nums.index(input[i + 1])]
109             if nextvalue > value:
110                 value *= -1
111         except IndexError:
112             # there is no next place.
113             pass
114         places.append(value)
115     sum = 0
116     for n in places:
117         sum += n
118     # Easiest test for validity...
119     if int_to_roman(sum) == input:
120         return sum
121     else:
122         raise ValueError('input is not a valid roman numeral: %s' % input)
123
124
125 def makedirs(path):
126     if not os.path.isdir(path):
127         os.makedirs(path)
128
129
130 def get_translation(language):
131     import gettext
132     from .functions import lang_code_3to2
133
134     return gettext.translation(
135         'messages',
136         localedir=os.path.join(os.path.dirname(__file__), 'locale'),
137         languages=[lang_code_3to2(language), 'pl'],
138     )