diff --git a/pymath/calculus/operator.py b/pymath/calculus/operator.py deleted file mode 100644 index 01cdee9..0000000 --- a/pymath/calculus/operator.py +++ /dev/null @@ -1,646 +0,0 @@ -#!/usr/bin/env python -# encoding: utf-8 - -#from debug.tools import report - -from .generic import flatten_list, isNumber -from functools import wraps -import types - -class Operator(str): - - """The operator class, is a string (representation of the operator) with its arity""" - - def __new__(cls, operator = "", name = "", priority = 0, actions = ("",""), txt = "", tex = "", arity = 2): - """ Create an Operator """ - #def __new__(cls, operator, arity = 2): - op = str.__new__(cls, operator) - op.operator = operator - op.name = name - op.arity = arity - op.priority = priority - op.actions = actions - op.txt = txt - op.tex = tex - - op.isOperator = 1 - # TODO: Add self.visibility |sam. nov. 8 17:00:08 CET 2014 - return op - - def __call__(self, *args): - """ Calling this operator performs the rigth calculus """ - return self._call(*args) - - - def _call(self, *args): - """Trick to avoid overloading __call__ """ - if self.arity == 1: - return getattr(args[0], self.actions)() - - elif self.arity == 2: - #if type(args[1]) == int: - if issubclass(type(args[1]),int): - return getattr(args[0], self.actions[0])(args[1]) - else: - return getattr(args[1], self.actions[1])(args[0]) - - def _render(self, link, *args): - """Global step for __txt__ and __tex__ - - :param link: the link between operators - :param *args: the operands - :returns: the string with operator and operands - - """ - if self.arity == 1: - op1 = self.l_parenthesis(args[0], True) - ans = link.format(op1 = op1) - - elif self.arity == 2: - op1 = self.l_parenthesis(args[0], True) - op2 = self.r_parenthesis(args[1], True) - ans = link.format(op1 = op1, op2 = op2) - - ans = save_mainOp(ans, self) - return ans - - def __txt__(self, *args): - """Txt rendering for the operator - - :*args: Operands for this operation - :returns: String with operator and his operands - - >>> op.mul.__txt__('1','2') - '1 * 2' - >>> op.add.__txt__('1','2') - '1 + 2' - >>> f = save_mainOp('2 + 3',op.add) - >>> op.mul.__txt__(f, '4') - '( 2 + 3 ) * 4' - >>> f = save_mainOp('-3',op.sub1) - >>> op.sub1.__txt__(f) - '- ( -3 )' - >>> op.sub1.__txt__('-3') - '- ( -3 )' - >>> f = save_mainOp('2 + 3',op.add) - >>> op.sub1.__txt__(f) - '- ( 2 + 3 )' - """ - return self._render(self.txt, *args) - - def __tex__(self, *args): - """Tex rendering for the operator - - :*args: Operands for this operation - :returns: String with operator and his operands - - >>> op.mul.__tex__('1','2') - '1 \\\\times 2' - >>> op.add.__tex__('1','2') - '1 + 2' - >>> f = save_mainOp('2 + 3',op.add) - >>> op.mul.__tex__(f, '4') - '( 2 + 3 ) \\\\times 4' - >>> f = save_mainOp('-3',op.sub1) - >>> op.sub1.__tex__(f) - '- ( -3 )' - >>> op.sub1.__tex__('-3') - '- ( -3 )' - >>> f = save_mainOp('2 + 3',op.add) - >>> op.sub1.__tex__(f) - '- ( 2 + 3 )' - """ - return self._render(self.tex, *args) - - def __p2i__(self, *args): - """Fix list transformation for the operator - - :*args: Operands for this operation - :returns: list with the operator surrounded by operands - - >>> op.mul.__p2i__(1,2) - [1, '*', 2] - >>> f = save_mainOp([2, op.add, 3],op.add) - >>> op.mul.__p2i__(f, 4) - ['(', 2, '+', 3, ')', '*', 4] - >>> f = save_mainOp([op.sub1, 3],op.sub1) - >>> op.sub1.__p2i__(f) - ['-', '(', '-', 3, ')'] - >>> op.sub1.__p2i__(-3) - ['-', '(', -3, ')'] - >>> f = save_mainOp([2, op.add, 3],op.add) - >>> op.sub1.__p2i__(f) - ['-', '(', 2, '+', 3, ')'] - """ - if self.arity == 1: - op1 = self.l_parenthesis(args[0]) - ans = flatten_list([self, op1]) - - elif self.arity == 2: - op1 = self.l_parenthesis(args[0]) - op2 = self.r_parenthesis(args[1]) - ans = flatten_list([op1, self, op2]) - - ans = save_mainOp(ans, self) - return ans - - def l_parenthesis(self, opl, str_join=False): - """ Add parenthesis for left operand if necessary """ - ans = opl - try: - # TODO: Je pige pas pourquoi quand on enlève .name ça marche plus... |lun. avril 27 19:07:24 CEST 2015 - if opl.mainOp.name == op.sub1.name: - ans = opl - elif opl.mainOp.priority < self.priority: - ans = flatten_list(["(", opl, ")"]) - except AttributeError as e: - # op has not the attribute priority - pass - - ans = flatten_list([ans]) - if str_join: - ans = ' '.join([str(i) for i in ans]) - return ans - - def r_parenthesis(self, op, str_join=False): - """ Add parenthesis for rigth operand if necessary """ - try: - if op.mainOp.priority < self.priority: - op = flatten_list(["(", op, ")"]) - except AttributeError: - # op has not the attribute priority - try: - if int(op) < 0: - op = ['(', op, ')'] - except ValueError: - pass - ans = flatten_list([op]) - if str_join: - ans = ' '.join([str(i) for i in ans]) - return ans - -def save_mainOp(obj, mainOp): - """Create a temporary class build over built-in type to stock the main operation of a calculus - - :obj: the object to add the attribute - :mainOp: the main operator - :returns: the same object with the main operation attribute - """ - Fake = type('fake_'+str(type(obj)), (type(obj),), {'mainOp': mainOp}) - - return Fake(obj) - -def operatorize(fun): - """Transform the answer of the function into an operator - - The returned value of the function has to be a dictionnary with those keys - * "operator": the name (Needed!) - * "priority": the priority level - * "actions": mathematics actions of the operator (list of 1 element if the arity is 1, 2 elements if arity is 2) - * "txt": string ready to be formated in txt for with {op1} and/or {op2} - * "tex": string ready to be formated in tex for with {op1} and/or {op2} - * "arity": arity ie number of operands needed - * "_call": action to perform when call the operator - * "_render": action use in __txt__ and __tex__ - * "__txt__": txt rendering - * "__tex__": tex rendering - * "l_parenthesis": mechanism to add parenthesis for left operande - * "r_parenthesis": mechanism to add parenthesis for rigth operande - """ - @wraps(fun) - def mod_fun(self, *args): - ans = fun(self, *args) - - def _eq(op1, op2): - """ op1 == op2 """ - return op1.name == op2.name == name and op1.arity == op2.arity - - ans["__eq__"] = _eq - - new_op = Operator(ans["operator"]) - for (attr, value) in ans.items(): - if hasattr(value, '__call__'): - setattr(new_op, attr, types.MethodType(value, new_op)) - else: - setattr(new_op, attr, value) - - return new_op - return mod_fun - -class ClassProperty(object): - - def __init__(self, fget): - self.fget = fget - - def __get__(self, owner_self, owner_cls): - return self.fget(owner_cls) - -class op(object): - """ List of admited operations """ - - _operators = {("+",2): "add",\ - ("-", 2): "sub",\ - ("-", 1): "sub1",\ - ("*", 2): "mul",\ - ("/", 2): "div",\ - ("^", 2): "pw",\ - ("(", 2): "par",\ - } - - @ClassProperty - @operatorize - def add(cls): - """ The operator + - - For doctest see test/test_operator.py - - """ - - def _render(self, link, *args): - """Global step for __txt__ and __tex__ - - :param link: the link between operators - :param *args: the operands - :returns: the string with operator and operands - - """ - if args[1][0] == "-": - op1 = self.l_parenthesis(args[0], True) - op2 = self.r_parenthesis(args[1][1:], True) - ans = link.replace('+','-').format(op1 = op1, op2 = op2) - - ans = save_mainOp(ans, self) - return ans - else: - op1 = self.l_parenthesis(args[0], True) - op2 = self.r_parenthesis(args[1], True) - ans = link.format(op1 = op1, op2 = op2) - - ans = save_mainOp(ans, self) - return ans - - caract = { - "operator" : "+", \ - "name" : "add",\ - "priority" : 1, \ - "arity" : 2, \ - "actions" : ("__add__","__radd__"), \ - "txt" : "{op1} + {op2}",\ - "tex" : "{op1} + {op2}",\ - "_render": _render,\ - } - - return caract - - @ClassProperty - @operatorize - def sub(self): - """ The operator - - - >>> sub = op.sub - >>> sub - '-' - >>> sub(1, 2) - -1 - >>> sub.__tex__('1','2') - '1 - 2' - >>> sub.__txt__('1','2') - '1 - 2' - >>> sub.__tex__('1','-2') - '1 - (-2)' - >>> sub.__tex__('-1','2') - '-1 - 2' - """ - def l_parenthesis(self, op, str_join=False): - return op - - def r_parenthesis(self, op, str_join=False): - try: - if op.mainOp.priority <= self.priority: - op = flatten_list(["(", op, ")"]) - elif op[0] == '-': - op = flatten_list(["(", op, ")"]) - except AttributeError: - # op has not the attribute priority - try: - if int(op) < 0: - op = ['(', op, ')'] - except ValueError: - pass - ans = flatten_list([op]) - if str_join: - ans = ' '.join([str(i) for i in ans]) - return ans - - caract = { - "operator" : "-", \ - "name" : "sub",\ - "priority" : 2, \ - "arity" : 2, \ - "actions" : ("__sub__","__rsub__"), \ - "txt" : "{op1} - {op2}",\ - "tex" : "{op1} - {op2}",\ - "l_parenthesis": l_parenthesis,\ - "r_parenthesis": r_parenthesis,\ - } - - return caract - - @ClassProperty - @operatorize - def sub1(self): - """ The operator - - - >>> sub1 = op.sub1 - >>> sub1 - '-' - >>> sub1(1) - -1 - >>> sub1.__tex__('1') - '- 1' - >>> sub1.__txt__('1') - '- 1' - >>> sub1.__tex__('-1') - '- (-1)' - """ - def l_parenthesis(self, op, str_join=False): - """ Add parenthesis if necessary """ - try: - if op.mainOp.priority <= self.priority: - op = flatten_list(["("] + [op] + [")"]) - except AttributeError: - # op has not the attribute priority - try: - if int(op) < 0: - op = ['(', op, ')'] - except ValueError: - pass - - ans = flatten_list([op]) - if str_join: - ans = ' '.join([str(i) for i in ans]) - return ans - - caract = { - "operator" : "-", \ - "name" : "sub1",\ - "priority" : 3, \ - "arity" : 1, \ - "actions" : "__neg__",\ - "txt" : "- {op1}",\ - "tex" : "- {op1}",\ - "l_parenthesis": l_parenthesis,\ - } - - return caract - - @ClassProperty - @operatorize - def mul(self): - """ The operator * - - >>> mul = op.mul - >>> mul - '*' - >>> mul(1, 2) - 2 - >>> mul.__tex__('1','2') - '1 \\times 2' - >>> mul.__tex__('2','a') - '2 a' - >>> mul.__txt__('1','2') - '1 * 2' - >>> mul.__txt__('2','a') - '2 a' - >>> mul.__txt__('a','2') - 'a * 2' - >>> mul.__tex__('1','-2') - '1 \\times (-2)' - """ - # * can not be display in some cases - def is_visible(self, op1, op2): - """ Tells whether self has to be visible or not - - :param op1: left operande - :param op2: rigth operande - - """ - # TODO: À finir!!! |lun. mars 9 00:03:40 CET 2015 - if type(op2) == int: - # op2 est maintenant une chaine de caractères - return True - elif op2.isdecimal(): - return True - elif op2.isalpha(): - return False - elif op2[0].isdecimal(): - return True - elif op2[0] == "(" and not ("+" in op2 or "-" in op2[3:]): - return True - # Giga bricolage... - elif "frac" in op2: - return True - else: - return False - - def _render(self, link, *args): - - op1 = self.l_parenthesis(args[0], True) - op2 = self.r_parenthesis(args[1], True) - - if not self.is_visible(op1, op2): - ans = "{op1} {op2}".format(op1 = op1, op2 = op2) - else: - ans = link.format(op1 = op1, op2 = op2) - - ans = save_mainOp(ans, self) - return ans - - caract = { - "operator" : "*", \ - "name" : "mul",\ - "priority" : 4, \ - "arity" : 2, \ - "actions" : ("__mul__","__rmul__"), \ - "txt" : "{op1} * {op2}",\ - "tex" : "{op1} \\times {op2}",\ - "visibility": 1,\ - "_render": _render,\ - "is_visible": is_visible,\ - } - - return caract - - @ClassProperty - @operatorize - def div(self): - """ The operator / - - >>> div = op.div - >>> div - '/' - >>> div(1, 2) - < Fraction 1 / 2> - >>> div.__tex__('1','2') - '\\frac{ 1 }{ 2 }' - >>> div.__tex__('1','2') - '\\frac{ -1 }{ 2 }' - >>> div.__txt__('1','2') - '1 / 2' - """ - from .fraction import Fraction - def _call(self, op1, op2): - if op2 == 1: - return op1 - else: - return Fraction(op1,op2) - - def __tex__(self, *args): - # Pas besoin de parenthèses en plus pour \frac - replacement = {"op"+str(i+1): op for (i,op) in enumerate(args)} - - ans = self.tex.format(**replacement) - ans = save_mainOp(ans, self) - return ans - - caract = { - "operator" : "/", \ - "name" : "div",\ - "priority" : 5, \ - "arity" : 2, \ - "txt" : "{op1} / {op2}",\ - "tex" : "\\frac{{ {op1} }}{{ {op2} }}",\ - "_call": _call,\ - "__tex__":__tex__,\ - } - - return caract - - @ClassProperty - @operatorize - def pw(self): - """ The operator ^ - - >>> pw = op.pw - >>> pw - '^' - >>> pw(2, 3) - 8 - >>> pw.__tex__('2','3') - '2^{ 3 }' - >>> pw.__txt__('2','3') - '2 ^ 3' - >>> pw.__txt__('-2','3') - '( -2 ) ^ 3' - """ - def _call(self, op1, op2): - """ Calling this operator performs the rigth calculus """ - return getattr(op1, "__pow__")(op2) - - def l_parenthesis(self, opl, str_join=False): - """ Add parenthesis for left operand if necessary """ - ans = opl - try: - if opl.mainOp.priority < self.priority: - ans = flatten_list(["(", opl, ")"]) - except AttributeError as e: - # op has not the attribute priority - pass - try: - if int(opl) < 0: - ans = ["(", opl, ")"] - except ValueError: - pass - - ans = flatten_list([ans]) - if str_join: - ans = ' '.join([str(i) for i in ans]) - return ans - - caract = { - "operator" : "^", \ - "name" : "pw",\ - "priority" : 6, \ - "arity" : 2, \ - "actions" : ("__pow__",""), \ - "txt" : "{op1} ^ {op2}",\ - "tex" : "{op1}^{{ {op2} }}",\ - "l_parenthesis": l_parenthesis,\ - "_call":_call,\ - } - - return caract - - @ClassProperty - @operatorize - def par(self): - """ The operator ( """ - caract = { - "operator" : "(", \ - "name" : "par",\ - "priority" : 0, \ - "arity" : 0, \ - } - return caract - - @classmethod - def get_op(cls, op, arity = 2): - """Return the corresponding operator - - :op: symbole of the op - :arity: the arity - - >>> op.get_op('+') - '+' - >>> mul = op.get_op('*') - >>> mul.tex - '{op1} \\\\times {op2}' - >>> mul.txt - '{op1} * {op2}' - """ - try: - return getattr(cls, cls._operators[(op, arity)]) - except KeyError: - raise KeyError("{theOp} (arity: {arity}) is not available".format(theOp = op, arity = arity)) - - @classmethod - def can_be_operator(cls, symbole): - """ Tell if the symbole can be an operator """ - if type(symbole) == str: - return symbole in [i[0] for i in cls._operators] - else: - return False - -if __name__ == '__main__': - #print(op.add.__tex__('1','2')) - #print(op.mul.__tex__('1','2')) - #print(op.sub.__tex__('1','2')) - #f = save_mainOp('2 + 3',op.add) - #print(op.mul.__txt__(f, '4')) - #f = save_mainOp('-3',op.sub1) - #print(op.sub1.__txt__(f)) - #print(op.sub1.__txt__('-3')) - #f = save_mainOp('2 + 3',op.add) - #print(op.sub1.__txt__(f)) - - #from .fraction import Fraction - #f = Fraction(1, 2) - #print(op.add.__txt__(f.__txt__(),'2')) - #print(op.add.__tex__(f.__tex__(),'2')) - - #print("\t op.can_be_operator('+') :" + str(op.can_be_operator('+'))) - #print("\t op.can_be_operator('t') :" + str(op.can_be_operator('t'))) - - print("op.sub.__dict__ -> ", op.sub.__dict__) - print(op.sub == op.sub1) - #import doctest - #doctest.testmod() - - - - - -# ----------------------------- -# Reglages pour 'vim' -# vim:set autoindent expandtab tabstop=4 shiftwidth=4: -# cursor: 16 del diff --git a/pymath/calculus/operator/__init__.py b/pymath/calculus/operator/__init__.py new file mode 100644 index 0000000..ccf3861 --- /dev/null +++ b/pymath/calculus/operator/__init__.py @@ -0,0 +1,12 @@ +#!/usr/bin/env python +# encoding: utf-8 + + +from .operator_set import op +from .operator import save_mainOp + + +# ----------------------------- +# Reglages pour 'vim' +# vim:set autoindent expandtab tabstop=4 shiftwidth=4: +# cursor: 16 del diff --git a/pymath/calculus/operator/add.py b/pymath/calculus/operator/add.py new file mode 100644 index 0000000..d4110ac --- /dev/null +++ b/pymath/calculus/operator/add.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python +# encoding: utf-8 + +from .operator import Operator, save_mainOp + +class Add(Operator): + + _CARACT = { + "operator" : "+", \ + "name" : "add",\ + "priority" : 1, \ + "arity" : 2, \ + "actions" : ("__add__","__radd__"), \ + "txt" : "{op1} + {op2}",\ + "tex" : "{op1} + {op2}",\ + } + + def __init__(self): + """ Initiate Add Operator """ + super(Add, self).__init__(**self._CARACT) + + def _render(self, link, *args): + """Global step for __txt__ and __tex__ + + :param link: the link between operators + :param *args: the operands + :returns: the string with operator and operands + + """ + if args[1][0] == "-": + op1 = self.l_parenthesis(args[0], True) + op2 = self.r_parenthesis(args[1][1:], True) + ans = link.replace('+','-').format(op1 = op1, op2 = op2) + + ans = save_mainOp(ans, self) + return ans + else: + op1 = self.l_parenthesis(args[0], True) + op2 = self.r_parenthesis(args[1], True) + ans = link.format(op1 = op1, op2 = op2) + + ans = save_mainOp(ans, self) + return ans + + + +# ----------------------------- +# Reglages pour 'vim' +# vim:set autoindent expandtab tabstop=4 shiftwidth=4: +# cursor: 16 del diff --git a/pymath/calculus/operator/div.py b/pymath/calculus/operator/div.py new file mode 100644 index 0000000..51d67c0 --- /dev/null +++ b/pymath/calculus/operator/div.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python +# encoding: utf-8 + +from .operator import Operator, save_mainOp + +class Div(Operator): + + """ The operator / + + >>> div = op.div + >>> div + '/' + >>> div(1, 2) + < Fraction 1 / 2> + >>> div.__tex__('1','2') + '\\frac{ 1 }{ 2 }' + >>> div.__tex__('1','2') + '\\frac{ -1 }{ 2 }' + >>> div.__txt__('1','2') + '1 / 2' + """ + _CARACT = { + "operator" : "/", \ + "name" : "div",\ + "priority" : 5, \ + "arity" : 2, \ + "txt" : "{op1} / {op2}",\ + "tex" : "\\frac{{ {op1} }}{{ {op2} }}",\ + } + + def __init__(self): + """ Initiate Div Operator """ + super(Div, self).__init__(**self._CARACT) + + def __call__(self, op1, op2): + if op2 == 1: + return op1 + else: + from ..fraction import Fraction + return Fraction(op1,op2) + + def __tex__(self, *args): + # Pas besoin de parenthèses en plus pour \frac + replacement = {"op"+str(i+1): op for (i,op) in enumerate(args)} + + ans = self.tex.format(**replacement) + ans = save_mainOp(ans, self) + return ans + + +# ----------------------------- +# Reglages pour 'vim' +# vim:set autoindent expandtab tabstop=4 shiftwidth=4: +# cursor: 16 del diff --git a/pymath/calculus/operator/mul.py b/pymath/calculus/operator/mul.py new file mode 100644 index 0000000..7ac8fb0 --- /dev/null +++ b/pymath/calculus/operator/mul.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python +# encoding: utf-8 + + +from .operator import Operator, save_mainOp + +class Mul(Operator): + + """ The operator * + + >>> mul = op.mul + >>> mul + '*' + >>> mul(1, 2) + 2 + >>> mul.__tex__('1','2') + '1 \\times 2' + >>> mul.__tex__('2','a') + '2 a' + >>> mul.__txt__('1','2') + '1 * 2' + >>> mul.__txt__('2','a') + '2 a' + >>> mul.__txt__('a','2') + 'a * 2' + >>> mul.__tex__('1','-2') + '1 \\times (-2)' + """ + + _CARACT = { + "operator" : "*", \ + "name" : "mul",\ + "priority" : 4, \ + "arity" : 2, \ + "actions" : ("__mul__","__rmul__"), \ + "txt" : "{op1} * {op2}",\ + "tex" : "{op1} \\times {op2}",\ + } + + def __init__(self): + """ Initiate Mul Operator """ + super(Mul, self).__init__(**self._CARACT) + + # TODO: Add self.visibility |sam. nov. 8 17:00:08 CET 2014 + self.visibility = 1 + + def is_visible(self, op1, op2): + """ Tells whether self has to be visible or not + + :param op1: left operande + :param op2: rigth operande + + """ + # TODO: À finir!!! |lun. mars 9 00:03:40 CET 2015 + if type(op2) == int: + # op2 est maintenant une chaine de caractères + return True + elif op2.isdecimal(): + return True + elif op2.isalpha(): + return False + elif op2[0].isdecimal(): + return True + elif op2[0] == "(" and not ("+" in op2 or "-" in op2[3:]): + return True + # Giga bricolage... + elif "frac" in op2: + return True + else: + return False + + def _render(self, link, *args): + + op1 = self.l_parenthesis(args[0], True) + op2 = self.r_parenthesis(args[1], True) + + if not self.is_visible(op1, op2): + ans = "{op1} {op2}".format(op1 = op1, op2 = op2) + else: + ans = link.format(op1 = op1, op2 = op2) + + ans = save_mainOp(ans, self) + return ans + + + + + +# ----------------------------- +# Reglages pour 'vim' +# vim:set autoindent expandtab tabstop=4 shiftwidth=4: +# cursor: 16 del diff --git a/pymath/calculus/operator/operator.py b/pymath/calculus/operator/operator.py new file mode 100644 index 0000000..0efd1d9 --- /dev/null +++ b/pymath/calculus/operator/operator.py @@ -0,0 +1,236 @@ +#!/usr/bin/env python +# encoding: utf-8 + +#from debug.tools import report + +from ..generic import flatten_list, isNumber +from functools import wraps +import types + +class Operator(): + + """The operator class, is a string (representation of the operator) with its arity""" + + def __init__(self, operator = "", name = "", priority = 0, actions = ("",""), txt = "", tex = "", arity = 2, **kwrds): + """ Create an Operator """ + self.operator = operator + self.name = name + self.arity = arity + self.priority = priority + self.actions = actions + self.txt = txt + self.tex = tex + + self.isOperator = 1 + + def __call__(self, *args): + """ Calling this operator performs the rigth calculus """ + if self.arity == 1: + return getattr(args[0], self.actions)() + + elif self.arity == 2: + #if type(args[1]) == int: + if issubclass(type(args[1]),int): + return getattr(args[0], self.actions[0])(args[1]) + else: + return getattr(args[1], self.actions[1])(args[0]) + + def _render(self, link, *args): + """Global step for __txt__ and __tex__ + + :param link: the link between operators + :param *args: the operands + :returns: the string with operator and operands + + """ + if self.arity == 1: + op1 = self.l_parenthesis(args[0], True) + ans = link.format(op1 = op1) + + elif self.arity == 2: + op1 = self.l_parenthesis(args[0], True) + op2 = self.r_parenthesis(args[1], True) + ans = link.format(op1 = op1, op2 = op2) + + ans = save_mainOp(ans, self) + return ans + + def __repr__(self): + return self.operator + + def __str__(self): + return str(self.operator) + + def __txt__(self, *args): + """Txt rendering for the operator + + :*args: Operands for this operation + :returns: String with operator and his operands + + >>> op.mul.__txt__('1','2') + '1 * 2' + >>> op.add.__txt__('1','2') + '1 + 2' + >>> f = save_mainOp('2 + 3',op.add) + >>> op.mul.__txt__(f, '4') + '( 2 + 3 ) * 4' + >>> f = save_mainOp('-3',op.sub1) + >>> op.sub1.__txt__(f) + '- ( -3 )' + >>> op.sub1.__txt__('-3') + '- ( -3 )' + >>> f = save_mainOp('2 + 3',op.add) + >>> op.sub1.__txt__(f) + '- ( 2 + 3 )' + """ + return self._render(self.txt, *args) + + def __tex__(self, *args): + """Tex rendering for the operator + + :*args: Operands for this operation + :returns: String with operator and his operands + + >>> op.mul.__tex__('1','2') + '1 \\\\times 2' + >>> op.add.__tex__('1','2') + '1 + 2' + >>> f = save_mainOp('2 + 3',op.add) + >>> op.mul.__tex__(f, '4') + '( 2 + 3 ) \\\\times 4' + >>> f = save_mainOp('-3',op.sub1) + >>> op.sub1.__tex__(f) + '- ( -3 )' + >>> op.sub1.__tex__('-3') + '- ( -3 )' + >>> f = save_mainOp('2 + 3',op.add) + >>> op.sub1.__tex__(f) + '- ( 2 + 3 )' + """ + return self._render(self.tex, *args) + + def __p2i__(self, *args): + """Fix list transformation for the operator + + :*args: Operands for this operation + :returns: list with the operator surrounded by operands + + >>> op.mul.__p2i__(1,2) + [1, '*', 2] + >>> f = save_mainOp([2, op.add, 3],op.add) + >>> op.mul.__p2i__(f, 4) + ['(', 2, '+', 3, ')', '*', 4] + >>> f = save_mainOp([op.sub1, 3],op.sub1) + >>> op.sub1.__p2i__(f) + ['-', '(', '-', 3, ')'] + >>> op.sub1.__p2i__(-3) + ['-', '(', -3, ')'] + >>> f = save_mainOp([2, op.add, 3],op.add) + >>> op.sub1.__p2i__(f) + ['-', '(', 2, '+', 3, ')'] + """ + if self.arity == 1: + op1 = self.l_parenthesis(args[0]) + ans = flatten_list([self, op1]) + + elif self.arity == 2: + op1 = self.l_parenthesis(args[0]) + op2 = self.r_parenthesis(args[1]) + ans = flatten_list([op1, self, op2]) + + ans = save_mainOp(ans, self) + return ans + + def l_parenthesis(self, opl, str_join=False): + """ Add parenthesis for left operand if necessary """ + ans = opl + try: + # TODO: Je pige pas pourquoi quand on enlève .name ça marche plus... |lun. avril 27 19:07:24 CEST 2015 + if opl.mainOp.name == "sub1": + ans = opl + elif opl.mainOp.priority < self.priority: + ans = flatten_list(["(", opl, ")"]) + except AttributeError as e: + # op has not the attribute priority + pass + + ans = flatten_list([ans]) + if str_join: + ans = ' '.join([str(i) for i in ans]) + return ans + + def r_parenthesis(self, op, str_join=False): + """ Add parenthesis for rigth operand if necessary """ + try: + if op.mainOp.priority < self.priority: + op = flatten_list(["(", op, ")"]) + except AttributeError: + # op has not the attribute priority + try: + if int(op) < 0: + op = ['(', op, ')'] + except ValueError: + pass + ans = flatten_list([op]) + if str_join: + ans = ' '.join([str(i) for i in ans]) + return ans + + def uniq_desc(self): + """ Return the uniq description of the operator + :returns: (self.name, self.arity) + + """ + return (self.operator, self.arity) + + def __eq__(self, op2): + """ op1 == op2 """ + return self.name == op2.name and self.arity == op2.arity + +def save_mainOp(obj, mainOp): + """Create a temporary class build over built-in type to stock the main operation of a calculus + + :obj: the object to add the attribute + :mainOp: the main operator + :returns: the same object with the main operation attribute + """ + # TODO: À mettre avec render? |mar. févr. 23 09:45:22 EAT 2016 + # TODO: On pourrait mettre un try avant de créer une nouvelle classe |mar. févr. 23 09:44:45 EAT 2016 + Fake = type('fake_'+str(type(obj)), (type(obj),), {'mainOp': mainOp}) + + return Fake(obj) + + +if __name__ == '__main__': + #print(op.add.__tex__('1','2')) + #print(op.mul.__tex__('1','2')) + #print(op.sub.__tex__('1','2')) + #f = save_mainOp('2 + 3',op.add) + #print(op.mul.__txt__(f, '4')) + #f = save_mainOp('-3',op.sub1) + #print(op.sub1.__txt__(f)) + #print(op.sub1.__txt__('-3')) + #f = save_mainOp('2 + 3',op.add) + #print(op.sub1.__txt__(f)) + + #from .fraction import Fraction + #f = Fraction(1, 2) + #print(op.add.__txt__(f.__txt__(),'2')) + #print(op.add.__tex__(f.__tex__(),'2')) + + #print("\t op.can_be_operator('+') :" + str(op.can_be_operator('+'))) + #print("\t op.can_be_operator('t') :" + str(op.can_be_operator('t'))) + + print("op.sub.__dict__ -> ", op.sub.__dict__) + print(op.sub == op.sub1) + #import doctest + #doctest.testmod() + + + + + +# ----------------------------- +# Reglages pour 'vim' +# vim:set autoindent expandtab tabstop=4 shiftwidth=4: +# cursor: 16 del diff --git a/pymath/calculus/operator/operator_set.py b/pymath/calculus/operator/operator_set.py new file mode 100644 index 0000000..2a42cfb --- /dev/null +++ b/pymath/calculus/operator/operator_set.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python +# encoding: utf-8 + + +import types +from .add import Add +from .div import Div +from .mul import Mul +from .par import Par +from .pw import Pw +from .sub import Sub +from .sub1 import Sub1 + + +class Operator_set(object): + """ Class for sets of operators""" + + def __init__(self): + """ Initiate the operator_set """ + self._operators = {} + + def get_op(self, op, arity = 2): + """Return the corresponding operator + + :op: symbole of the op + :arity: the arity + + >>> op = Operator_set() + >>> op.get_op('+') + '+' + >>> mul = op.get_op('*') + >>> mul.tex + '{op1} \\\\times {op2}' + >>> mul.txt + '{op1} * {op2}' + """ + try: + return getattr(self, self._operators[(op, arity)]) + except KeyError: + raise KeyError("{theOp} (arity: {arity}) is not available".format(theOp = op, arity = arity)) + + def can_be_operator(cls, symbole): + """ Tell if the symbole can be an operator """ + if type(symbole) == str: + return symbole in [i[0] for i in cls._operators] + else: + return False + + def store_operator(self, operator): + """ Save the operator as a method and + + :param operator: the operator (the class) (it will be accessible through .name method name. + """ + # TODO: faire une vérif si on peut utiliser operator_name |mar. févr. 23 09:09:44 EAT 2016 + self._operators[operator.uniq_desc()] = operator.name + setattr(self, operator.name, operator) + + +op = Operator_set() +op.store_operator(Add()) +op.store_operator(Div()) +op.store_operator(Mul()) +op.store_operator(Par()) +op.store_operator(Pw()) +op.store_operator(Sub()) +op.store_operator(Sub1()) + + + + + + + + + + + +# ----------------------------- +# Reglages pour 'vim' +# vim:set autoindent expandtab tabstop=4 shiftwidth=4: +# cursor: 16 del diff --git a/pymath/calculus/operator/par.py b/pymath/calculus/operator/par.py new file mode 100644 index 0000000..4977c58 --- /dev/null +++ b/pymath/calculus/operator/par.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python +# encoding: utf-8 + + +from .operator import Operator + +class Par(Operator): + + """ The operator ( """ + + _CARACT = { + "operator" : "(", \ + "name" : "par",\ + "priority" : 0, \ + "arity" : 0, \ + } + + def __init__(self): + """ Initiate Par Operator """ + super(Par, self).__init__(**self._CARACT) + + + + +# ----------------------------- +# Reglages pour 'vim' +# vim:set autoindent expandtab tabstop=4 shiftwidth=4: +# cursor: 16 del diff --git a/pymath/calculus/operator/pw.py b/pymath/calculus/operator/pw.py new file mode 100644 index 0000000..b98103a --- /dev/null +++ b/pymath/calculus/operator/pw.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python +# encoding: utf-8 + + +from .operator import Operator +from ..generic import flatten_list + +class Pw(Operator): + + """ The operator ^ + + >>> pw = op.pw + >>> pw + '^' + >>> pw(2, 3) + 8 + >>> pw.__tex__('2','3') + '2^{ 3 }' + >>> pw.__txt__('2','3') + '2 ^ 3' + >>> pw.__txt__('-2','3') + '( -2 ) ^ 3' + """ + + _CARACT = { + "operator" : "^", \ + "name" : "pw",\ + "priority" : 6, \ + "arity" : 2, \ + "actions" : ("__pow__",""), \ + "txt" : "{op1} ^ {op2}",\ + "tex" : "{op1}^{{ {op2} }}",\ + } + + def __init__(self): + """ Initiate Pw Operator """ + super(Pw, self).__init__(**self._CARACT) + + def __call__(self, op1, op2): + """ Calling this operator performs the rigth calculus """ + return getattr(op1, "__pow__")(op2) + + def l_parenthesis(self, opl, str_join=False): + """ Add parenthesis for left operand if necessary """ + ans = opl + try: + if opl.mainOp.priority < self.priority: + ans = flatten_list(["(", opl, ")"]) + except AttributeError as e: + # op has not the attribute priority + pass + try: + if int(opl) < 0: + ans = ["(", opl, ")"] + except ValueError: + pass + + ans = flatten_list([ans]) + if str_join: + ans = ' '.join([str(i) for i in ans]) + return ans + + +# ----------------------------- +# Reglages pour 'vim' +# vim:set autoindent expandtab tabstop=4 shiftwidth=4: +# cursor: 16 del diff --git a/pymath/calculus/operator/sub.py b/pymath/calculus/operator/sub.py new file mode 100644 index 0000000..43d46ef --- /dev/null +++ b/pymath/calculus/operator/sub.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python +# encoding: utf-8 + + +from .operator import Operator +from ..generic import flatten_list + +class Sub(Operator): + + """ The operator - + + >>> sub = op.sub + >>> sub + '-' + >>> sub(1, 2) + -1 + >>> sub.__tex__('1','2') + '1 - 2' + >>> sub.__txt__('1','2') + '1 - 2' + >>> sub.__tex__('1','-2') + '1 - (-2)' + >>> sub.__tex__('-1','2') + '-1 - 2' + """ + _CARACT = { + "operator" : "-", \ + "name" : "sub",\ + "priority" : 2, \ + "arity" : 2, \ + "actions" : ("__sub__","__rsub__"), \ + "txt" : "{op1} - {op2}",\ + "tex" : "{op1} - {op2}",\ + } + + def __init__(self): + """ Initiate Sub Operator """ + super(Sub, self).__init__(**self._CARACT) + + def l_parenthesis(self, op, str_join=False): + return op + + def r_parenthesis(self, op, str_join=False): + try: + if op.mainOp.priority <= self.priority: + op = flatten_list(["(", op, ")"]) + elif op[0] == '-': + op = flatten_list(["(", op, ")"]) + except AttributeError: + # op has not the attribute priority + try: + if int(op) < 0: + op = ['(', op, ')'] + except ValueError: + pass + ans = flatten_list([op]) + if str_join: + ans = ' '.join([str(i) for i in ans]) + return ans + + + + + + + +# ----------------------------- +# Reglages pour 'vim' +# vim:set autoindent expandtab tabstop=4 shiftwidth=4: +# cursor: 16 del diff --git a/pymath/calculus/operator/sub1.py b/pymath/calculus/operator/sub1.py new file mode 100644 index 0000000..dcd8300 --- /dev/null +++ b/pymath/calculus/operator/sub1.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python +# encoding: utf-8 + + +from .operator import Operator +from ..generic import flatten_list + +class Sub1(Operator): + + """ The operator - + + >>> sub1 = op.sub1 + >>> sub1 + '-' + >>> sub1(1) + -1 + >>> sub1.__tex__('1') + '- 1' + >>> sub1.__txt__('1') + '- 1' + >>> sub1.__tex__('-1') + '- (-1)' + """ + + _CARACT = { + "operator" : "-", \ + "name" : "sub1",\ + "priority" : 3, \ + "arity" : 1, \ + "actions" : "__neg__",\ + "txt" : "- {op1}",\ + "tex" : "- {op1}",\ + } + + def __init__(self): + """ Initiate Sub1 Operator """ + super(Sub1, self).__init__(**self._CARACT) + + def l_parenthesis(self, op, str_join=False): + """ Add parenthesis if necessary """ + try: + if op.mainOp.priority <= self.priority: + op = flatten_list(["("] + [op] + [")"]) + except AttributeError: + # op has not the attribute priority + try: + if int(op) < 0: + op = ['(', op, ')'] + except ValueError: + pass + + ans = flatten_list([op]) + if str_join: + ans = ' '.join([str(i) for i in ans]) + return ans + + +# ----------------------------- +# Reglages pour 'vim' +# vim:set autoindent expandtab tabstop=4 shiftwidth=4: +# cursor: 16 del diff --git a/pymath/calculus/operator/test/test_add.py b/pymath/calculus/operator/test/test_add.py new file mode 100644 index 0000000..4d62688 --- /dev/null +++ b/pymath/calculus/operator/test/test_add.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python +# encoding: utf-8 + + +from pymath.calculus.operator.add import Add + + +def test_add_render_tex(): + assert Add().__tex__('1', '2') == '1 + 2' + assert Add().__tex__('1', '-2') == '1 - 2' + + +def test_add_render_txt(): + assert Add().__txt__('1', '2') == '1 + 2' + assert Add().__txt__('1', '-2') == '1 - 2' + + +# ----------------------------- +# Reglages pour 'vim' +# vim:set autoindent expandtab tabstop=4 shiftwidth=4: +# cursor: 16 del diff --git a/pymath/calculus/test/test_operator.py b/pymath/calculus/operator/test/test_operator.py similarity index 100% rename from pymath/calculus/test/test_operator.py rename to pymath/calculus/operator/test/test_operator.py