#! /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. :attr value: sympy compatible version of the MO :attr _tree: tree version of the MO :attr _signature: Name to identify the MO in the API """ def __init__(self, value): """ Initiate the MO It should be idempotent. >>> a = MO(3) >>> a >>> a = MO(a) >>> a """ try: self._tree = value._tree except AttributeError: self._tree = value self.is_scalar = True self._signature = None @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._tree) @property def __txt__(self): try: return tree2txt(self._tree) except AttributeError: return str(self._tree) @property def __tex__(self): try: return tree2tex(self._tree) except AttributeError: return str(self._tree) def __hash__(self): return self._tree.__hash__() def __eq__(self, other): """ == a MOnumber """ try: return self._tree == other._tree except AttributeError: return self._tree == other @property def signature(self): """ Name of the mo in the API :example: >>> MOnumber(3).signature 'scalar' >>> MOstr("x").signature 'monome1' """ return self._signature @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._tree except AttributeError: val = value if isinstance(val, (int, Decimal)): MO.__init__(self, val) 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") self.value = self._tree self._signature = "scalar" @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 __lt__(self, other): """ < a MOnumber """ try: return self.value < other.value except AttributeError: return self.value < other 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._tree 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, val) self.is_scalar = False self._variable = val self._signature = "monome1" @property def variable(self): return self._variable # ----------------------------- # Reglages pour 'vim' # vim:set autoindent expandtab tabstop=4 shiftwidth=4: # cursor: 16 del