2013-08-09 09:35:14 +00:00
#!/usr/bin/env python
# encoding: utf-8
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
2015-02-26 18:02:20 +00:00
from . render import txt , tex
from . explicable import Explicable
2013-11-01 21:58:42 +00:00
2014-12-19 15:45:22 +00:00
from . random_expression import RdExpression
2015-02-26 18:02:20 +00:00
__all__ = [ ' Expression ' , ' Renderable ' ]
2013-08-09 09:35:14 +00:00
2015-02-26 18:02:20 +00:00
class Renderable ( object ) :
2014-08-29 14:35:38 +00:00
STR_RENDER = tex
2014-11-21 14:49:43 +00:00
DEFAULT_RENDER = tex
@classmethod
def set_render ( cls , render ) :
cls . STR_RENDER = render
2014-12-21 17:22:33 +00:00
@classmethod
2015-02-26 18:02:20 +00:00
def get_render ( cls ) :
2014-12-21 17:22:33 +00:00
return cls . STR_RENDER
2014-11-21 14:49:43 +00:00
@classmethod
2014-12-20 15:43:58 +00:00
def set_default_render ( cls ) :
2014-11-21 14:49:43 +00:00
cls . set_render ( cls . DEFAULT_RENDER )
2013-08-09 11:08:24 +00:00
2014-12-28 09:25:21 +00:00
@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 }
2015-02-26 18:02:20 +00:00
>> > for i in exp . simplify ( ) . explain ( ) :
2014-12-28 09:25:21 +00:00
. . . print ( i )
\\frac { 2 \\times 3 } { 5 }
\\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
< Expression [ 2 , 3 , ' * ' , 5 , ' / ' ] >
< Expression [ 6 , 5 , ' / ' ] >
< Fraction 6 / 5 >
>> > 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
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 )
\\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 ( )
2015-02-26 18:02:20 +00:00
class Expression ( Explicable , Renderable ) :
""" A calculus expression. Today it can andle only expression with numbers later it will be able to manipulate unknown """
2014-12-28 09:25:21 +00:00
2014-12-19 15:45:22 +00:00
@classmethod
def random ( self , form = " " , conditions = [ ] , val_min = - 10 , val_max = 10 ) :
""" 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 ) )
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 )
2013-11-01 21:58:42 +00:00
if type ( exp ) == str :
2015-02-26 18:02:20 +00:00
expression . postfix_tokens = str2tokens ( exp )
2013-11-01 21:58:42 +00:00
elif type ( exp ) == list :
2014-12-20 17:48:51 +00:00
expression . postfix_tokens = flatten_list ( [ tok . postfix_tokens if Expression . isExpression ( tok ) else tok for tok in exp ] )
else :
raise ValueError ( " Can ' t build Expression with {} object " . format ( type ( exp ) ) )
2014-11-11 20:45:24 +00:00
2014-12-20 17:48:51 +00:00
if len ( expression . postfix_tokens ) == 1 :
2014-12-22 08:38:51 +00:00
token = expression . postfix_tokens [ 0 ]
2015-02-26 18:02:20 +00:00
if hasattr ( token , ' simplify ' ) and hasattr ( token , ' explain ' ) :
2014-12-22 08:38:51 +00:00
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
2015-02-26 18:02:20 +00:00
simplify = lambda x : x
2014-12-22 08:38:51 +00:00
is_number = True
2015-02-26 18:02:20 +00:00
methods_attr = { ' simplify ' : simplify , ' isNumber ' : is_number , ' postfix_tokens ' : [ token ] }
fake_token = type ( ' fake_int ' , ( int , Explicable , Renderable ) , methods_attr ) ( token )
2014-12-22 10:21:58 +00:00
return fake_token
elif type ( token ) == str :
2015-02-26 18:02:20 +00:00
# TODO: Pourquoi ne pas créer directement un polynom ici? |jeu. févr. 26 18:59:24 CET 2015
2014-12-22 10:21:58 +00:00
# On crée un faux str en ajoutant la méthode simplify et simplified et la caractérisique isNumber
simplify = lambda x : [ x ]
is_polynom = True
2015-02-26 18:02:20 +00:00
methods_attr = { ' simplify ' : simplify , ' _isPolynom ' : is_polynom , ' postfix_tokens ' : [ token ] }
fake_token = type ( ' fake_str ' , ( str , Explicable , Renderable ) , methods_attr ) ( token )
2014-12-22 08:38:51 +00:00
return fake_token
else :
raise ValueError ( " Unknow type in Expression: {} " . format ( type ( token ) ) )
2014-12-21 18:03:44 +00:00
2014-12-20 17:48:51 +00:00
else :
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 ) :
2015-02-26 18:02:20 +00:00
return " " . join ( [ " < " , self . __class__ , str ( self . postfix_tokens ) , " > " ] )
2014-11-14 15:20:02 +00:00
2015-02-26 18:02:20 +00:00
#def __str__(self):
# """
# Overload str
# If you want to changer render use Expression.set_render(...)
# """
# return self.STR_RENDER(self.postfix_tokens)
2014-01-17 11:48:48 +00:00
2015-02-26 18:02:20 +00:00
#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)
2014-01-17 11:48:48 +00:00
2013-11-01 21:58:42 +00:00
## ---------------------
## Mechanism functions
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 """
2014-12-21 18:13:52 +00:00
self . compute_exp ( )
2015-02-26 18:02:20 +00:00
self . simplified = self . child . simplify ( )
2014-12-20 17:48:51 +00:00
try :
2015-02-26 18:02:20 +00:00
self . simplified . steps = self . child . steps + self . simplified . steps
2014-12-20 17:48:51 +00:00
except AttributeError :
2015-02-26 18:02:20 +00:00
pass
return self . simplified
# TODO: À changer |jeu. févr. 26 17:18:49 CET 2015
# if not self.can_go_further():
# yield self.STR_RENDER(self.postfix_tokens)
# else:
# self.compute_exp()
# old_s = ''
# for s in self.steps:
# new_s = self.STR_RENDER(s)
# # Astuce pour éviter d'avoir deux fois la même étape (par exemple pour la transfo d'une division en fraction)
# if new_s != old_s:
# old_s = new_s
# yield new_s
# if Expression.isExpression(self.child):
# for s in self.child.simplify():
# if old_s != s:
# old_s = s
# yield s
# else:
# for s in self.child.simplify():
# new_s = self.STR_RENDER([s])
# # Astuce pour éviter d'avoir deux fois la même étape (par exemple pour la transfo d'une division en fraction)
# if new_s != old_s:
# old_s = new_s
# yield new_s
# if old_s != self.STR_RENDER([self.child]):
# yield self.STR_RENDER([self.child])
#def simplified(self):
# """ Get the simplified version of the expression """
# self.compute_exp()
# try:
# return self.child.simplified()
# except AttributeError:
# return self.child
# TODO: Normalement ne devrait plus être necessaire. Il faudra par contre s'assurer qu'il soit impossible de créer des Expressions avec une seul élément |jeu. févr. 26 17:26:28 CET 2015
# def can_go_further(self):
# """Check whether it's a last step or not. If not create self.child the next expression.
# :returns: 1 if it's not the last step, 0 otherwise
# """
# if len(self.postfix_tokens) == 1:
# return 0
# else:
# return 1
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 """
child_steps = [ self . postfix_tokens ]
2013-11-01 21:58:42 +00:00
tokenList = self . postfix_tokens . copy ( )
tmpTokenList = [ ]
while len ( tokenList ) > 2 :
2014-11-08 17:15:04 +00:00
# on va chercher les motifs du genre A B +, quand l'operateur est d'arité 2, pour les calculer
2014-12-22 13:14:37 +00:00
if isNumerand ( tokenList [ 0 ] ) and isNumerand ( tokenList [ 1 ] ) \
2014-11-08 17:15:04 +00:00
and isOperator ( tokenList [ 2 ] ) and tokenList [ 2 ] . arity == 2 :
2013-11-01 21:58:42 +00:00
# S'il y a une opération à faire
op1 = tokenList [ 0 ]
op2 = tokenList [ 1 ]
2014-11-02 07:19:31 +00:00
operator = tokenList [ 2 ]
2014-12-02 13:31:27 +00:00
2014-11-02 07:19:31 +00:00
res = operator ( op1 , op2 )
2013-11-01 21:58:42 +00:00
tmpTokenList . append ( res )
# 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
2014-11-11 20:45:24 +00:00
# Et les motifs du gens A -, quand l'operateur est d'arité 1
2014-12-22 13:14:37 +00:00
elif isNumerand ( tokenList [ 0 ] ) \
2014-11-11 20:45:24 +00:00
and isOperator ( tokenList [ 1 ] ) and tokenList [ 1 ] . arity == 1 :
2014-11-02 07:19:31 +00:00
# S'il y a une opération à faire
op1 = tokenList [ 0 ]
operator = tokenList [ 1 ]
res = operator ( op1 )
tmpTokenList . append ( res )
# Comme on vient de faire le calcul, on peut détruire aussi les deux prochains termes
del tokenList [ 0 : 2 ]
2013-11-01 21:58:42 +00:00
else :
tmpTokenList . append ( tokenList [ 0 ] )
del tokenList [ 0 ]
2014-12-21 18:03:44 +00:00
2014-12-22 13:14:37 +00:00
if len ( tokenList ) == 2 and isNumerand ( tokenList [ 0 ] ) \
2014-12-21 18:03:44 +00:00
and isOperator ( tokenList [ 1 ] ) and tokenList [ 1 ] . arity == 1 :
# S'il reste deux éléments dont un operation d'arité 1
op1 = tokenList [ 0 ]
operator = tokenList [ 1 ]
res = operator ( op1 )
tmpTokenList . append ( res )
# Comme on vient de faire le calcul, on peut détruire aussi les deux prochains termes
del tokenList [ 0 : 2 ]
2013-11-01 21:58:42 +00:00
tmpTokenList + = tokenList
2013-11-02 15:36:08 +00:00
steps = expand_list ( tmpTokenList )
2013-11-01 21:58:42 +00:00
2013-11-02 15:47:47 +00:00
if len ( steps [ : - 1 ] ) > 0 :
2015-02-26 18:02:20 +00:00
child_steps + = [ flatten_list ( s ) for s in steps [ : - 1 ] ]
2013-11-02 15:47:47 +00:00
2013-11-02 15:36:08 +00:00
self . child = Expression ( steps [ - 1 ] )
2015-02-26 18:02:20 +00:00
self . child . steps = child_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 :
return 0
return 1
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 ) :
if type ( other ) == Expression :
return Expression ( self . postfix_tokens + other . postfix_tokens + [ operator ] )
elif type ( other ) == list :
return Expression ( self . postfix_tokens + other + [ operator ] )
else :
return Expression ( self . postfix_tokens + [ other ] + [ operator ] )
def roperate ( self , other , operator ) :
if type ( other ) == Expression :
return Expression ( other . postfix_tokens + self . postfix_tokens + [ operator ] )
elif type ( other ) == list :
return Expression ( other + self . postfix_tokens + [ operator ] )
else :
return Expression ( [ other ] + self . postfix_tokens + [ operator ] )
def __add__ ( self , other ) :
return self . operate ( other , op . add )
def __radd__ ( self , other ) :
return self . roperate ( other , op . add )
def __sub__ ( self , other ) :
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 ) :
return self . operate ( other , op . mul )
def __rmul__ ( self , other ) :
return self . roperate ( other , op . mul )
def __div__ ( self , other ) :
return self . operate ( other , op . div )
def __rdiv__ ( self , other ) :
return self . roperate ( other , op . div )
def __pow__ ( self , other ) :
return self . operate ( other , op . pow )
def __neg__ ( self ) :
return Expression ( self . postfix_tokens + [ op . sub1 ] )
2013-11-01 11:42:42 +00:00
2013-11-02 15:47:47 +00:00
def test ( exp ) :
a = Expression ( exp )
2015-02-26 18:02:20 +00:00
b = a . simplify ( )
for i in b . explain ( ) :
#print(type(i))
2014-11-11 20:45:24 +00:00
print ( i )
2013-08-09 09:35:14 +00:00
2014-12-22 13:14:37 +00:00
#print(type(a.simplified()), ":", a.simplified())
2014-12-19 14:40:35 +00:00
2013-11-08 09:05:28 +00:00
print ( " \n " )
2013-11-02 15:47:47 +00:00
if __name__ == ' __main__ ' :
2014-12-28 09:25:21 +00:00
#render = lambda _,x : str(x)
#Expression.set_render(render)
#exp = Expression("1/2 - 4")
#print(list(exp.simplify()))
2014-12-22 14:31:00 +00:00
#Expression.set_render(txt)
2014-12-22 13:14:37 +00:00
#exp = "2 ^ 3 * 5"
#test(exp)
2013-11-02 15:47:47 +00:00
2014-12-22 14:31:00 +00:00
#exp = "2x + 5"
#test(exp)
2014-11-21 14:49:43 +00:00
2014-12-22 13:14:37 +00:00
#Expression.set_render(tex)
2014-11-21 14:49:43 +00:00
2014-12-22 13:14:37 +00:00
#test(exp1)
2014-11-11 08:44:39 +00:00
2014-12-22 13:14:37 +00:00
#from pymath.operator import op
#exp = [2, 3, op.pw, 5, op.mul]
#test(exp)
2014-11-21 14:49:43 +00:00
2014-12-22 13:14:37 +00:00
#test([Expression(exp1), Expression(exp), op.add])
2014-11-11 20:45:24 +00:00
2014-12-22 13:14:37 +00:00
#exp = "1 + 3 * 5"
#e = Expression(exp)
#f = -e
#print(f)
2014-01-17 19:17:49 +00:00
2015-02-26 18:02:20 +00:00
exp = " 2 * 3 * 3 * 5 "
test ( exp )
2013-11-02 15:47:47 +00:00
2015-02-26 18:02:20 +00:00
exp = " 2 * 3 + 3 * 5 "
test ( exp )
2013-11-02 15:47:47 +00:00
2015-02-26 18:02:20 +00:00
exp = " 2 * ( 3 + 4 ) + 3 * 5 "
test ( exp )
2013-11-08 09:05:28 +00:00
2015-02-26 18:02:20 +00:00
exp = " 2 * ( 3 + 4 ) + ( 3 - 4 ) * 5 "
test ( exp )
exp = " 2 * ( 2 - ( 3 + 4 ) ) + ( 3 - 4 ) * 5 "
test ( exp )
exp = " 2 * ( 2 - ( 3 + 4 ) ) + 5 * ( 3 - 4 ) "
test ( exp )
exp = " 2 + 5 * ( 3 - 4 ) "
test ( exp )
2013-11-08 09:05:28 +00:00
2015-02-26 18:02:20 +00:00
exp = " ( 2 + 5 ) * ( 3 - 4 )^4 "
test ( exp )
2013-11-08 09:05:28 +00:00
2015-02-26 18:02:20 +00:00
exp = " ( 2 + 5 ) * ( 3 * 4 ) "
test ( exp )
2013-11-08 09:05:28 +00:00
2014-12-22 13:14:37 +00:00
#exp = "( 2 + 5 - 1 ) / ( 3 * 4 )"
#test(exp)
2013-11-02 15:47:47 +00:00
2014-12-22 13:14:37 +00:00
#exp = "( 2 + 5 ) / ( 3 * 4 ) + 1 / 12"
#test(exp)
2013-11-02 15:47:47 +00:00
2014-12-22 13:14:37 +00:00
#exp = "( 2+ 5 )/( 3 * 4 ) + 1 / 2"
#test(exp)
2013-11-02 15:47:47 +00:00
2014-12-22 13:14:37 +00:00
#exp="(-2+5)/(3*4)+1/12+5*5"
#test(exp)
2014-01-15 15:41:51 +00:00
2014-12-22 13:14:37 +00:00
#exp="-2*4(12 + 1)(3-12)"
#test(exp)
2014-01-28 19:52:38 +00:00
2014-01-17 19:17:49 +00:00
2014-12-22 13:14:37 +00:00
#exp="(-2+5)/(3*4)+1/12+5*5"
#test(exp)
2014-01-17 19:17:49 +00:00
2014-01-17 13:57:45 +00:00
# TODO: The next one doesn't work |ven. janv. 17 14:56:58 CET 2014
#exp="-2*(-a)(12 + 1)(3-12)"
#e = Expression(exp)
#print(e)
2014-01-15 15:54:33 +00:00
## Can't handle it yet!!
#exp="-(-2)"
#test(exp)
2014-12-22 14:31:00 +00:00
#print("\n")
#exp = Expression.random("({a} + 3)({b} - 1)", ["{a} > 4"])
#for i in exp.simplify():
# print(i)
2014-12-19 15:45:22 +00:00
2015-02-26 18:02:20 +00:00
#import doctest
#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:
# cursor: 16 del