
from __future__ import annotations

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


class   __lowerCAmelCase		(		snake_case__ ):



      """simple docstring"""







      @abstractmethod
      def __lt__( self						:    T			,    _snake_case						:    T			):
          pass




      @abstractmethod
      def __gt__( self						:    T			,    _snake_case						:    T			):
          pass




      @abstractmethod
      def __eq__( self						:    T			,    _snake_case						:    object			):
          pass






__lowerCAmelCase   :     Tuple      =						TypeVar("T", bound=Comparable)


class   __lowerCAmelCase		(		Generic[T] ):



      """simple docstring"""







      def __init__( self						:    Any			):
          __lowercase						:     list[T]		      =    []
          __lowercase						:     int		      =    0




      def __repr__( self						:    List[str]			):
          return str(self.h			)




      def 						snake_case_     ( self						:    int			,    _snake_case						:    int			):
          if child_idx > 0:
              return (child_idx - 1) // 2
          return None




      def 						snake_case_     ( self						:    List[str]			,    _snake_case						:    int			):
          __lowercase						:     Optional[Any]		      =    2 * parent_idx + 1
          if left_child_index < self.heap_size:
              return left_child_index
          return None




      def 						snake_case_     ( self						:    List[Any]			,    _snake_case						:    int			):
          __lowercase						:     Tuple		      =    2 * parent_idx + 2
          if right_child_index < self.heap_size:
              return right_child_index
          return None




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




      def 						snake_case_     ( self						:    List[str]			,    _snake_case						:    Iterable[T]			):
          __lowercase						:     Dict		      =    list(_snake_case			)
          __lowercase						:     List[str]		      =    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(_snake_case			)




      def 						snake_case_     ( self						:    Tuple			):
          if self.heap_size >= 2:
              __lowercase						:     Tuple		      =    self.h[0]
              __lowercase						:     Any		      =    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 						snake_case_     ( self						:    List[Any]			,    _snake_case						:    T			):
          self.h.append(_snake_case			)
          __lowercase						:     Optional[Any]		      =    (self.heap_size - 1) // 2
          self.heap_size += 1
          while idx >= 0:
              self.max_heapify(_snake_case			)
              __lowercase						:     str		      =    (idx - 1) // 2




      def 						snake_case_     ( self						:    Tuple			):
          __lowercase						:     Tuple		      =    self.heap_size
          for j in range(size - 1			,    0			,    -1			):
              __lowercase						:     Optional[int]		      =    self.h[j], self.h[0]
              self.heap_size -= 1
              self.max_heapify(0			)
          __lowercase						:     List[Any]		      =    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, 10, 8],
     [103, 9, 1, 7, 11, 15, 25, 201, 209, 107, 5],
     [-45, -2, -5],
 ]:
  print(F'unsorted array: {unsorted}')

  __lowerCAmelCase   :     Heap[int]      =						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(100)
  print(F'after new value 100 inserted: {heap}')

  heap.heap_sort()
  print(F'heap-sorted array: {heap}\n')




from __future__ import annotations

from math import pi, pow


def       UpperCAmelCase_					(  __lowerCAmelCase		)	->  float:
    if side_length < 0:
        raise ValueError('''vol_cube() only accepts non-negative values'''		)
    return pow(__lowerCAmelCase		,   3		)


def       UpperCAmelCase_					(  __lowerCAmelCase		,   __lowerCAmelCase		)	->  float:
    if height < 0 or radius < 0:
        raise ValueError('''vol_spherical_cap() only accepts non-negative values'''		)
    # Volume is 1/3 pi * height squared * (3 * radius - height)
    return 1 / 3 * pi * pow(__lowerCAmelCase		,   2		) * (3 * radius - height)


def       UpperCAmelCase_					(  __lowerCAmelCase		,   __lowerCAmelCase		,   __lowerCAmelCase		)	->  float:
    if radius_a < 0 or radius_a < 0 or centers_distance < 0:
        raise ValueError('''vol_spheres_intersect() only accepts non-negative values'''		)
    if centers_distance == 0:
        return vol_sphere(min(__lowerCAmelCase		,   __lowerCAmelCase		)		)

    __lowercase						:     Dict		      =    (
        (radius_a - radius_a + centers_distance)
        * (radius_a + radius_a - centers_distance)
        / (2 * centers_distance)
    )
    __lowercase						:     Optional[int]		      =    (
        (radius_a - radius_a + centers_distance)
        * (radius_a + radius_a - centers_distance)
        / (2 * centers_distance)
    )

    return vol_spherical_cap(__lowerCAmelCase		,   __lowerCAmelCase		) + vol_spherical_cap(__lowerCAmelCase		,   __lowerCAmelCase		)


def       UpperCAmelCase_					(  __lowerCAmelCase		,   __lowerCAmelCase		,   __lowerCAmelCase		)	->  float:

    if radius_a <= 0 or radius_a <= 0 or centers_distance < 0:
        raise ValueError(
            '''vol_spheres_union() only accepts non-negative values, non-zero radius'''		)

    if centers_distance == 0:
        return vol_sphere(max(__lowerCAmelCase		,   __lowerCAmelCase		)		)

    return (
        vol_sphere(__lowerCAmelCase		)
        + vol_sphere(__lowerCAmelCase		)
        - vol_spheres_intersect(__lowerCAmelCase		,   __lowerCAmelCase		,   __lowerCAmelCase		)
    )


def       UpperCAmelCase_					(  __lowerCAmelCase		,   __lowerCAmelCase		,   __lowerCAmelCase		)	->  float:
    if width < 0 or height < 0 or length < 0:
        raise ValueError('''vol_cuboid() only accepts non-negative values'''		)
    return float(width * height * length		)


def       UpperCAmelCase_					(  __lowerCAmelCase		,   __lowerCAmelCase		)	->  float:
    if height < 0 or area_of_base < 0:
        raise ValueError('''vol_cone() only accepts non-negative values'''		)
    return area_of_base * height / 3.0


def       UpperCAmelCase_					(  __lowerCAmelCase		,   __lowerCAmelCase		)	->  float:
    if height < 0 or radius < 0:
        raise ValueError('''vol_right_circ_cone() only accepts non-negative values'''		)
    return pi * pow(__lowerCAmelCase		,   2		) * height / 3.0


def       UpperCAmelCase_					(  __lowerCAmelCase		,   __lowerCAmelCase		)	->  float:
    if height < 0 or area_of_base < 0:
        raise ValueError('''vol_prism() only accepts non-negative values'''		)
    return float(area_of_base * height		)


def       UpperCAmelCase_					(  __lowerCAmelCase		,   __lowerCAmelCase		)	->  float:
    if height < 0 or area_of_base < 0:
        raise ValueError('''vol_pyramid() only accepts non-negative values'''		)
    return area_of_base * height / 3.0


def       UpperCAmelCase_					(  __lowerCAmelCase		)	->  float:
    if radius < 0:
        raise ValueError('''vol_sphere() only accepts non-negative values'''		)
    # Volume is 4/3 * pi * radius cubed
    return 4 / 3 * pi * pow(__lowerCAmelCase		,   3		)


def       UpperCAmelCase_					(  __lowerCAmelCase		)	->  float:
    if radius < 0:
        raise ValueError('''vol_hemisphere() only accepts non-negative values'''		)
    # Volume is radius cubed * pi * 2/3
    return pow(__lowerCAmelCase		,   3		) * pi * 2 / 3


def       UpperCAmelCase_					(  __lowerCAmelCase		,   __lowerCAmelCase		)	->  float:
    if height < 0 or radius < 0:
        raise ValueError('''vol_circular_cylinder() only accepts non-negative values'''		)
    # Volume is radius squared * height * pi
    return pow(__lowerCAmelCase		,   2		) * height * pi


def       UpperCAmelCase_					(  __lowerCAmelCase		,   __lowerCAmelCase		,   __lowerCAmelCase		)	->  float:
    # Volume - (outer_radius squared - inner_radius squared) * pi * height
    if inner_radius < 0 or outer_radius < 0 or height < 0:
        raise ValueError(
            '''vol_hollow_circular_cylinder() only accepts non-negative values'''		)
    if outer_radius <= inner_radius:
        raise ValueError('''outer_radius must be greater than inner_radius'''		)
    return pi * (pow(__lowerCAmelCase		,   2		) - pow(__lowerCAmelCase		,   2		)) * height


def       UpperCAmelCase_					(  __lowerCAmelCase		,   __lowerCAmelCase		,   __lowerCAmelCase		)	->  float:
    # Volume is 1/3 * pi * height *
    #           (radius_1 squared + radius_2 squared + radius_1 * radius_2)
    if radius_a < 0 or radius_a < 0 or height < 0:
        raise ValueError('''vol_conical_frustum() only accepts non-negative values'''		)
    return (
        1
        / 3
        * pi
        * height
        * (pow(__lowerCAmelCase		,   2		) + pow(__lowerCAmelCase		,   2		) + radius_a * radius_a)
    )


def       UpperCAmelCase_					(  __lowerCAmelCase		,   __lowerCAmelCase		)	->  float:
    if torus_radius < 0 or tube_radius < 0:
        raise ValueError('''vol_torus() only accepts non-negative values'''		)
    return 2 * pow(__lowerCAmelCase		,   2		) * torus_radius * pow(__lowerCAmelCase		,   2		)


def       UpperCAmelCase_					(  )	->  Optional[Any]:
    print('''Volumes:'''		)
    print(F'Cube: {vol_cube(2		) = }'		)  # = 8
    print(F'Cuboid: {vol_cuboid(2		,   2		,   2		) = }'		)  # = 8
    print(F'Cone: {vol_cone(2		,   2		) = }'		)  # ~= 1.33
    print(F'Right Circular Cone: {vol_right_circ_cone(2		,   2		) = }'		)  # ~= 8.38
    print(F'Prism: {vol_prism(2		,   2		) = }'		)  # = 4
    print(F'Pyramid: {vol_pyramid(2		,   2		) = }'		)  # ~= 1.33
    print(F'Sphere: {vol_sphere(2		) = }'		)  # ~= 33.5
    print(F'Hemisphere: {vol_hemisphere(2		) = }'		)  # ~= 16.75
    print(F'Circular Cylinder: {vol_circular_cylinder(2		,   2		) = }'		)  # ~= 25.1
    print(F'Torus: {vol_torus(2		,   2		) = }'		)  # ~= 157.9
    print(F'Conical Frustum: {vol_conical_frustum(2		,   2		,   4		) = }'		)  # ~= 58.6
    print(F'Spherical cap: {vol_spherical_cap(1		,   2		) = }'		)  # ~= 5.24
    print(F'Spheres intersetion: {vol_spheres_intersect(2		,   2		,   1		) = }'		)  # ~= 21.21
    print(F'Spheres union: {vol_spheres_union(2		,   2		,   1		) = }'		)  # ~= 45.81
    print(
        F'Hollow Circular Cylinder: {vol_hollow_circular_cylinder(1		,   2		,   3		) = }'		)  # ~= 28.3


if __name__ == "__main__":
 main()


