Beispiel #1
0
    def construct(self):
        t1 = TextMobject(
            "Let's look at our \\texttt{power(x,n)} function from Part 1 again"
        )
        t1.to_edge(UP)
        power1_code = CodeBlock(
            'Java', r"""
            public static int power(int x, int n) {
                if (n == 0) {
                    return 1;
                }
                int t = power(x, n - 1);
                return x * t;
            }
            """)
        power1_code.next_to(t1, DOWN, buff=MED_LARGE_BUFF)
        self.play(
            FadeIn(t1),
            FadeIn(power1_code),
        )
        self.wait()

        t2 = TextMobject("How many times does it call itself?")
        t2.next_to(power1_code, DOWN, buff=MED_LARGE_BUFF)
        self.play(Write(t2))
        self.wait(duration=2)

        t3 = TextMobject(
            "It call's itself $n$ times. Recall our original equation:")
        t3.move_to(t2)
        f1 = TexMobject('x^n=', 'x \\times x \\times ... \\times x',
                        '= x \\times x^{n-1}')
        f1.next_to(t3, DOWN)
        b1 = BraceLabel(f1[1],
                        '$n$ times',
                        brace_direction=DOWN,
                        label_constructor=TextMobject)
        f1 = VGroup(f1, b1)
        hr = SurroundingRectangle(power1_code.get_code().get_lines(5)[-5:-2])
        self.play(ReplacementTransform(t2, t3), FadeInFromDown(f1), FadeIn(hr))
        self.wait(duration=4)

        t4 = TextMobject('Can we do better?')
        self.play(*[FadeOut(o) for o in self.mobjects], FadeIn(t4))
        self.wait()

        self.play(FadeOut(t4))
Beispiel #2
0
 def get_fib_code(self, scale):
     return CodeBlock(
         'Java',
         r"""
         public static int fib(int n) {
             if (n == 0) {
                 return 0;
             }
             if (n == 1) {
                 return 1;
             }
             int fn1 = fib(n - 1);
             int fn2 = fib(n - 2);
             return fn1 + fn2;
         }
         """,
         code_scale=scale,
     )
Beispiel #3
0
    def construct(self):
        # Look at power1() and highlight parts of it. Anatomy of a recursive function.
        # Always has two parts: base case and recursive step.
        code_scale = 0.75
        power_code = CodeBlock(
            'Java',
            r"""
            public static int power(int x, int n) {
                if (n == 0) {
                    return 1;
                }
                int t = power(x, n - 1);
                return x * t;
            }
            """,
            code_scale=code_scale,
        )
        power_code.to_edge(UP)
        self.add(power_code)
        self.wait()

        title = TextMobject('Anatomy of a Recursive Function').to_edge(UP)
        power_code.generate_target()
        power_code.target.next_to(title, DOWN, buff=MED_LARGE_BUFF)
        self.play(
            FadeInFrom(title, UP),
            MoveToTarget(power_code),
        )
        self.wait()

        pc_groups = [
            VGroup(power_code.get_code().get_lines(p))
            for p in [(1, 2), (2, 5), (5, 7), (7, 8)]
        ]
        pc_t = [g.generate_target() for g in pc_groups]

        # Base Case
        #   - All of these have a base case that ends the recursion and returns.
        #   - Computed directly from the inputs.
        #   - Base cases are often some variant of emptiness. Power1() is a good example, with n == 0.
        #     What if you returned x for n == 1?
        base_shifts = [UP * 6, DOWN, DOWN * 6, DOWN * 6]
        power_code.save_state()
        for t, s in zip(pc_t, base_shifts):
            t.shift(s)

        t1 = TextMobject('Base Case').next_to(title, DOWN, buff=0.75)
        self.play(
            *[MoveToTarget(o) for o in pc_groups],
            FadeIn(t1),
        )
        self.wait()

        points_dwell_time = 2.5
        t2 = TextMobject(
            "- stops the recursion and returns\\\\",
            "- value is computed directly from the inputs, or constant\\\\",
            "- often it's some form of ``emptiness'' or ``singleness''\\\\",
            alignment="").next_to(title, DOWN, buff=LARGE_BUFF * 3.5)
        base_highlights = [
            SurroundingRectangle(pc_groups[1][0][1][0:6]),
            SurroundingRectangle(pc_groups[1][0][1][6:7]),
            SurroundingRectangle(pc_groups[1][0][0][3:7]),
        ]

        for t, h, ph in zip(t2, base_highlights, [None] + base_highlights):
            opt_anim = [FadeOut(ph)] if ph else []
            self.play(
                *opt_anim,
                FadeInFromDown(t),
                FadeIn(h),
            )
            self.wait(duration=points_dwell_time)
        self.play(FadeOut(base_highlights[-1]))
        self.wait()

        self.play(
            FadeOut(t1),
            FadeOut(t2),
            power_code.restore,
        )
        self.wait()

        # Recursive Step
        # The recursive step is some variant of decompose/reduce, call, recombine/compute.
        #   - Recursive case: compute the result with the help of one or more recursive calls to the same function.
        #     In this case, the data is reduced in some way in either size, complexity, or both.
        #   - In this case, reducing n by 1 each time.
        pc_t = [g.generate_target() for g in pc_groups]
        recurse_shifts = [UP * 6, UP * 6, UP * 0.45, DOWN * 6]
        power_code.save_state()
        for t, s in zip(pc_t, recurse_shifts):
            if s is not None:
                t.shift(s)

        t1 = TextMobject('Recursive Step').next_to(title, DOWN, buff=0.75)
        self.play(
            *[MoveToTarget(o) for o in pc_groups],
            FadeIn(t1),
        )
        self.wait()

        t2 = TextMobject(
            "- reduce: make the problem smaller, or break it into parts\\\\",
            "- call: one or more recursive calls\\\\",
            "- recombine: compute the result from the pieces\\\\",
            alignment="").next_to(title, DOWN, buff=LARGE_BUFF * 3.5)

        # 012345678901234567
        # intt=power(x,n-1);
        # returnx*t;
        recurse_highlights = [
            SurroundingRectangle(pc_groups[2][0][0][13:16]),
            SurroundingRectangle(pc_groups[2][0][0][5:17]),
            SurroundingRectangle(pc_groups[2][0][1][6:9]),
        ]

        for t, h, ph in zip(t2, recurse_highlights,
                            [None, *recurse_highlights]):
            opt_anim = [FadeOut(ph)] if ph else []
            self.play(
                *opt_anim,
                FadeInFromDown(t),
                FadeIn(h),
            )
            self.wait(duration=points_dwell_time)
        self.play(FadeOut(recurse_highlights[-1]))
        self.wait()

        self.play(
            FadeOut(t1),
            FadeOut(t2),
            power_code.restore,
        )
        self.wait(0.5)

        power_code.generate_target()
        power_code.target.to_edge(RIGHT)
        bcb = BraceLabel(power_code.target.get_code().get_lines((2, 5)),
                         '\\textit{Base Case}',
                         brace_direction=LEFT,
                         label_constructor=TextMobject,
                         buff=LARGE_BUFF)
        rsb = BraceLabel(power_code.target.get_code().get_lines((5, 7)),
                         '\\textit{Recursive Step}',
                         brace_direction=LEFT,
                         label_constructor=TextMobject,
                         buff=LARGE_BUFF)
        self.play(MoveToTarget(power_code), )
        self.play(
            ShowCreation(bcb),
            ShowCreation(rsb),
        )
        self.wait(0.5)

        t1 = TextMobject('Base Case', ':', ' stops the recursion')
        t2 = TextMobject('Recursive Step', ':', ' reduce - call - recombine')
        t2.next_to(t1.get_part_by_tex(':'),
                   DOWN,
                   submobject_to_align=t2.get_part_by_tex(':'),
                   buff=MED_LARGE_BUFF)
        g = VGroup(t1, t2).shift(DOWN * 1.5)
        self.play(FadeIn(g))
        self.wait(duration=points_dwell_time)

        t3 = TextMobject(
            "In Part 2 we'll look at making this function more efficient!"
        ).scale(0.75)
        t3.to_edge(DOWN).set_color(BLUE)
        self.play(Write(t3))

        # self.play(*[FadeOut(o) for o in self.mobjects])
        self.wait(duration=3)

        end_scale_group = VGroup(*self.mobjects)
        end_fade_group = VGroup(t3, title)
        self.animate_yt_end_screen(end_scale_group,
                                   end_fade_group,
                                   show_elements=False)
Beispiel #4
0
    def construct(self):
        code_scale = 0.75
        ss_code = CodeBlock(
            'Java',
            r"""
            public static double StrangeSqrt(double a) { 
                if (a < 0) { 
                    double b = StrangeSqrt(a * -1);
                    return b;
                } 
                return Math.sqrt(a);
            }
            """,
            code_scale=code_scale,
        )

        t1 = TextMobject(
            'Consider this somewhat strange square root function:')
        t1.next_to(ss_code, UP, buff=LARGE_BUFF)
        self.play(
            FadeIn(t1),
            ShowCreation(ss_code),
        )
        self.wait(duration=2.5)

        t2 = TextMobject(
            'It has a recursive call, meaning \\textit{it calls itself.}')
        t2.next_to(ss_code, DOWN, buff=LARGE_BUFF)
        # 012345 6 7 8901234567890 1 2345
        # double b = StrangeSqrt(a * -1);
        rl = ss_code.get_code().get_lines(3)
        rc_highlight = SurroundingRectangle(rl[8:-1])
        self.play(
            FadeIn(t2),
            FadeIn(rc_highlight),
        )
        self.wait(duration=3)
        self.play(
            FadeOut(t2),
            FadeOut(rc_highlight),
        )

        # Run with a positive input
        main_code = CodeBlock(
            'Java',
            r"""
            public static void main(String[] args) {
                double n = StrangeSqrt(4);
            }
            """,
            line_offset=7,
            code_scale=code_scale - 0.1,
        )
        frame_width = 3.5
        main_frame = StackFrame(main_code,
                                'main()',
                                9, ['n'],
                                width=frame_width)
        main_code.highlight_lines(9)
        VGroup(main_code, main_frame).arrange(RIGHT,
                                              buff=LARGE_BUFF).to_edge(DOWN)
        t3 = TextMobject("Let's see what it does with a positive input...")\
            .next_to(ss_code, DOWN, buff=LARGE_BUFF)
        self.play(
            FadeOut(t1),
            MaintainPositionRelativeTo(t1, ss_code),
            ss_code.to_edge,
            UP,
            FadeIn(t3),
            MaintainPositionRelativeTo(t3, ss_code),
        )
        self.wait()
        self.play(
            FadeInFromDown(main_frame),
            FadeInFromDown(main_code),
        )
        self.wait()

        ss_frame = StackFrame(ss_code,
                              'StrangeSqrt(4)',
                              1, ['a', 'b'],
                              width=frame_width)
        ss_frame.next_to(main_frame, UP, buff=SMALL_BUFF)

        xi = main_code.pre_call(ss_code, 1)
        self.play(
            *main_code.get_control_transfer_counterclockwise(xi),
            FadeInFrom(ss_frame, UP),
            FadeOut(t3),
        )
        ss_code.post_control_transfer(xi, self)
        self.wait()

        self.play(
            ss_code.highlight_lines,
            2,
            ss_frame.set_line,
            2,
            ss_frame.update_slot,
            'a',
            4,
        )
        self.wait()

        self.play(
            ss_code.highlight_lines,
            6,
            ss_frame.set_line,
            6,
        )
        self.wait()

        xi = ss_code.pre_return(main_code, 9)
        self.play(
            *ss_code.get_control_transfer_clockwise(xi),
            Uncreate(ss_frame),
        )
        main_code.post_control_transfer(xi, self)
        self.wait()

        t1 = TextMobject('That seems pretty normal...').next_to(
            ss_code, DOWN, buff=LARGE_BUFF)
        self.play(
            main_code.highlight_lines,
            10,
            main_frame.set_line,
            10,
            main_frame.update_slot,
            'n',
            2,
            FadeIn(t1),
        )
        self.wait(duration=2)

        t2 = TextMobject("Let's try it with a negative input...")\
            .next_to(ss_code, DOWN, buff=LARGE_BUFF)
        self.play(
            FadeOutAndShiftDown(main_code),
            FadeOutAndShiftDown(main_frame),
            FadeOut(t1),
            FadeIn(t2),
        )

        # Run with a negative input and see the recursion
        main_code = CodeBlock(
            'Java',
            r"""
            public static void main(String[] args) {
                double n = StrangeSqrt(-4);
            }
            """,
            line_offset=7,
            code_scale=code_scale - 0.1,
        )
        main_frame = StackFrame(main_code,
                                'main()',
                                9, ['n'],
                                width=frame_width)
        main_code.highlight_lines(9)
        VGroup(main_code, main_frame).arrange(RIGHT,
                                              buff=LARGE_BUFF).to_edge(DOWN)

        self.play(
            FadeInFromDown(main_frame),
            FadeInFromDown(main_code),
        )
        self.wait()

        ss_frame = StackFrame(ss_code,
                              'StrangeSqrt(-4)',
                              1, ['a', 'b'],
                              width=frame_width)
        ss_frame.next_to(main_frame, UP, buff=SMALL_BUFF)
        xi = main_code.pre_call(ss_code, 1)
        self.play(
            *main_code.get_control_transfer_counterclockwise(xi),
            FadeInFrom(ss_frame, UP),
            FadeOut(t2),
        )
        ss_code.post_control_transfer(xi, self)
        self.wait()

        self.play(
            ss_code.highlight_lines,
            2,
            ss_frame.set_line,
            2,
            ss_frame.update_slot,
            'a',
            -4,
        )
        self.wait()

        self.play(
            ss_code.highlight_lines,
            3,
            ss_frame.set_line,
            3,
        )
        self.wait()

        self.play(Indicate(rl[8:-1]), )

        ss2_frame = StackFrame(ss_code,
                               'StrangeSqrt(4)',
                               1, ['a', 'b'],
                               width=frame_width)
        ss2_frame.next_to(ss_frame, UP, buff=SMALL_BUFF)

        r1_call_site_rect = ss_code.get_current_highlight().copy().set_color(
            WHITE)
        self.play(
            FadeInFrom(ss2_frame, UP),
            ss_code.highlight_lines,
            1,
            FadeIn(r1_call_site_rect),
        )
        self.wait()

        stack = VGroup(main_frame, ss_frame, ss2_frame)
        t1 = TextMobject('Same function, but with\\\\a new frame on the stack')\
            .next_to(stack, LEFT, buff=LARGE_BUFF).shift(UP * 0.5)
        a = Arrow(t1.get_right(), ss2_frame.get_left(), stroke_width=3)
        self.play(
            FadeIn(t1),
            GrowArrow(a),
        )
        self.wait(duration=2)

        # Now we have a positive input again
        self.play(
            ss_code.highlight_lines,
            2,
            ss2_frame.set_line,
            2,
            ss2_frame.update_slot,
            'a',
            4,
        )
        self.wait()

        self.play(
            ss_code.highlight_lines,
            6,
            ss2_frame.set_line,
            6,
            FadeOut(t1),
            FadeOut(a),
        )
        self.wait()

        t1 = TextMobject('The stack tells us where to return,\\\\and with which values')\
            .next_to(stack, LEFT, buff=LARGE_BUFF).shift(UP * 0.5)
        a = Arrow(t1.get_right(), ss_frame.get_left(), stroke_width=3)
        self.play(
            FadeIn(t1),
            GrowArrow(a),
        )
        self.wait(duration=2)

        # Return back to line 3...
        self.play(
            FadeOut(r1_call_site_rect),
            Uncreate(ss2_frame),
            ss_code.highlight_lines,
            3,
            ss_frame.set_line,
            3,
        )
        self.wait()

        self.play(
            ss_code.highlight_lines,
            4,
            ss_frame.set_line,
            4,
            ss_frame.update_slot,
            'b',
            2,
            FadeOut(t1),
            FadeOut(a),
        )
        self.wait()

        xi = ss_code.pre_return(main_code, 9)
        self.play(
            *ss_code.get_control_transfer_clockwise(xi),
            Uncreate(ss_frame),
        )
        main_code.post_control_transfer(xi, self)
        self.wait()
        self.play(
            main_code.highlight_lines,
            10,
            main_frame.set_line,
            10,
            main_frame.update_slot,
            'n',
            2,
        )
        self.wait()

        t1 = TextMobject(
            'Thinking about the call stack can help you keep track of\\\\'
            'what a recursive function is doing at any given moment.').next_to(
                ss_code, DOWN, buff=LARGE_BUFF)
        self.play(
            FadeOutAndShiftDown(main_code),
            FadeOutAndShiftDown(main_frame),
            FadeIn(t1),
        )
        self.wait(duration=3)
        t2 = TextMobject('Try writing down part of the stack as you go.')\
            .next_to(t2, DOWN, buff=LARGE_BUFF)
        self.play(FadeInFromDown(t2))
        self.wait(duration=2)

        self.play(
            FadeOut(t1),
            FadeOut(t2),
        )
        self.wait()
Beispiel #5
0
    def construct(self):
        t1 = TextMobject("Let's compute $x^n$").shift(UP)
        self.play(FadeIn(t1))
        self.wait()

        t2 = TextMobject("i.e., $2^4=16$, or $4^3=64$, etc.")
        t2.next_to(t1, DOWN, buff=LARGE_BUFF)
        self.play(FadeInFromDown(t2))
        self.wait(duration=2)

        t3 = TextMobject('Recall that')
        f1 = TexMobject('x^n=', 'x', '\\times', 'x \\times ... \\times x',
                        'x^{n-1}')
        f2 = TexMobject('x^n=', 'x', '\\times', 'x^{n-1}')
        f2.next_to(t3, RIGHT)
        dg = VGroup(t3, f2).center().shift(
            UP)  # Place t3 so the final result is centered
        f1.next_to(t3, RIGHT)
        b1 = BraceLabel(f1[1:-1],
                        '$n$ times',
                        brace_direction=UP,
                        label_constructor=TextMobject)
        g = VGroup(t3, f1[:-1], b1)  # For display
        self.play(
            FadeOut(t1),
            FadeOut(t2),
            FadeInFromDown(g),
        )
        self.wait(duration=3)

        b2 = BraceLabel(f1[3],
                        '$n-1$ times',
                        brace_direction=UP,
                        label_constructor=TextMobject)
        self.play(ReplacementTransform(b1, b2), )
        self.wait(duration=2)

        self.play(
            ReplacementTransform(f1[3], f2[3]),
            FadeOut(b2),
        )
        self.wait(duration=2)

        t4 = TextMobject("That's starting to feel a bit recursive!")
        t4.next_to(dg, DOWN, buff=MED_LARGE_BUFF)
        self.play(FadeIn(t4))
        self.wait()

        code_scale = 0.75
        power_code = CodeBlock(
            'Java',
            r"""
            public static int power(int x, int n) {
                if (n == 0) {
                    return 1;
                }
                int t = power(x, n - 1);
                return x * t;
            }
            """,
            code_scale=code_scale,
        )
        self.play(
            *[FadeOut(o) for o in [t3, t4, f1[:-1], f2[3]]],
            FadeInFromDown(power_code),
        )
        self.wait()

        b1 = BraceLabel(
            power_code.get_code().get_lines((2, 4)),
            'Remember $x^0=1$, because math!',
            brace_direction=RIGHT,
            label_constructor=TextMobject,
            label_scale=0.75,
        )
        self.play(ShowCreation(b1))
        self.wait(duration=2)

        t1 = TextMobject("Let's step through it to see how it goes...")
        t1.next_to(power_code, DOWN)
        self.play(FadeIn(t1), )
        self.wait()

        # Start stepping through this and see it go.
        main_code = CodeBlock(
            'Java',
            r"""
            public static void main(String[] args) {
                int y = power(4, 3);
            }
            """,
            line_offset=7,
            code_scale=code_scale - 0.1,
        )
        frame_width = 3.5
        main_frame = StackFrame(main_code,
                                'main()',
                                9, ['y'],
                                width=frame_width)
        main_code.highlight_lines(9)
        VGroup(main_code, main_frame).arrange(RIGHT,
                                              buff=LARGE_BUFF).to_edge(DOWN)
        self.play(
            FadeOut(t1),
            FadeOut(b1),
            power_code.to_edge,
            UP,
            FadeInFromDown(main_frame),
            FadeInFromDown(main_code),
        )
        self.wait()

        def call_power(x, n, call_stack):
            stack_frame = StackFrame(power_code,
                                     'power(%d, %d)' % (x, n),
                                     1, ['x', 'n', 't'],
                                     width=frame_width)
            call_stack.animate_call(stack_frame, self)

            self.play(*stack_frame.get_update_line_anims(2),
                      stack_frame.update_slot, 'x', x, stack_frame.update_slot,
                      'n', n)

            if n == 0:
                self.play(*stack_frame.get_update_line_anims(3))
                self.wait()
                call_stack.animate_return(self)
                return 1
            else:
                self.play(*stack_frame.get_update_line_anims(5))
                self.wait()
                t = call_power(x, n - 1, call_stack)

                self.play(
                    *stack_frame.get_update_line_anims(6),
                    stack_frame.update_slot,
                    't',
                    t,
                )
                self.wait()
                call_stack[-1].code = power_code
                call_stack.animate_return(self)
                return x * t

        result = call_power(4, 3, CallStack(main_frame))
        self.play(*main_frame.get_update_line_anims(10),
                  main_frame.update_slot, 'y', result)
        self.wait()

        final_eq = TexMobject('4^3=', '64')
        final_eq.next_to(power_code, DOWN, buff=LARGE_BUFF)
        lhs = final_eq[0].copy().move_to(
            main_code.get_code().get_lines(2)[12:15])
        rhs = final_eq[1].copy().move_to(main_frame.slots()[0])
        self.play(
            ReplacementTransform(lhs, final_eq[0]),
            ReplacementTransform(rhs, final_eq[1]),
        )
        self.wait(duration=2)

        self.play(*[FadeOut(o) for o in [final_eq, main_frame, main_code]])
        self.wait()
Beispiel #6
0
    def construct(self):
        # - Now, let's change this a bit and break it!
        # - Now modify it (a <= 0) and call it with foo(0).
        # - switch it out for the broken one, and highlight and note the change.
        code_scale = 0.75
        ss_good_code = CodeBlock(
            'Java',
            r"""
            public static double StrangeSqrt(double a) { 
                if (a < 0) { 
                    double b = StrangeSqrt(a * -1);
                    return b;
                } 
                return Math.sqrt(a);
            }
            """,
            code_scale=code_scale,
        )
        ss_good_code.to_edge(UP)
        self.add(ss_good_code)

        t1 = TextMobject(
            "Let's break this code in a very small way and\\\\see what happens..."
        )
        t1.next_to(ss_good_code, DOWN, buff=LARGE_BUFF)
        self.play(FadeIn(t1))
        self.wait()

        gl2 = ss_good_code.get_code().get_lines(2)
        ghr = SurroundingRectangle(gl2[3:6])
        self.play(ShowCreation(ghr))
        self.wait()

        ss_bad_code = CodeBlock(
            'Java',
            r"""
            public static double StrangeSqrt(double a) { 
                if (a <= 0) { 
                    double b = StrangeSqrt(a * -1);
                    return b;
                } 
                return Math.sqrt(a);
            }
            """,
            code_scale=code_scale,
        )
        ss_bad_code.to_edge(UP)
        bl2 = ss_bad_code.get_code().get_lines(2)
        bhr = SurroundingRectangle(bl2[3:7])
        self.play(
            ReplacementTransform(ss_good_code, ss_bad_code),
            ReplacementTransform(ghr, bhr),
        )
        self.wait()

        t2 = TextMobject(
            "This small change will have a big effect on one case: 0\\\\"
            "Let's run it and see.").next_to(ss_bad_code,
                                             DOWN,
                                             buff=LARGE_BUFF)
        self.play(
            FadeOut(t1),
            FadeIn(t2),
        )
        self.wait(duration=3)

        self.play(
            FadeOut(t2),
            FadeOut(bhr),
        )
        self.wait()

        # - Start stepping through this and see the stack start to grow forever.
        main_code = CodeBlock(
            'Java',
            r"""
            public static void main(String[] args) {
                double n = StrangeSqrt(0);
            }
            """,
            line_offset=7,
            code_scale=code_scale - 0.1,
        )
        frame_width = 3.5
        main_frame = StackFrame(main_code,
                                'main()',
                                9, ['n'],
                                width=frame_width)
        main_code.highlight_lines(9)
        VGroup(main_code, main_frame).arrange(RIGHT,
                                              buff=LARGE_BUFF).to_edge(DOWN)
        self.play(
            FadeInFromDown(main_frame),
            FadeInFromDown(main_code),
        )
        self.wait()

        ss_frame = StackFrame(ss_bad_code,
                              'StrangeSqrt(0)',
                              1, ['a', 'b'],
                              width=frame_width)
        ss_frame.next_to(main_frame, UP, buff=SMALL_BUFF)

        xi = main_code.pre_call(ss_bad_code, 1)
        self.play(
            *main_code.get_control_transfer_counterclockwise(xi),
            FadeInFrom(ss_frame, UP),
        )
        ss_bad_code.post_control_transfer(xi, self)
        self.wait()

        self.play(ss_bad_code.highlight_lines, 2, ss_frame.set_line, 2,
                  ss_frame.update_slot, 'a', 0)
        self.wait()

        self.play(ss_bad_code.highlight_lines, 3, ss_frame.set_line, 3)
        self.wait()

        def call_ss_again(stack_group, previous_frame, extras=None):
            wait_each_step = len(stack_group) < 3
            runtime = 1.0 if len(stack_group) < 2 else 0.5 if len(
                stack_group) < 6 else 0.20
            extra_anims = []
            if len(stack_group) > 2:
                extra_anims.append(
                    ApplyMethod(
                        stack_group.shift,
                        DOWN * previous_frame.get_height() +
                        DOWN * SMALL_BUFF))
            if extras:
                extra_anims.extend(extras)

            f = StackFrame(ss_bad_code,
                           'StrangeSqrt(0)',
                           1, ['a', 'b'],
                           width=frame_width)
            f.next_to(previous_frame, UP, buff=SMALL_BUFF)
            self.play(*extra_anims,
                      ss_bad_code.highlight_lines,
                      1,
                      f.set_line,
                      1,
                      FadeIn(f),
                      MaintainPositionRelativeTo(f, previous_frame),
                      run_time=runtime)
            if wait_each_step:
                self.wait()

            self.play(ss_bad_code.highlight_lines,
                      2,
                      f.set_line,
                      2,
                      f.update_slot,
                      'a',
                      0,
                      run_time=runtime)
            if wait_each_step:
                self.wait()

            self.play(ss_bad_code.highlight_lines,
                      3,
                      f.set_line,
                      3,
                      run_time=runtime)
            if wait_each_step:
                self.wait()
            stack_group.add(f)
            return f

        sg = VGroup(main_frame, ss_frame)
        curr_ss_frame = ss_frame
        for i in range(3):
            curr_ss_frame = call_ss_again(sg, curr_ss_frame)

        # - When will this program end? Never?
        t1 = TextMobject("Hrm... when is this going to end??").next_to(
            main_code, UP, buff=LARGE_BUFF)
        extra_text = [FadeIn(t1)]
        for i in range(4):
            curr_ss_frame = call_ss_again(sg, curr_ss_frame, extra_text)
            extra_text = None

        t2 = TextMobject("Never?").next_to(main_code, UP, buff=LARGE_BUFF)
        extra_text = [FadeOut(t1), FadeIn(t2)]
        for i in range(4):
            curr_ss_frame = call_ss_again(sg, curr_ss_frame, extra_text)
            extra_text = None

        sg.save_state()
        t3 = TextMobject("The stack is getting quite deep!").next_to(
            main_code, UP, buff=LARGE_BUFF)
        self.play(sg.scale, 0.20, {'about_edge': TOP}, FadeOut(t2), FadeIn(t3))
        self.wait(duration=2)
        self.play(sg.restore)

        extra_text = [FadeOut(t3)]
        for i in range(4):
            curr_ss_frame = call_ss_again(sg, curr_ss_frame, extra_text)
            extra_text = None

        # - Well, the stack is finite, so eventually you get a SO.
        #   Use the Java SO exception and show it hit and stop.
        so_frame = StackFrame(ss_bad_code,
                              'StrangeSqrt(0)',
                              1, ['a', 'b'],
                              width=frame_width)
        so_frame.slots().set_color(ORANGE)
        so_frame.header_line().set_color(ORANGE)
        so_frame.background_rect().set_fill(color=[BLACK, RED])
        so_frame.next_to(curr_ss_frame, UP, buff=SMALL_BUFF)
        so_exception = TextMobject(
            '\\texttt{Exception in thread "main"\\\\java.lang.StackOverflowError}',
        ).set_color(YELLOW).scale(0.75).next_to(main_code, UP, buff=LARGE_BUFF)
        self.play(sg.shift,
                  DOWN * so_frame.get_height() + DOWN * SMALL_BUFF,
                  ss_bad_code.get_current_highlight().set_color, ORANGE,
                  FadeIn(so_frame),
                  MaintainPositionRelativeTo(so_frame, curr_ss_frame),
                  Write(so_exception))
        sg.add(so_frame)
        self.wait(duration=2)

        ss_bad_code.generate_target()
        so_exception.generate_target()
        ss_bad_code.target.scale(0.75)
        g = VGroup(so_exception.target,
                   ss_bad_code.target).arrange(RIGHT, buff=LARGE_BUFF)
        g.to_edge(UP)
        sg.generate_target()
        sg.target.scale(0.15).next_to(g, DOWN).to_edge(RIGHT)
        t1 = TextMobject(
            "Space for the call stack is limited,\\\\so we can't make recursive calls forever!"
        )
        self.play(
            *[MoveToTarget(t) for t in [so_exception, ss_bad_code, sg]],
            FadeOutAndShiftDown(main_code),
            FadeInFromDown(t1),
        )
        self.wait(duration=2)

        t2 = TextMobject(
            "This program made {\\raise.17ex\\hbox{$\\scriptstyle\\sim$}}15,000 calls before crashing."
        )
        t4 = TextMobject(
            "\\textit{Java SE 13.0.1 on macOS Catalina 10.15.4}").scale(0.5)
        t2.next_to(t1, DOWN, buff=MED_LARGE_BUFF)
        t4.to_edge(DL)
        self.play(
            FadeIn(t2),
            FadeInFromDown(t4),
        )
        self.wait(duration=4)

        t5 = TextMobject('\\textit{Most common mistake with recursion.}')
        t6 = TextMobject("You'll see this a lot. Everyone does!")
        t6.next_to(t5, DOWN, buff=LARGE_BUFF)
        self.play(
            *[FadeOut(t) for t in [t1, t2, t4]],
            *[FadeIn(t) for t in [t5, t6]],
        )
        self.wait(duration=3)

        # Transition
        t1 = TextMobject(
            "Alright, let's look at a more interesting function...")
        self.play(
            *[FadeOut(o) for o in self.mobjects],
            FadeIn(t1),
        )
        self.wait()

        self.play(FadeOut(t1))
        self.wait()
Beispiel #7
0
    def construct(self):
        code_scale = 0.75
        foo_code = CodeBlock(
            'Java',
            r"""
            int foo() {
                int n = bar(2, 1);
                n += bar(2, 2);
                return n;
            }
            """,
            code_scale=code_scale,
        )
        bar_code = CodeBlock(
            'Java',
            r"""
            int bar(int x, int y) {
                if (x == 1) {
                    return y;
                }
                int b = bar(x - 1, y + 1);
                return b;
            }
            """,
            line_offset=5,
            code_scale=code_scale,
        )
        main_code = CodeBlock('Java', 'off_screen')
        main_code.shift(UP * 8)  # Offscreen up
        self.add(main_code)

        title = TextMobject('CodeBlock Stepping Demo')
        title.to_edge(UP)
        foo_code.next_to(title, DOWN)
        bar_code.next_to(foo_code.get_code(),
                         DOWN,
                         buff=MED_LARGE_BUFF,
                         aligned_edge=LEFT,
                         submobject_to_align=bar_code.get_code())
        self.play(*[FadeIn(o) for o in [title, foo_code, bar_code]])
        self.wait()

        # Step through both code blocks until done.
        xi = main_code.pre_call(foo_code, 1)
        self.play(*main_code.get_control_transfer_counterclockwise(xi))
        foo_code.post_control_transfer(xi, self)

        foo_code.prep_annotations(2, 3)
        foo_code.generate_target().highlight_lines(2).set_annotation(2, 'n: ?')
        self.play(MoveToTarget(foo_code))
        n = self.run_bar(bar_code, foo_code, 2, 2, 1)
        self.play(foo_code.highlight_lines, 3, foo_code.set_annotation, 2,
                  None, foo_code.set_annotation, 3, 'n: %d' % n,
                  bar_code.fade_labels)
        n += self.run_bar(bar_code, foo_code, 3, 2, 2)
        self.play(foo_code.highlight_lines, 4, foo_code.set_annotation, 3,
                  'n: %d' % n)

        xi = foo_code.pre_return(main_code, 1)
        self.play(*foo_code.get_control_transfer_clockwise(xi))
        main_code.post_control_transfer(xi, self)

        self.play(FadeOut(title), FadeOut(foo_code), FadeOut(bar_code))
        self.wait()
Beispiel #8
0
    def construct(self):
        title = TextMobject('The Call Stack').to_edge(UP)
        t1 = TextMobject("Alright, let's do a more complicated one!")
        self.add(title, t1)
        self.wait()

        code_scale = 0.75
        main_code = CodeBlock(
            'Java',
            r"""
            public static
            void main(String[] args) {
                System.out.println(foo(1));
            }
            """,
            code_scale=code_scale,
        )
        # Start line: 5
        foo_code = CodeBlock(
            'Java',
            r"""
            foo(int a) {
                int b = bar(a, 2);
                int c = bar(a, b);
                return c;
            }
            """,
            code_scale=code_scale,
        )
        # Start line: 10
        bar_code = CodeBlock(
            'Java',
            r"""
            int bar(int x, int y) {
                int a = x + y;
                int b = a * 2;
                return b;
            }
            """,
            code_scale=code_scale,
        )

        cg = VGroup(main_code, foo_code, bar_code)
        cg.arrange(DOWN, aligned_edge=LEFT, buff=MED_SMALL_BUFF)
        cg.next_to(title, DOWN, buff=MED_SMALL_BUFF).to_edge(RIGHT)
        self.play(
            FadeOut(t1),
            FadeInFrom(cg, RIGHT),
        )
        self.wait()

        frame_width = 3.0
        args_ref = TextMobject('[ ]').scale(0.5)
        main_frame = StackFrame(main_code,
                                'main()',
                                3, [('args', args_ref)],
                                width=frame_width)
        main_frame.next_to(cg, LEFT, buff=LARGE_BUFF * 2).to_edge(DOWN)
        main_code.move_hidden_highlight(3)
        self.play(
            FadeInFromDown(main_frame),
            main_code.highlight_lines,
            3,
        )
        self.wait()

        foo_frame = StackFrame(foo_code,
                               'foo(1)',
                               5, ['a', 'b', 'c'],
                               width=frame_width)
        foo_frame.next_to(main_frame, UP, buff=SMALL_BUFF)

        xi = main_code.pre_call(foo_code, 1)
        self.play(
            *main_code.get_control_transfer_counterclockwise(xi),
            FadeInFrom(foo_frame, UP),
        )
        foo_code.post_control_transfer(xi, self)
        self.wait()

        self.play(foo_code.highlight_lines, 2, foo_frame.set_line, 6,
                  foo_frame.update_slot, 'a', 1)
        self.wait()

        bar_frame = StackFrame(bar_code,
                               'bar(1, 2)',
                               10, ['x', 'y', 'a', 'b'],
                               width=frame_width)
        bar_frame.next_to(foo_frame, UP, buff=SMALL_BUFF)

        xi = foo_code.pre_call(bar_code, 1)
        self.play(
            *foo_code.get_control_transfer_counterclockwise(xi),
            FadeInFrom(bar_frame, UP),
        )
        bar_code.post_control_transfer(xi, self)
        self.wait()

        self.play(bar_code.highlight_lines, 2, bar_frame.set_line, 11,
                  bar_frame.update_slot, 'x', 1, bar_frame.update_slot, 'y', 2)
        self.wait()

        self.play(bar_code.highlight_lines, 3, bar_frame.set_line, 12,
                  bar_frame.update_slot, 'a', 3)
        self.wait()

        self.play(bar_code.highlight_lines, 4, bar_frame.set_line, 13,
                  bar_frame.update_slot, 'b', 6)
        self.wait()

        xi = bar_code.pre_return(foo_code, 2)
        self.play(
            *bar_code.get_control_transfer_clockwise(xi),
            Uncreate(bar_frame),
        )
        foo_code.post_control_transfer(xi, self)
        self.wait()

        self.play(foo_code.highlight_lines, 3, foo_frame.set_line, 7,
                  foo_frame.update_slot, 'b', 6)
        self.wait()

        bar_frame = StackFrame(bar_code,
                               'bar(1, 6)',
                               10, ['x', 'y', 'a', 'b'],
                               width=frame_width)
        bar_frame.next_to(foo_frame, UP, buff=SMALL_BUFF)
        xi = foo_code.pre_call(bar_code, 1)
        self.play(
            *foo_code.get_control_transfer_counterclockwise(xi),
            FadeInFrom(bar_frame, UP),
        )
        bar_code.post_control_transfer(xi, self)
        self.wait()

        self.play(bar_code.highlight_lines, 2, bar_frame.set_line, 11,
                  bar_frame.update_slot, 'x', 1, bar_frame.update_slot, 'y', 6)
        self.wait()

        self.play(bar_code.highlight_lines, 3, bar_frame.set_line, 12,
                  bar_frame.update_slot, 'a', 7)
        self.wait()

        self.play(bar_code.highlight_lines, 4, bar_frame.set_line, 13,
                  bar_frame.update_slot, 'b', 14)
        self.wait()

        xi = bar_code.pre_return(foo_code, 3)
        self.play(
            *bar_code.get_control_transfer_clockwise(xi),
            Uncreate(bar_frame),
        )
        foo_code.post_control_transfer(xi, self)
        self.wait()

        self.play(foo_code.highlight_lines, 4, foo_frame.set_line, 8,
                  foo_frame.update_slot, 'c', 14)
        self.wait()

        xi = foo_code.pre_return(main_code, 3)
        self.play(
            *foo_code.get_control_transfer_clockwise(xi),
            Uncreate(foo_frame),
        )
        main_code.post_control_transfer(xi, self)
        self.wait()

        def fake_frame(name):
            frame_name = TextMobject(name).scale(0.75)
            br = BackgroundRectangle(frame_name,
                                     buff=SMALL_BUFF,
                                     fill_opacity=0.15)
            br.set_fill(color=[ORANGE, BLUE])
            return VGroup(frame_name, br)

        println_funcs = [
            'BufferedWriter.write("14", 0, 2)',
            'BufferedWriter(Writer).write("14")',
            'PrintStream.writeln("14")',
            'PrintStream.println(14)',
        ]

        println_frames = [fake_frame(f) for f in reversed(println_funcs)]

        for n, p in zip(println_frames, [main_frame] + println_frames):
            n.next_to(p, UP, buff=SMALL_BUFF)
            self.play(FadeInFrom(n, UP))
        self.wait()

        for f in reversed(println_frames):
            self.play(Uncreate(f))
        # self.wait()

        self.play(main_code.highlight_lines, 4, main_frame.set_line, 4)

        off_screen_code = CodeBlock('Java', 'off_screen')
        off_screen_code.shift(UP * 8)  # Offscreen up
        self.add(off_screen_code)

        xi = main_code.pre_return(off_screen_code, 1)
        self.play(
            *main_code.get_control_transfer_clockwise(xi),
            Uncreate(main_frame),
        )
        off_screen_code.post_control_transfer(xi, self)
        self.wait()

        self.play(FadeOut(main_code), FadeOut(foo_code), FadeOut(bar_code))
        self.wait()
Beispiel #9
0
    def construct(self):
        # Even case
        ef0 = TexMobject('x^n=', 'x', '\\times', 'x \\times ... \\times x')
        eb0 = BraceLabel(ef0[1:],
                         '$n$ \\textit{times}',
                         brace_direction=UP,
                         label_constructor=TextMobject)
        ef0g = VGroup(ef0, eb0)

        ef1 = TexMobject('x^n=', 'x \\times ... \\times x', '\\times',
                         'x \\times ... \\times x')
        ef1.next_to(ef0, ORIGIN, index_of_submobject_to_align=0)
        eb1n = BraceLabel(ef1[1:],
                          '$n$ \\textit{times}',
                          brace_direction=UP,
                          label_constructor=TextMobject)
        eb1l = BraceLabel(ef1[1:2],
                          '$\\frac{n}{2}$ \\textit{times}',
                          brace_direction=UP,
                          label_constructor=TextMobject)
        eb1r = BraceLabel(ef1[3:4],
                          '$\\frac{n}{2}$ \\textit{times}',
                          brace_direction=UP,
                          label_constructor=TextMobject)

        ef2 = TexMobject('x^n=', 'x \\times ... \\times x', '\\times',
                         'x^{\\frac{n}{2}}')
        ef2.next_to(ef0, ORIGIN, index_of_submobject_to_align=0)
        eb2r = BraceLabel(ef2[3:4],
                          '$\\frac{n}{2}$ \\textit{times}',
                          brace_direction=UP,
                          label_constructor=TextMobject)

        ef3 = TexMobject('x^n=', 'x^{\\frac{n}{2}}', '\\times',
                         'x^{\\frac{n}{2}}')
        ef3.next_to(ef0, ORIGIN, index_of_submobject_to_align=0)
        eb3r = BraceLabel(ef3[3:4],
                          '$\\frac{n}{2}$ \\textit{times}',
                          brace_direction=UP,
                          label_constructor=TextMobject)

        if_even = TextMobject('\\textit{if $n$ is even:}').next_to(ef0, LEFT)
        self.play(ShowCreation(ef0g))
        self.wait()

        self.play(ReplacementTransform(ef0, ef1),
                  ReplacementTransform(eb0, eb1n))
        self.wait()

        self.play(ReplacementTransform(eb1n, eb1l), ShowCreation(eb1r),
                  FadeIn(if_even))
        self.wait(duration=2)

        self.play(ReplacementTransform(ef1, ef2),
                  ReplacementTransform(eb1r, eb2r))
        self.wait()

        self.play(ReplacementTransform(ef2, ef3), FadeOut(eb1l),
                  ReplacementTransform(eb2r, eb3r))
        self.wait()

        even_group = VGroup(if_even, ef3)
        self.play(FadeOutAndShift(eb3r, UL), even_group.to_edge, UL)
        self.wait()

        # Prime1 eq
        p1f = TexMobject('x^n=', 'x', '\\times', 'x^{n-1}')
        p1t = TextMobject('From our first version, we know')
        p1t.next_to(p1f, UP)
        self.play(FadeIn(p1t), ShowCreation(p1f))
        self.wait()

        # Odd case
        ot1 = TextMobject('So if $n$ is odd, then $n-1$ is even... hmm...')
        ot1.next_to(p1f, UP)
        self.play(ReplacementTransform(p1t, ot1))
        self.wait()

        of1 = TexMobject('x^n=', 'x', '\\times',
                         'x^{\\frac{n-1}{2}} \\times x^{\\frac{n-1}{2}}')
        of1.next_to(p1f, ORIGIN, index_of_submobject_to_align=0)

        self.play(ReplacementTransform(VGroup(ef3[1:]).copy(), of1[3]),
                  FadeOut(p1f[3]))
        self.remove(*p1f[:3])
        self.add(of1)
        self.wait()

        if_odd = TextMobject('\\textit{if $n$ is odd:}').next_to(of1[0], LEFT)
        self.play(FadeOut(ot1), FadeIn(if_odd))
        self.wait()

        # Both
        originals = [if_even, ef3, if_odd, of1]
        for o in originals:
            o.generate_target()
        ef3.target.next_to(of1, UP, aligned_edge=LEFT)
        if_even.target.next_to(ef3.target[0], LEFT)
        VGroup(*[o.target for o in originals]).center().to_edge(TOP)

        self.play(*[MoveToTarget(o) for o in originals])
        self.wait()
        eqg = VGroup(*originals)

        # Simplify with int division
        t1 = TextMobject(
            'Fun Fact: if $n$ is odd, and we use \\texttt{int} as the datatype, then'
        ).next_to(eqg, DOWN, buff=LARGE_BUFF)
        div_code = CodeTextString('Java',
                                  'n / 2 == (n - 1) / 2').next_to(t1, DOWN)

        self.play(FadeInFromDown(t1), FadeInFromDown(div_code))
        self.wait()

        of2 = TexMobject('x^n=', 'x', '\\times',
                         'x^{\\frac{n}{2}} \\times x^{\\frac{n}{2}}')
        of2.next_to(of1, ORIGIN, index_of_submobject_to_align=0)

        self.play(ReplacementTransform(div_code, of2[3]), FadeOut(of1[3]),
                  FadeOut(t1))
        self.remove(*of1[:3])
        self.add(of2)
        self.wait()

        originals = [if_even, ef3, if_odd, of2]
        for o in originals:
            o.generate_target()
        for p in [ef3.target[1:], of2.target[3]]:
            p.set_color(ORANGE)
        eqtg = VGroup(*[o.target for o in originals]).center().to_edge(TOP)

        t1 = TextMobject(
            'Using \\texttt{int} for $n$ makes the equations very similar,\\\\'
            'and the coding very simple!')
        t1.next_to(eqtg, DOWN, buff=MED_LARGE_BUFF)
        self.play(*[MoveToTarget(o) for o in originals], FadeIn(t1))
        self.wait()
        eqg = VGroup(*originals)

        # Transform to code
        code_scale = 0.7
        power2_code = CodeBlock(
            'Java',
            r"""
            public static int power2(int x, int n) {
                if (n == 0) {
                    return 1;
                }
                int t = power2(x, n / 2);
                if (n % 2 == 0) {
                    return t * t;
                }
                return t * t * x;
            }	
            """,
            code_scale=code_scale,
        )
        power2_code.to_edge(RIGHT)

        ef3.generate_target()
        of2.generate_target()
        ef3.target.next_to(power2_code.get_code().get_lines(7),
                           LEFT,
                           buff=LARGE_BUFF)
        of2.target.next_to(power2_code.get_code().get_lines(9),
                           LEFT,
                           buff=LARGE_BUFF)
        ef3.target.next_to(of2.target,
                           UP,
                           aligned_edge=RIGHT,
                           coor_mask=X_AXIS)

        et = TextMobject('\\textit{even:}')
        ot = TextMobject('\\textit{odd:}')
        ot.next_to(of2.target[0], LEFT)
        et.next_to(ef3.target[0], LEFT)

        self.play(FadeOut(t1))
        self.play(
            FadeOut(if_even),
            FadeOut(if_odd),
            FadeInFrom(power2_code, RIGHT),
            MoveToTarget(ef3),
            MoveToTarget(of2),
            ReplacementTransform(if_even, et),
            ReplacementTransform(if_odd, ot),
        )
        self.wait()

        recursive_call_hr = SurroundingRectangle(
            power2_code.get_code().get_lines(5)[5:-1])
        xn2_hr = SurroundingRectangle(ef3[-1])

        ec_hr = SurroundingRectangle(
            power2_code.get_code().get_lines(7)[-4:-1])
        ef_hr = SurroundingRectangle(ef3[1:])

        oc_hr = SurroundingRectangle(
            power2_code.get_code().get_lines(9)[-6:-1])
        of_hr = SurroundingRectangle(of2[1:])

        for f, c in [(xn2_hr, recursive_call_hr), (ef_hr, ec_hr),
                     (of_hr, oc_hr)]:
            self.play(ShowCreation(f), ShowCreation(c))
            self.wait()
            self.play(Uncreate(f), Uncreate(c))
        self.wait()
Beispiel #10
0
    def construct(self):
        t1 = TextMobject("Let's go back to our original equation").shift(UP)
        self.play(FadeInFromDown(t1))
        self.wait()

        # Even case
        ef1 = TexMobject('x^n=', 'x \\times ... \\times x', '\\times',
                         'x \\times ... \\times x')
        eb1n = BraceLabel(ef1[1:],
                          '$n$ \\textit{times}',
                          brace_direction=UP,
                          label_constructor=TextMobject)
        eb1l = BraceLabel(ef1[1:2],
                          '$\\frac{n}{2}$ \\textit{times}',
                          brace_direction=UP,
                          label_constructor=TextMobject)
        eb1r = BraceLabel(ef1[3:4],
                          '$\\frac{n}{2}$ \\textit{times}',
                          brace_direction=UP,
                          label_constructor=TextMobject)

        ef3 = TexMobject('x^n=', 'x^{\\frac{n}{2}}', '\\times', 'x',
                         '^{\\frac{n}{2}}')
        ef3.next_to(ef1, ORIGIN, index_of_submobject_to_align=0)
        eb3l = BraceLabel(ef3[1:2],
                          '$\\frac{n}{2}$ \\textit{times}',
                          brace_direction=UP,
                          label_constructor=TextMobject)

        if_even = TextMobject('\\textit{if $n$ is even:}').next_to(
            ef1, LEFT).set_color(YELLOW)
        self.play(ShowCreation(ef1), ShowCreation(eb1n), t1.shift, UP)
        self.wait(duration=2)

        self.play(ReplacementTransform(eb1n, eb1l), ShowCreation(eb1r),
                  FadeIn(if_even), FadeOut(t1))
        self.wait(duration=2)

        self.play(ReplacementTransform(ef1[1], ef3[1]),
                  ReplacementTransform(eb1l, eb3l))
        self.wait()

        tmp_ef3_right = VGroup(ef3[2].copy(), ef3[3].copy(), ef3[4].copy())
        tmp_ef3_right.next_to(ef1[2],
                              ORIGIN,
                              submobject_to_align=tmp_ef3_right[0])
        eb3r = BraceLabel(tmp_ef3_right[1:],
                          '$\\frac{n}{2}$ \\textit{times}',
                          brace_direction=UP,
                          label_constructor=TextMobject)

        self.play(ReplacementTransform(ef1[3:], tmp_ef3_right[1:]),
                  ReplacementTransform(eb1r, eb3r))
        self.wait()

        self.play(
            ReplacementTransform(ef1[2], ef3[2]),
            ReplacementTransform(tmp_ef3_right[1:], ef3[3:]),
            FadeOutAndShift(eb3r, LEFT),
            FadeOut(eb3l),
        )
        self.remove(ef1[0])
        self.add(ef3)
        self.wait()

        even_group = VGroup(if_even, ef3)
        self.play(even_group.to_edge, UL)
        self.wait()

        # Odd case
        ot1 = TextMobject(
            "Now, let's figure out what to do if $n$ is odd").shift(
                UP * 2).set_color(BLUE)
        self.play(FadeInFromDown(ot1))

        of1 = TexMobject('x^n=', 'x \\times ... \\times x', '\\times',
                         'x \\times ... \\times x', '\\times x')
        ob1n = BraceLabel(of1[1:],
                          '$n$ \\textit{times}',
                          brace_direction=UP,
                          label_constructor=TextMobject)
        ob1nm1 = BraceLabel(of1[1:-1],
                            '$n-1$ \\textit{times}',
                            brace_direction=UP,
                            label_constructor=TextMobject)
        ob1l = BraceLabel(of1[1:2],
                          '$\\frac{n-1}{2}$ \\textit{times}',
                          brace_direction=UP,
                          label_constructor=TextMobject)
        ob1r = BraceLabel(of1[3:4],
                          '$\\frac{n-1}{2}$ \\textit{times}',
                          brace_direction=UP,
                          label_constructor=TextMobject)

        of3 = TexMobject('x^n=', 'x', '^{\\frac{n-1}{2}}', '\\times', 'x',
                         '^{\\frac{n-1}{2}}', '\\times x')
        of3.next_to(of1, ORIGIN, index_of_submobject_to_align=0)
        ob3l = BraceLabel(of3[1:3],
                          '$\\frac{n-1}{2}$ \\textit{times}',
                          brace_direction=UP,
                          label_constructor=TextMobject)

        if_odd = TextMobject('\\textit{if $n$ is odd:}').next_to(
            of1, LEFT).set_color(YELLOW)
        self.play(ShowCreation(of1), ShowCreation(ob1n))
        self.wait(duration=2)

        self.play(ReplacementTransform(ob1n, ob1nm1), FadeIn(if_odd),
                  FadeOut(ot1))
        self.wait(duration=2)

        self.play(ReplacementTransform(ob1nm1, ob1l), ShowCreation(ob1r))
        self.wait(duration=2)

        self.play(ReplacementTransform(of1[1], of3[1:3]),
                  ReplacementTransform(ob1l, ob3l))
        self.wait()

        tmp_of3_right = VGroup(of3[3].copy(), of3[4].copy(), of3[5].copy())
        tmp_of3_right.next_to(of1[2],
                              ORIGIN,
                              submobject_to_align=tmp_of3_right[0])
        ob3r = BraceLabel(tmp_of3_right[1:],
                          '$\\frac{n-1}{2}$ \\textit{times}',
                          brace_direction=UP,
                          label_constructor=TextMobject)

        self.play(ReplacementTransform(of1[3], tmp_of3_right[1:]),
                  ReplacementTransform(ob1r, ob3r))
        self.wait()

        self.play(
            ReplacementTransform(of1[2], of3[3]),
            ReplacementTransform(tmp_of3_right[1:], of3[4:6]),
            ReplacementTransform(of1[4], of3[-1]),
            FadeOutAndShift(ob3r, LEFT),
            FadeOut(ob3l),
        )
        self.remove(of1[0])
        self.add(of3)
        self.wait()

        # Both
        originals = [if_even, ef3, if_odd, of3]
        for o in originals:
            o.generate_target()
        ef3.target.next_to(of3, UP, aligned_edge=LEFT)
        if_even.target.next_to(ef3.target[0], LEFT)
        VGroup(*[o.target for o in originals]).center().to_edge(TOP)

        self.play(*[MoveToTarget(o) for o in originals])
        self.wait()
        eqg = VGroup(*originals)

        # Simplify with int division
        t1 = TextMobject(
            'Fun Fact: if $n$ is odd, and we use \\texttt{int} as the datatype, then',
            tex_to_color_map={
                'int': RED
            }).next_to(eqg, DOWN, buff=LARGE_BUFF)
        div_code = CodeTextString('Java', '(n-1)/2 == n/2').next_to(t1, DOWN)

        self.play(FadeInFromDown(t1), FadeInFromDown(div_code))
        self.wait(duration=3)

        t2 = TextMobject('Integer division \\textit{truncates}: '
                         '\\texttt{5/2 == 2}'
                         ', \\textit{not} '
                         '\\texttt{2.5}')
        t2.next_to(div_code, DOWN, buff=LARGE_BUFF)
        self.play(FadeInFromDown(t2))
        self.wait(duration=3)

        # Indicate movement before each exp transformation from code
        of4 = TexMobject('x^n=', 'x', '^{\\frac{n}{2}}', '\\times', 'x',
                         '^{\\frac{n}{2}}', '\\times x')
        of4.next_to(of3, ORIGIN, index_of_submobject_to_align=0)

        def highlight_and_switch_exp(target_exp, old_exp):
            hrc = SurroundingRectangle(div_code[0][:7])
            hrf = SurroundingRectangle(old_exp)
            self.play(ShowCreation(hrc), ShowCreation(hrf))
            self.wait()
            self.play(Uncreate(hrc), Uncreate(hrf))
            self.play(Indicate(div_code[0][-3:]))
            self.play(
                ReplacementTransform(div_code[0][-3:].copy(), target_exp),
                FadeOut(old_exp))

        highlight_and_switch_exp(of4[2], of3[2])
        tmp_of4_right = VGroup(of4[4].copy(), of4[5].copy())
        tmp_of4_right.next_to(of3[4],
                              ORIGIN,
                              submobject_to_align=tmp_of4_right[0])
        highlight_and_switch_exp(tmp_of4_right[1], of3[5])
        self.wait()

        self.play(
            ReplacementTransform(of3[3:5], of4[3:5]),
            ReplacementTransform(tmp_of4_right[1], of4[5]),
            ReplacementTransform(of3[-1], of4[-1]),
        )
        self.remove(*of3)
        self.add(of4)
        self.wait()

        self.play(*[FadeOut(o) for o in [t1, t2, div_code]])

        originals = [if_even, ef3, if_odd, of4]
        for o in originals:
            o.generate_target()
        for p in [ef3.target[1:], of4.target[1:6]]:
            p.set_color(ORANGE)
        eqtg = VGroup(*[o.target for o in originals]).center().to_edge(TOP)

        t1 = TextMobject(
            'Using \\texttt{int} for $n$ makes the equations very similar,\\\\'
            'and the coding very simple!',
            tex_to_color_map={'int': RED})
        t1.next_to(eqtg, DOWN, buff=MED_LARGE_BUFF)
        self.play(*[MoveToTarget(o) for o in originals], FadeIn(t1))
        self.wait(duration=3)
        eqg = VGroup(*originals)

        # Transform to code
        code_scale = 0.7
        power2_code = CodeBlock(
            'Java',
            r"""
            public static int power2(int x, int n) {
                if (n == 0) {
                    return 1;
                }
                int t = power2(x, n / 2);
                if (n % 2 == 0) {
                    return t * t;
                }
                return t * t * x;
            }	
            """,
            code_scale=code_scale,
        )
        power2_code.to_edge(RIGHT)

        ef3.generate_target()
        of4.generate_target()
        ef3.target.next_to(power2_code.get_code().get_lines(7),
                           LEFT,
                           buff=LARGE_BUFF)
        of4.target.next_to(power2_code.get_code().get_lines(9),
                           LEFT,
                           buff=LARGE_BUFF)
        ef3.target.next_to(of4.target,
                           UP,
                           index_of_submobject_to_align=0,
                           coor_mask=X_AXIS)

        et = TextMobject('\\textit{even:}')
        ot = TextMobject('\\textit{odd:}')
        ot.next_to(of4.target[0], LEFT)
        et.next_to(ef3.target[0], LEFT)

        self.play(FadeOut(t1))
        self.play(
            FadeOut(if_even),
            FadeOut(if_odd),
            FadeInFrom(power2_code, RIGHT),
            MoveToTarget(ef3),
            MoveToTarget(of4),
            ReplacementTransform(if_even, et),
            ReplacementTransform(if_odd, ot),
        )
        self.wait()

        highlights = [
            [
                SurroundingRectangle(
                    power2_code.get_code().get_lines(1)[-6:-2]),
                SurroundingRectangle(
                    power2_code.get_code().get_lines(5)[-5:-2]),
                SurroundingRectangle(ef3[-1]),
            ],
            [
                SurroundingRectangle(
                    power2_code.get_code().get_lines(5)[5:-1]),
                SurroundingRectangle(ef3[-2:]),
            ],
            [
                SurroundingRectangle(
                    power2_code.get_code().get_lines(7)[-4:-1]),
                SurroundingRectangle(ef3[1:]),
            ],
            [
                SurroundingRectangle(
                    power2_code.get_code().get_lines(9)[-6:-1]),
                SurroundingRectangle(of4[1:]),
            ],
        ]

        for g in highlights:
            self.play(*[ShowCreation(h) for h in g])
            self.wait(duration=1.5)
            self.play(*[Uncreate(h) for h in g])
        self.wait()

        self.play(*[FadeOut(o) for o in self.mobjects if o != power2_code])
        power2_code.generate_target()
        power2_code.target.center()
        power2_code.target.to_edge(UP)
        self.play(MoveToTarget(power2_code))
Beispiel #11
0
    def construct(self):
        code_scale = 0.7
        power2_code = CodeBlock(
            'Java',
            r"""
            public static int power2(int x, int n) {
                if (n == 0) {
                    return 1;
                }
                int t = power2(x, n / 2);
                if (n % 2 == 0) {
                    return t * t;
                }
                return t * t * x;
            }	
            """,
            code_scale=code_scale,
        )
        power2_code.to_edge(UP)
        self.add(power2_code)

        t1 = TextMobject(
            "This version should call itself fewer than $n$ times")
        t1.next_to(power2_code, DOWN, buff=LARGE_BUFF)
        self.play(FadeInFromDown(t1))
        self.wait(duration=2.5)

        t2 = TextMobject(
            'How many times do you think \\texttt{power2(2,30)} will call itself?',
            tex_to_color_map={'30': YELLOW})
        t2.next_to(t1, ORIGIN)
        self.play(FadeOutAndShift(t1, UP), FadeInFromDown(t2))
        self.wait(duration=2)

        t3 = TextMobject("We know it'll be less than 30!  Maybe 15?")
        t3.next_to(t2, DOWN, buff=MED_LARGE_BUFF)
        self.play(FadeInFromDown(t3))
        self.wait(duration=2)

        t4 = TextMobject("How about a lot less?  Let's see...")
        t4.next_to(t3, ORIGIN)
        self.play(ReplacementTransform(t3, t4))
        self.wait(duration=2)

        self.play(*[FadeOut(o) for o in self.mobjects if o != power2_code])

        # Start stepping through this and see it go.
        main_code = CodeBlock(
            'Java',
            r"""
            public static void main(String[] args) {
                int y = power2(2, 30);
            }
            """,
            line_offset=10,
            code_scale=code_scale - 0.1,
        )
        frame_width = 3.5
        main_frame = StackFrame(main_code,
                                'main()',
                                12, ['y'],
                                width=frame_width,
                                slot_char_width=8)
        main_code.highlight_lines(12)
        VGroup(main_code, main_frame).arrange(RIGHT,
                                              buff=LARGE_BUFF).to_edge(DOWN)
        self.play(
            FadeInFromDown(main_frame),
            FadeInFromDown(main_code),
        )
        self.wait()

        def call_power2(x, n, call_stack, cc_num):
            new_cc_num = TextMobject(str(len(call_stack) -
                                         1)).move_to(cc_num).set_color(YELLOW)
            update_cc_anims = [ReplacementTransform(cc_num, new_cc_num)]

            call_ret_delay = 0.5
            stack_frame = StackFrame(power2_code,
                                     'power(%d, %d)' % (x, n),
                                     1, ['x', 'n', 't'],
                                     width=frame_width)
            call_stack.animate_call(stack_frame,
                                    self,
                                    extra_anims=update_cc_anims)

            self.play(
                *stack_frame.get_update_line_anims(2),
                stack_frame.update_slot,
                'x',
                x,
                stack_frame.update_slot,
                'n',
                n,
            )

            if n == 0:
                self.play(*stack_frame.get_update_line_anims(3))
                self.wait(duration=call_ret_delay)
                call_stack.animate_return(self)
                return 1
            else:
                self.play(*stack_frame.get_update_line_anims(5))
                self.wait(duration=call_ret_delay)

                t = call_power2(x, n // 2, call_stack, new_cc_num)
                self.play(
                    *stack_frame.get_update_line_anims(6),
                    stack_frame.update_slot,
                    't',
                    t,
                )

                if n % 2 == 0:
                    self.play(*stack_frame.get_update_line_anims(7))
                    self.wait(duration=call_ret_delay)
                    call_stack.animate_return(self)
                    return t * t

                self.play(*stack_frame.get_update_line_anims(9))
                self.wait(duration=call_ret_delay)
                call_stack.animate_return(self)
                return t * t * x

        cc_label = TextMobject('Recursive Calls').scale(0.75)
        cc_label.to_edge(UL).shift(DOWN)
        call_counter = TextMobject('0').set_color(YELLOW).next_to(
            cc_label, DOWN)
        self.play(FadeIn(call_counter), FadeIn(cc_label))

        result = call_power2(2, 30, CallStack(main_frame), call_counter)
        self.play(
            *main_frame.get_update_line_anims(13),
            main_frame.update_slot,
            'y',
            result,
        )
        self.wait()

        t1 = TextMobject(
            'We got our answer in just 5 recursive calls!').set_color(YELLOW)
        t1.next_to(power2_code, DOWN, buff=MED_LARGE_BUFF)
        self.play(ShowCreation(t1))
        self.wait(duration=2)

        t1.generate_target()
        t1.target.center().to_edge(TOP)
        t2 = TextMobject('But why?').next_to(t1.target, DOWN, buff=LARGE_BUFF)
        self.play(
            MoveToTarget(t1),
            FadeInFromDown(t2),
            *[FadeOut(o) for o in self.mobjects if o != t1],
        )
Beispiel #12
0
    def construct(self):
        title = TextMobject('Quick Review: The Call Stack').to_edge(UP)

        code_scale = 0.75
        main_code = CodeBlock(
            'Java',
            r"""
            public static
            void main(String[] args) {
                int n = foo(2);
            }
            """,
            code_scale=code_scale,
        )
        foo_code = CodeBlock(
            'Java',
            r"""
            static int foo(int x) {
                int n = bar(x + 1, x * 2);
                return n;
            }
            """,
            code_scale=code_scale,
        )
        bar_code = CodeBlock(
            'Java',
            r"""
            static int bar(int x,
                           int y) {
                int a = x + y;
                return a;
            }
            """,
            code_scale=code_scale,
        )
        fbg = VGroup(main_code, foo_code, bar_code)
        fbg.arrange(DOWN, buff=MED_SMALL_BUFF, aligned_edge=LEFT)
        fbg.to_edge(DR)
        self.play(
            ShowCreation(title),
            FadeInFrom(fbg, RIGHT),
        )
        self.wait()

        frame_width = 3.0
        args_ref = TextMobject('[ ]').scale(0.5)
        main_frame = StackFrame(main_code,
                                'main()',
                                3, [('args', args_ref), 'n'],
                                width=frame_width)
        main_frame.next_to(fbg, LEFT, buff=LARGE_BUFF).to_edge(DOWN)
        main_code.move_highlight_rect(3)
        text_scale = 0.75
        b1 = BraceLabel(main_frame,
                        'The call stack\\\\starts with main()',
                        brace_direction=LEFT,
                        label_constructor=TextMobject,
                        label_scale=text_scale)
        self.play(
            FadeInFrom(main_frame, UP),
            main_code.highlight_lines,
            3,
            FadeInFrom(b1, UP),
        )
        self.wait()

        foo_frame = StackFrame(foo_code,
                               'foo(2)',
                               5, ['x', 'n'],
                               width=frame_width)
        foo_frame.next_to(main_frame, UP, buff=SMALL_BUFF)
        b2 = BraceLabel(foo_frame,
                        'Calls push frames',
                        brace_direction=LEFT,
                        label_constructor=TextMobject,
                        label_scale=text_scale)
        hr_caller, hr_callee = main_code.setup_for_call(foo_code, 1)
        self.play(
            main_code.highlight_caller,
            ReplacementTransform(hr_caller, hr_callee, path_arc=np.pi),
            FadeInFrom(foo_frame, UP),
            FadeInFrom(b2, UP),
        )
        foo_code.complete_callee(hr_callee, self)
        self.wait()

        self.play(
            foo_code.highlight_lines,
            2,
            foo_frame.set_line,
            6,
            foo_frame.update_slot,
            'x',
            2,
        )
        self.wait()

        bar_frame = StackFrame(bar_code,
                               'bar(3, 4)',
                               10, ['x', 'y', 'a'],
                               width=frame_width)
        bar_frame.next_to(foo_frame, UP, buff=SMALL_BUFF)
        b3 = BraceLabel(bar_frame,
                        'Holds arguments\\\\and locals',
                        brace_direction=LEFT,
                        label_constructor=TextMobject,
                        label_scale=text_scale)
        hr_caller, hr_callee = foo_code.setup_for_call(bar_code, (1, 2))
        self.play(
            foo_code.highlight_caller,
            ReplacementTransform(hr_caller, hr_callee, path_arc=np.pi),
            FadeInFrom(bar_frame, UP),
            FadeInFrom(b3, UP),
        )
        bar_code.complete_callee(hr_callee, self)
        self.wait()

        self.play(
            bar_code.highlight_lines,
            3,
            bar_frame.set_line,
            11,
            bar_frame.update_slot,
            'x',
            3,
            bar_frame.update_slot,
            'y',
            4,
        )
        self.wait()

        self.play(
            bar_code.highlight_lines,
            4,
            bar_frame.set_line,
            12,
            bar_frame.update_slot,
            'a',
            7,
        )
        self.wait(duration=3)

        hr_returner, hr_returnee = bar_code.setup_for_return(foo_code)
        self.play(
            ReplacementTransform(hr_returner, hr_returnee, path_arc=-np.pi),
            foo_code.highlight_returnee,
            Uncreate(bar_frame),
            FadeOutAndShift(b3, LEFT),
            FadeOutAndShift(b2, LEFT),
            FadeOutAndShift(b1, LEFT),
        )
        foo_code.complete_returnee(hr_returnee, self)
        self.wait()

        b4 = BraceLabel(foo_frame,
                        'Returns pop',
                        brace_direction=LEFT,
                        label_constructor=TextMobject,
                        label_scale=text_scale)
        self.play(
            foo_code.highlight_lines,
            3,
            foo_frame.set_line,
            7,
            foo_frame.update_slot,
            'n',
            7,
            ShowCreation(b4),
        )
        self.wait()

        hr_returner, hr_returnee = foo_code.setup_for_return(main_code)
        self.play(
            ReplacementTransform(hr_returner, hr_returnee, path_arc=-np.pi),
            main_code.highlight_returnee,
            Uncreate(foo_frame),
            FadeOutAndShift(b4, LEFT),
        )
        main_code.complete_returnee(hr_returnee, self)
        self.wait()

        self.play(
            main_code.highlight_lines,
            4,
            main_frame.set_line,
            4,
            main_frame.update_slot,
            'n',
            7,
        )
        self.wait()

        hr_returner, hr_returnee = main_code.setup_for_return(main_code)
        hr_returnee.shift(UP * 4)
        self.play(
            ReplacementTransform(hr_returner, hr_returnee, path_arc=-np.pi),
            Uncreate(main_frame),
        )
        self.remove(hr_returnee)
        self.wait()

        t1 = TextMobject('Take a moment to think about\\\\'
                         'where args and locals live,\\\\'
                         'and  what the call stack looks like.').to_edge(LEFT)
        t2 = TextMobject('\\textit{It will help later!!}').next_to(
            t1, DOWN, buff=LARGE_BUFF)
        self.play(FadeIn(t1))
        self.wait()
        self.play(ShowCreation(t2))
        self.wait(duration=2)
Beispiel #13
0
    def construct(self):
        title = TextMobject('The Call Stack').to_edge(UP)
        self.add(title)

        frame_width = 3.0
        dummy_code = CodeBlock('Java', 'foo')
        args_ref = TextMobject('[ ]').scale(0.5)
        main_frame = StackFrame(dummy_code,
                                'main()',
                                3, [('args', args_ref)],
                                width=frame_width)
        foo_frame = StackFrame(dummy_code,
                               'foo()',
                               6, [('n', 6)],
                               width=frame_width)
        bar_frame = StackFrame(dummy_code,
                               'bar(1, 2)',
                               13, [('x', 1), ('y', 2), ('a', 3), ('b', 6)],
                               width=frame_width)
        main_frame.to_edge(DOWN)
        foo_frame.next_to(main_frame, UP)
        bar_frame.next_to(foo_frame, UP)
        frame_group = VGroup(main_frame, foo_frame, bar_frame)
        self.play(
            LaggedStartMap(FadeInFrom,
                           frame_group,
                           group=frame_group,
                           direction=UP,
                           lag_ratio=0.5))
        self.wait()

        text_scale = 0.75
        b1 = BraceLabel(main_frame,
                        'Always starts\\\\with main()',
                        brace_direction=LEFT,
                        label_constructor=TextMobject,
                        label_scale=text_scale)
        # b2 = BraceLabel(foo_frame, ['Calls push frames,\\\\', 'returns pop'],
        #                 brace_direction=RIGHT, label_constructor=TextMobject,
        #                 alignment='')
        b3 = BraceLabel(bar_frame.slots()[0:2],
                        'Parameters',
                        brace_direction=RIGHT,
                        label_constructor=TextMobject,
                        label_scale=text_scale)
        b4 = BraceLabel(bar_frame.slots()[2:4],
                        'Locals',
                        brace_direction=RIGHT,
                        label_constructor=TextMobject,
                        label_scale=text_scale)
        b5 = BraceLabel(bar_frame.slots()[0:4],
                        'Storage for all\\\\variables in a function',
                        brace_direction=LEFT,
                        label_constructor=TextMobject,
                        label_scale=text_scale)

        notes = VGroup(b1, b5, b3, b4)

        box_count = 8
        colors = color_gradient([BLUE, ORANGE], box_count)
        little_boxes = VGroup(*[
            Rectangle(height=0.25, width=0.75, fill_opacity=1, color=colors[i])
            for i in range(box_count)
        ])
        little_boxes.arrange(UP, buff=0.1)

        push_up = TextMobject('Calls push\\\\frames').scale(text_scale)
        push_up.next_to(little_boxes, DOWN)
        pua = Arrow(push_up.get_bottom(), push_up.get_top()).scale(2)
        pua.next_to(push_up, LEFT)
        pug = VGroup(push_up, pua)

        pop_down = TextMobject('Returns pop\\\\frames').scale(text_scale)
        pop_down.next_to(little_boxes, UP)
        pda = Arrow(pop_down.get_top(), pop_down.get_bottom()).scale(2)
        pda.next_to(pop_down, RIGHT)
        pdg = VGroup(pop_down, pda)

        bg = VGroup(little_boxes, pug, pdg)
        bg.to_edge(RIGHT, buff=MED_LARGE_BUFF)

        self.play(
            LaggedStartMap(ShowCreation,
                           notes,
                           group=notes,
                           lag_ratio=0.7,
                           run_time=3.0))
        self.play(
            FadeIn(pug),
            LaggedStartMap(FadeInFrom,
                           little_boxes,
                           lambda m: (m, RIGHT),
                           lag_ratio=1.0,
                           run_time=3.0))
        self.play(FadeIn(pdg))
        pdg.generate_target()
        pdg.target.next_to(little_boxes[3], UP,
                           submobject_to_align=pdg[0]).set_opacity(1.0)
        self.play(
            MoveToTarget(pdg, run_time=3.0),
            LaggedStartMap(FadeOutAndShift,
                           VGroup(*list(reversed(little_boxes))[:4]),
                           lambda m: (m, RIGHT),
                           lag_ratio=1.0,
                           run_time=2.0),
        )
        self.wait(duration=5)

        end_scale_group = VGroup(*self.mobjects)
        end_fade_group = VGroup(title)
        self.animate_yt_end_screen(end_scale_group,
                                   end_fade_group,
                                   show_elements=False,
                                   show_rects=False)
Beispiel #14
0
    def construct(self):
        f0 = TexMobject("F_0 = 0")
        f1 = TexMobject("F_1 = 1")
        fn = TexMobject("F_n = F_{n-1} + F_{n-2}")
        fg = VGroup(f0, f1, fn)
        fg.arrange(RIGHT, buff=LARGE_BUFF).shift(UP)
        self.add(fg)

        fib_small_code = CodeBlock(
            'Java',
            r"""
            public static int fib(int n) {
                if (n == 0) {
                    return 0;
                }
                if (n == 1) {
                    return 1;
                }
                return fib(n - 1) + fib(n - 2);
            }
            """,
        )
        fib_code = CodeBlock(
            'Java',
            r"""
            public static int fib(int n) {
                if (n == 0) {
                    return 0;
                }
                if (n == 1) {
                    return 1;
                }
                int fn1 = fib(n - 1);
                int fn2 = fib(n - 2);
                return fn1 + fn2;
            }
            """,
        )

        fib_small_code.to_edge(RIGHT)
        f0.generate_target()
        f1.generate_target()
        fn.generate_target()
        fsc = fib_small_code.get_code()
        eq_code_buff = LARGE_BUFF * 1.5
        f0.target.next_to(fsc.get_lines((2, 5)), LEFT, buff=eq_code_buff)
        f1.target.next_to(fsc.get_lines((5, 8)), LEFT, buff=eq_code_buff)
        fn.target.next_to(fsc.get_lines(8), LEFT, buff=eq_code_buff)
        self.play(*[MoveToTarget(o) for o in [f0, f1, fn]], )
        self.play(FadeInFrom(fib_small_code, RIGHT), )
        self.wait(duration=3)

        fib_small_code.generate_target()
        fib_small_code.target.move_to(fib_code, aligned_edge=UL)
        self.play(FadeOut(VGroup(f0, f1, fn)), MoveToTarget(fib_small_code))

        adj_desc = TextMobject(
            "Let's make this a little easier to trace through...")
        adj_desc.to_edge(DOWN)
        self.play(FadeIn(adj_desc))
        self.wait()

        fc = fib_code.get_code()
        self.play(
            fsc[-2].next_to,
            fc[-2],
            {
                'direction': ORIGIN,
                'index_of_submobject_to_align': 0,
            },
            fsc[-1].move_to,
            fc[-1],
            {'aligned_edge': LEFT},
        )

        # 012 345 6 78901 2 345
        # int fn1 = fib(n - 1);
        self.play(FadeIn(fc[-4][:7]), )

        # 012345 67890 1 23 4 56789 0 123
        # return fib(n - 1) + fib(n - 2);
        self.play(
            fsc[-2][6:14].move_to,
            fc[-4][7:15],
        )

        # 012345 678 9 0123
        # return fn1 + fn2;
        self.play(
            FadeIn(fc[-4][-1]),
            FadeIn(fc[-2][6:9]),
            fsc[-2][14].move_to,
            fc[-2][9],
        )

        self.play(FadeIn(fc[-3][:7]), )
        self.play(
            fsc[-2][15:23].move_to,
            fc[-3][7:15],
        )
        self.play(
            FadeIn(fc[-3][-1]),
            FadeIn(fc[-2][10:13]),
            fsc[-2][-1].move_to,
            fc[-2][-1],
        )
        self.clear()
        self.add(fib_code, adj_desc)
        self.wait()

        t1 = TextMobject("Okay, let's run it")
        t1.move_to(adj_desc)
        self.play(ReplacementTransform(adj_desc, t1))
        self.wait(duration=2)

        self.play(FadeOut(t1))
Beispiel #15
0
    def construct(self):
        unsorted = [11, 0, 8, 2, 2, 9, 14, 5]

        code_scale = 0.6
        # TODO: move to base, since it references code lines and runs this code
        ms_code = CodeBlock(
            'Java',
            r"""
            public static int[] mergeSort(int[] a) {
                if (a.length == 1) {
                    return a;
                }
                int m = a.length / 2;
                int[] l, r;
                l = Arrays.copyOfRange(a, 0, m);
                r = Arrays.copyOfRange(a, m, a.length);
                l = mergeSort(l);
                r = mergeSort(r);
                return merge(l, r);
            }
            """,
            code_scale=code_scale,
        )
        ms_code.set_annotation(5, None)

        t1 = TextMobject("Recursive mergesort in Java")
        t1.to_edge(UP)
        ms_code.next_to(t1, DOWN, buff=MED_LARGE_BUFF)
        self.play(FadeIn(t1))
        self.play(
            LaggedStartMap(FadeInFromDown, ms_code.get_code(), lag_ratio=0.2))
        self.wait(duration=3.0)

        t2 = TextMobject(
            "Let's trace through this and track our work with a tree",
            buff=LARGE_BUFF)
        t2.next_to(ms_code, DOWN, buff=LARGE_BUFF)
        self.play(FadeInFromDown(t2))
        self.wait(duration=3)

        self.play(ms_code.to_edge, DR, FadeOutAndShiftDown(t2),
                  FadeOutAndShift(t1, UP))

        colors = color_gradient([PINK, BLUE, YELLOW_D], 15)
        tree = build_merge_sort_tree(unsorted, text_scale=1.0)
        update_tree_colors(tree, colors)
        tree.layout(1.6, 1.2)
        g = tree.to_vgroup()
        g.center().to_edge(UP)

        be = Merge8Exposition(self, tree, ms_code)

        t1 = TextMobject("Start with an array of random numbers")
        t1.next_to(tree.label, DOWN, buff=MED_LARGE_BUFF)
        be.deferred_play(1, FadeIn(t1))
        be.deferred_wait(1)
        be.deferred_play(3, FadeOut(t1))

        be.play(FadeIn(tree.label))
        self.run_merge_sort(tree, be, 0)
        self.wait()

        t1 = TextMobject("We divide the problem on the way down")
        t2 = TextMobject("and we recombine by merging on the way up")
        t1.shift(DOWN * 1.5)
        t2.next_to(t1, DOWN)
        self.play(FadeInFromDown(t1))
        self.wait()
        self.play(FadeInFromDown(t2))
        self.wait(duration=3)

        t3 = TextMobject("We call this \\textit{divide-and-conquer}")
        t4 = TextMobject("\\textit{This is very common!} You'll see it a lot.")
        t3.shift(DOWN * 1.5)
        t4.next_to(t3, DOWN)
        self.play(FadeOutAndShiftDown(t1), FadeOutAndShiftDown(t2))
        self.play(FadeInFromDown(t3))
        self.wait()
        self.play(FadeInFromDown(t4))
        self.wait(duration=3)

        t5 = TextMobject("This one was nicely balanced and even...")
        t6 = TextMobject("let's try one that's a little bit odd")
        t5.shift(DOWN * 1.5)
        t6.next_to(t5, DOWN)
        self.play(FadeOutAndShiftDown(t3), FadeOutAndShiftDown(t4))
        self.play(FadeInFromDown(t5))
        self.wait()
        self.play(FadeInFromDown(t6))
        self.wait(duration=3)

        self.play(*[FadeOut(o) for o in self.mobjects])
Beispiel #16
0
    def construct(self):
        fib_code = CodeBlock(
            'Java',
            r"""
            public static int fib(int n) {
                if (n == 0) {
                    return 0;
                }
                if (n == 1) {
                    return 1;
                }
                int fn1 = fib(n - 1);
                int fn2 = fib(n - 2);
                return fn1 + fn2;
            }
            """,
        )
        self.add(fib_code)

        main_code = CodeBlock(
            'Java',
            r"""
            public static void main(String[] args) {
                int x = fib(3);
            }
            """,
            line_offset=11,
            code_scale=0.6,
        )

        frame_width = 2.5
        main_frame = StackFrame(main_code,
                                'main()',
                                13, ['x'],
                                width=frame_width)
        main_code.highlight_lines(13)
        VGroup(main_code,
               main_frame).arrange(RIGHT, buff=LARGE_BUFF * 2).to_edge(DOWN)
        self.play(
            fib_code.to_edge,
            UP,
            FadeInFromDown(main_frame),
            FadeInFromDown(main_code),
        )
        self.wait()

        def call_fib(run_ctx, n, call_stack):
            run_ctx['call_count'] += 1
            call_ret_delay = 0.5
            stack_frame = StackFrame(fib_code,
                                     'fib(%d)' % n,
                                     1, ['n', 'fn1', 'fn2'],
                                     width=frame_width)
            call_stack.animate_call(stack_frame, self)

            self.play(
                *stack_frame.get_update_line_anims(2),
                stack_frame.update_slot,
                'n',
                n,
            )

            if n == 0:
                self.play(*stack_frame.get_update_line_anims(3))
                self.wait(duration=call_ret_delay)
                call_stack.animate_return(self)
                return 0

            self.play(*stack_frame.get_update_line_anims(5))
            if n == 1:
                self.play(*stack_frame.get_update_line_anims(6))
                self.wait(duration=call_ret_delay)
                call_stack.animate_return(self)
                return 1

            self.play(*stack_frame.get_update_line_anims(8))
            self.wait(duration=call_ret_delay)
            fn1 = call_fib(run_ctx, n - 1, call_stack)
            self.play(
                *stack_frame.get_update_line_anims(9),
                stack_frame.update_slot,
                'fn1',
                fn1,
            )

            if run_ctx['call_count'] == 4:
                t1 = TextMobject('\\textit{Whoa!!}')
                t1.set_color(YELLOW).scale(1.2)
                t1.next_to(fib_code, LEFT, buff=MED_LARGE_BUFF)
                self.play(GrowFromCenter(t1))
                t2 = TextMobject("\\textit{I'm lost...}")
                t2.next_to(t1, DOWN, buff=LARGE_BUFF)
                self.play(FadeInFromPoint(t2, t2.get_center()))
                run_ctx['whoa_desc'] = VGroup(t1, t2)

            self.wait(duration=call_ret_delay)
            fn2 = call_fib(run_ctx, n - 2, call_stack)
            self.play(
                *stack_frame.get_update_line_anims(10),
                stack_frame.update_slot,
                'fn2',
                fn2,
            )

            self.wait(duration=call_ret_delay)
            call_stack.animate_return(self)
            return fn1 + fn2

        run_ctx = {
            'call_count': 0,
        }

        result = call_fib(run_ctx, 3, CallStack(main_frame))
        self.play(
            *main_frame.get_update_line_anims(14),
            main_frame.update_slot,
            'x',
            result,
        )
        self.wait(duration=2)

        hrm_desc = TextMobject(
            "That worked, but it was really hard to follow!")
        hrm_desc.next_to(fib_code, DOWN, buff=MED_LARGE_BUFF)
        self.play(FadeOutAndShiftDown(main_frame),
                  FadeOutAndShiftDown(main_code),
                  FadeOut(run_ctx['whoa_desc']))
        self.play(FadeInFromDown(hrm_desc))
        self.wait(duration=2.5)

        t1 = TextMobject("The call stack shows where we are at any moment")
        t1.move_to(hrm_desc)
        t2 = TextMobject("but we don't know where we've been...")
        t2.next_to(t1, DOWN)
        t3 = TextMobject("or where we're going...")
        t3.next_to(t2, DOWN)

        self.play(ReplacementTransform(hrm_desc, t1))
        self.wait(duration=2)
        self.play(FadeInFromDown(t2))
        self.wait(duration=2)
        self.play(FadeInFromDown(t3))
        self.wait(duration=2)

        t4 = TextMobject(
            "Let's go again, but draw a new picture along the way")
        t4.move_to(t1)
        self.play(*[FadeOutAndShift(o, UP) for o in [t1, t2, t3]],
                  FadeInFromDown(t4))
        self.wait(duration=2.5)
        self.play(FadeOut(t4))
Beispiel #17
0
    def construct(self):
        code_scale = 0.75
        foo_code = CodeBlock(
            'Java',
            r"""
            int foo() {
                int n = bar(2, 1);
                n += bar(2, 2);
                return n;
            }
            """,
            add_labels=True,
            code_scale=code_scale,
        )
        bar_code = CodeBlock(
            'Java',
            r"""
            int bar(int x, int y) {
                if (x == 1) {
                    return y;
                }
                int b = bar(x - 1, y + 1);
                return b;
            }
            """,
            line_offset=5,
            add_labels=True,
            code_scale=code_scale,
        )

        title = TextMobject('CodeBlock Basics Demo')
        title.to_edge(UP)
        foo_code.next_to(title, DOWN)
        foo_code.fade_labels()
        bar_code.next_to(foo_code.get_code(),
                         DOWN,
                         buff=MED_LARGE_BUFF,
                         aligned_edge=LEFT,
                         submobject_to_align=bar_code.get_code())
        self.play(*[FadeIn(o) for o in [title, foo_code, bar_code]])
        self.wait(0.5)

        self.play(foo_code.highlight_lines, 1, bar_code.highlight_lines,
                  (6, 8))
        self.wait(0.5)
        self.play(foo_code.highlight_lines, 2, bar_code.highlight_lines,
                  (8, 10))
        self.play(foo_code.highlight_lines, 3, bar_code.highlight_lines,
                  (10, 12))
        self.wait(0.5)
        self.play(Indicate(foo_code))
        self.wait(0.5)
        self.play(Indicate(bar_code))
        self.wait(0.5)
        self.play(foo_code.remove_highlight, bar_code.remove_highlight)
        self.wait(0.5)
        self.play(FadeOut(foo_code))
        self.wait(0.5)
        self.play(FadeIn(foo_code))
        self.wait(0.5)
        self.play(foo_code.show_labels, bar_code.fade_labels)
        self.wait(0.5)
        self.play(foo_code.fade_labels, bar_code.show_labels)
        self.wait(0.5)

        foo_code.move_hidden_highlight(2)
        bar_code.move_hidden_highlight((8, 10))
        self.wait(0.5)
        self.play(foo_code.highlight_lines, 2, bar_code.highlight_lines,
                  (8, 10))
        self.wait(0.5)
        self.play(foo_code.highlight_lines, 4, bar_code.highlight_lines,
                  (6, 9))
        self.wait(0.5)
        self.play(foo_code.remove_highlight, bar_code.remove_highlight)
        self.wait(0.5)

        self.play(FadeOut(foo_code), FadeOut(bar_code))
        self.wait()
Beispiel #18
0
    def construct(self):
        fib_code = self.get_fib_code(0.75)
        fib_code.to_edge(UP)
        self.add(fib_code)

        fc2 = self.get_fib_code(0.6)
        fc2.to_edge(UR)
        self.play(ReplacementTransform(fib_code, fc2))
        fib_code = fc2

        first_fib_arg = 4
        main_code = CodeBlock(
            'Java',
            r"""
            public static void main(String[] args) {
                int x = fib(%d);
            }
            """ % first_fib_arg,
            line_offset=11,
            code_scale=0.6,
        )

        main_frame = StackFrame(main_code,
                                'main()',
                                13, ['x'],
                                width=2.5,
                                text_scale=0.6)
        main_code.highlight_lines(13)
        VGroup(main_code,
               main_frame).arrange(RIGHT, buff=LARGE_BUFF * 2).to_edge(DOWN)
        tree_area_frame = Polygon(
            np.array((-FRAME_X_RADIUS, FRAME_Y_RADIUS, 0)),
            np.array((fib_code.get_corner(UL)[0], FRAME_Y_RADIUS, 0)),
            np.array((fib_code.get_corner(DL)[0], main_code.get_top()[1], 0)),
            np.array((-FRAME_X_RADIUS, main_code.get_top()[1], 0)),
            mark_paths_closed=True,
            close_new_points=True,
        )

        ready_desc = TextMobject(
            "We'll compute $F_%d$\\\\and track \\textit{all} "
            "our work as we go" % first_fib_arg)
        ready_desc.move_to(tree_area_frame)
        self.play(FadeIn(ready_desc))
        self.wait(duration=2)

        self.play(
            fib_code.to_edge,
            UP,
            FadeInFromDown(main_frame),
            FadeInFromDown(main_code),
        )

        tree_text_scale = 1.0
        tmp_leaf = TextMobject('$F_%d$' % 1).scale(tree_text_scale)
        leaf_width = tmp_leaf.get_width()
        level_spread_factors = [4, 1.9, 1.75]
        level_spreads = [f * leaf_width for f in level_spread_factors]

        tree = BinaryTree(
            max_depth=first_fib_arg,
            leaf_height=tmp_leaf.get_height(),
            level_buff=LARGE_BUFF * 1.5,
            level_spreads=level_spreads,
            text_scale=tree_text_scale,
        )

        def update_for_return(current_node, val, run_ctx):
            v = TextMobject(str(val))
            # v = TextMobject(current_node.label.tex_string, '$=%d$' % val)
            v.set_color(GREEN_SCREEN)
            v.scale(tree_text_scale)
            v.move_to(current_node.label)
            self.play(ReplacementTransform(current_node.label, v))
            self.wait(duration=run_ctx['update_node_delay'])
            extra_anims = []
            if current_node.parent:
                sr = SurroundingRectangle(current_node.parent)
                extra_anims.append(ReplacementTransform(tree.current_rect, sr))
                tree.current_rect = sr
                extra_anims.append(
                    ApplyMethod(current_node.parent.set_color, YELLOW))
            else:
                extra_anims.append(FadeOut(tree.current_rect))
            return extra_anims

        def call_fib(run_ctx, call_stack, current_node, n):
            run_ctx['call_count'] += 1
            call_ret_delay = 0.5
            stack_frame = StackFrame(fib_code,
                                     'fib(%d)' % n,
                                     1, ['n', 'fn1', 'fn2'],
                                     width=call_stack[0].width,
                                     text_scale=call_stack[0].text_scale)
            sr = SurroundingRectangle(current_node)
            extra_anims = [current_node.set_color, YELLOW]
            if tree.current_rect:
                extra_anims.append(ReplacementTransform(tree.current_rect, sr))
            else:
                extra_anims.append(ShowCreation(sr))
            tree.current_rect = sr
            if current_node.parent:
                extra_anims.append(
                    ApplyMethod(current_node.parent.set_color, ORANGE))
            call_stack.animate_call(stack_frame, self, extra_anims=extra_anims)

            self.play(
                *stack_frame.get_update_line_anims(2),
                stack_frame.update_slot,
                'n',
                n,
            )
            if n == 0:
                self.play(*stack_frame.get_update_line_anims(3))
                self.wait(duration=call_ret_delay)

                extra_anims = []
                if run_ctx['call_count'] == 5:
                    s1 = TextMobject("and we know $F_0=0$")
                    s1.move_to(tree_area_frame).shift(DOWN)
                    self.play(FadeIn(s1))
                    extra_anims.append(FadeOut(s1))

                extra_anims.extend(update_for_return(current_node, 0, run_ctx))
                call_stack.animate_return(self, extra_anims=extra_anims)
                return 0

            self.play(*stack_frame.get_update_line_anims(5))
            if n == 1:
                self.play(*stack_frame.get_update_line_anims(6))
                self.wait(duration=call_ret_delay)

                extra_anims = []
                if run_ctx['call_count'] == 4:
                    s1 = TextMobject("we know $F_1=1$")
                    s1.move_to(tree_area_frame).shift(DOWN)
                    self.play(FadeIn(s1))
                    extra_anims.append(FadeOut(s1))

                extra_anims.extend(update_for_return(current_node, 1, run_ctx))
                call_stack.animate_return(self, extra_anims=extra_anims)
                return 1

            self.play(*stack_frame.get_update_line_anims(8))

            self.wait(duration=call_ret_delay)

            s1 = None
            if run_ctx['call_count'] == 1:
                s1 = TextMobject('we need $F_3$ and $F_2$')
                s1.move_to(tree_area_frame)
                self.play(FadeIn(s1))
                self.wait()
            elif run_ctx['call_count'] == 2:
                s1 = TextMobject('and now we need $F_2$ and $F_1$')
                s1.move_to(tree_area_frame).shift(DOWN)
                self.play(FadeIn(s1))
                self.wait()

            left_node, ll = tree.add_left_node(current_node,
                                               '$F_%d$' % (n - 1))
            right_node, rl = tree.add_right_node(current_node,
                                                 '$F_%d$' % (n - 2))
            left_node.set_color(BLUE)
            right_node.set_color(BLUE)
            self.add(ll, rl,
                     tree.current_rect)  # Keep the rect on top of lines
            self.play(*[FadeIn(o) for o in [left_node, ll, right_node, rl]])

            if s1:
                self.wait()
                self.play(FadeOut(s1))
                s1 = None

            fn1 = call_fib(run_ctx, call_stack, left_node, n - 1)

            if run_ctx['call_count'] == 4:
                s1 = TextMobject("now do the other half")
                s1.move_to(tree_area_frame).shift(DOWN)
                self.play(FadeIn(s1))
            elif run_ctx['call_count'] == 6:
                t1 = TextMobject("Using a ``tree'' for this is \\textit{much} "
                                 "more helpful!").scale(0.8)
                t2 = TextMobject("Shows where we've ", "been", ", where we ",
                                 "are", ",\\\\and where we're ", "going")
                t2.scale(0.8)
                t2.set_color_by_tex_to_color_map({
                    "been": GREEN_SCREEN,
                    "are": YELLOW,
                    "going": BLUE,
                })
                t1.move_to(tree_area_frame).shift(DOWN)
                t2.next_to(t1, DOWN, buff=MED_SMALL_BUFF)
                self.play(FadeIn(t1))
                self.wait()
                self.play(Write(t2))
                self.wait()
                run_ctx['final_desc'] = VGroup(t1, t2)

            self.play(
                *stack_frame.get_update_line_anims(9),
                stack_frame.update_slot,
                'fn1',
                fn1,
            )

            if s1:
                self.wait()
                self.play(FadeOut(s1))
                s1 = None

            self.wait(duration=call_ret_delay)
            fn2 = call_fib(run_ctx, call_stack, right_node, n - 2)

            extra_anims = []
            if run_ctx['call_count'] == 5:
                s1 = TextMobject("add the two halves")
                s1.move_to(tree_area_frame).shift(DOWN)
                self.play(FadeIn(s1))
                extra_anims.append(FadeOut(s1))
                run_ctx['update_node_delay'] = 0.1

            self.play(
                *stack_frame.get_update_line_anims(10),
                stack_frame.update_slot,
                'fn2',
                fn2,
            )

            self.wait(duration=call_ret_delay)
            extra_anims.extend(
                update_for_return(current_node, fn1 + fn2, run_ctx))
            call_stack.animate_return(self, extra_anims=extra_anims)
            return fn1 + fn2

        root_node, _ = tree.add_left_node(None, '$F_%d$' % first_fib_arg)
        root_node.move_to(tree_area_frame)
        root_node.to_edge(UP)
        self.play(ShowCreation(root_node))
        self.wait()

        self.play(FadeOut(ready_desc))

        run_ctx = {
            'call_count': 0,
            'update_node_delay': 1.0,
        }

        result = call_fib(run_ctx, CallStack(main_frame, scroll_height=1),
                          root_node, first_fib_arg)
        self.play(
            *main_frame.get_update_line_anims(14),
            main_frame.update_slot,
            'x',
            result,
        )
        self.wait(duration=2)

        self.play(FadeOutAndShiftDown(main_code),
                  FadeOutAndShiftDown(main_frame))
        self.wait()

        t1 = TextMobject("It is common to see recursive algorithms "
                         "described with trees")
        t2 = TextMobject("\\textit{Try drawing them out as you go!}")
        t2.set_color(GREEN)
        t2.to_edge(DOWN)
        t1.next_to(t2, UP, buff=MED_LARGE_BUFF)
        self.play(FadeInFromDown(t1))
        self.wait(duration=1.5)
        self.play(Write(t2))
        self.wait(duration=5)

        x = self.camera.get_mobjects_to_display(self.mobjects)
        end_scale_group = VGroup(*x)
        end_fade_group = VGroup()
        self.animate_yt_end_screen(end_scale_group,
                                   end_fade_group,
                                   end_scale=0.6,
                                   show_elements=False)
Beispiel #19
0
    def construct(self):
        high_quality = False

        # - Foo calls bar passing some args. Bar does something, returns.
        code_scale = 0.75
        main_code = CodeBlock(
            'Java',
            r"""
            public static
            void main(String[] args) {
                foo();
            }
            """,
            code_scale=code_scale,
        )
        foo_code = CodeBlock(
            'Java',
            r"""
            static int foo() {
                int n = bar(1, 2);
                return n;
            }
            """,
            code_scale=code_scale,
        )
        bar_code = CodeBlock(
            'Java',
            r"""
            static int bar(int x,
                           int y) {
                int a = x + y;
                int b = a * 2;
                return b;
            }
            """,
            code_scale=code_scale,
        )
        fbg = VGroup(foo_code, bar_code)
        bar_code.next_to(foo_code, DOWN, aligned_edge=LEFT, buff=LARGE_BUFF)
        fbg.to_edge(TOP)
        title = TextMobject('We have two Java functions, foo() and bar()')
        title.to_edge(UP)
        self.play(
            ShowCreation(title),
            FadeInFromDown(fbg),
        )
        self.wait(duration=2)

        # Let's write down what happens when we run this.
        t2 = TextMobject("Let's run them and write down\\\\"
                         "each variable as we go...")
        t2.to_edge(LEFT)
        self.play(Write(t2), fbg.to_edge, RIGHT, {'buff': MED_SMALL_BUFF})
        self.wait()

        t3 = TextMobject('Running foo() and bar() by hand').to_edge(UP)
        if high_quality:
            self.play(ReplacementTransform(VGroup(title, t2), t3))
        else:
            self.play(FadeOut(title), FadeOut(t2), FadeIn(t3))
        self.remove(title, t2)
        title = t3
        self.wait()

        t4 = TextMobject('First, make a place\\\\to write each variable')
        t4.next_to(title, DOWN, buff=LARGE_BUFF).to_edge(LEFT)
        self.play(FadeIn(t4))
        self.wait()

        foo_vars = VGroup(TexMobject('n:', '\\_'))
        foo_vars.next_to(foo_code, LEFT, buff=LARGE_BUFF * 2)

        bar_vars = VGroup(
            TexMobject('x:', '\\_'),
            TexMobject('y:', '\\_').shift(DOWN * .75),
            TexMobject('a:', '\\_').shift(DOWN * 1.5),
            TexMobject('b:', '\\_').shift(DOWN * 2.25),
        )
        bar_vars.next_to(bar_code, LEFT, aligned_edge=TOP, buff=LARGE_BUFF * 2)
        self.play(Write(foo_vars))
        self.play(Write(bar_vars))
        self.wait()

        t5 = TextMobject("Start in foo()...")
        t5.move_to(t4, aligned_edge=LEFT)
        self.play(FadeOut(t4), FadeIn(t5))
        self.wait()

        foo_code.move_hidden_highlight(2)
        self.play(FadeOut(t5), foo_code.highlight_lines, 2)
        self.wait()

        foo_n_q = TexMobject('?').move_to(foo_vars[0][1],
                                          aligned_edge=BOTTOM).shift(UP * 0.1)
        self.play(Write(foo_n_q))
        foo_vars.add(foo_n_q)
        self.wait()

        xi = foo_code.pre_call(bar_code, (1, 3))
        self.play(*foo_code.get_control_transfer_counterclockwise(xi), )
        bar_code.post_control_transfer(xi, self)
        self.wait()

        bar_x = TexMobject('1').move_to(bar_vars[0][1],
                                        aligned_edge=BOTTOM).shift(UP * 0.1)
        bar_y = TexMobject('2').move_to(bar_vars[1][1],
                                        aligned_edge=BOTTOM).shift(UP * 0.1)
        a = Arrow(bar_code.get_code().get_lines(2),
                  VGroup(bar_x, bar_y).get_right(),
                  stroke_width=3)
        self.play(Write(bar_x), Write(bar_y), ShowCreationThenDestruction(a))
        bar_vars_extras = VGroup()
        bar_vars_extras.add(bar_x, bar_y)
        self.play(bar_code.highlight_lines, 3)
        self.wait()

        bar_a = TexMobject('3').move_to(bar_vars[2][1],
                                        aligned_edge=BOTTOM).shift(UP * 0.1)
        a = Arrow(bar_code.get_code().get_lines(3),
                  bar_a.get_right(),
                  stroke_width=3)
        self.play(Write(bar_a), ShowCreationThenDestruction(a))
        bar_vars_extras.add(bar_a)
        self.play(bar_code.highlight_lines, 4)
        self.wait()

        bar_b = TexMobject('6').move_to(bar_vars[3][1],
                                        aligned_edge=BOTTOM).shift(UP * 0.1)
        a = Arrow(bar_code.get_code().get_lines(4),
                  bar_b.get_right(),
                  stroke_width=3)
        self.play(Write(bar_b), ShowCreationThenDestruction(a))
        bar_vars_extras.add(bar_b)
        self.play(bar_code.highlight_lines, 5)
        self.wait()

        xi = bar_code.pre_return(foo_code, 2)
        self.play(*bar_code.get_control_transfer_clockwise(xi), )
        foo_code.post_control_transfer(xi, self)
        self.wait()

        foo_n = TexMobject('6').move_to(foo_vars[0][1],
                                        aligned_edge=BOTTOM).shift(UP * 0.1)
        a = Arrow(foo_code.get_code().get_lines(2),
                  foo_n.get_right(),
                  stroke_width=3)
        self.play(
            foo_code.highlight_lines,
            3,
            ReplacementTransform(foo_n_q, foo_n),
            ShowCreationThenDestruction(a),
        )
        foo_vars.add(foo_n)
        self.wait()

        # - Now give the variables "homes" in each function. Variables like
        # homes; they're warm and safe and sized just for them!
        print('Give variables homes')

        t1 = TextMobject('So how does the\\\\computer do this?').to_edge(LEFT)
        self.play(FadeIn(t1), foo_code.remove_highlight)
        self.wait(duration=2)

        t2 = TextMobject(
            'Every variable is stored\\\\someplace in memory').to_edge(LEFT)
        new_title = TextMobject('Where are variables stored?').to_edge(UP)
        self.play(
            FadeOut(t1),
            FadeOut(title),
            FadeIn(new_title),
            FadeIn(t2),
        )
        title = new_title
        self.wait()

        em = TextMobject('M')
        slot_height = em.get_height() + SMALL_BUFF * 2
        slot_width = em.get_width() * 4 + SMALL_BUFF * 2

        def build_var_home(name, value):
            var_name = TextMobject(name)
            slot_box = Rectangle(height=slot_height,
                                 width=slot_width,
                                 stroke_width=1)
            var_value = TextMobject(value)
            var_value.move_to(slot_box)
            var_name.next_to(slot_box, LEFT)
            stack_slot = VGroup(slot_box, var_name, var_value)
            return stack_slot

        foo_homes = build_var_home('n', '6')
        foo_homes.move_to(foo_vars, aligned_edge=LEFT)
        self.play(ReplacementTransform(foo_vars, foo_homes))
        self.wait()

        bar_var_vals = [('x', '1'), ('y', '2'), ('a', '3'), ('b', '6')]
        bar_homes = VGroup()
        bar_homes_shift = 0
        for n, v in bar_var_vals:
            bar_homes.add(build_var_home(n, v).shift(DOWN * bar_homes_shift))
            bar_homes_shift += 0.75
        bar_homes.move_to(bar_vars, aligned_edge=LEFT)
        t3 = TextMobject("Each variable lives in a\\\\``slot''").to_edge(LEFT)
        self.play(
            ReplacementTransform(bar_vars, bar_homes),
            # FadeOut(bar_vars),
            FadeOut(bar_vars_extras),
            FadeIn(bar_homes),
        )
        self.wait()
        self.play(
            FadeOut(t2),
            FadeIn(t3),
        )
        self.wait()

        # - Now arrange the homes into a "stack frame".
        print('Build stack frames')

        t4 = TextMobject(
            "All slots for a function\\\\are put together in a\\\\``frame''")
        t4.to_edge(LEFT)
        self.play(FadeOut(t3), FadeIn(t4))

        frame_width = 2.8
        foo_vars = [('n', '6')]
        foo_frame = StackFrame(foo_code,
                               'foo()',
                               3,
                               foo_vars,
                               width=frame_width)
        foo_frame.move_to(foo_homes, aligned_edge=LEFT).shift(LEFT * .5)
        self.play(ReplacementTransform(foo_homes, foo_frame))
        self.wait()

        bar_frame = StackFrame(bar_code,
                               'bar(1,2)',
                               9,
                               bar_var_vals,
                               width=frame_width)
        bar_frame.move_to(bar_homes, aligned_edge=LEFT).shift(LEFT * .5)
        self.play(ReplacementTransform(bar_homes, bar_frame))
        self.wait()

        t5 = TextMobject("\\textit{They're happy and warm\\\\together!}")\
            .scale(0.75).next_to(t4, DOWN, buff=LARGE_BUFF)
        self.play(FadeIn(t5))
        self.wait()

        # Cool, so where do frames live?
        print('Where do frames live')
        t6 = TextMobject('So where do frames live?').to_edge(LEFT)
        self.play(
            FadeOut(t5),
            FadeOut(t4),
            FadeIn(t6),
        )
        self.wait()

        t7 = TextMobject("On the ``call stack''!").next_to(t6,
                                                           DOWN,
                                                           buff=LARGE_BUFF)
        self.play(FadeIn(t7))
        self.wait()

        new_title = TextMobject('The Call Stack').to_edge(UP)
        t1 = TextMobject('Function calls push a new frame\\\\onto the stack')\
            .to_edge(LEFT).shift(UP)
        t2 = TextMobject('Returning pops a frame off\\\\the stack').next_to(
            t1, DOWN, buff=LARGE_BUFF)

        box_count = 8
        colors = color_gradient([BLUE, ORANGE], box_count)
        little_boxes = VGroup(*[
            Rectangle(height=0.25, width=0.75, fill_opacity=1, color=colors[i])
            for i in range(box_count)
        ])
        little_boxes.arrange(UP, buff=0.1)
        little_boxes.next_to(VGroup(foo_code, bar_code), LEFT, buff=LARGE_BUFF)

        self.play(
            ReplacementTransform(title, new_title),
            FadeOut(t6),
            FadeOut(t7),
            FadeOut(foo_frame),
            FadeOut(bar_frame),
            FadeIn(t1),
            LaggedStartMap(FadeInFrom,
                           little_boxes,
                           lambda m: (m, UP),
                           lag_ratio=1.0,
                           run_time=4.0),
        )
        title = new_title
        self.wait()

        self.play(
            FadeIn(t2),
            LaggedStartMap(FadeOutAndShift,
                           VGroup(*reversed(little_boxes)),
                           lambda m: (m, UP),
                           lag_ratio=1.0,
                           run_time=4.0),
        )
        self.wait(duration=1)

        t3 = TextMobject(
            "Let's run again and see\\\\the call stack in action...").to_edge(
                LEFT)
        t3.shift(UP)
        self.play(FadeOut(t1), FadeOut(t2), FadeIn(t3))
        self.wait()

        print('Run with real call stack')

        # Let's also put main() into the picture. Start it off-frame upper right
        t4 = TextMobject("... and get main()\\\\into the picture.")\
            .next_to(t3, DOWN, buff=LARGE_BUFF)
        main_code.next_to(title, DOWN, buff=MED_SMALL_BUFF).to_edge(RIGHT)
        main_code.shift(UP * 3 + RIGHT * 3)
        g = VGroup(main_code, foo_code, bar_code)
        self.play(
            g.arrange,
            DOWN,
            {
                'aligned_edge': LEFT,
                'buff': MED_SMALL_BUFF
            },
            g.next_to,
            title,
            DOWN,
            {'buff': MED_SMALL_BUFF},
            g.to_edge,
            RIGHT,
            FadeInFromDown(t4),
        )
        self.wait(duration=2)

        t1 = TextMobject('Start in main()...')
        t1.next_to(title, DOWN, buff=LARGE_BUFF).to_edge(LEFT)
        frame_width = 3.0
        args_ref = TextMobject('[ ]').scale(0.5)
        main_frame = StackFrame(main_code,
                                'main()',
                                3, [('args', args_ref)],
                                width=frame_width)
        main_frame.next_to(g, LEFT, buff=LARGE_BUFF).to_edge(DOWN)
        main_code.move_hidden_highlight(3)
        self.play(FadeIn(t1), FadeInFromDown(main_frame),
                  main_code.highlight_lines, 3, FadeOut(t3), FadeOut(t4))
        self.wait()

        foo_frame = StackFrame(foo_code, 'foo()', 5, ['n'], width=frame_width)
        foo_frame.next_to(main_frame, UP, buff=SMALL_BUFF)
        b1 = BraceLabel(foo_frame,
                        'Calling foo()\\\\pushes a frame',
                        brace_direction=LEFT,
                        label_constructor=TextMobject)

        xi = main_code.pre_call(foo_code, 1)
        self.play(
            *main_code.get_control_transfer_counterclockwise(xi),
            FadeInFrom(foo_frame, UP),
            FadeInFrom(b1, UP),
            FadeOut(t1),
        )
        foo_code.post_control_transfer(xi, self)
        self.wait(duration=2)

        self.play(foo_code.highlight_lines, 2, foo_frame.set_line, 6,
                  FadeOut(b1))
        self.wait()

        bar_frame = StackFrame(bar_code,
                               'bar(1, 2)',
                               10, ['x', 'y', 'a', 'b'],
                               width=frame_width)
        bar_frame.next_to(foo_frame, UP, buff=SMALL_BUFF)
        b1 = BraceLabel(bar_frame,
                        'Calling bar()\\\\pushes a frame',
                        brace_direction=LEFT,
                        label_constructor=TextMobject)

        xi = foo_code.pre_call(bar_code, (1, 3))
        self.play(
            *foo_code.get_control_transfer_counterclockwise(xi),
            FadeInFrom(bar_frame, UP),
            FadeInFrom(b1, UP),
        )
        bar_code.post_control_transfer(xi, self)
        self.wait()

        self.play(bar_frame.update_slot, 'x', 1, bar_frame.update_slot, 'y', 2,
                  FadeOut(b1))
        self.play(bar_code.highlight_lines, 3, bar_frame.set_line, 11)
        self.wait()

        self.play(bar_frame.update_slot, 'a', 3)
        self.play(bar_code.highlight_lines, 4, bar_frame.set_line, 12)
        self.wait()

        self.play(bar_frame.update_slot, 'b', 6)
        self.play(bar_code.highlight_lines, 5, bar_frame.set_line, 13)
        b1 = BraceLabel(bar_frame,
                        "Returning pops\\\\bar's frame",
                        brace_direction=LEFT,
                        label_constructor=TextMobject)
        self.play(FadeIn(b1))
        self.wait(duration=2)

        xi = bar_code.pre_return(foo_code, 2)
        self.play(
            *bar_code.get_control_transfer_clockwise(xi),
            Uncreate(bar_frame),
            FadeOut(b1),
        )
        foo_code.post_control_transfer(xi, self)
        self.wait()

        self.play(foo_code.highlight_lines, 3, foo_frame.set_line, 7,
                  foo_frame.update_slot, 'n', 6)
        b1 = BraceLabel(foo_frame,
                        "Returning pops\\\\foo's frame",
                        brace_direction=LEFT,
                        label_constructor=TextMobject)
        self.play(FadeIn(b1))
        self.wait()

        xi = foo_code.pre_return(main_code, 3)
        self.play(
            *foo_code.get_control_transfer_clockwise(xi),
            Uncreate(foo_frame),
            FadeOut(b1),
        )
        main_code.post_control_transfer(xi, self)
        self.wait()

        self.play(main_code.highlight_lines, 4, main_frame.set_line, 4)
        self.wait()

        t1 = TextMobject(
            'And when main() returns\\\\the program ends').to_edge(LEFT)
        self.play(FadeIn(t1))
        self.wait()

        off_screen_code = CodeBlock('Java', 'off_screen')
        off_screen_code.shift(UP * 8)  # Offscreen up
        self.add(off_screen_code)

        xi = main_code.pre_return(off_screen_code, 1)
        self.play(
            *main_code.get_control_transfer_clockwise(xi),
            Uncreate(main_frame),
            FadeOut(t1),
        )
        off_screen_code.post_control_transfer(xi, self)
        self.wait()

        t1 = TextMobject("Alright, let's do a more complicated one!")
        self.play(
            FadeInFromDown(t1),
            FadeOutAndShift(g, RIGHT),
        )
        self.wait()