1 Matrices, Listas y Dataframes

1.1 Matrices

Al igual que los vectores, las matrices son una estructura de datos del mismo tipo, con la diferencia de que en las matrices los datos están estructurados en dos dimensiones: Filas y Columnas.
Es posible crear matrices usando el comando matrix().

##      [,1] [,2] [,3] [,4] [,5]
## [1,]    1    2    3    4    5
## [2,]    6    7    8    9   10
## [3,]   11   12   13   14   15

También es posible tener estructuras de datos de más de dos dimensiones, para ello usamos el comando array().
En este tutorial veremos poco sobre matrices, pero es importante que algunos de los comandos que veremos para los DataFrames son también aplicables para las matrices. Puedes consultar más al respecto en los siguientes vinculos: 1, 2.


1.2 Listas

  • Las listas funcionan como contenedores de objetos.
  • Al contrario de los vectores, las listas pueden contener objetos de diferentes tipos (pueden ser heterogéneas).
  • Las listas pueden incluso contener otras listas.
  • Se crean con el comando list().
## [[1]]
## [1] "Arc"
## 
## [[2]]
## [1] 1000
## 
## [[3]]
## [1] TRUE
## 
## [[4]]
## [1] "AGTTTG"

Los elementos de las listas pueden ser nombrados:

## $gen
## [1] "Arc"
## 
## $num_bases
## [1] 1000
## 
## $expresado
## [1] TRUE
## 
## $sec_inicial
## [1] "AGTTTG"

Los elementos dentro de una lista se obtienen usando dos corchetes cuadrados anidados: [[ i ]], donde \(i\) es el número de elemento deseado.

Ejercicio:

  • ¿Cuál es la diferencia entre los siguientes dos comandos?
  • ¿Qué tipo de dato es cada uno? Usa la función class() para veriguarlo.

Si se han asignado nombres a la lista, es posible usarlos para extraer los datos:

## [1] "gen"         "num_bases"   "expresado"   "sec_inicial"

Por ejemplo, nos interesa extraer de la lista los datos correspondientes a la variable gen:

## [1] "Arc"
## [1] "Arc"
## [1] "Arc"

Las tres formas anteriores son válidas y permiten obtener el mismo resultado.


1.3 DataFrames

  • Los DataFrames son uno de los objetos más usados en R.
  • Podemos entender a un Dataframe como una tabla de datos de un determinado número de filas y columnas. Sin embargo, a diferencia de las matrices, los DataFrames pueden tener datos heterogéneos.

Comencémos creando un DataFrame usando el comando data.frame():

##   Num_de_gen Nombre_gen Expresado Num_pbases
## 1          1        Arc      TRUE        100
## 2          2       cdk2     FALSE        200
## 3          3    miR-1–1     FALSE         60
Mi primer DataFrame
Num_de_gen Nombre_gen Expresado Num_pbases
1 Arc TRUE 100
2 cdk2 FALSE 200
3 miR-1–1 FALSE 60

Ya has creado tu primer DataFrame, sin embargo, vamos ahora un conjunto de datos proveniente de un archivo.


1.3.1 Ejercicio con Dataframes

Vamos a utilizar los datos proporcionados por el Dr. Cei Abreu en la página Prácticas de Bioinformática: R. En particular, vamos a utilizar el archivo ensembl_info.tab.

La información en la tabla que acaban de descargar fue obtenida de la página de Ensembl, que reune una gran cantidad de información sobre genomas completamente secuenciados de vertebrados y algunos organismos modelo.

Citado la página http://datos.langebio.cinvestav.mx/~cei/cursos/BP_2018/

Una vez descargado el archivo, guárdalo en tu directorio de trabajo (puedes usar el comando getwd() (getWorkDirectory) para guardar ahí el archivo).

Vamos a abrir el archivo utilizando el comando read.table().
Nota: ¿Qué indican los parámetros header= y row.names=?

Una vez cargado, podemos comprobar que el objeto es un DataFrame:

## [1] "data.frame"

Además, podemos usar los comandos head() y tail() para ver las primeras y las últimas filas de la tabla, respectivamente. Tú puedes imprimir toda la tabla en consola, si así lo deseas.

##              coding_genes ncrna_genes pseudo_genes coding_gene_avg_length
## Anole_lizard        18595        7168          157               35332.78
## C_elegans           20362         922         1658                3088.45
## Cat                 19493        1855          542               36566.75
##              ncrna_gene_avg_length pseudo_gene_avg_length
## Anole_lizard              10318.72                 878.24
## C_elegans                   334.04                1389.75
## Cat                         103.16                 792.30
##              coding_trans_avg_length ncrna_trans_avg_length
## Anole_lizard                 2572.26                 548.16
## C_elegans                    1399.78                 255.94
## Cat                          2169.50                 103.16
##              pseudo_trans_avg_length genome_length
## Anole_lizard                  868.57    1799143587
## C_elegans                     855.84     100286401
## Cat                           743.36    2455541136
##                 coding_genes ncrna_genes pseudo_genes
## Tasmanian_devil        18788        1490          178
## Xenopus                18442        1306          173
## Zebrafish              25658        4182          368
##                 coding_gene_avg_length ncrna_gene_avg_length
## Tasmanian_devil               36980.19                119.02
## Xenopus                       22927.14                125.49
## Zebrafish                     31539.44               3518.02
##                 pseudo_gene_avg_length coding_trans_avg_length
## Tasmanian_devil               73189.70                 2062.99
## Xenopus                         936.50                 1957.54
## Zebrafish                      5966.81                 2551.89
##                 ncrna_trans_avg_length pseudo_trans_avg_length
## Tasmanian_devil                 119.02                 2223.13
## Xenopus                         125.49                  923.82
## Zebrafish                       316.81                 1285.90
##                 genome_length
## Tasmanian_devil    3174709637
## Xenopus            1511735326
## Zebrafish          1371719383

¡Ojo!

En Environment puedes comprobar que el objeto ensTab ha sido creado, puedes abrirlo desde RStudio para examinarlo. También puedes usar el comando View(nombre_del_dataFrame).


1.3.2 Inspeccionando el DataFrame

Ejercicio: Usando los comandos indicados, averigua lo siguiente de la tabla ensTab:

  1. ¿Qué dimensiones tiene la tabla? dim()
  2. ¿Qué datos brinda la función str()?
  3. ¿Cuántas especies hay en la tabla? nrow()
  4. ¿Cuántas variables se midieron para cada especie? ncols()
  5. ¿Cuáles son los nombres de dichas variables? colnames()
  6. ¿Cuáles son los nombres de las especies?

Ahora vamos a inspeccionar más a fondo los datos de la tabla. Si queremos obtener un dato particular de la tabla, por ejemplo cuántos genes codificantes tiene el Humano, podemos usar lo siguiente:

## [1] 22097

¿Cuál es la longitud del genoma del Tasmanian_devil?

¡Recuerda!

Primero nombra o indica la fila, y luego la columna, separados por una coma ,.

Ahora queremos saber los datos de las primeras dos columnas (variables coding_genes y ncrna_genes) de las primeras tres especies:

¿Qué pasa si queremos ver la longitud de los genomas de todas las especies?

##  [1] 1799143587  100286401 2455541136 1230258557 3309577922 2670422299
##  [7] 2410976875 2521923936 1105035747 3196760833  143725995  393312790
## [13] 3040677044 2474929062 3096649726 2034575300 2730871774 2299509015
## [19] 2808525991 2073148626 2870184193  461533448 3174709637 1511735326
## [25] 1371719383

Analiza lo siguiente:
¿Qué especie tiene la mayor longitud de genoma? Para ello podemos averiguar qué hace la función which.max().
Tómate un tiempo para analizar los siguientes comandos.

¿Qué ocurrió?

  1. El comando ensTab$genome_length es similar a ejecutar ensTab[ , "genome_length" ] (recuerda que las columnas se nonbran después de las filas y de la coma ,), con lo cual obtenemos un vector de valores correspondentes a la longitud del genoma de cada especie.
  2. El comando which.max identifica al valor máximo y nos devuelve su posición en el vector. En cambio, si recuerdas, el comando max() nos devolvía el valor máximo (pero no su posición en el vector).
  3. A la variable maximo_pos se le asignó el valor de la posición del valor máximo del vector.
  4. Escribir ensTab[ maximo_pos , ] es equivalente a ensTab[ 5 , ], y en la tabla, el Chimpance está en la fila número 5.
  5. Por último, usar rownames() nos simplifica la visualización mostrándonos únicamente el nombre de esa fila.

Ahora podemos ver el resultado de la ejecución:

## [1] "Chimpanzee"

Nota: Aunque parezca más “profesional” ejecutar todo en una sola línea, como en el ejemplo de arriba, lo más recoemendable es que escribas tu código de tal manera que facilite a otras personas, incluyendo al futuro , su lectura e interpretación.


1.4 Un poco sobre gráficos

Podemos usar el comando barplot() para visualizar nuestros datos en un gráfico de barras. Supon que queremos visualizar únicamente los datos de genes codificantes, ncRAN y pseudo genes. Para ello creamos un nuevo DataFrame con sólo estos datos:

Si ahora intentamos usar barplot() tendremos lo siguiente:

## Error in barplot.default(genesTab, beside = TRUE): 'height' must be a vector or a matrix

Como indica el error, debemos convertir el DataFrame genesTab a una matriz, para ello usamos el comando as.matriz().

Y generamos la gráfica:

Besides nos grafica los datos de cada columna por separado. Podemos cambiar esto girando o transponiendo la matriz con el comando t(), esto hará que ahora las columnas sean las filas, es decir, que las especies sean ahora las columnas (prueba en la consola t(m.ensTab).

¡Casi! Pero aún es difícil comparar por especie, de hecho, los datos están tan apilados que R no ha podido “dibujar” los nombres de todas las especies.

Segundo intento, removamos besides().

¿Mejor?. Ahora tenemos una gráfica de barras apiladas. Con una barra por especie. Pero seguimos sin visualizar los nombres de las especies. Lo mejor será graficar horizontalmente:


¡Listo!
Puedes continuar hacia la siguiente sección.


Anterior

Siguiente

LS0tCnRpdGxlOiAnRXN0cnVjdHVyYXMgZGUgZGF0b3MnCi0tLQoqKioKIyBNYXRyaWNlcywgTGlzdGFzIHkgRGF0YWZyYW1lcwoKIyMgTWF0cmljZXMKQWwgaWd1YWwgcXVlIGxvcyB2ZWN0b3JlcywgbGFzIG1hdHJpY2VzIHNvbiB1bmEgZXN0cnVjdHVyYSBkZSBkYXRvcyBkZWwgKiptaXNtbyB0aXBvKiosIGNvbiBsYSBkaWZlcmVuY2lhIGRlIHF1ZSBlbiBsYXMgbWF0cmljZXMgbG9zIGRhdG9zIGVzdMOhbiBlc3RydWN0dXJhZG9zIGVuIGRvcyBkaW1lbnNpb25lczogKipGaWxhcyoqIHkgKipDb2x1bW5hcyoqLiAgCkVzIHBvc2libGUgY3JlYXIgbWF0cmljZXMgdXNhbmRvIGVsIGNvbWFuZG8gYG1hdHJpeCgpYC4gCgpgYGB7cn0KbWF0cml6ID0gbWF0cml4KDE6MTUsIGJ5cm93ID0gVFJVRSwgbnJvdyA9IDMpCm1hdHJpegpgYGAKClRhbWJpw6luIGVzIHBvc2libGUgdGVuZXIgZXN0cnVjdHVyYXMgZGUgZGF0b3MgZGUgbcOhcyBkZSBkb3MgZGltZW5zaW9uZXMsIHBhcmEgZWxsbyB1c2Ftb3MgZWwgY29tYW5kbyBgYXJyYXkoKWAuICAKRW4gZXN0ZSB0dXRvcmlhbCB2ZXJlbW9zIHBvY28gc29icmUgbWF0cmljZXMsIHBlcm8gZXMgaW1wb3J0YW50ZSBxdWUgYWxndW5vcyBkZSBsb3MgY29tYW5kb3MgcXVlIHZlcmVtb3MgcGFyYSBsb3MgYERhdGFGcmFtZXNgIHNvbiB0YW1iacOpbiBhcGxpY2FibGVzIHBhcmEgbGFzIG1hdHJpY2VzLiBQdWVkZXMgY29uc3VsdGFyIG3DoXMgYWwgcmVzcGVjdG8gZW4gbG9zIHNpZ3VpZW50ZXMgdmluY3Vsb3M6IFsxXShodHRwczovL3d3dy5vcmVpbGx5LmNvbS9saWJyYXJ5L3ZpZXcvbGVhcm5pbmctci85NzgxNDQ5MzU3MTYwL2NoMDQuaHRtbCksIFsyXShodHRwOi8vd3d3LnItdHV0b3IuY29tL3ItaW50cm9kdWN0aW9uL21hdHJpeCkuCgo8YnI+CgojIyBMaXN0YXMKCiogTGFzIGxpc3RhcyBmdW5jaW9uYW4gY29tbyBjb250ZW5lZG9yZXMgZGUgb2JqZXRvcy4gCiogQWwgY29udHJhcmlvIGRlIGxvcyB2ZWN0b3JlcywgKipsYXMgbGlzdGFzIHB1ZWRlbiBjb250ZW5lciBvYmpldG9zIGRlIGRpZmVyZW50ZXMgdGlwb3MqKiAocHVlZGVuIHNlciBoZXRlcm9nw6luZWFzKS4KKiBMYXMgbGlzdGFzIHB1ZWRlbiBpbmNsdXNvIGNvbnRlbmVyIG90cmFzIGxpc3Rhcy4KKiBTZSBjcmVhbiBjb24gZWwgY29tYW5kbyBgbGlzdCgpYC4KCmBgYHtyfQptaV9saXN0YSA9IGxpc3QoICJBcmMiLCAxMDAwLCBUUlVFLCAiQUdUVFRHIikKbWlfbGlzdGEKYGBgCgpMb3MgZWxlbWVudG9zIGRlIGxhcyBsaXN0YXMgcHVlZGVuIHNlciBub21icmFkb3M6CgpgYGB7cn0KbWlfbGlzdGEgPSBsaXN0KCBnZW4gPSAiQXJjIiwgCiAgICAgICAgICAgICAgICAgbnVtX2Jhc2VzID0gMTAwMCwgCiAgICAgICAgICAgICAgICAgZXhwcmVzYWRvID0gVFJVRSwgCiAgICAgICAgICAgICAgICAgc2VjX2luaWNpYWwgPSAiQUdUVFRHIikKbWlfbGlzdGEKYGBgCgpMb3MgZWxlbWVudG9zIGRlbnRybyBkZSB1bmEgbGlzdGEgc2Ugb2J0aWVuZW4gdXNhbmRvIGRvcyBjb3JjaGV0ZXMgY3VhZHJhZG9zIGFuaWRhZG9zOiBgW1sgaSBdXWAsIGRvbmRlICRpJCBlcyBlbCBuw7ptZXJvIGRlIGVsZW1lbnRvIGRlc2VhZG8uCgoqKkVqZXJjaWNpbzoqKgoKKiDCv0N1w6FsIGVzIGxhIGRpZmVyZW5jaWEgZW50cmUgbG9zIHNpZ3VpZW50ZXMgZG9zIGNvbWFuZG9zPwoqIMK/UXXDqSB0aXBvIGRlIGRhdG8gZXMgY2FkYSB1bm8/IFVzYSBsYSBmdW5jacOzbiBgY2xhc3MoKWAgcGFyYSB2ZXJpZ3VhcmxvLgoKYGBge3IgZXZhbD1GQUxTRX0KbWlfbGlzdGFbNF0KbWlfbGlzdGFbWzRdXQpgYGAKClNpIHNlIGhhbiBhc2lnbmFkbyBub21icmVzIGEgbGEgbGlzdGEsIGVzIHBvc2libGUgdXNhcmxvcyBwYXJhIGV4dHJhZXIgbG9zIGRhdG9zOgoKYGBge3J9Cm5hbWVzKG1pX2xpc3RhKSAjIERldnVlbHZlIGxvcyBub21icmVzIGRlIGxvcyBlbGVtZW50b3MgZGUgbGEgbGlzdGEKYGBgCgpQb3IgZWplbXBsbywgbm9zIGludGVyZXNhIGV4dHJhZXIgZGUgbGEgbGlzdGEgbG9zIGRhdG9zIGNvcnJlc3BvbmRpZW50ZXMgYSBsYSB2YXJpYWJsZSBgZ2VuYDoKCmBgYHtyfQptaV9saXN0YVtbMV1dICMgRGV2dWVsdmUgZWwgZWxlbWVudG8gbsO6bWVybyAxIGRlIGxhIGxpc3RhCm1pX2xpc3RhJGdlbiAjIERldnVlbHZlIGVsIGVsZW1lbnRvIG5vbWJyYWRvIGNvbW8gImdlbiIKbWlfbGlzdGFbWyJnZW4iXV0gIyBEZXZ1ZWx2ZSBlbCBlbGVtZW50byBub21icmFkbyBjb21vICJnZW4iCmBgYAoKTGFzIHRyZXMgZm9ybWFzIGFudGVyaW9yZXMgc29uIHbDoWxpZGFzIHkgcGVybWl0ZW4gb2J0ZW5lciBlbCBtaXNtbyByZXN1bHRhZG8uCgo8YnI+CgojIyBEYXRhRnJhbWVzCgoqIExvcyBgRGF0YUZyYW1lc2Agc29uIHVubyBkZSBsb3Mgb2JqZXRvcyBtw6FzIHVzYWRvcyBlbiBSLgoqIFBvZGVtb3MgZW50ZW5kZXIgYSB1biBgRGF0YWZyYW1lYCBjb21vIHVuYSB0YWJsYSBkZSBkYXRvcyBkZSB1biBkZXRlcm1pbmFkbyBuw7ptZXJvIGRlIGZpbGFzIHkgY29sdW1uYXMuIFNpbiBlbWJhcmdvLCBhIGRpZmVyZW5jaWEgZGUgbGFzIG1hdHJpY2VzLCBsb3MgRGF0YUZyYW1lcyBwdWVkZW4gdGVuZXIgZGF0b3MgaGV0ZXJvZ8OpbmVvcy4KCkNvbWVuY8OpbW9zIGNyZWFuZG8gdW4gYERhdGFGcmFtZWAgdXNhbmRvIGVsIGNvbWFuZG8gYGRhdGEuZnJhbWUoKWA6CgpgYGB7cn0KbXlfZGYgPSBkYXRhLmZyYW1lKCAiTnVtX2RlX2dlbiIgPSBjKDE6MyksCiAgICAgICAgICAgICAgICAgICAgIk5vbWJyZV9nZW4iID0gYygiQXJjIiwgImNkazIiLCAibWlSLTHigJMxIiksCiAgICAgICAgICAgICAgICAgICAgIkV4cHJlc2FkbyIgPSBjKFRSVUUsIEZBTFNFLCBGQUxTRSksCiAgICAgICAgICAgICAgICAgICAgIk51bV9wYmFzZXMiID0gYygxMDAsIDIwMCwgNjApCiAgICAgICAgICAgICAgICAgICAgKQpteV9kZgpgYGAKCmBgYHtyIGVjaG89RkFMU0V9CmxpYnJhcnkoa25pdHIpCmthYmxlKG15X2RmLCBjYXB0aW9uID0gIk1pIHByaW1lciBEYXRhRnJhbWUiKQpgYGAKCllhIGhhcyBjcmVhZG8gdHUgcHJpbWVyIGBEYXRhRnJhbWVgLCBzaW4gZW1iYXJnbywgdmFtb3MgYWhvcmEgdW4gY29uanVudG8gZGUgZGF0b3MgcHJvdmVuaWVudGUgZGUgdW4gYXJjaGl2by4KCjxicj4KCiMjIyBFamVyY2ljaW8gY29uIERhdGFmcmFtZXMKClZhbW9zIGEgdXRpbGl6YXIgbG9zIGRhdG9zIHByb3BvcmNpb25hZG9zIHBvciBlbCBEci4gQ2VpIEFicmV1IGVuIGxhIHDDoWdpbmEgW1Byw6FjdGljYXMgZGUgQmlvaW5mb3Jtw6F0aWNhOiBSXShodHRwOi8vZGF0b3MubGFuZ2ViaW8uY2ludmVzdGF2Lm14L35jZWkvY3Vyc29zL0JQXzIwMTgvbl9zZXNpb24wNi5odG1sKS4gRW4gcGFydGljdWxhciwgdmFtb3MgYSB1dGlsaXphciBlbCBhcmNoaXZvIFtlbnNlbWJsX2luZm8udGFiXShodHRwOi8vZGF0b3MubGFuZ2ViaW8uY2ludmVzdGF2Lm14L35jZWkvY3Vyc29zL0JQXzIwMTgvZGF0YS9lbnNlbWJsX2luZm8udGFiKS4gCgo8YmxvY2txdW90ZT4KICA8cD5MYSBpbmZvcm1hY2nDs24gZW4gbGEgdGFibGEgcXVlIGFjYWJhbiBkZSBkZXNjYXJnYXIgZnVlIG9idGVuaWRhIGRlIGxhIHDDoWdpbmEgZGUgRW5zZW1ibCwgcXVlIHJldW5lIHVuYSBncmFuIGNhbnRpZGFkIGRlIGluZm9ybWFjacOzbiBzb2JyZSBnZW5vbWFzIGNvbXBsZXRhbWVudGUgc2VjdWVuY2lhZG9zIGRlIHZlcnRlYnJhZG9zIHkgYWxndW5vcyBvcmdhbmlzbW9zIG1vZGVsby48L3A+CiAgPHNtYWxsPkNpdGFkbyBsYSBww6FnaW5hIDxjaXRlIHRpdGxlPSJOb21icmUgQXBlbGxpZG9zIj5odHRwOi8vZGF0b3MubGFuZ2ViaW8uY2ludmVzdGF2Lm14L35jZWkvY3Vyc29zL0JQXzIwMTgvPC9jaXRlPjwvc21hbGw+CjwvYmxvY2txdW90ZT4KClVuYSB2ZXogZGVzY2FyZ2FkbyBlbCBhcmNoaXZvLCBndcOhcmRhbG8gZW4gdHUgZGlyZWN0b3JpbyBkZSB0cmFiYWpvIChwdWVkZXMgdXNhciBlbCBjb21hbmRvIGBnZXR3ZCgpYCAoKmdldFdvcmtEaXJlY3RvcnkqKSBwYXJhIGd1YXJkYXIgYWjDrSBlbCBhcmNoaXZvKS4KClZhbW9zIGEgYWJyaXIgZWwgYXJjaGl2byB1dGlsaXphbmRvIGVsIGNvbWFuZG8gYHJlYWQudGFibGUoKWAuICAKKipOb3RhOioqIMK/UXXDqSBpbmRpY2FuIGxvcyBwYXLDoW1ldHJvcyBgaGVhZGVyPWAgeSBgcm93Lm5hbWVzPWA/CmBgYHtyfQplbnNUYWIgPSByZWFkLnRhYmxlKCJlbnNlbWJsX2luZm8udGFiIiwgaGVhZGVyID0gVFJVRSwgcm93Lm5hbWVzID0gMSkKYGBgClVuYSB2ZXogY2FyZ2FkbywgcG9kZW1vcyBjb21wcm9iYXIgcXVlIGVsIG9iamV0byBlcyB1biBgRGF0YUZyYW1lYDoKYGBge3J9CmNsYXNzKGVuc1RhYikKYGBgCgpBZGVtw6FzLCBwb2RlbW9zIHVzYXIgbG9zIGNvbWFuZG9zIGBoZWFkKClgIHkgYHRhaWwoKWAgcGFyYSB2ZXIgbGFzIHByaW1lcmFzIHkgbGFzIMO6bHRpbWFzIGZpbGFzIGRlIGxhIHRhYmxhLCByZXNwZWN0aXZhbWVudGUuIFTDuiBwdWVkZXMgaW1wcmltaXIgdG9kYSBsYSB0YWJsYSBlbiBjb25zb2xhLCBzaSBhc8OtIGxvIGRlc2Vhcy4KYGBge3J9CmhlYWQoZW5zVGFiLCBuID0gMykKYGBgCgpgYGB7cn0KdGFpbChlbnNUYWIsIG4gPSAzKQpgYGAKCjxkaXYgY2xhc3M9ImFsZXJ0IGFsZXJ0LWRpc21pc3NpYmxlIGFsZXJ0LXN1Y2Nlc3MiPgo8aDQgY2xhc3M9ImFsZXJ0LWhlYWRpbmciPsKhT2pvITwvaDQ+CiAgPGJ1dHRvbiB0eXBlPSJidXR0b24iIGNsYXNzPSJjbG9zZSIgZGF0YS1kaXNtaXNzPSJhbGVydCI+PC9idXR0b24+CiAgIEVuIGBFbnZpcm9ubWVudGAgcHVlZGVzIGNvbXByb2JhciBxdWUgZWwgb2JqZXRvIGBlbnNUYWJgIGhhIHNpZG8gY3JlYWRvLCBwdWVkZXMgYWJyaXJsbyBkZXNkZSAqUlN0dWRpbyogcGFyYSBleGFtaW5hcmxvLiBUYW1iacOpbiBwdWVkZXMgdXNhciBlbCBjb21hbmRvIGBWaWV3KG5vbWJyZV9kZWxfZGF0YUZyYW1lKWAuCjwvZGl2PgoKPGJyPgoKIyMjIEluc3BlY2Npb25hbmRvIGVsICpEYXRhRnJhbWUqCgoqKkVqZXJjaWNpbzoqKgpVc2FuZG8gbG9zIGNvbWFuZG9zIGluZGljYWRvcywgYXZlcmlndWEgbG8gc2lndWllbnRlIGRlIGxhIHRhYmxhIGBlbnNUYWJgOgoKMS4gwr9RdcOpIGRpbWVuc2lvbmVzIHRpZW5lIGxhIHRhYmxhPyBgZGltKClgCjIuIMK/UXXDqSBkYXRvcyBicmluZGEgbGEgZnVuY2nDs24gYHN0cigpYD8gIAozLiDCv0N1w6FudGFzIGVzcGVjaWVzIGhheSBlbiBsYSB0YWJsYT8gYG5yb3coKWAKNC4gwr9DdcOhbnRhcyB2YXJpYWJsZXMgc2UgbWlkaWVyb24gcGFyYSBjYWRhIGVzcGVjaWU/IGBuY29scygpYAo1LiDCv0N1w6FsZXMgc29uIGxvcyBub21icmVzIGRlIGRpY2hhcyB2YXJpYWJsZXM/IGBjb2xuYW1lcygpYCAKNi4gwr9DdcOhbGVzIHNvbiBsb3Mgbm9tYnJlcyBkZSBsYXMgZXNwZWNpZXM/CgoqKioKCkFob3JhIHZhbW9zIGEgaW5zcGVjY2lvbmFyIG3DoXMgYSBmb25kbyBsb3MgZGF0b3MgZGUgbGEgdGFibGEuClNpIHF1ZXJlbW9zIG9idGVuZXIgdW4gZGF0byBwYXJ0aWN1bGFyIGRlIGxhIHRhYmxhLCBwb3IgZWplbXBsbyAqKmN1w6FudG9zIGdlbmVzIGNvZGlmaWNhbnRlcyB0aWVuZSBlbCBIdW1hbm8qKiwgcG9kZW1vcyB1c2FyIGxvIHNpZ3VpZW50ZToKYGBge3J9CiMgRsOtamF0ZSBlbiBsYSBzaW50w6F4aXM6CiMgUHJpbWVybyBwb25lbW9zIGVsIG5vbWJyZSBkZSBsYSBmaWxhLCB5IGx1ZWdvIGVsIGRlIGxhIGNvbHVtbmEuCmVuc1RhYlsiSHVtYW4iLCAiY29kaW5nX2dlbmVzIl0KYGBgCgrCv0N1w6FsIGVzIGxhIGxvbmdpdHVkIGRlbCBnZW5vbWEgZGVsICpUYXNtYW5pYW5fZGV2aWwqPwoKYGBge3IgZXZhbD1GQUxTRX0KZW5zVGFiWyJUYXNtYW5pYW5fZGV2aWwiLCAiZ2Vub21lX2xlbmd0aCJdICMgRXN0ZSBmdW5jaW9uYXLDoQoKZW5zVGFiWyJnZW5vbWVfbGVuZ3RoIiwgIlRhc21hbmlhbl9kZXZpbCJdICMgRXN0ZSBOTyBmdW5jaW9uYXLDoQpgYGAKCjxkaXYgY2xhc3M9ImFsZXJ0IGFsZXJ0LWRpc21pc3NpYmxlIGFsZXJ0LWRhbmdlciI+CjxoNCBjbGFzcz0iYWxlcnQtaGVhZGluZyI+wqFSZWN1ZXJkYSE8L2g0PgogIDxidXR0b24gdHlwZT0iYnV0dG9uIiBjbGFzcz0iY2xvc2UiIGRhdGEtZGlzbWlzcz0iYWxlcnQiPjwvYnV0dG9uPgogIFByaW1lcm8gbm9tYnJhIG8gaW5kaWNhIGxhIGZpbGEsIHkgbHVlZ28gbGEgY29sdW1uYSwgc2VwYXJhZG9zIHBvciB1bmEgY29tYSBgLGAuCjwvZGl2PgoKQWhvcmEgcXVlcmVtb3Mgc2FiZXIgbG9zIGRhdG9zIGRlIGxhcyBwcmltZXJhcyBkb3MgY29sdW1uYXMgKHZhcmlhYmxlcyBgY29kaW5nX2dlbmVzYCB5IGBuY3JuYV9nZW5lc2ApIGRlIGxhcyBwcmltZXJhcyB0cmVzIGVzcGVjaWVzOgpgYGB7ciBldmFsPUZBTFNFfQojIFBvZGVtb3MgdXNhciB2YWxvcmVzIG51bcOpcmljb3MgY29tbyDDrW5kaWNlcyBkZSBsYSB0YWJsYQplbnNUYWJbIGMoMTozKSAsIGMoMToyKSBdCmBgYAoKwr9RdcOpIHBhc2Egc2kgcXVlcmVtb3MgdmVyIGxhIGxvbmdpdHVkIGRlIGxvcyBnZW5vbWFzIGRlIHRvZGFzIGxhcyBlc3BlY2llcz8KYGBge3J9CmVuc1RhYlsgLCAiZ2Vub21lX2xlbmd0aCIgXSAjIMKhTm8gb2x2aWRlcyBsYSBjb21hIQpgYGAKCioqQW5hbGl6YSBsbyBzaWd1aWVudGU6KiogIArCv1F1w6kgZXNwZWNpZSB0aWVuZSBsYSBtYXlvciBsb25naXR1ZCBkZSBnZW5vbWE/IFBhcmEgZWxsbyBwb2RlbW9zIGF2ZXJpZ3VhciBxdcOpIGhhY2UgbGEgZnVuY2nDs24gYHdoaWNoLm1heCgpYC4gIApUw7NtYXRlIHVuIHRpZW1wbyBwYXJhIGFuYWxpemFyIGxvcyBzaWd1aWVudGVzIGNvbWFuZG9zLgpgYGB7ciBldmFsPUZBTFNFfQpsb25naXR1ZGVzX2dlbm9tYSA9IGVuc1RhYiRnZW5vbWVfbGVuZ3RoIAojIMK/RXMgZXN0byBzaW1pbGFyIGEgbGEgb3BjacOzbiBkZSBhcnJpYmE/IC0+IGVuc1RhYlsgLCAiZ2Vub21lX2xlbmd0aCIgXSAKIyDCv1JlY3VlcmRhcyBxdWUgdGFtYmnDqW4gZXJhIHBvc2libGUgaGFjZXIgZXN0byBjb24gbGFzIGxpc3Rhcz8KCiMgwr9RdcOpIGNyZWVzIHF1ZSBpbmRpcXVlIGxhIGZ1bmNpw7NuIHdoaWNoLm1heCgpPwptYXhpbW9fcG9zID0gd2hpY2gubWF4KCBsb25naXR1ZGVzX2dlbm9tYSApCnJvd25hbWVzKGVuc1RhYlsgbWF4aW1vX3BvcyAsIF0pCmBgYAoKKirCv1F1w6kgb2N1cnJpw7M/KioKCjEuIEVsIGNvbWFuZG8gYGVuc1RhYiRnZW5vbWVfbGVuZ3RoYCBlcyBzaW1pbGFyIGEgZWplY3V0YXIgYGVuc1RhYlsgLCAiZ2Vub21lX2xlbmd0aCIgXWAgKHJlY3VlcmRhIHF1ZSBsYXMgY29sdW1uYXMgc2Ugbm9uYnJhbiBkZXNwdcOpcyBkZSBsYXMgZmlsYXMgeSBkZSBsYSBjb21hIGAsYCksIGNvbiBsbyBjdWFsIG9idGVuZW1vcyB1biB2ZWN0b3IgZGUgdmFsb3JlcyBjb3JyZXNwb25kZW50ZXMgYSBsYSBsb25naXR1ZCBkZWwgZ2Vub21hIGRlIGNhZGEgZXNwZWNpZS4KMi4gRWwgY29tYW5kbyBgd2hpY2gubWF4YCBpZGVudGlmaWNhIGFsIHZhbG9yIG3DoXhpbW8geSBub3MgZGV2dWVsdmUgc3UgcG9zaWNpw7NuIGVuIGVsICoqdmVjdG9yKiouIEVuIGNhbWJpbywgc2kgcmVjdWVyZGFzLCBlbCBjb21hbmRvIGBtYXgoKWAgbm9zIGRldm9sdsOtYSBlbCB2YWxvciBtw6F4aW1vIChwZXJvIG5vIHN1IHBvc2ljacOzbiBlbiBlbCB2ZWN0b3IpLgozLiBBIGxhIHZhcmlhYmxlIGBtYXhpbW9fcG9zYCBzZSBsZSBhc2lnbsOzIGVsIHZhbG9yIGRlIGxhIHBvc2ljacOzbiBkZWwgdmFsb3IgbcOheGltbyBkZWwgdmVjdG9yLgo0LiBFc2NyaWJpciBgZW5zVGFiWyBtYXhpbW9fcG9zICwgXWAgZXMgZXF1aXZhbGVudGUgYSBgZW5zVGFiWyA1ICwgXWAsIHkgZW4gbGEgdGFibGEsIGVsICoqQ2hpbXBhbmNlKiogZXN0w6EgZW4gbGEgZmlsYSBuw7ptZXJvIDUuCjUuIFBvciDDumx0aW1vLCB1c2FyIGByb3duYW1lcygpYCBub3Mgc2ltcGxpZmljYSBsYSB2aXN1YWxpemFjacOzbiBtb3N0csOhbmRvbm9zIMO6bmljYW1lbnRlIGVsIG5vbWJyZSBkZSBlc2EgZmlsYS4KCkFob3JhIHBvZGVtb3MgdmVyIGVsIHJlc3VsdGFkbyBkZSBsYSBlamVjdWNpw7NuOgpgYGB7cn0KIyBQb2RlbW9zIGVqZWN1YXRhciB0b2RvIGVuIHVuYSBzb2xhIGzDrW5lYQpyb3duYW1lcyhlbnNUYWJbIHdoaWNoLm1heChlbnNUYWIkZ2Vub21lX2xlbmd0aCkgLCBdKQpgYGAKCioqTm90YToqKiBBdW5xdWUgcGFyZXpjYSBtw6FzICJwcm9mZXNpb25hbCIgZWplY3V0YXIgdG9kbyBlbiB1bmEgc29sYSBsw61uZWEsIGNvbW8gZW4gZWwgZWplbXBsbyBkZSBhcnJpYmEsIGxvIG3DoXMgcmVjb2VtZW5kYWJsZSBlcyBxdWUgZXNjcmliYXMgdHUgY8OzZGlnbyBkZSB0YWwgbWFuZXJhIHF1ZSBmYWNpbGl0ZSBhIG90cmFzIHBlcnNvbmFzLCBpbmNsdXllbmRvIGFsIGZ1dHVybyAqKnTDuioqLCBzdSBsZWN0dXJhIGUgaW50ZXJwcmV0YWNpw7NuLgoKPGJyPgoKIyMjIEFuw6FsaXNpcyBmaW5hbGVzIGNvbiBEYXRhRnJhbWVzLi4uIHBvciBhaG9yYQoKVMOzbWF0ZSB1bm9zIG1pbnV0b3MgcGFyYSBhbsOhbGl6YXIgbG8gcXVlIGhhY2VuIGNhZGEgdW5vIGRlIGxvcyBzaWd1aWVudGVzIGNvbWFuZG9zOgpgYGB7ciBldmFsPUZBTFNFfQojIDEpIMK/UXXDqSBlc3BlY2llcyB0aWVuZW4gbcOhcyBkZSAyMiwwMDAgZ2VuZXMgY29kaWZpY2FudGVzPwpyb3duYW1lcyggZW5zVGFiIClbZW5zVGFiJGNvZGluZ19nZW5lcyA+IDIyZTNdCgojIDIpIMK/Q3XDoWwgZXMgZWwgcHJvbWVkaW8sIGVuIE1lZ2FiYXNlcywgZGUgdG9kYXMgbGFzIGVzcGVjaWVzPwpsb25nc19NYiA9IGVuc1RhYiRnZW5vbWVfbGVuZ3RoIC8gMWU2Cm1lYW4oIGxvbmdzX01iICkKCiMgMykgwr9RdcOpIHBvcmNlbnRhamUgZGVsIGdlbm9tYSBkZSBjYWRhIGVzcGVjaWUgZXMgY29kaWZpY2FudGU/CiMgQ2FsY3VsYW1vcyBlbCBuw7ptZXJvIGRlIHBiIGNvZGlmaWNhbnRlcwphdmdfY29kaW5nX2Jhc2VfcGFpcnMgPSBlbnNUYWIkY29kaW5nX2dlbmVzICogZW5zVGFiJGNvZGluZ19nZW5lX2F2Z19sZW5ndGgKIyBPYnRlbmVtb3MgZWwgcG9yY2VudGFqZQpwZXJjZW50YWdlID0gIGF2Z19jb2RpbmdfYmFzZV9wYWlycyAvIGVuc1RhYiRnZW5vbWVfbGVuZ3RoICogMTAwCiMgTG8gYWdyZWdhbW9zIGEgbGEgdGFibGEgCiMgKE9KTzogTGEgY29sdW1uYSAiY29kaW5nX2dlbmVzX3BlcmNlbnRhZ2UiIGxhIGVzdGFtb3MgY3JlYW5kbyBub3NvdHJvcykKZW5zVGFiJGNvZGluZ19nZW5lc19wZXJjZW50YWdlID0gcGVyY2VudGFnZQpgYGAKCiMjIFVuIHBvY28gc29icmUgZ3LDoWZpY29zClBvZGVtb3MgdXNhciBlbCBjb21hbmRvIGBiYXJwbG90KClgIHBhcmEgdmlzdWFsaXphciBudWVzdHJvcyBkYXRvcyBlbiB1biBncsOhZmljbyBkZSBiYXJyYXMuIFN1cG9uIHF1ZSBxdWVyZW1vcyB2aXN1YWxpemFyIMO6bmljYW1lbnRlIGxvcyBkYXRvcyBkZSBnZW5lcyBjb2RpZmljYW50ZXMsIG5jUkFOIHkgcHNldWRvIGdlbmVzLiBQYXJhIGVsbG8gY3JlYW1vcyB1biBudWV2byBEYXRhRnJhbWUgY29uIHPDs2xvIGVzdG9zIGRhdG9zOgoKYGBge3J9CmdlbmVzVGFiID0gZW5zVGFiWyxjKCJjb2RpbmdfZ2VuZXMiLCJuY3JuYV9nZW5lcyIsInBzZXVkb19nZW5lcyIpXQpgYGAKClNpIGFob3JhIGludGVudGFtb3MgdXNhciBgYmFycGxvdCgpYCB0ZW5kcmVtb3MgbG8gc2lndWllbnRlOgpgYGB7ciBlcnJvciA9IFRSVUV9CmJhcnBsb3QoZ2VuZXNUYWIsIGJlc2lkZT1UUlVFKQpgYGAKCkNvbW8gaW5kaWNhIGVsIGVycm9yLCBkZWJlbW9zIGNvbnZlcnRpciBlbCBgRGF0YUZyYW1lYCAqZ2VuZXNUYWIqIGEgdW5hIG1hdHJpeiwgcGFyYSBlbGxvIHVzYW1vcyBlbCBjb21hbmRvIGBhcy5tYXRyaXooKWAuCmBgYHtyfQptLmVuc1RhYiA9IGFzLm1hdHJpeCggZ2VuZXNUYWIgKQpgYGAKClkgZ2VuZXJhbW9zIGxhIGdyw6FmaWNhOgoKYGBge3J9CmJhcnBsb3QobS5lbnNUYWIsIGJlc2lkZT1UUlVFKSAKYGBgCgpCZXNpZGVzIG5vcyBncmFmaWNhIGxvcyBkYXRvcyBkZSBjYWRhIGNvbHVtbmEgcG9yIHNlcGFyYWRvLiBQb2RlbW9zIGNhbWJpYXIgZXN0byBnaXJhbmRvIG8gdHJhbnNwb25pZW5kbyBsYSBtYXRyaXogY29uIGVsIGNvbWFuZG8gYHQoKWAsIGVzdG8gaGFyw6EgcXVlIGFob3JhIGxhcyBjb2x1bW5hcyBzZWFuIGxhcyBmaWxhcywgZXMgZGVjaXIsIHF1ZSBsYXMgZXNwZWNpZXMgc2VhbiBhaG9yYSBsYXMgY29sdW1uYXMgKHBydWViYSBlbiBsYSBjb25zb2xhIGB0KG0uZW5zVGFiKWAuCgpgYGB7ciB9CmJhcnBsb3QoIHQobS5lbnNUYWIpLCBiZXNpZGUgPSBUUlVFKSAKYGBgCgoqKsKhQ2FzaSEqKiBQZXJvIGHDum4gZXMgZGlmw61jaWwgY29tcGFyYXIgcG9yIGVzcGVjaWUsIGRlIGhlY2hvLCBsb3MgZGF0b3MgZXN0w6FuIHRhbiBhcGlsYWRvcyBxdWUgUiBubyBoYSBwb2RpZG8gImRpYnVqYXIiIGxvcyBub21icmVzIGRlIHRvZGFzIGxhcyBlc3BlY2llcy4KClNlZ3VuZG8gaW50ZW50bywgcmVtb3ZhbW9zIGBiZXNpZGVzKClgLgpgYGB7ciB9CmJhcnBsb3QoIHQobS5lbnNUYWIpKSAKYGBgCgoqKsK/TWVqb3I/KiouIEFob3JhIHRlbmVtb3MgdW5hIGdyw6FmaWNhIGRlIGJhcnJhcyBhcGlsYWRhcy4gQ29uIHVuYSBiYXJyYSBwb3IgZXNwZWNpZS4gUGVybyBzZWd1aW1vcyBzaW4gdmlzdWFsaXphciBsb3Mgbm9tYnJlcyBkZSBsYXMgZXNwZWNpZXMuIExvIG1lam9yIHNlcsOhIGdyYWZpY2FyIGhvcml6b250YWxtZW50ZToKCgpgYGB7ciBmaWcuaGVpZ2h0PTcsICBmaWcud2lkdGg9MTB9CmJhcnBsb3QoIHQobS5lbnNUYWIpLCBob3JpeiA9IFRSVUUsIGxhcyA9IDEsIAogICAgICAgICAjIFkgYWdyZWdhbW9zIGFsZ28gZGUgZGVjb3JhY2nDs24KICAgICAgICAgY29sID0gYygiaW5kaWFucmVkMyIsICJ0dXJxdW9pc2U0IiwgImxpZ2h0Z29sZGVucm9kIiksIAogICAgICAgICB4bGFiID0gIk7Dum1lcm8gZGUgZ2VuZXMiLAogICAgICAgICBsZWdlbmQudGV4dCA9IChjb2xuYW1lcyhnZW5lc1RhYikpKSAKYGBgCgoqKioKCioqwqFMaXN0byEqKiAgClB1ZWRlcyBjb250aW51YXIgaGFjaWEgbGEgc2lndWllbnRlIHNlY2Npw7NuLgoKKioqCgogIDxhIGNsYXNzPSJidG4gYnRuLXByaW1hcnkgcHVsbC1sZWZ0IiBocmVmPSIuL2ludHJvX1JfcDMuaHRtbCIgcm9sZT0iYnV0dG9uIj4gQW50ZXJpb3IgPC9hPgoKCiAgPGEgY2xhc3M9ImJ0biBidG4tcHJpbWFyeSBwdWxsLXJpZ2h0IiBocmVmPSIuL2luZGV4Lmh0bWwiIHJvbGU9ImJ1dHRvbiI+IFNpZ3VpZW50ZSA8L2E+Cg==