diff --git a/tp-huffman/Makefile b/tp-huffman/Makefile new file mode 100644 index 0000000..9d8204e --- /dev/null +++ b/tp-huffman/Makefile @@ -0,0 +1,49 @@ +# CC=gcc or clang or gcc-14 (on OSX) +CC=gcc + +# Math, big numbers and SDL2 fonts +LDLIBS=-lm + +# The DEBUG_LEVEL (OFF, CRITICAL, ERROR, WARNING, INFO, TRACE) +# is set by default to WARNING in error.h. +# To set it on the command line, force compilation and set it. +# Example: make -B CFLAGS="-DDEBUG_LEVEL=INFO" to see all debug messages +# above the 'INFO' level. + +# clang 14.0.3 does implement the C23 feature __VA_OPTS__ but issues a spurious +# warning. The -Wno-gnu-zero-variadic-macro-arguments disables this warning. +# This flag is ignored by gcc (which implements __VA_OPTS__ without any warning). +ifeq ($(shell uname -s), Darwin) +override CFLAGS += -std=gnu2x -MMD -Wall -pedantic -Wextra -Wshadow -Wpointer-arith \ +-Wcast-qual -Wstrict-prototypes -Wmissing-prototypes -Wno-gnu-zero-variadic-macro-arguments +endif + +ifeq ($(shell uname -s), Linux) +override CFLAGS += -std=gnu2x -MMD -Wall -pedantic -Wextra -Wshadow -Wpointer-arith \ +-Wcast-qual -Wstrict-prototypes -Wmissing-prototypes +endif + +SOURCES := $(wildcard *.c) +OBJECTS := $(SOURCES:%.c=%.o) +DEPS := $(SOURCES:%.c=%.d) +OBJECTS_NO_SOURCE = dequeue.o + +# Compilation in debug mode by default, to use gdb and valgrind. Warnings produce an error. +all: CFLAGS += -g -O0 -Werror -Wno-unused-parameter -Wno-unused-function +all: tp + +# Once the program works, optimized mode (and no error in case of warning). +nowerror: CFLAGS += -O3 -Wunused-parameter +nowerror: tp + +# Add parser.o scan.o if bison/flex interface. +tp: $(OBJECTS) $(OBJECTS_NO_SOURCE) + $(CC) -o $@ $(LDFLAGS) $(CFLAGS) $^ $(LDLIBS) + +# Include dependancies generated by gcc -MMD. +-include $(DEPS) + +# Clean all. +.PHONY: clean +clean: + rm -f $(OBJECTS) $(DEPS) tp TAGS core diff --git a/tp-huffman/alloc.c b/tp-huffman/alloc.c new file mode 100644 index 0000000..aa89e9c --- /dev/null +++ b/tp-huffman/alloc.c @@ -0,0 +1,19 @@ +/** + * @file alloc.c + * @brief Fonctions d'allocation mémoire. + */ + +#include "alloc.h" + +void check_null(const char *function, char *file, int line, int n, ...) { + va_list ap; + char *s; + + va_start(ap, n); + for (int i = 0; i < n; i++) { + void *p = va_arg(ap, void *); + s = va_arg(ap, char *); + if (p == NULL) + PRINT_ERROR(KO, "CRITICAL", function, file, line, "%s is NULL.", s); + } +} diff --git a/tp-huffman/alloc.d b/tp-huffman/alloc.d new file mode 100644 index 0000000..f1b5d34 --- /dev/null +++ b/tp-huffman/alloc.d @@ -0,0 +1 @@ +alloc.o: alloc.c alloc.h error.h diff --git a/tp-huffman/alloc.h b/tp-huffman/alloc.h new file mode 100644 index 0000000..8af0886 --- /dev/null +++ b/tp-huffman/alloc.h @@ -0,0 +1,182 @@ +/** + * @file alloc.h + * @brief Macros et fonctions d'allocation mémoire. + * + * @details Les macros MALLOC(), CALLOC() et REALLOC() définies dans ce fichier + * permettent d'allouer de la mémoire dynamiquement, en vérifiant que + * l'allocation a réussi. En cas d'échec, elles affichent un message d'erreur + * sur la sortie erreur, en indiquant la fonction, le fichier et la ligne où + * l'allocation a échoué, et quittent le programme. + * + * La fonction multiple_free() permet de libérer plusieurs pointeurs en + * un seul appel (mais attention, aucun de ces pointeurs ne doit être `NULL`, et + * tous doivent avoir été alloués dynamiquement avant cette libération). + * + * La macro CHECK_NULL() permet de vérifier qu'un ou plusieurs pointeurs ne sont + * pas `NULL`. + * + * La fonction check_null() n'est pas destinée à être utilisée directement, mais + * est utilisée par la macro CHECK_NULL(). + */ + +#ifndef ALLOC_H_ +#define ALLOC_H_ + +#include "error.h" +#include +#include +#include + +/** + * @brief Macro pour allouer de la mémoire en utilisant la fonction malloc(). + * @param p Pointeur à allouer. + * @param num_objects Nombre d'objets à allouer. + * + * @details + * Alloue de la mémoire permettant de stocker `num_objects`, chacun de type + * `*p`. + * + * **Exemple d'utilisation :** + * ``` + * backmove *p; + * MALLOC(p, 10); // Alloue un tableau de 10 backmove. + * ``` + * + * Si l'allocation échoue, affiche un message d'erreur et quitte le programme. + * + * @attention + * Le pointeur `p` doit avoir été déclaré, mais ne pas pointer vers une zone + * déjà allouée dynamiquement (sous peine de créer une fuite mémoire). + * Si `p` pointe déjà vers une zone allouée dynamiquement, il faut utiliser + * REALLOC. + * + * @see REALLOC() + */ + +#define MALLOC(p, num_objects) \ + do { \ + void *tmp = malloc(sizeof *(p) * (num_objects)); \ + if (!tmp) { \ + FATAL("Malloc error."); \ + } \ + (p) = tmp; \ + } while (0) + +/** + * @brief Macro pour allouer de la mémoire en utilisant la fonction calloc(). + * @param p Pointeur à allouer. + * @param num_objects Nombre d'objets à allouer. + * + * @details + * Alloue de la mémoire initialisée à 0, permettant de stocker `num_objects`, + * chacun de type `*p`. + * + * **Exemple d'utilisation :** + * ``` + * backmove *p; + * CALLOC(p, 10); // Alloue un tableau de 10 backmove, initialisé à 0. + * ``` + * + * Si l'allocation échoue, affiche un message d'erreur et quitte le programme. + * + * @attention + * Le pointeur `p` doit avoir été déclaré, mais ne pas pointer vers une zone + * déjà allouée dynamiquement (sous peine de créer une fuite mémoire). + * Si `p` pointe déjà vers une zone allouée dynamiquement, il faut utiliser + * REALLOC. + * + * @sa REALLOC() + */ + +#define CALLOC(p, num_objects) \ + do { \ + void *tmp = calloc((num_objects), sizeof *(p)); \ + if (!tmp) { \ + FATAL("Calloc error."); \ + } \ + (p) = tmp; \ + } \ + while (0) + +/** + * @brief Macro pour réallouer de la mémoire en utilisant la fonction realloc(). + * @param p Pointeur à allouer. + * @param num_objects Nouveau nombre d'objets à allouer. + * + * Ré-alloue la mémoire pointée par un pointeur p, dont la valeur doit avoir été + * retournée précédemment par une fonction d'allocation dynamique (malloc, + * calloc, realloc ou une des macros de ce fichier). + * + * Si l'allocation échoue, affiche un message d'erreur et quitte le programme. + * + * **Exemple d'utilisation :** + * ``` + * backmove *p; + * MALLOC(p, 10); // Alloue un tableau de 10 backmove. + * // ... + * REALLOC(p, 20); // Ré-alloue un tableau de 20 backmove en copiant le tableau + * // précédent en début ce nouveay tableau, et affecte à p + * // l'adresse de la première case de ce tableau. + * ``` + * + * Si l'allocation échoue, affiche un message d'erreur et quitte le programme. + * + * @attention + * Le pointeur `p` doit avoir été déclaré et pointer vers une zone + * déjà allouée dynamiquement. + * + * @sa MALLOC(), CALLOC() + */ + +#define REALLOC(p, num_objects) \ + do { \ + void *tmp = realloc((p), sizeof *(p) * (num_objects)); \ + if (!tmp) { \ + FATAL("Realloc error."); \ + } \ + (p) = tmp; \ + } while (0) + + +/** + * @brief Macro permettant de tester si un ou plusieurs pointeurs sont NULL. + * @param n Nombre de pointeurs à tester. + * @param ... Pointeurs à tester, en alternance avec des chaînes de caractères. + * + * Il doit y avoir un pointeur à tester pour chaque chaîne de caractères, et + * inversement. + * + * La chaîne de caractères qui suit chaque pointeur permet de personnaliser le + * message d'erreur. + * + * @details + * **Exemple d'utilisation :** + * ``` + * CHECK_NULL(3, p1, "Name1", p2, "Name2", p3, "Name3"); + * ``` + * Ici, 3 est le nombre de pointeurs à vérifier, et p1, p2, p3 sont les + * pointeurs. Si `p1` et `p3` sont NULL, la macro affichera un message d'erreur + * de la forme : + *** + [CRITICAL] Name1 is NULL!\n + [CRITICAL] Name3 is NULL! + *** + * @sa check_null() + */ + +#define CHECK_NULL(n, ...) \ + check_null \ + (__func__, __FILE__, __LINE__, \ + n __VA_OPT__( , ) __VA_ARGS__) + +/** + * @brief Ne pas utiliser cette fonction directement, mais à la place la macro + * ::CHECK_NULL(), qui indique dans le message la position de l'erreur dans le + * source. + * + * @sa CHECK_NULL() + */ + +void check_null(const char *function, char *file, int line, int n, ...); + +#endif // ALLOC_H_ diff --git a/tp-huffman/alloc.o b/tp-huffman/alloc.o new file mode 100644 index 0000000..f8bd5c0 Binary files /dev/null and b/tp-huffman/alloc.o differ diff --git a/tp-huffman/binheap.c b/tp-huffman/binheap.c new file mode 100644 index 0000000..5f8f7ad --- /dev/null +++ b/tp-huffman/binheap.c @@ -0,0 +1,138 @@ +#include "binheap.h" + +/*************************/ +/* Fonctions auxiliaires */ +/*************************/ + +/* La fonction de comparaison pour les tas d'entiers */ +bool fcmp_int(void *x, void *y) { + return *(int *)x < *(int *)y; +} + +/* Fonctions de navigation dans un arbre représenté par un tableau */ +int left_binheap(int i) { + return i*2+1; +} +int right_binheap(int i) { + return i*2+2; +} +int parent_binheap(int i) { + return (i-1)/2; +} +bool isvalid_binheap(binheap *p, int i) { + return i < p->size_heap; +} + +/* Modification de la taille du tableau */ +static void grow_binheap(binheap *p) { + p->size_array *= 2; + p->array = realloc(p->array, p->size_array * sizeof(void *)); + if (p->array == NULL) { + ERROR("Erreur d'allocation mémoire\n"); + exit(EXIT_FAILURE); + } +} + +static void shrink_binheap(binheap *p) { + p->size_array /= 2; + p->array = realloc(p->array, p->size_array * sizeof(void *)); + if (p->array == NULL) { + ERROR("Erreur d'allocation mémoire\n"); + exit(EXIT_FAILURE); + } +} + +/************************/ +/* Fonctions primitives */ +/************************/ + +/* Création d'un tas vide */ +binheap *create_binheap(bool (*fc)(void *, void *)) { + binheap *p = malloc(sizeof(binheap)); + if (p == NULL) { + ERROR("Erreur d'allocation mémoire\n"); + exit(EXIT_FAILURE); + } + p->array = malloc(1 * sizeof(void *)); + if (p->array == NULL) { + ERROR("Erreur d'allocation mémoire\n"); + exit(EXIT_FAILURE); + } + p->size_array = 1; + p->size_heap = 0; + p->fc = fc; + return p; +} + +/* Suppression */ +void delete_binheap(binheap *p) { + if (p != NULL) { + if (p->array != NULL) { + free(p->array); + } + free(p); + } +} + +/* Test du vide */ +bool isempty_binheap(binheap *p) { + return p == NULL || p->size_heap == 0; +} + +/* Récupération de la taille */ +int getsize_binheap(binheap *p) { + return p->size_heap; +} + +/* Insertion d'une valeur */ +void push_binheap(binheap *p, void *val) { + if (p->size_heap == p->size_array) { + grow_binheap(p); + } + p->array[p->size_heap] = val; + p->size_heap++; + int i = p->size_heap - 1; + while (i > 0 && p->fc(p->array[i], p->array[parent_binheap(i)])) { + void *temp = p->array[i]; + p->array[i] = p->array[parent_binheap(i)]; + p->array[parent_binheap(i)] = temp; + i = parent_binheap(i); + } +} + +/* Récupération du minimum sans le retirer */ +void *peekmin_binheap(binheap *p) { + if (isempty_binheap(p)) { + return NULL; + } + return p->array[0]; +} + +/* Récupération du minimum en le retirant */ +void *popmin_binheap(binheap *p) { + if (isempty_binheap(p)) { + return NULL; + } + void *min = p->array[0]; + p->size_heap--; + p->array[0] = p->array[p->size_heap]; + int i = 0; + while (isvalid_binheap(p, left_binheap(i))) { + int j = left_binheap(i); + if (isvalid_binheap(p, right_binheap(i)) && p->fc(p->array[right_binheap(i)], p->array[j])) { + j = right_binheap(i); + } + if (p->fc(p->array[j], p->array[i])) { + void *temp = p->array[i]; + p->array[i] = p->array[j]; + p->array[j] = temp; + i = j; + } else { + break; + } + } + if (p->size_heap < p->size_array / 4) { + shrink_binheap(p); + } + return min; +} diff --git a/tp-huffman/binheap.d b/tp-huffman/binheap.d new file mode 100644 index 0000000..9482ed5 --- /dev/null +++ b/tp-huffman/binheap.d @@ -0,0 +1 @@ +binheap.o: binheap.c binheap.h alloc.h error.h diff --git a/tp-huffman/binheap.h b/tp-huffman/binheap.h new file mode 100644 index 0000000..f01ab75 --- /dev/null +++ b/tp-huffman/binheap.h @@ -0,0 +1,82 @@ +/************************************************************************/ +/** Files de priorité: implémentation d'une tas binaire par un tableau **/ +/************************************************************************/ + +#ifndef BINHEAP_H_ +#define BINHEAP_H_ + +#include "alloc.h" +#include +#include + +// On travaille avec des tas génériques: le type des valeurs n'est pas fixé +// Pour ce faire, les éléments du tas seront des pointeurs génériques (void *). +// Le tas ne contiendra donc pas directement les valeurs mais des pointeurs +// sur celles-ci. +// +// Puisque le type des valeurs n'est pas fixé, la fonction de comparaison qui +// sert à ordonner les éléments du tas est un paramètre de ce tas. On utilise +// pour cela des pointeurs de fonctions: un des champs du type sera un pointeur +// sur une fonction qui associe un Booléen à deux éléments de type void * (qui +// indique si le premier est considéré comme "plus petit" que le deuxième). + +typedef struct { + // Tableau stockant des pointeurs vers chaque valeur du tas + void **array; + // Taille totale du tableau + int size_array; + // Taille du tas (nombres de case utilisées dans le tableau) + int size_heap; + // Pointeur sur la fonction de comparaison utilisée par le tas + bool (*fc)(void *, void *); +} binheap; + +/*************************/ +/* Fonctions auxiliaires */ +/*************************/ + +/* La fonction de comparaison pour les tas d'entiers */ +bool fcmp_int(void *x, void *y); +// Déjà écrite dans binheap.c + +/* Fonctions de navigation dans un arbre représenté par un tableau */ +int left_binheap(int i); +// Retourne l'indice du fils gauche du noeud à l'indice i +int right_binheap(int i); // Fils droit +// Retourne l'indice du fils droit du noeud à l'indice i +int parent_binheap(int i); // Parent +// Retourne l'indice du parent du noeud à l'indice i +bool isvalid_binheap(binheap *p, int i); +// Teste si l'indice i correspond à un noeud est valide dans le tas p + +/************************/ +/* Fonctions primitives */ +/************************/ + +/* Création d'un tas vide */ +binheap *create_binheap(bool (*)(void *, void *)); +// Le paramètre est un pointeur de fonction +// Il faut lui donner l'adresse de la fonction de comparaison à utiliser +// Par exemple, pour créer un tas d'entier on appellera la primitive avec +// l'adresse de la fonction fcmp_int ci-dessus: +// p = create_binheap(&fcmp_int); + +/* Suppression */ +void delete_binheap(binheap *); + +/* Test du vide */ +bool isempty_binheap(binheap *); + +/* Taille */ +int getsize_binheap(binheap *); + +/* Insertion d'une valeur */ +void push_binheap(binheap *, void *); + +/* Récupération du minimum sans le retirer */ +void *peekmin_binheap(binheap *); + +/* Récupération du minimum en le retirant */ +void *popmin_binheap(binheap *); + +#endif diff --git a/tp-huffman/binheap.o b/tp-huffman/binheap.o new file mode 100644 index 0000000..3737bc5 Binary files /dev/null and b/tp-huffman/binheap.o differ diff --git a/tp-huffman/dequeue.h b/tp-huffman/dequeue.h new file mode 100644 index 0000000..625d599 --- /dev/null +++ b/tp-huffman/dequeue.h @@ -0,0 +1,92 @@ +/******************************************************/ +/** Implémentation des tuyaux étendus par un tableau **/ +/******************************************************/ + +// Ce module permet de manipuler des tuyaux, et même en fait une extension du +// type vu en cours d'algorithmique. En plus de pouvoir ajouter ou retirer un +// élément à gauche ou à droite du tuyau, on peut aussi lire la i-ème valeur à +// partir de la gauche ou de la droite (en revanche, on ne peut pas écrire "au +// milieu" d'un tuyau). Voir les fonctions leftread et rightread. +// +// Ces tuyaux étendus sont implémentés par un tableau dynamique. Les fonctions +// auxiliaires grow_dequeue et shrink_dequeue permettent de modifier la taille +// du tableau en conséquence. La fonction print_dequeue permet d'afficher le +// contenu du tuyau. +// +// Une version compilée Linux de ce module vous est fournie : dequeue.o. + +#ifndef DEQUEUE_H_ +#define DEQUEUE_H_ + +#include + +/**********************/ +/* Définition du type */ +/**********************/ + +typedef struct { + int *array; // Tableau des valeurs + int size_array; // Taille du tableau des valeurs + int left; // Indice de la valeur à gauche du tuyau (si non-vide). + int right; // Indice qui suit celui de la valeur à droite du tuyau (si + // non-vide). + bool empty; // Booléen indiquant si le tuyau est vide. +} dequeue; + +/*************************/ +/* Fonctions auxiliaires */ +/*************************/ + +// Double la taille du tableau utilisé dans la représentation. +void grow_dequeue(dequeue *); + +// Divise par deux la taille du tableau utilisé dans la représentation +// (on supposera que seulement la moitié des cases sont utilisées dans +// la représentation). +void shrink_dequeue(dequeue *); + +/************************/ +/* Fonctions primitives */ +/************************/ + +// Création +dequeue *create_dequeue(void); + +// Suppression +void delete_dequeue(dequeue *); + +// Taille +int getsize_dequeue(dequeue *p); + +// Test du vide +bool isempty_dequeue(dequeue *); + +// Insertion +void leftinsert(int, dequeue *); +void rightinsert(int, dequeue *); + +// Lecture + +// Les deux fonctions suivantes étendent le type abstrait tuyau vu en TD. +// En effet, elles permettent de lire la i-ème valeur à partir de la gauche ou +// de la droite, ce qui n'était pas possible avec le type abstrait tuyau. + +// Lit la ième valeur en partant de la gauche (la première à gauche a pour +// indice 0). +int leftread(dequeue *, int i); + +// Lit la ième valeur en partant de la droite (la première à droite a pour +// indice 0). +int rightread(dequeue *, int i); + +// Suppression + +int leftpull(dequeue *); + +int rightpull(dequeue *); + +// Affichage d'un tuyau +// Cette primitive est déjà écrite. Utilisez-la pour vos tests. +void print_dequeue(dequeue *p); + +#endif // DEQUEUE_H_ diff --git a/tp-huffman/dequeue.o b/tp-huffman/dequeue.o new file mode 120000 index 0000000..4fea2bb --- /dev/null +++ b/tp-huffman/dequeue.o @@ -0,0 +1 @@ +dequeue_x86-64.o \ No newline at end of file diff --git a/tp-huffman/dequeue_arm64.o b/tp-huffman/dequeue_arm64.o new file mode 100644 index 0000000..b362d24 Binary files /dev/null and b/tp-huffman/dequeue_arm64.o differ diff --git a/tp-huffman/dequeue_x86-64.o b/tp-huffman/dequeue_x86-64.o new file mode 100644 index 0000000..778a621 Binary files /dev/null and b/tp-huffman/dequeue_x86-64.o differ diff --git a/tp-huffman/error.h b/tp-huffman/error.h new file mode 100644 index 0000000..cf79c2b --- /dev/null +++ b/tp-huffman/error.h @@ -0,0 +1,58 @@ +#ifndef ERROR_H_ +#define ERROR_H_ + +#define ATTENTION "\xe2\x9d\x97" +#define HINT "\xe2\x9c\xa8" +#define OK "\xE2\x9C\x85" +#define KO "\xE2\x9D\x8C" + +// In X mode, all messages more severe than X are printed. +// clang-format off +enum debug_level {OFF, CRITICAL, ERROR, WARNING, INFO, DEBUG, TRACE}; +enum msg_level {CRITICAL_L, ERROR_L, WARNING_L, INFO_L, DEBUG_L, TRACE_L}; +// clang-format on + +#ifndef DEBUG_LEVEL +#define DEBUG_LEVEL INFO +#endif + +// __VA_OPTS__ requires C2x. Clang issues spurious warning. + +#define PRINT_ERROR(symbol, name, function, file, line, msg, ...) \ + do { \ + fprintf(stderr, \ + "\n" symbol \ + " [" \ + name \ + "]" \ + " - Function %s (%s:%d) -\n " \ + msg "\n", \ + function, file, line \ + __VA_OPT__( , ) __VA_ARGS__); \ + } while (0) + +#define FATAL(msg, ...) \ + do { \ + PRINT_ERROR(ATTENTION, "FATAL", __func__, __FILE__, __LINE__, msg __VA_OPT__( , ) __VA_ARGS__); \ + exit(EXIT_FAILURE); \ + } while (0) + +#define PRINT_DEBUG(symbol, name, level, msg, ...) \ + do { \ + if ((int)DEBUG_LEVEL > (int)level) \ + PRINT_ERROR(symbol, name, __func__, __FILE__, __LINE__, msg __VA_OPT__( , ) __VA_ARGS__); \ + } while (0) + +#define TRACE(msg, ...) PRINT_DEBUG(OK, "TRACE", TRACE_L, msg, __VA_ARGS__) + +#define DEBUG(msg, ...) PRINT_DEBUG(HINT, "DEBUG", DEBUG_L, msg, __VA_ARGS__) + +#define INFO(msg, ...) PRINT_DEBUG(HINT, "INFO", INFO_L, msg, __VA_ARGS__) + +#define WARNING(msg, ...) PRINT_DEBUG(ATTENTION, "WARNING", WARNING_L, msg, __VA_ARGS__) + +#define ERROR(msg, ...) PRINT_DEBUG(ATTENTION, "ERROR", ERROR_L, msg, __VA_ARGS__) + +#define CRITICAL(msg, ...) PRINT_DEBUG(KO, "CRITICAL", CRITICAL_L, msg, __VA_ARGS__) + +#endif // ERROR_H_ diff --git a/tp-huffman/huffcomp.c b/tp-huffman/huffcomp.c new file mode 100644 index 0000000..e6f905c --- /dev/null +++ b/tp-huffman/huffcomp.c @@ -0,0 +1,59 @@ +/************************************/ +/** Codage de Huffman: Compression **/ +/************************************/ + +#include "huffcomp.h" + +/**********************************/ +/* Fonctions manipulant les codes */ +/**********************************/ + +/* Construction du tableau des codes à partir de l'arbre de Huffman */ +huffcode **create_huffdict(huffnode *tree) { + // À écrire + return NULL; +} + +/* Suppression du tableau de code */ +void delete_huffdict(huffcode **dict) { + // À écrire +} + +/***********************************************/ +/** Fonction pour la compression d'un fichier **/ +/***********************************************/ + +/* Écriture d'un unsigned int sur 4 octets dans un fichier */ +void write_size(FILE *output, unsigned int size) { + // À écrire +} + +/* Insertion d'un code à droite d'un tuyau servant de tampon */ +void insert_code_in_buffer(dequeue *buffer, huffcode *code) { + // À écrire +} + +/* Calcule l'octet correspondant aux 8 derniers éléments d'un tuyau servant de + * tampon */ +int buffer_to_byte(dequeue *buffer) { + // À écrire + return 0; +} + +/* Procédure de compression */ +void compress_file(FILE *in, FILE *out) { + /* Phase 1: calcul de la taille de l'arbre et du dictionnaire à partir du + * fichier source */ + + // À écrire + + /* Phase 2: écriture de la taille du fichier original */ + + // À écrire + + /* Phase 3: écriture de l'arbre en représentation préfixe */ + + // À écrire + + /* Phase 4: écriture du codage */ +} diff --git a/tp-huffman/huffcomp.d b/tp-huffman/huffcomp.d new file mode 100644 index 0000000..e159f55 --- /dev/null +++ b/tp-huffman/huffcomp.d @@ -0,0 +1,2 @@ +huffcomp.o: huffcomp.c huffcomp.h alloc.h error.h dequeue.h hufftree.h \ + binheap.h diff --git a/tp-huffman/huffcomp.h b/tp-huffman/huffcomp.h new file mode 100644 index 0000000..7d47610 --- /dev/null +++ b/tp-huffman/huffcomp.h @@ -0,0 +1,71 @@ +/************************************/ +/** Codage de Huffman: Compression **/ +/************************************/ + +#ifndef HUFFCOMP_H_ +#define HUFFCOMP_H_ + +#include "alloc.h" +#include "dequeue.h" +#include "hufftree.h" +#include +#include + +/************************************************/ +/* Type utilisé pour stocker un code de Huffman */ +/************************************************/ + +// Type pour représenter un seul code +typedef struct { + // Le nombre de bits dans le code (un code n'aura jamais plus de 256 bits). + unsigned char size; + + // Le tableau qui stocke le code : les valeurs sont toutes 0 ou 1. + unsigned char *code; +} huffcode; + +// Le dictionnaire contiendra tous les codes. +// Il sera de type huffcode **, et pointera vers 256 cases. +// Chacun de ses éléments sera donc de type huffcode *. +// Pour les valeurs i ayant un code, la ième case contient ce code +// Pour les valeurs i n'ayant pas de code (de fréquence nulle), la ième case +// contient NULL. + +/********************************************/ +/* Fonction de construction du dictionnaire */ +/********************************************/ + +// Construction du dictionnaire à partir de l'arbre de Huffman. +huffcode **create_huffdict(huffnode *tree); + +// Suppression du dictionnaire +void delete_huffdict(huffcode **dict); + +/***********************************************/ +/** Fonction pour la compression d'un fichier **/ +/***********************************************/ + +// Écriture d'un unsigned int sur 4 octets dans un fichier. +// output doit être ouvert en écriture. +void write_size(FILE *output, unsigned int size); + +// Insertion de tous les bits d'un code à droite d'un tuyau tampon. +// Le tuyau est supposé contenir une séquence de 0 et de 1. +void insert_code_in_buffer(dequeue *buffer, huffcode *code); + +// Calcule l'octet correspondant aux 8 premiers bits à gauche d'un tuyau tampon. + +// Le tuyau est supposé contenir une séquence de bits (0 ou 1) de longueur au +// moins 8 Les 8 bits lus doivent être supprimés du tuyau. +int buffer_to_byte(dequeue *buffer); + +// Fonction de compression du fichier input dans output. +// input doit être ouvert en lecture. +// output doit être ouvert en écriture. +// Le fichier output devra contenir dans l'ordre : +// 1) La taille de input codée sur 4 octets +// 2) La sauvegarde de l'arbre de Huffman obtenu à partir de input. +// 3) Le codage de input. +void compress_file(FILE *input, FILE *output); + +#endif diff --git a/tp-huffman/huffcomp.o b/tp-huffman/huffcomp.o new file mode 100644 index 0000000..a512bee Binary files /dev/null and b/tp-huffman/huffcomp.o differ diff --git a/tp-huffman/huffdecomp.c b/tp-huffman/huffdecomp.c new file mode 100644 index 0000000..5bc1e1a --- /dev/null +++ b/tp-huffman/huffdecomp.c @@ -0,0 +1,34 @@ +#include "huffdecomp.h" + +/**************************************************/ +/**************************************************/ +/** Fonctions pour la décompression d'un fichier **/ +/**************************************************/ +/**************************************************/ + +/* Lecture de la taille du fichier décompressé dans le fichier compressé */ +unsigned int read_size(FILE *in) { + // À écrire + return 0; +} + +// Décodage d'une valeur à partir d'un arbre de Huffman +int read_huffcode(FILE *in, dequeue *deq, huffnode *thetree) { + // À écrire + return 0; +} + +/* Décompression */ +void decompress_file(FILE *in, FILE *out) { + /* Phase 1: lecture de la taille du fichier décompressé */ + + // À écrire + + /* Phase 2: lecture de l'arbre de Huffman */ + + // À écrire + + /* Phase 3: décodage */ + + // À écrire +} diff --git a/tp-huffman/huffdecomp.d b/tp-huffman/huffdecomp.d new file mode 100644 index 0000000..c0ec71c --- /dev/null +++ b/tp-huffman/huffdecomp.d @@ -0,0 +1,2 @@ +huffdecomp.o: huffdecomp.c huffdecomp.h alloc.h error.h dequeue.h \ + hufftree.h binheap.h diff --git a/tp-huffman/huffdecomp.h b/tp-huffman/huffdecomp.h new file mode 100644 index 0000000..cbaf737 --- /dev/null +++ b/tp-huffman/huffdecomp.h @@ -0,0 +1,40 @@ +/**************************************/ +/** Codage de Huffman: Décompression **/ +/**************************************/ + +#ifndef HUFFDECOMP_H_ +#define HUFFDECOMP_H_ + +#include "alloc.h" +#include "dequeue.h" +#include "hufftree.h" +#include +#include + +// Lecture de la taille du fichier décompressé dans le fichier compressé. +// Le fichier doit être ouvert en lecture. +// La taille est codée sur les 4 premiers octets. +unsigned int read_size(FILE *in); + +// Lecture d'un code de Huffman à partir du buffer et du fichier compressé. +// Le fichier doit être ouvert en lecture. +// +// Lit un code entier et retourne la valeur qui lui est associée par l'arbre de +// Huffman. +// +// Tant que le buffer est non-vide, c'est dans celui-ci qu'on lit. +// +// Si le buffer est vide, on lit un octet dans le fichier pour insérer ses 8 +// bits dans le buffer. +// +// Le buffer est potentiellement non-vide à la fin de la lecture d'un code. Les +// bits qui restent dans le buffer font partie du prochain code à lire. +int read_huffcode(FILE *in, dequeue *deq, huffnode *thetree); + +// Procédure de décompression. +// Le flux in doit être ouvert en lecture. +// Le flux out doit être ouvert en écriture. +// Décompresse le fichier in et écrit la version décompressée dans out. +void decompress_file(FILE *in, FILE *out); + +#endif diff --git a/tp-huffman/huffdecomp.o b/tp-huffman/huffdecomp.o new file mode 100644 index 0000000..8bc89e6 Binary files /dev/null and b/tp-huffman/huffdecomp.o differ diff --git a/tp-huffman/hufftree.c b/tp-huffman/hufftree.c new file mode 100644 index 0000000..9cff40d --- /dev/null +++ b/tp-huffman/hufftree.c @@ -0,0 +1,85 @@ +#include "hufftree.h" + +/************************************/ +/* Primitives des arbres de Huffman */ +/************************************/ + +/* Création d'une feuille */ +huffnode *create_huffleaf(int byte, int freq) { + // À écrire + return NULL; +} + +/* Fusion de deux arbres avec un nouveau noeud racine */ +huffnode *merge_hufftree(huffnode *pl, huffnode *pr) { + // À écrire + return NULL; +} + +/* Teste si un noeud est une feuille */ +bool isleaf_huffnode(huffnode *p) { + // À écrire + return true; +} + +/* Retourne la valeur de l'octet correspondant à un noeud */ +int getbyte_huffnode(huffnode *p) { + // À écrire + return 0; +} + +/* Retournent les fils d'un noeud */ + +huffnode *getleft_huffnode(huffnode *p) { + // À écrire + return NULL; +} +huffnode *getright_huffnode(huffnode *p) { + // À écrire + return NULL; +} + +/* Libération d'un arbre */ + +void free_hufftree(huffnode *p) { + // À écrire +} + +/**********************************************/ +/* Fonctions manipulant les arbres de Huffman */ +/**********************************************/ + +/* Comparaison de deux arbres */ +bool compare_hufftree(void *p1, void *p2) { + // À écrire + return true; +} + +/* Création de l'arbre de Huffman à partir du fichier à compresser */ +huffnode *datafile_to_hufftree(FILE *input) { + /* Phase 1: création du tableau de fréquences */ + + // À écrire + + /* Phase 2: intialisation de la file de priorité à partir du tableau de + * fréquences */ + + // À écrire + + /* Phase 3: création de l'arbre de Huffman à partir de la file de priorités */ + + // À écrire + + return NULL; +} + +/* Écriture de l'arbre de Huffman dans le futur fichier compressé */ +void save_hufftree(huffnode *p, FILE *f) { + // À écrire +} + +/* Lecture de l'arbre de Huffman dans le fichier compressé */ +huffnode *read_hufftree(FILE *f) { + // À écrire + return NULL; +} diff --git a/tp-huffman/hufftree.d b/tp-huffman/hufftree.d new file mode 100644 index 0000000..419befa --- /dev/null +++ b/tp-huffman/hufftree.d @@ -0,0 +1 @@ +hufftree.o: hufftree.c hufftree.h alloc.h error.h binheap.h diff --git a/tp-huffman/hufftree.h b/tp-huffman/hufftree.h new file mode 100644 index 0000000..1ca106a --- /dev/null +++ b/tp-huffman/hufftree.h @@ -0,0 +1,98 @@ +/**************************************/ +/** Les arbres de Huffman **/ +/** Implémentation par des pointeurs **/ +/**************************************/ + +#ifndef HUFFTREE_H_ +#define HUFFTREE_H_ + +#include "alloc.h" +#include "binheap.h" +#include +#include + +/*******************************************/ +/* Type utilisé pour les arbres de Huffman */ +/*******************************************/ + +// Chaque objet correspond à un noeud de l'arbre. +// On identifie un arbre avec un pointeur vers son noeud racine. +// +// Cela explique pourquoi les fonctions manipulant les arbres prennent en +// argument un pointeur vers un noeud, et que leur nom se termine soit par +// _huffnode, soit par _hufftree. + +typedef struct huffnode { + // Fréquence + int freq; + + // Octet codé par le noeud (significatif seulement si le noeud est une + // feuille) + int byte; + + // Enfant gauche (NULL si pas de fils gauche) + struct huffnode *leftchild; + + // Enfant droit (NULL si pas de fils droit) + struct huffnode *rightchild; +} huffnode; + +/************************************/ +/* Primitives des arbres de Huffman */ +/************************************/ + +// Création d'une feuille. +// Les paramètres servent à initialiser les champs byte et freq (dans cet +// ordre). +huffnode *create_huffleaf(int byte, int freq); + +// Fusion de deux arbres non-vides en utilisant un nouveau noeud racine. +// La fréquence du nouveau noeud sera la somme de celles de ses deux enfants +// Le champ byte du nouveau noeud n'est pas significatif (car ce n'est pas une +// feuille). +huffnode *merge_hufftree(huffnode *pl, huffnode *pr); + +// Teste si un arbre est réduit à une seule feuille. +// On doit tester si les deux enfants du noeud racine sont NULL +bool isleaf_huffnode(huffnode *p); + +// Retourne la valeur de l'octet codé dans un noeud. +// Ne doit être utilisé que sur les feuilles. +int getbyte_huffnode(huffnode *p); + +// Retourne les enfants gauche et droit d'un noeud. +huffnode *getleft_huffnode(huffnode *p); +huffnode *getright_huffnode(huffnode *p); + +// Libération complète d'un arbre de Huffman. +void free_hufftree(huffnode *p); + +/**********************************************/ +/* Fonctions manipulant les arbres de Huffman */ +/**********************************************/ + +// Fonction de comparaison entre deux arbres de Huffman à utiliser pour le tas +// binaire. Les deux paramètres sont donc donnés par des void *. Il faut les +// convertir ces pointeurs de façon appropriée dans la fonction +// compare_hufftree. +// +// Le plus petit de deux arbres est celui dont la racine a la plus petite +// fréquence. +bool compare_hufftree(void *p1, void *p2); + +// Création de l'arbre de Huffman à partir du fichier à compresser. +// Le fichier doit être ouvert en lecture. +// L'algorithme est basé sur les files de priorités. +huffnode *datafile_to_hufftree(FILE *input); + +// Écriture de l'arbre de Huffman dans le futur fichier compressé. +// Le fichier doit être ouvert en lecture. +// On stocke l'arbre par une représentation préfixe. +void save_hufftree(huffnode *p, FILE *output); + +// Lecture de l'arbre de Huffman dans un fichier. +// Le fichier doit être ouvert en lecture. +// La tête de lecture doit être positionnée au début du codage de l'arbre. +huffnode *read_hufftree(FILE *in); + +#endif diff --git a/tp-huffman/hufftree.o b/tp-huffman/hufftree.o new file mode 100644 index 0000000..b6f41be Binary files /dev/null and b/tp-huffman/hufftree.o differ diff --git a/tp-huffman/main.c b/tp-huffman/main.c new file mode 100644 index 0000000..771e006 --- /dev/null +++ b/tp-huffman/main.c @@ -0,0 +1,312 @@ +#include "alloc.h" +#include "binheap.h" +#include "error.h" +#include "huffcomp.h" +#include "huffdecomp.h" +#include "hufftree.h" +#include "testprint.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void test_heap(int *tab, int size); + +/********************************/ +/* Fonction main pour les tests */ +/********************************/ + +// clang-format off +static struct option long_options[] = { + {"help" , no_argument, NULL, 'h'}, + {"tree" , no_argument , NULL, 'T'}, + {"dict" , no_argument , NULL, 'D'}, + {"decomp" , no_argument, NULL, 'd'}, + {"heap" , optional_argument, NULL, 'H'}, + {"output" , required_argument, NULL, 'o'}, + {"seed" , required_argument, NULL, 's'}, + // + {NULL, 0, NULL, 0}}; +// clang-format on + +#define DEFAULT_HEAP_SIZE 10 +int size_heap = DEFAULT_HEAP_SIZE; +int seed = -1; +bool decompress_mode = false, test_binheap_mode = false, huffdict = false, + hufftree = false; +char *outfilename = NULL; + +static void usage(char *progname) { + printf("\nUsage: %s [options]\n\n", progname); + printf("Options disponibles :\n" + "\t--help ou -h :" + "\n\t\tAffiche cette aide.\n" + "\n" + "\t--tree ou -T:" + "\n\t\tAffiche l'arbre de Huffman en cas de compression (ce n'est pas " + "fait par défaut).\n" + "\n" + "\t--dict ou -D:" + "\n\t\tAffiche le dictionnaire en cas de compression (ce n'est pas " + "fait par " + "défaut).\n" + "\n" + "\t--heap=ou -H:" + "\n\t\tTeste les fonctions de tas sur un tas de taille (%d " + "par défaut).\n" + "\n" + "\t--output= ou -o :" + "\n\t\tDéfinit le nom du fichier de sortie pour la décompression.\n" + "\n" + "\t--seed= ou -s :" + "\n\t\tDéfinit la graine du générateur aléatoire pour générer un " + "tableau.\n" + "\n", + DEFAULT_HEAP_SIZE); +} + +static void parse_argv(int argc, char *argv[]) { + for (;;) { + int c = getopt_long(argc, argv, "hTdDs:o:H::", long_options, NULL); + if (c == -1) + break; // fin de lecture des options + + switch (c) { + case 'h': + usage(argv[0]); + exit(EXIT_SUCCESS); + + case 'H': + test_binheap_mode = true; + size_heap = (optarg == NULL) ? DEFAULT_HEAP_SIZE : atoi(optarg); + break; + + case 's': + seed = atoi(optarg); + if (seed <= -1) { + ERROR("La graine doit être positive\n"); + usage(argv[0]); + exit(EXIT_FAILURE); + } + break; + + case 'd': + decompress_mode = true; + break; + + case 'D': + huffdict = true; + break; + + case 'T': + hufftree = true; + break; + + case 'o': + outfilename = strdup(optarg); + break; + + default: + break; + } + } +} + +int main(int argc, char *argv[]) { + parse_argv(argc, argv); + + // Initialisation du générateur de nombres aléatoires + // Voir man 3 srandom. + if (seed == -1) { + seed = time(NULL) % 1000; + INFO("Pour rejouer avec les mêmes tirages aléatoires: %s --seed=%d", + argv[0], seed); + } + srandom(seed); + + // Vous pouvez ajouter des tests pour les tas binaires et les arbres de + // Huffman. + // + // Pensez à utiliser les fonctions disponibles dans testprint.h + // pour afficher les tas binaires et les arbres de Huffman + + // printf("binheap null - \n"); print_binheap(NULL); + // binheap *heap = create_binheap(fcmp_int); + // print_binheap(heap); + // int v10 = 10; + // push_binheap(heap, &v10); + // print_binheap(heap); + // int v5 = 5; + // push_binheap(heap, &v5); + // print_binheap(heap); + // int v7 = 7; + // push_binheap(heap, &v7); + // push_binheap(heap, &v7); + // print_binheap(heap); + // popmin_binheap(heap); + // print_binheap(heap); + // popmin_binheap(heap); + // print_binheap(heap); + + // Test de binheap. Dans ce cas, on ne fait pas de compression/décompression. + if (test_binheap_mode) { + INFO("TEST DES TAS BINAIRES"); + INFO("---------------------\n"); + int *array = random_array(size_heap, 100); + + INFO("\nTableau avant tri:"); + print_array(array, size_heap); + + test_heap(array, size_heap); + + INFO("\nTableau après tri:"); + print_array(array, size_heap); + free(array); + return EXIT_SUCCESS; + } + + // Ici, compression ou décompression. + // La compression du fichier f est faite dans un fichier f.huff. + // La décompression du fichier f.huff est faite dans le fichier f. + + if (!decompress_mode) { + // Compression : tous les arguments suivants de main sont considérés comme + // des noms de fichiers à compresser. + + for (int i = optind; i < argc; i++) { + struct stat b; + + if (stat(argv[i], &b) != 0) { + ERROR("Impossible de calculer la taille du fichier \"%s\"", argv[i]); + continue; + } + + if (b.st_size == 0) { + WARNING("Fichier \"%s\" de taille nulle", argv[i]); + continue; + } + + INFO("Compression du fichier \"%s\" de taille %lld", argv[i], + (long long)b.st_size); + + FILE *infile = fopen(argv[i], "r"); + if (infile == NULL) { + ERROR("Impossible d'ouvrir le fichier à compresser \"%s\"", argv[i]); + continue; + } + + huffnode *t = datafile_to_hufftree(infile); + + // Arbre de Huffman + if (hufftree) { + INFO("Arbre de Huffman"); + print_hufftree(t); + } + + huffcode **d = create_huffdict(t); + if (huffdict) { + INFO("Affichage du dictionnaire"); + print_huffdict(d); + } + + // Création éventuelle du nom de fichier de sortie + if (outfilename == NULL) { + MALLOC(outfilename, strlen(argv[i]) + 6); + strcpy(outfilename, argv[i]); + strcpy(outfilename + strlen(outfilename), ".huff"); + } + + FILE *outfile = fopen(outfilename, "w"); + if (outfile == NULL) { + ERROR("Impossible d'ouvrir le fichier de sortie \"%s\"", outfilename); + continue; + } + + INFO("Fichier compressé -> \"%s\"", outfilename); + + // Appel de la fonction de compression + compress_file(infile, outfile); + + free(outfilename); + + // Fermeture des fichiers + fclose(infile); + fclose(outfile); + } + } + + if (decompress_mode) { + // Décompression : tous les arguments suivants de main sont considérés comme + // des noms de fichiers à décompresser. + for (int i = optind; i < argc; i++) { + FILE *infile = fopen(argv[i], "r"); + if (infile == NULL) { + ERROR("Impossible d'ouvrir le fichier d'entrée à décompresser \"%s\"", + argv[i]); + continue; + } + + INFO("Décompression de %s", argv[i]); + + // Création éventuelle du nom de fichier de sortie + if (outfilename == NULL) { + MALLOC(outfilename, strlen(argv[i]) + 8); + strcpy(outfilename, argv[i]); + strcpy(outfilename + strlen(outfilename), ".dehuff"); + } + + // Ouverture du fichier de sortie + FILE *outfile = fopen(outfilename, "w"); + if (outfile == NULL) { + ERROR("Impossible d'ouvrir le fichier de sortie \"%s\"", outfilename); + continue; + } + + // Appel de la fonction de décompression + decompress_file(infile, outfile); + + free(outfilename); + + // Fermeture des fichiers + fclose(infile); + fclose(outfile); + } + } + return EXIT_SUCCESS; +} + +// Implémentation du tri par les tas +void test_heap(int *tab, int size) { + int i; + int temp[size]; + for (i = 0; i < size; i++) { + temp[i] = tab[i]; + } + + binheap *p = create_binheap(&fcmp_int); + + printf("\nTest de push_binheap"); + printf("\n********************\n\n"); + for (i = 0; i < size; i++) { + printf("Push de %d\n", temp[i]); + push_binheap(p, temp + i); + print_binheap(p); + } + printf("\n"); + printf("\nTest de pop_binheap"); + printf("\n*******************\n\n"); + if (p == NULL) { + WARNING("Essai de pop sur un tas NULL, rien ne se passe\n"); + return; + } + for (i = 0; i < size; i++) { + tab[i] = *(int *)popmin_binheap(p); + printf("Élément supprimé: %d\n", tab[i]); + print_binheap(p); + } +} diff --git a/tp-huffman/main.d b/tp-huffman/main.d new file mode 100644 index 0000000..0f0ebf1 --- /dev/null +++ b/tp-huffman/main.d @@ -0,0 +1,2 @@ +main.o: main.c alloc.h error.h binheap.h huffcomp.h dequeue.h hufftree.h \ + huffdecomp.h testprint.h diff --git a/tp-huffman/main.o b/tp-huffman/main.o new file mode 100644 index 0000000..e060e5a Binary files /dev/null and b/tp-huffman/main.o differ diff --git a/tp-huffman/testprint.c b/tp-huffman/testprint.c new file mode 100644 index 0000000..c774015 --- /dev/null +++ b/tp-huffman/testprint.c @@ -0,0 +1,340 @@ +#include "testprint.h" +#include "alloc.h" +#include +#include +#include +#include +#include + +#define BAR "-" // un tiret + +typedef char *string; + +// Type utilisé pour représenter les arbres de Huffman en utilisant +// des tableaux (plus pratique pour écrire la fonction d'affichage) +typedef struct { + bool *array_af; + int *array_val; + int size; +} arraytree; + +// Affichage d'un tableau d'entiers +void print_array(int *tab, int size) { + int i; + printf("{"); + for (i = 0; i < size - 1; i++) { + printf("%d, ", tab[i]); + } + printf("%d}\n", tab[size - 1]); +} + +// Affichage d'un tableau de pointeurs sur des entiers +void print_point_array(int **tab, int size) { + int i; + printf("{"); + for (i = 0; i < size - 1; i++) { + printf("%d, ", *tab[i]); + } + printf("%d}\n", *tab[size - 1]); +} + +// Génération d'un tableau d'entiers aléatoire +int *random_array(int size, int max) { + int *array; + MALLOC(array, size); + int i; + for (i = 0; i < size; i++) { + array[i] = random() % (max + 1); + } + return array; +} + +// Génération d'un tas à partir d'un tableau d'entiers +binheap *array_to_binheap(int *array, int size) { + binheap *heap = create_binheap(&fcmp_int); + int i; + for (i = 0; i < size; i++) { + push_binheap(heap, (void *)(array + i)); + } + return heap; +} + +// Affiche n fois le même la chaîne s +static void rule(int n, string s) { + for (int i = 0; i < n; i++) + printf("%s", s); +} + +// Affichage d'un tas binaire +void print_binheap(binheap *p) { + rule(10, BAR); + printf("\n"); + + if (p == NULL) { + printf("Trying to print a NULL tree\n\n"); + return; + } + + int h = 0; // Calcul de la hauteur de l'arbre (=nombre de lignes) + int j = p->size_heap; + while (j > 0) { + h++; + j = j >> 1; + } + + int s = 1; // nombre de caractères avant le milieu du pattern + int B = 1; // B = nombre de patterns dans la ligne + j = 0; // j = nombre d'éléments déjà affichés + int sons = 0; + + int y; + for (y = h - 1; y >= 0; y--, B <<= 1) { + // On part du haut de l'arbre + + int pm = (s + 1) * (1 << y) - 1; + int lb = (s + 1) * (1 << (y - 1)) - 1; + int ep = 2 * pm + 1 - 2 * s; + + // ligne avec les éléments + rule(pm - s, " "); + for (int b = 0; b < B && (j < p->size_heap); b++) { + // printf(" "); // cas pair + printf("%03i ", ((int *)p->array[j])[0]); + j++; + if (b < B - 1) + rule(ep - 1, " "); // -1 pour l'espace terminal + } + printf("\n"); + if (y == 0) + break; // fini si dernière ligne d'éléments + + // ligne avec les branchements + rule(lb, " "); + for (int b = 0; b < B; b++) { + if (right_binheap(sons) < p->size_heap) { + printf("┌"); + rule(lb, "─"); + printf("┴"); + rule(lb, "─"); + printf("┐"); + if (b < B - 1) + rule(pm, " "); + } else if (left_binheap(sons) < p->size_heap) { + printf("┌"); + rule(lb, "─"); + printf("┘"); + if (b < B - 1) + rule(pm + 2, " "); + } + sons++; + } + printf("\n"); + } + + rule(10, BAR); + printf("\n\n"); +} + +// Affichage d'un arbre de Huffman +static int maxoftwo(int a, int b) { + if (a < b) { + return b; + } else { + return a; + } +} + +static int hufftree_maxsize(huffnode *n) { + if (n == NULL) { + return 0; + } else { + return 2 * maxoftwo(hufftree_maxsize(n->leftchild), + hufftree_maxsize(n->rightchild)) + + 1; + } +} + +static void hufftree_fillarray(huffnode *n, int *arrayval, bool *arraytv, + int pos) { + if (n != NULL) { + arrayval[pos] = n->byte; + arraytv[pos] = true; + hufftree_fillarray(n->leftchild, arrayval, arraytv, 2 * pos + 1); + hufftree_fillarray(n->rightchild, arrayval, arraytv, 2 * pos + 2); + } +} + +static arraytree *hufftree_to_array(huffnode *n) { + int size = hufftree_maxsize(n); + // printf("%d\n",size); + arraytree *new; + MALLOC(new, 1); + MALLOC(new->array_af, size); + MALLOC(new->array_val, size); + new->size = size; + int i; + for (i = 0; i < size; i++) { + new->array_af[i] = false; + } + hufftree_fillarray(n, new->array_val, new->array_af, 0); + return new; +} + +void print_hufftree(huffnode *n) { + arraytree *p = hufftree_to_array(n); + + rule(10, BAR); + printf("\n"); + + if (p == NULL) { + WARNING("Essai d'affichage d'un arbre de Huffman NULL\n\n"); + return; + } + + if (p->size == 0) { + WARNING("Essai d'affichage d'un arbre de Huffman vide\n\n"); + return; + } + + if (p->size == 1) { + printf(" %c\n", n->byte); + rule(10, BAR); + printf("\n\n"); + return; + } + + int h = 0; // Calcul de la hauteur de l'arbre (=nombre de lignes) + int j = p->size; + while (j > 0) { + h++; + j = j >> 1; + } + + int s = 0; // nombre de caractères avant le milieu du pattern + int B = 1; // B = nombre de patterns dans la ligne + j = 1; // j = nombre d'éléments déjà affichés + int mine1 = 1; + int sons = 0; + + int y; + for (y = h - 1; y >= 0; y--, B <<= 1) { + // On part du haut de l'arbre + + int pm = (s + 1) * (1 << y) - 1; + int lb = (s + 1) * (1 << (y - 1)) - 1; + int ep = 2 * pm + 1 - 2 * s; + + // ligne avec les éléments + if (y != h - 1) { + rule(pm - s, " "); + for (int b = 0; b < B && (mine1 < p->size); b++) { + // printf(" "); // cas pair + if (p->array_af[mine1] && mine1 % 2 == 1) { + printf("│0"); + } else if (p->array_af[mine1] && mine1 % 2 == 0) { + printf("│1"); + } else { + printf(" "); + } + mine1++; + if (b < B - 1) + rule(ep - 1, " "); // -1 pour l'espace terminal + } + printf("\n"); + + rule(pm - s, " "); + for (int b = 0; b < B && (j < p->size); b++) { + // printf(" "); // cas pair + if (p->array_af[j] && !p->array_af[2 * j + 1] && + !p->array_af[2 * j + 2]) { + char thechar = (char)p->array_val[j]; + if (thechar == '\n') { + printf("⏎ "); + } else if (thechar == ' ') { + printf("␠ "); + } else { + printf("%c ", (char)p->array_val[j]); + } + } else if (p->array_af[j]) { + printf("│ "); + } else { + printf(" "); + } + j++; + if (b < B - 1) + rule(ep - 1, " "); // -1 pour l'espace terminal + } + printf("\n"); + + if (y == 0) + break; // fini si dernière ligne d'éléments + } + + // ligne avec les branchements + rule(lb, " "); + for (int b = 0; b < B; b++) { + if (p->array_af[2 * sons + 1] && p->array_af[2 * sons + 2]) { + printf("┌"); + rule(lb, "─"); + printf("┴"); + rule(lb, "─"); + printf("┐"); + if (b < B - 1) + rule(pm, " "); + } else if (p->array_af[2 * sons + 1]) { + printf("┌"); + rule(lb, "─"); + printf("┘"); + rule(lb + 1, " "); + if (b < B - 1) + rule(pm, " "); + } else if (p->array_af[2 * sons + 2]) { + rule(lb + 1, " "); + printf("└"); + rule(lb, "─"); + printf("┐"); + if (b < B - 1) + rule(pm, " "); + + } else { + rule(2 * lb + 3, " "); + if (b < B - 1) + rule(pm, " "); + } + sons++; + } + printf("\n"); + } + + rule(10, BAR); + printf("\n\n"); +} + +/* Affichage d'un code de Huffman */ +void print_code(huffcode *e) { + CHECK_NULL(2, e, "The code entry", e->code, "The array in the code entry"); + int i; + for (i = 0; i < e->size; i++) { + printf("%d", e->code[i]); + } + printf("\n"); +} + +/* Affichage d'un tableau de codes de Huffman */ +void print_huffdict(huffcode **codes) { + int i; + for (i = 0; i < 256; i++) { + if (codes[i] != NULL) { + printf("Valeur: %3d ", i); + if (i == '\n') { + printf("ASCII: ⏎ Code: "); + } else if (i == ' ') { + printf("ASCII: ␠ Code: "); + } else { + printf("ASCII: %c Code: ", i); + } + print_code(codes[i]); + printf("\n"); + } + } +} diff --git a/tp-huffman/testprint.d b/tp-huffman/testprint.d new file mode 100644 index 0000000..7ff5974 --- /dev/null +++ b/tp-huffman/testprint.d @@ -0,0 +1,2 @@ +testprint.o: testprint.c testprint.h alloc.h error.h binheap.h huffcomp.h \ + dequeue.h hufftree.h huffdecomp.h diff --git a/tp-huffman/testprint.h b/tp-huffman/testprint.h new file mode 100644 index 0000000..75aa573 --- /dev/null +++ b/tp-huffman/testprint.h @@ -0,0 +1,74 @@ +/******************************************/ +/******************************************/ +/** Fonctions d'affichage pour les tests **/ +/******************************************/ +/******************************************/ + +#ifndef TESTPRINT_H_ +#define TESTPRINT_H_ + +#include "alloc.h" +#include "binheap.h" +#include "huffcomp.h" +#include "huffdecomp.h" +#include "hufftree.h" +#include +#include + +/************/ +/* Tableaux */ +/************/ + +// Affichage d'un tableau d'entiers. +void print_array(int *tab, int size); + +// Affichage d'un tableau de pointeurs sur des entiers. +void print_point_array(int **tab, int size); + +// Génération d'un tableau d'entiers aléatoire. +// Les entiers générés sont compris entre 0 et max. +int *random_array(int size, int max); + +/****************/ +/* Tas binaires */ +/****************/ + +// Génération d'un tas à partir d'un tableau d'entiers. +// +// Attention, les éléments dans le tas seront des pointeurs vers +// les éléments du tableau pris en entrée. Il ne faut donc plus +// modifier les valeurs référencées par les pointeurs dans le +// tableau (sous peine de casser le tas). +binheap *array_to_binheap(int *array, int size); + +// Affichage d'un tas binaire. +// +// Prévu pour un tas dont les éléments sont des pointeurs vers des entiers. +// On peut en construire un avec les fonctions précédentes. +// Attention à ne pas afficher des tas "trop gros". Ils sont trop larges +// pour un terminal et les retours à la ligne rendent l'affichage illisible. +void print_binheap(binheap *); + +/*********************/ +/* Arbres de Huffman */ +/*********************/ + +// Affichage d'un arbre de Huffman. +// +// À nouveau, l'affichage n'est lisible que pour de "petits" arbres de Huffman. +// Faites attention à tester vos fonctions sur des fichiers qui ne contiennent +// pas beaucoup de caractères distincts (afin de minimiser la taille des +// arbres). +void print_hufftree(huffnode *); + +/*******************/ +/* Code de Huffman */ +/*******************/ + +// Affichage d'un code de Huffman. +void print_code(huffcode *e); + +// Affichage d'un tableau de codes de Huffman. +void print_huffdict(huffcode **dict); + +#endif diff --git a/tp-huffman/testprint.o b/tp-huffman/testprint.o new file mode 100644 index 0000000..f48fee5 Binary files /dev/null and b/tp-huffman/testprint.o differ diff --git a/tp-huffman/tp b/tp-huffman/tp new file mode 100755 index 0000000..e8f3916 Binary files /dev/null and b/tp-huffman/tp differ