UNIVERSIDAD AUTÓNOMA METROPOLITANA UNIDAD IZTAPALAPA P R ~ Y E C TTERMINAL O & 6 6 L/ C MEDIANTE ~ ~ ~ ORDENAMIENTO ~ ~ ~ ~ '' REVERSIBLE PROFESOR: OSCARYÁÑEz SUÁREZ ALUMNO: LIMÓNGÓMEZZAHIDGABRIEL ' FECHA:25 DE JULIO DE 2000 ó ~ COMPRESION MEDIANTE ORDENAMIENTO REVERSIBLE Existen archivos que por el tipo de información que poseen, éstos ocupan mucho espacio en disco y que algunas veces, éstos no son utilizados frecuentemente, por lo que sólo están ocupando demasiado espacio en disco. Para reducir el tamaño del archivo y por ende, el espacio que ocupa en disco, puede utilizarse una propiedad que utilizan la mayoría de los algoritmos para comprimir archivos y que se llama redundancia, esto es, que en un archivo puede encontrarse repetidas algunas o demasiadas veces, un mismo símbolo ó cadenas de éstos símbolos y que contando la frecuencia y posición en donde se hallaba el símbolo o la cadena de símbolos, puede codificarse la información para que el archivo resultante sea de menor tamaño en comparación con el original sin que ocurra pérdida de información, ya que el tipo de información puede o no exigir q,ue sea mantenida la información original sin alteraciones. El propósito de este proyecto es de implementar un algoritmo compresor de archivos que maximiza la propiedad de redundancia de un tipo específico de archivos, concretamente archivos que guardan información de señales electrocardiográficais. , que como se conoce éstas poseen mucha redundancia, cualidad que es aprovechada aplicando un primer algoritmo llamado Block Sorting Lossless (Ordenamiento de Bloques Sin Pérdidas) cuya función es la de aumentar la redundancia del archivo reordenando la cadena o archivo aumentando su redundancia para después ser pasado a un segundo algoritmo llamado Move-To-Front para aumentar símbolos que al comprimirlos ocupen el menor espacio posible, y por último, el tercer algoritmo que es en sí el verdadero compresor, el algoritmo de Huffinan, las dos etapas anteriores son de preprocesamiento para aumentar la redundancia y maximizar la razón de compresión de Hut'fman, este algoritmo debe trabajar también a una velocidad razonable, ya que hay un compromiso de velocidad-razón de conipresión que debe al menos ser similar a los compresores conocidos comúnmente. Obviamente, el compresor debe tener su contraparte, el descompresor, que aplica las tres etapas anteriores en orden inverso y que procesan inversamente la información, hasta obtener la información exactamente igual a la original. Aunque éste programa está hecho específicamente para comprimir señales electrocardiográficas, también podría ser utilizado para comprimir otros tipos de archivos. El algoritmo aquí utilizado se divide básicamente en tres fases: I . Un algoritmo de transformación reversible llamado block-sorting-lossless para generar redundancia, o sea, reordenar los símbolos para agruparlos en cadenas del mismo símbolo en el archivo. 2.- Un algoritmo llamado move-to-front que agrega más redundancia al archivo. 3.- Un tercer algoritmo que utiliza el algoritmo de compresión de Huffman. El orden en que se aplican los algoritmos para comprimir es el mismo anteriormente descrito y, como es de suponerse, existe la contraparte de cada fase del algoritmo, encargada de recuperar el archivo original y que su orden de aplicación, es el inverso de la compresión. A continuación, se describe la manera en cómo trabaja cada fase del algoritmo de compresión: La primera fase de transformación reversible lee un archivo en modo binario y lo guarda en una cadena “X’de tamaño N en donde cada elemento es un byte para ser procesado, a continuación, se crea una matriz de tamaño NxN en donde cada renglón es la rotación cíclica hacia la izquierda de un elemento de la cadena “X’, posteriormente se procede a ordenar los renglones de la matriz numéricamente en orden ascendente desde el renglón O hasta el renglón N-1 basándose en el primer elemento de cada renglón y siguiendo después con el segundo elemento y así sucesivamente hasta que la matriz quede totalmente ordenada, una vez hecho esto, se procede a buscar el renglón cuyos elementos coincidan exactamente con la. cadena original “X’ y el número de renglón en donde se encontró la coincidencia se marca como el índice “I”, finalmente el elemento que esta fase va a mandar a la siguiente es la cadena generada con la última columna de la matriz NxN más el índice “I”, como este programa está hecho en WIN32 (32bits), el índice “I” tiene un tamaño de 4 bytes que son añadidos al final de la cadena resukmte. El siguiente ejemplo usa una cadena ‘“X” de 16 símbolos ó elementos que son los siguientes: Matriz de NxN con lo corrimientos circulares a la izquierda: Una vez hechos los corrimientos se procede a ordenar los renglones empezando por el primer símbolo ó columna de cada renglón y así sucesivamente. A Hecho esto, se procede a obtener la nueva cadena ordenada tomhdola de la última columna de la matriz NxN final empezando por el renglón cero hasta el último renglón, por l o que nos queda: Puede notarse de la cadena obtenida que algunos símbolos que antes estaban diispersos ahora fueron agrupados en cadenas del mismo símbolo, como ocurre con el 42 ó con los ceros, de este modo se le aumenta redundancia a la cadena. Ahora, para poder recuperar la cadena original se necesita obtener un elemento llamado índice “I”, este es el número de renglón en el cual quedó numerado la cadena original en la matriz final, o sea en este caso el índice es “1l”, y es afladido al final de l a cadena ordenada para después poder ser recuperado y ejecutar el ordenamiento inverso. I Indiice “I” 1 rh-7 Prima elemento a buscar en el buffer auxiliar La segunda fase (move-to-front) utiliza un buffer auxiliar que contiene los símbolos utilizados en la cadena (0-255) ordenados inicialmente: 4 Elemento encontrado para empezar se obtiene el primer símbolo de la cadena “X’y ese elemento se busca en el buffer auxiliar y la posición en donde se encontró se guarda en una nueva cadena llamada “S” generada del mismo tamaño de “X’, el símbolo encontrado se pasa al tope del buffer recorriéndose los otros símbolos hacia abajo: I Elemento al tope de buffer y se recorren los símbolos I Se guarda la posición donde se encontró posteriormente se pasa ai siguiente elemento de la cadena “X’ y si éste resulta igual al símbolo anterior, lo que se hace es que se agrega un cero en la posición correspondiente en la nueva cadena “S” y el buffer no se altera, cuando el símbolo a buscar ha cambiado entonces se repite el procedimiento anteriormente descrito, se busca el símbolo en el buffer auxiliar y la posición donde se encontró es guardada en la nueva cadena “S”, el símbolo buscado se pone al principio del buffer y los demás símbolos se recorren hacia abajo, si el símbolo a buscar se repite se colocan un cero en la nueva cadena “S” y así se continúa hasta llegar al tin de la cadena “X’, al finalizar el elemento que se pisa a la etapa fmal es la cadena “S” (se le agrega al final un byte de protección, el 253 que es eliminado en la descompresión). h La tercera fase usa el algoritmo de compresión de Huffinan y la cadena “S”, primero guarda en el archivo resultante el conteo de bytes, o sea, el tamaño de la cadena “S”, después lee toda la cadena y cuenta el número de veces que se repiten cada uno de los símbolos presentes, guarda el conteo de símbolos existentes y crea un arreglo de frecuencias de cada símbolo que también guarda en el archivo resultante, posteriormente empieza a construir el árbol de Huffman usando el arreglo de frecuencias, va creando nodos padre en los que en los nodos hijos guarda los símbolos u otros nodos con menor o misma frecuencia y en el nodo padre resultante la suma de las frecuencias de los hijos, así continúa hasta haber cubierto todos los símbolos presentes en la cadena terminando en el nodo raíz del árbol, así pues los símbolos más cercanos a la raíz son los símbolos que más se repiten y en las hojas los símbolos que menos se repiten, hecho esto comienza la compresión en sí, lee el primer elemento de la cadena “S” y lo busca en el árbol de Huffman comenzando desde la raíz, al ir recorriendo las ramas se van creando nuevos bytes donde por cada nodo atravesado se agrega ya sea un bit 1ó un bit O al byte nuevo y cuando se juntan 8 bits, esos bits son escritos al archivo de salida como blytes, la compresión resulta por el hecho de que el elemento más frecuente en la cadena es representado por uno o dos bits, el segundo, por tres bits, etc., consiguiendo una reducción en el número de bits utilizados y por consiguiente, se reduce el tamaño del archivo. Ilustrando el ejemplo obtendríamos: Conteo de bytes (bytectr) = 21. Conteo de frecuencias o símbolos (freqctr) = 11 __- Símbolo O _1 ___.___ 2 3 ___16 29 42 Frecuencia Codificación 8 3 2 1 1 1 1 0010 001 1 0100 -_ 0101 L 7 El árbol quedaría: Si buscáramos el número 16 al recorrer el árbol quedaría la secuencia de bits O011 raíz 1 0 ; -$- - - - - [ 1 0 Si guardáramos la cadena “S” sin comprimir el espacio que ocuparía es: (21 bytes) x (8 bits) = 168 bits Si usamos el compresor el espacio ocupado sería: (2x8)+(3~3)+( 4~1)+(4~1)+(4x1)+(4~1)+(4x1)+(4x1)+(4x1)+(4x1)+(3x2) = 63 bits = 8 byes o sea lo comprime al 37.5 YOdel tamaño original. Y Ahora se describe cómo trabaja el algoritmo de descompresión: El descompresor aplica en forma y orden inverso las tres etapas usadas en la compresión. Primeramente el descompresor de Huffman, recupera el conteo de bytes, el conteo de frecuencias y el arreglo de frecuencias para reconstruir el árbol de Huffman, después empieza a leer bit por bit del archivo y recorre el árbol de Huffman, cuando llej,’a a un nodo hoja, el símbolo que contiene es recuperado y colocado en un arreglo nuevo llamado “S”, continúa así hasta que el archivo es descomprimido. El árbol reconstruido quedaría: raíz I con la secuencia de bits 1001 el número hallado sería el 255 o La segunda fase es la de Move-To-Front inversa, en la cual el funcionamiento es casi idéntico a su modo normal, tenemos el mismo buffer con los 256 símbolos ordenados, entonces tomamos el primer elemento de la cadena descomprimida y buscamos esa posición en el buffer, el número que se halle en esa posición es colocado en el tope del buffer y además en una nueva cadena llamada “L“ que va a ser el resultado de esta fase, posteriormente pasamos con el segundo elemento de la cadena descomprimida y si éste resulta ser un cero, se repite el mismo número que antes se había colocado en lai nueva cadena y así sucesivamente hasta que terminen los ceros, cuando el número cambia de cero, se repite el procedimiento anterior hasta acabar con la cadena descomprimida (el símbolo de protección 255 es eliminado en este paso). Por último para terminar el procedimiento y hacer la recuperación del archivo original aplicamos a la cadena el algoritmo block-sorting-lossless inverso, en esta fase recuperamos del fin de la cadena el índice I que fue añadido al momento de ordenar ya que posteriormente va a ser utilizado, se crean dos nuevas cadenas, una llamada “i” que va a estar numerada desde el O hasta N-I tamaño de la cadena “L” y otra llamada “S” que va contener ya la cadena original, -. I ~ . . . .. I Posición . .... -en .. ..“L‘ - .una vez hecho esto mediante el algoritmo de ordenamiento “qsort” ordenamos 1aL cadena “i” en base a “L”y el resultado del ordenamiento lo guardamos en “i“, 1 enbasea“L” in finalmente para generar la cadena original tomamos el índice “I” recuperado anteriormente y buscamos esa posición en la cadena “L” ordenada y el número contenido en esa posición va a ser el primer elemento de la cadena “S”, después buscamos en la cadena ‘‘2’ esa misma posición y el número que contiene se convierte en el nuevo índice y pasamos a buscar esa posición en “L” y el número que contiene es el siguiente elemento de “S” y buscamos esa misma posición en “i” y el número que contiene se convierte en el nuevo índice y así continuamos sucesivamente hasta recuperar toda la cadena original. lndice .c En este ejemplo el índice es 11, y el elemento colocado en la posición de la cadena “L” ordenada es 42 que se convierte en el primer elemento de “S”, después el símbolo en esa misma posición pero en la cadena “i” ordenada es 2, este pasa a convertirse en el nuevo índice, vemos que en la posición 2 de “L” hay un cero, éste se convierte en el segundo símbolo de “S”, y en esa misma posición pero en “i” está el 6, éste es el nuevo índice, repitiendo el método hasta llegar nuevamente al índice inicial 11nos queda la cadena final “S” que comparándola con la cadena original “X’ vemos que es la misma. 11 RESULTADOS. Aplicando el programa obtenido a dos archivos muestra de ECG ambulatorio y comparando los resultados con otros dos compresores, uno es el algoritmo de Huffman puro sin el preprocesamiento y el otro es el programa WINZIP que usa el algoriitmo de Lempel y Ziv (Este algoritmo codifica los datos en una pasada y permite compresión más grande que la codificación de Huffman con menos cómputos, es un algoritmo de longitud variable y fija. Analiza secuencialmente la salida de una fuente discreia hacia frases de longitud variable, divide la cadena fuente en frases que no habían aparecido antes (excepto por la última). Las frases son almacenadas en un diccionario como son construidas, las localidades en el diccionario son numeradas consecutivamente. La palabra de código para cada Frase es entonces construida al listar las localidades del diccionario (en binario) que igualan la nueva frase en casi todo a la última localidad (prefijo); después del análisis secuencial, cada prefijo de cada frase aparece más próximo en el análisis. El valor del último bit es entonces agregado al prefijo). Se obtuvieron los siguientes resultados: Para el archivo ECG1.BIN 154029 112105 p?Gy2ZSOOUT 225000__ WinZip- . . __ Para el archivo ECG2.BIN - Programa Utilizado BSL . Huffman Puro WinZip . . ~ a m a i i oInicial- 225000 225000 225000 Tamaño Final . . . . . . 85069 148966 108197 48 17 //UNIVERSIDAD AUTONOMA METROPOLITANA //PROYECTO TERMINAL I1 //PROYECTO: COMPRESION MEDIANTE ORDENAMIENTO REVERSIBLE //PROFESOR: OSCAR YAñIEZ SUAREZ //ALUMNO: ZAHID GABRIEL LIMON GOMEZ #include #include #include #include #include #include #include <io.h> <dos.h> <conio.h> <stdio.h> <stdlib.h> <string.h> "htree.h" cOMp32.c * * * * * * * * * * * * * * * / compresor 32 bits* * * * + * * * * i t * * / /It************ /*******ti* . A static void compress(F1LE *fo, struct htree *h, struct htree "child); static void outbit(F1tE *fo, int bit); unsigned char *L, *x; long length; void Carga-buffer(FiLE *fi,long *i) long k; fseek ( fi,OL,O) ; fread(x, 1, length, fi)r / / carga el archivo en el buffer fclose(fi1; for (k=O; k<length; k++) * (itk)=k; / / numera el arreglo de posiciones progresivamente x '.1\ MOVE TO FRONT N O W void MTF(unsigned char *p, unsigned char * S I /t***t**itC***l******~****~~* *******t***i***t*t***t*/ ( long k, r; unsigned char w=O, pos; printf("Rea1izando MOVE TO FRONT normal... \t"); for (r=O;r<256;r++) ( ,,'- . //inicializo arreglo de simbolos de 256 simbolos * (p+r)=w; W++; f for(k=O;k<lengtht4;k++) ( //recorre el arreglo L for(wx0; * (Ltk)!E* (p+wl; w++) //busca elemento en arreglo de simbolos 11 .* pos=w; .- while (w!=O) ( //guardo la posicion donde se encontro * (p+w)=*(p+w-1); //recorro hacia abajo los simbolos W--; 1 *p=* (Ltk); //pongo el simbolo a la cabeza del arreglo * (stk)-pos; //pongo la posicion en el arreglo resultante s while(* (Ltk)==*(Ltktl)) //verifico si el elemento se repite t //consecutivamente, si es asi, coloco ceros en el k++; //arreglo resultante (s+k)=O; 1 1 1 printf ("Ok!\n"); RUTINA DE COMp-CION /****t*t***** t DE QSORT DE BSL ....................... int comparar( const void *argl, const void *arg2) long nl,n2,n3,k; int sal=O; nl=length-*(long *)argl; n2=length-* (long *)arg2; if (nl<n2) ( n3=nZ-nl; I . - ., . 66 for(k=O; k<nl ( c.. . 1 sal==O ;k++) sal= *(x+(*(long *)argl+k)) - *(x+(*(long *)arg2+k)); 1 for(k=O; k<n3 *- &6 sal==O; k+t) sal= *(x+k) ' l 1 - *(x+(*(long *)arg2)+nl+k); if (sal==O) ( ,^ for(k=O; k<*(iong *)arg2 t * .I 1 1 1 sal= / / (nl>n2) ( n3=nl-n2; forlk=O; k<n2 h6 sal==O; k+t) * (xtn3ck) - * (xtk); ) else 66 sal==O; k+t) ..* I -- mi . . a - Sal= *(,+(*(long 1 if (sal==O) *)argl+k)) - *(x+(*(long *)argZ+k)); { for(k=O; k<n3 ( 1 && sal==O; k++) sal= * (x+( * (long * ) argll+nZ+k) - * (x+k); if (sal==O) for(k=O; k<*(long *)argl sal= *(x+k) 1 - && sal==O; k++) +(x+n3+k); ) 1 1 return(sa1); 1 BLOCK-SORTING LOSSLESS NORMAL /******t**t***tCf******* **ti***********+**/ void BSL(1ong *i) f long y, pos, k; long *I; //elemento a buscar para encontrar el indice pos = length-1; printf ("Quicksort.. \t\t")i //ordeno i en base a x /* ./ . .................................................................. qsort ( (long * ) i, (size-t)length, sizeof (*i), comparar); e- / * .................................................................. ,c I r)_ printf ("Ok!\n"); printf ("Calculando L.. . \t"); I=(L+length); *1=0; //asigno a I la direccion donde se va a copiar for (k = O; k < length ; k++) //carga el buffer L ordenado I y=(* (i+k)+length-1)%length; * (L+k)=*(x+(yclength)%length); if (y==pos) //busca el indice I [ *I=k; 1 //guarda el indice al final de L I printf ("Ok!\n"); MAIN /*t***********i*tt*********t***l*f *t**********t******tt********i***/ static char out8; static int ct8; c ./ void main(int argc, char *argv[l) { FILE *fi; //apuntador a archivo FILE *fo; *- ,l ,- ,- int comparar(const void *argl, const void *arg2); unsigned char *p, * s i long *i; long k; int c; BYTECOUNTER bytectr a int freqctr = O; O; I". if (argc < 3 ) ( 1 printf("\nusar: comp32 infile outfile"); exit (1); if ((fi = fopen(argv[l], "rb")) == NULL) ( .-. I printf ("\no se puede abrir % s " , argvill) ; exit (1); fseek(fi, OL, SEEK-END); length = ftell(fi1 ; printf ("El tamaflo del archivo %s es %u bytes\n",argv[ll ,length); if ((fo = fopen(argv[2], "wb")) == NULL) ( 1 printf ("\no se puede abrir %s", argv[21) ; fcloseífi); exit ( 1 ); //buffer de datos x=(unsigned char *)malloc(length*sizeof(unsigned char)); L=(unsigned char *)malloc((lengtht4)*sizeof(unsignedchar)); //buffer final, indice I al final s=(unsigned char *)malloc((lengtht5)*sizeof(unsigned char)) ; p=(unsigned char *)malloc(256*sizeof(unsignedchar)); //buffer de MTF //buffer de posiciones i=(long *)malloc(length*sizeof(longi); '+. I- C " I .*. printf("Leyend0 archivo. ..\t"); //carga el buffer con el archivo Cargabuffer(fi,i); printf ("Ok!\n"); / / RUTINA BLOCK-SORTING LOSSLESS NORMAL BSt(i); MTF(p,s) ; / / RUTINA MOVE TO FRONT * (s+(length+4))=255; /*---------------------- COMPRESOR DE WF- ------------------------*/ /*--- lee el archivo de.entrada y cuenta la frecuencia de caracteres ---*/ printf ("Comprimiendo...\t"); while( bytectr < (length+5) I c c = * (s+bytectr); &= 255; ) / / lee un caracter del buffer / / busca si el simbolo ya esta incluido if (ht[cl.cnt == O) 4 / / contador de los simbolos existentes / / guarda el nuevo simbolo freqctr++; ht[c] .ch = c; 1 ht [c].cnt++; bytectr++; / / incrementa la frecuencia del simbolo / / contador de bytes leidos /* _____-escribe la cuenta de bytes al archivo de salida --------*/ fwrite (Sbytectr, sizeof bytectr, 1, fo); /*----- escribe el conteo de frecuencia al archivo se salida -----*/ fwrite(&freqctr, sizeof freqctr, .._ fo); escribe el arreglo de frecuencias al archivo de salida ----*/ /*---- ".. I, for (c = O; c < 256; c++) ( . Y if (ht[cl .cnt > O ) t I ^ . r . , 1 1 fwrite(&ht[cl .ch, sizeof (char), 1, fo); fwrite(&ht[cl .cnt, sizeof (BYTECOUNTER), 1, fo); -7, /*---------------- construye el arbol de huffman ----------------- * / c 1 buildtree ( ) ; comprime el archivo /*--------------------k=O; while ( k < (length+5) ) c = *(s+k); compress(fo, ht + (c k++ i & _____-_____-____-_--__ */ 255), NULL); I if (ct8 < 8) out6 = (out8 << (8-ct6)); //termina de recorrer los bits a 1 //la izquierda fputc(out8, f0); .-.e printf("\nEl tamaflo del archivo % s es %u bytes\n",argv[2l,ftell(fo)); printf ("Comprimido al %u %\n', (ftell(fo)*lOO)/length); fclose (fi); fclose(fo); /t----------------------------------------------------------------*/ 1 c .,-, //termina MAIN ( ) ; comprime un valor de caracter a una cadena de bits ------*/ /*-----static void compress(F1LE 'fo, struct htree *h, struct htree *child) ( if (h->parent != NüLL) compress (fo, h->parent, h) ; .- , I- if (child) t if (child == h->right) outbit (fo, O ) ; 1 1 else if (child == h->left) outbit (fo, 1) ; reune y escribe bits al archivo comprimido de salida -----*/ static void outbit(F1LE ‘fo, int bit) /c----- t if (ct8 == 8 ) t fputc (OUt8, f0); Ct8 = o; 1 out8 = (out8 << 1) ct8++; ””\ I - ... ^ I bit; //UNIVERSIDAD AUTONOMA METROPOLITANA //PROYECTO TERMINAL I1 //PROYECTO: COMPRESION MEDIANTE ORDENAMIENTO REVERSIBLE //PROFESOR: OSCAR YAí?EZ SUAñEZ //ALUMNO: ZAHID GABRIEL LIMON GOMEZ , ,- #include #include #include #include #include #include #include <io.h> <dos.h> <conio.h> <stdio.h> <stdlib.h> <string.h> "htree.h" /*fi****iiif***** ~cmp32.c* * * * * * * f t * l * * * t * * * / /*******+* DESCOMPRESOR 32 bits *********/ L ". unsigned char *L; long length; static int decompress(F1LE *fi, struct htree *root); MOVE TO FRONT INVERSA void MTFI(unsigned char *p,unsigned char * s ) /t*****iCtt*ltt*t**t***fti**tf < I . ( ******i*tt****+t**f**/ unsigned char w=O; long k; printf("Realizand0 MOVE TO FRONT inversa... \t"); for (k=O;k<256;k++) //inicializo arreglo de simbolos de 256 simbolos ( 1 + (P+W) 'w; w++ ; for(k=O;k<length+4;k++) //recorre el arreglo s para generar los valores w=* (stk); *(Ltk)=*(p+w); a- //obtengo el primer elemento de posicion //leo el simbolo y lo coloco en L while(w!=O) ,.- * (p+w)=* (p+w-l); //recorro hacia abajo los simbolos w--; ) *p=* (Ltk); //pongo el simbolo a la cabeza del arreglo c while(*(s+k+l)==O) //verifico si se repiten ceros consecutivamente i / / si es asi, coloco el mismo numero (L+k+l)=* (Ltk); //en el arreglo resultante k++; I I- ,-. /I- ,e. 1 1 printf ("Ok!\n"); / * * * * * * * * * * * * + RUTINA DE COMPARACION DE QSORT DE BSLI ******************/ I .- int compara2( const void *argl, const void *arg2) int sal; sal= *(L+(*(long *)argl)) - +(L+(*(long *)arg2)); if (sal==O) ( 1 if( (*(long *)argl) I sal=ll; 1 else { sal=-ll; 1 > (*(long *)arg2) ) return(sa1) ; 1 I BLOCK-SORTING LOSSLESS INVERSA /***t*****il*if***t *. I ***tft**i********+**t/ void BSLI (unsigned char *s,long *i) long pos,k; long *I; //numera los bytes en i for (k=O; k<length; k++) * (i+k)=k; printf("Reca1culando i...\t"); //ordena sobre el arreglo i //en base al arreglo L /* ..................................................................... qsort((1ong /* *) ~/ i, (size-tllength, sizeof(*i), compara2); ..................................................................... ~/ printf ("Ok!\n"); I=L+length; pos=*1; r- //apunto al final del arreglo para recuperar el indice I //recupero el indice printf ("CalculandoS.. I.". . \t"); for (k=O; k<length; k++) I pos=* (i+pos); * (s+k)=*(L+pos); 1 printf ("Ok!\n"): ,A- ,- ... ) //recupero la cadena original en s //empezando por el indice MAIN /~**t*********C*t***ft*********ii* *t**tt*fl**t******f**t****ft********/ void main(int argc, char *argv[l) I FILE *fi, *fo; unsigned char c: BYTECOUNTER bytectr; int freqctr; unsigned char *p, * s i long *i; long k=O; if (argc < 3) { .4, 1 printf("\nusar: dcomp32 infile outfile"); exit(1); if ((fi = fopen(argv[ll, "rb")) == NULL) ( 1 printf ("\no se puede abrir %s", argv[ll); exit (1); if ((fo = fopen(argv[21, "wb")) ( 1 == NULL) printf ("\no se puede abrir Bs", argvC21); fclose(fi) ; exit (1); /c------------------- DESCWPF(ESOR DE W F W ... printf("Descomprirniendo /*------------------- .................... */ \t"); lee el conteo de bytes ------------------ * / fread(&bytectr, sizeof bytectr, 1, fi); fread(&freqctr, sizeof freqctr, 1, fi); length=bytectr-5; P". c L=(unsigned char *)rnalloc((lengtht4)*sizeof(unsignedchar)); //buffer final, indice I al final s=(unsigned char *)malloc((length+5)*sizeof(unsignedchar)); //buffer de MTF p=(unsigned char *)malloc(256*sizeof(unsignedchar)); //buffer de posiciones i=(long ')malloc(length*sizeof(long)); /*----------------- lee el conteo de frecuencias --------------*/ I- * ". .I while (freqctr--) t fread(&c, sizeof (char), 1, fi); ht[cl .ch = c; fread(&ht icl .cnt, sizeof (BYTECOUNTER), 1, fi); /*---------------buildtree ( ) ; -------------- * / ------------------ * / construye el arbol de huffman /*------------------- descomprime el archivo while (bytectr--) i c=decompress (fi, root); * (s+k)=c; ktt; 1 printf ("\nReorganizando... \t\n"); / / RUTINA MOVE TO FRONT INVERSA / / RUTINA BLOCK-SORTING LOSSLESS INVERSA MTFI (ptS ) ; BSLI ! s , i); fwrite(s, 1, length, fo); r. I *. ..* printf ("\nArchivo descomprimido. \t\n"); fclose (fo); fclose(fi1; 1 //termina MAINO; static int in8; static int ct8 = 8; /*------------ lee un bit a la vez desde el archivo -----------*/ static int inbit(F1LE *fi) ( int obit; if (ct8 == 8) ! in8 = fgetc(fi1; ct8 = O; " .* 1 1 obit = in8 h in8 <<= 1; ct8tt; return obit; /*----- 0x80; descomprime los bits de archivo hacia caracteres ------*/ I^ .". static int decompress(F1LE *fi, struct htree *h) ( while (h->right != NULL) *_ 9 4 4 if !inbit (fi)) h = h->left; else h = h->right; return h->ch; - #include <stdio.h> #include <stdlib.h> #include "htree.h" struct htree htI5121; struct htree *root; /*-- construye un arbol de Huffman desde un arreglo de frecuencia --*/ void buildtree (void) t int treect int i; = 256; /*---- construye el arbol de Huffman -----*/ while (1) t struct htree *hl = NULL, *h2 = NULL; encuentra l a s dos frecuencias mas pequenas ----*/ for(i=O; i < treect; i++) /I---- if (ht+i != hl) t if (ht[il.cnt > O ( 6& ht[il .parent == NULL) if (hl == NULL I I ht[il .cnt < hl-xnt) t if (h2 == NULL I1 hl->cnt < hZ->cnt) h2 =hl; hl = ht+i; 1 1 1 t else if (h2 == NULL I I ht[il .cnt < hZ->cnt) h2 = htti; 1 if (h2 == NULL) root =hl; break; 1 /*---------combina dos nodos y lo agrega ------------*/ hl->parent = ht+treect; h2->parent = ht+treect; ht[treectl.cnt = hl->cnt t h2->cnt; ht[treectl.right = hl; ht [treect].left = h2; treectt+; 1 ) . ., *% I I . .. .-. /*-----------_------------typedef long BYTECOUNTER; 1; *x, void buildtree(void): r- ."* e- ..-. c ,- I* __________________________ */ /*-------------------Huffman tree structure ----------------*/ struct htree ( unsigned char ch: //valor del caracter BYTECOUNTER cnt; //frecuencia del caracter struct htree *parent; //apuntador a nodo padre struct htree +right; //apuntador a nodo hijo derecho struct htree *left; //apuntador a nodo hijo izquierdo extern struct htree ht[l; extern struct htree *rooti a.. htree h CONCLUSIONES Revisando los resultados obtenidos puede verse que para los dos archivos muestra el algoritmo que mejor comprimió fue el del proyecto terminal (BSL) ya que tuvo ai menos un 10% de mayor compresión que el programa que le siguió, el de WINZIP, y al final el algoritmo de Huffman puro, esto se debe a que en el BSL se le agregaron dos etapas de preprocesamiento, destinadas a aumentar la redundancia que ya de por sí tienen estos tipos de archivos, la velocidad de compresión fue similar a la de los otros programas, únicamente se noto una disminución de la velocidad con el compresor BSL dado que el programa simula la generación de la matriz NxN y su posterior ordenamiento usando el algoritmo quicksort que tiene un desempeño O(n log n), pero en la descompresión aunque se vueleve a utilizar el quicksort no se ve esa disminución de la velocidad, esto podría mejorarse usando otro algoritmo que trabaje mejor que quicksort pero aúin así el resultado fue como el que se esperaba, mayor factor de compresión que otros programas a una velocidad razonable, por l o que creo que el objetivo de este proyecto fue alcanzado satisfactoriamente. REFERENCLAS Dr. Dobb‘s Journal, February 199l . Burrows, M.and Wheeler, D.J. (1994) “A Block-sorting Lossless Data Compression Algorithm”, Digital Systems Research Center Research Report 124. ALUMNO ASESOR