From 2c3cddca21112e2124e56dd37bf6761a1ef52a33 Mon Sep 17 00:00:00 2001 From: POURCELOT Matthis <matthis.pourcelot6@etu.univ-lorraine.fr> Date: Sun, 9 Jun 2024 22:32:56 +0200 Subject: [PATCH] Rendu SAE202 --- code/instances/b.txt | 10 -- code/instances/hh.txt | 8 -- code/instances/test.txt | 8 ++ code/instances/tests | 6 - code/rendu/RapportSAE202.txt | 69 +++++++++ code/rendu/UML_GrapheOrientePondere.webp | Bin 0 -> 21118 bytes "code/rendu/UML_R\303\251sultat.webp" | Bin 0 -> 9582 bytes code/src/GrapheOrientePondere.js | 166 +++++++++++++++++----- code/src/GrapheOrientePondere.ts | 158 +++++++++++++++------ code/src/index.js | 76 +++++++--- code/src/index.ts | 83 +++++++---- code/test_graphe_liste.txt | 4 + code/test_graphe_matrice.txt | 4 + code/tests/dijkstra.test.js | 33 +++++ code/tests/grapheOrientePondere.test.js | 172 +++++++++++++++++++++++ code/tests/grapheOrientePondere.test.ts | 130 +++++++++++++---- 16 files changed, 755 insertions(+), 172 deletions(-) delete mode 100644 code/instances/b.txt delete mode 100644 code/instances/hh.txt create mode 100644 code/instances/test.txt delete mode 100644 code/instances/tests create mode 100644 code/rendu/RapportSAE202.txt create mode 100644 code/rendu/UML_GrapheOrientePondere.webp create mode 100644 "code/rendu/UML_R\303\251sultat.webp" create mode 100644 code/test_graphe_liste.txt create mode 100644 code/test_graphe_matrice.txt create mode 100644 code/tests/dijkstra.test.js create mode 100644 code/tests/grapheOrientePondere.test.js diff --git a/code/instances/b.txt b/code/instances/b.txt deleted file mode 100644 index ad8e41d..0000000 --- a/code/instances/b.txt +++ /dev/null @@ -1,10 +0,0 @@ -c fjfj -p sp 6 6 - -p sp 6 6 -a 1 2 3 -a 1 4 2 -a 1 5 3 -a 1 3 2 -a 2 4 1 -a 5 2 3 diff --git a/code/instances/hh.txt b/code/instances/hh.txt deleted file mode 100644 index 78688e1..0000000 --- a/code/instances/hh.txt +++ /dev/null @@ -1,8 +0,0 @@ -c dhdfh -p sp 4 4 - -p sp 4 4 -a 1 2 3 -a 1 3 4 -a 1 4 1 -a 2 3 1 diff --git a/code/instances/test.txt b/code/instances/test.txt new file mode 100644 index 0000000..1ced7cc --- /dev/null +++ b/code/instances/test.txt @@ -0,0 +1,8 @@ +c hhh +p sp 4 4 + +p sp 4 4 +a 1 2 3 +a 1 3 3 +a 2 4 1 +a 3 4 2 diff --git a/code/instances/tests b/code/instances/tests deleted file mode 100644 index 776df1e..0000000 --- a/code/instances/tests +++ /dev/null @@ -1,6 +0,0 @@ -c ggg -p sp 4 2 - -p sp 4 2 -a 1 2 3 -a 3 1 2 diff --git a/code/rendu/RapportSAE202.txt b/code/rendu/RapportSAE202.txt new file mode 100644 index 0000000..4c50655 --- /dev/null +++ b/code/rendu/RapportSAE202.txt @@ -0,0 +1,69 @@ +Rapport SAE 2.02 + +Introduction + +Dans le cadre de ce rapport, nous souhaitons exposer la manière dont nous avons appréhendé la gestion des accès aux sommets au sein d'un graphe orienté, dans un contexte pondéré, en nous concentrant notamment sur la prise en charge des sommets inexistants. Cette considération s'avère cruciale afin d'assurer la robustesse et la fiabilité de notre implémentation en TypeScript. + +Gestion des Sommets Inexistants + +Ajout d'un Arc + +Lorsqu'un utilisateur tente d'ajouter un arc entre des sommets qui ne sont pas répertoriés, nous avons adopté deux approches principales : + +1. Création Automatique des Sommets : Si l'option de création automatique des sommets est activée, les sommets source et destination sont instanciés automatiquement avant d'ajouter l'arc avec le poids spécifié. Cette méthode garantit la réussite de l'opération même en l'absence de définition préalable des sommets. + +2. Levée d'une Exception : Si l'option de création automatique des sommets n'est pas activée, une exception explicite est émise. Cette démarche notifie l'utilisateur de l'inexistence des sommets spécifiés, interdisant par conséquent l'ajout de l'arc. Il revient alors à l'utilisateur de créer manuellement les sommets avant de réitérer l'opération. + +Suppression d'un Sommet ou d'un Arc + +Pour la suppression de sommets ou d'arcs, la prise en charge des sommets inexistants revêt également une importance capitale : + +1. Suppression d'un Sommet : Si l'utilisateur tente de supprimer un sommet qui n'existe pas, une exception est déclenchée. Cette anomalie indique à l'utilisateur que le sommet spécifié est inexistant, rendant ainsi l'opération invalide. + +2. Suppression d'un Arc : Dans le cas où l'utilisateur essaie de supprimer un arc entre des sommets inexistants ou un arc qui n'existe pas, une exception est également levée. Cette procédure assure que seules les opérations légitimes sont exécutées sur le graphe. + +Vérification de l'Existence d'un Arc + +Lorsqu'il s'agit de vérifier l'existence d'un arc entre deux sommets, nous avons mis en place un mécanisme afin de prendre en charge les sommets inexistants : + +- Vérification de l'Existence d'un Arc : Préalablement à la vérification de l'existence d'un arc, nous nous assurons que les sommets source et destination sont présents dans le graphe. Si l'un des sommets fait défaut, une valeur signifiant l'absence de l'arc est retournée. + +Récupération de Poids et de Voisins + +La récupération des poids des arcs et des voisins des sommets nécessite également une prise en charge des sommets inexistants : + +1. Récupération du Poids d'un Arc : Avant de récupérer le poids d'un arc, nous vérifions l'existence des sommets source et destination. En cas d'absence d'un des sommets, une valeur indiquant l'inexistence de l'arc est retournée. + +2. Récupération des Voisins : Pour récupérer les voisins d'un sommet, une vérification préalable de l'existence du sommet est effectuée. Si le sommet est absent, une liste vide est retournée, témoignant de l'absence de voisins. + +Tests Unitaires avec Jest + +Fonctionnalités de Jest + +Jest constitue un framework de test largement utilisé, permettant l'écriture et l'exécution de tests unitaires pour du code JavaScript et TypeScript. Il offre une interface claire pour la définition de tests et d'assertions, vérifiant ainsi la conformité du code aux attentes. Jest se distingue notamment par sa facilité d'utilisation et ses fonctionnalités intégrées, simplifiant la gestion des tests. + +Utilisation de Jest pour les Tests Unitaires + +Avec Jest, il est possible de : + +- crire des Tests Unitaires : Jest autorise la création de tests pour chaque fonction du projet, permettant ainsi de vérifier que chaque fonction produit les résultats escomptés pour diverses données d'entrée. + +- **Exécuter les Tests : Jest exécute l'ensemble des tests définis dans le projet, générant un rapport détaillé indiquant les tests réussis et échoués. Cette fonctionnalité permet de vérifier rapidement l'intégrité du code. + +- Automatiser les Tests : Jest peut être intégré à des pipelines d'intégration continue, automatisant ainsi l'exécution des tests à chaque modification du code. Cette pratique contribue à la détection précoce des erreurs, favorisant la stabilité du code. + +Avantages de Jest pour Notre Projet + +Pour notre projet de graphe orienté pondéré, l'utilisation de Jest offre plusieurs avantages : + +- Validation des Fonctionnalités : En rédigeant des tests unitaires, nous nous assurons du bon fonctionnement des fonctionnalités essentielles, telles que l'ajout et la suppression de sommets et d'arcs. + +- Détection Précoce des Erreurs : Les tests unitaires facilitent l'identification des erreurs introduites lors du développement, limitant ainsi les risques de dysfonctionnements en production. + +- Facilité de Maintenance : Grâce à une suite de tests bien définie, nous sommes en mesure de refondre notre code en toute confiance, les tests alertant de toute régression ou comportement imprévu. + +Il convient de noter que Deno, une alternative à Node.js, ne prend pas en charge les modules de système de fichiers (fs) utilisés pour la lecture et l'écriture de fichiers dans notre projet. Jest s'avère donc plus adapté à notre environnement de développement basé sur TypeScript/Node.js. + +Conclusion + +En conclusion, la prise en charge des sommets inexistants constitue un aspect essentiel de notre implémentation du graphe orienté pondéré en TypeScript. En adoptant des stratégies telles que la création automatique, la levée d'exceptions et la vérification préalable, nous garantissons la robustesse et la fiabilité de notre système, capable de gérer efficacement les erreurs potentielles liées aux opérations sur des sommets inexistants. Pour assurer la qualité de notre implémentation, nous avons recours à Jest pour effectuer des tests unitaires, nous permettant ainsi de vérifier que chaque composant de notre code fonctionne correctement dans divers scénarios diff --git a/code/rendu/UML_GrapheOrientePondere.webp b/code/rendu/UML_GrapheOrientePondere.webp new file mode 100644 index 0000000000000000000000000000000000000000..ff6dbba36f679011b965e25248e1dd026aef5b58 GIT binary patch literal 21118 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 literal 0 HcmV?d00001 diff --git "a/code/rendu/UML_R\303\251sultat.webp" "b/code/rendu/UML_R\303\251sultat.webp" new file mode 100644 index 0000000000000000000000000000000000000000..39e34a727e5c3d11ac1ce9124cc29f92df538486 GIT binary patch literal 9582 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 N8)>l+Slt@|1^|B5coqNv literal 0 HcmV?d00001 diff --git a/code/src/GrapheOrientePondere.js b/code/src/GrapheOrientePondere.js index f267b07..c8ca670 100644 --- a/code/src/GrapheOrientePondere.js +++ b/code/src/GrapheOrientePondere.js @@ -3,9 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.GrapheOrientePondere = void 0; const fs_1 = require("fs"); class GrapheOrientePondere { - constructor(fichier) { + constructor(utiliserListeAdjacence = true, fichier) { + this.utiliserListeAdjacence = utiliserListeAdjacence; this.listeAdjacence = new Map(); - this.utiliserListeAdjacence = true; // Par défaut, utiliser la liste d'adjacence + this.matriceAdjacence = []; this.commentaire = ''; this.infoGraphe = ''; if (fichier) { @@ -16,68 +17,133 @@ class GrapheOrientePondere { this.utiliserListeAdjacence = utiliser; } redimensionnerSommets(n) { - while (this.listeAdjacence.size > n) { - const sommet = this.listeAdjacence.size - 1; - this.listeAdjacence.delete(sommet); - this.listeAdjacence.forEach((val) => val.delete(sommet)); + if (this.utiliserListeAdjacence) { + while (this.listeAdjacence.size > n) { + const sommet = this.listeAdjacence.size - 1; + this.listeAdjacence.delete(sommet); + this.listeAdjacence.forEach((val) => val.delete(sommet)); + } + while (this.listeAdjacence.size < n) { + this.listeAdjacence.set(this.listeAdjacence.size, new Map()); + } } - while (this.listeAdjacence.size < n) { - this.listeAdjacence.set(this.listeAdjacence.size, new Map()); + else { + this.matriceAdjacence = this.matriceAdjacence.slice(0, n); + for (let i = 0; i < n; i++) { + if (!this.matriceAdjacence[i]) { + this.matriceAdjacence[i] = new Array(n).fill(Infinity); + } + else { + this.matriceAdjacence[i] = this.matriceAdjacence[i].slice(0, n); + } + } } this.mettreAJourInfosGraphe(); } nombreSommets() { - return this.listeAdjacence.size; + return this.utiliserListeAdjacence ? this.listeAdjacence.size : this.matriceAdjacence.length; } nombreArcs() { - let count = 0; - this.listeAdjacence.forEach(val => count += val.size); - return count; + if (this.utiliserListeAdjacence) { + let count = 0; + this.listeAdjacence.forEach(val => count += val.size); + return count; + } + else { + let count = 0; + for (let i = 0; i < this.matriceAdjacence.length; i++) { + for (let j = 0; j < this.matriceAdjacence[i].length; j++) { + if (this.matriceAdjacence[i][j] < Infinity) + count++; + } + } + return count; + } } ajouterArc(src, dest, poids) { var _a; - if (!this.listeAdjacence.has(src)) { - this.listeAdjacence.set(src, new Map()); + if (this.utiliserListeAdjacence) { + if (!this.listeAdjacence.has(src)) { + this.listeAdjacence.set(src, new Map()); + } + (_a = this.listeAdjacence.get(src)) === null || _a === void 0 ? void 0 : _a.set(dest, poids); + } + else { + this.matriceAdjacence[src][dest] = poids; } - (_a = this.listeAdjacence.get(src)) === null || _a === void 0 ? void 0 : _a.set(dest, poids); this.mettreAJourInfosGraphe(); } retirerArc(src, dest) { var _a, _b; - if (!this.listeAdjacence.has(src) || !((_a = this.listeAdjacence.get(src)) === null || _a === void 0 ? void 0 : _a.has(dest))) { - console.log("L'arc n'existe pas"); - return; + if (this.utiliserListeAdjacence) { + if (!this.listeAdjacence.has(src) || !((_a = this.listeAdjacence.get(src)) === null || _a === void 0 ? void 0 : _a.has(dest))) { + console.log("L'arc n'existe pas"); + return; + } + (_b = this.listeAdjacence.get(src)) === null || _b === void 0 ? void 0 : _b.delete(dest); + } + else { + if (this.matriceAdjacence[src][dest] === Infinity) { + console.log("L'arc n'existe pas"); + return; + } + this.matriceAdjacence[src][dest] = Infinity; } - (_b = this.listeAdjacence.get(src)) === null || _b === void 0 ? void 0 : _b.delete(dest); this.mettreAJourInfosGraphe(); } testerArc(src, dest) { var _a, _b; - return this.listeAdjacence.has(src) && ((_b = (_a = this.listeAdjacence.get(src)) === null || _a === void 0 ? void 0 : _a.has(dest)) !== null && _b !== void 0 ? _b : false); + if (this.utiliserListeAdjacence) { + return this.listeAdjacence.has(src) && ((_b = (_a = this.listeAdjacence.get(src)) === null || _a === void 0 ? void 0 : _a.has(dest)) !== null && _b !== void 0 ? _b : false); + } + else { + return this.matriceAdjacence[src][dest] < Infinity; + } } recupererPoids(src, dest) { var _a; - return (_a = this.listeAdjacence.get(src)) === null || _a === void 0 ? void 0 : _a.get(dest); + if (this.utiliserListeAdjacence) { + return (_a = this.listeAdjacence.get(src)) === null || _a === void 0 ? void 0 : _a.get(dest); + } + else { + return this.matriceAdjacence[src][dest]; + } } recupererSuccesseurs(sommet) { var _a; - return Array.from(((_a = this.listeAdjacence.get(sommet)) === null || _a === void 0 ? void 0 : _a.keys()) || []); + if (this.utiliserListeAdjacence) { + return Array.from(((_a = this.listeAdjacence.get(sommet)) === null || _a === void 0 ? void 0 : _a.keys()) || []); + } + else { + const successeurs = []; + for (let i = 0; i < this.matriceAdjacence.length; i++) { + if (this.matriceAdjacence[sommet][i] < Infinity) { + successeurs.push(i); + } + } + return successeurs; + } } recupererPredecesseurs(sommet) { const predecesseurs = []; - this.listeAdjacence.forEach((val, key) => { - if (val.has(sommet)) { - predecesseurs.push(key); + if (this.utiliserListeAdjacence) { + this.listeAdjacence.forEach((val, key) => { + if (val.has(sommet)) { + predecesseurs.push(key); + } + }); + } + else { + for (let i = 0; i < this.matriceAdjacence.length; i++) { + if (this.matriceAdjacence[i][sommet] < Infinity) { + predecesseurs.push(i); + } } - }); + } return predecesseurs; } recupererVoisins(sommet) { - const successeurs = this.recupererSuccesseurs(sommet); - const predecesseurs = this.recupererPredecesseurs(sommet); - const voisins = Array.from(new Set([...successeurs, ...predecesseurs])); - console.log(`Sommet: ${sommet}, Successeurs: ${successeurs}, Prédécesseurs: ${predecesseurs}, Voisins: ${voisins}`); - return voisins; + return [...new Set([...this.recupererSuccesseurs(sommet), ...this.recupererPredecesseurs(sommet)])]; } sauvegarderGraphe(output) { let stream; @@ -89,11 +155,22 @@ class GrapheOrientePondere { } stream.write(`${this.commentaire}\n`); stream.write(`${this.infoGraphe}\n`); - this.listeAdjacence.forEach((val, src) => { - val.forEach((poids, dest) => { - stream.write(`a ${src} ${dest} ${poids}\n`); + if (this.utiliserListeAdjacence) { + this.listeAdjacence.forEach((val, src) => { + val.forEach((poids, dest) => { + stream.write(`a ${src} ${dest} ${poids}\n`); + }); }); - }); + } + else { + for (let i = 0; i < this.matriceAdjacence.length; i++) { + for (let j = 0; j < this.matriceAdjacence[i].length; j++) { + if (this.matriceAdjacence[i][j] < Infinity) { + stream.write(`a ${i} ${j} ${this.matriceAdjacence[i][j]}\n`); + } + } + } + } if (typeof output === 'string') { stream.end(); } @@ -126,7 +203,12 @@ class GrapheOrientePondere { return this.infoGraphe; } sommetExiste(sommet) { - return this.listeAdjacence.has(sommet); + if (this.utiliserListeAdjacence) { + return this.listeAdjacence.has(sommet); + } + else { + return sommet >= 0 && sommet < this.matriceAdjacence.length; + } } ajouterSommet() { const newSommet = this.nombreSommets(); @@ -135,8 +217,16 @@ class GrapheOrientePondere { } supprimerSommet(sommet) { if (this.sommetExiste(sommet)) { - this.listeAdjacence.delete(sommet); - this.listeAdjacence.forEach((val) => val.delete(sommet)); + if (this.utiliserListeAdjacence) { + this.listeAdjacence.delete(sommet); + this.listeAdjacence.forEach((val) => val.delete(sommet)); + } + else { + for (let i = 0; i < this.matriceAdjacence.length; i++) { + this.matriceAdjacence[i][sommet] = Infinity; + this.matriceAdjacence[sommet][i] = Infinity; + } + } this.mettreAJourInfosGraphe(); } else { diff --git a/code/src/GrapheOrientePondere.ts b/code/src/GrapheOrientePondere.ts index b9d05c1..ac66626 100644 --- a/code/src/GrapheOrientePondere.ts +++ b/code/src/GrapheOrientePondere.ts @@ -1,14 +1,16 @@ -import { readFileSync, writeFileSync, createWriteStream, WriteStream } from 'fs'; +import { readFileSync, createWriteStream, WriteStream } from 'fs'; export class GrapheOrientePondere { private listeAdjacence: Map<number, Map<number, number>>; + private matriceAdjacence: number[][]; private utiliserListeAdjacence: boolean; private commentaire: string; private infoGraphe: string; - constructor(fichier?: string) { + constructor(utiliserListeAdjacence: boolean = true, fichier?: string) { + this.utiliserListeAdjacence = utiliserListeAdjacence; this.listeAdjacence = new Map(); - this.utiliserListeAdjacence = true; // Par défaut, utiliser la liste d'adjacence + this.matriceAdjacence = []; this.commentaire = ''; this.infoGraphe = ''; @@ -22,72 +24,127 @@ export class GrapheOrientePondere { } redimensionnerSommets(n: number) { - while (this.listeAdjacence.size > n) { - const sommet = this.listeAdjacence.size - 1; - this.listeAdjacence.delete(sommet); - this.listeAdjacence.forEach((val) => val.delete(sommet)); - } - while (this.listeAdjacence.size < n) { - this.listeAdjacence.set(this.listeAdjacence.size, new Map()); + if (this.utiliserListeAdjacence) { + while (this.listeAdjacence.size > n) { + const sommet = this.listeAdjacence.size - 1; + this.listeAdjacence.delete(sommet); + this.listeAdjacence.forEach((val) => val.delete(sommet)); + } + while (this.listeAdjacence.size < n) { + this.listeAdjacence.set(this.listeAdjacence.size, new Map()); + } + } else { + this.matriceAdjacence = this.matriceAdjacence.slice(0, n); + for (let i = 0; i < n; i++) { + if (!this.matriceAdjacence[i]) { + this.matriceAdjacence[i] = new Array(n).fill(Infinity); + } else { + this.matriceAdjacence[i] = this.matriceAdjacence[i].slice(0, n); + } + } } this.mettreAJourInfosGraphe(); } nombreSommets(): number { - return this.listeAdjacence.size; + return this.utiliserListeAdjacence ? this.listeAdjacence.size : this.matriceAdjacence.length; } nombreArcs(): number { - let count = 0; - this.listeAdjacence.forEach(val => count += val.size); - return count; + if (this.utiliserListeAdjacence) { + let count = 0; + this.listeAdjacence.forEach(val => count += val.size); + return count; + } else { + let count = 0; + for (let i = 0; i < this.matriceAdjacence.length; i++) { + for (let j = 0; j < this.matriceAdjacence[i].length; j++) { + if (this.matriceAdjacence[i][j] < Infinity) count++; + } + } + return count; + } } ajouterArc(src: number, dest: number, poids: number) { - if (!this.listeAdjacence.has(src)) { - this.listeAdjacence.set(src, new Map()); + if (this.utiliserListeAdjacence) { + if (!this.listeAdjacence.has(src)) { + this.listeAdjacence.set(src, new Map()); + } + this.listeAdjacence.get(src)?.set(dest, poids); + } else { + this.matriceAdjacence[src][dest] = poids; } - this.listeAdjacence.get(src)?.set(dest, poids); this.mettreAJourInfosGraphe(); } retirerArc(src: number, dest: number) { - if (!this.listeAdjacence.has(src) || !this.listeAdjacence.get(src)?.has(dest)) { - console.log("L'arc n'existe pas"); - return; + if (this.utiliserListeAdjacence) { + if (!this.listeAdjacence.has(src) || !this.listeAdjacence.get(src)?.has(dest)) { + console.log("L'arc n'existe pas"); + return; + } + this.listeAdjacence.get(src)?.delete(dest); + } else { + if (this.matriceAdjacence[src][dest] === Infinity) { + console.log("L'arc n'existe pas"); + return; + } + this.matriceAdjacence[src][dest] = Infinity; } - this.listeAdjacence.get(src)?.delete(dest); this.mettreAJourInfosGraphe(); } testerArc(src: number, dest: number): boolean { - return this.listeAdjacence.has(src) && (this.listeAdjacence.get(src)?.has(dest) ?? false); + if (this.utiliserListeAdjacence) { + return this.listeAdjacence.has(src) && (this.listeAdjacence.get(src)?.has(dest) ?? false); + } else { + return this.matriceAdjacence[src][dest] < Infinity; + } } recupererPoids(src: number, dest: number): number | undefined { - return this.listeAdjacence.get(src)?.get(dest); + if (this.utiliserListeAdjacence) { + return this.listeAdjacence.get(src)?.get(dest); + } else { + return this.matriceAdjacence[src][dest]; + } } recupererSuccesseurs(sommet: number): number[] { - return Array.from(this.listeAdjacence.get(sommet)?.keys() || []); + if (this.utiliserListeAdjacence) { + return Array.from(this.listeAdjacence.get(sommet)?.keys() || []); + } else { + const successeurs: number[] = []; + for (let i = 0; i < this.matriceAdjacence.length; i++) { + if (this.matriceAdjacence[sommet][i] < Infinity) { + successeurs.push(i); + } + } + return successeurs; + } } recupererPredecesseurs(sommet: number): number[] { const predecesseurs: number[] = []; - this.listeAdjacence.forEach((val, key) => { - if (val.has(sommet)) { - predecesseurs.push(key); + if (this.utiliserListeAdjacence) { + this.listeAdjacence.forEach((val, key) => { + if (val.has(sommet)) { + predecesseurs.push(key); + } + }); + } else { + for (let i = 0; i < this.matriceAdjacence.length; i++) { + if (this.matriceAdjacence[i][sommet] < Infinity) { + predecesseurs.push(i); + } } - }); + } return predecesseurs; } recupererVoisins(sommet: number): number[] { - const successeurs = this.recupererSuccesseurs(sommet); - const predecesseurs = this.recupererPredecesseurs(sommet); - const voisins = Array.from(new Set([...successeurs, ...predecesseurs])); - console.log(`Sommet: ${sommet}, Successeurs: ${successeurs}, Prédécesseurs: ${predecesseurs}, Voisins: ${voisins}`); - return voisins; + return [...new Set([...this.recupererSuccesseurs(sommet), ...this.recupererPredecesseurs(sommet)])]; } sauvegarderGraphe(output: WriteStream | string) { @@ -101,11 +158,21 @@ export class GrapheOrientePondere { stream.write(`${this.commentaire}\n`); stream.write(`${this.infoGraphe}\n`); - this.listeAdjacence.forEach((val, src) => { - val.forEach((poids, dest) => { - stream.write(`a ${src} ${dest} ${poids}\n`); + if (this.utiliserListeAdjacence) { + this.listeAdjacence.forEach((val, src) => { + val.forEach((poids, dest) => { + stream.write(`a ${src} ${dest} ${poids}\n`); + }); }); - }); + } else { + for (let i = 0; i < this.matriceAdjacence.length; i++) { + for (let j = 0; j < this.matriceAdjacence[i].length; j++) { + if (this.matriceAdjacence[i][j] < Infinity) { + stream.write(`a ${i} ${j} ${this.matriceAdjacence[i][j]}\n`); + } + } + } + } if (typeof output === 'string') { stream.end(); @@ -142,7 +209,11 @@ export class GrapheOrientePondere { } sommetExiste(sommet: number): boolean { - return this.listeAdjacence.has(sommet); + if (this.utiliserListeAdjacence) { + return this.listeAdjacence.has(sommet); + } else { + return sommet >= 0 && sommet < this.matriceAdjacence.length; + } } ajouterSommet(): number { @@ -153,8 +224,15 @@ export class GrapheOrientePondere { supprimerSommet(sommet: number) { if (this.sommetExiste(sommet)) { - this.listeAdjacence.delete(sommet); - this.listeAdjacence.forEach((val) => val.delete(sommet)); + if (this.utiliserListeAdjacence) { + this.listeAdjacence.delete(sommet); + this.listeAdjacence.forEach((val) => val.delete(sommet)); + } else { + for (let i = 0; i < this.matriceAdjacence.length; i++) { + this.matriceAdjacence[i][sommet] = Infinity; + this.matriceAdjacence[sommet][i] = Infinity; + } + } this.mettreAJourInfosGraphe(); } else { console.log('Sommet non existant.'); diff --git a/code/src/index.js b/code/src/index.js index 2a28f3e..3207d4e 100644 --- a/code/src/index.js +++ b/code/src/index.js @@ -42,10 +42,10 @@ function afficherMenu() { menuModifierGraphe(); break; case '2': - creerGraphe(); + choisirRepresentationPourCreation(); break; case '3': - calculerIsochrones(); + choisirRepresentationPourCalcul(); break; case '4': console.log("Au revoir !"); @@ -58,9 +58,32 @@ function afficherMenu() { } }); } +function choisirRepresentationPourCreation() { + rl.question('Voulez-vous utiliser une liste d\'adjacence (1) ou une matrice d\'adjacence (2)? ', (reponse) => { + if (reponse === '1' || reponse === '2') { + creerGraphe(reponse === '1'); + } + else { + console.log("Option invalide. Veuillez réessayer."); + choisirRepresentationPourCreation(); + } + }); +} +function choisirRepresentationPourCalcul() { + rl.question('Voulez-vous utiliser une liste d\'adjacence (1) ou une matrice d\'adjacence (2)? ', (reponse) => { + if (reponse === '1' || reponse === '2') { + calculerIsochrones(reponse === '1'); + } + else { + console.log("Option invalide. Veuillez réessayer."); + choisirRepresentationPourCalcul(); + } + }); +} function menuModifierGraphe() { rl.question('Entrez le chemin du fichier de graphe à modifier: ', (chemin) => { - const graphe = new GrapheOrientePondere_1.GrapheOrientePondere(chemin); + const utiliserListeAdjacence = detecterRepresentation(chemin); + const graphe = new GrapheOrientePondere_1.GrapheOrientePondere(utiliserListeAdjacence, chemin); // Détecte la représentation à utiliser console.log("Graphe chargé."); afficherSousMenuModification(graphe, chemin); }); @@ -219,9 +242,9 @@ function afficherSousMenuModification(graphe, chemin) { } }); } -function creerGraphe() { +function creerGraphe(utiliserListeAdjacence) { rl.question('Entrez un commentaire pour l\'instance de graphe (ligne commencée par "c"): ', (commentaire) => { - const graphe = new GrapheOrientePondere_1.GrapheOrientePondere(); + const graphe = new GrapheOrientePondere_1.GrapheOrientePondere(utiliserListeAdjacence); let nombreSommets = 0; let nombreArcs = 0; const ajouterSommetsEtArcs = () => { @@ -231,7 +254,7 @@ function creerGraphe() { const ajouterArc = () => { rl.question('Entrez l\'arc à ajouter (format: src dest poids), ou "fin" pour terminer: ', (data) => { if (data.toLowerCase() === 'fin') { - finaliserGraphe(); + finaliserGraphe(commentaire, graphe); } else { const [src, dest, poids] = data.split(' ').map(Number); @@ -244,24 +267,24 @@ function creerGraphe() { ajouterArc(); }); }; - const finaliserGraphe = () => { - rl.question('Entrez le chemin pour sauvegarder le graphe: ', (chemin) => { - const output = (0, fs_1.createWriteStream)(chemin); - output.write(`c ${commentaire}\n`); - graphe.mettreAJourInfosGraphe(); - output.write(`${graphe.getInfosGraphe()}\n`); - graphe.sauvegarderGraphe(output); - output.end(); - console.log('Graphe créé et sauvegardé.'); - afficherMenu(); - }); - }; ajouterSommetsEtArcs(); }); } -function calculerIsochrones() { +function finaliserGraphe(commentaire, graphe) { + rl.question('Entrez le chemin pour sauvegarder le graphe: ', (chemin) => { + const output = (0, fs_1.createWriteStream)(chemin); + output.write(`c ${commentaire}\n`); + graphe.mettreAJourInfosGraphe(); + output.write(`${graphe.getInfosGraphe()}\n`); + graphe.sauvegarderGraphe(output); + output.end(); + console.log('Graphe créé et sauvegardé.'); + afficherMenu(); + }); +} +function calculerIsochrones(utiliserListeAdjacence) { rl.question('Entrez le chemin du fichier de graphe: ', (chemin) => { - const graphe = new GrapheOrientePondere_1.GrapheOrientePondere(chemin); + const graphe = new GrapheOrientePondere_1.GrapheOrientePondere(utiliserListeAdjacence, chemin); rl.question('Entrez le sommet de départ: ', (data) => { const sommetInitial = parseInt(data); if (sommetInitial < 0 || sommetInitial >= graphe.nombreSommets()) { @@ -278,7 +301,7 @@ function calculerIsochrones() { console.log(`Distance de ${sommetInitial} à ${i}: ${resultat.distances[i]}`); } else { - console.log(`Distance de ${sommetInitial} à ${i}: Infinity`); + console.log(`Distance de ${sommetInitial} à ${i}: Infini`); } } console.log("Prédécesseurs :"); @@ -295,4 +318,15 @@ function calculerIsochrones() { }); }); } +function detecterRepresentation(fichier) { + const lignes = (0, fs_1.readFileSync)(fichier, 'utf8').split('\n'); + for (let ligne of lignes) { + if (ligne.startsWith('p sp ')) { + const [, , numSommets] = ligne.split(' '); + const n = parseInt(numSommets); + return n < 100; + } + } + return true; // Par défaut, utilise la liste d'adjacence +} afficherMenu(); diff --git a/code/src/index.ts b/code/src/index.ts index bb0c30c..5ea4570 100644 --- a/code/src/index.ts +++ b/code/src/index.ts @@ -1,7 +1,7 @@ import * as readline from 'readline'; import { GrapheOrientePondere } from './GrapheOrientePondere'; import { dijkstra } from './dijkstra'; -import { createWriteStream } from 'fs'; +import { createWriteStream, readFileSync } from 'fs'; const rl = readline.createInterface({ input: process.stdin, @@ -20,10 +20,10 @@ function afficherMenu() { menuModifierGraphe(); break; case '2': - creerGraphe(); + choisirRepresentationPourCreation(); break; case '3': - calculerIsochrones(); + choisirRepresentationPourCalcul(); break; case '4': console.log("Au revoir !"); @@ -36,10 +36,33 @@ function afficherMenu() { } }); } - + +function choisirRepresentationPourCreation() { + rl.question('Voulez-vous utiliser une liste d\'adjacence (1) ou une matrice d\'adjacence (2)? ', (reponse) => { + if (reponse === '1' || reponse === '2') { + creerGraphe(reponse === '1'); + } else { + console.log("Option invalide. Veuillez réessayer."); + choisirRepresentationPourCreation(); + } + }); +} + +function choisirRepresentationPourCalcul() { + rl.question('Voulez-vous utiliser une liste d\'adjacence (1) ou une matrice d\'adjacence (2)? ', (reponse) => { + if (reponse === '1' || reponse === '2') { + calculerIsochrones(reponse === '1'); + } else { + console.log("Option invalide. Veuillez réessayer."); + choisirRepresentationPourCalcul(); + } + }); +} + function menuModifierGraphe() { rl.question('Entrez le chemin du fichier de graphe à modifier: ', (chemin) => { - const graphe = new GrapheOrientePondere(chemin); + const utiliserListeAdjacence = detecterRepresentation(chemin); + const graphe = new GrapheOrientePondere(utiliserListeAdjacence, chemin); // Détecte la représentation à utiliser console.log("Graphe chargé."); afficherSousMenuModification(graphe, chemin); }); @@ -194,9 +217,9 @@ function afficherSousMenuModification(graphe: GrapheOrientePondere, chemin: stri }); } -function creerGraphe() { +function creerGraphe(utiliserListeAdjacence: boolean) { rl.question('Entrez un commentaire pour l\'instance de graphe (ligne commencée par "c"): ', (commentaire) => { - const graphe = new GrapheOrientePondere(); + const graphe = new GrapheOrientePondere(utiliserListeAdjacence); let nombreSommets = 0; let nombreArcs = 0; @@ -208,7 +231,7 @@ function creerGraphe() { const ajouterArc = () => { rl.question('Entrez l\'arc à ajouter (format: src dest poids), ou "fin" pour terminer: ', (data) => { if (data.toLowerCase() === 'fin') { - finaliserGraphe(); + finaliserGraphe(commentaire, graphe); } else { const [src, dest, poids] = data.split(' ').map(Number); graphe.ajouterArc(src, dest, poids); @@ -222,26 +245,26 @@ function creerGraphe() { }); }; - const finaliserGraphe = () => { - rl.question('Entrez le chemin pour sauvegarder le graphe: ', (chemin) => { - const output = createWriteStream(chemin); - output.write(`c ${commentaire}\n`); - graphe.mettreAJourInfosGraphe(); - output.write(`${graphe.getInfosGraphe()}\n`); - graphe.sauvegarderGraphe(output); - output.end(); - console.log('Graphe créé et sauvegardé.'); - afficherMenu(); - }); - }; - ajouterSommetsEtArcs(); }); } -function calculerIsochrones() { +function finaliserGraphe(commentaire: string, graphe: GrapheOrientePondere) { + rl.question('Entrez le chemin pour sauvegarder le graphe: ', (chemin) => { + const output = createWriteStream(chemin); + output.write(`c ${commentaire}\n`); + graphe.mettreAJourInfosGraphe(); + output.write(`${graphe.getInfosGraphe()}\n`); + graphe.sauvegarderGraphe(output); + output.end(); + console.log('Graphe créé et sauvegardé.'); + afficherMenu(); + }); +} + +function calculerIsochrones(utiliserListeAdjacence: boolean) { rl.question('Entrez le chemin du fichier de graphe: ', (chemin) => { - const graphe = new GrapheOrientePondere(chemin); + const graphe = new GrapheOrientePondere(utiliserListeAdjacence, chemin); rl.question('Entrez le sommet de départ: ', (data) => { const sommetInitial = parseInt(data); if (sommetInitial < 0 || sommetInitial >= graphe.nombreSommets()) { @@ -258,7 +281,7 @@ function calculerIsochrones() { if (resultat.distances[i] !== Infinity) { console.log(`Distance de ${sommetInitial} à ${i}: ${resultat.distances[i]}`); } else { - console.log(`Distance de ${sommetInitial} à ${i}: Infinity`); + console.log(`Distance de ${sommetInitial} à ${i}: Infini`); } } @@ -277,4 +300,16 @@ function calculerIsochrones() { }); } +function detecterRepresentation(fichier: string): boolean { + const lignes = readFileSync(fichier, 'utf8').split('\n'); + for (let ligne of lignes) { + if (ligne.startsWith('p sp ')) { + const [, , numSommets] = ligne.split(' '); + const n = parseInt(numSommets); + return n < 100; + } + } + return true; // Par défaut, utilise la liste d'adjacence +} + afficherMenu(); diff --git a/code/test_graphe_liste.txt b/code/test_graphe_liste.txt new file mode 100644 index 0000000..6ace6bc --- /dev/null +++ b/code/test_graphe_liste.txt @@ -0,0 +1,4 @@ + +p sp 4 2 +a 1 2 5 +a 2 3 10 diff --git a/code/test_graphe_matrice.txt b/code/test_graphe_matrice.txt new file mode 100644 index 0000000..6ace6bc --- /dev/null +++ b/code/test_graphe_matrice.txt @@ -0,0 +1,4 @@ + +p sp 4 2 +a 1 2 5 +a 2 3 10 diff --git a/code/tests/dijkstra.test.js b/code/tests/dijkstra.test.js new file mode 100644 index 0000000..e96da29 --- /dev/null +++ b/code/tests/dijkstra.test.js @@ -0,0 +1,33 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const GrapheOrientePondere_1 = require("../src/GrapheOrientePondere"); +const dijkstra_1 = require("../src/dijkstra"); +// Test simple pour vérifier le fonctionnement de l'algorithme de Dijkstra +test("Dijkstra - Graphe simple", () => { + const graphe = new GrapheOrientePondere_1.GrapheOrientePondere(); + graphe.redimensionnerSommets(4); + graphe.ajouterArc(1, 2, 3); + graphe.ajouterArc(1, 3, 4); + graphe.ajouterArc(2, 1, 2); + graphe.ajouterArc(2, 3, 1); + const resultat = (0, dijkstra_1.dijkstra)(graphe, 1, Infinity); + expect(resultat.obtenirDistance(1)).toBe(0); + expect(resultat.obtenirDistance(2)).toBe(3); + expect(resultat.obtenirDistance(3)).toBe(4); + expect(resultat.obtenirPredecesseur(2)).toBe(1); + expect(resultat.obtenirPredecesseur(3)).toBe(1); +}); +test("Dijkstra - Limite de parcours", () => { + const graphe = new GrapheOrientePondere_1.GrapheOrientePondere(); + graphe.redimensionnerSommets(4); + graphe.ajouterArc(1, 2, 3); + graphe.ajouterArc(1, 3, 4); + graphe.ajouterArc(2, 1, 2); + graphe.ajouterArc(2, 3, 1); + const resultat = (0, dijkstra_1.dijkstra)(graphe, 1, 3); + expect(resultat.obtenirDistance(1)).toBe(0); + expect(resultat.obtenirDistance(2)).toBe(3); + expect(resultat.obtenirDistance(3)).toBe(Infinity); + expect(resultat.obtenirPredecesseur(2)).toBe(1); + expect(resultat.obtenirPredecesseur(3)).toBe(null); +}); diff --git a/code/tests/grapheOrientePondere.test.js b/code/tests/grapheOrientePondere.test.js new file mode 100644 index 0000000..02380d2 --- /dev/null +++ b/code/tests/grapheOrientePondere.test.js @@ -0,0 +1,172 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const GrapheOrientePondere_1 = require("../src/GrapheOrientePondere"); +const fs_1 = require("fs"); +const testFilePathListe = "test_graphe_liste.txt"; +const testFilePathMatrice = "test_graphe_matrice.txt"; +// Test pour vérifier l'ajout et la suppression de sommets +test("Gestion des sommets - Ajout et suppression (Liste d'adjacence)", () => { + const graphe = new GrapheOrientePondere_1.GrapheOrientePondere(true); + graphe.redimensionnerSommets(3); + expect(graphe.nombreSommets()).toBe(3); + graphe.ajouterSommet(); + expect(graphe.nombreSommets()).toBe(4); + graphe.supprimerSommet(3); + expect(graphe.nombreSommets()).toBe(3); + expect(graphe.sommetExiste(3)).toBe(false); +}); +// Test pour vérifier l'ajout, la suppression et la récupération des arcs +test("Gestion des arcs - Ajout, suppression et récupération (Liste d'adjacence)", () => { + const graphe = new GrapheOrientePondere_1.GrapheOrientePondere(true); + graphe.redimensionnerSommets(3); + graphe.ajouterArc(1, 2, 5); + expect(graphe.testerArc(1, 2)).toBe(true); + expect(graphe.recupererPoids(1, 2)).toBe(5); + graphe.retirerArc(1, 2); + expect(graphe.testerArc(1, 2)).toBe(false); +}); +test("Gestion des arcs - Ajout, suppression et récupération (Matrice d'adjacence)", () => { + const graphe = new GrapheOrientePondere_1.GrapheOrientePondere(false); + graphe.redimensionnerSommets(3); + graphe.ajouterArc(1, 2, 5); + expect(graphe.testerArc(1, 2)).toBe(true); + expect(graphe.recupererPoids(1, 2)).toBe(5); + graphe.retirerArc(1, 2); + expect(graphe.testerArc(1, 2)).toBe(false); +}); +// Test pour vérifier la récupération des successeurs +test("Récupération des successeurs (Liste d'adjacence)", () => { + const graphe = new GrapheOrientePondere_1.GrapheOrientePondere(true); + graphe.redimensionnerSommets(3); + graphe.ajouterArc(1, 2, 5); + graphe.ajouterArc(1, 3, 10); + const successeurs = graphe.recupererSuccesseurs(1); + expect(successeurs).toEqual([2, 3]); +}); +// Test pour vérifier la récupération des prédécesseurs +test("Récupération des prédécesseurs (Liste d'adjacence)", () => { + const graphe = new GrapheOrientePondere_1.GrapheOrientePondere(true); + graphe.redimensionnerSommets(3); + graphe.ajouterArc(1, 2, 5); + graphe.ajouterArc(3, 2, 10); + const predecesseurs = graphe.recupererPredecesseurs(2); + expect(predecesseurs).toEqual([1, 3]); +}); +// Test pour vérifier la récupération des voisins +test("Récupération des voisins (Liste d'adjacence)", () => { + const graphe = new GrapheOrientePondere_1.GrapheOrientePondere(true); + graphe.redimensionnerSommets(3); + graphe.ajouterArc(1, 2, 5); + graphe.ajouterArc(2, 3, 10); + const voisins = graphe.recupererVoisins(2).sort((a, b) => a - b); + expect(voisins).toEqual([1, 3]); +}); +// Test pour vérifier l'ajout de sommets multiples +test("Ajout de sommets multiples (Liste d'adjacence)", () => { + const graphe = new GrapheOrientePondere_1.GrapheOrientePondere(true); + graphe.redimensionnerSommets(5); + expect(graphe.nombreSommets()).toBe(5); + graphe.ajouterSommet(); + graphe.ajouterSommet(); + expect(graphe.nombreSommets()).toBe(7); +}); +test("Ajout de sommets multiples (Matrice d'adjacence)", () => { + const graphe = new GrapheOrientePondere_1.GrapheOrientePondere(false); + graphe.redimensionnerSommets(5); + expect(graphe.nombreSommets()).toBe(5); + graphe.ajouterSommet(); + graphe.ajouterSommet(); + expect(graphe.nombreSommets()).toBe(7); +}); +// Test pour vérifier la suppression d'un sommet qui n'existe pas +test("Suppression d'un sommet qui n'existe pas (Liste d'adjacence)", () => { + const graphe = new GrapheOrientePondere_1.GrapheOrientePondere(true); + graphe.redimensionnerSommets(3); + expect(graphe.nombreSommets()).toBe(3); + graphe.supprimerSommet(5); + expect(graphe.nombreSommets()).toBe(3); +}); +test("Suppression d'un sommet qui n'existe pas (Matrice d'adjacence)", () => { + const graphe = new GrapheOrientePondere_1.GrapheOrientePondere(false); + graphe.redimensionnerSommets(3); + expect(graphe.nombreSommets()).toBe(3); + graphe.supprimerSommet(5); + expect(graphe.nombreSommets()).toBe(3); +}); +// Test pour vérifier la récupération des arcs +test("Récupération des arcs (Liste d'adjacence)", () => { + const graphe = new GrapheOrientePondere_1.GrapheOrientePondere(true); + graphe.redimensionnerSommets(4); + graphe.ajouterArc(1, 2, 5); + graphe.ajouterArc(2, 3, 10); + graphe.ajouterArc(3, 4, 15); + expect(graphe.recupererPoids(1, 2)).toBe(5); + expect(graphe.recupererPoids(2, 3)).toBe(10); + expect(graphe.recupererPoids(3, 4)).toBe(15); +}); +test("Récupération des arcs (Matrice d'adjacence)", () => { + const graphe = new GrapheOrientePondere_1.GrapheOrientePondere(false); + graphe.redimensionnerSommets(4); + graphe.ajouterArc(1, 2, 5); + graphe.ajouterArc(2, 3, 10); + graphe.ajouterArc(3, 4, 15); + expect(graphe.recupererPoids(1, 2)).toBe(5); + expect(graphe.recupererPoids(2, 3)).toBe(10); + expect(graphe.recupererPoids(3, 4)).toBe(15); +}); +// Test pour vérifier la sauvegarde et la lecture du graphe +test("Sauvegarde et lecture du graphe (Liste d'adjacence)", () => { + const graphe = new GrapheOrientePondere_1.GrapheOrientePondere(true); + graphe.redimensionnerSommets(4); + graphe.ajouterArc(1, 2, 5); + graphe.ajouterArc(2, 3, 10); + const path = testFilePathListe; + graphe.sauvegarderGraphe(path); + const nouveauGraphe = new GrapheOrientePondere_1.GrapheOrientePondere(true, path); + expect(nouveauGraphe.nombreSommets()).toBe(4); + expect(nouveauGraphe.nombreArcs()).toBe(2); + expect(nouveauGraphe.recupererPoids(1, 2)).toBe(5); + expect(nouveauGraphe.recupererPoids(2, 3)).toBe(10); +}); +test("Sauvegarde et lecture du graphe (Matrice d'adjacence)", () => { + const graphe = new GrapheOrientePondere_1.GrapheOrientePondere(false); + graphe.redimensionnerSommets(4); + graphe.ajouterArc(1, 2, 5); + graphe.ajouterArc(2, 3, 10); + const path = testFilePathMatrice; + graphe.sauvegarderGraphe(path); + const nouveauGraphe = new GrapheOrientePondere_1.GrapheOrientePondere(false, path); + expect(nouveauGraphe.nombreSommets()).toBe(4); + expect(nouveauGraphe.nombreArcs()).toBe(2); + expect(nouveauGraphe.recupererPoids(1, 2)).toBe(5); + expect(nouveauGraphe.recupererPoids(2, 3)).toBe(10); +}); +// Test pour vérifier la mise à jour des informations du graphe +test("Mise à jour des informations du graphe (Liste d'adjacence)", () => { + const graphe = new GrapheOrientePondere_1.GrapheOrientePondere(true); + graphe.redimensionnerSommets(3); + graphe.ajouterArc(1, 2, 5); + expect(graphe.getInfosGraphe()).toBe("p sp 3 1"); + graphe.ajouterArc(2, 3, 10); + graphe.redimensionnerSommets(4); + expect(graphe.getInfosGraphe()).toBe("p sp 4 2"); +}); +test("Mise à jour des informations du graphe (Matrice d'adjacence)", () => { + const graphe = new GrapheOrientePondere_1.GrapheOrientePondere(false); + graphe.redimensionnerSommets(3); + graphe.ajouterArc(1, 2, 5); + expect(graphe.getInfosGraphe()).toBe("p sp 3 1"); + graphe.ajouterArc(2, 3, 10); + graphe.redimensionnerSommets(4); + expect(graphe.getInfosGraphe()).toBe("p sp 4 2"); +}); +beforeAll(() => { + // Avant tous les tests, créez le fichier de test + (0, fs_1.writeFileSync)(testFilePathListe, 'c Test graph\np sp 4 2\na 1 2 5\na 2 3 10\n'); + (0, fs_1.writeFileSync)(testFilePathMatrice, 'c Test graph\np sp 4 2\na 1 2 5\na 2 3 10\n'); +}); +afterAll(() => { + // Après tous les tests, pour supprimer le fichier de test + (0, fs_1.unlinkSync)(testFilePathListe); + (0, fs_1.unlinkSync)(testFilePathMatrice); +}); diff --git a/code/tests/grapheOrientePondere.test.ts b/code/tests/grapheOrientePondere.test.ts index 867556b..057ef88 100644 --- a/code/tests/grapheOrientePondere.test.ts +++ b/code/tests/grapheOrientePondere.test.ts @@ -1,11 +1,12 @@ import { GrapheOrientePondere } from "../src/GrapheOrientePondere"; import { writeFileSync, unlinkSync } from "fs"; -const testFilePath = "test_graphe.txt"; +const testFilePathListe = "test_graphe_liste.txt"; +const testFilePathMatrice = "test_graphe_matrice.txt"; // Test pour vérifier l'ajout et la suppression de sommets -test("Gestion des sommets - Ajout et suppression", () => { - const graphe = new GrapheOrientePondere(); +test("Gestion des sommets - Ajout et suppression (Liste d'adjacence)", () => { + const graphe = new GrapheOrientePondere(true); graphe.redimensionnerSommets(3); expect(graphe.nombreSommets()).toBe(3); @@ -18,9 +19,22 @@ test("Gestion des sommets - Ajout et suppression", () => { expect(graphe.sommetExiste(3)).toBe(false); }); + // Test pour vérifier l'ajout, la suppression et la récupération des arcs -test("Gestion des arcs - Ajout, suppression et récupération", () => { - const graphe = new GrapheOrientePondere(); +test("Gestion des arcs - Ajout, suppression et récupération (Liste d'adjacence)", () => { + const graphe = new GrapheOrientePondere(true); + graphe.redimensionnerSommets(3); + graphe.ajouterArc(1, 2, 5); + + expect(graphe.testerArc(1, 2)).toBe(true); + expect(graphe.recupererPoids(1, 2)).toBe(5); + + graphe.retirerArc(1, 2); + expect(graphe.testerArc(1, 2)).toBe(false); +}); + +test("Gestion des arcs - Ajout, suppression et récupération (Matrice d'adjacence)", () => { + const graphe = new GrapheOrientePondere(false); graphe.redimensionnerSommets(3); graphe.ajouterArc(1, 2, 5); @@ -32,8 +46,8 @@ test("Gestion des arcs - Ajout, suppression et récupération", () => { }); // Test pour vérifier la récupération des successeurs -test("Récupération des successeurs", () => { - const graphe = new GrapheOrientePondere(); +test("Récupération des successeurs (Liste d'adjacence)", () => { + const graphe = new GrapheOrientePondere(true); graphe.redimensionnerSommets(3); graphe.ajouterArc(1, 2, 5); graphe.ajouterArc(1, 3, 10); @@ -43,8 +57,8 @@ test("Récupération des successeurs", () => { }); // Test pour vérifier la récupération des prédécesseurs -test("Récupération des prédécesseurs", () => { - const graphe = new GrapheOrientePondere(); +test("Récupération des prédécesseurs (Liste d'adjacence)", () => { + const graphe = new GrapheOrientePondere(true); graphe.redimensionnerSommets(3); graphe.ajouterArc(1, 2, 5); graphe.ajouterArc(3, 2, 10); @@ -54,8 +68,8 @@ test("Récupération des prédécesseurs", () => { }); // Test pour vérifier la récupération des voisins -test("Récupération des voisins", () => { - const graphe = new GrapheOrientePondere(); +test("Récupération des voisins (Liste d'adjacence)", () => { + const graphe = new GrapheOrientePondere(true); graphe.redimensionnerSommets(3); graphe.ajouterArc(1, 2, 5); graphe.ajouterArc(2, 3, 10); @@ -64,9 +78,22 @@ test("Récupération des voisins", () => { expect(voisins).toEqual([1, 3]); }); + + // Test pour vérifier l'ajout de sommets multiples -test("Ajout de sommets multiples", () => { - const graphe = new GrapheOrientePondere(); +test("Ajout de sommets multiples (Liste d'adjacence)", () => { + const graphe = new GrapheOrientePondere(true); + graphe.redimensionnerSommets(5); + + expect(graphe.nombreSommets()).toBe(5); + + graphe.ajouterSommet(); + graphe.ajouterSommet(); + expect(graphe.nombreSommets()).toBe(7); +}); + +test("Ajout de sommets multiples (Matrice d'adjacence)", () => { + const graphe = new GrapheOrientePondere(false); graphe.redimensionnerSommets(5); expect(graphe.nombreSommets()).toBe(5); @@ -77,8 +104,18 @@ test("Ajout de sommets multiples", () => { }); // Test pour vérifier la suppression d'un sommet qui n'existe pas -test("Suppression d'un sommet qui n'existe pas", () => { - const graphe = new GrapheOrientePondere(); +test("Suppression d'un sommet qui n'existe pas (Liste d'adjacence)", () => { + const graphe = new GrapheOrientePondere(true); + graphe.redimensionnerSommets(3); + + expect(graphe.nombreSommets()).toBe(3); + + graphe.supprimerSommet(5); + expect(graphe.nombreSommets()).toBe(3); +}); + +test("Suppression d'un sommet qui n'existe pas (Matrice d'adjacence)", () => { + const graphe = new GrapheOrientePondere(false); graphe.redimensionnerSommets(3); expect(graphe.nombreSommets()).toBe(3); @@ -88,8 +125,20 @@ test("Suppression d'un sommet qui n'existe pas", () => { }); // Test pour vérifier la récupération des arcs -test("Récupération des arcs", () => { - const graphe = new GrapheOrientePondere(); +test("Récupération des arcs (Liste d'adjacence)", () => { + const graphe = new GrapheOrientePondere(true); + graphe.redimensionnerSommets(4); + graphe.ajouterArc(1, 2, 5); + graphe.ajouterArc(2, 3, 10); + graphe.ajouterArc(3, 4, 15); + + expect(graphe.recupererPoids(1, 2)).toBe(5); + expect(graphe.recupererPoids(2, 3)).toBe(10); + expect(graphe.recupererPoids(3, 4)).toBe(15); +}); + +test("Récupération des arcs (Matrice d'adjacence)", () => { + const graphe = new GrapheOrientePondere(false); graphe.redimensionnerSommets(4); graphe.ajouterArc(1, 2, 5); graphe.ajouterArc(2, 3, 10); @@ -101,16 +150,32 @@ test("Récupération des arcs", () => { }); // Test pour vérifier la sauvegarde et la lecture du graphe -test("Sauvegarde et lecture du graphe", () => { - const graphe = new GrapheOrientePondere(); +test("Sauvegarde et lecture du graphe (Liste d'adjacence)", () => { + const graphe = new GrapheOrientePondere(true); graphe.redimensionnerSommets(4); graphe.ajouterArc(1, 2, 5); graphe.ajouterArc(2, 3, 10); - const path = testFilePath; + const path = testFilePathListe; graphe.sauvegarderGraphe(path); - const nouveauGraphe = new GrapheOrientePondere(path); + const nouveauGraphe = new GrapheOrientePondere(true, path); + expect(nouveauGraphe.nombreSommets()).toBe(4); + expect(nouveauGraphe.nombreArcs()).toBe(2); + expect(nouveauGraphe.recupererPoids(1, 2)).toBe(5); + expect(nouveauGraphe.recupererPoids(2, 3)).toBe(10); +}); + +test("Sauvegarde et lecture du graphe (Matrice d'adjacence)", () => { + const graphe = new GrapheOrientePondere(false); + graphe.redimensionnerSommets(4); + graphe.ajouterArc(1, 2, 5); + graphe.ajouterArc(2, 3, 10); + + const path = testFilePathMatrice; + graphe.sauvegarderGraphe(path); + + const nouveauGraphe = new GrapheOrientePondere(false, path); expect(nouveauGraphe.nombreSommets()).toBe(4); expect(nouveauGraphe.nombreArcs()).toBe(2); expect(nouveauGraphe.recupererPoids(1, 2)).toBe(5); @@ -118,8 +183,21 @@ test("Sauvegarde et lecture du graphe", () => { }); // Test pour vérifier la mise à jour des informations du graphe -test("Mise à jour des informations du graphe", () => { - const graphe = new GrapheOrientePondere(); +test("Mise à jour des informations du graphe (Liste d'adjacence)", () => { + const graphe = new GrapheOrientePondere(true); + graphe.redimensionnerSommets(3); + graphe.ajouterArc(1, 2, 5); + + expect(graphe.getInfosGraphe()).toBe("p sp 3 1"); + + graphe.ajouterArc(2, 3, 10); + graphe.redimensionnerSommets(4); + + expect(graphe.getInfosGraphe()).toBe("p sp 4 2"); +}); + +test("Mise à jour des informations du graphe (Matrice d'adjacence)", () => { + const graphe = new GrapheOrientePondere(false); graphe.redimensionnerSommets(3); graphe.ajouterArc(1, 2, 5); @@ -133,10 +211,12 @@ test("Mise à jour des informations du graphe", () => { beforeAll(() => { // Avant tous les tests, créez le fichier de test - writeFileSync(testFilePath, 'c Test graph\np sp 4 2\na 1 2 5\na 2 3 10\n'); + writeFileSync(testFilePathListe, 'c Test graph\np sp 4 2\na 1 2 5\na 2 3 10\n'); + writeFileSync(testFilePathMatrice, 'c Test graph\np sp 4 2\na 1 2 5\na 2 3 10\n'); }); afterAll(() => { // Après tous les tests, pour supprimer le fichier de test - unlinkSync(testFilePath); + unlinkSync(testFilePathListe); + unlinkSync(testFilePathMatrice); }); -- GitLab