From 2763e46fabbc7581c3b7b5d8722c0745c2e735af Mon Sep 17 00:00:00 2001 From: Lafrite Date: Wed, 25 Feb 2015 10:17:01 +0100 Subject: [PATCH 01/26] Remove bugs from .gitignore and add a new bug --- .gitignore | 1 - bugs | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 bugs diff --git a/.gitignore b/.gitignore index 3db55bf..644cdba 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ __pycache__/ *.pyc dist/ -bugs *.egg-info/ diff --git a/bugs b/bugs new file mode 100644 index 0000000..bd480c0 --- /dev/null +++ b/bugs @@ -0,0 +1,36 @@ +* Expression importe mal 4x^2 + In [9]: e = Expression("3x + 4x^2 - 1") + + In [10]: print(e) + 3 x + ( 4 x ) ^ 2 - 1 + + -> faire un test unitaire dessus + +* Render ne met pas les parenthèses là où il faut. + + In [31]: r + Out[31]: < Polynom [-4, -15, -14]> + + In [35]: print(r) + -14 * x ^ 2 + -15 x + ( -4 ) + + In [36]: r.get_postfix() + Out[36]: [-14, 'x', 2, '^', '*', -15, 'x', '*', '+', -4, '+'] + + In [37]: txt(r.get_postfix()) + Out[37]: '-14 * x ^ 2 + -15 x + ( -4 )' + + + -> faire un test unitaire dessus + +* Fraction ne simplifie pas correctement + + In [5]: for i in P.alpha.simplify(): + print(i) + ...: + \frac{ - 2 }{ 2 \times 3 } + \frac{ -2 }{ 6 } + \frac{ ( -1 ) \times 2 }{ 3 \times 2 } + \frac{ -1 }{ 3 } + \frac{ -2 }{ 6 } + From 26838c7b0ab149a0829603ba1acd4303da3ac8df Mon Sep 17 00:00:00 2001 From: Lafrite Date: Wed, 25 Feb 2015 14:54:44 +0100 Subject: [PATCH 02/26] Explain how I want my program to work --- docs/construction.mdwn | 45 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/docs/construction.mdwn b/docs/construction.mdwn index 116c65d..6f3210b 100644 --- a/docs/construction.mdwn +++ b/docs/construction.mdwn @@ -9,7 +9,7 @@ Ces objets doivent pouvoir être afficher en *txt* ou en *tex* avec les méthode ### Operator Cette classe regroupe les operateurs. Que l'on s'autorise à utiliser. On y accède à partir de deux caractérisiques le symbole et l'arité. -Liste des attributs mportants: +Liste des attributs importants: * arity: nombre d'opérande accepté * priority: où se place l'opérateur dans la règles des priorités parmis les autres opérateurs * isOperator: permet de vérifier que c'est bien un opérateur @@ -34,10 +34,49 @@ Pour définir ces anneaux, il faudra contre avoir les méthodes suivantes: #### Quotient de polynomes (racines) - ## Expression - ## Render +## Simplify-simplified / compute-child + +Dans cette partie, on va expliquer le fonctionnement des mécanismes de simplification des expressions/objets mathématiques. + +La simplification des expressions se fait avec les deux méthodes suivantes: + +* *simplify()* pour: + * un polynôme permet d'accéder à la forme canonique d'un polynôme + * une fraction permet d'avoir la fraction irréductible associée + * une expression permet de faire tous les calculs possibles de cette expression (à la fin il ne doit y avoir qu'un élément de la liste de tokens) +* *compute_exp()* pour: + * un polynôme ou une fraction fait la même chose que $simplify$. + * une expression fait tous les calculs élémentaires de cette expression. + +Ces deux méthodes fonctionnent ensuite sur le même principe. Elles vont faire le calcul qui leurs est attribuée en enregistrant les étapes dans *steps* puis elles retourneront l'objet de fin de calcul à qui sera assigné les *steps* (ce qui nécessitera par exemple de détourner la classe *int*). + +### Tentative d'explications +C'est ce que je voudrai donc le render ne sera peut être pas exactement le même. + + >>> P = Polynom([0,1,2]) + >>> Q = Polynom([1,1,1]) + >>> R = P+Q + >>> print(R) + 3x^2 + 2x + 1 + >>> for i in explain(R): + ... print(i) + 2x^2 + x + x^2 + x + 1 + (2 + 1)x^2 + (1+1)x + 1 + 3x^3 + 2x + 1 + + >>> P = Polynom([[1,2], [3,4]]) + >>> Q = P.simplify() + >>> print(Q) + 7x + 3 + >>> for i in explain(Q): + ... print(i) + 3x + 4x + 1 + 2 + (3+4)x + (1+2) + 7x + 3 + + From f400582c5b65cda950e4501fca9c43475f86c443 Mon Sep 17 00:00:00 2001 From: Lafrite Date: Wed, 25 Feb 2015 15:06:31 +0100 Subject: [PATCH 03/26] What I want start to sound cool --- docs/construction.mdwn | 45 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/docs/construction.mdwn b/docs/construction.mdwn index 6f3210b..c5269ab 100644 --- a/docs/construction.mdwn +++ b/docs/construction.mdwn @@ -54,15 +54,19 @@ La simplification des expressions se fait avec les deux méthodes suivantes: Ces deux méthodes fonctionnent ensuite sur le même principe. Elles vont faire le calcul qui leurs est attribuée en enregistrant les étapes dans *steps* puis elles retourneront l'objet de fin de calcul à qui sera assigné les *steps* (ce qui nécessitera par exemple de détourner la classe *int*). +Pour accéder à ces étapes, on utilisera alors la méthode *explain* qui expliqueront les étapes intermédiaires. + ### Tentative d'explications C'est ce que je voudrai donc le render ne sera peut être pas exactement le même. +Comportement avec les Polynom (ce serait similaire avec les fractions) + >>> P = Polynom([0,1,2]) >>> Q = Polynom([1,1,1]) >>> R = P+Q >>> print(R) 3x^2 + 2x + 1 - >>> for i in explain(R): + >>> for i in R.explain(): ... print(i) 2x^2 + x + x^2 + x + 1 (2 + 1)x^2 + (1+1)x + 1 @@ -72,11 +76,48 @@ C'est ce que je voudrai donc le render ne sera peut être pas exactement le mêm >>> Q = P.simplify() >>> print(Q) 7x + 3 - >>> for i in explain(Q): + >>> for i in Q.explain(): ... print(i) 3x + 4x + 1 + 2 (3+4)x + (1+2) 7x + 3 + +Comportement avec les expressions + + >>> e = Expression("1+2*3") + >>> e1 = e.compute_exp() + >>> e1 + 1 + 6 + >>> type(e1) + Expression + >>> for i in e1.explain(): # Peu interessant mais il aurai pu y avoir des calculs de fractions + ... print(i) + 1 + 2 * 3 + 1 + 6 + >>> e2 = e.simplify() + >>> e2 + 7 + >>> type(e2) + FakeInt + >>> for i in e2.explain(): + ... print(i) + 1 + 2 * 3 + 1 + 6 + 7 + >>> f = Expression("4 - 5") + >>> g = e + f + >>> g # Les deux expressions ont été concaténée mais aucun calcul n'a été fait + < Expression [1, 2, 3, '*', '+', 4, 5, '-', '+']> + >>> for i in g.explain(): + ... print(i) + 1 + 2 * 3 + 4 - 5 + >>> for i in g.simplify().explain(): + ... print(i) + 1 + 2 \times 3 + 4 - 5 + 1 + 6 + ( -1 ) + 7 + ( -1 ) + 6 + From d88c8ff7381a2874328943e91dc7774913d52643 Mon Sep 17 00:00:00 2001 From: Lafrite Date: Wed, 25 Feb 2015 15:23:01 +0100 Subject: [PATCH 04/26] Start working on parent class: Renderable_expression and Computable_expression --- pymath/computable_expression.py | 46 +++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 pymath/computable_expression.py diff --git a/pymath/computable_expression.py b/pymath/computable_expression.py new file mode 100644 index 0000000..59a513e --- /dev/null +++ b/pymath/computable_expression.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python +# encoding: utf-8 + +class Renderable_expression(object): + + """Docstring for Renderable_expression. """ + + def __init__(self): + """@todo: to be defined1. """ + + + + +class Computable_expression(Renderable_expression): + + """ A computable_expression is an expression represented by a list of postfix tokens. It's a parent class of a more classical Expression, Fraction and Polynom (and later square root) + Child class will herits those methods + * simplify: Compute entirely the expression + * explain: Generator which return steps which leed to himself + + """ + + def __init__(self): + """@todo: to be defined1. """ + + pass + + def simplify(self): + """ Simplify the expression + + :returns: the simplified expression with .steps attributes where steps are stocked + + """ + pass + + def explain(self): + """ Generate and render steps which leed to itself """ + pass + + + + +# ----------------------------- +# Reglages pour 'vim' +# vim:set autoindent expandtab tabstop=4 shiftwidth=4: +# cursor: 16 del From 6d8b1786b337ba1936b0ed736817b01823c3bf5a Mon Sep 17 00:00:00 2001 From: Lafrite Date: Thu, 26 Feb 2015 19:02:20 +0100 Subject: [PATCH 05/26] explain works for Expression. Need to adapt Fraction and Polynom now --- pymath/computable_expression.py | 46 ------- pymath/explicable.py | 40 ++++++ pymath/expression.py | 228 ++++++++++++++++++-------------- 3 files changed, 168 insertions(+), 146 deletions(-) delete mode 100644 pymath/computable_expression.py create mode 100644 pymath/explicable.py diff --git a/pymath/computable_expression.py b/pymath/computable_expression.py deleted file mode 100644 index 59a513e..0000000 --- a/pymath/computable_expression.py +++ /dev/null @@ -1,46 +0,0 @@ -#!/usr/bin/env python -# encoding: utf-8 - -class Renderable_expression(object): - - """Docstring for Renderable_expression. """ - - def __init__(self): - """@todo: to be defined1. """ - - - - -class Computable_expression(Renderable_expression): - - """ A computable_expression is an expression represented by a list of postfix tokens. It's a parent class of a more classical Expression, Fraction and Polynom (and later square root) - Child class will herits those methods - * simplify: Compute entirely the expression - * explain: Generator which return steps which leed to himself - - """ - - def __init__(self): - """@todo: to be defined1. """ - - pass - - def simplify(self): - """ Simplify the expression - - :returns: the simplified expression with .steps attributes where steps are stocked - - """ - pass - - def explain(self): - """ Generate and render steps which leed to itself """ - pass - - - - -# ----------------------------- -# Reglages pour 'vim' -# vim:set autoindent expandtab tabstop=4 shiftwidth=4: -# cursor: 16 del diff --git a/pymath/explicable.py b/pymath/explicable.py new file mode 100644 index 0000000..e6b6f0d --- /dev/null +++ b/pymath/explicable.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python +# encoding: utf-8 + +class Explicable(object): + + """ An Explicable object is an object which can be explicable! + + It's a parent class of a more classical Expression, Fraction and Polynom (and later square root) + Child class will have the following method + * explain: Generator which return steps which leed to himself + + """ + def __init__(self, *args, **kwargs): + self.steps = [] + + def explain(self): + """ Generate and render steps which leed to itself """ + old_s = '' + # les étapes pour l'atteindre + try: + for s in self.steps: + new_s = self.STR_RENDER(s) + if new_s != old_s: + old_s = new_s + yield new_s + except AttributeError: + pass + + # Lui même + new_s = self.STR_RENDER(self.postfix_tokens) + if new_s != old_s: + yield new_s + + + + +# ----------------------------- +# Reglages pour 'vim' +# vim:set autoindent expandtab tabstop=4 shiftwidth=4: +# cursor: 16 del diff --git a/pymath/expression.py b/pymath/expression.py index 44dc9e9..93ffd1c 100644 --- a/pymath/expression.py +++ b/pymath/expression.py @@ -2,17 +2,16 @@ # encoding: utf-8 from .generic import Stack, flatten_list, expand_list, isNumber, isOperator, isNumerand -from .render import txt, tex from .str2tokens import str2tokens from .operator import op +from .render import txt, tex +from .explicable import Explicable from .random_expression import RdExpression -__all__ = ['Expression'] - -class Expression(object): - """A calculus expression. Today it can andle only expression with numbers later it will be able to manipulate unknown""" +__all__ = ['Expression', 'Renderable'] +class Renderable(object): STR_RENDER = tex DEFAULT_RENDER = tex @@ -21,7 +20,7 @@ class Expression(object): cls.STR_RENDER = render @classmethod - def get_render(cls ): + def get_render(cls): return cls.STR_RENDER @classmethod @@ -37,23 +36,23 @@ class Expression(object): >>> exp = Expression("2*3/5") >>> print(exp) \\frac{ 2 \\times 3 }{ 5 } - >>> for i in exp.simplify(): + >>> for i in exp.simplify().explain(): ... print(i) \\frac{ 2 \\times 3 }{ 5 } \\frac{ 6 }{ 5 } >>> with Expression.tmp_render(): - ... for i in exp.simplify(): + ... for i in exp.simplify().explain(): ... i < Expression [2, 3, '*', 5, '/']> < Expression [6, 5, '/']> < Fraction 6 / 5> >>> with Expression.tmp_render(txt): - ... for i in exp.simplify(): + ... for i in exp.simplify().explain(): ... print(i) 2 * 3 / 5 6 / 5 - >>> for i in exp.simplify(): + >>> for i in exp.simplify().explain(): ... print(i) \\frac{ 2 \\times 3 }{ 5 } \\frac{ 6 }{ 5 } @@ -67,6 +66,11 @@ class Expression(object): def __exit__(self, type, value, traceback): Expression.set_render(self.old_render) return TmpRenderEnv() + + +class Expression(Explicable, Renderable): + """A calculus expression. Today it can andle only expression with numbers later it will be able to manipulate unknown""" + @classmethod def random(self, form="", conditions=[], val_min = -10, val_max=10): @@ -89,8 +93,7 @@ class Expression(object): """ expression = object.__new__(cls) if type(exp) == str: - #self._exp = exp - expression.postfix_tokens = str2tokens(exp) # les tokens seront alors stockés dans self.tokens temporairement + expression.postfix_tokens = str2tokens(exp) elif type(exp) == list: expression.postfix_tokens = flatten_list([tok.postfix_tokens if Expression.isExpression(tok) else tok for tok in exp]) else: @@ -98,25 +101,24 @@ class Expression(object): if len(expression.postfix_tokens) == 1: token = expression.postfix_tokens[0] - if hasattr(token, 'simplify'): + if hasattr(token, 'simplify') and hasattr(token, 'explain'): return expression.postfix_tokens[0] elif type(token) == int: # On crée un faux int en ajoutant la méthode simplify et simplified et la caractérisique isNumber - simplify = lambda x:[x] - simplified = lambda x:x + simplify = lambda x:x is_number = True - methods_attr = {'simplify':simplify, 'simplified':simplified, 'isNumber': is_number} - fake_token = type('fake_int', (int,), methods_attr)(token) + methods_attr = {'simplify':simplify, 'isNumber': is_number, 'postfix_tokens': [token]} + fake_token = type('fake_int', (int,Explicable, Renderable), methods_attr)(token) return fake_token elif type(token) == str: + # TODO: Pourquoi ne pas créer directement un polynom ici? |jeu. févr. 26 18:59:24 CET 2015 # On crée un faux str en ajoutant la méthode simplify et simplified et la caractérisique isNumber simplify = lambda x:[x] - simplified = lambda x:x is_polynom = True - methods_attr = {'simplify':simplify, 'simplified':simplified, '_isPolynom': is_polynom} - fake_token = type('fake_str', (str,), methods_attr)(token) + methods_attr = {'simplify':simplify, '_isPolynom': is_polynom, 'postfix_tokens': [token]} + fake_token = type('fake_str', (str,Explicable, Renderable), methods_attr)(token) return fake_token else: @@ -129,74 +131,98 @@ class Expression(object): def __str__(self): """ Overload str - If you want to changer render use Expression.set_render(...) + + If you want to changer render use Expression.set_render(...) or use tmp_render context manager. """ return self.STR_RENDER(self.postfix_tokens) def __repr__(self): - return "< Expression " + str(self.postfix_tokens) + ">" + return " ".join(["<", self.__class__ , str(self.postfix_tokens), ">"]) - def render(self, render = lambda x:str(x)): - """ Same as __str__ but accept render as argument - :param render: function which render the list of token (postfix form) to string + #def __str__(self): + # """ + # Overload str + # If you want to changer render use Expression.set_render(...) + # """ + # return self.STR_RENDER(self.postfix_tokens) - """ - # TODO: I don't like the name of this method |ven. janv. 17 12:48:14 CET 2014 - return render(self.postfix_tokens) + #def __repr__(self): + # return "< Expression " + str(self.postfix_tokens) + ">" + + #def render(self, render = lambda x:str(x)): + # """ Same as __str__ but accept render as argument + # :param render: function which render the list of token (postfix form) to string + + # """ + # # TODO: I don't like the name of this method |ven. janv. 17 12:48:14 CET 2014 + # return render(self.postfix_tokens) ## --------------------- ## Mechanism functions def simplify(self): - """ Generator which return steps for computing the expression """ - if not self.can_go_further(): - yield self.STR_RENDER(self.postfix_tokens) - else: - self.compute_exp() - old_s = '' - for s in self.steps: - new_s = self.STR_RENDER(s) - # Astuce pour éviter d'avoir deux fois la même étape (par exemple pour la transfo d'une division en fraction) - if new_s != old_s: - old_s = new_s - yield new_s - - if Expression.isExpression(self.child): - for s in self.child.simplify(): - if old_s != s: - old_s = s - yield s - else: - for s in self.child.simplify(): - new_s = self.STR_RENDER([s]) - # Astuce pour éviter d'avoir deux fois la même étape (par exemple pour la transfo d'une division en fraction) - if new_s != old_s: - old_s = new_s - yield new_s - if old_s != self.STR_RENDER([self.child]): - yield self.STR_RENDER([self.child]) - - - def simplified(self): - """ Get the simplified version of the expression """ + """ Compute entirely the expression and return the result with .steps attribute """ self.compute_exp() - try: - return self.child.simplified() - except AttributeError: - return self.child - def can_go_further(self): - """Check whether it's a last step or not. If not create self.child the next expression. - :returns: 1 if it's not the last step, 0 otherwise - """ - if len(self.postfix_tokens) == 1: - return 0 - else: - return 1 + self.simplified = self.child.simplify() + try: + self.simplified.steps = self.child.steps + self.simplified.steps + except AttributeError: + pass + + return self.simplified + + + # TODO: À changer |jeu. févr. 26 17:18:49 CET 2015 + # if not self.can_go_further(): + # yield self.STR_RENDER(self.postfix_tokens) + # else: + # self.compute_exp() + # old_s = '' + # for s in self.steps: + # new_s = self.STR_RENDER(s) + # # Astuce pour éviter d'avoir deux fois la même étape (par exemple pour la transfo d'une division en fraction) + # if new_s != old_s: + # old_s = new_s + # yield new_s + + # if Expression.isExpression(self.child): + # for s in self.child.simplify(): + # if old_s != s: + # old_s = s + # yield s + # else: + # for s in self.child.simplify(): + # new_s = self.STR_RENDER([s]) + # # Astuce pour éviter d'avoir deux fois la même étape (par exemple pour la transfo d'une division en fraction) + # if new_s != old_s: + # old_s = new_s + # yield new_s + # if old_s != self.STR_RENDER([self.child]): + # yield self.STR_RENDER([self.child]) + + + #def simplified(self): + # """ Get the simplified version of the expression """ + # self.compute_exp() + # try: + # return self.child.simplified() + # except AttributeError: + # return self.child + + # TODO: Normalement ne devrait plus être necessaire. Il faudra par contre s'assurer qu'il soit impossible de créer des Expressions avec une seul élément |jeu. févr. 26 17:26:28 CET 2015 + # def can_go_further(self): + # """Check whether it's a last step or not. If not create self.child the next expression. + # :returns: 1 if it's not the last step, 0 otherwise + # """ + # if len(self.postfix_tokens) == 1: + # return 0 + # else: + # return 1 def compute_exp(self): - """ Create self.child with self.steps to go up to it """ - self.steps = [self.postfix_tokens] + """ Create self.child with and stock steps in it """ + child_steps = [self.postfix_tokens] tokenList = self.postfix_tokens.copy() tmpTokenList = [] @@ -256,9 +282,10 @@ class Expression(object): steps = expand_list(tmpTokenList) if len(steps[:-1]) > 0: - self.steps += [flatten_list(s) for s in steps[:-1]] + child_steps += [flatten_list(s) for s in steps[:-1]] self.child = Expression(steps[-1]) + self.child.steps = child_steps @classmethod def isExpression(self, other): @@ -329,9 +356,10 @@ class Expression(object): def test(exp): a = Expression(exp) - print(a) - for i in a.simplify(): - print(type(i)) + b = a.simplify() + + for i in b.explain(): + #print(type(i)) print(i) #print(type(a.simplified()), ":", a.simplified()) @@ -366,32 +394,32 @@ if __name__ == '__main__': #f = -e #print(f) - #exp = "2 * 3 * 3 * 5" - #test(exp) + exp = "2 * 3 * 3 * 5" + test(exp) - #exp = "2 * 3 + 3 * 5" - #test(exp) + exp = "2 * 3 + 3 * 5" + test(exp) - #exp = "2 * ( 3 + 4 ) + 3 * 5" - #test(exp) + exp = "2 * ( 3 + 4 ) + 3 * 5" + test(exp) - #exp = "2 * ( 3 + 4 ) + ( 3 - 4 ) * 5" - #test(exp) - # - #exp = "2 * ( 2 - ( 3 + 4 ) ) + ( 3 - 4 ) * 5" - #test(exp) - # - #exp = "2 * ( 2 - ( 3 + 4 ) ) + 5 * ( 3 - 4 )" - #test(exp) - # - #exp = "2 + 5 * ( 3 - 4 )" - #test(exp) + exp = "2 * ( 3 + 4 ) + ( 3 - 4 ) * 5" + test(exp) + + exp = "2 * ( 2 - ( 3 + 4 ) ) + ( 3 - 4 ) * 5" + test(exp) + + exp = "2 * ( 2 - ( 3 + 4 ) ) + 5 * ( 3 - 4 )" + test(exp) + + exp = "2 + 5 * ( 3 - 4 )" + test(exp) - #exp = "( 2 + 5 ) * ( 3 - 4 )^4" - #test(exp) + exp = "( 2 + 5 ) * ( 3 - 4 )^4" + test(exp) - #exp = "( 2 + 5 ) * ( 3 * 4 )" - #test(exp) + exp = "( 2 + 5 ) * ( 3 * 4 )" + test(exp) #exp = "( 2 + 5 - 1 ) / ( 3 * 4 )" #test(exp) @@ -426,8 +454,8 @@ if __name__ == '__main__': #for i in exp.simplify(): # print(i) - import doctest - doctest.testmod() + #import doctest + #doctest.testmod() # ----------------------------- # Reglages pour 'vim' From 3c21b42d6c4bfdcc1d777dbf188dd67e6876974b Mon Sep 17 00:00:00 2001 From: Lafrite Date: Fri, 27 Feb 2015 08:55:36 +0100 Subject: [PATCH 06/26] Simplify test_expression for py.test and adapt it for explain method --- test/test_expression.py | 94 +++++++++++++++++++++++------------------ 1 file changed, 54 insertions(+), 40 deletions(-) diff --git a/test/test_expression.py b/test/test_expression.py index d8f6eb6..003e986 100644 --- a/test/test_expression.py +++ b/test/test_expression.py @@ -1,63 +1,77 @@ #!/usr/bin/env python # encoding: utf-8 +""" Testing Expression """ -import unittest - from pymath.expression import Expression from pymath.fraction import Fraction from pymath.generic import first_elem from pymath.render import txt, tex -class TestExpression(unittest.TestCase): - """Testing functions from pymath.expression""" - - def setup(self): - Expression.set_render(txt) - - def test_init_from_str(self): +def test_init_from_str(): exp = Expression("2 + 3") - self.assertEqual(exp.postfix_tokens, [2, 3, "+"]) + assert exp.postfix_tokens == [2, 3, "+"] - def test_init_from_exp(self): - pass +def test_init_from_exp(): + pass - def test_list(self): - exp = Expression([2, 3, "+"]) - self.assertEqual(exp.postfix_tokens, [2, 3, "+"]) +def test_init_list(): + exp = Expression([2, 3, "+"]) + assert exp.postfix_tokens == [2, 3, "+"] - def test_simplify_frac(self): - exp = Expression("1/2 - 4") - steps = ['\\frac{ 1 }{ 2 } - 4', \ - '\\frac{ 1 \\times 1 }{ 2 \\times 1 } - \\frac{ 4 \\times 2 }{ 1 \\times 2 }',\ - '\\frac{ 1 }{ 2 } - \\frac{ 8 }{ 2 }',\ - '\\frac{ 1 - 8 }{ 2 }',\ - '\\frac{ -7 }{ 2 }'] - self.assertEqual(steps, list(exp.simplify())) - Expression.set_render(txt) +def test_init_one_element_int_from_str(): + exp = Expression("1") - def test_add_exp(self): - e = Expression("12- 4") - f = Expression("4 + 1") - g = e + f - self.assertEqual(g.postfix_tokens, [12, 4, '-', 4, 1, "+", "+"]) +def test_init_one_element_int_from_list(): + exp = Expression([1]) - def test_mul_exp(self): - e = Expression("12- 4") - f = Expression("4 + 1") - g = e * f - self.assertEqual(g.postfix_tokens, [12, 4, '-', 4, 1, "+", "*"]) +#def test_init_one_element_str_from_str(): +# exp = Expression("x") +# +#def test_init_one_element_str_from_list(): +# exp = Expression(["x"]) - def test_neg_exp(self): - e = Expression("12- 4") - g = -e - self.assertEqual(g.postfix_tokens, [12, 4, '-', '-']) +def test_simplify_exp(): + exp = Expression("1 + 2 * 3") + simplified = exp.simplify() + ans = Expression("7") + assert ans == simplified +#def test_simplify_frac(): +# exp = Expression("1/2 - 4") +# simplified = exp.simplify() +# ans = Expression("-7/2") +# assert simplified == ans +# +#def test_explain_frac(): +# exp = Expression("1/2 - 4") +# simplified = exp.simplify() +# +# steps = ['\\frac{ 1 }{ 2 } - 4', \ +# '\\frac{ 1 \\times 1 }{ 2 \\times 1 } - \\frac{ 4 \\times 2 }{ 1 \\times 2 }',\ +# '\\frac{ 1 }{ 2 } - \\frac{ 8 }{ 2 }',\ +# '\\frac{ 1 - 8 }{ 2 }',\ +# '\\frac{ -7 }{ 2 }'] +# assert simplified.steps == list(exp.simplify()) -if __name__ == '__main__': - unittest.main() +def test_add_exp(): + e = Expression("12- 4") + f = Expression("4 + 1") + g = e + f + assert g.postfix_tokens == [12, 4, '-', 4, 1, "+", "+"] + +def test_mul_exp(): + e = Expression("12- 4") + f = Expression("4 + 1") + g = e * f + assert g.postfix_tokens == [12, 4, '-', 4, 1, "+", "*"] + +def test_neg_exp(): + e = Expression("12- 4") + g = -e + assert g.postfix_tokens == [12, 4, '-', '-'] # ----------------------------- From 7e76f4aecde8167d7366cd8203ec9c736c6f95d7 Mon Sep 17 00:00:00 2001 From: Lafrite Date: Fri, 27 Feb 2015 09:09:42 +0100 Subject: [PATCH 07/26] Remove commented parts --- pymath/expression.py | 69 -------------------------------------------- 1 file changed, 69 deletions(-) diff --git a/pymath/expression.py b/pymath/expression.py index 93ffd1c..5c18743 100644 --- a/pymath/expression.py +++ b/pymath/expression.py @@ -139,27 +139,6 @@ class Expression(Explicable, Renderable): def __repr__(self): return " ".join(["<", self.__class__ , str(self.postfix_tokens), ">"]) - #def __str__(self): - # """ - # Overload str - # If you want to changer render use Expression.set_render(...) - # """ - # return self.STR_RENDER(self.postfix_tokens) - - #def __repr__(self): - # return "< Expression " + str(self.postfix_tokens) + ">" - - #def render(self, render = lambda x:str(x)): - # """ Same as __str__ but accept render as argument - # :param render: function which render the list of token (postfix form) to string - - # """ - # # TODO: I don't like the name of this method |ven. janv. 17 12:48:14 CET 2014 - # return render(self.postfix_tokens) - - ## --------------------- - ## Mechanism functions - def simplify(self): """ Compute entirely the expression and return the result with .steps attribute """ self.compute_exp() @@ -171,54 +150,6 @@ class Expression(Explicable, Renderable): pass return self.simplified - - - # TODO: À changer |jeu. févr. 26 17:18:49 CET 2015 - # if not self.can_go_further(): - # yield self.STR_RENDER(self.postfix_tokens) - # else: - # self.compute_exp() - # old_s = '' - # for s in self.steps: - # new_s = self.STR_RENDER(s) - # # Astuce pour éviter d'avoir deux fois la même étape (par exemple pour la transfo d'une division en fraction) - # if new_s != old_s: - # old_s = new_s - # yield new_s - - # if Expression.isExpression(self.child): - # for s in self.child.simplify(): - # if old_s != s: - # old_s = s - # yield s - # else: - # for s in self.child.simplify(): - # new_s = self.STR_RENDER([s]) - # # Astuce pour éviter d'avoir deux fois la même étape (par exemple pour la transfo d'une division en fraction) - # if new_s != old_s: - # old_s = new_s - # yield new_s - # if old_s != self.STR_RENDER([self.child]): - # yield self.STR_RENDER([self.child]) - - - #def simplified(self): - # """ Get the simplified version of the expression """ - # self.compute_exp() - # try: - # return self.child.simplified() - # except AttributeError: - # return self.child - - # TODO: Normalement ne devrait plus être necessaire. Il faudra par contre s'assurer qu'il soit impossible de créer des Expressions avec une seul élément |jeu. févr. 26 17:26:28 CET 2015 - # def can_go_further(self): - # """Check whether it's a last step or not. If not create self.child the next expression. - # :returns: 1 if it's not the last step, 0 otherwise - # """ - # if len(self.postfix_tokens) == 1: - # return 0 - # else: - # return 1 def compute_exp(self): """ Create self.child with and stock steps in it """ From d7a2df190fd0c101dad1002172d4204f712179f5 Mon Sep 17 00:00:00 2001 From: Lafrite Date: Fri, 27 Feb 2015 11:38:17 +0100 Subject: [PATCH 08/26] mod div to truediv in expression --- pymath/expression.py | 103 ++++++++++++++++++++++++++++++++----------- 1 file changed, 77 insertions(+), 26 deletions(-) diff --git a/pymath/expression.py b/pymath/expression.py index 5c18743..00e63af 100644 --- a/pymath/expression.py +++ b/pymath/expression.py @@ -137,7 +137,7 @@ class Expression(Explicable, Renderable): return self.STR_RENDER(self.postfix_tokens) def __repr__(self): - return " ".join(["<", self.__class__ , str(self.postfix_tokens), ">"]) + return " ".join(["<", str(self.__class__) , str(self.postfix_tokens), ">"]) def simplify(self): """ Compute entirely the expression and return the result with .steps attribute """ @@ -255,31 +255,82 @@ class Expression(Explicable, Renderable): return Expression([other] + self.postfix_tokens + [operator]) def __add__(self, other): + """ Overload + + + >>> a = Expression("1+2") + >>> print(a.postfix_tokens) + [1, 2, '+'] + >>> b = Expression("3+4") + >>> print(b.postfix_tokens) + [3, 4, '+'] + >>> c = a + b + >>> print(c.postfix_tokens) + [1, 2, '+', 3, 4, '+', '+'] + """ return self.operate(other, op.add) def __radd__(self, other): return self.roperate(other, op.add) def __sub__(self, other): + """ Overload - + + >>> a = Expression("1+2") + >>> print(a.postfix_tokens) + [1, 2, '+'] + >>> b = Expression("3+4") + >>> print(b.postfix_tokens) + [3, 4, '+'] + >>> c = a - b + >>> print(c.postfix_tokens) + [1, 2, '+', 3, 4, '+', '-'] + """ return self.operate(other, op.sub) def __rsub__(self, other): return self.roperate(other, op.sub) def __mul__(self, other): + """ Overload * + + >>> a = Expression("1+2") + >>> print(a.postfix_tokens) + [1, 2, '+'] + >>> b = Expression("3+4") + >>> print(b.postfix_tokens) + [3, 4, '+'] + >>> c = a * b + >>> print(c.postfix_tokens) + [1, 2, '+', 3, 4, '+', '*'] + """ return self.operate(other, op.mul) def __rmul__(self, other): return self.roperate(other, op.mul) - def __div__(self, other): + def __truediv__(self, other): + """ Overload / + + >>> a = Expression("1+2") + >>> print(a.postfix_tokens) + [1, 2, '+'] + >>> b = Expression("3+4") + >>> print(b.postfix_tokens) + [3, 4, '+'] + >>> c = a / b + >>> print(c.postfix_tokens) + [1, 2, '+', 3, 4, '+', '/'] + """ return self.operate(other, op.div) def __rdiv__(self, other): return self.roperate(other, op.div) def __pow__(self, other): - return self.operate(other, op.pow) + return self.operate(other, op.pw) + + def __xor__(self, other): + return self.operate(other, op.pw) def __neg__(self): return Expression(self.postfix_tokens + [op.sub1]) @@ -325,32 +376,32 @@ if __name__ == '__main__': #f = -e #print(f) - exp = "2 * 3 * 3 * 5" - test(exp) + #exp = "2 * 3 * 3 * 5" + #test(exp) - exp = "2 * 3 + 3 * 5" - test(exp) + #exp = "2 * 3 + 3 * 5" + #test(exp) - exp = "2 * ( 3 + 4 ) + 3 * 5" - test(exp) + #exp = "2 * ( 3 + 4 ) + 3 * 5" + #test(exp) - exp = "2 * ( 3 + 4 ) + ( 3 - 4 ) * 5" - test(exp) - - exp = "2 * ( 2 - ( 3 + 4 ) ) + ( 3 - 4 ) * 5" - test(exp) - - exp = "2 * ( 2 - ( 3 + 4 ) ) + 5 * ( 3 - 4 )" - test(exp) - - exp = "2 + 5 * ( 3 - 4 )" - test(exp) + #exp = "2 * ( 3 + 4 ) + ( 3 - 4 ) * 5" + #test(exp) + # + #exp = "2 * ( 2 - ( 3 + 4 ) ) + ( 3 - 4 ) * 5" + #test(exp) + # + #exp = "2 * ( 2 - ( 3 + 4 ) ) + 5 * ( 3 - 4 )" + #test(exp) + # + #exp = "2 + 5 * ( 3 - 4 )" + #test(exp) - exp = "( 2 + 5 ) * ( 3 - 4 )^4" - test(exp) + #exp = "( 2 + 5 ) * ( 3 - 4 )^4" + #test(exp) - exp = "( 2 + 5 ) * ( 3 * 4 )" - test(exp) + #exp = "( 2 + 5 ) * ( 3 * 4 )" + #test(exp) #exp = "( 2 + 5 - 1 ) / ( 3 * 4 )" #test(exp) @@ -385,8 +436,8 @@ if __name__ == '__main__': #for i in exp.simplify(): # print(i) - #import doctest - #doctest.testmod() + import doctest + doctest.testmod() # ----------------------------- # Reglages pour 'vim' From 769666892f41a7815d59c76c5a317bae0ecc254b Mon Sep 17 00:00:00 2001 From: Lafrite Date: Fri, 27 Feb 2015 11:43:20 +0100 Subject: [PATCH 09/26] Adapt Fraction for explain and new logic and verify doctest. Need to adapt unittest --- pymath/fraction.py | 245 +++++++++++++++++++++++++++++---------------- 1 file changed, 159 insertions(+), 86 deletions(-) diff --git a/pymath/fraction.py b/pymath/fraction.py index 4b9ff3e..9cf4c91 100644 --- a/pymath/fraction.py +++ b/pymath/fraction.py @@ -4,13 +4,15 @@ from .arithmetic import gcd from .generic import isNumber from .operator import op -from .expression import Expression +from .expression import Expression, Renderable +from .explicable import Explicable from .render import txt, tex +from copy import copy __all__ = ['Fraction'] -class Fraction(object): +class Fraction(Explicable, Renderable): """Fractions!""" def __init__(self, num, denom = 1): @@ -20,6 +22,7 @@ class Fraction(object): :param denom: the denominator """ + super(Fraction, self).__init__() self._num = num self._denom = denom @@ -32,44 +35,57 @@ class Fraction(object): >>> f = Fraction(3, 6) >>> f.simplify() - [< Expression [1, 3, '*', 2, 3, '*', '/']>, < Fraction 1 / 2>] + < Fraction 1 / 2> + >>> for i in f.simplify().explain(): + ... print(i) + \\frac{ 3 }{ 6 } + \\frac{ 1 \\times 3 }{ 2 \\times 3 } + \\frac{ 1 }{ 2 } + >>> f = Fraction(6,9) + >>> f.simplify() + < Fraction 2 / 3> + >>> for i in f.simplify().explain(): + ... print(i) + \\frac{ 6 }{ 9 } + \\frac{ 2 \\times 3 }{ 3 \\times 3 } + \\frac{ 2 }{ 3 } >>> f = Fraction(0,3) >>> f.simplify() - [0] + 0 """ - steps = [] + ini_step = [Expression(self.postfix_tokens)] if self._num == 0: - steps.append(0) + return Expression([0]) - return steps - - if self._denom < 0: + elif self._denom < 0: n_frac = Fraction(-self._num, -self._denom) - steps.append(n_frac) - else: - n_frac = self + ans = n_frac.simplify() + ans.steps = ini_step + ans.steps + return ans - gcd_ = gcd(abs(n_frac._num), abs(n_frac._denom)) - if gcd_ == n_frac._denom: - n_frac = n_frac._num // gcd_ - steps.append(n_frac) + gcd_ = gcd(abs(self._num), abs(self._denom)) + if gcd_ == self._denom: + n_frac = self._num // gcd_ + return Expression([n_frac]) elif gcd_ != 1: - n_frac = Fraction(n_frac._num // gcd_ , n_frac._denom // gcd_) - steps.append(Expression([n_frac._num, gcd_, op.mul, n_frac._denom, gcd_, op.mul, op.div ])) + n_frac = Fraction(self._num // gcd_ , self._denom // gcd_) + ini_step += [Expression([n_frac._num, gcd_, op.mul, n_frac._denom, gcd_, op.mul, op.div ])] - steps.append(n_frac) + n_frac.steps = ini_step + n_frac.steps + return n_frac - return steps + else: + return self @property - def postfix(self): + def postfix_tokens(self): """Postfix form of the fraction >>> f = Fraction(3, 5) - >>> f.postfix + >>> f.postfix_tokens [3, 5, '/'] """ @@ -79,12 +95,13 @@ class Fraction(object): return [self._num, self._denom, op.div] def __str__(self): - return str(Expression(self.postfix)) + return str(Expression(self.postfix_tokens)) def __repr__(self): return "< Fraction {num} / {denom}>".format(num=self._num, denom = self._denom) def __txt__(self): + # TODO: À simplifier je ne comprends plus le pourquoi du comment de cette méthode. |ven. févr. 27 09:21:49 CET 2015 old_render = Expression.get_render() Expression.set_render(txt) _txt = self.__str__() @@ -119,18 +136,28 @@ class Fraction(object): >>> f = Fraction(1, 2) >>> g = Fraction(2, 3) >>> f + g - [< Expression [1, 3, '*', 2, 3, '*', '/', 2, 2, '*', 3, 2, '*', '/', '+']>, < Expression [3, 6, '/', 4, 6, '/', '+']>, < Expression [< Fraction 3 / 6>, < Fraction 4 / 6>, '+']>, < Expression [3, 4, '+', 6, '/']>, < Expression [7, 6, '/']>, < Fraction 7 / 6>] + < Fraction 7 / 6> + >>> (f+g).steps + [< [1, 2, '/', 2, 3, '/', '+'] >, [1, 3, '*', 2, 3, '*', '/', 2, 2, '*', 3, 2, '*', '/', '+'], [3, 6, '/', 4, 6, '/', '+'], [< Fraction 3 / 6>, < Fraction 4 / 6>, '+'], [< Fraction 3 / 6>, < Fraction 4 / 6>, '+']] >>> f + 2 - [< Expression [1, 1, '*', 2, 1, '*', '/', 2, 2, '*', 1, 2, '*', '/', '+']>, < Expression [1, 2, '/', 4, 2, '/', '+']>, < Expression [< Fraction 1 / 2>, < Fraction 4 / 2>, '+']>, < Expression [1, 4, '+', 2, '/']>, < Expression [5, 2, '/']>, < Fraction 5 / 2>] + < Fraction 5 / 2> + >>> (f+2).steps + [< [1, 2, '/', 2, '+'] >, [1, 1, '*', 2, 1, '*', '/', 2, 2, '*', 1, 2, '*', '/', '+'], [1, 2, '/', 4, 2, '/', '+'], [< Fraction 1 / 2>, < Fraction 4 / 2>, '+'], [< Fraction 1 / 2>, < Fraction 4 / 2>, '+']] >>> f = Fraction(3, 4) >>> g = Fraction(5, 4) >>> f + g - [< Expression [3, 5, '+', 4, '/']>, < Expression [8, 4, '/']>, 2] + 2 + >>> (f+g).steps + [< [3, 4, '/', 5, 4, '/', '+'] >, [3, 5, '+', 4, '/'], [8, 4, '/']] + >>> f+0 + < Fraction 3 / 4> + >>> (f+0).steps + [] """ if other == 0: - return [self] + return copy(self) number = self.convert2fraction(other) @@ -148,14 +175,14 @@ class Fraction(object): exp = Expression([self._num, coef1, op.mul, self._denom, coef1, op.mul, op.div, number._num, coef2, op.mul, number._denom, coef2, op.mul, op.div,op.add]) - with Expression.tmp_render(): - steps = list(exp.simplify()) - - return steps + ini_step = Expression(self.postfix_tokens) + Expression(number.postfix_tokens) + ans = exp.simplify() + ans.steps = [ini_step] + ans.steps + return ans def __radd__(self, other): if other == 0: - return [self] + return Expression(self.postfix_tokens) number = self.convert2fraction(other) @@ -167,11 +194,17 @@ class Fraction(object): >>> f = Fraction(1, 2) >>> g = Fraction(2, 3) >>> f - g - [< Expression [1, 3, '*', 2, 3, '*', '/', 2, 2, '*', 3, 2, '*', '/', '-']>, < Expression [3, 6, '/', 4, 6, '/', '-']>, < Expression [< Fraction 3 / 6>, < Fraction 4 / 6>, '-']>, < Expression [3, 4, '-', 6, '/']>, < Expression [-1, 6, '/']>, < Fraction -1 / 6>] + < Fraction -1 / 6> + >>> (f-g).steps + [< [1, 2, '/', 2, 3, '/', '-'] >, [1, 3, '*', 2, 3, '*', '/', 2, 2, '*', 3, 2, '*', '/', '-'], [3, 6, '/', 4, 6, '/', '-'], [< Fraction 3 / 6>, < Fraction 4 / 6>, '-'], [< Fraction 3 / 6>, < Fraction 4 / 6>, '-']] + >>> f - 0 + < Fraction 1 / 2> + >>> (f-0).steps + [] """ if other == 0: - return [self] + return copy(self) number = self.convert2fraction(other) @@ -189,38 +222,40 @@ class Fraction(object): exp = Expression([self._num, coef1, op.mul, self._denom, coef1, op.mul, op.div, number._num, coef2, op.mul, number._denom, coef2, op.mul, op.div,op.sub]) - with Expression.tmp_render(): - steps = list(exp.simplify()) - - return steps + ini_step = Expression(self.postfix_tokens) - Expression(number.postfix_tokens) + ans = exp.simplify() + ans.steps = [ini_step] + ans.steps + return ans def __rsub__(self, other): if other == 0: - return [self] + return copy(self) number = self.convert2fraction(other) return number - self def __neg__(self): - """ overload - (as arity 1 operator + """ overload - (as arity 1 operator) >>> f = Fraction(1, 2) >>> -f - [< Fraction -1 / 2>] + < Fraction -1 / 2> + >>> (-f).steps + [] >>> f = Fraction(1, -2) >>> f < Fraction 1 / -2> >>> -f - [< Fraction -1 / -2>, < Fraction 1 / 2>] + < Fraction 1 / 2> + >>> (-f).steps + [< [-1, -2, '/'] >] """ f = Fraction(-self._num, self._denom) + ans = f.simplify() - with Expression.tmp_render(): - steps = [f] + f.simplify() - - return steps + return ans def __mul__(self, other): """ overload * @@ -228,21 +263,29 @@ class Fraction(object): >>> f = Fraction(1, 2) >>> g = Fraction(2, 3) >>> f*g - [< Expression [1, 1, 2, '*', '*', 1, 2, '*', 3, '*', '/']>, < Expression [1, 2, '*', 2, 3, '*', '/']>, < Expression [2, 6, '/']>, < Expression [1, 2, '*', 3, 2, '*', '/']>, < Fraction 1 / 3>] + < Fraction 1 / 3> + >>> (f*g).steps + [< [1, 2, '/', 2, 3, '/', '*'] >, [1, 1, 2, '*', '*', 1, 2, '*', 3, '*', '/'], [1, 2, '*', 2, 3, '*', '/'], [2, 6, '/'], < [2, 6, '/'] >, < [1, 2, '*', 3, 2, '*', '/'] >] >>> f * 0 - [0] + 0 + >>> (f*0).steps + [] >>> f*1 - [< Fraction 1 / 2>] + < Fraction 1 / 2> + >>> (f*1).steps + [] >>> f*4 - [< Expression [1, 2, '*', 2, '*', 1, 2, '*', '/']>, < Expression [2, 2, '*', 2, '/']>, < Expression [4, 2, '/']>, 2] + 2 + >>> (f*4).steps + [< [1, 2, '/', 4, '*'] >, [1, 2, '*', 2, '*', 1, 2, '*', '/'], [2, 2, '*', 2, '/'], [4, 2, '/']] """ steps = [] if other == 0: - return [0] + return Expression([0]) elif other == 1: - return [self] + return copy(self) # TODO: Changer dans le cas où il y a trop de 1 |dim. déc. 28 10:44:10 CET 2014 @@ -256,6 +299,7 @@ class Fraction(object): denom = [self._denom] exp = Expression(num + denom + [op.div]) + ini_step = Expression(self.postfix_tokens) * Expression([other]) else: number = self.convert2fraction(other) @@ -279,28 +323,45 @@ class Fraction(object): exp = Expression(num1 + num2 + [ op.mul] + denom1 + denom2 + [op.mul, op.div]) - with Expression.tmp_render(): - steps = list(exp.simplify()) - - return steps + ini_step = Expression(self.postfix_tokens) * Expression(number.postfix_tokens) + ans = exp.simplify() + ans.steps = [ini_step] + ans.steps + return ans def __rmul__(self, other): return self * other def __truediv__(self, other): + """ overload / + + >>> f = Fraction(1,2) + >>> g = Fraction(3,4) + >>> f / 0 + Traceback (most recent call last): + ... + ZeroDivisionError: division by zero + >>> f / 1 + < Fraction 1 / 2> + >>> (f/1).steps + [] + >>> f / g + < Fraction 2 / 3> + + """ if other == 0: raise ZeroDivisionError("division by zero") elif other == 1: - return [self] + return copy(self) number = self.convert2fraction(other) - steps = [] - number = Fraction(number._denom, number._num) - steps.append(Expression([self, number, op.mul])) - steps += self * number + ini_step = Expression(self.postfix_tokens) / Expression(number.postfix_tokens) - return steps + number = Fraction(number._denom, number._num) + ans = self * number + + ans.steps = [ini_step] + ans.steps + return ans def __rtruediv__(self, other): number = self.convert2fraction(other) @@ -312,44 +373,51 @@ class Fraction(object): >>> f = Fraction(3, 4) >>> f**0 - [1] + 1 + >>> (f**0).steps + [] >>> f**1 - [< Fraction 3 / 4>] + < Fraction 3 / 4> + >>> (f**1).steps + [] >>> f**3 - [< Expression [3, 3, '^', 4, 3, '^', '/']>, < Expression [27, 64, '/']>, < Fraction 27 / 64>] + < Fraction 27 / 64> + >>> (f**3).steps + [< [3, 4, '/', 3, '^'] >, [3, 3, '^', 4, 3, '^', '/'], [27, 64, '/'], [27, 64, '/']] >>> f = Fraction(6, 4) >>> f**3 - [< Expression [6, 3, '^', 4, 3, '^', '/']>, < Expression [216, 64, '/']>, < Expression [27, 8, '*', 8, 8, '*', '/']>, < Fraction 27 / 8>] + < Fraction 27 / 8> + >>> (f**3).steps + [< [6, 4, '/', 3, '^'] >, [6, 3, '^', 4, 3, '^', '/'], [216, 64, '/'], < [216, 64, '/'] >, < [27, 8, '*', 8, 8, '*', '/'] >] """ if not type(power) == int: raise ValueError("Can't raise fraction to power {}".format(str(power))) if power == 0: - return [1] + return Expression([1]) elif power == 1: - return [self] + return copy(self) else: + ini_step = Expression(self.postfix_tokens) ** power exp = Expression([self._num, power, op.pw, self._denom, power, op.pw, op.div]) - with Expression.tmp_render(): - steps = list(exp.simplify()) - return steps + + ans = exp.simplify() + ans.steps = [ini_step] + ans.steps + return ans def __xor__(self, power): """ overload ^ + Work like ** >>> f = Fraction(3, 4) >>> f^0 - [1] + 1 >>> f^1 - [< Fraction 3 / 4>] + < Fraction 3 / 4> >>> f^3 - [< Expression [3, 3, '^', 4, 3, '^', '/']>, < Expression [27, 64, '/']>, < Fraction 27 / 64>] - >>> f = Fraction(6, 4) - >>> f^3 - [< Expression [6, 3, '^', 4, 3, '^', '/']>, < Expression [216, 64, '/']>, < Expression [27, 8, '*', 8, 8, '*', '/']>, < Fraction 27 / 8>] - + < Fraction 27 / 64> """ return self.__pow__(power) @@ -382,17 +450,22 @@ class Fraction(object): """ >= """ return float(self) >= float(other) + def __copy__(self): + """ Copying the fraction removing steps where it is from """ + return Fraction(self._num, self._denom) + if __name__ == '__main__': - #f = Fraction(1, 12) - #g = Fraction(1, 12) - #h = Fraction(1,-5) - #t = Fraction(10,3) - #print("---------") - #print("1 + ", str(h)) - #for i in (1 + h): - # print(i) + f = Fraction(1, 12) + g = Fraction(6, 12) + for i in g.simplify().explain(): + print("g = ",i) + h = Fraction(1,-5) + t = Fraction(10,3) + print("---------") + for i in (0 + h).explain(): + print('0 + h = ',i) #print("---------") #print(str(f) , "+", str(t)) #for i in (f + t): From 03499439dd0996552e912f5db64bf0df3f864a1f Mon Sep 17 00:00:00 2001 From: Lafrite Date: Fri, 27 Feb 2015 16:56:43 +0100 Subject: [PATCH 10/26] rdiv -> rtruediv in expression --- pymath/expression.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pymath/expression.py b/pymath/expression.py index 00e63af..4d1bc2a 100644 --- a/pymath/expression.py +++ b/pymath/expression.py @@ -320,10 +320,11 @@ class Expression(Explicable, Renderable): >>> c = a / b >>> print(c.postfix_tokens) [1, 2, '+', 3, 4, '+', '/'] + >>> """ return self.operate(other, op.div) - def __rdiv__(self, other): + def __rtruediv__(self, other): return self.roperate(other, op.div) def __pow__(self, other): From c2bb98ab40437da4b42a9129995c21303f37e594 Mon Sep 17 00:00:00 2001 From: Lafrite Date: Fri, 27 Feb 2015 16:57:13 +0100 Subject: [PATCH 11/26] Finish adapting test_fraction with explain --- test/test_fraction.py | 57 ++++--------------------------------------- 1 file changed, 5 insertions(+), 52 deletions(-) diff --git a/test/test_fraction.py b/test/test_fraction.py index 3ed27f9..b631d9e 100644 --- a/test/test_fraction.py +++ b/test/test_fraction.py @@ -22,43 +22,21 @@ class TestFraction(unittest.TestCase): ans = [[Fraction(2, 3), 1, Fraction(17, 15), 0, 0, Fraction(4,3)], \ [Fraction(4,3), Fraction(5,3), Fraction(9,5), Fraction(2,3), Fraction(2,3), 2] \ ] - # TODO: Bug pour 1 + 1/-3 |sam. févr. 22 07:01:29 CET 2014 for (i, f1) in enumerate(self.listFrom): for (j, f2) in enumerate(self.listAgainst): res = f1 + f2 - - #print("-----------") - #print("f1 : ", f1) - #print("f2 : ", f2) - #print(res) - - # On est obligé de faire ça pour gérer le cas de 1+1 qui ne passe pas par la classe Fraction - if type(res) == list: - self.assertEqual(res[-1], ans[i][j]) - else: - self.assertEqual(res, ans[i][j]) + self.assertEqual(res, ans[i][j]) def test_sub(self): ans = [[0, Fraction(-1,3), Fraction(-7, 15), Fraction(2,3), Fraction(2,3), Fraction(-2,3)], \ [Fraction(2,3), Fraction(1,3), Fraction(1,5), Fraction(4,3), Fraction(4,3), 0] \ ] - # TODO: bug pour 1 - 1/-3 |sam. févr. 22 07:05:15 CET 2014 for (i, f1) in enumerate(self.listFrom): for (j, f2) in enumerate(self.listAgainst): res = f1 - f2 - - #print("-----------") - #print("f1 : ", f1) - #print("f2 : ", f2) - #print(res) - - # On est obligé de faire ça pour gérer le cas de 1-1 qui ne passe pas par la classe Fraction - if type(res) == list: - self.assertEqual(res[-1], ans[i][j]) - else: - self.assertEqual(res, ans[i][j]) + self.assertEqual(res, ans[i][j]) def test_neg(self): ans = [ Fraction(-1,3), \ @@ -70,10 +48,7 @@ class TestFraction(unittest.TestCase): ] for (j, f) in enumerate(self.listAgainst): res = -f - if type(res) == list: - self.assertEqual(res[-1], ans[j]) - else: - self.assertEqual(res, ans[j]) + self.assertEqual(res, ans[j]) def test_mul(self): ans = [[Fraction(1, 9), Fraction(2,9), Fraction(4, 15), Fraction(-1,9), Fraction(-1,9), Fraction(1,3)], \ @@ -83,17 +58,7 @@ class TestFraction(unittest.TestCase): for (i, f1) in enumerate(self.listFrom): for (j, f2) in enumerate(self.listAgainst): res = f1 * f2 - - #print("-----------") - #print("f1 : ", f1) - #print("f2 : ", f2) - #print(res) - - # On est obligé de faire ça pour gérer le cas de 1*1 qui ne passe pas par la classe Fraction - if type(res) == list: - self.assertEqual(res[-1], ans[i][j]) - else: - self.assertEqual(res, ans[i][j]) + self.assertEqual(res, ans[i][j]) def test_truediv(self): ans = [[1, Fraction(1,2), Fraction(5, 12), -1, -1, Fraction(1,3)], \ @@ -103,17 +68,7 @@ class TestFraction(unittest.TestCase): for (i, f1) in enumerate(self.listFrom): for (j, f2) in enumerate(self.listAgainst): res = f1 / f2 - - #print("-----------") - #print("f1 : ", f1) - #print("f2 : ", f2) - #print(res) - - # On est obligé de faire ça pour gérer le cas de 1/1 qui ne passe pas par la classe Fraction - if type(res) == list: - self.assertEqual(res[-1], ans[i][j]) - else: - self.assertEqual(res, ans[i][j]) + self.assertEqual(res, ans[i][j]) def test_lt(self): pass @@ -130,8 +85,6 @@ class TestFraction(unittest.TestCase): f = Fraction(2, 3) ans = "2 / 3" self.assertEqual(f.__txt__(), ans) - - if __name__ == '__main__': unittest.main() From 506045a6704b1e5e06d9e332355cf0f5249e22b5 Mon Sep 17 00:00:00 2001 From: Lafrite Date: Fri, 27 Feb 2015 17:46:16 +0100 Subject: [PATCH 12/26] Solve bug with context manager --- pymath/explicable.py | 53 +++++++++++++++++++++++- pymath/expression.py | 97 ++++++++++++++++---------------------------- pymath/fraction.py | 4 +- 3 files changed, 90 insertions(+), 64 deletions(-) diff --git a/pymath/explicable.py b/pymath/explicable.py index e6b6f0d..5fe56e0 100644 --- a/pymath/explicable.py +++ b/pymath/explicable.py @@ -1,7 +1,58 @@ #!/usr/bin/env python # encoding: utf-8 -class Explicable(object): +from .render import txt, tex + +class Renderable(object): + STR_RENDER = tex + DEFAULT_RENDER = tex + + @classmethod + def set_render(cls, render): + cls.STR_RENDER = render + + @classmethod + def get_render(cls): + return cls.STR_RENDER + + @classmethod + def set_default_render(cls): + cls.set_render(cls.DEFAULT_RENDER) + + @classmethod + def tmp_render(cls, render = tex): + """ Create a container in which Expression render is temporary modify + + The default temporary render is Expression in order to perform calculus inside numbers + + >>> exp = Expression("2*3/5") + >>> print(exp) + 2 \\times \\frac{ 3 }{ 5 } + >>> for i in exp.simplify().explain(): + ... print(i) + 2 \\times \\frac{ 3 }{ 5 } + \\frac{ 6 }{ 5 } + >>> with Expression.tmp_render(txt): + ... for i in exp.simplify().explain(): + ... print(i) + 2 * 3 / 5 + 6 / 5 + >>> for i in exp.simplify().explain(): + ... print(i) + 2 \\times \\frac{ 3 }{ 5 } + \\frac{ 6 }{ 5 } + + """ + class TmpRenderEnv(object): + def __enter__(self): + self.old_render = Renderable.get_render() + Renderable.set_render(render) + + def __exit__(self, type, value, traceback): + Renderable.set_render(self.old_render) + return TmpRenderEnv() + +class Explicable(Renderable): """ An Explicable object is an object which can be explicable! diff --git a/pymath/expression.py b/pymath/expression.py index 4d1bc2a..1dc4fe0 100644 --- a/pymath/expression.py +++ b/pymath/expression.py @@ -4,71 +4,14 @@ from .generic import Stack, flatten_list, expand_list, isNumber, isOperator, isNumerand from .str2tokens import str2tokens from .operator import op -from .render import txt, tex from .explicable import Explicable from .random_expression import RdExpression -__all__ = ['Expression', 'Renderable'] +__all__ = ['Expression'] -class Renderable(object): - STR_RENDER = tex - DEFAULT_RENDER = tex - @classmethod - def set_render(cls, render): - cls.STR_RENDER = render - - @classmethod - def get_render(cls): - return cls.STR_RENDER - - @classmethod - def set_default_render(cls): - cls.set_render(cls.DEFAULT_RENDER) - - @classmethod - def tmp_render(cls, render = lambda _,x:Expression(x)): - """ Create a container in which Expression render is temporary modify - - The default temporary render is Expression in order to perform calculus inside numbers - - >>> exp = Expression("2*3/5") - >>> print(exp) - \\frac{ 2 \\times 3 }{ 5 } - >>> for i in exp.simplify().explain(): - ... print(i) - \\frac{ 2 \\times 3 }{ 5 } - \\frac{ 6 }{ 5 } - >>> with Expression.tmp_render(): - ... for i in exp.simplify().explain(): - ... i - < Expression [2, 3, '*', 5, '/']> - < Expression [6, 5, '/']> - < Fraction 6 / 5> - - >>> with Expression.tmp_render(txt): - ... for i in exp.simplify().explain(): - ... print(i) - 2 * 3 / 5 - 6 / 5 - >>> for i in exp.simplify().explain(): - ... print(i) - \\frac{ 2 \\times 3 }{ 5 } - \\frac{ 6 }{ 5 } - - """ - class TmpRenderEnv(object): - def __enter__(self): - self.old_render = Expression.get_render() - Expression.set_render(render) - - def __exit__(self, type, value, traceback): - Expression.set_render(self.old_render) - return TmpRenderEnv() - - -class Expression(Explicable, Renderable): +class Expression(Explicable): """A calculus expression. Today it can andle only expression with numbers later it will be able to manipulate unknown""" @@ -85,6 +28,38 @@ class Expression(Explicable, Renderable): random_generator = RdExpression(form, conditions) return Expression(random_generator(val_min, val_max)) + @classmethod + def tmp_render(cls, render = lambda _,x:Expression(x)): + """ Same ad tmp_render for Renderable but default render is Expression + + >>> exp = Expression("2*3/5") + >>> print(exp) + 2 \\times \\frac{ 3 }{ 5 } + >>> for i in exp.simplify().explain(): + ... print(i) + 2 \\times \\frac{ 3 }{ 5 } + \\frac{ 6 }{ 5 } + >>> with Expression.tmp_render(): + ... for i in exp.simplify().explain(): + ... i + < [2, 3, 5, '/', '*'] > + < [2, < Fraction 3 / 5>, '*'] > + < [2, < Fraction 3 / 5>, '*'] > + < [6, 5, '/'] > + >>> from .render import txt + >>> with Expression.tmp_render(txt): + ... for i in exp.simplify().explain(): + ... print(i) + 2 * 3 / 5 + 6 / 5 + >>> for i in exp.simplify().explain(): + ... print(i) + 2 \\times \\frac{ 3 }{ 5 } + \\frac{ 6 }{ 5 } + + """ + return super(Expression, cls).tmp_render(render) + def __new__(cls, exp): """Create Expression objects @@ -109,7 +84,7 @@ class Expression(Explicable, Renderable): simplify = lambda x:x is_number = True methods_attr = {'simplify':simplify, 'isNumber': is_number, 'postfix_tokens': [token]} - fake_token = type('fake_int', (int,Explicable, Renderable), methods_attr)(token) + fake_token = type('fake_int', (int,Explicable, ), methods_attr)(token) return fake_token elif type(token) == str: @@ -118,7 +93,7 @@ class Expression(Explicable, Renderable): simplify = lambda x:[x] is_polynom = True methods_attr = {'simplify':simplify, '_isPolynom': is_polynom, 'postfix_tokens': [token]} - fake_token = type('fake_str', (str,Explicable, Renderable), methods_attr)(token) + fake_token = type('fake_str', (str,Explicable, ), methods_attr)(token) return fake_token else: diff --git a/pymath/fraction.py b/pymath/fraction.py index 9cf4c91..ae01747 100644 --- a/pymath/fraction.py +++ b/pymath/fraction.py @@ -4,7 +4,7 @@ from .arithmetic import gcd from .generic import isNumber from .operator import op -from .expression import Expression, Renderable +from .expression import Expression from .explicable import Explicable from .render import txt, tex from copy import copy @@ -12,7 +12,7 @@ from copy import copy __all__ = ['Fraction'] -class Fraction(Explicable, Renderable): +class Fraction(Explicable): """Fractions!""" def __init__(self, num, denom = 1): From e2e3287420365fd2de9e8c6b3985d7ae4b1133ca Mon Sep 17 00:00:00 2001 From: Lafrite Date: Fri, 27 Feb 2015 18:02:27 +0100 Subject: [PATCH 13/26] explan accept Polynom --- pymath/explicable.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pymath/explicable.py b/pymath/explicable.py index 5fe56e0..360235d 100644 --- a/pymath/explicable.py +++ b/pymath/explicable.py @@ -70,7 +70,10 @@ class Explicable(Renderable): # les étapes pour l'atteindre try: for s in self.steps: - new_s = self.STR_RENDER(s) + if hasattr(s, 'postfix_tokens'): + new_s = self.STR_RENDER(s.postfix_tokens) + else: + new_s = self.STR_RENDER(s) if new_s != old_s: old_s = new_s yield new_s From ce94c27430ff129f23153ce9f8087bbde04ee92f Mon Sep 17 00:00:00 2001 From: Lafrite Date: Fri, 27 Feb 2015 18:04:50 +0100 Subject: [PATCH 14/26] Reduce for polynoms works --- pymath/polynom.py | 84 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 57 insertions(+), 27 deletions(-) diff --git a/pymath/polynom.py b/pymath/polynom.py index 0c1bc1b..52b5aad 100644 --- a/pymath/polynom.py +++ b/pymath/polynom.py @@ -3,6 +3,7 @@ from .expression import Expression +from .explicable import Explicable from .operator import op from .generic import spe_zip, expand_list, isNumber, transpose_fill, flatten_list, isPolynom from .render import txt @@ -25,7 +26,7 @@ def power_cache(fun): return poly_powered return cached_fun -class Polynom(object): +class Polynom(Explicable): """Docstring for Polynom. """ @@ -100,9 +101,9 @@ class Polynom(object): """ if isNumber(value): - postfix_exp = [value if i==self._letter else i for i in self.postfix] + postfix_exp = [value if i==self._letter else i for i in self.postfix_tokens] else: - postfix_exp = [Expression(value) if i==self._letter else i for i in self.postfix] + postfix_exp = [Expression(value) if i==self._letter else i for i in self.postfix_tokens] return Expression(postfix_exp) @@ -147,11 +148,17 @@ class Polynom(object): return 0 def __str__(self): - return str(Expression(self.postfix)) + return str(Expression(self.postfix_tokens)) def __repr__(self): return "< Polynom " + str(self._coef) + ">" + def __txt__(self): + return self.postfix_tokens + + def __tex__(self): + return self.postfix_tokens + def coef_postfix(self, a, i): """Return the postfix display of a coeficient @@ -188,34 +195,34 @@ class Polynom(object): return ans @property - def postfix(self): + def postfix_tokens(self): """Return the postfix form of the polynom :returns: the postfix list of polynom's tokens >>> p = Polynom([1, 2]) - >>> p.postfix + >>> p.postfix_tokens [2, 'x', '*', 1, '+'] >>> p = Polynom([1, -2]) - >>> p.postfix + >>> p.postfix_tokens [2, 'x', '*', '-', 1, '+'] >>> p = Polynom([1,2,3]) - >>> p.postfix + >>> p.postfix_tokens [3, 'x', 2, '^', '*', 2, 'x', '*', '+', 1, '+'] >>> p = Polynom([1,[2,3]]) - >>> p.postfix + >>> p.postfix_tokens [2, 'x', '*', 3, 'x', '*', '+', 1, '+'] >>> p = Polynom([1,[2,-3]]) - >>> p.postfix + >>> p.postfix_tokens [2, 'x', '*', 3, 'x', '*', '-', 1, '+'] >>> p = Polynom([1,[-2,-3]]) - >>> p.postfix + >>> p.postfix_tokens [2, 'x', '*', '-', 3, 'x', '*', '-', 1, '+'] >>> from pymath.expression import Expression >>> from pymath.operator import op >>> e = Expression([2,3,op.add]) >>> p = Polynom([1,e]) - >>> p.postfix + >>> p.postfix_tokens [2, 3, '+', 'x', '*', 1, '+'] """ @@ -298,8 +305,22 @@ class Polynom(object): """Compute coefficients which have same degree :returns: new Polynom with numbers coefficients + + >>> P = Polynom([1,2,3]) + >>> Q = P.reduce() + >>> Q + < Polynom [1, 2, 3]> + >>> Q.steps() + [] + >>> P = Polynom([[1,2], [3,4,5], 6]) + >>> Q = P.reduce() + >>> Q + < Polynom [3, 12, 6]> + >>> Q.steps """ - steps = [] + + # TODO: It doesn't not compute quick enough |ven. févr. 27 18:04:01 CET 2015 + # gather steps for every coeficients coefs_steps = [] for coef in self._coef: @@ -311,20 +332,20 @@ class Polynom(object): coef_exp = Expression(postfix_add) with Expression.tmp_render(): - coef_steps = list(coef_exp.simplify()) + coef_steps = list(coef_exp.simplify().explain()) #print('\t 1.coef_steps -> ', coef_steps) elif type(coef) == Expression: with Expression.tmp_render(): - coef_steps = list(coef.simplify()) + coef_steps = list(coef.simplify().explain()) #print('\t 2.coef_steps -> ', coef_steps) else: try: - coef_steps += coef.simplify() + coef_steps += coef.simplify().explaine() except AttributeError: coef_steps = [coef] @@ -335,9 +356,12 @@ class Polynom(object): #print('\t coefs_steps -> ', coefs_steps) # On retourne la matrice - ans = [] + steps = [] for coefs in transpose_fill(coefs_steps): - ans.append(Polynom(coefs, self._letter)) + steps.append(Polynom(coefs, self._letter)) + + ans, steps = steps[-1], steps[:-1] + ans.steps = steps return ans @@ -513,19 +537,19 @@ def test(p,q): print(p, "+", q) for i in (p + q): #print(repr(i)) - #print("\t", str(i.postfix)) + #print("\t", str(i.postfix_tokens)) print(i) print("\n Moins ------") for i in (p - q): #print(repr(i)) - #print("\t", str(i.postfix)) + #print("\t", str(i.postfix_tokens)) print(i) print("\n Multiplier ------") for i in (p * q): #print(repr(i)) - #print("\t", str(i.postfix)) + #print("\t", str(i.postfix_tokens)) print(i) print("\n Evaluer p ------") @@ -541,14 +565,20 @@ if __name__ == '__main__': #from .fraction import Fraction with Expression.tmp_render(txt): p = Polynom([10, -5]) - q = Polynom([3, -9]) - print(p-q) - for i in p-q: - print(i) + print(p.reduce()) + q = Polynom([[1,2], [3,4,5], 6]) + print("q = ", q) + r = q.reduce() + print("r = ", r) + for i in r.explain(): + print("q = ", i) + #print(p-q) + #for i in p-q: + # print(i) - import doctest - doctest.testmod() + #import doctest + #doctest.testmod() # ----------------------------- From 96dd6824eea453c0514bc51207a39a6c09f087f5 Mon Sep 17 00:00:00 2001 From: Lafrite Date: Fri, 27 Feb 2015 18:05:07 +0100 Subject: [PATCH 15/26] name mistake in fraction --- pymath/fraction.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pymath/fraction.py b/pymath/fraction.py index ae01747..dd61d59 100644 --- a/pymath/fraction.py +++ b/pymath/fraction.py @@ -101,7 +101,6 @@ class Fraction(Explicable): return "< Fraction {num} / {denom}>".format(num=self._num, denom = self._denom) def __txt__(self): - # TODO: À simplifier je ne comprends plus le pourquoi du comment de cette méthode. |ven. févr. 27 09:21:49 CET 2015 old_render = Expression.get_render() Expression.set_render(txt) _txt = self.__str__() @@ -112,10 +111,10 @@ class Fraction(Explicable): def __tex__(self): old_render = Expression.get_render() Expression.set_render(tex) - _txt = self.__str__() + _tex = self.__str__() Expression.set_render(old_render) - return _txt + return _tex def __float__(self): return self._num / self._denom From 6e0d36f4110137355109614401ff62fe61c5bc87 Mon Sep 17 00:00:00 2001 From: Lafrite Date: Fri, 27 Feb 2015 18:58:01 +0100 Subject: [PATCH 16/26] Reduce __add__ __sub__ __neg__ for Polynom --- pymath/polynom.py | 75 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 64 insertions(+), 11 deletions(-) diff --git a/pymath/polynom.py b/pymath/polynom.py index 52b5aad..70843b6 100644 --- a/pymath/polynom.py +++ b/pymath/polynom.py @@ -209,6 +209,12 @@ class Polynom(Explicable): >>> p = Polynom([1,2,3]) >>> p.postfix_tokens [3, 'x', 2, '^', '*', 2, 'x', '*', '+', 1, '+'] + >>> p = Polynom([1]) + >>> p.postfix_tokens + [1] + >>> p = Polynom([0]) + >>> p.postfix_tokens + [0] >>> p = Polynom([1,[2,3]]) >>> p.postfix_tokens [2, 'x', '*', 3, 'x', '*', '+', 1, '+'] @@ -226,6 +232,8 @@ class Polynom(Explicable): [2, 3, '+', 'x', '*', 1, '+'] """ + if self == 0: + return [0] # TODO: Faudrait factoriser un peu tout ça..! |dim. déc. 21 16:02:34 CET 2014 postfix = [] for (i,a) in list(enumerate(self._coef))[::-1]: @@ -293,7 +301,15 @@ class Polynom(Explicable): return flatten_list(postfix) def conv2poly(self, other): - """Convert anything number into a polynom""" + """Convert anything number into a polynom + + >>> P = Polynom([1,2,3]) + >>> P.conv2poly(1) + < Polynom [1]> + >>> P.conv2poly(0) + < Polynom [0]> + + """ if isNumber(other) and not isPolynom(other): return Polynom([other], letter = self._letter) elif isPolynom(other): @@ -317,6 +333,7 @@ class Polynom(Explicable): >>> Q < Polynom [3, 12, 6]> >>> Q.steps + [< Polynom [< [1, 2, '+'] >, < [3, 4, '+', 5, '+'] >, 6]>, < Polynom [< [1, 2, '+'] >, < [7, 5, '+'] >, 6]>, < Polynom [3, < [7, 5, '+'] >, 6]>] """ # TODO: It doesn't not compute quick enough |ven. févr. 27 18:04:01 CET 2015 @@ -399,28 +416,64 @@ class Polynom(Explicable): return 0 def __add__(self, other): - steps = [] + """ Overload + + >>> P = Polynom([1,2,3]) + >>> Q = Polynom([4,5]) + >>> R = P+Q + >>> R + < Polynom [5, 7, 3]> + >>> R.steps + [< [3, 'x', 2, '^', '*', 2, 'x', '*', '+', 1, '+', 5, 'x', '*', 4, '+', '+'] >, < Polynom [< [1, 4, '+'] >, < [2, 5, '+'] >, 3]>, < Polynom [< [1, 4, '+'] >, < [2, 5, '+'] >, 3]>] + """ o_poly = self.conv2poly(other) n_coef = spe_zip(self._coef, o_poly._coef) p = Polynom(n_coef, letter = self._letter) - steps.append(p) - steps += p.simplify() - return steps + ini_step = [Expression(self.postfix_tokens) + Expression(o_poly.postfix_tokens)] + ans = p.simplify() + ans.steps = ini_step + ans.steps + return ans def __radd__(self, other): return self.__add__(other) def __neg__(self): - return Polynom([-i for i in self._coef], letter = self._letter) + """ overload - (as arity 1 operator) + + >>> P = Polynom([1,2,3]) + >>> Q = -P + >>> Q + < Polynom [-1, -2, -3]> + >>> Q.steps + [< [3, 'x', 2, '^', '*', 2, 'x', '*', '+', 1, '+', '-'] >] + """ + ini_step = [- Expression(self.postfix_tokens)] + ans = Polynom([-i for i in self._coef], letter = self._letter).simplify() + ans.steps = ini_step + ans.steps + return ans def __sub__(self, other): - o_poly = self.conv2poly(other) - o_poly = -o_poly + """ overload - - return self.__add__(o_poly) + >>> P = Polynom([1,2,3]) + >>> Q = Polynom([4,5,6]) + >>> R = P - Q + >>> R + < Polynom [-3, -3, -3]> + >>> R.steps + [< [3, 'x', 2, '^', '*', 2, 'x', '*', '+', 1, '+', 6, 'x', 2, '^', '*', 5, 'x', '*', '+', 4, '+', '-'] >, < [3, 'x', 2, '^', '*', 2, 'x', '*', '+', 1, '+', 6, 'x', 2, '^', '*', '-', 5, 'x', '*', '-', 4, '-', '+'] >, < Polynom [< [1, -4, '+'] >, < [2, -5, '+'] >, < [3, -6, '+'] >]>, < Polynom [< [1, -4, '+'] >, < [2, -5, '+'] >, < [3, -6, '+'] >]>] + """ + o_poly = self.conv2poly(other) + ini_step = [Expression(self.postfix_tokens) - Expression(o_poly.postfix_tokens)] + o_poly = -o_poly + #ini_step += o_poly.steps + + ans = self + o_poly + ans.steps = ini_step + ans.steps + + return ans def __rsub__(self, other): o_poly = self.conv2poly(other) @@ -577,8 +630,8 @@ if __name__ == '__main__': # print(i) - #import doctest - #doctest.testmod() + import doctest + doctest.testmod() # ----------------------------- From 8e63c64a6585ebf4f89dcf474aef7ae0731cc999 Mon Sep 17 00:00:00 2001 From: Lafrite Date: Fri, 27 Feb 2015 19:24:27 +0100 Subject: [PATCH 17/26] __mul__ for poly still __pow__ and an issue with random --- pymath/polynom.py | 71 +++++++++++++++++++++++++++-------------------- 1 file changed, 41 insertions(+), 30 deletions(-) diff --git a/pymath/polynom.py b/pymath/polynom.py index 70843b6..4a4e07e 100644 --- a/pymath/polynom.py +++ b/pymath/polynom.py @@ -42,13 +42,13 @@ class Polynom(Explicable): /!\ variables need to be in brackets {} >>> Polynom.random(["{b}", "{a}"]) # doctest:+ELLIPSIS - ... + < Polynom ... >>> Polynom.random(degree = 2) # doctest:+ELLIPSIS - ... + < Polynom ... >>> Polynom.random(degree = 2, conditions=["{b**2-4*a*c}>0"]) # Polynom deg 2 with positive Delta (ax^2 + bx + c) - ... + < Polynom ... >>> Polynom.random(["{c}", "{b}", "{a}"], conditions=["{b**2-4*a*c}>0"]) # Same as above - ... + < Polynom ... """ if (degree > 0 and degree < 26): @@ -326,7 +326,7 @@ class Polynom(Explicable): >>> Q = P.reduce() >>> Q < Polynom [1, 2, 3]> - >>> Q.steps() + >>> Q.steps [] >>> P = Polynom([[1,2], [3,4,5], 6]) >>> Q = P.reduce() @@ -437,7 +437,8 @@ class Polynom(Explicable): return ans def __radd__(self, other): - return self.__add__(other) + o_poly = self.conv2poly(other) + return o_poly.__add__(self) def __neg__(self): """ overload - (as arity 1 operator) @@ -478,27 +479,35 @@ class Polynom(Explicable): def __rsub__(self, other): o_poly = self.conv2poly(other) - return o_poly.__sub__(-self) + return o_poly.__sub__(self) def __mul__(self, other): """ Overload * >>> p = Polynom([1,2]) >>> p*3 - [< Polynom [3, < Expression [2, 3, '*']>]>, < Polynom [3, < Expression [2, 3, '*']>]>, < Polynom [3, 6]>] + < Polynom [3, 6]> + >>> (p*3).steps + [[< [2, 'x', '*', 1, '+', 3, '*'] >], < Polynom [3, < [2, 3, '*'] >]>, < Polynom [3, < [2, 3, '*'] >]>] >>> q = Polynom([0,0,4]) >>> q*3 - [< Polynom [0, 0, < Expression [4, 3, '*']>]>, < Polynom [0, 0, < Expression [4, 3, '*']>]>, < Polynom [0, 0, 12]>] + < Polynom [0, 0, 12]> + >>> (q*3).steps + [[< [4, 'x', 2, '^', '*', 3, '*'] >], < Polynom [0, 0, < [4, 3, '*'] >]>, < Polynom [0, 0, < [4, 3, '*'] >]>] >>> r = Polynom([0,1]) >>> r*3 - [< Polynom [0, 3]>, < Polynom [0, 3]>] + < Polynom [0, 3]> + >>> (r*3).steps + [[< ['x', 3, '*'] >]] >>> p*q - [< Polynom [0, 0, 4, < Expression [2, 4, '*']>]>, < Polynom [0, 0, 4, < Expression [2, 4, '*']>]>, < Polynom [0, 0, 4, 8]>] + < Polynom [0, 0, 4, 8]> + >>> (p*q).steps + [[< [2, 'x', '*', 1, '+', 4, 'x', 2, '^', '*', '*'] >], < Polynom [0, 0, 4, < [2, 4, '*'] >]>, < Polynom [0, 0, 4, < [2, 4, '*'] >]>] >>> p*r - [< Polynom [0, 1, 2]>, < Polynom [0, 1, 2]>] + < Polynom [0, 1, 2]> """ - steps = [] + # TODO: Je trouve qu'elle grille trop d'étapes... |ven. févr. 27 19:08:44 CET 2015 o_poly = self.conv2poly(other) coefs = [] @@ -521,13 +530,11 @@ class Polynom(Explicable): coefs.append(elem) p = Polynom(coefs, letter = self._letter) - steps.append(p) + ini_step = [Expression(self.postfix_tokens + o_poly.postfix_tokens + [op.mul])] + ans = p.simplify() - steps += p.simplify() - - #print("steps -> \n", "\n".join(["\t {}".format(s.postfix) for s in steps])) - - return steps + ans.steps = [ini_step] + ans.steps + return ans def __rmul__(self, other): o_poly = self.conv2poly(other) @@ -617,17 +624,21 @@ def test(p,q): if __name__ == '__main__': #from .fraction import Fraction with Expression.tmp_render(txt): - p = Polynom([10, -5]) - print(p.reduce()) - q = Polynom([[1,2], [3,4,5], 6]) - print("q = ", q) - r = q.reduce() - print("r = ", r) - for i in r.explain(): - print("q = ", i) - #print(p-q) - #for i in p-q: - # print(i) + p = Polynom([1,2,3]) + q = Polynom([0, 2]) + for i in (p*q).explain(): + print(i) + r = Polynom([0,1]) + for i in (r*3).explain(): + print(i) + # print("q = ", q) + # r = q.reduce() + # print("r = ", r) + # for i in r.explain(): + # print("q = ", i) + # print(p-q) + # for i in p-q: + # print(i) import doctest From 9e212e5add6d157c2e47df6b22351aba0b8483f2 Mon Sep 17 00:00:00 2001 From: Lafrite Date: Fri, 27 Feb 2015 22:16:18 +0100 Subject: [PATCH 18/26] power done for Polynom --- pymath/polynom.py | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/pymath/polynom.py b/pymath/polynom.py index 4a4e07e..0d7c712 100644 --- a/pymath/polynom.py +++ b/pymath/polynom.py @@ -9,6 +9,7 @@ from .generic import spe_zip, expand_list, isNumber, transpose_fill, flatten_lis from .render import txt from .random_expression import RdExpression from itertools import chain +from functools import wraps __all__ = ["Polynom"] @@ -16,6 +17,7 @@ __all__ = ["Polynom"] def power_cache(fun): """Decorator which cache calculated powers of polynoms """ cache = {} + @wraps(fun) def cached_fun(self, power): #print("cache -> ", cache) if (tuple(self._coef), power) in cache.keys(): @@ -83,6 +85,7 @@ class Polynom(Explicable): >>> Polynom([1, 2, 3], "y")._letter 'y' """ + super(Polynom, self).__init__() self.feed_coef(coef) self._letter = letter @@ -547,39 +550,42 @@ class Polynom(Explicable): >>> p = Polynom([0,0,3]) >>> p**2 - [< Polynom [0, 0, 0, 0, < Expression [3, 2, '^']>]>, < Polynom [0, 0, 0, 0, < Expression [3, 2, '^']>]>, < Polynom [0, 0, 0, 0, 9]>, < Polynom [0, 0, 0, 0, 9]>] + < Polynom [0, 0, 0, 0, 9]> + >>> (p**2).steps + [< [3, 'x', 2, '^', '*', 2, '^'] >, < Polynom [0, 0, 0, 0, < [3, 2, '^'] >]>, < Polynom [0, 0, 0, 0, < [3, 2, '^'] >]>] >>> p = Polynom([1,2]) >>> p**2 - [[< Polynom [1, 2]>, < Polynom [1, 2]>, '*'], < Polynom [< Expression [1, 1, '*']>, [< Expression [1, 2, '*']>, < Expression [2, 1, '*']>], < Expression [2, 2, '*']>]>, < Polynom [< Expression [1, 1, '*']>, < Expression [1, 2, '*', 2, 1, '*', '+']>, < Expression [2, 2, '*']>]>, < Polynom [1, < Expression [2, 2, '+']>, 4]>, < Polynom [1, 4, 4]>] + < Polynom [1, 4, 4]> + >>> (p**2).steps + [< [2, 'x', '*', 1, '+', 2, '^'] >, [< [2, 'x', '*', 1, '+', 2, 'x', '*', 1, '+', '*'] >], < Polynom [1, < [2, 2, '+'] >, < [2, 2, '*'] >]>, < Polynom [1, < [2, 2, '+'] >, < [2, 2, '*'] >]>] >>> p = Polynom([0,0,1]) >>> p**3 - [< Polynom [0, 0, 0, 0, 0, 0, 1]>] - + < Polynom [0, 0, 0, 0, 0, 0, 1]> """ if not type(power): raise ValueError("Can't raise Polynom to {} power".format(str(power))) - steps = [] + ini_step = [Expression(self.postfix_tokens) ^ power] if self.is_monom(): if self._coef[self.degree] == 1: coefs = [0]*self.degree*power + [1] p = Polynom(coefs, letter = self._letter) - steps.append(p) + ans = p else: coefs = [0]*self.degree*power + [Expression([self._coef[self.degree] , power, op.pw])] p = Polynom(coefs, letter = self._letter) - steps.append(p) - - steps += p.simplify() + ans = p.simplify() else: if power == 2: - return [[self, self, op.mul]] + self * self + ans = self * self else: + # TODO: faudrait changer ça c'est pas très sérieux |ven. févr. 27 22:08:00 CET 2015 raise AttributeError("__pw__ not implemented yet when power is greatter than 2") - return steps + ans.steps = ini_step + ans.steps + return ans def __xor__(self, power): return self.__pow__(power) From 9370e8c48be8f09b0732190d86c2c8187287f0c5 Mon Sep 17 00:00:00 2001 From: Lafrite Date: Fri, 27 Feb 2015 22:26:53 +0100 Subject: [PATCH 19/26] solve bug with random and degree --- pymath/polynom.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/pymath/polynom.py b/pymath/polynom.py index 0d7c712..1a79843 100644 --- a/pymath/polynom.py +++ b/pymath/polynom.py @@ -56,7 +56,7 @@ class Polynom(Explicable): if (degree > 0 and degree < 26): # Générer assez de lettre pour les coefs coefs_name = map(chr, range(97, 98+degree)) - coefs_form = ["{" + i + "}" for i in coefs_name].reverse() + coefs_form = ["{" + i + "}" for i in coefs_name][::-1] form = str(coefs_form) # On créé les valeurs toutes concaténées dans un string @@ -629,14 +629,14 @@ def test(p,q): if __name__ == '__main__': #from .fraction import Fraction - with Expression.tmp_render(txt): - p = Polynom([1,2,3]) - q = Polynom([0, 2]) - for i in (p*q).explain(): - print(i) - r = Polynom([0,1]) - for i in (r*3).explain(): - print(i) + # with Expression.tmp_render(txt): + # p = Polynom([1,2,3]) + # q = Polynom([0, 2]) + # for i in (p*q).explain(): + # print(i) + # r = Polynom([0,1]) + # for i in (r*3).explain(): + # print(i) # print("q = ", q) # r = q.reduce() # print("r = ", r) @@ -645,10 +645,11 @@ if __name__ == '__main__': # print(p-q) # for i in p-q: # print(i) + Polynom.random(degree = 2, conditions=["{b**2-4*a*c}>0"]) # Polynom deg 2 with positive Delta (ax^2 + bx + c) import doctest - doctest.testmod() + doctest.testmod(optionflags=doctest.ELLIPSIS) # ----------------------------- From 79033c74ce73f50eac056003178927543ab270e6 Mon Sep 17 00:00:00 2001 From: Lafrite Date: Sat, 28 Feb 2015 22:52:28 +0100 Subject: [PATCH 20/26] Adapt test_polynom for explain still bug with eval_poly --- test/test_polynom.py | 74 ++++++++++++++++++++++++-------------------- 1 file changed, 41 insertions(+), 33 deletions(-) diff --git a/test/test_polynom.py b/test/test_polynom.py index 33ba472..8c1cdf3 100644 --- a/test/test_polynom.py +++ b/test/test_polynom.py @@ -33,19 +33,20 @@ class TestPolynom(unittest.TestCase): def test_eval_base(self): p = Polynom([1, 2]) - self.assertEqual(p(3).simplified(), 7) + self.assertEqual(p(3).simplify(), 7) def test_eval_const(self): p = Polynom([1]) - self.assertEqual(p(3).simplified(), 1) + self.assertEqual(p(3).simplify(), 1) def test_eval_const_neg(self): p = Polynom([-1]) - self.assertEqual(p(3).simplified(), -1) + self.assertEqual(p(3).simplify(), -1) def test_eval_poly(self): p = Polynom([1, 2]) - self.assertEqual(p("1+h").simplified(), Polynom([3,2], "h")) + hp1 = Expression("h+1") + self.assertEqual(p(hp1).simplify(), Polynom([3,2], "h")) #def test_print(self): # p = Polynom([1,2,3]) @@ -71,97 +72,104 @@ class TestPolynom(unittest.TestCase): p = Polynom([1,2,3]) #ans = [1, 2, "x", "*", "+", 3, "x", 2, "^", "*", "+"] ans = [3, 'x', 2, '^', '*', 2, 'x', '*', '+', 1, '+'] - self.assertEqual(ans, p.postfix) + self.assertEqual(ans, p.postfix_tokens) def test_postfix_monom(self): p = Polynom([0,2]) ans = [2, "x", "*"] - self.assertEqual(ans, p.postfix) + self.assertEqual(ans, p.postfix_tokens) def test_postfix_0_coef(self): p = Polynom([0,2,0,4]) #ans = [2, "x", "*", 4, "x", 3, "^", "*", "+"] ans = [4, 'x', 3, '^', '*', 2, 'x', '*', '+'] - self.assertEqual(ans, p.postfix) + self.assertEqual(ans, p.postfix_tokens) def test_postfix_1_coef(self): p = Polynom([0,1,1]) #ans = ["x", "x", 2, "^", "+"] ans = ["x", 2, "^", "x", "+"] - self.assertEqual(ans, p.postfix) + self.assertEqual(ans, p.postfix_tokens) def test_postfix_neg_coef(self): # TODO: Choix arbitraire (vis à vis des + et des -) il faudra faire en fonction de render |sam. juin 14 09:45:55 CEST 2014 p = Polynom([-1,-2,-3]) #ans = [-1, -2, "x", "*", "+", -3, "x", 2, "^", "*", "+"] ans = [3, 'x', 2, '^', '*', '-', 2, 'x', '*', '-', 1, '-'] - self.assertEqual(ans, p.postfix) + self.assertEqual(ans, p.postfix_tokens) def test_postfix_multi_coef(self): p = Polynom([1,[2, 3],4]) #ans = [1, 2, "x", "*", "+", 3, "x", "*", "+", 4, "x", 2, "^", "*", "+"] ans = [4, 'x', 2, '^', '*', 2, 'x', '*', '+', 3, 'x', '*', '+', 1, '+'] - self.assertEqual(ans, p.postfix) + self.assertEqual(ans, p.postfix_tokens) def test_postfix_arithm_coef(self): p = Polynom([1,Expression([2, 3, "+"]),4]) #ans = [1, 2, 3, "+", "x", "*", "+", 4, "x", 2, "^", "*", "+"] ans = [4, 'x', 2, '^', '*', 2, 3, '+', 'x', '*', '+', 1, '+'] - self.assertEqual(ans, p.postfix) + self.assertEqual(ans, p.postfix_tokens) - #def test_reduce_nilpo(self): - # p = Polynom([1, 2, 3]) - # self.assertEqual(p, p.reduce()[-1]) + def test_reduce_nilpo(self): + p = Polynom([1, 2, 3]) + self.assertEqual(p, p.reduce()) def test_reduce(self): p = Polynom([1, [2, 3], 4]) - self.assertEqual(str(p.reduce()[-1]),'4 x ^ 2 + 5 x + 1') + ans = '4 x^{ 2 } + 5 x + 1' + self.assertEqual(str(p.reduce()), ans) def test_add_int(self): p = Polynom([1, 2, 3]) - q = (p + 2)[-1] - self.assertEqual(str(q), '3 x ^ 2 + 2 x + 3') + q = p + 2 + ans = '3 x^{ 2 } + 2 x + 3' + self.assertEqual(str(q), ans) 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') + q = p + f + ans = '3 x^{ 2 } + 2 x + \\frac{ 3 }{ 2 }' + self.assertEqual(str(q),ans) 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') + r = p + q + ans = '6 x^{ 2 } + 2 x + 1' + self.assertEqual(str(r), ans) def test_sub_int(self): p = Polynom([1, 2, 3]) - q = (p - 2)[-1] - self.assertEqual(str(q), '3 x ^ 2 + 2 x - 1') + q = p - 2 + ans = '3 x^{ 2 } + 2 x - 1' + self.assertEqual(str(q),ans ) def test_sub_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 + 1 / 2') + q = p - f + ans = '3 x^{ 2 } + 2 x + \\frac{ 1 }{ 2 }' + self.assertEqual(str(q), ans) def test_sub_poly(self): p = Polynom([1, 0, 2]) q = Polynom([0, 2, 3]) - r = (p - q)[-1] - self.assertEqual(str(r), '- x ^ 2 - 2 x + 1') + r = p - q + ans = '- x^{ 2 } - 2 x + 1' + self.assertEqual(str(r), ans) def test_pow_monome(self): p = Polynom([0,-2]) - r = (p**3)[-1] - self.assertEqual(str(r), '- 8 x ^ 3') + r = p**3 + ans = '- 8 x^{ 3 }' + self.assertEqual(str(r), ans) def test_pow2_monome(self): p = Polynom([0,-2]) - r = (p^3)[-1] - self.assertEqual(str(r), '- 8 x ^ 3') + r = p^3 + ans = '- 8 x^{ 3 }' + self.assertEqual(str(r), ans) From 20c9c208366ab402106546f461c2c9c05549c44e Mon Sep 17 00:00:00 2001 From: Lafrite Date: Sat, 28 Feb 2015 23:02:41 +0100 Subject: [PATCH 21/26] Enable to initialize Expression with Expression --- pymath/expression.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pymath/expression.py b/pymath/expression.py index 1dc4fe0..2f63b3d 100644 --- a/pymath/expression.py +++ b/pymath/expression.py @@ -71,6 +71,8 @@ class Expression(Explicable): expression.postfix_tokens = str2tokens(exp) elif type(exp) == list: expression.postfix_tokens = flatten_list([tok.postfix_tokens if Expression.isExpression(tok) else tok for tok in exp]) + elif type(exp) == Expression: + return exp else: raise ValueError("Can't build Expression with {} object".format(type(exp))) @@ -95,7 +97,6 @@ class Expression(Explicable): methods_attr = {'simplify':simplify, '_isPolynom': is_polynom, 'postfix_tokens': [token]} fake_token = type('fake_str', (str,Explicable, ), methods_attr)(token) return fake_token - else: raise ValueError("Unknow type in Expression: {}".format(type(token))) From cba821660a73d660af4bbf34ce06e50af9e03f20 Mon Sep 17 00:00:00 2001 From: Lafrite Date: Sat, 28 Feb 2015 23:08:41 +0100 Subject: [PATCH 22/26] mod all ini_step in fraction --- pymath/fraction.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pymath/fraction.py b/pymath/fraction.py index dd61d59..7a21356 100644 --- a/pymath/fraction.py +++ b/pymath/fraction.py @@ -174,7 +174,7 @@ class Fraction(Explicable): exp = Expression([self._num, coef1, op.mul, self._denom, coef1, op.mul, op.div, number._num, coef2, op.mul, number._denom, coef2, op.mul, op.div,op.add]) - ini_step = Expression(self.postfix_tokens) + Expression(number.postfix_tokens) + ini_step = Expression(self.postfix_tokens + number.postfix_tokens + [op.add]) ans = exp.simplify() ans.steps = [ini_step] + ans.steps return ans @@ -221,7 +221,7 @@ class Fraction(Explicable): exp = Expression([self._num, coef1, op.mul, self._denom, coef1, op.mul, op.div, number._num, coef2, op.mul, number._denom, coef2, op.mul, op.div,op.sub]) - ini_step = Expression(self.postfix_tokens) - Expression(number.postfix_tokens) + ini_step = Expression(self.postfix_tokens + number.postfix_tokens + [op.sub]) ans = exp.simplify() ans.steps = [ini_step] + ans.steps return ans @@ -298,7 +298,7 @@ class Fraction(Explicable): denom = [self._denom] exp = Expression(num + denom + [op.div]) - ini_step = Expression(self.postfix_tokens) * Expression([other]) + ini_step = Expression(self.postfix_tokens + [other, op.mul]) else: number = self.convert2fraction(other) @@ -322,7 +322,7 @@ class Fraction(Explicable): exp = Expression(num1 + num2 + [ op.mul] + denom1 + denom2 + [op.mul, op.div]) - ini_step = Expression(self.postfix_tokens) * Expression(number.postfix_tokens) + ini_step = Expression(self.postfix_tokens + number.postfix_tokens + [op.mul]) ans = exp.simplify() ans.steps = [ini_step] + ans.steps return ans @@ -354,7 +354,7 @@ class Fraction(Explicable): number = self.convert2fraction(other) - ini_step = Expression(self.postfix_tokens) / Expression(number.postfix_tokens) + ini_step = Expression(self.postfix_tokens + number.postfix_tokens + [op.div]) number = Fraction(number._denom, number._num) ans = self * number @@ -398,7 +398,7 @@ class Fraction(Explicable): elif power == 1: return copy(self) else: - ini_step = Expression(self.postfix_tokens) ** power + ini_step = Expression(self.postfix_tokens + [power, op.pw]) exp = Expression([self._num, power, op.pw, self._denom, power, op.pw, op.div]) ans = exp.simplify() From 316b8bf52bcafeda47c1edd6f9a6dcf3b551ddc0 Mon Sep 17 00:00:00 2001 From: Lafrite Date: Sat, 28 Feb 2015 23:11:04 +0100 Subject: [PATCH 23/26] mod all ini_step in Polynom. It solve bug with eval poly --- pymath/polynom.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pymath/polynom.py b/pymath/polynom.py index 1a79843..1355e0d 100644 --- a/pymath/polynom.py +++ b/pymath/polynom.py @@ -434,7 +434,7 @@ class Polynom(Explicable): n_coef = spe_zip(self._coef, o_poly._coef) p = Polynom(n_coef, letter = self._letter) - ini_step = [Expression(self.postfix_tokens) + Expression(o_poly.postfix_tokens)] + ini_step = [Expression(self.postfix_tokens + o_poly.postfix_tokens + [op.add])] ans = p.simplify() ans.steps = ini_step + ans.steps return ans @@ -453,7 +453,7 @@ class Polynom(Explicable): >>> Q.steps [< [3, 'x', 2, '^', '*', 2, 'x', '*', '+', 1, '+', '-'] >] """ - ini_step = [- Expression(self.postfix_tokens)] + ini_step = [Expression(self.postfix_tokens + [op.sub1])] ans = Polynom([-i for i in self._coef], letter = self._letter).simplify() ans.steps = ini_step + ans.steps return ans @@ -470,7 +470,7 @@ class Polynom(Explicable): [< [3, 'x', 2, '^', '*', 2, 'x', '*', '+', 1, '+', 6, 'x', 2, '^', '*', 5, 'x', '*', '+', 4, '+', '-'] >, < [3, 'x', 2, '^', '*', 2, 'x', '*', '+', 1, '+', 6, 'x', 2, '^', '*', '-', 5, 'x', '*', '-', 4, '-', '+'] >, < Polynom [< [1, -4, '+'] >, < [2, -5, '+'] >, < [3, -6, '+'] >]>, < Polynom [< [1, -4, '+'] >, < [2, -5, '+'] >, < [3, -6, '+'] >]>] """ o_poly = self.conv2poly(other) - ini_step = [Expression(self.postfix_tokens) - Expression(o_poly.postfix_tokens)] + ini_step = [Expression(self.postfix_tokens + o_poly.postfix_tokens + [op.sub])] o_poly = -o_poly #ini_step += o_poly.steps @@ -566,7 +566,7 @@ class Polynom(Explicable): if not type(power): raise ValueError("Can't raise Polynom to {} power".format(str(power))) - ini_step = [Expression(self.postfix_tokens) ^ power] + ini_step = [Expression(self.postfix_tokens + [power, op.pw])] if self.is_monom(): if self._coef[self.degree] == 1: From 9ffde06b9c76e36e5e3a86aae2d3291151ff6f42 Mon Sep 17 00:00:00 2001 From: Lafrite Date: Sat, 28 Feb 2015 23:13:51 +0100 Subject: [PATCH 24/26] all are passed :DDD --- pymath/explicable.py | 1 + pymath/expression.py | 8 ++++---- pymath/str2tokens.py | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/pymath/explicable.py b/pymath/explicable.py index 360235d..4db74b3 100644 --- a/pymath/explicable.py +++ b/pymath/explicable.py @@ -25,6 +25,7 @@ class Renderable(object): The default temporary render is Expression in order to perform calculus inside numbers + >>> from .expression import Expression >>> exp = Expression("2*3/5") >>> print(exp) 2 \\times \\frac{ 3 }{ 5 } diff --git a/pymath/expression.py b/pymath/expression.py index 2f63b3d..08ec98b 100644 --- a/pymath/expression.py +++ b/pymath/expression.py @@ -42,10 +42,10 @@ class Expression(Explicable): >>> with Expression.tmp_render(): ... for i in exp.simplify().explain(): ... i - < [2, 3, 5, '/', '*'] > - < [2, < Fraction 3 / 5>, '*'] > - < [2, < Fraction 3 / 5>, '*'] > - < [6, 5, '/'] > + < [2, 3, 5, '/', '*'] > + < [2, < Fraction 3 / 5>, '*'] > + < [2, < Fraction 3 / 5>, '*'] > + < [6, 5, '/'] > >>> from .render import txt >>> with Expression.tmp_render(txt): ... for i in exp.simplify().explain(): diff --git a/pymath/str2tokens.py b/pymath/str2tokens.py index ad340d7..7899a03 100644 --- a/pymath/str2tokens.py +++ b/pymath/str2tokens.py @@ -12,7 +12,7 @@ def str2tokens(exp): >>> str2tokens('2*3+4') [2, 3, '*', 4, '+'] >>> str2tokens('2x+4') - [2, < Polynom [0, 1]>, '*', 1, '+'] + [2, < Polynom [0, 1]>, '*', 4, '+'] """ in_tokens = str2in_tokens(exp) post_tokens = in2post_fix(in_tokens) From eec7c8c2ce058ca5c199c9db1809a9c9038696b9 Mon Sep 17 00:00:00 2001 From: Lafrite Date: Sun, 1 Mar 2015 13:50:22 +0100 Subject: [PATCH 25/26] Add bug about operation between expression and other types --- bugs | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/bugs b/bugs index bd480c0..7504b6f 100644 --- a/bugs +++ b/bugs @@ -33,4 +33,36 @@ \frac{ ( -1 ) \times 2 }{ 3 \times 2 } \frac{ -1 }{ 3 } \frac{ -2 }{ 6 } + +* Opération entre une expression et une fraction ou un Polynom + + In [3]: P = Polynom([1,2,1]) + + In [4]: e = Expression("1+2*3") + + In [5]: e + P + Out[5]: < [1, 2, 3, '*', '+', < Polynom [1, 2, 1]>, '+'] > + + In [6]: P + e + --------------------------------------------------------------------------- + TypeError Traceback (most recent call last) + in () + ----> 1 P + e + + /home/lafrite/scripts/pyMath/pymath/polynom.py in __add__(self, other) + 430 [< [3, 'x', 2, '^', '*', 2, 'x', '*', '+', 1, '+', 5, 'x', '*', 4, '+', '+'] >, < Polynom [< [1, 4, '+'] >, < [2, 5, '+'] >, 3]>, < Polynom [< [1, 4, '+'] >, < [2, 5, '+'] >, 3]>] + 431 """ + --> 432 o_poly = self.conv2poly(other) + 433 + 434 n_coef = spe_zip(self._coef, o_poly._coef) + + /home/lafrite/scripts/pyMath/pymath/polynom.py in conv2poly(self, other) + 319 return other + 320 else: + --> 321 raise ValueError(type(other) + " can't be converted into a polynom") + 322 + 323 def reduce(self): + + TypeError: unsupported operand type(s) for +: 'type' and 'str' + From ed086fce2dc875fc732cea342332db7b8c72c386 Mon Sep 17 00:00:00 2001 From: Lafrite Date: Sun, 1 Mar 2015 14:13:33 +0100 Subject: [PATCH 26/26] Adapte doc to explain --- docs/tutorial.mdwn | 90 +++++++++++++++++++++++----------------------- 1 file changed, 44 insertions(+), 46 deletions(-) diff --git a/docs/tutorial.mdwn b/docs/tutorial.mdwn index f6f09c2..1ed1ae4 100644 --- a/docs/tutorial.mdwn +++ b/docs/tutorial.mdwn @@ -8,7 +8,10 @@ pyMath est un module python qui permet la manipulation d'expressions mathématiq >>> from pymath.expression import Expression >>> ajout_fractions = Expression("2 / 5 + 2 / 3") - >>> for i in ajout_fractions.simpliy(): + >>> resultat = ajout_fractions.simplify() + >>> print(resultat) + \frac{ 16 }{ 15 } + >>> for i in resultat.explain(): ... print(i) ... \frac{ 2 }{ 5 } + \frac{ 2 }{ 3 } @@ -38,16 +41,16 @@ pyMath est un module python qui permet la manipulation d'expressions mathématiq >>> from pymath.expression import Expression >>> ajout_fractions = Expression("2 / 5 + 2 / 3") - >>> for i in ajout_fractions.simpliy(): + >>> for i in ajout_fractions.simpliy().explain(): ... print(i) \frac{ 2 }{ 5 } + \frac{ 2 }{ 3 } \frac{ 2 \times 3 }{ 5 \times 3 } + \frac{ 2 \times 5 }{ 3 \times 5 } \frac{ 6 + 10 }{ 15 } \frac{ 16 }{ 15 } >>> from pymath.render import txt - >>> Expression.set_render(txt) - >>> for i in ajout_fractions.simpliy(): - ... print(i) + >>> with Expression.tmp_render(txt): + ... for i in ajout_fractions.simpliy(): + ... print(i) ... 2 / 5 + 2 / 3 2 * 3 / 5 * 3 + 2 * 5 / 3 * 5 @@ -84,14 +87,18 @@ Et si l'on souhaite un rendu plus adapté à la console: 2 / 5 + 2 / 3 ### Simplification des expressions -Une fois les expressions créées, elles peuvent se réduire en expliquant les étapes et en respectant les règles de priorités. Les exemples suivants seront données avec un rendu texte. +Une fois les expressions créées, elles peuvent se réduire en expliquant les étapes et en respectant les règles de priorités. Ces étapes de calcul sont stockés dans l'objet résultat du calcul et sont accéssibles à travers la méthode *explain*. +Les exemples suivants seront données avec un rendu texte. >>> from pymath.expression import Expression >>> from pymath.renders import txt >>> Expression.set_render(txt) >>> exp = Expression("1 + 2 * 3") - >>> for i in exp.simplify(): + >>> exp_simplifiee = exp.simplify() + >>> print(exp_simplifiee) + 7 + >>> for i in exp_simplifiee.explain(): ... print(i) ... 1 + 2 * 3 @@ -102,7 +109,7 @@ Une fois les expressions créées, elles peuvent se réduire en expliquant les Les opérations autorisées sont les opérations "classique": + - * / ^. L'utilisation des parenthèses est aussi gérée. >>> exp = Expression("1 + 2 / 5") - >>> for i in exp.simplify(): + >>> for i in exp.simplify().explain(): ... print(i) ... 1 + 2 / 5 @@ -111,7 +118,7 @@ Les opérations autorisées sont les opérations "classique": + - * / ^. L'utili 7 / 5 >>> exp = Expression("(2 + 4)(3 - 4 * 2)") - >>> for i in exp.simplify(): + >>> for i in exp.simplify().explain(): ... print(i) ... ( 2 + 4 ) ( 3 - ( 4 * 2 ) ) @@ -126,7 +133,7 @@ On peut vouloir créer directement des objets (fractions ou polynômes) sans pas >>> fraction1 = Fraction(1,2) >>> fraction2 = Fraction(2,3) >>> print(fraction1) - + 1 / 2 >>> from pymath.polynom import Polynom >>> p = Polynom([1,2,3]) >>> print(p) @@ -134,7 +141,7 @@ On peut vouloir créer directement des objets (fractions ou polynômes) sans pas >>> q = Polynom([0,0,1]) x ^ 2 -Contrairement aux opérations sur les Expressions, qui renvoient une nouvelle expression +On peut effectuer des opérations entre les Expressions. >>> fraction_expression = Expression("2 / 3") >>> autre_fraction_expression = Expression("4 / 9") @@ -143,17 +150,19 @@ Contrairement aux opérations sur les Expressions, qui renvoient une nouvelle ex les opérations sur les fractions ou les polynômes renvoient la liste des étapes jusqu'à leur forme simplifiée - >>> fraction1 + fraction2 - [< Expression [1, 3, '*', 2, 3, '*', '/', 2, 2, '*', 3, 2, '*', '/', '+']>, < Expression [3, 4, '+', 6, '/']>, < Fraction 7 / 6>] - >>> for i in (fraction1 + fraction2): + >>> addition_fraction = fraction1 + fraction2 + >>> print(addition_fraction) + 7 / 6 + >>> for i in addition_fraction.explain(): ... print(i) ... 1 * 3 / 2 * 3 + 2 * 2 / 3 * 2 ( 3 + 4 ) / 6 7 / 6 - >>> p + q - [< Polynom [[1, 0], [2, 0], [3, 1]]>, < Polynom [1, 2, < Expression [3, 1, '+']>]>, < Polynom [1, 2, 4]> - >>> for i in (p+q): + >>> r = p + q + >>> print(r) + 4 x ^ 2 + 2 x + 1 + >>> for i in r.explain(): ... print(i) ... 3 x ^ 2 + x ^ 2 + 2 x + 1 @@ -166,7 +175,7 @@ Comme dit dans l'introduction, il y a deux types de rendus: un rendu texte (util Voici un exemple de l'utilisation du rendu latex (par défaut). >>> exp = Expression("1 + 2 / 5") - >>> for i in exp.simplify(): + >>> for i in exp.simplify().explain(): ... print(i) ... 1 + \frac{ 2 }{ 5 } @@ -181,7 +190,7 @@ Voici un exemple d'utilisation du rendu txt >>> from pymath.render import txt >>> Expression.set_render(txt) >>> exp = Expression("1 + 2 / 5") - >>> for i in exp.simplify(): + >>> for i in exp.simplify().explain(): ... print(i) ... 2 / 5 + 2 / 3 @@ -192,33 +201,23 @@ Voici un exemple d'utilisation du rendu txt ## Générer des expressions aléatoirement. -Le module qui permet de générer des expressions aléatoirement est *pymath.random_expression* - - >>> from pymath.random_expression import RdExpression - ### Créer un expression Pour créer une expression il faut au moins une chose: la forme de l'expression. Toutes les lettres entre accolades seront remplacées par des valeurs aléatoires (par défaut entre -10 et 10 et non nulles). >>> form = "2* {a} + 3" - >>> random_expression_generator = RdExpression(form) - >>> generated_expression = random_expression_generator() - >>> type(generated_expression) - pymath.expression.Expression - >>> print(generated_expression) + >>> expression_aleatoire = Expression.random(form) + >>> print(expression_aleatoire) '2 \times 9 + 3' - >>> print(random_expression_generator(30, 40)) + >>> print(Expression.random(form,val_min = 30, val_max = 40)) '2 \times 31 + 3' -On remarque que ici que *random_expression_generator* renvoie une expression. Cela permettra ensuite de simplifier ou manipuler les expressions - On verra plus tard comment se passer de cette classe pour par exemple créer des expressions avec des opérateurs non implémentés dans Expression. - ### Créer une expression avec conditions Parfois il peut être nécessaire d'imposer des conditions sur les éléments à générer pour créer des exercices spécifiques. >>> form = "{a} / {b} + {c} / {d}" >>> conditions = ["abs({b}) != 1", "{d} > 1", "{b} != {d}", "gcd({a},{b}) == 1", "gcd({c},{d}) == 1"] - >>> random_frac_add_generator = RdExpression(form, conditions) - >>> print(random_frac_add_generator()) + >>> addition_fraction_alea = Expression.random(form, conditions) + >>> print(addition_fraction_alea) '\frac{ 4 }{ 5 } + \frac{ 9 }{ 7 }' La méthode pour créer les valeurs avec des conditions est la méthode par rejet. Elle n'est pas très efficace et rien n'est encore prévu dans le cas où aucune valeur n'est possible. @@ -233,23 +232,22 @@ Pour éviter de faire tourner la méthode par rejet trop longtemps, il est possi '\frac{ -9 }{ 7 } + \frac{ 1 }{ 28 } ### Rendu des expressions -On peut vouloir ne pas passer par la classe Expression pour obtenir notre expression (si l'on veut utiliser la racine carré par exemple, ou pour créer n'importe quoi qui ne fait pas intervenir d'expression). Pour cela il va falloir changer la forme du rendu +On peut vouloir ne pas passer par la classe Expression pour obtenir notre expression (si l'on veut utiliser la racine carré par exemple, ou pour créer n'importe quoi qui ne fait pas intervenir d'expression). Ainsi pymath ne gère plus le rendu de l'expression ni son calcul. - >>> RdExpression.set_form("raw") +La fonction qui permet de faire cela est *random_str*: + + >>> from pymath.random_expression import random_str >>> form = "{a} / {b} + {c} / {k*b}" >>> conditions = ["abs({b}) != 1", "{d} > 1", "{b} != {d}", "gcd({a},{b}) == 1", "gcd({c},{k*b}) == 1"] - >>> random_frac_add_generator = RdExpression(form, conditions) - >>> exp = random_frac_add_generator() - >>> type(exp) + >>> str_addition_fraction = random_str(form, conditions) + >>> type(str_addition_fraction) str - >>> print(exp) + >>> print(str_addition_fraction) -2 / 5 + -8 / 35 - >>> form = "{a**2}x^2 + {2*a*b} x + {b**2}" - >>> random_id_rmq_generator = RdExpression(form) - >>> print(random_id_rmq_generator()) - '100x^2 + -180 x + 81' - >>> random_id_rmq_generator() - '49x^2 + 42 x + 9' + >>> form = "A({a},{b}), B({2*a}, {3*b})" + >>> points_alea = random_str(form) + >>> points_alea + 'A(7,5), B(14, 15)' On remarque le défaut d'utiliser cette forme, le rendu est moins bien fait (dans l'exemple, il n'y a pas de parenthèses autour du -8).