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 419e5955eb
commit 219d923ff5
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.polynomial import MOpolynomial
from decimal import Decimal as _Decimal
from functools import wraps
from .token import Token
__all__ = ["factory"]
def tokenify(mo, name="", ancestor=None):
""" 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):
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)
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):
""" 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.value, int):
from .number import Integer
return Integer.from_mo(mo, name, ancestor)
elif isinstance(mo.value, _Decimal):
from .number import Decimal
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, MOFraction):
if isinstance(mo._denominator, MOnumber) and isinstance(
mo._numerator, MOnumber
):
from .number import Fraction
return Fraction.from_mo(mo, name, ancestor)
raise TypeError(
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)):
raise TypeError(
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)
):
from .polynomial import Linear
return Linear.from_mo(mo, name, ancestor)
elif (
(isinstance(mo, MOstrPower) and mo.power.value == 2)
@ -127,14 +155,14 @@ def _tokenify(mo, name="", ancestor=None):
or (isinstance(mo, MOpolynomial) and mo.power.value == 2)
):
from .polynomial import Quadratic
return Quadratic.from_mo(mo, name, ancestor)
else:
from .polynomial import Polynomial
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):

View File

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

View File

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