From 0ea0bfb2d89ebff123865d7ab843143198a1a20b Mon Sep 17 00:00:00 2001 From: Nicolas Gresset-Bourgeois Date: Fri, 28 Jun 2024 10:32:50 +0200 Subject: [PATCH] one big commit --- .gitignore | 3 +- Makefile | 15 +- README.md | 14 +- include/algo.h | 32 +-- include/array-list.h | 70 +---- include/display.h | 16 +- include/erreur.h | 14 + include/fonctions.h | 18 +- include/options.h | 2 +- include/parse.h | 119 +------- include/pile.h | 14 +- include/stat.h | 13 +- include/struct.h | 146 +++++----- src/algo.c | 319 +++++++++++----------- src/arbres.c | 104 ------- src/array-list.c | 132 +++------ src/display.c | 303 ++++++++++---------- src/erreur.c | 8 + src/fonctions.c | 89 +++--- src/main.c | 87 ++++++ src/options.c | 189 +++++++------ src/parse.c | 485 ++++++++++++++------------------- src/pile.c | 44 +-- src/restrictiveOptions.c | 132 --------- src/restrictiveOptions.c.bakup | 157 +++++++++++ src/stat.c | 228 ++++++++-------- test/test | Bin 0 -> 36896 bytes test/test.c | 12 + test/tst.h | 277 +++++++++++++++++++ 29 files changed, 1543 insertions(+), 1499 deletions(-) create mode 100644 include/erreur.h delete mode 100644 src/arbres.c create mode 100644 src/erreur.c create mode 100644 src/main.c delete mode 100644 src/restrictiveOptions.c create mode 100644 src/restrictiveOptions.c.bakup create mode 100755 test/test create mode 100644 test/test.c create mode 100644 test/tst.h diff --git a/.gitignore b/.gitignore index 4eb2a41..d2408fa 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ /bin/ /.vscode/ docs/ -latex/ \ No newline at end of file +latex/ +out/ \ No newline at end of file diff --git a/Makefile b/Makefile index 4881c1a..d2bfd8a 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,9 @@ CC ?= gcc -CFLAGS ?= -Wall -g -Wextra -O1 -LDLIBS ?= -lm -ltps -lSDL2 -lSDL2_ttf +CFLAGS ?= -Wall -g -Wextra -O3 +LDLIBS ?= -lm INCLUDE_PATH = ./include -TARGET =arbres +TARGET =main SRCDIR = src OBJDIR = obj @@ -14,6 +14,10 @@ SOURCES := $(wildcard $(SRCDIR)/*.c) INCLUDES := $(wildcard $(INCLUDE_PATH)/*.h) OBJECTS := $(SOURCES:$(SRCDIR)/%.c=$(OBJDIR)/%.o) +TEST_DIR = test +TEST_SRC = $(TEST_DIR)/test.c +TEST_BIN = $(TEST_DIR)/test + $(BINDIR)/$(TARGET): $(OBJECTS) mkdir -p $(BINDIR) @@ -27,6 +31,11 @@ $(OBJECTS): $(OBJDIR)/%.o : $(SRCDIR)/%.c doc: doxygen Doxyfile +test: $(TEST_BIN) + +$(TEST_BIN): $(TEST_SRC) + $(CC) $(CFLAGS) -o $(TEST_BIN) $(TEST_SRC) + .PHONY: clean clean: rm -rf obj/*.o diff --git a/README.md b/README.md index 1eeb6d6..ca7bc7a 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,15 @@ # paris-trees-mst -we'll take a dive into the utilization of this program \ No newline at end of file +This project is an academical project which aims to compute minimum spanning tree algorithm on Paris trees from a csv database. + +One year after, I come back on the database to try to enhance coding quality and review my past mistakes. + +## to be modified + +- CLI parsing, use [vrgcli](https://dev.to/rdentato/vrg-for-comand-lines-5hmm), it seems easier +- gui library : use sdl2 instead, more universal and portable + +taking a first look at the basecode shows there are plenty of modifications to be done ! + +A test framework needs to be used, why not [this one](https://github.com/rdentato/tst/blob/main/src/tst.h) +let's write some unit tests diff --git a/include/algo.h b/include/algo.h index e67bebf..aee8280 100644 --- a/include/algo.h +++ b/include/algo.h @@ -3,27 +3,25 @@ #include - /** - * @brief Une fonction qui calcule l'arbre recouvrant minimal du graphe - * structure passé en paramètre - * - * @param forest le graphe dont on calcule l'arbre - * @param size la taille du graphe - * @return arete* : la liste des arètes constituant l'arbre recouvrant - * minimal + * @brief Compute the minimum spanning tree (MST) for the given forest + * considered as a complete graph using Prim's algorithm + * + * @param forest + * @param MST */ -edge* minimumSpanningTree(tree* forest, size_t size); - +void computeMST(forest *forest, MST *MST); /** - * @brief Get the Forest Max Composante object Renvoie la liste d'arbres "forêt" composée de la plus grande composante connexe, - * paramétrée par la distance distmax - * - * @param forest + * @brief Get the Forest Max Composante object Renvoie la liste d'arbres + * "forêt" composée de la plus grande composante connexe, paramétrée par la + * distance distmax + * + * @param forest * @param size la taille de forest - * @param distmax + * @param distmax * @param newsize pointeur vers la nouvelle taille de la forêt - * @return tree* + * @return tree* */ -tree* getForestMaxComposante(tree* forest, size_t size, double distmax, size_t* newsize); \ No newline at end of file +tree *getForestMaxComposante(tree *forest, size_t size, double distmax, + size_t *newsize); \ No newline at end of file diff --git a/include/array-list.h b/include/array-list.h index 80ddd09..7ebb1db 100644 --- a/include/array-list.h +++ b/include/array-list.h @@ -1,81 +1,37 @@ #include -typedef struct list_t list_t ; - - +typedef struct list dynamic_list; /** * @brief Crée une liste vide - * + * * @return list_t* ou NULL en cas de problème */ -list_t* list_create(void); - +dynamic_list *list_create(void); /** * @brief Ajoute un élément à la fin de la liste - * - * @return 0 en cas de problème, 1 sinon - */ -int list_append(list_t*, void*); - -/** - * @brief Ajoute un élément en début de liste - * + * * @return 0 en cas de problème, 1 sinon */ -int list_prepend(list_t*, void*); - +int list_append(dynamic_list *list, size_t elem); /** * @brief retourne le (idx+1)ème élément * comportement indéfini en cas de dépassemnt - * - * @return void* + * + * @return void* */ -void* list_get(list_t*, int); +size_t list_get(dynamic_list *list, int index); +size_t list_pop(dynamic_list *list); /** * @brief remplace l'élément à la position donnée par l'élément donné - * - */ -void list_set(list_t*, int, void*); - -/** - * @brief remplace l'élément à la position donnée par l'élément donné et free l'élément remplacé - * - */ -void list_set2(list_t* it, int pos, void* element); - -/** - * @brief revoie la taille de la liste - * - * @return size_t - */ -size_t list_size(list_t*); - -/** - * @brief insère un élément à la position idx - * - * @return 0 en cas de problème, 1 sinon - */ -int list_insert(list_t*, size_t, void*); - -/** - * @brief retourne l'élément à la position idc, puis enlève cet élément de la liste - * - * @return void* - */ -void* list_take(list_t*, int); - -/** - * @brief libère la liste et tous ses éléments - * + * */ -void list_free(list_t*); - +void list_set(dynamic_list *, int index, size_t new_value); -size_t* create_size_t(size_t i); +size_t list_size(dynamic_list *list); -void list_free2(list_t* it); \ No newline at end of file +void list_free(dynamic_list *list); diff --git a/include/display.h b/include/display.h index 1dc3e68..28bc5db 100644 --- a/include/display.h +++ b/include/display.h @@ -4,14 +4,14 @@ #include -/** - * @brief Une fonction qui permet d'afficher l'arbre recouvrant minimal - * - * @param listeAretes la liste des aretes de l'arbre - * @param size la taille du graphe (la structure) - * @param structure la structure de l'arbre de taille size + 1 - */ -void displayTrees(edge* listeAretes, size_t size, tree* structure); +// /** +// * @brief Une fonction qui permet d'afficher l'arbre recouvrant minimal +// * +// * @param listeAretes la liste des aretes de l'arbre +// * @param size la taille du graphe (la structure) +// * @param structure la structure de l'arbre de taille size + 1 +// */ +// void displayTrees(edge* listeAretes, size_t size, tree* structure); diff --git a/include/erreur.h b/include/erreur.h new file mode 100644 index 0000000..31a31f3 --- /dev/null +++ b/include/erreur.h @@ -0,0 +1,14 @@ +#include +#include + +/** +A convenient way to handle errors : will call raler if condition is true +--> means bad return code + */ +#define CHK(condition) \ + do { \ + if (condition) \ + raler(#condition, __FILE__, __LINE__); \ + } while (0); + +void raler(const char *msg, const char *file, int line); \ No newline at end of file diff --git a/include/fonctions.h b/include/fonctions.h index 8851d39..f667519 100644 --- a/include/fonctions.h +++ b/include/fonctions.h @@ -2,19 +2,13 @@ #include "struct.h" #include #include -#include -#define RAYON 6378137.0 +#define RAYON_TERRE 6378137.0 // exprimé en mètres -/** - * @brief Cette fonction renvoie la distance en mètres entre deux arbres repérés par leurs coordonnées latitude et longitude - * - * @param tree1 premier arbre à comparer - * @param tree2 second arbre à comparer - * @return double - * - */ -double distanceTrees(tree * tree1, tree * tree2); +// renvoie le carré de la distance entre deux arbres (en mètres), sans compter le rayon de la terre +double squaredDistanceTrees(tree * tree1, tree * tree2); + +double distanceTrees(tree *tree1, tree *tree2); /** @@ -31,6 +25,6 @@ double radians(double); * @param forest * @param size */ -void projectionTrees(tree* forest, size_t size); +void equirectangularProjection(forest *forest); void manageActions(tree* forest, size_t size, edge** MST, opt handlingOptions); \ No newline at end of file diff --git a/include/options.h b/include/options.h index 388dcc6..971560b 100644 --- a/include/options.h +++ b/include/options.h @@ -10,4 +10,4 @@ * @param options pointeur vers une structure stockant chaque option mise en argument * */ -void options(int argc, char ** argv, opt * options); \ No newline at end of file +void handleCLI(int argc, char ** argv, opt * options); \ No newline at end of file diff --git a/include/parse.h b/include/parse.h index 3611f69..b29e168 100644 --- a/include/parse.h +++ b/include/parse.h @@ -2,119 +2,6 @@ #include "struct.h" #include - -/** - * @brief Cette fonction vérifie si le genre donné en argument est connu ou non - * - * @param genus genre de l'arbre considéré (string) - * @param id pointeur vers l'identifiant id (char), on lui affecte l'id correspondant au genre obtenu dans tab_corres, on ne fait rien si genre pas dans tab_corres - * @param tab_corres tableau de paires genre/id correspondant des genres déjà connus - * @return renvoie 1 si le genre est connu et affecte l'id correspond, renvoie 0 sinon - */ -int genusKnown(char * genus, char * id, tab_genus * tab_corres); - -/** - * @brief Cette fonction associe à genre un identifiant stocké dans *id - * - * @param genus genre (string) - * @param id pointeur vers id (short) - * @param tab_corres tableau de paires genre/id correspondant des genres déjà connus - */ -void genusId(char * genus, char * id, tab_genus * tab_corres); - -/** - * @brief Récupère le champ Genre d'une ligne et stocke le résultat dans le champ genre (un char) de l'arbre pointé par tree et l'ajoute à tab_corres si non connu - * - * @param line ligne du fichier csv - * @param tree pointeur vers une structure qui stocke les informations d'un arbre - * @param tab_corres tableau des correspondances entre genre et identifiant attribué - */ -void getGenus(char* line, tree * tree, tab_genus * tab_corres); - - -/** - * @brief Récupère le champ Hauteur d'une ligne et stocke le résultat dans le champ hauteur de l'arbre pointé par tree - * - * @param line ligne du fichier csv - * @param tree pointeur vers une structure qui stocke les informations d'un arbre - * - */ -void getHeight(char* line, tree * tree); - -/** - * @brief Récupère le champ PositionX d'une ligne et stocke le résultat dans le champ geoloc.x de l'arbre pointé par tree - * - * @param line ligne du fichier csv - * @param tree pointeur vers une structure qui stocke les informations d'un arbre - * - */ -void getposX(char* line, tree * tree); - -/** - * @brief Récupère le champ PositionY d'une ligne et stocke le résultat dans le champ geoloc.y de l'arbre pointé par tree - * - * @param line ligne du fichier csv - * @param tree pointeur vers une structure qui stocke les informations d'un arbre - * - */ -void getposY(char* line, tree * tree); - - -/** - * @brief Cette fonction trie des données utiles d'un fichier csv - * - * @param file fichier que l'on va trier - * @param forest pointure vers la structure dans lequel on va stocker les données utiles - * @param numberoflines nombre total de lignes du fichier - * - */ -void parseFile(char* file, tree * forest, size_t numberoflines, tab_genus* tab); - -/** - * @brief Cette fonction met les informations d'un arbre dans un fichier binaire - * - * @param structure liste de structures de tous les arbres pris en compte - * @param size taille totale du tableau - * @param save fichier dans lequel on sauvegarde les données de la structure - * - */ -void binaryFile(tree * forest, size_t size, char * save); - -/** - * @brief Cette fonction trie des données utiles d'un fichier binaire - * - * @param file fichier que l'on va trier - * @param forest pointure vers la structure dans lequel on va stocker les données utiles - * @param numberoflines nombre total de lignes du fichier - * - */ -void parseFromBinaryFile(char * file, tree * forest, size_t numberoflines); - - -/** - * @brief Cette fonction permet la sauvegarde de l'association d'un genre (char *) à un identifiant (char) - * - * @param tab tableau de correspondance - */ -void SaveStructGenus(opt handlingOptions,tab_genus tab); - -/** - * @brief Cette fonction vérifie si le fichier est un fichier binaire ou non - * - * @param file chemin vers le fichier binaire ou non - * @return renvoie 0 si le fichier n'est pas binaire, le nombre d'arbres sinon - */ -int isBinary(char * file); - -/** - * @brief Cette fonction gère l'ouverture des fichiers, le parsage, la sauvegarde de la structure etc - * - * @param gestionOptions options entrées en ligne de commande - * @param tree tableau où sont stockées les informations sur les arbres après le parsage - */ -void handlingFile(opt handlingOptions, tree * tree, int * nbTrees); - - /** * @brief Cette fonction écrit dans un fichier l'arbre recouvrant minimal * @@ -122,4 +9,8 @@ void handlingFile(opt handlingOptions, tree * tree, int * nbTrees); * @param arbre L'arbre recouvrant minimal * @param size Sa taille */ -void writeEdges(edge * MST, size_t size, opt handlingOptions); \ No newline at end of file +void writeEdges(edge * MST, size_t size, opt handlingOptions); + + +// load the file parsed in CLI +void loadAndParseFile(opt *option, forest* forest); diff --git a/include/pile.h b/include/pile.h index 69bf646..83cd94d 100644 --- a/include/pile.h +++ b/include/pile.h @@ -1,16 +1,14 @@ #pragma once #include -typedef struct list_t pile_t; +typedef struct pile pile; -pile_t* pile_create(void); +pile* pile_create(void); -void* pop(pile_t* pile); +size_t pop(pile* pile); -int push(pile_t* pile, void* elm); +int push(pile* pile, size_t element); -int is_empty(pile_t* pile); +int is_empty(pile* pile); -void pile_free(pile_t* pile); - -size_t pile_size(pile_t* pile); \ No newline at end of file +void pile_free(pile* pile); diff --git a/include/stat.h b/include/stat.h index 21dc1de..c34b7fb 100644 --- a/include/stat.h +++ b/include/stat.h @@ -3,16 +3,5 @@ #include #include "struct.h" -void stat(opt handlingOptions, edge * MST,size_t size, tree* forest); - -/** - * @brief renvoie le poids de l'arbre recouvrant minimal passé en paramètre - * - * @param MST les aretes constituant l'arbre recouvrant minimal - * @param size sa taille - * @return double le poids de l'arbre recouvrant minimal - */ -double weightMinimumSpanningTree(edge* MST, size_t size); - -double diameterMST(edge* MST, tree* forest, size_t size); +void dumpStats(MST* MST, forest* forest); \ No newline at end of file diff --git a/include/struct.h b/include/struct.h index 568b03a..a1a96e0 100644 --- a/include/struct.h +++ b/include/struct.h @@ -1,117 +1,101 @@ #pragma once -#include #include "array-list.h" +#include + +// why should we have a file with different structures ?? +// it's perfect nonsense for me /** - * @brief structure donnant la localisation d'un arbre + * @brief Allows to store a pair of cooordinates with different names depending + * on the context */ -typedef struct position{ - /// @brief latitude position arbre (en degré) +typedef struct { + union { + double latitude; double x; - /// @brief longitude position arbre (en degré) + } coord_1; + union { + double longitude; double y; + } coord_2; } position; /** * @brief structure informations importantes concernant un arbre */ -typedef struct tree{ - /// @brief struture correspondant à la position de l'arbre - position geoloc; - /// @brief genre de l'arbre - short genus; - /// @brief hauteur de l'arbre - short height; - +typedef struct tree { + position position; + /// index renvoyant au genre (correspondance via une table d'indexation) + short genus; + short height; } tree; +typedef struct { + tree *trees; + size_t size; +} forest; + /** * @brief gestion des options en argument */ -typedef struct opt{ - - int stat; //s - int help; //a - char *in_path; //i - char *out_path; //o - char *span_tree_path; //t - char *location; //l - char *limit_value; //f - char *genus; //g - char *height; //h - char *conserve; //c - char *recharge; //r - +typedef struct opt { -}opt; + int stat; // s + int help; // a + char *in_path; // i + char *out_path; // o + char *span_tree_path; // t + int max_distance; // l + char *limit_value; // f + char *genus; // g + int height; // h + char *genus_conserve_path; // c + char *genus_recharge_path; // r -/** - * @brief structure associant une genre à une identifiant - */ -typedef struct corres_genus{ - /// @brief genre de l'arbre (string) - char genus[30]; - /// @brief identifiant associé - char id; +} opt; - } corres_genus; +#define MAX_GENUS_NAME_SIZE 30 +typedef char genus[MAX_GENUS_NAME_SIZE + 1]; + +#define MAX_GENUS 255 // arbitraty value that seems reasonable /** * @brief structure de tableau et taille associée */ - typedef struct tab_genus{ - /// @brief tableau de paires genre/id correspondant - corres_genus genuses[255]; - /// @brief nombre d'éléments dans le tableau - int size; - - }tab_genus; +typedef struct { + /// @brief tableau de paires genre/id correspondant + genus genuses[MAX_GENUS]; + /// @brief nombre d'éléments dans le tableau + size_t size; +} tab_genus; /** * @brief structure définissant une arête - * + * */ - typedef struct edge{ - /// @brief premier arbre connecté - tree * tree1; - /// @brief deuxième arbre connecté - tree * tree2; +typedef struct { + /// @brief index du premier arbre connecté + size_t tree1; + /// @brief index du deuxième arbre connecté + size_t tree2; } edge; - - -/** - * @brief 2ième structure définissant une arête (pour options) - * - */ -typedef struct edge2{ - size_t tree1; - size_t tree2; -} edge2; +typedef struct { + edge *edges; + size_t size; +} MST; /** * @brief structure représentant un sommet pour la fonction minimumSpanningTree - * + * */ -typedef struct vertex{ - /// @brief 1 si le sommet est déjà dans la composante - short isInComponent; - /// @brief la distance minimale aux autres sommets enregistrée - double distMin; - /// @brief le sommet réalisant cette distance - size_t vertexMin; - } vertex; - -/** - * @brief structure définissant un noeud - * - */ -typedef struct node{ - list_t* neighbors; -} node; - - - - +typedef struct vertex { + /// @brief la distance minimale aux autres sommets enregistrée (vaut 0 si le + /// sommet est dans la composante) + double distanceToComponent; + /// @brief le sommet réalisant cette distance (identifié par son index) + size_t whichVertex; + /// +} vertex; diff --git a/src/algo.c b/src/algo.c index 5fc447b..2af777e 100644 --- a/src/algo.c +++ b/src/algo.c @@ -1,171 +1,186 @@ #include "../include/algo.h" +#include "../include/display.h" #include "../include/fonctions.h" #include "../include/struct.h" #include -#include "../include/display.h" #include +#define INFINI 4000000 // todo modifier par le double maximum -#define INFINI 4000000 - - - -size_t vertexDistMin(vertex* tab, size_t size){ - double distmin = tab[0].distMin; - size_t imin = 0; - for (size_t i = 0; iedges = malloc((numberofedges) * sizeof(edge)); + if (MST->edges == NULL) { + fprintf(stderr, "Allocation error in function computeMST"); + exit(1); + } } - - -/// Pour gérer le cas où on ne veut que la plus grande composante connexe - -/** - * @brief retourne le premier sommet pas encore traité - * - * @param composante - * @param size - * @return size_t - */ -size_t firstNotHandledVertex(short* composante, size_t size){ - for (size_t i = 0; idistanceToComponent == 0; } -/** - * @brief Ajoute à currentcomposante le premier sommet non traité à distancesize; i++) { + if (!isInComponent(&vertexes[i])) { + currentDist = squaredDistanceTrees(&forest->trees[i], + &forest->trees[lastIntegratedVertex]); + if (currentDist < vertexes[i].distanceToComponent) { + vertexes[i].distanceToComponent = currentDist; + vertexes[i].whichVertex = lastIntegratedVertex; + } + if (currentDist < minDist) { + minDist = vertexes[i].distanceToComponent; + closestVertex = i; + } } + } + return closestVertex; } - -/** - * @brief Copie le contenu de composante2 dans composante1 - * en supposant que size(composante2) > size(composante1) - * - * @param composante1 - * @param composante2 - */ -void copyComposant(list_t* composante1, list_t* composante2){ - for (size_t i = 0; iedges[MST->size].tree1 = tree1; + MST->edges[MST->size].tree2 = tree2; + MST->size++; } +void computeMST(forest *forest, MST *MST) { + clock_t start = clock(); + printf("Computing MST...\n"); + printf("Projecting trees...\n"); + equirectangularProjection(forest); + printf("Trees projected\n"); + + vertex vertexes[forest->size]; + initializeVertexes(vertexes, forest->size); + + MST->size = 0; + allocateEdgesMemory(MST, forest->size - 1); + + size_t lastIntegratedVertex = 0; + size_t closestVertex; + while (MST->size < forest->size - 1) { + closestVertex = updateDistancesAndFindClosestVertex(vertexes, forest, + lastIntegratedVertex); + addNewEdge(MST, closestVertex, vertexes[closestVertex].whichVertex); + loadingBar(MST->size, forest->size - 1, clock() - start); + } + printf("\nFinished computing MST, took %ld seconds\n", + (int)(clock() - start) / CLOCKS_PER_SEC); +} - -tree* getForestMaxComposante(tree* forest, size_t size, double distmax, size_t* newsize){ - clock_t start = clock(); - short composante[size]; //si l'arbre appartient déjà à une composante - size_t numberOfVertexHandled = 0; - - for (size_t i = 0; i list_size(composantemax)){; - copyComposant(composantemax, currentcomposante); - } - list_free(currentcomposante); - } - - tree* forest2 = malloc(sizeof(*forest2)*list_size(composantemax)); - - for (size_t i = 0; i size(composante1) +// * +// * @param composante1 +// * @param composante2 +// */ +// void copyComposant(list_t *composante1, list_t *composante2) { +// for (size_t i = 0; i < list_size(composante1); i++) { +// size_t elm = *((size_t *)list_get(composante2, i)); +// list_set2(composante1, i, create_size_t(elm)); +// } +// for (size_t i = list_size(composante1); i < list_size(composante2); i++) { +// size_t elm = *((size_t *)list_get(composante2, i)); +// list_append(composante1, create_size_t(elm)); +// } +// } + +// tree *getForestMaxComposante(tree *forest, size_t size, double distmax, +// size_t *newsize) { +// clock_t start = clock(); +// short composante[size]; // si l'arbre appartient déjà à une composante +// size_t numberOfVertexHandled = 0; + +// for (size_t i = 0; i < size; i++) { +// composante[i] = 0; +// } + +// list_t *composantemax = list_create(); + +// while (numberOfVertexHandled < size) { +// list_t *currentcomposante = list_create(); +// list_append(currentcomposante, +// create_size_t(firstNotHandledVertex(composante, size))); +// addVertexes(composante, size, *((size_t *)list_get(currentcomposante, +// 0)), +// forest, distmax, currentcomposante); + +// numberOfVertexHandled += list_size(currentcomposante); +// // loadingBar(numberOfVertexHandled, size, clock() - start); +// if (list_size(currentcomposante) > list_size(composantemax)) { +// ; +// copyComposant(composantemax, currentcomposante); +// } +// list_free(currentcomposante); +// } + +// tree *forest2 = malloc(sizeof(*forest2) * list_size(composantemax)); + +// for (size_t i = 0; i < list_size(composantemax); i++) { +// size_t tree = *((size_t *)list_get(composantemax, i)); +// forest2[i] = forest[tree]; +// } + +// *newsize = list_size(composantemax); +// list_free(composantemax); +// return forest2; +// } \ No newline at end of file diff --git a/src/arbres.c b/src/arbres.c deleted file mode 100644 index 410f82e..0000000 --- a/src/arbres.c +++ /dev/null @@ -1,104 +0,0 @@ -#include -#include "../include/struct.h" -#include "../include/fonctions.h" -#include "../include/options.h" -#include "../include/parse.h" -#include "../include/algo.h" -#include "../include/display.h" -#include "../include/stat.h" -#include "../include/restrictiveOptions.h" - -/** - * @mainpage - * - * \section sous_titre Voici le projet 1A IR 2022-2023 de Inès LEMESLE et Nicolas GRESSET-BOURGOIS. - * - * - * \subsection step1 *Commande type à entrer en ligne de commande : - * - * ./bin/arbres -i in_path.csv -o out_path -c save-struct-genre -f 1,1,50,50 -s -g Acer -h 10 - * - * ./bin/arbres -i in_path.bin -o out_path -r save-struct-genre -f 1,1,50,50 -s -g Acer -h 10 - * - * - * \subsection step2 *Les différentes options implémentées : - * - * -Attention les options -i ... et -o ... et (-c ... ou -r ...) sont nécessaires au lancement du programme - * - * -Possibilité de sauvegarder les informations du minimum spanning tree dans un fichier (-t ...) - * - * -Possibilité de choisir le genre de l'arbre (-g ...) - * - * -Possibilité de choisir la hauteur de l'arbre (-h ...) - * - * -Possibilité de choisir un carré limite de géolocalisation (-f ...) - * - * -Possibilité de voir les statistiques du graphe (-s) - * - * -Possibilité de mettre une distance maximale en mètres entre 2 arbres et ne récupérer que la plus grande composante connexe (-l ...) - * - * \subsection step3 *Tests disponibles : - * - * -Test de la bonne gestion des options (test_option) - * - * -Test de la réalisation du parsage (test_parsage) - * - * -Test de la validité de la valeur des distances entre les arbres (test-distance) - * - * -Test de la reconnaissance d'un fichier binaire ou non (test-binary) - * - * -Test arrêt du programme si fichier non existant (test_error_file) - * - * -Test correspondance entre les identifiants associés aux genres et les genres eux-mêmes (test_corres_genus) - */ - - -int main(int argc, char ** argv){ - (void)argc; - - opt handlingOptions; - options(argc,argv,&handlingOptions); - - int size; - tree * trees=malloc(300000*sizeof(tree)); - if(trees == NULL) { exit(15); } - memset(trees,0,sizeof(*trees)*300000); - handlingFile(handlingOptions,trees,&size); - - int sizeSelection; - tree * treesSelection=malloc(300000*sizeof(tree)); - if(treesSelection == NULL){ exit(15); } - edge * MST; - char * endPointer; - size_t newsize; - - projectionTrees(trees, size); - - - if(handlingOptions.height == NULL && handlingOptions.genus == NULL && handlingOptions.limit_value == NULL){ - - if(handlingOptions.location != NULL) { - tree* treesSelection2 = getForestMaxComposante(trees, size, pow(strtod(handlingOptions.location,&endPointer)/RAYON, 2), &newsize); - manageActions(treesSelection2, newsize, &MST, handlingOptions); - free(treesSelection2); - }else{ - manageActions(trees, size, &MST, handlingOptions); - } - - }else{ - handlingRestrictingOptions(handlingOptions,trees,treesSelection,size,&sizeSelection); - if(handlingOptions.location != NULL) { - tree* treesSelection2 = getForestMaxComposante(treesSelection, sizeSelection, pow(strtod(handlingOptions.location,&endPointer)/RAYON, 2), &newsize); - manageActions(treesSelection2, newsize, &MST, handlingOptions); - free(treesSelection2); - }else{ - manageActions(treesSelection, sizeSelection, &MST, handlingOptions); - } - - } - - free(MST); - free(trees); - free(treesSelection); - return 0; -} diff --git a/src/array-list.c b/src/array-list.c index a15fcb2..f155b58 100644 --- a/src/array-list.c +++ b/src/array-list.c @@ -2,106 +2,64 @@ #include #include -struct list_t{ - void** element; - size_t allocated; - size_t size; -}; - -list_t* list_create(void){ - list_t* it = malloc(sizeof(*it)); - if (it == NULL) return NULL; - - it->element = malloc(10*sizeof(it->element[0])); - if (it->element == NULL){ - free(it); - return NULL; - } - - it->allocated = 10; - it->size = 0; - return it; -} - - -int list_append(list_t* it, void* element_to_append){ - if( it->size < it->allocated){ - it->element[it->size] = element_to_append; - it->size ++; - return 1; - } - - void** tmp = realloc(it->element,(it->allocated+15)*sizeof(it->element[0])); - if (tmp == NULL){ - return 0; - } - - it->element = tmp; - it->allocated += 15; - it->element[it->size] = element_to_append; - it->size ++; - return 1; -} +#define INITIAL_LIST_SIZE 8 +#define GROWTH_FACTOR 2 -void* list_get(list_t* it, int pos){ - return it->element[pos]; -} +struct list { + size_t *elements; + size_t allocated; + size_t size; +}; -void list_set(list_t* it, int pos, void* element){ - it->element[pos] = element; -} +dynamic_list *list_create(void) { + dynamic_list *list = malloc(sizeof(*list)); + if (list == NULL) + return NULL; -void list_set2(list_t* it, int pos, void* element){ - free(list_get(it, pos)); - it->element[pos] = element; -} + list->elements = malloc(INITIAL_LIST_SIZE * sizeof(list->elements[0])); + if (list->elements == NULL) { + free(list); + return NULL; + } -size_t list_size(list_t* it){ - return it->size; + list->allocated = INITIAL_LIST_SIZE; + list->size = 0; + return list; } -int list_insert(list_t* it, size_t pos, void* elmt){ - - if (list_append(it, elmt) == 0) return 0; - - for (size_t i = it->size - 1; i>pos; i--){ - list_set(it, i, list_get(it, i-1)); - } - list_set(it, pos, elmt); +int list_append(dynamic_list *list, size_t element) { + if (list->size < list->allocated) { + list->elements[list->size] = element; + list->size++; return 1; + } + + size_t *tmp = realloc(list->elements, (list->allocated * GROWTH_FACTOR * + sizeof(list->elements[0]))); + if (tmp == NULL) { + return 0; + } + list->elements = tmp; + list->allocated *= GROWTH_FACTOR; + list->elements[list->size] = element; + list->size++; + return 1; } -int list_prepend(list_t* it, void* elmt){ - return list_insert(it, 0, elmt); -} - -void* list_take(list_t* it, int pos){ - void* elmt = list_get(it, pos); +size_t list_get(dynamic_list *list, int index) { return list->elements[index]; } - for (size_t i = pos; i < it->size-1; i++){ - it->element[i] = it->element[i+1]; - } - it->size--; - return elmt; +void list_set(dynamic_list *list, int index, size_t element) { + list->elements[index] = element; } +size_t list_size(dynamic_list *it) { return it->size; } -void list_free(list_t* it){ - for(size_t i = 0; i < list_size(it); i++){ - free(list_get(it, i)); - } - - free(it->element); - free(it); //modif concernant le projet programmation -} - -size_t* create_size_t(size_t i){ - size_t* p = malloc(sizeof(size_t)); - *p = i; - return p; +size_t list_pop(dynamic_list *list) { + list->size--; + return list->elements[list->size]; } -void list_free2(list_t* it){ - free(it->element); - free(it); +void list_free(dynamic_list *list) { + free(list->elements); + free(list); } diff --git a/src/display.c b/src/display.c index d69dcca..73accf7 100644 --- a/src/display.c +++ b/src/display.c @@ -1,6 +1,5 @@ #include "../include/display.h" #include "../include/struct.h" -#include #define HEIGHT 800 @@ -15,165 +14,165 @@ void infoDisplay(void){ } -/** - * @brief Effectue une transformation affine des coordonnées des arbres - * de la liste forest de taille size, de manière à recadrer les arbres - * dans la fenêtre de taille WIDTH*HEIGHT, et à prendre compte du zoom - * et du déplacement avec les flèches - * - * @param forest - * @param size - * @param zoom - * @param left - * @param up - */ -void scaleTree(tree* forest, size_t size, int zoom, int left, int up){ - double xmin, xmax, ymin, ymax; - getExtremum(forest, size, &xmin, &ymin, &xmax, &ymax); - - - double alpha = (WIDTH)/(xmax - xmin )*(1+ 0.02*zoom); - double beta = (HEIGHT)/(ymax - ymin )*(1+ 0.02*zoom); - - for (size_t i = 0; igeoloc.x; - y1 = edgeList[i].tree1->geoloc.y; - x2 = edgeList[i].tree2->geoloc.x; - y2 = edgeList[i].tree2->geoloc.y; - tps_drawLine(x1, y1, x2, y2); - } +// size--; +// double x1, x2, y1, y2; +// tps_createWindow("Arbres de Paris", WIDTH, HEIGHT); +// tps_background(255, 255, 255); +// tps_setColor(0, 0, 0); +// scaleTree(forest, size + 1,0,0,0); + +// for (size_t i = 0; igeoloc.x; +// y1 = edgeList[i].tree1->geoloc.y; +// x2 = edgeList[i].tree2->geoloc.x; +// y2 = edgeList[i].tree2->geoloc.y; +// tps_drawLine(x1, y1, x2, y2); +// } - int zoom=0; - int left=0; - int up=0; - while(tps_isRunning()){ +// int zoom=0; +// int left=0; +// int up=0; +// while(tps_isRunning()){ - if(tps_getKeyPressed() == SDLK_p ) {zoom++;} - if(tps_getKeyPressed() == SDLK_m ) {zoom--;} +// if(tps_getKeyPressed() == SDLK_p ) {zoom++;} +// if(tps_getKeyPressed() == SDLK_m ) {zoom--;} - if(tps_getKeyPressed() == SDLK_LEFT ) {left++;} - if(tps_getKeyPressed() == SDLK_RIGHT ) {left--;} +// if(tps_getKeyPressed() == SDLK_LEFT ) {left++;} +// if(tps_getKeyPressed() == SDLK_RIGHT ) {left--;} - if(tps_getKeyPressed() == SDLK_UP ) {up++;} - if(tps_getKeyPressed() == SDLK_DOWN ) {up--;} +// if(tps_getKeyPressed() == SDLK_UP ) {up++;} +// if(tps_getKeyPressed() == SDLK_DOWN ) {up--;} - tps_background(255, 255, 255); - scaleTree(forest, size + 1,zoom,left,up); - for (size_t i = 0; igeoloc.x; - y1 = edgeList[i].tree1->geoloc.y; - x2 = edgeList[i].tree2->geoloc.x; - y2 = edgeList[i].tree2->geoloc.y; - tps_drawLine(x1, y1, x2, y2); - } +// tps_background(255, 255, 255); +// scaleTree(forest, size + 1,zoom,left,up); +// for (size_t i = 0; igeoloc.x; +// y1 = edgeList[i].tree1->geoloc.y; +// x2 = edgeList[i].tree2->geoloc.x; +// y2 = edgeList[i].tree2->geoloc.y; +// tps_drawLine(x1, y1, x2, y2); +// } - tps_render(); - } - tps_closeWindow(); - return; -} - - - - -/** - * @brief Renvoie le champ geoloc.x minimum de la liste forest de taille size - * - * @param forest - * @param size - * @return double - */ -double minX(tree* forest, size_t size){ - size_t i = 0; - double distMin = forest[i].geoloc.x; - - for (i = 1; i distMax) distMax = forest[i].geoloc.x; - } - - return distMax; -} - -/** - * @brief Renvoie le champ geoloc.y maximum de la liste forest de taille size - * - * @param forest - * @param size - * @return double - */ -double maxY(tree* forest, size_t size){ - size_t i = 0; - double distMax = forest[i].geoloc.y; - - for (i = 1; i distMax) distMax = forest[i].geoloc.y; - } - - return distMax; -} - - -void getExtremum(tree* forest, size_t size, double* minimumX, -double* minimumY, double* maximumX, double* maximumY){ - *minimumX = minX(forest, size); - *minimumY = minY(forest, size); - *maximumX = maxX(forest, size); - *maximumY = maxY(forest, size); -} +// tps_render(); +// } +// tps_closeWindow(); +// return; +// } + + + + +// /** +// * @brief Renvoie le champ geoloc.x minimum de la liste forest de taille size +// * +// * @param forest +// * @param size +// * @return double +// */ +// double minX(tree* forest, size_t size){ +// size_t i = 0; +// double distMin = forest[i].position.latitude; + +// for (i = 1; i distMax) distMax = forest[i].position.latitude; +// } + +// return distMax; +// } + +// /** +// * @brief Renvoie le champ geoloc.y maximum de la liste forest de taille size +// * +// * @param forest +// * @param size +// * @return double +// */ +// double maxY(tree* forest, size_t size){ +// size_t i = 0; +// double distMax = forest[i].position.longitude; + +// for (i = 1; i distMax) distMax = forest[i].position.longitude; +// } + +// return distMax; +// } + + +// void getExtremum(tree* forest, size_t size, double* minimumX, +// double* minimumY, double* maximumX, double* maximumY){ +// *minimumX = minX(forest, size); +// *minimumY = minY(forest, size); +// *maximumX = maxX(forest, size); +// *maximumY = maxY(forest, size); +// } void loadingBar(int current, int total, clock_t delta){ static double previoustime = 0; diff --git a/src/erreur.c b/src/erreur.c new file mode 100644 index 0000000..460cfb4 --- /dev/null +++ b/src/erreur.c @@ -0,0 +1,8 @@ +#include "../include/erreur.h" +#include + +void raler(const char *msg, const char *file, int line) { + fprintf(stderr, "%s:%d:", file, line); + perror(msg); + exit(EXIT_FAILURE); +} \ No newline at end of file diff --git a/src/fonctions.c b/src/fonctions.c index 1556f89..3167d72 100644 --- a/src/fonctions.c +++ b/src/fonctions.c @@ -1,64 +1,55 @@ #include "../include/fonctions.h" -#include #include "../include/algo.h" -#include "../include/stat.h" #include "../include/display.h" #include "../include/parse.h" +#include "../include/stat.h" +#include - -/** - * @brief Get the Mean Longitude object - * Renvoie la longitude moyenne des arbres de la liste forest de taille size - * - * @param forest - * @param size - * @return double - */ -double getMeanLongitude(tree* forest, size_t size){ - double meanLongitude = 0; - for (size_t i = 0; isize; i++) { + sumLatitude += forest->trees[i].position.coord_1.latitude; + sumLongitude += forest->trees[i].position.coord_2.longitude; + } + *meanLatitude = sumLatitude / forest->size; + *meanLongitude = sumLongitude / forest->size; } -/** - * @brief Get the Mean Latitude object - * Renvoie la latitude moyenne des arbres de la liste forest de taille size - * - * @param forest - * @param size - * @return double - */ -double getMeanLatitude(tree* forest, size_t size){ - double meanLatitude = 0; - for (size_t i = 0; iposition.coord_1.x = + cosphi0 * (tree->position.coord_1.latitude - lambda0) * M_PI / 180; + tree->position.coord_2.y = + (tree->position.coord_2.longitude - phi0) * M_PI / 180; } - -void projectionTrees(tree* forest, size_t size){ - double phi0 = getMeanLatitude(forest, size); - double lambda0 = getMeanLongitude(forest, size); - double cosphi0 = cos(phi0*M_PI/180); - - for(size_t i = 0; isize; i++) { + setNewCoordinates(&forest->trees[i], phi0, lambda0, cosphi0); + } } -double distanceTrees(tree* tree1, tree* tree2){ - return ((pow(tree1->geoloc.x - tree2->geoloc.x, 2) + pow(tree1->geoloc.y - tree2->geoloc.y, 2))); +double squaredDistanceTrees(tree *tree1, tree *tree2) { + return ((pow(tree1->position.coord_1.x - tree2->position.coord_1.x, 2) + + pow(tree1->position.coord_2.y - tree2->position.coord_2.y, 2))); } +double distanceTrees(tree *tree1, tree *tree2) { + return RAYON_TERRE * sqrt(squaredDistanceTrees(tree1, tree2)); +} -void manageActions(tree* forest, size_t size, edge** MST, opt handlingOptions){ - *MST = minimumSpanningTree(forest, size); - stat(handlingOptions, *MST, size, forest); - displayTrees(*MST, size, forest); - writeEdges(*MST, size - 1, handlingOptions); +void manageActions(tree *forest, size_t size, edge **MST, opt handlingOptions) { + //*MST = minimumSpanningTree(forest, size); + // stat(handlingOptions, *MST, size, forest); + // displayTrees(*MST, size, forest); + writeEdges(*MST, size - 1, handlingOptions); } \ No newline at end of file diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..83be09f --- /dev/null +++ b/src/main.c @@ -0,0 +1,87 @@ +#include "../include/algo.h" +#include "../include/options.h" +#include "../include/parse.h" +#include "../include/stat.h" +#include "../include/struct.h" +#include +#include + +/** + * @mainpage + * + * \section sous_titre Voici le projet 1A IR 2022-2023 de Inès LEMESLE et + * Nicolas GRESSET-BOURGOIS. + * + * + * \subsection step1 *Commande type à entrer en ligne de commande : + * + * ./bin/arbres -i in_path.csv -o out_path -c save-struct-genre -f 1,1,50,50 + * -s -g Acer -h 10 + * + * ./bin/arbres -i in_path.bin -o out_path -r save-struct-genre -f 1,1,50,50 + * -s -g Acer -h 10 + * + * + * \subsection step2 *Les différentes options implémentées : + * + * -Attention les options -i ... et -o ... et (-c ... ou -r ...) sont + * nécessaires au lancement du programme + * + * -Possibilité de sauvegarder les informations du minimum spanning tree + * dans un fichier (-t ...) + * + * -Possibilité de choisir le genre de l'arbre (-g ...) + * + * -Possibilité de choisir la hauteur de l'arbre (-h ...) + * + * -Possibilité de choisir un carré limite de géolocalisation (-f ...) + * + * -Possibilité de voir les statistiques du graphe (-s) + * + * -Possibilité de mettre une distance maximale en mètres entre 2 arbres et + * ne récupérer que la plus grande composante connexe (-l ...) + * + * \subsection step3 *Tests disponibles : + * + * -Test de la bonne gestion des options (test_option) + * + * -Test de la réalisation du parsage (test_parsage) + * + * -Test de la validité de la valeur des distances entre les arbres + * (test-distance) + * + * -Test de la reconnaissance d'un fichier binaire ou non (test-binary) + * + * -Test arrêt du programme si fichier non existant (test_error_file) + * + * -Test correspondance entre les identifiants associés aux genres et les + * genres eux-mêmes (test_corres_genus) + */ + +void test(forest *forest) { + for (size_t i = 0; i < forest->size; i++) { + printf("tree %ld : %d %f %f\n", i, forest->trees[i].height, + forest->trees[i].position.coord_1.latitude, + forest->trees[i].position.coord_2.longitude); + } +} + +int main(int argc, char **argv) { + opt options; + forest forest; + MST MST; + + handleCLI(argc, argv, &options); + loadAndParseFile(&options, &forest); + computeMST(&forest, &MST); + + // writeEdges(options) // on s'en tape non ? + if (options.stat) { + dumpStats(&MST, &forest); + } + // displayMST(options) // à faire avec la SDL + + free(forest.trees); + free(MST.edges); + return 0; +} diff --git a/src/options.c b/src/options.c index b049bcc..a3c8934 100644 --- a/src/options.c +++ b/src/options.c @@ -1,103 +1,112 @@ +#include "../include/struct.h" #include #include #include +#include #include -#include "../include/struct.h" - -void help(void){ - printf("\n**********************************************************************************************************\n"); - printf("i option (followed by a path) : path of the file you want to parse [csv or binary]\n"); - printf("c option (followed by a path) : path of the file where you want to save your gender correspondance table (if use of csv file)\n"); - printf("r option (followed by a path) : path of the file from where you want to load your gender correspondance table (if use of binary file)\n"); - printf("o option (followed by a path) : path of the file where you save the binary version of your input [binary]\n"); - printf("t option (followed by a path) : path of the file saving the minimum panning tree informations\n"); - printf("l option (followed by a positive int) : maximum distance allowed betwween your trees [meters]\n"); - printf("s option (no arguments) : to get statistics on the tree\n"); - printf("f option (followed by 4 positives int) : x minimum X1, y minimum Y1,x maximum X2, y maximum Y2 [°]\n"); - printf("g option (followed by a string) : to get a mimimum spanning tree only considering this genus\n"); - printf("h option (followed by an positive int) : to get a mimimum spanning tree only considering this height [meters]\n"); - printf("**********************************************************************************************************\n"); -} - -void initOptions(opt * options){ - options->in_path = NULL; //ok - options->out_path = NULL; //ok - options->span_tree_path = NULL; //ok - options->limit_value = NULL; - options->stat = 0; - options->location = NULL; //ok - options->genus = NULL; //ok - options->height = NULL; //ok - options->conserve = NULL; //ok - options->recharge = NULL; //ok - options->help = 0; //ok +void help(void) { + printf("\n*******************************************************************" + "***************************************\n"); + printf("i option (followed by a path) : path of the file you want to parse " + "[csv or binary]\n"); + printf("c option (followed by a path) : path of the file where you want to " + "save your gender correspondance table (if use of csv file)\n"); + printf("r option (followed by a path) : path of the file from where you want " + "to load your gender correspondance table (if use of binary file)\n"); + printf("o option (followed by a path) : path of the file where you save the " + "binary version of your input [binary]\n"); + printf("t option (followed by a path) : path of the file saving the minimum " + "panning tree informations\n"); + printf("l option (followed by a positive int) : maximum distance allowed " + "betwween your trees [meters]\n"); + printf("s option (no arguments) : to get statistics on the tree\n"); + printf("f option (followed by 4 positives int) : x minimum X1, y minimum " + "Y1,x maximum X2, y maximum Y2 [°]\n"); + printf("g option (followed by a string) : to get a mimimum spanning tree " + "only considering this genus\n"); + printf("h option (followed by an positive int) : to get a mimimum spanning " + "tree only considering at most this height [meters]\n"); + printf("*********************************************************************" + "*************************************\n"); } -void options(int argc, char ** argv, opt * options){ +void handleCLI(int argc, char **argv, opt *options) { - initOptions(options); - int c; - while ((c = getopt (argc, argv, "i:o:t:l:sf:g:h:c:r:a")) != -1) - switch (c) { - case 'i': - options->in_path = optarg; - break; - case 'o': - options->out_path = optarg; - break; - case 't': - options->span_tree_path = optarg; - break; - case 'f': - options->limit_value = optarg; - if(atoi(options->limit_value) <= 0) {fprintf(stderr,"The limit %d must be a positive value",atoi(options->limit_value)); help(); exit(1);} - break; - case 's': - options->stat= 1; - break; - case 'l': - options->location = optarg; - break; - case 'g': - options->genus = optarg; - break; - case 'h': - options->height = optarg; - if(atoi(options->height) <= 0) {fprintf(stderr,"Th height %d must be a positive value",atoi(options->height)); help(); exit(1);} - break; - case 'c': - options->conserve = optarg; - break; - case 'r': - options->recharge = optarg; - break; - case 'a': - options->help = 1; - help(); - break; + memset(options, 0, sizeof(opt)); + int c; + while ((c = getopt(argc, argv, "i:o:t:l:sf:g:h:c:r:a")) != -1) { + switch (c) { + case 'i': + options->in_path = optarg; // required + break; + case 'o': + options->out_path = optarg; // only required if input is csv + break; + case 't': + options->span_tree_path = optarg; // optional + break; + case 'f': // optional + // todo change this to reflect what the acutal options wants to do (4 + // values representing a square in between the trees must only be + // considered) + options->limit_value = optarg; + if (atoi(options->limit_value) <= 0) { + fprintf(stderr, "The limit %d must be a positive value", + atoi(options->limit_value)); + help(); + exit(EXIT_FAILURE); + } + break; + case 's': // optional + options->stat = 1; + break; + case 'l': + options->max_distance = atoi(optarg); // optional + break; + case 'g': + options->genus = optarg; // optional + break; + case 'h': + options->height = atoi(optarg); + if (options->height <= 0) { + fprintf(stderr, "The height %d must be a positive value", + options->height); + help(); + exit(EXIT_FAILURE); + } + break; + case 'c': // only required in case of csv + options->genus_conserve_path = optarg; + break; + case 'r': // only required in case of binary + options->genus_recharge_path = optarg; + break; + case 'a': + options->help = 1; + help(); + break; - case '?': + case '?': - if (optopt == 'i' || optopt == 'o'|| optopt == 't' || optopt == 'l' || optopt == 'f' || optopt == 'g' || optopt == 'h'){ - fprintf (stderr, "Option -%c requires an argument.\n", optopt); - help(); - exit(2); - - }else if (isprint (optopt)){ - fprintf (stderr, "Unknown option -%c.\n", optopt); - exit(3); - - }else{ - fprintf (stderr,"Unknown option character `\\x%x'.\n", optopt); - exit(3); - } - - - default: - abort (); - } + if (optopt == 'i' || optopt == 'o' || optopt == 't' || optopt == 'l' || + optopt == 'f' || optopt == 'g' || optopt == 'h') { + fprintf(stderr, "Option -%c requires an argument.\n", optopt); + } + if (isprint(optopt)) { + fprintf(stderr, "Unknown option -%c.\n", optopt); + } + help(); + exit(EXIT_FAILURE); + default: + abort(); + } + } + if (options->in_path == NULL) { + fprintf(stderr, "You must provide a path to the file you want to parse\n"); + help(); + exit(EXIT_FAILURE); + } } - diff --git a/src/parse.c b/src/parse.c index f449552..1bed599 100644 --- a/src/parse.c +++ b/src/parse.c @@ -1,335 +1,262 @@ -#include +#include "../include/parse.h" +#include "../include/struct.h" #include #include #include #include -#include -#include "../include/parse.h" -#include "../include/struct.h" -#include "../include/manageSIGINT.h" -#include "../include/display.h" +#define MAGIC_NUMBER 13 +#define MAGIC_COOKIE "NICOLASETINES" +#include "../include/erreur.h" -int genusKnown(char * genus, char * id, tab_genus * tab_corres){ +// pour stocker les genres de manière plus optimisée, on décide d'établir une +// table d'indexation à chaque genre est associé un identifiant (la première +// fois qu'il a été rencontré) ces corrrespondances seront stockées dans un +// fichier texte - for(int i=0;isize;i++){ - if(strcmp(genus,tab_corres->genuses[i].genus) == 0){ *id = tab_corres->genuses[i].id; return 1;} +/* Given a certain genus, it returns the index of the genus (might evenutally + * add it at the end of the array if it doesn"t exist yet) + */ +size_t proceedGenus(const char *genus, tab_genus *tab_corres) { + size_t i = 0; + for (i = 0; i < tab_corres->size; i++) { + if (!strcmp(genus, tab_corres->genuses[i])) { + return i; } - return 0; + } + memcpy(tab_corres->genuses[tab_corres->size], genus, strlen(genus) + 1); + tab_corres->size++; + return tab_corres->size - 1; } -void genusId(char * genus, char * id, tab_genus * tab_corres){ +static void getGenus(char **current, tree *tree, tab_genus *genus_save_tab) { + (*current)++; // skipping the first comma + char genus[MAX_GENUS_NAME_SIZE + 1]; + memset(genus, 0, MAX_GENUS_NAME_SIZE + 1); - int isInTab = genusKnown(genus,id,tab_corres); + size_t i = 0; + while ((*current)[i] != ';') { + genus[i] = (*current)[i]; + i++; + } + genus[i] = '\0'; - if(isInTab == 0){ - tab_corres->size+= 1; - tab_corres->genuses[tab_corres->size-1].id = tab_corres->size - 1; - *id = tab_corres->size - 1;; - strcpy(tab_corres->genuses[tab_corres->size-1].genus,genus); - } + tree->genus = proceedGenus(genus, genus_save_tab); + (*current) += i - 1; } -void getGenus(char* line, tree * tree, tab_genus * tab_corres){ - int cursor = 0; - int count = 0; - int count_semi_colon = 0; - - char * genustree = malloc(30); - if(genustree == NULL) { exit(15); } - - while (count_semi_colon != 9){ - if (line[cursor] == ';') { count_semi_colon++; } - cursor++; - } - - while (line[cursor] != ';'){ - genustree[count] = line[cursor]; - count++; - cursor++; - } - genustree[count] = '\0'; - - char id; - genusId(genustree,&id,tab_corres); - tree->genus = id; - - free(genustree); +inline static void getHeight(char **current, tree *tree) { + tree->height = atoi(*current); } -void getHeight(char* line, tree * tree){ - int cursor = 0; - int count = 0; - int count_semi_colon = 0; +#define POSITION_X_SIZE 17 - char * height_tree = malloc(5); - if(height_tree == NULL) { exit(15); } - - while (count_semi_colon != 13){ - if (line[cursor] == ';') { count_semi_colon++; } - cursor++; - } - - while (line[cursor] != ';' && count < 4){ - height_tree[count] = line[cursor]; - count++; - cursor++; - } - height_tree[count] = '\0'; - - tree->height = (short)atoi(height_tree); - free(height_tree); +static void getPosition(char **current, tree *tree) { + (*current)++; // skipping the first comma + char posX[POSITION_X_SIZE + 1]; + memccpy(posX, *current, ',', POSITION_X_SIZE); + posX[POSITION_X_SIZE] = '\0'; + tree->position.coord_1.latitude = atof(posX); + tree->position.coord_2.longitude = atof(*current + POSITION_X_SIZE + 1); } -void getposX(char* line, tree * tree){ - int cursor = 0; - int count = 0; - int count_semi_colon = 0; - char * posx = malloc(25); - if(posx == NULL) { exit(15); } +#define MAX_LINE_SIZE 500 // arbitraty value that seems reasonable - while (count_semi_colon != 16){ - if (line[cursor] == ';') { count_semi_colon++; } - cursor++; +static void parseCSVLine(char *line, tree *tree, tab_genus *genus_save_tab) { + char *current = line; + int count_semi_colon = 0; + while (*current != '\n') { + if (*current == ';') { + count_semi_colon++; } - - while (line[cursor] != ','){ - posx[count] = line[cursor]; - count++; - cursor++; + if (count_semi_colon == 9) { + getGenus(¤t, tree, genus_save_tab); } - posx[count] = '\0'; - - tree->geoloc.x = atof(posx); - free(posx); -} - -void getposY(char* line, tree * tree){ - int cursor = 0; - int count = 0; - int count_semi_colon = 0; - - char * posy = malloc(25); - if(posy == NULL) { exit(15); } - - while (count_semi_colon != 15){ - if (line[cursor] == ';') { count_semi_colon++; } - cursor++; + if (count_semi_colon == 13) { + getHeight(¤t, tree); } - - while (line[cursor] != ','){ cursor++; } - cursor++; - - while (line[cursor] != '\n' && count<18){ - posy[count] = line[cursor]; - count++; - cursor++; + if (count_semi_colon == 16) { + getPosition(¤t, tree); + return; // exiting earlier because we don't need to parse the rest of + // the line } - posy[count] = '\0'; - - tree->geoloc.y = atof(posy); - free(posy); + current++; + } } - -void parseFile(char* file, tree * forest, size_t numberoflines, tab_genus* tab){ - - printf("Parsing csv file\n"); - - FILE* in = fopen(file, "r"); - if (in == NULL){ - fprintf(stderr, "Unable to open the file in parseFile %s\n", file); - exit(16); - } - - char * line = malloc(500*sizeof(char)); - if(line == NULL) { exit(15); } - - size_t line_number = 0; - - if(fgets(line, 500, in) == NULL) { exit(50); } //on écrase la première ligne - - - while ( line_number < numberoflines - 1){ //pour chaque ligne donc chaque arbre - if(fgets(line, 500, in) == NULL) { exit(50); } - - getGenus(line, &forest[line_number],tab); - getHeight(line, &forest[line_number]); - getposX(line, &forest[line_number]); - getposY(line, &forest[line_number]); - line_number++; - } - - free(line); - if(fclose(in) != 0) {exit(17);} - printf("Data has been succesfully read\n"); - return; +static void parseCSVFile(FILE *file, forest *forest, + tab_genus *genus_save_tab) { + printf("Parsing csv file\n"); + + char line[MAX_LINE_SIZE + 1]; + forest->size = -1; + while (fgets(line, MAX_LINE_SIZE, file) != NULL) { + forest->size++; + } + forest->trees = malloc(forest->size * sizeof(tree)); + rewind(file); + + if (fgets(line, MAX_LINE_SIZE, file) == NULL) { + fprintf(stderr, "File is only one line long, strange, aborting\n"); + exit(EXIT_FAILURE); + } + + char *current = line; + size_t i = 0; + while (fgets(line, MAX_LINE_SIZE, file) != NULL) { + parseCSVLine(current, &forest->trees[i], genus_save_tab); + i++; + } + + printf("Data has been succesfully read\n"); } -void binaryFile(tree * forest, size_t size, char * save){ +static void saveToBinaryFile(forest *forest, const char *file_path) { + FILE *out; + CHK((out = fopen(file_path, "wb")) == NULL); - sigset_t masked; - sigset_t old; - sigset_t pending; - struct sigaction ignore; - - sigIntitialise(&masked,&old); + // write header + if (fwrite(MAGIC_COOKIE, MAGIC_NUMBER, 1, out) != 1) { + exit(EXIT_FAILURE); + } - FILE* out = fopen(save, "wb"); - if (out == NULL){ - fprintf(stderr, "Unable to open the file in binaryFile %s\n", save); - exit(16); - } - - int nbTrees = size - 1; - fwrite(&nbTrees, sizeof(nbTrees), 1, out); - - char * header = "NICOLASETINES"; - - if(fwrite(header, 13, 1, out)!=1){ exit(51); } - - - for (size_t i = 0; isize, sizeof(forest->size), 1, out) != 1) { + exit(EXIT_FAILURE); + } - sigHandle(&ignore,&pending,&old); + // write trees informations + if (fwrite(forest->trees, sizeof(tree), forest->size, out) != forest->size) { + exit(EXIT_FAILURE); + } - printf("Conversion to binary format ended succesfully\n"); - if(fclose(out) != 0) {exit(17);} - + printf("Conversion to binary format ended succesfully\n"); + CHK(fclose(out)); } -void parseFromBinaryFile(char * file, tree * forest, size_t numberoflines){ - - printf("Parsing binary file\n"); - - FILE* in = fopen(file, "rb"); - if(in == NULL) { - fprintf(stderr, "Unable to open the file in binaryFile %s\n", file); - exit(16); - } - - char lineIgnore[sizeof(int)]; - if(fread(lineIgnore, sizeof(int), 1, in) != 1) { exit(52);} - - char lineIgnore2[13]; - if(fread(lineIgnore2, 13, 1, in) != 1) { exit(52);} - - int i=0; - size_t nbBlocks=0; - - while(nbBlocks != numberoflines){ - - if(fread(&(forest[i]), sizeof(forest[i]), 1, in) != 1) {exit(52);} - i++; - nbBlocks++; - } - - printf("Data was succesfully read from BinaryFile\n"); +void parseFromBinaryFile(const char *file_path, forest *forest) { + printf("Parsing from binary file\n"); + FILE *in; + CHK((in = fopen(file_path, "rb")) == NULL); + // todo check return value + + // header has been checked previously so we can just skip it + fseek(in, MAGIC_NUMBER, SEEK_SET); + + // read number of trees + if (fread(&forest->size, sizeof(forest->size), 1, in) != 1) { + exit(EXIT_FAILURE); + } + + // allocate memory for trees + forest->trees = malloc(forest->size * sizeof(tree)); + + // read trees informations + if (fread(forest->trees, sizeof(tree), forest->size, in) != forest->size) { + exit(EXIT_FAILURE); + } + + if (fclose(in) != 0) { + raler("fclose", __FILE__, __LINE__); + exit(EXIT_FAILURE); + } + printf("Data was succesfully read from BinaryFile\n"); } -void SaveStructGenus(opt handlingOptions,tab_genus tab){ +void saveGenusCorrespondance(const char *file_path, tab_genus *tab) { + FILE *out; + CHK((out = fopen(file_path, "w")) == NULL); - FILE * saveStruct = fopen(handlingOptions.conserve,"w"); - if(saveStruct == NULL){ - fprintf(stderr, "Unable to open the file in SaveStructGenus %s\n", handlingOptions.conserve); - exit(16); - } + // write number of genuses + fprintf(out, "%zu\n", tab->size); - for(int i=0;isize; i++) { + fprintf(out, "%s\n", tab->genuses[i]); + } + CHK(fclose(out) != 0); } -int isBinary(char * file){ - - FILE * in = fopen(file,"r"); - if(in == NULL){ - fprintf(stderr, "Unable to open the file in binaryFile %s\n", file); - exit(16); - } - - int size; - if(fread(&size,sizeof(size),1,in) != 1) { exit(52); } - - char line[13]; - if(fread(line,13,1,in) != 1) { exit(52); } - line[13] = 0; - - if(strcmp(line,"NICOLASETINES") == 0) { return size;} - - if(fclose(in) != 0) { exit(17); } - return 0; +static int isBinary(FILE *file) { + char line[MAGIC_NUMBER + 1]; + if (fread(line, MAGIC_NUMBER, 1, file) != 1) { + exit(EXIT_FAILURE); + } + line[MAGIC_NUMBER] = 0; + return !(strcmp(line, MAGIC_COOKIE)); } -void writeEdges(edge * MST, size_t size,opt handlingOptions){ - - if(handlingOptions.span_tree_path == NULL){ return; } - - FILE* out = fopen(handlingOptions.span_tree_path, "w"); - if (out == NULL){ - fprintf(stderr, "Unable to open %s in function writeEdges\n", handlingOptions.span_tree_path); - exit(16); - } - - - size_t nbTrees = size; - fwrite(&nbTrees,sizeof(size_t),1,out); - - for (size_t i = 0; isize, sizeof(tab->size), 1, in); - int size=0; - char line[1000]; - - FILE * in = fopen(handlingOptions.in_path,"r"); - if(in == NULL) { - fprintf(stderr, "Unable to open the file in binaryFile %s\n", handlingOptions.in_path); - exit(16); - } - - while(fgets(line,1000,in) != NULL){ size ++; } + // get genuses + for (size_t i = 0; i < tab->size; i++) { + fgets(tab->genuses[i], MAX_GENUS_NAME_SIZE, in); + tab->genuses[i][strlen(tab->genuses[i]) - 1] = 0; // removing the \n + } + // restore genuses + for (size_t i = 0; i < forest->size; i++) { + forest->trees[i].genus = + proceedGenus(tab->genuses[forest->trees[i].genus], tab); + } - if(isBinary(handlingOptions.in_path) == 0){ - - if(handlingOptions.conserve == NULL){ printf("A path to save the gender correspondance table is needed with -c (to get more help : -a)\n"); exit(1);} - parseFile(handlingOptions.in_path,tree,size,&tab); - binaryFile(tree,size,handlingOptions.out_path); - SaveStructGenus(handlingOptions,tab); - *nbTrees=size -1; + CHK(fclose(in) != 0); +} - }else{ - if(handlingOptions.recharge == NULL){ printf("A path to load the gender correspondance table is needed with -r (to get more help : -a)\n"); exit(1);} - parseFromBinaryFile(handlingOptions.in_path,tree,isBinary(handlingOptions.in_path)); - *nbTrees=isBinary(handlingOptions.in_path); +void loadAndParseFile(opt *option, forest *forest) { + FILE *in; + CHK((in = fopen(option->in_path, "r")) == NULL); + + tab_genus genus_save_tab; + memset(&genus_save_tab, 0, sizeof(tab_genus)); + if (isBinary(in)) { + if (option->genus_recharge_path == NULL) { + fprintf(stderr, "You used a binary file to load data but did not specify " + "any gender correspondance table\n"); + exit(EXIT_FAILURE); } + parseFromBinaryFile(option->in_path, forest); + restoreGenus(option->genus_recharge_path, forest, &genus_save_tab); + CHK(fclose(in) != 0); + return; + } - - if(fclose(in) != 0 ){ exit(17); } + // else : file is csv + parseCSVFile(in, forest, &genus_save_tab); + saveToBinaryFile(forest, option->out_path); + saveGenusCorrespondance(option->genus_conserve_path, &genus_save_tab); + CHK(fclose(in) != 0); } - - - diff --git a/src/pile.c b/src/pile.c index 56dc6b2..51cfddf 100644 --- a/src/pile.c +++ b/src/pile.c @@ -1,30 +1,32 @@ #include "../include/pile.h" -#include #include "../include/array-list.h" - - -pile_t* pile_create(void){ - return list_create(); -} - -void* pop(pile_t* pile){ - return list_take(pile, list_size(pile) - 1); +#include + +struct pile { + dynamic_list *list; +}; + +pile *pile_create(void) { + pile *pile = malloc(sizeof(*pile)); + if (pile == NULL) + return NULL; + + if ((pile->list = list_create()) == NULL) { + free(pile); + return NULL; + } + return pile; } +size_t pop(pile *pile) { return list_pop(pile->list); } -int push(pile_t* pile, void* elm){ - return list_append(pile, elm); +int push(pile *pile, size_t element) { + return list_append(pile->list, element); } -int is_empty(pile_t* pile){ - if (list_size(pile) == 0) return 1; - return 0; -} +int is_empty(pile *pile) { return list_size(pile->list) == 0; } -void pile_free(pile_t* pile){ - list_free2(pile); +void pile_free(pile *pile) { + list_free(pile->list); + free(pile); } - -size_t pile_size(pile_t* pile){ - return list_size(pile); -} \ No newline at end of file diff --git a/src/restrictiveOptions.c b/src/restrictiveOptions.c deleted file mode 100644 index 1972b49..0000000 --- a/src/restrictiveOptions.c +++ /dev/null @@ -1,132 +0,0 @@ -#include "../include/parse.h" -#include "../include/struct.h" -#include "../include/restrictiveOptions.h" -#include -#include -#include -#include - - -int genderInSaveFile(opt options, char * id){ - - char genus[100]; - strcpy(genus,options.genus); - - FILE * saveStruct; - if(options.conserve != NULL) { saveStruct = fopen(options.conserve,"r");} - if(options.recharge != NULL) { saveStruct = fopen(options.recharge,"r");} - - char line[100]; - char lineGenus[100]; - - - while(fgets(line,100,saveStruct) != NULL){ - - for(int k=0;k<97;k++){ lineGenus[k] = line[k+2]; if(line[k+2] == '\n') lineGenus[k]=0; } - - if(strcmp(lineGenus,genus) == 0){ *id = line[0]-48; return 1; } - } - - fclose(saveStruct); - - return 0; -} - -int testGender(opt handlingOptions, tree tree){ - - if(handlingOptions.genus == NULL){ return 1;} - char id; - int g = genderInSaveFile(handlingOptions,&id); - - if(!g){ fprintf(stderr,"Gender %s not existing",handlingOptions.genus); exit(1);} - - if(tree.genus == id){ return 1;} - - return 0; - - -} - -int testHeight(opt handlingOptions, tree tree){ - - - if(handlingOptions.height == NULL){ return 1;} - - if(tree.height == atoi(handlingOptions.height)){return 1;} - - return 0; -} - -void separationLimit(opt handlingOptions, double * xmin, double * xmax, double * ymin, double * ymax){ - - int count=0; - int n = strlen(handlingOptions.location); - char line[50]; - int j=0; - for(int i=0;i= xmin && tree.geoloc.x <= xmax && tree.geoloc.y >= ymin && tree.geoloc.y <= ymax ){return 1;} - - return 0; - -} - -void handlingRestrictingOptions(opt handlingOptions, tree * trees, tree * treesSelection, int size, int * sizeSelection){ - - int j=0; - int g; - int h; - int l; - for(int i=0;i +#include +#include +#include + +int genderInSaveFile(opt options, char *id) { + + char genus[100]; + strcpy(genus, options.genus); + + FILE *saveStruct; + if (options.conserve != NULL) { + saveStruct = fopen(options.conserve, "r"); + } + if (options.recharge != NULL) { + saveStruct = fopen(options.recharge, "r"); + } + + char line[100]; + char lineGenus[100]; + + while (fgets(line, 100, saveStruct) != NULL) { + + for (int k = 0; k < 97; k++) { + lineGenus[k] = line[k + 2]; + if (line[k + 2] == '\n') + lineGenus[k] = 0; + } + + if (strcmp(lineGenus, genus) == 0) { + *id = line[0] - 48; + return 1; + } + } + + fclose(saveStruct); + + return 0; +} + +int testGender(opt handlingOptions, tree tree) { + + if (handlingOptions.genus == NULL) { + return 1; + } + char id; + int g = genderInSaveFile(handlingOptions, &id); + + if (!g) { + fprintf(stderr, "Gender %s not existing", handlingOptions.genus); + exit(1); + } + + if (tree.genus == id) { + return 1; + } + + return 0; +} + +int testHeight(opt handlingOptions, tree tree) { + + if (handlingOptions.height == NULL) { + return 1; + } + + if (tree.height == atoi(handlingOptions.height)) { + return 1; + } + + return 0; +} + +void separationLimit(opt handlingOptions, double *xmin, double *xmax, + double *ymin, double *ymax) { + + int count = 0; + int n = strlen(handlingOptions.location); + char line[50]; + int j = 0; + for (int i = 0; i < n; i++) { + + if (handlingOptions.location[i] == ',') { + + line[j] = 0; + j = 0; + + switch (count) { + case 0: + *xmin = atof(line); + break; + case 1: + *ymin = atof(line); + break; + case 2: + *xmax = atof(line); + break; + } + count++; + } + + else { + line[j] = handlingOptions.location[i]; + j++; + } + } + line[j] = 0; + *ymax = atof(line); +} + +int testLimit(opt handlingOptions, tree tree) { + + if (handlingOptions.location == NULL) { + return 1; + } + + double xmin; + double xmax; + double ymin; + double ymax; + separationLimit(handlingOptions, &xmin, &xmax, &ymin, &ymax); + + // printf("%lf %lf %lf %lf\n",xmin,ymin,xmax,ymax); + + if (tree.geoloc.x >= xmin && tree.geoloc.x <= xmax && tree.geoloc.y >= ymin && + tree.geoloc.y <= ymax) { + return 1; + } + + return 0; +} + +void handlingRestrictingOptions(opt handlingOptions, tree *trees, + tree *treesSelection, int size, + int *sizeSelection) { + + int j = 0; + int g; + int h; + int l; + for (int i = 0; i < size; i++) { + + g = testGender(handlingOptions, trees[i]); + h = testHeight(handlingOptions, trees[i]); + l = testLimit(handlingOptions, trees[i]); + + if (g && h && l) { + treesSelection[j] = trees[i]; + j++; + } + } + + *sizeSelection = j; +} diff --git a/src/stat.c b/src/stat.c index 8487047..8f7d231 100644 --- a/src/stat.c +++ b/src/stat.c @@ -1,141 +1,133 @@ -#include "../include/options.h" -#include "../include/struct.h" #include "../include/stat.h" #include "../include/algo.h" -#include +#include "../include/array-list.h" #include "../include/fonctions.h" +#include "../include/options.h" #include "../include/pile.h" -#include "../include/array-list.h" +#include "../include/struct.h" +#include +#include +#include #define INFINI 4000000 -/*double averageHeight(tree * trees,int size){ - - int sum = 0; - for(int i=0;isize; i++) { + list_free(graph->nodes[i].neighbors); + } + free(graph->nodes); } -void stat(opt handlingOptions, edge * MST,size_t size, tree* forest){ // size est la taille de forest - - if(handlingOptions.stat == 1) { - - printf("Nombre d'arbres : %ld\n",size); - printf("Degré moyen : %ld/%ld soit %lf\n",2*(size-1),size, 2*(size-1)/(double)size); - - double weight = weightMinimumSpanningTree(MST, size - 1); - printf("Poids du minimum spanning tree : %lfm\n",weight); - - printf("Le diamètre du graphe vaut %fm\n", diameterMST(MST, forest, size)); - - } - +void allocateGraph(graph *graph) { + graph->nodes = malloc(graph->size * sizeof(node)); + for (size_t i = 0; i < graph->size; i++) { + graph->nodes[i].neighbors = list_create(); + } } - -edge2* otherMST(edge* MST, size_t size, tree* forest){ //size correspond au nombre d'arbres ie la taille de forest - edge2* otherMST = malloc((size - 1)*sizeof(*otherMST)); - if (otherMST == NULL){ - fprintf(stderr, "Allocation error in generateMST\n"); - exit(1); - } - - for (size_t i = 0; isize = MST->size + 1; + allocateGraph(graph); + for (size_t i = 0; i < MST->size; i++) { + list_append(graph->nodes[MST->edges[i].tree1].neighbors, + MST->edges[i].tree2); + list_append(graph->nodes[MST->edges[i].tree2].neighbors, + MST->edges[i].tree1); + } } -double exentricityVertex(node* MST, size_t size, size_t current_tree, tree* forest, size_t *excentricVertex){ - double excentricity = 0; - double distance[size]; - for (size_t i = 0; i excentricity){ - excentricity = distance[*y]; - *excentricVertex = *y; - } - - } - else{ - free(y); - } +#define NOT_VISITED -1 + +// pour un sommet vertex donné, renvoie la distance au sommet le plus +// éloigné de current_vertex et met l'index de ce sommet dans excentric_vertex +// utilise un parcours en largeur avec une structure de pile +double getExcentricVertex(graph *graph, forest *forest, size_t vertex, + size_t *excentric_vertex) { + double excentricity = 0; // distance maximale trouvée + double distance[graph->size]; + for (size_t i = 0; i < graph->size; i++) { + distance[i] = NOT_VISITED; + } + distance[vertex] = 0; + + pile *pile = pile_create(); + push(pile, vertex); + size_t current; + while (!is_empty(pile)) { + current = pop(pile); + + size_t neighbor; + for (size_t i = 0; i < list_size(graph->nodes[current].neighbors); i++) { + neighbor = list_get(graph->nodes[current].neighbors, i); + + if (distance[neighbor] == NOT_VISITED) { + push(pile, neighbor); + distance[neighbor] = + distance[current] + + distanceTrees(&forest->trees[current], &forest->trees[neighbor]); + + // update excentricity + if (distance[neighbor] > excentricity) { + excentricity = distance[neighbor]; + *excentric_vertex = neighbor; } + } } - pile_free(pile); - return excentricity; + } + pile_free(pile); + return excentricity; } -void freeNode(node* listofnodes, size_t size){ - for (size_t i = 0; isize; i++) { + weight += distanceTrees(&forest->trees[MST->edges->tree1], + &forest->trees[MST->edges->tree2]); + } + return weight; +} -double diameterMST(edge* MST, tree* forest, size_t size){ //size correspond à la taille de forest - edge2* othermst = otherMST(MST, size, forest); - node* newmst = newMST(othermst, size); +void dumpStats(MST *MST, forest *forest) { + printf("Nombre d'arbres : %ld\n", forest->size); + printf("Degré moyen : %ld/%ld soit %lf\n", 2 * (forest->size - 1), + forest->size, + 2 * (forest->size - 1) / + (double)forest->size); // todo verifier que c'est bien ce résultat - free(othermst); - size_t v = 0, w = 0; - exentricityVertex(newmst, size, 0, forest, &v); - double excentricity = exentricityVertex(newmst, size, v, forest, &w); + double weight = weightMinimumSpanningTree(MST, forest); + printf("Poids de l'arbre couvrant minimal : %lf\n", weight); - freeNode(newmst, size); - return RAYON*sqrt(excentricity); + double diameter = diameterMST(MST, forest); + printf("Diamètre de l'arbre couvrant minimal : %lf\n", diameter); } - diff --git a/test/test b/test/test new file mode 100755 index 0000000000000000000000000000000000000000..37539342f8bdb2866d82ba04cece831202fdb535 GIT binary patch literal 36896 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 literal 0 HcmV?d00001 diff --git a/test/test.c b/test/test.c new file mode 100644 index 0000000..ed81783 --- /dev/null +++ b/test/test.c @@ -0,0 +1,12 @@ +#include "tst.h" // Ensure the tst framework is included + +tstsuite("Parsing test") +{ + tstcase("Equality Checks %d, %d", 1, 1) { + tstcheck(1 == 1, "Mismatch: %d != %d", 1, 1); + tstcheck(1 != 1, "Failed on purpose"); + } + + + tstnote("Testing Complete. Review for any FAIL flags."); +} \ No newline at end of file diff --git a/test/tst.h b/test/tst.h new file mode 100644 index 0000000..ed8d9eb --- /dev/null +++ b/test/tst.h @@ -0,0 +1,277 @@ +// SPDX-FileCopyrightText: © 2023 Remo Dentato +// SPDX-License-Identifier: MIT +// SPDX-PackageVersion: 0.7.3-rc + +#ifndef TST_VERSION +#define TST_VERSION 0x0007003C + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include +#include + +static volatile short tst_zero = 0; +static short tst_result = 0; +static short tst_color = 0; +static short tst_report_err = 0; + +static int tst_pass = 0; +static int tst_fail = 0; +static int tst_skip = 0; +static const char* tst_title = NULL; + +static const char *tst_str_red = "\0\033[1;31m"; +static const char *tst_str_green = "\0\033[0;32m"; +static const char *tst_str_yellow = "\0\033[0;33m"; +static const char *tst_str_cyan = "\0\033[1;36m"; +static const char *tst_str_normal = "\0\033[0m"; +//static const char *tst_str_bold = "\0\033[1m"; + +const char *tst_str_skip = "SKIP| "; +const char *tst_str_fail = "FAIL| "; +const char *tst_str_pass = "PASS| "; +const char *tst_str_skip_tst = "SKPT|,-(%s)"; +const char *tst_str_skip_end = " |`---"; +const char *tst_str_case = "CASE,--"; +const char *tst_str_case_end = " `--- "; +const char *tst_str_file = "SUIT /"; +const char *tst_str_file_end = "^^^^^ RSLT \\ "; +const char *tst_str_file_abr = "^^^^^ ABRT \\ "; +const char *tst_str_clck = "CLCK: %ld %ss "; +const char *tst_str_note = "NOTE:"; +const char *tst_str_sctn = "SCTN|,--"; +const char *tst_str_sctn_end = " |`---"; +const char *tst_str_scrn = "<<<<< "; +const char *tst_str_scrn_end = ">>>>>\n"; + +#define tstprintf(...) fprintf(stderr,__VA_ARGS__) +#define tst_prtf(...) (fprintf(stderr, __VA_ARGS__), tst_zero &= (short)fputc('\n',stderr)) +#define tst_prtln(s) fprintf(stderr, "%5d %s" , __LINE__, s) + +static int tst_prt_results(int fail, int pass, int skip) { + fprintf(stderr,"%s%d FAIL%s | ",tst_color+tst_str_red , fail, tst_str_normal+tst_color); + fprintf(stderr,"%s%d PASS%s | ",tst_color+tst_str_green , pass, tst_str_normal+tst_color); + fprintf(stderr,"%s%d SKIP%s" ,tst_color+tst_str_yellow, skip, tst_str_normal+tst_color); + return 0; +} + +// The macros `exp1`, `exp2` and `cat2` have been introduced to support Micorsoft `cl` compiler +#define tst__count(_1,_2,_3,_4,_5,_6,_7,_8,_9,_A,_B,_C,_D,_E,_F,_N, ...) _N +#define tst__argn(...) tst__count tst__exp1((__VA_ARGS__, F, E, D, C, B, A, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)) +#define tst__exp2(x,y) x y +#define tst__exp1(x) x +#define tst__cat0(x,y) x ## y +#define tst__cat1(x,y) tst__cat0(x,y) +#define tst__cat2(x,y) tst__cat1(x,y) +#define tst_vrg(_f,...) tst__exp2(tst__cat2(_f, tst__argn(__VA_ARGS__)),tst__exp1((__VA_ARGS__))) + +#define tst_tags(...) tst_vrg(tst_tags_,__VA_ARGS__) +#define tst_tags_1(_0) tst_tags__(0,_1,_2,_3,_4,_5,_6,_7,_8) +#define tst_tags_2(_0,_1) tst_tags__(1,_1,_2,_3,_4,_5,_6,_7,_8) +#define tst_tags_3(_0,_1,_2) tst_tags__(2,_1,_2,_3,_4,_5,_6,_7,_8) +#define tst_tags_4(_0,_1,_2,_3) tst_tags__(3,_1,_2,_3,_4,_5,_6,_7,_8) +#define tst_tags_5(_0,_1,_2,_3,_4) tst_tags__(4,_1,_2,_3,_4,_5,_6,_7,_8) +#define tst_tags_6(_0,_1,_2,_3,_4,_5) tst_tags__(5,_1,_2,_3,_4,_5,_6,_7,_8) +#define tst_tags_7(_0,_1,_2,_3,_4,_5,_6) tst_tags__(6,_1,_2,_3,_4,_5,_6,_7,_8) +#define tst_tags_8(_0,_1,_2,_3,_4,_5,_6,_7) tst_tags__(7,_1,_2,_3,_4,_5,_6,_7,_8) +#define tst_tags_9(_0,_1,_2,_3,_4,_5,_6,_7,_8) tst_tags__(8,_1,_2,_3,_4,_5,_6,_7,_8) + +static unsigned char tst_tags_val = 0x00; // All tags are "off" by default + +#define tsttag(...) tst_vrg(tsttag_,__VA_ARGS__) +#define tsttag_1(t_) (!!((tst_tag_ ## t_) & tst_tags_val)) +#define tsttag_2(t_,x_) ((x_) ? (tst_tags_val |= (tst_tag_ ## t_)), !(tst_zero = 0) \ + : (tst_tags_val &= ~(tst_tag_ ## t_)), (tst_zero = 0)) + +#define tst_tags__(n_,_1,_2,_3,_4,_5,_6,_7,_8) \ + static unsigned char tst_tag_##_1=0x01; static unsigned char tst_tag_##_2=0x02; \ + static unsigned char tst_tag_##_3=0x04; static unsigned char tst_tag_##_4=0x08; \ + static unsigned char tst_tag_##_5=0x10; static unsigned char tst_tag_##_6=0x20; \ + static unsigned char tst_tag_##_7=0x40; static unsigned char tst_tag_##_8=0x80; \ + static const char *tst_tag_names[8] = {#_1,#_2,#_3,#_4,#_5,#_6,#_7,#_8}; \ + static inline int tst_parsetags(int argc, const char **argv) {return tst_parse_tags(argc,argv, n_, tst_tag_names);}\ + static inline int tst_tags_zero(void) { return tst_zero & (tst_tag_##_1 | tst_tag_##_2 | tst_tag_##_3| tst_tag_##_4| \ + tst_tag_##_5 | tst_tag_##_6 | tst_tag_##_7| tst_tag_##_8); } + +static inline int tst_tags_zero(); // tst_tags_zero() always returns 0 and is used just to avoid compiler warnings. + +#define TST_STR_HELP_NOTAGS "[--help] [--color] [--report-error] [--list]" +#define TST_STR_HELP_TAGS " [+/-]tag ... ]\ntags:" + +static inline short tst_parse_tags(int argc, const char **argv, int ntags, const char **names) { + unsigned char v; + const char *arg; + short report_error = tst_report_err; + if (names[0][0] == '\0') ntags=tst_tags_zero(); + if (*argv == NULL) return report_error; + for (int n=0; n0) fputs(TST_STR_HELP_TAGS,stderr); + goto prttags; + case 'l': fprintf(stderr,"%s \"%s\"", argv[0], tst_title); + prttags: for (int k=0; k ((clock_t)1000000) + tst_zero) tst_clock_unit = "n"; \ + else if(CLOCKS_PER_SEC > ((clock_t)1000) + tst_zero) tst_clock_unit = "u"; \ + else tst_clock_unit = "m"; \ + fprintf(stderr, "----- %s%s %s \"%s\" %s%s%s\n", tst_color+tst_str_cyan,tst_str_file, __FILE__, tst_title, tst_time(), tst_color+tst_str_normal,(tst_?"":" (disabled)"));\ + if (tst_) tst__run(tst_usestatic); \ + fputs(tst_str_file_end,stderr); tst_prt_results(tst_fail, tst_pass, tst_skip); fprintf(stderr," %s\n",tst_time());\ + return ((tst_fail > 0) * tst_report_err); \ + } void tst__run(int tst_n) + +#define tstsuite(title_,...) tstrun_((!tst_zero), title_, __VA_ARGS__) +#define tst_suite(title_,...) tstrun_(( tst_zero), title_, __VA_ARGS__) + +static short tst_vars[6] = {0}; // Ensures that `tstcheck` can be used outside a `tstcase` block. + +// This is only used to avoid that the compiler complains about unused static variables. +#define tst_usestatic (( tst_result & tst_case_pass & tst_case_fail & tst_case_skip \ + & tst_vars[0] & tstdata[0] & (int)tstelapsed)) + +#define tst(x) (tst_result = (short)(!!(x))) + +static inline int tstfailed(void) {return !tst_result;} +static inline int tstpassed(void) {return tst_result;} +static inline int tstskipped(void) {return (tst_result < 0);} + +#define tstcheck_(tst_abrt,tst_str,tst_res,...) \ + do { \ + tst_result = (short)(tst_skip_test? -1 : !!(tst_res)); \ + switch (tst_result) { \ + case -1: tst_skip++; tst_case_skip++; tst_prtln(tst_str_skip); fputs(tst_color+tst_str_yellow, stderr); break; \ + case 0: tst_fail++; tst_case_fail++; tst_prtln(tst_str_fail); fputs(tst_color+tst_str_red , stderr); break; \ + case 1: tst_pass++; tst_case_pass++; tst_prtln(tst_str_pass); fputs(tst_color+tst_str_green , stderr); break; \ + } \ + fprintf(stderr, "%s%s", tst_str, tst_str_normal+tst_color); \ + if (tst_result == 0) { \ + fprintf(stderr," \"" __VA_ARGS__); fputc('"',stderr); \ + if (tst_abrt) { \ + fputs(tst_str_file_abr,stderr); tst_prt_results(tst_fail, tst_pass, tst_skip); fprintf(stderr," %s\n",tst_time()); \ + exit(0);\ + } \ + } \ + fputc('\n', stderr); \ + } while(0); + +#define tstcheck(t_,...) tstcheck_(tst_zero,#t_,t_,__VA_ARGS__) +#define tstassert(t_,...) tstcheck_(!tst_zero,#t_,t_,__VA_ARGS__) + +#define tst_skip_test tst_vars[5] +#define tstskipif(tst_) \ + for (int tst_k = 1 ; \ + tst_k && ((tst_skip_test = (short)(!!(tst_))),1); \ + tst_k = (tst_skip_test = (short)(tst_skip_test? (tst_prtln(""), tst_prtf("%s",tst_str_skip_end)):0))) \ + if (tst_skip_test && (tst_prtln(""), tst_prtf(tst_str_skip_tst,#tst_))) ; else + +static const char *tst_clock_unit; +static clock_t tstelapsed = 0; +#define tstelapsed() tstelapsed + +#define tstclock(...) \ + for(clock_t tst_clk = clock(); \ + tst_clk; \ + tstelapsed=(clock()-tst_clk), \ + tst_prtln(""), fprintf(stderr, tst_str_clck, tstelapsed, tst_clock_unit), tst_clk=tst_prtf(__VA_ARGS__)) + +#define tstnote(...) (tst_prtln(tst_str_note), tst_prtf( " " __VA_ARGS__)) + +#define tstouterr(...) for (int tst_k = (tst_prtln(tst_str_scrn),tst_prtf(" " __VA_ARGS__ ),1); \ + tst_k; \ + tst_k = 0,fputc('\n',stderr),tst_prtln(tst_str_scrn_end) ) + +#define tst_sect_iterator tst_vars[0] +#define tst_sect_counter tst_vars[1] +#define tst_sect_not_last -2 +#define tst_sect_last -3 + +#define tst_case_pass tst_vars[2] +#define tst_case_fail tst_vars[3] +#define tst_case_skip tst_vars[4] + +#define tstcase(...) \ + if (tst_prtln(tst_str_case), tst_prtf(" " __VA_ARGS__)) ; \ + else for (short tst_vars[6] = {0, tst_sect_not_last, 0, 0, 0, 0}; \ + ((tst_sect_counter == tst_sect_not_last) && (tst_sect_counter = -1)) || \ + (tst_prtln(tst_str_case_end), tst_prt_results(tst_case_fail, tst_case_pass, tst_case_skip), tst_zero &= (short)fputc('\n',stderr));\ + tst_sect_iterator += 1) + +static volatile unsigned short tstdata[1]={0}; + +#define tstcurdata tstdata[tst_data_count] +#define tst_data_size ((int)(sizeof(tstdata)/sizeof(tstdata[0]))) + +#define tstsection(...) \ + for (int tst_sect = 1; \ + tst_sect && ((tst_sect_counter > tst_sect_not_last) || !(tst_sect_counter = tst_sect_not_last))\ + && (++tst_sect_counter == tst_sect_iterator) \ + && !(tst_prtln(tst_str_sctn), tst_prtf(" " __VA_ARGS__)); \ + tst_sect = 0, tst_sect_counter = tst_sect_last, tst_prtln(tst_str_sctn_end), fputc('\n',stderr)) \ + for (int tst_data_count = 0; tst_data_count < tst_data_size; tst_data_count++) + +#define tst_check(...) +#define tst_assert(...) +#define tst_note(...) +#define tst_skpif(...) if ( tst_zero) ; else +#define tst_clock(...) if ( tst_zero) ; else +#define tst_case(...) if (!tst_zero) ; else +#define tst_section(...) if (!tst_zero) ; else + +#ifdef __cplusplus +} +#endif + +#endif // TST_VERSION \ No newline at end of file