from big_ol_pile_of_manim_imports import * from functools import reduce # revert_to_original_skipping_status def chi_func(n): if n%2 == 0: return 0 if n%4 == 1: return 1 else: return -1 class LatticePointScene(Scene): CONFIG = { "y_radius" : 6, "x_radius" : None, "plane_center" : ORIGIN, "max_lattice_point_radius" : 6, "dot_radius" : 0.075, "secondary_line_ratio" : 0, "plane_color" : BLUE_E, "dot_color" : YELLOW, "dot_drawing_stroke_color" : PINK, "circle_color" : MAROON_D, "radial_line_color" : RED, } def setup(self): if self.x_radius is None: self.x_radius = self.y_radius*FRAME_X_RADIUS/FRAME_Y_RADIUS plane = ComplexPlane( y_radius = self.y_radius, x_radius = self.x_radius, secondary_line_ratio = self.secondary_line_ratio, radius = self.plane_color ) plane.set_height(FRAME_HEIGHT) plane.shift(self.plane_center) self.add(plane) self.plane = plane self.setup_lattice_points() def setup_lattice_points(self): M = self.max_lattice_point_radius int_range = list(range(-M, M+1)) self.lattice_points = VGroup() for x, y in it.product(*[int_range]*2): r_squared = x**2 + y**2 if r_squared > M**2: continue dot = Dot( self.plane.coords_to_point(x, y), color = self.dot_color, radius = self.dot_radius, ) dot.r_squared = r_squared self.lattice_points.add(dot) self.lattice_points.sort( lambda p : get_norm(p - self.plane_center) ) def get_circle(self, radius = None, color = None): if radius is None: radius = self.max_lattice_point_radius if color is None: color = self.circle_color radius *= self.plane.get_space_unit_to_y_unit() circle = Circle( color = color, radius = radius, ) circle.move_to(self.plane.get_center()) return circle def get_radial_line_with_label(self, radius = None, color = None): if radius is None: radius = self.max_lattice_point_radius if color is None: color = self.radial_line_color radial_line = Line( self.plane_center, self.plane.coords_to_point(radius, 0), color = color ) r_squared = int(np.round(radius**2)) root_label = TexMobject("\\sqrt{%d}"%r_squared) root_label.add_background_rectangle() root_label.next_to(radial_line, UP, SMALL_BUFF) return radial_line, root_label def get_lattice_points_on_r_squared_circle(self, r_squared): points = VGroup(*[dot for dot in self.lattice_points if dot.r_squared == r_squared]) points.sort( lambda p : angle_of_vector(p-self.plane_center)%(2*np.pi) ) return points def draw_lattice_points(self, points = None, run_time = 4): if points is None: points = self.lattice_points self.play(*[ DrawBorderThenFill( dot, stroke_width = 4, stroke_color = self.dot_drawing_stroke_color, run_time = run_time, rate_func = squish_rate_func( double_smooth, a, a + 0.25 ), ) for dot, a in zip( points, np.linspace(0, 0.75, len(points)) ) ]) def add_axis_labels(self, spacing = 2): x_max = int(self.plane.point_to_coords(FRAME_X_RADIUS*RIGHT)[0]) y_max = int(self.plane.point_to_coords(FRAME_Y_RADIUS*UP)[1]) x_range = list(range(spacing, x_max, spacing)) y_range = list(range(spacing, y_max, spacing)) for r in x_range, y_range: r += [-n for n in r] tick = Line(ORIGIN, MED_SMALL_BUFF*UP) x_ticks = VGroup(*[ tick.copy().move_to(self.plane.coords_to_point(x, 0)) for x in x_range ]) tick.rotate(-np.pi/2) y_ticks = VGroup(*[ tick.copy().move_to(self.plane.coords_to_point(0, y)) for y in y_range ]) x_labels = VGroup(*[ TexMobject(str(x)) for x in x_range ]) y_labels = VGroup(*[ TexMobject(str(y) + "i") for y in y_range ]) for labels, ticks in (x_labels, x_ticks), (y_labels, y_ticks): labels.scale(0.6) for tex_mob, tick in zip(labels, ticks): tex_mob.add_background_rectangle() tex_mob.next_to( tick, tick.get_start() - tick.get_end(), SMALL_BUFF ) self.add(x_ticks, y_ticks, x_labels, y_labels) digest_locals(self, [ "x_ticks", "y_ticks", "x_labels", "y_labels", ]) def point_to_int_coords(self, point): x, y = self.plane.point_to_coords(point)[:2] return (int(np.round(x)), int(np.round(y))) def dot_to_int_coords(self, dot): return self.point_to_int_coords(dot.get_center()) ###### class Introduction(PiCreatureScene): def construct(self): self.introduce_three_objects() self.show_screen() def introduce_three_objects(self): primes = self.get_primes() primes.to_corner(UP+RIGHT) primes.shift(DOWN) plane = self.get_complex_numbers() plane.shift(2*LEFT) pi_group = self.get_pi_group() pi_group.next_to(primes, DOWN, buff = MED_LARGE_BUFF) pi_group.shift_onto_screen() morty = self.get_primary_pi_creature() video = VideoIcon() video.set_color(TEAL) video.next_to(morty.get_corner(UP+LEFT), UP) self.play( morty.change_mode, "raise_right_hand", DrawBorderThenFill(video) ) self.wait() self.play( Write(primes, run_time = 2), morty.change_mode, "happy", video.set_height, FRAME_WIDTH, video.center, video.set_fill, None, 0 ) self.wait() self.play( Write(plane, run_time = 2), morty.change, "raise_right_hand" ) self.wait() self.remove(morty) morty = morty.copy() self.add(morty) self.play( ReplacementTransform( morty.body, pi_group.get_part_by_tex("pi"), run_time = 1 ), FadeOut(VGroup(morty.eyes, morty.mouth)), Write(VGroup(*pi_group[1:])) ) self.wait(2) self.play( plane.set_width, pi_group.get_width(), plane.next_to, pi_group, DOWN, MED_LARGE_BUFF ) def show_screen(self): screen = ScreenRectangle(height = 4.3) screen.to_edge(LEFT) titles = VGroup( TextMobject("From zeta video"), TextMobject("Coming up") ) for title in titles: title.next_to(screen, UP) title.set_color(YELLOW) self.play( ShowCreation(screen), FadeIn(titles[0]) ) self.show_frame() self.wait(2) self.play(Transform(*titles)) self.wait(3) def get_primes(self): return TexMobject("2, 3, 5, 7, 11, 13, \\dots") def get_complex_numbers(self): plane = ComplexPlane( x_radius = 3, y_radius = 2.5, ) plane.add_coordinates() point = plane.number_to_point(complex(1, 2)) dot = Dot(point, radius = YELLOW) label = TexMobject("1 + 2i") label.add_background_rectangle() label.next_to(dot, UP+RIGHT, buff = SMALL_BUFF) label.set_color(YELLOW) plane.label = label plane.add(dot, label) return plane def get_pi_group(self): result = TexMobject("\\pi", "=", "%.8f\\dots"%np.pi) pi = result.get_part_by_tex("pi") pi.scale(2, about_point = pi.get_right()) pi.set_color(MAROON_B) return result class ShowSum(TeacherStudentsScene): CONFIG = { "num_terms_to_add" : 40, } def construct(self): self.say_words() self.show_sum() def say_words(self): self.teacher_says("This won't be easy") self.change_student_modes( "hooray", "sassy", "angry" ) self.wait(2) def show_sum(self): line = UnitInterval() line.add_numbers(0, 1) # line.shift(UP) sum_point = line.number_to_point(np.pi/4) numbers = [0] + [ ((-1)**n)/(2.0*n + 1) for n in range(self.num_terms_to_add) ] partial_sums = np.cumsum(numbers) points = list(map(line.number_to_point, partial_sums)) arrows = [ Arrow( p1, p2, tip_length = 0.2*min(1, get_norm(p1-p2)), buff = 0 ) for p1, p2 in zip(points, points[1:]) ] dot = Dot(points[0]) sum_mob = TexMobject( "1", "-\\frac{1}{3}", "+\\frac{1}{5}", "-\\frac{1}{7}", "+\\frac{1}{9}", "-\\frac{1}{11}", "+\\cdots" ) sum_mob.to_corner(UP+RIGHT) lhs = TexMobject( "\\frac{\\pi}{4}", "=", ) lhs.next_to(sum_mob, LEFT) lhs.set_color_by_tex("pi", YELLOW) sum_arrow = Arrow( lhs.get_part_by_tex("pi").get_bottom(), sum_point ) fading_terms = [ TexMobject(sign + "\\frac{1}{%d}"%(2*n + 1)) for n, sign in zip( list(range(self.num_terms_to_add)), it.cycle("+-") ) ] for fading_term, arrow in zip(fading_terms, arrows): fading_term.next_to(arrow, UP) terms = it.chain(sum_mob, it.repeat(None)) last_arrows = it.chain([None], arrows) last_fading_terms = it.chain([None], fading_terms) self.change_student_modes( *["pondering"]*3, look_at_arg = line, added_anims = [ FadeIn(VGroup(line, dot)), FadeIn(lhs), RemovePiCreatureBubble( self.teacher, target_mode = "raise_right_hand" ) ] ) run_time = 1 for term, arrow, last_arrow, fading_term, last_fading_term in zip( terms, arrows, last_arrows, fading_terms, last_fading_terms ): anims = [] if term: anims.append(Write(term)) if last_arrow: anims.append(FadeOut(last_arrow)) if last_fading_term: anims.append(FadeOut(last_fading_term)) dot_movement = ApplyMethod(dot.move_to, arrow.get_end()) anims.append(ShowCreation(arrow)) anims.append(dot_movement) anims.append(FadeIn(fading_term)) self.play(*anims, run_time = run_time) if term: self.wait() else: run_time *= 0.8 self.play( FadeOut(arrow), FadeOut(fading_term), dot.move_to, sum_point ) self.play(ShowCreation(sum_arrow)) self.wait() self.change_student_modes("erm", "confused", "maybe") self.play(self.teacher.change_mode, "happy") self.wait(2) class FermatsDreamExcerptWrapper(Scene): def construct(self): words = TextMobject( "From ``Fermat's dream'' by Kato, Kurokawa and Saito" ) words.scale(0.8) words.to_edge(UP) self.add(words) self.wait() class ShowCalculus(PiCreatureScene): def construct(self): frac_sum = TexMobject( "1 - \\frac{1}{3} + \\frac{1}{5} - \\frac{1}{7} + \\cdots", ) int1 = TexMobject( "= \\int_0^1 (1 - x^2 + x^4 - \\dots )\\,dx" ) int2 = TexMobject( "= \\int_0^1 \\frac{1}{1+x^2}\\,dx" ) arctan = TexMobject("= \\tan^{-1}(1)") pi_fourths = TexMobject("= \\frac{\\pi}{4}") frac_sum.to_corner(UP+LEFT) frac_sum.shift(RIGHT) rhs_group = VGroup(int1, int2, arctan, pi_fourths) rhs_group.arrange( DOWN, buff = MED_LARGE_BUFF, aligned_edge = LEFT ) rhs_group.shift( frac_sum.get_right() + MED_SMALL_BUFF*RIGHT \ -int1[0].get_left() ) self.add(frac_sum) modes = it.chain(["plain"], it.cycle(["confused"])) for rhs, mode in zip(rhs_group, modes): self.play( FadeIn(rhs), self.pi_creature.change, mode ) self.wait() self.change_mode("maybe") self.wait() self.look_at(rhs_group[-1]) self.wait() self.pi_creature_says( "Where's the \\\\ circle?", bubble_kwargs = {"width" : 4, "height" : 3}, target_mode = "maybe" ) self.look_at(rhs_group[0]) self.wait() def create_pi_creature(self): return Randolph(color = BLUE_C).to_corner(DOWN+LEFT) class CertainRegularityInPrimes(LatticePointScene): CONFIG = { "y_radius" : 8, "x_radius" : 20, "max_lattice_point_radius" : 8, "plane_center" : 2.5*RIGHT, "primes" : [5, 13, 17, 29, 37, 41, 53], "include_pi_formula" : True, } def construct(self): if self.include_pi_formula: self.add_pi_formula() self.walk_through_primes() def add_pi_formula(self): formula = TexMobject( "\\frac{\\pi}{4}", "=", "1", "-", "\\frac{1}{3}", "+", "\\frac{1}{5}", "-", "\\frac{1}{7}", "+\\cdots" ) formula.set_color_by_tex("pi", YELLOW) formula.add_background_rectangle() formula.to_corner(UP+LEFT, buff = MED_SMALL_BUFF) self.add_foreground_mobject(formula) def walk_through_primes(self): primes = self.primes lines_and_labels = [ self.get_radial_line_with_label(np.sqrt(p)) for p in primes ] lines, labels = list(zip(*lines_and_labels)) circles = [ self.get_circle(np.sqrt(p)) for p in primes ] dots_list = [ self.get_lattice_points_on_r_squared_circle(p) for p in primes ] groups = [ VGroup(*mobs) for mobs in zip(lines, labels, circles, dots_list) ] curr_group = groups[0] self.play(Write(curr_group, run_time = 2)) self.wait() for group in groups[1:]: self.play(Transform(curr_group, group)) self.wait(2) class Outline(PiCreatureScene): def construct(self): self.generate_list() self.wonder_at_pi() self.count_lattice_points() self.write_steps_2_and_3() self.show_chi() self.show_complicated_formula() self.show_last_step() def generate_list(self): steps = VGroup( TextMobject("1. Count lattice points"), TexMobject("2. \\text{ Things like }17 = ", "4", "^2 + ", "1", "^2"), TexMobject("3. \\text{ Things like }17 = (", "4", " + ", "i", ")(", "4", " - ", "i", ")"), TextMobject("4. Introduce $\\chi$"), TextMobject("5. Shift perspective"), ) for step in steps[1:3]: step.set_color_by_tex("1", RED, substring = False) step.set_color_by_tex("i", RED, substring = False) step.set_color_by_tex("4", GREEN, substring = False) steps.arrange( DOWN, buff = MED_LARGE_BUFF, aligned_edge = LEFT ) steps.to_corner(UP+LEFT) self.steps = steps def wonder_at_pi(self): question = TexMobject("\\pi", "=???") pi = question.get_part_by_tex("pi") pi.scale(2, about_point = pi.get_right()) pi.set_color(YELLOW) question.next_to(self.pi_creature.body, LEFT, aligned_edge = UP) self.think( "Who am I really?", look_at_arg = question, added_anims = [ FadeIn(question) ] ) self.wait(2) self.play( RemovePiCreatureBubble(self.pi_creature), question.to_corner, UP+RIGHT ) self.question = question self.pi = question.get_part_by_tex("pi") def count_lattice_points(self): step = self.steps[0] plane = NumberPlane( x_radius = 10, y_radius = 10, secondary_line_ratio = 0, color = BLUE_E, ) plane.set_height(6) plane.next_to(step, DOWN) plane.to_edge(LEFT) circle = Circle( color = YELLOW, radius = get_norm( plane.coords_to_point(10, 0) - \ plane.coords_to_point(0, 0) ) ) plane_center = plane.coords_to_point(0, 0) circle.move_to(plane_center) lattice_points = VGroup(*[ Dot( plane.coords_to_point(a, b), radius = 0.05, color = PINK, ) for a in range(-10, 11) for b in range(-10, 11) if a**2 + b**2 <= 10**2 ]) lattice_points.sort( lambda p : get_norm(p - plane_center) ) lattice_group = VGroup(plane, circle, lattice_points) self.play(ShowCreation(circle)) self.play(Write(plane, run_time = 2), Animation(circle)) self.play( *[ DrawBorderThenFill( dot, stroke_width = 4, stroke_color = YELLOW, run_time = 4, rate_func = squish_rate_func( double_smooth, a, a + 0.25 ) ) for dot, a in zip( lattice_points, np.linspace(0, 0.75, len(lattice_points)) ) ] ) self.play( FadeIn(step) ) self.wait() self.play( lattice_group.set_height, 2.5, lattice_group.next_to, self.question, DOWN, lattice_group.to_edge, RIGHT ) def write_steps_2_and_3(self): for step in self.steps[1:3]: self.play(FadeIn(step)) self.wait(2) self.wait() def show_chi(self): input_range = list(range(1, 7)) chis = VGroup(*[ TexMobject("\\chi(%d)"%n) for n in input_range ]) chis.arrange(RIGHT, buff = LARGE_BUFF) chis.set_stroke(WHITE, width = 1) numerators = VGroup() arrows = VGroup() for chi, n in zip(chis, input_range): arrow = TexMobject("\\Downarrow") arrow.next_to(chi, DOWN, SMALL_BUFF) arrows.add(arrow) value = TexMobject(str(chi_func(n))) value.set_color_by_tex("1", BLUE) value.set_color_by_tex("-1", GREEN) value.next_to(arrow, DOWN) numerators.add(value) group = VGroup(chis, arrows, numerators) group.set_width(1.3*FRAME_X_RADIUS) group.to_corner(DOWN+LEFT) self.play(FadeIn(self.steps[3])) self.play(*[ FadeIn( mob, run_time = 3, lag_ratio = 0.5 ) for mob in [chis, arrows, numerators] ]) self.change_mode("pondering") self.wait() self.chis = chis self.arrows = arrows self.numerators = numerators def show_complicated_formula(self): rhs = TexMobject( " = \\lim_{N \\to \\infty}", " \\frac{4}{N}", "\\sum_{n = 1}^N", "\\sum_{d | n} \\chi(d)", ) pi = self.pi self.add(pi.copy()) pi.generate_target() pi.target.next_to(self.steps[3], RIGHT, MED_LARGE_BUFF) pi.target.shift(MED_LARGE_BUFF*DOWN) rhs.next_to(pi.target, RIGHT) self.play( MoveToTarget(pi), Write(rhs) ) self.change_mode("confused") self.wait(2) self.complicated_formula = rhs def show_last_step(self): expression = TexMobject( "=", "\\frac{\\quad}{1}", *it.chain(*[ ["+", "\\frac{\\quad}{%d}"%d] for d in range(2, len(self.numerators)+1) ] + [["+ \\cdots"]]) ) over_four = TexMobject("\\quad \\over 4") over_four.to_corner(DOWN+LEFT) over_four.shift(UP) pi = self.pi pi.generate_target() pi.target.scale(0.75) pi.target.next_to(over_four, UP) expression.next_to(over_four, RIGHT, align_using_submobjects = True) self.numerators.generate_target() for num, denom in zip(self.numerators.target, expression[1::2]): num.scale(1.2) num.next_to(denom, UP, MED_SMALL_BUFF) self.play( MoveToTarget(self.numerators), MoveToTarget(pi), Write(over_four), FadeOut(self.chis), FadeOut(self.arrows), FadeOut(self.complicated_formula), ) self.play( Write(expression), self.pi_creature.change_mode, "pondering" ) self.wait(3) ######## def create_pi_creature(self): return Randolph(color = BLUE_C).flip().to_corner(DOWN+RIGHT) class CountLatticePoints(LatticePointScene): CONFIG = { "y_radius" : 11, "max_lattice_point_radius" : 10, "dot_radius" : 0.05, "example_coords" : (7, 5), } def construct(self): self.introduce_lattice_point() self.draw_lattice_points_in_circle() self.turn_points_int_units_of_area() self.write_pi_R_squared() self.allude_to_alternate_counting_method() def introduce_lattice_point(self): x, y = self.example_coords example_dot = Dot( self.plane.coords_to_point(x, y), color = self.dot_color, radius = 1.5*self.dot_radius, ) label = TexMobject(str(self.example_coords)) label.add_background_rectangle() label.next_to(example_dot, UP+RIGHT, buff = 0) h_line = Line( ORIGIN, self.plane.coords_to_point(x, 0), color = GREEN ) v_line = Line( h_line.get_end(), self.plane.coords_to_point(x, y), color = RED ) lines = VGroup(h_line, v_line) dots = self.lattice_points.copy() random.shuffle(dots.submobjects) self.play(*[ ApplyMethod( dot.set_fill, None, 0, run_time = 3, rate_func = squish_rate_func( lambda t : 1 - there_and_back(t), a, a + 0.5 ), remover = True ) for dot, a in zip(dots, np.linspace(0, 0.5, len(dots))) ]) self.play( Write(label), ShowCreation(lines), DrawBorderThenFill(example_dot), run_time = 2, ) self.wait(2) self.play(*list(map(FadeOut, [label, lines, example_dot]))) def draw_lattice_points_in_circle(self): circle = self.get_circle() radius = Line(ORIGIN, circle.get_right()) radius.set_color(RED) brace = Brace(radius, DOWN, buff = SMALL_BUFF) radius_label = brace.get_text( str(self.max_lattice_point_radius), buff = SMALL_BUFF ) radius_label.add_background_rectangle() brace.add(radius_label) self.play( ShowCreation(circle), Rotating(radius, about_point = ORIGIN), run_time = 2, rate_func = smooth, ) self.play(FadeIn(brace)) self.add_foreground_mobject(brace) self.draw_lattice_points() self.wait() self.play(*list(map(FadeOut, [brace, radius]))) self.circle = circle def turn_points_int_units_of_area(self): square = Square(fill_opacity = 0.9) unit_line = Line( self.plane.coords_to_point(0, 0), self.plane.coords_to_point(1, 0), ) square.set_width(unit_line.get_width()) squares = VGroup(*[ square.copy().move_to(point) for point in self.lattice_points ]) squares.set_color_by_gradient(BLUE_E, GREEN_E) squares.set_stroke(WHITE, 1) point_copies = self.lattice_points.copy() self.play( ReplacementTransform( point_copies, squares, run_time = 3, lag_ratio = 0.5, lag_factor = 4, ), Animation(self.lattice_points) ) self.wait() self.play(FadeOut(squares), Animation(self.lattice_points)) def write_pi_R_squared(self): equations = VGroup(*[ VGroup( TextMobject( "\\# Lattice points\\\\", "within radius ", R, alignment = "" ), TexMobject( "\\approx \\pi", "(", R, ")^2" ) ).arrange(RIGHT) for R in ("10", "1{,}000{,}000", "R") ]) radius_10_eq, radius_million_eq, radius_R_eq = equations for eq in equations: for tex_mob in eq: tex_mob.set_color_by_tex("0", BLUE) radius_10_eq.to_corner(UP+LEFT) radius_million_eq.next_to(radius_10_eq, DOWN, LARGE_BUFF) radius_million_eq.to_edge(LEFT) brace = Brace(radius_million_eq, DOWN) brace.add(brace.get_text("More accurate")) brace.set_color(YELLOW) background = FullScreenFadeRectangle(opacity = 0.9) self.play( FadeIn(background), Write(radius_10_eq) ) self.wait(2) self.play(ReplacementTransform( radius_10_eq.copy(), radius_million_eq )) self.play(FadeIn(brace)) self.wait(3) self.radius_10_eq = radius_10_eq self.million_group = VGroup(radius_million_eq, brace) self.radius_R_eq = radius_R_eq def allude_to_alternate_counting_method(self): alt_count = TextMobject( "(...something else...)", "$R^2$", "=", arg_separator = "" ) alt_count.to_corner(UP+LEFT) alt_count.set_color_by_tex("something", MAROON_B) self.radius_R_eq.next_to(alt_count, RIGHT) final_group = VGroup( alt_count.get_part_by_tex("something"), self.radius_R_eq[1].get_part_by_tex("pi"), ).copy() self.play( FadeOut(self.million_group), Write(alt_count), ReplacementTransform( self.radius_10_eq, self.radius_R_eq ) ) self.wait(2) self.play( final_group.arrange, RIGHT, final_group.next_to, ORIGIN, UP ) rect = BackgroundRectangle(final_group) self.play(FadeIn(rect), Animation(final_group)) self.wait(2) class SoYouPlay(TeacherStudentsScene): def construct(self): self.teacher_says( "So you play!", run_time = 2 ) self.change_student_modes("happy", "thinking", "hesitant") self.wait() self.look_at(Dot().to_corner(UP+LEFT)) self.wait(3) class CountThroughRings(LatticePointScene): CONFIG = { "example_coords" : (3, 2), "num_rings_to_show_explicitly" : 7, "x_radius" : 15, "plane_center" : 2*RIGHT, "max_lattice_point_radius" : 5, } def construct(self): self.add_lattice_points() self.preview_rings() self.isolate_single_ring() self.show_specific_lattice_point_distance() self.count_through_rings() def add_lattice_points(self): big_circle = self.get_circle() self.add(big_circle) self.add(self.lattice_points) self.big_circle = big_circle def preview_rings(self): radii = list(set([ np.sqrt(p.r_squared) for p in self.lattice_points ])) radii.sort() circles = VGroup(*[ self.get_circle(radius = r) for r in radii ]) circles.set_stroke(width = 2) self.add_foreground_mobject(self.lattice_points) self.play(FadeIn(circles)) self.play(OldLaggedStart( ApplyMethod, circles, arg_creator = lambda m : (m.set_stroke, PINK, 4), rate_func = there_and_back, )) self.wait() self.remove_foreground_mobject(self.lattice_points) digest_locals(self, ["circles", "radii"]) def isolate_single_ring(self): x, y = self.example_coords example_circle = self.circles[ self.radii.index(np.sqrt(x**2 + y**2)) ] self.circles.remove(example_circle) points_on_example_circle = self.get_lattice_points_on_r_squared_circle( x**2 + y**2 ).copy() self.play( FadeOut(self.circles), self.lattice_points.set_fill, GREY, 0.5, Animation(points_on_example_circle) ) self.wait() digest_locals(self, ["points_on_example_circle", "example_circle"]) def show_specific_lattice_point_distance(self): x, y = self.example_coords dot = Dot( self.plane.coords_to_point(x, y), color = self.dot_color, radius = self.dot_radius ) label = TexMobject("(a, b)") num_label = TexMobject(str(self.example_coords)) for mob in label, num_label: mob.add_background_rectangle() mob.next_to(dot, UP + RIGHT, SMALL_BUFF) a, b = label[1][1].copy(), label[1][3].copy() x_spot = self.plane.coords_to_point(x, 0) radial_line = Line(self.plane_center, dot) h_line = Line(self.plane_center, x_spot) h_line.set_color(GREEN) v_line = Line(x_spot, dot) v_line.set_color(RED) distance = TexMobject("\\sqrt{a^2 + b^2}") distance_num = TexMobject("\\sqrt{%d}"%(x**2 + y**2)) for mob in distance, distance_num: mob.scale(0.75) mob.add_background_rectangle() mob.next_to(radial_line.get_center(), UP, SMALL_BUFF) mob.rotate( radial_line.get_angle(), about_point = mob.get_bottom() ) self.play(Write(label)) self.play( ApplyMethod(a.next_to, h_line, DOWN, SMALL_BUFF), ApplyMethod(b.next_to, v_line, RIGHT, SMALL_BUFF), ShowCreation(h_line), ShowCreation(v_line), ) self.play(ShowCreation(radial_line)) self.play(Write(distance)) self.wait(2) a_num, b_num = [ TexMobject(str(coord))[0] for coord in self.example_coords ] a_num.move_to(a, UP) b_num.move_to(b, LEFT) self.play( Transform(label, num_label), Transform(a, a_num), Transform(b, b_num), ) self.wait() self.play(Transform(distance, distance_num)) self.wait(3) self.play(*list(map(FadeOut, [ self.example_circle, self.points_on_example_circle, distance, a, b, radial_line, h_line, v_line, label ]))) def count_through_rings(self): counts = [ len(self.get_lattice_points_on_r_squared_circle(N)) for N in range(self.max_lattice_point_radius**2 + 1) ] left_list = VGroup(*[ TexMobject( "\\sqrt{%d} \\Rightarrow"%n, str(count) ) for n, count in zip( list(range(self.num_rings_to_show_explicitly)), counts ) ]) left_counts = VGroup() left_roots = VGroup() for mob in left_list: mob[1].set_color(YELLOW) left_counts.add(VGroup(mob[1])) mob.add_background_rectangle() left_roots.add(VGroup(mob[0], mob[1][0])) left_list.arrange( DOWN, buff = MED_LARGE_BUFF, aligned_edge = LEFT, ) left_list.to_corner(UP + LEFT) top_list = VGroup(*[ TexMobject("%d, "%count) for count in counts ]) top_list.set_color(YELLOW) top_list.arrange(RIGHT, aligned_edge = DOWN) top_list.set_width(FRAME_WIDTH - MED_LARGE_BUFF) top_list.to_edge(UP, buff = SMALL_BUFF) top_rect = BackgroundRectangle(top_list) for r_squared, count_mob, root in zip(it.count(), left_counts, left_roots): self.show_ring_count( r_squared, count_mob, added_anims = [FadeIn(root)] ) self.wait(2) self.play( FadeOut(left_roots), FadeIn(top_rect), *[ ReplacementTransform( lc, VGroup(tc), path_arc = np.pi/2 ) for lc, tc in zip(left_counts, top_list) ] ) for r_squared in range(len(left_counts), self.max_lattice_point_radius**2 + 1): self.show_ring_count( r_squared, top_list[r_squared], ) self.wait(3) def show_ring_count( self, radius_squared, target, added_anims = None, run_time = 1 ): added_anims = added_anims or [] radius = np.sqrt(radius_squared) points = self.get_lattice_points_on_r_squared_circle(radius_squared) points.save_state() circle = self.get_circle(radius) radial_line = Line( self.plane_center, self.plane.coords_to_point(radius, 0), color = RED ) root = TexMobject("\\sqrt{%d}"%radius_squared) root.add_background_rectangle() root.set_width( min(0.7*radial_line.get_width(), root.get_width()) ) root.next_to(radial_line, DOWN, SMALL_BUFF) if not hasattr(self, "little_circle"): self.little_circle = circle if not hasattr(self, "radial_line"): self.radial_line = radial_line if not hasattr(self, "root"): self.root = root if hasattr(self, "last_points"): added_anims += [self.last_points.restore] self.last_points = points if radius_squared == 0: points.set_fill(YELLOW, 1) self.play( DrawBorderThenFill(points, stroke_color = PINK), *added_anims, run_time = run_time ) self.play(ReplacementTransform( points.copy(), target )) return points.set_fill(YELLOW, 1) self.play( Transform(self.little_circle, circle), Transform(self.radial_line, radial_line), Transform(self.root, root), DrawBorderThenFill( points, stroke_width = 4, stroke_color = PINK, ), *added_anims, run_time = run_time ) self.wait(run_time) if len(points) > 0: mover = points.copy() else: mover = VectorizedPoint(self.plane_center) self.play(ReplacementTransform(mover, target, run_time = run_time)) class LookAtExampleRing(LatticePointScene): CONFIG = { "dot_radius" : 0.1, "plane_center" : 2*LEFT, "x_radius" : 17, "y_radius" : 7, } def construct(self): self.analyze_25() self.analyze_11() def analyze_25(self): x_color = GREEN y_color = RED circle = self.get_circle(radius = 5) points = self.get_lattice_points_on_r_squared_circle(25) radius, root_label = self.get_radial_line_with_label(5) coords_list = [(5, 0), (4, 3), (3, 4), (0, 5), (-3, 4), (-4, 3)] labels = [ TexMobject("(", str(x), ",", str(y), ")") for x, y in coords_list ] for label in labels: label.x = label[1] label.y = label[3] label.x.set_color(x_color) label.y.set_color(y_color) label.add_background_rectangle() for label, point in zip(labels, points): x_coord = (point.get_center() - self.plane_center)[0] vect = UP+RIGHT if x_coord >= 0 else UP+LEFT label.next_to(point, vect, SMALL_BUFF) label.point = point def special_str(n): return "(%d)"%n if n < 0 else str(n) sums_of_squares = [ TexMobject( special_str(x), "^2", "+", special_str(y), "^2", "= 25" ) for x, y in coords_list ] for tex_mob in sums_of_squares: tex_mob.x = tex_mob[0] tex_mob.y = tex_mob[3] tex_mob.x.set_color(x_color) tex_mob.y.set_color(y_color) tex_mob.add_background_rectangle() tex_mob.to_corner(UP+RIGHT) self.play( ShowCreation(radius), Write(root_label) ) self.play( ShowCreation(circle), Rotating( radius, about_point = self.plane_center, rate_func = smooth, ), FadeIn(points, lag_ratio = 0.5), run_time = 2, ) self.wait() curr_label = labels[0] curr_sum_of_squares = sums_of_squares[0] self.play( Write(curr_label), curr_label.point.set_color, PINK ) x, y = curr_label.x.copy(), curr_label.y.copy() self.play( Transform(x, curr_sum_of_squares.x), Transform(y, curr_sum_of_squares.y), ) self.play( Write(curr_sum_of_squares), Animation(VGroup(x, y)) ) self.remove(x, y) self.wait() for label, sum_of_squares in zip(labels, sums_of_squares)[1:]: self.play( ReplacementTransform(curr_label, label), label.point.set_color, PINK, curr_label.point.set_color, self.dot_color ) curr_label = label self.play( ReplacementTransform( curr_sum_of_squares, sum_of_squares ) ) curr_sum_of_squares = sum_of_squares self.wait() points.save_state() points.generate_target() for i, point in enumerate(points.target): point.move_to( self.plane.coords_to_point(i%3, i//3) ) points.target.next_to(circle, RIGHT) self.play(MoveToTarget( points, run_time = 2, )) self.wait() self.play(points.restore, run_time = 2) self.wait() self.play(*list(map(FadeOut, [ curr_label, curr_sum_of_squares, circle, points, radius, root_label ]))) def analyze_11(self): R = np.sqrt(11) circle = self.get_circle(radius = R) radius, root_label = self.get_radial_line_with_label(R) equation = TexMobject("11 \\ne ", "a", "^2", "+", "b", "^2") equation.set_color_by_tex("a", GREEN) equation.set_color_by_tex("b", RED) equation.add_background_rectangle() equation.to_corner(UP+RIGHT) self.play( Write(root_label), ShowCreation(radius), run_time = 1 ) self.play( ShowCreation(circle), Rotating( radius, about_point = self.plane_center, rate_func = smooth, ), run_time = 2, ) self.wait() self.play(Write(equation)) self.wait(3) class Given2DThinkComplex(TeacherStudentsScene): def construct(self): tex = TextMobject("2D $\\Leftrightarrow$ Complex numbers") plane = ComplexPlane( x_radius = 0.6*FRAME_X_RADIUS, y_radius = 0.6*FRAME_Y_RADIUS, ) plane.add_coordinates() plane.set_height(FRAME_Y_RADIUS) plane.to_corner(UP+LEFT) self.teacher_says(tex) self.change_student_modes("pondering", "confused", "erm") self.wait() self.play( Write(plane), RemovePiCreatureBubble( self.teacher, target_mode = "raise_right_hand" ) ) self.change_student_modes( *["thinking"]*3, look_at_arg = plane ) self.wait(3) class IntroduceComplexConjugate(LatticePointScene): CONFIG = { "y_radius" : 20, "x_radius" : 30, "plane_scale_factor" : 1.7, "plane_center" : 2*LEFT, "example_coords" : (3, 4), "x_color" : GREEN, "y_color" : RED, } def construct(self): self.resize_plane() self.write_points_with_complex_coords() self.introduce_complex_conjugate() self.show_confusion() self.expand_algebraically() self.discuss_geometry() self.show_geometrically() def resize_plane(self): self.plane.scale( self.plane_scale_factor, about_point = self.plane_center ) self.plane.set_stroke(width = 1) self.plane.axes.set_stroke(width = 3) def write_points_with_complex_coords(self): x, y = self.example_coords x_color = self.x_color y_color = self.y_color point = self.plane.coords_to_point(x, y) dot = Dot(point, color = self.dot_color) x_point = self.plane.coords_to_point(x, 0) h_arrow = Arrow(self.plane_center, x_point, buff = 0) v_arrow = Arrow(x_point, point, buff = 0) h_arrow.set_color(x_color) v_arrow.set_color(y_color) x_coord = TexMobject(str(x)) x_coord.next_to(h_arrow, DOWN, SMALL_BUFF) x_coord.set_color(x_color) x_coord.add_background_rectangle() y_coord = TexMobject(str(y)) imag_y_coord = TexMobject(str(y) + "i") for coord in y_coord, imag_y_coord: coord.next_to(v_arrow, RIGHT, SMALL_BUFF) coord.set_color(y_color) coord.add_background_rectangle() tuple_label = TexMobject(str((x, y))) tuple_label[1].set_color(x_color) tuple_label[3].set_color(y_color) complex_label = TexMobject("%d+%di"%(x, y)) complex_label[0].set_color(x_color) complex_label[2].set_color(y_color) for label in tuple_label, complex_label: label.add_background_rectangle() label.next_to(dot, UP+RIGHT, buff = 0) y_range = list(range(-9, 10, 3)) ticks = VGroup(*[ Line( ORIGIN, MED_SMALL_BUFF*RIGHT ).move_to(self.plane.coords_to_point(0, y)) for y in y_range ]) imag_coords = VGroup() for y, tick in zip(y_range, ticks): if y == 0: continue if y == 1: tex = "i" elif y == -1: tex = "-i" else: tex = "%di"%y imag_coord = TexMobject(tex) imag_coord.scale(0.75) imag_coord.add_background_rectangle() imag_coord.next_to(tick, LEFT, SMALL_BUFF) imag_coords.add(imag_coord) self.add(dot) self.play( ShowCreation(h_arrow), Write(x_coord) ) self.play( ShowCreation(v_arrow), Write(y_coord) ) self.play(FadeIn(tuple_label)) self.wait() self.play(*list(map(FadeOut, [tuple_label, y_coord]))) self.play(*list(map(FadeIn, [complex_label, imag_y_coord]))) self.play(*list(map(Write, [imag_coords, ticks]))) self.wait() self.play(*list(map(FadeOut, [ v_arrow, h_arrow, x_coord, imag_y_coord, ]))) self.complex_label = complex_label self.example_dot = dot def introduce_complex_conjugate(self): x, y = self.example_coords equation = VGroup( TexMobject("25 = ", str(x), "^2", "+", str(y), "^2", "="), TexMobject("(", str(x), "+", str(y), "i", ")"), TexMobject("(", str(x), "-", str(y), "i", ")"), ) equation.arrange( RIGHT, buff = SMALL_BUFF, ) VGroup(*equation[-2:]).shift(0.5*SMALL_BUFF*DOWN) equation.scale(0.9) equation.to_corner(UP+RIGHT, buff = MED_SMALL_BUFF) equation.shift(MED_LARGE_BUFF*DOWN) for tex_mob in equation: tex_mob.set_color_by_tex(str(x), self.x_color) tex_mob.set_color_by_tex(str(y), self.y_color) tex_mob.add_background_rectangle() dot = Dot( self.plane.coords_to_point(x, -y), color = self.dot_color ) label = TexMobject("%d-%di"%(x, y)) label[0].set_color(self.x_color) label[2].set_color(self.y_color) label.add_background_rectangle() label.next_to(dot, DOWN+RIGHT, buff = 0) brace = Brace(equation[-1], DOWN) conjugate_words = TextMobject("Complex \\\\ conjugate") conjugate_words.scale(0.8) conjugate_words.add_background_rectangle() conjugate_words.next_to(brace, DOWN) self.play(FadeIn( equation, run_time = 3, lag_ratio = 0.5 )) self.wait(2) self.play( GrowFromCenter(brace), Write(conjugate_words, run_time = 2) ) self.wait() self.play(*[ ReplacementTransform(m1.copy(), m2) for m1, m2 in [ (self.example_dot, dot), (self.complex_label, label), ] ]) self.wait(2) self.conjugate_label = VGroup(brace, conjugate_words) self.equation = equation self.conjugate_dot = dot def show_confusion(self): randy = Randolph(color = BLUE_C).to_corner(DOWN+LEFT) morty = Mortimer().to_edge(DOWN) randy.make_eye_contact(morty) self.play(*list(map(FadeIn, [randy, morty]))) self.play(PiCreatureSays( randy, "Wait \\dots why?", target_mode = "confused", )) self.play(Blink(randy)) self.wait(2) self.play( RemovePiCreatureBubble( randy, target_mode = "erm", ), PiCreatureSays( morty, "Now it's a \\\\ factoring problem!", target_mode = "hooray", bubble_kwargs = {"width" : 5, "height" : 3} ) ) self.play( morty.look_at, self.equation, randy.look_at, self.equation, ) self.play(Blink(morty)) self.play(randy.change_mode, "pondering") self.play(RemovePiCreatureBubble(morty)) self.play(*list(map(FadeOut, [randy, morty]))) def expand_algebraically(self): x, y = self.example_coords expansion = VGroup( TexMobject(str(x), "^2"), TexMobject("-", "(", str(y), "i", ")^2") ) expansion.arrange(RIGHT, buff = SMALL_BUFF) expansion.next_to( VGroup(*self.equation[-2:]), DOWN, LARGE_BUFF ) alt_y_term = TexMobject("+", str(y), "^2") alt_y_term.move_to(expansion[1], LEFT) for tex_mob in list(expansion) + [alt_y_term]: tex_mob.set_color_by_tex(str(x), self.x_color) tex_mob.set_color_by_tex(str(y), self.y_color) tex_mob.rect = BackgroundRectangle(tex_mob) x1 = self.equation[-2][1][1] x2 = self.equation[-1][1][1] y1 = VGroup(*self.equation[-2][1][3:5]) y2 = VGroup(*self.equation[-1][1][2:5]) vect = MED_LARGE_BUFF*UP self.play(FadeOut(self.conjugate_label)) group = VGroup(x1, x2) self.play(group.shift, -vect) self.play( FadeIn(expansion[0].rect), ReplacementTransform(group.copy(), expansion[0]), ) self.play(group.shift, vect) group = VGroup(x1, y2) self.play(group.shift, -vect) self.wait() self.play(group.shift, vect) group = VGroup(x2, y1) self.play(group.shift, -vect) self.wait() self.play(group.shift, vect) group = VGroup(*it.chain(y1, y2)) self.play(group.shift, -vect) self.wait() self.play( FadeIn(expansion[1].rect), ReplacementTransform(group.copy(), expansion[1]), ) self.play(group.shift, vect) self.wait(2) self.play( Transform(expansion[1].rect, alt_y_term.rect), Transform(expansion[1], alt_y_term), ) self.wait() self.play(*list(map(FadeOut, [ expansion[0].rect, expansion[1].rect, expansion ]))) def discuss_geometry(self): randy = Randolph(color = BLUE_C) randy.scale(0.8) randy.to_corner(DOWN+LEFT) morty = Mortimer() morty.set_height(randy.get_height()) morty.next_to(randy, RIGHT) randy.make_eye_contact(morty) screen = ScreenRectangle(height = 3.5) screen.to_corner(DOWN+RIGHT, buff = MED_SMALL_BUFF) self.play(*list(map(FadeIn, [randy, morty]))) self.play(PiCreatureSays( morty, "More geometry!", target_mode = "hooray", run_time = 2, bubble_kwargs = {"height" : 2, "width" : 4} )) self.play(Blink(randy)) self.play( RemovePiCreatureBubble( morty, target_mode = "plain", ), PiCreatureSays( randy, "???", target_mode = "maybe", bubble_kwargs = {"width" : 3, "height" : 2} ) ) self.play( ShowCreation(screen), morty.look_at, screen, randy.look_at, screen, ) self.play(Blink(morty)) self.play(RemovePiCreatureBubble(randy, target_mode = "pondering")) self.wait() self.play(*list(map(FadeOut, [randy, morty, screen]))) def show_geometrically(self): dots = [self.example_dot, self.conjugate_dot] top_dot, low_dot = dots for dot in dots: dot.line = Line( self.plane_center, dot.get_center(), color = BLUE ) dot.angle = dot.line.get_angle() dot.arc = Arc( dot.angle, radius = 0.75, color = YELLOW ) dot.arc.shift(self.plane_center) dot.arc.add_tip(tip_length = 0.2) dot.rotate_word = TextMobject("Rotate") dot.rotate_word.scale(0.5) dot.rotate_word.next_to(dot.arc, RIGHT, SMALL_BUFF) dot.magnitude_word = TextMobject("Length 5") dot.magnitude_word.scale(0.6) dot.magnitude_word.next_to( ORIGIN, np.sign(dot.get_center()[1])*UP, buff = SMALL_BUFF ) dot.magnitude_word.add_background_rectangle() dot.magnitude_word.rotate(dot.angle) dot.magnitude_word.shift(dot.line.get_center()) twenty_five_label = TexMobject("25") twenty_five_label.add_background_rectangle() twenty_five_label.next_to( self.plane.coords_to_point(25, 0), DOWN ) self.play(ShowCreation(top_dot.line)) mover = VGroup( top_dot.line.copy().set_color(PINK), top_dot.copy() ) self.play(FadeIn( top_dot.magnitude_word, lag_ratio = 0.5 )) self.wait() self.play(ShowCreation(top_dot.arc)) self.wait(2) self.play(ShowCreation(low_dot.line)) self.play( ReplacementTransform( top_dot.arc, low_dot.arc ), FadeIn(low_dot.rotate_word) ) self.play( Rotate( mover, low_dot.angle, about_point = self.plane_center ), run_time = 2 ) self.play( FadeOut(low_dot.arc), FadeOut(low_dot.rotate_word), FadeIn(low_dot.magnitude_word), ) self.play( mover[0].scale_about_point, 5, self.plane_center, mover[1].move_to, self.plane.coords_to_point(25, 0), run_time = 2 ) self.wait() self.play(Write(twenty_five_label)) self.wait(3) class NameGaussianIntegers(LatticePointScene): CONFIG = { "max_lattice_point_radius" : 15, "dot_radius" : 0.05, "plane_center" : 2*LEFT, "x_radius" : 15, } def construct(self): self.add_axis_labels() self.add_a_plus_bi() self.draw_lattice_points() self.add_name() self.restrict_to_one_circle() self.show_question_algebraically() def add_a_plus_bi(self): label = TexMobject( "a", "+", "b", "i" ) a = label.get_part_by_tex("a") b = label.get_part_by_tex("b") a.set_color(GREEN) b.set_color(RED) label.add_background_rectangle() label.to_corner(UP+RIGHT) integers = TextMobject("Integers") integers.next_to(label, DOWN, LARGE_BUFF) integers.add_background_rectangle() arrows = VGroup(*[ Arrow(integers.get_top(), mob, tip_length = 0.15) for mob in (a, b) ]) self.add_foreground_mobjects(label, integers, arrows) self.a_plus_bi = label self.integers_label = VGroup(integers, arrows) def add_name(self): gauss_name = TextMobject( "Carl Friedrich Gauss" ) gauss_name.add_background_rectangle() gauss_name.next_to(ORIGIN, UP, MED_LARGE_BUFF) gauss_name.to_edge(LEFT) gaussian_integers = TextMobject("``Gaussian integers'': ") gaussian_integers.scale(0.9) gaussian_integers.next_to(self.a_plus_bi, LEFT) gaussian_integers.add_background_rectangle() self.play(FadeIn(gaussian_integers)) self.add_foreground_mobject(gaussian_integers) self.play(FadeIn( gauss_name, run_time = 2, lag_ratio = 0.5 )) self.wait(3) self.play(FadeOut(gauss_name)) self.gaussian_integers = gaussian_integers def restrict_to_one_circle(self): dots = self.get_lattice_points_on_r_squared_circle(25).copy() for dot in dots: dot.scale_in_place(2) circle = self.get_circle(5) radius, root_label = self.get_radial_line_with_label(5) self.play( FadeOut(self.lattice_points), ShowCreation(circle), Rotating( radius, run_time = 1, rate_func = smooth, about_point = self.plane_center ), *list(map(GrowFromCenter, dots)) ) self.play(Write(root_label)) self.wait() self.circle_dots = dots def show_question_algebraically(self): for i, dot in enumerate(self.circle_dots): x, y = self.dot_to_int_coords(dot) x_str = str(x) y_str = str(y) if y >= 0 else "(%d)"%y label = TexMobject(x_str, "+", y_str, "i") label.scale(0.8) label.next_to( dot, dot.get_center()-self.plane_center + SMALL_BUFF*(UP+RIGHT), buff = 0, ) label.add_background_rectangle() dot.label = label equation = TexMobject( "25 = " "(", x_str, "+", y_str, "i", ")", "(", x_str, "-", y_str, "i", ")", ) equation.scale(0.9) equation.add_background_rectangle() equation.to_corner(UP + RIGHT) dot.equation = equation for mob in label, equation: mob.set_color_by_tex(x_str, GREEN, substring = False) mob.set_color_by_tex(y_str, RED, substring = False) dot.line_pair = VGroup(*[ Line( self.plane_center, self.plane.coords_to_point(x, u*y), color = PINK, ) for u in (1, -1) ]) dot.conjugate_dot = self.circle_dots[-i] self.play(*list(map(FadeOut, [ self.a_plus_bi, self.integers_label, self.gaussian_integers, ]))) last_dot = None for dot in self.circle_dots: anims = [ dot.set_color, PINK, dot.conjugate_dot.set_color, PINK, ] if last_dot is None: anims += [ FadeIn(dot.equation), FadeIn(dot.label), ] anims += list(map(ShowCreation, dot.line_pair)) else: anims += [ last_dot.set_color, self.dot_color, last_dot.conjugate_dot.set_color, self.dot_color, ReplacementTransform(last_dot.equation, dot.equation), ReplacementTransform(last_dot.label, dot.label), ReplacementTransform(last_dot.line_pair, dot.line_pair), ] self.play(*anims) self.wait() last_dot = dot class FactorOrdinaryNumber(TeacherStudentsScene): def construct(self): equation = TexMobject( "2{,}250", "=", "2 \\cdot 3^2 \\cdot 5^3" ) equation.next_to(self.get_pi_creatures(), UP, LARGE_BUFF) number = equation[0] alt_rhs_list = list(it.starmap(TexMobject, [ ("\\ne", "2^2 \\cdot 563"), ("\\ne", "2^2 \\cdot 3 \\cdot 11 \\cdot 17"), ("\\ne", "2 \\cdot 7^2 \\cdot 23"), ("=", "(-2) \\cdot (-3) \\cdot (3) \\cdot 5^3"), ("=", "2 \\cdot (-3) \\cdot (3) \\cdot (-5) \\cdot 5^2"), ])) for alt_rhs in alt_rhs_list: if "\\ne" in alt_rhs.get_tex_string(): alt_rhs.set_color(RED) else: alt_rhs.set_color(GREEN) alt_rhs.move_to(equation.get_right()) number.save_state() number.next_to(self.teacher, UP+LEFT) title = TextMobject("Almost", "Unique factorization") title.set_color_by_tex("Almost", YELLOW) title.to_edge(UP) self.play( self.teacher.change_mode, "raise_right_hand", Write(number) ) self.wait(2) self.play( number.restore, Write(VGroup(*equation[1:])), Write(title[1]) ) self.change_student_modes( *["pondering"]*3, look_at_arg = equation, added_anims = [self.teacher.change_mode, "happy"] ) self.wait() last_alt_rhs = None for alt_rhs in alt_rhs_list: equation.generate_target() equation.target.next_to(alt_rhs, LEFT) anims = [MoveToTarget(equation)] if last_alt_rhs: anims += [ReplacementTransform(last_alt_rhs, alt_rhs)] else: anims += [FadeIn(alt_rhs)] self.play(*anims) if alt_rhs is alt_rhs_list[-2]: self.change_student_modes( *["sassy"]*3, look_at_arg = alt_rhs, added_anims = [Write(title[0])] ) self.wait(2) last_alt_rhs = alt_rhs self.play( FadeOut(VGroup(equation, alt_rhs)), PiCreatureSays( self.teacher, "It's similar for \\\\ Gaussian integers", bubble_kwargs = {"height" : 3.5} ) ) self.change_student_modes(*["happy"]*3) self.wait(3) class IntroduceGaussianPrimes(LatticePointScene, PiCreatureScene): CONFIG = { "plane_center" : LEFT, "x_radius" : 13, } def create_pi_creature(self): morty = Mortimer().flip() morty.scale(0.7) morty.next_to(ORIGIN, UP, buff = 0) morty.to_edge(LEFT) return morty def setup(self): LatticePointScene.setup(self) PiCreatureScene.setup(self) self.remove(self.pi_creature) def construct(self): self.plane.set_stroke(width = 2) morty = self.pi_creature dots = [ Dot(self.plane.coords_to_point(*coords)) for coords in [ (5, 0), (2, 1), (2, -1), (-1, 2), (-1, -2), (-2, -1), (-2, 1), ] ] five_dot = dots[0] five_dot.set_color(YELLOW) p_dots = VGroup(*dots[1:]) p1_dot, p2_dot, p3_dot, p4_dot, p5_dot, p6_dot = p_dots VGroup(p1_dot, p3_dot, p5_dot).set_color(PINK) VGroup(p2_dot, p4_dot, p6_dot).set_color(RED) labels = [ TexMobject(tex).add_background_rectangle() for tex in ("5", "2+i", "2-i", "-1+2i", "-1-2i", "-2-i", "-2+i") ] five_label, p1_label, p2_label, p3_label, p4_label, p5_label, p6_label = labels vects = [ DOWN, UP+RIGHT, DOWN+RIGHT, UP+LEFT, DOWN+LEFT, DOWN+LEFT, UP+LEFT, ] for dot, label, vect in zip(dots, labels, vects): label.next_to(dot, vect, SMALL_BUFF) arc_angle = 0.8*np.pi times_i_arc = Arrow( p1_dot.get_top(), p3_dot.get_top(), path_arc = arc_angle ) times_neg_i_arc = Arrow( p2_dot.get_bottom(), p4_dot.get_bottom(), path_arc = -arc_angle ) times_i = TexMobject("\\times i") times_i.add_background_rectangle() times_i.next_to( times_i_arc.point_from_proportion(0.5), UP ) times_neg_i = TexMobject("\\times (-i)") times_neg_i.add_background_rectangle() times_neg_i.next_to( times_neg_i_arc.point_from_proportion(0.5), DOWN ) VGroup( times_i, times_neg_i, times_i_arc, times_neg_i_arc ).set_color(MAROON_B) gaussian_prime = TextMobject("$\\Rightarrow$ ``Gaussian prime''") gaussian_prime.add_background_rectangle() gaussian_prime.scale(0.9) gaussian_prime.next_to(p1_label, RIGHT) factorization = TexMobject( "5", "= (2+i)(2-i)" ) factorization.to_corner(UP+RIGHT) factorization.shift(1.5*LEFT) factorization.add_background_rectangle() neg_alt_factorization = TexMobject("=(-2-i)(-2+i)") i_alt_factorization = TexMobject("=(-1+2i)(-1-2i)") for alt_factorization in neg_alt_factorization, i_alt_factorization: alt_factorization.next_to( factorization.get_part_by_tex("="), DOWN, aligned_edge = LEFT ) alt_factorization.add_background_rectangle() for dot in dots: dot.add(Line( self.plane_center, dot.get_center(), color = dot.get_color() )) self.add(factorization) self.play( DrawBorderThenFill(five_dot), FadeIn(five_label) ) self.wait() self.play( ReplacementTransform( VGroup(five_dot).copy(), VGroup(p1_dot, p2_dot) ) ) self.play(*list(map(Write, [p1_label, p2_label]))) self.wait() self.play(Write(gaussian_prime)) self.wait() #Show morty self.play(FadeIn(morty)) self.play(PiCreatureSays( morty, "\\emph{Almost} unique", bubble_kwargs = {"height" : 2, "width" : 5}, )) self.wait() self.play(RemovePiCreatureBubble(morty, target_mode = "pondering")) #Show neg_alternate expression movers = [p1_dot, p2_dot, p1_label, p2_label] for mover in movers: mover.save_state() self.play( Transform(p1_dot, p5_dot), Transform(p1_label, p5_label), ) self.play( Transform(p2_dot, p6_dot), Transform(p2_label, p6_label), ) self.play(Write(neg_alt_factorization)) self.wait() self.play( FadeOut(neg_alt_factorization), *[m.restore for m in movers] ) self.wait() ##Show i_alternate expression self.play( ShowCreation(times_i_arc), FadeIn(times_i), *[ ReplacementTransform( mob1.copy(), mob2, path_arc = np.pi/2 ) for mob1, mob2 in [ (p1_dot, p3_dot), (p1_label, p3_label), ] ] ) self.wait() self.play( ShowCreation(times_neg_i_arc), FadeIn(times_neg_i), *[ ReplacementTransform( mob1.copy(), mob2, path_arc = -np.pi/2 ) for mob1, mob2 in [ (p2_dot, p4_dot), (p2_label, p4_label), ] ] ) self.wait() self.play(Write(i_alt_factorization)) self.change_mode("hesitant") self.wait(3) class FromIntegerFactorsToGaussianFactors(TeacherStudentsScene): def construct(self): expression = TexMobject( "30", "=", "2", "\\cdot", "3", "\\cdot", "5" ) expression.shift(2*UP) two = expression.get_part_by_tex("2") five = expression.get_part_by_tex("5") two.set_color(BLUE) five.set_color(GREEN) two.factors = TexMobject("(1+i)", "(1-i)") five.factors = TexMobject("(2+i)", "(2-i)") for mob, vect in (two, DOWN), (five, UP): mob.factors.next_to(mob, vect, LARGE_BUFF) mob.factors.set_color(mob.get_color()) mob.arrows = VGroup(*[ Arrow( mob.get_edge_center(vect), factor.get_edge_center(-vect), color = mob.get_color(), tip_length = 0.15 ) for factor in mob.factors ]) self.add(expression) for mob in two, five: self.play( ReplacementTransform( mob.copy(), mob.factors ), *list(map(ShowCreation, mob.arrows)) ) self.wait() self.play(*[ ApplyMethod(pi.change, "pondering", expression) for pi in self.get_pi_creatures() ]) self.wait(5) group = VGroup( expression, two.arrows, two.factors, five.arrows, five.factors, ) self.teacher_says( "Now for a \\\\ surprising fact...", added_anims = [FadeOut(group)] ) self.wait(2) class FactorizationPattern(Scene): def construct(self): self.force_skipping() self.add_number_line() self.show_one_mod_four_primes() self.show_three_mod_four_primes() self.ask_why_this_is_true() self.show_two() def add_number_line(self): line = NumberLine( x_min = 0, x_max = 36, unit_size = 0.4, numbers_to_show = list(range(0, 33, 4)), numbers_with_elongated_ticks = list(range(0, 33, 4)), ) line.shift(2*DOWN) line.to_edge(LEFT) line.add_numbers() self.add(line) self.number_line = line def show_one_mod_four_primes(self): primes = [5, 13, 17, 29] dots = VGroup(*[ Dot(self.number_line.number_to_point(prime)) for prime in primes ]) dots.set_color(GREEN) prime_mobs = VGroup(*list(map(TexMobject, list(map(str, primes))))) arrows = VGroup() for prime_mob, dot in zip(prime_mobs, dots): prime_mob.next_to(dot, UP, LARGE_BUFF) prime_mob.set_color(dot.get_color()) arrow = Arrow(prime_mob, dot, buff = SMALL_BUFF) arrow.set_color(dot.get_color()) arrows.add(arrow) factorizations = VGroup(*[ TexMobject("=(%d+%si)(%d-%si)"%(x, y_str, x, y_str)) for x, y in [(2, 1), (3, 2), (4, 1), (5, 2)] for y_str in [str(y) if y is not 1 else ""] ]) factorizations.arrange(DOWN, aligned_edge = LEFT) factorizations.to_corner(UP+LEFT) factorizations.shift(RIGHT) movers = VGroup() for p_mob, factorization in zip(prime_mobs, factorizations): mover = p_mob.copy() mover.generate_target() mover.target.next_to(factorization, LEFT) movers.add(mover) v_dots = TexMobject("\\vdots") v_dots.next_to(factorizations[-1][0], DOWN) factorization.add(v_dots) self.play(*it.chain( list(map(Write, prime_mobs)), list(map(ShowCreation, arrows)), list(map(DrawBorderThenFill, dots)), )) self.wait() self.play(*[ MoveToTarget( mover, run_time = 2, path_arc = np.pi/2, ) for mover in movers ]) self.play(FadeIn( factorizations, run_time = 2, lag_ratio = 0.5 )) self.wait(4) self.play(*list(map(FadeOut, [movers, factorizations]))) def show_three_mod_four_primes(self): primes = [3, 7, 11, 19, 23, 31] dots = VGroup(*[ Dot(self.number_line.number_to_point(prime)) for prime in primes ]) dots.set_color(RED) prime_mobs = VGroup(*list(map(TexMobject, list(map(str, primes))))) arrows = VGroup() for prime_mob, dot in zip(prime_mobs, dots): prime_mob.next_to(dot, UP, LARGE_BUFF) prime_mob.set_color(dot.get_color()) arrow = Arrow(prime_mob, dot, buff = SMALL_BUFF) arrow.set_color(dot.get_color()) arrows.add(arrow) words = TextMobject("Already Gaussian primes") words.to_corner(UP+LEFT) word_arrows = VGroup(*[ Line( words.get_bottom(), p_mob.get_top(), color = p_mob.get_color(), buff = MED_SMALL_BUFF ) for p_mob in prime_mobs ]) self.play(*it.chain( list(map(Write, prime_mobs)), list(map(ShowCreation, arrows)), list(map(DrawBorderThenFill, dots)), )) self.wait() self.play( Write(words), *list(map(ShowCreation, word_arrows)) ) self.wait(4) self.play(*list(map(FadeOut, [words, word_arrows]))) def ask_why_this_is_true(self): randy = Randolph(color = BLUE_C) randy.scale(0.7) randy.to_edge(LEFT) randy.shift(0.8*UP) links_text = TextMobject("(See links in description)") links_text.scale(0.7) links_text.to_corner(UP+RIGHT) links_text.shift(DOWN) self.play(FadeIn(randy)) self.play(PiCreatureBubbleIntroduction( randy, "Wait...why?", bubble_class = ThoughtBubble, bubble_kwargs = {"height" : 2, "width" : 3}, target_mode = "confused", look_at_arg = self.number_line, )) self.play(Blink(randy)) self.wait() self.play(FadeIn(links_text)) self.wait(2) self.play(*list(map(FadeOut, [ randy, randy.bubble, randy.bubble.content, links_text ]))) def show_two(self): two_dot = Dot(self.number_line.number_to_point(2)) two = TexMobject("2") two.next_to(two_dot, UP, LARGE_BUFF) arrow = Arrow(two, two_dot, buff = SMALL_BUFF) VGroup(two_dot, two, arrow).set_color(YELLOW) mover = two.copy() mover.generate_target() mover.target.to_corner(UP+LEFT) factorization = TexMobject("=", "(1+i)", "(1-i)") factorization.next_to(mover.target, RIGHT) factors = VGroup(*factorization[1:]) time_i_arrow = Arrow( factors[1].get_bottom(), factors[0].get_bottom(), path_arc = -np.pi ) times_i = TexMobject("\\times i") # times_i.scale(1.5) times_i.next_to(time_i_arrow, DOWN) times_i.set_color(time_i_arrow.get_color()) words = TextMobject("You'll see why this matters...") words.next_to(times_i, DOWN) words.shift_onto_screen() self.play( Write(two), ShowCreation(arrow), DrawBorderThenFill(two_dot) ) self.wait() self.play( MoveToTarget(mover), Write(factorization) ) self.revert_to_original_skipping_status() self.wait(2) self.play(ShowCreation(time_i_arrow)) self.play(Write(times_i)) self.wait(2) self.play(FadeIn(words)) self.wait(2) class RingsWithOneModFourPrimes(CertainRegularityInPrimes): CONFIG = { "plane_center" : ORIGIN, "primes" : [5, 13, 17, 29, 37, 41, 53], "include_pi_formula" : False, } class RingsWithThreeModFourPrimes(CertainRegularityInPrimes): CONFIG = { "plane_center" : ORIGIN, "primes" : [3, 7, 11, 19, 23, 31, 43], "include_pi_formula" : False, } class FactorTwo(LatticePointScene): CONFIG = { "y_radius" : 3, } def construct(self): two_dot = Dot(self.plane.coords_to_point(2, 0)) two_dot.set_color(YELLOW) factor_dots = VGroup(*[ Dot(self.plane.coords_to_point(1, u)) for u in (1, -1) ]) two_label = TexMobject("2").next_to(two_dot, DOWN) two_label.set_color(YELLOW) two_label.add_background_rectangle() factor_labels = VGroup(*[ TexMobject(tex).add_background_rectangle().next_to(dot, vect) for tex, dot, vect in zip( ["1+i", "1-i"], factor_dots, [UP, DOWN] ) ]) VGroup(factor_labels, factor_dots).set_color(MAROON_B) for dot in it.chain(factor_dots, [two_dot]): line = Line(self.plane_center, dot.get_center()) line.set_color(dot.get_color()) dot.add(line) self.play( ShowCreation(two_dot), Write(two_label), ) self.play(*[ ReplacementTransform( VGroup(mob1.copy()), mob2 ) for mob1, mob2 in [ (two_label, factor_labels), (two_dot, factor_dots), ] ]) self.wait(2) dot_copy = factor_dots[1].copy() dot_copy.set_color(RED) for angle in np.pi/2, -np.pi/2: self.play(Rotate(dot_copy, angle, run_time = 2)) self.wait(2) class CountThroughRingsCopy(CountThroughRings): pass class NameGaussianIntegersCopy(NameGaussianIntegers): pass class IntroduceRecipe(Scene): CONFIG = { "N_string" : "25", "integer_factors" : [5, 5], "gaussian_factors" : [ complex(2, 1), complex(2, -1), complex(2, 1), complex(2, -1), ], "x_color" : GREEN, "y_color" : RED, "N_color" : WHITE, "i_positive_color" : BLUE, "i_negative_color" : YELLOW, "i_zero_color" : MAROON_B, "T_chart_width" : 8, "T_chart_height" : 6, } def construct(self): self.add_title() self.show_ordinary_factorization() self.subfactor_ordinary_factorization() self.organize_factors_into_columns() self.mention_conjugate_rule() self.take_product_of_columns() self.mark_left_product_as_result() self.swap_factors() def add_title(self): title = TexMobject( "\\text{Recipe for }", "a", "+", "b", "i", "\\text{ satisfying }", "(", "a", "+", "b", "i", ")", "(", "a", "-", "b", "i", ")", "=", self.N_string ) strings = ("a", "b", self.N_string) colors = (self.x_color, self.y_color, self.N_color) for tex, color in zip(strings, colors): title.set_color_by_tex(tex, color, substring = False) title.to_edge(UP, buff = MED_SMALL_BUFF) h_line = Line(LEFT, RIGHT).scale(FRAME_X_RADIUS) h_line.next_to(title, DOWN) self.add(title, h_line) N_mob = title.get_part_by_tex(self.N_string) digest_locals(self, ["title", "h_line", "N_mob"]) def show_ordinary_factorization(self): N_mob = self.N_mob.copy() N_mob.generate_target() N_mob.target.next_to(self.h_line, DOWN) N_mob.target.to_edge(LEFT) factors = self.integer_factors symbols = ["="] + ["\\cdot"]*(len(factors)-1) factorization = TexMobject(*it.chain(*list(zip( symbols, list(map(str, factors)) )))) factorization.next_to(N_mob.target, RIGHT) self.play(MoveToTarget( N_mob, run_time = 2, path_arc = -np.pi/6 )) self.play(Write(factorization)) self.wait() self.factored_N_mob = N_mob self.integer_factorization = factorization def subfactor_ordinary_factorization(self): factors = self.gaussian_factors factorization = TexMobject( "=", *list(map(self.complex_number_to_tex, factors)) ) max_width = FRAME_WIDTH - 2 if factorization.get_width() > max_width: factorization.set_width(max_width) factorization.next_to( self.integer_factorization, DOWN, aligned_edge = LEFT ) for factor, mob in zip(factors, factorization[1:]): mob.underlying_number = factor y = complex(factor).imag if y == 0: mob.set_color(self.i_zero_color) elif y > 0: mob.set_color(self.i_positive_color) elif y < 0: mob.set_color(self.i_negative_color) movers = VGroup() mover = self.integer_factorization[0].copy() mover.target = factorization[0] movers.add(mover) index = 0 for prime_mob in self.integer_factorization[1::2]: gauss_prime = factors[index] gauss_prime_mob = factorization[index+1] mover = prime_mob.copy() mover.target = gauss_prime_mob movers.add(mover) if abs(complex(gauss_prime).imag) > 0: index += 1 mover = prime_mob.copy() mover.target = factorization[index+1] movers.add(mover) index += 1 self.play(OldLaggedStart( MoveToTarget, movers, replace_mobject_with_target_in_scene = True )) self.wait() self.gaussian_factorization = factorization def organize_factors_into_columns(self): T_chart = self.get_T_chart() factors = self.gaussian_factorization.copy()[1:] left_factors, right_factors = self.get_left_and_right_factors() for group in left_factors, right_factors: group.generate_target() group.target.arrange(DOWN) left_factors.target.next_to(T_chart.left_h_line, DOWN) right_factors.target.next_to(T_chart.right_h_line, DOWN) self.play(ShowCreation(T_chart)) self.wait() self.play(MoveToTarget(left_factors)) self.play(MoveToTarget(right_factors)) self.wait() digest_locals(self, ["left_factors", "right_factors"]) def mention_conjugate_rule(self): left_factors = self.left_factors right_factors = self.right_factors double_arrows = VGroup() for lf, rf in zip(left_factors.target, right_factors.target): arrow = DoubleArrow( lf, rf, buff = SMALL_BUFF, tip_length = SMALL_BUFF, color = GREEN ) word = TextMobject("Conjugates") word.scale(0.75) word.add_background_rectangle() word.next_to(arrow, DOWN, SMALL_BUFF) arrow.add(word) double_arrows.add(arrow) main_arrow = double_arrows[0] self.play(Write(main_arrow, run_time = 1)) self.wait() for new_arrow in double_arrows[1:]: self.play(Transform(main_arrow, new_arrow)) self.wait() self.wait() self.play(FadeOut(main_arrow)) def take_product_of_columns(self): arrows = self.get_product_multiplication_lines() products = self.get_product_mobjects() factor_groups = [self.left_factors, self.right_factors] for arrow, product, group in zip(arrows, products, factor_groups): self.play(ShowCreation(arrow)) self.play(ReplacementTransform( group.copy(), VGroup(product) )) self.wait() self.wait(3) def mark_left_product_as_result(self): rect = self.get_result_surrounding_rect() words = TextMobject("Output", " of recipe") words.next_to(rect, DOWN, buff = MED_LARGE_BUFF) words.to_edge(LEFT) arrow = Arrow(words.get_top(), rect.get_left()) self.play(ShowCreation(rect)) self.play( Write(words, run_time = 2), ShowCreation(arrow) ) self.wait(3) self.play(*list(map(FadeOut, [words, arrow]))) self.output_label_group = VGroup(words, arrow) def swap_factors(self): for i in range(len(self.left_factors)): self.swap_factors_at_index(i) self.wait() ######### def get_left_and_right_factors(self): factors = self.gaussian_factorization.copy()[1:] return VGroup(*factors[::2]), VGroup(*factors[1::2]) def get_T_chart(self): T_chart = VGroup() h_lines = VGroup(*[ Line(ORIGIN, self.T_chart_width*RIGHT/2.0) for x in range(2) ]) h_lines.arrange(RIGHT, buff = 0) h_lines.shift(UP) v_line = Line(self.T_chart_height*UP, ORIGIN) v_line.move_to(h_lines.get_center(), UP) T_chart.left_h_line, T_chart.right_h_line = h_lines T_chart.v_line = v_line T_chart.digest_mobject_attrs() return T_chart def complex_number_to_tex(self, z): z = complex(z) x, y = z.real, z.imag if y == 0: return "(%d)"%x y_sign_tex = "+" if y >= 0 else "-" if abs(y) == 1: y_str = y_sign_tex + "i" else: y_str = y_sign_tex + "%di"%abs(y) return "(%d%s)"%(x, y_str) def get_product_multiplication_lines(self): lines = VGroup() for factors in self.left_factors, self.right_factors: line = Line(ORIGIN, 3*RIGHT) line.next_to(factors, DOWN, SMALL_BUFF) times = TexMobject("\\times") times.next_to(line.get_left(), UP+RIGHT, SMALL_BUFF) line.add(times) lines.add(line) self.multiplication_lines = lines return lines def get_product_mobjects(self): factor_groups = [self.left_factors, self.right_factors] product_mobjects = VGroup() for factors, line in zip(factor_groups, self.multiplication_lines): product = reduce(op.mul, [ factor.underlying_number for factor in factors ]) color = average_color(*[ factor.get_color() for factor in factors ]) product_mob = TexMobject( self.complex_number_to_tex(product) ) product_mob.set_color(color) product_mob.next_to(line, DOWN) product_mobjects.add(product_mob) self.product_mobjects = product_mobjects return product_mobjects def swap_factors_at_index(self, index): factor_groups = self.left_factors, self.right_factors factors_to_swap = [group[index] for group in factor_groups] self.play(*[ ApplyMethod( factors_to_swap[i].move_to, factors_to_swap[1-i], path_arc = np.pi/2, ) for i in range(2) ]) for i, group in enumerate(factor_groups): group.submobjects[index] = factors_to_swap[1-i] self.play(FadeOut(self.product_mobjects)) self.get_product_mobjects() rect = self.result_surrounding_rect new_rect = self.get_result_surrounding_rect() self.play(*[ ReplacementTransform(group.copy(), VGroup(product)) for group, product in zip( factor_groups, self.product_mobjects, ) ]+[ ReplacementTransform(rect, new_rect) ]) self.wait() def get_result_surrounding_rect(self, product = None): if product is None: product = self.product_mobjects[0] rect = SurroundingRectangle(product) self.result_surrounding_rect = rect return rect def write_last_step(self): output_words, arrow = self.output_label_group final_step = TextMobject( "Multiply by $1$, $i$, $-1$ or $-i$" ) final_step.scale(0.9) final_step.next_to(arrow.get_start(), DOWN, SMALL_BUFF) final_step.shift_onto_screen() anims = [Write(final_step)] if arrow not in self.get_mobjects(): # arrow = Arrow( # final_step.get_top(), # self.result_surrounding_rect.get_left() # ) anims += [ShowCreation(arrow)] self.play(*anims) self.wait(2) class StateThreeChoices(TeacherStudentsScene): def construct(self): self.teacher_says( "$5^2$ gives 3 choices." ) self.wait(3) class ThreeOutputsAsLatticePoints(LatticePointScene): CONFIG = { "coords_list" : [(3, 4), (5, 0), (3, -4)], "dot_radius" : 0.1, "colors" : [YELLOW, GREEN, PINK, MAROON_B], } def construct(self): self.add_circle() self.add_dots_and_labels() def add_circle(self): radius = np.sqrt(self.radius_squared) circle = self.get_circle(radius) radial_line, root_label = self.get_radial_line_with_label(radius) self.add(radial_line, root_label, circle) self.add_foreground_mobject(root_label) def add_dots_and_labels(self): dots = VGroup(*[ Dot( self.plane.coords_to_point(*coords), radius = self.dot_radius, color = self.colors[0], ) for coords in self.coords_list ]) labels = VGroup() for x, y in self.coords_list: if y == 0: y_str = "" vect = DOWN+RIGHT elif y > 1: y_str = "+%di"%y vect = UP+RIGHT else: y_str = "%di"%y vect = DOWN+RIGHT label = TexMobject("%d%s"%(x, y_str)) label.add_background_rectangle() point = self.plane.coords_to_point(x, y) label.next_to(point, vect) labels.add(label) for dot, label in zip(dots, labels): self.play( FadeIn(label), DrawBorderThenFill( dot, stroke_color = PINK, stroke_width = 4 ) ) self.wait(2) self.original_dots = dots class LooksLikeYoureMissingSome(TeacherStudentsScene): def construct(self): self.student_says( "Looks like you're \\\\ missing a few", target_mode = "sassy", student_index = 0, ) self.play(self.teacher.change, "guilty") self.wait(3) class ShowAlternateFactorizationOfTwentyFive(IntroduceRecipe): CONFIG = { "gaussian_factors" : [ complex(-1, 2), complex(-1, -2), complex(2, 1), complex(2, -1), ], } def construct(self): self.add_title() self.show_ordinary_factorization() self.subfactor_ordinary_factorization() self.organize_factors_into_columns() self.take_product_of_columns() self.mark_left_product_as_result() self.swap_factors() class WriteAlternateLastStep(IntroduceRecipe): def construct(self): self.force_skipping() self.add_title() self.show_ordinary_factorization() self.subfactor_ordinary_factorization() self.organize_factors_into_columns() self.take_product_of_columns() self.mark_left_product_as_result() self.revert_to_original_skipping_status() self.cross_out_output_words() self.write_last_step() def cross_out_output_words(self): output_words, arrow = self.output_label_group cross = TexMobject("\\times") cross.replace(output_words, stretch = True) cross.set_color(RED) self.add(output_words, arrow) self.play(Write(cross)) output_words.add(cross) self.play(output_words.to_edge, DOWN) class ThreeOutputsAsLatticePointsContinued(ThreeOutputsAsLatticePoints): def construct(self): self.force_skipping() ThreeOutputsAsLatticePoints.construct(self) self.revert_to_original_skipping_status() self.show_multiplication_by_units() def show_multiplication_by_units(self): original_dots = self.original_dots lines = VGroup() for dot in original_dots: line = Line(self.plane_center, dot.get_center()) line.set_stroke(dot.get_color(), 6) lines.add(line) dot.add(line) words_group = VGroup(*[ TextMobject("Multiply by $%s$"%s) for s in ("1", "i", "-1", "-i") ]) for words, color in zip(words_group, self.colors): words.add_background_rectangle() words.set_color(color) words_group.arrange(DOWN, aligned_edge = LEFT) words_group.to_corner(UP+LEFT, buff = MED_SMALL_BUFF) angles = [np.pi/2, np.pi, -np.pi/2] self.play( FadeIn(words_group[0]), *list(map(ShowCreation, lines)) ) for words, angle, color in zip(words_group[1:], angles, self.colors[1:]): self.play(FadeIn(words)) dots_copy = original_dots.copy() self.play( dots_copy.rotate, angle, dots_copy.set_color, color, path_arc = angle ) self.wait() self.wait(2) class RecipeFor125(IntroduceRecipe): CONFIG = { "N_string" : "125", "integer_factors" : [5, 5, 5], "gaussian_factors" : [ complex(2, -1), complex(2, 1), complex(2, -1), complex(2, 1), complex(2, -1), complex(2, 1), ], } def construct(self): self.force_skipping() self.add_title() self.show_ordinary_factorization() self.subfactor_ordinary_factorization() self.revert_to_original_skipping_status() self.organize_factors_into_columns() # self.take_product_of_columns() # self.mark_left_product_as_result() # self.swap_factors() # self.write_last_step() class StateFourChoices(TeacherStudentsScene): def construct(self): self.teacher_says( "$5^3$ gives 4 choices." ) self.wait(3) class Show125Circle(ThreeOutputsAsLatticePointsContinued): CONFIG = { "radius_squared" : 125, "coords_list" : [(2, 11), (10, 5), (10, -5), (2, -11)], "y_radius" : 15, } def construct(self): self.draw_circle() self.add_dots_and_labels() self.show_multiplication_by_units() self.ask_about_two() def draw_circle(self): self.plane.scale(2) radius = np.sqrt(self.radius_squared) circle = self.get_circle(radius) radial_line, root_label = self.get_radial_line_with_label(radius) self.play( Write(root_label), ShowCreation(radial_line) ) self.add_foreground_mobject(root_label) self.play( Rotating( radial_line, rate_func = smooth, about_point = self.plane_center ), ShowCreation(circle), run_time = 2, ) group = VGroup( self.plane, radial_line, circle, root_label ) self.play(group.scale, 0.5) class RecipeFor375(IntroduceRecipe): CONFIG = { "N_string" : "375", "integer_factors" : [3, 5, 5, 5], "gaussian_factors" : [ 3, complex(2, 1), complex(2, -1), complex(2, 1), complex(2, -1), complex(2, 1), complex(2, -1), ], } def construct(self): self.add_title() self.show_ordinary_factorization() self.subfactor_ordinary_factorization() self.organize_factors_into_columns() self.express_trouble_with_three() self.take_product_of_columns() def express_trouble_with_three(self): morty = Mortimer().flip().to_corner(DOWN+LEFT) three = self.gaussian_factorization[1].copy() three.generate_target() three.target.next_to(morty, UP, MED_LARGE_BUFF) self.play(FadeIn(morty)) self.play( MoveToTarget(three), morty.change, "angry", three.target ) self.play(Blink(morty)) self.wait() for factors in self.left_factors, self.right_factors: self.play( three.next_to, factors, DOWN, morty.change, "sassy", factors.get_bottom() ) self.wait() self.right_factors.add(three) self.play(morty.change_mode, "pondering") #### def get_left_and_right_factors(self): factors = self.gaussian_factorization.copy()[1:] return VGroup(*factors[1::2]), VGroup(*factors[2::2]) class Show375Circle(LatticePointScene): CONFIG = { "y_radius" : 20, } def construct(self): radius = np.sqrt(375) circle = self.get_circle(radius) radial_line, root_label = self.get_radial_line_with_label(radius) self.play( ShowCreation(radial_line), Write(root_label, run_time = 1) ) self.add_foreground_mobject(root_label) self.play( Rotating( radial_line, rate_func = smooth, about_point = self.plane_center ), ShowCreation(circle), run_time = 2, ) group = VGroup( self.plane, radial_line, root_label, circle ) self.wait(2) class RecipeFor1125(IntroduceRecipe): CONFIG = { "N_string" : "1125", "integer_factors" : [3, 3, 5, 5, 5], "gaussian_factors" : [ 3, 3, complex(2, 1), complex(2, -1), complex(2, 1), complex(2, -1), complex(2, 1), complex(2, -1), ], } def construct(self): self.add_title() self.show_ordinary_factorization() self.subfactor_ordinary_factorization() self.organize_factors_into_columns() self.mention_conjugate_rule() self.take_product_of_columns() self.mark_left_product_as_result() self.swap_factors() self.write_last_step() def write_last_step(self): words = TextMobject( "Multiply by \\\\ ", "$1$, $i$, $-1$ or $-i$" ) words.scale(0.7) words.to_corner(DOWN+LEFT) product = self.product_mobjects[0] self.play(Write(words)) self.wait() class Show125CircleSimple(LatticePointScene): CONFIG = { "radius_squared" : 125, "y_radius" : 12, "max_lattice_point_radius" : 12, } def construct(self): self.plane.set_stroke(width = 1) radius = np.sqrt(self.radius_squared) circle = self.get_circle(radius) radial_line, root_label = self.get_radial_line_with_label(radius) dots = self.get_lattice_points_on_r_squared_circle(self.radius_squared) self.play( ShowCreation(radial_line), Write(root_label, run_time = 1) ) self.add_foreground_mobject(root_label) self.play( Rotating( radial_line, rate_func = smooth, about_point = self.plane_center ), ShowCreation(circle), OldLaggedStart( DrawBorderThenFill, dots, stroke_width = 4, stroke_color = PINK, ), run_time = 2, ) self.wait(2) class Show1125Circle(Show125CircleSimple): CONFIG = { "radius_squraed" : 1125, "y_radius" : 35, "max_lattice_point_radius" : 35, } class SummarizeCountingRule(Show125Circle): CONFIG = { "dot_radius" : 0.075, "N_str" : "N", "rect_opacity" : 1, } def construct(self): self.add_count_words() self.draw_circle() self.add_full_screen_rect() self.talk_through_rules() self.ask_about_two() def add_count_words(self): words = TextMobject( "\\# Lattice points \\\\ on $\\sqrt{%s}$ circle"%self.N_str ) words.to_corner(UP+LEFT) words.add_background_rectangle() self.add(words) self.count_words = words def draw_circle(self): radius = np.sqrt(self.radius_squared) circle = self.get_circle(radius) radial_line, num_root_label = self.get_radial_line_with_label(radius) root_label = TexMobject("\\sqrt{%s}"%self.N_str) root_label.next_to(radial_line, UP, SMALL_BUFF) dots = VGroup(*[ Dot( self.plane.coords_to_point(*coords), radius = self.dot_radius, color = self.dot_color ) for coords in self.coords_list ]) for angle in np.pi/2, np.pi, -np.pi/2: dots.add(*dots.copy().rotate(angle)) self.play( Write(root_label), ShowCreation(radial_line) ) self.play( Rotating( radial_line, rate_func = smooth, about_point = self.plane_center ), ShowCreation(circle), run_time = 2, ) self.play(OldLaggedStart( DrawBorderThenFill, dots, stroke_width = 4, stroke_color = PINK )) self.wait(2) def add_full_screen_rect(self): rect = FullScreenFadeRectangle( fill_opacity = self.rect_opacity ) self.play( FadeIn(rect), Animation(self.count_words) ) def talk_through_rules(self): factorization = TexMobject( "N =", "3", "^4", "\\cdot", "5", "^3", "\\cdot", "13", "^2" ) factorization.next_to(ORIGIN, RIGHT) factorization.to_edge(UP) three, five, thirteen = [ factorization.get_part_by_tex(str(n), substring = False) for n in (3, 5, 13) ] three_power = factorization.get_part_by_tex("^4") five_power = factorization.get_part_by_tex("^3") thirteen_power = factorization.get_part_by_tex("^2") alt_three_power = five_power.copy().move_to(three_power) three_brace = Brace(VGroup(*factorization[1:3]), DOWN) five_brace = Brace(VGroup(*factorization[3:6]), DOWN) thirteen_brace = Brace(VGroup(*factorization[6:9]), DOWN) three_choices = three_brace.get_tex("(", "1", ")") five_choices = five_brace.get_tex( "(", "3", "+", "1", ")" ) thirteen_choices = thirteen_brace.get_tex( "(", "2", "+", "1", ")" ) all_choices = VGroup(three_choices, five_choices, thirteen_choices) for choices in all_choices: choices.scale(0.75, about_point = choices.get_top()) thirteen_choices.next_to(five_choices, RIGHT) three_choices.next_to(five_choices, LEFT) alt_three_choices = TexMobject("(", "0", ")") alt_three_choices.scale(0.75) alt_three_choices.move_to(three_choices, RIGHT) self.play(FadeIn(factorization)) self.wait() self.play( five.set_color, GREEN, thirteen.set_color, GREEN, FadeIn(five_brace), FadeIn(thirteen_brace), ) self.wait() for choices, power in (five_choices, five_power), (thirteen_choices, thirteen_power): self.play( Write(VGroup(choices[0], *choices[2:])), ReplacementTransform( power.copy(), choices[1] ) ) self.wait() self.play( three.set_color, RED, FadeIn(three_brace) ) self.wait() self.play( Write(VGroup(three_choices[0], three_choices[2])), ReplacementTransform( three_power.copy(), three_choices[1] ) ) self.wait() movers = three_power, three_choices for mob in movers: mob.save_state() self.play( Transform( three_power, alt_three_power, path_arc = np.pi ), Transform(three_choices, alt_three_choices) ) self.wait() self.play( *[mob.restore for mob in movers], path_arc = -np.pi ) self.wait() equals_four = TexMobject("=", "4") four = equals_four.get_part_by_tex("4") four.set_color(YELLOW) final_choice_words = TextMobject( "Mutiply", "by $1$, $i$, $-1$ or $-i$" ) final_choice_words.set_color(YELLOW) final_choice_words.next_to(four, DOWN, LARGE_BUFF, LEFT) final_choice_words.to_edge(RIGHT) final_choice_arrow = Arrow( final_choice_words[0].get_top(), four.get_bottom(), buff = SMALL_BUFF ) choices_copy = all_choices.copy() choices_copy.generate_target() choices_copy.target.scale(1./0.75) choices_copy.target.arrange(RIGHT, buff = SMALL_BUFF) choices_copy.target.next_to(equals_four, RIGHT, SMALL_BUFF) choices_copy.target.shift(0.25*SMALL_BUFF*DOWN) self.play( self.count_words.next_to, equals_four, LEFT, MoveToTarget(choices_copy), FadeIn(equals_four) ) self.play(*list(map(FadeIn, [final_choice_words, final_choice_arrow]))) self.wait() def ask_about_two(self): randy = Randolph(color = BLUE_C) randy.scale(0.7) randy.to_edge(LEFT) self.play(FadeIn(randy)) self.play(PiCreatureBubbleIntroduction( randy, "What about \\\\ factors of 2?", bubble_class = ThoughtBubble, bubble_kwargs = {"height" : 3, "width" : 3}, target_mode = "confused", look_at_arg = self.count_words )) self.play(Blink(randy)) self.wait() class ThisIsTheHardestPart(TeacherStudentsScene): def construct(self): self.change_student_modes("horrified", "confused", "pleading") self.teacher_says("This is the \\\\ hardest part") self.change_student_modes("thinking", "happy", "pondering") self.wait(2) class RecipeFor10(IntroduceRecipe): CONFIG = { "N_string" : "10", "integer_factors" : [2, 5], "gaussian_factors" : [ complex(1, 1), complex(1, -1), complex(2, 1), complex(2, -1), ], } def construct(self): self.add_title() self.show_ordinary_factorization() self.subfactor_ordinary_factorization() self.organize_factors_into_columns() self.take_product_of_columns() self.mark_left_product_as_result() self.swap_two_factors() self.write_last_step() def swap_two_factors(self): left = self.left_factors[0] right = self.right_factors[0] arrow = Arrow(right, left, buff = SMALL_BUFF) times_i = TexMobject("\\times i") times_i.next_to(arrow, DOWN, 0) times_i.add_background_rectangle() curr_product = self.product_mobjects[0].copy() for x in range(2): self.swap_factors_at_index(0) self.play( ShowCreation(arrow), Write(times_i, run_time = 1) ) self.wait() self.play(curr_product.to_edge, LEFT) self.swap_factors_at_index(0) new_arrow = Arrow( self.result_surrounding_rect, curr_product, buff = SMALL_BUFF ) self.play( Transform(arrow, new_arrow), MaintainPositionRelativeTo(times_i, arrow) ) self.wait(2) self.play(*list(map(FadeOut, [arrow, times_i, curr_product]))) class FactorsOfTwoNeitherHelpNorHurt(TeacherStudentsScene): def construct(self): words = TextMobject( "Factors of", "$2^k$", "neither \\\\ help nor hurt" ) words.set_color_by_tex("2", YELLOW) self.teacher_says(words) self.change_student_modes(*["pondering"]*3) self.wait(3) class EffectOfPowersOfTwo(LatticePointScene): CONFIG = { "y_radius" : 9, "max_lattice_point_radius" : 9, "square_radii" : [5, 10, 20, 40, 80], } def construct(self): radii = list(map(np.sqrt, self.square_radii)) circles = list(map(self.get_circle, radii)) radial_lines, root_labels = list(zip(*list(map( self.get_radial_line_with_label, radii )))) dots_list = list(map( self.get_lattice_points_on_r_squared_circle, self.square_radii )) groups = [ VGroup(*mobs) for mobs in zip(radial_lines, circles, root_labels, dots_list) ] group = groups[0] self.add(group) self.play(OldLaggedStart( DrawBorderThenFill, dots_list[0], stroke_width = 4, stroke_color = PINK )) self.wait() for new_group in groups[1:]: self.play(Transform(group, new_group)) self.wait(2) class NumberTheoryAtItsBest(TeacherStudentsScene): def construct(self): self.teacher_says( "Number theory at its best!", target_mode = "hooray", run_time = 2, ) self.change_student_modes(*["hooray"]*3) self.wait(3) class IntroduceChi(FactorizationPattern): CONFIG = { "numbers_list" : [ list(range(i, 36, d)) for i, d in [(1, 4), (3, 4), (2, 2)] ], "colors" : [GREEN, RED, YELLOW] } def construct(self): self.add_number_line() self.add_define_chi_label() for index in range(3): self.describe_values(index) self.fade_out_labels() self.cyclic_pattern() self.write_multiplicative_label() self.show_multiplicative() def add_define_chi_label(self): label = TextMobject("Define $\\chi(n)$:") chi_expressions = VGroup(*[ self.get_chi_expression(numbers, color) for numbers, color in zip( self.numbers_list, self.colors ) ]) chi_expressions.scale(0.9) chi_expressions.arrange( DOWN, buff = MED_LARGE_BUFF, aligned_edge = LEFT ) chi_expressions.to_corner(UP+RIGHT) brace = Brace(chi_expressions, LEFT) label.next_to(brace, LEFT) self.play( Write(label), GrowFromCenter(brace) ) self.define_chi_label = label self.chi_expressions = chi_expressions def describe_values(self, index): numbers = self.numbers_list[index] color = self.colors[index] dots, arrows, labels = self.get_dots_arrows_and_labels( numbers, color ) chi_expression = self.chi_expressions[index] self.introduce_dots_arrows_and_labels(dots, arrows, labels) self.wait() self.play( Write(VGroup(*[ part for part in chi_expression if part not in chi_expression.inputs ])), *[ ReplacementTransform(label.copy(), num_mob) for label, num_mob in zip( labels, chi_expression.inputs ) ]) self.wait() def fade_out_labels(self): self.play(*list(map(FadeOut, [ self.last_dots, self.last_arrows, self.last_labels, self.number_line ]))) def cyclic_pattern(self): input_range = list(range(1, 9)) chis = VGroup(*[ TexMobject("\\chi(%d)"%n) for n in input_range ]) chis.arrange(RIGHT, buff = LARGE_BUFF) chis.set_stroke(WHITE, width = 1) numbers = VGroup() arrows = VGroup() for chi, n in zip(chis, input_range): arrow = TexMobject("\\Uparrow") arrow.next_to(chi, UP, SMALL_BUFF) arrows.add(arrow) value = TexMobject(str(chi_func(n))) for tex, color in zip(["1", "-1", "0"], self.colors): value.set_color_by_tex(tex, color) value.next_to(arrow, UP) numbers.add(value) group = VGroup(chis, arrows, numbers) group.set_width(FRAME_WIDTH - LARGE_BUFF) group.to_edge(DOWN, buff = LARGE_BUFF) self.play(*[ FadeIn( mob, run_time = 3, lag_ratio = 0.5 ) for mob in [chis, arrows, numbers] ]) self.play(OldLaggedStart( ApplyMethod, numbers, lambda m : (m.shift, MED_SMALL_BUFF*UP), rate_func = there_and_back, lag_ratio = 0.2, run_time = 6 )) self.wait() self.play(*list(map(FadeOut, [chis, arrows, numbers]))) def write_multiplicative_label(self): morty = Mortimer() morty.scale(0.7) morty.to_corner(DOWN+RIGHT) self.play(PiCreatureSays( morty, "$\\chi$ is ``multiplicative''", bubble_kwargs = {"height" : 2.5, "width" : 5} )) self.play(Blink(morty)) self.morty = morty def show_multiplicative(self): pairs = [(3, 5), (5, 5), (2, 13), (3, 11)] expressions = VGroup() for x, y in pairs: expression = TexMobject( "\\chi(%d)"%x, "\\cdot", "\\chi(%d)"%y, "=", "\\chi(%d)"%(x*y) ) braces = [ Brace(expression[i], UP) for i in (0, 2, 4) ] for brace, n in zip(braces, [x, y, x*y]): output = chi_func(n) label = brace.get_tex(str(output)) label.set_color(self.number_to_color(output)) brace.add(label) expression.add(brace) expressions.add(expression) expressions.next_to(ORIGIN, LEFT) expressions.shift(DOWN) expression = expressions[0] self.play( FadeIn(expression), self.morty.change, "pondering", expression ) self.wait(2) for new_expression in expressions[1:]: self.play(Transform(expression, new_expression)) self.wait(2) ######### def get_dots_arrows_and_labels(self, numbers, color): dots = VGroup() arrows = VGroup() labels = VGroup() for number in numbers: point = self.number_line.number_to_point(number) dot = Dot(point) label = TexMobject(str(number)) label.scale(0.8) label.next_to(dot, UP, LARGE_BUFF) arrow = Arrow(label, dot, buff = SMALL_BUFF) VGroup(dot, label, arrow).set_color(color) dots.add(dot) arrows.add(arrow) labels.add(label) return dots, arrows, labels def introduce_dots_arrows_and_labels(self, dots, arrows, labels): if hasattr(self, "last_dots"): self.play( ReplacementTransform(self.last_dots, dots), ReplacementTransform(self.last_arrows, arrows), ReplacementTransform(self.last_labels, labels), ) else: self.play( Write(labels), FadeIn(arrows, lag_ratio = 0.5), OldLaggedStart( DrawBorderThenFill, dots, stroke_width = 4, stroke_color = YELLOW ), run_time = 2 ) self.last_dots = dots self.last_arrows = arrows self.last_labels = labels def get_chi_expression(self, numbers, color, num_terms = 4): truncated_numbers = numbers[:num_terms] output = str(chi_func(numbers[0])) result = TexMobject(*it.chain(*[ ["\\chi(", str(n), ")", "="] for n in truncated_numbers ] + [ ["\\cdots =", output] ])) result.inputs = VGroup() for n in truncated_numbers: num_mob = result.get_part_by_tex(str(n), substring = False) num_mob.set_color(color) result.inputs.add(num_mob) result.set_color_by_tex(output, color, substring = False) return result def number_to_color(self, n): output = chi_func(n) if n == 1: return self.colors[0] elif n == -1: return self.colors[1] else: return self.colors[2] class WriteCountingRuleWithChi(SummarizeCountingRule): CONFIG = { "colors" : [GREEN, RED, YELLOW] } def construct(self): self.add_count_words() self.draw_circle() self.add_full_screen_rect() self.add_factorization_and_rule() self.write_chi_expression() self.walk_through_expression_terms() self.circle_four() def add_factorization_and_rule(self): factorization = TexMobject( "N", "=", "2", "^2", "\\cdot", "3", "^4", "\\cdot", "5", "^3", ) for tex, color in zip(["5", "3", "2"], self.colors): factorization.set_color_by_tex(tex, color, substring = False) factorization.to_edge(UP) factorization.shift(LEFT) count = VGroup( TexMobject("=", "4"), TexMobject("(", "1", ")"), TexMobject("(", "1", ")"), TexMobject("(", "3+1", ")"), ) count.arrange(RIGHT, buff = SMALL_BUFF) for i, color in zip([3, 2, 1], self.colors): count[i][1].set_color(color) count.next_to( factorization.get_part_by_tex("="), DOWN, buff = LARGE_BUFF, aligned_edge = LEFT ) self.play( FadeIn(factorization), self.count_words.next_to, count, LEFT ) self.wait() self.play(*[ ReplacementTransform( VGroup(factorization.get_part_by_tex( tex, substring = False )).copy(), part ) for tex, part in zip(["=", "2", "3", "5"], count) ]) self.wait() self.factorization = factorization self.count = count def write_chi_expression(self): equals_four = TexMobject("=", "4") expression = VGroup(equals_four) for n, k, color in zip([2, 3, 5], [2, 4, 3], reversed(self.colors)): args = ["(", "\\chi(", "1", ")", "+"] for i in range(1, k+1): args += ["\\chi(", str(n), "^%d"%i, ")", "+"] args[-1] = ")" factor = TexMobject(*args) factor.set_color_by_tex(str(n), color, substring = False) factor.set_color_by_tex("1", color, substring = False) factor.scale(0.8) expression.add(factor) expression.arrange( DOWN, buff = MED_SMALL_BUFF, aligned_edge = LEFT ) equals_four.next_to(expression[1], LEFT, SMALL_BUFF) expression.shift( self.count[0].get_center() + LARGE_BUFF*DOWN -\ equals_four.get_center() ) count_copy = self.count.copy() self.play(*[ ApplyMethod( c_part.move_to, e_part, LEFT, path_arc = -np.pi/2, run_time = 2 ) for c_part, e_part in zip(count_copy, expression) ]) self.wait() self.play(ReplacementTransform( count_copy, expression, run_time = 2 )) self.wait() self.chi_expression = expression def walk_through_expression_terms(self): rect = FullScreenFadeRectangle() groups = [ VGroup( self.chi_expression[index], self.count[index], self.factorization.get_part_by_tex(tex1, substring = False), self.factorization.get_part_by_tex(tex2, substring = False), ) for index, tex1, tex2 in [ (-1, "5", "^3"), (-2, "3", "^4"), (-3, "2", "^2") ] ] evaluation_strings = [ "(1+1+1+1)", "(1-1+1-1+1)", "(1+0+0)", ] for group, tex in zip(groups, evaluation_strings): chi_sum, count, base, exp = group brace = Brace(chi_sum, DOWN) evaluation = brace.get_tex(*tex) evaluation.set_color(base.get_color()) evaluation_rect = BackgroundRectangle(evaluation) self.play(FadeIn(rect), Animation(group)) self.play(GrowFromCenter(brace)) self.play( FadeIn(evaluation_rect), ReplacementTransform(chi_sum.copy(), evaluation), ) self.wait(2) self.play(Indicate(count, color = PINK)) self.wait() if base.get_tex_string() is "3": new_exp = TexMobject("3") new_exp.replace(exp) count_num = count[1] new_count = TexMobject("0") new_count.replace(count_num, dim_to_match = 1) new_count.set_color(count_num.get_color()) evaluation_point = VectorizedPoint(evaluation[-4].get_right()) chi_sum_point = VectorizedPoint(chi_sum[-7].get_right()) new_brace = Brace(VGroup(*chi_sum[:-6]), DOWN) to_save = [brace, exp, evaluation, count_num, chi_sum] for mob in to_save: mob.save_state() self.play(FocusOn(exp)) self.play(Transform(exp, new_exp)) self.play( Transform(brace, new_brace), Transform( VGroup(*evaluation[-3:-1]), evaluation_point ), evaluation[-1].next_to, evaluation_point, RIGHT, SMALL_BUFF, Transform( VGroup(*chi_sum[-6:-1]), chi_sum_point ), chi_sum[-1].next_to, chi_sum_point, RIGHT, SMALL_BUFF ) self.play(Transform(count_num, new_count)) self.play(Indicate(count_num, color = PINK)) self.wait() self.play(*[mob.restore for mob in to_save]) self.play( FadeOut(VGroup( rect, brace, evaluation_rect, evaluation )), Animation(group) ) def circle_four(self): four = self.chi_expression[0][1] rect = SurroundingRectangle(four) self.revert_to_original_skipping_status() self.play(ShowCreation(rect)) self.wait(3) class WeAreGettingClose(TeacherStudentsScene): def construct(self): self.teacher_says("We're getting close...") self.change_student_modes(*["hooray"]*3) self.wait(2) class ExpandCountWith45(SummarizeCountingRule): CONFIG = { "N_str" : "45", "coords_list" : [(3, 6), (6, 3)], "radius_squared" : 45, "y_radius" : 7, "rect_opacity" : 0.75, } def construct(self): self.add_count_words() self.draw_circle() self.add_full_screen_rect() self.add_factorization_and_count() self.expand_expression() self.show_divisor_sum() def add_factorization_and_count(self): factorization = TexMobject( "45", "=", "3", "^2", "\\cdot", "5", ) for tex, color in zip(["5", "3",], [GREEN, RED]): factorization.set_color_by_tex(tex, color, substring = False) factorization.to_edge(UP) factorization.shift(1.7*LEFT) equals_four = TexMobject("=", "4") expression = VGroup(equals_four) for n, k, color in zip([3, 5], [2, 1], [RED, GREEN]): args = ["("] ["\\chi(1)", "+"] for i in range(k+1): if i == 0: input_str = "1" elif i == 1: input_str = str(n) else: input_str = "%d^%d"%(n, i) args += ["\\chi(%s)"%input_str, "+"] args[-1] = ")" factor = TexMobject(*args) for part in factor[1::2]: part[2].set_color(color) factor.scale(0.8) expression.add(factor) expression.arrange(RIGHT, buff = SMALL_BUFF) expression.next_to( factorization[1], DOWN, buff = LARGE_BUFF, aligned_edge = LEFT, ) braces = VGroup(*[ Brace(part, UP) for part in expression[1:] ]) for brace, num, color in zip(braces, [1, 2], [RED, GREEN]): num_mob = brace.get_tex(str(num), buff = SMALL_BUFF) num_mob.set_color(color) brace.add(num_mob) self.play( FadeIn(factorization), self.count_words.next_to, expression, LEFT ) self.wait() self.play(*[ ReplacementTransform( VGroup(factorization.get_part_by_tex( tex, substring = False )).copy(), part ) for tex, part in zip(["=", "3", "5"], expression) ]) self.play(FadeIn(braces)) self.wait() self.chi_expression = expression self.factorization = factorization def expand_expression(self): equals, four, lp, rp = list(map(TexMobject, [ "=", "4", "\\big(", "\\big)" ])) expansion = VGroup(equals, four, lp) chi_pairs = list(it.product(*[ factor[1::2] for factor in self.chi_expression[1:] ])) num_pairs = list(it.product([1, 3, 9], [1, 5])) products = list(it.starmap(op.mul, num_pairs)) sorted_indices = np.argsort(products) mover_groups = [VGroup(), VGroup()] plusses = VGroup() prime_pairs = VGroup() for index in sorted_indices: pair = chi_pairs[index] prime_pair = VGroup() for chi, movers in zip(pair, mover_groups): mover = chi.copy() mover.generate_target() expansion.add(mover.target) movers.add(mover) prime_pair.add(mover.target[2]) prime_pairs.add(prime_pair) if index != sorted_indices[-1]: plus = TexMobject("+") plusses.add(plus) expansion.add(plus) expansion.add(rp) expansion.arrange(RIGHT, buff = SMALL_BUFF) expansion.set_width(FRAME_WIDTH - LARGE_BUFF) expansion.next_to(ORIGIN, UP) rect = BackgroundRectangle(expansion) rect.stretch_in_place(1.5, 1) self.play( FadeIn(rect), *[ ReplacementTransform( self.chi_expression[i][j].copy(), mob ) for i, j, mob in [ (0, 0, equals), (0, 1, four), (1, 0, lp), (2, -1, rp), ] ] ) for movers in mover_groups: self.wait() self.play(movers.next_to, rect, DOWN) self.play(*list(map(MoveToTarget, movers))) self.play(Write(plusses)) self.wait() self.expansion = expansion self.prime_pairs = prime_pairs def show_divisor_sum(self): equals, four, lp, rp = list(map(TexMobject, [ "=", "4", "\\big(", "\\big)" ])) divisor_sum = VGroup(equals, four, lp) num_pairs = list(it.product([1, 3, 9], [1, 5])) products = list(it.starmap(op.mul, num_pairs)) products.sort() color = BLACK product_mobs = VGroup() chi_mobs = VGroup() for product in products: chi_mob = TexMobject("\\chi(", str(product), ")") product_mob = chi_mob.get_part_by_tex(str(product)) product_mob.set_color(color) product_mobs.add(product_mob) divisor_sum.add(chi_mob) chi_mobs.add(chi_mob) if product != products[-1]: divisor_sum.add(TexMobject("+")) divisor_sum.add(rp) divisor_sum.arrange(RIGHT, buff = SMALL_BUFF) divisor_sum.next_to(self.expansion, DOWN, MED_LARGE_BUFF) rect = BackgroundRectangle(divisor_sum) prime_pairs = self.prime_pairs.copy() for prime_pair, product_mob in zip(prime_pairs, product_mobs): prime_pair.target = product_mob.copy() prime_pair.target.set_color(YELLOW) braces = VGroup(*[Brace(m, DOWN) for m in chi_mobs]) for brace, product in zip(braces, products): value = brace.get_tex(str(chi_func(product))) brace.add(value) self.play( FadeIn(rect), Write(divisor_sum, run_time = 2) ) self.play(OldLaggedStart( MoveToTarget, prime_pairs, run_time = 4, lag_ratio = 0.25, )) self.remove(prime_pairs) product_mobs.set_color(YELLOW) self.wait(2) self.play(OldLaggedStart( ApplyMethod, product_mobs, lambda m : (m.shift, MED_LARGE_BUFF*DOWN), rate_func = there_and_back )) self.play(FadeIn( braces, run_time = 2, lag_ratio = 0.5, )) self.wait(2) class CountLatticePointsInBigCircle(LatticePointScene): CONFIG = { "y_radius" : 2*11, "max_lattice_point_radius" : 10, "dot_radius" : 0.05 } def construct(self): self.resize_plane() self.introduce_points() self.show_rings() self.ignore_center_dot() def resize_plane(self): self.plane.set_stroke(width = 2) self.plane.scale(2) self.lattice_points.scale(2) for point in self.lattice_points: point.scale_in_place(0.5) def introduce_points(self): circle = self.get_circle(radius = self.max_lattice_point_radius) radius = Line(ORIGIN, circle.get_right()) radius.set_color(RED) R = TexMobject("R").next_to(radius, UP) R_rect = BackgroundRectangle(R) R_group = VGroup(R_rect, R) pi_R_squared = TexMobject("\\pi", "R", "^2") pi_R_squared.next_to(ORIGIN, UP) pi_R_squared.to_edge(RIGHT) pi_R_squared_rect = BackgroundRectangle(pi_R_squared) pi_R_squared_group = VGroup(pi_R_squared_rect, pi_R_squared) self.play(*list(map(FadeIn, [circle, radius, R_group]))) self.add_foreground_mobject(R_group) self.draw_lattice_points() self.wait() self.play( FadeOut(R_rect), FadeIn(pi_R_squared_rect), ReplacementTransform(R, pi_R_squared.get_part_by_tex("R")), Write(VGroup(*[ part for part in pi_R_squared if part is not pi_R_squared.get_part_by_tex("R") ])) ) self.remove(R_group) self.add_foreground_mobject(pi_R_squared_group) self.wait() self.circle = circle self.radius = radius def show_rings(self): N_range = list(range(self.max_lattice_point_radius**2)) rings = VGroup(*[ self.get_circle(radius = np.sqrt(N)) for N in N_range ]) rings.set_color_by_gradient(TEAL, GREEN) rings.set_stroke(width = 2) dot_groups = VGroup(*[ self.get_lattice_points_on_r_squared_circle(N) for N in N_range ]) radicals = self.get_radicals() self.play( OldLaggedStart(FadeIn, rings), Animation(self.lattice_points), OldLaggedStart(FadeIn, radicals), run_time = 3 ) self.add_foreground_mobject(radicals) self.play( OldLaggedStart( ApplyMethod, dot_groups, lambda m : (m.set_stroke, PINK, 5), rate_func = there_and_back, run_time = 4, lag_ratio = 0.1, ), ) self.wait() self.rings = rings def ignore_center_dot(self): center_dot = self.lattice_points[0] circle = Circle(color = RED) circle.replace(center_dot) circle.scale_in_place(2) arrow = Arrow(ORIGIN, UP+RIGHT, color = RED) arrow.next_to(circle, DOWN+LEFT, SMALL_BUFF) new_max = 2*self.max_lattice_point_radius new_dots = VGroup(*[ Dot( self.plane.coords_to_point(x, y), color = self.dot_color, radius = self.dot_radius, ) for x in range(-new_max, new_max+1) for y in range(-new_max, new_max+1) if (x**2 + y**2) > self.max_lattice_point_radius**2 if (x**2 + y**2) < new_max**2 ]) new_dots.sort(get_norm) self.play(*list(map(ShowCreation, [circle, arrow]))) self.play(*list(map(FadeOut, [circle, arrow]))) self.play(FadeOut(center_dot)) self.lattice_points.remove(center_dot) self.wait() self.play(*[ ApplyMethod(m.scale, 0.5) for m in [ self.plane, self.circle, self.radius, self.rings, self.lattice_points ] ]) new_dots.scale(0.5) self.play(FadeOut(self.rings)) self.play( ApplyMethod( VGroup(self.circle, self.radius).scale_in_place, 2, rate_func=linear, ), OldLaggedStart( DrawBorderThenFill, new_dots, stroke_width = 4, stroke_color = PINK, lag_ratio = 0.2, ), run_time = 4, ) self.wait(2) ##### @staticmethod def get_radicals(): radicals = VGroup(*[ TexMobject("\\sqrt{%d}"%N) for N in range(1, 13) ]) radicals.add( TexMobject("\\vdots"), TexMobject("\\sqrt{R^2}") ) radicals.arrange(DOWN, buff = MED_SMALL_BUFF) radicals.set_height(FRAME_HEIGHT - MED_LARGE_BUFF) radicals.to_edge(DOWN, buff = MED_SMALL_BUFF) radicals.to_edge(LEFT) for radical in radicals: radical.add_background_rectangle() return radicals class AddUpGrid(Scene): def construct(self): self.add_radicals() self.add_row_lines() self.add_chi_sums() self.put_four_in_corner() self.talk_through_rows() self.organize_into_columns() self.add_count_words() self.collapse_columns() self.factor_out_R() self.show_chi_sum_values() self.compare_to_pi_R_squared() self.celebrate() def add_radicals(self): self.radicals = CountLatticePointsInBigCircle.get_radicals() self.add(self.radicals) def add_row_lines(self): h_line = Line(LEFT, RIGHT).scale(FRAME_X_RADIUS - MED_LARGE_BUFF) h_line.set_stroke(WHITE, 1) row_lines = VGroup(*[ h_line.copy().next_to( radical, DOWN, buff = SMALL_BUFF, aligned_edge = LEFT ) for radical in self.radicals ]) row_lines[-2].shift( row_lines[-1].get_left()[0]*RIGHT -\ row_lines[-2].get_left()[0]*RIGHT ) self.play(OldLaggedStart(ShowCreation, row_lines)) self.wait() self.row_lines = row_lines def add_chi_sums(self): chi_sums = VGroup() chi_mobs = VGroup() plusses = VGroup() fours = VGroup() parens = VGroup() arrows = VGroup() for N, radical in zip(list(range(1, 13)), self.radicals): arrow, four, lp, rp = list(map(TexMobject, [ "\\Rightarrow", "4", "\\big(", "\\big)" ])) fours.add(four) parens.add(lp, rp) arrows.add(arrow) chi_sum = VGroup(arrow, four, lp) for d in range(1, N+1): if N%d != 0: continue chi_mob = TexMobject("\\chi(", str(d), ")") chi_mob[1].set_color(YELLOW) chi_mob.d = d chi_mobs.add(chi_mob) chi_sum.add(chi_mob) if d != N: plus = TexMobject("+") plus.chi_mob = chi_mob plusses.add(plus) chi_sum.add(plus) chi_sum.add(rp) chi_sum.arrange(RIGHT, buff = SMALL_BUFF) chi_sum.scale(0.7) chi_sum.next_to(radical, RIGHT) chi_sums.add(chi_sum) radical.chi_sum = chi_sum self.play(OldLaggedStart( Write, chi_sums, run_time = 5, rate_func = lambda t : t, )) self.wait() digest_locals(self, [ "chi_sums", "chi_mobs", "plusses", "fours", "parens", "arrows", ]) def put_four_in_corner(self): corner_four = TexMobject("4") corner_four.to_corner(DOWN+RIGHT, buff = MED_SMALL_BUFF) rect = SurroundingRectangle(corner_four, color = BLUE) corner_four.rect = rect self.play( ReplacementTransform( self.fours, VGroup(corner_four), run_time = 2, ), FadeOut(self.parens) ) self.play(ShowCreation(rect)) self.corner_four = corner_four def talk_through_rows(self): rect = Rectangle( stroke_width = 0, fill_color = BLUE_C, fill_opacity = 0.3, ) rect.stretch_to_fit_width( VGroup(self.radicals, self.chi_mobs).get_width() ) rect.stretch_to_fit_height(self.radicals[0].get_height()) composite_rects, prime_rects = [ VGroup(*[ rect.copy().move_to(self.radicals[N-1], LEFT) for N in numbers ]) for numbers in ([6, 12], [2, 3, 5, 7, 11]) ] prime_rects.set_color(GREEN) randy = Randolph().flip() randy.next_to(self.chi_mobs, RIGHT) self.play(FadeIn(randy)) self.play(randy.change_mode, "pleading") self.play( FadeIn(composite_rects), randy.look_at, composite_rects.get_bottom() ) self.wait(2) self.play( FadeOut(composite_rects), FadeIn(prime_rects), randy.look_at, prime_rects.get_top(), ) self.play(Blink(randy)) self.wait() self.play(*list(map(FadeOut, [prime_rects, randy]))) def organize_into_columns(self): left_x = self.arrows.get_right()[0] + SMALL_BUFF spacing = self.chi_mobs[-1].get_width() + SMALL_BUFF for chi_mob in self.chi_mobs: y = chi_mob.get_left()[1] x = left_x + (chi_mob.d - 1)*spacing chi_mob.generate_target() chi_mob.target.move_to(x*RIGHT + y*UP, LEFT) for plus in self.plusses: plus.generate_target() plus.target.scale(0.5) plus.target.next_to( plus.chi_mob.target, RIGHT, SMALL_BUFF ) self.play(*it.chain( list(map(MoveToTarget, self.chi_mobs)), list(map(MoveToTarget, self.plusses)), ), run_time = 2) self.wait() def add_count_words(self): rect = Rectangle( stroke_color = WHITE, stroke_width = 2, fill_color = average_color(BLUE_E, BLACK), fill_opacity = 1, height = 1.15, width = FRAME_WIDTH - 2*MED_SMALL_BUFF, ) rect.move_to(3*LEFT, LEFT) rect.to_edge(UP, buff = SMALL_BUFF) words = TextMobject("Total") words.scale(0.8) words.next_to(rect.get_left(), RIGHT, SMALL_BUFF) approx = TexMobject("\\approx") approx.scale(0.7) approx.next_to(words, RIGHT, SMALL_BUFF) words.add(approx) self.play(*list(map(FadeIn, [rect, words]))) self.wait() self.count_rect = rect self.count_words = words def collapse_columns(self): chi_mob_columns = [VGroup() for i in range(12)] for chi_mob in self.chi_mobs: chi_mob_columns[chi_mob.d - 1].add(chi_mob) full_sum = VGroup() for d in range(1, 7): R_args = ["{R^2"] if d != 1: R_args.append("\\over %d}"%d) term = VGroup( TexMobject(*R_args), TexMobject("\\chi(", str(d), ")"), TexMobject("+") ) term.arrange(RIGHT, SMALL_BUFF) term[1][1].set_color(YELLOW) full_sum.add(term) full_sum.arrange(RIGHT, SMALL_BUFF) full_sum.scale(0.7) full_sum.next_to(self.count_words, RIGHT, SMALL_BUFF) for column, term in zip(chi_mob_columns, full_sum): rect = SurroundingRectangle(column) rect.stretch_to_fit_height(FRAME_HEIGHT) rect.move_to(column, UP) rect.set_stroke(width = 0) rect.set_fill(YELLOW, 0.3) self.play(FadeIn(rect)) self.wait() self.play( ReplacementTransform( column.copy(), VGroup(term[1]), run_time = 2 ), Write(term[0]), Write(term[2]), ) self.wait() if term is full_sum[2]: vect = sum([ self.count_rect.get_left()[0], FRAME_X_RADIUS, -MED_SMALL_BUFF, ])*LEFT self.play(*[ ApplyMethod(m.shift, vect) for m in [ self.count_rect, self.count_words, ]+list(full_sum[:3]) ]) VGroup(*full_sum[3:]).shift(vect) self.play(FadeOut(rect)) self.full_sum = full_sum def factor_out_R(self): self.corner_four.generate_target() R_squared = TexMobject("R^2") dots = TexMobject("\\cdots") lp, rp = list(map(TexMobject, ["\\big(", "\\big)"])) new_sum = VGroup( self.corner_four.target, R_squared, lp ) R_fracs, chi_terms, plusses = full_sum_parts = [ VGroup(*[term[i] for term in self.full_sum]) for i in range(3) ] targets = [] for part in full_sum_parts: part.generate_target() targets.append(part.target) for R_frac, chi_term, plus in zip(*targets): chi_term.scale(0.9) chi_term.move_to(R_frac[0], DOWN) if R_frac is R_fracs.target[0]: new_sum.add(chi_term) else: new_sum.add(VGroup(chi_term, R_frac[1])) new_sum.add(plus) new_sum.add(dots) new_sum.add(rp) new_sum.arrange(RIGHT, buff = SMALL_BUFF) new_sum.next_to(self.count_words, RIGHT, SMALL_BUFF) R_squared.shift(0.5*SMALL_BUFF*UP) R_movers = VGroup() for R_frac in R_fracs.target: if R_frac is R_fracs.target[0]: mover = R_frac else: mover = R_frac[0] Transform(mover, R_squared).update(1) R_movers.add(mover) self.play(*it.chain( list(map(Write, [lp, rp, dots])), list(map(MoveToTarget, full_sum_parts)), ), run_time = 2) self.remove(R_movers) self.add(R_squared) self.wait() self.play( MoveToTarget(self.corner_four, run_time = 2), FadeOut(self.corner_four.rect) ) self.wait(2) self.remove(self.full_sum, self.corner_four) self.add(new_sum) self.new_sum = new_sum def show_chi_sum_values(self): alt_rhs = TexMobject( "\\approx", "4", "R^2", "\\left(1 - \\frac{1}{3} + \\frac{1}{5}" + \ "-\\frac{1}{7} + \\frac{1}{9} - \\frac{1}{11}" + \ "+ \\cdots \\right)", ) alt_rhs.scale(0.9) alt_rhs.next_to( self.count_words[-1], DOWN, buff = LARGE_BUFF, aligned_edge = LEFT ) self.play( *list(map(FadeOut, [ self.chi_mobs, self.plusses, self.arrows, self.radicals, self.row_lines ])) + [ FadeOut(self.count_rect), Animation(self.new_sum), Animation(self.count_words), ] ) self.play(Write(alt_rhs)) self.wait(2) self.alt_rhs = alt_rhs def compare_to_pi_R_squared(self): approx, pi, R_squared = area_rhs = TexMobject( "\\approx", "\\pi", "R^2" ) area_rhs.next_to(self.alt_rhs, RIGHT) brace = Brace( VGroup(self.alt_rhs, area_rhs), DOWN ) brace.add(brace.get_text( "Arbitrarily good as $R \\to \\infty$" )) pi_sum = TexMobject( "4", self.alt_rhs[-1].get_tex_string(), "=", "\\pi" ) pi_sum.scale(0.9) pi = pi_sum.get_part_by_tex("pi") pi.scale(2, about_point = pi.get_left()) pi.set_color(YELLOW) pi_sum.shift( self.alt_rhs[-1].get_bottom(), MED_SMALL_BUFF*DOWN, -pi_sum[1].get_top() ) self.play(Write(area_rhs)) self.wait() self.play(FadeIn(brace)) self.wait(2) self.play(FadeOut(brace)) self.play(*[ ReplacementTransform(m.copy(), pi_sum_part) for pi_sum_part, m in zip(pi_sum, [ self.alt_rhs.get_part_by_tex("4"), self.alt_rhs[-1], area_rhs[0], area_rhs[1], ]) ]) def celebrate(self): creatures = TeacherStudentsScene().get_pi_creatures() self.play(FadeIn(creatures)) self.play(*[ ApplyMethod(pi.change, "hooray", self.alt_rhs) for pi in creatures ]) self.wait() for i in 0, 2, 3: self.play(Blink(creatures[i])) self.wait() class IntersectionOfTwoFields(TeacherStudentsScene): def construct(self): circles = VGroup() for vect, color, adj in (LEFT, BLUE, "Algebraic"), (RIGHT, YELLOW, "Analytic"): circle = Circle(color = WHITE) circle.set_fill(color, opacity = 0.3) circle.stretch_to_fit_width(7) circle.stretch_to_fit_height(4) circle.shift(FRAME_X_RADIUS*vect/3.0 + LEFT) title = TextMobject("%s \\\\ number theory"%adj) title.scale(0.7) title.move_to(circle) title.to_edge(UP, buff = SMALL_BUFF) circle.next_to(title, DOWN, SMALL_BUFF) title.set_color(color) circle.title = title circles.add(circle) new_number_systems = TextMobject( "New \\\\ number systems" ) gaussian_integers = TextMobject( "e.g. Gaussian \\\\ integers" ) new_number_systems.next_to(circles[0].get_top(), DOWN, MED_SMALL_BUFF) new_number_systems.shift(MED_LARGE_BUFF*(DOWN+2*LEFT)) gaussian_integers.next_to(new_number_systems, DOWN) gaussian_integers.set_color(BLUE) circles[0].words = VGroup(new_number_systems, gaussian_integers) zeta = TexMobject("\\zeta(s) = \\sum_{n=1}^\\infty \\frac{1}{n^s}") L_function = TexMobject( "L(s, \\chi) = \\sum_{n=1}^\\infty \\frac{\\chi(n)}{n^s}" ) for mob in zeta, L_function: mob.scale(0.8) zeta.next_to(circles[1].get_top(), DOWN, MED_LARGE_BUFF) zeta.shift(MED_LARGE_BUFF*RIGHT) L_function.next_to(zeta, DOWN, MED_LARGE_BUFF) L_function.set_color(YELLOW) circles[1].words = VGroup(zeta, L_function) mid_words = TextMobject("Where\\\\ we \\\\ were") mid_words.scale(0.7) mid_words.move_to(circles) for circle in circles: self.play( Write(circle.title, run_time = 2), DrawBorderThenFill(circle, run_time = 2), self.teacher.change_mode, "raise_right_hand" ) self.wait() for circle in circles: for word in circle.words: self.play( Write(word, run_time = 2), self.teacher.change, "speaking", *[ ApplyMethod(pi.change, "pondering") for pi in self.get_students() ] ) self.wait() self.play( Write(mid_words), self.teacher.change, "raise_right_hand" ) self.change_student_modes( *["thinking"]*3, look_at_arg = mid_words ) self.wait(3) class LeibnizPatreonThanks(PatreonThanks): CONFIG = { "specific_patrons" : [ "Ali Yahya", "Burt Humburg", "CrypticSwarm", "Erik Sundell", "Yana Chernobilsky", "Kaustuv DeBiswas", "Kathryn Schmiedicke", "Karan Bhargava", "Ankit Agarwal", "Yu Jun", "Dave Nicponski", "Damion Kistler", "Juan Benet", "Othman Alikhan", "Markus Persson", "Yoni Nazarathy", "Joseph John Cox", "Dan Buchoff", "Luc Ritchie", "Guido Gambardella", "Julian Pulgarin", "John Haley", "Jeff Linse", "Suraj Pratap", "Cooper Jones", "Ryan Dahl", "Ahmad Bamieh", "Mark Govea", "Robert Teed", "Jason Hise", "Meshal Alshammari", "Bernd Sing", "Nils Schneider", "James Thornton", "Mustafa Mahdi", "Mathew Bramson", "Jerry Ling", "Vecht", "Shimin Kuang", "Rish Kundalia", "Achille Brighton", "Ripta Pasay", ], } class Sponsorship(PiCreatureScene): def construct(self): morty = self.pi_creature logo = SVGMobject( file_name = "remix_logo", ) logo.set_height(1) logo.center() logo.set_stroke(width = 0) logo.set_fill(BLUE_D, 1) VGroup(*logo[6:]).set_color_by_gradient(BLUE_B, BLUE_E) logo.next_to(morty.get_corner(UP+LEFT), UP) url = TextMobject("www.remix.com") url.to_corner(UP+LEFT) rect = ScreenRectangle(height = 5) rect.next_to(url, DOWN, aligned_edge = LEFT) self.play( morty.change_mode, "raise_right_hand", OldLaggedStart(DrawBorderThenFill, logo, run_time = 3) ) self.wait() self.play( ShowCreation(rect), logo.scale, 0.8, logo.to_corner, UP+RIGHT, morty.change, "happy" ) self.wait() self.play(Write(url)) self.wait(3) for mode in "confused", "pondering", "happy": self.play(morty.change_mode, mode) self.wait(3) class Thumbnail(Scene): def construct(self): randy = Randolph() randy.set_height(5) body_copy = randy.body.copy() body_copy.set_stroke(YELLOW, width = 3) body_copy.set_fill(opacity = 0) self.add(randy) primes = [ n for n in range(2, 1000) if all(n%k != 0 for k in list(range(2, n))) ] prime_mobs = VGroup() x_spacing = 1.7 y_spacing = 1.5 n_rows = 10 n_cols = 8 for i, prime in enumerate(primes[:n_rows*n_cols]): prime_mob = Integer(prime) prime_mob.scale(1.5) x = i%n_cols y = i//n_cols prime_mob.shift(x*x_spacing*RIGHT + y*y_spacing*DOWN) prime_mobs.add(prime_mob) prime_mob.set_color({ -1 : YELLOW, 0 : RED, 1 : BLUE_C, }[chi_func(prime)]) prime_mobs.center().to_edge(UP) for i in range(7): self.add(SurroundingRectangle( VGroup(*prime_mobs[n_cols*i:n_cols*(i+1)]), fill_opacity = 0.7, fill_color = BLACK, stroke_width = 0, buff = 0, )) self.add(prime_mobs) self.add(body_copy)