2013-08-09 09:35:14 +00:00
#!/usr/bin/env python
# encoding: utf-8
2016-02-13 04:04:08 +00:00
# debuging
2015-04-21 15:31:56 +00:00
#from debug.tools import report
2014-12-22 13:14:37 +00:00
from . generic import Stack , flatten_list , expand_list , isNumber , isOperator , isNumerand
2014-11-09 11:06:31 +00:00
from . str2tokens import str2tokens
2014-11-14 15:48:38 +00:00
from . operator import op
2016-02-13 16:47:35 +00:00
from . explicable import Explicable , Explicable_int , Explicable_float
2013-11-01 21:58:42 +00:00
2014-12-19 15:45:22 +00:00
from . random_expression import RdExpression
2015-02-27 16:46:16 +00:00
__all__ = [ ' Expression ' ]
2013-08-09 09:35:14 +00:00
2016-02-14 17:22:04 +00:00
def pstf_factory ( pstf_tokens ) :
""" Factory which tranform postfix tokens list into an Expression or the simpliest object type ready to be rendered
: param pstf_tokens : a postfix tokens list
: returns : the object
>> > from . operator import op
>> > pstf_t = [ 2 , 3 , op . add ]
>> > pstf_factory ( pstf_t )
< < class ' pymath . calculus . expression . Expression ' > [2, 3, ' + ' ] >
>> > pstf_factory ( [ 2 ] )
2
>> > type ( pstf_factory ( [ 2 ] ) )
< class ' pymath . calculus . explicable . Explicable_int ' >
>> > pstf_factory ( [ 2.45 ] )
2.45
>> > type ( pstf_factory ( [ 2.45 ] ) )
< class ' pymath . calculus . explicable . Explicable_float ' >
>> > from . fraction import Fraction
>> > f = Fraction ( 1 , 2 )
>> > pstf_factory ( [ f ] )
< Fraction 1 / 2 >
"""
try :
l_pstf_token = len ( pstf_tokens )
except TypeError :
if isinstance ( pstf_tokens [ 0 ] , int ) :
return Explicable_int ( pstf_tokens [ 0 ] )
elif isinstance ( pstf_tokens [ 0 ] , float ) :
return Explicable_float ( pstf_tokens [ 0 ] )
elif hasattr ( pstf_tokens [ 0 ] , ' STR_RENDER ' ) :
return pstf_tokens [ 0 ]
else :
return Expression ( self )
else :
if l_pstf_token == 1 :
if isinstance ( pstf_tokens [ 0 ] , int ) :
return Explicable_int ( pstf_tokens [ 0 ] )
elif isinstance ( pstf_tokens [ 0 ] , float ) :
return Explicable_float ( pstf_tokens [ 0 ] )
elif hasattr ( pstf_tokens [ 0 ] , ' STR_RENDER ' ) :
return pstf_tokens [ 0 ]
else :
return Expression ( self )
else :
return Expression ( pstf_tokens )
2016-02-13 04:04:08 +00:00
2015-02-27 16:46:16 +00:00
class Expression ( Explicable ) :
""" A calculus expression. Today it can andle only expression with numbers later it will be able to manipulate unknown """
2014-11-21 14:49:43 +00:00
@classmethod
2016-02-13 04:04:08 +00:00
def random ( self , form = " " , conditions = [ ] , val_min = - 10 , val_max = 10 ) :
2015-02-27 16:46:16 +00:00
""" Create a random expression from form and with conditions
: param form : the form of the expression ( / ! \ variables need to be in brackets { } )
: param conditions : condition on variables ( / ! \ variables need to be in brackets { } )
: param val_min : min value for generate variables
: param val_max : max value for generate variables
"""
random_generator = RdExpression ( form , conditions )
return Expression ( random_generator ( val_min , val_max ) )
2013-08-09 11:08:24 +00:00
2014-12-28 09:25:21 +00:00
@classmethod
2016-02-14 17:22:04 +00:00
def tmp_render ( cls , render = lambda _ , x : pstf_factory ( x ) ) :
# def tmp_render(cls, render=lambda _, x: Expression(x)):
2015-02-27 16:46:16 +00:00
""" Same ad tmp_render for Renderable but default render is Expression
2014-12-28 09:25:21 +00:00
>> > exp = Expression ( " 2*3/5 " )
>> > print ( exp )
2015-02-27 16:46:16 +00:00
2 \\times \\frac { 3 } { 5 }
2015-02-26 18:02:20 +00:00
>> > for i in exp . simplify ( ) . explain ( ) :
2014-12-28 09:25:21 +00:00
. . . print ( i )
2015-02-27 16:46:16 +00:00
2 \\times \\frac { 3 } { 5 }
2015-04-06 15:57:13 +00:00
\\frac { 3 } { 5 } \\times 2
\\frac { 3 \\times 2 } { 5 }
2014-12-28 09:25:21 +00:00
\\frac { 6 } { 5 }
>> > with Expression . tmp_render ( ) :
2015-02-26 18:02:20 +00:00
. . . for i in exp . simplify ( ) . explain ( ) :
2014-12-28 09:25:21 +00:00
. . . i
2016-01-07 16:34:23 +00:00
< < class ' pymath . calculus . expression . Expression ' > [2, 3, 5, ' / ' , ' * ' ] >
< < class ' pymath . calculus . expression . Expression ' > [2, < Fraction 3 / 5>, ' * ' ] >
2016-02-14 17:22:04 +00:00
< < class ' pymath . calculus . expression . Expression ' > [< Fraction 3 / 5>, 2, ' * ' ] >
2016-01-07 16:34:23 +00:00
< < class ' pymath . calculus . expression . Expression ' > [3, 2, ' * ' , 5, ' / ' ] >
< < class ' pymath . calculus . expression . Expression ' > [6, 5, ' / ' ] >
2015-02-27 16:46:16 +00:00
>> > from . render import txt
2014-12-28 09:25:21 +00:00
>> > with Expression . tmp_render ( txt ) :
2015-02-26 18:02:20 +00:00
. . . for i in exp . simplify ( ) . explain ( ) :
2014-12-28 09:25:21 +00:00
. . . print ( i )
2 * 3 / 5
2015-04-06 15:57:13 +00:00
3 / 5 * 2
( 3 * 2 ) / 5
2014-12-28 09:25:21 +00:00
6 / 5
2015-02-26 18:02:20 +00:00
>> > for i in exp . simplify ( ) . explain ( ) :
2014-12-28 09:25:21 +00:00
. . . print ( i )
2015-02-27 16:46:16 +00:00
2 \\times \\frac { 3 } { 5 }
2015-04-06 15:57:13 +00:00
\\frac { 3 } { 5 } \\times 2
\\frac { 3 \\times 2 } { 5 }
2014-12-28 09:25:21 +00:00
\\frac { 6 } { 5 }
"""
2015-02-27 16:46:16 +00:00
return super ( Expression , cls ) . tmp_render ( render )
2014-12-19 15:45:22 +00:00
2014-12-20 17:48:51 +00:00
def __new__ ( cls , exp ) :
""" Create Expression objects
2013-08-09 09:35:14 +00:00
2014-11-08 17:15:04 +00:00
: param exp : the expression . It can be a string or a list of postfix tokens .
2014-12-20 17:48:51 +00:00
2013-08-09 09:35:14 +00:00
"""
2014-12-20 17:48:51 +00:00
expression = object . __new__ ( cls )
2015-06-21 18:43:38 +00:00
2016-02-13 04:04:08 +00:00
if isinstance ( exp , str ) :
2016-02-13 03:29:26 +00:00
expression . postfix_tokens = str2tokens ( exp )
2015-06-21 18:43:38 +00:00
2016-02-13 04:04:08 +00:00
elif isinstance ( exp , list ) :
2016-02-13 07:30:19 +00:00
# Ici on ne peut convertir les "+-*/..." en opérateur que s'ils sont
2016-02-13 04:04:08 +00:00
# d'arité 2.
exp_mod_op = [
2016-02-13 07:30:19 +00:00
op . get_op ( i ) if op . can_be_operator ( i ) else i for i in exp
]
2016-02-13 04:04:08 +00:00
expression . postfix_tokens = flatten_list (
2016-02-13 07:30:19 +00:00
[ tok . postfix_tokens if Expression . isExpression ( tok ) else tok for tok in exp_mod_op ]
)
2015-06-21 18:43:38 +00:00
2016-02-13 04:04:08 +00:00
elif isinstance ( exp , Expression ) :
2016-02-13 03:29:26 +00:00
return exp
2015-06-21 18:43:38 +00:00
elif isNumerand ( exp ) :
expression . postfix_tokens = [ exp ]
2014-12-20 17:48:51 +00:00
else :
2016-02-13 04:04:08 +00:00
raise ValueError (
" Can ' t build Expression with {} object " . format (
2016-02-13 07:30:19 +00:00
type ( exp )
)
)
2014-11-11 20:45:24 +00:00
2016-02-13 17:54:13 +00:00
# if len(expression.postfix_tokens) == 1:
# token = expression.postfix_tokens[0]
2015-06-21 18:43:38 +00:00
2016-02-13 17:54:13 +00:00
# if isinstance(token, Explicable_int) or isinstance(token, int):
# return Explicable_int(token)
2015-06-21 18:43:38 +00:00
2016-02-13 17:54:13 +00:00
# # TODO: J'en arrive au soucis même soucis qu'avec les fractions qui une fois simplifiée devrait être des Explicable_int. Mais comment on ne redéfini pas les opérations, ce sont les opérations des int qui se font et donc on perd toute l'historique. |sam. févr. 13 18:57:45 EAT 2016
# if isinstance(token, Explicable_float) or isinstance(token, float):
# return Explicable_float(token)
2016-02-13 16:47:35 +00:00
2016-02-13 17:54:13 +00:00
# elif hasattr(token, 'simplify') and hasattr(token, 'explain'):
# ans = expression.postfix_tokens[0]
# return ans
2014-12-22 08:38:51 +00:00
2016-02-13 17:54:13 +00:00
# elif isinstance(token, str):
# from .polynom import Polynom
# return Polynom([0,1], letter = token)
2015-06-21 18:43:38 +00:00
2016-02-13 17:54:13 +00:00
# else:
# raise ValueError(
# "Unknow token type in Expression: {}".format(
# type(token)))
2014-12-21 18:03:44 +00:00
2016-02-13 17:54:13 +00:00
# else:
# expression._isExpression = 1
# return expression
expression . _isExpression = 1
return expression
2013-11-01 21:58:42 +00:00
2014-01-17 11:48:48 +00:00
def __str__ ( self ) :
2014-08-29 14:35:38 +00:00
"""
Overload str
2015-02-26 18:02:20 +00:00
If you want to changer render use Expression . set_render ( . . . ) or use tmp_render context manager .
2014-08-29 14:35:38 +00:00
"""
return self . STR_RENDER ( self . postfix_tokens )
2014-01-17 11:48:48 +00:00
2014-11-14 15:20:02 +00:00
def __repr__ ( self ) :
2016-02-13 04:04:08 +00:00
return " " . join ( [ " < " , str ( self . __class__ ) ,
str ( self . postfix_tokens ) , " > " ] )
2014-11-14 15:20:02 +00:00
2014-11-21 14:49:43 +00:00
def simplify ( self ) :
2015-02-26 18:02:20 +00:00
""" Compute entirely the expression and return the result with .steps attribute """
2016-02-13 17:54:13 +00:00
try :
self . compute_exp ( )
except ComputeError :
try :
self . simplified = self . postfix_tokens [ 0 ] . simplify ( )
except AttributeError :
if isinstance ( self . postfix_tokens [ 0 ] , int ) :
self . simplified = Explicable_int ( self . postfix_tokens [ 0 ] )
elif isinstance ( self . postfix_tokens [ 0 ] , float ) :
self . simplified = Explicable_float ( self . postfix_tokens [ 0 ] )
else :
self . simplified = self
2015-02-26 18:02:20 +00:00
2016-02-13 17:54:13 +00:00
else :
self . simplified = self . child . simplify ( )
self . simplified . steps = self . child . steps + self . simplified . steps
2015-02-26 18:02:20 +00:00
return self . simplified
2013-11-01 21:58:42 +00:00
def compute_exp ( self ) :
2015-02-26 18:02:20 +00:00
""" Create self.child with and stock steps in it """
2016-02-13 17:54:13 +00:00
if len ( self . postfix_tokens ) == 1 :
raise ComputeError ( " Nothing to compute in {} " . format ( self . postfix_tokens ) )
else :
ini_step = Expression ( self . postfix_tokens )
2013-11-01 21:58:42 +00:00
2016-02-13 17:54:13 +00:00
tokenList = self . postfix_tokens . copy ( )
tmpTokenList = [ ]
2013-11-01 21:58:42 +00:00
2016-02-13 17:54:13 +00:00
while len ( tokenList ) > 2 :
# on va chercher les motifs du genre A B +, quand l'operateur est
# d'arité 2, pour les calculer
if isNumerand ( tokenList [ 0 ] ) and isNumerand ( tokenList [ 1 ] ) \
and isOperator ( tokenList [ 2 ] ) and tokenList [ 2 ] . arity == 2 :
2016-02-13 03:29:26 +00:00
2016-02-13 17:54:13 +00:00
# S'il y a une opération à faire
op1 = tokenList [ 0 ]
op2 = tokenList [ 1 ]
operator = tokenList [ 2 ]
2016-02-13 03:29:26 +00:00
2016-02-13 17:54:13 +00:00
res = operator ( op1 , op2 )
2013-11-01 21:58:42 +00:00
2016-02-13 17:54:13 +00:00
tmpTokenList . append ( res )
2013-11-01 21:58:42 +00:00
2016-02-13 17:54:13 +00:00
# Comme on vient de faire le calcul, on peut détruire aussi les
# deux prochains termes
del tokenList [ 0 : 3 ]
2014-11-02 07:19:31 +00:00
2016-02-13 17:54:13 +00:00
# Et les motifs du gens A -, quand l'operateur est d'arité 1
elif isNumerand ( tokenList [ 0 ] ) \
and isOperator ( tokenList [ 1 ] ) and tokenList [ 1 ] . arity == 1 :
# S'il y a une opération à faire
op1 = tokenList [ 0 ]
operator = tokenList [ 1 ]
res = operator ( op1 )
tmpTokenList . append ( res )
2016-02-13 03:29:26 +00:00
2016-02-13 17:54:13 +00:00
# Comme on vient de faire le calcul, on peut détruire aussi les
# deux prochains termes
del tokenList [ 0 : 2 ]
else :
tmpTokenList . append ( tokenList [ 0 ] )
del tokenList [ 0 ]
if len ( tokenList ) == 2 and isNumerand ( tokenList [ 0 ] ) \
and isOperator ( tokenList [ 1 ] ) and tokenList [ 1 ] . arity == 1 :
# S'il reste deux éléments dont un operation d'arité 1
2014-11-02 07:19:31 +00:00
op1 = tokenList [ 0 ]
operator = tokenList [ 1 ]
res = operator ( op1 )
tmpTokenList . append ( res )
2016-02-13 04:04:08 +00:00
# Comme on vient de faire le calcul, on peut détruire aussi les
# deux prochains termes
2014-11-02 07:19:31 +00:00
del tokenList [ 0 : 2 ]
2016-02-13 17:54:13 +00:00
tmpTokenList + = tokenList
self . child = Expression ( tmpTokenList )
2014-12-21 18:03:44 +00:00
2016-02-13 17:54:13 +00:00
steps = self . develop_steps ( tmpTokenList )
2014-12-21 18:03:44 +00:00
2016-02-13 17:54:13 +00:00
if self . child . postfix_tokens == ini_step . postfix_tokens :
self . child . steps = steps
else :
self . child . steps = [ ini_step ] + steps
2015-04-05 15:04:04 +00:00
def develop_steps ( self , tokenList ) :
""" From a list of tokens, it develops steps of each tokens """
tmp_steps = [ ]
2016-02-09 07:14:54 +00:00
for t in tokenList :
try :
with Expression . tmp_render ( ) :
2015-04-05 15:04:04 +00:00
tmp_steps . append ( [ i for i in t . explain ( ) ] )
2016-02-09 07:14:54 +00:00
except AttributeError :
tmp_steps . append ( [ t ] )
2015-04-20 19:21:19 +00:00
if max ( [ len ( i ) for i in tmp_steps ] ) == 1 :
2015-04-07 04:44:20 +00:00
# Cas où rien n'a dû être expliqué.
2015-04-06 15:57:13 +00:00
return [ ]
else :
tmp_steps = expand_list ( tmp_steps ) [ : - 1 ]
steps = [ Expression ( s ) for s in tmp_steps ]
return steps
2013-11-01 11:42:42 +00:00
2014-12-20 17:48:51 +00:00
@classmethod
2014-11-11 20:45:24 +00:00
def isExpression ( self , other ) :
try :
other . _isExpression
except AttributeError :
2016-02-13 04:04:08 +00:00
return 0
return 1
2014-11-11 20:45:24 +00:00
2014-12-20 15:52:45 +00:00
# -----------
# Expression act as container from self.postfix_tokens
def __getitem__ ( self , index ) :
return self . postfix_tokens [ index ]
def __setitem__ ( self , index , value ) :
self . postfix_tokens [ index ] = value
2014-11-14 15:20:02 +00:00
# -----------
# Some math manipulations
2014-11-14 15:48:38 +00:00
def operate ( self , other , operator ) :
2016-02-13 04:04:08 +00:00
if isinstance ( other , Expression ) :
return Expression (
self . postfix_tokens +
other . postfix_tokens +
[ operator ] )
elif isinstance ( other , list ) :
2014-11-14 15:48:38 +00:00
return Expression ( self . postfix_tokens + other + [ operator ] )
else :
return Expression ( self . postfix_tokens + [ other ] + [ operator ] )
2016-02-13 03:29:26 +00:00
2014-11-14 15:48:38 +00:00
def roperate ( self , other , operator ) :
2016-02-13 04:04:08 +00:00
if isinstance ( other , Expression ) :
return Expression (
other . postfix_tokens +
self . postfix_tokens +
[ operator ] )
elif isinstance ( other , list ) :
2014-11-14 15:48:38 +00:00
return Expression ( other + self . postfix_tokens + [ operator ] )
else :
return Expression ( [ other ] + self . postfix_tokens + [ operator ] )
def __add__ ( self , other ) :
2015-02-27 10:38:17 +00:00
""" 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 , ' + ' , ' + ' ]
"""
2014-11-14 15:48:38 +00:00
return self . operate ( other , op . add )
def __radd__ ( self , other ) :
return self . roperate ( other , op . add )
def __sub__ ( self , other ) :
2015-02-27 10:38:17 +00:00
""" 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 , ' + ' , ' - ' ]
"""
2014-11-14 15:48:38 +00:00
return self . operate ( other , op . sub )
def __rsub__ ( self , other ) :
return self . roperate ( other , op . sub )
2014-11-11 20:45:24 +00:00
2014-11-14 15:48:38 +00:00
def __mul__ ( self , other ) :
2015-02-27 10:38:17 +00:00
""" 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 , ' + ' , ' * ' ]
"""
2014-11-14 15:48:38 +00:00
return self . operate ( other , op . mul )
def __rmul__ ( self , other ) :
return self . roperate ( other , op . mul )
2015-02-27 10:38:17 +00:00
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 , ' + ' , ' / ' ]
2016-02-13 03:29:26 +00:00
>> >
2015-02-27 10:38:17 +00:00
"""
2014-11-14 15:48:38 +00:00
return self . operate ( other , op . div )
2015-02-27 15:56:43 +00:00
def __rtruediv__ ( self , other ) :
2014-11-14 15:48:38 +00:00
return self . roperate ( other , op . div )
def __pow__ ( self , other ) :
2015-02-27 10:38:17 +00:00
return self . operate ( other , op . pw )
def __xor__ ( self , other ) :
return self . operate ( other , op . pw )
2014-11-14 15:48:38 +00:00
def __neg__ ( self ) :
return Expression ( self . postfix_tokens + [ op . sub1 ] )
2016-02-13 03:29:26 +00:00
2016-02-14 17:22:04 +00:00
2016-02-13 17:54:13 +00:00
class ExpressionError ( Exception ) :
pass
class ComputeError ( Exception ) :
pass
2013-11-01 11:42:42 +00:00
2013-11-02 15:47:47 +00:00
if __name__ == ' __main__ ' :
2015-04-21 17:48:05 +00:00
print ( ' \n ' )
A = Expression ( " ( -8 x + 8 ) ( -8 - ( -6 x ) ) " )
Ar = A . simplify ( )
for i in Ar . explain ( ) :
print ( i )
2016-02-13 04:04:08 +00:00
# print("------------")
# for i in Ar.explain():
2015-04-20 19:21:19 +00:00
# print(i)
2014-01-17 13:57:45 +00:00
2016-02-13 04:04:08 +00:00
# print(type(Ar))
2014-01-15 15:54:33 +00:00
2016-02-13 04:04:08 +00:00
# print('\n-----------')
2015-04-21 17:48:05 +00:00
#A = Expression("-6 / 3 + 10 / -5")
2015-04-21 16:10:14 +00:00
#Ar = A.simplify()
2016-02-13 04:04:08 +00:00
# for i in Ar.explain():
2015-04-21 16:10:14 +00:00
# print(i)
2014-12-19 15:45:22 +00:00
2016-02-13 04:04:08 +00:00
# print('\n-----------')
2015-04-21 17:48:05 +00:00
#A = Expression("1/3 + 4/6")
#Ar = A.simplify()
2016-02-13 04:04:08 +00:00
# for i in Ar.explain():
2015-04-21 17:48:05 +00:00
# print(i)
2015-04-06 15:57:13 +00:00
2015-04-21 17:48:05 +00:00
#import doctest
2016-02-13 04:04:08 +00:00
# doctest.testmod()
2013-11-02 15:47:47 +00:00
2013-08-09 09:35:14 +00:00
# -----------------------------
# Reglages pour 'vim'
# vim:set autoindent expandtab tabstop=4 shiftwidth=4:
2016-02-13 03:29:26 +00:00
# cursor: 16 del