#include "common.h" #include "huftree.h" #include "buffer.h" #include #include #include /** * 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); /** * 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(bytecount* counts) { // 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 (counts[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 (int i = 0; i < NUM_CHARS; i++) { if (counts[i] > 0) { remaining[next_index] = malloc(sizeof(*remaining[next_index])); remaining[next_index]->value = i; remaining[next_index]->count = counts[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->value = -1; parent->count = min_vert->count + sec_min_vert->count; 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--; } HufTree tree; if (leaves_count == 0) { // Cas particulier : aucun caractère dans le fichier, dans ce // cas l'arbre est l'arbre vide tree = NULL; } else if (leaves_count == 1) { // Cas particulier : un seul type de caractère dans le fichier, // dans ce cas on crée un noeud artificiel et une racine. Sans // cette opération, on aurait un arbre avec un unique sommet et // donc une étiquette vide HufTree child_l = remaining[0]; HufTree child_r = malloc(sizeof(*child_r)); tree = malloc(sizeof(*tree)); tree->value = -1; tree->count = -1; tree->child_l = child_l; tree->child_r = child_r; child_r->value = -1; child_r->count = -1; child_r->child_l = NULL; child_r->child_r = NULL; } else { // Sinon, la racine de l'arbre est le dernier sommet encore // dans le tableau `remaining` tree = remaining[0]; } // Il est désormais possible de désallouer les pointeurs sur les // sommets de l'arbre, car la seule connaissance de la racine permet // d'accéder à tout l'arbre 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 `count(min_index) < count(sec_min_index)` if (vertices[0]->count < vertices[1]->count) { *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]->count; if (freq < vertices[*min_index]->count) { // 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]->count) { // Sommet de valeur entre le minimum et le second minimum trouvé, // la valeur devient le nouveau second minimum *sec_min_index = i; } } } void writeTree(HufTree tree, WriteBuffer* buffer) { if (tree == NULL) { // Écriture de l'arbre vide dans le fichier : aucun // bit n'est ajouté return; } if (tree->child_l != NULL && tree->child_r != NULL) { // Bit "1" indiquant que le sommet a des enfants putBuffer(1, buffer); // Écriture du fils gauche et du fils droit writeTree(tree->child_l, buffer); writeTree(tree->child_r, buffer); } else { // Bit "0" indiquant que le sommet n'a pas d'enfants putBuffer(0, buffer); // Écriture de la valeur du sommet for (int i = 7; i >= 0; i--) { putBuffer((tree->value & (1 << i)) != 0, buffer); } } } HufTree readTree(ReadBuffer* buffer) { HufTree tree = malloc(sizeof(*tree)); char bit = getBuffer(buffer); // FIXME: gérer la fin inattendue du fichier if (bit == 1) { // Sommet avec enfants tree->value = -1; tree->count = -1; tree->child_l = readTree(buffer); tree->child_r = readTree(buffer); } else if (bit == 0) { // Feuille de l'arbre int value = 0; for (int i = 7; i >= 0; i--) { value |= getBuffer(buffer) << i; } tree->value = value; tree->count = 0; tree->child_l = NULL; tree->child_r = NULL; } return tree; } void freeTree(HufTree tree) { if (tree != NULL && tree->child_l != NULL && tree->child_r != NULL) { freeTree(tree->child_l); freeTree(tree->child_r); } free(tree); } 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; } if (input != NULL) { _labelVertex(*input, 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 if (vertex.value != -1) { labels[vertex.value] = 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); }