

from __future__ import annotations

from abc import abstractmethod
from collections.abc import Iterable
from typing import Generic, Protocol, TypeVar






class 							A_			(		snake_case__ ):


     @abstractmethod
     def __lt__(							self      :		T      ,  UpperCAmelCase      :		T )							->     bool:
      pass


     @abstractmethod
     def __gt__(							self      :		T      ,  UpperCAmelCase      :		T )							->     bool:
      pass


     @abstractmethod
     def __eq__(							self      :		T      ,  UpperCAmelCase      :		object )							->     bool:
      pass







_a			=       TypeVar('''T''', bound=Comparable)






class 							A_			(		Generic[T] ):


     def __init__(							self      :		Optional[int] )							->     None:
      __lowerCAmelCase:       list[T]            =				[]
      __lowerCAmelCase:       int            =				0


     def __repr__(							self      :		int )							->     str:
      return str(self.h )


     def   UpperCAmelCase  (							self      :		Tuple      ,  UpperCAmelCase      :		int )							->     int | None:
      if child_idx > 0:
       return (child_idx - 1) // 2
      return None


     def   UpperCAmelCase  (							self      :		List[Any]      ,  UpperCAmelCase      :		int )							->     int | None:
      __lowerCAmelCase:       List[Any]            =				2 * parent_idx + 1
      if left_child_index < self.heap_size:
       return left_child_index
      return None


     def   UpperCAmelCase  (							self      :		int      ,  UpperCAmelCase      :		int )							->     int | None:
      __lowerCAmelCase:       List[str]            =				2 * parent_idx + 2
      if right_child_index < self.heap_size:
       return right_child_index
      return None


     def   UpperCAmelCase  (							self      :		str      ,  UpperCAmelCase      :		int )							->     None:
      if index < self.heap_size:
       __lowerCAmelCase:       int            =				index
       __lowerCAmelCase:       Dict            =				self.left_child_idx(UpperCAmelCase )
       __lowerCAmelCase:       Optional[int]            =				self.right_child_idx(UpperCAmelCase )
       # check which child is larger than its parent
       if left_child is not None and self.h[left_child] > self.h[violation]:
        __lowerCAmelCase:       Dict            =				left_child
       if right_child is not None and self.h[right_child] > self.h[violation]:
        __lowerCAmelCase:       Dict            =				right_child
       # if violation indeed exists
       if violation != index:
        # swap to fix the violation
        __lowerCAmelCase:       List[Any]            =				self.h[index], self.h[violation]
        # fix the subsequent violation recursively if any
        self.max_heapify(UpperCAmelCase )


     def   UpperCAmelCase  (							self      :		List[str]      ,  UpperCAmelCase      :		Iterable[T] )							->     None:
      __lowerCAmelCase:       Any            =				list(UpperCAmelCase )
      __lowerCAmelCase:       Optional[Any]            =				len(self.h )
      if self.heap_size > 1:
       # max_heapify from right to left but exclude leaves (last level)
       for i in range(self.heap_size // 2 - 1      ,  -1      ,  -1 ):
        self.max_heapify(UpperCAmelCase )


     def   UpperCAmelCase  (							self      :		str )							->     T:
      if self.heap_size >= 2:
       __lowerCAmelCase:       Dict            =				self.h[0]
       __lowerCAmelCase:       Tuple            =				self.h.pop(-1 )
       self.heap_size -= 1
       self.max_heapify(0 )
       return me
      elif self.heap_size == 1:
       self.heap_size -= 1
       return self.h.pop(-1 )
      else:
       raise Exception('Empty heap' )


     def   UpperCAmelCase  (							self      :		Any      ,  UpperCAmelCase      :		T )							->     None:
      self.h.append(UpperCAmelCase )
      __lowerCAmelCase:       Tuple            =				(self.heap_size - 1) // 2
      self.heap_size += 1
      while idx >= 0:
       self.max_heapify(UpperCAmelCase )
       __lowerCAmelCase:       str            =				(idx - 1) // 2


     def   UpperCAmelCase  (							self      :		str )							->     None:
      __lowerCAmelCase:       Any            =				self.heap_size
      for j in range(size - 1      ,  0      ,  -1 ):
       __lowerCAmelCase:       str            =				self.h[j], self.h[0]
       self.heap_size -= 1
       self.max_heapify(0 )
      __lowerCAmelCase:       Optional[int]            =				size


if __name__ == "__main__":
  import doctest

  # run doc test
  doctest.testmod()

  # demo
  for unsorted in [
      [0],
      [2],
      [3, 5],
      [5, 3],
      [5, 5],
      [0, 0, 0, 0],
      [1, 1, 1, 1],
      [2, 2, 3, 5],
      [0, 2, 2, 3, 5],
      [2, 5, 3, 0, 2, 3, 0, 3],
      [6, 1, 2, 7, 9, 3, 4, 5, 1_0, 8],
      [1_0_3, 9, 1, 7, 1_1, 1_5, 2_5, 2_0_1, 2_0_9, 1_0_7, 5],
      [-4_5, -2, -5],
  ]:
    print(f"unsorted array: {unsorted}")

    _a			=       Heap()
    heap.build_max_heap(unsorted)
    print(f"after build heap: {heap}")

    print(f"max value: {heap.extract_max()}")
    print(f"after max value removed: {heap}")

    heap.insert(1_0_0)
    print(f"after new value 100 inserted: {heap}")

    heap.heap_sort()
    print(f"heap-sorted array: {heap}\n")








from __future__ import annotations

from collections.abc import Iterable
from dataclasses import dataclass
from typing import Any






class 							A_			:
     _lowercase      :					Any       =     ('_front', '_back', '_len')






     @dataclass
     class 							A_			:
          _lowercase      :					Any       =     None
          _lowercase      :					Deque._Node | None       =     None
          _lowercase      :					Deque._Node | None       =     None







     class 							A_			:
          _lowercase      :					Optional[Any]       =     ('_cur',)


          def __init__(							self      :		Any      ,  UpperCAmelCase      :		Deque._Node | None )							->     None:
           __lowerCAmelCase:       int            =				cur


          def __iter__(							self      :		int )							->     Deque._Iterator:
           return self


          def   UpperCAmelCase  (							self      :		Optional[Any] )							->     Any:
           if self._cur is None:
            # finished iterating
            raise StopIteration
           __lowerCAmelCase:       Optional[Any]            =				self._cur.val
           __lowerCAmelCase:       List[Any]            =				self._cur.next_node

           return val







     def __init__(							self      :		Any      ,  UpperCAmelCase      :		Iterable[Any] | None = None )							->     None:
      __lowerCAmelCase:       Any            =				None
      __lowerCAmelCase:       Any            =				None
      __lowerCAmelCase:       int            =				0

      if iterable is not None:
       # append every value to the deque
       for val in iterable:
        self.append(UpperCAmelCase )


     def   UpperCAmelCase  (							self      :		List[str]      ,  UpperCAmelCase      :		Any )							->     None:
      __lowerCAmelCase:       Union[str, Any]            =				self._Node(UpperCAmelCase      ,  UpperCAmelCase      ,  UpperCAmelCase )
      if self.is_empty():
       # front = back
       __lowerCAmelCase:       int            =				node
       __lowerCAmelCase:       List[Any]            =				1
      else:
       # connect nodes
       __lowerCAmelCase:       Optional[Any]            =				node
       __lowerCAmelCase:       str            =				self._back
       __lowerCAmelCase:       Optional[int]            =				node  # assign new back to the new node

       self._len += 1

       # make sure there were no errors
       assert not self.is_empty(), "Error on appending value."


     def   UpperCAmelCase  (							self      :		Optional[int]      ,  UpperCAmelCase      :		Any )							->     None:
      __lowerCAmelCase:       int            =				self._Node(UpperCAmelCase      ,  UpperCAmelCase      ,  UpperCAmelCase )
      if self.is_empty():
       # front = back
       __lowerCAmelCase:       Union[str, Any]            =				node
       __lowerCAmelCase:       str            =				1
      else:
       # connect nodes
       __lowerCAmelCase:       Dict            =				self._front
       __lowerCAmelCase:       Optional[int]            =				node
       __lowerCAmelCase:       Any            =				node  # assign new front to the new node

       self._len += 1

       # make sure there were no errors
       assert not self.is_empty(), "Error on appending value."


     def   UpperCAmelCase  (							self      :		Union[str, Any]      ,  UpperCAmelCase      :		Iterable[Any] )							->     None:
      for val in iterable:
       self.append(UpperCAmelCase )


     def   UpperCAmelCase  (							self      :		List[str]      ,  UpperCAmelCase      :		Iterable[Any] )							->     None:
      for val in iterable:
       self.appendleft(UpperCAmelCase )


     def   UpperCAmelCase  (							self      :		Dict )							->     Any:
      assert not self.is_empty(), "Deque is empty."

      __lowerCAmelCase:       Optional[int]            =				self._back
      __lowerCAmelCase:       Optional[Any]            =				self._back.prev_node  # set new back
      # drop the last node - python will deallocate memory automatically
      __lowerCAmelCase:       Optional[int]            =				None

      self._len -= 1

      return topop.val


     def   UpperCAmelCase  (							self      :		Dict )							->     Any:
      assert not self.is_empty(), "Deque is empty."

      __lowerCAmelCase:       Optional[Any]            =				self._front
      __lowerCAmelCase:       Optional[Any]            =				self._front.next_node  # set new front and drop the first node
      __lowerCAmelCase:       int            =				None

      self._len -= 1

      return topop.val


     def   UpperCAmelCase  (							self      :		Dict )							->     bool:
      return self._front is None


     def __len__(							self      :		List[Any] )							->     int:
      return self._len


     def __eq__(							self      :		Union[str, Any]      ,  UpperCAmelCase      :		object )							->     bool:

      if not isinstance(UpperCAmelCase      ,  UpperCAmelCase ):
       return NotImplemented

      __lowerCAmelCase:       List[Any]            =				self._front
      __lowerCAmelCase:       Dict            =				other._front

      # if the length of the dequeues are not the same, they are not equal
      if len(self ) != len(UpperCAmelCase ):
       return False

      while me is not None and oth is not None:
       # compare every value
       if me.val != oth.val:
        return False
       __lowerCAmelCase:       Optional[Any]            =				me.next_node
       __lowerCAmelCase:       Optional[Any]            =				oth.next_node

      return True


     def __iter__(							self      :		Tuple )							->     Deque._Iterator:
      return Deque._Iterator(self._front )


     def __repr__(							self      :		Optional[Any] )							->     str:
      __lowerCAmelCase:       Union[str, Any]            =				[]
      __lowerCAmelCase:       Optional[int]            =				self._front
      while aux is not None:
       # append the values in a list to display
       values_list.append(aux.val )
       __lowerCAmelCase:       Any            =				aux.next_node

      return F'''[{', '.join(repr(UpperCAmelCase ) for val in values_list )}]'''


if __name__ == "__main__":
  import doctest

  doctest.testmod()





