from __future__ import annotations
from cake import (
INumber,
IComplex,
IReal,
IRational,
IIntegeral,
)
import numbers
from math import trunc, floor, ceil
from cake import BasicExpression
from cake.basic import OtherType
from typing import Any, Union, Tuple
# Other type may be a basic expr, a cake library number or a generic python number
''' Methods implemented
__add__, __radd__, __iadd__
__sub__, __rsub__, __isub__
__mul__, __rmul__, __imul__
__truediv__, __rtruediv__, __itruediv__
__floordiv__, __rfloordiv__, __ifloordiv__
__mod__, __rmod__, __imod__
__divmod__, __rdivmod__
__pow__, __rpow__, __ipow__
__lshift__, __rlshift__, __ilshift__
__rshift__, __rrshift__, __irshift__
__and__, __rand__, __iand__
__xor__, __rxor__, __ixor__
__or__, __ror__, __ior__
__neg__, __pos__, __abs__, __invert__
__complex__, __float__, __int__, __str__, __bool__
__format__
__eq__, __ne__, __lt__, __gt__, __le__, __ge__
__call__, __round__, __trunc__, __floor__, __ceil__
'''
[docs]class Number(INumber):
'''
Basic class for creating different types of numbers,
this class can be treated as a normal number like in python.
.. tip::
To retrieve the actual value of the class, use :attr:`Number.value`
.. code-block:: py
from cake import Number, Variable
x = 5
x += Number(10)
## x is now an Integral not a Number
a = Variable('a')
x = x + a
## x is now an Expression
## x = Expr(Integral(15), Variable('a'))
## which can be represented as f(x) = 15 + a
'''
_type = None
def __init_subclass__(cls, type, *args, **kwds) -> None:
cls._type = type
return super().__init_subclass__(*args, **kwds)
[docs] @staticmethod
def convert(x: Union[Number, numbers.Number, BasicExpression]) -> Number:
''' Used to convert generic python types into types of the ``cake`` library
.. code-block:: py
>>> Number.convert(1)
Integral(1)
>>> Number.convert(1.4)
Real(1.4)
Parameters
----------
x: Any[Like[Number]]
Value to convert, can be any value,
if value cannot be converted the original value is returned.
'''
if isinstance(x, int):
return Integral(x)
elif isinstance(x, float):
return Real(x)
elif isinstance(x, complex):
return Complex(x)
return x
def __add__(self, other: OtherType) -> OtherType:
r = self.value + other
if isinstance(r, NumInstance):
return self.convert(r)
return r
__radd__ = __add__
__iadd__ = __add__
def __sub__(self, other: OtherType) -> OtherType:
return self.__add__(-other)
def __rsub__(self, other: OtherType) -> OtherType:
r = other - self.value
if isinstance(r, NumInstance):
return self.convert(r)
return r
__isub__ = __sub__
def __mul__(self, other: OtherType) -> OtherType:
r = other * self.value
if isinstance(r, NumInstance):
return self.convert(r)
return r
__rmul__ = __mul__
__imul__ = __mul__
def __truediv__(self, other: OtherType) -> OtherType:
r = self.value / other
if isinstance(r, NumInstance):
return self.convert(r)
return r
def __rtruediv__(self, other: OtherType) -> OtherType:
r = other / self.value
if isinstance(r, NumInstance):
return self.convert(r)
return r
__itruediv__ = __truediv__
def __floordiv__(self, other: OtherType) -> OtherType:
r = self.value // other
if isinstance(r, NumInstance):
return self.convert(r)
return r
def __rfloordiv__(self, other: OtherType) -> OtherType:
r = other // self.value
if isinstance(r, NumInstance):
return self.convert(r)
return r
__ifloordiv__ = __floordiv__
def __mod__(self, other: OtherType) -> OtherType:
r = self.value % other
if isinstance(r, NumInstance):
return self.convert(r)
return r
def __rmod__(self, other: OtherType) -> OtherType:
r = other % self.value
if isinstance(r, NumInstance):
return self.convert(r)
return r
__imod__ = __mod__
def __divmod__(self, other: OtherType) -> Tuple[OtherType, OtherType]:
truediv = self.__floordiv__(other)
mod = self.__mod__(other)
return (truediv, mod)
def __rdivmod__(self, other: OtherType) -> Tuple[OtherType, OtherType]:
truediv = other.__floordiv__(self.value)
mod = other.__mod__(self.value)
return (self.convert(truediv), self.convert(mod))
def __pow__(self, other: OtherType, *modulo: NumInstance) -> OtherType:
r = self.value ** other
if modulo:
r %= modulo[0]
if isinstance(r, NumInstance):
return self.convert(r)
return r
def __rpow__(self, other: OtherType, *modulo: NumInstance) -> OtherType:
r = other ** self.value
if modulo:
r %= modulo[0]
if isinstance(r, NumInstance):
return self.convert(r)
return r
__ipow__ = __pow__
def __lshift__(self, other: OtherType) -> OtherType:
r = self.value << other
if isinstance(r, NumInstance):
return self.convert(r)
return r
def __rshift__(self, other: OtherType) -> OtherType:
r = self.value >> other
if isinstance(r, NumInstance):
return self.convert(r)
return r
__ilshift__ = __lshift__
__irshift__ = __rshift__
def __rlshift__(self, other: OtherType) -> OtherType:
r = other << self.value
if isinstance(r, NumInstance):
return self.convert(r)
return r
def __rrshift__(self, other: OtherType) -> OtherType:
r = other >> self.value
if isinstance(r, NumInstance):
return self.convert(r)
return r
def __and__(self, other: OtherType) -> OtherType:
r = self.value & other
if isinstance(r, NumInstance):
return self.convert(r)
return r
def __rand__(self, other: OtherType) -> OtherType:
r = self.value & other
if isinstance(r, NumInstance):
return self.convert(r)
return r
__iand__ = __and__
def __xor__(self, other: OtherType) -> OtherType:
r = self.value ^ other
if isinstance(r, NumInstance):
return self.convert(r)
return r
def __rxor__(self, other: OtherType) -> OtherType:
r = self.value ^ other
if isinstance(r, NumInstance):
return self.convert(r)
return r
__ixor__ = __xor__
def __or__(self, other: OtherType) -> OtherType:
r = self.value | other
if isinstance(r, NumInstance):
return self.convert(r)
return r
def __ror__(self, other: OtherType) -> OtherType:
r = self.value | other
if isinstance(r, NumInstance):
return self.convert(r)
return r
__ior__ = __or__
def __neg__(self) -> Number:
return self.convert(-self.value)
def __pos__(self) -> Number:
return self
def __abs__(self) -> Number:
if self.value < 0:
return self.__neg__()
return self
def __invert__(self) -> Number:
return self.convert(~self.value)
''' END NUMERICAL METHODS '''
def __complex__(self) -> Complex:
return complex(self.value)
def __float__(self) -> Real:
return float(self.value)
def __int__(self) -> Integral:
return int(self.value)
def __str__(self) -> str:
return str(self.value)
def __bool__(self) -> bool:
return self.value != 0
def __format__(self, __format_spec: str) -> str:
return self.value.__format__(__format_spec)
''' END CONVERSION METHODS '''
def __eq__(self, other: OtherType) -> bool:
return self.value == other
def __ne__(self, other: OtherType) -> bool:
return self.value != other
def __lt__(self, other: OtherType) -> bool:
return self.value < other
def __gt__(self, other: OtherType) -> bool:
return self.value > other
def __le__(self, other: OtherType) -> bool:
return self.value <= other
def __ge__(self, other: OtherType) -> bool:
return self.value >= other
''' END COMPARATIVE METHODS '''
def __call__(self, other: OtherType) -> Any:
## Equivalent to __mul__, were 10 * 5 is represented as 10(5)
return self.__mul__(other)
def __round__(self, *ndigits: int) -> OtherType:
return round(self.value, *ndigits)
def __trunc__(self) -> OtherType:
return trunc(self.value)
def __floor__(self) -> OtherType:
return floor(self.value)
def __ceil__(self) -> OtherType:
return ceil(self.value)
NumInstance = (Number, numbers.Number)
[docs]class Complex(Number, IComplex, numbers.Complex, type=complex):
''' Represents a generic complex number
.. code-block:: py
>>> Complex(1, 2) == 1+2j
True
>>> Complex(1, 2j) == 1+2j
True
'''
@property
def imag(self) -> Any:
return self.value.imag
@property
def real(self) -> Any:
return self.value.real
[docs] def conjugate(self) -> Any:
return Complex(self.real, -self.imag)
[docs]class Real(Complex, IReal, numbers.Real, type=float):
''' Represents a generic real number
.. code-block:: py
>>> Real(1.5) == 1.5
True
>>> Real(Integral(5)) = 5
True
'''
[docs] def as_integer_ratio(self):
return self.value.as_integer_ratio()
as_integer_ratio.__doc__ = float.as_integer_ratio.__doc__
[docs]class Rational(Real, IRational, numbers.Rational, type=float):
''' Represents a generic rational number
.. code-block:: py
>>> Rational(1, 3) == (1/3)
True
>>> Rational(1.5) == 1.5
True
'''
[docs]class Integral(Rational, IIntegeral, numbers.Integral, type=int):
''' Represents a generic integer
.. code-block:: py
>>> Integral(10) == 10
True
'''