Table of Contents

Tema 4

Termen de predare: Luni, 25 Mai 2015, ora 23:55

Deadline hard: Joi, 28 Mai, ora 23:55

1 Mai Adaugat precizare legata de reprezentantul unui nod. Actualizat arhiva de teste.

23 Mai Modificare deadline: 25 Mai.

24 Mai Update script testare: limita de timp a fost marita la 100 de secunde.

Obiective

În urma realizării acestei teme, studentul va fi capabil:

Cunoştinţe necesare

Pentru a putea rezolva această temă, studentul trebuie să fie familiar cu următoarele noţiuni:

Cerinţă

Enunt

Sarcina voastră este să implementaţi un program care să comprime / decomprime un fişier arbitrar, aplicând algoritmul Huffman. Aici este descris, pe scurt, algoritmul şi aveţi şi un exemplu pentru compresia unui şir de caractere (fişier text). Algoritmul funcţionează pentru orice tip de fişier (nu neapărat text): se consideră fişierul ca un şir de octeţi.

Executabil

Programul vostru va trebui să primească la intrare numele unui fişier şi o serie de opţiuni, în felul următor:

./comp -c <nume_fisier_in>

sau

./comp -d <nume_fisier_in>.cmp

unde:

Astfel, daca numele fişierului este poza.bmp, fişierul comprimat se va numi poza.bmp.cmp, iar fişierul decomprimat decompressed_poza.bmp

Compresie

Se va considera şirul de octeti din fişier ca pe un şir de simboluri. Acestui şir de simboluri va trebui să-i aplicaţi o codificare Huffman, (a se vedea documentaţia pusă la dispoziţie) asociind fiecărui simbol o probabilitate de apariţie egală cu frecvenţa simbolului în fişier (deci va trebui să parcurgeţi la început întregul fişier pentru a determina aceste probabilităţi). Pentru reprezentarea cozii prioritare din care veţi extrage nodurile cu costul cel mai mic, veţi folosi o structură de heap. Pentru heap, veţi avea nevoie de o funcţie care compară două noduri între ele, şi veţi folosi următoarele reguli (presupunem ca prioritatea este maximă în vârful cozii):

Practic, aceste reguli specifică ordinea în care se vor scoate elementele din coadă: se preferă cele cu probabilitate mică, iar dacă sunt mai multe cu aceeaşi probabilitate, le vom lua pe cele de pe nivelurile cele mai mici, pentru a păstra echilibrat arborele. Daca si inaltimile sunt egale, luam in considerare reprezentantul. Astfel vom avem un mod unic de a scoate elementele din coada prioritara.

Reprezentantul unui nod se defineste in felul urmator:

Serializare

Arborele Huffman construit va fi necesar şi in pasul de decompresie, astfel că este nevoie sa fie salvat in fişier. Reprezentarea arborelui în fişier trebuie să fie cel de vector de structuri de forma:

struct HuffmanSerializedNode {
  	uint8_t isTerminal;
  	union {
	    uint8_t value;
	    struct {
	      uint16_t leftChild;
	      uint16_t rightChild;
	    } __attribute__((__packed__)) childData;
  	} __attribute__((__packed__));
} __attribute__((__packed__));

Mai întâi se scrie în fişier un întreg de tip uint16_t care specifică numărul de elemente de tip HuffmanSerializedNode care urmează, apoi urmează vectorul de structuri, unde câmpurile din fiecare structură semnifică:

Observam ca reprezentantul unui nod nu este salvat in fisier deoarece nu mai avem nevoie de el. Acesta a fost folosit pentru a garanta o secventa unica de extragere a nodurilor din coada prioritara.

După ce este scris arborele Huffman se scrie şi dimensiunea fişierului de intrare ca un element de tip întreg fara semn(necesar la decomprimare pentru a şti câte simboluri trebuie decodificate). Urmează simbolurile scrise codificat în ordinea în care au fost citite din fişierul original. În urma aplicării arborelui Huffman asupra simbolurilor, se obţine un şir de biţi.

Se convine ca un bit '1' să reprezinte o trecere spre descendentul drept în arborele Huffman construit, în timp ce un bit '0' să însemne o trecere spre descendentul stâng.

Aceştia vor fi scrişi in fişerul rezultat, grupaţi pe octeţi, în felul următor:

[7 6 5 4 3 2 1 0] [15 14 13 12 11 10 9 8] [23 22 21….

Conform exemplului de aici sirul “ana are mere” se codifica astfel:

Bit index01234567891011121314151617181920212223242526272829
Value011100011110100101111101100010

Octetii care vor fi scrisi in fisier sunt

Atentie: in cazul in care numarul total de biti nu este multiplu de 8, se vor adauga biti de 0 ca padding.

Pentru operaţiile pe biţi folosiţi DOAR variabile unsigned.

Recomandarea noastră este să folosiţi tipurile de date uint8_t / uint16_t / uint32_t după caz.

Format fisier comprimat

Conform celor spuse mai sus, formatul unui fisier comprimat trebuie sa arate astfel (in aceasta ordine):

CampK V N L
Semnificatienumarul de noduri din arbore arborele serializat dimensiunea fisierului original fisier comprimat
Tip/Dimensiune 1 * uint16_t K * HuffmanSerializedNode 1 * uint32_t sir de biti ce reprezinta fisierul comprimat

Testare

Testarea temei se va face folosind vmchecker.

Arhiva voastră trebuie să conţina sursele, un fişier README şi un fişier Makefile cu 2 reguli build şi clean. Regula build va compila sursele şi va genera un executabil numit comp. Regula clean va sterge fisierele temporare generate in timpul compilării (dacă există) şi executabilul comp.

Fişierul README trebuie să conţină detalii despre implementarea temei.

Puteţi descărca checker-ul pentru testare locală sub linux de aici.

Folositi flag-ul de optimizare -O2 la compilare.

Alte precizari