# python -m manim eratosthene2026.py Crible
from manim import *

# ============================================================
# CRIBLE D'ÉRATOSTHÈNE
# Format vertical 9:16
# ============================================================

config.pixel_width = 2160#1080
config.pixel_height = 3840#1920
config.frame_width = 9
config.frame_height = 16


# ============================================================
# Recherche des nombres premiers
# ============================================================

def eratosthene(n):
    premiers = []

    for i in range(2, n + 1):
        if all(i % p != 0 for p in premiers):
            premiers.append(i)

    return premiers


# ============================================================
# SCÈNE
# ============================================================

class Crible(Scene):

    def construct(self):

        self.camera.background_color = "#008000"

        # ----------------------------------------------------
        # TITRE
        # ----------------------------------------------------

        titre = Text(
            "Le crible d'Ératosthène",
            color=GREEN
        ).scale(0.65)

        titre.to_edge(UP, buff=0.45)

        self.play(
            FadeIn(titre, shift=DOWN * 0.2),
            run_time=0.6
        )

        # ----------------------------------------------------
        # TEXTE D'INTRODUCTION
        # ----------------------------------------------------

        intro = Text(
            "On place les entiers de 2 à 100\n"
            "dans une grille."
        ).scale(0.38).set_color(BLACK)

        intro.next_to(titre, DOWN, buff=0.35)

        self.play(
            FadeIn(intro),
            run_time=0.5
        )

        # ----------------------------------------------------
        # GRILLE
        # ----------------------------------------------------

        # La grille fait 7 unités de large.
        # Elle est placée sous le texte d'introduction.

        largeur = 7
        hauteur = 7
        pas = largeur / 10

        x_min = -largeur / 2
        x_max = largeur / 2

        y_max = 5.0
        y_min = y_max - hauteur

        grille = VGroup()

        # Lignes verticales
        for i in range(11):

            x = x_min + i * pas

            ligne = Line(
                [x, y_min, 0],
                [x, y_max, 0],
                color=BLACK,
                stroke_width=1.5
            )

            grille.add(ligne)

        # Lignes horizontales
        for i in range(11):

            y = y_min + i * pas

            ligne = Line(
                [x_min, y, 0],
                [x_max, y, 0],
                color=BLACK,
                stroke_width=1.5
            )

            grille.add(ligne)

        self.play(
            Create(grille),
            run_time=0.8
        )

        # ----------------------------------------------------
        # NOMBRES
        # ----------------------------------------------------

        nombres = VGroup()

        for n in range(2, 101):

            colonne = (n - 2) % 10
            ligne = (n - 2) // 10

            x = x_min + pas / 2 + colonne * pas
            y = y_max - pas / 2 - ligne * pas

            taille = 0.31

            nombre = Text(
                str(n),
                color=BLACK
            ).scale(taille)

            nombre.move_to([x, y, 0])

            nombres.add(nombre)

        # Apparition rapide des nombres
        self.play(
            LaggedStart(
                *[FadeIn(n) for n in nombres],
                lag_ratio=0.008
            ),
            run_time=1.5
        )

        # ----------------------------------------------------
        # TEXTE D'ÉTAPE
        # ----------------------------------------------------

        explication = Text(
            "On commence par 2."
        ).scale(0.4).set_color(BLUE)

        explication.to_edge(DOWN, buff=1.7)

        self.play(
            FadeIn(explication),
            run_time=0.4
        )

        # ----------------------------------------------------
        # CRIBLE
        # ----------------------------------------------------

        nombres_premiers = [2, 3, 5, 7]

        # Les traits rouges restent à l'écran.
        rayures = VGroup()

        # Rectangle de sélection du nombre premier
        premier_entoure = SurroundingRectangle(
            nombres[0],
            color=GREEN,
            buff=0.04,
            stroke_width=4
        )

        self.add(premier_entoure)

        # ----------------------------------------------------
        # TRAITEMENT DE 2, 3, 5 ET 7
        # ----------------------------------------------------

        for p in nombres_premiers:

            index_p = p - 2

            nombre_p = nombres[index_p]

            # Nouveau rectangle autour du nombre premier
            nouveau_rectangle = SurroundingRectangle(
                nombre_p,
                color=GREEN,
                buff=0.04,
                stroke_width=4
            )

            # Texte explicatif
            nouveau_texte = Text(
                f"On élimine les multiples de {p}."
            ).scale(0.38).set_color(BLUE)

            nouveau_texte.to_edge(DOWN, buff=1.7)

            # Changement de texte + déplacement du rectangle
            self.play(
                Transform(premier_entoure, nouveau_rectangle),
                Transform(explication, nouveau_texte),
                run_time=0.35
            )

            # Le nombre premier devient vert
            self.play(
                nombre_p.animate.set_color(GREEN).scale(1.12),
                run_time=0.2
            )

            # ------------------------------------------------
            # Multiples de p
            # ------------------------------------------------

            animations = []

            for multiple in range(2 * p, 101, p):

                index = multiple - 2

                nombre = nombres[index]

                centre = nombre.get_center()

                # Trait diagonal
                trait = Line(
                    centre + LEFT * 0.10 + DOWN * 0.10,
                    centre + RIGHT * 0.10 + UP * 0.10,
                    color=RED,
                    stroke_width=3
                )

                rayures.add(trait)

                animations.append(
                    AnimationGroup(
                        Create(trait),
                        nombre.animate.set_opacity(0.28),
                        lag_ratio=0
                    )
                )

            # Animation groupée des multiples
            if animations:

                self.play(
                    LaggedStart(
                        *animations,
                        lag_ratio=0.025
                    ),
                    run_time=0.9
                )

            # Le nombre premier reprend sa taille
            self.play(
                nombre_p.animate.scale(1 / 1.12),
                run_time=0.15
            )

            # Petite pause entre les étapes
            self.wait(0.25)

        # ----------------------------------------------------
        # EXPLICATION : POURQUOI S'ARRÊTER À 7 ?
        # ----------------------------------------------------

        texte_sqrt = Text(
            "Pourquoi peut-on s'arrêter ici ?"
        ).scale(0.38).set_color(BLUE)

        texte_sqrt.to_edge(DOWN, buff=1.7)

        self.play(
            Transform(explication, texte_sqrt),
            run_time=0.4
        )

        self.wait(0.5)

        # ----------------------------------------------------
        # √100 = 10
        # ----------------------------------------------------

        racine = MathTex(
            r"\sqrt{100}=10"
        ).scale(0.75).set_color(BLACK)

        racine.next_to(grille, DOWN, buff=0.45)

        self.play(
            Write(racine),
            run_time=0.7
        )

        # ----------------------------------------------------
        # 11 > 10
        # ----------------------------------------------------

        conclusion_1 = MathTex(
            r"11>10"
        ).scale(0.65).set_color(GREEN)

        conclusion_1.next_to(racine, DOWN, buff=0.25)

        self.play(
            Write(conclusion_1),
            run_time=0.5
        )

        # ----------------------------------------------------
        # CONCLUSION
        # ----------------------------------------------------

        conclusion = Text(
            "Il n'est donc plus nécessaire\nd'éliminer d'autres multiples."
        ).scale(0.34).set_color(BLACK)

        conclusion.next_to(conclusion_1, DOWN, buff=0.25)

        self.play(
            FadeIn(conclusion),
            run_time=0.5
        )

        self.wait(1)

        # ----------------------------------------------------
        # MISE EN ÉVIDENCE DES NOMBRES PREMIERS
        # ----------------------------------------------------

        derniers_premiers = [
            11, 13, 17, 19, 23, 29,
            31, 37, 41, 43, 47, 53,
            59, 61, 67, 71, 73, 79,
            83, 89, 97
        ]

        animations_finales = []

        for p in derniers_premiers:

            nombre = nombres[p - 2]

            animations_finales.append(
                nombre.animate.set_color(GREEN)
            )

        self.play(
            *animations_finales,
            run_time=0.8
        )

        # ----------------------------------------------------
        # MESSAGE FINAL
        # ----------------------------------------------------

        message_final = Text(
            "Les nombres non rayés sont premiers !",
            color=GREEN
        ).scale(0.42)

        message_final.to_edge(DOWN, buff=0.45)

        self.play(
            FadeIn(message_final, shift=UP * 0.2),
            run_time=0.5
        )

        # Petite signature
        signature = Text(
            "courspasquet.fr"
        ).scale(0.22).set_color(GREY_B)

        signature.to_corner(
            DOWN + RIGHT,
            buff=0.25
        )

        self.play(
            FadeIn(signature),
            run_time=0.3
        )

        self.wait(2)