diff --git a/src/main/java/fr/univamu/progav/td2/ASimpleLoop.java b/src/main/java/fr/univamu/progav/td2/ASimpleLoop.java new file mode 100644 index 0000000000000000000000000000000000000000..45593bfe0484086068bc53f2d543683829c90b36 --- /dev/null +++ b/src/main/java/fr/univamu/progav/td2/ASimpleLoop.java @@ -0,0 +1,13 @@ +package fr.univamu.progav.td2; + +public class ASimpleLoop { + + public static double nonTerminatingSum(int n) { + double sum = 0; + for (double i = 0; i != n; i = i + 0.1) { + sum = sum + i ; + } + return sum; + } + +} diff --git a/src/main/java/fr/univamu/progav/td2/GuessingGame.java b/src/main/java/fr/univamu/progav/td2/GuessingGame.java new file mode 100644 index 0000000000000000000000000000000000000000..0d75eb86cd450def04cdc3a2a8e953584050adc9 --- /dev/null +++ b/src/main/java/fr/univamu/progav/td2/GuessingGame.java @@ -0,0 +1,41 @@ +package fr.univamu.progav.td2; + +public class GuessingGame { + + public static int solve(int mystery) { + return new GuessingGame(mystery).nbAttempts(); + } + + public static final int LOWER_BOUND = 1; + public static final int UPPER_BOUND = 1000; + + private final int mystery; + private int attempts = 0; + + public GuessingGame(int mystery) { + this.mystery = mystery; + guess(LOWER_BOUND, UPPER_BOUND); + } + + private int nbAttempts() { + return attempts; + } + + private void guess(int lowerBound, int upperBound) { + this.attempts++; + if (lowerBound == upperBound) { + return; + } + int middle = (upperBound + lowerBound) / 2; + if (middle == mystery) { + return; + } + if (middle < mystery) { + guess(middle,upperBound); + } else { + guess(lowerBound,middle-1); + } + + } + +} diff --git a/src/main/java/fr/univamu/progav/td2/People.java b/src/main/java/fr/univamu/progav/td2/People.java new file mode 100644 index 0000000000000000000000000000000000000000..1873654d712f2e454edae98dbd5f84aed45a08a4 --- /dev/null +++ b/src/main/java/fr/univamu/progav/td2/People.java @@ -0,0 +1,44 @@ +package fr.univamu.progav.td2; + +import java.util.List; + +public class People { + + private final String name; + private final int age; + + public People(String name, int age) { + this.name = name; + this.age = age; + } + + public String getName() { + return name; + } + + public int getAge() { + return age; + } + + /** Decides whether the list contains a 25-year-old "Charlie" + * @param peopleList + * @return true if a 25-year-old charlie is in the list + */ + public static int whereIsCharlie(List<People> peopleList) { + People charlie = new People("Charlie", 25); + int count = 0; + for (People p : peopleList) { + if (p == charlie) { + return count; + } + count++; + } + return -1; // Charlie is not here. + } + + + @Override + public String toString() { + return this.name; + } +} diff --git a/src/main/java/fr/univamu/progav/td2/Ratio.java b/src/main/java/fr/univamu/progav/td2/Ratio.java new file mode 100644 index 0000000000000000000000000000000000000000..cde58322c4da073b86a09e434a2e6874d55e5b7b --- /dev/null +++ b/src/main/java/fr/univamu/progav/td2/Ratio.java @@ -0,0 +1,54 @@ +package fr.univamu.progav.td2; + +public class Ratio { + + private final int num; + private final int denom; + + private Ratio(int num, int denom) { + this.num = num; + this.denom = denom; + } + + /** Builds a ratio, given its numerator and denominator + * @param num the numerator of the ratio + * @param denom the denominator of the ratio + * @return a reduced ratio equals to num/denom, represented in reduced form. + */ + public static Ratio of(int num, int denom) { + int d = gcd(denom, num); + return new Ratio(num / d, denom /d ); + } + + // UnsafeOf is used in test, to build a ratio without reducing it, + // and must be used with already reduced numerator and denominator. + protected static Ratio unsafeOf(int num, int denom) { + return new Ratio(num, denom); + } + + // Euclid's algorithm + public static int gcd(int a, int b) { + return a == 0 || b == 0? a + b: + gcd(b, a % b); + } + + + public Ratio plus(Ratio r) { + return of(this.num * r.denom + this.denom * r.num, this.denom * r.denom); + } + + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof Ratio r)) { + return false; + } + return r.num == this.num && r.denom == this.denom; + } + + @Override + public String toString() { + double value = this.num / (double) this.denom; + return String.valueOf(value); + } +} diff --git a/src/main/java/fr/univamu/progav/td2/TD2.md b/src/main/java/fr/univamu/progav/td2/TD2.md new file mode 100644 index 0000000000000000000000000000000000000000..2e59ad9204820d0990425302b44c86a6181b5ddc --- /dev/null +++ b/src/main/java/fr/univamu/progav/td2/TD2.md @@ -0,0 +1,109 @@ +Introduction +============ + +Dans les exercices suivants, chaque petit programme comporte une +erreur, qui +apparait lors du test. L'objectif est d'apprendre à utiliser le débugger +pour Java d'IntelliJ pour trouver la cause de l'erreur, et la corriger. + +La tâche Gradle permet de lancer les tests, mais on préfèrera cette fois +lancer uniquement le test de l'exercice en cours. Pour cela, ouvrir le +fichier contenant le test, trouver la méthode de test, et dans la marge +`clic gauche` sur l'icône de lancement , +et choisir l'option *debug* . + +Pour arrêter le débugger à une instruction du programme, il faut placer des +*breakpoints* . Il suffit +pour cela d'un `clic gauche` dans la marge du programme, sur le numéro de +ligne. Un `clic droit` permet de le paramétrer si nécessaire, et un deuxième +`clic gauche` le fait disparaître. + +Lorsque le programme s'exécute, il s'interrompt automatiquement aux +breakpoints, et affiche l'état de la pile. + + + +Dans la partie gauche se trouve la liste des *frames*, c'est-à-dire des +niveaux ou couches de la pile, la plus récente en haut. Attention, lancer un +test demande un grand nombre d'appels de méthode avant d'arriver à la +méthode de test, donc la plupart des *frames* ne sont pas pertinents, seuls +les frames les plus hauts, pour lesquels on reconnaît les méthodes du projet, +nous intéressent. + +La partie droite affiche le contenu du frame courant, en commençant par la +valeur de `this` (si la méthode n'est pas statique), puis toutes les +variables et leurs valeurs. Les tableaux et les objets peuvent être +inspectés en dépliant les arborescences. + +La barre du haut contient les actions réalisables, dont voici les plus +importantes. + + relance le test depuis le +début, toujours en mode *debug*. + + arrête l'évaluation du +programme immédiatement, et ferme le débuggeur. + + reprend l'évaluation, +jusqu'au prochain *breakpoint* ou la fin normale de l'exécution du programme. + + passe à l'instruction +suivante, en ignorant tout appel de méthodes dans l'instruction en cours. + + passe à la première +instruction d'une méthode appelée dans l'instruction en cours. En cas de +choix entre plusieurs méthodes (ou constructeurs), il faut cliquer sur celle +qui nous intéresse. Il est conseillé de ne pas entrer dans les méthodes ne +faisant pas partie du projet. + + reprend l'exécution jusqu'à +terminer la méthode actuellement en cours, et s'interrompt à l'instruction +ayant appelé la méthode. + +Ces actions permettent d'exécuter le programme pas-à-pas, plus ou moins +finement, en inspectant les valeurs des variables. + +Exercice 1 +========== + +Dans la classe `People`, la méthode `whereIsCharlie` devrait retourner la +position de Charlie, 25 ans, dans la liste. Utiliser le test et le débuggeur +pour trouver l'erreur. + +Exercice 2 +========== + +Dans la classe `ASimpleLoop`, la méthode `nonTerminatingSum` fait un calcul, +qui ne termine pas. Utiliser le test et le débuggeur, pour comprendre +pourquoi elle ne termine pas. Corriger ensuite la méthode pour que le calcul +s'arrête, + +Exercice 3 +========== + +Dans la classe `Ratio`, représentant les nombres rationnels, l'addition ne +semble pas produire le bon résultat. Pourtant, nous avons pris soin de bien +réduire les fractions en calculant le plus grand commun diviseur avec +l'algorithme d'Euclide, et de forcer cette réduction dans la méthode +statique `of` que nous utilisons pour construire les fractions. Pour être +sûr de bien tester, nous avons aussi ajouter une méthode `unsafeOf`, qui +ignore l'étape de réduction : ainsi les valeurs attendues dans les tests +sont certainement bien définies (nous les avons réduites nous-mêmes). Enfin, +la méthode de test d'égalité profite de la forme réduite : il suffit de +tester l'égalité des numérateurs et des dénominateurs. Utiliser la méthode +de test et le débuggeur pour trouver l'erreur et la corriger. + + +Exercice 4 +========== + +La classe `GuessingGame` reprend la stratégie optimale pour le jeu +consistant à trouver un entier d'un intervalle (par exemple entre 1 et 1000), +en tentant des valeurs, et en obtenant pour chaque mauvaise réponse une +indication "plus petit" ou "plus grand". Bien sûr, il s'agit de faire une +recherche dichotomique. Ici, une erreur a été faite, et la dichotomie ne +termine pas toujours. Il va d'abord falloir trouver une valeur mystère pour +laquelle l'algorithme boucle infiniment (provoquant un dépassement de pile). +Ensuite, en utilisant le débuggeur et en partant avec cette valeur, il sera +possible de repérer la raison pour laquelle l'algorithme boucle ainsi. +Utiliser donc une fois de plus le débugger pour trouver et corriger l'erreur. \ No newline at end of file diff --git a/src/main/resources/breakpoint.png b/src/main/resources/breakpoint.png new file mode 100644 index 0000000000000000000000000000000000000000..835ccd8307666d7a7abc325bdbd435ddac2fc48d Binary files /dev/null and b/src/main/resources/breakpoint.png differ diff --git a/src/main/resources/debug-panel.png b/src/main/resources/debug-panel.png new file mode 100644 index 0000000000000000000000000000000000000000..59d6c03d8b471d96f531c1c87e9d73d3e35cb634 Binary files /dev/null and b/src/main/resources/debug-panel.png differ diff --git a/src/main/resources/debug.png b/src/main/resources/debug.png new file mode 100644 index 0000000000000000000000000000000000000000..0e410347746d30c95c66beab6edc26803d774ad9 Binary files /dev/null and b/src/main/resources/debug.png differ diff --git a/src/main/resources/mute-breakpoint.png b/src/main/resources/mute-breakpoint.png new file mode 100644 index 0000000000000000000000000000000000000000..f7a3efb2366dff36520c44792abf20d6b4d1d5f9 Binary files /dev/null and b/src/main/resources/mute-breakpoint.png differ diff --git a/src/main/resources/rerun-auto.png b/src/main/resources/rerun-auto.png new file mode 100644 index 0000000000000000000000000000000000000000..8262d20240f06795c099f4ecc8d80ca9d7354562 Binary files /dev/null and b/src/main/resources/rerun-auto.png differ diff --git a/src/main/resources/rerun-failed.png b/src/main/resources/rerun-failed.png new file mode 100644 index 0000000000000000000000000000000000000000..9dd194a24d4f368872f04ccc54ece354a5ee58d6 Binary files /dev/null and b/src/main/resources/rerun-failed.png differ diff --git a/src/main/resources/rerun.png b/src/main/resources/rerun.png new file mode 100644 index 0000000000000000000000000000000000000000..1acc05554535be535d91515c4ee4b952e849b017 Binary files /dev/null and b/src/main/resources/rerun.png differ diff --git a/src/main/resources/resume.png b/src/main/resources/resume.png new file mode 100644 index 0000000000000000000000000000000000000000..ed843ff60e8d1aa8f4aa6a79d3d48d8ef2c7b1e3 Binary files /dev/null and b/src/main/resources/resume.png differ diff --git a/src/main/resources/run.png b/src/main/resources/run.png new file mode 100644 index 0000000000000000000000000000000000000000..a466a16d2078b9f5c686cd283685d0d2cff40f6c Binary files /dev/null and b/src/main/resources/run.png differ diff --git a/src/main/resources/step-into.png b/src/main/resources/step-into.png new file mode 100644 index 0000000000000000000000000000000000000000..362b040202578116a15c0a3af8d7c441f81ed4fe Binary files /dev/null and b/src/main/resources/step-into.png differ diff --git a/src/main/resources/step-out.png b/src/main/resources/step-out.png new file mode 100644 index 0000000000000000000000000000000000000000..cd340d1d08c01a6d416a13d87cb0bee23377dc90 Binary files /dev/null and b/src/main/resources/step-out.png differ diff --git a/src/main/resources/step-over.png b/src/main/resources/step-over.png new file mode 100644 index 0000000000000000000000000000000000000000..a179c06db4bcfd1a01c4d497c6818b81269abfc0 Binary files /dev/null and b/src/main/resources/step-over.png differ diff --git a/src/main/resources/stop.png b/src/main/resources/stop.png new file mode 100644 index 0000000000000000000000000000000000000000..a4e7520311c536fe68b5f1451137f314222ace09 Binary files /dev/null and b/src/main/resources/stop.png differ diff --git a/src/main/resources/view-breakpoint.png b/src/main/resources/view-breakpoint.png new file mode 100644 index 0000000000000000000000000000000000000000..fd8542e5d4c65bec5f3d21c33cb2b37055e831ad Binary files /dev/null and b/src/main/resources/view-breakpoint.png differ diff --git a/src/test/java/fr/univamu/progav/td2/ASimpleLoopTest.java b/src/test/java/fr/univamu/progav/td2/ASimpleLoopTest.java new file mode 100644 index 0000000000000000000000000000000000000000..a534b433fe5ac1529a588375f0aae6d7755ebbad --- /dev/null +++ b/src/test/java/fr/univamu/progav/td2/ASimpleLoopTest.java @@ -0,0 +1,15 @@ +package fr.univamu.progav.td2; + +import org.junit.jupiter.api.Test; + +import static fr.univamu.progav.td2.ASimpleLoop.nonTerminatingSum; +import static org.junit.jupiter.api.Assertions.*; + +class ASimpleLoopTest { + + @Test + void nonTerminatingSumTest() { + double result = nonTerminatingSum(10); + assertEquals(505,result); + } +} \ No newline at end of file diff --git a/src/test/java/fr/univamu/progav/td2/GuessingGameTest.java b/src/test/java/fr/univamu/progav/td2/GuessingGameTest.java new file mode 100644 index 0000000000000000000000000000000000000000..4fc05a27d18b1d97ce02ff371ca9423b765698d7 --- /dev/null +++ b/src/test/java/fr/univamu/progav/td2/GuessingGameTest.java @@ -0,0 +1,32 @@ +package fr.univamu.progav.td2; + +import org.junit.jupiter.api.Test; + +import static fr.univamu.progav.td2.GuessingGame.LOWER_BOUND; +import static fr.univamu.progav.td2.GuessingGame.UPPER_BOUND; +import static org.junit.jupiter.api.Assertions.*; + +class GuessingGameTest { + + @Test + void solve() { + int max_allowed = // a bound on the minimum number of guesses by the best strategy. + (int) Math.ceil(log2(GuessingGame.UPPER_BOUND - GuessingGame.LOWER_BOUND + 2,1e-1)); + for (int i = GuessingGame.LOWER_BOUND; i <= GuessingGame.UPPER_BOUND; i++) { + int r = GuessingGame.solve(i); + assertTrue(r <= max_allowed, + "Guessing " + i + " in " + r + "/" + max_allowed + " attempts"); + } + } + + // compute log(x) in base 2, with given precision, for instance log2(x,1e-6) + // is at most 0.000001 away from the exact value. + private static double log2(double x, double precision) { + return + (x >= 2)? 1 + log2(x/2, precision): + (x == 1)? 0: + (x < 1)? - log2(1/x, precision): + (precision > 1) ? 0: + 0.5 * log2(x * x, precision*2); + } +} \ No newline at end of file diff --git a/src/test/java/fr/univamu/progav/td2/PeopleTest.java b/src/test/java/fr/univamu/progav/td2/PeopleTest.java new file mode 100644 index 0000000000000000000000000000000000000000..79b0ce8b14b629d51f691100d701b90ea25c4ae4 --- /dev/null +++ b/src/test/java/fr/univamu/progav/td2/PeopleTest.java @@ -0,0 +1,36 @@ +package fr.univamu.progav.td2; + +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static fr.univamu.progav.td2.People.whereIsCharlie; +import static org.junit.jupiter.api.Assertions.*; + +class PeopleTest { + + private final List<People> peopleList = + List.of( + new People("Alfa", 12), + new People("Bravo", 18), + new People("Charlie", 25), + new People("Delta", 36), + new People("Echo", 8), + new People("Foxtrot", 11), + new People("Golf", 21), + new People("Hotel", 53), + new People("India", 42), + new People("Juliett", 28) + ); + + + @Test + void whereIsCharlieTest() { + int charliePosition = whereIsCharlie(peopleList); + assertEquals(2, charliePosition); + List<People> noCharlie = + peopleList.stream().filter(p -> !p.getName().equals("Charlie")).toList(); + int noCharliePosition = whereIsCharlie(noCharlie); + assertEquals(-1, noCharliePosition); + } +} \ No newline at end of file diff --git a/src/test/java/fr/univamu/progav/td2/RatioTest.java b/src/test/java/fr/univamu/progav/td2/RatioTest.java new file mode 100644 index 0000000000000000000000000000000000000000..c4ac7a3c08c8266fb53aa73f84116777ba5e3042 --- /dev/null +++ b/src/test/java/fr/univamu/progav/td2/RatioTest.java @@ -0,0 +1,21 @@ +package fr.univamu.progav.td2; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class RatioTest { + + + @Test + void plusTest() { + assertEquals(Ratio.unsafeOf(2,1),Ratio.of(1,1).plus(Ratio.of(1,1))); + assertEquals(Ratio.unsafeOf(5,6),Ratio.of(1,3).plus(Ratio.of(1,2))); + assertEquals(Ratio.unsafeOf(1,6),Ratio.of(-1,3).plus(Ratio.of(1,2))); + assertEquals(Ratio.unsafeOf(-1,6),Ratio.of(1,3).plus(Ratio.of(-1,2))); + assertEquals(Ratio.unsafeOf(-5,6),Ratio.of(-1,3).plus(Ratio.of(-1,2))); + assertEquals(Ratio.unsafeOf(-5,6),Ratio.of(1,-3).plus(Ratio.of(1,-2))); + assertEquals(Ratio.unsafeOf(5,6),Ratio.of(-1,-3).plus(Ratio.of(1,2))); + } + +} \ No newline at end of file