diff --git a/pagerank/Makefile b/pagerank/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..8516972287c8660ff5bca03f84ea6148cb8cbb96 --- /dev/null +++ b/pagerank/Makefile @@ -0,0 +1,12 @@ +CC= gcc +CXXFLAGS= -Wall --pedantic -O3 + +CPP_O_FILE = main-pagerank.o +LIB = -lm + + +all: $(CPP_O_FILE) + $(CC) $(CXXFLAGS) -o main-pagerank.exe main-pagerank.c ./src/pagerank.c ./src/graphe.c ./src/liste_url.c $(LIB) + +clean: + rm -rf *.o *.exe diff --git a/pagerank/README.md b/pagerank/README.md index 7d4f4e7e1b74629e624dad066745e1b0379403a7..73b043dae2163b606f684ca1c6f26e6de7e95c92 100644 --- a/pagerank/README.md +++ b/pagerank/README.md @@ -1,7 +1,12 @@ Le répertoire src contient les sources du programme qui crée un graphe des pages web du lipn et en calcule le pagerank. -Le répertoire test cotient les tests unitaires qui vérifie que les fonctions +Le répertoire test contient les tests unitaires qui vérifie que les fonctions dans contenues dans le répertoire src jouent correctement leur rôle +Les bibliothèques requises sont celles de base en C, stdlib, stdio, string, time et assert. (.h) + +Pour compiler les fichiers de test, il faut se placer dans le répertoire test et entrer la commande make + +Le programme final, quand il sera fait se compilera en faisant make dans le répertoire pagerank diff --git a/pagerank/src/graphe.c b/pagerank/src/graphe.c new file mode 100644 index 0000000000000000000000000000000000000000..89756435145da109f300b43bbcedd0a4a867ffef --- /dev/null +++ b/pagerank/src/graphe.c @@ -0,0 +1,455 @@ +#include +#include +#include "graphe.h" + + + +/** Fonction qui crée un graphe vide qui sera stocké à l'adresse g déjà allouée + * @param g un pointeur vers un graphe alloué en mémoire + * @param n le nombre strictement positif de cases des tableaux du graphe + * @requires n > 0 + * @return 0 si le graphe a été créé et -1 si n <= 0 et -2 s'il y a eu une erreur d'allocation + * O(n) + */ +int creer_graphe(graphe* g, int n) { + int i; + if(n <= 0) { + printf("Erreur paramètre creer_graphe, n <= 0\n"); + return -1; + } + if(!(g->ligne = (int*) malloc(sizeof(int) * n))) { + printf("Erreur allocation creer_graphe, g->ligne\n"); + return -2; + } + if(!(g->col = (int*) malloc(sizeof(int) * n))) { + printf("Erreur allocation creer_graphe, g->col\n"); + free(g->ligne); + return -2; + } + g->max = n; + g->premierVide = 0; + for(i = 0; i < n; i++) { + g->ligne[i] = -1; + g->col[i] = -1; + } + return 0; +} + + + +/** Libère la mémoire allouée dans un graphe, mais pas le graphe lui même (le pointeur donné en paramètre reste à libérer + * @param g un pointeur vers un graphe alloué en mémoire + */ +void graphe_detruire(graphe* g) { + if(g) { + free(g->ligne); + free(g->col); + } +} + + +/** Renvoie la taille maximale actuelle des tableaux du graphe g + * @param g un pointeur vers un graphe alloué en mémoire + * @return g->max un entier + * O(1) + */ +int graphe_get_max(graphe* g) { + return g->max; +} + + +/** Renvoie l'indice du premier (-1 -1) dans le graphe + * @param g un pointeur vers un graphe alloué en mémoire + * @return g->premierVide un entier + * O(1) + */ +int graphe_get_premier_vide(graphe* g) { + return g->premierVide; +} + + +/** Indique si un sommet est dans le graphe + * @param g un pointeur vers un graphe alloué en mémoire + * @param u un entier, le sommet dont on veut savoir s'il est dans le graphe + * @return 1 si le sommet est dans le graphe et 0 sinon + * O(n) + */ +int graphe_contains(graphe* g, int u) { + return (graphe_find(g, u) != -1); +} + + +/** Donne l'indice de la première occurence d'un sommet dans le graphe + * @param g un pointeur vers un graphe alloué en mémoire + * @param u un entier, le sommet dont on veut connaître l'indice dans le graphe + * @return l'indice de le sommet si il est dans le graphe et -1 sinon + * O(n) + */ +int graphe_find(graphe* g, int u) { + if(g && u >= 0 && u <= graphe_get_plus_grand_sommet(g)) { + int i; + for(i = 0; i < graphe_get_premier_vide(g); i++) { + if(g->ligne[i] == u || g->col[i] == u) { + return i; + } + } + } + return -1; +} + + +/** Renvoie le plus grand indice des sommets du graphe + * @param g un pointeur vers un graphe alloué en mémoire + * @return un entier + * O(n) + */ +int graphe_get_plus_grand_sommet(graphe* g) { + int i, tmp, res = -1; + if(g) { + for(i = 0; i < graphe_get_max(g); i++) { + tmp = max(g->ligne[i], g->col[i]); + if(tmp > res) { + res = tmp; + } + } + } + return res; +} + + +/** Ajoute une arête entre les sommets i et j du graphe + * @param g un pointeur vers un graphe alloué en mémoire + * @param i un des sommets de l'arête + * @param j un des sommets de l'arête + */ +void graphe_ajouter_arete(graphe* g, int i, int j) { + if(g) { + if(graphe_get_premier_vide(g) == graphe_get_max(g)) { + g->max *= 2; + g->ligne = realloc(g->ligne, g->max * sizeof(int)); + g->col = realloc(g->col, g->max * sizeof(int)); + for(int i = graphe_get_premier_vide(g); i < graphe_get_max(g); i++) { + g->ligne[i] = -1; + g->col[i] = -1; + } + } + g->ligne[graphe_get_premier_vide(g)] = i; + g->col[graphe_get_premier_vide(g)] = j; + g->premierVide++; + } +} + + +/** Supprime une arête entre les sommets i et j du graphe + * @param g un pointeur vers un graphe alloué en mémoire + * @param i un des sommets de l'arête + * @param j un des sommets de l'arête + * O(n) + */ +void graphe_supprimer_arete(graphe* g, int i, int j) { + if(g) { + int cpt, flag = 0; + for(cpt = 0; cpt < graphe_get_premier_vide(g); cpt++) { + if(flag) { //On a trouvé l'arête et on l'a enlevée, donc maintenant il suffit de décaler le reste d'une case à gauche + g->ligne[cpt] = g->ligne[cpt+1]; + g->col[cpt] = g->col[cpt+1]; + } + else if(g->ligne[cpt] == i && g->col[cpt] == j) { + flag = 1; + g->ligne[cpt] = g->ligne[cpt+1]; + g->col[cpt] = g->col[cpt+1]; + } + } + if(flag) { + g->premierVide--; + } + } +} + + +/** Retire un sommet du graphe, assure qu'aucun sommet n'a d'arête allant vers ce dernier et qu'aucune arête ne part de celui ci. + * Puis les sommets supérieurs sont renommés, ex : si x = 5, 6->5, 7->6 etc + * @param g un pointeur vers un graphe alloué en mémoire + * @param x le sommet à retirer + * O(n) + */ +void graphe_retirer_sommet(graphe* g, int x) { + if(g) { + int n = graphe_get_plus_grand_sommet(g), i; + if(x >= 0 && x < n) { + int nbCasesVides = 0, *casesVides = (int*) calloc(sizeof(int), graphe_get_max(g)); + if(!casesVides) { + printf("Erreur allocation graphe_retirer_sommet (tableau casesVides)\n"); + return; + } + for(i=0; i < graphe_get_max(g); i++) { + casesVides[i]--; + } + for(i=0; i < graphe_get_max(g); i++) { + if(g->ligne[i] == x || g->col[i] == x) { //Une arête part de x, ou arrive en x, on retire le sommet donc tout part + g->ligne[i] = -1; + g->col[i] = -1; + casesVides[nbCasesVides++] = i; + } + else { // Rien n'est supprimé, mais les indices des sommets > x sont décrémentés + if(g->ligne[i] > x) { + g->ligne[i]--; + } + if(g->col[i] > x) { + g->col[i]--; + } + } + } + g->premierVide -= nbCasesVides; + //Maintenant on a aucune trace du sommet x, mais plein de (-1 -1) où il était (ATTENTION ON PEUT ENCORE VOIR DES SOMMETS X, MAIS CE SONT LES ANCIENS SOMMETS X+1, CECI N'EST PAS UN BUG, NE PAS DÉBUGGER) + //Il faut retasser tout ça + + for(i = graphe_get_max(g) - 1; i >= 0 && nbCasesVides; i--) { + if(g->ligne[i] != -1) { //Pas une case vide, donc on peut l'échanger avec une case vide + g->ligne[casesVides[nbCasesVides-1]] = g->ligne[i]; + g->col[casesVides[nbCasesVides-1]] = g->col[i]; + nbCasesVides--; + g->ligne[i] = -1; + g->col[i] = -1; + } + } + free(casesVides); + } + } +} + + +/** Indique si 2 sommets sont adjacents ou non, s'il existe une arête les reliant + * @param g un pointeur vers un graphe alloué en mémoire + * @param u un des sommets de l'arête + * @param v un des sommets de l'arête + * @return 1 si les sommets sont adjacents et 0 sinon + * O(n) + */ +int graphe_voisins(graphe* g, int u, int v) { + if(g) { + int max = graphe_get_plus_grand_sommet(g); + if(u >= 0 && v >= 0 && u <= max && v <= max) { + int i; + for(i = 0; i < graphe_get_premier_vide(g); i++) { + if(g->ligne[i] == u && g->col[i] == v) { + return 1; + } + if(g->ligne[i] == v && g->col[i] == u) { + return 1; + } + } + } + } + return 0; +} + + +/** Indique si 1 sommet est successeur d'un autre ou non + * @param g un pointeur vers un graphe alloué en mémoire + * @param u un des sommets de l'arête + * @param v un des sommets de l'arête + * @return 1 si v est un successeur de u et 0 sinon + * O(n) + */ +int graphe_est_succ(graphe* g, int u, int v) { + if(g) { + int max = graphe_get_plus_grand_sommet(g); + if(u >= 0 && v >= 0 && u <= max && v <= max) { + int i; + for(i = 0; i < graphe_get_premier_vide(g); i++) { + if(g->ligne[i] == u && g->col[i] == v) { + return 1; + } + } + } + } + return 0; +} + + +/** Affiche le graphe sous la forme d'une matrice d'adjacence + * @param g un pointeur vers un graphe alloué en mémoire + * O(n) + */ +void graphe_afficher(graphe* g) { + if(g) { + printf("Graphe à %d arêtes\n", graphe_get_premier_vide(g)); + int lim = graphe_get_plus_grand_sommet(g)+1, i, v, w; + int* matrice = (int*) calloc(sizeof(int), lim * lim); + if(!matrice) { + printf("Erreur allocation graphe_afficher \n"); + return; + } + for(i = 0; i < g->premierVide; i++) { + matrice[g->ligne[i] * lim + g->col[i]]++; + // pas dans les 2 sens matrice[g->col[i] * lim + g->ligne[i]]++; + } + /* matrice d'adjacence faite */ + /* ligne indices colonnes */ + printf("\t\t"); + for(w = 0 ; w < lim ; w++) { + printf("%d\t", w); + } + printf("\n"); + + printf("\t\t"); + for(w = 0 ; w < lim ; w++) { + printf("_\t"); + } + printf("\n"); + + /* lignes de la matrice */ + for(v = 0 ; v < lim ; v++) { + printf("%d\t|\t", v); + for(w = 0 ; w < lim ; w++) { + printf("%d\t", matrice[v * lim + w]); + } + printf("|\n"); + } + printf("\t\t"); + for(w = 0 ; w < lim ; w++) { + printf("_\t"); + } + printf("\n"); + free(matrice); + } + else { + printf("Erreur graphe_afficher Le graphe est NULL\n"); + } +} + + +/** Affiche le graphe sous la forme des 2 tableaux le composant + * @param g un pointeur vers un graphe alloué en mémoire + * O(n) + */ +void graphe_afficher_tableaux(graphe* g) { + int i; + printf("\n======================"); + printf("\nIndices\t\t"); + for(i = 0; i < graphe_get_max(g); i++) { + printf("| %2d ", i); + } + printf("\n\nlignes :\t"); + for(i = 0; i < graphe_get_max(g); i++) { + if(i < graphe_get_premier_vide(g)) { + printf("| %2d ", g->ligne[i]); + } + else { //Il ne devrait y avoir que des (-1) + printf("| (%d) ", g->ligne[i]); + } + } + printf("\ncolonnes :\t"); + for(i = 0; i < graphe_get_max(g); i++) { + if(i < graphe_get_premier_vide(g)) { + printf("| %2d ", g->col[i]); + } + else { //Il ne devrait y avoir que des (-1) + printf("| (%d) ", g->col[i]); + } + } + printf("\n======================"); + printf("\n\n"); +} + + +/** Convertit un graphe au format DOT dans un fichier + * dot -Tpdf fichier.dot -o fichier.pdf + * @param g un graphe alloué en mémoire + * @param nomFichier, le nom du fichier où l'on veut écrire le graphe + * @return 0 si le graphe a bien été écris au format DOT, + * -1 s'il y a eu une erreur d'ouverture du fichier, et -2 sinon + * O(n) + */ +int graphe_ecrire_dot(graphe* g, char* nomFichier) { + if(g) { + int i, nbSommets = 0, n = graphe_get_premier_vide(g), sommetMax = graphe_get_plus_grand_sommet(g); + if(!n) { + printf("Erreur premier vide = 0, graphe vide graphe_ecrire_dot\n"); + return -2; + } + /* + On utilise 2 tableaux pour avoir une liste des sommets dans le graphe. + Soit n = graphe_get_premier_vide(g), il y a au max 2n sommets différents + (Si tous les sommets dans le tableau des lignes sont distincts et pointent chacun vers un sommets distinct) + Donc c'est le nombre max de sommets dans le fichier DOT. + + Et les valeurs des sommets sont entre 0 et graphe_get_plus_grand_sommet(g) inclus + */ + + int* flagSommet = (int *) calloc(sizeof(int), sommetMax + 1); + /* + flagSommet[i] = + 0 : sommet pas dans le graphe + 1 : sommet dans le graphe, il faudra l'ajouter dans le fichier dot + Toutes les valeurs entières entre 0 et sommetMax inclus peuvent être dans le graphe + */ + if(!flagSommet) { + printf("Erreur allocation flagSommet graphe_ecrire_dot\n"); + return -2; + } + int* listeSommets = (int *) calloc(sizeof(int), 2*n); + /* + listeSommets[i] : + -1 : sommet pas dans le graphe + >= 0 : sommet dans le graphe, il faudra l'ajouter dans le fichier dot + Il y a au max 2n sommets différents + */ + if(!listeSommets) { + printf("Erreur allocation listeSommets graphe_ecrire_dot\n"); + free(flagSommet); + return -2; + } + + for(i = 0; i < 2*n; i++) { + listeSommets[i]--; //-1 partout + } + FILE *f = fopen(nomFichier, "w"); + if (!f) { + free(flagSommet); + free(listeSommets); + printf("Erreur ouverture fichier \"%s\"graphe_ecrire_dot\n", nomFichier); + return -1; + } + for(i = 0; i < n; i++) { + if(!flagSommet[g->ligne[i]]) { //g->ligne[i] valait 0, donc le sommet n'était pas découvert + flagSommet[g->ligne[i]] = 1; + listeSommets[nbSommets++] = g->ligne[i]; + } + if(!flagSommet[g->col[i]]) { //g->col[i] valait 0, donc le sommet n'était pas découvert + flagSommet[g->col[i]] = 1; + listeSommets[nbSommets++] = g->col[i]; + } + } + + //Dans flagSommet, on a tous les flags indiquant si un sommet est dans le graphe ou non + //Dans listeSommets on a la liste des sommets distincts + fputs("graph {\n", f); + for(i = 0; i < nbSommets; i++) { + fprintf(f, "\t%d;\n", listeSommets[i]); + } + fputs("\n", f); + for(i = 0; i < n; i++) { + fprintf(f, "\t%d -- %d;\n", g->ligne[i], g->col[i]); + } + fputs("}\n", f); + free(flagSommet); + free(listeSommets); + fclose(f); + return 0; + } + printf("Erreur pagerank NULL graphe_ecrire_dot\n"); + return -2; +} + + +/** Renvoie le maximum entre 2 entiers + * @param x un entier + * @param y un entier + * @return un entier, le plus grand des 2 paramètres + * O(1) + */ +int max(int x, int y) { + return (x > y) ? x : y; +} diff --git a/pagerank/src/graphe.h b/pagerank/src/graphe.h new file mode 100644 index 0000000000000000000000000000000000000000..f7adac23959b87e8e5b6f820a681f601d8f95fd7 --- /dev/null +++ b/pagerank/src/graphe.h @@ -0,0 +1,151 @@ +#ifndef GRAPHE_H +#define GRAPHE_H + + +struct s_graphe { + int max; // Longueur de chacun des tableaux + int premierVide; // Indice du premier (-1 -1) + int *ligne; // Indices i {-1 si non défini, entre 0 et n-1 sinon} + int *col; // Indices j {-1 si non défini, entre 0 et n-1 sinon} +}; +typedef struct s_graphe graphe; + +/** Fonction qui crée un graphe vide qui sera stocké à l'adresse g déjà allouée + * @param g un pointeur vers un graphe alloué en mémoire + * @param n le nombre strictement positif de cases des tableaux du graphe + * @requires n > 0 + * @return 0 si le graphe a été créé et -1 si n <= 0 et -2 s'il y a eu une erreur d'allocation + * O(n) + */ +int creer_graphe(graphe* g, int n); + + +/** Libère la mémoire allouée dans un graphe, mais pas le graphe lui même (le pointeur donné en paramètre reste à libérer + * @param g un pointeur vers un graphe alloué en mémoire + * O(n); + */ +void graphe_detruire(graphe* g); + + +/** Renvoie la taille maximale actuelle des tableaux du graphe g + * @param g un pointeur vers un graphe alloué en mémoire + * @return g->max un entier + * O(1) + */ +int graphe_get_max(graphe* g); + + +/** Renvoie l'indice du premier (-1 -1) dans le graphe + * @param g un pointeur vers un graphe alloué en mémoire + * @return g->premierVide un entier + * O(1) + */ +int graphe_get_premier_vide(graphe* g); + + +/** Indique si un sommet est dans le graphe + * @param g un pointeur vers un graphe alloué en mémoire + * @param u un entier, le sommet dont on veut savoir s'il est dans le graphe + * @return 1 si le sommet est dans le graphe et 0 sinon + * O(n) + */ +int graphe_contains(graphe* g, int u); + + +/** Donne l'indice de la première occurence d'un sommet dans le graphe + * @param g un pointeur vers un graphe alloué en mémoire + * @param u un entier, le sommet dont on veut connaître l'indice dans le graphe + * @return l'indice de le sommet si il est dans le graphe et -1 sinon + * O(n) + */ +int graphe_find(graphe* g, int u); + + +/** Renvoie le plus grand indice des sommets du graphe + * @param g un pointeur vers un graphe alloué en mémoire + * @return un entier + * O(n) + */ +int graphe_get_plus_grand_sommet(graphe* g); + + +/** Ajoute une arête entre les sommets i et j du graphe + * @param g un pointeur vers un graphe alloué en mémoire + * @param i un des sommets de l'arête + * @param j un des sommets de l'arête + * O(1) + */ +void graphe_ajouter_arete(graphe* g, int i, int j); + + +/** Supprime une arête entre les sommets i et j du graphe + * @param g un pointeur vers un graphe alloué en mémoire + * @param i un des sommets de l'arête + * @param j un des sommets de l'arête + * O(n) + */ +void graphe_supprimer_arete(graphe* g, int i, int j); + + +/** Retire un sommet du graphe, assure qu'aucun sommet n'a d'arête allant vers ce dernier et qu'aucune arête ne part de celui ci. + * Puis les sommets supérieurs sont renommés, ex : si x = 5, 6->5, 7->6 etc + * @param g un pointeur vers un graphe alloué en mémoire + * @param x le sommet à retirer + * O(n) + */ +void graphe_retirer_sommet(graphe* g, int x); + + +/** Indique si 2 sommets sont adjacents ou non, s'il existe une arête les reliant + * @param g un pointeur vers un graphe alloué en mémoire + * @param u un des sommets de l'arête + * @param v un des sommets de l'arête + * @return 1 si les sommets sont adjacents et 0 sinon + * O(n) + */ +int graphe_voisins(graphe* g, int u, int v); + + +/** Indique si 1 sommet est successeur d'un autre ou non + * @param g un pointeur vers un graphe alloué en mémoire + * @param u un des sommets de l'arête + * @param v un des sommets de l'arête + * @return 1 si v est un successeur de u et 0 sinon + * O(n) + */ +int graphe_est_succ(graphe* g, int u, int v); + + +/** Affiche le graphe sous la forme d'une matrice d'adjacence + * @param g un pointeur vers un graphe alloué en mémoire + * O(n) + */ +void graphe_afficher(graphe* g); + + +/** Affiche le graphe sous la forme des 2 tableaux le composant + * @param g un pointeur vers un graphe alloué en mémoire + * O(n) + */ +void graphe_afficher_tableaux(graphe* g); + + +/** Convertit un graphe au format DOT dans un fichier + * @param g un graphe alloué en mémoire + * @param nomFichier, le nom du fichier où l'on veut écrire le graphe + * @return 0 si le graphe a bien été écris au format DOT, + * -1 s'il y a eu une erreur d'ouverture du fichier, et -2 sinon + * O(n) + */ +int graphe_ecrire_dot(graphe* g, char* nomFichier); + + +/** Renvoie le maximum entre 2 entiers + * @param x un entier + * @param y un entier + * @return un entier, le plus grand des 2 paramètres + * O(1) + */ +int max(int x, int y); + +#endif diff --git a/pagerank/src/liste_url.c b/pagerank/src/liste_url.c new file mode 100644 index 0000000000000000000000000000000000000000..1ee2e2087cf2ceeedb7f19d215b4963421e512c5 --- /dev/null +++ b/pagerank/src/liste_url.c @@ -0,0 +1,189 @@ +#include +#include +#include +#include "liste_url.h" + + +/** Fonction qui crée une liste d'url qui est stockée à l'adresse donnée en argument, déjà allouée, la liste aura déjà n cases pour stocker des URLs (c'est un tableau dynamique, sa taille pourra être amenée à augmenter) + * @param l une liste d'url allouée en mémoire + * @param n un entier strictement positif, la taille de la liste + * @return 0 si la liste a été créée et -1 si n <= 0 et -2 s'il y a eu une erreur d'allocation + * O(n) + */ +int creer_liste(liste_url* l, int n) { + if(n <= 0) { + printf("Erreur paramètre creer_liste, n <= 0\n"); + return -1; + } + if(!(l->urls = (char**) calloc(sizeof(char*), n))) { + printf("Erreur allocation creer_liste, l->urls\n"); + return -2; + } + l->max = n; + l->premierNULL = 0; + return 0; +} + + +/** Renvoie la taille maximale actuelle de la liste l + * @param l un pointeur vers une liste allouée en mémoire + * @return g->max un entier + * O(1) + */ +int liste_get_max(liste_url* l) { + return l->max; +} + + +/** Renvoie l'indice du premier NULL dans le liste + * @param l un pointeur vers une liste allouée en mémoire + * @return l->premierNULL un entier + * O(1) + */ +int liste_get_premier_NULL(liste_url* l) { + return l->premierNULL; +} + + +/** Indique si une url est dans la liste + * @param l un pointeur vers une liste allouée en mémoire + * @param s une chaîne, l'URL dont on veut savoir si elle est dans la liste + * @return 1 si l'URL est dans la liste et 0 sinon + * O(n) + */ +int liste_contains(liste_url* l, char* s) { + return (liste_find(l, s) != -1); +} + + +/** Donne l'indice d'une url est dans la liste + * @param l un pointeur vers une liste allouée en mémoire + * @param s une chaîne, l'URL dont on veut connaître l'indice dans la liste + * @return l'indice de l'URL si elle est dans la liste et -1 sinon + * O(n) + */ +int liste_find(liste_url* l, char* s) { + if(l && s) { + int i; + for(i = 0; i < liste_get_premier_NULL(l); i++) { + if(!strcmp(s, liste_get_i(l, i))) { + return i; + } + } + } + return -1; +} + + +/** Renvoie l'url à l'indice i dans la liste + * @param l un pointeur vers une liste allouée en mémoire + * @param i un entier + * @return l->urls[i] une chaîne de caractères si 0 <= i < liste_get_max(l), NULL sinon + * O(1) + */ +char* liste_get_i(liste_url* l, int i) { + if(0 <= i && i < liste_get_max(l)) { + return l->urls[i]; + } + return NULL; +} + + +/** Ajoute une url à la première case vide dans la liste + * @param l un pointeur vers une liste allouée en mémoire + * @param s une chaîne, l'URL à ajouter + * O(1) + */ +void liste_add(liste_url* l, char* s) { + if(l) { + if(liste_get_premier_NULL(l) == liste_get_max(l)) { //Réallocation si nécessaire, complexité amortie en temps linéaire + l->max *= 2; + char** nouvelleListe = (char**) calloc(sizeof(char*), liste_get_max(l)); + + for (int i = 0; i < liste_get_premier_NULL(l); i++) { + nouvelleListe[i] = (char*) malloc(sizeof(char) * (strlen(l->urls[i]) + 1)); + strcpy(nouvelleListe[i], l->urls[i]); + free(l->urls[i]); + } + nouvelleListe[l->premierNULL] = (char*) calloc(sizeof(char), strlen(s)+1); + strcpy(nouvelleListe[l->premierNULL++], s); + free(l->urls); + l->urls = nouvelleListe; + return; + } + l->urls[l->premierNULL] = (char*) calloc(sizeof(char), strlen(s)+1); + strcpy(l->urls[l->premierNULL++], s); + } +} + + +/** Retire une url de la liste + * @param l un pointeur vers une liste allouée en mémoire + * @param s une chaîne, l'URL à retirer + * O(n) + */ +void liste_remove(liste_url* l, char* s) { + if(l && s) { + int i, trouve = 0; + for(i = 0; i < liste_get_premier_NULL(l); i++) { + if(!trouve && (!strcmp(s, liste_get_i(l, i)))) { + free(liste_get_i(l, i)); + if(i+1 < liste_get_max(l)) { + l->urls[i] = l->urls[i+1]; + } + else { + l->urls[i] = NULL; + } + trouve = 1; + } + else if(trouve) { + if(i+1 < liste_get_max(l)) { + l->urls[i] = l->urls[i+1]; + } + else { + l->urls[i] = NULL; + } + } + } + l->premierNULL--; + } +} + + +/** Libère la mémoire allouée dans une liste et toutes les chaînes contenues dedans + * @param l une liste d'url allouée en mémoire + * O(n) + */ +void liste_detruire(liste_url* l) { + if(l) { + int i; + for(i = 0; i < liste_get_premier_NULL(l); i++) { + free(l->urls[i]); + l->urls[i] = NULL; + } + free(l->urls); + } +} + + +/** Affiche la liste d'URLs + * @param l un pointeur vers une liste allouée en mémoire + * O(n) + */ +void liste_afficher(liste_url* l) { + if(l) { + int i, n = liste_get_max(l); + printf("i\t|\tURLs:\n"); + for(i = 0; i < n; i++) { + if(i < liste_get_premier_NULL(l)) { + printf("%d \t|\t\"%s\"\n", i, liste_get_i(l, i)); + } + else { + printf("%d \t|\tNULL\n", i); + } + } + } + else { + printf("La liste est NULL\n"); + } +} diff --git a/pagerank/src/liste_url.h b/pagerank/src/liste_url.h new file mode 100644 index 0000000000000000000000000000000000000000..476bb56df1509fd325a14201637996678b9725f4 --- /dev/null +++ b/pagerank/src/liste_url.h @@ -0,0 +1,95 @@ +#ifndef LISTE_URL_H +#define LISTE_URL_H + + +struct s_liste_url { + int max; // Longueur de la liste allouée + int premierNULL; // Indice de la première chaîne NULL + char** urls; // Les urls de la liste +}; +typedef struct s_liste_url liste_url; + + +/** Fonction qui crée une liste d'url qui est stockée à l'adresse donnée en argument, déjà allouée, la liste aura déjà n cases pour stocker des URLs (c'est un tableau dynamique, sa taille pourra être amenée à augmenter) + * @param l une liste d'url allouée en mémoire + * @param n un entier strictement positif, la taille de la liste + * @return 0 si la liste a été créée et -1 si n <= 0 et -2 s'il y a eu une erreur d'allocation + * O(n) + */ +int creer_liste(liste_url* l, int n); + + +/** Renvoie la taille maximale actuelle de la liste l + * @param l un pointeur vers une liste allouée en mémoire + * @return g->max un entier + * O(1) + */ +int liste_get_max(liste_url* l); + + +/** Renvoie l'indice du premier NULL dans le liste + * @param l un pointeur vers une liste allouée en mémoire + * @return l->premierNULL un entier + * O(1) + */ +int liste_get_premier_NULL(liste_url* l); + + +/** Indique si une url est dans la liste + * @param l un pointeur vers une liste allouée en mémoire + * @param s une chaîne, l'URL dont on veut savoir si elle est dans la liste + * @return 1 si l'URL est dans la liste et 0 sinon + * O(n) + */ +int liste_contains(liste_url* l, char* s); + + +/** Donne l'indice d'une url est dans la liste + * @param l un pointeur vers une liste allouée en mémoire + * @param s une chaîne, l'URL dont on veut connaître l'indice dans la liste + * @return l'indice de l'URL si elle est dans la liste et -1 sinon + * O(n) + */ +int liste_find(liste_url* l, char* s); + + +/** Renvoie l'url à l'indice i dans la liste + * @param l un pointeur vers une liste allouée en mémoire + * @param i un entier + * @return l->urls[i] une chaîne de caractères si 0 <= i < liste_get_max(l), NULL sinon + * O(1) + */ +char* liste_get_i(liste_url* l, int i); + + +/** Ajoute une url à la première case vide dans la liste + * @param l un pointeur vers une liste allouée en mémoire + * @param s une chaîne, l'URL à ajouter + * O(1) + */ +void liste_add(liste_url* l, char* s); + + +/** Retire une url de la liste + * @param l un pointeur vers une liste allouée en mémoire + * @param s une chaîne, l'URL à retirer + * O(n) + */ +void liste_remove(liste_url* l, char* s); + + + +/** Libère la mémoire allouée dans une liste et toutes les chaînes contenues dedans + * @param l une liste d'url allouée en mémoire + * O(n) + */ +void liste_detruire(liste_url* l); + + +/** Affiche la liste d'URLs + * @param l un pointeur vers une liste allouée en mémoire + * O(n) + */ +void liste_afficher(liste_url* l); + +#endif diff --git a/pagerank/test/Makefile b/pagerank/test/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..4aeb4da71cca427511068028febe21e3b25f3434 --- /dev/null +++ b/pagerank/test/Makefile @@ -0,0 +1,14 @@ +CC= gcc +CXXFLAGS= -Wall --pedantic -O3 + +CPP_O_FILE = test-graphe.o test-liste.o test-pagerank.o +LIB = -lm + + +all: $(CPP_O_FILE) + $(CC) $(CXXFLAGS) -o test-graphe.exe test-graphe.c ../src/graphe.c $(LIB) + $(CC) $(CXXFLAGS) -o test-liste.exe test-liste.c ../src/liste_url.c $(LIB) + $(CC) $(CXXFLAGS) -o test-pagerank.exe test-pagerank.c ../src/pagerank.c ../src/graphe.c ../src/liste_url.c $(LIB) + +clean: + rm -rf *.o *.exe diff --git a/pagerank/test/test-graphe.c b/pagerank/test/test-graphe.c new file mode 100644 index 0000000000000000000000000000000000000000..be169a18b3a0383e703aa18957d1ae840b78a1ca --- /dev/null +++ b/pagerank/test/test-graphe.c @@ -0,0 +1,145 @@ +#include +#include +#include +#include "../src/graphe.h" + +/* +dot -Tpdf graphe_1.dot -o graphe_1.pdf +dot -Tpdf graphe_2.dot -o graphe_2.pdf +dot -Tpdf graphe_3.dot -o graphe_3.pdf +*/ + +#define NB_SOMMETS 7 + +int main() +{ + graphe g; + + char* fichier1 = "graphe_1.dot"; + assert(fichier1); + char* fichier2 = "graphe_2.dot"; + assert(fichier2); + char* fichier3 = "graphe_3.dot"; + assert(fichier3); + + creer_graphe(&g, NB_SOMMETS); + assert(g.max > 0); + assert(g.premierVide == 0); + assert(g.ligne); + assert(g.col); + + graphe_ajouter_arete(&g, 0, 1); + assert(g.ligne[0] == 0); + assert(g.col[0] == 1); + assert(g.premierVide == 1); + assert(graphe_get_plus_grand_sommet(&g) == 1); + + graphe_afficher_tableaux(&g); + graphe_ajouter_arete(&g, 0, 9); + assert(g.ligne[1] == 0); + assert(g.col[1] == 9); + assert(g.premierVide == 2); + assert(graphe_get_plus_grand_sommet(&g) == 9); + + //graphe_afficher_tableaux(&g); + graphe_ajouter_arete(&g, 1, 3); + assert(g.ligne[2] == 1); + assert(g.col[2] == 3); + assert(g.premierVide == 3); + assert(graphe_get_plus_grand_sommet(&g) == 9); + + //graphe_afficher_tableaux(&g); + graphe_ajouter_arete(&g, 1, 2); + assert(g.ligne[3] == 1); + assert(g.col[3] == 2); + assert(g.premierVide == 4); + assert(graphe_get_plus_grand_sommet(&g) == 9); + + //graphe_afficher_tableaux(&g); + graphe_ajouter_arete(&g, 1, 4); + assert(g.ligne[4] == 1); + assert(g.col[4] == 4); + assert(g.premierVide == 5); + assert(graphe_get_plus_grand_sommet(&g) == 9); + + //graphe_afficher_tableaux(&g); + graphe_ajouter_arete(&g, 2, 4); + assert(g.ligne[5] == 2); + assert(g.col[5] == 4); + assert(g.premierVide == 6); + assert(graphe_get_plus_grand_sommet(&g) == 9); + + //graphe_afficher_tableaux(&g); + graphe_ajouter_arete(&g, 3, 4); + assert(g.ligne[6] == 3); + assert(g.col[6] == 4); + assert(g.premierVide == 7); + assert(graphe_get_plus_grand_sommet(&g) == 9); + + //graphe_afficher_tableaux(&g); + graphe_ajouter_arete(&g, 3, 5); + assert(g.ligne[7] == 3); + assert(g.col[7] == 5); + assert(g.premierVide == 8); + assert(graphe_get_plus_grand_sommet(&g) == 9); + + //graphe_afficher_tableaux(&g); + graphe_ajouter_arete(&g, 4, 5); + assert(g.ligne[8] == 4); + assert(g.col[8] == 5); + assert(g.premierVide == 9); + assert(graphe_get_plus_grand_sommet(&g) == 9); + + //graphe_afficher_tableaux(&g); + graphe_ajouter_arete(&g, 4, 6); + assert(g.ligne[9] == 4); + assert(g.col[9] == 6); + assert(g.premierVide == 10); + assert(graphe_get_plus_grand_sommet(&g) == 9); + + graphe_afficher_tableaux(&g); + graphe_ajouter_arete(&g, 5, 6); + assert(g.ligne[10] == 5); + assert(g.col[10] == 6); + assert(g.premierVide == 11); + assert(graphe_get_plus_grand_sommet(&g) == 9); + + graphe_afficher_tableaux(&g); + + graphe_afficher(&g); + printf("Le graphe %s le sommet 9, indice : %d\n", (graphe_contains(&g, 9) ? "contient" : "ne contient pas"), graphe_find(&g, 9)); + + printf("On stocke le graphe tel qu'il est après tous les ajouts dans le fichier %s\n", fichier1); + graphe_ecrire_dot(&g, fichier1); + + printf("Retirons le sommet 4 (un nouveau sommet 4 prendra sa place\n"); + + graphe_retirer_sommet(&g, 4); + assert(graphe_get_plus_grand_sommet(&g) == 8); + + printf("On stocke le graphe tel qu'il est après la suppression du sommet 4 dans le fichier %s\n", fichier2); + graphe_ecrire_dot(&g, fichier2); + + printf("Retirons les arêtes 4-5, 1-3, et 5-4 (déjà retirée)\n"); + graphe_supprimer_arete(&g, 4, 5); + graphe_afficher(&g); + graphe_supprimer_arete(&g, 1, 3); + graphe_afficher(&g); + graphe_supprimer_arete(&g, 5, 4); + graphe_afficher(&g); + + printf("Les sommets 1 et 7 %s\n", (graphe_voisins(&g, 1, 7) ? "sont voisins" : "ne sont pas voisins")); + assert(!graphe_voisins(&g, 1, 7)); + printf("Ajoutons une arête 1-7\n"); + graphe_ajouter_arete(&g, 1, 7); + printf("Les sommets 1 et 7 %s\n", (graphe_voisins(&g, 1, 7) ? "sont voisins" : "ne sont pas voisins")); + assert(graphe_voisins(&g, 1, 7)); + graphe_afficher(&g); + printf("Le graphe %s le sommet 9, indice : %d\n", (graphe_contains(&g, 9) ? "contient" : "ne contient pas"), graphe_find(&g, 9)); + + printf("On stocke le graphe tel qu'il est après des retraits et l'ajout du sommet 7 dans le fichier %s\n", fichier3); + graphe_ecrire_dot(&g, fichier3); + + graphe_detruire(&g); + return EXIT_SUCCESS; +} diff --git a/pagerank/test/test-liste.c b/pagerank/test/test-liste.c new file mode 100644 index 0000000000000000000000000000000000000000..32a212f022b8c2d3dc37b3f1ba77561304d7292a --- /dev/null +++ b/pagerank/test/test-liste.c @@ -0,0 +1,46 @@ +#include +#include +#include +#include +#include "../src/liste_url.h" + + +int main() +{ + liste_url l; + creer_liste(&l, 1); + liste_add(&l, "www.google.com"); + assert(!strcmp(l.urls[0], "www.google.com")); + + liste_add(&l, "www.gogol.com"); + assert(!strcmp(l.urls[1], "www.gogol.com")); + + liste_add(&l, "file://C:\\Users\\Nicolas\\Homework"); + assert(!strcmp(l.urls[2], "file://C:\\Users\\Nicolas\\Homework")); + + liste_add(&l, "lipn.univ-paris13.fr/"); + assert(!strcmp(l.urls[3], "lipn.univ-paris13.fr/")); + + liste_add(&l, "https://lipn.univ-paris13.fr/accueil/presentation/le-laboratoire/"); + assert(!strcmp(l.urls[4], "https://lipn.univ-paris13.fr/accueil/presentation/le-laboratoire/")); + + + liste_afficher(&l); + + printf("La première URL est %s\n", liste_get_i(&l, 0)); + + printf("La liste %s l'URL \"www.google.com\", indice : %d\n", (liste_contains(&l, "www.google.com") ? "contient" : "ne contient pas"), liste_find(&l, "www.google.com")); + assert(liste_contains(&l, "www.google.com")); + + printf("Retirons la première URL, celle de google\n"); + + liste_remove(&l, "www.google.com"); + assert(strcmp(l.urls[0], "www.google.com")); //strcmp != 0 => chaînes différentes (inférieures ou supérieures) + + liste_afficher(&l); + printf("La liste %s l'URL \"www.google.com\", indice : %d\n", (liste_contains(&l, "www.google.com") ? "contient" : "ne contient pas"), liste_find(&l, "www.google.com")); + assert(!liste_contains(&l, "www.google.com")); + + liste_detruire(&l); + return EXIT_SUCCESS; +}