From 6c68a8b2f2cfb8390548261c2742a8f3f812f223 Mon Sep 17 00:00:00 2001 From: lafrite Date: Sun, 17 Nov 2013 08:45:27 +0100 Subject: [PATCH 01/80] start working on a text render --- textRender.py | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 textRender.py diff --git a/textRender.py b/textRender.py new file mode 100644 index 0000000..5ccb83b --- /dev/null +++ b/textRender.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python +# encoding: utf-8 + +from generic import Stack + + +def post2in_fix(postfix_tokens): + """ From the postfix_tokens list compute the corresponding infix_tokens list + + @param postfix_tokens: the postfix list of tokens to transform into infix form. If nothing is set, it takes the value self.postfix_tokens + @return: the corresponding infix list of tokens if postfix_tokens is set. nothing otherwise but stock it in self.infix_tokens + + >>> post2in_fix([2, 5, '+', 1, '-', 3, 4, '*', '/']) + ['( ', 2, '+', 5, '-', 1, ' )', '/', '( ', 3, '*', 4, ' )'] + """ + operandeStack = Stack() + + for token in postfix_tokens: + if self.isOperator(token): + op2 = operandeStack.pop() + if self.needPar(op2, token, "after"): + op2 = ["( ", op2, " )"] + op1 = operandeStack.pop() + if self.needPar(op1, token, "before"): + op1 = ["( ", op1, " )"] + res = [op1, token, op2] + + operandeStack.push(res) + + else: + operandeStack.push(token) + + infix_tokens = flatten_list(operandeStack.pop()) + + return infix_tokens + +def textRender(postfix_tokens): + """ A text baser render + + :param postfix_tokens: The postfix list of tokens + :returns: the text expression + + """ + infix_tokens = post2in_fix(postfix_tokens) + return ' '.join(infix_tokens) + +if __name__ == '__main__': + + import doctest + doctest.testmod() + +# ----------------------------- +# Reglages pour 'vim' +# vim:set autoindent expandtab tabstop=4 shiftwidth=4: +# cursor: 16 del From a7fb8844b0292452e8e45b3f9b56f324d6ac6dfb Mon Sep 17 00:00:00 2001 From: lafrite Date: Sun, 17 Nov 2013 20:33:56 +0100 Subject: [PATCH 02/80] move testRender to texRender --- textRender.py => texRender.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename textRender.py => texRender.py (100%) diff --git a/textRender.py b/texRender.py similarity index 100% rename from textRender.py rename to texRender.py From ead4e15d9244e324b54a8128db859be97a73fbfe Mon Sep 17 00:00:00 2001 From: lafrite Date: Sun, 8 Dec 2013 21:02:19 +0100 Subject: [PATCH 03/80] Render done need to clean expression --- expression.py | 89 +++++++++++++++++++----- render.py | 184 ++++++++++++++++++++++++++++++++++++++++++++++++++ texRender.py | 55 --------------- 3 files changed, 256 insertions(+), 72 deletions(-) create mode 100644 render.py delete mode 100644 texRender.py diff --git a/expression.py b/expression.py index f46562a..389caa8 100644 --- a/expression.py +++ b/expression.py @@ -8,6 +8,7 @@ class Expression(object): """A calculus expression. Today it can andle only expression with numbers later it will be able to manipulate unknown""" PRIORITY = {"*" : 3, "/": 3, "+": 2, "-":2, "(": 1} + TEXSYM = {"*" : " \\times ", "+" : " + " , "-" : " - "} def __init__(self, exp): """ Initiate the expression @@ -191,11 +192,60 @@ class Expression(object): def postfix_tokens(self, val): self._postfix_tokens = val + # ---------------------- + # Latex render + + @classmethod + def texRender(cls, postfix_tokens): + """@todo: Docstring for texRender + + :param postfix_tokens: the postfix list of tokens to transform into infix form. + :returns: the latex render ready to insert into a maht environment + + >>> Expression.post2in_fix([2, 5, '+', 1, '-', 3, 4, '*', '/']) + ['( ', 2, '+', 5, '-', 1, ' )', '/', '( ', 3, '*', 4, ' )'] + "\frac{2 + 5 - 1}{3 \times 4)" + >>> Expression.post2in_fix([2]) + "2" + """ + operandeStack = Stack() + + for token in postfix_tokens: + if cls.isOperator(token): + op2 = operandeStack.pop() + op1 = operandeStack.pop() + + if token == "/": + res = "\\frac{" + str(op1) + "}{" + str(op2) + "}" + + else: + if cls.needPar(op2, token, "after"): + op2 = "\\left( " + str(op2) + " \\right) " + + if cls.needPar(op1, token, "before"): + op2 = "\\left( " + str(op1) + " \\right) " + res = str(op1) + cls.TEXSYM[token] + str(op2) + + operandeStack.push(res) + + else: + operandeStack.push(token) + + # Manip pour gerer les cas similaires au deuxième exemple + infix_tokens = operandeStack.pop() + if type(infix_tokens) == list: + infix_tokens = flatten_list(infix_tokens) + elif cls.isNumber(infix_tokens): + infix_tokens = [infix_tokens] + + return infix_tokens + + # ---------------------- # "fix" tranformations @classmethod - def in2post_fix(self, infix_tokens): + def in2post_fix(cls, infix_tokens): """ From the infix_tokens list compute the corresponding postfix_tokens list @param infix_tokens: the infix list of tokens to transform into postfix form. @@ -215,9 +265,9 @@ class Expression(object): while topToken != "(": postfixList.append(topToken) topToken = opStack.pop() - elif self.isOperator(token): + elif cls.isOperator(token): # On doit ajouter la condition == str sinon python ne veut pas tester l'appartenance à la chaine de caractère. - while (not opStack.isEmpty()) and (self.PRIORITY[opStack.peek()] >= self.PRIORITY[token]): + while (not opStack.isEmpty()) and (cls.PRIORITY[opStack.peek()] >= cls.PRIORITY[token]): postfixList.append(opStack.pop()) opStack.push(token) else: @@ -229,7 +279,7 @@ class Expression(object): return postfixList @classmethod - def post2in_fix(self, postfix_tokens): + def post2in_fix(cls, postfix_tokens): """ From the postfix_tokens list compute the corresponding infix_tokens list @param postfix_tokens: the postfix list of tokens to transform into infix form. @@ -243,14 +293,14 @@ class Expression(object): operandeStack = Stack() for token in postfix_tokens: - if self.isOperator(token): + if cls.isOperator(token): op2 = operandeStack.pop() - if self.needPar(op2, token, "after"): + if cls.needPar(op2, token, "after"): op2 = ["( ", op2, " )"] op1 = operandeStack.pop() - if self.needPar(op1, token, "before"): + if cls.needPar(op1, token, "before"): op1 = ["( ", op1, " )"] res = [op1, token, op2] @@ -263,7 +313,7 @@ class Expression(object): infix_tokens = operandeStack.pop() if type(infix_tokens) == list: infix_tokens = flatten_list(infix_tokens) - elif self.isNumber(infix_tokens): + elif cls.isNumber(infix_tokens): infix_tokens = [infix_tokens] return infix_tokens @@ -272,7 +322,7 @@ class Expression(object): # Tools for placing parenthesis in infix notation @classmethod - def needPar(self, operande, operator, posi = "after"): + def needPar(cls, operande, operator, posi = "after"): """Says whether or not the operande needs parenthesis :param operande: the operande @@ -280,13 +330,13 @@ class Expression(object): :param posi: "after"(default) if the operande will be after the operator, "before" othewise :returns: bollean """ - if self.isNumber(operande) and operande < 0: + if cls.isNumber(operande) and operande < 0: return 1 - elif not self.isNumber(operande): + elif not cls.isNumber(operande): # Si c'est une grande expression ou un chiffre négatif - stand_alone = self.get_main_op(operande) + stand_alone = cls.get_main_op(operande) # Si la priorité de l'operande est plus faible que celle de l'opérateur - minor_priority = self.PRIORITY[self.get_main_op(operande)] < self.PRIORITY[operator] + minor_priority = cls.PRIORITY[cls.get_main_op(operande)] < cls.PRIORITY[operator] # Si l'opérateur est -/ pour after ou juste / pour before special = (operator in "-/" and posi == "after") or (operator in "/" and posi == "before") @@ -295,13 +345,16 @@ class Expression(object): return 0 @classmethod - def get_main_op(self, tokens): + def get_main_op(cls, tokens): """Getting the main operation of the list of tokens :param exp: the list of tokens :returns: the main operation (+, -, * or /) or 0 if the expression is only one element """ + + print("tokens: ", tokens) + parStack = Stack() if len(tokens) == 1: @@ -315,10 +368,12 @@ class Expression(object): parStack.push(token) elif token == ")": parStack.pop() - elif self.isOperator(token) and parStack.isEmpty(): + elif cls.isOperator(token) and parStack.isEmpty(): main_op.append(token) - return min(main_op, key = lambda s: self.PRIORITY[s]) + print("main_op", main_op) + + return min(main_op, key = lambda s: cls.PRIORITY[s]) ## --------------------- ## Computing the expression @@ -368,7 +423,7 @@ class Expression(object): def test(exp): a = Expression(exp) #for i in a.simplify(): - for i in a.simplify(render = render): + for i in a.simplify(render = Expression.texRender): print(i) print("\n") diff --git a/render.py b/render.py new file mode 100644 index 0000000..dad1d73 --- /dev/null +++ b/render.py @@ -0,0 +1,184 @@ +#!/usr/bin/env python +# encoding: utf-8 + +from generic import Stack,flatten_list +from fraction import Fraction + + + +class Render(object): + """A class which aims to create render functions from three dictionnaries: + - op_infix: dict of caracters + - op_postfix: dict of 2 arguments functions + - other: dict of caracters + Those three dictionnaries while define how a postfix expression will be transform into a string. + """ + + PRIORITY = {"*" : 3, "/": 3, "+": 2, "-":2, "(": 1} + + def __init__(self, op_infix = {}, op_postfix = {}, other = {}): + """Initiate the render + + @param op_infix: the dictionnary of infix operator with how they have to be render + @param op_postfix: the dictionnary of postfix operator with how they have to be render + @param other: other caracters like parenthesis. + """ + + self.op_infix = op_infix + self.op_postfix = op_postfix + self.other = other + # TODO: there may be issues with PRIORITY + + self.operators = list(self.op_infix.keys()) + list(self.op_postfix.keys()) + list(self.other.keys()) + + def __call__(self, postfix_tokens): + """Make the object acting like a function + + :param postfix_tokens: the list of postfix tokens to be render + :returns: the render string + + """ + operandeStack = Stack() + + + for token in postfix_tokens: + if self.isOperator(token): + op2 = operandeStack.pop() + + if self.needPar(op2, token, "after"): + op2 = self.other["("] + str(op2) + self.other[")"] + op1 = operandeStack.pop() + + if self.needPar(op1, token, "before"): + op1 = self.other["("] + str(op1) + self.other[")"] + + + if token in self.op_infix: + res = fstr(str(op1) + self.op_infix[token] + str(op2)) + + elif token in self.op_postfix: + res = fstr(self.op_postfix[token](str(op1), str(op2))) + + # Trick to remember the main op when the render will be done! + res.mainOp = token + + operandeStack.push(res) + + else: + operandeStack.push(token) + + # Manip pour gerer les cas similaires au deuxième exemple + infix_tokens = operandeStack.pop() + if type(infix_tokens) == list: + infix_tokens = flatten_list(infix_tokens) + elif self.isNumber(infix_tokens): + infix_tokens = [infix_tokens] + + return infix_tokens + + # --------------------- + # Tools for placing parenthesis in infix notation + + def needPar(self, operande, operator, posi = "after"): + """Says whether or not the operande needs parenthesis + + :param operande: the operande + :param operator: the operator + :param posi: "after"(default) if the operande will be after the operator, "before" othewise + :returns: bollean + """ + if self.isNumber(operande) and operande < 0: + return 1 + elif not self.isNumber(operande): + # Si c'est une grande expression ou un chiffre négatif + stand_alone = self.get_main_op(operande) + # Si la priorité de l'operande est plus faible que celle de l'opérateur + minor_priority = self.PRIORITY[self.get_main_op(operande)] < self.PRIORITY[operator] + # Si l'opérateur est -/ pour after ou juste / pour before + special = (operator in "-/" and posi == "after") or (operator in "/" and posi == "before") + + return stand_alone and (minor_priority or special) + else: + return 0 + + def get_main_op(self, tokens): + """Getting the main operation of the list of tokens + + :param exp: the list of tokens + :returns: the main operation (+, -, * or /) or 0 if the expression is only one element + + """ + if hasattr(tokens, "mainOp"): + return tokens.mainOp + + if len(tokens) == 1: + # Si l'expression n'est qu'un élément + return 0 + + parStack = Stack() + main_op = [] + + for token in tokens: + if token == "(": + parStack.push(token) + elif token == ")": + parStack.pop() + elif self.isOperator(token) and parStack.isEmpty(): + main_op.append(token) + + return min(main_op, key = lambda s: self.PRIORITY[s]) + + ## --------------------- + ## Recognize numbers and operators + + def isNumber(self, exp): + """Check if the expression can be a number which means that it is not a operator + + :param exp: an expression + :returns: True if the expression can be a number and false otherwise + + """ + return type(exp) == int or type(exp) == Fraction + + def isOperator(self, exp): + """Check if the expression is in self.operators + + :param exp: an expression + :returns: boolean + + """ + return (type(exp) == str and exp in self.operators) + +class fstr(str): + """Fake string - they are used to stock the main operation of an rendered expression""" + pass + +txt_infix = {"+": " + ", "-": " - ", "*": " * ", "/" : " / "} +txt_postfix = {} +txt_other = {"(": "( ", ")": ") "} +txt_render = Render(txt_infix, txt_postfix, txt_other) + + +def texFrac(op1, op2): + if op1[0] == "(" and op1[-1] == ")": + op1 = op1[1:-1] + if op2[0] == "(" and op2[-1] == ")": + op2 = op2[1:-1] + return "\\frac{" + str(op1) + "}{" + str(op2) + "}" + +tex_infix = {"+": " + ", "-": " - ", "*": " * "} +tex_postfix = {"/": texFrac} +tex_other = {"(": "( ", ")": " )"} +tex_render = Render(tex_infix, tex_postfix, tex_other) + +if __name__ == '__main__': + #exp = [2, 5, '+', 1, '-', 3, 4, '*', '/'] + #print(txt_render(exp)) + exp = [2, 5, '+', 1, '-', 3, 4, '*', '/', 3, '+'] + print(tex_render(exp)) + + +# ----------------------------- +# Reglages pour 'vim' +# vim:set autoindent expandtab tabstop=4 shiftwidth=4: +# cursor: 16 del diff --git a/texRender.py b/texRender.py deleted file mode 100644 index 5ccb83b..0000000 --- a/texRender.py +++ /dev/null @@ -1,55 +0,0 @@ -#!/usr/bin/env python -# encoding: utf-8 - -from generic import Stack - - -def post2in_fix(postfix_tokens): - """ From the postfix_tokens list compute the corresponding infix_tokens list - - @param postfix_tokens: the postfix list of tokens to transform into infix form. If nothing is set, it takes the value self.postfix_tokens - @return: the corresponding infix list of tokens if postfix_tokens is set. nothing otherwise but stock it in self.infix_tokens - - >>> post2in_fix([2, 5, '+', 1, '-', 3, 4, '*', '/']) - ['( ', 2, '+', 5, '-', 1, ' )', '/', '( ', 3, '*', 4, ' )'] - """ - operandeStack = Stack() - - for token in postfix_tokens: - if self.isOperator(token): - op2 = operandeStack.pop() - if self.needPar(op2, token, "after"): - op2 = ["( ", op2, " )"] - op1 = operandeStack.pop() - if self.needPar(op1, token, "before"): - op1 = ["( ", op1, " )"] - res = [op1, token, op2] - - operandeStack.push(res) - - else: - operandeStack.push(token) - - infix_tokens = flatten_list(operandeStack.pop()) - - return infix_tokens - -def textRender(postfix_tokens): - """ A text baser render - - :param postfix_tokens: The postfix list of tokens - :returns: the text expression - - """ - infix_tokens = post2in_fix(postfix_tokens) - return ' '.join(infix_tokens) - -if __name__ == '__main__': - - import doctest - doctest.testmod() - -# ----------------------------- -# Reglages pour 'vim' -# vim:set autoindent expandtab tabstop=4 shiftwidth=4: -# cursor: 16 del From c25fee213508f1ee67cc1f2e62622656d599f8ca Mon Sep 17 00:00:00 2001 From: lafrite Date: Sun, 8 Dec 2013 21:38:11 +0100 Subject: [PATCH 04/80] add in2post_fix function --- render.py | 67 ++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 47 insertions(+), 20 deletions(-) diff --git a/render.py b/render.py index dad1d73..5931012 100644 --- a/render.py +++ b/render.py @@ -16,18 +16,21 @@ class Render(object): PRIORITY = {"*" : 3, "/": 3, "+": 2, "-":2, "(": 1} - def __init__(self, op_infix = {}, op_postfix = {}, other = {}): + def __init__(self, op_infix = {}, op_postfix = {}, other = {}, join = " "): """Initiate the render @param op_infix: the dictionnary of infix operator with how they have to be render @param op_postfix: the dictionnary of postfix operator with how they have to be render @param other: other caracters like parenthesis. + @param raw: the caracter for joining the list of tokens (if False then it returns the list of tokens) """ self.op_infix = op_infix self.op_postfix = op_postfix self.other = other - # TODO: there may be issues with PRIORITY + # TODO: there may be issues with PRIORITY if a sign does not appear in PRIORITY + + self.join = join self.operators = list(self.op_infix.keys()) + list(self.op_postfix.keys()) + list(self.other.keys()) @@ -46,18 +49,18 @@ class Render(object): op2 = operandeStack.pop() if self.needPar(op2, token, "after"): - op2 = self.other["("] + str(op2) + self.other[")"] + op2 = [self.other["("] , op2 , self.other[")"]] op1 = operandeStack.pop() if self.needPar(op1, token, "before"): - op1 = self.other["("] + str(op1) + self.other[")"] + op1 = [self.other["("] , op1 , self.other[")"]] if token in self.op_infix: - res = fstr(str(op1) + self.op_infix[token] + str(op2)) + res = flist([op1 , self.op_infix[token] , op2]) elif token in self.op_postfix: - res = fstr(self.op_postfix[token](str(op1), str(op2))) + res = flist([self.op_postfix[token](op1, op2)]) # Trick to remember the main op when the render will be done! res.mainOp = token @@ -69,12 +72,15 @@ class Render(object): # Manip pour gerer les cas similaires au deuxième exemple infix_tokens = operandeStack.pop() - if type(infix_tokens) == list: + if type(infix_tokens) == list or type(infix_tokens) == flist: infix_tokens = flatten_list(infix_tokens) elif self.isNumber(infix_tokens): infix_tokens = [infix_tokens] - return infix_tokens + if self.join: + return self.join.join([str(t) for t in infix_tokens]) + else: + return infix_tokens # --------------------- # Tools for placing parenthesis in infix notation @@ -149,33 +155,54 @@ class Render(object): """ return (type(exp) == str and exp in self.operators) -class fstr(str): - """Fake string - they are used to stock the main operation of an rendered expression""" +class flist(list): + """Fake list- they are used to stock the main operation of an rendered expression""" pass -txt_infix = {"+": " + ", "-": " - ", "*": " * ", "/" : " / "} -txt_postfix = {} -txt_other = {"(": "( ", ")": ") "} -txt_render = Render(txt_infix, txt_postfix, txt_other) +# ------------------------ +# A console render + +txt_infix = {"+": "+", "-": "-", "*": "*", "/" : "/"} +txt_postfix = {} +txt_other = {"(": "(", ")": ")"} + +txt = Render(txt_infix, txt_postfix, txt_other) + +# ------------------------ +# A infix to postfix list convertor + +i2p_infix = {"+": "+", "-": "-", "*": "*", "/" : "/"} +i2p_postfix = {} +i2p_other = {"(": "(", ")": ")"} + +in2post_fix = Render(i2p_infix, i2p_postfix, i2p_other, join = False) + +# ------------------------ +# A latex render def texFrac(op1, op2): if op1[0] == "(" and op1[-1] == ")": op1 = op1[1:-1] if op2[0] == "(" and op2[-1] == ")": op2 = op2[1:-1] - return "\\frac{" + str(op1) + "}{" + str(op2) + "}" + return ["\\frac{" , op1 , "}{" , op2 , "}"] tex_infix = {"+": " + ", "-": " - ", "*": " * "} tex_postfix = {"/": texFrac} -tex_other = {"(": "( ", ")": " )"} -tex_render = Render(tex_infix, tex_postfix, tex_other) +tex_other = {"(": "(", ")": ")"} + +tex = Render(tex_infix, tex_postfix, tex_other) + + if __name__ == '__main__': - #exp = [2, 5, '+', 1, '-', 3, 4, '*', '/'] - #print(txt_render(exp)) + exp = [2, 5, '+', 1, '-', 3, 4, '*', '/'] + print(txt(exp)) exp = [2, 5, '+', 1, '-', 3, 4, '*', '/', 3, '+'] - print(tex_render(exp)) + print(tex(exp)) + exp = [2, 5, '+', 1, '-', 3, 4, '*', '/', 3, '+'] + print(in2post_fix(exp)) # ----------------------------- From 31b433faf540656491497207cb775568dc408609 Mon Sep 17 00:00:00 2001 From: lafrite Date: Sun, 8 Dec 2013 22:24:27 +0100 Subject: [PATCH 05/80] Nice render (even with latex! :D) And seems to work --- expression.py | 157 ++------------------------------------------------ render.py | 59 ++++++++++++------- 2 files changed, 43 insertions(+), 173 deletions(-) diff --git a/expression.py b/expression.py index 389caa8..2739e87 100644 --- a/expression.py +++ b/expression.py @@ -3,12 +3,12 @@ from generic import Stack, flatten_list, expand_list from fraction import Fraction +from render import txt_render, post2in_fix, tex_render class Expression(object): """A calculus expression. Today it can andle only expression with numbers later it will be able to manipulate unknown""" PRIORITY = {"*" : 3, "/": 3, "+": 2, "-":2, "(": 1} - TEXSYM = {"*" : " \\times ", "+" : " + " , "-" : " - "} def __init__(self, exp): """ Initiate the expression @@ -162,7 +162,7 @@ class Expression(object): return self._infix_tokens elif self._postfix_tokens: - self._infix_tokens = self.post2in_fix(self._postfix_tokens) + self._infix_tokens = post2in_fix(self._postfix_tokens) return self._infix_tokens else: @@ -192,55 +192,6 @@ class Expression(object): def postfix_tokens(self, val): self._postfix_tokens = val - # ---------------------- - # Latex render - - @classmethod - def texRender(cls, postfix_tokens): - """@todo: Docstring for texRender - - :param postfix_tokens: the postfix list of tokens to transform into infix form. - :returns: the latex render ready to insert into a maht environment - - >>> Expression.post2in_fix([2, 5, '+', 1, '-', 3, 4, '*', '/']) - ['( ', 2, '+', 5, '-', 1, ' )', '/', '( ', 3, '*', 4, ' )'] - "\frac{2 + 5 - 1}{3 \times 4)" - >>> Expression.post2in_fix([2]) - "2" - """ - operandeStack = Stack() - - for token in postfix_tokens: - if cls.isOperator(token): - op2 = operandeStack.pop() - op1 = operandeStack.pop() - - if token == "/": - res = "\\frac{" + str(op1) + "}{" + str(op2) + "}" - - else: - if cls.needPar(op2, token, "after"): - op2 = "\\left( " + str(op2) + " \\right) " - - if cls.needPar(op1, token, "before"): - op2 = "\\left( " + str(op1) + " \\right) " - res = str(op1) + cls.TEXSYM[token] + str(op2) - - operandeStack.push(res) - - else: - operandeStack.push(token) - - # Manip pour gerer les cas similaires au deuxième exemple - infix_tokens = operandeStack.pop() - if type(infix_tokens) == list: - infix_tokens = flatten_list(infix_tokens) - elif cls.isNumber(infix_tokens): - infix_tokens = [infix_tokens] - - return infix_tokens - - # ---------------------- # "fix" tranformations @@ -278,103 +229,6 @@ class Expression(object): return postfixList - @classmethod - def post2in_fix(cls, postfix_tokens): - """ From the postfix_tokens list compute the corresponding infix_tokens list - - @param postfix_tokens: the postfix list of tokens to transform into infix form. - @return: the corresponding infix list of tokens if postfix_tokens. - - >>> Expression.post2in_fix([2, 5, '+', 1, '-', 3, 4, '*', '/']) - ['( ', 2, '+', 5, '-', 1, ' )', '/', '( ', 3, '*', 4, ' )'] - >>> Expression.post2in_fix([2]) - [2] - """ - operandeStack = Stack() - - for token in postfix_tokens: - if cls.isOperator(token): - op2 = operandeStack.pop() - - if cls.needPar(op2, token, "after"): - op2 = ["( ", op2, " )"] - op1 = operandeStack.pop() - - if cls.needPar(op1, token, "before"): - op1 = ["( ", op1, " )"] - res = [op1, token, op2] - - operandeStack.push(res) - - else: - operandeStack.push(token) - - # Manip pour gerer les cas similaires au deuxième exemple - infix_tokens = operandeStack.pop() - if type(infix_tokens) == list: - infix_tokens = flatten_list(infix_tokens) - elif cls.isNumber(infix_tokens): - infix_tokens = [infix_tokens] - - return infix_tokens - - # --------------------- - # Tools for placing parenthesis in infix notation - - @classmethod - def needPar(cls, operande, operator, posi = "after"): - """Says whether or not the operande needs parenthesis - - :param operande: the operande - :param operator: the operator - :param posi: "after"(default) if the operande will be after the operator, "before" othewise - :returns: bollean - """ - if cls.isNumber(operande) and operande < 0: - return 1 - elif not cls.isNumber(operande): - # Si c'est une grande expression ou un chiffre négatif - stand_alone = cls.get_main_op(operande) - # Si la priorité de l'operande est plus faible que celle de l'opérateur - minor_priority = cls.PRIORITY[cls.get_main_op(operande)] < cls.PRIORITY[operator] - # Si l'opérateur est -/ pour after ou juste / pour before - special = (operator in "-/" and posi == "after") or (operator in "/" and posi == "before") - - return stand_alone and (minor_priority or special) - else: - return 0 - - @classmethod - def get_main_op(cls, tokens): - """Getting the main operation of the list of tokens - - :param exp: the list of tokens - :returns: the main operation (+, -, * or /) or 0 if the expression is only one element - - """ - - print("tokens: ", tokens) - - parStack = Stack() - - if len(tokens) == 1: - # Si l'expression n'est qu'un élément - return 0 - - main_op = [] - - for token in tokens: - if token == "(": - parStack.push(token) - elif token == ")": - parStack.pop() - elif cls.isOperator(token) and parStack.isEmpty(): - main_op.append(token) - - print("main_op", main_op) - - return min(main_op, key = lambda s: cls.PRIORITY[s]) - ## --------------------- ## Computing the expression @@ -423,15 +277,12 @@ class Expression(object): def test(exp): a = Expression(exp) #for i in a.simplify(): - for i in a.simplify(render = Expression.texRender): + #for i in a.simplify(render = txt_render): + for i in a.simplify(render = tex_render): print(i) print("\n") -def render(tokens): - post_tokens = Expression.post2in_fix(tokens) - return ' '.join([str(t) for t in post_tokens]) - if __name__ == '__main__': exp = "1 + 3 * 5" test(exp) diff --git a/render.py b/render.py index 5931012..d101b66 100644 --- a/render.py +++ b/render.py @@ -16,13 +16,14 @@ class Render(object): PRIORITY = {"*" : 3, "/": 3, "+": 2, "-":2, "(": 1} - def __init__(self, op_infix = {}, op_postfix = {}, other = {}, join = " "): + def __init__(self, op_infix = {}, op_postfix = {}, other = {}, join = " ", type_render = {int: str, Fraction: str}): """Initiate the render @param op_infix: the dictionnary of infix operator with how they have to be render @param op_postfix: the dictionnary of postfix operator with how they have to be render @param other: other caracters like parenthesis. @param raw: the caracter for joining the list of tokens (if False then it returns the list of tokens) + @param type_render: how to render number (str or tex for fractions for example) """ self.op_infix = op_infix @@ -31,6 +32,7 @@ class Render(object): # TODO: there may be issues with PRIORITY if a sign does not appear in PRIORITY self.join = join + self.type_render = type_render self.operators = list(self.op_infix.keys()) + list(self.op_postfix.keys()) + list(self.other.keys()) @@ -46,16 +48,15 @@ class Render(object): for token in postfix_tokens: if self.isOperator(token): + op2 = operandeStack.pop() - if self.needPar(op2, token, "after"): op2 = [self.other["("] , op2 , self.other[")"]] + op1 = operandeStack.pop() - if self.needPar(op1, token, "before"): op1 = [self.other["("] , op1 , self.other[")"]] - if token in self.op_infix: res = flist([op1 , self.op_infix[token] , op2]) @@ -70,7 +71,7 @@ class Render(object): else: operandeStack.push(token) - # Manip pour gerer les cas similaires au deuxième exemple + # Manip pour gerer les cas de listes imbriquées dans d'autres listes infix_tokens = operandeStack.pop() if type(infix_tokens) == list or type(infix_tokens) == flist: infix_tokens = flatten_list(infix_tokens) @@ -78,10 +79,23 @@ class Render(object): infix_tokens = [infix_tokens] if self.join: - return self.join.join([str(t) for t in infix_tokens]) + return self.join.join(flatten_list([self.render_from_type(t) for t in infix_tokens])) else: return infix_tokens + def render_from_type(self, op): + """ If the op is a number, it transforms it with type_render conditions + + :param op: the operator + :returns: the op transformed if it's necessary + + """ + if self.isNumber(op): + return self.type_render[type(op)](op) + else: + return op + + # --------------------- # Tools for placing parenthesis in infix notation @@ -137,7 +151,8 @@ class Render(object): ## --------------------- ## Recognize numbers and operators - def isNumber(self, exp): + @staticmethod + def isNumber( exp): """Check if the expression can be a number which means that it is not a operator :param exp: an expression @@ -167,32 +182,36 @@ txt_infix = {"+": "+", "-": "-", "*": "*", "/" : "/"} txt_postfix = {} txt_other = {"(": "(", ")": ")"} -txt = Render(txt_infix, txt_postfix, txt_other) +txt_render = Render(txt_infix, txt_postfix, txt_other) # ------------------------ # A infix to postfix list convertor -i2p_infix = {"+": "+", "-": "-", "*": "*", "/" : "/"} -i2p_postfix = {} -i2p_other = {"(": "(", ")": ")"} +p2i_infix = {"+": "+", "-": "-", "*": "*", "/" : "/"} +p2i_postfix = {} +p2i_other = {"(": "(", ")": ")"} -in2post_fix = Render(i2p_infix, i2p_postfix, i2p_other, join = False) +post2in_fix = Render(p2i_infix, p2i_postfix, p2i_other, join = False) # ------------------------ # A latex render -def texFrac(op1, op2): - if op1[0] == "(" and op1[-1] == ")": +def texSlash(op1, op2): + if not Render.isNumber(op1) and op1[0] == "(" and op1[-1] == ")": op1 = op1[1:-1] - if op2[0] == "(" and op2[-1] == ")": + if not Render.isNumber(op2) and op2[0] == "(" and op2[-1] == ")": op2 = op2[1:-1] return ["\\frac{" , op1 , "}{" , op2 , "}"] -tex_infix = {"+": " + ", "-": " - ", "*": " * "} -tex_postfix = {"/": texFrac} -tex_other = {"(": "(", ")": ")"} +def texFrac(frac): + return ["\\frac{" , str(frac._num) , "}{" , str(frac._denom) , "}"] -tex = Render(tex_infix, tex_postfix, tex_other) +tex_infix = {"+": " + ", "-": " - ", "*": " * "} +tex_postfix = {"/": texSlash} +tex_other = {"(": "(", ")": ")"} +tex_type_render = {int: str, Fraction: texFrac} + +tex_render = Render(tex_infix, tex_postfix, tex_other, type_render = tex_type_render) @@ -202,7 +221,7 @@ if __name__ == '__main__': exp = [2, 5, '+', 1, '-', 3, 4, '*', '/', 3, '+'] print(tex(exp)) exp = [2, 5, '+', 1, '-', 3, 4, '*', '/', 3, '+'] - print(in2post_fix(exp)) + print(post2in_fix(exp)) # ----------------------------- From db44c787edcd8b8cd113fb3f55c772f28a19036a Mon Sep 17 00:00:00 2001 From: lafrite Date: Wed, 15 Jan 2014 15:58:45 +0100 Subject: [PATCH 06/80] Mod latex "*" sign --- render.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/render.py b/render.py index d101b66..05def52 100644 --- a/render.py +++ b/render.py @@ -206,7 +206,7 @@ def texSlash(op1, op2): def texFrac(frac): return ["\\frac{" , str(frac._num) , "}{" , str(frac._denom) , "}"] -tex_infix = {"+": " + ", "-": " - ", "*": " * "} +tex_infix = {"+": " + ", "-": " - ", "*": " \\times "} tex_postfix = {"/": texSlash} tex_other = {"(": "(", ")": ")"} tex_type_render = {int: str, Fraction: texFrac} From b6930c144b268d55d2785c7d7c52a388dcc14aa8 Mon Sep 17 00:00:00 2001 From: lafrite Date: Fri, 17 Jan 2014 12:48:48 +0100 Subject: [PATCH 07/80] Expression have 2 new method to be pritned --- expression.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/expression.py b/expression.py index 2739e87..f941766 100644 --- a/expression.py +++ b/expression.py @@ -26,13 +26,25 @@ class Expression(object): self.feed_fix() # Determine le fix et range la liste dans self.[fix]_tokens + def __str__(self): + """Overload str as it aim to be use in console the render is txt_render""" + return txt_render(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 def simplify(self, render = lambda x:str(x)): """ Generator which return steps for computing the expression - @param render: function which render the list of token (postfix form now) + @param render: function which render the list of token (postfix form now) to string """ if not self.can_go_further(): From 6e541144bb11e3cbaaa732af95605ef9e7b1b1c9 Mon Sep 17 00:00:00 2001 From: lafrite Date: Wed, 15 Jan 2014 16:01:07 +0100 Subject: [PATCH 08/80] add TODOlist --- TODO | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 TODO diff --git a/TODO b/TODO new file mode 100644 index 0000000..0c86743 --- /dev/null +++ b/TODO @@ -0,0 +1,4 @@ +# Todolist + +* Improve fix recognition +* More flexible expression parsing From b52ec78d21516b64d143032597f775b292384062 Mon Sep 17 00:00:00 2001 From: lafrite Date: Wed, 15 Jan 2014 16:32:12 +0100 Subject: [PATCH 09/80] parsing expression is done! :D --- expression.py | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/expression.py b/expression.py index f941766..9433ce4 100644 --- a/expression.py +++ b/expression.py @@ -113,19 +113,27 @@ class Expression(object): def str2tokens(self, exp): """ Parse the expression, ie tranform a string into a list of tokens + /!\ float are not availiable yet! + :param exp: The expression (a string) :returns: list of token """ - tokens = exp.split(" ") + tokens = [''] - for (i,t) in enumerate(tokens): - try: - tokens[i] = int(t) - except ValueError: - pass + for character in exp: + if character.isdigit(): + if type(tokens[-1]) == int: + tokens[-1] = tokens[-1]*10 + int(character) + else: + tokens.append(int(character)) + elif character in "+-*/()": + tokens.append(character) + elif character != " ": + raise ValueError("{} is an unvalid character".format(character)) - return tokens + print(tokens[1:]) + return tokens[1:] # --------------------- # "fix" recognition @@ -332,10 +340,10 @@ if __name__ == '__main__': exp = "( 2 + 5 ) / ( 3 * 4 ) + 1 / 12" test(exp) - exp = "( 2 + 5 ) / ( 3 * 4 ) + 1 / 2" + exp = "( 2+ 5 )/( 3 * 4 ) + 1 / 2" test(exp) - exp = "( 2 + 5 ) / ( 3 * 4 ) + 1 / 12 + 5 * 5" + exp="(2+5)/(3*4)+1/12+5*5" test(exp) import doctest From 95bc15b91721b2256e1011b27a535a8c36707dab Mon Sep 17 00:00:00 2001 From: lafrite Date: Wed, 15 Jan 2014 16:41:51 +0100 Subject: [PATCH 10/80] handle - at the beginig of expression and after "(" --- expression.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/expression.py b/expression.py index 9433ce4..9309c1d 100644 --- a/expression.py +++ b/expression.py @@ -125,6 +125,10 @@ class Expression(object): if character.isdigit(): if type(tokens[-1]) == int: tokens[-1] = tokens[-1]*10 + int(character) + # Special case for "-" at the begining of an expression or before "(" + elif tokens[-1] == "-" and \ + str(tokens[-2]) in " (": + tokens[-1] = - int(character) else: tokens.append(int(character)) elif character in "+-*/()": @@ -343,7 +347,10 @@ if __name__ == '__main__': exp = "( 2+ 5 )/( 3 * 4 ) + 1 / 2" test(exp) - exp="(2+5)/(3*4)+1/12+5*5" + exp="(-2+5)/(3*4)+1/12+5*5" + test(exp) + + exp="-2*4*(12 + 1)" test(exp) import doctest From 856cf8a323c40ed131cff1e2c50580e5b87be724 Mon Sep 17 00:00:00 2001 From: lafrite Date: Wed, 15 Jan 2014 16:51:32 +0100 Subject: [PATCH 11/80] handle implied "*" --- expression.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/expression.py b/expression.py index 9309c1d..9164709 100644 --- a/expression.py +++ b/expression.py @@ -131,8 +131,15 @@ class Expression(object): tokens[-1] = - int(character) else: tokens.append(int(character)) - elif character in "+-*/()": + + elif character in "+-*/)": tokens.append(character) + + elif character in "(": + if self.isNumber(tokens[-1]) or tokens[-1] == ")": + tokens.append("*") + tokens.append(character) + elif character != " ": raise ValueError("{} is an unvalid character".format(character)) @@ -350,7 +357,7 @@ if __name__ == '__main__': exp="(-2+5)/(3*4)+1/12+5*5" test(exp) - exp="-2*4*(12 + 1)" + exp="-2*4(12 + 1)(3-12)" test(exp) import doctest From fbb89512163d4343e915bc4079cd3eccb34f6dcc Mon Sep 17 00:00:00 2001 From: lafrite Date: Wed, 15 Jan 2014 16:54:33 +0100 Subject: [PATCH 12/80] add a bug repport! --- TODO | 5 +++-- expression.py | 4 ++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/TODO b/TODO index 0c86743..e88367a 100644 --- a/TODO +++ b/TODO @@ -1,4 +1,5 @@ # Todolist -* Improve fix recognition -* More flexible expression parsing +* Improve fix recognition (DONE) +* More flexible expression parsing (DONE) +* bug: expression can't handle -(-2) diff --git a/expression.py b/expression.py index 9164709..428cbfc 100644 --- a/expression.py +++ b/expression.py @@ -360,6 +360,10 @@ if __name__ == '__main__': exp="-2*4(12 + 1)(3-12)" test(exp) + ## Can't handle it yet!! + #exp="-(-2)" + #test(exp) + import doctest doctest.testmod() From 1935a4471504ba004502cde8cb8848942db48957 Mon Sep 17 00:00:00 2001 From: lafrite Date: Wed, 15 Jan 2014 17:04:24 +0100 Subject: [PATCH 13/80] clean directory --- calculus.py | 409 --------------------------------------------- expression.py.back | 115 ------------- expression.py_back | 191 --------------------- 3 files changed, 715 deletions(-) delete mode 100644 calculus.py delete mode 100644 expression.py.back delete mode 100644 expression.py_back diff --git a/calculus.py b/calculus.py deleted file mode 100644 index 19d2fcc..0000000 --- a/calculus.py +++ /dev/null @@ -1,409 +0,0 @@ -#!/usr/bin/env python -# encoding: utf-8 - - -from generic import Stack -from fraction import Fraction - -def str2tokens(exp): - """Convert an expression into a list of tokens - - :param exp: The expression - :returns: the list of tokens - - >>> str2tokens("1 + 2") - [1, '+', 2] - - """ - tokens = exp.split(" ") - - for (i,t) in enumerate(tokens): - try: - tokens[i] = int(t) - except ValueError: - pass - - return tokens - -def infixToPostfix(infixTokens): - """Transform an infix list of tokens into postfix tokens - - :param infixTokens: an infix list of tokens - :returns: the corresponding postfix list of tokens - - :Example: - - >>> infixToPostfix([1, "+", 2]) - [1, 2, '+'] - - >>> infixToPostfix([1, "*", 2, "+", 3]) - [1, 2, '*', 3, '+'] - """ - - priority = {"*" : 3, "/": 3, "+": 2, "-":2, "(": 1} - - opStack = Stack() - postfixList = [] - - #infixTokens = infixExp.split(" ") - - for token in infixTokens: - if token == "(": - opStack.push(token) - elif token == ")": - topToken = opStack.pop() - while topToken != "(": - postfixList.append(topToken) - topToken = opStack.pop() - elif isOperation(token): - # On doit ajouter la condition == str sinon python ne veut pas tester l'appartenance à la chaine de caractère. - while (not opStack.isEmpty()) and (priority[opStack.peek()] >= priority[token]): - postfixList.append(opStack.pop()) - opStack.push(token) - else: - postfixList.append(token) - - while not opStack.isEmpty(): - postfixList.append(opStack.pop()) - - return postfixList - -def computePostfix(postfixTokens): - """Compute a postfix list of tokens - - :param postfixTokens: a postfix list of tokens - :returns: the result of the calculus - - """ - #print(postfixToInfix(postfixExp)) - # where to save numbers or - operandeStack = Stack() - - #tokenList = postfixExp.split(" ") - - for (i,token) in enumerate(postfixTokens): - if isOperation(token): - op2 = operandeStack.pop() - op1 = operandeStack.pop() - res = doMath(token, op1, op2) - operandeStack.push(res) - - #print("Operation: {op1} {op} {op2}".format(op1 = op1, op = token, op2 = op2)) - #print(operandeStack) - #print(postfixTokens[i+1:]) - newPostfix = " ".join(operandeStack + postfixTokens[i+1:]) - #print(postfixToInfix(newPostfix)) - - else: - operandeStack.push(token) - - return operandeStack.pop() - -def computePostfixBis(postfixTokens): - """Compute a postfix list of tokens like a good student - - :param postfixTokens: a postfix list of tokens - :returns: the result of the expression - - """ - # where to save numbers or - operandeStack = Stack() - - #tokenList = postfixExp.split(" ") - tokenList = postfixTokens.copy() - - steps = [] - - steps = [] - - # On fait le calcul jusqu'à n'avoir plus qu'un élément - while len(tokenList) > 1: - tmpTokenList = [] - # on va chercher les motifs du genre A B + pour les calculer - while len(tokenList) > 2: - if isNumber(tokenList[0]) and isNumber(tokenList[1]) and isOperation(tokenList[2]): - # S'il y a une opération à faire - op1 = tokenList[0] - op2 = tokenList[1] - token = tokenList[2] - - res = doMath(token, op1, op2) - - tmpTokenList.append(res) - - # Comme on vient de faire le calcul, on peut détruire aussi les deux prochains termes - del tokenList[0:3] - else: - tmpTokenList.append(tokenList[0]) - - del tokenList[0] - tmpTokenList += tokenList - - steps += expand_list(tmpTokenList) - - tokenList = steps[-1].copy() - - - return steps - -def isNumber(exp): - """Check if the expression can be a number - - :param exp: an expression - :returns: True if the expression can be a number and false otherwise - - """ - return type(exp) == int or type(exp) == Fraction - -def isOperation(exp): - """Check if the expression is an opération in "+-*/" - - :param exp: an expression - :returns: boolean - - """ - return (type(exp) == str and exp in "+-*/") - - -def doMath(op, op1, op2): - """Compute "op1 op op2" - - :param op: operator - :param op1: first operande - :param op2: second operande - :returns: string representing the result - - """ - operations = {"+": "__add__", "-": "__sub__", "*": "__mul__"} - if op == "/": - ans = [Fraction(op1, op2)] - ans += ans[0].simplify() - return ans - else: - return getattr(op1,operations[op])(op2) - - - -def postfixToInfix(postfixTokens): - """Transforms postfix list of tokens into infix string - - :param postfixTokens: a postfix list of tokens - :returns: the corresponding infix string - - """ - operandeStack = Stack() - - #print(postfixTokens) - - #tokenList = postfixExp.split(" ") - - for (i,token) in enumerate(postfixTokens): - if isOperation(token): - op2 = operandeStack.pop() - if needPar(op2, token, "after"): - op2 = "( " + str(op2) + " )" - op1 = operandeStack.pop() - if needPar(op1, token, "before"): - op1 = "( " + str(op1) + " )" - res = "{op1} {op} {op2}".format(op1 = op1, op = token, op2 = op2) - - operandeStack.push(res) - - else: - operandeStack.push(token) - - return operandeStack.pop() - -def needPar(operande, operator, posi = "after"): - """Says whether or not the operande needs parenthesis - - :param operande: the operande - :param operator: the operator - :param posi: "after"(default) if the operande will be after the operator, "before" othewise - :returns: bollean - """ - - priority = {"*" : 3, "/": 3, "+": 2, "-":2} - - if isNumber(operande) and operande < 0: - return 1 - elif not isNumber(operande): - # Si c'est une grande expression ou un chiffre négatif - stand_alone = get_main_op(operande) - # Si la priorité de l'operande est plus faible que celle de l'opérateur - #debug_var("stand_alone",stand_alone) - #debug_var("operande", type(operande)) - minor_priority = priority[get_main_op(operande)] < priority[operator] - # Si l'opérateur est -/ pour after ou juste / pour before - special = (operator in "-/" and posi == "after") or (operator in "/" and posi == "before") - - return stand_alone and (minor_priority or special) - else: - return 0 - -def get_main_op(tokens): - """Getting the main operation of the list of tokens - - :param exp: the list of tokens - :returns: the main operation (+, -, * or /) or 0 if the expression is only one element - - """ - - priority = {"*" : 3, "/": 3, "+": 2, "-":2} - - parStack = Stack() - #tokenList = exp.split(" ") - - if len(tokens) == 1: - # Si l'expression n'est qu'un élément - return 0 - - main_op = [] - - for token in tokens: - if token == "(": - parStack.push(token) - elif token == ")": - parStack.pop() - elif isOperation(token) and parStack.isEmpty(): - main_op.append(token) - - return min(main_op, key = lambda s: priority[s]) - - -def expand_list(list_list): - """Expand list of list - - :param list: the list to expande - :returns: list of expanded lists - - :Example: - - >>> expand_list([1,2,[3,4],5,[6,7,8]]) - [[1, 2, 3, 5, 6], [1, 2, 4, 5, 7], [1, 2, 4, 5, 8]] - >>> expand_list([1,2,4,5,6,7,8]) - [[1, 2, 4, 5, 6, 7, 8]] - - """ - list_in_list = [i for i in list_list if type(i) == list].copy() - - try: - nbr_ans_list = max([len(i) for i in list_in_list]) - - ans = [list_list.copy() for i in range(nbr_ans_list)] - for (i,l) in enumerate(ans): - for (j,e) in enumerate(l): - if type(e) == list: - ans[i][j] = e[min(i,len(e)-1)] - # S'il n'y a pas eut d'étapes intermédiaires (2e exemple) - except ValueError: - ans = [list_list] - - return ans - -def print_steps(steps): - """Juste print a list - - :param steps: @todo - :returns: @todo - - """ - print("{first} \t = {sec}".format(first = str_from_postfix(steps[0]), sec = str_from_postfix(steps[1]))) - for i in steps[2:]: - print("\t\t = {i}".format(i=str_from_postfix(i))) - - -def str_from_postfix(postfix): - """Return the string representing the expression - - :param postfix: a postfix ordered list of tokens - :returns: the corresponding string expression - - """ - infix = postfixToInfix(postfix) - return infix - - -def debug_var(name, var): - """print the name of the variable and the value - - :param name: the name of the variable - :param var: the variable we want information - """ - print(name, ": ", var) - - -def test(exp): - """Make various test on an expression - - """ - print("-------------") - print("Expression ",exp) - tokens = str2tokens(exp) - postfix = infixToPostfix(tokens) - #print("Postfix " , postfix) - #print(computePostfix(postfix)) - #print("Bis") - steps = [postfix] - steps += computePostfixBis(postfix) - print_steps(steps) - #print(postfixToInfix(postfix)) - #print(get_main_op(exp)) - - -if __name__ == '__main__': - exp = "1 + 3 * 5" - test(exp) - - #exp = "2 * 3 * 3 * 5" - #test(exp) - - #exp = "2 * 3 + 3 * 5" - #test(exp) - - #exp = "2 * ( 3 + 4 ) + 3 * 5" - #test(exp) - # - #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) - # - #exp = "( 2 + 5 ) * ( 3 - 4 )" - #test(exp) - # - #exp = "( 2 + 5 ) * ( 3 * 4 )" - #test(exp) - - #exp = "( 2 + 5 - 1 ) / ( 3 * 4 )" - #test(exp) - - exp = "( 2 + 5 ) / ( 3 * 4 ) + 1 / 12" - test(exp) - - exp = "( 2 + 5 ) / ( 3 * 4 ) + 1 / 2" - test(exp) - - exp = "( 2 + 5 ) / ( 3 * 4 ) + 1 / 12 + 5 * 5" - test(exp) - - #print(expand_list([1,2,['a','b','c'], 3, ['d','e']])) - - ## Ce denier pose un soucis. Pour le faire marcher il faudrai implémenter le calcul avec les fractions - #exp = "( 2 + 5 ) / 3 * 4" - #test(exp) - import doctest - doctest.testmod() - - -# ----------------------------- -# Reglages pour 'vim' -# vim:set autoindent expandtab tabstop=4 shiftwidth=4: -# cursor: 16 del diff --git a/expression.py.back b/expression.py.back deleted file mode 100644 index 75c3e4e..0000000 --- a/expression.py.back +++ /dev/null @@ -1,115 +0,0 @@ -#!/usr/bin/env python -# encoding: utf-8 - -from generic import Stack -from calculus import isNumber, doMath - - -class Expression(object): - """An arithmetic expression""" - - priority = {"*" : 3, "/": 3, "+": 2, "-":2, "(": 1} - - def __init__(self, exp, fix = "infix"): - """Initiate the expression. - - :param exp: the string representing the expression (infx form by default) - :param fix: form of the expression infix (default) - postfix - prefix - - """ - if fix == "infix": - self._exp_infix = exp - self._token_infix = exp.split(" ") - elif fix == "postfix": - self._token_postfix = exp - else: - print("Pas encore fait!") - - def infixToPostfix(self): - """Convert the infix expression into postfix expression. - """ - #@todo importer les exemples - opStack = Stack() - postfixList = [] - - for token in self._token_infix: - if token == "(": - opStack.push(token) - elif token == ")": - topToken = opStack.pop() - while topToken != "(": - postfixList.append(topToken) - topToken = opStack.pop() - elif token in "+-*/": - while (not opStack.isEmpty()) and (self.priority[opStack.peek()] >= self.priority[token]): - postfixList.append(opStack.pop()) - opStack.push(token) - else: - postfixList.append(token) - - while not opStack.isEmpty(): - postfixList.append(opStack.pop()) - - self._token_postfix = postfixList - self._exp_postfix = " ".join(postfixList) - - def computePostfix(self): - """Compute a postfix expression like a good student - - :returns: list of steps to reach the result (postfix form) - - """ - tokenList = self._token_postfix.copy() - tmpTokenList = [] - # on va chercher les motifs du genre A B + pour les calculer - while len(tokenList) > 2: - #print(tokenList[2]) - if isNumber(tokenList[0]) and isNumber(tokenList[1]) and tokenList[2] in "+-*/": - # S'il y a une opération à faire - op1 = tokenList[0] - op2 = tokenList[1] - token = tokenList[2] - res = doMath(token, op1, op2) - - tmpTokenList.append(res) - - # Comme on vient de faire le calcul, on peut détruire aussi les deux prochains termes - del tokenList[0:3] - else: - tmpTokenList.append(tokenList[0]) - - del tokenList[0] - tmpTokenList += tokenList - - if len(tmpTokenList) > 1: - next_steps = Expression(tmpTokenList, fix = "postfix").computePostfix() - else: - next_steps = tmpTokenList - print(self._exp_postfix) - return [self._exp_postfix] + next_steps - - -def test(exp): - """Make various test on an expression - - """ - print("-------------") - print("Expression ",exp) - expression = Expression(exp) - expression.infixToPostfix() - print(expression.computePostfix()) - -if __name__ == '__main__': - exp = "1 + 3 * 5" - test(exp) - - exp = "2 * 3 * 3 * 5" - test(exp) - - exp = "2 * 3 + 3 * 5" - test(exp) - -# ----------------------------- -# Reglages pour 'vim' -# vim:set autoindent expandtab tabstop=4 shiftwidth=4: -# cursor: 16 del diff --git a/expression.py_back b/expression.py_back deleted file mode 100644 index a4d4ee0..0000000 --- a/expression.py_back +++ /dev/null @@ -1,191 +0,0 @@ -#!/usr/bin/env python -# encoding: utf-8 - - -class Expression(object): - """A calculus expression. Today it can andle only expression with numbers later it will be able to manipulate unknown""" - - priority = {"*" : 3, "/": 3, "+": 2, "-":2, "(": 1} - - def __init__(self, exp): - """ Initiate the expression. - - :param exp: the expression. It can be a string or a list of tokens. It can be infix or postfix expression - """ - self._tokens = {} - self._strings = {} - if type(exp) == list: - fix = self.get_fix(exp) - self._tokens[fix] = exp - elif type(exp) == str: - tokens = self.parseExp(exp) - fix = self.get_fix(tokens) - self._tokens[fix] = tokens - self._strings[fix] = exp - else: - raise ValueError("The expression needs to be a string or a list of token") - - # --------------------- - # String parsing - - # @classmethod ???? - def parseExp(self, exp): - """ Parse the expression, ie tranform a string into a list of tokens - - :param exp: The expression (a string) - :returns: list of token - - """ - return exp.split(" ") - - # --------------------- - # "fix" recognition - - def get_fix(self, tokens): - """ Give the "fix" of an expression - infix -> A + B - prefix -> + A B - postfix -> A B + - - :param exp: the expression (list of token) - :returns: the "fix" (infix, postfix, prefix) - - """ - if tokens[0] in "+-*/": - return "prefix" - elif token[0] not in "+-*/" ans token[1] not in "+-*/": - return "postfix" - else: - return "infix" - - # ---------------------- - # Expressions - tokens getters - - def tokens(self, fix = "infix"): - """Get the list of tokens with the wanted fix - - :param fix: the fix wanted (infix default) - :returns: list of tokens - - """ - # Si ce fix n'a pas encore été enregistré - if fix not in self._tokens: - # Il peut venir de la version string - if fix in self._string: - self._tokens[fix] = self.parseExp(self._string[fix]) - # Sinon il faut le calculer à partir d'une autre forme - else: - fix_transfo = "to" + fix.capitalize() - getattr(self,fix_transfo)() - - return self._tokens[fix] - -##### On en est là,il faudra faire attention à bien vérifier ce que les "to..." enregistre (string ou tokens) - - def string(self, fix = "infix"): - """Get the string with the wanted fix - - :param fix: the fix wanted (infix default) - :returns: the string representing the expression - - """ - # Si ce fix n'a pas encore été enregistré - if fix not in self._string: - # Il peut venir de la version string - if fix in self._tokens: - self._string[fix] = self.parseExp(self._string[fix]) - # Sinon il faut le calculer à partir d'une autre forme - else: - fix_transfo = "to" + fix.capitalize() - getattr(self,fix_transfo)() - - return self._string[fix] - - # ---------------------- - # "fix" tranformations - - def toPostfix(self): - """ Transorm the expression into postfix form using the infix form""" - pass - - def toInfix(self): - """ Tranform the expression into infix form using postfix form""" - pass - - # --------------------- - # Tools for placing parenthesis in infix notation - - def needPar(operande, operator, posi = "after"): - """ Says whether or not the operande needs parenthesis - - :param operande: the operande - :param operator: the operator - :param posi: "after"(default) if the operande will be after the operator, "before" othewise - :returns: bollean - """ - if isNumber(operande) and "-" in operande: - return 1 - elif not isNumber(operande): - # Si c'est une grande expression ou un chiffre négatif - stand_alone = get_main_op(operande) - # Si la priorité de l'operande est plus faible que celle de l'opérateur - minor_priority = self.priority[get_main_op(operande)] < self.priority[operator] - # Si l'opérateur est -/ pour after ou juste / pour before - special = (operator in "-/" and posi == "after") or (operator in "/" and posi == "before") - - return stand_alone and (minor_priority or special) - else: - return 0 - -# J'aime pas bien cette endroit faudrait que ce soit une méthode qui s'applique uniquement à l'expression en question (self) pas à n'importe quel string, ça serait plus propre. - def get_main_op(exp): - """ Gives the main operation of the expression - - :param exp: the expression - :returns: the main operation (+, -, * or /) or 0 if the expression is only one element - - """ - parStack = Stack() - tokenList = exp.split(" ") - - if len(tokenList) == 1: - # Si l'expression n'est qu'un élément - return 0 - - main_op = [] - - for token in tokenList: - if token == "(": - parStack.push(token) - elif token == ")": - parStack.pop() - elif token in "+-*/" and parStack.isEmpty(): - main_op.append(token) - - return min(main_op, key = lambda s: priority[s]) - - # --------------------- - # Computing the expression - - def compute(self): - """ Recursive method for computing as a student the expression - :returns: list of steps needed to reach the result - - """ - pass - - def doMath(op, op1, op2): - """Compute "op1 op op2" - - :param op: operator - :param op1: first operande - :param op2: second operande - :returns: string representing the result - - """ - return str(eval(op1 + op + op2)) - -# ----------------------------- -# Reglages pour 'vim' -# vim:set autoindent expandtab tabstop=4 shiftwidth=4: -# cursor: 16 del From 582fc2850c3230ae043b754e8d0ddb6790427a34 Mon Sep 17 00:00:00 2001 From: lafrite Date: Wed, 15 Jan 2014 23:18:55 +0100 Subject: [PATCH 14/80] import random expression generator --- random_expression.py | 75 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 random_expression.py diff --git a/random_expression.py b/random_expression.py new file mode 100644 index 0000000..ceed4ac --- /dev/null +++ b/random_expression.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python +# encoding: utf-8 + +from random import randint +import re + +class RdExpression(object): + """A generator of random expression builder""" + + def __init__(self, form, conditions = []): + """Initiate the generator + + :param form: the form of the expression (/!\ variables need to be in brackets {}) + :param conditions: condition on variables (/!\ variables need to be in brackets {}) + + """ + self._form = form + self._conditions = conditions + self._letters = self.get_letters() + self._gene_varia = {} + + def get_letters(self): + """Find letters in the form + :returns: list of letters + + """ + pattern = "\{(\w+)\}" + varia = re.findall(pattern, self._form) + print(varia) + return list(set(varia)) + + def __call__(self, val_min = -10, val_max = 10): + """RdExpression once it is initiate act like a function which create random expressions. + + :param val_min: minimum value random generation + :param val_max: maximum value random generation + :returns: an random expression + + """ + self.gene_varia(val_min, val_max) + + while not(self.val_conditions()): + self.gene_varia(val_min, val_max) + + return self._form.format(**self._gene_varia) + + def gene_varia(self, val_min = -10, val_max = 10): + """RAndomly generates variables/letters + + """ + for l in self._letters: + self._gene_varia[l] = randint(val_min, val_max) + + def val_conditions(self): + """Tells whether or not conditions are validates + :returns: boolean + + """ + return eval(" and ".join(self._conditions).format(**self._gene_varia)) + + +if __name__ == '__main__': + form = "{a}x + 2*{b}" + cond = ["{a} + {b} in [1, 2, 3, 4, 5]"] + rdExp1 = RdExpression(form, cond) + print(rdExp1()) + + + + + +# ----------------------------- +# Reglages pour 'vim' +# vim:set autoindent expandtab tabstop=4 shiftwidth=4: +# cursor: 16 del From 36bebd6c1aa5b81137a29e4c99f4c38946b0eea5 Mon Sep 17 00:00:00 2001 From: lafrite Date: Wed, 15 Jan 2014 23:28:08 +0100 Subject: [PATCH 15/80] Add things in TODO --- TODO | 3 +++ 1 file changed, 3 insertions(+) diff --git a/TODO b/TODO index e88367a..4da6cca 100644 --- a/TODO +++ b/TODO @@ -3,3 +3,6 @@ * Improve fix recognition (DONE) * More flexible expression parsing (DONE) * bug: expression can't handle -(-2) +* Overload + - * / for expression + +* Expression parents class and his children: Numerical_exp, toGenerate_exp and formal expression From 9dfb1ed574cffef36fa85e0db909107f655d57d9 Mon Sep 17 00:00:00 2001 From: lafrite Date: Fri, 17 Jan 2014 11:19:28 +0100 Subject: [PATCH 16/80] add case if there is no condition --- random_expression.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/random_expression.py b/random_expression.py index ceed4ac..aae23db 100644 --- a/random_expression.py +++ b/random_expression.py @@ -26,7 +26,6 @@ class RdExpression(object): """ pattern = "\{(\w+)\}" varia = re.findall(pattern, self._form) - print(varia) return list(set(varia)) def __call__(self, val_min = -10, val_max = 10): @@ -56,14 +55,19 @@ class RdExpression(object): :returns: boolean """ - return eval(" and ".join(self._conditions).format(**self._gene_varia)) + if self._conditions != []: + return eval(" and ".join(self._conditions).format(**self._gene_varia)) + else: + return True if __name__ == '__main__': form = "{a}x + 2*{b}" - cond = ["{a} + {b} in [1, 2, 3, 4, 5]"] + cond = ["{a} + {b} in [1, 2, 3, 4, 5]", "{a} not in [0,1]", "{b} not in [0,1]"] rdExp1 = RdExpression(form, cond) print(rdExp1()) + rdExp2 = RdExpression(form) + print(rdExp2()) From 65c05a35b53b59e6f56fb188379167853654804c Mon Sep 17 00:00:00 2001 From: lafrite Date: Fri, 17 Jan 2014 12:28:59 +0100 Subject: [PATCH 17/80] Can make calculus in expression form --- random_expression.py | 50 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 44 insertions(+), 6 deletions(-) diff --git a/random_expression.py b/random_expression.py index aae23db..988247b 100644 --- a/random_expression.py +++ b/random_expression.py @@ -18,15 +18,35 @@ class RdExpression(object): self._conditions = conditions self._letters = self.get_letters() self._gene_varia = {} + self._gene_2replaced= {} + + def get_2replaced(self): + """Get elements of self._form which will have to be replaced + :returns: set for elements which have to be replaced + + """ + pattern = "\{(.*?)\}" #select inside {} non greedy way + varia = re.findall(pattern, self._form) + varia = set(varia) + self._2replaced = varia + + return varia def get_letters(self): """Find letters in the form :returns: list of letters """ - pattern = "\{(\w+)\}" - varia = re.findall(pattern, self._form) - return list(set(varia)) + v2replaced = self.get_2replaced() + varia = set() + + pattern = "([a-zA-Z]+)" + for v in v2replaced: + lvar = set(re.findall(pattern, v)) + varia = varia | lvar + + return varia + def __call__(self, val_min = -10, val_max = 10): """RdExpression once it is initiate act like a function which create random expressions. @@ -41,7 +61,7 @@ class RdExpression(object): while not(self.val_conditions()): self.gene_varia(val_min, val_max) - return self._form.format(**self._gene_varia) + return self._form.format(**self._gene_2replaced) def gene_varia(self, val_min = -10, val_max = 10): """RAndomly generates variables/letters @@ -50,6 +70,9 @@ class RdExpression(object): for l in self._letters: self._gene_varia[l] = randint(val_min, val_max) + for e in self._2replaced: + self._gene_2replaced[e] = eval(e, globals(), self._gene_varia) + def val_conditions(self): """Tells whether or not conditions are validates :returns: boolean @@ -60,14 +83,29 @@ class RdExpression(object): else: return True +def desc_rdExp(rdExp): + print("--------------------") + print("form: ",rdExp._form) + print("Conditions: ",rdExp._conditions) + print("Letters: ", rdExp._letters) + print("2replaced: ", rdExp._2replaced) + print("Call : ", rdExp()) + print("Gene varia: ", rdExp._gene_varia) + print("Gene 2replaced: ", rdExp._gene_2replaced) + print('') + if __name__ == '__main__': form = "{a}x + 2*{b}" cond = ["{a} + {b} in [1, 2, 3, 4, 5]", "{a} not in [0,1]", "{b} not in [0,1]"] rdExp1 = RdExpression(form, cond) - print(rdExp1()) + desc_rdExp(rdExp1) rdExp2 = RdExpression(form) - print(rdExp2()) + desc_rdExp(rdExp2) + form = "{a+a/10}x + {a} + 2*{b}" + cond = ["{a} + {b} in [1, 2, 3, 4, 5]", "{a} not in [0,1]", "{b} not in [0,1]"] + rdExp3 = RdExpression(form) + desc_rdExp(rdExp3) From 8a41031fa82156f0187c1aec42683999cfd4fb3c Mon Sep 17 00:00:00 2001 From: lafrite Date: Fri, 17 Jan 2014 14:57:45 +0100 Subject: [PATCH 18/80] Render ~work with letters still some bugs --- expression.py | 20 ++++++++++++++++++-- render.py | 13 +++++++++---- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/expression.py b/expression.py index 428cbfc..861fb4b 100644 --- a/expression.py +++ b/expression.py @@ -132,6 +132,12 @@ class Expression(object): else: tokens.append(int(character)) + elif character.isalpha(): + if str(tokens[-1]).isalpha(): + tokens[-1] += character + else: + tokens.append(character) + elif character in "+-*/)": tokens.append(character) @@ -143,7 +149,6 @@ class Expression(object): elif character != " ": raise ValueError("{} is an unvalid character".format(character)) - print(tokens[1:]) return tokens[1:] # --------------------- @@ -292,7 +297,9 @@ class Expression(object): :returns: True if the expression can be a number and false otherwise """ - return type(exp) == int or type(exp) == Fraction + return type(exp) == int or \ + type(exp) == Fraction or \ + exp.isalpha() @staticmethod def isOperator(exp): @@ -360,6 +367,15 @@ if __name__ == '__main__': exp="-2*4(12 + 1)(3-12)" test(exp) + exp="-2*a(12 + 1)(3-12)" + e = Expression(exp) + print(e) + + # 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) + ## Can't handle it yet!! #exp="-(-2)" #test(exp) diff --git a/render.py b/render.py index 05def52..191ca05 100644 --- a/render.py +++ b/render.py @@ -16,7 +16,7 @@ class Render(object): PRIORITY = {"*" : 3, "/": 3, "+": 2, "-":2, "(": 1} - def __init__(self, op_infix = {}, op_postfix = {}, other = {}, join = " ", type_render = {int: str, Fraction: str}): + def __init__(self, op_infix = {}, op_postfix = {}, other = {}, join = " ", type_render = {int: str, Fraction: str, str: str}): """Initiate the render @param op_infix: the dictionnary of infix operator with how they have to be render @@ -107,7 +107,9 @@ class Render(object): :param posi: "after"(default) if the operande will be after the operator, "before" othewise :returns: bollean """ - if self.isNumber(operande) and operande < 0: + if self.isNumber(operande) \ + and type(operande) != str \ + and operande < 0: return 1 elif not self.isNumber(operande): # Si c'est une grande expression ou un chiffre négatif @@ -159,7 +161,10 @@ class Render(object): :returns: True if the expression can be a number and false otherwise """ - return type(exp) == int or type(exp) == Fraction + return type(exp) == int or \ + type(exp) == Fraction or \ + (type(exp) == str and exp.isalpha()) + #return type(exp) == int or type(exp) == Fraction def isOperator(self, exp): """Check if the expression is in self.operators @@ -209,7 +214,7 @@ def texFrac(frac): tex_infix = {"+": " + ", "-": " - ", "*": " \\times "} tex_postfix = {"/": texSlash} tex_other = {"(": "(", ")": ")"} -tex_type_render = {int: str, Fraction: texFrac} +tex_type_render = {int: str, Fraction: texFrac, str: str} tex_render = Render(tex_infix, tex_postfix, tex_other, type_render = tex_type_render) From 999fde1c513a2c50c432acc321e4c8d31f7eb7fd Mon Sep 17 00:00:00 2001 From: lafrite Date: Fri, 17 Jan 2014 18:10:38 +0100 Subject: [PATCH 19/80] Add -f for Fraction --- fraction.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fraction.py b/fraction.py index 289df7e..9d0f28c 100644 --- a/fraction.py +++ b/fraction.py @@ -126,6 +126,9 @@ class Fraction(object): steps += ans_frac.simplify() return steps + + def __neg__(self): + return [Fraction(-self._num,self._denom)] def __mul__(self, other): if type(other) == Fraction: From 039f27c4fc5b6f5060af09d7d9e6939242d327cd Mon Sep 17 00:00:00 2001 From: lafrite Date: Fri, 17 Jan 2014 20:17:49 +0100 Subject: [PATCH 20/80] formal (which should be called polynom...) are ok.No mult yet --- expression.py | 74 +++++++++++++++++++---------- formal.py | 128 ++++++++++++++++++++++++++++++++++++++++++++++++++ generic.py | 48 +++++++++++++++++++ render.py | 45 +++++++++++++----- 4 files changed, 259 insertions(+), 36 deletions(-) create mode 100644 formal.py diff --git a/expression.py b/expression.py index 861fb4b..cf40ad0 100644 --- a/expression.py +++ b/expression.py @@ -4,6 +4,8 @@ from generic import Stack, flatten_list, expand_list from fraction import Fraction from render import txt_render, post2in_fix, tex_render +from formal import FormalExp +from formal import FormalExp class Expression(object): """A calculus expression. Today it can andle only expression with numbers later it will be able to manipulate unknown""" @@ -32,7 +34,7 @@ class Expression(object): 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 + :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 @@ -44,7 +46,7 @@ class Expression(object): def simplify(self, render = lambda x:str(x)): """ Generator which return steps for computing the expression - @param render: function which render the list of token (postfix form now) to string + :param render: function which render the list of token (postfix form now) to string """ if not self.can_go_further(): @@ -123,8 +125,10 @@ class Expression(object): for character in exp: if character.isdigit(): + # for "big" numbers (like 2345) if type(tokens[-1]) == int: tokens[-1] = tokens[-1]*10 + int(character) + # Special case for "-" at the begining of an expression or before "(" elif tokens[-1] == "-" and \ str(tokens[-2]) in " (": @@ -133,16 +137,29 @@ class Expression(object): tokens.append(int(character)) elif character.isalpha(): - if str(tokens[-1]).isalpha(): - tokens[-1] += character + # If "3x", ")x" or "yx" + if self.isNumber(tokens[-1]) \ + or tokens[-1] == ")" \ + or type(tokens[-1]) == FormalExp: + tokens.append("*") + tokens.append(FormalExp(letter = character)) + + # Special case for "-" at the begining of an expression or before "(" + elif tokens[-1] == "-" \ + or str(tokens[-2]) in " (": + tokens[-1] = - FormalExp(letter = character) + else: - tokens.append(character) + tokens.append(FormalExp(letter = character)) elif character in "+-*/)": tokens.append(character) elif character in "(": - if self.isNumber(tokens[-1]) or tokens[-1] == ")": + # If "3(", ")(" or "x(" + if self.isNumber(tokens[-1]) \ + or tokens[-1] == ")" \ + or type(tokens[-1]) == FormalExp: tokens.append("*") tokens.append(character) @@ -278,13 +295,18 @@ class Expression(object): :returns: string representing the result """ - operations = {"+": "__add__", "-": "__sub__", "*": "__mul__"} if op == "/": ans = [Fraction(op1, op2)] ans += ans[0].simplify() return ans else: - return getattr(op1,operations[op])(op2) + if type(op2) != int: + operations = {"+": "__radd__", "-": "__rsub__", "*": "__rmul__"} + return getattr(op2,operations[op])(op1) + else: + operations = {"+": "__add__", "-": "__sub__", "*": "__mul__"} + return getattr(op1,operations[op])(op2) + ## --------------------- ## Recognize numbers and operators @@ -299,7 +321,7 @@ class Expression(object): """ return type(exp) == int or \ type(exp) == Fraction or \ - exp.isalpha() + type(exp) == FormalExp @staticmethod def isOperator(exp): @@ -322,17 +344,17 @@ def test(exp): print("\n") if __name__ == '__main__': - exp = "1 + 3 * 5" - test(exp) + #exp = "1 + 3 * 5" + #test(exp) #exp = "2 * 3 * 3 * 5" #test(exp) - exp = "2 * 3 + 3 * 5" - test(exp) + #exp = "2 * 3 + 3 * 5" + #test(exp) - exp = "2 * ( 3 + 4 ) + 3 * 5" - test(exp) + #exp = "2 * ( 3 + 4 ) + 3 * 5" + #test(exp) #exp = "2 * ( 3 + 4 ) + ( 3 - 4 ) * 5" #test(exp) @@ -352,25 +374,29 @@ if __name__ == '__main__': #exp = "( 2 + 5 ) * ( 3 * 4 )" #test(exp) - exp = "( 2 + 5 - 1 ) / ( 3 * 4 )" - test(exp) + #exp = "( 2 + 5 - 1 ) / ( 3 * 4 )" + #test(exp) - exp = "( 2 + 5 ) / ( 3 * 4 ) + 1 / 12" - test(exp) + #exp = "( 2 + 5 ) / ( 3 * 4 ) + 1 / 12" + #test(exp) - exp = "( 2+ 5 )/( 3 * 4 ) + 1 / 2" - test(exp) + #exp = "( 2+ 5 )/( 3 * 4 ) + 1 / 2" + #test(exp) - exp="(-2+5)/(3*4)+1/12+5*5" - test(exp) + #exp="(-2+5)/(3*4)+1/12+5*5" + #test(exp) exp="-2*4(12 + 1)(3-12)" test(exp) - exp="-2*a(12 + 1)(3-12)" + exp="-2+a+(12 + 1)(3-12) + 34a" + test(exp) e = Expression(exp) print(e) + #exp="-2*b+a(12 + 1)(3-12)" + #test(exp) + # 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) diff --git a/formal.py b/formal.py new file mode 100644 index 0000000..ca837c5 --- /dev/null +++ b/formal.py @@ -0,0 +1,128 @@ +#!/usr/bin/env python +# encoding: utf-8 + +from fraction import Fraction +from generic import add_in_dict, remove_in_dict +import re + +class FormalExp(object): + """A formal expression (similare to Symbol in Sympy""" + + def __init__(self, coef = {}, letter = ""): + """Initiat the formal expression + + :param coef: the dictionary representing the expression + :param letter: minimum expression, a letter + + """ + + if coef != {} and letter != "": + raise ValueError("A FormalExp can't be initiate with dict_exp and a letter") + elif letter != "": + self._letter = letter + self._coef = {letter: 1} + elif coef != {}: + self._coef = coef + else: + raise ValueError("FormalExp needs a letter or dictionary of coeficients") + + if len(self) != 1: + self.mainOp = "+" + + def master_coef(self): + """Return the master coefficient + /!\ may not work pretty well if there is more than one indeterminate + :returns: a_n + + """ + pattern = "\w\*\*(\d*)" + finder = re.compile(pattern) + power = {} + for (k,v) in self._coef.items(): + if k=="": + power[0] = v + else: + p = finder.findall(k) + if p == []: + power[1] = v + else: + power[int(p[0])] = v + + m_power = max(power) + return power[m_power] + + def __add__(self, other): + if type(other) in [int, Fraction]: + d = {"":other} + elif type(other) == FormalExp: + d = other._coef + else: + raise ValueError("Can't add {type} with FormalExp".format(type=type(other))) + + d = add_in_dict(self._coef, d) + d = remove_in_dict(d) + if list(d.keys()) == ['']: + return [d['']] + else: + return [FormalExp(d)] + + def __radd__(self, other): + return self + other + + def __sub__(self, other): + o_tmp = -other + return self + o_tmp + + def __neg__(self): + d = {} + for k,v in self._coef.items(): + d[k] = -v + return FormalExp(d) + + def __mul__(self, other): + pass + + def __rmul__(self, other): + pass + + def __div__(self, other): + pass + + def __pow__(self, other): + pass + + def __len__(self): + return len(list(self._coef.keys())) + + def __str__(self): + return " + ".join([str(v) + str(k) for k,v in self._coef.items()]) + +if __name__ == '__main__': + #fe1 = FormalExp({"x": 1, "":2}) + #print(fe1) + #fe2 = FormalExp({"x**12": 5, "":2}) + #print(fe2) + #fe3 = fe1 + fe2 + #for s in fe3: + # print(s) + #fe4 = fe1 + 2 + #for s in fe4: + # print(s) + + #print(fe1.master_coef()) + #print(fe2.master_coef()) + #print(fe3[0].master_coef()) + #print(fe4[0].master_coef()) + + fe = FormalExp(letter = "a") + fe_ = -2 + fe + print(fe_[0]) + + + + + +# ----------------------------- +# Reglages pour 'vim' +# vim:set autoindent expandtab tabstop=4 shiftwidth=4: +# cursor: 16 del diff --git a/generic.py b/generic.py index 8cdf749..745ebcd 100644 --- a/generic.py +++ b/generic.py @@ -99,6 +99,54 @@ def expand_list(list_list): ans = [list_list] return ans + +def add_in_dict(dict1, dict2): + """Merge dictionary keys and add the content from dict1 and dict2 + + :param dict1: first dictionary + :param dict2: second dictionary + :returns: merged and added dictionary + + >>> add_in_dict({'a':1, 'b':2}, {'c':3, 'd': 4}) == {'d': 4, 'a': 1, 'c': 3, 'b': 2} + True + >>> add_in_dict({'a':1, 'b':2}, {'a':3, 'b': 4}) == {'a': 4, 'b': 6} + True + >>> add_in_dict({'a':1, 'b':2}, {'a':3, 'c': 4}) == {'a': 4, 'b': 2, 'c': 4} + True + + """ + new_dict = {} + new_dict.update(dict1) + for (k,v) in dict2.items(): + if k in new_dict.keys(): + new_dict[k] += v + else: + new_dict[k] = v + + return new_dict + +def remove_in_dict(d, value = 0): + """ In a dictionary, remove keys which have certain value + + :param d: the dictionary + :param value: value to remove + :returns: new dictionary whithout unwanted value + + >>> remove_in_dict({'b': 1, 'a': 0}) == {'b': 1} + True + >>> remove_in_dict({'b': 1, 'a': 0}, 1) == {'a': 0} + True + """ + new_dict = {} + for (k,v) in d.items(): + if v != value: + new_dict[k] = v + return new_dict + +if __name__ == '__main__': + import doctest + doctest.testmod() + # ----------------------------- # Reglages pour 'vim' # vim:set autoindent expandtab tabstop=4 shiftwidth=4: diff --git a/render.py b/render.py index 191ca05..05bf1ce 100644 --- a/render.py +++ b/render.py @@ -3,6 +3,7 @@ from generic import Stack,flatten_list from fraction import Fraction +from formal import FormalExp @@ -16,7 +17,7 @@ class Render(object): PRIORITY = {"*" : 3, "/": 3, "+": 2, "-":2, "(": 1} - def __init__(self, op_infix = {}, op_postfix = {}, other = {}, join = " ", type_render = {int: str, Fraction: str, str: str}): + def __init__(self, op_infix = {}, op_postfix = {}, other = {}, join = " ", type_render = {int: str, Fraction: str, FormalExp: str}): """Initiate the render @param op_infix: the dictionnary of infix operator with how they have to be render @@ -70,12 +71,13 @@ class Render(object): else: operandeStack.push(token) + # Manip pour gerer les cas de listes imbriquées dans d'autres listes infix_tokens = operandeStack.pop() if type(infix_tokens) == list or type(infix_tokens) == flist: infix_tokens = flatten_list(infix_tokens) - elif self.isNumber(infix_tokens): + elif self.isNumerande(infix_tokens): infix_tokens = [infix_tokens] if self.join: @@ -84,13 +86,13 @@ class Render(object): return infix_tokens def render_from_type(self, op): - """ If the op is a number, it transforms it with type_render conditions + """ If the op is a numerande, it transforms it with type_render conditions :param op: the operator :returns: the op transformed if it's necessary """ - if self.isNumber(op): + if self.isNumerande(op): return self.type_render[type(op)](op) else: return op @@ -108,9 +110,17 @@ class Render(object): :returns: bollean """ if self.isNumber(operande) \ - and type(operande) != str \ and operande < 0: return 1 + + elif type(operande) == FormalExp: + if operator in ["*", "/"]: + if len(operande) > 1 \ + or operande.master_coef() < 0: + return 1 + else: + return 0 + elif not self.isNumber(operande): # Si c'est une grande expression ou un chiffre négatif stand_alone = self.get_main_op(operande) @@ -155,17 +165,28 @@ class Render(object): @staticmethod def isNumber( exp): - """Check if the expression can be a number which means that it is not a operator + """Check if the expression can be a number which means int or Fraction :param exp: an expression :returns: True if the expression can be a number and false otherwise """ - return type(exp) == int or \ - type(exp) == Fraction or \ - (type(exp) == str and exp.isalpha()) + return type(exp) == int \ + or type(exp) == Fraction #return type(exp) == int or type(exp) == Fraction + @staticmethod + def isNumerande(exp): + """Check if the expression can be a numerande (not an operator) + + :param exp: an expression + :returns: True if the expression can be a number and false otherwise + + """ + return type(exp) == int \ + or type(exp) == Fraction \ + or type(exp) == FormalExp + def isOperator(self, exp): """Check if the expression is in self.operators @@ -202,9 +223,9 @@ post2in_fix = Render(p2i_infix, p2i_postfix, p2i_other, join = False) # A latex render def texSlash(op1, op2): - if not Render.isNumber(op1) and op1[0] == "(" and op1[-1] == ")": + if not Render.isNumerande(op1) and op1[0] == "(" and op1[-1] == ")": op1 = op1[1:-1] - if not Render.isNumber(op2) and op2[0] == "(" and op2[-1] == ")": + if not Render.isNumerande(op2) and op2[0] == "(" and op2[-1] == ")": op2 = op2[1:-1] return ["\\frac{" , op1 , "}{" , op2 , "}"] @@ -214,7 +235,7 @@ def texFrac(frac): tex_infix = {"+": " + ", "-": " - ", "*": " \\times "} tex_postfix = {"/": texSlash} tex_other = {"(": "(", ")": ")"} -tex_type_render = {int: str, Fraction: texFrac, str: str} +tex_type_render = {int: str, Fraction: texFrac, FormalExp: str} tex_render = Render(tex_infix, tex_postfix, tex_other, type_render = tex_type_render) From 12b29be0d943e6cb270750671836d06d0997e8f8 Mon Sep 17 00:00:00 2001 From: lafrite Date: Sat, 18 Jan 2014 16:42:03 +0100 Subject: [PATCH 21/80] Dictionary convolution --- generic.py | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/generic.py b/generic.py index 745ebcd..5c78ce3 100644 --- a/generic.py +++ b/generic.py @@ -143,6 +143,42 @@ def remove_in_dict(d, value = 0): new_dict[k] = v return new_dict +def convolution_dict(D1, D2, op = lambda x,y:x*y,\ + commutative = True, op_twice = lambda x,y: x + y): + """Convolution of two dictionaries + + :param D1: First dictionary + :param D2: Second dictionary + :param op: Operation of perform in value + :param commutative: keys are commutative? + :param op_twice: operation on value if the key appear twice + + >>> convolution_dict({"a": 1, "b":3}, {"a":2, "":4}) == {"aa":2, "a": 4, "ba":6, "b":12} + True + >>> convolution_dict({"a": 1, "b":3}, {"a":2, "b":4}) == {"aa":2, "ab":10, "bb":12} + True + >>> convolution_dict({"a": 1, "b":3}, {"a":2, "b":4}, commutative = False) == {"aa":2, "ab":10, "bb":12} + False + >>> convolution_dict({"a": 1, "b":3}, {"a":2, "b":4}, commutative = False) == {"aa":2, "ab":4,"ba":6, "bb":12} + True + >>> convolution_dict({"a": 1, "b":3}, {"a":2, "b":4}, \ + op_twice = lambda x,y:[x,y]) == {"aa":2, "ab":[4,6], "bb":12} + True + + """ + new_dict = {} + + for k1 in sorted(D1.keys()): + for k2 in sorted(D2.keys()): + if k1+k2 in new_dict.keys(): + new_dict[k1+k2] = op_twice(new_dict[k1+k2], op(D1[k1],D2[k2])) + elif k2+k1 in new_dict.keys() and commutative: + new_dict[k2+k1] = op_twice(new_dict[k2+k1], op(D1[k1],D2[k2])) + else: + new_dict[k1+k2] = op(D1[k1],D2[k2]) + + return new_dict + if __name__ == '__main__': import doctest doctest.testmod() From 65ba8f3f04bbd75a061caa89b4d2389cfca15f23 Mon Sep 17 00:00:00 2001 From: lafrite Date: Sat, 18 Jan 2014 17:08:04 +0100 Subject: [PATCH 22/80] formal multiplication done, need to simplify keys --- formal.py | 89 ++++++++++++++++++++++++++++++++++++++---------------- generic.py | 16 +++++++--- 2 files changed, 74 insertions(+), 31 deletions(-) diff --git a/formal.py b/formal.py index ca837c5..861a8bb 100644 --- a/formal.py +++ b/formal.py @@ -2,7 +2,7 @@ # encoding: utf-8 from fraction import Fraction -from generic import add_in_dict, remove_in_dict +from generic import add_in_dict, remove_in_dict, convolution_dict import re class FormalExp(object): @@ -51,20 +51,39 @@ class FormalExp(object): m_power = max(power) return power[m_power] - def __add__(self, other): + def check_calculous(self, other): + """Check if other is a constant and then transform it into a dictionary compatible with FormalExp + + :param other: The thing to compute with the expression + :returns: dictionary of this thing + + """ if type(other) in [int, Fraction]: - d = {"":other} + return {"":other} elif type(other) == FormalExp: - d = other._coef + return other._coef.copy() else: raise ValueError("Can't add {type} with FormalExp".format(type=type(other))) + def const_or_formal(self, d): + """Return a constant if there is nothing else, FormalExp otherwise + + :param d: dictionary descripting the expression + :returns: a constant or a FormalExp + + """ + if list(d.keys()) == ['']: + return d[''] + else: + return FormalExp(d) + + def __add__(self, other): + d = self.check_calculous(other) + d = add_in_dict(self._coef, d) d = remove_in_dict(d) - if list(d.keys()) == ['']: - return [d['']] - else: - return [FormalExp(d)] + + return [self.const_or_formal(d)] def __radd__(self, other): return self + other @@ -80,15 +99,35 @@ class FormalExp(object): return FormalExp(d) def __mul__(self, other): - pass + d = self.check_calculous(other) + + d = convolution_dict(self._coef, d, op_key = self.op_key) + d = remove_in_dict(d) + + return [self.const_or_formal(d)] + + def op_key(self, x,y): + """Operation on keys for convolution_dict""" + if x == "" or y == "": + return x+y + else: + return x + "*" + y + def __rmul__(self, other): - pass + d = self.check_calculous(other) + + d = convolution_dict(d, self._coef, op_key = self.op_key) + d = remove_in_dict(d) + + return [self.const_or_formal(d)] def __div__(self, other): + # Will never be done :D pass def __pow__(self, other): + # Will never be done :D quoique pass def __len__(self): @@ -98,26 +137,24 @@ class FormalExp(object): return " + ".join([str(v) + str(k) for k,v in self._coef.items()]) if __name__ == '__main__': - #fe1 = FormalExp({"x": 1, "":2}) - #print(fe1) - #fe2 = FormalExp({"x**12": 5, "":2}) - #print(fe2) - #fe3 = fe1 + fe2 - #for s in fe3: - # print(s) - #fe4 = fe1 + 2 - #for s in fe4: - # print(s) - - #print(fe1.master_coef()) - #print(fe2.master_coef()) - #print(fe3[0].master_coef()) - #print(fe4[0].master_coef()) + fe1 = FormalExp({"x": 1, "":2}) + print(fe1) + fe2 = FormalExp({"x**12": 5, "":2}) + print(fe2) + fe3 = fe1 * fe2 + for s in fe3: + print(s) + fe4 = fe1 * 2 + for s in fe4: + print(s) fe = FormalExp(letter = "a") - fe_ = -2 + fe + fe_ = -2 * fe print(fe_[0]) + fe = FormalExp(letter = "a") + fe_ = fe * (-2) + print(fe_[0]) diff --git a/generic.py b/generic.py index 5c78ce3..3bb88a3 100644 --- a/generic.py +++ b/generic.py @@ -144,6 +144,7 @@ def remove_in_dict(d, value = 0): return new_dict def convolution_dict(D1, D2, op = lambda x,y:x*y,\ + op_key = lambda x,y: x + y, \ commutative = True, op_twice = lambda x,y: x + y): """Convolution of two dictionaries @@ -170,12 +171,17 @@ def convolution_dict(D1, D2, op = lambda x,y:x*y,\ for k1 in sorted(D1.keys()): for k2 in sorted(D2.keys()): - if k1+k2 in new_dict.keys(): - new_dict[k1+k2] = op_twice(new_dict[k1+k2], op(D1[k1],D2[k2])) - elif k2+k1 in new_dict.keys() and commutative: - new_dict[k2+k1] = op_twice(new_dict[k2+k1], op(D1[k1],D2[k2])) + if op_key(k1,k2) in new_dict.keys(): + key = op_key(k1,k2) + new_dict[key] = op_twice(new_dict[key], op(D1[k1],D2[k2])) + + elif op_key(k2,k1) in new_dict.keys() and commutative: + key = op_key(k1,k2) + new_dict[key] = op_twice(new_dict[key], op(D1[k1],D2[k2])) + else: - new_dict[k1+k2] = op(D1[k1],D2[k2]) + key = op_key(k1,k2) + new_dict[key] = op(D1[k1],D2[k2]) return new_dict From 047fb903e734eda4f8a450cd0fe84f7dc58ebd9c Mon Sep 17 00:00:00 2001 From: lafrite Date: Sat, 18 Jan 2014 19:07:44 +0100 Subject: [PATCH 23/80] Change ** to ^ and mod test in render --- formal.py | 10 +++++----- render.py | 5 ++--- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/formal.py b/formal.py index 861a8bb..a9f34f0 100644 --- a/formal.py +++ b/formal.py @@ -35,7 +35,7 @@ class FormalExp(object): :returns: a_n """ - pattern = "\w\*\*(\d*)" + pattern = "\w\^(\d*)" finder = re.compile(pattern) power = {} for (k,v) in self._coef.items(): @@ -135,12 +135,12 @@ class FormalExp(object): def __str__(self): return " + ".join([str(v) + str(k) for k,v in self._coef.items()]) - + if __name__ == '__main__': fe1 = FormalExp({"x": 1, "":2}) - print(fe1) - fe2 = FormalExp({"x**12": 5, "":2}) - print(fe2) + print(fe1.get_postfix()) + fe2 = FormalExp({"x^12": 5, "":2}) + print(fe2.get_postfix()) fe3 = fe1 * fe2 for s in fe3: print(s) diff --git a/render.py b/render.py index 05bf1ce..995f430 100644 --- a/render.py +++ b/render.py @@ -243,13 +243,12 @@ tex_render = Render(tex_infix, tex_postfix, tex_other, type_render = tex_type_re if __name__ == '__main__': exp = [2, 5, '+', 1, '-', 3, 4, '*', '/'] - print(txt(exp)) + print(txt_render(exp)) exp = [2, 5, '+', 1, '-', 3, 4, '*', '/', 3, '+'] - print(tex(exp)) + print(tex_render(exp)) exp = [2, 5, '+', 1, '-', 3, 4, '*', '/', 3, '+'] print(post2in_fix(exp)) - # ----------------------------- # Reglages pour 'vim' # vim:set autoindent expandtab tabstop=4 shiftwidth=4: From bb12e6755779c9ff53b3b64b1fa895d11d0615bf Mon Sep 17 00:00:00 2001 From: lafrite Date: Sat, 18 Jan 2014 19:25:10 +0100 Subject: [PATCH 24/80] Integrate exp and render into random_expression --- expression.py | 8 ++++++-- random_expression.py | 41 +++++++++++++++++++++++++++++++++-------- 2 files changed, 39 insertions(+), 10 deletions(-) diff --git a/expression.py b/expression.py index cf40ad0..3a7f301 100644 --- a/expression.py +++ b/expression.py @@ -127,11 +127,15 @@ class Expression(object): if character.isdigit(): # for "big" numbers (like 2345) if type(tokens[-1]) == int: - tokens[-1] = tokens[-1]*10 + int(character) + if tokens[-1] > 0: + tokens[-1] = tokens[-1]*10 + int(character) + else: + tokens[-1] = tokens[-1]*10 - int(character) + # Special case for "-" at the begining of an expression or before "(" elif tokens[-1] == "-" and \ - str(tokens[-2]) in " (": + str(tokens[-2]) in " (+-*/": tokens[-1] = - int(character) else: tokens.append(int(character)) diff --git a/random_expression.py b/random_expression.py index 988247b..edac8b0 100644 --- a/random_expression.py +++ b/random_expression.py @@ -2,6 +2,7 @@ # encoding: utf-8 from random import randint +from expression import Expression import re class RdExpression(object): @@ -53,7 +54,28 @@ class RdExpression(object): :param val_min: minimum value random generation :param val_max: maximum value random generation - :returns: an random expression + :returns: an random expression formated for console printing + + """ + return str(self.raw_exp(val_min, val_max)) + + def render(self, val_min = -10, val_max = 10, render = lambda x: str(x)): + """Same as __call_ but uses render from the Expression object + + :param val_min: minimum value random generation + :param val_max: maximum value random generation + :param render: function which render the list of token (postfix form) to string + :returns: an random expression formated by render + + """ + return render(self.raw_exp(val_min, val_max).postfix_tokens) + + def raw_exp(self, val_min = -10, val_max = 10): + """Same as __call_ but returns an Expression object + + :param val_min: minimum value random generation + :param val_max: maximum value random generation + :returns: an random Expression object """ self.gene_varia(val_min, val_max) @@ -61,7 +83,9 @@ class RdExpression(object): while not(self.val_conditions()): self.gene_varia(val_min, val_max) - return self._form.format(**self._gene_2replaced) + exp = self._form.format(**self._gene_2replaced) + + return Expression(exp) def gene_varia(self, val_min = -10, val_max = 10): """RAndomly generates variables/letters @@ -84,28 +108,29 @@ class RdExpression(object): return True def desc_rdExp(rdExp): + from render import tex_render print("--------------------") print("form: ",rdExp._form) print("Conditions: ",rdExp._conditions) print("Letters: ", rdExp._letters) print("2replaced: ", rdExp._2replaced) - print("Call : ", rdExp()) + print("Call : ", rdExp.render(render = tex_render)) print("Gene varia: ", rdExp._gene_varia) print("Gene 2replaced: ", rdExp._gene_2replaced) print('') if __name__ == '__main__': - form = "{a}x + 2*{b}" + form = "{a}*-14 / 2*{b} + -23" cond = ["{a} + {b} in [1, 2, 3, 4, 5]", "{a} not in [0,1]", "{b} not in [0,1]"] rdExp1 = RdExpression(form, cond) desc_rdExp(rdExp1) rdExp2 = RdExpression(form) desc_rdExp(rdExp2) - form = "{a+a/10}x + {a} + 2*{b}" - cond = ["{a} + {b} in [1, 2, 3, 4, 5]", "{a} not in [0,1]", "{b} not in [0,1]"] - rdExp3 = RdExpression(form) - desc_rdExp(rdExp3) + #form = "{a+a/10}x + {a} + 2*{b}" + #cond = ["{a} + {b} in [1, 2, 3, 4, 5]", "{a} not in [0,1]", "{b} not in [0,1]"] + #rdExp3 = RdExpression(form) + #desc_rdExp(rdExp3) From a0dbef2946d90ecbd87e1d08afad24ce1ebf0bd6 Mon Sep 17 00:00:00 2001 From: lafrite Date: Sun, 19 Jan 2014 18:32:32 +0100 Subject: [PATCH 25/80] render accept ":" --- render.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/render.py b/render.py index 995f430..a93112f 100644 --- a/render.py +++ b/render.py @@ -15,7 +15,7 @@ class Render(object): Those three dictionnaries while define how a postfix expression will be transform into a string. """ - PRIORITY = {"*" : 3, "/": 3, "+": 2, "-":2, "(": 1} + PRIORITY = {"*" : 3, "/": 3, ":": 3, "+": 2, "-":2, "(": 1} def __init__(self, op_infix = {}, op_postfix = {}, other = {}, join = " ", type_render = {int: str, Fraction: str, FormalExp: str}): """Initiate the render @@ -204,7 +204,7 @@ class flist(list): # ------------------------ # A console render -txt_infix = {"+": "+", "-": "-", "*": "*", "/" : "/"} +txt_infix = {"+": "+", "-": "-", "*": "*", "/" : "/", ":": ":"} txt_postfix = {} txt_other = {"(": "(", ")": ")"} @@ -213,7 +213,7 @@ txt_render = Render(txt_infix, txt_postfix, txt_other) # ------------------------ # A infix to postfix list convertor -p2i_infix = {"+": "+", "-": "-", "*": "*", "/" : "/"} +p2i_infix = {"+": "+", "-": "-", "*": "*", "/" : "/", ":":":"} p2i_postfix = {} p2i_other = {"(": "(", ")": ")"} @@ -232,7 +232,7 @@ def texSlash(op1, op2): def texFrac(frac): return ["\\frac{" , str(frac._num) , "}{" , str(frac._denom) , "}"] -tex_infix = {"+": " + ", "-": " - ", "*": " \\times "} +tex_infix = {"+": " + ", "-": " - ", "*": " \\times ", ":": ":"} tex_postfix = {"/": texSlash} tex_other = {"(": "(", ")": ")"} tex_type_render = {int: str, Fraction: texFrac, FormalExp: str} @@ -242,9 +242,9 @@ tex_render = Render(tex_infix, tex_postfix, tex_other, type_render = tex_type_re if __name__ == '__main__': - exp = [2, 5, '+', 1, '-', 3, 4, '*', '/'] + exp = [2, 5, '+', 1, '-', 3, 4, '*', ':'] print(txt_render(exp)) - exp = [2, 5, '+', 1, '-', 3, 4, '*', '/', 3, '+'] + exp = [2, 5, '+', 1, '-', 3, 4, '*', '/', 3, 5, '/', ':'] print(tex_render(exp)) exp = [2, 5, '+', 1, '-', 3, 4, '*', '/', 3, '+'] print(post2in_fix(exp)) From c88569b347d820bb3f0d20fc98402e4bfcfd1921 Mon Sep 17 00:00:00 2001 From: lafrite Date: Sun, 19 Jan 2014 18:38:15 +0100 Subject: [PATCH 26/80] Expression handle ":" but can't simplify with it yet --- expression.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/expression.py b/expression.py index 3a7f301..ff616d1 100644 --- a/expression.py +++ b/expression.py @@ -10,7 +10,7 @@ from formal import FormalExp class Expression(object): """A calculus expression. Today it can andle only expression with numbers later it will be able to manipulate unknown""" - PRIORITY = {"*" : 3, "/": 3, "+": 2, "-":2, "(": 1} + PRIORITY = {"*" : 3, "/": 3, ":": 3, "+": 2, "-":2, "(": 1} def __init__(self, exp): """ Initiate the expression @@ -135,7 +135,7 @@ class Expression(object): # Special case for "-" at the begining of an expression or before "(" elif tokens[-1] == "-" and \ - str(tokens[-2]) in " (+-*/": + str(tokens[-2]) in " (+-*/:": tokens[-1] = - int(character) else: tokens.append(int(character)) @@ -156,7 +156,7 @@ class Expression(object): else: tokens.append(FormalExp(letter = character)) - elif character in "+-*/)": + elif character in "+-*/):": tokens.append(character) elif character in "(": @@ -329,13 +329,13 @@ class Expression(object): @staticmethod def isOperator(exp): - """Check if the expression is an opération in "+-*/" + """Check if the expression is an opération in "+-*/:" :param exp: an expression :returns: boolean """ - return (type(exp) == str and exp in "+-*/") + return (type(exp) == str and exp in "+-*/:") def test(exp): @@ -393,10 +393,10 @@ if __name__ == '__main__': exp="-2*4(12 + 1)(3-12)" test(exp) - exp="-2+a+(12 + 1)(3-12) + 34a" - test(exp) + exp="-2+a+(12 + 1)(3-12) : 34a" + #test(exp) e = Expression(exp) - print(e) + print(e.render(render = tex_render)) #exp="-2*b+a(12 + 1)(3-12)" #test(exp) From 852c67b22af429b60f8f795eec3a7535742ac91a Mon Sep 17 00:00:00 2001 From: lafrite Date: Sun, 19 Jan 2014 18:40:00 +0100 Subject: [PATCH 27/80] add in todo --- TODO | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/TODO b/TODO index 4da6cca..375517a 100644 --- a/TODO +++ b/TODO @@ -3,6 +3,7 @@ * Improve fix recognition (DONE) * More flexible expression parsing (DONE) * bug: expression can't handle -(-2) -* Overload + - * / for expression +* Overload + - * for expression (DONE ~ no steps yet) +* Expression should be able to simplify expression with ":" * Expression parents class and his children: Numerical_exp, toGenerate_exp and formal expression From 5e6cec5f58c679a6bb715a9080805a50c3c425ea Mon Sep 17 00:00:00 2001 From: lafrite Date: Sun, 19 Jan 2014 18:44:16 +0100 Subject: [PATCH 28/80] change priority of "/" to have the priority over "*" and ":" --- expression.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/expression.py b/expression.py index ff616d1..c268135 100644 --- a/expression.py +++ b/expression.py @@ -10,7 +10,7 @@ from formal import FormalExp class Expression(object): """A calculus expression. Today it can andle only expression with numbers later it will be able to manipulate unknown""" - PRIORITY = {"*" : 3, "/": 3, ":": 3, "+": 2, "-":2, "(": 1} + PRIORITY = {"*" : 3, "/": 4, ":": 3, "+": 2, "-":2, "(": 1} def __init__(self, exp): """ Initiate the expression From 8c424a94c9bfc4f8f09d67fb29646ebc76b31d90 Mon Sep 17 00:00:00 2001 From: lafrite Date: Sun, 19 Jan 2014 18:44:54 +0100 Subject: [PATCH 29/80] test ":" in random_expression --- random_expression.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/random_expression.py b/random_expression.py index edac8b0..2a81215 100644 --- a/random_expression.py +++ b/random_expression.py @@ -121,7 +121,7 @@ def desc_rdExp(rdExp): if __name__ == '__main__': - form = "{a}*-14 / 2*{b} + -23" + form = "{a}*-14 / (2*{b}) : -23 / 4" cond = ["{a} + {b} in [1, 2, 3, 4, 5]", "{a} not in [0,1]", "{b} not in [0,1]"] rdExp1 = RdExpression(form, cond) desc_rdExp(rdExp1) From 0655179928ad6ded92ef53c45ae99e611eb01d4b Mon Sep 17 00:00:00 2001 From: lafrite Date: Sun, 19 Jan 2014 21:39:35 +0100 Subject: [PATCH 30/80] modif for be able to use it for first big use --- expression.py | 2 +- random_expression.py | 21 +++++---------- render.py | 49 ---------------------------------- renders.py | 62 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 69 insertions(+), 65 deletions(-) create mode 100644 renders.py diff --git a/expression.py b/expression.py index c268135..563e1cd 100644 --- a/expression.py +++ b/expression.py @@ -3,7 +3,7 @@ from generic import Stack, flatten_list, expand_list from fraction import Fraction -from render import txt_render, post2in_fix, tex_render +from renders import txt_render, post2in_fix, tex_render from formal import FormalExp from formal import FormalExp diff --git a/random_expression.py b/random_expression.py index 2a81215..7007226 100644 --- a/random_expression.py +++ b/random_expression.py @@ -3,6 +3,7 @@ from random import randint from expression import Expression +from renders import tex_render, txt_render import re class RdExpression(object): @@ -49,23 +50,13 @@ class RdExpression(object): return varia - def __call__(self, val_min = -10, val_max = 10): + def __call__(self, val_min = -10, val_max = 10, render = tex_render): """RdExpression once it is initiate act like a function which create random expressions. :param val_min: minimum value random generation :param val_max: maximum value random generation - :returns: an random expression formated for console printing - - """ - return str(self.raw_exp(val_min, val_max)) - - def render(self, val_min = -10, val_max = 10, render = lambda x: str(x)): - """Same as __call_ but uses render from the Expression object - - :param val_min: minimum value random generation - :param val_max: maximum value random generation - :param render: function which render the list of token (postfix form) to string - :returns: an random expression formated by render + :param render: Render of the expression (returns an Expression by default) + :returns: an formated random expression """ return render(self.raw_exp(val_min, val_max).postfix_tokens) @@ -108,13 +99,13 @@ class RdExpression(object): return True def desc_rdExp(rdExp): - from render import tex_render + from renders import tex_render print("--------------------") print("form: ",rdExp._form) print("Conditions: ",rdExp._conditions) print("Letters: ", rdExp._letters) print("2replaced: ", rdExp._2replaced) - print("Call : ", rdExp.render(render = tex_render)) + print("Call : ", rdExp(render = tex_render)) print("Gene varia: ", rdExp._gene_varia) print("Gene 2replaced: ", rdExp._gene_2replaced) print('') diff --git a/render.py b/render.py index a93112f..c3c872b 100644 --- a/render.py +++ b/render.py @@ -5,8 +5,6 @@ from generic import Stack,flatten_list from fraction import Fraction from formal import FormalExp - - class Render(object): """A class which aims to create render functions from three dictionnaries: - op_infix: dict of caracters @@ -201,53 +199,6 @@ class flist(list): pass -# ------------------------ -# A console render - -txt_infix = {"+": "+", "-": "-", "*": "*", "/" : "/", ":": ":"} -txt_postfix = {} -txt_other = {"(": "(", ")": ")"} - -txt_render = Render(txt_infix, txt_postfix, txt_other) - -# ------------------------ -# A infix to postfix list convertor - -p2i_infix = {"+": "+", "-": "-", "*": "*", "/" : "/", ":":":"} -p2i_postfix = {} -p2i_other = {"(": "(", ")": ")"} - -post2in_fix = Render(p2i_infix, p2i_postfix, p2i_other, join = False) - -# ------------------------ -# A latex render - -def texSlash(op1, op2): - if not Render.isNumerande(op1) and op1[0] == "(" and op1[-1] == ")": - op1 = op1[1:-1] - if not Render.isNumerande(op2) and op2[0] == "(" and op2[-1] == ")": - op2 = op2[1:-1] - return ["\\frac{" , op1 , "}{" , op2 , "}"] - -def texFrac(frac): - return ["\\frac{" , str(frac._num) , "}{" , str(frac._denom) , "}"] - -tex_infix = {"+": " + ", "-": " - ", "*": " \\times ", ":": ":"} -tex_postfix = {"/": texSlash} -tex_other = {"(": "(", ")": ")"} -tex_type_render = {int: str, Fraction: texFrac, FormalExp: str} - -tex_render = Render(tex_infix, tex_postfix, tex_other, type_render = tex_type_render) - - - -if __name__ == '__main__': - exp = [2, 5, '+', 1, '-', 3, 4, '*', ':'] - print(txt_render(exp)) - exp = [2, 5, '+', 1, '-', 3, 4, '*', '/', 3, 5, '/', ':'] - print(tex_render(exp)) - exp = [2, 5, '+', 1, '-', 3, 4, '*', '/', 3, '+'] - print(post2in_fix(exp)) # ----------------------------- # Reglages pour 'vim' diff --git a/renders.py b/renders.py new file mode 100644 index 0000000..51db589 --- /dev/null +++ b/renders.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python +# encoding: utf-8 + +from render import Render +from fraction import Fraction +from formal import FormalExp + +# ------------------------ +# A console render + +txt_infix = {"+": "+", "-": "-", "*": "*", "/" : "/", ":": ":"} +txt_postfix = {} +txt_other = {"(": "(", ")": ")"} + +txt_render = Render(txt_infix, txt_postfix, txt_other) + +# ------------------------ +# A infix to postfix list convertor + +p2i_infix = {"+": "+", "-": "-", "*": "*", "/" : "/", ":":":"} +p2i_postfix = {} +p2i_other = {"(": "(", ")": ")"} + +post2in_fix = Render(p2i_infix, p2i_postfix, p2i_other, join = False) + +# ------------------------ +# A latex render + +def texSlash(op1, op2): + if not Render.isNumerande(op1) and op1[0] == "(" and op1[-1] == ")": + op1 = op1[1:-1] + if not Render.isNumerande(op2) and op2[0] == "(" and op2[-1] == ")": + op2 = op2[1:-1] + return ["\\frac{" , op1 , "}{" , op2 , "}"] + +def texFrac(frac): + return ["\\frac{" , str(frac._num) , "}{" , str(frac._denom) , "}"] + +tex_infix = {"+": " + ", "-": " - ", "*": " \\times ", ":": ":"} +tex_postfix = {"/": texSlash} +tex_other = {"(": "(", ")": ")"} +tex_type_render = {int: str, Fraction: texFrac, FormalExp: str} + +tex_render = Render(tex_infix, tex_postfix, tex_other, type_render = tex_type_render) + + + +if __name__ == '__main__': + exp = [2, 5, '+', 1, '-', 3, 4, '*', ':'] + print(txt_render(exp)) + exp = [2, 5, '+', 1, '-', 3, 4, '*', '/', 3, 5, '/', ':'] + print(tex_render(exp)) + exp = [2, 5, '+', 1, '-', 3, 4, '*', '/', 3, '+'] + print(post2in_fix(exp)) + + + + +# ----------------------------- +# Reglages pour 'vim' +# vim:set autoindent expandtab tabstop=4 shiftwidth=4: +# cursor: 16 del From 3b2176fb5c88d670580babe94a5c7e1742da44ae Mon Sep 17 00:00:00 2001 From: lafrite Date: Tue, 28 Jan 2014 20:24:01 +0100 Subject: [PATCH 31/80] test calculus in {} --- random_expression.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/random_expression.py b/random_expression.py index 7007226..648334f 100644 --- a/random_expression.py +++ b/random_expression.py @@ -123,6 +123,11 @@ if __name__ == '__main__': #rdExp3 = RdExpression(form) #desc_rdExp(rdExp3) + form1 = "{a**2}y + {2*a*b}x + {b**2}" + cond1 = ["{a} != 0", "{b} != 0"] + rdExp1 = RdExpression(form1, cond1) + desc_rdExp(rdExp1) + From 1e376787aa7f963138e38ed2ea92e84a46f498d2 Mon Sep 17 00:00:00 2001 From: lafrite Date: Tue, 28 Jan 2014 20:52:38 +0100 Subject: [PATCH 32/80] parsing and rendering "^" done.Computing with integer too --- expression.py | 26 +++++++++++++------------- random_expression.py | 2 +- render.py | 10 ++++++---- renders.py | 12 ++++++------ 4 files changed, 26 insertions(+), 24 deletions(-) diff --git a/expression.py b/expression.py index 563e1cd..e3ba90e 100644 --- a/expression.py +++ b/expression.py @@ -10,7 +10,7 @@ from formal import FormalExp class Expression(object): """A calculus expression. Today it can andle only expression with numbers later it will be able to manipulate unknown""" - PRIORITY = {"*" : 3, "/": 4, ":": 3, "+": 2, "-":2, "(": 1} + PRIORITY = {"^": 5, "*" : 3, "/": 4, ":": 3, "+": 2, "-":2, "(": 1} def __init__(self, exp): """ Initiate the expression @@ -156,7 +156,7 @@ class Expression(object): else: tokens.append(FormalExp(letter = character)) - elif character in "+-*/):": + elif character in "+-*/):^": tokens.append(character) elif character in "(": @@ -308,7 +308,7 @@ class Expression(object): operations = {"+": "__radd__", "-": "__rsub__", "*": "__rmul__"} return getattr(op2,operations[op])(op1) else: - operations = {"+": "__add__", "-": "__sub__", "*": "__mul__"} + operations = {"+": "__add__", "-": "__sub__", "*": "__mul__", "^": "__pow__"} return getattr(op1,operations[op])(op2) @@ -329,13 +329,13 @@ class Expression(object): @staticmethod def isOperator(exp): - """Check if the expression is an opération in "+-*/:" + """Check if the expression is an opération in "+-*/:^" :param exp: an expression :returns: boolean """ - return (type(exp) == str and exp in "+-*/:") + return (type(exp) == str and exp in "+-*/:^") def test(exp): @@ -348,7 +348,7 @@ def test(exp): print("\n") if __name__ == '__main__': - #exp = "1 + 3 * 5" + #exp = "2 ^ 3 * 5" #test(exp) #exp = "2 * 3 * 3 * 5" @@ -372,7 +372,7 @@ if __name__ == '__main__': #exp = "2 + 5 * ( 3 - 4 )" #test(exp) - #exp = "( 2 + 5 ) * ( 3 - 4 )" + #exp = "( 2 + 5 ) * ( 3 - 4 )^4" #test(exp) #exp = "( 2 + 5 ) * ( 3 * 4 )" @@ -390,13 +390,13 @@ if __name__ == '__main__': #exp="(-2+5)/(3*4)+1/12+5*5" #test(exp) - exp="-2*4(12 + 1)(3-12)" - test(exp) - - exp="-2+a+(12 + 1)(3-12) : 34a" + #exp="-2*4(12 + 1)(3-12)" #test(exp) - e = Expression(exp) - print(e.render(render = tex_render)) + + #exp="-2+a+(12 + 1)(3-12) : 34a" + ##test(exp) + #e = Expression(exp) + #print(e.render(render = tex_render)) #exp="-2*b+a(12 + 1)(3-12)" #test(exp) diff --git a/random_expression.py b/random_expression.py index 648334f..77d8f49 100644 --- a/random_expression.py +++ b/random_expression.py @@ -123,7 +123,7 @@ if __name__ == '__main__': #rdExp3 = RdExpression(form) #desc_rdExp(rdExp3) - form1 = "{a**2}y + {2*a*b}x + {b**2}" + form1 = "{a**2}x^2 + {2*a*b}x + {b**2}" cond1 = ["{a} != 0", "{b} != 0"] rdExp1 = RdExpression(form1, cond1) desc_rdExp(rdExp1) diff --git a/render.py b/render.py index c3c872b..1b2dd6f 100644 --- a/render.py +++ b/render.py @@ -13,7 +13,7 @@ class Render(object): Those three dictionnaries while define how a postfix expression will be transform into a string. """ - PRIORITY = {"*" : 3, "/": 3, ":": 3, "+": 2, "-":2, "(": 1} + PRIORITY = {"^": 4,"*" : 3, "/": 3, ":": 3, "+": 2, "-":2, "(": 1} def __init__(self, op_infix = {}, op_postfix = {}, other = {}, join = " ", type_render = {int: str, Fraction: str, FormalExp: str}): """Initiate the render @@ -107,12 +107,14 @@ class Render(object): :param posi: "after"(default) if the operande will be after the operator, "before" othewise :returns: bollean """ + # Si l'operande est negatif if self.isNumber(operande) \ and operande < 0: return 1 + # Si c'est un expression formelle elif type(operande) == FormalExp: - if operator in ["*", "/"]: + if operator in ["*", "/", "^"]: if len(operande) > 1 \ or operande.master_coef() < 0: return 1 @@ -124,8 +126,8 @@ class Render(object): stand_alone = self.get_main_op(operande) # Si la priorité de l'operande est plus faible que celle de l'opérateur minor_priority = self.PRIORITY[self.get_main_op(operande)] < self.PRIORITY[operator] - # Si l'opérateur est -/ pour after ou juste / pour before - special = (operator in "-/" and posi == "after") or (operator in "/" and posi == "before") + # Si l'opérateur est - ou / pour after ou / ou ^ pour before + special = (operator in "-/" and posi == "after") or (operator in "/^" and posi == "before") return stand_alone and (minor_priority or special) else: diff --git a/renders.py b/renders.py index 51db589..1cc162b 100644 --- a/renders.py +++ b/renders.py @@ -8,7 +8,7 @@ from formal import FormalExp # ------------------------ # A console render -txt_infix = {"+": "+", "-": "-", "*": "*", "/" : "/", ":": ":"} +txt_infix = {"+": "+", "-": "-", "*": "*", "/" : "/", ":": ":", "^":"^"} txt_postfix = {} txt_other = {"(": "(", ")": ")"} @@ -17,7 +17,7 @@ txt_render = Render(txt_infix, txt_postfix, txt_other) # ------------------------ # A infix to postfix list convertor -p2i_infix = {"+": "+", "-": "-", "*": "*", "/" : "/", ":":":"} +p2i_infix = {"+": "+", "-": "-", "*": "*", "/" : "/", ":":":", "^":"^"} p2i_postfix = {} p2i_other = {"(": "(", ")": ")"} @@ -36,7 +36,7 @@ def texSlash(op1, op2): def texFrac(frac): return ["\\frac{" , str(frac._num) , "}{" , str(frac._denom) , "}"] -tex_infix = {"+": " + ", "-": " - ", "*": " \\times ", ":": ":"} +tex_infix = {"+": " + ", "-": " - ", "*": " \\times ", ":": ":", "^":"^"} tex_postfix = {"/": texSlash} tex_other = {"(": "(", ")": ")"} tex_type_render = {int: str, Fraction: texFrac, FormalExp: str} @@ -46,11 +46,11 @@ tex_render = Render(tex_infix, tex_postfix, tex_other, type_render = tex_type_re if __name__ == '__main__': - exp = [2, 5, '+', 1, '-', 3, 4, '*', ':'] + exp = [2, 5, '^', 1, '-', 3, 4, '*', ':'] print(txt_render(exp)) - exp = [2, 5, '+', 1, '-', 3, 4, '*', '/', 3, 5, '/', ':'] + exp = [2, 5, '^', 1, '-', 3, 4, '*', '/', 3, 5, '/', ':'] print(tex_render(exp)) - exp = [2, 5, '+', 1, '-', 3, 4, '*', '/', 3, '+'] + exp = [2, 5, '^', 1, '-', 3, 4, '*', '/', 3, '+'] print(post2in_fix(exp)) From 33220c2a3cd09a22e6eae413c3f3e3707556598a Mon Sep 17 00:00:00 2001 From: lafrite Date: Tue, 28 Jan 2014 21:04:27 +0100 Subject: [PATCH 33/80] improve printing ofr FormalExp --- formal.py | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/formal.py b/formal.py index a9f34f0..4e4a98c 100644 --- a/formal.py +++ b/formal.py @@ -134,13 +134,27 @@ class FormalExp(object): return len(list(self._coef.keys())) def __str__(self): - return " + ".join([str(v) + str(k) for k,v in self._coef.items()]) + ans = "" + for k,v in self._coef.items(): + if v < 0: + ans += "-" + else: + ans += "+" + + if abs(v) == 1: + ans += str(k) + else: + ans += str(abs(v)) + str(k) + if ans[0] == "+": + return ans[1:] + else: + return ans if __name__ == '__main__': - fe1 = FormalExp({"x": 1, "":2}) - print(fe1.get_postfix()) + fe1 = FormalExp({"x": -1, "":-2}) + print(fe1) fe2 = FormalExp({"x^12": 5, "":2}) - print(fe2.get_postfix()) + print(fe2) fe3 = fe1 * fe2 for s in fe3: print(s) From 4834186bec9fbebbe1fa31a27fbf0e35d326e581 Mon Sep 17 00:00:00 2001 From: Lafrite Date: Fri, 21 Feb 2014 06:01:34 +0100 Subject: [PATCH 34/80] Packaging files --- pymath/__init__.py | 0 arithmetic.py => pymath/arithmetic.py | 0 expression.py => pymath/expression.py | 1 - fraction.py => pymath/fraction.py | 2 +- generic.py => pymath/generic.py | 0 random_expression.py => pymath/random_expression.py | 0 render.py => pymath/render.py | 0 setup.py | 11 +++++++++++ 8 files changed, 12 insertions(+), 2 deletions(-) create mode 100644 pymath/__init__.py rename arithmetic.py => pymath/arithmetic.py (100%) rename expression.py => pymath/expression.py (99%) rename fraction.py => pymath/fraction.py (99%) rename generic.py => pymath/generic.py (100%) rename random_expression.py => pymath/random_expression.py (100%) rename render.py => pymath/render.py (100%) create mode 100644 setup.py diff --git a/pymath/__init__.py b/pymath/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/arithmetic.py b/pymath/arithmetic.py similarity index 100% rename from arithmetic.py rename to pymath/arithmetic.py diff --git a/expression.py b/pymath/expression.py similarity index 99% rename from expression.py rename to pymath/expression.py index e3ba90e..fb2809f 100644 --- a/expression.py +++ b/pymath/expression.py @@ -5,7 +5,6 @@ from generic import Stack, flatten_list, expand_list from fraction import Fraction from renders import txt_render, post2in_fix, tex_render from formal import FormalExp -from formal import FormalExp class Expression(object): """A calculus expression. Today it can andle only expression with numbers later it will be able to manipulate unknown""" diff --git a/fraction.py b/pymath/fraction.py similarity index 99% rename from fraction.py rename to pymath/fraction.py index 9d0f28c..728e4f4 100644 --- a/fraction.py +++ b/pymath/fraction.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # encoding: utf-8 -from arithmetic import gcd +from .arithmetic import gcd class Fraction(object): """Fractions!""" diff --git a/generic.py b/pymath/generic.py similarity index 100% rename from generic.py rename to pymath/generic.py diff --git a/random_expression.py b/pymath/random_expression.py similarity index 100% rename from random_expression.py rename to pymath/random_expression.py diff --git a/render.py b/pymath/render.py similarity index 100% rename from render.py rename to pymath/render.py diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..e4fca94 --- /dev/null +++ b/setup.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python + +from distutils.core import setup + +setup(name='pyMath', + version='0.1dev', + description='Computing like a student', + author='Benjamin Bertrand', + author_email='lafrite@poneyworld.net', + packages=['pymath'], + ) From 915f92c71047e966d4a68fa82d29eeda673b3c44 Mon Sep 17 00:00:00 2001 From: Lafrite Date: Fri, 21 Feb 2014 06:02:44 +0100 Subject: [PATCH 35/80] Add dist in gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 7a60b85..86fff81 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ __pycache__/ *.pyc +dist/ From 0c4306c0583a6e9e88ad548985f6201db6c60b32 Mon Sep 17 00:00:00 2001 From: Lafrite Date: Fri, 21 Feb 2014 06:03:10 +0100 Subject: [PATCH 36/80] New MANIFEST --- MANIFEST | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 MANIFEST diff --git a/MANIFEST b/MANIFEST new file mode 100644 index 0000000..5ce56e9 --- /dev/null +++ b/MANIFEST @@ -0,0 +1,9 @@ +# file GENERATED by distutils, do NOT edit +setup.py +pymath/__init__.py +pymath/arithmetic.py +pymath/expression.py +pymath/fraction.py +pymath/generic.py +pymath/random_expression.py +pymath/render.py From b0a60f706b102cbfba561bb4a62ca487a6d73b08 Mon Sep 17 00:00:00 2001 From: lafrite Date: Fri, 17 Jan 2014 14:57:45 +0100 Subject: [PATCH 37/80] Render ~work with letters still some bugs --- pymath/expression.py | 13 +++++++++++++ pymath/render.py | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/pymath/expression.py b/pymath/expression.py index fb2809f..e48f460 100644 --- a/pymath/expression.py +++ b/pymath/expression.py @@ -324,7 +324,11 @@ class Expression(object): """ return type(exp) == int or \ type(exp) == Fraction or \ +<<<<<<< HEAD type(exp) == FormalExp +======= + exp.isalpha() +>>>>>>> Render ~work with letters still some bugs @staticmethod def isOperator(exp): @@ -405,6 +409,15 @@ if __name__ == '__main__': #e = Expression(exp) #print(e) + exp="-2*a(12 + 1)(3-12)" + e = Expression(exp) + print(e) + + # 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) + ## Can't handle it yet!! #exp="-(-2)" #test(exp) diff --git a/pymath/render.py b/pymath/render.py index 1b2dd6f..326224b 100644 --- a/pymath/render.py +++ b/pymath/render.py @@ -15,7 +15,7 @@ class Render(object): PRIORITY = {"^": 4,"*" : 3, "/": 3, ":": 3, "+": 2, "-":2, "(": 1} - def __init__(self, op_infix = {}, op_postfix = {}, other = {}, join = " ", type_render = {int: str, Fraction: str, FormalExp: str}): + def __init__(self, op_infix = {}, op_postfix = {}, other = {}, join = " ", type_render = {int: str, Fraction: str, str: str}): """Initiate the render @param op_infix: the dictionnary of infix operator with how they have to be render From 2ffce6786799042ae8536e94b339795123b3727a Mon Sep 17 00:00:00 2001 From: lafrite Date: Fri, 17 Jan 2014 20:17:49 +0100 Subject: [PATCH 38/80] formal (which should be called polynom...) are ok.No mult yet --- pymath/expression.py | 24 +++++--- pymath/formal.py | 128 +++++++++++++++++++++++++++++++++++++++++++ pymath/render.py | 9 +-- 3 files changed, 148 insertions(+), 13 deletions(-) create mode 100644 pymath/formal.py diff --git a/pymath/expression.py b/pymath/expression.py index e48f460..8b08f42 100644 --- a/pymath/expression.py +++ b/pymath/expression.py @@ -1,10 +1,10 @@ #!/usr/bin/env python # encoding: utf-8 -from generic import Stack, flatten_list, expand_list -from fraction import Fraction -from renders import txt_render, post2in_fix, tex_render -from formal import FormalExp +from .generic import Stack, flatten_list, expand_list +from .fraction import Fraction +from .render import txt_render, post2in_fix, tex_render +from .formal import FormalExp class Expression(object): """A calculus expression. Today it can andle only expression with numbers later it will be able to manipulate unknown""" @@ -324,11 +324,7 @@ class Expression(object): """ return type(exp) == int or \ type(exp) == Fraction or \ -<<<<<<< HEAD type(exp) == FormalExp -======= - exp.isalpha() ->>>>>>> Render ~work with letters still some bugs @staticmethod def isOperator(exp): @@ -354,6 +350,9 @@ if __name__ == '__main__': #exp = "2 ^ 3 * 5" #test(exp) + #exp = "1 + 3 * 5" + #test(exp) + #exp = "2 * 3 * 3 * 5" #test(exp) @@ -404,15 +403,22 @@ if __name__ == '__main__': #exp="-2*b+a(12 + 1)(3-12)" #test(exp) + #exp="(-2+5)/(3*4)+1/12+5*5" + #test(exp) + # 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) - exp="-2*a(12 + 1)(3-12)" + exp="-2+a+(12 + 1)(3-12) + 34a" + test(exp) e = Expression(exp) print(e) + #exp="-2*b+a(12 + 1)(3-12)" + #test(exp) + # 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) diff --git a/pymath/formal.py b/pymath/formal.py new file mode 100644 index 0000000..5901fb7 --- /dev/null +++ b/pymath/formal.py @@ -0,0 +1,128 @@ +#!/usr/bin/env python +# encoding: utf-8 + +from .fraction import Fraction +from .generic import add_in_dict, remove_in_dict +import re + +class FormalExp(object): + """A formal expression (similare to Symbol in Sympy""" + + def __init__(self, coef = {}, letter = ""): + """Initiat the formal expression + + :param coef: the dictionary representing the expression + :param letter: minimum expression, a letter + + """ + + if coef != {} and letter != "": + raise ValueError("A FormalExp can't be initiate with dict_exp and a letter") + elif letter != "": + self._letter = letter + self._coef = {letter: 1} + elif coef != {}: + self._coef = coef + else: + raise ValueError("FormalExp needs a letter or dictionary of coeficients") + + if len(self) != 1: + self.mainOp = "+" + + def master_coef(self): + """Return the master coefficient + /!\ may not work pretty well if there is more than one indeterminate + :returns: a_n + + """ + pattern = "\w\*\*(\d*)" + finder = re.compile(pattern) + power = {} + for (k,v) in self._coef.items(): + if k=="": + power[0] = v + else: + p = finder.findall(k) + if p == []: + power[1] = v + else: + power[int(p[0])] = v + + m_power = max(power) + return power[m_power] + + def __add__(self, other): + if type(other) in [int, Fraction]: + d = {"":other} + elif type(other) == FormalExp: + d = other._coef + else: + raise ValueError("Can't add {type} with FormalExp".format(type=type(other))) + + d = add_in_dict(self._coef, d) + d = remove_in_dict(d) + if list(d.keys()) == ['']: + return [d['']] + else: + return [FormalExp(d)] + + def __radd__(self, other): + return self + other + + def __sub__(self, other): + o_tmp = -other + return self + o_tmp + + def __neg__(self): + d = {} + for k,v in self._coef.items(): + d[k] = -v + return FormalExp(d) + + def __mul__(self, other): + pass + + def __rmul__(self, other): + pass + + def __div__(self, other): + pass + + def __pow__(self, other): + pass + + def __len__(self): + return len(list(self._coef.keys())) + + def __str__(self): + return " + ".join([str(v) + str(k) for k,v in self._coef.items()]) + +if __name__ == '__main__': + #fe1 = FormalExp({"x": 1, "":2}) + #print(fe1) + #fe2 = FormalExp({"x**12": 5, "":2}) + #print(fe2) + #fe3 = fe1 + fe2 + #for s in fe3: + # print(s) + #fe4 = fe1 + 2 + #for s in fe4: + # print(s) + + #print(fe1.master_coef()) + #print(fe2.master_coef()) + #print(fe3[0].master_coef()) + #print(fe4[0].master_coef()) + + fe = FormalExp(letter = "a") + fe_ = -2 + fe + print(fe_[0]) + + + + + +# ----------------------------- +# Reglages pour 'vim' +# vim:set autoindent expandtab tabstop=4 shiftwidth=4: +# cursor: 16 del diff --git a/pymath/render.py b/pymath/render.py index 326224b..9ba3d35 100644 --- a/pymath/render.py +++ b/pymath/render.py @@ -1,10 +1,11 @@ #!/usr/bin/env python # encoding: utf-8 -from generic import Stack,flatten_list -from fraction import Fraction -from formal import FormalExp +from .generic import Stack,flatten_list +from .fraction import Fraction +from .formal import FormalExp + class Render(object): """A class which aims to create render functions from three dictionnaries: - op_infix: dict of caracters @@ -15,7 +16,7 @@ class Render(object): PRIORITY = {"^": 4,"*" : 3, "/": 3, ":": 3, "+": 2, "-":2, "(": 1} - def __init__(self, op_infix = {}, op_postfix = {}, other = {}, join = " ", type_render = {int: str, Fraction: str, str: str}): + def __init__(self, op_infix = {}, op_postfix = {}, other = {}, join = " ", type_render = {int: str, Fraction: str, FormalExp: str}): """Initiate the render @param op_infix: the dictionnary of infix operator with how they have to be render From c5300991d52b79cafcac6b52e4c0842374483005 Mon Sep 17 00:00:00 2001 From: lafrite Date: Sat, 18 Jan 2014 16:42:03 +0100 Subject: [PATCH 39/80] Dictionary convolution --- pymath/generic.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/pymath/generic.py b/pymath/generic.py index 3bb88a3..7803f1b 100644 --- a/pymath/generic.py +++ b/pymath/generic.py @@ -144,7 +144,6 @@ def remove_in_dict(d, value = 0): return new_dict def convolution_dict(D1, D2, op = lambda x,y:x*y,\ - op_key = lambda x,y: x + y, \ commutative = True, op_twice = lambda x,y: x + y): """Convolution of two dictionaries @@ -171,17 +170,14 @@ def convolution_dict(D1, D2, op = lambda x,y:x*y,\ for k1 in sorted(D1.keys()): for k2 in sorted(D2.keys()): - if op_key(k1,k2) in new_dict.keys(): - key = op_key(k1,k2) - new_dict[key] = op_twice(new_dict[key], op(D1[k1],D2[k2])) + if k1+k2 in new_dict.keys(): + new_dict[k1+k2] = op_twice(new_dict[k1+k2], op(D1[k1],D2[k2])) - elif op_key(k2,k1) in new_dict.keys() and commutative: - key = op_key(k1,k2) - new_dict[key] = op_twice(new_dict[key], op(D1[k1],D2[k2])) + elif k2+k1 in new_dict.keys() and commutative: + new_dict[k2+k1] = op_twice(new_dict[k2+k1], op(D1[k1],D2[k2])) else: - key = op_key(k1,k2) - new_dict[key] = op(D1[k1],D2[k2]) + new_dict[k1+k2] = op(D1[k1],D2[k2]) return new_dict From 9b104f020ff8b602c2bb8decf99b86f232237f8c Mon Sep 17 00:00:00 2001 From: lafrite Date: Sat, 18 Jan 2014 17:08:04 +0100 Subject: [PATCH 40/80] formal multiplication done, need to simplify keys --- pymath/formal.py | 89 +++++++++++++++++++++++++++++++++-------------- pymath/generic.py | 14 +++++--- 2 files changed, 72 insertions(+), 31 deletions(-) diff --git a/pymath/formal.py b/pymath/formal.py index 5901fb7..4792275 100644 --- a/pymath/formal.py +++ b/pymath/formal.py @@ -2,7 +2,7 @@ # encoding: utf-8 from .fraction import Fraction -from .generic import add_in_dict, remove_in_dict +from .generic import add_in_dict, remove_in_dict, convolution_dict import re class FormalExp(object): @@ -51,20 +51,39 @@ class FormalExp(object): m_power = max(power) return power[m_power] - def __add__(self, other): + def check_calculous(self, other): + """Check if other is a constant and then transform it into a dictionary compatible with FormalExp + + :param other: The thing to compute with the expression + :returns: dictionary of this thing + + """ if type(other) in [int, Fraction]: - d = {"":other} + return {"":other} elif type(other) == FormalExp: - d = other._coef + return other._coef.copy() else: raise ValueError("Can't add {type} with FormalExp".format(type=type(other))) + def const_or_formal(self, d): + """Return a constant if there is nothing else, FormalExp otherwise + + :param d: dictionary descripting the expression + :returns: a constant or a FormalExp + + """ + if list(d.keys()) == ['']: + return d[''] + else: + return FormalExp(d) + + def __add__(self, other): + d = self.check_calculous(other) + d = add_in_dict(self._coef, d) d = remove_in_dict(d) - if list(d.keys()) == ['']: - return [d['']] - else: - return [FormalExp(d)] + + return [self.const_or_formal(d)] def __radd__(self, other): return self + other @@ -80,15 +99,35 @@ class FormalExp(object): return FormalExp(d) def __mul__(self, other): - pass + d = self.check_calculous(other) + + d = convolution_dict(self._coef, d, op_key = self.op_key) + d = remove_in_dict(d) + + return [self.const_or_formal(d)] + + def op_key(self, x,y): + """Operation on keys for convolution_dict""" + if x == "" or y == "": + return x+y + else: + return x + "*" + y + def __rmul__(self, other): - pass + d = self.check_calculous(other) + + d = convolution_dict(d, self._coef, op_key = self.op_key) + d = remove_in_dict(d) + + return [self.const_or_formal(d)] def __div__(self, other): + # Will never be done :D pass def __pow__(self, other): + # Will never be done :D quoique pass def __len__(self): @@ -98,26 +137,24 @@ class FormalExp(object): return " + ".join([str(v) + str(k) for k,v in self._coef.items()]) if __name__ == '__main__': - #fe1 = FormalExp({"x": 1, "":2}) - #print(fe1) - #fe2 = FormalExp({"x**12": 5, "":2}) - #print(fe2) - #fe3 = fe1 + fe2 - #for s in fe3: - # print(s) - #fe4 = fe1 + 2 - #for s in fe4: - # print(s) - - #print(fe1.master_coef()) - #print(fe2.master_coef()) - #print(fe3[0].master_coef()) - #print(fe4[0].master_coef()) + fe1 = FormalExp({"x": 1, "":2}) + print(fe1) + fe2 = FormalExp({"x**12": 5, "":2}) + print(fe2) + fe3 = fe1 * fe2 + for s in fe3: + print(s) + fe4 = fe1 * 2 + for s in fe4: + print(s) fe = FormalExp(letter = "a") - fe_ = -2 + fe + fe_ = -2 * fe print(fe_[0]) + fe = FormalExp(letter = "a") + fe_ = fe * (-2) + print(fe_[0]) diff --git a/pymath/generic.py b/pymath/generic.py index 7803f1b..0388e28 100644 --- a/pymath/generic.py +++ b/pymath/generic.py @@ -144,6 +144,7 @@ def remove_in_dict(d, value = 0): return new_dict def convolution_dict(D1, D2, op = lambda x,y:x*y,\ + op_key = lambda x,y: x + y, \ commutative = True, op_twice = lambda x,y: x + y): """Convolution of two dictionaries @@ -170,14 +171,17 @@ def convolution_dict(D1, D2, op = lambda x,y:x*y,\ for k1 in sorted(D1.keys()): for k2 in sorted(D2.keys()): - if k1+k2 in new_dict.keys(): - new_dict[k1+k2] = op_twice(new_dict[k1+k2], op(D1[k1],D2[k2])) + if op_key(k1,k2) in new_dict.keys(): + key = op_key(k1,k2) + new_dict[key] = op_twice(new_dict[key], op(D1[k1],D2[k2])) - elif k2+k1 in new_dict.keys() and commutative: - new_dict[k2+k1] = op_twice(new_dict[k2+k1], op(D1[k1],D2[k2])) + elif op_key(k2,k1) in new_dict.keys() and commutative: + key = op_key(k2,k1) + new_dict[key] = op_twice(new_dict[key], op(D1[k1],D2[k2])) else: - new_dict[k1+k2] = op(D1[k1],D2[k2]) + key = op_key(k1,k2) + new_dict[key] = op(D1[k1],D2[k2]) return new_dict From d74e18b663b6fa8049bafa4f4b5415171e4c3ecc Mon Sep 17 00:00:00 2001 From: lafrite Date: Sat, 18 Jan 2014 19:07:44 +0100 Subject: [PATCH 41/80] Change ** to ^ and mod test in render --- pymath/formal.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pymath/formal.py b/pymath/formal.py index 4792275..7fe01bb 100644 --- a/pymath/formal.py +++ b/pymath/formal.py @@ -35,7 +35,7 @@ class FormalExp(object): :returns: a_n """ - pattern = "\w\*\*(\d*)" + pattern = "\w\^(\d*)" finder = re.compile(pattern) power = {} for (k,v) in self._coef.items(): @@ -135,12 +135,12 @@ class FormalExp(object): def __str__(self): return " + ".join([str(v) + str(k) for k,v in self._coef.items()]) - + if __name__ == '__main__': fe1 = FormalExp({"x": 1, "":2}) - print(fe1) - fe2 = FormalExp({"x**12": 5, "":2}) - print(fe2) + print(fe1.get_postfix()) + fe2 = FormalExp({"x^12": 5, "":2}) + print(fe2.get_postfix()) fe3 = fe1 * fe2 for s in fe3: print(s) From 52ee73c340a220db746c4b351dcb65bf2b9fdba8 Mon Sep 17 00:00:00 2001 From: lafrite Date: Sat, 18 Jan 2014 19:25:10 +0100 Subject: [PATCH 42/80] Integrate exp and render into random_expression --- pymath/random_expression.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pymath/random_expression.py b/pymath/random_expression.py index 77d8f49..9ddd731 100644 --- a/pymath/random_expression.py +++ b/pymath/random_expression.py @@ -2,8 +2,8 @@ # encoding: utf-8 from random import randint -from expression import Expression -from renders import tex_render, txt_render +from .expression import Expression +from .renders import tex_render, txt_render import re class RdExpression(object): @@ -99,7 +99,6 @@ class RdExpression(object): return True def desc_rdExp(rdExp): - from renders import tex_render print("--------------------") print("form: ",rdExp._form) print("Conditions: ",rdExp._conditions) From 5799d9a48d8ef4fa61ae62a05e02b59e1373f993 Mon Sep 17 00:00:00 2001 From: lafrite Date: Sun, 19 Jan 2014 18:32:32 +0100 Subject: [PATCH 43/80] render accept ":" --- pymath/render.py | 51 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/pymath/render.py b/pymath/render.py index 9ba3d35..58a7272 100644 --- a/pymath/render.py +++ b/pymath/render.py @@ -14,7 +14,11 @@ class Render(object): Those three dictionnaries while define how a postfix expression will be transform into a string. """ +<<<<<<< HEAD PRIORITY = {"^": 4,"*" : 3, "/": 3, ":": 3, "+": 2, "-":2, "(": 1} +======= + PRIORITY = {"*" : 3, "/": 3, ":": 3, "+": 2, "-":2, "(": 1} +>>>>>>> render accept ":" def __init__(self, op_infix = {}, op_postfix = {}, other = {}, join = " ", type_render = {int: str, Fraction: str, FormalExp: str}): """Initiate the render @@ -202,6 +206,53 @@ class flist(list): pass +# ------------------------ +# A console render + +txt_infix = {"+": "+", "-": "-", "*": "*", "/" : "/", ":": ":"} +txt_postfix = {} +txt_other = {"(": "(", ")": ")"} + +txt_render = Render(txt_infix, txt_postfix, txt_other) + +# ------------------------ +# A infix to postfix list convertor + +p2i_infix = {"+": "+", "-": "-", "*": "*", "/" : "/", ":":":"} +p2i_postfix = {} +p2i_other = {"(": "(", ")": ")"} + +post2in_fix = Render(p2i_infix, p2i_postfix, p2i_other, join = False) + +# ------------------------ +# A latex render + +def texSlash(op1, op2): + if not Render.isNumerande(op1) and op1[0] == "(" and op1[-1] == ")": + op1 = op1[1:-1] + if not Render.isNumerande(op2) and op2[0] == "(" and op2[-1] == ")": + op2 = op2[1:-1] + return ["\\frac{" , op1 , "}{" , op2 , "}"] + +def texFrac(frac): + return ["\\frac{" , str(frac._num) , "}{" , str(frac._denom) , "}"] + +tex_infix = {"+": " + ", "-": " - ", "*": " \\times ", ":": ":"} +tex_postfix = {"/": texSlash} +tex_other = {"(": "(", ")": ")"} +tex_type_render = {int: str, Fraction: texFrac, FormalExp: str} + +tex_render = Render(tex_infix, tex_postfix, tex_other, type_render = tex_type_render) + + + +if __name__ == '__main__': + exp = [2, 5, '+', 1, '-', 3, 4, '*', ':'] + print(txt_render(exp)) + exp = [2, 5, '+', 1, '-', 3, 4, '*', '/', 3, 5, '/', ':'] + print(tex_render(exp)) + exp = [2, 5, '+', 1, '-', 3, 4, '*', '/', 3, '+'] + print(post2in_fix(exp)) # ----------------------------- # Reglages pour 'vim' From 9ec684477f18799d91b500e9a6f14ee87caeae3a Mon Sep 17 00:00:00 2001 From: lafrite Date: Sun, 19 Jan 2014 18:38:15 +0100 Subject: [PATCH 44/80] Expression handle ":" but can't simplify with it yet --- pymath/expression.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pymath/expression.py b/pymath/expression.py index 8b08f42..e997ff6 100644 --- a/pymath/expression.py +++ b/pymath/expression.py @@ -411,10 +411,10 @@ if __name__ == '__main__': #e = Expression(exp) #print(e) - exp="-2+a+(12 + 1)(3-12) + 34a" - test(exp) + exp="-2+a+(12 + 1)(3-12) : 34a" + #test(exp) e = Expression(exp) - print(e) + print(e.render(render = tex_render)) #exp="-2*b+a(12 + 1)(3-12)" #test(exp) From 5a8da2f4700b062768614a7b96d25817a0555e56 Mon Sep 17 00:00:00 2001 From: lafrite Date: Sun, 19 Jan 2014 21:39:35 +0100 Subject: [PATCH 45/80] modif for be able to use it for first big use --- pymath/expression.py | 2 +- pymath/render.py | 52 -------------------------------------------- 2 files changed, 1 insertion(+), 53 deletions(-) diff --git a/pymath/expression.py b/pymath/expression.py index e997ff6..df1e302 100644 --- a/pymath/expression.py +++ b/pymath/expression.py @@ -3,7 +3,7 @@ from .generic import Stack, flatten_list, expand_list from .fraction import Fraction -from .render import txt_render, post2in_fix, tex_render +from .renders import txt_render, post2in_fix, tex_render from .formal import FormalExp class Expression(object): diff --git a/pymath/render.py b/pymath/render.py index 58a7272..f63f894 100644 --- a/pymath/render.py +++ b/pymath/render.py @@ -5,7 +5,6 @@ from .generic import Stack,flatten_list from .fraction import Fraction from .formal import FormalExp - class Render(object): """A class which aims to create render functions from three dictionnaries: - op_infix: dict of caracters @@ -14,11 +13,7 @@ class Render(object): Those three dictionnaries while define how a postfix expression will be transform into a string. """ -<<<<<<< HEAD PRIORITY = {"^": 4,"*" : 3, "/": 3, ":": 3, "+": 2, "-":2, "(": 1} -======= - PRIORITY = {"*" : 3, "/": 3, ":": 3, "+": 2, "-":2, "(": 1} ->>>>>>> render accept ":" def __init__(self, op_infix = {}, op_postfix = {}, other = {}, join = " ", type_render = {int: str, Fraction: str, FormalExp: str}): """Initiate the render @@ -206,53 +201,6 @@ class flist(list): pass -# ------------------------ -# A console render - -txt_infix = {"+": "+", "-": "-", "*": "*", "/" : "/", ":": ":"} -txt_postfix = {} -txt_other = {"(": "(", ")": ")"} - -txt_render = Render(txt_infix, txt_postfix, txt_other) - -# ------------------------ -# A infix to postfix list convertor - -p2i_infix = {"+": "+", "-": "-", "*": "*", "/" : "/", ":":":"} -p2i_postfix = {} -p2i_other = {"(": "(", ")": ")"} - -post2in_fix = Render(p2i_infix, p2i_postfix, p2i_other, join = False) - -# ------------------------ -# A latex render - -def texSlash(op1, op2): - if not Render.isNumerande(op1) and op1[0] == "(" and op1[-1] == ")": - op1 = op1[1:-1] - if not Render.isNumerande(op2) and op2[0] == "(" and op2[-1] == ")": - op2 = op2[1:-1] - return ["\\frac{" , op1 , "}{" , op2 , "}"] - -def texFrac(frac): - return ["\\frac{" , str(frac._num) , "}{" , str(frac._denom) , "}"] - -tex_infix = {"+": " + ", "-": " - ", "*": " \\times ", ":": ":"} -tex_postfix = {"/": texSlash} -tex_other = {"(": "(", ")": ")"} -tex_type_render = {int: str, Fraction: texFrac, FormalExp: str} - -tex_render = Render(tex_infix, tex_postfix, tex_other, type_render = tex_type_render) - - - -if __name__ == '__main__': - exp = [2, 5, '+', 1, '-', 3, 4, '*', ':'] - print(txt_render(exp)) - exp = [2, 5, '+', 1, '-', 3, 4, '*', '/', 3, 5, '/', ':'] - print(tex_render(exp)) - exp = [2, 5, '+', 1, '-', 3, 4, '*', '/', 3, '+'] - print(post2in_fix(exp)) # ----------------------------- # Reglages pour 'vim' From eece2625663da3091547e9e95fc29b14c6c8c948 Mon Sep 17 00:00:00 2001 From: lafrite Date: Tue, 28 Jan 2014 20:52:38 +0100 Subject: [PATCH 46/80] parsing and rendering "^" done.Computing with integer too --- pymath/expression.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pymath/expression.py b/pymath/expression.py index df1e302..539082d 100644 --- a/pymath/expression.py +++ b/pymath/expression.py @@ -413,8 +413,11 @@ if __name__ == '__main__': exp="-2+a+(12 + 1)(3-12) : 34a" #test(exp) - e = Expression(exp) - print(e.render(render = tex_render)) + + #exp="-2+a+(12 + 1)(3-12) : 34a" + ##test(exp) + #e = Expression(exp) + #print(e.render(render = tex_render)) #exp="-2*b+a(12 + 1)(3-12)" #test(exp) From cb198eb5f0184fca37223d402a79860e37234573 Mon Sep 17 00:00:00 2001 From: lafrite Date: Tue, 28 Jan 2014 21:04:27 +0100 Subject: [PATCH 47/80] improve printing ofr FormalExp --- pymath/formal.py | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/pymath/formal.py b/pymath/formal.py index 7fe01bb..a1e420b 100644 --- a/pymath/formal.py +++ b/pymath/formal.py @@ -134,13 +134,27 @@ class FormalExp(object): return len(list(self._coef.keys())) def __str__(self): - return " + ".join([str(v) + str(k) for k,v in self._coef.items()]) + ans = "" + for k,v in self._coef.items(): + if v < 0: + ans += "-" + else: + ans += "+" + + if abs(v) == 1: + ans += str(k) + else: + ans += str(abs(v)) + str(k) + if ans[0] == "+": + return ans[1:] + else: + return ans if __name__ == '__main__': - fe1 = FormalExp({"x": 1, "":2}) - print(fe1.get_postfix()) + fe1 = FormalExp({"x": -1, "":-2}) + print(fe1) fe2 = FormalExp({"x^12": 5, "":2}) - print(fe2.get_postfix()) + print(fe2) fe3 = fe1 * fe2 for s in fe3: print(s) From d566867ae845da6d6c95b92158587bce7d44df1b Mon Sep 17 00:00:00 2001 From: lafrite Date: Tue, 28 Jan 2014 23:13:16 +0100 Subject: [PATCH 48/80] not very good render especially with negativ numbers... but can work --- pymath/generic.py | 32 ++++++++++++++++++++++++++++++++ pymath/random_expression.py | 12 ++++++------ pymath/render.py | 9 +++++++-- renders.py | 13 ++++++++++++- 4 files changed, 57 insertions(+), 9 deletions(-) diff --git a/pymath/generic.py b/pymath/generic.py index 0388e28..dadc2c5 100644 --- a/pymath/generic.py +++ b/pymath/generic.py @@ -75,6 +75,37 @@ def flatten_list(a, result=None): return result +def first_elem(ll): + """Get the first element in imbricates lists + # TODO: Fonction pourrie mais j'ai pas le temps de faire mieux! |mar. janv. 28 22:32:22 CET 2014 + + :param list: list of lists of lists... + :returns: the first element + + >>> first_elem(1) + 1 + >>> first_elem([1,2]) + 1 + >>> first_elem([["abc"]]) + 'a' + >>> first_elem("abc") + 'a' + >>> first_elem([[[1,2],[3,4]], [5,6]]) + 1 + >>> first_elem([[["ab",2],[3,4]], [5,6]]) + 'a' + + """ + if hasattr(ll, '__contains__'): + if len(ll) == 1 and type(ll) == str: + return ll[0] + else: + return first_elem(ll[0]) + else: + return ll + + + def expand_list(list_list): """Expand list of list @@ -189,6 +220,7 @@ if __name__ == '__main__': import doctest doctest.testmod() + # ----------------------------- # Reglages pour 'vim' # vim:set autoindent expandtab tabstop=4 shiftwidth=4: diff --git a/pymath/random_expression.py b/pymath/random_expression.py index 9ddd731..d898f72 100644 --- a/pymath/random_expression.py +++ b/pymath/random_expression.py @@ -111,12 +111,12 @@ def desc_rdExp(rdExp): if __name__ == '__main__': - form = "{a}*-14 / (2*{b}) : -23 / 4" - cond = ["{a} + {b} in [1, 2, 3, 4, 5]", "{a} not in [0,1]", "{b} not in [0,1]"] - rdExp1 = RdExpression(form, cond) - desc_rdExp(rdExp1) - rdExp2 = RdExpression(form) - desc_rdExp(rdExp2) + #form = "{a}*-14 / (2*{b}) : -23 / 4" + #cond = ["{a} + {b} in [1, 2, 3, 4, 5]", "{a} not in [0,1]", "{b} not in [0,1]"] + #rdExp1 = RdExpression(form, cond) + #desc_rdExp(rdExp1) + #rdExp2 = RdExpression(form) + #desc_rdExp(rdExp2) #form = "{a+a/10}x + {a} + 2*{b}" #cond = ["{a} + {b} in [1, 2, 3, 4, 5]", "{a} not in [0,1]", "{b} not in [0,1]"] #rdExp3 = RdExpression(form) diff --git a/pymath/render.py b/pymath/render.py index f63f894..e5fc00b 100644 --- a/pymath/render.py +++ b/pymath/render.py @@ -7,7 +7,7 @@ from .formal import FormalExp class Render(object): """A class which aims to create render functions from three dictionnaries: - - op_infix: dict of caracters + - op_infix: dict of caracters or two argument functions - op_postfix: dict of 2 arguments functions - other: dict of caracters Those three dictionnaries while define how a postfix expression will be transform into a string. @@ -57,7 +57,11 @@ class Render(object): op1 = [self.other["("] , op1 , self.other[")"]] if token in self.op_infix: - res = flist([op1 , self.op_infix[token] , op2]) + if type(self.op_infix[token]) == str: + res = flist([op1 , self.op_infix[token] , op2]) + else: + res = flist([self.op_infix[token](op1, op2)]) + elif token in self.op_postfix: res = flist([self.op_postfix[token](op1, op2)]) @@ -73,6 +77,7 @@ class Render(object): # Manip pour gerer les cas de listes imbriquées dans d'autres listes infix_tokens = operandeStack.pop() + if type(infix_tokens) == list or type(infix_tokens) == flist: infix_tokens = flatten_list(infix_tokens) elif self.isNumerande(infix_tokens): diff --git a/renders.py b/renders.py index 1cc162b..be25d01 100644 --- a/renders.py +++ b/renders.py @@ -4,6 +4,7 @@ from render import Render from fraction import Fraction from formal import FormalExp +from generic import first_elem # ------------------------ # A console render @@ -36,7 +37,17 @@ def texSlash(op1, op2): def texFrac(frac): return ["\\frac{" , str(frac._num) , "}{" , str(frac._denom) , "}"] -tex_infix = {"+": " + ", "-": " - ", "*": " \\times ", ":": ":", "^":"^"} +def texMult(op1,op2): + fe = first_elem(op2) + if type(fe) == FormalExp or fe.isalpha(): + if type(op1) == list and op1[0] == "(": + return ["(", op1[1:-1], op2, ")"] + else: + return [op1, op2] + else: + return [op1, "\\times", op2] + +tex_infix = {"+": " + ", "-": " - ", "*": texMult , ":": ":", "^":"^"} tex_postfix = {"/": texSlash} tex_other = {"(": "(", ")": ")"} tex_type_render = {int: str, Fraction: texFrac, FormalExp: str} From 698fea5c63e61ae579fbe6f60a274882db509de8 Mon Sep 17 00:00:00 2001 From: lafrite Date: Tue, 28 Jan 2014 23:21:37 +0100 Subject: [PATCH 49/80] generate letters can't be equal to 0 --- pymath/random_expression.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pymath/random_expression.py b/pymath/random_expression.py index d898f72..253db8a 100644 --- a/pymath/random_expression.py +++ b/pymath/random_expression.py @@ -84,6 +84,8 @@ class RdExpression(object): """ for l in self._letters: self._gene_varia[l] = randint(val_min, val_max) + while self._gene_varia[l] == 0: + self._gene_varia[l] = randint(val_min, val_max) for e in self._2replaced: self._gene_2replaced[e] = eval(e, globals(), self._gene_varia) @@ -123,7 +125,7 @@ if __name__ == '__main__': #desc_rdExp(rdExp3) form1 = "{a**2}x^2 + {2*a*b}x + {b**2}" - cond1 = ["{a} != 0", "{b} != 0"] + cond1 = [] rdExp1 = RdExpression(form1, cond1) desc_rdExp(rdExp1) From e669e9707dfd0fa60b7944f223ab05ebb08308e3 Mon Sep 17 00:00:00 2001 From: lafrite Date: Thu, 30 Jan 2014 09:26:48 +0100 Subject: [PATCH 50/80] allow raw string generation --- pymath/random_expression.py | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/pymath/random_expression.py b/pymath/random_expression.py index 253db8a..357a23b 100644 --- a/pymath/random_expression.py +++ b/pymath/random_expression.py @@ -9,15 +9,17 @@ import re class RdExpression(object): """A generator of random expression builder""" - def __init__(self, form, conditions = []): + def __init__(self, form, conditions = [], with_Exp = True): """Initiate the generator :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 with_Exp: If True, __call__ return an expression rendered through Expression class. If False, keep form and replace inside. """ self._form = form self._conditions = conditions + self._with_Exp = with_Exp self._letters = self.get_letters() self._gene_varia = {} self._gene_2replaced= {} @@ -59,10 +61,13 @@ class RdExpression(object): :returns: an formated random expression """ - return render(self.raw_exp(val_min, val_max).postfix_tokens) + if self._with_Exp: + return render(self.raw_exp(val_min, val_max).postfix_tokens) + else: + return self.raw_str(val_min, val_max) - def raw_exp(self, val_min = -10, val_max = 10): - """Same as __call_ but returns an Expression object + def raw_str(self, val_min = -10, val_max = 10): + """Return raw string (don't use Expression for rendering or parsing) :param val_min: minimum value random generation :param val_max: maximum value random generation @@ -76,6 +81,18 @@ class RdExpression(object): exp = self._form.format(**self._gene_2replaced) + return exp + + def raw_exp(self, val_min = -10, val_max = 10): + """Same as raw_str but returns an Expression object + + :param val_min: minimum value random generation + :param val_max: maximum value random generation + :returns: an random Expression object + + """ + exp = self.raw_str(val_min, val_max) + return Expression(exp) def gene_varia(self, val_min = -10, val_max = 10): @@ -126,7 +143,7 @@ if __name__ == '__main__': form1 = "{a**2}x^2 + {2*a*b}x + {b**2}" cond1 = [] - rdExp1 = RdExpression(form1, cond1) + rdExp1 = RdExpression(form1, cond1, with_Exp = False) desc_rdExp(rdExp1) From 77da30d0b7a6d13c168d1d93ea693624fa55ea7a Mon Sep 17 00:00:00 2001 From: Lafrite Date: Fri, 21 Feb 2014 06:12:35 +0100 Subject: [PATCH 51/80] add a test --- pymath/random_expression.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pymath/random_expression.py b/pymath/random_expression.py index 357a23b..d11212c 100644 --- a/pymath/random_expression.py +++ b/pymath/random_expression.py @@ -145,6 +145,8 @@ if __name__ == '__main__': cond1 = [] rdExp1 = RdExpression(form1, cond1, with_Exp = False) desc_rdExp(rdExp1) + rdExp1 = RdExpression(form1, cond1) + desc_rdExp(rdExp1) From ab903c368e2b1828afd38a3ad2870fef774d22f3 Mon Sep 17 00:00:00 2001 From: Lafrite Date: Fri, 21 Feb 2014 06:27:33 +0100 Subject: [PATCH 52/80] renders for packaging --- pymath/expression.py | 8 ++++---- renders.py => pymath/renders.py | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) rename renders.py => pymath/renders.py (94%) diff --git a/pymath/expression.py b/pymath/expression.py index 539082d..7318827 100644 --- a/pymath/expression.py +++ b/pymath/expression.py @@ -395,10 +395,10 @@ if __name__ == '__main__': #exp="-2*4(12 + 1)(3-12)" #test(exp) - #exp="-2+a+(12 + 1)(3-12) : 34a" - ##test(exp) - #e = Expression(exp) - #print(e.render(render = tex_render)) + exp="-2+a+(12 + 1)(3-12) / 34a" + #test(exp) + e = Expression(exp) + print(e.render(render = tex_render)) #exp="-2*b+a(12 + 1)(3-12)" #test(exp) diff --git a/renders.py b/pymath/renders.py similarity index 94% rename from renders.py rename to pymath/renders.py index be25d01..f213e17 100644 --- a/renders.py +++ b/pymath/renders.py @@ -1,10 +1,10 @@ #!/usr/bin/env python # encoding: utf-8 -from render import Render -from fraction import Fraction -from formal import FormalExp -from generic import first_elem +from .render import Render +from .fraction import Fraction +from .formal import FormalExp +from .generic import first_elem # ------------------------ # A console render From 6e1ca2eff85fa45d45d61242b2ac8e67fc02179c Mon Sep 17 00:00:00 2001 From: Lafrite Date: Fri, 21 Feb 2014 09:45:45 +0100 Subject: [PATCH 53/80] Start unittest for generic --- test/__init__.py | 0 test/test_generic.py | 76 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 test/__init__.py create mode 100644 test/test_generic.py diff --git a/test/__init__.py b/test/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/test_generic.py b/test/test_generic.py new file mode 100644 index 0000000..7f9c5b2 --- /dev/null +++ b/test/test_generic.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python +# encoding: utf-8 + + +import unittest + +from pymath import generic + +class TestGeneric(unittest.TestCase): + """Testing functions from pymath.generic""" + + def test_flatten_list1(self): + l = [1, [2,3], [[4,5], 6], 7] + flat_l = generic.flatten_list(l) + + true_flat = list(range(1,8)) + + self.assertEqual(flat_l, true_flat) + + def test_flatten_list2(self): + l = list(range(10)) + flat_l = generic.flatten_list(l) + + true_flat = list(range(10)) + + self.assertEqual(flat_l, true_flat) + + def test_first_elem_simple_iter(self): + """ For simple iterable """ + l = range(10) + first = generic.first_elem(l) + + self.assertAlmostEqual(0,first) + + s = "plopplop" + first = generic.first_elem(s) + self.assertAlmostEqual("p", first) + + def test_first_elem_iter_in_iter(self): + """ Interable in iterable """ + l = [[1,2],[4, 5, [6,7,8]], 9] + first = generic.first_elem(l) + + self.assertAlmostEqual(first, 1) + + l = [[[1]]] + first = generic.first_elem(l) + + self.assertAlmostEqual(first, 1) + + l = ["abc"] + first = generic.first_elem(l) + + self.assertAlmostEqual(first, "a") + + l = ["abc",[4, 5, [6,7,8]], 9] + first = generic.first_elem(l) + + self.assertAlmostEqual(first, "a") + + l = [["abc",1],[4, 5, [6,7,8]], 9] + first = generic.first_elem(l) + + self.assertAlmostEqual(first, "a") + +if __name__ == '__main__': + unittest.main() + + + + + +# ----------------------------- +# Reglages pour 'vim' +# vim:set autoindent expandtab tabstop=4 shiftwidth=4: +# cursor: 16 del From 1abd903c9b82d82b0cfc7d7e68c06edfd2c682d5 Mon Sep 17 00:00:00 2001 From: Lafrite Date: Fri, 21 Feb 2014 11:05:25 +0100 Subject: [PATCH 54/80] Add case when numerator is 0 --- pymath/fraction.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/pymath/fraction.py b/pymath/fraction.py index 728e4f4..6b8726d 100644 --- a/pymath/fraction.py +++ b/pymath/fraction.py @@ -23,6 +23,11 @@ class Fraction(object): """ steps = [] + if self._num == 0: + steps.append(0) + + return steps + if self._denom < 0: n_frac = Fraction(-self._num, -self._denom) steps.append(n_frac) @@ -70,16 +75,12 @@ class Fraction(object): coef1 = number._denom // gcd_denom coef2 = self._denom // gcd_denom - #steps.append("( {num1} * {coef1} ) / ( {den1} * {coef1} ) + ( {num2} * {coef2} ) / ( {den2} * {coef2} )".format(num1 = self._num, den1 = self._denom, coef1 = coef1, num2 = number._num, den2 = number._denom, coef2 = coef2)) - steps.append([self._num, coef1, "*", self._denom, coef1, "*", '/', number._num, coef2, "*", number._denom, coef2, "*", "/",'+']) com_denom = self._denom * coef1 num1 = self._num * coef1 num2 = number._num * coef2 - #steps.append("( {num1} + {num2} ) / {denom}".format(num1 = num1, num2 = num2, denom = com_denom)) - steps.append([num1, num2, '+', com_denom, '/']) num = num1 + num2 @@ -109,14 +110,12 @@ class Fraction(object): coef1 = number._denom // gcd_denom coef2 = self._denom // gcd_denom - #steps.append("( {num1} * {coef1} ) / ( {den1} * {coef1} ) - ( {num2} * {coef2} ) / ( {den2} * {coef2} )".format(num1 = self._num, den1 = self._denom, coef1 = coef1, num2 = number._num, den2 = number._denom, coef2 = coef2)) steps.append([self._num, coef1, "*", self._denom, coef1, "*", '/', number._num, coef2, "*", number._denom, coef2, "*", "/",'-']) com_denom = self._denom * coef1 num1 = self._num * coef1 num2 = number._num * coef2 - #steps.append("( {num1} - {num2} ) / {denom}".format(num1 = num1, num2 = num2, denom = com_denom)) steps.append([num1, num2, '-', com_denom, '/']) num = num1 - num2 @@ -190,7 +189,7 @@ if __name__ == '__main__': for i in (f + 1): print(i) print("---------") - for i in (f + g): + for i in (f - g): print(i) #print("---------") #for i in (f - g): From 8e86220b50e38f7e9e8ba73ea30e45ead1cb7ee9 Mon Sep 17 00:00:00 2001 From: Lafrite Date: Fri, 21 Feb 2014 11:08:55 +0100 Subject: [PATCH 55/80] add float method in fraction --- pymath/fraction.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pymath/fraction.py b/pymath/fraction.py index 6b8726d..ab774f5 100644 --- a/pymath/fraction.py +++ b/pymath/fraction.py @@ -55,6 +55,9 @@ class Fraction(object): def __repr__(self): return "< Fraction " + self.__str__() + ">" + + def __float__(self): + return self._num / self._denom def __add__(self, other): if type(other) == Fraction: From 49b224bedc3012a7fb9eadc988e5659ca69d373a Mon Sep 17 00:00:00 2001 From: Lafrite Date: Fri, 21 Feb 2014 11:09:12 +0100 Subject: [PATCH 56/80] start unittest for expression and fraction --- test/test_expression.py | 53 +++++++++++++++++++++++++++++++++ test/test_fraction.py | 65 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 118 insertions(+) create mode 100644 test/test_expression.py create mode 100644 test/test_fraction.py diff --git a/test/test_expression.py b/test/test_expression.py new file mode 100644 index 0000000..34cc015 --- /dev/null +++ b/test/test_expression.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python +# encoding: utf-8 + + + +import unittest + +from pymath.expression import Expression +from pymath.fraction import Fraction +from pymath.generic import first_elem + + +class TestExpression(unittest.TestCase): + """Testing functions from pymath.expression""" + + def test_init_from_exp(self): + pass + + def test_init_from_exp(self): + pass + + def test_infix_tokens(self): + pass + + def test_postfix_tokens(self): + pass + + def test_doMath(self): + ops = [\ + {"op": ("+", 1 , 2), "res" : 3}, \ + {"op": ("-", 1 , 2), "res" : -1}, \ + {"op": ("*", 1 , 2), "res" : 2}, \ + {"op": ("/", 1 , 2), "res" : Fraction(1,2)}, \ + {"op": ("^", 1 , 2), "res" : 1}, \ + ] + for op in ops: + res = first_elem(Expression.doMath(*op["op"])) + self.assertAlmostEqual(res, op["res"]) + + def test_isNumber(self): + pass + + def test_isOperator(self): + pass + +if __name__ == '__main__': + unittest.main() + + +# ----------------------------- +# Reglages pour 'vim' +# vim:set autoindent expandtab tabstop=4 shiftwidth=4: +# cursor: 16 del diff --git a/test/test_fraction.py b/test/test_fraction.py new file mode 100644 index 0000000..08806a2 --- /dev/null +++ b/test/test_fraction.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python +# encoding: utf-8 + +import unittest + +from pymath.fraction import Fraction + +class TestFraction(unittest.TestCase): + """Testing functions from pymath.Fraction""" + + def setUp(self): + self.listFrom = [Fraction(1,3), 1] + self.listAgainst = [ Fraction(1,3), \ + Fraction(2,3), \ + Fraction(4,5), \ + Fraction(-1, 3), \ + Fraction(1,-3), \ + Fraction(0,2), \ + 1, + ] + + def test_add(self): + ans = [[Fraction(2, 3), 1, Fraction(17, 15), 0, 0, Fraction(1,3), Fraction(4,3)], \ + [Fraction(4,3), Fraction(5,3), Fraction(9,3), Fraction(2,3), Fraction(2,3), 1, 0] \ + ] + + for (i, f1) in enumerate(self.listFrom): + for (j, f2) in enumerate(self.listAgainst): + res = f1 + f2 + print(res) + self.assertAlmostEqual(res[-1], ans[i][j]) + + + def test_radd(self): + pass + + def test_sub(self): + pass + + def test_rsub(self): + pass + + def test_neg(self): + pass + + def test_mul(self): + pass + + def test_truediv(self): + pass + + def test_lt(self): + pass + + def test_le(self): + pass + + +if __name__ == '__main__': + unittest.main() + +# ----------------------------- +# Reglages pour 'vim' +# vim:set autoindent expandtab tabstop=4 shiftwidth=4: +# cursor: 16 del From 6509e0164a987b6531e03771552fa88603503179 Mon Sep 17 00:00:00 2001 From: Lafrite Date: Fri, 21 Feb 2014 18:02:34 +0100 Subject: [PATCH 57/80] Add __eq__ in fraction --- pymath/fraction.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/pymath/fraction.py b/pymath/fraction.py index ab774f5..1b91b02 100644 --- a/pymath/fraction.py +++ b/pymath/fraction.py @@ -166,13 +166,24 @@ class Fraction(object): return steps + def __eq__(self, other): + """ == """ + if type(other) == Fraction: + number = other + else: + number = Fraction(other) + + return self._num * number._denom == self._denom * number._num + def __lt__(self, other): + """ < """ if type(other) == Fraction: return (self._num / self._denom) < (other._num / other._denom) else: return (self._num / self._denom) < other def __le__(self, other): + """ <= """ if type(other) == Fraction: return (self._num / self._denom) <= (other._num / other._denom) else: From 88597369ea18931aa827e4eff6e9c2e2f50a33ff Mon Sep 17 00:00:00 2001 From: Lafrite Date: Fri, 21 Feb 2014 18:02:50 +0100 Subject: [PATCH 58/80] OK test need to make r* operations --- test/test_fraction.py | 60 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 48 insertions(+), 12 deletions(-) diff --git a/test/test_fraction.py b/test/test_fraction.py index 08806a2..499a98c 100644 --- a/test/test_fraction.py +++ b/test/test_fraction.py @@ -15,39 +15,75 @@ class TestFraction(unittest.TestCase): Fraction(4,5), \ Fraction(-1, 3), \ Fraction(1,-3), \ - Fraction(0,2), \ 1, ] def test_add(self): - ans = [[Fraction(2, 3), 1, Fraction(17, 15), 0, 0, Fraction(1,3), Fraction(4,3)], \ - [Fraction(4,3), Fraction(5,3), Fraction(9,3), Fraction(2,3), Fraction(2,3), 1, 0] \ + ans = [[Fraction(2, 3), 1, Fraction(17, 15), 0, 0, Fraction(4,3)], \ + [Fraction(4,3), Fraction(5,3), Fraction(9,3), Fraction(2,3), Fraction(2,3), 0] \ ] for (i, f1) in enumerate(self.listFrom): for (j, f2) in enumerate(self.listAgainst): res = f1 + f2 - print(res) - self.assertAlmostEqual(res[-1], ans[i][j]) + #print("-----------") + #print("f1 : ", f1) + #print("f2 : ", f2) + #print(res) - def test_radd(self): - pass + self.assertEqual(res[-1], ans[i][j]) def test_sub(self): - pass + ans = [[0, Fraction(-1,3), Fraction(-7, 15), Fraction(2,3), Fraction(2,3), Fraction(-2,3)], \ + [Fraction(2,3), Fraction(1,3), Fraction(1,5), Fraction(4,3), Fraction(4,3), 0] \ + ] - def test_rsub(self): - pass + for (i, f1) in enumerate(self.listFrom): + for (j, f2) in enumerate(self.listAgainst): + res = f1 - f2 + + #print("-----------") + #print("f1 : ", f1) + #print("f2 : ", f2) + #print(res) + + self.assertEqual(res[-1], ans[i][j]) def test_neg(self): pass def test_mul(self): - pass + ans = [[Fraction(1, 9), Fraction(2,9), Fraction(4, 15), Fraction(-1,9), Fraction(-1,9), Fraction(1,3)], \ + [ Fraction(1,3), Fraction(2,3), Fraction(4,5), Fraction(-1, 3), Fraction(1,-3), 1] \ + ] + + for (i, f1) in enumerate(self.listFrom): + for (j, f2) in enumerate(self.listAgainst): + res = f1 * f2 + + #print("-----------") + #print("f1 : ", f1) + #print("f2 : ", f2) + #print(res) + + self.assertEqual(res[-1], ans[i][j]) def test_truediv(self): - pass + ans = [[1, Fraction(1,2), Fraction(5, 12), -1, -1, Fraction(1,3)], \ + [3, Fraction(3,2), Fraction(5,4), -3, -3, 1] \ + ] + + for (i, f1) in enumerate(self.listFrom): + for (j, f2) in enumerate(self.listAgainst): + res = f1 / f2 + + #print("-----------") + #print("f1 : ", f1) + #print("f2 : ", f2) + #print(res) + + self.assertEqual(res[-1], ans[i][j]) def test_lt(self): pass From 9dfc0b9c47741e276746f877a925b29135bdf9d7 Mon Sep 17 00:00:00 2001 From: Lafrite Date: Fri, 21 Feb 2014 18:03:08 +0100 Subject: [PATCH 59/80] cleaning --- formal.py | 179 ------------------------------------------------------ 1 file changed, 179 deletions(-) delete mode 100644 formal.py diff --git a/formal.py b/formal.py deleted file mode 100644 index 4e4a98c..0000000 --- a/formal.py +++ /dev/null @@ -1,179 +0,0 @@ -#!/usr/bin/env python -# encoding: utf-8 - -from fraction import Fraction -from generic import add_in_dict, remove_in_dict, convolution_dict -import re - -class FormalExp(object): - """A formal expression (similare to Symbol in Sympy""" - - def __init__(self, coef = {}, letter = ""): - """Initiat the formal expression - - :param coef: the dictionary representing the expression - :param letter: minimum expression, a letter - - """ - - if coef != {} and letter != "": - raise ValueError("A FormalExp can't be initiate with dict_exp and a letter") - elif letter != "": - self._letter = letter - self._coef = {letter: 1} - elif coef != {}: - self._coef = coef - else: - raise ValueError("FormalExp needs a letter or dictionary of coeficients") - - if len(self) != 1: - self.mainOp = "+" - - def master_coef(self): - """Return the master coefficient - /!\ may not work pretty well if there is more than one indeterminate - :returns: a_n - - """ - pattern = "\w\^(\d*)" - finder = re.compile(pattern) - power = {} - for (k,v) in self._coef.items(): - if k=="": - power[0] = v - else: - p = finder.findall(k) - if p == []: - power[1] = v - else: - power[int(p[0])] = v - - m_power = max(power) - return power[m_power] - - def check_calculous(self, other): - """Check if other is a constant and then transform it into a dictionary compatible with FormalExp - - :param other: The thing to compute with the expression - :returns: dictionary of this thing - - """ - if type(other) in [int, Fraction]: - return {"":other} - elif type(other) == FormalExp: - return other._coef.copy() - else: - raise ValueError("Can't add {type} with FormalExp".format(type=type(other))) - - def const_or_formal(self, d): - """Return a constant if there is nothing else, FormalExp otherwise - - :param d: dictionary descripting the expression - :returns: a constant or a FormalExp - - """ - if list(d.keys()) == ['']: - return d[''] - else: - return FormalExp(d) - - def __add__(self, other): - d = self.check_calculous(other) - - d = add_in_dict(self._coef, d) - d = remove_in_dict(d) - - return [self.const_or_formal(d)] - - def __radd__(self, other): - return self + other - - def __sub__(self, other): - o_tmp = -other - return self + o_tmp - - def __neg__(self): - d = {} - for k,v in self._coef.items(): - d[k] = -v - return FormalExp(d) - - def __mul__(self, other): - d = self.check_calculous(other) - - d = convolution_dict(self._coef, d, op_key = self.op_key) - d = remove_in_dict(d) - - return [self.const_or_formal(d)] - - def op_key(self, x,y): - """Operation on keys for convolution_dict""" - if x == "" or y == "": - return x+y - else: - return x + "*" + y - - - def __rmul__(self, other): - d = self.check_calculous(other) - - d = convolution_dict(d, self._coef, op_key = self.op_key) - d = remove_in_dict(d) - - return [self.const_or_formal(d)] - - def __div__(self, other): - # Will never be done :D - pass - - def __pow__(self, other): - # Will never be done :D quoique - pass - - def __len__(self): - return len(list(self._coef.keys())) - - def __str__(self): - ans = "" - for k,v in self._coef.items(): - if v < 0: - ans += "-" - else: - ans += "+" - - if abs(v) == 1: - ans += str(k) - else: - ans += str(abs(v)) + str(k) - if ans[0] == "+": - return ans[1:] - else: - return ans - -if __name__ == '__main__': - fe1 = FormalExp({"x": -1, "":-2}) - print(fe1) - fe2 = FormalExp({"x^12": 5, "":2}) - print(fe2) - fe3 = fe1 * fe2 - for s in fe3: - print(s) - fe4 = fe1 * 2 - for s in fe4: - print(s) - - fe = FormalExp(letter = "a") - fe_ = -2 * fe - print(fe_[0]) - - fe = FormalExp(letter = "a") - fe_ = fe * (-2) - print(fe_[0]) - - - - -# ----------------------------- -# Reglages pour 'vim' -# vim:set autoindent expandtab tabstop=4 shiftwidth=4: -# cursor: 16 del From ceab817c28214ab9f3568ea7d8ea102982273b1b Mon Sep 17 00:00:00 2001 From: Lafrite Date: Sat, 22 Feb 2014 07:23:42 +0100 Subject: [PATCH 60/80] new methods for Fraction __radd__ __rsub__ __r*__ --- pymath/fraction.py | 111 ++++++++++++++++++++++++++++++++++++++++-- test/test_fraction.py | 28 +++++++++-- 2 files changed, 131 insertions(+), 8 deletions(-) diff --git a/pymath/fraction.py b/pymath/fraction.py index 1b91b02..aabb35a 100644 --- a/pymath/fraction.py +++ b/pymath/fraction.py @@ -94,6 +94,42 @@ class Fraction(object): return steps + def __radd__(self, other): + if type(other) == Fraction: + #cool + number = other + else: + number = Fraction(other) + + steps = [] + + if number._denom == self._denom: + com_denom = number._denom + num1 = number._num + num2 = self._num + + else: + gcd_denom = gcd(number._denom, self._denom) + coef1 = self._denom // gcd_denom + coef2 = number._denom // gcd_denom + + steps.append([number._num, coef1, "*", number._denom, coef1, "*", '/', self._num, coef2, "*", self._denom, coef2, "*", "/",'+']) + + com_denom = number._denom * coef1 + num1 = number._num * coef1 + num2 = self._num * coef2 + + steps.append([num1, num2, '+', com_denom, '/']) + + num = num1 + num2 + + ans_frac = Fraction(num, com_denom) + steps.append(ans_frac) + steps += ans_frac.simplify() + + return steps + + def __sub__(self, other): if type(other) == Fraction: #cool @@ -129,6 +165,41 @@ class Fraction(object): return steps + def __rsub__(self, other): + if type(other) == Fraction: + #cool + number = other + else: + number = Fraction(other) + + steps = [] + + if number._denom == self._denom: + com_denom = number._denom + num1 = number._num + num2 = self._num + + else: + gcd_denom = gcd(number._denom, self._denom) + coef1 = self._denom // gcd_denom + coef2 = number._denom // gcd_denom + + steps.append([number._num, coef1, "*", number._denom, coef1, "*", '/', self._num, coef2, "*", self._denom, coef2, "*", "/",'-']) + + com_denom = number._denom * coef1 + num1 = number._num * coef1 + num2 = self._num * coef2 + + steps.append([num1, num2, '-', com_denom, '/']) + + num = num1 - num2 + + ans_frac = Fraction(num, com_denom) + steps.append(ans_frac) + steps += ans_frac.simplify() + + return steps + def __neg__(self): return [Fraction(-self._num,self._denom)] @@ -140,7 +211,6 @@ class Fraction(object): number = Fraction(other) steps = [] - #steps.append("( {num1} * {num2} ) / ( {denom1} * {denom2} )".format(num1 = self._num, num2 = number._num, denom1 = self._denom, denom2 = number._denom)) steps.append([self._num, number._num, '*', self._denom, number._denom, '*', '/']) @@ -153,6 +223,26 @@ class Fraction(object): return steps + def __rmul__(self, other): + if type(other) == Fraction: + #cool + number = other + else: + number = Fraction(other) + + steps = [] + + steps.append([number._num, self._num, '*', number._denom, self._denom, '*', '/']) + + num = self._num * number._num + denom = self._denom * number._denom + + ans_frac = Fraction(num, denom) + steps.append(ans_frac) + steps += ans_frac.simplify() + + return steps + def __truediv__(self, other): if type(other) == Fraction: #cool @@ -162,10 +252,25 @@ class Fraction(object): steps = [] number = Fraction(number._denom, number._num) + steps.append([self, number, "/"]) steps += self * number return steps + def __rtruediv__(self, other): + if type(other) == Fraction: + #cool + number = other + else: + number = Fraction(other) + + steps = [] + self_inv = Fraction(self._denom, self._num) + steps.append([number, self_inv, "/"]) + steps += number * self_inv + + return steps + def __eq__(self, other): """ == """ if type(other) == Fraction: @@ -197,10 +302,10 @@ if __name__ == '__main__': h = Fraction(-1,5) t = Fraction(-4,5) print("---------") - for i in (f - 1): + for i in (f / t): print(i) print("---------") - for i in (f + 1): + for i in (f / h): print(i) print("---------") for i in (f - g): diff --git a/test/test_fraction.py b/test/test_fraction.py index 499a98c..05963a1 100644 --- a/test/test_fraction.py +++ b/test/test_fraction.py @@ -20,8 +20,9 @@ class TestFraction(unittest.TestCase): def test_add(self): ans = [[Fraction(2, 3), 1, Fraction(17, 15), 0, 0, Fraction(4,3)], \ - [Fraction(4,3), Fraction(5,3), Fraction(9,3), Fraction(2,3), Fraction(2,3), 0] \ + [Fraction(4,3), Fraction(5,3), Fraction(9,5), Fraction(2,3), Fraction(2,3), 2] \ ] + # TODO: Bug pour 1 + 1/-3 |sam. févr. 22 07:01:29 CET 2014 for (i, f1) in enumerate(self.listFrom): for (j, f2) in enumerate(self.listAgainst): @@ -32,12 +33,17 @@ class TestFraction(unittest.TestCase): #print("f2 : ", f2) #print(res) - self.assertEqual(res[-1], ans[i][j]) + # On est obligé de faire ça pour gérer le cas de 1+1 qui ne passe pas par la classe Fraction + if type(res) == list: + self.assertEqual(res[-1], ans[i][j]) + else: + self.assertEqual(res, ans[i][j]) def test_sub(self): ans = [[0, Fraction(-1,3), Fraction(-7, 15), Fraction(2,3), Fraction(2,3), Fraction(-2,3)], \ [Fraction(2,3), Fraction(1,3), Fraction(1,5), Fraction(4,3), Fraction(4,3), 0] \ ] + # TODO: bug pour 1 - 1/-3 |sam. févr. 22 07:05:15 CET 2014 for (i, f1) in enumerate(self.listFrom): for (j, f2) in enumerate(self.listAgainst): @@ -48,7 +54,11 @@ class TestFraction(unittest.TestCase): #print("f2 : ", f2) #print(res) - self.assertEqual(res[-1], ans[i][j]) + # On est obligé de faire ça pour gérer le cas de 1-1 qui ne passe pas par la classe Fraction + if type(res) == list: + self.assertEqual(res[-1], ans[i][j]) + else: + self.assertEqual(res, ans[i][j]) def test_neg(self): pass @@ -67,7 +77,11 @@ class TestFraction(unittest.TestCase): #print("f2 : ", f2) #print(res) - self.assertEqual(res[-1], ans[i][j]) + # On est obligé de faire ça pour gérer le cas de 1*1 qui ne passe pas par la classe Fraction + if type(res) == list: + self.assertEqual(res[-1], ans[i][j]) + else: + self.assertEqual(res, ans[i][j]) def test_truediv(self): ans = [[1, Fraction(1,2), Fraction(5, 12), -1, -1, Fraction(1,3)], \ @@ -83,7 +97,11 @@ class TestFraction(unittest.TestCase): #print("f2 : ", f2) #print(res) - self.assertEqual(res[-1], ans[i][j]) + # On est obligé de faire ça pour gérer le cas de 1/1 qui ne passe pas par la classe Fraction + if type(res) == list: + self.assertEqual(res[-1], ans[i][j]) + else: + self.assertEqual(res, ans[i][j]) def test_lt(self): pass From c3a6628ae3ccee03c5d5eaa0cb470756a2fb4ec9 Mon Sep 17 00:00:00 2001 From: Lafrite Date: Sat, 22 Feb 2014 10:12:05 +0100 Subject: [PATCH 61/80] gcd works with negatives numbers and it is now tested --- pymath/arithmetic.py | 29 ++++++++++++++++++++--------- test/test_arthmetic.py | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 9 deletions(-) create mode 100644 test/test_arthmetic.py diff --git a/pymath/arithmetic.py b/pymath/arithmetic.py index b559788..2b2cadd 100644 --- a/pymath/arithmetic.py +++ b/pymath/arithmetic.py @@ -10,19 +10,30 @@ def gcd(a, b): :returns: the gcd """ - if a > b: - c = a % b + pos_a, _a = (a >= 0), abs(a) + pos_b, _b = (b >= 0), abs(b) + + gcd_sgn = (-1 + 2*(pos_a or pos_b)) + + if _a > _b: + c = _a % _b else: - c = b % a + c = _b % _a if c == 0: - return min(a,b) - elif a == 1: - return b - elif b == 1: - return a + return gcd_sgn * min(_a,_b) + elif _a == 1: + return gcd_sgn * _b + elif _b == 1: + return gcd_sgn * _a else: - return gcd(min(a,b), c) + return gcd_sgn * gcd(min(_a,_b), c) + +if __name__ == '__main__': + print(gcd(3, 15)) + print(gcd(3, 15)) + print(gcd(-15, -3)) + print(gcd(-3, -12)) # ----------------------------- diff --git a/test/test_arthmetic.py b/test/test_arthmetic.py new file mode 100644 index 0000000..f5cb2d6 --- /dev/null +++ b/test/test_arthmetic.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python +# encoding: utf-8 + + +import unittest + +from pymath import arithmetic + + + +class TestArithmetic(unittest.TestCase): + """Testing functions from pymath.arithmetic""" + + def test_gcd_commu(self): + self.assertEqual(arithmetic.gcd(3, 15), arithmetic.gcd(15,3)) + + def test_gcd1(self): + self.assertEqual(arithmetic.gcd(3, 15), 3) + + def test_gcd2(self): + self.assertEqual(arithmetic.gcd(14, 21), 7) + + def test_gcd_prem(self): + self.assertEqual(arithmetic.gcd(14, 19), 1) + + def test_gcd_neg(self): + self.assertEqual(arithmetic.gcd(3, -15), 3) + self.assertEqual(arithmetic.gcd(-3, -15), -3) + + +if __name__ == '__main__': + unittest.main() + + + + + +# ----------------------------- +# Reglages pour 'vim' +# vim:set autoindent expandtab tabstop=4 shiftwidth=4: +# cursor: 16 del + From 2e417f3befe4b4bb6a5caadc886ef6e0053b062c Mon Sep 17 00:00:00 2001 From: Lafrite Date: Sat, 22 Feb 2014 10:13:12 +0100 Subject: [PATCH 62/80] thanks to gcd beggug now tests on fractions work --- pymath/fraction.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pymath/fraction.py b/pymath/fraction.py index aabb35a..aed0d06 100644 --- a/pymath/fraction.py +++ b/pymath/fraction.py @@ -299,17 +299,17 @@ class Fraction(object): if __name__ == '__main__': f = Fraction(1, 12) g = Fraction(1, 12) - h = Fraction(-1,5) - t = Fraction(-4,5) + h = Fraction(1,-5) + t = Fraction(4,5) print("---------") - for i in (f / t): + for i in (1 + h): print(i) print("---------") - for i in (f / h): - print(i) - print("---------") - for i in (f - g): - print(i) + #for i in (f + t): + # print(i) + #print("---------") + #for i in (f + g): + # print(i) #print("---------") #for i in (f - g): # print(i) From 3786bd5cf699008f19c8f4818b543b6bca671cf8 Mon Sep 17 00:00:00 2001 From: Lafrite Date: Sat, 22 Feb 2014 10:38:57 +0100 Subject: [PATCH 63/80] move formal to polynom and solve (I hope) consequences --- pymath/expression.py | 14 ++++---- pymath/{formal.py => polynom.py} | 55 ++++++++++++++++---------------- pymath/render.py | 8 ++--- pymath/renders.py | 6 ++-- 4 files changed, 41 insertions(+), 42 deletions(-) rename pymath/{formal.py => polynom.py} (78%) diff --git a/pymath/expression.py b/pymath/expression.py index 7318827..777d8be 100644 --- a/pymath/expression.py +++ b/pymath/expression.py @@ -4,7 +4,7 @@ from .generic import Stack, flatten_list, expand_list from .fraction import Fraction from .renders import txt_render, post2in_fix, tex_render -from .formal import FormalExp +from .polynom import Polynom class Expression(object): """A calculus expression. Today it can andle only expression with numbers later it will be able to manipulate unknown""" @@ -143,17 +143,17 @@ class Expression(object): # If "3x", ")x" or "yx" if self.isNumber(tokens[-1]) \ or tokens[-1] == ")" \ - or type(tokens[-1]) == FormalExp: + or type(tokens[-1]) == Polynom: tokens.append("*") - tokens.append(FormalExp(letter = character)) + tokens.append(Polynom(letter = character)) # Special case for "-" at the begining of an expression or before "(" elif tokens[-1] == "-" \ or str(tokens[-2]) in " (": - tokens[-1] = - FormalExp(letter = character) + tokens[-1] = - Polynom(letter = character) else: - tokens.append(FormalExp(letter = character)) + tokens.append(Polynom(letter = character)) elif character in "+-*/):^": tokens.append(character) @@ -162,7 +162,7 @@ class Expression(object): # If "3(", ")(" or "x(" if self.isNumber(tokens[-1]) \ or tokens[-1] == ")" \ - or type(tokens[-1]) == FormalExp: + or type(tokens[-1]) == Polynom: tokens.append("*") tokens.append(character) @@ -324,7 +324,7 @@ class Expression(object): """ return type(exp) == int or \ type(exp) == Fraction or \ - type(exp) == FormalExp + type(exp) == Polynom @staticmethod def isOperator(exp): diff --git a/pymath/formal.py b/pymath/polynom.py similarity index 78% rename from pymath/formal.py rename to pymath/polynom.py index a1e420b..1341243 100644 --- a/pymath/formal.py +++ b/pymath/polynom.py @@ -5,11 +5,11 @@ from .fraction import Fraction from .generic import add_in_dict, remove_in_dict, convolution_dict import re -class FormalExp(object): - """A formal expression (similare to Symbol in Sympy""" +class Polynom(object): + """A polynom (similare to Symbol in Sympy""" def __init__(self, coef = {}, letter = ""): - """Initiat the formal expression + """Initiat the polynom :param coef: the dictionary representing the expression :param letter: minimum expression, a letter @@ -17,14 +17,14 @@ class FormalExp(object): """ if coef != {} and letter != "": - raise ValueError("A FormalExp can't be initiate with dict_exp and a letter") + raise ValueError("A Polynom can't be initiate with dict_exp and a letter") elif letter != "": self._letter = letter self._coef = {letter: 1} elif coef != {}: self._coef = coef else: - raise ValueError("FormalExp needs a letter or dictionary of coeficients") + raise ValueError("Polynom needs a letter or dictionary of coeficients") if len(self) != 1: self.mainOp = "+" @@ -52,7 +52,7 @@ class FormalExp(object): return power[m_power] def check_calculous(self, other): - """Check if other is a constant and then transform it into a dictionary compatible with FormalExp + """Check if other is a constant and then transform it into a dictionary compatible with Polynom :param other: The thing to compute with the expression :returns: dictionary of this thing @@ -60,22 +60,22 @@ class FormalExp(object): """ if type(other) in [int, Fraction]: return {"":other} - elif type(other) == FormalExp: + elif type(other) == Polynom: return other._coef.copy() else: - raise ValueError("Can't add {type} with FormalExp".format(type=type(other))) + raise ValueError("Can't add {type} with Polynom".format(type=type(other))) - def const_or_formal(self, d): - """Return a constant if there is nothing else, FormalExp otherwise + def const_or_poly(self, d): + """Return a constant if there is nothing else, Polynom otherwise :param d: dictionary descripting the expression - :returns: a constant or a FormalExp + :returns: a constant or a Polynom """ if list(d.keys()) == ['']: return d[''] else: - return FormalExp(d) + return Polynom(d) def __add__(self, other): d = self.check_calculous(other) @@ -83,7 +83,7 @@ class FormalExp(object): d = add_in_dict(self._coef, d) d = remove_in_dict(d) - return [self.const_or_formal(d)] + return [self.const_or_poly(d)] def __radd__(self, other): return self + other @@ -96,7 +96,7 @@ class FormalExp(object): d = {} for k,v in self._coef.items(): d[k] = -v - return FormalExp(d) + return Polynom(d) def __mul__(self, other): d = self.check_calculous(other) @@ -104,7 +104,15 @@ class FormalExp(object): d = convolution_dict(self._coef, d, op_key = self.op_key) d = remove_in_dict(d) - return [self.const_or_formal(d)] + return [self.const_or_poly(d)] + + def __rmul__(self, other): + d = self.check_calculous(other) + + d = convolution_dict(d, self._coef, op_key = self.op_key) + d = remove_in_dict(d) + + return [self.const_or_poly(d)] def op_key(self, x,y): """Operation on keys for convolution_dict""" @@ -112,16 +120,7 @@ class FormalExp(object): return x+y else: return x + "*" + y - - def __rmul__(self, other): - d = self.check_calculous(other) - - d = convolution_dict(d, self._coef, op_key = self.op_key) - d = remove_in_dict(d) - - return [self.const_or_formal(d)] - def __div__(self, other): # Will never be done :D pass @@ -151,9 +150,9 @@ class FormalExp(object): return ans if __name__ == '__main__': - fe1 = FormalExp({"x": -1, "":-2}) + fe1 = Polynom({"x": -1, "":-2}) print(fe1) - fe2 = FormalExp({"x^12": 5, "":2}) + fe2 = Polynom({"x^12": 5, "":2}) print(fe2) fe3 = fe1 * fe2 for s in fe3: @@ -162,11 +161,11 @@ if __name__ == '__main__': for s in fe4: print(s) - fe = FormalExp(letter = "a") + fe = Polynom(letter = "a") fe_ = -2 * fe print(fe_[0]) - fe = FormalExp(letter = "a") + fe = Polynom(letter = "a") fe_ = fe * (-2) print(fe_[0]) diff --git a/pymath/render.py b/pymath/render.py index e5fc00b..4ff6c6c 100644 --- a/pymath/render.py +++ b/pymath/render.py @@ -3,7 +3,7 @@ from .generic import Stack,flatten_list from .fraction import Fraction -from .formal import FormalExp +from .polynom import Polynom class Render(object): """A class which aims to create render functions from three dictionnaries: @@ -15,7 +15,7 @@ class Render(object): PRIORITY = {"^": 4,"*" : 3, "/": 3, ":": 3, "+": 2, "-":2, "(": 1} - def __init__(self, op_infix = {}, op_postfix = {}, other = {}, join = " ", type_render = {int: str, Fraction: str, FormalExp: str}): + def __init__(self, op_infix = {}, op_postfix = {}, other = {}, join = " ", type_render = {int: str, Fraction: str, Polynom: str}): """Initiate the render @param op_infix: the dictionnary of infix operator with how they have to be render @@ -118,7 +118,7 @@ class Render(object): return 1 # Si c'est un expression formelle - elif type(operande) == FormalExp: + elif type(operande) == Polynom: if operator in ["*", "/", "^"]: if len(operande) > 1 \ or operande.master_coef() < 0: @@ -190,7 +190,7 @@ class Render(object): """ return type(exp) == int \ or type(exp) == Fraction \ - or type(exp) == FormalExp + or type(exp) == Polynom def isOperator(self, exp): """Check if the expression is in self.operators diff --git a/pymath/renders.py b/pymath/renders.py index f213e17..c729c17 100644 --- a/pymath/renders.py +++ b/pymath/renders.py @@ -3,7 +3,7 @@ from .render import Render from .fraction import Fraction -from .formal import FormalExp +from .polynom import Polynom from .generic import first_elem # ------------------------ @@ -39,7 +39,7 @@ def texFrac(frac): def texMult(op1,op2): fe = first_elem(op2) - if type(fe) == FormalExp or fe.isalpha(): + if type(fe) == Polynom or fe.isalpha(): if type(op1) == list and op1[0] == "(": return ["(", op1[1:-1], op2, ")"] else: @@ -50,7 +50,7 @@ def texMult(op1,op2): tex_infix = {"+": " + ", "-": " - ", "*": texMult , ":": ":", "^":"^"} tex_postfix = {"/": texSlash} tex_other = {"(": "(", ")": ")"} -tex_type_render = {int: str, Fraction: texFrac, FormalExp: str} +tex_type_render = {int: str, Fraction: texFrac, Polynom: str} tex_render = Render(tex_infix, tex_postfix, tex_other, type_render = tex_type_render) From d393e1e794f6c3e14be8b2339070c40d167bba79 Mon Sep 17 00:00:00 2001 From: Lafrite Date: Tue, 25 Feb 2014 15:59:22 +0100 Subject: [PATCH 64/80] debug texrenders but need unittest --- pymath/renders.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pymath/renders.py b/pymath/renders.py index c729c17..e9a35e7 100644 --- a/pymath/renders.py +++ b/pymath/renders.py @@ -28,6 +28,7 @@ post2in_fix = Render(p2i_infix, p2i_postfix, p2i_other, join = False) # A latex render def texSlash(op1, op2): + """ Tex render for / """ if not Render.isNumerande(op1) and op1[0] == "(" and op1[-1] == ")": op1 = op1[1:-1] if not Render.isNumerande(op2) and op2[0] == "(" and op2[-1] == ")": @@ -35,11 +36,13 @@ def texSlash(op1, op2): return ["\\frac{" , op1 , "}{" , op2 , "}"] def texFrac(frac): + """ Tex render for Fractions""" return ["\\frac{" , str(frac._num) , "}{" , str(frac._denom) , "}"] def texMult(op1,op2): + """ Tex render for * """ fe = first_elem(op2) - if type(fe) == Polynom or fe.isalpha(): + if type(fe) != int and (type(fe) == Polynom or fe.isalpha()): if type(op1) == list and op1[0] == "(": return ["(", op1[1:-1], op2, ")"] else: From ccd388c87c313e2c842883664dc6fd94353767b7 Mon Sep 17 00:00:00 2001 From: Lafrite Date: Tue, 25 Feb 2014 16:39:03 +0100 Subject: [PATCH 65/80] tex_render unittest need to debug now... --- test/test_renders.py | 76 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 test/test_renders.py diff --git a/test/test_renders.py b/test/test_renders.py new file mode 100644 index 0000000..6ed502c --- /dev/null +++ b/test/test_renders.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python +# encoding: utf-8 + + +import unittest + +from pymath.renders import tex_render, txt_render +from pymath.fraction import Fraction +from pymath.polynom import Polynom + + + +class TestTexRender(unittest.TestCase): + """Testing functions from pymath.renders.tex_render""" + + def test_type_render_int(self): + self.assertEqual(tex_render([2]), "2") + + def test_type_render_str(self): + self.assertEqual(tex_render(["a"]), "a") + + def test_type_render_fraction(self): + self.assertEqual(tex_render([Fraction(1,2)]), "\\frac{ 1 }{ 2 }") + + def test_type_render_polynom(self): + self.assertEqual(tex_render([Polynom({"": 1, "x": 3})]), "3x + 1") + + def test_mult_interger(self): + exps = [ [2, 3, "*"], [2, -3, "*"], [-2, 3, "*"]] + wanted_render = [ "2 \\times 3", "2 \\times ( -3 )", "-2 \\times 3"] + for (i,e) in enumerate(exps): + rend = tex_render(e) + self.assertEqual(rend, wanted_render[i]) + + def test_mult_letter(self): + exps = [ [2, "a", "*"], ["a", 3, "*"], [-2, "a", "*"], ["a", -2, "*"]] + wanted_render = [ "2 a", "3 a", "-2 a", "-2 a"] + for (i,e) in enumerate(exps): + rend = tex_render(e) + self.assertEqual(rend, wanted_render[i]) + + def test_mult_fraction(self): + exps = [ [2, Fraction(1,2), "*"], [Fraction(1,2), 3, "*"]] + wanted_render = [ "2 \\times \\frac{1}{2}", "\\frac{1}{2} \\times 3"] + for (i,e) in enumerate(exps): + rend = tex_render(e) + self.assertEqual(rend, wanted_render[i]) + + def test_mult_exp(self): + pass + + def test_slash(self): + pass + + + + +class TesttxtRender(unittest.TestCase): + """Testing functions from pymath.renders.txt_render""" + + +if __name__ == '__main__': + unittest.main() + + + + + + + + +# ----------------------------- +# Reglages pour 'vim' +# vim:set autoindent expandtab tabstop=4 shiftwidth=4: +# cursor: 16 del + From c8261eb6422015b7cf256bf0cbf291e83b0e2c5e Mon Sep 17 00:00:00 2001 From: Lafrite Date: Tue, 25 Feb 2014 16:52:18 +0100 Subject: [PATCH 66/80] add str in render --- pymath/render.py | 7 ++++--- pymath/renders.py | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/pymath/render.py b/pymath/render.py index 4ff6c6c..cc9e1f6 100644 --- a/pymath/render.py +++ b/pymath/render.py @@ -15,7 +15,7 @@ class Render(object): PRIORITY = {"^": 4,"*" : 3, "/": 3, ":": 3, "+": 2, "-":2, "(": 1} - def __init__(self, op_infix = {}, op_postfix = {}, other = {}, join = " ", type_render = {int: str, Fraction: str, Polynom: str}): + def __init__(self, op_infix = {}, op_postfix = {}, other = {}, join = " ", type_render = {str: str, int: str, Fraction: str, Polynom: str}): """Initiate the render @param op_infix: the dictionnary of infix operator with how they have to be render @@ -117,7 +117,7 @@ class Render(object): and operande < 0: return 1 - # Si c'est un expression formelle + # Si c'est un polynom elif type(operande) == Polynom: if operator in ["*", "/", "^"]: if len(operande) > 1 \ @@ -190,7 +190,8 @@ class Render(object): """ return type(exp) == int \ or type(exp) == Fraction \ - or type(exp) == Polynom + or type(exp) == Polynom \ + or exp.isalpha() def isOperator(self, exp): """Check if the expression is in self.operators diff --git a/pymath/renders.py b/pymath/renders.py index e9a35e7..5ebc60c 100644 --- a/pymath/renders.py +++ b/pymath/renders.py @@ -53,7 +53,7 @@ def texMult(op1,op2): tex_infix = {"+": " + ", "-": " - ", "*": texMult , ":": ":", "^":"^"} tex_postfix = {"/": texSlash} tex_other = {"(": "(", ")": ")"} -tex_type_render = {int: str, Fraction: texFrac, Polynom: str} +tex_type_render = {str:str, int: str, Fraction: texFrac, Polynom: str} tex_render = Render(tex_infix, tex_postfix, tex_other, type_render = tex_type_render) From bf7344c31875527c3f3c0936100c9f98ad4ca174 Mon Sep 17 00:00:00 2001 From: Lafrite Date: Tue, 25 Feb 2014 16:58:51 +0100 Subject: [PATCH 67/80] unittest for txt_render --- test/test_renders.py | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/test/test_renders.py b/test/test_renders.py index 6ed502c..f6b189d 100644 --- a/test/test_renders.py +++ b/test/test_renders.py @@ -58,6 +58,45 @@ class TestTexRender(unittest.TestCase): class TesttxtRender(unittest.TestCase): """Testing functions from pymath.renders.txt_render""" + def test_type_render_int(self): + self.assertEqual(txt_render([2]), "2") + + def test_type_render_str(self): + self.assertEqual(txt_render(["a"]), "a") + + def test_type_render_fraction(self): + self.assertEqual(txt_render([Fraction(1,2)]), "1 / 2") + + def test_type_render_polynom(self): + self.assertEqual(txt_render([Polynom({"": 1, "x": 3})]), "3x + 1") + + def test_mult_interger(self): + exps = [ [2, 3, "*"], [2, -3, "*"], [-2, 3, "*"]] + wanted_render = [ "2 * 3", "2 * ( -3 )", "-2 * 3"] + for (i,e) in enumerate(exps): + rend = txt_render(e) + self.assertEqual(rend, wanted_render[i]) + + def test_mult_letter(self): + exps = [ [2, "a", "*"], ["a", 3, "*"], [-2, "a", "*"], ["a", -2, "*"]] + wanted_render = [ "2 a", "3 a", "-2 a", "-2 a"] + for (i,e) in enumerate(exps): + rend = txt_render(e) + self.assertEqual(rend, wanted_render[i]) + + def test_mult_fraction(self): + exps = [ [2, Fraction(1,2), "*"], [Fraction(1,2), 3, "*"]] + wanted_render = [ "2 * 1 / 2", "1 / 2 * 3"] + for (i,e) in enumerate(exps): + rend = txt_render(e) + self.assertEqual(rend, wanted_render[i]) + + def test_mult_exp(self): + pass + + def test_slash(self): + pass + if __name__ == '__main__': unittest.main() From c420ef980ba978f69dbdebe517fb09e3e09fbe35 Mon Sep 17 00:00:00 2001 From: Lafrite Date: Tue, 25 Feb 2014 17:02:15 +0100 Subject: [PATCH 68/80] solve (-2) * ... parenthesis issue --- pymath/render.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pymath/render.py b/pymath/render.py index cc9e1f6..b942902 100644 --- a/pymath/render.py +++ b/pymath/render.py @@ -114,7 +114,8 @@ class Render(object): """ # Si l'operande est negatif if self.isNumber(operande) \ - and operande < 0: + and operande < 0 \ + and posi == "after": return 1 # Si c'est un polynom From 1ecc25b9bf2f17f169f23339a3d7ca797395268e Mon Sep 17 00:00:00 2001 From: Lafrite Date: Wed, 26 Feb 2014 12:39:38 +0100 Subject: [PATCH 69/80] add last_elem --- pymath/generic.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/pymath/generic.py b/pymath/generic.py index dadc2c5..f1a43ef 100644 --- a/pymath/generic.py +++ b/pymath/generic.py @@ -104,6 +104,34 @@ def first_elem(ll): else: return ll +def last_elem(ll): + """Get the last element in imbricates lists + # TODO: Fonction pourrie mais j'ai pas le temps de faire mieux! |mar. janv. 28 22:32:22 CET 2014 + + :param list: list of lists of lists... + :returns: the last element + + >>> last_elem(1) + 1 + >>> last_elem([1,2]) + 2 + >>> last_elem([["abc"]]) + 'c' + >>> last_elem("abc") + 'c' + >>> last_elem([[[1,2],[3,4]], [5,6]]) + 6 + >>> last_elem([[["ab",2],[3,4]], [5,6]]) + 6 + + """ + if hasattr(ll, '__contains__'): + if len(ll) == 1 and type(ll) == str: + return ll[-1] + else: + return last_elem(ll[-1]) + else: + return ll def expand_list(list_list): From 232368fd7535cf6ad5bf5b8ea23a3f3f728f5b74 Mon Sep 17 00:00:00 2001 From: Lafrite Date: Wed, 26 Feb 2014 12:48:41 +0100 Subject: [PATCH 70/80] bug and unittests for renders --- pymath/render.py | 10 ++++-- pymath/renders.py | 72 ++++++++++++++++++++++++++++++-------------- test/test_renders.py | 6 ++-- 3 files changed, 59 insertions(+), 29 deletions(-) diff --git a/pymath/render.py b/pymath/render.py index b942902..e0dd8b9 100644 --- a/pymath/render.py +++ b/pymath/render.py @@ -74,7 +74,6 @@ class Render(object): else: operandeStack.push(token) - # Manip pour gerer les cas de listes imbriquées dans d'autres listes infix_tokens = operandeStack.pop() @@ -117,6 +116,11 @@ class Render(object): and operande < 0 \ and posi == "after": return 1 + + # Pas de parenthèses si c'est une lettre ou une fraction + elif (type(operande) == str and operande.isalpha()) \ + or type(operande) == Fraction: + return 0 # Si c'est un polynom elif type(operande) == Polynom: @@ -128,7 +132,7 @@ class Render(object): return 0 elif not self.isNumber(operande): - # Si c'est une grande expression ou un chiffre négatif + # Si c'est une grande expression stand_alone = self.get_main_op(operande) # Si la priorité de l'operande est plus faible que celle de l'opérateur minor_priority = self.PRIORITY[self.get_main_op(operande)] < self.PRIORITY[operator] @@ -202,7 +206,7 @@ class Render(object): """ return (type(exp) == str and exp in self.operators) - + class flist(list): """Fake list- they are used to stock the main operation of an rendered expression""" pass diff --git a/pymath/renders.py b/pymath/renders.py index 5ebc60c..8bb4f5e 100644 --- a/pymath/renders.py +++ b/pymath/renders.py @@ -4,26 +4,44 @@ from .render import Render from .fraction import Fraction from .polynom import Polynom -from .generic import first_elem - -# ------------------------ -# A console render - -txt_infix = {"+": "+", "-": "-", "*": "*", "/" : "/", ":": ":", "^":"^"} -txt_postfix = {} -txt_other = {"(": "(", ")": ")"} - -txt_render = Render(txt_infix, txt_postfix, txt_other) +from .generic import first_elem, last_elem # ------------------------ # A infix to postfix list convertor -p2i_infix = {"+": "+", "-": "-", "*": "*", "/" : "/", ":":":", "^":"^"} +p2i_infix = {"+": "+", "-": "-", "*": "*", "/" : "/", ":": ":", "^":"^"} p2i_postfix = {} p2i_other = {"(": "(", ")": ")"} post2in_fix = Render(p2i_infix, p2i_postfix, p2i_other, join = False) +# ------------------------ +# A console render + +def txtMult(op1,op2): + """ Tex render for * + Cases where \\times won't be displayed + * nbr letter + * nbr ( + * )( + """ + first_nbr = type(op1) in [int, Fraction] + seg_letter = type(op2) == str and op2.isalpha() + first_par = (first_elem(op2) == "(") + seg_par = (last_elem(op1) == ")") + + if (first_nbr and (seg_letter or seg_par)) \ + or (first_par and seg_par): + return [op1, op2] + else: + return [op1, "*", op2] + +txt_infix = {"+": "+", "-": "-", "*": txtMult, "/" : "/", ":":":", "^":"^"} +txt_postfix = {} +txt_other = {"(": "(", ")": ")"} + +txt_render = Render(txt_infix, txt_postfix, txt_other) + # ------------------------ # A latex render @@ -40,13 +58,20 @@ def texFrac(frac): return ["\\frac{" , str(frac._num) , "}{" , str(frac._denom) , "}"] def texMult(op1,op2): - """ Tex render for * """ - fe = first_elem(op2) - if type(fe) != int and (type(fe) == Polynom or fe.isalpha()): - if type(op1) == list and op1[0] == "(": - return ["(", op1[1:-1], op2, ")"] - else: - return [op1, op2] + """ Tex render for * + Cases where \\times won't be displayed + * nbr letter + * nbr ( + * )( + """ + first_nbr = type(op1) in [int, Fraction] + seg_letter = type(op2) == str and op2.isalpha() + first_par = (first_elem(op2) == "(") + seg_par = (last_elem(op1) == ")") + + if (first_nbr and (seg_letter or seg_par)) \ + or (first_par and seg_par): + return [op1, op2] else: return [op1, "\\times", op2] @@ -60,12 +85,13 @@ tex_render = Render(tex_infix, tex_postfix, tex_other, type_render = tex_type_re if __name__ == '__main__': - exp = [2, 5, '^', 1, '-', 3, 4, '*', ':'] - print(txt_render(exp)) - exp = [2, 5, '^', 1, '-', 3, 4, '*', '/', 3, 5, '/', ':'] + #exp = [2, 5, '^', 1, '-', 3, 4, '*', ':'] + #print(txt_render(exp)) + #exp = [2, 5, '^', 1, '-', 3, 4, '*', '/', 3, 5, '/', ':'] + exp = [2, -3, "*"] print(tex_render(exp)) - exp = [2, 5, '^', 1, '-', 3, 4, '*', '/', 3, '+'] - print(post2in_fix(exp)) + #exp = [2, 5, '^', 1, '-', 3, 4, '*', '/', 3, '+'] + #print(post2in_fix(exp)) diff --git a/test/test_renders.py b/test/test_renders.py index f6b189d..7d2dd59 100644 --- a/test/test_renders.py +++ b/test/test_renders.py @@ -34,14 +34,14 @@ class TestTexRender(unittest.TestCase): def test_mult_letter(self): exps = [ [2, "a", "*"], ["a", 3, "*"], [-2, "a", "*"], ["a", -2, "*"]] - wanted_render = [ "2 a", "3 a", "-2 a", "-2 a"] + wanted_render = [ "2 a", "a \\times 3", "-2 a", "a \\times ( -2 )"] for (i,e) in enumerate(exps): rend = tex_render(e) self.assertEqual(rend, wanted_render[i]) def test_mult_fraction(self): exps = [ [2, Fraction(1,2), "*"], [Fraction(1,2), 3, "*"]] - 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): rend = tex_render(e) self.assertEqual(rend, wanted_render[i]) @@ -79,7 +79,7 @@ class TesttxtRender(unittest.TestCase): def test_mult_letter(self): exps = [ [2, "a", "*"], ["a", 3, "*"], [-2, "a", "*"], ["a", -2, "*"]] - wanted_render = [ "2 a", "3 a", "-2 a", "-2 a"] + wanted_render = [ "2 a", "a * 3", "-2 a", "a * ( -2 )"] for (i,e) in enumerate(exps): rend = txt_render(e) self.assertEqual(rend, wanted_render[i]) From 8631cfd3497ddf5b0d26ba10be6f77b41d31c24b Mon Sep 17 00:00:00 2001 From: Lafrite Date: Thu, 27 Feb 2014 18:02:34 +0100 Subject: [PATCH 71/80] add __all__ --- pymath/arithmetic.py | 2 ++ pymath/expression.py | 2 ++ pymath/fraction.py | 2 ++ pymath/polynom.py | 2 ++ pymath/render.py | 2 ++ pymath/renders.py | 2 ++ 6 files changed, 12 insertions(+) diff --git a/pymath/arithmetic.py b/pymath/arithmetic.py index 2b2cadd..98fee2e 100644 --- a/pymath/arithmetic.py +++ b/pymath/arithmetic.py @@ -2,6 +2,8 @@ # encoding: utf-8 +__all__ = ['gcd'] + def gcd(a, b): """Compute gcd(a,b) diff --git a/pymath/expression.py b/pymath/expression.py index 777d8be..8121120 100644 --- a/pymath/expression.py +++ b/pymath/expression.py @@ -6,6 +6,8 @@ from .fraction import Fraction from .renders import txt_render, post2in_fix, tex_render from .polynom import Polynom +__all__ = ['Expression'] + class Expression(object): """A calculus expression. Today it can andle only expression with numbers later it will be able to manipulate unknown""" diff --git a/pymath/fraction.py b/pymath/fraction.py index aed0d06..450fb59 100644 --- a/pymath/fraction.py +++ b/pymath/fraction.py @@ -3,6 +3,8 @@ from .arithmetic import gcd +__all__ = ['Fraction'] + class Fraction(object): """Fractions!""" diff --git a/pymath/polynom.py b/pymath/polynom.py index 1341243..e376c22 100644 --- a/pymath/polynom.py +++ b/pymath/polynom.py @@ -5,6 +5,8 @@ from .fraction import Fraction from .generic import add_in_dict, remove_in_dict, convolution_dict import re +__all__ = ['Polynom'] + class Polynom(object): """A polynom (similare to Symbol in Sympy""" diff --git a/pymath/render.py b/pymath/render.py index e0dd8b9..5769673 100644 --- a/pymath/render.py +++ b/pymath/render.py @@ -5,6 +5,8 @@ from .generic import Stack,flatten_list from .fraction import Fraction from .polynom import Polynom +__all__ = ['Render'] + class Render(object): """A class which aims to create render functions from three dictionnaries: - op_infix: dict of caracters or two argument functions diff --git a/pymath/renders.py b/pymath/renders.py index 8bb4f5e..d10b67a 100644 --- a/pymath/renders.py +++ b/pymath/renders.py @@ -6,6 +6,8 @@ from .fraction import Fraction from .polynom import Polynom from .generic import first_elem, last_elem +__all__ = ['post2in_fix', 'tex_render', 'txt_render'] + # ------------------------ # A infix to postfix list convertor From f444a58a2b03fb13680a6e2a5426be802168dd9d Mon Sep 17 00:00:00 2001 From: Lafrite Date: Fri, 28 Feb 2014 08:49:03 +0100 Subject: [PATCH 72/80] Add abs method for Fraction --- pymath/fraction.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pymath/fraction.py b/pymath/fraction.py index 450fb59..74faa3d 100644 --- a/pymath/fraction.py +++ b/pymath/fraction.py @@ -273,6 +273,9 @@ class Fraction(object): return steps + def __abs__(self): + return Fraction(abs(self._num), abs(self._denom)) + def __eq__(self, other): """ == """ if type(other) == Fraction: From ab83751e57ec5d6348d91401178d8463c9d6bfc5 Mon Sep 17 00:00:00 2001 From: Lafrite Date: Fri, 28 Feb 2014 09:43:54 +0100 Subject: [PATCH 73/80] remove everything about polynoms --- pymath/expression.py | 48 +----------- pymath/polynom.py | 180 ------------------------------------------- pymath/render.py | 13 +--- pymath/renders.py | 3 +- test/test_renders.py | 7 -- 5 files changed, 5 insertions(+), 246 deletions(-) delete mode 100644 pymath/polynom.py diff --git a/pymath/expression.py b/pymath/expression.py index 8121120..58f9dd6 100644 --- a/pymath/expression.py +++ b/pymath/expression.py @@ -4,7 +4,6 @@ from .generic import Stack, flatten_list, expand_list from .fraction import Fraction from .renders import txt_render, post2in_fix, tex_render -from .polynom import Polynom __all__ = ['Expression'] @@ -141,30 +140,13 @@ class Expression(object): else: tokens.append(int(character)) - elif character.isalpha(): - # If "3x", ")x" or "yx" - if self.isNumber(tokens[-1]) \ - or tokens[-1] == ")" \ - or type(tokens[-1]) == Polynom: - tokens.append("*") - tokens.append(Polynom(letter = character)) - - # Special case for "-" at the begining of an expression or before "(" - elif tokens[-1] == "-" \ - or str(tokens[-2]) in " (": - tokens[-1] = - Polynom(letter = character) - - else: - tokens.append(Polynom(letter = character)) - elif character in "+-*/):^": tokens.append(character) elif character in "(": - # If "3(", ")(" or "x(" + # If "3(", ")(" if self.isNumber(tokens[-1]) \ - or tokens[-1] == ")" \ - or type(tokens[-1]) == Polynom: + or tokens[-1] == ")" : tokens.append("*") tokens.append(character) @@ -325,8 +307,7 @@ class Expression(object): """ return type(exp) == int or \ - type(exp) == Fraction or \ - type(exp) == Polynom + type(exp) == Fraction @staticmethod def isOperator(exp): @@ -397,13 +378,6 @@ if __name__ == '__main__': #exp="-2*4(12 + 1)(3-12)" #test(exp) - exp="-2+a+(12 + 1)(3-12) / 34a" - #test(exp) - e = Expression(exp) - print(e.render(render = tex_render)) - - #exp="-2*b+a(12 + 1)(3-12)" - #test(exp) #exp="(-2+5)/(3*4)+1/12+5*5" #test(exp) @@ -413,22 +387,6 @@ if __name__ == '__main__': #e = Expression(exp) #print(e) - exp="-2+a+(12 + 1)(3-12) : 34a" - #test(exp) - - #exp="-2+a+(12 + 1)(3-12) : 34a" - ##test(exp) - #e = Expression(exp) - #print(e.render(render = tex_render)) - - #exp="-2*b+a(12 + 1)(3-12)" - #test(exp) - - # 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) - ## Can't handle it yet!! #exp="-(-2)" #test(exp) diff --git a/pymath/polynom.py b/pymath/polynom.py deleted file mode 100644 index e376c22..0000000 --- a/pymath/polynom.py +++ /dev/null @@ -1,180 +0,0 @@ -#!/usr/bin/env python -# encoding: utf-8 - -from .fraction import Fraction -from .generic import add_in_dict, remove_in_dict, convolution_dict -import re - -__all__ = ['Polynom'] - -class Polynom(object): - """A polynom (similare to Symbol in Sympy""" - - def __init__(self, coef = {}, letter = ""): - """Initiat the polynom - - :param coef: the dictionary representing the expression - :param letter: minimum expression, a letter - - """ - - if coef != {} and letter != "": - raise ValueError("A Polynom can't be initiate with dict_exp and a letter") - elif letter != "": - self._letter = letter - self._coef = {letter: 1} - elif coef != {}: - self._coef = coef - else: - raise ValueError("Polynom needs a letter or dictionary of coeficients") - - if len(self) != 1: - self.mainOp = "+" - - def master_coef(self): - """Return the master coefficient - /!\ may not work pretty well if there is more than one indeterminate - :returns: a_n - - """ - pattern = "\w\^(\d*)" - finder = re.compile(pattern) - power = {} - for (k,v) in self._coef.items(): - if k=="": - power[0] = v - else: - p = finder.findall(k) - if p == []: - power[1] = v - else: - power[int(p[0])] = v - - m_power = max(power) - return power[m_power] - - def check_calculous(self, other): - """Check if other is a constant and then transform it into a dictionary compatible with Polynom - - :param other: The thing to compute with the expression - :returns: dictionary of this thing - - """ - if type(other) in [int, Fraction]: - return {"":other} - elif type(other) == Polynom: - return other._coef.copy() - else: - raise ValueError("Can't add {type} with Polynom".format(type=type(other))) - - def const_or_poly(self, d): - """Return a constant if there is nothing else, Polynom otherwise - - :param d: dictionary descripting the expression - :returns: a constant or a Polynom - - """ - if list(d.keys()) == ['']: - return d[''] - else: - return Polynom(d) - - def __add__(self, other): - d = self.check_calculous(other) - - d = add_in_dict(self._coef, d) - d = remove_in_dict(d) - - return [self.const_or_poly(d)] - - def __radd__(self, other): - return self + other - - def __sub__(self, other): - o_tmp = -other - return self + o_tmp - - def __neg__(self): - d = {} - for k,v in self._coef.items(): - d[k] = -v - return Polynom(d) - - def __mul__(self, other): - d = self.check_calculous(other) - - d = convolution_dict(self._coef, d, op_key = self.op_key) - d = remove_in_dict(d) - - return [self.const_or_poly(d)] - - def __rmul__(self, other): - d = self.check_calculous(other) - - d = convolution_dict(d, self._coef, op_key = self.op_key) - d = remove_in_dict(d) - - return [self.const_or_poly(d)] - - def op_key(self, x,y): - """Operation on keys for convolution_dict""" - if x == "" or y == "": - return x+y - else: - return x + "*" + y - - def __div__(self, other): - # Will never be done :D - pass - - def __pow__(self, other): - # Will never be done :D quoique - pass - - def __len__(self): - return len(list(self._coef.keys())) - - def __str__(self): - ans = "" - for k,v in self._coef.items(): - if v < 0: - ans += "-" - else: - ans += "+" - - if abs(v) == 1: - ans += str(k) - else: - ans += str(abs(v)) + str(k) - if ans[0] == "+": - return ans[1:] - else: - return ans - -if __name__ == '__main__': - fe1 = Polynom({"x": -1, "":-2}) - print(fe1) - fe2 = Polynom({"x^12": 5, "":2}) - print(fe2) - fe3 = fe1 * fe2 - for s in fe3: - print(s) - fe4 = fe1 * 2 - for s in fe4: - print(s) - - fe = Polynom(letter = "a") - fe_ = -2 * fe - print(fe_[0]) - - fe = Polynom(letter = "a") - fe_ = fe * (-2) - print(fe_[0]) - - - - -# ----------------------------- -# Reglages pour 'vim' -# vim:set autoindent expandtab tabstop=4 shiftwidth=4: -# cursor: 16 del diff --git a/pymath/render.py b/pymath/render.py index 5769673..7550686 100644 --- a/pymath/render.py +++ b/pymath/render.py @@ -3,7 +3,6 @@ from .generic import Stack,flatten_list from .fraction import Fraction -from .polynom import Polynom __all__ = ['Render'] @@ -17,7 +16,7 @@ class Render(object): PRIORITY = {"^": 4,"*" : 3, "/": 3, ":": 3, "+": 2, "-":2, "(": 1} - def __init__(self, op_infix = {}, op_postfix = {}, other = {}, join = " ", type_render = {str: str, int: str, Fraction: str, Polynom: str}): + def __init__(self, op_infix = {}, op_postfix = {}, other = {}, join = " ", type_render = {str: str, int: str, Fraction: str}): """Initiate the render @param op_infix: the dictionnary of infix operator with how they have to be render @@ -124,15 +123,6 @@ class Render(object): or type(operande) == Fraction: return 0 - # Si c'est un polynom - elif type(operande) == Polynom: - if operator in ["*", "/", "^"]: - if len(operande) > 1 \ - or operande.master_coef() < 0: - return 1 - else: - return 0 - elif not self.isNumber(operande): # Si c'est une grande expression stand_alone = self.get_main_op(operande) @@ -197,7 +187,6 @@ class Render(object): """ return type(exp) == int \ or type(exp) == Fraction \ - or type(exp) == Polynom \ or exp.isalpha() def isOperator(self, exp): diff --git a/pymath/renders.py b/pymath/renders.py index d10b67a..1db9114 100644 --- a/pymath/renders.py +++ b/pymath/renders.py @@ -3,7 +3,6 @@ from .render import Render from .fraction import Fraction -from .polynom import Polynom from .generic import first_elem, last_elem __all__ = ['post2in_fix', 'tex_render', 'txt_render'] @@ -80,7 +79,7 @@ def texMult(op1,op2): tex_infix = {"+": " + ", "-": " - ", "*": texMult , ":": ":", "^":"^"} tex_postfix = {"/": texSlash} tex_other = {"(": "(", ")": ")"} -tex_type_render = {str:str, int: str, Fraction: texFrac, Polynom: str} +tex_type_render = {str:str, int: str, Fraction: texFrac} tex_render = Render(tex_infix, tex_postfix, tex_other, type_render = tex_type_render) diff --git a/test/test_renders.py b/test/test_renders.py index 7d2dd59..87da4f6 100644 --- a/test/test_renders.py +++ b/test/test_renders.py @@ -6,7 +6,6 @@ import unittest from pymath.renders import tex_render, txt_render from pymath.fraction import Fraction -from pymath.polynom import Polynom @@ -22,9 +21,6 @@ class TestTexRender(unittest.TestCase): def test_type_render_fraction(self): self.assertEqual(tex_render([Fraction(1,2)]), "\\frac{ 1 }{ 2 }") - def test_type_render_polynom(self): - self.assertEqual(tex_render([Polynom({"": 1, "x": 3})]), "3x + 1") - def test_mult_interger(self): exps = [ [2, 3, "*"], [2, -3, "*"], [-2, 3, "*"]] wanted_render = [ "2 \\times 3", "2 \\times ( -3 )", "-2 \\times 3"] @@ -67,9 +63,6 @@ class TesttxtRender(unittest.TestCase): def test_type_render_fraction(self): self.assertEqual(txt_render([Fraction(1,2)]), "1 / 2") - def test_type_render_polynom(self): - self.assertEqual(txt_render([Polynom({"": 1, "x": 3})]), "3x + 1") - def test_mult_interger(self): exps = [ [2, 3, "*"], [2, -3, "*"], [-2, 3, "*"]] wanted_render = [ "2 * 3", "2 * ( -3 )", "-2 * 3"] From 09bbb04ab280d9580bf6a850b679cbe6c03625ef Mon Sep 17 00:00:00 2001 From: Lafrite Date: Fri, 28 Feb 2014 09:45:19 +0100 Subject: [PATCH 74/80] remove expressions with letters --- pymath/random_expression.py | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/pymath/random_expression.py b/pymath/random_expression.py index d11212c..35cd268 100644 --- a/pymath/random_expression.py +++ b/pymath/random_expression.py @@ -136,18 +136,11 @@ if __name__ == '__main__': #desc_rdExp(rdExp1) #rdExp2 = RdExpression(form) #desc_rdExp(rdExp2) - #form = "{a+a/10}x + {a} + 2*{b}" + #form = "{a+a/10}*4 + {a} + 2*{b}" #cond = ["{a} + {b} in [1, 2, 3, 4, 5]", "{a} not in [0,1]", "{b} not in [0,1]"] #rdExp3 = RdExpression(form) #desc_rdExp(rdExp3) - form1 = "{a**2}x^2 + {2*a*b}x + {b**2}" - cond1 = [] - rdExp1 = RdExpression(form1, cond1, with_Exp = False) - desc_rdExp(rdExp1) - rdExp1 = RdExpression(form1, cond1) - desc_rdExp(rdExp1) - From d67d68e080b7f228996419f182140ab200c61741 Mon Sep 17 00:00:00 2001 From: Lafrite Date: Fri, 28 Feb 2014 09:52:27 +0100 Subject: [PATCH 75/80] spelling mistake in filename --- test/{test_arthmetic.py => test_arithmetic.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename test/{test_arthmetic.py => test_arithmetic.py} (100%) diff --git a/test/test_arthmetic.py b/test/test_arithmetic.py similarity index 100% rename from test/test_arthmetic.py rename to test/test_arithmetic.py From ae6664b7cf868804fdd8b242c29a5550ae8cb464 Mon Sep 17 00:00:00 2001 From: Lafrite Date: Fri, 28 Feb 2014 10:31:56 +0100 Subject: [PATCH 76/80] new unittest for expression --- pymath/expression.py | 2 +- test/test_expression.py | 41 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/pymath/expression.py b/pymath/expression.py index 58f9dd6..99f0761 100644 --- a/pymath/expression.py +++ b/pymath/expression.py @@ -111,7 +111,7 @@ class Expression(object): ## --------------------- ## String parsing - ## @classmethod ???? + @classmethod def str2tokens(self, exp): """ Parse the expression, ie tranform a string into a list of tokens diff --git a/test/test_expression.py b/test/test_expression.py index 34cc015..16276ba 100644 --- a/test/test_expression.py +++ b/test/test_expression.py @@ -8,13 +8,16 @@ import unittest from pymath.expression import Expression from pymath.fraction import Fraction from pymath.generic import first_elem +from pymath.renders import txt_render class TestExpression(unittest.TestCase): """Testing functions from pymath.expression""" - def test_init_from_exp(self): - pass + def test_init_from_str(self): + exp = Expression("2 + 3") + self.assertEqual(exp.infix_tokens, [2, "+", 3]) + self.assertEqual(exp.postfix_tokens, [2, 3, "+"]) def test_init_from_exp(self): pass @@ -25,6 +28,30 @@ class TestExpression(unittest.TestCase): def test_postfix_tokens(self): pass + def test_str2tokens_big_num(self): + exp = "123 + 3" + tok = Expression.str2tokens(exp) + self.assertEqual(tok, [123, "+", 3]) + + def test_str2tokens_beg_minus(self): + exp = "-123 + 3" + tok = Expression.str2tokens(exp) + self.assertEqual(tok, [-123, "+", 3]) + + def test_str2tokens_time_lack(self): + exp = "(-3)(2)" + tok = Expression.str2tokens(exp) + self.assertEqual(tok, ["(", -3, ")", "*","(", 2, ")" ]) + + def test_str2tokens_time_lack2(self): + exp = "-3(2)" + tok = Expression.str2tokens(exp) + self.assertEqual(tok, [-3, "*","(", 2, ")" ]) + + def test_str2tokens_error(self): + exp = "1 + $" + self.assertRaises(ValueError, Expression.str2tokens, exp) + def test_doMath(self): ops = [\ {"op": ("+", 1 , 2), "res" : 3}, \ @@ -43,6 +70,16 @@ class TestExpression(unittest.TestCase): def test_isOperator(self): pass + def test_simplify_frac(self): + exp = Expression("1/2 - 4") + steps = ["[1, 2, '/', 4, '-']", \ + "[< Fraction 1 / 2>, 4, '-']", \ + "[1, 1, '*', 2, 1, '*', '/', 4, 2, '*', 1, 2, '*', '/', '-']", \ + "[1, 8, '-', 2, '/']", \ + '[< Fraction -7 / 2>]'] + self.assertEqual(steps, list(exp.simplify())) + + if __name__ == '__main__': unittest.main() From 1aaecd4b8400ac43568eb9f677c1fbaf65513f0f Mon Sep 17 00:00:00 2001 From: Lafrite Date: Fri, 28 Feb 2014 11:34:31 +0100 Subject: [PATCH 77/80] Do not accept floating numbers --- pymath/expression.py | 3 +++ test/test_expression.py | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/pymath/expression.py b/pymath/expression.py index 99f0761..1e63c53 100644 --- a/pymath/expression.py +++ b/pymath/expression.py @@ -150,6 +150,9 @@ class Expression(object): tokens.append("*") tokens.append(character) + elif character == ".": + raise ValueError("No float number please") + elif character != " ": raise ValueError("{} is an unvalid character".format(character)) diff --git a/test/test_expression.py b/test/test_expression.py index 16276ba..d079e77 100644 --- a/test/test_expression.py +++ b/test/test_expression.py @@ -48,6 +48,10 @@ class TestExpression(unittest.TestCase): tok = Expression.str2tokens(exp) self.assertEqual(tok, [-3, "*","(", 2, ")" ]) + def test_str2tokens_error_float(self): + exp = "1 + 1.3" + self.assertRaises(ValueError, Expression.str2tokens, exp) + def test_str2tokens_error(self): exp = "1 + $" self.assertRaises(ValueError, Expression.str2tokens, exp) From bdad59ef8a89589a190860170fc498c386a9041e Mon Sep 17 00:00:00 2001 From: Lafrite Date: Fri, 28 Feb 2014 13:16:57 +0100 Subject: [PATCH 78/80] Forgot to remove a isalpha --- pymath/render.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pymath/render.py b/pymath/render.py index 7550686..24c36e8 100644 --- a/pymath/render.py +++ b/pymath/render.py @@ -186,8 +186,7 @@ class Render(object): """ return type(exp) == int \ - or type(exp) == Fraction \ - or exp.isalpha() + or type(exp) == Fraction def isOperator(self, exp): """Check if the expression is in self.operators From 193b050a09023d5f0635cd0dd27e90d55e8622e2 Mon Sep 17 00:00:00 2001 From: Lafrite Date: Fri, 28 Feb 2014 13:17:12 +0100 Subject: [PATCH 79/80] Random_expression accept calc in conditions --- pymath/random_expression.py | 49 +++++++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/pymath/random_expression.py b/pymath/random_expression.py index 35cd268..ba209bd 100644 --- a/pymath/random_expression.py +++ b/pymath/random_expression.py @@ -6,6 +6,8 @@ from .expression import Expression from .renders import tex_render, txt_render import re +from .arithmetic import gcd + class RdExpression(object): """A generator of random expression builder""" @@ -30,11 +32,17 @@ class RdExpression(object): """ pattern = "\{(.*?)\}" #select inside {} non greedy way - varia = re.findall(pattern, self._form) - varia = set(varia) - self._2replaced = varia - return varia + varia_form = re.findall(pattern, self._form) + varia_form = set(varia_form) + + varia_cond = set() + for c in self._conditions: + varia_cond = varia_cond | set(re.findall(pattern, c)) + + self._2replaced = varia_cond | varia_form + + return self._2replaced def get_letters(self): """Find letters in the form @@ -96,7 +104,9 @@ class RdExpression(object): return Expression(exp) def gene_varia(self, val_min = -10, val_max = 10): - """RAndomly generates variables/letters + """Randomly generates variables/letters + + Varia can't be equal to 0 """ for l in self._letters: @@ -104,6 +114,7 @@ class RdExpression(object): while self._gene_varia[l] == 0: self._gene_varia[l] = randint(val_min, val_max) + for e in self._2replaced: self._gene_2replaced[e] = eval(e, globals(), self._gene_varia) @@ -113,7 +124,7 @@ class RdExpression(object): """ if self._conditions != []: - return eval(" and ".join(self._conditions).format(**self._gene_varia)) + return eval(" and ".join(self._conditions).format(**self._gene_2replaced)) else: return True @@ -130,16 +141,22 @@ def desc_rdExp(rdExp): if __name__ == '__main__': - #form = "{a}*-14 / (2*{b}) : -23 / 4" - #cond = ["{a} + {b} in [1, 2, 3, 4, 5]", "{a} not in [0,1]", "{b} not in [0,1]"] - #rdExp1 = RdExpression(form, cond) - #desc_rdExp(rdExp1) - #rdExp2 = RdExpression(form) - #desc_rdExp(rdExp2) - #form = "{a+a/10}*4 + {a} + 2*{b}" - #cond = ["{a} + {b} in [1, 2, 3, 4, 5]", "{a} not in [0,1]", "{b} not in [0,1]"] - #rdExp3 = RdExpression(form) - #desc_rdExp(rdExp3) + form = "{a}*-14 / (2*{b}) : -23 / 4" + cond = ["{a} + {b} in [1, 2, 3, 4, 5]", "{a} not in [1]", "{b} not in [1]"] + rdExp1 = RdExpression(form, cond) + desc_rdExp(rdExp1) + rdExp2 = RdExpression(form) + desc_rdExp(rdExp2) + + form = "{a+a*10}*4 + {a} + 2*{b}" + cond = ["{a} + {b} in [1, 2, 3, 4, 5]", "abs({a}) not in [1]", "{b} not in [1]", "gcd({a},{b}) == 1"] + rdExp3 = RdExpression(form, cond) + desc_rdExp(rdExp3) + + 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"] + rdExp3 = RdExpression(form, cond) + desc_rdExp(rdExp3) From 661498efcfef240accd754519f7d00395e9a607b Mon Sep 17 00:00:00 2001 From: Lafrite Date: Fri, 28 Feb 2014 14:11:33 +0100 Subject: [PATCH 80/80] add unittest for random_expression --- test/test_random_expression.py | 100 +++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 test/test_random_expression.py diff --git a/test/test_random_expression.py b/test/test_random_expression.py new file mode 100644 index 0000000..f69a627 --- /dev/null +++ b/test/test_random_expression.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python +# encoding: utf-8 + + +import unittest + +from pymath.random_expression import RdExpression + + +class TestRandomExpression(unittest.TestCase): + """Testing functions from pymath.random_expression""" + + def test_only_form(self): + form = "{a} + 2" + rdExp = RdExpression(form) + + self.assertEqual(rdExp._letters, {'a'}) + self.assertEqual(rdExp._2replaced, {'a'}) + + rdExp() + self.assertEqual(set(rdExp._gene_varia.keys()), {'a'}) + self.assertEqual(set(rdExp._gene_2replaced.keys()), {'a'}) + + def test_only_form_calc(self): + form = "{a + b} + 2" + rdExp = RdExpression(form) + + self.assertEqual(rdExp._letters, {'a', 'b'}) + self.assertEqual(rdExp._2replaced, {'a + b'}) + + rdExp() + self.assertEqual(set(rdExp._gene_varia.keys()), {'a', 'b'}) + self.assertEqual(set(rdExp._gene_2replaced.keys()), {'a + b'}) + + def test_only_form_cond(self): + form = "{a} + 2" + cond = ["{a} == 3"] + rdExp = RdExpression(form, cond) + + self.assertEqual(rdExp._letters, {'a'}) + self.assertEqual(rdExp._2replaced, {'a'}) + + rdExp() + self.assertEqual(set(rdExp._gene_varia.keys()), {'a'}) + self.assertEqual(set(rdExp._gene_2replaced.keys()), {'a'}) + + self.assertEqual(rdExp._gene_varia['a'], 3) + + def test_only_form_conds(self): + form = "{a} + 2" + cond = ["{a} in list(range(5))", "{a} % 2 == 1"] + rdExp = RdExpression(form, cond) + + self.assertEqual(rdExp._letters, {'a'}) + self.assertEqual(rdExp._2replaced, {'a'}) + + rdExp() + self.assertEqual(set(rdExp._gene_varia.keys()), {'a'}) + self.assertEqual(set(rdExp._gene_2replaced.keys()), {'a'}) + + self.assertTrue(rdExp._gene_varia['a'] in list(range(5))) + self.assertTrue(rdExp._gene_varia['a'] % 2 == 1) + + def test_only_form_calc_cond(self): + form = "{a*3} * {b}" + cond = ["{a} == 3"] + rdExp = RdExpression(form, cond) + + self.assertEqual(rdExp._letters, {'a', 'b'}) + self.assertEqual(rdExp._2replaced, {'a', 'b', 'a*3'}) + + rdExp() + self.assertEqual(set(rdExp._gene_varia.keys()), {'a', 'b'}) + self.assertEqual(set(rdExp._gene_2replaced.keys()), {'a', 'b', 'a*3'}) + + self.assertEqual(rdExp._gene_varia['a'], 3) + + + def test_only_form_calc_cond_calc(self): + form = "{a} + 2" + + pass + + + +if __name__ == '__main__': + unittest.main() + + + + + + + + +# ----------------------------- +# Reglages pour 'vim' +# vim:set autoindent expandtab tabstop=4 shiftwidth=4: +# cursor: 16 del +