diff --git a/slides/cours_20.md b/slides/cours_20.md
new file mode 100644
index 0000000000000000000000000000000000000000..e609d2a08442ef08c78f8779bcc54e930af48398
--- /dev/null
+++ b/slides/cours_20.md
@@ -0,0 +1,891 @@
+---
+title: "Arbres AVL et arbres quaternaires"
+date: "2024-04-09"
+---
+
+# Rappel: Algorithme d'insertion
+
+* Insérer le noeud comme d'habitude.
+* Mettre à jour les facteurs d'équilibre jusqu'à la racine (ou au premier
+  noeud déséquilibré).
+* Rééquilibrer le noeud si nécessaire.
+
+# Rappel: les cas de déséquilibre
+
+::: columns
+
+:::: column
+
+## Cas 1a
+
+* `u`, `v`, `w` même hauteur.
+* déséquilibre en `B` après insertion dans `u`
+
+![Après insertion](figs/cas1a_gauche.png)
+
+::::
+
+:::: column
+
+## Cas 1a
+
+* Comment rééquilibrer?
+
+. . .
+
+* ramène `u`, `v` `w` à la même hauteur.
+* `v` à droite de `A` (gauche de `B`)
+
+![Après équilibrage](figs/cas1a_droite.png)
+
+::::
+
+:::
+
+# Rappel: Les cas de déséquilibre
+
+
+::: columns
+
+:::: column
+
+## Cas 2a
+
+* `h(v1)=h(v2), h(u)=h(w)`.
+* déséquilibre en `C` après insertion dans `v2`
+
+![Après insertion](figs/cas2a_gauche.png)
+
+::::
+
+:::: column
+
+## Cas 2a
+
+* Comment rééquilibrer?
+
+. . .
+
+* ramène `u`, `v2`, `w` à la même hauteur (`v1` pas tout à fait).
+* `v2` à droite de `B` (gauche de `C`)
+* `B` à droite de `A` (gauche de `C`)
+* `v1` à droite de `A` (gauche de `B`)
+
+![Après équilibrage](figs/cas2a_droite.png)
+
+::::
+
+:::
+
+# Rappel: Rééquilibrage
+
+## Rotation simple
+
+![On verra un peu après les rotations.](figs/rotation_gauche_droite.png)
+
+# Rappel: La rotation gauche-droite
+
+## Le cas 2a/b
+
+![La double rotation de l'enfer.](figs/double_rotation_gauche_droite.png)
+
+# Un petit exercice
+
+* Insérer les nœuds suivants dans un arbre AVL
+
+```
+25 | 60 | 35 | 10 | 5 | 20 | 65 | 45 | 70 | 40 
+    | 50 | 55 | 30 | 15
+```
+
+```
+
+
+
+
+
+
+
+
+
+
+```
+
+
+# Suppression dans un arbre AVL
+
+
+::: columns
+
+:::: column
+
+## Algorithme par problème: supprimer 10
+
+```{.mermaid format=pdf width=400 loc=figs/}
+graph TD;
+    id0((8))-->id1((4));
+    id0-->id2((10));
+    id1-->id3((2));
+    id1-->id4((6));
+    id3-->id5((1));
+    id3-->id6((  ))
+    id4-->id7((  ))
+    id4-->id8((7))
+    id2-->id9((9))
+    id2-->id10((14))
+    id10-->id11((12))
+    id10-->id12((16))
+    style id6 fill:#fff,stroke:#fff
+    style id7 fill:#fff,stroke:#fff
+```
+
+::::
+
+:::: column
+
+. . .
+
+## Algorithme par problème: supprimer 10
+
+```{.mermaid format=pdf width=400 loc=figs/}
+graph TD;
+    id0((8))-->id1((4));
+    id0-->id2((12));
+    id1-->id3((2));
+    id1-->id4((6));
+    id3-->id5((1));
+    id3-->id6((  ))
+    id4-->id7((  ))
+    id4-->id8((7))
+    id2-->id9((9))
+    id2-->id10((14))
+    id10-->id11((  ))
+    id10-->id12((16))
+    style id6 fill:#fff,stroke:#fff
+    style id7 fill:#fff,stroke:#fff
+    style id11 fill:#fff,stroke:#fff
+```
+
+::::
+
+:::
+
+# Suppression dans un arbre AVL
+
+
+::: columns
+
+:::: column
+
+## Algorithme par problème: supprimer 8
+
+```{.mermaid format=pdf width=400 loc=figs/}
+graph TD;
+    id0((8))-->id1((4));
+    id0-->id2((12));
+    id1-->id3((2));
+    id1-->id4((6));
+    id3-->id5((1));
+    id3-->id6((  ))
+    id4-->id7((  ))
+    id4-->id8((7))
+    id2-->id9((9))
+    id2-->id10((14))
+    id10-->id11((  ))
+    id10-->id12((16))
+    style id6 fill:#fff,stroke:#fff
+    style id7 fill:#fff,stroke:#fff
+    style id11 fill:#fff,stroke:#fff
+```
+
+::::
+
+:::: column
+
+. . .
+
+## Algorithme par problème: rotation de 12
+
+```{.mermaid format=pdf width=400 loc=figs/}
+graph TD;
+    id0((9))-->id1((4));
+    id0-->id2((12));
+    id1-->id3((2));
+    id1-->id4((6));
+    id3-->id5((1));
+    id3-->id6((  ))
+    id4-->id7((  ))
+    id4-->id8((7))
+    id2-->id9((  ))
+    id2-->id10((14))
+    id10-->id11((  ))
+    id10-->id12((16))
+    style id6 fill:#fff,stroke:#fff
+    style id7 fill:#fff,stroke:#fff
+    style id9 fill:#fff,stroke:#fff
+    style id11 fill:#fff,stroke:#fff
+```
+
+::::
+
+:::
+
+# Suppression dans un arbre AVL
+
+::: columns
+
+:::: column
+
+## Algorithme par problème: rotation de 12
+
+```{.mermaid format=pdf width=400 loc=figs/}
+graph TD;
+    id0((9))-->id1((4));
+    id0-->id2((14));
+    id1-->id3((2));
+    id1-->id4((6));
+    id3-->id5((1));
+    id3-->id6((  ))
+    id4-->id7((  ))
+    id4-->id8((7))
+    id2-->id9((12))
+    id2-->id10((16))
+    style id6 fill:#fff,stroke:#fff
+    style id7 fill:#fff,stroke:#fff
+```
+
+::::
+
+:::: column
+
+. . .
+
+1. On supprime comme d'habitude.
+2. On rééquilibre si besoin à l'endroit de la suppression.
+
+* Facile non?
+
+. . .
+
+* Plus dur....
+
+::::
+
+:::
+
+# Suppression dans un arbre AVL 2.0
+
+::: columns
+
+:::: column
+
+## Algorithme par problème: suppression de 30
+
+```{.mermaid format=pdf width=400 loc=figs/}
+graph TD;
+    id0((50))-->id1((30));
+    id0-->id2((100));
+    id1-->id3((10));
+    id1-->id4((40));
+    id3-->id5((  ));
+    id3-->id6((20))
+    id2-->id7((80))
+    id2-->id8((200))
+    id7-->id9((70))
+    id7-->id10((90))
+    id9-->id11((60))
+    id9-->id12((  ))
+    id8-->id13((  ))
+    id8-->id14((300))
+    style id5 fill:#fff,stroke:#fff
+    style id12 fill:#fff,stroke:#fff
+    style id13 fill:#fff,stroke:#fff
+```
+
+::::
+
+:::: column
+
+. . .
+
+## Algorithme par problème: rotation GD autour de 40
+
+```{.mermaid format=pdf width=400 loc=figs/}
+graph TD;
+    id0((50))-->id1((40));
+    id0-->id2((100));
+    id1-->id3((10));
+    id1-->id4((  ));
+    id3-->id5((  ));
+    id3-->id6((20))
+    id2-->id7((80))
+    id2-->id8((200))
+    id7-->id9((70))
+    id7-->id10((90))
+    id9-->id11((60))
+    id9-->id12((  ))
+    id8-->id13((  ))
+    id8-->id14((300))
+    style id4 fill:#fff,stroke:#fff
+    style id5 fill:#fff,stroke:#fff
+    style id12 fill:#fff,stroke:#fff
+    style id13 fill:#fff,stroke:#fff
+```
+
+::::
+
+:::
+
+# Suppression dans un arbre AVL 2.0
+
+::: columns
+
+:::: column
+
+## Argl! 50 est déséquilibré!
+
+```{.mermaid format=pdf width=400 loc=figs/}
+graph TD;
+    id0((50))-->id1((20));
+    id0-->id2((100));
+    id1-->id3((10));
+    id1-->id4((40));
+    id2-->id7((80))
+    id2-->id8((200))
+    id7-->id9((70))
+    id7-->id10((90))
+    id9-->id11((60))
+    id9-->id12((  ))
+    id8-->id13((  ))
+    id8-->id14((300))
+    style id12 fill:#fff,stroke:#fff
+    style id13 fill:#fff,stroke:#fff
+```
+
+::::
+
+:::: column
+
+. . .
+
+## Algorithme par problème: rotation DG autour de 50
+
+```{.mermaid format=pdf width=400 loc=figs/}
+graph TD;
+    id0((80))-->id1((50));
+    id0-->id2((100));
+    id1-->id3((20));
+    id1-->id4((70));
+    id3-->id5((10));
+    id3-->id6((40));
+    id4-->id9((60))
+    id4-->id10((  ))
+    id2-->id7((90))
+    id2-->id8((200))
+    id8-->id13((  ))
+    id8-->id14((300))
+    style id10 fill:#fff,stroke:#fff
+    style id13 fill:#fff,stroke:#fff
+```
+
+::::
+
+:::
+
+# Résumé de la suppression
+
+1. On supprime comme pour un arbre binaire de recherche.
+2. Si un nœud est déséquilibré, on le rééquilibre.
+    * Cette opération peut déséquilibrer un autre nœud.
+3. On continue à rééquilibrer tant qu'il y a des nœuds à équilibrer.
+
+
+# Les arbres quaternaires
+
+\Huge Les arbres quaternaires
+
+
+# Les arbres quaternaires
+
+## Définition
+
+Arbre dont chaque nœud a 4 enfants ou aucun. 
+
+![Un exemple d'arbre quaternaire.](figs/quad_ex.svg)
+
+# Les arbres quaternaires
+
+## Cas d'utilisation
+
+Typiquement utilisés pour représenter des données bidimensionnelles.
+
+Son équivalent tri-dimensionnel est l'octree (chaque nœud a 8 enfants ou aucun).
+
+## Cas d'utilisation: images
+
+* Stockage: compression.
+* Transformations: symétries, rotations, etc.
+
+## Cas d'utilisation: simulation
+
+* Indexation spatiale.
+* Détection de collisions.
+* Simulation de galaxies, Barnes-Hut.
+
+# Exemple de compression
+
+::: columns
+
+:::: {.column width=30%}
+
+## Comment représenter l'image
+
+![Image noir/blanc.](figs/board_blacked_parts.svg)
+
+::::
+
+:::: {.column width=70%}
+
+## Sous la forme d'un arbre quaternaire?
+
+. . .
+
+![L'arbre quaternaire correspondant.](figs/quad_img.svg)
+
+**Économie?**
+
+. . .
+
+Image 64 pixels, arbre 25 nœuds.
+
+::::
+
+:::
+
+
+# Structure de données
+
+::: columns
+
+:::: {.column width=50%}
+
+## Pseudo-code?
+
+. . .
+
+```python
+struct node
+    info
+    node sup_gauche, sup_droit,
+        inf_gauche, inf_droit
+```
+
+![Un nœud d'arbre quaternaire.](figs/quad_struct.svg)
+
+::::
+
+:::: {.column width=50%}
+
+## En C?
+
+. . .
+
+```C
+struct _node {
+    int info;
+    struct _node *sup_left;
+    struct _node *sup_right;
+    struct _node *inf_left;
+    struct _node *inf_right;
+};
+```
+
+* Pourquoi le `*` est important?
+
+. . .
+
+* Type récursif => taille inconnue à la compilation.
+
+::::
+
+:::
+
+# Une fonctionnalité simple
+
+\footnotesize
+
+## La fonction `est_feuille(noeud)`
+
+* Problème avec cette implémentation?
+
+```python
+bool est_feuille(noeud) 
+    retourne
+        est_vide(sup_gauche(noeud)) &&
+        est_vide(sup_droit(noeud)) &&
+        est_vide(inf_gauche(noeud)) &&
+        est_vide(inf_droit(noeud))
+```
+
+. . .
+
+* Inutile d'avoir 4 conditions (soit 4 enfants soit aucun!)
+* Facile d'en oublier un!
+* Comment changer la structure pour que ça soit moins terrible?
+
+. . .
+
+```python
+struct node
+    info
+    node enfant[4]
+```
+
+# Structure de données
+
+## En C?
+
+. . .
+
+```C 
+typedef struct _node {
+    int info;
+    struct _node *child[4];
+} node;
+```
+
+## Fonction `is_leaf(node *tree)`?
+
+. . .
+
+```C 
+bool is_leaf(node *tree) {
+    return (NULL == tree->child[0]); // only first matters
+}
+```
+
+# Problème à résoudre
+
+* Construire un arbre quaternaire à partir d'une image:
+    * Créer l'arbre (allouer la mémoire pour tous les nœuds),
+    * Le remplir avec les valeurs des pixels.
+* Compression de l'image:
+    * Si les pixels sont les mêmes dans le quadrant on supprime le sous-arbre (sans perte)
+    * Si les pixels dévient pas trop on supprime le quadrant (avec perte)
+
+# Création de l'arbre
+
+## Comment créer un arbre de profondeur `prof` (3min)?
+
+. . .
+
+```python
+arbre creer_arbre(prof)
+    n = nouveau_noeud() # alloue la mémoire
+    si prof > 0
+        pour i = 0 à 3
+            n.enfant[i] = creer_arbre(prof-1)
+    retourne n
+```
+
+## En `C` (3 min, matrix)?
+
+. . .
+
+```C
+node *qt_create(int depth) {
+    node *n = calloc(1, sizeof(node));
+    if (depth > 0) {
+        for (int i = 0; i < 4; ++i) {
+            n->child[i] = qt_create(depth-1);
+        }
+    }
+    return n;
+}
+```
+
+# Le nombre de nœuds?
+
+## Comment implémenter la fonction (pseudo-code, 5min, matrix)?
+
+. . .
+
+```C
+entier nombre_nœuds(arbre)
+    si est_feuille(arbre)
+        retourne 1
+    sinon
+        somme = 1
+        pour i de 0 à 3
+            somme += nombre_nœuds(arbre.enfant[i])
+        retourne somme
+```
+
+# Le nombre de nœuds?
+
+## Comment implémenter la fonction en C (3min, matrix)?
+
+. . .
+
+```C
+int size(node *qt) {
+    if (is_leaf(qt)) {
+        return 1;
+    } else {
+        int sum = 1;
+        for (int i = 0; i < 4; ++i) {
+            sum += size(qt->child[i]);
+        }
+        return sum;
+    }
+}
+```
+
+# La profondeur en C?
+
+## Implémentation (5min, matrix)
+
+. . .
+
+\footnotesize
+
+```C
+int max(int x, int y) {
+   return  (x >= y ? x : y);
+}
+int max_depth(int depths[4]) {
+    int m = depths[0];
+    for (int i = 1; i < 4; ++i) {
+        m = max(m, depths[i]);
+    }
+    return m;
+}
+int depth(node *qt) {
+    int depths[] = {0, 0, 0, 0};
+    if (is_leaf(qt)) {
+        return 0;
+    } else {
+        for (int i = 0; i < 4; ++i) {
+            depths[i] = depth(qt->child[i]);
+        }
+        return 1 + max_depth(depths);
+    }
+}
+```
+
+# Fonctions utiles (1/4)
+
+## Comment remplir un arbre depuis une matrice?
+
+```
+   SG=0  |  SD=1
+ 21 | 12 | 4 |  4
+  9 |  7 | 4 |  4
+-----------------
+  1 |  1 | 0 | 31
+  1 |  1 | 3 | 27
+   IG=2  |  ID=3
+```
+
+## Quel arbre cela représente?
+
+. . .
+
+![L'arbre correspondant](figs/quad_img_simple.svg)
+
+# Fonctions utiles (2/4)
+
+* On veut transformer une ligne/colonne en feuille.
+* Comment?
+
+::: columns
+
+:::: {.column width=40%}
+
+## Soit `ligne=2`, `colonne=3`
+
+```
+   SG=0  |  SD=1
+ 21 | 12 | 4 |  4
+  9 |  7 | 4 |  4
+-----------------
+  1 |  1 | 0 | 31
+  1 |  1 | 3 | 27
+   IG=2  |  ID=3
+```
+
+::::
+
+:::: {.column width=70%}
+
+## Trouver un algorithme
+
+![Déterminer un algorithme.](figs/quad_img_simple.svg)
+
+* Quelle feuille pour 31 (`li=2`, `co=3`)?
+* Plus important: quel chemin?
+
+. . .
+
+* `co -> G/D`, `li -> S/I`,
+* `2 * (li / 2) + co / 2 -> 2 * 1 + 1 = 3`
+* `2 * ((li % 2) / 1) + (co % 2) / 1 -> 2 * 0 + 1 = 1`
+* Comment généraliser?
+
+::::
+
+:::
+
+# Fonctions utiles (3/4)
+
+::: columns
+
+:::: {.column width=40%}
+
+## Soit `ligne=2`, `colonne=3`
+
+```
+   SG=0  |  SD=1
+ 21 | 12 | 4 |  4
+  9 |  7 | 4 |  4
+-----------------
+  1 |  1 | 0 | 31
+  1 |  1 | 3 | 27
+   IG=2  |  ID=3
+```
+
+::::
+
+:::: {.column width=70%}
+
+## Trouver un algorithme (prendre plusieurs exemples, 15min, matrix)
+
+![Déterminer un algorithme.](figs/quad_img_simple.svg)
+
+* Comment généraliser?
+
+. . .
+
+```C
+noeud position(li, co, arbre)
+    d = profondeur(arbre);
+    tant_que (d >= 1)
+        index = 2 * ((li % 2^d) / 2^(d-1)) +
+            (col % 2^d) / 2^(d-1)
+        arbre = arbre.enfant[index]
+        d -= 1
+    retourne arbre
+```
+
+
+::::
+
+:::
+
+# Fonctions utiles (4/4)
+
+\footnotesize
+
+## Pseudo-code
+
+```C
+noeud position(li, co, arbre)
+    d = profondeur(arbre);
+    tant_que (d >= 1)
+        index = 2 * ((li % 2^d) / 2^(d-1)) +
+            (col % 2^d) / 2^(d-1)
+        arbre = arbre.enfant[index]
+        d -= 1
+    retourne arbre
+```
+
+## Écrire le code `C` correspondant (5min, matrix)
+
+```C
+
+
+
+
+
+
+
+
+
+
+
+```
+
+# Remplir l'arbre
+
+## A partir d'une matrice (pseudo-code, 5min, matrix)?
+
+. . .
+
+```C
+arbre matrice_à_arbre(matrice)
+    arbre = creer_arbre(profondeur)
+    pour li de 0 à nb_lignes(matrice)
+        pour co de 0 à nb_colonnes(matrice)
+            noeud = position(li, co, arbre)
+            noeud.info = matrice[co][li]
+    retourne arbre
+```
+
+. . .
+
+## A partir d'une matrice (C, 5min, matrix)?
+
+. . .
+
+\footnotesize
+
+```C
+node *matrix_to_qt(int nb_li, int nb_co, int matrix[nb_li][nb_co], int depth)
+{
+    node *qt = qt_create(depth);
+    for (int li = 0; li < nd_li; ++li) {
+        for (int co = 0; co < nd_co; ++co) {
+            node *current = position(li, co, qt);
+            current->info = matrix[li][co];
+        }
+    }
+    return qt;
+}
+```
+
+
+# Remplir la matrice
+
+## A partir de l'arbre (pseudo-code, 3min, matrix)?
+
+. . .
+
+```C
+matrice arbre_à_matrice(arbre)
+    matrice = creer_matrice(nb_lignes(arbre), nb_colonnes(arbre))
+    pour li de 0 à nb_lignes(matrice)
+        pour co de 0 à nb_colonnes(matrice)
+            noeud = position(li, co, arbre)
+            matrice[co][li] = noeud.info
+    retourne matrice
+```
+
+. . .
+
+## A partir de l'arbre (C, 3min, matrix)?
+
+. . .
+
+\footnotesize
+
+```C
+void qt_to_matrix(node *qt, int nb_li, int nb_co, int matrix[nb_li][nb_co])
+    for (int li = 0; li < nd_li; ++li) {
+        for (int co = 0; co < nd_co; ++co) {
+            node *current = position(li, co, qt);
+            matrix[li][co] = current->info;
+        }
+    }
+```
+
+