from __future__ import annotations

import random
from dataclasses import dataclass, field
from typing import Any, Callable, Generic, Iterable, Iterator, Optional, TypeVar

T = TypeVar('T')
T_Cell = TypeVar('T_Cell')


class LinkedList(Generic[T]):
    """Doubly linked list implementation using sentinel nodes."""

    @dataclass
    class _Cell(Generic[T_Cell]):
        """A cell in the linked list.

        """
        value: Optional[T_Cell] = None
        before: Optional['LinkedList._Cell[T_Cell]'] = field(default=None, repr=False)
        after: Optional['LinkedList._Cell[T_Cell]'] = field(default=None, repr=False)

        def __repr__(self) -> str:
            return repr(self.value)

    def __init__(self) -> None:
        """Initialize an empty LinkedList with sentinel nodes."""
        self._head: LinkedList._Cell[T] = LinkedList._Cell()
        self._tail: LinkedList._Cell[T] = LinkedList._Cell()
        self._head.after = self._tail
        self._tail.before = self._head
        self._size = 0

    def __iter__(self) -> Iterator[T]:
        """Iterate over the values in the list."""
        current = self._head.after
        while current is not self._tail:
            yield current.value  # type: ignore
            current = current.after

    def __len__(self) -> int:
        """Return the number of elements in the list."""
        return self._size

    @property
    def is_empty(self) -> bool:
        """Check whether the list is empty."""
        return self._size == 0

    def head_append(self, value: T) -> None:
        """Append a value at the head of the list.

        Args:
            value (T): The value to append.
        """
        self._insert_between(value, self._head, self._head.after)

    def tail_append(self, value: T) -> None:
        """Append a value at the tail of the list.

        Args:
            value (T): The value to append.
        """
        self._insert_between(value, self._tail.before, self._tail)

    def append_at(self, value: T, index: int) -> None:
        """Append a value at a specific index.

        Args:
            value (T): The value to append.
            index (int): The index at which to insert.

        Raises:
            IndexError: If index is out of bounds.
        """
        if index < 0 or index > self._size:
            raise IndexError("Index out of range")

        current = self._head.after
        for _ in range(index):
            current = current.after
        self._insert_between(value, current.before, current)

    def _insert_between(self, value: T, before: _Cell[T], after: _Cell[T]) -> None:
        """Insert a value between two cells.

        Args:
            value (T): The value to insert.
            before (_Cell[T]): The cell before.
            after (_Cell[T]): The cell after.
        """
        cell = LinkedList._Cell(value, before, after)
        before.after = cell
        after.before = cell
        self._size += 1

    def to_list(self) -> list[T]:
        """Convert the linked list to a Python list.

        Returns:
            list[T]: List of values.
        """
        return list(self)


class HashSet(Generic[T]):
    """Hash-based set optimized for fast membership checks.

    This structure is designed for maximum lookup performance. It stores
    elements in a hash table (backed by a Python ``dict``), providing average
    O(1) time complexity for membership tests, insertions, and removals.

    Note:
        Elements must be hashable (i.e., usable as keys in a dict).

    Example:
        >>> s = HashSet[int]([1, 2, 3])
        >>> 2 in s
        True
        >>> s.add(10)
        >>> s.discard(1)
        >>> list(s)
        [2, 3, 10]
    """

    __slots__ = ("_data",)

    def __init__(self, values: Optional[Iterable[T]] = None) -> None:
        """Initialize the set.

        Args:
            values: Optional iterable of initial values to add.
        """
        self._data: dict[T, None] = {}
        if values is not None:
            for v in values:
                self._data[v] = None

    def __len__(self) -> int:
        """Return the number of stored elements."""
        return len(self._data)

    def __iter__(self) -> Iterator[T]:
        """Iterate over the stored elements (arbitrary order)."""
        return iter(self._data.keys())

    def __contains__(self, value: T) -> bool:
        """Return True if value is present in the set.

        Args:
            value: Value to test.

        Returns:
            True if present, False otherwise.
        """
        return value in self._data

    @property
    def is_empty(self) -> bool:
        """Check whether the set is empty."""
        return not self._data

    def add(self, value: T) -> None:
        """Add a value to the set.

        Args:
            value: Value to add.
        """
        self._data[value] = None

    def discard(self, value: T) -> None:
        """Remove a value if present (no error if missing).

        Args:
            value: Value to remove.
        """
        self._data.pop(value, None)

    def remove(self, value: T) -> None:
        """Remove a value from the set.

        Args:
            value: Value to remove.

        Raises:
            KeyError: If the value is not present.
        """
        del self._data[value]

    def clear(self) -> None:
        """Remove all elements."""
        self._data.clear()

    def to_list(self) -> list[T]:
        """Convert the set to a Python list.

        Returns:
            A list of stored elements (arbitrary order).
        """
        return list(self._data.keys())


class TreapSet(Generic[T]):
    """Ordered set implemented as a Treap (BST + heap priority).

    Supports efficient ordered queries without external dependencies:
        - lower_bound(x): smallest element >= x
        - range(a, b): iterate elements within [a, b] (inclusive by default)
        - sorted iteration

    Performance (expected/average):
        - add / discard / contains / lower_bound: O(log n)
        - range(a, b): O(log n + k) where k is number of returned items

    Notes:
        - Worst-case can degrade to O(n) (rare with randomized priorities).
        - Elements are unique (set semantics).
        - Optional `key` supports custom ordering.

    Args:
        values: Optional iterable of initial values.
        key: Optional key function (like sorted()).
        seed: Optional RNG seed for reproducible structure.

    Example:
        s = TreapSet([5, 1, 9, 3])
        s.add(7)
        print(s.lower_bound(4))      # 5
        print(list(s.range(3, 7)))   # [3, 5, 7]
        print(list(s))               # [1, 3, 5, 7, 9]
    """

    @dataclass
    class _Node:
        value: T
        prio: int
        left: Optional["TreapSet._Node"] = None
        right: Optional["TreapSet._Node"] = None

    __slots__ = ("_root", "_size", "_key", "_rand")

    def __init__(
        self,
        values: Optional[Iterable[T]] = None,
        *,
        key: Optional[Callable[[T], Any]] = None,
        seed: Optional[int] = None,
    ) -> None:
        self._root: Optional[TreapSet._Node] = None
        self._size = 0
        self._key = key
        self._rand = random.Random(seed)

        if values is not None:
            for v in values:
                self.add(v)

    def _k(self, v: T) -> Any:
        return v if self._key is None else self._key(v)

    # ---- rotations ----
    def _rot_right(self, p: _Node) -> _Node:
        l = p.left
        assert l is not None
        p.left = l.right
        l.right = p
        return l

    def _rot_left(self, p: _Node) -> _Node:
        r = p.right
        assert r is not None
        p.right = r.left
        r.left = p
        return r

    # ---- public protocol ----
    def __len__(self) -> int:
        return self._size

    def __iter__(self) -> Iterator[T]:
        # In-order traversal yields sorted order
        stack: list[TreapSet._Node] = []
        cur = self._root
        while cur is not None or stack:
            while cur is not None:
                stack.append(cur)
                cur = cur.left
            cur = stack.pop()
            yield cur.value
            cur = cur.right

    def __contains__(self, value: T) -> bool:
        kv = self._k(value)
        cur = self._root
        while cur is not None:
            kc = self._k(cur.value)
            if kv == kc and value == cur.value:
                return True
            if kv < kc:
                cur = cur.left
            else:
                cur = cur.right
        return False

    def __repr__(self) -> str:
        return f"{self.__class__.__name__}({list(self)!r})"

    # ---- core ops ----
    def add(self, value: T) -> bool:
        """Add value (set semantics).

        Returns:
            True if inserted, False if already present.
        """
        inserted, self._root = self._insert(self._root, value)
        if inserted:
            self._size += 1
        return inserted

    def _insert(self, node: Optional[_Node], value: T) -> tuple[bool, Optional[_Node]]:
        if node is None:
            return True, TreapSet._Node(value=value, prio=self._rand.getrandbits(31))

        kv = self._k(value)
        kn = self._k(node.value)

        # Uniqueness rule: same key AND same value => considered duplicate
        if kv == kn and value == node.value:
            return False, node

        if kv < kn:
            inserted, node.left = self._insert(node.left, value)
            if inserted and node.left is not None and node.left.prio > node.prio:
                node = self._rot_right(node)
            return inserted, node
        else:
            inserted, node.right = self._insert(node.right, value)
            if inserted and node.right is not None and node.right.prio > node.prio:
                node = self._rot_left(node)
            return inserted, node

    def discard(self, value: T) -> bool:
        """Remove value if present.

        Returns:
            True if removed, False if not found.
        """
        removed, self._root = self._delete(self._root, value)
        if removed:
            self._size -= 1
        return removed

    def _delete(self, node: Optional[_Node], value: T) -> tuple[bool, Optional[_Node]]:
        if node is None:
            return False, None

        kv = self._k(value)
        kn = self._k(node.value)

        if kv == kn and value == node.value:
            # delete this node by rotating down until leaf-ish
            return True, self._merge_children(node.left, node.right)

        if kv < kn:
            removed, node.left = self._delete(node.left, value)
            return removed, node
        else:
            removed, node.right = self._delete(node.right, value)
            return removed, node

    def _merge_children(self, left: Optional[_Node], right: Optional[_Node]) -> Optional[_Node]:
        if left is None:
            return right
        if right is None:
            return left
        # keep heap property by picking higher priority as root
        if left.prio > right.prio:
            left.right = self._merge_children(left.right, right)
            return left
        else:
            right.left = self._merge_children(left, right.left)
            return right

    # ---- ordered queries ----
    def lower_bound(self, value: T) -> Optional[T]:
        """Return the smallest element >= value, or None."""
        kv = self._k(value)
        cur = self._root
        candidate: Optional[T] = None
        while cur is not None:
            kc = self._k(cur.value)
            if kc >= kv:
                candidate = cur.value
                cur = cur.left
            else:
                cur = cur.right
        return candidate

    def range(self, a: T, b: T, *, inclusive: bool = True) -> Iterator[T]:
        """Yield elements in [a, b] (or [a, b) if inclusive=False)."""
        ka = self._k(a)
        kb = self._k(b)

        # iterative in-order with pruning
        stack: list[TreapSet._Node] = []
        cur = self._root

        while cur is not None or stack:
            while cur is not None:
                if self._k(cur.value) < ka:
                    cur = cur.right
                else:
                    stack.append(cur)
                    cur = cur.left

            cur = stack.pop()
            kc = self._k(cur.value)

            if inclusive:
                if kc > kb:
                    # since in-order, everything after is >= kc > kb
                    return
                if kc >= ka:
                    yield cur.value
            else:
                if kc >= kb:
                    return
                if kc >= ka:
                    yield cur.value

            cur = cur.right


class RBTreeSet(Generic[T]):
    """Ordered set implemented with a Red-Black Tree (worst-case O(log n)).

    This structure is designed for *robust* ordered operations with guaranteed
    worst-case time complexity for search/insert/delete.

    Supported operations:
        - lower_bound(x): smallest element >= x
        - range(a, b): iterate elements in [a, b] (inclusive by default)
        - sorted iteration: iterates in ascending order (or descending if reverse=True)
        - add / discard / contains

    Complexity (worst-case):
        - add, discard, contains, lower_bound: O(log n)
        - range(a, b): O(log n + k) where k is number of returned items
        - iteration: O(n)

    Ordering:
        Comparisons are performed using:
            (key(value), value)  if key is provided
            (value)              otherwise

        If two different elements share the same key and their values are not
        mutually comparable, insertion raises TypeError.

    Args:
        values: Optional initial values.
        key: Optional key function used for ordering (like built-in sorted()).
        reverse: If True, reverses the ordering.

    Example:
        s = RBTreeSet([5, 1, 9, 3])
        s.add(7)
        assert s.lower_bound(4) == 5
        assert list(s.range(3, 7)) == [3, 5, 7]
        assert list(s) == [1, 3, 5, 7, 9]
    """

    RED = True
    BLACK = False

    @dataclass
    class _Node:
        value: T
        color: bool
        left: Optional["RBTreeSet._Node"] = None
        right: Optional["RBTreeSet._Node"] = None
        parent: Optional["RBTreeSet._Node"] = None

    __slots__ = ("_root", "_size", "_key", "_reverse")

    def __init__(
        self,
        values: Optional[Iterable[T]] = None,
        *,
        key: Optional[Callable[[T], Any]] = None,
        reverse: bool = False,
    ) -> None:
        self._root: Optional[RBTreeSet._Node] = None
        self._size: int = 0
        self._key = key
        self._reverse = bool(reverse)

        if values is not None:
            for v in values:
                self.add(v)

    # --------------------- basic protocol ---------------------
    def __len__(self) -> int:
        """Return number of stored elements."""
        return self._size

    def __iter__(self) -> Iterator[T]:
        """Iterate over elements in sorted order."""
        if not self._reverse:
            cur = self._min_node(self._root)
            while cur is not None:
                yield cur.value
                cur = self._successor(cur)
        else:
            cur = self._max_node(self._root)
            while cur is not None:
                yield cur.value
                cur = self._predecessor(cur)

    def __contains__(self, value: T) -> bool:
        """Return True if value is present."""
        return self._find_node(value) is not None

    def __repr__(self) -> str:
        return f"{self.__class__.__name__}({list(self)!r})"

    # --------------------- ordering helpers ---------------------
    def _k(self, v: T) -> Any:
        return v if self._key is None else self._key(v)

    def _cmp(self, a: T, b: T) -> int:
        """Compare two values under (key, reverse). Return -1, 0, +1."""
        ka, kb = self._k(a), self._k(b)

        if ka < kb:
            return 1 if self._reverse else -1
        if ka > kb:
            return -1 if self._reverse else 1

        # Tie-breaker on the values themselves to get a total ordering.
        # If values are not comparable, raise TypeError.
        try:
            if a < b:
                return 1 if self._reverse else -1
            if a > b:
                return -1 if self._reverse else 1
        except TypeError as e:
            raise TypeError(
                "RBTreeSet requires values to be comparable when their key() is equal. "
                "Provide a key that makes ties impossible or store comparable values."
            ) from e

        return 0

    def _lt(self, a: T, b: T) -> bool:
        return self._cmp(a, b) < 0

    def _le(self, a: T, b: T) -> bool:
        return self._cmp(a, b) <= 0

    # --------------------- navigation ---------------------
    def _min_node(self, node: Optional[_Node]) -> Optional[_Node]:
        cur = node
        while cur is not None and cur.left is not None:
            cur = cur.left
        return cur

    def _max_node(self, node: Optional[_Node]) -> Optional[_Node]:
        cur = node
        while cur is not None and cur.right is not None:
            cur = cur.right
        return cur

    def _successor(self, node: _Node) -> Optional[_Node]:
        if node.right is not None:
            return self._min_node(node.right)
        cur = node
        p = cur.parent
        while p is not None and cur is p.right:
            cur = p
            p = p.parent
        return p

    def _predecessor(self, node: _Node) -> Optional[_Node]:
        if node.left is not None:
            return self._max_node(node.left)
        cur = node
        p = cur.parent
        while p is not None and cur is p.left:
            cur = p
            p = p.parent
        return p

    # --------------------- rotations ---------------------
    def _rotate_left(self, x: _Node) -> None:
        y = x.right
        if y is None:
            return
        x.right = y.left
        if y.left is not None:
            y.left.parent = x
        y.parent = x.parent
        if x.parent is None:
            self._root = y
        elif x is x.parent.left:
            x.parent.left = y
        else:
            x.parent.right = y
        y.left = x
        x.parent = y

    def _rotate_right(self, x: _Node) -> None:
        y = x.left
        if y is None:
            return
        x.left = y.right
        if y.right is not None:
            y.right.parent = x
        y.parent = x.parent
        if x.parent is None:
            self._root = y
        elif x is x.parent.right:
            x.parent.right = y
        else:
            x.parent.left = y
        y.right = x
        x.parent = y

    # --------------------- search ---------------------
    def _find_node(self, value: T) -> Optional[_Node]:
        cur = self._root
        while cur is not None:
            c = self._cmp(value, cur.value)
            if c == 0:
                return cur
            cur = cur.left if c < 0 else cur.right
        return None

    # --------------------- public ordered queries ---------------------
    def lower_bound(self, value: T) -> Optional[T]:
        """Return the smallest element >= value, or None if it does not exist."""
        cur = self._root
        candidate: Optional[RBTreeSet._Node] = None
        while cur is not None:
            if self._cmp(cur.value, value) >= 0:
                candidate = cur
                cur = cur.left
            else:
                cur = cur.right
        return None if candidate is None else candidate.value

    def range(self, a: T, b: T, *, inclusive: bool = True) -> Iterator[T]:
        """Iterate over all elements in [a, b] (or [a, b) if inclusive=False)."""
        if self._reverse:
            # Under reversed ordering, "between" still means according to that order.
            # Users typically expect [a, b] in the same comparator space.
            # If you want "numeric interval regardless of reverse", do not use reverse here.
            pass

        cur_val = self.lower_bound(a)
        if cur_val is None:
            return
        cur = self._find_node(cur_val)
        if cur is None:
            return

        while cur is not None:
            v = cur.value
            if inclusive:
                if not self._le(v, b):
                    break
            else:
                if not self._lt(v, b):
                    break
            yield v
            cur = self._successor(cur) if not self._reverse else self._predecessor(cur)

    # --------------------- insertion ---------------------
    def add(self, value: T) -> bool:
        """Add value to the set.

        Returns:
            True if inserted, False if already present.
        """
        if self._root is None:
            self._root = RBTreeSet._Node(value=value, color=RBTreeSet.BLACK)
            self._size = 1
            return True

        parent: Optional[RBTreeSet._Node] = None
        cur = self._root
        direction_left = False

        while cur is not None:
            parent = cur
            c = self._cmp(value, cur.value)
            if c == 0:
                return False
            if c < 0:
                direction_left = True
                cur = cur.left
            else:
                direction_left = False
                cur = cur.right

        new = RBTreeSet._Node(value=value, color=RBTreeSet.RED, parent=parent)
        if parent is None:
            self._root = new
        else:
            if direction_left:
                parent.left = new
            else:
                parent.right = new

        self._insert_fixup(new)
        self._size += 1
        return True

    def _insert_fixup(self, z: _Node) -> None:
        while z.parent is not None and z.parent.color == RBTreeSet.RED:
            gp = z.parent.parent
            if gp is None:
                break
            if z.parent is gp.left:
                y = gp.right  # uncle
                if y is not None and y.color == RBTreeSet.RED:
                    z.parent.color = RBTreeSet.BLACK
                    y.color = RBTreeSet.BLACK
                    gp.color = RBTreeSet.RED
                    z = gp
                else:
                    if z is z.parent.right:
                        z = z.parent
                        self._rotate_left(z)
                    z.parent.color = RBTreeSet.BLACK
                    gp.color = RBTreeSet.RED
                    self._rotate_right(gp)
            else:
                y = gp.left  # uncle
                if y is not None and y.color == RBTreeSet.RED:
                    z.parent.color = RBTreeSet.BLACK
                    y.color = RBTreeSet.BLACK
                    gp.color = RBTreeSet.RED
                    z = gp
                else:
                    if z is z.parent.left:
                        z = z.parent
                        self._rotate_right(z)
                    z.parent.color = RBTreeSet.BLACK
                    gp.color = RBTreeSet.RED
                    self._rotate_left(gp)
        if self._root is not None:
            self._root.color = RBTreeSet.BLACK

    # --------------------- deletion ---------------------
    def discard(self, value: T) -> bool:
        """Remove value if present.

        Returns:
            True if removed, False if not found.
        """
        z = self._find_node(value)
        if z is None:
            return False
        self._delete_node(z)
        self._size -= 1
        return True

    def _transplant(self, u: _Node, v: Optional[_Node]) -> None:
        if u.parent is None:
            self._root = v
        elif u is u.parent.left:
            u.parent.left = v
        else:
            u.parent.right = v
        if v is not None:
            v.parent = u.parent

    def _delete_node(self, z: _Node) -> None:
        y = z
        y_original_color = y.color
        x: Optional[RBTreeSet._Node]
        x_parent: Optional[RBTreeSet._Node]

        if z.left is None:
            x = z.right
            x_parent = z.parent
            self._transplant(z, z.right)
        elif z.right is None:
            x = z.left
            x_parent = z.parent
            self._transplant(z, z.left)
        else:
            y = self._min_node(z.right)
            assert y is not None
            y_original_color = y.color
            x = y.right
            if y.parent is z:
                x_parent = y
                if x is not None:
                    x.parent = y
            else:
                x_parent = y.parent
                self._transplant(y, y.right)
                y.right = z.right
                assert y.right is not None
                y.right.parent = y
            self._transplant(z, y)
            y.left = z.left
            assert y.left is not None
            y.left.parent = y
            y.color = z.color

        if y_original_color == RBTreeSet.BLACK:
            self._delete_fixup(x, x_parent)

    def _node_color(self, n: Optional[_Node]) -> bool:
        return RBTreeSet.BLACK if n is None else n.color

    def _delete_fixup(self, x: Optional[_Node], parent: Optional[_Node]) -> None:
        # x may be None; parent is x's parent after deletion/transplant
        while (x is not self._root) and (self._node_color(x) == RBTreeSet.BLACK):
            if parent is None:
                break
            if x is parent.left:
                w = parent.right
                if self._node_color(w) == RBTreeSet.RED:
                    assert w is not None
                    w.color = RBTreeSet.BLACK
                    parent.color = RBTreeSet.RED
                    self._rotate_left(parent)
                    w = parent.right
                # w is black
                if self._node_color(w.left if w else None) == RBTreeSet.BLACK and self._node_color(w.right if w else None) == RBTreeSet.BLACK:
                    if w is not None:
                        w.color = RBTreeSet.RED
                    x = parent
                    parent = x.parent
                else:
                    if self._node_color(w.right if w else None) == RBTreeSet.BLACK:
                        if w is not None and w.left is not None:
                            w.left.color = RBTreeSet.BLACK
                        if w is not None:
                            w.color = RBTreeSet.RED
                            self._rotate_right(w)
                        w = parent.right
                    if w is not None:
                        w.color = parent.color
                    parent.color = RBTreeSet.BLACK
                    if w is not None and w.right is not None:
                        w.right.color = RBTreeSet.BLACK
                    self._rotate_left(parent)
                    x = self._root
                    parent = None
            else:
                w = parent.left
                if self._node_color(w) == RBTreeSet.RED:
                    assert w is not None
                    w.color = RBTreeSet.BLACK
                    parent.color = RBTreeSet.RED
                    self._rotate_right(parent)
                    w = parent.left
                if self._node_color(w.right if w else None) == RBTreeSet.BLACK and self._node_color(w.left if w else None) == RBTreeSet.BLACK:
                    if w is not None:
                        w.color = RBTreeSet.RED
                    x = parent
                    parent = x.parent
                else:
                    if self._node_color(w.left if w else None) == RBTreeSet.BLACK:
                        if w is not None and w.right is not None:
                            w.right.color = RBTreeSet.BLACK
                        if w is not None:
                            w.color = RBTreeSet.RED
                            self._rotate_left(w)
                        w = parent.left
                    if w is not None:
                        w.color = parent.color
                    parent.color = RBTreeSet.BLACK
                    if w is not None and w.left is not None:
                        w.left.color = RBTreeSet.BLACK
                    self._rotate_right(parent)
                    x = self._root
                    parent = None

        if x is not None:
            x.color = RBTreeSet.BLACK
        if self._root is not None:
            self._root.color = RBTreeSet.BLACK