1. Intro


Ya sabemos que R es un lenguaje de programación orientado al análisis de datos. Lo primero que tenemos que hacer para empezar un análisis con datos en R es, evidentemente, cargar los datos en R. En realidad en este tutorial aprenderemos a importar y exportar datos en diferentes formatos.

Hay datos de muchos tipos y en muchos formatos: imágenes, texto, … , pero en el curso nos centraremos en conjuntos de datos que pueden almacenarse en hojas de calculo, ya que esta es la forma habitual de trabajar con datos en las ciencias sociales.


Utilizando un diagrama de este fantástico libro, estamos en la casilla de salida de cualquier análisis de datos.

Primera etapa: Importar datos (http://r4ds.had.co.nz/)

Primera etapa: Importar datos (http://r4ds.had.co.nz/)

Para importar/exportar datos vamos a usar funciones de varios packages, así que tenemos que saber como acceder a su documentación, pero esto ya se vio en el tutorial sobre R-base.


Cargar datos es una de las primeras frustraciones de alguien que comienza a aprender R. Generalmente piensan: pero si en Excel/SSPSS sólo tengo que pinchar en el fichero!! Como mucho tengo que usar los menús desplegables!! En R esto también es posible: R tiene 2 formatos de datos propios que se abren simplemente haciendo doble click y la última versión de RStudio también permite cargar datos a través de menús; pero …. no os acordáis de la Investigación Reproducible!!


RStudio permite cargar datos a través de menús, pero …

RStudio permite cargar datos a través de menús (File > Import Dataset). Por menús se pueden cargar datos CSV, EXCEL, SPSS, SAS y STATA. En el curso pensamos que hay que hacerlo todo a través de scripts; por lo tanto, no usaremos los menús.

Al usar los menús de RStudio para importar datos en realidad se está llamando a unas funciones que son las que importan realmente los datos; ademas, para importar datos a través de los menús, RStudio no usa las funciones de R-base sino las funciones de dos packages readr y haven. En el curso seguiremos este enfoque y usaremos readr y haven, además de algún otro pkg, para importar y exportar datos.

Haremos un poco más de énfasis en la importación de datos ya que si usas R, lo normal es hacer todo el análisis (incluso la generación de informes) en el entorno R.




¿Por qué no usar R-base? [OPCIONAL]

Ya hemos dicho que RStudio carga datos a través de menús, pero no utiliza las funciones de R-base, sino de otros paquetes, concretamente readr y haven

R tiene ya unos 20 años. Las funciones de R-base se construyeron pensando en los estadísticos de hace 20 años (hoy se llamarían analistas de datos). Modificar las funciones de R-base haría que código antiguo dejase de funcionar, así que la mayoría de avances y mejoras se producen en los packages.

Las funciones de readr tratan de ser lo mas parecidas a las funciones equivalentes de R-base pero en cierto sentido mejorándolas y haciéndolas más consistentes; por ejemplo para leer datos CSV la función de R-base es read.csv(); mientas que la función equivalente de “readr” es read_csv(). Las dos hacen lo mismo, leer datos en formato CSV, pero las nuevas funciones tienen algunas ventajas:

  • Son más rápidas.

  • Encajan más en el workflow/paradigma de la investigación reproducible. Por ejemplo, algunas de las funciones de R-base heredan algunas opciones del sistema operativo y las variables de entorno, haciendo posible que un script que funciona en un ordenador no funcione en otro. (Esto aún puede pasarnos a nosotros en el curso. Esperemos que no!!).

  • En lugar de generar data.frames, producen tibbles. Las tibbles son en realidad data.frames pero con algunas particularidades.

  • Las tibbles o “data frames tuneados” tienen unas ciertas ventajas: no convierten por defecto vectores de texto en factores, no usan row names, ni transforman los column names (estás 3 cosas que sí hacen los “data.frames tradicionales” pueden provocar algunas complicaciones, así que mejor tener herramientas que las sorteen).



Datos precargados en R [OPCIONAL]

R-base viene con muchos datos precargados; concretamente en el pkg de R-base llamado datasets. Además muchos packages contienen también conjuntos de datos. Para ver los datos que tenemos precargados y disponibles en R se usa la función data():

#- se abrirá una ventana con el listado de datos disponibles
data()  
#!! guardamos el listado de datos en un data.frame llamado "aa"
aa <- as.data.frame(data()[[3]]) 


Si queremos ver los datos que hay en un package concreto usaremos data(package = "pkg_name")

#- vemos en una ventana el listado de datos disponibles en el pkg ggplot2
data(package = "ggplot2")
#!! guardamos el listado de datos del pkg ggplot2 en el df "aa"
aa <- as.data.frame(data(package = "ggplot2")[[3]]) %>% select(-2)
#!! guardamos el listado de datos del pkg ggplot2 en una tibble
aa <- as_tibble(data(package = "ggplot2")[[3]]) %>% select(-2) 


Por ejemplo, el package ggplot2 tiene los siguientes conjuntos de datos:

Package Item Title
ggplot2 diamonds Prices of over 50,000 round cut diamonds
ggplot2 economics US economic time series
ggplot2 economics_long US economic time series
ggplot2 faithfuld 2d density estimate of Old Faithful data
ggplot2 luv_colours ‘colors()’ in Luv space
ggplot2 midwest Midwest demographics
ggplot2 mpg Fuel economy data from 1999 to 2008 for 38 popular models of cars
ggplot2 msleep An updated and expanded version of the mammals sleep dataset
ggplot2 presidential Terms of 11 presidents from Eisenhower to Obama
ggplot2 seals Vector field of seal movements
ggplot2 txhousing Housing sales in TX


Podemos ver todos los datasets que hay en los packages que tenemos en nuestra librería de packages de nuestro ordenador:

# !! abre una ventana donde se ve el listado de todos los datasets que contienen los packages de nuestra librería
data(package = .packages(all.available = TRUE))




2. Tipos de datos que veremos


Introduciremos funciones para importar/exportar datos de los siguientes formatos:


  • Datos en formato texto (o tabulares)

    • CSV: .csv (comma separated values o , en castellano, datos separados por comas)
    • otros datos en formato texto


  • Formatos de otros programas (software propietario)

    • EXCEL: .xls y .xlsx
    • SPSS: .sav y .por
    • STATA: .dta
    • SAS: .sas


  • Formatos propios de R

    • R objects: .RData o .rda
    • Serialized R objects: .rds


  • Otros Formatos

    • JSON
    • XML


Además aprenderemos como bajar datos a través de APIs:

  • Eurostat
  • INE
  • Banco Mundial



Estrategia que seguiremos para aprender a Importar/Exportar datos

Lo que vamos a hacer en este tutorial para aprender a importar (y exportar) datos en R es elegir un fichero de datos precargado en R y exportarlo a un determinado formato para luego importar el archivo generado o exportado. Repetiremos esto para distintos formatos de datos.

Da igual que archivo de datos usar, así que utilizaremos un conjunto de datos famoso y que pesa poco: “el iris dataset” que fue utilizado por Ronald Fisher. Iris contiene 150 observaciones de 5 variables: mediciones de 5 características sobre 150 flores de la especie Iris.



¿Cómo podemos ver que variables (y de que tipo) hay en un df?

Supongamos que ya hemos cargado un conjunto de datos y que está almacenado en un df, ¿cómo podemos ver que variables (y de que tipo) hay en el df?

Vamos a ver los nombres de las variables (columnas) del dataset iris:

# names() muestra los nombres de las variables de un dataframe
names(iris)
#> [1] "Sepal.Length" "Sepal.Width"  "Petal.Length" "Petal.Width"  "Species"

No nos hace falta, pero veamos los primeros valores de iris:

# head() muestra las n (ne este caso 4) primeras filas de un dataframe
head(iris, n = 4)
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
5.1 3.5 1.4 0.2 setosa
4.9 3.0 1.4 0.2 setosa
4.7 3.2 1.3 0.2 setosa
4.6 3.1 1.5 0.2 setosa

La función summary(), nos hace un resumen (!) del df

# Fíjate que la variable "Species" no tiene media, ni mínimo, ni max. ... es porque es un factor
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  
#>                 
#>                 
#> 


SIEMPRE-SIEMPRE hay que chequear de que clase son las variables que contiene el df.

#- ver la estructura del df. Visualizaremos los nombres y el tipo de las variables
str(iris)
#> 'data.frame':    150 obs. of  5 variables:
#>  $ Sepal.Length: num  5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
#>  $ Sepal.Width : num  3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
#>  $ Petal.Length: num  1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
#>  $ Petal.Width : num  0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
#>  $ Species     : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...

También podéis usar la función skim() del package skimr.

#devtools::install_github("ropenscilabs/skimr")
library(skimr)
skim(iris)


Vamos YA a exportar (e importar) “iris” a diferentes formatos. Empezamos!!




3. Datos tabulares (o de texto)


Estamos acostumbrados a visualizar datos en formato tabular; es decir, como una tabla. Generalmente las columnas son variables y las filas son observaciones de esas variables para diferentes unidades de análisis (“individuos”).

Las columnas se separan con un carácter (generalmente la coma) y las filas con un salto de linea.

Podemos pensar que dependiendo de como se separen las observaciones tenemos distintos tipos de datos tabulares, pero en realidad su estructura es similar: variables en columnas y las observaciones de un individuo separadas por una marca o carácter. Este carácter puede ser un espacio, un tabulador, una coma, punto y coma etc… El formato tabular mas extendido es el CSV, donde las observaciones están separadas por comas.

Estos datos se pueden visualizar en los editores de texto y por eso también se llaman datos en formato texto.

Podemos pensar que hay 2 grupos de datos tabulares:

  • delimitados por caracteres
  • de anchura fija

El package readr lee datos tabulares con las siguientes funciones:

  • si los datos están delimitados por caracteres utiliza: read_delim(), read_csv(), read_tsv()
  • si los datos son de anchura fija: read_fwf() y read_table()

Sólo veremos como importar/exportar datos tabulares del primer tipo; es decir, separados por caracteres. Comenzaremos con el formato CSV que es el más utilizado.



CSV

CSV significa “comma separated data”. En realidad CSV es un caso particular de “tabular o text data”

Recordad que tenemos que exportar el dataframe iris a formato CSV y luego importarlo.

Para exportar iris a un fichero en formato CSV utilizaremos la función write_csv(): solo hay que decirle el objeto que queremos exportar (en este caso un df “iris”) y el nombre (junto con la ruta) del archivo donde queremos guardarlo.

Podemos especificar la ruta completa. Por ejemplo:

#- exporta en formato CSV el df iris al fichero "iris.csv" 
#- Cuidado!! es una ruta absoluta. No funcionará en todos los ordenadores
write_csv(iris, path = "C:/Users/perezp/Desktop/iris.csv")


En realidad no hace falta especificar la ruta completa. Si solo especificamos el nombre del archivo, R lo guardará en el directorio de trabajo.

#- exporta en formato CSV el df iris al fichero "iris.csv". Como no se especifica la ruta, se grabará en el directorio de trabajo 
write_csv(iris, path = "iris.csv")

Recuerda que para saber cual es tu directorio de trabajo puedes usar la función getwd() y puedes cambiarlo desde los menús de RStudio o con setwd(). Por ejemplo:

#- almacenamos en el objeto "path_wd" la ruta del directorio de trabajo del ordenador que estás usando
path_a_mi_wd <- getwd() 

#- Podemos fijar  el directorio de trabajo donde queramos. Por ejemplo:
setwd("C:/Users/perezp/Desktop/Mis_datos/")  
#- en tu ordenador no funcionará porque tu ordenador no tiene esa ruta o estructura de carpetas

#- fijamos el directorio de trabajo (aunque en realidad no hace falta porque esa ruta almacenada en  "path_a_mi_wd" ya era ese el directorio de trabajo
setwd(path_a_mi_wd)      


Recomendamos trabajar con Rprojects y guardar los ficheros de datos en una carpeta llamada /datos/.

Por lo tanto, para exportar los datos de “iris” en la subcarpeta /datos/pruebas/ dentro del proyecto, hay que hacer lo siguiente:

#- exporta en formato .csv el df iris al fichero "iris.csv". Se guardará en la subcarpeta "datos/pruebas/" del proyecto
write_csv(iris, "./datos/pruebas/iris.csv")

Si queremos, podemos poner explícitamente los argumentos (o parámetros) de la función write_csv():

#- Otra vez exportamos en formato .csv el df iris. Esta vez explicitamos las opciones o parámetros de la función
write_csv(iris, path = "./datos/pruebas/iris.csv", col_names = TRUE)


Bien, ya hemos exportado “iris” a un fichero en formato CSV, ahora vamos a importarlo.


Para importar los datos del fichero “iris.csv” hacemos lo siguiente:

#- importamos los datos del fichero "iris.csv" y los guardamos en un objeto que llamamos "iris_imp_csv". Recuerda que acabamos de exportar "iris" a la carpeta "/datos/pruebas/" dentro del Rproject
iris_imp_csv <- read_csv("./datos/pruebas/iris.csv")


Así de sencillo!! Además la mayoría de programas permiten leer y exportar datos en CSV; así que si trabajamos con otro software (Excel, SPSS …), siempre podemos pasar nuestros datos a R exportándolos a CSV; y desde R podemos hacer lo mismo.


Algunas opciones de read_csv() que conviene conocer

A veces los datos tienen ciertos problemas que hay que arreglar; por lo que conviene conocer algunas opciones de read_csv():

  • col_names: read_csv() asume que la primera fila contiene los nombres de las variables. Esto puede cambiarse con col_names = FALSE. Puedes proveer nombres a las variables (o columnas) con col_names = c("X1", "X2")

  • skip:read_csv() por defecto importa todas las filas del archivo, pero puedes hacer que comience a importar en la fila que quieras con skip = n

  • na: En algunos ficheros con datos tabulares los NAs se especifican con algún carácter. Esto podemos tratarlo al leer los datos con el argumento na = "xxx"

Por ejemplo, el chunk que ves abajo utiliza read_csv() para cargar el fichero “my_fichero.csv”. Comienza a importar datos desde la quinta columna, trata los valores -44 y $ como NAs y provee un vector con los nombres que queremos para las variables (o columnas)

mis_datos <- read_csv("my_fichero.csv", skip = 5, na = c("-44", "$"), col_names = c("X1", "X2", "YY", "X4", "ZZ"))

Otros datos tabulares

En realidad, todos los datos tabulares (separados por caracteres) son muy similares. ¡Solo se diferencian en el carácter que hace de separador.

El package “readr” tiene una función especifica para cada tipo de datos tabulares. Por ejemplo, si el separador es un punto y coma, la función para importar estos datos es read_csv2(); si el separador es un tabulador, la función es read_tsv(). Pero también tiene una función genérica que sirve para cualquier tipo de separador: read_delim() . Obviamente usaremos estas funciones.

Por ejemplo, podemos cargar el fichero “my_iris_exportado.csv” que hemos exportado anteriormente utilizando la función genérica read_delim(), solo hay que decirle que el separador es una coma. Se lo decimos con la opción delim = ",". Veámoslo:

#- importamos los datos del fichero "iris.csv" y los guardamos en un objeto que llamamos iris_imp_csv_2. Fíjate en el argumento 'delim'
iris_imp_csv_2 <- read_delim("./datos/pruebas/iris.csv", delim = ",")

Como el formato tabular mas extendido es el CSV; en general, no tendremos necesidad de exportar datos tabulares separados por caracteres distintos a la coma, pero si quisiéramos hacerlo, podríamos hacerlo con write_tsv() o con write_delim():

#- exportamos iris en formato tabular separado por punto y coma. 
write_delim(iris, "./datos/pruebas/iris_2.txt", delim = ";")
#- exportamos iris en formato tabular separado por tabuladores 
write_delim(iris, "./datos/pruebas/iris_3.txt", delim = "\t")
#- exportamos iris en formato tabular separado por un espacio en blanco 
write_delim(iris, "./datos/pruebas/iris_4.txt", delim = " ")


Si quisiéramos importarlos, tendríamos que hacer:

#- exportamos iris en formato tabular separado por punto y coma. 
read_delim("./datos/pruebas/iris_2.txt", delim = ";")
#- exportamos iris en formato tabular separado por tabuladores 
read_delim("./datos/pruebas/iris_3.txt", delim = "\t")
#- exportamos iris en formato tabular separado por un espacio en blanco 
read_delim("./datos/pruebas/iris_4.txt", delim = " ")





4. Formatos propietarios

Hasta que R haga desparecer a SPSS, Stata, ….. 💪, aún será necesario importar algún fichero en formatos de software propietario como los de Excel, SAS, Stata , SPSS y Eviews

Normalmente lo que necesitaremos es importar datos en esos formatos, sólo alguna vez tendremos que exportarlos, ya que todos estos programas pueden leer datos en CSV.

Lo que sí puede resultarnos útil es exportar datos en formato .xls, ya que Excel es muy útil para visualizar datos en formato tabular y también porque mucha gente prefiere (o solo sabe) leer/analizar datos en Excel.




Excel

Exportar a excel

Hay varios packages que graban datos en formato .xls. Pero el más sencillo es el package xlsx. Veámoslo:

library(xlsx)
write.xlsx(iris, "./datos/pruebas/iris.xlsx")

La función write.xlsx() permite especificar el nombre del libro y alguna cosa más; por ejemplo:

# library(xlsx)
write.xlsx(iris, "./datos/pruebas/iris.xlsx", sheetName = "IRIS", row.names = FALSE)

La función write.xlsx() permite añadir datos a un archivo .xlsx preexistente; para ello tenemos que usar la opción append = TRUE:

# library(xlsx)
write.xlsx(iris, "./datos/pruebas/iris.xlsx", sheetName = "IRIS_2", append = TRUE)


El paquete xlsx depende de Java. No suele haber problemas, pero si los tuvieses, el package writexl no tiene ninguna dependencia. Tendrías que hacer lo siguiente:

library(writexl)
write_xlsx(iris, "./datos/pruebas/iris8.xlsx")


Importar archivos en formato excel

También hay varios paquetes, pero usaremos el mismo que usa RStudio en sus menús: readxl

readxl permite leer ficheros .xls y .xlsx. Por ejemplo:

# library(readxl)
iris_imp_xls <- read_excel("./datos/pruebas/iris.xlsx")


Podemos especificar el libro que queremos abrir, ya sea especificando su nombre o su posición en el fichero

# library(readxl)
iris_imp_xls <- read_excel("./datos/pruebas/iris.xlsx", sheet = 2)
iris_imp_xls <- read_excel("./datos/pruebas/iris.xlsx", sheet = "IRIS_2")

La función read_excel() tiene más posibilidades; como ejemplo, la opción skip = 4 permite empezar a importar a partir de la cuarta fila.


Si queremos importar todos los libros (o sheets) de un archivo Excel, podemos hacerlo así:

# library(readxl)
# (!!!)
IRIS_list <- lapply(excel_sheets("./datos/pruebas/iris.xlsx"), read_excel, path = "./datos/pruebas/iris.xlsx")

Hemos guardado los 2 sheets del archivo “./datos/pruebas/iris.xlsx” en un objeto R llamado IRIS_list. Este objeto es una lista con 2 elementos. cada elemento contiene los datos de cada uno de los 2 sheets. Podemos verlo con str():

# (!!!)
str(IRIS_list)

Si quisiéramos recuperar los datos en el formato en el que estamos habituados (dataframes) lo haríamos así:

# (!!!)
primer_iris  <- IRIS_list[[1]]
segundo_iris <- IRIS_list[[2]]




Otros formatos propietarios

Veremos SPSS, Stata y SAS.

Utilizaremos el mismo package que usa RStudio: haven. Si necesitásemos importar otro tipo formato es muy posible que se pueda hacer con los los packages foreign y rio



SPSS

Exportación a SPSS (formato .sav)

# library(haven)
write_sav(iris, "./datos/pruebas/iris.sav")


Importacion de ficheros .sav (tb .por)

# library(haven)
iris_imp_spss <- read_spss("./datos/pruebas/iris.sav")



Stata

Exportación a STATA (formato .dta)

Se puede exportar con haven (pero no permite labelled data!!), así que mejor hacerlo esta vez con el package foreign

# library(foreign)
write.dta(iris, "./datos/pruebas/iris.dta")


Importacion de ficheros STATA (.dta)

# library(haven)
iris_imp_stata <- read_stata("./datos/pruebas/iris.dta")



SAS

Exportación a SAS

haven exporta bien a SAS; pero … los nombres de las variables no pueden contener puntos, así que usamos otro fichero de datos mtcars. Podríamos haber cambiado el nombre de las columnas de iris (quitando los puntos), pero lo dejamos para el próximo tutorial.

# mtcars es un dataset  del pkg ggplot2, asi que ggplot2 debe estar cargado
# library(ggplot2)
# library(haven)
write_sas(mtcars, "./datos/pruebas/mtcars.sas") 


Importacion de ficheros SAS

# library(haven)
mtcars_imp_sas <- read_sas("./datos/pruebas/mtcars.sas")




5. Formato(s) propios de R

Guardar datos en formatos como txt, csv o Excel es lo más habitual si quieres abrir estos datos en otros programas; pero al grabar en estos formatos guardas los datos, PERO no guardas la estructura de los datos; es decir, si por ejemplo una columna la has definido como un factor o como integer, esta información se perderá. En estos casos, una solución es usar el formato propio de R.

Hay dos posibilidades:

  • si quieres grabar un solo objeto, es preferible hacerlo como Rds
  • si quieres grabar varios objetos tienes que hacerlo como RData o abreviado como Rda



RData

RData (o Rda) es un formato especifico de R, pero tiene dos ventajas:

  • es bastante eficiente
  • permite guardar varios objetos en un único archivo


Para exportar my_iris a un fichero en formato .RData utilizaremos la función save(): solo hay que decirle el objeto que queremos exportar (en este caso un df/tibble llamado “my_iris”) y el nombre (junto con la ruta) del archivo donde queremos guardarlo.

#- exporta en formato .RData el df my_iris al fichero "iris.RData". 
save(iris, file = "./datos/pruebas/iris.RData")


El formato .RData tienen la ventaja de que puedes guardar varios objetos a la vez.

save(mtcars, iris,  file = "./datos/pruebas/mtcars_and_iris.RData")

Incluso puede guardar todos los objetos de la sesión. Mejor no hacerlo

save(list = ls(all = TRUE), file= "./datos/pruebas/all_objects.RData")

O todo el espacio de trabajo:

save.image(file = "./datos/pruebas/my_work_space.RData")

para luego cargarlos con:

load("./datos/pruebas/all_objects.RData")
load("./datos/pruebas/my_work_space.RData")


Para cargar o importar datos en formato .RData basta con arrastrarlos dentro de RStudio, o seguir la ruta de menús File > Open File pero para hacerlo en un script se hace así:

#- importamos el fichero "my_data2.RData"
load(file = "./datos/pruebas/iris.RData")


Un posible pega es que al cargar datos con load() se cargan todos los datos que guardaste y además se cargan en el espacio de trabajo con el mismo nombre con que los guardaste. Esto puede ser una pega pues es posible que haya un objeto que se llame igual que un objeto con el que estás trabajando en RStudio.

RDS (Serialized R objects)

Una “desventaja” del formato RData es que al importar un fichero .RData, los objetos que contiene se cargan siempre con el nombre con el que fueron grabados. ¿Qué más da? Bueno, puede parecer que no es muy importante, pero a veces esto puede limitar el workflow, así que muchas veces es preferible grabar como .RDS con la función write_rds(). La pega es que está función solo permite guardar un objeto.

Para exportar iris a formato RDS hacemos:

write_rds(iris, "./datos/pruebas/iris.rds")


Para leer o importar datos RDS (hay que asignarle un nombre, que puede ser o no el nombre original) hacemos:

iris_imp_rds <- readRDS("./datos/pruebas/iris.rds")




6. Otros formatos


Los formatos más frecuentes (al menos en nuestra área) continúan siendo los .csv, .xls, … PERO existen muchos otros formatos.

Hay 2 formatos que es bueno, al menos, saber que existen, porque cada vez son más frecuentes:

  • JSON (JavaScript Object Notation)
  • XML (Extensible Markup Language).

Para JSON, se recomienda usar el paquete jsonlite y para XML, xml2.


El package rio es la navaja suiza del I/O data en R

Si hay algún formato de datos que quieras abrir en R y no lo hayamos tratado, probablemente el package rio sea capaz de importarlo. Aquí tienes la lista de los formatos que puede importar; además todos ellos con una sola función (import()). Para exportar hay que usar la función export()

El paquete rio es muy fácil de usar, pero en realidad lo que hace es llamar/utilizar otros paquetes para hacer el trabajo. Por ejemplo para exportar/importar a SPSS usa el paquete heaven, para Matlab usa el paquete rmatio, así que si quieres importar datos de Matlab tendrás que tenerlo instalado. Al cargar el paquete rio con library(rio nos avisará de si nos falta algún paquete y si queremos los instalará por nosotros con la función rio::install_formats()

Veámos como funciona rio. Para ello primero exportaremos el fichero de datos mtcars adiferentes formatos:

library(rio)
export(mtcars, "./datos/pruebas/mtcars.csv")   # comma-separated values
export(mtcars, "./datos/pruebas/mtcars.rds")   # R serialized
export(mtcars, "./datos/pruebas/mtcars.sav")   # SPSS
export(mtcars, "./datos/pruebas/mtcars.json")  # JSON
export(mtcars, "./datos/pruebas/mtcars.arff")  # Weka Attribute-Relation File Format


Además permite exportar y comprimir los datos directamente:

library(rio)
export(mtcars, "./datos/pruebas/mtcars.tsv.zip") # TSV & Zip it


Para importar los ficheros que hemos exportado previamente:

library(rio)
mtcars_csv  <- import("./datos/pruebas/mtcars.csv")  # CSV
mtcars_spss <- import("./datos/pruebas/mtcars.sav")  # SPSS (.sav)

Como curiosidad decir que rio importa los datos como data.frames, no como tibbles, pero puedes forzarlo con:

library(rio)
mtcars_csv  <- import("./datos/pruebas/mtcars.csv", setclass = "tibble")  # CSV

En este post también explican que rio puede ser una alternativa a rvest y/o xml2 para importar tablas html en internet. Por ejemplo:

my_url <- "http://www.scoresway.com/?sport=soccer&page=competition&id=8"
table <- import(my_url, format = "html", which = 2)

Si tampoco rio puede leer el formato que necesitas, es muy probable que alguien haya escrito un package para hacerlo; tendrás que ver si existe buscando en internete.

Recientemente ha aparecido otro meta-pkg para leer datos. Es el pkg readit. Aún no lo he usado pero, por varias razones, no quiero olvidarme de él. Puedes verlo aquí




7. Descargar datos de internet


Hay muchísimos datos en internet para descargar; siempre podemos descargarlos usando el navegador, PERO la filosofía del curso es (si podemos) hacerlo todo desde R/RStudio

Desde RStudio, podemos descargar datos con las mismas funciones que usábamos para cargar en el entorno de trabajo los datos que teníamos en nuestro PC. La única diferencia consiste en que, en lugar de proporcionar la ruta al fichero, tendremos que proporcionar la ruta de internet. Por ejemplo:


# cargamos los datos del fichero "bio260-heights.csv"
url <- "https://raw.githubusercontent.com/datasciencelabs/data/master/bio260-heights.csv"
datos <- read_csv(url)


A veces podemos necesitar hacer una copia de los datos a nuestro ordenador. En este caso, lo que yo haría es cargar los datos y luego exportarlos a .rds; pero también podemos hacerlo directamente con la función download.file():


# descargamos y almacenamos en nuestro PC los datos del fichero "bio260-heights.csv"
url <- "https://raw.githubusercontent.com/datasciencelabs/data/master/bio260-heights.csv"
destino <- "./datos/pruebas/bio260-heights.csv"
download.file(url, destino)
dat <- read.csv(destino)


A veces, la función de R-base download.file puede tener problemas si el protocolo es https. En estos casos, la función dowload() del pkg downloader puede solucionarlo:

#install.packages("downloader")
library(downloader)
url <- "https://raw.githubusercontent.com/datasciencelabs/data/master/bio260-heights.csv"
filename <- basename(url)
destino <- paste0("./datos/pruebas/", filename)
download(url,destino)
dat <- read.csv(destino)




8. API’s y Web Scrapping


El proceso y acciones para recopilar información de la Web se conoce como web scrapping. Este proceso se puede hacer manualmente, pero lo habitual es automatizarlo utilizando software. Se puede acceder a los datos directamente pero actualmente es muy común hacerlo a través de APIs, ya que la mayoría de organismos/empresas tienen una o varias APIs.

API significa “Aplication Programming Interface” y se puede entender como un mecanismo que nos permite interactuar (por ejemplo para hacer una petición de datos) con un servidor de internet. Por ejemplo, muchos bancos tienen APIs a las que se les pueden hacer peticiones, esto hace posible que se desarrollen apps para hacer ciertas operaciones bancarias; es decir, una API es un mecanismo que nos permite acceder y/o interactuar con determinadas funciones de un servicio web.

Las APIs facilitan mucho la recopilación de datos al poderse acceder a ellas de forma programática ya que proveen de un proceso de acceso a ellos estandarizado: se envía una “http request” a la API y se reciben los datos en un determinado formato, generalmente JSON.

En el entorno R se pueden desarrollar paquetes para acceder a APIs; por ejemplo, vamos a utilizar el paquete de R eurostat para acceder a la API de Eurostat y descargar datos directamente en R. Veámoslo:



Eurostat

Eursotat tiene una API que permite hacer peticiones de datos. Obviamente, para poder hacer peticiones de datos a través de su API has de conocer su sintaxis; si estás interesado puedes empezar aquí. Nosotros accederemos a Eurostat a través del package eurostat. Si estas interesado en bajar datos de Eurostat es conveniente que uses esta vignette y la cheat sheet. Veamos un ejemplo:

Con la función get_eurostat() es suficiente para bajar una tabla de Eurostat con el porcentaje de empleos en sectores culturales:

# install.packages("eurostat")
library("eurostat")
df <- get_eurostat("cult_emp_sex", time_format = 'raw', keepFlags = T)       #- bajamos los datos de la tabla "cult_emp_sex": empleo cultural por genero"


Un ejemplo más completo: descargaremos los datos de la tabla hlth_silc_17 que contiene datos con la “esperanza de vida saludable” para diferentes años en los países de la UE.

# install.packages("eurostat")
library("eurostat")

#------------------ podemos buscar un  "tema" con la f. search_eurostat()
aa <- search_eurostat("employment", type = "all") 

#------------------ elegimos una tabla de Eurostat
my_table <- "hlth_silc_17"          #- elegimos una tabla; por ejemplo "hlth_silc_17": "Healthy life expectancy based on self-perceived health"
label_eurostat_tables(my_table)     #- da informacion sobre la Base de datos q estas buscando

#------------------ descargamos los datos con get_eurostat()
df <- get_eurostat(my_table, time_format = 'raw', keepFlags = T )       #- bajamos los datos de una tabla
df_l <- label_eurostat(df)        #- pone labels: Spain en lugar de su código (mas legible,menos fácil de programar)

#------------------ los arreglamos un poco 
library("tidyverse")
library("pjpv2020.01") #- remotes::install_github("perezp44/pjpv2020.01")
aa <- pjp_f_valores_unicos(df)       #- ver los valores unicos de cada columna
aa <- pjp_f_valores_unicos(df_l)     #- ver los valores unicos de cada columna
df <- label_eurostat(df, code = c("geo", "unit", "indic_he"))

Ahora vamos a fusionarlo con datos de los límites espaciales de cada país, para finalmente hacer un gráfico espacial …. PERO Eurostat o eurostat cambiaron su API, entonces …. este chunk no funciona con la versión actual del paquete eurostat. Lo dejo por si aún funcionase en los ordenadores del aula.

#- selecciono datos de 2016, Females, y HE_50 y después hago un cut de "values"
df_x <- df %>% filter(time == "2016") %>%  filter(sex == "Females") %>% filter(indic_he_code == "HE_50") %>% 
        mutate(cat = cut_to_classes(values, n = 7, decimals = 1))
mapdata <- merge_eurostat_geodata(df_x, resolution = "20", geocolumn = "geo_code") #- fusiono con geo data

ggplot(mapdata, aes(x = long, y = lat, group = group))+
  geom_polygon(aes(fill = cat), color = "black", size = .1)+
  scale_fill_brewer(palette = "RdYlBu") +
  labs(title = "Healthy life expectancy, 2016",
       subtitle = "Health expectancy in years at 50",
       fill = "Healthy life expectancy",
       caption = "(C) EuroGeographics for the administrative boundaries") + theme_light() +
  coord_map(xlim = c(-12, 44), ylim = c(35, 67))

Este sí funcionará con la nueva versión del pkg eurostat

df_x <- df %>% filter(time == "2016") %>%  filter(sex == "Females") %>% filter(indic_he_code == "HE_50") %>% 
        mutate(cat = cut_to_classes(values, n = 7, decimals = 1))

geometrias <- get_eurostat_geospatial(resolution = "20", nuts_level = "0") #- ahora se bajan las geometrías y tienes que unirla tu con dplyr (Hay un Pb de encoding)
mapdata <- inner_join(geometrias, df_x, by = c("geo" = "geo_code"))

p <- ggplot(mapdata) +
  geom_sf(aes(fill = cat, geometry = geometry), color = "black", size = .1) +
  scale_fill_brewer(palette = "RdYlBu") +
  labs(title = "Healthy life expectancy, 2016",
       subtitle = "Health expectancy in years at 50",
       fill = "Healthy life expectancy",
       caption = "(C) EuroGeographics for the administrative boundaries") + theme_light() +
  coord_sf(xlim = c(-12, 44), ylim = c(35, 67)) 
p




INE


¿El INE tiene API? Pues sí, aquí puedes “verla”, pero …

Hace poco tuvimos que utilizar alguna tabla del INE y, en lugar de usar la API, nos bajamos los datos así:

library("pxR")              #- para trabajar con datos PC-Axis
library("tidyverse")
library("pjpv2020.01")
file_name <- "http://www.ine.es/jaxiT3/files/t/es/px/4189.px?nocab=1"
df <- read.px(file_name) %>% as.data.frame() %>% as.tbl()   #- no funcionaba en 3.5 x $
aa <- pjpv2020.01::pjp_f_valores_unicos(df)     #- ver los valores únicos de cada columna

La verdad es que ahora (2019) hay un paquete que funciona/funcionaba bastante bien: https://github.com/oddworldng/INEbaseR



Banco Mundial


Para acceder a la API del Banco Mundial hay, actualmente, 2 paquetes de R: WDI y wbstats.


Podemos bajar datos del Banco Mundial con el paquete WDI así:

#install.packages("WDI")
library("WDI")

#---- buscamos datos relacionados con GDP
aa <- WDIsearch('gdp')
aa <- WDIsearch('gdp.*capita.*constant')

#---- descargamos "NY.GDP.PCAP.KD":  GDP per capita (constant 2010 US$)
df <- WDI(indicator = "NY.GDP.PCAP.KD")
#---- podemos filtrar la querry
df <- WDI(indicator = "NY.GDP.PCAP.KD", country = c('MX','CA','US'), start = 1960, end = 2017)


Podemos bajar datos del Banco Mundial con el paquete wbstats así:

#install.packages("wbstats")
library("wbstats")

#-------  lista de indicadores disponibles
aa <- wb_cachelist

#---- buscamos datos relacionados con GDP
aa <- wbsearch(pattern = "gdp")
aa <- wbsearch('gdp.*capita.*constant')

#---- descargamos "NY.GDP.PCAP.KD":  GDP per capita (constant 2010 US$)
df <- wb(indicator = "NY.GDP.PCAP.KD")

#---- podemos filtrar la querry
df <- wb(indicator = "NY.GDP.PCAP.KD", country = c('MX','CA','US'), startdate = 2000, enddate = 2017)


Aquí tenemos un post en el que se usa el pkg wbstats para obtener datos y luego graficarlos.



CrossRef

El paquete rcrossref permite acceder a varias de las APIs de CrossRef. ¿Que qué es CrossRef? Pues es un servicio que permite, entre otras cosas, facilitar el proceso de referenciar artículos en tus papers. Aquí lo explican. Hay otro package para acceder a CrossRef: crminer este pkg permite bajarse el texto del documento, pero claro, el texto ha de estar disponible!!


#install.packages("rcrossref")
library("rcrossref")

#----- con cr_cn() podemos ver como se cita un determinado artículo en un determinado formato, por ejemplo "apa"
my_doi <- "10.1111/j.1467-6486.2012.01072.x"
cr_cn(dois = my_doi, format = "text", style = "apa")

cr_cn(dois = my_doi, format = "bibtex", style = "apa", locale = "en-US", raw = FALSE, progress = "none")

#------ con cr_citation_count() puedes ver el numero de citas de un artículo/DOI
aa <- cr_citation_count(doi = my_doi)

#------ con cr_abstract()
aa <- cr_abstract(doi = "10.1109/TASC.2010.2088091")

#------ con cr_journals() vemos journals
aa <- cr_journals(query = "economics", limit = 100) %>% .$data %>% as.tibble()

#------ mucha informacion del articulo
aa <- cr_works(dois = my_doi) %>% .$data %>% as.tibble()


Otros pkg for APIs


Hay muchos otros paquetes de R hechos para acceder a APIs (twitter, ECB, spotify, pdfetch, naturalearth, ….). Puedes ver algunos aquí, aquí y aquí.

Aquí puedes ver un listado enorrrrrme😱eeee: Pinterest, Instagram, GoogleTrends, Google Analytics, Flickr, …., …., ….

Una de las ultimas que he visto ha sido el pkg spooc. En su vignette nos dicen que se pueden acceder a un conjunto de paquetes que contienen: “some form of biodiversity or taxonomic data. Since several of these datasets have been georeferenced, it provides numerous opportunities for visualizing species distributions”



Scrapping tables

Además de utilizar paquetes para acceder a servicios web a través de sus APIs, podemos usar otros paquetes (principalmente rvest) para hacer web scrapping. Puedes ver ejemplos aquí, aquí, aquí o aquí.


Aquí tenéis un ejemplo sencillo para bajar datos de jurgol … pero ya no funciona


library(XML)

url <- "http://www.comuniazo.com/comunio/jugadores"
url <- "https://www.comuniazo.com/comunio/jugadores"

jugadores <-  readHTMLTable(url, stringsAsFactors = T, colnames = c("Posicion","Equipo","Jugador","Puntos","Media","Puntos_Casa","Media_Casa","Puntos_Fuera","Media_fuera", "Valor"), colClasses = c("character","character","character","FormattedNumber","FormattedNumber","FormattedNumber","FormattedNumber","FormattedNumber","FormattedNumber"))

aa <- jugadores[[1]] %>% as.tibble()

Para sustituir el ejemplo del jurgol bajemos una tabla de la wikipedia. Este ejemplo está sacado de este post

library("rvest")
library("tidyverse")
content <- read_html("https://es.wikipedia.org/wiki/Anexo:Municipios_de_la_provincia_de_Teruel")

body_table <- content %>% html_nodes('body')  %>%
                    html_nodes('table') %>%
                    html_table(dec = ",") 
Teruel <- body_table[[1]]
names(Teruel) <- c("Nombre", "Extension", "Poblacion", "Densidad", "Comarca", "Partido_judicial", "Altitud")
library(stringr)
Teruel <- Teruel %>% map(str_trim) %>% as_tibble() #- quita caracteres al final
Teruel <- Teruel %>% mutate(Altitud = str_replace_all(Altitud,"[[:punct:]]", "")) 
Teruel <- Teruel %>% mutate(Altitud = as.double(Altitud)) %>% arrange(desc(Altitud))
library(kableExtra)
aa <- Teruel %>% select(1,3,5,7) %>%  slice(1:4) 
#knitr::kable(aa, digits = 2, align = "c", caption = "Los 4 municipios de Teruel con más altitud" )
knitr::kable(aa, "html", digits = 2,  caption = "Los 4 municipios de Teruel con más altitud") %>%
  kable_styling(bootstrap_options = c("striped", "hover"))
Los 4 municipios de Teruel con más altitud
Nombre Poblacion Comarca Altitud
Valdelinares 106 Gúdar-Javalambre 1695
Griegos 136 Sierra de Albarracín 1601
Gúdar 76 Gúdar-Javalambre 1587
Bronchales 432 Sierra de Albarracín 1569




Bibliografía

LS0tCnRpdGxlOiAiSW1wb3J0YXIgKHkgZXhwb3J0YXIpIGRhdG9zIGNvbiBSIgphdXRob3I6ICJQZWRybyBKLiBQw6lyZXogKHBlZHJvLmoucGVyZXpAdXYuZXMpLiBVbml2ZXJzaXRhdCBkZSBWYWzDqG5jaWEgIDxicj4gPGJyPiBXZWIgZGVsIGN1cnNvOiA8aHR0cHM6Ly9wZXJlenA0NC5naXRodWIuaW8vaW50cm8tZHMtMjAtMjEtd2ViLz4iCmRhdGU6ICJOb3ZpZW1icmUgZGUgMjAxNyAoYWN0dWFsaXphZG8gZWwgYHIgZm9ybWF0KFN5cy50aW1lKCksICclZC0lbS0lWScpYCkiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgY3NzOiAhZXhwciBoZXJlOjpoZXJlKCJhc3NldHMiLCAic3R5bGVzX3BqcC5jc3MiKSAjLWh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzU2NjgxODc5L2hvdy10by11c2UtaGVyZS1mb3ItcGF0aHMtdG8tY3NzLWJlZm9yZS1ib2R5LWFuZC1hZnRlci1ib2QKICAgIHRoZW1lOiBwYXBlcgogICAgaGlnaGxpZ2h0OiB0ZXh0bWF0ZQogICAgdG9jOiB0cnVlCiAgICB0b2NfZGVwdGg6IDMKICAgIHRvY19mbG9hdDoKICAgICAgY29sbGFwc2VkOiB0cnVlCiAgICAgIHNtb290aF9zY3JvbGw6IHRydWUKICAgIHNlbGZfY29udGFpbmVkOiB0cnVlCiAgICBudW1iZXJfc2VjdGlvbnM6IGZhbHNlCiAgICBpbmNsdWRlczoKICAgICAgYWZ0ZXJfYm9keTogIWV4cHIgaGVyZTo6aGVyZSgiYXNzZXRzIiwgImZvb3Rlci5odG1sIikgCiAgICAgIGluX2hlYWRlcjogCiAgICAgICAgLSAhZXhwciBoZXJlOjpoZXJlKCJhc3NldHMiLCAiZ29vZ2xlLWFuYWx5dGljcy5odG1sIikgCiAgICAgICAgLSAhZXhwciBoZXJlOjpoZXJlKCJhc3NldHMiLCAiZmF2aWNvbi1zb2wuaHRtbCIpCiAgICBkZl9wcmludDoga2FibGUKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKZWRpdG9yX29wdGlvbnM6CiAgY2h1bmtfb3V0cHV0X3R5cGU6IGNvbnNvbGUKI2JpYmxpb2dyYXBoeTogImByIGhlcmU6OmhlcmUoJ2Fzc2V0cycsICdiaWJsaW8uYmliJylgIiAgIy0gam9vb2Rlci4gc2luZ2xlIHF1b3RlcyBodHRwczovL2NvbW11bml0eS5yc3R1ZGlvLmNvbS90L3VzZS1oZXJlLWhlcmUtZnVuY3Rpb24taW4teWFtbC1vcHRpb24vMTg2NjcvOQotLS0KCmBgYHtyLCBpbmNsdWRlID0gRkFMU0V9CmxpYnJhcnkodGlkeXZlcnNlKQpgYGAKCmBgYHtyIGNodW5rLXNldHVwLCBpbmNsdWRlID0gRkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgZXZhbCA9IFRSVUUsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgI3Jlc3VsdHMgPSAiaG9sZCIsCiAgICAgICAgICAgICAgICAgICAgICBjYWNoZSA9IEZBTFNFLCBjYWNoZS5wYXRoID0gIi9jYWNoZXMvIiwgY29tbWVudCA9ICIjPiIsCiAgICAgICAgICAgICAgICAgICAgICAjZmlnLndpZHRoID0gNywgI2ZpZy5oZWlnaHQ9IDcsCiAgICAgICAgICAgICAgICAgICAgICAjb3V0LndpZHRoID0gNywgb3V0LmhlaWdodCA9IDcsCiAgICAgICAgICAgICAgICAgICAgICBjb2xsYXBzZSA9IFRSVUUsICBmaWcuc2hvdyA9ICJob2xkIiwKICAgICAgICAgICAgICAgICAgICAgIGZpZy5hc3AgPSA3LzksIG91dC53aWR0aCA9ICI2MCUiLCBmaWcuYWxpZ24gPSAiY2VudGVyIikKCiMtIHBhcmEgbWVqb3JhciBsb3MgZ3LDoWZpY29zLCBidWVubyBlbiByZWFsaWRhZCBwYXJhIHF1ZSBzZSB2ZWFuIGlndWFsIGVuIGRpc3RpbnRvcyBTTwojLSBodHRwczovL3d3dy5qdW1waW5ncml2ZXJzLmNvbS9ibG9nL3Ita25pdHItbWFya2Rvd24tcG5nLXBkZi1ncmFwaGljcy8Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGRldiA9ICJwbmciLCBkZXYuYXJncyA9IGxpc3QodHlwZSA9ICJjYWlyby1wbmciKSkKYGBgCgpgYGB7ciBvcHRpb25zLXNldHVwLCBpbmNsdWRlID0gRkFMU0V9Cm9wdGlvbnMoc2NpcGVuID0gOTk5KSAjLSBwYXJhIHF1aXRhciBsYSBub3RhY2nDs24gY2llbnTDrWZpY2EKb3B0aW9ucygieWFtbC5ldmFsLmV4cHIiID0gVFJVRSkgIy0gaHR0cHM6Ly9naXRodWIuY29tL3Zpa2luZy9yLXlhbWwvaXNzdWVzLzQ3ICAobG8gcHVzZSB4IGVsIHBiIGNvbiBlbCB3YXJuaW5nKSBFbiByZWFsaWRhZCBjcmVvIHF1ZSBtZWpvciBzZXLDrWEgcG9uZXJsbyBlbiBSUHJvZmlsZQpgYGAKCmBgYHtyIGtsaXBweSwgZWNobyA9IEZBTFNFfQprbGlwcHk6OmtsaXBweShwb3NpdGlvbiA9IGMoInRvcCIsICJyaWdodCIpKSAjLSByZW1vdGVzOjppbnN0YWxsX2dpdGh1Yigicmxlc3VyL2tsaXBweSIpCmBgYAoKYGBge3IsIGluY2x1ZGUgPSBGQUxTRX0Kb3B0aW9ucyhodG1sdG9vbHMuZGlyLnZlcnNpb24gPSBGQUxTRSkKI2tuaXRyOjpvcHRzX2NodW5rJHNldChmaWcucmV0aW5hID0gMywgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIGVjaG89RkFMU0UsIG91dC53aWR0aCA9ICI4NSUiKQpsaWJyYXJ5KG1ldGF0aGlzKQptZXRhKCkgJT4lIG1ldGFfbmFtZSgiZ2l0aHViLXJlcG8iID0gInBlcmV6cDQ0L2ludHJvLWRzLTIwLTIxLXdlYiIpICU+JSAKICBtZXRhX3NvY2lhbCgKICAgIHRpdGxlID0gIkltcG9ydGFyIHkgZXhwb3J0YXIgZGF0b3MgY29uIFIiLAogICAgZGVzY3JpcHRpb24gPSBwYXN0ZSgiRW4gZXN0ZSB0dXRvcmlhbCBhcHJlbmRlcmVtb3MgYSwgZGVzZGUgUlN0dWRpbywgaW1wb3J0YXIgeSBleHBvcnRhciBkYXRvcyBlbiBkaWZlcmVudGVzIGZvcm1hdG9zLCB1c2FuZG8gcGFxdWV0ZXMgY29tbyByZWFkciwgcmVhZHhsIHkgcmlvLiIpLAogICAgdXJsID0gImh0dHBzOi8vcGVyZXpwNDQuZ2l0aHViLmlvL2ludHJvLWRzLTIwLTIxLXdlYi90dXRvcmlhbGVzL3R0XzA0X2Nhcmdhcl9kYXRvcy5odG1sIiwKICAgIG9nX3R5cGUgPSAid2Vic2l0ZSIsCiAgICBvZ19hdXRob3IgPSAiUGVkcm8gSi4gUMOpcmV6IgogICkKYGBgCgotLS0tLS0tLS0tLS0tLS0tLQoKPGJyPgoKIyAxLiBJbnRybwoKPGJyPgoKWWEgc2FiZW1vcyBxdWUgUiBlcyB1biBsZW5ndWFqZSBkZSBwcm9ncmFtYWNpw7NuIG9yaWVudGFkbyBhbCBhbsOhbGlzaXMgZGUgZGF0b3MuIExvIHByaW1lcm8gcXVlIHRlbmVtb3MgcXVlIGhhY2VyIHBhcmEgZW1wZXphciB1biBhbsOhbGlzaXMgY29uIGRhdG9zIGVuIFIgZXMsIGV2aWRlbnRlbWVudGUsIGNhcmdhciBsb3MgZGF0b3MgZW4gUi4gRW4gcmVhbGlkYWQgZW4gZXN0ZSB0dXRvcmlhbCBhcHJlbmRlcmVtb3MgYSAqKmltcG9ydGFyIHkgZXhwb3J0YXIgZGF0b3MgZW4gZGlmZXJlbnRlcyBmb3JtYXRvcyoqLgoKCkhheSBkYXRvcyBkZSBtdWNob3MgdGlwb3MgeSBlbiBtdWNob3MgZm9ybWF0b3M6IGltw6FnZW5lcywgdGV4dG8sIC4uLiAsIHBlcm8gZW4gZWwgY3Vyc28gbm9zIGNlbnRyYXJlbW9zIGVuIGNvbmp1bnRvcyBkZSBkYXRvcyBxdWUgcHVlZGVuIGFsbWFjZW5hcnNlIGVuIGhvamFzIGRlIGNhbGN1bG8sIHlhIHF1ZSBlc3RhIGVzIGxhIGZvcm1hIGhhYml0dWFsIGRlIHRyYWJhamFyIGNvbiBkYXRvcyBlbiBsYXMgY2llbmNpYXMgc29jaWFsZXMuCgo8YnI+CgpVdGlsaXphbmRvIHVuIGRpYWdyYW1hIGRlIFtlc3RlIGZhbnTDoXN0aWNvIGxpYnJvXShodHRwOi8vcjRkcy5oYWQuY28ubnovKSwgZXN0YW1vcyBlbiBsYSBjYXNpbGxhIGRlIHNhbGlkYSBkZSBjdWFscXVpZXIgYW7DoWxpc2lzIGRlIGRhdG9zLgoKCmBgYHtyLCAgZWNobyA9IEZBTFNFLCBldmFsID0gVFJVRSwgZmlnLmNhcCA9ICJQcmltZXJhIGV0YXBhOiBJbXBvcnRhciBkYXRvcyAoaHR0cDovL3I0ZHMuaGFkLmNvLm56LykiLCBmaWcuYXNwID0gNC8yLCBvdXQud2lkdGggPSAiODAlIiwgZmlnLmFsaWduID0gImNlbnRlciJ9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltYWdlbmVzIiwgInR0XzA0X2ltZ18wMV9jYXJnYXItZGF0b3MucG5nIikpCmBgYAoKCgpQYXJhIGltcG9ydGFyL2V4cG9ydGFyIGRhdG9zIHZhbW9zIGEgdXNhciBmdW5jaW9uZXMgZGUgdmFyaW9zIHBhY2thZ2VzLCBhc8OtIHF1ZSB0ZW5lbW9zIHF1ZSBzYWJlciBjb21vIGFjY2VkZXIgYSBzdSBkb2N1bWVudGFjacOzbiwgcGVybyBlc3RvIHlhIHNlIHZpbyBlbiBlbCB0dXRvcmlhbCBzb2JyZSBSLWJhc2UuCgoKCjxicj4KCkNhcmdhciBkYXRvcyBlcyB1bmEgZGUgbGFzIHByaW1lcmFzIGZydXN0cmFjaW9uZXMgZGUgYWxndWllbiBxdWUgY29taWVuemEgYSBhcHJlbmRlciBSLiBHZW5lcmFsbWVudGUgcGllbnNhbjogcGVybyBzaSBlbiBFeGNlbC9TU1BTUyBzw7NsbyB0ZW5nbyBxdWUgcGluY2hhciBlbiBlbCBmaWNoZXJvISEgQ29tbyBtdWNobyB0ZW5nbyBxdWUgdXNhciBsb3MgbWVuw7pzIGRlc3BsZWdhYmxlcyEhIApFbiBSIGVzdG8gdGFtYmnDqW4gZXMgcG9zaWJsZTogUiB0aWVuZSAyIGZvcm1hdG9zIGRlIGRhdG9zIHByb3Bpb3MgcXVlIHNlIGFicmVuIHNpbXBsZW1lbnRlIGhhY2llbmRvIGRvYmxlIGNsaWNrIHkgbGEgw7psdGltYSB2ZXJzacOzbiBkZSBSU3R1ZGlvIHRhbWJpw6luIHBlcm1pdGUgY2FyZ2FyIGRhdG9zIGEgdHJhdsOpcyBkZSBtZW7DunM7IHBlcm8gLi4uLiBubyBvcyBhY29yZMOhaXMgZGUgbGEgSW52ZXN0aWdhY2nDs24gUmVwcm9kdWNpYmxlISEgCgoKPGJyPgoKIyMjIyAgUlN0dWRpbyBwZXJtaXRlIGNhcmdhciBkYXRvcyBhIHRyYXbDqXMgZGUgbWVuw7pzLCBwZXJvIC4uLgoKUlN0dWRpbyBwZXJtaXRlIGNhcmdhciBkYXRvcyBhIHRyYXbDqXMgZGUgbWVuw7pzIChgIEZpbGUgPiBJbXBvcnQgRGF0YXNldCBgKS4gUG9yIG1lbsO6cyBzZSBwdWVkZW4gY2FyZ2FyIGRhdG9zIENTViwgRVhDRUwsIFNQU1MsIFNBUyB5IFNUQVRBLiBFbiBlbCBjdXJzbyBwZW5zYW1vcyBxdWUgaGF5IHF1ZSBoYWNlcmxvIHRvZG8gYSB0cmF2w6lzIGRlIHNjcmlwdHM7IHBvciBsbyB0YW50bywgbm8gdXNhcmVtb3MgbG9zIG1lbsO6cy4KCkFsIHVzYXIgbG9zIG1lbsO6cyBkZSBSU3R1ZGlvIHBhcmEgaW1wb3J0YXIgZGF0b3MgZW4gcmVhbGlkYWQgc2UgZXN0w6EgbGxhbWFuZG8gYSB1bmFzIGZ1bmNpb25lcyBxdWUgc29uIGxhcyBxdWUgaW1wb3J0YW4gcmVhbG1lbnRlIGxvcyBkYXRvczsgYWRlbWFzLCBwYXJhIGltcG9ydGFyIGRhdG9zIGEgdHJhdsOpcyBkZSBsb3MgbWVuw7pzLCBSU3R1ZGlvIG5vIHVzYSBsYXMgZnVuY2lvbmVzIGRlIFItYmFzZSBzaW5vIGxhcyBmdW5jaW9uZXMgZGUgZG9zIHBhY2thZ2VzICoqYHJlYWRyYCoqIHkgKipgaGF2ZW5gKiouIEVuIGVsIGN1cnNvIHNlZ3VpcmVtb3MgZXN0ZSBlbmZvcXVlIHkgdXNhcmVtb3MgYHJlYWRyYCB5IGBoYXZlbmAsIGFkZW3DoXMgZGUgYWxnw7puIG90cm8gcGtnLCBwYXJhIGltcG9ydGFyIHkgZXhwb3J0YXIgZGF0b3MuCgpIYXJlbW9zIHVuIHBvY28gbcOhcyBkZSDDqW5mYXNpcyBlbiBsYSBpbXBvcnRhY2nDs24gZGUgZGF0b3MgeWEgcXVlIHNpIHVzYXMgUiwgbG8gbm9ybWFsIGVzIGhhY2VyIHRvZG8gZWwgYW7DoWxpc2lzIChpbmNsdXNvIGxhIGdlbmVyYWNpw7NuIGRlIGluZm9ybWVzKSBlbiBlbCBlbnRvcm5vIFIuIAoKPGJyPgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKPGJyPgoKCiMjIyMgwr9Qb3IgcXXDqSBubyB1c2FyIFItYmFzZT8gW09QQ0lPTkFMXQoKWWEgaGVtb3MgZGljaG8gcXVlIFJTdHVkaW8gY2FyZ2EgZGF0b3MgYSB0cmF2w6lzIGRlIG1lbsO6cywgcGVybyBubyB1dGlsaXphIGxhcyBmdW5jaW9uZXMgZGUgUi1iYXNlLCBzaW5vIGRlIG90cm9zIHBhcXVldGVzLCBjb25jcmV0YW1lbnRlIGByZWFkcmAgeSBgaGF2ZW5gCgpSIHRpZW5lIHlhIHVub3MgMjAgYcOxb3MuIExhcyBmdW5jaW9uZXMgZGUgUi1iYXNlIHNlIGNvbnN0cnV5ZXJvbiBwZW5zYW5kbyBlbiBsb3MgZXN0YWTDrXN0aWNvcyBkZSBoYWNlIDIwIGHDsW9zIChob3kgc2UgbGxhbWFyw61hbiBhbmFsaXN0YXMgZGUgZGF0b3MpLiBNb2RpZmljYXIgbGFzIGZ1bmNpb25lcyBkZSBSLWJhc2UgaGFyw61hIHF1ZSBjw7NkaWdvIGFudGlndW8gZGVqYXNlIGRlIGZ1bmNpb25hciwgYXPDrSBxdWUgbGEgbWF5b3LDrWEgZGUgYXZhbmNlcyB5IG1lam9yYXMgc2UgcHJvZHVjZW4gZW4gbG9zIHBhY2thZ2VzLiAKCkxhcyBmdW5jaW9uZXMgZGUgYHJlYWRyYCB0cmF0YW4gZGUgc2VyIGxvIG1hcyBwYXJlY2lkYXMgYSBsYXMgZnVuY2lvbmVzIGVxdWl2YWxlbnRlcyBkZSBSLWJhc2UgcGVybyBlbiBjaWVydG8gc2VudGlkbyBtZWpvcsOhbmRvbGFzIHkgaGFjacOpbmRvbGFzIG3DoXMgY29uc2lzdGVudGVzOyBwb3IgZWplbXBsbyBwYXJhIGxlZXIgZGF0b3MgQ1NWIGxhIGZ1bmNpw7NuIGRlIFItYmFzZSBlcyBgcmVhZC5jc3YoKWA7IG1pZW50YXMgcXVlIGxhIGZ1bmNpw7NuIGVxdWl2YWxlbnRlIGRlICJyZWFkciIgZXMgYHJlYWRfY3N2KClgLiBMYXMgZG9zIGhhY2VuIGxvIG1pc21vLCBsZWVyIGRhdG9zIGVuIGZvcm1hdG8gQ1NWLCBwZXJvIGxhcyBudWV2YXMgZnVuY2lvbmVzIHRpZW5lbiBhbGd1bmFzIHZlbnRhamFzOiAgCgogIC0gU29uIG3DoXMgcsOhcGlkYXMuCiAgCiAgIC0gRW5jYWphbiBtw6FzIGVuIGVsIHdvcmtmbG93L3BhcmFkaWdtYSBkZSBsYSBpbnZlc3RpZ2FjacOzbiByZXByb2R1Y2libGUuIFBvciBlamVtcGxvLCBhbGd1bmFzIGRlIGxhcyBmdW5jaW9uZXMgZGUgUi1iYXNlIGhlcmVkYW4gYWxndW5hcyBvcGNpb25lcyBkZWwgc2lzdGVtYSBvcGVyYXRpdm8geSBsYXMgdmFyaWFibGVzIGRlIGVudG9ybm8sIGhhY2llbmRvIHBvc2libGUgcXVlIHVuIHNjcmlwdCBxdWUgZnVuY2lvbmEgZW4gdW4gb3JkZW5hZG9yIG5vIGZ1bmNpb25lIGVuIG90cm8uIChFc3RvIGHDum4gcHVlZGUgcGFzYXJub3MgYSBub3NvdHJvcyBlbiBlbCBjdXJzby4gRXNwZXJlbW9zIHF1ZSBubyEhKS4KCiAgLSBFbiBsdWdhciBkZSBnZW5lcmFyIGRhdGEuZnJhbWVzLCBwcm9kdWNlbiB0aWJibGVzLiBMYXMgdGliYmxlcyBzb24gZW4gcmVhbGlkYWQgZGF0YS5mcmFtZXMgcGVybyBjb24gYWxndW5hcyBwYXJ0aWN1bGFyaWRhZGVzLiAKICAKICAtIExhcyB0aWJibGVzIG8gImRhdGEgZnJhbWVzIHR1bmVhZG9zIiB0aWVuZW4gdW5hcyBjaWVydGFzIHZlbnRhamFzOiBubyBjb252aWVydGVuIHBvciBkZWZlY3RvIHZlY3RvcmVzIGRlIHRleHRvIGVuIGZhY3RvcmVzLCBubyB1c2FuIHJvdyBuYW1lcywgbmkgdHJhbnNmb3JtYW4gbG9zIGNvbHVtbiBuYW1lcyAoZXN0w6FzIDMgY29zYXMgcXVlIHPDrSBoYWNlbiBsb3MgImRhdGEuZnJhbWVzIHRyYWRpY2lvbmFsZXMiIHB1ZWRlbiBwcm92b2NhciBhbGd1bmFzIGNvbXBsaWNhY2lvbmVzLCBhc8OtIHF1ZSBtZWpvciB0ZW5lciBoZXJyYW1pZW50YXMgcXVlIGxhcyBzb3J0ZWVuKS4KCgoKPGJyPgoKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMjIyBEYXRvcyBwcmVjYXJnYWRvcyBlbiBSIFtPUENJT05BTF0KClItYmFzZSB2aWVuZSBjb24gbXVjaG9zIGRhdG9zIHByZWNhcmdhZG9zOyBjb25jcmV0YW1lbnRlIGVuIGVsIHBrZyBkZSBSLWJhc2UgbGxhbWFkbyBgZGF0YXNldHNgLiBBZGVtw6FzIG11Y2hvcyBwYWNrYWdlcyBjb250aWVuZW4gdGFtYmnDqW4gY29uanVudG9zIGRlIGRhdG9zLiBQYXJhIHZlciBsb3MgZGF0b3MgcXVlIHRlbmVtb3MgcHJlY2FyZ2Fkb3MgeSBkaXNwb25pYmxlcyBlbiBSIHNlIHVzYSBsYSBmdW5jacOzbiBgZGF0YSgpYDoKCmBgYHtyLCBlY2hvID0gVCwgZXZhbCA9IEZ9CiMtIHNlIGFicmlyw6EgdW5hIHZlbnRhbmEgY29uIGVsIGxpc3RhZG8gZGUgZGF0b3MgZGlzcG9uaWJsZXMKZGF0YSgpICAKIyEhIGd1YXJkYW1vcyBlbCBsaXN0YWRvIGRlIGRhdG9zIGVuIHVuIGRhdGEuZnJhbWUgbGxhbWFkbyAiYWEiCmFhIDwtIGFzLmRhdGEuZnJhbWUoZGF0YSgpW1szXV0pIApgYGAKCjxicj4KClNpIHF1ZXJlbW9zIHZlciBsb3MgZGF0b3MgcXVlIGhheSBlbiB1biBwYWNrYWdlIGNvbmNyZXRvIHVzYXJlbW9zIGBkYXRhKHBhY2thZ2UgPSAicGtnX25hbWUiKWAKCmBgYHtyLCBlY2hvID0gVCwgZXZhbCA9IEZ9CiMtIHZlbW9zIGVuIHVuYSB2ZW50YW5hIGVsIGxpc3RhZG8gZGUgZGF0b3MgZGlzcG9uaWJsZXMgZW4gZWwgcGtnIGdncGxvdDIKZGF0YShwYWNrYWdlID0gImdncGxvdDIiKQojISEgZ3VhcmRhbW9zIGVsIGxpc3RhZG8gZGUgZGF0b3MgZGVsIHBrZyBnZ3Bsb3QyIGVuIGVsIGRmICJhYSIKYWEgPC0gYXMuZGF0YS5mcmFtZShkYXRhKHBhY2thZ2UgPSAiZ2dwbG90MiIpW1szXV0pICU+JSBzZWxlY3QoLTIpCiMhISBndWFyZGFtb3MgZWwgbGlzdGFkbyBkZSBkYXRvcyBkZWwgcGtnIGdncGxvdDIgZW4gdW5hIHRpYmJsZQphYSA8LSBhc190aWJibGUoZGF0YShwYWNrYWdlID0gImdncGxvdDIiKVtbM11dKSAlPiUgc2VsZWN0KC0yKSAKYGBgCgoKPGJyPgoKUG9yIGVqZW1wbG8sIGVsIHBhY2thZ2UgYGdncGxvdDJgIHRpZW5lIGxvcyBzaWd1aWVudGVzIGNvbmp1bnRvcyBkZSBkYXRvczoKCgpgYGB7ciwgZWNobyA9IEYsIGV2YWwgPSBUfQojLSBndWFyZGFtb3MgZWwgbGlzdGFkbyBkZSBkYXRvcyBkZWwgcGtnIGdncGxvdDIgZW4gdW5hIHRpYmJsZQpsaWJyYXJ5KHRpZHl2ZXJzZSkKYWEgPC0gYXNfdGliYmxlKGRhdGEocGFja2FnZSA9ICJnZ3Bsb3QyIilbWzNdXSkgJT4lIHNlbGVjdCgtMikgCiMtIG1vc3RyYW1vcyBhYSBjb21vIHRhYmxhCmtuaXRyOjprYWJsZShhYSkKYGBgCgo8YnI+CgpQb2RlbW9zIHZlciB0b2RvcyBsb3MgZGF0YXNldHMgcXVlIGhheSBlbiBsb3MgcGFja2FnZXMgcXVlIHRlbmVtb3MgZW4gbnVlc3RyYSBsaWJyZXLDrWEgZGUgcGFja2FnZXMgZGUgbnVlc3RybyBvcmRlbmFkb3I6CgpgYGB7ciwgZWNobyA9IFRSVUUsIGV2YWwgPSBGQUxTRX0KIyAhISBhYnJlIHVuYSB2ZW50YW5hIGRvbmRlIHNlIHZlIGVsIGxpc3RhZG8gZGUgdG9kb3MgbG9zIGRhdGFzZXRzIHF1ZSBjb250aWVuZW4gbG9zIHBhY2thZ2VzIGRlIG51ZXN0cmEgbGlicmVyw61hCmRhdGEocGFja2FnZSA9IC5wYWNrYWdlcyhhbGwuYXZhaWxhYmxlID0gVFJVRSkpCmBgYAoKPGJyPgoKLS0tLS0tLS0tLS0tLS0tLS0tLQoKPGJyPgoKCiMgMi4gVGlwb3MgZGUgZGF0b3MgcXVlIHZlcmVtb3MKCjxicj4KCkludHJvZHVjaXJlbW9zIGZ1bmNpb25lcyBwYXJhIGltcG9ydGFyL2V4cG9ydGFyIGRhdG9zIGRlIGxvcyBzaWd1aWVudGVzIGZvcm1hdG9zOgoKPGJyPgoKLSBEYXRvcyBlbiBmb3JtYXRvIHRleHRvIChvIHRhYnVsYXJlcykKCiAgICAtIENTVjogYC5jc3ZgIChjb21tYSBzZXBhcmF0ZWQgdmFsdWVzIG8gLCBlbiBjYXN0ZWxsYW5vLCBkYXRvcyBzZXBhcmFkb3MgcG9yIGNvbWFzKQogICAgLSBvdHJvcyBkYXRvcyBlbiBmb3JtYXRvIHRleHRvCiAgICAKPGJyPgoKLSAgRm9ybWF0b3MgZGUgb3Ryb3MgcHJvZ3JhbWFzIChzb2Z0d2FyZSBwcm9waWV0YXJpbykKCiAgICAtIEVYQ0VMOiBgLnhsc2AgeSBgLnhsc3hgCiAgICAtIFNQU1M6ICAgYC5zYXZgIHkgYC5wb3JgCiAgICAtIFNUQVRBOiBgLmR0YWAKICAgIC0gU0FTOiAgYC5zYXNgCgo8YnI+CgotIEZvcm1hdG9zIHByb3Bpb3MgZGUgUgoKICAgIC0gUiBvYmplY3RzOiBgLlJEYXRhYCBvIGAucmRhYAogICAgLSBTZXJpYWxpemVkIFIgb2JqZWN0czogYC5yZHNgCgo8YnI+CgotIE90cm9zIEZvcm1hdG9zCgogICAgLSBKU09OCiAgICAtIFhNTAogIAo8YnI+CiAgCkFkZW3DoXMgYXByZW5kZXJlbW9zIGNvbW8gYmFqYXIgZGF0b3MgYSB0cmF2w6lzIGRlIEFQSXM6CgogIC0gRXVyb3N0YXQKICAtIElORQogIC0gQmFuY28gTXVuZGlhbAoKCgo8YnI+CgotLS0tLS0tLS0tLS0tLS0tCgoKIyMjIyBFc3RyYXRlZ2lhIHF1ZSBzZWd1aXJlbW9zIHBhcmEgYXByZW5kZXIgYSBJbXBvcnRhci9FeHBvcnRhciBkYXRvcwoKTG8gcXVlIHZhbW9zIGEgIGhhY2VyIGVuIGVzdGUgdHV0b3JpYWwgcGFyYSBhcHJlbmRlciBhIGltcG9ydGFyICh5IGV4cG9ydGFyKSBkYXRvcyBlbiBSIGVzIGVsZWdpciB1biBmaWNoZXJvIGRlIGRhdG9zIHByZWNhcmdhZG8gZW4gUiB5IGV4cG9ydGFybG8gYSB1biBkZXRlcm1pbmFkbyBmb3JtYXRvIHBhcmEgbHVlZ28gaW1wb3J0YXIgZWwgYXJjaGl2byBnZW5lcmFkbyBvIGV4cG9ydGFkby4gUmVwZXRpcmVtb3MgZXN0byBwYXJhIGRpc3RpbnRvcyBmb3JtYXRvcyBkZSBkYXRvcy4gCgpEYSBpZ3VhbCBxdWUgYXJjaGl2byBkZSBkYXRvcyB1c2FyLCBhc8OtIHF1ZSB1dGlsaXphcmVtb3MgdW4gY29uanVudG8gZGUgZGF0b3MgZmFtb3NvIHkgcXVlIHBlc2EgcG9jbzogImVsIFtgaXJpc2BdKGh0dHBzOi8vZXMud2lraXBlZGlhLm9yZy93aWtpL0lyaXNfZmxvcl9jb25qdW50b19kZV9kYXRvcykgZGF0YXNldCIgcXVlIGZ1ZSB1dGlsaXphZG8gcG9yIFJvbmFsZCBGaXNoZXIuIElyaXMgY29udGllbmUgMTUwIG9ic2VydmFjaW9uZXMgZGUgNSB2YXJpYWJsZXM6IG1lZGljaW9uZXMgZGUgNSBjYXJhY3RlcsOtc3RpY2FzIHNvYnJlIDE1MCBmbG9yZXMgZGUgbGEgZXNwZWNpZSBJcmlzLgoKCjxicj4KCi0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyMjIMK/Q8OzbW8gcG9kZW1vcyB2ZXIgcXVlIHZhcmlhYmxlcyAoeSBkZSBxdWUgdGlwbykgaGF5IGVuIHVuIGRmPwoKU3Vwb25nYW1vcyBxdWUgeWEgaGVtb3MgY2FyZ2FkbyB1biBjb25qdW50byBkZSBkYXRvcyB5IHF1ZSBlc3TDoSBhbG1hY2VuYWRvIGVuIHVuIGRmLCDCv2PDs21vIHBvZGVtb3MgdmVyIHF1ZSB2YXJpYWJsZXMgKHkgZGUgcXVlIHRpcG8pIGhheSBlbiBlbCBkZj8KClZhbW9zIGEgdmVyIGxvcyBub21icmVzIGRlIGxhcyB2YXJpYWJsZXMgKGNvbHVtbmFzKSBkZWwgZGF0YXNldCBpcmlzOgoKYGBge3IsIGVjaG8gPSBUUlVFLCBldmFsID0gVFJVRX0KIyBuYW1lcygpIG11ZXN0cmEgbG9zIG5vbWJyZXMgZGUgbGFzIHZhcmlhYmxlcyBkZSB1biBkYXRhZnJhbWUKbmFtZXMoaXJpcykKYGBgCk5vIG5vcyBoYWNlIGZhbHRhLCBwZXJvIHZlYW1vcyBsb3MgcHJpbWVyb3MgdmFsb3JlcyBkZSBpcmlzOgoKYGBge3IsIGVjaG8gPSBUUlVFLCBldmFsID0gVFJVRX0KIyBoZWFkKCkgbXVlc3RyYSBsYXMgbiAobmUgZXN0ZSBjYXNvIDQpIHByaW1lcmFzIGZpbGFzIGRlIHVuIGRhdGFmcmFtZQpoZWFkKGlyaXMsIG4gPSA0KQpgYGAKCkxhIGZ1bmNpw7NuIGBzdW1tYXJ5KClgLCBub3MgaGFjZSB1biByZXN1bWVuICghKSBkZWwgZGYKCmBgYHtyLCBlY2hvID0gVFJVRSwgZXZhbCA9IFRSVUV9CiMgRsOtamF0ZSBxdWUgbGEgdmFyaWFibGUgIlNwZWNpZXMiIG5vIHRpZW5lIG1lZGlhLCBuaSBtw61uaW1vLCBuaSBtYXguIC4uLiBlcyBwb3JxdWUgZXMgdW4gZmFjdG9yCnN1bW1hcnkoaXJpcykKYGBgCgoKCgo8YnI+CgoqKlNJRU1QUkUtU0lFTVBSRSoqIGhheSBxdWUgY2hlcXVlYXIgZGUgcXVlIGNsYXNlIHNvbiBsYXMgdmFyaWFibGVzIHF1ZSBjb250aWVuZSBlbCBkZi4KCgpgYGB7ciwgZWNobyA9IFRSVUUsIGV2YWwgPSBUUlVFfQojLSB2ZXIgbGEgZXN0cnVjdHVyYSBkZWwgZGYuIFZpc3VhbGl6YXJlbW9zIGxvcyBub21icmVzIHkgZWwgdGlwbyBkZSBsYXMgdmFyaWFibGVzCnN0cihpcmlzKQpgYGAKClRhbWJpw6luIHBvZMOpaXMgdXNhciBsYSBmdW5jacOzbiBgc2tpbSgpYCBkZWwgcGFja2FnZSAgYHNraW1yYC4KCgpgYGB7ciwgZWNobyA9IFRSVUUsIGV2YWwgPSBGQUxTRX0KI2RldnRvb2xzOjppbnN0YWxsX2dpdGh1Yigicm9wZW5zY2lsYWJzL3NraW1yIikKbGlicmFyeShza2ltcikKc2tpbShpcmlzKQpgYGAKCgpgYGB7ciAsIGVjaG8gPSBGQUxTRSwgZXZhbCA9IFRSVUUsIGZpZy5hc3AgPSA0LzIsIG91dC53aWR0aCA9ICI4MCUiLCBmaWcuYWxpZ24gPSAibGVmdCJ9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltYWdlbmVzIiwgInR0XzA0X2ltZ18wM19vdXRwdXQtc2tpbXIucG5nIikpCmBgYAoKCgoKPGJyPgoKKipWYW1vcyBZQSBhIGV4cG9ydGFyIChlIGltcG9ydGFyKSAiaXJpcyIgYSBkaWZlcmVudGVzIGZvcm1hdG9zLiBFbXBlemFtb3MhISoqCgo8YnI+CgotLS0tLS0tLS0tLS0tLS0tLS0tLQoKPGJyPgoKCiMgMy4gRGF0b3MgdGFidWxhcmVzIChvIGRlIHRleHRvKQoKPGJyPgoKRXN0YW1vcyBhY29zdHVtYnJhZG9zIGEgdmlzdWFsaXphciBkYXRvcyBlbiAqKmZvcm1hdG8gdGFidWxhcioqOyBlcyAgZGVjaXIsIGNvbW8gdW5hIHRhYmxhLiBHZW5lcmFsbWVudGUgKipsYXMgY29sdW1uYXMgc29uIHZhcmlhYmxlcyB5IGxhcyBmaWxhcyBzb24gb2JzZXJ2YWNpb25lcyoqIGRlIGVzYXMgdmFyaWFibGVzIHBhcmEgZGlmZXJlbnRlcyB1bmlkYWRlcyBkZSBhbsOhbGlzaXMgKCJpbmRpdmlkdW9zIikuIAoKTGFzIGNvbHVtbmFzICoqc2Ugc2VwYXJhbiBjb24gdW4gY2Fyw6FjdGVyKiogKGdlbmVyYWxtZW50ZSBsYSBjb21hKSB5IGxhcyBmaWxhcyBjb24gdW4gc2FsdG8gZGUgbGluZWEuCgpQb2RlbW9zIHBlbnNhciBxdWUgZGVwZW5kaWVuZG8gZGUgY29tbyBzZSBzZXBhcmVuIGxhcyBvYnNlcnZhY2lvbmVzIHRlbmVtb3MgZGlzdGludG9zIHRpcG9zIGRlIGRhdG9zIHRhYnVsYXJlcywgcGVybyBlbiByZWFsaWRhZCBzdSBlc3RydWN0dXJhIGVzIHNpbWlsYXI6IHZhcmlhYmxlcyBlbiBjb2x1bW5hcyB5IGxhcyBvYnNlcnZhY2lvbmVzIGRlIHVuIGluZGl2aWR1byBzZXBhcmFkYXMgcG9yIHVuYSBtYXJjYSBvIGNhcsOhY3Rlci4gRXN0ZSBjYXLDoWN0ZXIgcHVlZGUgc2VyIHVuIGVzcGFjaW8sIHVuIHRhYnVsYWRvciwgdW5hIGNvbWEsIHB1bnRvIHkgY29tYSBldGMuLi4gRWwgZm9ybWF0byB0YWJ1bGFyIG1hcyBleHRlbmRpZG8gZXMgZWwgKipDU1YqKiwgZG9uZGUgbGFzIG9ic2VydmFjaW9uZXMgZXN0w6FuIHNlcGFyYWRhcyBwb3IgY29tYXMuIAoKCkVzdG9zIGRhdG9zIHNlIHB1ZWRlbiB2aXN1YWxpemFyIGVuIGxvcyBlZGl0b3JlcyBkZSB0ZXh0byB5IHBvciBlc28gdGFtYmnDqW4gc2UgbGxhbWFuIGRhdG9zIGVuIGZvcm1hdG8gdGV4dG8uCgpQb2RlbW9zIHBlbnNhciBxdWUgaGF5IDIgZ3J1cG9zIGRlIGRhdG9zIHRhYnVsYXJlczoKCiAgLSBkZWxpbWl0YWRvcyBwb3IgY2FyYWN0ZXJlcwogIC0gZGUgYW5jaHVyYSBmaWphICAKCkVsIHBhY2thZ2UgYHJlYWRyYCBsZWUgZGF0b3MgdGFidWxhcmVzIGNvbiBsYXMgc2lndWllbnRlcyBmdW5jaW9uZXM6CgotIHNpIGxvcyBkYXRvcyBlc3TDoW4gZGVsaW1pdGFkb3MgcG9yIGNhcmFjdGVyZXMgdXRpbGl6YTogYHJlYWRfZGVsaW0oKWAsIGByZWFkX2NzdigpYCwgYHJlYWRfdHN2KClgIC4uLgotIHNpIGxvcyBkYXRvcyBzb24gZGUgYW5jaHVyYSBmaWphOiBgcmVhZF9md2YoKWAgeSBgcmVhZF90YWJsZSgpYAoKU8OzbG8gdmVyZW1vcyBjb21vIGltcG9ydGFyL2V4cG9ydGFyIGRhdG9zIHRhYnVsYXJlcyBkZWwgcHJpbWVyIHRpcG87IGVzIGRlY2lyLCBzZXBhcmFkb3MgcG9yIGNhcmFjdGVyZXMuIENvbWVuemFyZW1vcyBjb24gZWwgZm9ybWF0byBDU1YgcXVlIGVzIGVsIG3DoXMgdXRpbGl6YWRvLgoKPGJyPgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyBDU1YKCkNTViBzaWduaWZpY2EgImNvbW1hIHNlcGFyYXRlZCBkYXRhIi4gRW4gcmVhbGlkYWQgQ1NWIGVzIHVuIGNhc28gcGFydGljdWxhciBkZSAidGFidWxhciBvIHRleHQgZGF0YSIKClJlY29yZGFkIHF1ZSB0ZW5lbW9zIHF1ZSBleHBvcnRhciBlbCBkYXRhZnJhbWUgYGlyaXNgIGEgZm9ybWF0byBDU1YgeSBsdWVnbyBpbXBvcnRhcmxvLgoKUGFyYSBleHBvcnRhciBgaXJpc2AgYSB1biBmaWNoZXJvIGVuIGZvcm1hdG8gYENTVmAgdXRpbGl6YXJlbW9zIGxhIGZ1bmNpw7NuIGB3cml0ZV9jc3YoKWA6IHNvbG8gaGF5IHF1ZSBkZWNpcmxlIGVsIG9iamV0byBxdWUgcXVlcmVtb3MgZXhwb3J0YXIgKGVuIGVzdGUgY2FzbyB1biBkZiAiaXJpcyIpIHkgZWwgbm9tYnJlIChqdW50byBjb24gbGEgcnV0YSkgZGVsIGFyY2hpdm8gZG9uZGUgcXVlcmVtb3MgZ3VhcmRhcmxvLgoKUG9kZW1vcyBlc3BlY2lmaWNhciBsYSBydXRhIGNvbXBsZXRhLiBQb3IgZWplbXBsbzoKCmBgYHtyLCBlY2hvID0gVCwgZXZhbCA9IEZ9CiMtIGV4cG9ydGEgZW4gZm9ybWF0byBDU1YgZWwgZGYgaXJpcyBhbCBmaWNoZXJvICJpcmlzLmNzdiIgCiMtIEN1aWRhZG8hISBlcyB1bmEgcnV0YSBhYnNvbHV0YS4gTm8gZnVuY2lvbmFyw6EgZW4gdG9kb3MgbG9zIG9yZGVuYWRvcmVzCndyaXRlX2NzdihpcmlzLCBwYXRoID0gIkM6L1VzZXJzL3BlcmV6cC9EZXNrdG9wL2lyaXMuY3N2IikKYGBgCgo8YnI+CgpFbiByZWFsaWRhZCBubyBoYWNlIGZhbHRhIGVzcGVjaWZpY2FyIGxhIHJ1dGEgY29tcGxldGEuIFNpIHNvbG8gZXNwZWNpZmljYW1vcyBlbCBub21icmUgZGVsIGFyY2hpdm8sIFIgbG8gZ3VhcmRhcsOhIGVuIGVsIGRpcmVjdG9yaW8gZGUgdHJhYmFqby4gCgpgYGB7ciwgZWNobyA9IFQsIGV2YWwgPSBGfQojLSBleHBvcnRhIGVuIGZvcm1hdG8gQ1NWIGVsIGRmIGlyaXMgYWwgZmljaGVybyAiaXJpcy5jc3YiLiBDb21vIG5vIHNlIGVzcGVjaWZpY2EgbGEgcnV0YSwgc2UgZ3JhYmFyw6EgZW4gZWwgZGlyZWN0b3JpbyBkZSB0cmFiYWpvIAp3cml0ZV9jc3YoaXJpcywgcGF0aCA9ICJpcmlzLmNzdiIpCmBgYAoKClJlY3VlcmRhIHF1ZSBwYXJhIHNhYmVyIGN1YWwgZXMgdHUgZGlyZWN0b3JpbyBkZSB0cmFiYWpvIHB1ZWRlcyB1c2FyIGxhIGZ1bmNpw7NuIGBnZXR3ZCgpYCB5IHB1ZWRlcyBjYW1iaWFybG8gZGVzZGUgbG9zIG1lbsO6cyBkZSBSU3R1ZGlvIG8gY29uIGBzZXR3ZCgpYC4gUG9yIGVqZW1wbG86CgpgYGB7ciwgZWNobyA9IFRSVUUsIGV2YWwgPSBGQUxTRX0KIy0gYWxtYWNlbmFtb3MgZW4gZWwgb2JqZXRvICJwYXRoX3dkIiBsYSBydXRhIGRlbCBkaXJlY3RvcmlvIGRlIHRyYWJham8gZGVsIG9yZGVuYWRvciBxdWUgZXN0w6FzIHVzYW5kbwpwYXRoX2FfbWlfd2QgPC0gZ2V0d2QoKSAKCiMtIFBvZGVtb3MgZmlqYXIgIGVsIGRpcmVjdG9yaW8gZGUgdHJhYmFqbyBkb25kZSBxdWVyYW1vcy4gUG9yIGVqZW1wbG86CnNldHdkKCJDOi9Vc2Vycy9wZXJlenAvRGVza3RvcC9NaXNfZGF0b3MvIikgIAojLSBlbiB0dSBvcmRlbmFkb3Igbm8gZnVuY2lvbmFyw6EgcG9ycXVlIHR1IG9yZGVuYWRvciBubyB0aWVuZSBlc2EgcnV0YSBvIGVzdHJ1Y3R1cmEgZGUgY2FycGV0YXMKCiMtIGZpamFtb3MgZWwgZGlyZWN0b3JpbyBkZSB0cmFiYWpvIChhdW5xdWUgZW4gcmVhbGlkYWQgbm8gaGFjZSBmYWx0YSBwb3JxdWUgZXNhIHJ1dGEgYWxtYWNlbmFkYSBlbiAgInBhdGhfYV9taV93ZCIgeWEgZXJhIGVzZSBlbCBkaXJlY3RvcmlvIGRlIHRyYWJham8Kc2V0d2QocGF0aF9hX21pX3dkKSAgICAgIApgYGAKCgo8YnI+CgpSZWNvbWVuZGFtb3MgdHJhYmFqYXIgY29uIFJwcm9qZWN0cyB5IGd1YXJkYXIgbG9zIGZpY2hlcm9zIGRlIGRhdG9zIGVuIHVuYSBjYXJwZXRhIGxsYW1hZGEgYC9kYXRvcy9gLiAKCgpQb3IgbG8gdGFudG8sIHBhcmEgZXhwb3J0YXIgbG9zIGRhdG9zIGRlICJpcmlzIiBlbiBsYSBzdWJjYXJwZXRhIGAvZGF0b3MvcHJ1ZWJhcy9gIGRlbnRybyBkZWwgcHJveWVjdG8sIGhheSBxdWUgaGFjZXIgbG8gc2lndWllbnRlOgoKYGBge3IsIGVjaG8gPSBUUlVFLCBldmFsID0gRkFMU0V9CiMtIGV4cG9ydGEgZW4gZm9ybWF0byAuY3N2IGVsIGRmIGlyaXMgYWwgZmljaGVybyAiaXJpcy5jc3YiLiBTZSBndWFyZGFyw6EgZW4gbGEgc3ViY2FycGV0YSAiZGF0b3MvcHJ1ZWJhcy8iIGRlbCBwcm95ZWN0bwp3cml0ZV9jc3YoaXJpcywgIi4vZGF0b3MvcHJ1ZWJhcy9pcmlzLmNzdiIpCmBgYAoKU2kgcXVlcmVtb3MsIHBvZGVtb3MgcG9uZXIgZXhwbMOtY2l0YW1lbnRlIGxvcyBhcmd1bWVudG9zIChvIHBhcsOhbWV0cm9zKSBkZSBsYSBmdW5jacOzbiBgd3JpdGVfY3N2KClgOgoKCmBgYHtyLCBlY2hvID0gVFJVRSwgZXZhbCA9IEZBTFNFfQojLSBPdHJhIHZleiBleHBvcnRhbW9zIGVuIGZvcm1hdG8gLmNzdiBlbCBkZiBpcmlzLiBFc3RhIHZleiBleHBsaWNpdGFtb3MgbGFzIG9wY2lvbmVzIG8gcGFyw6FtZXRyb3MgZGUgbGEgZnVuY2nDs24Kd3JpdGVfY3N2KGlyaXMsIHBhdGggPSAiLi9kYXRvcy9wcnVlYmFzL2lyaXMuY3N2IiwgY29sX25hbWVzID0gVFJVRSkKYGBgCgoKCmBgYHtyLCBlY2hvID0gRkFMU0UsIGV2YWwgPSBUUlVFfQojLSBwYXJhIHF1ZSBzZSBwdWVkYSBjb3JyZXIgZW4gLlJtZCBlbiBvdHJhIGNhcnBldGEgcXVlIG5vIHNlIGxhIHJhaXoKIy0gT3RyYSB2ZXogZXhwb3J0YW1vcyBlbiBmb3JtYXRvIC5jc3YgZWwgZGYgaXJpcy4gRXN0YSB2ZXogZXhwbGljaXRhbW9zIGxhcyBvcGNpb25lcyBvIHBhcsOhbWV0cm9zIGRlIGxhIGZ1bmNpw7NuCndyaXRlX2NzdihpcmlzLCBwYXRoID0gaGVyZTo6aGVyZSgiLi9kYXRvcy9wcnVlYmFzL2lyaXMuY3N2IiksIGNvbF9uYW1lcyA9IFRSVUUpCmBgYAoKCgoKPGJyPgoKQmllbiwgeWEgaGVtb3MgZXhwb3J0YWRvICJpcmlzIiBhIHVuIGZpY2hlcm8gZW4gZm9ybWF0byBDU1YsIGFob3JhIHZhbW9zIGEgaW1wb3J0YXJsby4KCgo8YnI+CgpQYXJhIGltcG9ydGFyIGxvcyBkYXRvcyBkZWwgZmljaGVybyAiaXJpcy5jc3YiIGhhY2Vtb3MgbG8gc2lndWllbnRlOgoKCmBgYHtyLCBlY2hvID0gVFJVRSwgZXZhbCA9IEZBTFNFfQojLSBpbXBvcnRhbW9zIGxvcyBkYXRvcyBkZWwgZmljaGVybyAiaXJpcy5jc3YiIHkgbG9zIGd1YXJkYW1vcyBlbiB1biBvYmpldG8gcXVlIGxsYW1hbW9zICJpcmlzX2ltcF9jc3YiLiBSZWN1ZXJkYSBxdWUgYWNhYmFtb3MgZGUgZXhwb3J0YXIgImlyaXMiIGEgbGEgY2FycGV0YSAiL2RhdG9zL3BydWViYXMvIiBkZW50cm8gZGVsIFJwcm9qZWN0CmlyaXNfaW1wX2NzdiA8LSByZWFkX2NzdigiLi9kYXRvcy9wcnVlYmFzL2lyaXMuY3N2IikKYGBgCgoKPGJyPgoKQXPDrSBkZSBzZW5jaWxsbyEhIEFkZW3DoXMgbGEgbWF5b3LDrWEgZGUgcHJvZ3JhbWFzIHBlcm1pdGVuIGxlZXIgeSBleHBvcnRhciBkYXRvcyBlbiBDU1Y7IGFzw60gcXVlIHNpIHRyYWJhamFtb3MgY29uIG90cm8gc29mdHdhcmUgKEV4Y2VsLCBTUFNTIC4uLiksIHNpZW1wcmUgcG9kZW1vcyBwYXNhciBudWVzdHJvcyBkYXRvcyBhIFIgZXhwb3J0w6FuZG9sb3MgYSBDU1Y7IHkgZGVzZGUgUiBwb2RlbW9zIGhhY2VyIGxvIG1pc21vLiAKCjxicj4KCgojIyMjIEFsZ3VuYXMgb3BjaW9uZXMgZGUgYHJlYWRfY3N2KClgIHF1ZSBjb252aWVuZSBjb25vY2VyCgpBIHZlY2VzIGxvcyBkYXRvcyB0aWVuZW4gY2llcnRvcyBwcm9ibGVtYXMgcXVlIGhheSBxdWUgYXJyZWdsYXI7IHBvciBsbyBxdWUgY29udmllbmUgY29ub2NlciBhbGd1bmFzIG9wY2lvbmVzIGRlIGByZWFkX2NzdigpYDoKCi0gKipjb2xfbmFtZXM6KiogcmVhZF9jc3YoKSBhc3VtZSBxdWUgbGEgcHJpbWVyYSBmaWxhIGNvbnRpZW5lIGxvcyBub21icmVzIGRlIGxhcyB2YXJpYWJsZXMuIEVzdG8gcHVlZGUgY2FtYmlhcnNlIGNvbiBgY29sX25hbWVzID0gRkFMU0VgLiBQdWVkZXMgcHJvdmVlciBub21icmVzIGEgbGFzIHZhcmlhYmxlcyAobyBjb2x1bW5hcykgY29uIGBjb2xfbmFtZXMgPSBjKCJYMSIsICJYMiIpYAoKLSAqKnNraXA6KipyZWFkX2NzdigpIHBvciBkZWZlY3RvIGltcG9ydGEgdG9kYXMgbGFzIGZpbGFzIGRlbCBhcmNoaXZvLCBwZXJvIHB1ZWRlcyBoYWNlciBxdWUgY29taWVuY2UgYSBpbXBvcnRhciBlbiBsYSBmaWxhIHF1ZSBxdWllcmFzIGNvbiBgc2tpcCA9IG5gCgotICoqbmE6KiogRW4gYWxndW5vcyBmaWNoZXJvcyBjb24gZGF0b3MgdGFidWxhcmVzIGxvcyBOQXMgc2UgZXNwZWNpZmljYW4gY29uIGFsZ8O6biBjYXLDoWN0ZXIuIEVzdG8gcG9kZW1vcyB0cmF0YXJsbyBhbCBsZWVyIGxvcyBkYXRvcyBjb24gZWwgYXJndW1lbnRvICAgYG5hID0gInh4eCJgCgoKCgpQb3IgZWplbXBsbywgZWwgY2h1bmsgcXVlIHZlcyBhYmFqbyB1dGlsaXphIHJlYWRfY3N2KCkgcGFyYSBjYXJnYXIgZWwgZmljaGVybyAibXlfZmljaGVyby5jc3YiLiBDb21pZW56YSBhIGltcG9ydGFyIGRhdG9zIGRlc2RlIGxhIHF1aW50YSBjb2x1bW5hLCAgdHJhdGEgbG9zIHZhbG9yZXMgLTQ0IHkgJCBjb21vIE5BcyB5IHByb3ZlZSB1biB2ZWN0b3IgY29uIGxvcyBub21icmVzIHF1ZSBxdWVyZW1vcyBwYXJhIGxhcyB2YXJpYWJsZXMgKG8gY29sdW1uYXMpCgpgYGB7ciwgZWNobyA9IFRSVUUsIGV2YWwgPSBGQUxTRX0KbWlzX2RhdG9zIDwtIHJlYWRfY3N2KCJteV9maWNoZXJvLmNzdiIsIHNraXAgPSA1LCBuYSA9IGMoIi00NCIsICIkIiksIGNvbF9uYW1lcyA9IGMoIlgxIiwgIlgyIiwgIllZIiwgIlg0IiwgIlpaIikpCmBgYAoKCgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyBPdHJvcyBkYXRvcyB0YWJ1bGFyZXMgCgpFbiByZWFsaWRhZCwgdG9kb3MgbG9zIGRhdG9zIHRhYnVsYXJlcyAoKipzZXBhcmFkb3MgcG9yIGNhcmFjdGVyZXMqKikgc29uIG11eSBzaW1pbGFyZXMuIMKhU29sbyBzZSBkaWZlcmVuY2lhbiBlbiBlbCBjYXLDoWN0ZXIgcXVlIGhhY2UgZGUgc2VwYXJhZG9yLiAKCkVsIHBhY2thZ2UgInJlYWRyIiB0aWVuZSB1bmEgZnVuY2nDs24gZXNwZWNpZmljYSBwYXJhIGNhZGEgdGlwbyBkZSBkYXRvcyB0YWJ1bGFyZXMuIFBvciBlamVtcGxvLCBzaSBlbCBzZXBhcmFkb3IgZXMgdW4gcHVudG8geSBjb21hLCBsYSBmdW5jacOzbiBwYXJhIGltcG9ydGFyIGVzdG9zIGRhdG9zIGVzIGByZWFkX2NzdjIoKWA7IHNpIGVsIHNlcGFyYWRvciBlcyB1biB0YWJ1bGFkb3IsIGxhIGZ1bmNpw7NuIGVzIGByZWFkX3RzdigpYC4gUGVybyB0YW1iacOpbiB0aWVuZSB1bmEgZnVuY2nDs24gZ2Vuw6lyaWNhIHF1ZSBzaXJ2ZSBwYXJhIGN1YWxxdWllciB0aXBvIGRlIHNlcGFyYWRvcjogYHJlYWRfZGVsaW0oKWAgLiBPYnZpYW1lbnRlIHVzYXJlbW9zIGVzdGFzIGZ1bmNpb25lcy4KCgpQb3IgZWplbXBsbywgcG9kZW1vcyBjYXJnYXIgZWwgZmljaGVybyAibXlfaXJpc19leHBvcnRhZG8uY3N2IiBxdWUgaGVtb3MgZXhwb3J0YWRvIGFudGVyaW9ybWVudGUgdXRpbGl6YW5kbyBsYSBmdW5jacOzbiBnZW7DqXJpY2EgYHJlYWRfZGVsaW0oKWAsIHNvbG8gaGF5IHF1ZSBkZWNpcmxlIHF1ZSBlbCBzZXBhcmFkb3IgZXMgdW5hIGNvbWEuIFNlIGxvIGRlY2ltb3MgY29uIGxhIG9wY2nDs24gYGRlbGltID0gIiwiYC4gVmXDoW1vc2xvOgoKCmBgYHtyLCBlY2hvID0gVFJVRSwgZXZhbCA9IEZBTFNFfQojLSBpbXBvcnRhbW9zIGxvcyBkYXRvcyBkZWwgZmljaGVybyAiaXJpcy5jc3YiIHkgbG9zIGd1YXJkYW1vcyBlbiB1biBvYmpldG8gcXVlIGxsYW1hbW9zIGlyaXNfaW1wX2Nzdl8yLiBGw61qYXRlIGVuIGVsIGFyZ3VtZW50byAnZGVsaW0nCmlyaXNfaW1wX2Nzdl8yIDwtIHJlYWRfZGVsaW0oIi4vZGF0b3MvcHJ1ZWJhcy9pcmlzLmNzdiIsIGRlbGltID0gIiwiKQpgYGAKCkNvbW8gZWwgZm9ybWF0byB0YWJ1bGFyIG1hcyBleHRlbmRpZG8gZXMgZWwgQ1NWOyBlbiBnZW5lcmFsLCBubyB0ZW5kcmVtb3MgbmVjZXNpZGFkIGRlIGV4cG9ydGFyIGRhdG9zIHRhYnVsYXJlcyBzZXBhcmFkb3MgcG9yIGNhcmFjdGVyZXMgZGlzdGludG9zIGEgbGEgY29tYSwgcGVybyBzaSBxdWlzacOpcmFtb3MgaGFjZXJsbywgcG9kcsOtYW1vcyBoYWNlcmxvIGNvbiBgd3JpdGVfdHN2KClgIG8gY29uIGB3cml0ZV9kZWxpbSgpYDoKCgpgYGB7ciwgZWNobyA9IFRSVUUsIGV2YWwgPSBGQUxTRX0KIy0gZXhwb3J0YW1vcyBpcmlzIGVuIGZvcm1hdG8gdGFidWxhciBzZXBhcmFkbyBwb3IgcHVudG8geSBjb21hLiAKd3JpdGVfZGVsaW0oaXJpcywgIi4vZGF0b3MvcHJ1ZWJhcy9pcmlzXzIudHh0IiwgZGVsaW0gPSAiOyIpCiMtIGV4cG9ydGFtb3MgaXJpcyBlbiBmb3JtYXRvIHRhYnVsYXIgc2VwYXJhZG8gcG9yIHRhYnVsYWRvcmVzIAp3cml0ZV9kZWxpbShpcmlzLCAiLi9kYXRvcy9wcnVlYmFzL2lyaXNfMy50eHQiLCBkZWxpbSA9ICJcdCIpCiMtIGV4cG9ydGFtb3MgaXJpcyBlbiBmb3JtYXRvIHRhYnVsYXIgc2VwYXJhZG8gcG9yIHVuIGVzcGFjaW8gZW4gYmxhbmNvIAp3cml0ZV9kZWxpbShpcmlzLCAiLi9kYXRvcy9wcnVlYmFzL2lyaXNfNC50eHQiLCBkZWxpbSA9ICIgIikKYGBgCgo8YnI+CgpTaSBxdWlzacOpcmFtb3MgaW1wb3J0YXJsb3MsIHRlbmRyw61hbW9zIHF1ZSBoYWNlcjoKCgpgYGB7ciwgZWNobyA9IFRSVUUsIGV2YWwgPSBGQUxTRX0KIy0gZXhwb3J0YW1vcyBpcmlzIGVuIGZvcm1hdG8gdGFidWxhciBzZXBhcmFkbyBwb3IgcHVudG8geSBjb21hLiAKcmVhZF9kZWxpbSgiLi9kYXRvcy9wcnVlYmFzL2lyaXNfMi50eHQiLCBkZWxpbSA9ICI7IikKIy0gZXhwb3J0YW1vcyBpcmlzIGVuIGZvcm1hdG8gdGFidWxhciBzZXBhcmFkbyBwb3IgdGFidWxhZG9yZXMgCnJlYWRfZGVsaW0oIi4vZGF0b3MvcHJ1ZWJhcy9pcmlzXzMudHh0IiwgZGVsaW0gPSAiXHQiKQojLSBleHBvcnRhbW9zIGlyaXMgZW4gZm9ybWF0byB0YWJ1bGFyIHNlcGFyYWRvIHBvciB1biBlc3BhY2lvIGVuIGJsYW5jbyAKcmVhZF9kZWxpbSgiLi9kYXRvcy9wcnVlYmFzL2lyaXNfNC50eHQiLCBkZWxpbSA9ICIgIikKYGBgCgoKCjxicj4KPGJyPgoKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgo8YnI+CgojIDQuIEZvcm1hdG9zIHByb3BpZXRhcmlvcwoKSGFzdGEgcXVlIFIgaGFnYSBkZXNwYXJlY2VyIGEgU1BTUywgU3RhdGEsICAuLi4uLiBgciBlbW86OmppKCJiaWNlcHMiKWAsICBhw7puIHNlcsOhIG5lY2VzYXJpbyBpbXBvcnRhciBhbGfDum4gZmljaGVybyBlbiBmb3JtYXRvcyBkZSBzb2Z0d2FyZSBwcm9waWV0YXJpbyBjb21vIGxvcyBkZSBFeGNlbCwgU0FTLCBTdGF0YSAsIFNQU1MgeSBFdmlld3MKCgpOb3JtYWxtZW50ZSBsbyBxdWUgbmVjZXNpdGFyZW1vcyBlcyBpbXBvcnRhciBkYXRvcyBlbiBlc29zIGZvcm1hdG9zLCBzw7NsbyBhbGd1bmEgdmV6IHRlbmRyZW1vcyBxdWUgZXhwb3J0YXJsb3MsIHlhIHF1ZSB0b2RvcyBlc3RvcyBwcm9ncmFtYXMgcHVlZGVuIGxlZXIgZGF0b3MgZW4gQ1NWLgoKCkxvIHF1ZSBzw60gcHVlZGUgcmVzdWx0YXJub3Mgw7p0aWwgZXMgZXhwb3J0YXIgZGF0b3MgZW4gZm9ybWF0byAueGxzLCB5YSBxdWUgRXhjZWwgZXMgbXV5IMO6dGlsICBwYXJhIHZpc3VhbGl6YXIgZGF0b3MgZW4gZm9ybWF0byB0YWJ1bGFyIHkgdGFtYmnDqW4gcG9ycXVlIG11Y2hhIGdlbnRlIHByZWZpZXJlIChvIHNvbG8gc2FiZSkgbGVlci9hbmFsaXphciBkYXRvcyBlbiBFeGNlbC4KCgo8YnI+CgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCjxicj4KCiMjIEV4Y2VsCgojIyMjIEV4cG9ydGFyIGEgZXhjZWwKCkhheSB2YXJpb3MgcGFja2FnZXMgcXVlIGdyYWJhbiBkYXRvcyBlbiBmb3JtYXRvIC54bHMuIFBlcm8gZWwgbcOhcyBzZW5jaWxsbyBlcyBlbCBwYWNrYWdlIGB4bHN4YC4gVmXDoW1vc2xvOgoKYGBge3IsIGVjaG8gPSBULCBldmFsID0gRkFMU0V9CmxpYnJhcnkoeGxzeCkKd3JpdGUueGxzeChpcmlzLCAiLi9kYXRvcy9wcnVlYmFzL2lyaXMueGxzeCIpCmBgYAoKCkxhIGZ1bmNpw7NuIGB3cml0ZS54bHN4KClgIHBlcm1pdGUgZXNwZWNpZmljYXIgZWwgbm9tYnJlIGRlbCBsaWJybyB5IGFsZ3VuYSBjb3NhIG3DoXM7IHBvciBlamVtcGxvOgoKCmBgYHtyLCBlY2hvID0gVCwgZXZhbCA9IEZBTFNFfQojIGxpYnJhcnkoeGxzeCkKd3JpdGUueGxzeChpcmlzLCAiLi9kYXRvcy9wcnVlYmFzL2lyaXMueGxzeCIsIHNoZWV0TmFtZSA9ICJJUklTIiwgcm93Lm5hbWVzID0gRkFMU0UpCmBgYAoKTGEgZnVuY2nDs24gYHdyaXRlLnhsc3goKWAgcGVybWl0ZSBhw7FhZGlyIGRhdG9zIGEgdW4gYXJjaGl2byAueGxzeCBwcmVleGlzdGVudGU7IHBhcmEgZWxsbyB0ZW5lbW9zIHF1ZSB1c2FyIGxhIG9wY2nDs24gYGFwcGVuZCA9IFRSVUVgOgoKCmBgYHtyLCBlY2hvID0gVCwgZXZhbCA9IEZBTFNFfQojIGxpYnJhcnkoeGxzeCkKd3JpdGUueGxzeChpcmlzLCAiLi9kYXRvcy9wcnVlYmFzL2lyaXMueGxzeCIsIHNoZWV0TmFtZSA9ICJJUklTXzIiLCBhcHBlbmQgPSBUUlVFKQpgYGAKCjxicj4KCkVsIHBhcXVldGUgYHhsc3hgIGRlcGVuZGUgZGUgSmF2YS4gTm8gc3VlbGUgaGFiZXIgcHJvYmxlbWFzLCBwZXJvIHNpIGxvcyB0dXZpZXNlcywgZWwgcGFja2FnZSBgd3JpdGV4bGAgbm8gdGllbmUgbmluZ3VuYSBkZXBlbmRlbmNpYS4gVGVuZHLDrWFzIHF1ZSBoYWNlciBsbyBzaWd1aWVudGU6CgoKYGBge3IsIGVjaG8gPSBUUlVFLCBtZXNzYWdlID0gRkFMU0UsIGV2YWwgPSBGQUxTRX0KbGlicmFyeSh3cml0ZXhsKQp3cml0ZV94bHN4KGlyaXMsICIuL2RhdG9zL3BydWViYXMvaXJpczgueGxzeCIpCmBgYAoKPGJyPgoKCiMjIyMgSW1wb3J0YXIgYXJjaGl2b3MgZW4gZm9ybWF0byBleGNlbAoKVGFtYmnDqW4gaGF5IHZhcmlvcyBwYXF1ZXRlcywgcGVybyB1c2FyZW1vcyBlbCBtaXNtbyBxdWUgdXNhIFJTdHVkaW8gZW4gc3VzIG1lbsO6czogYHJlYWR4bGAKCgoqKmByZWFkeGxgKiogcGVybWl0ZSBsZWVyIGZpY2hlcm9zIGAueGxzYCB5IGAueGxzeGAuIFBvciBlamVtcGxvOgoKCmBgYHtyLCBlY2hvID0gVCwgZXZhbCA9IEZBTFNFfQojIGxpYnJhcnkocmVhZHhsKQppcmlzX2ltcF94bHMgPC0gcmVhZF9leGNlbCgiLi9kYXRvcy9wcnVlYmFzL2lyaXMueGxzeCIpCmBgYAoKPGJyPgoKUG9kZW1vcyBlc3BlY2lmaWNhciBlbCBsaWJybyBxdWUgcXVlcmVtb3MgYWJyaXIsIHlhIHNlYSBlc3BlY2lmaWNhbmRvIHN1IG5vbWJyZSBvIHN1IHBvc2ljacOzbiBlbiBlbCBmaWNoZXJvCgoKYGBge3IsIGVjaG8gPSBULCBldmFsID0gRkFMU0V9CiMgbGlicmFyeShyZWFkeGwpCmlyaXNfaW1wX3hscyA8LSByZWFkX2V4Y2VsKCIuL2RhdG9zL3BydWViYXMvaXJpcy54bHN4Iiwgc2hlZXQgPSAyKQppcmlzX2ltcF94bHMgPC0gcmVhZF9leGNlbCgiLi9kYXRvcy9wcnVlYmFzL2lyaXMueGxzeCIsIHNoZWV0ID0gIklSSVNfMiIpCmBgYAoKCkxhIGZ1bmNpw7NuIGByZWFkX2V4Y2VsKClgIHRpZW5lIG3DoXMgcG9zaWJpbGlkYWRlczsgY29tbyBlamVtcGxvLCBsYSBvcGNpw7NuIGBza2lwID0gNGAgcGVybWl0ZSBlbXBlemFyIGEgaW1wb3J0YXIgYSBwYXJ0aXIgZGUgbGEgY3VhcnRhIGZpbGEuCgo8YnI+CgpTaSBxdWVyZW1vcyBpbXBvcnRhciB0b2RvcyBsb3MgbGlicm9zIChvIHNoZWV0cykgZGUgdW4gYXJjaGl2byBFeGNlbCwgcG9kZW1vcyBoYWNlcmxvIGFzw606CgoKYGBge3IsIGVjaG8gPSBULCBldmFsID0gRkFMU0V9CiMgbGlicmFyeShyZWFkeGwpCiMgKCEhISkKSVJJU19saXN0IDwtIGxhcHBseShleGNlbF9zaGVldHMoIi4vZGF0b3MvcHJ1ZWJhcy9pcmlzLnhsc3giKSwgcmVhZF9leGNlbCwgcGF0aCA9ICIuL2RhdG9zL3BydWViYXMvaXJpcy54bHN4IikKYGBgCgpIZW1vcyBndWFyZGFkbyBsb3MgMiBzaGVldHMgZGVsIGFyY2hpdm8gIi4vZGF0b3MvcHJ1ZWJhcy9pcmlzLnhsc3giIGVuIHVuIG9iamV0byBSIGxsYW1hZG8gYElSSVNfbGlzdGAuIEVzdGUgb2JqZXRvIGVzIHVuYSBsaXN0YSBjb24gMiBlbGVtZW50b3MuIGNhZGEgZWxlbWVudG8gY29udGllbmUgbG9zIGRhdG9zIGRlIGNhZGEgdW5vIGRlIGxvcyAyIHNoZWV0cy4gUG9kZW1vcyB2ZXJsbyBjb24gYHN0cigpYDoKCgpgYGB7ciwgZWNobyA9IFQsIGV2YWwgPSBGQUxTRX0KIyAoISEhKQpzdHIoSVJJU19saXN0KQpgYGAKClNpIHF1aXNpw6lyYW1vcyByZWN1cGVyYXIgbG9zIGRhdG9zIGVuIGVsIGZvcm1hdG8gZW4gZWwgcXVlIGVzdGFtb3MgaGFiaXR1YWRvcyAoZGF0YWZyYW1lcykgbG8gaGFyw61hbW9zIGFzw606CgoKCmBgYHtyLCBlY2hvID0gVCwgZXZhbCA9IEZBTFNFfQojICghISEpCnByaW1lcl9pcmlzICA8LSBJUklTX2xpc3RbWzFdXQpzZWd1bmRvX2lyaXMgPC0gSVJJU19saXN0W1syXV0KYGBgCgo8YnI+CgotLS0tLS0tLS0tLS0tLS0tLS0tLS0KCjxicj4KCiMjICBPdHJvcyBmb3JtYXRvcyBwcm9waWV0YXJpb3MKClZlcmVtb3MgU1BTUywgU3RhdGEgeSBTQVMuCgpVdGlsaXphcmVtb3MgZWwgbWlzbW8gcGFja2FnZSBxdWUgdXNhIFJTdHVkaW86IGBoYXZlbmAuIFNpIG5lY2VzaXTDoXNlbW9zIGltcG9ydGFyIG90cm8gdGlwbyBmb3JtYXRvIGVzIG11eSBwb3NpYmxlIHF1ZSBzZSBwdWVkYSBoYWNlciBjb24gbG9zIGxvcyBwYWNrYWdlcyBgZm9yZWlnbmAgeSBgcmlvYAoKPGJyPgoKLS0tLS0tLS0tLS0tLS0tLQoKIyMjIFNQU1MKCgojIyMjIEV4cG9ydGFjacOzbiBhIFNQU1MgKGZvcm1hdG8gYC5zYXZgKQoKCmBgYHtyLCBlY2hvID0gVCwgZXZhbCA9IEZBTFNFfQojIGxpYnJhcnkoaGF2ZW4pCndyaXRlX3NhdihpcmlzLCAiLi9kYXRvcy9wcnVlYmFzL2lyaXMuc2F2IikKYGBgCjxicj4KCiMjIyMgSW1wb3J0YWNpb24gZGUgZmljaGVyb3MgYC5zYXZgICh0YiBgLnBvcmApCgoKYGBge3IsIGVjaG8gPSBULCBldmFsID0gRkFMU0V9CiMgbGlicmFyeShoYXZlbikKaXJpc19pbXBfc3BzcyA8LSByZWFkX3Nwc3MoIi4vZGF0b3MvcHJ1ZWJhcy9pcmlzLnNhdiIpCmBgYAoKCjxicj4KCi0tLS0tLS0tLS0tLS0tLS0KCgojIyMgU3RhdGEKCgojIyMjIEV4cG9ydGFjacOzbiBhIFNUQVRBIChmb3JtYXRvIGAuZHRhYCkKClNlIHB1ZWRlIGV4cG9ydGFyIGNvbiBgaGF2ZW5gIChwZXJvIG5vIHBlcm1pdGUgbGFiZWxsZWQgZGF0YSEhKSwgYXPDrSBxdWUgbWVqb3IgaGFjZXJsbyBlc3RhIHZleiBjb24gZWwgcGFja2FnZSBgZm9yZWlnbmAKCmBgYHtyLCBlY2hvID0gVCwgZXZhbCA9IEZBTFNFfQojIGxpYnJhcnkoZm9yZWlnbikKd3JpdGUuZHRhKGlyaXMsICIuL2RhdG9zL3BydWViYXMvaXJpcy5kdGEiKQpgYGAKCgo8YnI+CgojIyMjIEltcG9ydGFjaW9uIGRlIGZpY2hlcm9zIFNUQVRBIChgLmR0YWApCgoKYGBge3IsIGVjaG8gPSBULCBldmFsID0gRkFMU0V9CiMgbGlicmFyeShoYXZlbikKaXJpc19pbXBfc3RhdGEgPC0gcmVhZF9zdGF0YSgiLi9kYXRvcy9wcnVlYmFzL2lyaXMuZHRhIikKYGBgCgoKCjxicj4KCi0tLS0tLS0tLS0tLS0tLS0KCgojIyMgU0FTCgoKCiMjIyMgRXhwb3J0YWNpw7NuIGEgU0FTIAoKKipgaGF2ZW5gKiogZXhwb3J0YSBiaWVuIGEgU0FTOyBwZXJvIC4uLiBsb3Mgbm9tYnJlcyBkZSBsYXMgdmFyaWFibGVzIG5vIHB1ZWRlbiBjb250ZW5lciBwdW50b3MsIGFzw60gcXVlIHVzYW1vcyBvdHJvIGZpY2hlcm8gZGUgZGF0b3MgYG10Y2Fyc2AuIFBvZHLDrWFtb3MgaGFiZXIgY2FtYmlhZG8gZWwgbm9tYnJlIGRlIGxhcyBjb2x1bW5hcyBkZSBpcmlzIChxdWl0YW5kbyBsb3MgcHVudG9zKSwgcGVybyBsbyBkZWphbW9zIHBhcmEgZWwgcHLDs3hpbW8gdHV0b3JpYWwuCgpgYGB7ciwgZWNobyA9IFQsIGV2YWwgPSBGQUxTRX0KIyBtdGNhcnMgZXMgdW4gZGF0YXNldCAgZGVsIHBrZyBnZ3Bsb3QyLCBhc2kgcXVlIGdncGxvdDIgZGViZSBlc3RhciBjYXJnYWRvCiMgbGlicmFyeShnZ3Bsb3QyKQojIGxpYnJhcnkoaGF2ZW4pCndyaXRlX3NhcyhtdGNhcnMsICIuL2RhdG9zL3BydWViYXMvbXRjYXJzLnNhcyIpIApgYGAKCgoKPGJyPgoKIyMjIyBJbXBvcnRhY2lvbiBkZSBmaWNoZXJvcyBTQVMgCgoKYGBge3IsIGVjaG8gPSBULCBldmFsID0gRkFMU0V9CiMgbGlicmFyeShoYXZlbikKbXRjYXJzX2ltcF9zYXMgPC0gcmVhZF9zYXMoIi4vZGF0b3MvcHJ1ZWJhcy9tdGNhcnMuc2FzIikKYGBgCgoKPGJyPgoKLS0tLS0tLS0tLS0tLS0tLQoKPGJyPgoKIyA1LiBGb3JtYXRvKHMpIHByb3Bpb3MgZGUgUgoKR3VhcmRhciBkYXRvcyBlbiBmb3JtYXRvcyBjb21vIHR4dCwgY3N2IG8gRXhjZWwgZXMgbG8gbcOhcyBoYWJpdHVhbCBzaSBxdWllcmVzIGFicmlyIGVzdG9zIGRhdG9zIGVuIG90cm9zIHByb2dyYW1hczsgcGVybyBhbCBncmFiYXIgZW4gZXN0b3MgZm9ybWF0b3MgZ3VhcmRhcyBsb3MgZGF0b3MsIFBFUk8gbm8gZ3VhcmRhcyBsYSBlc3RydWN0dXJhIGRlIGxvcyBkYXRvczsgZXMgZGVjaXIsIHNpIHBvciBlamVtcGxvIHVuYSBjb2x1bW5hIGxhIGhhcyBkZWZpbmlkbyBjb21vIHVuIGZhY3RvciBvIGNvbW8gaW50ZWdlciwgZXN0YSBpbmZvcm1hY2nDs24gc2UgcGVyZGVyw6EuIEVuIGVzdG9zIGNhc29zLCB1bmEgc29sdWNpw7NuIGVzIHVzYXIgZWwgZm9ybWF0byBwcm9waW8gZGUgUi4gCgpIYXkgZG9zIHBvc2liaWxpZGFkZXM6CgogIC0gc2kgcXVpZXJlcyBncmFiYXIgdW4gc29sbyBvYmpldG8sIGVzIHByZWZlcmlibGUgaGFjZXJsbyBjb21vIGBSZHNgCiAgLSBzaSBxdWllcmVzIGdyYWJhciB2YXJpb3Mgb2JqZXRvcyB0aWVuZXMgcXVlIGhhY2VybG8gY29tbyBgUkRhdGFgIG8gYWJyZXZpYWRvIGNvbW8gYFJkYWAKCjxicj4KCi0tLS0tLS0tLS0tLS0tLS0KCiMjIFJEYXRhCgoqKlJEYXRhKiogKG8gUmRhKSBlcyB1biBmb3JtYXRvIGVzcGVjaWZpY28gZGUgUiwgcGVybyB0aWVuZSBkb3MgdmVudGFqYXM6CgogIC0gZXMgYmFzdGFudGUgZWZpY2llbnRlIAogIC0gcGVybWl0ZSBndWFyZGFyIHZhcmlvcyBvYmpldG9zIGVuIHVuIMO6bmljbyBhcmNoaXZvCgo8YnI+IAoKUGFyYSAqKmV4cG9ydGFyKiogYG15X2lyaXNgIGEgdW4gZmljaGVybyBlbiBmb3JtYXRvIGAuUkRhdGFgIHV0aWxpemFyZW1vcyBsYSBmdW5jacOzbiBgc2F2ZSgpYDogc29sbyBoYXkgcXVlIGRlY2lybGUgZWwgb2JqZXRvIHF1ZSBxdWVyZW1vcyBleHBvcnRhciAoZW4gZXN0ZSBjYXNvIHVuIGRmL3RpYmJsZSBsbGFtYWRvICJteV9pcmlzIikgeSBlbCBub21icmUgKGp1bnRvIGNvbiBsYSBydXRhKSBkZWwgYXJjaGl2byBkb25kZSBxdWVyZW1vcyBndWFyZGFybG8uCgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KIy0gZXhwb3J0YSBlbiBmb3JtYXRvIC5SRGF0YSBlbCBkZiBteV9pcmlzIGFsIGZpY2hlcm8gImlyaXMuUkRhdGEiLiAKc2F2ZShpcmlzLCBmaWxlID0gIi4vZGF0b3MvcHJ1ZWJhcy9pcmlzLlJEYXRhIikKYGBgCgoKCjxicj4KRWwgZm9ybWF0byBgLlJEYXRhYCB0aWVuZW4gbGEgdmVudGFqYSBkZSBxdWUgcHVlZGVzIGd1YXJkYXIgdmFyaW9zIG9iamV0b3MgYSBsYSB2ZXouIAoKCmBgYHtyLCBlY2hvID0gVCwgZXZhbCA9IEZBTFNFfQpzYXZlKG10Y2FycywgaXJpcywgIGZpbGUgPSAiLi9kYXRvcy9wcnVlYmFzL210Y2Fyc19hbmRfaXJpcy5SRGF0YSIpCmBgYAoKSW5jbHVzbyBwdWVkZSBndWFyZGFyICB0b2RvcyBsb3Mgb2JqZXRvcyBkZSBsYSBzZXNpw7NuLiBNZWpvciBubyBoYWNlcmxvCgpgYGB7ciwgZWNobyA9IFQsIGV2YWwgPSBGQUxTRX0Kc2F2ZShsaXN0ID0gbHMoYWxsID0gVFJVRSksIGZpbGU9ICIuL2RhdG9zL3BydWViYXMvYWxsX29iamVjdHMuUkRhdGEiKQpgYGAKCk8gdG9kbyBlbCBlc3BhY2lvIGRlIHRyYWJham86CgpgYGB7ciwgZWNobyA9IFQsIGV2YWwgPSBGQUxTRX0Kc2F2ZS5pbWFnZShmaWxlID0gIi4vZGF0b3MvcHJ1ZWJhcy9teV93b3JrX3NwYWNlLlJEYXRhIikKYGBgCgpwYXJhIGx1ZWdvIGNhcmdhcmxvcyBjb246CgoKYGBge3IsIGVjaG8gPSBULCBldmFsID0gRkFMU0V9CmxvYWQoIi4vZGF0b3MvcHJ1ZWJhcy9hbGxfb2JqZWN0cy5SRGF0YSIpCmxvYWQoIi4vZGF0b3MvcHJ1ZWJhcy9teV93b3JrX3NwYWNlLlJEYXRhIikKYGBgCgoKCjxicj4KClBhcmEgY2FyZ2FyIG8gKippbXBvcnRhcioqIGRhdG9zIGVuIGZvcm1hdG8gYC5SRGF0YWAgYmFzdGEgY29uIGFycmFzdHJhcmxvcyBkZW50cm8gZGUgUlN0dWRpbywgbyBzZWd1aXIgbGEgcnV0YSBkZSBtZW7DunMgYCBGaWxlID4gT3BlbiBGaWxlIGAgcGVybyBwYXJhIGhhY2VybG8gZW4gdW4gc2NyaXB0IHNlIGhhY2UgYXPDrToKCmBgYHtyLCBldmFsID0gRkFMU0V9CiMtIGltcG9ydGFtb3MgZWwgZmljaGVybyAibXlfZGF0YTIuUkRhdGEiCmxvYWQoZmlsZSA9ICIuL2RhdG9zL3BydWViYXMvaXJpcy5SRGF0YSIpCmBgYAoKPGJyPgoKClVuIHBvc2libGUgcGVnYSBlcyBxdWUgYWwgY2FyZ2FyIGRhdG9zIGNvbiBgbG9hZCgpYCBzZSBjYXJnYW4gdG9kb3MgbG9zIGRhdG9zIHF1ZSBndWFyZGFzdGUgeSBhZGVtw6FzIHNlIGNhcmdhbiBlbiBlbCBlc3BhY2lvIGRlIHRyYWJham8gY29uIGVsIG1pc21vICBub21icmUgY29uIHF1ZSBsb3MgZ3VhcmRhc3RlLiBFc3RvIHB1ZWRlIHNlciB1bmEgcGVnYSBwdWVzIGVzIHBvc2libGUgcXVlIGhheWEgdW4gb2JqZXRvIHF1ZSBzZSBsbGFtZSBpZ3VhbCBxdWUgdW4gb2JqZXRvIGNvbiBlbCBxdWUgZXN0w6FzIHRyYWJhamFuZG8gZW4gUlN0dWRpby4KCgoKCiMjIFJEUyAoU2VyaWFsaXplZCBSIG9iamVjdHMpCgpVbmEgImRlc3ZlbnRhamEiIGRlbCBmb3JtYXRvIGBSRGF0YWAgZXMgcXVlIGFsIGltcG9ydGFyIHVuIGZpY2hlcm8gLlJEYXRhLCBsb3Mgb2JqZXRvcyBxdWUgY29udGllbmUgc2UgY2FyZ2FuIHNpZW1wcmUgY29uIGVsIG5vbWJyZSBjb24gZWwgcXVlIGZ1ZXJvbiBncmFiYWRvcy4gwr9RdcOpIG3DoXMgZGE/IEJ1ZW5vLCBwdWVkZSBwYXJlY2VyIHF1ZSBubyBlcyBtdXkgaW1wb3J0YW50ZSwgcGVybyBhIHZlY2VzIGVzdG8gcHVlZGUgbGltaXRhciBlbCB3b3JrZmxvdywgYXPDrSBxdWUgbXVjaGFzIHZlY2VzIGVzIHByZWZlcmlibGUgZ3JhYmFyIGNvbW8gYC5SRFNgIGNvbiBsYSBmdW5jacOzbiBgd3JpdGVfcmRzKClgLiBMYSBwZWdhIGVzIHF1ZSBlc3TDoSBmdW5jacOzbiBzb2xvIHBlcm1pdGUgZ3VhcmRhciB1biBvYmpldG8uCgpQYXJhICoqZXhwb3J0YXIqKiBpcmlzIGEgZm9ybWF0byBSRFMgaGFjZW1vczogCgpgYGB7ciwgZWNobyA9IFQsIGV2YWwgPSBGQUxTRX0Kd3JpdGVfcmRzKGlyaXMsICIuL2RhdG9zL3BydWViYXMvaXJpcy5yZHMiKQpgYGAKCjxicj4KClBhcmEgbGVlciBvICoqaW1wb3J0YXIqKiBkYXRvcyBSRFMgKGhheSBxdWUgYXNpZ25hcmxlIHVuIG5vbWJyZSwgcXVlIHB1ZWRlIHNlciBvIG5vIGVsIG5vbWJyZSBvcmlnaW5hbCkgaGFjZW1vczoKCmBgYHtyLCBlY2hvID0gVCwgZXZhbCA9IEZBTFNFfQppcmlzX2ltcF9yZHMgPC0gcmVhZFJEUygiLi9kYXRvcy9wcnVlYmFzL2lyaXMucmRzIikKYGBgCgoKCgo8YnI+CgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKPGJyPgoKCiMgNi4gT3Ryb3MgZm9ybWF0b3MKCjxicj4KCkxvcyBmb3JtYXRvcyBtw6FzIGZyZWN1ZW50ZXMgKGFsIG1lbm9zIGVuIG51ZXN0cmEgw6FyZWEpIGNvbnRpbsO6YW4gc2llbmRvIGxvcyAuY3N2LCAueGxzLCAuLi4gUEVSTyBleGlzdGVuIG11Y2hvcyBvdHJvcyBmb3JtYXRvcy4gCgoKSGF5IDIgZm9ybWF0b3MgcXVlIGVzIGJ1ZW5vLCBhbCBtZW5vcywgc2FiZXIgcXVlIGV4aXN0ZW4sIHBvcnF1ZSBjYWRhIHZleiBzb24gbcOhcyBmcmVjdWVudGVzOiAKCiAgLSBKU09OIChKYXZhU2NyaXB0IE9iamVjdCBOb3RhdGlvbikgCiAgLSBYTUwgKEV4dGVuc2libGUgTWFya3VwIExhbmd1YWdlKS4gCiAgCiAgClBhcmEgSlNPTiwgc2UgcmVjb21pZW5kYSB1c2FyIGVsIHBhcXVldGUgYGpzb25saXRlYCB5IHBhcmEgWE1MLCBgeG1sMmAuCgo8YnI+CgoKIyMjIyBFbCBwYWNrYWdlIGByaW9gIGVzIGxhIG5hdmFqYSBzdWl6YSBkZWwgSS9PIGRhdGEgZW4gUgoKU2kgaGF5IGFsZ8O6biBmb3JtYXRvIGRlIGRhdG9zIHF1ZSBxdWllcmFzIGFicmlyIGVuIFIgeSBubyBsbyBoYXlhbW9zIHRyYXRhZG8sIHByb2JhYmxlbWVudGUgZWwgcGFja2FnZSBgcmlvYCBzZWEgY2FwYXogZGUgaW1wb3J0YXJsby4gW0FxdcOtXShodHRwczovL2dpdGh1Yi5jb20vbGVlcGVyL3JpbyNzdXBwb3J0ZWQtZmlsZS1mb3JtYXRzKSB0aWVuZXMgbGEgbGlzdGEgZGUgbG9zIGZvcm1hdG9zIHF1ZSBwdWVkZSBpbXBvcnRhcjsgYWRlbcOhcyB0b2RvcyBlbGxvcyBjb24gdW5hIHNvbGEgZnVuY2nDs24gKGBpbXBvcnQoKWApLiBQYXJhIGV4cG9ydGFyIGhheSBxdWUgdXNhciBsYSBmdW5jacOzbiBgZXhwb3J0KClgCgoKRWwgcGFxdWV0ZSBgcmlvYCBlcyBtdXkgZsOhY2lsIGRlIHVzYXIsIHBlcm8gZW4gcmVhbGlkYWQgbG8gcXVlIGhhY2UgZXMgbGxhbWFyL3V0aWxpemFyIG90cm9zIHBhcXVldGVzIHBhcmEgaGFjZXIgZWwgdHJhYmFqby4gUG9yIGVqZW1wbG8gcGFyYSBleHBvcnRhci9pbXBvcnRhciBhIFNQU1MgdXNhIGVsIHBhcXVldGUgYGhlYXZlbmAsIHBhcmEgTWF0bGFiIHVzYSBlbCBwYXF1ZXRlIGBybWF0aW9gLCBhc8OtIHF1ZSBzaSBxdWllcmVzIGltcG9ydGFyIGRhdG9zIGRlIE1hdGxhYiB0ZW5kcsOhcyBxdWUgdGVuZXJsbyBpbnN0YWxhZG8uIEFsIGNhcmdhciBlbCBwYXF1ZXRlIGByaW9gIGNvbiBgbGlicmFyeShyaW9gIG5vcyBhdmlzYXLDoSBkZSBzaSBub3MgZmFsdGEgYWxnw7puIHBhcXVldGUgeSBzaSBxdWVyZW1vcyBsb3MgaW5zdGFsYXLDoSBwb3Igbm9zb3Ryb3MgY29uIGxhIGZ1bmNpw7NuIGByaW86Omluc3RhbGxfZm9ybWF0cygpYAoKClZlw6Ftb3MgY29tbyBmdW5jaW9uYSBgcmlvYC4gUGFyYSBlbGxvIHByaW1lcm8gKipleHBvcnRhcmVtb3MqKiBlbCBmaWNoZXJvIGRlIGRhdG9zIGBtdGNhcnNgIGFkaWZlcmVudGVzIGZvcm1hdG9zOgoKYGBge3IsIGVjaG8gPSBUUlVFLCBldmFsID0gRkFMU0V9CmxpYnJhcnkocmlvKQpleHBvcnQobXRjYXJzLCAiLi9kYXRvcy9wcnVlYmFzL210Y2Fycy5jc3YiKSAgICMgY29tbWEtc2VwYXJhdGVkIHZhbHVlcwpleHBvcnQobXRjYXJzLCAiLi9kYXRvcy9wcnVlYmFzL210Y2Fycy5yZHMiKSAgICMgUiBzZXJpYWxpemVkCmV4cG9ydChtdGNhcnMsICIuL2RhdG9zL3BydWViYXMvbXRjYXJzLnNhdiIpICAgIyBTUFNTCmV4cG9ydChtdGNhcnMsICIuL2RhdG9zL3BydWViYXMvbXRjYXJzLmpzb24iKSAgIyBKU09OCmV4cG9ydChtdGNhcnMsICIuL2RhdG9zL3BydWViYXMvbXRjYXJzLmFyZmYiKSAgIyBXZWthIEF0dHJpYnV0ZS1SZWxhdGlvbiBGaWxlIEZvcm1hdApgYGAKCjxicj4KCkFkZW3DoXMgcGVybWl0ZSBleHBvcnRhciB5ICoqY29tcHJpbWlyIGxvcyBkYXRvcyBkaXJlY3RhbWVudGUqKjoKCmBgYHtyLCBlY2hvID0gVFJVRSwgZXZhbCA9IEZBTFNFfQpsaWJyYXJ5KHJpbykKZXhwb3J0KG10Y2FycywgIi4vZGF0b3MvcHJ1ZWJhcy9tdGNhcnMudHN2LnppcCIpICMgVFNWICYgWmlwIGl0CmBgYAoKPGJyPgoKUGFyYSAqKmltcG9ydGFyKiogbG9zIGZpY2hlcm9zIHF1ZSBoZW1vcyBleHBvcnRhZG8gcHJldmlhbWVudGU6CgoKYGBge3IsIGVjaG8gPSBUUlVFLCBldmFsID0gRkFMU0V9CmxpYnJhcnkocmlvKQptdGNhcnNfY3N2ICA8LSBpbXBvcnQoIi4vZGF0b3MvcHJ1ZWJhcy9tdGNhcnMuY3N2IikgICMgQ1NWCm10Y2Fyc19zcHNzIDwtIGltcG9ydCgiLi9kYXRvcy9wcnVlYmFzL210Y2Fycy5zYXYiKSAgIyBTUFNTICguc2F2KQpgYGAKCgpDb21vIGN1cmlvc2lkYWQgZGVjaXIgcXVlIGByaW9gIGltcG9ydGEgbG9zIGRhdG9zIGNvbW8gZGF0YS5mcmFtZXMsIG5vIGNvbW8gdGliYmxlcywgcGVybyBwdWVkZXMgZm9yemFybG8gY29uOgoKYGBge3IsIGVjaG8gPSBUUlVFLCBldmFsID0gRkFMU0V9CmxpYnJhcnkocmlvKQptdGNhcnNfY3N2ICA8LSBpbXBvcnQoIi4vZGF0b3MvcHJ1ZWJhcy9tdGNhcnMuY3N2Iiwgc2V0Y2xhc3MgPSAidGliYmxlIikgICMgQ1NWCmBgYAoKRW4gW2VzdGUgcG9zdF0oaHR0cDovL3JpdHNva2lndWVzcy5zaXRlL2RvY3MvMjAxOC8wMy8yNS90b2RheS1vbi10d2l0dGVyLWktbGVhcm5lZC8pIHRhbWJpw6luIGV4cGxpY2FuIHF1ZSBgcmlvYCBwdWVkZSBzZXIgdW5hIGFsdGVybmF0aXZhIGEgYHJ2ZXN0YCB5L28gYHhtbDJgIHBhcmEgaW1wb3J0YXIgdGFibGFzIGh0bWwgZW4gaW50ZXJuZXQuIFBvciBlamVtcGxvOgoKYGBge3IsIGVjaG8gPSBUUlVFLCBldmFsID0gRkFMU0V9Cm15X3VybCA8LSAiaHR0cDovL3d3dy5zY29yZXN3YXkuY29tLz9zcG9ydD1zb2NjZXImcGFnZT1jb21wZXRpdGlvbiZpZD04Igp0YWJsZSA8LSBpbXBvcnQobXlfdXJsLCBmb3JtYXQgPSAiaHRtbCIsIHdoaWNoID0gMikKYGBgCgoKCgpTaSB0YW1wb2NvIGByaW9gIHB1ZWRlIGxlZXIgZWwgZm9ybWF0byBxdWUgbmVjZXNpdGFzLCBlcyBtdXkgcHJvYmFibGUgcXVlIGFsZ3VpZW4gaGF5YSBlc2NyaXRvIHVuIHBhY2thZ2UgcGFyYSBoYWNlcmxvOyB0ZW5kcsOhcyBxdWUgdmVyIHNpIGV4aXN0ZSBidXNjYW5kbyBlbiBpbnRlcm5ldGUuCgoKUmVjaWVudGVtZW50ZSBoYSBhcGFyZWNpZG8gb3RybyBtZXRhLXBrZyBwYXJhIGxlZXIgZGF0b3MuIEVzIGVsIHBrZyBgcmVhZGl0YC4gQcO6biBubyBsbyBoZSB1c2FkbyBwZXJvLCBwb3IgdmFyaWFzIHJhem9uZXMsIG5vIHF1aWVybyBvbHZpZGFybWUgZGUgw6lsLiBQdWVkZXMgdmVybG8gW2FxdcOtXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvcmVhZGl0L2luZGV4Lmh0bWwpCgo8YnI+CgotLS0tLS0tLS0tLS0tLS0tLS0tLQoKPGJyPgoKCiMgNy4gRGVzY2FyZ2FyIGRhdG9zIGRlIGludGVybmV0Cgo8YnI+CgpIYXkgbXVjaMOtc2ltb3MgZGF0b3MgZW4gaW50ZXJuZXQgcGFyYSBkZXNjYXJnYXI7IHNpZW1wcmUgcG9kZW1vcyBkZXNjYXJnYXJsb3MgdXNhbmRvIGVsIG5hdmVnYWRvciwgUEVSTyBsYSBmaWxvc29mw61hIGRlbCBjdXJzbyBlcyAoc2kgcG9kZW1vcykgaGFjZXJsbyB0b2RvIGRlc2RlIFIvUlN0dWRpbwoKRGVzZGUgUlN0dWRpbywgcG9kZW1vcyBkZXNjYXJnYXIgZGF0b3MgY29uIGxhcyBtaXNtYXMgZnVuY2lvbmVzIHF1ZSB1c8OhYmFtb3MgcGFyYSBjYXJnYXIgZW4gZWwgZW50b3JubyBkZSB0cmFiYWpvIGxvcyAgZGF0b3MgcXVlIHRlbsOtYW1vcyBlbiBudWVzdHJvIFBDLiBMYSDDum5pY2EgZGlmZXJlbmNpYSBjb25zaXN0ZSBlbiBxdWUsIGVuIGx1Z2FyIGRlIHByb3BvcmNpb25hciBsYSBydXRhIGFsIGZpY2hlcm8sIHRlbmRyZW1vcyBxdWUgcHJvcG9yY2lvbmFyIGxhIHJ1dGEgZGUgaW50ZXJuZXQuIFBvciBlamVtcGxvOgoKPGJyPgoKYGBge3IsIGVjaG8gPSBUUlVFLCBldmFsID0gRkFMU0V9CiMgY2FyZ2Ftb3MgbG9zIGRhdG9zIGRlbCBmaWNoZXJvICJiaW8yNjAtaGVpZ2h0cy5jc3YiCnVybCA8LSAiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2RhdGFzY2llbmNlbGFicy9kYXRhL21hc3Rlci9iaW8yNjAtaGVpZ2h0cy5jc3YiCmRhdG9zIDwtIHJlYWRfY3N2KHVybCkKYGBgCgoKCjxicj4KCkEgdmVjZXMgcG9kZW1vcyBuZWNlc2l0YXIgaGFjZXIgdW5hIGNvcGlhIGRlIGxvcyBkYXRvcyBhIG51ZXN0cm8gb3JkZW5hZG9yLiBFbiBlc3RlIGNhc28sIGxvIHF1ZSB5byBoYXLDrWEgZXMgY2FyZ2FyIGxvcyBkYXRvcyB5IGx1ZWdvIGV4cG9ydGFybG9zIGEgLnJkczsgcGVybyB0YW1iacOpbiBwb2RlbW9zIGhhY2VybG8gZGlyZWN0YW1lbnRlIGNvbiBsYSBmdW5jacOzbiBgZG93bmxvYWQuZmlsZSgpYDoKCjxicj4KCmBgYHtyLCBlY2hvID0gVFJVRSwgZXZhbCA9IEZBTFNFfQojIGRlc2NhcmdhbW9zIHkgYWxtYWNlbmFtb3MgZW4gbnVlc3RybyBQQyBsb3MgZGF0b3MgZGVsIGZpY2hlcm8gImJpbzI2MC1oZWlnaHRzLmNzdiIKdXJsIDwtICJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vZGF0YXNjaWVuY2VsYWJzL2RhdGEvbWFzdGVyL2JpbzI2MC1oZWlnaHRzLmNzdiIKZGVzdGlubyA8LSAiLi9kYXRvcy9wcnVlYmFzL2JpbzI2MC1oZWlnaHRzLmNzdiIKZG93bmxvYWQuZmlsZSh1cmwsIGRlc3Rpbm8pCmRhdCA8LSByZWFkLmNzdihkZXN0aW5vKQpgYGAKCjxicj4KCkEgdmVjZXMsIGxhIGZ1bmNpw7NuIGRlIFItYmFzZSBgZG93bmxvYWQuZmlsZWAgcHVlZGUgdGVuZXIgcHJvYmxlbWFzIHNpIGVsIHByb3RvY29sbyBlcyBodHRwcy4gRW4gZXN0b3MgY2Fzb3MsIGxhIGZ1bmNpw7NuIGBkb3dsb2FkKClgIGRlbCBwa2cgZG93bmxvYWRlciBwdWVkZSBzb2x1Y2lvbmFybG86CgpgYGB7ciwgZWNobyA9IFRSVUUsIGV2YWwgPSBGQUxTRX0KI2luc3RhbGwucGFja2FnZXMoImRvd25sb2FkZXIiKQpsaWJyYXJ5KGRvd25sb2FkZXIpCnVybCA8LSAiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2RhdGFzY2llbmNlbGFicy9kYXRhL21hc3Rlci9iaW8yNjAtaGVpZ2h0cy5jc3YiCmZpbGVuYW1lIDwtIGJhc2VuYW1lKHVybCkKZGVzdGlubyA8LSBwYXN0ZTAoIi4vZGF0b3MvcHJ1ZWJhcy8iLCBmaWxlbmFtZSkKZG93bmxvYWQodXJsLGRlc3Rpbm8pCmRhdCA8LSByZWFkLmNzdihkZXN0aW5vKQpgYGAKCgo8YnI+CgotLS0tLS0tLS0tLS0tLS0tLS0tLQoKPGJyPgoKCgojIDguIEFQSSdzIHkgV2ViIFNjcmFwcGluZwoKPGJyPgoKRWwgcHJvY2VzbyB5IGFjY2lvbmVzIHBhcmEgcmVjb3BpbGFyIGluZm9ybWFjacOzbiBkZSBsYSBXZWIgc2UgY29ub2NlIGNvbW8gW3dlYiBzY3JhcHBpbmddKGh0dHBzOi8vZXMud2lraXBlZGlhLm9yZy93aWtpL1dlYl9zY3JhcGluZykuIEVzdGUgcHJvY2VzbyBzZSBwdWVkZSBoYWNlciBtYW51YWxtZW50ZSwgcGVybyBsbyBoYWJpdHVhbCBlcyBhdXRvbWF0aXphcmxvIHV0aWxpemFuZG8gc29mdHdhcmUuIFNlIHB1ZWRlIGFjY2VkZXIgYSBsb3MgZGF0b3MgZGlyZWN0YW1lbnRlIHBlcm8gYWN0dWFsbWVudGUgZXMgbXV5IGNvbcO6biBoYWNlcmxvIGEgdHJhdsOpcyBkZSBBUElzLCB5YSBxdWUgbGEgbWF5b3LDrWEgZGUgb3JnYW5pc21vcy9lbXByZXNhcyB0aWVuZW4gdW5hIG8gdmFyaWFzIEFQSXMuIAoKCkFQSSBzaWduaWZpY2EgIkFwbGljYXRpb24gUHJvZ3JhbW1pbmcgSW50ZXJmYWNlIiB5IHNlIHB1ZWRlIGVudGVuZGVyIGNvbW8gdW4gbWVjYW5pc21vIHF1ZSBub3MgcGVybWl0ZSBpbnRlcmFjdHVhciAocG9yIGVqZW1wbG8gcGFyYSBoYWNlciB1bmEgcGV0aWNpw7NuIGRlIGRhdG9zKSBjb24gdW4gc2Vydmlkb3IgZGUgaW50ZXJuZXQuIFBvciBlamVtcGxvLCBtdWNob3MgYmFuY29zIHRpZW5lbiBBUElzIGEgbGFzIHF1ZSBzZSBsZXMgcHVlZGVuIGhhY2VyIHBldGljaW9uZXMsIGVzdG8gaGFjZSBwb3NpYmxlIHF1ZSBzZSBkZXNhcnJvbGxlbiBhcHBzIHBhcmEgaGFjZXIgY2llcnRhcyBvcGVyYWNpb25lcyBiYW5jYXJpYXM7IGVzIGRlY2lyLCB1bmEgQVBJIGVzIHVuIG1lY2FuaXNtbyBxdWUgbm9zIHBlcm1pdGUgYWNjZWRlciB5L28gaW50ZXJhY3R1YXIgY29uIGRldGVybWluYWRhcyBmdW5jaW9uZXMgZGUgdW4gc2VydmljaW8gd2ViLiAKCkxhcyBBUElzIGZhY2lsaXRhbiBtdWNobyBsYSByZWNvcGlsYWNpw7NuIGRlIGRhdG9zIGFsIHBvZGVyc2UgYWNjZWRlciBhIGVsbGFzIGRlIGZvcm1hIHByb2dyYW3DoXRpY2EgeWEgcXVlIHByb3ZlZW4gZGUgdW4gcHJvY2VzbyBkZSBhY2Nlc28gYSBlbGxvcyBlc3RhbmRhcml6YWRvOiBzZSBlbnbDrWEgdW5hICJodHRwIHJlcXVlc3QiIGEgbGEgQVBJIHkgc2UgcmVjaWJlbiBsb3MgZGF0b3MgZW4gdW4gZGV0ZXJtaW5hZG8gZm9ybWF0bywgZ2VuZXJhbG1lbnRlIEpTT04uCgpFbiBlbCBlbnRvcm5vIFIgc2UgcHVlZGVuIGRlc2Fycm9sbGFyIHBhcXVldGVzIHBhcmEgYWNjZWRlciBhIEFQSXM7IHBvciBlamVtcGxvLCB2YW1vcyBhIHV0aWxpemFyIGVsIHBhcXVldGUgZGUgUiBgZXVyb3N0YXRgIHBhcmEgYWNjZWRlciBhIGxhIEFQSSBkZSBFdXJvc3RhdCB5IGRlc2NhcmdhciBkYXRvcyBkaXJlY3RhbWVudGUgZW4gUi4gVmXDoW1vc2xvOgoKLS0tLS0tLS0tLS0tLS0tLS0tLQoKPGJyPgoKCiMjIEV1cm9zdGF0IAoKRXVyc290YXQgdGllbmUgdW5hIEFQSSBxdWUgcGVybWl0ZSBoYWNlciBwZXRpY2lvbmVzIGRlIGRhdG9zLiBPYnZpYW1lbnRlLCBwYXJhIHBvZGVyIGhhY2VyIHBldGljaW9uZXMgZGUgZGF0b3MgYSB0cmF2w6lzIGRlIHN1IEFQSSBoYXMgZGUgY29ub2NlciBzdSBzaW50YXhpczsgc2kgZXN0w6FzIGludGVyZXNhZG8gcHVlZGVzIGVtcGV6YXIgW2FxdcOtXShodHRwOi8vZWMuZXVyb3BhLmV1L2V1cm9zdGF0L2RhdGEvd2ViLXNlcnZpY2VzKS4gTm9zb3Ryb3MgYWNjZWRlcmVtb3MgYSBFdXJvc3RhdCBhIHRyYXbDqXMgZGVsIHBhY2thZ2UgW2BldXJvc3RhdGBdKGh0dHBzOi8vcm9wZW5nb3YuZ2l0aHViLmlvL2V1cm9zdGF0LykuIFNpIGVzdGFzIGludGVyZXNhZG8gZW4gYmFqYXIgZGF0b3MgZGUgRXVyb3N0YXQgZXMgY29udmVuaWVudGUgcXVlIHVzZXMgZXN0YSBbdmlnbmV0dGVdKGh0dHA6Ly9yb3Blbmdvdi5naXRodWIuaW8vZXVyb3N0YXQvYXJ0aWNsZXMvZXVyb3N0YXRfdHV0b3JpYWwuaHRtbCkgeSBsYSBbY2hlYXQgc2hlZXRdKGh0dHA6Ly9yb3Blbmdvdi5naXRodWIuaW8vZXVyb3N0YXQvYXJ0aWNsZXMvY2hlYXRzaGVldC5odG1sKS4gVmVhbW9zIHVuIGVqZW1wbG86CgoKQ29uIGxhIGZ1bmNpw7NuIGBnZXRfZXVyb3N0YXQoKWAgZXMgc3VmaWNpZW50ZSBwYXJhIGJhamFyIHVuYSB0YWJsYSBkZSBFdXJvc3RhdCBjb24gZWwgcG9yY2VudGFqZSBkZSBlbXBsZW9zIGVuIHNlY3RvcmVzIGN1bHR1cmFsZXM6CgpgYGB7ciwgZWNobyA9IFRSVUUsIGV2YWwgPSBGQUxTRX0KIyBpbnN0YWxsLnBhY2thZ2VzKCJldXJvc3RhdCIpCmxpYnJhcnkoImV1cm9zdGF0IikKZGYgPC0gZ2V0X2V1cm9zdGF0KCJjdWx0X2VtcF9zZXgiLCB0aW1lX2Zvcm1hdCA9ICdyYXcnLCBrZWVwRmxhZ3MgPSBUKSAgICAgICAjLSBiYWphbW9zIGxvcyBkYXRvcyBkZSBsYSB0YWJsYSAiY3VsdF9lbXBfc2V4IjogZW1wbGVvIGN1bHR1cmFsIHBvciBnZW5lcm8iCmBgYAoKCjxicj4KClVuIGVqZW1wbG8gbcOhcyBjb21wbGV0bzogZGVzY2FyZ2FyZW1vcyBsb3MgZGF0b3MgZGUgbGEgdGFibGEgYGhsdGhfc2lsY18xN2AgcXVlIGNvbnRpZW5lIGRhdG9zIGNvbiBsYSAiZXNwZXJhbnphIGRlIHZpZGEgc2FsdWRhYmxlIiBwYXJhIGRpZmVyZW50ZXMgYcOxb3MgZW4gbG9zIHBhw61zZXMgZGUgbGEgVUUuCgpgYGB7ciwgZWNobyA9IFRSVUUsIGV2YWwgPSBUUlVFLCByZXN1bHRzID0gImhpZGUifQojIGluc3RhbGwucGFja2FnZXMoImV1cm9zdGF0IikKbGlicmFyeSgiZXVyb3N0YXQiKQoKIy0tLS0tLS0tLS0tLS0tLS0tLSBwb2RlbW9zIGJ1c2NhciB1biAgInRlbWEiIGNvbiBsYSBmLiBzZWFyY2hfZXVyb3N0YXQoKQphYSA8LSBzZWFyY2hfZXVyb3N0YXQoImVtcGxveW1lbnQiLCB0eXBlID0gImFsbCIpIAoKIy0tLS0tLS0tLS0tLS0tLS0tLSBlbGVnaW1vcyB1bmEgdGFibGEgZGUgRXVyb3N0YXQKbXlfdGFibGUgPC0gImhsdGhfc2lsY18xNyIgICAgICAgICAgIy0gZWxlZ2ltb3MgdW5hIHRhYmxhOyBwb3IgZWplbXBsbyAiaGx0aF9zaWxjXzE3IjogIkhlYWx0aHkgbGlmZSBleHBlY3RhbmN5IGJhc2VkIG9uIHNlbGYtcGVyY2VpdmVkIGhlYWx0aCIKbGFiZWxfZXVyb3N0YXRfdGFibGVzKG15X3RhYmxlKSAgICAgIy0gZGEgaW5mb3JtYWNpb24gc29icmUgbGEgQmFzZSBkZSBkYXRvcyBxIGVzdGFzIGJ1c2NhbmRvCgojLS0tLS0tLS0tLS0tLS0tLS0tIGRlc2NhcmdhbW9zIGxvcyBkYXRvcyBjb24gZ2V0X2V1cm9zdGF0KCkKZGYgPC0gZ2V0X2V1cm9zdGF0KG15X3RhYmxlLCB0aW1lX2Zvcm1hdCA9ICdyYXcnLCBrZWVwRmxhZ3MgPSBUICkgICAgICAgIy0gYmFqYW1vcyBsb3MgZGF0b3MgZGUgdW5hIHRhYmxhCmRmX2wgPC0gbGFiZWxfZXVyb3N0YXQoZGYpICAgICAgICAjLSBwb25lIGxhYmVsczogU3BhaW4gZW4gbHVnYXIgZGUgc3UgY8OzZGlnbyAobWFzIGxlZ2libGUsbWVub3MgZsOhY2lsIGRlIHByb2dyYW1hcikKCiMtLS0tLS0tLS0tLS0tLS0tLS0gbG9zIGFycmVnbGFtb3MgdW4gcG9jbyAKbGlicmFyeSgidGlkeXZlcnNlIikKbGlicmFyeSgicGpwdjIwMjAuMDEiKSAjLSByZW1vdGVzOjppbnN0YWxsX2dpdGh1YigicGVyZXpwNDQvcGpwdjIwMjAuMDEiKQphYSA8LSBwanBfZl92YWxvcmVzX3VuaWNvcyhkZikgICAgICAgIy0gdmVyIGxvcyB2YWxvcmVzIHVuaWNvcyBkZSBjYWRhIGNvbHVtbmEKYWEgPC0gcGpwX2ZfdmFsb3Jlc191bmljb3MoZGZfbCkgICAgICMtIHZlciBsb3MgdmFsb3JlcyB1bmljb3MgZGUgY2FkYSBjb2x1bW5hCmRmIDwtIGxhYmVsX2V1cm9zdGF0KGRmLCBjb2RlID0gYygiZ2VvIiwgInVuaXQiLCAiaW5kaWNfaGUiKSkKYGBgCgpBaG9yYSB2YW1vcyBhIGZ1c2lvbmFybG8gY29uIGRhdG9zIGRlIGxvcyBsw61taXRlcyBlc3BhY2lhbGVzIGRlIGNhZGEgcGHDrXMsIHBhcmEgZmluYWxtZW50ZSBoYWNlciB1biBncsOhZmljbyBlc3BhY2lhbCAuLi4uIFBFUk8gRXVyb3N0YXQgbyBldXJvc3RhdCBjYW1iaWFyb24gc3UgQVBJLCBlbnRvbmNlcyAuLi4uICBlc3RlIGNodW5rIG5vIGZ1bmNpb25hIGNvbiBsYSB2ZXJzacOzbiBhY3R1YWwgZGVsIHBhcXVldGUgYGV1cm9zdGF0YC4gTG8gZGVqbyBwb3Igc2kgYcO6biBmdW5jaW9uYXNlIGVuIGxvcyBvcmRlbmFkb3JlcyBkZWwgYXVsYS4KCgpgYGB7ciwgZWNobyA9IFRSVUUsIGV2YWwgPSBGQUxTRX0KIy0gc2VsZWNjaW9ubyBkYXRvcyBkZSAyMDE2LCBGZW1hbGVzLCB5IEhFXzUwIHkgZGVzcHXDqXMgaGFnbyB1biBjdXQgZGUgInZhbHVlcyIKZGZfeCA8LSBkZiAlPiUgZmlsdGVyKHRpbWUgPT0gIjIwMTYiKSAlPiUgIGZpbHRlcihzZXggPT0gIkZlbWFsZXMiKSAlPiUgZmlsdGVyKGluZGljX2hlX2NvZGUgPT0gIkhFXzUwIikgJT4lIAogICAgICAgIG11dGF0ZShjYXQgPSBjdXRfdG9fY2xhc3Nlcyh2YWx1ZXMsIG4gPSA3LCBkZWNpbWFscyA9IDEpKQptYXBkYXRhIDwtIG1lcmdlX2V1cm9zdGF0X2dlb2RhdGEoZGZfeCwgcmVzb2x1dGlvbiA9ICIyMCIsIGdlb2NvbHVtbiA9ICJnZW9fY29kZSIpICMtIGZ1c2lvbm8gY29uIGdlbyBkYXRhCgpnZ3Bsb3QobWFwZGF0YSwgYWVzKHggPSBsb25nLCB5ID0gbGF0LCBncm91cCA9IGdyb3VwKSkrCiAgZ2VvbV9wb2x5Z29uKGFlcyhmaWxsID0gY2F0KSwgY29sb3IgPSAiYmxhY2siLCBzaXplID0gLjEpKwogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiUmRZbEJ1IikgKwogIGxhYnModGl0bGUgPSAiSGVhbHRoeSBsaWZlIGV4cGVjdGFuY3ksIDIwMTYiLAogICAgICAgc3VidGl0bGUgPSAiSGVhbHRoIGV4cGVjdGFuY3kgaW4geWVhcnMgYXQgNTAiLAogICAgICAgZmlsbCA9ICJIZWFsdGh5IGxpZmUgZXhwZWN0YW5jeSIsCiAgICAgICBjYXB0aW9uID0gIihDKSBFdXJvR2VvZ3JhcGhpY3MgZm9yIHRoZSBhZG1pbmlzdHJhdGl2ZSBib3VuZGFyaWVzIikgKyB0aGVtZV9saWdodCgpICsKICBjb29yZF9tYXAoeGxpbSA9IGMoLTEyLCA0NCksIHlsaW0gPSBjKDM1LCA2NykpCmBgYAoKCkVzdGUgc8OtIGZ1bmNpb25hcsOhIGNvbiBsYSBudWV2YSB2ZXJzacOzbiBkZWwgcGtnIGBldXJvc3RhdGAKCmBgYHtyLCBlY2hvID0gVFJVRSwgZXZhbCA9IFRSVUV9CmRmX3ggPC0gZGYgJT4lIGZpbHRlcih0aW1lID09ICIyMDE2IikgJT4lICBmaWx0ZXIoc2V4ID09ICJGZW1hbGVzIikgJT4lIGZpbHRlcihpbmRpY19oZV9jb2RlID09ICJIRV81MCIpICU+JSAKICAgICAgICBtdXRhdGUoY2F0ID0gY3V0X3RvX2NsYXNzZXModmFsdWVzLCBuID0gNywgZGVjaW1hbHMgPSAxKSkKCmdlb21ldHJpYXMgPC0gZ2V0X2V1cm9zdGF0X2dlb3NwYXRpYWwocmVzb2x1dGlvbiA9ICIyMCIsIG51dHNfbGV2ZWwgPSAiMCIpICMtIGFob3JhIHNlIGJhamFuIGxhcyBnZW9tZXRyw61hcyB5IHRpZW5lcyBxdWUgdW5pcmxhIHR1IGNvbiBkcGx5ciAoSGF5IHVuIFBiIGRlIGVuY29kaW5nKQptYXBkYXRhIDwtIGlubmVyX2pvaW4oZ2VvbWV0cmlhcywgZGZfeCwgYnkgPSBjKCJnZW8iID0gImdlb19jb2RlIikpCgpwIDwtIGdncGxvdChtYXBkYXRhKSArCiAgZ2VvbV9zZihhZXMoZmlsbCA9IGNhdCwgZ2VvbWV0cnkgPSBnZW9tZXRyeSksIGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IC4xKSArCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJSZFlsQnUiKSArCiAgbGFicyh0aXRsZSA9ICJIZWFsdGh5IGxpZmUgZXhwZWN0YW5jeSwgMjAxNiIsCiAgICAgICBzdWJ0aXRsZSA9ICJIZWFsdGggZXhwZWN0YW5jeSBpbiB5ZWFycyBhdCA1MCIsCiAgICAgICBmaWxsID0gIkhlYWx0aHkgbGlmZSBleHBlY3RhbmN5IiwKICAgICAgIGNhcHRpb24gPSAiKEMpIEV1cm9HZW9ncmFwaGljcyBmb3IgdGhlIGFkbWluaXN0cmF0aXZlIGJvdW5kYXJpZXMiKSArIHRoZW1lX2xpZ2h0KCkgKwogIGNvb3JkX3NmKHhsaW0gPSBjKC0xMiwgNDQpLCB5bGltID0gYygzNSwgNjcpKSAKcApgYGAKCgoKCgo8YnI+CgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCjxicj4KCiMjIElORQoKPGJyPgoKwr9FbCBJTkUgdGllbmUgQVBJPyBQdWVzIHPDrSwgW2FxdcOtXShodHRwOi8vd3d3LmluZS5lcy9keW5ncy9EYXRhTGFiL21hbnVhbC5odG1sP2NpZD00NSkgcHVlZGVzICJ2ZXJsYSIsIHBlcm8gLi4uCgpIYWNlIHBvY28gdHV2aW1vcyBxdWUgdXRpbGl6YXIgYWxndW5hIHRhYmxhIGRlbCBJTkUgeSwgZW4gbHVnYXIgZGUgdXNhciBsYSBBUEksIG5vcyBiYWphbW9zIGxvcyBkYXRvcyBhc8OtOgoKCgpgYGB7ciwgZWNobyA9IFRSVUUsIGV2YWwgPSBGQUxTRX0KbGlicmFyeSgicHhSIikgICAgICAgICAgICAgICMtIHBhcmEgdHJhYmFqYXIgY29uIGRhdG9zIFBDLUF4aXMKbGlicmFyeSgidGlkeXZlcnNlIikKbGlicmFyeSgicGpwdjIwMjAuMDEiKQpmaWxlX25hbWUgPC0gImh0dHA6Ly93d3cuaW5lLmVzL2pheGlUMy9maWxlcy90L2VzL3B4LzQxODkucHg/bm9jYWI9MSIKZGYgPC0gcmVhZC5weChmaWxlX25hbWUpICU+JSBhcy5kYXRhLmZyYW1lKCkgJT4lIGFzLnRibCgpICAgIy0gbm8gZnVuY2lvbmFiYSBlbiAzLjUgeCAkCmFhIDwtIHBqcHYyMDIwLjAxOjpwanBfZl92YWxvcmVzX3VuaWNvcyhkZikgICAgICMtIHZlciBsb3MgdmFsb3JlcyDDum5pY29zIGRlIGNhZGEgY29sdW1uYQpgYGAKCgpMYSB2ZXJkYWQgZXMgcXVlIGFob3JhICgyMDE5KSBoYXkgdW4gcGFxdWV0ZSBxdWUgZnVuY2lvbmEvZnVuY2lvbmFiYSBiYXN0YW50ZSBiaWVuOiA8aHR0cHM6Ly9naXRodWIuY29tL29kZHdvcmxkbmcvSU5FYmFzZVI+CgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCjxicj4KCgoKIyMgQmFuY28gTXVuZGlhbAoKPGJyPgoKUGFyYSBhY2NlZGVyIGEgbGEgW0FQSSBkZWwgQmFuY28gTXVuZGlhbF0oaHR0cHM6Ly9kYXRhaGVscGRlc2sud29ybGRiYW5rLm9yZy9rbm93bGVkZ2ViYXNlL3RvcGljcy8xMjU1ODkpIGhheSwgYWN0dWFsbWVudGUsIDIgcGFxdWV0ZXMgZGUgUjogW2BXRElgXShodHRwczovL2dpdGh1Yi5jb20vdmluY2VudGFyZWxidW5kb2NrL1dESSkgeSBbYHdic3RhdHNgXShodHRwczovL2dpdGh1Yi5jb20vR0lTVC1PUk5ML3dic3RhdHMpLiAKCjxicj4KClBvZGVtb3MgYmFqYXIgZGF0b3MgZGVsIEJhbmNvIE11bmRpYWwgY29uIGVsIHBhcXVldGUgYFdESWAgYXPDrToKCmBgYHtyLCBlY2hvID0gVFJVRSwgZXZhbCA9IEZBTFNFfQojaW5zdGFsbC5wYWNrYWdlcygiV0RJIikKbGlicmFyeSgiV0RJIikKCiMtLS0tIGJ1c2NhbW9zIGRhdG9zIHJlbGFjaW9uYWRvcyBjb24gR0RQCmFhIDwtIFdESXNlYXJjaCgnZ2RwJykKYWEgPC0gV0RJc2VhcmNoKCdnZHAuKmNhcGl0YS4qY29uc3RhbnQnKQoKIy0tLS0gZGVzY2FyZ2Ftb3MgIk5ZLkdEUC5QQ0FQLktEIjogIEdEUCBwZXIgY2FwaXRhIChjb25zdGFudCAyMDEwIFVTJCkKZGYgPC0gV0RJKGluZGljYXRvciA9ICJOWS5HRFAuUENBUC5LRCIpCiMtLS0tIHBvZGVtb3MgZmlsdHJhciBsYSBxdWVycnkKZGYgPC0gV0RJKGluZGljYXRvciA9ICJOWS5HRFAuUENBUC5LRCIsIGNvdW50cnkgPSBjKCdNWCcsJ0NBJywnVVMnKSwgc3RhcnQgPSAxOTYwLCBlbmQgPSAyMDE3KQpgYGAKCjxicj4KClBvZGVtb3MgYmFqYXIgZGF0b3MgZGVsIEJhbmNvIE11bmRpYWwgY29uIGVsIHBhcXVldGUgYHdic3RhdHNgIGFzw606CgpgYGB7ciwgZWNobyA9IFRSVUUsIGV2YWwgPSBGQUxTRX0KI2luc3RhbGwucGFja2FnZXMoIndic3RhdHMiKQpsaWJyYXJ5KCJ3YnN0YXRzIikKCiMtLS0tLS0tICBsaXN0YSBkZSBpbmRpY2Fkb3JlcyBkaXNwb25pYmxlcwphYSA8LSB3Yl9jYWNoZWxpc3QKCiMtLS0tIGJ1c2NhbW9zIGRhdG9zIHJlbGFjaW9uYWRvcyBjb24gR0RQCmFhIDwtIHdic2VhcmNoKHBhdHRlcm4gPSAiZ2RwIikKYWEgPC0gd2JzZWFyY2goJ2dkcC4qY2FwaXRhLipjb25zdGFudCcpCgojLS0tLSBkZXNjYXJnYW1vcyAiTlkuR0RQLlBDQVAuS0QiOiAgR0RQIHBlciBjYXBpdGEgKGNvbnN0YW50IDIwMTAgVVMkKQpkZiA8LSB3YihpbmRpY2F0b3IgPSAiTlkuR0RQLlBDQVAuS0QiKQoKIy0tLS0gcG9kZW1vcyBmaWx0cmFyIGxhIHF1ZXJyeQpkZiA8LSB3YihpbmRpY2F0b3IgPSAiTlkuR0RQLlBDQVAuS0QiLCBjb3VudHJ5ID0gYygnTVgnLCdDQScsJ1VTJyksIHN0YXJ0ZGF0ZSA9IDIwMDAsIGVuZGRhdGUgPSAyMDE3KQpgYGAKCgo8YnI+CgpbQXF1w61dKGh0dHA6Ly93d3cubWFnZXNibG9nLmNvbS8yMDE2LzA0L25ldy1yLXBhY2thZ2UtdG8tYWNjZXNzLXdvcmxkLWJhbmstZGF0YS5odG1sKSB0ZW5lbW9zIHVuIHBvc3QgZW4gZWwgcXVlIHNlIHVzYSBlbCBwa2cgd2JzdGF0cyBwYXJhIG9idGVuZXIgZGF0b3MgeSBsdWVnbyBncmFmaWNhcmxvcy4KCgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgo8YnI+CgojIyBDcm9zc1JlZgoKCkVsIHBhcXVldGUgYHJjcm9zc3JlZmAgcGVybWl0ZSBhY2NlZGVyIGEgdmFyaWFzIGRlIGxhcyBBUElzIGRlIFtDcm9zc1JlZl0oaHR0cDovL3NlYXJjaC5jcm9zc3JlZi5vcmcvKS4gwr9RdWUgcXXDqSBlcyBDcm9zc1JlZj8gUHVlcyBlcyB1biBzZXJ2aWNpbyBxdWUgcGVybWl0ZSwgZW50cmUgb3RyYXMgY29zYXMsIGZhY2lsaXRhciBlbCBwcm9jZXNvIGRlIHJlZmVyZW5jaWFyIGFydMOtY3Vsb3MgZW4gdHVzIHBhcGVycy4gW0FxdcOtXShodHRwOi8vd3d3Lm5lb3NjaWVudGlhLmNvbS9jcm9zc3JlZi8pIGxvIGV4cGxpY2FuLiBIYXkgb3RybyBwYWNrYWdlIHBhcmEgYWNjZWRlciBhIENyb3NzUmVmOiBbYGNybWluZXJgXShodHRwczovL2dpdGh1Yi5jb20vcm9wZW5zY2kvY3JtaW5lcikgZXN0ZSBwa2cgcGVybWl0ZSBiYWphcnNlIGVsIHRleHRvIGRlbCBkb2N1bWVudG8sIHBlcm8gY2xhcm8sIGVsIHRleHRvIGhhIGRlIGVzdGFyIGRpc3BvbmlibGUhIQoKCjxicj4KCmBgYHtyLCBlY2hvID0gVFJVRSwgZXZhbCA9IEZBTFNFfQojaW5zdGFsbC5wYWNrYWdlcygicmNyb3NzcmVmIikKbGlicmFyeSgicmNyb3NzcmVmIikKCiMtLS0tLSBjb24gY3JfY24oKSBwb2RlbW9zIHZlciBjb21vIHNlIGNpdGEgdW4gZGV0ZXJtaW5hZG8gYXJ0w61jdWxvIGVuIHVuIGRldGVybWluYWRvIGZvcm1hdG8sIHBvciBlamVtcGxvICJhcGEiCm15X2RvaSA8LSAiMTAuMTExMS9qLjE0NjctNjQ4Ni4yMDEyLjAxMDcyLngiCmNyX2NuKGRvaXMgPSBteV9kb2ksIGZvcm1hdCA9ICJ0ZXh0Iiwgc3R5bGUgPSAiYXBhIikKCmNyX2NuKGRvaXMgPSBteV9kb2ksIGZvcm1hdCA9ICJiaWJ0ZXgiLCBzdHlsZSA9ICJhcGEiLCBsb2NhbGUgPSAiZW4tVVMiLCByYXcgPSBGQUxTRSwgcHJvZ3Jlc3MgPSAibm9uZSIpCgojLS0tLS0tIGNvbiBjcl9jaXRhdGlvbl9jb3VudCgpIHB1ZWRlcyB2ZXIgZWwgbnVtZXJvIGRlIGNpdGFzIGRlIHVuIGFydMOtY3Vsby9ET0kKYWEgPC0gY3JfY2l0YXRpb25fY291bnQoZG9pID0gbXlfZG9pKQoKIy0tLS0tLSBjb24gY3JfYWJzdHJhY3QoKQphYSA8LSBjcl9hYnN0cmFjdChkb2kgPSAiMTAuMTEwOS9UQVNDLjIwMTAuMjA4ODA5MSIpCgojLS0tLS0tIGNvbiBjcl9qb3VybmFscygpIHZlbW9zIGpvdXJuYWxzCmFhIDwtIGNyX2pvdXJuYWxzKHF1ZXJ5ID0gImVjb25vbWljcyIsIGxpbWl0ID0gMTAwKSAlPiUgLiRkYXRhICU+JSBhcy50aWJibGUoKQoKIy0tLS0tLSBtdWNoYSBpbmZvcm1hY2lvbiBkZWwgYXJ0aWN1bG8KYWEgPC0gY3Jfd29ya3MoZG9pcyA9IG15X2RvaSkgJT4lIC4kZGF0YSAlPiUgYXMudGliYmxlKCkKYGBgCgoKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKPGJyPgoKCgoKIyMgT3Ryb3MgcGtnIGZvciBBUElzCgo8YnI+CgpIYXkgbXVjaG9zIG90cm9zIHBhcXVldGVzIGRlIFIgaGVjaG9zIHBhcmEgYWNjZWRlciBhIEFQSXMgKHR3aXR0ZXIsIEVDQiwgc3BvdGlmeSwgcGRmZXRjaCwgbmF0dXJhbGVhcnRoLCAuLi4uKS4gUHVlZGVzIHZlciBhbGd1bm9zIFthcXXDrV0oaHR0cHM6Ly9ydmlld3MucnN0dWRpby5jb20vMjAxNy8xMS8wMS9yLWRhdGEtcGFja2FnZXMvKSwgW2FxdcOtXShodHRwczovL3d3dy5yLWJsb2dnZXJzLmNvbS9yLXBhY2thZ2VzLWZvci1kYXRhLWFjY2Vzcy8pIHkgW2FxdcOtXShodHRwczovL3d3dy5yLWJsb2dnZXJzLmNvbS9uZXctZGF0YS1zb3VyY2VzLWZvci1yLykuCgoKW0FxdcOtXShodHRwczovL2dpdGh1Yi5jb20vcm9wZW5zY2kvd2Vic2VydmljZXMpIHB1ZWRlcyB2ZXIgdW4gbGlzdGFkbyBlbm9ycnJycm1lYHIgZW1vOjpqaSgibXVuY2giKWBlZWVlOiBQaW50ZXJlc3QsIEluc3RhZ3JhbSwgR29vZ2xlVHJlbmRzLCBHb29nbGUgQW5hbHl0aWNzLCBGbGlja3IsIC4uLi4sIC4uLi4sIC4uLi4KCgpVbmEgZGUgbGFzIHVsdGltYXMgcXVlIGhlIHZpc3RvIGhhIHNpZG8gZWwgcGtnIFtzcG9vY10oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL3Nwb2NjL2luZGV4Lmh0bWwpLiBFbiBzdSBbdmlnbmV0dGVdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9zcG9jYy92aWduZXR0ZXMvc3BvY2NfdmlnbmV0dGUuaHRtbCkgbm9zIGRpY2VuIHF1ZSBzZSBwdWVkZW4gYWNjZWRlciBhIHVuIGNvbmp1bnRvIGRlIHBhcXVldGVzIHF1ZSBjb250aWVuZW46ICJzb21lIGZvcm0gb2YgYmlvZGl2ZXJzaXR5IG9yIHRheG9ub21pYyBkYXRhLiBTaW5jZSBzZXZlcmFsIG9mIHRoZXNlIGRhdGFzZXRzIGhhdmUgYmVlbiBnZW9yZWZlcmVuY2VkLCBpdCBwcm92aWRlcyBudW1lcm91cyBvcHBvcnR1bml0aWVzIGZvciB2aXN1YWxpemluZyBzcGVjaWVzIGRpc3RyaWJ1dGlvbnMiIAoKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKPGJyPgoKIyMgU2NyYXBwaW5nIHRhYmxlcwoKQWRlbcOhcyBkZSB1dGlsaXphciBwYXF1ZXRlcyBwYXJhIGFjY2VkZXIgYSBzZXJ2aWNpb3Mgd2ViIGEgdHJhdsOpcyBkZSBzdXMgQVBJcywgcG9kZW1vcyB1c2FyIG90cm9zIHBhcXVldGVzIChwcmluY2lwYWxtZW50ZSBgcnZlc3RgKSBwYXJhIGhhY2VyIHdlYiBzY3JhcHBpbmcuIFB1ZWRlcyB2ZXIgZWplbXBsb3MgW2FxdcOtXShodHRwOi8vemV2cm9zcy5jb20vYmxvZy8yMDE1LzA1LzE5L3NjcmFwZS13ZWJzaXRlLWRhdGEtd2l0aC10aGUtbmV3LXItcGFja2FnZS1ydmVzdC8pLCBbYXF1w61dKGh0dHBzOi8vd3d3LnItYmxvZ2dlcnMuY29tL2ZhbnRhc3ktaG9ja2V5LXdpdGgtcnZlc3QtYW5kLXB1cnJyLyksIFthcXXDrV0oaHR0cHM6Ly93d3cuYW5hbHl0aWNzdmlkaHlhLmNvbS9ibG9nLzIwMTcvMDMvYmVnaW5uZXJzLWd1aWRlLW9uLXdlYi1zY3JhcGluZy1pbi1yLXVzaW5nLXJ2ZXN0LXdpdGgtaGFuZHMtb24ta25vd2xlZGdlLykgbwpbYXF1w61dKGh0dHBzOi8vcmF5bXMuZ2l0aHViLmlvLzIwMTgtMDEtMTMtY2l2aWwtcG9saXRpY2FsLXJpZ2h0cy8pLgoKCgo8YnI+CgpbQXF1w61dKGh0dHBzOi8vYWxtZXJpYXJ1c2Vycy53b3JkcHJlc3MuY29tLzIwMTYvMTAvMTEvd2ViLXNjcmFwaW5nLXBjYS15LWstbWVhbnMtcGFyYS1zYWNhci10b2RvLWVsLXBvdGVuY2lhbC1hLWxhbGlnYS8/dXRtX3NvdXJjZT1kbHZyLml0JnV0bV9tZWRpdW09dHdpdHRlcikgdGVuw6lpcyB1biBlamVtcGxvIHNlbmNpbGxvIHBhcmEgYmFqYXIgZGF0b3MgZGUganVyZ29sIC4uLiBwZXJvIHlhIG5vIGZ1bmNpb25hCgo8YnI+CgpgYGB7ciwgZWNobyA9IFRSVUUsIGV2YWwgPSBGQUxTRX0KbGlicmFyeShYTUwpCgp1cmwgPC0gImh0dHA6Ly93d3cuY29tdW5pYXpvLmNvbS9jb211bmlvL2p1Z2Fkb3JlcyIKdXJsIDwtICJodHRwczovL3d3dy5jb211bmlhem8uY29tL2NvbXVuaW8vanVnYWRvcmVzIgoKanVnYWRvcmVzIDwtICByZWFkSFRNTFRhYmxlKHVybCwgc3RyaW5nc0FzRmFjdG9ycyA9IFQsIGNvbG5hbWVzID0gYygiUG9zaWNpb24iLCJFcXVpcG8iLCJKdWdhZG9yIiwiUHVudG9zIiwiTWVkaWEiLCJQdW50b3NfQ2FzYSIsIk1lZGlhX0Nhc2EiLCJQdW50b3NfRnVlcmEiLCJNZWRpYV9mdWVyYSIsICJWYWxvciIpLCBjb2xDbGFzc2VzID0gYygiY2hhcmFjdGVyIiwiY2hhcmFjdGVyIiwiY2hhcmFjdGVyIiwiRm9ybWF0dGVkTnVtYmVyIiwiRm9ybWF0dGVkTnVtYmVyIiwiRm9ybWF0dGVkTnVtYmVyIiwiRm9ybWF0dGVkTnVtYmVyIiwiRm9ybWF0dGVkTnVtYmVyIiwiRm9ybWF0dGVkTnVtYmVyIikpCgphYSA8LSBqdWdhZG9yZXNbWzFdXSAlPiUgYXMudGliYmxlKCkKYGBgCgoKCgpQYXJhIHN1c3RpdHVpciBlbCBlamVtcGxvIGRlbCBqdXJnb2wgYmFqZW1vcyB1bmEgdGFibGEgZGUgbGEgd2lraXBlZGlhLiBFc3RlIGVqZW1wbG8gZXN0w6Egc2FjYWRvIGRlIFtlc3RlIHBvc3RdKGh0dHBzOi8vcmZsb3dlcnM1Lm5ldGxpZnkuY29tLzIwMTgvMDEvMTcvYWx0aXR1ZC1kZS1sb3MtbXVuaWNpcGlvcy1kZS10ZXJ1ZWwvKQoKCmBgYHtyLCBlY2hvID0gVFJVRSwgZXZhbCA9IFRSVUV9CmxpYnJhcnkoInJ2ZXN0IikKbGlicmFyeSgidGlkeXZlcnNlIikKY29udGVudCA8LSByZWFkX2h0bWwoImh0dHBzOi8vZXMud2lraXBlZGlhLm9yZy93aWtpL0FuZXhvOk11bmljaXBpb3NfZGVfbGFfcHJvdmluY2lhX2RlX1RlcnVlbCIpCgpib2R5X3RhYmxlIDwtIGNvbnRlbnQgJT4lIGh0bWxfbm9kZXMoJ2JvZHknKSAgJT4lCiAgICAgICAgICAgICAgICAgICAgaHRtbF9ub2RlcygndGFibGUnKSAlPiUKICAgICAgICAgICAgICAgICAgICBodG1sX3RhYmxlKGRlYyA9ICIsIikgClRlcnVlbCA8LSBib2R5X3RhYmxlW1sxXV0KbmFtZXMoVGVydWVsKSA8LSBjKCJOb21icmUiLCAiRXh0ZW5zaW9uIiwgIlBvYmxhY2lvbiIsICJEZW5zaWRhZCIsICJDb21hcmNhIiwgIlBhcnRpZG9fanVkaWNpYWwiLCAiQWx0aXR1ZCIpCmxpYnJhcnkoc3RyaW5ncikKVGVydWVsIDwtIFRlcnVlbCAlPiUgbWFwKHN0cl90cmltKSAlPiUgYXNfdGliYmxlKCkgIy0gcXVpdGEgY2FyYWN0ZXJlcyBhbCBmaW5hbApUZXJ1ZWwgPC0gVGVydWVsICU+JSBtdXRhdGUoQWx0aXR1ZCA9IHN0cl9yZXBsYWNlX2FsbChBbHRpdHVkLCJbWzpwdW5jdDpdXSIsICIiKSkgClRlcnVlbCA8LSBUZXJ1ZWwgJT4lIG11dGF0ZShBbHRpdHVkID0gYXMuZG91YmxlKEFsdGl0dWQpKSAlPiUgYXJyYW5nZShkZXNjKEFsdGl0dWQpKQpgYGAKCgoKCmBgYHtyfQpsaWJyYXJ5KGthYmxlRXh0cmEpCmFhIDwtIFRlcnVlbCAlPiUgc2VsZWN0KDEsMyw1LDcpICU+JSAgc2xpY2UoMTo0KSAKI2tuaXRyOjprYWJsZShhYSwgZGlnaXRzID0gMiwgYWxpZ24gPSAiYyIsIGNhcHRpb24gPSAiTG9zIDQgbXVuaWNpcGlvcyBkZSBUZXJ1ZWwgY29uIG3DoXMgYWx0aXR1ZCIgKQprbml0cjo6a2FibGUoYWEsICJodG1sIiwgZGlnaXRzID0gMiwgIGNhcHRpb24gPSAiTG9zIDQgbXVuaWNpcGlvcyBkZSBUZXJ1ZWwgY29uIG3DoXMgYWx0aXR1ZCIpICU+JQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIikpCmBgYAoKCjxicj4KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgo8YnI+CgoKIyBCaWJsaW9ncmFmw61hCgotIFtUaGlzIFIgRGF0YSBJbXBvcnQgVHV0b3JpYWwgSXMgRXZlcnl0aGluZyBZb3UgTmVlZF0oaHR0cHM6Ly93d3cuZGF0YWNhbXAuY29tL2NvbW11bml0eS90dXRvcmlhbHMvci1kYXRhLWltcG9ydC10dXRvcmlhbCNncy5wcGE2YUZnKS4gQXPDrSBlcyBjb21vIHNlIGxsYW1hIGVzdGUgdHV0b3JpYWwgZGUgRGF0YWNhbXAuIEVzIHNlbmNpbGxvIHkgbm8gbXV5IGxhcmdvLiBUaWVuZSBtdXkgYnVlbmEgcGludGEuIEVuIGdlbmVyYWwgdXRpbGl6YSBmdW5jaW9uZXMgZGUgYmFzZS1SLiBMbyBoYW4gYWN0dWFsaXphZG8gaGFjZSA0LTUgZMOtYXMuIE5vIGhlIHRlbmlkbyB0aWVtcG8gZGUgdmVyIHF1ZSBoYSBjYW1iaWFkbywgcGVybyBzZWd1cm8gcXVlIGEgbWVqb3IuCgotIExhIFtjaGVhdCBTaGVldCBkZSBkYXRhIGltcG9ydCBkZSBSU3R1ZGlvXShodHRwczovL3d3dy5yc3R1ZGlvLmNvbS9yZXNvdXJjZXMvY2hlYXRzaGVldHMvKS4gTXV5IGJ1ZW4gcmVzdW1lbi9jaHVsZXRhCgotIExhIFtzZWNjacOzbiBkZSBpbXBvcnRhciBkYXRvcyBkZSBRdWljay1SXShodHRwOi8vd3d3LnN0YXRtZXRob2RzLm5ldC9pbnB1dC9pbXBvcnRpbmdkYXRhLmh0bWwpLiBDb25jaXNvIHBlcm8gbXV5IGNsYXJvLiBRdWljay1SIGVzIG11eSBidWVuIHJlY3Vyc28gcGFyYSBhcHJlbmRlciBSLgoKCi0gW0xhIGRvY3VtZW50YWNpb24gb2ZpY2lhbF0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvZG9jL21hbnVhbHMvci1yZWxlYXNlL1ItZGF0YS5odG1sKSBkZSBSCgoKLSBbVW4gcG9zdCBzb2JyZSBBUElzIHkgUl0oaHR0cHM6Ly93d3cuci1ibG9nZ2Vycy5jb20vYWNjZXNzaW5nLWFwaXMtZnJvbS1yLWFuZC1hLWxpdHRsZS1yLXByb2dyYW1taW5nLykuIFN1cG9uZ28gcXVlIGNvbXBsaWNhZG8uCgoKLSBEb3MgcG9zdCBxdWUgaGFjZW4gdXNvIGRlIHdlYiBzY3JhcHBpbmc6IFtlc3RlXShodHRwczovL3JheW1zLmdpdGh1Yi5pby8yMDE4LTAxLTEzLWNpdmlsLXBvbGl0aWNhbC1yaWdodHMvKSAgeSBbZXN0ZV0oaHR0cHM6Ly9kYXRhc2NpZW5jZXBsdXMuY29tL3dlYi1zY3JhcGluZy1hbmQtYXBwbGllZC1jbHVzdGVyaW5nLWdsb2JhbC1oYXBwaW5lc3MtYW5kLXNvY2lhbC1wcm9ncmVzcy1pbmRleC8pLiBUYW1iacOpbiBwdWVkZSBxdWUgY29tcGxpY2FkbywgcGVybyBzb24gZG9zIGVqZW1wbG9zIGRlIGFuw6FsaXNpcyBkZSBkYXRvcyAoY29tcGxldG8pIGNvbiBSLgoKCgoK