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

264 lines
6.3 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.tree import Tree
class MOError(Exception):
pass
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 self.__txt__
@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 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>
>>> MOstr("ui")
Traceback (most recent call last):
...
mo.MOError: An MOstr should be initiate with a single caracter string
>>> MOstr(2)
Traceback (most recent call last):
...
mo.MOError: An MOstr should be initiate with a string - the unknown
"""
try:
val = value.value
except AttributeError:
val = value
if not isinstance(val, str):
raise MOError("An MOstr should be initiate with a string - the unknown")
if len(val) != 1:
raise MOError("An MOstr should be initiate with a single caracter string")
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)
class MOFraction(MO):
""" Fraction math object"""
def __init__(self, numerator, denominator, negative=False):
""" Initiate the MOFraction
It can't be indempotent.
:param numerator: Numerator of the Fraction
:param denominator: Denominator of the Fraction
:param negative: Is the fraction negative (not concidering sign of
numerator or denominator.
>>> f = MOFraction(2, 3)
>>> f
<MOFraction 2 / 3>
>>> f = MOFraction(2, 3, negative = True)
>>> f
<MOFraction - 2 / 3>
"""
value = Tree("/",
MO.factory(numerator),
MO.factory(denominator),
)
MO.__init__(self, value)
self._numerator = numerator
self._denominator = denominator
self.negative = negative
@property
def __txt__(self):
# TODO: fonctionnement temporaire. Il faudrait utilser un moteur de rendu plus fin. |jeu. mars 8 15:26:49 EAT 2018
try:
numerator = self._numerator.__txt__
except AttributeError:
numerator = str(self._numerator)
try:
denominator = self._denominator.__txt__
except AttributeError:
denominator = str(self._denominator)
return "- "*self.negative + f"{numerator} / {denominator}"
def __add__(self, other):
""" Overload * for MOFraction
:param other: any other MO
:yields: calculus steps and the final yielded value is a MO.
>>> f = MOFraction(1, 2)
>>> g = MOFraction(3, 2)
"""
raise NotImplemented
def __mul__(self, other):
raise NotImplemented
def __truediv__(self, other):
raise NotImplemented
# -----------------------------
# Reglages pour 'vim'
# vim:set autoindent expandtab tabstop=4 shiftwidth=4:
# cursor: 16 del