From 6d8d72cdff49939eeae4e9c3d37c19bbb6b554e4 Mon Sep 17 00:00:00 2001 From: Lafrite Date: Mon, 24 Nov 2014 07:17:51 +0100 Subject: [PATCH 01/14] start particularising operators --- pymath/operator.py | 138 +++++++++++++++++++++++++++++++-------------- 1 file changed, 97 insertions(+), 41 deletions(-) diff --git a/pymath/operator.py b/pymath/operator.py index 223b28e..3a0a54c 100644 --- a/pymath/operator.py +++ b/pymath/operator.py @@ -56,18 +56,24 @@ class Operator(str): return getattr(args[0], self.actions)() elif self.arity == 2: - # C'est moche mais je veux que ça marche... - if str(self) == "/": - # TODO: faudra changer ça c'est pas beau! |ven. nov. 14 16:13:49 CET 2014 - from .fraction import Fraction - ans = [Fraction(args[0], args[1])] - ans += ans[0].simplify() - return ans + if type(args[1]) == int: + return getattr(args[0], self.actions[0])(args[1]) else: - if type(args[1]) == int: - return getattr(args[0], self.actions[0])(args[1]) - else: - return getattr(args[1], self.actions[1])(args[0]) + return getattr(args[1], self.actions[1])(args[0]) + + def _render(self, link, *args): + """Global step for __txt__ and __tex__ + + :param link: the link between operators + :param *args: the operands + :returns: the string with operator and operands + + """ + replacement = {"op"+str(i+1): ' '.join(self.add_parenthesis(op)) for (i,op) in enumerate(args)} + + ans = link.format(**replacement) + ans = save_mainOp(ans, self) + return ans def __txt__(self, *args): """Txt rendering for the operator @@ -95,11 +101,7 @@ class Operator(str): >>> sub1.__txt__(f) '- ( 2 + 3 )' """ - replacement = {"op"+str(i+1): ' '.join(self.add_parenthesis(op)) for (i,op) in enumerate(args)} - - ans = self._txt.format(**replacement) - ans = save_mainOp(ans, self) - return ans + return self._render(self._txt, *args) def __tex__(self, *args): """Tex rendering for the operator @@ -127,11 +129,7 @@ class Operator(str): >>> sub1.__tex__(f) '- ( 2 + 3 )' """ - replacement = {"op"+str(i+1): ' '.join(self.add_parenthesis(op)) for (i,op) in enumerate(args)} - - ans = self._tex.format(**replacement) - ans = save_mainOp(ans, self) - return ans + return self._render(self._tex, *args) def __p2i__(self, *args): """Fix list transformation for the operator @@ -139,6 +137,7 @@ class Operator(str): :*args: Operands for this operation :returns: list with the operator surrounded by operands + # TODO: order doctest |lun. nov. 24 07:17:29 CET 2014 >>> mul = Operator("*", 2) >>> add = Operator("+", 2) >>> sub1 = Operator("-", 1) @@ -176,6 +175,64 @@ class Operator(str): if op.mainOp.priority < self.priority: op = flatten_list(["("] + [op] + [")"]) except AttributeError: + # op has not the attribute priority + try: + if int(op) < 0: + op = ['(', op, ')'] + except ValueError: + pass + return flatten_list([op]) + +class Mul(Operator): + def __new__(cls, visibility = 1): + op = Operator.__new__(cls, "*") + op.visibility = visibility + return op + + def _render(self, link, *args): + replacement = {"op"+str(i+1): ' '.join(self.add_parenthesis(op)) for (i,op) in enumerate(args)} + + if not self.visibility or args[1][0] == "(" or \ + (type(args[1][0]) == str and args[1][0].isalpha()): + ans = "{op1} {op2}".format(**replacement) + ans = save_mainOp(ans, self) + return ans + else: + ans = link.format(**replacement) + ans = save_mainOp(ans, self) + return ans + +class Div(Operator): + def __new__(cls, visibility = 1): + op = Operator.__new__(cls, "/") + return op + + def __call__(self, op1, op2): + if op2 == 1: + return op1 + else: + return Fraction(op1,op2) + + def __tex__(self, *args): + # Pas besoin de parenthèses en plus pour \frac + replacement = {"op"+str(i+1): op for (i,op) in enumerate(args)} + + ans = self._tex.format(**replacement) + ans = save_mainOp(ans, self) + return ans + +class Sub1(Operator): + def __new__(cls,): + op = Operator.__new__(cls, "-", 1) + return op + + def add_parenthesis(self, op): + """ Add parenthesis if necessary """ + try: + if op.mainOp.priority <= self.priority: + op = flatten_list(["("] + [op] + [")"]) + except AttributeError: + # op has not the attribute priority try: if int(op) < 0: op = ['(', op, ')'] @@ -184,15 +241,19 @@ class Operator(str): return flatten_list([op]) + class op(object): """ List of admited operations """ # TODO: On pourrait peut être le faire plus proprement avec des décorateurs? |mar. nov. 11 20:24:54 CET 2014 add = Operator("+") sub = Operator("-") - mul = Operator("*") - div = Operator("/") + #mul = Mul("*") + #div = Div("/") + mul = Mul() + div = Div() pw = Operator("^") - sub1 = Operator("-", 1) + #sub1 = Operator("-", 1) + sub1 = Sub1() par = Operator("(") def save_mainOp(obj, mainOp): @@ -212,26 +273,21 @@ def save_mainOp(obj, mainOp): return Fake(obj) if __name__ == '__main__': - op = Operator("+", 2) - print(op.__txt__('1','2')) - mul = Operator("*", 2) - add = Operator("+", 2) - sub1 = Operator("-", 1) - div = Operator("/", 1) - print(mul.__txt__('1','2')) - print(add.__txt__('1','2')) - f = save_mainOp('2 + 3',add) - print(mul.__txt__(f, '4')) - f = save_mainOp('-3',sub1) - print(sub1.__txt__(f)) - print(sub1.__txt__('-3')) - f = save_mainOp('2 + 3',add) - print(sub1.__txt__(f)) + print(op.mul.__txt__('1','2')) + print(op.sub.__txt__('1','2')) + print(op.add.__txt__('1','2')) + f = save_mainOp('2 + 3',op.add) + print(op.mul.__txt__(f, '4')) + f = save_mainOp('-3',op.sub1) + print(op.sub1.__txt__(f)) + print(op.sub1.__txt__('-3')) + f = save_mainOp('2 + 3',op.add) + print(op.sub1.__txt__(f)) from .fraction import Fraction f = Fraction(1, 2) - print(add.__txt__(f.__txt__(),'2')) - print(add.__tex__(f.__tex__(),'2')) + print(op.add.__txt__(f.__txt__(),'2')) + print(op.add.__tex__(f.__tex__(),'2')) import doctest From 8485890fdf3c76fc28db352fa36862218deee906 Mon Sep 17 00:00:00 2001 From: lafrite Date: Tue, 25 Nov 2014 10:46:00 +0100 Subject: [PATCH 02/14] test with no * between number and letter - doctest in operator --- pymath/operator.py | 63 ++++++++++++++++++-------------------------- test/test_polynom.py | 8 +++--- 2 files changed, 30 insertions(+), 41 deletions(-) diff --git a/pymath/operator.py b/pymath/operator.py index 3a0a54c..66d2aa8 100644 --- a/pymath/operator.py +++ b/pymath/operator.py @@ -81,24 +81,20 @@ class Operator(str): :*args: Operands for this operation :returns: String with operator and his operands - >>> mul = Operator("*", 2) - >>> add = Operator("+", 2) - >>> sub1 = Operator("-", 1) - >>> div = Operator("/", 1) - >>> mul.__txt__('1','2') + >>> op.mul.__txt__('1','2') '1 * 2' - >>> add.__txt__('1','2') + >>> op.add.__txt__('1','2') '1 + 2' - >>> f = save_mainOp('2 + 3',add) - >>> mul.__txt__(f, '4') + >>> f = save_mainOp('2 + 3',op.add) + >>> op.mul.__txt__(f, '4') '( 2 + 3 ) * 4' - >>> f = save_mainOp('-3',sub1) - >>> sub1.__txt__(f) + >>> f = save_mainOp('-3',op.sub1) + >>> op.sub1.__txt__(f) '- ( -3 )' - >>> sub1.__txt__('-3') + >>> op.sub1.__txt__('-3') '- ( -3 )' - >>> f = save_mainOp('2 + 3',add) - >>> sub1.__txt__(f) + >>> f = save_mainOp('2 + 3',op.add) + >>> op.sub1.__txt__(f) '- ( 2 + 3 )' """ return self._render(self._txt, *args) @@ -109,24 +105,20 @@ class Operator(str): :*args: Operands for this operation :returns: String with operator and his operands - >>> mul = Operator("*", 2) - >>> add = Operator("+", 2) - >>> sub1 = Operator("-", 1) - >>> div = Operator("/", 1) - >>> mul.__tex__('1','2') + >>> op.mul.__tex__('1','2') '1 \\\\times 2' - >>> add.__tex__('1','2') + >>> op.add.__tex__('1','2') '1 + 2' - >>> f = save_mainOp('2 + 3',add) - >>> mul.__tex__(f, '4') + >>> f = save_mainOp('2 + 3',op.add) + >>> op.mul.__tex__(f, '4') '( 2 + 3 ) \\\\times 4' - >>> f = save_mainOp('-3',sub1) - >>> sub1.__tex__(f) + >>> f = save_mainOp('-3',op.sub1) + >>> op.sub1.__tex__(f) '- ( -3 )' - >>> sub1.__tex__('-3') + >>> op.sub1.__tex__('-3') '- ( -3 )' - >>> f = save_mainOp('2 + 3',add) - >>> sub1.__tex__(f) + >>> f = save_mainOp('2 + 3',op.add) + >>> op.sub1.__tex__(f) '- ( 2 + 3 )' """ return self._render(self._tex, *args) @@ -138,21 +130,18 @@ class Operator(str): :returns: list with the operator surrounded by operands # TODO: order doctest |lun. nov. 24 07:17:29 CET 2014 - >>> mul = Operator("*", 2) - >>> add = Operator("+", 2) - >>> sub1 = Operator("-", 1) - >>> mul.__p2i__(1,2) + >>> op.mul.__p2i__(1,2) [1, '*', 2] - >>> f = save_mainOp([2, add, 3],add) - >>> mul.__p2i__(f, 4) + >>> f = save_mainOp([2, op.add, 3],op.add) + >>> op.mul.__p2i__(f, 4) ['(', 2, '+', 3, ')', '*', 4] - >>> f = save_mainOp([sub1, 3],sub1) - >>> sub1.__p2i__(f) + >>> f = save_mainOp([op.sub1, 3],op.sub1) + >>> op.sub1.__p2i__(f) ['-', '(', '-', 3, ')'] - >>> sub1.__p2i__(-3) + >>> op.sub1.__p2i__(-3) ['-', '(', -3, ')'] - >>> f = save_mainOp([2, add, 3],add) - >>> sub1.__p2i__(f) + >>> f = save_mainOp([2, op.add, 3],op.add) + >>> op.sub1.__p2i__(f) ['-', '(', 2, '+', 3, ')'] """ # TODO: Attention à gestion des fractions qui se comportent chelou avec les parenthèses |dim. nov. 9 09:21:52 CET 2014 diff --git a/test/test_polynom.py b/test/test_polynom.py index 17edd7b..de491fb 100644 --- a/test/test_polynom.py +++ b/test/test_polynom.py @@ -98,25 +98,25 @@ class TestPolynom(unittest.TestCase): def test_reduce(self): p = Polynom([1, [2, 3], 4]) - self.assertEqual(str(p.reduce()[-1]),'4 * x ^ 2 + 5 * x + 1') + self.assertEqual(str(p.reduce()[-1]),'4 x ^ 2 + 5 x + 1') def test_add_int(self): p = Polynom([1, 2, 3]) q = (p + 2)[-1] - self.assertEqual(str(q), '3 * x ^ 2 + 2 * x + 3') + self.assertEqual(str(q), '3 x ^ 2 + 2 x + 3') def test_add_frac(self): p = Polynom([1, 2, 3]) f = Fraction(1, 2) q = (p + f)[-1] #ans = repr(Polynom([Expression(Fraction(3, 2)), Expression(2), Expression(3)])) - self.assertEqual(str(q),'3 * x ^ 2 + 2 * x + 3 / 2') + self.assertEqual(str(q),'3 x ^ 2 + 2 x + 3 / 2') def test_add_poly(self): p = Polynom([1, 0, 3]) q = Polynom([0, 2, 3]) r = (p + q)[-1] - self.assertEqual(str(r), '6 * x ^ 2 + 2 * x + 1') + self.assertEqual(str(r), '6 x ^ 2 + 2 x + 1') def test_radd_int(self): pass From 24fb46e3d0eb677f6e017fe8a1b6637a3ffc81ee Mon Sep 17 00:00:00 2001 From: lafrite Date: Tue, 25 Nov 2014 10:48:24 +0100 Subject: [PATCH 03/14] can't put spaces in {} in random_expression --- test/test_random_expression.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/test_random_expression.py b/test/test_random_expression.py index bed6114..8dc11e1 100644 --- a/test/test_random_expression.py +++ b/test/test_random_expression.py @@ -22,15 +22,15 @@ class TestRandomExpression(unittest.TestCase): self.assertEqual(set(rdExp._gene_2replaced.keys()), {'a'}) def test_only_form_calc(self): - form = "{a + b} + 2" + form = "{a+b} + 2" rdExp = RdExpression(form) self.assertEqual(rdExp._letters, {'a', 'b'}) - self.assertEqual(rdExp._2replaced, {'a + b'}) + self.assertEqual(rdExp._2replaced, {'a+b'}) rdExp() self.assertEqual(set(rdExp._gene_varia.keys()), {'a', 'b'}) - self.assertEqual(set(rdExp._gene_2replaced.keys()), {'a + b'}) + self.assertEqual(set(rdExp._gene_2replaced.keys()), {'a+b'}) def test_only_form_cond(self): form = "{a} + 2" @@ -78,15 +78,15 @@ class TestRandomExpression(unittest.TestCase): def test_only_form_calc_cond_calc(self): form = "{a*3} * {b}" - cond = ["{a + b} == 3"] + cond = ["{a+b} == 3"] rdExp = RdExpression(form, cond) self.assertEqual(rdExp._letters, {'a', 'b'}) - self.assertEqual(rdExp._2replaced, {'b', 'a*3', 'a + b'}) + self.assertEqual(rdExp._2replaced, {'b', 'a*3', 'a+b'}) rdExp() self.assertEqual(set(rdExp._gene_varia.keys()), {'a', 'b'}) - self.assertEqual(set(rdExp._gene_2replaced.keys()), {'b', 'a*3', 'a + b'}) + self.assertEqual(set(rdExp._gene_2replaced.keys()), {'b', 'a*3', 'a+b'}) self.assertEqual((rdExp._gene_varia['a'] + rdExp._gene_varia['b']), 3) From cccfb9e90be5f9f05c21e7e6839b4039f22c68a3 Mon Sep 17 00:00:00 2001 From: Lafrite Date: Tue, 2 Dec 2014 10:05:02 +0100 Subject: [PATCH 04/14] does not work.. --- pymath/operator.py | 325 +++++++++++++++++++++++++++++---------------- 1 file changed, 211 insertions(+), 114 deletions(-) diff --git a/pymath/operator.py b/pymath/operator.py index 66d2aa8..ead5ee7 100644 --- a/pymath/operator.py +++ b/pymath/operator.py @@ -4,51 +4,26 @@ from .generic import flatten_list, isNumber -class Operator(str): +class Operator(object): """The operator class, is a string (representation of the operator) with its arity""" - PRIORITY = {"^": [0, 5], "/": [0, 4], "*" : [0,3], ":": [0,3], "+": [0,1], "-":[2,1], "(":[0,0]} - OPERATIONS = { \ - "+": ["", ("__add__","__radd__")],\ - "-": ["__neg__", ("__sub__", "__rsub__")], \ - "*": ["", ("__mul__", "__rmul__")], \ - "/": ["", ("__div__","__rdiv__")], \ - "^": ["", ("__pow__", "")], \ - "(": ["",""],\ - } - TXT = { \ - "+": ["", "{op1} + {op2}"] ,\ - "-": ["- {op1}", "{op1} - {op2}"] ,\ - "*": ["", "{op1} * {op2}"] ,\ - "/": ["", "{op1} / {op2}"] ,\ - "^": ["", "{op1} ^ {op2}"] ,\ - "(": ["",""],\ - } - TEX = { \ - "+": ["", "{op1} + {op2}"] ,\ - "-": ["- {op1}", "{op1} - {op2}"] ,\ - "*": ["", "{op1} \\times {op2}"] ,\ - "/": ["", "\\frac{{ {op1} }}{{ {op2} }}"] ,\ - "^": ["", "{op1}^{{ {op2} }}"] ,\ - "(": ["",""],\ - } + def __init__(self, operator = "", priority = 0, actions = ("",""), txt = "", tex = "", arity = 2): + """ Create an Operator """ + self.name = operator + self.arity = arity + # TODO: Add self.visibility |sam. nov. 8 17:00:08 CET 2014 - def __new__(cls, operator, arity = 2): - op = str.__new__(cls, operator) - op.arity = arity + self.priority = priority + self.actions = actions + self.txt = txt + self.tex = tex - # TODO: Add op.visibility |sam. nov. 8 17:00:08 CET 2014 + self.isselferator = 1 - op.priority = cls.PRIORITY[operator][arity - 1] - op.actions = cls.OPERATIONS[operator][arity-1] - op._txt = cls.TXT[operator][arity-1] - op._tex = cls.TEX[operator][arity-1] - - op.isOperator = 1 - - return op + def __str__(self): + return self.name def __call__(self, *args): """ Calling this operator performs the rigth calculus """ @@ -97,7 +72,7 @@ class Operator(str): >>> op.sub1.__txt__(f) '- ( 2 + 3 )' """ - return self._render(self._txt, *args) + return self._render(self.txt, *args) def __tex__(self, *args): """Tex rendering for the operator @@ -121,7 +96,7 @@ class Operator(str): >>> op.sub1.__tex__(f) '- ( 2 + 3 )' """ - return self._render(self._tex, *args) + return self._render(self.tex, *args) def __p2i__(self, *args): """Fix list transformation for the operator @@ -172,79 +147,6 @@ class Operator(str): pass return flatten_list([op]) -class Mul(Operator): - def __new__(cls, visibility = 1): - op = Operator.__new__(cls, "*") - op.visibility = visibility - return op - - def _render(self, link, *args): - replacement = {"op"+str(i+1): ' '.join(self.add_parenthesis(op)) for (i,op) in enumerate(args)} - - if not self.visibility or args[1][0] == "(" or \ - (type(args[1][0]) == str and args[1][0].isalpha()): - ans = "{op1} {op2}".format(**replacement) - ans = save_mainOp(ans, self) - return ans - else: - ans = link.format(**replacement) - ans = save_mainOp(ans, self) - return ans - -class Div(Operator): - def __new__(cls, visibility = 1): - op = Operator.__new__(cls, "/") - return op - - def __call__(self, op1, op2): - if op2 == 1: - return op1 - else: - return Fraction(op1,op2) - - def __tex__(self, *args): - # Pas besoin de parenthèses en plus pour \frac - replacement = {"op"+str(i+1): op for (i,op) in enumerate(args)} - - ans = self._tex.format(**replacement) - ans = save_mainOp(ans, self) - return ans - -class Sub1(Operator): - def __new__(cls,): - op = Operator.__new__(cls, "-", 1) - return op - - def add_parenthesis(self, op): - """ Add parenthesis if necessary """ - try: - if op.mainOp.priority <= self.priority: - op = flatten_list(["("] + [op] + [")"]) - except AttributeError: - # op has not the attribute priority - try: - if int(op) < 0: - op = ['(', op, ')'] - except ValueError: - pass - return flatten_list([op]) - - - -class op(object): - """ List of admited operations """ - # TODO: On pourrait peut être le faire plus proprement avec des décorateurs? |mar. nov. 11 20:24:54 CET 2014 - add = Operator("+") - sub = Operator("-") - #mul = Mul("*") - #div = Div("/") - mul = Mul() - div = Div() - pw = Operator("^") - #sub1 = Operator("-", 1) - sub1 = Sub1() - par = Operator("(") - def save_mainOp(obj, mainOp): """Create a temporary class build over built-in type to stock the main operation of a calculus @@ -261,10 +163,205 @@ def save_mainOp(obj, mainOp): return Fake(obj) +def operatorize(fun): + """Transform the answer of the function into an operator + + The returned value of the function has to be a dictionnary with those keys + * "operator": the name (Needed!) + * "priority": the priority level + * "action": mathematic action of the operator (list of 1 element if the arity is 1, 2 elements if arity is 2) + * "txt": string ready to be formated in txt for with {op1} and/or {op2} + * "tex": string ready to be formated in tex for with {op1} and/or {op2} + * "arity": arity ie number of operands needed + * "__call__": action to perform when call the operator + * "_render": action use in __txt__ and __tex__ + * "__txt__": txt rendering + * "__tex__": tex rendering + * "add_parenthesis": mechanism to add parenthesis + """ + def mod_fun(self, *args): + ans = fun(self, *args) + + op = Operator(ans["operator"]) + print("type(op)", type(op)) + for (attr, value) in ans.items(): + if hasattr(value, '__call__'): + print("value :", type(value)) + print("value :", str(value)) + callback = lambda *args, **kwrds: value(op, *args, **kwrds) + setattr(op, attr, callback) + else: + setattr(op, attr, value) + return op + return mod_fun + +class ClassProperty(object): + + def __init__(self, fget): + self.fget = fget + + def __get__(self, owner_self, owner_cls): + return self.fget(owner_cls) + +class op(object): + """ List of admited operations """ + + @ClassProperty + @operatorize + def add(cls): + """ The operator + """ + caract = { + "operator" : "+", \ + "priority" : 1, \ + "arity" : 2, \ + "action" : ("__add__","__radd__"), \ + "txt" : "{op1} + {op2}",\ + "tex" : "{op1} + {op2}",\ + } + + return caract + + @ClassProperty + @operatorize + def sub(self): + """ The operator - """ + caract = { + "operator" : "-", \ + "priority" : 1, \ + "arity" : 2, \ + "action" : ("__sub__","__rsub__"), \ + "txt" : "{op1} - {op2}",\ + "tex" : "{op1} - {op2}",\ + } + + return caract + + @ClassProperty + @operatorize + def sub1(self): + """ The operator - """ + def add_parenthesis(self, op): + """ Add parenthesis if necessary """ + try: + if op.mainOp.priority <= self.priority: + op = flatten_list(["("] + [op] + [")"]) + except AttributeError: + # op has not the attribute priority + try: + if int(op) < 0: + op = ['(', op, ')'] + except ValueError: + pass + return flatten_list([op]) + + caract = { + "operator" : "-", \ + "priority" : 2, \ + "arity" : 2, \ + "action" : "__neg__",\ + "txt" : "- {op1}",\ + "tex" : "- {op1}",\ + "add_parenthesis": add_parenthesis,\ + } + + return caract + + @ClassProperty + @operatorize + def mul(self): + """ The operator * """ + # * can not be display in some cases + def _render(self, link, *args): + + #print("self->", str(self)) + #print("link ->", str(link)) + #print("*args ->", str(args)) + + replacement = {"op"+str(i+1): ' '.join(self.add_parenthesis(op)) for (i,op) in enumerate(args)} + + if not self.visibility or args[1][0] == "(" or \ + (type(args[1][0]) == str and args[1][0].isalpha()): + ans = "{op1} {op2}".format(**replacement) + ans = save_mainOp(ans, self) + return ans + else: + ans = link.format(**replacement) + ans = save_mainOp(ans, self) + return ans + + caract = { + "operator" : "*", \ + "priority" : 4, \ + "arity" : 2, \ + "action" : ("__mul__","__rmul__"), \ + "txt" : "{op1} * {op2}",\ + "tex" : "{op1} \\times {op2}",\ + "visibility": 1,\ + "_render": _render + } + + return caract + + @ClassProperty + @operatorize + def div(self): + """ The operator / """ + def __call__(self, op1, op2): + if op2 == 1: + return op1 + else: + return Fraction(op1,op2) + + def __tex__(self, *args): + # Pas besoin de parenthèses en plus pour \frac + replacement = {"op"+str(i+1): op for (i,op) in enumerate(args)} + + ans = self._tex.format(**replacement) + ans = save_mainOp(ans, self) + return ans + + caract = { + "operator" : "/", \ + "priority" : 4, \ + "arity" : 2, \ + "txt" : "{op1} /^ {op2}",\ + "tex" : "\\frac{{ {op1} }}{{ {op2} }}",\ + "__call__": __call__,\ + "__tex__":__tex__,\ + } + + return caract + + @ClassProperty + @operatorize + def pw(self): + """ The operator ^ """ + caract = { + "operator" : "^", \ + "priority" : 5, \ + "arity" : 2, \ + "action" : ("__pow__",""), \ + "txt" : "{op1} ^ {op2}",\ + "tex" : "{op1}^{{ {op2} }}",\ + } + + return caract + + @ClassProperty + @operatorize + def par(self): + """ The operator ( """ + caract = { + "operator" : "(", \ + "priority" : 0, \ + "arity" : 0, \ + } + return caract + if __name__ == '__main__': + print(op.add.__txt__('1','2')) print(op.mul.__txt__('1','2')) print(op.sub.__txt__('1','2')) - print(op.add.__txt__('1','2')) f = save_mainOp('2 + 3',op.add) print(op.mul.__txt__(f, '4')) f = save_mainOp('-3',op.sub1) From cecf06e08203fdcaf5bafae4cef0cf346d335a25 Mon Sep 17 00:00:00 2001 From: lafrite Date: Tue, 2 Dec 2014 11:06:29 +0100 Subject: [PATCH 05/14] inside doctest works not unittest --- pymath/operator.py | 42 ++++++++++++++++++------------------------ 1 file changed, 18 insertions(+), 24 deletions(-) diff --git a/pymath/operator.py b/pymath/operator.py index ead5ee7..3feb793 100644 --- a/pymath/operator.py +++ b/pymath/operator.py @@ -3,27 +3,26 @@ from .generic import flatten_list, isNumber +import types -class Operator(object): +class Operator(str): """The operator class, is a string (representation of the operator) with its arity""" - def __init__(self, operator = "", priority = 0, actions = ("",""), txt = "", tex = "", arity = 2): + def __new__(cls, operator = "", priority = 0, actions = ("",""), txt = "", tex = "", arity = 2): """ Create an Operator """ - self.name = operator - self.arity = arity + #def __new__(cls, operator, arity = 2): + op = str.__new__(cls, operator) + op.name = operator + op.arity = arity + op.priority = priority + op.actions = actions + op.txt = txt + op.tex = tex + op.isOperator = 1 # TODO: Add self.visibility |sam. nov. 8 17:00:08 CET 2014 - - self.priority = priority - self.actions = actions - self.txt = txt - self.tex = tex - - self.isselferator = 1 - - def __str__(self): - return self.name + return op def __call__(self, *args): """ Calling this operator performs the rigth calculus """ @@ -183,15 +182,14 @@ def operatorize(fun): ans = fun(self, *args) op = Operator(ans["operator"]) - print("type(op)", type(op)) for (attr, value) in ans.items(): if hasattr(value, '__call__'): - print("value :", type(value)) - print("value :", str(value)) - callback = lambda *args, **kwrds: value(op, *args, **kwrds) - setattr(op, attr, callback) + #callback = lambda *args, **kwrds: value(op, *args, **kwrds) + #setattr(op, attr, callback) + setattr(op, attr, types.MethodType(value, op)) else: setattr(op, attr, value) + return op return mod_fun @@ -257,7 +255,7 @@ class op(object): caract = { "operator" : "-", \ "priority" : 2, \ - "arity" : 2, \ + "arity" : 1, \ "action" : "__neg__",\ "txt" : "- {op1}",\ "tex" : "- {op1}",\ @@ -273,10 +271,6 @@ class op(object): # * can not be display in some cases def _render(self, link, *args): - #print("self->", str(self)) - #print("link ->", str(link)) - #print("*args ->", str(args)) - replacement = {"op"+str(i+1): ' '.join(self.add_parenthesis(op)) for (i,op) in enumerate(args)} if not self.visibility or args[1][0] == "(" or \ From e1da8d24539e4660268bc45437b4bf4ae419f901 Mon Sep 17 00:00:00 2001 From: lafrite Date: Tue, 2 Dec 2014 11:32:59 +0100 Subject: [PATCH 06/14] operator selector --- pymath/operator.py | 36 ++++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/pymath/operator.py b/pymath/operator.py index 3feb793..4b27514 100644 --- a/pymath/operator.py +++ b/pymath/operator.py @@ -9,11 +9,12 @@ class Operator(str): """The operator class, is a string (representation of the operator) with its arity""" - def __new__(cls, operator = "", priority = 0, actions = ("",""), txt = "", tex = "", arity = 2): + def __new__(cls, operator = "", name = "", priority = 0, actions = ("",""), txt = "", tex = "", arity = 2): """ Create an Operator """ #def __new__(cls, operator, arity = 2): op = str.__new__(cls, operator) - op.name = operator + op.operator = operator + op.name = name op.arity = arity op.priority = priority op.actions = actions @@ -184,12 +185,10 @@ def operatorize(fun): op = Operator(ans["operator"]) for (attr, value) in ans.items(): if hasattr(value, '__call__'): - #callback = lambda *args, **kwrds: value(op, *args, **kwrds) - #setattr(op, attr, callback) setattr(op, attr, types.MethodType(value, op)) else: setattr(op, attr, value) - + return op return mod_fun @@ -204,12 +203,31 @@ class ClassProperty(object): class op(object): """ List of admited operations """ + _operators = {("+",2): "add",\ + ("-", 2): "sub",\ + ("-", 1): "sub1",\ + ("*", 2): "mul",\ + ("/", 2): "div",\ + ("^", 2): "pw",\ + (")", 2): "par",\ + } + + @classmethod + def get_op(cls, op, arity = 2): + """Return the corresponding operator + + :op: symbole of the op + :arity: the arity + """ + return getattr(cls, cls._operators[(op, arity)]) + @ClassProperty @operatorize def add(cls): """ The operator + """ caract = { "operator" : "+", \ + "name" : "add",\ "priority" : 1, \ "arity" : 2, \ "action" : ("__add__","__radd__"), \ @@ -225,6 +243,7 @@ class op(object): """ The operator - """ caract = { "operator" : "-", \ + "name" : "sub",\ "priority" : 1, \ "arity" : 2, \ "action" : ("__sub__","__rsub__"), \ @@ -254,6 +273,7 @@ class op(object): caract = { "operator" : "-", \ + "name" : "sub1",\ "priority" : 2, \ "arity" : 1, \ "action" : "__neg__",\ @@ -285,6 +305,7 @@ class op(object): caract = { "operator" : "*", \ + "name" : "mul",\ "priority" : 4, \ "arity" : 2, \ "action" : ("__mul__","__rmul__"), \ @@ -316,6 +337,7 @@ class op(object): caract = { "operator" : "/", \ + "name" : "div",\ "priority" : 4, \ "arity" : 2, \ "txt" : "{op1} /^ {op2}",\ @@ -332,6 +354,7 @@ class op(object): """ The operator ^ """ caract = { "operator" : "^", \ + "name" : "pw",\ "priority" : 5, \ "arity" : 2, \ "action" : ("__pow__",""), \ @@ -347,6 +370,7 @@ class op(object): """ The operator ( """ caract = { "operator" : "(", \ + "name" : "par",\ "priority" : 0, \ "arity" : 0, \ } @@ -368,7 +392,7 @@ if __name__ == '__main__': f = Fraction(1, 2) print(op.add.__txt__(f.__txt__(),'2')) print(op.add.__tex__(f.__tex__(),'2')) - + import doctest doctest.testmod() From 543af699792a1ef2eb77e064257067327f8a5bca Mon Sep 17 00:00:00 2001 From: lafrite Date: Tue, 2 Dec 2014 12:37:23 +0100 Subject: [PATCH 07/14] Doctest and bugs in str2tokens --- pymath/operator.py | 11 ++++++++- pymath/str2tokens.py | 53 +++++++++++++++++++------------------------- 2 files changed, 33 insertions(+), 31 deletions(-) diff --git a/pymath/operator.py b/pymath/operator.py index 4b27514..e271cfd 100644 --- a/pymath/operator.py +++ b/pymath/operator.py @@ -209,7 +209,7 @@ class op(object): ("*", 2): "mul",\ ("/", 2): "div",\ ("^", 2): "pw",\ - (")", 2): "par",\ + ("(", 2): "par",\ } @classmethod @@ -221,6 +221,12 @@ class op(object): """ return getattr(cls, cls._operators[(op, arity)]) + @classmethod + def can_be_operator(cls, symbole): + """ Tell if the symbole can be an operator """ + return symbole in [i[0] for i in cls._operators] + + @ClassProperty @operatorize def add(cls): @@ -393,6 +399,9 @@ if __name__ == '__main__': print(op.add.__txt__(f.__txt__(),'2')) print(op.add.__tex__(f.__tex__(),'2')) + print("\t op.can_be_operator('+') :" + str(op.can_be_operator('+'))) + print("\t op.can_be_operator('t') :" + str(op.can_be_operator('t'))) + import doctest doctest.testmod() diff --git a/pymath/str2tokens.py b/pymath/str2tokens.py index 69042b7..124b698 100644 --- a/pymath/str2tokens.py +++ b/pymath/str2tokens.py @@ -1,8 +1,8 @@ #!/usr/bin/env python # encoding: utf-8 -from .operator import Operator from .generic import Stack, isOperator, isNumber +from pymath.operator import op def str2tokens(exp): """ Parse the string into tokens then turn it into postfix form @@ -51,7 +51,7 @@ def str2in_tokens(exp): tokens.append(int(character)) elif character in "+-*/:^": - tokens.append(Operator(character)) + tokens.append(character) elif character == ")": tokens.append(character) @@ -60,9 +60,8 @@ def str2in_tokens(exp): # If "3(", ")(" if isNumber(tokens[-1]) \ or tokens[-1] == ")" : - #tokens.append(Operator("*")) - tokens.append(Operator("*")) - tokens.append(Operator(character)) + tokens.append("*") + tokens.append(character) elif character == ".": raise ValueError("No float number please") @@ -80,15 +79,11 @@ def in2post_fix(infix_tokens): @param infix_tokens: the infix list of tokens to transform into postfix form. @return: the corresponding postfix list of tokens. - >>> a, s, m, d, p = Operator("+"), Operator("-"), Operator("*"), Operator("/"), Operator("^") - >>> s1 = Operator("-", 1) - >>> par = Operator("(") - - >>> in2post_fix([par, 2, a, 5, s, 1, ')', d, par, 3, m, 4, ')']) + >>> in2post_fix([op.par, 2, op.add, 5, op.sub, 1, ')', op.div, op.par, 3, op.mul, 4, ')']) [2, 5, '+', 1, '-', 3, 4, '*', '/'] - >>> in2post_fix([s1, par, s1, 2, ')']) + >>> in2post_fix([op.sub1, op.par, op.sub1, 2, ')']) [2, '-', '-'] - >>> in2post_fix([s1, par, s1, 2, a, 3, m, 4, ')']) + >>> in2post_fix([op.sub1, op.par, op.sub1, 2, op.add, 3, op.mul, 4, ')']) [2, '-', 3, 4, '*', '+', '-'] """ # Stack where operator will be stocked @@ -101,13 +96,13 @@ def in2post_fix(infix_tokens): for (pos_token,token) in enumerate(infix_tokens): - ## Pour voir ce qu'il se passe dans cette procédure + # Pour voir ce qu'il se passe dans cette procédure #print(str(postfix_tokens), " | ", str(opStack), " | ", str(infix_tokens[(pos_token+1):]), " | ", str(arity_Stack)) if token == ")": - op = opStack.pop() - while op != "(": - postfix_tokens.append(op) - op = opStack.pop() + next_op = opStack.pop() + while next_op != "(": + postfix_tokens.append(next_op) + next_op = opStack.pop() # Go back to old arity arity_Stack.pop() @@ -115,23 +110,22 @@ def in2post_fix(infix_tokens): arity = arity_Stack.pop() arity_Stack.push(arity + 1) - elif isOperator(token): + elif op.can_be_operator(token): if token == "(": - opStack.push(token) + opStack.push(op.get_op(token)) # Set next arity counter arity_Stack.push(0) else: - while (not opStack.isEmpty()) and opStack.peek().priority >= token.priority: - op = opStack.pop() - postfix_tokens.append(op) - arity = arity_Stack.pop() - - token.arity = arity + 1 - opStack.push(token) - # print("--", token, " -> ", str(arity + 1)) + token_op = op.get_op(token, arity + 1) # Reset arity to 0 in case there is other operators (the real operation would be "-op.arity + 1") arity_Stack.push(0) + while (not opStack.isEmpty()) and opStack.peek().priority >= token_op.priority: + next_op = opStack.pop() + postfix_tokens.append(next_op) + + opStack.push(token_op) + #print("--", token, " -> ", str(arity + 1)) else: postfix_tokens.append(token) arity = arity_Stack.pop() @@ -141,8 +135,8 @@ def in2post_fix(infix_tokens): #print(str(postfix_tokens), " | ", str(opStack), " | ", str(infix_tokens[(pos_token+1):]), " | ", str(arity_Stack)) while not opStack.isEmpty(): - op = opStack.pop() - postfix_tokens.append(op) + next_op = opStack.pop() + postfix_tokens.append(next_op) ## Pour voir ce qu'il se passe dans cette procédure #print(str(postfix_tokens), " | ", str(opStack), " | ", str(infix_tokens[(pos_token+1):]), " | ", str(arity_Stack)) @@ -161,7 +155,6 @@ if __name__ == '__main__': # #print(in2post_fix(in_tokens)) - from .operator import op print(in2post_fix([op.par, 2, op.add, 5, op.sub, 1, ')', op.div, op.par, 3, op.mul, 4, ')'])) print(in2post_fix([op.sub1, op.par, op.sub1, 2, ')'])) print(in2post_fix([op.sub1, op.par, op.sub1, 2, op.add, 3, op.mul, 4, ')'])) From ff605c87cb4728cbd817f595dc18b4b470f02b9e Mon Sep 17 00:00:00 2001 From: lafrite Date: Tue, 2 Dec 2014 14:31:27 +0100 Subject: [PATCH 08/14] okfor doctest --- pymath/expression.py | 2 +- pymath/operator.py | 43 +++++++++++++++++++++++++++---------------- 2 files changed, 28 insertions(+), 17 deletions(-) diff --git a/pymath/expression.py b/pymath/expression.py index eae21fe..1a5dc10 100644 --- a/pymath/expression.py +++ b/pymath/expression.py @@ -98,7 +98,7 @@ class Expression(object): op1 = tokenList[0] op2 = tokenList[1] operator = tokenList[2] - + res = operator(op1, op2) tmpTokenList.append(res) diff --git a/pymath/operator.py b/pymath/operator.py index e271cfd..5d71603 100644 --- a/pymath/operator.py +++ b/pymath/operator.py @@ -27,6 +27,11 @@ class Operator(str): def __call__(self, *args): """ Calling this operator performs the rigth calculus """ + return self._call(*args) + + + def _call(self, *args): + """Trick to avoid overloading __call__ """ if self.arity == 1: return getattr(args[0], self.actions)() @@ -169,11 +174,11 @@ def operatorize(fun): The returned value of the function has to be a dictionnary with those keys * "operator": the name (Needed!) * "priority": the priority level - * "action": mathematic action of the operator (list of 1 element if the arity is 1, 2 elements if arity is 2) + * "actions": mathematics actions of the operator (list of 1 element if the arity is 1, 2 elements if arity is 2) * "txt": string ready to be formated in txt for with {op1} and/or {op2} * "tex": string ready to be formated in tex for with {op1} and/or {op2} * "arity": arity ie number of operands needed - * "__call__": action to perform when call the operator + * "_call": action to perform when call the operator * "_render": action use in __txt__ and __tex__ * "__txt__": txt rendering * "__tex__": tex rendering @@ -182,14 +187,14 @@ def operatorize(fun): def mod_fun(self, *args): ans = fun(self, *args) - op = Operator(ans["operator"]) + new_op = Operator(ans["operator"]) for (attr, value) in ans.items(): if hasattr(value, '__call__'): - setattr(op, attr, types.MethodType(value, op)) + setattr(new_op, attr, types.MethodType(value, new_op)) else: - setattr(op, attr, value) + setattr(new_op, attr, value) - return op + return new_op return mod_fun class ClassProperty(object): @@ -236,7 +241,7 @@ class op(object): "name" : "add",\ "priority" : 1, \ "arity" : 2, \ - "action" : ("__add__","__radd__"), \ + "actions" : ("__add__","__radd__"), \ "txt" : "{op1} + {op2}",\ "tex" : "{op1} + {op2}",\ } @@ -252,7 +257,7 @@ class op(object): "name" : "sub",\ "priority" : 1, \ "arity" : 2, \ - "action" : ("__sub__","__rsub__"), \ + "actions" : ("__sub__","__rsub__"), \ "txt" : "{op1} - {op2}",\ "tex" : "{op1} - {op2}",\ } @@ -282,7 +287,7 @@ class op(object): "name" : "sub1",\ "priority" : 2, \ "arity" : 1, \ - "action" : "__neg__",\ + "actions" : "__neg__",\ "txt" : "- {op1}",\ "tex" : "- {op1}",\ "add_parenthesis": add_parenthesis,\ @@ -314,7 +319,7 @@ class op(object): "name" : "mul",\ "priority" : 4, \ "arity" : 2, \ - "action" : ("__mul__","__rmul__"), \ + "actions" : ("__mul__","__rmul__"), \ "txt" : "{op1} * {op2}",\ "tex" : "{op1} \\times {op2}",\ "visibility": 1,\ @@ -327,7 +332,8 @@ class op(object): @operatorize def div(self): """ The operator / """ - def __call__(self, op1, op2): + from .fraction import Fraction + def _call(self, op1, op2): if op2 == 1: return op1 else: @@ -348,7 +354,7 @@ class op(object): "arity" : 2, \ "txt" : "{op1} /^ {op2}",\ "tex" : "\\frac{{ {op1} }}{{ {op2} }}",\ - "__call__": __call__,\ + "_call": _call,\ "__tex__":__tex__,\ } @@ -358,14 +364,19 @@ class op(object): @operatorize def pw(self): """ The operator ^ """ + def _call(self, op1, op2): + """ Calling this operator performs the rigth calculus """ + return getattr(op1, "__pow__")(op2) + caract = { "operator" : "^", \ "name" : "pw",\ "priority" : 5, \ "arity" : 2, \ - "action" : ("__pow__",""), \ + "actions" : ("__pow__",""), \ "txt" : "{op1} ^ {op2}",\ "tex" : "{op1}^{{ {op2} }}",\ + "_call":_call,\ } return caract @@ -383,9 +394,9 @@ class op(object): return caract if __name__ == '__main__': - print(op.add.__txt__('1','2')) - print(op.mul.__txt__('1','2')) - print(op.sub.__txt__('1','2')) + print(op.add.__tex__('1','2')) + print(op.mul.__tex__('1','2')) + print(op.sub.__tex__('1','2')) f = save_mainOp('2 + 3',op.add) print(op.mul.__txt__(f, '4')) f = save_mainOp('-3',op.sub1) From b8ca063368d8a6769cba1a89f34306db82b8f077 Mon Sep 17 00:00:00 2001 From: lafrite Date: Tue, 2 Dec 2014 14:33:45 +0100 Subject: [PATCH 09/14] Yeah! Unittest works! --- test/test_render.py | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/test/test_render.py b/test/test_render.py index aa22126..0dee1dd 100644 --- a/test/test_render.py +++ b/test/test_render.py @@ -6,7 +6,7 @@ import unittest from pymath.render import tex, txt,p2i from pymath.fraction import Fraction -from pymath.operator import Operator +from pymath.operator import op @@ -23,29 +23,29 @@ class TestTexRender(unittest.TestCase): self.assertEqual(tex([Fraction(1,2)]), "\\frac{ 1 }{ 2 }") def test_mult_interger(self): - exps = [ [2, 3, Operator("*", 2)], [2, -3, Operator("*", 2)], [-2, 3, Operator("*", 2)]] + exps = [ [2, 3, op.get_op("*", 2)], [2, -3, op.get_op("*", 2)], [-2, 3, op.get_op("*", 2)]] wanted_render = [ "2 \\times 3", "2 \\times ( -3 )", "-2 \\times 3"] for (i,e) in enumerate(exps): rend = tex(e) self.assertEqual(rend, wanted_render[i]) def test_mult_letter(self): - exps = [ [2, "a", Operator("*", 2)], ["a", 3, Operator("*", 2)], [-2, "a", Operator("*", 2)], ["a", -2, Operator("*", 2)]] + exps = [ [2, "a", op.get_op("*", 2)], ["a", 3, op.get_op("*", 2)], [-2, "a", op.get_op("*", 2)], ["a", -2, op.get_op("*", 2)]] wanted_render = [ "2 a", "a \\times 3", "-2 a", "a \\times ( -2 )"] for (i,e) in enumerate(exps): rend = tex(e) self.assertEqual(rend, wanted_render[i]) def test_mult_fraction(self): - exps = [ [2, Fraction(1,2), Operator("*", 2)], [Fraction(1,2), 3, Operator("*", 2)]] + exps = [ [2, Fraction(1,2), op.get_op("*", 2)], [Fraction(1,2), 3, op.get_op("*", 2)]] wanted_render = [ "2 \\times \\frac{ 1 }{ 2 }", "\\frac{ 1 }{ 2 } \\times 3"] for (i,e) in enumerate(exps): rend = tex(e) self.assertEqual(rend, wanted_render[i]) def test_parentheses(self): - mul = Operator("*", 2) - add = Operator("+", 2) + mul = op.get_op("*", 2) + add = op.get_op("+", 2) exps = [\ [ 2, 3, add, 4, mul],\ [ 2, 3, mul, 4, add],\ @@ -81,35 +81,35 @@ class TesttxtRender(unittest.TestCase): self.assertEqual(txt([Fraction(1,2)]), "1 / 2") def test_mult_interger(self): - exps = [ [2, 3, Operator("*", 2)], \ - [2, -3, Operator("*", 2)], \ - [-2, 3, Operator("*", 2)]] + exps = [ [2, 3, op.get_op("*", 2)], \ + [2, -3, op.get_op("*", 2)], \ + [-2, 3, op.get_op("*", 2)]] wanted_render = [ "2 * 3", "2 * ( -3 )", "-2 * 3"] for (i,e) in enumerate(exps): rend = txt(e) self.assertEqual(rend, wanted_render[i]) def test_mult_letter(self): - exps = [ [2, "a", Operator("*", 2)], \ - ["a", 3, Operator("*", 2)], \ - [-2, "a", Operator("*", 2)], \ - ["a", -2, Operator("*", 2)]] + exps = [ [2, "a", op.get_op("*", 2)], \ + ["a", 3, op.get_op("*", 2)], \ + [-2, "a", op.get_op("*", 2)], \ + ["a", -2, op.get_op("*", 2)]] wanted_render = [ "2 a", "a * 3", "-2 a", "a * ( -2 )"] for (i,e) in enumerate(exps): rend = txt(e) self.assertEqual(rend, wanted_render[i]) def test_mult_fraction(self): - exps = [ [2, Fraction(1,2), Operator("*", 2)], \ - [Fraction(1,2), 3, Operator("*", 2)]] + exps = [ [2, Fraction(1,2), op.get_op("*", 2)], \ + [Fraction(1,2), 3, op.get_op("*", 2)]] wanted_render = [ "2 * 1 / 2", "1 / 2 * 3"] for (i,e) in enumerate(exps): rend = txt(e) self.assertEqual(rend, wanted_render[i]) def test_parentheses(self): - mul = Operator("*", 2) - add = Operator("+", 2) + mul = op.get_op("*", 2) + add = op.get_op("+", 2) exps = [\ [ 2, 3, add, 4, mul],\ [ 2, 3, mul, 4, add],\ From b518a85befcfaeada0389f12ffc4005c45d148ff Mon Sep 17 00:00:00 2001 From: lafrite Date: Wed, 3 Dec 2014 15:02:59 +0100 Subject: [PATCH 10/14] add requirements file --- requirements.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 requirements.txt diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..a4ced4f --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +pyparsing==2.0.3 From f033cbad854b6f9bae9c1434f40c3c6d7788b9f6 Mon Sep 17 00:00:00 2001 From: lafrite Date: Wed, 3 Dec 2014 15:03:07 +0100 Subject: [PATCH 11/14] Update packaging with distutils --- MANIFEST | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/MANIFEST b/MANIFEST index 5ce56e9..c5c233b 100644 --- a/MANIFEST +++ b/MANIFEST @@ -5,5 +5,16 @@ pymath/arithmetic.py pymath/expression.py pymath/fraction.py pymath/generic.py +pymath/operator.py +pymath/polynom.py pymath/random_expression.py pymath/render.py +pymath/str2tokens.py +test/test_arithmetic.py +test/test_expression.py +test/test_fraction.py +test/test_generic.py +test/test_polynom.py +test/test_random_expression.py +test/test_render.py +test/test_str2tokens.py From 573945a63ad3386d60fe000d890525b7caa961a2 Mon Sep 17 00:00:00 2001 From: lafrite Date: Wed, 3 Dec 2014 15:14:50 +0100 Subject: [PATCH 12/14] add test on printing for Fraction --- test/test_fraction.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test/test_fraction.py b/test/test_fraction.py index 78c4cdf..3ed27f9 100644 --- a/test/test_fraction.py +++ b/test/test_fraction.py @@ -121,6 +121,17 @@ class TestFraction(unittest.TestCase): def test_le(self): pass + def test_tex(self): + f = Fraction(2, 3) + ans = "\\frac{ 2 }{ 3 }" + self.assertEqual(f.__tex__(), ans) + + def test_txt(self): + f = Fraction(2, 3) + ans = "2 / 3" + self.assertEqual(f.__txt__(), ans) + + if __name__ == '__main__': unittest.main() From ffd2a55b659b3cf8f2f0e5af14fb34ac88fcd33c Mon Sep 17 00:00:00 2001 From: lafrite Date: Wed, 3 Dec 2014 15:52:08 +0100 Subject: [PATCH 13/14] add doctest in random_expression --- pymath/random_expression.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/pymath/random_expression.py b/pymath/random_expression.py index 32935a6..a638744 100644 --- a/pymath/random_expression.py +++ b/pymath/random_expression.py @@ -31,6 +31,19 @@ class RdExpression(object): @classmethod def set_form(cls, form): + """ Define whether RdExpression create expression with Expression (nice render) or if it only replace inside {} not taking care or render + + >>> form = "{a}*{b}" + >>> exp = RdExpression(form)() + >>> print(type(exp)) + + >>> RdExpression.set_form("raw") + >>> form = "{a}*{b}" + >>> exp = RdExpression(form)() + >>> print(type(exp)) + + """ + cls.FORM = form @classmethod @@ -189,12 +202,13 @@ if __name__ == '__main__': rdExp3 = RdExpression(form, cond) desc_rdExp(rdExp3) - form = "{a + a*10}*4 + {a} + 2*{b}" + form = "{a+a*10}*4 + {a} + 2*{b}" cond = ["{a-b} + {b} in list(range(20))", "abs({a}) not in [1]", "{b} not in [1]", "gcd({a},{b}) == 1"] rdExp3 = RdExpression(form, cond) desc_rdExp(rdExp3) - + import doctest + doctest.testmod() From edae08d3bc318aa4a16a23e84b7dc5daf259e7f2 Mon Sep 17 00:00:00 2001 From: lafrite Date: Wed, 3 Dec 2014 16:25:02 +0100 Subject: [PATCH 14/14] add doctest operator --- pymath/operator.py | 107 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 98 insertions(+), 9 deletions(-) diff --git a/pymath/operator.py b/pymath/operator.py index 5d71603..975772a 100644 --- a/pymath/operator.py +++ b/pymath/operator.py @@ -223,8 +223,19 @@ class op(object): :op: symbole of the op :arity: the arity + + >>> op.get_op('+') + '+' + >>> mul = op.get_op('*') + >>> mul.tex + '{op1} \\\\times {op2}' + >>> mul.txt + '{op1} * {op2}' """ - return getattr(cls, cls._operators[(op, arity)]) + try: + return getattr(cls, cls._operators[(op, arity)]) + except KeyError: + raise KeyError("{theOp} (arity: {arity}) is not available".format(theOp = op, arity = arity)) @classmethod def can_be_operator(cls, symbole): @@ -235,7 +246,20 @@ class op(object): @ClassProperty @operatorize def add(cls): - """ The operator + """ + """ The operator + + + >>> add = op.add + >>> add + '+' + >>> add(1, 2) + 3 + >>> add.__tex__('1','2') + '1 + 2' + >>> add.__txt__('1','2') + '1 + 2' + >>> add.__tex__('1','-2') + '1 + (-2)' + """ caract = { "operator" : "+", \ "name" : "add",\ @@ -251,7 +275,20 @@ class op(object): @ClassProperty @operatorize def sub(self): - """ The operator - """ + """ The operator - + + >>> sub = op.sub + >>> sub + '-' + >>> sub(1, 2) + -1 + >>> sub.__tex__('1','2') + '1 - 2' + >>> sub.__txt__('1','2') + '1 - 2' + >>> sub.__tex__('1','-2') + '1 - (-2)' + """ caract = { "operator" : "-", \ "name" : "sub",\ @@ -267,7 +304,20 @@ class op(object): @ClassProperty @operatorize def sub1(self): - """ The operator - """ + """ The operator - + + >>> sub1 = op.sub1 + >>> sub1 + '-' + >>> sub1(1) + -1 + >>> sub1.__tex__('1') + '- 1' + >>> sub1.__txt__('1') + '- 1' + >>> sub1.__tex__('-1') + '- (-1)' + """ def add_parenthesis(self, op): """ Add parenthesis if necessary """ try: @@ -298,7 +348,20 @@ class op(object): @ClassProperty @operatorize def mul(self): - """ The operator * """ + """ The operator * + + >>> mul = op.mul + >>> mul + '*' + >>> mul(1, 2) + 2 + >>> mul.__tex__('1','2') + '1 \\times 2' + >>> mul.__txt__('1','2') + '1 * 2' + >>> mul.__tex__('1','-2') + '1 \\times (-2)' + """ # * can not be display in some cases def _render(self, link, *args): @@ -331,7 +394,20 @@ class op(object): @ClassProperty @operatorize def div(self): - """ The operator / """ + """ The operator / + + >>> div = op.div + >>> div + '/' + >>> div(1, 2) + < Fraction 1 / 2> + >>> div.__tex__('1','2') + '\\frac{ 1 }{ 2 }' + >>> div.__tex__('1','2') + '\\frac{ -1 }{ 2 }' + >>> div.__txt__('1','2') + '1 / 2' + """ from .fraction import Fraction def _call(self, op1, op2): if op2 == 1: @@ -343,7 +419,7 @@ class op(object): # Pas besoin de parenthèses en plus pour \frac replacement = {"op"+str(i+1): op for (i,op) in enumerate(args)} - ans = self._tex.format(**replacement) + ans = self.tex.format(**replacement) ans = save_mainOp(ans, self) return ans @@ -352,7 +428,7 @@ class op(object): "name" : "div",\ "priority" : 4, \ "arity" : 2, \ - "txt" : "{op1} /^ {op2}",\ + "txt" : "{op1} / {op2}",\ "tex" : "\\frac{{ {op1} }}{{ {op2} }}",\ "_call": _call,\ "__tex__":__tex__,\ @@ -363,7 +439,20 @@ class op(object): @ClassProperty @operatorize def pw(self): - """ The operator ^ """ + """ The operator ^ + + >>> pw = op.pw + >>> pw + '^' + >>> pw(2, 3) + 8 + >>> pw.__tex__('2','3') + '2^{ 3 }' + >>> pw.__txt__('2','3') + '2 ^ 3' + >>> pw.__txt__('-2','3') + '( -2 ) ^ 3' + """ def _call(self, op1, op2): """ Calling this operator performs the rigth calculus """ return getattr(op1, "__pow__")(op2)