zjowowen's picture
init space
079c32c
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__)
# Attention: DO NOT USE LINKING COMPARE OPERATORS, IT WILL CAUSE ERROR.
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