Merge remote-tracking branch 'origin/dev' into dev
This commit is contained in:
commit
450af74a0d
11
MANIFEST
11
MANIFEST
@ -5,5 +5,16 @@ pymath/arithmetic.py
|
|||||||
pymath/expression.py
|
pymath/expression.py
|
||||||
pymath/fraction.py
|
pymath/fraction.py
|
||||||
pymath/generic.py
|
pymath/generic.py
|
||||||
|
pymath/operator.py
|
||||||
|
pymath/polynom.py
|
||||||
pymath/random_expression.py
|
pymath/random_expression.py
|
||||||
pymath/render.py
|
pymath/render.py
|
||||||
|
pymath/str2tokens.py
|
||||||
|
test/test_arithmetic.py
|
||||||
|
test/test_expression.py
|
||||||
|
test/test_fraction.py
|
||||||
|
test/test_generic.py
|
||||||
|
test/test_polynom.py
|
||||||
|
test/test_random_expression.py
|
||||||
|
test/test_render.py
|
||||||
|
test/test_str2tokens.py
|
||||||
|
@ -3,71 +3,57 @@
|
|||||||
|
|
||||||
|
|
||||||
from .generic import flatten_list, isNumber
|
from .generic import flatten_list, isNumber
|
||||||
|
import types
|
||||||
|
|
||||||
class Operator(str):
|
class Operator(str):
|
||||||
|
|
||||||
"""The operator class, is a string (representation of the operator) with its arity"""
|
"""The operator class, is a string (representation of the operator) with its arity"""
|
||||||
|
|
||||||
PRIORITY = {"^": [0, 5], "/": [0, 4], "*" : [0,3], ":": [0,3], "+": [0,1], "-":[2,1], "(":[0,0]}
|
def __new__(cls, operator = "", name = "", priority = 0, actions = ("",""), txt = "", tex = "", arity = 2):
|
||||||
OPERATIONS = { \
|
""" Create an Operator """
|
||||||
"+": ["", ("__add__","__radd__")],\
|
#def __new__(cls, operator, arity = 2):
|
||||||
"-": ["__neg__", ("__sub__", "__rsub__")], \
|
|
||||||
"*": ["", ("__mul__", "__rmul__")], \
|
|
||||||
"/": ["", ("__div__","__rdiv__")], \
|
|
||||||
"^": ["", ("__pow__", "")], \
|
|
||||||
"(": ["",""],\
|
|
||||||
}
|
|
||||||
TXT = { \
|
|
||||||
"+": ["", "{op1} + {op2}"] ,\
|
|
||||||
"-": ["- {op1}", "{op1} - {op2}"] ,\
|
|
||||||
"*": ["", "{op1} * {op2}"] ,\
|
|
||||||
"/": ["", "{op1} / {op2}"] ,\
|
|
||||||
"^": ["", "{op1} ^ {op2}"] ,\
|
|
||||||
"(": ["",""],\
|
|
||||||
}
|
|
||||||
TEX = { \
|
|
||||||
"+": ["", "{op1} + {op2}"] ,\
|
|
||||||
"-": ["- {op1}", "{op1} - {op2}"] ,\
|
|
||||||
"*": ["", "{op1} \\times {op2}"] ,\
|
|
||||||
"/": ["", "\\frac{{ {op1} }}{{ {op2} }}"] ,\
|
|
||||||
"^": ["", "{op1}^{{ {op2} }}"] ,\
|
|
||||||
"(": ["",""],\
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def __new__(cls, operator, arity = 2):
|
|
||||||
op = str.__new__(cls, operator)
|
op = str.__new__(cls, operator)
|
||||||
|
op.operator = operator
|
||||||
|
op.name = name
|
||||||
op.arity = arity
|
op.arity = arity
|
||||||
|
op.priority = priority
|
||||||
# TODO: Add op.visibility |sam. nov. 8 17:00:08 CET 2014
|
op.actions = actions
|
||||||
|
op.txt = txt
|
||||||
op.priority = cls.PRIORITY[operator][arity - 1]
|
op.tex = tex
|
||||||
op.actions = cls.OPERATIONS[operator][arity-1]
|
|
||||||
op._txt = cls.TXT[operator][arity-1]
|
|
||||||
op._tex = cls.TEX[operator][arity-1]
|
|
||||||
|
|
||||||
op.isOperator = 1
|
op.isOperator = 1
|
||||||
|
# TODO: Add self.visibility |sam. nov. 8 17:00:08 CET 2014
|
||||||
return op
|
return op
|
||||||
|
|
||||||
def __call__(self, *args):
|
def __call__(self, *args):
|
||||||
""" Calling this operator performs the rigth calculus """
|
""" Calling this operator performs the rigth calculus """
|
||||||
|
return self._call(*args)
|
||||||
|
|
||||||
|
|
||||||
|
def _call(self, *args):
|
||||||
|
"""Trick to avoid overloading __call__ """
|
||||||
if self.arity == 1:
|
if self.arity == 1:
|
||||||
return getattr(args[0], self.actions)()
|
return getattr(args[0], self.actions)()
|
||||||
|
|
||||||
elif self.arity == 2:
|
elif self.arity == 2:
|
||||||
# C'est moche mais je veux que ça marche...
|
if type(args[1]) == int:
|
||||||
if str(self) == "/":
|
return getattr(args[0], self.actions[0])(args[1])
|
||||||
# TODO: faudra changer ça c'est pas beau! |ven. nov. 14 16:13:49 CET 2014
|
|
||||||
from .fraction import Fraction
|
|
||||||
ans = [Fraction(args[0], args[1])]
|
|
||||||
ans += ans[0].simplify()
|
|
||||||
return ans
|
|
||||||
else:
|
else:
|
||||||
if type(args[1]) == int:
|
return getattr(args[1], self.actions[1])(args[0])
|
||||||
return getattr(args[0], self.actions[0])(args[1])
|
|
||||||
else:
|
def _render(self, link, *args):
|
||||||
return getattr(args[1], self.actions[1])(args[0])
|
"""Global step for __txt__ and __tex__
|
||||||
|
|
||||||
|
:param link: the link between operators
|
||||||
|
:param *args: the operands
|
||||||
|
: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)
|
||||||
|
ans = save_mainOp(ans, self)
|
||||||
|
return ans
|
||||||
|
|
||||||
def __txt__(self, *args):
|
def __txt__(self, *args):
|
||||||
"""Txt rendering for the operator
|
"""Txt rendering for the operator
|
||||||
@ -91,11 +77,7 @@ class Operator(str):
|
|||||||
>>> op.sub1.__txt__(f)
|
>>> op.sub1.__txt__(f)
|
||||||
'- ( 2 + 3 )'
|
'- ( 2 + 3 )'
|
||||||
"""
|
"""
|
||||||
replacement = {"op"+str(i+1): ' '.join(self.add_parenthesis(op)) for (i,op) in enumerate(args)}
|
return self._render(self.txt, *args)
|
||||||
|
|
||||||
ans = self._txt.format(**replacement)
|
|
||||||
ans = save_mainOp(ans, self)
|
|
||||||
return ans
|
|
||||||
|
|
||||||
def __tex__(self, *args):
|
def __tex__(self, *args):
|
||||||
"""Tex rendering for the operator
|
"""Tex rendering for the operator
|
||||||
@ -119,11 +101,7 @@ class Operator(str):
|
|||||||
>>> op.sub1.__tex__(f)
|
>>> op.sub1.__tex__(f)
|
||||||
'- ( 2 + 3 )'
|
'- ( 2 + 3 )'
|
||||||
"""
|
"""
|
||||||
replacement = {"op"+str(i+1): ' '.join(self.add_parenthesis(op)) for (i,op) in enumerate(args)}
|
return self._render(self.tex, *args)
|
||||||
|
|
||||||
ans = self._tex.format(**replacement)
|
|
||||||
ans = save_mainOp(ans, self)
|
|
||||||
return ans
|
|
||||||
|
|
||||||
def __p2i__(self, *args):
|
def __p2i__(self, *args):
|
||||||
"""Fix list transformation for the operator
|
"""Fix list transformation for the operator
|
||||||
@ -166,6 +144,7 @@ class Operator(str):
|
|||||||
if op.mainOp.priority < self.priority:
|
if op.mainOp.priority < self.priority:
|
||||||
op = flatten_list(["("] + [op] + [")"])
|
op = flatten_list(["("] + [op] + [")"])
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
|
# op has not the attribute priority
|
||||||
try:
|
try:
|
||||||
if int(op) < 0:
|
if int(op) < 0:
|
||||||
op = ['(', op, ')']
|
op = ['(', op, ')']
|
||||||
@ -173,18 +152,6 @@ class Operator(str):
|
|||||||
pass
|
pass
|
||||||
return flatten_list([op])
|
return flatten_list([op])
|
||||||
|
|
||||||
|
|
||||||
class op(object):
|
|
||||||
""" List of admited operations """
|
|
||||||
# TODO: On pourrait peut être le faire plus proprement avec des décorateurs? |mar. nov. 11 20:24:54 CET 2014
|
|
||||||
add = Operator("+")
|
|
||||||
sub = Operator("-")
|
|
||||||
mul = Operator("*")
|
|
||||||
div = Operator("/")
|
|
||||||
pw = Operator("^")
|
|
||||||
sub1 = Operator("-", 1)
|
|
||||||
par = Operator("(")
|
|
||||||
|
|
||||||
def save_mainOp(obj, mainOp):
|
def save_mainOp(obj, mainOp):
|
||||||
"""Create a temporary class build over built-in type to stock the main operation of a calculus
|
"""Create a temporary class build over built-in type to stock the main operation of a calculus
|
||||||
|
|
||||||
@ -201,27 +168,339 @@ def save_mainOp(obj, mainOp):
|
|||||||
|
|
||||||
return Fake(obj)
|
return Fake(obj)
|
||||||
|
|
||||||
|
def operatorize(fun):
|
||||||
|
"""Transform the answer of the function into an operator
|
||||||
|
|
||||||
|
The returned value of the function has to be a dictionnary with those keys
|
||||||
|
* "operator": the name (Needed!)
|
||||||
|
* "priority": the priority level
|
||||||
|
* "actions": mathematics actions of the operator (list of 1 element if the arity is 1, 2 elements if arity is 2)
|
||||||
|
* "txt": string ready to be formated in txt for with {op1} and/or {op2}
|
||||||
|
* "tex": string ready to be formated in tex for with {op1} and/or {op2}
|
||||||
|
* "arity": arity ie number of operands needed
|
||||||
|
* "_call": action to perform when call the operator
|
||||||
|
* "_render": action use in __txt__ and __tex__
|
||||||
|
* "__txt__": txt rendering
|
||||||
|
* "__tex__": tex rendering
|
||||||
|
* "add_parenthesis": mechanism to add parenthesis
|
||||||
|
"""
|
||||||
|
def mod_fun(self, *args):
|
||||||
|
ans = fun(self, *args)
|
||||||
|
|
||||||
|
new_op = Operator(ans["operator"])
|
||||||
|
for (attr, value) in ans.items():
|
||||||
|
if hasattr(value, '__call__'):
|
||||||
|
setattr(new_op, attr, types.MethodType(value, new_op))
|
||||||
|
else:
|
||||||
|
setattr(new_op, attr, value)
|
||||||
|
|
||||||
|
return new_op
|
||||||
|
return mod_fun
|
||||||
|
|
||||||
|
class ClassProperty(object):
|
||||||
|
|
||||||
|
def __init__(self, fget):
|
||||||
|
self.fget = fget
|
||||||
|
|
||||||
|
def __get__(self, owner_self, owner_cls):
|
||||||
|
return self.fget(owner_cls)
|
||||||
|
|
||||||
|
class op(object):
|
||||||
|
""" List of admited operations """
|
||||||
|
|
||||||
|
_operators = {("+",2): "add",\
|
||||||
|
("-", 2): "sub",\
|
||||||
|
("-", 1): "sub1",\
|
||||||
|
("*", 2): "mul",\
|
||||||
|
("/", 2): "div",\
|
||||||
|
("^", 2): "pw",\
|
||||||
|
("(", 2): "par",\
|
||||||
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_op(cls, op, arity = 2):
|
||||||
|
"""Return the corresponding operator
|
||||||
|
|
||||||
|
:op: symbole of the op
|
||||||
|
:arity: the arity
|
||||||
|
|
||||||
|
>>> op.get_op('+')
|
||||||
|
'+'
|
||||||
|
>>> mul = op.get_op('*')
|
||||||
|
>>> mul.tex
|
||||||
|
'{op1} \\\\times {op2}'
|
||||||
|
>>> mul.txt
|
||||||
|
'{op1} * {op2}'
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
return getattr(cls, cls._operators[(op, arity)])
|
||||||
|
except KeyError:
|
||||||
|
raise KeyError("{theOp} (arity: {arity}) is not available".format(theOp = op, arity = arity))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def can_be_operator(cls, symbole):
|
||||||
|
""" Tell if the symbole can be an operator """
|
||||||
|
return symbole in [i[0] for i in cls._operators]
|
||||||
|
|
||||||
|
|
||||||
|
@ClassProperty
|
||||||
|
@operatorize
|
||||||
|
def add(cls):
|
||||||
|
""" The operator +
|
||||||
|
|
||||||
|
>>> add = op.add
|
||||||
|
>>> add
|
||||||
|
'+'
|
||||||
|
>>> add(1, 2)
|
||||||
|
3
|
||||||
|
>>> add.__tex__('1','2')
|
||||||
|
'1 + 2'
|
||||||
|
>>> add.__txt__('1','2')
|
||||||
|
'1 + 2'
|
||||||
|
>>> add.__tex__('1','-2')
|
||||||
|
'1 + (-2)'
|
||||||
|
"""
|
||||||
|
caract = {
|
||||||
|
"operator" : "+", \
|
||||||
|
"name" : "add",\
|
||||||
|
"priority" : 1, \
|
||||||
|
"arity" : 2, \
|
||||||
|
"actions" : ("__add__","__radd__"), \
|
||||||
|
"txt" : "{op1} + {op2}",\
|
||||||
|
"tex" : "{op1} + {op2}",\
|
||||||
|
}
|
||||||
|
|
||||||
|
return caract
|
||||||
|
|
||||||
|
@ClassProperty
|
||||||
|
@operatorize
|
||||||
|
def sub(self):
|
||||||
|
""" The operator -
|
||||||
|
|
||||||
|
>>> sub = op.sub
|
||||||
|
>>> sub
|
||||||
|
'-'
|
||||||
|
>>> sub(1, 2)
|
||||||
|
-1
|
||||||
|
>>> sub.__tex__('1','2')
|
||||||
|
'1 - 2'
|
||||||
|
>>> sub.__txt__('1','2')
|
||||||
|
'1 - 2'
|
||||||
|
>>> sub.__tex__('1','-2')
|
||||||
|
'1 - (-2)'
|
||||||
|
"""
|
||||||
|
caract = {
|
||||||
|
"operator" : "-", \
|
||||||
|
"name" : "sub",\
|
||||||
|
"priority" : 1, \
|
||||||
|
"arity" : 2, \
|
||||||
|
"actions" : ("__sub__","__rsub__"), \
|
||||||
|
"txt" : "{op1} - {op2}",\
|
||||||
|
"tex" : "{op1} - {op2}",\
|
||||||
|
}
|
||||||
|
|
||||||
|
return caract
|
||||||
|
|
||||||
|
@ClassProperty
|
||||||
|
@operatorize
|
||||||
|
def sub1(self):
|
||||||
|
""" The operator -
|
||||||
|
|
||||||
|
>>> sub1 = op.sub1
|
||||||
|
>>> sub1
|
||||||
|
'-'
|
||||||
|
>>> sub1(1)
|
||||||
|
-1
|
||||||
|
>>> sub1.__tex__('1')
|
||||||
|
'- 1'
|
||||||
|
>>> sub1.__txt__('1')
|
||||||
|
'- 1'
|
||||||
|
>>> sub1.__tex__('-1')
|
||||||
|
'- (-1)'
|
||||||
|
"""
|
||||||
|
def add_parenthesis(self, op):
|
||||||
|
""" Add parenthesis if necessary """
|
||||||
|
try:
|
||||||
|
if op.mainOp.priority <= self.priority:
|
||||||
|
op = flatten_list(["("] + [op] + [")"])
|
||||||
|
except AttributeError:
|
||||||
|
# op has not the attribute priority
|
||||||
|
try:
|
||||||
|
if int(op) < 0:
|
||||||
|
op = ['(', op, ')']
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
return flatten_list([op])
|
||||||
|
|
||||||
|
caract = {
|
||||||
|
"operator" : "-", \
|
||||||
|
"name" : "sub1",\
|
||||||
|
"priority" : 2, \
|
||||||
|
"arity" : 1, \
|
||||||
|
"actions" : "__neg__",\
|
||||||
|
"txt" : "- {op1}",\
|
||||||
|
"tex" : "- {op1}",\
|
||||||
|
"add_parenthesis": add_parenthesis,\
|
||||||
|
}
|
||||||
|
|
||||||
|
return caract
|
||||||
|
|
||||||
|
@ClassProperty
|
||||||
|
@operatorize
|
||||||
|
def mul(self):
|
||||||
|
""" The operator *
|
||||||
|
|
||||||
|
>>> mul = op.mul
|
||||||
|
>>> mul
|
||||||
|
'*'
|
||||||
|
>>> mul(1, 2)
|
||||||
|
2
|
||||||
|
>>> mul.__tex__('1','2')
|
||||||
|
'1 \\times 2'
|
||||||
|
>>> mul.__txt__('1','2')
|
||||||
|
'1 * 2'
|
||||||
|
>>> mul.__tex__('1','-2')
|
||||||
|
'1 \\times (-2)'
|
||||||
|
"""
|
||||||
|
# * can not be display in some cases
|
||||||
|
def _render(self, link, *args):
|
||||||
|
|
||||||
|
replacement = {"op"+str(i+1): ' '.join(self.add_parenthesis(op)) for (i,op) in enumerate(args)}
|
||||||
|
|
||||||
|
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
|
||||||
|
else:
|
||||||
|
ans = link.format(**replacement)
|
||||||
|
ans = save_mainOp(ans, self)
|
||||||
|
return ans
|
||||||
|
|
||||||
|
caract = {
|
||||||
|
"operator" : "*", \
|
||||||
|
"name" : "mul",\
|
||||||
|
"priority" : 4, \
|
||||||
|
"arity" : 2, \
|
||||||
|
"actions" : ("__mul__","__rmul__"), \
|
||||||
|
"txt" : "{op1} * {op2}",\
|
||||||
|
"tex" : "{op1} \\times {op2}",\
|
||||||
|
"visibility": 1,\
|
||||||
|
"_render": _render
|
||||||
|
}
|
||||||
|
|
||||||
|
return caract
|
||||||
|
|
||||||
|
@ClassProperty
|
||||||
|
@operatorize
|
||||||
|
def div(self):
|
||||||
|
""" The operator /
|
||||||
|
|
||||||
|
>>> div = op.div
|
||||||
|
>>> div
|
||||||
|
'/'
|
||||||
|
>>> div(1, 2)
|
||||||
|
< Fraction 1 / 2>
|
||||||
|
>>> div.__tex__('1','2')
|
||||||
|
'\\frac{ 1 }{ 2 }'
|
||||||
|
>>> div.__tex__('1','2')
|
||||||
|
'\\frac{ -1 }{ 2 }'
|
||||||
|
>>> div.__txt__('1','2')
|
||||||
|
'1 / 2'
|
||||||
|
"""
|
||||||
|
from .fraction import Fraction
|
||||||
|
def _call(self, op1, op2):
|
||||||
|
if op2 == 1:
|
||||||
|
return op1
|
||||||
|
else:
|
||||||
|
return Fraction(op1,op2)
|
||||||
|
|
||||||
|
def __tex__(self, *args):
|
||||||
|
# Pas besoin de parenthèses en plus pour \frac
|
||||||
|
replacement = {"op"+str(i+1): op for (i,op) in enumerate(args)}
|
||||||
|
|
||||||
|
ans = self.tex.format(**replacement)
|
||||||
|
ans = save_mainOp(ans, self)
|
||||||
|
return ans
|
||||||
|
|
||||||
|
caract = {
|
||||||
|
"operator" : "/", \
|
||||||
|
"name" : "div",\
|
||||||
|
"priority" : 4, \
|
||||||
|
"arity" : 2, \
|
||||||
|
"txt" : "{op1} / {op2}",\
|
||||||
|
"tex" : "\\frac{{ {op1} }}{{ {op2} }}",\
|
||||||
|
"_call": _call,\
|
||||||
|
"__tex__":__tex__,\
|
||||||
|
}
|
||||||
|
|
||||||
|
return caract
|
||||||
|
|
||||||
|
@ClassProperty
|
||||||
|
@operatorize
|
||||||
|
def pw(self):
|
||||||
|
""" The operator ^
|
||||||
|
|
||||||
|
>>> pw = op.pw
|
||||||
|
>>> pw
|
||||||
|
'^'
|
||||||
|
>>> pw(2, 3)
|
||||||
|
8
|
||||||
|
>>> pw.__tex__('2','3')
|
||||||
|
'2^{ 3 }'
|
||||||
|
>>> pw.__txt__('2','3')
|
||||||
|
'2 ^ 3'
|
||||||
|
>>> pw.__txt__('-2','3')
|
||||||
|
'( -2 ) ^ 3'
|
||||||
|
"""
|
||||||
|
def _call(self, op1, op2):
|
||||||
|
""" Calling this operator performs the rigth calculus """
|
||||||
|
return getattr(op1, "__pow__")(op2)
|
||||||
|
|
||||||
|
caract = {
|
||||||
|
"operator" : "^", \
|
||||||
|
"name" : "pw",\
|
||||||
|
"priority" : 5, \
|
||||||
|
"arity" : 2, \
|
||||||
|
"actions" : ("__pow__",""), \
|
||||||
|
"txt" : "{op1} ^ {op2}",\
|
||||||
|
"tex" : "{op1}^{{ {op2} }}",\
|
||||||
|
"_call":_call,\
|
||||||
|
}
|
||||||
|
|
||||||
|
return caract
|
||||||
|
|
||||||
|
@ClassProperty
|
||||||
|
@operatorize
|
||||||
|
def par(self):
|
||||||
|
""" The operator ( """
|
||||||
|
caract = {
|
||||||
|
"operator" : "(", \
|
||||||
|
"name" : "par",\
|
||||||
|
"priority" : 0, \
|
||||||
|
"arity" : 0, \
|
||||||
|
}
|
||||||
|
return caract
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
op = Operator("+", 2)
|
print(op.add.__tex__('1','2'))
|
||||||
print(op.__txt__('1','2'))
|
print(op.mul.__tex__('1','2'))
|
||||||
mul = Operator("*", 2)
|
print(op.sub.__tex__('1','2'))
|
||||||
add = Operator("+", 2)
|
f = save_mainOp('2 + 3',op.add)
|
||||||
sub1 = Operator("-", 1)
|
print(op.mul.__txt__(f, '4'))
|
||||||
div = Operator("/", 1)
|
f = save_mainOp('-3',op.sub1)
|
||||||
print(mul.__txt__('1','2'))
|
print(op.sub1.__txt__(f))
|
||||||
print(add.__txt__('1','2'))
|
print(op.sub1.__txt__('-3'))
|
||||||
f = save_mainOp('2 + 3',add)
|
f = save_mainOp('2 + 3',op.add)
|
||||||
print(mul.__txt__(f, '4'))
|
print(op.sub1.__txt__(f))
|
||||||
f = save_mainOp('-3',sub1)
|
|
||||||
print(sub1.__txt__(f))
|
|
||||||
print(sub1.__txt__('-3'))
|
|
||||||
f = save_mainOp('2 + 3',add)
|
|
||||||
print(sub1.__txt__(f))
|
|
||||||
|
|
||||||
from .fraction import Fraction
|
from .fraction import Fraction
|
||||||
f = Fraction(1, 2)
|
f = Fraction(1, 2)
|
||||||
print(add.__txt__(f.__txt__(),'2'))
|
print(op.add.__txt__(f.__txt__(),'2'))
|
||||||
print(add.__tex__(f.__tex__(),'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')))
|
||||||
|
|
||||||
|
|
||||||
import doctest
|
import doctest
|
||||||
|
@ -31,6 +31,19 @@ class RdExpression(object):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def set_form(cls, form):
|
def set_form(cls, form):
|
||||||
|
""" Define whether RdExpression create expression with Expression (nice render) or if it only replace inside {} not taking care or render
|
||||||
|
|
||||||
|
>>> form = "{a}*{b}"
|
||||||
|
>>> exp = RdExpression(form)()
|
||||||
|
>>> print(type(exp))
|
||||||
|
<class 'pymath.expression.Expression'>
|
||||||
|
>>> RdExpression.set_form("raw")
|
||||||
|
>>> form = "{a}*{b}"
|
||||||
|
>>> exp = RdExpression(form)()
|
||||||
|
>>> print(type(exp))
|
||||||
|
<class 'str'>
|
||||||
|
"""
|
||||||
|
|
||||||
cls.FORM = form
|
cls.FORM = form
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -189,12 +202,13 @@ if __name__ == '__main__':
|
|||||||
rdExp3 = RdExpression(form, cond)
|
rdExp3 = RdExpression(form, cond)
|
||||||
desc_rdExp(rdExp3)
|
desc_rdExp(rdExp3)
|
||||||
|
|
||||||
form = "{a + a*10}*4 + {a} + 2*{b}"
|
form = "{a+a*10}*4 + {a} + 2*{b}"
|
||||||
cond = ["{a-b} + {b} in list(range(20))", "abs({a}) not in [1]", "{b} not in [1]", "gcd({a},{b}) == 1"]
|
cond = ["{a-b} + {b} in list(range(20))", "abs({a}) not in [1]", "{b} not in [1]", "gcd({a},{b}) == 1"]
|
||||||
rdExp3 = RdExpression(form, cond)
|
rdExp3 = RdExpression(form, cond)
|
||||||
desc_rdExp(rdExp3)
|
desc_rdExp(rdExp3)
|
||||||
|
|
||||||
|
import doctest
|
||||||
|
doctest.testmod()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# encoding: utf-8
|
# encoding: utf-8
|
||||||
|
|
||||||
from .operator import Operator
|
|
||||||
from .generic import Stack, isOperator, isNumber
|
from .generic import Stack, isOperator, isNumber
|
||||||
|
from pymath.operator import op
|
||||||
|
|
||||||
def str2tokens(exp):
|
def str2tokens(exp):
|
||||||
""" Parse the string into tokens then turn it into postfix form
|
""" Parse the string into tokens then turn it into postfix form
|
||||||
@ -51,7 +51,7 @@ def str2in_tokens(exp):
|
|||||||
tokens.append(int(character))
|
tokens.append(int(character))
|
||||||
|
|
||||||
elif character in "+-*/:^":
|
elif character in "+-*/:^":
|
||||||
tokens.append(Operator(character))
|
tokens.append(character)
|
||||||
|
|
||||||
elif character == ")":
|
elif character == ")":
|
||||||
tokens.append(character)
|
tokens.append(character)
|
||||||
@ -60,9 +60,8 @@ def str2in_tokens(exp):
|
|||||||
# If "3(", ")("
|
# If "3(", ")("
|
||||||
if isNumber(tokens[-1]) \
|
if isNumber(tokens[-1]) \
|
||||||
or tokens[-1] == ")" :
|
or tokens[-1] == ")" :
|
||||||
#tokens.append(Operator("*"))
|
tokens.append("*")
|
||||||
tokens.append(Operator("*"))
|
tokens.append(character)
|
||||||
tokens.append(Operator(character))
|
|
||||||
|
|
||||||
elif character == ".":
|
elif character == ".":
|
||||||
raise ValueError("No float number please")
|
raise ValueError("No float number please")
|
||||||
@ -80,15 +79,11 @@ def in2post_fix(infix_tokens):
|
|||||||
@param infix_tokens: the infix list of tokens to transform into postfix form.
|
@param infix_tokens: the infix list of tokens to transform into postfix form.
|
||||||
@return: the corresponding postfix list of tokens.
|
@return: the corresponding postfix list of tokens.
|
||||||
|
|
||||||
>>> a, s, m, d, p = Operator("+"), Operator("-"), Operator("*"), Operator("/"), Operator("^")
|
>>> in2post_fix([op.par, 2, op.add, 5, op.sub, 1, ')', op.div, op.par, 3, op.mul, 4, ')'])
|
||||||
>>> s1 = Operator("-", 1)
|
|
||||||
>>> par = Operator("(")
|
|
||||||
|
|
||||||
>>> in2post_fix([par, 2, a, 5, s, 1, ')', d, par, 3, m, 4, ')'])
|
|
||||||
[2, 5, '+', 1, '-', 3, 4, '*', '/']
|
[2, 5, '+', 1, '-', 3, 4, '*', '/']
|
||||||
>>> in2post_fix([s1, par, s1, 2, ')'])
|
>>> in2post_fix([op.sub1, op.par, op.sub1, 2, ')'])
|
||||||
[2, '-', '-']
|
[2, '-', '-']
|
||||||
>>> in2post_fix([s1, par, s1, 2, a, 3, m, 4, ')'])
|
>>> in2post_fix([op.sub1, op.par, op.sub1, 2, op.add, 3, op.mul, 4, ')'])
|
||||||
[2, '-', 3, 4, '*', '+', '-']
|
[2, '-', 3, 4, '*', '+', '-']
|
||||||
"""
|
"""
|
||||||
# Stack where operator will be stocked
|
# Stack where operator will be stocked
|
||||||
@ -101,13 +96,13 @@ def in2post_fix(infix_tokens):
|
|||||||
|
|
||||||
for (pos_token,token) in enumerate(infix_tokens):
|
for (pos_token,token) in enumerate(infix_tokens):
|
||||||
|
|
||||||
## Pour voir ce qu'il se passe dans cette procédure
|
# Pour voir ce qu'il se passe dans cette procédure
|
||||||
#print(str(postfix_tokens), " | ", str(opStack), " | ", str(infix_tokens[(pos_token+1):]), " | ", str(arity_Stack))
|
#print(str(postfix_tokens), " | ", str(opStack), " | ", str(infix_tokens[(pos_token+1):]), " | ", str(arity_Stack))
|
||||||
if token == ")":
|
if token == ")":
|
||||||
op = opStack.pop()
|
next_op = opStack.pop()
|
||||||
while op != "(":
|
while next_op != "(":
|
||||||
postfix_tokens.append(op)
|
postfix_tokens.append(next_op)
|
||||||
op = opStack.pop()
|
next_op = opStack.pop()
|
||||||
|
|
||||||
# Go back to old arity
|
# Go back to old arity
|
||||||
arity_Stack.pop()
|
arity_Stack.pop()
|
||||||
@ -115,23 +110,22 @@ def in2post_fix(infix_tokens):
|
|||||||
arity = arity_Stack.pop()
|
arity = arity_Stack.pop()
|
||||||
arity_Stack.push(arity + 1)
|
arity_Stack.push(arity + 1)
|
||||||
|
|
||||||
elif isOperator(token):
|
elif op.can_be_operator(token):
|
||||||
if token == "(":
|
if token == "(":
|
||||||
opStack.push(token)
|
opStack.push(op.get_op(token))
|
||||||
# Set next arity counter
|
# Set next arity counter
|
||||||
arity_Stack.push(0)
|
arity_Stack.push(0)
|
||||||
else:
|
else:
|
||||||
while (not opStack.isEmpty()) and opStack.peek().priority >= token.priority:
|
|
||||||
op = opStack.pop()
|
|
||||||
postfix_tokens.append(op)
|
|
||||||
|
|
||||||
arity = arity_Stack.pop()
|
arity = arity_Stack.pop()
|
||||||
|
token_op = op.get_op(token, arity + 1)
|
||||||
token.arity = arity + 1
|
|
||||||
opStack.push(token)
|
|
||||||
# print("--", token, " -> ", str(arity + 1))
|
|
||||||
# Reset arity to 0 in case there is other operators (the real operation would be "-op.arity + 1")
|
# Reset arity to 0 in case there is other operators (the real operation would be "-op.arity + 1")
|
||||||
arity_Stack.push(0)
|
arity_Stack.push(0)
|
||||||
|
while (not opStack.isEmpty()) and opStack.peek().priority >= token_op.priority:
|
||||||
|
next_op = opStack.pop()
|
||||||
|
postfix_tokens.append(next_op)
|
||||||
|
|
||||||
|
opStack.push(token_op)
|
||||||
|
#print("--", token, " -> ", str(arity + 1))
|
||||||
else:
|
else:
|
||||||
postfix_tokens.append(token)
|
postfix_tokens.append(token)
|
||||||
arity = arity_Stack.pop()
|
arity = arity_Stack.pop()
|
||||||
@ -141,8 +135,8 @@ def in2post_fix(infix_tokens):
|
|||||||
#print(str(postfix_tokens), " | ", str(opStack), " | ", str(infix_tokens[(pos_token+1):]), " | ", str(arity_Stack))
|
#print(str(postfix_tokens), " | ", str(opStack), " | ", str(infix_tokens[(pos_token+1):]), " | ", str(arity_Stack))
|
||||||
|
|
||||||
while not opStack.isEmpty():
|
while not opStack.isEmpty():
|
||||||
op = opStack.pop()
|
next_op = opStack.pop()
|
||||||
postfix_tokens.append(op)
|
postfix_tokens.append(next_op)
|
||||||
|
|
||||||
## Pour voir ce qu'il se passe dans cette procédure
|
## Pour voir ce qu'il se passe dans cette procédure
|
||||||
#print(str(postfix_tokens), " | ", str(opStack), " | ", str(infix_tokens[(pos_token+1):]), " | ", str(arity_Stack))
|
#print(str(postfix_tokens), " | ", str(opStack), " | ", str(infix_tokens[(pos_token+1):]), " | ", str(arity_Stack))
|
||||||
@ -161,7 +155,6 @@ if __name__ == '__main__':
|
|||||||
#
|
#
|
||||||
#print(in2post_fix(in_tokens))
|
#print(in2post_fix(in_tokens))
|
||||||
|
|
||||||
from .operator import op
|
|
||||||
print(in2post_fix([op.par, 2, op.add, 5, op.sub, 1, ')', op.div, op.par, 3, op.mul, 4, ')']))
|
print(in2post_fix([op.par, 2, op.add, 5, op.sub, 1, ')', op.div, op.par, 3, op.mul, 4, ')']))
|
||||||
print(in2post_fix([op.sub1, op.par, op.sub1, 2, ')']))
|
print(in2post_fix([op.sub1, op.par, op.sub1, 2, ')']))
|
||||||
print(in2post_fix([op.sub1, op.par, op.sub1, 2, op.add, 3, op.mul, 4, ')']))
|
print(in2post_fix([op.sub1, op.par, op.sub1, 2, op.add, 3, op.mul, 4, ')']))
|
||||||
|
1
requirements.txt
Normal file
1
requirements.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
pyparsing==2.0.3
|
@ -121,6 +121,17 @@ class TestFraction(unittest.TestCase):
|
|||||||
def test_le(self):
|
def test_le(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def test_tex(self):
|
||||||
|
f = Fraction(2, 3)
|
||||||
|
ans = "\\frac{ 2 }{ 3 }"
|
||||||
|
self.assertEqual(f.__tex__(), ans)
|
||||||
|
|
||||||
|
def test_txt(self):
|
||||||
|
f = Fraction(2, 3)
|
||||||
|
ans = "2 / 3"
|
||||||
|
self.assertEqual(f.__txt__(), ans)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
@ -22,15 +22,15 @@ class TestRandomExpression(unittest.TestCase):
|
|||||||
self.assertEqual(set(rdExp._gene_2replaced.keys()), {'a'})
|
self.assertEqual(set(rdExp._gene_2replaced.keys()), {'a'})
|
||||||
|
|
||||||
def test_only_form_calc(self):
|
def test_only_form_calc(self):
|
||||||
form = "{a + b} + 2"
|
form = "{a+b} + 2"
|
||||||
rdExp = RdExpression(form)
|
rdExp = RdExpression(form)
|
||||||
|
|
||||||
self.assertEqual(rdExp._letters, {'a', 'b'})
|
self.assertEqual(rdExp._letters, {'a', 'b'})
|
||||||
self.assertEqual(rdExp._2replaced, {'a + b'})
|
self.assertEqual(rdExp._2replaced, {'a+b'})
|
||||||
|
|
||||||
rdExp()
|
rdExp()
|
||||||
self.assertEqual(set(rdExp._gene_varia.keys()), {'a', 'b'})
|
self.assertEqual(set(rdExp._gene_varia.keys()), {'a', 'b'})
|
||||||
self.assertEqual(set(rdExp._gene_2replaced.keys()), {'a + b'})
|
self.assertEqual(set(rdExp._gene_2replaced.keys()), {'a+b'})
|
||||||
|
|
||||||
def test_only_form_cond(self):
|
def test_only_form_cond(self):
|
||||||
form = "{a} + 2"
|
form = "{a} + 2"
|
||||||
@ -78,15 +78,15 @@ class TestRandomExpression(unittest.TestCase):
|
|||||||
|
|
||||||
def test_only_form_calc_cond_calc(self):
|
def test_only_form_calc_cond_calc(self):
|
||||||
form = "{a*3} * {b}"
|
form = "{a*3} * {b}"
|
||||||
cond = ["{a + b} == 3"]
|
cond = ["{a+b} == 3"]
|
||||||
rdExp = RdExpression(form, cond)
|
rdExp = RdExpression(form, cond)
|
||||||
|
|
||||||
self.assertEqual(rdExp._letters, {'a', 'b'})
|
self.assertEqual(rdExp._letters, {'a', 'b'})
|
||||||
self.assertEqual(rdExp._2replaced, {'b', 'a*3', 'a + b'})
|
self.assertEqual(rdExp._2replaced, {'b', 'a*3', 'a+b'})
|
||||||
|
|
||||||
rdExp()
|
rdExp()
|
||||||
self.assertEqual(set(rdExp._gene_varia.keys()), {'a', 'b'})
|
self.assertEqual(set(rdExp._gene_varia.keys()), {'a', 'b'})
|
||||||
self.assertEqual(set(rdExp._gene_2replaced.keys()), {'b', 'a*3', 'a + b'})
|
self.assertEqual(set(rdExp._gene_2replaced.keys()), {'b', 'a*3', 'a+b'})
|
||||||
|
|
||||||
self.assertEqual((rdExp._gene_varia['a'] + rdExp._gene_varia['b']), 3)
|
self.assertEqual((rdExp._gene_varia['a'] + rdExp._gene_varia['b']), 3)
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ import unittest
|
|||||||
|
|
||||||
from pymath.render import tex, txt,p2i
|
from pymath.render import tex, txt,p2i
|
||||||
from pymath.fraction import Fraction
|
from pymath.fraction import Fraction
|
||||||
from pymath.operator import Operator
|
from pymath.operator import op
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -23,29 +23,29 @@ class TestTexRender(unittest.TestCase):
|
|||||||
self.assertEqual(tex([Fraction(1,2)]), "\\frac{ 1 }{ 2 }")
|
self.assertEqual(tex([Fraction(1,2)]), "\\frac{ 1 }{ 2 }")
|
||||||
|
|
||||||
def test_mult_interger(self):
|
def test_mult_interger(self):
|
||||||
exps = [ [2, 3, Operator("*", 2)], [2, -3, Operator("*", 2)], [-2, 3, Operator("*", 2)]]
|
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"]
|
wanted_render = [ "2 \\times 3", "2 \\times ( -3 )", "-2 \\times 3"]
|
||||||
for (i,e) in enumerate(exps):
|
for (i,e) in enumerate(exps):
|
||||||
rend = tex(e)
|
rend = tex(e)
|
||||||
self.assertEqual(rend, wanted_render[i])
|
self.assertEqual(rend, wanted_render[i])
|
||||||
|
|
||||||
def test_mult_letter(self):
|
def test_mult_letter(self):
|
||||||
exps = [ [2, "a", Operator("*", 2)], ["a", 3, Operator("*", 2)], [-2, "a", Operator("*", 2)], ["a", -2, Operator("*", 2)]]
|
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 \\times 3", "-2 a", "a \\times ( -2 )"]
|
wanted_render = [ "2 a", "a \\times 3", "-2 a", "a \\times ( -2 )"]
|
||||||
for (i,e) in enumerate(exps):
|
for (i,e) in enumerate(exps):
|
||||||
rend = tex(e)
|
rend = tex(e)
|
||||||
self.assertEqual(rend, wanted_render[i])
|
self.assertEqual(rend, wanted_render[i])
|
||||||
|
|
||||||
def test_mult_fraction(self):
|
def test_mult_fraction(self):
|
||||||
exps = [ [2, Fraction(1,2), Operator("*", 2)], [Fraction(1,2), 3, Operator("*", 2)]]
|
exps = [ [2, Fraction(1,2), op.get_op("*", 2)], [Fraction(1,2), 3, op.get_op("*", 2)]]
|
||||||
wanted_render = [ "2 \\times \\frac{ 1 }{ 2 }", "\\frac{ 1 }{ 2 } \\times 3"]
|
wanted_render = [ "2 \\times \\frac{ 1 }{ 2 }", "\\frac{ 1 }{ 2 } \\times 3"]
|
||||||
for (i,e) in enumerate(exps):
|
for (i,e) in enumerate(exps):
|
||||||
rend = tex(e)
|
rend = tex(e)
|
||||||
self.assertEqual(rend, wanted_render[i])
|
self.assertEqual(rend, wanted_render[i])
|
||||||
|
|
||||||
def test_parentheses(self):
|
def test_parentheses(self):
|
||||||
mul = Operator("*", 2)
|
mul = op.get_op("*", 2)
|
||||||
add = Operator("+", 2)
|
add = op.get_op("+", 2)
|
||||||
exps = [\
|
exps = [\
|
||||||
[ 2, 3, add, 4, mul],\
|
[ 2, 3, add, 4, mul],\
|
||||||
[ 2, 3, mul, 4, add],\
|
[ 2, 3, mul, 4, add],\
|
||||||
@ -81,35 +81,35 @@ class TesttxtRender(unittest.TestCase):
|
|||||||
self.assertEqual(txt([Fraction(1,2)]), "1 / 2")
|
self.assertEqual(txt([Fraction(1,2)]), "1 / 2")
|
||||||
|
|
||||||
def test_mult_interger(self):
|
def test_mult_interger(self):
|
||||||
exps = [ [2, 3, Operator("*", 2)], \
|
exps = [ [2, 3, op.get_op("*", 2)], \
|
||||||
[2, -3, Operator("*", 2)], \
|
[2, -3, op.get_op("*", 2)], \
|
||||||
[-2, 3, Operator("*", 2)]]
|
[-2, 3, op.get_op("*", 2)]]
|
||||||
wanted_render = [ "2 * 3", "2 * ( -3 )", "-2 * 3"]
|
wanted_render = [ "2 * 3", "2 * ( -3 )", "-2 * 3"]
|
||||||
for (i,e) in enumerate(exps):
|
for (i,e) in enumerate(exps):
|
||||||
rend = txt(e)
|
rend = txt(e)
|
||||||
self.assertEqual(rend, wanted_render[i])
|
self.assertEqual(rend, wanted_render[i])
|
||||||
|
|
||||||
def test_mult_letter(self):
|
def test_mult_letter(self):
|
||||||
exps = [ [2, "a", Operator("*", 2)], \
|
exps = [ [2, "a", op.get_op("*", 2)], \
|
||||||
["a", 3, Operator("*", 2)], \
|
["a", 3, op.get_op("*", 2)], \
|
||||||
[-2, "a", Operator("*", 2)], \
|
[-2, "a", op.get_op("*", 2)], \
|
||||||
["a", -2, Operator("*", 2)]]
|
["a", -2, op.get_op("*", 2)]]
|
||||||
wanted_render = [ "2 a", "a * 3", "-2 a", "a * ( -2 )"]
|
wanted_render = [ "2 a", "a * 3", "-2 a", "a * ( -2 )"]
|
||||||
for (i,e) in enumerate(exps):
|
for (i,e) in enumerate(exps):
|
||||||
rend = txt(e)
|
rend = txt(e)
|
||||||
self.assertEqual(rend, wanted_render[i])
|
self.assertEqual(rend, wanted_render[i])
|
||||||
|
|
||||||
def test_mult_fraction(self):
|
def test_mult_fraction(self):
|
||||||
exps = [ [2, Fraction(1,2), Operator("*", 2)], \
|
exps = [ [2, Fraction(1,2), op.get_op("*", 2)], \
|
||||||
[Fraction(1,2), 3, Operator("*", 2)]]
|
[Fraction(1,2), 3, op.get_op("*", 2)]]
|
||||||
wanted_render = [ "2 * 1 / 2", "1 / 2 * 3"]
|
wanted_render = [ "2 * 1 / 2", "1 / 2 * 3"]
|
||||||
for (i,e) in enumerate(exps):
|
for (i,e) in enumerate(exps):
|
||||||
rend = txt(e)
|
rend = txt(e)
|
||||||
self.assertEqual(rend, wanted_render[i])
|
self.assertEqual(rend, wanted_render[i])
|
||||||
|
|
||||||
def test_parentheses(self):
|
def test_parentheses(self):
|
||||||
mul = Operator("*", 2)
|
mul = op.get_op("*", 2)
|
||||||
add = Operator("+", 2)
|
add = op.get_op("+", 2)
|
||||||
exps = [\
|
exps = [\
|
||||||
[ 2, 3, add, 4, mul],\
|
[ 2, 3, add, 4, mul],\
|
||||||
[ 2, 3, mul, 4, add],\
|
[ 2, 3, mul, 4, add],\
|
||||||
|
Loading…
Reference in New Issue
Block a user