1. Introducción

El proceso habitual para hacer un informe (o unas transparencias) en el que aparezcan gráficos o tablas resumen de algún análisis estadístico consiste en :

  1. Escribir el texto en un programa (Word, Powerpoint, Prezi, etc.)
  2. Realizar los cálculos estadísticos y gráficos en otro programa (R, Stata, Eviews, etc.)
  3. Pegar los gráficos y tablas en el documento de texto.

Este proceso tiene ciertas desventajas: dificulta la investigación reproducible y puede ser tedioso de rehacer si por ejemplo cambian ligeramente los datos, etc.


En R es posible realizar todo el informe, tanto la escritura del texto como la realización de los cálculos y gráficos, en un único documento. Hay varias formas de hacerlo, pero nos centraremos en los documentos RMarkdown (.Rmd).

Los documentos RMarkdown (.Rmd) facilitan mucho la realización de informes y transparencias ya que permiten combinar texto, código y resultados de la evaluación del código en un único documento. Si por ejemplo cambian los datos sólo habría que cambiar la ruta a los nuevos datos y el informe (gráficos, tablas etc…) se volvería a generar automáticamente con los nuevos datos.

La razón para hacer los análisis reproducibles no sólo es cumplir con los estándares científicos, que también, sino también hay un interés personal para el analista. Para entenderlo puedes ver el siguiente video de 1’44’’ que muestra una de las principales ventajas de usar documentos .Rmd.

Hace poco David Keyes pregunto en Twitter a la comunidad R cuales eran los principales beneficios de usar ficheros .Rmd para tus investigaciones/informes:

Con las respuestas al tweet, David elaboró este post.


Ya hemos trabajado con algunos documentos Rmarkdown (.Rmd); de hecho, todos los tutoriales del curso (incluido éste que estás viendo) se han elaborado usando ficheros .Rmd. Los tutoriales se escriben en ficheros RMarkdown, pero estos ficheros .Rmd se convierten a html, que suele ser el formato final en el que se muestran los tutoriales. En RStudio el proceso de conversión de .Rmd a .html consiste simplemente en pinchar un botón; además, simplemente cambiando una linea podemos convertir los documentos .Rmd a una gran variedad de formatos: html, pdf, word, ioslides, beamer, etc…

Si quieres ver algunos ejemplos de la gran variedad de formatos a los que puedes transformar un documento .Rmd ve a la siguiente galería. Para iniciarte en el universo Rmarkdown puedes consultar R Markdown: The Definitive Guide.


Proceso para convertir los .Rmd a otro(s) formatos [OPCIONAL]

Como trabajamos con RStudio, en la práctica, procesar los ficheros .Rmd consistirá solamente en pinchar en el icono Knit1. Muy fácil!!

No es necesario, pero quizá os interese saber cómo se procesan realmente los ficheros .Rmd para acabar convirtiéndose en html, pdf, etc.

La respuesta es que se ocupa de ello el paquete rmarkdown que llama otro paquete de R, knitr y a un programa llamado pandoc. Si quieres más detalles puedesir aquí

En palabras: knitr se ocupa de ejecutar todos los trozos con código R que haya en el fichero .Rmd, después de ejecutar el código, pegará los resultados de la evaluación del código (gráficos, tablas etc…) junto con el texto en un documento intermedio (con extensión .md), para después transferir, con la ayuda del paquete rmarkdown, este documento .md a pandoc que se encargará de traducirlo al formato elegido (html, pdf, …)2. Knitr sabe diferenciar el texto del código R porque éste se señaliza con unas marcas.

Visualmente:



2 Creando .Rmd’s en RStudio

En la práctica, RStudio facilita mucho la creación de documentos Rmarkdown. Para generar un documento .Rmd basta con seguir la siguiente ruta de menús: File > New File > R Markdown ...

Se abrirá una ventana que nos solicitará un título y un autor para nuestro .Rmd, así como el formato de salida.

Cuando aceptemos nos generará un documento/plantilla para nuestro .Rmd. Si queremos procesarlo o “knitearlo” tendremos que hacer click en el icono Knit



3. ¿Qué son los documentos .Rmd?

Son simplemente ficheros de texto (se pueden escribir en cualquier editor de texto, por ejemplo Notepad); PERO facilitan mucho la tarea de generar informes o transparencias con contenido estadístico, ya que permiten mezclar en un mismo documento texto y código R.

El código R (así como los resultados de la evaluación del código) se mostrarán automáticamente (gracias a knitr) en el documento final; de esta forma, se facilita mucho la realización de informes y transparencias ya que evita el tener que ir copiando los resultados (tablas, gráficos etc…) en el informe.


Veamos un ejemplo con un documento Rmarkdown muy sencillo:

---
title: "El título de mi informe"
author: "Yo mismo"
date: "Marzo de 2017"
output: html_document
---

En este informe haremos un resumen (o summary) del conjunto de datos iris.

```{r}
summary(iris)
```

El trozo de arriba es un chunk de código R. Cuando compile el documento, knitr ejecutará el código y mostrará los resultados en el documento final.


Con esto finalizamos este informe.


Como veis, los documentos Rmarkdown tienen 3 partes o elementos:

  • encabezamiento
  • trozos de código R
  • texto

Luego hablaremos de ellas. Antes veamos como quedaría el documento .Rmd tras ser procesado por knitr (“kniteado”). Tras pasar por knitr y pandoc se generará un documento .html que se verá así:

Como veis, el chunk con código R summary(iris) se ha ejecutado y se ha mostrado tanto el código como el resultado de su evaluación en unas cajas de texto.



4. Partes de los ficheros .Rmd

Los documentos Rmarkdown tienen 3 partes o elementos:

  • Encabezamiento o YAML header
  • Trozos de código R(chunks)
  • Texto (escrito en markdown)

Veámoslas una a una.



Encabezamiento (YAML header)


Abajo tenéis un ejemplo sencillo de un YAML header:

---
title: "El título de mi informe"
author: "Yo mismo"
date: "Marzo de 2017"
output: html_document
---


Como podéis imaginar, el encabezamiento se coloca al principio del documento y comienza y acaba con una marca de 3 guiones: ---

En el encabezamiento se introducen elementos básicos del documento como el título, el autor, fecha, y el formato de salida del documento. En el ejemplo hemos elegido como formato de salida html; si preferimos pdf, habría que sustituir output: html_document por output: pdf_document


El YAML header puede incluir otros elementos para personalizar un poco más el documento final o output. Abajo tenéis un ejemplo más complejo de un YAML header. Es el que utilizo para hacer los tutoriales del curso.

---
title: "Documentos Rmarkdown (.Rmd)"
author: "Pedro J. Pérez"
date: "`r Sys.Date()`"  
output:
  html_document:
    code_folding: show
    theme: journal
    toc: yes
    toc_float: yes
---

En este post y en esta vignette del paquete ymlthis tienes más opciones/posibilidades a especificar en el YAML.



Chunks (o código R)


Los trozos de código R o chunks permiten hacer análisis estadísticos y mostrar los resultados en el documento final.

Para que knitr distinga las instrucciones de R del texto normal tenemos que poner las instrucciones de R dentro de unas marcas o identificadores: ```{r} al principio y ``` al final.

Por ejemplo:

```{r}
summary(iris)
```

Knitr interpreta ese trozo de texto como instrucciones de R porque van dentro de las marcas, y hará que R las ejecute y muestre los resultados en el documento final.


Los chunks pueden tienen diversas opciones que permiten una mayor flexibilidad en como se muestra el código y los resultados en el documento final. Las opciones más usadas son:

  • echo
  • eval


Por ejemplo, si introducimos este texto en nuestro fichero .Rmd:

```{r, echo = TRUE, eval = TRUE}
summary(iris)
```

En este caso, se mostrará el chunk(echo = TRUE) y también se evaluará (eval = TRUE) y, por tanto, se mostrarán los resultados en el documento final. Se verá algo como:

summary(iris)
#>   Sepal.Length    Sepal.Width     Petal.Length    Petal.Width   
#>  Min.   :4.300   Min.   :2.000   Min.   :1.000   Min.   :0.100  
#>  1st Qu.:5.100   1st Qu.:2.800   1st Qu.:1.600   1st Qu.:0.300  
#>  Median :5.800   Median :3.000   Median :4.350   Median :1.300  
#>  Mean   :5.843   Mean   :3.057   Mean   :3.758   Mean   :1.199  
#>  3rd Qu.:6.400   3rd Qu.:3.300   3rd Qu.:5.100   3rd Qu.:1.800  
#>  Max.   :7.900   Max.   :4.400   Max.   :6.900   Max.   :2.500  
#>        Species  
#>  setosa    :50  
#>  versicolor:50  
#>  virginica :50  
#>                 
#>                 
#> 


Mientras que si en el .Rmd escribimos lo siguiente:

```{r, echo = TRUE, eval = FALSE}
summary(iris)
```

Se mostrará el código (echo = TRUE), pero no se evaluará (eval = FALSE) y ,por lo tanto, no se mostrarán los resultados en el documento final.


Si en el .Rmd escribimos lo siguiente:

```{r, echo = FALSE, eval = TRUE}
summary(iris)
```

NO se mostrará el código (echo = FALSE), pero SÍ se evaluará (eval = FALSE) y ,por lo tanto, SI se mostrarán los resultados en el documento final.


Si en el .Rmd escribimos lo siguiente:

```{r, echo = FALSE, eval = TRUE, results = "hide"}
summary(iris)
```

NO se mostrará el código (echo = FALSE), SI se evaluará (eval = FALSE), PERO como results = "hide" NO se mostrarán los resultados en documento final.


Hay más opciones sobre los chunks que nos permiten una mayor flexibilidad sobre como mostrar los resultados y el código; pero si quieres ver todas las opciones tendrás que ir a la página web de knitr o al cheat sheet sobre Rmarkdown.

Una opción útil es include = FALSE; en este caso, el chunk se ejecutará, pero ni se mostrará en el documento final ni se mostrarán los resultados de la ejecución del código. Esta opción es muy útil para los chunks que se utilizan para hacer el “setup”.

Generalmente los documentos .Rmd tienen un primer chunk (chunk de setup) donde se fijan opciones globales para los chunks, fijra opciones globales, incluso se puede utilizar para cargar paquetes básicos como el tidyverse. Por ejemplo este suele ser el primer chunk en mis documentos .RMd:

```{r chunk_setup, include = FALSE}
knitr::opts_chunk$set(echo = TRUE, eval = TRUE, message = FALSE, warning = FALSE, 
                      cache = FALSE, cache.path = "/caches/", comment = "#>",
                      #fig.width = 7, fig.height= 7,   
                      #out.width = 7, out.height = 7,
                      collapse = TRUE,  fig.show = "hold", fig.retina = 3,
                      fig.asp = 7/9, out.width = "60%", fig.align = "center")
```


Con la llegada de knitr v1.35 disponemos de una nueva forma de especificar las opciones de los chunks.



El texto (en Rmarkdown)


La parte principal de un informe suele ser texto (narratives). En un fichero .Rmd, todo lo que no sea encabezamiento o chunks será interpretado por knit como texto y lo mostrará tal cual; es decir, como texto.

Aquí podríamos acabar nuestro tutoría sobre como escribir texto en un fichero .Rmd; pero generalmente en un texto queremos resaltar ciertas palabras con negrita, o ponerlas en cursiva, o poner un titulo de sección y de sub-secciones. Todo esto lo tendremos que hacer utilizando Markdown. Markdown es un lenguaje de marcas ligero y muy sencillo de aprender (lo básico se aprende en unos 10 minutos) pero muy utilizado. Es posible que alguno de vosotros haya utilizado una variante de Markdown al escribir en negrita en Whatsapp.

El texto de un documento .Rmd es “simplemente” texto PERO está escrito en Markdown. Ahora lo veremos, pero antes vamos a ver un poco más sobre qué es Markdown.


¿Qué es Markdown? [OPCIONAL]

Mardown es un lenguaje de marcado ligero ideado en 2004 por Jhon Grueber y Aaron Swartz. Hay diversas variantes de Markdown, aquí peuedes leer sobre un intento de estandarización.

Podemos pensar que Markdown es un método de escritura3: evidentemente sirve para escribir. La ventaja de escribir en Markdown es que es un lenguaje muy fácil de aprender y que como está basado en un formato de texto plano, es y será compatible con la mayoría de plataformas.

La mayoría de vosotros escribís en Word. Es muy fácil escribir en Word pero un archivo word solo es posible leerlo en el programa WORD de Microsoft. Si intentas abrir un documento .doc en Notepad, éste será completamente ilegible; sin embargo, la mayoría de plataformas y servicios web saben interpretar y mostrar correctamente un documento escrito en Markdown.

Lo que escribas en Rmarkdown se mostrará tal cual en el documento final, pero lo más habitual es que quieras dar un poco de formato el texto: negritas, cursivas, listas, enlaces de internet, etc…

Todos estos formatos (negrita, …) se introducen en rmarkdown con marcas; por ejemplo si quieres que una palabra se resalte en negritas tienes que escribirla enmarcada en **: **esto se mostraría en negrita**

Para aprender las principales reglas de Rmarkdown podéis usar un editor on-line de Markdown y probar a escribir algo.

En este otro tutorial de Markdown, se puede leer lo siguiente:

Markdown is a way to write content for the web. It’s written in what nerds like to call “plaintext”, which is exactly the sort of text you’re used to writing and seeing. Plaintext is just the regular alphabet, with a few familiar symbols. Unlike cumbersome word processing applications, text written in Markdown can be easily shared between computers, mobile phones, and people. It’s quickly becoming the writing standard for academics, scientists, writers, and many more. Websites like GitHub and reddit use Markdown to style their comments.

Aquí tienes algunas reglas de Markdown


5. Más cosas de RMarkdown

En la página web de Markdown, concretamente aquí nos avisan de lo siguiente:

Markdown is not a replacement for HTML, or even close to it. Its syntax is very small, corresponding only to a very small subset of HTML tags … The idea for Markdown is to make it easy to read, write, and edit prose. HTML is a publishing format; Markdown is a writing format. Markdown was not designed to solve everything.

A pesar de que, como nos avisó Jhon Grueber, el desarrollador de Markdown, Markdown no está diseñado para resolver todas las necesidades de un escritor/científico, en Rmarkdown se pueden introducir también elementos como:

Ecuaciones

Se pueden introducir formulas matemáticas escritas en Látex. Para formulas en linea se usa la marca $ y para formulas independientes se usa $$.

  • Para fórmulas en linea (o dentro del texto) se utiliza la marca $ al principio y final de la formula. Por ejemplo $\sum_{i=1}^n X_i$ se mistaría así: \(\sum_{i=1}^n X_i\). Ves, la formula está dentro del texto, en una linea del texto.

  • Para presentar una ecuación independiente (en una linea independiente), se usa la marca $$ al principio y final de la formula. Si escribes $$E = mc^{2}$$, se mostrará en una linea independiente tal que así:

\[E = mc^{2}\]

¿Que pasa, que no sabes escribir formulas o ecuaciones en Latex? Yo tampoco mucho, pero puedes utilizar un programa como Lyx, o mucho más fácil, puedes utilizar algún editor online de Latex, por ejemplo: este o este. En este libro tienen algunos ejemplos de ecuaciones en Latex.

Recientemente, el paquete equatiomatic permite obtener fácilmente la ecuación de un modelo:

# remotes::install_github("datalorax/equatiomatic")
library(equatiomatic)

# Fit a simple model
mod1 <- lm(mpg ~ cyl + disp, mtcars)

# Give the results to extract_eq
extract_eq(mod1)

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

También de un modelo estimado:

extract_eq(mod1, use_coefs = TRUE)

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


Imágenes

Para mostrar una imagen basta con poner:

![Una imagen chula](./imagenes/img1.jpeg) 

Aunque yo prefiero hacerlo así:

```{r eval = TRUE, echo = TRUE}
knitr::include_graphics(here::here("imagenes", "rmarkdown_ejemplos", "imagenes", "fucking_ages_image.jpeg")  )
```
Una imagén chula

Una imagén chula

Notas al pie de página

Para poner notas al pie has de poner [^1] y luego al final del documento poner [^1]: esto es una nota al pie., y se verá esto: 4

O alternativamente poner, en el sitio del texto donde quieras insertar una nota al pie, esta marca: ^[el texto que quiero que se lea en la nota al pie] y se mostrará como una nota al pie.

Tablas

Habrá un tutorial especifico para tablas, pero para mostrar una tabla, lo más básico y sencillo es utilizar la función knitr::kable():

```{r}   
knitr::kable(summary(iris))    
```    
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
Min. :4.300 Min. :2.000 Min. :1.000 Min. :0.100 setosa :50
1st Qu.:5.100 1st Qu.:2.800 1st Qu.:1.600 1st Qu.:0.300 versicolor:50
Median :5.800 Median :3.000 Median :4.350 Median :1.300 virginica :50
Mean :5.843 Mean :3.057 Mean :3.758 Mean :1.199 NA
3rd Qu.:6.400 3rd Qu.:3.300 3rd Qu.:5.100 3rd Qu.:1.800 NA
Max. :7.900 Max. :4.400 Max. :6.900 Max. :2.500 NA


6. Aún más cosas

Hay más cosas, como referencias bibliográficas, pero ya será para el segundo curso de R o lo tendrás que aprender por tu cuenta en R Markdown: The Definitive Guide.

A pesar de que Markdown permite formatear el texto, en ciertos sentidos es limitado; pero si quieres aún más flexibilidad en el output, tendrás que aprender html y mejor html+CSS.


Algunos ejemplos:

  1. Si quieres introducir un párrafo en otro color tendrás que hacerlo en html, tendrás que escribir: <FONT COLOR="Red">Esto se mostrará en ROJO!!</FONT>

    Esto se mostrará en ROJO!!


  1. si quieres centrar un párrafo, tendrás que escribir: <CENTER>Este párrafo irá centrado</CENTER>
Este párrafo irá centrado


  1. Puedes insertar en tu documento (sólo si el output es html) una página web completa, para ello has de escribir: <iframe src="http://www.eldiario.es/" height="400" width="800"></iframe>



  1. Un vídeo. Has de escribir: <iframe width="560" height="315" src="https://www.youtube.com/embed/ACv9zaBa1A4" frameborder="0" allowfullscreen></iframe>

Aunque también hay un paquete, al menos, que facilita la inserción de videos en documentos .Rmd: es el paquete vembedr.




Aún más cosas (II)

  • El paquete demoR ayuda a presentar el código R en documentos Rmd. Por ejemplo, permite marcar/highligt algunas partes de una sentencia o código R. Para aprender puedes ir a la viñeta del paquete. Por ejemplo, en el próximo chunk voy a marcar en amarillo el operador pipe %>%

  • Podemos usar iconos en nuestros Rmds. Por ejemplo: + =

  • Podemos usar cajas de colores para resaltar un trozo de texto por ejemplo para poner conclusiones. Esto lo aprendí aquí. En este otro post nos enseñan a hacer esos cuadros realmente bonitos.

Conclusiones:
- This is my first conclusion - This is my second conclusion

  • si queremos que los chunks puedan copiarse en el portapapeles, podemos usar el paquete klippy
klippy::klippy()  #- remotes::install_github("rlesur/klippy")

Aún más cosas (III)

  • Con CSS se puede personalizar completamente el aspecto de los documentos html que se generan con Rmarkdown. Aunque no sepas CSS tienes una gran variedad de formatos predefinidos. Puedes ver un listado aquí, aquí o aquí.

  • Incorporando algunas opciones al YAML, se pueden cambiar algunos aspectos de tu documento html. Puedes verlo aquí

  • El paquete bslib facilita el tuneado de los html.

  • El paquete thematic puede hacer que los gráficos reflejen el theme de RStudio que estas usando: sólo hay que hacer thematic_on().

  • Se pueden hacer tabs si despues de un título pones {.tabset .tabset-fade .tabset-pills}

  • El paquete checkdown permite crear campos y casillas de verificación. Por ejemplo:

¿Cuanto es 7 + 2?


  • Puedes incluir páginas web con knitr::include_url() , shiny’s con knitr::include_app() e imágenes con knitr::include_graphics()

  • Se pueden incluir vídeos con vembedr::embed_url()

  • El paquete fontawesome permite incluir iconos de Font Awesome en documentos RMarkdown. Por ejemplo con ` fontawesome::fa("r-project", fill = "steelblue")` podemos insertar el icono de




7. Investigación reproducible y Rmd

Hemos hablado muy poco de investigación reproducible pero, es evidente que usar documentos .Rmd y trabajar con Rprojects facilita la investigación reproducible. No la garantiza del todo, para ello habría que ver/usar más herramientas (docker, packrat, github, …). No lo vamos a hacer, pero al menos señalar dos prácticas que facilitan que tus análisis se acerquen a ser reproducibles:


  1. Cuando estás haciendo un análisis y quieres compartirlo hay que estar seguro de los packages que se necesitan cargar para replicarlo, por eso es bueno hacer el análisis desde una sesión nueva/fresca de R y cargar los paquetes al principio del script. Para ello, puede ser de utilidad saber que paquetes tienes cargados en un momento dado, y puedes saberlo con: (.packages()).

  2. A pesar de que yo no lo suelo hacer (👎), es recomendable introducir al final de los ficheros .Rmd la siguiente instrucción: sessionInfo(). De esta forma, proporcionarás información sobre que ordenador, sistema operativo y versión de R utilizaste en tu análisis, así como de las opciones locales de tu sistema (idioma, etc …) y de los paquetes que tienes cargados en memoria. Por ejemplo, ahora mismo mi sessionInfo es:


sessionInfo()
#> R version 4.2.1 (2022-06-23)
#> Platform: x86_64-pc-linux-gnu (64-bit)
#> Running under: Ubuntu 20.04.5 LTS
#> 
#> Matrix products: default
#> BLAS:   /usr/lib/x86_64-linux-gnu/blas/libblas.so.3.9.0
#> LAPACK: /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.9.0
#> 
#> locale:
#>  [1] LC_CTYPE=es_ES.UTF-8       LC_NUMERIC=C              
#>  [3] LC_TIME=es_ES.UTF-8        LC_COLLATE=es_ES.UTF-8    
#>  [5] LC_MONETARY=es_ES.UTF-8    LC_MESSAGES=es_ES.UTF-8   
#>  [7] LC_PAPER=es_ES.UTF-8       LC_NAME=C                 
#>  [9] LC_ADDRESS=C               LC_TELEPHONE=C            
#> [11] LC_MEASUREMENT=es_ES.UTF-8 LC_IDENTIFICATION=C       
#> 
#> attached base packages:
#> [1] stats     graphics  grDevices utils     datasets  methods   base     
#> 
#> other attached packages:
#>  [1] icons_0.2.0        demoR_0.0.0.9000   vembedr_0.1.5      equatiomatic_0.3.1
#>  [5] forcats_0.5.2      stringr_1.4.1      dplyr_1.0.9        purrr_0.3.4       
#>  [9] readr_2.1.2        tidyr_1.2.0        tibble_3.1.8       ggplot2_3.3.6     
#> [13] tidyverse_1.3.1   
#> 
#> loaded via a namespace (and not attached):
#>  [1] httr_1.4.4        sass_0.4.2        jsonlite_1.8.0    here_1.0.1       
#>  [5] modelr_0.1.9      bslib_0.4.0       shiny_1.7.1       assertthat_0.2.1 
#>  [9] highr_0.9         emo_0.0.0.9000    cellranger_1.1.0  yaml_2.3.5       
#> [13] pillar_1.8.1      backports_1.4.1   glue_1.6.2        digest_0.6.29    
#> [17] promises_1.2.0.1  rvest_1.0.3       colorspace_2.0-3  htmltools_0.5.3  
#> [21] httpuv_1.6.5      pkgconfig_2.0.3   tweetrmd_0.0.9    broom_1.0.0      
#> [25] haven_2.5.1       xtable_1.8-4      scales_1.2.1      fontawesome_0.2.2
#> [29] later_1.3.0       tzdb_0.3.0        generics_0.1.3    ellipsis_0.3.2   
#> [33] cachem_1.0.6      withr_2.5.0       klippy_0.0.0.9500 cli_3.3.0        
#> [37] magrittr_2.0.3    crayon_1.5.1      readxl_1.4.1      mime_0.12        
#> [41] evaluate_0.16     fs_1.5.2          fansi_1.0.3       xml2_1.3.3       
#> [45] tools_4.2.1       hms_1.1.2         lifecycle_1.0.1   munsell_0.5.0    
#> [49] reprex_2.0.2      compiler_4.2.1    jquerylib_0.1.4   rlang_1.0.4      
#> [53] grid_4.2.1        rstudioapi_0.14   rappdirs_0.3.3    rmarkdown_2.16   
#> [57] checkdown_0.0.7   gtable_0.3.0      DBI_1.1.3         curl_4.3.2       
#> [61] markdown_1.1      R6_2.5.1          lubridate_1.8.0   knitr_1.39       
#> [65] fastmap_1.1.0     utf8_1.2.2        rprojroot_2.0.3   stringi_1.7.8    
#> [69] Rcpp_1.0.9        vctrs_0.4.1       dbplyr_2.2.1      tidyselect_1.1.2 
#> [73] xfun_0.32

Otra forma de poner los detalles de la sesión es utilizar sessioninfo::session_info(); además lo usamos junto a details::details() que genera un desplegable para ver (o no) el outpt).

sessioninfo::session_info() %>% details::details(summary = 'current session info') 
current session info

─ Session info ───────────────────────────────────────────────────────────────
 setting  value
 version  R version 4.2.1 (2022-06-23)
 os       Ubuntu 20.04.5 LTS
 system   x86_64, linux-gnu
 ui       X11
 language (EN)
 collate  es_ES.UTF-8
 ctype    es_ES.UTF-8
 tz       Europe/Madrid
 date     2022-09-07
 pandoc   2.18 @ /usr/lib/rstudio/bin/quarto/bin/tools/ (via rmarkdown)

─ Packages ───────────────────────────────────────────────────────────────────
 package      * version    date (UTC) lib source
 assertthat     0.2.1      2019-03-21 [3] CRAN (R 4.0.0)
 backports      1.4.1      2021-12-13 [3] CRAN (R 4.1.2)
 broom          1.0.0      2022-07-01 [3] CRAN (R 4.2.1)
 bslib          0.4.0      2022-07-16 [3] CRAN (R 4.2.1)
 cachem         1.0.6      2021-08-19 [1] CRAN (R 4.2.0)
 cellranger     1.1.0      2016-07-27 [3] CRAN (R 4.0.0)
 checkdown      0.0.7      2020-11-01 [1] CRAN (R 4.2.0)
 cli            3.3.0      2022-04-25 [3] CRAN (R 4.2.0)
 clipr          0.8.0      2022-02-22 [3] CRAN (R 4.1.2)
 colorspace     2.0-3      2022-02-21 [3] CRAN (R 4.1.2)
 crayon         1.5.1      2022-03-26 [3] CRAN (R 4.1.3)
 curl           4.3.2      2021-06-23 [1] CRAN (R 4.2.0)
 DBI            1.1.3      2022-06-18 [3] CRAN (R 4.2.1)
 dbplyr         2.2.1      2022-06-27 [3] CRAN (R 4.2.1)
 demoR        * 0.0.0.9000 2022-08-18 [1] Github (kbodwin/demoR@a8dc5db)
 desc           1.4.1      2022-03-06 [3] CRAN (R 4.1.3)
 details        0.3.0      2022-03-27 [1] CRAN (R 4.2.0)
 digest         0.6.29     2021-12-01 [3] CRAN (R 4.1.2)
 dplyr        * 1.0.9      2022-04-28 [3] CRAN (R 4.2.0)
 ellipsis       0.3.2      2021-04-29 [3] CRAN (R 4.0.5)
 emo            0.0.0.9000 2022-05-10 [1] Github (hadley/emo@3f03b11)
 equatiomatic * 0.3.1      2022-01-30 [1] CRAN (R 4.2.0)
 evaluate       0.16       2022-08-09 [3] CRAN (R 4.2.1)
 fansi          1.0.3      2022-03-24 [3] CRAN (R 4.1.3)
 fastmap        1.1.0      2021-01-25 [3] CRAN (R 4.0.3)
 fontawesome    0.2.2      2021-07-02 [1] CRAN (R 4.2.0)
 forcats      * 0.5.2      2022-08-19 [3] CRAN (R 4.2.1)
 fs             1.5.2      2021-12-08 [3] CRAN (R 4.1.2)
 generics       0.1.3      2022-07-05 [3] CRAN (R 4.2.1)
 ggplot2      * 3.3.6      2022-05-03 [1] CRAN (R 4.2.0)
 glue           1.6.2      2022-02-24 [3] CRAN (R 4.1.2)
 gtable         0.3.0      2019-03-25 [3] CRAN (R 4.0.0)
 haven          2.5.1      2022-08-22 [3] CRAN (R 4.2.1)
 here           1.0.1      2020-12-13 [1] CRAN (R 4.2.0)
 highr          0.9        2021-04-16 [3] CRAN (R 4.0.5)
 hms            1.1.2      2022-08-19 [3] CRAN (R 4.2.1)
 htmltools      0.5.3      2022-07-18 [3] CRAN (R 4.2.1)
 httpuv         1.6.5      2022-01-05 [1] CRAN (R 4.2.0)
 httr           1.4.4      2022-08-17 [1] CRAN (R 4.2.1)
 icons        * 0.2.0      2022-05-13 [1] Github (mitchelloharawild/icons@6e4dc37)
 jquerylib      0.1.4      2021-04-26 [3] CRAN (R 4.0.5)
 jsonlite       1.8.0      2022-02-22 [3] CRAN (R 4.1.2)
 klippy         0.0.0.9500 2022-05-06 [1] Github (rlesur/klippy@378c247)
 knitr          1.39       2022-04-26 [1] CRAN (R 4.2.0)
 later          1.3.0      2021-08-18 [1] CRAN (R 4.2.0)
 lifecycle      1.0.1      2021-09-24 [3] CRAN (R 4.1.1)
 lubridate      1.8.0      2021-10-07 [3] CRAN (R 4.1.1)
 magrittr       2.0.3      2022-03-30 [3] CRAN (R 4.1.3)
 markdown       1.1        2019-08-07 [3] CRAN (R 4.0.0)
 mime           0.12       2021-09-28 [3] CRAN (R 4.1.1)
 modelr         0.1.9      2022-08-19 [3] CRAN (R 4.2.1)
 munsell        0.5.0      2018-06-12 [3] CRAN (R 4.0.0)
 pillar         1.8.1      2022-08-19 [1] CRAN (R 4.2.1)
 pkgconfig      2.0.3      2019-09-22 [3] CRAN (R 4.0.0)
 png            0.1-7      2013-12-03 [1] CRAN (R 4.2.0)
 promises       1.2.0.1    2021-02-11 [1] CRAN (R 4.2.0)
 purrr        * 0.3.4      2020-04-17 [3] CRAN (R 4.0.0)
 R6             2.5.1      2021-08-19 [3] CRAN (R 4.1.1)
 rappdirs       0.3.3      2021-01-31 [3] CRAN (R 4.0.3)
 Rcpp           1.0.9      2022-07-08 [3] CRAN (R 4.2.1)
 readr        * 2.1.2      2022-01-30 [3] CRAN (R 4.1.2)
 readxl         1.4.1      2022-08-17 [1] CRAN (R 4.2.1)
 reprex         2.0.2      2022-08-17 [3] CRAN (R 4.2.1)
 rlang          1.0.4      2022-07-12 [1] CRAN (R 4.2.1)
 rmarkdown      2.16       2022-08-24 [3] CRAN (R 4.2.1)
 rprojroot      2.0.3      2022-04-02 [3] CRAN (R 4.1.3)
 rstudioapi     0.14       2022-08-22 [3] CRAN (R 4.2.1)
 rvest          1.0.3      2022-08-19 [3] CRAN (R 4.2.1)
 sass           0.4.2      2022-07-16 [3] CRAN (R 4.2.1)
 scales         1.2.1      2022-08-20 [3] CRAN (R 4.2.1)
 sessioninfo    1.2.2      2021-12-06 [1] CRAN (R 4.2.0)
 shiny          1.7.1      2021-10-02 [1] CRAN (R 4.2.0)
 stringi        1.7.8      2022-07-11 [1] CRAN (R 4.2.1)
 stringr      * 1.4.1      2022-08-20 [3] CRAN (R 4.2.1)
 tibble       * 3.1.8      2022-07-22 [1] CRAN (R 4.2.1)
 tidyr        * 1.2.0      2022-02-01 [3] CRAN (R 4.1.2)
 tidyselect     1.1.2      2022-02-21 [3] CRAN (R 4.1.2)
 tidyverse    * 1.3.1      2021-04-15 [3] CRAN (R 4.0.5)
 tweetrmd       0.0.9      2022-05-10 [1] Github (gadenbuie/tweetrmd@075102b)
 tzdb           0.3.0      2022-03-28 [3] CRAN (R 4.1.3)
 utf8           1.2.2      2021-07-24 [3] CRAN (R 4.1.0)
 vctrs          0.4.1      2022-04-13 [3] CRAN (R 4.1.3)
 vembedr      * 0.1.5      2021-12-11 [1] CRAN (R 4.2.0)
 withr          2.5.0      2022-03-03 [3] CRAN (R 4.1.3)
 xfun           0.32       2022-08-10 [1] CRAN (R 4.2.1)
 xml2           1.3.3      2021-11-30 [3] CRAN (R 4.1.2)
 xtable         1.8-4      2019-04-21 [1] CRAN (R 4.2.0)
 yaml           2.3.5      2022-02-21 [3] CRAN (R 4.1.2)

 [1] /home/pjpv/R/x86_64-pc-linux-gnu-library/4.2
 [2] /usr/local/lib/R/site-library
 [3] /usr/lib/R/site-library
 [4] /usr/lib/R/library

──────────────────────────────────────────────────────────────────────────────


Otro ejemplo de uso de details::details():

details::details(plot(sin, xlim = c(0, 20)), summary = "My curvy plot. Thanks McBain!!")
My curvy plot. Thanks McBain!!



Si quieres ver todos los paquetes que tienes instalados en tu ordenador, lo puedes hacer así:

pkgs_instalados <- installed.packages(fields = c("Package", "Version")) %>% 
                        as.data.frame()





Bibliografía

Tutorial oficial de Rmarkdown Muy bueno y muy completo. Quizás la primera opción para aprender.

Tutorial completo. Tutorial sencillo pero bastante completo. Bastante didáctico.

Pagina oficial de knitr. Solamente para verla. Usarla sólo cuando ya “pilotes”.

Cheat Sheet de Rmarkdon en castellano. Parece complicado, pero cuando te acostumbras es una fantástica chuleta.

Tutorial de Rmarkdown en español

Tutorial de Markdown en español

Customizing & Extending R Markdown. Tutorial del desarrollador de knitr. Corto pero avanzado.

R Markdown: The Definitive Guide. Pues eso, la guía definitiva. Fantástica!!

R Markdown Cookbook. Nuevo libro, 2020, de Yihui Xie, Christophe Dervieux y Emily Riederer.

Officeverse. Un bookdown para facilitar la generación de documentos word desde Rmarkdown.

RMarkdown for Scientists de Nicholas Tierney.


  1. En realidad, under the hood estaremos usando la función rmarkdown::render()↩︎

  2. Si te interesa saber un poco más de este proceso puedes ir aquí↩︎

  3. Markdown es un lenguaje de marcado que permite la aplicación de formato a un texto empleando una serie de marcas o caracteres especiales↩︎

  4. esto es una nota al pie.↩︎

LS0tCnRpdGxlOiAiRG9jdW1lbnRvcyByZXByb2R1Y2libGVzIGNvbiAqKlJtYXJrZG93bioqIgphdXRob3I6ICJQZWRybyBKLiBQw6lyZXogKHBlZHJvLmoucGVyZXpAdXYuZXMpLiBVbml2ZXJzaXRhdCBkZSBWYWzDqG5jaWEgPGJyPiA8YnI+IFdlYiBkZWwgY3Vyc286IDxodHRwczovL3BlcmV6cDQ0LmdpdGh1Yi5pby9pbnRyby1kcy0yMi0yMy13ZWIvPiIKZGF0ZTogIk5vdmllbWJyZSBkZSAyMDE3IChhY3R1YWxpemFkbyBlbCBgciBmb3JtYXQoU3lzLnRpbWUoKSwgJyVkLSVtLSVZJylgKSIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICBjc3M6ICFleHByIGhlcmU6OmhlcmUoImFzc2V0cyIsICJzdHlsZXNfcGpwLmNzcyIpCiAgICB0aGVtZTogcGFwZXIKICAgIGhpZ2hsaWdodDogdGV4dG1hdGUKICAgIHRvYzogdHJ1ZQogICAgdG9jX2RlcHRoOiAzCiAgICB0b2NfZmxvYXQ6CiAgICAgIGNvbGxhcHNlZDogdHJ1ZQogICAgICBzbW9vdGhfc2Nyb2xsOiB0cnVlCiAgICBzZWxmX2NvbnRhaW5lZDogdHJ1ZQogICAgbnVtYmVyX3NlY3Rpb25zOiBmYWxzZQogICAgaW5jbHVkZXM6CiAgICAgIGFmdGVyX2JvZHk6ICFleHByIGhlcmU6OmhlcmUoImFzc2V0cyIsICJmb290ZXIuaHRtbCIpIAogICAgICBpbl9oZWFkZXI6IAogICAgICAgIC0gIWV4cHIgaGVyZTo6aGVyZSgiYXNzZXRzIiwgImdvb2dsZS1hbmFseXRpY3MuaHRtbCIpIAogICAgICAgIC0gIWV4cHIgaGVyZTo6aGVyZSgiYXNzZXRzIiwgImZhdmljb24tc29sLmh0bWwiKQogICAgZGZfcHJpbnQ6IGthYmxlCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCmVkaXRvcl9vcHRpb25zOgogIGNodW5rX291dHB1dF90eXBlOiBjb25zb2xlCi0tLQoKYGBge3IsIGluY2x1ZGUgPSBGQUxTRX0KbGlicmFyeSh0aWR5dmVyc2UpCmBgYAoKYGBge3IgY2h1bmstc2V0dXAsIGluY2x1ZGUgPSBGQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCBldmFsID0gVFJVRSwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAjcmVzdWx0cyA9ICJob2xkIiwKICAgICAgICAgICAgICAgICAgICAgIGNhY2hlID0gRkFMU0UsIGNhY2hlLnBhdGggPSAiL2NhY2hlcy8iLCBjb21tZW50ID0gIiM+IiwKICAgICAgICAgICAgICAgICAgICAgICNmaWcud2lkdGggPSA3LCAjZmlnLmhlaWdodD0gNywKICAgICAgICAgICAgICAgICAgICAgICNvdXQud2lkdGggPSA3LCBvdXQuaGVpZ2h0ID0gNywKICAgICAgICAgICAgICAgICAgICAgIGNvbGxhcHNlID0gVFJVRSwgIGZpZy5zaG93ID0gImhvbGQiLAogICAgICAgICAgICAgICAgICAgICAgZmlnLmFzcCA9IDcvOSwgb3V0LndpZHRoID0gIjYwJSIsIGZpZy5hbGlnbiA9ICJjZW50ZXIiKQoKIy0gcGFyYSBtZWpvcmFyIGxvcyBncsOhZmljb3MsIGJ1ZW5vIGVuIHJlYWxpZGFkIHBhcmEgcXVlIHNlIHZlYW4gaWd1YWwgZW4gZGlzdGludG9zIFNPCiMtIGh0dHBzOi8vd3d3Lmp1bXBpbmdyaXZlcnMuY29tL2Jsb2cvci1rbml0ci1tYXJrZG93bi1wbmctcGRmLWdyYXBoaWNzLwprbml0cjo6b3B0c19jaHVuayRzZXQoZGV2ID0gInBuZyIsIGRldi5hcmdzID0gbGlzdCh0eXBlID0gImNhaXJvLXBuZyIpKQpgYGAKCmBgYHtyIG9wdGlvbnMtc2V0dXAsIGluY2x1ZGUgPSBGQUxTRX0Kb3B0aW9ucyhzY2lwZW4gPSA5OTkpICMtIHBhcmEgcXVpdGFyIGxhIG5vdGFjacOzbiBjaWVudMOtZmljYQpvcHRpb25zKCJ5YW1sLmV2YWwuZXhwciIgPSBUUlVFKSAjLSBodHRwczovL2dpdGh1Yi5jb20vdmlraW5nL3IteWFtbC9pc3N1ZXMvNDcgIChsbyBwdXNlIHggZWwgcGIgY29uIGVsIHdhcm5pbmcpIEVuIHJlYWxpZGFkIGNyZW8gcXVlIG1lam9yIHNlcsOtYSBwb25lcmxvIGVuIFJQcm9maWxlCmBgYAoKCmBgYHtyIGtsaXBweSwgZWNobyA9IEZBTFNFfQprbGlwcHk6OmtsaXBweShwb3NpdGlvbiA9IGMoInRvcCIsICJyaWdodCIpKSAjLSByZW1vdGVzOjppbnN0YWxsX2dpdGh1Yigicmxlc3VyL2tsaXBweSIpCmBgYAoKLS0tLS0tLS0tLS0tLQoKPGJyPgoKIyAxLiBJbnRyb2R1Y2Npw7NuCgpFbCBwcm9jZXNvIGhhYml0dWFsIHBhcmEgaGFjZXIgdW4gaW5mb3JtZSAobyB1bmFzIHRyYW5zcGFyZW5jaWFzKSBlbiBlbCBxdWUgYXBhcmV6Y2FuIGdyw6FmaWNvcyBvIHRhYmxhcyByZXN1bWVuIGRlIGFsZ8O6biBhbsOhbGlzaXMgZXN0YWTDrXN0aWNvIGNvbnNpc3RlIGVuIDoKCiAgMS4gRXNjcmliaXIgZWwgdGV4dG8gZW4gdW4gcHJvZ3JhbWEgKFdvcmQsIFBvd2VycG9pbnQsIFByZXppLCBldGMuKSAKICAyLiBSZWFsaXphciBsb3MgY8OhbGN1bG9zIGVzdGFkw61zdGljb3MgeSBncsOhZmljb3MgZW4gb3RybyBwcm9ncmFtYSAoUiwgU3RhdGEsIEV2aWV3cywgZXRjLikgCiAgMy4gUGVnYXIgbG9zIGdyw6FmaWNvcyB5IHRhYmxhcyBlbiBlbCBkb2N1bWVudG8gZGUgdGV4dG8uIAogIApFc3RlIHByb2Nlc28gdGllbmUgY2llcnRhcyBkZXN2ZW50YWphczogZGlmaWN1bHRhIGxhIGludmVzdGlnYWNpw7NuIHJlcHJvZHVjaWJsZSB5IHB1ZWRlIHNlciB0ZWRpb3NvIGRlIHJlaGFjZXIgc2kgcG9yIGVqZW1wbG8gY2FtYmlhbiBsaWdlcmFtZW50ZSBsb3MgZGF0b3MsIGV0Yy4KCjxicj4KCioqRW4gUiBlcyBwb3NpYmxlIHJlYWxpemFyIHRvZG8gZWwgaW5mb3JtZSoqLCB0YW50byBsYSBlc2NyaXR1cmEgZGVsIHRleHRvIGNvbW8gbGEgcmVhbGl6YWNpw7NuIGRlIGxvcyBjw6FsY3Vsb3MgeSBncsOhZmljb3MsICoqZW4gdW4gw7puaWNvIGRvY3VtZW50byoqLiBIYXkgdmFyaWFzIGZvcm1hcyBkZSBoYWNlcmxvLCBwZXJvIG5vcyBjZW50cmFyZW1vcyBlbiBsb3MgZG9jdW1lbnRvcyBSTWFya2Rvd24gKC5SbWQpLiAKCgpMb3MgKipkb2N1bWVudG9zIFJNYXJrZG93biAoLlJtZCkqKiBmYWNpbGl0YW4gbXVjaG8gbGEgcmVhbGl6YWNpw7NuIGRlIGluZm9ybWVzIHkgdHJhbnNwYXJlbmNpYXMgeWEgcXVlIHBlcm1pdGVuIGNvbWJpbmFyIHRleHRvLCBjw7NkaWdvIHkgcmVzdWx0YWRvcyBkZSBsYSBldmFsdWFjacOzbiBkZWwgY8OzZGlnbyBlbiB1biDDum5pY28gZG9jdW1lbnRvLiBTaSBwb3IgZWplbXBsbyBjYW1iaWFuIGxvcyBkYXRvcyBzw7NsbyBoYWJyw61hIHF1ZSBjYW1iaWFyIGxhIHJ1dGEgYSBsb3MgbnVldm9zIGRhdG9zIHkgZWwgaW5mb3JtZSAoZ3LDoWZpY29zLCB0YWJsYXMgZXRjLi4uKSBzZSB2b2x2ZXLDrWEgYSBnZW5lcmFyIGF1dG9tw6F0aWNhbWVudGUgY29uIGxvcyBudWV2b3MgZGF0b3MuCgoKTGEgcmF6w7NuIHBhcmEgaGFjZXIgbG9zIGFuw6FsaXNpcyByZXByb2R1Y2libGVzIG5vIHPDs2xvIGVzIGN1bXBsaXIgY29uIGxvcyBlc3TDoW5kYXJlcyBjaWVudMOtZmljb3MsIHF1ZSB0YW1iacOpbiwgc2lubyB0YW1iacOpbiBoYXkgdW4gaW50ZXLDqXMgcGVyc29uYWwgcGFyYSBlbCBhbmFsaXN0YS4gUGFyYSBlbnRlbmRlcmxvIHB1ZWRlcyB2ZXIgZWwgc2lndWllbnRlIFt2aWRlbyBkZSAx4oCZNDTigJnigJldKGh0dHBzOi8vd3d3LnlvdXR1YmUuY29tL3dhdGNoP3Y9czNKbGRLb0EwencmZmVhdHVyZT15b3V0dS5iZSkgcXVlIG11ZXN0cmEgdW5hIGRlIGxhcyBwcmluY2lwYWxlcyB2ZW50YWphcyBkZSB1c2FyIGRvY3VtZW50b3MgYC5SbWRgLgoKCkhhY2UgcG9jbyBEYXZpZCBLZXllcyBwcmVndW50byBlbiBUd2l0dGVyIGEgbGEgY29tdW5pZGFkIFIgY3VhbGVzIGVyYW4gbG9zIHByaW5jaXBhbGVzIGJlbmVmaWNpb3MgZGUgdXNhciBmaWNoZXJvcyAuUm1kIHBhcmEgdHVzIGludmVzdGlnYWNpb25lcy9pbmZvcm1lczoKCgpgYGB7ciwgZWNobyA9IEZBTFNFLCBmaWcuYXNwID0gNy85fQp0d2VldHJtZDo6dHdlZXRfZW1iZWQoImh0dHBzOi8vdHdpdHRlci5jb20vZGdrZXllcy9zdGF0dXMvMTEwMTU1NDY5OTU2NjY0MTE1MiIsIHRoZW1lID0gImxpZ2h0IiwgYWxpZ24gPSAiY2VudGVyIiwgZG50ID0gVFJVRSwgbWF4d2lkdGggPSA0MDApCmBgYAoKCkNvbiBsYXMgcmVzcHVlc3RhcyBhbCB0d2VldCwgRGF2aWQgZWxhYm9yw7MgW2VzdGUgcG9zdF0oaHR0cHM6Ly9yZm9ydGhlcmVzdG9mdXMuY29tLzIwMTkvMDMvci1raWxsZXItZmVhdHVyZS1ybWFya2Rvd24vKS4KCgo8YnI+CgpZYSBoZW1vcyB0cmFiYWphZG8gY29uIGFsZ3Vub3MgKipkb2N1bWVudG9zIFJtYXJrZG93bioqIChgLlJtZGApOyBkZSBoZWNobywgdG9kb3MgbG9zIHR1dG9yaWFsZXMgZGVsIGN1cnNvIChpbmNsdWlkbyDDqXN0ZSBxdWUgZXN0w6FzIHZpZW5kbykgc2UgaGFuIGVsYWJvcmFkbyB1c2FuZG8gZmljaGVyb3MgLlJtZC4gTG9zIHR1dG9yaWFsZXMgc2UgZXNjcmliZW4gZW4gZmljaGVyb3MgUk1hcmtkb3duLCBwZXJvIGVzdG9zIGZpY2hlcm9zIC5SbWQgc2UgY29udmllcnRlbiBhIGh0bWwsIHF1ZSBzdWVsZSBzZXIgZWwgZm9ybWF0byBmaW5hbCBlbiBlbCBxdWUgc2UgbXVlc3RyYW4gbG9zIHR1dG9yaWFsZXMuIEVuIFJTdHVkaW8gZWwgcHJvY2VzbyBkZSBjb252ZXJzacOzbiBkZSAuUm1kIGEgLmh0bWwgY29uc2lzdGUgc2ltcGxlbWVudGUgZW4gcGluY2hhciB1biBib3TDs247IGFkZW3DoXMsIHNpbXBsZW1lbnRlIGNhbWJpYW5kbyB1bmEgbGluZWEgcG9kZW1vcyBjb252ZXJ0aXIgbG9zIGRvY3VtZW50b3MgLlJtZCBhIHVuYSBncmFuIHZhcmllZGFkIGRlIGZvcm1hdG9zOiBodG1sLCBwZGYsIHdvcmQsIGlvc2xpZGVzLCBiZWFtZXIsIGV0Yy4uLgoKU2kgcXVpZXJlcyB2ZXIgYWxndW5vcyBlamVtcGxvcyBkZSBsYSBncmFuIHZhcmllZGFkIGRlIGZvcm1hdG9zIGEgbG9zIHF1ZSBwdWVkZXMgdHJhbnNmb3JtYXIgdW4gZG9jdW1lbnRvIC5SbWQgdmUgYSBsYSBzaWd1aWVudGUgW2dhbGVyw61hXShodHRwOi8vcm1hcmtkb3duLnJzdHVkaW8uY29tL2dhbGxlcnkuaHRtbCkuIFBhcmEgaW5pY2lhcnRlIGVuIGVsIHVuaXZlcnNvIFJtYXJrZG93biBwdWVkZXMgY29uc3VsdGFyIFtgUiBNYXJrZG93bjogVGhlIERlZmluaXRpdmUgR3VpZGVgXShodHRwczovL2Jvb2tkb3duLm9yZy95aWh1aS9ybWFya2Rvd24vKS4KCjxicj4KCgojIyMjIFByb2Nlc28gcGFyYSBjb252ZXJ0aXIgbG9zIC5SbWQgYSBvdHJvKHMpIGZvcm1hdG9zIFtPUENJT05BTF0KCkNvbW8gdHJhYmFqYW1vcyBjb24gUlN0dWRpbywgZW4gbGEgcHLDoWN0aWNhLCBwcm9jZXNhciBsb3MgZmljaGVyb3MgLlJtZCBjb25zaXN0aXLDoSBzb2xhbWVudGUgZW4gcGluY2hhciBlbiBlbCBpY29ubyBgS25pdGBeW0VuIHJlYWxpZGFkLCB1bmRlciB0aGUgaG9vZCBlc3RhcmVtb3MgdXNhbmRvIGxhIGZ1bmNpw7NuIGBybWFya2Rvd246OnJlbmRlcigpYF0uIE11eSBmw6FjaWwhIQoKTm8gZXMgbmVjZXNhcmlvLCBwZXJvIHF1aXrDoSBvcyBpbnRlcmVzZSBzYWJlciBjw7NtbyBzZSBwcm9jZXNhbiByZWFsbWVudGUgbG9zIGZpY2hlcm9zIC5SbWQgcGFyYSBhY2FiYXIgY29udmlydGnDqW5kb3NlIGVuIGh0bWwsIHBkZiwgZXRjLgoKTGEgcmVzcHVlc3RhIGVzIHF1ZSBzZSBvY3VwYSBkZSBlbGxvIGVsIHBhcXVldGUgW2BybWFya2Rvd25gXShodHRwczovL2dpdGh1Yi5jb20vcnN0dWRpby9ybWFya2Rvd24pIHF1ZSBsbGFtYSBvdHJvIHBhcXVldGUgZGUgUiwgW2Brbml0cmBdKGh0dHBzOi8veWlodWkubmFtZS9rbml0ci8pIHkgYSB1biBwcm9ncmFtYSBsbGFtYWRvIFtgcGFuZG9jYF0oaHR0cHM6Ly9lcy53aWtpcGVkaWEub3JnL3dpa2kvUGFuZG9jKS4gU2kgcXVpZXJlcyBtw6FzIGRldGFsbGVzIHB1ZWRlc2lyIFthcXXDrV0oaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvNDA1NjM0NzkvcmVsYXRpb25zaGlwLWJldHdlZW4tci1tYXJrZG93bi1rbml0ci1wYW5kb2MtYW5kLWJvb2tkb3duKQoKKipFbiBwYWxhYnJhczoqKiBrbml0ciBzZSBvY3VwYSBkZSBlamVjdXRhciB0b2RvcyBsb3MgdHJvem9zIGNvbiBjw7NkaWdvIFIgcXVlIGhheWEgZW4gZWwgZmljaGVybyAuUm1kLCBkZXNwdcOpcyBkZSBlamVjdXRhciBlbCBjw7NkaWdvLCBwZWdhcsOhIGxvcyByZXN1bHRhZG9zIGRlIGxhIGV2YWx1YWNpw7NuIGRlbCBjw7NkaWdvIChncsOhZmljb3MsIHRhYmxhcyBldGMuLi4pIGp1bnRvIGNvbiBlbCB0ZXh0byBlbiB1biBkb2N1bWVudG8gaW50ZXJtZWRpbyAoY29uIGV4dGVuc2nDs24gLm1kKSwgcGFyYSBkZXNwdcOpcyB0cmFuc2ZlcmlyLCBjb24gbGEgYXl1ZGEgZGVsIHBhcXVldGUgYHJtYXJrZG93bmAsIGVzdGUgZG9jdW1lbnRvIC5tZCBhIGBwYW5kb2NgIHF1ZSBzZSBlbmNhcmdhcsOhIGRlIHRyYWR1Y2lybG8gYWwgZm9ybWF0byBlbGVnaWRvIChodG1sLCBwZGYsIC4uLileW1NpIHRlIGludGVyZXNhIHNhYmVyIHVuIHBvY28gbcOhcyBkZSBlc3RlIHByb2Nlc28gcHVlZGVzIGlyIFthcXXDrV0oIGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzQwNTYzNDc5L3JlbGF0aW9uc2hpcC1iZXR3ZWVuLXItbWFya2Rvd24ta25pdHItcGFuZG9jLWFuZC1ib29rZG93bildLiBLbml0ciBzYWJlIGRpZmVyZW5jaWFyIGVsIHRleHRvIGRlbCBjw7NkaWdvIFIgcG9ycXVlIMOpc3RlIHNlIHNlw7FhbGl6YSBjb24gdW5hcyBtYXJjYXMuCgoKKipWaXN1YWxtZW50ZToqKgoKYGBge3IgZWNobyA9IEZBTFNFLCBjb21tZW50ID0gIiIsICBvdXQud2lkdGggPSAnNDUlJywgZmlnLmFsaWduID0gJ2NlbnRlcid9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCBoZXJlOjpoZXJlKCJpbWFnZW5lcyIsICJ0dF8wN19pbWdfMDFfUk1hcmtkb3duRmxvdy5wbmciKSApCmBgYAoKPGJyPgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKCiMgMiBDcmVhbmRvIC5SbWQncyBlbiBSU3R1ZGlvCgoKRW4gbGEgcHLDoWN0aWNhLCBSU3R1ZGlvIGZhY2lsaXRhIG11Y2hvIGxhIGNyZWFjacOzbiBkZSBkb2N1bWVudG9zIFJtYXJrZG93bi4gUGFyYSBnZW5lcmFyIHVuIGRvY3VtZW50byAuUm1kIGJhc3RhIGNvbiBzZWd1aXIgbGEgc2lndWllbnRlIHJ1dGEgZGUgbWVuw7pzOiBgRmlsZSA+IE5ldyBGaWxlID4gUiBNYXJrZG93biAuLi5gCgpgYGB7ciBlY2hvID0gRkFMU0UsIGNvbW1lbnQgPSAiIiwgIG91dC53aWR0aCA9ICc0NSUnfQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlOjpoZXJlKCJpbWFnZW5lcyIsICJ0dF8wN19pbWdfMDJfY3JlYXItcm1hcmtkb3duLTEucG5nIikgKQpgYGAKClNlIGFicmlyw6EgdW5hIHZlbnRhbmEgcXVlIG5vcyBzb2xpY2l0YXLDoSB1biB0w610dWxvIHkgdW4gYXV0b3IgcGFyYSBudWVzdHJvIC5SbWQsIGFzw60gY29tbyBlbCBmb3JtYXRvIGRlIHNhbGlkYS4gCgpgYGB7ciBpbWFnZV9ncm9icywgZmlnLnNob3cgPSAiaG9sZCIsIGVjaG8gPSBGQUxTRSwgb3V0LndpZHRoID0gIjUwJSIsIGZpZy5hbGlnbiA9ICJkZWZhdWx0In0KIy0gZ3JhZmljb3Mgc2lkZSB0byBzaWRlIGVuIFJtYXJrZG93bjogaHR0cHM6Ly9jb21tdW5pdHkucnN0dWRpby5jb20vdC9ob3ctdG8tc3RhY2stdHdvLWltYWdlcy1ob3Jpem9udGFsbHktaW4tci1tYXJrZG93bi8xODk0MS8xMgoKCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltYWdlbmVzIiwgInR0XzA3X2ltZ18wM19jcmVhci1ybWFya2Rvd24tMi5wbmciKSkKCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltYWdlbmVzIiwgInR0XzA3X2ltZ18wNF9jcmVhci1ybWFya2Rvd24tMy5wbmciKSkKCmBgYAoKCkN1YW5kbyBhY2VwdGVtb3Mgbm9zIGdlbmVyYXLDoSB1biBkb2N1bWVudG8vcGxhbnRpbGxhIHBhcmEgbnVlc3RybyAuUm1kLiBTaSBxdWVyZW1vcyBwcm9jZXNhcmxvIG8gImtuaXRlYXJsbyIgdGVuZHJlbW9zIHF1ZSBoYWNlciBjbGljayBlbiBlbCBpY29ubyAqKmBLbml0YCoqCgoKYGBge3IgaW1hZ2VfZ3JvYnNfMiwgZmlnLnNob3cgPSAiaG9sZCIsIGVjaG8gPSBGQUxTRSwgb3V0LndpZHRoID0gIjUwJSIsIGZpZy5hbGlnbiA9ICJkZWZhdWx0In0KCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltYWdlbmVzIiwgInR0XzA3X2ltZ18wNV9jcmVhci1ybWFya2Rvd24tNC5wbmciKSkKCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltYWdlbmVzIiwgInR0XzA3X2ltZ18wNl9jcmVhci1ybWFya2Rvd24tNS5wbmciKSkKYGBgCgoKCgo8YnI+CgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgMy4gwr9RdcOpIHNvbiBsb3MgZG9jdW1lbnRvcyAuUm1kPwoKU29uIHNpbXBsZW1lbnRlIGZpY2hlcm9zIGRlIHRleHRvIChzZSBwdWVkZW4gZXNjcmliaXIgZW4gY3VhbHF1aWVyIGVkaXRvciBkZSB0ZXh0bywgcG9yIGVqZW1wbG8gTm90ZXBhZCk7IFBFUk8gKipmYWNpbGl0YW4gbXVjaG8gbGEgdGFyZWEgZGUgZ2VuZXJhciBpbmZvcm1lcyBvIHRyYW5zcGFyZW5jaWFzKiogY29uIGNvbnRlbmlkbyBlc3RhZMOtc3RpY28sIHlhIHF1ZSBwZXJtaXRlbiBtZXpjbGFyIGVuIHVuIG1pc21vIGRvY3VtZW50byB0ZXh0byB5IGPDs2RpZ28gUi4gCgpFbCBjw7NkaWdvIFIgKGFzw60gY29tbyBsb3MgcmVzdWx0YWRvcyBkZSBsYSBldmFsdWFjacOzbiBkZWwgY8OzZGlnbykgc2UgbW9zdHJhcsOhbiBhdXRvbcOhdGljYW1lbnRlIChncmFjaWFzIGEgYGtuaXRyYCkgZW4gZWwgZG9jdW1lbnRvIGZpbmFsOyBkZSBlc3RhIGZvcm1hLCBzZSBmYWNpbGl0YSBtdWNobyBsYSByZWFsaXphY2nDs24gZGUgaW5mb3JtZXMgeSB0cmFuc3BhcmVuY2lhcyB5YSBxdWUgZXZpdGEgZWwgdGVuZXIgcXVlIGlyIGNvcGlhbmRvIGxvcyByZXN1bHRhZG9zICh0YWJsYXMsIGdyw6FmaWNvcyBldGMuLi4pIGVuIGVsIGluZm9ybWUuCgo8YnI+CgpWZWFtb3MgdW4gKiplamVtcGxvKiogY29uIHVuIGRvY3VtZW50byBSbWFya2Rvd24gbXV5IHNlbmNpbGxvOgoKCmBgYHtyIGVjaG8gPSBGQUxTRSwgY29tbWVudCA9ICIifQpjYXQoaHRtbHRvb2xzOjppbmNsdWRlVGV4dChoZXJlOjpoZXJlKCIuL2ltYWdlbmVzL3JtYXJrZG93bl9lamVtcGxvcy9lamVtcGxvXzEuUm1kIikpKQpgYGAKCjxicj4KCkNvbW8gdmVpcywgbG9zIGRvY3VtZW50b3MgUm1hcmtkb3duIHRpZW5lbiAzIHBhcnRlcyBvIGVsZW1lbnRvczogCgogIC0gZW5jYWJlemFtaWVudG8KICAtIHRyb3pvcyBkZSBjw7NkaWdvIFIKICAtIHRleHRvCgpMdWVnbyBoYWJsYXJlbW9zIGRlIGVsbGFzLiBBbnRlcyB2ZWFtb3MgY29tbyBxdWVkYXLDrWEgZWwgZG9jdW1lbnRvIC5SbWQgdHJhcyBzZXIgcHJvY2VzYWRvIHBvciBrbml0ciAoImtuaXRlYWRvIikuIFRyYXMgcGFzYXIgcG9yIGtuaXRyIHkgcGFuZG9jIHNlIGdlbmVyYXLDoSB1biBkb2N1bWVudG8gLmh0bWwgcXVlIHNlIHZlcsOhIGFzw606CgoKYGBge3IgZWNobyA9IEZBTFNFLCBjb21tZW50ID0gIiIsICBvdXQud2lkdGggPSAnODUlJywgZmlnLmFsaWduID0gJ2NlbnRlcid9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltYWdlbmVzIiwgInJtYXJrZG93bl9lamVtcGxvcyIsICJpbWFnZW5lcyIsICJlamVtcGxvXzFfaW1hZ2VuLlBORyIpICkKYGBgCgoKQ29tbyB2ZWlzLCBlbCBjaHVuayBjb24gY8OzZGlnbyBSIGBzdW1tYXJ5KGlyaXMpYCBzZSBoYSBlamVjdXRhZG8geSBzZSBoYSBtb3N0cmFkbyB0YW50byBlbCBjw7NkaWdvIGNvbW8gZWwgcmVzdWx0YWRvIGRlIHN1IGV2YWx1YWNpw7NuIGVuIHVuYXMgY2FqYXMgZGUgdGV4dG8uCgo8YnI+CgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgNC4gUGFydGVzIGRlIGxvcyBmaWNoZXJvcyAuUm1kCgpMb3MgZG9jdW1lbnRvcyBSbWFya2Rvd24gdGllbmVuIDMgcGFydGVzIG8gZWxlbWVudG9zOgoKICAtIEVuY2FiZXphbWllbnRvIG8gWUFNTCBoZWFkZXIgCiAgLSBUcm96b3MgZGUgY8OzZGlnbyBSKGNodW5rcykKICAtIFRleHRvIChlc2NyaXRvIGVuIG1hcmtkb3duKQoKICAKVmXDoW1vc2xhcyB1bmEgYSB1bmEuCgo8YnI+CgotLS0tLS0tLS0tLS0tLS0tLS0tCiAgCiMjIEVuY2FiZXphbWllbnRvIChZQU1MIGhlYWRlcikKCjxicj4KCkFiYWpvIHRlbsOpaXMgdW4gZWplbXBsbyBzZW5jaWxsbyBkZSB1biBZQU1MIGhlYWRlcjoKCmBgYHtyIGVjaG8gPSBGQUxTRSwgY29tbWVudCA9ICIifQpjYXQoaHRtbHRvb2xzOjppbmNsdWRlVGV4dChoZXJlOjpoZXJlKCIuL2ltYWdlbmVzL3JtYXJrZG93bl9lamVtcGxvcy9lamVtcGxvX3lhbWxfMC5SbWQiKSkpCmBgYAo8YnI+CgpDb21vIHBvZMOpaXMgaW1hZ2luYXIsIGVsIGVuY2FiZXphbWllbnRvIHNlIGNvbG9jYSAqKmFsIHByaW5jaXBpbyBkZWwgZG9jdW1lbnRvIHkgY29taWVuemEgeSBhY2FiYSBjb24gdW5hIG1hcmNhIGRlIDMgZ3Vpb25lcyoqOiAqKmAtLS1gKioKCkVuIGVsIGVuY2FiZXphbWllbnRvIHNlIGludHJvZHVjZW4gZWxlbWVudG9zIGLDoXNpY29zIGRlbCBkb2N1bWVudG8gY29tbyBlbCB0w610dWxvLCBlbCBhdXRvciwgZmVjaGEsIHkgZWwgZm9ybWF0byBkZSBzYWxpZGEgZGVsIGRvY3VtZW50by4gRW4gZWwgZWplbXBsbyBoZW1vcyBlbGVnaWRvIGNvbW8gZm9ybWF0byBkZSBzYWxpZGEgYGh0bWxgOyBzaSBwcmVmZXJpbW9zIHBkZiwgaGFicsOtYSBxdWUgc3VzdGl0dWlyIGBvdXRwdXQ6IGh0bWxfZG9jdW1lbnRgIHBvciBgb3V0cHV0OiBwZGZfZG9jdW1lbnRgCgo8YnI+CgpFbCBZQU1MIGhlYWRlciBwdWVkZSBpbmNsdWlyIG90cm9zIGVsZW1lbnRvcyBwYXJhIHBlcnNvbmFsaXphciB1biBwb2NvIG3DoXMgZWwgZG9jdW1lbnRvIGZpbmFsIG8gb3V0cHV0LiBBYmFqbyB0ZW7DqWlzIHVuIGVqZW1wbG8gbcOhcyBjb21wbGVqbyBkZSB1biBZQU1MIGhlYWRlci4gRXMgZWwgcXVlIHV0aWxpem8gcGFyYSBoYWNlciBsb3MgdHV0b3JpYWxlcyBkZWwgY3Vyc28uCgoKYGBge3IgZWNobyA9IEZBTFNFLCBjb21tZW50ID0gIiJ9CmNhdChodG1sdG9vbHM6OmluY2x1ZGVUZXh0KGhlcmU6OmhlcmUoIi4vaW1hZ2VuZXMvcm1hcmtkb3duX2VqZW1wbG9zL2VqZW1wbG9feWFtbF8xLlJtZCIpKSkKYGBgCgpFbiBbZXN0ZSBwb3N0XShodHRwczovL3NjaWVuY2Vsb2Z0LmNvbS90ZWNobmljYWwvdXNlZnVsLXlhbWwtb3B0aW9ucy1mb3ItZ2VuZXJhdGluZy1odG1sLXJlcG9ydHMtaW4tci8pIHkgZW4gW2VzdGEgdmlnbmV0dGVdKGh0dHBzOi8veW1sdGhpcy5yLWxpYi5vcmcvYXJ0aWNsZXMveWFtbC1maWVsZGd1aWRlLmh0bWwpIGRlbCBwYXF1ZXRlIGB5bWx0aGlzYCB0aWVuZXMgbcOhcyBvcGNpb25lcy9wb3NpYmlsaWRhZGVzIGEgZXNwZWNpZmljYXIgZW4gZWwgWUFNTC4KCjxicj4KCgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMgQ2h1bmtzIChvIGPDs2RpZ28gUikKCjxicj4KCkxvcyB0cm96b3MgZGUgY8OzZGlnbyBSIG8gY2h1bmtzIHBlcm1pdGVuIGhhY2VyIGFuw6FsaXNpcyBlc3RhZMOtc3RpY29zIHkgbW9zdHJhciBsb3MgcmVzdWx0YWRvcyBlbiBlbCBkb2N1bWVudG8gZmluYWwuCgpQYXJhIHF1ZSBrbml0ciBkaXN0aW5nYSBsYXMgaW5zdHJ1Y2Npb25lcyBkZSBSIGRlbCB0ZXh0byBub3JtYWwgKip0ZW5lbW9zIHF1ZSBwb25lciBsYXMgaW5zdHJ1Y2Npb25lcyBkZSBSIGRlbnRybyBkZSB1bmFzIG1hcmNhcyoqIG8gaWRlbnRpZmljYWRvcmVzOiBgIGBgYHtyfSBgIGFsIHByaW5jaXBpbyB5IGAgYGBgIGAgYWwgZmluYWwuIAoKUG9yIGVqZW1wbG86CgoKYGBge3IgZWNobyA9IEZBTFNFLCBjb21tZW50ID0gIiJ9CmNhdChodG1sdG9vbHM6OmluY2x1ZGVUZXh0KGhlcmU6OmhlcmUoIi4vaW1hZ2VuZXMvcm1hcmtkb3duX2VqZW1wbG9zL2NodW5rcy9jaHVua18xIikpKQpgYGAKCgpLbml0ciBpbnRlcnByZXRhIGVzZSB0cm96byBkZSB0ZXh0byBjb21vIGluc3RydWNjaW9uZXMgZGUgUiBwb3JxdWUgdmFuIGRlbnRybyBkZSBsYXMgbWFyY2FzLCB5IGhhcsOhIHF1ZSBSIGxhcyBlamVjdXRlIHkgbXVlc3RyZSBsb3MgcmVzdWx0YWRvcyBlbiBlbCBkb2N1bWVudG8gZmluYWwuCgo8YnI+CgpMb3MgY2h1bmtzIHB1ZWRlbiB0aWVuZW4gZGl2ZXJzYXMgb3BjaW9uZXMgcXVlIHBlcm1pdGVuIHVuYSBtYXlvciBmbGV4aWJpbGlkYWQgZW4gY29tbyBzZSBtdWVzdHJhIGVsIGPDs2RpZ28geSBsb3MgcmVzdWx0YWRvcyBlbiBlbCBkb2N1bWVudG8gZmluYWwuIExhcyBvcGNpb25lcyBtw6FzIHVzYWRhcyBzb246CgogIC0gZWNobwogIC0gZXZhbAoKPGJyPgoKUG9yIGVqZW1wbG8sIHNpIGludHJvZHVjaW1vcyBlc3RlIHRleHRvIGVuIG51ZXN0cm8gZmljaGVybyAuUm1kOgoKYGBge3IgZWNobyA9IEZBTFNFLCBjb21tZW50ID0gIiJ9CmNhdChodG1sdG9vbHM6OmluY2x1ZGVUZXh0KGhlcmU6OmhlcmUoIi4vaW1hZ2VuZXMvcm1hcmtkb3duX2VqZW1wbG9zL2NodW5rcy9jaHVua18yIikpKQpgYGAKCkVuIGVzdGUgY2Fzbywgc2UgbW9zdHJhcsOhIGVsIGNodW5rKGBlY2hvID0gVFJVRWApIHkgdGFtYmnDqW4gc2UgZXZhbHVhcsOhIChgZXZhbCA9IFRSVUVgKSB5LCBwb3IgdGFudG8sIHNlIG1vc3RyYXLDoW4gbG9zIHJlc3VsdGFkb3MgZW4gZWwgZG9jdW1lbnRvIGZpbmFsLiBTZSB2ZXLDoSBhbGdvIGNvbW86CgoKYGBge3IsIGVjaG8gPSBUUlVFLCBldmFsID0gVFJVRX0Kc3VtbWFyeShpcmlzKQpgYGAKCgo8YnI+CgoKTWllbnRyYXMgcXVlIHNpIGVuIGVsIC5SbWQgZXNjcmliaW1vcyBsbyBzaWd1aWVudGU6CgoKYGBge3IgZWNobyA9IEZBTFNFLCBjb21tZW50ID0gIiJ9CmNhdChodG1sdG9vbHM6OmluY2x1ZGVUZXh0KGhlcmU6OmhlcmUoIi4vaW1hZ2VuZXMvcm1hcmtkb3duX2VqZW1wbG9zL2NodW5rcy9jaHVua18zIikpKQpgYGAKClNlIG1vc3RyYXLDoSBlbCBjw7NkaWdvIChgZWNobyA9IFRSVUVgKSwgcGVybyBubyBzZSBldmFsdWFyw6EgKGBldmFsID0gRkFMU0VgKSB5ICxwb3IgbG8gdGFudG8sIG5vIHNlIG1vc3RyYXLDoW4gbG9zIHJlc3VsdGFkb3MgZW4gZWwgZG9jdW1lbnRvIGZpbmFsLgoKCjxicj4KClNpIGVuIGVsIC5SbWQgZXNjcmliaW1vcyBsbyBzaWd1aWVudGU6CgoKYGBge3IgZWNobyA9IEZBTFNFLCBjb21tZW50ID0gIiJ9CmNhdChodG1sdG9vbHM6OmluY2x1ZGVUZXh0KGhlcmU6OmhlcmUoIi4vaW1hZ2VuZXMvcm1hcmtkb3duX2VqZW1wbG9zL2NodW5rcy9jaHVua180IikpKQpgYGAKCk5PIHNlIG1vc3RyYXLDoSBlbCBjw7NkaWdvIChgZWNobyA9IEZBTFNFYCksIHBlcm8gU8ONIHNlIGV2YWx1YXLDoSAoYGV2YWwgPSBGQUxTRWApIHkgLHBvciBsbyB0YW50bywgU0kgc2UgbW9zdHJhcsOhbiBsb3MgcmVzdWx0YWRvcyBlbiBlbCBkb2N1bWVudG8gZmluYWwuCgo8YnI+CgpTaSBlbiBlbCAuUm1kIGVzY3JpYmltb3MgbG8gc2lndWllbnRlOgoKCmBgYHtyIGVjaG8gPSBGQUxTRSwgY29tbWVudCA9ICIifQpjYXQoaHRtbHRvb2xzOjppbmNsdWRlVGV4dChoZXJlOjpoZXJlKCIuL2ltYWdlbmVzL3JtYXJrZG93bl9lamVtcGxvcy9jaHVua3MvY2h1bmtfNSIpKSkKYGBgCgpOTyBzZSBtb3N0cmFyw6EgZWwgY8OzZGlnbyAoYGVjaG8gPSBGQUxTRWApLCAgU0kgc2UgZXZhbHVhcsOhIChgZXZhbCA9IEZBTFNFYCksIFBFUk8gY29tbyBgcmVzdWx0cyA9ICJoaWRlImAgTk8gc2UgbW9zdHJhcsOhbiBsb3MgcmVzdWx0YWRvcyBlbiBkb2N1bWVudG8gZmluYWwuCgoKPGJyPgoKSGF5IG3DoXMgb3BjaW9uZXMgc29icmUgbG9zIGNodW5rcyBxdWUgbm9zIHBlcm1pdGVuIHVuYSBtYXlvciBmbGV4aWJpbGlkYWQgc29icmUgY29tbyBtb3N0cmFyIGxvcyByZXN1bHRhZG9zIHkgZWwgY8OzZGlnbzsgcGVybyBzaSBxdWllcmVzIHZlciB0b2RhcyBsYXMgb3BjaW9uZXMgdGVuZHLDoXMgcXVlIGlyIGEgbGEgW3DDoWdpbmEgd2ViIGRlIGtuaXRyXShodHRwczovL3lpaHVpLm5hbWUva25pdHIvb3B0aW9ucy8pIG8gYWwgW2NoZWF0IHNoZWV0IHNvYnJlIFJtYXJrZG93bl0oaHR0cHM6Ly93d3cucnN0dWRpby5jb20vd3AtY29udGVudC91cGxvYWRzLzIwMTYvMDMvcm1hcmtkb3duLWNoZWF0c2hlZXQtMi4wLnBkZikuIAoKVW5hIG9wY2nDs24gw7p0aWwgZXMgYGluY2x1ZGUgPSBGQUxTRWA7IGVuIGVzdGUgY2FzbywgZWwgY2h1bmsgc2UgZWplY3V0YXLDoSwgcGVybyBuaSBzZSBtb3N0cmFyw6EgZW4gZWwgZG9jdW1lbnRvIGZpbmFsIG5pIHNlIG1vc3RyYXLDoW4gbG9zIHJlc3VsdGFkb3MgZGUgbGEgZWplY3VjacOzbiBkZWwgY8OzZGlnby4gRXN0YSBvcGNpw7NuIGVzIG11eSDDunRpbCBwYXJhIGxvcyBjaHVua3MgcXVlIHNlIHV0aWxpemFuIHBhcmEgaGFjZXIgZWwgInNldHVwIi4gCgpHZW5lcmFsbWVudGUgbG9zIGRvY3VtZW50b3MgLlJtZCB0aWVuZW4gdW4gcHJpbWVyIGNodW5rIChjaHVuayBkZSBzZXR1cCkgZG9uZGUgc2UgZmlqYW4gb3BjaW9uZXMgZ2xvYmFsZXMgcGFyYSBsb3MgY2h1bmtzLCBmaWpyYSBvcGNpb25lcyBnbG9iYWxlcywgaW5jbHVzbyBzZSBwdWVkZSB1dGlsaXphciBwYXJhIGNhcmdhciBwYXF1ZXRlcyBiw6FzaWNvcyBjb21vIGVsIGB0aWR5dmVyc2VgLiBQb3IgZWplbXBsbyBlc3RlIHN1ZWxlIHNlciBlbCBwcmltZXIgY2h1bmsgZW4gbWlzIGRvY3VtZW50b3MgLlJNZDoKCgpgYGB7ciBlY2hvID0gRkFMU0UsIGNvbW1lbnQgPSAiIn0KY2F0KGh0bWx0b29sczo6aW5jbHVkZVRleHQoaGVyZTo6aGVyZSgiLi9pbWFnZW5lcy9ybWFya2Rvd25fZWplbXBsb3MvY2h1bmtzL2NodW5rXzYiKSkpCmBgYAoKCjxicj4KCkNvbiBsYSBsbGVnYWRhIGRlIFtga25pdHIgdjEuMzVgXShodHRwczovL2dpdGh1Yi5jb20veWlodWkva25pdHIvcmVsZWFzZXMpIGRpc3BvbmVtb3MgZGUgdW5hIG51ZXZhIGZvcm1hIGRlIGVzcGVjaWZpY2FyIGxhcyBvcGNpb25lcyBkZSBsb3MgY2h1bmtzLgoKYGBge3IsIGVjaG8gPSBGQUxTRSwgZmlnLmFzcCA9IDcvOX0KdHdlZXRybWQ6OnR3ZWV0X2VtYmVkKCJodHRwczovL3R3aXR0ZXIuY29tL2FuZHJld2hlaXNzL3N0YXR1cy8xNDQzNTcwOTcyNzAzOTM2NTIzIiwgdGhlbWUgPSAibGlnaHQiLCBhbGlnbiA9ICJjZW50ZXIiLCBkbnQgPSBUUlVFLCBtYXh3aWR0aCA9IDkwMCkKYGBgCgoKPGJyPgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKCiMjIEVsIHRleHRvIChlbiBSbWFya2Rvd24pCgo8YnI+CgpMYSBwYXJ0ZSBwcmluY2lwYWwgZGUgdW4gaW5mb3JtZSBzdWVsZSBzZXIgdGV4dG8gKG5hcnJhdGl2ZXMpLiBFbiB1biBmaWNoZXJvIC5SbWQsIHRvZG8gbG8gcXVlIG5vIHNlYSBlbmNhYmV6YW1pZW50byBvIGNodW5rcyBzZXLDoSBpbnRlcnByZXRhZG8gcG9yIGtuaXQgY29tbyB0ZXh0byB5IGxvIG1vc3RyYXLDoSB0YWwgY3VhbDsgZXMgZGVjaXIsIGNvbW8gdGV4dG8uCgpBcXXDrSBwb2Ryw61hbW9zIGFjYWJhciBudWVzdHJvIHR1dG9yw61hIHNvYnJlIGNvbW8gZXNjcmliaXIgdGV4dG8gZW4gdW4gZmljaGVybyAuUm1kOyBwZXJvIGdlbmVyYWxtZW50ZSBlbiB1biB0ZXh0byBxdWVyZW1vcyByZXNhbHRhciBjaWVydGFzIHBhbGFicmFzIGNvbiBuZWdyaXRhLCBvIHBvbmVybGFzIGVuIGN1cnNpdmEsIG8gcG9uZXIgdW4gdGl0dWxvIGRlIHNlY2Npw7NuIHkgZGUgc3ViLXNlY2Npb25lcy4gVG9kbyBlc3RvIGxvIHRlbmRyZW1vcyBxdWUgaGFjZXIgdXRpbGl6YW5kbyAqKk1hcmtkb3duKiouIE1hcmtkb3duIGVzIHVuIGxlbmd1YWplIGRlIG1hcmNhcyBsaWdlcm8geSBtdXkgc2VuY2lsbG8gZGUgYXByZW5kZXIgKGxvIGLDoXNpY28gc2UgYXByZW5kZSBlbiB1bm9zIDEwIG1pbnV0b3MpIHBlcm8gbXV5IHV0aWxpemFkby4gRXMgcG9zaWJsZSBxdWUgYWxndW5vIGRlIHZvc290cm9zIGhheWEgdXRpbGl6YWRvIHVuYSB2YXJpYW50ZSBkZSBNYXJrZG93biBhbCBlc2NyaWJpciBlbiBuZWdyaXRhIGVuIFdoYXRzYXBwLgoKCgpFbCB0ZXh0byBkZSB1biBkb2N1bWVudG8gLlJtZCBlcyAic2ltcGxlbWVudGUiIHRleHRvIFBFUk8gZXN0w6EgZXNjcml0byBlbiAqKk1hcmtkb3duKiouICBBaG9yYSBsbyB2ZXJlbW9zLCBwZXJvIGFudGVzIHZhbW9zIGEgdmVyIHVuIHBvY28gbcOhcyBzb2JyZSBxdcOpIGVzIE1hcmtkb3duLgoKPGJyPgoKIyMjIyDCv1F1w6kgZXMgTWFya2Rvd24/IFtPUENJT05BTF0KCk1hcmRvd24gZXMgdW4gbGVuZ3VhamUgZGUgbWFyY2FkbyBsaWdlcm8gaWRlYWRvIGVuIDIwMDQgcG9yIEpob24gR3J1ZWJlciB5IFtBYXJvbiBTd2FydHpdKGh0dHBzOi8vZXMud2lraXBlZGlhLm9yZy93aWtpL0Fhcm9uX1N3YXJ0eikuIEhheSBkaXZlcnNhcyB2YXJpYW50ZXMgZGUgTWFya2Rvd24sIFthcXXDrV0oaHR0cHM6Ly9ibG9nLmNvZGluZ2hvcnJvci5jb20vc3RhbmRhcmQtbWFya2Rvd24taXMtbm93LWNvbW1vbi1tYXJrZG93bi8pIHBldWVkZXMgbGVlciBzb2JyZSB1biBpbnRlbnRvIGRlIGVzdGFuZGFyaXphY2nDs24uCgoKUG9kZW1vcyBwZW5zYXIgcXVlIE1hcmtkb3duIGVzIHVuIG3DqXRvZG8gZGUgZXNjcml0dXJhXltNYXJrZG93biBlcyB1biBsZW5ndWFqZSBkZSBtYXJjYWRvIHF1ZSBwZXJtaXRlIGxhIGFwbGljYWNpw7NuIGRlIGZvcm1hdG8gYSB1biB0ZXh0byBlbXBsZWFuZG8gdW5hIHNlcmllIGRlIG1hcmNhcyBvIGNhcmFjdGVyZXMgZXNwZWNpYWxlc106IGV2aWRlbnRlbWVudGUgc2lydmUgcGFyYSBlc2NyaWJpci4gTGEgdmVudGFqYSBkZSBlc2NyaWJpciBlbiBNYXJrZG93biBlcyBxdWUgZXMgdW4gbGVuZ3VhamUgbXV5IGbDoWNpbCBkZSBhcHJlbmRlciB5IHF1ZSBjb21vIGVzdMOhIGJhc2FkbyBlbiB1biBmb3JtYXRvIGRlIHRleHRvIHBsYW5vLCBlcyB5IHNlcsOhIGNvbXBhdGlibGUgY29uIGxhIG1heW9yw61hIGRlIHBsYXRhZm9ybWFzLgoKTGEgbWF5b3LDrWEgZGUgdm9zb3Ryb3MgZXNjcmliw61zIGVuIFdvcmQuIEVzIG11eSBmw6FjaWwgZXNjcmliaXIgZW4gV29yZCBwZXJvIHVuIGFyY2hpdm8gd29yZCBzb2xvIGVzIHBvc2libGUgbGVlcmxvIGVuIGVsIHByb2dyYW1hIFdPUkQgZGUgTWljcm9zb2Z0LiBTaSBpbnRlbnRhcyBhYnJpciB1biBkb2N1bWVudG8gLmRvYyBlbiBOb3RlcGFkLCDDqXN0ZSBzZXLDoSBjb21wbGV0YW1lbnRlIGlsZWdpYmxlOyBzaW4gZW1iYXJnbywgbGEgbWF5b3LDrWEgZGUgcGxhdGFmb3JtYXMgeSBzZXJ2aWNpb3Mgd2ViIHNhYmVuIGludGVycHJldGFyIHkgbW9zdHJhciBjb3JyZWN0YW1lbnRlIHVuIGRvY3VtZW50byBlc2NyaXRvIGVuIE1hcmtkb3duLiAKCkxvIHF1ZSBlc2NyaWJhcyBlbiBSbWFya2Rvd24gc2UgbW9zdHJhcsOhIHRhbCBjdWFsIGVuIGVsIGRvY3VtZW50byBmaW5hbCwgcGVybyBsbyBtw6FzIGhhYml0dWFsIGVzIHF1ZSBxdWllcmFzIGRhciB1biBwb2NvIGRlIGZvcm1hdG8gZWwgdGV4dG86IG5lZ3JpdGFzLCBjdXJzaXZhcywgbGlzdGFzLCBlbmxhY2VzIGRlIGludGVybmV0LCBldGMuLi4KClRvZG9zIGVzdG9zIGZvcm1hdG9zIChuZWdyaXRhLCAuLi4pIHNlIGludHJvZHVjZW4gZW4gcm1hcmtkb3duIGNvbiBtYXJjYXM7IHBvciBlamVtcGxvIHNpIHF1aWVyZXMgcXVlIHVuYSBwYWxhYnJhIHNlIHJlc2FsdGUgZW4gbmVncml0YXMgdGllbmVzIHF1ZSBlc2NyaWJpcmxhIGVubWFyY2FkYSBlbiBgKipgOiAqKmAqKmVzdG8gc2UgbW9zdHJhcsOtYSBlbiBuZWdyaXRhKipgKioKCgoKUGFyYSBhcHJlbmRlciBsYXMgcHJpbmNpcGFsZXMgcmVnbGFzIGRlIFJtYXJrZG93biBwb2TDqWlzIHVzYXIgdW4gW2VkaXRvciBvbi1saW5lIGRlIE1hcmtkb3duXShodHRwczovL3Z1ZWpzLm9yZy92Mi9leGFtcGxlcy8pIHkgcHJvYmFyIGEgZXNjcmliaXIgYWxnby4KCgoKCkVuIGVzdGUgb3RybyBbdHV0b3JpYWwgIGRlIE1hcmtkb3duXShodHRwczovL3d3dy5tYXJrZG93bnR1dG9yaWFsLmNvbS8pLCBzZSBwdWVkZSBsZWVyIGxvIHNpZ3VpZW50ZToKCj4gTWFya2Rvd24gaXMgYSB3YXkgdG8gd3JpdGUgY29udGVudCBmb3IgdGhlIHdlYi4gSXTigJlzIHdyaXR0ZW4gaW4gd2hhdCBuZXJkcyBsaWtlIHRvIGNhbGwg4oCccGxhaW50ZXh04oCdLCB3aGljaCBpcyBleGFjdGx5IHRoZSBzb3J0IG9mIHRleHQgeW914oCZcmUgdXNlZCB0byB3cml0aW5nIGFuZCBzZWVpbmcuIFBsYWludGV4dCBpcyBqdXN0IHRoZSByZWd1bGFyIGFscGhhYmV0LCB3aXRoIGEgZmV3IGZhbWlsaWFyIHN5bWJvbHMuIFVubGlrZSBjdW1iZXJzb21lIHdvcmQgcHJvY2Vzc2luZyBhcHBsaWNhdGlvbnMsIHRleHQgd3JpdHRlbiBpbiBNYXJrZG93biBjYW4gYmUgZWFzaWx5IHNoYXJlZCBiZXR3ZWVuIGNvbXB1dGVycywgbW9iaWxlIHBob25lcywgYW5kIHBlb3BsZS4gSXTigJlzIHF1aWNrbHkgYmVjb21pbmcgdGhlIHdyaXRpbmcgc3RhbmRhcmQgZm9yIGFjYWRlbWljcywgc2NpZW50aXN0cywgd3JpdGVycywgYW5kIG1hbnkgbW9yZS4gV2Vic2l0ZXMgbGlrZSBHaXRIdWIgYW5kIHJlZGRpdCB1c2UgTWFya2Rvd24gdG8gc3R5bGUgdGhlaXIgY29tbWVudHMuCgoKIyMjIyMgQXF1w60gdGllbmVzIGFsZ3VuYXMgcmVnbGFzIGRlIE1hcmtkb3duCgpgYGB7ciBlY2hvID0gRkFMU0UsIGNvbW1lbnQgPSAiIiwgIG91dC53aWR0aCA9ICcxMTAlJywgZmlnLmFsaWduID0gJ2NlbnRlcid9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltYWdlbmVzIiwgInJtYXJrZG93bl9lamVtcGxvcyIsICJpbWFnZW5lcyIsICJpbWFnZW5fZWplbXBsb19tYXJrZG93bi5QTkciKSApCmBgYAoKCjxicj4KCiMgNS4gTcOhcyBjb3NhcyBkZSBSTWFya2Rvd24KCkVuIGxhIHDDoWdpbmEgd2ViIGRlIE1hcmtkb3duLCBjb25jcmV0YW1lbnRlIFthcXXDrV0oaHR0cHM6Ly9kYXJpbmdmaXJlYmFsbC5uZXQvcHJvamVjdHMvbWFya2Rvd24vc3ludGF4I2h0bWwpIG5vcyBhdmlzYW4gZGUgbG8gc2lndWllbnRlOgoKPiBNYXJrZG93biBpcyBub3QgYSByZXBsYWNlbWVudCBmb3IgSFRNTCwgb3IgZXZlbiBjbG9zZSB0byBpdC4gSXRzIHN5bnRheCBpcyB2ZXJ5IHNtYWxsLCBjb3JyZXNwb25kaW5nIG9ubHkgdG8gYSB2ZXJ5IHNtYWxsIHN1YnNldCBvZiBIVE1MIHRhZ3MgLi4uIFRoZSBpZGVhIGZvciBNYXJrZG93biBpcyB0byBtYWtlIGl0IGVhc3kgdG8gcmVhZCwgd3JpdGUsIGFuZCBlZGl0IHByb3NlLiBIVE1MIGlzIGEgcHVibGlzaGluZyBmb3JtYXQ7IE1hcmtkb3duIGlzIGEgd3JpdGluZyBmb3JtYXQuIE1hcmtkb3duIHdhcyBub3QgZGVzaWduZWQgdG8gc29sdmUgZXZlcnl0aGluZy4KCkEgcGVzYXIgZGUgcXVlLCBjb21vIG5vcyBhdmlzw7MgSmhvbiBHcnVlYmVyLCBlbCBkZXNhcnJvbGxhZG9yIGRlIE1hcmtkb3duLCBNYXJrZG93biBubyBlc3TDoSBkaXNlw7FhZG8gcGFyYSByZXNvbHZlciB0b2RhcyBsYXMgbmVjZXNpZGFkZXMgZGUgdW4gZXNjcml0b3IvY2llbnTDrWZpY28sIGVuIFJtYXJrZG93biBzZSBwdWVkZW4gaW50cm9kdWNpciB0YW1iacOpbiBlbGVtZW50b3MgY29tbzoKCiMjIyBFY3VhY2lvbmVzCgpTZSBwdWVkZW4gaW50cm9kdWNpciBmb3JtdWxhcyBtYXRlbcOhdGljYXMgZXNjcml0YXMgZW4gTMOhdGV4LiBQYXJhIGZvcm11bGFzIGVuIGxpbmVhIHNlIHVzYSBsYSBtYXJjYSBgJGAgeSBwYXJhIGZvcm11bGFzIGluZGVwZW5kaWVudGVzIHNlIHVzYSBgJCRgLgoKLSBQYXJhIGbDs3JtdWxhcyBlbiBsaW5lYSAobyBkZW50cm8gZGVsIHRleHRvKSBzZSB1dGlsaXphIGxhIG1hcmNhIGAkYCBhbCBwcmluY2lwaW8geSBmaW5hbCBkZSBsYSBmb3JtdWxhLiBQb3IgZWplbXBsbyBgJFxzdW1fe2k9MX1ebiBYX2kkYCBzZSBtaXN0YXLDrWEgYXPDrTogJFxzdW1fe2k9MX1ebiBYX2kkLiBWZXMsIGxhIGZvcm11bGEgZXN0w6EgZGVudHJvIGRlbCB0ZXh0bywgZW4gdW5hIGxpbmVhIGRlbCB0ZXh0by4KICAKIAotIFBhcmEgcHJlc2VudGFyIHVuYSBlY3VhY2nDs24gaW5kZXBlbmRpZW50ZSAoZW4gdW5hIGxpbmVhIGluZGVwZW5kaWVudGUpLCBzZSB1c2EgbGEgbWFyY2EgYCQkYCBhbCBwcmluY2lwaW8geSBmaW5hbCBkZSBsYSBmb3JtdWxhLiBTaSBlc2NyaWJlcyAgYCQkRSA9IG1jXnsyfSQkYCwgc2UgbW9zdHJhcsOhIGVuIHVuYSBsaW5lYSBpbmRlcGVuZGllbnRlIHRhbCBxdWUgYXPDrToKICAKICAKJCRFID0gbWNeezJ9JCQKCsK/UXVlIHBhc2EsIHF1ZSBubyBzYWJlcyBlc2NyaWJpciBmb3JtdWxhcyBvIGVjdWFjaW9uZXMgZW4gTGF0ZXg/IFlvIHRhbXBvY28gbXVjaG8sIHBlcm8gcHVlZGVzIHV0aWxpemFyIHVuIHByb2dyYW1hIGNvbW8gW0x5eF0oaHR0cHM6Ly93d3cubHl4Lm9yZy8pLCBvIG11Y2hvIG3DoXMgZsOhY2lsLCBwdWVkZXMgdXRpbGl6YXIgYWxnw7puIGVkaXRvciBvbmxpbmUgZGUgTGF0ZXgsIHBvciBlamVtcGxvOiBbZXN0ZV0oaHR0cDovL3d3dy5zY2l3ZWF2ZXJzLm9yZy9mcmVlLW9ubGluZS1sYXRleC1lcXVhdGlvbi1lZGl0b3IpIG8gW2VzdGVdKGh0dHBzOi8vd3d3LmxhdGV4NHRlY2huaWNzLmNvbS8pLiBFbiBbZXN0ZSBsaWJyb10oaHR0cHM6Ly9kZXJla3NvbmRlcmVnZ2VyLmdpdGh1Yi5pby81NzBMLzE2LXJtYXJrZG93bi10cmlja3MuaHRtbCkgdGllbmVuIGFsZ3Vub3MgZWplbXBsb3MgZGUgZWN1YWNpb25lcyBlbiBMYXRleC4KCgpSZWNpZW50ZW1lbnRlLCBlbCBwYXF1ZXRlIFtgZXF1YXRpb21hdGljYF0oaHR0cHM6Ly9naXRodWIuY29tL2RhdGFsb3JheC9lcXVhdGlvbWF0aWMpIHBlcm1pdGUgb2J0ZW5lciBmw6FjaWxtZW50ZSBsYSBlY3VhY2nDs24gZGUgdW4gbW9kZWxvOgoKCmBgYHtyLCByZXN1bHRzID0gImFzaXMifQojIHJlbW90ZXM6Omluc3RhbGxfZ2l0aHViKCJkYXRhbG9yYXgvZXF1YXRpb21hdGljIikKbGlicmFyeShlcXVhdGlvbWF0aWMpCgojIEZpdCBhIHNpbXBsZSBtb2RlbAptb2QxIDwtIGxtKG1wZyB+IGN5bCArIGRpc3AsIG10Y2FycykKCiMgR2l2ZSB0aGUgcmVzdWx0cyB0byBleHRyYWN0X2VxCmV4dHJhY3RfZXEobW9kMSkKCmBgYAoKClRhbWJpw6luIGRlIHVuIG1vZGVsbyBlc3RpbWFkbzoKCgpgYGB7ciwgcmVzdWx0cyA9ICJhc2lzIn0KZXh0cmFjdF9lcShtb2QxLCB1c2VfY29lZnMgPSBUUlVFKQpgYGAKCgoKCgo8YnI+CgojIyMgSW3DoWdlbmVzCgpQYXJhIG1vc3RyYXIgdW5hIGltYWdlbiBiYXN0YSBjb24gcG9uZXI6IAoKICBgYGAKICAhW1VuYSBpbWFnZW4gY2h1bGFdKC4vaW1hZ2VuZXMvaW1nMS5qcGVnKSAKICBgYGAKICAKQXVucXVlIHlvIHByZWZpZXJvIGhhY2VybG8gYXPDrToKICAKCiAgICBgYGB7ciBldmFsID0gVFJVRSwgZWNobyA9IFRSVUV9YHIgJydgCiAgICBrbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlOjpoZXJlKCJpbWFnZW5lcyIsICJybWFya2Rvd25fZWplbXBsb3MiLCAiaW1hZ2VuZXMiLCAiZnVja2luZ19hZ2VzX2ltYWdlLmpwZWciKSAgKQogICAgYGBgCgoKYGBge3IgZWNobyA9IEZBTFNFLCBvdXQud2lkdGggPSAiNTUlIiwgZXZhbCA9IFRSVUUsIGZpZy5jYXAgPSAnVW5hIGltYWfDqW4gY2h1bGEnfQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlOjpoZXJlKCJpbWFnZW5lcyIsICJybWFya2Rvd25fZWplbXBsb3MiLCAiaW1hZ2VuZXMiLCAiZnVja2luZ19hZ2VzX2ltYWdlLmpwZWciKSkKYGBgCgoKCgojIyMgSHlwZXJsaW5rcwoKClBhcmEgcG9uZXIgaHlwZXJsaW5rcyBzZSBwdWVkZSBlc2NyaWJpciBsbyBzaWd1aWVudGU6IGA8aHR0cDovL3d3dy53aWtpcGVkaWEuZXM+YCB5IHNlIG1vc3RyYXLDoSBhc8OtOiA8aHR0cDovL3d3dy53aWtpcGVkaWEuZXM+CgpQZXJvIGVzIG1lam9yIHBvbmVybG8gYXPDrTogYFtlbmxhY2UgYSBsYSBXaWtpcGVkaWFdKGh0dHA6Ly93d3cud2lraXBlZGlhLmVzKWAgeSBzZSBtb3N0cmFyw6EgYXPDrTogW2VubGFjZSBhIGxhIFdpa2lwZWRpYV0oaHR0cDovL3d3dy53aWtpcGVkaWEuZXMpe3RhcmdldD0iX2JsYW5rIn0uCgpTaSBxdWllcmVzIHF1ZSBlbCBlbmxhY2Ugc2UgYWJyYSBlbiBlbCBuYXZlZ2Fkb3IgZW4gdW5hIHDDoWdpbmEgbnVldmEgaGFzIGRlIGHDsWFkaXIgYHt0YXJnZXQ9Il9ibGFuayJ9YAoKCiMjIyBOb3RhcyBhbCBwaWUgZGUgcMOhZ2luYQoKUGFyYSBwb25lciBub3RhcyBhbCBwaWUgaGFzIGRlIHBvbmVyIGBbXjFdYCB5IGx1ZWdvIGFsIGZpbmFsIGRlbCBkb2N1bWVudG8gcG9uZXIgYFteMV06IGVzdG8gZXMgdW5hIG5vdGEgYWwgcGllLmAsIHkgc2UgdmVyw6EgZXN0bzogW14xXSAKClteMV06IGVzdG8gZXMgdW5hIG5vdGEgYWwgcGllLgoKTyBhbHRlcm5hdGl2YW1lbnRlIHBvbmVyLCBlbiBlbCBzaXRpbyBkZWwgdGV4dG8gZG9uZGUgcXVpZXJhcyBpbnNlcnRhciB1bmEgbm90YSBhbCBwaWUsIGVzdGEgbWFyY2E6IGBeW2VsIHRleHRvIHF1ZSBxdWllcm8gcXVlIHNlIGxlYSBlbiBsYSBub3RhIGFsIHBpZV1gIHkgc2UgbW9zdHJhcsOhIGNvbW8gdW5hIG5vdGEgYWwgcGllLgoKCiMjIyBUYWJsYXMKCkhhYnLDoSB1biB0dXRvcmlhbCBlc3BlY2lmaWNvIHBhcmEgdGFibGFzLCBwZXJvIHBhcmEgbW9zdHJhciB1bmEgdGFibGEsIGxvIG3DoXMgYsOhc2ljbyB5IHNlbmNpbGxvIGVzIHV0aWxpemFyIGxhIGZ1bmNpw7NuIGBrbml0cjo6a2FibGUoKWA6CgpgYGBgCmBgYHtyfWByICcnYCAgIAprbml0cjo6a2FibGUoc3VtbWFyeShpcmlzKSkgICAgCmBgYCAgICAKYGBgYAoKYGBge3IsIGVjaG8gPSBGQUxTRX0KIy0gSG93IHRvIHdyaXRlIHRoZSBjb2RlIHRvIHNob3cgaG93IHRvIHdyaXRlIHRoZSBjb2RlPyEgIkp1c3QgUXVpY2tseTogSG93IHRvIHNob3cgdmVyYmF0aW0gaW5saW5lIFIgY29kZSIgYnkgQG5qX3RpZXJuZXkgICBodHRwczovL2J1ZmYubHkvMzEyWE1uagojLSBAbmpfdGllcm5leTogVGhpcyBtb3JuaW5nIEB4aWV5aWh1aSBzaG93ZWQgbWUgdGhlIHdvbmRlcnMgb2YgaG93IHRvIHZlcmJhdGltIHdyaXRlIGFuIGlubGluZSBjb2RlIGNodW5rIHVzaW5nIGBrbml0cjo6aW5saW5lX2V4cHIoKWAsIHNvIEkgd3JvdGUgYSBzaG9ydCBibG9nIHBvc3QgYWJvdXQgaXQ6ICBodHRwczovL3d3dy5uanRpZXJuZXkuY29tL3Bvc3QvMjAxOS8wNy8xMC9qcS12ZXJiYXRpbS1pbmxpbmUtci8KCiMgdmVyYmF0aW0gY29kZTogQG5qX3RpZXJuZXk6IEFmdGVyIGEgbmljZSBzdWdnZXN0aW9uIGZyb20gQGdycnJjayByZSBAeGlleWlodWkgJ3Mgd2F5IHRvIGdlbmVyYXRlIHZlcmJhdGltIGNvZGUgY2h1bmtzIChodHRwczovL3lpaHVpLm5hbWUvZW4vMjAxNy8xMS9rbml0ci12ZXJiYXRpbS1jb2RlLWNodW5rLykgSSB0aG91Z2h0LCBob3cgY29vbCB3b3VsZCBpdCBiZSB0byB0eXBlIGB2ZXJiYXRpbSA9IFRSVUVgIGluc3RlYWQ/IElmIGZvbGtzIHdhbnQgdG8gY2hpbWUgaW4sIGhlcmUgaXMgYW4gcnN0dWRpbyBjb21tdW5pdHkgcG9zdDogaHR0cHM6Ly9jb21tdW5pdHkucnN0dWRpby5jb20vdC9xdWVzdGlvbi1mZWF0dXJlLXJlcXVlc3QtY29kZS1jaHVuay1vcHRpb24tdmVyYmF0aW0tdHJ1ZS8zMzUyMQoKCmtuaXRyOjprYWJsZShzdW1tYXJ5KGlyaXMpKSAgICAKYGBgCgoKPGJyPgoKIyA2LiAgQcO6biBtw6FzIGNvc2FzCgpIYXkgbcOhcyBjb3NhcywgY29tbyByZWZlcmVuY2lhcyBiaWJsaW9ncsOhZmljYXMsIHBlcm8geWEgc2Vyw6EgcGFyYSBlbCBzZWd1bmRvIGN1cnNvIGRlIFIgbyBsbyB0ZW5kcsOhcyBxdWUgYXByZW5kZXIgcG9yIHR1IGN1ZW50YSBlbiBbYFIgTWFya2Rvd246IFRoZSBEZWZpbml0aXZlIEd1aWRlYF0oaHR0cHM6Ly9ib29rZG93bi5vcmcveWlodWkvcm1hcmtkb3duLykuCgpBIHBlc2FyIGRlIHF1ZSBNYXJrZG93biBwZXJtaXRlIGZvcm1hdGVhciBlbCB0ZXh0bywgZW4gY2llcnRvcyBzZW50aWRvcyBlcyBsaW1pdGFkbzsgcGVybyBzaSBxdWllcmVzIGHDum4gbcOhcyBmbGV4aWJpbGlkYWQgZW4gZWwgb3V0cHV0LCB0ZW5kcsOhcyBxdWUgYXByZW5kZXIgaHRtbCB5IG1lam9yIGh0bWwrQ1NTLgoKPGJyPgoKKipBbGd1bm9zIGVqZW1wbG9zOioqCgoxLiBTaSBxdWllcmVzIGludHJvZHVjaXIgdW4gcMOhcnJhZm8gZW4gb3RybyBjb2xvciB0ZW5kcsOhcyBxdWUgaGFjZXJsbyBlbiBodG1sLCB0ZW5kcsOhcyBxdWUgZXNjcmliaXI6IGA8Rk9OVCBDT0xPUj0iUmVkIj5Fc3RvIHNlIG1vc3RyYXLDoSBlbiBST0pPISE8L0ZPTlQ+YAoKICAgIDxGT05UIENPTE9SPSJSZWQiPkVzdG8gc2UgbW9zdHJhcsOhIGVuIFJPSk8hITwvRk9OVD4KCjxicj4KCgoyLiBzaSBxdWllcmVzIGNlbnRyYXIgdW4gcMOhcnJhZm8sIHRlbmRyw6FzIHF1ZSBlc2NyaWJpcjogYDxDRU5URVI+RXN0ZSBww6FycmFmbyBpcsOhIGNlbnRyYWRvPC9DRU5URVI+YAoKPENFTlRFUj5Fc3RlIHDDoXJyYWZvIGlyw6EgY2VudHJhZG88L0NFTlRFUj4KCjxicj4KCgozLiBQdWVkZXMgaW5zZXJ0YXIgZW4gdHUgZG9jdW1lbnRvIChzw7NsbyBzaSBlbCBvdXRwdXQgZXMgaHRtbCkgdW5hIHDDoWdpbmEgd2ViIGNvbXBsZXRhLCBwYXJhIGVsbG8gaGFzIGRlIGVzY3JpYmlyOiBgPGlmcmFtZSBzcmM9Imh0dHA6Ly93d3cuZWxkaWFyaW8uZXMvIiBoZWlnaHQ9IjQwMCIgd2lkdGg9IjgwMCI+PC9pZnJhbWU+YAoKPGJyPgoKCjxpZnJhbWUgc3JjPSJodHRwOi8vd3d3LmVsZGlhcmlvLmVzLyIgaGVpZ2h0PSI0MDAiIHdpZHRoPSI4MDAiPgo8L2lmcmFtZT4KIAoKIDxicj4KIAogCiAKYGBge3IsIGV2YWwgPSBGQUxTRSwgZWNobyA9IEZBTFNFfQprbml0cjo6aW5jbHVkZV91cmwoImh0dHA6Ly93d3cuZWxkaWFyaW8uZXMvIikKYGBgCgo0LiBVbiB2w61kZW8uIEhhcyBkZSBlc2NyaWJpcjogYDxpZnJhbWUgd2lkdGg9IjU2MCIgaGVpZ2h0PSIzMTUiIHNyYz0iaHR0cHM6Ly93d3cueW91dHViZS5jb20vZW1iZWQvQUN2OXphQmExQTQiIGZyYW1lYm9yZGVyPSIwIiBhbGxvd2Z1bGxzY3JlZW4+PC9pZnJhbWU+YAoKCgoKCmBgYHtyLCBlY2hvID0gRkFMU0UsIGV2YWwgPSBUUlVFfQpsaWJyYXJ5KCJ2ZW1iZWRyIikKZW1iZWRfdXJsKCJodHRwczovL3d3dy55b3V0dWJlLmNvbS93YXRjaD92PUFDdjl6YUJhMUE0IikKYGBgCgoKQXVucXVlIHRhbWJpw6luIGhheSB1biBwYXF1ZXRlLCBhbCBtZW5vcywgcXVlIGZhY2lsaXRhIGxhIGluc2VyY2nDs24gZGUgdmlkZW9zIGVuIGRvY3VtZW50b3MgLlJtZDogZXMgZWwgcGFxdWV0ZSBbYHZlbWJlZHJgXShodHRwczovL2lqbHl0dGxlLmdpdGh1Yi5pby92ZW1iZWRyL2FydGljbGVzL3ZlbWJlZHIuaHRtbCkuCgo8YnI+CgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgo8YnI+CgoKIyMgQcO6biBtw6FzIGNvc2FzIChJSSkKCgotIEVsIHBhcXVldGUgIFtgZGVtb1JgXShodHRwczovL2dpdGh1Yi5jb20va2JvZHdpbi9kZW1vUikgYXl1ZGEgYSBwcmVzZW50YXIgZWwgY8OzZGlnbyBSIGVuIGRvY3VtZW50b3MgUm1kLiBQb3IgZWplbXBsbywgcGVybWl0ZSBtYXJjYXIvaGlnaGxpZ3QgYWxndW5hcyBwYXJ0ZXMgZGUgdW5hIHNlbnRlbmNpYSBvIGPDs2RpZ28gUi4gUGFyYSBhcHJlbmRlciBwdWVkZXMgaXIgYSBsYSBbdmnDsWV0YV0oaHR0cHM6Ly93ZWIuY2FscG9seS5lZHUvfmtib2R3aW4vZGVtb1IvYXJ0aWNsZXMvZGVtb1IuaHRtbCkgZGVsIHBhcXVldGUuIFBvciBlamVtcGxvLCBlbiBlbCBwcsOzeGltbyBjaHVuayB2b3kgYSBtYXJjYXIgZW4gYW1hcmlsbG8gZWwgb3BlcmFkb3IgcGlwZSBgICU+JWAKCgpgYGB7ciwgZWNobyA9IEZBTFNFLCByZXN1bHQgPSAiaGlkZSJ9CiNkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoImtib2R3aW4vZGVtb1IiKQpsaWJyYXJ5KGRlbW9SKQpkZW1vX2NvZGUoJwphYSA8LSBpcmlzICU+JSBncm91cF9ieShTcGVjaWVzKSAlPiUgc3VtbWFyaXplKG1lYW4oU2VwYWwuTGVuZ3RoKSkKJykgJT4lICBobHRfZml4ZWQoIiU+JSIpCmBgYAoKCmBgYHtyLCBlY2hvID0gRkFMU0V9CmxpYnJhcnkoaWNvbnMpICMtIHJlbW90ZXM6Omluc3RhbGxfZ2l0aHViKCJyb3BlbnNjaWxhYnMvaWNvbiIpCmBgYAoKLSBQb2RlbW9zIFt1c2FyIGljb25vc10oaHR0cHM6Ly9yb3BlbnNjaS5vcmcvdGVjaG5vdGVzLzIwMTgvMDUvMTUvaWNvbi8pIGVuIG51ZXN0cm9zIFJtZHMuIFBvciBlamVtcGxvOiBgciBpY29uczo6aWNvbl9zdHlsZShpY29uczo6Zm9udGF3ZXNvbWUoInJvY2tldCIpLCBmaWxsID0gIiMxRkE2N0EiKWAgKyAKYHIgaWNvbnM6Omljb25fc3R5bGUoaWNvbnM6OmZvbnRhd2Vzb21lKCJyLXByb2plY3QiKSwgZmlsbCA9ICIjMzg0Q0I3IilgID0gCmByIGljb25zOjppY29uX3N0eWxlKGljb25zOjpmb250YXdlc29tZSgiaGVhcnQiKSwgZmlsbCA9ICJyZWQiKWAKCgotIFBvZGVtb3MgdXNhciBjYWphcyBkZSBjb2xvcmVzIHBhcmEgcmVzYWx0YXIgdW4gdHJvem8gZGUgdGV4dG8gcG9yIGVqZW1wbG8gcGFyYSBwb25lciBjb25jbHVzaW9uZXMuIEVzdG8gbG8gYXByZW5kw60gW2FxdcOtXShodHRwczovL2hvbHR6eS5naXRodWIuaW8vUGltcC1teS1ybWQvI2ZpZ3VyZXNfY2FwdGlvbikuIEVuIGVzdGUgW290cm8gcG9zdF0oaHR0cHM6Ly9kZXNpcmVlLnJiaW5kLmlvL3Bvc3QvMjAxOS9tYWtpbmctdGlwLWJveGVzLXdpdGgtYm9va2Rvd24tYW5kLXJtYXJrZG93bi8pIG5vcyBlbnNlw7FhbiBhIGhhY2VyIGVzb3MgY3VhZHJvcyByZWFsbWVudGUgYm9uaXRvcy4KCjxzdHlsZT4KZGl2LmJsdWUge2JhY2tncm91bmQtY29sb3I6I2U2ZjBmZjsgYm9yZGVyLXJhZGl1czogNXB4OyBwYWRkaW5nOiAyMHB4O30KPC9zdHlsZT4KPGRpdiBjbGFzcyA9ICJibHVlIj4KKipDb25jbHVzaW9uZXM6KiogIAotIFRoaXMgaXMgbXkgZmlyc3QgY29uY2x1c2lvbgotIFRoaXMgaXMgbXkgc2Vjb25kIGNvbmNsdXNpb24KPC9kaXY+CgotIHNpIHF1ZXJlbW9zIHF1ZSBsb3MgY2h1bmtzIHB1ZWRhbiBjb3BpYXJzZSBlbiBlbCBwb3J0YXBhcGVsZXMsIHBvZGVtb3MgdXNhciBlbCBwYXF1ZXRlIFtga2xpcHB5YF0oaHR0cHM6Ly9ybGVzdXIuZ2l0aHViLmlvL2tsaXBweS9pbmRleC5odG1sKQoKYGBge3Iga2xpcHB5MSwgZWNobyA9IFRSVUUsIGV2YWwgPSBGQUxTRX0Ka2xpcHB5OjprbGlwcHkoKSAgIy0gcmVtb3Rlczo6aW5zdGFsbF9naXRodWIoInJsZXN1ci9rbGlwcHkiKQpgYGAKCgojIyBBw7puIG3DoXMgY29zYXMgKElJSSkKCi0gQ29uIENTUyBzZSBwdWVkZSBwZXJzb25hbGl6YXIgY29tcGxldGFtZW50ZSBlbCBhc3BlY3RvIGRlIGxvcyBkb2N1bWVudG9zIGh0bWwgcXVlIHNlIGdlbmVyYW4gY29uIFJtYXJrZG93bi4gQXVucXVlIG5vIHNlcGFzIENTUyB0aWVuZXMgdW5hIGdyYW4gdmFyaWVkYWQgZGUgZm9ybWF0b3MgcHJlZGVmaW5pZG9zLiBQdWVkZXMgdmVyIHVuIGxpc3RhZG8gW2FxdcOtXShodHRwczovL3d3dy5kYXRhZHJlYW1pbmcub3JnL3Bvc3Qvci1tYXJrZG93bi10aGVtZS1nYWxsZXJ5LyksICBbYXF1w61dKGh0dHBzOi8vZ2l0aHViLmNvbS9nYWRlbmJ1aWUvY2xlYW5ybWQpIG8gW2FxdcOtXShodHRwczovL2dpdGh1Yi5jb20vanViYS9ybWRmb3JtYXRzKS4gCgotIEluY29ycG9yYW5kbyBhbGd1bmFzIG9wY2lvbmVzIGFsIFlBTUwsIHNlIHB1ZWRlbiBjYW1iaWFyIGFsZ3Vub3MgYXNwZWN0b3MgZGUgdHUgZG9jdW1lbnRvIGh0bWwuIFB1ZWRlcyB2ZXJsbyBbYXF1w61dKGh0dHBzOi8vYm9va2Rvd24ub3JnL3lpaHVpL3JtYXJrZG93bi9odG1sLWRvY3VtZW50Lmh0bWwjYXBwZWFyYW5jZS1hbmQtc3R5bGUuKQoKLSBFbCBwYXF1ZXRlIFtic2xpYl0oaHR0cHM6Ly9yc3R1ZGlvLmdpdGh1Yi5pby9ic2xpYi8pIGZhY2lsaXRhIGVsIHR1bmVhZG8gZGUgbG9zIGh0bWwuCgotIEVsIHBhcXVldGUgW3RoZW1hdGljXShodHRwczovL3JzdHVkaW8uZ2l0aHViLmlvL3RoZW1hdGljLykgcHVlZGUgaGFjZXIgcXVlIGxvcyBncsOhZmljb3MgcmVmbGVqZW4gZWwgdGhlbWUgZGUgUlN0dWRpbyBxdWUgZXN0YXMgdXNhbmRvOiBzw7NsbyBoYXkgcXVlIGhhY2VyIGB0aGVtYXRpY19vbigpYC4KCi0gU2UgcHVlZGVuIGhhY2VyIHRhYnMgc2kgZGVzcHVlcyBkZSB1biB0w610dWxvIHBvbmVzIGB7LnRhYnNldCAudGFic2V0LWZhZGUgLnRhYnNldC1waWxsc31gCgoKLSBFbCBwYXF1ZXRlIFtjaGVja2Rvd25dKGh0dHBzOi8vYWdyaWNvbGFtei5naXRodWIuaW8vY2hlY2tkb3duLykgcGVybWl0ZSBjcmVhciBjYW1wb3MgeSBjYXNpbGxhcyBkZSB2ZXJpZmljYWNpw7NuLiBQb3IgZWplbXBsbzogCgpgYGAKwr9DdWFudG8gZXMgNyArIDI/CmBgYAoKCmBgYHtyLCByZXN1bHRzPSdhc2lzJywgZWNobyA9IEZBTFNFfQpjaGVja2Rvd246OmNoZWNrX3F1ZXN0aW9uKGFuc3dlciA9ICA5LCByaWdodCA9ICJBY2VydGFzdGUhISEiLCB3cm9uZyA9ICJubyB3YXkiKQpgYGAKCjxicj4KCi0gUHVlZGVzIGluY2x1aXIgcMOhZ2luYXMgd2ViIGNvbiBga25pdHI6OmluY2x1ZGVfdXJsKClgICwgc2hpbnkncyBjb24gYGtuaXRyOjppbmNsdWRlX2FwcCgpYCAgZSBpbcOhZ2VuZXMgY29uIGBrbml0cjo6aW5jbHVkZV9ncmFwaGljcygpYCAKCi0gU2UgcHVlZGVuIGluY2x1aXIgdsOtZGVvcyBjb24gYHZlbWJlZHI6OmVtYmVkX3VybCgpYAoKCi0gRWwgcGFxdWV0ZSBbZm9udGF3ZXNvbWVdKGh0dHBzOi8vZ2l0aHViLmNvbS9yc3R1ZGlvL2ZvbnRhd2Vzb21lKSBwZXJtaXRlIGluY2x1aXIgaWNvbm9zIGRlIFsqKkZvbnQgQXdlc29tZSoqXShodHRwczovL2ZvbnRhd2Vzb21lLmNvbS8pIGVuIGRvY3VtZW50b3MgUk1hcmtkb3duLiBQb3IgZWplbXBsbyBjb24gYGAgYCBmb250YXdlc29tZTo6ZmEoInItcHJvamVjdCIsIGZpbGwgPSAic3RlZWxibHVlIilgIGBgICAgcG9kZW1vcyBpbnNlcnRhciBlbCBpY29ubyBkZSBgciBmb250YXdlc29tZTo6ZmEoInItcHJvamVjdCIsIGZpbGwgPSAic3RlZWxibHVlIilgIAoKCgo8YnI+CgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgo8YnI+CgojIDcuIEludmVzdGlnYWNpw7NuIHJlcHJvZHVjaWJsZSB5IFJtZAoKCkhlbW9zIGhhYmxhZG8gbXV5IHBvY28gZGUgaW52ZXN0aWdhY2nDs24gcmVwcm9kdWNpYmxlIHBlcm8sIGVzIGV2aWRlbnRlIHF1ZSB1c2FyIGRvY3VtZW50b3MgYC5SbWRgIHkgdHJhYmFqYXIgY29uIGBScHJvamVjdHNgIGZhY2lsaXRhIGxhIGludmVzdGlnYWNpw7NuIHJlcHJvZHVjaWJsZS4gTm8gbGEgZ2FyYW50aXphIGRlbCB0b2RvLCBwYXJhIGVsbG8gaGFicsOtYSBxdWUgdmVyL3VzYXIgbcOhcyBoZXJyYW1pZW50YXMgKGRvY2tlciwgcGFja3JhdCwgZ2l0aHViLCAuLi4pLiBObyBsbyB2YW1vcyBhIGhhY2VyLCBwZXJvIGFsIG1lbm9zIHNlw7FhbGFyIGRvcyBwcsOhY3RpY2FzIHF1ZSBmYWNpbGl0YW4gcXVlIHR1cyBhbsOhbGlzaXMgc2UgYWNlcnF1ZW4gYSBzZXIgcmVwcm9kdWNpYmxlczoKCjxicj4KCiAgMS4gIEN1YW5kbyBlc3TDoXMgaGFjaWVuZG8gdW4gYW7DoWxpc2lzIHkgcXVpZXJlcyBjb21wYXJ0aXJsbyBoYXkgcXVlIGVzdGFyIHNlZ3VybyBkZSBsb3MgcGFja2FnZXMgcXVlIHNlIG5lY2VzaXRhbiBjYXJnYXIgcGFyYSByZXBsaWNhcmxvLCBwb3IgZXNvIGVzIGJ1ZW5vIGhhY2VyIGVsIGFuw6FsaXNpcyBkZXNkZSB1bmEgc2VzacOzbiBudWV2YS9mcmVzY2EgZGUgUiB5IGNhcmdhciBsb3MgcGFxdWV0ZXMgYWwgcHJpbmNpcGlvIGRlbCBzY3JpcHQuIFBhcmEgZWxsbywgcHVlZGUgc2VyIGRlIHV0aWxpZGFkIHNhYmVyIHF1ZSBwYXF1ZXRlcyB0aWVuZXMgY2FyZ2Fkb3MgZW4gdW4gbW9tZW50byBkYWRvLCB5IHB1ZWRlcyBzYWJlcmxvIGNvbjogYCgucGFja2FnZXMoKSlgLiAgCiAgCiAgMi4gQSBwZXNhciBkZSBxdWUgeW8gbm8gbG8gc3VlbG8gaGFjZXIgKGByIGVtbzo6amkoIi0xIilgKSwgZXMgcmVjb21lbmRhYmxlIGludHJvZHVjaXIgYWwgZmluYWwgZGUgbG9zIGZpY2hlcm9zIGAuUm1kYCBsYSBzaWd1aWVudGUgaW5zdHJ1Y2Npw7NuOiBgc2Vzc2lvbkluZm8oKWAuIERlIGVzdGEgZm9ybWEsIHByb3BvcmNpb25hcsOhcyBpbmZvcm1hY2nDs24gc29icmUgcXVlIG9yZGVuYWRvciwgc2lzdGVtYSBvcGVyYXRpdm8geSAgdmVyc2nDs24gZGUgUiB1dGlsaXphc3RlIGVuIHR1IGFuw6FsaXNpcywgYXPDrSBjb21vIGRlIGxhcyBvcGNpb25lcyBsb2NhbGVzIGRlIHR1IHNpc3RlbWEgKGlkaW9tYSwgZXRjIC4uLikgeSBkZSBsb3MgcGFxdWV0ZXMgcXVlIHRpZW5lcyBjYXJnYWRvcyBlbiBtZW1vcmlhLiBQb3IgZWplbXBsbywgYWhvcmEgbWlzbW8gbWkgc2Vzc2lvbkluZm8gZXM6IAoKPGJyPgoKYGBge3J9CnNlc3Npb25JbmZvKCkKYGBgCgpPdHJhIGZvcm1hIGRlIHBvbmVyIGxvcyBkZXRhbGxlcyBkZSBsYSBzZXNpw7NuIGVzIHV0aWxpemFyIGBzZXNzaW9uaW5mbzo6c2Vzc2lvbl9pbmZvKClgOyBhZGVtw6FzIGxvIHVzYW1vcyBqdW50byBhIGBkZXRhaWxzOjpkZXRhaWxzKClgIHF1ZSBnZW5lcmEgdW4gZGVzcGxlZ2FibGUgcGFyYSB2ZXIgKG8gbm8pIGVsIG91dHB0KS4KCmBgYHtyfQpzZXNzaW9uaW5mbzo6c2Vzc2lvbl9pbmZvKCkgJT4lIGRldGFpbHM6OmRldGFpbHMoc3VtbWFyeSA9ICdjdXJyZW50IHNlc3Npb24gaW5mbycpIApgYGAKCk90cm8gZWplbXBsbyBkZSB1c28gZGUgYGRldGFpbHM6OmRldGFpbHMoKWA6CgpgYGB7cn0KZGV0YWlsczo6ZGV0YWlscyhwbG90KHNpbiwgeGxpbSA9IGMoMCwgMjApKSwgc3VtbWFyeSA9ICJNeSBjdXJ2eSBwbG90LiBUaGFua3MgTWNCYWluISEiKQpgYGAKCgoKPGJyPgoKU2kgcXVpZXJlcyB2ZXIgdG9kb3MgbG9zIHBhcXVldGVzIHF1ZSB0aWVuZXMgaW5zdGFsYWRvcyBlbiB0dSBvcmRlbmFkb3IsIGxvIHB1ZWRlcyBoYWNlciBhc8OtOgoKYGBge3IsIGVjaG8gPSBUUlVFLCBldmFsID0gRkFMU0V9CnBrZ3NfaW5zdGFsYWRvcyA8LSBpbnN0YWxsZWQucGFja2FnZXMoZmllbGRzID0gYygiUGFja2FnZSIsICJWZXJzaW9uIikpICU+JSAKICAgICAgICAgICAgICAgICAgICAgICAgYXMuZGF0YS5mcmFtZSgpCmBgYAoKCjxicj4KCjxicj4KCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCgoKIyBCaWJsaW9ncmFmw61hCgpbVHV0b3JpYWwgb2ZpY2lhbCBkZSBSbWFya2Rvd25dKGh0dHA6Ly9ybWFya2Rvd24ucnN0dWRpby5jb20vbGVzc29uLTEuaHRtbCkgTXV5IGJ1ZW5vIHkgbXV5IGNvbXBsZXRvLiBRdWl6w6FzIGxhIHByaW1lcmEgb3BjacOzbiBwYXJhIGFwcmVuZGVyLgoKW1R1dG9yaWFsIGNvbXBsZXRvXShodHRwOi8vd3d3LnNmcy51bmktdHVlYmluZ2VuLmRlL35qdmFucmlqL1R1dG9yaWFsL3R1dG9yaWFsTWFya2Rvd24uaHRtbCkuIFR1dG9yaWFsIHNlbmNpbGxvIHBlcm8gYmFzdGFudGUgY29tcGxldG8uIEJhc3RhbnRlIGRpZMOhY3RpY28uCgpbUGFnaW5hIG9maWNpYWwgZGUga25pdHJdKGh0dHBzOi8veWlodWkubmFtZS9rbml0ci8pLiBTb2xhbWVudGUgcGFyYSB2ZXJsYS4gVXNhcmxhIHPDs2xvIGN1YW5kbyB5YSAicGlsb3RlcyIuCgpbQ2hlYXQgU2hlZXQgZGUgUm1hcmtkb24gZW4gY2FzdGVsbGFub10oaHR0cHM6Ly93d3cucnN0dWRpby5jb20vd3AtY29udGVudC91cGxvYWRzLzIwMTUvMDMvcm1hcmtkb3duLXNwYW5pc2gucGRmKS4gUGFyZWNlIGNvbXBsaWNhZG8sIHBlcm8gY3VhbmRvIHRlIGFjb3N0dW1icmFzIGVzIHVuYSBmYW50w6FzdGljYSBjaHVsZXRhLgoKW1R1dG9yaWFsIGRlIFJtYXJrZG93biBlbiBlc3Bhw7FvbF0oaHR0cHM6Ly9mZXJuYW5kb3NhbnNlZ3VuZG8ud29yZHByZXNzLmNvbS8yMDE2LzA1LzA1L3Byb2dyYW1hY2lvbi1saXRlcmFyaWEtZW4tci1jb24ta25pdHIteS1tYXJrZG93bi8pCgpbVHV0b3JpYWwgZGUgTWFya2Rvd24gZW4gZXNwYcOxb2xdKGh0dHA6Ly9qb2VkaWNhc3Ryby5jb20vcGFnZXMvbWFya2Rvd24uaHRtbCkKCltDdXN0b21pemluZyAmIEV4dGVuZGluZyBSIE1hcmtkb3duXShodHRwczovL3NsaWRlcy55aWh1aS5uYW1lLzIwMTctcnN0dWRpby1jb25mLWV4dC1ybWQtWWlodWktWGllLmh0bWwjMSkuIFR1dG9yaWFsIGRlbCBkZXNhcnJvbGxhZG9yIGRlIGtuaXRyLiBDb3J0byBwZXJvIGF2YW56YWRvLgoKW1IgTWFya2Rvd246IFRoZSBEZWZpbml0aXZlIEd1aWRlXShodHRwczovL2Jvb2tkb3duLm9yZy95aWh1aS9ybWFya2Rvd24vKS4gUHVlcyBlc28sIGxhIGd1w61hIGRlZmluaXRpdmEuIEZhbnTDoXN0aWNhISEKCltSIE1hcmtkb3duIENvb2tib29rXShodHRwczovL2Jvb2tkb3duLm9yZy95aWh1aS9ybWFya2Rvd24tY29va2Jvb2svKS4gTnVldm8gbGlicm8sIDIwMjAsIGRlIFlpaHVpIFhpZSwgQ2hyaXN0b3BoZSBEZXJ2aWV1eCB5IEVtaWx5IFJpZWRlcmVyLgoKCltPZmZpY2V2ZXJzZV0oaHR0cHM6Ly9hcmRhdGEtZnIuZ2l0aHViLmlvL29mZmljZXZlcnNlL2luZGV4Lmh0bWwpLiBVbiBib29rZG93biBwYXJhIGZhY2lsaXRhciBsYSBnZW5lcmFjacOzbiBkZSBkb2N1bWVudG9zIHdvcmQgZGVzZGUgUm1hcmtkb3duLgoKCltSTWFya2Rvd24gZm9yIFNjaWVudGlzdHNdKGh0dHBzOi8vcm1kNHNjaS5uanRpZXJuZXkuY29tLykgZGUgTmljaG9sYXMgVGllcm5leS4KCg==