#import "composants.typ": *
// #import "@preview/touying:0.5.2": *
#import "code_slides.typ": generer_slides_code

#set page(
  paper: "presentation-4-3",
  margin: 0cm,
)

// Font
#set text(
  font: "New Computer Modern",
  size: 20pt,
  fill: black,
)
#set math.mat(delim: "[")

// Page de garde
#slide[
  #place(center + horizon)[
    #graphe_tanner_fond(1cm, 1.5)
  ]
  #v(1fr)
  #align(center + horizon)[
    #pad(x: 2cm)[
      #text(size: 3em, weight: "bold", fill: black)[#titre]

      #text(size: 1.2em, weight: "bold", fill: black)[#auteur]
      t

      #h(0.5em)

      #text(size: 1.2em, fill: black)[n°#numero]

      #text(size: 0.95em, fill: black)[#annee]
    ]
  ]
  #v(1fr)
]

#myslide("Introduction : Utilisation")[
  #align(center + horizon)[
    #grid(
      columns: (1.5fr, 1.5fr),
      gutter: 1.5cm,
      align: center + horizon,
      [
        #box(width: 80%)[
          #stack(
            dir: ttb,
            spacing: -0.5em,
            image("src/athena_fidus_no_text.jpg", width: 100%),
            align(left)[
              #box(
                inset: (x: 5pt, y: 0pt),
                text(size: 0.45em, fill: white)[* https://gallery.ariane.group *],
              )
            ],
          )
        ]
        #v(0.5em)
        Athena-Fidus
      ],
      [
        #box(width: 80%)[
          #stack(
            dir: ttb,
            spacing: -0.5em,
            image("src/runcamfpv2.png", width: 100%),
            align(left)[
              #box(
                inset: (x: 5pt, y: 0pt),
                text(size: 0.45em, fill: white)[* https://www.runcam.com/ *],
              )
            ],
          )
        ]
        #v(0.5em)
        Module OpenIPC
      ],
    )
  ]
]

#myslide("Introduction : Communication Numérique")[
  #place(center + horizon, dy: 8cm)[
    #canal_shannon_intro()
  ]
]

#myslide("Problématique")[
  #place(center + horizon, dy: 8.1cm)[#graphe_tanner_fond(0.9cm, 1.75)]
  #place(center + horizon, dy: 7.7cm)[
    #block(width: 100%)[
      #text(size: 1.2em, weight: "bold", fill: black)[
        Comment utiliser les codes LDPC pour garantir la fiabilité d'une transmission en présence de bruit ?
      ]
    ]
  ]
]

#myslide("Plan")[
  #grid(
    columns: (auto, 1fr),
    align: horizon,
    place(center + horizon, dx: 11.8cm, dy: 0.7cm)[#decor_matrice_etoilee()],
    design_plan((
      [Introduction],
      [Codes linéaires],
      [LDPC],
      [Codage],
      [Décodage],
      [Analyse],
    )),
  )
]

#myslide("Définition : Codes Linéaires en Bloc")[
  #definition(titre: [Code $display((n,k) in NN^2)$])[
    $cal(C)$ sous-espace vectoriel de dimension $k$ de $FF_2^n$
  ]

  #[
    #set text(size: 1.1em)
    - $k$ : longueur du message original
    - $n$ : longueur du mot de code
    - $m = n - k$ : nombre de bits de parités
  ]

  #definition(titre: "Encodage")[
    $Phi : FF_2^k & -> FF_2^n in cal(L)(FF_2^k, FF_2^n)$
  ]

  #v(-1.3em)

  #align(center + horizon)[
    #plongement_schema()
  ]
]

#myslide("Définition : Matrice Génératrice")[
  #v(0.80cm)
  #definition(titre: "Matrice Génératrice")[
    $G in cal(M)_(k,n)(FF_2)$ dont les lignes sont une base de $cal(C)$
  ]

  #definition(titre: "Encodage")[
    Pour un message $u in FF_2^k$ le mot de code $c in cal(C)$ est :
    $
      c = Phi(u) = u G
    $
  ]

  #definition(titre: "Forme systématique")[
    // TODO : changer [I_k | P] en un graphique jolie avec I et P dans un carré coloré
    $
      #dessiner_matrice($G =$, ((texte: $I_k$, largeur: 2.2, fond: gray.lighten(75%)), (texte: $P$, largeur: 2.2, fond: gray.lighten(75%))))
      // G = mat(
      //   I_k, P;
      //   augment: #1,
      //   delim: "[",
      // )
    $
  ]

  #[
    #set text(size: 1.2em)
    // - Pour $u in FF_2^k, space display(u dot.o G = mat(u, u dot.o P; augment: #1, delim: "[",))$

    // - Pour $u in FF_2^k, space #dessiner_matrice($u dot.o G =$, (
    //     (texte: $u$, largeur: 1.1, fond: gray.lighten(75%)),
    //     (texte: $u dot.o P$, largeur: 3.0, fond: gray.lighten(75%)),
    //   ))$

    - $P in cal(M)_(k ,n-k)(FF_2)$ matrice de parité\
  ]
]

#myslide("Définition : Matrice de Contrôle")[
  #v(1.1cm)
  #definition(titre: "Matrice de Contrôle")[
    // $H = mat(
    //   P^top, I_(n-k);
    //   augment: #1,
    //   delim: "[",
    // )
    $
      #dessiner_matrice($H =$, (
        (texte: $P^top$, largeur: 2.2, fond: gray.lighten(75%)),
        (texte: $I_(n-k)$, largeur: 2.2, fond: gray.lighten(75%)),
      ))
    $

  ]
  #[
    #set text(size: 1.2em)
    // - $cal(C) = ker(H) = {v in FF_2^n | H dot.o v^top = 0}$

    - $cal(C) = ker(H)$ -- $H c^top = 0$ $=>$ $c in cal(C)$


    // - $display(G dot.o H^top = 0)$
  ]

  #definition(titre: "Syndrome")[
    Pour un vecteur reçu $r = c + e, space s in FF_2^(n - k)$
    $
      s = H r^top = H c^top + H e^top = 0 + H e^top
    $
  ]
  #[
    #set text(size: 1.2em)
    - $s = 0 => r in cal(C)$
    - $s != 0$ donne la signature de l'erreur $e$
  ]
  // Possible décodage par syndrome
]

#myslide("Exemple d'un code linéaire")[
  #[
    #set text(size: 1.1em)
    #let col-u = blue
    #let col-p = orange

    #underline(offset: 3pt)[Exemple d'un code $(5,2)$]


    - On choisit la matrice de parité $P$ :
    $
      P = #math.mat(
        (text(fill: col-p)[1], text(fill: col-p)[1], text(fill: col-p)[0]),
        (text(fill: col-p)[0], text(fill: col-p)[1], text(fill: col-p)[1]),
      )
    $

    - Alors la matrice génératrice $G$ est :
    $
      G = #math.mat(
        (text(fill: col-u)[1], text(fill: col-u)[0], text(fill: col-p)[1], text(fill: col-p)[1], text(fill: col-p)[0]),
        (text(fill: col-u)[0], text(fill: col-u)[1], text(fill: col-p)[0], text(fill: col-p)[1], text(fill: col-p)[1]),
        augment: 2,
      )
    $

    - Message $display(u = #math.mat((text(fill: col-u)[1], text(fill: col-u)[1])))$

    - Mot de code $c = u G$ :
    #set text(size: 1.2em)
    $
      c = #math.mat((text(fill: col-u)[1], text(fill: col-u)[1]))
      #math.mat(
        (text(fill: col-u)[1], text(fill: col-u)[0], text(fill: col-p)[1], text(fill: col-p)[1], text(fill: col-p)[0]),
        (text(fill: col-u)[0], text(fill: col-u)[1], text(fill: col-p)[0], text(fill: col-p)[1], text(fill: col-p)[1]),
        augment: 2,
      )
      =
      #math.mat((
        text(fill: col-u)[1],
        text(fill: col-u)[1],
        text(fill: col-p)[1],
        text(fill: col-p)[0],
        text(fill: col-p)[1],
      ))
    $
  ]
]

#myslide("Exemple d'un code linéaire")[
  #let colp = orange
  #let colu = blue
  #let cole = red
  #set math.mat(delim: "[")
  #v(0.2cm)
  #set text(size: 1.1em)
  Structure systématique de $H$ :
  #v(0.3em)
  #align(center)[
    #scale(108%)[
      #dessiner_matrice($H =$, (
        (texte: $P^top$, largeur: 2.0, fond: colp.lighten(90%)),
        (texte: $I_3$, largeur: 3.0, fond: colu.lighten(90%)),
      ))
    ]
  ]
  #v(0.4em)
  Ainsi :
  #v(-1.2em)
  #align(center)[
    #scale(110%)[
      $
        H = mat(
          text(fill: colp, "1"), text(fill: colp, "0"), text(fill: colu, "1"), text(fill: colu, "0"), text(fill: colu, "0");
          text(fill: colp, "1"), text(fill: colp, "1"), text(fill: colu, "0"), text(fill: colu, "1"), text(fill: colu, "0");
          text(fill: colp, "0"), text(fill: colp, "1"), text(fill: colu, "0"), text(fill: colu, "0"), text(fill: colu, "1")
        )
      $
    ]
  ]
  #v(0.55em)
  Mot de code valide $c = (#text(fill: colp)[1], #text(fill: colp)[1], #text(fill: colu)[1], #text(fill: colu)[0], #text(fill: colu)[1])$ : #h(0.4em) $H c^top = mat(0; 0; 0)$ #h(0.5em) #text(fill: green.darken(20%))[✓]
  #v(-0.5em)
  Mot reçu avec #text(fill: cole)[une erreur] : $r = (#text(fill: colp)[1], #text(fill: colp)[1], text(fill: cole, "0"), #text(fill: colu)[0], #text(fill: colu)[1])$
  #v(0.4em)
  #align(center)[
    #scale(110%)[
      $
        H r^top = mat(
          text(fill: colp, "1"), text(fill: colp, "0"), text(fill: colu, "1"), text(fill: colu, "0"), text(fill: colu, "0");
          text(fill: colp, "1"), text(fill: colp, "1"), text(fill: colu, "0"), text(fill: colu, "1"), text(fill: colu, "0");
          text(fill: colp, "0"), text(fill: colp, "1"), text(fill: colu, "0"), text(fill: colu, "0"), text(fill: colu, "1")
        ) mat(
          text(fill: colp, "1"), text(fill: colp, "1"), text(fill: cole, "0"), text(fill: colu, "0"), text(fill: colu, "1")
        )^top = mat(1; 0; 0) != mat(0; 0; 0)
      $
    ]
  ]
]

// A REMPLACER AVEC DE VRAI DONNE SUR DE VRAI CODE LDPC ET HAMMING PAR EXEMPLE
#myslide("Approcher la Limite de Shannon")[
  // Faire un graphique waterfall comme sur les papiers de recherche...
  #limite_shannon_graphique()
]

// #myslide("Redondance et limite théorique")[
//   Graphique waterfall avec n = 100 et n = 64800 avec limite de Shannon, $display(R = k /n) < 1$, $m = n - k$
//
//   Bande passante...
//
//   Il existe $C$ pour un canal tel que pour $R < C$ on peut atteindre une probabilité d'erreur nulle.
//   $=>$ gros bloc (moyenne du bruit aléatoire)
// ]

#myslide("Le Mur de la Complexité")[
  #place(dy: 2.5cm)[
    #set text(size: 19pt)
    #definition(titre: [Décodage par Maximum de Vraisemblance], accent: black)[
      Chercher le mot de code $bold(c) in cal(C)$ le plus probable sachant $bold(r)$ reçu :
      $ hat(bold(c)) = arg min_(bold(c) in cal(C)) d_H (bold(r), bold(c)) $
    ]

    #text(size: 21pt)[
      - Équivalent à chercher l'erreur $bold(e)$ de poids minimal tel que $bold(H) bold(e)^top = bold(s)$.
    ]

    #v(0.5em)

    #definition(titre: "Le Problème du décodage par Syndrome")[
      NP-Difficile et pour $H$ quelconque : $cal(O)(2^k)$
    ]

    // - Pour $k=100$ bits, $2^100 approx 10^30$ opérations nécessaires.
  ]
]

#myslide("Définition des Codes LDPC")[
  #definition(titre: [Codes LDPC Réguliers])[
    #text(size: 22.97pt)[
      Code linéaire en bloc avec une matrice de contrôle $bold(H)$ *clairsemée*.
      #align(center)[
        #grid(
          columns: (auto, 3em, auto),
          align: left,
          [- Poids de Colonne *$w_c$*], [], [- Poids de Ligne *$w_r$*],
        )
      ]
    ]
  ]

  #place(center, dx: -290pt, dy: 60pt)[
    #text(size: 13pt, style: "italic", fill: luma(160))[
      $H in cal(M)_(15, 30)(FF_2)$, $display(R = 1/2)$
    ]
  ]
  #v(-0.3cm)
  #align(center)[
    #scale(100%)[#hldpc()]
  ]

  #v(0.0cm)
  #grid(
    columns: (1fr, 1fr),
    column-gutter: 1.75em,
    [#definition(titre: "Faible Densité", accent: black)[
      #set align(center)
      $w_c << m$ #h(1cm) $w_r << n$
    ]],
    [#definition(titre: "Rendement")[
      #set align(center)
      $R = 1 - m / n$
    ]],
  )
]

// #myslide("Définition des Codes LDPC")[
//   #v(1.3cm)
//   #definition(titre: [Codes LDPC Réguliers])[
//     #text(size: 22.97pt)[
//       Code linéaire en bloc avec une matrice de contrôle $bold(H)$ *clairsemée*.
//     ]
//   ]
//   #v(0.3em)
//   #align(center + horizon)[
//     #grid(
//       columns: (auto, 3em, auto),
//       align: left,
//       [• Poids de Colonne *$w_c$*], [], [• Poids de Ligne *$w_r$*],
//     )
//   ]
//   #v(0.5em)
//
//   // - Poids de Colonne *$w_c$*
//   //
//   // - Poids de Ligne *$w_r$*
//   //
//   // #v(0.5em)
//
//   #definition(titre: "Conditions de Faible Densité", accent: black)[
//     #set align(center)
//     // $w_c << n - k$ #h(2cm) $w_r << n$
//     $w_c << m$ #h(2cm) $w_r << n$
//   ]
//
//   #definition(titre: "Rendement")[
//     // $ display(R = (n - op("rg")(H)) / n >= 1 - m / n) $
//     $ display(R = 1 - m / n) $
//   ]
// ]
//
// #myslide([Matrice de contrôle])[
//   // #definition(titre: [Code LDPC $(6, 3)$])[
//   //   $m w_r = n w_c$ donc $H in cal(M)_(15, 30)(FF_2)$ et $display(R = 1 - m / n = 1/2)$
//   // ]
//   #definition(titre: [Code LDPC $(6, 3)$])[
//     #v(8pt)
//     $H in cal(M)_(15, 30)(FF_2)$ et $display(R = 1/2)$
//     #v(5pt)
//   ]
//
//   #v(2cm)
//
//   #place(center + horizon, dx: 0cm, dy: 7.3cm)[
//     #scale(140%)[#hldpc()]
//   ]
// ]

#myslide("De la Matrice aux Équations de Parité")[
  #set text(size: 17pt)

  #let v_space = 2.83cm

  #align(center)[
    #scale(115%)[
      #grid(
        columns: (auto, auto),
        gutter: -15pt,
        align: horizon,
        [#hldpc_dual(row1: 0, row2: none)],
        [
          // #set math.mat(gap: 24.5pt)
          #move(dy: 13.8pt)[
            $
              underbrace(
                mat(r_0; r_1; #v(v_space);dots.v; r_29; delim: "["),
                #text()[Mot reçu] r space in space FF_2^30
              )
            $
          ]
        ],
      )
    ]
  ]

  #v(0.5cm)

  #set text(size: 1.2em)

  // - Chaque ligne $j$ de $H$ définit une équation de parité $f_j$.
  - $L_j$ définit une équation de parité $f_j$
  - Pour $r$, on vérifie le syndrome : $H r^top = 0$
  #v(0.2cm)

  #definition(titre: [Équations de Parité], accent: orange)[
    #set text(size: 1em)
    #text(fill: orange)[$ f_0 : r_7 plus.o r_10 plus.o r_15 plus.o r_22 plus.o r_24 plus.o r_29 = 0 $]
  ]

  #v(0.3cm)
  #text(size: 1.1em)[- Si $f_j = 1$, un nombre impair de bits a été inversé par le canal.]
]

// #myslide("L'Entrelacement des Contraintes")[
//   #set text(size: 17pt)
//
//   #align(center)[
//     #move(dx: -1.2cm)[
//       #scale(115%)[#hldpc_dual(row1: 0, row2: 14)]
//     ]
//   ]
//
//   #v(1cm)
//
//   #set text(size: 1.1em)
//   - Chaque bit $r_i$ participe à $w_c = 3$ équations distinctes
//
//   #v(1cm)
//
//   // --- Étage 2 : Système et Décision ---
//   #set align(center + horizon)
//   #scale(115%)[
//     #block(fill: gray.lighten(95%), inset: 10pt, stroke: 0.5pt + gray, radius: 4pt)[
//       $
//         cases(
//           #text(fill: orange)[$r_7 plus.o r_10 plus.o r_15 plus.o r_22 plus.o bold(r_24) plus.o r_29 &= 0$],
//           #h(4.5cm) #text(size: 20pt)[$bold(dots.v)$],
//           #text(fill: blue)[$r_1 plus.o r_3 plus.o r_12 plus.o bold(r_24) plus.o r_25 plus.o r_28 &= 0$]
//         )
//       $
//     ]
//   ]
//
//   #v(1cm)
//
//   #set align(left)
//   #set text(size: 1.1em)
//   - *$r_24$* : Surveillé par #text(fill: orange)[$f_0$] et #text(fill: blue)[$f_14$].
//   - Si #text(fill: orange)[$f_0 = 1$] et #text(fill: blue)[$f_14 = 1$], $r_24$ est suspect
// ]

#myslide("L'Entrelacement des Contraintes")[
  #v(0.7cm)
  #set text(size: 20pt)
  #align(center)[
    #move(dx: -1.2cm)[
      // #scale(110%)[#hldpc_triple(row1: 0, row2: 9, row3: 14)]
      #scale(110%)[#hldpc_col24(row1: 0, row2: 9, row3: 14, col: 24)]
    ]
  ]
  #v(0.5cm)
  - Chaque bit $r_i$ participe à $w_c = 3$ équations distinctes :
  #v(0.3cm)
  #align(center)[
    #block(fill: gray.lighten(95%), inset: 10pt, stroke: 0.5pt + gray, radius: 4pt)[
      $
        cases(
          #text(fill: orange)[$r_7 plus.o r_10 plus.o r_15 plus.o r_22 plus.o$ #text(fill: red)[$bold(r_24)$] #text(fill: orange)[$plus.o r_29 &= 0 quad (f_0)$]],
          #text(fill: green.darken(20%))[$r_7 plus.o r_13 plus.o r_23 plus.o$ #text(fill: red)[$bold(r_24)$] #text(fill: green.darken(20%))[$plus.o r_25 plus.o r_27 &= 0 quad (f_9)$]],
          #text(fill: blue)[$r_1 plus.o r_3 space thick$] #h(0.3pt) #text(fill: blue)[$plus.o r_12 plus.o$ #text(fill: red)[$bold(r_24)$] #text(fill: blue)[$plus.o r_25 plus.o r_28 &= 0 quad (f_14)$]]
        )
      $
    ]
  ]
  #v(0.5cm)
  - *$r_24$* : Surveillé simultanément par #text(fill: orange)[$f_0$], #text(fill: green.darken(20%))[$f_9$] et #text(fill: blue)[$f_14$].
  - Si $forall #tricolor-j in {#text(fill: orange)[$0$], #text(fill: green.darken(20%))[$9$], #text(fill: blue)[$14$]}, space f_(#scale(80%)[#tricolor-j]) = 1$, alors le bit est considéré suspect.
]

#myslide("Graphe de Tanner : Définition")[
  #set text(size: 1em)
  #definition(titre: [Graphe de Tanner $cal(G)(bold(H))$])[
    #set text(size: 0.9em)
    Graphe bipartite $cal(G) = (#text(fill: blue)[$V$] union.sq #text(fill: orange)[$C$], A)$
    :
    #v(4pt)
    $ (#text(fill: blue)[$v_j$], #text(fill: orange)[$c_i$]) in A space <==> space H_(i,j) = 1 $
  ]

  #v(0.4em)

  #set text(size: 1.1em)
  #grid(
    columns: (1.4fr, 1fr),
    gutter: 1em,
    [- $#text(fill: blue)[$V$] = {#text(fill: blue)[$v_0$], dots, #text(fill: blue)[$v_(n-1)$]}$ nœuds de *variable*],
    [- $|A| = n dot #text(fill: blue)[$w_c$] = m dot #text(fill: orange)[$w_r$]$],

    [
      - $#text(fill: orange)[$C$] = {#text(fill: orange)[$c_0$], dots, #text(fill: orange)[$c_(m-1)$]}$ nœuds de *contrôle*
    ],
    [- $H tilde.equiv cal(G)$],

    [- $deg(#text(fill: blue)[$v_j$]) = #text(fill: blue)[$w_c$]$],
    [- $deg(#text(fill: orange)[$c_i$]) = #text(fill: orange)[$w_r$]$],
  )

  #align(center)[
    #scale(115%)[
      #tanner_illustration()
    ]
    #text(size: 0.8em, style: "italic", fill: gray.darken(20%))[
      Exemple $n=4, space m=2$
    ]
  ]
]

#myslide("Construction du Graphe : Les Nœuds")[

  #v(1.2em)

  #align(center)[
    #scale(100%)[
      // #h_mini_tanner()
      #grid(
        columns: (1fr, 1fr),
        gutter: 1em,

        [#hldpc_dynamic(hl_cols: range(30), show_labels: false, h_show: true)],
        [#hldpc_dynamic(hl_rows: range(15), show_labels: false, h_show: false)],
      )
    ]
  ]

  #v(3.5em)

  #align(center)[
    #scale(180%)[
      #tanner_canvas(colored: true)
    ]
  ]

  // #v(2.5em)

  // #[
  //   #set text(size: 1.05em)
  //   - Chaque *colonne* $j$ de $bold(H)$ $arrow$ nœud de variable #text(fill: blue, weight: "bold")[$v_j in V$] \ #text(fill: blue)[$n = 30$] nœuds, représentés par des *cercles* ○
  //   - Chaque *ligne* $i$ de $bold(H)$ $arrow$ nœud de contrôle #text(fill: orange, weight: "bold")[$c_i in C$] \ #text(fill: orange)[$m = 15$] nœuds, représentés par des *carrés* □
  //   - Les arêtes seront déterminées par les $1$ de $bold(H)$ — étapes suivantes
  // ]
]

#myslide("Construction du Graphe :  Nœud de Contrôle")[

  #v(1.2em)

  #align(center)[
    #move(dx: 12.6pt, dy: -1.3pt)[
      #scale(100%)[
        #hldpc_dynamic(hl_rows: (0,), show_labels: true, h_show: true)
      ]
    ]
  ]

  #v(3.5em)

  #align(center)[
    #move(dx: 2.7pt, dy: -13.8pt)[
      #scale(180%)[
        #tanner_canvas(hl_row: 0)
      ]
    ]
  ]

  // #v(0.4em)
  // #[
  //   #set text(size: 1.05em)
  //   - Ligne #text(fill: orange, weight: "bold")[0] de $bold(H)$ : $bold(H)_(0,j) = 1$ pour $j in {7, 10, 15, 22, 24, 29}$
  //   - #text(fill: orange)[$c_0$] est relié à #text(fill: orange)[$v_7, v_10, v_15, v_22, v_24, v_29$] — chaque 1 crée une arête
  //   - $deg(c_0) = 6 = w_r$ — $c_0$ porte l'équation : #text(fill: orange)[$f_0 : r_7 plus.o r_10 plus.o r_15 plus.o r_22 plus.o r_24 plus.o r_29 = 0$]
  // ]
]

#myslide("Construction du Graphe : Nœud de Variable")[

  #v(1.2em)

  #align(center)[
    #scale(100%)[
      #hldpc_dynamic(hl_cols: (10,), show_labels: true, h_show: true)
    ]
  ]

  #v(3.5em)

  #align(center)[
    #move(dx: -0pt, dy: -39pt)[
      #scale(180%)[
        #tanner_canvas(hl_col: 10)
      ]
    ]
  ]
  // #v(0.15em)
  // #align(center)[
  //   #grid(
  //     columns: (auto, auto),
  //     column-gutter: 0.7cm,
  //     align: horizon,
  //     [#h_mini_tanner(hl_col: 10)], [#tanner_canvas(hl_col: 10)],
  //   )
  // ]

  // #[
  //   #set text(size: 1.05em)
  //   - Colonne #text(fill: blue, weight: "bold")[10] de $bold(H)$ : $bold(H)_(i,10) = 1$ pour $i in {0, 5, 11}$
  //   - #text(fill: blue)[$v_10$] est relié aux nœuds de contrôle #text(fill: blue)[$c_0, c_5, c_11$] — participe à *3 équations* de parité
  //   - $deg(v_10) = 3 = w_c$ — chaque bit est surveillé par $w_c$ contraintes indépendantes
  // ]
]

#myslide("Graphe de Tanner Final")[

  #move(dy: 4.5cm)[
    #align(center + horizon)[
      #tanner_canvas(scale: 0.91cm, show_all: true, colored: true, v_c_show: false)
    ]

    #align(center)[
      #grid(
        columns: (1fr, 1fr),
        gutter: 0.45cm,
        block(
          fill: blue.lighten(88%),
          stroke: 0.5pt + blue.lighten(40%),
          radius: 6pt,
          inset: (x: 10pt, y: 8pt),
          width: 100%,
        )[
          #place(dx: 47pt, dy: -0pt)[#scale(110%)[#icon_var]] #text(fill: blue, weight: "bold")[ Nœuds de variable] \
          // $n = 30 quad deg = w_c = 3$
        ],
        block(
          fill: orange.lighten(88%),
          stroke: 0.5pt + orange.lighten(40%),
          radius: 6pt,
          inset: (x: 10pt, y: 8pt),
          width: 100%,
        )[
          #place(dx: 47pt, dy: -0pt)[#scale(110%)[#icon_chk]] #text(
            fill: orange,
            weight: "bold",
          )[ Nœuds de contrôle] \
          // $m = 15 quad deg = w_r = 6$
        ],
      )
    ]
  ]
]

#let mot_valide = (
  0,
  0,
  0,
  0,
  0,
  1,
  0,
  1,
  1,
  0,
  0,
  0,
  0,
  0,
  1,
  0,
  1,
  0,
  0,
  0,
  0,
  0,
  0,
  0,
  1,
  1,
  0,
  1,
  0,
  0,
)

#myslide("La Contrainte de Somme Nulle")[
  #set text(size: 18pt)

  #definition(titre: "Vision Graphe", accent: green)[
    Si $s = 0$ alors que chaque nœud de contrôle est localement satisfait
  ]

  #v(1cm)

  #align(center)[
    #scale(150%)[
      #tanner_status(
        scale: 0.55cm,
        v_values: mot_valide,
        hl_v: (),
      )
    ]
  ]

  #v(1.2cm)

  #set text(size: 20pt)
  #align(center + horizon)[
    Chaque #icon_chk calcule le xor de ses voisins #icon_var : $space display(f_i = xor.big_(j in cal(N)(c_i)) v_j)$
  ]

  #v(1em)
  #align(center)[
    #move(dy: -10pt)[
      #scale(150%)[
        #zoom_contrainte(is_ok: true)
      ]
    ]
  ]

  // #v(1em)
  // #block(inset: 10pt, fill: rgb("#f0fdf4"), stroke: 1pt + rgb("#22c55e"), radius: 5pt)[
  //   Toutes les équations de parité sont vérifiées simultanément.
  // ]
]

#myslide("La Contrainte de Somme Nulle")[
  #set text(size: 18pt)

  #definition(titre: "Détection d'Erreur", accent: red)[
    Si un bit est inversé, toutes les contraintes associées sont à $1$
  ]

  #v(1cm)

  #align(center)[
    #let idx_erreur = 10
    #let mot_erronne = mot_valide.enumerate().map(p => if p.first() == idx_erreur { 1 } else { p.last() })
    #scale(150%)[
      #tanner_status(
        scale: 0.55cm,
        v_values: mot_erronne,
        hl_v: (idx_erreur,),
        highlight_edges: true,
      )
    ]
  ]

  #v(1.3cm)

  #align(center)[
    $0 plus.o bold(1) plus.o 0 = bold(1) arrow$ *Erreur détectée*
  ]

  #v(1em)
  #align(center)[
    #move(dy: 11.5pt)[
      #scale(150%)[
        #zoom_contrainte(is_ok: false)
      ]
    ]
  ]

  // #block(inset: 10pt, fill: rgb("#fef2f2"), stroke: 1pt + red, radius: 5pt)[
  //   *Principe du décodage :*
  //   Le bit $v_{10}$ est relié à *plusieurs alarmes* (arêtes rouges au premier plan). Il est le suspect idéal pour une correction par "bit-flipping".
  // ]
]


#myslide("Encodage LDPC : Calcul de G")[
  #set text(size: 18pt)

  #definition(titre: "Encodage", accent: blue)[
    Mot de code $bold(c)$ généré à partir d'un message $bold(u)$ :
    $bold(c) = bold(u) bold(G)$\
    // $bold(H) bold(G)^top = bold(0)$
  ]

  #v(2em)

  #align(center)[#scale(135%)[#paradoxe_densite_reel()]]

  #v(2em)

  #set text(size: 1em)
  // - Forme Systématique : Par élimination de Gauss sur $bold(H)$, on obtient
  - Forme Systématique

  #v(1em)
  #align(center)[
    #schema_systematique()
  ]

  #v(1em)
  #set text(size: 1em)
  - La matrice $bold(G)$ devient dense $=>$ encodage en $cal(O)(n^2)$

  // #v(1em)
  // #block(fill: rgb("#fef2f2"), stroke: 1pt + red, inset: 10pt, radius: 5pt)[
  //   *Défi Matériel :* Pour $n=64\,800$ (DVB-S2), $n^2$ est prohibitif. Solution : les codes *quasi-cycliques* (5G).
  // ]
]

#myslide("Décodage : Bit-Flipping")[
  #set text(size: 16pt)

  #definition(titre: "Décision Stricte (Hard Decision)", accent: black)[
    Algorithme *itératif* : les nœuds *échangent des bits* pour localiser les erreurs.
  ]

  #v(0.2em)

  #set text(size: 1em)

  #set text(size: 16pt)
  #definition(titre: "Message Passing", accent: blue)[
    // L'information *circule le long des arêtes* du graphe de Tanner à chaque itération.
    - #icon_var envoie son bit courant à ses voisins #icon_chk
    - #icon_chk renvoie son *verdict de parité* ($0$ ou $1$)
  ]

  #v(0.6em)

  #set text(1em)
  - Si $v_j$ participe à *trop d'équations non satisfaites* $=>$ on l'inverse.

    #uncover(2)[
      #grid(
        columns: (1fr, 1fr, 1fr),
        column-gutter: 10pt,
        // On aligne tout par le bas (bottom) pour que les CN soient sur la même ligne
        align: center + bottom,
        [
          #bf_step1_sending()
          #v(-0.5em)
          #text(style: "italic", fill: gray.darken(40%), size: 0.8em)[VN $arrow$ CN]
        ],
        [
          #bf_step2_verdict()
          #v(-0.5em)
          #text(style: "italic", fill: gray.darken(40%), size: 0.8em)[CN $arrow$ VN]
        ],
        [
          #bf_step3_flip()
          #v(-0.5em)
          #text(style: "italic", fill: gray.darken(40%), size: 0.8em)[Correction]
        ],
      )
    ]
  // #align(center + horizon)[
  //   #scale(100%)[#bp_hard_diagram()]
  //   #v(0.4em)
  //   #text(
  //     size: 0.72em,
  //     style: "italic",
  //     fill: gray.darken(20%),
  //   )[Échanges itératifs entre $V$ (cercles) et $C$ (carrés)]
  // ]
]

#myslide("Bit-Flipping : Graphe de flot de contrôle")[
  #set text(size: 17pt)

  // #grid(
  //   columns: (1.25fr, 0.75fr),
  //   gutter: 0.8cm,
  //   align: top,
  //   [
  //     #step_box(1, orange)[
  //       *CN Update — Évaluation* \
  //       #set text(size: 0.88em)
  //       Chaque nœud de contrôle $c_i$ calcule sa parité :
  //       $
  //         f_i = xor.big_(j in cal(N)(c_i)) v_j in {0, 1}
  //       $
  //       Si $f_i = 1$ : *l'équation n'est pas satisfaite* $=>$ $c_i$ envoie le message "Erreur" à tous ses voisins.
  //     ]
  //
  //     #v(0.45em)
  //
  //     #step_box(2, blue)[
  //       *VN Update — Vote* \
  //       #set text(size: 0.88em)
  //       Chaque bit $v_j$ compte ses alarmes reçues $k_j$.
  //       Si $k_j$ dépasse le seuil (ex. *majorité*) :
  //       $
  //         v_j arrow.l 1 - v_j quad ("FLIP")
  //       $
  //     ]
  //
  //     #v(0.45em)
  //
  //     #step_box(3, green.darken(10%))[
  //       *Vérification — Syndrome* \
  //       #set text(size: 0.88em)
  //       On recalcule $bold(s) = bold(H) bold(r)^top$.
  //       - Si $bold(s) = bold(0)$ : *Succès*, on s'arrête.
  //       - Sinon : on recommence l'étape ① (jusqu'à `max_iter`).
  //     ]
  //   ],
  //   [
  //     #align(center)[
  //       #v(0.3em)
  //       #scale(96%)[#schema_boucle_bf()]
  //     ]
  //   ],
  // )
  #align(center + horizon)[
    #move(dx: 2.5cm, dy: -0.5cm)[#scale(95%)[#schema_boucle_bf()]]
  ]
]

#let log10(x) = if x < 1e-9 { -9.0 } else { calc.log(x) / calc.log(10) }

#let raw_waterfall = (
  (0.0, 0.158461, 0.281269),
  (1.0, 0.130748, 0.247257),
  (2.0, 0.104310, 0.205882),
  (3.0, 0.079301, 0.151808),
  (4.0, 0.056395, 0.063852),
  (4.5, 0.046493, 0.023414),
  (5.0, 0.037572, 0.004078),
  (5.5, 0.029673, 0.000470),
  (6.0, 0.022973, 0.000045),
  (7.0, 0.012519, 0.000002),
  (8.0, 0.006030, 0.000000001),
  // On lisse la fin pour éviter l'effet "escalier"
  (8.5, 0.004200, 0.0),
  (9.0, 0.002800, 0.0),
  (9.5, 0.001800, 0.0),
  (10.0, 0.001100, 0.0),
)

#let pts_unc = raw_waterfall.map(r => (r.at(0), log10(r.at(1))))
#let pts_bf = raw_waterfall.map(r => (r.at(0), log10(r.at(2))))

// Données Convergence
#let iters = range(25)
#let syndrms = (135, 66, 60, 61, 31, 24, 21, 19, 19, 16, 15, 155, 45, 35, 31, 19, 16, 13, 10, 9, 6, 5, 4, 3, 0)

#myslide([Waterfall : LDPC (3, 6) $n = 1296, space k = 648, space R = 1/2$])[
  #align(center)[#waterfall_plot(pts_unc, pts_bf)]
  // #place(dx: 140pt, dy: -140pt)[
  //   #align(center)[
  //     // Affichage des paramètres du code
  //     #scale(110%)[
  //     #rect(fill: blue.lighten(95%), stroke: 0.5pt + blue.lighten(80%), radius: 4pt, inset: 10pt)[
  //       #set text(size: 18pt)
  //       *LDPC $(3, 6)$* #h(1cm)
  //       $n = 1296$, $k = 648$ #h(1cm)
  //       Rendement $R = 1/2$
  //     ]]
  //   ]
  // ]
]

// #myslide([Résultats : Convergence syndrome])[
//   #align(center)[#convergence_plot(iters, syndrms)]
// ]

// #myslide("Bit-Flipping : Analyse")[
#myslide("Bit-Flipping : Syndrome et Analyse")[
  #place(center + horizon, dx: -2cm, dy: 8cm)[#convergence_plot(iters, syndrms)]
  // TODO  PARLER DU GIRTH 4 => MAUVAIS
  #set text(size: 17pt)

  #uncover(2)[
    #place(dx: 13cm, dy: 1.0cm)[
      #scale(100%)[
        #definition(titre: "Avantages", accent: green.darken(10%), compact: true)[
          #set text(size: 0.88em)
          - *Complexité* : XOR et compteurs
          - $cal(O)(n)$ par itération
          // - *Matériel* : idéal FPGA/ASIC, massivement parallélisable
          // - *Simplicité* : inventé par Gallager (1962)
        ]
      ]

      #place(dx: -1.5cm, dy: 1.0cm)[
        #scale(100%)[
          #definition(titre: "Limite", accent: red, compact: true)[
            #set text(size: 0.88em)
            - Ignore la *confiance* du récepteur physique
            - Un bit reçu à $0.51$ V est traité comme $0$
            // - $=>$ Sous-optimal par rapport à la limite de Shannon

            // #v(0.3em)
            // #align(center)[
            //   #box(fill: rgb("#fff7ed"), stroke: (left: 3pt + orange), inset: (x: 8pt, y: 5pt))[
            //     #text(
            //       size: 0.82em,
            //     )[$arrow$ Nécessite le *Soft-Decision* (Belief Propagation) pour exploiter les niveaux de gris du signal]
            // ]
            // ]
          ]]
      ]]
  ]

]

#myslide("Décodage Soft : Le LLR")[
  #set text(size: 17pt)

  #definition(titre: "Signal", accent: blue.darken(10%))[
    On reçoit une valeur $y_i$ (ex: $+4.5$V ou $-0.2$V).
  ]

  #v(0.6em)

  #definition(titre: "Log-Likelihood Ratio (LLR)", accent: black)[
    $
      L(v_i) = ln(display(frac(P(v_i = 0 | y_i), P(v_i = 1 | y_i))))
    $
  ]

  #v(2em)

  #align(center)[
    #move(dx: 1.05cm)[
      #scale(160%)[#schema_llr_droite()]
    ]
  ]

  #v(1.7em)

  // Blocs de légende centrés
  #align(center)[
    #grid(
      columns: (220pt, 220pt),
      gutter: 1cm,
      block(fill: blue.lighten(92%), stroke: 1pt + blue, radius: 6pt, inset: 12pt)[
        #align(center)[
          #text(fill: blue.darken(20%), weight: "bold")[Signe] \
          #text(size: 0.85em)[Définit la valeur du bit]
        ]
      ],
      block(fill: orange.lighten(90%), stroke: 1pt + orange, radius: 6pt, inset: 12pt)[
        #align(center)[
          #text(fill: orange.darken(20%), weight: "bold")[|Valeur|] \
          #text(size: 0.85em)[Confiance dans la décision]
        ]
      ],
    )
  ]
]

#myslide("Sum-Product : Belief Propagation")[
  #set text(size: 17pt)
  #v(-0.7em)

  #definition(titre: "Décodage Optimal")[
    Échange itératif de croyances (LLR) entre les nœuds du graphe
  ]

  #definition(titre: "Information Extrinsèque")[
    Exclure l'avis du destinataire pour éviter l'auto-influence
  ]


  #block(
    fill: orange.lighten(92%),
    stroke: (left: 4pt + orange),
    radius: 4pt,
    inset: 18pt,
    width: 100%,
  )[
    #set text(size: 1.2em)
    *Mise à jour*  \
    #set text(size: 1.1em)
    // $ tanh(m_(c arrow v) / 2) = product_(u != v) tanh(m_(u arrow c) / 2) $
    $ m_(c arrow v) = 2 tanh^(-1) ( product_(u in cal(N)(c) \\ {v}) tanh(m_(u arrow c) / 2) ) $
  ]

  #place(dx: 5.2cm, dy: -5.2cm)[#scale(170%)[#icon_chk]]

  #block(
    fill: blue.lighten(92%),
    stroke: (left: 4pt + blue),
    radius: 4pt,
    inset: 18pt,
    width: 100%,
  )[
    #set text(size: 1.2em)
    *Mise à jour* \
    #set text(size: 1.1em)
    // $ m_(v arrow c) = L_"canal" + sum_(c' != c) m_(c' arrow v) $
    // $ m_(v arrow c) = L_v^((0)) + sum_(c' in cal(N)(v) \\ {c}) m_(c' arrow v) $
    $ m_(v arrow c) = L_(v"canal") + sum_(c' in cal(N)(v) \\ {c}) m_(c' arrow v) $
  ]

  #place(dx: 5.2cm, dy: -3.85cm)[#scale(170%)[#icon_var]]
]

#myslide("Sum-Product")[
  #set text(size: 16pt)

  #place(left, dx: 1.5cm, dy: 0cm)[
    #text(weight: "bold", size: 1.1em * 1.3)[Initialisation]
  ]

  #place(left, dx: 1.5cm, dy: 2cm)[
    #scale(130%)[
      #grid(
        columns: 1,
        gutter: 0.2cm,
        align: horizon,
        schema_detailed_init(),
        text(size: 0.9em)[$m_(v_j arrow c_i) = L_"canal"$],
      )
    ]
  ]

  #place(right, dx: -4.5cm, dy: 0cm)[
    #text(weight: "bold", fill: orange, size: 1.1em * 1.3)[Échange]
  ]

  #place(dx: 21cm, dy: 0.1cm)[#scale(170%)[#icon_chk]]

  #place(right, dx: -1cm, dy: 2.7cm)[
    #scale(130%)[
      #schema_detailed_cn()
    ]
  ]

  #place(left, dx: 1.5cm, dy: 9cm)[
    #text(weight: "bold", fill: blue, size: 1.1em * 1.3)[Échange]
  ]

  #place(dx: 5.4cm, dy: 9.15cm)[#scale(170%)[#icon_var]]

  #place(left, dx: 1.5cm, dy: 11cm)[
    #scale(130%)[
      #schema_detailed_vn()
    ]
  ]

  #place(right, dx: -3.5cm, dy: 9cm)[
    #text(weight: "bold", fill: green, size: 1.1em * 1.3)[Décision Finale]
  ]

  #place(right, dx: -1cm, dy: 11cm)[
    #scale(145%)[
      #schema_detailed_decision()
    ]
  ]

  #place(center + horizon, dx: -0.9cm, dy: 8.2cm)[
    #scale(115%)[
      #cetz.canvas(length: 1cm, {
        import cetz.draw: *

        let col_iter = gray.darken(50%)

        arc(
          (0, 0),
          radius: 1.8,
          start: 150deg,
          delta: -300deg,
          stroke: (paint: col_iter, thickness: 2pt, cap: "round"),
          mark: (end: "stealth", fill: col_iter, size: 0.3),
        )

        content((1.6, -1), [
          #set text(fill: col_iter, weight: "bold", size: 0.8em)
          #set align(center)
          Itérations\
          $i = 1, dots, I_(max)$
        ])
      })
    ]
  ]
]

#myslide([Transmission d'image])[
  #place(center, dy: -2cm)[
    #scale(85%)[
      #simulation_image_flow(
        image("src/origine.png", width: 100%),
        image("src/noisy.png", width: 100%),
        image("src/decoded_R05_1-2.png", width: 100%),
        image("src/decoded_R66_2-3.png", width: 100%),
        image("src/decoded_R75_3-4.png", width: 100%),
        img_w: 230pt,
        gap_top: 7.0,
        gap_bot: 2.0,
      )
    ]
  ]
]

#myslide("Min-Sum")[
  #set text(size: 15pt)

  // #grid(
  //   columns: (1fr, 1fr),
  //   gutter: 0.5cm,
  //   definition(titre: "Avantage Matériel", accent: black)[
  //     - *Comparateurs* pour le minimum
  //     - *XOR* pour le produit des signes
  //     #v(0.21cm)
  //   ],
  // definition(titre: "Mise à jour des CN", accent: orange)[
  //   #set text(size: 19pt)
  //   $
  //     m_(c arrow v_i) = product_(j != i) "sgn"(m_(v_j arrow c)) times min_(j != i) |m_(v_j arrow c)|
  //   $
  // ],
  // definition(titre: "Mise à jour des CN", accent: orange)[
  //   #set text(size: 19pt)
  //   $
  //     m_(c arrow v) = product_(u in cal(N)(c) \\ {v}) "sgn"(m_(u arrow c)) times min_(u in cal(N)(c) \\ {v}) |m_(u arrow c)|
  //   $
  // ],
  // definition(titre: "Mise à jour des CN", accent: orange)[
  //   #set text(size: 19pt)
  //   $
  //     m_(c arrow v) = & product_(u in cal(N)(c) \\ {v}) "sgn"(m_(u arrow c)) \
  //                     & times min_(u in cal(N)(c) \\ {v}) |m_(u arrow c)|
  //   $
  // ],
  // )
  #definition(titre: "Avantage Matériel", accent: black, titre_taille: 1.4em)[
    - *Comparateurs* pour le minimum
    - *XOR* pour le produit des signes
    #v(0.21cm)
  ]

  #definition(titre: "Mise à jour des CN", accent: orange, titre_taille: 1.4em)[
    #set text(size: 24pt)
    $
      m_(c arrow v) = product_(u in cal(N)(c) \\ {v}) "sgn"(m_(u arrow c)) times min_(u in cal(N)(c) \\ {v}) |m_(u arrow c)|
    $
  ]

  #v(1.5em)

  #align(center)[#scale(130%)[#schema_min_sum_complet()]]

  #v(-0.5em)
  #align(center)[
    #text(size: 1.1em, fill: gray.darken(40%), style: "italic")[
      Pour les VN : $display(m_(v arrow c) = L_"canal" + sum_(c' in cal(N)(v) \\ {c}) m_(c' arrow v))$
    ]
  ]
]

#let log10(x) = if x <= 1e-9 { -8.0 } else { calc.log(x) / calc.log(10) }

#let raw_data = (
  (0.00, 1.2397e-1, 4.9986e-1, 1.1404e-1, 1.2470e-1),
  (0.50, 1.1057e-1, 4.9993e-1, 9.5550e-2, 1.0485e-1),
  (1.00, 9.7682e-2, 4.9978e-1, 7.5017e-2, 8.2555e-2),
  (1.50, 8.4916e-2, 4.9993e-1, 4.6412e-2, 5.1824e-2),
  (2.00, 7.3000e-2, 5.0008e-1, 1.1640e-2, 1.3790e-2),
  (2.50, 6.1705e-2, 4.9987e-1, 4.0818e-4, 5.3380e-4),
  (3.00, 5.1501e-2, 5.0020e-1, 4.8765e-5, 8.0247e-6),
  (3.50, 4.1921e-2, 5.0005e-1, 0, 0),
  (4.00, 3.3645e-2, 4.9959e-1, 0, 0),
  (5.00, 1.9973e-2, 4.9890e-1, 0, 0),
  (6.00, 1.0584e-2, 3.8048e-1, 0, 0),
  (7.00, 4.8994e-3, 4.0660e-2, 0, 0),
  (8.00, 1.8194e-3, 3.8519e-4, 0, 0),
  (9.00, 5.6235e-4, 8.1790e-6, 0, 0),
  (10.00, 1.3133e-4, 6.1728e-7, 0, 0),
  (11.00, 2.5154e-5, 0, 0, 0),
)

#let pts_unc = raw_data.map(d => (d.at(0), log10(d.at(1))))
#let pts_bf = raw_data.map(d => (d.at(0), log10(d.at(2))))
#let pts_sp = raw_data.map(d => (d.at(0), log10(d.at(3))))
#let pts_ms = raw_data.map(d => (d.at(0), log10(d.at(4))))

#myslide([Waterfall : LDPC (3, 9) $n = 1296, space k = 864, space R = 2/3$])[
  #align(center)[
    // #rect(fill: gray.lighten(95%), radius: 4pt, inset: 8pt)[
    //   #set text(size: 16pt)
    //   Comparaison BER : *Bit-Flipping* (Décision dure) vs *Soft-Decisions* (SP & MS)
    // ]
    #comparison_waterfall_plot(pts_unc, pts_bf, pts_sp, pts_ms)
  ]
]

// #myslide("QC-LDPC")[
//   #align(center + horizon)[
//     #image("src/construction.jpg", width: 80%)
//   ]
// ]
//
// #myslide("Test réel")[
//   Transmission hackrf, test de diff de debit avec paquets
//   Test de transmission d'image avec différent ldpc non opti et opti (H diff etc)
//   #align(center + horizon)[
//     #image("src/construction.jpg", width: 80%)
//   ]
// ]
//
// #myslide("FPGA")[
//   #align(center + horizon)[
//     #image("src/construction.jpg", width: 80%)
//   ]
// ]

#myslide("La Topologie de H : Le Girth")[
  #set text(size: 20pt)

  #definition(titre: "Définition : Le Girth (La Maille)", accent: blue)[
    Longueur du plus court cycle dans le graphe de Tanner
  ]

  #v(0.5em)

  - Le girth est *pair*
  - La valeur minimale est $g = 4$.

  #v(1em)

  #align(center)[
    #block(fill: rgb("#f8fafc"), stroke: 1pt + blue.lighten(50%), inset: 10pt, radius: 5pt)[
      Girth élevé $=>$ Meilleure diffusion de l'information.
    ]
  ]

  #v(2.5em)

  #align(center)[
    #scale(140%)[
      #grid(
        columns: (1fr, 1fr),
        gutter: -7cm,
        [
          #schema_girth_4(highlight: false)
          #v(0.5em)
          #text(size: 0.8em, style: "italic")[Graphe de Tanner]
        ],
        [
          #schema_girth_4(highlight: true)
          #v(0.5em)
          #text(size: 0.8em, fill: red, weight: "bold")[4-Cycle]
        ],
      )
    ]
  ]
]

#myslide("Méthode de génération de H")[
  #place(center + horizon, dy: 8.1cm)[#graphe_tanner_fond(0.9cm, 1.75)]
  #place(center + horizon, dy: 7cm)[
    #block(width: 100%)[
      #text(size: 1.5em, weight: "bold", fill: black)[
        Gallager \

        Mackay-Neal \

        Progressive Edge-Growth
      ]
    ]
  ]
  // #align(center + horizon)[
  //   #image("src/construction.jpg", width: 50%)
  // ]
]
#myslide("Conclusion")[
  #place(center + horizon, dy: 8.1cm)[#graphe_tanner_fond(0.9cm, 1.75)]
  #place(center + horizon, dy: 7.7cm)[
    #block(width: 100%)[
      #text(size: 1.5em, weight: "bold", fill: black)[
        QC-LDPC Encodage \

        FPGA \

        Test Réels
      ]
    ]
  ]
]

#[]<fin>

#myslide("Annexe")[
  // #place(center + horizon, dy: 3.7cm)[#graphe_tanner_fond(0.9cm, 1.75)]
  #place(center + horizon, dy: 8.1cm)[#graphe_tanner_fond(0.9cm, 1.75)]
  #place(center + horizon, dy: 4cm)[
    #v(6.5cm)
    #align(center + horizon)[
      #text(size: 100pt)[
        Annexe
      ]
    ]
    // #align(center + horizon)[
    //   #image("src/construction.jpg", width: 80%)
  ]
]

// #myslide("Définition : Matrice Génératrice")[
//   #definition(titre: "Matrice Génératrice")[
//     $G in cal(M)_(k,n)(FF_2)$ dont les lignes sont une base de $cal(C)$
//   ]
//
//   #definition(titre: "Encodage")[
//     Pour un message $u in FF_2^k$ le mot de code $c in cal(C)$ est :
//     $
//       c = Phi(u) = u G
//     $
//   ]
//
//   #definition(titre: "Forme systématique")[
//     // TODO : changer [I_k | P] en un graphique jolie avec I et P dans un carré coloré
//     $
//       #dessiner_matrice($G =$, ((texte: $I_k$, largeur: 2.2, fond: gray.lighten(75%)), (texte: $P$, largeur: 2.2, fond: gray.lighten(75%))))
//       // G = mat(
//       //   I_k, P;
//       //   augment: #1,
//       //   delim: "[",
//       // )
//     $
//   ]
//
//   #[
//     #set text(size: 1.2em)
//     // - Pour $u in FF_2^k, space display(u dot.o G = mat(u, u dot.o P; augment: #1, delim: "[",))$
//
//     - Pour $u in FF_2^k, space #dessiner_matrice($u G =$, (
//         (texte: $u$, largeur: 1.1, fond: gray.lighten(75%)),
//         (texte: $u P$, largeur: 3.0, fond: gray.lighten(75%)),
//       ))$
//
//     - $P in cal(M)_(k ,n-k)(FF_2)$ matrice de parité\
//   ]
// ]
//
// #myslide("Définition : Matrice de Contrôle")[
//   #definition(titre: "Matrice de Contrôle")[
//     // $H = mat(
//     //   P^top, I_(n-k);
//     //   augment: #1,
//     //   delim: "[",
//     // )
//     $
//       #dessiner_matrice($H =$, (
//         (texte: $P^top$, largeur: 2.2, fond: gray.lighten(75%)),
//         (texte: $I_(n-k)$, largeur: 2.2, fond: gray.lighten(75%)),
//       ))
//     $
//
//   ]
//   #[
//     #set text(size: 1.2em)
//     - $cal(C) = ker(H) = {v in FF_2^n | H v^top = 0}$
//
//     - $display(G H^top = 0)$
//   ]
//
//   #definition(titre: "Syndrome")[
//     Pour un vecteur reçu $r = c + e, space s in FF_2^(n - k)$
//     $
//       s = H r^top = H c^top + H e^top = 0 + H e^top
//     $
//   ]
//   #[
//     #set text(size: 1.2em)
//     - Si $s = 0, space r$ est un mot de code valide
//     - Sinon $s$ donne la signature de l'erreur $e$
//   ]
//   // Possible décodage par syndrome
// ]

#myslide("Théorie derrière la définition des codes linaires")[
  Poser les notations algebriques etc...
]

#myslide("Métriques : BER et FER")[
  #set text(size: 18pt)

  #definition(titre: "Bit Error Rate (BER)", accent: blue)[
    $
      "BER" = frac("Nombre de bits incorrects reçus", "Nombre total de bits transmis")
    $
  ]

  #v(0.6em)

  #definition(titre: "Frame Error Rate (FER)", accent: orange)[
    $
      "FER" = frac("Nombre de trames avec au moins 1 bit incorrect", "Nombre total de trames")
    $
  ]

  #v(0.6em)

  #grid(
    columns: (1fr, 1fr),
    gutter: 0.8em,
    block(fill: blue.lighten(90%), stroke: 0.5pt + blue.lighten(30%), inset: 10pt, radius: 4pt)[
      *Courbe Waterfall* : BER en fonction de $E_b \/ N_0$ (dB) — représente les performances
    ],
    block(fill: orange.lighten(90%), stroke: 0.5pt + orange.lighten(30%), inset: 10pt, radius: 4pt)[
      FER $>=$ BER \
      Une seule erreur de bit invalide toute la trame
    ],
  )

  #v(0.5em)

  // - *Monte-Carlo* : simuler un grand nombre de transmissions, collecter ~1000 erreurs par point
  - $E_b\/N_0$ en dB $= 10 log_10 (E_b\/N_0)$
]

#myslide("Décodage par Maximum de Vraisemblance (ML)")[
  #set text(size: 17pt)

  #definition(titre: "Décodeur ML — Canal AWGN + BPSK")[
    $
      hat(bold(c)) = arg max_(bold(c) in cal(C)) P(bold(r) | bold(c)) = arg min_(bold(c) in cal(C)) d_H (bold(r), bold(c))
    $
    Minimiser la distance de Hamming au mot de code le plus proche.
  ]

  #v(0.5em)

  #definition(titre: "Décodage par Syndrome (Cosets)")[
    - Calculer $bold(s) = bold(H) bold(r)^top$
    - Chercher $bold(e)$ de poids minimal tel que $bold(H) bold(e)^top = bold(s)$
    - Table préalculée de $2^(n-k)$ entrées $-->$ faisable seulement si $n - k$ petit
  ]

  #v(0.5em)

  #definition(titre: "Complexité — NP-Difficile (Berlekamp, 1978)", accent: red)[
    Pour $bold(H)$ quelconque : $cal(O)(2^k)$ candidats à tester.

    - $k = 648$ : $2^(648) approx 10^(195)$ — hors de portée
    - Solution : exploiter la *structure creuse* de $bold(H)$ $-->$ BP itératif
  ]
]

#myslide("Codes LDPC Irréguliers")[
  #set text(size: 17pt)

  #definition(titre: "Distribution de Degrés (polynômes sur les arêtes)")[
    $
      lambda(x) = sum_i lambda_i x^(i-1), quad rho(x) = sum_j rho_j x^(j-1)
    $
    $lambda_i$ = fraction d'*arêtes* reliées à des VN de degré $i$, idem pour $rho_j$ et les CN.
  ]

  #v(0.5em)

  #grid(
    columns: (1fr, 1fr),
    gutter: 0.8em,
    definition(titre: "Avantages", accent: green.darken(10%))[
      - Approchent la limite de Shannon de plus près
      - VN de degré 2 : accélèrent la convergence initiale du BP
    ],
    definition(titre: "Optimisation", accent: orange)[
      *Density Evolution* : calcule analytiquement le seuil de décodage en fonction de $(lambda, rho)$ $->$ optimiser numériquement
    ],
  )

  #v(0.5em)

  - Contrainte : $n sum_i lambda_i \/ i = |E| = m sum_j rho_j \/ j$
  - DVB-S2 ($n = 64800$) : degrés variables entre 2 et 13 selon le rendement cible
]

#myslide("Encodage Efficace : Richardson-Urbanke")[
  #set text(size: 17pt)

  *Problème :* Forme systématique $->$ $bold(G)$ dense $->$ $cal(O)(n^2)$ opérations.

  #v(0.4em)

  #definition(titre: "Forme ALT : Approximate Lower Triangular")[
    Par permutation des lignes/colonnes de $bold(H)$ :
    $
      bold(H) = mat(A, B, T; C, D, E)
    $
    où $T$ est *triangulaire inférieure* de taille $(m-g) times (m-g)$, $g$ = gap (très petit).
  ]

  #v(0.4em)

  #definition(titre: [Encodage en $cal(O)(n + g^2)$])[
    Résolution en 2 phases :
    1. Calculer $bold(p)_1 in FF_2^g$ par élimination sur système de taille $g$
    2. Calculer $bold(p)_2 in FF_2^(m-g)$ par back-substitution sur $T$ (triangulaire)
  ]

  #v(0.3em)

  - En pratique (QC-LDPC / 5G / DVB-S2) : encodage par *registres à décalage* $->$ $cal(O)(n)$
]

#myslide("Canal AWGN")[
  #v(-0.8em)
  #set text(size: 18pt)

  #definition(titre: "Modèle du Canal — BPSK sur AWGN")[
    $
      y_i = x_i + n_i, quad x_i in {+1, -1}, quad n_i ~ cal(N)(0, sigma^2)
    $
    Mapping BPSK : bit $0 arrow.bar +1$, bit $1 arrow.bar -1$
  ]

  #v(-0.5em)

  #definition(titre: "Rapport Signal sur Bruit")[
    $
      frac(E_b, N_0) = frac(1, 2 R sigma^2)
    $
    $R = k\/n$ : rendement du code. Exprimé en dB : $10 log_10(E_b\/N_0)$.
  ]

  #v(-0.5em)

  #definition(titre: "LLR Initial sur Canal AWGN")[
    $
      L_"canal"(y_i) = ln frac(P(v_i = 0 | y_i), P(v_i = 1 | y_i)) = frac(2 y_i, sigma^2)
    $
    Le LLR est *proportionnel* à la valeur reçue $y_i$ : signe = décision, valeur absolue = confiance.
  ]
]

#myslide("Construction de Gallager (1962)")[
  #set text(size: 17pt)

  #definition(titre: "Principe")[
    Empiler $w_c$ sous-matrices de taille $(m \/ w_c) times n$ :
    $
      bold(H) = mat(H_1; H_2; dots.v; H_(w_c))
    $
  ]

  #v(0.4em)

  #set text(size: 21pt)
  *Algorithme :*
  - $H_1$ : blocs réguliers de $w_r$ uns consécutifs (colonnes disjointes)
  - $H_2, dots, H_(w_c)$ : copies de $H_1$ avec colonnes *permutées aléatoirement*

  #v(0.5em)

  #set text(size: 17pt)
  #grid(
    columns: (1fr, 1fr),
    gutter: 0.8em,
    definition(titre: "Avantage", accent: green.darken(10%))[
      Simple à construire.\
      Garantit $w_c$ et $w_r$ exacts.
    ],
    definition(titre: "Limite", accent: red)[
      Cycles de longueur 4 fréquents.\
      Aucun contrôle du girth.
    ],
  )
]

#myslide("Construction de MacKay-Neal (1996)")[
  #v(1.6cm)
  #set text(size: 19pt)

  #definition(titre: "Principe")[
    Construction *aléatoire* de $bold(H)$ avec évitement actif des 4-cycles.
  ]

  #v(0.4em)

  #set text(size: 21pt)
  *Algorithme* — pour chaque arête à placer $(v_j, c_i)$ :
  1. Vérifier l'absence de 4-cycle : $exists.not (j', i')$ tel que $H_(i,j') = H_(i',j) = H_(i',j') = 1$
  2. Si conflit : *rejeter* $c_i$ et tirer un autre nœud de contrôle
  3. Sinon : ajouter l'arête

  #v(0.5em)

  #set text(size: 18pt)
  #definition(titre: "Résultat")[
    - Garanti *sans 4-cycles* par construction $->$ girth $>= 6$
    // - Non garanti globalement (dépend de la densité)
    // - Fondement théorique du BP (article MacKay & Neal, 1996)
    - Performances nettement supérieures à Gallager
  ]
]

#myslide("Progressive Edge-Growth (Hu et al., 2005)")[
  #set text(size: 18pt)

  #definition(titre: "Idée")[
    Construire les arêtes *une par une* en maximisant le *girth local* à chaque étape.
  ]

  #v(0.4em)

  #set text(size: 21pt)
  *Algorithme* — pour relier $v_j$ à un nouveau CN :
  1. BFS depuis $v_j$ dans le graphe courant
  2. S'arrêter quand tous les CN ne sont plus accessibles à un nouveau niveau
  3. Relier $v_j$ au CN de plus faible degré *non encore atteint* par le BFS

  #v(0.5em)

  #set text(size: 18pt)
  #grid(
    columns: (1fr, 1fr),
    gutter: 0.8em,
    definition(titre: "Avantage", accent: green.darken(10%))[
      Maximise le girth global.\
      Surpasse Gallager et MacKay.\
      Standard de référence.
    ],
    definition(titre: "Complexité", accent: orange)[
      $cal(O)(n dot w_c dot m)$\
      Coût en construction seulement, pas en décodage.
    ],
  )
]

#myslide("Limite de Shannon : Canal AWGN")[
  #set text(size: 18pt)

  #definition(titre: "Capacité du Canal AWGN (Shannon, 1948)")[
    $
      C = 1/2 log_2(1 + "SNR") quad ["bit / utilisation"]
    $
    Il *existe* un code de rendement $R < C$ avec BER $-> 0$ quand $n -> infinity$.
  ]

  #v(0.6em)

  - Pour $R = 1/2$ : limite à $E_b\/N_0 approx 0.19$ dB
  - *Bit-Flipping* : ~5–6 dB de la limite
  - *Sum-Product* : ~1–1.5 dB de la limite

  #v(0.5em)

  #definition(titre: "Pourquoi de grands blocs ?", accent: orange)[
    Loi des grands nombres : pour $n$ grand, le bruit moyen par bit converge vers $sigma^2$.
    Plus $n$ est grand, plus on s'approche de la limite — au prix de la latence.
  ]

  #v(0.3em)

  #align(center)[
    #block(fill: gray.lighten(90%), stroke: 0.5pt + gray, inset: 10pt, radius: 4pt)[
      DVB-S2 : $n = 64800$ → à 0.5 dB de Shannon pour $R = 1/2$.
    ]
  ]
]

#myslide("Girth : Impact sur la Convergence du BP")[
  #v(0.7cm)
  #set text(size: 17pt)

  #definition(titre: [BP Exact $->$ Graphe = Arbre])[
    Sans cycle, le BP est *exact* et converge en au plus diamètre(graphe) itérations.
  ]

  #v(0.4em)

  #definition(titre: "Problème des Cycles Courts", accent: red)[
    Un cycle de longueur $2l$ : après $l$ itérations, un message revient à son point de départ.
    $->$ *Violation de l'indépendance* des messages $->$ BP sous-optimal.
  ]

  #v(0.4em)

  - *Girth = 4* : deux VN partagent deux CN $->$ corrélation immédiate, convergence vers solution incorrecte
  - *Girth = 6* : premier retour après 3 itérations $->$ stable pour quelques itérations
  // - PEG garantit girth $>= 6$ voire $8$ pour $n = 1296$, $w_c = 3$

  #v(0.4em)

  #align(center)[
    #block(fill: gray.lighten(90%), stroke: 0.5pt + gray, inset: 10pt, radius: 4pt)[
      girth $>= 2 I_"max"$ pour que les cycles n'affectent pas les $I_"max"$ premières itérations.
    ]
  ]
]

#myslide("QC-LDPC : Codes Quasi-Cycliques")[
  #v(1cm)
  #set text(size: 18pt)

  #definition(titre: [Structure de $bold(H)$ — Blocs Circulants])[
    $
      bold(H) = mat(
        bold(Pi)^(p_(0,0)), dots, bold(Pi)^(p_(0,n_b-1));
        dots.v, dots.down, dots.v;
        bold(Pi)^(p_(m_b-1,0)), dots, bold(Pi)^(p_(m_b-1,n_b-1))
      )
    $
    $bold(Pi)^p$ = identité décalée de $p$ lignes ($p = -1$ $->$ matrice nulle)
  ]

  #v(0.5em)

  #definition(titre: "Avantages")[
    - *Stockage* : seulement la matrice des exposants $p_(i,j)$ (taille $m_b times n_b$)
    - *Encodage* : registres à décalage $->$ $cal(O)(n)$ simple en FPGA
    - *Décodage* : $Z$ noeuds traités en parallèle par bloc circulant
  ]

  #v(0.3em)

  - DVB-S2 : $Z = 360$, $m_b times n_b = 45 times 90$ $->$ $n = 64800$
]

#myslide("Convergence du BP : Comportement et Critères")[
  #set text(size: 17pt)

  #definition(titre: "Hypothèse du BP")[
    Messages entrants en un nœud supposés *indépendants*.
    $->$ Exact sur arbre, *approché* sur graphe à grand girth.
  ]

  #v(0.4em)

  #definition(titre: "Critères d'Arrêt")[
    - *Syndrome nul* : $bold(H) hat(bold(c))^top = bold(0)$ $->$ succès, on s'arrête
    - *$I_"max"$ itérations* atteint $->$ échec $->$ paquet perdu (compte dans le FER)
  ]

  #v(0.4em)

  - En pratique : $I_"max" = 50$–$200$ selon le code et le SNR
  - Oscillations possibles autour d'un *pseudo-mot de code* (trapping set)
  - Plus le SNR est élevé, moins d'itérations nécessaires

  #v(0.4em)

  #align(center)[
    #block(fill: gray.lighten(90%), stroke: 0.5pt + gray, inset: 10pt, radius: 4pt)[
      Analyse théorique : *Density Evolution* — calcule le seuil exact de convergence en fonction de $E_b\/N_0$.
    ]
  ]
]

#myslide("CN Update : Formalisme probabiliste")[
  #set text(size: 16pt)
  #v(-0.3em)

  Soit $(V_u)_(u in cal(N)(c))$ une famille de variables aléatoires mutuellement indépendantes à valeurs dans $bb(F)_2$.
  On cherche à déterminer la loi du message $R_(c arrow v)(b)$ envoyé par le nœud de contrôle $c$.

  #v(0.5em)
  #definition(titre: "Conditionnement de l'événement de parité", accent: orange)[
    Le message $R_(c arrow v)(b)$ correspond à la probabilité conditionnelle :
    #v(0.5em)
    #align(center)[#scale(105%)[$
      display(R_(c arrow v)(b) = P( limits(xor.big)_(u in cal(N)(c)) V_u = 0 | V_v = b ))
    $]]

    Par linéarité du XOR, cette condition est équivalente à :
    #v(0.5em)
    #align(center)[#scale(110%)[$ display(P( limits(xor.big)_(u in cal(N)(c) backslash {v}) V_u = b )) $]]
  ]

  Par le théorème des probabilités totales appliqué au système complet d'événements associé aux configurations $x in bb(F)_2^(d_c - 1)$ des voisins :

  #align(center)[#scale(120%)[
    $
      display(R_(c arrow v)(b) = limits(sum)_(x in bb(F)_2^(d_c - 1) \ limits(xor.big)_(u in cal(N)(c) backslash {v}) x_u = b) limits(product)_(u in cal(N)(c) backslash {v}) P(V_u = x_u))
    $
  ]]
]

#myslide("CN Update (1) : Probabilités")[
  #set text(size: 16pt)
  #v(-0.3em)

  En utilisant les messages entrants du graphe, on définit la probabilité locale $P_(u arrow c)(x_u) = P(V_u = x_u)$.

  Le message sortant devient :

  #v(1em)
  #align(center)[#scale(130%)[
    $
      display(R_(c arrow v)(b) = limits(sum)_(x in bb(F)_2^(d_c - 1) \ limits(xor.big)_(u in cal(N)(c) backslash {v}) x_u = b) quad limits(product)_(u in cal(N)(c) backslash {v}) P_(u arrow c)(x_u))
    $
  ]]

  #v(1em)
  Exacte mais complexité est exponentielle en $d_c$. On utilise alors une transformation pour simplifier le calcul (Lemme de Gallager).
]

#myslide("CN Update (2) : Lemme de Gallager")[
  #set text(size: 16pt)

  #block(
    fill: orange.lighten(94%),
    stroke: (left: 4pt + orange),
    radius: 4pt,
    inset: 20pt,
    width: 100%,
  )[
    *Lemme de Gallager* -- Soient $(X_1, ..., X_n)$ des variables de Bernoulli indépendantes sur $bb(F)_2$.
    #v(0.5em)
    #align(center)[#scale(130%)[
      $ display(P(limits(xor.big)_(i=1)^n X_i = 0) = 1/2 (1 + limits(product)_(i=1)^n E[(-1)^(X_i)])) $
    ]]
  ]

  #v(0.8em)
  // L'espérance correspond au biais de la loi, noté $delta_(u arrow c)$ :
  On notes :
  #align(center)[#scale(120%)[
    $ display(E[(-1)^(V_u)] = delta_(u arrow c) = P(V_u = 0) - P(V_u = 1) = 1 - 2 P_(u arrow c)(1)) $
  ]]

  #v(0.8em)
  On en déduit les probabilités marginales conditionnelles pour le nœud $c$ :

  #align(center)[#scale(120%)[
    $
      display(R_(c arrow v)(0) = 1/2 (1 + limits(product)_(u in cal(N)(c) backslash {v}) delta_(u arrow c)))
      space , space space
      display(R_(c arrow v)(1) = 1/2 (1 - limits(product)_(u in cal(N)(c) backslash {v}) delta_(u arrow c)))
    $
  ]]
]

#myslide("CN Update (3) : Passage aux LLR")[
  #set text(size: 16pt)

  #definition(titre: "Log-Likelihood Ratio (LLR)", accent: blue.darken(10%))[
    Le message LLR entrant $m_(u arrow c)$ au nœud de contrôle est défini par :
    #v(0.5em)
    #align(center)[#scale(130%)[
      $ display(m_(u arrow c) = ln(frac(P(V_u = 0), P(V_u = 1)))) $
    ]]
  ]

  #v(0.8em)
  $delta_(u arrow c)$ s'exprime alors :
  #align(center)[#scale(130%)[
    $ display(delta_(u arrow c) = tanh(frac(m_(u arrow c), 2))) $
  ]]

  #v(0.8em)
  On en déduit le LLR du message sortant :
  #align(center)[#scale(130%)[
    $
      display(m_(c arrow v) = ln(frac(R_(c arrow v)(0), R_(c arrow v)(1))) = 2 tanh^(-1) ( limits(product)_(u in cal(N)(c) backslash {v}) tanh(frac(m_(u arrow c), 2)) ))
    $
  ]]
]

#myslide("CN Update (4) : Algorithmes")[
  #set text(size: 16pt)

  #block(
    fill: orange.lighten(92%),
    stroke: (left: 4pt + orange),
    radius: 4pt,
    inset: 15pt,
    width: 100%,
  )[
    *Sum-Product* -- Mise à jour :
    #v(0.5em)
    #align(center)[#scale(130%)[
      $
        display(m_(c arrow v) = 2 tanh^(-1) ( limits(product)_(u in cal(N)(c) backslash {v}) tanh(frac(m_(u arrow c), 2)) ))
      $
    ]]
  ]

  #v(1em)
  #block(
    fill: blue.lighten(92%),
    stroke: (left: 4pt + blue),
    radius: 4pt,
    inset: 15pt,
    width: 100%,
  )[
    *Min-Sum* -- Approximation :
    #v(0.5em)
    #align(center)[#scale(130%)[
      $
        display(m_(c arrow v) approx ( limits(product)_(u in cal(N)(c) backslash {v}) op("sgn")(m_(u arrow c)) ) times limits(min)_(u in cal(N)(c) backslash {v}) |m_(u arrow c)|)
      $
    ]]
  ]
]

#myslide("Bit-Flipping : Choix du Seuil")[
  #set text(size: 18pt)

  - Chaque $v_j$ reçoit de ses $w_c$ voisins $c_i$ un verdict $f_i in {0, 1}$.
  - Il retourne son bit si *trop d'équations échouent*.

  #v(0.3em)

  #definition(titre: "Stratégies de Seuil")[
    #table(
      columns: (1.2fr, 1.4fr, 1.4fr),
      inset: 8pt,
      stroke: 0.5pt + gray,
      align: center + horizon,
      [*Seuil*], [*Règle*], [*Remarque*],
      [Majorité stricte], [$k_j > w_c \/ 2$], [Standard Gallager-A],
      [Seuil fixe $b$], [$k_j >= b$ (ex. $b = w_c$)], [Gallager-B : plus conservateur],
      [Tous insatisfaits], [$k_j = w_c$], [Très conservateur, peu de faux retournements],
    )
  ]

  #v(0.5em)

  - *Gallager-A* : retourner si *toutes* les contraintes sont violées ($k_j = w_c$) — évite les oscillations
  - *Gallager-B* : retourner si *au moins* $b < w_c$ contraintes violées — plus agressif
]

#myslide("Que Se Passe-t-il en Cas d'Échec ?")[
  #set text(size: 16pt)

  #definition(titre: "Échec du Décodeur")[
    Après $I_"max"$ itérations : syndrome $bold(s) != bold(0)$ $->$ *décodage échoué*. \
    Deux scénarios :
    - La trame est *perdue* (FPV, diffusion) $->$ acceptable
    - La trame doit *arriver* (fichier, protocole) $->$ retransmission nécessaire
  ]

  #v(0.5em)

  #definition(titre: "ARQ — Automatic Repeat reQuest")[
    Le récepteur envoie un *NACK* (Not Acknowledged) : l'émetteur retransmet.
    - *Stop-and-Wait* : simple mais inefficace
    - *Hybrid ARQ (HARQ)* : combine ARQ + codage — standard LTE/5G
  ]

  #v(0.4em)

  #definition(titre: "HARQ Type II — Chase Combining", accent: blue)[
    Le récepteur *combine* les LLR des deux transmissions avant de redécoder :
    $
      L_"total"(y_i) = L_"canal"^1(y_i) + L_"canal"^2(y_i)
    $
    // $->$ gain de $3$ dB sans retransmission supplémentaire effective.
  ]
]

#myslide("Limites du Modèle AWGN")[
  #v(-0.7cm)
  #set text(size: 17pt)

  #definition(titre: "Ce que AWGN suppose")[
    - Bruit *blanc* : indépendant d'un symbole à l'autre
    - Distribution *gaussienne* stationnaire
    - Canal *sans mémoire* : chaque bit perturbé indépendamment
  ]

  #v(-0.2cm)

  #definition(titre: "Canaux Réels — Ce qu'AWGN ne capture pas", accent: red)[
    #table(
      columns: (1.2fr, 1.8fr),
      inset: 8pt,
      stroke: 0.5pt + gray,
      [*Phénomène*], [*Impact*],
      [Évanouissements (fading)], [SNR varie dans le temps],
      [Burst d'erreurs], [Bits consécutifs corrompus $->$ entrelacement nécessaire],
      [Bruit impulsionnel], [Pics de bruit ponctuels (moteurs, orages)],
      [Erreurs de phase], [Synchronisation imparfaite en BPSK],
    )
  ]

  #v(-0.2cm)

  #definition(titre: "Solution : Entrelacement", accent: blue)[
    Permuter les bits *avant* l'envoi : les erreurs en rafale deviennent des erreurs isolées pour le décodeur LDPC.
  ]
]

#set raw(theme: "ldpc-theme.tmTheme")

#generer_slides_code(
  (
    "src/rs/code.rs",
    "src/rs/encoder.rs",
    "src/rs/decoder.rs",
    "src/rs/generator.rs",
    "src/rs/channel.rs",
    "src/rs/graph.rs",
    "src/rs/matrix.rs",
    "src/rs/benchmark.rs",
    "src/rs/benchmark2.rs",
    "src/rs/image_sim.rs",
    "src/rs/lib.rs",
  ),
  myslide,
  graphe_tanner_fond,
)
