|
import operator |
|
from abc import abstractmethod |
|
from functools import wraps |
|
from typing import Callable, Any |
|
|
|
from .base import ILoaderClass |
|
|
|
|
|
def _callable_to_norm(func: Callable[[Any], Any]) -> 'INormClass': |
|
""" |
|
Overview: |
|
Convert callable to norm. |
|
Arguments: |
|
- func (:obj:`Callable[[Any], Any]`): The callable to be converted. |
|
""" |
|
|
|
class _Norm(INormClass): |
|
|
|
def _call(self, value): |
|
return func(value) |
|
|
|
return _Norm() |
|
|
|
|
|
def norm(value) -> 'INormClass': |
|
""" |
|
Overview: |
|
Convert value to norm. |
|
Arguments: |
|
- value (:obj:`Any`): The value to be converted. |
|
""" |
|
|
|
if isinstance(value, INormClass): |
|
return value |
|
elif isinstance(value, ILoaderClass): |
|
return _callable_to_norm(value) |
|
else: |
|
return _callable_to_norm(lambda v: value) |
|
|
|
|
|
def normfunc(func): |
|
""" |
|
Overview: |
|
Convert function to norm function. |
|
Arguments: |
|
- func (:obj:`Callable[[Any], Any]`): The function to be converted. |
|
""" |
|
|
|
@wraps(func) |
|
def _new_func(*args_norm, **kwargs_norm): |
|
args_norm = [norm(item) for item in args_norm] |
|
kwargs_norm = {key: norm(value) for key, value in kwargs_norm.items()} |
|
|
|
def _callable(v): |
|
args = [item(v) for item in args_norm] |
|
kwargs = {key: value(v) for key, value in kwargs_norm.items()} |
|
return func(*args, **kwargs) |
|
|
|
return _callable_to_norm(_callable) |
|
|
|
return _new_func |
|
|
|
|
|
UNARY_FUNC = Callable[[Any], Any] |
|
BINARY_FUNC = Callable[[Any, Any], Any] |
|
|
|
|
|
def _unary(a: 'INormClass', func: UNARY_FUNC) -> 'INormClass': |
|
""" |
|
Overview: |
|
Create a unary norm. |
|
Arguments: |
|
- a (:obj:`INormClass`): The norm. |
|
- func (:obj:`UNARY_FUNC`): The function. |
|
""" |
|
|
|
return _callable_to_norm(lambda v: func(a(v))) |
|
|
|
|
|
def _binary(a: 'INormClass', b: 'INormClass', func: BINARY_FUNC) -> 'INormClass': |
|
""" |
|
Overview: |
|
Create a binary norm. |
|
Arguments: |
|
- a (:obj:`INormClass`): The first norm. |
|
- b (:obj:`INormClass`): The second norm. |
|
- func (:obj:`BINARY_FUNC`): The function. |
|
""" |
|
return _callable_to_norm(lambda v: func(a(v), b(v))) |
|
|
|
|
|
def _binary_reducing(func: BINARY_FUNC, zero): |
|
""" |
|
Overview: |
|
Create a binary reducing norm. |
|
Arguments: |
|
- func (:obj:`BINARY_FUNC`): The function. |
|
- zero (:obj:`Any`): The zero value. |
|
""" |
|
|
|
@wraps(func) |
|
def _new_func(*args) -> 'INormClass': |
|
_sum = norm(zero) |
|
for item in args: |
|
_sum = _binary(_sum, norm(item), func) |
|
return _sum |
|
|
|
return _new_func |
|
|
|
|
|
class INormClass: |
|
""" |
|
Overview: |
|
The norm class. |
|
Interfaces: |
|
``__call__``, ``__add__``, ``__radd__``, ``__sub__``, ``__rsub__``, ``__mul__``, ``__rmul__``, ``__matmul__``, |
|
``__rmatmul__``, ``__truediv__``, ``__rtruediv__``, ``__floordiv__``, ``__rfloordiv__``, ``__mod__``, |
|
``__rmod__``, ``__pow__``, ``__rpow__``, ``__lshift__``, ``__rlshift__``, ``__rshift__``, ``__rrshift__``, |
|
``__and__``, ``__rand__``, ``__or__``, ``__ror__``, ``__xor__``, ``__rxor__``, ``__invert__``, ``__pos__``, |
|
``__neg__``, ``__eq__``, ``__ne__``, ``__lt__``, ``__le__``, ``__gt__``, ``__ge__`` |
|
""" |
|
|
|
@abstractmethod |
|
def _call(self, value): |
|
""" |
|
Overview: |
|
Call the norm. |
|
Arguments: |
|
- value (:obj:`Any`): The value to be normalized. |
|
""" |
|
|
|
raise NotImplementedError |
|
|
|
def __call__(self, value): |
|
""" |
|
Overview: |
|
Call the norm. |
|
Arguments: |
|
- value (:obj:`Any`): The value to be normalized. |
|
""" |
|
|
|
return self._call(value) |
|
|
|
def __add__(self, other): |
|
""" |
|
Overview: |
|
Add the norm. |
|
Arguments: |
|
- other (:obj:`Any`): The other norm. |
|
""" |
|
|
|
return _binary(self, norm(other), operator.__add__) |
|
|
|
def __radd__(self, other): |
|
""" |
|
Overview: |
|
Add the norm. |
|
Arguments: |
|
- other (:obj:`Any`): The other norm. |
|
""" |
|
|
|
return norm(other) + self |
|
|
|
def __sub__(self, other): |
|
""" |
|
Overview: |
|
Subtract the norm. |
|
Arguments: |
|
- other (:obj:`Any`): The other norm. |
|
""" |
|
|
|
return _binary(self, norm(other), operator.__sub__) |
|
|
|
def __rsub__(self, other): |
|
""" |
|
Overview: |
|
Subtract the norm. |
|
Arguments: |
|
- other (:obj:`Any`): The other norm. |
|
""" |
|
|
|
return norm(other) - self |
|
|
|
def __mul__(self, other): |
|
""" |
|
Overview: |
|
Multiply the norm. |
|
Arguments: |
|
- other (:obj:`Any`): The other norm. |
|
""" |
|
|
|
return _binary(self, norm(other), operator.__mul__) |
|
|
|
def __rmul__(self, other): |
|
""" |
|
Overview: |
|
Multiply the norm. |
|
Arguments: |
|
- other (:obj:`Any`): The other norm. |
|
""" |
|
|
|
return norm(other) * self |
|
|
|
def __matmul__(self, other): |
|
""" |
|
Overview: |
|
Matrix multiply the norm. |
|
Arguments: |
|
- other (:obj:`Any`): The other norm. |
|
""" |
|
|
|
return _binary(self, norm(other), operator.__matmul__) |
|
|
|
def __rmatmul__(self, other): |
|
""" |
|
Overview: |
|
Matrix multiply the norm. |
|
Arguments: |
|
- other (:obj:`Any`): The other norm. |
|
""" |
|
|
|
return norm(other) @ self |
|
|
|
def __truediv__(self, other): |
|
""" |
|
Overview: |
|
Divide the norm. |
|
Arguments: |
|
- other (:obj:`Any`): The other norm. |
|
""" |
|
|
|
return _binary(self, norm(other), operator.__truediv__) |
|
|
|
def __rtruediv__(self, other): |
|
""" |
|
Overview: |
|
Divide the norm. |
|
Arguments: |
|
- other (:obj:`Any`): The other norm. |
|
""" |
|
|
|
return norm(other) / self |
|
|
|
def __floordiv__(self, other): |
|
""" |
|
Overview: |
|
Floor divide the norm. |
|
Arguments: |
|
- other (:obj:`Any`): The other norm. |
|
""" |
|
|
|
return _binary(self, norm(other), operator.__floordiv__) |
|
|
|
def __rfloordiv__(self, other): |
|
""" |
|
Overview: |
|
Floor divide the norm. |
|
Arguments: |
|
- other (:obj:`Any`): The other norm. |
|
""" |
|
|
|
return norm(other) // self |
|
|
|
def __mod__(self, other): |
|
""" |
|
Overview: |
|
Mod the norm. |
|
Arguments: |
|
- other (:obj:`Any`): The other norm. |
|
""" |
|
|
|
return _binary(self, norm(other), operator.__mod__) |
|
|
|
def __rmod__(self, other): |
|
""" |
|
Overview: |
|
Mod the norm. |
|
Arguments: |
|
- other (:obj:`Any`): The other norm. |
|
""" |
|
|
|
return norm(other) % self |
|
|
|
def __pow__(self, power, modulo=None): |
|
""" |
|
Overview: |
|
Power the norm. |
|
Arguments: |
|
- power (:obj:`Any`): The power. |
|
- modulo (:obj:`Any`): The modulo. |
|
""" |
|
|
|
return _binary(self, norm(power), operator.__pow__) |
|
|
|
def __rpow__(self, other): |
|
""" |
|
Overview: |
|
Power the norm. |
|
Arguments: |
|
- other (:obj:`Any`): The other norm. |
|
""" |
|
|
|
return norm(other) ** self |
|
|
|
def __lshift__(self, other): |
|
""" |
|
Overview: |
|
Lshift the norm. |
|
Arguments: |
|
- other (:obj:`Any`): The other norm. |
|
""" |
|
|
|
return _binary(self, norm(other), operator.__lshift__) |
|
|
|
def __rlshift__(self, other): |
|
""" |
|
Overview: |
|
Lshift the norm. |
|
Arguments: |
|
- other (:obj:`Any`): The other norm. |
|
""" |
|
|
|
return norm(other) << self |
|
|
|
def __rshift__(self, other): |
|
""" |
|
Overview: |
|
Rshift the norm. |
|
Arguments: |
|
- other (:obj:`Any`): The other norm. |
|
""" |
|
|
|
return _binary(self, norm(other), operator.__rshift__) |
|
|
|
def __rrshift__(self, other): |
|
""" |
|
Overview: |
|
Rshift the norm. |
|
Arguments: |
|
- other (:obj:`Any`): The other norm. |
|
""" |
|
|
|
return norm(other) >> self |
|
|
|
def __and__(self, other): |
|
""" |
|
Overview: |
|
And operation the norm. |
|
Arguments: |
|
- other (:obj:`Any`): The other norm. |
|
""" |
|
|
|
return _binary(self, norm(other), operator.__and__) |
|
|
|
def __rand__(self, other): |
|
""" |
|
Overview: |
|
And operation the norm. |
|
Arguments: |
|
- other (:obj:`Any`): The other norm. |
|
""" |
|
|
|
return norm(other) & self |
|
|
|
def __or__(self, other): |
|
""" |
|
Overview: |
|
Or operation the norm. |
|
Arguments: |
|
- other (:obj:`Any`): The other norm. |
|
""" |
|
|
|
return _binary(self, norm(other), operator.__or__) |
|
|
|
def __ror__(self, other): |
|
""" |
|
Overview: |
|
Or operation the norm. |
|
Arguments: |
|
- other (:obj:`Any`): The other norm. |
|
""" |
|
|
|
return norm(other) | self |
|
|
|
def __xor__(self, other): |
|
""" |
|
Overview: |
|
Xor operation the norm. |
|
Arguments: |
|
- other (:obj:`Any`): The other norm. |
|
""" |
|
|
|
return _binary(self, norm(other), operator.__xor__) |
|
|
|
def __rxor__(self, other): |
|
""" |
|
Overview: |
|
Xor operation the norm. |
|
Arguments: |
|
- other (:obj:`Any`): The other norm. |
|
""" |
|
|
|
return norm(other) ^ self |
|
|
|
def __invert__(self): |
|
""" |
|
Overview: |
|
Invert the norm. |
|
""" |
|
|
|
return _unary(self, operator.__invert__) |
|
|
|
def __pos__(self): |
|
""" |
|
Overview: |
|
Positive the norm. |
|
""" |
|
|
|
return _unary(self, operator.__pos__) |
|
|
|
def __neg__(self): |
|
""" |
|
Overview: |
|
Negative the norm. |
|
""" |
|
|
|
return _unary(self, operator.__neg__) |
|
|
|
|
|
def __eq__(self, other): |
|
""" |
|
Overview: |
|
Compare the norm if they are equal. |
|
Arguments: |
|
- other (:obj:`Any`): The other norm. |
|
""" |
|
|
|
return _binary(self, norm(other), operator.__eq__) |
|
|
|
def __ne__(self, other): |
|
""" |
|
Overview: |
|
Compare the norm if they are not equal. |
|
Arguments: |
|
- other (:obj:`Any`): The other norm. |
|
""" |
|
|
|
return _binary(self, norm(other), operator.__ne__) |
|
|
|
def __lt__(self, other): |
|
""" |
|
Overview: |
|
Compare the norm if it is less than the other norm. |
|
Arguments: |
|
- other (:obj:`Any`): The other norm. |
|
""" |
|
|
|
return _binary(self, norm(other), operator.__lt__) |
|
|
|
def __le__(self, other): |
|
""" |
|
Overview: |
|
Compare the norm if it is less than or equal to the other norm. |
|
Arguments: |
|
- other (:obj:`Any`): The other norm. |
|
""" |
|
|
|
return _binary(self, norm(other), operator.__le__) |
|
|
|
def __gt__(self, other): |
|
""" |
|
Overview: |
|
Compare the norm if it is greater than the other norm. |
|
Arguments: |
|
- other (:obj:`Any`): The other norm. |
|
""" |
|
|
|
return _binary(self, norm(other), operator.__gt__) |
|
|
|
def __ge__(self, other): |
|
""" |
|
Overview: |
|
Compare the norm if it is greater than or equal to the other norm. |
|
Arguments: |
|
- other (:obj:`Any`): The other norm. |
|
""" |
|
|
|
return _binary(self, norm(other), operator.__ge__) |
|
|
|
|
|
lnot = normfunc(lambda x: not x) |
|
land = _binary_reducing(lambda x, y: x and y, True) |
|
lor = _binary_reducing(lambda x, y: x or y, True) |
|
|
|
lin = normfunc(operator.__contains__) |
|
lis = normfunc(operator.is_) |
|
lisnot = normfunc(operator.is_not) |
|
|
|
lsum = _binary_reducing(lambda x, y: x + y, 0) |
|
|
|
_COMPARE_OPERATORS = { |
|
'!=': operator.__ne__, |
|
'==': operator.__eq__, |
|
'<': operator.__lt__, |
|
'<=': operator.__le__, |
|
'>': operator.__gt__, |
|
'>=': operator.__ge__, |
|
} |
|
|
|
|
|
@normfunc |
|
def lcmp(first, *items): |
|
""" |
|
Overview: |
|
Compare the items. |
|
Arguments: |
|
- first (:obj:`Any`): The first item. |
|
- items (:obj:`Any`): The other items. |
|
""" |
|
|
|
if len(items) % 2 == 1: |
|
raise ValueError('Count of items should be odd number but {number} found.'.format(number=len(items) + 1)) |
|
|
|
ops, items = items[0::2], items[1::2] |
|
for op in ops: |
|
if op not in _COMPARE_OPERATORS.keys(): |
|
raise KeyError('Invalid compare operator - {op}.'.format(op=repr(op))) |
|
|
|
_last = first |
|
for op, item in zip(ops, items): |
|
if not _COMPARE_OPERATORS[op](_last, item): |
|
return False |
|
_last = item |
|
|
|
return True |
|
|