Merge branch 'dev' into 2nd_deg

This commit is contained in:
Lafrite
2015-03-06 14:36:17 +01:00
11 changed files with 803 additions and 464 deletions

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

@@ -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 .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,7 +28,7 @@ def power_cache(fun):
return poly_powered
return cached_fun
class Polynom(object):
class Polynom(Explicable):
"""Docstring for Polynom. """
@@ -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
@@ -82,6 +85,7 @@ class Polynom(object):
>>> Polynom([1, 2, 3], "y")._letter
'y'
"""
super(Polynom, self).__init__()
self.feed_coef(coef)
self._letter = letter
@@ -100,9 +104,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 +151,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,37 +198,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 +304,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 +324,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 +352,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 +376,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
@@ -375,53 +419,98 @@ 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]>
"""
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 = []
@@ -444,13 +533,11 @@ class Polynom(object):
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)
@@ -463,39 +550,42 @@ 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]>
"""
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 +603,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 +629,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([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)
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)
# -----------------------------

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)