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.
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:
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:
Formatos propios de R
- R objects:
.RData
o .rda
- Serialized R objects:
.rds
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)
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 = " ")
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
|
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