diff --git a/ufpy/__init__.py b/ufpy/__init__.py index bd6db52..04f1c43 100644 --- a/ufpy/__init__.py +++ b/ufpy/__init__.py @@ -40,3 +40,7 @@ def new_func(*args, **kwargs): # GitHub package __github_version__ = '0.1' from ufpy.github import * + +# Algebra package +__algebra_version = '0.1' +from ufpy.algebra import * diff --git a/ufpy/algebra/__init__.py b/ufpy/algebra/__init__.py new file mode 100644 index 0000000..c10760e --- /dev/null +++ b/ufpy/algebra/__init__.py @@ -0,0 +1 @@ +from ufpy.algebra.sets import * \ No newline at end of file diff --git a/ufpy/algebra/sets.py b/ufpy/algebra/sets.py new file mode 100644 index 0000000..53496ef --- /dev/null +++ b/ufpy/algebra/sets.py @@ -0,0 +1,105 @@ +from __future__ import annotations +from os import environ + +from ufpy.math_op import i_generator, r_generator +from ufpy.utils import withrepr + +__all__ = ( + 'USet', + 'U' +) + +from typing import Iterable, Iterator, Optional, TypeVar, overload + +T = TypeVar('T') +OT = TypeVar('OT') + +@i_generator +@r_generator +class USet[T]: + @overload + def __init__(self, *values: T) -> None: ... + @overload + def __init__(self, *values: T, auto_update_U: bool) -> None: ... + @overload + def __init__(self, *, iterable: Iterable[T]) -> None: ... + @overload + def __init__(self, *, iterable: Iterable[T], auto_update_U: bool) -> None: ... + + def __init__(self, *values: T, iterable: Optional[Iterable[T]] = None, auto_update_U: bool = True) -> None: + self.__set = set(iterable) if iterable else set(values) + self.__auto_update_U = auto_update_U + self.__update__() + + @property + def set(self) -> set[T]: + return self.__set + + @set.setter + def set(self, value: Iterable[T]): + self.__set = set(value) + self.__update__() + + # When USet updates (not required, that new USet must be not same that old one) + def __update__(self): + if self.__auto_update_U: + U(iterable=_get_U() | self) + + # Convert to other types + def __repr__(self) -> str: + return f'u{self.set if self.set else '{}'}' + + def __str__(self) -> str: + return repr(self.set) if self.set else '{}' + + # Iteration + def __iter__(self) -> Iterator[T]: + return iter(self.__set) + + # Or + def __or__(self, other: Iterable[OT]) -> USet[T | OT]: + return USet(iterable=set(self) | set(other), auto_update_U=self.__auto_update_U) + + def __add__(self, other: Iterable[OT]) -> USet[T | OT]: + return self | other + + # Substract + def __sub__(self, other: Iterable[OT]) -> USet[T]: + return USet(iterable=set(self) - set(other), auto_update_U=self.__auto_update_U) + + def __div__(self, other: Iterable[OT]) -> USet[T]: + return self - other + + # And + def __and__(self, other: Iterable[OT]) -> USet: + return USet(iterable=set(self) & set(other), auto_update_U=self.__auto_update_U) + + def __mul__(self, other: Iterable[OT]) -> USet: + return self & other + + # Not + def __neg__(self) -> USet: + return _get_U() - self + +environ["ufpy_USet_U"] = "{}" + +@overload +def U(*values: T) -> None: ... +@overload +def U(*, iterable: Iterable[T]) -> None: ... + +@withrepr(lambda _: repr(_get_U())) +def U(*values: T, iterable: Optional[Iterable[T]] = None) -> None: + environ["ufpy_USet_U"] = str(_get_U() + (USet(iterable=iterable, auto_update_U=False) + if iterable else + USet(*values, auto_update_U=False))) + +def _convert_type(s: str): + if "'" in s or '"' in s: return s.replace("'", '').replace('"', '') + elif s.replace('-', '').isdigit(): return int(s) + else: return s + +def _get_U(): + s = environ["ufpy_USet_U"].replace('{', '').replace('}', '').replace('(', '').replace(')', '') + st = s.split(', ') + return USet(iterable=[x for i in st if (x := _convert_type(i))], auto_update_U=False) diff --git a/ufpy/utils.py b/ufpy/utils.py index 3d3a984..585892c 100644 --- a/ufpy/utils.py +++ b/ufpy/utils.py @@ -2,9 +2,12 @@ 'get_items_for_several_keys', 'set_items_for_several_keys', 'del_items_for_several_keys', + 'withrepr' ) -from typing import TypeVar +from ast import Call +from typing import Any, Callable, TypeVar +import functools from ufpy.typ import SupportsGet, SupportsSetItem, SupportsDelItem, AnyCollection @@ -31,3 +34,22 @@ def del_items_for_several_keys(o: SupportsDelItem[KT, VT], keys: AnyCollection[K for k in keys: del res[k] return res + +# Useful decorators + +class __reprwrapper: + def __init__(self, repr, func): + self._repr = repr + self._func = func + functools.update_wrapper(self, func) + def __call__(self, *args, **kw): + return self._func(*args, **kw) + def __repr__(self): + return self._repr(self._func) + +T = TypeVar('T', bound=Callable) + +def withrepr(f: Callable[[T], str]): + def _wrap(func: T): + return __reprwrapper(f, func) + return _wrap