import scipy.integrate
from manimlib.imports import *

OUTPUT_DIRECTORY = "bayes/part1"

HYPOTHESIS_COLOR = YELLOW
NOT_HYPOTHESIS_COLOR = GREY
EVIDENCE_COLOR1 = BLUE_C
EVIDENCE_COLOR2 = BLUE_E
NOT_EVIDENCE_COLOR1 = GREY
NOT_EVIDENCE_COLOR2 = DARK_GREY

#


def get_bayes_formula(expand_denominator=False):
    t2c = {
        "{H}": HYPOTHESIS_COLOR,
        "{\\neg H}": NOT_HYPOTHESIS_COLOR,
        "{E}": EVIDENCE_COLOR1,
    }
    substrings_to_isolate = ["P", "\\over", "=", "\\cdot", "+"]

    tex = "P({H} | {E}) = {P({H}) P({E} | {H}) \\over "
    if expand_denominator:
        tex += "P({H}) P({E} | {H}) + P({\\neg H}) \\cdot P({E} | {\\neg H})}"
    else:
        tex += "P({E})}"

    formula = TexMobject(
        tex,
        tex_to_color_map=t2c,
        substrings_to_isolate=substrings_to_isolate,
    )

    formula.posterior = formula[:6]
    formula.prior = formula[8:12]
    formula.likelihood = formula[13:19]

    if expand_denominator:
        pass
        formula.denom_prior = formula[20:24]
        formula.denom_likelihood = formula[25:31]
        formula.denom_anti_prior = formula[32:36]
        formula.denom_anti_likelihood = formula[37:42]
    else:
        formula.p_evidence = formula[20:]

    return formula


class BayesDiagram(VGroup):
    CONFIG = {
        "height": 2,
        "square_style": {
            "fill_color": DARK_GREY,
            "fill_opacity": 1,
            "stroke_color": WHITE,
            "stroke_width": 2,
        },
        "rect_style": {
            "stroke_color": WHITE,
            "stroke_width": 1,
            "fill_opacity": 1,
        },
        "hypothesis_color": HYPOTHESIS_COLOR,
        "not_hypothesis_color": NOT_HYPOTHESIS_COLOR,
        "evidence_color1": EVIDENCE_COLOR1,
        "evidence_color2": EVIDENCE_COLOR2,
        "not_evidence_color1": NOT_EVIDENCE_COLOR1,
        "not_evidence_color2": NOT_EVIDENCE_COLOR2,
        "prior_rect_direction": DOWN,
    }

    def __init__(self, prior, likelihood, antilikelihood, **kwargs):
        super().__init__(**kwargs)
        square = Square(side_length=self.height)
        square.set_style(**self.square_style)

        # Create all rectangles
        h_rect, nh_rect, he_rect, nhe_rect, hne_rect, nhne_rect = [
            square.copy().set_style(**self.rect_style)
            for x in range(6)
        ]

        # Add as attributes
        self.square = square
        self.h_rect = h_rect  # Hypothesis
        self.nh_rect = nh_rect  # Not hypothesis
        self.he_rect = he_rect  # Hypothesis and evidence
        self.hne_rect = hne_rect  # Hypothesis and not evidence
        self.nhe_rect = nhe_rect  # Not hypothesis and evidence
        self.nhne_rect = nhne_rect  # Not hypothesis and not evidence

        # Stretch the rectangles
        for rect in h_rect, he_rect, hne_rect:
            rect.stretch(prior, 0, about_edge=LEFT)
        for rect in nh_rect, nhe_rect, nhne_rect:
            rect.stretch(1 - prior, 0, about_edge=RIGHT)

        he_rect.stretch(likelihood, 1, about_edge=DOWN)
        hne_rect.stretch(1 - likelihood, 1, about_edge=UP)
        nhe_rect.stretch(antilikelihood, 1, about_edge=DOWN)
        nhne_rect.stretch(1 - antilikelihood, 1, about_edge=UP)

        # Color the rectangles
        h_rect.set_fill(self.hypothesis_color)
        nh_rect.set_fill(self.not_hypothesis_color)
        he_rect.set_fill(self.evidence_color1)
        hne_rect.set_fill(self.not_evidence_color1)
        nhe_rect.set_fill(self.evidence_color2)
        nhne_rect.set_fill(self.not_evidence_color2)

        # Add them
        self.hypothesis_split = VGroup(h_rect, nh_rect)
        self.evidence_split = VGroup(he_rect, hne_rect, nhe_rect, nhne_rect)

        # Don't add hypothesis split by default
        self.add(self.square, self.hypothesis_split, self.evidence_split)
        self.square.set_opacity(0)
        self.hypothesis_split.set_opacity(0)

    def add_brace_attrs(self, buff=SMALL_BUFF):
        braces = self.braces = self.create_braces(buff)
        self.braces_buff = buff
        attrs = [
            "h_brace",
            "nh_brace",
            "he_brace",
            "hne_brace",
            "nhe_brace",
            "nhne_brace",
        ]
        for brace, attr in zip(braces, attrs):
            setattr(self, attr, brace)
        return self

    def create_braces(self, buff=SMALL_BUFF):
        kw = {
            "buff": buff,
            "min_num_quads": 1,
        }
        return VGroup(
            Brace(self.h_rect, self.prior_rect_direction, **kw),
            Brace(self.nh_rect, self.prior_rect_direction, **kw),
            Brace(self.he_rect, LEFT, **kw),
            Brace(self.hne_rect, LEFT, **kw),
            Brace(self.nhe_rect, RIGHT, **kw),
            Brace(self.nhne_rect, RIGHT, **kw),
        )

    def refresh_braces(self):
        if hasattr(self, "braces"):
            self.braces.become(
                self.create_braces(self.braces_buff)
            )
        return self

    def set_prior(self, new_prior):
        p = new_prior
        q = 1 - p
        full_width = self.square.get_width()

        left_rects = [self.h_rect, self.he_rect, self.hne_rect]
        right_rects = [self.nh_rect, self.nhe_rect, self.nhne_rect]

        for group, vect, value in [(left_rects, LEFT, p), (right_rects, RIGHT, q)]:
            for rect in group:
                rect.set_width(
                    value * full_width,
                    stretch=True,
                    about_edge=vect,
                )

        self.refresh_braces()
        return self

    def general_set_likelihood(self, new_likelihood, low_rect, high_rect):
        height = self.square.get_height()

        low_rect.set_height(
            new_likelihood * height,
            stretch=True,
            about_edge=DOWN,
        )
        high_rect.set_height(
            (1 - new_likelihood) * height,
            stretch=True,
            about_edge=UP,
        )
        self.refresh_braces()
        return self

    def set_likelihood(self, new_likelihood):
        self.general_set_likelihood(
            new_likelihood,
            self.he_rect,
            self.hne_rect,
        )
        return self

    def set_antilikelihood(self, new_antilikelihood):
        self.general_set_likelihood(
            new_antilikelihood,
            self.nhe_rect,
            self.nhne_rect,
        )
        return self

    def copy(self):
        return self.deepcopy()


class ProbabilityBar(VGroup):
    CONFIG = {
        "color1": BLUE_D,
        "color2": GREY_BROWN,
        "height": 0.5,
        "width": 6,
        "rect_style": {
            "stroke_width": 1,
            "stroke_color": WHITE,
            "fill_opacity": 1,
        },
        "include_braces": False,
        "brace_direction": UP,
        "include_percentages": True,
        "percentage_background_stroke_width": 2,
    }

    def __init__(self, p=0.5, **kwargs):
        super().__init__(**kwargs)
        self.add_backbone()
        self.add_p_tracker(p)
        self.add_bars()
        if self.include_braces:
            self.braces = always_redraw(lambda: self.get_braces())
            self.add(self.braces)
        if self.include_percentages:
            self.percentages = always_redraw(lambda: self.get_percentages())
            self.add(self.percentages)

    def add_backbone(self):
        backbone = Line()
        backbone.set_opacity(0)
        backbone.set_width(self.width)
        self.backbone = backbone
        self.add(backbone)

    def add_p_tracker(self, p):
        self.p_tracker = ValueTracker(p)

    def add_bars(self):
        bars = VGroup(Rectangle(), Rectangle())
        bars.set_height(self.height)
        colors = [self.color1, self.color2]
        for bar, color in zip(bars, colors):
            bar.set_style(**self.rect_style)
            bar.set_fill(color=color)

        bars.add_updater(self.update_bars)
        self.bars = bars
        self.add(bars)

    def update_bars(self, bars):
        vects = [LEFT, RIGHT]
        p = self.p_tracker.get_value()
        values = [p, 1 - p]
        total_width = self.backbone.get_width()
        for bar, vect, value in zip(bars, vects, values):
            bar.set_width(value * total_width, stretch=True)
            bar.move_to(self.backbone, vect)
        return bars

    def get_braces(self):
        return VGroup(*[
            Brace(
                bar,
                self.brace_direction,
                min_num_quads=1,
                buff=SMALL_BUFF,
            )
            for bar in self.bars
        ])

    def get_percentages(self):
        p = self.p_tracker.get_value()
        labels = VGroup(*[
            Integer(value, unit="\\%")
            for value in [
                np.floor(p * 100),
                100 - np.floor(p * 100),
            ]
        ])
        for label, bar in zip(labels, self.bars):
            label.set_height(0.75 * bar.get_height())
            min_width = 0.75 * bar.get_width()
            if label.get_width() > min_width:
                label.set_width(min_width)
            label.move_to(bar)
            label.set_stroke(
                BLACK,
                self.percentage_background_stroke_width,
                background=True
            )
        return labels

    def add_icons(self, *icons, buff=SMALL_BUFF):
        if hasattr(self, "braces"):
            refs = self.braces
        else:
            refs = self.bars

        for icon, ref in zip(icons, refs):
            icon.ref = ref
            icon.add_updater(lambda i: i.next_to(
                i.ref,
                self.brace_direction,
                buff=buff
            ))
        self.icons = VGroup(*icons)
        self.add(self.icons)


class Steve(SVGMobject):
    CONFIG = {
        "file_name": "steve",
        "fill_color": GREY,
        "sheen_factor": 0.5,
        "sheen_direction": UL,
        "stroke_width": 0,
        "height": 3,
        "include_name": True,
        "name": "Steve"
    }

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        if self.include_name:
            self.add_name()

    def add_name(self):
        self.name = TextMobject(self.name)
        self.name.match_width(self)
        self.name.next_to(self, DOWN, SMALL_BUFF)
        self.add(self.name)


class Linda(Steve):
    CONFIG = {
        "file_name": "linda",
        "name": "Linda"
    }


class LibrarianIcon(SVGMobject):
    CONFIG = {
        "file_name": "book",
        "stroke_width": 0,
        "fill_color": LIGHT_GREY,
        "sheen_factor": 0.5,
        "sheen_direction": UL,
        "height": 0.75,
    }


class FarmerIcon(SVGMobject):
    CONFIG = {
        "file_name": "farming",
        "stroke_width": 0,
        "fill_color": GREEN_E,
        "sheen_factor": 0.5,
        "sheen_direction": UL,
        "height": 1.5,
    }


class PitchforkIcon(SVGMobject):
    CONFIG = {
        "file_name": "pitch_fork_and_roll",
        "stroke_width": 0,
        "fill_color": LIGHT_GREY,
        "sheen_factor": 0.5,
        "sheen_direction": UL,
        "height": 1.5,
    }


class Person(SVGMobject):
    CONFIG = {
        "file_name": "person",
        "height": 1.5,
        "stroke_width": 0,
        "fill_opacity": 1,
        "fill_color": LIGHT_GREY,
    }


class Librarian(Person):
    CONFIG = {
        "IconClass": LibrarianIcon,
        "icon_style": {
            "background_stroke_width": 5,
            "background_stroke_color": BLACK,
        },
    }

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        icon = self.IconClass()
        icon.set_style(**self.icon_style)
        icon.match_width(self)
        icon.move_to(self.get_corner(DR), DOWN)
        self.add(icon)


class Farmer(Librarian):
    CONFIG = {
        "IconClass": FarmerIcon,
        "icon_style": {
            "background_stroke_width": 2,
        },
        "fill_color": GREEN,
    }


# Scenes


class Test(Scene):
    def construct(self):
        icon = FarmerIcon()
        icon.scale(2)
        self.add(icon)
        # self.add(get_submobject_index_labels(icon))


# class FullFormulaIndices(Scene):
#     def construct(self):
#         formula = get_bayes_formula(expand_denominator=True)
#         formula.set_width(FRAME_WIDTH - 1)
#         self.add(formula)
#         self.add(get_submobject_index_labels(formula))


class IntroduceFormula(Scene):
    def construct(self):
        formula = get_bayes_formula()
        formula.save_state()
        formula.set_width(FRAME_WIDTH - 1)

        def get_formula_slice(*indices):
            return VGroup(*[formula[i] for i in indices])

        H_label = formula.get_part_by_tex("{H}")
        E_label = formula.get_part_by_tex("{E}")

        hyp_label = TextMobject("Hypothesis")
        hyp_label.set_color(HYPOTHESIS_COLOR)
        hyp_label.next_to(H_label, UP, LARGE_BUFF)

        evid_label = TextMobject("Evidence")
        evid_label.set_color(EVIDENCE_COLOR1)
        evid_label.next_to(E_label, DOWN, LARGE_BUFF)

        hyp_arrow = Arrow(hyp_label.get_bottom(), H_label.get_top(), buff=SMALL_BUFF)
        evid_arrow = Arrow(evid_label.get_top(), E_label.get_bottom(), buff=SMALL_BUFF)

        self.add(formula[:6])
        # self.add(get_submobject_index_labels(formula))
        # return
        self.play(
            FadeInFrom(hyp_label, DOWN),
            GrowArrow(hyp_arrow),
            FadeInFrom(evid_label, UP),
            GrowArrow(evid_arrow),
        )
        self.wait()

        # Prior
        self.play(
            ShowCreation(formula.get_part_by_tex("=")),
            TransformFromCopy(
                get_formula_slice(0, 1, 2, 5),
                get_formula_slice(8, 9, 10, 11),
            ),
        )

        # Likelihood
        lhs_copy = formula[:6].copy()
        likelihood = formula[12:18]
        run_time = 1
        self.play(
            lhs_copy.next_to, likelihood, UP,
            run_time=run_time,
        )
        self.play(
            Swap(lhs_copy[2], lhs_copy[4]),
            run_time=run_time,
        )
        self.play(
            lhs_copy.move_to, likelihood,
            run_time=run_time,
        )

        # Evidence
        self.play(
            ShowCreation(formula.get_part_by_tex("\\over")),
            TransformFromCopy(
                get_formula_slice(0, 1, 4, 5),
                get_formula_slice(19, 20, 21, 22),
            ),
        )
        self.wait()

        self.clear()
        self.play(
            formula.restore,
            formula.scale, 1.5,
            formula.to_edge, UP,
            FadeOut(VGroup(
                hyp_arrow, hyp_label,
                evid_arrow, evid_label,
            ))
        )


class StateGoal(PiCreatureScene, Scene):
    CONFIG = {
        "default_pi_creature_kwargs": {
            "color": BLUE_B,
            "height": 2,
        },

    }

    def construct(self):
        # Zoom to later
        you = self.pi_creature
        line = NumberLine(
            x_min=-2,
            x_max=12,
            include_tip=True
        )
        line.to_edge(DOWN, buff=1.5)
        line.to_edge(LEFT, buff=-0.5)

        you.next_to(line.n2p(0), UP)

        you_label = TextMobject("you")
        you_label.next_to(you, RIGHT, MED_LARGE_BUFF)
        you_arrow = Arrow(you_label.get_left(), you.get_right() + 0.5 * LEFT, buff=0.1)

        now_label = TextMobject("Now")
        later_label = TextMobject("Later")
        now_label.next_to(line.n2p(0), DOWN)
        later_label.next_to(line.n2p(10), DOWN)

        self.add(line, now_label)
        self.add(you)
        self.play(
            FadeInFrom(you_label, LEFT),
            GrowArrow(you_arrow),
            you.change, "pondering",
        )
        self.wait()
        you_label.add(you_arrow)
        self.play(
            you.change, "horrified",
            you.look, DOWN,
            you.next_to, line.n2p(10), UP,
            MaintainPositionRelativeTo(you_label, you),
            FadeInFromPoint(later_label, now_label.get_center()),
        )
        self.wait()

        # Add bubble
        bubble = you.get_bubble(
            height=4,
            width=6,
        )
        bubble.set_fill(opacity=0)
        formula = get_bayes_formula()
        bubble.position_mobject_inside(formula)

        self.play(
            you.change, "confused", bubble,
            ShowCreation(bubble),
        )
        self.play(FadeIn(formula))
        self.play(you.change, "hooray", formula)
        self.wait(2)

        # Show examples
        icons = VGroup(
            SVGMobject(file_name="science"),
            SVGMobject(file_name="robot"),
        )
        for icon in icons:
            icon.set_stroke(width=0)
            icon.set_fill(GREY)
            icon.set_sheen(1, UL)
            icon.set_height(1.5)
        icons[0].set_stroke(GREY, 3, background=True)
        gold = self.get_gold()
        icons.add(gold)

        icons.arrange(DOWN, buff=MED_LARGE_BUFF)
        icons.to_corner(UL)

        for icon in icons[:2]:
            self.play(
                Write(icon, run_time=2),
                you.change, "thinking", icon,
            )
        self.play(
            Blink(you),
            FadeOut(VGroup(
                line, now_label, later_label,
                you_label, you_arrow
            )),
        )
        self.play(
            FadeInFrom(gold, LEFT),
            you.change, "erm", gold,
        )
        self.play(Blink(you))

        # Brief Thompson description
        words = VGroup(
            TextMobject("1988").scale(1.5),
            TextMobject("<NAME>\\\\and friends"),
        )
        words.arrange(DOWN, buff=0.75)

        ship = ImageMobject("ss_central_america")
        ship.set_width(4)
        ship.move_to(gold, DL)
        ship_title = TextMobject("SS Central America")
        ship_title.next_to(ship, UP)

        words.next_to(ship, RIGHT)

        self.play(
            FadeInFrom(words[0], LEFT),
            you.change, "tease", words,
            FadeOut(icons[:2]),
        )
        self.play(FadeInFrom(words[1], UP))
        self.wait()

        self.add(ship, gold)
        self.play(
            FadeIn(ship),
            gold.scale, 0.2,
            gold.move_to, ship,
        )
        self.play(FadeInFromDown(ship_title))
        self.play(you.change, "thinking", ship)

        amount = TexMobject("> \\$700{,}000{,}000")
        amount.scale(1.5)
        amount.next_to(ship, DOWN, MED_LARGE_BUFF)
        amount.to_edge(LEFT, buff=2)
        amount.set_color(YELLOW)

        gold_copy = gold.copy()
        self.play(
            gold_copy.scale, 3,
            gold_copy.next_to, amount, LEFT,
            FadeIn(amount),
        )
        self.play(Blink(you))
        self.wait()
        self.play(LaggedStartMap(
            FadeOutAndShift,
            Group(*words, ship_title, ship, gold, gold_copy, amount),
        ))

        # Levels of understanding
        # Turn bubble into level points
        level_points = VGroup(*[bubble.copy() for x in range(3)])
        for n, point in enumerate(level_points):
            point.set_width(0.5)
            point.set_height(0.5, stretch=True)
            point.add(*[
                point[-1].copy().scale(1.2**k)
                for k in range(1, n + 1)
            ])
            point[:3].scale(1.2**n, about_point=point[3].get_center())
            point.set_stroke(width=2)
            point.set_fill(opacity=0)
        level_points.arrange(DOWN, buff=LARGE_BUFF)

        title = TextMobject("Levels of understanding")
        title.scale(1.5)
        title.to_corner(UL)
        underline = Line()
        underline.match_width(title)
        underline.move_to(title, DOWN)
        title.add(underline)

        level_points.next_to(title, DOWN, buff=1.5)
        level_points.to_edge(LEFT)
        level_points.set_submobject_colors_by_gradient(GREEN, YELLOW, RED)

        self.remove(bubble)
        self.play(
            formula.to_corner, UR,
            FadeOut(you),
            *[
                ReplacementTransform(bubble.copy(), point)
                for point in level_points
            ],
        )
        self.play(Write(title, run_time=1))
        self.wait()

        # Write level 1
        level_labels = VGroup(
            TextMobject("What is it saying?"),
            TextMobject("Why is it true?"),
            TextMobject("When is it useful?"),
        )
        for lp, ll in zip(level_points, level_labels):
            ll.scale(1.25)
            ll.match_color(lp)
            ll.next_to(lp, RIGHT)

        formula_parts = VGroup(
            formula.prior,
            formula.likelihood,
            formula.p_evidence,
            formula.posterior,
        ).copy()
        formula_parts.generate_target()
        formula_parts.target.scale(1.5)
        formula_parts.target.arrange(DOWN, buff=LARGE_BUFF, aligned_edge=LEFT)
        formula_parts.target.next_to(formula, DOWN, buff=LARGE_BUFF)
        formula_parts.target.shift(3 * LEFT)

        equal_signs = VGroup(*[
            TextMobject("=").next_to(fp, RIGHT)
            for fp in formula_parts.target
        ])

        kw = {
            "tex_to_color_map": {
                "hypothesis": HYPOTHESIS_COLOR,
                "evidence": EVIDENCE_COLOR1,
            },
            "alignment": "",
        }
        meanings = VGroup(
            TextMobject("Probability a hypothesis is true\\\\(before any evidence)", **kw),
            TextMobject("Probability of seeing the evidence \\quad \\\\if the hypothesis is true", **kw),
            TextMobject("Probability of seeing the evidence", **kw),
            TextMobject("Probability a hypothesis is true\\\\given some evidence", **kw),
        )
        for meaning, equals in zip(meanings, equal_signs):
            meaning.scale(0.5)
            meaning.next_to(equals, RIGHT)

        self.play(
            FadeIn(level_labels[0], lag_ratio=0.1),
            MoveToTarget(formula_parts),
            LaggedStartMap(FadeInFrom, equal_signs, lambda m: (m, RIGHT)),
            LaggedStartMap(FadeIn, meanings),
        )
        self.wait()

        # Write level 2
        diagram = BayesDiagram(0.35, 0.5, 0.2, height=2.5)
        diagram.next_to(formula, DOWN, aligned_edge=LEFT)

        braces = VGroup(*[
            Brace(diagram.he_rect, vect, buff=SMALL_BUFF)
            for vect in [DOWN, LEFT]
        ])

        formula_parts.generate_target()
        formula_parts.target[:2].scale(0.5)
        formula_parts.target[0].next_to(braces[0], DOWN, SMALL_BUFF)
        formula_parts.target[1].next_to(braces[1], LEFT, SMALL_BUFF)

        pe_picture = VGroup(
            diagram.he_rect.copy(),
            TexMobject("+"),
            diagram.nhe_rect.copy()
        )
        pe_picture.arrange(RIGHT, buff=SMALL_BUFF)
        pe_picture.next_to(equal_signs[2], RIGHT)

        phe_picture = VGroup(
            diagram.he_rect.copy(),
            Line().match_width(pe_picture),
            pe_picture.copy(),
        )
        phe_picture.arrange(DOWN, buff=MED_SMALL_BUFF)
        phe_picture.next_to(equal_signs[3], RIGHT)

        pe_picture.scale(0.5, about_edge=LEFT)
        phe_picture.scale(0.3, about_edge=LEFT)

        self.play(
            FadeOut(meanings),
            FadeOut(equal_signs[:2]),
            MoveToTarget(formula_parts),
            FadeIn(diagram),
            LaggedStartMap(GrowFromCenter, braces),
            FadeIn(level_labels[1], lag_ratio=0.1),
            level_labels[0].set_opacity, 0.5,
        )
        self.play(
            TransformFromCopy(diagram.he_rect, pe_picture[0]),
            TransformFromCopy(diagram.nhe_rect, pe_picture[2]),
            FadeIn(pe_picture[1]),
        )
        self.play(
            TransformFromCopy(pe_picture, phe_picture[2]),
            TransformFromCopy(pe_picture[0], phe_picture[0]),
            ShowCreation(phe_picture[1])
        )
        self.wait()

        # Write level 3
        steve = Steve(height=3)
        steve.to_edge(RIGHT, buff=2)

        arrow = Arrow(level_points.get_bottom(), level_points.get_top(), buff=0)
        arrow.shift(0.25 * LEFT)

        self.play(
            LaggedStartMap(
                FadeOutAndShift,
                VGroup(
                    VGroup(diagram, braces, formula_parts[:2]),
                    VGroup(formula_parts[2], equal_signs[2], pe_picture),
                    VGroup(formula_parts[3], equal_signs[3], phe_picture),
                ),
                lambda m: (m, 3 * RIGHT),
            ),
            FadeIn(level_labels[2], lag_ratio=0.1),
            level_labels[1].set_opacity, 0.5,
        )
        self.wait()
        self.play(
            GrowArrow(arrow),
            level_points.shift, 0.5 * RIGHT,
            level_labels.shift, 0.5 * RIGHT,
            level_labels.set_opacity, 1,
        )
        self.wait()
        self.play(Write(steve, run_time=3))
        self.wait()

        # Transition to next scene
        self.play(
            steve.to_corner, UR,
            Uncreate(arrow),
            LaggedStartMap(
                FadeOutAndShift,
                VGroup(
                    title,
                    formula,
                    *level_points,
                    *level_labels,
                ),
                lambda m: (m, DOWN),
            ),
        )
        self.wait()

    def get_gold(self):
        gold = SVGMobject(file_name="gold_bars")[0]
        gold.set_stroke(width=0)
        gold.set_fill(GOLD)
        gold.set_sheen(0.5, UP)
        gold.flip(UP)
        gold_copy = gold.copy()
        gold_copy.shift(2 * OUT)

        rects = VGroup()
        for curve in CurvesAsSubmobjects(gold):
            p1 = curve.points[0]
            p2 = curve.points[-1]
            rect = Polygon(p1, p2, p2 + 2 * OUT, p1 + 2 * OUT)
            rect.match_style(gold)
            # rect.set_fill(GOLD)
            # rect.set_sheen(1, UL)
            rects.add(rect)
        rects.sort(lambda p: p[1])
        gold.add(*rects)
        gold.add(gold_copy)

        # gold = rects

        gold.rotate(2 * DEGREES, UP)
        gold.rotate(2 * DEGREES, RIGHT)
        gold.set_shade_in_3d(True)
        gold.set_height(1.5)
        gold.set_stroke(BLACK, 0.5)
        return gold


class DescriptionOfSteve(Scene):
    def construct(self):
        self.write_description()
        self.compare_probabilities()

    def write_description(self):
        steve = Steve(height=3)
        steve.to_corner(UR)

        description = self.get_description()
        description.to_edge(LEFT)
        description.align_to(steve, UP)

        mt_parts = VGroup(
            description.get_part_by_tex("meek"),
            description.get_part_by_tex("soul"),
        )
        mt_parts.set_color(WHITE)

        self.add(steve)
        self.play(
            FadeIn(description),
            run_time=3,
            lag_ratio=0.01,
            rate_func=linear,
        )
        self.wait(3)

        lines = VGroup(*[
            Line(mob.get_corner(DL), mob.get_corner(DR), color=YELLOW)
            for mob in mt_parts
        ])
        self.play(
            ShowCreation(lines),
            mt_parts.set_color, YELLOW,
        )
        self.play(FadeOut(lines))
        self.wait()

    def compare_probabilities(self):
        bar = ProbabilityBar(
            0.5, width=10,
            include_braces=True,
            percentage_background_stroke_width=2,
        )
        icons = VGroup(
            LibrarianIcon(),
            FarmerIcon(),
        )
        for icon, text, half in zip(icons, ["Librarian", "Farmer"], bar.bars):
            icon.set_height(0.7)
            label = TextMobject(text)
            label.next_to(icon, DOWN, buff=SMALL_BUFF)
            label.set_color(
                interpolate_color(half.get_color(), WHITE, 0.5)
            )
            icon.add(label)

        bar.add_icons(*icons)
        bar.move_to(1.75 * DOWN)

        bar.icons.set_opacity(0)

        q_marks = TexMobject(*"???")
        q_marks.scale(1.5)
        q_marks.space_out_submobjects(1.5)
        q_marks.next_to(bar, DOWN)

        self.play(FadeIn(bar))
        self.wait()
        self.play(
            bar.p_tracker.set_value, 0.9,
            bar.icons[0].set_opacity, 1,
        )
        self.wait()
        self.play(
            bar.p_tracker.set_value, 0.1,
            bar.icons[1].set_opacity, 1,
        )
        self.play(
            LaggedStartMap(
                FadeInFrom, q_marks,
                lambda m: (m, UP),
                run_time=2,
            ),
            ApplyMethod(
                bar.p_tracker.set_value, 0.7,
                run_time=8,
            )
        )
        for value in 0.3, 0.7:
            self.play(
                bar.p_tracker.set_value, 0.3,
                run_time=7,
            )

    def get_description(self):
        return TextMobject(
            """
            Steve is very shy and withdrawn,\\\\
            invariably helpful but with very\\\\
            little interest in people or in the\\\\
            world of reality. A meek and tidy\\\\
            soul, he has a need for order and\\\\
            structure, and a passion for detail.\\\\
            """,
            tex_to_color_map={
                "shy and withdrawn": BLUE,
                "meek and tidy": YELLOW,
                "soul": YELLOW,
            },
            alignment="",
        )


class IntroduceKahnemanAndTversky(DescriptionOfSteve, MovingCameraScene):
    def construct(self):
        # Introduce K and T
        images = Group(
            ImageMobject("kahneman"),
            ImageMobject("tversky"),
        )
        danny, amos = images
        images.set_height(3.5)
        images.arrange(DOWN, buff=0.5)
        images.to_edge(LEFT, buff=MED_LARGE_BUFF)

        names = VGroup(
            TextMobject("Daniel\\\\Kahneman", alignment=""),
            TextMobject("Amos\\\\Tversky", alignment=""),
        )
        for name, image in zip(names, images):
            name.scale(1.25)
            name.next_to(image, RIGHT)
            image.name = name

        prize = ImageMobject("nobel_prize", height=1)
        prize.move_to(danny, UR)
        prize.shift(MED_SMALL_BUFF * UR)

        books = Group(
            ImageMobject("thinking_fast_and_slow"),
            ImageMobject("undoing_project"),
        )
        books.set_height(5)
        books.arrange(RIGHT, buff=0.5)
        books.to_edge(RIGHT, buff=MED_LARGE_BUFF)

        self.play(
            FadeInFrom(danny, DOWN),
            FadeInFrom(danny.name, LEFT),
        )
        self.play(
            FadeInFrom(amos, UP),
            FadeInFrom(amos.name, LEFT),
        )
        self.wait()
        self.play(FadeInFromLarge(prize))
        self.wait()
        for book in books:
            self.play(FadeInFrom(book, LEFT))
        self.wait()

        # Show them thinking
        for image in images:
            image.generate_target()
        amos.target.to_corner(DL)
        danny.target.to_corner(DR)
        targets = Group(amos.target, danny.target)

        bubble = ThoughtBubble(
            width=7, height=4,
        )
        bubble.next_to(targets, UP)
        new_stem = bubble[:-1].copy()
        new_stem.rotate(PI, UP, about_point=targets.get_top())
        new_stem.shift(SMALL_BUFF * DR)
        bubble.add_to_back(*new_stem)
        bubble[-1].scale(1.2)
        bubble[-1].to_edge(UP, buff=0)
        bubble[:-1].shift(DOWN)
        bubble.set_fill(DARK_GREY, 1)

        randy = Randolph(color=BLUE_B)
        randy.set_height(1)
        randy.next_to(bubble[-1].get_center(), DL)
        randy.shift(LEFT)

        lil_bubble = ThoughtBubble(height=1.5, width=2)
        lil_bubble.next_to(randy, UR, buff=0)
        lil_bubble[:-1].rotate(
            PI, axis=UR, about_point=lil_bubble[:-1].get_corner(UL),
        )
        lil_bubble.move_to(randy.get_top(), DL)
        for i, part in enumerate(lil_bubble[-2::-1]):
            part.rotate(90 * DEGREES)
            part.shift(0.05 * i * UR)
        lil_bubble[-1].scale(0.8)

        librarian = TextMobject("Librarian")
        librarian.set_color(BLUE)
        librarian.scale(0.5)
        librarian.move_to(lil_bubble[-1])

        bar = ProbabilityBar(percentage_background_stroke_width=1)
        bar.add_icons(
            LibrarianIcon(height=1),
            FarmerIcon(height=1),
        )
        bar.scale(0.5)
        bar.next_to(randy, RIGHT, buff=0.75)
        bar.update()

        self.play(
            LaggedStartMap(MoveToTarget, images),
            LaggedStartMap(FadeOutAndShiftDown, books),
            LaggedStartMap(FadeOut, Group(prize, *names)),
        )
        self.play(
            DrawBorderThenFill(bubble),
            FadeInFrom(
                randy, UR,
                rate_func=squish_rate_func(smooth, 0.5, 1),
                run_time=2,
            )
        )
        self.play(
            DrawBorderThenFill(lil_bubble),
            randy.change, "pondering",
        )
        self.play(Blink(randy))
        self.play(Write(librarian))
        self.add(bar, lil_bubble, librarian)
        self.play(FadeIn(bar))
        self.play(
            bar.p_tracker.set_value, 1 / 6,
            randy.change, "thinking", bar,
        )
        self.play(Blink(randy))
        self.wait()

        # Zoom in
        description = self.get_description()
        description.scale(0.4)
        description.next_to(randy, UL)
        description.shift(1.25 * RIGHT + 0.75 * UP)
        description.set_color(WHITE)

        frame = self.camera_frame

        steve = Steve()
        steve.match_height(description)
        steve.align_to(bar, RIGHT)
        steve.align_to(description, UP)

        # cross = Cross(librarian)

        book_border = bar.icons[0].copy()
        farm_border = bar.icons[1].copy()
        for border in [book_border, farm_border]:
            border.set_fill(opacity=0)
            border.set_stroke(YELLOW, 1)

        seems_bookish = TextMobject("Seems\\\\bookish")
        seems_bookish.match_width(librarian)
        seems_bookish.scale(0.8)
        seems_bookish.move_to(librarian)

        self.play(
            frame.scale, 0.5,
            frame.move_to, bubble[-1], DOWN,
            frame.shift, 0.75 * LEFT,
            FadeOut(bubble),
            FadeOut(images),
            FadeOut(lil_bubble),
            FadeOut(librarian),
            FadeIn(description, lag_ratio=0.05),
            randy.change, "pondering", description,
            run_time=6,
        )
        self.play(randy.change, "happy", description)
        self.play(
            description.get_part_by_tex("shy").set_color, BLUE,
            lag_ratio=0.1,
        )
        self.play(
            description.get_part_by_tex("meek").set_color, YELLOW,
            description.get_part_by_tex("soul").set_color, YELLOW,
            lag_ratio=0.1,
        )
        self.play(
            bar.p_tracker.set_value, 0.9,
            FadeIn(lil_bubble),
            Write(librarian),
        )
        self.play(ShowCreationThenFadeOut(book_border))
        self.play(Blink(randy))
        self.play(
            FadeInFromDown(steve),
            randy.look_at, steve,
        )
        self.play(
            randy.change, "tease", steve,
            FadeOut(librarian),
            FadeIn(seems_bookish),
        )
        lil_bubble.add(seems_bookish)
        self.wait()
        self.play(Blink(randy))

        self.play(
            LaggedStartMap(
                FadeOutAndShift, lil_bubble,
                lambda m: (m, LEFT),
                run_time=1,
            ),
            bar.p_tracker.set_value, 1 / 6,
            randy.change, "confused", bar,
        )
        self.play(
            ShowCreationThenFadeOut(farm_border)
        )
        self.wait()

        # Transition to next scene
        fh = frame.get_height()
        fw = frame.get_width()
        center = frame.get_center()
        right = (fw / FRAME_WIDTH) * RIGHT
        up = (fh / FRAME_HEIGHT) * UP
        left = -right
        down = -up

        book, farm = bar.icons.deepcopy()
        bar.clear_updaters()
        bar.icons.set_opacity(0)

        for mob in book, farm:
            mob.clear_updaters()
            mob.generate_target(use_deepcopy=True)
            mob.target.set_height(get_norm(up))
            mob.target.move_to(center + down + 2 * left)
        farm.target.shift(4 * right)

        steve.generate_target()
        steve.target.match_width(book.target)
        steve.target.move_to(book.target, DOWN)
        steve.target.shift(3 * up)
        steve_copy = steve.target.copy()
        steve_copy.match_x(farm.target),

        self.play(
            TransformFromCopy(steve, steve_copy),
            LaggedStartMap(MoveToTarget, VGroup(steve, book, farm)),
            LaggedStartMap(
                FadeOutAndShift,
                description,
                lambda m: (m, LEFT)
            ),
            FadeOutAndShift(randy, LEFT),
            FadeOutAndShift(bar, LEFT),
        )


class CorrectViewOfFarmersAndLibrarians(Scene):
    def construct(self):
        # Match last scene
        steves = VGroup(Steve(), Steve())
        book = LibrarianIcon()
        farm = FarmerIcon()
        icons = VGroup(book, farm)

        for mob in icons:
            mob.set_height(1)
            mob.move_to(DOWN + 2 * LEFT)
        farm.shift(4 * RIGHT)

        steves.match_width(book)
        steves.move_to(book, DOWN)
        steves.shift(3 * UP)
        steve1, steve2 = steves
        steve2.match_x(farm)

        self.add(steves, book, farm)

        # Add arrows
        arrows = VGroup(*[
            Arrow(s.get_bottom(), m.get_top())
            for s, m in zip(steves, icons)
        ])
        words = VGroup(
            TextMobject("Stereotype"),
            TextMobject("Unexpected"),
        )
        for arrow, word, vect in zip(arrows, words, [LEFT, RIGHT]):
            word.scale(1.5)
            word.next_to(arrow, vect)
            self.play(
                GrowArrow(arrow),
                FadeInFrom(word, UP),
            )
        self.wait()

        # Show people proportions
        librarian = Librarian()
        farmer = Farmer()

        librarian.move_to(LEFT).to_edge(UP)
        farmer.move_to(RIGHT).to_edge(UP)
        farmer_ul = farmer.get_corner(UL)

        farmers = VGroup(farmer, *[farmer.copy() for x in range(19)])
        farmers.arrange_in_grid(n_rows=4)
        farmers.move_to(farmer_ul, UL)

        farmer_outlines = farmers.copy()
        farmer_outlines.set_fill(opacity=0)
        farmer_outlines.set_stroke(YELLOW, 1)

        farmer_count = Integer(1)
        farmer_count.scale(2)
        farmer_count.set_color(GREEN)
        farmer_count.next_to(farmers, LEFT, buff=LARGE_BUFF)
        farmer_count.add_updater(lambda m: m.set_value(len(farmer_outlines)))

        for person, icon in zip([librarian, farmer], icons):
            person.save_state()
            person[:-1].set_opacity(0)
            person.scale(
                icon.get_height() / person[-1].get_height()
            )
            person.move_to(icon, DR)

        self.remove(*icons)
        self.play(
            LaggedStartMap(FadeOut, VGroup(steves, arrows, words)),
            Restore(librarian),
            Restore(farmer),
            run_time=1,
        )
        self.play(
            LaggedStartMap(FadeIn, farmers[1:])
        )
        self.wait()
        self.add(farmer_count)
        self.play(
            ShowIncreasingSubsets(farmer_outlines),
            int_func=np.ceil,
            rate_func=linear,
            run_time=2,
        )
        self.play(FadeOut(farmer_outlines))
        self.wait()

        # Show higher number of farmers
        farmers.save_state()
        farmers.generate_target()
        farmers.target.scale(0.5, about_edge=UL)
        new_farmers = VGroup(*it.chain(*[
            farmers.target.copy().next_to(
                farmers.target, vect, buff=SMALL_BUFF,
            )
            for vect in [RIGHT, DOWN]
        ]))
        new_farmers[-10:].align_to(new_farmers, RIGHT)
        new_farmers[-10:].align_to(new_farmers[-20], UP)

        farmer_count.clear_updaters()
        self.play(
            MoveToTarget(farmers),
            ShowIncreasingSubsets(new_farmers),
            ChangeDecimalToValue(farmer_count, 60),
        )
        self.wait()
        self.play(
            FadeOut(new_farmers),
            Restore(farmers),
            ChangeDecimalToValue(farmer_count, 20),
        )
        self.wait()

        # Organize into a representative sample
        farmers.generate_target()
        librarian.generate_target()
        group = VGroup(librarian.target, *farmers.target)
        self.arrange_bottom_row(group)

        l_brace = self.get_count_brace(VGroup(librarian.target))
        f_brace = self.get_count_brace(farmers.target)

        self.play(
            MoveToTarget(librarian),
            MoveToTarget(farmers),
            ReplacementTransform(farmer_count, f_brace[-1]),
            GrowFromCenter(f_brace[:-1]),
        )
        self.play(GrowFromCenter(l_brace))
        self.wait()

    def get_count_brace(self, people):
        brace = Brace(people, UP, buff=SMALL_BUFF)
        count = Integer(len(people), edge_to_fix=DOWN)
        count.next_to(brace, UP, SMALL_BUFF)
        brace.add(count)
        brace.count = count
        return brace

    def arrange_bottom_row(self, group):
        group.arrange(RIGHT, buff=0.5)
        group.set_width(FRAME_WIDTH - 3)
        group.to_edge(DOWN)
        for person in group:
            person[-1].set_stroke(BLACK, 1, background=True)


class ComplainAboutNotKnowingTheStats(TeacherStudentsScene):
    def construct(self):
        self.student_says(
            "Are people expected\\\\to know that?",
            student_index=2
        )
        self.change_student_modes(
            "sassy", "sassy",
        )
        self.play(self.teacher.change, "hesitant")
        self.look_at(self.screen)
        self.wait(3)
        self.teacher_says(
            "No, but did you\\\\think to estimate it?",
            bubble_kwargs={"width": 4.5, "height": 3.5},
        )
        self.change_all_student_modes("guilty")
        self.wait(2)
        self.change_all_student_modes("pondering")
        self.wait(3)


class SpoilerAlert(Scene):
    def construct(self):
        sa_words = TextMobject("Spoiler Alert")
        sa_words.scale(2)
        sa_words.to_edge(UP)
        sa_words.set_color(RED)

        alert = Triangle(start_angle=90 * DEGREES)
        alert.set_stroke(RED, 8)
        alert.set_height(sa_words.get_height())
        alert.round_corners(0.1)
        bang = TextMobject("!")
        bang.set_color(RED)
        bang.scale(1.5)
        bang.move_to(alert)
        alert.add(bang)

        alert.next_to(sa_words, LEFT)
        sa_words.add(alert.copy())
        alert.next_to(sa_words, RIGHT)
        sa_words.add(alert)

        formula = get_bayes_formula()
        formula_words = TextMobject("This is secretly ")
        formula_words.scale(1.5)
        formula_group = VGroup(formula_words, formula)
        formula_group.arrange(DOWN, buff=MED_LARGE_BUFF)
        formula_group.next_to(sa_words, DOWN, LARGE_BUFF)

        self.add(sa_words)
        self.wait()
        self.play(FadeInFrom(formula_group, UP))
        self.wait()


class ReasonByRepresentativeSample(CorrectViewOfFarmersAndLibrarians):
    CONFIG = {
        "ignore_icons": False,
        # "ignore_icons": True,
    }

    def construct(self):
        # Match previous scene
        librarians = VGroup(Librarian())
        farmers = VGroup(*[Farmer() for x in range(20)])
        everyone = VGroup(*librarians, *farmers)
        self.arrange_bottom_row(everyone)
        self.add(everyone)

        if self.ignore_icons:
            for person in everyone:
                person.submobjects.pop()

        l_brace = self.get_count_brace(librarians)
        f_brace = self.get_count_brace(farmers)
        braces = VGroup(l_brace, f_brace)
        for brace in braces:
            brace.count.set_stroke(BLACK, 3, background=True)
            brace.count.brace = brace
            brace.remove(brace.count)
            brace.count_tracker = ValueTracker(brace.count.get_value())
            brace.count.add_updater(
                lambda c: c.set_value(
                    c.brace.count_tracker.get_value(),
                ).next_to(c.brace, UP, SMALL_BUFF)
            )
            self.add(brace, brace.count)

        # Multiply by 10
        new_people = VGroup()
        for group in [librarians, farmers]:
            new_rows = VGroup(*[group.copy() for x in range(9)])
            new_rows.arrange(UP, buff=SMALL_BUFF)
            new_rows.next_to(group, UP, SMALL_BUFF)
            new_people.add(new_rows)
        new_librarians, new_farmers = new_people

        self.play(
            *[
                FadeIn(new_rows, lag_ratio=0.1)
                for new_rows in new_people
            ],
            *[
                ApplyMethod(brace.next_to, new_rows, UP, {"buff": SMALL_BUFF})
                for brace, new_rows in zip(braces, new_people)
            ],
            *[
                ApplyMethod(
                    brace.count_tracker.set_value,
                    10 * brace.count_tracker.get_value(),
                )
                for brace in braces
            ],
        )

        farmers = VGroup(farmers, *new_farmers)
        librarians = VGroup(librarians, *new_librarians)
        everyone = VGroup(farmers, librarians)

        # Add background rectangles
        big_rect = SurroundingRectangle(
            everyone,
            buff=0.05,
            stroke_width=1,
            stroke_color=WHITE,
            fill_opacity=1,
            fill_color=DARKER_GREY,
        )
        left_rect = big_rect.copy()
        prior = 1 / 21
        left_rect.stretch(prior, 0, about_edge=LEFT)
        right_rect = big_rect.copy()
        right_rect.stretch(1 - prior, 0, about_edge=RIGHT)

        dl_rect = left_rect.copy()
        ul_rect = left_rect.copy()
        dl_rect.stretch(0.4, 1, about_edge=DOWN)
        ul_rect.stretch(0.6, 1, about_edge=UP)

        dr_rect = right_rect.copy()
        ur_rect = right_rect.copy()
        dr_rect.stretch(0.1, 1, about_edge=DOWN)
        ur_rect.stretch(0.9, 1, about_edge=UP)

        colors = [
            interpolate_color(color, BLACK, 0.5)
            for color in [EVIDENCE_COLOR1, EVIDENCE_COLOR2]
        ]
        for rect, color in zip([dl_rect, dr_rect], colors):
            rect.set_fill(color)

        all_rects = VGroup(
            left_rect, right_rect,
            dl_rect, ul_rect, dr_rect, ur_rect,
        )
        all_rects.set_opacity(0)

        self.add(all_rects, everyone)
        self.play(
            left_rect.set_opacity, 1,
            right_rect.set_opacity, 1,
        )
        self.wait()

        # 40% of librarians and 10% of farmers
        for rect, vect in zip([dl_rect, dr_rect], [LEFT, RIGHT]):
            rect.set_opacity(1)
            rect.save_state()
            rect.brace = Brace(rect, vect, buff=SMALL_BUFF)
            rect.brace.save_state()

            rect.number = Integer(0, unit="\\%")
            rect.number.scale(0.75)
            rect.number.next_to(rect.brace, vect, SMALL_BUFF)
            rect.number.brace = rect.brace
            rect.number.vect = vect

            rect.number.add_updater(
                lambda d: d.set_value(100 * d.brace.get_height() / big_rect.get_height())
            )
            rect.number.add_updater(lambda d: d.next_to(d.brace, d.vect, SMALL_BUFF))

            for mob in [rect, rect.brace]:
                mob.stretch(0, 1, about_edge=DOWN)

        for rect, to_fade in [(dl_rect, librarians[4:]), (dr_rect, farmers[1:])]:
            self.add(rect.brace, rect.number)
            self.play(
                Restore(rect),
                Restore(rect.brace),
                to_fade.set_opacity, 0.1,
            )
            self.wait()

        # Emphasize restricted set
        highlighted_librarians = librarians[:4].copy()
        highlighted_farmers = farmers[0].copy()
        for highlights in [highlighted_librarians, highlighted_farmers]:
            highlights.set_color(YELLOW)

        self.add(braces, *[b.count for b in braces])
        self.play(
            l_brace.next_to, librarians[:4], UP, SMALL_BUFF,
            l_brace.count_tracker.set_value, 4,
            ShowIncreasingSubsets(highlighted_librarians)
        )
        self.play(FadeOut(highlighted_librarians))
        self.play(
            f_brace.next_to, farmers[:1], UP, SMALL_BUFF,
            f_brace.count_tracker.set_value, 20,
            ShowIncreasingSubsets(highlighted_farmers),
            run_time=2,
        )
        self.play(FadeOut(highlighted_farmers))
        self.wait()

        # Write answer
        equation = TexMobject(
            "P\\left(",
            "\\text{Librarian }",
            "\\text{given }",
            "\\text{description}",
            "\\right)",
            "=",
            "{4", "\\over", " 4", "+", "20}",
            "\\approx", "16.7\\%",
        )
        equation.set_color_by_tex_to_color_map({
            "Librarian": HYPOTHESIS_COLOR,
            "description": EVIDENCE_COLOR1,
        })

        equation.set_width(FRAME_WIDTH - 2)
        equation.to_edge(UP)
        equation_rect = BackgroundRectangle(equation, buff=MED_SMALL_BUFF)
        equation_rect.set_fill(opacity=1)
        equation_rect.set_stroke(WHITE, width=1, opacity=1)

        self.play(
            FadeIn(equation_rect),
            FadeInFromDown(equation[:6])
        )
        self.wait()
        self.play(
            TransformFromCopy(
                l_brace.count,
                equation.get_parts_by_tex("4")[0],
            ),
            Write(equation.get_part_by_tex("\\over")),
        )
        self.play(
            Write(equation.get_part_by_tex("+")),
            TransformFromCopy(
                f_brace.count,
                equation.get_part_by_tex("20"),
            ),
            TransformFromCopy(
                l_brace.count,
                equation.get_parts_by_tex("4")[1],
            ),
        )

        self.wait()
        self.play(FadeIn(equation[-2:]))
        self.wait()

        # Compare raw likelihoods
        axes = Axes(
            x_min=0,
            x_max=10,
            y_min=0,
            y_max=100,
            y_axis_config={
                "unit_size": 0.07,
                "tick_frequency": 10,
            },
            axis_config={
                "include_tip": False,
            },
        )
        axes.x_axis.tick_marks.set_opacity(0)
        axes.y_axis.add_numbers(
            *range(20, 120, 20),
            number_config={"unit": "\\%"}
        )
        axes.center().to_edge(DOWN)

        title = TextMobject("Likelihood of fitting the description")
        title.scale(1.25)
        title.to_edge(UP)
        title.shift(RIGHT)
        axes.add(title)

        lines = VGroup(
            Line(axes.c2p(3, 0), axes.c2p(3, 40)),
            Line(axes.c2p(7, 0), axes.c2p(7, 10)),
        )
        rects = VGroup(*[Rectangle() for x in range(2)])
        rects.set_fill(EVIDENCE_COLOR1, 1)
        rects[1].set_fill(GREEN)
        rects.set_stroke(WHITE, 1)
        icons = VGroup(LibrarianIcon(), FarmerIcon())

        for rect, line, icon in zip(rects, lines, icons):
            rect.replace(line, dim_to_match=1)
            rect.set_width(1, stretch=True)
            icon.set_width(1)
            icon.next_to(rect, UP)
            y = axes.y_axis.p2n(rect.get_top())
            y_point = axes.y_axis.n2p(y)
            rect.line = DashedLine(y_point, rect.get_corner(UL))

        pre_rects = VGroup()
        for r in dl_rect, dr_rect:
            r_copy = r.deepcopy()
            pre_rects.add(r_copy)

        people_copy = VGroup(librarians[:4], farmers[:1]).copy()
        everything = self.get_mobjects()

        self.play(
            *[FadeOut(mob) for mob in everything],
            FadeIn(axes),
            FadeIn(icons),
            *[
                TransformFromCopy(pr, r)
                for pr, r in zip(pre_rects, rects)
            ],
            FadeOut(people_copy),
        )
        self.play(*[
            ShowCreation(rect.line)
            for rect in rects
        ])
        self.wait()
        self.play(
            FadeOut(axes),
            FadeOut(rects[0].line),
            FadeOut(rects[1].line),
            FadeOut(icons),
            *[
                ReplacementTransform(r, pr)
                for pr, r in zip(pre_rects, rects)
            ],
            # FadeOut(fsfr),
            *[FadeIn(mob) for mob in everything],
        )
        self.remove(*pre_rects)
        self.wait()

        # Emphasize prior belief
        prior_equation = TexMobject(
            "P\\left(",
            "\\text{Librarian}",
            "\\right)",
            "=",
            "{1", "\\over", "21}",
            "\\approx", "4.8\\%",
        )
        prior_equation.set_color_by_tex_to_color_map({
            "Librarian": HYPOTHESIS_COLOR,
        })

        prior_equation.match_height(equation)

        prior_rect = BackgroundRectangle(prior_equation, buff=MED_SMALL_BUFF)
        prior_rect.match_style(equation_rect)

        group = VGroup(prior_equation, prior_rect)
        group.align_to(equation_rect, UP)
        group.shift(
            (
                equation.get_part_by_tex("\\over").get_x() -
                prior_equation.get_part_by_tex("\\over").get_x()
            ) * RIGHT
        )

        prior_label = TextMobject("Prior belief")
        prior_label.scale(1.5)
        prior_label.next_to(prior_rect, LEFT, buff=1.5)
        prior_label.to_edge(UP, buff=0.25)
        prior_label.set_stroke(BLACK, 5, background=True)
        prior_arrow = Arrow(
            prior_label.get_right(),
            prior_equation.get_left(),
            buff=SMALL_BUFF,
        )

        self.play(
            VGroup(equation_rect, equation).shift, prior_rect.get_height() * DOWN,
            FadeIn(prior_rect),
            FadeIn(prior_equation),
            FadeInFrom(prior_label, RIGHT),
            GrowArrow(prior_arrow),
        )
        self.wait()


class NewEvidenceUpdatesPriorBeliefs(DescriptionOfSteve):
    def construct(self):
        # Determining belief in a vacuum
        description = self.get_description()
        rect = SurroundingRectangle(description)
        rect.set_stroke(WHITE, 2)
        rect.set_fill(BLACK, 0.9)
        evid = VGroup(rect, description)
        evid.set_height(2)

        librarian = Librarian()
        librarian.set_height(2)

        arrow = Arrow(LEFT, RIGHT)
        arrow.set_stroke(WHITE, 5)

        group = VGroup(evid, arrow, librarian)
        group.arrange(RIGHT, buff=LARGE_BUFF)

        cross = Cross(VGroup(group))
        cross.set_stroke(RED, 12)
        cross.scale(1.2)

        self.add(evid)
        self.play(
            GrowArrow(arrow),
            FadeInFrom(librarian, LEFT)
        )
        self.play(ShowCreation(cross))
        self.wait()

        #
        icons = VGroup(LibrarianIcon(), FarmerIcon())
        for icon in icons:
            icon.set_height(0.5)

        kw = {
            "include_braces": True,
            "width": 11,
            "height": 0.75,
        }
        top_bar = ProbabilityBar(p=1 / 21, **kw)
        low_bar = ProbabilityBar(p=1 / 21, brace_direction=DOWN, **kw)

        bars = VGroup(top_bar, low_bar)
        for bar in bars:
            bar.percentages[1].add_updater(lambda m: m.set_opacity(0))
            bar.add_icons(*icons.copy())
            bar.suspend_updating()

        new_arrow = Arrow(1.5 * UP, 1.5 * DOWN)
        new_arrow.set_stroke(WHITE, 5)

        top_bar.next_to(new_arrow, UP)
        low_bar.next_to(new_arrow, DOWN)

        self.add(arrow, evid, cross)
        self.play(
            FadeOut(cross),
            Transform(arrow, new_arrow),
            evid.scale, 0.6,
            evid.move_to, new_arrow,
            ReplacementTransform(librarian, low_bar.icons[0]),
            FadeIn(bars)
        )
        self.play(low_bar.p_tracker.set_value, 1 / 6)
        self.wait()


class HeartOfBayesTheorem(Scene):
    def construct(self):
        title = TextMobject("Heart of Bayes' theorem")
        title.scale(1.5)
        title.add(Underline(title))
        title.to_edge(UP)

        # Bayes diagrams
        # prior_tracker = ValueTracker(0.1)
        # likelihood_tracker = ValueTracker(0.4)
        # antilikelihood_tracker = ValueTracker(0.1)
        diagram = self.get_diagram(
            prior=0.1, likelihood=0.4, antilikelihood=0.1,
            # prior_tracker.get_value(),
            # likelihood_tracker.get_value(),
            # antilikelihood_tracker.get_value(),
        )
        diagram.nh_rect.set_fill(GREEN_E)
        diagram.hypothesis_split.set_opacity(1)
        diagram.evidence_split.set_opacity(0)
        restricted_diagram = self.get_restricted_diagram(diagram)
        diagram_copy = diagram.copy()
        diagram_copy.move_to(restricted_diagram)

        diagrams = VGroup(diagram, restricted_diagram)

        label1 = TextMobject("All possibilities")
        label2 = TextMobject(
            "All possibilities\\\\", "fitting the evidence",
            tex_to_color_map={"evidence": EVIDENCE_COLOR1},
        )
        labels = VGroup(label1, label2)
        labels.scale(diagram.get_width() / label1.get_width())

        for l, d in zip(labels, diagrams):
            l.next_to(d, UP)

        label2.save_state()
        label2[0].move_to(label2, DOWN)
        label2[1:].shift(0.25 * UP)
        label2[1:].set_opacity(0)

        # Final fraction written geometrically
        fraction = always_redraw(
            lambda: self.get_geometric_fraction(restricted_diagram)
        )
        frac_box = always_redraw(lambda: DashedVMobject(
            SurroundingRectangle(
                fraction,
                buff=MED_SMALL_BUFF,
                stroke_width=2,
                stroke_color=WHITE,
            ),
            num_dashes=100,
        ))
        prob = TexMobject(
            "P\\left(",
            "{\\text{Librarian }",
            "\\text{given}", "\\over", "\\text{the evidence}}",
            "\\right)"
        )
        prob.set_color_by_tex("Librarian", HYPOTHESIS_COLOR)
        prob.set_color_by_tex("evidence", EVIDENCE_COLOR1)
        prob.get_part_by_tex("\\over").set_opacity(0)
        prob.match_width(frac_box)
        prob.next_to(frac_box, UP)

        updaters = VGroup(
            diagram, restricted_diagram, fraction, frac_box
        )
        updaters.suspend_updating()

        self.play(
            FadeIn(diagram),
            FadeInFromDown(label1),
        )
        self.play(
            TransformFromCopy(diagram, diagram_copy),
            TransformFromCopy(label1[0], label2[0]),
        )
        self.play(
            Restore(label2),
            ReplacementTransform(diagram_copy, restricted_diagram)
        )
        self.wait()

        self.play(
            TransformFromCopy(
                restricted_diagram.he_rect,
                fraction[0],
            ),
            TransformFromCopy(
                restricted_diagram.he_rect,
                fraction[2][0],
            ),
            TransformFromCopy(
                restricted_diagram.nhe_rect,
                fraction[2][2],
            ),
            ShowCreation(fraction[1]),
            Write(fraction[2][1]),
        )
        self.add(fraction)
        self.play(
            ShowCreation(frac_box),
            FadeIn(prob)
        )
        self.wait()

        self.play(Write(title, run_time=1))
        self.wait()

        # Mess with some numbers
        updaters.resume_updating()
        self.play(*[
            ApplyMethod(d.set_prior, 0.4, run_time=2)
            for d in diagrams
        ])
        self.play(*[
            ApplyMethod(d.set_likelihood, 0.3, run_time=2)
            for d in diagrams
        ])
        self.play(*[
            ApplyMethod(d.set_antilikelihood, 0.6, run_time=2)
            for d in diagrams
        ])
        # self.play(prior_tracker.set_value, 0.4, run_time=2)
        # self.play(antilikelihood_tracker.set_value, 0.3, run_time=2)
        # self.play(likelihood_tracker.set_value, 0.6, run_time=2)
        self.wait()
        updaters.suspend_updating()

        # Ask about a formula
        words = TextMobject("Write this more\\\\mathematically")
        words.scale(1.25)
        words.set_color(RED)
        words.to_corner(UR)
        arrow = Arrow(words.get_bottom(), frac_box.get_top(), buff=SMALL_BUFF)
        arrow.match_color(words)
        arrow.set_stroke(width=5)

        self.play(
            FadeInFrom(words, DOWN),
            GrowArrow(arrow),
            FadeOut(prob),
            title.to_edge, LEFT
        )
        self.wait()

    def get_diagram(self, prior, likelihood, antilikelihood):
        diagram = BayesDiagram(
            prior, likelihood, antilikelihood,
            not_evidence_color1=GREY,
            not_evidence_color2=GREEN_E,
        )
        diagram.set_height(3)
        diagram.move_to(5 * LEFT + DOWN)

        diagram.add_brace_attrs()
        braces = VGroup(diagram.h_brace, diagram.nh_brace)
        diagram.add(*braces)
        icons = VGroup(LibrarianIcon(), FarmerIcon())
        icons[0].set_color(YELLOW_D)
        for icon, brace in zip(icons, braces):
            icon.set_height(0.5)
            icon.brace = brace
            icon.add_updater(lambda m: m.next_to(m.brace, DOWN, SMALL_BUFF))
            diagram.add(icon)
        return diagram

    def get_restricted_diagram(self, diagram):
        restricted_diagram = diagram.deepcopy()
        restricted_diagram.set_x(0)
        restricted_diagram.hypothesis_split.set_opacity(0)
        restricted_diagram.evidence_split.set_opacity(1)
        restricted_diagram.hne_rect.set_opacity(0.1)
        restricted_diagram.nhne_rect.set_opacity(0.1)
        return restricted_diagram

    def get_geometric_fraction(self, diagram):
        fraction = VGroup(
            diagram.he_rect.copy(),
            Line(LEFT, RIGHT),
            VGroup(
                diagram.he_rect.copy(),
                TexMobject("+"),
                diagram.nhe_rect.copy(),
            ).arrange(RIGHT, buff=SMALL_BUFF)
        )
        fraction.arrange(DOWN)
        fraction[1].match_width(fraction)
        fraction.to_edge(RIGHT)
        fraction.align_to(diagram.square, UP)
        return fraction


class WhenDoesBayesApply(DescriptionOfSteve):
    def construct(self):
        title = TextMobject("When to use Bayes' rule")
        title.add(Underline(title, buff=-SMALL_BUFF))
        title.scale(1.5)
        title.to_edge(UP)
        self.add(title)

        # Words
        all_words = VGroup(
            TextMobject("You have a\\\\", "hypothesis"),
            TextMobject("You've observed\\\\some ", "evidence"),
            TexMobject(
                "\\text{You want}\\\\",
                "P", "(", "H", "|", "E", ")\\\\",
                "P", "\\left(",
                "\\substack{""\\text{Hypothesis} \\\\",
                "\\textbf{given} \\\\",
                "\\, \\text{the evidence} \\,}",
                "\\right)",
            ),
        )
        for words in all_words:
            words.set_color_by_tex_to_color_map({
                "hypothesis": HYPOTHESIS_COLOR,
                "H": HYPOTHESIS_COLOR,
                "evidence": EVIDENCE_COLOR1,
                "E": EVIDENCE_COLOR1,
            })

        goal = all_words[2]
        prob = goal[1:7]
        big_prob = goal[7:]
        for mob in [prob, big_prob]:
            mob.match_x(goal[0])
        prob.shift(0.2 * DOWN)
        big_prob.shift(0.4 * DOWN)
        VGroup(big_prob[1], big_prob[-1]).stretch(1.2, 1)

        all_words.arrange(RIGHT, buff=2, aligned_edge=UP)
        all_words.next_to(title, DOWN, buff=MED_LARGE_BUFF)

        big_prob.save_state()
        big_prob.move_to(prob, UP)

        # Icons
        hypothesis_icon = self.get_hypothesis_icon()
        evidence_icon = self.get_evidence_icon()

        hypothesis_icon.next_to(all_words[0], DOWN, LARGE_BUFF)
        evidence_icon.next_to(all_words[1], DOWN, LARGE_BUFF)

        # Show icons
        self.play(FadeInFromDown(all_words[0]))
        self.play(
            LaggedStart(
                FadeInFrom(hypothesis_icon[0], DOWN),
                Write(hypothesis_icon[1]),
                FadeInFrom(hypothesis_icon[2], UP),
                run_time=1,
            )
        )
        self.wait()

        self.play(FadeInFromDown(all_words[1]))
        self.play(
            FadeIn(evidence_icon),
            lag_ratio=0.1,
            run_time=2,
        )
        self.wait()

        # More compact probability
        self.play(FadeInFromDown(VGroup(goal[0], big_prob)))
        self.wait()
        self.play(
            Restore(big_prob),
            *[
                TransformFromCopy(big_prob[i], prob[i])
                for i in [0, 1, -1]
            ]
        )
        self.play(LaggedStart(*[
            TransformFromCopy(big_prob[i][j], prob[i])
            for i, j in [(2, 0), (3, 1), (4, 3)]
        ]))
        self.wait()

        # Highlight "given"
        rects = VGroup(*[
            SurroundingRectangle(
                goal.get_part_by_tex(tex),
                buff=0.05,
                stroke_width=2,
                stroke_color=RED,
            )
            for tex in ("|", "given")
        ])

        self.play(ShowCreation(rects))
        self.play(Transform(rects[0].copy(), rects[1], remover=True))
        self.play(FadeOut(rects))
        self.wait()

        self.remove(prob)
        everything = Group(*self.get_mobjects())
        self.play(
            LaggedStartMap(FadeOut, everything, run_time=2),
            prob.copy().to_corner, UR,
        )

    def get_hypothesis_icon(self):
        group = VGroup(
            Steve().set_height(1.5),
            TexMobject("\\updownarrow"),
            LibrarianIcon().set_color(YELLOW_D)
        )
        group.arrange(DOWN)
        return group

    def get_evidence_icon(self):
        result = self.get_description()
        result.scale(0.5)
        result.set_color_by_tex("meek", EVIDENCE_COLOR1)
        result.set_color_by_tex("soul", EVIDENCE_COLOR1)
        rect = SurroundingRectangle(result)
        rect.set_stroke(WHITE, 2)
        result.add(rect)
        return result


class CreateFormulaFromDiagram(Scene):
    CONFIG = {
        "tex_to_color_map": {
            "P": WHITE,
            "H": HYPOTHESIS_COLOR,
            "E": EVIDENCE_COLOR1,
            "\\neg": RED,
        },
        "bayes_diagram_config": {
            "prior": 1 / 21,
            "likelihood": 0.4,
            "antilikelihood": 0.1,
            "not_evidence_color1": GREY,
            "not_evidence_color2": DARK_GREY,
            "prior_rect_direction": UP,
        },
    }

    def construct(self):
        t2c = self.tex_to_color_map

        # Add posterior
        posterior = TexMobject("P(H|E)", tex_to_color_map=t2c)
        posterior.to_corner(UR)
        posterior_words = TextMobject("Goal: ")
        posterior_words.next_to(posterior, LEFT, aligned_edge=UP)
        self.add(posterior)
        self.add(posterior_words)

        # Show prior
        diagram = self.get_diagram()

        prior_label = TexMobject("P(H)", tex_to_color_map=t2c)
        prior_label.add_updater(
            lambda m: m.next_to(diagram.h_brace, UP, SMALL_BUFF)
        )

        prior_example = TexMobject("= 1 / 21")
        prior_example.add_updater(
            lambda m: m.next_to(prior_label, RIGHT).shift(0.03 * UP)

        )
        # example_words = TextMobject("In our example")
        # example_words.next_to(prior_example[0][1:], UP, buff=SMALL_BUFF, aligned_edge=LEFT)
        # prior_example.add(example_words)

        prior_arrow = Vector(0.7 * RIGHT)
        prior_arrow.next_to(prior_label, LEFT, SMALL_BUFF)
        prior_word = TextMobject("``Prior''")
        prior_word.next_to(prior_arrow, LEFT, SMALL_BUFF)
        prior_word.align_to(prior_label[0], DOWN)
        prior_word.set_color(HYPOTHESIS_COLOR)

        self.add(diagram)
        self.play(ShowIncreasingSubsets(diagram.people, run_time=2))
        self.wait()
        self.play(
            diagram.hypothesis_split.set_opacity, 1,
            FadeIn(diagram.h_brace),
            FadeInFromDown(prior_label),
        )
        self.wait()
        self.play(FadeIn(prior_example))
        self.play(
            LaggedStartMap(
                Indicate, diagram.people[::10],
                color=BLUE,
            )
        )
        self.wait()
        self.play(
            FadeInFrom(prior_word, RIGHT),
            GrowArrow(prior_arrow)
        )
        self.wait()
        prior_group = VGroup(
            prior_word, prior_arrow,
            prior_label, prior_example,
            diagram.h_brace,
        )

        # First likelihood split
        like_example = TexMobject("= 0.4")
        like_example.add_updater(
            lambda m: m.next_to(diagram.he_brace, LEFT)
        )
        like_example.update()

        like_label = TexMobject("P(E|H)", tex_to_color_map=t2c)
        like_label.add_updater(
            lambda m: m.next_to(like_example, LEFT)
        )
        like_label.update()

        like_word = TextMobject("``Likelihood''")
        like_word.next_to(like_label, UP, buff=LARGE_BUFF, aligned_edge=LEFT)
        like_arrow = Arrow(
            like_word.get_bottom(),
            like_label.get_top(),
            buff=0.2,
        )

        limit_arrow = Vector(0.5 * UP)
        limit_arrow.next_to(like_label.get_part_by_tex("|"), UP, SMALL_BUFF)
        limit_arrow.set_stroke(WHITE, 4)
        limit_word = TextMobject("Limit\\\\your\\\\view")
        limit_word.next_to(limit_arrow, UP)

        hne_people = diagram.people[:6]
        he_people = diagram.people[6:10]
        nhe_people = diagram.people[19::10]
        nhne_people = diagram.people[10:]
        nhne_people.remove(*nhe_people)

        for group in [hne_people, nhne_people]:
            group.generate_target()
            group.target.set_opacity(0.25)
            group.target.set_stroke(BLACK, 0, background=True)

        self.play(
            diagram.he_rect.set_opacity, 1,
            diagram.hne_rect.set_opacity, 1,
            MoveToTarget(hne_people),
            GrowFromCenter(diagram.he_brace),
            FadeInFrom(like_label, RIGHT),
            FadeInFrom(like_example, RIGHT),
        )
        self.wait()
        self.play(
            ShowCreationThenFadeAround(
                like_label.get_part_by_tex("E"),
                surrounding_rectangle_config={
                    "color": EVIDENCE_COLOR1
                }
            ),
        )
        self.play(WiggleOutThenIn(like_label.get_part_by_tex("|")))
        self.play(
            ShowCreationThenFadeAround(
                like_label.get_part_by_tex("H")
            ),
        )
        self.wait()
        self.play(
            diagram.people[10:].set_opacity, 0.2,
            diagram.nh_rect.set_opacity, 0.2,
            FadeInFrom(limit_word, DOWN),
            GrowArrow(limit_arrow),
            rate_func=there_and_back_with_pause,
            run_time=6,
        )
        self.wait()
        self.play(
            Write(like_word, run_time=1),
            GrowArrow(like_arrow),
        )
        self.wait()

        # Show anti-likelihood
        anti_label = TexMobject("P(E| \\neg H)", tex_to_color_map=t2c)
        anti_label.add_updater(
            lambda m: m.next_to(diagram.nhe_brace, RIGHT)
        )

        anti_example = TexMobject("= 0.1")
        anti_example.add_updater(
            lambda m: m.next_to(anti_label, RIGHT).align_to(anti_label[0], DOWN)
        )

        neg_sym = anti_label.get_part_by_tex("\\neg").copy()
        neg_sym.generate_target()
        neg_sym.target.scale(2.5)
        not_word = TextMobject("means ", "``not''")
        not_word[1].set_color(RED)
        neg_group = VGroup(neg_sym.target, not_word)
        neg_group.arrange(RIGHT)
        neg_group.next_to(anti_label, UP, LARGE_BUFF)
        neg_group.to_edge(RIGHT, buff=MED_SMALL_BUFF)

        diagram.nhe_rect.set_opacity(1)
        diagram.nhe_rect.save_state()
        diagram.nhe_rect.become(diagram.nh_rect)
        self.play(
            Restore(diagram.nhe_rect),
            GrowFromCenter(diagram.nhe_brace),
            MoveToTarget(nhne_people),
            FadeInFrom(anti_label, LEFT),
            FadeInFrom(anti_example, LEFT),
        )
        diagram.nhne_rect.set_opacity(1)
        self.wait()
        self.play(
            ShowCreationThenFadeAround(
                anti_label[2],
                surrounding_rectangle_config={"color": EVIDENCE_COLOR1},
            )
        )
        self.play(
            ShowCreationThenFadeAround(
                anti_label[4:6],
                surrounding_rectangle_config={"color": RED},
            )
        )
        self.wait()
        self.play(
            MoveToTarget(neg_sym),
            FadeIn(not_word)
        )
        self.wait()
        self.play(
            FadeOut(not_word),
            Transform(neg_sym, anti_label.get_part_by_tex("\\neg"))
        )
        self.remove(neg_sym)

        # Recall final answer, geometrically
        he_group = VGroup(diagram.he_rect, he_people)
        nhe_group = VGroup(diagram.nhe_rect, nhe_people)

        denom = VGroup(
            he_group.copy(),
            TexMobject("+"),
            nhe_group.copy(),
        )
        denom.arrange(RIGHT)
        answer = VGroup(
            he_group.copy(),
            Underline(denom, stroke_width=2),
            denom,
        )
        answer.arrange(DOWN)
        answer.scale(0.5)
        answer.to_edge(UP, MED_SMALL_BUFF)
        answer.shift(LEFT)

        equals = TexMobject("=")
        posterior.generate_target()

        post_group = VGroup(posterior.target, equals, answer)
        post_group.arrange(RIGHT)
        post_group.to_corner(UL)

        post_word = TextMobject("``Posterior''")
        post_word.set_color(YELLOW)
        post_word.to_corner(UL, buff=MED_SMALL_BUFF)

        post_arrow = Arrow(
            post_word.get_bottom(),
            posterior.target[1].get_top(),
            buff=0.2,
        )
        post_arrow.set_stroke(WHITE, 5)

        dividing_line = DashedLine(ORIGIN, FRAME_WIDTH * RIGHT)
        dividing_line.set_stroke(WHITE, 1)
        dividing_line.next_to(answer, DOWN)
        dividing_line.set_x(0)

        diagram.add(
            diagram.h_brace,
            diagram.he_brace,
            diagram.nhe_brace,
        )
        diagram.generate_target(use_deepcopy=True)
        diagram.target.scale(0.5, about_edge=DL)
        diagram.target.refresh_braces()

        like_group = VGroup(like_word, like_arrow)
        like_vect = like_group.get_center() - like_label.get_center()
        self.play(
            ShowCreation(dividing_line),
            MoveToTarget(diagram),
            MaintainPositionRelativeTo(like_group, like_label),
            MaintainPositionRelativeTo(
                VGroup(prior_word, prior_arrow),
                prior_label,
            ),
            MoveToTarget(posterior),
            ReplacementTransform(posterior_words, equals),
        )
        like_group.move_to(like_label.get_center() + like_vect)
        self.wait()
        self.play(TransformFromCopy(he_group, answer[0]))
        self.play(ShowCreation(answer[1]))
        self.play(LaggedStart(
            TransformFromCopy(he_group, answer[2][0]),
            GrowFromCenter(answer[2][1]),
            TransformFromCopy(nhe_group, answer[2][2]),
        ))
        self.wait()

        # Write final answer, as a formula
        formula = TexMobject(
            "=", "{P(H) P(E | H)", "\\over",
            "P(H) P(E | H) + P(\\neg H) P(E | \\neg H)}",
            tex_to_color_map=t2c
        )
        formula.scale(0.9)
        formula.next_to(answer, RIGHT)

        ph = formula[2:6]
        peh = formula[6:12]
        over = formula.get_part_by_tex("\\over")
        ph2 = formula[13:17]
        peh2 = formula[17:23]
        pnh = formula[23:28]
        penh = formula[28:]

        parts = VGroup(ph, peh, ph2, peh2, pnh, penh)
        parts.save_state()

        np1 = TexMobject("(\\# I )")[0]
        person = Person()
        person.replace(np1[2], dim_to_match=1)
        person.scale(1.5)
        np1.submobjects[2] = person

        np1.match_height(ph)
        np1.next_to(ph, LEFT, SMALL_BUFF)
        VGroup(np1, ph, peh).match_x(over)

        np2 = np1.copy()
        np2.next_to(ph2, LEFT, SMALL_BUFF)
        VGroup(np2, ph2, peh2).match_width(
            VGroup(ph2, peh2), about_edge=RIGHT
        )

        np3 = np1.copy()
        np3.next_to(pnh, LEFT, SMALL_BUFF)
        VGroup(np3, pnh, penh).match_width(
            VGroup(pnh, penh), about_edge=RIGHT
        )

        nps = VGroup(np1, np2, np3)
        crosses = VGroup(*[Cross(np)[0] for np in nps])

        top_brace = Brace(np1, UP, buff=SMALL_BUFF)
        top_count = Integer(210)
        top_count.add_updater(lambda m: m.next_to(top_brace, UP, SMALL_BUFF))

        low_brace = Brace(np3, DOWN, buff=SMALL_BUFF)
        low_count = Integer(210)
        low_count.add_updater(lambda m: m.next_to(low_brace, DOWN, SMALL_BUFF))

        h_rect = Rectangle(  # Highlighting rectangle
            stroke_color=YELLOW,
            stroke_width=3,
            fill_color=YELLOW,
            fill_opacity=0.25,
        )
        h_rect.replace(diagram.square, stretch=True)

        s_rect = SurroundingRectangle(answer[0])

        diagram.refresh_braces()
        nh_group = VGroup(
            diagram.nh_brace,
            TexMobject("P(\\neg H)", tex_to_color_map=t2c),
            TexMobject("= 20 / 21"),
        )
        nh_group[1].next_to(nh_group[0], UP, SMALL_BUFF)
        nh_group[2].next_to(nh_group[1], RIGHT, SMALL_BUFF)

        self.play(
            Write(formula.get_part_by_tex("=")),
            Write(formula.get_part_by_tex("\\over")),
            run_time=1
        )
        self.play(ShowCreation(s_rect))
        self.wait()
        self.play(
            FadeIn(np1),
            FadeIn(top_brace),
            FadeIn(top_count),
        )
        self.wait()
        self.play(h_rect.replace, diagram.h_rect, {"stretch": True})
        self.play(
            TransformFromCopy(prior_label, ph),
            top_brace.become, Brace(VGroup(np1, ph), UP, buff=SMALL_BUFF),
            ChangeDecimalToValue(top_count, 10),
        )
        self.wait()
        self.play(h_rect.replace, diagram.he_rect, {"stretch": True})
        self.play(
            TransformFromCopy(like_label, peh),
            top_brace.become, Brace(VGroup(np1, peh), UP, buff=SMALL_BUFF),
            ChangeDecimalToValue(top_count, 4)
        )
        self.wait()

        self.play(
            s_rect.move_to, answer[2][0],
            TransformFromCopy(np1, np2),
            TransformFromCopy(ph, ph2),
            TransformFromCopy(peh, peh2),
        )
        self.wait()

        self.play(
            FadeOut(h_rect),
            s_rect.become, SurroundingRectangle(answer[2][2]),
            FadeOut(prior_group),
            FadeIn(nh_group),
        )
        self.wait()
        h_rect.replace(diagram.square, stretch=True)
        self.play(
            FadeIn(np3),
            FadeIn(low_brace),
            FadeIn(low_count),
        )
        self.play(h_rect.replace, diagram.nh_rect, {"stretch": True})
        self.play(
            TransformFromCopy(nh_group[1], pnh),
            low_brace.become, Brace(VGroup(np3, pnh), DOWN, buff=SMALL_BUFF),
            ChangeDecimalToValue(low_count, 200),
        )
        self.play(h_rect.replace, diagram.nhe_rect, {"stretch": True})
        self.play(
            TransformFromCopy(anti_label, penh),
            low_brace.become, Brace(VGroup(np3, penh), DOWN, buff=SMALL_BUFF),
            ChangeDecimalToValue(low_count, 20),
        )
        self.wait()

        # Clean up
        self.play(
            FadeOut(nh_group),
            FadeOut(s_rect),
            FadeOut(h_rect),
            FadeIn(prior_group),
        )
        self.wait()

        self.play(
            ShowCreation(crosses),
            FadeOut(low_brace),
            FadeOut(top_brace),
            FadeOut(low_count),
            FadeOut(top_count),
        )
        self.wait()
        self.play(
            Restore(parts),
            FadeOut(crosses),
            FadeOut(nps),
            answer.set_opacity, 0.2
        )
        self.wait()

        # Write Bayes' theorem
        formula_rect = SurroundingRectangle(formula[1:])
        formula_rect.set_stroke(TEAL, 2)

        bayes_words = TextMobject("Bayes' theorem")
        bayes_words.scale(1.5)
        bayes_words.next_to(formula_rect, UP, SMALL_BUFF)
        bayes_words.match_color(formula_rect)

        self.play(ShowCreation(formula_rect))
        self.play(FadeInFromDown(bayes_words))
        self.wait()

        # Simplify denominator
        simpler_form = get_bayes_formula()[7:]
        simpler_form.move_to(answer)
        pe = simpler_form[-4:].copy()
        pe.save_state()

        big_denom_rect = SurroundingRectangle(VGroup(ph2, penh))
        lil_denom_rect = SurroundingRectangle(pe)
        for rect in big_denom_rect, lil_denom_rect:
            rect.set_stroke(BLUE, 0)
            rect.set_fill(BLUE, 0.25)
        pe.move_to(big_denom_rect)
        pe.set_opacity(0)

        self.play(
            FadeOut(answer),
            FadeIn(simpler_form[:-4])
        )
        self.play(TransformFromCopy(formula_rect, big_denom_rect))
        self.wait()
        self.play(
            Restore(pe),
            ReplacementTransform(big_denom_rect, lil_denom_rect),
            Transform(
                formula_rect,
                SurroundingRectangle(simpler_form, color=TEAL),
            ),
            bayes_words.match_x, simpler_form,
        )
        self.remove(pe)
        self.add(simpler_form)
        self.wait()

        # Show all evidence cases
        he_group_copy = he_group.copy()
        nhe_group_copy = nhe_group.copy()
        copies = VGroup(he_group_copy, nhe_group_copy)

        self.play(
            copies.arrange, RIGHT, {"buff": LARGE_BUFF},
            copies.move_to, DOWN,
            copies.to_edge, RIGHT, LARGE_BUFF,
        )
        self.wait()
        self.play(
            he_group_copy.next_to, VGroup(ph2, peh2), DOWN,
        )
        self.play(
            nhe_group_copy.next_to, VGroup(pnh, penh), DOWN,
        )
        self.wait()
        self.play(
            FadeOut(copies),
            FadeOut(lil_denom_rect),
        )
        self.wait()

        # Name posterior
        self.play(
            GrowArrow(post_arrow),
            FadeInFrom(post_word, RIGHT),
            FadeOut(formula_rect),
            FadeOut(bayes_words),
        )
        self.wait()

        # Show confusion
        randy = Randolph()
        randy.flip()
        randy.next_to(dividing_line, DOWN)
        randy.to_edge(RIGHT)

        prior_rect = SurroundingRectangle(prior_word)
        post_rect = SurroundingRectangle(post_word)
        VGroup(prior_rect, post_rect).set_stroke(WHITE, 2)

        self.play(FadeIn(randy))
        self.play(randy.change, "confused", formula)
        self.play(Blink(randy))
        self.play(randy.change, "horrified", formula)
        self.play(randy.look_at, diagram)
        self.play(Blink(randy))
        self.play(randy.look_at, formula)
        self.play(randy.change, "tired")
        self.wait(2)
        self.play(
            randy.change, "pondering", prior_label,
            ShowCreation(prior_rect)
        )
        self.play(Blink(randy))
        self.play(
            ReplacementTransform(prior_rect, post_rect),
            randy.look_at, post_rect,
        )
        self.play(randy.change, "thinking")
        self.play(FadeOut(post_rect))
        self.play(Blink(randy))
        self.wait()
        self.play(randy.look_at, formula)
        self.play(Blink(randy))
        self.wait()

        # Transition to next scene
        return  # Skip
        to_move = VGroup(posterior, formula)
        self.remove(to_move, *to_move, *to_move[1])
        to_move.generate_target()
        to_move.target[1].scale(1 / 0.9)
        to_move.target.arrange(RIGHT)
        to_move.target.to_corner(UL)

        everything = Group(*self.get_mobjects())

        self.play(
            LaggedStartMap(FadeOutAndShiftDown, everything),
            MoveToTarget(to_move, rate_func=squish_rate_func(smooth, 0.5, 1)),
            run_time=2,
        )

    def get_diagram(self, include_people=True):
        diagram = BayesDiagram(**self.bayes_diagram_config)
        diagram.set_height(5.5)
        diagram.set_width(5.5, stretch=True)
        diagram.move_to(0.5 * DOWN)
        diagram.add_brace_attrs()

        diagram.evidence_split.set_opacity(0)
        diagram.hypothesis_split.set_opacity(0)
        diagram.nh_rect.set_fill(DARK_GREY)

        if include_people:
            people = VGroup(*[Person() for x in range(210)])
            people.set_color(interpolate_color(LIGHT_GREY, WHITE, 0.5))
            people.arrange_in_grid(n_cols=21)
            people.set_width(diagram.get_width() - SMALL_BUFF)
            people.set_height(diagram.get_height() - SMALL_BUFF, stretch=True)
            people.move_to(diagram)
            people[10:].set_color(GREEN_D)
            people.set_stroke(BLACK, 2, background=True)

            diagram.add(people)
            diagram.people = people

        return diagram


class DiscussFormulaAndAreaModel(CreateFormulaFromDiagram):
    CONFIG = {
        "bayes_diagram_config": {
            "prior_rect_direction": DOWN,
        },
    }

    def construct(self):
        # Show smaller denominator
        t2c = self.tex_to_color_map
        formula = TexMobject(
            "P(H | E)", "=",
            "{P(H) P(E | H)", "\\over",
            "P(H) P(E | H) + P(\\neg H) P(E | \\neg H)}",
            tex_to_color_map=t2c
        )
        equals = formula.get_part_by_tex("=")
        equals_index = formula.index_of_part(equals)
        lhs = formula[:equals_index]
        rhs = formula[equals_index + 1:]
        lhs.next_to(equals, LEFT)
        formula.to_corner(UL)

        alt_rhs = TexMobject(
            "{P(H)P(E|H)", "\\over", "P(E)}",
            "=",
            tex_to_color_map=t2c,
        )
        alt_rhs.next_to(equals, RIGHT, SMALL_BUFF)

        s_rect = SurroundingRectangle(rhs[12:], color=BLUE)

        self.add(formula)
        self.wait()
        self.play(ShowCreation(s_rect))
        self.add(alt_rhs, s_rect)
        self.play(
            VGroup(rhs, s_rect).next_to, alt_rhs, RIGHT, SMALL_BUFF,
            GrowFromCenter(alt_rhs),
        )
        self.play(
            s_rect.become,
            SurroundingRectangle(alt_rhs[12:-1], color=BLUE)
        )
        self.wait()

        # Bring in diagram
        diagram = self.get_diagram(include_people=False)
        diagram.evidence_split.set_opacity(1)
        diagram.set_height(3)
        diagram.set_prior(0.1)

        diagram.refresh_braces()
        diagram.add(
            diagram.h_brace,
            diagram.he_brace,
            diagram.nhe_brace,
        )

        h_label, he_label, nhe_label = labels = [
            TexMobject(tex, tex_to_color_map=t2c)
            for tex in ["P(H)", "P(E|H)", "P(E|\\neg H)"]
        ]
        h_label.add_updater(lambda m: m.next_to(diagram.h_brace, DOWN))
        he_label.add_updater(lambda m: m.next_to(diagram.he_brace, LEFT))
        nhe_label.add_updater(lambda m: m.next_to(diagram.nhe_brace, RIGHT))
        diagram.add(*labels)

        diagram.to_corner(DL)

        he_rect_copy = diagram.he_rect.copy()
        nhe_rect_copy = diagram.nhe_rect.copy()

        self.play(
            FadeIn(diagram),
            # FadeOut(s_rect),
            ReplacementTransform(
                VGroup(s_rect, s_rect.copy()),
                VGroup(he_rect_copy, nhe_rect_copy),
            ),
        )
        self.wait()
        self.play(LaggedStart(
            ApplyMethod(he_rect_copy.next_to, rhs[12:22], DOWN),
            ApplyMethod(nhe_rect_copy.next_to, rhs[23:], DOWN),
            lag_ratio=0.7,
            run_time=1.5
        ))
        self.wait()
        self.play(FadeOut(VGroup(he_rect_copy, nhe_rect_copy)))

        # Tell what to memorize
        big_rect = SurroundingRectangle(formula)
        big_rect.set_stroke(WHITE, 2)
        big_rect.set_fill(BLACK, 0)

        words1 = TextMobject("Don't memorize\\\\this")
        words2 = TextMobject("Remember\\\\this")
        for words in words1, words2:
            words.scale(1.5)
            words.to_edge(RIGHT)

        arrow1 = Arrow(words1.get_corner(UL), big_rect.get_bottom())
        arrow2 = Arrow(words2.get_left(), diagram.square.get_right())

        self.play(
            FadeIn(words1),
            ShowCreation(arrow1),
            ShowCreation(big_rect)
        )
        self.wait()
        self.play(
            ReplacementTransform(arrow1, arrow2),
            FadeOut(words1),
            FadeIn(words2),
            big_rect.set_stroke, WHITE, 0,
            big_rect.set_fill, BLACK, 0.7,
        )
        self.wait()

        # Talk about diagram slices
        to_fade = VGroup(
            diagram.evidence_split,
            diagram.he_brace,
            diagram.nhe_brace,
            he_label, nhe_label,
        )
        to_fade.save_state()

        h_part = VGroup(
            diagram.hypothesis_split,
            diagram.h_brace,
            h_label,
        )
        people = self.get_diagram().people
        people.set_width(diagram.square.get_width() - 0.05)
        people.move_to(diagram.square)

        sides = VGroup(
            DashedLine(
                diagram.square.get_corner(UL),
                diagram.square.get_corner(UR),
            ),
            DashedLine(
                diagram.square.get_corner(UL),
                diagram.square.get_corner(DL),
            ),
        )
        sides.set_stroke(YELLOW, 4)
        ones = VGroup(
            TexMobject("1").next_to(diagram.square, UP),
            TexMobject("1").next_to(diagram.square, LEFT),
        )

        self.play(
            to_fade.set_opacity, 0,
            h_part.set_opacity, 0,
            diagram.square.set_opacity, 1,
            ShowIncreasingSubsets(people),
        )
        self.play(FadeOut(people))
        self.play(
            LaggedStartMap(ShowCreation, sides, lag_ratio=0.8),
            LaggedStartMap(FadeIn, ones, lag_ratio=0.8),
        )
        self.wait()

        self.play(
            h_part.set_opacity, 1,
        )
        diagram.square.set_opacity(0)
        self.wait()
        self.play(
            FadeOut(sides),
            FadeOut(ones),
        )
        self.wait()
        VGroup(
            to_fade[1:],
            to_fade[0][::2],
        ).stretch(0, 1, about_edge=DOWN)
        self.play(
            Restore(to_fade),
            diagram.hypothesis_split.set_opacity, 0,
            diagram.hne_rect.set_opacity, 0.2,
            diagram.nhne_rect.set_opacity, 0.2,
        )
        self.wait()

        # Add posterior bar
        post_bar = always_redraw(
            lambda: self.get_posterior_bar(
                diagram.he_rect,
                diagram.nhe_rect,
            )
        )
        post_label = TexMobject("P(H|E)", tex_to_color_map=t2c)
        post_label.add_updater(lambda m: m.next_to(post_bar.brace, DOWN))

        self.play(
            FadeOut(words2),
            FadeOut(arrow2),
            TransformFromCopy(
                diagram.he_rect, post_bar.rects[0],
            ),
            TransformFromCopy(
                diagram.nhe_rect, post_bar.rects[1],
            ),
            FadeIn(post_bar.brace),
            FadeIn(post_label),
        )
        self.add(post_bar)
        self.wait()

        self.play(
            diagram.set_likelihood, 0.8,
            rate_func=there_and_back,
            run_time=4,
        )
        self.wait()
        self.play(diagram.set_antilikelihood, 0.4)
        self.wait()
        self.play(
            diagram.set_likelihood, 0.8,
            diagram.set_antilikelihood, 0.05,
        )
        self.wait()
        self.play(
            diagram.set_likelihood, 0.4,
            diagram.set_antilikelihood, 0.1,
        )
        self.wait()

        self.play(
            big_rect.set_width, rhs.get_width() + 0.7,
            {"about_edge": RIGHT, "stretch": True},
        )
        self.wait()

        # Terms from formula to sides in diagram
        hs_rect = SurroundingRectangle(alt_rhs[:5], buff=0.05)
        hes_rect = SurroundingRectangle(alt_rhs[5:11], buff=0.05)
        hs_rect.set_stroke(YELLOW, 3)
        hes_rect.set_stroke(BLUE, 3)

        self.play(ShowCreation(hs_rect))
        self.play(ShowCreation(hes_rect))
        self.wait()
        self.play(hs_rect.move_to, h_label)
        self.play(hes_rect.move_to, he_label)
        self.wait()
        self.play(FadeOut(VGroup(hs_rect, hes_rect)))

    def get_posterior_bar(self, he_rect, nhe_rect):
        he_height = he_rect.get_height()
        he_width = he_rect.get_width()
        nhe_height = nhe_rect.get_height()
        nhe_width = nhe_rect.get_width()

        total_width = he_width + nhe_width
        he_area = he_width * he_height
        nhe_area = nhe_width * nhe_height

        posterior = he_area / (he_area + nhe_area)

        new_he_width = posterior * total_width
        new_he_height = he_area / new_he_width
        new_nhe_width = (1 - posterior) * total_width
        new_nhe_height = nhe_area / new_nhe_width

        new_he_rect = Rectangle(
            width=new_he_width,
            height=new_he_height,
        ).match_style(he_rect)
        new_nhe_rect = Rectangle(
            width=new_nhe_width,
            height=new_nhe_height,
        ).match_style(nhe_rect)

        rects = VGroup(new_he_rect, new_nhe_rect)
        rects.arrange(RIGHT, buff=0)

        brace = Brace(
            new_he_rect, DOWN,
            buff=SMALL_BUFF,
            min_num_quads=2,
        )
        result = VGroup(rects, brace)
        result.rects = rects
        result.brace = brace

        # Put positioning elsewhere?
        result.to_edge(RIGHT)
        return result


class RandomShapes(Scene):
    def construct(self):
        diagram = BayesDiagram(0.1, 0.4, 0.1)
        diagram.set_height(3)

        e_part = VGroup(diagram.he_rect, diagram.nhe_rect).copy()
        e_part.set_fill(BLUE)

        circle = Circle()
        circle.set_fill(RED, 0.5)
        circle.set_stroke(RED, 2)
        circle.move_to(diagram)

        tri = Polygon(UP, ORIGIN, RIGHT)
        tri.match_height(diagram)
        tri.set_fill(PURPLE, 0.5)
        tri.set_stroke(PURPLE, 2)
        tri.move_to(diagram)

        h_rect = diagram.h_rect
        h_rect.set_fill(YELLOW, 1)

        pi = TexMobject("\\pi")
        pi.set_height(2)
        pi.set_stroke(GREEN, 2)
        pi.set_fill(GREEN, 0.5)

        events = VGroup(
            e_part,
            h_rect,
            circle,
            pi,
            tri,
        )

        last = VMobject()
        for event in events:
            self.play(
                FadeIn(event),
                FadeOut(last)
            )
            self.wait()
            last = event


class BigArrow(Scene):
    def construct(self):
        arrow = Arrow(
            3 * DOWN + 4 * LEFT,
            3 * RIGHT + DOWN,
            path_arc=50 * DEGREES,
        )

        self.play(ShowCreation(arrow))
        self.wait()


class UsesOfBayesTheorem(Scene):
    def construct(self):
        formula = get_bayes_formula()
        formula.to_corner(UL)
        rhs = formula[7:]

        bubble = ThoughtBubble(direction=RIGHT)
        bubble.set_fill(opacity=0)
        arrow = Vector(RIGHT)
        arrow.next_to(formula, RIGHT, MED_LARGE_BUFF)
        bubble.set_height(2)
        bubble.next_to(arrow, RIGHT, MED_LARGE_BUFF)
        bubble.align_to(formula, UP)
        bar = ProbabilityBar(
            0.1,
            percentage_background_stroke_width=1,
            include_braces=False,
        )
        bar.set_width(0.8 * bubble[-1].get_width())
        bar.move_to(bubble.get_bubble_center())

        scientist = SVGMobject(file_name="scientist")
        programmer = SVGMobject(file_name="programmer")
        randy = Randolph().flip()

        people = VGroup(scientist, programmer, randy)
        people.set_stroke(width=0)
        for person in people:
            person.set_height(2.5)
            if person is not randy:
                person.set_fill(GREY)
                person.set_sheen(0.5, UL)

        people.arrange(RIGHT, buff=2.5)
        # programmer.shift(0.5 * RIGHT)
        people.to_edge(DOWN, buff=LARGE_BUFF)

        self.add(formula)
        self.wait()
        self.play(GrowArrow(arrow))
        self.play(ShowCreation(bubble), FadeIn(bar))
        self.play(bar.p_tracker.set_value, 0.8)
        self.wait()

        # Add people
        for person in [scientist, programmer]:
            self.play(FadeInFrom(person, DOWN))
            rhs_copy = rhs.copy()
            rhs_copy.add_to_back(
                SurroundingRectangle(
                    rhs_copy,
                    fill_color=BLACK,
                    fill_opacity=0.8,
                    stroke_color=WHITE,
                    stroke_width=2,
                )
            )
            rhs_copy.generate_target()
            rhs_copy[0].set_stroke(width=0)
            rhs_copy.target.scale(0.5)
            rhs_copy.target.move_to(person.get_corner(DR))
            self.add(rhs_copy, formula)
            self.play(MoveToTarget(rhs_copy))
            self.wait()
            person.add(rhs_copy)

        self.play(FadeInFromDown(randy))
        self.play(randy.change, "pondering")
        self.play(Blink(randy))
        bubble_group = VGroup(bubble, bar)
        self.play(
            bubble_group.scale, 1.4,
            bubble_group.move_to, randy.get_corner(UL), DR,
            randy.look_at, bubble,
            FadeOut(arrow),
        )
        self.wait()
        self.play(Blink(randy))
        self.play(bar.p_tracker.set_value, 0.3, run_time=3)
        self.play(randy.change, "thinking")
        self.play(Blink(randy))
        self.wait()

        self.play(LaggedStartMap(
            FadeOutAndShift,
            VGroup(*people, bubble_group),
            lambda m: (m, DOWN),
            lag_ratio=0.15,
        ))


class AskAboutWhenProbabilityIsIntuitive(TeacherStudentsScene):
    def construct(self):
        words = TextMobject("What makes probability\\\\more intuitive?")
        words.move_to(self.hold_up_spot, DOWN)
        words.shift_onto_screen()

        self.play(
            self.teacher.change, "speaking",
            self.get_student_changes(
                "pondering", "sassy", "happy",
                look_at_arg=self.screen,
            )
        )
        self.wait(2)

        self.play(
            self.teacher.change, "raise_right_hand",
            FadeInFrom(words, DOWN),
            self.get_student_changes("erm", "pondering", "confused")
        )
        self.wait(2)
        self.play(
            words.scale, 2,
            words.center,
            words.to_edge, UP,
            self.teacher.change, "pondering", 3 * UP,
            self.get_student_changes(
                "pondering", "thinking", "thinking",
                look_at_arg=3 * UP,
            )
        )
        self.wait(6)


class IntroduceLinda(DescriptionOfSteve):
    def construct(self):
        # Kahneman and Tversky
        images = self.get_images()

        self.play(
            LaggedStartMap(
                FadeInFrom, images,
                lambda m: (m, LEFT),
                lag_ratio=0.3,
                run_time=3,
            )
        )
        self.wait()

        # Add steve
        steve = Steve()
        steve.set_height(3)
        steve.move_to(2 * RIGHT)
        steve.to_edge(DOWN, buff=LARGE_BUFF)
        steve_words = self.get_description()
        steve_words.scale(0.8)
        steve_words.next_to(steve, UP, LARGE_BUFF)

        self.play(LaggedStart(
            FadeInFrom(steve, LEFT),
            FadeInFrom(steve_words, LEFT),
        ))
        self.wait()

        # Replace with Linda
        linda = Linda()
        linda_words = self.get_linda_description()
        linda_words.scale(0.8)
        linda.replace(steve)
        linda_words.move_to(steve_words)

        self.play(
            LaggedStart(
                FadeOutAndShift(steve_words, 2 * RIGHT),
                FadeOutAndShift(steve, 2 * RIGHT),
                FadeInFrom(linda, 2 * LEFT),
                lag_ratio=0.15,
            )
        )
        self.wait()

        self.play(
            FadeIn(linda_words),
            lag_ratio=0.1,
            run_time=6,
            rate_func=linear,
        )
        self.wait()

        # Ask question
        options = VGroup(
            TextMobject("1) Linda is a bank teller."),
            TextMobject(
                "2) Linda is a bank teller and is active\\\\",
                "\\phantom{2)} in the feminist movement.",
                alignment="",
            ),
        )
        options.arrange(DOWN, buff=LARGE_BUFF, aligned_edge=LEFT)
        options.to_edge(DOWN, buff=LARGE_BUFF)

        self.play(
            linda.match_height, linda_words,
            linda.next_to, linda_words, LEFT, LARGE_BUFF,
            LaggedStartMap(
                FadeOutAndShift, images,
                lambda m: (m, 2 * LEFT),
            )
        )
        for option in options:
            self.play(FadeIn(option, lag_ratio=0.1, run_time=2))
            self.wait()

        # Show result
        rect = SurroundingRectangle(options[1], color=RED)
        options.generate_target()
        rect.generate_target()
        VGroup(options.target, rect.target).to_edge(LEFT)

        result = VGroup(
            Integer(85, unit="\\%"),
            TextMobject("chose this!")
        )
        result.arrange(RIGHT)
        result.scale(1.25)
        result.set_color(RED)
        result.move_to(rect)
        result.to_edge(RIGHT)
        result.shift(2 * UP)
        arrow = Arrow(result.get_bottom(), rect.target.get_corner(UR))

        self.play(
            MoveToTarget(options),
            MoveToTarget(rect),
            VFadeIn(rect),
            FadeInFrom(result, LEFT),
            GrowArrow(arrow)
        )
        self.wait()

        # Show subsets
        fb_words = TextMobject("Active feminist\\\\bank tellers")
        fb_words.scale(0.5)
        fb_words.set_stroke(BLACK, 0, background=True)
        fb_set = Circle()
        fb_set.flip(RIGHT)
        fb_set.rotate(3 * TAU / 8)
        fb_set.set_stroke(WHITE, 1)
        fb_set.set_fill(YELLOW, 0.5)
        fb_set.replace(fb_words, stretch=True)
        fb_set.scale(1.2)
        fb_set.stretch(1.4, 1)
        fb_group = VGroup(fb_set, fb_words)
        fb_group.move_to(linda_words, RIGHT)

        b_set = fb_set.copy()
        b_set.set_fill(BLUE)
        b_set.scale(3, about_edge=RIGHT)
        b_set.stretch(1.5, 1)
        b_set.to_corner(UR)
        b_words = TextMobject("Bank\\\\tellers")
        b_words.next_to(b_set.get_left(), RIGHT, LARGE_BUFF)

        self.play(
            FadeOut(linda_words),
            TransformFromCopy(rect, fb_set),
            ReplacementTransform(
                VectorizedPoint(rect.get_center()),
                fb_words,
            ),
        )
        self.add(fb_set, fb_words)
        self.wait()
        self.add(b_set, fb_set, fb_words)
        self.play(
            DrawBorderThenFill(b_set),
            TransformFromCopy(
                options[0][0][10:], b_words[0]
            ),
        )
        sets_group = VGroup(b_set, b_words, fb_set, fb_words)
        self.add(sets_group)
        self.wait()

        # Reduce 85
        number = result[0]

        self.play(
            LaggedStart(
                FadeOut(arrow),
                FadeOut(result[1]),
                FadeOut(rect),
                FadeOut(options[0]),
                FadeOut(options[1]),
            ),
            number.scale, 1.5,
            number.move_to, DOWN,
        )
        self.play(
            ChangeDecimalToValue(number, 0),
            UpdateFromAlphaFunc(
                number,
                lambda m, a: m.set_color(interpolate_color(
                    RED, GREEN, a,
                ))
            ),
            run_time=2,
        )
        self.wait()

        self.play(
            FadeOut(number),
            FadeOut(sets_group),
            FadeIn(linda_words),
        )

        # New options
        words = TextMobject("100 people fit this description.  How many are:")
        words.set_color(BLUE_B)
        kw = {"tex_to_color_map": {"\\underline{\\qquad}": WHITE}}
        new_options = VGroup(
            TextMobject("1) Bank tellers? \\underline{\\qquad} of 100", **kw),
            TextMobject(
                "2) Bank tellers and active in the",
                " feminist movement? \\underline{\\qquad} of 100",
                **kw
            ),
        )
        new_options.scale(0.9)
        new_options.arrange(DOWN, aligned_edge=LEFT, buff=MED_LARGE_BUFF)
        new_options.to_edge(DOWN, buff=LARGE_BUFF)

        words.next_to(new_options, UP, LARGE_BUFF)

        self.play(
            FadeIn(words, lag_ratio=0.1, rate_func=linear)
        )
        self.wait()
        for option in new_options:
            self.play(FadeIn(option))
            self.wait()

        example_numbers = VGroup(Integer(8), Integer(5))
        for exn, option in zip(example_numbers, new_options):
            line = option.get_part_by_tex("underline")
            exn.scale(1.1)
            exn.next_to(line, UP, buff=0.05)
            exn.set_color(YELLOW)
            self.play(Write(exn))
        self.wait()

    def get_images(self):
        images = Group(
            ImageMobject("kahneman"),
            ImageMobject("tversky"),
        )
        images.set_height(3.5)
        images.arrange(DOWN, buff=0.5)
        images.to_edge(LEFT, buff=MED_LARGE_BUFF)

        names = VGroup(
            TextMobject("Kahneman", alignment=""),
            TextMobject("Tversky", alignment=""),
        )
        for name, image in zip(names, images):
            name.next_to(image, DOWN)
            image.name = name
            image.add(name)
        images.arrange(DOWN, buff=1, aligned_edge=LEFT)
        images.set_height(FRAME_HEIGHT - 1)
        images.to_edge(LEFT)
        return images

    def get_linda_description(self):
        result = TextMobject(
            "Linda is 31 years old, single, outspoken, and\\\\",
            "very bright. She majored in philosophy. As a \\\\",
            "student, she was deeply concerned with issues\\\\",
            "of discrimination and social justice, and also\\\\",
            "participated in anti-nuclear demonstrations.\\\\",
            alignment="",
            tex_to_color_map={
                "deeply concerned with issues": YELLOW,
                "of discrimination": YELLOW,
            }
        )
        return result


class LindaDescription(IntroduceLinda):
    def construct(self):
        words = self.get_linda_description()
        words.set_color(WHITE)

        highlighted_part = VGroup(
            *words.get_part_by_tex("deeply"),
            *words.get_part_by_tex("discrimination"),
        )

        self.add(words)
        self.play(
            FadeIn(words),
            run_time=3,
            lag_ratio=0.01,
            rate_func=linear,
        )
        self.wait(1)
        self.play(
            highlighted_part.set_color, YELLOW,
            lag_ratio=0.1,
            run_time=2
        )
        self.wait()


class AlternatePhrasings(PiCreatureScene):
    CONFIG = {
        "camera_config": {
            "background_color": DARKER_GREY,
        }
    }

    def construct(self):
        randy = self.pi_creature

        phrases = VGroup(
            TextMobject("40 out of 100"),
            TexMobject("40\\%"),
            TexMobject("0.4"),
            TextMobject("What's more likely$\\dots$"),
        )
        for phrase in phrases:
            phrase.scale(1.5)
            phrase.next_to(randy, RIGHT, buff=LARGE_BUFF)
            phrase.align_to(randy, UP)

        def push_down(phrase):
            phrase.scale(0.8, about_edge=LEFT)
            phrase.shift(1 * DOWN)
            phrase.set_opacity(0.5)
            return phrase

        bubble = randy.get_bubble()
        content_width = 4.5

        people = VGroup(*[Person() for x in range(100)])
        people.arrange_in_grid(n_cols=20)
        people.set_width(content_width)
        people.move_to(bubble.get_bubble_center())
        people.shift(SMALL_BUFF * UP)
        people[:40].set_color(YELLOW)

        bar = ProbabilityBar(0.9999)
        bar.set_width(content_width)
        bar.move_to(people)

        steve = Steve()
        steve.set_height(1)
        steve_words = TextMobject("seems bookish...")
        steve_words.next_to(steve, RIGHT, MED_LARGE_BUFF)
        steve.add(steve_words)

        linda = Linda()
        linda.set_height(1)
        linda_words = TextMobject("seems activist...")
        linda_words.next_to(linda, RIGHT, MED_LARGE_BUFF)
        linda.add(linda_words)

        stereotypes = VGroup(steve, linda)
        stereotypes.arrange(DOWN, buff=MED_SMALL_BUFF, aligned_edge=LEFT)
        stereotypes.move_to(people)

        self.play(
            FadeInFrom(phrases[0], UP),
            randy.change, "pondering",
        )
        self.play(
            DrawBorderThenFill(bubble, lag_ratio=0.1),
            FadeIn(people, lag_ratio=0.1),
            randy.change, "thinking", people,
        )
        self.wait()
        self.play(
            FadeInFrom(phrases[1], UP),
            randy.change, "confused", phrases[1],
            FadeOut(people),
            ApplyFunction(push_down, phrases[0]),
            FadeIn(bar),
        )
        self.play(bar.p_tracker.set_value, 0.4)
        bar.clear_updaters()
        self.play(
            FadeInFrom(phrases[2], UP),
            ApplyFunction(push_down, phrases[:2]),
            FadeOut(bar.percentages),
            randy.change, "guilty",
        )
        self.wait()
        bar.remove(bar.percentages)
        self.play(
            FadeInFrom(phrases[3], UP),
            ApplyFunction(push_down, phrases[:3]),
            FadeOut(bar),
            FadeIn(stereotypes),
            randy.change, "shruggie", stereotypes,
        )
        self.wait(6)


class WhenDiscreteChunksArentSoClean(Scene):
    def construct(self):
        squares = VGroup(*[Square() for x in range(100)])
        squares.arrange_in_grid(n_cols=10, buff=0)
        squares.set_stroke(WHITE, 1)
        squares.set_fill(DARKER_GREY, 1)
        squares.set_height(6)
        squares.to_edge(DOWN)

        target_p = 0.3612

        rain, sun = icon_templates = VGroup(
            SVGMobject("rain_cloud"),
            SVGMobject("sunny"),
        )
        for icon in icon_templates:
            icon.set_width(0.6 * squares[0].get_width())
            icon.set_stroke(width=0)
            icon.set_sheen(0.5, UL)
        rain.set_color(BLUE_E)
        sun.set_color(YELLOW)

        partial_rects = VGroup()
        icons = VGroup()
        q_marks = VGroup()
        for i, square in enumerate(squares):
            icon = rain.copy() if i < 40 else sun.copy()
            icon.move_to(square)
            icons.add(icon)

            partial_rect = square.copy()
            partial_rect.set_stroke(width=0)
            partial_rect.scale(0.95)
            partial_rect.stretch(
                0.4,
                0,
                about_edge=RIGHT
            )
            partial_rects.add(partial_rect)

            q_mark = TexMobject("?")
            q_mark.replace(partial_rect, dim_to_match=0)
            q_mark.scale(0.8)
            q_marks.add(q_mark)

        p_label = VGroup(
            TexMobject("P", "(", "\\text{Rain}", ")", "="),
            DecimalNumber(40, unit="\\%", num_decimal_places=2)
        )
        percentage = p_label[1]
        p_label.arrange(RIGHT)
        p_label.to_edge(UP)
        p_label[0].set_color_by_tex("Rain", BLUE)
        percentage.align_to(p_label[0][0], DOWN)

        alt_percentage = Integer(0, unit="\\%")
        alt_percentage.move_to(percentage, LEFT)

        self.add(squares)
        self.add(p_label[0])
        self.play(
            ChangeDecimalToValue(alt_percentage, 40),
            ShowIncreasingSubsets(icons[:40])
        )
        self.play(FadeIn(icons[40:]))
        self.wait()
        self.remove(alt_percentage)
        self.add(percentage)
        self.play(
            ChangeDecimalToValue(percentage, 100 * target_p),
            FadeIn(partial_rects[30:40]),
            FadeIn(q_marks[30:40], lag_ratio=0.3)
        )
        self.wait(2)

        l_rect = Rectangle(fill_color=BLUE_D)
        r_rect = Rectangle(fill_color=LIGHT_GREY)
        rects = VGroup(l_rect, r_rect)
        for rect, p in (l_rect, target_p), (r_rect, 1 - target_p):
            rect.set_height(squares.get_height())
            rect.set_width(p * squares.get_width(), stretch=True)
            rect.set_stroke(WHITE, 2)
            rect.set_fill(opacity=1)
        rects.arrange(RIGHT, buff=0)
        rects.move_to(squares)

        brace = Brace(l_rect, UP, buff=SMALL_BUFF)

        sun = icons[40].copy()
        rain = icons[0].copy()
        for mob, rect in [(rain, l_rect), (sun, r_rect)]:
            mob.generate_target()
            mob.target.set_stroke(BLACK, 3, background=True)
            mob.target.set_height(1)
            mob.target.move_to(rect)
        self.play(
            FadeIn(rects),
            MoveToTarget(rain),
            MoveToTarget(sun),
            GrowFromCenter(brace),
            p_label.shift,
            brace.get_top() + MED_SMALL_BUFF * UP -
            percentage.get_bottom(),
        )
        self.wait()

        # With updaters
        full_width = rects.get_width()
        rain.add_updater(lambda m: m.move_to(l_rect))
        sun.add_updater(lambda m: m.move_to(r_rect))
        r_rect.add_updater(lambda m: m.set_width(
            full_width - l_rect.get_width(),
            about_edge=RIGHT,
            stretch=True,
        ))
        brace.add_updater(lambda m: m.match_width(l_rect, stretch=True))
        brace.add_updater(lambda m: m.next_to(l_rect, UP, SMALL_BUFF))
        percentage.add_updater(lambda m: m.set_value(
            100 * l_rect.get_width() / full_width,
        ))
        percentage.add_updater(lambda m: m.next_to(brace, UP, MED_SMALL_BUFF))

        self.play(
            MaintainPositionRelativeTo(p_label[0], percentage),
            l_rect.stretch, 2, 0, {"about_edge": LEFT},
            run_time=8,
            rate_func=there_and_back,
        )


class RandomnessVsProportions(Scene):
    def construct(self):
        prob_word = TextMobject("Probability")
        unc_word = TextMobject("Uncertainty")
        prop_word = TextMobject("Proportions")
        words = VGroup(prop_word, prob_word, unc_word)
        words.arrange(RIGHT, buff=LARGE_BUFF)
        words.set_width(FRAME_WIDTH - 1)
        words.to_edge(UP)
        arrows = VGroup()
        for w1, w2 in zip(words, words[1:]):
            arrow = TexMobject("\\rightarrow")
            arrow.move_to(midpoint(w1.get_right(), w2.get_left()))
            arrows.add(arrow)

        random_dice = self.get_random_dice()
        random_dice.next_to(unc_word, DOWN, LARGE_BUFF)

        diagram = self.get_dice_diagram()
        diagram.next_to(prop_word, DOWN, LARGE_BUFF)
        diagram.shift_onto_screen()

        grid = diagram[0]
        border = grid[0][0].copy()
        border.set_stroke(BLACK, 3)
        border.set_fill(WHITE, opacity=0.2)
        border.scale(1.02)

        def update_border(border):
            r1, r2 = random_dice
            i = len(r1[1]) - 1
            j = len(r2[1]) - 1
            border.move_to(diagram[0][i][j])
        border.add_updater(update_border)

        example = VGroup(
            TextMobject("P(X = 5)", tex_to_color_map={"5": YELLOW}),
            Line(LEFT, RIGHT)
        )
        example.arrange(RIGHT)
        example.next_to(grid, RIGHT, LARGE_BUFF)
        example.align_to(random_dice, RIGHT)
        example.shift(0.5 * DOWN)
        grid_copy = grid.copy()
        five_part = VGroup(*[
            square
            for i, row in enumerate(grid_copy)
            for j, square in enumerate(row)
            if i + j == 3
        ])

        self.play(FadeInFromDown(prob_word))
        self.play(
            FadeInFrom(unc_word, LEFT),
            Write(arrows[1]),
        )
        self.add(random_dice)
        self.wait(9)
        self.play(
            FadeInFrom(prop_word, RIGHT),
            Write(arrows[0])
        )
        self.play(FadeIn(diagram))
        self.add(border)
        self.wait(2)

        self.play(FadeIn(example))
        self.add(grid_copy, diagram[1])
        self.play(
            grid_copy.set_width, 0.8 * example[1].get_width(),
            grid_copy.next_to, example[1], DOWN,
        )
        self.play(five_part.copy().next_to, example[1], UP)
        self.wait(6)

    def get_die_faces(self):
        dot = Dot()
        dot.set_width(0.15)
        dot.set_color(BLUE_B)

        square = Square()
        square.round_corners(0.25)
        square.set_stroke(WHITE, 2)
        square.set_fill(DARKER_GREY, 1)
        square.set_width(0.6)

        edge_groups = [
            (ORIGIN,),
            (UL, DR),
            (UL, ORIGIN, DR),
            (UL, UR, DL, DR),
            (UL, UR, ORIGIN, DL, DR),
            (UL, UR, LEFT, RIGHT, DL, DR),
        ]

        arrangements = VGroup(*[
            VGroup(*[
                dot.copy().move_to(square.get_critical_point(ec))
                for ec in edge_group
            ])
            for edge_group in edge_groups
        ])
        square.set_width(1)

        faces = VGroup(*[
            VGroup(square.copy(), arrangement)
            for arrangement in arrangements
        ])
        faces.arrange(RIGHT)

        return faces

    def get_random_dice(self):
        faces = list(self.get_die_faces())

        def get_random_pair():
            result = VGroup(*random.sample(faces, 2)).copy()
            result.arrange(RIGHT)
            for mob in result:
                mob.shift(random.random() * RIGHT * MED_SMALL_BUFF)
                mob.shift(random.random() * UP * MED_SMALL_BUFF)
            return result

        result = VGroup(*get_random_pair())
        result.time = 0
        result.iter_count = 0

        def update_result(group, dt):
            group.time += dt
            group.iter_count += 1
            if int(group.time) % 3 == 2:
                group.set_stroke(YELLOW)
                return group
            elif result.iter_count % 3 != 0:
                return group
            else:
                pair = get_random_pair()
                pair.move_to(group)
                group.submobjects = [*pair]

        result.add_updater(update_result)
        result.update()
        return result

    def get_dice_diagram(self):
        grid = VGroup(*[
            VGroup(*[
                Square() for x in range(6)
            ]).arrange(RIGHT, buff=0)
            for y in range(6)
        ]).arrange(DOWN, buff=0)
        grid.set_stroke(WHITE, 1)
        grid.set_height(5)

        colors = color_gradient([RED, YELLOW, GREEN, BLUE], 11)

        numbers = VGroup()
        for i, row in enumerate(grid):
            for j, square in enumerate(row):
                num = Integer(i + j + 2)
                num.set_height(square.get_height() - MED_LARGE_BUFF)
                num.move_to(square)
                # num.set_stroke(BLACK, 2, background=True)
                num.set_fill(DARK_GREY)
                square.set_fill(colors[i + j], 0.9)
                numbers.add(num)

        faces = VGroup()
        face_templates = self.get_die_faces()
        face_templates.scale(0.5)
        for face, row in zip(face_templates, grid):
            face.next_to(row, LEFT, MED_SMALL_BUFF)
            faces.add(face)
        for face, square in zip(faces.copy(), grid[0]):
            face.next_to(square, UP, MED_SMALL_BUFF)
            faces.add(face)

        result = VGroup(grid, numbers, faces)
        return result


class JustRandomDice(RandomnessVsProportions):
    def construct(self):
        random_dice = self.get_random_dice()
        random_dice.center()

        self.add(random_dice)
        self.wait(60)


class BayesTheoremOnProportions(Scene):
    def construct(self):
        # Place on top of visuals from "HeartOfBayes"
        formula = get_bayes_formula()
        formula.scale(1.5)

        title = TextMobject("Bayes' theorem")
        title.scale(2)
        title.next_to(formula, UP, LARGE_BUFF)
        group = VGroup(formula, title)

        equals = TexMobject("=")
        equals.next_to(formula, RIGHT)
        h_line = Line(LEFT, RIGHT)
        h_line.set_width(4)
        h_line.next_to(equals, RIGHT)
        h_line.set_stroke(WHITE, 3)

        self.add(group)
        self.wait()
        self.play(
            group.to_edge, LEFT,
            MaintainPositionRelativeTo(equals, group),
            VFadeIn(equals),
            MaintainPositionRelativeTo(h_line, group),
            VFadeIn(h_line),
        )

        # People
        people = VGroup(*[Person() for x in range(7)])
        people.arrange(RIGHT)
        people.match_width(h_line)
        people.next_to(h_line, DOWN)
        people.set_color(BLUE_E)
        people[:3].set_color(GREEN)
        num_people = people[:3].copy()

        self.play(FadeIn(people, lag_ratio=0.1))
        self.play(num_people.next_to, h_line, UP)
        self.wait(0.5)

        # Diagrams
        diagram = BayesDiagram(0.25, 0.5, 0.2)
        diagram.set_width(0.7 * h_line.get_width())
        diagram.next_to(h_line, DOWN)
        diagram.hne_rect.set_fill(opacity=0.1)
        diagram.nhne_rect.set_fill(opacity=0.1)
        num_diagram = diagram.deepcopy()
        num_diagram.next_to(h_line, UP)
        num_diagram.nhe_rect.set_fill(opacity=0.1)
        low_diagram_rects = VGroup(diagram.he_rect, diagram.nhe_rect)
        top_diagram_rects = VGroup(num_diagram.he_rect)

        self.play(
            FadeOut(people),
            FadeOut(num_people),
            FadeIn(diagram),
            FadeIn(num_diagram),
        )
        self.wait()

        # Circle each part
        E_part = VGroup(formula[4], *formula[19:]).copy()
        H_part = VGroup(formula[2], *formula[8:18]).copy()

        E_arrow = Vector(UP, color=BLUE)
        E_arrow.next_to(E_part[0], DOWN)
        E_words = TextMobject(
            "...among cases where\\\\$E$ is True",
            tex_to_color_map={"$E$": BLUE},
        )
        E_words.next_to(E_arrow, DOWN)
        H_arrow = Vector(DOWN, color=YELLOW)
        H_arrow.next_to(H_part[0], UP)
        H_words = TextMobject(
            "How often is\\\\$H$ True...",
            tex_to_color_map={"$H$": YELLOW},
        )
        H_words.next_to(H_arrow, UP)

        denom_rect = SurroundingRectangle(E_part[1:], color=BLUE)
        numer_rect = SurroundingRectangle(H_part[1:], color=YELLOW)

        self.play(
            formula.set_opacity, 0.5,
            ApplyMethod(
                E_part.set_stroke, YELLOW, 3, {"background": True},
                rate_func=there_and_back,
            ),
            FadeIn(denom_rect),
            ShowCreation(E_arrow),
            FadeInFrom(E_words, UP),
            low_diagram_rects.set_stroke, TEAL, 3,
        )
        self.wait()
        self.play(
            FadeOut(E_part),
            FadeIn(H_part),
            FadeOut(denom_rect),
            FadeIn(numer_rect),
            ShowCreation(H_arrow),
            FadeInFrom(H_words, DOWN),
            FadeOutAndShift(title, UP),
            low_diagram_rects.set_stroke, WHITE, 1,
            top_diagram_rects.set_stroke, YELLOW, 3,
        )
        self.wait()


class GlimpseOfNextVideo(GraphScene):
    CONFIG = {
        "x_axis_label": "",
        "y_axis_label": "",
        "x_min": 0,
        "x_max": 15,
        "x_axis_width": 12,
        "y_min": 0,
        "y_max": 1.0,
        "y_axis_height": 6,
        "y_tick_frequency": 0.125,
        "add_x_coords": True,
        "formula_position": ORIGIN,
        "dx": 0.2,
    }

    def setup(self):
        super().setup()
        self.setup_axes()
        self.y_axis.add_numbers(
            0.25, 0.5, 0.75, 1,
            number_config={
                "num_decimal_places": 2,
            },
            direction=LEFT,
        )
        if self.add_x_coords:
            self.x_axis.add_numbers(*range(1, 15),)

    def construct(self):
        f1 = self.prior

        def f2(x):
            return f1(x) * self.likelihood(x)

        pe = scipy.integrate.quad(f2, 0, 20)[0]

        graph1 = self.get_graph(f1)
        graph2 = self.get_graph(f2)

        rects1 = self.get_riemann_rectangles(graph1, dx=self.dx)
        rects2 = self.get_riemann_rectangles(graph2, dx=self.dx)

        rects1.set_color(YELLOW_D)
        rects2.set_color(BLUE)
        for rects in rects1, rects2:
            rects.set_stroke(WHITE, 1)

        rects1.save_state()
        rects1.stretch(0, 1, about_edge=DOWN)

        formula = self.get_formula()

        self.play(
            FadeInFromDown(formula[:4]),
            Restore(rects1, lag_ratio=0.05, run_time=2)
        )
        self.wait()
        self.add(rects1.copy().set_opacity(0.4))
        self.play(
            FadeInFromDown(formula[4:10]),
            Transform(rects1, rects2),
        )
        self.wait()
        self.play(
            rects1.stretch, 1 / pe, 1, {"about_edge": DOWN},
            Write(formula[10:], run_time=1)
        )
        self.wait()

    def get_formula(self):
        formula = TexMobject(
            "p(H) p(E|H) \\over p(E)",
            tex_to_color_map={
                "H": HYPOTHESIS_COLOR,
                "E": EVIDENCE_COLOR1,
            },
            substrings_to_isolate=list("p(|)")
        )
        formula.move_to(self.formula_position)
        return formula

    def prior(self, x):
        return (x**3 / 6) * np.exp(-x)

    def likelihood(self, x):
        return np.exp(-0.5 * x)


class ComingUp(Scene):
    CONFIG = {
        "camera_config": {"background_color": DARK_GREY}
    }

    def construct(self):
        rect = ScreenRectangle()
        rect.set_height(6)
        rect.set_fill(BLACK, 1)
        rect.set_stroke(WHITE, 2)

        words = TextMobject("Later...")
        words.scale(2)
        words.to_edge(UP)
        rect.next_to(words, DOWN)

        self.add(rect)
        self.play(FadeIn(words))
        self.wait()


class QuestionSteveConclusion(HeartOfBayesTheorem, DescriptionOfSteve):
    def construct(self):
        # Setup
        steve = Steve()
        steve.shift(UP)
        self.add(steve)

        kt = Group(
            ImageMobject("kahneman"),
            ImageMobject("tversky"),
        )
        kt.arrange(DOWN)
        kt.set_height(6)
        randy = Randolph()
        kt.next_to(randy, RIGHT, LARGE_BUFF)
        randy.align_to(kt, DOWN)

        farmers = VGroup(*[Farmer() for x in range(20)])
        farmers.arrange_in_grid(n_cols=5)
        people = VGroup(Librarian(), farmers)
        people.arrange(RIGHT, aligned_edge=UP)
        people.set_height(3)
        people.next_to(randy.get_corner(UL), UP)
        cross = Cross(people)
        cross.set_stroke(RED, 8)

        # Question K&T
        self.play(
            steve.scale, 0.5,
            steve.to_corner, DL,
            FadeIn(randy),
            FadeInFromDown(kt, lag_ratio=0.3),
        )
        self.play(randy.change, "sassy")
        self.wait()
        self.play(
            FadeInFrom(people, RIGHT, lag_ratio=0.01),
            randy.change, "raise_left_hand", people,
        )
        self.wait()
        self.play(
            ShowCreation(cross),
            randy.change, "angry"
        )
        self.wait()

        # Who is Steve?
        people.add(cross)
        self.play(
            people.scale, 0.3,
            people.to_corner, UL,
            steve.scale, 1.5,
            steve.next_to, randy.get_corner(UL), LEFT,
            randy.change, "pondering", steve,
        )
        self.play(randy.look_at, steve)
        self.play(Blink(randy))

        kt.generate_target()
        steve.generate_target()
        steve.target.set_height(0.9 * kt[0].get_height())
        group = Group(kt.target[0], steve.target, kt.target[1])
        group.arrange(RIGHT)
        group.to_edge(RIGHT)

        self.play(
            MoveToTarget(kt),
            MoveToTarget(steve),
            randy.shift, 2 * LEFT,
            randy.change, 'erm', kt.target,
            FadeOutAndShift(people, 2 * LEFT),
        )
        self.remove(people, cross)
        self.play(Blink(randy))
        self.wait()

        jessy = Randolph(color=BLUE_B)
        jessy.next_to(randy, LEFT, MED_LARGE_BUFF)
        steve.target.match_height(randy)
        steve.target.next_to(randy, RIGHT, MED_LARGE_BUFF)
        morty = Mortimer()
        morty.next_to(steve.target, RIGHT, MED_LARGE_BUFF)
        morty.look_at(steve.target),
        jessy.look_at(steve.target),
        VGroup(jessy, morty, steve.target).to_edge(DOWN)
        pis = VGroup(randy, jessy, morty)

        self.play(
            LaggedStartMap(FadeOutAndShift, kt, lambda m: (m, 3 * UR)),
            MoveToTarget(steve),
            randy.to_edge, DOWN,
            randy.change, "happy", steve.target,
            FadeIn(jessy),
            FadeIn(morty),
        )
        self.play(LaggedStart(*[
            ApplyMethod(pi.change, "hooray", steve)
            for pi in pis
        ]))
        self.play(Blink(morty))
        self.play(Blink(jessy))

        # The assumption changes the prior
        diagram = self.get_diagram(0.05, 0.4, 0.1)
        diagram.nhne_rect.set_fill(DARK_GREY)
        diagram.set_height(3.5)
        diagram.center().to_edge(UP, buff=MED_SMALL_BUFF)

        self.play(
            FadeIn(diagram),
            *[
                ApplyMethod(pi.change, "pondering", diagram)
                for pi in pis
            ],
        )
        self.play(diagram.set_prior, 0.5)
        self.play(Blink(jessy))
        self.wait()
        self.play(Blink(morty))
        self.play(
            morty.change, "raise_right_hand", diagram,
            ApplyMethod(diagram.set_prior, 0.9, run_time=2),
        )
        self.play(Blink(randy))
        self.wait()

        # Likelihood of description
        description = self.get_description()
        description.scale(0.5)
        description.to_corner(UL)

        self.play(
            FadeIn(description),
            *[ApplyMethod(pi.change, "sassy", description) for pi in pis]
        )
        self.play(
            diagram.set_likelihood, 0.2,
            run_time=2,
        )
        self.play(
            diagram.set_antilikelihood, 0.5,
            run_time=2,
        )
        self.play(Blink(jessy))
        self.play(Blink(randy))
        self.wait()

        # Focus on diagram
        diagram.generate_target()
        diagram.target.set_height(6)
        diagram.target.move_to(3 * LEFT)

        formula = get_bayes_formula()
        formula.scale(0.75)
        formula.to_corner(UR)

        self.play(
            FadeInFromDown(formula),
            LaggedStart(*[
                ApplyMethod(pi.change, "thinking", formula)
                for pi in pis
            ])
        )
        self.play(Blink(randy))
        self.play(
            LaggedStartMap(
                FadeOutAndShiftDown,
                VGroup(description, *pis, steve),
            ),
            MoveToTarget(diagram, run_time=3),
            ApplyMethod(
                formula.scale, 1.5, {"about_edge": UR},
                run_time=2.5,
            ),
        )
        self.wait()

        kw = {"run_time": 2}
        self.play(diagram.set_prior, 0.1, **kw)
        self.play(diagram.set_prior, 0.6, **kw)
        self.play(diagram.set_likelihood, 0.5, **kw),
        self.play(diagram.set_antilikelihood, 0.1, **kw),
        self.wait()


class WhoAreYou(Scene):
    def construct(self):
        words = TextMobject("Who are you?")
        self.add(words)


class FadeInHeart(Scene):
    def construct(self):
        heart = SuitSymbol("hearts")

        self.play(FadeInFromDown(heart))
        self.play(FadeOut(heart))


class ReprogrammingThought(Scene):
    CONFIG = {
        "camera_config": {
            "background_color": DARKER_GREY,
        }
    }

    def construct(self):
        brain = SVGMobject("brain")
        brain.set_fill(GREY, 1)
        brain.set_sheen(1, UL)
        brain.set_stroke(width=0)

        arrow = DoubleArrow(ORIGIN, 3 * RIGHT)

        formula = get_bayes_formula()

        group = VGroup(brain, arrow, formula)
        group.arrange(RIGHT)
        group.center()

        q_marks = TexMobject("???")
        q_marks.scale(1.5)
        q_marks.next_to(arrow, UP, SMALL_BUFF)

        kt = Group(
            ImageMobject("kahneman"),
            ImageMobject("tversky"),
        )
        kt.arrange(RIGHT)
        kt.set_height(2)
        kt.to_corner(UR)

        brain_outline = brain.copy()
        brain_outline.set_fill(opacity=0)
        brain_outline.set_stroke(TEAL, 4)

        self.play(FadeInFrom(brain, RIGHT))
        self.play(
            GrowFromCenter(arrow),
            LaggedStartMap(FadeInFromDown, q_marks[0]),
            run_time=1
        )
        self.play(FadeInFrom(formula, LEFT))
        self.wait()

        kw = {"run_time": 1, "lag_ratio": 0.3}
        self.play(LaggedStartMap(FadeInFromDown, kt, **kw))
        self.play(LaggedStartMap(FadeOut, kt, **kw))
        self.wait()

        self.add(brain)
        self.play(ShowCreationThenFadeOut(
            brain_outline,
            lag_ratio=0.01,
            run_time=2
        ))

        # Bubble
        bubble = ThoughtBubble()
        bubble.next_to(brain, UR, SMALL_BUFF)
        bubble.shift(2 * DOWN)

        diagram = BayesDiagram(0.25, 0.8, 0.5)
        diagram.set_height(2.5)
        diagram.move_to(bubble.get_bubble_center())

        group = VGroup(brain, arrow, q_marks, formula)

        self.play(
            DrawBorderThenFill(VGroup(*reversed(bubble))),
            group.shift, 2 * DOWN,
        )
        self.play(FadeIn(diagram))
        self.wait()
        self.play(
            q_marks.scale, 1.5,
            q_marks.space_out_submobjects, 1.5,
            q_marks.set_opacity, 0,
        )
        self.remove(q_marks)
        self.wait()

        # Move parts
        prior_outline = formula[7:12].copy()
        prior_outline.set_stroke(YELLOW, 5, background=True)
        like_outline = formula[12:18].copy()
        like_outline.set_stroke(BLUE, 5, background=True)

        self.play(
            FadeIn(prior_outline),
            ApplyMethod(diagram.set_prior, 0.5, run_time=2)
        )
        self.play(FadeOut(prior_outline))
        self.play(
            FadeIn(like_outline),
            ApplyMethod(diagram.set_likelihood, 0.2, run_time=2),
        )
        self.play(FadeOut(like_outline))
        self.wait()


class MassOfEarthEstimates(GlimpseOfNextVideo):
    CONFIG = {
        "add_x_coords": False,
        "formula_position": 2 * UP + 0.5 * RIGHT,
        "dx": 0.05,
    }

    def setup(self):
        super().setup()
        earth = SVGMobject(
            file_name="earth",
            height=1.5,
            fill_color=BLACK,
        )
        earth.set_stroke(width=0)
        # earth.set_stroke(BLACK, 1, background=True)
        circle = Circle(
            stroke_width=3,
            stroke_color=GREEN,
            fill_opacity=1,
            fill_color=BLUE_C,
        )
        circle.replace(earth)
        earth.add_to_back(circle)
        earth.set_height(0.75)

        words = TextMobject("Mass of ")
        words.next_to(earth, LEFT)
        group = VGroup(words, earth)

        group.to_edge(DOWN).shift(2 * RIGHT)
        self.add(group)

    def get_formula(self):
        formula = TexMobject(
            "p(M) p(\\text{data}|M) \\over p(\\text{data})",
            tex_to_color_map={
                "M": HYPOTHESIS_COLOR,
                "\\text{data}": EVIDENCE_COLOR1,
            },
            substrings_to_isolate=list("p(|)")
        )
        formula.move_to(self.formula_position)
        return formula

    def prior(self, x, mu=6, sigma=1):
        factor = (1 / sigma / np.sqrt(TAU))
        return factor * np.exp(-0.5 * ((x - mu) / sigma)**2)

    def likelihood(self, x):
        return self.prior(x, 5, 1)


class ShowProgrammer(Scene):
    CONFIG = {
        "camera_config": {
            "background_color": DARKER_GREY,
        }
    }

    def construct(self):
        programmer = SVGMobject(file_name="programmer")
        programmer.set_stroke(width=0)
        programmer.set_fill(GREY, 1)
        programmer.set_sheen(1, UL)
        programmer.set_height(3)

        programmer.to_corner(DL)
        self.play(FadeInFrom(programmer, DOWN))
        self.wait()


class BayesEndScene(PatreonEndScreen):
    CONFIG = {
        "specific_patrons": [
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "soekul",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "Lambda AI Hardware",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "Quantopian",
            "<NAME>, Ph.D.",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "Lukas -krtek.net- Novy",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "J",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "1stViewMaths",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "Anto<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "Awoo",
            "<NAME>",
            "AZsorcerer",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "DeathByShrimp",
            "<NAME>",
            "<NAME>",
            "eaglle",
            "emptymachine",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>, Jr.",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>-Subramanian",
            "L0j1k",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "rehmi post",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "Roobie",
            "<NAME>",
            "<NAME>",
            "Solara570",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "The Responsible One",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "Veritasium",
            "<NAME>",
            "<NAME>",
            "<NAME>",
            "YinYangBalance.Asia",
        ],
    }


class Thumbnail(Scene):
    def construct(self):
        diagram = BayesDiagram(0.25, 0.4, 0.1)
        diagram.set_height(3)
        diagram.add_brace_attrs()
        braces = VGroup(
            diagram.h_brace,
            diagram.he_brace,
            diagram.nhe_brace,
        )
        diagram.add(*braces)

        kw = {
            "tex_to_color_map": {
                "H": YELLOW,
                "E": BLUE,
                "\\neg": RED,
            }
        }
        labels = VGroup(
            TexMobject("P(H)", **kw),
            TexMobject("P(E|H)", **kw),
            TexMobject("P(E|\\neg H)", **kw),
        )
        labels.scale(1)

        for label, brace, vect in zip(labels, braces, [DOWN, LEFT, RIGHT]):
            label.next_to(brace, vect)

        diagram.add(*labels)

        # diagram.set_height(6)
        diagram.to_edge(DOWN, buff=MED_SMALL_BUFF)
        diagram.shift(2 * LEFT)
        self.add(diagram)

        diagram.set_height(FRAME_HEIGHT - 1)
        diagram.center().to_edge(DOWN)
        for rect in diagram.evidence_split:
            rect.set_sheen(0.2, UL)
        return

        # Formula
        formula = get_bayes_formula()
        formula.scale(1.5)
        formula.to_corner(UL)

        frac = VGroup(
            diagram.he_rect.copy(),
            Line(ORIGIN, 4 * RIGHT).set_stroke(WHITE, 3),
            VGroup(
                diagram.he_rect.copy(),
                TexMobject("+"),
                diagram.nhe_rect.copy(),
            ).arrange(RIGHT)
        )
        frac.arrange(DOWN)
        equals = TexMobject("=")
        equals.next_to(formula, RIGHT)
        frac.next_to(equals, RIGHT)

        self.add(formula)
        self.add(equals, frac)

        VGroup(formula, equals, frac).to_edge(UP, buff=SMALL_BUFF)
