Introducción
En este documento empezaremos con los aspectos básicos de cómo usar
la base de datos del Barómetro de las Américas de LAPOP para fines
estadísticos. En primer lugar, veremos aspectos básicos de cómo
describir una variable mediante una tabla de distribución de frecuencias
y cómo graficar esa variable mediante gráficos circulares o de barras.
Para eso, vamos a usar los informes regionales “El pulso de la
democracia”, con los resultados de la ronda 2021, disponible aquí,
y con los resultados de la ronda 2018/19, disponible aquí.
Uno de los resultados más relevantes en la ronda 2021 presenta los
resultados sobre apoyo a la democracia en la región. Para la ronda
2018/19, una de las secciones de este informe, reporta los datos sobre
redes sociales y actitudes políticas. En esta sección, se presentan
datos sobre el uso de internet y el uso de redes sociales, en general y
por país. Con los datos del Barómetro de las Américas se puede saber el
porcentaje de hogares con acceso a celulares, con acceso a internet, así
como el porcentaje de personas que usa Whatsapp, Facebook o Twitter. En
este documento vamos a repasar estos resultados.
Sobre la base de datos
Los datos que vamos a usar deben citarse de la siguiente manera:
Fuente: Barómetro de las Américas por el Proyecto de Opinión Pública de
América Latina (LAPOP), wwww.LapopSurveys.org. En este documento se
carga nuevamente desde cero una base de datos recortada. Se recomienda
nuevamente limpiar el Environment de los objetos usados en módulos
anteriores.
Esta base de datos se encuentra alojada en el repositorio
“materials_edu” de la cuenta de LAPOP en GitHub. Mediante la librería
rio
y el comando import
se puede importar esta
base de datos desde este repositorio. Además, se seleccionan los datos
de países con códigos menores o iguales a 35, es decir, se eliminan las
observaciones de Estados Unidos y Canadá.
library(rio)
lapop18 = import("https://raw.github.com/lapop-central/materials_edu/main/LAPOP_AB_Merge_2018_v1.0.sav")
lapop18 = subset(lapop18, pais<=35)
También cargamos la base de datos de la ronda 2021.
lapop21 = import("https://raw.github.com/lapop-central/materials_edu/main/lapop21.RData")
lapop21 = subset(lapop21, pais<=35)
Apoyo a la democracia
En el reporte de El Pulso de la Democracia 2021 se presenta los
resultados del apoyo a la democracia por país. El gráfico 1.1 muestra el
porcentaje de personas en cada país que apoya a la democracia en
abstracto.
En los módulos anteriores se vio cómo recodificar la variable ING4,
medida originalmente en una escala del 1 al 7, donde 1 significa “muy en
desacuerdo” y 7 significa “muy de acuerdo”. Los valores entre 5 a 7 son
recodificados como “1”, que identifica a aquellos que apoyan a la
democracia. El resto se recodifica como “0”, aquellos que no apoyan a la
democracia. Esta recodificación se guarda en una nueva variable
“ing4rec”.
library(car)
lapop21$ing4rec = car::recode(lapop21$ing4, "1:4=0; 5:7=1")
En sentido estricto, esta variable no es numérica, a pesar que está
definida en la base de datos como “dbl”, que es un tipo de variable
numérica. Esta variable es de tipo cualitativo, nominal, que en R se
define como factor. Para tener correctamente definida y etiquetada esta
variable, se tiene que transformar. En primer lugar se define como
factor con el comando as.factor
.
lapop21$ing4rec = as.factor(lapop21$ing4rec)
Una variable de tipo factor puede tener etiquetas por cada código
numérico. La definición de etiquetas tiene el objetivo que en cualquier
tabla o gráfico no aparezca el código numérico, sino la etiqueta
correspondiente. Esto se hace usando el comando levels
.
Luego, esta variable se puede describir con el comando
table
, que nos brinda las frecuencias absolutas por cada
categoría de la variable.
levels(lapop21$ing4rec) = c("No", "Sí")
table(lapop21$ing4rec)
##
## No Sí
## 20523 36240
Describir la variable
Como vimos en el módulo sobre Manejo de Datos, se puede usar el
comando prop.table
para tener las frecuencias relativas y
el comando round
para mostrar solo un decimal.
round(prop.table(table(lapop21$ing4rec))*100, 1)
##
## No Sí
## 36.2 63.8
Este gráfico muestra los resultados de las 2 categorías definidas de
la variable de apoyo a la democracia. Sin embargo, esta variable tiene
valores perdidos. Para poder tener una tabla con los valores perdidos,
se puede usar el comando table
con la especificación
useNA = "always"
.
round(prop.table(table(lapop21$ing4rec, useNA = "always"))*100, 1)
##
## No Sí <NA>
## 33.8 59.7 6.4
En esta tabla se ve que existe un 6.4% de casos perdidos del total de
observaciones. La presentación de valores perdidos en tablas o gráficos
depende del investigador. Aquí vamos a presentar los gráficos sobre los
datos válidos.
Graficar la variable
Una variable de tipo “factor” se puede graficar de varias maneras.
Una posibilidad es tener un gráfico circular. Se puede usar el comando
pie
que es parte de la sintaxis básica de R. Dentro de este
comando se puede anidar el comando table
para graficar
estos valores.
pie(table(lapop21$ing4rec))
Este gráfico tiene algunas opciones de personalización. Por ejemplo,
la especificación labels=…
sirve para incluir el número de
casos de cada sector y la especificación col=…
sirve para
definir los colores de los sectores.
pie(table(lapop21$ing4rec), labels=table(lapop21$ing4rec), col=1:2)
Otra opción es usar un gráfico de barras. Usando los comandos básicos
de R, se puede usar el comando barplot
.
barplot(prop.table(table(lapop21$ing4rec))*100, col=1:2)
Los comando de base de R tienen un nivel de personalización, pero
existe una librería especializada para hacer gráficos llamada
ggplot
con más opciones gráficas. En esta serie de módulos
vamos a usar esta librería.
Por ejemplo, para reproducir un gráfico de barras de la variable de
apoyo a la democracia se llama a la librería ggplot2
. Para
graficar los datos de una variable de tipo factor, tenemos dos opciones:
hacerlo desde la misma base de datos original o crear un nuevo objeto
que guarde los resultados en una tabla.
En este ejemplo partiremos de la segunda opción por ser más simple.
Lo primero que tenemos que hacer es guardar la tabla en un objeto, como
un dataframe. Para esto usamos el comando as.data.frame
donde anidamos el código para producir la tabla. Este comando crea dos
columna, “Var1” que guarda las etiquetas y “Freq” que guarda el valor de
los porcentajes.
tabla1 = as.data.frame(round(prop.table(table(lapop21$ing4rec))*100, 1))
tabla1
## Var1 Freq
## 1 No 36.2
## 2 Sí 63.8
Luego, tenemos que definir son los datos que se van a usar con la
especificación data=tabla1
. Ojo, no usaremos la base de
datos original “lapop21”, sino la “tabla1” donde hemos guardado los
resultados.
El comando ggplot
trabaja sumando capas. La
especificación aes
sirve para definir la “estética” del
gráfico. Generalmente se usa para indicar qué variable se va a graficar
en qué eje (x o y). También se puede usar la especificación
fill=
para definir los grupos que se van a generar. En este
caso, en el eje X queremos las categorías “Sí” y “No” y en el eje Y, los
datos de “Freq”.
Luego de especificar los datos y los ejes, se tiene que especificar
el tipo de gráfico que se quiere realizar. Esto se hace con las
geometrías (“geom_xxx”). Se define un gráfico de barras simple, usando
el comando geom_bar(...)
, donde internamente especificamos
stat="identity"
para que este comando trabaje con los datos
tal cual están en la tabla. También se define el ancho de la barra con
la especificación width
. Con la especificación
labs
se define las etiquetas de ejes y el “caption”.
Finalmente, con la especificación coord_cartesian
se define
los límites del eje Y de 0 a 80. Se debe notar que el gráfico no
presenta una columna para el porcentaje de valores perdidos. Presentar
esta columna es opción del investigador.
library(ggplot2)
ggplot(data=tabla1, aes(x=Var1, y=Freq))+
geom_bar(stat = "identity", width=0.5)+
labs(x="Apoyo a la democracia", y="Porcentaje",
caption="Barómetro de las Américas por LAPOP, 2021")+
coord_cartesian(ylim=c(0, 80))
Hasta aquí se ha presentado un gráfico de barras de la variable apoyo
a la democracia para toda la muestra, que incluye a todos los países. El
gráfico 1.1 muestra el porcentaje que apoya a la democracia por país.
Este tipo de gráfico se verá más adelante.
Usuarios de redes sociales
Ahora se usará un ejemplo del reporte El Pulso de la Democracia de la
ronda 2018/19. Se seguirá procedimientos similares a la sección anterior
y se replicarán algunos gráficos del mismo reporte de esa ronda. Las
variables con las que se trabajará son: SMEDIA1. ¿Tiene usted cuenta de
Facebook?; SMEDIA4. ¿Tiene usted cuenta de Twitter?; SMEDIA7. ¿Tiene
usted cuenta de Whatsapp?. Estas preguntas tienen como opciones de
respuesta:
Sí
No
Al momento de leer la base de datos en R, este programa importa las
variables como “num”, que la mayoría de funciones en R trata como
numéricas. Estas variables se tienen que convertir a variables de tipo
“factor” con el comando as.factor
, pues son variables
categóricas. Esta nuevas variables las guardamos en el dataframe.
lapop18$smedia1r = as.factor(lapop18$smedia1)
lapop18$smedia4r = as.factor(lapop18$smedia4)
lapop18$smedia7r = as.factor(lapop18$smedia7)
Estas nuevas variables de tipo factor se tienen que etiquetar con el
comando levels
. Se usa un vector con las etiquetas
concatenadas, usando el comando c()
.
levels(lapop18$smedia1r) = c("Sí", "No")
levels(lapop18$smedia4r) = c("Sí", "No")
levels(lapop18$smedia7r) = c("Sí", "No")
Calcular las variables de usuarios de redes sociales
Como vimos en un módulo anterior, se puede calcular nuevas variables
con valores condicionales de otras variables usando el comando
ifelse
. De esta manera, se crea las variables de usuarios
de redes sociales.
lapop18$fb_user = ifelse(lapop18$smedia1==1 & lapop18$smedia2<=4, 1, 0)
lapop18$tw_user = ifelse(lapop18$smedia4==1 & lapop18$smedia5<=4, 1, 0)
lapop18$wa_user = ifelse(lapop18$smedia7==1 & lapop18$smedia8<=4, 1, 0)
Describir las variables
Con las variables listas, ahora procedemos a hacer las tablas
generales con el comando table
. Se puede notar el uso de
#
como forma de hacer anotaciones, que no son código en
R.
table(lapop18$smedia1r) #Facebook
##
## Sí No
## 15389 11573
table(lapop18$smedia4r) #Twitter
##
## Sí No
## 2363 24558
table(lapop18$smedia7r) #Whatsapp
##
## Sí No
## 17446 9569
Este comando table
nos brinda las frecuencias absolutas
(número de observaciones) por cada categoría de las variables (en este
caso Sí y No). Para obtener las frecuencias relativas, usaremos el
comando prop.table
, donde se anida el comando anterior
table
.
prop.table(table(lapop18$smedia1r))
##
## Sí No
## 0.5707663 0.4292337
prop.table(table(lapop18$smedia4r))
##
## Sí No
## 0.08777534 0.91222466
prop.table(table(lapop18$smedia7r))
##
## Sí No
## 0.6457894 0.3542106
Sin embargo, el comando prop.table
nos devuelve
demasiados decimales y las frecuencias relativas en una escala de 0 a 1.
Para redondear esta cifra usamos el comando round
, que nos
permite especificar el número de decimales que se quiere mostrar. Tanto
el comando table
, como prop.table
se anidan
dentro de este nuevo comando. En este caso se ha usado 3 decimales, para
cuando se multiplique por 100, quede en forma de porcentaje con 1
decimal.
round(prop.table(table(lapop18$smedia1r)), 3)*100
##
## Sí No
## 57.1 42.9
round(prop.table(table(lapop18$smedia4r)), 3)*100
##
## Sí No
## 8.8 91.2
round(prop.table(table(lapop18$smedia7r)), 3)*100
##
## Sí No
## 64.6 35.4
No es práctico presentar 3 tablas cuando las variables tienen las
mismas categorías de respuesta. Para fines de presentación podría ser
mejor construir una sola tabla. Se puede guardar las tablas parciales en
nuevos objetos con el operador <-
y luego unirlas como
filas con el comando rbind
en un nuevo dataframe “tabla”,
de tal manera que las respuestas a cada red social aparezcan en
filas.
Facebook = round(prop.table(table(lapop18$smedia1r)), 3)*100
Twitter = round(prop.table(table(lapop18$smedia4r)), 3)*100
Whatsapp = round(prop.table(table(lapop18$smedia7r)), 3)*100
tabla2 = as.data.frame(rbind(Facebook, Twitter, Whatsapp))
tabla2
## Sí No
## Facebook 57.1 42.9
## Twitter 8.8 91.2
## Whatsapp 64.6 35.4
Para tener una mejor presentación de la tabla, se puede usar el
comando kable
del paquete knitr
, usando la
tabla construida anteriormente.
library(knitr)
knitr::kable(tabla2, format="markdown")
Facebook |
57.1 |
42.9 |
Twitter |
8.8 |
91.2 |
Whatsapp |
64.6 |
35.4 |
Graficar las variables
En el Gráfico 3.1 del reporte se observa que se reportan estos datos
mediante un gráfico de sectores circulares.
Se puede reproducir ese gráfico usando el comando pie
que es parte de la sintaxis básica de R. Dentro de este comando se puede
anidar el comando table
para graficar estos valores.
pie(table(lapop18$smedia1r))
También se podría pensar en un gráfico de barras. Usando los comandos
básicos de R, se puede usar el comando barplot
.
barplot(prop.table(table(lapop18$smedia1r)))
Estos comandos gráficos tienen opciones para adecuar el gráfico, por
ejemplo, para incluir los porcentajes y adecuar las escalas.
Pero, como mencionamos más arriba, para tener más opciones gráficas,
usaremos el paquete ggplot
para reproducir el gráfico
circular y a lo largo de estos módulos.
Lo primero que tenemos que hacer es crear una tabla que guarde los
datos que queremos graficar sobre los usuarios de Facebook. Recordemos
que tenemos una tabla con estos resultados donde la primera fila muestra
las etiquetas “Sí” y “No” y la segunda fila los porcentajes. Requerimos
que las etiquetas estén como columnas y los porcentajes también. Para
esto tenemos que transponer la tabla, que significa “darle la vuelta”.
Luego, esta nueva tabla la guardamos como un dataframe con el comando
as.data.frame
que crea tres columnas “Var1” (sin interés),
“Var2” con las etiquetas y Freq con los porcentajes.
Face = as.data.frame(t(Facebook))
Face
## Var1 Var2 Freq
## 1 A Sí 57.1
## 2 A No 42.9
Con esta tabla podemos crear un gráfico de barras como el visto más
arriba. Otro tipo de barras que se usa frecuentemente es del tipo de
barras apiladas al 100%.
Vamos a crear una barra apilada con estos resultados. A diferencia
del gráfico de barras convencional, las barras apiladas requieren que en
la aes(x="", y=Freq, fill=Var2)
. Es decir, que no haya
etiquetas en el eje X, que el eje Y muestre los porcentajes (guardados
en “Freq”) y que la barra esté separada por las categorías (guardadas en
“Var2”).
Se agrega una capa de texto, con la especificación
geom_text
. Dentro de esta especificación se determina una
“estética” con la etiqueta del dato aes(label=...)
, donde
se junta con el comando paste
el dato del porcentaje “per”
y el símbolo “%”, con un espacio (sep=...
) entre ellos. Se
establece el color de la fuente con color="..."
. Se ajusta
a blanco para que contraste con los colores del gráfico circular. Con el
comando hjust=...
se ajusta la posición horizontal de este
texto. El comando ggplot
puede incluir varios “temas” para
el gráfico. En este caso se ha usado theme_void()
que
indica un fondo vacío. Finalmente, con la especificación
scale_fill_discrete(name=...)
se puede cambiar el título de
la leyenda para que no aparezca el nombre de la variable, sino una
etiqueta más adecuada.
ggplot(data=Face, aes(x="", y=Freq, fill=Var2))+
geom_bar(width=1, stat="identity")+
geom_text(aes(label=paste(Freq, "%", sep="")), color="white",
position=position_stack(vjust=0.5), size=3)+
theme_void()+
scale_fill_discrete(name="¿Usa Facebook?")
Para crear un gráfico circular, no existe una “geometría” en
ggplot
. Sin embargo, para genera el gráfico circular, se
tiene que agregar otro capa coord_polar
, que transforma la
barra a coordenadas polares, creando un gráfico circular. Todo el resto
del código es similar al anterior.
ggplot(data=Face, aes(x="", y=Freq, fill=Var2))+
geom_bar(width=1, stat="identity")+
geom_text(aes(label=paste(Freq, "%", sep="")), color="white",
position=position_stack(vjust=0.5), size=3)+
coord_polar("y")+
theme_void()+
scale_fill_discrete(name="¿Usa Facebook?")
Si quisiéramos regresar a hacer un gráfico de barras, podemos incluir
el texto de los valores con geom_text
, el título general
del gráfico con title
dentro de labs
, los
nombres de los ejes y el “caption”.
ggplot(Face, aes(x=Var2, y=Freq))+
geom_bar(stat="identity", width=0.5)+
geom_text(aes(label=paste(Freq, "%", sep="")), color="black", vjust=-0.5)+
labs(title="¿Qué tan frecuente se usan las redes sociales?",
x="Usuario de Facebook", y="Porcentaje",
caption="Barómetro de las Américas por LAPOP, 2018/19")+
coord_cartesian(ylim=c(0, 60))
Resumen
En este documento se ha trabajado con variables categóricas
nominales, como apoyo a la democracia o si usa o no usa redes sociales.
Se ha presentando las formas de cómo describir en tablas de frecuencia y
cómo graficar estas variables, mediante gráficos circulares o de
barras.
Cálculos incluyendo el efecto de diseño
Estos últimos resultados de la ronda 2018/19 no son exactamente
iguales a los del reporte pues LAPOP incluye el efecto del diseño
muestral en sus cálculos. Según esta sintaxis, se encuentra que el 57.1%
de entrevistados reporta ser usuario de Facebook, cuando en el reporte
aparece 56.2%. Lo mismo con Twitter, que aquí se calcula en 8.8% y en el
reporte 7.9%; y con Whatsapp que aquí aparece con 64.6% y en el reporte
con 64.4%. Como se indicó en el documento sobre el uso de los factores
de expansión usando los datos del Barómetro de las Américas (disponible
aquí),
hay varias maneras de reproducir los resultados incorporando el efecto
de diseño. Una primera opción es usar el comando freq
que
permite la inclusión de una variable de factor de expansión, como
“weight1500”. Se incluye la especificación plot=F
para no
producir los gráficos de barras.
library(descr)
descr::freq(lapop18$fb_user, lapop18$weight1500, plot = F)
## lapop18$fb_user
## Frequency Percent Valid Percent
## 0 11337 41.988 43.77
## 1 14564 53.939 56.23
## NA's 1100 4.073
## Total 27000 100.000 100.00
descr::freq(lapop18$tw_user, lapop18$weight1500, plot = F)
## lapop18$tw_user
## Frequency Percent Valid Percent
## 0 23819 88.220 92.023
## 1 2065 7.647 7.977
## NA's 1116 4.133
## Total 27000 100.000 100.000
descr::freq(lapop18$wa_user, lapop18$weight1500, plot = F)
## lapop18$wa_user
## Frequency Percent Valid Percent
## 0 9252 34.266 35.63
## 1 16714 61.903 64.37
## NA's 1035 3.832
## Total 27000 100.000 100.00
Sin considerar el efecto de diseño, se tiene que 57.1% de
entrevistados cuenta con una cuenta de Facebook. Este porcentaje varía a
55.2% si se incluye la variable de expansión, que es el valor que se
muestra en el reporte. Estos resultados ponderados también se pueden
guardar en objetos y luego graficar de la misma manera que se ha hecho
con los resultados sin ponderar.
Para el caso de Facebook, la tabla se puede guardar como un
dataframe, usando el comando as.data.frame
. Esta tabla
incluye datos que no requerimos, como la fila de NA´s y del total y como
la columna de Percent. Estas filas y esta columna se borran usando la
especificación [-c(3,4), -2]
.
Luego, se le cambia el nombre a las columnas para evitar el nombre
“Valid Percent”. Se las nombre simplemente como “freq” y “per”. Esta
columna “per” es la que tiene los datos que graficaremos. Finalmente, se
añade una columna “lab” con las etiquetas de cada fila de
resultados.
fb = as.data.frame(descr::freq(lapop18$fb_user, lapop18$weight1500,
plot = F))
fb = fb[-c(3,4), -2]
colnames(fb) = c("freq", "per")
fb$lab = c("No", "Sí")
fb
## freq per lab
## 0 11336.69 43.77052 No
## 1 14563.60 56.22948 Sí
Con este nuevo dataframe podemos replicar los mismo códigos usados
más arriba para hacer un gráfico de barras o un gráfico circular. El
siguiente código muestra el gráfico de barras. Nótese que ahora se usa
el dataframe “fb” y que en aes se especifica que en el eje X deben estar
los datos de la columna “lab” y en el eje Y los datos de la columna
“per”.
ggplot(data=fb, aes(x=lab, y=per))+
geom_bar(stat="identity", width=0.5)+
geom_text(aes(label=paste(round(per, 1), "%", sep="")),
color="black", vjust=-0.5)+
labs(title="¿Qué tan frecuente se usan las redes sociales?",
x="Usuario de Facebook", y="Porcentaje",
caption="Barómetro de las Américas por LAPOP, 2018/19")+
coord_cartesian(ylim=c(0, 60))
Esto mismo se puede hacer para crear un gráfico circular. Este
gráfico reproduce los resultados hallados en el Gráfico 3.1 del
reporte.
ggplot(data=fb, aes(x=2, y=per, fill=lab))+
geom_bar(stat="identity")+
geom_text(aes(label=paste(round(per, 1), "%", sep="")), color="white",
position=position_stack(vjust=0.5), size=3)+
coord_polar("y")+
theme_void()+
labs(title="¿Qué tan frecuente se usan las redes sociales?",
caption="Barómetro de las Américas por LAPOP, 2018/19")+
scale_fill_discrete(name="¿Usa Facebook?")+
xlim(0.5, 2.5)
La segunda opción para reproducir los datos del reporte exactamente
es mediante el paquete survey
. Como se indicó en esta sección,
primero se tiene que definir el diseño muestral con el comando
svydesign
.
library(survey)
diseno18 = svydesign(ids = ~upm, strata = ~estratopri,
weights = ~weight1500, nest=TRUE, data=lapop18)
Una vez creado los datos con el factor de expansión en el objeto
“diseno18”, se puede usar los comandos nativos del paquete
survey
para realizar cálculos. Por ejemplo, para calcular
la tabla de distribución de frecuencias se puede usar el comando
svytable
.
svytable(~fb_user, design=diseno18)
## fb_user
## 0 1
## 11336.69 14563.60
Estas frecuencias se pueden anidar en el comando
prop.table
para calcular los porcentajes de usuarios de
redes sociales. Estos resultados son iguales a los mostrados en los
gráficos anteriores y a los que aparecen en el reporte.
Estos datos también se pueden guardar en un dataframe que se adapta
para graficar, siguiendo el mismo procedimiento que en los gráficos
anteriores.
prop.table(svytable(~fb_user, design=diseno18))
## fb_user
## 0 1
## 0.4377052 0.5622948
prop.table(svytable(~tw_user, design=diseno18))
## tw_user
## 0 1
## 0.92023002 0.07976998
prop.table(svytable(~wa_user, design=diseno18))
## wa_user
## 0 1
## 0.3563091 0.6436909
LS0tCnRpdGxlOiAiRXN0YWTDrXN0aWNhIGRlc2NyaXB0aXZhIHVzYW5kbyBlbCBCYXLDs21ldHJvIGRlIGxhcyBBbcOpcmljYXMgKDEpIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICBjb2xsYXBzZWQ6IGZhbHNlCiAgICBudW1iZXJfc2VjdGlvbnM6IGZhbHNlCiAgICB0b2NfZGVwdGg6IDEKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgIHRoZW1lOiBmbGF0bHkKICAgICNjb2RlX2ZvbGRpbmc6IGhpZGUKZWRpdG9yX29wdGlvbnM6IAogIG1hcmtkb3duOiAKICAgIHdyYXA6IHNlbnRlbmNlCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0UsIGNhY2hlPVRSVUUpCmBgYAoKYGBge2NzcyBjb2xvciwgZWNobz1GQUxTRX0KLmNvbHVtbnMge2Rpc3BsYXk6IGZsZXg7fQpoMSB7Y29sb3I6ICMzMzY2Q0M7fQpgYGAKCiMgSW50cm9kdWNjacOzbgoKRW4gZXN0ZSBkb2N1bWVudG8gZW1wZXphcmVtb3MgY29uIGxvcyBhc3BlY3RvcyBiw6FzaWNvcyBkZSBjw7NtbyB1c2FyIGxhIGJhc2UgZGUgZGF0b3MgZGVsIEJhcsOzbWV0cm8gZGUgbGFzIEFtw6lyaWNhcyBkZSBMQVBPUCBwYXJhIGZpbmVzIGVzdGFkw61zdGljb3MuCkVuIHByaW1lciBsdWdhciwgdmVyZW1vcyBhc3BlY3RvcyBiw6FzaWNvcyBkZSBjw7NtbyBkZXNjcmliaXIgdW5hIHZhcmlhYmxlIG1lZGlhbnRlIHVuYSB0YWJsYSBkZSBkaXN0cmlidWNpw7NuIGRlIGZyZWN1ZW5jaWFzIHkgY8OzbW8gZ3JhZmljYXIgZXNhIHZhcmlhYmxlIG1lZGlhbnRlIGdyw6FmaWNvcyBjaXJjdWxhcmVzIG8gZGUgYmFycmFzLgpQYXJhIGVzbywgdmFtb3MgYSB1c2FyIGxvcyBpbmZvcm1lcyByZWdpb25hbGVzICJFbCBwdWxzbyBkZSBsYSBkZW1vY3JhY2lhIiwgY29uIGxvcyByZXN1bHRhZG9zIGRlIGxhIHJvbmRhIDIwMjEsIGRpc3BvbmlibGUgW2FxdcOtXShodHRwczovL3d3dy52YW5kZXJiaWx0LmVkdS9sYXBvcC9hYjIwMjEvMjAyMV9MQVBPUF9BbWVyaWNhc0Jhcm9tZXRlcl8yMDIxX1B1bHNlX29mX0RlbW9jcmFjeS5wZGYpLCB5IGNvbiBsb3MgcmVzdWx0YWRvcyBkZSBsYSByb25kYSAyMDE4LzE5LCBkaXNwb25pYmxlIFthcXXDrV0oaHR0cHM6Ly93d3cudmFuZGVyYmlsdC5lZHUvbGFwb3AvYWIyMDE4LzIwMTgtMTlfQW1lcmljYXNCYXJvbWV0ZXJfUmVnaW9uYWxfUmVwb3J0X1NwYW5pc2hfV18wMy4yNy4yMC5wZGZ4KS4KClVubyBkZSBsb3MgcmVzdWx0YWRvcyBtw6FzIHJlbGV2YW50ZXMgZW4gbGEgcm9uZGEgMjAyMSBwcmVzZW50YSBsb3MgcmVzdWx0YWRvcyBzb2JyZSBhcG95byBhIGxhIGRlbW9jcmFjaWEgZW4gbGEgcmVnacOzbi4KUGFyYSBsYSByb25kYSAyMDE4LzE5LCB1bmEgZGUgbGFzIHNlY2Npb25lcyBkZSBlc3RlIGluZm9ybWUsIHJlcG9ydGEgbG9zIGRhdG9zIHNvYnJlIHJlZGVzIHNvY2lhbGVzIHkgYWN0aXR1ZGVzIHBvbMOtdGljYXMuCkVuIGVzdGEgc2VjY2nDs24sIHNlIHByZXNlbnRhbiBkYXRvcyBzb2JyZSBlbCB1c28gZGUgaW50ZXJuZXQgeSBlbCB1c28gZGUgcmVkZXMgc29jaWFsZXMsIGVuIGdlbmVyYWwgeSBwb3IgcGHDrXMuCkNvbiBsb3MgZGF0b3MgZGVsIEJhcsOzbWV0cm8gZGUgbGFzIEFtw6lyaWNhcyBzZSBwdWVkZSBzYWJlciBlbCBwb3JjZW50YWplIGRlIGhvZ2FyZXMgY29uIGFjY2VzbyBhIGNlbHVsYXJlcywgY29uIGFjY2VzbyBhIGludGVybmV0LCBhc8OtIGNvbW8gZWwgcG9yY2VudGFqZSBkZSBwZXJzb25hcyBxdWUgdXNhIFdoYXRzYXBwLCBGYWNlYm9vayBvIFR3aXR0ZXIuCkVuIGVzdGUgZG9jdW1lbnRvIHZhbW9zIGEgcmVwYXNhciBlc3RvcyByZXN1bHRhZG9zLgoKIyBTb2JyZSBsYSBiYXNlIGRlIGRhdG9zCgpMb3MgZGF0b3MgcXVlIHZhbW9zIGEgdXNhciBkZWJlbiBjaXRhcnNlIGRlIGxhIHNpZ3VpZW50ZSBtYW5lcmE6IEZ1ZW50ZTogQmFyw7NtZXRybyBkZSBsYXMgQW3DqXJpY2FzIHBvciBlbCBQcm95ZWN0byBkZSBPcGluacOzbiBQw7pibGljYSBkZSBBbcOpcmljYSBMYXRpbmEgKExBUE9QKSwgd3d3dy5MYXBvcFN1cnZleXMub3JnLgpFbiBlc3RlIGRvY3VtZW50byBzZSBjYXJnYSBudWV2YW1lbnRlIGRlc2RlIGNlcm8gdW5hIGJhc2UgZGUgZGF0b3MgcmVjb3J0YWRhLgpTZSByZWNvbWllbmRhIG51ZXZhbWVudGUgbGltcGlhciBlbCBFbnZpcm9ubWVudCBkZSBsb3Mgb2JqZXRvcyB1c2Fkb3MgZW4gbcOzZHVsb3MgYW50ZXJpb3Jlcy4KCkVzdGEgYmFzZSBkZSBkYXRvcyBzZSBlbmN1ZW50cmEgYWxvamFkYSBlbiBlbCByZXBvc2l0b3JpbyAibWF0ZXJpYWxzX2VkdSIgZGUgbGEgY3VlbnRhIGRlIExBUE9QIGVuIEdpdEh1Yi4KTWVkaWFudGUgbGEgbGlicmVyw61hIGByaW9gIHkgZWwgY29tYW5kbyBgaW1wb3J0YCBzZSBwdWVkZSBpbXBvcnRhciBlc3RhIGJhc2UgZGUgZGF0b3MgZGVzZGUgZXN0ZSByZXBvc2l0b3Jpby4KQWRlbcOhcywgc2Ugc2VsZWNjaW9uYW4gbG9zIGRhdG9zIGRlIHBhw61zZXMgY29uIGPDs2RpZ29zIG1lbm9yZXMgbyBpZ3VhbGVzIGEgMzUsIGVzIGRlY2lyLCBzZSBlbGltaW5hbiBsYXMgb2JzZXJ2YWNpb25lcyBkZSBFc3RhZG9zIFVuaWRvcyB5IENhbmFkw6EuCgpgYGB7ciBiYXNlLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KHJpbykKbGFwb3AxOCA9IGltcG9ydCgiaHR0cHM6Ly9yYXcuZ2l0aHViLmNvbS9sYXBvcC1jZW50cmFsL21hdGVyaWFsc19lZHUvbWFpbi9MQVBPUF9BQl9NZXJnZV8yMDE4X3YxLjAuc2F2IikKbGFwb3AxOCA9IHN1YnNldChsYXBvcDE4LCBwYWlzPD0zNSkKYGBgCgpUYW1iacOpbiBjYXJnYW1vcyBsYSBiYXNlIGRlIGRhdG9zIGRlIGxhIHJvbmRhIDIwMjEuCgpgYGB7ciBiYXNlMjF9CmxhcG9wMjEgPSBpbXBvcnQoImh0dHBzOi8vcmF3LmdpdGh1Yi5jb20vbGFwb3AtY2VudHJhbC9tYXRlcmlhbHNfZWR1L21haW4vbGFwb3AyMS5SRGF0YSIpCmxhcG9wMjEgPSBzdWJzZXQobGFwb3AyMSwgcGFpczw9MzUpCmBgYAoKIyBBcG95byBhIGxhIGRlbW9jcmFjaWEKCkVuIGVsIHJlcG9ydGUgZGUgRWwgUHVsc28gZGUgbGEgRGVtb2NyYWNpYSAyMDIxIHNlIHByZXNlbnRhIGxvcyByZXN1bHRhZG9zIGRlbCBhcG95byBhIGxhIGRlbW9jcmFjaWEgcG9yIHBhw61zLgpFbCBncsOhZmljbyAxLjEgbXVlc3RyYSBlbCBwb3JjZW50YWplIGRlIHBlcnNvbmFzIGVuIGNhZGEgcGHDrXMgcXVlIGFwb3lhIGEgbGEgZGVtb2NyYWNpYSBlbiBhYnN0cmFjdG8uCgohW10oR3JhZmljbzEuMS5wbmcpCgpFbiBsb3MgbcOzZHVsb3MgYW50ZXJpb3JlcyBzZSB2aW8gY8OzbW8gcmVjb2RpZmljYXIgbGEgdmFyaWFibGUgSU5HNCwgbWVkaWRhIG9yaWdpbmFsbWVudGUgZW4gdW5hIGVzY2FsYSBkZWwgMSBhbCA3LCBkb25kZSAxIHNpZ25pZmljYSAibXV5IGVuIGRlc2FjdWVyZG8iIHkgNyBzaWduaWZpY2EgIm11eSBkZSBhY3VlcmRvIi4KTG9zIHZhbG9yZXMgZW50cmUgNSBhIDcgc29uIHJlY29kaWZpY2Fkb3MgY29tbyAiMSIsIHF1ZSBpZGVudGlmaWNhIGEgYXF1ZWxsb3MgcXVlIGFwb3lhbiBhIGxhIGRlbW9jcmFjaWEuCkVsIHJlc3RvIHNlIHJlY29kaWZpY2EgY29tbyAiMCIsIGFxdWVsbG9zIHF1ZSBubyBhcG95YW4gYSBsYSBkZW1vY3JhY2lhLgpFc3RhIHJlY29kaWZpY2FjacOzbiBzZSBndWFyZGEgZW4gdW5hIG51ZXZhIHZhcmlhYmxlICJpbmc0cmVjIi4KCmBgYHtyIGFwb3lvfQpsaWJyYXJ5KGNhcikKbGFwb3AyMSRpbmc0cmVjID0gY2FyOjpyZWNvZGUobGFwb3AyMSRpbmc0LCAiMTo0PTA7IDU6Nz0xIikKYGBgCgpFbiBzZW50aWRvIGVzdHJpY3RvLCBlc3RhIHZhcmlhYmxlIG5vIGVzIG51bcOpcmljYSwgYSBwZXNhciBxdWUgZXN0w6EgZGVmaW5pZGEgZW4gbGEgYmFzZSBkZSBkYXRvcyBjb21vICJkYmwiLCBxdWUgZXMgdW4gdGlwbyBkZSB2YXJpYWJsZSBudW3DqXJpY2EuCkVzdGEgdmFyaWFibGUgZXMgZGUgdGlwbyBjdWFsaXRhdGl2bywgbm9taW5hbCwgcXVlIGVuIFIgc2UgZGVmaW5lIGNvbW8gZmFjdG9yLgpQYXJhIHRlbmVyIGNvcnJlY3RhbWVudGUgZGVmaW5pZGEgeSBldGlxdWV0YWRhIGVzdGEgdmFyaWFibGUsIHNlIHRpZW5lIHF1ZSB0cmFuc2Zvcm1hci4KRW4gcHJpbWVyIGx1Z2FyIHNlIGRlZmluZSBjb21vIGZhY3RvciBjb24gZWwgY29tYW5kbyBgYXMuZmFjdG9yYC4KCmBgYHtyIGFwb3lvIHJlY30KbGFwb3AyMSRpbmc0cmVjID0gYXMuZmFjdG9yKGxhcG9wMjEkaW5nNHJlYykKYGBgCgpVbmEgdmFyaWFibGUgZGUgdGlwbyBmYWN0b3IgcHVlZGUgdGVuZXIgZXRpcXVldGFzIHBvciBjYWRhIGPDs2RpZ28gbnVtw6lyaWNvLgpMYSBkZWZpbmljacOzbiBkZSBldGlxdWV0YXMgdGllbmUgZWwgb2JqZXRpdm8gcXVlIGVuIGN1YWxxdWllciB0YWJsYSBvIGdyw6FmaWNvIG5vIGFwYXJlemNhIGVsIGPDs2RpZ28gbnVtw6lyaWNvLCBzaW5vIGxhIGV0aXF1ZXRhIGNvcnJlc3BvbmRpZW50ZS4KRXN0byBzZSBoYWNlIHVzYW5kbyBlbCBjb21hbmRvIGBsZXZlbHNgLgpMdWVnbywgZXN0YSB2YXJpYWJsZSBzZSBwdWVkZSBkZXNjcmliaXIgY29uIGVsIGNvbWFuZG8gYHRhYmxlYCwgcXVlIG5vcyBicmluZGEgbGFzIGZyZWN1ZW5jaWFzIGFic29sdXRhcyBwb3IgY2FkYSBjYXRlZ29yw61hIGRlIGxhIHZhcmlhYmxlLgoKYGBge3IgYXBveW8gbGFiZWxzfQpsZXZlbHMobGFwb3AyMSRpbmc0cmVjKSA9IGMoIk5vIiwgIlPDrSIpCnRhYmxlKGxhcG9wMjEkaW5nNHJlYykKYGBgCgojIyBEZXNjcmliaXIgbGEgdmFyaWFibGUKCkNvbW8gdmltb3MgZW4gZWwgbcOzZHVsbyBzb2JyZSBNYW5lam8gZGUgRGF0b3MsIHNlIHB1ZWRlIHVzYXIgZWwgY29tYW5kbyBgcHJvcC50YWJsZWAgcGFyYSB0ZW5lciBsYXMgZnJlY3VlbmNpYXMgcmVsYXRpdmFzIHkgZWwgY29tYW5kbyBgcm91bmRgIHBhcmEgbW9zdHJhciBzb2xvIHVuIGRlY2ltYWwuCgpgYGB7ciBhcG95byBkZXNjfQpyb3VuZChwcm9wLnRhYmxlKHRhYmxlKGxhcG9wMjEkaW5nNHJlYykpKjEwMCwgMSkKYGBgCgpFc3RlIGdyw6FmaWNvIG11ZXN0cmEgbG9zIHJlc3VsdGFkb3MgZGUgbGFzIDIgY2F0ZWdvcsOtYXMgZGVmaW5pZGFzIGRlIGxhIHZhcmlhYmxlIGRlIGFwb3lvIGEgbGEgZGVtb2NyYWNpYS4KU2luIGVtYmFyZ28sIGVzdGEgdmFyaWFibGUgdGllbmUgdmFsb3JlcyBwZXJkaWRvcy4KUGFyYSBwb2RlciB0ZW5lciB1bmEgdGFibGEgY29uIGxvcyB2YWxvcmVzIHBlcmRpZG9zLCBzZSBwdWVkZSB1c2FyIGVsIGNvbWFuZG8gYHRhYmxlYCBjb24gbGEgZXNwZWNpZmljYWNpw7NuIGB1c2VOQSA9ICJhbHdheXMiYC4KCmBgYHtyIGFwb3lvIHJvdW5kfQpyb3VuZChwcm9wLnRhYmxlKHRhYmxlKGxhcG9wMjEkaW5nNHJlYywgdXNlTkEgPSAiYWx3YXlzIikpKjEwMCwgMSkKYGBgCgpFbiBlc3RhIHRhYmxhIHNlIHZlIHF1ZSBleGlzdGUgdW4gNi40JSBkZSBjYXNvcyBwZXJkaWRvcyBkZWwgdG90YWwgZGUgb2JzZXJ2YWNpb25lcy4KTGEgcHJlc2VudGFjacOzbiBkZSB2YWxvcmVzIHBlcmRpZG9zIGVuIHRhYmxhcyBvIGdyw6FmaWNvcyBkZXBlbmRlIGRlbCBpbnZlc3RpZ2Fkb3IuCkFxdcOtIHZhbW9zIGEgcHJlc2VudGFyIGxvcyBncsOhZmljb3Mgc29icmUgbG9zIGRhdG9zIHbDoWxpZG9zLgoKIyMgR3JhZmljYXIgbGEgdmFyaWFibGUKClVuYSB2YXJpYWJsZSBkZSB0aXBvICJmYWN0b3IiIHNlIHB1ZWRlIGdyYWZpY2FyIGRlIHZhcmlhcyBtYW5lcmFzLgpVbmEgcG9zaWJpbGlkYWQgZXMgdGVuZXIgdW4gZ3LDoWZpY28gY2lyY3VsYXIuClNlIHB1ZWRlIHVzYXIgZWwgY29tYW5kbyBgcGllYCBxdWUgZXMgcGFydGUgZGUgbGEgc2ludGF4aXMgYsOhc2ljYSBkZSBSLgpEZW50cm8gZGUgZXN0ZSBjb21hbmRvIHNlIHB1ZWRlIGFuaWRhciBlbCBjb21hbmRvIGB0YWJsZWAgcGFyYSBncmFmaWNhciBlc3RvcyB2YWxvcmVzLgoKYGBge3IgcGllfQpwaWUodGFibGUobGFwb3AyMSRpbmc0cmVjKSkKYGBgCgpFc3RlIGdyw6FmaWNvIHRpZW5lIGFsZ3VuYXMgb3BjaW9uZXMgZGUgcGVyc29uYWxpemFjacOzbi4KUG9yIGVqZW1wbG8sIGxhIGVzcGVjaWZpY2FjacOzbiBgbGFiZWxzPeKApmAgc2lydmUgcGFyYSBpbmNsdWlyIGVsIG7Dum1lcm8gZGUgY2Fzb3MgZGUgY2FkYSBzZWN0b3IgeSBsYSBlc3BlY2lmaWNhY2nDs24gYGNvbD3igKZgIHNpcnZlIHBhcmEgZGVmaW5pciBsb3MgY29sb3JlcyBkZSBsb3Mgc2VjdG9yZXMuCgpgYGB7ciBwaWUgY29sb3J9CnBpZSh0YWJsZShsYXBvcDIxJGluZzRyZWMpLCBsYWJlbHM9dGFibGUobGFwb3AyMSRpbmc0cmVjKSwgY29sPTE6MikKYGBgCgpPdHJhIG9wY2nDs24gZXMgdXNhciB1biBncsOhZmljbyBkZSBiYXJyYXMuClVzYW5kbyBsb3MgY29tYW5kb3MgYsOhc2ljb3MgZGUgUiwgc2UgcHVlZGUgdXNhciBlbCBjb21hbmRvIGBiYXJwbG90YC4KCmBgYHtyIGJhcnJhfQpiYXJwbG90KHByb3AudGFibGUodGFibGUobGFwb3AyMSRpbmc0cmVjKSkqMTAwLCBjb2w9MToyKQpgYGAKCkxvcyBjb21hbmRvIGRlIGJhc2UgZGUgUiB0aWVuZW4gdW4gbml2ZWwgZGUgcGVyc29uYWxpemFjacOzbiwgcGVybyBleGlzdGUgdW5hIGxpYnJlcsOtYSBlc3BlY2lhbGl6YWRhIHBhcmEgaGFjZXIgZ3LDoWZpY29zIGxsYW1hZGEgYGdncGxvdGAgY29uIG3DoXMgb3BjaW9uZXMgZ3LDoWZpY2FzLgpFbiBlc3RhIHNlcmllIGRlIG3Ds2R1bG9zIHZhbW9zIGEgdXNhciBlc3RhIGxpYnJlcsOtYS4KClBvciBlamVtcGxvLCBwYXJhIHJlcHJvZHVjaXIgdW4gZ3LDoWZpY28gZGUgYmFycmFzIGRlIGxhIHZhcmlhYmxlIGRlIGFwb3lvIGEgbGEgZGVtb2NyYWNpYSBzZSBsbGFtYSBhIGxhIGxpYnJlcsOtYSBgZ2dwbG90MmAuClBhcmEgZ3JhZmljYXIgbG9zIGRhdG9zIGRlIHVuYSB2YXJpYWJsZSBkZSB0aXBvIGZhY3RvciwgdGVuZW1vcyBkb3Mgb3BjaW9uZXM6IGhhY2VybG8gZGVzZGUgbGEgbWlzbWEgYmFzZSBkZSBkYXRvcyBvcmlnaW5hbCBvIGNyZWFyIHVuIG51ZXZvIG9iamV0byBxdWUgZ3VhcmRlIGxvcyByZXN1bHRhZG9zIGVuIHVuYSB0YWJsYS4KCkVuIGVzdGUgZWplbXBsbyBwYXJ0aXJlbW9zIGRlIGxhIHNlZ3VuZGEgb3BjacOzbiBwb3Igc2VyIG3DoXMgc2ltcGxlLgpMbyBwcmltZXJvIHF1ZSB0ZW5lbW9zIHF1ZSBoYWNlciBlcyBndWFyZGFyIGxhIHRhYmxhIGVuIHVuIG9iamV0bywgY29tbyB1biBkYXRhZnJhbWUuClBhcmEgZXN0byB1c2Ftb3MgZWwgY29tYW5kbyBgYXMuZGF0YS5mcmFtZWAgZG9uZGUgYW5pZGFtb3MgZWwgY8OzZGlnbyBwYXJhIHByb2R1Y2lyIGxhIHRhYmxhLgpFc3RlIGNvbWFuZG8gY3JlYSBkb3MgY29sdW1uYSwgIlZhcjEiIHF1ZSBndWFyZGEgbGFzIGV0aXF1ZXRhcyB5ICJGcmVxIiBxdWUgZ3VhcmRhIGVsIHZhbG9yIGRlIGxvcyBwb3JjZW50YWplcy4KCmBgYHtyIHRhYmxhMX0KdGFibGExID0gYXMuZGF0YS5mcmFtZShyb3VuZChwcm9wLnRhYmxlKHRhYmxlKGxhcG9wMjEkaW5nNHJlYykpKjEwMCwgMSkpCnRhYmxhMQpgYGAKCkx1ZWdvLCB0ZW5lbW9zIHF1ZSBkZWZpbmlyIHNvbiBsb3MgZGF0b3MgcXVlIHNlIHZhbiBhIHVzYXIgY29uIGxhIGVzcGVjaWZpY2FjacOzbiBgZGF0YT10YWJsYTFgLgpPam8sIG5vIHVzYXJlbW9zIGxhIGJhc2UgZGUgZGF0b3Mgb3JpZ2luYWwgImxhcG9wMjEiLCBzaW5vIGxhICJ0YWJsYTEiIGRvbmRlIGhlbW9zIGd1YXJkYWRvIGxvcyByZXN1bHRhZG9zLgoKRWwgY29tYW5kbyBgZ2dwbG90YCB0cmFiYWphIHN1bWFuZG8gY2FwYXMuCkxhIGVzcGVjaWZpY2FjacOzbiBgYWVzYCBzaXJ2ZSBwYXJhIGRlZmluaXIgbGEgImVzdMOpdGljYSIgZGVsIGdyw6FmaWNvLgpHZW5lcmFsbWVudGUgc2UgdXNhIHBhcmEgaW5kaWNhciBxdcOpIHZhcmlhYmxlIHNlIHZhIGEgZ3JhZmljYXIgZW4gcXXDqSBlamUgKHggbyB5KS4KVGFtYmnDqW4gc2UgcHVlZGUgdXNhciBsYSBlc3BlY2lmaWNhY2nDs24gYGZpbGw9YCBwYXJhIGRlZmluaXIgbG9zIGdydXBvcyBxdWUgc2UgdmFuIGEgZ2VuZXJhci4KRW4gZXN0ZSBjYXNvLCBlbiBlbCBlamUgWCBxdWVyZW1vcyBsYXMgY2F0ZWdvcsOtYXMgIlPDrSIgeSAiTm8iIHkgZW4gZWwgZWplIFksIGxvcyBkYXRvcyBkZSAiRnJlcSIuCgpMdWVnbyBkZSBlc3BlY2lmaWNhciBsb3MgZGF0b3MgeSBsb3MgZWplcywgc2UgdGllbmUgcXVlIGVzcGVjaWZpY2FyIGVsIHRpcG8gZGUgZ3LDoWZpY28gcXVlIHNlIHF1aWVyZSByZWFsaXphci4KRXN0byBzZSBoYWNlIGNvbiBsYXMgZ2VvbWV0csOtYXMgKCJnZW9tX3h4eCIpLgpTZSBkZWZpbmUgdW4gZ3LDoWZpY28gZGUgYmFycmFzIHNpbXBsZSwgdXNhbmRvIGVsIGNvbWFuZG8gYGdlb21fYmFyKC4uLilgLCBkb25kZSBpbnRlcm5hbWVudGUgZXNwZWNpZmljYW1vcyBgc3RhdD0iaWRlbnRpdHkiYCBwYXJhIHF1ZSBlc3RlIGNvbWFuZG8gdHJhYmFqZSBjb24gbG9zIGRhdG9zIHRhbCBjdWFsIGVzdMOhbiBlbiBsYSB0YWJsYS4KVGFtYmnDqW4gc2UgZGVmaW5lIGVsIGFuY2hvIGRlIGxhIGJhcnJhIGNvbiBsYSBlc3BlY2lmaWNhY2nDs24gYHdpZHRoYC4KQ29uIGxhIGVzcGVjaWZpY2FjacOzbiBgbGFic2Agc2UgZGVmaW5lIGxhcyBldGlxdWV0YXMgZGUgZWplcyB5IGVsICJjYXB0aW9uIi4KRmluYWxtZW50ZSwgY29uIGxhIGVzcGVjaWZpY2FjacOzbiBgY29vcmRfY2FydGVzaWFuYCBzZSBkZWZpbmUgbG9zIGzDrW1pdGVzIGRlbCBlamUgWSBkZSAwIGEgODAuClNlIGRlYmUgbm90YXIgcXVlIGVsIGdyw6FmaWNvIG5vIHByZXNlbnRhIHVuYSBjb2x1bW5hIHBhcmEgZWwgcG9yY2VudGFqZSBkZSB2YWxvcmVzIHBlcmRpZG9zLgpQcmVzZW50YXIgZXN0YSBjb2x1bW5hIGVzIG9wY2nDs24gZGVsIGludmVzdGlnYWRvci4KCmBgYHtyIGdnIHRhYmxhfQpsaWJyYXJ5KGdncGxvdDIpCmdncGxvdChkYXRhPXRhYmxhMSwgYWVzKHg9VmFyMSwgeT1GcmVxKSkrCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHdpZHRoPTAuNSkrCiAgbGFicyh4PSJBcG95byBhIGxhIGRlbW9jcmFjaWEiLCB5PSJQb3JjZW50YWplIiwgCiAgICAgICBjYXB0aW9uPSJCYXLDs21ldHJvIGRlIGxhcyBBbcOpcmljYXMgcG9yIExBUE9QLCAyMDIxIikrCiAgY29vcmRfY2FydGVzaWFuKHlsaW09YygwLCA4MCkpCmBgYAoKSGFzdGEgYXF1w60gc2UgaGEgcHJlc2VudGFkbyB1biBncsOhZmljbyBkZSBiYXJyYXMgZGUgbGEgdmFyaWFibGUgYXBveW8gYSBsYSBkZW1vY3JhY2lhIHBhcmEgdG9kYSBsYSBtdWVzdHJhLCBxdWUgaW5jbHV5ZSBhIHRvZG9zIGxvcyBwYcOtc2VzLgpFbCBncsOhZmljbyAxLjEgbXVlc3RyYSBlbCBwb3JjZW50YWplIHF1ZSBhcG95YSBhIGxhIGRlbW9jcmFjaWEgcG9yIHBhw61zLgpFc3RlIHRpcG8gZGUgZ3LDoWZpY28gc2UgdmVyw6EgbcOhcyBhZGVsYW50ZS4KCiMgVXN1YXJpb3MgZGUgcmVkZXMgc29jaWFsZXMKCkFob3JhIHNlIHVzYXLDoSB1biBlamVtcGxvIGRlbCByZXBvcnRlIEVsIFB1bHNvIGRlIGxhIERlbW9jcmFjaWEgZGUgbGEgcm9uZGEgMjAxOC8xOS4KU2Ugc2VndWlyw6EgcHJvY2VkaW1pZW50b3Mgc2ltaWxhcmVzIGEgbGEgc2VjY2nDs24gYW50ZXJpb3IgeSBzZSByZXBsaWNhcsOhbiBhbGd1bm9zIGdyw6FmaWNvcyBkZWwgbWlzbW8gcmVwb3J0ZSBkZSBlc2Egcm9uZGEuCkxhcyB2YXJpYWJsZXMgY29uIGxhcyBxdWUgc2UgdHJhYmFqYXLDoSBzb246IFNNRURJQTEuCsK/VGllbmUgdXN0ZWQgY3VlbnRhIGRlIEZhY2Vib29rPzsKU01FRElBNC4Kwr9UaWVuZSB1c3RlZCBjdWVudGEgZGUgVHdpdHRlcj87ClNNRURJQTcuCsK/VGllbmUgdXN0ZWQgY3VlbnRhIGRlIFdoYXRzYXBwPy4KRXN0YXMgcHJlZ3VudGFzIHRpZW5lbiBjb21vIG9wY2lvbmVzIGRlIHJlc3B1ZXN0YToKCjEuICBTw60KCjIuICBObwoKQWwgbW9tZW50byBkZSBsZWVyIGxhIGJhc2UgZGUgZGF0b3MgZW4gUiwgZXN0ZSBwcm9ncmFtYSBpbXBvcnRhIGxhcyB2YXJpYWJsZXMgY29tbyAibnVtIiwgcXVlIGxhIG1heW9yw61hIGRlIGZ1bmNpb25lcyBlbiBSIHRyYXRhIGNvbW8gbnVtw6lyaWNhcy4KRXN0YXMgdmFyaWFibGVzIHNlIHRpZW5lbiBxdWUgY29udmVydGlyIGEgdmFyaWFibGVzIGRlIHRpcG8gImZhY3RvciIgY29uIGVsIGNvbWFuZG8gYGFzLmZhY3RvcmAsIHB1ZXMgc29uIHZhcmlhYmxlcyBjYXRlZ8OzcmljYXMuCkVzdGEgbnVldmFzIHZhcmlhYmxlcyBsYXMgZ3VhcmRhbW9zIGVuIGVsIGRhdGFmcmFtZS4KCmBgYHtyIGZhY3Rvcn0KbGFwb3AxOCRzbWVkaWExciA9IGFzLmZhY3RvcihsYXBvcDE4JHNtZWRpYTEpCmxhcG9wMTgkc21lZGlhNHIgPSBhcy5mYWN0b3IobGFwb3AxOCRzbWVkaWE0KQpsYXBvcDE4JHNtZWRpYTdyID0gYXMuZmFjdG9yKGxhcG9wMTgkc21lZGlhNykKYGBgCgpFc3RhcyBudWV2YXMgdmFyaWFibGVzIGRlIHRpcG8gZmFjdG9yIHNlIHRpZW5lbiBxdWUgZXRpcXVldGFyIGNvbiBlbCBjb21hbmRvIGBsZXZlbHNgLgpTZSB1c2EgdW4gdmVjdG9yIGNvbiBsYXMgZXRpcXVldGFzIGNvbmNhdGVuYWRhcywgdXNhbmRvIGVsIGNvbWFuZG8gYGMoKWAuCgpgYGB7ciBldGlxdWV0YX0KbGV2ZWxzKGxhcG9wMTgkc21lZGlhMXIpID0gYygiU8OtIiwgIk5vIikKbGV2ZWxzKGxhcG9wMTgkc21lZGlhNHIpID0gYygiU8OtIiwgIk5vIikKbGV2ZWxzKGxhcG9wMTgkc21lZGlhN3IpID0gYygiU8OtIiwgIk5vIikKYGBgCgojIyBDYWxjdWxhciBsYXMgdmFyaWFibGVzIGRlIHVzdWFyaW9zIGRlIHJlZGVzIHNvY2lhbGVzCgpDb21vIHZpbW9zIGVuIHVuIG3Ds2R1bG8gYW50ZXJpb3IsIHNlIHB1ZWRlIGNhbGN1bGFyIG51ZXZhcyB2YXJpYWJsZXMgY29uIHZhbG9yZXMgY29uZGljaW9uYWxlcyBkZSBvdHJhcyB2YXJpYWJsZXMgdXNhbmRvIGVsIGNvbWFuZG8gYGlmZWxzZWAuCkRlIGVzdGEgbWFuZXJhLCBzZSBjcmVhIGxhcyB2YXJpYWJsZXMgZGUgdXN1YXJpb3MgZGUgcmVkZXMgc29jaWFsZXMuCgpgYGB7ciB1c3VhcmlvfQpsYXBvcDE4JGZiX3VzZXIgPSBpZmVsc2UobGFwb3AxOCRzbWVkaWExPT0xICYgbGFwb3AxOCRzbWVkaWEyPD00LCAxLCAwKQpsYXBvcDE4JHR3X3VzZXIgPSBpZmVsc2UobGFwb3AxOCRzbWVkaWE0PT0xICYgbGFwb3AxOCRzbWVkaWE1PD00LCAxLCAwKQpsYXBvcDE4JHdhX3VzZXIgPSBpZmVsc2UobGFwb3AxOCRzbWVkaWE3PT0xICYgbGFwb3AxOCRzbWVkaWE4PD00LCAxLCAwKQpgYGAKCiMjIERlc2NyaWJpciBsYXMgdmFyaWFibGVzCgpDb24gbGFzIHZhcmlhYmxlcyBsaXN0YXMsIGFob3JhIHByb2NlZGVtb3MgYSBoYWNlciBsYXMgdGFibGFzIGdlbmVyYWxlcyBjb24gZWwgY29tYW5kbyBgdGFibGVgLgpTZSBwdWVkZSBub3RhciBlbCB1c28gZGUgYCNgIGNvbW8gZm9ybWEgZGUgaGFjZXIgYW5vdGFjaW9uZXMsIHF1ZSBubyBzb24gY8OzZGlnbyBlbiBSLgoKYGBge3IgdGFibGFzfQp0YWJsZShsYXBvcDE4JHNtZWRpYTFyKSAjRmFjZWJvb2sKdGFibGUobGFwb3AxOCRzbWVkaWE0cikgI1R3aXR0ZXIKdGFibGUobGFwb3AxOCRzbWVkaWE3cikgI1doYXRzYXBwCmBgYAoKRXN0ZSBjb21hbmRvIGB0YWJsZWAgbm9zIGJyaW5kYSBsYXMgZnJlY3VlbmNpYXMgYWJzb2x1dGFzIChuw7ptZXJvIGRlIG9ic2VydmFjaW9uZXMpIHBvciBjYWRhIGNhdGVnb3LDrWEgZGUgbGFzIHZhcmlhYmxlcyAoZW4gZXN0ZSBjYXNvIFPDrSB5IE5vKS4KUGFyYSBvYnRlbmVyIGxhcyBmcmVjdWVuY2lhcyByZWxhdGl2YXMsIHVzYXJlbW9zIGVsIGNvbWFuZG8gYHByb3AudGFibGVgLCBkb25kZSBzZSBhbmlkYSBlbCBjb21hbmRvIGFudGVyaW9yIGB0YWJsZWAuCgpgYGB7ciBwcm9wb3JjaW9uZXN9CnByb3AudGFibGUodGFibGUobGFwb3AxOCRzbWVkaWExcikpCnByb3AudGFibGUodGFibGUobGFwb3AxOCRzbWVkaWE0cikpCnByb3AudGFibGUodGFibGUobGFwb3AxOCRzbWVkaWE3cikpCmBgYAoKU2luIGVtYmFyZ28sIGVsIGNvbWFuZG8gYHByb3AudGFibGVgIG5vcyBkZXZ1ZWx2ZSBkZW1hc2lhZG9zIGRlY2ltYWxlcyB5IGxhcyBmcmVjdWVuY2lhcyByZWxhdGl2YXMgZW4gdW5hIGVzY2FsYSBkZSAwIGEgMS4KUGFyYSByZWRvbmRlYXIgZXN0YSBjaWZyYSB1c2Ftb3MgZWwgY29tYW5kbyBgcm91bmRgLCBxdWUgbm9zIHBlcm1pdGUgZXNwZWNpZmljYXIgZWwgbsO6bWVybyBkZSBkZWNpbWFsZXMgcXVlIHNlIHF1aWVyZSBtb3N0cmFyLgpUYW50byBlbCBjb21hbmRvIGB0YWJsZWAsIGNvbW8gYHByb3AudGFibGVgIHNlIGFuaWRhbiBkZW50cm8gZGUgZXN0ZSBudWV2byBjb21hbmRvLgpFbiBlc3RlIGNhc28gc2UgaGEgdXNhZG8gMyBkZWNpbWFsZXMsIHBhcmEgY3VhbmRvIHNlIG11bHRpcGxpcXVlIHBvciAxMDAsIHF1ZWRlIGVuIGZvcm1hIGRlIHBvcmNlbnRhamUgY29uIDEgZGVjaW1hbC4KCmBgYHtyIHRhYmxhfQpyb3VuZChwcm9wLnRhYmxlKHRhYmxlKGxhcG9wMTgkc21lZGlhMXIpKSwgMykqMTAwCnJvdW5kKHByb3AudGFibGUodGFibGUobGFwb3AxOCRzbWVkaWE0cikpLCAzKSoxMDAKcm91bmQocHJvcC50YWJsZSh0YWJsZShsYXBvcDE4JHNtZWRpYTdyKSksIDMpKjEwMApgYGAKCk5vIGVzIHByw6FjdGljbyBwcmVzZW50YXIgMyB0YWJsYXMgY3VhbmRvIGxhcyB2YXJpYWJsZXMgdGllbmVuIGxhcyBtaXNtYXMgY2F0ZWdvcsOtYXMgZGUgcmVzcHVlc3RhLgpQYXJhIGZpbmVzIGRlIHByZXNlbnRhY2nDs24gcG9kcsOtYSBzZXIgbWVqb3IgY29uc3RydWlyIHVuYSBzb2xhIHRhYmxhLgpTZSBwdWVkZSBndWFyZGFyIGxhcyB0YWJsYXMgcGFyY2lhbGVzIGVuIG51ZXZvcyBvYmpldG9zIGNvbiBlbCBvcGVyYWRvciBgPC1gIHkgbHVlZ28gdW5pcmxhcyBjb21vIGZpbGFzIGNvbiBlbCBjb21hbmRvIGByYmluZGAgZW4gdW4gbnVldm8gZGF0YWZyYW1lICJ0YWJsYSIsIGRlIHRhbCBtYW5lcmEgcXVlIGxhcyByZXNwdWVzdGFzIGEgY2FkYSByZWQgc29jaWFsIGFwYXJlemNhbiBlbiBmaWxhcy4KCmBgYHtyIHRhYmxhanVudGF9CkZhY2Vib29rID0gcm91bmQocHJvcC50YWJsZSh0YWJsZShsYXBvcDE4JHNtZWRpYTFyKSksIDMpKjEwMApUd2l0dGVyID0gcm91bmQocHJvcC50YWJsZSh0YWJsZShsYXBvcDE4JHNtZWRpYTRyKSksIDMpKjEwMApXaGF0c2FwcCA9IHJvdW5kKHByb3AudGFibGUodGFibGUobGFwb3AxOCRzbWVkaWE3cikpLCAzKSoxMDAKdGFibGEyID0gYXMuZGF0YS5mcmFtZShyYmluZChGYWNlYm9vaywgVHdpdHRlciwgV2hhdHNhcHApKQp0YWJsYTIKYGBgCgpQYXJhIHRlbmVyIHVuYSBtZWpvciBwcmVzZW50YWNpw7NuIGRlIGxhIHRhYmxhLCBzZSBwdWVkZSB1c2FyIGVsIGNvbWFuZG8gYGthYmxlYCBkZWwgcGFxdWV0ZSBga25pdHJgLCB1c2FuZG8gbGEgdGFibGEgY29uc3RydWlkYSBhbnRlcmlvcm1lbnRlLgoKYGBge3IgdGFibGFtZWpvcmFkYSwgcmVzdWx0cz0nYXNpcyd9CmxpYnJhcnkoa25pdHIpCmtuaXRyOjprYWJsZSh0YWJsYTIsIGZvcm1hdD0ibWFya2Rvd24iKQpgYGAKCiMjIEdyYWZpY2FyIGxhcyB2YXJpYWJsZXMKCkVuIGVsIEdyw6FmaWNvIDMuMSBkZWwgcmVwb3J0ZSBzZSBvYnNlcnZhIHF1ZSBzZSByZXBvcnRhbiBlc3RvcyBkYXRvcyBtZWRpYW50ZSB1biBncsOhZmljbyBkZSBzZWN0b3JlcyBjaXJjdWxhcmVzLgoKIVtdKEdyYWYzLjEucG5nKXt3aWR0aD0iNDE4In0KClNlIHB1ZWRlIHJlcHJvZHVjaXIgZXNlIGdyw6FmaWNvIHVzYW5kbyBlbCBjb21hbmRvIGBwaWVgIHF1ZSBlcyBwYXJ0ZSBkZSBsYSBzaW50YXhpcyBiw6FzaWNhIGRlIFIuCkRlbnRybyBkZSBlc3RlIGNvbWFuZG8gc2UgcHVlZGUgYW5pZGFyIGVsIGNvbWFuZG8gYHRhYmxlYCBwYXJhIGdyYWZpY2FyIGVzdG9zIHZhbG9yZXMuCgpgYGB7ciBwaWUyfQpwaWUodGFibGUobGFwb3AxOCRzbWVkaWExcikpCmBgYAoKVGFtYmnDqW4gc2UgcG9kcsOtYSBwZW5zYXIgZW4gdW4gZ3LDoWZpY28gZGUgYmFycmFzLgpVc2FuZG8gbG9zIGNvbWFuZG9zIGLDoXNpY29zIGRlIFIsIHNlIHB1ZWRlIHVzYXIgZWwgY29tYW5kbyBgYmFycGxvdGAuCgpgYGB7ciBiYXJyYXMgc2ltcGxlfQpiYXJwbG90KHByb3AudGFibGUodGFibGUobGFwb3AxOCRzbWVkaWExcikpKQpgYGAKCkVzdG9zIGNvbWFuZG9zIGdyw6FmaWNvcyB0aWVuZW4gb3BjaW9uZXMgcGFyYSBhZGVjdWFyIGVsIGdyw6FmaWNvLCBwb3IgZWplbXBsbywgcGFyYSBpbmNsdWlyIGxvcyBwb3JjZW50YWplcyB5IGFkZWN1YXIgbGFzIGVzY2FsYXMuCgpQZXJvLCBjb21vIG1lbmNpb25hbW9zIG3DoXMgYXJyaWJhLCBwYXJhIHRlbmVyIG3DoXMgb3BjaW9uZXMgZ3LDoWZpY2FzLCB1c2FyZW1vcyBlbCBwYXF1ZXRlIGBnZ3Bsb3RgIHBhcmEgcmVwcm9kdWNpciBlbCBncsOhZmljbyBjaXJjdWxhciB5IGEgbG8gbGFyZ28gZGUgZXN0b3MgbcOzZHVsb3MuCgpMbyBwcmltZXJvIHF1ZSB0ZW5lbW9zIHF1ZSBoYWNlciBlcyBjcmVhciB1bmEgdGFibGEgcXVlIGd1YXJkZSBsb3MgZGF0b3MgcXVlIHF1ZXJlbW9zIGdyYWZpY2FyIHNvYnJlIGxvcyB1c3VhcmlvcyBkZSBGYWNlYm9vay4KUmVjb3JkZW1vcyBxdWUgdGVuZW1vcyB1bmEgdGFibGEgY29uIGVzdG9zIHJlc3VsdGFkb3MgZG9uZGUgbGEgcHJpbWVyYSBmaWxhIG11ZXN0cmEgbGFzIGV0aXF1ZXRhcyAiU8OtIiB5ICJObyIgeSBsYSBzZWd1bmRhIGZpbGEgbG9zIHBvcmNlbnRhamVzLgpSZXF1ZXJpbW9zIHF1ZSBsYXMgZXRpcXVldGFzIGVzdMOpbiBjb21vIGNvbHVtbmFzIHkgbG9zIHBvcmNlbnRhamVzIHRhbWJpw6luLgpQYXJhIGVzdG8gdGVuZW1vcyBxdWUgdHJhbnNwb25lciBsYSB0YWJsYSwgcXVlIHNpZ25pZmljYSAiZGFybGUgbGEgdnVlbHRhIi4KTHVlZ28sIGVzdGEgbnVldmEgdGFibGEgbGEgZ3VhcmRhbW9zIGNvbW8gdW4gZGF0YWZyYW1lIGNvbiBlbCBjb21hbmRvIGBhcy5kYXRhLmZyYW1lYCBxdWUgY3JlYSB0cmVzIGNvbHVtbmFzICJWYXIxIiAoc2luIGludGVyw6lzKSwgIlZhcjIiIGNvbiBsYXMgZXRpcXVldGFzIHkgRnJlcSBjb24gbG9zIHBvcmNlbnRhamVzLgoKYGBge3IgdGFibGEgRmFjZX0KRmFjZSA9IGFzLmRhdGEuZnJhbWUodChGYWNlYm9vaykpCkZhY2UKYGBgCgpDb24gZXN0YSB0YWJsYSBwb2RlbW9zIGNyZWFyIHVuIGdyw6FmaWNvIGRlIGJhcnJhcyBjb21vIGVsIHZpc3RvIG3DoXMgYXJyaWJhLgpPdHJvIHRpcG8gZGUgYmFycmFzIHF1ZSBzZSB1c2EgZnJlY3VlbnRlbWVudGUgZXMgZGVsIHRpcG8gZGUgYmFycmFzIGFwaWxhZGFzIGFsIDEwMCUuCgpWYW1vcyBhIGNyZWFyIHVuYSBiYXJyYSBhcGlsYWRhIGNvbiBlc3RvcyByZXN1bHRhZG9zLgpBIGRpZmVyZW5jaWEgZGVsIGdyw6FmaWNvIGRlIGJhcnJhcyBjb252ZW5jaW9uYWwsIGxhcyBiYXJyYXMgYXBpbGFkYXMgcmVxdWllcmVuIHF1ZSBlbiBsYSBgYWVzKHg9IiIsIHk9RnJlcSwgZmlsbD1WYXIyKWAuCkVzIGRlY2lyLCBxdWUgbm8gaGF5YSBldGlxdWV0YXMgZW4gZWwgZWplIFgsIHF1ZSBlbCBlamUgWSBtdWVzdHJlIGxvcyBwb3JjZW50YWplcyAoZ3VhcmRhZG9zIGVuICJGcmVxIikgeSBxdWUgbGEgYmFycmEgZXN0w6kgc2VwYXJhZGEgcG9yIGxhcyBjYXRlZ29yw61hcyAoZ3VhcmRhZGFzIGVuICJWYXIyIikuCgpTZSBhZ3JlZ2EgdW5hIGNhcGEgZGUgdGV4dG8sIGNvbiBsYSBlc3BlY2lmaWNhY2nDs24gYGdlb21fdGV4dGAuCkRlbnRybyBkZSBlc3RhIGVzcGVjaWZpY2FjacOzbiBzZSBkZXRlcm1pbmEgdW5hICJlc3TDqXRpY2EiIGNvbiBsYSBldGlxdWV0YSBkZWwgZGF0byBgYWVzKGxhYmVsPS4uLilgLCBkb25kZSBzZSBqdW50YSBjb24gZWwgY29tYW5kbyBgcGFzdGVgIGVsIGRhdG8gZGVsIHBvcmNlbnRhamUgInBlciIgeSBlbCBzw61tYm9sbyAiJSIsIGNvbiB1biBlc3BhY2lvIChgc2VwPS4uLmApIGVudHJlIGVsbG9zLgpTZSBlc3RhYmxlY2UgZWwgY29sb3IgZGUgbGEgZnVlbnRlIGNvbiBgY29sb3I9Ii4uLiJgLgpTZSBhanVzdGEgYSBibGFuY28gcGFyYSBxdWUgY29udHJhc3RlIGNvbiBsb3MgY29sb3JlcyBkZWwgZ3LDoWZpY28gY2lyY3VsYXIuCkNvbiBlbCBjb21hbmRvIGBoanVzdD0uLi5gIHNlIGFqdXN0YSBsYSBwb3NpY2nDs24gaG9yaXpvbnRhbCBkZSBlc3RlIHRleHRvLgpFbCBjb21hbmRvIGBnZ3Bsb3RgIHB1ZWRlIGluY2x1aXIgdmFyaW9zICJ0ZW1hcyIgcGFyYSBlbCBncsOhZmljby4KRW4gZXN0ZSBjYXNvIHNlIGhhIHVzYWRvIGB0aGVtZV92b2lkKClgIHF1ZSBpbmRpY2EgdW4gZm9uZG8gdmFjw61vLgpGaW5hbG1lbnRlLCBjb24gbGEgZXNwZWNpZmljYWNpw7NuIGBzY2FsZV9maWxsX2Rpc2NyZXRlKG5hbWU9Li4uKWAgc2UgcHVlZGUgY2FtYmlhciBlbCB0w610dWxvIGRlIGxhIGxleWVuZGEgcGFyYSBxdWUgbm8gYXBhcmV6Y2EgZWwgbm9tYnJlIGRlIGxhIHZhcmlhYmxlLCBzaW5vIHVuYSBldGlxdWV0YSBtw6FzIGFkZWN1YWRhLgoKYGBge3IgZ2cgYXBpbGFkYX0KZ2dwbG90KGRhdGE9RmFjZSwgYWVzKHg9IiIsIHk9RnJlcSwgZmlsbD1WYXIyKSkrCiAgZ2VvbV9iYXIod2lkdGg9MSwgc3RhdD0iaWRlbnRpdHkiKSsKICBnZW9tX3RleHQoYWVzKGxhYmVsPXBhc3RlKEZyZXEsICIlIiwgc2VwPSIiKSksIGNvbG9yPSJ3aGl0ZSIsCiAgICAgICAgICAgIHBvc2l0aW9uPXBvc2l0aW9uX3N0YWNrKHZqdXN0PTAuNSksIHNpemU9MykrCiAgdGhlbWVfdm9pZCgpKwogIHNjYWxlX2ZpbGxfZGlzY3JldGUobmFtZT0iwr9Vc2EgRmFjZWJvb2s/IikKYGBgCgpQYXJhIGNyZWFyIHVuIGdyw6FmaWNvIGNpcmN1bGFyLCBubyBleGlzdGUgdW5hICJnZW9tZXRyw61hIiBlbiBgZ2dwbG90YC4KU2luIGVtYmFyZ28sIHBhcmEgZ2VuZXJhIGVsIGdyw6FmaWNvIGNpcmN1bGFyLCBzZSB0aWVuZSBxdWUgYWdyZWdhciBvdHJvIGNhcGEgYGNvb3JkX3BvbGFyYCwgcXVlIHRyYW5zZm9ybWEgbGEgYmFycmEgYSBjb29yZGVuYWRhcyBwb2xhcmVzLCBjcmVhbmRvIHVuIGdyw6FmaWNvIGNpcmN1bGFyLgpUb2RvIGVsIHJlc3RvIGRlbCBjw7NkaWdvIGVzIHNpbWlsYXIgYWwgYW50ZXJpb3IuCgpgYGB7ciBnZyBwaWUyfQpnZ3Bsb3QoZGF0YT1GYWNlLCBhZXMoeD0iIiwgeT1GcmVxLCBmaWxsPVZhcjIpKSsKICBnZW9tX2Jhcih3aWR0aD0xLCBzdGF0PSJpZGVudGl0eSIpKwogIGdlb21fdGV4dChhZXMobGFiZWw9cGFzdGUoRnJlcSwgIiUiLCBzZXA9IiIpKSwgY29sb3I9IndoaXRlIiwKICAgICAgICAgICAgcG9zaXRpb249cG9zaXRpb25fc3RhY2sodmp1c3Q9MC41KSwgc2l6ZT0zKSsKICBjb29yZF9wb2xhcigieSIpKwogIHRoZW1lX3ZvaWQoKSsKICBzY2FsZV9maWxsX2Rpc2NyZXRlKG5hbWU9IsK/VXNhIEZhY2Vib29rPyIpCmBgYAoKU2kgcXVpc2nDqXJhbW9zIHJlZ3Jlc2FyIGEgaGFjZXIgdW4gZ3LDoWZpY28gZGUgYmFycmFzLCBwb2RlbW9zIGluY2x1aXIgZWwgdGV4dG8gZGUgbG9zIHZhbG9yZXMgY29uIGBnZW9tX3RleHRgLCBlbCB0w610dWxvIGdlbmVyYWwgZGVsIGdyw6FmaWNvIGNvbiBgdGl0bGVgIGRlbnRybyBkZSBgbGFic2AsIGxvcyBub21icmVzIGRlIGxvcyBlamVzIHkgZWwgImNhcHRpb24iLgoKYGBge3IgYmFycmEgZmFjZX0KZ2dwbG90KEZhY2UsIGFlcyh4PVZhcjIsIHk9RnJlcSkpKwogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IiwgIHdpZHRoPTAuNSkrCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbD1wYXN0ZShGcmVxLCAiJSIsIHNlcD0iIikpLCBjb2xvcj0iYmxhY2siLCB2anVzdD0tMC41KSsKICBsYWJzKHRpdGxlPSLCv1F1w6kgdGFuIGZyZWN1ZW50ZSBzZSB1c2FuIGxhcyByZWRlcyBzb2NpYWxlcz8iLCAKICAgICAgIHg9IlVzdWFyaW8gZGUgRmFjZWJvb2siLCB5PSJQb3JjZW50YWplIiwgCiAgICAgICBjYXB0aW9uPSJCYXLDs21ldHJvIGRlIGxhcyBBbcOpcmljYXMgcG9yIExBUE9QLCAyMDE4LzE5IikrCiAgY29vcmRfY2FydGVzaWFuKHlsaW09YygwLCA2MCkpCmBgYAoKIyBSZXN1bWVuCgpFbiBlc3RlIGRvY3VtZW50byBzZSBoYSB0cmFiYWphZG8gY29uIHZhcmlhYmxlcyBjYXRlZ8OzcmljYXMgbm9taW5hbGVzLCBjb21vIGFwb3lvIGEgbGEgZGVtb2NyYWNpYSBvIHNpIHVzYSBvIG5vIHVzYSByZWRlcyBzb2NpYWxlcy4KU2UgaGEgcHJlc2VudGFuZG8gbGFzIGZvcm1hcyBkZSBjw7NtbyBkZXNjcmliaXIgZW4gdGFibGFzIGRlIGZyZWN1ZW5jaWEgeSBjw7NtbyBncmFmaWNhciBlc3RhcyB2YXJpYWJsZXMsIG1lZGlhbnRlIGdyw6FmaWNvcyBjaXJjdWxhcmVzIG8gZGUgYmFycmFzLgoKIyBDw6FsY3Vsb3MgaW5jbHV5ZW5kbyBlbCBlZmVjdG8gZGUgZGlzZcOxbwoKRXN0b3Mgw7psdGltb3MgcmVzdWx0YWRvcyBkZSBsYSByb25kYSAyMDE4LzE5IG5vIHNvbiBleGFjdGFtZW50ZSBpZ3VhbGVzIGEgbG9zIGRlbCByZXBvcnRlIHB1ZXMgTEFQT1AgaW5jbHV5ZSBlbCBlZmVjdG8gZGVsIGRpc2XDsW8gbXVlc3RyYWwgZW4gc3VzIGPDoWxjdWxvcy4KU2Vnw7puIGVzdGEgc2ludGF4aXMsIHNlIGVuY3VlbnRyYSBxdWUgZWwgNTcuMSUgZGUgZW50cmV2aXN0YWRvcyByZXBvcnRhIHNlciB1c3VhcmlvIGRlIEZhY2Vib29rLCBjdWFuZG8gZW4gZWwgcmVwb3J0ZSBhcGFyZWNlIDU2LjIlLgpMbyBtaXNtbyBjb24gVHdpdHRlciwgcXVlIGFxdcOtIHNlIGNhbGN1bGEgZW4gOC44JSB5IGVuIGVsIHJlcG9ydGUgNy45JTsgeSBjb24gV2hhdHNhcHAgcXVlIGFxdcOtIGFwYXJlY2UgY29uIDY0LjYlIHkgZW4gZWwgcmVwb3J0ZSBjb24gNjQuNCUuCkNvbW8gc2UgaW5kaWPDsyBlbiBlbCBkb2N1bWVudG8gc29icmUgZWwgdXNvIGRlIGxvcyBmYWN0b3JlcyBkZSBleHBhbnNpw7NuIHVzYW5kbyBsb3MgZGF0b3MgZGVsIEJhcsOzbWV0cm8gZGUgbGFzIEFtw6lyaWNhcyAoZGlzcG9uaWJsZSBbYXF1w61dKGh0dHBzOi8vYXJ0dXJvbWFsZG9uYWRvLmdpdGh1Yi5pby9CYXJvbWV0cm9FZHVfV2ViL0V4cGFuc2lvbi5odG1sKSksIGhheSB2YXJpYXMgbWFuZXJhcyBkZSByZXByb2R1Y2lyIGxvcyByZXN1bHRhZG9zIGluY29ycG9yYW5kbyBlbCBlZmVjdG8gZGUgZGlzZcOxby4KVW5hIHByaW1lcmEgb3BjacOzbiBlcyB1c2FyIGVsIGNvbWFuZG8gYGZyZXFgIHF1ZSBwZXJtaXRlIGxhIGluY2x1c2nDs24gZGUgdW5hIHZhcmlhYmxlIGRlIGZhY3RvciBkZSBleHBhbnNpw7NuLCBjb21vICJ3ZWlnaHQxNTAwIi4KU2UgaW5jbHV5ZSBsYSBlc3BlY2lmaWNhY2nDs24gYHBsb3Q9RmAgcGFyYSBubyBwcm9kdWNpciBsb3MgZ3LDoWZpY29zIGRlIGJhcnJhcy4KCmBgYHtyIGRlc2NyaXB0aXZvcyBwb25kZXJhZG9zfQpsaWJyYXJ5KGRlc2NyKQpkZXNjcjo6ZnJlcShsYXBvcDE4JGZiX3VzZXIsIGxhcG9wMTgkd2VpZ2h0MTUwMCwgcGxvdCA9IEYpCmRlc2NyOjpmcmVxKGxhcG9wMTgkdHdfdXNlciwgbGFwb3AxOCR3ZWlnaHQxNTAwLCBwbG90ID0gRikKZGVzY3I6OmZyZXEobGFwb3AxOCR3YV91c2VyLCBsYXBvcDE4JHdlaWdodDE1MDAsIHBsb3QgPSBGKQpgYGAKClNpbiBjb25zaWRlcmFyIGVsIGVmZWN0byBkZSBkaXNlw7FvLCBzZSB0aWVuZSBxdWUgNTcuMSUgZGUgZW50cmV2aXN0YWRvcyBjdWVudGEgY29uIHVuYSBjdWVudGEgZGUgRmFjZWJvb2suCkVzdGUgcG9yY2VudGFqZSB2YXLDrWEgYSA1NS4yJSBzaSBzZSBpbmNsdXllIGxhIHZhcmlhYmxlIGRlIGV4cGFuc2nDs24sIHF1ZSBlcyBlbCB2YWxvciBxdWUgc2UgbXVlc3RyYSBlbiBlbCByZXBvcnRlLgpFc3RvcyByZXN1bHRhZG9zIHBvbmRlcmFkb3MgdGFtYmnDqW4gc2UgcHVlZGVuIGd1YXJkYXIgZW4gb2JqZXRvcyB5IGx1ZWdvIGdyYWZpY2FyIGRlIGxhIG1pc21hIG1hbmVyYSBxdWUgc2UgaGEgaGVjaG8gY29uIGxvcyByZXN1bHRhZG9zIHNpbiBwb25kZXJhci4KClBhcmEgZWwgY2FzbyBkZSBGYWNlYm9vaywgbGEgdGFibGEgc2UgcHVlZGUgZ3VhcmRhciBjb21vIHVuIGRhdGFmcmFtZSwgdXNhbmRvIGVsIGNvbWFuZG8gYGFzLmRhdGEuZnJhbWVgLgpFc3RhIHRhYmxhIGluY2x1eWUgZGF0b3MgcXVlIG5vIHJlcXVlcmltb3MsIGNvbW8gbGEgZmlsYSBkZSBOQcK0cyB5IGRlbCB0b3RhbCB5IGNvbW8gbGEgY29sdW1uYSBkZSBQZXJjZW50LgpFc3RhcyBmaWxhcyB5IGVzdGEgY29sdW1uYSBzZSBib3JyYW4gdXNhbmRvIGxhIGVzcGVjaWZpY2FjacOzbiBgWy1jKDMsNCksIC0yXWAuCgpMdWVnbywgc2UgbGUgY2FtYmlhIGVsIG5vbWJyZSBhIGxhcyBjb2x1bW5hcyBwYXJhIGV2aXRhciBlbCBub21icmUgIlZhbGlkIFBlcmNlbnQiLgpTZSBsYXMgbm9tYnJlIHNpbXBsZW1lbnRlIGNvbW8gImZyZXEiIHkgInBlciIuCkVzdGEgY29sdW1uYSAicGVyIiBlcyBsYSBxdWUgdGllbmUgbG9zIGRhdG9zIHF1ZSBncmFmaWNhcmVtb3MuCkZpbmFsbWVudGUsIHNlIGHDsWFkZSB1bmEgY29sdW1uYSAibGFiIiBjb24gbGFzIGV0aXF1ZXRhcyBkZSBjYWRhIGZpbGEgZGUgcmVzdWx0YWRvcy4KCmBgYHtyIHRhYmxhIGZifQpmYiA9IGFzLmRhdGEuZnJhbWUoZGVzY3I6OmZyZXEobGFwb3AxOCRmYl91c2VyLCBsYXBvcDE4JHdlaWdodDE1MDAsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGxvdCA9IEYpKQpmYiA9IGZiWy1jKDMsNCksIC0yXQpjb2xuYW1lcyhmYikgPSBjKCJmcmVxIiwgInBlciIpCmZiJGxhYiA9IGMoIk5vIiwgIlPDrSIpCmZiCmBgYAoKQ29uIGVzdGUgbnVldm8gZGF0YWZyYW1lIHBvZGVtb3MgcmVwbGljYXIgbG9zIG1pc21vIGPDs2RpZ29zIHVzYWRvcyBtw6FzIGFycmliYSBwYXJhIGhhY2VyIHVuIGdyw6FmaWNvIGRlIGJhcnJhcyBvIHVuIGdyw6FmaWNvIGNpcmN1bGFyLgpFbCBzaWd1aWVudGUgY8OzZGlnbyBtdWVzdHJhIGVsIGdyw6FmaWNvIGRlIGJhcnJhcy4KTsOzdGVzZSBxdWUgYWhvcmEgc2UgdXNhIGVsIGRhdGFmcmFtZSAiZmIiIHkgcXVlIGVuIGFlcyBzZSBlc3BlY2lmaWNhIHF1ZSBlbiBlbCBlamUgWCBkZWJlbiBlc3RhciBsb3MgZGF0b3MgZGUgbGEgY29sdW1uYSAibGFiIiB5IGVuIGVsIGVqZSBZIGxvcyBkYXRvcyBkZSBsYSBjb2x1bW5hICJwZXIiLgoKYGBge3IgYmFycmFzIHBvbmRlcmFkYXN9CmdncGxvdChkYXRhPWZiLCBhZXMoeD1sYWIsIHk9cGVyKSkrCiAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiLCAgd2lkdGg9MC41KSsKICBnZW9tX3RleHQoYWVzKGxhYmVsPXBhc3RlKHJvdW5kKHBlciwgMSksICIlIiwgc2VwPSIiKSksIAogICAgICAgICAgICBjb2xvcj0iYmxhY2siLCB2anVzdD0tMC41KSsKICBsYWJzKHRpdGxlPSLCv1F1w6kgdGFuIGZyZWN1ZW50ZSBzZSB1c2FuIGxhcyByZWRlcyBzb2NpYWxlcz8iLCAKICAgICAgIHg9IlVzdWFyaW8gZGUgRmFjZWJvb2siLCB5PSJQb3JjZW50YWplIiwgCiAgICAgICBjYXB0aW9uPSJCYXLDs21ldHJvIGRlIGxhcyBBbcOpcmljYXMgcG9yIExBUE9QLCAyMDE4LzE5IikrCiAgY29vcmRfY2FydGVzaWFuKHlsaW09YygwLCA2MCkpCmBgYAoKRXN0byBtaXNtbyBzZSBwdWVkZSBoYWNlciBwYXJhIGNyZWFyIHVuIGdyw6FmaWNvIGNpcmN1bGFyLgpFc3RlIGdyw6FmaWNvIHJlcHJvZHVjZSBsb3MgcmVzdWx0YWRvcyBoYWxsYWRvcyBlbiBlbCBHcsOhZmljbyAzLjEgZGVsIHJlcG9ydGUuCgpgYGB7ciBwaWUgcG9uZGVyYWRvfQpnZ3Bsb3QoZGF0YT1mYiwgYWVzKHg9MiwgeT1wZXIsIGZpbGw9bGFiKSkrCiAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiKSsKICBnZW9tX3RleHQoYWVzKGxhYmVsPXBhc3RlKHJvdW5kKHBlciwgMSksICIlIiwgc2VwPSIiKSksIGNvbG9yPSJ3aGl0ZSIsIAogICAgICAgICAgICBwb3NpdGlvbj1wb3NpdGlvbl9zdGFjayh2anVzdD0wLjUpLCBzaXplPTMpKwogIGNvb3JkX3BvbGFyKCJ5IikrCiAgdGhlbWVfdm9pZCgpKwogIGxhYnModGl0bGU9IsK/UXXDqSB0YW4gZnJlY3VlbnRlIHNlIHVzYW4gbGFzIHJlZGVzIHNvY2lhbGVzPyIsIAogICAgICAgY2FwdGlvbj0iQmFyw7NtZXRybyBkZSBsYXMgQW3DqXJpY2FzIHBvciBMQVBPUCwgMjAxOC8xOSIpKwogIHNjYWxlX2ZpbGxfZGlzY3JldGUobmFtZT0iwr9Vc2EgRmFjZWJvb2s/IikrCiAgeGxpbSgwLjUsIDIuNSkKYGBgCgpMYSBzZWd1bmRhIG9wY2nDs24gcGFyYSByZXByb2R1Y2lyIGxvcyBkYXRvcyBkZWwgcmVwb3J0ZSBleGFjdGFtZW50ZSBlcyBtZWRpYW50ZSBlbCBwYXF1ZXRlIGBzdXJ2ZXlgLgpDb21vIHNlIGluZGljw7MgZW4gZXN0YSBbc2VjY2nDs25dKGh0dHBzOi8vYXJ0dXJvbWFsZG9uYWRvLmdpdGh1Yi5pby9CYXJvbWV0cm9FZHVfV2ViL0V4cGFuc2lvbi5odG1sKSwgcHJpbWVybyBzZSB0aWVuZSBxdWUgZGVmaW5pciBlbCBkaXNlw7FvIG11ZXN0cmFsIGNvbiBlbCBjb21hbmRvIGBzdnlkZXNpZ25gLgoKYGBge3Igc3VydmV5fQpsaWJyYXJ5KHN1cnZleSkKZGlzZW5vMTggPSBzdnlkZXNpZ24oaWRzID0gfnVwbSwgc3RyYXRhID0gfmVzdHJhdG9wcmksIAogICAgICAgICAgICAgICAgICAgICB3ZWlnaHRzID0gfndlaWdodDE1MDAsIG5lc3Q9VFJVRSwgZGF0YT1sYXBvcDE4KQpgYGAKClVuYSB2ZXogY3JlYWRvIGxvcyBkYXRvcyBjb24gZWwgZmFjdG9yIGRlIGV4cGFuc2nDs24gZW4gZWwgb2JqZXRvICJkaXNlbm8xOCIsIHNlIHB1ZWRlIHVzYXIgbG9zIGNvbWFuZG9zIG5hdGl2b3MgZGVsIHBhcXVldGUgYHN1cnZleWAgcGFyYSByZWFsaXphciBjw6FsY3Vsb3MuClBvciBlamVtcGxvLCBwYXJhIGNhbGN1bGFyIGxhIHRhYmxhIGRlIGRpc3RyaWJ1Y2nDs24gZGUgZnJlY3VlbmNpYXMgc2UgcHVlZGUgdXNhciBlbCBjb21hbmRvIGBzdnl0YWJsZWAuCgpgYGB7ciBzdnl0YWJsZX0Kc3Z5dGFibGUofmZiX3VzZXIsIGRlc2lnbj1kaXNlbm8xOCkKYGBgCgpFc3RhcyBmcmVjdWVuY2lhcyBzZSBwdWVkZW4gYW5pZGFyIGVuIGVsIGNvbWFuZG8gYHByb3AudGFibGVgIHBhcmEgY2FsY3VsYXIgbG9zIHBvcmNlbnRhamVzIGRlIHVzdWFyaW9zIGRlIHJlZGVzIHNvY2lhbGVzLgpFc3RvcyByZXN1bHRhZG9zIHNvbiBpZ3VhbGVzIGEgbG9zIG1vc3RyYWRvcyBlbiBsb3MgZ3LDoWZpY29zIGFudGVyaW9yZXMgeSBhIGxvcyBxdWUgYXBhcmVjZW4gZW4gZWwgcmVwb3J0ZS4KCkVzdG9zIGRhdG9zIHRhbWJpw6luIHNlIHB1ZWRlbiBndWFyZGFyIGVuIHVuIGRhdGFmcmFtZSBxdWUgc2UgYWRhcHRhIHBhcmEgZ3JhZmljYXIsIHNpZ3VpZW5kbyBlbCBtaXNtbyBwcm9jZWRpbWllbnRvIHF1ZSBlbiBsb3MgZ3LDoWZpY29zIGFudGVyaW9yZXMuCgpgYGB7ciBzdnl0YWJsZSBwcm9wfQpwcm9wLnRhYmxlKHN2eXRhYmxlKH5mYl91c2VyLCBkZXNpZ249ZGlzZW5vMTgpKQpwcm9wLnRhYmxlKHN2eXRhYmxlKH50d191c2VyLCBkZXNpZ249ZGlzZW5vMTgpKQpwcm9wLnRhYmxlKHN2eXRhYmxlKH53YV91c2VyLCBkZXNpZ249ZGlzZW5vMTgpKQpgYGAK