text: from big_ol_pile_of_manim_imports import *
  from ka_playgrounds.circuits import Resistor, Source, LongResistor
  
  class OpeningQuote(Scene):
      def construct(self):
          words = TextMobject(
              "To ask the",
              "right question\\\\",
              "is harder than to answer it." 
          )
          words.to_edge(UP)
          words[1].set_color(BLUE)
          author = TextMobject("-Georg Cantor")
          author.set_color(YELLOW)
          author.next_to(words, DOWN, buff = 0.5)
  
          self.play(FadeIn(words))
          self.wait(2)
          self.play(Write(author, run_time = 3))
          self.wait()
  
  class ListTerms(Scene):
      def construct(self):
          title = TextMobject("Under the light of linear transformations")
          title.set_color(YELLOW)
          title.to_edge(UP)
          randy = Randolph().to_corner()
          words = VMobject(*map(TextMobject, [
              "Inverse matrices", 
              "Column space", 
              "Rank",
              "Null space",
          ]))
          words.arrange_submobjects(DOWN, aligned_edge = LEFT)
          words.next_to(title, DOWN, aligned_edge = LEFT)
          words.shift(RIGHT)
  
          self.add(title, randy)
          for i, word in enumerate(words.split()):
              self.play(Write(word), run_time = 1)
              if i%2 == 0:
                  self.play(Blink(randy))
              else:
                  self.wait()
          self.wait()
  
  class NoComputations(TeacherStudentsScene):
      def construct(self):
          self.setup()
          self.student_says(
              "Will you cover \\\\ computations?",
              target_mode = "raise_left_hand"
          )
          self.random_blink()
          self.teacher_says(
              "Well...uh...no",
              target_mode = "guilty",
          )
          self.play(*[
              ApplyMethod(student.change_mode, mode)
              for student, mode in zip(
                  self.get_students(),
                  ["dejected", "confused", "angry"]
              )
          ])
          self.random_blink()
          self.wait()
          new_words = self.teacher.bubble.position_mobject_inside(
              TextMobject([
                  "Search",
                  "``Gaussian elimination'' \\\\",
                  "and",
                  "``Row echelon form''",
              ])
          )
          new_words.split()[1].set_color(YELLOW)
          new_words.split()[3].set_color(GREEN)
          self.play(
              Transform(self.teacher.bubble.content, new_words),
              self.teacher.change_mode, "speaking"
          )
          self.play(*[
              ApplyMethod(student.change_mode, "pondering")
              for student in self.get_students()
          ])
          self.random_blink()
  
  class PuntToSoftware(Scene):
      def construct(self):
          self.play(Write("Let the computers do the computing"))
  
  class UsefulnessOfMatrices(Scene):
      def construct(self):
          title = TextMobject("Usefulness of matrices")
          title.set_color(YELLOW)
          title.to_edge(UP)
          self.add(title)
          self.wait(3) #Play some 3d linear transform over this
  
          equations = TexMobject("""
              6x - 3y + 2z &= 7 \\\\
              x + 2y + 5z &= 0 \\\\
              2x - 8y - z &= -2 \\\\
          """)
          equations.to_edge(RIGHT, buff = 2)
          syms = VMobject(*np.array(equations.split())[[1, 4, 7]])
          new_syms = VMobject(*[
              m.copy().set_color(c) 
              for m, c in zip(syms.split(), [X_COLOR, Y_COLOR, Z_COLOR])
          ])
          new_syms.arrange_submobjects(RIGHT, buff = 0.5)
          new_syms.next_to(equations, LEFT, buff = 3)
          sym_brace = Brace(new_syms, DOWN)
          unknowns = sym_brace.get_text("Unknown variables")
          eq_brace = Brace(equations, DOWN)
          eq_words = eq_brace.get_text("Equations")
  
          self.play(Write(equations))
          self.wait()
          self.play(Transform(syms.copy(), new_syms, path_arc = np.pi/2))
          for brace, words in (sym_brace, unknowns), (eq_brace, eq_words):
              self.play(
                  GrowFromCenter(brace),
                  Write(words)
              )
              self.wait()
  
  class CircuitDiagram(Scene):
      def construct(self):
          self.add(TextMobject("Voltages").to_edge(UP))
  
          source = Source()
          p1, p2 = source.get_top(), source.get_bottom()
          r1 = Resistor(p1, p1+2*RIGHT)
          r2 = LongResistor(p1+2*RIGHT, p2+2*RIGHT)
          r3 = Resistor(p1+2*RIGHT, p1+2*2*RIGHT)
          l1 = Line(p1+2*2*RIGHT, p2+2*2*RIGHT)
          l2 = Line(p2+2*2*RIGHT, p2)
          circuit = VMobject(source, r1, r2, r3, l1, l2)
          circuit.center()
          v1 = TexMobject("v_1").next_to(r1, UP)
          v2 = TexMobject("v_2").next_to(r2, RIGHT)
          v3 = TexMobject("v_3").next_to(r3, UP)
          unknowns = VMobject(v1, v2, v3)
          unknowns.set_color(BLUE)
  
          self.play(ShowCreation(circuit))
          self.wait()
          self.play(Write(unknowns))
          self.wait()
  
  class StockLine(VMobject):
      CONFIG = {
          "num_points" : 15,
          "step_range" : 2
      }
      def generate_points(self):
          points = [ORIGIN]
          for x in range(self.num_points):
              step_size = self.step_range*(random.random() - 0.5)
              points.append(points[-1] + 0.5*RIGHT + step_size*UP)
          self.set_anchor_points(points, mode = "corners")
  
  class StockPrices(Scene):
      def construct(self):
          self.add(TextMobject("Stock prices").to_edge(UP))
  
          x_axis = Line(ORIGIN, FRAME_X_RADIUS*RIGHT)
          y_axis = Line(ORIGIN, FRAME_Y_RADIUS*UP)
          everyone = VMobject(x_axis, y_axis)
          stock_lines = []
          for color in TEAL, PINK, YELLOW, RED, BLUE:
              sl = StockLine(color = color)
              sl.move_to(y_axis.get_center(), aligned_edge = LEFT)
              everyone.add(sl)
              stock_lines.append(sl)
          everyone.center()
  
          self.add(x_axis, y_axis)
          self.play(ShowCreation(
              VMobject(*stock_lines),
              run_time = 3,
              submobject_mode = "lagged_start"
          ))
          self.wait()
  
  class MachineLearningNetwork(Scene):
      def construct(self):
          self.add(TextMobject("Machine learning parameters").to_edge(UP))
  
          layers = []
          for i, num_nodes in enumerate([3, 4, 4, 1]):
              layer = VMobject(*[
                  Circle(radius = 0.5, color = YELLOW)
                  for x in range(num_nodes)
              ])
              for j, mob in enumerate(layer.split()):
                  sym = TexMobject("x_{%d, %d}"%(i, j))
                  sym.move_to(mob)
                  mob.add(sym)
              layer.arrange_submobjects(DOWN, buff = 0.5)
              layer.center()
              layers.append(layer)
          VMobject(*layers).arrange_submobjects(RIGHT, buff = 1.5)
          lines = VMobject()
          for l_layer, r_layer in zip(layers, layers[1:]):
              for l_node, r_node in it.product(l_layer.split(), r_layer.split()):
                  lines.add(Line(l_node, r_node))
          lines.set_submobject_colors_by_gradient(BLUE_E, BLUE_A)
          for mob in VMobject(*layers), lines:
              self.play(Write(mob), run_time = 2)
          self.wait()
  
  class ComplicatedSystem(Scene):
      def construct(self):
          system = TexMobject("""
          \\begin{align*}
              \\dfrac{1}{1-e^{2x-3y+4z}} &= 1 \\\\ \\\\
              \\sin(xy) + z^2 &= \\sqrt{y} \\\\ \\\\
              x^2 + y^2 &= e^{-z}
          \\end{align*}
          """)
          system.to_edge(UP)
          randy = Randolph().to_corner(DOWN+LEFT)
  
          self.add(randy)
          self.play(Write(system, run_time = 1))
          self.play(randy.change_mode, "sassy")
          self.play(Blink(randy))
          self.wait()
  
  class SystemOfEquations(Scene):
      def construct(self):
          equations = self.get_equations()
          self.show_linearity_rules(equations)
          self.describe_organization(equations)
          self.factor_into_matrix(equations)
  
      def get_equations(self):
          matrix = Matrix([
              [2, 5, 3],
              [4, 0, 8],
              [1, 3, 0]
          ])
          mob_matrix = matrix.get_mob_matrix()
          rhs = map(TexMobject, map(str, [-3, 0, 2]))
          variables = map(TexMobject, list("xyz"))
          for v, color in zip(variables, [X_COLOR, Y_COLOR, Z_COLOR]):
              v.set_color(color)
          equations = VMobject()
          for row in mob_matrix:
              equation = VMobject(*it.chain(*zip(
                  row, 
                  [v.copy() for v in variables],
                  map(TexMobject, list("++="))
              )))
              equation.arrange_submobjects(
                  RIGHT, buff = 0.1, 
                  aligned_edge = DOWN
              )
              equation.split()[4].shift(0.1*DOWN)
              equation.split()[-1].next_to(equation.split()[-2], RIGHT)
              equations.add(equation)
          equations.arrange_submobjects(DOWN, aligned_edge = RIGHT)
          for eq, rhs_elem in zip(equations.split(), rhs):
              rhs_elem.next_to(eq, RIGHT)
              eq.add(rhs_elem)
          equations.center()
          self.play(Write(equations))
          self.add(equations)
          return equations
  
      def show_linearity_rules(self, equations):
          top_equation = equations.split()[0]
          other_equations = VMobject(*equations.split()[1:])
          other_equations.save_state()
          scaled_vars = VMobject(*[
              VMobject(*top_equation.split()[3*i:3*i+2])
              for i in range(3)
          ])
          scaled_vars.save_state()
          isolated_scaled_vars = scaled_vars.copy()
          isolated_scaled_vars.scale(1.5)
          isolated_scaled_vars.next_to(top_equation, UP)
          scalars = VMobject(*[m.split()[0] for m in scaled_vars.split()])
          plusses = np.array(top_equation.split())[[2, 5]]
  
          self.play(other_equations.fade, 0.7)
          self.play(Transform(scaled_vars, isolated_scaled_vars))
          self.play(scalars.set_color, YELLOW, submobject_mode = "lagged_start")
          self.play(*[
              ApplyMethod(m.scale_in_place, 1.2, rate_func = there_and_back)
              for m in scalars.split()
          ])
          self.wait()
          self.remove(scalars)
          self.play(scaled_vars.restore)
          self.play(*[
              ApplyMethod(p.scale_in_place, 1.5, rate_func = there_and_back)
              for p in plusses
          ])
          self.wait()
          self.show_nonlinearity_examples()
          self.play(other_equations.restore)
  
      def show_nonlinearity_examples(self):
          squared = TexMobject("x^2")
          squared.split()[0].set_color(X_COLOR)
          sine = TexMobject("\\sin(x)")
          sine.split()[-2].set_color(X_COLOR)
          product = TexMobject("xy")
          product.split()[0].set_color(X_COLOR)
          product.split()[1].set_color(Y_COLOR)
  
  
          words = TextMobject("Not allowed!")
          words.set_color(RED)
          words.to_corner(UP+LEFT, buff = 1)
          arrow = Vector(RIGHT, color = RED)
          arrow.next_to(words, RIGHT)
          for mob in squared, sine, product:
              mob.scale(1.7)
              mob.next_to(arrow.get_end(), RIGHT, buff = 0.5)
          circle_slash = Circle(color = RED)
          line = Line(LEFT, RIGHT, color = RED)
          line.rotate(np.pi/4)
          circle_slash.add(line)
          circle_slash.next_to(arrow, RIGHT)
          def draw_circle_slash(mob):
              circle_slash.replace(mob)
              circle_slash.scale_in_place(1.4)
              self.play(ShowCreation(circle_slash), run_time = 0.5)
              self.wait(0.5)
              self.play(FadeOut(circle_slash), run_time = 0.5)
  
          self.play(
              Write(squared),
              Write(words, run_time = 1),
              ShowCreation(arrow),
          )
          draw_circle_slash(squared)
          for mob in sine, product:
              self.play(Transform(squared, mob))
              draw_circle_slash(mob)
          self.play(*map(FadeOut, [words, arrow, squared]))
          self.wait()
  
  
      def describe_organization(self, equations):
          variables = VMobject(*it.chain(*[
              eq.split()[:-2]
              for eq in equations.split()
          ]))
          variables.words = "Throw variables on the left"
          constants = VMobject(*[
              eq.split()[-1]
              for eq in equations.split()
          ])
          constants.words = "Lingering constants on the right"
          xs, ys, zs = [
              VMobject(*[
                  eq.split()[i]
                  for eq in equations.split()
              ])
              for i in (1, 4, 7)
          ]
          ys.words = "Vertically align variables"
          colors = [PINK, YELLOW, BLUE_B, BLUE_C, BLUE_D]
          for mob, color in zip([variables, constants, xs, ys, zs], colors):
              mob.square = Square(color = color)
              mob.square.replace(mob, stretch = True)
              mob.square.scale_in_place(1.1)
              if hasattr(mob, "words"):
                  mob.words = TextMobject(mob.words)
                  mob.words.set_color(color)
                  mob.words.next_to(mob.square, UP)
          ys.square.add(xs.square, zs.square)
          zero_circles = VMobject(*[
              Circle().replace(mob).scale_in_place(1.3)
              for mob in [
                  VMobject(*equations.split()[i].split()[j:j+2])
                  for i, j in [(1, 3), (2, 6)]
              ]
          ])
          zero_circles.set_color(PINK)
          zero_circles.words = TextMobject("Add zeros as needed")
          zero_circles.words.set_color(zero_circles.get_color())
          zero_circles.words.next_to(equations, UP)
  
          for mob in variables, constants, ys:
              self.play(
                  FadeIn(mob.square),
                  FadeIn(mob.words)
              )
              self.wait()
              self.play(*map(FadeOut, [mob.square, mob.words]))
          self.play(
              ShowCreation(zero_circles),
              Write(zero_circles.words, run_time = 1)
          )
          self.wait()
          self.play(*map(FadeOut, [zero_circles, zero_circles.words]))
          self.wait()
          title = TextMobject("``Linear system of equations''")
          title.scale(1.5)
          title.to_edge(UP)
          self.play(Write(title))
          self.wait()
          self.play(FadeOut(title))
  
  
      def factor_into_matrix(self, equations):
          coefficients = np.array([
              np.array(eq.split())[[0, 3, 6]]
              for eq in equations.split()
          ])
          variable_arrays = np.array([
              np.array(eq.split())[[1, 4, 7]]
              for eq in equations.split()
          ])
          rhs_entries = np.array([
              eq.split()[-1]
              for eq in equations.split()
          ])
  
          matrix = Matrix(copy.deepcopy(coefficients))
          x_array = Matrix(copy.deepcopy(variable_arrays[0]))
          v_array = Matrix(copy.deepcopy(rhs_entries))
          equals = TexMobject("=")
          ax_equals_v = VMobject(matrix, x_array, equals, v_array)
          ax_equals_v.arrange_submobjects(RIGHT)
          ax_equals_v.to_edge(RIGHT)
          all_brackets = [
              mob.get_brackets()
              for mob in (matrix, x_array, v_array)
          ]
  
          self.play(equations.to_edge, LEFT)
          arrow = Vector(RIGHT, color = YELLOW)
          arrow.next_to(ax_equals_v, LEFT)
          self.play(ShowCreation(arrow))
          self.play(*it.chain(*[
              [
                  Transform(
                      m1.copy(), m2, 
                      run_time = 2,
                      path_arc = -np.pi/2
                  )
                  for m1, m2 in zip(
                      start_array.flatten(),
                      matrix_mobject.get_entries().split()
                  )
              ]
              for start_array, matrix_mobject in [
                  (coefficients, matrix),
                  (variable_arrays[0], x_array),
                  (variable_arrays[1], x_array),
                  (variable_arrays[2], x_array),
                  (rhs_entries, v_array)
              ]
          ]))
          self.play(*[
              Write(mob)
              for mob in all_brackets + [equals]
          ])
          self.wait()
          self.label_matrix_product(matrix, x_array, v_array)
          
      def label_matrix_product(self, matrix, x_array, v_array):
          matrix.words = "Coefficients"
          matrix.symbol = "A"
          x_array.words = "Variables"
          x_array.symbol = "\\vec{\\textbf{x}}"
          v_array.words = "Constants"
          v_array.symbol = "\\vec{\\textbf{v}}"
          parts = matrix, x_array, v_array
          for mob in parts:
              mob.brace = Brace(mob, UP)
              mob.words = mob.brace.get_text(mob.words)
              mob.words.shift_onto_screen()
              mob.symbol = TexMobject(mob.symbol)
              mob.brace.put_at_tip(mob.symbol)
          x_array.words.set_submobject_colors_by_gradient(
              X_COLOR, Y_COLOR, Z_COLOR
          )
          x_array.symbol.set_color(PINK)
          v_array.symbol.set_color(YELLOW)
          for mob in parts:
              self.play(
                  GrowFromCenter(mob.brace),
                  FadeIn(mob.words)
              )
              self.wait()
              self.play(*map(FadeOut, [mob.brace, mob.words]))
          self.wait()
          for mob in parts:
              self.play(
                  FadeIn(mob.brace),
                  Write(mob.symbol)
              )
          compact_equation = VMobject(*[
              mob.symbol for mob in parts
          ])
          compact_equation.submobjects.insert(
              2, TexMobject("=").next_to(x_array, RIGHT)
          )
          compact_equation.target = compact_equation.copy()
          compact_equation.target.arrange_submobjects(buff = 0.1)
          compact_equation.target.to_edge(UP)
  
          self.play(Transform(
              compact_equation.copy(), 
              compact_equation.target
          ))
          self.wait()
  
  class LinearSystemTransformationScene(LinearTransformationScene):
      def setup(self):
          LinearTransformationScene.setup(self)
          equation = TexMobject([
              "A",
              "\\vec{\\textbf{x}}",
              "=",
              "\\vec{\\textbf{v}}",
          ])
          equation.scale(1.5)
          equation.next_to(ORIGIN, LEFT).to_edge(UP)
          equation.add_background_rectangle()
          self.add_foreground_mobject(equation)
          self.equation = equation
          self.A, self.x, eq, self.v = equation.split()[1].split()
          self.x.set_color(PINK)
          self.v.set_color(YELLOW)
  
  class MentionThatItsATransformation(LinearSystemTransformationScene):
      CONFIG = {
          "t_matrix" : np.array([[2, 1], [2, 3]])
      }
      def construct(self):
          self.setup()
          brace = Brace(self.A)
          words = brace.get_text("Transformation")
          words.add_background_rectangle()
          self.play(GrowFromCenter(brace), Write(words, run_time = 1))
          self.add_foreground_mobject(words, brace)
          self.apply_transposed_matrix(self.t_matrix)
          self.wait()
  
  class LookForX(MentionThatItsATransformation):
      CONFIG = {
          "show_basis_vectors" : False
      }
      def construct(self):
          self.setup()
          v = [-4, - 1]
          x = np.linalg.solve(self.t_matrix.T, v)
          v = Vector(v, color = YELLOW)
          x = Vector(x, color = PINK)
          v_label = self.get_vector_label(v, "v", color = YELLOW)
          x_label = self.get_vector_label(x, "x", color = PINK)
          for label in x_label, v_label:
              label.add_background_rectangle()
          self.play(
              ShowCreation(v),
              Write(v_label)
          )
          self.add_foreground_mobject(v_label)
          x = self.add_vector(x, animate = False)
          self.play(
              ShowCreation(x),
              Write(x_label)
          )
          self.wait()
          self.add(VMobject(x, x_label).copy().fade())
          self.apply_transposed_matrix(self.t_matrix)
          self.wait()
  
  class ThinkAboutWhatsHappening(Scene):
      def construct(self):
          randy = Randolph()
          randy.to_corner()
          bubble = randy.get_bubble(height = 5)
          bubble.add_content(TexMobject("""
              3x + 1y + 4z &= 1 \\\\
              5x + 9y + 2z &= 6 \\\\
              5x + 3y + 5z &= 8
          """))
  
          self.play(randy.change_mode, "pondering")
          self.play(ShowCreation(bubble))
          self.play(Write(bubble.content, run_time = 2))
          self.play(Blink(randy))
          self.wait()
          everything = VMobject(*self.get_mobjects())
          self.play(
              ApplyFunction(
                  lambda m : m.shift(2*DOWN).scale(5),
                  everything
              ),
              bubble.content.set_color, BLACK,
              run_time = 2
          )
  
  class SystemOfTwoEquationsTwoUnknowns(Scene):
      def construct(self):
          system = TexMobject("""
              2x + 2y &= -4 \\\\
              1x + 3y &= -1
          """)
          system.to_edge(UP)
          for indices, color in ((1, 9), X_COLOR), ((4, 12), Y_COLOR):
              for i in indices:
                  system.split()[i].set_color(color)
          matrix = Matrix([[2, 2], [1, 3]])
          v = Matrix([-4, -1])
          x = Matrix(["x", "y"])
          x.get_entries().set_submobject_colors_by_gradient(X_COLOR, Y_COLOR)
          matrix_system = VMobject(
              matrix, x, TexMobject("="), v
          )
          matrix_system.arrange_submobjects(RIGHT)
          matrix_system.next_to(system, DOWN, buff = 1)
          matrix.label = "A"
          matrix.label_color = WHITE
          x.label = "\\vec{\\textbf{x}}"
          x.label_color = PINK
          v.label = "\\vec{\\textbf{v}}"
          v.label_color = YELLOW
          for mob in matrix, x, v:
              brace = Brace(mob)
              label = brace.get_text("$%s$"%mob.label)
              label.set_color(mob.label_color)
              brace.add(label)
              mob.brace = brace
  
          self.add(system)
          self.play(Write(matrix_system))
          self.wait()
          for mob in matrix, v, x:
              self.play(Write(mob.brace))
              self.wait()
  
  class ShowBijectivity(LinearTransformationScene):
      CONFIG = {
          "show_basis_vectors" : False,
          "t_matrix" : np.array([[0, -1], [2, 1]])
      }
      def construct(self):
          self.setup()
          vectors = VMobject(*[
              Vector([x, y])
              for x, y in it.product(*[
                  np.arange(-int(val)+0.5, int(val)+0.5)
                  for val in (FRAME_X_RADIUS, FRAME_Y_RADIUS)
              ])
          ])
          vectors.set_submobject_colors_by_gradient(BLUE_E, PINK)
          dots = VMobject(*[
              Dot(v.get_end(), color = v.get_color())
              for v in vectors.split()
          ])
          titles = [
              TextMobject([
                  "Each vector lands on\\\\",
                  "exactly one vector"
              ]),
              TextMobject([
                  "Every vector has \\\\",
                  "been landed on"
              ])
          ]
          for title in titles:
              title.to_edge(UP)
          background = BackgroundRectangle(VMobject(*titles))
          self.add_foreground_mobject(background, titles[0])
  
          kwargs = {
              "submobject_mode" : "lagged_start",
              "run_time" : 2
          }
          anims = map(Animation, self.foreground_mobjects)
          self.play(ShowCreation(vectors, **kwargs), *anims)
          self.play(Transform(vectors, dots, **kwargs), *anims)
          self.wait()
          self.add_transformable_mobject(vectors)
          self.apply_transposed_matrix(self.t_matrix)
          self.wait()
          self.play(Transform(*titles))
          self.wait()
          self.apply_transposed_matrix(
              np.linalg.inv(self.t_matrix.T).T
          )
          self.wait()
  
  class LabeledExample(LinearSystemTransformationScene):
      CONFIG = {
          "title" : "",
          "t_matrix" : [[0, 0], [0, 0]],
          "show_square" : False,
      }
      def setup(self):
          LinearSystemTransformationScene.setup(self)
          title = TextMobject(self.title)
          title.next_to(self.equation, DOWN, buff = 1)
          title.add_background_rectangle()
          title.shift_onto_screen()
          self.add_foreground_mobject(title)        
          self.title = title
          if self.show_square:
              self.add_unit_square()
  
      def construct(self):
          self.wait()
          self.apply_transposed_matrix(self.t_matrix)
          self.wait()
  
  class SquishExmapleWithWords(LabeledExample):
      CONFIG = {
          "title" : "$A$ squishes things to a lower dimension",
          "t_matrix" : [[-2, -1], [2, 1]]
      }
  
  class FullRankExmapleWithWords(LabeledExample):
      CONFIG = {
          "title" : "$A$ keeps things 2D",
          "t_matrix" : [[3, 0], [2, 1]]
      }
  
  class SquishExmapleDet(SquishExmapleWithWords):
      CONFIG = {
          "title" : "$\\det(A) = 0$",
          "show_square" : True,
      }
  
  class FullRankExmapleDet(FullRankExmapleWithWords):
      CONFIG = {
          "title" : "$\\det(A) \\ne 0$",
          "show_square" : True,
      }
  
  class StartWithNonzeroDetCase(TeacherStudentsScene):
      def construct(self):
          words = TextMobject(
              "Let's start with \\\\",
              "the", "$\\det(A) \\ne 0$", "case"
          )
          words[2].set_color(TEAL)
          self.teacher_says(words)
          self.random_blink()
          self.play(
              random.choice(self.get_students()).change_mode,
              "happy"
          )
          self.wait()
  
  class DeclareNewTransformation(TeacherStudentsScene):
      def construct(self):
          words = TextMobject(
              "Playing a transformation in\\\\",
              "reverse gives a", "new transformation"
          )
          words[-1].set_color(GREEN)
          self.teacher_says(words)
          self.change_student_modes("pondering", "sassy")
          self.random_blink()
  
  class PlayInReverse(FullRankExmapleDet):
      CONFIG = {
          "show_basis_vectors" : False
      }
      def construct(self):
          FullRankExmapleDet.construct(self)
          v = self.add_vector([-4, -1], color = YELLOW)
          v_label = self.label_vector(v, "v", color = YELLOW)
          self.add(v.copy())
          self.apply_inverse_transpose(self.t_matrix)
          self.play(v.set_color, PINK)
          self.label_vector(v, "x", color = PINK)
          self.wait()
  
  class DescribeInverse(LinearTransformationScene):
      CONFIG = {
          "show_actual_inverse" : False,
          "matrix_label" : "$A$",
          "inv_label" : "$A^{-1}$",
      }
      def construct(self):
          title = TextMobject("Transformation:")
          new_title = TextMobject("Inverse transformation:")
          matrix = Matrix(self.t_matrix.T)
          if not self.show_actual_inverse:
              inv_matrix = matrix.copy()
              neg_1 = TexMobject("-1")
              neg_1.move_to(
                  inv_matrix.get_corner(UP+RIGHT), 
                  aligned_edge = LEFT
              )
              neg_1.shift(0.1*RIGHT)
              inv_matrix.add(neg_1)
              matrix.add(VectorizedPoint(matrix.get_corner(UP+RIGHT)))
          else:
              inv_matrix = Matrix(np.linalg.inv(self.t_matrix.T).astype('int'))
          matrix.label = self.matrix_label
          inv_matrix.label = self.inv_label
          for m, text in (matrix, title), (inv_matrix, new_title):
              m.add_to_back(BackgroundRectangle(m))
              text.add_background_rectangle()
              m.next_to(text, RIGHT)
              brace = Brace(m)
              label_mob = brace.get_text(m.label)
              label_mob.add_background_rectangle()
              m.add(brace, label_mob)
              text.add(m)
              if text.get_width() > FRAME_WIDTH-1:
                  text.scale_to_fit_width(FRAME_WIDTH-1)
              text.center().to_corner(UP+RIGHT)
          matrix.set_color(PINK)
          inv_matrix.set_color(YELLOW)
  
          self.add_foreground_mobject(title)
          self.apply_transposed_matrix(self.t_matrix)
          self.wait()
          self.play(Transform(title, new_title))
          self.apply_inverse_transpose(self.t_matrix)
          self.wait()
  
  class ClockwiseCounterclockwise(DescribeInverse):
      CONFIG = {
          "t_matrix" : [[0, 1], [-1, 0]],
          "show_actual_inverse" : True,
          "matrix_label" : "$90^\\circ$ Couterclockwise",
          "inv_label" : "$90^\\circ$ Clockwise",
      }
  
  class ShearInverseShear(DescribeInverse):
      CONFIG = {
          "t_matrix" : [[1, 0], [1, 1]],
          "show_actual_inverse" : True,
          "matrix_label" : "Rightward shear",
          "inv_label" : "Leftward shear",
      }
  
  class MultiplyToIdentity(LinearTransformationScene):
      def construct(self):
          self.setup()
          lhs = TexMobject("A^{-1}", "A", "=")
          lhs.scale(1.5)
          A_inv, A, eq = lhs.split()
          identity = Matrix([[1, 0], [0, 1]])
          identity.set_color_columns(X_COLOR, Y_COLOR)
          identity.next_to(eq, RIGHT)
          VMobject(lhs, identity).center().to_corner(UP+RIGHT)
          for mob in A, A_inv, eq:
              mob.add_to_back(BackgroundRectangle(mob))
          identity.background = BackgroundRectangle(identity)
  
          col1 = VMobject(*identity.get_mob_matrix()[:,0])
          col2 = VMobject(*identity.get_mob_matrix()[:,1])
  
          A.text = "Transformation"
          A_inv.text = "Inverse transformation"
          product = VMobject(A, A_inv)
          product.text = "Matrix multiplication"
          identity.text = "The transformation \\\\ that does nothing"
          for mob in A, A_inv, product, identity:
              mob.brace = Brace(mob)
              mob.text = mob.brace.get_text(mob.text)
              mob.text.shift_onto_screen()
              mob.text.add_background_rectangle()
  
          self.add_foreground_mobject(A, A_inv)
          brace, text = A.brace, A.text
          self.play(GrowFromCenter(brace), Write(text), run_time = 1)
          self.add_foreground_mobject(brace, text)
          self.apply_transposed_matrix(self.t_matrix)
          self.play(
              Transform(brace, A_inv.brace),
              Transform(text, A_inv.text),
          )
          self.apply_inverse_transpose(self.t_matrix)
          self.wait()
          self.play(
              Transform(brace, product.brace),
              Transform(text, product.text)
          )
          self.wait()
          self.play(
              Write(identity.background),
              Write(identity.get_brackets()),
              Write(eq),
              Transform(brace, identity.brace),
              Transform(text, identity.text)
          )
          self.wait()
          self.play(Write(col1))
          self.wait()
          self.play(Write(col2))
          self.wait()
  
  class ThereAreComputationMethods(TeacherStudentsScene):
      def construct(self):
          self.teacher_says("""
              There are methods
              to compute $A^{-1}$
          """)
          self.random_blink()
          self.wait()
  
  class TwoDInverseFormula(Scene):
      def construct(self):
          title = TextMobject("If you're curious...")
          title.set_color(YELLOW)
          title.to_edge(UP)
          morty = Mortimer().to_corner(DOWN+RIGHT)
          self.add(title, morty)
          matrix = [["a", "b"], ["c", "d"]]
          scaled_inv = [["d", "-b"], ["-c", "a"]]
          formula = TexMobject("""
              %s^{-1} = \\dfrac{1}{ad-bc} %s
          """%(
              matrix_to_tex_string(matrix),
              matrix_to_tex_string(scaled_inv)
          ))
          self.play(Write(formula))
          self.play(morty.change_mode, "confused")
          self.play(Blink(morty))
  
  class SymbolicInversion(Scene):
      def construct(self):
          vec = lambda s : "\\vec{\\textbf{%s}}"%s
          
          words = TextMobject("Once you have this:")
          words.to_edge(UP, buff = 2)
          inv = TexMobject("A^{-1}")
          inv.set_color(GREEN)
          inv.next_to(words.split()[-1], RIGHT, aligned_edge = DOWN)
          inv2 = inv.copy()
  
          start = TexMobject("A", vec("x"), "=", vec("v"))
          interim = TexMobject("A^{-1}", "A", vec("x"), "=", "A^{-1}", vec("v"))
          end = TexMobject(vec("x"), "=", "A^{-1}", vec("v"))
  
          A, x, eq, v = start.split()
          x.set_color(PINK)
          v.set_color(YELLOW)
          interim_mobs = [inv, A, x, eq, inv2, v]
          for i, mob in enumerate(interim_mobs):
              mob.interim = mob.copy().move_to(interim.split()[i])
  
          self.add(start)
          self.play(Write(words), FadeIn(inv), run_time = 1)
          self.wait()
          self.play(
              FadeOut(words),
              *[Transform(m, m.interim) for m in interim_mobs]
          )
          self.wait()
  
          product = VMobject(A, inv)
          product.brace = Brace(product)
          product.words = product.brace.get_text(
              "The ``do nothing'' matrix"
          )
          product.words.set_color(BLUE)
          self.play(
              GrowFromCenter(product.brace),
              Write(product.words, run_time = 1),
              product.set_color, BLUE
          )
          self.wait()
          self.play(*[
              ApplyMethod(m.set_color, BLACK)
              for m in (product, product.brace, product.words)
          ])
          self.wait()
          self.play(ApplyFunction(
              lambda m : m.center().to_edge(UP),
              VMobject(x, eq, inv2, v)
          ))
          self.wait()
  
  class PlayInReverseWithSolution(PlayInReverse):
      CONFIG = {
          "t_matrix" : [[2, 1], [2, 3]]
      }
      def setup(self):
          LinearTransformationScene.setup(self)
          equation = TexMobject([
              "\\vec{\\textbf{x}}",
              "=",
              "A^{-1}",
              "\\vec{\\textbf{v}}",
          ])
          equation.to_edge(UP)
          equation.add_background_rectangle()
          self.add_foreground_mobject(equation)
          self.equation = equation
          self.x, eq, self.inv, self.v = equation.split()[1].split()
          self.x.set_color(PINK)
          self.v.set_color(YELLOW)
          self.inv.set_color(GREEN)
  
  class OneUniqueSolution(Scene):
      def construct(self):
          system = TexMobject("""
              \\begin{align*}
                  ax + cy &= e \\\\
                  bx + dy &= f
              \\end{align*}
          """)
          VMobject(*np.array(system.split())[[1, 8]]).set_color(X_COLOR)
          VMobject(*np.array(system.split())[[4, 11]]).set_color(Y_COLOR)
          brace = Brace(system, UP)
          brace.set_color(YELLOW)
          words = brace.get_text("One unique solution \\dots", "probably")
          words.set_color(YELLOW)
          words.split()[1].set_color(GREEN)
  
          self.add(system)
          self.wait()
          self.play(
              GrowFromCenter(brace),
              Write(words.split()[0])
          )
          self.wait()
          self.play(Write(words.split()[1], run_time = 1))
          self.wait()
  
  class ThreeDTransformXToV(Scene):
      pass
  
  class ThreeDTransformAndReverse(Scene):
      pass
  
  class DetNEZeroRule(Scene):
      def construct(self):
          text = TexMobject("\\det(A) \\ne 0")
          text.shift(2*UP)
          A_inv = TextMobject("$A^{-1}$ exists")
          A_inv.shift(DOWN)
          arrow = Arrow(text, A_inv)
          self.play(Write(text))
          self.wait()
          self.play(ShowCreation(arrow))
          self.play(Write(A_inv, run_time = 1))
          self.wait()
  
  
  class ThreeDInverseRule(Scene):
      def construct(self):
          form = TexMobject("A^{-1} A = ")
          form.scale(2)
          matrix = Matrix(np.identity(3, 'int'))
          matrix.set_color_columns(X_COLOR, Y_COLOR, Z_COLOR)
          matrix.next_to(form, RIGHT)
          self.add(form)
          self.play(Write(matrix))
          self.wait()
  
  class ThreeDApplyReverseToV(Scene):
      pass
  
  class InversesDontAlwaysExist(TeacherStudentsScene):
      def construct(self):
          self.teacher_says("$A^{-1}$ doesn't always exist")
          self.random_blink()
          self.wait()
          self.random_blink()
  
  class InvertNonInvertable(LinearTransformationScene):
      CONFIG = {
          "t_matrix" : [[2, 1], [-2, -1]]
      }
      def setup(self):
          LinearTransformationScene.setup(self)
          det_text = TexMobject("\\det(A) = 0")
          det_text.scale(1.5)
          det_text.to_corner(UP+LEFT)
          det_text.add_background_rectangle()
          self.add_foreground_mobject(det_text)
  
      def construct(self):
          no_func = TextMobject("No function does this")
          no_func.shift(2*UP)
          no_func.set_color(RED)
          no_func.add_background_rectangle()
          grid = VMobject(self.plane, self.i_hat, self.j_hat)
          grid.save_state()
          self.apply_transposed_matrix(self.t_matrix, path_arc = 0)
          self.wait()
          self.play(Write(no_func, run_time = 1))
          self.add_foreground_mobject(no_func)
          self.play(
              grid.restore,
              *map(Animation, self.foreground_mobjects),
              run_time = 3
          )
          self.wait()
  
  class OneInputMultipleOutputs(InvertNonInvertable):
      def construct(self):
          input_vectors = VMobject(*[
              Vector([x+2, x]) for x in np.arange(-4, 4.5, 0.5)
          ])
          input_vectors.set_submobject_colors_by_gradient(PINK, YELLOW)
          output_vector = Vector([4, 2], color = YELLOW)
  
          grid = VMobject(self.plane, self.i_hat, self.j_hat)
          grid.save_state()
  
          self.apply_transposed_matrix(self.t_matrix, path_arc = 0)
          self.play(ShowCreation(output_vector))
          single_input = TextMobject("Single vector")
          single_input.add_background_rectangle()
          single_input.next_to(output_vector.get_end(), UP)
          single_input.set_color(YELLOW) 
          self.play(Write(single_input))
          self.wait()
          self.remove(single_input, output_vector)
          self.play(
              grid.restore,
              *[
                  Transform(output_vector.copy(), input_vector)
                  for input_vector in input_vectors.split()
              ] + map(Animation, self.foreground_mobjects),
              run_time = 3
          )
          multiple_outputs = TextMobject(
              "Must map to \\\\",
              "multiple vectors"
          )
          multiple_outputs.split()[1].set_submobject_colors_by_gradient(YELLOW, PINK)
          multiple_outputs.next_to(ORIGIN, DOWN).to_edge(RIGHT)
          multiple_outputs.add_background_rectangle()
          self.play(Write(multiple_outputs, run_time = 2))
          self.wait()
  
  class SolutionsCanStillExist(TeacherStudentsScene):
      def construct(self):
          words = TextMobject("""
              Solutions can still 
              exist when""",  "$\\det(A) = 0$"
          )
          words[1].set_color(TEAL)
          self.teacher_says(words)
          self.random_blink(2)
  
  class ShowVInAndOutOfColumnSpace(LinearSystemTransformationScene):
      CONFIG = {
          "t_matrix" : [[2, 1], [-2, -1]]
      }
      def construct(self):
          v_out = Vector([1, -1])
          v_in = Vector([-4, -2])
          v_out.words = "No solution exists"
          v_in.words = "Solutions exist"
          v_in.words_color = YELLOW
          v_out.words_color = RED
  
  
          self.apply_transposed_matrix(self.t_matrix, path_arc = 0)
          self.wait()
          for v in v_in, v_out:
              self.add_vector(v, animate = True)
              words = TextMobject(v.words)
              words.set_color(v.words_color)
              words.next_to(v.get_end(), DOWN+RIGHT)
              words.add_background_rectangle()
              self.play(Write(words), run_time = 2)
          self.wait()
  
  class NotAllSquishesAreCreatedEqual(TeacherStudentsScene):
      def construct(self):
          self.student_says("""
              Some squishes feel
              ...squishier
          """)
          self.random_blink(2)
  
  class PrepareForRank(Scene):
      def construct(self):
          new_term, rank = words = TextMobject(
              "New terminology: ",
              "rank"
          )
          rank.set_color(TEAL)
          self.play(Write(words))
          self.wait()
  
  class DefineRank(Scene):
      def construct(self):
          rank = TextMobject("``Rank''")
          rank.set_color(TEAL)
          arrow = DoubleArrow(LEFT, RIGHT)
          dims = TextMobject(
              "Number of\\\\", "dimensions \\\\", 
              "in the output"
          )
          dims[1].set_color(rank.get_color())
  
          rank.next_to(arrow, LEFT)
          dims.next_to(arrow, RIGHT)
  
          self.play(Write(rank))
          self.play(
              ShowCreation(arrow),
              *map(Write, dims)
          )
          self.wait()
  
  class DefineColumnSpace(Scene):
      def construct(self):
          left_words = TextMobject(
              "Set of all possible\\\\",
              "outputs",
              "$A\\vec{\\textbf{v}}$",
          )
          left_words[1].set_color(TEAL)
          VMobject(*left_words[-1][1:]).set_color(YELLOW)
          arrow = DoubleArrow(LEFT, RIGHT).to_edge(UP)
          right_words = TextMobject("``Column space''", "of $A$")
          right_words[0].set_color(left_words[1].get_color())
  
          everyone = VMobject(left_words, arrow, right_words)
          everyone.arrange_submobjects(RIGHT)
          everyone.to_edge(UP)
  
          self.play(Write(left_words))
          self.wait()
          self.play(
              ShowCreation(arrow),
              Write(right_words)
          )
          self.wait()
  
  class ColumnsRepresentBasisVectors(Scene):
      def construct(self):
          matrix = Matrix([[3, 1], [4, 1]])
          matrix.shift(UP)
          i_hat_words, j_hat_words = [
              TextMobject("Where $\\hat{\\%smath}$ lands"%char)
              for char in ("i", "j")
          ]
          i_hat_words.set_color(X_COLOR)
          i_hat_words.next_to(ORIGIN, LEFT).to_edge(UP)
          j_hat_words.set_color(Y_COLOR)
          j_hat_words.next_to(ORIGIN, RIGHT).to_edge(UP)
  
          self.add(matrix)
          self.wait()
          for i, words in enumerate([i_hat_words, j_hat_words]):
  
              arrow = Arrow(
                  words.get_bottom(),
                  matrix.get_mob_matrix()[0,i].get_top(),
                  color = words.get_color()
              )
              self.play(
                  Write(words, run_time = 1),
                  ShowCreation(arrow),
                  *[
                      ApplyMethod(m.set_color, words.get_color())
                      for m in matrix.get_mob_matrix()[:,i]
                  ]
              )
          self.wait()
  
  class ThreeDOntoPlane(Scene):
      pass
  
  class ThreeDOntoLine(Scene):
      pass
  
  class ThreeDOntoPoint(Scene):
      pass
  
  class TowDColumnsDontSpan(LinearTransformationScene):
      CONFIG = {
          "t_matrix" : [[2, 1], [-2, -1]]
      }
      def construct(self):
          matrix = Matrix(self.t_matrix.T)
          matrix.set_color_columns(X_COLOR, Y_COLOR)
          matrix.add_to_back(BackgroundRectangle(matrix))
          self.add_foreground_mobject(matrix)
          brace = Brace(matrix)
          words = VMobject(
              TextMobject("Span", "of columns"),
              TexMobject("\\Updownarrow"),
              TextMobject("``Column space''")
          )
          words.arrange_submobjects(DOWN, buff = 0.1)
          words.next_to(brace, DOWN)
          words[0][0].set_color(PINK)
          words[2].set_color(TEAL)
          words[0].add_background_rectangle()
          words[2].add_background_rectangle()
          VMobject(matrix, brace, words).to_corner(UP+LEFT)
  
          self.apply_transposed_matrix(self.t_matrix, path_arc = 0)
          self.play(
              GrowFromCenter(brace),
              Write(words, run_time = 2)
          )
          self.wait()
          self.play(ApplyFunction(
              lambda m : m.scale(-1).shift(self.i_hat.get_end()),
              self.j_hat
          ))
          bases = [self.i_hat, self.j_hat]
          for mob in bases:
              mob.original = mob.copy()
          for x in range(8):
              for mob in bases:
                  mob.target = mob.original.copy()
                  mob.target.set_stroke(width = 6)
                  target_len = random.uniform(0.5, 1.5)
                  target_len *= random.choice([-1, 1])
                  mob.target.scale(target_len)
              self.j_hat.target.shift(
                  -self.j_hat.target.get_start()+ \
                  self.i_hat.target.get_end()
              )
              self.play(Transform(
                  VMobject(*bases),
                  VMobject(*[m.target for m in bases]),
                  run_time = 2
              ))
  
  class FullRankWords(Scene):
      def construct(self):
          self.play(Write(TextMobject("Full rank").scale(2)))
  
  class ThreeDColumnsDontSpan(Scene):
      def construct(self):
          matrix = Matrix(np.array([
              [1, 1, 0],
              [0, 1, 1],
              [-1, -2, -1],
          ]).T)
          matrix.set_color_columns(X_COLOR, Y_COLOR, Z_COLOR)
          brace = Brace(matrix)
          words = brace.get_text(
              "Columns don't",
              "span \\\\",
              "full output space"
          )
          words[1].set_color(PINK)
  
          self.add(matrix)
          self.play(
              GrowFromCenter(brace),
              Write(words, run_time = 2)
          )
          self.wait()
  
  class NameColumnSpace(Scene):
      def construct(self):
          matrix = Matrix(np.array([
              [1, 1, 0],
              [0, 1, 1],
              [-1, -2, -1],
          ]).T)
          matrix.set_color_columns(X_COLOR, Y_COLOR, Z_COLOR)
          matrix.to_corner(UP+LEFT)
          cols = list(matrix.copy().get_mob_matrix().T)
          col_arrays = map(Matrix, cols)
  
          span_text = TexMobject(
              "\\text{Span}", 
              "\\Big(",
              matrix_to_tex_string([1, 2, 3]),
              ",",
              matrix_to_tex_string([1, 2, 3]),
              ",",
              matrix_to_tex_string([1, 2, 3]),
              "\\big)"
          )
          for i in 1, -1:
              span_text[i].stretch(1.5, 1)
              span_text[i].do_in_place(
                  span_text[i].scale_to_fit_height, 
                  span_text.get_height()
              )
          for col_array, index in zip(col_arrays, [2, 4, 6]):
              col_array.replace(span_text[index], dim_to_match = 1)
              span_text.submobjects[index] = col_array
          span_text.arrange_submobjects(RIGHT, buff = 0.2)
  
          arrow = DoubleArrow(LEFT, RIGHT)
          column_space = TextMobject("``Column space''")
          for mob in column_space, arrow:
              mob.set_color(TEAL)
          text = VMobject(span_text, arrow, column_space)
          text.arrange_submobjects(RIGHT)
          text.next_to(matrix, DOWN, buff = 1, aligned_edge = LEFT)
  
          self.add(matrix)
          self.wait()
          self.play(*[
              Transform(
                  VMobject(*matrix.copy().get_mob_matrix()[:,i]),
                  col_arrays[i].get_entries()
              )
              for i in range(3)
          ])
          self.play(
              Write(span_text),
              *map(Animation, self.get_mobjects_from_last_animation())
          )
          self.play(
              ShowCreation(arrow),
              Write(column_space)
          )
          self.wait()
          self.play(FadeOut(matrix))
          self.clear()
          self.add(text)
  
          words = TextMobject(
              "To solve", 
              "$A\\vec{\\textbf{x}} = \\vec{\\textbf{v}}$,\\\\",
              "$\\vec{\\textbf{v}}$", 
              "must be in \\\\ the",
              "column space."
          )
          VMobject(*words[1][1:3]).set_color(PINK)
          VMobject(*words[1][4:6]).set_color(YELLOW)
          words[2].set_color(YELLOW)
          words[4].set_color(TEAL)
          words.to_corner(UP+LEFT)
  
          self.play(Write(words))
          self.wait(2)
          self.play(FadeOut(words))
  
          brace = Brace(column_space, UP)
          rank_words = brace.get_text(
              "Number of dimensions \\\\ is called",
              "``rank''"
          )
          rank_words[1].set_color(MAROON)
          self.play(
              GrowFromCenter(brace),
              Write(rank_words)
          )
          self.wait()
          self.cycle_through_span_possibilities(span_text)
  
      def cycle_through_span_possibilities(self, span_text):
          span_text.save_state()
          two_d_span = span_text.copy()
          for index, arr, c in (2, [1, 1], X_COLOR), (4, [0, 1], Y_COLOR):
              col = Matrix(arr)
              col.replace(two_d_span[index])
              two_d_span.submobjects[index] = col
              col.get_entries().set_color(c)
          for index in 5, 6:
              two_d_span[index].scale(0)
          two_d_span.arrange_submobjects(RIGHT, buff = 0.2)
          two_d_span[-1].next_to(two_d_span[4], RIGHT, buff = 0.2)
          two_d_span.move_to(span_text, aligned_edge = RIGHT)
          mob_matrix = np.array([
              two_d_span[i].get_entries().split()
              for i in (2, 4)
          ])
  
          self.play(Transform(span_text, two_d_span))
          #horrible hack
          span_text.shift(10*DOWN)
          span_text = span_text.copy().restore()
          ###
          self.add(two_d_span)
          self.wait()
          self.replace_number_matrix(mob_matrix, [[1, 1], [1, 1]])
          self.wait()
          self.replace_number_matrix(mob_matrix, [[0, 0], [0, 0]])
          self.wait()
          self.play(Transform(two_d_span, span_text))
          self.wait()
          self.remove(two_d_span)
          self.add(span_text)
          mob_matrix = np.array([
              span_text[i].get_entries().split()
              for i in (2, 4, 6)
          ])
          self.replace_number_matrix(mob_matrix, [[1, 1, 0], [0, 1, 1], [1, 0, 1]])
          self.wait()
          self.replace_number_matrix(mob_matrix, [[1, 1, 0], [0, 1, 1], [-1, -2, -1]])
          self.wait()
          self.replace_number_matrix(mob_matrix, [[1, 1, 0], [2, 2, 0], [3, 3, 0]])
          self.wait()
          self.replace_number_matrix(mob_matrix, np.zeros((3, 3)).astype('int'))
          self.wait()
  
  
      def replace_number_matrix(self, matrix, new_numbers):
          starters = matrix.flatten()
          targets = map(TexMobject, map(str, np.array(new_numbers).flatten()))
          for start, target in zip(starters, targets):
              target.move_to(start)
              target.set_color(start.get_color())
          self.play(*[
              Transform(*pair, path_arc = np.pi)
              for pair in zip(starters, targets)
          ])
  
  class IHatShear(LinearTransformationScene):
      CONFIG = {
          "foreground_plane_kwargs" : {
              "x_radius" : FRAME_WIDTH,
              "y_radius" : FRAME_WIDTH,
              "secondary_line_ratio" : 0
          },
      }
      def construct(self):
          self.apply_transposed_matrix([[1, 1], [0, 1]])
          self.wait()
  
  class DiagonalDegenerate(LinearTransformationScene):
      def construct(self):
          self.apply_transposed_matrix([[1, 1], [1, 1]])
  
  class ZeroMatirx(LinearTransformationScene):
      def construct(self):
          origin = Dot(ORIGIN)
          self.play(Transform(
              VMobject(self.plane, self.i_hat, self.j_hat),
              origin,
              run_time = 3
          ))
          self.wait()
  
  class RankNumber(Scene):
      CONFIG = {
          "number" : 3,
          "color" : BLUE
      }
      def construct(self):
          words = TextMobject("Rank", "%d"%self.number)
          words[1].set_color(self.color)
          self.add(words)
  
  class RankNumber2(RankNumber):
      CONFIG = {
          "number" : 2,
          "color" : RED,
      }
  
  class RankNumber1(RankNumber):
      CONFIG = {
          "number" : 1,
          "color" : GREEN
      }
  
  class RankNumber0(RankNumber):
      CONFIG = {
          "number" : 0,
          "color" : GREY
      }  
  
  class NameFullRank(Scene):
      def construct(self):
          matrix = Matrix([[2, 5, 1], [3, 1, 4], [-4, 0, 0]])
          matrix.set_color_columns(X_COLOR, Y_COLOR, Z_COLOR)
          matrix.to_edge(UP)
          brace = Brace(matrix)
          top_words = brace.get_text(
              "When", "rank", "$=$", "number of columns",
          )
          top_words[1].set_color(MAROON)
          low_words = TextMobject(
              "matrix is", "``full rank''"
          )
          low_words[1].set_color(MAROON)
          low_words.next_to(top_words, DOWN)
          VMobject(matrix, brace, top_words, low_words).to_corner(UP+LEFT)
          self.add(matrix)
          self.play(
              GrowFromCenter(brace),
              Write(top_words)
          )
          self.wait()
          self.play(Write(low_words))
          self.wait()
  
  class OriginIsAlwaysInColumnSpace(LinearTransformationScene):
      def construct(self):
          vector = Matrix([0, 0]).set_color(YELLOW)
          words = TextMobject("is always in the", "column space")
          words[1].set_color(TEAL)
          words.next_to(vector, RIGHT)
          vector.add_to_back(BackgroundRectangle(vector))
          words.add_background_rectangle()
          VMobject(vector, words).center().to_edge(UP)
          arrow = Arrow(vector.get_bottom(), ORIGIN)
          dot = Dot(ORIGIN, color = YELLOW)
  
          self.play(Write(vector), Write(words))
          self.play(ShowCreation(arrow))
          self.play(ShowCreation(dot, run_time = 0.5))
          self.add_foreground_mobject(vector, words, arrow, dot)
          self.wait()
          self.apply_transposed_matrix(self.t_matrix)
          self.wait()
          self.apply_transposed_matrix([[1./3, -1./2], [-1./3, 1./2]])
          self.wait()
  
  class FullRankCase(LinearTransformationScene):
      CONFIG = {
          "foreground_plane_kwargs" : {
              "x_radius" : FRAME_WIDTH,
              "y_radius" : FRAME_WIDTH,
              "secondary_line_ratio" : 0
          },
      }
      def construct(self):
          t_matrices = [
              [[2, 1], [-3, 2]],
              [[1./2, 1], [1./3, -1./2]]
          ]
          vector = Matrix([0, 0]).set_color(YELLOW)
          title = VMobject(
              TextMobject("Only"), vector,
              TextMobject("lands on"), vector.copy()
          )
          title.arrange_submobjects(buff = 0.2)
          title.to_edge(UP)
          for mob in title:
              mob.add_to_back(BackgroundRectangle(mob))
          arrow = Arrow(vector.get_bottom(), ORIGIN)
          dot = Dot(ORIGIN, color = YELLOW) 
  
          words_on = False
          for t_matrix in t_matrices:
              self.apply_transposed_matrix(t_matrix)
              if not words_on:
                  self.play(Write(title))
                  self.play(ShowCreation(arrow))
                  self.play(ShowCreation(dot, run_time = 0.5))
                  self.add_foreground_mobject(title, arrow, dot)
                  words_on = True
              self.apply_inverse_transpose(t_matrix)
              self.wait()
  
  class NameNullSpace(LinearTransformationScene):
      CONFIG = {
          "show_basis_vectors" : False,
          "t_matrix" : [[1, -1], [-1, 1]]
      }
      def construct(self):
          vectors = self.get_vectors()
          dot = Dot(ORIGIN, color = YELLOW)
          line = Line(vectors[0].get_end(), vectors[-1].get_end())
          line.set_color(YELLOW)
          null_space_label = TextMobject("``Null space''")
          kernel_label = TextMobject("``Kernel''")
          null_space_label.move_to(vectors[13].get_end(), aligned_edge = UP+LEFT)
          kernel_label.next_to(null_space_label, DOWN)
          for mob in null_space_label, kernel_label:
              mob.set_color(YELLOW)
              mob.add_background_rectangle()
  
          self.play(ShowCreation(vectors, run_time = 3))
          self.wait()
          vectors.save_state()
          self.plane.save_state()
          self.apply_transposed_matrix(
              self.t_matrix,
              added_anims = [Transform(vectors, dot)],
              path_arc = 0
          )
          self.wait()
          self.play(
              vectors.restore, 
              self.plane.restore, 
              *map(Animation, self.foreground_mobjects),
              run_time = 2
          )
          self.play(Transform(
              vectors, line, 
              run_time = 2,
              submobject_mode = "lagged_start"
          ))
          self.wait()
          for label in null_space_label, kernel_label:
              self.play(Write(label))
              self.wait()
          self.apply_transposed_matrix(
              self.t_matrix,
              added_anims = [
                  Transform(vectors, dot),
                  ApplyMethod(null_space_label.scale, 0),
                  ApplyMethod(kernel_label.scale, 0),
              ],
              path_arc = 0
          )
          self.wait()
  
      def get_vectors(self, offset = 0):
          vect = np.array(UP+RIGHT)
          vectors = VMobject(*[
              Vector(a*vect + offset)
              for a in np.linspace(-5, 5, 18)
          ])
          vectors.set_submobject_colors_by_gradient(PINK, YELLOW)
          return vectors
  
  class ThreeDNullSpaceIsLine(Scene):
      pass
  
  class ThreeDNullSpaceIsPlane(Scene):
      pass
  
  class NullSpaceSolveForVEqualsZero(NameNullSpace):
      def construct(self):
          vec = lambda s : "\\vec{\\textbf{%s}}"%s
          equation = TexMobject("A", vec("x"), "=", vec("v"))
          A, x, eq, v = equation
          x.set_color(PINK)
          v.set_color(YELLOW)
          zero_vector = Matrix([0, 0])
          zero_vector.set_color(YELLOW)
          zero_vector.scale(0.7)
          zero_vector.move_to(v, aligned_edge = LEFT)
          VMobject(equation, zero_vector).next_to(ORIGIN, LEFT).to_edge(UP)
          zero_vector_rect = BackgroundRectangle(zero_vector)        
          equation.add_background_rectangle()
  
          self.play(Write(equation))
          self.wait()
          self.play(
              ShowCreation(zero_vector_rect),
              Transform(v, zero_vector)
          )
          self.wait()
          self.add_foreground_mobject(zero_vector_rect, equation)
          NameNullSpace.construct(self)
  
  class OffsetNullSpace(NameNullSpace):
      def construct(self):
          x = Vector([-2, 1], color = RED)
          vectors = self.get_vectors()
          offset_vectors = self.get_vectors(offset = x.get_end())
          dots = VMobject(*[
              Dot(v.get_end(), color = v.get_color())
              for v in offset_vectors
          ])
          dot = Dot(
              self.get_matrix_transformation(self.t_matrix)(x.get_end()),
              color = RED
          )
          circle = Circle(color = YELLOW).replace(dot)
          circle.scale_in_place(5)
          words = TextMobject("""
              All vectors still land 
              on the same spot
          """)
          words.set_color(YELLOW)
          words.add_background_rectangle()
          words.next_to(circle)
          x_copies = VMobject(*[
              x.copy().shift(v.get_end())
              for v in vectors
          ])
  
          self.play(FadeIn(vectors))
          self.wait()
          self.add_vector(x, animate = True)
          self.wait()
          x_copy = VMobject(x.copy())
          self.play(Transform(x_copy, x_copies))
          self.play(
              Transform(vectors, offset_vectors),
              *[
                  Transform(v, VectorizedPoint(v.get_end()))
                  for v in x_copy
              ]
          )
          self.remove(x_copy)
          self.wait()
          self.play(Transform(vectors, dots))
          self.wait()
          self.apply_transposed_matrix(
              self.t_matrix,
              added_anims = [Transform(vectors, dot)]
          )
          self.wait()        
          self.play(
              ShowCreation(circle),
              Write(words)
          )
          self.wait()
  
  class ShowAdditivityProperty(LinearTransformationScene):
      CONFIG = {
          "show_basis_vectors" : False,
          "t_matrix" : [[1, 0.5], [-1, 1]],
          "include_background_plane" : False,
      }
      def construct(self):
          v = Vector([2, -1])
          w = Vector([1, 2])
          v.set_color(YELLOW)
          w.set_color(MAROON_B)
          sum_vect = Vector(v.get_end()+w.get_end(), color = PINK)
          form = TexMobject(
              "A(",
              "\\vec{\\textbf{v}}",
              "+",
              "\\vec{\\textbf{w}}",
              ")",
              "=A",
              "\\vec{\\textbf{v}}",
              "+A",
              "\\vec{\\textbf{w}}",
          )
          form.to_corner(UP+RIGHT)
          VMobject(form[1], form[6]).set_color(YELLOW)
          VMobject(form[3], form[8]).set_color(MAROON_B)
          initial_sum = VMobject(*form[1:4])
          transformer = VMobject(form[0], form[4])
          final_sum = VMobject(*form[5:])
          form_rect = BackgroundRectangle(form)
  
          self.add(form_rect)
          self.add_vector(v, animate = True)
          self.add_vector(w, animate = True)
          w_copy = w.copy()
          self.play(w_copy.shift, v.get_end())
          self.add_vector(sum_vect, animate = True)
          self.play(
              Write(initial_sum),
              FadeOut(w_copy)
          )
          self.add_foreground_mobject(form_rect, initial_sum)
          self.apply_transposed_matrix(
              self.t_matrix,
              added_anims = [Write(transformer)]
          )
          self.wait()
          self.play(w.copy().shift, v.get_end())
          self.play(Write(final_sum))
          self.wait()
  
  class AddJustOneNullSpaceVector(NameNullSpace):
      def construct(self):
          vectors = self.get_vectors()
          self.add(vectors)
          null_vector = vectors[int(0.7*len(vectors.split()))]
          vectors.remove(null_vector)
          null_vector.label = "$\\vec{\\textbf{n}}$"
          x = Vector([-1, 1], color = RED)
          x.label = "$\\vec{\\textbf{x}}$"
          sum_vect = Vector(
              x.get_end() + null_vector.get_end(),
              color = PINK
          )
          for v in x, null_vector:
              v.label = TextMobject(v.label)
              v.label.set_color(v.get_color())
              v.label.next_to(v.get_end(), UP)
              v.label.add_background_rectangle()
          dot = Dot(ORIGIN, color = null_vector.get_color())
  
          form = TexMobject(
              "A(",
              "\\vec{\\textbf{x}}",
              "+",
              "\\vec{\\textbf{n}}",
              ")",
              "=A",
              "\\vec{\\textbf{x}}",
              "+A",
              "\\vec{\\textbf{n}}",
          )
          form.to_corner(UP+RIGHT)
          VMobject(form[1], form[6]).set_color(x.get_color())
          VMobject(form[3], form[8]).set_color(null_vector.get_color())
          initial_sum = VMobject(*form[1:4])
          transformer = VMobject(form[0], form[4])
          final_sum = VMobject(*form[5:])
          brace = Brace(VMobject(*form[-2:]))
          brace.add(brace.get_text("+0").add_background_rectangle())
          form_rect = BackgroundRectangle(form)
          sum_vect.label = initial_sum.copy()
          sum_vect.label.next_to(sum_vect.get_end(), UP)
  
          self.add_vector(x, animate = True)
          self.play(Write(x.label))
          self.wait()
          self.play(
              FadeOut(vectors),
              Animation(null_vector)
          )
          self.play(Write(null_vector.label))
          self.wait()
          x_copy = x.copy()
          self.play(x_copy.shift, null_vector.get_end())
          self.add_vector(sum_vect, animate = True)
          self.play(
              FadeOut(x_copy),
              Write(sum_vect.label)
          )
          self.wait()
          self.play(
              ShowCreation(form_rect),
              sum_vect.label.replace, initial_sum
          )
          self.add_foreground_mobject(form_rect, sum_vect.label)
          self.remove(x.label, null_vector.label)
          self.apply_transposed_matrix(
              self.t_matrix,
              added_anims = [
                  Transform(null_vector, dot),
                  Write(transformer)
              ]
          )
          self.play(Write(final_sum))
          self.wait()
          self.play(Write(brace))
          self.wait()
          words = TextMobject(
              "$\\vec{\\textbf{x}}$",
              "and the",
              "$\\vec{\\textbf{x}} + \\vec{\\textbf{n}}$\\\\",
              "land on the same spot"
          )
          words[0].set_color(x.get_color())
          VMobject(*words[2][:2]).set_color(x.get_color())
          VMobject(*words[2][3:]).set_color(null_vector.get_color())
          words.next_to(brace, DOWN)
          words.to_edge(RIGHT)
          self.play(Write(words))
          self.wait()
  
  class NullSpaceOffsetRule(Scene):
      def construct(self):
          vec = lambda s : "\\vec{\\textbf{%s}}"%s
          equation = TexMobject("A", vec("x"), "=", vec("v"))
          A, x, equals, v = equation
          x.set_color(PINK)
          v.set_color(YELLOW)
          A_text = TextMobject(
              "When $A$ is not", "full rank"
          )
          A_text[1].set_color(MAROON_C)
          A_text.next_to(A, UP+LEFT, buff = 1)
          A_text.shift_onto_screen()
          A_arrow = Arrow(A_text.get_bottom(), A, color = WHITE)
          v_text = TextMobject(
              "If", "$%s$"%vec("v"), "is in the", 
              "column space", "of $A$"
          )
          v_text[1].set_color(YELLOW)
          v_text[3].set_color(TEAL)
          v_text.next_to(v, DOWN+RIGHT, buff = 1)
          v_text.shift_onto_screen()
          v_arrow = Arrow(v_text.get_top(), v, color = YELLOW)
  
  
          self.add(equation)
          self.play(Write(A_text, run_time = 2))
          self.play(ShowCreation(A_arrow))
          self.wait()
          self.play(Write(v_text, run_time = 2))
          self.play(ShowCreation(v_arrow))
          self.wait()
  
  class MuchLeftToLearn(TeacherStudentsScene):
      def construct(self):
          self.teacher_says(
              "That's the high \\\\",
              "level overview"
          )
          self.random_blink()
          self.wait()
          self.teacher_says(
              "There is still \\\\",
              "much to learn"
          )
          for pi in self.get_students():
              target_mode = random.choice([
                  "raise_right_hand", "raise_left_hand"
              ])
              self.play(pi.change_mode, target_mode)
          self.random_blink()
          self.wait()
  
  class NotToLearnItAllNow(TeacherStudentsScene):
      def construct(self):
          self.teacher_says("""
              The goal is not to
              learn it all now
          """)
          self.random_blink()
          self.wait()
          self.random_blink()
  
  class NextVideo(Scene):
      def construct(self):
          title = TextMobject("""
              Next video: Nonsquare matrices
          """)
          title.scale_to_fit_width(FRAME_WIDTH - 2)
          title.to_edge(UP)
          rect = Rectangle(width = 16, height = 9, color = BLUE)
          rect.scale_to_fit_height(6)
          rect.next_to(title, DOWN)
  
          self.add(title)
          self.play(ShowCreation(rect))
          self.wait()  
  
  class WhatAboutNonsquareMatrices(TeacherStudentsScene):
      def construct(self):
          self.student_says(
              "What about \\\\ nonsquare matrices?",
              target_mode = "raise_right_hand"
          )
          self.play(self.get_students()[0].change_mode, "confused")
          self.random_blink(6)
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  