text: from manim_imports_ext import *
  import math
  
  
  class StreamIntro(Scene):
      def construct(self):
          # Add logo
          logo = Logo()
          spikes = VGroup(*[
              spike
              for layer in logo.spike_layers
              for spike in layer
          ])
          self.add(*logo.family_members_with_points())
  
          # Add label
          label = TexText("The lesson will\\\\begin shortly")
          label.scale(2)
          label.next_to(logo, DOWN)
          self.add(label)
  
          self.camera.frame.move_to(DOWN)
  
          for spike in spikes:
              point = spike.get_start()
              spike.angle = angle_of_vector(point)
  
          anims = []
          for spike in spikes:
              anims.append(Rotate(
                  spike, spike.angle * 28 * 2,
                  about_point=ORIGIN,
                  rate_func=linear,
              ))
          self.play(*anims, run_time=60 * 5)
          self.wait(20)
  
  
  class OldStreamIntro(Scene):
      def construct(self):
          morty = Mortimer()
          morty.flip()
          morty.set_height(2)
          morty.to_corner(DL)
          self.play(PiCreatureSays(
              morty, "The lesson will\\\\begin soon.",
              bubble_kwargs={
                  "height": 2,
                  "width": 3,
              },
              target_mode="hooray",
          ))
          bound = AnimatedBoundary(morty.bubble.content, max_stroke_width=1)
          self.add(bound, morty.bubble, morty.bubble.content)
          self.remove(morty.bubble.content)
          morty.bubble.set_fill(opacity=0)
  
          self.camera.frame.scale(0.6, about_edge=DL)
  
          self.play(Blink(morty))
          self.wait(5)
          self.play(Blink(morty))
          self.wait(3)
          return
  
          text = TexText("The lesson will\\\\begin soon.")
          text.set_height(1.5)
          text.to_corner(DL, buff=LARGE_BUFF)
          self.add(text)
  
  
  class QuadraticFormula(TeacherStudentsScene):
      def construct(self):
          formula = Tex(
              "\\frac{-b \\pm \\sqrt{b^2 - 4ac}}{2a}",
          )
          formula.next_to(self.students, UP, buff=MED_LARGE_BUFF, aligned_edge=LEFT)
          self.add(formula)
  
          self.change_student_modes(
              "angry", "tired", "sad",
              look_at_arg=formula,
          )
          self.teacher_says(
              "It doesn't have\\\\to be this way.",
              bubble_kwargs={
                  "width": 4,
                  "height": 3,
              }
          )
          self.wait(5)
          self.change_student_modes(
              "pondering", "thinking", "erm",
              look_at_arg=formula
          )
          self.wait(12)
  
  
  class SimplerQuadratic(Scene):
      def construct(self):
          tex = Tex("m \\pm \\sqrt{m^2 - p}")
          tex.set_stroke(BLACK, 12, background=True)
          tex.scale(1.5)
          self.add(tex)
  
  
  class CosGraphs(Scene):
      def construct(self):
          axes = Axes(
              x_min=-0.75 * TAU,
              x_max=0.75 * TAU,
              y_min=-1.5,
              y_max=1.5,
              x_axis_config={
                  "tick_frequency": PI / 4,
                  "include_tip": False,
              },
              y_axis_config={
                  "tick_frequency": 0.5,
                  "include_tip": False,
                  "unit_size": 1.5,
              }
          )
  
          graph1 = axes.get_graph(np.cos)
          graph2 = axes.get_graph(lambda x: np.cos(x)**2)
  
          graph1.set_stroke(YELLOW, 5)
          graph2.set_stroke(BLUE, 5)
  
          label1 = Tex("\\cos(x)")
          label2 = Tex("\\cos^2(x)")
  
          label1.match_color(graph1)
          label1.set_height(0.75)
          label1.next_to(axes.input_to_graph_point(-PI, graph1), DOWN)
  
          label2.match_color(graph2)
          label2.set_height(0.75)
          label2.next_to(axes.input_to_graph_point(PI, graph2), UP)
  
          for mob in [graph1, graph2, label1, label2]:
              mc = mob.copy()
              mc.set_stroke(BLACK, 10, background=True)
              self.add(mc)
  
          self.add(axes)
          self.add(graph1)
          self.add(graph2)
          self.add(label1)
          self.add(label2)
  
          self.embed()
  
  
  class SineWave(Scene):
      def construct(self):
          w_axes = self.get_wave_axes()
          square, circle, c_axes = self.get_edge_group()
  
          self.add(w_axes)
          self.add(square, circle, c_axes)
  
          theta_tracker = ValueTracker(0)
          c_dot = Dot(color=YELLOW)
          c_line = Line(DOWN, UP, color=GREEN)
          w_dot = Dot(color=YELLOW)
          w_line = Line(DOWN, UP, color=GREEN)
  
          def update_c_dot(dot, axes=c_axes, tracker=theta_tracker):
              theta = tracker.get_value()
              dot.move_to(axes.c2p(
                  np.cos(theta),
                  np.sin(theta),
              ))
  
          def update_c_line(line, axes=c_axes, tracker=theta_tracker):
              theta = tracker.get_value()
              x = np.cos(theta)
              y = np.sin(theta)
              if y == 0:
                  y = 1e-6
              line.put_start_and_end_on(
                  axes.c2p(x, 0),
                  axes.c2p(x, y),
              )
  
          def update_w_dot(dot, axes=w_axes, tracker=theta_tracker):
              theta = tracker.get_value()
              dot.move_to(axes.c2p(theta, np.sin(theta)))
  
          def update_w_line(line, axes=w_axes, tracker=theta_tracker):
              theta = tracker.get_value()
              x = theta
              y = np.sin(theta)
              if y == 0:
                  y = 1e-6
              line.put_start_and_end_on(
                  axes.c2p(x, 0),
                  axes.c2p(x, y),
              )
  
          def get_partial_circle(circle=circle, tracker=theta_tracker):
              result = circle.copy()
              theta = tracker.get_value()
              result.pointwise_become_partial(
                  circle, 0, clip(theta / TAU, 0, 1),
              )
              result.set_stroke(RED, width=3)
              return result
  
          def get_partial_wave(axes=w_axes, tracker=theta_tracker):
              theta = tracker.get_value()
              graph = axes.get_graph(np.sin, x_min=0, x_max=theta, step_size=0.025)
              graph.set_stroke(BLUE, 3)
              return graph
  
          def get_h_line(axes=w_axes, tracker=theta_tracker):
              theta = tracker.get_value()
              return Line(
                  axes.c2p(0, 0),
                  axes.c2p(theta, 0),
                  stroke_color=RED
              )
  
          c_dot.add_updater(update_c_dot)
          c_line.add_updater(update_c_line)
          w_dot.add_updater(update_w_dot)
          w_line.add_updater(update_w_line)
          partial_circle = always_redraw(get_partial_circle)
          partial_wave = always_redraw(get_partial_wave)
          h_line = always_redraw(get_h_line)
  
          self.add(partial_circle)
          self.add(partial_wave)
          self.add(h_line)
          self.add(c_line, c_dot)
          self.add(w_line, w_dot)
  
          sin_label = Tex(
              "\\sin\\left(\\theta\\right)",
              tex_to_color_map={"\\theta": RED}
          )
          sin_label.next_to(w_axes.get_top(), UR)
          self.add(sin_label)
  
          self.play(
              theta_tracker.set_value, 1.25 * TAU,
              run_time=15,
              rate_func=linear,
          )
  
      def get_wave_axes(self):
          wave_axes = Axes(
              x_min=0,
              x_max=1.25 * TAU,
              y_min=-1.0,
              y_max=1.0,
              x_axis_config={
                  "tick_frequency": TAU / 8,
                  "unit_size": 1.0,
              },
              y_axis_config={
                  "tick_frequency": 0.5,
                  "include_tip": False,
                  "unit_size": 1.5,
              }
          )
          wave_axes.y_axis.add_numbers(
              -1, 1, num_decimal_places=1
          )
          wave_axes.to_edge(RIGHT, buff=MED_SMALL_BUFF)
  
          pairs = [
              (PI / 2, "\\frac{\\pi}{2}"),
              (PI, "\\pi"),
              (3 * PI / 2, "\\frac{3\\pi}{2}"),
              (2 * PI, "2\\pi"),
          ]
          syms = VGroup()
          for val, tex in pairs:
              sym = Tex(tex)
              sym.scale(0.5)
              sym.next_to(wave_axes.c2p(val, 0), DOWN, MED_SMALL_BUFF)
              syms.add(sym)
          wave_axes.add(syms)
  
          theta = Tex("\\theta")
          theta.set_color(RED)
          theta.next_to(wave_axes.x_axis.get_end(), UP)
          wave_axes.add(theta)
  
          return wave_axes
  
      def get_edge_group(self):
          axes_max = 1.25
          radius = 1.5
          axes = Axes(
              x_min=-axes_max,
              x_max=axes_max,
              y_min=-axes_max,
              y_max=axes_max,
              axis_config={
                  "tick_frequency": 0.5,
                  "include_tip": False,
                  "numbers_with_elongated_ticks": [-1, 1],
                  "tick_size": 0.05,
                  "unit_size": radius,
              },
          )
          axes.to_edge(LEFT, buff=MED_LARGE_BUFF)
  
          background = SurroundingRectangle(axes, buff=MED_SMALL_BUFF)
          background.set_stroke(WHITE, 1)
          background.set_fill(GREY_E, 1)
  
          circle = Circle(radius=radius)
          circle.move_to(axes)
          circle.set_stroke(WHITE, 1)
  
          nums = VGroup()
          for u in 1, -1:
              num = Integer(u)
              num.set_height(0.2)
              num.set_stroke(BLACK, 3, background=True)
              num.next_to(axes.c2p(u, 0), DOWN + u * RIGHT, SMALL_BUFF)
              nums.add(num)
  
          axes.add(nums)
  
          return background, circle, axes
  
  
  class CosWave(SineWave):
      CONFIG = {
          "include_square": False,
      }
  
      def construct(self):
          w_axes = self.get_wave_axes()
          square, circle, c_axes = self.get_edge_group()
  
          self.add(w_axes)
          self.add(square, circle, c_axes)
  
          theta_tracker = ValueTracker(0)
          c_dot = Dot(color=YELLOW)
          c_line = Line(DOWN, UP, color=GREEN)
          w_dot = Dot(color=YELLOW)
          w_line = Line(DOWN, UP, color=GREEN)
  
          def update_c_dot(dot, axes=c_axes, tracker=theta_tracker):
              theta = tracker.get_value()
              dot.move_to(axes.c2p(
                  np.cos(theta),
                  np.sin(theta),
              ))
  
          def update_c_line(line, axes=c_axes, tracker=theta_tracker):
              theta = tracker.get_value()
              x = np.cos(theta)
              y = np.sin(theta)
              line.set_points_as_corners([
                  axes.c2p(0, y),
                  axes.c2p(x, y),
              ])
  
          def update_w_dot(dot, axes=w_axes, tracker=theta_tracker):
              theta = tracker.get_value()
              dot.move_to(axes.c2p(theta, np.cos(theta)))
  
          def update_w_line(line, axes=w_axes, tracker=theta_tracker):
              theta = tracker.get_value()
              x = theta
              y = np.cos(theta)
              if y == 0:
                  y = 1e-6
              line.set_points_as_corners([
                  axes.c2p(x, 0),
                  axes.c2p(x, y),
              ])
  
          def get_partial_circle(circle=circle, tracker=theta_tracker):
              result = circle.copy()
              theta = tracker.get_value()
              result.pointwise_become_partial(
                  circle, 0, clip(theta / TAU, 0, 1),
              )
              result.set_stroke(RED, width=3)
              return result
  
          def get_partial_wave(axes=w_axes, tracker=theta_tracker):
              theta = tracker.get_value()
              graph = axes.get_graph(np.cos, x_min=0, x_max=theta, step_size=0.025)
              graph.set_stroke(PINK, 3)
              return graph
  
          def get_h_line(axes=w_axes, tracker=theta_tracker):
              theta = tracker.get_value()
              return Line(
                  axes.c2p(0, 0),
                  axes.c2p(theta, 0),
                  stroke_color=RED
              )
  
          def get_square(line=c_line):
              square = Square()
              square.set_stroke(WHITE, 1)
              square.set_fill(MAROON_B, opacity=0.5)
              square.match_width(line)
              square.move_to(line, DOWN)
              return square
  
          def get_square_graph(axes=w_axes, tracker=theta_tracker):
              theta = tracker.get_value()
              graph = axes.get_graph(
                  lambda x: np.cos(x)**2, x_min=0, x_max=theta, step_size=0.025
              )
              graph.set_stroke(MAROON_B, 3)
              return graph
  
          c_dot.add_updater(update_c_dot)
          c_line.add_updater(update_c_line)
          w_dot.add_updater(update_w_dot)
          w_line.add_updater(update_w_line)
          h_line = always_redraw(get_h_line)
          partial_circle = always_redraw(get_partial_circle)
          partial_wave = always_redraw(get_partial_wave)
  
          self.add(partial_circle)
          self.add(partial_wave)
          self.add(h_line)
          self.add(c_line, c_dot)
          self.add(w_line, w_dot)
  
          if self.include_square:
              self.add(always_redraw(get_square))
              self.add(always_redraw(get_square_graph))
  
          cos_label = Tex(
              "\\cos\\left(\\theta\\right)",
              tex_to_color_map={"\\theta": RED}
          )
          cos_label.next_to(w_axes.get_top(), UR)
          self.add(cos_label)
  
          self.play(
              theta_tracker.set_value, 1.25 * TAU,
              run_time=15,
              rate_func=linear,
          )
  
  
  class CosSquare(CosWave):
      CONFIG = {
          "include_square": True
      }
  
  
  class ComplexNumberPreview(Scene):
      def construct(self):
          plane = ComplexPlane(axis_config={"stroke_width": 4})
          plane.add_coordinates()
  
          z = complex(2, 1)
          dot = Dot()
          dot.move_to(plane.n2p(z))
          label = Tex("2+i")
          label.set_color(YELLOW)
          dot.set_color(YELLOW)
          label.next_to(dot, UR, SMALL_BUFF)
          label.set_stroke(BLACK, 5, background=True)
  
          line = Line(plane.n2p(0), plane.n2p(z))
          arc = Arc(start_angle=0, angle=np.log(z).imag, radius=0.5)
  
          self.add(plane)
          self.add(line, arc)
          self.add(dot)
          self.add(label)
  
          self.embed()
  
  
  class ComplexMultiplication(Scene):
      def construct(self):
          # Add plane
          plane = ComplexPlane()
          plane.add_coordinates()
  
          z = complex(2, 1)
          z_dot = Dot(color=PINK)
          z_dot.move_to(plane.n2p(z))
          z_label = Tex("z")
          z_label.next_to(z_dot, UR, buff=0.5 * SMALL_BUFF)
          z_label.match_color(z_dot)
  
          self.add(plane)
          self.add(z_dot)
          self.add(z_label)
  
          # Show 1
          one_vect = Vector(RIGHT)
          one_vect.set_color(YELLOW)
          one_vect.target = Vector(plane.n2p(z))
          one_vect.target.match_style(one_vect)
  
          z_rhs = Tex("=", "z \\cdot 1")
          z_rhs[1].match_color(one_vect)
          z_rhs.next_to(z_label, RIGHT, 1.5 * SMALL_BUFF, aligned_edge=DOWN)
          z_rhs.set_stroke(BLACK, 3, background=True)
  
          one_label, i_label = [l for l in plane.coordinate_labels if l.get_value() == 1]
  
          self.play(GrowArrow(one_vect))
          self.wait()
          self.add(one_vect, z_dot)
          self.play(
              MoveToTarget(one_vect),
              TransformFromCopy(one_label, z_rhs),
          )
          self.wait()
  
          # Show i
          i_vect = Vector(UP, color=GREEN)
          zi_point = plane.n2p(z * complex(0, 1))
          i_vect.target = Vector(zi_point)
          i_vect.target.match_style(i_vect)
          i_vect_label = Tex("z \\cdot i")
          i_vect_label.match_color(i_vect)
          i_vect_label.set_stroke(BLACK, 3, background=True)
          i_vect_label.next_to(zi_point, UL, SMALL_BUFF)
  
          self.play(GrowArrow(i_vect))
          self.wait()
          self.play(
              MoveToTarget(i_vect),
              TransformFromCopy(i_label, i_vect_label),
              run_time=1,
          )
          self.wait()
  
          self.play(
              TransformFromCopy(one_vect, i_vect.target, path_arc=-90 * DEGREES),
          )
          self.wait()
  
          # Transform plane
          plane.generate_target()
          for mob in plane.target.family_members_with_points():
              if isinstance(mob, Line):
                  mob.set_stroke(GREY, opacity=0.5)
          new_plane = ComplexPlane(faded_line_ratio=0)
  
          self.remove(plane)
          self.add(plane, new_plane, *self.mobjects)
  
          new_plane.generate_target()
          new_plane.target.apply_complex_function(lambda w, z=z: w * z)
  
          self.play(
              MoveToTarget(plane),
              MoveToTarget(new_plane),
              run_time=6,
              rate_func=there_and_back_with_pause
          )
          self.wait()
  
          # Show Example Point
          w = complex(2, -1)
          w_dot = Dot(plane.n2p(w), color=WHITE)
          one_vects = VGroup(*[Vector(RIGHT) for x in range(2)])
          one_vects.arrange(RIGHT, buff=0)
          one_vects.move_to(plane.n2p(0), LEFT)
          one_vects.set_color(YELLOW)
          new_i_vect = Vector(DOWN)
          new_i_vect.move_to(plane.n2p(2), UP)
          new_i_vect.set_color(GREEN)
          vects = VGroup(*one_vects, new_i_vect)
          vects.set_opacity(0.8)
  
          w_group = VGroup(*vects, w_dot)
          w_group.target = VGroup(
              one_vect.copy().set_opacity(0.8),
              one_vect.copy().shift(plane.n2p(z)).set_opacity(0.8),
              i_vect.copy().rotate(PI, about_point=ORIGIN).shift(2 * plane.n2p(z)).set_opacity(0.8),
              Dot(plane.n2p(w * z), color=WHITE)
          )
  
          self.play(FadeInFromLarge(w_dot))
          self.wait()
          self.play(ShowCreation(vects))
          self.wait()
  
          self.play(
              MoveToTarget(plane),
              MoveToTarget(new_plane),
              MoveToTarget(w_group),
              run_time=2,
              path_arc=np.log(z).imag,
          )
          self.wait()
  
  
  class RotatePiCreature(Scene):
      def construct(self):
          randy = Randolph(mode="thinking")
          randy.set_height(6)
  
          plane = ComplexPlane(x_min=-12, x_max=12)
          plane.add_coordinates()
  
          self.camera.frame.move_to(3 * RIGHT)
  
          self.add(randy)
          self.wait()
          self.play(Rotate(randy, 30 * DEGREES, run_time=3))
          self.wait()
          self.play(Rotate(randy, -30 * DEGREES))
  
          self.add(plane, randy)
          self.play(
              ShowCreation(plane),
              randy.set_opacity, 0.75,
          )
          self.wait()
  
          dots = VGroup()
          for mob in randy.family_members_with_points():
              for point in mob.get_anchors():
                  dot = Dot(point)
                  dot.set_height(0.05)
                  dots.add(dot)
  
          self.play(ShowIncreasingSubsets(dots))
          self.wait()
  
          label = VGroup(
              Tex("(x + iy)"),
              Vector(DOWN),
              Tex("(\\cos(30^\\circ) + i\\sin(30^\\circ))", "(x + iy)"),
          )
          label[2][0].set_color(YELLOW)
          label.arrange(DOWN)
          label.to_corner(DR)
          label.shift(3 * RIGHT)
  
          for mob in label:
              mob.add_background_rectangle()
  
          self.play(FadeIn(label))
          self.wait()
  
          randy.add(dots)
          self.play(Rotate(randy, 30 * DEGREES), run_time=3)
          self.wait()
  
  
  class ExpMeaning(Scene):
      CONFIG = {
          "include_circle": True
      }
  
      def construct(self):
          # Plane
          plane = ComplexPlane(y_min=-6, y_max=6)
          plane.shift(1.5 * DOWN)
          plane.add_coordinates()
          if self.include_circle:
              circle = Circle(radius=1)
              circle.set_stroke(RED, 1)
              circle.move_to(plane.n2p(0))
              plane.add(circle)
  
          # Equation
          equation = Tex(
              "\\text{exp}(i\\theta) = ",
              "1 + ",
              "i\\theta + ",
              "{(i\\theta)^2 \\over 2} + ",
              "{(i\\theta)^3 \\over 6} + ",
              "{(i\\theta)^4 \\over 24} + ",
              "\\cdots",
              tex_to_color_map={
                  "\\theta": YELLOW,
                  "i": GREEN,
              },
          )
          equation.add_background_rectangle(buff=MED_SMALL_BUFF, opacity=1)
          equation.to_edge(UL, buff=0)
  
          # Label
          theta_tracker = ValueTracker(0)
          theta_label = VGroup(
              Tex("\\theta = "),
              DecimalNumber(0, num_decimal_places=4)
          )
          theta_decimal = theta_label[1]
          theta_decimal.add_updater(
              lambda m, tt=theta_tracker: m.set_value(tt.get_value())
          )
          theta_label.arrange(RIGHT, buff=SMALL_BUFF)
          theta_label.set_color(YELLOW)
          theta_label.add_to_back(BackgroundRectangle(
              theta_label,
              buff=MED_SMALL_BUFF,
              fill_opacity=1,
          ))
          theta_label.next_to(equation, DOWN, aligned_edge=LEFT, buff=0)
  
          # Vectors
          def get_vectors(n_vectors=20, plane=plane, tracker=theta_tracker):
              last_tip = plane.n2p(0)
              z = complex(0, tracker.get_value())
              vects = VGroup()
              colors = color_gradient([GREEN, YELLOW, RED], 6)
              for i, color in zip(range(n_vectors), it.cycle(colors)):
                  vect = Vector(complex_to_R3(z**i / math.factorial(i)))
                  vect.set_color(color)
                  vect.shift(last_tip)
                  last_tip = vect.get_end()
                  vects.add(vect)
              return vects
  
          vectors = always_redraw(get_vectors)
          dot = Dot()
          dot.set_height(0.03)
          dot.add_updater(lambda m, vs=vectors: m.move_to(vs[-1].get_end()))
  
          self.add(plane)
          self.add(vectors)
          self.add(dot)
          self.add(equation)
          self.add(theta_label)
  
          self.play(
              theta_tracker.set_value, 1,
              run_time=3,
              rate_func=smooth,
          )
          self.wait()
          for target in PI, TAU:
              self.play(
                  theta_tracker.set_value, target,
                  run_time=10,
              )
              self.wait()
  
          self.embed()
  
  
  class ExpMeaningWithoutCircle(ExpMeaning):
      CONFIG = {
          "include_circle": False,
      }
  
  
  class PositionAndVelocityExample(Scene):
      def construct(self):
          plane = NumberPlane()
  
          self.add(plane)
  
          self.embed()
  
  
  class EulersFormula(Scene):
      def construct(self):
          kw = {"tex_to_color_map": {"\\theta": YELLOW}}
          formula = Tex(
              "&e^{i\\theta} = \\\\ &\\cos\\left(\\theta\\right) + i\\cdot\\sin\\left(\\theta\\right)",
          )[0]
          formula[:4].scale(2, about_edge=UL)
          formula[:4].shift(SMALL_BUFF * RIGHT + MED_LARGE_BUFF * UP)
          VGroup(formula[2], formula[8], formula[17]).set_color(YELLOW)
          formula.scale(1.5)
          formula.set_stroke(BLACK, 5, background=True)
          self.add(formula)
  
  
  class EtoILimit(Scene):
      def construct(self):
          tex = Tex(
              "\\lim_{n \\to \\infty} \\left(1 + \\frac{it}{n}\\right)^n",
          )[0]
          VGroup(tex[3], tex[12], tex[14]).set_color(YELLOW)
          tex[9].set_color(BLUE)
          tex.scale(1.5)
          tex.set_stroke(BLACK, 5, background=True)
          # self.add(tex)
  
          text = TexText("Interest rate\\\\of ", "$\\sqrt{-1}$")
          text[1].set_color(BLUE) 
          text.scale(1.5)
          text.set_stroke(BLACK, 5, background=True)
          self.add(text)
  
  
  class ImaginaryInterestRates(Scene):
      def construct(self):
          plane = ComplexPlane(x_min=-20, x_max=20, y_min=-20, y_max=20)
          plane.add_coordinate_labels()
          circle = Circle(radius=1)
          circle.set_stroke(YELLOW, 1)
          self.add(plane, circle)
  
          frame = self.camera.frame
          frame.save_state()
          frame.generate_target()
          frame.target.set_width(25)
          frame.target.move_to(8 * RIGHT + 2 * DOWN)
  
          dt_tracker = ValueTracker(1)
  
          def get_vectors(tracker=dt_tracker, plane=plane, T=8):
              dt = tracker.get_value()
              last_z = 1
              vects = VGroup()
              for t in np.arange(0, T, dt):
                  next_z = last_z + complex(0, 1) * last_z * dt
                  vects.add(Arrow(
                      plane.n2p(last_z),
                      plane.n2p(next_z),
                      buff=0,
                  ))
                  last_z = next_z
              vects.set_submobject_colors_by_gradient(YELLOW, GREEN, BLUE)
              return vects
  
          vects = get_vectors()
  
          line = Line()
          line.add_updater(lambda m, v=vects: m.put_start_and_end_on(
              ORIGIN, v[-1].get_start() if len(v) > 0 else RIGHT,
          ))
  
          self.add(line)
          self.play(
              ShowIncreasingSubsets(
                  vects,
                  rate_func=linear,
                  int_func=np.ceil,
              ),
              MoveToTarget(
                  frame,
                  rate_func=squish_rate_func(smooth, 0.5, 1),
              ),
              run_time=8,
          )
          self.wait()
          self.play(FadeOut(line))
  
          self.remove(vects)
          vects = always_redraw(get_vectors)
          self.add(vects)
          self.play(
              Restore(frame),
              dt_tracker.set_value, 0.2,
              run_time=5,
          )
          self.wait()
          self.play(dt_tracker.set_value, 0.01, run_time=3)
          vects.clear_updaters()
          self.wait()
  
          theta_tracker = ValueTracker(0)
  
          def get_arc(tracker=theta_tracker):
              theta = tracker.get_value()
              arc = Arc(
                  radius=1,
                  stroke_width=3,
                  stroke_color=RED,
                  start_angle=0,
                  angle=theta
              )
              return arc
  
          arc = always_redraw(get_arc)
          dot = Dot()
          dot.add_updater(lambda m, arc=arc: m.move_to(arc.get_end()))
  
          label = VGroup(
              DecimalNumber(0, num_decimal_places=3),
              TexText("Years")
          )
          label.arrange(RIGHT, aligned_edge=DOWN)
          label.move_to(3 * LEFT + 1.5 * UP)
  
          label[0].set_color(RED)
          label[0].add_updater(lambda m, tt=theta_tracker: m.set_value(tt.get_value()))
  
          self.add(BackgroundRectangle(label), label, arc, dot)
          for n in range(1, 5):
              target = n * PI / 2
              self.play(
                  theta_tracker.set_value, target,
                  run_time=3
              )
              self.wait(2)
  
  
  class Logs(Scene):
      def construct(self):
          log = Tex(
              "&\\text{log}(ab) = \\\\ &\\text{log}(a) + \\text{log}(b)",
              tex_to_color_map={"a": BLUE, "b": YELLOW},
              alignment="",
          )
          
          log.scale(1.5)
          log.set_stroke(BLACK, 5, background=True)
  
          self.add(log)
  
  
  class LnX(Scene):
      def construct(self):
          sym = Tex("\\ln(x)")
          sym.scale(3)
          sym.shift(UP)
          sym.set_stroke(BLACK, 5, background=True)
  
          word = TexText("Natural?")
          word.scale(1.5)
          word.set_color(YELLOW)
          word.set_stroke(BLACK, 5, background=True)
          word.next_to(sym, DOWN, buff=0.5)
          arrow = Arrow(word.get_top(), sym[0][1].get_bottom())
  
          self.add(sym, word, arrow)
  
  
  class HarmonicSum(Scene):
      def construct(self):
          axes = Axes(
              x_min=0,
              x_max=13,
              y_min=0,
              y_max=1.25,
              y_axis_config={
                  "unit_size": 4,
                  "tick_frequency": 0.25,
              }
          )
          axes.to_corner(DL, buff=1)
          axes.x_axis.add_numbers()
          axes.y_axis.add_numbers(
              *np.arange(0.25, 1.25, 0.25),
              num_decimal_places=2,
          )
          self.add(axes)
  
          graph = axes.get_graph(lambda x: 1 / x, x_min=0.1, x_max=15)
          graph_fill = graph.copy()
          graph_fill.add_line_to(axes.c2p(15, 0))
          graph_fill.add_line_to(axes.c2p(1, 0))
          graph_fill.add_line_to(axes.c2p(1, 1))
          graph.set_stroke(WHITE, 3)
          graph_fill.set_fill(BLUE_E, 0.5)
          graph_fill.set_stroke(width=0)
          self.add(graph, graph_fill)
  
          bars = VGroup()
          bar_labels = VGroup()
          for x in range(1, 15):
              line = Line(axes.c2p(x, 0), axes.c2p(x + 1, 1 / x))
              bar = Rectangle()
              bar.set_fill(GREEN_E, 1)
              bar.replace(line, stretch=True)
              bars.add(bar)
  
              label = Tex(f"1 \\over {x}")
              label.set_height(0.7)
              label.next_to(bar, UP, SMALL_BUFF)
              bar_labels.add(label)
  
          bars.set_submobject_colors_by_gradient(GREEN_C, GREEN_E)
          bars.set_stroke(WHITE, 1)
          bars.set_fill(opacity=0.25)
  
          self.add(bars)
          self.add(bar_labels)
  
  
          self.embed()
  
  
  class PowerTower(Scene):
      def construct(self):
          mob = Tex("4 = x^{x^{{x^{x^{x^{\cdot^{\cdot^{\cdot}}}}}}}}")
          mob[0][-1].shift(0.1 * DL)
          mob[0][-2].shift(0.05 * DL)
  
          mob.set_height(4)
          mob.set_stroke(BLACK, 5, background=True)
  
          self.add(mob)
  
  
  class ItoTheI(Scene):
      def construct(self):
          tex = Tex("i^i")
          # tex = Tex("\\sqrt{-1}^{\\sqrt{-1}}")
          tex.set_height(3)
          tex.set_stroke(BLACK, 8, background=True)
          self.add(tex)
  
  
  class ComplexExponentialPlay(Scene):
      def setup(self):
          self.transform_alpha = 0
  
      def construct(self):
          # Plane
          plane = ComplexPlane(
              x_min=-2 * FRAME_WIDTH,
              x_max=2 * FRAME_WIDTH,
              y_min=-2 * FRAME_HEIGHT,
              y_max=2 * FRAME_HEIGHT,
          )
          plane.add_coordinates()
          self.add(plane)
  
          # R Dot
          r_dot = Dot(color=YELLOW)
  
          def update_r_dot(dot, point_tracker=self.mouse_drag_point):
              point = point_tracker.get_location()
              if abs(point[0]) < 0.1:
                  point[0] = 0
              if abs(point[1]) < 0.1:
                  point[1] = 0
              dot.move_to(point)
  
          r_dot.add_updater(update_r_dot)
          self.mouse_drag_point.move_to(plane.n2p(1))
  
          # Transformed sample dots
          def func(z, dot=r_dot, plane=plane):
              r = plane.p2n(dot.get_center())
              result = np.exp(r * z)
              if abs(result) > 20:
                  result *= 20 / abs(result)
              return result
  
          sample_dots = VGroup()
          dot_template = Dot(radius=0.05)
          dot_template.set_opacity(0.8)
          spacing = 0.05
          for x in np.arange(-7, 7, spacing):
              dot = dot_template.copy()
              dot.set_color(TEAL)
              dot.z = x
              dot.move_to(plane.n2p(dot.z))
              sample_dots.add(dot)
          for y in np.arange(-6, 6, spacing):
              dot = dot_template.copy()
              dot.set_color(MAROON)
              dot.z = complex(0, y)
              dot.move_to(plane.n2p(dot.z))
              sample_dots.add(dot)
  
          special_values = [1, complex(0, 1), -1, complex(0, -1)]
          special_dots = VGroup(*[
              list(filter(lambda d: abs(d.z - x) < 0.01, sample_dots))[0]
              for x in special_values
          ])
          for dot in special_dots:
              dot.set_fill(opacity=1)
              dot.scale(1.2)
              dot.set_stroke(WHITE, 2)
  
          sample_dots.save_state()
  
          def update_sample(sample, f=func, plane=plane, scene=self):
              sample.restore()
              sample.apply_function_to_submobject_positions(
                  lambda p: interpolate(
                      p,
                      plane.n2p(f(plane.p2n(p))),
                      scene.transform_alpha,
                  )
              )
              return sample
  
          sample_dots.add_updater(update_sample)
  
          # Sample lines
          x_line = Line(plane.n2p(plane.x_min), plane.n2p(plane.x_max))
          y_line = Line(plane.n2p(plane.y_min), plane.n2p(plane.y_max))
          y_line.rotate(90 * DEGREES)
          x_line.set_color(GREEN)
          y_line.set_color(PINK)
          axis_lines = VGroup(x_line, y_line)
          for line in axis_lines:
              line.insert_n_curves(50)
          axis_lines.save_state()
  
          def update_axis_liens(lines=axis_lines, f=func, plane=plane, scene=self):
              lines.restore()
              lines.apply_function(
                  lambda p: interpolate(
                      p,
                      plane.n2p(f(plane.p2n(p))),
                      scene.transform_alpha,
                  )
              )
              lines.make_smooth()
  
          axis_lines.add_updater(update_axis_liens)
  
          # Labels
          labels = VGroup(
              Tex("f(1)"),
              Tex("f(i)"),
              Tex("f(-1)"),
              Tex("f(-i)"),
          )
          for label, dot in zip(labels, special_dots):
              label.set_height(0.3)
              label.match_color(dot)
              label.set_stroke(BLACK, 3, background=True)
              label.add_background_rectangle(opacity=0.5)
  
          def update_labels(labels, dots=special_dots, scene=self):
              for label, dot in zip(labels, dots):
                  label.next_to(dot, UR, 0.5 * SMALL_BUFF)
                  label.set_opacity(self.transform_alpha)
  
          labels.add_updater(update_labels)
  
          # Titles
          title = Tex(
              "f(x) =", "\\text{exp}(r\\cdot x)",
              tex_to_color_map={"r": YELLOW}
          )
          title.to_corner(UL)
          title.set_stroke(BLACK, 5, background=True)
          brace = Brace(title[1:], UP, buff=SMALL_BUFF)
          e_pow = Tex("e^{rx}", tex_to_color_map={"r": YELLOW})
          e_pow.add_background_rectangle()
          e_pow.next_to(brace, UP, buff=SMALL_BUFF)
          title.add(brace, e_pow)
  
          r_eq = VGroup(
              Tex("r=", tex_to_color_map={"r": YELLOW}),
              DecimalNumber(1)
          )
          r_eq.arrange(RIGHT, aligned_edge=DOWN)
          r_eq.next_to(title, DOWN, aligned_edge=LEFT)
          r_eq[0].set_stroke(BLACK, 5, background=True)
          r_eq[1].set_color(YELLOW)
          r_eq[1].add_updater(lambda m: m.set_value(plane.p2n(r_dot.get_center())))
  
          self.add(title)
          self.add(r_eq)
  
          # self.add(axis_lines)
          self.add(sample_dots)
          self.add(r_dot)
          self.add(labels)
  
          # Animations
          def update_transform_alpha(mob, alpha, scene=self):
              scene.transform_alpha = alpha
  
          frame = self.camera.frame
          frame.set_height(10)
          r_dot.clear_updaters()
          r_dot.move_to(plane.n2p(1))
  
          self.play(
              UpdateFromAlphaFunc(
                  VectorizedPoint(),
                  update_transform_alpha,
              )
          )
          self.play(r_dot.move_to, plane.n2p(2))
          self.wait()
          self.play(r_dot.move_to, plane.n2p(PI))
          self.wait()
          self.play(r_dot.move_to, plane.n2p(np.log(2)))
          self.wait()
          self.play(r_dot.move_to, plane.n2p(complex(0, np.log(2))), path_arc=90 * DEGREES, run_time=2)
          self.wait()
          self.play(r_dot.move_to, plane.n2p(complex(0, PI / 2)))
          self.wait()
          self.play(r_dot.move_to, plane.n2p(np.log(2)), run_time=2)
          self.wait()
          self.play(frame.set_height, 14)
          self.play(r_dot.move_to, plane.n2p(complex(np.log(2), PI)), run_time=3)
          self.wait()
          self.play(r_dot.move_to, plane.n2p(complex(np.log(2), TAU)), run_time=3)
          self.wait()
  
          self.embed()
  
      def on_mouse_scroll(self, point, offset):
          frame = self.camera.frame
          if self.zoom_on_scroll:
              factor = 1 + np.arctan(10 * offset[1])
              frame.scale(factor, about_point=ORIGIN)
          else:
              self.transform_alpha = clip(self.transform_alpha + 5 * offset[1], 0, 1)
  
  
  class LDMEndScreen(PatreonEndScreen):
      CONFIG = {
          "scroll_time": 20,
      }
  
  
  class ProbDiagram(Scene):
      def construct(self):
          square = Square(side_length=1)
          square.move_to(ORIGIN, DL)
          square.set_stroke(width=0)
          square.set_fill(BLUE_E, 1)
  
          frame = self.camera.frame
          frame.set_height(1.5)
          frame.move_to(square)
  
          tri = Polygon(ORIGIN, UP, UR)
          tri.set_fill(BLUE_E, 1)
          tri.set_stroke(width=0)
  
          tris = VGroup(tri)
          N = 1000
          for n in range(1, N):
              tri = Polygon(
                  ORIGIN,
                  RIGHT + UP / n,
                  RIGHT + UP / (n + 1),
              )
              tri.set_stroke(width=0)
              color = BLUE_E if (n % 2 == 0) else RED_D
              tri.set_fill(color, 1)
              tris.add(tri)
  
          self.add(tris)
  