1. Introducción

La ciencia de datos es un campo interdisciplinario que involucra métodos científicos, procesos y sistemas para extraer conocimiento a través del entendimiento de datos en sus distintas formas.

A través de este cuaderno aprenderás a usar el lenguage de programación R, y en específico, las herramientos del tidyverse, para atender distintas tareas de la ciencia de datos.

Así, la mayor parte del material del cuaderno está inspirado en la vasta documentación (libros, papers, repositorios, etc.) elaborada por Hadley Wickham, principal gestor del tidyverse, y por ende, en cada capítulo se incluye un hipervínculo al material original:

En específico, el objetivo de este material es dar una visión general sobre las herramientas del tidyverse disponibles para un proyecto de ciencia de datos. A continuación podrás ver un flujo común en este tipo de proyectos:

Veámoslo por pasos:

  • Primero, es necesario importar los datos. Esto lo haremos con las primeras herramientas del tidyverse: readr y readxl. Si la data fuese a ser tomada desde otras fuentes, como bases de datos es posible usar librerías como RODBC y DBI.
  • Una vez cargada la data, tenemos que colocarla en formato tidy. Es decir, transformarla de tal manera que cada fila sea una observación y que cada columna sea una variable. El hecho de tener los datos en este formato es útil para luchar contra las preguntas que tengamos en mente, y no contra el hecho de tener que poner los datos en un formato adecuado para la ejecución de alguna función. Esta tarea la podemos lograr con librerías como tidyr y dplyr.
  • Cuando los datos hayan sido colocados en formato tidy, el siguiente paso es transformarlos. Esta transformación incluye el filtrado de los datos, crear nuevas variales en función de variables existentes y calcular estadística descriptiva. A todo este proceso se le suele llamar data wrangling. Esto se logra con el uso de la misma librería dplyr, apoyada en purrr.
  • Una vez que tenemos los datos procesados pasamos a las etapas de visualización y modelamiento, las cuales nos permitirán generar conocimiento.

    • La visualización es una actividad fundamental del ser humano. Una buena visualización te puede mostrar cosas que no esperabas o a su vez, despertar nuevas preguntas. Además nos ayudará a darnos cuenta si es que la pregunta que estamos haciendo es la correcta, o si necesitamos replantear algo. Finalmente, si bien las visualizaciones son útiles, no son escalables debido a que requieren que un humano las interprete. Esto se consigue a través de la librería ggplot2.

    • Los modelos entonces van un paso más allá, al permitirnos responder las preguntas que hayamos planteados. En específico, estos son herramientas matemáticas/computacionales que generalmente escalan bien. Esto se logra a través de la librería tidymodels.

  • El último paso del proceso es la comunicación. No importa qué tan bien estén hechos nuestros modelos y visualizaciones si no nos es posible comunicar los resultados. Esto se logra a través de las librerías rmarkdown y knitr.

Cabe notar que, si bien con ayuda de todas estas librerías podemos hacer una gran variedad de cosas, cubrimos con ellas el 80% de tareas de ciencia de datos. En alguno casos nos tendremos que ayudar de herramientas adicionales como sparklyr (para trabajar con Apache Spark) o data.table (para trabajar rápidamente con conjuntos de datos más grandes).

2. Creación de proyectos y Pipes

2.1. Creación de proyectos

El día a día del análisis de datos, si bien requiere que usemos un lenguaje de programación como R, también requiere que usemos otro tipo de herramientas y que estemos presentes en otro tipo de escenarios. Es por ello, que luego de presentar un análisis posiblemente regresemos a R sin recordar lo que estábamos haciendo, dónde estábamos trabajando, e incluso, cuántos proyectos teníamos. Para superar estas barreras es importante realizar dos decisiones:

  1. ¿Qué es real sobre nuestro análisis? Es decir, ¿qué es lo que consideramos como el último registro de lo que trabajamos?
  • Cuando comenzamos a trabajar con R, es normal que consideremos nuestro ambiente de trabajo (los objetos que se encuentran en la ventana environment de RStudio) como ‘’real’‘, sin embargo, en el largo plazo, estaremos mejor si consideramos nuestros scripts como’‘reales’’. Es decir, nos valdremos únicamente de estos últimos para realizar nuestro trabajo, ya que ellos serán los únicos capaces de replicar exactamente el mismo espacio de trabajo cada vez que utilicemos R. Con el fin de educarnos a nosotros mismos, es conveniente desactivar las opciones para preservar nuestros espacios de trabajo entre sesiones, dentro de Tools -> Global Options en RStudio, como se muestra en la siguiente imagen. Hacer esto es importante para no depender de cálculos que sin darnos cuenta, guardamos únicamente en el ambiente de trabajo.
  1. ¿Dónde ‘’vive’’ nuestro análisis?
  • En R existe el concepto de directorio de trabajo, el cual es el directorio de nuestra máquina donde R busca los archivos que pedimos cargar, y guarda los archivos que pedimos guardar. Este se muestra sobre la consola como se presenta en la siguiente imagen. Dentro de la consola podemos preguntar por este espacio de trabajo a través de la instrucción getwd(). Adicionalmente, es posible (aunque no recomendable) establecer otra dirección como nuestro directorio de trabajo a través de la instrucción setwd('/direccion/de/mi/proyecto'). Cabe mencionar además como nota que en R los directorios se escriben con “/” y no con “\” (como se hace en Windows), además, en R existen directorios absolutos. Los directorios absolutos apuntan a un lugar específico en la máquina, sin importar nuestro directorio de trabajo (empiezan con “C:”, “\\server” o “/”, dependiendo de nuestro sistema operativo).

Para facilitarnos la vida, RStudio cuenta con proyectos. Este complemento de RStudio nos permite tener todos los archivos asociados con un análisis en el mismo lugar. Para crearlo, basta con navegar a File->New Project y seleccionar las opciones que se muestran en la siguiente imagen (o las que convengan):

Cuando utilizamos proyectos debemos ubicarlos sobre carpetas que podamos encontrar, de lo contrario todo nuestro esfuerzo habrá sido en vano. Finalmente, cuando estemos trabajando sobre nuestro proyecto, R buscará los archivos que le indiquemos sobre este directorio, y de igual manera, guardará todos los resultados en ese mismo directorio.

2.2. Pipes

Un elemento fundamental del tidyverse son los pipes. Estos son símbolos que nos permitirán anidar operaciones, evitando que el código se haga inentendible. Su único requisito de uso es tener instalada la librería magrittr (que se instala por defecto al instalar el tidyverse).

Veamos un ejemplo de uso. Para ello primero cargaremos la librería tidyverse, que nos será de ayuda en todo el ejercicio.

library(tidyverse)

Con la librería cargada, utilizaremos el dataset diamonds incluido en ggplot2.

diamonds <- ggplot2::diamonds

Sobre dicho dataset realizaremos un filtro y un slice (subconjunto) de datos, sin preocuparnos por ahora de los detalles. Para ello utilizaremos un primer método, creando un objeto por cada operación.

diamonds2 <- filter(diamonds, cut=="Ideal", color=="E")
diamonds3 <- slice(diamonds, 1:1000)
diamonds3

Al hacerlo hemos creado un objeto extra llamado diamonds2, del que quizás no necesitemos la información. Para evitarnos esta situación podemos crear un el objeto diamonds3 utilizando funciones anidadas.

diamonds3 <- slice(
  filter(
    diamonds, cut=="Ideal", color=="E"
  ),
  1:1000
)
diamonds3

Este código, a pesar de que evita que creemos el paso extra de diamonds2, puede volverse un poco ilegible. Para evitar esto utilizaremos el operador pipe, tipeado como %>%.

diamonds3 <- diamonds %>% 
  filter(cut=="Ideal", color=="E") %>% 
  slice(1:1000)
diamonds3

Como vemos, el resultado es el mismo. El operador pipe entonces lo que hace es ejecutar la función de la derecha, utilizando como primer argumento el objeto de la izquierda. El hacer operaciones de esta manera es intuitivo, nos permite una fácil lectura sobre el código y evita anidar funciones. Por detrás, la función pipe utiliza el primer método que revisamos.

Dado que el %>% (¡ahora seguro lo lees sin problema!) es una herramienta poderosa, hay que usarla con sabiduría. Por ejemplo, es recomendable no usar estos pipes si hay partes intermedias de las transformaciones que nos pueden ser de utilidad (generalmente sucede cuando existen más de 10 pasos), cuando tenemos múltiples inputs y outputs en cada paso, o cuando la secuencia de pasos no es lineal. En el resto de casos, explotemos esta herramienta sin miedo alguno.

Finalmente, si bien el operador %>% es súmamente útil, existen otros operadores útiles como %<>% y %$% (aunque no sean tan comúnmente usados). El símbolo %<>% es un pipe de asignación que sobreescribe la transformación de la derecha en el objeto de la izquierda; mientras que %$% llama a las variables del dataframe de la izquierda para ejecutar funciones de la derecha que solo reciban vectores.

3. Importar y exportar datos

Esta sección es sencilla pero esencial. En ella aprenderemos a cargar archivos rectangulares en R como tibbles o dataframes (para nuestros propósitos, se usarán ambos términos de manera intercambiable, pero siempre estaremos refiriéndonos a tibbles). Para ello utilizaremos la librería readr (precargada con el tidyverse). Para ello, carguemos el tidyverse.

library(tidyverse)

La mayoría de las funciones de readr buscan importar archivos planos como data frames. Algunas de ellas se detalla a continuación.

  • read_csv permite leer archivos separados por comas (,).
  • read_csv2 permite leer archivos separados por punto y coma (;).
  • read_tsv permite leer archivos separados por tabuladores.
  • read_delim permite leer archivos separados por cualquier delimitador que se le especifique.
  • read_fwf permite leer archivos con columnas que tienen un ancho específico.
  • read_table permite leer archivos con columnas que tienen un ancho específico o que están separadas por un espacio en blanco.

Todas estas funciones tienen una sintaxis similar, así q si conocemos una, las conocemos a todas. Veamos un ejemplo con un archivo csv leído desde una dirección web (GitHub de Daniel Morales).

heights <- read_csv("https://gist.githubusercontent.com/danielmoralesp/650bf482113d05e75f283607637ea11b/raw/71b4ebd2472767662ffbd23fa35ec7ad6f5f92a6/heights.csv")
heights %>% head()

Como se puede notar es bastante sencillo, el primer argumento siempre corresponde a la dirección del archivo (sea que este provenga de la web o de nuestra máquina). Además en este primer argumento también podemos colocar una cadena de texto adecuada.

read_csv("a,b,c
1,2,3
4,5,6")

En ambos casos, la función read_csv lee la primera fila del archivo como nombres de columnas (este comportamiento es modificable a través del argumento col_names).

read_csv("1,2,3\n4,5,6", col_names = FALSE)

Si queremos nombres específicos para las columnas lo haremos con el mismo argumento.

read_csv("1,2,3\n4,5,6", col_names = c("x", "y", "z"))

Además, por defecto se nos imprime en consola la especificación del tipo de columnas que ha inferido en base a los primeros registros del archivo (comportamiento modificable a través del argument show_col_types).

read_csv("https://gist.githubusercontent.com/danielmoralesp/650bf482113d05e75f283607637ea11b/raw/71b4ebd2472767662ffbd23fa35ec7ad6f5f92a6/heights.csv", show_col_types = F)

Si queremos por ejemplo, saltarnos un determinado número de líneas del archivo lo podemos hacer a través del argumento skip.

read_csv("Primer línea
  Segunda línea
  x,y,z
  1,2,3", skip = 2)

O si el archivo tiene comentarios precedidos por algún caracter, las podemos ignorar con el argumento comment.

read_csv("# Mi comentario
  x,y,z
  1,2,3", comment = "#")

Finalmente, si tenemos caracteres particulares para la lectura de datos perdidos, usaremos el argumento na.

read_csv("a,b,c
         1,2,.", na = ".")

Al conocer el funcionamiento de estos argumentos tenemos ya visión sobre el 80% de archivos que usaremos en la vida real. El restante corresponde a argumentos muy específicos de otras funciones, que pueden ser consultados con la función help.

Adicional al importar datos, debemos conocer cómo exportar datos. Esto lo podemos hacer a través de las funciones write_csv, write_tsv y en general, write_delim. Su funcionamiento es muy similar al de las anteriores funciones, salvo que necesita primero el objeto a guardar.

write_delim(heights, "heights.txt", delim = ",")

Es preciso mencionar que al guardar archivos debemos tener mucho cuidado con el delimitador que usemos, ya que esto podría dañar nuestros datos (e.g. cuando hay una columna de nombres con comas y colocamos como separador la coma). Para evitar este problema, lo más usual es usar la función write_rds. Esta guarda nuestro archivo en un formato binario propio de R conocido como RDS. Para cargar archivos de este tipo se utiliza la función read_rds.

write_rds(heights, "heights.rds")
heights <- read_rds("heights.rds")
heights %>% head()

Una alternativa a esta es el uso de la librería feather, la cual guarda un archivo binario que pueden leer varios lenguajes de programación.

library(feather)
write_feather(heights, "heights.feather")
heights <- read_feather("heights.feather")
heights %>% head()

Esta última opción suele ser más rápida que RDS pero no soporta un tipo de dato especial que son las columnas-lista (las cuales son útiles al estimar modelos y realizar procedimientos más complejos sobre nuestros datos).

Finalmente, si deseamos leer datos desde otros formatos existen las siguientes opciones:

  • haven para leer archivos de SPSS, Stata y SAS.
  • readxl para leer archivos excel.
  • DBI junto a backends específicos de bases de datos (RMySQL, RSQLite, RPostgreSQL, etc), la cual permite correr consultas SQL contra una base de datos, y devuelve un data frame.
  • jsonlite para archivos json.
  • xml2 para archivos xml (revisa el tutorial de purrr).
  • rio para otro tipo de datos (revisa su github).

4. Datos en formato tidy

4.1. Introducción

En esta sección aprenderemos a utilizar la librería tidyr (incluida en el tidyverse) para organizar nuestros conjuntos de datos (que vienen en todas las formas y colores) en una estructura tidy (organizada). Si bien esta tarea puede ser la más pesada, y la que toma más tiempo de dedicación, al final dará sus frutos, facilitando la visualización y el modelado de datos. Si quieres conocer acerca del background teórico de este formato, no olvides leer ‘Tidy Data’.

Para que un dataset sea tidy se deben cumplir 3 reglas:

  1. Cada variable debe ser una columna.
  2. Cada observación debe ser una fila.
  3. Cada celda debe contener un valor.

Visualmente, estas reglas se ven como se muestra a continuación.

Las dos principales ventajas de tener los datos en este formato son:

  1. Dado que existe uniformidad en la forma en la que los datos se encuentran almacenados, es más fácil trabajar con ellos al unirlos, cruzarlos, visualizarlos, modelarlos, etc.
  2. Permite que la naturaleza vectorizada de R brille. Al aplicar distintas funciones podremos llamar más fácilmente a una variable para que sea analizada.

Además de estas ventajas, todas las herramientas del tidyverse están creadas para funcionar con datos en formato tidy.

4.2. Pivote de datos

En la vida real, y pese a que las ventajas de los datos tidy son obvias, por lo general los datos no son almacenados de esta manera, sino que busca facilitar algún uso en particular. Por ello, en algunos análisis, necesitaremos lograr que se cumplan los principios de data tidy, y el primer paso siempre es descubrir qué son variables, y qué son observaciones. Algunas veces esto es fácil y otras, es necesario preguntarle a quien generó los datos porque se pueden dar dos problemas:

  • Una variable puede estar dividida en varias columnas.
  • Una observación puede estar dividida en varias filas.

Si se sufre de este problema, las principales funciones que usaremos son pivot_longer y pivot_wider.

Pivote de columnas a filas (Pivot Longer)

Cuando los nombres de una columna no son variables, sino valores de una variable tendremos que usar este método. Veamos un ejemplo.

library(tidyverse)
billboard %>% head()

En este dataset los nombres de las columnas representan semanas y no variables. Para convertirlas en filas usaremos la siguiente instrucción, la cual depende principalmente de los argumentos cols (que indica las columnas que pivotaremos), names_to (que indica la variable a la que moveremos los nombres de columna) y values_to (que indica la variable a la que moveremos los valores de la columna).

billboard %>%
 pivot_longer(
   cols = starts_with("wk"),
   names_to = "week",
   values_to = "rank"
 )

Al haber ejecutado la instrucción anterior ya tenemos una variable por columna, y una observación en cada fila. Es decir, hemos creado un dataset más largo, porque tiene más filas (de allí el nombre pivot_longer). A continuación podremos ver un gráfico que nos recordará la ejecución de esta operación.

Pivote de filas a columnas (Pivot Wider)

Esta operación es la opuesta a pivot_longer porque hace los datos más anchos, es decir, incrementa el número de columnas. En este caso, los valores de una fila son variables y no valores de una variable. Veamos un ejemplo.

us_rent_income %>% head()

En este caso, en la columna variable se guardan en realidad dos variables. Para pasar los datos a columnas, usaremos la siguiente función. Esta depende de dos argumentos: names_from (el cual indica la columna con los nombres variable) y values_from (el cual indica la/las columnas con los valores de la variables).

us_rent_income %>%
  pivot_wider(names_from = variable, values_from = c(estimate, moe))

Para no olvidar este comportamiento, nos referiremos a la siguiente imagen.

4.3. Separación y unión

¿Qué sucede si nuestro problema con los datos es que en una variable se han almacenado en realidad dos columnas? En ese caso usaremos la función separate. ¿Y si hay una variable dividida en varias (como un identificador)? En ese caso usaremos la función unite.

Veamos en acción primero a la función separate. Supongamos que tenemos el siguiente dataset.

df <- tibble(x = c("x.y", "x.z", "y.z"))
df

Separemos la primera columna en dos columnas distintas. Para ello especificaremos la variable a separar (col), los nombres de las columnas nuevas (into) y el separador (sep).

df %>% separate(col = x, into = c("A", "B"), sep = "\\.")

¿Ven que es bastante sencillo? Refirámonos a la siguiente imagen cuando no lo recordemos.

Para realizar unión, refirámonos al siguiente ejemplo.

df <- expand_grid(x = c("a", "1"), y = c("b", "2"))
df

Unamos de este dataset las dos primeras columnas. Para ello usaremos la función unite, que depende de los argumentos col (que indica el nombre de la columna resultado) y ... que acepta todas las columnas a incluir en la unión (por defecto esta se hace con el símbolo _, si deseas cambiarlo, usa el argumento sep).

df %>% unite(col = "z", x, y, sep = "-")

Al igual que antes, para no olvidar el funcionamiento de esta función, veremos la siguiente imagen.

4.4. Valores perdidos

Por lo general, los valores perdidos pueden estar expresados de dos maneras: explícita (NA) o implícita (simplemente no aparecen).

Veamos un ejemplo:

stocks <- tibble(
  year   = c(2015, 2015, 2015, 2015, 2016, 2016, 2016),
  qtr    = c(   1,    2,    3,    4,    2,    3,    4),
  return = c(1.88, 0.59, 0.35,   NA, 0.92, 0.17, 2.66)
)
stocks

En este dataset existen dos datos perdidos, el último trimestre del 2015 (que tiene un NA) y el primer trimestre de 2016 (que no aparece en los datos). Así, si quisieramos convertir el dato implícito en explícito, usaremos la función complete, a la cual basta únicamente con especificarle las columnas con todas las posibles combinaciones de datos.

stocks %>% complete(year, qtr)

En este caso, ambos datos perdidos se representan con NA.

Si en cambio, ya tenemos los datos perdidos explícitos, pero queremos llenarlos como el último dato disponible (si los datos vienen de una tabla dinámica en excel por ejemplo), usaremos la función fill, como se muestra a continuación.

treatment <- tibble(person = c("Derrick Whitmore", NA, NA, "Katherine Burke"),
                    treatment = c(1,2,3,1), response = c(7,10,9,4))
treatment
treatment %>% 
  fill(person)

A este procedimiento se le suele llamar llevar la última observación hacia adelante.

4.5. Datos non-tidy

Los datos que no se encuentran en formato tidy no necesariamente será negligidos. En algunos casos es conveniente tener datos en otro formato dado que:

  • Algunas representaciones alternativas pueden tener mejor desempeño o ventajas de espacio.
  • Áreas especializadas del conocimiento pueden sus propias formas de guardar y procesar los datos.

Si quieres saber más al respecto, visita el post de Jeff Leek.

6. Transformación de datos

7. Análisis exploratorio de datos

8. Visualización de datos

LS0tDQp0aXRsZTogIkludHJvZHVjY2nDs24gYSBsYXMgaGVycmFtaWVudGFzIGRlbCB0aWR5dmVyc2UiDQphdXRob3I6ICJIdWdvIFBvcnJhcyINCmRhdGU6ICIxOC8xMC8yMDIxIg0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOg0KICAgIGNzczogRXN0aWxvcy5jc3MNCiAgICB0b2M6IHllcw0KICAgIHRvY19kZXB0aDogMg0KICAgIHRvY19mbG9hdDoNCiAgICAgIGNvbGxhcHNlZDogeWVzDQogICAgICBzbW9vdGhfc2Nyb2xsOiBubw0KICBodG1sX2RvY3VtZW50Og0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiAnMicNCiAgICBkZl9wcmludDogcGFnZWQNCmNzbDogY2VwYWwueG1sDQotLS0NCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpDQpsaWJyYXJ5KGNvd3Bsb3QpDQpsaWJyYXJ5KGdncGxvdDIpDQpgYGANCg0KYGBge3IsIG91dC53aWR0aD0iNDAlIiwgZmlnLmFsaWduPSJjZW50ZXIiLCBlY2hvPUZ9DQpwMSA8LSBnZ2RyYXcoKSArIGRyYXdfaW1hZ2UoIkxPR08tQ0UucG5nIikNCnBsb3RfZ3JpZChwMSkNCmBgYA0KDQojIDEuIEludHJvZHVjY2nDs24NCg0KTGEgKipjaWVuY2lhIGRlIGRhdG9zKiogZXMgdW4gY2FtcG8gaW50ZXJkaXNjaXBsaW5hcmlvIHF1ZSBpbnZvbHVjcmEgbcOpdG9kb3MgY2llbnTDrWZpY29zLCBwcm9jZXNvcyB5IHNpc3RlbWFzIHBhcmEgZXh0cmFlciBjb25vY2ltaWVudG8gYSB0cmF2w6lzIGRlbCBlbnRlbmRpbWllbnRvIGRlIGRhdG9zIGVuIHN1cyBkaXN0aW50YXMgZm9ybWFzLg0KDQpBIHRyYXbDqXMgZGUgZXN0ZSBjdWFkZXJubyBhcHJlbmRlcsOhcyBhIHVzYXIgZWwgbGVuZ3VhZ2UgZGUgcHJvZ3JhbWFjacOzbiAqKlIqKiwgeSBlbiBlc3BlY8OtZmljbywgbGFzIGhlcnJhbWllbnRvcyBkZWwgYHRpZHl2ZXJzZWAsIHBhcmEgYXRlbmRlciBkaXN0aW50YXMgdGFyZWFzIGRlIGxhIGNpZW5jaWEgZGUgZGF0b3MuIA0KDQpBc8OtLCBsYSBtYXlvciBwYXJ0ZSBkZWwgbWF0ZXJpYWwgZGVsIGN1YWRlcm5vIGVzdMOhIGluc3BpcmFkbyBlbiBsYSB2YXN0YSBkb2N1bWVudGFjacOzbiAobGlicm9zLCBwYXBlcnMsIHJlcG9zaXRvcmlvcywgZXRjLikgZWxhYm9yYWRhIHBvciBbSGFkbGV5IFdpY2toYW1dKGh0dHA6Ly9oYWRsZXkubnovKSwgcHJpbmNpcGFsIGdlc3RvciBkZWwgYHRpZHl2ZXJzZWAsIHkgcG9yIGVuZGUsIGVuIGNhZGEgY2Fww610dWxvIHNlIGluY2x1eWUgdW4gaGlwZXJ2w61uY3VsbyBhbCBtYXRlcmlhbCBvcmlnaW5hbDoNCg0KICArIFtSIGZvciBEYXRhIFNjaWVuY2VdKGh0dHBzOi8vcjRkcy5oYWQuY28ubnovKQ0KICArIFtUaWR5IERhdGFdKGh0dHBzOi8vdml0YS5oYWQuY28ubnovcGFwZXJzL3RpZHktZGF0YS5wZGYpLg0KDQpFbiBlc3BlY8OtZmljbywgZWwgb2JqZXRpdm8gZGUgZXN0ZSBtYXRlcmlhbCBlcyBkYXIgdW5hIHZpc2nDs24gZ2VuZXJhbCBzb2JyZSBsYXMgaGVycmFtaWVudGFzIGRlbCBgdGlkeXZlcnNlYCBkaXNwb25pYmxlcyBwYXJhIHVuIHByb3llY3RvIGRlIGNpZW5jaWEgZGUgZGF0b3MuIEEgY29udGludWFjacOzbiBwb2Ryw6FzIHZlciB1biBmbHVqbyBjb23Dum4gZW4gZXN0ZSB0aXBvIGRlIHByb3llY3RvczoNCg0KYGBge3IsIG91dC53aWR0aD0iNjAlIiwgZmlnLmFsaWduPSJjZW50ZXIiLCBlY2hvPUZ9DQpwMSA8LSBnZ2RyYXcoKSArIGRyYXdfaW1hZ2UoImh0dHBzOi8vZDMzd3VicmZraTBsNjguY2xvdWRmcm9udC5uZXQvNTcxYjA1Njc1N2Q2OGU2ZGY4MWEzZTM4NTNmNTRkM2M3NmFkNmVmYy8zMmQzNy9kaWFncmFtcy9kYXRhLXNjaWVuY2UucG5nIikNCnBsb3RfZ3JpZChwMSkNCmBgYA0KDQpWZcOhbW9zbG8gcG9yIHBhc29zOg0KDQorIFByaW1lcm8sIGVzIG5lY2VzYXJpbyAqKmltcG9ydGFyKiogbG9zIGRhdG9zLiBFc3RvIGxvIGhhcmVtb3MgY29uIGxhcyBwcmltZXJhcyBoZXJyYW1pZW50YXMgZGVsIGB0aWR5dmVyc2VgOiBgcmVhZHJgIHkgYHJlYWR4bGAuIFNpIGxhIGRhdGEgZnVlc2UgYSBzZXIgdG9tYWRhIGRlc2RlIG90cmFzIGZ1ZW50ZXMsIGNvbW8gYmFzZXMgZGUgZGF0b3MgZXMgcG9zaWJsZSB1c2FyIGxpYnJlcsOtYXMgY29tbyBgUk9EQkNgIHkgYERCSWAuDQoNCmBgYHtyLCBvdXQud2lkdGg9IjQwJSIsIGZpZy5hbGlnbj0iY2VudGVyIiwgZWNobz1GfQ0KcDEgPC0gZ2dkcmF3KCkgKyBkcmF3X2ltYWdlKCJodHRwczovL3JlYWRyLnRpZHl2ZXJzZS5vcmcvbG9nby5wbmciKQ0KcDIgPC0gZ2dkcmF3KCkgKyBkcmF3X2ltYWdlKCJodHRwczovL3JlYWR4bC50aWR5dmVyc2Uub3JnL2xvZ28ucG5nIikNCnBsb3RfZ3JpZChwMSwgcDIpDQpgYGANCg0KKyBVbmEgdmV6IGNhcmdhZGEgbGEgZGF0YSwgdGVuZW1vcyBxdWUgY29sb2NhcmxhIGVuIGZvcm1hdG8gKip0aWR5KiouIEVzIGRlY2lyLCB0cmFuc2Zvcm1hcmxhIGRlIHRhbCBtYW5lcmEgcXVlIGNhZGEgKipmaWxhKiogc2VhIHVuYSAqKm9ic2VydmFjacOzbioqIHkgcXVlIGNhZGEgKipjb2x1bW5hKiogc2VhIHVuYSAqKnZhcmlhYmxlKiouIEVsIGhlY2hvIGRlIHRlbmVyIGxvcyBkYXRvcyBlbiBlc3RlIGZvcm1hdG8gZXMgw7p0aWwgcGFyYSBsdWNoYXIgY29udHJhIGxhcyBwcmVndW50YXMgcXVlIHRlbmdhbW9zIGVuIG1lbnRlLCB5IG5vIGNvbnRyYSBlbCBoZWNobyBkZSB0ZW5lciBxdWUgcG9uZXIgbG9zIGRhdG9zIGVuIHVuIGZvcm1hdG8gYWRlY3VhZG8gcGFyYSBsYSBlamVjdWNpw7NuIGRlIGFsZ3VuYSBmdW5jacOzbi4gRXN0YSB0YXJlYSBsYSBwb2RlbW9zIGxvZ3JhciBjb24gbGlicmVyw61hcyBjb21vIGB0aWR5cmAgeSBgZHBseXJgLg0KDQpgYGB7ciwgb3V0LndpZHRoPSI0MCUiLCBmaWcuYWxpZ249ImNlbnRlciIsIGVjaG89Rn0NCnAxIDwtIGdnZHJhdygpICsgZHJhd19pbWFnZSgiaHR0cHM6Ly90aWR5ci50aWR5dmVyc2Uub3JnL2xvZ28ucG5nIikNCnAyIDwtIGdnZHJhdygpICsgZHJhd19pbWFnZSgiaHR0cHM6Ly9kcGx5ci50aWR5dmVyc2Uub3JnL2xvZ28ucG5nIikNCnBsb3RfZ3JpZChwMSwgcDIpDQpgYGANCg0KKyBDdWFuZG8gbG9zIGRhdG9zIGhheWFuIHNpZG8gY29sb2NhZG9zIGVuIGZvcm1hdG8gKip0aWR5KiosIGVsIHNpZ3VpZW50ZSBwYXNvIGVzICoqdHJhbnNmb3JtYXJsb3MqKi4gRXN0YSB0cmFuc2Zvcm1hY2nDs24gaW5jbHV5ZSBlbCAqKmZpbHRyYWRvKiogZGUgbG9zIGRhdG9zLCAqKmNyZWFyKiogbnVldmFzIHZhcmlhbGVzIGVuIGZ1bmNpw7NuIGRlIHZhcmlhYmxlcyBleGlzdGVudGVzIHkgKipjYWxjdWxhcioqIGVzdGFkw61zdGljYSBkZXNjcmlwdGl2YS4gQSB0b2RvIGVzdGUgcHJvY2VzbyBzZSBsZSBzdWVsZSBsbGFtYXIgKipkYXRhIHdyYW5nbGluZyoqLiBFc3RvIHNlIGxvZ3JhIGNvbiBlbCB1c28gZGUgbGEgbWlzbWEgbGlicmVyw61hIGBkcGx5cmAsIGFwb3lhZGEgZW4gYHB1cnJyYC4NCg0KYGBge3IsIG91dC53aWR0aD0iNDAlIiwgZmlnLmFsaWduPSJjZW50ZXIiLCBlY2hvPUZ9DQpwMSA8LSBnZ2RyYXcoKSArIGRyYXdfaW1hZ2UoImh0dHBzOi8vcHVycnIudGlkeXZlcnNlLm9yZy9sb2dvLnBuZyIpDQpwMiA8LSBnZ2RyYXcoKSArIGRyYXdfaW1hZ2UoImh0dHBzOi8vZHBseXIudGlkeXZlcnNlLm9yZy9sb2dvLnBuZyIpDQpwbG90X2dyaWQocDEsIHAyKQ0KYGBgDQoNCisgVW5hIHZleiBxdWUgdGVuZW1vcyBsb3MgZGF0b3MgcHJvY2VzYWRvcyBwYXNhbW9zIGEgbGFzIGV0YXBhcyBkZSAqKnZpc3VhbGl6YWNpw7NuKiogeSAqKm1vZGVsYW1pZW50byoqLCBsYXMgY3VhbGVzIG5vcyBwZXJtaXRpcsOhbiAqKmdlbmVyYXIgY29ub2NpbWllbnRvKiouDQoNCiAgKyBMYSAqKnZpc3VhbGl6YWNpw7NuKiogZXMgdW5hIGFjdGl2aWRhZCBmdW5kYW1lbnRhbCBkZWwgc2VyIGh1bWFuby4gVW5hICoqYnVlbmEgdmlzdWFsaXphY2nDs24qKiB0ZSBwdWVkZSBtb3N0cmFyIGNvc2FzIHF1ZSBubyBlc3BlcmFiYXMgbyBhIHN1IHZleiwgZGVzcGVydGFyIG51ZXZhcyBwcmVndW50YXMuIEFkZW3DoXMgbm9zIGF5dWRhcsOhIGEgZGFybm9zIGN1ZW50YSBzaSBlcyBxdWUgbGEgcHJlZ3VudGEgcXVlIGVzdGFtb3MgaGFjaWVuZG8gZXMgbGEgY29ycmVjdGEsIG8gc2kgbmVjZXNpdGFtb3MgcmVwbGFudGVhciBhbGdvLiBGaW5hbG1lbnRlLCBzaSBiaWVuIGxhcyB2aXN1YWxpemFjaW9uZXMgc29uIMO6dGlsZXMsICoqbm8gc29uIGVzY2FsYWJsZXMqKiBkZWJpZG8gYSBxdWUgcmVxdWllcmVuIHF1ZSB1biBodW1hbm8gbGFzIGludGVycHJldGUuIEVzdG8gc2UgY29uc2lndWUgYSB0cmF2w6lzIGRlIGxhIGxpYnJlcsOtYSBgZ2dwbG90MmAuDQogIA0KICArIExvcyAqKm1vZGVsb3MqKiBlbnRvbmNlcyB2YW4gdW4gcGFzbyBtw6FzIGFsbMOhLCBhbCBwZXJtaXRpcm5vcyByZXNwb25kZXIgbGFzIHByZWd1bnRhcyBxdWUgaGF5YW1vcyBwbGFudGVhZG9zLiBFbiBlc3BlY8OtZmljbywgZXN0b3Mgc29uIGhlcnJhbWllbnRhcyBtYXRlbcOhdGljYXMvY29tcHV0YWNpb25hbGVzIHF1ZSBnZW5lcmFsbWVudGUgZXNjYWxhbiBiaWVuLiBFc3RvIHNlIGxvZ3JhIGEgdHJhdsOpcyBkZSBsYSBsaWJyZXLDrWEgYHRpZHltb2RlbHNgLg0KDQpgYGB7ciwgb3V0LndpZHRoPSI0MCUiLCBmaWcuYWxpZ249ImNlbnRlciIsIGVjaG89Rn0NCnAxIDwtIGdnZHJhdygpICsgZHJhd19pbWFnZSgiaHR0cHM6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvbG9nby5wbmciKQ0KcDIgPC0gZ2dkcmF3KCkgKyBkcmF3X2ltYWdlKCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vcnN0dWRpby9oZXgtc3RpY2tlcnMvbWFzdGVyL1NWRy90aWR5bW9kZWxzLnN2ZyIpDQpwbG90X2dyaWQocDEsIHAyKQ0KYGBgDQoNCiAgDQorIEVsIMO6bHRpbW8gcGFzbyBkZWwgcHJvY2VzbyBlcyBsYSAqKmNvbXVuaWNhY2nDs24qKi4gTm8gaW1wb3J0YSBxdcOpIHRhbiBiaWVuIGVzdMOpbiBoZWNob3MgbnVlc3Ryb3MgbW9kZWxvcyB5IHZpc3VhbGl6YWNpb25lcyBzaSBubyBub3MgZXMgcG9zaWJsZSBjb211bmljYXIgbG9zIHJlc3VsdGFkb3MuIEVzdG8gc2UgbG9ncmEgYSB0cmF2w6lzIGRlIGxhcyBsaWJyZXLDrWFzIGBybWFya2Rvd25gIHkgYGtuaXRyYC4NCg0KYGBge3IsIG91dC53aWR0aD0iNDAlIiwgZmlnLmFsaWduPSJjZW50ZXIiLCBlY2hvPUZ9DQpwMSA8LSBnZ2RyYXcoKSArIGRyYXdfaW1hZ2UoImh0dHBzOi8vc3RhdGljLWJjcmYuYmlvY2hlbS53aXNjLmVkdS9jb3Vyc2VzL1RhYnVsYXItZGF0YS1hbmFseXNpcy13aXRoLVItYW5kLVRpZHl2ZXJzZS9ib29rL2ltYWdlcy9pY29ucy9rbml0ci1ybWFya2Rvd24tbG9nb3MtNTAucG5nIikNCnBsb3RfZ3JpZChwMSkNCmBgYA0KDQpDYWJlIG5vdGFyIHF1ZSwgc2kgYmllbiBjb24gYXl1ZGEgZGUgdG9kYXMgZXN0YXMgbGlicmVyw61hcyBwb2RlbW9zIGhhY2VyIHVuYSBncmFuIHZhcmllZGFkIGRlIGNvc2FzLCBjdWJyaW1vcyBjb24gZWxsYXMgZWwgODAlIGRlIHRhcmVhcyBkZSBjaWVuY2lhIGRlIGRhdG9zLiBFbiBhbGd1bm8gY2Fzb3Mgbm9zIHRlbmRyZW1vcyBxdWUgYXl1ZGFyIGRlIGhlcnJhbWllbnRhcyBhZGljaW9uYWxlcyBjb21vIGBzcGFya2x5cmAgKHBhcmEgdHJhYmFqYXIgY29uIEFwYWNoZSBTcGFyaykgbyBgZGF0YS50YWJsZWAgKHBhcmEgdHJhYmFqYXIgcsOhcGlkYW1lbnRlIGNvbiBjb25qdW50b3MgZGUgZGF0b3MgbcOhcyBncmFuZGVzKS4NCg0KIyAyLiBDcmVhY2nDs24gZGUgcHJveWVjdG9zIHkgUGlwZXMNCg0KIyMgMi4xLiBbQ3JlYWNpw7NuIGRlIHByb3llY3Rvc10oaHR0cHM6Ly9yNGRzLmhhZC5jby5uei93b3JrZmxvdy1wcm9qZWN0cy5odG1sKQ0KDQpFbCBkw61hIGEgZMOtYSBkZWwgYW7DoWxpc2lzIGRlIGRhdG9zLCBzaSBiaWVuIHJlcXVpZXJlIHF1ZSB1c2Vtb3MgdW4gbGVuZ3VhamUgZGUgcHJvZ3JhbWFjacOzbiBjb21vIFIsIHRhbWJpw6luIHJlcXVpZXJlIHF1ZSB1c2Vtb3Mgb3RybyB0aXBvIGRlIGhlcnJhbWllbnRhcyB5IHF1ZSBlc3RlbW9zIHByZXNlbnRlcyBlbiBvdHJvIHRpcG8gZGUgZXNjZW5hcmlvcy4gRXMgcG9yIGVsbG8sIHF1ZSBsdWVnbyBkZSBwcmVzZW50YXIgdW4gYW7DoWxpc2lzIHBvc2libGVtZW50ZSByZWdyZXNlbW9zIGEgKipSKiogc2luIHJlY29yZGFyIGxvIHF1ZSBlc3TDoWJhbW9zIGhhY2llbmRvLCBkw7NuZGUgZXN0w6FiYW1vcyB0cmFiYWphbmRvLCBlIGluY2x1c28sIGN1w6FudG9zIHByb3llY3RvcyB0ZW7DrWFtb3MuIFBhcmEgc3VwZXJhciBlc3RhcyBiYXJyZXJhcyBlcyBpbXBvcnRhbnRlIHJlYWxpemFyICoqZG9zIGRlY2lzaW9uZXMqKjoNCg0KMS4gwr9RdcOpIGVzIHJlYWwgc29icmUgbnVlc3RybyBhbsOhbGlzaXM/IEVzIGRlY2lyLCDCv3F1w6kgZXMgbG8gcXVlIGNvbnNpZGVyYW1vcyBjb21vIGVsIMO6bHRpbW8gcmVnaXN0cm8gZGUgbG8gcXVlIHRyYWJhamFtb3M/DQoNCiAgKyBDdWFuZG8gY29tZW56YW1vcyBhIHRyYWJhamFyIGNvbiBSLCBlcyBub3JtYWwgcXVlIGNvbnNpZGVyZW1vcyBudWVzdHJvIGFtYmllbnRlIGRlIHRyYWJham8gKGxvcyBvYmpldG9zIHF1ZSBzZSBlbmN1ZW50cmFuIGVuIGxhIHZlbnRhbmEgKiplbnZpcm9ubWVudCoqIGRlIFJTdHVkaW8pIGNvbW8gJydyZWFsJycsIHNpbiBlbWJhcmdvLCBlbiBlbCBsYXJnbyBwbGF6bywgZXN0YXJlbW9zIG1lam9yIHNpIGNvbnNpZGVyYW1vcyBudWVzdHJvcyAqKnNjcmlwdHMqKiBjb21vICcncmVhbGVzJycuIEVzIGRlY2lyLCBub3MgdmFsZHJlbW9zIMO6bmljYW1lbnRlIGRlIGVzdG9zIMO6bHRpbW9zIHBhcmEgcmVhbGl6YXIgbnVlc3RybyB0cmFiYWpvLCB5YSBxdWUgZWxsb3Mgc2Vyw6FuIGxvcyDDum5pY29zIGNhcGFjZXMgZGUgcmVwbGljYXIgZXhhY3RhbWVudGUgZWwgKiptaXNtbyBlc3BhY2lvIGRlIHRyYWJham8qKiBjYWRhIHZleiBxdWUgdXRpbGljZW1vcyBSLiBDb24gZWwgZmluIGRlIGVkdWNhcm5vcyBhIG5vc290cm9zIG1pc21vcywgZXMgY29udmVuaWVudGUgZGVzYWN0aXZhciBsYXMgb3BjaW9uZXMgcGFyYSBwcmVzZXJ2YXIgbnVlc3Ryb3MgZXNwYWNpb3MgZGUgdHJhYmFqbyBlbnRyZSBzZXNpb25lcywgZGVudHJvIGRlICpUb29scyogLT4gKkdsb2JhbCBPcHRpb25zKiBlbiBSU3R1ZGlvLCBjb21vIHNlIG11ZXN0cmEgZW4gbGEgc2lndWllbnRlIGltYWdlbi4gSGFjZXIgZXN0byBlcyBpbXBvcnRhbnRlIHBhcmEgbm8gZGVwZW5kZXIgZGUgKipjw6FsY3Vsb3MgcXVlIHNpbiBkYXJub3MgY3VlbnRhLCBndWFyZGFtb3Mgw7puaWNhbWVudGUgZW4gZWwgYW1iaWVudGUgZGUgdHJhYmFqbyoqLg0KICANCmBgYHtyLCBvdXQud2lkdGg9IjQwJSIsIGZpZy5hbGlnbj0iY2VudGVyIiwgZWNobz1GfQ0KcDEgPC0gZ2dkcmF3KCkgKyBkcmF3X2ltYWdlKCJodHRwczovL2QzM3d1YnJma2kwbDY4LmNsb3VkZnJvbnQubmV0LzdmYTQ0YTU0NzFkNDAwMjUzNDQxNzZlZGU0MTY5YzVhZDMxNTk0ODIvMTU3N2Yvc2NyZWVuc2hvdHMvcnN0dWRpby13b3Jrc3BhY2UucG5nIikNCnBsb3RfZ3JpZChwMSkNCmBgYA0KDQoyLiDCv0TDs25kZSAnJ3ZpdmUnJyBudWVzdHJvIGFuw6FsaXNpcz8NCg0KICArIEVuIFIgZXhpc3RlIGVsIGNvbmNlcHRvIGRlICoqZGlyZWN0b3JpbyBkZSB0cmFiYWpvKiosIGVsIGN1YWwgZXMgZWwgZGlyZWN0b3JpbyBkZSBudWVzdHJhIG3DoXF1aW5hIGRvbmRlICoqUioqIGJ1c2NhIGxvcyBhcmNoaXZvcyBxdWUgcGVkaW1vcyBjYXJnYXIsIHkgZ3VhcmRhIGxvcyBhcmNoaXZvcyBxdWUgcGVkaW1vcyBndWFyZGFyLiBFc3RlIHNlIG11ZXN0cmEgc29icmUgbGEgKipjb25zb2xhKiogY29tbyBzZSBwcmVzZW50YSBlbiBsYSBzaWd1aWVudGUgaW1hZ2VuLiBEZW50cm8gZGUgbGEgY29uc29sYSBwb2RlbW9zIHByZWd1bnRhciBwb3IgZXN0ZSBlc3BhY2lvIGRlIHRyYWJham8gYSB0cmF2w6lzIGRlIGxhIGluc3RydWNjacOzbiAgYGdldHdkKClgLiBBZGljaW9uYWxtZW50ZSwgZXMgcG9zaWJsZSAoYXVucXVlICoqbm8gcmVjb21lbmRhYmxlKiopIGVzdGFibGVjZXIgb3RyYSBkaXJlY2Npw7NuIGNvbW8gbnVlc3RybyBkaXJlY3RvcmlvIGRlIHRyYWJham8gYSB0cmF2w6lzIGRlIGxhIGluc3RydWNjacOzbiBgc2V0d2QoJy9kaXJlY2Npb24vZGUvbWkvcHJveWVjdG8nKWAuIENhYmUgbWVuY2lvbmFyIGFkZW3DoXMgY29tbyBub3RhIHF1ZSBlbiAqUiogbG9zIGRpcmVjdG9yaW9zIHNlIGVzY3JpYmVuIGNvbiAiLyIgeSBubyBjb24gIlxcIiAoY29tbyBzZSBoYWNlIGVuIFdpbmRvd3MpLCBhZGVtw6FzLCBlbiAqKlIqKiBleGlzdGVuIGRpcmVjdG9yaW9zICoqYWJzb2x1dG9zKiouIExvcyBkaXJlY3RvcmlvcyAqKmFic29sdXRvcyoqIGFwdW50YW4gYSB1biBsdWdhciBlc3BlY8OtZmljbyBlbiBsYSBtw6FxdWluYSwgc2luIGltcG9ydGFyIG51ZXN0cm8gZGlyZWN0b3JpbyBkZSB0cmFiYWpvIChlbXBpZXphbiBjb24gIkM6IiwgIlxcXFxzZXJ2ZXIiIG8gIi8iLCBkZXBlbmRpZW5kbyBkZSBudWVzdHJvIHNpc3RlbWEgb3BlcmF0aXZvKS4gDQogIA0KYGBge3IsIG91dC53aWR0aD0iNDAlIiwgZmlnLmFsaWduPSJjZW50ZXIiLCBlY2hvPUZ9DQpwMSA8LSBnZ2RyYXcoKSArIGRyYXdfaW1hZ2UoImh0dHBzOi8vZDMzd3VicmZraTBsNjguY2xvdWRmcm9udC5uZXQvMTc2ZmMxMWIwYjQ4NDIwOWJkNzdmMTNhYjUxMTZiOGEwZDdhYTEzYS8yYjZmNy9zY3JlZW5zaG90cy9yc3R1ZGlvLXdkLnBuZyIpDQpwbG90X2dyaWQocDEpDQpgYGANCg0KUGFyYSBmYWNpbGl0YXJub3MgbGEgdmlkYSwgKipSU3R1ZGlvKiogY3VlbnRhIGNvbiAqKnByb3llY3RvcyoqLiBFc3RlIGNvbXBsZW1lbnRvIGRlICoqUlN0dWRpbyoqIG5vcyBwZXJtaXRlIHRlbmVyIHRvZG9zIGxvcyBhcmNoaXZvcyBhc29jaWFkb3MgY29uIHVuIGFuw6FsaXNpcyBlbiBlbCBtaXNtbyBsdWdhci4gUGFyYSBjcmVhcmxvLCBiYXN0YSBjb24gbmF2ZWdhciBhICpGaWxlKi0+Kk5ldyBQcm9qZWN0KiB5IHNlbGVjY2lvbmFyIGxhcyBvcGNpb25lcyBxdWUgc2UgbXVlc3RyYW4gZW4gbGEgc2lndWllbnRlIGltYWdlbiAobyBsYXMgcXVlIGNvbnZlbmdhbik6DQoNCmBgYHtyLCBvdXQud2lkdGg9IjQwJSIsIGZpZy5hbGlnbj0iY2VudGVyIiwgZWNobz1GfQ0KcDEgPC0gZ2dkcmF3KCkgKyBkcmF3X2ltYWdlKCJodHRwczovL2QzM3d1YnJma2kwbDY4LmNsb3VkZnJvbnQubmV0Lzg3NTYyYzQ4NTFiZjRkMGIwYzg0MTVkYzlkNjY5MDQ5MzU3MmUzNjIvYTY2ODUvc2NyZWVuc2hvdHMvcnN0dWRpby1wcm9qZWN0LTEucG5nIikNCnBsb3RfZ3JpZChwMSkNCmBgYA0KDQpgYGB7ciwgb3V0LndpZHRoPSI0MCUiLCBmaWcuYWxpZ249ImNlbnRlciIsIGVjaG89Rn0NCnAxIDwtIGdnZHJhdygpICsgZHJhd19pbWFnZSgiaHR0cHM6Ly9kMzN3dWJyZmtpMGw2OC5jbG91ZGZyb250Lm5ldC8wZmE3OTFlMjYyMWJlMjk3Y2I5YzVjYWMwYjI4MDIyMjNlM2Q3NzE0LzU3ZDg5L3NjcmVlbnNob3RzL3JzdHVkaW8tcHJvamVjdC0yLnBuZyIpDQpwbG90X2dyaWQocDEpDQpgYGANCg0KYGBge3IsIG91dC53aWR0aD0iNDAlIiwgZmlnLmFsaWduPSJjZW50ZXIiLCBlY2hvPUZ9DQpwMSA8LSBnZ2RyYXcoKSArIGRyYXdfaW1hZ2UoImh0dHBzOi8vZDMzd3VicmZraTBsNjguY2xvdWRmcm9udC5uZXQvZGVlNjMyNGRmMWY1YzUxMjFkMWMxZTllZWQ4MjJlZTUyYzg3MTY3Yi8xZjMyNS9zY3JlZW5zaG90cy9yc3R1ZGlvLXByb2plY3QtMy5wbmciKQ0KcGxvdF9ncmlkKHAxKQ0KYGBgDQoNCkN1YW5kbyB1dGlsaXphbW9zIHByb3llY3RvcyBkZWJlbW9zIHViaWNhcmxvcyBzb2JyZSBjYXJwZXRhcyBxdWUgcG9kYW1vcyBlbmNvbnRyYXIsIGRlIGxvIGNvbnRyYXJpbyB0b2RvIG51ZXN0cm8gZXNmdWVyem8gaGFicsOhIHNpZG8gZW4gdmFuby4gRmluYWxtZW50ZSwgY3VhbmRvIGVzdGVtb3MgdHJhYmFqYW5kbyBzb2JyZSBudWVzdHJvICoqcHJveWVjdG8qKiwgKipSKiogYnVzY2Fyw6EgbG9zIGFyY2hpdm9zIHF1ZSBsZSBpbmRpcXVlbW9zIHNvYnJlIGVzdGUgZGlyZWN0b3JpbywgeSBkZSBpZ3VhbCBtYW5lcmEsIGd1YXJkYXLDoSB0b2RvcyBsb3MgcmVzdWx0YWRvcyBlbiBlc2UgbWlzbW8gZGlyZWN0b3Jpby4NCg0KIyMgMi4yLiBbUGlwZXNdKGh0dHBzOi8vcjRkcy5oYWQuY28ubnovcGlwZXMuaHRtbCkNCg0KVW4gZWxlbWVudG8gKipmdW5kYW1lbnRhbCoqIGRlbCBgdGlkeXZlcnNlYCBzb24gbG9zICoqcGlwZXMqKi4gRXN0b3Mgc29uIHPDrW1ib2xvcyBxdWUgbm9zIHBlcm1pdGlyw6FuIGFuaWRhciBvcGVyYWNpb25lcywgZXZpdGFuZG8gcXVlIGVsIGPDs2RpZ28gc2UgaGFnYSBpbmVudGVuZGlibGUuIFN1IMO6bmljbyByZXF1aXNpdG8gZGUgdXNvIGVzIHRlbmVyIGluc3RhbGFkYSBsYSBsaWJyZXLDrWEgYG1hZ3JpdHRyYCAocXVlIHNlIGluc3RhbGEgcG9yIGRlZmVjdG8gYWwgaW5zdGFsYXIgZWwgYHRpZHl2ZXJzZWApLg0KDQpWZWFtb3MgdW4gZWplbXBsbyBkZSB1c28uIFBhcmEgZWxsbyBwcmltZXJvIGNhcmdhcmVtb3MgbGEgbGlicmVyw61hIGB0aWR5dmVyc2VgLCBxdWUgbm9zIHNlcsOhIGRlIGF5dWRhIGVuIHRvZG8gZWwgZWplcmNpY2lvLg0KDQpgYGB7cn0NCmxpYnJhcnkodGlkeXZlcnNlKQ0KYGBgDQoNCkNvbiBsYSBsaWJyZXLDrWEgY2FyZ2FkYSwgdXRpbGl6YXJlbW9zIGVsIGRhdGFzZXQgW2RpYW1vbmRzXShhICJEYXRhc2V0IHF1ZSBjb250aWVuZSBsb3MgcHJlY2lvcyB5IG90cm9zIGF0cmlidXRvcyBkZSBhcHJveGltYWRhbWVudGUgNTQuMDAwIGRpYW1hbnRlcy4iKSBpbmNsdWlkbyBlbiBgZ2dwbG90MmAuDQoNCmBgYHtyfQ0KZGlhbW9uZHMgPC0gZ2dwbG90Mjo6ZGlhbW9uZHMNCmBgYA0KDQpTb2JyZSBkaWNobyBkYXRhc2V0IHJlYWxpemFyZW1vcyB1biBmaWx0cm8geSB1biBzbGljZSAoc3ViY29uanVudG8pIGRlIGRhdG9zLCBzaW4gcHJlb2N1cGFybm9zIHBvciBhaG9yYSBkZSBsb3MgZGV0YWxsZXMuIFBhcmEgZWxsbyB1dGlsaXphcmVtb3MgdW4gcHJpbWVyIG3DqXRvZG8sIGNyZWFuZG8gdW4gb2JqZXRvIHBvciBjYWRhIG9wZXJhY2nDs24uDQoNCmBgYHtyfQ0KZGlhbW9uZHMyIDwtIGZpbHRlcihkaWFtb25kcywgY3V0PT0iSWRlYWwiLCBjb2xvcj09IkUiKQ0KZGlhbW9uZHMzIDwtIHNsaWNlKGRpYW1vbmRzLCAxOjEwMDApDQpkaWFtb25kczMNCmBgYA0KDQpBbCBoYWNlcmxvIGhlbW9zIGNyZWFkbyB1biBvYmpldG8gZXh0cmEgbGxhbWFkbyBgZGlhbW9uZHMyYCwgZGVsIHF1ZSBxdWl6w6FzIG5vIG5lY2VzaXRlbW9zIGxhIGluZm9ybWFjacOzbi4gUGFyYSBldml0YXJub3MgZXN0YSBzaXR1YWNpw7NuIHBvZGVtb3MgY3JlYXIgdW4gZWwgb2JqZXRvIGBkaWFtb25kczNgIHV0aWxpemFuZG8gZnVuY2lvbmVzIGFuaWRhZGFzLg0KDQpgYGB7cn0NCmRpYW1vbmRzMyA8LSBzbGljZSgNCiAgZmlsdGVyKA0KICAgIGRpYW1vbmRzLCBjdXQ9PSJJZGVhbCIsIGNvbG9yPT0iRSINCiAgKSwNCiAgMToxMDAwDQopDQpkaWFtb25kczMNCmBgYA0KDQpFc3RlIGPDs2RpZ28sIGEgcGVzYXIgZGUgcXVlIGV2aXRhIHF1ZSBjcmVlbW9zIGVsIHBhc28gZXh0cmEgZGUgYGRpYW1vbmRzMmAsIHB1ZWRlIHZvbHZlcnNlIHVuIHBvY28gaWxlZ2libGUuIFBhcmEgZXZpdGFyIGVzdG8gdXRpbGl6YXJlbW9zIGVsIG9wZXJhZG9yICoqcGlwZSoqLCB0aXBlYWRvIGNvbW8gYCU+JWAuDQoNCmBgYHtyfQ0KZGlhbW9uZHMzIDwtIGRpYW1vbmRzICU+JSANCiAgZmlsdGVyKGN1dD09IklkZWFsIiwgY29sb3I9PSJFIikgJT4lIA0KICBzbGljZSgxOjEwMDApDQpkaWFtb25kczMNCmBgYA0KDQpDb21vIHZlbW9zLCBlbCByZXN1bHRhZG8gZXMgZWwgbWlzbW8uIEVsIG9wZXJhZG9yICoqcGlwZSoqIGVudG9uY2VzIGxvIHF1ZSBoYWNlIGVzICoqZWplY3V0YXIgbGEgZnVuY2nDs24gZGUgbGEgZGVyZWNoYSwgdXRpbGl6YW5kbyBjb21vIHByaW1lciBhcmd1bWVudG8gZWwgb2JqZXRvIGRlIGxhIGl6cXVpZXJkYSoqLiBFbCBoYWNlciBvcGVyYWNpb25lcyBkZSBlc3RhIG1hbmVyYSBlcyBpbnR1aXRpdm8sIG5vcyBwZXJtaXRlIHVuYSBmw6FjaWwgbGVjdHVyYSBzb2JyZSBlbCBjw7NkaWdvIHkgZXZpdGEgYW5pZGFyIGZ1bmNpb25lcy4gUG9yIGRldHLDoXMsIGxhIGZ1bmNpw7NuIHBpcGUgdXRpbGl6YSBlbCBwcmltZXIgbcOpdG9kbyBxdWUgcmV2aXNhbW9zLg0KDQpEYWRvIHF1ZSBlbCBgJT4lYCAowqFhaG9yYSBzZWd1cm8gbG8gbGVlcyBzaW4gcHJvYmxlbWEhKSBlcyB1bmEgKipoZXJyYW1pZW50YSBwb2Rlcm9zYSoqLCBoYXkgcXVlIHVzYXJsYSBjb24gc2FiaWR1csOtYS4gUG9yIGVqZW1wbG8sIGVzIHJlY29tZW5kYWJsZSBubyB1c2FyIGVzdG9zIHBpcGVzIHNpIGhheSBwYXJ0ZXMgaW50ZXJtZWRpYXMgZGUgbGFzIHRyYW5zZm9ybWFjaW9uZXMgcXVlIG5vcyBwdWVkZW4gc2VyIGRlIHV0aWxpZGFkIChnZW5lcmFsbWVudGUgc3VjZWRlIGN1YW5kbyBleGlzdGVuIG3DoXMgZGUgMTAgcGFzb3MpLCBjdWFuZG8gdGVuZW1vcyBtw7psdGlwbGVzIGlucHV0cyB5IG91dHB1dHMgZW4gY2FkYSBwYXNvLCBvIGN1YW5kbyBsYSBzZWN1ZW5jaWEgZGUgcGFzb3Mgbm8gZXMgbGluZWFsLiBFbiBlbCByZXN0byBkZSBjYXNvcywgZXhwbG90ZW1vcyBlc3RhIGhlcnJhbWllbnRhIHNpbiBtaWVkbyBhbGd1bm8uDQoNCkZpbmFsbWVudGUsIHNpIGJpZW4gZWwgb3BlcmFkb3IgYCU+JWAgZXMgc8O6bWFtZW50ZSDDunRpbCwgZXhpc3RlbiBvdHJvcyBvcGVyYWRvcmVzIMO6dGlsZXMgY29tbyBgJTw+JWAgeSBgJSQlYCAoYXVucXVlIG5vIHNlYW4gdGFuIGNvbcO6bm1lbnRlIHVzYWRvcykuIEVsIHPDrW1ib2xvIGAlPD4lYCBlcyB1biBwaXBlIGRlIGFzaWduYWNpw7NuIHF1ZSBzb2JyZWVzY3JpYmUgbGEgdHJhbnNmb3JtYWNpw7NuIGRlIGxhIGRlcmVjaGEgZW4gZWwgb2JqZXRvIGRlIGxhIGl6cXVpZXJkYTsgbWllbnRyYXMgcXVlIGAlJCVgIGxsYW1hIGEgbGFzIHZhcmlhYmxlcyBkZWwgZGF0YWZyYW1lIGRlIGxhIGl6cXVpZXJkYSBwYXJhIGVqZWN1dGFyIGZ1bmNpb25lcyBkZSBsYSBkZXJlY2hhIHF1ZSBzb2xvIHJlY2liYW4gdmVjdG9yZXMuDQoNCiMgMy4gW0ltcG9ydGFyIHkgZXhwb3J0YXIgZGF0b3NdKGh0dHBzOi8vcjRkcy5oYWQuY28ubnovZGF0YS1pbXBvcnQuaHRtbCkNCg0KRXN0YSBzZWNjacOzbiBlcyBzZW5jaWxsYSBwZXJvIGVzZW5jaWFsLiBFbiBlbGxhIGFwcmVuZGVyZW1vcyBhIGNhcmdhciBhcmNoaXZvcyByZWN0YW5ndWxhcmVzIGVuICoqUioqIGNvbW8gW3RpYmJsZXNdKGEgIkxvcyB0aWJibGVzIHNvbiBkYXRhIGZyYW1lcyBjb24gbWVqb3JhcyBhIGNpZXJ0b3MgY29tcG9ydGFtaWVudG9zIGRlIGVzdG9zIMO6bHRpbW9zIGNvbiBsYSBmaW5hbGlkYWQgZGUgaGFjZXJub3MgbGEgdmlkYSBtw6FzIGbDoWNpbC4iKSBvIGRhdGFmcmFtZXMgKHBhcmEgbnVlc3Ryb3MgcHJvcMOzc2l0b3MsIHNlIHVzYXLDoW4gYW1ib3MgdMOpcm1pbm9zIGRlIG1hbmVyYSBpbnRlcmNhbWJpYWJsZSwgcGVybyBzaWVtcHJlIGVzdGFyZW1vcyByZWZpcmnDqW5kb25vcyBhIHRpYmJsZXMpLiBQYXJhIGVsbG8gdXRpbGl6YXJlbW9zIGxhIGxpYnJlcsOtYSBgcmVhZHJgIChwcmVjYXJnYWRhIGNvbiBlbCBgdGlkeXZlcnNlYCkuIFBhcmEgZWxsbywgY2FyZ3VlbW9zIGVsIGB0aWR5dmVyc2VgLg0KDQpgYGB7cn0NCmxpYnJhcnkodGlkeXZlcnNlKQ0KYGBgDQoNCkxhIG1heW9yw61hIGRlIGxhcyBmdW5jaW9uZXMgZGUgYHJlYWRyYCBidXNjYW4gaW1wb3J0YXIgYXJjaGl2b3MgcGxhbm9zIGNvbW8gZGF0YSBmcmFtZXMuIEFsZ3VuYXMgZGUgZWxsYXMgc2UgZGV0YWxsYSBhIGNvbnRpbnVhY2nDs24uDQoNCisgYHJlYWRfY3N2YCBwZXJtaXRlIGxlZXIgYXJjaGl2b3Mgc2VwYXJhZG9zIHBvciBjb21hcyAoLCkuDQorIGByZWFkX2NzdjJgIHBlcm1pdGUgbGVlciBhcmNoaXZvcyBzZXBhcmFkb3MgcG9yIHB1bnRvIHkgY29tYSAoOykuDQorIGByZWFkX3RzdmAgcGVybWl0ZSBsZWVyIGFyY2hpdm9zIHNlcGFyYWRvcyBwb3IgdGFidWxhZG9yZXMuDQorIGByZWFkX2RlbGltYCBwZXJtaXRlIGxlZXIgYXJjaGl2b3Mgc2VwYXJhZG9zIHBvciBjdWFscXVpZXIgZGVsaW1pdGFkb3IgcXVlIHNlIGxlIGVzcGVjaWZpcXVlLg0KKyBgcmVhZF9md2ZgIHBlcm1pdGUgbGVlciBhcmNoaXZvcyBjb24gY29sdW1uYXMgcXVlIHRpZW5lbiB1biBhbmNobyBlc3BlY8OtZmljby4NCisgYHJlYWRfdGFibGVgIHBlcm1pdGUgbGVlciBhcmNoaXZvcyBjb24gY29sdW1uYXMgcXVlIHRpZW5lbiB1biBhbmNobyBlc3BlY8OtZmljbyBvIHF1ZSBlc3TDoW4gc2VwYXJhZGFzIHBvciB1biBlc3BhY2lvIGVuIGJsYW5jby4NCg0KVG9kYXMgZXN0YXMgZnVuY2lvbmVzIHRpZW5lbiB1bmEgc2ludGF4aXMgc2ltaWxhciwgYXPDrSBxIHNpIGNvbm9jZW1vcyB1bmEsIGxhcyBjb25vY2Vtb3MgYSB0b2Rhcy4gVmVhbW9zIHVuIGVqZW1wbG8gY29uIHVuIGFyY2hpdm8gY3N2IGxlw61kbyBkZXNkZSB1bmEgZGlyZWNjacOzbiB3ZWIgKEdpdEh1YiBkZSBbRGFuaWVsIE1vcmFsZXNdKGh0dHBzOi8vZ2lzdC5naXRodWIuY29tL2RhbmllbG1vcmFsZXNwKSkuDQoNCmBgYHtyfQ0KaGVpZ2h0cyA8LSByZWFkX2NzdigiaHR0cHM6Ly9naXN0LmdpdGh1YnVzZXJjb250ZW50LmNvbS9kYW5pZWxtb3JhbGVzcC82NTBiZjQ4MjExM2QwNWU3NWYyODM2MDc2MzdlYTExYi9yYXcvNzFiNGViZDI0NzI3Njc2NjJmZmJkMjNmYTM1ZWM3YWQ2ZjVmOTJhNi9oZWlnaHRzLmNzdiIpDQpoZWlnaHRzICU+JSBoZWFkKCkNCmBgYA0KDQpDb21vIHNlIHB1ZWRlIG5vdGFyIGVzIGJhc3RhbnRlIHNlbmNpbGxvLCBlbCBwcmltZXIgYXJndW1lbnRvIHNpZW1wcmUgY29ycmVzcG9uZGUgYSBsYSBkaXJlY2Npw7NuIGRlbCBhcmNoaXZvIChzZWEgcXVlIGVzdGUgcHJvdmVuZ2EgZGUgbGEgd2ViIG8gZGUgbnVlc3RyYSBtw6FxdWluYSkuIEFkZW3DoXMgZW4gZXN0ZSBwcmltZXIgYXJndW1lbnRvIHRhbWJpw6luIHBvZGVtb3MgY29sb2NhciB1bmEgY2FkZW5hIGRlIHRleHRvIGFkZWN1YWRhLg0KDQpgYGB7cn0NCnJlYWRfY3N2KCJhLGIsYw0KMSwyLDMNCjQsNSw2IikNCmBgYA0KDQpFbiBhbWJvcyBjYXNvcywgbGEgZnVuY2nDs24gYHJlYWRfY3N2YCBsZWUgbGEgcHJpbWVyYSBmaWxhIGRlbCBhcmNoaXZvIGNvbW8gbm9tYnJlcyBkZSBjb2x1bW5hcyAoZXN0ZSBjb21wb3J0YW1pZW50byBlcyBtb2RpZmljYWJsZSBhIHRyYXbDqXMgZGVsIGFyZ3VtZW50byBgY29sX25hbWVzYCkuDQoNCmBgYHtyfQ0KcmVhZF9jc3YoIjEsMiwzXG40LDUsNiIsIGNvbF9uYW1lcyA9IEZBTFNFKQ0KYGBgDQoNClNpIHF1ZXJlbW9zIG5vbWJyZXMgZXNwZWPDrWZpY29zIHBhcmEgbGFzIGNvbHVtbmFzIGxvIGhhcmVtb3MgY29uIGVsIG1pc21vIGFyZ3VtZW50by4NCg0KYGBge3J9DQpyZWFkX2NzdigiMSwyLDNcbjQsNSw2IiwgY29sX25hbWVzID0gYygieCIsICJ5IiwgInoiKSkNCmBgYA0KDQoNCkFkZW3DoXMsIHBvciBkZWZlY3RvIHNlIG5vcyBpbXByaW1lIGVuIGNvbnNvbGEgbGEgZXNwZWNpZmljYWNpw7NuIGRlbCB0aXBvIGRlIGNvbHVtbmFzIHF1ZSBoYSBpbmZlcmlkbyBlbiBiYXNlIGEgbG9zIHByaW1lcm9zIHJlZ2lzdHJvcyBkZWwgYXJjaGl2byAoY29tcG9ydGFtaWVudG8gbW9kaWZpY2FibGUgYSB0cmF2w6lzIGRlbCBhcmd1bWVudCBgc2hvd19jb2xfdHlwZXNgKS4gDQoNCmBgYHtyfQ0KcmVhZF9jc3YoImh0dHBzOi8vZ2lzdC5naXRodWJ1c2VyY29udGVudC5jb20vZGFuaWVsbW9yYWxlc3AvNjUwYmY0ODIxMTNkMDVlNzVmMjgzNjA3NjM3ZWExMWIvcmF3LzcxYjRlYmQyNDcyNzY3NjYyZmZiZDIzZmEzNWVjN2FkNmY1ZjkyYTYvaGVpZ2h0cy5jc3YiLCBzaG93X2NvbF90eXBlcyA9IEYpDQpgYGANCg0KU2kgcXVlcmVtb3MgcG9yIGVqZW1wbG8sIHNhbHRhcm5vcyB1biBkZXRlcm1pbmFkbyBuw7ptZXJvIGRlIGzDrW5lYXMgZGVsIGFyY2hpdm8gbG8gcG9kZW1vcyBoYWNlciBhIHRyYXbDqXMgZGVsIGFyZ3VtZW50byBgc2tpcGAuDQoNCmBgYHtyfQ0KcmVhZF9jc3YoIlByaW1lciBsw61uZWENCiAgU2VndW5kYSBsw61uZWENCiAgeCx5LHoNCiAgMSwyLDMiLCBza2lwID0gMikNCmBgYA0KTyBzaSBlbCBhcmNoaXZvIHRpZW5lIGNvbWVudGFyaW9zIHByZWNlZGlkb3MgcG9yIGFsZ8O6biBjYXJhY3RlciwgbGFzIHBvZGVtb3MgaWdub3JhciBjb24gZWwgYXJndW1lbnRvIGBjb21tZW50YC4NCg0KYGBge3J9DQpyZWFkX2NzdigiIyBNaSBjb21lbnRhcmlvDQogIHgseSx6DQogIDEsMiwzIiwgY29tbWVudCA9ICIjIikNCmBgYA0KDQpGaW5hbG1lbnRlLCBzaSB0ZW5lbW9zIGNhcmFjdGVyZXMgcGFydGljdWxhcmVzIHBhcmEgbGEgbGVjdHVyYSBkZSBkYXRvcyBwZXJkaWRvcywgdXNhcmVtb3MgZWwgYXJndW1lbnRvIGBuYWAuDQoNCmBgYHtyfQ0KcmVhZF9jc3YoImEsYixjDQogICAgICAgICAxLDIsLiIsIG5hID0gIi4iKQ0KYGBgDQoNCkFsIGNvbm9jZXIgZWwgZnVuY2lvbmFtaWVudG8gZGUgZXN0b3MgYXJndW1lbnRvcyB0ZW5lbW9zIHlhIHZpc2nDs24gc29icmUgZWwgODAlIGRlIGFyY2hpdm9zIHF1ZSB1c2FyZW1vcyBlbiBsYSB2aWRhIHJlYWwuIEVsIHJlc3RhbnRlIGNvcnJlc3BvbmRlIGEgYXJndW1lbnRvcyBtdXkgZXNwZWPDrWZpY29zIGRlIG90cmFzIGZ1bmNpb25lcywgcXVlIHB1ZWRlbiBzZXIgY29uc3VsdGFkb3MgY29uIGxhIGZ1bmNpw7NuIGBoZWxwYC4NCg0KQWRpY2lvbmFsIGFsIGltcG9ydGFyIGRhdG9zLCBkZWJlbW9zIGNvbm9jZXIgY8OzbW8gZXhwb3J0YXIgZGF0b3MuIEVzdG8gbG8gcG9kZW1vcyBoYWNlciBhIHRyYXbDqXMgZGUgbGFzIGZ1bmNpb25lcyBgd3JpdGVfY3N2YCwgYHdyaXRlX3RzdmAgeSBlbiBnZW5lcmFsLCBgd3JpdGVfZGVsaW1gLiBTdSBmdW5jaW9uYW1pZW50byBlcyBtdXkgc2ltaWxhciBhbCBkZSBsYXMgYW50ZXJpb3JlcyBmdW5jaW9uZXMsIHNhbHZvIHF1ZSBuZWNlc2l0YSBwcmltZXJvIGVsIG9iamV0byBhIGd1YXJkYXIuDQoNCmBgYHtyfQ0Kd3JpdGVfZGVsaW0oaGVpZ2h0cywgImhlaWdodHMudHh0IiwgZGVsaW0gPSAiLCIpDQpgYGANCg0KRXMgcHJlY2lzbyBtZW5jaW9uYXIgcXVlIGFsIGd1YXJkYXIgYXJjaGl2b3MgZGViZW1vcyB0ZW5lciBtdWNobyBjdWlkYWRvIGNvbiBlbCBkZWxpbWl0YWRvciBxdWUgdXNlbW9zLCB5YSBxdWUgZXN0byBwb2Ryw61hIGRhw7FhciBudWVzdHJvcyBkYXRvcyAoZS5nLiBjdWFuZG8gaGF5IHVuYSBjb2x1bW5hIGRlIG5vbWJyZXMgY29uIGNvbWFzIHkgY29sb2NhbW9zIGNvbW8gc2VwYXJhZG9yIGxhIGNvbWEpLiBQYXJhIGV2aXRhciBlc3RlIHByb2JsZW1hLCBsbyBtw6FzIHVzdWFsIGVzIHVzYXIgbGEgZnVuY2nDs24gYHdyaXRlX3Jkc2AuIEVzdGEgZ3VhcmRhIG51ZXN0cm8gYXJjaGl2byBlbiB1biBmb3JtYXRvIGJpbmFyaW8gcHJvcGlvIGRlIFIgY29ub2NpZG8gY29tbyAqKlJEUyoqLiBQYXJhIGNhcmdhciBhcmNoaXZvcyBkZSBlc3RlIHRpcG8gc2UgdXRpbGl6YSBsYSBmdW5jacOzbiBgcmVhZF9yZHNgLg0KDQpgYGB7cn0NCndyaXRlX3JkcyhoZWlnaHRzLCAiaGVpZ2h0cy5yZHMiKQ0KaGVpZ2h0cyA8LSByZWFkX3JkcygiaGVpZ2h0cy5yZHMiKQ0KaGVpZ2h0cyAlPiUgaGVhZCgpDQpgYGANCg0KVW5hIGFsdGVybmF0aXZhIGEgZXN0YSBlcyBlbCB1c28gZGUgbGEgbGlicmVyw61hIGBmZWF0aGVyYCwgbGEgY3VhbCBndWFyZGEgdW4gYXJjaGl2byBiaW5hcmlvIHF1ZSBwdWVkZW4gbGVlciB2YXJpb3MgbGVuZ3VhamVzIGRlIHByb2dyYW1hY2nDs24uDQoNCmBgYHtyfQ0KbGlicmFyeShmZWF0aGVyKQ0Kd3JpdGVfZmVhdGhlcihoZWlnaHRzLCAiaGVpZ2h0cy5mZWF0aGVyIikNCmhlaWdodHMgPC0gcmVhZF9mZWF0aGVyKCJoZWlnaHRzLmZlYXRoZXIiKQ0KaGVpZ2h0cyAlPiUgaGVhZCgpDQpgYGANCg0KRXN0YSDDumx0aW1hIG9wY2nDs24gc3VlbGUgc2VyIG3DoXMgcsOhcGlkYSBxdWUgKipSRFMqKiBwZXJvIG5vIHNvcG9ydGEgdW4gdGlwbyBkZSBkYXRvIGVzcGVjaWFsIHF1ZSBzb24gbGFzIGNvbHVtbmFzLWxpc3RhIChsYXMgY3VhbGVzIHNvbiDDunRpbGVzIGFsIGVzdGltYXIgbW9kZWxvcyB5IHJlYWxpemFyIHByb2NlZGltaWVudG9zIG3DoXMgY29tcGxlam9zIHNvYnJlIG51ZXN0cm9zIGRhdG9zKS4NCg0KRmluYWxtZW50ZSwgc2kgZGVzZWFtb3MgbGVlciBkYXRvcyBkZXNkZSBvdHJvcyBmb3JtYXRvcyBleGlzdGVuIGxhcyBzaWd1aWVudGVzIG9wY2lvbmVzOg0KDQorIGBoYXZlbmAgcGFyYSBsZWVyIGFyY2hpdm9zIGRlIFNQU1MsIFN0YXRhIHkgU0FTLg0KKyBgcmVhZHhsYCBwYXJhIGxlZXIgYXJjaGl2b3MgZXhjZWwuDQorIGBEQklgIGp1bnRvIGEgYmFja2VuZHMgZXNwZWPDrWZpY29zIGRlIGJhc2VzIGRlIGRhdG9zIChgUk15U1FMYCwgYFJTUUxpdGVgLCBgUlBvc3RncmVTUUxgLCBldGMpLCBsYSBjdWFsIHBlcm1pdGUgY29ycmVyIGNvbnN1bHRhcyBTUUwgY29udHJhIHVuYSBiYXNlIGRlIGRhdG9zLCB5IGRldnVlbHZlIHVuIGRhdGEgZnJhbWUuDQorIGBqc29ubGl0ZWAgcGFyYSBhcmNoaXZvcyBqc29uLg0KKyBgeG1sMmAgcGFyYSBhcmNoaXZvcyB4bWwgKHJldmlzYSBlbCB0dXRvcmlhbCBkZSBbcHVycnJdKGh0dHBzOi8vamVubnliYy5naXRodWIuaW8vcHVycnItdHV0b3JpYWwvKSkuDQorIGByaW9gIHBhcmEgb3RybyB0aXBvIGRlIGRhdG9zIChyZXZpc2Egc3UgW2dpdGh1Yl0oaHR0cHM6Ly9naXRodWIuY29tL2xlZXBlci9yaW8pKS4NCg0KIyA0LiBbRGF0b3MgZW4gZm9ybWF0byB0aWR5XShodHRwczovL3I0ZHMuaGFkLmNvLm56L3RpZHktZGF0YS5odG1sKQ0KDQojIyA0LjEuIEludHJvZHVjY2nDs24NCg0KRW4gZXN0YSBzZWNjacOzbiBhcHJlbmRlcmVtb3MgYSB1dGlsaXphciBsYSBsaWJyZXLDrWEgYHRpZHlyYCAoaW5jbHVpZGEgZW4gZWwgYHRpZHl2ZXJzZWApIHBhcmEgb3JnYW5pemFyIG51ZXN0cm9zIGNvbmp1bnRvcyBkZSBkYXRvcyAocXVlIHZpZW5lbiBlbiB0b2RhcyBsYXMgZm9ybWFzIHkgY29sb3JlcykgZW4gdW5hIGVzdHJ1Y3R1cmEgKip0aWR5KiogKG9yZ2FuaXphZGEpLiBTaSBiaWVuIGVzdGEgdGFyZWEgcHVlZGUgc2VyIGxhIG3DoXMgcGVzYWRhLCB5IGxhIHF1ZSB0b21hIG3DoXMgdGllbXBvIGRlIGRlZGljYWNpw7NuLCBhbCBmaW5hbCBkYXLDoSBzdXMgZnJ1dG9zLCBmYWNpbGl0YW5kbyBsYSB2aXN1YWxpemFjacOzbiB5IGVsIG1vZGVsYWRvIGRlIGRhdG9zLiBTaSBxdWllcmVzIGNvbm9jZXIgYWNlcmNhIGRlbCBiYWNrZ3JvdW5kIHRlw7NyaWNvIGRlIGVzdGUgZm9ybWF0bywgbm8gb2x2aWRlcyBsZWVyIFsnVGlkeSBEYXRhJ10oaHR0cHM6Ly92aXRhLmhhZC5jby5uei9wYXBlcnMvdGlkeS1kYXRhLnBkZikuIA0KDQpQYXJhIHF1ZSB1biBkYXRhc2V0IHNlYSAqKnRpZHkqKiBzZSBkZWJlbiBjdW1wbGlyIDMgcmVnbGFzOg0KDQoxLiBDYWRhIHZhcmlhYmxlIGRlYmUgc2VyIHVuYSBjb2x1bW5hLg0KMi4gQ2FkYSBvYnNlcnZhY2nDs24gZGViZSBzZXIgdW5hIGZpbGEuDQozLiBDYWRhIGNlbGRhIGRlYmUgY29udGVuZXIgdW4gdmFsb3IuDQoNClZpc3VhbG1lbnRlLCBlc3RhcyByZWdsYXMgc2UgdmVuIGNvbW8gc2UgbXVlc3RyYSBhIGNvbnRpbnVhY2nDs24uDQoNCmBgYHtyLCBvdXQud2lkdGg9IjYwJSIsIGZpZy5hbGlnbj0iY2VudGVyIiwgZWNobz1GfQ0KcDEgPC0gZ2dkcmF3KCkgKyBkcmF3X2ltYWdlKCJodHRwczovL2QzM3d1YnJma2kwbDY4LmNsb3VkZnJvbnQubmV0LzZmMWRkYjU0NGZjNWM2OWEyNDc4ZTQ0NGFiODExMmZiMGVlYTIzZjgvOTFhZGMvaW1hZ2VzL3RpZHktMS5wbmciKQ0KcGxvdF9ncmlkKHAxKQ0KYGBgDQoNCkxhcyBkb3MgcHJpbmNpcGFsZXMgdmVudGFqYXMgZGUgdGVuZXIgbG9zIGRhdG9zIGVuIGVzdGUgZm9ybWF0byBzb246DQoNCjEuIERhZG8gcXVlIGV4aXN0ZSB1bmlmb3JtaWRhZCBlbiBsYSBmb3JtYSBlbiBsYSBxdWUgbG9zIGRhdG9zIHNlIGVuY3VlbnRyYW4gYWxtYWNlbmFkb3MsIGVzIG3DoXMgZsOhY2lsIHRyYWJhamFyIGNvbiBlbGxvcyBhbCB1bmlybG9zLCBjcnV6YXJsb3MsIHZpc3VhbGl6YXJsb3MsIG1vZGVsYXJsb3MsIGV0Yy4NCjIuIFBlcm1pdGUgcXVlIGxhIG5hdHVyYWxlemEgKip2ZWN0b3JpemFkYSoqIGRlIFIgYnJpbGxlLiBBbCBhcGxpY2FyIGRpc3RpbnRhcyBmdW5jaW9uZXMgcG9kcmVtb3MgbGxhbWFyIG3DoXMgZsOhY2lsbWVudGUgYSB1bmEgdmFyaWFibGUgcGFyYSBxdWUgc2VhIGFuYWxpemFkYS4NCg0KQWRlbcOhcyBkZSBlc3RhcyB2ZW50YWphcywgdG9kYXMgbGFzIGhlcnJhbWllbnRhcyBkZWwgYHRpZHl2ZXJzZWAgZXN0w6FuIGNyZWFkYXMgcGFyYSBmdW5jaW9uYXIgY29uIGRhdG9zIGVuIGZvcm1hdG8gKip0aWR5KiouDQoNCiMjIDQuMi4gUGl2b3RlIGRlIGRhdG9zDQoNCkVuIGxhIHZpZGEgcmVhbCwgeSBwZXNlIGEgcXVlIGxhcyB2ZW50YWphcyBkZSBsb3MgZGF0b3MgdGlkeSBzb24gb2J2aWFzLCBwb3IgbG8gZ2VuZXJhbCBsb3MgZGF0b3Mgbm8gc29uIGFsbWFjZW5hZG9zIGRlIGVzdGEgbWFuZXJhLCBzaW5vIHF1ZSBidXNjYSBmYWNpbGl0YXIgYWxnw7puIHVzbyBlbiBwYXJ0aWN1bGFyLiBQb3IgZWxsbywgZW4gYWxndW5vcyBhbsOhbGlzaXMsIG5lY2VzaXRhcmVtb3MgbG9ncmFyIHF1ZSBzZSBjdW1wbGFuIGxvcyBwcmluY2lwaW9zIGRlIGRhdGEgKip0aWR5KiosIHkgZWwgcHJpbWVyIHBhc28gc2llbXByZSBlcyAqKmRlc2N1YnJpciBxdcOpIHNvbiB2YXJpYWJsZXMsIHkgcXXDqSBzb24gb2JzZXJ2YWNpb25lcyoqLiBBbGd1bmFzIHZlY2VzIGVzdG8gZXMgZsOhY2lsIHkgb3RyYXMsIGVzIG5lY2VzYXJpbyBwcmVndW50YXJsZSBhIHF1aWVuIGdlbmVyw7MgbG9zIGRhdG9zIHBvcnF1ZSBzZSBwdWVkZW4gZGFyIGRvcyBwcm9ibGVtYXM6DQoNCisgVW5hIHZhcmlhYmxlIHB1ZWRlIGVzdGFyIGRpdmlkaWRhIGVuIHZhcmlhcyBjb2x1bW5hcy4NCisgVW5hIG9ic2VydmFjacOzbiBwdWVkZSBlc3RhciBkaXZpZGlkYSBlbiB2YXJpYXMgZmlsYXMuDQoNClNpIHNlIHN1ZnJlIGRlIGVzdGUgcHJvYmxlbWEsIGxhcyBwcmluY2lwYWxlcyBmdW5jaW9uZXMgcXVlIHVzYXJlbW9zIHNvbiBgcGl2b3RfbG9uZ2VyYCB5IGBwaXZvdF93aWRlcmAuDQoNCiMjIyBQaXZvdGUgZGUgY29sdW1uYXMgYSBmaWxhcyAoUGl2b3QgTG9uZ2VyKQ0KDQpDdWFuZG8gbG9zIG5vbWJyZXMgZGUgdW5hIGNvbHVtbmEgbm8gc29uIHZhcmlhYmxlcywgc2lubyB2YWxvcmVzIGRlIHVuYSB2YXJpYWJsZSB0ZW5kcmVtb3MgcXVlIHVzYXIgZXN0ZSBtw6l0b2RvLiBWZWFtb3MgdW4gZWplbXBsby4NCg0KYGBge3J9DQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmJpbGxib2FyZCAlPiUgaGVhZCgpDQpgYGANCg0KRW4gZXN0ZSBkYXRhc2V0IGxvcyBub21icmVzIGRlIGxhcyBjb2x1bW5hcyByZXByZXNlbnRhbiBzZW1hbmFzIHkgbm8gdmFyaWFibGVzLiBQYXJhIGNvbnZlcnRpcmxhcyBlbiBmaWxhcyB1c2FyZW1vcyBsYSBzaWd1aWVudGUgaW5zdHJ1Y2Npw7NuLCBsYSBjdWFsIGRlcGVuZGUgcHJpbmNpcGFsbWVudGUgZGUgbG9zIGFyZ3VtZW50b3MgYGNvbHNgIChxdWUgaW5kaWNhIGxhcyBjb2x1bW5hcyBxdWUgcGl2b3RhcmVtb3MpLCBgbmFtZXNfdG9gIChxdWUgaW5kaWNhIGxhIHZhcmlhYmxlIGEgbGEgcXVlIG1vdmVyZW1vcyBsb3Mgbm9tYnJlcyBkZSBjb2x1bW5hKSB5IGB2YWx1ZXNfdG9gIChxdWUgaW5kaWNhIGxhIHZhcmlhYmxlIGEgbGEgcXVlIG1vdmVyZW1vcyBsb3MgdmFsb3JlcyBkZSBsYSBjb2x1bW5hKS4NCg0KYGBge3J9DQpiaWxsYm9hcmQgJT4lDQogcGl2b3RfbG9uZ2VyKA0KICAgY29scyA9IHN0YXJ0c193aXRoKCJ3ayIpLA0KICAgbmFtZXNfdG8gPSAid2VlayIsDQogICB2YWx1ZXNfdG8gPSAicmFuayINCiApDQpgYGANCg0KQWwgaGFiZXIgZWplY3V0YWRvIGxhIGluc3RydWNjacOzbiBhbnRlcmlvciB5YSB0ZW5lbW9zIHVuYSB2YXJpYWJsZSBwb3IgY29sdW1uYSwgeSB1bmEgb2JzZXJ2YWNpw7NuIGVuIGNhZGEgZmlsYS4gRXMgZGVjaXIsIGhlbW9zIGNyZWFkbyB1biBkYXRhc2V0IG3DoXMgKipsYXJnbyoqLCBwb3JxdWUgdGllbmUgbcOhcyBmaWxhcyAoZGUgYWxsw60gZWwgbm9tYnJlIGBwaXZvdF9sb25nZXJgKS4gQSBjb250aW51YWNpw7NuIHBvZHJlbW9zIHZlciB1biBncsOhZmljbyBxdWUgbm9zIHJlY29yZGFyw6EgbGEgZWplY3VjacOzbiBkZSBlc3RhIG9wZXJhY2nDs24uDQoNCmBgYHtyLCBvdXQud2lkdGg9IjcwJSIsIGZpZy5hbGlnbj0iY2VudGVyIiwgZWNobz1GfQ0KcDEgPC0gZ2dkcmF3KCkgKyBkcmF3X2ltYWdlKCJodHRwczovL2QzM3d1YnJma2kwbDY4LmNsb3VkZnJvbnQubmV0LzNhZWExOTEwOGQzOTYwNmJiZTQ5OTgxYWNkYTA3Njk2YzBjN2ZjZDgvMmRlNjUvaW1hZ2VzL3RpZHktOS5wbmciKQ0KcGxvdF9ncmlkKHAxKQ0KYGBgDQoNCiMjIyBQaXZvdGUgZGUgZmlsYXMgYSBjb2x1bW5hcyAoUGl2b3QgV2lkZXIpDQoNCkVzdGEgb3BlcmFjacOzbiBlcyBsYSBvcHVlc3RhIGEgYHBpdm90X2xvbmdlcmAgcG9ycXVlIGhhY2UgbG9zIGRhdG9zIG3DoXMgYW5jaG9zLCBlcyBkZWNpciwgaW5jcmVtZW50YSBlbCBuw7ptZXJvIGRlIGNvbHVtbmFzLiBFbiBlc3RlIGNhc28sIGxvcyB2YWxvcmVzIGRlIHVuYSBmaWxhIHNvbiB2YXJpYWJsZXMgeSBubyB2YWxvcmVzIGRlIHVuYSB2YXJpYWJsZS4gVmVhbW9zIHVuIGVqZW1wbG8uDQoNCmBgYHtyfQ0KdXNfcmVudF9pbmNvbWUgJT4lIGhlYWQoKQ0KYGBgDQoNCkVuIGVzdGUgY2FzbywgZW4gbGEgY29sdW1uYSB2YXJpYWJsZSBzZSBndWFyZGFuIGVuIHJlYWxpZGFkIGRvcyB2YXJpYWJsZXMuIFBhcmEgcGFzYXIgbG9zIGRhdG9zIGEgY29sdW1uYXMsIHVzYXJlbW9zIGxhIHNpZ3VpZW50ZSBmdW5jacOzbi4gRXN0YSBkZXBlbmRlIGRlIGRvcyBhcmd1bWVudG9zOiBgbmFtZXNfZnJvbWAgKGVsIGN1YWwgaW5kaWNhIGxhIGNvbHVtbmEgY29uIGxvcyBub21icmVzIHZhcmlhYmxlKSB5IGB2YWx1ZXNfZnJvbWAgKGVsIGN1YWwgaW5kaWNhIGxhL2xhcyBjb2x1bW5hcyBjb24gbG9zIHZhbG9yZXMgZGUgbGEgdmFyaWFibGVzKS4NCg0KYGBge3J9DQp1c19yZW50X2luY29tZSAlPiUNCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IHZhcmlhYmxlLCB2YWx1ZXNfZnJvbSA9IGMoZXN0aW1hdGUsIG1vZSkpDQpgYGANCg0KUGFyYSBubyBvbHZpZGFyIGVzdGUgY29tcG9ydGFtaWVudG8sIG5vcyByZWZlcmlyZW1vcyBhIGxhIHNpZ3VpZW50ZSBpbWFnZW4uDQoNCmBgYHtyLCBvdXQud2lkdGg9IjcwJSIsIGZpZy5hbGlnbj0iY2VudGVyIiwgZWNobz1GfQ0KcDEgPC0gZ2dkcmF3KCkgKyBkcmF3X2ltYWdlKCJodHRwczovL2QzM3d1YnJma2kwbDY4LmNsb3VkZnJvbnQubmV0LzgzNTBmMGRkYTQxNDYyOWI5ZDZjMzU0Zjg3YWNmNWM1ZjcyMmJlNDMvYmNiODQvaW1hZ2VzL3RpZHktOC5wbmciKQ0KcGxvdF9ncmlkKHAxKQ0KYGBgDQoNCiMjIDQuMy4gU2VwYXJhY2nDs24geSB1bmnDs24NCg0Kwr9RdcOpIHN1Y2VkZSBzaSBudWVzdHJvIHByb2JsZW1hIGNvbiBsb3MgZGF0b3MgZXMgcXVlIGVuIHVuYSB2YXJpYWJsZSBzZSBoYW4gYWxtYWNlbmFkbyBlbiByZWFsaWRhZCBkb3MgY29sdW1uYXM/IEVuIGVzZSBjYXNvIHVzYXJlbW9zIGxhIGZ1bmNpw7NuIGBzZXBhcmF0ZWAuIMK/WSBzaSBoYXkgdW5hIHZhcmlhYmxlIGRpdmlkaWRhIGVuIHZhcmlhcyAoY29tbyB1biBpZGVudGlmaWNhZG9yKT8gRW4gZXNlIGNhc28gdXNhcmVtb3MgbGEgZnVuY2nDs24gYHVuaXRlYC4NCg0KVmVhbW9zIGVuIGFjY2nDs24gcHJpbWVybyBhIGxhIGZ1bmNpw7NuIGBzZXBhcmF0ZWAuIFN1cG9uZ2Ftb3MgcXVlIHRlbmVtb3MgZWwgc2lndWllbnRlIGRhdGFzZXQuDQoNCmBgYHtyfQ0KZGYgPC0gdGliYmxlKHggPSBjKCJ4LnkiLCAieC56IiwgInkueiIpKQ0KZGYNCmBgYA0KDQpTZXBhcmVtb3MgbGEgcHJpbWVyYSBjb2x1bW5hIGVuIGRvcyBjb2x1bW5hcyBkaXN0aW50YXMuIFBhcmEgZWxsbyBlc3BlY2lmaWNhcmVtb3MgbGEgdmFyaWFibGUgYSBzZXBhcmFyIChgY29sYCksIGxvcyBub21icmVzIGRlIGxhcyBjb2x1bW5hcyBudWV2YXMgKGBpbnRvYCkgeSBlbCBzZXBhcmFkb3IgKGBzZXBgKS4NCg0KYGBge3J9DQpkZiAlPiUgc2VwYXJhdGUoY29sID0geCwgaW50byA9IGMoIkEiLCAiQiIpLCBzZXAgPSAiXFwuIikNCmBgYA0KDQrCv1ZlbiBxdWUgZXMgYmFzdGFudGUgc2VuY2lsbG8/IFJlZmlyw6Ftb25vcyBhIGxhIHNpZ3VpZW50ZSBpbWFnZW4gY3VhbmRvIG5vIGxvIHJlY29yZGVtb3MuDQoNCmBgYHtyLCBvdXQud2lkdGg9IjcwJSIsIGZpZy5hbGlnbj0iY2VudGVyIiwgZWNobz1GfQ0KcDEgPC0gZ2dkcmF3KCkgKyBkcmF3X2ltYWdlKCJodHRwczovL2QzM3d1YnJma2kwbDY4LmNsb3VkZnJvbnQubmV0L2Y2ZmNhNTM3ZTc3ODk2ODY4ZmVkY2Q4NWQ5ZDAxMDMxOTMwZDc2YzkvNjM3ZDkvaW1hZ2VzL3RpZHktMTcucG5nIikNCnBsb3RfZ3JpZChwMSkNCmBgYA0KDQpQYXJhIHJlYWxpemFyIHVuacOzbiwgcmVmaXLDoW1vbm9zIGFsIHNpZ3VpZW50ZSBlamVtcGxvLg0KDQpgYGB7cn0NCmRmIDwtIGV4cGFuZF9ncmlkKHggPSBjKCJhIiwgIjEiKSwgeSA9IGMoImIiLCAiMiIpKQ0KZGYNCmBgYA0KDQpVbmFtb3MgZGUgZXN0ZSBkYXRhc2V0IGxhcyBkb3MgcHJpbWVyYXMgY29sdW1uYXMuIFBhcmEgZWxsbyB1c2FyZW1vcyBsYSBmdW5jacOzbiBgdW5pdGVgLCBxdWUgZGVwZW5kZSBkZSBsb3MgYXJndW1lbnRvcyBgY29sYCAocXVlIGluZGljYSBlbCBub21icmUgZGUgbGEgY29sdW1uYSByZXN1bHRhZG8pIHkgYC4uLmAgcXVlIGFjZXB0YSB0b2RhcyBsYXMgY29sdW1uYXMgYSBpbmNsdWlyIGVuIGxhIHVuacOzbiAocG9yIGRlZmVjdG8gZXN0YSBzZSBoYWNlIGNvbiBlbCBzw61tYm9sbyBgX2AsIHNpIGRlc2VhcyBjYW1iaWFybG8sIHVzYSBlbCBhcmd1bWVudG8gYHNlcGApLg0KDQpgYGB7cn0NCmRmICU+JSB1bml0ZShjb2wgPSAieiIsIHgsIHksIHNlcCA9ICItIikNCmBgYA0KDQpBbCBpZ3VhbCBxdWUgYW50ZXMsIHBhcmEgbm8gb2x2aWRhciBlbCBmdW5jaW9uYW1pZW50byBkZSBlc3RhIGZ1bmNpw7NuLCB2ZXJlbW9zIGxhIHNpZ3VpZW50ZSBpbWFnZW4uDQoNCmBgYHtyLCBvdXQud2lkdGg9IjcwJSIsIGZpZy5hbGlnbj0iY2VudGVyIiwgZWNobz1GfQ0KcDEgPC0gZ2dkcmF3KCkgKyBkcmF3X2ltYWdlKCJodHRwczovL2QzM3d1YnJma2kwbDY4LmNsb3VkZnJvbnQubmV0LzNkOThkM2JhMDE5ZmVkM2Y5ZWUzMjgyODQ1NjhkNDUwOGU0NzllZjgvMGIzZTYvaW1hZ2VzL3RpZHktMTgucG5nIikNCnBsb3RfZ3JpZChwMSkNCmBgYA0KDQojIyA0LjQuIFZhbG9yZXMgcGVyZGlkb3MNCg0KUG9yIGxvIGdlbmVyYWwsIGxvcyB2YWxvcmVzIHBlcmRpZG9zIHB1ZWRlbiBlc3RhciBleHByZXNhZG9zIGRlIGRvcyBtYW5lcmFzOiBleHBsw61jaXRhIChgTkFgKSAgbyBpbXBsw61jaXRhIChzaW1wbGVtZW50ZSBubyBhcGFyZWNlbikuDQoNClZlYW1vcyB1biBlamVtcGxvOg0KDQpgYGB7cn0NCnN0b2NrcyA8LSB0aWJibGUoDQogIHllYXIgICA9IGMoMjAxNSwgMjAxNSwgMjAxNSwgMjAxNSwgMjAxNiwgMjAxNiwgMjAxNiksDQogIHF0ciAgICA9IGMoICAgMSwgICAgMiwgICAgMywgICAgNCwgICAgMiwgICAgMywgICAgNCksDQogIHJldHVybiA9IGMoMS44OCwgMC41OSwgMC4zNSwgICBOQSwgMC45MiwgMC4xNywgMi42NikNCikNCnN0b2Nrcw0KYGBgDQoNCkVuIGVzdGUgZGF0YXNldCBleGlzdGVuIGRvcyBkYXRvcyBwZXJkaWRvcywgZWwgw7psdGltbyB0cmltZXN0cmUgZGVsIDIwMTUgKHF1ZSB0aWVuZSB1biBgTkFgKSB5IGVsIHByaW1lciB0cmltZXN0cmUgZGUgMjAxNiAocXVlIG5vIGFwYXJlY2UgZW4gbG9zIGRhdG9zKS4gQXPDrSwgc2kgcXVpc2llcmFtb3MgY29udmVydGlyIGVsIGRhdG8gaW1wbMOtY2l0byBlbiBleHBsw61jaXRvLCB1c2FyZW1vcyBsYSBmdW5jacOzbiBjb21wbGV0ZSwgYSBsYSBjdWFsIGJhc3RhIMO6bmljYW1lbnRlIGNvbiBlc3BlY2lmaWNhcmxlIGxhcyBjb2x1bW5hcyBjb24gdG9kYXMgbGFzIHBvc2libGVzIGNvbWJpbmFjaW9uZXMgZGUgZGF0b3MuDQoNCmBgYHtyfQ0Kc3RvY2tzICU+JSBjb21wbGV0ZSh5ZWFyLCBxdHIpDQpgYGANCkVuIGVzdGUgY2FzbywgYW1ib3MgZGF0b3MgcGVyZGlkb3Mgc2UgcmVwcmVzZW50YW4gY29uIGBOQWAuDQoNClNpIGVuIGNhbWJpbywgeWEgdGVuZW1vcyBsb3MgZGF0b3MgcGVyZGlkb3MgZXhwbMOtY2l0b3MsIHBlcm8gcXVlcmVtb3MgbGxlbmFybG9zIGNvbW8gZWwgw7psdGltbyBkYXRvIGRpc3BvbmlibGUgKHNpIGxvcyBkYXRvcyB2aWVuZW4gZGUgdW5hIHRhYmxhIGRpbsOhbWljYSBlbiBleGNlbCBwb3IgZWplbXBsbyksIHVzYXJlbW9zIGxhIGZ1bmNpw7NuIGBmaWxsYCwgY29tbyBzZSBtdWVzdHJhIGEgY29udGludWFjacOzbi4NCg0KYGBge3J9DQp0cmVhdG1lbnQgPC0gdGliYmxlKHBlcnNvbiA9IGMoIkRlcnJpY2sgV2hpdG1vcmUiLCBOQSwgTkEsICJLYXRoZXJpbmUgQnVya2UiKSwNCiAgICAgICAgICAgICAgICAgICAgdHJlYXRtZW50ID0gYygxLDIsMywxKSwgcmVzcG9uc2UgPSBjKDcsMTAsOSw0KSkNCnRyZWF0bWVudA0KYGBgDQoNCmBgYHtyfQ0KdHJlYXRtZW50ICU+JSANCiAgZmlsbChwZXJzb24pDQpgYGANCg0KQSBlc3RlIHByb2NlZGltaWVudG8gc2UgbGUgc3VlbGUgbGxhbWFyICoqbGxldmFyIGxhIMO6bHRpbWEgb2JzZXJ2YWNpw7NuIGhhY2lhIGFkZWxhbnRlKiouDQoNCiMjIDQuNS4gRGF0b3Mgbm9uLXRpZHkNCg0KTG9zIGRhdG9zIHF1ZSBubyBzZSBlbmN1ZW50cmFuIGVuIGZvcm1hdG8gdGlkeSBubyBuZWNlc2FyaWFtZW50ZSBzZXLDoSBuZWdsaWdpZG9zLiBFbiBhbGd1bm9zIGNhc29zIGVzIGNvbnZlbmllbnRlIHRlbmVyIGRhdG9zIGVuIG90cm8gZm9ybWF0byBkYWRvIHF1ZToNCg0KKyBBbGd1bmFzIHJlcHJlc2VudGFjaW9uZXMgYWx0ZXJuYXRpdmFzIHB1ZWRlbiB0ZW5lciBtZWpvciBkZXNlbXBlw7FvIG8gdmVudGFqYXMgZGUgZXNwYWNpby4NCisgw4FyZWFzIGVzcGVjaWFsaXphZGFzIGRlbCBjb25vY2ltaWVudG8gcHVlZGVuIHN1cyBwcm9waWFzIGZvcm1hcyBkZSBndWFyZGFyIHkgcHJvY2VzYXIgbG9zIGRhdG9zLg0KDQpTaSBxdWllcmVzIHNhYmVyIG3DoXMgYWwgcmVzcGVjdG8sIHZpc2l0YSBlbCBwb3N0IGRlIFtKZWZmIExlZWtdKGh0dHBzOi8vc2ltcGx5c3RhdGlzdGljcy5vcmcvMjAxNi8wMi8xNy9ub24tdGlkeS1kYXRhLykuDQoNCiMgNS4gW0RhdG9zIHJlbGFjaW9uYWxlc10oaHR0cHM6Ly9yNGRzLmhhZC5jby5uei9yZWxhdGlvbmFsLWRhdGEuaHRtbCNyZWxhdGlvbmFsLWRhdGEpDQoNCiMgNi4gVHJhbnNmb3JtYWNpw7NuIGRlIGRhdG9zDQoNCiMgNy4gQW7DoWxpc2lzIGV4cGxvcmF0b3JpbyBkZSBkYXRvcw0KDQojIDguIFZpc3VhbGl6YWNpw7NuIGRlIGRhdG9zDQoNCg==