From 9649a0dbafcc89448c8078d0a7f3177fd484d8bc Mon Sep 17 00:00:00 2001 From: Lafrite Date: Fri, 23 Jan 2015 17:19:14 +0100 Subject: [PATCH 01/59] start working on deg 2 polynoms --- pymath/polynomDeg2.py | 65 ++++++++++++++++++++++++++++++++++++++++ test/test_polynomDeg2.py | 31 +++++++++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 pymath/polynomDeg2.py create mode 100644 test/test_polynomDeg2.py diff --git a/pymath/polynomDeg2.py b/pymath/polynomDeg2.py new file mode 100644 index 0000000..d17c762 --- /dev/null +++ b/pymath/polynomDeg2.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python +# encoding: utf-8 + +from .polynom import Polynom +from .expression import Expression +from .operator import op +from math import sqrt + + +class Polynom_deg2(Polynom): + + """ Degree 2 polynoms + Child of Polynom with some extro tools + """ + + def __init__(self, coefs = [0, 0, 1], letter = "x"): + """@todo: to be defined1. """ + Polynom.__init__(self, coefs, letter) + + @property + def a(self): + return self._coef[2] + + @property + def b(self): + return self._coef[1] + + @property + def c(self): + return self._coef[0] + + @property + def delta(self): + """Compute the discriminant expression + :returns: discriminant expression + + """ + return Expression([self.b, 2, op.pw, 4, self.a, self.c, op.mul, op.mul, op.sub]) + + def roots(self): + """Compute roots of the polynom + """ + if self.delta > 0: + roots = [(-self.b - sqrt(self.delta) + + + + +if __name__ == '__main__': + from .render import txt + with Expression.tmp_render(txt): + P = Polynom_deg2([2, 3, 4]) + print(P) + + print("Delta") + for i in P.delta.simplify(): + print(i) + + + + +# ----------------------------- +# Reglages pour 'vim' +# vim:set autoindent expandtab tabstop=4 shiftwidth=4: +# cursor: 16 del diff --git a/test/test_polynomDeg2.py b/test/test_polynomDeg2.py new file mode 100644 index 0000000..e32a79a --- /dev/null +++ b/test/test_polynomDeg2.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python +# encoding: utf-8 + + +import unittest + +from pymath.polynomDeg2 import Polynom_deg2 + + + +class TestPolynomDeg2(unittest.TestCase): + """Testing functions from pymath.polynomDeg2""" + + pass + + +if __name__ == '__main__': + unittest.main() + + + + + + + + +# ----------------------------- +# Reglages pour 'vim' +# vim:set autoindent expandtab tabstop=4 shiftwidth=4: +# cursor: 16 del + From 324dfab81777cdebfea35ca7f0cb9d6a44aec5a0 Mon Sep 17 00:00:00 2001 From: Lafrite Date: Wed, 25 Feb 2015 09:18:18 +0100 Subject: [PATCH 02/59] roots and start tbl_sgn --- pymath/polynomDeg2.py | 100 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 89 insertions(+), 11 deletions(-) diff --git a/pymath/polynomDeg2.py b/pymath/polynomDeg2.py index d17c762..793acd5 100644 --- a/pymath/polynomDeg2.py +++ b/pymath/polynomDeg2.py @@ -10,11 +10,15 @@ from math import sqrt class Polynom_deg2(Polynom): """ Degree 2 polynoms - Child of Polynom with some extro tools + Child of Polynom with some extra tools """ def __init__(self, coefs = [0, 0, 1], letter = "x"): """@todo: to be defined1. """ + if len(coefs) < 3 or len(coefs) > 4: + raise ValueError("Polynom_deg2 have to be degree 2 polynoms, they need 3 coefficients, {} are given".format(len(coefs))) + if coefs[2] == 0: + raise ValueError("Polynom_deg2 have to be degree 2 polynoms, coefficient of x^2 can't be 0") Polynom.__init__(self, coefs, letter) @property @@ -34,27 +38,101 @@ class Polynom_deg2(Polynom): """Compute the discriminant expression :returns: discriminant expression + >>> P = Polynom_deg2([1,2,3]) + >>> P.delta + < Expression [2, 2, '^', 4, 3, 1, '*', '*', '-']> + >>> for i in P.delta.simplify(): + print(i) + 2^{ 2 } - 4 \times 3 \times 1 + 4 - 4 \times 3 + 4 - 12 + -8 + >>> P.delta.simplified() + -8 """ + return Expression([self.b, 2, op.pw, 4, self.a, self.c, op.mul, op.mul, op.sub]) def roots(self): - """Compute roots of the polynom + """ Compute roots of the polynom + + /!\ Can't manage exact solution because of pymath does not handle sqare root yet + + # TODO: Pymath has to know how to compute with sqare root |mar. févr. 24 18:40:04 CET 2015 + + >>> P = Polynom_deg2([1, 1, 1]) + >>> P.roots() + [] + >>> P = Polynom_deg2([1, 2, 1]) + >>> P.roots() + [-1.0] + >>> P = Polynom_deg2([-1, 0, 1]) + >>> P.roots() + [-1.0, 1.0] """ - if self.delta > 0: - roots = [(-self.b - sqrt(self.delta) + if self.delta.simplified() > 0: + self.roots = [(-self.b - sqrt(self.delta.simplified()))/(2*self.a), (-self.b + sqrt(self.delta.simplified()))/(2*self.a)] + elif self.delta.simplified() == 0: + self.roots = [-self.b /(2*self.a)] + else: + self.roots = [] + return self.roots + + def tbl_sgn(self): + """ Return the sign line for tkzTabLine + + >>> P = Polynom_deg2([2, 5, 2]) + >>> P.tbl_sgn() + '\\tkzTabLine{, +, z, -, z , +,}' + >>> P = Polynom_deg2([2, 1, -2]) + >>> P.tbl_sgn() + '\\tkzTabLine{, -, z, +, z , -,}' + >>> P = Polynom_deg2([1, 2, 1]) + >>> P.tbl_sgn() + '\\tkzTabLine{, +, z, +,}' + >>> P = Polynom_deg2([0, 0, -2]) + >>> P.tbl_sgn() + '\\tkzTabLine{, -, z, -,}' + >>> P = Polynom_deg2([1, 0, 1]) + >>> P.tbl_sgn() + '\\tkzTabLine{, +,}' + >>> P = Polynom_deg2([-1, 0, -1]) + >>> P.tbl_sgn() + '\\tkzTabLine{, -,}' + """ + if self.delta.simplified() > 0: + if self.a > 0: + return "\\tkzTabLine{, +, z, -, z , +,}" + else: + return "\\tkzTabLine{, -, z, +, z , -,}" + elif self.delta.simplified() == 0: + if self.a > 0: + return "\\tkzTabLine{, +, z, +,}" + else: + return "\\tkzTabLine{, -, z, -,}" + else: + if self.a > 0: + return "\\tkzTabLine{, +,}" + else: + return "\\tkzTabLine{, -,}" + + if __name__ == '__main__': - from .render import txt - with Expression.tmp_render(txt): - P = Polynom_deg2([2, 3, 4]) - print(P) + # from .render import txt + # with Expression.tmp_render(txt): + # P = Polynom_deg2([2, 3, 4]) + # print(P) - print("Delta") - for i in P.delta.simplify(): - print(i) + # print("Delta") + # for i in P.delta.simplify(): + # print(i) + + import doctest + doctest.testmod() From 6a605eea19ff040b292df42ed97ac666d83bfb80 Mon Sep 17 00:00:00 2001 From: Lafrite Date: Wed, 25 Feb 2015 09:50:06 +0100 Subject: [PATCH 03/59] Add remark about tbl sgn and varia --- TODO | 1 + 1 file changed, 1 insertion(+) diff --git a/TODO b/TODO index 375517a..7b2f402 100644 --- a/TODO +++ b/TODO @@ -7,3 +7,4 @@ * Expression should be able to simplify expression with ":" * Expression parents class and his children: Numerical_exp, toGenerate_exp and formal expression +* Create tbl sgn and variation render From 720eb37a4fe041ba196c92a2a31b0ab98ff3b12c Mon Sep 17 00:00:00 2001 From: Lafrite Date: Wed, 25 Feb 2015 10:17:01 +0100 Subject: [PATCH 04/59] 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 de338e914f07471f913e32df145dcaefeb92bd93 Mon Sep 17 00:00:00 2001 From: Lafrite Date: Wed, 25 Feb 2015 10:23:24 +0100 Subject: [PATCH 05/59] Add alpha and beta method to polynomDeg2 --- pymath/polynomDeg2.py | 97 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 80 insertions(+), 17 deletions(-) diff --git a/pymath/polynomDeg2.py b/pymath/polynomDeg2.py index 793acd5..797a0c9 100644 --- a/pymath/polynomDeg2.py +++ b/pymath/polynomDeg2.py @@ -14,7 +14,6 @@ class Polynom_deg2(Polynom): """ def __init__(self, coefs = [0, 0, 1], letter = "x"): - """@todo: to be defined1. """ if len(coefs) < 3 or len(coefs) > 4: raise ValueError("Polynom_deg2 have to be degree 2 polynoms, they need 3 coefficients, {} are given".format(len(coefs))) if coefs[2] == 0: @@ -42,9 +41,9 @@ class Polynom_deg2(Polynom): >>> P.delta < Expression [2, 2, '^', 4, 3, 1, '*', '*', '-']> >>> for i in P.delta.simplify(): - print(i) - 2^{ 2 } - 4 \times 3 \times 1 - 4 - 4 \times 3 + ... print(i) + 2^{ 2 } - 4 \\times 3 \\times 1 + 4 - 4 \\times 3 4 - 12 -8 >>> P.delta.simplified() @@ -53,6 +52,58 @@ class Polynom_deg2(Polynom): return Expression([self.b, 2, op.pw, 4, self.a, self.c, op.mul, op.mul, op.sub]) + @property + def alpha(self): + """ Compute alpha the abcisse of the extremum + + >>> P = Polynom_deg2([1,2,3]) + >>> P.alpha + < Expression [2, '-', 2, 3, '*', '/']> + >>> 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 } + >>> P.alpha.simplified() # Bug avec les fractions ici, on devrait avoir -1/3 pas -2/6... + < Fraction -2 / 6 > + + """ + return Expression([self.b, op.sub1, 2, self.a, op.mul, op.div]) + + @property + def beta(self): + """ Compute beta the extremum of self + + >>> P = Polynom_deg2([1,2,3]) + >>> P.beta + < Expression [3, < Fraction -2 / 6>, 2, '^', '*', 2, < Fraction -2 / 6>, '*', '+', 1, '+']> + >>> for i in P.beta.simplify(): # Ça serait bien que l'on puisse enlever des étapes maintenant... + ... print(i) + 3 \times \frac{ -2 }{ 6 }^{ 2 } + 2 \times \frac{ -2 }{ 6 } + 1 + 3 \times \frac{ ( -2 )^{ 2 } }{ 6^{ 2 } } + \frac{ ( -2 ) \times 1 \times 2 }{ 3 \times 2 } + 1 + 3 \times \frac{ 4 }{ 36 } + \frac{ ( -2 ) \times 2 }{ 6 } + 1 + 3 \times \frac{ 1 \times 4 }{ 9 \times 4 } + \frac{ -4 }{ 6 } + 1 + 3 \times \frac{ 1 }{ 9 } + \frac{ ( -2 ) \times 2 }{ 3 \times 2 } + 1 + 3 \times \frac{ 1 }{ 9 } + \frac{ -2 }{ 3 } + 1 + \frac{ 1 \times 1 \times 3 }{ 3 \times 3 } + \frac{ -2 }{ 3 } + 1 + \frac{ 1 \times 3 }{ 9 } + \frac{ -2 }{ 3 } + 1 + \frac{ 3 }{ 9 } + \frac{ -2 }{ 3 } + 1 + \frac{ 1 \times 3 }{ 3 \times 3 } + \frac{ -2 }{ 3 } + 1 + \frac{ 1 }{ 3 } + \frac{ -2 }{ 3 } + 1 + \frac{ 1 + ( -2 ) }{ 3 } + 1 + \frac{ -1 }{ 3 } + 1 + \frac{ ( -1 ) \times 1 }{ 3 \times 1 } + \frac{ 1 \times 3 }{ 1 \times 3 } + \frac{ -1 }{ 3 } + \frac{ 3 }{ 3 } + \frac{ ( -1 ) + 3 }{ 3 } + \frac{ 2 }{ 3 } + >>> P.beta.simplified() + < Fraction 2 / 3> + + """ + return self(self.alpha.simplified()) + def roots(self): """ Compute roots of the polynom @@ -82,23 +133,23 @@ class Polynom_deg2(Polynom): """ Return the sign line for tkzTabLine >>> P = Polynom_deg2([2, 5, 2]) - >>> P.tbl_sgn() - '\\tkzTabLine{, +, z, -, z , +,}' + >>> print(P.tbl_sgn()) + \\tkzTabLine{, +, z, -, z , +,} >>> P = Polynom_deg2([2, 1, -2]) - >>> P.tbl_sgn() - '\\tkzTabLine{, -, z, +, z , -,}' + >>> print(P.tbl_sgn()) + \\tkzTabLine{, -, z, +, z , -,} >>> P = Polynom_deg2([1, 2, 1]) - >>> P.tbl_sgn() - '\\tkzTabLine{, +, z, +,}' + >>> print(P.tbl_sgn()) + \\tkzTabLine{, +, z, +,} >>> P = Polynom_deg2([0, 0, -2]) - >>> P.tbl_sgn() - '\\tkzTabLine{, -, z, -,}' + >>> print(P.tbl_sgn()) + \\tkzTabLine{, -, z, -,} >>> P = Polynom_deg2([1, 0, 1]) - >>> P.tbl_sgn() - '\\tkzTabLine{, +,}' + >>> print(P.tbl_sgn()) + \\tkzTabLine{, +,} >>> P = Polynom_deg2([-1, 0, -1]) - >>> P.tbl_sgn() - '\\tkzTabLine{, -,}' + >>> print(P.tbl_sgn()) + \\tkzTabLine{, -,} """ if self.delta.simplified() > 0: if self.a > 0: @@ -116,8 +167,20 @@ class Polynom_deg2(Polynom): else: return "\\tkzTabLine{, -,}" - + def tbl_variation(self, limit = False): + """Return the variation line for tkzTabVar + :param limit: Display or not limits in tabular + + >>> P = Polynom_deg2([1,1,1]) + + """ + alpha = -self.b / (2*self.a) + beta = self(alpha).simplied() + + + +#\tkzTabVar{-/{}, +/{$f(-10)$}, -/{}} From c21509657aaab2f69f129f9984f6bf35fd62a182 Mon Sep 17 00:00:00 2001 From: Lafrite Date: Wed, 25 Feb 2015 10:32:27 +0100 Subject: [PATCH 06/59] tbl_variation for Polynom_deg2 --- pymath/polynomDeg2.py | 58 ++++++++++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 23 deletions(-) diff --git a/pymath/polynomDeg2.py b/pymath/polynomDeg2.py index 797a0c9..7b1f9f4 100644 --- a/pymath/polynomDeg2.py +++ b/pymath/polynomDeg2.py @@ -67,7 +67,7 @@ class Polynom_deg2(Polynom): \\frac{ -1 }{ 3 } \\frac{ -2 }{ 6 } >>> P.alpha.simplified() # Bug avec les fractions ici, on devrait avoir -1/3 pas -2/6... - < Fraction -2 / 6 > + < Fraction -2 / 6> """ return Expression([self.b, op.sub1, 2, self.a, op.mul, op.div]) @@ -81,23 +81,23 @@ class Polynom_deg2(Polynom): < Expression [3, < Fraction -2 / 6>, 2, '^', '*', 2, < Fraction -2 / 6>, '*', '+', 1, '+']> >>> for i in P.beta.simplify(): # Ça serait bien que l'on puisse enlever des étapes maintenant... ... print(i) - 3 \times \frac{ -2 }{ 6 }^{ 2 } + 2 \times \frac{ -2 }{ 6 } + 1 - 3 \times \frac{ ( -2 )^{ 2 } }{ 6^{ 2 } } + \frac{ ( -2 ) \times 1 \times 2 }{ 3 \times 2 } + 1 - 3 \times \frac{ 4 }{ 36 } + \frac{ ( -2 ) \times 2 }{ 6 } + 1 - 3 \times \frac{ 1 \times 4 }{ 9 \times 4 } + \frac{ -4 }{ 6 } + 1 - 3 \times \frac{ 1 }{ 9 } + \frac{ ( -2 ) \times 2 }{ 3 \times 2 } + 1 - 3 \times \frac{ 1 }{ 9 } + \frac{ -2 }{ 3 } + 1 - \frac{ 1 \times 1 \times 3 }{ 3 \times 3 } + \frac{ -2 }{ 3 } + 1 - \frac{ 1 \times 3 }{ 9 } + \frac{ -2 }{ 3 } + 1 - \frac{ 3 }{ 9 } + \frac{ -2 }{ 3 } + 1 - \frac{ 1 \times 3 }{ 3 \times 3 } + \frac{ -2 }{ 3 } + 1 - \frac{ 1 }{ 3 } + \frac{ -2 }{ 3 } + 1 - \frac{ 1 + ( -2 ) }{ 3 } + 1 - \frac{ -1 }{ 3 } + 1 - \frac{ ( -1 ) \times 1 }{ 3 \times 1 } + \frac{ 1 \times 3 }{ 1 \times 3 } - \frac{ -1 }{ 3 } + \frac{ 3 }{ 3 } - \frac{ ( -1 ) + 3 }{ 3 } - \frac{ 2 }{ 3 } + 3 \\times \\frac{ -2 }{ 6 }^{ 2 } + 2 \\times \\frac{ -2 }{ 6 } + 1 + 3 \\times \\frac{ ( -2 )^{ 2 } }{ 6^{ 2 } } + \\frac{ ( -2 ) \\times 1 \\times 2 }{ 3 \\times 2 } + 1 + 3 \\times \\frac{ 4 }{ 36 } + \\frac{ ( -2 ) \\times 2 }{ 6 } + 1 + 3 \\times \\frac{ 1 \\times 4 }{ 9 \\times 4 } + \\frac{ -4 }{ 6 } + 1 + 3 \\times \\frac{ 1 }{ 9 } + \\frac{ ( -2 ) \\times 2 }{ 3 \\times 2 } + 1 + 3 \\times \\frac{ 1 }{ 9 } + \\frac{ -2 }{ 3 } + 1 + \\frac{ 1 \\times 1 \\times 3 }{ 3 \\times 3 } + \\frac{ -2 }{ 3 } + 1 + \\frac{ 1 \\times 3 }{ 9 } + \\frac{ -2 }{ 3 } + 1 + \\frac{ 3 }{ 9 } + \\frac{ -2 }{ 3 } + 1 + \\frac{ 1 \\times 3 }{ 3 \\times 3 } + \\frac{ -2 }{ 3 } + 1 + \\frac{ 1 }{ 3 } + \\frac{ -2 }{ 3 } + 1 + \\frac{ 1 + ( -2 ) }{ 3 } + 1 + \\frac{ -1 }{ 3 } + 1 + \\frac{ ( -1 ) \\times 1 }{ 3 \\times 1 } + \\frac{ 1 \\times 3 }{ 1 \\times 3 } + \\frac{ -1 }{ 3 } + \\frac{ 3 }{ 3 } + \\frac{ ( -1 ) + 3 }{ 3 } + \\frac{ 2 }{ 3 } >>> P.beta.simplified() < Fraction 2 / 3> @@ -167,20 +167,32 @@ class Polynom_deg2(Polynom): else: return "\\tkzTabLine{, -,}" - def tbl_variation(self, limit = False): + def tbl_variation(self, limits = False): """Return the variation line for tkzTabVar :param limit: Display or not limits in tabular - >>> P = Polynom_deg2([1,1,1]) + >>> P = Polynom_deg2([1,2,3]) + >>> print(P.tbl_variation()) + \\tkzTabVar{+/{}, -/{$\\frac{ 2 }{ 3 }$}, +/{}} + >>> print(P.tbl_variation(limits = True)) + \\tkzTabVar{+/{$+\\infty$}, -/{$\\frac{ 2 }{ 3 }$}, +/{$+\\infty$}} """ - alpha = -self.b / (2*self.a) - beta = self(alpha).simplied() + beta = self.beta.simplified() + if limits: + if self.a > 0: + return "\\tkzTabVar{+/{$+\\infty$}, -/{$" + str(beta) + "$}, +/{$+\\infty$}}" + else: + return "\\tkzTabVar{-/{$-\\infty$}, +/{$" + str(beta) + "$}, -/{$-\\infty$}}" + else: + if self.a > 0: + return "\\tkzTabVar{+/{}, -/{$" + str(beta) + "$}, +/{}}" + else: + return "\\tkzTabVar{-/{}, +/{$" + str(beta) + "$}, -/{}}" -#\tkzTabVar{-/{}, +/{$f(-10)$}, -/{}} From 2763e46fabbc7581c3b7b5d8722c0745c2e735af Mon Sep 17 00:00:00 2001 From: Lafrite Date: Wed, 25 Feb 2015 10:17:01 +0100 Subject: [PATCH 07/59] 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 12af21b2a62aa48d33eb666f4b75066ff0ee42ed Mon Sep 17 00:00:00 2001 From: Lafrite Date: Wed, 25 Feb 2015 11:24:48 +0100 Subject: [PATCH 08/59] add less verbose in Todolist --- TODO | 1 + 1 file changed, 1 insertion(+) diff --git a/TODO b/TODO index 375517a..d1c0436 100644 --- a/TODO +++ b/TODO @@ -7,3 +7,4 @@ * Expression should be able to simplify expression with ":" * Expression parents class and his children: Numerical_exp, toGenerate_exp and formal expression +* Make less verbose fractions operations From 26838c7b0ab149a0829603ba1acd4303da3ac8df Mon Sep 17 00:00:00 2001 From: Lafrite Date: Wed, 25 Feb 2015 14:54:44 +0100 Subject: [PATCH 09/59] 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 10/59] 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 11/59] 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 12/59] 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 13/59] 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 14/59] 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 15/59] 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 16/59] 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 17/59] 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 18/59] 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 19/59] 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 20/59] 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 21/59] 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 22/59] 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 23/59] 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 24/59] __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 25/59] 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 26/59] 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 27/59] 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 28/59] 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 29/59] 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 30/59] 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 31/59] 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 32/59] 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 33/59] 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). From b6eccba83696f496735590469b591de417ec649a Mon Sep 17 00:00:00 2001 From: Lafrite Date: Fri, 6 Mar 2015 14:43:09 +0100 Subject: [PATCH 34/59] adapt polynomDeg2 to new explain style --- pymath/polynomDeg2.py | 61 ++++++++++++++----------------------------- 1 file changed, 20 insertions(+), 41 deletions(-) diff --git a/pymath/polynomDeg2.py b/pymath/polynomDeg2.py index 7b1f9f4..1efd025 100644 --- a/pymath/polynomDeg2.py +++ b/pymath/polynomDeg2.py @@ -39,18 +39,16 @@ class Polynom_deg2(Polynom): >>> P = Polynom_deg2([1,2,3]) >>> P.delta - < Expression [2, 2, '^', 4, 3, 1, '*', '*', '-']> - >>> for i in P.delta.simplify(): + -8 + >>> for i in P.delta.explain(): ... print(i) 2^{ 2 } - 4 \\times 3 \\times 1 4 - 4 \\times 3 4 - 12 -8 - >>> P.delta.simplified() - -8 """ - return Expression([self.b, 2, op.pw, 4, self.a, self.c, op.mul, op.mul, op.sub]) + return Expression([self.b, 2, op.pw, 4, self.a, self.c, op.mul, op.mul, op.sub]).simplify() @property def alpha(self): @@ -58,19 +56,15 @@ class Polynom_deg2(Polynom): >>> P = Polynom_deg2([1,2,3]) >>> P.alpha - < Expression [2, '-', 2, 3, '*', '/']> - >>> for i in P.alpha.simplify(): + < Fraction -1 / 3> + >>> for i in P.alpha.explain(): ... print(i) \\frac{ - 2 }{ 2 \\times 3 } \\frac{ -2 }{ 6 } \\frac{ ( -1 ) \\times 2 }{ 3 \\times 2 } \\frac{ -1 }{ 3 } - \\frac{ -2 }{ 6 } - >>> P.alpha.simplified() # Bug avec les fractions ici, on devrait avoir -1/3 pas -2/6... - < Fraction -2 / 6> - """ - return Expression([self.b, op.sub1, 2, self.a, op.mul, op.div]) + return Expression([self.b, op.sub1, 2, self.a, op.mul, op.div]).simplify() @property def beta(self): @@ -78,31 +72,16 @@ class Polynom_deg2(Polynom): >>> P = Polynom_deg2([1,2,3]) >>> P.beta - < Expression [3, < Fraction -2 / 6>, 2, '^', '*', 2, < Fraction -2 / 6>, '*', '+', 1, '+']> - >>> for i in P.beta.simplify(): # Ça serait bien que l'on puisse enlever des étapes maintenant... - ... print(i) - 3 \\times \\frac{ -2 }{ 6 }^{ 2 } + 2 \\times \\frac{ -2 }{ 6 } + 1 - 3 \\times \\frac{ ( -2 )^{ 2 } }{ 6^{ 2 } } + \\frac{ ( -2 ) \\times 1 \\times 2 }{ 3 \\times 2 } + 1 - 3 \\times \\frac{ 4 }{ 36 } + \\frac{ ( -2 ) \\times 2 }{ 6 } + 1 - 3 \\times \\frac{ 1 \\times 4 }{ 9 \\times 4 } + \\frac{ -4 }{ 6 } + 1 - 3 \\times \\frac{ 1 }{ 9 } + \\frac{ ( -2 ) \\times 2 }{ 3 \\times 2 } + 1 - 3 \\times \\frac{ 1 }{ 9 } + \\frac{ -2 }{ 3 } + 1 - \\frac{ 1 \\times 1 \\times 3 }{ 3 \\times 3 } + \\frac{ -2 }{ 3 } + 1 - \\frac{ 1 \\times 3 }{ 9 } + \\frac{ -2 }{ 3 } + 1 - \\frac{ 3 }{ 9 } + \\frac{ -2 }{ 3 } + 1 - \\frac{ 1 \\times 3 }{ 3 \\times 3 } + \\frac{ -2 }{ 3 } + 1 - \\frac{ 1 }{ 3 } + \\frac{ -2 }{ 3 } + 1 - \\frac{ 1 + ( -2 ) }{ 3 } + 1 - \\frac{ -1 }{ 3 } + 1 - \\frac{ ( -1 ) \\times 1 }{ 3 \\times 1 } + \\frac{ 1 \\times 3 }{ 1 \\times 3 } - \\frac{ -1 }{ 3 } + \\frac{ 3 }{ 3 } - \\frac{ ( -1 ) + 3 }{ 3 } - \\frac{ 2 }{ 3 } - >>> P.beta.simplified() < Fraction 2 / 3> - + >>> for i in P.beta.explain(): # Ça serait bien que l'on puisse enlever des étapes maintenant... + ... print(i) + 3 \\times \\frac{ -1 }{ 3 }^{ 2 } + 2 \\times \\frac{ -1 }{ 3 } + 1 + 3 \\times \\frac{ 1 }{ 9 } + \\frac{ -2 }{ 3 } + 1 + \\frac{ 1 }{ 3 } + \\frac{ -2 }{ 3 } + 1 + \\frac{ -1 }{ 3 } + 1 + \\frac{ 2 }{ 3 } """ - return self(self.alpha.simplified()) + return self(self.alpha).simplify() def roots(self): """ Compute roots of the polynom @@ -121,9 +100,9 @@ class Polynom_deg2(Polynom): >>> P.roots() [-1.0, 1.0] """ - if self.delta.simplified() > 0: - self.roots = [(-self.b - sqrt(self.delta.simplified()))/(2*self.a), (-self.b + sqrt(self.delta.simplified()))/(2*self.a)] - elif self.delta.simplified() == 0: + if self.delta > 0: + self.roots = [(-self.b - sqrt(self.delta))/(2*self.a), (-self.b + sqrt(self.delta))/(2*self.a)] + elif self.delta == 0: self.roots = [-self.b /(2*self.a)] else: self.roots = [] @@ -151,12 +130,12 @@ class Polynom_deg2(Polynom): >>> print(P.tbl_sgn()) \\tkzTabLine{, -,} """ - if self.delta.simplified() > 0: + if self.delta > 0: if self.a > 0: return "\\tkzTabLine{, +, z, -, z , +,}" else: return "\\tkzTabLine{, -, z, +, z , -,}" - elif self.delta.simplified() == 0: + elif self.delta == 0: if self.a > 0: return "\\tkzTabLine{, +, z, +,}" else: @@ -179,7 +158,7 @@ class Polynom_deg2(Polynom): \\tkzTabVar{+/{$+\\infty$}, -/{$\\frac{ 2 }{ 3 }$}, +/{$+\\infty$}} """ - beta = self.beta.simplified() + beta = self.beta if limits: if self.a > 0: return "\\tkzTabVar{+/{$+\\infty$}, -/{$" + str(beta) + "$}, +/{$+\\infty$}}" From 0423cca5aa78794efaac4b6d74a894fbbeaecc5f Mon Sep 17 00:00:00 2001 From: Lafrite Date: Fri, 6 Mar 2015 16:51:43 +0100 Subject: [PATCH 35/59] change coef to coefs in Polynom --- pymath/polynom.py | 6 +++--- pymath/polynomDeg2.py | 22 ++++++++++++++++++++++ 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/pymath/polynom.py b/pymath/polynom.py index 1355e0d..51e4ea2 100644 --- a/pymath/polynom.py +++ b/pymath/polynom.py @@ -64,9 +64,9 @@ class Polynom(Explicable): # On "parse" ce string pour créer les coefs coefs = [eval(i) if type(i)==str else i for i in eval(coefs)] # Création du polynom - return Polynom(coef = coefs, letter = letter) + return Polynom(coefs = coefs, letter = letter) - def __init__(self, coef = [1], letter = "x" ): + def __init__(self, coefs = [1], letter = "x" ): """Initiate the polynom :param coef: coefficients of the polynom (ascending degree sorted) @@ -86,7 +86,7 @@ class Polynom(Explicable): 'y' """ super(Polynom, self).__init__() - self.feed_coef(coef) + self.feed_coef(coefs) self._letter = letter diff --git a/pymath/polynomDeg2.py b/pymath/polynomDeg2.py index 1efd025..517a889 100644 --- a/pymath/polynomDeg2.py +++ b/pymath/polynomDeg2.py @@ -4,8 +4,10 @@ from .polynom import Polynom from .expression import Expression from .operator import op +from .random_expression import RdExpression from math import sqrt +__all__ = ["Polynom_deg2"] class Polynom_deg2(Polynom): @@ -13,6 +15,26 @@ class Polynom_deg2(Polynom): Child of Polynom with some extra tools """ + @classmethod + def random(self, coefs_form = ["{c}", "{b}", "{a}"], conditions = [], letter = "x"): + """ Create a 2nd degree poly from coefs_form ans conditions + + :param coefs_form: list of forms (one by coef) (ascending degree sorted) + :param conditions: condition on variables + :param letter: the letter for the polynom + + """ + if len(coefs_form) != 3: + raise ValueError("Polynom_deg2 have to be degree 2 polynoms, they need 3 coefficients, {} are given".format(len(coefs_form))) + + form = str(coefs_form) + # On créé les valeurs toutes concaténées dans un string + coefs = RdExpression(form, conditions)() + # On "parse" ce string pour créer les coefs + coefs = [eval(i) if type(i)==str else i for i in eval(coefs)] + # Création du polynom + return Polynom_deg2(coefs = coefs, letter = letter) + def __init__(self, coefs = [0, 0, 1], letter = "x"): if len(coefs) < 3 or len(coefs) > 4: raise ValueError("Polynom_deg2 have to be degree 2 polynoms, they need 3 coefficients, {} are given".format(len(coefs))) From cd911a5513898d482f4dc6d96e7fc451789e2ca5 Mon Sep 17 00:00:00 2001 From: Lafrite Date: Fri, 6 Mar 2015 17:00:09 +0100 Subject: [PATCH 36/59] derivate polynom --- pymath/polynom.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/pymath/polynom.py b/pymath/polynom.py index 1355e0d..bf39fe2 100644 --- a/pymath/polynom.py +++ b/pymath/polynom.py @@ -385,6 +385,23 @@ class Polynom(Explicable): return ans + def derivate(self): + """ Return the derivated polynom + + >>> P = Polynom([1, 2, 3]) + >>> Q = P.derivate() + >>> Q + < Polynom [2, 6]> + >>> for i in Q.explain(): + ... print(i) + 2 \\times 3 x + 1 \\times 2 + 6 x + 2 + """ + derv_coefs = [] + for (i,c) in enumerate(self._coef): + derv_coefs += [Expression([i, c, op.mul])] + return Polynom(derv_coefs[1:]).simplify() + @staticmethod def postfix_add(numbers): """Convert a list of numbers into a postfix addition From c1c71a4a6aeb6560005899d4586b0c6fd75fbe26 Mon Sep 17 00:00:00 2001 From: Lafrite Date: Fri, 6 Mar 2015 18:13:51 +0100 Subject: [PATCH 37/59] add tbl_sgn_header --- pymath/polynomDeg2.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/pymath/polynomDeg2.py b/pymath/polynomDeg2.py index 517a889..069e837 100644 --- a/pymath/polynomDeg2.py +++ b/pymath/polynomDeg2.py @@ -105,7 +105,7 @@ class Polynom_deg2(Polynom): """ return self(self.alpha).simplify() - def roots(self): + def roots(self, after_coma = 2): """ Compute roots of the polynom /!\ Can't manage exact solution because of pymath does not handle sqare root yet @@ -123,12 +123,21 @@ class Polynom_deg2(Polynom): [-1.0, 1.0] """ if self.delta > 0: - self.roots = [(-self.b - sqrt(self.delta))/(2*self.a), (-self.b + sqrt(self.delta))/(2*self.a)] + self._roots = [round((-self.b - sqrt(self.delta))/(2*self.a),after_coma), round((-self.b + sqrt(self.delta))/(2*self.a),after_coma)] elif self.delta == 0: - self.roots = [-self.b /(2*self.a)] + self._roots = [round(-self.b /(2*self.a), after_coma)] else: - self.roots = [] - return self.roots + self._roots = [] + return self._roots + + def tbl_sgn_header(self): + """ Return header of the sign line for tkzTabLine""" + if self.delta > 0: + return "{$-\\infty$, " + str(min(self.roots())) + " , " + str( max(self.roots())) + " , $+\\infty$}" + elif self.delta == 0: + return "{$-\\infty$, " + str(self.roots()[0]) + " , $+\\infty$}" + else: + return "{$-\\infty$, $+\\infty$}" def tbl_sgn(self): """ Return the sign line for tkzTabLine From c497627df95ad3e1c602b84fee01f653aeb9ac20 Mon Sep 17 00:00:00 2001 From: Lafrite Date: Sat, 7 Mar 2015 06:26:22 +0100 Subject: [PATCH 38/59] Add bugs and todos --- TODO | 3 +++ bugs | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/TODO b/TODO index 7b2f402..2f6262a 100644 --- a/TODO +++ b/TODO @@ -5,6 +5,9 @@ * bug: expression can't handle -(-2) * Overload + - * for expression (DONE ~ no steps yet) * Expression should be able to simplify expression with ":" +* Add name to polynom * Expression parents class and his children: Numerical_exp, toGenerate_exp and formal expression * Create tbl sgn and variation render + + diff --git a/bugs b/bugs index 31ee7b4..b7d7e9e 100644 --- a/bugs +++ b/bugs @@ -1,3 +1,7 @@ +* Soustraction de Polynômes!!!! + + + * Expression importe mal 4x^2 In [9]: e = Expression("3x + 4x^2 - 1") From b3a07bf6e4337fc9dce9405e24789d9d76a0714f Mon Sep 17 00:00:00 2001 From: Lafrite Date: Sat, 7 Mar 2015 09:15:21 +0100 Subject: [PATCH 39/59] adapt test on __call__ for polynom --- test/test_polynom.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/test/test_polynom.py b/test/test_polynom.py index 8c1cdf3..79743a7 100644 --- a/test/test_polynom.py +++ b/test/test_polynom.py @@ -33,20 +33,19 @@ class TestPolynom(unittest.TestCase): def test_eval_base(self): p = Polynom([1, 2]) - self.assertEqual(p(3).simplify(), 7) + self.assertEqual(p(3), 7) def test_eval_const(self): p = Polynom([1]) - self.assertEqual(p(3).simplify(), 1) + self.assertEqual(p(3), 1) def test_eval_const_neg(self): p = Polynom([-1]) - self.assertEqual(p(3).simplify(), -1) + self.assertEqual(p(3), -1) def test_eval_poly(self): p = Polynom([1, 2]) - hp1 = Expression("h+1") - self.assertEqual(p(hp1).simplify(), Polynom([3,2], "h")) + self.assertEqual(p("h+1"), Polynom([3,2], "h")) #def test_print(self): # p = Polynom([1,2,3]) From 6e1783da437d9648897492e9d2dd027a294921eb Mon Sep 17 00:00:00 2001 From: Lafrite Date: Sat, 7 Mar 2015 09:15:38 +0100 Subject: [PATCH 40/59] solve bug with * and ** and polynom of high degree --- pymath/polynom.py | 57 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 39 insertions(+), 18 deletions(-) diff --git a/pymath/polynom.py b/pymath/polynom.py index bf39fe2..ebc9223 100644 --- a/pymath/polynom.py +++ b/pymath/polynom.py @@ -5,7 +5,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 .generic import spe_zip, expand_list, isNumber, transpose_fill, flatten_list, isPolynom, isNumerand from .render import txt from .random_expression import RdExpression from itertools import chain @@ -102,13 +102,27 @@ class Polynom(Explicable): :returns: Expression ready to be simplify + >>> P = Polynom([1, 2, 3]) + >>> P(2) + 17 + >>> for i in P(2).explain(): + ... print(i) + 3 \\times 2^{ 2 } + 2 \\times 2 + 1 + 3 \\times 4 + 4 + 1 + 12 + 4 + 1 + 16 + 1 + 17 + >>> Q = P("1+h") + >>> print(Q) + 3 h^{ 2 } + 8 h + 6 + >>> R = P(Q) """ - if isNumber(value): + if isNumerand(value) or Expression.isExpression(value): 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_tokens] - return Expression(postfix_exp) + return Expression(postfix_exp).simplify() def feed_coef(self, l_coef): """Feed coef of the polynom. Manage differently whether it's a number or an expression @@ -525,12 +539,15 @@ class Polynom(Explicable): [[< [2, 'x', '*', 1, '+', 4, 'x', 2, '^', '*', '*'] >], < Polynom [0, 0, 4, < [2, 4, '*'] >]>, < Polynom [0, 0, 4, < [2, 4, '*'] >]>] >>> p*r < Polynom [0, 1, 2]> - + >>> P = Polynom([1,2,3]) + >>> Q = Polynom([4,5,6]) + >>> P*Q + < Polynom [4, 13, 28, 27, 18]> """ # TODO: Je trouve qu'elle grille trop d'étapes... |ven. févr. 27 19:08:44 CET 2015 o_poly = self.conv2poly(other) - coefs = [] + coefs = [0]*(self.degree + o_poly.degree + 1) for (i,a) in enumerate(self._coef): for (j,b) in enumerate(o_poly._coef): if a == 0 or b == 0: @@ -541,13 +558,14 @@ class Polynom(Explicable): elem = a else: elem = Expression([a, b, op.mul]) - try: - if coefs[i+j]==0: - coefs[i+j] = elem - elif elem != 0: - coefs[i+j] = [coefs[i+j], elem] - except IndexError: - coefs.append(elem) + + if coefs[i+j]==0: + coefs[i+j] = elem + elif elem != 0: + if type(coefs[i+j]) == list: + coefs[i+j] += [elem] + else: + coefs[i+j] = [coefs[i+j] , elem] p = Polynom(coefs, letter = self._letter) ini_step = [Expression(self.postfix_tokens + o_poly.postfix_tokens + [op.mul])] @@ -578,6 +596,9 @@ class Polynom(Explicable): >>> p = Polynom([0,0,1]) >>> p**3 < Polynom [0, 0, 0, 0, 0, 0, 1]> + >>> p = Polynom([1,2,3]) + >>> p**2 + < Polynom [1, 4, 10, 12, 9]> """ if not type(power): @@ -646,11 +667,11 @@ 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) + #with Expression.tmp_render(txt): + # p = Polynom([1, 2, 3]) + # q = Polynom([4, 5, 6]) + # for i in (p*q).explain(): + # print(i) # r = Polynom([0,1]) # for i in (r*3).explain(): # print(i) @@ -662,7 +683,7 @@ 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) + #Polynom.random(degree = 2, conditions=["{b**2-4*a*c}>0"]) # Polynom deg 2 with positive Delta (ax^2 + bx + c) import doctest From 4296242afd7f625efb6e3cf9a7c81ed8a3ac0f80 Mon Sep 17 00:00:00 2001 From: Lafrite Date: Sat, 7 Mar 2015 11:05:15 +0100 Subject: [PATCH 41/59] mod __tex__ and __txt__ for Polynom --- pymath/polynom.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pymath/polynom.py b/pymath/polynom.py index ebc9223..eb31271 100644 --- a/pymath/polynom.py +++ b/pymath/polynom.py @@ -6,7 +6,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, isNumerand -from .render import txt +from .render import txt,tex from .random_expression import RdExpression from itertools import chain from functools import wraps @@ -171,10 +171,10 @@ class Polynom(Explicable): return "< Polynom " + str(self._coef) + ">" def __txt__(self): - return self.postfix_tokens + return txt(self.postfix_tokens) def __tex__(self): - return self.postfix_tokens + return tex(self.postfix_tokens) def coef_postfix(self, a, i): """Return the postfix display of a coeficient From c832ae574231f4edd6faa796a75c8d7beca27a04 Mon Sep 17 00:00:00 2001 From: Lafrite Date: Sat, 7 Mar 2015 15:11:08 +0100 Subject: [PATCH 42/59] Add test with polynom in render --- test/test_render.py | 53 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 45 insertions(+), 8 deletions(-) diff --git a/test/test_render.py b/test/test_render.py index 0dee1dd..b94f995 100644 --- a/test/test_render.py +++ b/test/test_render.py @@ -6,6 +6,7 @@ import unittest from pymath.render import tex, txt,p2i from pymath.fraction import Fraction +from pymath.polynom import Polynom from pymath.operator import op @@ -22,6 +23,10 @@ class TestTexRender(unittest.TestCase): def test_type_render_fraction(self): self.assertEqual(tex([Fraction(1,2)]), "\\frac{ 1 }{ 2 }") + def test_type_render_poly(self): + P = Polynom([1,2,3]) + self.assertEqual(tex([P]), "3 x^{ 2 } + 2 x + 1") + def test_mult_interger(self): exps = [ [2, 3, op.get_op("*", 2)], [2, -3, op.get_op("*", 2)], [-2, 3, op.get_op("*", 2)]] wanted_render = [ "2 \\times 3", "2 \\times ( -3 )", "-2 \\times 3"] @@ -37,20 +42,31 @@ class TestTexRender(unittest.TestCase): self.assertEqual(rend, wanted_render[i]) def test_mult_fraction(self): - exps = [ [2, Fraction(1,2), op.get_op("*", 2)], [Fraction(1,2), 3, op.get_op("*", 2)]] + exps = [ [2, Fraction(1,2), op.mul], [Fraction(1,2), 3, op.mul]] wanted_render = [ "2 \\times \\frac{ 1 }{ 2 }", "\\frac{ 1 }{ 2 } \\times 3"] for (i,e) in enumerate(exps): rend = tex(e) self.assertEqual(rend, wanted_render[i]) - def test_parentheses(self): - mul = op.get_op("*", 2) - add = op.get_op("+", 2) + def test_mult_poly(self): + exps = [[2, Polynom([1,2,3]), op.mul], + [Polynom([1,2,3]), 2, op.mul], + [Polynom([1,2,3]), Polynom([4,5,6]), op.mul], + ] + wanted_render = [ "2 ( 3 x^{ 2 } + 2 x + 1 )", + "( 3 x^{ 2 } + 2 x + 1 ) \\times 2", + "( 3 x^{ 2 } + 2 x + 1 ) ( 3 x^{ 2 } + 2 x + 1 )", + ] + for (i,e) in enumerate(exps): + rend = tex(e) + self.assertEqual(rend, wanted_render[i]) + + def test_parentheses_int(self): exps = [\ - [ 2, 3, add, 4, mul],\ - [ 2, 3, mul, 4, add],\ - [ 2, 3, 4, mul, add],\ - [ 2, 3, 4, add, add],\ + [ 2, 3, op.add, 4, op.mul],\ + [ 2, 3, op.mul, 4, op.add],\ + [ 2, 3, 4, op.mul, op.add],\ + [ 2, 3, 4, op.add, op.add],\ ] wanted_render = [\ '( 2 + 3 ) \\times 4',\ @@ -62,6 +78,27 @@ class TestTexRender(unittest.TestCase): rend = tex(e) self.assertEqual(rend, wanted_render[i]) + def test_parentheses_poly(self): + P = Polynom([1,2,3]) + Q = Polynom([4,5,6]) + exps = [\ + [ 2, P, op.add],\ + [ 2, P, op.sub],\ + [ 2, P, P, op.mul, op.sub],\ + [ Q, P, op.add],\ + [ Q, P, op.sub],\ + ] + wanted_render = [\ + '2 + 3 x^{ 2 } + 2 x + 1' ,\ + '2 - ( 3 x^{ 2 } + 2 x + 1 )' ,\ + '2 - ( 3 x^{ 2 } + 2 x + 1 ) ( 3 x^{ 2 } + 2 x + 1 )' ,\ + '( 6 x^{ 2 } + 5 x + 4 ) + 3 x^{ 2 } + 2 x + 1' ,\ + '( 6 x^{ 2 } + 5 x + 4 ) - ( 3 x^{ 2 } + 2 x + 1 )' ,\ + ] + for (i,e) in enumerate(exps): + rend = tex(e) + self.assertEqual(rend, wanted_render[i]) + def test_slash(self): pass From 374f0bc07ea25df778cd60c333c5b1c15d2d2ce9 Mon Sep 17 00:00:00 2001 From: Lafrite Date: Sat, 7 Mar 2015 18:22:46 +0100 Subject: [PATCH 43/59] change mainOp in polynom --- pymath/polynom.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/pymath/polynom.py b/pymath/polynom.py index eb31271..f83788d 100644 --- a/pymath/polynom.py +++ b/pymath/polynom.py @@ -76,11 +76,13 @@ class Polynom(Explicable): - a: a Expression. [1, Expression("2+3"), 4] designate 1 + (2+3)x + 4x^2 :param letter: the string describing the unknown - >>> Polynom([1,2,3]).mainOp + >>> Polynom([1, 2, 3]).mainOp '+' >>> Polynom([1]).mainOp '*' - >>> Polynom([1,2, 3])._letter + >>> Polynom([0, 0, 3]).mainOp + '*' + >>> Polynom([1, 2, 3])._letter 'x' >>> Polynom([1, 2, 3], "y")._letter 'y' @@ -91,9 +93,9 @@ class Polynom(Explicable): if self.is_monom(): - self.mainOp = "*" + self.mainOp = op.mul else: - self.mainOp = "+" + self.mainOp = op.add self._isPolynom = 1 From e031f9b903a63fe82e92c9cd76db782b11a20753 Mon Sep 17 00:00:00 2001 From: Lafrite Date: Sat, 7 Mar 2015 18:23:10 +0100 Subject: [PATCH 44/59] reset priorities --- pymath/operator.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/pymath/operator.py b/pymath/operator.py index 2ba1234..701486e 100644 --- a/pymath/operator.py +++ b/pymath/operator.py @@ -139,10 +139,15 @@ class Operator(str): return ans def add_parenthesis(self, op): - """ Add parenthesis if necessary """ + """ Add parenthesis if necessary + + >>> from pymath.polynom import Polynom + >>> P = Polynom([1,2,3]) + + """ try: if op.mainOp.priority < self.priority: - op = flatten_list(["("] + [op] + [")"]) + op = flatten_list(["(", op, ")"]) except AttributeError: # op has not the attribute priority try: @@ -293,7 +298,7 @@ class op(object): caract = { "operator" : "-", \ "name" : "sub",\ - "priority" : 1, \ + "priority" : 2, \ "arity" : 2, \ "actions" : ("__sub__","__rsub__"), \ "txt" : "{op1} - {op2}",\ @@ -336,7 +341,7 @@ class op(object): caract = { "operator" : "-", \ "name" : "sub1",\ - "priority" : 2, \ + "priority" : 3, \ "arity" : 1, \ "actions" : "__neg__",\ "txt" : "- {op1}",\ @@ -461,7 +466,7 @@ class op(object): caract = { "operator" : "^", \ "name" : "pw",\ - "priority" : 5, \ + "priority" : 6, \ "arity" : 2, \ "actions" : ("__pow__",""), \ "txt" : "{op1} ^ {op2}",\ From 4841aef478285f452e94da451f8ecc9c5d66a59f Mon Sep 17 00:00:00 2001 From: Lafrite Date: Sat, 7 Mar 2015 18:23:44 +0100 Subject: [PATCH 45/59] new tests for render (not validate yet) --- test/test_render.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/test_render.py b/test/test_render.py index b94f995..bb42311 100644 --- a/test/test_render.py +++ b/test/test_render.py @@ -67,12 +67,14 @@ class TestTexRender(unittest.TestCase): [ 2, 3, op.mul, 4, op.add],\ [ 2, 3, 4, op.mul, op.add],\ [ 2, 3, 4, op.add, op.add],\ + [ 2, 3, 4, op.add, op.sub],\ ] wanted_render = [\ '( 2 + 3 ) \\times 4',\ '2 \\times 3 + 4',\ '2 + 3 \\times 4',\ '2 + 3 + 4',\ + '2 - ( 3 + 4 )',\ ] for (i,e) in enumerate(exps): rend = tex(e) @@ -92,8 +94,8 @@ class TestTexRender(unittest.TestCase): '2 + 3 x^{ 2 } + 2 x + 1' ,\ '2 - ( 3 x^{ 2 } + 2 x + 1 )' ,\ '2 - ( 3 x^{ 2 } + 2 x + 1 ) ( 3 x^{ 2 } + 2 x + 1 )' ,\ - '( 6 x^{ 2 } + 5 x + 4 ) + 3 x^{ 2 } + 2 x + 1' ,\ - '( 6 x^{ 2 } + 5 x + 4 ) - ( 3 x^{ 2 } + 2 x + 1 )' ,\ + '6 x^{ 2 } + 5 x + 4 + 3 x^{ 2 } + 2 x + 1' ,\ + '6 x^{ 2 } + 5 x + 4 - ( 3 x^{ 2 } + 2 x + 1 )' ,\ ] for (i,e) in enumerate(exps): rend = tex(e) From c00c0a86ccd0ec2c30652275b41eb3995a2a7d81 Mon Sep 17 00:00:00 2001 From: Lafrite Date: Sat, 7 Mar 2015 19:26:26 +0100 Subject: [PATCH 46/59] add_parenthesis to l_parenthesis and r_parenthesis --- pymath/operator.py | 61 +++++++++++++++++++++++++++++----------------- 1 file changed, 38 insertions(+), 23 deletions(-) diff --git a/pymath/operator.py b/pymath/operator.py index 701486e..57466cc 100644 --- a/pymath/operator.py +++ b/pymath/operator.py @@ -49,9 +49,15 @@ class Operator(str): :returns: the string with operator and operands """ - replacement = {"op"+str(i+1): ' '.join(self.add_parenthesis(op)) for (i,op) in enumerate(args)} - - ans = link.format(**replacement) + if self.arity == 1: + op1 = self.l_parenthesis(args[0], True) + ans = link.format(op1 = op1) + + elif self.arity == 2: + op1 = self.r_parenthesis(args[0], True) + op2 = self.l_parenthesis(args[1], True) + ans = link.format(op1 = op1, op2 = op2) + ans = save_mainOp(ans, self) return ans @@ -127,24 +133,19 @@ class Operator(str): # TODO: Attention à gestion des fractions qui se comportent chelou avec les parenthèses |dim. nov. 9 09:21:52 CET 2014 if self.arity == 1: # TODO: Marche juste avec -, il faudra voir quand il y aura d'autres operateurs unitaires |dim. nov. 9 09:24:53 CET 2014 - op1 = self.add_parenthesis(args[0]) + op1 = self.l_parenthesis(args[0]) ans = flatten_list([self, op1]) elif self.arity == 2: - op1 = self.add_parenthesis(args[0]) - op2 = self.add_parenthesis(args[1]) + op1 = self.l_parenthesis(args[0]) + op2 = self.r_parenthesis(args[1]) ans = flatten_list([op1, self, op2]) ans = save_mainOp(ans, self) return ans - def add_parenthesis(self, op): - """ Add parenthesis if necessary - - >>> from pymath.polynom import Polynom - >>> P = Polynom([1,2,3]) - - """ + def l_parenthesis(self, op, str_join=False): + """ Add parenthesis for left operand if necessary """ try: if op.mainOp.priority < self.priority: op = flatten_list(["(", op, ")"]) @@ -155,7 +156,14 @@ class Operator(str): op = ['(', op, ')'] except ValueError: pass - return flatten_list([op]) + ans = flatten_list([op]) + if str_join: + ans = ' '.join([str(i) for i in ans]) + return ans + + def r_parenthesis(self, op, str_join=False): + """ Add parenthesis for left operand if necessary """ + return self.l_parenthesis(op, str_join) def save_mainOp(obj, mainOp): """Create a temporary class build over built-in type to stock the main operation of a calculus @@ -188,7 +196,8 @@ def operatorize(fun): * "_render": action use in __txt__ and __tex__ * "__txt__": txt rendering * "__tex__": tex rendering - * "add_parenthesis": mechanism to add parenthesis + * "l_parenthesis": mechanism to add parenthesis for left operande + * "r_parenthesis": mechanism to add parenthesis for rigth operande """ def mod_fun(self, *args): ans = fun(self, *args) @@ -324,7 +333,7 @@ class op(object): >>> sub1.__tex__('-1') '- (-1)' """ - def add_parenthesis(self, op): + def l_parenthesis(self, op, str_join=False): """ Add parenthesis if necessary """ try: if op.mainOp.priority <= self.priority: @@ -336,7 +345,11 @@ class op(object): op = ['(', op, ')'] except ValueError: pass - return flatten_list([op]) + + ans = flatten_list([op]) + if str_join: + ans = ' '.join([str(i) for i in ans]) + return ans caract = { "operator" : "-", \ @@ -346,7 +359,7 @@ class op(object): "actions" : "__neg__",\ "txt" : "- {op1}",\ "tex" : "- {op1}",\ - "add_parenthesis": add_parenthesis,\ + "l_parenthesis": l_parenthesis,\ } return caract @@ -371,15 +384,17 @@ class op(object): # * can not be display in some cases def _render(self, link, *args): - replacement = {"op"+str(i+1): ' '.join(self.add_parenthesis(op)) for (i,op) in enumerate(args)} + op1 = self.r_parenthesis(args[0], True) + op2 = self.l_parenthesis(args[1], True) + ans = link.format(op1 = op1, op2 = op2) - if not self.visibility or args[1][0] == "(" or \ - (type(args[1][0]) == str and args[1][0].isalpha()): - ans = "{op1} {op2}".format(**replacement) + if not self.visibility or op2[0] == "(" or \ + (type(op2[0]) == str and op2[0].isalpha()): + ans = "{op1} {op2}".format(op1 = op1, op2 = op2) ans = save_mainOp(ans, self) return ans else: - ans = link.format(**replacement) + ans = link.format(op1 = op1, op2 = op2) ans = save_mainOp(ans, self) return ans From cdfd70d3f3c3de2f71d0e431a0dfea7af7ebe1cd Mon Sep 17 00:00:00 2001 From: Lafrite Date: Sat, 7 Mar 2015 19:35:34 +0100 Subject: [PATCH 47/59] solve render bug with (..) - (..) to .. - (..) --- pymath/operator.py | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/pymath/operator.py b/pymath/operator.py index 57466cc..3dab91a 100644 --- a/pymath/operator.py +++ b/pymath/operator.py @@ -54,8 +54,8 @@ class Operator(str): ans = link.format(op1 = op1) elif self.arity == 2: - op1 = self.r_parenthesis(args[0], True) - op2 = self.l_parenthesis(args[1], True) + op1 = self.l_parenthesis(args[0], True) + op2 = self.r_parenthesis(args[1], True) ans = link.format(op1 = op1, op2 = op2) ans = save_mainOp(ans, self) @@ -144,8 +144,8 @@ class Operator(str): ans = save_mainOp(ans, self) return ans - def l_parenthesis(self, op, str_join=False): - """ Add parenthesis for left operand if necessary """ + def all_parenthesis(self, op, str_join=False): + """ Add parenthesis if necessary (left and rigth)""" try: if op.mainOp.priority < self.priority: op = flatten_list(["(", op, ")"]) @@ -161,9 +161,13 @@ class Operator(str): ans = ' '.join([str(i) for i in ans]) return ans + def l_parenthesis(self, op, str_join=False): + """ Add parenthesis for left operand if necessary """ + return self.all_parenthesis(op, str_join) + def r_parenthesis(self, op, str_join=False): """ Add parenthesis for left operand if necessary """ - return self.l_parenthesis(op, str_join) + return self.all_parenthesis(op, str_join) def save_mainOp(obj, mainOp): """Create a temporary class build over built-in type to stock the main operation of a calculus @@ -196,6 +200,7 @@ def operatorize(fun): * "_render": action use in __txt__ and __tex__ * "__txt__": txt rendering * "__tex__": tex rendering + * "all_parenthesis": mechanism to add parenthesis for left or rigth operande * "l_parenthesis": mechanism to add parenthesis for left operande * "r_parenthesis": mechanism to add parenthesis for rigth operande """ @@ -303,7 +308,11 @@ class op(object): '1 - 2' >>> sub.__tex__('1','-2') '1 - (-2)' + >>> sub.__tex__('-1','2') + 'i-1 - 2' """ + def l_parenthesis(self, op, str_join=False): + return op caract = { "operator" : "-", \ "name" : "sub",\ @@ -312,6 +321,7 @@ class op(object): "actions" : ("__sub__","__rsub__"), \ "txt" : "{op1} - {op2}",\ "tex" : "{op1} - {op2}",\ + "l_parenthesis": l_parenthesis,\ } return caract From d503db6cefb430ab087a06c32cc794d3557c2d68 Mon Sep 17 00:00:00 2001 From: Lafrite Date: Sat, 7 Mar 2015 20:04:16 +0100 Subject: [PATCH 48/59] add doctest and unittest for render --- pymath/operator.py | 38 ++++++++++++++++++++++---------------- test/test_render.py | 21 +++++++++++++++------ 2 files changed, 37 insertions(+), 22 deletions(-) diff --git a/pymath/operator.py b/pymath/operator.py index 3dab91a..9706a96 100644 --- a/pymath/operator.py +++ b/pymath/operator.py @@ -386,8 +386,14 @@ class op(object): 2 >>> mul.__tex__('1','2') '1 \\times 2' + >>> mul.__tex__('2','a') + '2 a' >>> mul.__txt__('1','2') '1 * 2' + >>> mul.__txt__('2','a') + '2 a' + >>> mul.__txt__('a','2') + 'a * 2' >>> mul.__tex__('1','-2') '1 \\times (-2)' """ @@ -514,24 +520,24 @@ class op(object): return caract if __name__ == '__main__': - print(op.add.__tex__('1','2')) - print(op.mul.__tex__('1','2')) - print(op.sub.__tex__('1','2')) - f = save_mainOp('2 + 3',op.add) - print(op.mul.__txt__(f, '4')) - f = save_mainOp('-3',op.sub1) - print(op.sub1.__txt__(f)) - print(op.sub1.__txt__('-3')) - f = save_mainOp('2 + 3',op.add) - print(op.sub1.__txt__(f)) + #print(op.add.__tex__('1','2')) + #print(op.mul.__tex__('1','2')) + #print(op.sub.__tex__('1','2')) + #f = save_mainOp('2 + 3',op.add) + #print(op.mul.__txt__(f, '4')) + #f = save_mainOp('-3',op.sub1) + #print(op.sub1.__txt__(f)) + #print(op.sub1.__txt__('-3')) + #f = save_mainOp('2 + 3',op.add) + #print(op.sub1.__txt__(f)) - from .fraction import Fraction - f = Fraction(1, 2) - print(op.add.__txt__(f.__txt__(),'2')) - print(op.add.__tex__(f.__tex__(),'2')) + #from .fraction import Fraction + #f = Fraction(1, 2) + #print(op.add.__txt__(f.__txt__(),'2')) + #print(op.add.__tex__(f.__tex__(),'2')) - print("\t op.can_be_operator('+') :" + str(op.can_be_operator('+'))) - print("\t op.can_be_operator('t') :" + str(op.can_be_operator('t'))) + #print("\t op.can_be_operator('+') :" + str(op.can_be_operator('+'))) + #print("\t op.can_be_operator('t') :" + str(op.can_be_operator('t'))) import doctest diff --git a/test/test_render.py b/test/test_render.py index bb42311..4b58b55 100644 --- a/test/test_render.py +++ b/test/test_render.py @@ -55,7 +55,7 @@ class TestTexRender(unittest.TestCase): ] wanted_render = [ "2 ( 3 x^{ 2 } + 2 x + 1 )", "( 3 x^{ 2 } + 2 x + 1 ) \\times 2", - "( 3 x^{ 2 } + 2 x + 1 ) ( 3 x^{ 2 } + 2 x + 1 )", + "( 3 x^{ 2 } + 2 x + 1 ) ( 6 x^{ 2 } + 5 x + 4 )", ] for (i,e) in enumerate(exps): rend = tex(e) @@ -129,11 +129,20 @@ class TesttxtRender(unittest.TestCase): self.assertEqual(rend, wanted_render[i]) def test_mult_letter(self): - exps = [ [2, "a", op.get_op("*", 2)], \ - ["a", 3, op.get_op("*", 2)], \ - [-2, "a", op.get_op("*", 2)], \ - ["a", -2, op.get_op("*", 2)]] - wanted_render = [ "2 a", "a * 3", "-2 a", "a * ( -2 )"] + exps = [ [2, "a", op.mul], \ + ["a", 3, op.mul], \ + [-2, "a", op.mul], \ + ["a", -2, op.mul], + ["a", -2, op.mul, -2, op.mul], + ["a", -2, op.mul, "a", op.mul], + ] + wanted_render = [ "2 a", + "a * 3", + "-2 a", + "a * ( -2 )", + "a * ( -2 ) * ( -2 )", + "a * ( -2 ) a", + ] for (i,e) in enumerate(exps): rend = txt(e) self.assertEqual(rend, wanted_render[i]) From 256dbdec908876942acb07fce5f04b20e92b6869 Mon Sep 17 00:00:00 2001 From: Lafrite Date: Sun, 8 Mar 2015 15:55:44 +0100 Subject: [PATCH 49/59] Add a todo --- TODO | 1 + 1 file changed, 1 insertion(+) diff --git a/TODO b/TODO index 7b2f402..64b0f4a 100644 --- a/TODO +++ b/TODO @@ -8,3 +8,4 @@ * Expression parents class and his children: Numerical_exp, toGenerate_exp and formal expression * Create tbl sgn and variation render +* Give a name to polynoms From d81d65564b6054abb3bbbdea39f5a13aaa68c5f8 Mon Sep 17 00:00:00 2001 From: Lafrite Date: Sun, 8 Mar 2015 15:55:44 +0100 Subject: [PATCH 50/59] Add a todo --- TODO | 1 + 1 file changed, 1 insertion(+) diff --git a/TODO b/TODO index 7b2f402..64b0f4a 100644 --- a/TODO +++ b/TODO @@ -8,3 +8,4 @@ * Expression parents class and his children: Numerical_exp, toGenerate_exp and formal expression * Create tbl sgn and variation render +* Give a name to polynoms From 9ddb015a87200cb4a3d2d26184a5537f9d494138 Mon Sep 17 00:00:00 2001 From: Lafrite Date: Sun, 8 Mar 2015 18:51:53 +0100 Subject: [PATCH 51/59] Add a name to polynoms --- pymath/polynom.py | 24 +++++++++++++++++++----- pymath/polynomDeg2.py | 4 ++-- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/pymath/polynom.py b/pymath/polynom.py index f83788d..36e3f3d 100644 --- a/pymath/polynom.py +++ b/pymath/polynom.py @@ -33,7 +33,7 @@ class Polynom(Explicable): """Docstring for Polynom. """ @classmethod - def random(self, coefs_form=[], conditions=[], letter = "x", degree = 0): + def random(self, coefs_form=[], conditions=[], letter = "x", degree = 0, name = "P"): """ Create a random polynom from coefs_form and conditions :param coefs_form: list of forms (one by coef) (ascending degree sorted) @@ -64,9 +64,9 @@ class Polynom(Explicable): # On "parse" ce string pour créer les coefs coefs = [eval(i) if type(i)==str else i for i in eval(coefs)] # Création du polynom - return Polynom(coef = coefs, letter = letter) + return Polynom(coef = coefs, letter = letter, name = name) - def __init__(self, coef = [1], letter = "x" ): + def __init__(self, coef = [1], letter = "x", name = "P"): """Initiate the polynom :param coef: coefficients of the polynom (ascending degree sorted) @@ -75,9 +75,15 @@ class Polynom(Explicable): - [a,b,c]: list of coeficient for same degree. [1,[2,3],4] designate 1 + 2x + 3x + 4x^2 - a: a Expression. [1, Expression("2+3"), 4] designate 1 + (2+3)x + 4x^2 :param letter: the string describing the unknown + :param name: Name of the polynom - >>> Polynom([1, 2, 3]).mainOp + >>> P = Polynom([1, 2, 3]) + >>> P.mainOp '+' + >>> P.name + 'P' + >>> P._letter + 'x' >>> Polynom([1]).mainOp '*' >>> Polynom([0, 0, 3]).mainOp @@ -86,10 +92,13 @@ class Polynom(Explicable): 'x' >>> Polynom([1, 2, 3], "y")._letter 'y' + >>> Polynom([1, 2, 3], name = "Q").name + 'Q' """ super(Polynom, self).__init__() self.feed_coef(coef) self._letter = letter + self.name = name if self.is_monom(): @@ -408,6 +417,8 @@ class Polynom(Explicable): >>> Q = P.derivate() >>> Q < Polynom [2, 6]> + >>> print(Q.name) + P' >>> for i in Q.explain(): ... print(i) 2 \\times 3 x + 1 \\times 2 @@ -416,7 +427,10 @@ class Polynom(Explicable): derv_coefs = [] for (i,c) in enumerate(self._coef): derv_coefs += [Expression([i, c, op.mul])] - return Polynom(derv_coefs[1:]).simplify() + + ans = Polynom(derv_coefs[1:]).simplify() + ans.name = self.name + "'" + return ans @staticmethod def postfix_add(numbers): diff --git a/pymath/polynomDeg2.py b/pymath/polynomDeg2.py index 1efd025..099eff2 100644 --- a/pymath/polynomDeg2.py +++ b/pymath/polynomDeg2.py @@ -13,12 +13,12 @@ class Polynom_deg2(Polynom): Child of Polynom with some extra tools """ - def __init__(self, coefs = [0, 0, 1], letter = "x"): + def __init__(self, coefs = [0, 0, 1], letter = "x", name = "P"): if len(coefs) < 3 or len(coefs) > 4: raise ValueError("Polynom_deg2 have to be degree 2 polynoms, they need 3 coefficients, {} are given".format(len(coefs))) if coefs[2] == 0: raise ValueError("Polynom_deg2 have to be degree 2 polynoms, coefficient of x^2 can't be 0") - Polynom.__init__(self, coefs, letter) + Polynom.__init__(self, coefs, letter, name = name) @property def a(self): From 68f6cb32d44bc69081fb9e9788b2235566c497ae Mon Sep 17 00:00:00 2001 From: Lafrite Date: Sun, 8 Mar 2015 19:05:15 +0100 Subject: [PATCH 52/59] Give a name to polynomDeg2 --- pymath/polynomDeg2.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pymath/polynomDeg2.py b/pymath/polynomDeg2.py index 36f18fd..9a87efb 100644 --- a/pymath/polynomDeg2.py +++ b/pymath/polynomDeg2.py @@ -16,7 +16,7 @@ class Polynom_deg2(Polynom): """ @classmethod - def random(self, coefs_form = ["{c}", "{b}", "{a}"], conditions = [], letter = "x"): + def random(self, coefs_form = ["{c}", "{b}", "{a}"], conditions = [], letter = "x", name = "P"): """ Create a 2nd degree poly from coefs_form ans conditions :param coefs_form: list of forms (one by coef) (ascending degree sorted) @@ -33,7 +33,7 @@ class Polynom_deg2(Polynom): # On "parse" ce string pour créer les coefs coefs = [eval(i) if type(i)==str else i for i in eval(coefs)] # Création du polynom - return Polynom_deg2(coefs = coefs, letter = letter) + return Polynom_deg2(coefs = coefs, letter = letter, name = name) def __init__(self, coefs = [0, 0, 1], letter = "x", name = "P"): if len(coefs) < 3 or len(coefs) > 4: From 9a7f0da24e66f2e791fe19a94edf346468f2ed4a Mon Sep 17 00:00:00 2001 From: Lafrite Date: Sun, 8 Mar 2015 23:40:15 +0100 Subject: [PATCH 53/59] add ggive_name method to polynom --- pymath/polynom.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pymath/polynom.py b/pymath/polynom.py index 36e3f3d..13a17b6 100644 --- a/pymath/polynom.py +++ b/pymath/polynom.py @@ -175,6 +175,9 @@ class Polynom(Explicable): else: return 0 + def give_name(self, name): + self.name = name + def __str__(self): return str(Expression(self.postfix_tokens)) From 7272c66fb99254032bd4dc7f535101ab3c1ddd8d Mon Sep 17 00:00:00 2001 From: Lafrite Date: Sun, 8 Mar 2015 23:43:46 +0100 Subject: [PATCH 54/59] add bug about parenthesis --- bugs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/bugs b/bugs index 31ee7b4..5d1f54b 100644 --- a/bugs +++ b/bugs @@ -65,3 +65,11 @@ TypeError: unsupported operand type(s) for +: 'type' and 'str' +* Parenthèses abhérentes + + In [7]: P = Polynom([-6, 12, -20]) + + In [8]: print(P) + ( - 20 x^{ 2 } + 12 x ) - 6 + + From 097ad25fe2c80a2ca4ce1c0afaa010d05419964c Mon Sep 17 00:00:00 2001 From: Lafrite Date: Sun, 8 Mar 2015 23:56:59 +0100 Subject: [PATCH 55/59] solve bug with - and () --- pymath/operator.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/pymath/operator.py b/pymath/operator.py index 9706a96..6a05657 100644 --- a/pymath/operator.py +++ b/pymath/operator.py @@ -313,6 +313,23 @@ class op(object): """ def l_parenthesis(self, op, str_join=False): return op + + def r_parenthesis(self, op, str_join=False): + try: + if op.mainOp.priority <= self.priority: + op = flatten_list(["(", op, ")"]) + except AttributeError: + # op has not the attribute priority + try: + if int(op) < 0: + op = ['(', op, ')'] + except ValueError: + pass + ans = flatten_list([op]) + if str_join: + ans = ' '.join([str(i) for i in ans]) + return ans + caract = { "operator" : "-", \ "name" : "sub",\ @@ -322,6 +339,7 @@ class op(object): "txt" : "{op1} - {op2}",\ "tex" : "{op1} - {op2}",\ "l_parenthesis": l_parenthesis,\ + "r_parenthesis": r_parenthesis,\ } return caract From 8b79fbbace0716670abc2c44e7a739a50322aba2 Mon Sep 17 00:00:00 2001 From: Lafrite Date: Tue, 10 Mar 2015 11:59:27 +0100 Subject: [PATCH 56/59] solve issues with () for add and mul --- pymath/operator.py | 89 ++++++++++++++++++++++++++++++---------------- 1 file changed, 59 insertions(+), 30 deletions(-) diff --git a/pymath/operator.py b/pymath/operator.py index 6a05657..8ef319e 100644 --- a/pymath/operator.py +++ b/pymath/operator.py @@ -144,8 +144,26 @@ class Operator(str): ans = save_mainOp(ans, self) return ans - def all_parenthesis(self, op, str_join=False): - """ Add parenthesis if necessary (left and rigth)""" + def l_parenthesis(self, opl, str_join=False): + """ Add parenthesis for left operand if necessary """ + ans = opl + try: + if opl.mainOp == op.sub1: + ans = opl + elif opl.mainOp.priority < self.priority: + ans = flatten_list(["(", opl, ")"]) + except AttributeError as e: + # op has not the attribute priority + pass + + ans = flatten_list([ans]) + if str_join: + ans = ' '.join([str(i) for i in ans]) + return ans + + def r_parenthesis(self, op, str_join=False): + """ Add parenthesis for left operand if necessary """ + # TODO: /!\ Parenthèses pour -2abc et l'opérateur * |lun. mars 9 19:02:32 CET 2015 try: if op.mainOp.priority < self.priority: op = flatten_list(["(", op, ")"]) @@ -161,14 +179,6 @@ class Operator(str): ans = ' '.join([str(i) for i in ans]) return ans - def l_parenthesis(self, op, str_join=False): - """ Add parenthesis for left operand if necessary """ - return self.all_parenthesis(op, str_join) - - def r_parenthesis(self, op, str_join=False): - """ Add parenthesis for left operand if necessary """ - return self.all_parenthesis(op, str_join) - def save_mainOp(obj, mainOp): """Create a temporary class build over built-in type to stock the main operation of a calculus @@ -176,12 +186,6 @@ def save_mainOp(obj, mainOp): :mainOp: the main operator :returns: the same object with the main operation attribute """ - #class Fake(type(obj)): - # """ The fake class """ - # def __new__(cls, obj): - # op = type(obj).__new__(cls, obj) - # op.mainOp = mainOp - # return op Fake = type('fake_'+str(type(obj)), (type(obj),), {'mainOp': mainOp}) return Fake(obj) @@ -200,7 +204,6 @@ def operatorize(fun): * "_render": action use in __txt__ and __tex__ * "__txt__": txt rendering * "__tex__": tex rendering - * "all_parenthesis": mechanism to add parenthesis for left or rigth operande * "l_parenthesis": mechanism to add parenthesis for left operande * "r_parenthesis": mechanism to add parenthesis for rigth operande """ @@ -416,21 +419,38 @@ class op(object): '1 \\times (-2)' """ # * can not be display in some cases + def is_visible(self, op1, op2): + """ Tells whether self has to be visible or not + + :param op1: left operande + :param op2: rigth operande + + """ + # TODO: À finir!!! |lun. mars 9 00:03:40 CET 2015 + if type(op2) == int: + # op2 est maintenant une chaine de caractères + return True + elif op2.isdecimal(): + return True + elif op2.isalpha(): + return False + elif (op2[0] == "(" or op2[0].isdecimal()) and not ("+" in op2): + return True + else: + return False + def _render(self, link, *args): - op1 = self.r_parenthesis(args[0], True) - op2 = self.l_parenthesis(args[1], True) - ans = link.format(op1 = op1, op2 = op2) + op1 = self.l_parenthesis(args[0], True) + op2 = self.r_parenthesis(args[1], True) - if not self.visibility or op2[0] == "(" or \ - (type(op2[0]) == str and op2[0].isalpha()): + if not self.is_visible(op1, op2): ans = "{op1} {op2}".format(op1 = op1, op2 = op2) - ans = save_mainOp(ans, self) - return ans else: ans = link.format(op1 = op1, op2 = op2) - ans = save_mainOp(ans, self) - return ans + + ans = save_mainOp(ans, self) + return ans caract = { "operator" : "*", \ @@ -441,7 +461,8 @@ class op(object): "txt" : "{op1} * {op2}",\ "tex" : "{op1} \\times {op2}",\ "visibility": 1,\ - "_render": _render + "_render": _render,\ + "is_visible": is_visible,\ } return caract @@ -556,10 +577,18 @@ if __name__ == '__main__': #print("\t op.can_be_operator('+') :" + str(op.can_be_operator('+'))) #print("\t op.can_be_operator('t') :" + str(op.can_be_operator('t'))) - - import doctest - doctest.testmod() + from .render import tex + print(tex([-2, 3, op.add ])) + print("-----------------") + print(tex([-2, 3, op.mul ])) + print("-----------------") + from .polynom import Polynom + print(tex([Polynom([1,2,3]), 2, op.mul])) + print("-----------------") + + #import doctest + #doctest.testmod() From 25127acde3f9df39a0efb286484c62dbc08cfff2 Mon Sep 17 00:00:00 2001 From: Lafrite Date: Tue, 10 Mar 2015 11:59:35 +0100 Subject: [PATCH 57/59] tests for mul._render and render --- test/test_operator.py | 33 ++++++++++++++++++ test/test_render.py | 78 ++++++++++++++++++++++++++++++++++++++----- 2 files changed, 103 insertions(+), 8 deletions(-) create mode 100644 test/test_operator.py diff --git a/test/test_operator.py b/test/test_operator.py new file mode 100644 index 0000000..6820e45 --- /dev/null +++ b/test/test_operator.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python +# encoding: utf-8 + +from pymath.operator import op + + +def test_mul_is_visible(): + assert op.mul.is_visible(2,3) == True + assert op.mul.is_visible(2,-3) == True + assert op.mul.is_visible(-2,3) == True + assert op.mul.is_visible('a',2) == True + assert op.mul.is_visible('2a + 1', 2) == True + assert op.mul.is_visible(2, '(-2)') == True + assert op.mul.is_visible(2, '2a') == True + assert op.mul.is_visible(2, '(-2a)') == True + assert op.mul.is_visible(2, '(-2abc)') == True + + assert op.mul.is_visible(2,'a') == False + assert op.mul.is_visible(2, '(2a + 1)') == False + assert op.mul.is_visible('(3x - 1)', '(2a + 1)') == False + assert op.mul.is_visible(2, '(-2x + 1)(3x + 2)') == False + + + + + + + +# ----------------------------- +# Reglages pour 'vim' +# vim:set autoindent expandtab tabstop=4 shiftwidth=4: +# cursor: 16 del + diff --git a/test/test_render.py b/test/test_render.py index 4b58b55..5589a2c 100644 --- a/test/test_render.py +++ b/test/test_render.py @@ -27,15 +27,77 @@ class TestTexRender(unittest.TestCase): P = Polynom([1,2,3]) self.assertEqual(tex([P]), "3 x^{ 2 } + 2 x + 1") + def test_add_interger(self): + exps = [ [2, 3, op.add ], + [2, -3, op.add ], + [-2, 3, op.add ], + ] + wanted_render = [ "2 + 3", + "2 + ( -3 )", + "-2 + 3", + ] + for (i,e) in enumerate(exps): + rend = tex(e) + self.assertEqual(rend, wanted_render[i]) + + def test_add_letter(self): + exps = [[2, "a", op.add ], + ["a", 3, op.add ], + [-2, "a", op.add ], + ["a", -2, op.add ], + ] + wanted_render = [ "2 + a", + "a + 3", + "-2 + a", + "a + ( -2 )", + ] + for (i,e) in enumerate(exps): + rend = tex(e) + self.assertEqual(rend, wanted_render[i]) + + def test_add_fraction(self): + exps = [[2, Fraction(1,2), op.add], + [Fraction(1,2), 3, op.add], + ] + wanted_render = [ "2 + \\frac{ 1 }{ 2 }", + "\\frac{ 1 }{ 2 } + 3", + ] + for (i,e) in enumerate(exps): + rend = tex(e) + self.assertEqual(rend, wanted_render[i]) + + def test_add_poly(self): + exps = [[2, Polynom([1,2,3]), op.mul], + [Polynom([1,2,3]), 2, op.mul], + [Polynom([1,2,3]), Polynom([4,5,6]), op.mul], + ] + wanted_render = [ "2 ( 3 x^{ 2 } + 2 x + 1 )", + "( 3 x^{ 2 } + 2 x + 1 ) \\times 2", + "( 3 x^{ 2 } + 2 x + 1 ) ( 6 x^{ 2 } + 5 x + 4 )", + ] + for (i,e) in enumerate(exps): + rend = tex(e) + self.assertEqual(rend, wanted_render[i]) + def test_mult_interger(self): - exps = [ [2, 3, op.get_op("*", 2)], [2, -3, op.get_op("*", 2)], [-2, 3, op.get_op("*", 2)]] - wanted_render = [ "2 \\times 3", "2 \\times ( -3 )", "-2 \\times 3"] + exps = [[2, 3, op.mul], + [2, -3, op.mul], + [-2, 3, op.mul], + ] + wanted_render = [ "2 \\times 3", + "2 \\times ( -3 )", + "-2 \\times 3", + ] for (i,e) in enumerate(exps): rend = tex(e) self.assertEqual(rend, wanted_render[i]) def test_mult_letter(self): - exps = [ [2, "a", op.get_op("*", 2)], ["a", 3, op.get_op("*", 2)], [-2, "a", op.get_op("*", 2)], ["a", -2, op.get_op("*", 2)]] + exps = [[2, "a", op.mul], + ["a", 3, op.mul], + [-2, "a", op.mul], + ["a", -2, op.mul], + ] wanted_render = [ "2 a", "a \\times 3", "-2 a", "a \\times ( -2 )"] for (i,e) in enumerate(exps): rend = tex(e) @@ -120,9 +182,9 @@ class TesttxtRender(unittest.TestCase): self.assertEqual(txt([Fraction(1,2)]), "1 / 2") def test_mult_interger(self): - exps = [ [2, 3, op.get_op("*", 2)], \ - [2, -3, op.get_op("*", 2)], \ - [-2, 3, op.get_op("*", 2)]] + exps = [ [2, 3, op.mul], \ + [2, -3, op.mul], \ + [-2, 3, op.mul]] wanted_render = [ "2 * 3", "2 * ( -3 )", "-2 * 3"] for (i,e) in enumerate(exps): rend = txt(e) @@ -148,8 +210,8 @@ class TesttxtRender(unittest.TestCase): self.assertEqual(rend, wanted_render[i]) def test_mult_fraction(self): - exps = [ [2, Fraction(1,2), op.get_op("*", 2)], \ - [Fraction(1,2), 3, op.get_op("*", 2)]] + exps = [ [2, Fraction(1,2), op.mul], \ + [Fraction(1,2), 3, op.mul]] wanted_render = [ "2 * 1 / 2", "1 / 2 * 3"] for (i,e) in enumerate(exps): rend = txt(e) From b508a8afb4a05d6feb93858921f455c2b5fc8ab8 Mon Sep 17 00:00:00 2001 From: Lafrite Date: Tue, 10 Mar 2015 14:04:04 +0100 Subject: [PATCH 58/59] all tested are passed! :D --- pymath/operator.py | 10 +++++++++- test/test_operator.py | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/pymath/operator.py b/pymath/operator.py index 8ef319e..35e2a19 100644 --- a/pymath/operator.py +++ b/pymath/operator.py @@ -434,7 +434,12 @@ class op(object): return True elif op2.isalpha(): return False - elif (op2[0] == "(" or op2[0].isdecimal()) and not ("+" in op2): + elif op2[0].isdecimal(): + return True + elif op2[0] == "(" and not ("+" in op2 or "-" in op2[3:]): + return True + # Giga bricolage... + elif "frac" in op2: return True else: return False @@ -586,6 +591,9 @@ if __name__ == '__main__': from .polynom import Polynom print(tex([Polynom([1,2,3]), 2, op.mul])) print("-----------------") + from .fraction import Fraction + print(tex([2, Fraction(1,2), op.mul])) + print("-----------------") #import doctest #doctest.testmod() diff --git a/test/test_operator.py b/test/test_operator.py index 6820e45..c89f9fd 100644 --- a/test/test_operator.py +++ b/test/test_operator.py @@ -9,7 +9,7 @@ def test_mul_is_visible(): assert op.mul.is_visible(2,-3) == True assert op.mul.is_visible(-2,3) == True assert op.mul.is_visible('a',2) == True - assert op.mul.is_visible('2a + 1', 2) == True + assert op.mul.is_visible('(2a + 1)', 2) == True assert op.mul.is_visible(2, '(-2)') == True assert op.mul.is_visible(2, '2a') == True assert op.mul.is_visible(2, '(-2a)') == True From e58567b2f8da44c381e1ec4fcb23ebf9294a5281 Mon Sep 17 00:00:00 2001 From: Lafrite Date: Sat, 28 Mar 2015 09:09:32 +0100 Subject: [PATCH 59/59] change op.add and op.sub for better - management --- pymath/operator.py | 29 ++++++++++++++++++++++++++++- test/test_render.py | 4 ++-- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/pymath/operator.py b/pymath/operator.py index 35e2a19..4d171c2 100644 --- a/pymath/operator.py +++ b/pymath/operator.py @@ -281,8 +281,32 @@ class op(object): >>> add.__txt__('1','2') '1 + 2' >>> add.__tex__('1','-2') - '1 + (-2)' + '1 - 2' """ + + def _render(self, link, *args): + """Global step for __txt__ and __tex__ + + :param link: the link between operators + :param *args: the operands + :returns: the string with operator and operands + + """ + if args[1][0] == "-": + op1 = self.l_parenthesis(args[0], True) + op2 = self.r_parenthesis(args[1][1:], True) + ans = link.replace('+','-').format(op1 = op1, op2 = op2) + + ans = save_mainOp(ans, self) + return ans + else: + op1 = self.l_parenthesis(args[0], True) + op2 = self.r_parenthesis(args[1], True) + ans = link.format(op1 = op1, op2 = op2) + + ans = save_mainOp(ans, self) + return ans + caract = { "operator" : "+", \ "name" : "add",\ @@ -291,6 +315,7 @@ class op(object): "actions" : ("__add__","__radd__"), \ "txt" : "{op1} + {op2}",\ "tex" : "{op1} + {op2}",\ + "_render": _render,\ } return caract @@ -321,6 +346,8 @@ class op(object): try: if op.mainOp.priority <= self.priority: op = flatten_list(["(", op, ")"]) + elif op[0] == '-': + op = flatten_list(["(", op, ")"]) except AttributeError: # op has not the attribute priority try: diff --git a/test/test_render.py b/test/test_render.py index 5589a2c..50394b5 100644 --- a/test/test_render.py +++ b/test/test_render.py @@ -33,7 +33,7 @@ class TestTexRender(unittest.TestCase): [-2, 3, op.add ], ] wanted_render = [ "2 + 3", - "2 + ( -3 )", + "2 - 3", "-2 + 3", ] for (i,e) in enumerate(exps): @@ -49,7 +49,7 @@ class TestTexRender(unittest.TestCase): wanted_render = [ "2 + a", "a + 3", "-2 + a", - "a + ( -2 )", + "a - 2", ] for (i,e) in enumerate(exps): rend = tex(e)