from big_ol_pile_of_manim_imports import *
from old_projects.eola.chapter1 import plane_wave_homotopy

class OpeningQuote(Scene):
    def construct(self):
        words = TextMobject("""
            Mathematics requires a small dose, not of genius, 
            but of an imaginative freedom which, in a larger 
            dose, would be insanity.
        """)
        words.to_edge(UP)    
        for mob in words.submobjects[49:49+18]:
            mob.set_color(GREEN)
        author = TextMobject("-Angus K. Rodgers")
        author.set_color(YELLOW)
        author.next_to(words, DOWN, buff = 0.5)

        self.play(FadeIn(words))
        self.wait(3)
        self.play(Write(author, run_time = 3))
        self.wait()


class CoordinatesWereFamiliar(TeacherStudentsScene):
    def construct(self):
        self.setup()
        self.student_says("I know this already")
        self.random_blink()
        self.teacher_says("Ah, but there is a subtlety")
        self.random_blink()
        self.wait()


class CoordinatesAsScalars(VectorScene):
    CONFIG = {
        "vector_coords" : [3, -2]
    }

    def construct(self):
        self.lock_in_faded_grid()

        vector = self.add_vector(self.vector_coords)
        array, x_line, y_line = self.vector_to_coords(vector)
        self.add(array)
        self.wait()
        new_array = self.general_idea_of_scalars(array, vector)
        self.scale_basis_vectors(new_array)
        self.show_symbolic_sum(new_array, vector)

    def general_idea_of_scalars(self, array, vector):
        starting_mobjects = self.get_mobjects()

        title = TextMobject("Think of each coordinate as a scalar")
        title.to_edge(UP)

        x, y = array.get_mob_matrix().flatten()
        new_x = x.copy().scale(2).set_color(X_COLOR)
        new_x.move_to(3*LEFT+2*UP)
        new_y = y.copy().scale(2).set_color(Y_COLOR)
        new_y.move_to(3*RIGHT+2*UP)

        i_hat, j_hat = self.get_basis_vectors()
        new_i_hat = Vector(
            self.vector_coords[0]*i_hat.get_end(), 
            color = X_COLOR
        )
        new_j_hat = Vector(
            self.vector_coords[1]*j_hat.get_end(), 
            color = Y_COLOR
        )
        VMobject(i_hat, new_i_hat).shift(3*LEFT)
        VMobject(j_hat, new_j_hat).shift(3*RIGHT)

        new_array = Matrix([new_x.copy(), new_y.copy()])
        new_array.scale(0.5)
        new_array.shift(
            -new_array.get_boundary_point(-vector.get_end()) + \
            1.1*vector.get_end()
        )

        self.remove(*starting_mobjects)
        self.play(
            Transform(x, new_x),
            Transform(y, new_y),
            Write(title),
        )
        self.play(FadeIn(i_hat), FadeIn(j_hat))
        self.wait()
        self.play(
            Transform(i_hat, new_i_hat),
            Transform(j_hat, new_j_hat),
            run_time = 3
        )
        self.wait()
        starting_mobjects.remove(array)

        new_x, new_y = new_array.get_mob_matrix().flatten()
        self.play(
            Transform(x, new_x),
            Transform(y, new_y),
            FadeOut(i_hat),
            FadeOut(j_hat),
            Write(new_array.get_brackets()),
            FadeIn(VMobject(*starting_mobjects)),
            FadeOut(title)
        )
        self.remove(x, y)
        self.add(new_array)
        return new_array

    def scale_basis_vectors(self, new_array):
        i_hat, j_hat = self.get_basis_vectors()
        self.add_vector(i_hat)
        i_hat_label = self.label_vector(
            i_hat, "\\hat{\\imath}", 
            color = X_COLOR, 
            label_scale_factor = 1
        )
        self.add_vector(j_hat)
        j_hat_label = self.label_vector(
            j_hat, "\\hat{\\jmath}", 
            color = Y_COLOR, 
            label_scale_factor = 1
        )
        self.wait()

        x, y = new_array.get_mob_matrix().flatten()
        for coord, v, label, factor, shift_right in [
            (x, i_hat, i_hat_label, self.vector_coords[0], False), 
            (y, j_hat, j_hat_label, self.vector_coords[1], True)
            ]:
            faded_v = v.copy().fade(0.7)
            scaled_v = Vector(factor*v.get_end(), color = v.get_color())

            scaled_label = VMobject(coord.copy(), label.copy())
            scaled_label.arrange(RIGHT, buff = 0.1)
            scaled_label.move_to(label, DOWN+RIGHT)
            scaled_label.shift((scaled_v.get_end()-v.get_end())/2)
            coord_copy = coord.copy()
            self.play(
                Transform(v.copy(), faded_v),
                Transform(v, scaled_v),
                Transform(VMobject(coord_copy, label), scaled_label),
            )
            self.wait()
            if shift_right:
                group = VMobject(v, coord_copy, label)
                self.play(ApplyMethod(
                    group.shift, self.vector_coords[0]*RIGHT
                ))
        self.wait()


    def show_symbolic_sum(self, new_array, vector):
        new_mob = TexMobject([
            "(%d)\\hat{\\imath}"%self.vector_coords[0], 
            "+", 
            "(%d)\\hat{\\jmath}"%self.vector_coords[1]
        ])
        new_mob.move_to(new_array)
        new_mob.shift_onto_screen()
        i_hat, plus, j_hat = new_mob.split()
        i_hat.set_color(X_COLOR)
        j_hat.set_color(Y_COLOR)

        self.play(Transform(new_array, new_mob))
        self.wait()
        


class CoordinatesAsScalarsExample2(CoordinatesAsScalars):
    CONFIG = {
        "vector_coords" : [-5, 2]
    }

    def construct(self):
        self.lock_in_faded_grid()

        basis_vectors = self.get_basis_vectors()
        labels = self.get_basis_vector_labels()
        self.add(*basis_vectors)
        self.add(*labels)
        text = TextMobject("""
            $\\hat{\\imath}$ and $\\hat{\\jmath}$ 
            are the ``basis vectors'' \\\\
            of the $xy$ coordinate system
        """)
        text.set_width(FRAME_X_RADIUS-1)
        text.to_corner(UP+RIGHT)
        VMobject(*text.split()[:2]).set_color(X_COLOR)
        VMobject(*text.split()[5:7]).set_color(Y_COLOR)
        self.play(Write(text))
        self.wait(2)
        self.remove(*basis_vectors + labels)
        CoordinatesAsScalars.construct(self)


class WhatIfWeChoseADifferentBasis(Scene):
    def construct(self):
        self.play(Write(
            "What if we chose different basis vectors?",
            run_time = 2
        ))
        self.wait(2)

class ShowVaryingLinearCombinations(VectorScene):
    CONFIG = {
        "vector1" : [1, 2],
        "vector2" : [3, -1],
        "vector1_color" : MAROON_C,
        "vector2_color" : BLUE,
        "vector1_label" : "v",
        "vector2_label" : "w",
        "sum_color" : PINK,
        "scalar_pairs" : [
            (1.5, 0.6),
            (0.7, 1.3),
            (-1, -1.5),
            (1, -1.13),
            (1.25, 0.5),
            (-0.8, 1.3),
        ], 
        "leave_sum_vector_copies" : False,
        "start_with_non_sum_scaling" : True,
        "finish_with_standard_basis_comparison" : True,
        "finish_by_drawing_lines" : False,
    }
    def construct(self):
        self.lock_in_faded_grid()
        v1 = self.add_vector(self.vector1, color = self.vector1_color)
        v2 = self.add_vector(self.vector2, color = self.vector2_color)
        v1_label = self.label_vector(
            v1, self.vector1_label, color = self.vector1_color, 
            buff_factor = 3
        )
        v2_label = self.label_vector(
            v2, self.vector2_label, color = self.vector2_color, 
            buff_factor = 3
        )
        label_anims = [
            MaintainPositionRelativeTo(label, v)
            for v, label in [(v1, v1_label), (v2, v2_label)]
        ]
        scalar_anims = self.get_scalar_anims(v1, v2, v1_label, v2_label)
        self.last_scalar_pair = (1, 1)

        if self.start_with_non_sum_scaling:
            self.initial_scaling(v1, v2, label_anims, scalar_anims)
        self.show_sum(v1, v2, label_anims, scalar_anims)
        self.scale_with_sum(v1, v2, label_anims, scalar_anims)
        if self.finish_with_standard_basis_comparison:
            self.standard_basis_comparison(label_anims, scalar_anims)
        if self.finish_by_drawing_lines:
            self.draw_lines(v1, v2, label_anims, scalar_anims)

    def get_scalar_anims(self, v1, v2, v1_label, v2_label):
        def get_val_func(vect):
            original_vect = np.array(vect.get_end()-vect.get_start())
            square_norm = get_norm(original_vect)**2
            return lambda a : np.dot(
                original_vect, vect.get_end()-vect.get_start()
            )/square_norm
        return [
            RangingValues(
                tracked_mobject = label,
                tracked_mobject_next_to_kwargs = {
                    "direction" : LEFT,
                    "buff" : 0.1
                },
                scale_factor = 0.75,
                value_function = get_val_func(v)
            )
            for v, label in [(v1, v1_label), (v2, v2_label)]
        ]

    def get_rate_func_pair(self):
        return [
            squish_rate_func(smooth, a, b) 
            for a, b in [(0, 0.7), (0.3, 1)]
        ] 

    def initial_scaling(self, v1, v2, label_anims, scalar_anims):
        scalar_pair = self.scalar_pairs.pop(0)
        anims = [
            ApplyMethod(v.scale, s, rate_func = rf)
            for v, s, rf in zip(
                [v1, v2],
                scalar_pair,
                self.get_rate_func_pair()
            )
        ]
        anims += [
            ApplyMethod(v.copy().fade, 0.7)
            for v in (v1, v2)
        ]
        anims += label_anims + scalar_anims
        self.play(*anims, **{"run_time" : 2})
        self.wait()
        self.last_scalar_pair = scalar_pair

    def show_sum(self, v1, v2, label_anims, scalar_anims):
        self.play(
            ApplyMethod(v2.shift, v1.get_end()),
            *label_anims + scalar_anims
        )
        self.sum_vector = self.add_vector(
            v2.get_end(), color = self.sum_color
        )
        self.wait()

    def scale_with_sum(self, v1, v2, label_anims, scalar_anims):
        v2_anim, sum_anim = self.get_sum_animations(v1, v2)
        while self.scalar_pairs:
            scalar_pair = self.scalar_pairs.pop(0)
            anims = [
                ApplyMethod(v.scale, s/s_old, rate_func = rf)
                for v, s, s_old, rf in zip(
                    [v1, v2], 
                    scalar_pair, 
                    self.last_scalar_pair,
                    self.get_rate_func_pair()
                )
            ]
            anims += [v2_anim, sum_anim] + label_anims + scalar_anims
            self.play(*anims, **{"run_time" : 2})
            if self.leave_sum_vector_copies:
                self.add(self.sum_vector.copy())
            self.wait()
            self.last_scalar_pair = scalar_pair

    def get_sum_animations(self, v1, v2):
        v2_anim = UpdateFromFunc(
            v2, lambda m : m.shift(v1.get_end()-m.get_start())
        )
        sum_anim = UpdateFromFunc(
            self.sum_vector, 
            lambda v : v.put_start_and_end_on(v1.get_start(), v2.get_end())
        )
        return v2_anim, sum_anim

    def standard_basis_comparison(self, label_anims, scalar_anims):
        everything = self.get_mobjects()
        everything.remove(self.sum_vector)
        everything = VMobject(*everything)
        alt_coords = [a.mobject for a in scalar_anims]
        array = Matrix([
            mob.copy().set_color(color)
            for mob, color in zip(
                alt_coords, 
                [self.vector1_color, self.vector2_color]
            )
        ])
        array.scale(0.8)
        array.to_edge(UP)
        array.shift(RIGHT)
        brackets = array.get_brackets()

        anims = [
            Transform(*pair)
            for pair in zip(alt_coords, array.get_mob_matrix().flatten())
        ]
        # anims += [
        #     FadeOut(a.mobject)
        #     for a in label_anims
        # ]
        self.play(*anims + [Write(brackets)])
        self.wait()
        self.remove(brackets, *alt_coords)
        self.add(array)
        self.play(
            FadeOut(everything), 
            Animation(array),
        )

        self.add_axes(animate = True)
        ij_array, x_line, y_line = self.vector_to_coords(
            self.sum_vector, integer_labels = False
        )
        self.add(ij_array, x_line, y_line)
        x, y = ij_array.get_mob_matrix().flatten()
        self.play(
            ApplyMethod(x.set_color, X_COLOR),
            ApplyMethod(y.set_color, Y_COLOR),
        )
        neq = TexMobject("\\neq")
        neq.next_to(array)
        self.play(
            ApplyMethod(ij_array.next_to, neq),
            Write(neq)
        )
        self.wait()

    def draw_lines(self, v1, v2, label_anims, scalar_anims):
        sum_anims = self.get_sum_animations(v1, v2)
        aux_anims = list(sum_anims) + label_anims + scalar_anims

        scale_factor = 2
        for w1, w2 in (v2, v1), (v1, v2):
            w1_vect = w1.get_end()-w1.get_start()
            w2_vect = w2.get_end()-w2.get_start()
            for num in scale_factor, -1, -1./scale_factor:
                curr_tip = self.sum_vector.get_end()
                line = Line(ORIGIN, curr_tip)
                self.play(
                    ApplyMethod(w2.scale, num), 
                    UpdateFromFunc(
                        line, lambda l : l.put_start_and_end_on(curr_tip, self.sum_vector.get_end())
                    ),
                    *aux_anims
                )
                self.add(line, v2)
            self.wait()



class AltShowVaryingLinearCombinations(ShowVaryingLinearCombinations):
    CONFIG = {
        "scalar_pairs" : [
            (1.5, 0.3),
            (0.64, 1.3),
            (-1, -1.5),
            (1, 1.13),
            (1.25, 0.5),
            (-0.8, 1.14),
        ], 
        "finish_with_standard_basis_comparison" : False
    }


class NameLinearCombinations(Scene):
    def construct(self):
        v_color = MAROON_C
        w_color = BLUE
        words = TextMobject([
            "``Linear combination'' of",
            "$\\vec{\\textbf{v}}$",
            "and",
            "$\\vec{\\textbf{w}}$"
        ])
        words.split()[1].set_color(v_color)
        words.split()[3].set_color(w_color)
        words.set_width(FRAME_WIDTH - 1)
        words.to_edge(UP)

        equation = TexMobject([
            "a", "\\vec{\\textbf{v}}", "+", "b", "\\vec{\\textbf{w}}"
        ])
        equation.arrange(buff = 0.1, aligned_edge = DOWN)
        equation.split()[1].set_color(v_color)
        equation.split()[4].set_color(w_color)
        a, b = np.array(equation.split())[[0, 3]]
        equation.scale(2)
        equation.next_to(words, DOWN, buff = 1)

        scalars_word = TextMobject("Scalars")
        scalars_word.scale(1.5)
        scalars_word.next_to(equation, DOWN, buff = 2)
        arrows = [
            Arrow(scalars_word, letter)
            for letter in (a, b)
        ]

        self.add(equation)
        self.play(Write(words))
        self.play(
            ShowCreation(VMobject(*arrows)),
            Write(scalars_word)
        )
        self.wait(2)


class LinearCombinationsDrawLines(ShowVaryingLinearCombinations):
    CONFIG = {
        "scalar_pairs" : [
            (1.5, 0.6),
            (0.7, 1.3),
            (1, 1),
        ], 
        "start_with_non_sum_scaling" : False,
        "finish_with_standard_basis_comparison" : False,
        "finish_by_drawing_lines" : True,
    }


class LinearCombinationsWithSumCopies(ShowVaryingLinearCombinations):
    CONFIG = {
        "scalar_pairs" : [
            (1.5, 0.6),
            (0.7, 1.3),
            (-1, -1.5),
            (1, -1.13),
            (1.25, 0.5),
            (-0.8, 1.3),
            (-0.9, 1.4),
            (0.9, 2),
        ], 
        "leave_sum_vector_copies" : True,
        "start_with_non_sum_scaling" : False,
        "finish_with_standard_basis_comparison" : False,
        "finish_by_drawing_lines" : False,
    }



class LinearDependentVectors(ShowVaryingLinearCombinations):
    CONFIG = {
        "vector1" : [1, 2],
        "vector2" : [0.5, 1],
        "vector1_color" : MAROON_C,
        "vector2_color" : BLUE,
        "vector1_label" : "v",
        "vector2_label" : "w",
        "sum_color" : PINK,
        "scalar_pairs" : [
            (1.5, 0.6),
            (0.7, 1.3),
            (-1, -1.5),
            (1.25, 0.5),
            (-0.8, 1.3),
            (-0.9, 1.4),
            (0.9, 2),
        ], 
        "leave_sum_vector_copies" : False,
        "start_with_non_sum_scaling" : False,
        "finish_with_standard_basis_comparison" : False,
        "finish_by_drawing_lines" : False,
    }

    def get_sum_animations(self, v1, v2):
        v2_anim, sum_anim = ShowVaryingLinearCombinations.get_sum_animations(self, v1, v2) 
        self.remove(self.sum_vector)
        return v2_anim, Animation(VMobject())

class WhenVectorsLineUp(LinearDependentVectors):
    CONFIG = {
        "vector1" : [3, 2],
        "vector2" : [1.5, 1],
        "scalar_pairs" : [
            (1.5, 0.6),
            (0.7, 1.3),
        ], 
    }    

class AnimationUnderSpanDefinition(ShowVaryingLinearCombinations):
    CONFIG = {
        "scalar_pairs" : [
            (1.5, 0.6),
            (0.7, 1.3),
            (-1, -1.5),
            (1.25, 0.5),
            (0.8, 1.3),
            (0.93, -1.4),
            (-2, -0.5),
        ], 
        "leave_sum_vector_copies" : True,
        "start_with_non_sum_scaling" : False,
        "finish_with_standard_basis_comparison" : False,
        "finish_by_drawing_lines" : False,
    }


class BothVectorsCouldBeZero(VectorScene):
    def construct(self):
        plane = self.add_plane()
        plane.fade(0.7)
        v1 = self.add_vector([1, 2], color = MAROON_C)
        v2 = self.add_vector([3, -1], color = BLUE)
        self.play(Transform(v1, Dot(ORIGIN)))
        self.play(Transform(v2, Dot(ORIGIN)))
        self.wait()


class DefineSpan(Scene):
    def construct(self):
        v_color = MAROON_C
        w_color = BLUE

        definition = TextMobject("""
            The ``span'' of $\\vec{\\textbf{v}}$ and 
            $\\vec{\\textbf{w}}$ is the \\\\ set of all their
            linear combinations.
        """)
        definition.set_width(FRAME_WIDTH-1)
        definition.to_edge(UP)
        def_mobs = np.array(definition.split())
        VMobject(*def_mobs[4:4+4]).set_color(PINK)
        VMobject(*def_mobs[11:11+2]).set_color(v_color)
        VMobject(*def_mobs[16:16+2]).set_color(w_color)
        VMobject(*def_mobs[-19:-1]).set_color(YELLOW)

        equation = TexMobject([
            "a", "\\vec{\\textbf{v}}", "+", "b", "\\vec{\\textbf{w}}"
        ])
        equation.arrange(buff = 0.1, aligned_edge = DOWN)
        equation.split()[1].set_color(v_color)
        equation.split()[4].set_color(w_color)
        a, b = np.array(equation.split())[[0, 3]]
        equation.scale(2)
        equation.next_to(definition, DOWN, buff = 1)

        vary_words = TextMobject(
            "Let $a$ and $b$ vary \\\\ over all real numbers"
        )
        vary_words.scale(1.5)
        vary_words.next_to(equation, DOWN, buff = 2)
        arrows = [
            Arrow(vary_words, letter)
            for letter in (a, b)
        ]

        self.play(Write(definition))
        self.play(Write(equation))
        self.wait()
        self.play(
            FadeIn(vary_words),
            ShowCreation(VMobject(*arrows))
        )
        self.wait()


class VectorsVsPoints(Scene):
    def construct(self):
        self.play(Write("Vectors vs. Points"))
        self.wait(2)


class VectorsToDotsScene(VectorScene):
    CONFIG = {
        "num_vectors" : 16,
        "start_color" : PINK,
        "end_color" : BLUE_E,
    }
    def construct(self):
        self.lock_in_faded_grid()

        vectors = self.get_vectors()
        colors = Color(self.start_color).range_to(
            self.end_color, len(vectors)
        )
        for vect, color in zip(vectors, colors):
            vect.set_color(color)
        prototype_vector = vectors[3*len(vectors)/4]

        vector_group = VMobject(*vectors)
        self.play(
            ShowCreation(
                vector_group, 
                run_time = 3
            )
        )
        vectors.sort(key=lambda v: v.get_length())
        self.add(*vectors)
        def v_to_dot(vector):
            return Dot(vector.get_end(), fill_color = vector.get_stroke_color())
        self.wait()
        vectors.remove(prototype_vector)
        self.play(*list(map(FadeOut, vectors))+[Animation(prototype_vector)])
        self.remove(vector_group)
        self.add(prototype_vector)
        self.wait()
        self.play(Transform(prototype_vector, v_to_dot(prototype_vector)))
        self.wait()
        self.play(*list(map(FadeIn, vectors)) + [Animation(prototype_vector)])
        rate_functions = [
            squish_rate_func(smooth, float(x)/(len(vectors)+2), 1)
            for x in range(len(vectors))
        ]
        self.play(*[
            Transform(v, v_to_dot(v), rate_func = rf, run_time = 2) 
            for v, rf in zip(vectors, rate_functions)
        ])
        self.wait()
        self.remove(prototype_vector)
        self.play_final_animation(vectors, rate_functions)
        self.wait()

    def get_vectors(self):
        raise Exception("Not implemented")

    def play_final_animation(self, vectors, rate_functions):
        raise Exception("Not implemented")


class VectorsOnALine(VectorsToDotsScene):
    def get_vectors(self):
        return [
            Vector(a*np.array([1.5, 1]))
            for a in np.linspace(
                -FRAME_Y_RADIUS, FRAME_Y_RADIUS, self.num_vectors
            )
        ]

    def play_final_animation(self, vectors, rate_functions):
        line_copies = [
            Line(vectors[0].get_end(), vectors[-1].get_end())
            for v in vectors
        ]
        self.play(*[
            Transform(v, mob, rate_func = rf, run_time = 2)
            for v, mob, rf in zip(vectors, line_copies, rate_functions)
        ])


class VectorsInThePlane(VectorsToDotsScene):
    CONFIG = {
        "num_vectors" : 16,
        "start_color" : PINK,
        "end_color" : BLUE_E,
    }
    def get_vectors(self):
        return [
            Vector([x, y])
            for x in np.arange(-int(FRAME_X_RADIUS)-0.5, int(FRAME_X_RADIUS)+0.5)
            for y in np.arange(-int(FRAME_Y_RADIUS)-0.5, int(FRAME_Y_RADIUS)+0.5)
        ]

    def play_final_animation(self, vectors, rate_functions):
        h_line = Line(
            FRAME_X_RADIUS*RIGHT, FRAME_X_RADIUS*LEFT,
            stroke_width = 0.5,
            color = BLUE_E
        )
        v_line = Line(
            FRAME_Y_RADIUS*UP, FRAME_Y_RADIUS*DOWN,
            stroke_width = 0.5,
            color = BLUE_E
        )
        line_pairs = [
            VMobject(h_line.copy().shift(y), v_line.copy().shift(x))
            for v in vectors
            for x, y, z in [v.get_center()]
        ]
        plane = NumberPlane()
        self.play(
            ShowCreation(plane),
            *[
                Transform(v, p, rate_func = rf) 
                for v, p, rf in zip(vectors, line_pairs, rate_functions)
            ]
        )
        self.remove(*vectors)
        self.wait()


class HowToThinkVectorsVsPoint(Scene):
    def construct(self):
        randy = Randolph().to_corner()
        bubble = randy.get_bubble(height = 3.8)
        text1 = TextMobject("Think of individual vectors as arrows")
        text2 = TextMobject("Think of sets of vectors as points")
        for text in text1, text2:
            text.to_edge(UP)

        single_vector = Vector([2, 1])
        vector_group = VMobject(*[
            Vector([x, 1])
            for x in np.linspace(-2, 2, 5)
        ])
        bubble.position_mobject_inside(single_vector)
        bubble.position_mobject_inside(vector_group)
        dots = VMobject(*[
            Dot(v.get_end())
            for v in vector_group.split()
        ])

        self.add(randy)
        self.play(
            ApplyMethod(randy.change_mode, "pondering"),
            ShowCreation(bubble)
        )
        self.play(FadeIn(text1))
        self.play(ShowCreation(single_vector))
        self.wait(3)
        self.play(
            Transform(text1, text2),
            Transform(single_vector, vector_group)
        )
        self.remove(single_vector)
        self.add(vector_group)
        self.wait()
        self.play(Transform(vector_group, dots))
        self.wait()


class IntroduceThreeDSpan(Scene):
    pass

class AskAboutThreeDSpan(Scene):
    def construct(self):
        self.play(Write("What does the span of two 3d vectors look like?"))
        self.wait(2)

class ThreeDVectorSpan(Scene):
    pass


class LinearCombinationOfThreeVectors(Scene):
    pass

class VaryingLinearCombinationOfThreeVectors(Scene):
    pass

class LinearCombinationOfThreeVectorsText(Scene):
    def construct(self):
        text = TextMobject("""
            Linear combination of 
            $\\vec{\\textbf{v}}$, 
            $\\vec{\\textbf{w}}$, and
            $\\vec{\\textbf{u}}$:
        """)
        VMobject(*text.split()[-12:-10]).set_color(MAROON_C)
        VMobject(*text.split()[-9:-7]).set_color(BLUE)
        VMobject(*text.split()[-3:-1]).set_color(RED_C)
        VMobject(*text.split()[:17]).set_color(GREEN)        
        text.set_width(FRAME_WIDTH - 1)
        text.to_edge(UP)

        equation = TextMobject("""$
            a\\vec{\\textbf{v}} + 
            b\\vec{\\textbf{w}} + 
            c\\vec{\\textbf{u}}
        $""")
        VMobject(*equation.split()[-10:-8]).set_color(MAROON_C)
        VMobject(*equation.split()[-6:-4]).set_color(BLUE)
        VMobject(*equation.split()[-2:]).set_color(RED_C)

        a, b, c = np.array(equation.split())[[0, 4, 8]]

        equation.scale(1.5)
        equation.next_to(text, DOWN, buff = 1)

        span_comment = TextMobject("For span, let these constants vary")
        span_comment.scale(1.5)
        span_comment.next_to(equation, DOWN, buff = 2)
        VMobject(*span_comment.split()[3:7]).set_color(YELLOW)
        arrows = VMobject(*[
            Arrow(span_comment, var)
            for var in (a, b, c)
        ])

        self.play(Write(text))
        self.play(Write(equation))
        self.wait()
        self.play(
            ShowCreation(arrows),
            Write(span_comment)
        )
        self.wait()


class ThirdVectorOnSpanOfFirstTwo(Scene):
    pass

class ThirdVectorOutsideSpanOfFirstTwo(Scene):
    pass

class SpanCasesWords(Scene):
    def construct(self):
        words1 = TextMobject("""
            Case 1: $\\vec{\\textbf{u}}$ is in the span of
            $\\vec{\\textbf{v}}$ and $\\vec{\\textbf{u}}$
        """)
        VMobject(*words1.split()[6:8]).set_color(RED_C)
        VMobject(*words1.split()[-7:-5]).set_color(MAROON_C)
        VMobject(*words1.split()[-2:]).set_color(BLUE)

        words2 = TextMobject("""
            Case 2: $\\vec{\\textbf{u}}$ is not in the span of
            $\\vec{\\textbf{v}}$ and $\\vec{\\textbf{u}}$
        """)
        VMobject(*words2.split()[6:8]).set_color(RED_C)
        VMobject(*words2.split()[-7:-5]).set_color(MAROON_C)
        VMobject(*words2.split()[-2:]).set_color(BLUE)
        VMobject(*words2.split()[10:13]).set_color(RED)

        for words in words1, words2:
            words.set_width(FRAME_WIDTH - 1)
        self.play(Write(words1))
        self.wait()
        self.play(Transform(words1, words2))
        self.wait()



class LinearDependentWords(Scene):
    def construct(self):
        words1 = TextMobject([
            "$\\vec{\\textbf{v}}$", 
            "and",
            "$\\vec{\\textbf{w}}$",
            "are",
            "``Linearly dependent'' ",
        ])
        v, _and, w, are, rest = words1.split()
        v.set_color(MAROON_C)
        w.set_color(BLUE)
        rest.set_color(YELLOW)

        words2 = TextMobject([
            "$\\vec{\\textbf{v}}$,", 
            "$\\vec{\\textbf{w}}$",
            "and",
            "$\\vec{\\textbf{u}}$",
            "are",
            "``Linearly dependent'' ",
        ])
        v, w, _and, u, are, rest = words2.split()
        v.set_color(MAROON_C)
        w.set_color(BLUE)
        u.set_color(RED_C)
        rest.set_color(YELLOW)

        for words in words1, words2:
            words.set_width(FRAME_WIDTH - 1)

        self.play(Write(words1))
        self.wait()
        self.play(Transform(words1, words2))
        self.wait()


class LinearDependentEquations(Scene):
    def construct(self):
        title = TextMobject("``Linearly dependent'' ")
        title.set_color(YELLOW)
        title.scale(2)
        title.to_edge(UP)
        self.add(title)

        equation1 = TexMobject([
            "\\vec{\\textbf{w}}",
            "=",
            "a",
            "\\vec{\\textbf{v}}",
        ])
        w, eq, a, v = equation1.split()
        w.set_color(BLUE)
        v.set_color(MAROON_C)
        equation1.scale(2)
        eq1_copy = equation1.copy()

        low_words1 = TextMobject("For some value of $a$")
        low_words1.scale(2)
        low_words1.to_edge(DOWN)
        arrow = Arrow(low_words1, a)
        arrow_copy = arrow.copy()

        equation2 = TexMobject([
            "\\vec{\\textbf{u}}",
            "=",
            "a",
            "\\vec{\\textbf{v}}",
            "+",
            "b",
            "\\vec{\\textbf{w}}",
        ])
        u, eq, a, v, plus, b, w = equation2.split()
        u.set_color(RED)
        w.set_color(BLUE)
        v.set_color(MAROON_C)
        equation2.scale(2)
        eq2_copy = equation2.copy()

        low_words2 = TextMobject("For some values of a and b")
        low_words2.scale(2)
        low_words2.to_edge(DOWN)
        arrows = VMobject(*[
            Arrow(low_words2, var)
            for var in (a, b)
        ])

        self.play(Write(equation1))
        self.play(
            ShowCreation(arrow),
            Write(low_words1)
        )
        self.wait()

        self.play(
            Transform(equation1, equation2),
            Transform(low_words1, low_words2),
            Transform(arrow, arrows)
        )
        self.wait(2)

        new_title = TextMobject("``Linearly independent'' ")
        new_title.set_color(GREEN)
        new_title.replace(title)

        for eq_copy in eq1_copy, eq2_copy:
            neq = TexMobject("\\neq")
            neq.replace(eq_copy.submobjects[1])
            eq_copy.submobjects[1] = neq

        new_low_words1 = TextMobject(["For", "all", "values of a"])
        new_low_words2 = TextMobject(["For", "all", "values of a and b"])
        for low_words in new_low_words1, new_low_words2:
            low_words.split()[1].set_color(GREEN)
            low_words.scale(2)
            low_words.to_edge(DOWN)

        self.play(
            Transform(title, new_title),
            Transform(equation1, eq1_copy),
            Transform(arrow, arrow_copy),
            Transform(low_words1, new_low_words1)
        )
        self.wait()
        self.play(
            Transform(equation1, eq2_copy),
            Transform(arrow, arrows),
            Transform(low_words1, new_low_words2)
        )
        self.wait()



class AlternateDefOfLinearlyDependent(Scene):
    def construct(self):
        title1 = TextMobject([
            "$\\vec{\\textbf{v}}$,",
            "$\\vec{\\textbf{w}}$",
            "and",
            "$\\vec{\\textbf{u}}$",
            "are",
            "linearly dependent",
            "if",
        ])
        title2 = TextMobject([
            "$\\vec{\\textbf{v}}$,",
            "$\\vec{\\textbf{w}}$",
            "and",
            "$\\vec{\\textbf{u}}$",
            "are",
            "linearly independent",
            "if",
        ])
        for title in title1, title2:
            v, w, _and, u, are, ld, _if = title.split()
            v.set_color(MAROON_C)
            w.set_color(BLUE)
            u.set_color(RED_C)
            title.to_edge(UP)
        title1.split()[-2].set_color(YELLOW)
        title2.split()[-2].set_color(GREEN)

        subtitle = TextMobject("the only solution to")
        subtitle.next_to(title2, DOWN, aligned_edge = LEFT)

        self.add(title1)

        equations = self.get_equations()
        added_words1 = TextMobject(
            "where at least one of $a$, $b$ and $c$ is not $0$"
        )
        added_words2 = TextMobject(
            "is a = b = c = 0"
        )

        scalar_specification = TextMobject(
            "For some choice of $a$ and $b$"
        )
        scalar_specification.shift(1.5*DOWN)
        scalar_specification.add(*[
            Arrow(scalar_specification, equations[0].split()[i])
            for i in (2, 5)
        ])

        brace = Brace(VMobject(*equations[2].split()[2:]))
        brace_words = TextMobject("Linear combination")
        brace_words.next_to(brace, DOWN)

        equation = equations[0]
        for added_words in added_words1, added_words2:
            added_words.next_to(title, DOWN, buff = 3.5, aligned_edge = LEFT) 
        self.play(Write(equation))
        for i, new_eq in enumerate(equations):
            if i == 0:
                self.play(FadeIn(scalar_specification))
                self.wait(2)
                self.play(FadeOut(scalar_specification))
            elif i == 3:
                self.play(
                    GrowFromCenter(brace),
                    Write(brace_words)
                )
                self.wait(3)
                self.play(FadeOut(brace), FadeOut(brace_words))
            self.play(Transform(
                equation, new_eq, 
                path_arc = (np.pi/2 if i == 1 else 0)
            ))
            self.wait(3)
        self.play(Write(added_words1))
        self.wait(2)
        self.play(
            Transform(title1, title2),
            Write(subtitle),
            Transform(added_words1, added_words2),
        )
        self.wait(3)
        everything = VMobject(*self.get_mobjects())
        self.play(ApplyFunction(
            lambda m : m.scale(0.5).to_corner(UP+LEFT),
            everything
        ))
        self.wait()


    def get_equations(self):
        equation1 = TexMobject([
            "\\vec{\\textbf{u}}",
            "=",
            "a",
            "\\vec{\\textbf{v}}",
            "+",
            "b",
            "\\vec{\\textbf{w}}",
        ])
        u = equation1.split()[0]
        equation1.submobjects = list(it.chain(
            [VectorizedPoint(u.get_center())],
            equation1.submobjects[1:],
            [VectorizedPoint(u.get_left())],
            [u]
        ))
        equation2 = TexMobject([
            "\\left[\\begin{array}{c} 0 \\\\ 0 \\\\ 0 \\end{array} \\right]",
            "=",
            "a",
            "\\vec{\\textbf{v}}",
            "+",
            "b",
            "\\vec{\\textbf{w}}",
            "-",
            "\\vec{\\textbf{u}}",
        ])
        equation3 = TexMobject([
            "\\vec{\\textbf{0}}",
            "=",
            "a",
            "\\vec{\\textbf{v}}",
            "+",
            "b",
            "\\vec{\\textbf{w}}",
            "-",
            "\\vec{\\textbf{u}}",
        ])
        equation4 = TexMobject([
            "\\vec{\\textbf{0}}",
            "=",
            "0",
            "\\vec{\\textbf{v}}",
            "+",
            "0",
            "\\vec{\\textbf{w}}",
            "+0",
            "\\vec{\\textbf{u}}",
        ])
        equation5 = TexMobject([
            "\\vec{\\textbf{0}}",
            "=",
            "a",
            "\\vec{\\textbf{v}}",
            "+",
            "b",
            "\\vec{\\textbf{w}}",
            "+(-1)",
            "\\vec{\\textbf{u}}",
        ])
        equation5.split()[-2].set_color(YELLOW)
        equation6 = TexMobject([
            "\\vec{\\textbf{0}}",
            "=",
            "a",
            "\\vec{\\textbf{v}}",
            "+",
            "b",
            "\\vec{\\textbf{w}}",
            "+c",
            "\\vec{\\textbf{u}}",
        ])
        result = [equation1, equation2, equation3, equation4, equation5, equation6]
        for eq in result:
            eq.split()[3].set_color(MAROON_C)
            eq.split()[6].set_color(BLUE)
            eq.split()[-1].set_color(RED_C)
            eq.scale(1.5)
            eq.shift(UP)
        return result



class MathematiciansLikeToConfuse(TeacherStudentsScene):
    def construct(self):
        self.setup()
        self.teacher_says("""
            We wouldn't want things to \\\\ 
            be \\emph{understandable} would we?
        """)
        modes = "pondering", "sassy", "confused"
        self.play(*[
            ApplyMethod(student.change_mode, mode)
            for student, mode in zip(self.get_students(), modes)
        ])
        self.wait(2)

class CheckYourUnderstanding(TeacherStudentsScene):
    def construct(self):
        self.setup()
        self.teacher_says("Quiz time!")
        self.random_blink()
        self.wait()
        self.random_blink()


class TechnicalDefinitionOfBasis(Scene):
    def construct(self):
        title  = TextMobject("Technical definition of basis:")
        title.to_edge(UP)
        definition = TextMobject([
            "The",
            "basis",
            "of a vector space is a set of",
            "linearly independent",
            "vectors that",
            "span",
            "the full space",
        ])
        t, b, oavsiaso, li, vt, s, tfs = definition.split()
        b.set_color(BLUE)
        li.set_color(GREEN)
        s.set_color(YELLOW)
        definition.set_width(FRAME_WIDTH-1)

        self.add(title)
        self.play(Write(definition))
        self.wait()

class NextVideo(Scene):
    def construct(self):
        title = TextMobject("Next video: Matrices as linear transformations")
        title.to_edge(UP)
        rect = Rectangle(width = 16, height = 9, color = BLUE)
        rect.set_height(6)
        rect.next_to(title, DOWN)

        self.add(title)
        self.play(ShowCreation(rect))
        self.wait() 















