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
|
CC=gcc
|
||||||
FLAGS=-Wall -std=c99
|
CFLAGS=-Wall -std=c99
|
||||||
SRC=src
|
SRC=src
|
||||||
PROG=compress
|
PROG=compress
|
||||||
|
|
||||||
all: main.o huf.o
|
all: main.o compress.o huftree.o display.o
|
||||||
$(CC) $^ $(FLAGS) -o $(PROG)
|
$(CC) $^ $(CFLAGS) -o $(PROG)
|
||||||
|
|
||||||
%.o: src/%.c
|
%.o: src/%.c
|
||||||
$(CC) $(FLAGS) -c $^
|
$(CC) $(CFLAGS) -c $^
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f $(PROG) *.o
|
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);
|
||||||
|
}
|
41
src/main.c
41
src/main.c
|
@ -1,43 +1,20 @@
|
||||||
#include "../include/huf.h"
|
#include "../include/compress.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
int main(int argc, const char** argv) {
|
int main(int argc, const char** argv) {
|
||||||
if (argc != 2) {
|
if (argc != 2) {
|
||||||
fprintf(stderr, "Usage : compress <fichier>\n");
|
fprintf(stderr, "Utilisation : compress <source>\n");
|
||||||
|
|
||||||
|
if (argc == 1) {
|
||||||
fprintf(stderr, "Paramètre fichier manquant.\n");
|
fprintf(stderr, "Paramètre fichier manquant.\n");
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "Trop de paramètres.\n");
|
||||||
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
double* frequencies = computeFrequencies(argv[1]);
|
compress(argv[1]);
|
||||||
double sum = 0;
|
|
||||||
|
|
||||||
printf("--- 1 : CALCUL DES FRÉQUENCES ---\n\n");
|
|
||||||
|
|
||||||
for (int i = 0; i < 256; i++) {
|
|
||||||
if (frequencies[i] != 0) {
|
|
||||||
sum += frequencies[i];
|
|
||||||
printf("%1c (%3d) : %4f\n", i, i, frequencies[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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