Initial checkin
[ejpi] / src / operation.py
1 #!/usr/bin/env python
2
3
4 import itertools
5 import functools
6 import decimal
7
8 from libraries.recipes import overloading
9 from libraries.recipes import algorithms
10
11
12 @overloading.overloaded
13 def serialize_value(value, base, renderer):
14         yield renderer(value, base)
15
16
17 @serialize_value.register(complex, overloading.AnyType, overloading.AnyType)
18 def serialize_complex(value, base, renderer):
19         if value.real == 0.0:
20                 yield renderer(value.imag*1j, base)
21         elif value.imag == 0.0:
22                 yield renderer(value.real, base)
23         else:
24                 yield renderer(value.real, base)
25                 yield renderer(value.imag*1j, base)
26                 yield "+"
27
28
29 def render_float(value):
30         return str(value)
31
32
33 def render_float_dec(value):
34         floatText = str(value)
35         dec = decimal.Decimal(floatText)
36         return str(dec)
37
38
39 def render_float_eng(value):
40         floatText = str(value)
41         dec = decimal.Decimal(floatText)
42         return dec.to_eng_string()
43
44
45 def render_float_sci(value):
46         floatText = str(value)
47         dec = decimal.Decimal(floatText)
48         return dec.to_sci_string()
49
50
51 def render_complex(floatRender):
52
53         def render_complex_real(value):
54                 realRendered = floatRender(value.real)
55                 imagRendered = floatRender(value.imag)
56                 rendered = "%s+%sj" % (realRendered, imagRendered)
57                 return rendered
58
59         return render_complex_real
60
61
62 def _seperate_num(rendered, sep, count):
63         """
64         >>> _seperate_num("123", ",", 3)
65         '123'
66         >>> _seperate_num("123456", ",", 3)
67         '123,456'
68         >>> _seperate_num("1234567", ",", 3)
69         '1,234,567'
70         """
71         leadCount = len(rendered) % count
72         choppyRest = algorithms.itergroup(rendered[leadCount:], count)
73         rest = (
74                 "".join(group)
75                 for group in choppyRest
76         )
77         if 0 < leadCount:
78                 lead = rendered[0:leadCount]
79                 parts = itertools.chain((lead, ), rest)
80         else:
81                 parts = rest
82         return sep.join(parts)
83
84
85 def render_integer_oct(value, sep=""):
86         rendered = oct(int(value))
87         if 0 < len(sep):
88                 assert rendered.startswith("0")
89                 rendered = "0o%s" % _seperate_num(rendered[1:], sep, 3)
90         return rendered
91
92
93 def render_integer_dec(value, sep=""):
94         rendered = str(int(value))
95         if 0 < len(sep):
96                 rendered = "%s" % _seperate_num(rendered, sep, 3)
97         return rendered
98
99
100 def render_integer_hex(value, sep=""):
101         rendered = hex(int(value))
102         if 0 < len(sep):
103                 assert rendered.startswith("0x")
104                 rendered = "0x%s" % _seperate_num(rendered[2:], sep, 3)
105         return rendered
106
107
108 def set_render_int_seperator(renderer, sep):
109
110         @functools.wrap(renderer)
111         def render_with_sep(value):
112                 return renderer(value, sep)
113
114         return render_with_sep
115
116
117 class render_number(object):
118
119         def __init__(self,
120                 ints = None,
121                 f = None,
122                 c = None,
123         ):
124                 if ints is not None:
125                         self.render_int = ints
126                 else:
127                         self.render_int = {
128                                 2: render_integer_hex,
129                                 8: render_integer_oct,
130                                 10: render_integer_dec,
131                                 16: render_integer_hex,
132                         }
133                 self.render_float = f if c is not None else render_float
134                 self.render_complex = c if c is not None else self
135
136         def __call__(self, value, base):
137                 return self.render(value, base)
138
139         @overloading.overloaded
140         def render(self, value, base):
141                 return str(value)
142
143         @render.register(overloading.AnyType, int, overloading.AnyType)
144         def _render_int(self, value, base):
145                 renderer = self.render_int.get(base, render_integer_dec)
146                 return renderer(value)
147
148         @render.register(overloading.AnyType, float, overloading.AnyType)
149         def _render_float(self, value, base):
150                 return self.render_float(value)
151
152         @render.register(overloading.AnyType, complex, overloading.AnyType)
153         def _render_complex(self, value, base):
154                 return self.render_float(value)
155
156
157 class Operation(object):
158
159         def __init__(self):
160                 self.base = 10
161
162         def __str__(self):
163                 raise NotImplementedError
164
165         def get_children(self):
166                 return []
167
168         def serialize(self, renderer):
169                 for child in self.get_children():
170                         for childItem in child.serialize(renderer):
171                                 yield childItem
172
173         def simplify(self):
174                 """
175                 @returns an operation tree with all constant calculations performed and only variables left
176                 """
177                 raise NotImplementedError
178
179         def evaluate(self):
180                 """
181                 @returns a value that the tree represents, if it can't be evaluated,
182                         then an exception is throwd
183                 """
184                 raise NotImplementedError
185
186         def __call__(self):
187                 return self.evaluate()
188
189
190 class Value(Operation):
191
192         def __init__(self, value, base):
193                 super(Value, self).__init__()
194                 self.value = value
195                 self.base = base
196
197         def serialize(self, renderer):
198                 for item in super(Value, self).serialize(renderer):
199                         yield item
200                 for component in serialize_value(self.value, self.base, renderer):
201                         yield component
202
203         def __str__(self):
204                 return str(self.value)
205
206         def simplify(self):
207                 return self
208
209         def evaluate(self):
210                 return self.value
211
212
213 class Constant(Operation):
214
215         def __init__(self, name, valueNode):
216                 super(Constant, self).__init__()
217                 self.name = name
218                 self.__valueNode = valueNode
219
220         def serialize(self, renderer):
221                 for item in super(Constant, self).serialize(renderer):
222                         yield item
223                 yield self.name
224
225         def __str__(self):
226                 return self.name
227
228         def simplify(self):
229                 return self.__valueNode.simplify()
230
231         def evaluate(self):
232                 return self.__valueNode.evaluate()
233
234
235 class Variable(Operation):
236
237         def __init__(self, name):
238                 super(Variable, self).__init__()
239                 self.name = name
240
241         def serialize(self, renderer):
242                 for item in super(Variable, self).serialize(renderer):
243                         yield item
244                 yield self.name
245
246         def __str__(self):
247                 return self.name
248
249         def simplify(self):
250                 return self
251
252         def evaluate(self):
253                 raise KeyError
254
255
256 class Function(Operation):
257
258         REP_FUNCTION = 0
259         REP_PREFIX = 1
260         REP_INFIX = 2
261         REP_POSTFIX = 3
262
263         _op = None
264         _rep = REP_FUNCTION
265         symbol = None
266         argumentCount = 0
267
268         def __init__(self, *args, **kwd):
269                 super(Function, self).__init__()
270                 self._args = args
271                 self._kwd = kwd
272                 self.__simple = self.__simplify()
273                 self.__str = self.pretty_print(args, kwd)
274
275         def serialize(self, renderer):
276                 for item in super(Function, self).serialize(renderer):
277                         yield item
278                 yield self.symbol
279
280         def get_children(self):
281                 return (
282                         arg
283                         for arg in self._args
284                 )
285
286         def __str__(self):
287                 return self.__str
288
289         def simplify(self):
290                 return self.__simple
291
292         def evaluate(self):
293                 selfArgs = [arg.evaluate() for arg in self._args]
294                 return Value(self._op(*selfArgs))
295
296         def __simplify(self):
297                 selfArgs = [arg.simplify() for arg in self._args]
298                 selfKwd = dict(
299                         (name, arg.simplify())
300                         for (name, arg) in self._kwd
301                 )
302
303                 try:
304                         args = [arg.evaluate() for arg in selfArgs]
305                         base = self.base
306                         if base is None:
307                                 bases = [arg.base for arg in selfArgs]
308                                 base = bases[0]
309                         result = self._op(*args)
310
311                         node = Value(result, base)
312                 except KeyError:
313                         node = type(self)(*selfArgs, **selfKwd)
314
315                 return node
316
317         @classmethod
318         def pretty_print(cls, args = None, kwds = None):
319                 if args is None:
320                         args = []
321                 if kwds is None:
322                         kwds = {}
323
324                 if cls._rep == cls.REP_FUNCTION:
325                         positional = (str(arg) for arg in args)
326                         named = (
327                                 "%s=%s" % (str(key), str(value))
328                                 for (key, value) in kwds.iteritems()
329                         )
330                         return "%s(%s)" % (
331                                 cls.symbol,
332                                 ", ".join(itertools.chain(named, positional)),
333                         )
334                 elif cls._rep == cls.REP_PREFIX:
335                         assert len(args) == 1
336                         return "%s %s" % (cls.symbol, args[0])
337                 elif cls._rep == cls.REP_POSTFIX:
338                         assert len(args) == 1
339                         return "%s %s" % (args[0], cls.symbol)
340                 elif cls._rep == cls.REP_INFIX:
341                         assert len(args) == 2
342                         return "(%s %s %s)" % (
343                                 str(args[0]),
344                                 str(cls.symbol),
345                                 str(args[1]),
346                         )
347                 else:
348                         raise AssertionError("Unsupported rep style")
349
350
351 def generate_function(op, rep, style, numArgs):
352
353         class GenFunc(Function):
354
355                 def __init__(self, *args, **kwd):
356                         super(GenFunc, self).__init__(*args, **kwd)
357
358                 _op = op
359                 _rep = style
360                 symbol = rep
361                 argumentCount = numArgs
362
363         GenFunc.__name__ = op.__name__
364         return GenFunc
365
366
367 def change_base(base, rep):
368
369         class GenFunc(Function):
370
371                 def __init__(self, *args, **kwd):
372                         super(GenFunc, self).__init__(*args, **kwd)
373                         self.base = base
374
375                 _op = lambda self, n: n
376                 _rep = Function.REP_FUNCTION
377                 symbol = rep
378                 argumentCount = 1
379
380         GenFunc.__name__ = rep
381         return GenFunc
382
383
384 @overloading.overloaded
385 def render_operation(render_func, operation):
386         return str(operation)
387
388
389 @render_operation.register(overloading.AnyType, Value)
390 def render_value(render_func, operation):
391         return render_func(operation.value, operation.base)
392
393
394 @render_operation.register(overloading.AnyType, Variable)
395 @render_operation.register(overloading.AnyType, Constant)
396 def render_variable(render_func, operation):
397         return operation.name
398
399
400 @render_operation.register(overloading.AnyType, Function)
401 def render_function(render_func, operation):
402         args = [
403                 render_operation(render_func, arg)
404                 for arg in operation.get_children()
405         ]
406         return operation.pretty_print(args)