diff --git a/mapytex/calculus/API/expression.py b/mapytex/calculus/API/expression.py index abb0044..315b303 100644 --- a/mapytex/calculus/API/expression.py +++ b/mapytex/calculus/API/expression.py @@ -41,14 +41,38 @@ class Expression(object): self._ancestor = ancestor @classmethod - def from_str(cls, string): + def from_str(cls, string, typing=True): """ Initiate the expression from a string :param string: String to parse to generate the Expression :returns: the expression + :example: + >>> e = Expression.from_str("2 + 3 * 4") + >>> e + + >>> e = Expression.from_str("2/3") + >>> e + + >>> e = Expression.from_str("2x + 1") + >>> e + + >>> e = Expression.from_str("2x + 1 + 5x^2") + >>> e + + >>> e = Expression.from_str("2x + 1 + 5x") + >>> e + + """ t = Tree.from_str(string) + if typing: + tt = cls(t)._typing() + try: + return factory(tt) + except TypeError as e: + return cls(t) + return cls(t) @classmethod @@ -173,7 +197,7 @@ class Expression(object): """ Build a copy of self with as much typing as possible :example: - >>> e = Expression.from_str("2x") + >>> e = Expression.from_str("2x", typing=False) >>> print(e._tree.map_on_leaf(type)) * > @@ -185,7 +209,7 @@ class Expression(object): >>> print(type(typed_e._tree)) - >>> e = Expression.from_str("2x+3+4/5") + >>> e = Expression.from_str("2x+3+4/5", typing=False) >>> print(e._tree.map_on_leaf(type)) + > + diff --git a/mapytex/calculus/API/tokens/__init__.py b/mapytex/calculus/API/tokens/__init__.py index 2a0b4dd..d9a6b43 100644 --- a/mapytex/calculus/API/tokens/__init__.py +++ b/mapytex/calculus/API/tokens/__init__.py @@ -60,16 +60,16 @@ def factory(exp, name="", ancestor=None): if isinstance(mo, MOnumber): if isinstance(mo.value, int): - return Integer(mo, name, ancestor) + return Integer.from_mo(mo, name, ancestor) elif isinstance(mo.value, _Decimal): - return Decimal(mo, name, ancestor) + return Decimal.from_mo(mo, name, ancestor) raise TypeError(f"Can't build from MOnumber ({mo}) neither int nor decimal") elif isinstance(mo, MOFraction): if isinstance(mo._denominator, MOnumber) and \ isinstance(mo._numerator, MOnumber): - return Fraction(mo, name, ancestor) + return Fraction.from_mo(mo, name, ancestor) raise TypeError(f"Can't build from MOFraction ({mo}) numerator and denominator are not MOnumber") @@ -79,13 +79,13 @@ def factory(exp, name="", ancestor=None): if isinstance(mo, MOstr) or \ (isinstance(mo, MOMonomial) and mo.power.value == 1) or \ (isinstance(mo, MOpolynomial) and mo.power.value == 1): - return Linear(mo, name, ancestor) + return Linear.from_mo(mo, name, ancestor) elif (isinstance(mo, MOstrPower) and mo.power.value == 2) or \ (isinstance(mo, MOMonomial) and mo.power.value == 2) or \ (isinstance(mo, MOpolynomial) and mo.power.value == 2): - return Quadratic(mo, name, ancestor) + return Quadratic.from_mo(mo, name, ancestor) else: - return Polynomial(mo, name, ancestor) + return Polynomial.from_mo(mo, name, ancestor) else: raise TypeError(f"{type(mo)} is unknown MathObject") diff --git a/mapytex/calculus/API/tokens/number.py b/mapytex/calculus/API/tokens/number.py index 608cd4a..6cb497d 100644 --- a/mapytex/calculus/API/tokens/number.py +++ b/mapytex/calculus/API/tokens/number.py @@ -11,6 +11,7 @@ Tokens representing interger and decimal """ from .token import Token +from ...core.MO import MO from ...core.MO.mo import MOnumber from ...core.MO.fraction import MOFraction from decimal import Decimal as _Decimal @@ -19,17 +20,37 @@ __all__ = ["Integer", "Decimal"] class Integer(Token): - """ Token representing a integer """ + """ Token representing a integer + + :example: + >>> Integer(4) + + >>> a = MOnumber(4) + >>> Integer.from_mo(a) + + + """ - def __init__(self, mo, name="", ancestor=None): - if not isinstance(mo, MOnumber): - raise TypeError - if not isinstance(mo.value, int): - raise TypeError + def __init__(self, a, name="", ancestor=None): + if not isinstance(a, MO): + if not isinstance(a, int): + raise TypeError + mo = MOnumber(a) + else: + mo = a Token.__init__(self, mo, name, ancestor) self._mathtype = 'entier' + @classmethod + def from_mo(cls, mo, name="", ancestor=None): + if not isinstance(mo, MOnumber): + raise TypeError + if not isinstance(mo.value, int): + raise TypeError + + return cls(mo, name, ancestor) + @classmethod def random(cls): raise NotImplemented @@ -38,14 +59,26 @@ class Decimal(Token): """ Token representing a decimal """ - def __init__(self, mo, name="", ancestor=None): + def __init__(self, a, name="", ancestor=None): + if not isinstance(a, MO): + if isinstance(a, (str, float)): + mo = MOnumber(Decimal(a)) + else: + raise TypeError + else: + mo = a + + self._mathtype = 'décimal' + Token.__init__(self, mo, name, ancestor) + + @classmethod + def from_mo(cls, mo, name="", ancestor=None): if not isinstance(mo, MOnumber): raise TypeError if not isinstance(mo.value, _Decimal): raise TypeError - Token.__init__(self, mo, name, ancestor) - self._mathtype = 'décimal' + return cls(mo, name, ancestor) @classmethod def random(cls): @@ -55,7 +88,21 @@ class Fraction(Token): """ Token representing a fraction """ - def __init__(self, mo, name="", ancestor=None): + def __init__(self, a, name="", ancestor=None): + if not isinstance(a, MO): + if isinstance(a, str): + num, denom = a.split('/') + mo = MOFraction(int(num), int(denom)) + else: + raise TypeError + else: + mo = a + + self._mathtype = 'fraction' + Token.__init__(self, mo, name, ancestor) + + @classmethod + def from_mo(cls, mo, name="", ancestor=None): if not isinstance(mo, MOFraction): raise TypeError if not isinstance(mo._numerator, MOnumber): @@ -63,8 +110,7 @@ class Fraction(Token): if not isinstance(mo._denominator, MOnumber): raise TypeError - Token.__init__(self, mo, name, ancestor) - self._mathtype = 'fraction' + return cls(mo, name, ancestor) @classmethod def random(cls): diff --git a/mapytex/calculus/API/tokens/polynomial.py b/mapytex/calculus/API/tokens/polynomial.py index d93ba71..36e10fa 100644 --- a/mapytex/calculus/API/tokens/polynomial.py +++ b/mapytex/calculus/API/tokens/polynomial.py @@ -11,6 +11,7 @@ Tokens representing polynomials functions """ from .token import Token +from ...core.MO import MO __all__ = ["Polynomial", "Quadratic", "Linear"] @@ -18,11 +19,23 @@ class Polynomial(Token): """ Token representing a polynomial """ - def __init__(self, mo, name="", ancestor=None): + def __init__(self, a, name="", ancestor=None): + if not isinstance(a, MO): + if isinstance(a, str): + raise TypeError + else: + raise TypeError + else: + mo = a Token.__init__(self, mo, name, ancestor) self._mathtype = 'polynome' + @classmethod + def from_mo(cls, mo, name="", ancestor=None): + + return cls(mo, name, ancestor) + @classmethod def random(cls): raise NotImplemented @@ -39,26 +52,26 @@ class Polynomial(Token): """ Call a Polynomial to evaluate itself on value """ pass -class Linear(Token): +class Linear(Polynomial): """ Token representing a linear """ def __init__(self, mo, name="", ancestor=None): - Token.__init__(self, mo, name, ancestor) + Polynomial.__init__(self, mo, name, ancestor) self._mathtype = 'affine' @classmethod def random(cls): raise NotImplemented -class Quadratic(Token): +class Quadratic(Polynomial): """ Token representing a quadratic """ def __init__(self, mo, name="", ancestor=None): - Token.__init__(self, mo, name, ancestor) + Polynomial.__init__(self, mo, name, ancestor) self._mathtype = 'polynome du 2nd degré' @classmethod diff --git a/mapytex/calculus/API/tokens/token.py b/mapytex/calculus/API/tokens/token.py index 9aa481b..b89f037 100644 --- a/mapytex/calculus/API/tokens/token.py +++ b/mapytex/calculus/API/tokens/token.py @@ -63,6 +63,107 @@ class Token(object): def __tex__(self): return self._mo.__tex__ + def _operate(self, other, operation): + """ Make a operation between 2 Tokens, + a Token and en Expression ora + a Token an a builtin type + """ + from ..expression import Expression + from ...core import Tree + from . import factory + if not isinstance(other, Token): + _other = factory(other) + else: + _other = other + tree = Tree(operation, self._mo, _other._mo) + return Expression(tree).simplify() + + def __add__(self, other): + """ Adding 2 Tokens or a Token and a Expression + + :example: + >>> from .number import Integer + >>> a = Integer(3) + >>> b = Integer(7) + >>> c = a + b + >>> c + + >>> for i in c.explain(): + ... print(i) + 3 + 7 + 10 + >>> from .number import Fraction + >>> a = Fraction("4/3") + >>> b = Integer(7) + >>> c = a + b + >>> c + + >>> for i in c.explain(): + ... print(i) + 4 / 3 + 7 + 4 / 3 + 7 / 1 + 4 / 3 + (7 * 3) / (1 * 3) + 4 / 3 + 21 / 3 + (4 + 21) / 3 + 25 / 3 + """ + return self._operate(other, "+") + + def __mul__(self, other): + """ Multiply 2 Tokens or a Token and a Expression + + :example: + >>> from .number import Integer + >>> a = Integer(3) + >>> b = Integer(7) + >>> c = a * b + >>> c + + >>> for i in c.explain(): + ... print(i) + 3 * 7 + 21 + >>> from .number import Fraction + >>> a = Fraction("4/3") + >>> b = Integer(7) + >>> c = a * b + >>> c + + >>> for i in c.explain(): + ... print(i) + 4 / 3 * 7 + (4 * 7) / 3 + 28 / 3 + """ + return self._operate(other, "*") + + def __truediv__(self, other): + """ Divising 2 Tokens or a Token and a Expression + + :example: + >>> from .number import Integer + >>> a = Integer(3) + >>> b = Integer(7) + >>> c = a / b + >>> c + + >>> for i in c.explain(): + ... print(i) + 3 / 7 + >>> from .number import Fraction + >>> a = Fraction("4/3") + >>> b = Integer(7) + >>> c = a / b + >>> c + + >>> for i in c.explain(): + ... print(i) + 4 / 3 / 7 + 4 / 3 * 1 / 7 + (4 * 1) / (3 * 7) + 4 / 21 + """ + return self._operate(other, "/")