# Path: boris/portion/const.py
# class Bound(enum.Enum):
# class _Singleton():
# class _PInf(_Singleton):
# class _NInf(_Singleton):
#     CLOSED = True
#     OPEN = False
#     def __bool__(self):
#     def __invert__(self):
#     def __str__(self):
#     def __repr__(self):
#     def __new__(cls, *args, **kwargs):
#     def __neg__(self): return _NInf()
#     def __lt__(self, o): return False
#     def __le__(self, o): return isinstance(o, _PInf)
#     def __gt__(self, o): return not isinstance(o, _PInf)
#     def __ge__(self, o): return True
#     def __eq__(self, o): return isinstance(o, _PInf)
#     def __repr__(self): return '+inf'
#     def __hash__(self): return hash(float('+inf'))
#     def __neg__(self): return _PInf()
#     def __lt__(self, o): return not isinstance(o, _NInf)
#     def __le__(self, o): return True
#     def __gt__(self, o): return False
#     def __ge__(self, o): return isinstance(o, _NInf)
#     def __eq__(self, o): return isinstance(o, _NInf)
#     def __repr__(self): return '-inf'
#     def __hash__(self): return hash(float('-inf'))


from collections import namedtuple
from .const import Bound, inf
Atomic = namedtuple('Atomic', ['left', 'lower', 'upper', 'right'])


def mergeable(a, b):
    """
    Tester whether two atomic intervals can be merged (i.e. they overlap or are adjacent).

    :param a: an atomic interval.
    :param b: an atomic interval.
    :return: True if mergeable, False otherwise.
    """
    if a.lower < b.lower or (a.lower == b.lower and a.left == Bound.CLOSED):
        first, second = a, b
    else:
        first, second = b, a

    if first.upper == second.lower:
        return first.right == Bound.CLOSED or second.left == Bound.CLOSED

    return first.upper > second.lower


def open(lower, upper):
    """
    Create an open interval with given bounds.

    :param lower: value of the lower bound.
    :param upper: value of the upper bound.
    :return: an interval.
    """

# Path: boris/portion/const.py
# class Bound(enum.Enum):
# class _Singleton():
# class _PInf(_Singleton):
# class _NInf(_Singleton):
#     CLOSED = True
#     OPEN = False
#     def __bool__(self):
#     def __invert__(self):
#     def __str__(self):
#     def __repr__(self):
#     def __new__(cls, *args, **kwargs):
#     def __neg__(self): return _NInf()
#     def __lt__(self, o): return False
#     def __le__(self, o): return isinstance(o, _PInf)
#     def __gt__(self, o): return not isinstance(o, _PInf)
#     def __ge__(self, o): return True
#     def __eq__(self, o): return isinstance(o, _PInf)
#     def __repr__(self): return '+inf'
#     def __hash__(self): return hash(float('+inf'))
#     def __neg__(self): return _PInf()
#     def __lt__(self, o): return not isinstance(o, _NInf)
#     def __le__(self, o): return True
#     def __gt__(self, o): return False
#     def __ge__(self, o): return isinstance(o, _NInf)
#     def __eq__(self, o): return isinstance(o, _NInf)
#     def __repr__(self): return '-inf'
#     def __hash__(self): return hash(float('-inf'))


# Path: boris/portion/interval.py
from collections import namedtuple
from .const import Bound, inf

Atomic = namedtuple('Atomic', ['left', 'lower', 'upper', 'right'])


def mergeable(a, b):
    """
    Tester whether two atomic intervals can be merged (i.e. they overlap or are adjacent).

    :param a: an atomic interval.
    :param b: an atomic interval.
    :return: True if mergeable, False otherwise.
    """
    if a.lower < b.lower or (a.lower == b.lower and a.left == Bound.CLOSED):
        first, second = a, b
    else:
        first, second = b, a

    if first.upper == second.lower:
        return first.right == Bound.CLOSED or second.left == Bound.CLOSED

    return first.upper > second.lower


def open(lower, upper):
    """
    Create an open interval with given bounds.

    :param lower: value of the lower bound.
    :param upper: value of the upper bound.
    :return: an interval.
    """

    return Interval.from_atomic(Bound.OPEN, lower, upper, Bound.OPEN)