Favorites support
[gonvert] / src / converters.py
1 """
2 All classes for conversions are defined below:
3  each class should have one method for converting "to_base and another for converting "from_base"
4 the return value is the converted value to or from base
5 """
6
7 # For the sake of eval'ing some code
8 import math
9
10
11 # used for Computer numbers base definitions.
12 ALPHA_NUMERIC = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
13
14
15 def makeBase(x, base = len(ALPHA_NUMERIC), table=ALPHA_NUMERIC):
16         """
17         Convert from base 10 to any other base.
18         >> makeBase(1, 10)
19         '1'
20         >> makeBase(11, 10)
21         '11'
22         >> makeBase(11, 16)
23         'b'
24         """
25         div, mod = divmod(x, base)
26         if not div:
27                 return table[mod]
28         return makeBase(div, base, table) + table[mod]
29
30
31 # roman numerals
32 roman_group = {
33         1: ('i', 'v'),
34         10: ('x', 'l'),
35         100: ('c', 'd'),
36         1000: ('m', 'A'),
37         10000: ('B', 'C'),
38 }
39
40
41 # functions that convert Arabic digits to roman numerals
42 roman_value = {
43         0: lambda i, v, x: '',
44         1: lambda i, v, x: i,
45         2: lambda i, v, x: i+i,
46         3: lambda i, v, x: i+i+i,
47         4: lambda i, v, x: i+v,
48         5: lambda i, v, x: v,
49         6: lambda i, v, x: v+i,
50         7: lambda i, v, x: v+i+i,
51         8: lambda i, v, x: v+i+i+i,
52         9: lambda i, v, x: i+x,
53 }
54
55
56 def toroman(n):
57         """
58         convert a decimal number in [1,4000) to a roman numeral
59         >>> toroman(0)
60         >>> toroman(4001)
61         >>> toroman(1)
62         'i'
63         >>> toroman(4)
64         'iv'
65         >>> toroman(5)
66         'v'
67         >>> toroman(10)
68         'x'
69         >>> toroman(15)
70         'xv'
71         """
72         if n < 0:
73                 raise NotImplementedError("Value out of roman comprehension")
74         elif n == 0:
75                 pass
76         elif n >= 4000:
77                 raise NotImplementedError("Value Out of Range")
78
79         base = 1
80         s = ''
81         while n > 0:
82                 i, v = roman_group[base]
83                 base = base * 10
84                 x, l = roman_group[base]
85                 digit = n % 10
86                 n = (n-digit)/10
87                 s = roman_value[digit](i, v, x) + s
88         return s
89
90
91 def fromroman(s, rbase = 1):
92         """
93         convert a roman numeral (in lowercase) to a decimal integer
94         >>> fromroman('')
95         0
96         >>> fromroman('x')
97         5
98         >>> fromroman('xv')
99         15
100         """
101         if len(s) == 0:
102                 return 0
103         elif rbase > 1000:
104                 return 0
105
106         i, v = roman_group[rbase]
107         x, l = roman_group[rbase*10]
108         conversions = [
109                 (v+i+i+i, 8),
110                 (i+i+i+i, 5),
111                 (v+i+i, 7),
112                 (i+i+i, 3),
113                 (v+i, 6),
114                 (i+x, 9),
115                 (i+v, 4),
116                 (i+i, 2),
117                 (i, 1),
118                 (v, 5),
119         ]
120         for conversion in conversions:
121                 if s.endswith(conversion[0]):
122                         digit = conversion[1]
123                         s = s[:-len(conversion[0])]
124                         break
125         else:
126                 digit = 0
127                 s = s
128
129         return digit * rbase + fromroman(s, rbase*10)
130
131
132 class simple_multiplier(object):
133
134         def to_base(self, value, multiplier):
135                 return value * (multiplier)
136
137         def from_base(self, value, multiplier):
138                 if multiplier == 0:
139                         return 0.0
140                 else:
141                         return value / (multiplier)
142
143
144 class simple_inverter(object):
145
146         def to_base(self, value, multiplier):
147                 if value == 0:
148                         return 0.0
149                 else:
150                         return (multiplier) / value
151
152         def from_base(self, value, multiplier):
153                 if value == 0:
154                         return 0.0
155                 else:
156                         return (multiplier) / value
157
158
159 class simple_gain_offset(object):
160
161         def to_base(self, value, (gain, offset)):
162                 return (value * (gain)) + offset
163
164         def from_base(self, value, (gain, offset)):
165                 if gain == 0:
166                         return 0.0
167                 else:
168                         return (value - offset) / gain
169
170
171 class simple_offset_gain(object):
172
173         def to_base(self, value, (offset, gain)):
174                 return (value + offset) * gain
175
176         def from_base(self, value, (offset, gain)):
177                 if gain == 0:
178                         return 0.0
179                 else:
180                         return (value / gain) - offset
181
182
183 class slope_offset(object):
184         ''"convert using points on a graph''"
185
186         def to_base(self, value, ((low_in, high_in), (low_out, high_out))):
187                 gain = (high_out-low_out)/(high_in-low_in)
188                 offset = low_out - gain*low_in
189                 return gain*value+offset
190
191         def from_base(self, value, ((low_out, high_out), (low_in, high_in))):
192                 gain = (high_out-low_out)/(high_in-low_in)
193                 offset = low_out - gain*low_in
194                 return gain*value+offset
195
196
197 class double_slope_offset(object):
198         ''"convert using points on a graph, graph split into two slopes''"
199
200         def to_base(self, value, ((low1_in, high1_in), (low1_out, high1_out), (low2_in, high2_in), (low2_out, high2_out))):
201                 if low1_in <= value <= high1_in:
202                         gain = (high1_out-low1_out)/(high1_in-low1_in)
203                         offset = low1_out - gain*low1_in
204                         return gain*value+offset
205                 if low2_in <= value <= high2_in:
206                         gain = (high2_out-low2_out)/(high2_in-low2_in)
207                         offset = low2_out - gain*low2_in
208                         return gain*value+offset
209                 return 0.0
210
211         def from_base(self, value, ((low1_in, high1_in), (low1_out, high1_out), (low2_in, high2_in), (low2_out, high2_out))):
212                 if low1_out <= value <= high1_out:
213                         gain = (high1_in-low1_in)/(high1_out-low1_out)
214                         offset = low1_in - gain*low1_out
215                         return gain*value+offset
216                 if low2_out <= value <= high2_out:
217                         gain = (high2_in-low2_in)/(high2_out-low2_out)
218                         offset = low2_in - gain*low2_out
219                         return gain*value+offset
220                 return 0.0
221
222
223 class base_converter(object):
224
225         def to_base(self, value, base):
226                 """
227                 Convert from any base to base 10 (decimal)
228                 """
229                 # Protection against fractional values
230                 value = value.split(".", 1)[0]
231
232                 result = 0L #will contain the long base-10 (decimal) number to be returned
233                 position = len(value) #length of the string that is to be converted
234                 for x in value:
235                         position = position-1
236                         result = long(result + long(long(ALPHA_NUMERIC.find(x))*(long(base)**long(position))))
237                 return result
238
239         def from_base(self, value, base):
240                 """
241                 Convert from decimal to any base
242                 """
243                 return makeBase(int(value), base)
244
245
246 class roman_numeral(object):
247
248         def to_base(self, value, junk):
249                 """
250                 Convert from roman numeral to base 10 (decimal)
251                 """
252                 if value == "0":
253                         return 0L
254                 else:
255                         return fromroman(value)
256
257         def from_base(self, value, junk):
258                 """
259                 Convert from decimal to roman numeral
260                 """
261                 return toroman(int(value))
262
263
264
265 class function(object):
266         ''"defined simple function can be as complicated as you like, however, both to/from base must be defined.''"
267
268         #value is assumed to be a string
269         #convert from a defined function to base
270         def to_base(self, value, (to_base, from_base)):
271                 y = 0 # "undefined" y was driving me nuts
272                 exec "y="+to_base[:to_base.find('x')]+str(value)+to_base[to_base.find('x')+1:]
273                 return y
274
275         def from_base(self, value, (to_base, from_base)):
276                 y = 0 # "undefined" y was driving me nuts
277                 exec "y="+from_base[:from_base.find('x')]+str(value)+from_base[from_base.find('x')+1:]
278                 return y
279
280
281 #--------- function definitions from classes ------------
282 m = simple_multiplier()
283 inv = simple_inverter()
284 gof = simple_gain_offset()
285 ofg = simple_offset_gain()
286 slo = slope_offset()
287 dso = double_slope_offset()
288 b = base_converter()
289 r = roman_numeral()
290 f = function()