Algoritmos de compresión para secuencias biológicas y su aplicación en árboles logénicos construidos a partir de ADN mitocondrial. Pablo Urcola Irache

Proyecto Fin de Carrera de Ingeniería en Informática Algoritmos de compresión para secuencias biológicas y su aplicación en árboles logénicos constr

0 downloads 72 Views 3MB Size

Recommend Stories


Evolución humana y el ADN mitocondrial
Evolución humana y el ADN mitocondrial Hernán Amat Olazábal Universidad Nacional Mayor de San Marcos [email protected] RESUMEN En el presente ensayo

Enfermedades genéticas del ADN mitocondrial humano
Enfermedades del mtDNA A RTÍCULO DE REVISIÓN Enfermedades genéticas del ADN mitocondrial humano Abelardo Solano, Q. F. B., (1) Ana Playán, Ph.D.,(1

Determinación de la estructra de secuencias de ADN del tipo A(AT) n T Pág. 1
Determinación de la estructra de secuencias de ADN del tipo A(AT)nT Pág. 1 RESUMEN. El ADN es una molécula de importancia vital en los organismos vi

PURIFICACIÓN DE ADN GENÓMICO A PARTIR DE HOJAS, RAÍCES Y NEUMATÓFOROS DE Mauritia flexuosa aguaje
Recibido el 16-07-2013 Aprobado el 24-07-2013 209 PURIFICACIÓN DE ADN GENÓMICO A PARTIR DE HOJAS, RAÍCES Y NEUMATÓFOROS DE Mauritia flexuosa “aguaje

Comparación de tres métodos de extracción de ADN a partir de restos óseos
Rev. Biol. Trop. 52 (3): 717-725, 2004 www.ucr.ac.cr www.ots.ac.cr www.ots.duke.edu ARTÍCULO BREVE Comparación de tres métodos de extracción de ADN

Story Transcript

Proyecto Fin de Carrera de Ingeniería en Informática

Algoritmos de compresión para secuencias biológicas y su aplicación en árboles logénicos construidos a partir de ADN mitocondrial

Pablo Urcola Irache

Directora: Elvira Mayordomo Cámara

Departamento de Informática e Ingeniería de Sistemas Centro Politécnico Superior

Noviembre 2006

Comprimir es comprender Jorge Wagensberg

Agradezco a todo el mundo que me ha ayudado o me ha ofrecido su ayuda en algún momento de la realización de este proyecto su interés y disposición. Como último favor les pido que me dejen dedicar este trabajo a mi abuelo.

Índice general I Memoria

9

1. Introducción

11

1.1.

Objetivos

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

11

1.2.

Alcance

1.3.

Trabajo previo

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

11 12

1.4.

Contexto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

12

1.5.

Métodos, técnicas y herramientas

. . . . . . . . . . . . . . . . . . .

13

1.6.

Contenido de la memoria . . . . . . . . . . . . . . . . . . . . . . . .

13

2. Conceptos previos 2.1.

2.2.

Secuencias biológicas

15 . . . . . . . . . . . . . . . . . . . . . . . . . .

15

2.1.1.

Formación de proteínas . . . . . . . . . . . . . . . . . . . . .

16

2.1.2.

Estructura del ADN

. . . . . . . . . . . . . . . . . . . . . .

16

Árboles de logenia . . . . . . . . . . . . . . . . . . . . . . . . . . .

17

3. Desarrollo del estudio 3.1.

Adaptación del algoritmo de Lempel-Ziv 3.1.1.

3.2.

19 . . . . . . . . . . . . . . .

19

Resultados . . . . . . . . . . . . . . . . . . . . . . . . . . . .

20

Análisis de los algoritmos de compresión especícos

. . . . . . . . .

20

3.2.1.

GenCompress . . . . . . . . . . . . . . . . . . . . . . . . . .

20

3.2.2.

Pattern Hunter

. . . . . . . . . . . . . . . . . . . . . . . . .

21

3.2.3.

Pattern Hunter II . . . . . . . . . . . . . . . . . . . . . . . .

22

3.2.4.

dnaX . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

22

4. Implementación del compresor

25

4.1.

Especicación del algoritmo

. . . . . . . . . . . . . . . . . . . . . .

25

4.2.

Directivas de diseño . . . . . . . . . . . . . . . . . . . . . . . . . . .

25

4.2.1.

Sobre los datos

25

4.2.2.

Sobre el uso del algoritmo

4.3.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

26

Estructura del algoritmo . . . . . . . . . . . . . . . . . . . . . . . .

26

4.3.1.

27

Análisis de la entrada . . . . . . . . . . . . . . . . . . . . . .

1

ÍNDICE GENERAL

2

4.3.2.

Selección de las repeticiones

4.3.3.

Codicación de la salida

. . . . . . . . . . . . . . . . . .

28

. . . . . . . . . . . . . . . . . . . .

29

. . . . . . . . . . . . . . . . . . . . . . .

31

. . . . . . . . . . . . . . . . . . . . . . . . . . . . .

31

4.4.

Consideraciones generales

4.5.

Interfaz gráca

5. Árboles logénicos

35

5.1.

Cálculo de la distancia entre dos secuencias . . . . . . . . . . . . . .

35

5.2.

Características del compresor

. . . . . . . . . . . . . . . . . . . . .

36

5.3.

Métodos de cálculo

. . . . . . . . . . . . . . . . . . . . . . . . . . .

36

5.4.

Herramientas utilizadas . . . . . . . . . . . . . . . . . . . . . . . . .

37

6. Resultados 6.1.

6.2.

39

Resultados del compresor . . . . . . . . . . . . . . . . . . . . . . . .

39

6.1.1.

Banco de pruebas . . . . . . . . . . . . . . . . . . . . . . . .

39

6.1.2.

Análisis de los resultados . . . . . . . . . . . . . . . . . . . .

40

Resultados de los árboles de logenia

. . . . . . . . . . . . . . . . .

43

. . . . . . . . . . . . . . . . . . .

44

. . . . . . . . . . . . . . . . . . . . . . .

44

6.2.3.

Algoritmos y métodos utilizados . . . . . . . . . . . . . . . .

45

6.2.4.

Resultados obtenidos . . . . . . . . . . . . . . . . . . . . . .

45

6.2.1.

Corrección de las pruebas

6.2.2.

Ficheros de prueba

7. Conclusiones

49

7.1.

Conclusiones sobre el compresor . . . . . . . . . . . . . . . . . . . .

49

7.2.

Conclusiones sobre los árboles de logenia

. . . . . . . . . . . . . .

50

7.3.

Conclusiones personales

. . . . . . . . . . . . . . . . . . . . . . . .

51

Bibliografía

53

II Apéndices

55

A. Código fuente de la implementación

57

A.1. Notas de la distribución

. . . . . . . . . . . . . . . . . . . . . . . .

57

. . . . . . . . . . . . . . . . . . . . . . . . . . . .

58

A.3. ADN . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

59

A.4. Entrada-Salida de Bits (Cabecera) . . . . . . . . . . . . . . . . . . .

60

A.5. Entrada-Salida de Bits

63

A.2. ADN (Cabecera)

. . . . . . . . . . . . . . . . . . . . . . . . .

A.6. Árboles Rojinegros (Cabecera) . . . . . . . . . . . . . . . . . . . . .

67

A.7. Árboles Rojinegros

68

. . . . . . . . . . . . . . . . . . . . . . . . . . .

A.8. Cola de prioridades (Cabecera)

. . . . . . . . . . . . . . . . . . . .

71

A.9. Cola de prioridades . . . . . . . . . . . . . . . . . . . . . . . . . . .

72

A.10.Árbol de rangos (Cabecera)

73

. . . . . . . . . . . . . . . . . . . . . .

ÍNDICE GENERAL

3

A.11.Árbol de rangos . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

74

A.12.Referencias (Cabecera) . . . . . . . . . . . . . . . . . . . . . . . . .

77

A.13.Referencias

79

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

A.14.Autómata (Cabecera) . . . . . . . . . . . . . . . . . . . . . . . . . .

80

A.15.Autómata

82

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

A.16.Codicador (Cabecera) . . . . . . . . . . . . . . . . . . . . . . . . .

84

A.17.Codicador

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

85

A.18.Decodicador (Cabecera) . . . . . . . . . . . . . . . . . . . . . . . .

88

A.19.Decodicador

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

A.20.Compresor (Cabecera)

. . . . . . . . . . . . . . . . . . . . . . . . .

89 90

A.21.Compresor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

92

A.22.Descompresor (Cabecera)

. . . . . . . . . . . . . . . . . . . . . . .

96

A.23.Descompresor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

96

A.24.Excepciones (Cabecera)

. . . . . . . . . . . . . . . . . . . . . . . .

98

A.25.Excepciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

98

A.26.Ventana de estadísticas (Cabecera)

. . . . . . . . . . . . . . . . . .

99

A.27.Ventana de estadísticas . . . . . . . . . . . . . . . . . . . . . . . . .

99

A.28.Ventana principal (Cabecera)

. . . . . . . . . . . . . . . . . . . . . 100

A.29.Ventana principal . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102 A.30.Programa principal . . . . . . . . . . . . . . . . . . . . . . . . . . . 109 A.31.Makele

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110

4

ÍNDICE GENERAL

Índice de guras 2.1.

Procesos de transcripción y traducción

. . . . . . . . . . . . . . . .

16

2.2.

Ejemplo de árbol de logenia de cuatro individuos . . . . . . . . . .

17

4.1.

Fases del algoritmo . . . . . . . . . . . . . . . . . . . . . . . . . . .

27

4.2.

Ventana principal . . . . . . . . . . . . . . . . . . . . . . . . . . . .

32

4.3.

Ventanas de estadísticas

. . . . . . . . . . . . . . . . . . . . . . . .

32

6.1.

Árbol generado por el algoritmo RLE . . . . . . . . . . . . . . . . .

46

6.2.

Árbol generado por el algoritmo gzip

47

6.3.

Árbol generado por el compresor de cheros de ADN

5

. . . . . . . . . . . . . . . . . . . . . . . . .

48

6

ÍNDICE DE FIGURAS

Índice de cuadros 2.1.

Moléculas y monómeros

. . . . . . . . . . . . . . . . . . . . . . . .

15

3.1.

Ejemplo de construcción de cadena binaria . . . . . . . . . . . . . .

21

4.1.

Codicación de Bit de Continuidad

. . . . . . . . . . . . . . . . . .

30

6.1.

Banco de pruebas ajeno

. . . . . . . . . . . . . . . . . . . . . . . .

40

6.2.

Banco de pruebas propio . . . . . . . . . . . . . . . . . . . . . . . .

40

6.3.

Prueba t de Student de igualdad de medias 1 . . . . . . . . . . . . .

42

6.4.

Prueba t de Student de igualdad de medias 2 . . . . . . . . . . . . .

42

6.5.

Prueba de igualdad de varianzas . . . . . . . . . . . . . . . . . . . .

43

6.6.

Prueba t de Student de igualdad de muestras

43

7

. . . . . . . . . . . .

8

ÍNDICE DE CUADROS

Parte I Memoria

9

1 Introducción En esta sección se describen los elementos fundamentales en que se enmarca el proyecto como pueden ser el objetivo, el alcance, el trabajo previo en que se apoya, el contexto en que se desarrolla o las herramientas que se han utilizado para su realización. Además se indica de forma abreviada el contenido y distribución del resto de las secciones de la memoria.

1.1. Objetivos Los objetivos del proyecto, tal y como se indicaron en la propuesta, son los siguientes: Estudio de los algoritmos de compresión de secuencias biológicas utilizados en la actualidad, su comparación con los de propósito general y la obtención de unas pautas de diseño que se deben tener en cuenta al crearlos. Estudio de la aplicación de los algoritmos de compresión a la generación de árboles logénicos utilizando la información contenida en el ADN mitocondrial y análisis de las características necesarias de los primeros así como los distintos métodos de construcción de los árboles.

1.2. Alcance Denidos estos objetivos, la intención del proyecto no es superar los niveles de compresión o velocidad de otros algoritmos sino caracterizar de la mejor forma los fundamentos que un compresor de secuencias de adn debiera tener. Finalmente esta caracterización se ha implementado para probar en qué forma mejora los algoritmos existentes.

11

1. INTRODUCCIÓN

12

En relación a los árboles logénicos, éstos no se pueden comparar con otros puesto que hay multitud de versiones válidas y respaldadas cada una de ellas por alguna teoría, así que se considera que se ha alcanzado un resultado bueno al encontrar un método que se aproxima a las ideas más aceptadas en la actualidad por la mayoría. Para la contrucción de los árboles se utiliza la información genética del ADN mitocondrial puesto que debido a su pequeño tamaño se hace más manejable sin perder capacidad de diferenciación entre los distintos individuos.

1.3. Trabajo previo La documentación preexistente tanto en el campo de los algoritmos de compresión como en los árboles de logenia es muy extensa y diversa, aunque, debido a que en general son bastante recientes, la validez de sus contenidos no está muy contrastada, por lo que hay que agudizar el sentido crítico frente a ellos. Existe una gran variedad de algoritmos de compresión genéricos ([13, 12, 20]) y algunos dedicados a secuencias biológicas descritos en [4], [5] y [10], generalmente integrados en las grandes bases de datos de genomas que existen como NCBI

1

o

2

GenBank . Ambos tipos sirven como base para el proyecto puesto que se pretende saber por qué los primeros no funcionan con secuencia biológicas y qué características hacen a los segundos ecaces a la hora de comprimir este tipo de datos. En cuanto a los árboles de logenia, la variedad de métodos está relacionada directamente con la diversidad de hipótesis sobre la evolución de las especies, como se explica en [6], y con los distintos métodos numéricos de cálculo de distancias, descritos en [16].

1.4. Contexto El desarrollo de este proyecto se enmarca dentro del grupo de investigación de complejidad computacional (GCC) que forma parte del Grupo de Ingeniería de Sistemas de Eventos Discretos (GISED) del Departamento de Informática e Ingeniería de Sistemas (DIIS). Este grupo investiga sobre el funcionamiento de los distintos algoritmos de compresión de uso general y quería averiguar por qué no eran útiles al comprimir secuencias biológicas. Debido a que se desarrolla en el seno de un grupo de investigación, el proyecto tiene una gran carga de trabajo de recopilación de información y un apartado más reducido de los elementos típicos de un proyecto de software.

1 http://www.ncbi.nlm.nih.gov/

2 http://www.psc.edu/general/software/packages/genbank/genbank.html

1.5. MÉTODOS, TÉCNICAS Y HERRAMIENTAS

13

Este trabajo ha sido parcialmente nanciado por el proyecto de investigación del Ministerio de Educación y Ciencia TIN2005-08832-C03-02, que cuenta con el apoyo de la empresa deCODE genetics.

1.5. Métodos, técnicas y herramientas Gran parte del tiempo de trabajo del proyecto se invirtió en documentación, fundamentalmente lectura de artículos y consulta con otros investigadores que trabajan en el mismo tema. La disposición de los artículos y de algunos algoritmos fue dicultosa en algunos casos debido a las licencias restrictivas, lo que redujo la capacidad de investigación. Una vez conseguida toda la información al alcance, se extrajeron las ideas para la implementación del software desarrollado para posteriormente vericarlo y compararlo con los programas ya existentes. Por último se diseñó un interfaz amigable que facilitara la utilización y la extracción de los datos de la ejecución para su posterior análisis. Además se pudo incluir uno de los algoritmos ya existentes [10] en el mismo interfaz gráco gracias a que se facilitaron los cheros fuentes para su utilización, lo que simplica la labor de comparación de ambos algoritmos. El trabajo con los árboles de logenia no se decantaba tanto por la implementación sino por la realización de pruebas y el análisis de los resultados. Por eso 3

se optó por el uso de un paquete de programas

que alcanzara todas las nece-

sidades del proyecto, facilitando el trabajo con los árboles sin necesidad de una implementación propia de los métodos.

1.6. Contenido de la memoria En una primera sección, se denen los conceptos relativos a secuencias biológicas y árboles de logenia necesarios para la comprensión del trabajo realizado en el proyecto. La sección 3 aborda el proceso de documentación seguido hasta alcanzar el diseño nal del compresor, indicando todos los pasos recorridos y las ideas adquiridas en cada uno de ellos. Posteriormente se profundiza en la mejora implementada utilizando los conocimientos adquiridos en la fase de estudio. La sección 5 está dedicada al trabajo realizado sobre los árboles de logenia. Las dos últimas secciones contienen respectivamente información sobre las pruebas realizadas, los resultados que se obtuvieron y, por último, las conclusiones que se han podido extraer de todo el trabajo realizado.

3 http://evolution.genetics.washington.edu/phylip.html

14

1. INTRODUCCIÓN

2 Conceptos previos En esta sección se explican los conceptos necesarios para entender algunos elementos del proyecto que se alejan de la informática, fundamentalmente relacionados con la genética y con la teoría de la evolución.

2.1. Secuencias biológicas Llamamos secuencia biológica a la representación de cualquiera de la sucesiones de monómeros que intervienen en la síntesis de proteínas a partir de la información genética de los organismos. Se pueden diferenciar tres tipos de secuencias distintas: Ácido desoxirribonucleico o ADN Ácido ribonucleico o ARN Proteínas Cada uno de los elementos básicos que componen una de estas moléculas es un monómero, siendo de distinta naturaleza en cada una de ellas como se aprecia en el cuadro 2.1.

Molécula

Monómero

ADN

Base nitrogenada

ARN

Base nitrogenada

Proteína

Aminoácido

Cuadro 2.1: Moléculas y monómeros

Todas ellas están implicadas en el proceso de extracción de la información genética y su transformación.

15

2. CONCEPTOS PREVIOS

16

2.1.1. Formación de proteínas En el proceso de transcripción, el ADN se utiliza para la síntesis de ARN de diversos tipos. Sin embargo, no toda la información existente en el ADN queda reejada luego en el ARN. Existen fragmentos, llamados ADN basura, de los que, hoy por hoy, no se conoce funcionalidad alguna. El ARN resultante está exclusivamente dedicado a la generación de moleculas de proteínas. La fase de traducción es en la que se generan aminoácidos a partir de la información contenida en las moléculas de ARN. Para la construcción de un único aminoácido se necesitan tres bases de la secuencia de ARN. Esta traducción se realiza utilizando el llamado código genético por el que se hacen corresponder las tripletas de bases nitrogenadas con aminoácidos. Un mismo aminoácido puede ser sintetizado utilizando la información de distintas tripletas.

Transcripci´on

Traducci´on

adn

arn

proteina

Figura 2.1: Procesos de transcripción y traducción

Desde el punto de vista práctico, el uso del ARN es prácticamente imposible debido a que es un elemento temporal en el conjunto del proceso y su inestabilidad impide su manejo en el laboratorio. Si analizamos el proceso atendiendo a la información almacenada, la fase de traducción implica una pérdida de información al sintetizar las proteínas usando el antes mencionado código genético. Por estas razones se decidió utilizar las secuencias de ADN para su almacenamiento y compresión, a pesar de que contienen información aparentemente inútil.

2.1.2. Estructura del ADN Ya se ha dicho arriba que el ADN estaba formado por una sucesioón de bases nitrogenadas. Estas bases son cuatro que, por comodidad, se representan por su inicial: Adenina Citosina Guanina Timina

2.2. ÁRBOLES DE FILOGENIA

17

La molécula de ADN tiene una estructura de doble secuencia de bases complementarias. Dado que los emparejamientos están prejados, sólo es necesario almacenar una de ellas para conocer la otra. La longitud de estas moléculas puede oscilar desde unos centenares a millones de bases. Cabe decir que los estudios en esta rama de la ciencia están en pleno desarrollo lo que impide en estos momentos otorgar signicado alguno a las distintas combinaciones de bases de forma que pudiera repercutir en los métodos utilizados durante la compresión. Por este motivo se decidió que el algoritmo de compresión no debería hacer distinciones entre las distintas subcadenas de ADN.

2.2. Árboles de logenia Según la teoría de la evolución de las especies de Charles Darwin, todos los seres vivos son la evolución de un antepasado común. Un árbol de logenia es un grafo conexo acíclico en el que cada nodo representa a un organismo y cada arco

b

representa la relación de descendencia o evolución, de forma que se puede establecer qué seres tienen el antecesor común más cercano. Podríamos considerar el árbol genealógico de una familia como un árbol de logenia aunque los que se tratan en la realidad son más genéricos y, en lugar de tratar con individuos, tratan con especies. Sin embargo, un ejemplo con un b árbol genealógico servirá para explicar el uso y la construcción de estas estructuras.

Primo

Prima

Hermana

Individuo

Figura 2.2: Ejemplo de árbol de logenia de cuatro individuos

La gura 2.2 es el árbol logénico para cuatro individuos. Se aprecia cómo se crea el árbol de ancestros lógico según los parentescos dispuestos. Cabe resaltar que se crean nuevos individuos en un principio imaginarios que son ancestros de los reales o de otros creados de esta forma. Las conclusiones que se pueden extraer es que primo es más cercano a prima que al resto y lo mismo podemos decir de hermana e individuo. En este ejemplo, los nodos creados por el algoritmo corresponderían al abuelo, al padre y al tío del individuo.

2. CONCEPTOS PREVIOS

18

La complejidad de su construcción reside en decidir qué datos de cada individuo se usan para el estudio y en extraer información que sirva para construir el árbol a partir de los datos de cada individuo. Por supuesto, en este proyecto sólo nos interesan los obtenidos a partir de las secuencias de ADN mitocondrial de los distintos individuos. Este tipo de ADN, el mitocondrial, es algo especial puesto que sólo aparece en las mitocondrias, que son unos orgánulos presentes en las células de los organismos pluricelulares que realizan la respiración celular. Son los encargados de transformar el oxígeno en energía. La información genética almacenada es realmente extraña puesto que no sufre alteraciones durante la reproducción sino que es transmitida de forma directa a través del óvulo materno. De esta forma las variaciones sufridas a lo largo del tiempo son mucho menores que el resto de las cadenas de ADN, aunque estudios muy recientes apuntan cada vez más hacia la gran relevancia de dichas variaciones [15].

3 Desarrollo del estudio En este apartado se reeja el proceso de estudio de los distintos compresores de secuencias biológicas que han sido investigados, desde simples adaptaciones de algoritmos de compresión de carácter general a otros ya creados especícamente para secuencias de ADN.

3.1. Adaptación del algoritmo de Lempel-Ziv La primera opción como algoritmo de compresión para secuencias biológicas que sugirió la directora del proyecto fue la adaptación de los algoritmos de Lempel-Ziv al alfabeto formado por las cuatro bases nitrogenadas {A, C, G, T}. Generalmente, los algoritmos implementados de este tipo trabajan con bits o bytes, de forma que había que introducir alguna pequeña variación para que funcionaran con las secuencias de ADN. De entre la gran variedad de algoritmos de la familia LZ se eligió el LZ77 porque ofrecía la máxima simplicidad sin pérdida de capacidades. Este algoritmo recorre la entrada buscando en la parte aún no procesada una cadena que también esté en la parte ya recorrida, tal y como se explica en [20]. En caso de encontrar una repetición sucientemente larga, escribe una referencia a ella en lugar de la propia subcadena. Si no se encuentra tal repetición, se escribe el primer símbolo aún no procesado directamente en la salida y se continua con el siguiente símbolo hasta terminar el chero. La búsqueda puede limitarse hasta cierta ventana de comparación o comprobar todo el chero. Si está limitada en un cierto rango, la escritura de la referencia es más sencilla puesto que sólo pueden escribirse cierto rango de valores, lo que se puede hacer con un número de bits jo. Sin embargo, al limitar la búsqueda disminuye la probabilidad de encontrar una repetición y como consecuencia, la capacidad de compresión, por esta razón se eligió un algoritmo que hiciera una búsqueda completa. Por tanto, las referencias tuvieron que escribirse usando una

19

3. DESARROLLO DEL ESTUDIO

20

codicación de longitud variable [1]. En todos los casos se exigía que una repetición tuviera una longitud mínima tal que escribir una referencia a ella ahorra espacio en comparación a escribirla de forma directa. Esta parte sufrió la mayor variación junto con la codicación de los símbolos puesto que sólo se necesitan dos bits para codicar cualquiera de las cuatro bases nitrogenadas.

3.1.1. Resultados Los resultados obtenidos mediante esta adaptación no son en general buenos puesto que no se alcanzan niveles de compresión signicativos. Usando como unidad el número de bits que necesita una base para ser escrita, el mejor de estos algoritmos conseguía alcanzar una ratio de 1.927. Teniendo en cuenta que sin utilizar ningún método de compresión, al ser únicamente cuatro elementos los que componen las secuencias de ADN, se puede alcanzar unos niveles de compresión de 2 bits por base, el resultado no es muy bueno. De hecho, si se considera que el coste en tiempo de las búsquedas era bastante grande, no merecía la pena usar el algortimo implementado frente a la codicación directa de las bases.

3.2. Análisis de los algoritmos de compresión especícos Al quedar patente que con la adaptación del algoritmo de Lempel-Ziv no se alcanzaban resultados muy buenos se decidió analizar en profundidad los algoritmos creados para la compresión de secuencias de ADN. No resultó dicil encontrar el que parecía dar mejores resultados, DNACompress [5]. Es el último de los algoritmos conseguidos por un mismo grupo de trabajo que también fue el responsable del GenCompress [4].

3.2.1. GenCompress Este algoritmo es muy similar al algoritmo de Lempel-Ziv presentado arriba. También recorre secuencialmente el chero buscando repeticiones en la parte que ya ha sido analizada. La diferencia clave es que introduce la capacidad de detectar repeticiones inexactas. Al parecer, las secuencias de ADN, al estar sujetas a mutaciones puntuales, pueden poseer repeticiones de bastante longitud en las que unos pocos elementos dieren entre ambas apariciones. Al introducir esta posibilidad la forma de referenciar repeticiones cambia pues debe adaptarse a las nuevas capacidades. Ésto se traduce en que una referencia inexacta se codica como una referencia exacta más una serie de operaciones de

3.2. ANÁLISIS DE LOS ALGORITMOS DE COMPRESIÓN ESPECÍFICOS 21 edición (inserción, sustitución y borrado) que afectan a algunos de los elementos de esa repetición. La forma en que decide si una repetición debe tenerse en cuenta o no depende ahora de más factores. Para simplicar, se asignan puntos positivos por cada elemento repetido y negativos en caso de elementos diferentes. Esta puntuación se calcula en función del coste de la codicación de la cadena de operaciones que generaría. Cuando la puntuación de una repetición baja por debajo de un umbral se considera que no aportará más compresión si se continua con la misma referencia así que se decide acabar allí la repetición. La idea de aceptar repeticiones inexactas es innovadora pero los resultados empíricos no nos convencían mucho. En general, los elementos diferentes de las repeticiones aparecen al nal de las repeticiones puesto que son estos los que hacen bajar la puntuación hasta el límite permitido. Por esta razón no se consideró tan importante la capacidad de incluir repeticiones inexactas, al menos consideradas de esta forma.

3.2.2. Pattern Hunter La siguiente propuesta de este grupo de trabajo proponía un cambio radical. El compresor DNACompress se basa en los resultados de un motor de búsqueda de repeticiones llamado Pattern Hunter [8]. Este programa independiente devuelve una lista de las repeticiones inexactas más largas que tienen en común dos o más secuencias de ADN. DNACompress utiliza las salida producida por Pattern Hunter al ejecutarlo con un mismo chero como entrada para comprimir dicho chero cogiendo las mejores repeticiones. El algoritmo que nos interesa es el de Pattern Hunter. La diferencia principal con su predecesor es que trata la entrada de una manera totalmente distinta. Lo primero que hace es sacar una cadena binaria de comparación elemento a elemento de las entradas en las que un 1 indica que los elementos son iguales y un 0 indica que son distintos. Podemos ver un ejemplo en la gura 3.1. Secuencia A Secuencia B Diferencia

aaaccccaagggt aagctcaaagcgg 1101010111010

Cuadro 3.1: Ejemplo de construcción de cadena binaria

Cuando la entrada es una única cadena la compara con una copia desplazada de forma que todas las posiciones sean comparadas. Para buscar las repeticiones simplemente tendría que buscar secuencias de unos en la cadena binaria obtenida

3. DESARROLLO DEL ESTUDIO

22

pero como también permite repeticiones inexactas, lo que hace es buscar apariciones de un determinado patrón jo de longitud corta de forma que no require que todos los elementos sean unos. Por ejemplo, podría buscar en esa cadena binaria el patrón 11011 que indica que busque repeticiones inexactas que tengan dos bases repetidas alrededor de cada bases diferente. Una vez encontrados los patrones los intenta extender hacia ambos lados para obtener la mayor longitud posible. De nuevo, para considerar si un repetición es aceptable o no, utiliza el sistema de puntuación que también usa la versión anterior. Los resultados avalan este método fundamentalmente en lo que a velocidad se reere puesto que al usar cadenas binarias las comparaciones se pueden agilizar. Sin embargo mantiene el problema de su predecesor de dejar las inexactitudes en las repeticiones cercanas a ambos extremos de la cadena.

3.2.3. Pattern Hunter II La última versión del compresor DNACompress [7] consiste únicamente en la modicación del motor de búsqueda de repeticiones. Básicamente se trata de ampliar la posibilidad de encontrar repeticiones inexactas utilizando más de un patrón sobre la cadena binaria lo que hace que sea un algoritmo más exhaustivo que su primera versión sin aumentar drásticamente el tiempo de ejecución. Se presenta pues un nuevo problema muy complejo consistente en seleccionar un conjunto de patrones que funcionen de la mejor forma posible. Los resutados de esta nueva versión son muy similares a los de la primera versión aunque aumenta ligeramente la cantidad de repeticiones encontradas, lo cual mejora la compresión obtenida. El problema de estos dos algoritmos, claramente los mejores, es que no hay información suciente para estudiarlos en profundidad y mejorarlos sin tener que usar ingeniería inversa a partir del producto nal para reconstruirlos. Esta razón hacía imposible su uso como base para implementar alguna mejora.

3.2.4. dnaX A pesar de los buenos resultados de los algoritmos Pattern Hunter, no resultaba muy satisfactoria la solución basada en las repeticiones inexactas puesto que, analizando los resultados, los elementos distintos aparecen de forma muy escasa (nunca más de tres en una repetición) y generalmente situados hacia los extremos de las secuencias repetidas. Se consideró posible alcanzar los mismos niveles de compresión sin necesidad de tener en cuenta las repeticiones inexactas simplemente acortando las repeticiones. El algoritmo dnaX [10] opta por esta solución y añade una característica nueva como es la búsqueda de repeticiones invertidas. Al consultar ésto con bioquímicos

3.2. ANÁLISIS DE LOS ALGORITMOS DE COMPRESIÓN ESPECÍFICOS 23 se descubrió que no era descabellado encontrar inversiones de trozos de secuencias de longitudes que pueden llegar a casi un millón de bases [18]. Este algoritmo funciona de una forma algo distinta que los vistos con anterioridad. Básicamente trocea la secuencias en fragmentos de 20 bases y, tras la búsqueda de repeticiones exactas, codica la entrada según estos bloques, diciendo qué partes de las 20 bases están ya repetidas y qué partes no, referenciando las primeras y escribiendo las últimas directamente en el chero de salida. Las referencias a repeticiones deben ahora llevar un bit que indique si son repeticiones directas o inversas. Según el artículo que lo describe, los resultados que presenta este algoritmo son similares a los del Pattern Hunter tanto en tiempo como en tasa de compresión. Además, el autor facilitó las fuentes del algoritmo lo que hacía mucho más fácil el trabajo partiendo de su código para introducir alguna mejora.

24

3. DESARROLLO DEL ESTUDIO

4 Implementación del compresor En esta sección describo la solución implementada como compresor de secuencias de ADN. En la sección 6 se describen los resultados obtenidos con nuestro compresor y en comparación con los ya existentes.

4.1. Especicación del algoritmo Algoritmo: Entrada:

Compresor de cheros de ADN

Fichero en el que únicamente aparezcan los caracteres A, C, G y T en

mayúsculas o minúsculas.

Salida:

Se crea un chero que ocupa menos espacio que la entrada y que contiene

la misma información, de forma que se pueda recuperar el original utilizando un descompresor.

4.2. Directivas de diseño Tras el estudio realizado al problema se han obtenido ciertos elementos de diseño que se considera que el algoritmo debe tener presentes. Éstos se reeren tanto a las características particulares del tipo de datos tratado como al uso que se le va a dar a la aplicación obtenida.

4.2.1. Sobre los datos Las secuencias de ADN están formadas por cuatro elementos, por tanto con 2 bits por cada base se puede escribir el chero. Cualquier archivo comprimido generado debería superar esta tasa de compresión.

25

4. IMPLEMENTACIÓN DEL COMPRESOR

26

La longitud de las secuencias de ADN es enormemente diversa, desde unos pocos cientos a varios millones de bases. No se conoce ninguna estructura común a todas las secuencias de ADN sucientemente denida que permita dividirlo en secciones por razones semánticas o funcionales. La frecuencia de las distintas bases nitrogenadas es, en general, igual para todas ellas. El ADN suele contener repeticiones de longitudes muy variadas que no tienen por qué seguir cierta localidad espacial. Debido a la posibilidad biológica del ADN de sufrir mutaciones puntuales pueden aparecer repeticiones de secuencias no exactas que se diferencian en unas pocas bases. En las secuencias de ADN pueden aparecer fenómenos de inversión de ciertas subcadenas.

4.2.2. Sobre el uso del algoritmo Las condiciones de uso del algoritmo son de vital importancia para tenerlas en cuenta al diseñarlo. En el caso que nos ocupa el proceso de trabajo con una secuencia de ADN es aproximadamente el siguiente. 1. Se extrae en el laboratorio la molécula de ADN que se desea obtener. 2. Se secuencializa, también por métodos químicos, la molécula extrayendo así la sucesión de bases nitrogenadas. 3. Se introduce la secuencia obtenida en una base de datos de ADN. 4. Se consulta todas las veces que se necesite para compararla con otras muestras obtenidas o para cualquier otro cometido. Se puede deducir que la operación que más se realiza con este tipo de datos es la de consulta lo que nos sugiere que es ésta operación la que hay que agilizar en mayor medida, pudiendo invertir más tiempo en otras operaciones.

4.3. Estructura del algoritmo El algoritmo se divide en tres fases diferenciadas para facilitar su comprensión y su mantenimiento y son el análisis de la entrada, la selección de las repeticiones y la codicación, como se ve en la gura 4.1.

4.3. ESTRUCTURA DEL ALGORITMO

Entrada

An´alisis

Selecci´on

27

Codificaci´on

Salida

Figura 4.1: Fases del algoritmo

4.3.1. Análisis de la entrada En esta fase se trata de extraer la máxima información de la entrada que pueda servir posteriormente para comprimir al máximo. Fundamentalmente lo que se buscan son repeticiones dentro del chero de forma que luego puedan sustituirse las subsecuencias por referencias a otras que son iguales. La búsqueda de las repeticiones en el chero es una de las partes más costosas del algoritmo y donde hay que renar mucho el código para optimizar todo lo que se pueda el tiempo de ejecución. Sin embargo hay que tener en cuenta lo comentado anteriormente del proceso de trabajo con una secuencia de ADN en la que la compresión únicamente se realizaría una vez antes de almacenarla en la base de datos por lo que puede ser relativamente lenta comparado con la descompresión. Dado que la velocidad del algoritmo no era una prioridad se implementó como primera versión una busqueda exahustiva simple en la que para cada posición se buscaba en todo el chero la repetición más larga. Este código, aunque ecaz, era ya intratable para cheros de unos pocos millares de bases pues hacía enormemente costoso realizar cualquier prueba. La segunda opción surgió a partir de lo leído en [3] que indicaba que los árboles de sujos eran un tipo de datos que se amoldaba muy bien a la tarea a realizar. La dicultad de implementación de las operaciones sobre estas estructuras hizo que se optara por una versión más sencilla aunque un poco más ineciente, los vectores de sujos [9, 17]. Los vectores de sujos se basan en la ordenación de los sujos de una cadena de forma que se pueden encontrar las subcadenas repetidas como los prejos comunes de dos sujos consecutivos en la lista ordenada. Esta solución era satisfactoria en velocidad pero presentó un problema en la fase de selección de repeticiones. En caso de que dos subcadenas repetidas estén solapadas o varias repeticiones se reeran a una misma sección de la secuencia se debía descartar su uso y no se podían utilizar ni siquiera parcialmente para otra posible repetición. Debido a estos problemas se volvió a intentar la solución basada en los árboles de sujos porque permitían salvar los fallos de la anterior propuesta. Siguiendo las explicaciónes de Ukkonen [19] se implementó esta solución. Sin embargo no resultó la denitiva por varios motivos. El primero es que no había una forma sencilla de ampliar los árboles de sujos para que permitieran encontrar repeticiones invertidas y el segundo fue que era muy costoso encontrar las repeticiones porque aunque

4. IMPLEMENTACIÓN DEL COMPRESOR

28

la búsqueda de una subacadena es muy rápida, el número de subcadenas de una secuencia es muy grande como para que sea sucientemente rápido buscar todas y coger las mejores. A pesar de esto, la visión que Ukkonen da a los árboles de sujos tratándolos como autómatas sugirió la posibilidad de usar algún autómata para este trabajo. Precisamente ésto es lo que se describe en [14], donde se usan los autómatas para encontrar todas las repeticiones de subsecuencias en una secuencia. El funcionamiento además es simple y curioso. Basta con construir un autómata nito no determinista capaz de reconocer todas las subcadenas de una cadena de ADN y luego, al aplicarle la tranformación que lo convierte en determinista, se puede recuperar la información correspondiente a las repeticiones encontradas. Su sencillez permite a la vez extender el algoritmo descrito en el artículo para encontrar también las repeticiones invertidas simplemente modicando el autómata para que reconociera las subcadenas invertidas de la secuencia.

4.3.2. Selección de las repeticiones Esta segunda fase recoge las repeticiones encontradas en la primera y selecciona las que mayor tasa de compresión permiten. La compresión que aporta una repetición se puede cuanticar teniendo en cuenta lo que cuesta codicar las bases sin referenciar y lo que constaría crear la referencia. Una referencia consta de cinco elementos:

Original

Posición de la primera aparición

Repeticion Longitud Tipo

Posición de la segunda aparición

Longitud

Es directa o invertida

Los tres primeros son enteros positivos y el último un booleano. No se puede saber a priori la magnitud de los enteros puesto que no conocemos las posiciones relativas de las repeticiones y no hay ninguna longitud predilecta. Sin embargo, para tener en consideración la aparición de posibles mutaciones puntuales, una vez ordenadas las repeticiones por orden de posición dentro del chero, se puede suponer que dos repeticiones consecutivas están muy próximas y se puede almacenar la distancia entre ambas en lugar de la posición absoluta. La cuanticación del benecio de una referencia queda determinado por la siguiente fórmula:

Benef icio = Ref erencia − Codif icacin directa

4.3. ESTRUCTURA DEL ALGORITMO

29

Ref erencia = 3 × Entero + 1 Codif icacin directa = 2 × Longitud Además de seleccionar las repeticiones usando el benecio como principal factor hay que tener en cuenta que no se referencien dos veces la misma zona del chero por lo que hay que controlar que las repeticiones no se solapen. Si tenemos en cuenta el algoritmo de descompresión se puede apurar aún más el uso de las repeticiones. La reconstrucción se realiza en orden secuencial de forma que no es necesario tener toda la referencia completa antes de escribir el trozo repetido. Se pueden utilizar repeticiones solapadas. Un ejemplo de un caso extremo pero muy claricador es el siguiente: Secuencia:

aaaaaaaa

Si consideramos las repeticiones que no se solapen nos podría quedar un chero comprimido con la siguiente información:

aaaa, (0, 4, 4, normal) El paréntesis corresponde a la repetición que comienza en la posición 4, es igual a la que empieza en la posición 0 de longitud 4 en el orden normal. Si consideramos las repeticiones solapadas, esta misma secuencia podría quedar de la siguiente forma:

a, (0, 1, 7, normal) Vemos que ahora la repetición comienza en la posición 1 y que es igual a la que empieza en la posición 0 de longitud 7 en sentido normal. Esto es posible porque en cuanto se escribe la primera a repetida se incorpora a la cadena, lo que servirá a su vez para reconstruir el resto. Esta posibilidad no se presenta cuado las repeticiones están invertidas porque se necesita el último elemento de la subsecuencia original para recuperar la primera base de la repetida. Así que a la hora de seleccionar las secuencias que se usarán en las referencias, las repeticiones directas son mejores.

4.3.3. Codicación de la salida Ésta es la última fase del proceso pero puede intervenir en algunos aspectos del resto de las fases, fundamentalmente en el cálculo de los benecios que se obtienen al considerar una repetición. La compresión obtenida depende del método de codicación empleado para escribir los enteros y también del que se usa para las bases.

Codicación de los enteros Es uno de los puntos en los que más se ha trabajado para intentar reducir el tamaño de la codicación. El problema se reduce a escribir en el mínimo espacio un conjunto de enteros de los que se sabe bastante poco.

4. IMPLEMENTACIÓN DEL COMPRESOR

30

Lo primero que se desconoce es un rango sucientemente preciso. Es cierto que se puede hacer una estimación considerando que todos los enteros serán positivos y, como mucho, de la longitud de la secuencia. Sin embargo, esta estimación es muy mala puesto que los enteros más grandes son menos probables y una representación de longitud ja llevaría a un desperdicio de espacio. Hay que utilizar pues una codicación de enteros de longitud variable. Este tipo de codicaciones puede llegar a ser muy complejo, como las basadas en los números de Fibonacci o en la rampa logarítmica, y esta complejidad se traduce en tiempo de cómputo que ralentiza la ejecuión en gran medida. Una solución sencilla y eciente es la que se utiliza en el algoritmo dnaX [10] llamada Codicación de Bit de Continuidad (CBC). Se basa en dividir la codicación binaria del entero en grupos de bits de tamaño jo e intercalar entre estos un bit que será un 0 entre los dos últimos grupos y un 1 en el resto. Se puede ver un ejemplo en el cuadro 4.1, donde se utiliza éste método formando grupos de tres bits y añadiendo el de continuidad.

Decimal

Binario

CBC

75

1001011

1001 1001 0011

Cuadro 4.1: Codicación de Bit de Continuidad

Aparte del método de codicación, se puede variar el dato en sí para hacerlo más pequeño. Esto es algo que hemos aplicado puesto que en lugar de escribir las posiciones de las repeticiones se escriben las diferencias con las posiciones de las repeticiones anteriores, de forma que escribir la resta entre dos enteros es siempre más económico que escribir cualquiera de ellos. Lo único que permacece sin especicar de manera ja es el tamaño de los grupos que se utilizan en la codicación de bit de continuidad. Éste es un parámetro que hay que jar de forma empírica y cuyo valor óptimo puede variar para cada chero de ADN.

Codicación de las bases Se sabe que las bases se pueden codicar utilizando 2 bits para cada una. La cuestión es si este límite se puede reducir. Para ello se han probado métodos estadísticos de compresión como pueden ser los códigos de Human, arítmetico o de rango descritos en [2, 11, 13]. El primero se muestra incapaz de bajar del límite puesto que las diferencias entre las frecuencias de las bases son mínimas. Los otros dos son de la misma familia y sí que consiguen en algunos casos reducir la codicación. El aritmético posee ciertas trabas por licencias así que se empleó el de rango para este proyecto.

4.4. CONSIDERACIONES GENERALES

31

El problema que presenta en la práctica es que en ocasiones supera el límite de 2 bits por base y realiza una codicación peor. Por eso es necesaria una comprobación y, en caso de detectar que no consigue la compresión, se utiliza la traducción directa a dos bits.

4.4. Consideraciones generales Además de las directivas de diseño que se desprendían de los estudios de otros algoritmos ya existentes, se han tomado otras decisiones para incrementar las prestaciones del algoritmo desarrollado. En cuanto a la mejora en velocidad se ha optado por limitar ciertos elementos para que se redujera el tiempo de cáculo. En este apartado cabría destacar la acotación de la búsqueda de repeticiones a las que que sean más largas que una cierta longitud mínima. Este límite inferior viene marcado por la fórmula de benicio, ya que no se aceptan las repeticiones que no aportan benecio. Para ello se calcula para cada chero cuál es la longitud mínima de las repeticiones en el mejor de los casos, es decir, con los demás datos de la referencia lo más pequeños posibles. En el caso de la organización del chero comprimido se contrastaron dos versiones distintas. En una, las referencias se almacenan separadas del resto de los elementos no referenciados. La otra opta por insertar una marca entre las bases no referenciadas que indique que allí hay una referencia. Esta segunda forma permite eliminar uno de los campos de las referencias sin embargo añade un nuevo símbolo al alfabeto lo que hace que 2 bits no sean sucientes para representarlos. Las pruebas demostraron que la primera opción era la que más comprimía. Otro aspecto fundamental que se deseaba evitar era la parametrización. Gran parte de los algoritmos estudiados permitían variar ciertos parámetros para incrementar la compresión. Éstos parámetros no son intuitivos ni tienen relación directa con aspectos apreciables en la cadena de adn, de forma que cualquier usuario sería incapaz de decidir sin realizar varias pruebas cuál es el valor óptimo para cada parámetro. Dado que la compresión de una secuencia de ADN es una operación que se repite con poca frecuencia, se propuso que la elección del mejor valor la hiciera el propio algoritmo. Un ejemplo de este comportamiento es la búsqueda de la longitud mínima para las repeticiones, algo que otros algoritmos jaban de forma estática.

4.5. Interfaz gráca La última tarea que se realizó en la fase de implementación del compresor fue dotarlo de una interfaz gráca que facilitara la extracción de los resultados y la

4. IMPLEMENTACIÓN DEL COMPRESOR

32

comparación entre los distintos algoritmos.

Figura 4.2: Ventana principal Fundamentalmente consta de una ventana principal que nos muestra dos cuadros de selección donde se elige tanto el algoritmo que se va a usar como la acción (compresión o descompresión). También están los dos campos fundamentales correspondientes a los cheros de entrada y salida del algoritmo. Por último, y en los casos que el algoritmo lo requiera, se puede especicar el valor de un parámetro que interviene en la codicación de los enteros. Por supuesto aparecen también los botones necesarios para que comience la acción elegida y el que provca que se acabe la ejecución del programa. Una imagen de esta ventana se puede ver en la gura 4.2.

Figura 4.3: Ventanas de estadísticas Una vez concluida la ejecución de uno de los dos compresores, se muestra por pantalla en otra ventana la información del chero que se ha comprimido y la

4.5. INTERFAZ GRÁFICA

33

tasa de compresión que se ha alcanzado (en bits por base). Ésto permite realizar las pruebas de una forma sencilla y rápida. Se pueden ver unos ejemplo de estas ventanas en la gura 4.3.

34

4. IMPLEMENTACIÓN DEL COMPRESOR

5 Árboles logénicos En esta sección se tratan las características que debe tener un compresor de secuencias biológicas para poder ser usado como herramienta en la construcción de los árboles de logenia. Ya se ha comentado en secciones anteriores que los árboles logénicos tienen como nalidad crear una estructura gráca que represente las relaciones evolutivas de distintos individuos. En el caso de las secuencias biológicas, siven para conocer las ramas evolutivas que han llevado a la existencia de las distintas especies según la teoría de Darwin. Todos los métodos que existen se podrían clasicar en dos grandes grupos dependiendo de cómo comparan las secuencias. El primer grupo utiliza toda la información de todas las secuencias para construir el árbol. El segundo, compara las secuencias dos a dos y obtiene una matriz numérica simétrica cuyos valores caracterizan la similitud entre las dos secuencias. La intención del proyecto es la de utilizar el compresor como herramienta para generar el árbol así que se aprovechan las comparaciones que realiza el propio algoritmo descrito en la sección 4 para contabilizar la similitud entre dos secuencias. De esta forma, se construye una matriz de valores que luego servirá para generar el árbol.

5.1. Cálculo de la distancia entre dos secuencias En el artículo [4] realizan una tarea similar aunque con pretensiones más teóricas, pues tratan de caracterizar la complejidad de Kolmogorov de una cadena tanto simple como condicionada a otra y usar los resultados existentes sobre este tema para conseguir un número que indique la similitud entre dos secuencias de ADN. La medida que se usa en el proyecto como distancia entre dos secuencias es

35

5. ÁRBOLES FILOGÉNICOS

36

básicamente la tasa de compresión que se alcanza al comprimir la concatenación de esas dos secuencias. Este método no asegura que la matriz sea simétrica pero experimentalmente se comprueba que la variación es practicamente nula entre las dos posibles permutaciones del par de secuencias. De todas formas, se ha considerado directamente la media de las tasas de las dos permutaciones como distancia entre la pareja de secuencias, lo cual asegura la simetría.

5.2. Características del compresor Con estos planteamientos ya se pueden extraer algunas conclusiones de cómo debería ser el algoritmo de compresión para considerarlo como correcto en el cáculo de la similitud. Entre otras cosas, debe tener capacidad de almacenamiento de la parte de la secuencia ya procesada porque al tratar la parte correspondiente al segundo individuo, debe poder reconocer esos mismos elementos en la parte correspondiente al primero. Con este requerimiento se eliminan los compresores estadísticos como podrían ser los códigos de Human o la codicación aritmética. Dentro de la familia de los algoritmos de Lempel-Ziv se deben excluir los que derivan del LZ78 porque el método que utiliza para crear el diccionario de las subsecuencias encontradas es muy inestable. Con esto me reero a que la variación 1

de una única base puede provocar la reconstrucción de todo el diccionario . Por esa razón sólo se pueden considerar útiles aquellos algoritmos que almacenan las secuencias de una forma más consistente. En este grupo se incluyen por supuesto el DNACompress y el dnaX que, al ser especícos de las secuencias de ADN, recogen mejor las similitudes que los no dedicados como el gzip.

5.3. Métodos de cálculo En este punto ya se dispone de una matriz de valores con las distancias entre los distintos individuos. Se presentan de nuevo multitud de métodos para conseguir la forma arbórea del esquema. El más simple, busca la pareja más cercana, genera un individuo imaginario antecesor de ambas secuencias recalculando las distancias con el resto y repite el proceso hasta construir el árbol. En el otro extremo están los que consideran todos los posibles árboles y se quedan con el más probable basándose en el mínimo cambio entre padre e hijo u otras leyes de evolución. La elección del método de cálculo no es sencilla a priori puesto que la información que se maneja ya no es de tipo biológico sino matrices de números de las que

1 Es

un ejemplo de la catástrofe del bit [13, 12]

5.4. HERRAMIENTAS UTILIZADAS

37

poca información se puede extraer. Por eso se tomó la decisión de utilizar varias alternativas y escoger la que mejores resultados diera.

5.4. Herramientas utilizadas Como ya he dicho en la anterior sección, los métodos de construcción de árboles a partir de matrices numéricas están muy desarrollados por lo que se optó por usar algún paquete de software ya desarrollado. Se encontró de esta forma el 2

paquete PHYLIP

del Departameto de Biología de la Universidad de Washington,

que ofrece los algoritmos que se estaban buscando y otros que permiten la construcción gráca del árbol logénico. Los métodos que incorpora este paquete son los siguientes: Fitch Kitch Neighbor El primero y el segundo calculan la logenia a partir de la matriz de distancias usando el modelo que estima que las distancias deben igualar las sumas de las longitudes de las ramas de las distintas especies. Utilizan ambas el criterio de Fitch-Margoliash. La diferencia fundamental es que el método Kitch asume un reloj evolutivo lo que se traduce en que las longitudes totales de las ramas (desde la raiz a las hojas) deben ser iguales, pues son estas longitudes las que representan el tiempo. El método Fitch no considera esta restricción por lo que no se puede hacer ningún cálculo temporal sobre los resultados. El tercer método es el más simple de todos aunque también el más rápido y construye el árbol uniendo los

linages

más parecidos hasta llegar a unir todos. Por su simplicidad no considera

tampoco reloj evolutivo por lo que no se pueden realizar cálculos temporales sobre sus resultados. Las pruebas realizadas para comprobar qué método se ajustaba mejor a la construcción de árboles basados en cadenas de ADN y los resultados obtenidos se explican en detalle en la sección 6 de Resultados.

2 http://evolution.genetics.washington.edu/phylip.html

38

5. ÁRBOLES FILOGÉNICOS

6 Resultados En esta sección se comentan las pruebas realizadas con el compresor y con los métodos de construcción de árboles de logenia.

6.1. Resultados del compresor En esta parte se especican las pruebas realizadas al compresor y se expone una comparativa con los otros algoritmos de referencia utilizados.

6.1.1. Banco de pruebas Las pruebas se han realizado en primer lugar sobre un banco de pruebas que es usado en todos los artículos que se han consultado. No se explica en ninguno de ellos la razón de su uso pero parece muy estandarizado. Los resultados sobre este banco de pruebas se pueden consultar en el cuadro 6.1. El tamaño está medido en número de bases y el nivel de compresión indicado en cada casilla se reere a los bits por base que ocupa el chero obtenido. Se puede comprobar que el algoritmo dnaX es mejor que el resto en prácticamente todos los cheros. Al comprobar estos resultados y volver sobre el artículo que dene este algoritmo [10], se sospechó que el algoritmo estaba hecho prácticamente a la medida de este banco de pruebas así que se decidió construir otro banco de pruebas independiente para comprobar si mantenía el mismo rendimiento. El segundo banco de pruebas (Cuadro 6.2), se construyó cogiendo una secuencia genética de cada una de las clases en que las divide la base de datos NCBI buscando a la vez diversidad en las longitudes de las secuencias.

1 http://www.ncbi.nlm.nih.gov/

39

1

6. RESULTADOS

40

Secuencia Tamaño dnaX DNACompress Mi Algoritmo chmpxx 121024 1.68 1.84 1.96 chntxx 155844 1.62 1.93 2.00 hehcmvcg 229354 1.85 1.97 2.00 humdystrop 38770 1.95 1.92 1.99 humghcsa 66495 1.38 1.93 1.32 humhprtb 56737 1.92 1.92 1.97 mpomtcg 186608 1.93 1.96 1.97 vaccg 191737 1.76 1.91 1.93 Cuadro 6.1: Banco de pruebas ajeno

Secuencia Tamaño dnaX DNACompress Mi Algoritmo Aeromonas 11822 2.05 1.96 2.00 Anisakis 13916 1.84 1.77 1.85 Citrus Dwarf 294 3.95 2.37 2.12 Deinococcus 412344 1.87 1.88 1.98 Methanohalophilus 2158 2.44 1.89 2.02 Nanoarchaeum 490885 1.85 1.86 1.98 Plasmodium 643292 1.59 1.67 1.68 Porphyra Pluchra 6427 2.11 1.95 1.98 Virus de la Rabia 11932 2.07 1.97 2.00 Salmonella 46900 2.00 1.98 2.00 Cuadro 6.2: Banco de pruebas propio

6.1.2. Análisis de los resultados El análisis realizado a los datos tenía como objetivos fundamentales dos aspectos sobre los algoritmos:

Comparar los niveles de compresión de los distintos algoritmos.

Averiguar si la eciencia de los algoritmos era similar en los distintos bancos de pruebas.

Para conseguir estos objetivos se recurrió a realizar un estudio estadístico de los resultados.

6.1. RESULTADOS DEL COMPRESOR

41

Elección del estadístico Este es un punto fundamental a la hora de realizar el análisis pues afecta a todo el proceso. Se presenta una duda entre dos opciones al elegir el estadístico más correcto. La primera opción es la más simple y utiliza como elemento de medida la tasa de compresión (bits/base) obtenida en cada ejecución del algoritmo. Parece ser la más sencilla e intuitiva pero hay casos en los que perjudica de forma excesiva a cierto algoritmo. Si se observan los datos correspondientes al

Citrus Dwarf

del Cuadro

6.2, destaca de forma contundente el resultado correspondiente al algoritmo dnaX, que obtiene una tasa de 3.95 cuando el límite superior permitido es de 2 bits por base. Sin embargo, dado que el tamaño de esa secuencia es muy pequeño, sólo 294 bases, esto representa una pérdida de apenas 574 bits, lo que representa una cantidad nimia si tenemos en cuenta la ganancia que se obtiene en otros cheros más grandes. La segunda opción recoge este caso de forma que se considera como medida de compresión la tasa de compresión de todos los cheros juntos, es decir, el total del tamaño comprimido dividido para el total de bases. De esta forma se diluye el nefasto resultado antes mencionado pues su ponderación en el resultado nal es prácticamente despreciable. Sin embargo esta segunda opción no reeja con exactitud todos los aspectos que intervienen en la compresión. Además de la obligatoria cabecera que se ha de introducir en los cheros comprimidos y que hace que las tasas de los cheros pequeños sean peores, la decisión de poner una longitud mínima ja para las repeticiones también inuye en la capacidad de compresión, perjudicando de nuevo a los cheros pequeños. Dado que este factor era uno de los tratados en la implementación realizada en este proyecto, parece fundamental que se vea reejado en el análisis. Por eso se eligió la tasa de compresión de cada chero como estimador.

Comparativa de los niveles de compresión La cuestión que se aborda con este análisis es averiguar si el algoritmo implementado era igual, mejor o peor que los otros dos que se han tomado como referencia. Para conseguir ésto, la prueba más conveniente es una comparación de las medias de las tasas, basada en la t de Student, teniendo en cuenta que los resutados de ambos algoritmos provienen de los mismos datos, lo que es equivalente a decir que los datos están emparejados. Con los resultados presentados en el Cuadro 6.3 se puede armar que las medias son iguales para las dos muestras a un nivel de conanza de 95 %. Es decir, en media, tanto el algoritmo implementado como el basado en Pattern Hunter obtienen la misma tasa de compresión.

6. RESULTADOS

42

Media Varianza Observaciones Grados de libertad Estadístico t P(T0,

// T( n ) :

en

correspondientes

NSIMBOLOS

// PRE :

//

simbolos

cuatro

#define int int

y

ADN_H

los

//

adn

ADN_H

numero

∗ − ∗ − ∗/

de

1

0,

un

esta

asociado

variable entero

es

a

un

fichero

válido

y

el

y

el

válido

leido

en

longitud

variable

continuidad )

O( l o g

n)

getlvs () ;

// PRE : //

El

flujo

parámetro

// POST : //

// T( n ) :

bool

de

// POST : // T( n ) :

True

entrada longitud

n

un

esta

asociado

variable

entero

continuidad )

O( l o g

f a i l () {

// PRE :

de de

Devuelve

( bit

leido

y

es en

return

Devuelve

true

si

ha

currido

contador ;

flujo ;

BUFFER_SIZE =

8;

l ;

obstream { :

obstream ( ) ;

True Crea

// T( n ) :

O( 1 )

por

algún

O( 1 )

// POST :

fichero

un

un

flujo

de

bits

de

válido

variable bit

flujo . f a i l () ; } ;

:

// PRE :

un

longitud

precedido

buffer ;

ifstream

a

válido

n)

private char int static const int int

class public

a

O( 1 )

// f u n c i o n e s

};

variable

True

// POST :

int

de

close () ;

// PRE :

int

parametro

True

// POST :

int

el

O( 1 )

open (

// PRE :

void

61

salida

fallo

de

signo

APÉNDICE A. CÓDIGO FUENTE DE LA IMPLEMENTACIÓN

62

int

obstream (

// PRE : // POST : //

k) ;

True Crea

en

// T( n ) :

flujo

de

bits

variable

de

salida

con

( continuation

parametro

bit

k

para

const char

∗ nombre ) ;

True

// POST :

Crea

// T( n ) :

O( 1 )

un

flujo

const char

obstream (

// PRE :

de

bits

∗ nombre ,

True

// POST :

Crea

un

flujo

//

con

//

( continuation

// T( n ) :

parametro

de

k

bit

int

bits

para

de

salida

asociado

al

fichero

nombre

asociado

al

fichero

nombre

k) ;

de

salida

lectura

en

longitud

variable

encoding )

O( 1 )

~obstream ( ) ;

// PRE :

True

// POST :

Cierra

el

flujo

//

libera

la

memoria

y

// T( n ) :

void

int

return

el

parametro

open (

el

parametro

const char

Asocia O( 1 )

a

k

de

longitud

variable

a

k

∗ nombre ) ;

el

flujo

el

buffer

de

salida

al

fichero

y

cierra

el

flujo

nombre

close () ;

// PRE :

True

// POST :

Vuelca

// T( n ) :

O( b u f f e r . s i z e )

de

salida

volcar () ;

// PRE :

True

// POST :

Vuelca

// T( n ) :

O( b u f f e r . s i z e )

// f u n c i o n e s El

// POST :

flujo

escribe

// T( n ) :

buffer

bit ) ;

de

salida

bit

en

el

esta

asociado

flujo

de

a

un

fichero

válido

un

fichero

válido ,

salida

O( 1 )

int

put (

// PRE :

bool

el

escritura

putbit (

// PRE :

//

variable

True

// T( n ) :

void

longitud

O( 1 )

// POST :

void

de

l ;};

devuelve

// T( n ) :

void

buffer ,

True

// POST :

void

el

O( 1 )

getK ( ) {

// PRE :

volcando

k) ;

Establece

// T( n ) :

void

salida ,

k > 0

// POST :

// PRE :

de

O( 1 )

setK (

// PRE :

int

lectura

encoding )

O( 1 )

obstream (

// PRE :

un

longitud

El

0 <

c ,

flujo

bits

<

// POST :

escribe

// T( n ) :

O( b i t s )

int

bits ) ;

de

salida

32 , c

en

esta

asociado

l o g 2 ( c ) = 0

salida

usando

usando

bits

A.5. ENTRADA-SALIDA DE BITS

void

putlv (

// PRE : //

El

el

// POST : // T( n ) :

void

int

//

en

el

de

n

int

de de

Escribe

El

// POST : // T( n ) :

un

es

salida

fichero

válido ,

n >= 0 ,

válido

usando

bit

de

continuidad

n) ;

flujo

O( l o g

flujo

variable

a

n)

entrada longitud

n

en

por

el un

esta

flujo bit

asociado

variable de

de

es

a

un

fichero

válido

y

el

válido

salida

usando

bit

de

continuidad

y

signo

n)

bool

put ( v e c t o r <

// PRE :

longitud

asociado

escribe

precedido

void

en

variable

El

// T( n ) :

n

esta

longitud

parámetro

//

salida de

O( l o g

// POST :

de

parametro

putlvs (

// PRE :

// e s c r i b e

n) ;

flujo

63

flujo

Escribe

de

las

> &v ) ;

entrada

esta

componentes

asociado del

a

vector

un v

fichero en

el

válido

flujo

de

salida

O( v . s i z e ( ) )

int private int int static const int int size ;

//

numero

de

bits

escritos

en

el

flujo

:

buffer ;

contador ;

ofstream

flujo ;

BUFFER_SIZE =

8;

l ;

};

#endif

A.5. Entrada-Salida de Bits /∗

∗ Fichero : bstream . cc ∗ Autor : P a b l o U r c o l a I r a c h e ∗ Fecha : 2006 ∗ P r o y e c t o : PFC A l g o r i t m o s d e c o m p r e s i ó n d e s e c u e n c i a s b i o l ó g i c a s ∗ y s u a p l i c a c i ó n en á r b o l e s f i l o g é n i c o s ∗ Contenido : Implementación de l a s c l a s e s de s t r e a m s a f i c h e r o s de ∗ entrada y s a l i d a ( ibstream y obstream r e s p e c t i v a m e n t e ) ∗ L i c e n c i a : Ver f i c h e r o LEEME ∗/

#include #include #include #include #include #include using namespace

" bstream . h"



std ;

ibstream : : ibstream () { buffer

=

contador l

=

0; =

0;

0;

}

ibstream : : ibstream ( buffer

=

0;

int

k){

bits

de

APÉNDICE A. CÓDIGO FUENTE DE LA IMPLEMENTACIÓN

64

contador l

=

0;

= k;

}

ibstream : : ibstream ( buffer

=

contador

0; =

const char

∗ nombre ) {

0;

f l u j o . o p e n ( nombre ) ; l

=

0;

}

ibstream : : ibstream ( buffer

=

contador

0; =

const char

∗ nombre ,

int

0;

f l u j o . o p e n ( nombre ) ; l

= k;

}

ibstream : : ~ ibstream () { flujo . close () ; }

void l

i b s t r e a m : : setK (

= k;

int

k){

}

void

i b s t r e a m : : open (

buffer

=

contador

0; =

const char

∗ nombre ) {

0;

f l u j o . o p e n ( nombre ) ; }

void

ibstream : : c l o s e () {

flujo . close () ;

}

bool if

ibstream : : g e t b i t () {

if else

( contador

==

0) {

(! flujo . eof () )

buffer

=

f l u j o . get () ;

buffer

=

0;

contador

int

= BUFFER_SIZE ;

}

bit

buffer

return

contador

}

− 1) ;

=

b u f f e r >>(BUFFER_SIZE



arn : : arn ( ) { raiz

=

0;

}

arn : : ~ arn ( ) { borrar () ; }

std ;

conjuntos

A.7. ÁRBOLES ROJINEGROS

int if return

int return

arn : : buscar ( vector < ( c o n j u n t o . empty ( ) )

69

> &c o n j u n t o ) {

buscar ( conjunto ,

− 1;

0) ;

}

int

int

arn : : buscar ( vector <

∗ aux ;

nodo

if if return

aux

=

return

(p +

− 1;

1 == v . s i z e ( ) )

return

∗ arn : : buscarNodo ( ∗ aux = r a i z ;

while if if else

−>d a t o ;

aux

−>s i g u i e n t e −>b u s c a r ( v ,

aux

a r n : : nodo nodo

p){

buscarNodo ( v [ p ] ) ;

( ! aux )

}

int

> &v ,

c){

return

( aux ) {

aux

−>c l a v e == c ) −>c l a v e > c ) = aux−>i z q u i e r d o

aux

= aux

( aux

( aux

return

int

p+1) ;

aux ;

;

−>d e r e c h o ;

}

}

void if

0;

arn : : i n s e r t a r (

int

id ,

int

vector<

( ! c o n j u n t o . empty ( ) ) i n s e r t a r ( id ,

conjunto ,

> &c o n j u n t o ) {

0) ;

}

void if if else if

arn : : i n s e r t a r (

∗ aux

nodo

=

int

id ,

int

vector<

buscarNodo ( v [ p ] ) ;

> &v ,

int

p){

( ! aux )

aux

=

(p +

insertarNodo (v [ p ] ,

−1) ;

1 == v . s i z e ( ) )

−>d a t o

aux

=

id ;

{

new

−> s i g u i e n t e ) −> s i g u i e n t e = arn ; aux−>s i g u i e n t e −> i n s e r t a r ( i d ( ! aux

aux

,

v,

p +

1) ;

} }

int

∗ arn : : i n s e r t a r N o d o ( ∗x = insertarABB ( c , d ) ; ∗ nuevo = x ;

a r n : : nodo nodo nodo

while if if

−> c o l o r

x

=

c ,

int

d){

rojo ;

r a i z && x−>p a d r e −> c o l o r == r o j o ) { −>p a d r e == x−>p a d r e −>p a d r e −>i z q u i e r d o ) { n o d o ∗ y = x−>p a d r e −>p a d r e −>d e r e c h o ; ( y && y−> c o l o r == r o j o ) { x−>p a d r e −> c o l o r = n e g r o ; y−> c o l o r = n e g r o ; x−>p a d r e −>p a d r e −> c o l o r = r o j o ; x = x−>p a d r e −>p a d r e ; (x

!=

(x

else if

}

{

−>p a d r e −>d e r e c h o ) { −>p a d r e ;

( x == x x = x

rota_i ( x) ; }

APÉNDICE A. CÓDIGO FUENTE DE LA IMPLEMENTACIÓN

70

−>p a d r e −> c o l o r = n e g r o ; −>p a d r e −>p a d r e −> c o l o r = r o t a _ d ( x−>p a d r e −>p a d r e ) ;

x

x

rojo ;

}

else if

}

{

∗y

−>p a d r e −>p a d r e −>i z q u i e r d o −> c o l o r == r o j o ) { x−>p a d r e −> c o l o r = n e g r o ; y−> c o l o r = n e g r o ; x−>p a d r e −>p a d r e −> c o l o r = r o j o ; x = x−>p a d r e −>p a d r e ;

nodo

= x

;

( y && y

else if

}

{

−>p a d r e −>i z q u i e r d o ) { −>p a d r e ;

( x == x x = x

rota_d ( x ) ; }

−>p a d r e −> c o l o r = n e g r o ; −>p a d r e −>p a d r e −> c o l o r = r o t a _ i ( x−>p a d r e −>p a d r e ) ;

x x

rojo ;

} } }

return raiz

}

void

−> c o l o r

=

negro ;

nuevo ;

∗x ) { −>d e r e c h o ; x−>d e r e c h o = y−>i z q u i e r d o ; ( y−>i z q u i e r d o ) y−>i z q u i e r d o −>p a d r e y−>p a d r e = x−>p a d r e ; ( ! x−>p a d r e ) a r n : : r o t a _ i ( nodo

∗y

nodo

if if else if − else raiz

= x

= x;

= y;

−>p a d r e −>i z q u i e r d o ) −>i z q u i e r d o = y ;

( x == x

x

>p a d r e

x−>p a d r e −>d e r e c h o −>i z q u i e r d o = x ; x−>p a d r e = y ;

= y;

y

}

void

∗x ) { −>i z q u i e r d o ; x−>i z q u i e r d o = y−>d e r e c h o ; ( y−>d e r e c h o ) y−>d e r e c h o −>p a d r e y−>p a d r e = x−>p a d r e ; ( ! x−>p a d r e ) a r n : : rota_d ( nodo

∗y

nodo

if if else if − else raiz

= x

= x;

= y;

−>p a d r e −>d e r e c h o ) −>d e r e c h o = y ;

( x == x

x

>p a d r e

x−>p a d r e −>i z q u i e r d o −>d e r e c h o = x ; x−>p a d r e = y ;

= y;

y

}

∗ arn : : insertarABB ( ∗ aux = r a i z ;

a r n : : nodo nodo

int

c ,

int

d){

A.8. COLA DE PRIORIDADES (CABECERA)

if

new

71

(! raiz ){ raiz

=

nodo ( c ) ;

return while true if − if − − else raiz

−>d a t o

= d;

raiz ;

}

(

(c

){

<

aux

( aux

aux

>c l a v e ) {

>i z q u i e r d o )

= aux

>i z q u i e r d o ;

new

{

−>i z q u i e r d o = nodo ( c ) ; −>i z q u i e r d o −>d a t o = d ; aux−>i z q u i e r d o −>p a d r e = aux ; aux−>i z q u i e r d o ; aux

aux

return

}

else if else

}

{

−>d e r e c h o ) −>d e r e c h o ;

( aux

aux

= aux

new

{

−>d e r e c h o = nodo ( c ) ; −>d e r e c h o −>d a t o = d ; aux−>d e r e c h o −>p a d r e = aux ; aux−>d e r e c h o ; aux aux

return

} } } }

void

arn : : b o r r a r ( ) {

borrar ( raiz ) ;

}

void if

a r n : : b o r r a r ( nodo

∗x ) {

(x){

−>d e r e c h o ) ; −>i z q u i e r d o ) ;

borrar (x

if − − delete

borrar (x (x

x

}

>s i g u i e n t e )

>s i g u i e n t e

−>b o r r a r ( )

;

x;

}

A.8. Cola de prioridades (Cabecera) /∗

∗ Fichero : cola . h ∗ Autor : P a b l o U r c o l a I r a c h e ∗ Fecha : 2006 ∗ P r o y e c t o : PFC A l g o r i t m o s d e c o m p r e s i ó n d e s e c u e n c i a s b i o l ó g i c a s ∗ y s u a p l i c a c i ó n en á r b o l e s f i l o g é n i c o s ∗ C o n t e n i d o : D e c l a r a c i ó n d e l a c l a s e c o l a q u e i m p l e m e n t a una c o l a ∗ p r i o r i d a d e s de r e p e t i c i o n e s ∗ L i c e n c i a : Ver f i c h e r o LEEME ∗/

#ifndef #define #include

COLA_H COLA_H



de

APÉNDICE A. CÓDIGO FUENTE DE LA IMPLEMENTACIÓN

72

#include #include using namespace class public

" automata . h"

std ;

cola { :

cola () ;

// PRE :

true

// POST :

Crea

// T( n ) :

O( 1 )

una

cola

vacia

la

cola

y

~cola () ;

// PRE :

true

// POST :

Vacia

// T( n ) :

O( 1 )

void

la

e n c o l a r ( automata : : r e p e t i c i o n

// PRE : // POST : // T( n ) :

// POST : // T( n ) :

void

memoria

&x ) ;

true introduce O( l o g

x

la

en

la

cola

n)

automata : : r e p e t i c i o n

// PRE :

cola

no

Devuelve

&p r i m e r o ( ) ;

esta la

vacia

repetición

de

mayor

longitud

de

la

cola

O( 1 )

desencolar () ;

// PRE :

la

cola

no

// POST :

Elimina

// T( n ) :

O( l o g

int

libera

size ()

// PRE : // POST : // T( n ) :

private

esta

de

la

vacia cola

el

elemento

de

mayor

longitud

n)

const return {

monticulo . s i z e ( ) ; } ;

true

Devuelve

el

numero

de

elementos

que

hay

en

la

cola

O( 1 )

:

v e c t o r

monticulo ;

};

#endif

A.9. Cola de prioridades /∗

∗ Fichero : c o l a . cc ∗ Autor : P a b l o U r c o l a I r a c h e ∗ Fecha : 2006 ∗ P r o y e c t o : PFC A l g o r i t m o s d e c o m p r e s i ó n d e s e c u e n c i a s b i o l ó g i c a s ∗ y s u a p l i c a c i ó n en á r b o l e s f i l o g é n i c o s ∗ C o n t e n i d o : I m p l e m e n t a c i ó n d e l a c l a s e c o l a q u e i m p l e m e n t a una c o l a ∗ p r i o r i d a d e s de r e p e t i c i o n e s ∗ L i c e n c i a : Ver f i c h e r o LEEME ∗/

#include #include #include #include

" c o l a . h"

de

A.10. ÁRBOL DE RANGOS (CABECERA)

#include #include using namespace

73

" automata . h"

std ;

cola : : cola () { monticulo . c l e a r ( ) ; }

c o l a : : ~ c o l a ( ) {}

void int while

c o l a : : e n c o l a r ( automata : : r e p e t i c i o n

&x ) {

m o n t i c u l o . push_back ( x ) ; pos

=

monticulo . s i z e ( )

( pos

>

0 &&



1;

m o n t i c u l o [ ( pos

− 1) / 2 ] .

longitud

swap( m o n t i c u l o [ p o s ] , pos

=

( pos

<

monticulo [ pos ] . l o n g i t u d ) {

m o n t i c u l o [ ( pos

− 1) / 2 ] )

;

− 1) / 2 ;

} }

return

automata : : r e p e t i c i o n

}

void

&c o l a : : p r i m e r o ( )

{

monticulo . f r o n t ( ) ;

cola : : desencolar () {

swap( m o n t i c u l o . f r o n t ( ) ,

int int while if

m o n t i c u l o . back ( ) ) ;

m o n t i c u l o . pop_back ( ) ; pos

=

0;

hijo ; (2 ((2

∗ p o s +1 ∗ p o s +1

<

( monticulo [2

else if

monticulo . s i z e ( ) ) {

==

monticulo . s i z e ( )

∗ pos + 2]. l o n g i t u d

hijo

=

2

∗ pos

+

1;

hijo

=

2

∗ pos

+

2;

( monticulo [ pos ] . l o n g i t u d

<

<



1)

||

monticulo [2

monticulo [ h i j o ] . longitud ) {

swap( m o n t i c u l o [ p o s ] , pos

=

else break

∗ pos + 1]. l o n g i t u d ) )

monticulo [ h i j o ] ) ;

hijo ;

}

}

;

}

A.10. Árbol de rangos (Cabecera) /∗

∗ Fichero : rangos . h ∗ Autor : P a b l o U r c o l a I r a c h e ∗ Fecha : 2006 ∗ P r o y e c t o : PFC A l g o r i t m o s d e c o m p r e s i ó n d e s e c u e n c i a s b i o l ó g i c a s ∗ y s u a p l i c a c i ó n en á r b o l e s f i l o g é n i c o s ∗ Contenido : D e c l a r a c i ó n de l a c l a s e a r b o l R a n g o s que implementa l a s ∗ de o c u p a c i o n y p r e g u n t a r por l i b r e de r a n g o s ∗ L i c e n c i a : Ver f i c h e r o LEEME ∗/

#ifndef #define

ARANGOS_H ARANGOS_H

funciones

APÉNDICE A. CÓDIGO FUENTE DE LA IMPLEMENTACIÓN

74

#include using namespace class public



std ;

arbolRangos { :

arbolRangos ( ) ;

// PRE :

true

// POST :

crea

// T( n ) :

O( 1 )

un

arbol

de

rangos

con

todo

libre

y

libera

~arbolRangos ( ) ;

// PRE :

true

// POST :

borra

// T( n ) :

void

el

libre (r)

// POST :

ocupa

el

// T( n ) :

O( l o g

n)

la

memoria

devuelve

// T( n ) :

O( l o g

,

true

> &r ) ;

r

int int

true

// POST :

private enum enum struct int

rangos

,

rango

l i b r e ( pair<

// PRE :

de

int int

i n s e r t a r ( pair<

// PRE :

bool

arbol

O( n )

const

> &r )

si

el

rango

;

r

no

esta

ocupado

n)

:

colores

{ rojo ,

posicion nodo

ambos } ;

,

∗ izquierdo

,

∗ derecho ;

clave ;

posicion

info ;

colores

color ;

nodo ( ) :

padre ( 0 ) ,

void void void void void

segundo ,

{

∗ padre

nodo

negro } ;

{ primero ,

izquierdo (0) ,

derecho (0) {};

};

int

b o r r a r N o d o ( nodo insertar (

int

c ,

nodo

∗ insertarABB ( r o t a _ i ( nodo ∗n ) ; rota_d ( nodo ∗n ) ; p i n t a r ( nodo ∗n ) ;

nodo

∗ raiz

∗n ) ; posicion c ,

d) ;

posicion

p) ;

;

};

#endif

A.11. Árbol de rangos /∗

∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗

Fichero :

rangos . cc

Autor :

Pablo

Fecha :

2006

Proyecto : y

su

Contenido : de

PFC

Urcola

Irache

Algoritmos

aplicación

en

Implementación ocupacion

rojinegros

y

de

compresión

árboles de

la

preguntar

de

secuencias

biológicas

filogénicos clase por

arbolRangos

libre

de

que

rangos

implementa

usando

las

arboles

funciones

A.11. ÁRBOL DE RANGOS ∗ Licencia : ∗/

Ver

#include #include #include #include using namespace

fichero

75

LEEME

" r a n g o s . h"



std ;

arbolRangos : : arbolRangos ( ) { raiz

=

0;

}

arbolRangos : : ~ arbolRangos ( ) { borrarNodo ( r a i z ) ; }

void if else if

int int

arbolRangos : : i n s e r t a r ( pair < (r . first

==

r . second )

insertar ( r . first , (r . first

<

> &r ) {

ambos ) ; r . second ) {

insertar ( r . first ,

primero ) ;

i n s e r t a r ( r . second ,

else

,

segundo ) ;

}

{

insertar ( r . first ,

segundo ) ;

i n s e r t a r ( r . second ,

primero ) ;

} }

bool if return true int int ∗ while true if − if − else return − else if − if − else return − else return false

int int

arbolRangos : : l i b r e ( pair < (! raiz )

a

;

,

= min ( r . f i r s t ,

r . second ) ;

b = max ( r . f i r s t ,

r . second ) ;

nodo

aux

=

(

> &r )

const

raiz ;

){

( aux

>c l a v e

( aux

<

a){

>d e r e c h o ) aux

aux

>i n f o

−>d e r e c h o ;

= aux !=

primero ;

}

( aux

( aux

>

b){

>i z q u i e r d o )

>c l a v e

aux

aux

>i n f o

−>i z q u i e r d o

= aux

!=

;

segundo ;

}

;

}

}

void

arbolRangos : : i n s e r t a r (

nodo

∗x

=

−> c o l o r

x

while if if

=

insertarABB ( c ,

int

c ,

posicion

p){

p) ;

negro ;

!= r a i z && x−>p a d r e −> c o l o r == r o j o ) { −>p a d r e == x−>p a d r e −>p a d r e −>i z q u i e r d o ) { n o d o ∗ y = x−>p a d r e −>p a d r e −>d e r e c h o ; ( y−> c o l o r == r o j o ) { x−>p a d r e −> c o l o r = n e g r o ; y−> c o l o r = n e g r o ; x−>p a d r e −>p a d r e −> c o l o r = r o j o ; (x

(x

{

APÉNDICE A. CÓDIGO FUENTE DE LA IMPLEMENTACIÓN

76

−>p a d r e −>p a d r e ;

x = x

else if

}

{

−>p a d r e −>d e r e c h o ) { −>p a d r e ;

( x == x x = x

rota_i ( x) ; }

−>p a d r e −> c o l o r = n e g r o ; −>p a d r e −>p a d r e −> c o l o r = r o t a _ d ( x−>p a d r e −>p a d r e ) ;

x

x

rojo ;

}

else if

}

{

∗ y = x−>p a d r e −>p a d r e −>i z q u i e r d o −> c o l o r == r o j o ) { x−>p a d r e −> c o l o r = n e g r o ; y−> c o l o r = n e g r o ; x−>p a d r e −>p a d r e −> c o l o r = r o j o ; x = x−>p a d r e −>p a d r e ;

nodo

;

(y

else if

}

{

−>p a d r e −>i z q u i e r d o ) { −>p a d r e ;

( x == x x = x

rota_d ( x ) ; }

−>p a d r e −> c o l o r = n e g r o ; −>p a d r e −>p a d r e −> c o l o r = r o t a _ i ( x−>p a d r e −>p a d r e ) ;

x x

rojo ;

} } } raiz

−> c o l o r

=

negro ;

}

void if

a r b o l R a n g o s : : b o r r a r N o d o ( nodo

∗n ) {

(n){

− −

b o r r a r N o d o ( n >i z q u i e r d o ) ;

delete

b o r r a r N o d o ( n >d e r e c h o ) ;

}

n;

}

if

a r b o l R a n g o s : : nodo

new

∗ arbolRangos

: : insertarABB (

(! raiz ){ raiz

=

nodo ( ) ;

−>c l a v e = c ; r a i z −> i n f o = p ;

raiz

}

return

raiz ;

while true if − if − − else ∗ aux

nodo

=

(

(c

raiz ;

){

<

aux

( aux

aux

>c l a v e ) {

>i z q u i e r d o )

= aux

{

>i z q u i e r d o ;

new

−>i z q u i e r d o = nodo ( ) ; −>i z q u i e r d o −>c l a v e = c ; aux−>i z q u i e r d o −> i n f o = p ; aux−>i z q u i e r d o −>p a d r e = aux ; aux−>i z q u i e r d o ; aux aux

} }

return

int

c ,

posicion

p){

A.12. REFERENCIAS (CABECERA)

else if else

77

{

−>d e r e c h o ) −>d e r e c h o ;

( aux

aux

= aux

new

{

−>d e r e c h o = nodo ( ) ; −>d e r e c h o −>c l a v e = c ; aux−>d e r e c h o −> i n f o = p ; aux−>d e r e c h o −>p a d r e = aux ; aux−>d e r e c h o ; aux aux

return

} } } }

void

a r b o l R a n g o s : : r o t a _ i ( nodo

∗y

nodo

if − − if − else if − else −

n >d e r e c h o (y

y

∗n ) {



= n >d e r e c h o ;

−>i z q u i e r d o ; y−>i z q u i e r d o −>p a d r e

= y

>i z q u i e r d o )

>p a d r e

= n;



= n >p a d r e ;

( ! n >p a d r e )

raiz

= n;

{

− −>i z q u i e r d o ) −>i z q u i e r d o = y ;

( n == n >p a d r e

n >p a d r e



n >p a d r e

−>d e r e c h o

= y;

}

−>i z q u i e r d o = − = y;

y

n;

n >p a d r e }

void

a r b o l R a n g o s : : rota_d ( nodo

∗y

nodo

if − − if − else if − else −

n >i z q u i e r d o (y

y

−>d e r e c h o ; y−>d e r e c h o −>p a d r e

= y

>d e r e c h o )

>p a d r e

∗n ) {



= n >i z q u i e r d o ;

= n;



= n >p a d r e ;

( ! n >p a d r e )

raiz

= n;

{

− −>d e r e c h o ) −>d e r e c h o = y ;

( n == n >p a d r e

n >p a d r e



n >p a d r e

−>i z q u i e r d o

= y;

}

−>d e r e c h o − =

y

= n;

n >p a d r e

y;

}

A.12. Referencias (Cabecera) /∗

∗ Fichero : r e f e r e n c i a . h ∗ Autor : P a b l o U r c o l a I r a c h e ∗ Fecha : 2006 ∗ P r o y e c t o : PFC A l g o r i t m o s d e c o m p r e s i ó n d e s e c u e n c i a s b i o l ó g i c a s ∗ y s u a p l i c a c i ó n en á r b o l e s f i l o g é n i c o s ∗ C o n t e n i d o : D e c l a r a c i ó n d e l a c l a s e r e f e r e n c i a p a r a a l m a c e n a r una ∗ L i c e n c i a : Ver f i c h e r o LEEME ∗/

repetición

APÉNDICE A. CÓDIGO FUENTE DE LA IMPLEMENTACIÓN

78

#ifndef #define #include #include #include using namespace struct

REFERENCIA_H_ REFERENCIA_H_



std ;

referencia {

// i n v a r i a n t e :

int int

first

bool

pair<

=

,

original . first



original ,

&&

o r i g i n a l . second



original .

repeticion ;

normal ;

referencia () {};

// PRE :

true

// POST :

true

// T( n ) :

O( 1 )

referencia (

// PRE :

int

// POST :

crea

// T( n ) :

O( 1 )

referencia (

// PRE :

int

a ,

a &o ,

&r ,

( o r i g i n a l ,

repeticion

APÉNDICE A. CÓDIGO FUENTE DE LA IMPLEMENTACIÓN

80

normal

= n;

}

referencia : : referencia ( original

repeticion normal

const

referencia

&R) {

= R. o r i g i n a l ;

= R. r e p e t i c i o n ;

= R. normal ;

}

int int

r e f e r e n c i a : : r e f e r e n c i a ( pair< original

if

=

repeticion

o; =

int int

swap


> &o ,

int int

pair<

,

> &r ,

bool

n){

r ;

( repeticion . f i r s t

normal

,

,

<

original . first )

> >( o r i g i n a l ,

repeticion ) ;

}

bool return

referencia : :

operator

( original

==

==( r e f e r e n c i a

R. o r i g i n a l

&&

&R) {

repeticion

== R . r e p e t i c i o n ) ;

}

bool return

referencia : :

operator

( original

=

e

= AFD . b e g i n ( ) ;

int e

!=

l ){

AFD . e n d ( ) ;

l ){

nuevo ;

nuevo . l o n g i t u d

=

nuevo . p o s i c i o n e s

e

−>p r o f u n d i d a d ; e−>i d s ;

=

s o r t ( nuevo . p o s i c i o n e s . b e g i n ( ) , r e s u l t a d o . push_back ( n u e v o ) ;

nuevo . p o s i c i o n e s . end ( ) ) ;

e++){

A.15. AUTÓMATA

83

} }

return

AFD . c l e a r ( ) ;

}

void

resultado ;

a u t o m a t a : : AFN2AFD ( ) {

arn

ARN;

AFD . c l e a r ( ) ; estadoAFD

nuevo ;

nuevo . i n i c i a l i z a r ( ) ; nuevo . p r o f u n d i d a d

=

0;

n u e v o . i d s . push_back ( 0 ) ; ARN. i n s e r t a r ( 0 ,

nuevo . i d s ) ;

AFD . push_back ( n u e v o ) ;

int int do for int int if int if e

=

0;

insertados

=

1;

{

(

i

=

vector<

0;

>

i

<

4;

trans

( trans . s i z e () destino

( destino

= >

i ++){ t r a n s i c i o n (AFD [ e ] . i d s ,

= ARN. b u s c a r ( t r a n s ) ;

−1) {

!=

// y a

AFD [ e ] . t r a n s i c i o n [ i ]

=

estaba

definido

el

estado

destino ;

setProfundidad ( destino ,

else

i ) ;

1) {

AFD [ e ] . p r o f u n d i d a d

+

1) ;

}

{

nuevo . i n i c i a l i z a r ( ) ; nuevo . i d s

=

trans ;

nuevo . p r o f u n d i d a d

= AFD [ e ] . p r o f u n d i d a d

ARN. i n s e r t a r ( i n s e r t a d o s , AFD [ e ] . t r a n s i c i o n [ i ]

=

+

1;

trans ) ;

insertados ;

AFD . push_back ( n u e v o ) ; i n s e r t a d o s ++; }

else

}

{

AFD [ e ] . t r a n s i c i o n [ i ]

=

− 1;

} }

while e ++;

} }

(e

int int

vector<

>

vector<

for

!=

insertados ) ;

resultado ;

int

( vector<

transicion (

int

int

automata : : t r a n s i c i o n ( v e c t o r < >

∗i

>:: i t e r a t o r ,

s ,

return

>:: i t e r a t o r

resultado . erase ( fin ,

}

void if

= v . begin () ;

i

!=

int

s ){

v . end ( ) ;

i ++)

resultado ) ;

s o r t ( resultado . begin () , vector<

i

> &v ,

r e s u l t a d o . end ( ) ) ;

fin

=

unique ( r e s u l t a d o . begin ( ) ,

r e s u l t a d o . end ( ) ) ;

r e s u l t a d o . end ( ) ) ;

resultado ;

automata : : t r a n s i c i o n (

for int if ( estado (

== i

=

0) { 1;

i

int

&s a l i d a ) {

APÉNDICE A. CÓDIGO FUENTE DE LA IMPLEMENTACIÓN

84

for int if (

i

= N +

1;

i

=

else if if

( e s t a d o >= N+1 &&

}

− 1) {

1 &&

e s t a d o &s a l i d a ) ;

de

bases

usando

una

de

bases

A.17. CODIFICADOR

// T( n ) :

void

unsigned char

b ) { BITS = b ; } ;

0 < b < 32

// POST : // T( n ) :

int

O( n )

setBits (

// PRE :

85

establece

getBits () {

// PRE :

true

// POST : // T( n ) :

el

tamaño

del

buffer

a

b

bits

O( 1 )

return

devuelve

el

BITS ; } ;

numero

de

bits

del

buffer

O( 1 )

private unsigned char :

};

BITS ;

#endif

A.17. Codicador /∗

∗ Fichero : c o d i f i c a d o r . cc ∗ Autor : P a b l o U r c o l a I r a c h e ∗ Fecha : 2006 ∗ P r o y e c t o : PFC A l g o r i t m o s d e c o m p r e s i ó n d e s e c u e n c i a s b i o l ó g i c a s ∗ y s u a p l i c a c i ó n en á r b o l e s f i l o g é n i c o s ∗ C o n t e n i d o : I m p l e m e n t a c i ó n d e l a c l a s e c o d i f i c a d o r q u e comprime c a d e n a s ∗ L i c e n c i a : Ver f i c h e r o LEEME ∗/

#include #include #include #include #include #include void for int if

" c o d i f i c a d o r . h" " adn . h "

" e x c e p c i o n . h"

codificador : : codificar2 ( string

&b a s e s ,

salida . clear () ; (

i

=

0;

( bases [ i ]

i ==

<

bases . length () ;

s a l i d a . push_back ( }

( bases [ i ]

s a l i d a . push_back ( }

( bases [ i ]

s a l i d a . push_back ( }

( bases [ i ]

s a l i d a . push_back (

else throw

) ;

'c ' ){

true false

'g ' ){ ) ;

true true

==

s a l i d a . push_back (

}

) ;

i ++){

) ;

==

s a l i d a . push_back (

else if

false true

==

s a l i d a . push_back (

else if

) ;

> &s a l i d a ) {

'a ' ){

s a l i d a . push_back (

else if

false false

bool

vector<

) ;

' t ' ){ ) ; ) ;

{

} }

e x c e p c i o n ( " El

fichero

contiene

caracteres

no

válidos ") ;

de

adn

APÉNDICE A. CÓDIGO FUENTE DE LA IMPLEMENTACIÓN

86

}

void ∗ unsigned long int unsigned long unsigned long int unsigned long unsigned long bool do

codificador : : codificar ( string

BITS =

&b a s e s ,

bool

vector<

l o g (NSIMBOLOS NSIMBOLOS) ; L,

H,

// numero

bpf ;

total ;

de

MITAD,

vector< i ,

// i n t e r v a l o

ampliaciones

CUARTO,

actual

centrales

que

del

> &s a l i d a ) {

manejamos

intervalo

TRESCUARTOS ;



>

a c u m u l a d a s (NSIMBOLOS NSIMBOLOS,

1) ;

valor ;

paso ;

low_count ;

abortar ;

{

salida . clear () ;

unsigned long unsigned long unsigned int unsigned long unsigned int unsigned long unsigned int

f i l l ( acumuladas . b e g i n ( ) , L =

(

H =

(

a c u m u l a d a s . end ( ) ,

1) ;

) (0) ;

) (1= CUARTO && H < TRESCUARTOS) {

lo

marco

para

tenerlo

en

cuenta

//

intervalo

en

el

medio

luego

b p f ++;

// a c t u a l i z o L =

2

H =

2

∗ ∗

(L (H

el

− −

intervalo

CUARTO) ; CUARTO)

+

1;

}

a c u m u l a d a s [ v a l o r ]++; t o t a l ++; }

if

// e s c r i b i m o s

if

( i

== (H

el

fin

de

fichero

bases . length () ) {



L +

1 <

total ){

if throw true continue

BITS++;

( BITS >=

abortar

32)

=

excepcion (" Buffer

demasiado

pequeño " ) ;

;

;

}

paso

=

(H



L +

H = L +

paso

L = L +

paso

while if

∗ ∗

// Comprobamos

1)

/

si

el

( L >= MITAD

saco

for

los

intervalo

||

( L >= MITAD) {

//

total ;

− 1; ( t o t a l − 1) ; total

// H =

bits

bpf

>

0;

1...

true −− false

en

s a l i d a . push_back ( (;

L =

2

H =

2

else if

∗ ∗

(L (H

el

− −

bueno

o

hay

y

L =

1...

comun ) ;

bpf

)

s a l i d a . push_back (

// a c t u a l i z o

es

H < MITAD) {

) ;

intervalo

MITAD) ; MITAD)

+

1;

}

//

(H < MITAD) {

saco

for

los

bits

s a l i d a . push_back ( (;

bpf

>

0;

bpf

s a l i d a . push_back (

// a c t u a l i z o

} }

L =

2

H =

2

∗ ∗

el

L; H +

1;

// H =

false −− true

en

comun ) ;

)

) ;

intervalo

0...

y

L =

0...

que

ampliarlo

APÉNDICE A. CÓDIGO FUENTE DE LA IMPLEMENTACIÓN

88

while

(

//

//

L >= CUARTO && H < TRESCUARTOS) {

lo

marco

para

tenerlo

en

cuenta

intervalo

en

el

medio

luego

b p f ++;

// a c t u a l i z o L =

2

H =

2

∗ ∗

(L (H

el

− −

intervalo

CUARTO) ; CUARTO)

+

1;

}

while

} }

//

if

( abortar ) ;

L < MITAD && H >= MITAD && ( L < CUARTO

// e s c r i b o

un

valor

dentro

s a l i d a . push_back ( (;

b p f +1 >

bpf

}

{

s a l i d a . push_back ( (;

b p f +1 >

0;

H >= TRESCUARTOS) H]

)

) ;

true −− false ) ;

bpf

)

s a l i d a . push_back (

}

||

[L,

) ;

0;

s a l i d a . push_back (

else for

intervalo

false −− true

( L < CUARTO) {

for

del

) ;

}

A.18. Decodicador (Cabecera) /∗

∗ Fichero : d e c o d i f i c a d o r . h ∗ Autor : P a b l o U r c o l a I r a c h e ∗ Fecha : 2006 ∗ P r o y e c t o : PFC A l g o r i t m o s d e c o m p r e s i ó n d e s e c u e n c i a s ∗ y s u a p l i c a c i ó n en á r b o l e s f i l o g é n i c o s ∗ Contenido : D e c l a r a c i ó n de l a c l a s e d e c o d i f i c a d o r que ∗ b a s e s d e adn ∗ L i c e n c i a : Ver f i c h e r o LEEME ∗/

#ifndef #define #include #include #include using namespace class public void

biológicas descomprime

cadenas

DECODIFICADOR_H DECODIFICADOR_H

" bstream . h"



std ;

decodificador { :

d e c o d i f i c a r ( ibstream

// PRE :

// POST : //

escribe

de

// T( n ) :

void

entrada

en

salida

usando

la

la

string

&s a l i d a ) ;

cadena

de

codificación

caracteres de

interpretada

al

leer

al

leer

rango

O( n )

d e c o d i f i c a r 2 ( ibstream

// PRE :

&e n t r a d a ,

string

&s a l i d a ) ;

true

// POST : //

&e n t r a d a ,

true

escribe

de

// T( n ) :

entrada

O( n )

en

salida

usando

la

dos

cadena bits

por

de

caracteres

base

de

adn

interpretada

de

A.19. DECODIFICADOR

89

#endif };

A.19. Decodicador /∗

∗ Fichero : d e c o d i f i c a d o r . cc ∗ Autor : P a b l o U r c o l a I r a c h e ∗ Fecha : 2006 ∗ P r o y e c t o : PFC A l g o r i t m o s d e c o m p r e s i ó n d e s e c u e n c i a s ∗ y s u a p l i c a c i ó n en á r b o l e s f i l o g é n i c o s ∗ Contenido : D e c l a r a c i ó n de l a c l a s e d e c o d i f i c a d o r que ∗ b a s e s d e adn ∗ L i c e n c i a : Ver f i c h e r o LEEME ∗/

#include #include #include #include #include #include #include #include using namespace void int

biológicas descomprime

" d e c o d i f i c a d o r . h" " adn . h " " bstream . h"



std ;

de co di fi ca do r : : d e c o d i f i c a r 2 ( ibstream N =

&e n t r a d a ,

string

&s a l i d a ) {

entrada . g e t l v ( ) ;

for int if if else

salida . clear () ; (

i

=

0;

i

< N;

i ++){

( entrada . g e t b i t () ) { ( entrada . g e t b i t ( ) )

salida

+=

't ' ;

salida

+=

'g ' ;

else if else

}

{

( entrada . g e t b i t ( ) )

salida

+=

'c ' ;

salida

+=

'a ' ;

} } }

void unsigned long unsigned char long unsigned long unsigned long unsigned long

d e c o d i f i c a d o r : : d e c o d i f i c a r ( ibstream

vector<

>

BITS =

&e n t r a d a ,

string



a c u m u l a d a (NSIMBOLOS NSIMBOLOS,

&s a l i d a ) {

1) ;

entrada . g e t l v ( ) ;

salida . clear () ; L,

L =

0;

H =

(

H,

paso ,

MITAD,

CUARTO,

) (1= 0

// POST :

Devuelve

variable

// T( n ) :

cadena

cadena .

c a l c u l a r T a m a n o ( v e c t o r & r e p e t i c i o n e s ,

// PRE :

para

bits

n)

longitud

// T( n ) :

que

v e c t o r &r e p e t i c i o n e s , n,

O( r e s t o . l e n g t h ( )

0 < x 0

// POST :

componentes

lmin ,

longitudMinima (

// PRE :

//

repeticiones

las

repeticiones

const char int

codificados

// T( n ) :

las

las

&r e s t o ,

0 <

// POST :

//

las

son

n) ;

O( o r i g i n a l . l e n g t h ( ) )

string

//

l ,

True

// T( n ) :

#endif

la

O( 2 ^ n )

quitarPartesReferenciadas ( string

// POST :

};

contenido para

que

v e c t o r &r e p e t i c i o n e s ,

// PRE :

//

k

int

n > 0

el

//

int

el

parámetro

variable

mayor

int

un

O( 2 ^ n )

//

int

es

:

// POST :

int

k) ;

entrada

k > 0

v e c t o r &r e f e r e n c i a s ,

void

int

,

válidos ,

s a c a r R e f e r e n c i a s ( v e c t o r & r e p e t i c i o n e s ,

// PRE :

void

∗ salida

ficheros

O( l o g

de n)

la x

longitud

de

las

representacion

en

longitud

int

l ,

de

APÉNDICE A. CÓDIGO FUENTE DE LA IMPLEMENTACIÓN

92

A.21. Compresor /∗

∗ Fichero : compresor . cc ∗ Autor : P a b l o U r c o l a I r a c h e ∗ Fecha : 2006 ∗ P r o y e c t o : PFC A l g o r i t m o s d e c o m p r e s i ó n d e s e c u e n c i a s ∗ y s u a p l i c a c i ó n en á r b o l e s f i l o g é n i c o s ∗ C o n t e n i d o : C l a s e q u e comprime d e c a d e n a s d e adn ∗ L i c e n c i a : Ver f i c h e r o LEEME ∗/

#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace void

biológicas

" c om p re s o r . h" " automata . h" " r a n g o s . h" " adn . h " " r e f e r e n c i a . h" " bstream . h" " c o d i f i c a d o r . h" " c o l a . h"

" e x c e p c i o n . h"

std ;

compresor : : comprimir (

// l e o

if

la

ifstream

}

throw

entrada

const char

∗ fentrada

,

const char

∗ fsalida

,

int

k){

entrada ( fentrada ) ;

( entrada . f a i l ( ) ) {

string

excepcion ( " Error

al

abrir

el

fichero

de

entrada " ) ;

bases ;

g e t l i n e ( entrada ,

bases ) ;

entrada . c l o s e () ;

// c o n s t r u y o automata

int

// s a c o

las

lmin

el

automata

A( b a s e s . c _ s t r ( ) ,

=

repeticiones

bases . length () ) ;

mas

grandes

v e c t o r

// c o j o

las

referencias

v e c t o r

s o r t ( f i n a l . begin () ,

string

las

que

partes

repes

ordeno

por

tamaño

k) ;

= A. r e p e t i c i o n e s ( lmin ) ;

mas

bases

abarquen

final ,

lmin ,

bases . length () ) ;

f i n a l . end ( ) ,

referenciadas

anterior () ) ;

de

la

cadena

de

adn

y

almaceno

resto ;

q u i t a r P a r t e s R e f e r e n c i a d a s ( bases , escribir ( fsalida , }

las

final ;

saca rRefere ncias ( repes ,

// q u i t o

y

longitudMinima ( bases . l e n g t h ( ) ,

final ,

resto ,

final , lmin ,

resto ) ;

bases . length () ,

k) ;

lo

que

queda

A.21. COMPRESOR

void

93

int

int

c o m p r e s o r : : s a c a r R e f e r e n c i a s ( v e c t o r & r e p e t i c i o n e s , v e c t o r &r e f e r e n c i a s ,

arbolRangos

for if

cola

usados ;

l ,

n){

ordenadas ; ( v e c t o r : : i t e r a t o r i

!= (i

r e p e t i c i o n e s . end ( ) ;

−> p o s i c i o n e s

[0]

=

r e p e t i c i o n e s . begin () ;



0) {

mejor

=

ordenadas . primero ( ) ;

ordenadas . d e s e n c o l a r ( ) ;

if

int int

( mejor . l o n g i t u d

pair<

if

,

>

<

break

l )

;

o r i g i n a l ( p o s i c i o n ( mejor . p o s i c i o n e s [0]

p o s i c i o n ( mejor . p o s i c i o n e s [ 0 ] ,

int

( original . first swap<

if



(2

>

>

for int int int es

(

k =

pair<

,

la

1; >

primera k <

o r i g i n a l . second ) ;

int

∗1.33∗ log ( o r i g i n a l

// o r i g i n a l

( c e i l (1.33

. f i r s t /2)

aparicion

1) ) ) {

del

sufijo k++){

rango ( p o s i c i o n ( mejor . p o s i c i o n e s [ k]



p o s i c i o n ( mejor . p o s i c i o n e s [ k ] ,

n) ) ; rangoInv

int

=

( rango . f i r s t

swap<

// r a n g o

if

∗ l o g ( mejor . l o n g i t u d −l )

+

mejor . p o s i c i o n e s . s i z e ( ) ;

m e j o r . l o n g i t u d +1 , n ) ,

bool if

es

mejor . p o s i c i o n e s [ k ] >

kesima

&&

( ! r e f . solapada () &&

n;

rango . second ) ;

repeticion

ref ( original ,

( ( ( ! r e f . nula ( )

>

rango . second )

>( r a n g o . f i r s t ,

la

referencia

del

rango ,

r e f . normal ) &&

sufijo

! rangoInv ) ; ||

! r e f . normal ) )

usados . l i b r e ( r e f . r e p e t i c i o n ) ) {

usados . i n s e r t a r ( r e f . r e p e t i c i o n ) ; r e f e r e n c i a s . push_back ( r e f ) ; } } } mejor . l o n g i t u d

−−;

ordenadas . e n c o l a r ( mejor ) ; } }

void int int while if while

compresor : : q u i t a r P a r t e s R e f e r e n c i a d a s ( s t r i n g

v e c t o r &r e p e t i c i o n e s , ref

i

=

=

string

&o r i g i n a l ,

&r e s t o ) {

0;

0;

( i

<

( ref

o r i g i n a l . length () ){

<

repeticiones . size () ){

( i

resto

< +=

repeticiones [ ref ] . repeticion . f i r s t ){ original [ i ];

i ++; } i

=

r e p e t i c i o n e s [ r e f ] . r e p e t i c i o n . second

r e f ++;

else

}

{

resto i ++;

+=

l o n g i t u d +1 , n ) ,

o r i g i n a l . second )

>( o r i g i n a l . f i r s t ,

mejor . l o n g i t u d 2

− mejor .

n) ) ;

original [ i ];

+

1;

+

APÉNDICE A. CÓDIGO FUENTE DE LA IMPLEMENTACIÓN

94

} } }

void

int true

compresor : : e s c r i b i r ( string

bool

&r e s t o ,

agresivo

obstream

bool

vector< {

n,

v e c t o r &r e p e t i c i o n e s ,

k){

; k) ;

c ; >

restoc ;

c . c odif ica r2 ( resto ,

catch throw try

nombre ,

s a l i d a ( nombre ,

codificador

try

=

const char ∗ int int

lmin ,

restoc ) ;

}

( excepcion

&e ) {

e ;

}

{

c . c o d i f i c a r ( resto ,

catch

restoc ) ;

}

=

}

if

try

false

( excepcion

agresivo

&e ) {

( restoc . size ()

;

>

2

∗ resto

. length ()

||

agresivo

{

c . co dif icar 2 ( resto ,

catch throw

==

false

){

restoc ) ;

}

( excepcion

}

agresivo

=

}

// e s c r i b o

&e ) {

e ;

el

false

;

parametro

k

para

s a l i d a . pu t ( s a l i d a . getK ( ) ,

los

enteros

de

longitud

variable

8) ;

s a l i d a . putlv ( lmin ) ; s a l i d a . putlv ( r e p e t i c i o n e s . s i z e () ) ;

int int for

// e s c r i b o

las

anterior anteriorR

referencias =

0;

=

0;

( v e c t o r : : i t e r a t o r r

!=

r e p e t i c i o n e s . end ( ) ;

r

=

r e p e t i c i o n e s . begin () ;

r ++){

−> o r i g i n a l . f i r s t − r −> r e p e t i c i o n . f i r s t − −> r e p e t i c i o n . f i r s t − a n t e r i o r R ) ; s a l i d a . p u t l v ( r −>l o n g i t u d ( ) − l m i n ) ; s a l i d a . p u t b i t ( r −>n o r m a l ) ; a n t e r i o r = r −> o r i g i n a l . f i r s t − r −> r e p e t i c i o n . f i r s t ; a n t e r i o r R = r −> r e p e t i c i o n . s e c o n d ; salida . putlvs ( r s a l i d a . putlv ( r

} salida . putbit ( agresivo ) ;

if else

el

// e s c r i b o

las

// e s c r i b o

tamaño

del

// marco buffer

la

del

codificacion codificador

( agresivo )

s a l i d a . putlv ( c . getBits () ) ;

s a l i d a . putlv ( resto . length () ) ;

bases

no

referenciadas

de

referencias

s a l i d a . put ( r e s t o c ) ;

// e s c r i b o

el

numero

salida . close () ; }

int

compresor : : longitudMinima (

int

n,

int

k){

agresiva

anterior ) ;

A.21. COMPRESOR

int int int int int while

derecha

D =

= n

=

=



izquierda

( derecha

+

k) ;

n,

k) ;

1 && M >=

( (M <

0 && D >

izquierda

=

>

1) {

izquierda ) /2;

f u n c i o n ( medio , (M

medio ;

0 && D <

0) ) {

medio ;

= M;

else

}

{

derecha

=

medio ;

D = M; } }

}

return

int double return

derecha ;

int int double double

compresor : : f u n c i o n (

int

factor 2

=

∗x −

}

int

int

int

x,

(k)/

( ceil ( factor

int

n, (k

k){

− 1) ;

∗ log (x)

+

2

∗ f a c t o r ∗ log (n/3)

l ,

n){

int int int for

referencias ;

sacarReferencias ( repeticiones , tamano

=

2

anterior

referencias ,

!=

0;

r e f e r e n c i a s . end ( ) ;

−=2 ∗

n) ;

0;

=

( v e c t o r : : i t e r a t o r r

l ,

∗n ;

=

anteriorR

tamano

r

=

r e f e r e n c i a s . begin () ;

r ++){

−>l o n g i t u d ( ) ; −> o r i g i n a l . f i r s t − r −> r e p e t i c i o n . f i r s t − l o n g B i t s ( r −> r e p e t i c i o n . f i r s t − a n t e r i o r R ) ; ( r −>l o n g i t u d ( ) − l ) ; r

tamano+=l o n g B i t s ( r tamano += tamano += tamano++; anterior anteriorR

return

=

r

=

−> o r i g i n a l . f i r s t − r −> r e p e t i c i o n r −> r e p e t i c i o n . s e c o n d ;

}

int int int

tamano ;

compresor : : l o n g B i t s ( grupos ; l

=

grupos

if return

int

log (x) ;

=

l /3;

( l %3 !=

}

1) ) ;

c o m p r e s o r : : c a l c u l a r T a m a n o ( v e c t o r & r e p e t i c i o n e s ,

v e c t o r

}

+

0)

grupos

g r u p o s ++;

∗4;

x){

. first ;

a n t e r i o r ) +1;

APÉNDICE A. CÓDIGO FUENTE DE LA IMPLEMENTACIÓN

96

A.22. Descompresor (Cabecera) /∗

∗ Fichero : descompresor . h ∗ Autor : P a b l o U r c o l a I r a c h e ∗ Fecha : 2006 ∗ P r o y e c t o : PFC A l g o r i t m o s d e c o m p r e s s i ó n d e s e c u e n c i a s b i o l ó g i c a s ∗ y s u a p l i c a c i ó n en á r b o l a f i l o g é n i c o s ∗ C o n t e n i d o : D e c l a r a c i ó n d e l a c l a s e d e s c o m p r e s o r q u e d e s c o m p r i m e un ∗ d e adn c o m p r i m i d o s ∗ L i c e n c i a : Ver f i c h e r o LEEME ∗/

#ifndef #define class public void

ficheros

DESCOMPRESOR_H DESCOMPRESOR_H

descompresor { : descomprimir (

// PRE :

fentrada

// POST : //

el

fichero

compresión

// T( n ) :

const char

es

el

∗ fentrada

nombre

fsalida

de

un

,

const char

fichero

contiene

la

de

∗fsalida ) ;

adn

secuencia

comprimido biológica

sin

alguna

O( n )

};

#endif

A.23. Descompresor /∗

∗ Fichero : descompresor . cc ∗ Autor : P a b l o U r c o l a I r a c h e ∗ Fecha : 2006 ∗ P r o y e c t o : PFC A l g o r i t m o s d e c o m p r e s i ó n d e s e c u e n c i a s ∗ y s u a p l i c a c i ó n en á r b o l e s f i l o g é n i c o s ∗ Contenido : Implementación de l a c l a s e d e s c o m p r e s o r ∗ L i c e n c i a : Ver f i c h e r o LEEME ∗/

#include #include #include #include #include #include #include #include #include #include #include using namespace void if throw

biológicas

" d e s c o m p r e s o r . h" " bstream . h"

" r e f e r e n c i a . h" " d e c o d i f i c a d o r . h"

" e x c e p c i o n . h"

std ;

descompresor : : descomprimir (

ibstream

entrada ( fentrada ) ;

const char

∗ fentrada

,

const char

( entrada . f a i l ( ) ) { excepcion (" Fallo

}

al

abrir

el

fichero

de

entrada " ) ;

∗ fsalida ){

A.23. DESCOMPRESOR

int

// l e o

el

primer

k =

97

byte

entrada . get (8) ;

e n t r a d a . setK ( k ) ;

int

// l o n g i t u d lmin

int

// l e o

el

int int int int for

entrada . g e t l v ( ) ;

numero

nref

// l e o

minima

=

=

las

de

referencias

entrada . g e t l v ( ) ;

referencias

v e c t o r anterior

=

anteriorR

repeticiones ( nref ) ;

0;

=

0;

leido ; longitud ; ( v e c t o r : : i t e r a t o r r

!=

leido

r e p e t i c i o n e s . end ( ) ;

=

r

=

r e p e t i c i o n e s . begin () ;

r ++){

entrada . g e t l v s ( ) ;

−> r e p e t i c i o n . f i r s t r −> o r i g i n a l . f i r s t = r

longitud

=

=

entrada . g e t l v ()

leido

+

entrada . g e t l v ( )

r

+

+

anteriorR ;

−> r e p e t i c i o n

. first

+

anterior ;

lmin ;

−> o r i g i n a l . s e c o n d = r −> o r i g i n a l . f i r s t + l o n g i t u d − 1 ; −> r e p e t i c i o n . s e c o n d = r −> r e p e t i c i o n . f i r s t + l o n g i t u d − r −>n o r m a l = e n t r a d a . g e t b i t ( ) ; a n t e r i o r = r −> o r i g i n a l . f i r s t − r −> r e p e t i c i o n . f i r s t ; a n t e r i o r R = r −> r e p e t i c i o n . s e c o n d ; r

r

}

// l e o

las

string

bases

no

referenciadas

y

las

decodifico

resto ;

if else

decodificador

d;

( entrada . g e t b i t ( ) )

d . d e c o d i f i c a r ( entrada ,

resto ) ;

d . d e c o d i f i c a r 2 ( entrada ,

resto ) ;

entrada . c l o s e () ;

//

reconstruyo

int int int while if while string i

la

cadena

original

bases ;

=

// i n d i c e

0;

ref

=

0;

pos

=

0;

de

// i n d i c e

la de

cadena resto

( pos

<

resto . length () ){

( ref

<

repeticiones . size () ){

( i

bases

< +=

reconstruida

repeticiones [ ref ] . repeticion . f i r s t ){ r e s t o [ pos ] ;

p o s ++; i ++;

if

}

for int

( r e p e t i c i o n e s [ r e f ] . normal ) ( j

=

bases i

=

repeticiones [ ref ] . original . f irst ;

j

=

r e p e t i c i o n e s [ r e f ] . o r i g i n a l . second ;

repeticiones [ ref ] . original . first ;

+=

j ++)

bases [ j ] ;

bases [ j ] ;

r e p e t i c i o n e s [ r e f ] . r e p e t i c i o n . second

r e f ++; }

=

r e p e t i c i o n e s [ r e f ] . o r i g i n a l . second ;

+=

( j

j

+

1;

j

−−)

1;

APÉNDICE A. CÓDIGO FUENTE DE LA IMPLEMENTACIÓN

98

else

{

bases

+=

r e s t o [ pos ] ;

p o s ++; } }

ofstream salida

salida ( fsalida ) ;

add ( ∗ l f i c h e r o ) ; vbox−>add ( ∗ l t e n t r a d a ) ; vbox−>add ( ∗ l t s a l i d a ) ; vbox−>add ( ∗ l t i e m p o ) ; vbox−>add ( ∗ hbbox ) ; add ( ∗ v b o x ) ;

hbbox vbox

s e t _ t i t l e ( " Resultado

de

la

compresión " ) ;

show_all_children ( ) ; set_default_size (350 ,

300) ;

show ( ) ; }

A.28. Ventana principal (Cabecera) /∗

∗ Fichero : ventanaPrincipal . h ∗ Autor : P a b l o U r c o l a I r a c h e ∗ Fecha : 2006 ∗ P r o y e c t o : PFC A l g o r i t m o s d e c o m p r e s i ó n d e s e c u e n c i a s b i o l ó g i c a s ∗ y s u a p l i c a c i ó n en á r b o l e s f i l o g é n i c o s ∗ Contenido : D e c l a r a c i ó n de l a c l a s e V e n t a n a P r i n c i p a l que c r e a e l ∗ gráfico ∗ L i c e n c i a : Ver f i c h e r o LEEME ∗/

#ifndef #define

VENTANAPRINCIPAL_H VENTANAPRINCIPAL_H

interfaz

A.28. VENTANA PRINCIPAL (CABECERA)

#include #include #include #include #include #include #include #include #include #include using namespace using namespace class public

101



Gtk ; std ;

VentanaPrincipal

public

:

:

Window

{

VentanaPrincipal () ;

private void void

~VentanaPrincipal () ; :

string

directorio ;

cargarConfiguracion () ; ejecutar () ;

∗ b o t o n P r o c e s a r , ∗ botonAyuda , ∗ b o t o n S a l i r ∗ botonGuardar ; RadioButton ∗ rbComprimir , ∗ r b D e s c o m p r i m i r ;

Button

void void bool void void enum

modoDescomprimir ( ) ; accionComprimir ;

∗ rbmio

usarDnax ( ) ; algoritmo

metodo ;

∗ fsAbrir

void void

Entry

Image

#endif

∗ fsGuardar ;

fsAbrirCANCEL ( ) ; abrir () ; fs Gua rda rOK ( ) ; fsGuardarCANCEL ( ) ; guardar ( ) ;

∗ dialogoAyuda ;

ayudar ( ) ;

∗ etiquetaEntrada

,

∗ etiquetaSalida

etiquetas () ;

∗ entryEntrada

,

∗ entrySalida

modificar () ;

SpinButton

};

,

fsAbrirOK ( ) ;

Dialog

Label

DNAX} ;

botones ( ) ;

FileSelection

void

{MIO ,

nombres [ 2 ] ;

algoritmo

void

∗ rbdnax ;

,

usarMio ( ) ;

string

void void void void void void

∗ botonAbrir

modoComprimir ( ) ;

RadioButton

void

,

∗ parametro ;

entries () ;

∗ dibujo

;

;

,

∗ etiquetaParametro ;

,

APÉNDICE A. CÓDIGO FUENTE DE LA IMPLEMENTACIÓN

102

A.29. Ventana principal /∗

∗ Fichero : v e n t a n a P r i n c i p a l . cc ∗ Autor : P a b l o U r c o l a I r a c h e ∗ Fecha : 2006 ∗ P r o y e c t o : PFC A l g o r i t m o s d e c o m p r e s i ó n d e s e c u e n c i a s b i o l ó g i c a s ∗ y s u a p l i c a c i ó n en á r b o l e s f i l o g é n i c o s ∗ Contenido : Implementación de l a c l a s e V e n t a n a P r i n c i p a l que c r e a ∗ gráfico ∗ L i c e n c i a : Ver f i c h e r o LEEME ∗/

#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace using namespace using namespace

el

interfaz

" v e n t a n a P r i n c i p a l . h"

" c om p re s o r . h" " f p . h"

" d e s c o m p r e s o r . h" " e x c e p c i o n . h"

< s t d i o . h> " d i a l o g o . h"



Gtk ; SigC ; std ;

VentanaPrincipal : : VentanaPrincipal () { n o m b r e s [ MIO ] n o m b r e s [DNAX]

=

"Mi

=

Algoritmo " ;

"DnaX" ;

s e t _ t i t l e ( " Compresor / D e s c o m p r e s o r

de

secuencias

biológicas ") ;

set_default_icon_from_file ( " imagenes / icono . g i f " ) ;

cargarConfiguracion () ; botones ( ) ; etiquetas () ; entries () ;

VBox

∗ vbox = ∗ hbox

HBox

dibujo

=

new new new

VBox ( ) ;

=

HBox ( ) ;

I m a g e ( " i m a g e n e s / adn . j p g " ) ;

−>add ( ∗ d i b u j o ) ;

hbox

VBox

∗ vbox2

=

AspectFrame

new

new

VBox ( ) ;

∗ frame2

=

AspectFrame ( " A l g o r i t m o " ,

ALIGN_CENTER,

A.29. VENTANA PRINCIPAL

new

ALIGN_CENTER,

1.0 ,

103

true

) ;

∗ hbox3 = HBox ( ) ; hbox3−>p a c k _ s t a r t ( ∗ r b m i o ) ; hbox3−>p a c k _ s t a r t ( ∗ r b d n a x ) ; f r a m e 2 −>add ( ∗ h b o x 3 ) ; vbox2 −>add ( ∗ f r a m e 2 ) ; AspectFrame ∗ f r a m e = AspectFrame ( " A c c i ó n " , HBox

new

ALIGN_CENTER,

new true

1.0 ,

ALIGN_CENTER,

) ;

∗ hbox2 = HBox ( ) ; −>p a c k _ s t a r t ( ∗ r b C o m p r i m i r ) ; hbox2−>p a c k _ s t a r t ( ∗ r b D e s c o m p r i m i r ) ; f r a m e −>add ( ∗ h b o x 2 ) ; vbox2 −>add ( ∗ f r a m e ) ; HBox

hbox2

new

∗ layout = Table ( 3 , 3) ; −>a t t a c h ( ∗ e t i q u e t a E n t r a d a , 0 , 1 , 0 , 1 ) ; l a y o u t −>a t t a c h ( ∗ e t i q u e t a S a l i d a , 0 , 1 , 1 , 2) ; l a y o u t −>a t t a c h ( ∗ e t i q u e t a P a r a m e t r o , 0 , 1 , 2 , 3) ; l a y o u t −>a t t a c h ( ∗ e n t r y E n t r a d a , 1 , 2 , 0 , 1) ; l a y o u t −>a t t a c h ( ∗ e n t r y S a l i d a , 1 , 2 , 1 , 2) ; l a y o u t −>a t t a c h ( ∗ p a r a m e t r o , 1 , 2 , 2 , 3 , EXPAND) ; l a y o u t −>a t t a c h ( ∗ b o t o n A b r i r , 2 , 3 , 0 , 1 , FILL | EXPAND,

Table

layout

EXPAND, layout

5) ;

−>a t t a c h ( ∗ b o t o n G u a r d a r

EXPAND,

,

2,

3,

1,

2,

FILL | EXPAND,

5) ;

−>add ( ∗ l a y o u t ) ; −>add ( ∗ v b o x 2 ) ; vbox−>add ( ∗ hbox ) ; vbox2

hbox

new

∗ hbbox = HButtonBox (BUTTONBOX_SPREAD, −>add ( ∗ botonAy uda ) ; hbbox−>add ( ∗ b o t o n P r o c e s a r ) ; hbbox−>add ( ∗ b o t o n S a l i r ) ; vbox−>add ( ∗ hbbox ) ; add ( ∗ v b o x ) ; HButtonBox hbbox

accionComprimir

=

true

;

show_all_children ( ) ;

set_default_size (500 ,

250) ;

}

VentanaPrincipal : : ~ VentanaPrincipal () {

delete delete delete delete delete delete delete delete delete delete delete delete delete delete delete delete

// b o t o n e s botonProcesar ; botonSalir ; botonAy uda ; botonAbrir ; botonGuardar ; rbComprimir ; rbDescomprimir ; rbmio ; rbdnax ;

// e t i q u e t a s etiquetaEntrada ; etiquetaSalida ; etiquetaParametro ;

// e n t r i e s entryEntrada ; entrySalida ; parametro ;

// d i b u j o s dibujo ;

0) ;

APÉNDICE A. CÓDIGO FUENTE DE LA IMPLEMENTACIÓN

104

}

void

new

VentanaPrincipal : : botones ( ) {

botonProcesar

botonProcesar

=

B u t t o n ( S t o c k : : EXECUTE) ;

−>s i g n a l _ c l i c k e d

new

( ) . c o n n e c t ( mem_fun (



&V e n t a n a P r i n c i p a l : : e j e c u t a r ) ) ; botonSalir

=

new

&V e n t a n a P r i n c i p a l : : h i d e ) ) ; botonA yuda botonAyuda

=

new

( ) . c o n n e c t ( mem_fun (

botonAbrir

=

this



this

B u t t o n ( S t o c k : : OPEN) ;

−>s i g n a l _ c l i c k e d

new

( ) . c o n n e c t ( mem_fun (

&V e n t a n a P r i n c i p a l : : a b r i r ) ) ; botonGuardar botonGuardar

=

B u t t o n ( S t o c k : : SAVE) ;

−>s i g n a l _ c l i c k e d

new

this



B u t t o n ( S t o c k : : HELP) ;

−>s i g n a l _ c l i c k e d

&V e n t a n a P r i n c i p a l : : a y u d a r ) ) ;

botonAbrir

( ) . c o n n e c t ( mem_fun (



&V e n t a n a P r i n c i p a l : : g u a r d a r ) ) ; rbComprimir

=

( ) . c o n n e c t ( mem_fun (



&V e n t a n a P r i n c i p a l : : modoComprimir ) ) ;

new

R a d i o B u t t o n : : Group rbDescomprimir rbDescomprimir

=

rbmio

=

grupo

=

rbComprimir

R a d i o B u t t o n ( "Mi

this

( ) . c o n n e c t ( mem_fun (

new

rbdnax

;



this

Algoritmo " ) ;

( ) . c o n n e c t ( mem_fun (



&V e n t a n a P r i n c i p a l : : u s a r M i o ) ) ; R a d i o B u t t o n : : Group

,

" Descomprimir " ) ;

= MIO ;

=

,

;

−>s i g n a l _ c l i c k e d

rbdnax

,

−>g e t _ g r o u p ( )

RadioButton ( grupo ,

−>s i g n a l _ c l i c k e d

−>s e t _ a c t i v e ( )

metodo rbmio

new

,

this

&V e n t a n a P r i n c i p a l : : m o d o D e s c o m p r i m i r ) ) ;

rbmio

,

RadioButton ( " Comprimir " ) ;

−>s e t _ a c t i v e ( ) ; r b C o m p r i m i r −>s i g n a l _ c l i c k e d rbComprimir

grupo2

=

rbmio

−>s i g n a l _ c l i c k e d

this

,

−>g e t _ g r o u p ( )

RadioButton ( grupo2 ,

"DnaX" ) ;

( ) . c o n n e c t ( mem_fun (

&V e n t a n a P r i n c i p a l : : u s a r D n a x ) ) ;



this

;

,

}

void

etiquetaSalida

= =

etiquetaParametro }

void

new new new

VentanaPrincipal : : etiquetas () {

etiquetaEntrada

Label ( " Fichero

Label ( " Fichero

=

new

de de

entryEntrada

Label ( " Parámetro " ) ;

=

Entry ( ) ;

−>s i g n a l _ c h a n g e d ( )

new new

. c o n n e c t ( mem_fun (

&V e n t a n a P r i n c i p a l : : m o d i f i c a r ) ) ; entrySalida parametro

=

=

Entry ( ) ;

SpinButton ( ) ;

−> s e t _ d i g i t s ( 0 ) ; −>s e t _ n u m e r i c ( ) ; p a r a m e t r o −>s e t _ r a n g e ( 1 , 2 0 ) ; p a r a m e t r o −>s e t _ v a l u e ( 4 ) ; p a r a m e t r o −>s e t _ i n c r e m e n t s ( 1 ,

parametro

parametro

5) ;

}

void if

entrada " ) ; salida ") ;

VentanaPrincipal : : e n t r i e s () {

entryEntrada

VentanaPrincipal : : modificar () {

string

,

B u t t o n ( S t o c k : : CLOSE) ;

−>s e t _ f l a g s (CAN_DEFAULT) ; b o t o n S a l i r −>s i g n a l _ c l i c k e d ( ) . c o n n e c t ( mem_fun ( ∗ botonSalir

this

extension ;

( accionComprimir )



this

,

,

A.29. VENTANA PRINCIPAL

else switch case break case break extension

=

105

" . cca " ;

{

( metodo ) {

MIO :

extension

=

" . dca " ;

=

" . y" ;

;

DNAX:

extension ;

}

}

if else

entrySalida

−>g e t _ t e x t ( ) . empty ( ) ) −>s e t _ t e x t ( e n t r y E n t r a d a −>g e t _ t e x t ( )

entrySalida

−>s e t _ t e x t ( " " ) ;

( ! entryEntrada

+

extension ) ;

}

void if

VentanaPrincipal : : ejecutar () {

FILE

∗f

=

fopen ( entryEntrada

(! f ){

∗ aviso

MessageDialog

true − delete return " El

aviso

fichero ,

de

=

−>g e t _ t e x t ( )

new

entrada

MESSAGE_ERROR,

. c_str ( ) ,

MessageDialog ( no

se

puede



this

abrir

"r") ;

,

para

lectura " ,

BUTTONS_OK) ;

>r u n ( ) ; aviso ;

;

}

fclose ( f ) ;

if

f

=

fopen ( entrySalida

−>g e t _ t e x t ( )

(! f ){

∗ aviso

MessageDialog

true − delete return " El

aviso

fichero ,

de

=

new

salida

MESSAGE_ERROR,

. c_str ( ) ,

MessageDialog (

no

se

puede

"w" ) ;



this

abrir

,

para

escritura " ,

BUTTONS_OK) ;

>r u n ( ) ; aviso ;

;

}

switch case if

fclose ( f ) ; ( metodo ) { MIO : ( accionComprimir ) {

try

compresor

c ;

{

time_t

inicio

=

time ( 0 ) ;

−>g e t _ t e x t ( ) −>g e t _ t e x t ( ) . c _ s t r ( ) , ( p a r a m e t r o −>g e t _ v a l u e ( ) ) ) ;

c . comprimir ( entryEntrada

int

. c_str ( ) ,

entrySalida

time_t FILE FILE

fin

∗ fe ∗ fs

=

time ( 0 ) ;

fopen ( entryEntrada

=

fopen ( entrySalida

f s e e k ( fe ,

0,

SEEK_END) ;

fseek ( fs ,

0,

SEEK_END) ;

dialogoInfo

new

∗ i n f o= dialogoInfo ( −>g e t _ t e x t ( ) , n o m b r e s [ metodo ]

entryEntrada f t e l l ( fe ) ,

f t e l l ( fs ) ,

// i n f o −>r u n ( ) ; fclose ( fe ) ; fclose ( fs ) ;

catch

−>g e t _ t e x t ( ) . c _ s t r ( ) −>g e t _ t e x t ( ) . c _ s t r ( ) ,

=

}

( excepcion

&e ) {

difftime ( fin ,

,

inicio )) ;

,

"r") ; "r") ;

APÉNDICE A. CÓDIGO FUENTE DE LA IMPLEMENTACIÓN

106

true − delete catch

∗ aviso

MessageDialog

aviso

,

=

new

MESSAGE_ERROR,

MessageDialog (



this



this

BUTTONS_OK) ;

,

e . info () ,

>r u n ( ) ; aviso ;

}

( . . . ) {

∗ aviso

MessageDialog " Algo

ha

salido

=

new

mal " ,

BUTTONS_OK) ;

delete aviso

}

else

−>r u n ( )

true

MessageDialog ( ,

,

MESSAGE_ERROR,

;

aviso ;

}

{

descompresor

d;

Glib : : u s t r i n g

try

MessageType

mensaje ( " Descompresión

tipo

finalizada

con

éxito ") ;

= MESSAGE_INFO ;

{

d . descomprimir ( entryEntrada entrySalida

catch

−>g e t _ t e x t ( )

−>g e t _ t e x t ( )

. c_str ( ) ,

. c_str ( ) ) ;

}

( excepcion

mensaje tipo

catch

=

&e ) {

e . info () ;

= MESSAGE_ERROR;

}

( . . . ) {

mensaje tipo

=

" Algo

ha

salido

}

true − delete break case if

∗ aviso

MessageDialog

aviso

mal " ;

= MESSAGE_ERROR;

,

tipo ,

=

new

MessageDialog (

BUTTONS_OK) ;



this

,

mensaje ,

>r u n ( ) ; aviso ;

}

;

DNAX:

( accionComprimir ) {

string

args [ 4 ] ;

args [ 0 ]

=

" dnaX " ;

−O" ;

args [ 1 ]

=

args [ 2 ]

=

entrySalida

char ∗ for int

=

entryEntrada

args [ 3 ]

"

−>g e t _ t e x t ( ) ; −>g e t _ t e x t ( )

;

argsc [ 4 ] ;

(

char

i

=

0;

argsc [ i ]

=

(

try

time_t

inicio

i

=

<

4;

i ++)

∗) args

[ i ] . c_str ( ) ;

time ( 0 ) ;

{

dnax ( 4 , time_t

FILE FILE

argsc ) ; fin

∗ fe ∗ fs

=

time ( 0 ) ;

−>g e t _ t e x t ( ) . c _ s t r ( ) −>g e t _ t e x t ( ) . c _ s t r ( ) ,

=

fopen ( entryEntrada

=

fopen ( entrySalida

f s e e k ( fe ,

0,

SEEK_END) ;

fseek ( fs ,

0,

SEEK_END) ;

dialogoInfo

new

∗ i n f o= dialogoInfo ( −>g e t _ t e x t ( ) , n o m b r e s [ metodo ]

entryEntrada f t e l l ( fe ) ,

ft ell ( fs ) ,

difftime ( fin ,

,

"r") ; "r") ;

,

inicio )) ;

// i n f o −>r u n ( ) ; fclose ( fe ) ; fclose ( fs ) ;

catch

}

( excepcion

MessageDialog

&e ) {

∗ aviso

=

new

MessageDialog (



this

,

e . info () ,

A.29. VENTANA PRINCIPAL

true − delete catch aviso

,

107

MESSAGE_ERROR,

BUTTONS_OK) ;

>r u n ( ) ; aviso ;

}

( . . . ) {

∗ aviso

MessageDialog " Algo

ha

salido

=

new

mal " ,

BUTTONS_OK) ;

delete aviso

}

else

−>r u n ( )

true

MessageDialog ( ,



this

,

MESSAGE_ERROR,

;

aviso ;

}

{

string

args [ 3 ] ;

args [ 0 ]

=

" dnaX " ;

−d " ;

args [ 1 ]

=

"

char ∗ for int

=

entryEntrada

args [ 2 ]

−>g e t _ t e x t ( )

;

argsc [ 3 ] ;

(

char

i

=

0;

argsc [ i ]

=

(

Glib : : u s t r i n g

try

MessageType

i

<

3;

i ++)

∗) args

[ i ] . c_str ( ) ;

mensaje ( " Descompresión

tipo

finalizada

con

éxito ") ;

= MESSAGE_INFO ;

{

dnax ( 3 ,

catch

argsc ) ;

}

( excepcion

mensaje tipo

catch

=

&e ) {

e . info () ;

= MESSAGE_ERROR;

}

( . . . ) {

mensaje tipo

=

" Algo

ha

salido

}

true − delete break default

MessageDialog

aviso

mal " ;

= MESSAGE_ERROR;

,

∗ aviso

tipo ,

=

new

MessageDialog (



BUTTONS_OK) ;

this

,

mensaje ,

>r u n ( ) ; aviso ;

}

; :

MessageDialog " Algoritmo

delete aviso

}

−>r u n ( )

∗ aviso no

=

new

true

MessageDialog (

reconocido " ,

;

,



this

,

MESSAGE_ERROR,

BUTTONS_OK) ;

aviso ;

}

void

new

VentanaPrincipal : : abrir () {

fsAbrir

=

false

FileSelection (" Elija

el

fichero

−>s e t _ s e l e c t _ m u l t i p l e ( ) ; f s A b r i r −>g e t _ o k _ b u t t o n ( )−> s i g n a l _ c l i c k e d

fsAbrir

de

( ) . c o n n e c t ( mem_fun (

&V e n t a n a P r i n c i p a l : : f s A b r i r O K ) ) ; fsAbrir

−>g e t _ c a n c e l _ b u t t o n ( )−> s i g n a l _ c l i c k e d

delete

fsAbrir

}

void

−>s e t _ f i l e n a m e ( d i r e c t o r i o ) ; −>r u n ( ) ; fsAbrir ;

V e n t a n a P r i n c i p a l : : fsAbrirOK ( ) {

entryEntrada



this

( ) . c o n n e c t ( mem_fun (

&V e n t a n a P r i n c i p a l : : fsAbrirCANCEL ) ) ; fsAbrir

entrada " ) ;

−>s e t _ t e x t ( f s A b r i r −>g e t _ f i l e n a m e ( ) ) ;



,

this

,

APÉNDICE A. CÓDIGO FUENTE DE LA IMPLEMENTACIÓN

108

fsAbrir

−>h i d e ( )

;

}

void

V e n t a n a P r i n c i p a l : : fsAbrirCANCEL ( ) {

fsAbrir

−>h i d e ( )

;

}

void

new

VentanaPrincipal : : guardar ( ) {

fsGuardar

=

false

FileSelection (" Elija

el

fichero

−>s e t _ s e l e c t _ m u l t i p l e ( ) ; f s G u a r d a r −>g e t _ o k _ b u t t o n ( )−> s i g n a l _ c l i c k e d fsGuardar

de

salida ") ;

( ) . c o n n e c t ( mem_fun (



&V e n t a n a P r i n c i p a l : : fsG uar dar OK ) ) ; fsGuardar

−>g e t _ c a n c e l _ b u t t o n ( )−> s i g n a l _ c l i c k e d

( ) . c o n n e c t ( mem_fun (

&V e n t a n a P r i n c i p a l : : fsGuardarCANCEL ) ) ; fsGuardar

delete

fsGuardar

}

void

−>s e t _ f i l e n a m e ( d i r e c t o r i o ) ; −>r u n ( ) ;

fsGuardar ;

V e n t a n a P r i n c i p a l : : f sGu ard arO K ( ) {

−>s e t _ t e x t ( f s G u a r d a r −>g e t _ f i l e n a m e ( ) ) ; −>h i d e ( ) ;

entrySalida

fsGuardar }

void

V e n t a n a P r i n c i p a l : : fsGuardarCANCEL ( ) {

fsGuardar

−>h i d e ( )

;

}

void

true

V e n t a n a P r i n c i p a l : : modoComprimir ( ) {

accionComprimir

=

;

true

−> s e t _ s e n s i t i v e ( metodo == MIO) ; e n t r y S a l i d a −> s e t _ s e n s i t i v e ( ) ; e n t r y S a l i d a −>s e t _ t e x t ( e n t r y E n t r a d a −>g e t _ t e x t ( )

parametro

+

" . cca " ) ;

+

extension ) ;

}

void

false

V e n t a n a P r i n c i p a l : : modoDescomprimir ( ) {

accionComprimir

=

;

false

−> s e t _ s e n s i t i v e ( ) ; e n t r y S a l i d a −> s e t _ s e n s i t i v e ( metodo

parametro

switch case break case break string

== MIO) ;

extension ;

( metodo ) {

MIO :

extension

=

" . dca " ;

=

" . y" ;

;

DNAX:

extension ;

}

entrySalida

−>s e t _ t e x t ( e n t r y E n t r a d a −>g e t _ t e x t ( )

}

void

new

V e n t a n a P r i n c i p a l : : ayudar ( ) {

true

∗ dialogoAyuda = D i a l o g ( " A c e r c a d e CODESEBI" , ) ; −>s e t _ h a s _ s e p a r a t o r ( ) ; d i a l o g o A y u d a −>g e t _ a c t i o n _ a r e a ( )−>s e t _ l a y o u t (BUTTONBOX_SPREAD) ; d i a l o g o A y u d a −>a d d _ b u t t o n ( S t o c k : : OK, RESPONSE_NONE) ; Dialog

dialogoAyuda

Image

i m a g e n ( " i m a g e n e s / adn . j p g " ) ;

Label

a p l i c a c i o n ( "CODESEBI

Label

a u t o r ( " Pablo

Label

f e c h a ( " 2006 " ) ;

dialogoAyuda dialogoAyuda

Urcola

v1 . 0 " ) ; Irache ") ;

−>g e t _ v b o x ( )−>add ( i m a g e n ) ; −>g e t _ v b o x ( )−>add ( a p l i c a c i o n ) ;

this ∗

,

this

,

A.30. PROGRAMA PRINCIPAL

109

−>g e t _ v b o x ( )−>add ( a u t o r ) ; −>g e t _ v b o x ( )−>add ( f e c h a ) ; d i a l o g o A y u d a −>s h o w _ a l l _ c h i l d r e n ( ) ; d i a l o g o A y u d a −>s e t _ d e f a u l t _ s i z e ( 2 5 0 , 1 0 0 ) ; d i a l o g o A y u d a −>r u n ( ) ; dialogoAyuda dialogoAyuda

}

delete

void

dialogoAyuda ;

V e n t a n a P r i n c i p a l : : usarMio ( ) {

metodo

= MIO ;

parametro

if

true

−> s e t _ s e n s i t i v e ( a c c i o n C o m p r i m i r ) ; −> s e t _ s e n s i t i v e ( ) ;

entryEntrada

( ! accionComprimir ) entrySalida

−>s e t _ t e x t ( e n t r y E n t r a d a −>g e t _ t e x t ( )

+

" . dca " ) ;

+

" . y" ) ;

}

void

V e n t a n a P r i n c i p a l : : usarDnax ( ) {

metodo

if

= DNAX;

parametro

−> s e t _ s e n s i t i v e (

( ! accionComprimir ) { entrySalida entrySalida

false false ) ;

−> s e t _ s e n s i t i v e ( ) ; −>s e t _ t e x t ( e n t r y E n t r a d a −>g e t _ t e x t ( )

} }

void if

VentanaPrincipal : : cargarConfiguracion () {

ifstream

conf ( " codesebi . conf " ) ;

( ! conf . f a i l () ) { string

linea ;

g e t l i n e ( conf ,

int if else

linea ) ;

conf . c l o s e () ; igual

=

l i n e a . f i n d ( '= ' ) ;

( igual

<

linea . size ()

&&

directorio . assign ( linea ,

directorio

else

=

igual

>=

i g u a l +1 ,

0) linea . size () ) ;

".";

}

directorio

=

".";

}

A.30. Programa principal #include #include using namespace int int

" v e n t a n a P r i n c i p a l . h"

main

Main

(

Gtk ;

argc ,

k i t ( argc ,

char

VentanaPrincipal

ventana ;

Main : : r u n ( v e n t a n a ) ;

}

return

0;

∗ argv

argv ) ;

[]) {

APÉNDICE A. CÓDIGO FUENTE DE LA IMPLEMENTACIÓN

110

A.31. Makele SHELL =

OBJ =

/ bin / sh

bstream . o

referencia .o

compresor . o

cola . o

excepcion . o

dialogo . o

OBJDNAX = costi .o

stampa . o

APP =

# #

CC =

basic00 . o

expand

adn . o

decodificador . o

−2. o

bit_base . o

rangos . o

bitio .o

fingerprint .o

arn . o

automata . o

ventanaPrincipal . o

fp . o

cerca_primo . o hash . o

main . o

coder . o

huffman . o

model

statistiche .o

codesebi

g++

−g −O3 − f o m i t −f r a m e − p o i n t e r −W −W a l l −DDEBUG=0 −O3 += −g −O3 − f o m i t −f r a m e − p o i n t e r −DDEBUG=0 −W −W a l l += $ ( s h e l l pkg− c o n f i g gtkmm − 2 . 4 −− c f l a g s ) += $ ( s h e l l pkg− c o n f i g s i g c ++−2.0 −− c f l a g s )

CFLAGS +=

CFLAGS +=

CXXFLAGS CXXFLAGS CXXFLAGS

−c o n f i g −c o n f i g

LIBS += $ ( s h e l l

pkg

LIBS += $ ( s h e l l

pkg

all :

gtkmm

− 2 . 4 −− l i b s ) −2.0 −− l i b s )

s i g c ++

$ (APP)

$ (APP) : $ (CXX)

$ ( OBJ)

$ (OBJDNAX)

$ (OPCIONES)

−o

$ (APP)

$ ( OBJ)

clean : $ (RM)

$ (APP)

$ ( OBJ)

$ (OBJDNAX)

$ (OBJDNAX)

$ ( LIBS )

codificador . o

\

descompresor . o

codici . o

−2. o

comp

\

−2. o \

r i c e r c a . o\

Get in touch

Social

© Copyright 2013 - 2024 MYDOKUMENT.COM - All rights reserved.