




from collections.abc import Iterable
from typing import Any







class 						A:



 '''simple docstring'''


 def __init__(       self      :						List[Any]		,							A_      :						Union[str, Any] = None						)     ->			Union[str, Any]:

       """simple docstring"""






       lowerCamelCase_	  =					value
       lowerCamelCase_	  =					None  # Added in order to delete a node easier
       lowerCamelCase_	  =					None
       lowerCamelCase_	  =					None





 def __repr__(       self      :						Any						)     ->			Any:

       """simple docstring"""






       from pprint import pformat

       if self.left is None and self.right is None:
             return str(self.value						)
       return pformat({f"""{self.value}""": (self.left, self.right)}		,							indent=1						)







class 						A:



 '''simple docstring'''


 def __init__(       self      :						Tuple		,							A_      :						Union[str, Any] = None						)     ->			Any:

       """simple docstring"""






       lowerCamelCase_	  =					root

 def __str__(       self      :						Dict						)     ->			int:

       """simple docstring"""






       return str(self.root						)

 def        a__				(       self      :						Optional[int]		,							A_      :						Dict		,							A_      :						Union[str, Any]						)     ->			Any:

       """simple docstring"""






       if new_children is not None:  # reset its kids
             lowerCamelCase_	  =					node.parent
       if node.parent is not None:  # reset its parent
             if self.is_right(_lowercase						):  # If it is the right children
                   lowerCamelCase_	  =					new_children
             else:
                   lowerCamelCase_	  =					new_children
       else:
             lowerCamelCase_	  =					new_children

 def        a__				(       self      :						Optional[Any]		,							A_      :						Union[str, Any]						)     ->			str:

       """simple docstring"""






       if node.parent and node.parent.right:
             return node == node.parent.right
       return False

 def        a__				(       self      :						Optional[Any]						)     ->			str:

       """simple docstring"""






       return self.root is None

 def        a__				(       self      :						Union[str, Any]		,							A_      :						Dict						)     ->			int:

       """simple docstring"""






       lowerCamelCase_	  =					Node(_lowercase						)  # create a new Node
       if self.empty():  # if Tree is empty
             lowerCamelCase_	  =					new_node  # set its root
       else:  # Tree is not empty
             lowerCamelCase_	  =					self.root  # from root
             if parent_node is None:
                   return
             while True:  # While we don't get to a leaf
                   if value < parent_node.value:  # We go left
                         if parent_node.left is None:
                               lowerCamelCase_	  =					new_node  # We insert the new node in a leaf
                               break
                         else:
                               lowerCamelCase_	  =					parent_node.left
                   else:
                         if parent_node.right is None:
                               lowerCamelCase_	  =					new_node
                               break
                         else:
                               lowerCamelCase_	  =					parent_node.right
             lowerCamelCase_	  =					parent_node

 def        a__				(       self      :						str		,							*A_      :						Tuple						)     ->			Dict:

       """simple docstring"""






       for value in values:
             self.__insert(_lowercase						)

 def        a__				(       self      :						List[str]		,							A_      :						Tuple						)     ->			Any:

       """simple docstring"""






       if self.empty():
             raise IndexError('Warning: Tree is empty! please use another.'						)
       else:
             lowerCamelCase_	  =					self.root
             # use lazy evaluation here to avoid NoneType Attribute error
             while node is not None and node.value is not value:
                   lowerCamelCase_	  =					node.left if value < node.value else node.right
             return node

 def        a__				(       self      :						int		,							A_      :						str = None						)     ->			List[str]:

       """simple docstring"""






       if node is None:
             if self.root is None:
                   return None
             lowerCamelCase_	  =					self.root

       if not self.empty():
             while node.right is not None:
                   lowerCamelCase_	  =					node.right
       return node

 def        a__				(       self      :						str		,							A_      :						Optional[int] = None						)     ->			Union[str, Any]:

       """simple docstring"""






       if node is None:
             lowerCamelCase_	  =					self.root
       if self.root is None:
             return None
       if not self.empty():
             lowerCamelCase_	  =					self.root
             while node.left is not None:
                   lowerCamelCase_	  =					node.left
       return node

 def        a__				(       self      :						Dict		,							A_      :						Any						)     ->			int:

       """simple docstring"""






       lowerCamelCase_	  =					self.search(_lowercase						)  # Look for the node with that label
       if node is not None:
             if node.left is None and node.right is None:  # If it has no children
                   self.__reassign_nodes(_lowercase		,							_lowercase						)
             elif node.left is None:  # Has only right children
                   self.__reassign_nodes(_lowercase		,							node.right						)
             elif node.right is None:  # Has only left children
                   self.__reassign_nodes(_lowercase		,							node.left						)
             else:
                   lowerCamelCase_	  =					self.get_max(
                       node.left						)  # Gets the max value of the left branch
                   self.remove(tmp_node.value						)  # type: ignore
                   lowerCamelCase_	  =					(
                       tmp_node.value  # type: ignore
                   )  # Assigns the value to the node to delete and keep tree structure

 def        a__				(       self      :						Optional[Any]		,							A_      :						Tuple						)     ->			Tuple:

       """simple docstring"""






       if node is not None:
             yield node  # Preorder Traversal
             yield from self.preorder_traverse(node.left						)
             yield from self.preorder_traverse(node.right						)

 def        a__				(       self      :						Any		,							A_      :						Any=None						)     ->			Dict:

       """simple docstring"""






       if traversal_function is None:
             return self.preorder_traverse(self.root						)
       else:
             return traversal_function(self.root						)

 def        a__				(       self      :						Optional[int]		,							A_      :						int		,							A_      :						Union[str, Any]						)     ->			List[str]:

       """simple docstring"""






       if node:
             self.inorder(_lowercase		,							node.left						)
             arr.append(node.value						)
             self.inorder(_lowercase		,							node.right						)





 def        a__				(       self      :						Optional[Any]		,							A_      :						Any		,							A_      :						List[str]						)     ->			Tuple:

       """simple docstring"""






       lowerCamelCase_	  =					[]
       self.inorder(_lowercase		,							_lowercase						)  # append all values to list using inorder traversal
       return arr[k - 1]

def     _SCREAMING_SNAKE_CASE    (							lowercase			: Node | None  ):


      '''simple docstring'''



      lowerCamelCase_	  =					[]
      if curr_node is not None:
            lowerCamelCase_	  =					postorder(curr_node.left  ) + postorder(curr_node.right  ) + [curr_node]
      return node_list

def     _SCREAMING_SNAKE_CASE    (							):


      '''simple docstring'''



      lowerCamelCase_	  =					(8, 3, 6, 1, 10, 14, 13, 4, 7)
      lowerCamelCase_	  =					BinarySearchTree()
      for i in testlist:
            t.insert(_lowerCamelCase  )

      # Prints all the elements of the list in order traversal
      print(_lowerCamelCase  )

      if t.search(6  ) is not None:
            print('The value 6 exists'  )
      else:
            print('The value 6 doesn\'t exist'  )

      if t.search(-1  ) is not None:
            print('The value -1 exists'  )
      else:
            print('The value -1 doesn\'t exist'  )

      if not t.empty():
            print('Max Value: '			,							t.get_max().value  )  # type: ignore
            print('Min Value: '			,							t.get_min().value  )  # type: ignore

      for i in testlist:
            t.remove(_lowerCamelCase  )
            print(_lowerCamelCase  )


if __name__ == "__main__":
   import doctest

   doctest.testmod(verbose=True)









from manim import *







class 						A(		UpperCamelCase     ):



 '''simple docstring'''


 def        a__				(       self      :						Optional[Any]						)     ->			List[str]:

       """simple docstring"""






       lowerCamelCase_	  =					Rectangle(height=0.5		,							width=0.5						)
       lowerCamelCase_	  =					Rectangle(height=0.46		,							width=0.46						).set_stroke(width=0						)

       lowerCamelCase_	  =					Rectangle(height=0.25		,							width=0.25						)

       lowerCamelCase_	  =					[mem.copy() for i in range(6						)]
       lowerCamelCase_	  =					[mem.copy() for i in range(6						)]
       lowerCamelCase_	  =					VGroup(*A_						).arrange(A_		,							buff=0						)
       lowerCamelCase_	  =					VGroup(*A_						).arrange(A_		,							buff=0						)
       lowerCamelCase_	  =					VGroup(A_		,							A_						).arrange(A_		,							buff=0						)
       lowerCamelCase_	  =					Text('CPU'		,							font_size=24						)
       lowerCamelCase_	  =					Group(A_		,							A_						).arrange(A_		,							buff=0.5		,							aligned_edge=A_						)
       cpu.move_to([-2.5, -0.5, 0]						)
       self.add(A_						)

       lowerCamelCase_	  =					[mem.copy() for i in range(4						)]
       lowerCamelCase_	  =					VGroup(*A_						).arrange(A_		,							buff=0						)
       lowerCamelCase_	  =					Text('GPU'		,							font_size=24						)
       lowerCamelCase_	  =					Group(A_		,							A_						).arrange(A_		,							buff=0.5		,							aligned_edge=A_						)
       gpu.move_to([-1, -1, 0]						)
       self.add(A_						)

       lowerCamelCase_	  =					[mem.copy() for i in range(6						)]
       lowerCamelCase_	  =					VGroup(*A_						).arrange(A_		,							buff=0						)

       lowerCamelCase_	  =					Text('Model'		,							font_size=24						)
       lowerCamelCase_	  =					Group(A_		,							A_						).arrange(A_		,							buff=0.5		,							aligned_edge=A_						)
       model.move_to([3, -1.0, 0]						)
       self.add(A_						)

       lowerCamelCase_	  =					[]
       lowerCamelCase_	  =					[]

       for i, rect in enumerate(A_						):
             lowerCamelCase_	  =					fill.copy().set_fill(A_		,							opacity=0.8						)
             target.move_to(A_						)
             model_arr.append(A_						)

             lowerCamelCase_	  =					Rectangle(height=0.46		,							width=0.46						).set_stroke(width=0.0						).set_fill(A_		,							opacity=0.8						)
             cpu_target.move_to(cpu_left_col_base[i]						)
             model_cpu_arr.append(A_						)

       self.add(*A_		,							*A_						)

       lowerCamelCase_	  =					[meta_mem.copy() for i in range(6						)]
       lowerCamelCase_	  =					[meta_mem.copy() for i in range(6						)]
       lowerCamelCase_	  =					VGroup(*A_						).arrange(A_		,							buff=0						)
       lowerCamelCase_	  =					VGroup(*A_						).arrange(A_		,							buff=0						)
       lowerCamelCase_	  =					VGroup(A_		,							A_						).arrange(A_		,							buff=0						)
       lowerCamelCase_	  =					Text('Disk'		,							font_size=24						)
       lowerCamelCase_	  =					Group(A_		,							A_						).arrange(A_		,							buff=0.5		,							aligned_edge=A_						)
       disk.move_to([-4, -1.25, 0]						)
       self.add(A_		,							A_						)

       lowerCamelCase_	  =					Square(side_length=2.2						)
       key.move_to([-5, 2, 0]						)

       lowerCamelCase_	  =					MarkupText(
           f"""<b>Key:</b>\n\n<span fgcolor='{YELLOW}'>●</span> Empty Model"""		,							font_size=18		,							)

       key_text.move_to([-5, 2.4, 0]						)

       self.add(A_		,							A_						)

       lowerCamelCase_	  =					MarkupText(
           f"""<span fgcolor='{BLUE}'>●</span> Checkpoint"""		,							font_size=18		,							)

       blue_text.next_to(A_		,							DOWN * 2.4		,							aligned_edge=key_text.get_left()						)
       self.add(A_						)

       lowerCamelCase_	  =					MarkupText(
           f"""Now watch as an input is passed through the model\nand how the memory is utilized and handled."""		,							font_size=24		,							)
       step_a.move_to([2, 2, 0]						)

       self.play(Write(A_						)						)

       lowerCamelCase_	  =					Square(0.3						)
       input.set_fill(A_		,							opacity=1.0						)
       input.set_stroke(width=0.0						)
       input.next_to(model_base[0]		,							A_		,							buff=0.5						)

       self.play(Write(A_						)						)

       input.generate_target()
       input.target.next_to(model_arr[0]		,							direction=A_		,							buff=0.02						)
       self.play(MoveToTarget(A_						)						)

       self.play(FadeOut(A_						)						)

       lowerCamelCase_	  =					Arrow(start=A_		,							end=A_		,							color=A_		,							buff=0.5						)
       a.next_to(model_arr[0].get_left()		,							A_		,							buff=0.2						)

       model_cpu_arr[0].generate_target()
       model_cpu_arr[0].target.move_to(gpu_rect[0]						)

       lowerCamelCase_	  =					MarkupText(
           f"""As the input reaches a layer, the hook triggers\nand weights are moved from the CPU\nto the GPU and back."""		,							font_size=24		,							)
       step_a.move_to([2, 2, 0]						)

       self.play(Write(A_		,							run_time=3						)						)

       lowerCamelCase_	  =					{'run_time': 1, 'fade_in': True, 'fade_out': True, 'buff': 0.02}

       self.play(
           Write(A_						)		,							Circumscribe(model_arr[0]		,							color=A_		,							**A_						)		,							Circumscribe(model_cpu_arr[0]		,							color=A_		,							**A_						)		,							Circumscribe(gpu_rect[0]		,							color=A_		,							**A_						)		,							)
       self.play(MoveToTarget(model_cpu_arr[0]						)						)

       lowerCamelCase_	  =					a.copy()
       for i in range(6						):
             a_c.next_to(model_arr[i].get_right() + 0.02		,							A_		,							buff=0.2						)

             input.generate_target()
             input.target.move_to(model_arr[i].get_right() + 0.02						)

             lowerCamelCase_	  =					AnimationGroup(
                 FadeOut(A_		,							run_time=0.5						)		,							MoveToTarget(A_		,							run_time=0.5						)		,							FadeIn(A_		,							run_time=0.5						)		,							lag_ratio=0.2						)

             self.play(A_						)

             model_cpu_arr[i].generate_target()
             model_cpu_arr[i].target.move_to(cpu_left_col_base[i]						)

             if i < 5:
                   model_cpu_arr[i + 1].generate_target()
                   model_cpu_arr[i + 1].target.move_to(gpu_rect[0]						)
                   if i >= 1:
                         lowerCamelCase_	  =					0.7

                   self.play(
                       Circumscribe(model_arr[i]		,							**A_						)		,							Circumscribe(cpu_left_col_base[i]		,							**A_						)		,							Circumscribe(cpu_left_col_base[i + 1]		,							color=A_		,							**A_						)		,							Circumscribe(gpu_rect[0]		,							color=A_		,							**A_						)		,							Circumscribe(model_arr[i + 1]		,							color=A_		,							**A_						)		,							)
                   if i < 1:
                         self.play(
                             MoveToTarget(model_cpu_arr[i]						)		,							MoveToTarget(model_cpu_arr[i + 1]						)		,							)
                   else:
                         self.play(
                             MoveToTarget(model_cpu_arr[i]		,							run_time=0.7						)		,							MoveToTarget(model_cpu_arr[i + 1]		,							run_time=0.7						)		,							)
             else:
                   model_cpu_arr[i].generate_target()
                   model_cpu_arr[i].target.move_to(cpu_left_col_base[-1]						)
                   input.generate_target()
                   input.target.next_to(model_arr[-1].get_right()		,							RIGHT + 0.02		,							buff=0.2						)

                   self.play(
                       Circumscribe(model_arr[-1]		,							color=A_		,							**A_						)		,							Circumscribe(cpu_left_col_base[-1]		,							color=A_		,							**A_						)		,							Circumscribe(gpu_rect[0]		,							color=A_		,							**A_						)		,							)

                   self.play(MoveToTarget(model_cpu_arr[i]						)						)

             lowerCamelCase_	  =					a_c
             lowerCamelCase_	  =					a_c.copy()

       input.generate_target()
       input.target.next_to(model_base[-1]		,							RIGHT + 0.02		,							buff=0.5						)
       self.play(
           FadeOut(A_						)		,							FadeOut(A_		,							run_time=0.5						)		,							)

       lowerCamelCase_	  =					MarkupText(f"""Inference on a model too large for GPU memory\nis successfully completed."""		,							font_size=24						)
       step_a.move_to([2, 2, 0]						)

       self.play(Write(A_		,							run_time=3						)		,							MoveToTarget(A_						)						)

       self.wait()



