Durante este curso fuiste creando varios documentos con R Markdown en
HTML. Este es un formato que tiene un montón de flexibilidad, pero
seguramente no es el único que necesitás. Casi seguro que los informes
los tengas que presentar en formato PDF o, incluso, ¡en papel impreso!
{Rmarkdown}, y todo un amplio ecosistema de otros paquetes, permite
generar documentos en múltiples formatos usando el mismo archivo de
texto plano.
Personalizando la salida
Cada función de formato viene con sus opciones de personalización que
podés acceder leyendo su documentación. Para ver la documentación de
html_document
, usa este comando:
?rmarkdown::html_document
Vas a ver que tiene un montón de argumentos que modifican la salida.
La forma de setear estos argumentos en un documento de R Markdown es, de
nuevo, en el encabezado. Cada argumento de la función de salida
(html_document
en este caso) es un elemento debajo de la
función de output.
Por ejemplo, para que un documento de html tenga una tabla de
contenidos hay que setear el argumento toc
(de
table of contents) a
TRUE
. En el encabezado, esto queda así:
---
output:
html_document:
toc: TRUE
---
Conviene mirar eso con un poco de detenimiento porque requiere
“traducir” código de R –cual los argumentos de una función se fijan
entre paréntesis y con =
– en código de yaml –donde los
argumentos de la función son una lista cuyos elementos se definen con
:
.
En R lo que vemos como html_document(toc = TRUE)
se
traduce a yaml como
Si vas a la ayuda de pdf_document
vas a ver que también
tiene un argumento llamado toc
. Algunos argumentos son
compartidos, lo cual hace que se aún más fácil generar un mismo reporte
en muchos formatos haciendo muy pocos cambios.
Una forma rápida de hacer tus informes más vistosos es cambiarle el
tema visual. html_document
permite elegir entre una serie
de temas usando el argumento theme
. Por ejemplo, poniendo
esto en el encabezado, generás un documento HTML con un fondo oscuro
output:
html_document:
toc: TRUE
theme: darkly
Desafío
Andá a la ayuda de html_document
y fijate cuáles son los
valores válidos para el argumento theme
. ¡Probá
algunos!
Reportes parametrizados
Es muy común tener que hacer un reporte cuyo resultado dependa de
ciertos parámetros.
Por ejemplo, podrías tener un reporte que analiza la evolución de la
temperatura mínima y máxima para alguna estación del Servicio
Meteorológico Nacional.
library(metR)
library(dplyr)
library(tidyr)
library(ggplot2)
fechas <- seq.Date(lubridate::ymd(20220801), lubridate::ymd(20220831), by = "1 day")
observaciones <- GetSMNData(fechas, type = "daily")
observaciones %>%
filter(station == "SANTA ROSA AERO") %>%
pivot_longer(tmax:tmin, names_to = "variable", values_to = "valor") %>%
ggplot(aes(date, valor, color = variable)) +
geom_line() +
geom_point()
Si ahora querés hacer el mismo reporte pero para La Quiaca, tenés que
abrir el archivo y modificar la llamada a filter
para
quedarte sólo con ese país:
library(metR)
library(dplyr)
library(tidyr)
library(ggplot2)
fechas <- seq.Date(lubridate::ymd(20220801), lubridate::ymd(20220831), by = "1 day")
observaciones <- GetSMNData(fechas, type = "daily")
observaciones %>%
filter(station == "LA QUIACA OBSERVATORIO") %>%
pivot_longer(tmax:tmin, names_to = "variable", values_to = "valor") %>%
ggplot(aes(date, valor, color = variable)) +
geom_line() +
geom_point()
Si el reporte es largo y usa el nombre de la estación en múltiples
lugares cambiar “SANTA ROSA AERO” por “LA QUIACA OBSERVATORIO” puede ser
tedioso y propenso a error, ya que te obliga a modificar muchas partes
del código. Y si después tenés que hacer el mismo reporte para
Ushuaia…
En estas situaciones podés crear un reporte parametrizado.
La idea es que el reporte tiene una serie de parámetros que puede
modificar la salida. Es como si el archivo de R Markdown fuera una gran
función con sus argumentos!
Para generar un reporte parametrizado hay que agregar un elemento
llamado params
al encabezado con la lista de parámetros y
sus valores por default.
params:
estacion: SANTA ROSA AERO
Luego, en el código de R vas a tener acceso a una variable llamada
params
que es una lista que contiene los parámetros y su
valor. Para acceder al valor de cada parámetros se usa el operador
$
de la siguiente manera:
## NULL
De esta manera, el código original se puede modificar para usar el
valor de la estación almacenado en params$estacion
library(metR)
library(dplyr)
library(tidyr)
library(ggplot2)
fechas <- seq.Date(lubridate::ymd(20220801), lubridate::ymd(20220831), by = "1 day")
observaciones <- GetSMNData(fechas, type = "daily")
observaciones %>%
filter(station == params$station) %>%
pivot_longer(tmax:tmin, names_to = "variable", values_to = "valor") %>%
ggplot(aes(date, valor, color = variable)) +
geom_line() +
geom_point()
Y ahora el mismo código puede funcionar para distintas estaciones.
Para crear reportes distintos para cada estación sólo hay que modificar
el valor del parámetro en el encabezado:
params:
pais: LA QUIACA OBSERVATORIO
Desafío
Agregá al menos un parámetro al reporte que venís armando.
Control de chunks
Si recordás lo que vimos en la sección de reportes I mencionamos que un chunk tiene
una pinta como esta:
```{r nombre-del-chunk}
```
Ponerle nombre al chunk no es obligatorio pero está bueno para tener
una idea de qué hace cada uno, lo cual se vuelve más importante a medida
que un reporte se vuelve más largo y complejo. Pero lo que no dijimos es
que además del nombre, entre las llave se pueden poner un montón de
opciones que cambian el comportamiento y la apariencia del resultado del
chunk.
Para cambiar las opciones de un chunk, lo único que hay que hacer es
listarlas dentro de los corchetes. Por ejemplo:
```{r nombre-del-chunk, echo = FALSE, message = FALSE}
```
Hay una serie de opciones particularmente importante es la que
controla si el código se ejecuta y si el resultado del código va a
quedar en el reporte o no:
eval = FALSE
evita que se corra el código del chunk,
de manera que tampoco va a mostrar resultados. Es útil para mostrar
códigos de ejemplo si estás escribiendo, por ejemplo un documento para
enseñar R.
echo = FALSE
corre el código del chunk y muestra los
resultados, pero oculta el código en el reporte. Esto es útil para
escribir reportes para personas que no necesitan ver el código de R que
generó el gráfico o tabla.
include = FALSE
corre el código pero oculta tanto el
código como los resultados. Es útil para usar en chunks de configuración
general donde cargas las librerías.
Si estás escribiendo un informe en el que no querés que se muestre
ningún código, agregarle echo = FALSE
a cada chunk nuevo se
vuelve tedioso. La solución es cambiar la opción de forma global de
manera que aplique a todos los chunks. Esto se hace mediante la función
knitr::opts_chunk$set()
, que setea las opciones globales de
los chunks que le siguen. Si queŕes que todos los chunks tengan
echo = TRUE
crearías un chunk así:
```{r setup, include = FALSE}
knitr::opts_chunk$set(echo = TRUE)
```
Generalmente tiene sentido poner esto en el primer chunk de un
documento, que como suele ser cuestiones de configuración del reporte,
también conviene ponerle include = FALSE
.
Habrás visto que a vece algunas funciones escupen mensajes sobre lo
que hacen. Por ejemplo, cuando read_csv
lee un archivo
describe el tipo de dato de cada columna:
bariloche <- read_csv("datos/bariloche_enlimpio.csv")
## Rows: 3024 Columns: 31
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (3): Direccion_Viento_200cm, Direccion_Viento_1000cm, mes
## dbl (13): Temperatura_Abrigo_150cm, Temperatura_Abrigo_150cm_Maxima, Temper...
## lgl (14): Temperatura_Intemperie_5cm_Minima, Temperatura_Intemperie_50cm_Mi...
## dttm (1): Fecha
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
Esto es útil cuando uno está haciendo trabajo interactivo pero en
general no quiere que quede en el reporte. Para que no muestre estos
mensajes basta con poner la opción message = FALSE
```{r message = FALSE}
bariloche <- read_csv("datos/bariloche_enlimpio.csv")
```
En general no pasa nada si ignorás los mensajes. Son cuestiones
diagnósticas extra que sirven para que vos, como humano, te enteres de
lo que hizo una función. Distinto son las advertencias, o “warnings”.
Una advertencia te está diciendo que hay algo “raro” en el código que
puede significar que hay algo mal. No llega al nivel de error, que es
algo que literalmente “no computa”. Por ejemplo, sqrt
tira
una advertencia cuando recibe números negativos.
## Warning in sqrt(-1): NaNs produced
Si un chunk tira una advertencia que es esperable pero no querés que
aparezca en el reporte, podés ocultarlas con la opción
warning = FALSE
.
```{r warning = FALSE}
i <- sqrt(-1)
```
Finalmente, una opción tan poderosa como peligrosa es
cache = TRUE
. Lo que hace es que en vez de correr el código
de un chunk cada vez que kniteás el documento, guarda el
resultado del chunk en el disco para reutilizar la próxima vez que crees
el reporte. Esto es muy cómo si un chunk se un código que tarda mucho en
correr. Por ejemplo el siguiente chunk va a tardar 10 minutos en correr
la primera vez que knitees el reporte, pero luego va a ser mucho más
rápido:
```{r cache = FALSE}
datos <- funcion_que_tarda_10_minutos(x)
```
{knitr} es bastante inteligente y va a invalidar la cache si cambia
el código del chunk. Pero, ¿qué pasa si cambiás algo del código previo
que cambia el valor de x
o incluso el funcionamiento de
function_que_targa_10_minutos
? {knitr} no se da cuenta y va
a usar la cache, resultando que datos
va a tener un valor
incorrecto. Hay formas de decirle a {knitr} de qué depende cada chunk y
así obtener una cache más “inteligente” pero es algo que se vuelve
complicado muy rápido.
El resumen es usar la cache sólo cuando es imprescindible.
Desafío
Guardá el cógido de esta u otra sección yendo a Código → Descargar
Rmd arriba de todo a la derecha. Mirá los chunks y las opciones que
están puestas. ¿Por qué usamos cada opción en cada chunk?
LS0tCnRpdGxlOiAiUmVwb3J0ZXMgSUkiCm91dHB1dDogCiAgaHRtbF9kb2N1bWVudDoKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICBoaWdobGlnaHQ6IHRhbmdvCnBhcmFtczoKICBwYWlzOiBBcmdlbnRpbmEgICAgCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkKY2h1bmsgPC0gImBgYCIKYGBgCgpEdXJhbnRlIGVzdGUgY3Vyc28gZnVpc3RlIGNyZWFuZG8gdmFyaW9zIGRvY3VtZW50b3MgY29uIFIgTWFya2Rvd24gZW4gSFRNTC4gRXN0ZSBlcyB1biBmb3JtYXRvIHF1ZSB0aWVuZSB1biBtb250w7NuIGRlIGZsZXhpYmlsaWRhZCwgcGVybyBzZWd1cmFtZW50ZSBubyBlcyBlbCDDum5pY28gcXVlIG5lY2VzaXTDoXMuIENhc2kgc2VndXJvIHF1ZSBsb3MgaW5mb3JtZXMgbG9zIHRlbmdhcyBxdWUgcHJlc2VudGFyIGVuIGZvcm1hdG8gUERGIG8sIGluY2x1c28sIMKhZW4gcGFwZWwgaW1wcmVzbyEge1JtYXJrZG93bn0sIHkgdG9kbyB1biBhbXBsaW8gZWNvc2lzdGVtYSBkZSBvdHJvcyBwYXF1ZXRlcywgcGVybWl0ZSBnZW5lcmFyIGRvY3VtZW50b3MgZW4gbcO6bHRpcGxlcyBmb3JtYXRvcyB1c2FuZG8gZWwgbWlzbW8gYXJjaGl2byBkZSB0ZXh0byBwbGFuby4gCgojIyBFbGlnaWVuZG8gZWwgZm9ybWF0byBkZSBzYWxpZGEKCllhIGhhYnLDoXMgdmlzdG8gZXN0byBjdWFuZG8gY3Jlw6FzIHVuIGFyY2hpdm8gbWFya2Rvd24gbnVldm8sIFJTdHVkaW8gdGUgcGVybWl0ZSBlbGVnaXIgZW50cmUgdHJlcyBmb3JtYXRvcyBkZSBzYWxpZGE6CgohW10oaW1nL251ZXZvLXJtZC5wbmcpCgpDdcOhbCBlcyBlbCBmb3JtYXRvIGRlIHNhbGlkYSBkZSB1biBhcmNoaXZvIGRlIFIgTWFya2Rvd24gc2UgZGV0ZXJtaW5hIHByaW5jaXBhbG1lbnRlIGNvbiBsYSBvcGNpw7NuIGBvdXRwdXRgIGVuIGVsIGVuY2FiZXphZG8geWFtbC4gU2kgbWlyw6FzIGVsIGVuY2FiZXphZG8gZGVsIFthcmNoaXZvIFIgTWFya2Rvd24gZGUgZWplbXBsb10oZmlsZXMvbWktcHJpbWVyLXJtYXJrZG93bi5SbWQpIHZhcyBhIHZlciBxdWUgbGEgb3BjacOzbiBgb3V0cHV0YCBkaWNlIGBodG1sX2RvY3VtZW50YC4KCiFbXShpbWcvcm1kLWVqZW1wbG8tc2VjY2lvbmVzLnBuZykKCkVzZSBgaHRtbF9kb2N1bWVudGAgbm8gZXMgb3RyYSBjb3NhIHF1ZSB1bmEgZnVuY2nDs24gZGUge1JtYXJrZG93bn0gbGxhbWFkYSBgaHRtbF9kb2N1bWVudGAuIENvbW8gdGUgcG9kcsOhcyBpbWFnaW5hciwge1JtYXJrZG93bn0gdGllbmUgdW5hIHNlcmllIGRlIG90cmFzIGZ1bmNpb25lcyBxdWUgZGVmaW5lbiBmb3JtYXRvcyBkZSBzYWxpZGEuIExvcyBkb3MgcXVlIHNlZ3VyYW1lbnRlIHRlIHZhbiBhIHNlcnZpciBtw6FzIHNvbiBgcGRmX2RvY3VtZW50YCB5IGB3b3JkX2RvY3VtZW50YCBxdWUganVzdGFtZW50ZSBnZW5lcmFuIFBERnMgeSBhcmNoaXZvcyBkZSBXb3JkLCByZXNwZWN0aXZhbWVudGUuIAoKUGFyYSBjcmVhciB1biBkb2N1bWVudG8gZGUgUiBNYXJrZG93biBxdWUgZ2VuZXJlIHVuIGFyY2hpdm8gUERGIGJhc3RhIGNvbiBjYW1iaWFyIGVsIGBvdXRwdXRgIGVuIGVsIGVuY2FiZXphZG8gcG9yIGVzdG86CgpgYGB5YW1sCi0tLQpvdXRwdXQ6IHBkZl9kb2N1bWVudAotLS0KYGBgCgpQYXJhIHF1ZSBlbCBkb2N1bWVudG8gc2UgZ2VuZXJlIGNvcnJlY3RhbWVudGUgaGFjZSBmYWx0YSBpbnN0YWxhciBMYVRlWCwgcXVlIGVzIHVuIHNpc3RlbWEgZGUgY29tcG9zaWNpw7NuIGRlIHRleHRvcy4gQXVucXVlIHBhcmV6Y2EgbWVudGlyYSwgbGEgbWVqb3IgZm9ybWEgZGUgaW5zdGFsYXIgTGFUZVggcGFyYSB1c2FyIFIgTWFya2Rvd24gZXMgaW5zdGFsYW5kbyBlbCBwYXF1ZXRlIHtbdGlueXRleF0oaHR0cHM6Ly95aWh1aS5vcmcvdGlueXRleC8pfSBjb24gYGluc3RhbGwucGFja2FnZXMoInRpbnl0ZXgiKWAgeSBsdWVnbyBjb3JyZXIgYHRpbnl0ZXg6Omluc3RhbGxfdGlueXRleCgpYC4gRXN0byB2YSBhIGluc3RhbGFyIHVuYSB2ZXJzacOzbiBwZXF1ZcOxYSBkZSBMYVRlWCBlbiB1biBsdWdhciBkb25kZSBsdWVnbyB7Um1hcmtkb3dufSBsbyBwdWVkZSB1c2FyLiBFc3RhIGVzIGxhIGZvcm1hIGFsdGFtZW50ZSByZWNvbWVuZGFkYSBwYXJhIGdlbmVyYXIgUERGcyBjb24gUiBNYXJrZG93biBxdWUgdmEgYSBldml0YXJ0ZSB1biBtb250w7NuIGRlIGRvbG9yZXMgZGUgY2FiZXphLiAKCkFuw6Fsb2dhbWVudGUsIHBvZMOpcyBnZW5lcmFyIHVuIGFyY2hpdm8gZGUgd29yZCBjYW1iaWFuZG8gZWwgYG91dHB1dGAgYXPDrTogCgpgYGB5YW1sCi0tLQpvdXRwdXQ6IHdvcmRfZG9jdW1lbnQKLS0tCmBgYAoKWSB5YSBlc3TDoS4gRW4gbGEgZ3JhbiBtYXlvcsOtYSBkZSBsb3MgY2Fzb3Mgbm8gdmFzIGEgdGVuZXIgcXVlIG1vZGlmaWNhciBuYWRhIG3DoXMgZGVsIGPDs2RpZ28gbmkgZWwgdGV4dG8uIAoKCjo6OiB7LmFsZXJ0IC5hbGVydC1pbmZvfQoqKkRlc2Fmw61vKioKCkFnYXJyw6EgZWwgcmVwb3J0ZSBxdWUgZXN0dXZpc3RlIGFybWFuZG8gZW4gbG9zIGRlc2Fmw61vcyBvIGFsZ3VubyBxdWUgdXNhc3RlIGR1cmFudGUgZWwgY3Vyc28geSBjb21waWxhbG8gZW4gUERGIHkgbHVlZ28gZW4gV29yZC4KOjo6CgoKIyMgUGVyc29uYWxpemFuZG8gbGEgc2FsaWRhCgpDYWRhIGZ1bmNpw7NuIGRlIGZvcm1hdG8gdmllbmUgY29uIHN1cyBvcGNpb25lcyBkZSBwZXJzb25hbGl6YWNpw7NuIHF1ZSBwb2TDqXMgYWNjZWRlciBsZXllbmRvIHN1IGRvY3VtZW50YWNpw7NuLiBQYXJhIHZlciBsYSBkb2N1bWVudGFjacOzbiBkZSBgaHRtbF9kb2N1bWVudGAsIHVzYSBlc3RlIGNvbWFuZG86CgpgYGB7ciwgZXZhbCA9IEZBTFNFfQo/cm1hcmtkb3duOjpodG1sX2RvY3VtZW50CmBgYAoKVmFzIGEgdmVyIHF1ZSB0aWVuZSB1biBtb250w7NuIGRlIGFyZ3VtZW50b3MgcXVlIG1vZGlmaWNhbiBsYSBzYWxpZGEuIExhIGZvcm1hIGRlIHNldGVhciBlc3RvcyBhcmd1bWVudG9zIGVuIHVuIGRvY3VtZW50byBkZSBSIE1hcmtkb3duIGVzLCBkZSBudWV2bywgZW4gZWwgZW5jYWJlemFkby4gQ2FkYSBhcmd1bWVudG8gZGUgbGEgZnVuY2nDs24gZGUgc2FsaWRhIChgaHRtbF9kb2N1bWVudGAgZW4gZXN0ZSBjYXNvKSBlcyB1biBlbGVtZW50byBkZWJham8gZGUgbGEgZnVuY2nDs24gZGUgb3V0cHV0LiAKClBvciBlamVtcGxvLCBwYXJhIHF1ZSB1biBkb2N1bWVudG8gZGUgaHRtbCB0ZW5nYSB1bmEgdGFibGEgZGUgY29udGVuaWRvcyBoYXkgcXVlIHNldGVhciBlbCBhcmd1bWVudG8gYHRvY2AgKGRlICoqdCoqYWJsZSAqKm8qKmYgKipjKipvbnRlbnRzKSBhIGBUUlVFYC4gRW4gZWwgZW5jYWJlemFkbywgZXN0byBxdWVkYSBhc8OtOgoKCmBgYHlhbWwKLS0tCm91dHB1dDogCiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogVFJVRQotLS0KYGBgCgpDb252aWVuZSBtaXJhciBlc28gY29uIHVuIHBvY28gZGUgZGV0ZW5pbWllbnRvIHBvcnF1ZSByZXF1aWVyZSAidHJhZHVjaXIiIGPDs2RpZ28gZGUgUiAtLWN1YWwgbG9zIGFyZ3VtZW50b3MgZGUgdW5hIGZ1bmNpw7NuIHNlIGZpamFuIGVudHJlIHBhcsOpbnRlc2lzIHkgY29uIGA9YC0tIGVuIGPDs2RpZ28gZGUgeWFtbCAtLWRvbmRlIGxvcyBhcmd1bWVudG9zIGRlIGxhIGZ1bmNpw7NuIHNvbiB1bmEgbGlzdGEgY3V5b3MgZWxlbWVudG9zIHNlIGRlZmluZW4gY29uIGA6YC4gCgpFbiBSIGxvIHF1ZSB2ZW1vcyBjb21vIGBodG1sX2RvY3VtZW50KHRvYyA9IFRSVUUpYCBzZSB0cmFkdWNlIGEgeWFtbCBjb21vCgpgYGB5YW1sCmh0bWxfZG9jdW1lbnQ6CiAgdG9jOiBUUlVFCmBgYAoKU2kgdmFzIGEgbGEgYXl1ZGEgZGUgYHBkZl9kb2N1bWVudGAgdmFzIGEgdmVyIHF1ZSB0YW1iacOpbiB0aWVuZSB1biBhcmd1bWVudG8gbGxhbWFkbyBgdG9jYC4gQWxndW5vcyBhcmd1bWVudG9zIHNvbiBjb21wYXJ0aWRvcywgbG8gY3VhbCBoYWNlIHF1ZSBzZSBhw7puIG3DoXMgZsOhY2lsIGdlbmVyYXIgdW4gbWlzbW8gcmVwb3J0ZSBlbiBtdWNob3MgZm9ybWF0b3MgaGFjaWVuZG8gbXV5IHBvY29zIGNhbWJpb3MuIAoKVW5hIGZvcm1hIHLDoXBpZGEgZGUgaGFjZXIgdHVzIGluZm9ybWVzIG3DoXMgdmlzdG9zb3MgZXMgY2FtYmlhcmxlIGVsIHRlbWEgdmlzdWFsLiBgaHRtbF9kb2N1bWVudGAgcGVybWl0ZSBlbGVnaXIgZW50cmUgdW5hIHNlcmllIGRlIHRlbWFzIHVzYW5kbyBlbCBhcmd1bWVudG8gYHRoZW1lYC4gUG9yIGVqZW1wbG8sIHBvbmllbmRvIGVzdG8gZW4gZWwgZW5jYWJlemFkbywgZ2VuZXLDoXMgdW4gZG9jdW1lbnRvIEhUTUwgY29uIHVuIGZvbmRvIG9zY3VybwoKCmBgYHlhbWwKb3V0cHV0OiAKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiBUUlVFCiAgICB0aGVtZTogZGFya2x5CmBgYAoKOjo6IHsuYWxlcnQgLmFsZXJ0LWluZm99CioqRGVzYWbDrW8qKgoKQW5kw6EgYSBsYSBheXVkYSBkZSBgaHRtbF9kb2N1bWVudGAgeSBmaWphdGUgY3XDoWxlcyBzb24gbG9zIHZhbG9yZXMgdsOhbGlkb3MgcGFyYSBlbCBhcmd1bWVudG8gYHRoZW1lYC4gwqFQcm9iw6EgYWxndW5vcyEKOjo6CgoKCiMjIFJlcG9ydGVzIHBhcmFtZXRyaXphZG9zCgpFcyBtdXkgY29tw7puIHRlbmVyIHF1ZSBoYWNlciB1biByZXBvcnRlIGN1eW8gcmVzdWx0YWRvIGRlcGVuZGEgZGUgY2llcnRvcyBwYXLDoW1ldHJvcy4gCgpQb3IgZWplbXBsbywgcG9kcsOtYXMgdGVuZXIgdW4gcmVwb3J0ZSBxdWUgYW5hbGl6YSBsYSBldm9sdWNpw7NuIGRlIGxhIHRlbXBlcmF0dXJhIG3DrW5pbWEgeSBtw6F4aW1hIHBhcmEgYWxndW5hIGVzdGFjacOzbiBkZWwgU2VydmljaW8gTWV0ZW9yb2zDs2dpY28gTmFjaW9uYWwuCgpgYGB7ciwgbWVzc2FnZT1GQUxTRX0KbGlicmFyeShtZXRSKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHRpZHlyKQpsaWJyYXJ5KGdncGxvdDIpCgpmZWNoYXMgPC0gc2VxLkRhdGUobHVicmlkYXRlOjp5bWQoMjAyMjA4MDEpLCBsdWJyaWRhdGU6OnltZCgyMDIyMDgzMSksIGJ5ID0gIjEgZGF5IikKCm9ic2VydmFjaW9uZXMgPC0gR2V0U01ORGF0YShmZWNoYXMsIHR5cGUgPSAiZGFpbHkiKQoKb2JzZXJ2YWNpb25lcyAlPiUgCiAgZmlsdGVyKHN0YXRpb24gPT0gIlNBTlRBIFJPU0EgQUVSTyIpICU+JSAKICBwaXZvdF9sb25nZXIodG1heDp0bWluLCBuYW1lc190byA9ICJ2YXJpYWJsZSIsIHZhbHVlc190byA9ICJ2YWxvciIpICU+JSAKICBnZ3Bsb3QoYWVzKGRhdGUsIHZhbG9yLCBjb2xvciA9IHZhcmlhYmxlKSkgKwogIGdlb21fbGluZSgpICsKICBnZW9tX3BvaW50KCkgCmBgYAoKU2kgYWhvcmEgcXVlcsOpcyBoYWNlciBlbCBtaXNtbyByZXBvcnRlIHBlcm8gcGFyYSBMYSBRdWlhY2EsIHRlbsOpcyBxdWUgYWJyaXIgZWwgYXJjaGl2byB5IG1vZGlmaWNhciBsYSBsbGFtYWRhIGEgYGZpbHRlcmAgcGFyYSBxdWVkYXJ0ZSBzw7NsbyBjb24gZXNlIHBhw61zOgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KbGlicmFyeShtZXRSKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHRpZHlyKQpsaWJyYXJ5KGdncGxvdDIpCgpmZWNoYXMgPC0gc2VxLkRhdGUobHVicmlkYXRlOjp5bWQoMjAyMjA4MDEpLCBsdWJyaWRhdGU6OnltZCgyMDIyMDgzMSksIGJ5ID0gIjEgZGF5IikKCm9ic2VydmFjaW9uZXMgPC0gR2V0U01ORGF0YShmZWNoYXMsIHR5cGUgPSAiZGFpbHkiKQoKb2JzZXJ2YWNpb25lcyAlPiUgCiAgZmlsdGVyKHN0YXRpb24gPT0gIkxBIFFVSUFDQSBPQlNFUlZBVE9SSU8iKSAlPiUgCiAgcGl2b3RfbG9uZ2VyKHRtYXg6dG1pbiwgbmFtZXNfdG8gPSAidmFyaWFibGUiLCB2YWx1ZXNfdG8gPSAidmFsb3IiKSAlPiUgCiAgZ2dwbG90KGFlcyhkYXRlLCB2YWxvciwgY29sb3IgPSB2YXJpYWJsZSkpICsKICBnZW9tX2xpbmUoKSArCiAgZ2VvbV9wb2ludCgpIApgYGAKClNpIGVsIHJlcG9ydGUgZXMgbGFyZ28geSB1c2EgZWwgbm9tYnJlIGRlIGxhIGVzdGFjacOzbiBlbiBtw7psdGlwbGVzIGx1Z2FyZXMgY2FtYmlhciAiU0FOVEEgUk9TQSBBRVJPIiBwb3IgIkxBIFFVSUFDQSBPQlNFUlZBVE9SSU8iIHB1ZWRlIHNlciB0ZWRpb3NvIHkgcHJvcGVuc28gYSBlcnJvciwgeWEgcXVlIHRlIG9ibGlnYSBhIG1vZGlmaWNhciBtdWNoYXMgcGFydGVzIGRlbCBjw7NkaWdvLiBZIHNpIGRlc3B1w6lzIHRlbsOpcyBxdWUgaGFjZXIgZWwgbWlzbW8gcmVwb3J0ZSBwYXJhIFVzaHVhaWEuLi4gCgpFbiBlc3RhcyBzaXR1YWNpb25lcyBwb2TDqXMgY3JlYXIgdW4gcmVwb3J0ZSAqcGFyYW1ldHJpemFkbyouIExhIGlkZWEgZXMgcXVlIGVsIHJlcG9ydGUgdGllbmUgdW5hIHNlcmllIGRlIHBhcsOhbWV0cm9zIHF1ZSBwdWVkZSBtb2RpZmljYXIgbGEgc2FsaWRhLiBFcyBjb21vIHNpIGVsIGFyY2hpdm8gZGUgUiBNYXJrZG93biBmdWVyYSB1bmEgZ3JhbiBmdW5jacOzbiBjb24gc3VzIGFyZ3VtZW50b3MhCgpQYXJhIGdlbmVyYXIgdW4gcmVwb3J0ZSBwYXJhbWV0cml6YWRvIGhheSBxdWUgYWdyZWdhciB1biBlbGVtZW50byBsbGFtYWRvIGBwYXJhbXNgIGFsIGVuY2FiZXphZG8gY29uIGxhIGxpc3RhIGRlIHBhcsOhbWV0cm9zIHkgc3VzIHZhbG9yZXMgcG9yIGRlZmF1bHQuCgpgYGB5YW1sCnBhcmFtczoKICBlc3RhY2lvbjogU0FOVEEgUk9TQSBBRVJPCmBgYAoKTHVlZ28sIGVuIGVsIGPDs2RpZ28gZGUgUiB2YXMgYSB0ZW5lciBhY2Nlc28gYSB1bmEgdmFyaWFibGUgbGxhbWFkYSBgcGFyYW1zYCBxdWUgZXMgdW5hIGxpc3RhIHF1ZSBjb250aWVuZSBsb3MgcGFyw6FtZXRyb3MgeSBzdSB2YWxvci4gUGFyYSBhY2NlZGVyIGFsIHZhbG9yIGRlIGNhZGEgcGFyw6FtZXRyb3Mgc2UgdXNhIGVsIG9wZXJhZG9yIGAkYCBkZSBsYSBzaWd1aWVudGUgbWFuZXJhOgoKYGBge3J9CnBhcmFtcyRlc3RhY2lvbgpgYGAKRGUgZXN0YSBtYW5lcmEsIGVsIGPDs2RpZ28gb3JpZ2luYWwgc2UgcHVlZGUgbW9kaWZpY2FyIHBhcmEgdXNhciBlbCB2YWxvciBkZSBsYSBlc3RhY2nDs24gYWxtYWNlbmFkbyBlbiBgcGFyYW1zJGVzdGFjaW9uYAoKYGBge3IsIGV2YWwgPSBGQUxTRX0KbGlicmFyeShtZXRSKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHRpZHlyKQpsaWJyYXJ5KGdncGxvdDIpCgpmZWNoYXMgPC0gc2VxLkRhdGUobHVicmlkYXRlOjp5bWQoMjAyMjA4MDEpLCBsdWJyaWRhdGU6OnltZCgyMDIyMDgzMSksIGJ5ID0gIjEgZGF5IikKCm9ic2VydmFjaW9uZXMgPC0gR2V0U01ORGF0YShmZWNoYXMsIHR5cGUgPSAiZGFpbHkiKQoKb2JzZXJ2YWNpb25lcyAlPiUgCiAgZmlsdGVyKHN0YXRpb24gPT0gcGFyYW1zJHN0YXRpb24pICU+JSAKICBwaXZvdF9sb25nZXIodG1heDp0bWluLCBuYW1lc190byA9ICJ2YXJpYWJsZSIsIHZhbHVlc190byA9ICJ2YWxvciIpICU+JSAKICBnZ3Bsb3QoYWVzKGRhdGUsIHZhbG9yLCBjb2xvciA9IHZhcmlhYmxlKSkgKwogIGdlb21fbGluZSgpICsKICBnZW9tX3BvaW50KCkgCmBgYAoKClkgYWhvcmEgZWwgbWlzbW8gY8OzZGlnbyBwdWVkZSBmdW5jaW9uYXIgcGFyYSBkaXN0aW50YXMgZXN0YWNpb25lcy4gUGFyYSBjcmVhciByZXBvcnRlcyBkaXN0aW50b3MgcGFyYSBjYWRhIGVzdGFjacOzbiBzw7NsbyBoYXkgcXVlIG1vZGlmaWNhciBlbCB2YWxvciBkZWwgcGFyw6FtZXRybyBlbiBlbCBlbmNhYmV6YWRvOgoKCmBgYHlhbWwKcGFyYW1zOgogIHBhaXM6IExBIFFVSUFDQSBPQlNFUlZBVE9SSU8KYGBgCgoKOjo6IHsuYWxlcnQgLmFsZXJ0LWluZm99CioqRGVzYWbDrW8qKgoKQWdyZWfDoSBhbCBtZW5vcyB1biBwYXLDoW1ldHJvIGFsIHJlcG9ydGUgcXVlIHZlbsOtcyBhcm1hbmRvLiAKOjo6CgoKIyMgQ29udHJvbCBkZSBjaHVua3MKClNpIHJlY29yZMOhcyBsbyBxdWUgdmltb3MgZW4gbGEgc2VjY2nDs24gZGUgW3JlcG9ydGVzIEldKDEwLXJlcG9ydGVzLUkuaHRtbCkgbWVuY2lvbmFtb3MgcXVlIHVuIGNodW5rIHRpZW5lIHVuYSBwaW50YSBjb21vIGVzdGE6CgogICAgYHIgY2h1bmtge3Igbm9tYnJlLWRlbC1jaHVua30KICAgIAogICAgYHIgY2h1bmtgCiAgICAKClBvbmVybGUgbm9tYnJlIGFsIGNodW5rIG5vIGVzIG9ibGlnYXRvcmlvIHBlcm8gZXN0w6EgYnVlbm8gcGFyYSB0ZW5lciB1bmEgaWRlYSBkZSBxdcOpIGhhY2UgY2FkYSB1bm8sIGxvIGN1YWwgc2UgdnVlbHZlIG3DoXMgaW1wb3J0YW50ZSBhIG1lZGlkYSBxdWUgdW4gcmVwb3J0ZSBzZSB2dWVsdmUgbcOhcyBsYXJnbyB5IGNvbXBsZWpvLiBQZXJvIGxvIHF1ZSBubyBkaWppbW9zIGVzIHF1ZSBhZGVtw6FzIGRlbCBub21icmUsIGVudHJlIGxhcyBsbGF2ZSBzZSBwdWVkZW4gcG9uZXIgdW4gbW9udMOzbiBkZSBvcGNpb25lcyBxdWUgY2FtYmlhbiBlbCBjb21wb3J0YW1pZW50byB5IGxhIGFwYXJpZW5jaWEgZGVsIHJlc3VsdGFkbyBkZWwgY2h1bmsuIAoKUGFyYSBjYW1iaWFyIGxhcyBvcGNpb25lcyBkZSB1biBjaHVuaywgbG8gw7puaWNvIHF1ZSBoYXkgcXVlIGhhY2VyIGVzIGxpc3RhcmxhcyBkZW50cm8gZGUgbG9zIGNvcmNoZXRlcy4gUG9yIGVqZW1wbG86CgoKICAgIGByIGNodW5rYHtyIG5vbWJyZS1kZWwtY2h1bmssIGVjaG8gPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQogICAgCiAgICBgciBjaHVua2AKICAgIAoKCkhheSB1bmEgc2VyaWUgZGUgb3BjaW9uZXMgcGFydGljdWxhcm1lbnRlIGltcG9ydGFudGUgZXMgbGEgcXVlIGNvbnRyb2xhIHNpIGVsIGPDs2RpZ28gc2UgZWplY3V0YSB5IHNpIGVsIHJlc3VsdGFkbyBkZWwgY8OzZGlnbyB2YSBhIHF1ZWRhciBlbiBlbCByZXBvcnRlIG8gbm86CgoKKiBgZXZhbCA9IEZBTFNFYCBldml0YSBxdWUgc2UgY29ycmEgZWwgY8OzZGlnbyBkZWwgY2h1bmssIGRlIG1hbmVyYSBxdWUgdGFtcG9jbyB2YSBhIG1vc3RyYXIgcmVzdWx0YWRvcy4gRXMgw7p0aWwgcGFyYSBtb3N0cmFyIGPDs2RpZ29zIGRlIGVqZW1wbG8gc2kgZXN0w6FzIGVzY3JpYmllbmRvLCBwb3IgZWplbXBsbyB1biBkb2N1bWVudG8gcGFyYSBlbnNlw7FhciBSLgoKCiogYGVjaG8gPSBGQUxTRWAgY29ycmUgZWwgY8OzZGlnbyBkZWwgY2h1bmsgeSBtdWVzdHJhIGxvcyByZXN1bHRhZG9zLCBwZXJvIG9jdWx0YSBlbCBjw7NkaWdvIGVuIGVsIHJlcG9ydGUuIEVzdG8gZXMgw7p0aWwgcGFyYSBlc2NyaWJpciByZXBvcnRlcyBwYXJhIHBlcnNvbmFzIHF1ZSBubyBuZWNlc2l0YW4gdmVyIGVsIGPDs2RpZ28gZGUgUiBxdWUgZ2VuZXLDsyBlbCBncsOhZmljbyBvIHRhYmxhLiAKCiogYGluY2x1ZGUgPSBGQUxTRWAgY29ycmUgZWwgY8OzZGlnbyBwZXJvIG9jdWx0YSB0YW50byBlbCBjw7NkaWdvIGNvbW8gbG9zIHJlc3VsdGFkb3MuIEVzIMO6dGlsIHBhcmEgdXNhciBlbiBjaHVua3MgZGUgY29uZmlndXJhY2nDs24gZ2VuZXJhbCBkb25kZSBjYXJnYXMgbGFzIGxpYnJlcsOtYXMuIAoKClNpIGVzdMOhcyBlc2NyaWJpZW5kbyB1biBpbmZvcm1lIGVuIGVsIHF1ZSBubyBxdWVyw6lzIHF1ZSBzZSBtdWVzdHJlIG5pbmfDum4gY8OzZGlnbywgYWdyZWdhcmxlIGBlY2hvID0gRkFMU0VgIGEgY2FkYSBjaHVuayBudWV2byBzZSB2dWVsdmUgdGVkaW9zby4gTGEgc29sdWNpw7NuIGVzIGNhbWJpYXIgbGEgb3BjacOzbiBkZSBmb3JtYSBnbG9iYWwgZGUgbWFuZXJhIHF1ZSBhcGxpcXVlIGEgdG9kb3MgbG9zIGNodW5rcy4gRXN0byBzZSBoYWNlIG1lZGlhbnRlIGxhIGZ1bmNpw7NuIGBrbml0cjo6b3B0c19jaHVuayRzZXQoKWAsIHF1ZSBzZXRlYSBsYXMgb3BjaW9uZXMgZ2xvYmFsZXMgZGUgbG9zIGNodW5rcyBxdWUgbGUgc2lndWVuLiBTaSBxdWXFlWVzIHF1ZSB0b2RvcyBsb3MgY2h1bmtzIHRlbmdhbiBgZWNobyA9IFRSVUVgIGNyZWFyw61hcyB1biBjaHVuayBhc8OtOgoKCiAgICBgciBjaHVua2B7ciBzZXR1cCwgaW5jbHVkZSA9IEZBTFNFfQogICAga25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQogICAgYHIgY2h1bmtgCiAgICAKR2VuZXJhbG1lbnRlIHRpZW5lIHNlbnRpZG8gcG9uZXIgZXN0byBlbiBlbCBwcmltZXIgY2h1bmsgZGUgdW4gZG9jdW1lbnRvLCBxdWUgY29tbyBzdWVsZSBzZXIgY3Vlc3Rpb25lcyBkZSBjb25maWd1cmFjacOzbiBkZWwgcmVwb3J0ZSwgdGFtYmnDqW4gY29udmllbmUgcG9uZXJsZSBgaW5jbHVkZSA9IEZBTFNFYC4KCkhhYnLDoXMgdmlzdG8gcXVlIGEgdmVjZSBhbGd1bmFzIGZ1bmNpb25lcyBlc2N1cGVuIG1lbnNhamVzIHNvYnJlIGxvIHF1ZSBoYWNlbi4gUG9yIGVqZW1wbG8sIGN1YW5kbyBgcmVhZF9jc3ZgIGxlZSB1biBhcmNoaXZvIGRlc2NyaWJlIGVsIHRpcG8gZGUgZGF0byBkZSBjYWRhIGNvbHVtbmE6CgpgYGB7cn0KYmFyaWxvY2hlIDwtIHJlYWRfY3N2KCJkYXRvcy9iYXJpbG9jaGVfZW5saW1waW8uY3N2IikKYGBgCgpFc3RvIGVzIMO6dGlsIGN1YW5kbyB1bm8gZXN0w6EgaGFjaWVuZG8gdHJhYmFqbyBpbnRlcmFjdGl2byBwZXJvIGVuIGdlbmVyYWwgbm8gcXVpZXJlIHF1ZSBxdWVkZSBlbiBlbCByZXBvcnRlLiBQYXJhIHF1ZSBubyBtdWVzdHJlIGVzdG9zIG1lbnNhamVzIGJhc3RhIGNvbiBwb25lciBsYSBvcGNpw7NuIGBtZXNzYWdlID0gRkFMU0VgCgogICAgYHIgY2h1bmtge3IgbWVzc2FnZSA9IEZBTFNFfQogICAgYmFyaWxvY2hlIDwtIHJlYWRfY3N2KCJkYXRvcy9iYXJpbG9jaGVfZW5saW1waW8uY3N2IikKICAgIGByIGNodW5rYAogICAgCkVuIGdlbmVyYWwgbm8gcGFzYSBuYWRhIHNpIGlnbm9yw6FzIGxvcyBtZW5zYWplcy4gU29uIGN1ZXN0aW9uZXMgZGlhZ27Ds3N0aWNhcyBleHRyYSBxdWUgc2lydmVuIHBhcmEgcXVlIHZvcywgY29tbyBodW1hbm8sIHRlIGVudGVyZXMgZGUgbG8gcXVlIGhpem8gdW5hIGZ1bmNpw7NuLiBEaXN0aW50byBzb24gbGFzIGFkdmVydGVuY2lhcywgbyAid2FybmluZ3MiLiBVbmEgYWR2ZXJ0ZW5jaWEgdGUgZXN0w6EgZGljaWVuZG8gcXVlIGhheSBhbGdvICJyYXJvIiBlbiBlbCBjw7NkaWdvIHF1ZSBwdWVkZSBzaWduaWZpY2FyIHF1ZSBoYXkgYWxnbyBtYWwuIE5vIGxsZWdhIGFsIG5pdmVsIGRlIGVycm9yLCBxdWUgZXMgYWxnbyBxdWUgbGl0ZXJhbG1lbnRlICJubyBjb21wdXRhIi4gUG9yIGVqZW1wbG8sIGBzcXJ0YCB0aXJhIHVuYSBhZHZlcnRlbmNpYSBjdWFuZG8gcmVjaWJlIG7Dum1lcm9zIG5lZ2F0aXZvcy4KCmBgYHtyfQppIDwtIHNxcnQoLTEpCmBgYAoKU2kgdW4gY2h1bmsgdGlyYSB1bmEgYWR2ZXJ0ZW5jaWEgcXVlIGVzIGVzcGVyYWJsZSBwZXJvIG5vIHF1ZXLDqXMgcXVlIGFwYXJlemNhIGVuIGVsIHJlcG9ydGUsIHBvZMOpcyBvY3VsdGFybGFzIGNvbiBsYSBvcGNpw7NuIGB3YXJuaW5nID0gRkFMU0VgLgoKICAgIGByIGNodW5rYHtyIHdhcm5pbmcgPSBGQUxTRX0KICAgIGkgPC0gc3FydCgtMSkKICAgIGByIGNodW5rYAogICAgCgpGaW5hbG1lbnRlLCB1bmEgb3BjacOzbiB0YW4gcG9kZXJvc2EgY29tbyBwZWxpZ3Jvc2EgZXMgYGNhY2hlID0gVFJVRWAuIExvIHF1ZSBoYWNlIGVzIHF1ZSBlbiB2ZXogZGUgY29ycmVyIGVsIGPDs2RpZ28gZGUgdW4gY2h1bmsgY2FkYSB2ZXogcXVlICprbml0ZcOhcyogZWwgZG9jdW1lbnRvLCBndWFyZGEgZWwgcmVzdWx0YWRvIGRlbCBjaHVuayBlbiBlbCBkaXNjbyBwYXJhIHJldXRpbGl6YXIgbGEgcHLDs3hpbWEgdmV6IHF1ZSBjcmVlcyBlbCByZXBvcnRlLiBFc3RvIGVzIG11eSBjw7NtbyBzaSB1biBjaHVuayBzZSB1biBjw7NkaWdvIHF1ZSB0YXJkYSBtdWNobyBlbiBjb3JyZXIuIFBvciBlamVtcGxvIGVsIHNpZ3VpZW50ZSBjaHVuayB2YSBhIHRhcmRhciAxMCBtaW51dG9zIGVuIGNvcnJlciBsYSBwcmltZXJhIHZleiBxdWUga25pdGVlcyBlbCByZXBvcnRlLCBwZXJvIGx1ZWdvIHZhIGEgc2VyIG11Y2hvIG3DoXMgcsOhcGlkbzogCgoKICAgIGByIGNodW5rYHtyIGNhY2hlID0gRkFMU0V9CiAgICBkYXRvcyA8LSBmdW5jaW9uX3F1ZV90YXJkYV8xMF9taW51dG9zKHgpCiAgICBgciBjaHVua2AKCgp7a25pdHJ9IGVzIGJhc3RhbnRlIGludGVsaWdlbnRlIHkgdmEgYSBpbnZhbGlkYXIgbGEgY2FjaGUgc2kgY2FtYmlhIGVsIGPDs2RpZ28gZGVsIGNodW5rLiBQZXJvLCDCv3F1w6kgcGFzYSBzaSBjYW1iacOhcyBhbGdvIGRlbCBjw7NkaWdvIHByZXZpbyBxdWUgY2FtYmlhIGVsIHZhbG9yIGRlIGB4YCBvIGluY2x1c28gZWwgZnVuY2lvbmFtaWVudG8gZGUgYGZ1bmN0aW9uX3F1ZV90YXJnYV8xMF9taW51dG9zYD8ge2tuaXRyfSBubyBzZSBkYSBjdWVudGEgeSB2YSBhIHVzYXIgbGEgY2FjaGUsIHJlc3VsdGFuZG8gcXVlIGBkYXRvc2AgdmEgYSB0ZW5lciB1biB2YWxvciBpbmNvcnJlY3RvLiBIYXkgZm9ybWFzIGRlIGRlY2lybGUgYSB7a25pdHJ9IGRlIHF1w6kgZGVwZW5kZSBjYWRhIGNodW5rIHkgYXPDrSBvYnRlbmVyIHVuYSBjYWNoZSBtw6FzICJpbnRlbGlnZW50ZSIgcGVybyBlcyBhbGdvIHF1ZSBzZSB2dWVsdmUgY29tcGxpY2FkbyBtdXkgcsOhcGlkby4gCgpFbCByZXN1bWVuIGVzIHVzYXIgbGEgY2FjaGUgc8OzbG8gY3VhbmRvIGVzIGltcHJlc2NpbmRpYmxlLgoKOjo6IHsuYWxlcnQgLmFsZXJ0LWluZm99CioqRGVzYWbDrW8qKgoKR3VhcmTDoSBlbCBjw7NnaWRvIGRlIGVzdGEgdSBvdHJhIHNlY2Npw7NuIHllbmRvIGEgQ8OzZGlnbyDihpIgRGVzY2FyZ2FyIFJtZCBhcnJpYmEgZGUgdG9kbyBhIGxhIGRlcmVjaGEuIE1pcsOhIGxvcyBjaHVua3MgeSBsYXMgb3BjaW9uZXMgcXVlIGVzdMOhbiBwdWVzdGFzLiDCv1BvciBxdcOpIHVzYW1vcyBjYWRhIG9wY2nDs24gZW4gY2FkYSBjaHVuaz8KCjo6OgoKPGRpdiBjbGFzcz0iYnRuLWdyb3VwIiByb2xlPSJncm91cCIgYXJpYS1sYWJlbD0iTmF2ZWdhY2nDs24iPgogIDxhIGhyZWY9ICIxMC1ncmFmaWNvcy1JSUkuaHRtbCIgY2xhc3MgPSAiYnRuIGJ0bi1wcmltYXJ5Ij5BbnRlcmlvcjwvYT4KICA8YSBocmVmPSAiMTItcHVibGljYXIuaHRtbCIgY2xhc3MgPSAiYnRuIGJ0bi1wcmltYXJ5Ij5TaWd1aWVudGU8L2E+CjwvZGl2Pg==