huffman/src/huftree.c

270 lines
8.2 KiB
C

#include "common.h"
#include "huftree.h"
#include "buffer.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);
/**
* Écrit dans le tampon donné les informations sur le sommet
* en question et tous ses fils
*/
static void _writeVertex(HufVertex, Buffer*);
/**
* 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 (int i = 0; i < NUM_CHARS; i++) {
if (frequencies[i] > 0) {
remaining[next_index] = malloc(sizeof(*remaining[next_index]));
remaining[next_index]->name = i;
remaining[next_index]->frequency = frequencies[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 writeTree(HufTree tree, Buffer* buffer) {
_writeVertex(*tree.root, buffer);
}
void _writeVertex(HufVertex vertex, Buffer* buffer) {
if (vertex.child_l != NULL && vertex.child_r != NULL) {
// Séquence de 9 bits indiquant un sommet qui
// n'est pas une feuille (100000000)
pushToBuffer(1, buffer);
for (int i = 0; i < 8; i++) {
pushToBuffer(0, buffer);
}
// Écriture du fils gauche et du fils droit
_writeVertex(*vertex.child_l, buffer);
_writeVertex(*vertex.child_r, buffer);
} else {
// Séquence de 9 bits encodant la feuille rencontrée
// (0 + octet du caractère)
pushToBuffer(0, buffer);
for (int i = 7; i >= 0; i--) {
pushToBuffer(
// Écriture du n-ième bit du caractère du fils
// dans le tampon
(vertex.name & (1 << i)) != 0,
buffer
);
}
// Séquence de 9 bits indiquant un sommet
// sans fils (100000001)
pushToBuffer(1, buffer);
for (int i = 0; i < 7; i++) {
pushToBuffer(0, buffer);
}
pushToBuffer(1, buffer);
}
}
HufTree readTree(FILE* output) {
// TODO: implémenter la lecture des arbres
exit(0);
}
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[vertex.name] = 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);
}