Merge branch 'dev' into documentation

This commit is contained in:
Lafrite 2015-04-03 14:18:26 +02:00
commit 15d87cf147
18 changed files with 1521 additions and 557 deletions

1
.gitignore vendored
View File

@ -1,5 +1,4 @@
__pycache__/
*.pyc
dist/
bugs
*.egg-info/

2
TODO
View File

@ -5,5 +5,7 @@
* 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

79
bugs Normal file
View File

@ -0,0 +1,79 @@
* Soustraction de Polynômes!!!!
* 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
*(solved) 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
*(solved) 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 }
* 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]: < <class 'pymath.expression.Expression'> [1, 2, 3, '*', '+', < Polynom [1, 2, 1]>, '+'] >
In [6]: P + e
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-6-ff312ef36cdb> in <module>()
----> 1 P + e
/home/lafrite/scripts/pyMath/pymath/polynom.py in __add__(self, other)
430 [< <class 'pymath.expression.Expression'> [3, 'x', 2, '^', '*', 2, 'x', '*', '+', 1, '+', 5, 'x', '*', 4, '+', '+'] >, < Polynom [< <class 'pymath.expression.Expression'> [1, 4, '+'] >, < <class 'pymath.expression.Expression'> [2, 5, '+'] >, 3]>, < Polynom [< <class 'pymath.expression.Expression'> [1, 4, '+'] >, < <class 'pymath.expression.Expression'> [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'
* Parenthèses abhérentes
In [7]: P = Polynom([-6, 12, -20])
In [8]: print(P)
( - 20 x^{ 2 } + 12 x ) - 6

View File

@ -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,90 @@ 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*).
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 R.explain():
... 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 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

View File

@ -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).

95
pymath/explicable.py Normal file
View File

@ -0,0 +1,95 @@
#!/usr/bin/env python
# encoding: utf-8
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
>>> from .expression import 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(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!
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:
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
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

View File

@ -2,71 +2,18 @@
# 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 .explicable import Explicable
from .random_expression import RdExpression
__all__ = ['Expression']
class Expression(object):
class Expression(Explicable):
"""A calculus expression. Today it can andle only expression with numbers later it will be able to manipulate unknown"""
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():
... print(i)
\\frac{ 2 \\times 3 }{ 5 }
\\frac{ 6 }{ 5 }
>>> with Expression.tmp_render():
... for i in exp.simplify():
... i
< Expression [2, 3, '*', 5, '/']>
< Expression [6, 5, '/']>
< Fraction 6 / 5>
>>> with Expression.tmp_render(txt):
... for i in exp.simplify():
... print(i)
2 * 3 / 5
6 / 5
>>> for i in exp.simplify():
... 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()
@classmethod
def random(self, form="", conditions=[], val_min = -10, val_max=10):
@ -81,6 +28,38 @@ class Expression(object):
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
< <class 'pymath.expression.Expression'> [2, 3, 5, '/', '*'] >
< <class 'pymath.expression.Expression'> [2, < Fraction 3 / 5>, '*'] >
< <class 'pymath.expression.Expression'> [2, < Fraction 3 / 5>, '*'] >
< <class 'pymath.expression.Expression'> [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
@ -89,36 +68,35 @@ 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])
elif type(exp) == Expression:
return exp
else:
raise ValueError("Can't build Expression with {} object".format(type(exp)))
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, ), 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, ), methods_attr)(token)
return fake_token
else:
raise ValueError("Unknow type in Expression: {}".format(type(token)))
@ -129,74 +107,29 @@ 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) + ">"
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
return " ".join(["<", str(self.__class__) , str(self.postfix_tokens), ">"])
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
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 +189,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):
@ -297,31 +231,83 @@ class Expression(object):
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):
def __rtruediv__(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])
@ -329,9 +315,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())

View File

@ -5,12 +5,14 @@ from .arithmetic import gcd
from .generic import isNumber
from .operator import op
from .expression import Expression
from .explicable import Explicable
from .render import txt, tex
from copy import copy
__all__ = ['Fraction']
class Fraction(object):
class Fraction(Explicable):
"""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,7 +95,7 @@ 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)
@ -95,10 +111,10 @@ class Fraction(object):
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
@ -119,18 +135,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
[< <class 'pymath.expression.Expression'> [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
[< <class 'pymath.expression.Expression'> [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
[< <class 'pymath.expression.Expression'> [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 +174,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 + number.postfix_tokens + [op.add])
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 +193,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
[< <class 'pymath.expression.Expression'> [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 +221,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 + number.postfix_tokens + [op.sub])
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
[< <class 'pymath.expression.Expression'> [-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 +262,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
[< <class 'pymath.expression.Expression'> [1, 2, '/', 2, 3, '/', '*'] >, [1, 1, 2, '*', '*', 1, 2, '*', 3, '*', '/'], [1, 2, '*', 2, 3, '*', '/'], [2, 6, '/'], < <class 'pymath.expression.Expression'> [2, 6, '/'] >, < <class 'pymath.expression.Expression'> [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
[< <class 'pymath.expression.Expression'> [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 +298,7 @@ class Fraction(object):
denom = [self._denom]
exp = Expression(num + denom + [op.div])
ini_step = Expression(self.postfix_tokens + [other, op.mul])
else:
number = self.convert2fraction(other)
@ -279,28 +322,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 + number.postfix_tokens + [op.mul])
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 + number.postfix_tokens + [op.div])
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 +372,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
[< <class 'pymath.expression.Expression'> [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
[< <class 'pymath.expression.Expression'> [6, 4, '/', 3, '^'] >, [6, 3, '^', 4, 3, '^', '/'], [216, 64, '/'], < <class 'pymath.expression.Expression'> [216, 64, '/'] >, < <class 'pymath.expression.Expression'> [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, op.pw])
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 +449,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):

View File

@ -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.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
@ -127,22 +133,40 @@ 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 """
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] + [")"])
op = flatten_list(["(", op, ")"])
except AttributeError:
# op has not the attribute priority
try:
@ -150,7 +174,10 @@ 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 save_mainOp(obj, mainOp):
"""Create a temporary class build over built-in type to stock the main operation of a calculus
@ -159,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)
@ -183,7 +204,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)
@ -259,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",\
@ -269,6 +315,7 @@ class op(object):
"actions" : ("__add__","__radd__"), \
"txt" : "{op1} + {op2}",\
"tex" : "{op1} + {op2}",\
"_render": _render,\
}
return caract
@ -289,15 +336,40 @@ 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
def r_parenthesis(self, op, str_join=False):
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:
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",\
"priority" : 1, \
"priority" : 2, \
"arity" : 2, \
"actions" : ("__sub__","__rsub__"), \
"txt" : "{op1} - {op2}",\
"tex" : "{op1} - {op2}",\
"l_parenthesis": l_parenthesis,\
"r_parenthesis": r_parenthesis,\
}
return caract
@ -319,7 +391,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:
@ -331,17 +403,21 @@ 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" : "-", \
"name" : "sub1",\
"priority" : 2, \
"priority" : 3, \
"arity" : 1, \
"actions" : "__neg__",\
"txt" : "- {op1}",\
"tex" : "- {op1}",\
"add_parenthesis": add_parenthesis,\
"l_parenthesis": l_parenthesis,\
}
return caract
@ -358,25 +434,55 @@ 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)'
"""
# * 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].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
def _render(self, link, *args):
replacement = {"op"+str(i+1): ' '.join(self.add_parenthesis(op)) for (i,op) in enumerate(args)}
op1 = self.l_parenthesis(args[0], True)
op2 = self.r_parenthesis(args[1], True)
if not self.visibility or args[1][0] == "(" or \
(type(args[1][0]) == str and args[1][0].isalpha()):
ans = "{op1} {op2}".format(**replacement)
ans = save_mainOp(ans, self)
return ans
if not self.is_visible(op1, op2):
ans = "{op1} {op2}".format(op1 = op1, op2 = op2)
else:
ans = link.format(**replacement)
ans = save_mainOp(ans, self)
return ans
ans = link.format(op1 = op1, op2 = op2)
ans = save_mainOp(ans, self)
return ans
caract = {
"operator" : "*", \
@ -387,7 +493,8 @@ class op(object):
"txt" : "{op1} * {op2}",\
"tex" : "{op1} \\times {op2}",\
"visibility": 1,\
"_render": _render
"_render": _render,\
"is_visible": is_visible,\
}
return caract
@ -461,7 +568,7 @@ class op(object):
caract = {
"operator" : "^", \
"name" : "pw",\
"priority" : 5, \
"priority" : 6, \
"arity" : 2, \
"actions" : ("__pow__",""), \
"txt" : "{op1} ^ {op2}",\
@ -484,28 +591,39 @@ 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'))
#print("\t op.can_be_operator('+') :" + str(op.can_be_operator('+')))
#print("\t op.can_be_operator('t') :" + str(op.can_be_operator('t')))
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("-----------------")
from .fraction import Fraction
f = Fraction(1, 2)
print(op.add.__txt__(f.__txt__(),'2'))
print(op.add.__tex__(f.__tex__(),'2'))
print(tex([2, Fraction(1,2), op.mul]))
print("-----------------")
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()
#import doctest
#doctest.testmod()

View File

@ -3,11 +3,13 @@
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
from .generic import spe_zip, expand_list, isNumber, transpose_fill, flatten_list, isPolynom, isNumerand
from .render import txt,tex
from .random_expression import RdExpression
from itertools import chain
from functools import wraps
__all__ = ["Polynom"]
@ -15,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():
@ -25,12 +28,12 @@ def power_cache(fun):
return poly_powered
return cached_fun
class Polynom(object):
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)
@ -41,19 +44,19 @@ class Polynom(object):
/!\ 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):
# 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
@ -61,9 +64,9 @@ class Polynom(object):
# 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, name = name)
def __init__(self, coef = [1], letter = "x" ):
def __init__(self, coefs = [1], letter = "x", name = "P"):
"""Initiate the polynom
:param coef: coefficients of the polynom (ascending degree sorted)
@ -72,24 +75,36 @@ class Polynom(object):
- [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([1,2, 3])._letter
>>> Polynom([0, 0, 3]).mainOp
'*'
>>> Polynom([1, 2, 3])._letter
'x'
>>> Polynom([1, 2, 3], "y")._letter
'y'
>>> Polynom([1, 2, 3], name = "Q").name
'Q'
"""
self.feed_coef(coef)
super(Polynom, self).__init__()
self.feed_coef(coefs)
self._letter = letter
self.name = name
if self.is_monom():
self.mainOp = "*"
self.mainOp = op.mul
else:
self.mainOp = "+"
self.mainOp = op.add
self._isPolynom = 1
@ -98,13 +113,27 @@ class Polynom(object):
: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):
postfix_exp = [value if i==self._letter else i for i in self.postfix]
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]
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
@ -146,12 +175,21 @@ class Polynom(object):
else:
return 0
def give_name(self, name):
self.name = name
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 txt(self.postfix_tokens)
def __tex__(self):
return tex(self.postfix_tokens)
def coef_postfix(self, a, i):
"""Return the postfix display of a coeficient
@ -188,37 +226,45 @@ 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])
>>> p.postfix_tokens
[1]
>>> p = Polynom([0])
>>> p.postfix_tokens
[0]
>>> 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, '+']
"""
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]:
@ -286,7 +332,15 @@ class Polynom(object):
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):
@ -298,8 +352,23 @@ 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
[< Polynom [< <class 'pymath.expression.Expression'> [1, 2, '+'] >, < <class 'pymath.expression.Expression'> [3, 4, '+', 5, '+'] >, 6]>, < Polynom [< <class 'pymath.expression.Expression'> [1, 2, '+'] >, < <class 'pymath.expression.Expression'> [7, 5, '+'] >, 6]>, < Polynom [3, < <class 'pymath.expression.Expression'> [7, 5, '+'] >, 6]>]
"""
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 +380,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,12 +404,37 @@ 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
def derivate(self):
""" Return the derivated polynom
>>> P = Polynom([1, 2, 3])
>>> 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
6 x + 2
"""
derv_coefs = []
for (i,c) in enumerate(self._coef):
derv_coefs += [Expression([i, c, op.mul])]
ans = Polynom(derv_coefs[1:]).simplify()
ans.name = self.name + "'"
return ans
@staticmethod
def postfix_add(numbers):
"""Convert a list of numbers into a postfix addition
@ -375,56 +469,104 @@ class Polynom(object):
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
[< <class 'pymath.expression.Expression'> [3, 'x', 2, '^', '*', 2, 'x', '*', '+', 1, '+', 5, 'x', '*', 4, '+', '+'] >, < Polynom [< <class 'pymath.expression.Expression'> [1, 4, '+'] >, < <class 'pymath.expression.Expression'> [2, 5, '+'] >, 3]>, < Polynom [< <class 'pymath.expression.Expression'> [1, 4, '+'] >, < <class 'pymath.expression.Expression'> [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 + o_poly.postfix_tokens + [op.add])]
ans = p.simplify()
ans.steps = ini_step + ans.steps
return ans
def __radd__(self, other):
return self.__add__(other)
o_poly = self.conv2poly(other)
return o_poly.__add__(self)
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
[< <class 'pymath.expression.Expression'> [3, 'x', 2, '^', '*', 2, 'x', '*', '+', 1, '+', '-'] >]
"""
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
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
[< <class 'pymath.expression.Expression'> [3, 'x', 2, '^', '*', 2, 'x', '*', '+', 1, '+', 6, 'x', 2, '^', '*', 5, 'x', '*', '+', 4, '+', '-'] >, < <class 'pymath.expression.Expression'> [3, 'x', 2, '^', '*', 2, 'x', '*', '+', 1, '+', 6, 'x', 2, '^', '*', '-', 5, 'x', '*', '-', 4, '-', '+'] >, < Polynom [< <class 'pymath.expression.Expression'> [1, -4, '+'] >, < <class 'pymath.expression.Expression'> [2, -5, '+'] >, < <class 'pymath.expression.Expression'> [3, -6, '+'] >]>, < Polynom [< <class 'pymath.expression.Expression'> [1, -4, '+'] >, < <class 'pymath.expression.Expression'> [2, -5, '+'] >, < <class 'pymath.expression.Expression'> [3, -6, '+'] >]>]
"""
o_poly = self.conv2poly(other)
ini_step = [Expression(self.postfix_tokens + o_poly.postfix_tokens + [op.sub])]
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)
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
[[< <class 'pymath.expression.Expression'> [2, 'x', '*', 1, '+', 3, '*'] >], < Polynom [3, < <class 'pymath.expression.Expression'> [2, 3, '*'] >]>, < Polynom [3, < <class 'pymath.expression.Expression'> [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
[[< <class 'pymath.expression.Expression'> [4, 'x', 2, '^', '*', 3, '*'] >], < Polynom [0, 0, < <class 'pymath.expression.Expression'> [4, 3, '*'] >]>, < Polynom [0, 0, < <class 'pymath.expression.Expression'> [4, 3, '*'] >]>]
>>> r = Polynom([0,1])
>>> r*3
[< Polynom [0, 3]>, < Polynom [0, 3]>]
< Polynom [0, 3]>
>>> (r*3).steps
[[< <class 'pymath.expression.Expression'> ['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
[[< <class 'pymath.expression.Expression'> [2, 'x', '*', 1, '+', 4, 'x', 2, '^', '*', '*'] >], < Polynom [0, 0, 4, < <class 'pymath.expression.Expression'> [2, 4, '*'] >]>, < Polynom [0, 0, 4, < <class 'pymath.expression.Expression'> [2, 4, '*'] >]>]
>>> p*r
[< Polynom [0, 1, 2]>, < Polynom [0, 1, 2]>]
< Polynom [0, 1, 2]>
>>> P = Polynom([1,2,3])
>>> Q = Polynom([4,5,6])
>>> P*Q
< Polynom [4, 13, 28, 27, 18]>
"""
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 = []
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:
@ -435,22 +577,21 @@ class Polynom(object):
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)
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)
@ -463,39 +604,45 @@ class Polynom(object):
>>> 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
[< <class 'pymath.expression.Expression'> [3, 'x', 2, '^', '*', 2, '^'] >, < Polynom [0, 0, 0, 0, < <class 'pymath.expression.Expression'> [3, 2, '^'] >]>, < Polynom [0, 0, 0, 0, < <class 'pymath.expression.Expression'> [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
[< <class 'pymath.expression.Expression'> [2, 'x', '*', 1, '+', 2, '^'] >, [< <class 'pymath.expression.Expression'> [2, 'x', '*', 1, '+', 2, 'x', '*', 1, '+', '*'] >], < Polynom [1, < <class 'pymath.expression.Expression'> [2, 2, '+'] >, < <class 'pymath.expression.Expression'> [2, 2, '*'] >]>, < Polynom [1, < <class 'pymath.expression.Expression'> [2, 2, '+'] >, < <class 'pymath.expression.Expression'> [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]>
>>> p = Polynom([1,2,3])
>>> p**2
< Polynom [1, 4, 10, 12, 9]>
"""
if not type(power):
raise ValueError("Can't raise Polynom to {} power".format(str(power)))
steps = []
ini_step = [Expression(self.postfix_tokens + [power, op.pw])]
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)
@ -513,19 +660,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 ------")
@ -539,16 +686,27 @@ def test(p,q):
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)
#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)
# 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)
#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)
# -----------------------------

228
pymath/polynomDeg2.py Normal file
View File

@ -0,0 +1,228 @@
#!/usr/bin/env python
# encoding: utf-8
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):
""" Degree 2 polynoms
Child of Polynom with some extra tools
"""
@classmethod
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)
: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, name = name)
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, name = name)
@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
>>> P = Polynom_deg2([1,2,3])
>>> P.delta
-8
>>> for i in P.delta.explain():
... print(i)
2^{ 2 } - 4 \\times 3 \\times 1
4 - 4 \\times 3
4 - 12
-8
"""
return Expression([self.b, 2, op.pw, 4, self.a, self.c, op.mul, op.mul, op.sub]).simplify()
@property
def alpha(self):
""" Compute alpha the abcisse of the extremum
>>> P = Polynom_deg2([1,2,3])
>>> P.alpha
< 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 }
"""
return Expression([self.b, op.sub1, 2, self.a, op.mul, op.div]).simplify()
@property
def beta(self):
""" Compute beta the extremum of self
>>> P = Polynom_deg2([1,2,3])
>>> P.beta
< 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).simplify()
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
# 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:
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 = [round(-self.b /(2*self.a), after_coma)]
else:
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
>>> P = Polynom_deg2([2, 5, 2])
>>> print(P.tbl_sgn())
\\tkzTabLine{, +, z, -, z , +,}
>>> P = Polynom_deg2([2, 1, -2])
>>> print(P.tbl_sgn())
\\tkzTabLine{, -, z, +, z , -,}
>>> P = Polynom_deg2([1, 2, 1])
>>> print(P.tbl_sgn())
\\tkzTabLine{, +, z, +,}
>>> P = Polynom_deg2([0, 0, -2])
>>> print(P.tbl_sgn())
\\tkzTabLine{, -, z, -,}
>>> P = Polynom_deg2([1, 0, 1])
>>> print(P.tbl_sgn())
\\tkzTabLine{, +,}
>>> P = Polynom_deg2([-1, 0, -1])
>>> print(P.tbl_sgn())
\\tkzTabLine{, -,}
"""
if self.delta > 0:
if self.a > 0:
return "\\tkzTabLine{, +, z, -, z , +,}"
else:
return "\\tkzTabLine{, -, z, +, z , -,}"
elif self.delta == 0:
if self.a > 0:
return "\\tkzTabLine{, +, z, +,}"
else:
return "\\tkzTabLine{, -, z, -,}"
else:
if self.a > 0:
return "\\tkzTabLine{, +,}"
else:
return "\\tkzTabLine{, -,}"
def tbl_variation(self, limits = False):
"""Return the variation line for tkzTabVar
:param limit: Display or not limits in tabular
>>> 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$}}
"""
beta = self.beta
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) + "$}, -/{}}"
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)
import doctest
doctest.testmod()
# -----------------------------
# Reglages pour 'vim'
# vim:set autoindent expandtab tabstop=4 shiftwidth=4:
# cursor: 16 del

View File

@ -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)

View File

@ -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, '-', '-']
# -----------------------------

View File

@ -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()

33
test/test_operator.py Normal file
View File

@ -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

View File

@ -33,19 +33,19 @@ class TestPolynom(unittest.TestCase):
def test_eval_base(self):
p = Polynom([1, 2])
self.assertEqual(p(3).simplified(), 7)
self.assertEqual(p(3), 7)
def test_eval_const(self):
p = Polynom([1])
self.assertEqual(p(3).simplified(), 1)
self.assertEqual(p(3), 1)
def test_eval_const_neg(self):
p = Polynom([-1])
self.assertEqual(p(3).simplified(), -1)
self.assertEqual(p(3), -1)
def test_eval_poly(self):
p = Polynom([1, 2])
self.assertEqual(p("1+h").simplified(), Polynom([3,2], "h"))
self.assertEqual(p("h+1"), Polynom([3,2], "h"))
#def test_print(self):
# p = Polynom([1,2,3])
@ -71,97 +71,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)

31
test/test_polynomDeg2.py Normal file
View File

@ -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

View File

@ -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,41 +23,141 @@ 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_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)
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 ) ( 6 x^{ 2 } + 5 x + 4 )",
]
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],\
[ 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)
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)
@ -81,27 +182,36 @@ 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)
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])
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)