Mapytex/mapytex/calculus/core/MO/mo.py

277 lines
6.7 KiB
Python

#! /usr/bin/env python
# -*- coding: utf-8 -*-
# vim:fenc=utf-8
#
# Copyright © 2017 lafrite <lafrite@Poivre>
#
# Distributed under terms of the MIT license.
from mapytex.calculus.core.coroutine import coroutine, STOOOP
from decimal import Decimal
__all__ = ["moify", "MO", "MOstr"]
class MOError(Exception):
pass
@coroutine
def moify(target):
""" Coroutine which try to convert everything into an MO """
try:
target_ = target()
except TypeError:
target_ = target
try:
while True:
tok = yield
try:
target_.send(MOnumber(tok))
except MOError:
try:
target_.send(MOstr(tok))
except MOError:
target_.send(tok)
except STOOOP as err:
yield target_.throw(err)
class MO(object):
"""MO for math object
This base class is representing int and Decimal. It stocks its value in
self.value and it
"""
def __init__(self, value):
""" Initiate the MO
It should be idempotent.
>>> a = MO(3)
>>> a
<MO 3>
>>> a = MO(a)
>>> a
<MO 3>
"""
try:
self.value = value.value
except AttributeError:
self.value = value
self.is_scalar = True
@classmethod
def factory(cls, value):
""" Factory to ensure that a value is a MO before using it """
if isinstance(value, MO):
return value
elif isinstance(value, str):
return MOstr(value)
return MO(value)
def __repr__(self):
return f"<{self.__class__.__name__} {self.__txt__}>"
def __str__(self):
return str(self.value)
@property
def __txt__(self):
return str(self.value)
def __add__(self, other):
""" Overload + for MOs
>>> from decimal import Decimal
>>> a = MO(4)
>>> b = MO(Decimal("1.2"))
>>> a + b
<MO 5.2>
>>> b + a
<MO 5.2>
"""
return MO.factory(self.value + other.value)
def __mul__(self, other):
""" Overload * for MOs
>>> from decimal import Decimal
>>> a = MO(4)
>>> b = MO(Decimal("1.2"))
>>> a * b
<MO 4.8>
>>> b * a
<MO 4.8>
"""
return MO.factory(self.value * other.value)
def __truediv__(self, other):
""" Overload / for MOs
>>> from decimal import Decimal
>>> a = MO(4)
>>> b = MO(Decimal("1.4"))
>>> c = b / a
>>> c
<MO 0.35>
>>> type(c.value)
<class 'decimal.Decimal'>
>>> c = a / b
>>> c
<MO 2.857142857142857142857142857>
>>> type(c.value)
<class 'decimal.Decimal'>
"""
return MO.factory(self.value / other.value)
def __neg__(self):
""" Overload + for MOs
>>> from decimal import Decimal
>>> a = MO(4)
>>> - a
<MO -4>
>>> b = MO(Decimal("1.2"))
>>> - b
<MO -1.2>
"""
return MO.factory(-self.value)
class MOnumber(MO):
""" Base number math object (int or Decimal) """
def __init__(self, value, negative=False):
""" Initiate a number MO
>>> MOnumber(23)
<MOnumber 23>
>>> MOnumber(-23)
<MOnumber - 23>
>>> MOnumber(23.3)
<MOnumber 23.300000000000000710542735760100185871124267578125>
>>> MOnumber(Decimal("23.3"))
<MOnumber 23.3>
>>> MOnumber(Decimal("-23.3"))
<MOnumber - 23.3>
>>> a = MOnumber(23)
>>> MOnumber(a)
<MOnumber 23>
>>> MOnumber("a")
Traceback (most recent call last):
...
mapytex.calculus.core.MO.mo.MOError: The value of an MOnumber need to be a int, a float or a Decimal
"""
try:
val = value.value
except AttributeError:
val = value
if isinstance(val, int) or isinstance(val, Decimal):
MO.__init__(self, value)
elif isinstance(val, float):
MO.__init__(self, Decimal(val))
else:
raise MOError("The value of an MOnumber need to be a int, a float or a Decimal")
@property
def __txt__(self):
if self.value > 0:
return str(self.value)
return f"- {abs(self.value)}"
class MOstr(MO):
""" Unknown math object like x or n"""
def __init__(self, value, negative=False):
""" Initiate a string MO
>>> a = MOstr("x")
>>> a
<MOstr x>
>>> a = MOstr("x", negative=True)
>>> a
<MOstr -x>
>>> b = MOstr(a)
>>> b
<MOstr x>
>>> a = MOstr("+")
Traceback (most recent call last):
...
mapytex.calculus.core.MO.mo.MOError: An MOstr should be initiate with a alpha string, got +
>>> MOstr("ui")
Traceback (most recent call last):
...
mapytex.calculus.core.MO.mo.MOError: An MOstr should be initiate with a single caracter string, got ui
>>> MOstr(2)
Traceback (most recent call last):
...
mapytex.calculus.core.MO.mo.MOError: An MOstr should be initiate with a string - the unknown, got 2
"""
try:
val = value.value
except AttributeError:
val = value
if not isinstance(val, str):
raise MOError(f"An MOstr should be initiate with a string - the unknown, got {val}")
if len(val) != 1:
raise MOError(f"An MOstr should be initiate with a single caracter string, got {val}")
if not val.isalpha():
raise MOError(f"An MOstr should be initiate with a alpha string, got {val}")
MO.__init__(self, value)
self.negative = negative
self.is_scalar = False
@property
def __txt__(self):
return "-"*self.negative + str(self.value)
def __add__(self, other):
raise NotImplemented
def __radd__(self, other):
raise NotImplemented
def __mul__(self, other):
if other.is_scalar:
raise NotImplemented
raise NotImplemented
def __rmul__(self, other):
if other.is_scalar:
raise NotImplemented
raise NotImplemented
def __truediv__(self, other):
if other.is_scalar:
raise NotImplemented
raise NotImplemented
def __rtruediv__(self, other):
if other.is_scalar:
raise NotImplemented
raise NotImplemented
def __neg__(self):
return MO(self.value, not self.negative)
# -----------------------------
# Reglages pour 'vim'
# vim:set autoindent expandtab tabstop=4 shiftwidth=4:
# cursor: 16 del