#! /usr/bin/env python # -*- coding: utf-8 -*- # vim:fenc=utf-8 # # Copyright © 2017 lafrite # # Distributed under terms of the MIT license. from decimal import Decimal from .exceptions import MOError from ..coroutine import coroutine, STOOOP from ..renders import tree2txt, tree2tex from functools import total_ordering __all__ = ["moify", "MO", "MOstr"] @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 >>> a = MO(a) >>> a """ 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 Idempotent?? >>> MO.factory("x") >>> MO.factory(2) >>> MO.factory(2.3) >>> MO.factory(Decimal("2.3")) >>> x = MO.factory("x") >>> MO.factory(x) """ if isinstance(value, str): return MOstr(value) elif isinstance(value, int) \ or isinstance(value, Decimal) \ or isinstance(value, float): return MOnumber(value) elif isinstance(value, MO): return value raise MOError("Can't convert it into a MO." f"Need str, int, Decimal, float or MO, got {value}") def __repr__(self): return f"<{self.__class__.__name__} {self.__txt__}>" def __str__(self): return str(self.value) @property def __txt__(self): try: return tree2txt(self.value) except AttributeError: return str(self.value) @property def __tex__(self): try: return tree2tex(self.value) except AttributeError: return str(self.value) @total_ordering class MOnumber(MO): """ Base number math object (int or Decimal) """ def __init__(self, value): """ Initiate a number MO >>> MOnumber(23) >>> MOnumber(-23) >>> MOnumber(23.3) >>> MOnumber(Decimal("23.3")) >>> MOnumber(Decimal("-23.3")) >>> a = MOnumber(23) >>> MOnumber(a) >>> MOnumber("a") Traceback (most recent call last): ... mapytex.calculus.core.MO.exceptions.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, 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)}" @property def __tex__(self): if self.value > 0: return str(self.value) return f"- {abs(self.value)}" def __add__(self, other): """ Adding a MOnumber """ try: return self.value + other.value except AttributeError: return self.value + other def __radd__(self, other): """ rAdding a MOnumber """ try: return self.value + other.value except AttributeError: return self.value + other def __sub__(self, other): """ Subing a MOnumber """ try: return self.value - other.value except AttributeError: return self.value - other def __rsub__(self, other): """ rSubing a MOnumber """ try: return self.value - other.value except AttributeError: return self.value - other def __mul__(self, other): """ Multiply a MOnumber """ try: return self.value * other.value except AttributeError: return self.value * other def __rmul__(self, other): """ rMultiply a MOnumber """ try: return self.value * other.value except AttributeError: return self.value * other def __truediv__(self, other): """ Divide a MOnumber """ try: return self.value / other.value except AttributeError: return self.value / other def __rtruediv__(self, other): """ rDivide a MOnumber """ try: return self.value / other.value except AttributeError: return self.value / other def __floordiv__(self, other): """ Integer Division a MOnumber """ try: return self.value // other.value except AttributeError: return self.value // other def __rfloordiv__(self, other): """ rInteger Division a MOnumber """ try: return other.value // self.value except AttributeError: return other // self.value def __mod__(self, other): """ Moduling a MOnumber """ try: return self.value % other.value except AttributeError: return self.value % other def __rmod__(self, other): """ rModuling a MOnumber """ try: return other.value % self.value except AttributeError: return other % self.value def __eq__(self, other): """ == a MOnumber """ try: return self.value == other.value except AttributeError: return self.value == other def __lt__(self, other): """ < a MOnumber """ try: return self.value < other.value except AttributeError: return self.value < other def __hash__(self): return self.value.__hash__() class MOstr(MO): """ Unknown math object like x or n""" def __init__(self, value): """ Initiate a string MO >>> a = MOstr("x") >>> a >>> b = MOstr(a) >>> b >>> a = MOstr("+") Traceback (most recent call last): ... mapytex.calculus.core.MO.exceptions.MOError: An MOstr should be initiate with a alpha string, got + >>> MOstr("ui") Traceback (most recent call last): ... mapytex.calculus.core.MO.exceptions.MOError: An MOstr should be initiate with a single caracter string, got ui >>> MOstr(2) Traceback (most recent call last): ... mapytex.calculus.core.MO.exceptions.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.is_scalar = False # ----------------------------- # Reglages pour 'vim' # vim:set autoindent expandtab tabstop=4 shiftwidth=4: # cursor: 16 del