Réorganisation du code en modules
* Extraction des fonctions spécifiques à l'arbre dans huftree.h/.c * Extraction de la routine de compression dans compress.h/.c * Déplacement des fonctions d'affichage dans display.h/.c * Isolement des constantes dans common.h
This commit is contained in:
		
							parent
							
								
									4b19376b1b
								
							
						
					
					
						commit
						b3d7c1a32e
					
				
							
								
								
									
										8
									
								
								Makefile
								
								
								
								
							
							
						
						
									
										8
									
								
								Makefile
								
								
								
								
							|  | @ -1,13 +1,13 @@ | |||
| CC=gcc | ||||
| FLAGS=-Wall -std=c99 | ||||
| CFLAGS=-Wall -std=c99 | ||||
| SRC=src | ||||
| PROG=compress | ||||
| 
 | ||||
| all: main.o huf.o | ||||
| 	$(CC) $^ $(FLAGS) -o $(PROG) | ||||
| all: main.o compress.o huftree.o display.o | ||||
| 	$(CC) $^ $(CFLAGS) -o $(PROG) | ||||
| 
 | ||||
| %.o: src/%.c | ||||
| 	$(CC) $(FLAGS) -c $^ | ||||
| 	$(CC) $(CFLAGS) -c $^ | ||||
| 
 | ||||
| clean: | ||||
| 	rm -f $(PROG) *.o | ||||
|  |  | |||
|  | @ -0,0 +1,4 @@ | |||
| #define IS_VERBOSE TRUE | ||||
| #define TRUE 1 | ||||
| #define FALSE 0 | ||||
| #define NUM_CHARS 256 | ||||
|  | @ -0,0 +1,8 @@ | |||
| #ifndef __IN303_COMPRESS_H__ | ||||
| #define __IN303_COMPRESS_H__ | ||||
| 
 | ||||
| #include "huftree.h" | ||||
| 
 | ||||
| void compress(const char *filepath); | ||||
| 
 | ||||
| #endif | ||||
|  | @ -0,0 +1,12 @@ | |||
| #ifndef __IN303_DISPLAY_H__ | ||||
| #define __IN303_DISPLAY_H__ | ||||
| 
 | ||||
| #include "huftree.h" | ||||
| 
 | ||||
| /**
 | ||||
|  * Afficher sur la sortie standard l'arbre passé | ||||
|  * en paramètre | ||||
|  */ | ||||
| void printTree(HufTree tree); | ||||
| 
 | ||||
| #endif | ||||
|  | @ -1,49 +0,0 @@ | |||
| #ifndef __FREQ_H__ | ||||
| #define __FREQ_H__ | ||||
| 
 | ||||
| #include <stdlib.h> | ||||
| 
 | ||||
| #define TRUE 1 | ||||
| #define FALSE 0 | ||||
| #define NUM_CHARS 256 | ||||
| 
 | ||||
| // structure représentant un sommet d'un arbre de Huffman
 | ||||
| typedef struct Vertex Vertex; | ||||
| struct Vertex { | ||||
| 	// le double `freq` indique la fréquence d'apparition de la lettre
 | ||||
| 	// du sommet dans le corpus, ou bien la somme des fréquences de ses fils
 | ||||
| 	double frequency; | ||||
| 
 | ||||
| 	// le caractère `value` est le caractère du corpus que ce sommet
 | ||||
| 	// représente (caractère ASCII 0 - 255)
 | ||||
| 	char value; | ||||
| 
 | ||||
| 	// pointe vers le sommet parent à celui-ci, ou NULL s'il n'a pas de parent,
 | ||||
| 	// comme par exemple pour la racine et les sommets en cours de traitement
 | ||||
| 	Vertex* parent; | ||||
| 
 | ||||
| 	// pointe vers les deux enfants de ce sommet, ou NULL s'il n'a pas
 | ||||
| 	// d'enfants, comme par exemple pour les feuilles
 | ||||
| 	Vertex *child_l, *child_r; | ||||
| }; | ||||
| 
 | ||||
| // structure représentant un arbre de Huffman
 | ||||
| typedef struct Tree Tree; | ||||
| struct Tree { | ||||
| 	// quantité de sommets dans l'arbre. Le tableau des sommets contient
 | ||||
| 	// exactement `size` éléments
 | ||||
| 	size_t size; | ||||
| 
 | ||||
| 	// tableau contenant les sommets de l'arbre, liés à leur parent,
 | ||||
| 	// excepté pour la racine
 | ||||
| 	Vertex* vertices; | ||||
| }; | ||||
| 
 | ||||
| // calcule un tableau des fréquences des caractères dans le corpus,
 | ||||
| // indexé par la valeur numérique ASCII de chaque caractère
 | ||||
| double* computeFrequencies(const char* filepath); | ||||
| 
 | ||||
| // déduit d'un tableau de fréquences de caractères un arbre de Huffman
 | ||||
| Tree buildTree(double* frequencies); | ||||
| 
 | ||||
| #endif | ||||
|  | @ -0,0 +1,69 @@ | |||
| #ifndef __IN303_HUFTREE_H__ | ||||
| #define __IN303_HUFTREE_H__ | ||||
| 
 | ||||
| #include <stdlib.h> | ||||
| 
 | ||||
| /**
 | ||||
|  * Représente un des sommets d'un arbre de Huffman | ||||
|  */ | ||||
| typedef struct HufVertex HufVertex; | ||||
| struct HufVertex { | ||||
| 	// Indique la fréquence d'apparition de la lettre du sommet
 | ||||
| 	// dans le corpus original, ou bien la somme des fréquences
 | ||||
| 	// de ses enfants (si ce n'est pas une feuille)
 | ||||
| 	double frequency; | ||||
| 
 | ||||
| 	// Caractère du corpus original associé à ce sommet. Uniquement
 | ||||
| 	// valable pour les feuilles. Pour les autres sommets, la valeur
 | ||||
| 	// de (character) n'est pas garantie
 | ||||
| 	char character; | ||||
| 
 | ||||
| 	// Pointeurs vers les deux enfants de ce sommet. Ils valent tous
 | ||||
| 	// deux NULL si le sommet est une feuille. À noter qu'un sommet
 | ||||
| 	// a toujours 0 ou 2 enfants car un arbre de Huffman est binaire
 | ||||
| 	HufVertex *child_l, *child_r; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * Représente un arbre de Huffman | ||||
|  */ | ||||
| typedef struct HufTree HufTree; | ||||
| struct HufTree { | ||||
| 	// Pointeur sur la racine de l'arbre
 | ||||
| 	HufVertex *root; | ||||
| 
 | ||||
| 	// Quantité de sommets dans l'arbre
 | ||||
| 	size_t size; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * Construire un arbre de Huffman basé sur les fréquences | ||||
|  * de caractères passées dans `frequencies` | ||||
|  * | ||||
|  * (résultat à libérer avec `freeTree`) | ||||
|  */ | ||||
| HufTree createTree(double *frequencies); | ||||
| 
 | ||||
| /**
 | ||||
|  * Libérer la mémoire occupée par un arbre de Huffman | ||||
|  * généré par la fonction `createTree` | ||||
|  */ | ||||
| void freeTree(HufTree tree); | ||||
| 
 | ||||
| /**
 | ||||
|  * Associer à chaque feuille de l'arbre une étiquette unique basée | ||||
|  * sur sa position dans l'arbre. Aucune étiquette n'est ainsi préfixe | ||||
|  * d'une autre. Le tableau renvoyé associe chaque caractère ASCII | ||||
|  * à son préfixe, ou à NULL s'il n'est pas présent dans l'arbre | ||||
|  * | ||||
|  * (résultat à libérer avec `freeTreeLabels`) | ||||
|  */ | ||||
| char** createTreeLabels(HufTree tree); | ||||
| 
 | ||||
| /**
 | ||||
|  * Libérer la mémoire occupée par un tableau d'étiquettes renvoyé | ||||
|  * par la fonction `labelTree` | ||||
|  */ | ||||
| void freeTreeLabels(char** labels); | ||||
| 
 | ||||
| #endif | ||||
|  | @ -0,0 +1,83 @@ | |||
| #include "../include/compress.h" | ||||
| #include "../include/common.h" | ||||
| #include "../include/display.h" | ||||
| #include <stdlib.h> | ||||
| #include <stdio.h> | ||||
| 
 | ||||
| /**
 | ||||
|  * Calculer un tableau de fréquences d'apparition des | ||||
|  * caractères ASCII dans le fichier de chemin donné | ||||
|  * | ||||
|  * (résultat à libérer avec `free`) | ||||
|  */ | ||||
| static double* _createFrequencies(const char*); | ||||
| 
 | ||||
| double* _createFrequencies(const char *filepath) { | ||||
| 	double *frequencies = malloc(NUM_CHARS * sizeof(*frequencies)); | ||||
| 	int totalChars = 0; | ||||
| 
 | ||||
| 	for (size_t i = 0; i < NUM_CHARS; i++) { | ||||
| 		frequencies[i] = 0; | ||||
| 	} | ||||
| 
 | ||||
| 	// Ouverture du fichier en lecture seule, et comptage
 | ||||
| 	// des occurences de chaque caractère ASCII ainsi que
 | ||||
| 	// du nombre total de caractères
 | ||||
| 	FILE *file = fopen(filepath, "r"); | ||||
| 	char current; | ||||
| 
 | ||||
| 	while ((current = fgetc(file)) != EOF) { | ||||
| 		frequencies[(size_t) current]++; | ||||
| 		totalChars++; | ||||
| 	} | ||||
| 
 | ||||
| 	fclose(file); | ||||
| 
 | ||||
| 	// Conversion des effectifs des caractères en fréquences
 | ||||
| 	for (size_t i = 0; i < NUM_CHARS; i++) { | ||||
| 		frequencies[i] /= totalChars; | ||||
| 	} | ||||
| 
 | ||||
| 	return frequencies; | ||||
| } | ||||
| 
 | ||||
| void compress(const char *filepath) { | ||||
| 	double *frequencies = _createFrequencies(filepath); | ||||
| 
 | ||||
| 	if (IS_VERBOSE) { | ||||
| 		printf("--- 1 : CALCUL DES FRÉQUENCES ---\n\n"); | ||||
| 		double sum = 0; | ||||
| 
 | ||||
| 		for (size_t i = 0; i < NUM_CHARS; i++) { | ||||
| 			if (frequencies[i] != 0) { | ||||
| 				sum += frequencies[i]; | ||||
| 				printf("%1c (%3d) : %4f\n", (int) i, (int) i, frequencies[i]); | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		printf("Total   : %f\n\n", sum); | ||||
| 	} | ||||
| 
 | ||||
| 	HufTree tree = createTree(frequencies); | ||||
| 
 | ||||
| 	if (IS_VERBOSE) { | ||||
| 		printf("--- 2 : CONSTRUCTION DE L'ARBRE ---\n\n"); | ||||
| 		printf("Arbre à %zu sommets\n\n", tree.size); | ||||
| 		printTree(tree); | ||||
| 	} | ||||
| 
 | ||||
| 	char** labels = createTreeLabels(tree); | ||||
| 
 | ||||
| 	if (IS_VERBOSE) { | ||||
| 		printf("\n\n--- 3 : ATTRIBUTION DES CODES ---\n\n"); | ||||
| 		for (size_t i = 0; i < NUM_CHARS; i++) { | ||||
| 			if (labels[i] != NULL) { | ||||
| 				printf("%1c (%3d) : %s\n", (int) i, (int) i, labels[i]); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	free(frequencies); | ||||
| 	freeTree(tree); | ||||
| 	freeTreeLabels(labels); | ||||
| } | ||||
|  | @ -0,0 +1,48 @@ | |||
| #include "../include/display.h" | ||||
| #include "../include/common.h" | ||||
| #include <stdio.h> | ||||
| 
 | ||||
| /**
 | ||||
|  * Affiche l'arborescence à partir du sommet donné, | ||||
|  * indenté sur le niveau donné | ||||
|  */ | ||||
| static void _printVertex(HufVertex, int level, int is_first); | ||||
| 
 | ||||
| void _printVertex(HufVertex vert, int level, int is_first) { | ||||
| 	// affichage des n espaces d'indentation
 | ||||
| 	for (int i = 0; i < level; i++) { | ||||
| 		if (i < level - 1) { | ||||
| 			printf("│   "); | ||||
| 		} else { | ||||
| 			printf("├───"); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (vert.child_l != NULL && vert.child_r != NULL) { | ||||
| 		// affichage d'un noeud contenant des enfants
 | ||||
| 		// un tel noeud n'a pas de nom, on utilise un caractère
 | ||||
| 		// selon sa position
 | ||||
| 		const char *name; | ||||
| 
 | ||||
| 		if (level == 0) { | ||||
| 			name = "▅"; | ||||
| 		} else { | ||||
| 			if (is_first) { | ||||
| 		 		name = "┬"; | ||||
| 			} else { | ||||
| 				name = "┼"; | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		printf("%s (%4f)\n", name, vert.frequency); | ||||
| 		_printVertex(*vert.child_l, level + 1, TRUE); | ||||
| 		_printVertex(*vert.child_r, level + 1, FALSE); | ||||
| 	} else { | ||||
| 		// c'est une feuille, affichage du sommet
 | ||||
| 		printf("%c (%4f)\n", vert.character, vert.frequency); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void printTree(HufTree tree) { | ||||
| 	_printVertex(*tree.root, 0, TRUE); | ||||
| } | ||||
							
								
								
									
										121
									
								
								src/huf.c
								
								
								
								
							
							
						
						
									
										121
									
								
								src/huf.c
								
								
								
								
							|  | @ -1,121 +0,0 @@ | |||
| #include "../include/huf.h" | ||||
| #include <assert.h> | ||||
| #include <stdlib.h> | ||||
| #include <stdio.h> | ||||
| 
 | ||||
| double* computeFrequencies(const char* filepath) { | ||||
| 	double* frequencies = (double*) malloc(NUM_CHARS * sizeof(double)); | ||||
| 	int totalChars = 0; | ||||
| 
 | ||||
| 	// initialisation du tableau à 0
 | ||||
| 	for (size_t i = 0; i < NUM_CHARS; i++) { | ||||
| 		frequencies[i] = 0; | ||||
| 	} | ||||
| 
 | ||||
| 	// parcours du fichier et comptage des caractères
 | ||||
| 	FILE* file = fopen(filepath, "r"); | ||||
| 	char current; | ||||
| 
 | ||||
| 	while ((current = fgetc(file)) != EOF) { | ||||
| 		frequencies[(size_t) current]++; | ||||
| 		totalChars++; | ||||
| 	} | ||||
| 
 | ||||
| 	// conversion des effectifs en fréquences
 | ||||
| 	for (size_t i = 0; i < NUM_CHARS; i++) { | ||||
| 		frequencies[i] /= totalChars; | ||||
| 	} | ||||
| 
 | ||||
| 	return frequencies; | ||||
| } | ||||
| 
 | ||||
| Tree buildTree(double* frequencies) { | ||||
| 	// comptage du nombre de caractères différents dans le fichier,
 | ||||
| 	// il s'agit du nombre initial de sommets dans l'arbre binaire
 | ||||
| 	size_t vertex_count = 0; | ||||
| 
 | ||||
| 	for (size_t i = 0; i < NUM_CHARS; i++) { | ||||
| 		if (frequencies[i] > 0) { | ||||
| 			vertex_count++; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// allocation d'un tableau de 2n + 1 sommets, le nombre total de
 | ||||
| 	// sommets dans l'arbre binaire final
 | ||||
| 	size_t tree_size = 2 * vertex_count - 1; | ||||
| 	Vertex* vertices = (Vertex*) malloc(tree_size * sizeof(Vertex)); | ||||
| 
 | ||||
| 	// remplissage initial du tableau avec un sommet pour chaque lettre
 | ||||
| 	size_t next_available = 0; | ||||
| 
 | ||||
| 	for (size_t i = 0; i < NUM_CHARS; i++) { | ||||
| 		if (frequencies[i] > 0) { | ||||
| 			vertices[next_available].frequency = frequencies[i]; | ||||
| 			vertices[next_available].value = (char) i; | ||||
| 			vertices[next_available].parent = NULL; | ||||
| 			vertices[next_available].child_l = NULL; | ||||
| 			vertices[next_available].child_r = NULL; | ||||
| 
 | ||||
| 			next_available++; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// remplissage du tableau avec les sommets virtuels
 | ||||
| 	while (next_available < tree_size) { | ||||
| 		// détermination des deux sommets de fréquence les plus faibles
 | ||||
| 		// parmi tous les sommets n'ayant pas de parent
 | ||||
| 		size_t i = 0, min_vert, min_vert_sec; | ||||
| 		int is_init = FALSE, is_init_sec = FALSE; | ||||
| 		double min_freq; | ||||
| 
 | ||||
| 		for (; i < next_available; i++) { | ||||
| 			if (vertices[i].parent == NULL) { | ||||
| 				if (!is_init) { | ||||
| 					// initialisation du premier sommet sans parent
 | ||||
| 					min_vert = i; | ||||
| 					min_freq = vertices[i].frequency; | ||||
| 					is_init = TRUE; | ||||
| 				} else if (!is_init_sec) { | ||||
| 					// initialisation du second sommet sans parent,
 | ||||
| 					// en s'assurant qu'ils sont dans le bon ordre
 | ||||
| 					if (vertices[i].frequency < min_freq) { | ||||
| 						size_t swap = min_vert; | ||||
| 						min_vert = i; | ||||
| 						min_vert_sec = swap; | ||||
| 						min_freq = vertices[i].frequency; | ||||
| 					} else { | ||||
| 						min_vert_sec = i; | ||||
| 					} | ||||
| 
 | ||||
| 					is_init_sec = TRUE; | ||||
| 				} else if (vertices[i].frequency < min_freq) { | ||||
| 					// déplacement de l'ancien minimum en second minimum
 | ||||
| 					// et remplacement par le nouveau minimum
 | ||||
| 					min_vert_sec = min_vert; | ||||
| 					min_vert = i; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		// création d'un sommet virtuel parent de ces deux sommets
 | ||||
| 		// ce sommet est créé à la première position libre dans le tableau
 | ||||
| 		vertices[next_available].frequency = | ||||
| 			vertices[min_vert].frequency + vertices[min_vert_sec].frequency; | ||||
| 		vertices[next_available].parent = NULL; | ||||
| 		vertices[next_available].child_l = &vertices[min_vert]; | ||||
| 		vertices[next_available].child_r = &vertices[min_vert_sec]; | ||||
| 
 | ||||
| 		// assignation du nouveau parent et des enfants
 | ||||
| 		vertices[min_vert].parent = &vertices[next_available]; | ||||
| 		vertices[min_vert_sec].parent = &vertices[next_available]; | ||||
| 
 | ||||
| 		next_available++; | ||||
| 	} | ||||
| 
 | ||||
| 	Tree tree = { | ||||
| 		.size = tree_size, | ||||
| 		.vertices = vertices | ||||
| 	}; | ||||
| 
 | ||||
| 	return tree; | ||||
| } | ||||
|  | @ -0,0 +1,213 @@ | |||
| #include "../include/huftree.h" | ||||
| #include "../include/common.h" | ||||
| #include <stdio.h> | ||||
| #include <string.h> | ||||
| 
 | ||||
| /**
 | ||||
| * Trouver les deux sommets de valeur la plus faible parmi | ||||
| * tous les sommets pointés par le tableau. Place l'indice | ||||
| * du minimum dans `min` et de l'avant-dernier dans `sec`. | ||||
| */ | ||||
| static void _findMinimalVertices(HufVertex**, size_t, size_t* min, size_t* sec); | ||||
| 
 | ||||
| /**
 | ||||
| * Libérer récursivement la mémoire occupée par le sommet | ||||
| * donné ainsi que celle de tous ses enfants (s'il en a). | ||||
| */ | ||||
| static void _freeTreeVertex(HufVertex*); | ||||
| 
 | ||||
| /**
 | ||||
| * Créer une nouvelle chaîne contenant la chaîne donnée | ||||
| * suffixée du caractère donné | ||||
| */ | ||||
| static char* _appendToString(char*, size_t, char); | ||||
| 
 | ||||
| /**
 | ||||
| * Remplit le tableau d'étiquettes données avec les étiquettes | ||||
| * des feuilles trouvées dans la sous-partie de l'arbre dont | ||||
| * le sommet donné est la racine. Toutes les étiquettes ajoutées | ||||
| * au tableau seront préfixées de la chaîne passée en paramètre | ||||
| */ | ||||
| static void _labelVertex(HufVertex, char**, char*, size_t); | ||||
| 
 | ||||
| HufTree createTree(double* frequencies) { | ||||
|     // Comptage du nombre de caractères différents dans le fichier :
 | ||||
|     // il s'agit du nombre de feuilles dans l'arbre binaire
 | ||||
|     size_t leaves_count = 0; | ||||
| 
 | ||||
|     for (size_t i = 0; i < NUM_CHARS; i++) { | ||||
|         if (frequencies[i] > 0) { | ||||
|             leaves_count++; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // Allocation d'un tableau de `leaves_count` pointeurs vers sommets.
 | ||||
|     // Initialement, ce tableau contient les feuilles du futur arbre.
 | ||||
|     // Chaque feuille correspond à un caractère du corpus original
 | ||||
|     HufVertex **remaining = malloc(leaves_count * sizeof(*remaining)); | ||||
|     size_t next_index = 0; | ||||
| 
 | ||||
|     for (size_t i = 0; i < NUM_CHARS; i++) { | ||||
|         if (frequencies[i] > 0) { | ||||
|             remaining[next_index] = malloc(sizeof(*remaining[next_index])); | ||||
| 
 | ||||
|             remaining[next_index]->frequency = frequencies[i]; | ||||
|             remaining[next_index]->character = (char) i; | ||||
|             remaining[next_index]->child_l = NULL; | ||||
|             remaining[next_index]->child_r = NULL; | ||||
| 
 | ||||
|             next_index++; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // Coeur de l'algorithme. On itère jusqu'à ce que le tableau `remaining`
 | ||||
|     // ne contienne plus qu'un élément : la racine de l'arbre. À toute
 | ||||
|     // itération, `remaining` pointe sur les sommets qui doivent encore
 | ||||
|     // être traités (c-à-d les sommets sans parent), et `remaining_count`
 | ||||
|     // est le nombre de sommets à traiter
 | ||||
|     size_t remaining_count = leaves_count; | ||||
| 
 | ||||
|     while (remaining_count > 1) { | ||||
|         // Recherche des deux sommets A et B de valeurs les plus faibles
 | ||||
|         // parmi les sommets pointés par le tableau `remaining`
 | ||||
|         size_t min_vert_index, sec_min_vert_index; | ||||
| 
 | ||||
|         _findMinimalVertices( | ||||
|             remaining, remaining_count, | ||||
|             &min_vert_index, &sec_min_vert_index | ||||
|         ); | ||||
| 
 | ||||
|         HufVertex *min_vert = remaining[min_vert_index]; | ||||
|         HufVertex *sec_min_vert = remaining[sec_min_vert_index]; | ||||
| 
 | ||||
|         // Création d'un sommet parent P pour A et B
 | ||||
|         HufVertex *parent = malloc(sizeof(*parent)); | ||||
| 
 | ||||
|         parent->frequency = min_vert->frequency + sec_min_vert->frequency; | ||||
|         parent->child_l = min_vert; | ||||
|         parent->child_r = sec_min_vert; | ||||
| 
 | ||||
|         // Modification du tableau de pointeurs `remaining` pour
 | ||||
|         // faire sortir A et B, faire entrer P, et réduire la longueur
 | ||||
|         // de `remaining` de 1 sommet
 | ||||
|         remaining[min_vert_index] = parent; | ||||
|         remaining[sec_min_vert_index] = remaining[remaining_count - 1]; | ||||
|         remaining[remaining_count - 1] = NULL; | ||||
| 
 | ||||
|         remaining_count--; | ||||
|     } | ||||
| 
 | ||||
|     // Stockage de l'adresse vers la racine de l'arbre dans un HufTree.
 | ||||
|     // Il est désormais possible de désallouer `remaining`, car la seule
 | ||||
|     // connaissance de la racine permet de parcourir tout l'arbre
 | ||||
|     HufTree tree = { | ||||
|         .root = remaining[0], | ||||
|         .size = 2 * leaves_count - 1 | ||||
|     }; | ||||
| 
 | ||||
|     free(remaining); | ||||
|     return tree; | ||||
| } | ||||
| 
 | ||||
| void _findMinimalVertices( | ||||
|     HufVertex **vertices, size_t size, | ||||
|     size_t *min_index, size_t *sec_min_index | ||||
| ) { | ||||
|     // Initialisation de telle sorte qu'initialement
 | ||||
|     // on ait `freq(min_index) < freq(sec_min_index)`
 | ||||
|     if (vertices[0]->frequency < vertices[1]->frequency) { | ||||
|         *min_index = 0; | ||||
|         *sec_min_index = 1; | ||||
|     } else { | ||||
|         *min_index = 1; | ||||
|         *sec_min_index = 0; | ||||
|     } | ||||
| 
 | ||||
|     for (size_t i = 2; i < size; i++) { | ||||
|         double freq = vertices[i]->frequency; | ||||
| 
 | ||||
|         if (freq < vertices[*min_index]->frequency) { | ||||
|             // Sommet de valeur inférieure au minimum trouvé, la valeur
 | ||||
|             // devient le nouveau minimum, le minimum devient second minimum
 | ||||
|             *sec_min_index = *min_index; | ||||
|             *min_index = i; | ||||
|         } else if (freq < vertices[*sec_min_index]->frequency) { | ||||
|             // Sommet de valeur entre le minimum et le second minimum trouvé,
 | ||||
|             // la valeur devient le nouveau second minimum
 | ||||
|             *sec_min_index = i; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void freeTree(HufTree tree) { | ||||
|     _freeTreeVertex(tree.root); | ||||
|     tree.root = NULL; | ||||
| } | ||||
| 
 | ||||
| void _freeTreeVertex(HufVertex *vert) { | ||||
|     if (vert->child_l != NULL && vert->child_r != NULL) { | ||||
|         _freeTreeVertex(vert->child_l); | ||||
|         _freeTreeVertex(vert->child_r); | ||||
|     } | ||||
| 
 | ||||
|     free(vert); | ||||
| } | ||||
| 
 | ||||
| char** createTreeLabels(HufTree input) { | ||||
|     char** labels = malloc(NUM_CHARS * sizeof(*labels)); | ||||
| 
 | ||||
|     // Initialisation des étiquettes à NULL
 | ||||
|     for (size_t i = 0; i < NUM_CHARS; i++) { | ||||
|         labels[i] = NULL; | ||||
|     } | ||||
| 
 | ||||
|     _labelVertex(*input.root, labels, NULL, 0); | ||||
|     return labels; | ||||
| } | ||||
| 
 | ||||
| void _labelVertex(HufVertex vertex, char** labels, char* label, size_t length) { | ||||
|     if (vertex.child_l != NULL && vertex.child_r != NULL) { | ||||
|         // Si le sommet a des enfants, poursuite de la récursion.
 | ||||
|         // Étiquetage de la partie gauche avec '...0' et de la partie
 | ||||
|         // droite avec '...1'
 | ||||
|         _labelVertex( | ||||
|             *vertex.child_l, labels, | ||||
|             _appendToString(label, length, '0'), | ||||
|             length + 1 | ||||
|         ); | ||||
| 
 | ||||
|         _labelVertex( | ||||
|             *vertex.child_r, labels, | ||||
|             _appendToString(label, length, '1'), | ||||
|             length + 1 | ||||
|         ); | ||||
| 
 | ||||
|         // Libération de l'étiquette passée en paramètre,
 | ||||
|         // qui a désormais été propagée dans les enfants
 | ||||
|         free(label); | ||||
|     } else { | ||||
|         // Si le sommet est une feuille, étiquetage du caractère
 | ||||
|         // associé avec l'étiquette passée en paramètre. Fin de la récursion
 | ||||
|         labels[(size_t) vertex.character] = label; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| char* _appendToString(char* orig, size_t length, char append) { | ||||
|     char* result = malloc((length + 2) * sizeof(*result)); | ||||
| 
 | ||||
|     if (orig != NULL) { | ||||
|         strcpy(result, orig); | ||||
|     } | ||||
| 
 | ||||
|     result[length] = append; | ||||
|     result[length + 1] = '\0'; | ||||
|     return result; | ||||
| } | ||||
| 
 | ||||
| void freeTreeLabels(char** labels) { | ||||
|     for (size_t i = 0; i < NUM_CHARS; i++) { | ||||
|         free(labels[i]); | ||||
|     } | ||||
| 
 | ||||
|     free(labels); | ||||
| } | ||||
							
								
								
									
										47
									
								
								src/main.c
								
								
								
								
							
							
						
						
									
										47
									
								
								src/main.c
								
								
								
								
							|  | @ -1,43 +1,20 @@ | |||
| #include "../include/huf.h" | ||||
| #include "../include/compress.h" | ||||
| #include <stdio.h> | ||||
| 
 | ||||
| int main(int argc, const char** argv) { | ||||
| 	if (argc != 2) { | ||||
| 		fprintf(stderr, "Usage : compress <fichier>\n"); | ||||
| 		fprintf(stderr, "Paramètre fichier manquant.\n"); | ||||
| 		return 1; | ||||
| 	} | ||||
|     if (argc != 2) { | ||||
|         fprintf(stderr, "Utilisation : compress <source>\n"); | ||||
| 
 | ||||
| 	double* frequencies = computeFrequencies(argv[1]); | ||||
| 	double sum = 0; | ||||
|         if (argc == 1) { | ||||
|             fprintf(stderr, "Paramètre fichier manquant.\n"); | ||||
|         } else { | ||||
|             fprintf(stderr, "Trop de paramètres.\n"); | ||||
|         } | ||||
| 
 | ||||
| 	printf("--- 1 : CALCUL DES FRÉQUENCES ---\n\n"); | ||||
|         return 1; | ||||
|     } | ||||
| 
 | ||||
| 	for (int i = 0; i < 256; i++) { | ||||
| 		if (frequencies[i] != 0) { | ||||
| 			sum += frequencies[i]; | ||||
| 			printf("%1c (%3d) : %4f\n", i, i, frequencies[i]); | ||||
| 		} | ||||
| 	} | ||||
|     compress(argv[1]); | ||||
| 
 | ||||
| 	printf("Total   : %f\n\n", sum); | ||||
| 	printf("--- 2 : CONSTRUCTION DE L'ARBRE ---\n\n"); | ||||
| 
 | ||||
| 	Tree tree = buildTree(frequencies); | ||||
| 	printf("Arbre à %zu sommets\n\n", tree.size); | ||||
| 
 | ||||
| 	for (int i = 0; i < tree.size; i++) { | ||||
| 		Vertex* vertex = &tree.vertices[i]; | ||||
| 
 | ||||
| 		printf( | ||||
| 			"[%8p] Sommet %c (%4f) : parent %8p, enfants %8p %8p\n", | ||||
| 			(void*) vertex, vertex->value, vertex->frequency, | ||||
| 			(void*) vertex->parent, (void*) vertex->child_l, | ||||
| 			(void*) vertex->child_r | ||||
| 		); | ||||
| 	} | ||||
| 
 | ||||
| 	printf("\n\n--- 3 : ATTRIBUTION DES CODES ---\n\n"); | ||||
| 
 | ||||
| 	return 0; | ||||
|     return 0; | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue