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
|
LS0tCnRpdGxlOiAiSW1wb3J0YXIgKHkgZXhwb3J0YXIpIGRhdG9zIGNvbiBSIgphdXRob3I6ICJQZWRybyBKLiBQw6lyZXogKHBlZHJvLmoucGVyZXpAdXYuZXMpLiBVbml2ZXJzaXRhdCBkZSBWYWzDqG5jaWEgIDxicj4gPGJyPiBXZWIgZGVsIGN1cnNvOiA8aHR0cHM6Ly9wZXJlenA0NC5naXRodWIuaW8vaW50cm8tZHMtMjItMjMtd2ViLz4iCmRhdGU6ICJOb3ZpZW1icmUgZGUgMjAxNyAoYWN0dWFsaXphZG8gZWwgYHIgZm9ybWF0KFN5cy50aW1lKCksICclZC0lbS0lWScpYCkiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgY3NzOiAhZXhwciBoZXJlOjpoZXJlKCJhc3NldHMiLCAic3R5bGVzX3BqcC5jc3MiKSAjLWh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzU2NjgxODc5L2hvdy10by11c2UtaGVyZS1mb3ItcGF0aHMtdG8tY3NzLWJlZm9yZS1ib2R5LWFuZC1hZnRlci1ib2QKICAgIHRoZW1lOiBwYXBlcgogICAgaGlnaGxpZ2h0OiB0ZXh0bWF0ZQogICAgdG9jOiB0cnVlCiAgICB0b2NfZGVwdGg6IDMKICAgIHRvY19mbG9hdDoKICAgICAgY29sbGFwc2VkOiB0cnVlCiAgICAgIHNtb290aF9zY3JvbGw6IHRydWUKICAgIHNlbGZfY29udGFpbmVkOiB0cnVlCiAgICBudW1iZXJfc2VjdGlvbnM6IGZhbHNlCiAgICBpbmNsdWRlczoKICAgICAgYWZ0ZXJfYm9keTogIWV4cHIgaGVyZTo6aGVyZSgiYXNzZXRzIiwgImZvb3Rlci5odG1sIikgCiAgICAgIGluX2hlYWRlcjogCiAgICAgICAgLSAhZXhwciBoZXJlOjpoZXJlKCJhc3NldHMiLCAiZ29vZ2xlLWFuYWx5dGljcy5odG1sIikgCiAgICAgICAgLSAhZXhwciBoZXJlOjpoZXJlKCJhc3NldHMiLCAiZmF2aWNvbi1zb2wuaHRtbCIpCiAgICBkZl9wcmludDoga2FibGUKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKZWRpdG9yX29wdGlvbnM6CiAgY2h1bmtfb3V0cHV0X3R5cGU6IGNvbnNvbGUKI2JpYmxpb2dyYXBoeTogImByIGhlcmU6OmhlcmUoJ2Fzc2V0cycsICdiaWJsaW8uYmliJylgIiAgIy0gam9vb2Rlci4gc2luZ2xlIHF1b3RlcyBodHRwczovL2NvbW11bml0eS5yc3R1ZGlvLmNvbS90L3VzZS1oZXJlLWhlcmUtZnVuY3Rpb24taW4teWFtbC1vcHRpb24vMTg2NjcvOQotLS0KCmBgYHtyLCBpbmNsdWRlID0gRkFMU0V9CmxpYnJhcnkodGlkeXZlcnNlKQpgYGAKCmBgYHtyIGNodW5rLXNldHVwLCBpbmNsdWRlID0gRkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgZXZhbCA9IFRSVUUsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgI3Jlc3VsdHMgPSAiaG9sZCIsCiAgICAgICAgICAgICAgICAgICAgICBjYWNoZSA9IEZBTFNFLCBjYWNoZS5wYXRoID0gIi9jYWNoZXMvIiwgY29tbWVudCA9ICIjPiIsCiAgICAgICAgICAgICAgICAgICAgICAjZmlnLndpZHRoID0gNywgI2ZpZy5oZWlnaHQ9IDcsCiAgICAgICAgICAgICAgICAgICAgICAjb3V0LndpZHRoID0gNywgb3V0LmhlaWdodCA9IDcsCiAgICAgICAgICAgICAgICAgICAgICBjb2xsYXBzZSA9IFRSVUUsICBmaWcuc2hvdyA9ICJob2xkIiwKICAgICAgICAgICAgICAgICAgICAgIGZpZy5hc3AgPSA3LzksIG91dC53aWR0aCA9ICI2MCUiLCBmaWcuYWxpZ24gPSAiY2VudGVyIikKCiMtIHBhcmEgbWVqb3JhciBsb3MgZ3LDoWZpY29zLCBidWVubyBlbiByZWFsaWRhZCBwYXJhIHF1ZSBzZSB2ZWFuIGlndWFsIGVuIGRpc3RpbnRvcyBTTwojLSBodHRwczovL3d3dy5qdW1waW5ncml2ZXJzLmNvbS9ibG9nL3Ita25pdHItbWFya2Rvd24tcG5nLXBkZi1ncmFwaGljcy8Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGRldiA9ICJwbmciLCBkZXYuYXJncyA9IGxpc3QodHlwZSA9ICJjYWlyby1wbmciKSkKYGBgCgpgYGB7ciBvcHRpb25zLXNldHVwLCBpbmNsdWRlID0gRkFMU0V9Cm9wdGlvbnMoc2NpcGVuID0gOTk5KSAjLSBwYXJhIHF1aXRhciBsYSBub3RhY2nDs24gY2llbnTDrWZpY2EKb3B0aW9ucygieWFtbC5ldmFsLmV4cHIiID0gVFJVRSkgIy0gaHR0cHM6Ly9naXRodWIuY29tL3Zpa2luZy9yLXlhbWwvaXNzdWVzLzQ3ICAobG8gcHVzZSB4IGVsIHBiIGNvbiBlbCB3YXJuaW5nKSBFbiByZWFsaWRhZCBjcmVvIHF1ZSBtZWpvciBzZXLDrWEgcG9uZXJsbyBlbiBSUHJvZmlsZQpgYGAKCmBgYHtyIGtsaXBweSwgZWNobyA9IEZBTFNFfQprbGlwcHk6OmtsaXBweShwb3NpdGlvbiA9IGMoInRvcCIsICJyaWdodCIpKSAjLSByZW1vdGVzOjppbnN0YWxsX2dpdGh1Yigicmxlc3VyL2tsaXBweSIpCmBgYAoKCi0tLS0tLS0tLS0tLS0tLS0tCgo8YnI+CgojIDEuIEludHJvCgo8YnI+CgpZYSBzYWJlbW9zIHF1ZSBSIGVzIHVuIGxlbmd1YWplIGRlIHByb2dyYW1hY2nDs24gb3JpZW50YWRvIGFsIGFuw6FsaXNpcyBkZSBkYXRvcy4gTG8gcHJpbWVybyBxdWUgdGVuZW1vcyBxdWUgaGFjZXIgcGFyYSBlbXBlemFyIHVuIGFuw6FsaXNpcyBjb24gZGF0b3MgZW4gUiBlcywgZXZpZGVudGVtZW50ZSwgY2FyZ2FyIGxvcyBkYXRvcyBlbiBSLiBFbiByZWFsaWRhZCBlbiBlc3RlIHR1dG9yaWFsIGFwcmVuZGVyZW1vcyBhICoqaW1wb3J0YXIgeSBleHBvcnRhciBkYXRvcyBlbiBkaWZlcmVudGVzIGZvcm1hdG9zKiouCgoKSGF5IGRhdG9zIGRlIG11Y2hvcyB0aXBvcyB5IGVuIG11Y2hvcyBmb3JtYXRvczogaW3DoWdlbmVzLCB0ZXh0bywgLi4uICwgcGVybyBlbiBlbCBjdXJzbyBub3MgY2VudHJhcmVtb3MgZW4gY29uanVudG9zIGRlIGRhdG9zIHF1ZSBwdWVkZW4gYWxtYWNlbmFyc2UgZW4gaG9qYXMgZGUgY2FsY3VsbywgeWEgcXVlIGVzdGEgZXMgbGEgZm9ybWEgaGFiaXR1YWwgZGUgdHJhYmFqYXIgY29uIGRhdG9zIGVuIGxhcyBjaWVuY2lhcyBzb2NpYWxlcy4KCjxicj4KClV0aWxpemFuZG8gdW4gZGlhZ3JhbWEgZGUgW2VzdGUgZmFudMOhc3RpY28gbGlicm9dKGh0dHA6Ly9yNGRzLmhhZC5jby5uei8pLCBlc3RhbW9zIGVuIGxhIGNhc2lsbGEgZGUgc2FsaWRhIGRlIGN1YWxxdWllciBhbsOhbGlzaXMgZGUgZGF0b3MuCgoKYGBge3IsICBlY2hvID0gRkFMU0UsIGV2YWwgPSBUUlVFLCBmaWcuY2FwID0gIlByaW1lcmEgZXRhcGE6IEltcG9ydGFyIGRhdG9zIChodHRwOi8vcjRkcy5oYWQuY28ubnovKSIsIGZpZy5hc3AgPSA0LzIsIG91dC53aWR0aCA9ICI4MCUiLCBmaWcuYWxpZ24gPSAiY2VudGVyIn0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1hZ2VuZXMiLCAidHRfMDRfaW1nXzAxX2Nhcmdhci1kYXRvcy5wbmciKSkKYGBgCgoKClBhcmEgaW1wb3J0YXIvZXhwb3J0YXIgZGF0b3MgdmFtb3MgYSB1c2FyIGZ1bmNpb25lcyBkZSB2YXJpb3MgcGFja2FnZXMsIGFzw60gcXVlIHRlbmVtb3MgcXVlIHNhYmVyIGNvbW8gYWNjZWRlciBhIHN1IGRvY3VtZW50YWNpw7NuLCBwZXJvIGVzdG8geWEgc2UgdmlvIGVuIGVsIHR1dG9yaWFsIHNvYnJlIFItYmFzZS4KCgoKPGJyPgoKQ2FyZ2FyIGRhdG9zIGVzIHVuYSBkZSBsYXMgcHJpbWVyYXMgZnJ1c3RyYWNpb25lcyBkZSBhbGd1aWVuIHF1ZSBjb21pZW56YSBhIGFwcmVuZGVyIFIuIEdlbmVyYWxtZW50ZSBwaWVuc2FuOiBwZXJvIHNpIGVuIEV4Y2VsL1NTUFNTIHPDs2xvIHRlbmdvIHF1ZSBwaW5jaGFyIGVuIGVsIGZpY2hlcm8hISBDb21vIG11Y2hvIHRlbmdvIHF1ZSB1c2FyIGxvcyBtZW7DunMgZGVzcGxlZ2FibGVzISEgCkVuIFIgZXN0byB0YW1iacOpbiBlcyBwb3NpYmxlOiBSIHRpZW5lIDIgZm9ybWF0b3MgZGUgZGF0b3MgcHJvcGlvcyBxdWUgc2UgYWJyZW4gc2ltcGxlbWVudGUgaGFjaWVuZG8gZG9ibGUgY2xpY2sgeSBsYSDDumx0aW1hIHZlcnNpw7NuIGRlIFJTdHVkaW8gdGFtYmnDqW4gcGVybWl0ZSBjYXJnYXIgZGF0b3MgYSB0cmF2w6lzIGRlIG1lbsO6czsgcGVybyAuLi4uIG5vIG9zIGFjb3Jkw6FpcyBkZSBsYSBJbnZlc3RpZ2FjacOzbiBSZXByb2R1Y2libGUhISAKCgo8YnI+CgojIyMjICBSU3R1ZGlvIHBlcm1pdGUgY2FyZ2FyIGRhdG9zIGEgdHJhdsOpcyBkZSBtZW7DunMsIHBlcm8gLi4uCgpSU3R1ZGlvIHBlcm1pdGUgY2FyZ2FyIGRhdG9zIGEgdHJhdsOpcyBkZSBtZW7DunMgKGAgRmlsZSA+IEltcG9ydCBEYXRhc2V0IGApLiBQb3IgbWVuw7pzIHNlIHB1ZWRlbiBjYXJnYXIgZGF0b3MgQ1NWLCBFWENFTCwgU1BTUywgU0FTIHkgU1RBVEEuIEVuIGVsIGN1cnNvIHBlbnNhbW9zIHF1ZSBoYXkgcXVlIGhhY2VybG8gdG9kbyBhIHRyYXbDqXMgZGUgc2NyaXB0czsgcG9yIGxvIHRhbnRvLCBubyB1c2FyZW1vcyBsb3MgbWVuw7pzLgoKQWwgdXNhciBsb3MgbWVuw7pzIGRlIFJTdHVkaW8gcGFyYSBpbXBvcnRhciBkYXRvcyBlbiByZWFsaWRhZCBzZSBlc3TDoSBsbGFtYW5kbyBhIHVuYXMgZnVuY2lvbmVzIHF1ZSBzb24gbGFzIHF1ZSBpbXBvcnRhbiByZWFsbWVudGUgbG9zIGRhdG9zOyBhZGVtYXMsIHBhcmEgaW1wb3J0YXIgZGF0b3MgYSB0cmF2w6lzIGRlIGxvcyBtZW7DunMsIFJTdHVkaW8gbm8gdXNhIGxhcyBmdW5jaW9uZXMgZGUgUi1iYXNlIHNpbm8gbGFzIGZ1bmNpb25lcyBkZSBkb3MgcGFja2FnZXMgKipgcmVhZHJgKiogeSAqKmBoYXZlbmAqKi4gRW4gZWwgY3Vyc28gc2VndWlyZW1vcyBlc3RlIGVuZm9xdWUgeSB1c2FyZW1vcyBgcmVhZHJgIHkgYGhhdmVuYCwgYWRlbcOhcyBkZSBhbGfDum4gb3RybyBwa2csIHBhcmEgaW1wb3J0YXIgeSBleHBvcnRhciBkYXRvcy4KCkhhcmVtb3MgdW4gcG9jbyBtw6FzIGRlIMOpbmZhc2lzIGVuIGxhIGltcG9ydGFjacOzbiBkZSBkYXRvcyB5YSBxdWUgc2kgdXNhcyBSLCBsbyBub3JtYWwgZXMgaGFjZXIgdG9kbyBlbCBhbsOhbGlzaXMgKGluY2x1c28gbGEgZ2VuZXJhY2nDs24gZGUgaW5mb3JtZXMpIGVuIGVsIGVudG9ybm8gUi4gCgo8YnI+CgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgo8YnI+CgoKIyMjIyDCv1BvciBxdcOpIG5vIHVzYXIgUi1iYXNlPyBbT1BDSU9OQUxdCgpZYSBoZW1vcyBkaWNobyBxdWUgUlN0dWRpbyBjYXJnYSBkYXRvcyBhIHRyYXbDqXMgZGUgbWVuw7pzLCBwZXJvIG5vIHV0aWxpemEgbGFzIGZ1bmNpb25lcyBkZSBSLWJhc2UsIHNpbm8gZGUgb3Ryb3MgcGFxdWV0ZXMsIGNvbmNyZXRhbWVudGUgYHJlYWRyYCB5IGBoYXZlbmAKClIgdGllbmUgeWEgdW5vcyAyMCBhw7Fvcy4gTGFzIGZ1bmNpb25lcyBkZSBSLWJhc2Ugc2UgY29uc3RydXllcm9uIHBlbnNhbmRvIGVuIGxvcyBlc3RhZMOtc3RpY29zIGRlIGhhY2UgMjAgYcOxb3MgKGhveSBzZSBsbGFtYXLDrWFuIGFuYWxpc3RhcyBkZSBkYXRvcykuIE1vZGlmaWNhciBsYXMgZnVuY2lvbmVzIGRlIFItYmFzZSBoYXLDrWEgcXVlIGPDs2RpZ28gYW50aWd1byBkZWphc2UgZGUgZnVuY2lvbmFyLCBhc8OtIHF1ZSBsYSBtYXlvcsOtYSBkZSBhdmFuY2VzIHkgbWVqb3JhcyBzZSBwcm9kdWNlbiBlbiBsb3MgcGFja2FnZXMuIAoKTGFzIGZ1bmNpb25lcyBkZSBgcmVhZHJgIHRyYXRhbiBkZSBzZXIgbG8gbWFzIHBhcmVjaWRhcyBhIGxhcyBmdW5jaW9uZXMgZXF1aXZhbGVudGVzIGRlIFItYmFzZSBwZXJvIGVuIGNpZXJ0byBzZW50aWRvIG1lam9yw6FuZG9sYXMgeSBoYWNpw6luZG9sYXMgbcOhcyBjb25zaXN0ZW50ZXM7IHBvciBlamVtcGxvIHBhcmEgbGVlciBkYXRvcyBDU1YgbGEgZnVuY2nDs24gZGUgUi1iYXNlIGVzIGByZWFkLmNzdigpYDsgbWllbnRhcyBxdWUgbGEgZnVuY2nDs24gZXF1aXZhbGVudGUgZGUgInJlYWRyIiBlcyBgcmVhZF9jc3YoKWAuIExhcyBkb3MgaGFjZW4gbG8gbWlzbW8sIGxlZXIgZGF0b3MgZW4gZm9ybWF0byBDU1YsIHBlcm8gbGFzIG51ZXZhcyBmdW5jaW9uZXMgdGllbmVuIGFsZ3VuYXMgdmVudGFqYXM6ICAKCiAgLSBTb24gbcOhcyByw6FwaWRhcy4KICAKICAgLSBFbmNhamFuIG3DoXMgZW4gZWwgd29ya2Zsb3cvcGFyYWRpZ21hIGRlIGxhIGludmVzdGlnYWNpw7NuIHJlcHJvZHVjaWJsZS4gUG9yIGVqZW1wbG8sIGFsZ3VuYXMgZGUgbGFzIGZ1bmNpb25lcyBkZSBSLWJhc2UgaGVyZWRhbiBhbGd1bmFzIG9wY2lvbmVzIGRlbCBzaXN0ZW1hIG9wZXJhdGl2byB5IGxhcyB2YXJpYWJsZXMgZGUgZW50b3JubywgaGFjaWVuZG8gcG9zaWJsZSBxdWUgdW4gc2NyaXB0IHF1ZSBmdW5jaW9uYSBlbiB1biBvcmRlbmFkb3Igbm8gZnVuY2lvbmUgZW4gb3Ryby4gKEVzdG8gYcO6biBwdWVkZSBwYXNhcm5vcyBhIG5vc290cm9zIGVuIGVsIGN1cnNvLiBFc3BlcmVtb3MgcXVlIG5vISEpLgoKICAtIEVuIGx1Z2FyIGRlIGdlbmVyYXIgZGF0YS5mcmFtZXMsIHByb2R1Y2VuIHRpYmJsZXMuIExhcyB0aWJibGVzIHNvbiBlbiByZWFsaWRhZCBkYXRhLmZyYW1lcyBwZXJvIGNvbiBhbGd1bmFzIHBhcnRpY3VsYXJpZGFkZXMuIAogIAogIC0gTGFzIHRpYmJsZXMgbyAiZGF0YSBmcmFtZXMgdHVuZWFkb3MiIHRpZW5lbiB1bmFzIGNpZXJ0YXMgdmVudGFqYXM6IG5vIGNvbnZpZXJ0ZW4gcG9yIGRlZmVjdG8gdmVjdG9yZXMgZGUgdGV4dG8gZW4gZmFjdG9yZXMsIG5vIHVzYW4gcm93IG5hbWVzLCBuaSB0cmFuc2Zvcm1hbiBsb3MgY29sdW1uIG5hbWVzIChlc3TDoXMgMyBjb3NhcyBxdWUgc8OtIGhhY2VuIGxvcyAiZGF0YS5mcmFtZXMgdHJhZGljaW9uYWxlcyIgcHVlZGVuIHByb3ZvY2FyIGFsZ3VuYXMgY29tcGxpY2FjaW9uZXMsIGFzw60gcXVlIG1lam9yIHRlbmVyIGhlcnJhbWllbnRhcyBxdWUgbGFzIHNvcnRlZW4pLgoKCgo8YnI+CgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyMjIERhdG9zIHByZWNhcmdhZG9zIGVuIFIgW09QQ0lPTkFMXQoKUi1iYXNlIHZpZW5lIGNvbiBtdWNob3MgZGF0b3MgcHJlY2FyZ2Fkb3M7IGNvbmNyZXRhbWVudGUgZW4gZWwgcGtnIGRlIFItYmFzZSBsbGFtYWRvIGBkYXRhc2V0c2AuIEFkZW3DoXMgbXVjaG9zIHBhY2thZ2VzIGNvbnRpZW5lbiB0YW1iacOpbiBjb25qdW50b3MgZGUgZGF0b3MuIFBhcmEgdmVyIGxvcyBkYXRvcyBxdWUgdGVuZW1vcyBwcmVjYXJnYWRvcyB5IGRpc3BvbmlibGVzIGVuIFIgc2UgdXNhIGxhIGZ1bmNpw7NuIGBkYXRhKClgOgoKYGBge3IsIGVjaG8gPSBULCBldmFsID0gRn0KIy0gc2UgYWJyaXLDoSB1bmEgdmVudGFuYSBjb24gZWwgbGlzdGFkbyBkZSBkYXRvcyBkaXNwb25pYmxlcwpkYXRhKCkgIAojISEgZ3VhcmRhbW9zIGVsIGxpc3RhZG8gZGUgZGF0b3MgZW4gdW4gZGF0YS5mcmFtZSBsbGFtYWRvICJhYSIKYWEgPC0gYXMuZGF0YS5mcmFtZShkYXRhKClbWzNdXSkgCmBgYAoKPGJyPgoKU2kgcXVlcmVtb3MgdmVyIGxvcyBkYXRvcyBxdWUgaGF5IGVuIHVuIHBhY2thZ2UgY29uY3JldG8gdXNhcmVtb3MgYGRhdGEocGFja2FnZSA9ICJwa2dfbmFtZSIpYAoKYGBge3IsIGVjaG8gPSBULCBldmFsID0gRn0KIy0gdmVtb3MgZW4gdW5hIHZlbnRhbmEgZWwgbGlzdGFkbyBkZSBkYXRvcyBkaXNwb25pYmxlcyBlbiBlbCBwa2cgZ2dwbG90MgpkYXRhKHBhY2thZ2UgPSAiZ2dwbG90MiIpCiMhISBndWFyZGFtb3MgZWwgbGlzdGFkbyBkZSBkYXRvcyBkZWwgcGtnIGdncGxvdDIgZW4gZWwgZGYgImFhIgphYSA8LSBhcy5kYXRhLmZyYW1lKGRhdGEocGFja2FnZSA9ICJnZ3Bsb3QyIilbWzNdXSkgJT4lIHNlbGVjdCgtMikKIyEhIGd1YXJkYW1vcyBlbCBsaXN0YWRvIGRlIGRhdG9zIGRlbCBwa2cgZ2dwbG90MiBlbiB1bmEgdGliYmxlCmFhIDwtIGFzX3RpYmJsZShkYXRhKHBhY2thZ2UgPSAiZ2dwbG90MiIpW1szXV0pICU+JSBzZWxlY3QoLTIpIApgYGAKCgo8YnI+CgpQb3IgZWplbXBsbywgZWwgcGFja2FnZSBgZ2dwbG90MmAgdGllbmUgbG9zIHNpZ3VpZW50ZXMgY29uanVudG9zIGRlIGRhdG9zOgoKCmBgYHtyLCBlY2hvID0gRiwgZXZhbCA9IFR9CiMtIGd1YXJkYW1vcyBlbCBsaXN0YWRvIGRlIGRhdG9zIGRlbCBwa2cgZ2dwbG90MiBlbiB1bmEgdGliYmxlCmxpYnJhcnkodGlkeXZlcnNlKQphYSA8LSBhc190aWJibGUoZGF0YShwYWNrYWdlID0gImdncGxvdDIiKVtbM11dKSAlPiUgc2VsZWN0KC0yKSAKIy0gbW9zdHJhbW9zIGFhIGNvbW8gdGFibGEKa25pdHI6OmthYmxlKGFhKQpgYGAKCjxicj4KClBvZGVtb3MgdmVyIHRvZG9zIGxvcyBkYXRhc2V0cyBxdWUgaGF5IGVuIGxvcyBwYWNrYWdlcyBxdWUgdGVuZW1vcyBlbiBudWVzdHJhIGxpYnJlcsOtYSBkZSBwYWNrYWdlcyBkZSBudWVzdHJvIG9yZGVuYWRvcjoKCmBgYHtyLCBlY2hvID0gVFJVRSwgZXZhbCA9IEZBTFNFfQojICEhIGFicmUgdW5hIHZlbnRhbmEgZG9uZGUgc2UgdmUgZWwgbGlzdGFkbyBkZSB0b2RvcyBsb3MgZGF0YXNldHMgcXVlIGNvbnRpZW5lbiBsb3MgcGFja2FnZXMgZGUgbnVlc3RyYSBsaWJyZXLDrWEKZGF0YShwYWNrYWdlID0gLnBhY2thZ2VzKGFsbC5hdmFpbGFibGUgPSBUUlVFKSkKYGBgCgo8YnI+CgotLS0tLS0tLS0tLS0tLS0tLS0tCgo8YnI+CgoKIyAyLiBUaXBvcyBkZSBkYXRvcyBxdWUgdmVyZW1vcwoKPGJyPgoKSW50cm9kdWNpcmVtb3MgZnVuY2lvbmVzIHBhcmEgaW1wb3J0YXIvZXhwb3J0YXIgZGF0b3MgZGUgbG9zIHNpZ3VpZW50ZXMgZm9ybWF0b3M6Cgo8YnI+CgotIERhdG9zIGVuIGZvcm1hdG8gdGV4dG8gKG8gdGFidWxhcmVzKQoKICAgIC0gQ1NWOiBgLmNzdmAgKGNvbW1hIHNlcGFyYXRlZCB2YWx1ZXMgbyAsIGVuIGNhc3RlbGxhbm8sIGRhdG9zIHNlcGFyYWRvcyBwb3IgY29tYXMpCiAgICAtIG90cm9zIGRhdG9zIGVuIGZvcm1hdG8gdGV4dG8KICAgIAo8YnI+CgotICBGb3JtYXRvcyBkZSBvdHJvcyBwcm9ncmFtYXMgKHNvZnR3YXJlIHByb3BpZXRhcmlvKQoKICAgIC0gRVhDRUw6IGAueGxzYCB5IGAueGxzeGAKICAgIC0gU1BTUzogICBgLnNhdmAgeSBgLnBvcmAKICAgIC0gU1RBVEE6IGAuZHRhYAogICAgLSBTQVM6ICBgLnNhc2AKCjxicj4KCi0gRm9ybWF0b3MgcHJvcGlvcyBkZSBSCgogICAgLSBSIG9iamVjdHM6IGAuUkRhdGFgIG8gYC5yZGFgCiAgICAtIFNlcmlhbGl6ZWQgUiBvYmplY3RzOiBgLnJkc2AKCjxicj4KCi0gT3Ryb3MgRm9ybWF0b3MKCiAgICAtIEpTT04KICAgIC0gWE1MCiAgCjxicj4KICAKQWRlbcOhcyBhcHJlbmRlcmVtb3MgY29tbyBiYWphciBkYXRvcyBhIHRyYXbDqXMgZGUgQVBJczoKCiAgLSBFdXJvc3RhdAogIC0gSU5FCiAgLSBCYW5jbyBNdW5kaWFsCgoKCjxicj4KCi0tLS0tLS0tLS0tLS0tLS0KCgojIyMjIEVzdHJhdGVnaWEgcXVlIHNlZ3VpcmVtb3MgcGFyYSBhcHJlbmRlciBhIEltcG9ydGFyL0V4cG9ydGFyIGRhdG9zCgpMbyBxdWUgdmFtb3MgYSAgaGFjZXIgZW4gZXN0ZSB0dXRvcmlhbCBwYXJhIGFwcmVuZGVyIGEgaW1wb3J0YXIgKHkgZXhwb3J0YXIpIGRhdG9zIGVuIFIgZXMgZWxlZ2lyIHVuIGZpY2hlcm8gZGUgZGF0b3MgcHJlY2FyZ2FkbyBlbiBSIHkgZXhwb3J0YXJsbyBhIHVuIGRldGVybWluYWRvIGZvcm1hdG8gcGFyYSBsdWVnbyBpbXBvcnRhciBlbCBhcmNoaXZvIGdlbmVyYWRvIG8gZXhwb3J0YWRvLiBSZXBldGlyZW1vcyBlc3RvIHBhcmEgZGlzdGludG9zIGZvcm1hdG9zIGRlIGRhdG9zLiAKCkRhIGlndWFsIHF1ZSBhcmNoaXZvIGRlIGRhdG9zIHVzYXIsIGFzw60gcXVlIHV0aWxpemFyZW1vcyB1biBjb25qdW50byBkZSBkYXRvcyBmYW1vc28geSBxdWUgcGVzYSBwb2NvOiAiZWwgW2BpcmlzYF0oaHR0cHM6Ly9lcy53aWtpcGVkaWEub3JnL3dpa2kvSXJpc19mbG9yX2Nvbmp1bnRvX2RlX2RhdG9zKSBkYXRhc2V0IiBxdWUgZnVlIHV0aWxpemFkbyBwb3IgUm9uYWxkIEZpc2hlci4gSXJpcyBjb250aWVuZSAxNTAgb2JzZXJ2YWNpb25lcyBkZSA1IHZhcmlhYmxlczogbWVkaWNpb25lcyBkZSA1IGNhcmFjdGVyw61zdGljYXMgc29icmUgMTUwIGZsb3JlcyBkZSBsYSBlc3BlY2llIElyaXMuCgoKPGJyPgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMjIyMgwr9Dw7NtbyBwb2RlbW9zIHZlciBxdWUgdmFyaWFibGVzICh5IGRlIHF1ZSB0aXBvKSBoYXkgZW4gdW4gZGY/CgpTdXBvbmdhbW9zIHF1ZSB5YSBoZW1vcyBjYXJnYWRvIHVuIGNvbmp1bnRvIGRlIGRhdG9zIHkgcXVlIGVzdMOhIGFsbWFjZW5hZG8gZW4gdW4gZGYsIMK/Y8OzbW8gcG9kZW1vcyB2ZXIgcXVlIHZhcmlhYmxlcyAoeSBkZSBxdWUgdGlwbykgaGF5IGVuIGVsIGRmPwoKVmFtb3MgYSB2ZXIgbG9zIG5vbWJyZXMgZGUgbGFzIHZhcmlhYmxlcyAoY29sdW1uYXMpIGRlbCBkYXRhc2V0IGlyaXM6CgpgYGB7ciwgZWNobyA9IFRSVUUsIGV2YWwgPSBUUlVFfQojIG5hbWVzKCkgbXVlc3RyYSBsb3Mgbm9tYnJlcyBkZSBsYXMgdmFyaWFibGVzIGRlIHVuIGRhdGFmcmFtZQpuYW1lcyhpcmlzKQpgYGAKTm8gbm9zIGhhY2UgZmFsdGEsIHBlcm8gdmVhbW9zIGxvcyBwcmltZXJvcyB2YWxvcmVzIGRlIGlyaXM6CgpgYGB7ciwgZWNobyA9IFRSVUUsIGV2YWwgPSBUUlVFfQojIGhlYWQoKSBtdWVzdHJhIGxhcyBuIChuZSBlc3RlIGNhc28gNCkgcHJpbWVyYXMgZmlsYXMgZGUgdW4gZGF0YWZyYW1lCmhlYWQoaXJpcywgbiA9IDQpCmBgYAoKTGEgZnVuY2nDs24gYHN1bW1hcnkoKWAsIG5vcyBoYWNlIHVuIHJlc3VtZW4gKCEpIGRlbCBkZgoKYGBge3IsIGVjaG8gPSBUUlVFLCBldmFsID0gVFJVRX0KIyBGw61qYXRlIHF1ZSBsYSB2YXJpYWJsZSAiU3BlY2llcyIgbm8gdGllbmUgbWVkaWEsIG5pIG3DrW5pbW8sIG5pIG1heC4gLi4uIGVzIHBvcnF1ZSBlcyB1biBmYWN0b3IKc3VtbWFyeShpcmlzKQpgYGAKCgoKCjxicj4KCioqU0lFTVBSRS1TSUVNUFJFKiogaGF5IHF1ZSBjaGVxdWVhciBkZSBxdWUgY2xhc2Ugc29uIGxhcyB2YXJpYWJsZXMgcXVlIGNvbnRpZW5lIGVsIGRmLgoKCmBgYHtyLCBlY2hvID0gVFJVRSwgZXZhbCA9IFRSVUV9CiMtIHZlciBsYSBlc3RydWN0dXJhIGRlbCBkZi4gVmlzdWFsaXphcmVtb3MgbG9zIG5vbWJyZXMgeSBlbCB0aXBvIGRlIGxhcyB2YXJpYWJsZXMKc3RyKGlyaXMpCmBgYAoKVGFtYmnDqW4gcG9kw6lpcyB1c2FyIGxhIGZ1bmNpw7NuIGBza2ltKClgIGRlbCBwYWNrYWdlICBgc2tpbXJgLgoKCmBgYHtyLCBlY2hvID0gVFJVRSwgZXZhbCA9IEZBTFNFfQojZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCJyb3BlbnNjaWxhYnMvc2tpbXIiKQpsaWJyYXJ5KHNraW1yKQpza2ltKGlyaXMpCmBgYAoKCmBgYHtyICwgZWNobyA9IEZBTFNFLCBldmFsID0gVFJVRSwgZmlnLmFzcCA9IDQvMiwgb3V0LndpZHRoID0gIjgwJSIsIGZpZy5hbGlnbiA9ICJsZWZ0In0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1hZ2VuZXMiLCAidHRfMDRfaW1nXzAzX291dHB1dC1za2ltci5wbmciKSkKYGBgCgoKCgo8YnI+CgoqKlZhbW9zIFlBIGEgZXhwb3J0YXIgKGUgaW1wb3J0YXIpICJpcmlzIiBhIGRpZmVyZW50ZXMgZm9ybWF0b3MuIEVtcGV6YW1vcyEhKioKCjxicj4KCi0tLS0tLS0tLS0tLS0tLS0tLS0tCgo8YnI+CgoKIyAzLiBEYXRvcyB0YWJ1bGFyZXMgKG8gZGUgdGV4dG8pCgo8YnI+CgpFc3RhbW9zIGFjb3N0dW1icmFkb3MgYSB2aXN1YWxpemFyIGRhdG9zIGVuICoqZm9ybWF0byB0YWJ1bGFyKio7IGVzICBkZWNpciwgY29tbyB1bmEgdGFibGEuIEdlbmVyYWxtZW50ZSAqKmxhcyBjb2x1bW5hcyBzb24gdmFyaWFibGVzIHkgbGFzIGZpbGFzIHNvbiBvYnNlcnZhY2lvbmVzKiogZGUgZXNhcyB2YXJpYWJsZXMgcGFyYSBkaWZlcmVudGVzIHVuaWRhZGVzIGRlIGFuw6FsaXNpcyAoImluZGl2aWR1b3MiKS4gCgpMYXMgY29sdW1uYXMgKipzZSBzZXBhcmFuIGNvbiB1biBjYXLDoWN0ZXIqKiAoZ2VuZXJhbG1lbnRlIGxhIGNvbWEpIHkgbGFzIGZpbGFzIGNvbiB1biBzYWx0byBkZSBsaW5lYS4KClBvZGVtb3MgcGVuc2FyIHF1ZSBkZXBlbmRpZW5kbyBkZSBjb21vIHNlIHNlcGFyZW4gbGFzIG9ic2VydmFjaW9uZXMgdGVuZW1vcyBkaXN0aW50b3MgdGlwb3MgZGUgZGF0b3MgdGFidWxhcmVzLCBwZXJvIGVuIHJlYWxpZGFkIHN1IGVzdHJ1Y3R1cmEgZXMgc2ltaWxhcjogdmFyaWFibGVzIGVuIGNvbHVtbmFzIHkgbGFzIG9ic2VydmFjaW9uZXMgZGUgdW4gaW5kaXZpZHVvIHNlcGFyYWRhcyBwb3IgdW5hIG1hcmNhIG8gY2Fyw6FjdGVyLiBFc3RlIGNhcsOhY3RlciBwdWVkZSBzZXIgdW4gZXNwYWNpbywgdW4gdGFidWxhZG9yLCB1bmEgY29tYSwgcHVudG8geSBjb21hIGV0Yy4uLiBFbCBmb3JtYXRvIHRhYnVsYXIgbWFzIGV4dGVuZGlkbyBlcyBlbCAqKkNTVioqLCBkb25kZSBsYXMgb2JzZXJ2YWNpb25lcyBlc3TDoW4gc2VwYXJhZGFzIHBvciBjb21hcy4gCgoKRXN0b3MgZGF0b3Mgc2UgcHVlZGVuIHZpc3VhbGl6YXIgZW4gbG9zIGVkaXRvcmVzIGRlIHRleHRvIHkgcG9yIGVzbyB0YW1iacOpbiBzZSBsbGFtYW4gZGF0b3MgZW4gZm9ybWF0byB0ZXh0by4KClBvZGVtb3MgcGVuc2FyIHF1ZSBoYXkgMiBncnVwb3MgZGUgZGF0b3MgdGFidWxhcmVzOgoKICAtIGRlbGltaXRhZG9zIHBvciBjYXJhY3RlcmVzCiAgLSBkZSBhbmNodXJhIGZpamEgIAoKRWwgcGFja2FnZSBgcmVhZHJgIGxlZSBkYXRvcyB0YWJ1bGFyZXMgY29uIGxhcyBzaWd1aWVudGVzIGZ1bmNpb25lczoKCi0gc2kgbG9zIGRhdG9zIGVzdMOhbiBkZWxpbWl0YWRvcyBwb3IgY2FyYWN0ZXJlcyB1dGlsaXphOiBgcmVhZF9kZWxpbSgpYCwgYHJlYWRfY3N2KClgLCBgcmVhZF90c3YoKWAgLi4uCi0gc2kgbG9zIGRhdG9zIHNvbiBkZSBhbmNodXJhIGZpamE6IGByZWFkX2Z3ZigpYCB5IGByZWFkX3RhYmxlKClgCgpTw7NsbyB2ZXJlbW9zIGNvbW8gaW1wb3J0YXIvZXhwb3J0YXIgZGF0b3MgdGFidWxhcmVzIGRlbCBwcmltZXIgdGlwbzsgZXMgZGVjaXIsIHNlcGFyYWRvcyBwb3IgY2FyYWN0ZXJlcy4gQ29tZW56YXJlbW9zIGNvbiBlbCBmb3JtYXRvIENTViBxdWUgZXMgZWwgbcOhcyB1dGlsaXphZG8uCgo8YnI+CgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMjIENTVgoKQ1NWIHNpZ25pZmljYSAiY29tbWEgc2VwYXJhdGVkIGRhdGEiLiBFbiByZWFsaWRhZCBDU1YgZXMgdW4gY2FzbyBwYXJ0aWN1bGFyIGRlICJ0YWJ1bGFyIG8gdGV4dCBkYXRhIgoKUmVjb3JkYWQgcXVlIHRlbmVtb3MgcXVlIGV4cG9ydGFyIGVsIGRhdGFmcmFtZSBgaXJpc2AgYSBmb3JtYXRvIENTViB5IGx1ZWdvIGltcG9ydGFybG8uCgpQYXJhIGV4cG9ydGFyIGBpcmlzYCBhIHVuIGZpY2hlcm8gZW4gZm9ybWF0byBgQ1NWYCB1dGlsaXphcmVtb3MgbGEgZnVuY2nDs24gYHdyaXRlX2NzdigpYDogc29sbyBoYXkgcXVlIGRlY2lybGUgZWwgb2JqZXRvIHF1ZSBxdWVyZW1vcyBleHBvcnRhciAoZW4gZXN0ZSBjYXNvIHVuIGRmICJpcmlzIikgeSBlbCBub21icmUgKGp1bnRvIGNvbiBsYSBydXRhKSBkZWwgYXJjaGl2byBkb25kZSBxdWVyZW1vcyBndWFyZGFybG8uCgpQb2RlbW9zIGVzcGVjaWZpY2FyIGxhIHJ1dGEgY29tcGxldGEuIFBvciBlamVtcGxvOgoKYGBge3IsIGVjaG8gPSBULCBldmFsID0gRn0KIy0gZXhwb3J0YSBlbiBmb3JtYXRvIENTViBlbCBkZiBpcmlzIGFsIGZpY2hlcm8gImlyaXMuY3N2IiAKIy0gQ3VpZGFkbyEhIGVzIHVuYSBydXRhIGFic29sdXRhLiBObyBmdW5jaW9uYXLDoSBlbiB0b2RvcyBsb3Mgb3JkZW5hZG9yZXMKd3JpdGVfY3N2KGlyaXMsIHBhdGggPSAiQzovVXNlcnMvcGVyZXpwL0Rlc2t0b3AvaXJpcy5jc3YiKQpgYGAKCjxicj4KCkVuIHJlYWxpZGFkIG5vIGhhY2UgZmFsdGEgZXNwZWNpZmljYXIgbGEgcnV0YSBjb21wbGV0YS4gU2kgc29sbyBlc3BlY2lmaWNhbW9zIGVsIG5vbWJyZSBkZWwgYXJjaGl2bywgUiBsbyBndWFyZGFyw6EgZW4gZWwgZGlyZWN0b3JpbyBkZSB0cmFiYWpvLiAKCmBgYHtyLCBlY2hvID0gVCwgZXZhbCA9IEZ9CiMtIGV4cG9ydGEgZW4gZm9ybWF0byBDU1YgZWwgZGYgaXJpcyBhbCBmaWNoZXJvICJpcmlzLmNzdiIuIENvbW8gbm8gc2UgZXNwZWNpZmljYSBsYSBydXRhLCBzZSBncmFiYXLDoSBlbiBlbCBkaXJlY3RvcmlvIGRlIHRyYWJham8gCndyaXRlX2NzdihpcmlzLCBwYXRoID0gImlyaXMuY3N2IikKYGBgCgoKUmVjdWVyZGEgcXVlIHBhcmEgc2FiZXIgY3VhbCBlcyB0dSBkaXJlY3RvcmlvIGRlIHRyYWJham8gcHVlZGVzIHVzYXIgbGEgZnVuY2nDs24gYGdldHdkKClgIHkgcHVlZGVzIGNhbWJpYXJsbyBkZXNkZSBsb3MgbWVuw7pzIGRlIFJTdHVkaW8gbyBjb24gYHNldHdkKClgLiBQb3IgZWplbXBsbzoKCmBgYHtyLCBlY2hvID0gVFJVRSwgZXZhbCA9IEZBTFNFfQojLSBhbG1hY2VuYW1vcyBlbiBlbCBvYmpldG8gInBhdGhfd2QiIGxhIHJ1dGEgZGVsIGRpcmVjdG9yaW8gZGUgdHJhYmFqbyBkZWwgb3JkZW5hZG9yIHF1ZSBlc3TDoXMgdXNhbmRvCnBhdGhfYV9taV93ZCA8LSBnZXR3ZCgpIAoKIy0gUG9kZW1vcyBmaWphciAgZWwgZGlyZWN0b3JpbyBkZSB0cmFiYWpvIGRvbmRlIHF1ZXJhbW9zLiBQb3IgZWplbXBsbzoKc2V0d2QoIkM6L1VzZXJzL3BlcmV6cC9EZXNrdG9wL01pc19kYXRvcy8iKSAgCiMtIGVuIHR1IG9yZGVuYWRvciBubyBmdW5jaW9uYXLDoSBwb3JxdWUgdHUgb3JkZW5hZG9yIG5vIHRpZW5lIGVzYSBydXRhIG8gZXN0cnVjdHVyYSBkZSBjYXJwZXRhcwoKIy0gZmlqYW1vcyBlbCBkaXJlY3RvcmlvIGRlIHRyYWJham8gKGF1bnF1ZSBlbiByZWFsaWRhZCBubyBoYWNlIGZhbHRhIHBvcnF1ZSBlc2EgcnV0YSBhbG1hY2VuYWRhIGVuICAicGF0aF9hX21pX3dkIiB5YSBlcmEgZXNlIGVsIGRpcmVjdG9yaW8gZGUgdHJhYmFqbwpzZXR3ZChwYXRoX2FfbWlfd2QpICAgICAgCmBgYAoKCjxicj4KClJlY29tZW5kYW1vcyB0cmFiYWphciBjb24gUnByb2plY3RzIHkgZ3VhcmRhciBsb3MgZmljaGVyb3MgZGUgZGF0b3MgZW4gdW5hIGNhcnBldGEgbGxhbWFkYSBgL2RhdG9zL2AuIAoKClBvciBsbyB0YW50bywgcGFyYSBleHBvcnRhciBsb3MgZGF0b3MgZGUgImlyaXMiIGVuIGxhIHN1YmNhcnBldGEgYC9kYXRvcy9wcnVlYmFzL2AgZGVudHJvIGRlbCBwcm95ZWN0bywgaGF5IHF1ZSBoYWNlciBsbyBzaWd1aWVudGU6CgpgYGB7ciwgZWNobyA9IFRSVUUsIGV2YWwgPSBGQUxTRX0KIy0gZXhwb3J0YSBlbiBmb3JtYXRvIC5jc3YgZWwgZGYgaXJpcyBhbCBmaWNoZXJvICJpcmlzLmNzdiIuIFNlIGd1YXJkYXLDoSBlbiBsYSBzdWJjYXJwZXRhICJkYXRvcy9wcnVlYmFzLyIgZGVsIHByb3llY3RvCndyaXRlX2NzdihpcmlzLCAiLi9kYXRvcy9wcnVlYmFzL2lyaXMuY3N2IikKYGBgCgpTaSBxdWVyZW1vcywgcG9kZW1vcyBwb25lciBleHBsw61jaXRhbWVudGUgbG9zIGFyZ3VtZW50b3MgKG8gcGFyw6FtZXRyb3MpIGRlIGxhIGZ1bmNpw7NuIGB3cml0ZV9jc3YoKWA6CgoKYGBge3IsIGVjaG8gPSBUUlVFLCBldmFsID0gRkFMU0V9CiMtIE90cmEgdmV6IGV4cG9ydGFtb3MgZW4gZm9ybWF0byAuY3N2IGVsIGRmIGlyaXMuIEVzdGEgdmV6IGV4cGxpY2l0YW1vcyBsYXMgb3BjaW9uZXMgbyBwYXLDoW1ldHJvcyBkZSBsYSBmdW5jacOzbgp3cml0ZV9jc3YoaXJpcywgcGF0aCA9ICIuL2RhdG9zL3BydWViYXMvaXJpcy5jc3YiLCBjb2xfbmFtZXMgPSBUUlVFKQpgYGAKCgoKYGBge3IsIGVjaG8gPSBGQUxTRSwgZXZhbCA9IEZBTFNFfQojLSBwYXJhIHF1ZSBzZSBwdWVkYSBjb3JyZXIgZW4gLlJtZCBlbiBvdHJhIGNhcnBldGEgcXVlIG5vIHNlIGxhIHJhaXoKIy0gT3RyYSB2ZXogZXhwb3J0YW1vcyBlbiBmb3JtYXRvIC5jc3YgZWwgZGYgaXJpcy4gRXN0YSB2ZXogZXhwbGljaXRhbW9zIGxhcyBvcGNpb25lcyBvIHBhcsOhbWV0cm9zIGRlIGxhIGZ1bmNpw7NuCndyaXRlX2NzdihpcmlzLCBwYXRoID0gaGVyZTo6aGVyZSgiZGF0b3MiLCAicHJ1ZWJhcyIsICJpcmlzLmNzdiIpLCBjb2xfbmFtZXMgPSBUUlVFKQpgYGAKCgoKCjxicj4KCkJpZW4sIHlhIGhlbW9zIGV4cG9ydGFkbyAiaXJpcyIgYSB1biBmaWNoZXJvIGVuIGZvcm1hdG8gQ1NWLCBhaG9yYSB2YW1vcyBhIGltcG9ydGFybG8uCgoKPGJyPgoKUGFyYSBpbXBvcnRhciBsb3MgZGF0b3MgZGVsIGZpY2hlcm8gImlyaXMuY3N2IiBoYWNlbW9zIGxvIHNpZ3VpZW50ZToKCgpgYGB7ciwgZWNobyA9IFRSVUUsIGV2YWwgPSBGQUxTRX0KIy0gaW1wb3J0YW1vcyBsb3MgZGF0b3MgZGVsIGZpY2hlcm8gImlyaXMuY3N2IiB5IGxvcyBndWFyZGFtb3MgZW4gdW4gb2JqZXRvIHF1ZSBsbGFtYW1vcyAiaXJpc19pbXBfY3N2Ii4gUmVjdWVyZGEgcXVlIGFjYWJhbW9zIGRlIGV4cG9ydGFyICJpcmlzIiBhIGxhIGNhcnBldGEgIi9kYXRvcy9wcnVlYmFzLyIgZGVudHJvIGRlbCBScHJvamVjdAppcmlzX2ltcF9jc3YgPC0gcmVhZF9jc3YoIi4vZGF0b3MvcHJ1ZWJhcy9pcmlzLmNzdiIpCmBgYAoKCjxicj4KCkFzw60gZGUgc2VuY2lsbG8hISBBZGVtw6FzIGxhIG1heW9yw61hIGRlIHByb2dyYW1hcyBwZXJtaXRlbiBsZWVyIHkgZXhwb3J0YXIgZGF0b3MgZW4gQ1NWOyBhc8OtIHF1ZSBzaSB0cmFiYWphbW9zIGNvbiBvdHJvIHNvZnR3YXJlIChFeGNlbCwgU1BTUyAuLi4pLCBzaWVtcHJlIHBvZGVtb3MgcGFzYXIgbnVlc3Ryb3MgZGF0b3MgYSBSIGV4cG9ydMOhbmRvbG9zIGEgQ1NWOyB5IGRlc2RlIFIgcG9kZW1vcyBoYWNlciBsbyBtaXNtby4gCgo8YnI+CgoKIyMjIyBBbGd1bmFzIG9wY2lvbmVzIGRlIGByZWFkX2NzdigpYCBxdWUgY29udmllbmUgY29ub2NlcgoKQSB2ZWNlcyBsb3MgZGF0b3MgdGllbmVuIGNpZXJ0b3MgcHJvYmxlbWFzIHF1ZSBoYXkgcXVlIGFycmVnbGFyOyBwb3IgbG8gcXVlIGNvbnZpZW5lIGNvbm9jZXIgYWxndW5hcyBvcGNpb25lcyBkZSBgcmVhZF9jc3YoKWA6CgotICoqY29sX25hbWVzOioqIHJlYWRfY3N2KCkgYXN1bWUgcXVlIGxhIHByaW1lcmEgZmlsYSBjb250aWVuZSBsb3Mgbm9tYnJlcyBkZSBsYXMgdmFyaWFibGVzLiBFc3RvIHB1ZWRlIGNhbWJpYXJzZSBjb24gYGNvbF9uYW1lcyA9IEZBTFNFYC4gUHVlZGVzIHByb3ZlZXIgbm9tYnJlcyBhIGxhcyB2YXJpYWJsZXMgKG8gY29sdW1uYXMpIGNvbiBgY29sX25hbWVzID0gYygiWDEiLCAiWDIiKWAKCi0gKipza2lwOioqcmVhZF9jc3YoKSBwb3IgZGVmZWN0byBpbXBvcnRhIHRvZGFzIGxhcyBmaWxhcyBkZWwgYXJjaGl2bywgcGVybyBwdWVkZXMgaGFjZXIgcXVlIGNvbWllbmNlIGEgaW1wb3J0YXIgZW4gbGEgZmlsYSBxdWUgcXVpZXJhcyBjb24gYHNraXAgPSBuYAoKLSAqKm5hOioqIEVuIGFsZ3Vub3MgZmljaGVyb3MgY29uIGRhdG9zIHRhYnVsYXJlcyBsb3MgTkFzIHNlIGVzcGVjaWZpY2FuIGNvbiBhbGfDum4gY2Fyw6FjdGVyLiBFc3RvIHBvZGVtb3MgdHJhdGFybG8gYWwgbGVlciBsb3MgZGF0b3MgY29uIGVsIGFyZ3VtZW50byAgIGBuYSA9ICJ4eHgiYAoKCgoKUG9yIGVqZW1wbG8sIGVsIGNodW5rIHF1ZSB2ZXMgYWJham8gdXRpbGl6YSByZWFkX2NzdigpIHBhcmEgY2FyZ2FyIGVsIGZpY2hlcm8gIm15X2ZpY2hlcm8uY3N2Ii4gQ29taWVuemEgYSBpbXBvcnRhciBkYXRvcyBkZXNkZSBsYSBxdWludGEgY29sdW1uYSwgIHRyYXRhIGxvcyB2YWxvcmVzIC00NCB5ICQgY29tbyBOQXMgeSBwcm92ZWUgdW4gdmVjdG9yIGNvbiBsb3Mgbm9tYnJlcyBxdWUgcXVlcmVtb3MgcGFyYSBsYXMgdmFyaWFibGVzIChvIGNvbHVtbmFzKQoKYGBge3IsIGVjaG8gPSBUUlVFLCBldmFsID0gRkFMU0V9Cm1pc19kYXRvcyA8LSByZWFkX2NzdigibXlfZmljaGVyby5jc3YiLCBza2lwID0gNSwgbmEgPSBjKCItNDQiLCAiJCIpLCBjb2xfbmFtZXMgPSBjKCJYMSIsICJYMiIsICJZWSIsICJYNCIsICJaWiIpKQpgYGAKCgoKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMgT3Ryb3MgZGF0b3MgdGFidWxhcmVzIAoKRW4gcmVhbGlkYWQsIHRvZG9zIGxvcyBkYXRvcyB0YWJ1bGFyZXMgKCoqc2VwYXJhZG9zIHBvciBjYXJhY3RlcmVzKiopIHNvbiBtdXkgc2ltaWxhcmVzLiDCoVNvbG8gc2UgZGlmZXJlbmNpYW4gZW4gZWwgY2Fyw6FjdGVyIHF1ZSBoYWNlIGRlIHNlcGFyYWRvci4gCgpFbCBwYWNrYWdlICJyZWFkciIgdGllbmUgdW5hIGZ1bmNpw7NuIGVzcGVjaWZpY2EgcGFyYSBjYWRhIHRpcG8gZGUgZGF0b3MgdGFidWxhcmVzLiBQb3IgZWplbXBsbywgc2kgZWwgc2VwYXJhZG9yIGVzIHVuIHB1bnRvIHkgY29tYSwgbGEgZnVuY2nDs24gcGFyYSBpbXBvcnRhciBlc3RvcyBkYXRvcyBlcyBgcmVhZF9jc3YyKClgOyBzaSBlbCBzZXBhcmFkb3IgZXMgdW4gdGFidWxhZG9yLCBsYSBmdW5jacOzbiBlcyBgcmVhZF90c3YoKWAuIFBlcm8gdGFtYmnDqW4gdGllbmUgdW5hIGZ1bmNpw7NuIGdlbsOpcmljYSBxdWUgc2lydmUgcGFyYSBjdWFscXVpZXIgdGlwbyBkZSBzZXBhcmFkb3I6IGByZWFkX2RlbGltKClgIC4gT2J2aWFtZW50ZSB1c2FyZW1vcyBlc3RhcyBmdW5jaW9uZXMuCgoKUG9yIGVqZW1wbG8sIHBvZGVtb3MgY2FyZ2FyIGVsIGZpY2hlcm8gIm15X2lyaXNfZXhwb3J0YWRvLmNzdiIgcXVlIGhlbW9zIGV4cG9ydGFkbyBhbnRlcmlvcm1lbnRlIHV0aWxpemFuZG8gbGEgZnVuY2nDs24gZ2Vuw6lyaWNhIGByZWFkX2RlbGltKClgLCBzb2xvIGhheSBxdWUgZGVjaXJsZSBxdWUgZWwgc2VwYXJhZG9yIGVzIHVuYSBjb21hLiBTZSBsbyBkZWNpbW9zIGNvbiBsYSBvcGNpw7NuIGBkZWxpbSA9ICIsImAuIFZlw6Ftb3NsbzoKCgpgYGB7ciwgZWNobyA9IFRSVUUsIGV2YWwgPSBGQUxTRX0KIy0gaW1wb3J0YW1vcyBsb3MgZGF0b3MgZGVsIGZpY2hlcm8gImlyaXMuY3N2IiB5IGxvcyBndWFyZGFtb3MgZW4gdW4gb2JqZXRvIHF1ZSBsbGFtYW1vcyBpcmlzX2ltcF9jc3ZfMi4gRsOtamF0ZSBlbiBlbCBhcmd1bWVudG8gJ2RlbGltJwppcmlzX2ltcF9jc3ZfMiA8LSByZWFkX2RlbGltKCIuL2RhdG9zL3BydWViYXMvaXJpcy5jc3YiLCBkZWxpbSA9ICIsIikKYGBgCgpDb21vIGVsIGZvcm1hdG8gdGFidWxhciBtYXMgZXh0ZW5kaWRvIGVzIGVsIENTVjsgZW4gZ2VuZXJhbCwgbm8gdGVuZHJlbW9zIG5lY2VzaWRhZCBkZSBleHBvcnRhciBkYXRvcyB0YWJ1bGFyZXMgc2VwYXJhZG9zIHBvciBjYXJhY3RlcmVzIGRpc3RpbnRvcyBhIGxhIGNvbWEsIHBlcm8gc2kgcXVpc2nDqXJhbW9zIGhhY2VybG8sIHBvZHLDrWFtb3MgaGFjZXJsbyBjb24gYHdyaXRlX3RzdigpYCBvIGNvbiBgd3JpdGVfZGVsaW0oKWA6CgoKYGBge3IsIGVjaG8gPSBUUlVFLCBldmFsID0gRkFMU0V9CiMtIGV4cG9ydGFtb3MgaXJpcyBlbiBmb3JtYXRvIHRhYnVsYXIgc2VwYXJhZG8gcG9yIHB1bnRvIHkgY29tYS4gCndyaXRlX2RlbGltKGlyaXMsICIuL2RhdG9zL3BydWViYXMvaXJpc18yLnR4dCIsIGRlbGltID0gIjsiKQojLSBleHBvcnRhbW9zIGlyaXMgZW4gZm9ybWF0byB0YWJ1bGFyIHNlcGFyYWRvIHBvciB0YWJ1bGFkb3JlcyAKd3JpdGVfZGVsaW0oaXJpcywgIi4vZGF0b3MvcHJ1ZWJhcy9pcmlzXzMudHh0IiwgZGVsaW0gPSAiXHQiKQojLSBleHBvcnRhbW9zIGlyaXMgZW4gZm9ybWF0byB0YWJ1bGFyIHNlcGFyYWRvIHBvciB1biBlc3BhY2lvIGVuIGJsYW5jbyAKd3JpdGVfZGVsaW0oaXJpcywgIi4vZGF0b3MvcHJ1ZWJhcy9pcmlzXzQudHh0IiwgZGVsaW0gPSAiICIpCmBgYAoKPGJyPgoKU2kgcXVpc2nDqXJhbW9zIGltcG9ydGFybG9zLCB0ZW5kcsOtYW1vcyBxdWUgaGFjZXI6CgoKYGBge3IsIGVjaG8gPSBUUlVFLCBldmFsID0gRkFMU0V9CiMtIGV4cG9ydGFtb3MgaXJpcyBlbiBmb3JtYXRvIHRhYnVsYXIgc2VwYXJhZG8gcG9yIHB1bnRvIHkgY29tYS4gCnJlYWRfZGVsaW0oIi4vZGF0b3MvcHJ1ZWJhcy9pcmlzXzIudHh0IiwgZGVsaW0gPSAiOyIpCiMtIGV4cG9ydGFtb3MgaXJpcyBlbiBmb3JtYXRvIHRhYnVsYXIgc2VwYXJhZG8gcG9yIHRhYnVsYWRvcmVzIApyZWFkX2RlbGltKCIuL2RhdG9zL3BydWViYXMvaXJpc18zLnR4dCIsIGRlbGltID0gIlx0IikKIy0gZXhwb3J0YW1vcyBpcmlzIGVuIGZvcm1hdG8gdGFidWxhciBzZXBhcmFkbyBwb3IgdW4gZXNwYWNpbyBlbiBibGFuY28gCnJlYWRfZGVsaW0oIi4vZGF0b3MvcHJ1ZWJhcy9pcmlzXzQudHh0IiwgZGVsaW0gPSAiICIpCmBgYAoKCgo8YnI+Cjxicj4KCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKPGJyPgoKIyA0LiBGb3JtYXRvcyBwcm9waWV0YXJpb3MKCkhhc3RhIHF1ZSBSIGhhZ2EgZGVzcGFyZWNlciBhIFNQU1MsIFN0YXRhLCAgLi4uLi4gYHIgZW1vOjpqaSgiYmljZXBzIilgLCAgYcO6biBzZXLDoSBuZWNlc2FyaW8gaW1wb3J0YXIgYWxnw7puIGZpY2hlcm8gZW4gZm9ybWF0b3MgZGUgc29mdHdhcmUgcHJvcGlldGFyaW8gY29tbyBsb3MgZGUgRXhjZWwsIFNBUywgU3RhdGEgLCBTUFNTIHkgRXZpZXdzCgoKTm9ybWFsbWVudGUgbG8gcXVlIG5lY2VzaXRhcmVtb3MgZXMgaW1wb3J0YXIgZGF0b3MgZW4gZXNvcyBmb3JtYXRvcywgc8OzbG8gYWxndW5hIHZleiB0ZW5kcmVtb3MgcXVlIGV4cG9ydGFybG9zLCB5YSBxdWUgdG9kb3MgZXN0b3MgcHJvZ3JhbWFzIHB1ZWRlbiBsZWVyIGRhdG9zIGVuIENTVi4KCgpMbyBxdWUgc8OtIHB1ZWRlIHJlc3VsdGFybm9zIMO6dGlsIGVzIGV4cG9ydGFyIGRhdG9zIGVuIGZvcm1hdG8gLnhscywgeWEgcXVlIEV4Y2VsIGVzIG11eSDDunRpbCAgcGFyYSB2aXN1YWxpemFyIGRhdG9zIGVuIGZvcm1hdG8gdGFidWxhciB5IHRhbWJpw6luIHBvcnF1ZSBtdWNoYSBnZW50ZSBwcmVmaWVyZSAobyBzb2xvIHNhYmUpIGxlZXIvYW5hbGl6YXIgZGF0b3MgZW4gRXhjZWwuCgoKPGJyPgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgo8YnI+CgojIyBFeGNlbAoKIyMjIyBFeHBvcnRhciBhIGV4Y2VsCgpIYXkgdmFyaW9zIHBhY2thZ2VzIHF1ZSBncmFiYW4gZGF0b3MgZW4gZm9ybWF0byAueGxzLiBQZXJvIGVsIG3DoXMgc2VuY2lsbG8gZXMgZWwgcGFja2FnZSBgeGxzeGAuIFZlw6Ftb3NsbzoKCmBgYHtyLCBlY2hvID0gVCwgZXZhbCA9IEZBTFNFfQpsaWJyYXJ5KHhsc3gpCndyaXRlLnhsc3goaXJpcywgIi4vZGF0b3MvcHJ1ZWJhcy9pcmlzLnhsc3giKQpgYGAKCgpMYSBmdW5jacOzbiBgd3JpdGUueGxzeCgpYCBwZXJtaXRlIGVzcGVjaWZpY2FyIGVsIG5vbWJyZSBkZWwgbGlicm8geSBhbGd1bmEgY29zYSBtw6FzOyBwb3IgZWplbXBsbzoKCgpgYGB7ciwgZWNobyA9IFQsIGV2YWwgPSBGQUxTRX0KIyBsaWJyYXJ5KHhsc3gpCndyaXRlLnhsc3goaXJpcywgIi4vZGF0b3MvcHJ1ZWJhcy9pcmlzLnhsc3giLCBzaGVldE5hbWUgPSAiSVJJUyIsIHJvdy5uYW1lcyA9IEZBTFNFKQpgYGAKCkxhIGZ1bmNpw7NuIGB3cml0ZS54bHN4KClgIHBlcm1pdGUgYcOxYWRpciBkYXRvcyBhIHVuIGFyY2hpdm8gLnhsc3ggcHJlZXhpc3RlbnRlOyBwYXJhIGVsbG8gdGVuZW1vcyBxdWUgdXNhciBsYSBvcGNpw7NuIGBhcHBlbmQgPSBUUlVFYDoKCgpgYGB7ciwgZWNobyA9IFQsIGV2YWwgPSBGQUxTRX0KIyBsaWJyYXJ5KHhsc3gpCndyaXRlLnhsc3goaXJpcywgIi4vZGF0b3MvcHJ1ZWJhcy9pcmlzLnhsc3giLCBzaGVldE5hbWUgPSAiSVJJU18yIiwgYXBwZW5kID0gVFJVRSkKYGBgCgo8YnI+CgpFbCBwYXF1ZXRlIGB4bHN4YCBkZXBlbmRlIGRlIEphdmEuIE5vIHN1ZWxlIGhhYmVyIHByb2JsZW1hcywgcGVybyBzaSBsb3MgdHV2aWVzZXMsIGVsIHBhY2thZ2UgYHdyaXRleGxgIG5vIHRpZW5lIG5pbmd1bmEgZGVwZW5kZW5jaWEuIFRlbmRyw61hcyBxdWUgaGFjZXIgbG8gc2lndWllbnRlOgoKCmBgYHtyLCBlY2hvID0gVFJVRSwgbWVzc2FnZSA9IEZBTFNFLCBldmFsID0gRkFMU0V9CmxpYnJhcnkod3JpdGV4bCkKd3JpdGVfeGxzeChpcmlzLCAiLi9kYXRvcy9wcnVlYmFzL2lyaXM4Lnhsc3giKQpgYGAKCjxicj4KCgojIyMjIEltcG9ydGFyIGFyY2hpdm9zIGVuIGZvcm1hdG8gZXhjZWwKClRhbWJpw6luIGhheSB2YXJpb3MgcGFxdWV0ZXMsIHBlcm8gdXNhcmVtb3MgZWwgbWlzbW8gcXVlIHVzYSBSU3R1ZGlvIGVuIHN1cyBtZW7DunM6IGByZWFkeGxgCgoKKipgcmVhZHhsYCoqIHBlcm1pdGUgbGVlciBmaWNoZXJvcyBgLnhsc2AgeSBgLnhsc3hgLiBQb3IgZWplbXBsbzoKCgpgYGB7ciwgZWNobyA9IFQsIGV2YWwgPSBGQUxTRX0KIyBsaWJyYXJ5KHJlYWR4bCkKaXJpc19pbXBfeGxzIDwtIHJlYWRfZXhjZWwoIi4vZGF0b3MvcHJ1ZWJhcy9pcmlzLnhsc3giKQpgYGAKCjxicj4KClBvZGVtb3MgZXNwZWNpZmljYXIgZWwgbGlicm8gcXVlIHF1ZXJlbW9zIGFicmlyLCB5YSBzZWEgZXNwZWNpZmljYW5kbyBzdSBub21icmUgbyBzdSBwb3NpY2nDs24gZW4gZWwgZmljaGVybwoKCmBgYHtyLCBlY2hvID0gVCwgZXZhbCA9IEZBTFNFfQojIGxpYnJhcnkocmVhZHhsKQppcmlzX2ltcF94bHMgPC0gcmVhZF9leGNlbCgiLi9kYXRvcy9wcnVlYmFzL2lyaXMueGxzeCIsIHNoZWV0ID0gMikKaXJpc19pbXBfeGxzIDwtIHJlYWRfZXhjZWwoIi4vZGF0b3MvcHJ1ZWJhcy9pcmlzLnhsc3giLCBzaGVldCA9ICJJUklTXzIiKQpgYGAKCgpMYSBmdW5jacOzbiBgcmVhZF9leGNlbCgpYCB0aWVuZSBtw6FzIHBvc2liaWxpZGFkZXM7IGNvbW8gZWplbXBsbywgbGEgb3BjacOzbiBgc2tpcCA9IDRgIHBlcm1pdGUgZW1wZXphciBhIGltcG9ydGFyIGEgcGFydGlyIGRlIGxhIGN1YXJ0YSBmaWxhLgoKPGJyPgoKU2kgcXVlcmVtb3MgaW1wb3J0YXIgdG9kb3MgbG9zIGxpYnJvcyAobyBzaGVldHMpIGRlIHVuIGFyY2hpdm8gRXhjZWwsIHBvZGVtb3MgaGFjZXJsbyBhc8OtOgoKCmBgYHtyLCBlY2hvID0gVCwgZXZhbCA9IEZBTFNFfQojIGxpYnJhcnkocmVhZHhsKQojICghISEpCklSSVNfbGlzdCA8LSBsYXBwbHkoZXhjZWxfc2hlZXRzKCIuL2RhdG9zL3BydWViYXMvaXJpcy54bHN4IiksIHJlYWRfZXhjZWwsIHBhdGggPSAiLi9kYXRvcy9wcnVlYmFzL2lyaXMueGxzeCIpCmBgYAoKSGVtb3MgZ3VhcmRhZG8gbG9zIDIgc2hlZXRzIGRlbCBhcmNoaXZvICIuL2RhdG9zL3BydWViYXMvaXJpcy54bHN4IiBlbiB1biBvYmpldG8gUiBsbGFtYWRvIGBJUklTX2xpc3RgLiBFc3RlIG9iamV0byBlcyB1bmEgbGlzdGEgY29uIDIgZWxlbWVudG9zLiBjYWRhIGVsZW1lbnRvIGNvbnRpZW5lIGxvcyBkYXRvcyBkZSBjYWRhIHVubyBkZSBsb3MgMiBzaGVldHMuIFBvZGVtb3MgdmVybG8gY29uIGBzdHIoKWA6CgoKYGBge3IsIGVjaG8gPSBULCBldmFsID0gRkFMU0V9CiMgKCEhISkKc3RyKElSSVNfbGlzdCkKYGBgCgpTaSBxdWlzacOpcmFtb3MgcmVjdXBlcmFyIGxvcyBkYXRvcyBlbiBlbCBmb3JtYXRvIGVuIGVsIHF1ZSBlc3RhbW9zIGhhYml0dWFkb3MgKGRhdGFmcmFtZXMpIGxvIGhhcsOtYW1vcyBhc8OtOgoKCgpgYGB7ciwgZWNobyA9IFQsIGV2YWwgPSBGQUxTRX0KIyAoISEhKQpwcmltZXJfaXJpcyAgPC0gSVJJU19saXN0W1sxXV0Kc2VndW5kb19pcmlzIDwtIElSSVNfbGlzdFtbMl1dCmBgYAoKPGJyPgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgo8YnI+CgojIyAgT3Ryb3MgZm9ybWF0b3MgcHJvcGlldGFyaW9zCgpWZXJlbW9zIFNQU1MsIFN0YXRhIHkgU0FTLgoKVXRpbGl6YXJlbW9zIGVsIG1pc21vIHBhY2thZ2UgcXVlIHVzYSBSU3R1ZGlvOiBgaGF2ZW5gLiBTaSBuZWNlc2l0w6FzZW1vcyBpbXBvcnRhciBvdHJvIHRpcG8gZm9ybWF0byBlcyBtdXkgcG9zaWJsZSBxdWUgc2UgcHVlZGEgaGFjZXIgY29uIGxvcyBsb3MgcGFja2FnZXMgYGZvcmVpZ25gIHkgYHJpb2AKCjxicj4KCi0tLS0tLS0tLS0tLS0tLS0KCiMjIyBTUFNTCgoKIyMjIyBFeHBvcnRhY2nDs24gYSBTUFNTIChmb3JtYXRvIGAuc2F2YCkKCgpgYGB7ciwgZWNobyA9IFQsIGV2YWwgPSBGQUxTRX0KIyBsaWJyYXJ5KGhhdmVuKQp3cml0ZV9zYXYoaXJpcywgIi4vZGF0b3MvcHJ1ZWJhcy9pcmlzLnNhdiIpCmBgYAo8YnI+CgojIyMjIEltcG9ydGFjaW9uIGRlIGZpY2hlcm9zIGAuc2F2YCAodGIgYC5wb3JgKQoKCmBgYHtyLCBlY2hvID0gVCwgZXZhbCA9IEZBTFNFfQojIGxpYnJhcnkoaGF2ZW4pCmlyaXNfaW1wX3Nwc3MgPC0gcmVhZF9zcHNzKCIuL2RhdG9zL3BydWViYXMvaXJpcy5zYXYiKQpgYGAKCgo8YnI+CgotLS0tLS0tLS0tLS0tLS0tCgoKIyMjIFN0YXRhCgoKIyMjIyBFeHBvcnRhY2nDs24gYSBTVEFUQSAoZm9ybWF0byBgLmR0YWApCgpTZSBwdWVkZSBleHBvcnRhciBjb24gYGhhdmVuYCAocGVybyBubyBwZXJtaXRlIGxhYmVsbGVkIGRhdGEhISksIGFzw60gcXVlIG1lam9yIGhhY2VybG8gZXN0YSB2ZXogY29uIGVsIHBhY2thZ2UgYGZvcmVpZ25gCgpgYGB7ciwgZWNobyA9IFQsIGV2YWwgPSBGQUxTRX0KIyBsaWJyYXJ5KGZvcmVpZ24pCndyaXRlLmR0YShpcmlzLCAiLi9kYXRvcy9wcnVlYmFzL2lyaXMuZHRhIikKYGBgCgoKPGJyPgoKIyMjIyBJbXBvcnRhY2lvbiBkZSBmaWNoZXJvcyBTVEFUQSAoYC5kdGFgKQoKCmBgYHtyLCBlY2hvID0gVCwgZXZhbCA9IEZBTFNFfQojIGxpYnJhcnkoaGF2ZW4pCmlyaXNfaW1wX3N0YXRhIDwtIHJlYWRfc3RhdGEoIi4vZGF0b3MvcHJ1ZWJhcy9pcmlzLmR0YSIpCmBgYAoKCgo8YnI+CgotLS0tLS0tLS0tLS0tLS0tCgoKIyMjIFNBUwoKCgojIyMjIEV4cG9ydGFjacOzbiBhIFNBUyAKCioqYGhhdmVuYCoqIGV4cG9ydGEgYmllbiBhIFNBUzsgcGVybyAuLi4gbG9zIG5vbWJyZXMgZGUgbGFzIHZhcmlhYmxlcyBubyBwdWVkZW4gY29udGVuZXIgcHVudG9zLCBhc8OtIHF1ZSB1c2Ftb3Mgb3RybyBmaWNoZXJvIGRlIGRhdG9zIGBtdGNhcnNgLiBQb2Ryw61hbW9zIGhhYmVyIGNhbWJpYWRvIGVsIG5vbWJyZSBkZSBsYXMgY29sdW1uYXMgZGUgaXJpcyAocXVpdGFuZG8gbG9zIHB1bnRvcyksIHBlcm8gbG8gZGVqYW1vcyBwYXJhIGVsIHByw7N4aW1vIHR1dG9yaWFsLgoKYGBge3IsIGVjaG8gPSBULCBldmFsID0gRkFMU0V9CiMgbXRjYXJzIGVzIHVuIGRhdGFzZXQgIGRlbCBwa2cgZ2dwbG90MiwgYXNpIHF1ZSBnZ3Bsb3QyIGRlYmUgZXN0YXIgY2FyZ2FkbwojIGxpYnJhcnkoZ2dwbG90MikKIyBsaWJyYXJ5KGhhdmVuKQp3cml0ZV9zYXMobXRjYXJzLCAiLi9kYXRvcy9wcnVlYmFzL210Y2Fycy5zYXMiKSAKYGBgCgoKCjxicj4KCiMjIyMgSW1wb3J0YWNpb24gZGUgZmljaGVyb3MgU0FTIAoKCmBgYHtyLCBlY2hvID0gVCwgZXZhbCA9IEZBTFNFfQojIGxpYnJhcnkoaGF2ZW4pCm10Y2Fyc19pbXBfc2FzIDwtIHJlYWRfc2FzKCIuL2RhdG9zL3BydWViYXMvbXRjYXJzLnNhcyIpCmBgYAoKCjxicj4KCi0tLS0tLS0tLS0tLS0tLS0KCjxicj4KCiMgNS4gRm9ybWF0byhzKSBwcm9waW9zIGRlIFIKCkd1YXJkYXIgZGF0b3MgZW4gZm9ybWF0b3MgY29tbyB0eHQsIGNzdiBvIEV4Y2VsIGVzIGxvIG3DoXMgaGFiaXR1YWwgc2kgcXVpZXJlcyBhYnJpciBlc3RvcyBkYXRvcyBlbiBvdHJvcyBwcm9ncmFtYXM7IHBlcm8gYWwgZ3JhYmFyIGVuIGVzdG9zIGZvcm1hdG9zIGd1YXJkYXMgbG9zIGRhdG9zLCBQRVJPIG5vIGd1YXJkYXMgbGEgZXN0cnVjdHVyYSBkZSBsb3MgZGF0b3M7IGVzIGRlY2lyLCBzaSBwb3IgZWplbXBsbyB1bmEgY29sdW1uYSBsYSBoYXMgZGVmaW5pZG8gY29tbyB1biBmYWN0b3IgbyBjb21vIGludGVnZXIsIGVzdGEgaW5mb3JtYWNpw7NuIHNlIHBlcmRlcsOhLiBFbiBlc3RvcyBjYXNvcywgdW5hIHNvbHVjacOzbiBlcyB1c2FyIGVsIGZvcm1hdG8gcHJvcGlvIGRlIFIuIAoKSGF5IGRvcyBwb3NpYmlsaWRhZGVzOgoKICAtIHNpIHF1aWVyZXMgZ3JhYmFyIHVuIHNvbG8gb2JqZXRvLCBlcyBwcmVmZXJpYmxlIGhhY2VybG8gY29tbyBgUmRzYAogIC0gc2kgcXVpZXJlcyBncmFiYXIgdmFyaW9zIG9iamV0b3MgdGllbmVzIHF1ZSBoYWNlcmxvIGNvbW8gYFJEYXRhYCBvIGFicmV2aWFkbyBjb21vIGBSZGFgCgo8YnI+CgotLS0tLS0tLS0tLS0tLS0tCgojIyBSRGF0YQoKKipSRGF0YSoqIChvIFJkYSkgZXMgdW4gZm9ybWF0byBlc3BlY2lmaWNvIGRlIFIsIHBlcm8gdGllbmUgZG9zIHZlbnRhamFzOgoKICAtIGVzIGJhc3RhbnRlIGVmaWNpZW50ZSAKICAtIHBlcm1pdGUgZ3VhcmRhciB2YXJpb3Mgb2JqZXRvcyBlbiB1biDDum5pY28gYXJjaGl2bwoKPGJyPiAKClBhcmEgKipleHBvcnRhcioqIGBteV9pcmlzYCBhIHVuIGZpY2hlcm8gZW4gZm9ybWF0byBgLlJEYXRhYCB1dGlsaXphcmVtb3MgbGEgZnVuY2nDs24gYHNhdmUoKWA6IHNvbG8gaGF5IHF1ZSBkZWNpcmxlIGVsIG9iamV0byBxdWUgcXVlcmVtb3MgZXhwb3J0YXIgKGVuIGVzdGUgY2FzbyB1biBkZi90aWJibGUgbGxhbWFkbyAibXlfaXJpcyIpIHkgZWwgbm9tYnJlIChqdW50byBjb24gbGEgcnV0YSkgZGVsIGFyY2hpdm8gZG9uZGUgcXVlcmVtb3MgZ3VhcmRhcmxvLgoKCmBgYHtyLCBldmFsID0gRkFMU0V9CiMtIGV4cG9ydGEgZW4gZm9ybWF0byAuUkRhdGEgZWwgZGYgbXlfaXJpcyBhbCBmaWNoZXJvICJpcmlzLlJEYXRhIi4gCnNhdmUoaXJpcywgZmlsZSA9ICIuL2RhdG9zL3BydWViYXMvaXJpcy5SRGF0YSIpCmBgYAoKCgo8YnI+CkVsIGZvcm1hdG8gYC5SRGF0YWAgdGllbmVuIGxhIHZlbnRhamEgZGUgcXVlIHB1ZWRlcyBndWFyZGFyIHZhcmlvcyBvYmpldG9zIGEgbGEgdmV6LiAKCgpgYGB7ciwgZWNobyA9IFQsIGV2YWwgPSBGQUxTRX0Kc2F2ZShtdGNhcnMsIGlyaXMsICBmaWxlID0gIi4vZGF0b3MvcHJ1ZWJhcy9tdGNhcnNfYW5kX2lyaXMuUkRhdGEiKQpgYGAKCkluY2x1c28gcHVlZGUgZ3VhcmRhciAgdG9kb3MgbG9zIG9iamV0b3MgZGUgbGEgc2VzacOzbi4gTWVqb3Igbm8gaGFjZXJsbwoKYGBge3IsIGVjaG8gPSBULCBldmFsID0gRkFMU0V9CnNhdmUobGlzdCA9IGxzKGFsbCA9IFRSVUUpLCBmaWxlPSAiLi9kYXRvcy9wcnVlYmFzL2FsbF9vYmplY3RzLlJEYXRhIikKYGBgCgpPIHRvZG8gZWwgZXNwYWNpbyBkZSB0cmFiYWpvOgoKYGBge3IsIGVjaG8gPSBULCBldmFsID0gRkFMU0V9CnNhdmUuaW1hZ2UoZmlsZSA9ICIuL2RhdG9zL3BydWViYXMvbXlfd29ya19zcGFjZS5SRGF0YSIpCmBgYAoKcGFyYSBsdWVnbyBjYXJnYXJsb3MgY29uOgoKCmBgYHtyLCBlY2hvID0gVCwgZXZhbCA9IEZBTFNFfQpsb2FkKCIuL2RhdG9zL3BydWViYXMvYWxsX29iamVjdHMuUkRhdGEiKQpsb2FkKCIuL2RhdG9zL3BydWViYXMvbXlfd29ya19zcGFjZS5SRGF0YSIpCmBgYAoKCgo8YnI+CgpQYXJhIGNhcmdhciBvICoqaW1wb3J0YXIqKiBkYXRvcyBlbiBmb3JtYXRvIGAuUkRhdGFgIGJhc3RhIGNvbiBhcnJhc3RyYXJsb3MgZGVudHJvIGRlIFJTdHVkaW8sIG8gc2VndWlyIGxhIHJ1dGEgZGUgbWVuw7pzIGAgRmlsZSA+IE9wZW4gRmlsZSBgIHBlcm8gcGFyYSBoYWNlcmxvIGVuIHVuIHNjcmlwdCBzZSBoYWNlIGFzw606CgpgYGB7ciwgZXZhbCA9IEZBTFNFfQojLSBpbXBvcnRhbW9zIGVsIGZpY2hlcm8gIm15X2RhdGEyLlJEYXRhIgpsb2FkKGZpbGUgPSAiLi9kYXRvcy9wcnVlYmFzL2lyaXMuUkRhdGEiKQpgYGAKCjxicj4KCgpVbiBwb3NpYmxlIHBlZ2EgZXMgcXVlIGFsIGNhcmdhciBkYXRvcyBjb24gYGxvYWQoKWAgc2UgY2FyZ2FuIHRvZG9zIGxvcyBkYXRvcyBxdWUgZ3VhcmRhc3RlIHkgYWRlbcOhcyBzZSBjYXJnYW4gZW4gZWwgZXNwYWNpbyBkZSB0cmFiYWpvIGNvbiBlbCBtaXNtbyAgbm9tYnJlIGNvbiBxdWUgbG9zIGd1YXJkYXN0ZS4gRXN0byBwdWVkZSBzZXIgdW5hIHBlZ2EgcHVlcyBlcyBwb3NpYmxlIHF1ZSBoYXlhIHVuIG9iamV0byBxdWUgc2UgbGxhbWUgaWd1YWwgcXVlIHVuIG9iamV0byBjb24gZWwgcXVlIGVzdMOhcyB0cmFiYWphbmRvIGVuIFJTdHVkaW8uCgoKCgojIyBSRFMgKFNlcmlhbGl6ZWQgUiBvYmplY3RzKQoKVW5hICJkZXN2ZW50YWphIiBkZWwgZm9ybWF0byBgUkRhdGFgIGVzIHF1ZSBhbCBpbXBvcnRhciB1biBmaWNoZXJvIC5SRGF0YSwgbG9zIG9iamV0b3MgcXVlIGNvbnRpZW5lIHNlIGNhcmdhbiBzaWVtcHJlIGNvbiBlbCBub21icmUgY29uIGVsIHF1ZSBmdWVyb24gZ3JhYmFkb3MuIMK/UXXDqSBtw6FzIGRhPyBCdWVubywgcHVlZGUgcGFyZWNlciBxdWUgbm8gZXMgbXV5IGltcG9ydGFudGUsIHBlcm8gYSB2ZWNlcyBlc3RvIHB1ZWRlIGxpbWl0YXIgZWwgd29ya2Zsb3csIGFzw60gcXVlIG11Y2hhcyB2ZWNlcyBlcyBwcmVmZXJpYmxlIGdyYWJhciBjb21vIGAuUkRTYCBjb24gbGEgZnVuY2nDs24gYHdyaXRlX3JkcygpYC4gTGEgcGVnYSBlcyBxdWUgZXN0w6EgZnVuY2nDs24gc29sbyBwZXJtaXRlIGd1YXJkYXIgdW4gb2JqZXRvLgoKUGFyYSAqKmV4cG9ydGFyKiogaXJpcyBhIGZvcm1hdG8gUkRTIGhhY2Vtb3M6IAoKYGBge3IsIGVjaG8gPSBULCBldmFsID0gRkFMU0V9CndyaXRlX3JkcyhpcmlzLCAiLi9kYXRvcy9wcnVlYmFzL2lyaXMucmRzIikKYGBgCgo8YnI+CgpQYXJhIGxlZXIgbyAqKmltcG9ydGFyKiogZGF0b3MgUkRTIChoYXkgcXVlIGFzaWduYXJsZSB1biBub21icmUsIHF1ZSBwdWVkZSBzZXIgbyBubyBlbCBub21icmUgb3JpZ2luYWwpIGhhY2Vtb3M6CgpgYGB7ciwgZWNobyA9IFQsIGV2YWwgPSBGQUxTRX0KaXJpc19pbXBfcmRzIDwtIHJlYWRSRFMoIi4vZGF0b3MvcHJ1ZWJhcy9pcmlzLnJkcyIpCmBgYAoKCgoKPGJyPgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCjxicj4KCgojIDYuIE90cm9zIGZvcm1hdG9zCgo8YnI+CgpMb3MgZm9ybWF0b3MgbcOhcyBmcmVjdWVudGVzIChhbCBtZW5vcyBlbiBudWVzdHJhIMOhcmVhKSBjb250aW7DumFuIHNpZW5kbyBsb3MgLmNzdiwgLnhscywgLi4uIFBFUk8gZXhpc3RlbiBtdWNob3Mgb3Ryb3MgZm9ybWF0b3MuIAoKCkhheSAyIGZvcm1hdG9zIHF1ZSBlcyBidWVubywgYWwgbWVub3MsIHNhYmVyIHF1ZSBleGlzdGVuLCBwb3JxdWUgY2FkYSB2ZXogc29uIG3DoXMgZnJlY3VlbnRlczogCgogIC0gSlNPTiAoSmF2YVNjcmlwdCBPYmplY3QgTm90YXRpb24pIAogIC0gWE1MIChFeHRlbnNpYmxlIE1hcmt1cCBMYW5ndWFnZSkuIAogIAogIApQYXJhIEpTT04sIHNlIHJlY29taWVuZGEgdXNhciBlbCBwYXF1ZXRlIGBqc29ubGl0ZWAgeSBwYXJhIFhNTCwgYHhtbDJgLgoKPGJyPgoKCiMjIyMgRWwgcGFja2FnZSBgcmlvYCBlcyBsYSBuYXZhamEgc3VpemEgZGVsIEkvTyBkYXRhIGVuIFIKClNpIGhheSBhbGfDum4gZm9ybWF0byBkZSBkYXRvcyBxdWUgcXVpZXJhcyBhYnJpciBlbiBSIHkgbm8gbG8gaGF5YW1vcyB0cmF0YWRvLCBwcm9iYWJsZW1lbnRlIGVsIHBhY2thZ2UgYHJpb2Agc2VhIGNhcGF6IGRlIGltcG9ydGFybG8uIFtBcXXDrV0oaHR0cHM6Ly9naXRodWIuY29tL2xlZXBlci9yaW8jc3VwcG9ydGVkLWZpbGUtZm9ybWF0cykgdGllbmVzIGxhIGxpc3RhIGRlIGxvcyBmb3JtYXRvcyBxdWUgcHVlZGUgaW1wb3J0YXI7IGFkZW3DoXMgdG9kb3MgZWxsb3MgY29uIHVuYSBzb2xhIGZ1bmNpw7NuIChgaW1wb3J0KClgKS4gUGFyYSBleHBvcnRhciBoYXkgcXVlIHVzYXIgbGEgZnVuY2nDs24gYGV4cG9ydCgpYAoKCkVsIHBhcXVldGUgYHJpb2AgZXMgbXV5IGbDoWNpbCBkZSB1c2FyLCBwZXJvIGVuIHJlYWxpZGFkIGxvIHF1ZSBoYWNlIGVzIGxsYW1hci91dGlsaXphciBvdHJvcyBwYXF1ZXRlcyBwYXJhIGhhY2VyIGVsIHRyYWJham8uIFBvciBlamVtcGxvIHBhcmEgZXhwb3J0YXIvaW1wb3J0YXIgYSBTUFNTIHVzYSBlbCBwYXF1ZXRlIGBoZWF2ZW5gLCBwYXJhIE1hdGxhYiB1c2EgZWwgcGFxdWV0ZSBgcm1hdGlvYCwgYXPDrSBxdWUgc2kgcXVpZXJlcyBpbXBvcnRhciBkYXRvcyBkZSBNYXRsYWIgdGVuZHLDoXMgcXVlIHRlbmVybG8gaW5zdGFsYWRvLiBBbCBjYXJnYXIgZWwgcGFxdWV0ZSBgcmlvYCBjb24gYGxpYnJhcnkocmlvYCBub3MgYXZpc2Fyw6EgZGUgc2kgbm9zIGZhbHRhIGFsZ8O6biBwYXF1ZXRlIHkgc2kgcXVlcmVtb3MgbG9zIGluc3RhbGFyw6EgcG9yIG5vc290cm9zIGNvbiBsYSBmdW5jacOzbiBgcmlvOjppbnN0YWxsX2Zvcm1hdHMoKWAKCgpWZcOhbW9zIGNvbW8gZnVuY2lvbmEgYHJpb2AuIFBhcmEgZWxsbyBwcmltZXJvICoqZXhwb3J0YXJlbW9zKiogZWwgZmljaGVybyBkZSBkYXRvcyBgbXRjYXJzYCBhZGlmZXJlbnRlcyBmb3JtYXRvczoKCmBgYHtyLCBlY2hvID0gVFJVRSwgZXZhbCA9IEZBTFNFfQpsaWJyYXJ5KHJpbykKZXhwb3J0KG10Y2FycywgIi4vZGF0b3MvcHJ1ZWJhcy9tdGNhcnMuY3N2IikgICAjIGNvbW1hLXNlcGFyYXRlZCB2YWx1ZXMKZXhwb3J0KG10Y2FycywgIi4vZGF0b3MvcHJ1ZWJhcy9tdGNhcnMucmRzIikgICAjIFIgc2VyaWFsaXplZApleHBvcnQobXRjYXJzLCAiLi9kYXRvcy9wcnVlYmFzL210Y2Fycy5zYXYiKSAgICMgU1BTUwpleHBvcnQobXRjYXJzLCAiLi9kYXRvcy9wcnVlYmFzL210Y2Fycy5qc29uIikgICMgSlNPTgpleHBvcnQobXRjYXJzLCAiLi9kYXRvcy9wcnVlYmFzL210Y2Fycy5hcmZmIikgICMgV2VrYSBBdHRyaWJ1dGUtUmVsYXRpb24gRmlsZSBGb3JtYXQKYGBgCgo8YnI+CgpBZGVtw6FzIHBlcm1pdGUgZXhwb3J0YXIgeSAqKmNvbXByaW1pciBsb3MgZGF0b3MgZGlyZWN0YW1lbnRlKio6CgpgYGB7ciwgZWNobyA9IFRSVUUsIGV2YWwgPSBGQUxTRX0KbGlicmFyeShyaW8pCmV4cG9ydChtdGNhcnMsICIuL2RhdG9zL3BydWViYXMvbXRjYXJzLnRzdi56aXAiKSAjIFRTViAmIFppcCBpdApgYGAKCjxicj4KClBhcmEgKippbXBvcnRhcioqIGxvcyBmaWNoZXJvcyBxdWUgaGVtb3MgZXhwb3J0YWRvIHByZXZpYW1lbnRlOgoKCmBgYHtyLCBlY2hvID0gVFJVRSwgZXZhbCA9IEZBTFNFfQpsaWJyYXJ5KHJpbykKbXRjYXJzX2NzdiAgPC0gaW1wb3J0KCIuL2RhdG9zL3BydWViYXMvbXRjYXJzLmNzdiIpICAjIENTVgptdGNhcnNfc3BzcyA8LSBpbXBvcnQoIi4vZGF0b3MvcHJ1ZWJhcy9tdGNhcnMuc2F2IikgICMgU1BTUyAoLnNhdikKYGBgCgoKQ29tbyBjdXJpb3NpZGFkIGRlY2lyIHF1ZSBgcmlvYCBpbXBvcnRhIGxvcyBkYXRvcyBjb21vIGRhdGEuZnJhbWVzLCBubyBjb21vIHRpYmJsZXMsIHBlcm8gcHVlZGVzIGZvcnphcmxvIGNvbjoKCmBgYHtyLCBlY2hvID0gVFJVRSwgZXZhbCA9IEZBTFNFfQpsaWJyYXJ5KHJpbykKbXRjYXJzX2NzdiAgPC0gaW1wb3J0KCIuL2RhdG9zL3BydWViYXMvbXRjYXJzLmNzdiIsIHNldGNsYXNzID0gInRpYmJsZSIpICAjIENTVgpgYGAKCkVuIFtlc3RlIHBvc3RdKGh0dHA6Ly9yaXRzb2tpZ3Vlc3Muc2l0ZS9kb2NzLzIwMTgvMDMvMjUvdG9kYXktb24tdHdpdHRlci1pLWxlYXJuZWQvKSB0YW1iacOpbiBleHBsaWNhbiBxdWUgYHJpb2AgcHVlZGUgc2VyIHVuYSBhbHRlcm5hdGl2YSBhIGBydmVzdGAgeS9vIGB4bWwyYCBwYXJhIGltcG9ydGFyIHRhYmxhcyBodG1sIGVuIGludGVybmV0LiBQb3IgZWplbXBsbzoKCmBgYHtyLCBlY2hvID0gVFJVRSwgZXZhbCA9IEZBTFNFfQpteV91cmwgPC0gImh0dHA6Ly93d3cuc2NvcmVzd2F5LmNvbS8/c3BvcnQ9c29jY2VyJnBhZ2U9Y29tcGV0aXRpb24maWQ9OCIKdGFibGUgPC0gaW1wb3J0KG15X3VybCwgZm9ybWF0ID0gImh0bWwiLCB3aGljaCA9IDIpCmBgYAoKCgoKU2kgdGFtcG9jbyBgcmlvYCBwdWVkZSBsZWVyIGVsIGZvcm1hdG8gcXVlIG5lY2VzaXRhcywgZXMgbXV5IHByb2JhYmxlIHF1ZSBhbGd1aWVuIGhheWEgZXNjcml0byB1biBwYWNrYWdlIHBhcmEgaGFjZXJsbzsgdGVuZHLDoXMgcXVlIHZlciBzaSBleGlzdGUgYnVzY2FuZG8gZW4gaW50ZXJuZXRlLgoKClJlY2llbnRlbWVudGUgaGEgYXBhcmVjaWRvIG90cm8gbWV0YS1wa2cgcGFyYSBsZWVyIGRhdG9zLiBFcyBlbCBwa2cgYHJlYWRpdGAuIEHDum4gbm8gbG8gaGUgdXNhZG8gcGVybywgcG9yIHZhcmlhcyByYXpvbmVzLCBubyBxdWllcm8gb2x2aWRhcm1lIGRlIMOpbC4gUHVlZGVzIHZlcmxvIFthcXXDrV0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL3JlYWRpdC9pbmRleC5odG1sKQoKPGJyPgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0KCjxicj4KCgojIDcuIERlc2NhcmdhciBkYXRvcyBkZSBpbnRlcm5ldAoKPGJyPgoKSGF5IG11Y2jDrXNpbW9zIGRhdG9zIGVuIGludGVybmV0IHBhcmEgZGVzY2FyZ2FyOyBzaWVtcHJlIHBvZGVtb3MgZGVzY2FyZ2FybG9zIHVzYW5kbyBlbCBuYXZlZ2Fkb3IsIFBFUk8gbGEgZmlsb3NvZsOtYSBkZWwgY3Vyc28gZXMgKHNpIHBvZGVtb3MpIGhhY2VybG8gdG9kbyBkZXNkZSBSL1JTdHVkaW8KCkRlc2RlIFJTdHVkaW8sIHBvZGVtb3MgZGVzY2FyZ2FyIGRhdG9zIGNvbiBsYXMgbWlzbWFzIGZ1bmNpb25lcyBxdWUgdXPDoWJhbW9zIHBhcmEgY2FyZ2FyIGVuIGVsIGVudG9ybm8gZGUgdHJhYmFqbyBsb3MgIGRhdG9zIHF1ZSB0ZW7DrWFtb3MgZW4gbnVlc3RybyBQQy4gTGEgw7puaWNhIGRpZmVyZW5jaWEgY29uc2lzdGUgZW4gcXVlLCBlbiBsdWdhciBkZSBwcm9wb3JjaW9uYXIgbGEgcnV0YSBhbCBmaWNoZXJvLCB0ZW5kcmVtb3MgcXVlIHByb3BvcmNpb25hciBsYSBydXRhIGRlIGludGVybmV0LiBQb3IgZWplbXBsbzoKCjxicj4KCmBgYHtyLCBlY2hvID0gVFJVRSwgZXZhbCA9IEZBTFNFfQojIGNhcmdhbW9zIGxvcyBkYXRvcyBkZWwgZmljaGVybyAiYmlvMjYwLWhlaWdodHMuY3N2Igp1cmwgPC0gImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9kYXRhc2NpZW5jZWxhYnMvZGF0YS9tYXN0ZXIvYmlvMjYwLWhlaWdodHMuY3N2IgpkYXRvcyA8LSByZWFkX2Nzdih1cmwpCmBgYAoKCgo8YnI+CgpBIHZlY2VzIHBvZGVtb3MgbmVjZXNpdGFyIGhhY2VyIHVuYSBjb3BpYSBkZSBsb3MgZGF0b3MgYSBudWVzdHJvIG9yZGVuYWRvci4gRW4gZXN0ZSBjYXNvLCBsbyBxdWUgeW8gaGFyw61hIGVzIGNhcmdhciBsb3MgZGF0b3MgeSBsdWVnbyBleHBvcnRhcmxvcyBhIC5yZHM7IHBlcm8gdGFtYmnDqW4gcG9kZW1vcyBoYWNlcmxvIGRpcmVjdGFtZW50ZSBjb24gbGEgZnVuY2nDs24gYGRvd25sb2FkLmZpbGUoKWA6Cgo8YnI+CgpgYGB7ciwgZWNobyA9IFRSVUUsIGV2YWwgPSBGQUxTRX0KIyBkZXNjYXJnYW1vcyB5IGFsbWFjZW5hbW9zIGVuIG51ZXN0cm8gUEMgbG9zIGRhdG9zIGRlbCBmaWNoZXJvICJiaW8yNjAtaGVpZ2h0cy5jc3YiCnVybCA8LSAiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2RhdGFzY2llbmNlbGFicy9kYXRhL21hc3Rlci9iaW8yNjAtaGVpZ2h0cy5jc3YiCmRlc3Rpbm8gPC0gIi4vZGF0b3MvcHJ1ZWJhcy9iaW8yNjAtaGVpZ2h0cy5jc3YiCmRvd25sb2FkLmZpbGUodXJsLCBkZXN0aW5vKQpkYXQgPC0gcmVhZC5jc3YoZGVzdGlubykKYGBgCgo8YnI+CgpBIHZlY2VzLCBsYSBmdW5jacOzbiBkZSBSLWJhc2UgYGRvd25sb2FkLmZpbGVgIHB1ZWRlIHRlbmVyIHByb2JsZW1hcyBzaSBlbCBwcm90b2NvbG8gZXMgaHR0cHMuIEVuIGVzdG9zIGNhc29zLCBsYSBmdW5jacOzbiBgZG93bG9hZCgpYCBkZWwgcGtnIGRvd25sb2FkZXIgcHVlZGUgc29sdWNpb25hcmxvOgoKYGBge3IsIGVjaG8gPSBUUlVFLCBldmFsID0gRkFMU0V9CiNpbnN0YWxsLnBhY2thZ2VzKCJkb3dubG9hZGVyIikKbGlicmFyeShkb3dubG9hZGVyKQp1cmwgPC0gImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9kYXRhc2NpZW5jZWxhYnMvZGF0YS9tYXN0ZXIvYmlvMjYwLWhlaWdodHMuY3N2IgpmaWxlbmFtZSA8LSBiYXNlbmFtZSh1cmwpCmRlc3Rpbm8gPC0gcGFzdGUwKCIuL2RhdG9zL3BydWViYXMvIiwgZmlsZW5hbWUpCmRvd25sb2FkKHVybCxkZXN0aW5vKQpkYXQgPC0gcmVhZC5jc3YoZGVzdGlubykKYGBgCgoKPGJyPgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0KCjxicj4KCgoKIyA4LiBBUEkncyB5IFdlYiBTY3JhcHBpbmcKCjxicj4KCkVsIHByb2Nlc28geSBhY2Npb25lcyBwYXJhIHJlY29waWxhciBpbmZvcm1hY2nDs24gZGUgbGEgV2ViIHNlIGNvbm9jZSBjb21vIFt3ZWIgc2NyYXBwaW5nXShodHRwczovL2VzLndpa2lwZWRpYS5vcmcvd2lraS9XZWJfc2NyYXBpbmcpLiBFc3RlIHByb2Nlc28gc2UgcHVlZGUgaGFjZXIgbWFudWFsbWVudGUsIHBlcm8gbG8gaGFiaXR1YWwgZXMgYXV0b21hdGl6YXJsbyB1dGlsaXphbmRvIHNvZnR3YXJlLiBTZSBwdWVkZSBhY2NlZGVyIGEgbG9zIGRhdG9zIGRpcmVjdGFtZW50ZSBwZXJvIGFjdHVhbG1lbnRlIGVzIG11eSBjb23Dum4gaGFjZXJsbyBhIHRyYXbDqXMgZGUgQVBJcywgeWEgcXVlIGxhIG1heW9yw61hIGRlIG9yZ2FuaXNtb3MvZW1wcmVzYXMgdGllbmVuIHVuYSBvIHZhcmlhcyBBUElzLiAKCgpBUEkgc2lnbmlmaWNhICJBcGxpY2F0aW9uIFByb2dyYW1taW5nIEludGVyZmFjZSIgeSBzZSBwdWVkZSBlbnRlbmRlciBjb21vIHVuIG1lY2FuaXNtbyBxdWUgbm9zIHBlcm1pdGUgaW50ZXJhY3R1YXIgKHBvciBlamVtcGxvIHBhcmEgaGFjZXIgdW5hIHBldGljacOzbiBkZSBkYXRvcykgY29uIHVuIHNlcnZpZG9yIGRlIGludGVybmV0LiBQb3IgZWplbXBsbywgbXVjaG9zIGJhbmNvcyB0aWVuZW4gQVBJcyBhIGxhcyBxdWUgc2UgbGVzIHB1ZWRlbiBoYWNlciBwZXRpY2lvbmVzLCBlc3RvIGhhY2UgcG9zaWJsZSBxdWUgc2UgZGVzYXJyb2xsZW4gYXBwcyBwYXJhIGhhY2VyIGNpZXJ0YXMgb3BlcmFjaW9uZXMgYmFuY2FyaWFzOyBlcyBkZWNpciwgdW5hIEFQSSBlcyB1biBtZWNhbmlzbW8gcXVlIG5vcyBwZXJtaXRlIGFjY2VkZXIgeS9vIGludGVyYWN0dWFyIGNvbiBkZXRlcm1pbmFkYXMgZnVuY2lvbmVzIGRlIHVuIHNlcnZpY2lvIHdlYi4gCgpMYXMgQVBJcyBmYWNpbGl0YW4gbXVjaG8gbGEgcmVjb3BpbGFjacOzbiBkZSBkYXRvcyBhbCBwb2RlcnNlIGFjY2VkZXIgYSBlbGxhcyBkZSBmb3JtYSBwcm9ncmFtw6F0aWNhIHlhIHF1ZSBwcm92ZWVuIGRlIHVuIHByb2Nlc28gZGUgYWNjZXNvIGEgZWxsb3MgZXN0YW5kYXJpemFkbzogc2UgZW52w61hIHVuYSAiaHR0cCByZXF1ZXN0IiBhIGxhIEFQSSB5IHNlIHJlY2liZW4gbG9zIGRhdG9zIGVuIHVuIGRldGVybWluYWRvIGZvcm1hdG8sIGdlbmVyYWxtZW50ZSBKU09OLgoKRW4gZWwgZW50b3JubyBSIHNlIHB1ZWRlbiBkZXNhcnJvbGxhciBwYXF1ZXRlcyBwYXJhIGFjY2VkZXIgYSBBUElzOyBwb3IgZWplbXBsbywgdmFtb3MgYSB1dGlsaXphciBlbCBwYXF1ZXRlIGRlIFIgYGV1cm9zdGF0YCBwYXJhIGFjY2VkZXIgYSBsYSBBUEkgZGUgRXVyb3N0YXQgeSBkZXNjYXJnYXIgZGF0b3MgZGlyZWN0YW1lbnRlIGVuIFIuIFZlw6Ftb3NsbzoKCi0tLS0tLS0tLS0tLS0tLS0tLS0KCjxicj4KCgojIyBFdXJvc3RhdCAKCkV1cnNvdGF0IHRpZW5lIHVuYSBBUEkgcXVlIHBlcm1pdGUgaGFjZXIgcGV0aWNpb25lcyBkZSBkYXRvcy4gT2J2aWFtZW50ZSwgcGFyYSBwb2RlciBoYWNlciBwZXRpY2lvbmVzIGRlIGRhdG9zIGEgdHJhdsOpcyBkZSBzdSBBUEkgaGFzIGRlIGNvbm9jZXIgc3Ugc2ludGF4aXM7IHNpIGVzdMOhcyBpbnRlcmVzYWRvIHB1ZWRlcyBlbXBlemFyIFthcXXDrV0oaHR0cDovL2VjLmV1cm9wYS5ldS9ldXJvc3RhdC9kYXRhL3dlYi1zZXJ2aWNlcykuIE5vc290cm9zIGFjY2VkZXJlbW9zIGEgRXVyb3N0YXQgYSB0cmF2w6lzIGRlbCBwYWNrYWdlIFtgZXVyb3N0YXRgXShodHRwczovL3JvcGVuZ292LmdpdGh1Yi5pby9ldXJvc3RhdC8pLiBTaSBlc3RhcyBpbnRlcmVzYWRvIGVuIGJhamFyIGRhdG9zIGRlIEV1cm9zdGF0IGVzIGNvbnZlbmllbnRlIHF1ZSB1c2VzIGVzdGEgW3ZpZ25ldHRlXShodHRwOi8vcm9wZW5nb3YuZ2l0aHViLmlvL2V1cm9zdGF0L2FydGljbGVzL2V1cm9zdGF0X3R1dG9yaWFsLmh0bWwpIHkgbGEgW2NoZWF0IHNoZWV0XShodHRwOi8vcm9wZW5nb3YuZ2l0aHViLmlvL2V1cm9zdGF0L2FydGljbGVzL2NoZWF0c2hlZXQuaHRtbCkuIFZlYW1vcyB1biBlamVtcGxvOgoKCkNvbiBsYSBmdW5jacOzbiBgZ2V0X2V1cm9zdGF0KClgIGVzIHN1ZmljaWVudGUgcGFyYSBiYWphciB1bmEgdGFibGEgZGUgRXVyb3N0YXQgY29uIGVsIHBvcmNlbnRhamUgZGUgZW1wbGVvcyBlbiBzZWN0b3JlcyBjdWx0dXJhbGVzOgoKYGBge3IsIGVjaG8gPSBUUlVFLCBldmFsID0gRkFMU0V9CiMgaW5zdGFsbC5wYWNrYWdlcygiZXVyb3N0YXQiKQpsaWJyYXJ5KCJldXJvc3RhdCIpCmRmIDwtIGdldF9ldXJvc3RhdCgiY3VsdF9lbXBfc2V4IiwgdGltZV9mb3JtYXQgPSAncmF3Jywga2VlcEZsYWdzID0gVCkgICAgICAgIy0gYmFqYW1vcyBsb3MgZGF0b3MgZGUgbGEgdGFibGEgImN1bHRfZW1wX3NleCI6IGVtcGxlbyBjdWx0dXJhbCBwb3IgZ2VuZXJvIgpgYGAKCgo8YnI+CgpVbiBlamVtcGxvIG3DoXMgY29tcGxldG86IGRlc2NhcmdhcmVtb3MgbG9zIGRhdG9zIGRlIGxhIHRhYmxhIGBobHRoX3NpbGNfMTdgIHF1ZSBjb250aWVuZSBkYXRvcyBjb24gbGEgImVzcGVyYW56YSBkZSB2aWRhIHNhbHVkYWJsZSIgcGFyYSBkaWZlcmVudGVzIGHDsW9zIGVuIGxvcyBwYcOtc2VzIGRlIGxhIFVFLgoKYGBge3IsIGVjaG8gPSBUUlVFLCBldmFsID0gVFJVRSwgcmVzdWx0cyA9ICJoaWRlIn0KIyBpbnN0YWxsLnBhY2thZ2VzKCJldXJvc3RhdCIpCmxpYnJhcnkoImV1cm9zdGF0IikKCiMtLS0tLS0tLS0tLS0tLS0tLS0gcG9kZW1vcyBidXNjYXIgdW4gICJ0ZW1hIiBjb24gbGEgZi4gc2VhcmNoX2V1cm9zdGF0KCkKYWEgPC0gc2VhcmNoX2V1cm9zdGF0KCJlbXBsb3ltZW50IiwgdHlwZSA9ICJhbGwiKSAKCiMtLS0tLS0tLS0tLS0tLS0tLS0gZWxlZ2ltb3MgdW5hIHRhYmxhIGRlIEV1cm9zdGF0Cm15X3RhYmxlIDwtICJobHRoX3NpbGNfMTciICAgICAgICAgICMtIGVsZWdpbW9zIHVuYSB0YWJsYTsgcG9yIGVqZW1wbG8gImhsdGhfc2lsY18xNyI6ICJIZWFsdGh5IGxpZmUgZXhwZWN0YW5jeSBiYXNlZCBvbiBzZWxmLXBlcmNlaXZlZCBoZWFsdGgiCmxhYmVsX2V1cm9zdGF0X3RhYmxlcyhteV90YWJsZSkgICAgICMtIGRhIGluZm9ybWFjaW9uIHNvYnJlIGxhIEJhc2UgZGUgZGF0b3MgcSBlc3RhcyBidXNjYW5kbwoKIy0tLS0tLS0tLS0tLS0tLS0tLSBkZXNjYXJnYW1vcyBsb3MgZGF0b3MgY29uIGdldF9ldXJvc3RhdCgpCmRmIDwtIGdldF9ldXJvc3RhdChteV90YWJsZSwgdGltZV9mb3JtYXQgPSAncmF3Jywga2VlcEZsYWdzID0gVCApICAgICAgICMtIGJhamFtb3MgbG9zIGRhdG9zIGRlIHVuYSB0YWJsYQpkZl9sIDwtIGxhYmVsX2V1cm9zdGF0KGRmKSAgICAgICAgIy0gcG9uZSBsYWJlbHM6IFNwYWluIGVuIGx1Z2FyIGRlIHN1IGPDs2RpZ28gKG1hcyBsZWdpYmxlLG1lbm9zIGbDoWNpbCBkZSBwcm9ncmFtYXIpCgojLS0tLS0tLS0tLS0tLS0tLS0tIGxvcyBhcnJlZ2xhbW9zIHVuIHBvY28gCmxpYnJhcnkoInRpZHl2ZXJzZSIpCmxpYnJhcnkoInBqcHYyMDIwLjAxIikgIy0gcmVtb3Rlczo6aW5zdGFsbF9naXRodWIoInBlcmV6cDQ0L3BqcHYyMDIwLjAxIikKYWEgPC0gcGpwX2ZfdmFsb3Jlc191bmljb3MoZGYpICAgICAgICMtIHZlciBsb3MgdmFsb3JlcyB1bmljb3MgZGUgY2FkYSBjb2x1bW5hCmFhIDwtIHBqcF9mX3ZhbG9yZXNfdW5pY29zKGRmX2wpICAgICAjLSB2ZXIgbG9zIHZhbG9yZXMgdW5pY29zIGRlIGNhZGEgY29sdW1uYQpkZiA8LSBsYWJlbF9ldXJvc3RhdChkZiwgY29kZSA9IGMoImdlbyIsICJ1bml0IiwgImluZGljX2hlIikpCmBgYAoKQWhvcmEgdmFtb3MgYSBmdXNpb25hcmxvIGNvbiBkYXRvcyBkZSBsb3MgbMOtbWl0ZXMgZXNwYWNpYWxlcyBkZSBjYWRhIHBhw61zLCBwYXJhIGZpbmFsbWVudGUgaGFjZXIgdW4gZ3LDoWZpY28gZXNwYWNpYWwgLi4uLiBQRVJPIEV1cm9zdGF0IG8gZXVyb3N0YXQgY2FtYmlhcm9uIHN1IEFQSSwgZW50b25jZXMgLi4uLiAgZXN0ZSBjaHVuayBubyBmdW5jaW9uYSBjb24gbGEgdmVyc2nDs24gYWN0dWFsIGRlbCBwYXF1ZXRlIGBldXJvc3RhdGAuIExvIGRlam8gcG9yIHNpIGHDum4gZnVuY2lvbmFzZSBlbiBsb3Mgb3JkZW5hZG9yZXMgZGVsIGF1bGEuCgoKYGBge3IsIGVjaG8gPSBUUlVFLCBldmFsID0gRkFMU0V9CiMtIHNlbGVjY2lvbm8gZGF0b3MgZGUgMjAxNiwgRmVtYWxlcywgeSBIRV81MCB5IGRlc3B1w6lzIGhhZ28gdW4gY3V0IGRlICJ2YWx1ZXMiCmRmX3ggPC0gZGYgJT4lIGZpbHRlcih0aW1lID09ICIyMDE2IikgJT4lICBmaWx0ZXIoc2V4ID09ICJGZW1hbGVzIikgJT4lIGZpbHRlcihpbmRpY19oZV9jb2RlID09ICJIRV81MCIpICU+JSAKICAgICAgICBtdXRhdGUoY2F0ID0gY3V0X3RvX2NsYXNzZXModmFsdWVzLCBuID0gNywgZGVjaW1hbHMgPSAxKSkKbWFwZGF0YSA8LSBtZXJnZV9ldXJvc3RhdF9nZW9kYXRhKGRmX3gsIHJlc29sdXRpb24gPSAiMjAiLCBnZW9jb2x1bW4gPSAiZ2VvX2NvZGUiKSAjLSBmdXNpb25vIGNvbiBnZW8gZGF0YQoKZ2dwbG90KG1hcGRhdGEsIGFlcyh4ID0gbG9uZywgeSA9IGxhdCwgZ3JvdXAgPSBncm91cCkpKwogIGdlb21fcG9seWdvbihhZXMoZmlsbCA9IGNhdCksIGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IC4xKSsKICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIlJkWWxCdSIpICsKICBsYWJzKHRpdGxlID0gIkhlYWx0aHkgbGlmZSBleHBlY3RhbmN5LCAyMDE2IiwKICAgICAgIHN1YnRpdGxlID0gIkhlYWx0aCBleHBlY3RhbmN5IGluIHllYXJzIGF0IDUwIiwKICAgICAgIGZpbGwgPSAiSGVhbHRoeSBsaWZlIGV4cGVjdGFuY3kiLAogICAgICAgY2FwdGlvbiA9ICIoQykgRXVyb0dlb2dyYXBoaWNzIGZvciB0aGUgYWRtaW5pc3RyYXRpdmUgYm91bmRhcmllcyIpICsgdGhlbWVfbGlnaHQoKSArCiAgY29vcmRfbWFwKHhsaW0gPSBjKC0xMiwgNDQpLCB5bGltID0gYygzNSwgNjcpKQpgYGAKCgpFc3RlIHPDrSBmdW5jaW9uYXLDoSBjb24gbGEgbnVldmEgdmVyc2nDs24gZGVsIHBrZyBgZXVyb3N0YXRgCgpgYGB7ciwgZWNobyA9IFRSVUUsIGV2YWwgPSBUUlVFfQpkZl94IDwtIGRmICU+JSBmaWx0ZXIodGltZSA9PSAiMjAxNiIpICU+JSAgZmlsdGVyKHNleCA9PSAiRmVtYWxlcyIpICU+JSBmaWx0ZXIoaW5kaWNfaGVfY29kZSA9PSAiSEVfNTAiKSAlPiUgCiAgICAgICAgbXV0YXRlKGNhdCA9IGN1dF90b19jbGFzc2VzKHZhbHVlcywgbiA9IDcsIGRlY2ltYWxzID0gMSkpCgpnZW9tZXRyaWFzIDwtIGdldF9ldXJvc3RhdF9nZW9zcGF0aWFsKHJlc29sdXRpb24gPSAiMjAiLCBudXRzX2xldmVsID0gIjAiKSAjLSBhaG9yYSBzZSBiYWphbiBsYXMgZ2VvbWV0csOtYXMgeSB0aWVuZXMgcXVlIHVuaXJsYSB0dSBjb24gZHBseXIgKEhheSB1biBQYiBkZSBlbmNvZGluZykKbWFwZGF0YSA8LSBpbm5lcl9qb2luKGdlb21ldHJpYXMsIGRmX3gsIGJ5ID0gYygiZ2VvIiA9ICJnZW9fY29kZSIpKQoKcCA8LSBnZ3Bsb3QobWFwZGF0YSkgKwogIGdlb21fc2YoYWVzKGZpbGwgPSBjYXQsIGdlb21ldHJ5ID0gZ2VvbWV0cnkpLCBjb2xvciA9ICJibGFjayIsIHNpemUgPSAuMSkgKwogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiUmRZbEJ1IikgKwogIGxhYnModGl0bGUgPSAiSGVhbHRoeSBsaWZlIGV4cGVjdGFuY3ksIDIwMTYiLAogICAgICAgc3VidGl0bGUgPSAiSGVhbHRoIGV4cGVjdGFuY3kgaW4geWVhcnMgYXQgNTAiLAogICAgICAgZmlsbCA9ICJIZWFsdGh5IGxpZmUgZXhwZWN0YW5jeSIsCiAgICAgICBjYXB0aW9uID0gIihDKSBFdXJvR2VvZ3JhcGhpY3MgZm9yIHRoZSBhZG1pbmlzdHJhdGl2ZSBib3VuZGFyaWVzIikgKyB0aGVtZV9saWdodCgpICsKICBjb29yZF9zZih4bGltID0gYygtMTIsIDQ0KSwgeWxpbSA9IGMoMzUsIDY3KSkgCnAKYGBgCgoKCgoKPGJyPgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgo8YnI+CgojIyBJTkUKCjxicj4KCsK/RWwgSU5FIHRpZW5lIEFQST8gUHVlcyBzw60sIFthcXXDrV0oaHR0cDovL3d3dy5pbmUuZXMvZHluZ3MvRGF0YUxhYi9tYW51YWwuaHRtbD9jaWQ9NDUpIHB1ZWRlcyAidmVybGEiLCBwZXJvIC4uLgoKSGFjZSBwb2NvIHR1dmltb3MgcXVlIHV0aWxpemFyIGFsZ3VuYSB0YWJsYSBkZWwgSU5FIHksIGVuIGx1Z2FyIGRlIHVzYXIgbGEgQVBJLCBub3MgYmFqYW1vcyBsb3MgZGF0b3MgYXPDrToKCgoKYGBge3IsIGVjaG8gPSBUUlVFLCBldmFsID0gRkFMU0V9CmxpYnJhcnkoInB4UiIpICAgICAgICAgICAgICAjLSBwYXJhIHRyYWJhamFyIGNvbiBkYXRvcyBQQy1BeGlzCmxpYnJhcnkoInRpZHl2ZXJzZSIpCmxpYnJhcnkoInBqcHYyMDIwLjAxIikKZmlsZV9uYW1lIDwtICJodHRwOi8vd3d3LmluZS5lcy9qYXhpVDMvZmlsZXMvdC9lcy9weC80MTg5LnB4P25vY2FiPTEiCmRmIDwtIHJlYWQucHgoZmlsZV9uYW1lKSAlPiUgYXMuZGF0YS5mcmFtZSgpICU+JSBhcy50YmwoKSAgICMtIG5vIGZ1bmNpb25hYmEgZW4gMy41IHggJAphYSA8LSBwanB2MjAyMC4wMTo6cGpwX2ZfdmFsb3Jlc191bmljb3MoZGYpICAgICAjLSB2ZXIgbG9zIHZhbG9yZXMgw7puaWNvcyBkZSBjYWRhIGNvbHVtbmEKYGBgCgoKTGEgdmVyZGFkIGVzIHF1ZSBhaG9yYSAoMjAxOSkgaGF5IHVuIHBhcXVldGUgcXVlIGZ1bmNpb25hL2Z1bmNpb25hYmEgYmFzdGFudGUgYmllbjogPGh0dHBzOi8vZ2l0aHViLmNvbS9vZGR3b3JsZG5nL0lORWJhc2VSPgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgo8YnI+CgoKCiMjIEJhbmNvIE11bmRpYWwKCjxicj4KClBhcmEgYWNjZWRlciBhIGxhIFtBUEkgZGVsIEJhbmNvIE11bmRpYWxdKGh0dHBzOi8vZGF0YWhlbHBkZXNrLndvcmxkYmFuay5vcmcva25vd2xlZGdlYmFzZS90b3BpY3MvMTI1NTg5KSBoYXksIGFjdHVhbG1lbnRlLCAyIHBhcXVldGVzIGRlIFI6IFtgV0RJYF0oaHR0cHM6Ly9naXRodWIuY29tL3ZpbmNlbnRhcmVsYnVuZG9jay9XREkpIHkgW2B3YnN0YXRzYF0oaHR0cHM6Ly9naXRodWIuY29tL0dJU1QtT1JOTC93YnN0YXRzKS4gCgo8YnI+CgpQb2RlbW9zIGJhamFyIGRhdG9zIGRlbCBCYW5jbyBNdW5kaWFsIGNvbiBlbCBwYXF1ZXRlIGBXRElgIGFzw606CgpgYGB7ciwgZWNobyA9IFRSVUUsIGV2YWwgPSBGQUxTRX0KI2luc3RhbGwucGFja2FnZXMoIldESSIpCmxpYnJhcnkoIldESSIpCgojLS0tLSBidXNjYW1vcyBkYXRvcyByZWxhY2lvbmFkb3MgY29uIEdEUAphYSA8LSBXRElzZWFyY2goJ2dkcCcpCmFhIDwtIFdESXNlYXJjaCgnZ2RwLipjYXBpdGEuKmNvbnN0YW50JykKCiMtLS0tIGRlc2NhcmdhbW9zICJOWS5HRFAuUENBUC5LRCI6ICBHRFAgcGVyIGNhcGl0YSAoY29uc3RhbnQgMjAxMCBVUyQpCmRmIDwtIFdESShpbmRpY2F0b3IgPSAiTlkuR0RQLlBDQVAuS0QiKQojLS0tLSBwb2RlbW9zIGZpbHRyYXIgbGEgcXVlcnJ5CmRmIDwtIFdESShpbmRpY2F0b3IgPSAiTlkuR0RQLlBDQVAuS0QiLCBjb3VudHJ5ID0gYygnTVgnLCdDQScsJ1VTJyksIHN0YXJ0ID0gMTk2MCwgZW5kID0gMjAxNykKYGBgCgo8YnI+CgpQb2RlbW9zIGJhamFyIGRhdG9zIGRlbCBCYW5jbyBNdW5kaWFsIGNvbiBlbCBwYXF1ZXRlIGB3YnN0YXRzYCBhc8OtOgoKYGBge3IsIGVjaG8gPSBUUlVFLCBldmFsID0gRkFMU0V9CiNpbnN0YWxsLnBhY2thZ2VzKCJ3YnN0YXRzIikKbGlicmFyeSgid2JzdGF0cyIpCgojLS0tLS0tLSAgbGlzdGEgZGUgaW5kaWNhZG9yZXMgZGlzcG9uaWJsZXMKYWEgPC0gd2JfY2FjaGVsaXN0CgojLS0tLSBidXNjYW1vcyBkYXRvcyByZWxhY2lvbmFkb3MgY29uIEdEUAphYSA8LSB3YnNlYXJjaChwYXR0ZXJuID0gImdkcCIpCmFhIDwtIHdic2VhcmNoKCdnZHAuKmNhcGl0YS4qY29uc3RhbnQnKQoKIy0tLS0gZGVzY2FyZ2Ftb3MgIk5ZLkdEUC5QQ0FQLktEIjogIEdEUCBwZXIgY2FwaXRhIChjb25zdGFudCAyMDEwIFVTJCkKZGYgPC0gd2IoaW5kaWNhdG9yID0gIk5ZLkdEUC5QQ0FQLktEIikKCiMtLS0tIHBvZGVtb3MgZmlsdHJhciBsYSBxdWVycnkKZGYgPC0gd2IoaW5kaWNhdG9yID0gIk5ZLkdEUC5QQ0FQLktEIiwgY291bnRyeSA9IGMoJ01YJywnQ0EnLCdVUycpLCBzdGFydGRhdGUgPSAyMDAwLCBlbmRkYXRlID0gMjAxNykKYGBgCgoKPGJyPgoKW0FxdcOtXShodHRwOi8vd3d3Lm1hZ2VzYmxvZy5jb20vMjAxNi8wNC9uZXctci1wYWNrYWdlLXRvLWFjY2Vzcy13b3JsZC1iYW5rLWRhdGEuaHRtbCkgdGVuZW1vcyB1biBwb3N0IGVuIGVsIHF1ZSBzZSB1c2EgZWwgcGtnIHdic3RhdHMgcGFyYSBvYnRlbmVyIGRhdG9zIHkgbHVlZ28gZ3JhZmljYXJsb3MuCgoKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKPGJyPgoKIyMgQ3Jvc3NSZWYKCgpFbCBwYXF1ZXRlIGByY3Jvc3NyZWZgIHBlcm1pdGUgYWNjZWRlciBhIHZhcmlhcyBkZSBsYXMgQVBJcyBkZSBbQ3Jvc3NSZWZdKGh0dHA6Ly9zZWFyY2guY3Jvc3NyZWYub3JnLykuIMK/UXVlIHF1w6kgZXMgQ3Jvc3NSZWY/IFB1ZXMgZXMgdW4gc2VydmljaW8gcXVlIHBlcm1pdGUsIGVudHJlIG90cmFzIGNvc2FzLCBmYWNpbGl0YXIgZWwgcHJvY2VzbyBkZSByZWZlcmVuY2lhciBhcnTDrWN1bG9zIGVuIHR1cyBwYXBlcnMuIFtBcXXDrV0oaHR0cDovL3d3dy5uZW9zY2llbnRpYS5jb20vY3Jvc3NyZWYvKSBsbyBleHBsaWNhbi4gSGF5IG90cm8gcGFja2FnZSBwYXJhIGFjY2VkZXIgYSBDcm9zc1JlZjogW2Bjcm1pbmVyYF0oaHR0cHM6Ly9naXRodWIuY29tL3JvcGVuc2NpL2NybWluZXIpIGVzdGUgcGtnIHBlcm1pdGUgYmFqYXJzZSBlbCB0ZXh0byBkZWwgZG9jdW1lbnRvLCBwZXJvIGNsYXJvLCBlbCB0ZXh0byBoYSBkZSBlc3RhciBkaXNwb25pYmxlISEKCgo8YnI+CgpgYGB7ciwgZWNobyA9IFRSVUUsIGV2YWwgPSBGQUxTRX0KI2luc3RhbGwucGFja2FnZXMoInJjcm9zc3JlZiIpCmxpYnJhcnkoInJjcm9zc3JlZiIpCgojLS0tLS0gY29uIGNyX2NuKCkgcG9kZW1vcyB2ZXIgY29tbyBzZSBjaXRhIHVuIGRldGVybWluYWRvIGFydMOtY3VsbyBlbiB1biBkZXRlcm1pbmFkbyBmb3JtYXRvLCBwb3IgZWplbXBsbyAiYXBhIgpteV9kb2kgPC0gIjEwLjExMTEvai4xNDY3LTY0ODYuMjAxMi4wMTA3Mi54Igpjcl9jbihkb2lzID0gbXlfZG9pLCBmb3JtYXQgPSAidGV4dCIsIHN0eWxlID0gImFwYSIpCgpjcl9jbihkb2lzID0gbXlfZG9pLCBmb3JtYXQgPSAiYmlidGV4Iiwgc3R5bGUgPSAiYXBhIiwgbG9jYWxlID0gImVuLVVTIiwgcmF3ID0gRkFMU0UsIHByb2dyZXNzID0gIm5vbmUiKQoKIy0tLS0tLSBjb24gY3JfY2l0YXRpb25fY291bnQoKSBwdWVkZXMgdmVyIGVsIG51bWVybyBkZSBjaXRhcyBkZSB1biBhcnTDrWN1bG8vRE9JCmFhIDwtIGNyX2NpdGF0aW9uX2NvdW50KGRvaSA9IG15X2RvaSkKCiMtLS0tLS0gY29uIGNyX2Fic3RyYWN0KCkKYWEgPC0gY3JfYWJzdHJhY3QoZG9pID0gIjEwLjExMDkvVEFTQy4yMDEwLjIwODgwOTEiKQoKIy0tLS0tLSBjb24gY3Jfam91cm5hbHMoKSB2ZW1vcyBqb3VybmFscwphYSA8LSBjcl9qb3VybmFscyhxdWVyeSA9ICJlY29ub21pY3MiLCBsaW1pdCA9IDEwMCkgJT4lIC4kZGF0YSAlPiUgYXMudGliYmxlKCkKCiMtLS0tLS0gbXVjaGEgaW5mb3JtYWNpb24gZGVsIGFydGljdWxvCmFhIDwtIGNyX3dvcmtzKGRvaXMgPSBteV9kb2kpICU+JSAuJGRhdGEgJT4lIGFzLnRpYmJsZSgpCmBgYAoKCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCjxicj4KCgoKCiMjIE90cm9zIHBrZyBmb3IgQVBJcwoKPGJyPgoKSGF5IG11Y2hvcyBvdHJvcyBwYXF1ZXRlcyBkZSBSIGhlY2hvcyBwYXJhIGFjY2VkZXIgYSBBUElzICh0d2l0dGVyLCBFQ0IsIHNwb3RpZnksIHBkZmV0Y2gsIG5hdHVyYWxlYXJ0aCwgLi4uLikuIFB1ZWRlcyB2ZXIgYWxndW5vcyBbYXF1w61dKGh0dHBzOi8vcnZpZXdzLnJzdHVkaW8uY29tLzIwMTcvMTEvMDEvci1kYXRhLXBhY2thZ2VzLyksIFthcXXDrV0oaHR0cHM6Ly93d3cuci1ibG9nZ2Vycy5jb20vci1wYWNrYWdlcy1mb3ItZGF0YS1hY2Nlc3MvKSB5IFthcXXDrV0oaHR0cHM6Ly93d3cuci1ibG9nZ2Vycy5jb20vbmV3LWRhdGEtc291cmNlcy1mb3Itci8pLgoKCltBcXXDrV0oaHR0cHM6Ly9naXRodWIuY29tL3JvcGVuc2NpL3dlYnNlcnZpY2VzKSBwdWVkZXMgdmVyIHVuIGxpc3RhZG8gZW5vcnJycnJtZWByIGVtbzo6amkoIm11bmNoIilgZWVlZTogUGludGVyZXN0LCBJbnN0YWdyYW0sIEdvb2dsZVRyZW5kcywgR29vZ2xlIEFuYWx5dGljcywgRmxpY2tyLCAuLi4uLCAuLi4uLCAuLi4uCgoKVW5hIGRlIGxhcyB1bHRpbWFzIHF1ZSBoZSB2aXN0byBoYSBzaWRvIGVsIHBrZyBbc3Bvb2NdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9zcG9jYy9pbmRleC5odG1sKS4gRW4gc3UgW3ZpZ25ldHRlXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvc3BvY2MvdmlnbmV0dGVzL3Nwb2NjX3ZpZ25ldHRlLmh0bWwpIG5vcyBkaWNlbiBxdWUgc2UgcHVlZGVuIGFjY2VkZXIgYSB1biBjb25qdW50byBkZSBwYXF1ZXRlcyBxdWUgY29udGllbmVuOiAic29tZSBmb3JtIG9mIGJpb2RpdmVyc2l0eSBvciB0YXhvbm9taWMgZGF0YS4gU2luY2Ugc2V2ZXJhbCBvZiB0aGVzZSBkYXRhc2V0cyBoYXZlIGJlZW4gZ2VvcmVmZXJlbmNlZCwgaXQgcHJvdmlkZXMgbnVtZXJvdXMgb3Bwb3J0dW5pdGllcyBmb3IgdmlzdWFsaXppbmcgc3BlY2llcyBkaXN0cmlidXRpb25zIiAKCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0KCjxicj4KCiMjIFNjcmFwcGluZyB0YWJsZXMKCkFkZW3DoXMgZGUgdXRpbGl6YXIgcGFxdWV0ZXMgcGFyYSBhY2NlZGVyIGEgc2VydmljaW9zIHdlYiBhIHRyYXbDqXMgZGUgc3VzIEFQSXMsIHBvZGVtb3MgdXNhciBvdHJvcyBwYXF1ZXRlcyAocHJpbmNpcGFsbWVudGUgYHJ2ZXN0YCkgcGFyYSBoYWNlciB3ZWIgc2NyYXBwaW5nLiBQdWVkZXMgdmVyIGVqZW1wbG9zIFthcXXDrV0oaHR0cDovL3pldnJvc3MuY29tL2Jsb2cvMjAxNS8wNS8xOS9zY3JhcGUtd2Vic2l0ZS1kYXRhLXdpdGgtdGhlLW5ldy1yLXBhY2thZ2UtcnZlc3QvKSwgW2FxdcOtXShodHRwczovL3d3dy5yLWJsb2dnZXJzLmNvbS9mYW50YXN5LWhvY2tleS13aXRoLXJ2ZXN0LWFuZC1wdXJyci8pLCBbYXF1w61dKGh0dHBzOi8vd3d3LmFuYWx5dGljc3ZpZGh5YS5jb20vYmxvZy8yMDE3LzAzL2JlZ2lubmVycy1ndWlkZS1vbi13ZWItc2NyYXBpbmctaW4tci11c2luZy1ydmVzdC13aXRoLWhhbmRzLW9uLWtub3dsZWRnZS8pIG8KW2FxdcOtXShodHRwczovL3JheW1zLmdpdGh1Yi5pby8yMDE4LTAxLTEzLWNpdmlsLXBvbGl0aWNhbC1yaWdodHMvKS4KCgoKPGJyPgoKW0FxdcOtXShodHRwczovL2FsbWVyaWFydXNlcnMud29yZHByZXNzLmNvbS8yMDE2LzEwLzExL3dlYi1zY3JhcGluZy1wY2EteS1rLW1lYW5zLXBhcmEtc2FjYXItdG9kby1lbC1wb3RlbmNpYWwtYS1sYWxpZ2EvP3V0bV9zb3VyY2U9ZGx2ci5pdCZ1dG1fbWVkaXVtPXR3aXR0ZXIpIHRlbsOpaXMgdW4gZWplbXBsbyBzZW5jaWxsbyBwYXJhIGJhamFyIGRhdG9zIGRlIGp1cmdvbCAuLi4gcGVybyB5YSBubyBmdW5jaW9uYQoKPGJyPgoKYGBge3IsIGVjaG8gPSBUUlVFLCBldmFsID0gRkFMU0V9CmxpYnJhcnkoWE1MKQoKdXJsIDwtICJodHRwOi8vd3d3LmNvbXVuaWF6by5jb20vY29tdW5pby9qdWdhZG9yZXMiCnVybCA8LSAiaHR0cHM6Ly93d3cuY29tdW5pYXpvLmNvbS9jb211bmlvL2p1Z2Fkb3JlcyIKCmp1Z2Fkb3JlcyA8LSAgcmVhZEhUTUxUYWJsZSh1cmwsIHN0cmluZ3NBc0ZhY3RvcnMgPSBULCBjb2xuYW1lcyA9IGMoIlBvc2ljaW9uIiwiRXF1aXBvIiwiSnVnYWRvciIsIlB1bnRvcyIsIk1lZGlhIiwiUHVudG9zX0Nhc2EiLCJNZWRpYV9DYXNhIiwiUHVudG9zX0Z1ZXJhIiwiTWVkaWFfZnVlcmEiLCAiVmFsb3IiKSwgY29sQ2xhc3NlcyA9IGMoImNoYXJhY3RlciIsImNoYXJhY3RlciIsImNoYXJhY3RlciIsIkZvcm1hdHRlZE51bWJlciIsIkZvcm1hdHRlZE51bWJlciIsIkZvcm1hdHRlZE51bWJlciIsIkZvcm1hdHRlZE51bWJlciIsIkZvcm1hdHRlZE51bWJlciIsIkZvcm1hdHRlZE51bWJlciIpKQoKYWEgPC0ganVnYWRvcmVzW1sxXV0gJT4lIGFzLnRpYmJsZSgpCmBgYAoKCgoKUGFyYSBzdXN0aXR1aXIgZWwgZWplbXBsbyBkZWwganVyZ29sIGJhamVtb3MgdW5hIHRhYmxhIGRlIGxhIHdpa2lwZWRpYS4gRXN0ZSBlamVtcGxvIGVzdMOhIHNhY2FkbyBkZSBbZXN0ZSBwb3N0XShodHRwczovL3JmbG93ZXJzNS5uZXRsaWZ5LmNvbS8yMDE4LzAxLzE3L2FsdGl0dWQtZGUtbG9zLW11bmljaXBpb3MtZGUtdGVydWVsLykKCgpgYGB7ciwgZWNobyA9IFRSVUUsIGV2YWwgPSBUUlVFfQpsaWJyYXJ5KCJydmVzdCIpCmxpYnJhcnkoInRpZHl2ZXJzZSIpCmNvbnRlbnQgPC0gcmVhZF9odG1sKCJodHRwczovL2VzLndpa2lwZWRpYS5vcmcvd2lraS9BbmV4bzpNdW5pY2lwaW9zX2RlX2xhX3Byb3ZpbmNpYV9kZV9UZXJ1ZWwiKQoKYm9keV90YWJsZSA8LSBjb250ZW50ICU+JSBodG1sX25vZGVzKCdib2R5JykgICU+JQogICAgICAgICAgICAgICAgICAgIGh0bWxfbm9kZXMoJ3RhYmxlJykgJT4lCiAgICAgICAgICAgICAgICAgICAgaHRtbF90YWJsZShkZWMgPSAiLCIpIApUZXJ1ZWwgPC0gYm9keV90YWJsZVtbMV1dCm5hbWVzKFRlcnVlbCkgPC0gYygiTm9tYnJlIiwgIkV4dGVuc2lvbiIsICJQb2JsYWNpb24iLCAiRGVuc2lkYWQiLCAiQ29tYXJjYSIsICJQYXJ0aWRvX2p1ZGljaWFsIiwgIkFsdGl0dWQiKQpsaWJyYXJ5KHN0cmluZ3IpClRlcnVlbCA8LSBUZXJ1ZWwgJT4lIG1hcChzdHJfdHJpbSkgJT4lIGFzX3RpYmJsZSgpICMtIHF1aXRhIGNhcmFjdGVyZXMgYWwgZmluYWwKVGVydWVsIDwtIFRlcnVlbCAlPiUgbXV0YXRlKEFsdGl0dWQgPSBzdHJfcmVwbGFjZV9hbGwoQWx0aXR1ZCwiW1s6cHVuY3Q6XV0iLCAiIikpIApUZXJ1ZWwgPC0gVGVydWVsICU+JSBtdXRhdGUoQWx0aXR1ZCA9IGFzLmRvdWJsZShBbHRpdHVkKSkgJT4lIGFycmFuZ2UoZGVzYyhBbHRpdHVkKSkKYGBgCgoKCgpgYGB7cn0KbGlicmFyeShrYWJsZUV4dHJhKQphYSA8LSBUZXJ1ZWwgJT4lIHNlbGVjdCgxLDMsNSw3KSAlPiUgIHNsaWNlKDE6NCkgCiNrbml0cjo6a2FibGUoYWEsIGRpZ2l0cyA9IDIsIGFsaWduID0gImMiLCBjYXB0aW9uID0gIkxvcyA0IG11bmljaXBpb3MgZGUgVGVydWVsIGNvbiBtw6FzIGFsdGl0dWQiICkKa25pdHI6OmthYmxlKGFhLCAiaHRtbCIsIGRpZ2l0cyA9IDIsICBjYXB0aW9uID0gIkxvcyA0IG11bmljaXBpb3MgZGUgVGVydWVsIGNvbiBtw6FzIGFsdGl0dWQiKSAlPiUKICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIpKQpgYGAKCgo8YnI+CgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKPGJyPgoKCiMgQmlibGlvZ3JhZsOtYQoKLSBbVGhpcyBSIERhdGEgSW1wb3J0IFR1dG9yaWFsIElzIEV2ZXJ5dGhpbmcgWW91IE5lZWRdKGh0dHBzOi8vd3d3LmRhdGFjYW1wLmNvbS9jb21tdW5pdHkvdHV0b3JpYWxzL3ItZGF0YS1pbXBvcnQtdHV0b3JpYWwjZ3MucHBhNmFGZykuIEFzw60gZXMgY29tbyBzZSBsbGFtYSBlc3RlIHR1dG9yaWFsIGRlIERhdGFjYW1wLiBFcyBzZW5jaWxsbyB5IG5vIG11eSBsYXJnby4gVGllbmUgbXV5IGJ1ZW5hIHBpbnRhLiBFbiBnZW5lcmFsIHV0aWxpemEgZnVuY2lvbmVzIGRlIGJhc2UtUi4gTG8gaGFuIGFjdHVhbGl6YWRvIGhhY2UgNC01IGTDrWFzLiBObyBoZSB0ZW5pZG8gdGllbXBvIGRlIHZlciBxdWUgaGEgY2FtYmlhZG8sIHBlcm8gc2VndXJvIHF1ZSBhIG1lam9yLgoKLSBMYSBbY2hlYXQgU2hlZXQgZGUgZGF0YSBpbXBvcnQgZGUgUlN0dWRpb10oaHR0cHM6Ly93d3cucnN0dWRpby5jb20vcmVzb3VyY2VzL2NoZWF0c2hlZXRzLykuIE11eSBidWVuIHJlc3VtZW4vY2h1bGV0YQoKLSBMYSBbc2VjY2nDs24gZGUgaW1wb3J0YXIgZGF0b3MgZGUgUXVpY2stUl0oaHR0cDovL3d3dy5zdGF0bWV0aG9kcy5uZXQvaW5wdXQvaW1wb3J0aW5nZGF0YS5odG1sKS4gQ29uY2lzbyBwZXJvIG11eSBjbGFyby4gUXVpY2stUiBlcyBtdXkgYnVlbiByZWN1cnNvIHBhcmEgYXByZW5kZXIgUi4KCgotIFtMYSBkb2N1bWVudGFjaW9uIG9maWNpYWxdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL2RvYy9tYW51YWxzL3ItcmVsZWFzZS9SLWRhdGEuaHRtbCkgZGUgUgoKCi0gW1VuIHBvc3Qgc29icmUgQVBJcyB5IFJdKGh0dHBzOi8vd3d3LnItYmxvZ2dlcnMuY29tL2FjY2Vzc2luZy1hcGlzLWZyb20tci1hbmQtYS1saXR0bGUtci1wcm9ncmFtbWluZy8pLiBTdXBvbmdvIHF1ZSBjb21wbGljYWRvLgoKCi0gRG9zIHBvc3QgcXVlIGhhY2VuIHVzbyBkZSB3ZWIgc2NyYXBwaW5nOiBbZXN0ZV0oaHR0cHM6Ly9yYXltcy5naXRodWIuaW8vMjAxOC0wMS0xMy1jaXZpbC1wb2xpdGljYWwtcmlnaHRzLykgIHkgW2VzdGVdKGh0dHBzOi8vZGF0YXNjaWVuY2VwbHVzLmNvbS93ZWItc2NyYXBpbmctYW5kLWFwcGxpZWQtY2x1c3RlcmluZy1nbG9iYWwtaGFwcGluZXNzLWFuZC1zb2NpYWwtcHJvZ3Jlc3MtaW5kZXgvKS4gVGFtYmnDqW4gcHVlZGUgcXVlIGNvbXBsaWNhZG8sIHBlcm8gc29uIGRvcyBlamVtcGxvcyBkZSBhbsOhbGlzaXMgZGUgZGF0b3MgKGNvbXBsZXRvKSBjb24gUi4KCgoKCg==