1. Presentación sobre el manejo de “chunks” o “trozos” de código de R.

Una de las grandes cualidades de un documento en formato R Markdown consiste en la posibilidad de incluir códigos de R, evaluarlos y, a su vez, insertar los resultados en un texto. Para ello es muy útil la paquetería knitr()de R, que cuenta con diversos comandos o scripts. Estos ayudar a ajustar la manera de evaluar e integrar tanto los códigos de R utilizados, así como sus resultados (ya sean en formato de valores numéricos, tablas de contenido o figuras y gráficas) en el documento de salida o output renderizado para el proceso de integración de los elementos de un documento de R Markdown.

En este documento se revisarán algunos de los comandos básicos de knitr() que permiten manipular y ajustar los comandos de R, bajo las necesidades del autor del documento al momento de construir e integra códigos y resultados en el documento de salida (output).
Para ello primero se revisarán comandos básicos para insertar y configurar de manera general en un documento en formato R Markdown, y posteriormente se revisarán elementos particulares para configurar a estos códigos tanto sobre su evaluación, presentación de los resultados, inserción de figuras y gráficas. Y también se revisarán comandos para la generación de tablas de contenido.

2. Inserción de “trozos” de código de R o chunks.

La creación de códigos de R para el análisis de datos y su posterior inserción en un documento de salida deseado (ya sea en formato .html, .pdf o .doc) se realiza a través de las secciones de trozos de código o chunks. Estos se diseñan iniciando con 3 acentos iniciales y 3 acentos finales, y en su interior se colocan corchetes en el que se indica el lenguaje del código, ```{r and }. En este caso el lenguaje es el mismo de R, y su estructura es:

```{r} #aquí se indica el lenguaje
paste("Hola", "Mundo") #este corresponde al código insertado
```

Esta estructura del chunk se puede escribir manualmente o también se puede insertar mediante el cursor dándole click izquierdo al botón de insert en la parte superior de la ventana de source. Otra manera de realizarlo es a través de la combinación de teclas: Ctrl + Alt + I (Cmd + Option + I en macOS).

El resultado del chunk es:

#Esto es un "chunk"
library(tidyverse) #Esto es un código de R
## ── Attaching packages ─────────────────────────────────────── tidyverse 1.3.1 ──
## ✓ ggplot2 3.3.5     ✓ purrr   0.3.4
## ✓ tibble  3.1.6     ✓ dplyr   1.0.7
## ✓ tidyr   1.1.4     ✓ stringr 1.4.0
## ✓ readr   2.1.2     ✓ forcats 0.5.1
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## x dplyr::filter() masks stats::filter()
## x dplyr::lag()    masks stats::lag()

Al renderizar el documento de R Markdown se observa que en el documento de salida, por defecto, los elementos que aparecen por defecto son:

  1. el código de R (en un recuadro sombreado),
  2. el resultado del código (en un recuadro transparente),
  3. advertencias al correr el código (en un recuadro adicional, si ocurre).

Estos elementos se deben de “limpiar” al elaborar un reporte de investigación. En internet se puede encontrar una cantidad de elementos de apoyo, uno consiste en la página web de R Markdown elaborara por R Studio. También se cuenta con la hoja de apoyo o cheat sheet de R Markdown.
Finalmente, se recomienda el uso de la guía de referencia de R Markdown elaborada por R Studio.

A continuación se revisan algunos comandos para hacer dicha limpieza, empezando por las opciones para evaluar los códigos, configurar los resultados, inserción de gráficas y de tablas.
Además se utilizarán los datos de la encuesta correspondiente a la ola 2021 de Latinobarómetro

2.1. Evaluación de los chunks.

La evaluación de un código se refiere a si se desea que éste sea ejecutado, así como a si se desea que aparezca en impreso en el documento de salida.

El comando eval = puede adoptar dos valores: TRUE (por defecto) y FALSE. En el caso de señalar la última opción, entonces se indica que el código no sea ejecutado al momento de renderizar el documento R Markdown.

```{r eval=FALSE} #chunk configurado
mean(datoslb $ EDAD)
```

El resultado en el documento de salida solo consiste en presentar el código escrito pero no su resultado:

mean(datoslb $ EDAD)

El comando include = también puede adoptar dos valores posibles: TRUE (por defecto) o FALSE. En esta última opción lo que se indica es que el código sí sea ejecutado al momento de renderizar pero, a su vez, que el código mismo no sea incluido en el documento de salida.

```{r include=FALSE} #chunk configurado.
prom_edad <- mean(datoslb $ EDAD)
prom_edad
```

El resultado en el documento de salida no muestra el código ni el resultado en el documento de salida, pero sí se arroja el valor final en la venta de la consola y, aquí, también se guarda al objeto creado prom_edad:

2.2. Presentación de los resultados.

La presentación de resultados de los códigos evaluados también se puede configurar mediante varios comandos.

  1. El comando collapse= puede adoptar dos valores posibles: TRUE o FALSE (por defecto). Este comando permite juntar los bloques de resultados para, así, ahorrar espacio.
```{r collapse=TRUE} #chunk configurado.
prom_edad <- mean(datoslb $ EDAD)
prom_edad
```

El resultado es:

prom_edad <- mean(datoslb $ EDAD)
prom_edad
## [1] 40.56365
  1. Otro comando es echo=, sus valores son TRUE(por defecto) y FALSE. Este último indica que no se deba imprimir el código en el documento de salida. Por ejemplo:
```{r echo=FALSE} #chunk configurado.
prom_edad <- mean(datoslb $ EDAD)
prom_edad
```

El resultado es que no se imprimirá el código en el documento de salida, pero sí se evalúa el código.

## [1] 40.56365
  1. El comando results= puede adoptar 3 valores: hide, hold y asis. En el caso de hide no se muestran los resultados del código en el documento de salida. En el caso de hold se retrasa mostrar todos los resultados hasta el final del código. El argumento de asis separa los resultados sin re formatearlos.

El comando hide:

```{r results=`hide`} #chunk configurado.
prom_edad <- mean(datoslb $ EDAD)
prom_edad
```

El resultado es:

prom_edad <- mean(datoslb $ EDAD)
prom_edad

El comando hold:

```{r results=`hide`} #chunk configurado.
prom_edad <- mean(datoslb $ EDAD)
prom_edad
sd_edad <- sd(datoslb $ EDAD)
sd_edad
```

El resultado es:

prom_edad <- mean(datoslb $ EDAD)
prom_edad
sd_edad <- sd(datoslb $ EDAD)
sd_edad
## [1] 40.56365
## [1] 16.52447

El comando asis:

```{r results='asis'} #chunk configurado.
prom_edad <- mean(datoslb $ EDAD)
prom_edad
sd_edad <- sd(datoslb $ EDAD)
sd_edad
```

El resultado es:

prom_edad <- mean(datoslb $ EDAD)
prom_edad

[1] 40.56365

sd_edad <- sd(datoslb $ EDAD)
sd_edad

[1] 16.52447

  1. El comando error= puede adoptar dos valores: TRUE y FALSE (por defecto). El valor de TRUE indica que sí se muestren los mensajes de error, en caso de que el código contenga algún problema en el diseño o su evaluación. Asimismo, se debe tomar en cuenta que cuando error=FALSE (por defecto), además de arrojar el mensaje de error en la consola, también se detendrá el proceso de renderización del documento de salida, mientras que el valor de error=TRUE sí permite la renderización del documento, y en el output se presenta dicho mensaje de error.
```{r error=TRUE} #chunk configurado.
prom_edad <- mean(datoslb $ EDAD)
prom_EDAD #este código está mal escrito
```

El resultado es:

prom_edad <- mean(datoslb $ EDAD)
prom_EDAD #este código está mal escrito a propósito
## Error in eval(expr, envir, enclos): object 'prom_EDAD' not found
  1. El comando de message= puede adoptar dos valores: TRUE (por defecto) y FALSE permite controlar la presentación de mensajes aclaratorios sobre la evaluación de los códigos. El valor de message=FALSE indica que no se mostrarán los mensajes adicionales generados por R.
```{r message=FALSE} #chunk configurado.
library(tidyverse)
```

En el resultado se observa que no se arroja ningún mensaje del sistema sobre el proceso de instalación de tidyverse así como sus conflictos (véase la activación de tidyverse en el punto 2).

library(tidyverse)
  1. En cambio, también existe el comando warning= que puede adoptar dos valores: TRUE (por defecto) y FALSE, y este último permite indicarle al sistema que no se muestren las advertencias de posibles problemas en el documento de salida, tras la evaluación del código.
```{r warning=FALSE} #chunk configurado.
library(tidyverse)
```

El resultado será que se anulan los mensajes de advertencias en el output:

library(tidyverse)

2.2.1. Insertar resultados de R directamente en el texto.

También se pueden insertar valores que son resultados de correr código de R en los renglones de texto para, así, no estar escribiendo dicho valores. Para ello se utiliza la estructura de r función(objeto).
Por ejemplo:

```
"El promedio de anchura de los pétalos fue de `r mean(iris $ Sepal.Width` pulgadas".
```

Donde el resultado es:

“El promedio de anchura de los pétalos fue de 3.0573333 pulgadas”.

2.3. Incluir ecuaciones en texto.

En R Markdown es posible incluir ecuaciones algebráicas, que son elaboradas por el autor del documento. Para ello se utiliza el signo de moneda $ecuación$ para indicar el inicio y el final de la ecuación.
Existen dos maneras de insertar dichas ecuaciones, una puede ser dentro del mismo renglón del texto redactado y la otra es en un párrafo aparte al renglón en el que se escribe la ecuación.
Cuando se utiliza solo un signo $X=f(X)$, la ecuación se incluye dentro de la misma línea del texto, por ejemplo: \(X=f(X)\)
Cuando se utilizan dos signos de moneda tanto al inicio como al final $$X=f(X)$$ entonces la ecuación se incluye en un párrafo aparte y centrada, por ejemplo: \[X=f(X)\]

A su vez, se debe tomar en cuenta que la sintaxis de las ecuaciones sigue la propia del sistema LaTex, para ello se puede tomar como referencia la página de apoyo de ecuaciones WikiLatex o https://en.wikibooks.org/wiki/LaTeX/Mathematics

2.4. Convertir modelos estadísticos en ecuaciones en texto

Otra opción para escribir ecuaciones consiste en importar el código de un modelo estadístico, y que este sea traducido con los símbolos LaTex para representar al código estadístico de R. Para ello se utiliza el paquete equatiomatic(), que permite traducir un modelo estadístico de R en texto de R Markdown, mediante el comando extract_eq(nombre_del_modelo) a extraer.

Si solo se desea que se extraiga la ecuación con sus letras algebráicas, se realiza de la siguiente manera:

data("mtcars") #carga de los datos
modelo <- lm(mpg ~ cyl + disp, mtcars) #diseño del modelo de regresión
equatiomatic::extract_eq(modelo) # mostrar el modelo estadístico

Donde el resultado será:

data("mtcars") #carga de los datos
modelo <- lm(mpg ~ cyl + disp, mtcars) #diseño del modelo de regresión
equatiomatic::extract_eq(modelo) # mostrar el modelo estadístico

\[ \operatorname{mpg} = \alpha + \beta_{1}(\operatorname{cyl}) + \beta_{2}(\operatorname{disp}) + \epsilon \]

En caso de desear que se incluyan los valores de los coeficientes del modelo, entonces se ejecutará el script:

# mostrar el valor de los coeficientes
equatiomatic::extract_eq(modelo, use_coefs = TRUE)

Donde el resultado será:

# mostrar el valor de los coeficientes
equatiomatic::extract_eq(modelo, use_coefs = TRUE)

\[ \operatorname{\widehat{mpg}} = 34.66 - 1.59(\operatorname{cyl}) - 0.02(\operatorname{disp}) \]

2.5. Incluir los códigos en un apéndice.

La manera de colocar todos los códigos utilizados en una obra dentro de una sección de apéndice, se utiliza el script ref.label y la función knitr::all_labels() de la siguiente manera en un “chunk”:

# Appendix: Tódos los códigos utilizados en el reporte

```{r ref.label=knitr::all_labels(), echo=TRUE, eval=FALSE} #chunk modificado
```

Esto arrojará completamente todos los códigos.

Pero si solo se desea que en el apéndice de códigos se incluyan códigos seleccionados, se deben realizar algunos procedimientos adicionales:

  1. En cada uno de los “chunks” de código deseados se incluye el script appendix = TRUE, de manera:
```{r appendix = TRUE} #chunk modificado
```
  1. Posteriormente, en el “chunk” del apéndice de códigos se incluye el script ref.label = knitr::all_labels(appendix == TRUE) para llamar solo a los códigos previamente seleccionados.
# Appendix: Tódos los códigos utilizados en el reporte

```{r ref.label = knitr::all_labels(appendix == TRUE), echo=TRUE, eval=FALSE} #chunk modificado
```

3. Incluir imágenes y gráficas mediante “chunks”.

3.1. Incluir imágenes guardadas como objetos.

R Markdown permite el uso de diversos métodos para insertar imágenes dentro de un texto para, así, posteriormente renderizarlo. En el caso de usar la sintaxis de Markdown existe el comando ![nombre de la imagen](ruta/de/ubicación/de/la/imagen.png) para insertar imágenes guardadas en el directorio de trabajo del usuario en su computadora.
También existe la opción para insertar imágenes de diversos tipo mediante el uso de los chunks, en el que se utiliza la paquetería knitr() y que permite “llamarlos” cuando, previamente, fueron generados mediante un código de R y guardados como objetos.
El comando utilizado para incluir imágenes que, previamente, fueron guardados como objetos en R es knitr :: include_graphics(imagen1), y se incluyen desde un chunk nuevo, de la siguiente manera:

imagen1 <- c("https://telos.fundaciontelefonica.com/wp-content/uploads/2019/04/telos-110-regulacion-partidos-politicos-privacidad-ilustracion-daniel-tornero.jpg") #importación de una imagen desde internet y se guarda como objeto.
knitr :: include_graphics(imagen1) #comando para insertar objeto/imagen en el documento de salida.

En caso de que se deseé “limpiar” el código para que solo se presente la imagen en el documento de salida, entonces se pueden utilizar varios argumentos, como: echo = FALSE, out.width = '20%', fig.align ='center' de la siguiente manera:

3.2. Insertar gráficas generadas dentro de un “chunk”.

R Markdown también permite la inclusión de gráficos generados desde un mismo chunk, y de esta manera no es necesario generar el gráfico en una paquetería distinta, ni tampoco se debe guardar o copiar y pegar el gráfico dentro del procesador de texto (por ej. piénsese en los pasos que se deben realizar para generar un gráfico y pegarlo al momento de redactar un texto en el procesador de textos Word u otro parecido).
Para isertar una gráfica desde un data frame existente en el ambiente de trabajo de R Studio, primero se debe generar el código del gráfico deseado en un chunk, y posteriormente debe ser “llamado” dentro del mismo trozo de código. Al momento de renderizar dicho código, el documento de salida contendrá al gráfico elaborado.
Por ejemplo, a continuación se genera un boxplot mediante la paquetería ggplot() a partir del data frame data("iris") (disponible dentro de R Studio), y que será renderizado posteriormente.

library(tidyverse) #se carga la librería que contiene a ggplot
library(ggthemes) #se carga la librería para afinar los gráficos de ggplot
data("iris") #se carga el data frame
grafica1 <- ggplot(iris, aes(x = Species, y = Sepal.Length)) + #se crea el objeto y el lienzo del gráfico
  geom_boxplot() + #se define el formato de salida de la gráfica, o la gráfica de caja
  geom_jitter(aes(colour = Species), size = 3, alpha = 0.4) + #se incluyen un gráfico de dispersión para los datos
  theme_minimal() #se define el estilo del gráfico
grafica1 #se llama al gráfico para que sea impreso

Ahora si se desea insertar la misma gráfica pero quitando el código, las advertencias y los mensajes para que, al final, solo se observe el resultado del código, centrado y de tamaño reducido, se utilizan las opciones de chunk siguientes: echo=FALSE, fig.align='center', message=FALSE, warning=FALSE, out.width='45%'.
Y el resultado producido es:

4. Insertar tablas.

La inserción de tablas es un elemento importante para la presentación de una gran variedad de datos. En esta sección se revisarán algunas alternativas para insertar tablas de contenido generadas desde objetos previamente existentes en el ambiente de R y R Studio. Especialmente cuando se trabaja con objetos del tipo data frames. También se debe tener en cuenta que será importante reconocer el formato de salida del documento (.html, .pdf, .doc) a renderizar para, así, ubicar la forma en que será visualizado.

4.1. Inserción de tablas de contenido con kable.

La librería kable() es un paquete que cuenta con buena integración con knitr(), y permite la generación de tablas. Pero se debe tener en cuenta que la primer condición para generar esta forma de presentación consiste en que los datos se encuentren en formato rectangular como tipo matriz o data frames. Otro elemento a tomar en consideración es que dicha paquetería no permite fusionar celdas.
La estructura del script de kable() es:

kable(x, format, digits = getOption("digits"), row.names = NA,
  col.names = NA, align, caption = NULL, label = NULL,
  format.args = list(), escape = TRUE, ...)

Insertar una tabla utilizando la libraría knitr() y el comando kable().

data("mtcars")
tabla1 <- head(mtcars) #vistazo a los primeros 6 casos o filas
knitr :: kable(tabla1,
               caption = "Distribución de casos")
Distribución de casos
mpg cyl disp hp drat wt qsec vs am gear carb
Mazda RX4 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4
Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4
Datsun 710 22.8 4 108 93 3.85 2.320 18.61 1 1 4 1
Hornet 4 Drive 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1
Hornet Sportabout 18.7 8 360 175 3.15 3.440 17.02 0 0 3 2
Valiant 18.1 6 225 105 2.76 3.460 20.22 1 0 3 1

4.1.1. Definir el formato de salida de la tabla.

El formato de salida de la tabla de contenido puede ser modificado mediante el argumento format =, que define el tipo de resultado de la tabla que se genera y para ello acepta varios tipos de indicaciones, como: pipe(tabla donde los datos están separados por “barras”, valor por defecto), simple (tabla simple donde los valores están separados por espacios), latex (el resultado se arroja en redacción LaTex), html(el resultado se arroja en redacción html).
Por ejemplo:

Tabla en formato de pipe

knitr::kable(tabla1, "pipe") 
mpg cyl disp hp drat wt qsec vs am gear carb
Mazda RX4 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4
Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4
Datsun 710 22.8 4 108 93 3.85 2.320 18.61 1 1 4 1
Hornet 4 Drive 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1
Hornet Sportabout 18.7 8 360 175 3.15 3.440 17.02 0 0 3 2
Valiant 18.1 6 225 105 2.76 3.460 20.22 1 0 3 1

Tabla en formato simple

knitr::kable(tabla1, "simple")
mpg cyl disp hp drat wt qsec vs am gear carb
Mazda RX4 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4
Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4
Datsun 710 22.8 4 108 93 3.85 2.320 18.61 1 1 4 1
Hornet 4 Drive 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1
Hornet Sportabout 18.7 8 360 175 3.15 3.440 17.02 0 0 3 2
Valiant 18.1 6 225 105 2.76 3.460 20.22 1 0 3 1

Tabla en formato html

knitr::kable(tabla1, "html")
mpg cyl disp hp drat wt qsec vs am gear carb
Mazda RX4 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4
Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4
Datsun 710 22.8 4 108 93 3.85 2.320 18.61 1 1 4 1
Hornet 4 Drive 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1
Hornet Sportabout 18.7 8 360 175 3.15 3.440 17.02 0 0 3 2
Valiant 18.1 6 225 105 2.76 3.460 20.22 1 0 3 1

Tabla en formato latex (Este no será visible en otro formato que no sea .pdf)

knitr::kable(tabla1, "latex")

Tabla en formato rst (barras horizontales)

knitr::kable(tabla1, "rst")

================= ==== === ==== === ==== ===== ===== === === ==== ====   mpg cyl disp hp drat wt qsec vs am gear carb ================= ==== === ==== === ==== ===== ===== === === ==== ==== Mazda RX4 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4 Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4 Datsun 710 22.8 4 108 93 3.85 2.320 18.61 1 1 4 1 Hornet 4 Drive 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1 Hornet Sportabout 18.7 8 360 175 3.15 3.440 17.02 0 0 3 2 Valiant 18.1 6 225 105 2.76 3.460 20.22 1 0 3 1 ================= ==== === ==== === ==== ===== ===== === === ==== ====

4.1.2. Cambiar el nombre a las columnas.

Para ello se utiliza el argumento col.names = y se incluye un vector con el nombre de las columnas en texto (entrecomillado). El número de nombres de columnas debe ser idéntico a las columnas incluidas en la tabla. Por ejemplo:

tabla2 <- head(iris)
knitr::kable(tabla2,
  col.names = c('se', 'necesitan', 'cinco', 'nombres', 'aquí'))
se necesitan cinco nombres aquí
5.1 3.5 1.4 0.2 setosa
4.9 3.0 1.4 0.2 setosa
4.7 3.2 1.3 0.2 setosa
4.6 3.1 1.5 0.2 setosa
5.0 3.6 1.4 0.2 setosa
5.4 3.9 1.7 0.4 setosa

4.1.3. Definir el alineamiento de cada columna.

Esto se especifica a partir del argumento align =, y los valores posibles son: l para izquierda, c para centrado, r para derecha.
También se puede generar una versión simplificada de alineamientos según el número de columnas: kable(..., align = c('c', 'l')), y esto es lo mismo que kable(..., align = 'cl'). Por ejemplo:

# left, center, center, right, right
knitr::kable(tabla2, align = "lccrr")
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
5.1 3.5 1.4 0.2 setosa
4.9 3.0 1.4 0.2 setosa
4.7 3.2 1.3 0.2 setosa
4.6 3.1 1.5 0.2 setosa
5.0 3.6 1.4 0.2 setosa
5.4 3.9 1.7 0.4 setosa

4.1.4. Incluir título a la tabla.

Para ello se usa el argumento caption = "nombre de la tabla", de la siguiente manera:

knitr::kable(tabla2, caption = "Ejemplo de título de la tabla.")
Ejemplo de título de la tabla.
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
5.1 3.5 1.4 0.2 setosa
4.9 3.0 1.4 0.2 setosa
4.7 3.2 1.3 0.2 setosa
4.6 3.1 1.5 0.2 setosa
5.0 3.6 1.4 0.2 setosa
5.4 3.9 1.7 0.4 setosa

4.1.5. Formato de los contenidos numéricos de las columnas.

Se puede definir el número de decimales con el argumento digits = #

knitr::kable(tabla1, 
             digits = 1)
mpg cyl disp hp drat wt qsec vs am gear carb
Mazda RX4 21.0 6 160 110 3.9 2.6 16.5 0 1 4 4
Mazda RX4 Wag 21.0 6 160 110 3.9 2.9 17.0 0 1 4 4
Datsun 710 22.8 4 108 93 3.9 2.3 18.6 1 1 4 1
Hornet 4 Drive 21.4 6 258 110 3.1 3.2 19.4 1 0 3 1
Hornet Sportabout 18.7 8 360 175 3.1 3.4 17.0 0 0 3 2
Valiant 18.1 6 225 105 2.8 3.5 20.2 1 0 3 1

También se puede quitar la notación científica con el argumento format.args =, dentro del que se puede utilizar scientific = FALSE, así como añadir una coma a los números en miles con big.mark = ",")

knitr::kable(tabla1, 
             digits = 1, 
             format.args = list(big.mark = ",",
                                scientific = FALSE))
mpg cyl disp hp drat wt qsec vs am gear carb
Mazda RX4 21.0 6 160 110 3.9 2.6 16.5 0 1 4 4
Mazda RX4 Wag 21.0 6 160 110 3.9 2.9 17.0 0 1 4 4
Datsun 710 22.8 4 108 93 3.9 2.3 18.6 1 1 4 1
Hornet 4 Drive 21.4 6 258 110 3.1 3.2 19.4 1 0 3 1
Hornet Sportabout 18.7 8 360 175 3.1 3.4 17.0 0 0 3 2
Valiant 18.1 6 225 105 2.8 3.5 20.2 1 0 3 1

4.2. Modificaciones particulares a tablas mediante librería kableExtra().

La paquetería kableExtra() permite realizar modificaciones especiales a tablas, para ello primero se debe instalar y activar dicha paquetería:

# instalar desde CRAN
install.packages("kableExtra")

Este paquete se puede utilizar complementariamente con el argumento de %>%(pipes de dplyr() dentro de tidyverse()).
A partir del argumento kable_styling(), se pueden modificar diversos aspectos de las tablas, y a continuación se revisarán algunos comandos.

4.2.1. Sombreado de filas intercaladas de una tabla

Por ejemplo, se puede indicar la inclusión de sombreado en filas alternadas de la tabla con el argumento:

library(knitr)
library(kableExtra)
## 
## Attaching package: 'kableExtra'
## The following object is masked from 'package:dplyr':
## 
##     group_rows
kable(head(iris)) %>%
kable_styling(latex_options = "striped")
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
5.1 3.5 1.4 0.2 setosa
4.9 3.0 1.4 0.2 setosa
4.7 3.2 1.3 0.2 setosa
4.6 3.1 1.5 0.2 setosa
5.0 3.6 1.4 0.2 setosa
5.4 3.9 1.7 0.4 setosa

En caso que desear aplicar el estilo sombreado en un documento LaTex, en formato de salida .pdf, el argumento se debe modificar por latex_options = "striped".

4.2.2. Cambiar el tamaño de la fuente en una tabla.

Para ello se utiliza el argumento font_size = # dentro del argumento kable_styling().

kable(head(iris, 5), booktabs = TRUE) %>%
  kable_styling(font_size = 8)
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
5.1 3.5 1.4 0.2 setosa
4.9 3.0 1.4 0.2 setosa
4.7 3.2 1.3 0.2 setosa
4.6 3.1 1.5 0.2 setosa
5.0 3.6 1.4 0.2 setosa

4.2.3. Definir un estilo específico por cada fila o columna.

Para ello se utilizan los argumentos row_spec() y column_spec() de la siguiente manera:

kable(head(iris, 5), align = 'c', booktabs = TRUE) %>%   ##Aquí se definen los datos de la tabla
  row_spec(1, bold = TRUE, italic = TRUE) %>%  ##Aquí se define la primer fila, letra negritas e itálicas
  row_spec(2:3, color = 'white', background = 'black') %>% ##Aquí se define la 2a y 3a fila, color de letras blancas, fondo negro
  row_spec(4, underline = TRUE, monospace = TRUE) %>% ##Aquí se define la 4a fila, subrayado, cambio de letra
  row_spec(5, angle = 45) %>%  ##Aquí se define la 5a fila, letras en ángulo de 45 grados
  column_spec(5, strikeout = TRUE) ##Aquí se define la 5a columna, tachado
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
5.1 3.5 1.4 0.2 setosa
4.9 3.0 1.4 0.2 setosa
4.7 3.2 1.3 0.2 setosa
4.6 3.1 1.5 0.2 setosa
5.0 3.6 1.4 0.2 setosa

4.2.4. Agrupar columnas / filas dentro de otra celda más amplia.

Los argumentos a utilizar son pack_rows() para agrupar filas, y add_header_above() para integrar varias columnas bajo un título en común.
Por ejemplo:

iris2 <- iris[1:5, c(1, 3, 2, 4, 5)]
kable(iris2, booktabs = TRUE) %>% ##Se crea la tabla y las columnas con datos
  add_header_above(c("Largo" = 2, "Ancho" = 2, " " = 1)) %>% ##Se indica la creación de un encabezado que se llamará "Largo" y ocupa 2 columnas, otro encabezado que se llama "Ancho" y se extiende 2 columnas y un 3er encabezado con nombre "(vacío)" con extensión de 1 columna.
  add_header_above(c("Medidas" = 4, "Más atributos" = 1)) ##Se añade un 3er encabezado, el primero se llama "Medidas" y se extiende por 4 columnas y el 2o se llama "Más atributos" y se extiende 1 columna
Medidas
Más atributos
Largo
Ancho
Sepal.Length Petal.Length Sepal.Width Petal.Width Species
5.1 1.4 3.5 0.2 setosa
4.9 1.4 3.0 0.2 setosa
4.7 1.3 3.2 0.2 setosa
4.6 1.5 3.1 0.2 setosa
5.0 1.4 3.6 0.2 setosa

En el caso del argumento pack_rows(), funciona parecido a add_header_above(), donde para definir el número de filas que se agruparán se debe utilizar el argumento index, en donde se indica el título del agrupamiento así como su extensión definido en número de filas, de la siguiente manera:

iris3 <- iris[c(1:2, 51:54, 101:103), ]  ##Se define el data frame filtrado por las filas
kable(iris3[, 1:4], booktabs = TRUE) %>% ##Se crea la tabla
pack_rows(index = c("setosa" = 2, "versicolor" = 4, "virginica" = 3) #Se define el nombre de agrupamiento de las filas así como el número de filas incluido en cada grupo
)
Sepal.Length Sepal.Width Petal.Length Petal.Width
setosa
1 5.1 3.5 1.4 0.2
2 4.9 3.0 1.4 0.2
versicolor
51 7.0 3.2 4.7 1.4
52 6.4 3.2 4.5 1.5
53 6.9 3.1 4.9 1.5
54 5.5 2.3 4.0 1.3
virginica
101 6.3 3.3 6.0 2.5
102 5.8 2.7 5.1 1.9
103 7.1 3.0 5.9 2.1

4.2.5. Ajustar el tamaño de una tabla al ancho de página en LaTex.

Ajustar el tamaño de una tabla solo es aplicable en LaTex, para ello se usa la función kableExtra::landscape(), por ejemplo:

tab <- kable(tail(mtcars, 5), booktabs = TRUE)
tab  # tabla original, muy ancha en LaTex
mpg cyl disp hp drat wt qsec vs am gear carb
Lotus Europa 30.4 4 95.1 113 3.77 1.513 16.9 1 1 5 2
Ford Pantera L 15.8 8 351.0 264 4.22 3.170 14.5 0 1 5 4
Ferrari Dino 19.7 6 145.0 175 3.62 2.770 15.5 0 1 5 6
Maserati Bora 15.0 8 301.0 335 3.54 3.570 14.6 0 1 5 8
Volvo 142E 21.4 4 121.0 109 4.11 2.780 18.6 1 1 4 2
tab %>%
  kable_styling(latex_options = "scale_down")  ##Aquí se re escala la tabla en un formato de salida .pdf. Si esto se mira en .html no se nota ningún ajuste.
mpg cyl disp hp drat wt qsec vs am gear carb
Lotus Europa 30.4 4 95.1 113 3.77 1.513 16.9 1 1 5 2
Ford Pantera L 15.8 8 351.0 264 4.22 3.170 14.5 0 1 5 4
Ferrari Dino 19.7 6 145.0 175 3.62 2.770 15.5 0 1 5 6
Maserati Bora 15.0 8 301.0 335 3.54 3.570 14.6 0 1 5 8
Volvo 142E 21.4 4 121.0 109 4.11 2.780 18.6 1 1 4 2

4.3. Flextable.

Otra alternativa para insertar tablas es mediante la paquetería de flextable dentro de otro chunk.
Esta paquetería también permite trabajar con un data frame, además que se integra con el comando de %>% (pipes), y cuenta con gran flexibilidad para modificar los espacios de las tablas según las necesidades del autor.
El comando básico para llamar la creación de una tabla desde un data frame es mediante el comando flextable(data.frame), por ejemplo:

library(flextable)
flextable(head(iris))

flextable()también se puede integrar con el uso de los %>% (pipes) de dplyr() al momento de “pasar” el data frame y, así, generar las tablas de contenido, de la siguiente manera:

tabla3<-head(iris) #aquí se crea un objeto
tabla3 %>% #se incluye un "pipe"
  flextable() #se manda a llamar a la tabla de contenido

Afinando la tabla:

tabla3 %>%
  flextable() %>%
  fix_border_issues(part = "all") %>%
  bold(part = "header") %>%
  align(align = "center", part = "all") %>%
  color(., ~ Sepal.Width > 3.5, ~ Sepal.Width, color = "red") %>%
  color(., ~ Sepal.Length > 5, ~ Sepal.Length, color = "blue")

Afinando la presentación de la tabla:

5. References

6. Appendix

LS0tCnRpdGxlOiAiRWxlbWVudG9zIGLDoXNpY29zIGRlbCBjaHVuayBkZSBjw7NkaWdvcyIKYXV0aG9yOiAiR3VzdGF2byBNYXJ0w61uZXogVmFsZGVzIgpkYXRlOiAiMi8xMy8yMDIyIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogVFJVRQogICAgdG9jX2RlcHRoOiA1CiAgICB0b2NfZmxvYXQ6IFRSVUUKICAgIG51bWJlcl9zZWN0aW9uOiBGQUxTRQogICAgdGhlbWU6ICJjb3NtbyIKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKZWRpdG9yX29wdGlvbnM6IAogIG1hcmtkb3duOiAKICAgIHdyYXA6IDcyCi0tLQojIDEuIFByZXNlbnRhY2nDs24gc29icmUgZWwgbWFuZWpvIGRlICoqImNodW5rcyIqKiBvICJ0cm96b3MiIGRlIGPDs2RpZ28gZGUgUi4KClVuYSBkZSBsYXMgZ3JhbmRlcyBjdWFsaWRhZGVzIGRlIHVuIGRvY3VtZW50byBlbiBmb3JtYXRvICoqUiBNYXJrZG93bioqIGNvbnNpc3RlIGVuIGxhIHBvc2liaWxpZGFkIGRlIGluY2x1aXIgKipjw7NkaWdvcyBkZSBSKiosIGV2YWx1YXJsb3MgeSwgYSBzdSB2ZXosIGluc2VydGFyIGxvcyByZXN1bHRhZG9zIGVuIHVuIHRleHRvLiBQYXJhIGVsbG8gZXMgbXV5IMO6dGlsIGxhIHBhcXVldGVyw61hIGBrbml0cigpYGRlIFIsIHF1ZSBjdWVudGEgY29uIGRpdmVyc29zIGNvbWFuZG9zIG8gKnNjcmlwdHMqLiBFc3RvcyBheXVkYXIgYSBhanVzdGFyIGxhIG1hbmVyYSBkZSBldmFsdWFyIGUgaW50ZWdyYXIgdGFudG8gbG9zIGPDs2RpZ29zIGRlIFIgdXRpbGl6YWRvcywgYXPDrSBjb21vIHN1cyByZXN1bHRhZG9zICh5YSBzZWFuIGVuIGZvcm1hdG8gZGUgdmFsb3JlcyBudW3DqXJpY29zLCB0YWJsYXMgZGUgY29udGVuaWRvIG8gZmlndXJhcyB5IGdyw6FmaWNhcykgZW4gZWwgZG9jdW1lbnRvIGRlIHNhbGlkYSBvICpvdXRwdXQqIHJlbmRlcml6YWRvIHBhcmEgZWwgcHJvY2VzbyBkZSBpbnRlZ3JhY2nDs24gZGUgbG9zIGVsZW1lbnRvcyBkZSB1biBkb2N1bWVudG8gZGUgKipSIE1hcmtkb3duKiouXAoKRW4gZXN0ZSBkb2N1bWVudG8gc2UgcmV2aXNhcsOhbiBhbGd1bm9zIGRlIGxvcyBjb21hbmRvcyBiw6FzaWNvcyBkZSBga25pdHIoKWAgcXVlIHBlcm1pdGVuIG1hbmlwdWxhciB5IGFqdXN0YXIgbG9zIGNvbWFuZG9zIGRlIFIsIGJham8gbGFzIG5lY2VzaWRhZGVzIGRlbCBhdXRvciBkZWwgZG9jdW1lbnRvIGFsIG1vbWVudG8gZGUgY29uc3RydWlyIGUgaW50ZWdyYSBjw7NkaWdvcyB5IHJlc3VsdGFkb3MgZW4gZWwgZG9jdW1lbnRvIGRlIHNhbGlkYSAob3V0cHV0KS5cClBhcmEgZWxsbyBwcmltZXJvIHNlIHJldmlzYXLDoW4gY29tYW5kb3MgYsOhc2ljb3MgcGFyYSBpbnNlcnRhciB5IGNvbmZpZ3VyYXIgZGUgbWFuZXJhIGdlbmVyYWwgZW4gdW4gZG9jdW1lbnRvIGVuIGZvcm1hdG8gUiBNYXJrZG93biwgeSBwb3N0ZXJpb3JtZW50ZSBzZSByZXZpc2Fyw6FuIGVsZW1lbnRvcyBwYXJ0aWN1bGFyZXMgcGFyYSBjb25maWd1cmFyIGEgZXN0b3MgY8OzZGlnb3MgdGFudG8gc29icmUgc3UgKmV2YWx1YWNpw7NuKiwgcHJlc2VudGFjacOzbiBkZSBsb3MgKnJlc3VsdGFkb3MqLCBpbnNlcmNpw7NuIGRlICpmaWd1cmFzKiB5ICpncsOhZmljYXMqLiBZIHRhbWJpw6luIHNlIHJldmlzYXLDoW4gY29tYW5kb3MgcGFyYSBsYSBnZW5lcmFjacOzbiBkZSAqdGFibGFzIGRlIGNvbnRlbmlkbyouXAoKCiMgMi4gSW5zZXJjacOzbiBkZSAidHJvem9zIiBkZSBjw7NkaWdvIGRlIFIgbyAqKmNodW5rcyoqLgoKTGEgY3JlYWNpw7NuIGRlIGPDs2RpZ29zIGRlIFIgcGFyYSBlbCBhbsOhbGlzaXMgZGUgZGF0b3MgeSBzdSBwb3N0ZXJpb3IgaW5zZXJjacOzbiBlbiB1biBkb2N1bWVudG8gZGUgc2FsaWRhIGRlc2VhZG8gKHlhIHNlYSBlbiBmb3JtYXRvIC5odG1sLCAucGRmIG8gLmRvYykgc2UgcmVhbGl6YSBhIHRyYXbDqXMgZGUgbGFzIHNlY2Npb25lcyBkZSAqKnRyb3pvcyoqIGRlIGPDs2RpZ28gbyAqKmNodW5rcyoqLiBFc3RvcyBzZSBkaXNlw7FhbiBpbmljaWFuZG8gY29uIDMgYWNlbnRvcyBpbmljaWFsZXMgeSAzIGFjZW50b3MgZmluYWxlcywgeSBlbiBzdSBpbnRlcmlvciBzZSBjb2xvY2FuIGNvcmNoZXRlcyBlbiBlbCBxdWUgc2UgaW5kaWNhIGVsIGxlbmd1YWplIGRlbCBjw7NkaWdvLCBgIGBgYHtyIGFuZCB9IGAuIEVuIGVzdGUgY2FzbyBlbCBsZW5ndWFqZSBlcyBlbCBtaXNtbyBkZSBSLCB5IHN1IGVzdHJ1Y3R1cmEgZXM6CgpgYGBgCmBgYHtyfSAjYXF1w60gc2UgaW5kaWNhIGVsIGxlbmd1YWplCnBhc3RlKCJIb2xhIiwgIk11bmRvIikgI2VzdGUgY29ycmVzcG9uZGUgYWwgY8OzZGlnbyBpbnNlcnRhZG8KYGBgCmBgYGAKRXN0YSBlc3RydWN0dXJhIGRlbCBjaHVuayBzZSBwdWVkZSBlc2NyaWJpciBtYW51YWxtZW50ZSBvIHRhbWJpw6luIHNlIHB1ZWRlIGluc2VydGFyIG1lZGlhbnRlIGVsIGN1cnNvciBkw6FuZG9sZSBjbGljayBpenF1aWVyZG8gYWwgYm90w7NuIGRlIGBpbnNlcnRgIGVuIGxhIHBhcnRlIHN1cGVyaW9yIGRlIGxhIHZlbnRhbmEgZGUgYHNvdXJjZWAuIE90cmEgbWFuZXJhIGRlIHJlYWxpemFybG8gZXMgYSB0cmF2w6lzIGRlIGxhIGNvbWJpbmFjacOzbiBkZSB0ZWNsYXM6IGBDdHJsICsgQWx0ICsgSWAgKGBDbWQgKyBPcHRpb24gKyBJYCBlbiBtYWNPUykuCgpFbCByZXN1bHRhZG8gZGVsIGNodW5rIGVzOgpgYGB7cn0KI0VzdG8gZXMgdW4gImNodW5rIgpsaWJyYXJ5KHRpZHl2ZXJzZSkgI0VzdG8gZXMgdW4gY8OzZGlnbyBkZSBSCmBgYAoKQWwgKnJlbmRlcml6YXIqIGVsIGRvY3VtZW50byBkZSBSIE1hcmtkb3duIHNlIG9ic2VydmEgcXVlIGVuIGVsIGRvY3VtZW50byBkZSBzYWxpZGEsIHBvciBkZWZlY3RvLCBsb3MgZWxlbWVudG9zIHF1ZSBhcGFyZWNlbiBwb3IgZGVmZWN0byBzb246CgphLiAgZWwgY8OzZGlnbyBkZSBSIChlbiB1biByZWN1YWRybyBzb21icmVhZG8pLApiLiAgZWwgcmVzdWx0YWRvIGRlbCBjw7NkaWdvIChlbiB1biByZWN1YWRybyB0cmFuc3BhcmVudGUpLApjLiAgYWR2ZXJ0ZW5jaWFzIGFsIGNvcnJlciBlbCBjw7NkaWdvIChlbiB1biByZWN1YWRybyBhZGljaW9uYWwsIHNpIG9jdXJyZSkuCgpFc3RvcyBlbGVtZW50b3Mgc2UgZGViZW4gZGUgImxpbXBpYXIiIGFsIGVsYWJvcmFyIHVuIHJlcG9ydGUgZGUgaW52ZXN0aWdhY2nDs24uIEVuIGludGVybmV0IHNlIHB1ZWRlIGVuY29udHJhciB1bmEgY2FudGlkYWQgZGUgZWxlbWVudG9zIGRlIGFwb3lvLCB1bm8gY29uc2lzdGUgZW4gbGEgcMOhZ2luYSB3ZWIgZGUgUiBNYXJrZG93biBlbGFib3JhcmEgcG9yIFtSIFN0dWRpb10oaHR0cHM6Ly9ybWFya2Rvd24ucnN0dWRpby5jb20vbGVzc29uLTEuaHRtbCkuIFRhbWJpw6luIHNlIGN1ZW50YSBjb24gbGEgaG9qYSBkZSBhcG95byBvIFtjaGVhdCBzaGVldF0oaHR0cHM6Ly93d3cucnN0dWRpby5jb20vd3AtY29udGVudC91cGxvYWRzLzIwMTUvMDIvcm1hcmtkb3duLWNoZWF0c2hlZXQucGRmKSBkZSBSIE1hcmtkb3duLlwKRmluYWxtZW50ZSwgc2UgcmVjb21pZW5kYSBlbCB1c28gZGUgbGEgW2d1w61hIGRlIHJlZmVyZW5jaWFdKGh0dHBzOi8vd3d3LnJzdHVkaW8uY29tL3dwLWNvbnRlbnQvdXBsb2Fkcy8yMDE1LzAzL3JtYXJrZG93bi1yZWZlcmVuY2UucGRmKSBkZSBSIE1hcmtkb3duIGVsYWJvcmFkYSBwb3IgUiBTdHVkaW8uCgpBIGNvbnRpbnVhY2nDs24gc2UgcmV2aXNhbiBhbGd1bm9zIGNvbWFuZG9zIHBhcmEgaGFjZXIgZGljaGEgbGltcGllemEsIGVtcGV6YW5kbyBwb3IgbGFzIG9wY2lvbmVzIHBhcmEgKmV2YWx1YXIqIGxvcyBjw7NkaWdvcywgY29uZmlndXJhciBsb3MgKnJlc3VsdGFkb3MqLCBpbnNlcmNpw7NuIGRlICpncsOhZmljYXMqIHkgZGUgKnRhYmxhcyouXApBZGVtw6FzIHNlIHV0aWxpemFyw6FuIGxvcyBkYXRvcyBkZSBsYSBlbmN1ZXN0YSBjb3JyZXNwb25kaWVudGUgYSBsYSBvbGEgMjAyMSBkZSBbTGF0aW5vYmFyw7NtZXRyb10oaHR0cHM6Ly93d3cubGF0aW5vYmFyb21ldHJvLm9yZy9sYXRDb250ZW50cy5qc3ApCgpgYGB7ciBlY2hvPUZBTFNFfQpsaWJyYXJ5KGhhdmVuKQpkYXRvc2xiIDwtIHJlYWRfc2F2KCJ+L0Ryb3Bib3gvUi9MYXRpbm9iYXJvbWV0cm9fMjAxOF9Fc3BfU3Bzc192MjAxOTAzMDMuc2F2IikKYGBgCgoKCiMjIDIuMS4gRXZhbHVhY2nDs24gZGUgbG9zIGNodW5rcy4KCkxhIGV2YWx1YWNpw7NuIGRlIHVuIGPDs2RpZ28gc2UgcmVmaWVyZSBhIHNpIHNlIGRlc2VhIHF1ZSDDqXN0ZSBzZWEgZWplY3V0YWRvLCBhc8OtIGNvbW8gYSBzaSBzZSBkZXNlYSBxdWUgYXBhcmV6Y2EgZW4gaW1wcmVzbyBlbiBlbCBkb2N1bWVudG8gZGUgc2FsaWRhLlwKCkVsIGNvbWFuZG8gYGV2YWwgPWAgcHVlZGUgYWRvcHRhciBkb3MgdmFsb3JlczogYFRSVUVgIChwb3IgZGVmZWN0bykgeSBgRkFMU0VgLiBFbiBlbCBjYXNvIGRlIHNlw7FhbGFyIGxhIMO6bHRpbWEgb3BjacOzbiwgZW50b25jZXMgc2UgaW5kaWNhIHF1ZSBlbCBjw7NkaWdvIG5vIHNlYSBlamVjdXRhZG8gYWwgbW9tZW50byBkZSByZW5kZXJpemFyIGVsIGRvY3VtZW50byBSIE1hcmtkb3duLgoKYGBgYApgYGB7ciBldmFsPUZBTFNFfSAjY2h1bmsgY29uZmlndXJhZG8KbWVhbihkYXRvc2xiICQgRURBRCkKYGBgCmBgYGAKRWwgcmVzdWx0YWRvIGVuIGVsIGRvY3VtZW50byBkZSBzYWxpZGEgc29sbyBjb25zaXN0ZSBlbiBwcmVzZW50YXIgZWwgY8OzZGlnbyBlc2NyaXRvIHBlcm8gbm8gc3UgcmVzdWx0YWRvOgoKYGBge3IgZXZhbD1GQUxTRX0KbWVhbihkYXRvc2xiICQgRURBRCkKYGBgCgpFbCBjb21hbmRvIGBpbmNsdWRlID1gIHRhbWJpw6luIHB1ZWRlIGFkb3B0YXIgZG9zIHZhbG9yZXMgcG9zaWJsZXM6IGBUUlVFYCAocG9yIGRlZmVjdG8pIG8gYEZBTFNFYC4gRW4gZXN0YSDDumx0aW1hIG9wY2nDs24gbG8gcXVlIHNlIGluZGljYSBlcyBxdWUgZWwgY8OzZGlnbyBzw60gc2VhIGVqZWN1dGFkbyBhbCBtb21lbnRvIGRlIHJlbmRlcml6YXIgcGVybywgYSBzdSB2ZXosIHF1ZSBlbCBjw7NkaWdvIG1pc21vIG5vIHNlYSBpbmNsdWlkbyBlbiBlbCBkb2N1bWVudG8gZGUgc2FsaWRhLgoKYGBgYApgYGB7ciBpbmNsdWRlPUZBTFNFfSAjY2h1bmsgY29uZmlndXJhZG8uCnByb21fZWRhZCA8LSBtZWFuKGRhdG9zbGIgJCBFREFEKQpwcm9tX2VkYWQKYGBgCmBgYGAKCkVsIHJlc3VsdGFkbyBlbiBlbCBkb2N1bWVudG8gZGUgc2FsaWRhIG5vIG11ZXN0cmEgZWwgY8OzZGlnbyBuaSBlbCByZXN1bHRhZG8gZW4gZWwgZG9jdW1lbnRvIGRlIHNhbGlkYSwgcGVybyBzw60gc2UgYXJyb2phIGVsIHZhbG9yIGZpbmFsIGVuIGxhIHZlbnRhIGRlIGxhIGNvbnNvbGEgeSwgYXF1w60sIHRhbWJpw6luIHNlIGd1YXJkYSBhbCBvYmpldG8gY3JlYWRvIGBwcm9tX2VkYWRgOgoKYGBge3IgaW5jbHVkZT1GQUxTRX0KcHJvbV9lZGFkIDwtIG1lYW4oZGF0b3NsYiAkIEVEQUQpCnByb21fZWRhZApgYGAKCgojIyAyLjIuIFByZXNlbnRhY2nDs24gZGUgbG9zIHJlc3VsdGFkb3MuCgpMYSBwcmVzZW50YWNpw7NuIGRlIHJlc3VsdGFkb3MgZGUgbG9zIGPDs2RpZ29zIGV2YWx1YWRvcyB0YW1iacOpbiBzZSBwdWVkZSBjb25maWd1cmFyIG1lZGlhbnRlIHZhcmlvcyBjb21hbmRvcy5cCgoxLiBFbCBjb21hbmRvIGBjb2xsYXBzZT1gIHB1ZWRlIGFkb3B0YXIgZG9zIHZhbG9yZXMgcG9zaWJsZXM6IGBUUlVFYCBvIGBGQUxTRWAgKHBvciBkZWZlY3RvKS4gRXN0ZSBjb21hbmRvIHBlcm1pdGUganVudGFyIGxvcyBibG9xdWVzIGRlIHJlc3VsdGFkb3MgcGFyYSwgYXPDrSwgYWhvcnJhciBlc3BhY2lvLiAKCmBgYGAKYGBge3IgY29sbGFwc2U9VFJVRX0gI2NodW5rIGNvbmZpZ3VyYWRvLgpwcm9tX2VkYWQgPC0gbWVhbihkYXRvc2xiICQgRURBRCkKcHJvbV9lZGFkCmBgYApgYGBgCgpFbCByZXN1bHRhZG8gZXM6CmBgYHtyIGNvbGxhcHNlPUZBTFNFfQpwcm9tX2VkYWQgPC0gbWVhbihkYXRvc2xiICQgRURBRCkKcHJvbV9lZGFkCmBgYAoKMi4gT3RybyBjb21hbmRvIGVzIGBlY2hvPWAsIHN1cyB2YWxvcmVzIHNvbiBgVFJVRWAocG9yIGRlZmVjdG8pIHkgYEZBTFNFYC4gRXN0ZSDDumx0aW1vIGluZGljYSBxdWUgbm8gc2UgZGViYSBpbXByaW1pciBlbCBjw7NkaWdvIGVuIGVsIGRvY3VtZW50byBkZSBzYWxpZGEuIFBvciBlamVtcGxvOiAKYGBgYApgYGB7ciBlY2hvPUZBTFNFfSAjY2h1bmsgY29uZmlndXJhZG8uCnByb21fZWRhZCA8LSBtZWFuKGRhdG9zbGIgJCBFREFEKQpwcm9tX2VkYWQKYGBgCmBgYGAKRWwgcmVzdWx0YWRvIGVzIHF1ZSBubyBzZSBpbXByaW1pcsOhIGVsIGPDs2RpZ28gZW4gZWwgZG9jdW1lbnRvIGRlIHNhbGlkYSwgcGVybyBzw60gc2UgZXZhbMO6YSBlbCBjw7NkaWdvLgpgYGB7ciBlY2hvPUZBTFNFfQpwcm9tX2VkYWQgPC0gbWVhbihkYXRvc2xiICQgRURBRCkKcHJvbV9lZGFkCmBgYAoKMy4gRWwgY29tYW5kbyBgcmVzdWx0cz1gIHB1ZWRlIGFkb3B0YXIgMyB2YWxvcmVzOiBgaGlkZWAsIGBob2xkYCB5IGBhc2lzYC4gRW4gZWwgY2FzbyBkZSBgaGlkZWAgbm8gc2UgbXVlc3RyYW4gbG9zIHJlc3VsdGFkb3MgZGVsIGPDs2RpZ28gZW4gZWwgZG9jdW1lbnRvIGRlIHNhbGlkYS4gRW4gZWwgY2FzbyBkZSBgaG9sZGAgc2UgcmV0cmFzYSBtb3N0cmFyIHRvZG9zIGxvcyByZXN1bHRhZG9zIGhhc3RhIGVsIGZpbmFsIGRlbCBjw7NkaWdvLiBFbCBhcmd1bWVudG8gZGUgYGFzaXNgIHNlcGFyYSBsb3MgcmVzdWx0YWRvcyBzaW4gcmUgZm9ybWF0ZWFybG9zLgoKRWwgY29tYW5kbyBgaGlkZWA6IApgYGBgCmBgYHtyIHJlc3VsdHM9YGhpZGVgfSAjY2h1bmsgY29uZmlndXJhZG8uCnByb21fZWRhZCA8LSBtZWFuKGRhdG9zbGIgJCBFREFEKQpwcm9tX2VkYWQKYGBgCmBgYGAKCkVsIHJlc3VsdGFkbyBlczoKYGBge3IgcmVzdWx0cz0naGlkZSd9CnByb21fZWRhZCA8LSBtZWFuKGRhdG9zbGIgJCBFREFEKQpwcm9tX2VkYWQKYGBgCgpFbCBjb21hbmRvIGBob2xkYDogCmBgYGAKYGBge3IgcmVzdWx0cz1gaGlkZWB9ICNjaHVuayBjb25maWd1cmFkby4KcHJvbV9lZGFkIDwtIG1lYW4oZGF0b3NsYiAkIEVEQUQpCnByb21fZWRhZApzZF9lZGFkIDwtIHNkKGRhdG9zbGIgJCBFREFEKQpzZF9lZGFkCmBgYApgYGBgCgpFbCByZXN1bHRhZG8gZXM6CmBgYHtyIHJlc3VsdHM9J2hvbGQnfQpwcm9tX2VkYWQgPC0gbWVhbihkYXRvc2xiICQgRURBRCkKcHJvbV9lZGFkCnNkX2VkYWQgPC0gc2QoZGF0b3NsYiAkIEVEQUQpCnNkX2VkYWQKYGBgCgpFbCBjb21hbmRvIGBhc2lzYDoKYGBgYApgYGB7ciByZXN1bHRzPSdhc2lzJ30gI2NodW5rIGNvbmZpZ3VyYWRvLgpwcm9tX2VkYWQgPC0gbWVhbihkYXRvc2xiICQgRURBRCkKcHJvbV9lZGFkCnNkX2VkYWQgPC0gc2QoZGF0b3NsYiAkIEVEQUQpCnNkX2VkYWQKYGBgCmBgYGAKCkVsIHJlc3VsdGFkbyBlczoKYGBge3IgcmVzdWx0cz0nYXNpcyd9CnByb21fZWRhZCA8LSBtZWFuKGRhdG9zbGIgJCBFREFEKQpwcm9tX2VkYWQKc2RfZWRhZCA8LSBzZChkYXRvc2xiICQgRURBRCkKc2RfZWRhZApgYGAKCjQuIEVsIGNvbWFuZG8gYGVycm9yPWAgcHVlZGUgYWRvcHRhciBkb3MgdmFsb3JlczogYFRSVUVgIHkgYEZBTFNFYCAocG9yIGRlZmVjdG8pLiBFbCB2YWxvciBkZSBgVFJVRWAgaW5kaWNhIHF1ZSBzw60gc2UgbXVlc3RyZW4gbG9zIG1lbnNhamVzIGRlIGVycm9yLCBlbiBjYXNvIGRlIHF1ZSBlbCBjw7NkaWdvIGNvbnRlbmdhIGFsZ8O6biBwcm9ibGVtYSBlbiBlbCBkaXNlw7FvIG8gc3UgZXZhbHVhY2nDs24uIEFzaW1pc21vLCBzZSBkZWJlIHRvbWFyIGVuIGN1ZW50YSBxdWUgY3VhbmRvIGBlcnJvcj1GQUxTRWAgKHBvciBkZWZlY3RvKSwgYWRlbcOhcyBkZSBhcnJvamFyIGVsIG1lbnNhamUgZGUgZXJyb3IgZW4gbGEgY29uc29sYSwgdGFtYmnDqW4gc2UgZGV0ZW5kcsOhIGVsIHByb2Nlc28gZGUgcmVuZGVyaXphY2nDs24gZGVsIGRvY3VtZW50byBkZSBzYWxpZGEsIG1pZW50cmFzIHF1ZSBlbCB2YWxvciBkZSBgZXJyb3I9VFJVRWAgc8OtIHBlcm1pdGUgbGEgcmVuZGVyaXphY2nDs24gZGVsIGRvY3VtZW50bywgeSBlbiBlbCBvdXRwdXQgc2UgcHJlc2VudGEgZGljaG8gbWVuc2FqZSBkZSBlcnJvci4KCmBgYGAKYGBge3IgZXJyb3I9VFJVRX0gI2NodW5rIGNvbmZpZ3VyYWRvLgpwcm9tX2VkYWQgPC0gbWVhbihkYXRvc2xiICQgRURBRCkKcHJvbV9FREFEICNlc3RlIGPDs2RpZ28gZXN0w6EgbWFsIGVzY3JpdG8KYGBgCmBgYGAKCkVsIHJlc3VsdGFkbyBlczoKYGBge3IgZXJyb3I9VFJVRX0KcHJvbV9lZGFkIDwtIG1lYW4oZGF0b3NsYiAkIEVEQUQpCnByb21fRURBRCAjZXN0ZSBjw7NkaWdvIGVzdMOhIG1hbCBlc2NyaXRvIGEgcHJvcMOzc2l0bwpgYGAKCjUuIEVsIGNvbWFuZG8gZGUgYG1lc3NhZ2U9YCBwdWVkZSBhZG9wdGFyIGRvcyB2YWxvcmVzOiBgVFJVRWAgKHBvciBkZWZlY3RvKSB5IGBGQUxTRWAgcGVybWl0ZSBjb250cm9sYXIgbGEgcHJlc2VudGFjacOzbiBkZSBtZW5zYWplcyBhY2xhcmF0b3Jpb3Mgc29icmUgbGEgZXZhbHVhY2nDs24gZGUgbG9zIGPDs2RpZ29zLiBFbCB2YWxvciBkZSBgbWVzc2FnZT1GQUxTRWAgaW5kaWNhIHF1ZSBubyBzZSBtb3N0cmFyw6FuIGxvcyBtZW5zYWplcyBhZGljaW9uYWxlcyBnZW5lcmFkb3MgcG9yIFIuCgpgYGBgCmBgYHtyIG1lc3NhZ2U9RkFMU0V9ICNjaHVuayBjb25maWd1cmFkby4KbGlicmFyeSh0aWR5dmVyc2UpCmBgYApgYGBgCgpFbiBlbCByZXN1bHRhZG8gc2Ugb2JzZXJ2YSBxdWUgbm8gc2UgYXJyb2phIG5pbmfDum4gbWVuc2FqZSBkZWwgc2lzdGVtYSBzb2JyZSBlbCBwcm9jZXNvIGRlIGluc3RhbGFjacOzbiBkZSBgdGlkeXZlcnNlYCBhc8OtIGNvbW8gc3VzIGNvbmZsaWN0b3MgKHbDqWFzZSBsYSBhY3RpdmFjacOzbiBkZSBgdGlkeXZlcnNlYCBlbiBlbCBwdW50byAyKS4KYGBge3IgbWVzc2FnZT1GQUxTRX0KbGlicmFyeSh0aWR5dmVyc2UpCmBgYAoKNi4gRW4gY2FtYmlvLCB0YW1iacOpbiBleGlzdGUgZWwgY29tYW5kbyBgd2FybmluZz1gIHF1ZSBwdWVkZSBhZG9wdGFyIGRvcyB2YWxvcmVzOiBgVFJVRWAgKHBvciBkZWZlY3RvKSB5IGBGQUxTRWAsIHkgZXN0ZSDDumx0aW1vIHBlcm1pdGUgaW5kaWNhcmxlIGFsIHNpc3RlbWEgcXVlIG5vIHNlIG11ZXN0cmVuIGxhcyBhZHZlcnRlbmNpYXMgZGUgcG9zaWJsZXMgcHJvYmxlbWFzIGVuIGVsIGRvY3VtZW50byBkZSBzYWxpZGEsIHRyYXMgbGEgZXZhbHVhY2nDs24gZGVsIGPDs2RpZ28uCgpgYGBgCmBgYHtyIHdhcm5pbmc9RkFMU0V9ICNjaHVuayBjb25maWd1cmFkby4KbGlicmFyeSh0aWR5dmVyc2UpCmBgYApgYGBgCgpFbCByZXN1bHRhZG8gc2Vyw6EgcXVlIHNlIGFudWxhbiBsb3MgbWVuc2FqZXMgZGUgYWR2ZXJ0ZW5jaWFzIGVuIGVsIG91dHB1dDoKCmBgYHtyIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkodGlkeXZlcnNlKQpgYGAKCiMjIyAyLjIuMS4gSW5zZXJ0YXIgcmVzdWx0YWRvcyBkZSBSIGRpcmVjdGFtZW50ZSBlbiBlbCB0ZXh0by4KClRhbWJpw6luIHNlIHB1ZWRlbiBpbnNlcnRhciB2YWxvcmVzIHF1ZSBzb24gcmVzdWx0YWRvcyBkZSBjb3JyZXIgY8OzZGlnbyBkZSBSIGVuIGxvcyByZW5nbG9uZXMgZGUgdGV4dG8gcGFyYSwgYXPDrSwgbm8gZXN0YXIgZXNjcmliaWVuZG8gZGljaG8gdmFsb3Jlcy4gUGFyYSBlbGxvIHNlIHV0aWxpemEgbGEgZXN0cnVjdHVyYSBkZSAqKnIgZnVuY2nDs24ob2JqZXRvKSoqLlwKUG9yIGVqZW1wbG86IAoKYGBgYApgYGAKIkVsIHByb21lZGlvIGRlIGFuY2h1cmEgZGUgbG9zIHDDqXRhbG9zIGZ1ZSBkZSBgciBrbml0cjo6aW5saW5lX2V4cHIoIm1lYW4oaXJpcyAkIFNlcGFsLldpZHRoIilgIHB1bGdhZGFzIi4KYGBgCmBgYGAKCkRvbmRlIGVsIHJlc3VsdGFkbyBlczoKCiJFbCBwcm9tZWRpbyBkZSBhbmNodXJhIGRlIGxvcyBww6l0YWxvcyBmdWUgZGUgYHIgbWVhbihpcmlzICQgU2VwYWwuV2lkdGgpYCBwdWxnYWRhcyIuCgoKCiMjIDIuMy4gSW5jbHVpciBlY3VhY2lvbmVzIGVuIHRleHRvLgoKRW4gUiBNYXJrZG93biBlcyBwb3NpYmxlIGluY2x1aXIgZWN1YWNpb25lcyBhbGdlYnLDoWljYXMsIHF1ZSBzb24gZWxhYm9yYWRhcyBwb3IgZWwgYXV0b3IgZGVsIGRvY3VtZW50by4gUGFyYSBlbGxvIHNlIHV0aWxpemEgZWwgc2lnbm8gZGUgbW9uZWRhIGAkZWN1YWNpw7NuJGAgcGFyYSBpbmRpY2FyIGVsIGluaWNpbyB5IGVsIGZpbmFsIGRlIGxhIGVjdWFjacOzbi5cCkV4aXN0ZW4gZG9zIG1hbmVyYXMgZGUgaW5zZXJ0YXIgZGljaGFzIGVjdWFjaW9uZXMsIHVuYSBwdWVkZSBzZXIgZGVudHJvIGRlbCBtaXNtbyByZW5nbMOzbiBkZWwgdGV4dG8gcmVkYWN0YWRvIHkgbGEgb3RyYSBlcyBlbiB1biBww6FycmFmbyBhcGFydGUgYWwgcmVuZ2zDs24gZW4gZWwgcXVlIHNlIGVzY3JpYmUgbGEgZWN1YWNpw7NuLlwKQ3VhbmRvIHNlIHV0aWxpemEgc29sbyB1biBzaWdubyBgJFg9ZihYKSRgLCBsYSBlY3VhY2nDs24gc2UgaW5jbHV5ZSBkZW50cm8gZGUgbGEgbWlzbWEgbMOtbmVhIGRlbCB0ZXh0bywgcG9yIGVqZW1wbG86ICRYPWYoWCkkIFwKQ3VhbmRvIHNlIHV0aWxpemFuIGRvcyBzaWdub3MgZGUgbW9uZWRhIHRhbnRvIGFsIGluaWNpbyBjb21vIGFsIGZpbmFsIGAkJFg9ZihYKSQkYCAgZW50b25jZXMgbGEgZWN1YWNpw7NuIHNlIGluY2x1eWUgZW4gdW4gcMOhcnJhZm8gYXBhcnRlIHkgY2VudHJhZGEsIHBvciBlamVtcGxvOiAkJFg9ZihYKSQkCgpBIHN1IHZleiwgc2UgZGViZSB0b21hciBlbiBjdWVudGEgcXVlIGxhIHNpbnRheGlzIGRlIGxhcyBlY3VhY2lvbmVzIHNpZ3VlIGxhIHByb3BpYSBkZWwgc2lzdGVtYSBMYVRleCwgcGFyYSBlbGxvIHNlIHB1ZWRlIHRvbWFyIGNvbW8gcmVmZXJlbmNpYSBsYSBww6FnaW5hIGRlIGFwb3lvIGRlIFtlY3VhY2lvbmVzIFdpa2lMYXRleF0oaHR0cHM6Ly9lbi53aWtpYm9va3Mub3JnL3dpa2kvTGFUZVgvTWF0aGVtYXRpY3MpIG8gPGh0dHBzOi8vZW4ud2lraWJvb2tzLm9yZy93aWtpL0xhVGVYL01hdGhlbWF0aWNzPgoKCiMjIDIuNC4gQ29udmVydGlyIG1vZGVsb3MgZXN0YWTDrXN0aWNvcyBlbiBlY3VhY2lvbmVzIGVuIHRleHRvCk90cmEgb3BjacOzbiBwYXJhIGVzY3JpYmlyIGVjdWFjaW9uZXMgY29uc2lzdGUgZW4gaW1wb3J0YXIgZWwgY8OzZGlnbyBkZSB1biBtb2RlbG8gZXN0YWTDrXN0aWNvLCB5IHF1ZSBlc3RlIHNlYSB0cmFkdWNpZG8gY29uIGxvcyBzw61tYm9sb3MgTGFUZXggcGFyYSByZXByZXNlbnRhciBhbCBjw7NkaWdvIGVzdGFkw61zdGljbyBkZSBSLiBQYXJhIGVsbG8gc2UgdXRpbGl6YSBlbCBwYXF1ZXRlIFtlcXVhdGlvbWF0aWMoKV0oaHR0cHM6Ly9naXRodWIuY29tL2RhdGFsb3JheC9lcXVhdGlvbWF0aWMpLCBxdWUgcGVybWl0ZSB0cmFkdWNpciB1biBtb2RlbG8gZXN0YWTDrXN0aWNvIGRlIFIgZW4gdGV4dG8gZGUgUiBNYXJrZG93biwgbWVkaWFudGUgZWwgY29tYW5kbyBgZXh0cmFjdF9lcShub21icmVfZGVsX21vZGVsbylgIGEgZXh0cmFlci4KClNpIHNvbG8gc2UgZGVzZWEgcXVlIHNlIGV4dHJhaWdhIGxhIGVjdWFjacOzbiBjb24gc3VzIGxldHJhcyBhbGdlYnLDoWljYXMsIHNlIHJlYWxpemEgZGUgbGEgc2lndWllbnRlIG1hbmVyYToKCmBgYApkYXRhKCJtdGNhcnMiKSAjY2FyZ2EgZGUgbG9zIGRhdG9zCm1vZGVsbyA8LSBsbShtcGcgfiBjeWwgKyBkaXNwLCBtdGNhcnMpICNkaXNlw7FvIGRlbCBtb2RlbG8gZGUgcmVncmVzacOzbgplcXVhdGlvbWF0aWM6OmV4dHJhY3RfZXEobW9kZWxvKSAjIG1vc3RyYXIgZWwgbW9kZWxvIGVzdGFkw61zdGljbwpgYGAKCkRvbmRlIGVsIHJlc3VsdGFkbyBzZXLDoToKCmBgYHtyfQpkYXRhKCJtdGNhcnMiKSAjY2FyZ2EgZGUgbG9zIGRhdG9zCm1vZGVsbyA8LSBsbShtcGcgfiBjeWwgKyBkaXNwLCBtdGNhcnMpICNkaXNlw7FvIGRlbCBtb2RlbG8gZGUgcmVncmVzacOzbgplcXVhdGlvbWF0aWM6OmV4dHJhY3RfZXEobW9kZWxvKSAjIG1vc3RyYXIgZWwgbW9kZWxvIGVzdGFkw61zdGljbwpgYGAKCgpFbiBjYXNvIGRlIGRlc2VhciBxdWUgc2UgaW5jbHV5YW4gbG9zIHZhbG9yZXMgZGUgbG9zIGNvZWZpY2llbnRlcyBkZWwgbW9kZWxvLCBlbnRvbmNlcyBzZSBlamVjdXRhcsOhIGVsIHNjcmlwdDoKCmBgYAojIG1vc3RyYXIgZWwgdmFsb3IgZGUgbG9zIGNvZWZpY2llbnRlcwplcXVhdGlvbWF0aWM6OmV4dHJhY3RfZXEobW9kZWxvLCB1c2VfY29lZnMgPSBUUlVFKQpgYGAKRG9uZGUgZWwgcmVzdWx0YWRvIHNlcsOhOgoKYGBge3J9CiMgbW9zdHJhciBlbCB2YWxvciBkZSBsb3MgY29lZmljaWVudGVzCmVxdWF0aW9tYXRpYzo6ZXh0cmFjdF9lcShtb2RlbG8sIHVzZV9jb2VmcyA9IFRSVUUpCmBgYAoKCiMjIDIuNS4gSW5jbHVpciBsb3MgY8OzZGlnb3MgZW4gdW4gYXDDqW5kaWNlLgoKTGEgbWFuZXJhIGRlIGNvbG9jYXIgdG9kb3MgbG9zIGPDs2RpZ29zIHV0aWxpemFkb3MgZW4gdW5hIG9icmEgZGVudHJvIGRlIHVuYSBzZWNjacOzbiBkZSBhcMOpbmRpY2UsIHNlIHV0aWxpemEgZWwgc2NyaXB0IGByZWYubGFiZWxgIHkgbGEgZnVuY2nDs24gYGtuaXRyOjphbGxfbGFiZWxzKClgIGRlIGxhIHNpZ3VpZW50ZSBtYW5lcmEgZW4gdW4gImNodW5rIjoKCmBgYGAKIyBBcHBlbmRpeDogVMOzZG9zIGxvcyBjw7NkaWdvcyB1dGlsaXphZG9zIGVuIGVsIHJlcG9ydGUKCmBgYHtyIHJlZi5sYWJlbD1rbml0cjo6YWxsX2xhYmVscygpLCBlY2hvPVRSVUUsIGV2YWw9RkFMU0V9ICNjaHVuayBtb2RpZmljYWRvCmBgYApgYGBgCkVzdG8gYXJyb2phcsOhIGNvbXBsZXRhbWVudGUgdG9kb3MgbG9zIGPDs2RpZ29zLgoKUGVybyBzaSBzb2xvIHNlIGRlc2VhIHF1ZSBlbiBlbCBhcMOpbmRpY2UgZGUgY8OzZGlnb3Mgc2UgaW5jbHV5YW4gY8OzZGlnb3Mgc2VsZWNjaW9uYWRvcywgc2UgZGViZW4gcmVhbGl6YXIgYWxndW5vcyBwcm9jZWRpbWllbnRvcyBhZGljaW9uYWxlczoKCjEuIEVuIGNhZGEgdW5vIGRlIGxvcyAiY2h1bmtzIiBkZSBjw7NkaWdvIGRlc2VhZG9zIHNlIGluY2x1eWUgZWwgc2NyaXB0IGBhcHBlbmRpeCA9IFRSVUVgLCBkZSBtYW5lcmE6CmBgYGAKYGBge3IgYXBwZW5kaXggPSBUUlVFfSAjY2h1bmsgbW9kaWZpY2FkbwpgYGAKYGBgYAoKMi4gUG9zdGVyaW9ybWVudGUsIGVuIGVsICJjaHVuayIgZGVsIGFww6luZGljZSBkZSBjw7NkaWdvcyBzZSBpbmNsdXllIGVsIHNjcmlwdCBgcmVmLmxhYmVsID0ga25pdHI6OmFsbF9sYWJlbHMoYXBwZW5kaXggPT0gVFJVRSlgIHBhcmEgbGxhbWFyIHNvbG8gYSBsb3MgY8OzZGlnb3MgcHJldmlhbWVudGUgc2VsZWNjaW9uYWRvcy4KCmBgYGAKIyBBcHBlbmRpeDogVMOzZG9zIGxvcyBjw7NkaWdvcyB1dGlsaXphZG9zIGVuIGVsIHJlcG9ydGUKCmBgYHtyIHJlZi5sYWJlbCA9IGtuaXRyOjphbGxfbGFiZWxzKGFwcGVuZGl4ID09IFRSVUUpLCBlY2hvPVRSVUUsIGV2YWw9RkFMU0V9ICNjaHVuayBtb2RpZmljYWRvCmBgYApgYGBgCgojIDMuIEluY2x1aXIgaW3DoWdlbmVzIHkgZ3LDoWZpY2FzIG1lZGlhbnRlICJjaHVua3MiLgoKIyMgMy4xLiBJbmNsdWlyIGltw6FnZW5lcyBndWFyZGFkYXMgY29tbyBvYmpldG9zLgoKUiBNYXJrZG93biBwZXJtaXRlIGVsIHVzbyBkZSBkaXZlcnNvcyBtw6l0b2RvcyBwYXJhIGluc2VydGFyIGltw6FnZW5lcyBkZW50cm8gZGUgdW4gdGV4dG8gcGFyYSwgYXPDrSwgcG9zdGVyaW9ybWVudGUgcmVuZGVyaXphcmxvLiBFbiBlbCBjYXNvIGRlIHVzYXIgbGEgc2ludGF4aXMgZGUgKipNYXJrZG93bioqIGV4aXN0ZSBlbCBjb21hbmRvIGAhW25vbWJyZSBkZSBsYSBpbWFnZW5dKHJ1dGEvZGUvdWJpY2FjacOzbi9kZS9sYS9pbWFnZW4ucG5nKWAgcGFyYSBpbnNlcnRhciBpbcOhZ2VuZXMgZ3VhcmRhZGFzIGVuIGVsICoqZGlyZWN0b3JpbyBkZSB0cmFiYWpvKiogZGVsIHVzdWFyaW8gZW4gc3UgY29tcHV0YWRvcmEuXApUYW1iacOpbiBleGlzdGUgbGEgb3BjacOzbiBwYXJhIGluc2VydGFyIGltw6FnZW5lcyBkZSBkaXZlcnNvcyB0aXBvIG1lZGlhbnRlIGVsIHVzbyBkZSBsb3MgKipjaHVua3MqKiwgZW4gZWwgcXVlIHNlIHV0aWxpemEgbGEgcGFxdWV0ZXLDrWEgYGtuaXRyKClgIHkgcXVlIHBlcm1pdGUgImxsYW1hcmxvcyIgY3VhbmRvLCBwcmV2aWFtZW50ZSwgZnVlcm9uIGdlbmVyYWRvcyBtZWRpYW50ZSB1biBjw7NkaWdvIGRlIFIgeSBndWFyZGFkb3MgY29tbyBvYmpldG9zLlwKRWwgY29tYW5kbyB1dGlsaXphZG8gcGFyYSBpbmNsdWlyIGltw6FnZW5lcyBxdWUsIHByZXZpYW1lbnRlLCBmdWVyb24gZ3VhcmRhZG9zIGNvbW8gb2JqZXRvcyBlbiBSIGVzIGBrbml0ciA6OiBpbmNsdWRlX2dyYXBoaWNzKGltYWdlbjEpYCwgeSBzZSBpbmNsdXllbiBkZXNkZSB1biBjaHVuayBudWV2bywgZGUgbGEgc2lndWllbnRlIG1hbmVyYToKCmBgYHtyIH0KaW1hZ2VuMSA8LSBjKCJodHRwczovL3RlbG9zLmZ1bmRhY2lvbnRlbGVmb25pY2EuY29tL3dwLWNvbnRlbnQvdXBsb2Fkcy8yMDE5LzA0L3RlbG9zLTExMC1yZWd1bGFjaW9uLXBhcnRpZG9zLXBvbGl0aWNvcy1wcml2YWNpZGFkLWlsdXN0cmFjaW9uLWRhbmllbC10b3JuZXJvLmpwZyIpICNpbXBvcnRhY2nDs24gZGUgdW5hIGltYWdlbiBkZXNkZSBpbnRlcm5ldCB5IHNlIGd1YXJkYSBjb21vIG9iamV0by4Ka25pdHIgOjogaW5jbHVkZV9ncmFwaGljcyhpbWFnZW4xKSAjY29tYW5kbyBwYXJhIGluc2VydGFyIG9iamV0by9pbWFnZW4gZW4gZWwgZG9jdW1lbnRvIGRlIHNhbGlkYS4KYGBgCgpFbiBjYXNvIGRlIHF1ZSBzZSBkZXNlw6kgImxpbXBpYXIiIGVsIGPDs2RpZ28gcGFyYSBxdWUgc29sbyBzZSBwcmVzZW50ZSBsYSBpbWFnZW4gZW4gZWwgZG9jdW1lbnRvIGRlIHNhbGlkYSwgZW50b25jZXMgc2UgcHVlZGVuIHV0aWxpemFyIHZhcmlvcyBhcmd1bWVudG9zLCBjb21vOiBgZWNobyA9IEZBTFNFYCwgYG91dC53aWR0aCA9ICcyMCUnYCwgYGZpZy5hbGlnbiA9J2NlbnRlcidgIGRlIGxhIHNpZ3VpZW50ZSBtYW5lcmE6CgpgYGB7ciwgZWNobyA9IEZBTFNFLCBvdXQud2lkdGggPSAnMjAlJywgZmlnLmFsaWduID0nY2VudGVyJ30KaW1hZ2VuMSA8LSBjKCJodHRwczovL3RlbG9zLmZ1bmRhY2lvbnRlbGVmb25pY2EuY29tL3dwLWNvbnRlbnQvdXBsb2Fkcy8yMDE5LzA0L3RlbG9zLTExMC1yZWd1bGFjaW9uLXBhcnRpZG9zLXBvbGl0aWNvcy1wcml2YWNpZGFkLWlsdXN0cmFjaW9uLWRhbmllbC10b3JuZXJvLmpwZyIpICNpbXBvcnRhY2nDs24gZGUgdW5hIGltYWdlbiBkZXNkZSBpbnRlcm5ldCB5IHNlIGd1YXJkYSBjb21vIG9iamV0by4Ka25pdHIgOjogaW5jbHVkZV9ncmFwaGljcyhpbWFnZW4xKSAjY29tYW5kbyBwYXJhIGluc2VydGFyIG9iamV0by9pbWFnZW4gZW4gZWwgZG9jdW1lbnRvIGRlIHNhbGlkYS4KYGBgCgoKIyMgMy4yLiBJbnNlcnRhciBncsOhZmljYXMgZ2VuZXJhZGFzIGRlbnRybyBkZSB1biAiY2h1bmsiLgoKUiBNYXJrZG93biB0YW1iacOpbiBwZXJtaXRlIGxhIGluY2x1c2nDs24gZGUgZ3LDoWZpY29zIGdlbmVyYWRvcyBkZXNkZSB1biBtaXNtbyBjaHVuaywgeSBkZSBlc3RhIG1hbmVyYSBubyBlcyBuZWNlc2FyaW8gZ2VuZXJhciBlbCBncsOhZmljbyBlbiB1bmEgcGFxdWV0ZXLDrWEgZGlzdGludGEsIG5pIHRhbXBvY28gc2UgZGViZSBndWFyZGFyIG8gY29waWFyIHkgcGVnYXIgZWwgZ3LDoWZpY28gZGVudHJvIGRlbCBwcm9jZXNhZG9yIGRlIHRleHRvIChwb3IgZWouIHBpw6luc2VzZSBlbiBsb3MgcGFzb3MgcXVlIHNlIGRlYmVuIHJlYWxpemFyIHBhcmEgZ2VuZXJhciB1biBncsOhZmljbyB5IHBlZ2FybG8gYWwgbW9tZW50byBkZSByZWRhY3RhciB1biB0ZXh0byBlbiBlbCBwcm9jZXNhZG9yIGRlIHRleHRvcyBXb3JkIHUgb3RybyBwYXJlY2lkbykuXApQYXJhIGlzZXJ0YXIgdW5hIGdyw6FmaWNhIGRlc2RlIHVuICoqZGF0YSBmcmFtZSoqIGV4aXN0ZW50ZSBlbiBlbCBhbWJpZW50ZSBkZSB0cmFiYWpvIGRlIFIgU3R1ZGlvLCBwcmltZXJvIHNlIGRlYmUgZ2VuZXJhciBlbCBjw7NkaWdvIGRlbCBncsOhZmljbyBkZXNlYWRvIGVuIHVuIGNodW5rLCB5IHBvc3Rlcmlvcm1lbnRlIGRlYmUgc2VyICJsbGFtYWRvIiBkZW50cm8gZGVsIG1pc21vIHRyb3pvIGRlIGPDs2RpZ28uIEFsIG1vbWVudG8gZGUgcmVuZGVyaXphciBkaWNobyBjw7NkaWdvLCBlbCBkb2N1bWVudG8gZGUgc2FsaWRhIGNvbnRlbmRyw6EgYWwgZ3LDoWZpY28gZWxhYm9yYWRvLlwKUG9yIGVqZW1wbG8sIGEgY29udGludWFjacOzbiBzZSBnZW5lcmEgdW4gKmJveHBsb3QqIG1lZGlhbnRlIGxhIHBhcXVldGVyw61hIGBnZ3Bsb3QoKWAgYSBwYXJ0aXIgZGVsIGRhdGEgZnJhbWUgYGRhdGEoImlyaXMiKWAgKGRpc3BvbmlibGUgZGVudHJvIGRlIFIgU3R1ZGlvKSwgeSBxdWUgc2Vyw6EgcmVuZGVyaXphZG8gcG9zdGVyaW9ybWVudGUuCgpgYGB7cn0KbGlicmFyeSh0aWR5dmVyc2UpICNzZSBjYXJnYSBsYSBsaWJyZXLDrWEgcXVlIGNvbnRpZW5lIGEgZ2dwbG90CmxpYnJhcnkoZ2d0aGVtZXMpICNzZSBjYXJnYSBsYSBsaWJyZXLDrWEgcGFyYSBhZmluYXIgbG9zIGdyw6FmaWNvcyBkZSBnZ3Bsb3QKZGF0YSgiaXJpcyIpICNzZSBjYXJnYSBlbCBkYXRhIGZyYW1lCmdyYWZpY2ExIDwtIGdncGxvdChpcmlzLCBhZXMoeCA9IFNwZWNpZXMsIHkgPSBTZXBhbC5MZW5ndGgpKSArICNzZSBjcmVhIGVsIG9iamV0byB5IGVsIGxpZW56byBkZWwgZ3LDoWZpY28KICBnZW9tX2JveHBsb3QoKSArICNzZSBkZWZpbmUgZWwgZm9ybWF0byBkZSBzYWxpZGEgZGUgbGEgZ3LDoWZpY2EsIG8gbGEgZ3LDoWZpY2EgZGUgY2FqYQogIGdlb21faml0dGVyKGFlcyhjb2xvdXIgPSBTcGVjaWVzKSwgc2l6ZSA9IDMsIGFscGhhID0gMC40KSArICNzZSBpbmNsdXllbiB1biBncsOhZmljbyBkZSBkaXNwZXJzacOzbiBwYXJhIGxvcyBkYXRvcwogIHRoZW1lX21pbmltYWwoKSAjc2UgZGVmaW5lIGVsIGVzdGlsbyBkZWwgZ3LDoWZpY28KZ3JhZmljYTEgI3NlIGxsYW1hIGFsIGdyw6FmaWNvIHBhcmEgcXVlIHNlYSBpbXByZXNvCmBgYAoKQWhvcmEgc2kgc2UgZGVzZWEgaW5zZXJ0YXIgbGEgbWlzbWEgZ3LDoWZpY2EgcGVybyBxdWl0YW5kbyBlbCBjw7NkaWdvLCBsYXMgYWR2ZXJ0ZW5jaWFzIHkgbG9zIG1lbnNhamVzIHBhcmEgcXVlLCBhbCBmaW5hbCwgc29sbyBzZSBvYnNlcnZlIGVsIHJlc3VsdGFkbyBkZWwgY8OzZGlnbywgY2VudHJhZG8geSBkZSB0YW1hw7FvIHJlZHVjaWRvLCBzZSB1dGlsaXphbiBsYXMgb3BjaW9uZXMgZGUgY2h1bmsgc2lndWllbnRlczogYGVjaG89RkFMU0VgLCBgZmlnLmFsaWduPSdjZW50ZXInYCwgYG1lc3NhZ2U9RkFMU0VgLCBgd2FybmluZz1GQUxTRWAsIGBvdXQud2lkdGg9JzQ1JSdgLlwKWSBlbCByZXN1bHRhZG8gcHJvZHVjaWRvIGVzOgoKYGBge3IgZWNobz1GQUxTRSwgZmlnLmFsaWduPSdjZW50ZXInLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBvdXQud2lkdGg9JzQ1JSd9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGdndGhlbWVzKQpkYXRhKCJpcmlzIikKZ3JhZmljYTEgPC0gZ2dwbG90KGlyaXMsIGFlcyh4ID0gU3BlY2llcywgeSA9IFNlcGFsLkxlbmd0aCkpICsKICBnZW9tX2JveHBsb3QoKSArCiAgZ2VvbV9qaXR0ZXIoYWVzKGNvbG91ciA9IFNwZWNpZXMpLCBzaXplID0gMywgYWxwaGEgPSAwLjQpICsKICB0aGVtZV9taW5pbWFsKCkKZ3JhZmljYTEKYGBgCgoKIyA0LiBJbnNlcnRhciB0YWJsYXMuCgpMYSBpbnNlcmNpw7NuIGRlIHRhYmxhcyBlcyB1biBlbGVtZW50byBpbXBvcnRhbnRlIHBhcmEgbGEgcHJlc2VudGFjacOzbiBkZSB1bmEgZ3JhbiB2YXJpZWRhZCBkZSBkYXRvcy4gRW4gZXN0YSBzZWNjacOzbiBzZSByZXZpc2Fyw6FuIGFsZ3VuYXMgYWx0ZXJuYXRpdmFzIHBhcmEgaW5zZXJ0YXIgdGFibGFzIGRlIGNvbnRlbmlkbyBnZW5lcmFkYXMgZGVzZGUgb2JqZXRvcyBwcmV2aWFtZW50ZSBleGlzdGVudGVzIGVuIGVsIGFtYmllbnRlIGRlIFIgeSBSIFN0dWRpby4gRXNwZWNpYWxtZW50ZSBjdWFuZG8gc2UgdHJhYmFqYSBjb24gb2JqZXRvcyBkZWwgdGlwbyAqKmRhdGEgZnJhbWVzKiouIFRhbWJpw6luIHNlIGRlYmUgdGVuZXIgZW4gY3VlbnRhIHF1ZSBzZXLDoSBpbXBvcnRhbnRlIHJlY29ub2NlciBlbCBmb3JtYXRvIGRlIHNhbGlkYSBkZWwgZG9jdW1lbnRvICguaHRtbCwgLnBkZiwgLmRvYykgYSByZW5kZXJpemFyIHBhcmEsIGFzw60sIHViaWNhciBsYSBmb3JtYSBlbiBxdWUgc2Vyw6EgdmlzdWFsaXphZG8uCgoKIyMgNC4xLiBJbnNlcmNpw7NuIGRlIHRhYmxhcyBkZSBjb250ZW5pZG8gY29uICoqa2FibGUqKi4KCkxhIGxpYnJlcsOtYSBbYGthYmxlKClgXShodHRwczovL3d3dy5yZG9jdW1lbnRhdGlvbi5vcmcvcGFja2FnZXMva25pdHIvdmVyc2lvbnMvMS4zNy90b3BpY3Mva2FibGUpIGVzIHVuIHBhcXVldGUgcXVlIGN1ZW50YSBjb24gYnVlbmEgaW50ZWdyYWNpw7NuIGNvbiBga25pdHIoKWAsIHkgcGVybWl0ZSBsYSBnZW5lcmFjacOzbiBkZSB0YWJsYXMuIFBlcm8gc2UgZGViZSB0ZW5lciBlbiBjdWVudGEgcXVlIGxhIHByaW1lciBjb25kaWNpw7NuIHBhcmEgZ2VuZXJhciBlc3RhIGZvcm1hIGRlIHByZXNlbnRhY2nDs24gY29uc2lzdGUgZW4gcXVlIGxvcyBkYXRvcyBzZSBlbmN1ZW50cmVuIGVuIGZvcm1hdG8gcmVjdGFuZ3VsYXIgY29tbyB0aXBvICptYXRyaXoqIG8gKmRhdGEgZnJhbWVzKi4gT3RybyBlbGVtZW50byBhIHRvbWFyIGVuIGNvbnNpZGVyYWNpw7NuIGVzIHF1ZSBkaWNoYSBwYXF1ZXRlcsOtYSBubyBwZXJtaXRlIGZ1c2lvbmFyIGNlbGRhcy5cCkxhIGVzdHJ1Y3R1cmEgZGVsIHNjcmlwdCBkZSBga2FibGUoKWAgZXM6CgpgYGAKa2FibGUoeCwgZm9ybWF0LCBkaWdpdHMgPSBnZXRPcHRpb24oImRpZ2l0cyIpLCByb3cubmFtZXMgPSBOQSwKICBjb2wubmFtZXMgPSBOQSwgYWxpZ24sIGNhcHRpb24gPSBOVUxMLCBsYWJlbCA9IE5VTEwsCiAgZm9ybWF0LmFyZ3MgPSBsaXN0KCksIGVzY2FwZSA9IFRSVUUsIC4uLikKYGBgCgpJbnNlcnRhciB1bmEgdGFibGEgdXRpbGl6YW5kbyBsYSBsaWJyYXLDrWEgYGtuaXRyKClgIHkgZWwgY29tYW5kbyBga2FibGUoKWAuCgoKYGBge3J9CmRhdGEoIm10Y2FycyIpCnRhYmxhMSA8LSBoZWFkKG10Y2FycykgI3Zpc3Rhem8gYSBsb3MgcHJpbWVyb3MgNiBjYXNvcyBvIGZpbGFzCmtuaXRyIDo6IGthYmxlKHRhYmxhMSwKICAgICAgICAgICAgICAgY2FwdGlvbiA9ICJEaXN0cmlidWNpw7NuIGRlIGNhc29zIikKYGBgCgoKIyMjIDQuMS4xLiBEZWZpbmlyIGVsIGZvcm1hdG8gZGUgc2FsaWRhIGRlIGxhIHRhYmxhLgoKRWwgZm9ybWF0byBkZSBzYWxpZGEgZGUgbGEgdGFibGEgZGUgY29udGVuaWRvIHB1ZWRlIHNlciBtb2RpZmljYWRvIG1lZGlhbnRlIGVsIGFyZ3VtZW50byBgZm9ybWF0ID0gYCwgcXVlIGRlZmluZSBlbCB0aXBvIGRlIHJlc3VsdGFkbyBkZSBsYSB0YWJsYSBxdWUgc2UgZ2VuZXJhIHkgcGFyYSBlbGxvIGFjZXB0YSB2YXJpb3MgdGlwb3MgZGUgaW5kaWNhY2lvbmVzLCBjb21vOiBgcGlwZWAodGFibGEgZG9uZGUgbG9zIGRhdG9zIGVzdMOhbiBzZXBhcmFkb3MgcG9yICJiYXJyYXMiLCB2YWxvciBwb3IgZGVmZWN0byksIGBzaW1wbGVgICh0YWJsYSBzaW1wbGUgZG9uZGUgbG9zIHZhbG9yZXMgZXN0w6FuIHNlcGFyYWRvcyBwb3IgZXNwYWNpb3MpLCBgbGF0ZXhgIChlbCByZXN1bHRhZG8gc2UgYXJyb2phIGVuIHJlZGFjY2nDs24gTGFUZXgpLCBgaHRtbGAoZWwgcmVzdWx0YWRvIHNlIGFycm9qYSBlbiByZWRhY2Npw7NuIGh0bWwpLlwKUG9yIGVqZW1wbG86CgpUYWJsYSBlbiBmb3JtYXRvIGRlIGBwaXBlYAoKYGBgIHtyfQprbml0cjo6a2FibGUodGFibGExLCAicGlwZSIpIApgYGAKClRhYmxhIGVuIGZvcm1hdG8gYHNpbXBsZWAKCmBgYCB7cn0Ka25pdHI6OmthYmxlKHRhYmxhMSwgInNpbXBsZSIpCmBgYAoKClRhYmxhIGVuIGZvcm1hdG8gYGh0bWxgCgpgYGAge3J9CmtuaXRyOjprYWJsZSh0YWJsYTEsICJodG1sIikKYGBgCgoKVGFibGEgZW4gZm9ybWF0byBgbGF0ZXhgIChFc3RlIG5vIHNlcsOhIHZpc2libGUgZW4gb3RybyBmb3JtYXRvIHF1ZSBubyBzZWEgLnBkZikKCmBgYCB7cn0Ka25pdHI6OmthYmxlKHRhYmxhMSwgImxhdGV4IikKYGBgCgpUYWJsYSBlbiBmb3JtYXRvIGByc3RgIChiYXJyYXMgaG9yaXpvbnRhbGVzKQoKYGBgIHtyfQprbml0cjo6a2FibGUodGFibGExLCAicnN0IikKYGBgCgoKIyMjIDQuMS4yLiBDYW1iaWFyIGVsIG5vbWJyZSBhIGxhcyBjb2x1bW5hcy4KClBhcmEgZWxsbyBzZSB1dGlsaXphIGVsIGFyZ3VtZW50byBgY29sLm5hbWVzID0gYCB5IHNlIGluY2x1eWUgdW4gdmVjdG9yIGNvbiBlbCBub21icmUgZGUgbGFzIGNvbHVtbmFzIGVuIHRleHRvIChlbnRyZWNvbWlsbGFkbykuIEVsIG7Dum1lcm8gZGUgbm9tYnJlcyBkZSBjb2x1bW5hcyBkZWJlIHNlciBpZMOpbnRpY28gYSBsYXMgY29sdW1uYXMgaW5jbHVpZGFzIGVuIGxhIHRhYmxhLiBQb3IgZWplbXBsbzoKCmBgYHtyfQp0YWJsYTIgPC0gaGVhZChpcmlzKQprbml0cjo6a2FibGUodGFibGEyLAogIGNvbC5uYW1lcyA9IGMoJ3NlJywgJ25lY2VzaXRhbicsICdjaW5jbycsICdub21icmVzJywgJ2FxdcOtJykpCmBgYAoKCiMjIyA0LjEuMy4gRGVmaW5pciBlbCBhbGluZWFtaWVudG8gZGUgY2FkYSBjb2x1bW5hLgoKRXN0byBzZSBlc3BlY2lmaWNhIGEgcGFydGlyIGRlbCBhcmd1bWVudG8gYGFsaWduID0gYCwgeSBsb3MgdmFsb3JlcyBwb3NpYmxlcyBzb246IGBsYCBwYXJhICBpenF1aWVyZGEsIGBjYCBwYXJhIGNlbnRyYWRvLCBgcmAgcGFyYSBkZXJlY2hhLlwKVGFtYmnDqW4gc2UgcHVlZGUgZ2VuZXJhciB1bmEgdmVyc2nDs24gc2ltcGxpZmljYWRhIGRlIGFsaW5lYW1pZW50b3Mgc2Vnw7puIGVsIG7Dum1lcm8gZGUgY29sdW1uYXM6IGBrYWJsZSguLi4sIGFsaWduID0gYygnYycsICdsJykpYCwgeSBlc3RvIGVzIGxvIG1pc21vIHF1ZSBga2FibGUoLi4uLCBhbGlnbiA9ICdjbCcpYC4gUG9yIGVqZW1wbG86CgpgYGAge3J9CiMgbGVmdCwgY2VudGVyLCBjZW50ZXIsIHJpZ2h0LCByaWdodAprbml0cjo6a2FibGUodGFibGEyLCBhbGlnbiA9ICJsY2NyciIpCmBgYAoKCiMjIyA0LjEuNC4gSW5jbHVpciB0w610dWxvIGEgbGEgdGFibGEuCgpQYXJhIGVsbG8gc2UgdXNhIGVsIGFyZ3VtZW50byBgY2FwdGlvbiA9ICJub21icmUgZGUgbGEgdGFibGEiYCwgZGUgbGEgc2lndWllbnRlIG1hbmVyYToKCmBgYCB7cn0Ka25pdHI6OmthYmxlKHRhYmxhMiwgY2FwdGlvbiA9ICJFamVtcGxvIGRlIHTDrXR1bG8gZGUgbGEgdGFibGEuIikKYGBgCgojIyMgNC4xLjUuIEZvcm1hdG8gZGUgbG9zIGNvbnRlbmlkb3MgbnVtw6lyaWNvcyBkZSBsYXMgY29sdW1uYXMuCgpTZSBwdWVkZSBkZWZpbmlyIGVsIG7Dum1lcm8gZGUgZGVjaW1hbGVzIGNvbiBlbCBhcmd1bWVudG8gYGRpZ2l0cyA9ICNgCgpgYGAge3J9CmtuaXRyOjprYWJsZSh0YWJsYTEsIAogICAgICAgICAgICAgZGlnaXRzID0gMSkKYGBgCgpUYW1iacOpbiBzZSBwdWVkZSBxdWl0YXIgbGEgbm90YWNpw7NuIGNpZW50w61maWNhIGNvbiBlbCBhcmd1bWVudG8gYGZvcm1hdC5hcmdzID0gYCwgZGVudHJvIGRlbCBxdWUgc2UgcHVlZGUgdXRpbGl6YXIgYHNjaWVudGlmaWMgPSBGQUxTRWAsIGFzw60gY29tbyBhw7FhZGlyIHVuYSBjb21hIGEgbG9zIG7Dum1lcm9zIGVuIG1pbGVzIGNvbiBgYmlnLm1hcmsgPSAiLCIpYAoKYGBge3J9CmtuaXRyOjprYWJsZSh0YWJsYTEsIAogICAgICAgICAgICAgZGlnaXRzID0gMSwgCiAgICAgICAgICAgICBmb3JtYXQuYXJncyA9IGxpc3QoYmlnLm1hcmsgPSAiLCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2NpZW50aWZpYyA9IEZBTFNFKSkKYGBgCgoKCiMjIDQuMi4gTW9kaWZpY2FjaW9uZXMgcGFydGljdWxhcmVzIGEgdGFibGFzIG1lZGlhbnRlIGxpYnJlcsOtYSAqKmthYmxlRXh0cmEoKSoqLgpMYSBwYXF1ZXRlcsOtYSBga2FibGVFeHRyYSgpYCBwZXJtaXRlIHJlYWxpemFyIG1vZGlmaWNhY2lvbmVzIGVzcGVjaWFsZXMgYSB0YWJsYXMsIHBhcmEgZWxsbyBwcmltZXJvIHNlIGRlYmUgaW5zdGFsYXIgeSBhY3RpdmFyIGRpY2hhIFtwYXF1ZXRlcsOtYV0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL2thYmxlRXh0cmEvdmlnbmV0dGVzL2F3ZXNvbWVfdGFibGVfaW5faHRtbC5odG1sKToKCmBgYAojIGluc3RhbGFyIGRlc2RlIENSQU4KaW5zdGFsbC5wYWNrYWdlcygia2FibGVFeHRyYSIpCmBgYAoKRXN0ZSBwYXF1ZXRlIHNlIHB1ZWRlIHV0aWxpemFyIGNvbXBsZW1lbnRhcmlhbWVudGUgY29uIGVsIGFyZ3VtZW50byBkZSBgJT4lYChwaXBlcyBkZSBgZHBseXIoKWAgZGVudHJvIGRlIGB0aWR5dmVyc2UoKWApLlwKQSBwYXJ0aXIgZGVsIGFyZ3VtZW50byBga2FibGVfc3R5bGluZygpYCwgc2UgcHVlZGVuIG1vZGlmaWNhciBkaXZlcnNvcyBhc3BlY3RvcyBkZSBsYXMgdGFibGFzLCB5IGEgY29udGludWFjacOzbiBzZSByZXZpc2Fyw6FuIGFsZ3Vub3MgY29tYW5kb3MuCgojIyMgNC4yLjEuIFNvbWJyZWFkbyBkZSBmaWxhcyBpbnRlcmNhbGFkYXMgZGUgdW5hIHRhYmxhClBvciBlamVtcGxvLCBzZSBwdWVkZSBpbmRpY2FyIGxhIGluY2x1c2nDs24gZGUgc29tYnJlYWRvIGVuIGZpbGFzIGFsdGVybmFkYXMgZGUgbGEgdGFibGEgY29uIGVsIGFyZ3VtZW50bzogCgpgYGB7cn0KbGlicmFyeShrbml0cikKbGlicmFyeShrYWJsZUV4dHJhKQprYWJsZShoZWFkKGlyaXMpKSAlPiUKa2FibGVfc3R5bGluZyhsYXRleF9vcHRpb25zID0gInN0cmlwZWQiKQpgYGAKCkVuIGNhc28gcXVlIGRlc2VhciBhcGxpY2FyIGVsIGVzdGlsbyBzb21icmVhZG8gZW4gdW4gZG9jdW1lbnRvIExhVGV4LCBlbiBmb3JtYXRvIGRlIHNhbGlkYSBgLnBkZmAsIGVsIGFyZ3VtZW50byBzZSBkZWJlIG1vZGlmaWNhciBwb3IgYGxhdGV4X29wdGlvbnMgPSAic3RyaXBlZCJgLgoKIyMjIDQuMi4yLiBDYW1iaWFyIGVsIHRhbWHDsW8gZGUgbGEgZnVlbnRlIGVuIHVuYSB0YWJsYS4gClBhcmEgZWxsbyBzZSB1dGlsaXphIGVsIGFyZ3VtZW50byBgZm9udF9zaXplID0gI2AgZGVudHJvIGRlbCBhcmd1bWVudG8gYGthYmxlX3N0eWxpbmcoKWAuCgpgYGB7cn0Ka2FibGUoaGVhZChpcmlzLCA1KSwgYm9va3RhYnMgPSBUUlVFKSAlPiUKICBrYWJsZV9zdHlsaW5nKGZvbnRfc2l6ZSA9IDgpCmBgYAoKIyMjIDQuMi4zLiBEZWZpbmlyIHVuIGVzdGlsbyBlc3BlY8OtZmljbyBwb3IgY2FkYSBmaWxhIG8gY29sdW1uYS4KUGFyYSBlbGxvIHNlIHV0aWxpemFuIGxvcyBhcmd1bWVudG9zIGByb3dfc3BlYygpYCB5IGBjb2x1bW5fc3BlYygpYCBkZSBsYSBzaWd1aWVudGUgbWFuZXJhOgoKYGBge3J9CmthYmxlKGhlYWQoaXJpcywgNSksIGFsaWduID0gJ2MnLCBib29rdGFicyA9IFRSVUUpICU+JSAgICMjQXF1w60gc2UgZGVmaW5lbiBsb3MgZGF0b3MgZGUgbGEgdGFibGEKICByb3dfc3BlYygxLCBib2xkID0gVFJVRSwgaXRhbGljID0gVFJVRSkgJT4lICAjI0FxdcOtIHNlIGRlZmluZSBsYSBwcmltZXIgZmlsYSwgbGV0cmEgbmVncml0YXMgZSBpdMOhbGljYXMKICByb3dfc3BlYygyOjMsIGNvbG9yID0gJ3doaXRlJywgYmFja2dyb3VuZCA9ICdibGFjaycpICU+JSAjI0FxdcOtIHNlIGRlZmluZSBsYSAyYSB5IDNhIGZpbGEsIGNvbG9yIGRlIGxldHJhcyBibGFuY2FzLCBmb25kbyBuZWdybwogIHJvd19zcGVjKDQsIHVuZGVybGluZSA9IFRSVUUsIG1vbm9zcGFjZSA9IFRSVUUpICU+JSAjI0FxdcOtIHNlIGRlZmluZSBsYSA0YSBmaWxhLCBzdWJyYXlhZG8sIGNhbWJpbyBkZSBsZXRyYQogIHJvd19zcGVjKDUsIGFuZ2xlID0gNDUpICU+JSAgIyNBcXXDrSBzZSBkZWZpbmUgbGEgNWEgZmlsYSwgbGV0cmFzIGVuIMOhbmd1bG8gZGUgNDUgZ3JhZG9zCiAgY29sdW1uX3NwZWMoNSwgc3RyaWtlb3V0ID0gVFJVRSkgIyNBcXXDrSBzZSBkZWZpbmUgbGEgNWEgY29sdW1uYSwgdGFjaGFkbwpgYGAKCiMjIyA0LjIuNC4gQWdydXBhciBjb2x1bW5hcyAvIGZpbGFzIGRlbnRybyBkZSBvdHJhIGNlbGRhIG3DoXMgYW1wbGlhLgpMb3MgYXJndW1lbnRvcyBhIHV0aWxpemFyIHNvbiBgcGFja19yb3dzKClgIHBhcmEgYWdydXBhciBmaWxhcywgeSBgYWRkX2hlYWRlcl9hYm92ZSgpYCBwYXJhIGludGVncmFyIHZhcmlhcyBjb2x1bW5hcyBiYWpvIHVuIHTDrXR1bG8gZW4gY29tw7puLlwKUG9yIGVqZW1wbG86CgpgYGB7cn0KaXJpczIgPC0gaXJpc1sxOjUsIGMoMSwgMywgMiwgNCwgNSldCmthYmxlKGlyaXMyLCBib29rdGFicyA9IFRSVUUpICU+JSAjI1NlIGNyZWEgbGEgdGFibGEgeSBsYXMgY29sdW1uYXMgY29uIGRhdG9zCiAgYWRkX2hlYWRlcl9hYm92ZShjKCJMYXJnbyIgPSAyLCAiQW5jaG8iID0gMiwgIiAiID0gMSkpICU+JSAjI1NlIGluZGljYSBsYSBjcmVhY2nDs24gZGUgdW4gZW5jYWJlemFkbyBxdWUgc2UgbGxhbWFyw6EgIkxhcmdvIiB5IG9jdXBhIDIgY29sdW1uYXMsIG90cm8gZW5jYWJlemFkbyBxdWUgc2UgbGxhbWEgIkFuY2hvIiB5IHNlIGV4dGllbmRlIDIgY29sdW1uYXMgeSB1biAzZXIgZW5jYWJlemFkbyBjb24gbm9tYnJlICIodmFjw61vKSIgY29uIGV4dGVuc2nDs24gZGUgMSBjb2x1bW5hLgogIGFkZF9oZWFkZXJfYWJvdmUoYygiTWVkaWRhcyIgPSA0LCAiTcOhcyBhdHJpYnV0b3MiID0gMSkpICMjU2UgYcOxYWRlIHVuIDNlciBlbmNhYmV6YWRvLCBlbCBwcmltZXJvIHNlIGxsYW1hICJNZWRpZGFzIiB5IHNlIGV4dGllbmRlIHBvciA0IGNvbHVtbmFzIHkgZWwgMm8gc2UgbGxhbWEgIk3DoXMgYXRyaWJ1dG9zIiB5IHNlIGV4dGllbmRlIDEgY29sdW1uYQpgYGAKCkVuIGVsIGNhc28gZGVsIGFyZ3VtZW50byBgcGFja19yb3dzKClgLCBmdW5jaW9uYSBwYXJlY2lkbyBhIGBhZGRfaGVhZGVyX2Fib3ZlKClgLCBkb25kZSBwYXJhIGRlZmluaXIgZWwgbsO6bWVybyBkZSBmaWxhcyBxdWUgc2UgYWdydXBhcsOhbiBzZSBkZWJlIHV0aWxpemFyIGVsIGFyZ3VtZW50byBgaW5kZXhgLCBlbiBkb25kZSBzZSBpbmRpY2EgZWwgdMOtdHVsbyBkZWwgYWdydXBhbWllbnRvIGFzw60gY29tbyBzdSBleHRlbnNpw7NuIGRlZmluaWRvIGVuIG7Dum1lcm8gZGUgZmlsYXMsIGRlIGxhIHNpZ3VpZW50ZSBtYW5lcmE6CgpgYGB7cn0KaXJpczMgPC0gaXJpc1tjKDE6MiwgNTE6NTQsIDEwMToxMDMpLCBdICAjI1NlIGRlZmluZSBlbCBkYXRhIGZyYW1lIGZpbHRyYWRvIHBvciBsYXMgZmlsYXMKa2FibGUoaXJpczNbLCAxOjRdLCBib29rdGFicyA9IFRSVUUpICU+JSAjI1NlIGNyZWEgbGEgdGFibGEKcGFja19yb3dzKGluZGV4ID0gYygic2V0b3NhIiA9IDIsICJ2ZXJzaWNvbG9yIiA9IDQsICJ2aXJnaW5pY2EiID0gMykgI1NlIGRlZmluZSBlbCBub21icmUgZGUgYWdydXBhbWllbnRvIGRlIGxhcyBmaWxhcyBhc8OtIGNvbW8gZWwgbsO6bWVybyBkZSBmaWxhcyBpbmNsdWlkbyBlbiBjYWRhIGdydXBvCikKYGBgCgojIyMgNC4yLjUuIEFqdXN0YXIgZWwgdGFtYcOxbyBkZSB1bmEgdGFibGEgYWwgYW5jaG8gZGUgcMOhZ2luYSBlbiBMYVRleC4KQWp1c3RhciBlbCB0YW1hw7FvIGRlIHVuYSB0YWJsYSBzb2xvIGVzIGFwbGljYWJsZSBlbiBMYVRleCwgcGFyYSBlbGxvIHNlIHVzYSBsYSBmdW5jacOzbiBga2FibGVFeHRyYTo6bGFuZHNjYXBlKClgLCBwb3IgZWplbXBsbzoKCmBgYHtyfQp0YWIgPC0ga2FibGUodGFpbChtdGNhcnMsIDUpLCBib29rdGFicyA9IFRSVUUpCnRhYiAgIyB0YWJsYSBvcmlnaW5hbCwgbXV5IGFuY2hhIGVuIExhVGV4CnRhYiAlPiUKICBrYWJsZV9zdHlsaW5nKGxhdGV4X29wdGlvbnMgPSAic2NhbGVfZG93biIpICAjI0FxdcOtIHNlIHJlIGVzY2FsYSBsYSB0YWJsYSBlbiB1biBmb3JtYXRvIGRlIHNhbGlkYSAucGRmLiBTaSBlc3RvIHNlIG1pcmEgZW4gLmh0bWwgbm8gc2Ugbm90YSBuaW5nw7puIGFqdXN0ZS4KYGBgCgoKIyMgNC4zLiBGbGV4dGFibGUuCk90cmEgYWx0ZXJuYXRpdmEgcGFyYSBpbnNlcnRhciB0YWJsYXMgZXMgbWVkaWFudGUgbGEgcGFxdWV0ZXLDrWEgZGUgW2ZsZXh0YWJsZV0oaHR0cHM6Ly9hcmRhdGEtZnIuZ2l0aHViLmlvL2ZsZXh0YWJsZS1ib29rLykgZGVudHJvIGRlIG90cm8KY2h1bmsuXApFc3RhIHBhcXVldGVyw61hIHRhbWJpw6luIHBlcm1pdGUgdHJhYmFqYXIgY29uIHVuIGRhdGEgZnJhbWUsIGFkZW3DoXMgcXVlIHNlIGludGVncmEgY29uIGVsIGNvbWFuZG8gZGUgYCU+JWAgKHBpcGVzKSwgeSBjdWVudGEgY29uIGdyYW4gZmxleGliaWxpZGFkIHBhcmEgbW9kaWZpY2FyIGxvcyBlc3BhY2lvcyBkZSBsYXMgdGFibGFzIHNlZ8O6biBsYXMgbmVjZXNpZGFkZXMgZGVsIGF1dG9yLlwKRWwgY29tYW5kbyBiw6FzaWNvIHBhcmEgbGxhbWFyIGxhIGNyZWFjacOzbiBkZSB1bmEgdGFibGEgZGVzZGUgdW4gZGF0YSBmcmFtZSBlcyBtZWRpYW50ZSBlbCBjb21hbmRvIGBmbGV4dGFibGUoZGF0YS5mcmFtZSlgLCBwb3IgZWplbXBsbzoKCmBgYHtyIG1lc3NhZ2U9RkFMU0V9CmxpYnJhcnkoZmxleHRhYmxlKQpmbGV4dGFibGUoaGVhZChpcmlzKSkKYGBgCgpgZmxleHRhYmxlKClgdGFtYmnDqW4gc2UgcHVlZGUgaW50ZWdyYXIgY29uIGVsIHVzbyBkZSBsb3MgYCU+JWAgKHBpcGVzKSBkZSBgZHBseXIoKWAgYWwgbW9tZW50byBkZSAicGFzYXIiIGVsIGRhdGEgZnJhbWUgeSwgYXPDrSwgZ2VuZXJhciBsYXMgdGFibGFzIGRlIGNvbnRlbmlkbywgZGUgbGEgc2lndWllbnRlIG1hbmVyYToKCmBgYHtyfQp0YWJsYTM8LWhlYWQoaXJpcykgI2FxdcOtIHNlIGNyZWEgdW4gb2JqZXRvCnRhYmxhMyAlPiUgI3NlIGluY2x1eWUgdW4gInBpcGUiCiAgZmxleHRhYmxlKCkgI3NlIG1hbmRhIGEgbGxhbWFyIGEgbGEgdGFibGEgZGUgY29udGVuaWRvCmBgYAoKCkFmaW5hbmRvIGxhIHRhYmxhOgoKYGBge3J9CnRhYmxhMyAlPiUKICBmbGV4dGFibGUoKSAlPiUKICBmaXhfYm9yZGVyX2lzc3VlcyhwYXJ0ID0gImFsbCIpICU+JQogIGJvbGQocGFydCA9ICJoZWFkZXIiKSAlPiUKICBhbGlnbihhbGlnbiA9ICJjZW50ZXIiLCBwYXJ0ID0gImFsbCIpICU+JQogIGNvbG9yKC4sIH4gU2VwYWwuV2lkdGggPiAzLjUsIH4gU2VwYWwuV2lkdGgsIGNvbG9yID0gInJlZCIpICU+JQogIGNvbG9yKC4sIH4gU2VwYWwuTGVuZ3RoID4gNSwgfiBTZXBhbC5MZW5ndGgsIGNvbG9yID0gImJsdWUiKQogIApgYGAKCkFmaW5hbmRvIGxhIHByZXNlbnRhY2nDs24gZGUgbGEgdGFibGE6CgpgYGB7ciwgZWNobyA9IEZBTFNFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQp0YWJsYTMgJT4lCiAgZmxleHRhYmxlKCkgJT4lCiAgZml4X2JvcmRlcl9pc3N1ZXMocGFydCA9ICJhbGwiKSAlPiUKICBib2xkKHBhcnQgPSAiaGVhZGVyIikgJT4lCiAgYWxpZ24oYWxpZ24gPSAiY2VudGVyIiwgcGFydCA9ICJhbGwiKSAlPiUKICBjb2xvciguLCB+IFNlcGFsLldpZHRoID4gMy41LCB+IFNlcGFsLldpZHRoLCBjb2xvciA9ICJyZWQiKSAlPiUKICBjb2xvciguLCB+IFNlcGFsLkxlbmd0aCA+IDUsIH4gU2VwYWwuTGVuZ3RoLCBjb2xvciA9ICJibHVlIikgJT4lCiAgYmcoLiwgYmcgPSAiI2ZlZDk3NiIsIHBhcnQgPSAiaGVhZGVyIikKICAKYGBgCgoKCiMgNS4gUmVmZXJlbmNlcwoKPGRpdiBpZD0icmVmcyI+PC9kaXY+CgojIDYuIEFwcGVuZGl4Cg==