diff --git a/Bibliotheque.java b/Bibliotheque.java index 85bd0db..b39d079 100644 --- a/Bibliotheque.java +++ b/Bibliotheque.java @@ -1,4 +1,4 @@ -/* + /* * R5.A.04 - Qualité Algorithmique * TD Complexité cyclomatique - Partie Java * diff --git a/COMPTE_RENDU.md b/COMPTE_RENDU.md index 2c8903f..53b42a9 100644 --- a/COMPTE_RENDU.md +++ b/COMPTE_RENDU.md @@ -1,211 +1,259 @@ +Ugo Faye +Cléo Camille +Mathis Leclere + # Compte rendu – TD Complexité cyclomatique (R5.A.04) -## Partie 1 – Graphes de flot de contrôle +Formules utilisées : +- avec le graphe : **M = E − N + 2P** (E arêtes, N sommets, P = 1) +- en comptant : **M = décisions + 1** -Pour chaque fonction demandée dans l'énnoncé, on construit le graphe de flot de contrôle, puis on applique : +Classement selon la grille du cours : 1–10 simple, 11–20 modéré, 21–50 complexe, > 50 très risqué. -**M = E − N + 2P** (E = arêtes, N = nœuds, P = 1 pour une fonction) +--- -On vérifie ensuite le résultat avec la règle pratique **M = décisions + 1**. +# Partie C – `exercices.c` -### C2 – `moyenne` (C) +## __C1__ – `carre` +- complexite = 0 decisions + 1 = 1 + +> **Classement:** + +Simple + +## __C2__ – `moyenne` ![Graphe C2](Graphe/C2.jpg) -| Nœud | Contenu | -|---|---| -| 1 | `somme = 0 ; i = 0` | -| 2 | décision `i < n` | -| 3 | `somme += t[i]` | -| 4 | `i++` (arête de retour vers 2) | -| 5 | décision `n > 0` (ternaire) | -| 6 | `somme / n` | -| 7 | `0` | -| 8 | sortie (`return`) | +- complexite = 9 aretes − 8 sommets + 2 = 3 +- complexite = 2 decisions + 1 = 3 -- N = 8, E = 9, P = 1 -- **M = 9 − 8 + 2 × 1 = 3** -- Vérification : 1 `for` + 1 ternaire `?:` + 1 = 3 +> **Cas particuliers:** +- ternaire `n > 0 ? ... : 0` : compte pour 1 -### C4 – `nom_jour` (C) +> **Classement:** + +Simple + +## __C3__ – `est_bissextile` +- complexite = 2 decisions + 1 = 3 + +> **Cas particuliers:** +- `&&` et `||` sans `if` : 1 decision chacun (evaluation paresseuse) + +> **Classement:** + +Simple + +## __C4__ – `nom_jour` ![Graphe C4](Graphe/C4.jpg) -| Nœud | Contenu | -|---|---| -| 1 | `switch (jour)` | -| 2 à 8 | `case 1` à `case 7` : `return "lundi"` … `return "dimanche"` | -| 9 | `default` : `return "inconnu"` | -| 10 | sortie | +- complexite = 16 aretes − 10 sommets + 2 = 8 +- complexite = 7 decisions + 1 = 8 -- N = 10, E = 16 (8 arêtes du `switch` vers chaque branche + 8 arêtes des branches vers la sortie), P = 1 -- **M = 16 − 10 + 2 × 1 = 8** -- Vérification : 7 `case` + 1 = 8 (le `default` ne compte pas : c'est le « sinon » du `switch`) +> **Cas particuliers:** +- 7 `case` : 1 decision chacun +- `default` : ne compte pas -### J2 – `estDisponible` (Java) +> **Classement:** + +Simple + +## __C5__ – `compter_mots` +- complexite = 7 decisions + 1 = 8 + +> **Cas particuliers:** +- `while` : 1 +- `separateur = (... || ... || ... || ... || ...)` : 4 `||` sans `if` +- `if` + `else if` : 2 + +> **Classement:** + +Simple + +## __C6__ – `date_valide` +- complexite = 10 decisions + 1 = 11 + +> **Cas particuliers:** +- 1er `if` + 3 `||` : 4 +- `if (mois == 2)` + `if (est_bissextile)` : 2 +- `else if` + 3 `||` : 4 +- l'appel a `est_bissextile` ne compte pas (M est calcule par fonction) + +> **Classement:** + +Modéré + +--- + +# Partie Java – `Bibliotheque.java` + +## __J1__ – `nombreDeLivres` +- complexite = 0 decisions + 1 = 1 + +> **Classement:** + +Simple + +## __J2__ – `estDisponible` ![Graphe J2](Graphe/J2.png) -La méthode ne contient aucun `if`, mais les `&&` sont évalués paresseusement : chaque opérande est une décision (si `livre.emprunte` est vrai, on sort directement sans évaluer la suite). +- complexite = 5 aretes − 4 sommets + 2 = 3 +- complexite = 2 decisions + 1 = 3 -| Nœud | Contenu | -|---|---| -| 1 | décision `!livre.emprunte` | -| 2 | décision `!livre.reserve` | -| 3 | décision `!livre.abime` | -| 4 | sortie (`return`) | +> **Cas particuliers:** +- 2 `&&` sans `if` : 1 decision chacun -- N = 4, E = 5, P = 1 -- **M = 5 − 4 + 2 × 1 = 3** -- Vérification : 2 opérateurs `&&` + 1 = 3 +> **Classement:** -### J6 – `importer` (Java) +Simple + +## __J3__ – `peutEmprunter` +- complexite = 8 decisions + 1 = 9 + +> **Cas particuliers:** +- 4 `if` + 2 `else if` +- ternaire `a.etudiant ? 8 : 5` : +1 +- `l.reserve && !a.etudiant` : +1 + +> **Classement:** + +Simple + +## __J4__ – `calculerPenalite` +- complexite = 8 decisions + 1 = 9 + +> **Cas particuliers:** +- 4 `if` + 1 `else if` + 1 `for` +- `joursRetard <= 0 || nbLivres <= 0` : +1 +- ternaire `penalite > 50 ? 50 : penalite` : +1 +- `else` : ne compte pas + +> **Classement:** + +Simple + +## __J5__ – `rechercher` +- complexite = 10 decisions + 1 = 11 + +> **Cas particuliers:** +- 1 `for` + 5 `if` +- `genre != null && ...` : +1 +- `disponiblesSeulement && ...` : +1 +- `motCle == null || ...` : +1 +- `titre.contains(m) || auteur.contains(m)` : +1 +- `continue` : ne compte pas +- l'appel a `estDisponible` ne compte pas + +> **Classement:** + +Modéré + +## __J6__ – `importer` ![Graphe J6](Graphe/J6.png) -Les nœuds de décision sont en jaune, et la condition de la boucle `for` est en bleu. +- complexite = 21 aretes − 15 sommets + 2 = 8 +- complexite = 7 decisions + 1 = 8 -| Nœud | Contenu | -|---|---| -| 1 | `importes = 0` | -| 2 | décision `for` : reste-t-il une ligne ? | -| 3 | décision `ligne.isBlank()` | -| 4 | décision `ligne.startsWith("#")` (le `\|\|`) | -| 5 | `continue` | -| 6 | `champs = ligne.split(";")` | -| 7 | décision `champs.length != 4` | -| 8 | `System.err.println(...)` + `continue` | -| 9 | `try` : `annee = Integer.parseInt(...)` | -| 10 | décision `annee > 0` | -| 11 | décision `annee <= 2026` (le `&&`) | -| 12 | `livres.add(...)` + `importes++` | -| 13 | décision `catch` : `NumberFormatException` levée ? | -| 14 | `System.err.println("Année invalide ...")` | -| 15 | sortie (`return importes`) | +> **Cas particuliers:** +- 2 : `for` +- 3/4 : `if` + `||` +- 7 : `if` +- 10/11 : `if` + `&&` +- 13 : `catch` +- `continue` : ne compte pas -Arêtes : 1→2, 2→3, 2→15, 3→4, 3→5, 4→5, 4→6, 5→2, 6→7, 7→8, 7→9, 8→2, 9→10, 10→11, 10→13, 11→12, 11→13, 12→13, 13→2, 13→14, 14→2. +> **Classement:** -- N = 15, E = 21, P = 1 -- **M = 21 − 15 + 2 × 1 = 8** -- Vérification : 1 `for` + 1 `if` + 1 `||` + 1 `if` + 1 `if` + 1 `&&` + 1 `catch` = 7 décisions, + 1 = 8 +Simple -> Cas particuliers : le `||` et le `&&` comptent chacun comme une décision (évaluation paresseuse), et le `catch` aussi, puisqu'il crée un second chemin de sortie du `try`. Les `continue` ne comptent pas : ils renvoient simplement vers la condition de la boucle. +--- -### P2 – `signe` (Python) +# Partie Python – `exercices.py` + +## __P1__ – `celsius_vers_fahrenheit` +- complexite = 0 decisions + 1 = 1 + +> **Classement:** + +Simple + +## __P2__ – `signe` ![Graphe P2](Graphe/P2.jpg) -| Nœud | Contenu | -|---|---| -| 1 | décision `x > 0` | -| 2 | `return 1` | -| 3 | décision `x < 0` (`elif`) | -| 4 | `return -1` | -| 5 | `return 0` | -| 6 | sortie | +- complexite = 7 aretes − 6 sommets + 2 = 3 +- complexite = 2 decisions + 1 = 3 -- N = 6, E = 7, P = 1 -- **M = 7 − 6 + 2 × 1 = 3** -- Vérification : 1 `if` + 1 `elif` + 1 = 3 +> **Cas particuliers:** +- `if` + `elif` -### P3 – `notes_valides` (Python) +> **Classement:** + +Simple + +## __P3__ – `notes_valides` ![Graphe P3](Graphe/P3.jpg) -La compréhension de liste cache une boucle et un filtre : elle se lit comme un `for` contenant un `if`. +- complexite = 6 aretes − 5 sommets + 2 = 3 +- complexite = 2 decisions + 1 = 3 -| Nœud | Contenu | -|---|---| -| 1 | initialisation de la liste résultat | -| 2 | décision : reste-t-il un élément `n` dans `notes` ? | -| 3 | décision `0 <= n <= 20` | -| 4 | ajout de `n` au résultat (retour vers 2) | -| 5 | sortie (`return`) | +> **Cas particuliers:** +- comprehension de liste = 1 `for` + 1 `if` +- `0 <= n <= 20` : comparaison chainee comptee comme 1 seule condition -- N = 5, E = 6, P = 1 -- **M = 6 − 5 + 2 × 1 = 3** -- Vérification : 1 `for` + 1 `if` (dans la compréhension) + 1 = 3 +> **Classement:** -> Remarque : `0 <= n <= 20` est une comparaison chaînée, équivalente à `0 <= n and n <= 20`. Si on compte ce `and` implicite comme une décision, on obtient M = 4. Nous l'avons compté comme une seule condition, comme le font la plupart des outils (aucun opérateur `and` n'apparaît dans le code). +Simple -### Récapitulatif +## __P4__ – `mention` +- complexite = 2 decisions + 1 = 3 -| Fonction | N | E | M | Classe | -|---|---|---|---|---| -| C2 `moyenne` | 8 | 9 | 3 | Simple | -| C4 `nom_jour` | 10 | 16 | 8 | Simple | -| J2 `estDisponible` | 4 | 5 | 3 | Simple | -| J6 `importer` | 15 | 21 | 8 | Simple | -| P2 `signe` | 6 | 7 | 3 | Simple | -| P3 `notes_valides` | 5 | 6 | 3 | Simple | +> **Cas particuliers:** +- le dictionnaire remplace une cascade de `if/elif` (qui aurait donne M = 5) -## Partie 2 – Comptage des décisions (M = décisions + 1) +> **Classement:** -### C (`exercices.c`) +Simple -| Fonction | Décisions | M | Classe | -|---|---|---|---| -| C1 `carre` | aucune | **1** | Simple | -| C2 `moyenne` | 1 `for`, 1 ternaire `?:` | **3** | Simple | -| C3 `est_bissextile` | 1 `&&`, 1 `\|\|` | **3** | Simple | -| C4 `nom_jour` | 7 `case` | **8** | Simple | -| C5 `compter_mots` | 1 `while`, 4 `\|\|`, 1 `if`, 1 `else if` | **8** | Simple | -| C6 `date_valide` | 4 `if`/`else if`, 6 `\|\|` | **11** | Modéré | +## __P5__ – `analyser_mot_de_passe` +- complexite = 15 decisions + 1 = 16 -Détail de C3 : il n'y a aucun `if`, mais `&&` et `||` sont évalués paresseusement : chacun crée un branchement (si `annee % 4 == 0` est faux, `annee % 100 != 0` n'est pas évalué). 2 décisions + 1 = **3**, ce qui correspond au graphe vu en cours (N = 4, E = 5). +> **Cas particuliers:** +- 2 `for`, 8 `if`, 2 `elif` +- `majuscule and minuscule` : +1 +- `login and login.lower() in ...` : +1 +- ternaire `"moyen" if ... else "faible"` : +1 +- `else` et `break` : ne comptent pas -Détail de C4 : le `default` ne compte pas (c'est le « sinon » du `switch`) et les `return` non plus. 7 + 1 = **8**, comme avec le graphe (16 − 10 + 2). +> **Classement:** -Détail de C5 : +Modéré -| Ligne | Décision | +1 | +--- + +# Récapitulatif + +| Fonction | M | Classement | |---|---|---| -| `while (texte[i] != '\0')` | `while` | 1 | -| `c == ' ' \|\| c == '\t' \|\| c == '\n' \|\| c == ',' \|\| c == '.'` | 4 `\|\|` | 5 | -| `if (separateur)` | `if` | 6 | -| `else if (!dans_mot)` | `else if` | 7 | - -**M = 7 + 1 = 8.** Ici aussi les `||` comptent sans `if` : ils sont dans une affectation (`int separateur = ...`), mais restent des branchements. - -Détail de C6 : - -| Ligne | Décision | +1 | -|---|---|---| -| `if (annee < 1 \|\| mois < 1 \|\| mois > 12 \|\| jour < 1)` | `if` + 3 `\|\|` | 4 | -| `if (mois == 2)` | `if` | 5 | -| `if (est_bissextile(annee))` | `if` | 6 | -| `else if (mois == 4 \|\| mois == 6 \|\| mois == 9 \|\| mois == 11)` | `else if` + 3 `\|\|` | 10 | - -**M = 10 + 1 = 11**, soit juste au-dessus du seuil de 10 (McCabe, PMD) : c'est la fonction C la plus complexe. L'appel à `est_bissextile` ne compte pas : ses décisions appartiennent à `est_bissextile` (M calculé par fonction). - -### Python (`exercices.py`) - -| Fonction | Décisions | M | Classe | -|---|---|---|---| -| P1 `celsius_vers_fahrenheit` | aucune | **1** | Simple | -| P2 `signe` | 1 `if`, 1 `elif` | **3** | Simple | -| P3 `notes_valides` | 1 `for`, 1 `if` (dans la compréhension) | **3** | Simple | -| P4 `mention` | 1 `for`, 1 `if` | **3** | Simple | -| P5 `analyser_mot_de_passe` | 2 `for`, 8 `if`, 2 `elif`, 2 `and`, 1 ternaire | **16** | Modéré | - -Détail de P5 : - -| Ligne | Décision | +1 | -|---|---|---| -| `if len(mdp) < 12` | `if` | 1 | -| `for c in mdp` | `for` | 2 | -| `if c.isupper()` | `if` | 3 | -| `elif c.islower()` | `elif` | 4 | -| `elif c.isdigit()` | `elif` | 5 | -| `if not (majuscule and minuscule)` | `if` + `and` | 7 | -| `if not chiffre` | `if` | 8 | -| `if not special` | `if` | 9 | -| `if login and login.lower() in mdp.lower()` | `if` + `and` | 11 | -| `for i in range(len(mdp) - 2)` | `for` | 12 | -| `if mdp[i] == mdp[i + 1] == mdp[i + 2]` | `if` | 13 | -| `if not problemes` | `if` | 14 | -| `"moyen" if len(problemes) <= 2 else "faible"` | ternaire | 15 | - -**M = 15 + 1 = 16.** Le `else` final de la cascade `if/elif/else` et le `break` ne comptent pas. Comme pour P3, la comparaison chaînée `mdp[i] == mdp[i + 1] == mdp[i + 2]` est comptée comme une seule condition. - -P4 a le même M que la version refactorisée de `mention` vue en cours : le dictionnaire remplace la cascade de `if/elif` (qui aurait donné M = 5). +| C1 `carre` | 1 | Simple | +| C2 `moyenne` | 3 | Simple | +| C3 `est_bissextile` | 3 | Simple | +| C4 `nom_jour` | 8 | Simple | +| C5 `compter_mots` | 8 | Simple | +| C6 `date_valide` | 11 | Modéré | +| J1 `nombreDeLivres` | 1 | Simple | +| J2 `estDisponible` | 3 | Simple | +| J3 `peutEmprunter` | 9 | Simple | +| J4 `calculerPenalite` | 9 | Simple | +| J5 `rechercher` | 11 | Modéré | +| J6 `importer` | 8 | Simple | +| P1 `celsius_vers_fahrenheit` | 1 | Simple | +| P2 `signe` | 3 | Simple | +| P3 `notes_valides` | 3 | Simple | +| P4 `mention` | 3 | Simple | +| P5 `analyser_mot_de_passe` | 16 | Modéré | diff --git a/CptDécisionsC+classementcomplexitéC.txt b/CptDécisionsC+classementcomplexitéC.txt deleted file mode 100644 index 0131bed..0000000 --- a/CptDécisionsC+classementcomplexitéC.txt +++ /dev/null @@ -1,13 +0,0 @@ -Compter les décisions de chacune des fonctions : M = décisions + 1 - -C1 : 0 décisions, M = 1 -C2 : 2 décisions : for et opérateur ?, M = 3 -C3 : 2 décisions : && et ||, M = 3 -C4 : 7 décisions : 7 cases (défault ne comptant pas), M = 8 -C5 : 7 décisions : 5 conditions dans separateur + 2, M = 8 -C6 : 10 décisions : premier if + 3 ||, second et troisieme if, else if + 3 ||, M = 11 - -classement par complexité : -C1, C2, C3 : simple -C4, C5 : moyen -C6 : complexe \ No newline at end of file diff --git a/Graphe/J6.png b/Graphe/J6.png new file mode 100644 index 0000000..8af34db Binary files /dev/null and b/Graphe/J6.png differ