3b1b-manim/old_projects/leibniz.py
2019-02-08 15:53:27 -08:00

4827 lines
151 KiB
Python

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)