Feat: to_be_token decorator and force token to be Integer when it's

possible
This commit is contained in:
Bertrand Benjamin 2019-07-17 09:49:50 +02:00
parent d75fd4c4cf
commit 204fdf755b
3 changed files with 62 additions and 21 deletions

View File

@ -15,10 +15,12 @@ from ...core.MO.fraction import MOFraction
from ...core.MO.monomial import MOstrPower, MOMonomial from ...core.MO.monomial import MOstrPower, MOMonomial
from ...core.MO.polynomial import MOpolynomial from ...core.MO.polynomial import MOpolynomial
from decimal import Decimal as _Decimal from decimal import Decimal as _Decimal
from functools import wraps
from .token import Token from .token import Token
__all__ = ["factory"] __all__ = ["factory"]
def tokenify(mo, name="", ancestor=None): def tokenify(mo, name="", ancestor=None):
""" Transform a MO or a python builtin to the appropriate token """ Transform a MO or a python builtin to the appropriate token
@ -51,11 +53,33 @@ def tokenify(mo, name="", ancestor=None):
""" """
if isinstance(mo, MO): if isinstance(mo, MO):
return _tokenify(mo, name, ancestor) return _tokenify(mo, name, ancestor)
elif isinstance(mo, Token):
return _tokenify(mo._mo, name, ancestor) if isinstance(mo, Token):
if name == "":
_name = mo.name
else:
_name = name
if ancestor is None:
_ancestor = mo._ancestor
else:
_ancestor = ancestor
return _tokenify(mo._mo, _name, _ancestor)
return _tokenify(moify(mo), name, ancestor) return _tokenify(moify(mo), name, ancestor)
def to_be_token(func):
""" Decorator to ensure that the return value is a Token """
@wraps(func)
def wrapped(*args, **kwds):
ans = func(*args, **kwds)
try:
return [tokenify(t) for t in ans]
except TypeError:
return tokenify(ans)
return wrapped
def _tokenify(mo, name="", ancestor=None): def _tokenify(mo, name="", ancestor=None):
""" Transform a MO (from core) to the appropriate token (from API) """ Transform a MO (from core) to the appropriate token (from API)
@ -91,25 +115,28 @@ def _tokenify(mo, name="", ancestor=None):
if isinstance(mo, MOnumber): if isinstance(mo, MOnumber):
if isinstance(mo.value, int): if isinstance(mo.value, int):
from .number import Integer from .number import Integer
return Integer.from_mo(mo, name, ancestor) return Integer.from_mo(mo, name, ancestor)
elif isinstance(mo.value, _Decimal): elif isinstance(mo.value, _Decimal):
from .number import Decimal from .number import Decimal
return Decimal.from_mo(mo, name, ancestor) return Decimal.from_mo(mo, name, ancestor)
raise TypeError(f"Can't build from MOnumber ({mo}) neither int nor decimal") raise TypeError(f"Can't build from MOnumber ({mo}) neither int nor decimal")
elif isinstance(mo, MOFraction): if isinstance(mo, MOFraction):
if isinstance(mo._denominator, MOnumber) and isinstance( if isinstance(mo._denominator, MOnumber) and isinstance(
mo._numerator, MOnumber mo._numerator, MOnumber
): ):
from .number import Fraction from .number import Fraction
return Fraction.from_mo(mo, name, ancestor) return Fraction.from_mo(mo, name, ancestor)
raise TypeError( raise TypeError(
f"Can't build from MOFraction ({mo}) numerator and denominator are not MOnumber" f"Can't build from MOFraction ({mo}) numerator and denominator are not MOnumber"
) )
elif isinstance(mo, (MOstr, MOstrPower, MOMonomial, MOpolynomial)): if isinstance(mo, (MOstr, MOstrPower, MOMonomial, MOpolynomial)):
if not isinstance(mo._variable, (MOstr, str)): if not isinstance(mo._variable, (MOstr, str)):
raise TypeError( raise TypeError(
f"Can't build Polynom over something else than a letter (got {mo._variable})" f"Can't build Polynom over something else than a letter (got {mo._variable})"
@ -120,6 +147,7 @@ def _tokenify(mo, name="", ancestor=None):
or (isinstance(mo, MOpolynomial) and mo.power.value == 1) or (isinstance(mo, MOpolynomial) and mo.power.value == 1)
): ):
from .polynomial import Linear from .polynomial import Linear
return Linear.from_mo(mo, name, ancestor) return Linear.from_mo(mo, name, ancestor)
elif ( elif (
(isinstance(mo, MOstrPower) and mo.power.value == 2) (isinstance(mo, MOstrPower) and mo.power.value == 2)
@ -127,16 +155,16 @@ def _tokenify(mo, name="", ancestor=None):
or (isinstance(mo, MOpolynomial) and mo.power.value == 2) or (isinstance(mo, MOpolynomial) and mo.power.value == 2)
): ):
from .polynomial import Quadratic from .polynomial import Quadratic
return Quadratic.from_mo(mo, name, ancestor) return Quadratic.from_mo(mo, name, ancestor)
else: else:
from .polynomial import Polynomial from .polynomial import Polynomial
return Polynomial.from_mo(mo, name, ancestor) return Polynomial.from_mo(mo, name, ancestor)
else:
raise TypeError(f"{type(mo)} is unknown MathObject") raise TypeError(f"{type(mo)} is unknown MathObject")
def factory(exp, name="", ancestor=None): def factory(exp, name="", ancestor=None):
""" Transform a Expression with on single MathObject (from core) to a appropriate token (from API) """ Transform a Expression with on single MathObject (from core) to a appropriate token (from API)

View File

@ -12,6 +12,7 @@ Tokens representing polynomials functions
""" """
from ..expression import Expression from ..expression import Expression
from .token import Token from .token import Token
from . import to_be_token
from ...core.MO import MO from ...core.MO import MO
from ...core.MO.atoms import moify from ...core.MO.atoms import moify
@ -60,6 +61,7 @@ class Polynomial(Token):
""" Use Polynomial like if they were a dictionnary to set coefficients """ """ Use Polynomial like if they were a dictionnary to set coefficients """
raise NotImplementedError("Can't set coefficient of a polynomial") raise NotImplementedError("Can't set coefficient of a polynomial")
@to_be_token
def __getitem__(self, key): def __getitem__(self, key):
""" Use Polynomial like if they were a dictionnary to get coefficients """ Use Polynomial like if they were a dictionnary to get coefficients
@ -67,11 +69,11 @@ class Polynomial(Token):
>>> from ...core.MO.polynomial import MOpolynomial >>> from ...core.MO.polynomial import MOpolynomial
>>> P = Polynomial(MOpolynomial('x', [1, 2, 3])) >>> P = Polynomial(MOpolynomial('x', [1, 2, 3]))
>>> P[0] >>> P[0]
<MOnumber 1> <Integer 1>
>>> P[1] >>> P[1]
<MOnumber 2> <Integer 2>
>>> P[2] >>> P[2]
<MOnumber 3> <Integer 3>
>>> P[3] >>> P[3]
Traceback (most recent call last): Traceback (most recent call last):
... ...
@ -128,9 +130,9 @@ class Linear(Polynomial):
>>> P >>> P
<Linear 2x + 1> <Linear 2x + 1>
>>> P.a >>> P.a
<MOnumber 2> <Integer 2>
>>> P.b >>> P.b
<MOnumber 1> <Integer 1>
>>> P.differentiate() >>> P.differentiate()
<Integer 2> <Integer 2>
>>> P.roots >>> P.roots
@ -158,14 +160,17 @@ class Linear(Polynomial):
raise NotImplementedError raise NotImplementedError
@property @property
@to_be_token
def a(self): def a(self):
return self[1] return self[1]
@property @property
@to_be_token
def b(self): def b(self):
return self[0] return self[0]
@property @property
@to_be_token
def roots(self): def roots(self):
""" Get the root of the polynomial """ Get the root of the polynomial
@ -194,11 +199,11 @@ class Quadratic(Polynomial):
>>> P >>> P
<Quadratic 3x^2 + 2x + 1> <Quadratic 3x^2 + 2x + 1>
>>> P.a >>> P.a
<MOnumber 3> <Integer 3>
>>> P.b >>> P.b
<MOnumber 2> <Integer 2>
>>> P.c >>> P.c
<MOnumber 1> <Integer 1>
>>> P.delta >>> P.delta
<Integer - 8> <Integer - 8>
>>> for s in P.delta.explain(): >>> for s in P.delta.explain():
@ -231,6 +236,7 @@ class Quadratic(Polynomial):
raise NotImplementedError raise NotImplementedError
@property @property
@to_be_token
def a(self): def a(self):
try: try:
return self[2] return self[2]
@ -238,6 +244,7 @@ class Quadratic(Polynomial):
return 0 return 0
@property @property
@to_be_token
def b(self): def b(self):
try: try:
return self[1] return self[1]
@ -245,6 +252,7 @@ class Quadratic(Polynomial):
return 0 return 0
@property @property
@to_be_token
def c(self): def c(self):
try: try:
return self[0] return self[0]
@ -252,10 +260,12 @@ class Quadratic(Polynomial):
return 0 return 0
@property @property
@to_be_token
def delta(self): def delta(self):
return Expression.from_str(f"{self.b}^2-4*{self.a}*{self.c}").simplify() return Expression.from_str(f"{self.b}^2-4*{self.a}*{self.c}").simplify()
@property @property
@to_be_token
def roots(self): def roots(self):
""" Roots of the polynom """ Roots of the polynom
@ -266,10 +276,10 @@ class Quadratic(Polynomial):
[] []
>>> P = Quadratic(MOpolynomial('x', [4, -4, 1])) >>> P = Quadratic(MOpolynomial('x', [4, -4, 1]))
>>> P.roots >>> P.roots
[2.0] [<Integer 2>]
>>> P = Quadratic(MOpolynomial('x', [1, 0, -1])) >>> P = Quadratic(MOpolynomial('x', [1, 0, -1]))
>>> P.roots >>> P.roots
[-1.0, 1.0] [<Integer - 1>, <Integer 1>]
""" """
if self.delta._mo < 0: if self.delta._mo < 0:
return [] return []

View File

@ -123,10 +123,13 @@ class MOnumber(Atom):
""" """
if isinstance(value, Atom) and isinstance(value.value, (int, Decimal, float)): if isinstance(value, Atom) and isinstance(value.value, (int, Decimal, float)):
Atom.__init__(self, value.value) Atom.__init__(self, value.value)
elif isinstance(value, (int, Decimal)): elif isinstance(value, (float, Decimal)):
Atom.__init__(self, value) if int(value) == value:
elif isinstance(value, float): Atom.__init__(self, int(value))
else:
Atom.__init__(self, Decimal(value)) Atom.__init__(self, Decimal(value))
elif isinstance(value, int):
Atom.__init__(self, value)
else: else:
try: try:
float(value) float(value)
@ -165,7 +168,7 @@ class MOnumber(Atom):
>>> MOnumber(-3).__tex__ >>> MOnumber(-3).__tex__
'- 3' '- 3'
""" """
if self.value > 0: if self.value >= 0:
return str(self.value) return str(self.value)
return f"- {abs(self.value)}" return f"- {abs(self.value)}"