Source code for rbool.singles

"""
Defines some basic types of boolean 1D
"""

from __future__ import annotations

from numbers import Real
from typing import Iterable, List, Set, Tuple, Union

from .base import Empty, Future, SubSetR1
from .error import NotExpectedError
from .numbs import NEGINF, POSINF, Is, To


def lower(number: Real, closed: bool = True) -> Interval:
    """
    Gives the interval such points are lower than given number

    Parameters
    ----------
    number : Real
        The finite number
    closed : bool, default = True
        If the interval is closed on the right end

    Return
    ------
    Interval
        The Interval such is lower than the given `number`

    Example
    -------
    >>> lower(0)
    (-inf, 0]
    >>> lower(0, False)
    (-inf, 0)
    >>> lower(10, True)
    (-inf, 10]
    """
    return Interval(NEGINF, To.finite(number), False, closed)


def bigger(finite: Real, closed: bool = True) -> Interval:
    """
    Gives the interval such points are bigger than given number

    Parameters
    ----------
    number : Real
        The finite number
    closed : bool, default = True
        If the interval is closed on the left end

    Return
    ------
    Interval
        The Interval such is bigger than the given `number`

    Example
    -------
    >>> bigger(0)
    [0, +inf)
    >>> bigger(0, False)
    (0, +inf)
    >>> bigger(10, True)
    [10, +inf)
    """
    return Interval(finite, POSINF, closed, False)


[docs] class SingleValue(SubSetR1): """ SingleValue stores only one value, being a subset of the real line Only finite values are acceptable """ def __init__(self, value: Real): self.__internal = To.finite(value) @property def internal(self) -> Real: """ Gives the internal real value of the SingleValue :getter: Returns the internal value of the SubSetR1 :type: Real """ return self.__internal def __str__(self) -> str: return "{" + str(self.__internal) + "}" def __repr__(self): return f"SingleValue({self.__internal})" def __contains__(self, other): if Is.infinity(other): return False other = Future.convert(other) return isinstance(other, Empty) or self == other def __eq__(self, other): other = Future.convert(other) return ( isinstance(other, self.__class__) and self.internal == other.internal ) def __invert__(self): return Disjoint( [ lower(self.internal, False), bigger(self.internal, False), ] ) def __and__(self, other: SubSetR1): other = Future.convert(other) return self if other.__contains__(self) else Empty() def __hash__(self): return hash(self.internal)
[docs] class Interval(SubSetR1): """ Interval stores a continuous set of points on R1 """ def __init__( self, start: Real, end: Real, left: bool = True, right: bool = True ): start = To.real(start) end = To.real(end) if end <= start: raise ValueError( "Received interval [{start}, {end}], but {end} <= {start}" ) if Is.infinity(start) and Is.infinity(end): raise ValueError("Received interval (-inf, +inf), use Whole") if start == NEGINF: left = False if end == POSINF: right = False self.__start = start self.__end = end self.__left = left self.__right = right # pylint: disable=too-many-return-statements def __contains__(self, other): if Is.infinity(other): return other in (self[0], self[1]) other = Future.convert(other) if isinstance(other, SingleValue): other = other.internal if other < self[0] or self[1] < other: return False if self[0] < other < self[1]: return True return self.closed_left if self[0] == other else self.closed_right if isinstance(other, Interval): if other[0] < self[0] or self[1] < other[1]: return False if self[0] < other[0] and other[1] < self[1]: return True if self[0] == other[0] and (self.closed_left ^ other.closed_left): return False if self[1] == other[1] and ( self.closed_right ^ other.closed_right ): return False return True if isinstance(other, Disjoint): return all(map(self.__contains__, other)) return isinstance(other, Empty) def __invert__(self): if self[0] == NEGINF: return bigger(self[1], not self.closed_right) if self[1] == POSINF: return lower(self[0], not self.closed_left) return Disjoint( [ lower(self[0], not self.closed_left), bigger(self[1], not self.closed_right), ] ) def __getitem__(self, index): return self.__end if index else self.__start def __eq__(self, other): other = Future.convert(other) return ( isinstance(other, Interval) and self[0] == other[0] and self[1] == other[1] and self.closed_left == other.closed_left and self.closed_right == other.closed_right ) def __str__(self): msg = "[" if self.closed_left else "(" msg += str(self[0]) + ", " + str(self[1]) msg += "]" if self.closed_right else ")" return msg @property def closed_left(self) -> bool: """ Tells if the interval is closed on the left side :getter: Returns a boolean that tells if interval is bounded on bot :type: bool """ return self.__left @property def closed_right(self) -> bool: """ Tells if the interval is closed on the right side :getter: Returns a boolean that tells if interval is bounded on top :type: bool """ return self.__right def __hash__(self): return hash((self[0], self[1]))
[docs] class Disjoint(SubSetR1): """ Stores the union of SingleValue and Interval which are not connected The direct constructor should not be used. This object should be constructed by the standard boolean operations of the some SingleValue and Interval """ def __init__(self, items: Iterable[Union[SingleValue, Interval]]): items = tuple(items) if len(items) < 2: raise ValueError("Less than 2 items!") knots: Set[Real] = set() singles: List[SingleValue] = [] intervs: List[Interval] = [] for item in items: if isinstance(item, SingleValue): singles.append(item) knots.add(item.internal) elif isinstance(item, Interval): intervs.append(item) if isinstance(item[0], Real): knots.add(item[0]) if isinstance(item[1], Real): knots.add(item[1]) else: raise TypeError("Received wrong type!") weights = tuple(single.internal for single in singles) self.__singles = tuple( s for _, s in sorted(zip(weights, singles), key=lambda x: x[0]) ) weights = tuple((interv[0] + interv[1]) / 2 for interv in intervs) self.__intervs = tuple( i for _, i in sorted(zip(weights, intervs), key=lambda x: x[0]) ) @property def singles(self) -> Tuple[SingleValue, ...]: """ Gives all the isolated nodes that are inside the Disjoint :getter: Returns all the isolated points :type: Tuple[SingleValue, ...] """ return self.__singles @property def intervals(self) -> Tuple[Interval, ...]: """ Gives all the non-connected intervals that are inside the Disjoint :getter: Returns all the non-connected intervals :type: Tuple[SingleValue, ...] """ return self.__intervs def __iter__(self): yield from self.__singles yield from self.__intervs def __contains__(self, other): if Is.infinity(other): return any(other in sub for sub in self) other = Future.convert(other) if isinstance(other, Disjoint): return all(sub in self for sub in other) return any(other in sub for sub in self) # pylint: disable=too-many-branches, too-many-statements def __str__(self) -> str: ssize = len(self.singles) isize = len(self.intervals) if ssize == 0: return " U ".join(map(str, self.intervals)) if isize == 0: return ( "{" + ", ".join(str(single.internal) for single in self.singles) + "}" ) msgs: List[str] = [] first: bool = True flag: bool = False i, s = 0, 0 if self.intervals[0][0] == NEGINF: msgs.append(str(self.intervals[0])) i += 1 first = False while i < isize and s < ssize: if self.singles[s].internal < self.intervals[i][0]: if not flag: if not first: msgs.append(" U ") msgs.append("{") first = False flag = True else: msgs.append(", ") msgs.append(str(self.singles[s].internal)) s += 1 else: if flag: msgs.append("}") flag = False if not first: msgs.append(" U ") first = False msgs.append(str(self.intervals[i])) i += 1 if s < ssize: if not flag: if not first: msgs.append(" U ") msgs.append("{") first = False flag = True else: # msgs.append(", ") raise NotExpectedError("Not expected get here") msgs.append(str(self.singles[s].internal)) s += 1 while s < ssize: msgs.append(", ") msgs.append(str(self.singles[s].internal)) s += 1 if flag: msgs.append("}") while i < isize: msgs.append(" U ") msgs.append(str(self.intervals[i])) i += 1 return "".join(msgs) def __eq__(self, other): other = Future.convert(other) if not isinstance(other, Disjoint): return False if len(self.singles) != len(other.singles) or len( self.intervals ) != len(other.intervals): return False return all(subs == subo for subs, subo in zip(self, other)) def __hash__(self): return hash(tuple(map(hash, self)))