diff --git a/README.md b/README.md
index c141b28bf35866ba9fe20f53b9f55bcd1abf4d57..de5d93d2df981993b91fadece3b8c5fc9785c2e4 100644
--- a/README.md
+++ b/README.md
@@ -2,11 +2,13 @@
 
 ## Semestre 2023 Printemps
 
-| jour  | heure         | type | supports / remarques                                                 |
-| ----- | ------------- | ---- | -------------------------------------------------------------------- |
-| 16/01 | 8h            | CM   | [Diapositives](cm/lifpf-cm1.pdf)                                     |
-|       | 9h45          | TD   | [Sujet](td/lifpf-td1-enonce.pdf) <br> Groupes B et E à 11h30         |
-| 23/01 | 8h            | CM   | [Diapositives](cm/lifpf-cm2.pdf), [Script démos](cm/cm2-demo.md)     |
-|       | 9h45 ou 11h30 | TP   |  [Sujet](tp/tp1.md) <br> Groupe de TP, horaire et salle sur [tomuss] |
+| jour  | heure         | type     | supports / remarques                                                      |
+| ----- | ------------- | -------- | ------------------------------------------------------------------------- |
+| 16/01 | 8h            | CM       | [Diapositives](cm/lifpf-cm1.pdf)                                          |
+|       | 9h45          | TD       | [Sujet](td/lifpf-td1-enonce.pdf) <br> Groupes B et E à 11h30              |
+| 23/01 | 8h            | CM       | [Diapositives](cm/lifpf-cm2.pdf), [Script démos](cm/cm2-demo.md)          |
+|       | 9h45 ou 11h30 | TP       | [Sujet](tp/tp1.md) <br> Groupe de TP, horaire et salle sur [tomuss]       |
+| 30/01 | 8h            | TD + QCM | [Sujet](td/lifpf-td2-enonce.pdf) <br> Groupes A et F en salle Nautibus C1 |
+|       | 9h45 ou 11h30 | TP       | [Sujet](tp/tp2.md) <br> Groupe de TP, horaire et salle sur [tomuss]       |
 
 [tomuss]: https://tomuss.univ-lyon1.fr
diff --git a/td/lifpf-td2-enonce.pdf b/td/lifpf-td2-enonce.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..34bae1a7d46f62950bf361c654f75d47a0ececdd
Binary files /dev/null and b/td/lifpf-td2-enonce.pdf differ
diff --git a/tp/tp1.md b/tp/tp1.md
index ad46fb3dd2bec6d19f636d1bba702b8bb869f88a..e57abda129d758684bf0c374cf403400a7de84e7 100644
--- a/tp/tp1.md
+++ b/tp/tp1.md
@@ -375,5 +375,3 @@ valeur de liste par cas avec un `match` et fabriquer une liste comme résultat.
 > complètes de la chanson _99 bottles of beer_. On séparera chaque paragraphe du
 > suivant avec "\n\n". Vérifier que la variable a la bonne valeur en l'affichant
 > avec `print_endline chanson_99_bottles;;` .
-
-## 6. Application calculatrice avec notation polonaise inversée
diff --git a/tp/tp1.ml b/tp/tp1.ml
new file mode 100644
index 0000000000000000000000000000000000000000..0c3d332dc983140b0acdb458d0bb5f2bf2a9d734
--- /dev/null
+++ b/tp/tp1.ml
@@ -0,0 +1,265 @@
+(**********************************************************************)
+(* 1.1 *)
+(3 * 2) + (5 / 2);;
+3.2 +. (7.4 /. 2.0);;
+"AC" ^ "/" ^ "DC";;
+true || (false && false);;
+
+(* && est prioritaire sur || *)
+(false && false) || true;;
+
+(* 3 < 2.0 provoque une erreur car 3 et 2.0 n'ont pas le même type *)
+
+(**********************************************************************)
+(* 1.2 *)
+
+(* float_of_int 3.0 provoque une erreur car 3.0 n'est pas un int *)
+float_of_int 3;;
+int_of_float;;
+int_of_float 3.5 + 2;;
+float_of_int (int_of_float 3.6 + 2) +. 1.5;;
+(not false) && false;;
+2 < 3 && "b" < "ab";;
+
+(**********************************************************************)
+(* 1.3 *)
+let x1 = 3.5 +. float_of_int 2 in
+x1 +. 3.0
+;;
+
+let x1 = 3.5 +. float_of_int 2 in
+let x2 = x1 *. x1 in
+x2 *. 2.0
+;;
+
+(**********************************************************************)
+(* 1.4 *)
+
+(if true then 3 else 5) > 4
+
+(**********************************************************************)
+(* 2 *)
+let f x = x + 1;;
+
+3 + f 3
+
+let discriminant a b c = (b *. b) -. (4.0 *. a *. c)
+
+(* Le type affiche par l'interpréteur est float -> float -> float -> float *)
+
+(**
+[discriminant a b c] calcule le discriminant d'un trinôme.
+Cette fonction est utile pour trouver les racines d'un trinôme.
+@param a le coefficient d'ordre 2    
+@param b le coefficient d'ordre 1
+@param c le coefficient d'ordre 0
+@return le discriminant
+*)
+let discriminant (a : float) (b : float) (c : float) : float =
+  (b *. b) -. (4.0 *. a *. c)
+;;
+
+discriminant;;
+discriminant 2.0 8.0 8.0
+
+(* Erreur de type sur l'utilisation de a dans la def suivante: *)
+(*
+   let discriminant (a:int) (b:float) (c:float): float =
+       b *. b -. 4.0 *. a *. c;;
+*)
+
+(**********************************************************************)
+
+(* 3.1 *)
+type couleur = Rouge | Jaune | Bleu;;
+
+(* La valeur Rouge est bien définie et reconnue comme étant de type couleur *)
+Rouge;;
+
+(* L'égalité est bien (pré)définie sur les couleurs *)
+Rouge = Rouge;;
+Rouge != Bleu;;
+
+(* De même il existe un ordre par défaut sur les couleurs *)
+Bleu > Jaune
+
+type couleur = Rouge | Jaune | Bleu | Violet | Orange | Vert
+
+type couleur =
+  | Rouge
+  | Jaune
+  | Bleu
+  | Violet
+  | Orange
+  | Vert
+  | RJB of int * int * int
+;;
+
+(* le type est "int * float" c'est-à-dire une paire d'int et de float *)
+3, 5.6;;
+
+(* c'est le triplet de type int * int * int *)
+3, 5, 6
+
+(**********************************************************************)
+(* 3.2 *)
+
+let nom_couleur c =
+  match c with
+  | Rouge -> "rouge"
+  | Jaune -> "jaune"
+  | Bleu -> "bleu"
+  | Vert -> "vert"
+  | Violet -> "violet"
+  | Orange -> "orange"
+  | _ -> "mélange"
+
+(*
+  let nom_couleur c =
+  match c with
+  | Rouge -> "rouge"
+  | Jaune -> "jaune"
+  | Bleu -> "bleu"
+  | Vert -> "vert"
+  | Violet -> "violet"
+  | Orange -> "orange";;
+ Erreur: this pattern-matching is not exhaustive.
+ Elle indique que le match ne couvre pas tous les cas possibles.
+*)
+
+(*
+  let nom_couleur c =
+  match c with
+  | _ -> "mélange"
+  | Rouge -> "rouge"
+  | Jaune -> "jaune"
+  | Bleu -> "bleu"
+  | Vert -> "vert"
+  | Violet -> "violet"
+  | Orange -> "orange";;
+ Erreur: this pattern-matching is not exhaustive.
+ Elle indique que le match ne couvre pas tous les cas possibles.
+*)
+
+(**
+Génère le paragraphe pour [n] bouteilles de bières.
+@param n le nombre de bouteilles de bièere sur le mur au début du paragraphe.
+@return le paragraphe
+*)
+let paragraphe_bottles (n : int) : string =
+  match n with
+  | 0 ->
+      "No more bottles of beer on the wall, no more bottles of beer.\n"
+      ^ "Go to the store and buy some more, 99 bottles of beer on the wall."
+  | 1 ->
+      "1 bottle of beer on the wall, 1 bottle of beer.\n"
+      ^ "Take one down and pass it around, no more bottles of beer on the wall."
+  | 2 ->
+      "2 bottles of beer on the wall, 2 bottles of beer.\n"
+      ^ "Take one down and pass it around, 1 bottle of beer on the wall."
+  | k ->
+      string_of_int k ^ " bottles of beer on the wall, " ^ string_of_int k
+      ^ " bottles of beer.\n" ^ "Take one down and pass it around, "
+      ^ string_of_int (k - 1)
+      ^ " bottles of beer on the wall."
+;;
+
+(* tests *)
+
+paragraphe_bottles 0;;
+paragraphe_bottles 1;;
+paragraphe_bottles 2;;
+paragraphe_bottles 7
+
+(**********************************************************************)
+(* 4 *)
+
+(**
+Renvoie la somme des n premiers entiers
+@param n le nombre d'entiers à sommer
+@return la somme
+*)
+let rec sum_n (n : int) : int = if n <= 0 then 0 else n + sum_n (n - 1)
+
+(**
+[factorielle n] calcule la factorielle des [n] premiers entiers
+@param n doit être supérieur ou égal à 1
+@return !n
+*)
+let rec factorielle n = if n <= 1 then 1 else n * factorielle (n - 1)
+
+(**********************************************************************)
+(* 5 *)
+(* Erreur car on essaie d'ajouter une string dans une int list
+   "2" :: 3 :: [];;
+*)
+
+(**
+Cette fonction calcule la longueur d'une liste de `string`.
+@param l la liste dont on veut la longueur
+@return la longueur de l
+*)
+let rec longueur (l : string list) : int =
+  match l with [] -> 0 | _ :: l2 -> 1 + longueur l2
+
+(* La valeur de l'élément de list n'est pas utilisée dans le deuxième cas du match.
+   On a donc pas besoin de la recupérer dans une variable et on met _ pour plus de clarté. *)
+
+(**
+    Cette fonction calcule la somme des éléments de la liste
+    @param l la liste dont on veut additionner les éléments
+    @return la somme des éléments de la liste
+*)
+let rec sum_f (l : float list) : float =
+  match l with [] -> 0.0 | x :: l2 -> x +. sum_f l2
+
+(**
+Cette fonction concatène les éléments de la liste en les séparant par [sep].
+@param l la liste dont on veut concaténer les éléments
+@param sep le séparateur
+@return les éléments de la liste [l] concaténé et séparés les uns des autres par [sep]
+*)
+let rec join_s (l : string list) (sep : string) : string =
+  match l with
+  | [] -> ""
+  | s :: [] -> s
+  | s :: l2 ->
+      (* Plus général que le cas précédent car l2 n'est pas forcément vide.
+         Comme le match applique le premier cas dans l'ordre du programme,
+         Les listes à 1 élément ne passent pas dans le 3eme cas du match. *)
+      s ^ sep ^ join_s l2 sep
+;;
+
+(* test *)
+join_s [ "a"; "b"; "c" ] "|"
+
+(**
+Cette fonction créée une liste des int de n à 0 inclus.
+@param n l'int de début de liste
+@return la liste des entiers décroissants    
+*)
+let rec liste_n_0 (n : int) : int list =
+  if n <= 0 then [ 0 ] else n :: liste_n_0 (n - 1)
+;;
+
+(* test *)
+liste_n_0 12
+
+(**
+[bottles_of_list [n1; n2; ...]] crée la liste des paragraphes de la chanson 99
+bottles of beer pour n1 puis n2 ... bouteilles.
+@param l la liste des nombres de bouteilles à transformer
+@return la liste des paragraphes correspondant aux int de l
+*)
+let rec bottles_of_list (l : int list) : string list =
+  match l with
+  | [] -> []
+  | n :: l2 -> paragraphe_bottles n :: bottles_of_list l2
+
+let chanson_99_bottles =
+  let nums = liste_n_0 99 in
+  let paragraphes = bottles_of_list nums in
+  join_s paragraphes "\n\n"
+;;
+
+print_endline chanson_99_bottles
diff --git a/tp/tp2.md b/tp/tp2.md
new file mode 100644
index 0000000000000000000000000000000000000000..a7ef1058401aa2d1c7216dee3e3a6b7ca503c920
--- /dev/null
+++ b/tp/tp2.md
@@ -0,0 +1,176 @@
+# LIFPF: TP2 récursion sur les listes
+
+Finir le [TP1][tp1.md] avant de commencer ce TP.
+
+Le reste du TP consiste à travailler la récursion sur les listes.
+
+Comme dans le TP précédent, on prendra soin:
+
+- de donner le type des arguments et le type de retour de chaque fonction;
+- d'écrire le commentaire de description de la fonction **avant** d'écrire la fonction.
+
+_Remarque:_ ajouter un commentaire au-dessus de la fonction n'est pas la même chose que d'écrire le commentaire avant d'écrire la fonction. N'oubliez pas: vous écrivez avant tout ce commentaire pour vous aider à écrire la fonction et ensuite seulement pour la documenter.
+
+On prendra également soin d'écrire des tests pour chaque fonction en écrivant des expressions du type `ma_fonction valeur1 valeur2 = valeur attendue;;` et en vérifiant avec l'interpréteur que ces tests s'évaluent à `true`.
+
+## 1. Concaténations de listes
+
+Dans cette partie on souhaite implémenter des fonctions qui vont concaténer des listes.
+
+### 1.1 Concaténation de 2 listes
+
+Implémenter la fonction `concatene` qui prend deux `int list` et renvoie la liste constituée des éléments de la première suivis des éléments de la seconde.
+
+Quelques exemples de tests (à compléter):
+
+```ocaml
+concatene [ 1; 2; 3 ] [ 4; 5; 6 ] = [ 1; 2; 3; 4; 5; 6 ];;
+concatene [] [ 4; 5; 6 ] = [ 4; 5; 6 ];;
+```
+
+### 1.2 Concaténation (applatissement) d'une liste de liste
+
+Implémenter la fonction `applatit` qui prend une `(int list) list` et renvoie la liste constituée de la concaténation des éléments de cette liste de liste. Cette fonction utilisera la fonction `concatene` codée auparavant.
+
+Quelques exemples de tests (à compléter):
+
+```ocaml
+applatit [ [ 1; 2 ]; [ 3; 4; 5 ]; []; [ 6 ] ] = [ 1; 2; 3; 4; 5; 6 ];;
+applatit [ [ 1 ] ] = [ 1 ];;
+```
+
+Faire une deuxième version `applatit2` ayant la même spécification que `applatit` mais qui n'utilise pas `concatene`.
+Tester cette fonction avec les mêmes tests que `applatit`.
+
+## 2. Retournement de liste
+
+On souhaite implémenter une fonction `renverse` qui renverse une liste.
+
+L'algorithme naïf pour faire une inversion de liste consiste à inverser la queue de liste et à concaténer la tête de liste à la fin de la queue renversée. Cet algorithme est cependant inefficace car il conduit à effectuer un nombre quadratique (en fonction de la taille de la liste initiale) d'ajouts en tête de liste à cause de l'opération de concaténation.
+
+On veut donc implémenter un algorithme plus efficace qui ne fait qu'un nombre linéaire d'ajout en tête de liste.
+Pour cela on code auparavant une version modifiée `renverse_ajoute` qui prend deux listes en argument et renvoie la première renversée concaténée à la seconde, par exemple
+
+```ocaml
+renverse_ajoute [ 1; 2; 3 ] [ 4; 5; 6 ] = [ 3; 2; 1; 4; 5; 6]
+```
+
+on peut remarquer les égalités suivantes sont vraies:
+
+```
+renverse_ajoute [ 1; 2; 3 ] [ 4; 5; 6 ]
+= renverse_ajoute [ 2; 3 ] [ 1; 4; 5; 6 ]
+= renverse_ajoute [ 3 ] [ 2; 1; 4; 5; 6 ]
+= renverse_ajoute [] [ 3; 2; 1; 4; 5; 6 ]
+```
+
+On peut aussi remarquer que `renverse` peut se coder très facilement à partir de `renverse_ajoute` puisque `renverse l = renverse_ajoute l []`.
+
+Coder `renverse` et définissant `renverse_ajoute`.
+Définir tout d'abord `renverse_ajoute` avant `renverse` et tester. Essayer ensuite d'intégrer la définition de `renverse_ajoute` à l'intégrieur de celle de renverse via un `let rec renverse_ajoute ... in ...`.
+
+## 3. Tri par insersion
+
+On code l'algorithme de tri par insersion qui consiste, pour chaque élément, à l'insérer à la bonne place dans le reste de la liste préalablement trié.
+
+### 3.1 Insersion dans une liste triée
+
+On commence donc par coder une fonction `insersion` qui insère un élément dans une liste triée.
+
+Quelques tests à compléter:
+
+```ocaml
+insertion 3 [ 1; 2; 4; 5 ] = [ 1; 2; 3; 4; 5 ];;
+insertion 3 [ 1; 2; 3; 4; 5 ] = [ 1; 2; 3; 3; 4; 5 ];;
+```
+
+### 3.2 Tri
+
+Coder la fonction `tri_insersion` en utilisant `insersion`.
+
+Quelques tests à compléter:
+
+```ocaml
+tri_insertion [ 1; 4; 2; 3 ] = [ 1; 2; 3; 4 ];;
+tri_insertion [ 1; 2; 3; 4 ] = [ 1; 2; 3; 4 ];;
+```
+
+## 4. Recherche dans une liste d'association
+
+On rappelle qu'une liste d'association est une liste de paires (clé, valeur). L'opération principale sur ces listes consiste à chercher la valeur associée à une clé si elle existe.
+
+Dans cette question on considèrera que les clé sont de type `int` et les valeurs de type `string`.
+
+### 4.1 Type résultat
+
+Comme la clé n'est pas forcément présente dans la liste, on commence par créer un type somme `resultat` avec un constructeur représentant l'absence de valeur et un constructeur représentant le cas où une valeur a été trouvée.
+Pour cela il faut essentiellement se poser la question des données éventuellement associées à chaque constructeur, ainsi que de leur type.
+
+### 4.2 Fonction de recherche
+
+Coder la fonction `cherche` qui va chercher une clé dans une liste d'association et renvoyer la valeur associée lorsqu'elle existe.
+
+Bien penser à écrire des tests pour cette fonction.
+
+## 5. Calculatrice en notation polonaise
+
+On souhaite implémenter une calculatrice en notation polonaise, c'est à dire calculant le résultat d'expressions dans laquelle les opérateurs sont placés avant leur arguments.
+On donne quelques exemples dans le tableau suivant:
+
+| Notation habituelle  | Notation polonaise |
+| -------------------- | ------------------ |
+| 3 \* 2               | \* 3 2             |
+| 7 / 3                | / 7 3              |
+| (7 / 3) + 5          | + / 7 3 5          |
+| (3\*2) - ((7/3) + 5) | - \* 3 2 + / 7 3 5 |
+
+On peut remarquer que cette notation permet de se passer de parenthèses.
+
+Avant d'évaluer ce type d'expressions il faut pouvoir les représenter. On va donc se munir d'un type pour représenter les opérateurs, d'un type représentant les éléments d'expression (c'est-à-dire un opérateur ou un nombre):
+
+```ocaml
+type binop = Plus | Moins | Mult | Div
+type elt_expr = Op of binop | Cst of int
+```
+
+De plus l'évaluation d'une expression peut mal se passer pour deux raisons: une division par zéro ou une expression mal construite.
+Par exemple `+ - 2 3` est mal construite car il manque un argument au `+`.
+On va donc créer un type pour représenter un résultat ou une erreur:
+
+```ocaml
+type resultat = Ok of int | ErrDivZero | ErrExpr
+```
+
+### 5.1 Évaluation des opérations
+
+Afin de simplifier le code de l'évaluateur, on commence par créer une fonction `eval_op` qui va évaluer le résultat d'un opérateur appliqué à des valeurs.
+Comme il faut prendre en compte les erreurs de division par zéro, on renverra un `resultat` et pas un `int`. De plus comme cette fonction sera utilisée avec des arguments qui peuvent eux-même être obtenu via l'évaluation de d'autres expressions, on va prendre en argument des `resultat` et pas simplement des `int`. Si un résultat est une erreur, on renverra cette erreur au lieu d'effectuer le calcul. Ainsi le type de `eval_op` devra être `binop -> resultat -> resultat -> resultat`.
+
+Quelques tests à compléter:
+
+```ocaml
+eval_op Plus (Ok 1) (Ok 2) = Ok 3;;
+eval_op Moins (Ok 2) (Ok 3) = Ok (-1);;
+eval_op Div (Ok 3) (Ok 0) = ErrDivZero;;
+eval_op Div (Ok 5) ErrExpr = ErrExpr;;
+```
+
+### 5.2 Évaluation d'une suite d'expression
+
+Une suite d'expressions est représentée par une liste d'éléments d'expression. On peut remarquer qu'une telle liste peut contenir plusieurs expressions, par exemple `+ 3 5 - 2 7`, est représentée par `[ Op Plus; Cst 3; Cst 5; Op Moins; Cst 2; Cst 7 ]` et correspond aux deux expressions écrites habituellement `3+5` et `2-7`.
+
+On va coder une fonction `eval_expr` qui va évaluer une suite d'expressions et renvoyer une liste de résultats. Pour y arriver on peut faire les remarques suivantes:
+
+- Il faut toujours évaluer le reste d'une suite d'expressions non vide.
+- L'évaluation d'une constante est cette constante mais sous forme de résultat
+- L'évaluation d'un opérateur binaire nécessite de récupérer le résultat des deux expressions suivantes, donc d'avoir déjà fait l'appel récursif pour évaluer le reste des expressions.
+- Si l'appel récursif produit une liste de résultats contenant zéro ou un élément, il n'est pas possible d'évaluer un opérateur binaire. C'est le cas où l'expression est mal construite.
+
+Quelques tests à compléter:
+
+```ocaml
+eval_expr [ Cst 3 ] = [ Ok 3 ];;
+eval_expr [ Op Mult; Cst 3; Cst 2 ] = [ Ok 6 ];;
+eval_expr [ Op Plus; Cst 3; Cst 5; Op Moins; Cst 2; Cst 7 ] = [ Ok 8; Ok (-5) ];;
+eval_expr [ Op Plus; Op Div; Cst 7; Cst 3 ] = [ ErrExpr ];;
+```