Introducción

En este documento vamos a continuar usando el último informe regional “El pulso de la democracia”, disponible aquí, donde se presentan los principales hallazgos de la ronda 2018/19 del Barómetro de las Américas. 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. En este caso vamos a trabajar con la frecuencia de uso de las redes sociales. En este documento vamos a analizar descriptivamente estas variables sobre frecuencia de uso de redes sociales, variables de tipo ordinal (o de factor, en el lenguaje de R).

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 una base de datos recortada. Para reproducir los resultados mostrados en esta sección se debe limpiar en Environment.

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 elimina 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("lapop21.RData")
lapop21 <- subset(lapop21, pais<=35)

Describir y graficar las variables

En el documento sobre estadística descriptiva, que se puede ver aquí, se trabajó con variables nominales, con opciones de respuesta dicotómica (Sí/No). En este documento se va a trabajar con variables ordinales politómicas.

Los votos son contados correctamente

En esta sección se va a usar la variable COUNTFAIR1. Los votos son contados correcta y justamente. ¿Diría usted que sucede siempre, algunas veces o nunca? El gráfico 2.5 del reporte el Pulso de la Democracia, disponible aquí, presenta los resultados de esta variable por país.

De la misma manera que con las variables nominales, estas variables tienen que ser declaradas como “factor” en nuevas variables.

library(haven)
lapop21$countfair1r = as.factor(lapop21$countfair1)

Luego, estas variables se tienen que etiquetar y generar las tablas descriptivas básicas, con el comando table.

levels(lapop21$countfair1r) <- c("Siempre", "Algunas veces", "Nunca")
table(lapop21$countfair1r)
## 
##       Siempre Algunas veces         Nunca 
##          3477          5235          1698

Para calcular las tablas con porcentajes, redondeados a un decimal, usamos prop.table y round. Nuevamente, estos porcentajes no son exactamente iguales a los presentados en el reporte debido a que estos cálculos no incluyen el factor de expansión.

round(prop.table(table(lapop21$countfair1r)), 3)*100
## 
##       Siempre Algunas veces         Nunca 
##          33.4          50.3          16.3

Como se mencionó en la sección anterior, se puede graficar esta variable usando el comando barplot.

barplot(prop.table(table(lapop21$countfair1r))*100)

Otra opción es elaborar el gráfico de barras usando la librería ggplot. Una primera opción es trabajar directamente de la base de datos. El siguiente código, sin embargo, muestra una gran barra del porcentaje de casos perdidos. Esto se debe a que esta pregunta se realizó a la mitad de la muestra. Se registra NA a la otra mitad a la que no se le hizo esta pregunta.

library(ggplot2)
ggplot(data=lapop21, aes(x=countfair1r))+
  geom_bar(aes(y=..prop..*100, group=1), width=0.5)+
  labs(x="¿Los votos son contados correctamente?", y="Porcentaje", 
       caption="Barómetro de las Américas por LAPOP, 2021")+
  coord_cartesian(ylim=c(0, 100))

Para evitar que el gráfico presente la barra de NAs, estas observaciones se tienen que filtrar antes de producir el gráfico. Tal como indicamos en el módulo anterior, se filtra los NAs de la variable “countfair1r” con el comando subset y la especificación !is.na.

ggplot(data=subset(lapop21, !is.na(countfair1r)), aes(x=countfair1r))+
  geom_bar(aes(y=..prop..*100, group=1), width=0.5)+
  labs(x="¿Los votos son contados correctamente?", y="Porcentaje", 
       caption="Barómetro de las Américas por LAPOP, 2021")+
  coord_cartesian(ylim=c(0, 60))

Otra opción, que simplifica el código, es crear una tabla de frecuencias de esta variable con el comando table y prop.table. Esta table se redondea a un decimal con el comando round y se guarda como un dataframe con el comando as.data.frame en un objeto “count”. Esta tabla almacena dos columnas, la primera llamada “Var1” con las etiquetas de la variable y la segunda llamada “Freq” con los porcentajes.

count <- as.data.frame(round(prop.table(table(lapop21$countfair1r)), 3)*100)
count

Podemos usar esta tabla “count” para producir el gráfico de barras con el comando ggplot. En la especificación aes se define que los valores de la columna “Var1” se presenten en el eje X y los valores de la columna a “Freq” en el eje Y. Se define un gráfico de barras simple, usando el comando geom_bar(), donde internamente se define el ancho de la barra. Con la especificación labs se define las etiquetas de ejes y el “caption”.

ggplot(data=count, aes(x=Var1, y=Freq))+
  geom_bar(stat="identity", width=0.5)+
  geom_text(aes(label=paste(Freq, "%", sep="")), color="white", 
            position=position_stack(vjust=0.5), size=3)+
  labs(x="Los votos se cuentan justamente", y="Porcentaje", 
       caption="Barómetro de las Américas por LAPOP, 2021")

El gráfico 2.5 presenta una barra apilada con los datos por cada país. Primero presentaremos el gráfico de barras apilado usando los datos de toda la ronda 2021 del Barómetro de las Américas, es decir de todos los países. Para producir una barra apilada horizontal, se usará la variable “Freq” ahora en el eje X. Se usará la opción fill para dividir esta barra por los valores de la variable “Var1”. Como en el eje Y no se mostrará una variable se define como "". De la misma manera que se cambiaron las variables en los ejes, también se cambian las etiquetas en labs. En esta especificación se cambia la etiqueta de la leyenda con fill.

ggplot(data=count, aes(fill=Var1, x=Freq, y=""))+
  geom_bar(stat="identity", width=0.3)+
  geom_text(aes(label=paste(Freq, "%", sep="")), color="white", 
            position=position_stack(vjust=0.5), size=3)+
  labs(x="Porcentaje", y="", fill="Los votos se cuentan justamente",
       caption="Barómetro de las Américas por LAPOP, 2021")

Para replicar el gráfico comparativo por país se requiere crear la tabla de contingencia entre la variable “countfair” y “pais”. Esta tabla cruzada se guarda en un objeto “count_pais”. Se debe notar que el dataframe que se crea crea una fila por cada valor de “countfair” en cada país. De esta manera tenemos 3 opciones x 20 países = 60 filas.

count_pais = as.data.frame(round(prop.table(table(lapop21$pais, lapop21$countfair1r), 1), 3)*100)
count_pais

En esta tabla se calculan los datos por cada valor de la variable “pais”, incluso cuando no se tiene datos de la variable “countfair”, debido a que la pregunta no se realizó en ese país. Por este motivo se tienen que eliminar las filas de los países en los que no se recogió esta información. Esto se hace con la especificación [-c(filas),]. Luego se crea un vector con los nombres de los países. Esta lista se repite 3 veces (15 países restantes x 3 opciones). Este vector se agrega al dataframe en una columna “pais”.

count_pais = count_pais[-c(1:4,18,21:24,38,41:44,58),]
pais = c("Nicaragua","Costa Rica", "Panamá", "Colombia", "Ecuador", "Bolivia", "Perú",
        "Paraguay", "Chile", "Uruguay", "Brasil", "Argentina", "Rep. Dom.","Jamaica", "Guyana", "Nicaragua","Costa Rica", "Panamá", "Colombia", "Ecuador", "Bolivia", "Perú",
        "Paraguay", "Chile", "Uruguay", "Brasil", "Argentina", "Rep. Dom.","Jamaica", "Guyana","Nicaragua","Costa Rica", "Panamá", "Colombia", "Ecuador", "Bolivia", "Perú",
        "Paraguay", "Chile", "Uruguay", "Brasil", "Argentina", "Rep. Dom.","Jamaica", "Guyana")
count_pais$pais = pais
count_pais

Con este dataframe “count_pais” ya tenemos los elementos para replicar el gráfico de barras apiladas. En la especificación aes se define que en el eje X se grafiquen los porcentajes, en el eje Y los países y cada barra se divida por la columna Var2.

ggplot(data=count_pais, aes(x=Freq, y=pais, fill=Var2))+
  geom_bar(stat="identity", width=0.3)+
  geom_text(aes(label=paste(Freq, "%", sep="")), color="white", 
            position=position_stack(vjust=0.5), size=2)+
  labs(x="Porcentaje", y="País", fill="Los votos se cuentan justamente",
       caption="Barómetro de las Américas por LAPOP, 2021")

Frecuencia de uso de redes sociales

En la ronda 2018/19 se evaluó el uso de redes sociales. De esta manera, se analizaron las variables SMEDIA2. ¿Con qué frecuencia ve contenido en Facebook?, SMEDIA5.¿Con qué frecuencia ve contenido en Twitter? y SMEDIA8.¿Con qué frecuencia usa Whatsapp? Estas variables tienen como opciones de respuesta:

  1. Diariamente
  2. Algunas veces a la semana
  3. Algunas veces al mes
  4. Algunas veces al año
  5. Nunca

De la misma manera que con las variables nominales, estas variables tienen que ser declaradas como “factor” en nuevas variables.

lapop18$smedia2r = as.factor(lapop18$smedia2)
lapop18$smedia5r = as.factor(lapop18$smedia5)
lapop18$smedia8r = as.factor(lapop18$smedia8)

La nota a pie 15 del reporte indica que “los entrevistados que reportan nunca ver contenido en Facebook y Twitter, y quienes indican que nunca usan Whatsapp, son considerados como no usuarios de estas redes sociales” (p. 64). Por este motivo, el gráfico 3.3 de frecuencia de uso de redes sociales (pag. 57) solo incluyen las categorías “Diariamente”, “Algunas veces a la semana”, “Algunas veces al mes” y “Algunas veces al año”. Se excluya la categoría “Nunca”.

En las nuevas variables vamos a declarar el valor 5, correspondiente a “nunca”, como “NA”, es decir, como valor perdido en R.

library(car)
lapop18$smedia2r = car::recode(lapop18$smedia2r, "5=NA")
lapop18$smedia5r = car::recode(lapop18$smedia5r, "5=NA")
lapop18$smedia8r = car::recode(lapop18$smedia8r, "5=NA")

Luego, estas variables se tienen que etiquetar y generar las tablas descriptivas básicas, con el comando table.

levels(lapop18$smedia2r) <- c("Diariamente", "Algunas veces a la semana", 
                            "Algunas veces al mes", "Algunas veces al año")
levels(lapop18$smedia5r) <- c("Diariamente", "Algunas veces a la semana", 
                            "Algunas veces al mes", "Algunas veces al año")
levels(lapop18$smedia8r) <- c("Diariamente", "Algunas veces a la semana", 
                            "Algunas veces al mes", "Algunas veces al año")
table(lapop18$smedia2r)
## 
##               Diariamente Algunas veces a la semana      Algunas veces al mes 
##                      8633                      4927                      1286 
##      Algunas veces al año 
##                       282
table(lapop18$smedia5r)
## 
##               Diariamente Algunas veces a la semana      Algunas veces al mes 
##                       795                       728                       422 
##      Algunas veces al año 
##                       193
table(lapop18$smedia8r)
## 
##               Diariamente Algunas veces a la semana      Algunas veces al mes 
##                     14151                      2646                       495 
##      Algunas veces al año 
##                        50

Para calcular las tablas con porcentajes, redondeados a un decimal, usamos prop.table y round. Nuevamente, estos porcentajes no son exactamente iguales a los presentados en el reporte debido a que estos cálculos no incluyen el factor de expansión.

round(prop.table(table(lapop18$smedia2r)), 3)*100
## 
##               Diariamente Algunas veces a la semana      Algunas veces al mes 
##                      57.1                      32.6                       8.5 
##      Algunas veces al año 
##                       1.9
round(prop.table(table(lapop18$smedia5r)), 3)*100
## 
##               Diariamente Algunas veces a la semana      Algunas veces al mes 
##                      37.2                      34.1                      19.7 
##      Algunas veces al año 
##                       9.0
round(prop.table(table(lapop18$smedia8r)), 3)*100
## 
##               Diariamente Algunas veces a la semana      Algunas veces al mes 
##                      81.6                      15.3                       2.9 
##      Algunas veces al año 
##                       0.3

Para presentar todos los datos en una tabla conjunta, se guarda la tabla de cada red social en un nuevo objeto de R (llamado como cada red social). Luego, todas estas tablas parciales, que contienen las mismas opciones de respuesta, se unen como filas con el comando rbind. Esta nueva tabla conjunta se guarda como un nuevo dataframe “tabla”.

Facebook <- round(prop.table(table(lapop18$smedia2r)), 3)*100
Twitter <- round(prop.table(table(lapop18$smedia5r)), 3)*100
Whatsapp <- round(prop.table(table(lapop18$smedia8r)), 3)*100
tabla <- as.data.frame(rbind(Facebook, Twitter, Whatsapp))
tabla

Para tener una mejor presentación de la tabla, se puede usar el comando kable del paquete knitr o el comando formattable del paquete del mismo nombre.

library(knitr)
kable(head(tabla), format="markdown", digits=1)
Diariamente Algunas veces a la semana Algunas veces al mes Algunas veces al año
Facebook 57.1 32.6 8.5 1.9
Twitter 37.2 34.1 19.7 9.0
Whatsapp 81.6 15.3 2.9 0.3
library(formattable)
formattable(tabla)
Diariamente Algunas veces a la semana Algunas veces al mes Algunas veces al año
Facebook 57.1 32.6 8.5 1.9
Twitter 37.2 34.1 19.7 9.0
Whatsapp 81.6 15.3 2.9 0.3

Para graficar esta variable no vamos a seguir el mismo procedimiento que en el módulo anterior. En este módulo vamos a trabajar directamente desde la tabla creada con los porcentajes de las tres redes sociales. Esta tabla tiene a cada red social en las filas y las opciones de respuesta en las columnas. Para poder graficar estos datos se requiere que las redes sociales estén en las columnas y las opciones de respuesta en las filas, por lo que en primer lugar vamos a transponer esta tabla. Este procedimiento lo hacemos usando la librería data.table y el comando transpose. Estos datos transpuestos los guardamos en una nueva tabla “tabla_tr”.

Este comando transpone los datos, pero deja sin nombrar las filas y columnas. Primero se nombran las columnas con el comando colnames usando los nombres de las filas de “tabla”. Luego se nombran las filas con el comando rownames usando los nombres de las columnas de “tabla”.

Finalmente, requerimos una columna dentro de “tabla_tr” que incluya las etiquetas de respuesta de las preguntas de smedia. Estas etiquetas están como nombres de filas. Para incluirlas como una variable más, se agrega una variable “tabla_tr$lab” al que se le asigna los nombres de las filas con row.names.

library(data.table)
tabla_tr = data.frame(t(tabla[]))
colnames(tabla_tr) = rownames(tabla)
rownames(tabla_tr) = colnames(tabla)
tabla_tr$lab <- rownames(tabla_tr)
tabla_tr

En segundo lugar, se activa la librería ggplot2 para graficar los datos guardados en “tabla_tr” con el comando ggplot. Dentro de este comando se especifica que se trabajará con el dataframe “tabla_tr” (no con lapop18) y se especifica la “estética”, es decir que en el eje X no se incluirá una variable, que en el eje Y se incluirá los datos de “Facebook” del dataframe “tabla_tr” y que se dividirá por categorías de la variable guardadas en “lab”. Luego se añade la capa geom_bar para indicar que se graficará en barras de ancho (width) 1 y que la barra replicará lo especificado en la estética (stat="identity"). Con geom_text se agrega las etiquetas de los datos, con el símbolo de porcentaje, y se especifica la posición del texto con position=position_stack(…) y el tamaño con size=3. Con coord_polar se transforma las barras en un gráfico circular de sectores. Finalmente, se define el “tema” con theme_void indicando un fondo blanco y se modifica la etiqueta de la leyenda con scale_fill_discrete.

library(ggplot2)
ggplot(data=tabla_tr, aes(x="", y=Facebook, fill=lab))+
  geom_bar(width=1, stat="identity")+
  geom_text(aes(label=paste(Facebook, "%", sep="")), color="white", 
            position=position_stack(vjust=0.5), size=3)+
  coord_polar("y", start=0)+
  theme_void()+
  scale_fill_discrete(name="Frecuencia de uso de Facebook")

En el gráfico 3.3 del reporte “El Pulso de la Democracia” se presenta un gráfico circular tipo “donna”. Para reproducir exactamente este tipo de gráfico, se tiene que acomodar unos detalles de la sintaxis anterior. Se establece “x=2” en la “estética” y se establece límites en el eje X, entre 0.5 y 2.5, para que cuando se rote el eje, se cree el “hueco” dentro del círculo.

ggplot(data=tabla_tr, aes(x=2, y=Facebook, fill=lab))+
  geom_bar(stat="identity")+
  geom_text(aes(label=paste(Facebook, "%", sep="")), color="white", 
            position=position_stack(vjust=0.5), size=3)+
  coord_polar("y")+
  theme_void()+
  scale_fill_discrete(name="Frecuencia de uso de Facebook")+
   labs(title="Frecuencia con la que ve contenido en Facebbok", 
        caption="Barómetro de las Américas por LAPOP, 2018/19")+
  xlim(0.5, 2.5)

Para replicar el gráfico de barras, se hacen algunas modificaciones en el código anterior. Por ejemplo, en la “estética”, ahora se indica que en el eje X va la variable “smedia2r” y en el eje Y el porcentaje “per”. Se elimina, además, la especificación de las coordenadas polares. Se agrega un título al gráfico, etiquetas a los ejes y un caption con la especificación labs. Finalmente, se define el eje Y entre 0 y 60 con la especificación coord_cartesian. Un tema importante es que este gráfico también se puede guardar en un objeto en R, que llamaremos “graf1”.

graf1 <- ggplot(tabla_tr, aes(x=lab, y=Facebook))+
  geom_bar(stat="identity",  width=0.5)+
  geom_text(aes(label=paste(Facebook, "%", sep="")), color="black", vjust=-0.5)+
  labs(title="Frecuencia de uso de redes sociales", x="Frecuencia de uso de Facebook", y="Porcentaje", caption="Barómetro de las Américas por LAPOP, 2018/19")+
  coord_cartesian(ylim=c(0, 60))
graf1

Guardar un gráfico en un objeto nos permite luego incorporar más capas o mejoras en el gráfico. El gráfico anterior, por ejemplo, tiene las etiquetas de “Frecuencia de uso” en una sola línea, por lo se podrían trasponer. Para mejorar esta visualización se puede inclinar estas etiquetas, pero si se quisiera mantener la horizontalidad para facilitar la lectura, lo que se puede hacer es separar las etiquetas largas en dos o más líneas. Esto se puede hacer, por ejemplo, creando un vector con las nuevas etiquetas. Las etiquetas largas se pueden partir en líneas mediante la inclusión de \n entre los textos que se quiera separar. Luego, al objeto “graf1” se le puede agregar una nueva capa para reemplazar las etiquetas con las nuevas divididas en líneas. Esto se hace con la especificación scale_x_discrete donde se indica que las etiquetas usen el vector creado.

etiq <- c("Diariamente", "Algunas veces\na la semana", "Algunas veces\nal mes", 
          "Algunas veces\nal año")
graf1 +
  scale_x_discrete(labels=etiq)

De esta manera el gráfico queda más legible. Hasta aquí se ha replicado las tablas y gráficos que se usaron con las variables nominales, ahora usando variables ordinales. Si se quiere replicar el gráfico circular de las otras redes sociales, se puede ejecutar el mismo código, pero cambiando la columna de la red social. Para Twitter, por ejemplo, se tendría.

graf2 <- ggplot(data=tabla_tr, aes(x=2, y=Twitter, fill=lab))+
  geom_bar(stat="identity")+
  geom_text(aes(label=paste(Twitter, "%", sep="")), color="white", 
            position=position_stack(vjust=0.5), size=3)+
  coord_polar("y")+
  theme_void()+
  scale_fill_discrete(name="Frecuencia de uso de Twitter")+
   labs(title="Frecuencia con la que ve contenido en Twitter", 
        caption="Barómetro de las Américas por LAPOP, 2018/19")+
  xlim(0.5, 2.5)
graf2

Cruce de variables

En la tabla 3.1 (pag. 55) del reporte “El pulso de la democracia” se presenta los porcentajes de uso de las redes sociales por país. Luego, en la página 56 se presenta un cuadro con el porcentaje de usuarios de redes sociales por características sociodemográficas, por ejemplo, urbano/rural, hombre, edad promedio, riqueza promedio y años de estudio.

Empezaremos replicando los datos generales del uso de redes sociales que se reporta en el gráfico 3.1. Para replicar esta tabla primero se tiene que definir la variable “pais” y las variables de uso de redes sociales (smedia1, smedia4 y smedia7).

lapop18$smedia1r = as.factor(lapop18$smedia1)
lapop18$smedia4r = as.factor(lapop18$smedia4)
lapop18$smedia7r = as.factor(lapop18$smedia7)
levels(lapop18$smedia1r) <- c("Sí", "No")
levels(lapop18$smedia4r) <- c("Sí", "No")
levels(lapop18$smedia7r) <- c("Sí", "No")
lapop18$pais = as.factor(lapop18$pais)
levels(lapop18$pais) <- c("México", "Guatemala", "El Salvador", "Honduras",
                        "Nicaragua","Costa Rica", "Panamá", "Colombia", 
                        "Ecuador", "Bolivia", "Perú", "Paraguay", 
                        "Chile", "Uruguay", "Brasil", "Argentina", 
                        "Rep. Dom.", "Jamaica")
table(lapop18$pais) #País
## 
##      México   Guatemala El Salvador    Honduras   Nicaragua  Costa Rica 
##        1580        1596        1511        1560        1547        1501 
##      Panamá    Colombia     Ecuador     Bolivia        Perú    Paraguay 
##        1559        1663        1533        1682        1521        1515 
##       Chile     Uruguay      Brasil   Argentina   Rep. Dom.     Jamaica 
##        1638        1581        1498        1528        1516        1513
table(lapop18$smedia1r) #Facebook
## 
##    Sí    No 
## 15389 11573

Sin embargo, la variable “usuario” de cada red social se calcula como condición de dos variables, como se vio en el documento sobre manejo de datos, con el siguiente código.

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)

Esta variables son graficadas en el reporte en el Gráfico 3.1. Para reproducir estos datos, se puede describir estas variables.

prop.table(table(lapop18$fb_user))*100
## 
##       0       1 
## 43.8289 56.1711
prop.table(table(lapop18$tw_user))*100
## 
##         0         1 
## 92.056769  7.943231
prop.table(table(lapop18$wa_user))*100
## 
##        0        1 
## 35.76561 64.23439

Como en gráficos anteriores, con estos datos se puede crear un dataframe que se utilizaría para hacer los gráficos circulares mostrados en el reporte.

Para replicar el cuadro de uso de redes sociales por país, primero, se crean las tablas bivariadas con el porcentaje de los que usan y el porcentaje de los que no usan cada red social en cada país. Estas tablas se guardan en objetos de R. Luego se unen estos objetos usando los comandos cbind para juntar las columnas y as.data.frame para unir las tablas como un dataframe. Esta tabla presenta también los porcentajes de los que No usan estas redes sociales. Para presentar una tabla que incluya solo a los que sí usan las redes sociales, se eliminan estas columnas, usando la especificación [, c(-1,-3,-5)], que indica que se quiere eliminar las columnas 1, 3 y 5.Finalmente, se cambia el nombre de las columnas del dataframe.

fbpais <- round(prop.table(table(lapop18$pais, lapop18$fb_user), 1), 3)*100
twpais <- round(prop.table(table(lapop18$pais, lapop18$tw_user), 1), 3)*100
whpais <- round(prop.table(table(lapop18$pais, lapop18$wa_user), 1), 3)*100
tablapais <- as.data.frame(cbind(fbpais, twpais, whpais))
tablapais <- tablapais[, c(-1,-3,-5)]
varnames <- c("Usa Facebook", "Usa Twitter", "Usa Whatsapp")
colnames(tablapais) <- varnames
tablapais

Para tener una mejor presentación de la tabla se tienen dos alternativas: la primera con la librería knitr y la otra con la librería formattable.

library(knitr)
kable(head(tablapais), format="markdown", digits=1)
Usa Facebook Usa Twitter Usa Whatsapp
México 47.9 7.6 55.1
Guatemala 43.2 6.5 47.6
El Salvador 56.2 6.7 56.0
Honduras 44.6 4.8 46.9
Nicaragua 48.1 5.8 47.7
Costa Rica 66.6 8.0 81.6
library(formattable)
formattable(tablapais)
Usa Facebook Usa Twitter Usa Whatsapp
México 47.9 7.6 55.1
Guatemala 43.2 6.5 47.6
El Salvador 56.2 6.7 56.0
Honduras 44.6 4.8 46.9
Nicaragua 48.1 5.8 47.7
Costa Rica 66.6 8.0 81.6
Panamá 34.6 5.8 56.7
Colombia 60.0 10.7 63.5
Ecuador 66.9 11.2 60.2
Bolivia 57.9 5.6 63.5
Perú 61.4 7.6 58.6
Paraguay 60.5 8.0 69.2
Chile 62.9 9.2 75.5
Uruguay 66.5 9.8 80.0
Brasil 59.2 7.9 74.9
Argentina 67.3 12.9 78.9
Rep. Dom. 61.9 9.5 68.2
Jamaica 45.9 4.5 68.1

Cruce con variables sociodemográficas

En la página 56 del reporte “El pulso de la democracia” se presenta los resultados del cruce entre las variables uso de redes sociales y variables sociodemográficas como urbano/rural, sexo, edad, riqueza y años de educación.

La variable “q1” registra el género del entrevistado. Esta variable está codificada de la siguiente manera:

  1. Hombre
  2. Mujer

Para construir una variable “dummy” llamada “hombre”, de tal manera que hombre sea 1 y mujer sea 0, una forma de hacerlo es de manera algebraica (2-variable q1). De esta manera, ahora los hombres mantiene el valor 1 (2-1) y las mujeres pasar a ser 0 (2-2). En este caso, la nueva variable “hombre” se crea como una una variable numérica. Vamos a mantener a la variable como “num” para, más abajo, poder calcular la media de esta variable (cosa que no se puede hacer de una variable declarada como factor).

lapop18$hombre <- 2-lapop18$q1
table(lapop18$hombre)
## 
##     0     1 
## 14084 13943

La variable urbano/rural se llama “ur” en la base de datos y está codificada de la siguiente manera:

  1. Urbano
  2. Rural

De la misma manera que con género, se usa la fórmula 2- variable ur, pero esta variable sí se convierte en factor y se etiqueta.

lapop18$urban <- 2-lapop18$ur
lapop18$urban = as.factor(lapop18$urban)
levels(lapop18$urban) <- c("Rural", "Urbano")
table(lapop18$urban)
## 
##  Rural Urbano 
##   8089  19953

Se presentará las tablas cruzadas del uso de redes sociales por alguno de las variable sociodemográficas, para entender mejor cómo se construye la tabla general. Por ejemplo, el reporte muestra en la Tabla 3.2, luego de la columna de población general, las columnas para usuarios y no usuarios de Whatsapp (variable “wa_user”) y en las filas, la primera corresponde a urbano (variable “urban”, donde 0 es rural y 1 urbano). Se presenta que entre los usuarios de Whatsapp, el 76.7% son urbanos y entre los no usuarios, el 62% son urbanos. Estos datos se generan con el comando table que permite hacer una tabla de contingencia de 2 variables. En este caso se tiene que notar que cada variable tiene un título. De esta manera el comando es table(título1 = var1, título2 = var2). Luego, se usa el comando prop.table para que no se muestre las observaciones, sino los porcentajes. Este comando permite calcular los porcentajes sobre el total (la opción por defecto), sobre las filas y sobre las columnas. Para calcular sobre las columnas se debe especificar prop.table(table(...), 2). Si se quisiera el porcentaje sobre las filas, el código sería prop.table(table(...), 1). Esta tabla se pueden guardar como un dataframe con el comando as.data.frame en un nuevo objeto “t1”.

t1 <- as.data.frame(round(prop.table(table(Urbano = lapop18$urban, Usuario = lapop18$wa_user), 2)*100, 1))
t1

La filas correspondientes a “Urbano” de esta tabla reproducen los datos de la Tabla 3.2: 62.1% son urbanos entre los no usuarios de Whatsapp y 76.7% son urbanos entre los usuarios.

Ahora se presentará la tabla entre usuarios de Facebook (variable “fb_user”) y hombre (variable “hombre”), pero presentando solo los porcentajes usados en la tabla y ahora usando el estilo de código del Tidyverse, usando el operador “pipe”. Lo primero es definir la variable “fb_user” como un factor y etiquetarla. Luego, se usa el comando subset para filtrar los casos perdidos en la variable “fb_user”. Luego se pide que los resultados se agrupen por categorías de la variable “fb_user”. Con el comando summarise se guarda en la columna “hombre” el promedio de la variable “hombre”, con el comando mean que incluye la especificación na.rm=T para no incluir en el cálculo los valores perdidos. En este caso se aprovecha que la variable “hombre” es una variable dummy, de tal manera que el promedio corresponde a la proporción de hombres.

lapop18$fb_user = as.factor(lapop18$fb_user)
levels(lapop18$fb_user) <- c("No usuario", "Usuario")
tabla1 <- subset(lapop18, !is.na(fb_user)) %>% #Para no incluir al grupo de NA de usuarios de Facebook
  group_by(fb_user) %>%
  dplyr::summarise(Hombre=mean(hombre, na.rm=T)*100) #Se incluye na.rm=T porque hombre tiene NAs
tabla1

Estos son los porcentajes para los usuarios de Facebook, en la fila de la variable Hombre de la Tabla 3.2. Es decir, entre los no usuarios, 49.9% son hombres y entre los usuarios este porcentaje es 49.7%. Hasta aquí se ha reconstruido algunos resultados de la Tabla 3.2. Los demás datos pueden seguir siendo reconstruidos mediante combinaciones de las variables de usuarios de redes sociales y las variables sociodemográficas.

Gráfico de barras de dos variables

El cruce entre usuarios de Whatsapp y la variable urbano se puede ver también en un gráfico de barras agrupadas. Lo primero que haremos es definir la variable “wa_user” como factor y etiquetarla. Luego, se requiere crear una tabla con los datos agrupados. Se puede usar el dataframe “t1” creado anteriormente, pero aquí usaremos nuevamente el operador “pipe” para recrear los datos. Para esto, se agrupa tanto por uso de Whatsapp como por urbano/rural, es decir, en cuatro combinaciones. En cada subgrupo se calcula el n. Dado que los % se tienen que calcular por cada grupo de “wa_user”, se vuelve a agrupar y se calcula los % de cada subgrupo, de tal manera que los porcentajes sumen 100% en cada subgrupo de “wa_user”.

lapop18$wa_user = as.factor(lapop18$wa_user)
levels(lapop18$wa_user) <- c("No usuario", "Usuario")
face <- subset(lapop18, !is.na(wa_user)) %>% #Se usa !is.na para que no se reporte los NA en la tabla
  group_by(wa_user, urban) %>% #Se configuran los grupos
  dplyr::count() %>% #Se calcula el n
  group_by(wa_user) %>% #Se agrupa por usuario de Whatsapp
  dplyr::mutate(porcentaje = round(n/sum(n), 3)*100) #Se calcula el porcentaje en cada grupo de Whatsapp
face

Los datos se esta nueva tabla son exactamente iguales a los de “t1”. Con la tabla lista, se usa el comando ggplot definiendo que “wa_user” sea la variable en el eje X, que el eje Y sea el porcentaje y que los subgrupos se formen por la variable “urban” con la especificación fill. Se usa la especificación dodge en geom_bar para tener las barras separadas por cada grupo.

ggplot(data=face, aes(x=wa_user, y=porcentaje, fill=urban, ymax=100))+
  geom_bar(position="dodge", stat="identity")+
  geom_text(aes(label=paste(porcentaje, "%", sep="")), 
            position=position_dodge(width=0.9), vjust=-0.25)+
  ylab("Porcentaje")+
  xlab("Usuario de Whatsapp")

Si quisiéramos hacer el gráfico con barras apiladas, se tiene que cambiar la especificación de position="stack" en geom_bar y la especificación position=position_stack() de geom_text.

ggplot(data=face, aes(x=wa_user, y=porcentaje, fill=urban, ymax=100))+
  geom_bar(position="stack", stat="identity")+
  geom_text(aes(label=paste(porcentaje, "%", sep="")), 
            position=position_stack(), vjust=2.5)+
  ylab("Porcentaje")+
  xlab("Usuario de Whatsapp")

En ambos casos las barras celestes indican los porcentajes reportados en la tabla 3.2 del reporte y corresponden a la proporción de personas que viven en el ámbito urbano entre los usuarios y los no usuarios.

Resumen

En este documento se ha trabajado con variable categóricas ordinales, como la frecuencia de uso de redes sociales. También se ha introducido al uso de tablas de contingencia de dos variables categóricas y la creación de gráficos de barras agrupadas para 2 variables.

Cálculos incluyendo el efecto de diseño

Ejemplo con datos de la ronda 2021

Con los datos de la ronda 2021 del Barómetro de las Américas, hemos calculado los porcentajes de la variable que mide si los votos se han contado justamente. El gráfico que hemos creado ha sido para el total de la muestra, es decir, de todos los países. El Gráfico 2.5 del reporte El Pulso de la Democracia presenta los resultados para cada país.

Si calculáramos los porcentajes con el comando table y prop.table tendríamos resultados diferentes a los mostrados en el gráfico. Por ejemplo, en el país 5, que es Nicaragua, los resultados indican que 26% de ciudadanos de ese país indica que los votos siempre se cuentan justamente, 49% que algunas veces se cuentan justamente y 24.4% que nunca. Sin embargo, el gráfico indica que en Nicaragua, 29% indica que nunca de cuenta justamente, 45% indica que algunas veces y 25% que siempre. Estos porcentajes no corresponden a los que se encuentran con estos comandos.

round(prop.table(table(lapop21$pais, lapop21$countfair1r), 1), 3)*100
##     
##      Siempre Algunas veces Nunca
##   1                             
##   2                             
##   3                             
##   4                             
##   5     26.2          49.4  24.4
##   6     52.3          40.2   7.5
##   7     27.6          57.1  15.4
##   8     18.1          50.4  31.5
##   9     21.5          59.1  19.4
##   10    24.5          59.2  16.3
##   11    25.1          61.1  13.8
##   12    25.9          48.3  25.8
##   13    64.1          30.1   5.8
##   14    80.0          16.9   3.0
##   15    48.1          35.7  16.1
##   17    31.5          51.0  17.5
##   21    25.2          60.1  14.7
##   22                            
##   23    17.9          67.9  14.2
##   24    17.0          65.1  17.8

Esta diferencia es debido a que los comandos table y prop.table no incluyen el efecto de diseño y el factor de expansión en los cálculos. Más información sobre estas diferencias se encuentra aquí.

Para replicar los resultados del Gráfico 2.5 hay algunas opciones. La primera es mediante la librería especializada survey.

Para poder usar esta librería, primero debemos preparar la base de datos, eliminando los valores perdidos de las variables que definen el diseño. Un paso adicional es transformar las variables del dataframe. Esto es debido a que cuando se importan, el sistema lee las variables como tipo “haven_labelled”, es decir, mantiene las etiquetas de las variables, con lo que se podría producir un libro de códigos. Esto es útil en otras ocasiones, pero genera problemas con la librería survey. Para esto transformamos las variables a otro tipo con el comando sapply.

lapop21 = subset(lapop21, !is.na(weight1500))
sapply(lapop21, haven::zap_labels)

Una vez preparada la base de datos, se activa la librería y se define el diseño. En el módulo anterior, también usamos esta librería para calcular los resultados con el efecto de diseño en la ronda 2018. A diferencia de ese código, es que la ronda 2021 del Barómetro de las Américas utilizó la modalidad telefónica, y no cara a cara, por lo que ahora la unidad primaria de muestreo es el individuo, y así está definida en la variable “upm”. La variable que define los estratos es “strata” (y no “estratopri” como en la ronda 2018). La variable de ponderación sigue siendo “weight1500”.

Con estos datos calculamos guardamos el diseño en un objeto “diseno21”.

library(survey)
diseno21 = svydesign(ids = ~upm, strata = ~strata, weights = ~weight1500, nest=TRUE, data=lapop21)

La librería survey incluye comandos nativos para hacer múltiples operaciones que incluyan el efecto de diseño. Uno de esos comandos es svytable que nos permite hacer la tabla cruzada entre la variable “countfair1r” y “pais”, especificando el diseño. Este comando nos devuelve las frecuencias absolutas ponderadas, por lo que se puede anidar en el comando prop.table para calcular los porcentajes desde las frecuencias absolutas ponderadas y dentro del comando count para redondear los porcentajes, y dentro del comando as.data.table para guardar la tabla en un objeto “votoxpais” como un dataframe, que nos permita la manipulación con ggplot luego.

votoxpais = as.data.frame(round(prop.table(svytable(~pais+countfair1r, design=diseno21), 1)*100, 0))
votoxpais$pais = pais
votoxpais
ggplot(data=votoxpais, aes(fill=countfair1r, x=Freq, y=pais))+
  geom_bar(stat="identity", width=0.3)+
  geom_text(aes(label=paste(Freq, "%", sep="")), color="white", 
            position=position_stack(vjust=0.5), size=3)+
  labs(x="Porcentaje", y="País", fill="Los votos se cuentan justamente",
       caption="Barómetro de las Américas por LAPOP, 2021")

Este gráfico reproduce exactamente los resultados mostrados en el Gráfico 2.5 del reporte, aunque en un orden diferente.

Ejemplo con los datos de la ronda 2018/19

La pequeña diferencia entre los porcentajes que se muestran en el Gráfico 3.3 y los mostrados en la sección “Describir las variables” puede deberse a que en todos las tablas y gráficos anteriores no se incluye el factor de expansión. Si se incluyera, usando la variable “weight1500”, se replicarían los porcentajes mostrados en el informe. Por ejemplo, para el uso de Whatsapp, se puede usar el comando freq de la librería descr que permite incluir una variable de ponderación.

Esta tabla luego se puede guardar en un dataframe, al que se le pueden eliminar las filas y columnas que no se requieren, y se les puede cambiar el nombre a las columnas. y agregar las etiquetas.

tabla2 <- as.data.frame(descr::freq(lapop18$smedia8r, lapop18$weight1500, plot=F))
tabla2 <- tabla2[-c(5,6), -2]
colnames(tabla2) <- c("frec", "per")
tabla2$lab <- rownames(tabla2)
tabla2

Con este nuevo dataframe, se puede replicar el gráfico de Whatsapp, con los datos ponderados, usando el código de ggplot.

ggplot(data=tabla2, 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()+
  scale_fill_discrete(name="Frecuencia de uso de Facebook")+
   labs(title="Frecuencia con la que ve contenido en Facebook", 
        caption="Barómetro de las Américas por LAPOP, 2018/19")+
  xlim(0.5, 2.5)

Además del comando freq, también se puede usar la librería survey y el comando nativo svytable.

library(survey)
diseno18<-svydesign(ids = ~upm, strata = ~estratopri, weights = ~weight1500, nest=TRUE, data=lapop18)

Los resultados que se obtienen son iguales que con el método anterior y a los presentados en el reporte. Estos resultados también se pueden guardar en un “dataframe” para hacer el gráfico.

prop.table(svytable(~smedia8r, design=diseno18))*100
## smedia8r
##               Diariamente Algunas veces a la semana      Algunas veces al mes 
##                81.6626475                15.2056787                 2.8473540 
##      Algunas veces al año 
##                 0.2843197

La sección “Cruce con variables” presenta los datos de usuarios de redes sociales por país. Para construir la tabla considerando el efecto de diseño, también se puede usar el comando nativo svytable que permite calcular una tabla bivariada. De la misma manera que en caso no ponderado, las tablas parciales de cada red social se guardan en unos objetos de tipo lista, los que se juntan como un “dataframe” y se edita para presentar solo los datos de los usuarios de cada red social por país.

fbpais_2 <- round(prop.table(svytable(~pais+fb_user, design=diseno18), 1), 3)*100
twpais_2 <- round(prop.table(svytable(~pais+tw_user, design=diseno18), 1), 3)*100
wapais_2 <- round(prop.table(svytable(~pais+wa_user, design=diseno18), 1), 3)*100
tablapais_2 <- as.data.frame(cbind(fbpais_2, twpais_2, wapais_2))
tablapais_2 <- tablapais_2[, c(-1,-3,-5)]
varnames <- c("Usa Facebook", "Usa Twitter", "Usa Whatsapp")
colnames(tablapais_2) <- varnames
tablapais_2

Por último, la sección “Cruce con variables sociodemográficas” reproduce los resultados de la Tabla 3.2 del reporte. De la misma manera que en el caso anterior, se puede usar el comando nativo svytable para realizar el cruce entre las variables de uso de redes sociales y la variable urbano. Los resultados de la fila Urbano en cada red social corresponderían a la primera fila de resultados de la Tabla 3.2.

round(prop.table(svytable(~urban+wa_user, design=diseno18), 2), 3)*100
##         wa_user
## urban    No usuario Usuario
##   Rural        38.0    23.3
##   Urbano       62.0    76.7
round(prop.table(svytable(~urban+fb_user, design=diseno18), 2), 3)*100
##         fb_user
## urban    No usuario Usuario
##   Rural        35.6    23.0
##   Urbano       64.4    77.0
round(prop.table(svytable(~urban+tw_user, design=diseno18), 2), 3)*100
##         tw_user
## urban       0    1
##   Rural  29.7 15.3
##   Urbano 70.3 84.7

De esta manera se pueden calcular las tablas de distribución de frecuencias y las tablas bivariadas (o de contingencia) incluyendo el efecto de diseño o el factor de expansión.

LS0tCnRpdGxlOiAiRXN0YWTDrXN0aWNhIGRlc2NyaXB0aXZhIHVzYW5kbyBlbCBCYXLDs21ldHJvIGRlIGxhcyBBbcOpcmljYXMgKDIpIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICBjb2xsYXBzZWQ6IGZhbHNlCiAgICBudW1iZXJfc2VjdGlvbnM6IGZhbHNlCiAgICB0b2NfZGVwdGg6IDEKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgIHRoZW1lOiBmbGF0bHkKICAgIGRmX3ByaW50OiBwYWdlZAogICAgc2VsZl9jb250YWluZWQ6IG5vCiAgICBrZWVwX21kOiB5ZXMKICAgICNjb2RlX2ZvbGRpbmc6IGhpZGUKZWRpdG9yX29wdGlvbnM6IAogIG1hcmtkb3duOiAKICAgIHdyYXA6IHNlbnRlbmNlCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0UsIGNhY2hlPVRSVUUpCmBgYAoKYGBge2NzcyBjb2xvciwgZWNobz1GQUxTRX0KLmNvbHVtbnMge2Rpc3BsYXk6IGZsZXg7fQpoMSB7Y29sb3I6ICMzMzY2Q0M7fQpgYGAKCiMgSW50cm9kdWNjacOzbgoKRW4gZXN0ZSBkb2N1bWVudG8gdmFtb3MgYSBjb250aW51YXIgdXNhbmRvIGVsIMO6bHRpbW8gaW5mb3JtZSByZWdpb25hbCAiRWwgcHVsc28gZGUgbGEgZGVtb2NyYWNpYSIsIGRpc3BvbmlibGUgW2FxdcOtXShodHRwczovL3d3dy52YW5kZXJiaWx0LmVkdS9sYXBvcC9hYjIwMTgvMjAxOC0xOV9BbWVyaWNhc0Jhcm9tZXRlcl9SZWdpb25hbF9SZXBvcnRfU3BhbmlzaF9XXzAzLjI3LjIwLnBkZiksIGRvbmRlIHNlIHByZXNlbnRhbiBsb3MgcHJpbmNpcGFsZXMgaGFsbGF6Z29zIGRlIGxhIHJvbmRhIDIwMTgvMTkgZGVsIEJhcsOzbWV0cm8gZGUgbGFzIEFtw6lyaWNhcy4KVW5hIGRlIGxhcyBzZWNjaW9uZXMgZGUgZXN0ZSBpbmZvcm1lLCByZXBvcnRhIGxvcyBkYXRvcyBzb2JyZSByZWRlcyBzb2NpYWxlcyB5IGFjdGl0dWRlcyBwb2zDrXRpY2FzLgpFbiBlc3RhIHNlY2Npw7NuLCBzZSBwcmVzZW50YW4gZGF0b3Mgc29icmUgZWwgdXNvIGRlIGludGVybmV0IHkgZWwgdXNvIGRlIHJlZGVzIHNvY2lhbGVzLCBlbiBnZW5lcmFsIHkgcG9yIHBhw61zLgpFbiBlc3RlIGNhc28gdmFtb3MgYSB0cmFiYWphciBjb24gbGEgZnJlY3VlbmNpYSBkZSB1c28gZGUgbGFzIHJlZGVzIHNvY2lhbGVzLgpFbiBlc3RlIGRvY3VtZW50byB2YW1vcyBhIGFuYWxpemFyIGRlc2NyaXB0aXZhbWVudGUgZXN0YXMgdmFyaWFibGVzIHNvYnJlIGZyZWN1ZW5jaWEgZGUgdXNvIGRlIHJlZGVzIHNvY2lhbGVzLCB2YXJpYWJsZXMgZGUgdGlwbyBvcmRpbmFsIChvIGRlIGZhY3RvciwgZW4gZWwgbGVuZ3VhamUgZGUgUikuCgojIFNvYnJlIGxhIGJhc2UgZGUgZGF0b3MKCkxvcyBkYXRvcyBxdWUgdmFtb3MgYSB1c2FyIGRlYmVuIGNpdGFyc2UgZGUgbGEgc2lndWllbnRlIG1hbmVyYTogRnVlbnRlOiBCYXLDs21ldHJvIGRlIGxhcyBBbcOpcmljYXMgcG9yIGVsIFByb3llY3RvIGRlIE9waW5pw7NuIFDDumJsaWNhIGRlIEFtw6lyaWNhIExhdGluYSAoTEFQT1ApLCB3d3d3LkxhcG9wU3VydmV5cy5vcmcuCkVuIGVzdGUgZG9jdW1lbnRvIHNlIGNhcmdhIG51ZXZhbWVudGUgdW5hIGJhc2UgZGUgZGF0b3MgcmVjb3J0YWRhLgpQYXJhIHJlcHJvZHVjaXIgbG9zIHJlc3VsdGFkb3MgbW9zdHJhZG9zIGVuIGVzdGEgc2VjY2nDs24gc2UgZGViZSBsaW1waWFyIGVuIEVudmlyb25tZW50LgoKRXN0YSBiYXNlIGRlIGRhdG9zIHNlIGVuY3VlbnRyYSBhbG9qYWRhIGVuIGVsIHJlcG9zaXRvcmlvICJtYXRlcmlhbHNfZWR1IiBkZSBsYSBjdWVudGEgZGUgTEFQT1AgZW4gR2l0SHViLgpNZWRpYW50ZSBsYSBsaWJyZXLDrWEgYHJpb2AgeSBlbCBjb21hbmRvIGBpbXBvcnRgIHNlIHB1ZWRlIGltcG9ydGFyIGVzdGEgYmFzZSBkZSBkYXRvcyBkZXNkZSBlc3RlIHJlcG9zaXRvcmlvLgpBZGVtw6FzLCBzZSBzZWxlY2Npb25hbiBsb3MgZGF0b3MgZGUgcGHDrXNlcyBjb24gY8OzZGlnb3MgbWVub3JlcyBvIGlndWFsZXMgYSAzNSwgZXMgZGVjaXIsIHNlIGVsaW1pbmEgbGFzIG9ic2VydmFjaW9uZXMgZGUgRXN0YWRvcyBVbmlkb3MgeSBDYW5hZMOhLgoKYGBge3IgYmFzZX0KbGlicmFyeShyaW8pCmxhcG9wMTggPC0gaW1wb3J0KCJodHRwczovL3Jhdy5naXRodWIuY29tL2xhcG9wLWNlbnRyYWwvbWF0ZXJpYWxzX2VkdS9tYWluL0xBUE9QX0FCX01lcmdlXzIwMThfdjEuMC5zYXYiKQpsYXBvcDE4IDwtIHN1YnNldChsYXBvcDE4LCBwYWlzPD0zNSkKYGBgCgpUYW1iacOpbiBjYXJnYW1vcyBsYSBiYXNlIGRlIGRhdG9zIGRlIGxhIHJvbmRhIDIwMjEuCgpgYGB7ciBiYXNlMjF9CmxhcG9wMjEgPSBpbXBvcnQoImxhcG9wMjEuUkRhdGEiKQpsYXBvcDIxIDwtIHN1YnNldChsYXBvcDIxLCBwYWlzPD0zNSkKYGBgCgojIERlc2NyaWJpciB5IGdyYWZpY2FyIGxhcyB2YXJpYWJsZXMKCkVuIGVsIGRvY3VtZW50byBzb2JyZSBlc3RhZMOtc3RpY2EgZGVzY3JpcHRpdmEsIHF1ZSBzZSBwdWVkZSB2ZXIgW2FxdcOtXShodHRwczovL3JwdWJzLmNvbS9hcnR1cm9fbWFsZG9uYWRvLzY5Njc3MCksIHNlIHRyYWJhasOzIGNvbiB2YXJpYWJsZXMgbm9taW5hbGVzLCBjb24gb3BjaW9uZXMgZGUgcmVzcHVlc3RhIGRpY290w7NtaWNhIChTw60vTm8pLgpFbiBlc3RlIGRvY3VtZW50byBzZSB2YSBhIHRyYWJhamFyIGNvbiB2YXJpYWJsZXMgb3JkaW5hbGVzIHBvbGl0w7NtaWNhcy4KCiMgTG9zIHZvdG9zIHNvbiBjb250YWRvcyBjb3JyZWN0YW1lbnRlCgpFbiBlc3RhIHNlY2Npw7NuIHNlIHZhIGEgdXNhciBsYSB2YXJpYWJsZSBDT1VOVEZBSVIxLgpMb3Mgdm90b3Mgc29uIGNvbnRhZG9zIGNvcnJlY3RhIHkganVzdGFtZW50ZS4Kwr9EaXLDrWEgdXN0ZWQgcXVlIHN1Y2VkZSBzaWVtcHJlLCBhbGd1bmFzIHZlY2VzIG8gbnVuY2E/CkVsIGdyw6FmaWNvIDIuNSBkZWwgcmVwb3J0ZSBlbCBQdWxzbyBkZSBsYSBEZW1vY3JhY2lhLCBkaXNwb25pYmxlIFthcXXDrV0oaHR0cHM6Ly93d3cudmFuZGVyYmlsdC5lZHUvbGFwb3AvYWIyMDIxLzIwMjFfTEFQT1BfQW1lcmljYXNCYXJvbWV0ZXJfUHVsc2Vfb2ZfRGVtb2NyYWN5LnBkZiksIHByZXNlbnRhIGxvcyByZXN1bHRhZG9zIGRlIGVzdGEgdmFyaWFibGUgcG9yIHBhw61zLgoKIVtdKEZpZ3VyZTIuNS5wbmcpe3dpZHRoPSI1MTkifQoKRGUgbGEgbWlzbWEgbWFuZXJhIHF1ZSBjb24gbGFzIHZhcmlhYmxlcyBub21pbmFsZXMsIGVzdGFzIHZhcmlhYmxlcyB0aWVuZW4gcXVlIHNlciBkZWNsYXJhZGFzIGNvbW8gImZhY3RvciIgZW4gbnVldmFzIHZhcmlhYmxlcy4KCmBgYHtyIGZhY3RvcjF9CmxpYnJhcnkoaGF2ZW4pCmxhcG9wMjEkY291bnRmYWlyMXIgPSBhcy5mYWN0b3IobGFwb3AyMSRjb3VudGZhaXIxKQpgYGAKCkx1ZWdvLCBlc3RhcyB2YXJpYWJsZXMgc2UgdGllbmVuIHF1ZSBldGlxdWV0YXIgeSBnZW5lcmFyIGxhcyB0YWJsYXMgZGVzY3JpcHRpdmFzIGLDoXNpY2FzLCBjb24gZWwgY29tYW5kbyBgdGFibGVgLgoKYGBge3IgZXRpcXVldGFzMX0KbGV2ZWxzKGxhcG9wMjEkY291bnRmYWlyMXIpIDwtIGMoIlNpZW1wcmUiLCAiQWxndW5hcyB2ZWNlcyIsICJOdW5jYSIpCnRhYmxlKGxhcG9wMjEkY291bnRmYWlyMXIpCmBgYAoKUGFyYSBjYWxjdWxhciBsYXMgdGFibGFzIGNvbiBwb3JjZW50YWplcywgcmVkb25kZWFkb3MgYSB1biBkZWNpbWFsLCB1c2Ftb3MgYHByb3AudGFibGVgIHkgYHJvdW5kYC4KTnVldmFtZW50ZSwgZXN0b3MgcG9yY2VudGFqZXMgbm8gc29uIGV4YWN0YW1lbnRlIGlndWFsZXMgYSBsb3MgcHJlc2VudGFkb3MgZW4gZWwgcmVwb3J0ZSBkZWJpZG8gYSBxdWUgZXN0b3MgY8OhbGN1bG9zIG5vIGluY2x1eWVuIGVsIGZhY3RvciBkZSBleHBhbnNpw7NuLgoKYGBge3IgcG9yY2VudGFqZXMxfQpyb3VuZChwcm9wLnRhYmxlKHRhYmxlKGxhcG9wMjEkY291bnRmYWlyMXIpKSwgMykqMTAwCmBgYAoKQ29tbyBzZSBtZW5jaW9uw7MgZW4gbGEgc2VjY2nDs24gYW50ZXJpb3IsIHNlIHB1ZWRlIGdyYWZpY2FyIGVzdGEgdmFyaWFibGUgdXNhbmRvIGVsIGNvbWFuZG8gYGJhcnBsb3RgLgoKYGBge3IgYmFycmFzMX0KYmFycGxvdChwcm9wLnRhYmxlKHRhYmxlKGxhcG9wMjEkY291bnRmYWlyMXIpKSoxMDApCmBgYAoKT3RyYSBvcGNpw7NuIGVzIGVsYWJvcmFyIGVsIGdyw6FmaWNvIGRlIGJhcnJhcyB1c2FuZG8gbGEgbGlicmVyw61hIGBnZ3Bsb3RgLgpVbmEgcHJpbWVyYSBvcGNpw7NuIGVzIHRyYWJhamFyIGRpcmVjdGFtZW50ZSBkZSBsYSBiYXNlIGRlIGRhdG9zLgpFbCBzaWd1aWVudGUgY8OzZGlnbywgc2luIGVtYmFyZ28sIG11ZXN0cmEgdW5hIGdyYW4gYmFycmEgZGVsIHBvcmNlbnRhamUgZGUgY2Fzb3MgcGVyZGlkb3MuCkVzdG8gc2UgZGViZSBhIHF1ZSBlc3RhIHByZWd1bnRhIHNlIHJlYWxpesOzIGEgbGEgbWl0YWQgZGUgbGEgbXVlc3RyYS4KU2UgcmVnaXN0cmEgTkEgYSBsYSBvdHJhIG1pdGFkIGEgbGEgcXVlIG5vIHNlIGxlIGhpem8gZXN0YSBwcmVndW50YS4KCmBgYHtyIGdnYmFycmFzMWF9CmxpYnJhcnkoZ2dwbG90MikKZ2dwbG90KGRhdGE9bGFwb3AyMSwgYWVzKHg9Y291bnRmYWlyMXIpKSsKICBnZW9tX2JhcihhZXMoeT0uLnByb3AuLioxMDAsIGdyb3VwPTEpLCB3aWR0aD0wLjUpKwogIGxhYnMoeD0iwr9Mb3Mgdm90b3Mgc29uIGNvbnRhZG9zIGNvcnJlY3RhbWVudGU/IiwgeT0iUG9yY2VudGFqZSIsIAogICAgICAgY2FwdGlvbj0iQmFyw7NtZXRybyBkZSBsYXMgQW3DqXJpY2FzIHBvciBMQVBPUCwgMjAyMSIpKwogIGNvb3JkX2NhcnRlc2lhbih5bGltPWMoMCwgMTAwKSkKYGBgCgpQYXJhIGV2aXRhciBxdWUgZWwgZ3LDoWZpY28gcHJlc2VudGUgbGEgYmFycmEgZGUgTkFzLCBlc3RhcyBvYnNlcnZhY2lvbmVzIHNlIHRpZW5lbiBxdWUgZmlsdHJhciBhbnRlcyBkZSBwcm9kdWNpciBlbCBncsOhZmljby4KVGFsIGNvbW8gaW5kaWNhbW9zIGVuIGVsIG3Ds2R1bG8gYW50ZXJpb3IsIHNlIGZpbHRyYSBsb3MgTkFzIGRlIGxhIHZhcmlhYmxlICJjb3VudGZhaXIxciIgY29uIGVsIGNvbWFuZG8gYHN1YnNldGAgeSBsYSBlc3BlY2lmaWNhY2nDs24gYCFpcy5uYWAuCgpgYGB7ciBnZ2JhcnJhczFifQpnZ3Bsb3QoZGF0YT1zdWJzZXQobGFwb3AyMSwgIWlzLm5hKGNvdW50ZmFpcjFyKSksIGFlcyh4PWNvdW50ZmFpcjFyKSkrCiAgZ2VvbV9iYXIoYWVzKHk9Li5wcm9wLi4qMTAwLCBncm91cD0xKSwgd2lkdGg9MC41KSsKICBsYWJzKHg9IsK/TG9zIHZvdG9zIHNvbiBjb250YWRvcyBjb3JyZWN0YW1lbnRlPyIsIHk9IlBvcmNlbnRhamUiLCAKICAgICAgIGNhcHRpb249IkJhcsOzbWV0cm8gZGUgbGFzIEFtw6lyaWNhcyBwb3IgTEFQT1AsIDIwMjEiKSsKICBjb29yZF9jYXJ0ZXNpYW4oeWxpbT1jKDAsIDYwKSkKYGBgCgpPdHJhIG9wY2nDs24sIHF1ZSBzaW1wbGlmaWNhIGVsIGPDs2RpZ28sIGVzIGNyZWFyIHVuYSB0YWJsYSBkZSBmcmVjdWVuY2lhcyBkZSBlc3RhIHZhcmlhYmxlIGNvbiBlbCBjb21hbmRvIGB0YWJsZWAgeSBgcHJvcC50YWJsZWAuCkVzdGEgdGFibGUgc2UgcmVkb25kZWEgYSB1biBkZWNpbWFsIGNvbiBlbCBjb21hbmRvIGByb3VuZGAgeSBzZSBndWFyZGEgY29tbyB1biBkYXRhZnJhbWUgY29uIGVsIGNvbWFuZG8gYGFzLmRhdGEuZnJhbWVgIGVuIHVuIG9iamV0byAiY291bnQiLgpFc3RhIHRhYmxhIGFsbWFjZW5hIGRvcyBjb2x1bW5hcywgbGEgcHJpbWVyYSBsbGFtYWRhICJWYXIxIiBjb24gbGFzIGV0aXF1ZXRhcyBkZSBsYSB2YXJpYWJsZSB5IGxhIHNlZ3VuZGEgbGxhbWFkYSAiRnJlcSIgY29uIGxvcyBwb3JjZW50YWplcy4KCmBgYHtyIHRhYmxhMWF9CmNvdW50IDwtIGFzLmRhdGEuZnJhbWUocm91bmQocHJvcC50YWJsZSh0YWJsZShsYXBvcDIxJGNvdW50ZmFpcjFyKSksIDMpKjEwMCkKY291bnQKYGBgCgpQb2RlbW9zIHVzYXIgZXN0YSB0YWJsYSAiY291bnQiIHBhcmEgcHJvZHVjaXIgZWwgZ3LDoWZpY28gZGUgYmFycmFzIGNvbiBlbCBjb21hbmRvIGBnZ3Bsb3RgLgpFbiBsYSBlc3BlY2lmaWNhY2nDs24gYGFlc2Agc2UgZGVmaW5lIHF1ZSBsb3MgdmFsb3JlcyBkZSBsYSBjb2x1bW5hICJWYXIxIiBzZSBwcmVzZW50ZW4gZW4gZWwgZWplIFggeSBsb3MgdmFsb3JlcyBkZSBsYSBjb2x1bW5hIGEgIkZyZXEiIGVuIGVsIGVqZSBZLgpTZSBkZWZpbmUgdW4gZ3LDoWZpY28gZGUgYmFycmFzIHNpbXBsZSwgdXNhbmRvIGVsIGNvbWFuZG8gYGdlb21fYmFyKClgLCBkb25kZSBpbnRlcm5hbWVudGUgc2UgZGVmaW5lIGVsIGFuY2hvIGRlIGxhIGJhcnJhLgpDb24gbGEgZXNwZWNpZmljYWNpw7NuIGBsYWJzYCBzZSBkZWZpbmUgbGFzIGV0aXF1ZXRhcyBkZSBlamVzIHkgZWwgImNhcHRpb24iLgoKYGBge3IgZ2diYXJyYXMxY30KZ2dwbG90KGRhdGE9Y291bnQsIGFlcyh4PVZhcjEsIHk9RnJlcSkpKwogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5Iiwgd2lkdGg9MC41KSsKICBnZW9tX3RleHQoYWVzKGxhYmVsPXBhc3RlKEZyZXEsICIlIiwgc2VwPSIiKSksIGNvbG9yPSJ3aGl0ZSIsIAogICAgICAgICAgICBwb3NpdGlvbj1wb3NpdGlvbl9zdGFjayh2anVzdD0wLjUpLCBzaXplPTMpKwogIGxhYnMoeD0iTG9zIHZvdG9zIHNlIGN1ZW50YW4ganVzdGFtZW50ZSIsIHk9IlBvcmNlbnRhamUiLCAKICAgICAgIGNhcHRpb249IkJhcsOzbWV0cm8gZGUgbGFzIEFtw6lyaWNhcyBwb3IgTEFQT1AsIDIwMjEiKQpgYGAKCkVsIGdyw6FmaWNvIDIuNSBwcmVzZW50YSB1bmEgYmFycmEgYXBpbGFkYSBjb24gbG9zIGRhdG9zIHBvciBjYWRhIHBhw61zLgpQcmltZXJvIHByZXNlbnRhcmVtb3MgZWwgZ3LDoWZpY28gZGUgYmFycmFzIGFwaWxhZG8gdXNhbmRvIGxvcyBkYXRvcyBkZSB0b2RhIGxhIHJvbmRhIDIwMjEgZGVsIEJhcsOzbWV0cm8gZGUgbGFzIEFtw6lyaWNhcywgZXMgZGVjaXIgZGUgdG9kb3MgbG9zIHBhw61zZXMuClBhcmEgcHJvZHVjaXIgdW5hIGJhcnJhIGFwaWxhZGEgaG9yaXpvbnRhbCwgc2UgdXNhcsOhIGxhIHZhcmlhYmxlICJGcmVxIiBhaG9yYSBlbiBlbCBlamUgWC4KU2UgdXNhcsOhIGxhIG9wY2nDs24gYGZpbGxgIHBhcmEgZGl2aWRpciBlc3RhIGJhcnJhIHBvciBsb3MgdmFsb3JlcyBkZSBsYSB2YXJpYWJsZSAiVmFyMSIuCkNvbW8gZW4gZWwgZWplIFkgbm8gc2UgbW9zdHJhcsOhIHVuYSB2YXJpYWJsZSBzZSBkZWZpbmUgY29tbyBgIiJgLgpEZSBsYSBtaXNtYSBtYW5lcmEgcXVlIHNlIGNhbWJpYXJvbiBsYXMgdmFyaWFibGVzIGVuIGxvcyBlamVzLCB0YW1iacOpbiBzZSBjYW1iaWFuIGxhcyBldGlxdWV0YXMgZW4gYGxhYnNgLgpFbiBlc3RhIGVzcGVjaWZpY2FjacOzbiBzZSBjYW1iaWEgbGEgZXRpcXVldGEgZGUgbGEgbGV5ZW5kYSBjb24gYGZpbGxgLgoKYGBge3IgZ2diYXJyYXMxZH0KZ2dwbG90KGRhdGE9Y291bnQsIGFlcyhmaWxsPVZhcjEsIHg9RnJlcSwgeT0iIikpKwogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5Iiwgd2lkdGg9MC4zKSsKICBnZW9tX3RleHQoYWVzKGxhYmVsPXBhc3RlKEZyZXEsICIlIiwgc2VwPSIiKSksIGNvbG9yPSJ3aGl0ZSIsIAogICAgICAgICAgICBwb3NpdGlvbj1wb3NpdGlvbl9zdGFjayh2anVzdD0wLjUpLCBzaXplPTMpKwogIGxhYnMoeD0iUG9yY2VudGFqZSIsIHk9IiIsIGZpbGw9IkxvcyB2b3RvcyBzZSBjdWVudGFuIGp1c3RhbWVudGUiLAogICAgICAgY2FwdGlvbj0iQmFyw7NtZXRybyBkZSBsYXMgQW3DqXJpY2FzIHBvciBMQVBPUCwgMjAyMSIpCmBgYAoKUGFyYSByZXBsaWNhciBlbCBncsOhZmljbyBjb21wYXJhdGl2byBwb3IgcGHDrXMgc2UgcmVxdWllcmUgY3JlYXIgbGEgdGFibGEgZGUgY29udGluZ2VuY2lhIGVudHJlIGxhIHZhcmlhYmxlICJjb3VudGZhaXIiIHkgInBhaXMiLgpFc3RhIHRhYmxhIGNydXphZGEgc2UgZ3VhcmRhIGVuIHVuIG9iamV0byAiY291bnRfcGFpcyIuClNlIGRlYmUgbm90YXIgcXVlIGVsIGRhdGFmcmFtZSBxdWUgc2UgY3JlYSBjcmVhIHVuYSBmaWxhIHBvciBjYWRhIHZhbG9yIGRlICJjb3VudGZhaXIiIGVuIGNhZGEgcGHDrXMuCkRlIGVzdGEgbWFuZXJhIHRlbmVtb3MgMyBvcGNpb25lcyB4IDIwIHBhw61zZXMgPSA2MCBmaWxhcy4KCmBgYHtyIHRhYmxhMWJ9CmNvdW50X3BhaXMgPSBhcy5kYXRhLmZyYW1lKHJvdW5kKHByb3AudGFibGUodGFibGUobGFwb3AyMSRwYWlzLCBsYXBvcDIxJGNvdW50ZmFpcjFyKSwgMSksIDMpKjEwMCkKY291bnRfcGFpcwpgYGAKCkVuIGVzdGEgdGFibGEgc2UgY2FsY3VsYW4gbG9zIGRhdG9zIHBvciBjYWRhIHZhbG9yIGRlIGxhIHZhcmlhYmxlICJwYWlzIiwgaW5jbHVzbyBjdWFuZG8gbm8gc2UgdGllbmUgZGF0b3MgZGUgbGEgdmFyaWFibGUgImNvdW50ZmFpciIsIGRlYmlkbyBhIHF1ZSBsYSBwcmVndW50YSBubyBzZSByZWFsaXrDsyBlbiBlc2UgcGHDrXMuClBvciBlc3RlIG1vdGl2byBzZSB0aWVuZW4gcXVlIGVsaW1pbmFyIGxhcyBmaWxhcyBkZSBsb3MgcGHDrXNlcyBlbiBsb3MgcXVlIG5vIHNlIHJlY29nacOzIGVzdGEgaW5mb3JtYWNpw7NuLgpFc3RvIHNlIGhhY2UgY29uIGxhIGVzcGVjaWZpY2FjacOzbiBgWy1jKGZpbGFzKSxdYC4KTHVlZ28gc2UgY3JlYSB1biB2ZWN0b3IgY29uIGxvcyBub21icmVzIGRlIGxvcyBwYcOtc2VzLgpFc3RhIGxpc3RhIHNlIHJlcGl0ZSAzIHZlY2VzICgxNSBwYcOtc2VzIHJlc3RhbnRlcyB4IDMgb3BjaW9uZXMpLgpFc3RlIHZlY3RvciBzZSBhZ3JlZ2EgYWwgZGF0YWZyYW1lIGVuIHVuYSBjb2x1bW5hICJwYWlzIi4KCmBgYHtyIHRhYmxhMWN9CmNvdW50X3BhaXMgPSBjb3VudF9wYWlzWy1jKDE6NCwxOCwyMToyNCwzOCw0MTo0NCw1OCksXQpwYWlzID0gYygiTmljYXJhZ3VhIiwiQ29zdGEgUmljYSIsICJQYW5hbcOhIiwgIkNvbG9tYmlhIiwgIkVjdWFkb3IiLCAiQm9saXZpYSIsICJQZXLDuiIsCiAgICAgICAgIlBhcmFndWF5IiwgIkNoaWxlIiwgIlVydWd1YXkiLCAiQnJhc2lsIiwgIkFyZ2VudGluYSIsICJSZXAuIERvbS4iLCJKYW1haWNhIiwgIkd1eWFuYSIsICJOaWNhcmFndWEiLCJDb3N0YSBSaWNhIiwgIlBhbmFtw6EiLCAiQ29sb21iaWEiLCAiRWN1YWRvciIsICJCb2xpdmlhIiwgIlBlcsO6IiwKICAgICAgICAiUGFyYWd1YXkiLCAiQ2hpbGUiLCAiVXJ1Z3VheSIsICJCcmFzaWwiLCAiQXJnZW50aW5hIiwgIlJlcC4gRG9tLiIsIkphbWFpY2EiLCAiR3V5YW5hIiwiTmljYXJhZ3VhIiwiQ29zdGEgUmljYSIsICJQYW5hbcOhIiwgIkNvbG9tYmlhIiwgIkVjdWFkb3IiLCAiQm9saXZpYSIsICJQZXLDuiIsCiAgICAgICAgIlBhcmFndWF5IiwgIkNoaWxlIiwgIlVydWd1YXkiLCAiQnJhc2lsIiwgIkFyZ2VudGluYSIsICJSZXAuIERvbS4iLCJKYW1haWNhIiwgIkd1eWFuYSIpCmNvdW50X3BhaXMkcGFpcyA9IHBhaXMKY291bnRfcGFpcwpgYGAKCkNvbiBlc3RlIGRhdGFmcmFtZSAiY291bnRfcGFpcyIgeWEgdGVuZW1vcyBsb3MgZWxlbWVudG9zIHBhcmEgcmVwbGljYXIgZWwgZ3LDoWZpY28gZGUgYmFycmFzIGFwaWxhZGFzLgpFbiBsYSBlc3BlY2lmaWNhY2nDs24gYGFlc2Agc2UgZGVmaW5lIHF1ZSBlbiBlbCBlamUgWCBzZSBncmFmaXF1ZW4gbG9zIHBvcmNlbnRhamVzLCBlbiBlbCBlamUgWSBsb3MgcGHDrXNlcyB5IGNhZGEgYmFycmEgc2UgZGl2aWRhIHBvciBsYSBjb2x1bW5hIFZhcjIuCgpgYGB7ciBnZ2JhcnJhczFlfQpnZ3Bsb3QoZGF0YT1jb3VudF9wYWlzLCBhZXMoeD1GcmVxLCB5PXBhaXMsIGZpbGw9VmFyMikpKwogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5Iiwgd2lkdGg9MC4zKSsKICBnZW9tX3RleHQoYWVzKGxhYmVsPXBhc3RlKEZyZXEsICIlIiwgc2VwPSIiKSksIGNvbG9yPSJ3aGl0ZSIsIAogICAgICAgICAgICBwb3NpdGlvbj1wb3NpdGlvbl9zdGFjayh2anVzdD0wLjUpLCBzaXplPTIpKwogIGxhYnMoeD0iUG9yY2VudGFqZSIsIHk9IlBhw61zIiwgZmlsbD0iTG9zIHZvdG9zIHNlIGN1ZW50YW4ganVzdGFtZW50ZSIsCiAgICAgICBjYXB0aW9uPSJCYXLDs21ldHJvIGRlIGxhcyBBbcOpcmljYXMgcG9yIExBUE9QLCAyMDIxIikKYGBgCgojIEZyZWN1ZW5jaWEgZGUgdXNvIGRlIHJlZGVzIHNvY2lhbGVzCgpFbiBsYSByb25kYSAyMDE4LzE5IHNlIGV2YWx1w7MgZWwgdXNvIGRlIHJlZGVzIHNvY2lhbGVzLgpEZSBlc3RhIG1hbmVyYSwgc2UgYW5hbGl6YXJvbiBsYXMgdmFyaWFibGVzIFNNRURJQTIuCsK/Q29uIHF1w6kgZnJlY3VlbmNpYSB2ZSBjb250ZW5pZG8gZW4gRmFjZWJvb2s/LApTTUVESUE1LsK/Q29uIHF1w6kgZnJlY3VlbmNpYSB2ZSBjb250ZW5pZG8gZW4gVHdpdHRlcj8KeSBTTUVESUE4LsK/Q29uIHF1w6kgZnJlY3VlbmNpYSB1c2EgV2hhdHNhcHA/CkVzdGFzIHZhcmlhYmxlcyB0aWVuZW4gY29tbyBvcGNpb25lcyBkZSByZXNwdWVzdGE6CgoxLiAgRGlhcmlhbWVudGUKMi4gIEFsZ3VuYXMgdmVjZXMgYSBsYSBzZW1hbmEKMy4gIEFsZ3VuYXMgdmVjZXMgYWwgbWVzCjQuICBBbGd1bmFzIHZlY2VzIGFsIGHDsW8KNS4gIE51bmNhCgpEZSBsYSBtaXNtYSBtYW5lcmEgcXVlIGNvbiBsYXMgdmFyaWFibGVzIG5vbWluYWxlcywgZXN0YXMgdmFyaWFibGVzIHRpZW5lbiBxdWUgc2VyIGRlY2xhcmFkYXMgY29tbyAiZmFjdG9yIiBlbiBudWV2YXMgdmFyaWFibGVzLgoKYGBge3IgZmFjdG9yMn0KbGFwb3AxOCRzbWVkaWEyciA9IGFzLmZhY3RvcihsYXBvcDE4JHNtZWRpYTIpCmxhcG9wMTgkc21lZGlhNXIgPSBhcy5mYWN0b3IobGFwb3AxOCRzbWVkaWE1KQpsYXBvcDE4JHNtZWRpYThyID0gYXMuZmFjdG9yKGxhcG9wMTgkc21lZGlhOCkKYGBgCgpMYSBub3RhIGEgcGllIDE1IGRlbCByZXBvcnRlIGluZGljYSBxdWUgImxvcyBlbnRyZXZpc3RhZG9zIHF1ZSByZXBvcnRhbiBudW5jYSB2ZXIgY29udGVuaWRvIGVuIEZhY2Vib29rIHkgVHdpdHRlciwgeSBxdWllbmVzIGluZGljYW4gcXVlIG51bmNhIHVzYW4gV2hhdHNhcHAsIHNvbiBjb25zaWRlcmFkb3MgY29tbyBubyB1c3VhcmlvcyBkZSBlc3RhcyByZWRlcyBzb2NpYWxlcyIgKHAuIDY0KS4KUG9yIGVzdGUgbW90aXZvLCBlbCBncsOhZmljbyAzLjMgZGUgZnJlY3VlbmNpYSBkZSB1c28gZGUgcmVkZXMgc29jaWFsZXMgKHBhZy4gNTcpIHNvbG8gaW5jbHV5ZW4gbGFzIGNhdGVnb3LDrWFzICJEaWFyaWFtZW50ZSIsICJBbGd1bmFzIHZlY2VzIGEgbGEgc2VtYW5hIiwgIkFsZ3VuYXMgdmVjZXMgYWwgbWVzIiB5ICJBbGd1bmFzIHZlY2VzIGFsIGHDsW8iLgpTZSBleGNsdXlhIGxhIGNhdGVnb3LDrWEgIk51bmNhIi4KCiFbXShHcmFmMy4zLnBuZyl7d2lkdGg9IjM5OCJ9CgpFbiBsYXMgbnVldmFzIHZhcmlhYmxlcyB2YW1vcyBhIGRlY2xhcmFyIGVsIHZhbG9yIDUsIGNvcnJlc3BvbmRpZW50ZSBhICJudW5jYSIsIGNvbW8gIk5BIiwgZXMgZGVjaXIsIGNvbW8gdmFsb3IgcGVyZGlkbyBlbiBSLgoKYGBge3IgZWxpbWluYXJ9CmxpYnJhcnkoY2FyKQpsYXBvcDE4JHNtZWRpYTJyID0gY2FyOjpyZWNvZGUobGFwb3AxOCRzbWVkaWEyciwgIjU9TkEiKQpsYXBvcDE4JHNtZWRpYTVyID0gY2FyOjpyZWNvZGUobGFwb3AxOCRzbWVkaWE1ciwgIjU9TkEiKQpsYXBvcDE4JHNtZWRpYThyID0gY2FyOjpyZWNvZGUobGFwb3AxOCRzbWVkaWE4ciwgIjU9TkEiKQpgYGAKCkx1ZWdvLCBlc3RhcyB2YXJpYWJsZXMgc2UgdGllbmVuIHF1ZSBldGlxdWV0YXIgeSBnZW5lcmFyIGxhcyB0YWJsYXMgZGVzY3JpcHRpdmFzIGLDoXNpY2FzLCBjb24gZWwgY29tYW5kbyBgdGFibGVgLgoKYGBge3IgZXRpcXVldGEyfQpsZXZlbHMobGFwb3AxOCRzbWVkaWEycikgPC0gYygiRGlhcmlhbWVudGUiLCAiQWxndW5hcyB2ZWNlcyBhIGxhIHNlbWFuYSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIkFsZ3VuYXMgdmVjZXMgYWwgbWVzIiwgIkFsZ3VuYXMgdmVjZXMgYWwgYcOxbyIpCmxldmVscyhsYXBvcDE4JHNtZWRpYTVyKSA8LSBjKCJEaWFyaWFtZW50ZSIsICJBbGd1bmFzIHZlY2VzIGEgbGEgc2VtYW5hIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQWxndW5hcyB2ZWNlcyBhbCBtZXMiLCAiQWxndW5hcyB2ZWNlcyBhbCBhw7FvIikKbGV2ZWxzKGxhcG9wMTgkc21lZGlhOHIpIDwtIGMoIkRpYXJpYW1lbnRlIiwgIkFsZ3VuYXMgdmVjZXMgYSBsYSBzZW1hbmEiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICJBbGd1bmFzIHZlY2VzIGFsIG1lcyIsICJBbGd1bmFzIHZlY2VzIGFsIGHDsW8iKQp0YWJsZShsYXBvcDE4JHNtZWRpYTJyKQp0YWJsZShsYXBvcDE4JHNtZWRpYTVyKQp0YWJsZShsYXBvcDE4JHNtZWRpYThyKQpgYGAKClBhcmEgY2FsY3VsYXIgbGFzIHRhYmxhcyBjb24gcG9yY2VudGFqZXMsIHJlZG9uZGVhZG9zIGEgdW4gZGVjaW1hbCwgdXNhbW9zIGBwcm9wLnRhYmxlYCB5IGByb3VuZGAuCk51ZXZhbWVudGUsIGVzdG9zIHBvcmNlbnRhamVzIG5vIHNvbiBleGFjdGFtZW50ZSBpZ3VhbGVzIGEgbG9zIHByZXNlbnRhZG9zIGVuIGVsIHJlcG9ydGUgZGViaWRvIGEgcXVlIGVzdG9zIGPDoWxjdWxvcyBubyBpbmNsdXllbiBlbCBmYWN0b3IgZGUgZXhwYW5zacOzbi4KCmBgYHtyIHBvcmNlbnRhamVzMn0Kcm91bmQocHJvcC50YWJsZSh0YWJsZShsYXBvcDE4JHNtZWRpYTJyKSksIDMpKjEwMApyb3VuZChwcm9wLnRhYmxlKHRhYmxlKGxhcG9wMTgkc21lZGlhNXIpKSwgMykqMTAwCnJvdW5kKHByb3AudGFibGUodGFibGUobGFwb3AxOCRzbWVkaWE4cikpLCAzKSoxMDAKYGBgCgpQYXJhIHByZXNlbnRhciB0b2RvcyBsb3MgZGF0b3MgZW4gdW5hIHRhYmxhIGNvbmp1bnRhLCBzZSBndWFyZGEgbGEgdGFibGEgZGUgY2FkYSByZWQgc29jaWFsIGVuIHVuIG51ZXZvIG9iamV0byBkZSBSIChsbGFtYWRvIGNvbW8gY2FkYSByZWQgc29jaWFsKS4KTHVlZ28sIHRvZGFzIGVzdGFzIHRhYmxhcyBwYXJjaWFsZXMsIHF1ZSBjb250aWVuZW4gbGFzIG1pc21hcyBvcGNpb25lcyBkZSByZXNwdWVzdGEsIHNlIHVuZW4gY29tbyBmaWxhcyBjb24gZWwgY29tYW5kbyBgcmJpbmRgLgpFc3RhIG51ZXZhIHRhYmxhIGNvbmp1bnRhIHNlIGd1YXJkYSBjb21vIHVuIG51ZXZvIGRhdGFmcmFtZSAidGFibGEiLgoKYGBge3IgdGFibGEyYX0KRmFjZWJvb2sgPC0gcm91bmQocHJvcC50YWJsZSh0YWJsZShsYXBvcDE4JHNtZWRpYTJyKSksIDMpKjEwMApUd2l0dGVyIDwtIHJvdW5kKHByb3AudGFibGUodGFibGUobGFwb3AxOCRzbWVkaWE1cikpLCAzKSoxMDAKV2hhdHNhcHAgPC0gcm91bmQocHJvcC50YWJsZSh0YWJsZShsYXBvcDE4JHNtZWRpYThyKSksIDMpKjEwMAp0YWJsYSA8LSBhcy5kYXRhLmZyYW1lKHJiaW5kKEZhY2Vib29rLCBUd2l0dGVyLCBXaGF0c2FwcCkpCnRhYmxhCmBgYAoKUGFyYSB0ZW5lciB1bmEgbWVqb3IgcHJlc2VudGFjacOzbiBkZSBsYSB0YWJsYSwgc2UgcHVlZGUgdXNhciBlbCBjb21hbmRvIGBrYWJsZWAgZGVsIHBhcXVldGUgYGtuaXRyYCBvIGVsIGNvbWFuZG8gYGZvcm1hdHRhYmxlYCBkZWwgcGFxdWV0ZSBkZWwgbWlzbW8gbm9tYnJlLgoKYGBge3IgdGFibGEgbWVqb3JhZGF9CmxpYnJhcnkoa25pdHIpCmthYmxlKGhlYWQodGFibGEpLCBmb3JtYXQ9Im1hcmtkb3duIiwgZGlnaXRzPTEpCmxpYnJhcnkoZm9ybWF0dGFibGUpCmZvcm1hdHRhYmxlKHRhYmxhKQpgYGAKClBhcmEgZ3JhZmljYXIgZXN0YSB2YXJpYWJsZSBubyB2YW1vcyBhIHNlZ3VpciBlbCBtaXNtbyBwcm9jZWRpbWllbnRvIHF1ZSBlbiBlbCBtw7NkdWxvIGFudGVyaW9yLgpFbiBlc3RlIG3Ds2R1bG8gdmFtb3MgYSB0cmFiYWphciBkaXJlY3RhbWVudGUgZGVzZGUgbGEgdGFibGEgY3JlYWRhIGNvbiBsb3MgcG9yY2VudGFqZXMgZGUgbGFzIHRyZXMgcmVkZXMgc29jaWFsZXMuCkVzdGEgdGFibGEgdGllbmUgYSBjYWRhIHJlZCBzb2NpYWwgZW4gbGFzIGZpbGFzIHkgbGFzIG9wY2lvbmVzIGRlIHJlc3B1ZXN0YSBlbiBsYXMgY29sdW1uYXMuClBhcmEgcG9kZXIgZ3JhZmljYXIgZXN0b3MgZGF0b3Mgc2UgcmVxdWllcmUgcXVlIGxhcyByZWRlcyBzb2NpYWxlcyBlc3TDqW4gZW4gbGFzIGNvbHVtbmFzIHkgbGFzIG9wY2lvbmVzIGRlIHJlc3B1ZXN0YSBlbiBsYXMgZmlsYXMsIHBvciBsbyBxdWUgZW4gcHJpbWVyIGx1Z2FyIHZhbW9zIGEgdHJhbnNwb25lciBlc3RhIHRhYmxhLgpFc3RlIHByb2NlZGltaWVudG8gbG8gaGFjZW1vcyB1c2FuZG8gbGEgbGlicmVyw61hIGBkYXRhLnRhYmxlYCB5IGVsIGNvbWFuZG8gYHRyYW5zcG9zZWAuCkVzdG9zIGRhdG9zIHRyYW5zcHVlc3RvcyBsb3MgZ3VhcmRhbW9zIGVuIHVuYSBudWV2YSB0YWJsYSAidGFibGFfdHIiLgoKRXN0ZSBjb21hbmRvIHRyYW5zcG9uZSBsb3MgZGF0b3MsIHBlcm8gZGVqYSBzaW4gbm9tYnJhciBsYXMgZmlsYXMgeSBjb2x1bW5hcy4KUHJpbWVybyBzZSBub21icmFuIGxhcyBjb2x1bW5hcyBjb24gZWwgY29tYW5kbyBgY29sbmFtZXNgIHVzYW5kbyBsb3Mgbm9tYnJlcyBkZSBsYXMgZmlsYXMgZGUgInRhYmxhIi4KTHVlZ28gc2Ugbm9tYnJhbiBsYXMgZmlsYXMgY29uIGVsIGNvbWFuZG8gYHJvd25hbWVzYCB1c2FuZG8gbG9zIG5vbWJyZXMgZGUgbGFzIGNvbHVtbmFzIGRlICJ0YWJsYSIuCgpGaW5hbG1lbnRlLCByZXF1ZXJpbW9zIHVuYSBjb2x1bW5hIGRlbnRybyBkZSAidGFibGFfdHIiIHF1ZSBpbmNsdXlhIGxhcyBldGlxdWV0YXMgZGUgcmVzcHVlc3RhIGRlIGxhcyBwcmVndW50YXMgZGUgc21lZGlhLgpFc3RhcyBldGlxdWV0YXMgZXN0w6FuIGNvbW8gbm9tYnJlcyBkZSBmaWxhcy4KUGFyYSBpbmNsdWlybGFzIGNvbW8gdW5hIHZhcmlhYmxlIG3DoXMsIHNlIGFncmVnYSB1bmEgdmFyaWFibGUgInRhYmxhX3RyXCRsYWIiIGFsIHF1ZSBzZSBsZSBhc2lnbmEgbG9zIG5vbWJyZXMgZGUgbGFzIGZpbGFzIGNvbiBgcm93Lm5hbWVzYC4KCmBgYHtyIHRhYmxhIHRyYW5zcHVlc3RhLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KGRhdGEudGFibGUpCnRhYmxhX3RyID0gZGF0YS5mcmFtZSh0KHRhYmxhW10pKQpjb2xuYW1lcyh0YWJsYV90cikgPSByb3duYW1lcyh0YWJsYSkKcm93bmFtZXModGFibGFfdHIpID0gY29sbmFtZXModGFibGEpCnRhYmxhX3RyJGxhYiA8LSByb3duYW1lcyh0YWJsYV90cikKdGFibGFfdHIKYGBgCgpFbiBzZWd1bmRvIGx1Z2FyLCBzZSBhY3RpdmEgbGEgbGlicmVyw61hIGBnZ3Bsb3QyYCBwYXJhIGdyYWZpY2FyIGxvcyBkYXRvcyBndWFyZGFkb3MgZW4gInRhYmxhX3RyIiBjb24gZWwgY29tYW5kbyBgZ2dwbG90YC4KRGVudHJvIGRlIGVzdGUgY29tYW5kbyBzZSBlc3BlY2lmaWNhIHF1ZSBzZSB0cmFiYWphcsOhIGNvbiBlbCBkYXRhZnJhbWUgInRhYmxhX3RyIiAobm8gY29uIGxhcG9wMTgpIHkgc2UgZXNwZWNpZmljYSBsYSAiZXN0w6l0aWNhIiwgZXMgZGVjaXIgcXVlIGVuIGVsIGVqZSBYIG5vIHNlIGluY2x1aXLDoSB1bmEgdmFyaWFibGUsIHF1ZSBlbiBlbCBlamUgWSBzZSBpbmNsdWlyw6EgbG9zIGRhdG9zIGRlICJGYWNlYm9vayIgZGVsIGRhdGFmcmFtZSAidGFibGFfdHIiIHkgcXVlIHNlIGRpdmlkaXLDoSBwb3IgY2F0ZWdvcsOtYXMgZGUgbGEgdmFyaWFibGUgZ3VhcmRhZGFzIGVuICJsYWIiLgpMdWVnbyBzZSBhw7FhZGUgbGEgY2FwYSBgZ2VvbV9iYXJgIHBhcmEgaW5kaWNhciBxdWUgc2UgZ3JhZmljYXLDoSBlbiBiYXJyYXMgZGUgYW5jaG8gKGB3aWR0aGApIDEgeSBxdWUgbGEgYmFycmEgcmVwbGljYXLDoSBsbyBlc3BlY2lmaWNhZG8gZW4gbGEgZXN0w6l0aWNhIChgc3RhdD0iaWRlbnRpdHkiYCkuCkNvbiBgZ2VvbV90ZXh0YCBzZSBhZ3JlZ2EgbGFzIGV0aXF1ZXRhcyBkZSBsb3MgZGF0b3MsIGNvbiBlbCBzw61tYm9sbyBkZSBwb3JjZW50YWplLCB5IHNlIGVzcGVjaWZpY2EgbGEgcG9zaWNpw7NuIGRlbCB0ZXh0byBjb24gYHBvc2l0aW9uPXBvc2l0aW9uX3N0YWNrKOKApilgIHkgZWwgdGFtYcOxbyBjb24gYHNpemU9M2AuCkNvbiBgY29vcmRfcG9sYXJgIHNlIHRyYW5zZm9ybWEgbGFzIGJhcnJhcyBlbiB1biBncsOhZmljbyBjaXJjdWxhciBkZSBzZWN0b3Jlcy4KRmluYWxtZW50ZSwgc2UgZGVmaW5lIGVsICJ0ZW1hIiBjb24gYHRoZW1lX3ZvaWRgIGluZGljYW5kbyB1biBmb25kbyBibGFuY28geSBzZSBtb2RpZmljYSBsYSBldGlxdWV0YSBkZSBsYSBsZXllbmRhIGNvbiBgc2NhbGVfZmlsbF9kaXNjcmV0ZWAuCgpgYGB7ciBncmFmaWNvIHBpZX0KbGlicmFyeShnZ3Bsb3QyKQpnZ3Bsb3QoZGF0YT10YWJsYV90ciwgYWVzKHg9IiIsIHk9RmFjZWJvb2ssIGZpbGw9bGFiKSkrCiAgZ2VvbV9iYXIod2lkdGg9MSwgc3RhdD0iaWRlbnRpdHkiKSsKICBnZW9tX3RleHQoYWVzKGxhYmVsPXBhc3RlKEZhY2Vib29rLCAiJSIsIHNlcD0iIikpLCBjb2xvcj0id2hpdGUiLCAKICAgICAgICAgICAgcG9zaXRpb249cG9zaXRpb25fc3RhY2sodmp1c3Q9MC41KSwgc2l6ZT0zKSsKICBjb29yZF9wb2xhcigieSIsIHN0YXJ0PTApKwogIHRoZW1lX3ZvaWQoKSsKICBzY2FsZV9maWxsX2Rpc2NyZXRlKG5hbWU9IkZyZWN1ZW5jaWEgZGUgdXNvIGRlIEZhY2Vib29rIikKYGBgCgpFbiBlbCBncsOhZmljbyAzLjMgZGVsIHJlcG9ydGUgIkVsIFB1bHNvIGRlIGxhIERlbW9jcmFjaWEiIHNlIHByZXNlbnRhIHVuIGdyw6FmaWNvIGNpcmN1bGFyIHRpcG8gImRvbm5hIi4KUGFyYSByZXByb2R1Y2lyIGV4YWN0YW1lbnRlIGVzdGUgdGlwbyBkZSBncsOhZmljbywgc2UgdGllbmUgcXVlIGFjb21vZGFyIHVub3MgZGV0YWxsZXMgZGUgbGEgc2ludGF4aXMgYW50ZXJpb3IuClNlIGVzdGFibGVjZSAieD0yIiBlbiBsYSAiZXN0w6l0aWNhIiB5IHNlIGVzdGFibGVjZSBsw61taXRlcyBlbiBlbCBlamUgWCwgZW50cmUgMC41IHkgMi41LCBwYXJhIHF1ZSBjdWFuZG8gc2Ugcm90ZSBlbCBlamUsIHNlIGNyZWUgZWwgImh1ZWNvIiBkZW50cm8gZGVsIGPDrXJjdWxvLgoKYGBge3IgZG9uYX0KZ2dwbG90KGRhdGE9dGFibGFfdHIsIGFlcyh4PTIsIHk9RmFjZWJvb2ssIGZpbGw9bGFiKSkrCiAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiKSsKICBnZW9tX3RleHQoYWVzKGxhYmVsPXBhc3RlKEZhY2Vib29rLCAiJSIsIHNlcD0iIikpLCBjb2xvcj0id2hpdGUiLCAKICAgICAgICAgICAgcG9zaXRpb249cG9zaXRpb25fc3RhY2sodmp1c3Q9MC41KSwgc2l6ZT0zKSsKICBjb29yZF9wb2xhcigieSIpKwogIHRoZW1lX3ZvaWQoKSsKICBzY2FsZV9maWxsX2Rpc2NyZXRlKG5hbWU9IkZyZWN1ZW5jaWEgZGUgdXNvIGRlIEZhY2Vib29rIikrCiAgIGxhYnModGl0bGU9IkZyZWN1ZW5jaWEgY29uIGxhIHF1ZSB2ZSBjb250ZW5pZG8gZW4gRmFjZWJib2siLCAKICAgICAgICBjYXB0aW9uPSJCYXLDs21ldHJvIGRlIGxhcyBBbcOpcmljYXMgcG9yIExBUE9QLCAyMDE4LzE5IikrCiAgeGxpbSgwLjUsIDIuNSkKYGBgCgpQYXJhIHJlcGxpY2FyIGVsIGdyw6FmaWNvIGRlIGJhcnJhcywgc2UgaGFjZW4gYWxndW5hcyBtb2RpZmljYWNpb25lcyBlbiBlbCBjw7NkaWdvIGFudGVyaW9yLgpQb3IgZWplbXBsbywgZW4gbGEgImVzdMOpdGljYSIsIGFob3JhIHNlIGluZGljYSBxdWUgZW4gZWwgZWplIFggdmEgbGEgdmFyaWFibGUgInNtZWRpYTJyIiB5IGVuIGVsIGVqZSBZIGVsIHBvcmNlbnRhamUgInBlciIuClNlIGVsaW1pbmEsIGFkZW3DoXMsIGxhIGVzcGVjaWZpY2FjacOzbiBkZSBsYXMgY29vcmRlbmFkYXMgcG9sYXJlcy4KU2UgYWdyZWdhIHVuIHTDrXR1bG8gYWwgZ3LDoWZpY28sIGV0aXF1ZXRhcyBhIGxvcyBlamVzIHkgdW4gY2FwdGlvbiBjb24gbGEgZXNwZWNpZmljYWNpw7NuIGBsYWJzYC4KRmluYWxtZW50ZSwgc2UgZGVmaW5lIGVsIGVqZSBZIGVudHJlIDAgeSA2MCBjb24gbGEgZXNwZWNpZmljYWNpw7NuIGBjb29yZF9jYXJ0ZXNpYW5gLgpVbiB0ZW1hIGltcG9ydGFudGUgZXMgcXVlIGVzdGUgZ3LDoWZpY28gdGFtYmnDqW4gc2UgcHVlZGUgZ3VhcmRhciBlbiB1biBvYmpldG8gZW4gUiwgcXVlIGxsYW1hcmVtb3MgImdyYWYxIi4KCmBgYHtyIGdyYWZpY28gZGUgYmFycmFzfQpncmFmMSA8LSBnZ3Bsb3QodGFibGFfdHIsIGFlcyh4PWxhYiwgeT1GYWNlYm9vaykpKwogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IiwgIHdpZHRoPTAuNSkrCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbD1wYXN0ZShGYWNlYm9vaywgIiUiLCBzZXA9IiIpKSwgY29sb3I9ImJsYWNrIiwgdmp1c3Q9LTAuNSkrCiAgbGFicyh0aXRsZT0iRnJlY3VlbmNpYSBkZSB1c28gZGUgcmVkZXMgc29jaWFsZXMiLCB4PSJGcmVjdWVuY2lhIGRlIHVzbyBkZSBGYWNlYm9vayIsIHk9IlBvcmNlbnRhamUiLCBjYXB0aW9uPSJCYXLDs21ldHJvIGRlIGxhcyBBbcOpcmljYXMgcG9yIExBUE9QLCAyMDE4LzE5IikrCiAgY29vcmRfY2FydGVzaWFuKHlsaW09YygwLCA2MCkpCmdyYWYxCmBgYAoKR3VhcmRhciB1biBncsOhZmljbyBlbiB1biBvYmpldG8gbm9zIHBlcm1pdGUgbHVlZ28gaW5jb3Jwb3JhciBtw6FzIGNhcGFzIG8gbWVqb3JhcyBlbiBlbCBncsOhZmljby4KRWwgZ3LDoWZpY28gYW50ZXJpb3IsIHBvciBlamVtcGxvLCB0aWVuZSBsYXMgZXRpcXVldGFzIGRlICJGcmVjdWVuY2lhIGRlIHVzbyIgZW4gdW5hIHNvbGEgbMOtbmVhLCBwb3IgbG8gc2UgcG9kcsOtYW4gdHJhc3BvbmVyLgpQYXJhIG1lam9yYXIgZXN0YSB2aXN1YWxpemFjacOzbiBzZSBwdWVkZSBpbmNsaW5hciBlc3RhcyBldGlxdWV0YXMsIHBlcm8gc2kgc2UgcXVpc2llcmEgbWFudGVuZXIgbGEgaG9yaXpvbnRhbGlkYWQgcGFyYSBmYWNpbGl0YXIgbGEgbGVjdHVyYSwgbG8gcXVlIHNlIHB1ZWRlIGhhY2VyIGVzIHNlcGFyYXIgbGFzIGV0aXF1ZXRhcyBsYXJnYXMgZW4gZG9zIG8gbcOhcyBsw61uZWFzLgpFc3RvIHNlIHB1ZWRlIGhhY2VyLCBwb3IgZWplbXBsbywgY3JlYW5kbyB1biB2ZWN0b3IgY29uIGxhcyBudWV2YXMgZXRpcXVldGFzLgpMYXMgZXRpcXVldGFzIGxhcmdhcyBzZSBwdWVkZW4gcGFydGlyIGVuIGzDrW5lYXMgbWVkaWFudGUgbGEgaW5jbHVzacOzbiBkZSBgXG5gIGVudHJlIGxvcyB0ZXh0b3MgcXVlIHNlIHF1aWVyYSBzZXBhcmFyLgpMdWVnbywgYWwgb2JqZXRvICJncmFmMSIgc2UgbGUgcHVlZGUgYWdyZWdhciB1bmEgbnVldmEgY2FwYSBwYXJhIHJlZW1wbGF6YXIgbGFzIGV0aXF1ZXRhcyBjb24gbGFzIG51ZXZhcyBkaXZpZGlkYXMgZW4gbMOtbmVhcy4KRXN0byBzZSBoYWNlIGNvbiBsYSBlc3BlY2lmaWNhY2nDs24gYHNjYWxlX3hfZGlzY3JldGVgIGRvbmRlIHNlIGluZGljYSBxdWUgbGFzIGV0aXF1ZXRhcyB1c2VuIGVsIHZlY3RvciBjcmVhZG8uCgpgYGB7ciBldGlxdWV0YXMgc3VwZXJwdWVzdGFzfQpldGlxIDwtIGMoIkRpYXJpYW1lbnRlIiwgIkFsZ3VuYXMgdmVjZXNcbmEgbGEgc2VtYW5hIiwgIkFsZ3VuYXMgdmVjZXNcbmFsIG1lcyIsIAogICAgICAgICAgIkFsZ3VuYXMgdmVjZXNcbmFsIGHDsW8iKQpncmFmMSArCiAgc2NhbGVfeF9kaXNjcmV0ZShsYWJlbHM9ZXRpcSkKYGBgCgpEZSBlc3RhIG1hbmVyYSBlbCBncsOhZmljbyBxdWVkYSBtw6FzIGxlZ2libGUuCkhhc3RhIGFxdcOtIHNlIGhhIHJlcGxpY2FkbyBsYXMgdGFibGFzIHkgZ3LDoWZpY29zIHF1ZSBzZSB1c2Fyb24gY29uIGxhcyB2YXJpYWJsZXMgbm9taW5hbGVzLCBhaG9yYSB1c2FuZG8gdmFyaWFibGVzIG9yZGluYWxlcy4KU2kgc2UgcXVpZXJlIHJlcGxpY2FyIGVsIGdyw6FmaWNvIGNpcmN1bGFyIGRlIGxhcyBvdHJhcyByZWRlcyBzb2NpYWxlcywgc2UgcHVlZGUgZWplY3V0YXIgZWwgbWlzbW8gY8OzZGlnbywgcGVybyBjYW1iaWFuZG8gbGEgY29sdW1uYSBkZSBsYSByZWQgc29jaWFsLgpQYXJhIFR3aXR0ZXIsIHBvciBlamVtcGxvLCBzZSB0ZW5kcsOtYS4KCmBgYHtyIGdyYWZpY28gVHdpdHRlcn0KZ3JhZjIgPC0gZ2dwbG90KGRhdGE9dGFibGFfdHIsIGFlcyh4PTIsIHk9VHdpdHRlciwgZmlsbD1sYWIpKSsKICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIpKwogIGdlb21fdGV4dChhZXMobGFiZWw9cGFzdGUoVHdpdHRlciwgIiUiLCBzZXA9IiIpKSwgY29sb3I9IndoaXRlIiwgCiAgICAgICAgICAgIHBvc2l0aW9uPXBvc2l0aW9uX3N0YWNrKHZqdXN0PTAuNSksIHNpemU9MykrCiAgY29vcmRfcG9sYXIoInkiKSsKICB0aGVtZV92b2lkKCkrCiAgc2NhbGVfZmlsbF9kaXNjcmV0ZShuYW1lPSJGcmVjdWVuY2lhIGRlIHVzbyBkZSBUd2l0dGVyIikrCiAgIGxhYnModGl0bGU9IkZyZWN1ZW5jaWEgY29uIGxhIHF1ZSB2ZSBjb250ZW5pZG8gZW4gVHdpdHRlciIsIAogICAgICAgIGNhcHRpb249IkJhcsOzbWV0cm8gZGUgbGFzIEFtw6lyaWNhcyBwb3IgTEFQT1AsIDIwMTgvMTkiKSsKICB4bGltKDAuNSwgMi41KQpncmFmMgpgYGAKCiMjIENydWNlIGRlIHZhcmlhYmxlcwoKRW4gbGEgdGFibGEgMy4xIChwYWcuIDU1KSBkZWwgcmVwb3J0ZSAiRWwgcHVsc28gZGUgbGEgZGVtb2NyYWNpYSIgc2UgcHJlc2VudGEgbG9zIHBvcmNlbnRhamVzIGRlIHVzbyBkZSBsYXMgcmVkZXMgc29jaWFsZXMgcG9yIHBhw61zLgpMdWVnbywgZW4gbGEgcMOhZ2luYSA1NiBzZSBwcmVzZW50YSB1biBjdWFkcm8gY29uIGVsIHBvcmNlbnRhamUgZGUgdXN1YXJpb3MgZGUgcmVkZXMgc29jaWFsZXMgcG9yIGNhcmFjdGVyw61zdGljYXMgc29jaW9kZW1vZ3LDoWZpY2FzLCBwb3IgZWplbXBsbywgdXJiYW5vL3J1cmFsLCBob21icmUsIGVkYWQgcHJvbWVkaW8sIHJpcXVlemEgcHJvbWVkaW8geSBhw7FvcyBkZSBlc3R1ZGlvLgoKIVtdKHRhYmxhMy4xLnBuZyl7d2lkdGg9IjQ4MSJ9CgpFbXBlemFyZW1vcyByZXBsaWNhbmRvIGxvcyBkYXRvcyBnZW5lcmFsZXMgZGVsIHVzbyBkZSByZWRlcyBzb2NpYWxlcyBxdWUgc2UgcmVwb3J0YSBlbiBlbCBncsOhZmljbyAzLjEuClBhcmEgcmVwbGljYXIgZXN0YSB0YWJsYSBwcmltZXJvIHNlIHRpZW5lIHF1ZSBkZWZpbmlyIGxhIHZhcmlhYmxlICJwYWlzIiB5IGxhcyB2YXJpYWJsZXMgZGUgdXNvIGRlIHJlZGVzIHNvY2lhbGVzIChzbWVkaWExLCBzbWVkaWE0IHkgc21lZGlhNykuCgpgYGB7ciBwYcOtc30KbGFwb3AxOCRzbWVkaWExciA9IGFzLmZhY3RvcihsYXBvcDE4JHNtZWRpYTEpCmxhcG9wMTgkc21lZGlhNHIgPSBhcy5mYWN0b3IobGFwb3AxOCRzbWVkaWE0KQpsYXBvcDE4JHNtZWRpYTdyID0gYXMuZmFjdG9yKGxhcG9wMTgkc21lZGlhNykKbGV2ZWxzKGxhcG9wMTgkc21lZGlhMXIpIDwtIGMoIlPDrSIsICJObyIpCmxldmVscyhsYXBvcDE4JHNtZWRpYTRyKSA8LSBjKCJTw60iLCAiTm8iKQpsZXZlbHMobGFwb3AxOCRzbWVkaWE3cikgPC0gYygiU8OtIiwgIk5vIikKbGFwb3AxOCRwYWlzID0gYXMuZmFjdG9yKGxhcG9wMTgkcGFpcykKbGV2ZWxzKGxhcG9wMTgkcGFpcykgPC0gYygiTcOpeGljbyIsICJHdWF0ZW1hbGEiLCAiRWwgU2FsdmFkb3IiLCAiSG9uZHVyYXMiLAogICAgICAgICAgICAgICAgICAgICAgICAiTmljYXJhZ3VhIiwiQ29zdGEgUmljYSIsICJQYW5hbcOhIiwgIkNvbG9tYmlhIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICJFY3VhZG9yIiwgIkJvbGl2aWEiLCAiUGVyw7oiLCAiUGFyYWd1YXkiLCAKICAgICAgICAgICAgICAgICAgICAgICAgIkNoaWxlIiwgIlVydWd1YXkiLCAiQnJhc2lsIiwgIkFyZ2VudGluYSIsIAogICAgICAgICAgICAgICAgICAgICAgICAiUmVwLiBEb20uIiwgIkphbWFpY2EiKQp0YWJsZShsYXBvcDE4JHBhaXMpICNQYcOtcwp0YWJsZShsYXBvcDE4JHNtZWRpYTFyKSAjRmFjZWJvb2sKYGBgCgpTaW4gZW1iYXJnbywgbGEgdmFyaWFibGUgInVzdWFyaW8iIGRlIGNhZGEgcmVkIHNvY2lhbCBzZSBjYWxjdWxhIGNvbW8gY29uZGljacOzbiBkZSBkb3MgdmFyaWFibGVzLCBjb21vIHNlIHZpbyBlbiBlbCBkb2N1bWVudG8gc29icmUgbWFuZWpvIGRlIGRhdG9zLCBjb24gZWwgc2lndWllbnRlIGPDs2RpZ28uCgpgYGB7ciB1c3Vhcmlvc30KbGFwb3AxOCRmYl91c2VyIDwtIGlmZWxzZShsYXBvcDE4JHNtZWRpYTE9PTEgJiBsYXBvcDE4JHNtZWRpYTI8PTQsIDEsIDApCmxhcG9wMTgkdHdfdXNlciA8LSBpZmVsc2UobGFwb3AxOCRzbWVkaWE0PT0xICYgbGFwb3AxOCRzbWVkaWE1PD00LCAxLCAwKQpsYXBvcDE4JHdhX3VzZXIgPC0gaWZlbHNlKGxhcG9wMTgkc21lZGlhNz09MSAmIGxhcG9wMTgkc21lZGlhODw9NCwgMSwgMCkKYGBgCgpFc3RhIHZhcmlhYmxlcyBzb24gZ3JhZmljYWRhcyBlbiBlbCByZXBvcnRlIGVuIGVsIEdyw6FmaWNvIDMuMS4KUGFyYSByZXByb2R1Y2lyIGVzdG9zIGRhdG9zLCBzZSBwdWVkZSBkZXNjcmliaXIgZXN0YXMgdmFyaWFibGVzLgoKYGBge3IgdXNlcn0KcHJvcC50YWJsZSh0YWJsZShsYXBvcDE4JGZiX3VzZXIpKSoxMDAKcHJvcC50YWJsZSh0YWJsZShsYXBvcDE4JHR3X3VzZXIpKSoxMDAKcHJvcC50YWJsZSh0YWJsZShsYXBvcDE4JHdhX3VzZXIpKSoxMDAKYGBgCgpDb21vIGVuIGdyw6FmaWNvcyBhbnRlcmlvcmVzLCBjb24gZXN0b3MgZGF0b3Mgc2UgcHVlZGUgY3JlYXIgdW4gZGF0YWZyYW1lIHF1ZSBzZSB1dGlsaXphcsOtYSBwYXJhIGhhY2VyIGxvcyBncsOhZmljb3MgY2lyY3VsYXJlcyBtb3N0cmFkb3MgZW4gZWwgcmVwb3J0ZS4KClBhcmEgcmVwbGljYXIgZWwgY3VhZHJvIGRlIHVzbyBkZSByZWRlcyBzb2NpYWxlcyBwb3IgcGHDrXMsIHByaW1lcm8sIHNlIGNyZWFuIGxhcyB0YWJsYXMgYml2YXJpYWRhcyBjb24gZWwgcG9yY2VudGFqZSBkZSBsb3MgcXVlIHVzYW4geSBlbCBwb3JjZW50YWplIGRlIGxvcyBxdWUgbm8gdXNhbiBjYWRhIHJlZCBzb2NpYWwgZW4gY2FkYSBwYcOtcy4KRXN0YXMgdGFibGFzIHNlIGd1YXJkYW4gZW4gb2JqZXRvcyBkZSBSLgpMdWVnbyBzZSB1bmVuIGVzdG9zIG9iamV0b3MgdXNhbmRvIGxvcyBjb21hbmRvcyBgY2JpbmRgIHBhcmEganVudGFyIGxhcyBjb2x1bW5hcyB5IGBhcy5kYXRhLmZyYW1lYCBwYXJhIHVuaXIgbGFzIHRhYmxhcyBjb21vIHVuIGRhdGFmcmFtZS4KRXN0YSB0YWJsYSBwcmVzZW50YSB0YW1iacOpbiBsb3MgcG9yY2VudGFqZXMgZGUgbG9zIHF1ZSBObyB1c2FuIGVzdGFzIHJlZGVzIHNvY2lhbGVzLgpQYXJhIHByZXNlbnRhciB1bmEgdGFibGEgcXVlIGluY2x1eWEgc29sbyBhIGxvcyBxdWUgc8OtIHVzYW4gbGFzIHJlZGVzIHNvY2lhbGVzLCBzZSBlbGltaW5hbiBlc3RhcyBjb2x1bW5hcywgdXNhbmRvIGxhIGVzcGVjaWZpY2FjacOzbiBgWywgYygtMSwtMywtNSldYCwgcXVlIGluZGljYSBxdWUgc2UgcXVpZXJlIGVsaW1pbmFyIGxhcyBjb2x1bW5hcyAxLCAzIHkgNS5GaW5hbG1lbnRlLCBzZSBjYW1iaWEgZWwgbm9tYnJlIGRlIGxhcyBjb2x1bW5hcyBkZWwgZGF0YWZyYW1lLgoKYGBge3IgdGFibGEgcG9yIHBhw61zfQpmYnBhaXMgPC0gcm91bmQocHJvcC50YWJsZSh0YWJsZShsYXBvcDE4JHBhaXMsIGxhcG9wMTgkZmJfdXNlciksIDEpLCAzKSoxMDAKdHdwYWlzIDwtIHJvdW5kKHByb3AudGFibGUodGFibGUobGFwb3AxOCRwYWlzLCBsYXBvcDE4JHR3X3VzZXIpLCAxKSwgMykqMTAwCndocGFpcyA8LSByb3VuZChwcm9wLnRhYmxlKHRhYmxlKGxhcG9wMTgkcGFpcywgbGFwb3AxOCR3YV91c2VyKSwgMSksIDMpKjEwMAp0YWJsYXBhaXMgPC0gYXMuZGF0YS5mcmFtZShjYmluZChmYnBhaXMsIHR3cGFpcywgd2hwYWlzKSkKdGFibGFwYWlzIDwtIHRhYmxhcGFpc1ssIGMoLTEsLTMsLTUpXQp2YXJuYW1lcyA8LSBjKCJVc2EgRmFjZWJvb2siLCAiVXNhIFR3aXR0ZXIiLCAiVXNhIFdoYXRzYXBwIikKY29sbmFtZXModGFibGFwYWlzKSA8LSB2YXJuYW1lcwp0YWJsYXBhaXMKYGBgCgpQYXJhIHRlbmVyIHVuYSBtZWpvciBwcmVzZW50YWNpw7NuIGRlIGxhIHRhYmxhIHNlIHRpZW5lbiBkb3MgYWx0ZXJuYXRpdmFzOiBsYSBwcmltZXJhIGNvbiBsYSBsaWJyZXLDrWEgYGtuaXRyYCB5IGxhIG90cmEgY29uIGxhIGxpYnJlcsOtYSBgZm9ybWF0dGFibGVgLgoKYGBge3IgdGFibGEgcG9yIHBhw61zIG1lam9yYWRhfQpsaWJyYXJ5KGtuaXRyKQprYWJsZShoZWFkKHRhYmxhcGFpcyksIGZvcm1hdD0ibWFya2Rvd24iLCBkaWdpdHM9MSkKbGlicmFyeShmb3JtYXR0YWJsZSkKZm9ybWF0dGFibGUodGFibGFwYWlzKQpgYGAKCiMjIENydWNlIGNvbiB2YXJpYWJsZXMgc29jaW9kZW1vZ3LDoWZpY2FzCgpFbiBsYSBww6FnaW5hIDU2IGRlbCByZXBvcnRlICJFbCBwdWxzbyBkZSBsYSBkZW1vY3JhY2lhIiBzZSBwcmVzZW50YSBsb3MgcmVzdWx0YWRvcyBkZWwgY3J1Y2UgZW50cmUgbGFzIHZhcmlhYmxlcyB1c28gZGUgcmVkZXMgc29jaWFsZXMgeSB2YXJpYWJsZXMgc29jaW9kZW1vZ3LDoWZpY2FzIGNvbW8gdXJiYW5vL3J1cmFsLCBzZXhvLCBlZGFkLCByaXF1ZXphIHkgYcOxb3MgZGUgZWR1Y2FjacOzbi4KCiFbXShUYWJsYTMuMi5wbmcpe3dpZHRoPSI2NjMifQoKTGEgdmFyaWFibGUgInExIiByZWdpc3RyYSBlbCBnw6luZXJvIGRlbCBlbnRyZXZpc3RhZG8uCkVzdGEgdmFyaWFibGUgZXN0w6EgY29kaWZpY2FkYSBkZSBsYSBzaWd1aWVudGUgbWFuZXJhOgoKMS4gIEhvbWJyZQoyLiAgTXVqZXIKClBhcmEgY29uc3RydWlyIHVuYSB2YXJpYWJsZSAiZHVtbXkiIGxsYW1hZGEgImhvbWJyZSIsIGRlIHRhbCBtYW5lcmEgcXVlIGhvbWJyZSBzZWEgMSB5IG11amVyIHNlYSAwLCB1bmEgZm9ybWEgZGUgaGFjZXJsbyBlcyBkZSBtYW5lcmEgYWxnZWJyYWljYSAoMi12YXJpYWJsZSBxMSkuCkRlIGVzdGEgbWFuZXJhLCBhaG9yYSBsb3MgaG9tYnJlcyBtYW50aWVuZSBlbCB2YWxvciAxICgyLTEpIHkgbGFzIG11amVyZXMgcGFzYXIgYSBzZXIgMCAoMi0yKS4KRW4gZXN0ZSBjYXNvLCBsYSBudWV2YSB2YXJpYWJsZSAiaG9tYnJlIiBzZSBjcmVhIGNvbW8gdW5hIHVuYSB2YXJpYWJsZSBudW3DqXJpY2EuClZhbW9zIGEgbWFudGVuZXIgYSBsYSB2YXJpYWJsZSBjb21vICJudW0iIHBhcmEsIG3DoXMgYWJham8sIHBvZGVyIGNhbGN1bGFyIGxhIG1lZGlhIGRlIGVzdGEgdmFyaWFibGUgKGNvc2EgcXVlIG5vIHNlIHB1ZWRlIGhhY2VyIGRlIHVuYSB2YXJpYWJsZSBkZWNsYXJhZGEgY29tbyBmYWN0b3IpLgoKYGBge3IgaG9tYnJlfQpsYXBvcDE4JGhvbWJyZSA8LSAyLWxhcG9wMTgkcTEKdGFibGUobGFwb3AxOCRob21icmUpCmBgYAoKTGEgdmFyaWFibGUgdXJiYW5vL3J1cmFsIHNlIGxsYW1hICJ1ciIgZW4gbGEgYmFzZSBkZSBkYXRvcyB5IGVzdMOhIGNvZGlmaWNhZGEgZGUgbGEgc2lndWllbnRlIG1hbmVyYToKCjEuICBVcmJhbm8KMi4gIFJ1cmFsCgpEZSBsYSBtaXNtYSBtYW5lcmEgcXVlIGNvbiBnw6luZXJvLCBzZSB1c2EgbGEgZsOzcm11bGEgMi0gdmFyaWFibGUgdXIsIHBlcm8gZXN0YSB2YXJpYWJsZSBzw60gc2UgY29udmllcnRlIGVuIGZhY3RvciB5IHNlIGV0aXF1ZXRhLgoKYGBge3IgdXJiYW5vfQpsYXBvcDE4JHVyYmFuIDwtIDItbGFwb3AxOCR1cgpsYXBvcDE4JHVyYmFuID0gYXMuZmFjdG9yKGxhcG9wMTgkdXJiYW4pCmxldmVscyhsYXBvcDE4JHVyYmFuKSA8LSBjKCJSdXJhbCIsICJVcmJhbm8iKQp0YWJsZShsYXBvcDE4JHVyYmFuKQpgYGAKClNlIHByZXNlbnRhcsOhIGxhcyB0YWJsYXMgY3J1emFkYXMgZGVsIHVzbyBkZSByZWRlcyBzb2NpYWxlcyBwb3IgYWxndW5vIGRlIGxhcyB2YXJpYWJsZSBzb2Npb2RlbW9ncsOhZmljYXMsIHBhcmEgZW50ZW5kZXIgbWVqb3IgY8OzbW8gc2UgY29uc3RydXllIGxhIHRhYmxhIGdlbmVyYWwuClBvciBlamVtcGxvLCBlbCByZXBvcnRlIG11ZXN0cmEgZW4gbGEgVGFibGEgMy4yLCBsdWVnbyBkZSBsYSBjb2x1bW5hIGRlIHBvYmxhY2nDs24gZ2VuZXJhbCwgbGFzIGNvbHVtbmFzIHBhcmEgdXN1YXJpb3MgeSBubyB1c3VhcmlvcyBkZSBXaGF0c2FwcCAodmFyaWFibGUgIndhX3VzZXIiKSB5IGVuIGxhcyBmaWxhcywgbGEgcHJpbWVyYSBjb3JyZXNwb25kZSBhIHVyYmFubyAodmFyaWFibGUgInVyYmFuIiwgZG9uZGUgMCBlcyBydXJhbCB5IDEgdXJiYW5vKS4KU2UgcHJlc2VudGEgcXVlIGVudHJlIGxvcyB1c3VhcmlvcyBkZSBXaGF0c2FwcCwgZWwgNzYuNyUgc29uIHVyYmFub3MgeSBlbnRyZSBsb3Mgbm8gdXN1YXJpb3MsIGVsIDYyJSBzb24gdXJiYW5vcy4KRXN0b3MgZGF0b3Mgc2UgZ2VuZXJhbiBjb24gZWwgY29tYW5kbyBgdGFibGVgIHF1ZSBwZXJtaXRlIGhhY2VyIHVuYSB0YWJsYSBkZSBjb250aW5nZW5jaWEgZGUgMiB2YXJpYWJsZXMuCkVuIGVzdGUgY2FzbyBzZSB0aWVuZSBxdWUgbm90YXIgcXVlIGNhZGEgdmFyaWFibGUgdGllbmUgdW4gdMOtdHVsby4KRGUgZXN0YSBtYW5lcmEgZWwgY29tYW5kbyBlcyBgdGFibGUodMOtdHVsbzEgPSB2YXIxLCB0w610dWxvMiA9IHZhcjIpYC4KTHVlZ28sIHNlIHVzYSBlbCBjb21hbmRvIGBwcm9wLnRhYmxlYCBwYXJhIHF1ZSBubyBzZSBtdWVzdHJlIGxhcyBvYnNlcnZhY2lvbmVzLCBzaW5vIGxvcyBwb3JjZW50YWplcy4KRXN0ZSBjb21hbmRvIHBlcm1pdGUgY2FsY3VsYXIgbG9zIHBvcmNlbnRhamVzIHNvYnJlIGVsIHRvdGFsIChsYSBvcGNpw7NuIHBvciBkZWZlY3RvKSwgc29icmUgbGFzIGZpbGFzIHkgc29icmUgbGFzIGNvbHVtbmFzLgpQYXJhIGNhbGN1bGFyIHNvYnJlIGxhcyBjb2x1bW5hcyBzZSBkZWJlIGVzcGVjaWZpY2FyIGBwcm9wLnRhYmxlKHRhYmxlKC4uLiksIDIpYC4KU2kgc2UgcXVpc2llcmEgZWwgcG9yY2VudGFqZSBzb2JyZSBsYXMgZmlsYXMsIGVsIGPDs2RpZ28gc2Vyw61hIGBwcm9wLnRhYmxlKHRhYmxlKC4uLiksIDEpYC4KRXN0YSB0YWJsYSBzZSBwdWVkZW4gZ3VhcmRhciBjb21vIHVuIGRhdGFmcmFtZSBjb24gZWwgY29tYW5kbyBgYXMuZGF0YS5mcmFtZWAgZW4gdW4gbnVldm8gb2JqZXRvICJ0MSIuCgpgYGB7ciB0YWJsYSBXaHhVcn0KdDEgPC0gYXMuZGF0YS5mcmFtZShyb3VuZChwcm9wLnRhYmxlKHRhYmxlKFVyYmFubyA9IGxhcG9wMTgkdXJiYW4sIFVzdWFyaW8gPSBsYXBvcDE4JHdhX3VzZXIpLCAyKSoxMDAsIDEpKQp0MQpgYGAKCkxhIGZpbGFzIGNvcnJlc3BvbmRpZW50ZXMgYSAiVXJiYW5vIiBkZSBlc3RhIHRhYmxhIHJlcHJvZHVjZW4gbG9zIGRhdG9zIGRlIGxhIFRhYmxhIDMuMjogNjIuMSUgc29uIHVyYmFub3MgZW50cmUgbG9zIG5vIHVzdWFyaW9zIGRlIFdoYXRzYXBwIHkgNzYuNyUgc29uIHVyYmFub3MgZW50cmUgbG9zIHVzdWFyaW9zLgoKQWhvcmEgc2UgcHJlc2VudGFyw6EgbGEgdGFibGEgZW50cmUgdXN1YXJpb3MgZGUgRmFjZWJvb2sgKHZhcmlhYmxlICJmYl91c2VyIikgeSBob21icmUgKHZhcmlhYmxlICJob21icmUiKSwgcGVybyBwcmVzZW50YW5kbyBzb2xvIGxvcyBwb3JjZW50YWplcyB1c2Fkb3MgZW4gbGEgdGFibGEgeSBhaG9yYSB1c2FuZG8gZWwgZXN0aWxvIGRlIGPDs2RpZ28gZGVsIFRpZHl2ZXJzZSwgdXNhbmRvIGVsIG9wZXJhZG9yICJwaXBlIi4KTG8gcHJpbWVybyBlcyBkZWZpbmlyIGxhIHZhcmlhYmxlICJmYl91c2VyIiBjb21vIHVuIGZhY3RvciB5IGV0aXF1ZXRhcmxhLgpMdWVnbywgc2UgdXNhIGVsIGNvbWFuZG8gYHN1YnNldGAgcGFyYSBmaWx0cmFyIGxvcyBjYXNvcyBwZXJkaWRvcyBlbiBsYSB2YXJpYWJsZSAiZmJfdXNlciIuCkx1ZWdvIHNlIHBpZGUgcXVlIGxvcyByZXN1bHRhZG9zIHNlIGFncnVwZW4gcG9yIGNhdGVnb3LDrWFzIGRlIGxhIHZhcmlhYmxlICJmYl91c2VyIi4KQ29uIGVsIGNvbWFuZG8gYHN1bW1hcmlzZWAgc2UgZ3VhcmRhIGVuIGxhIGNvbHVtbmEgImhvbWJyZSIgZWwgcHJvbWVkaW8gZGUgbGEgdmFyaWFibGUgImhvbWJyZSIsIGNvbiBlbCBjb21hbmRvIGBtZWFuYCBxdWUgaW5jbHV5ZSBsYSBlc3BlY2lmaWNhY2nDs24gYG5hLnJtPVRgIHBhcmEgbm8gaW5jbHVpciBlbiBlbCBjw6FsY3VsbyBsb3MgdmFsb3JlcyBwZXJkaWRvcy4KRW4gZXN0ZSBjYXNvIHNlIGFwcm92ZWNoYSBxdWUgbGEgdmFyaWFibGUgImhvbWJyZSIgZXMgdW5hIHZhcmlhYmxlIGR1bW15LCBkZSB0YWwgbWFuZXJhIHF1ZSBlbCBwcm9tZWRpbyBjb3JyZXNwb25kZSBhIGxhIHByb3BvcmNpw7NuIGRlIGhvbWJyZXMuCgpgYGB7ciB0YWJsIEZieEhvbWJyZSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGFwb3AxOCRmYl91c2VyID0gYXMuZmFjdG9yKGxhcG9wMTgkZmJfdXNlcikKbGV2ZWxzKGxhcG9wMTgkZmJfdXNlcikgPC0gYygiTm8gdXN1YXJpbyIsICJVc3VhcmlvIikKdGFibGExIDwtIHN1YnNldChsYXBvcDE4LCAhaXMubmEoZmJfdXNlcikpICU+JSAjUGFyYSBubyBpbmNsdWlyIGFsIGdydXBvIGRlIE5BIGRlIHVzdWFyaW9zIGRlIEZhY2Vib29rCiAgZ3JvdXBfYnkoZmJfdXNlcikgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShIb21icmU9bWVhbihob21icmUsIG5hLnJtPVQpKjEwMCkgI1NlIGluY2x1eWUgbmEucm09VCBwb3JxdWUgaG9tYnJlIHRpZW5lIE5Bcwp0YWJsYTEKYGBgCgpFc3RvcyBzb24gbG9zIHBvcmNlbnRhamVzIHBhcmEgbG9zIHVzdWFyaW9zIGRlIEZhY2Vib29rLCBlbiBsYSBmaWxhIGRlIGxhIHZhcmlhYmxlIEhvbWJyZSBkZSBsYSBUYWJsYSAzLjIuCkVzIGRlY2lyLCBlbnRyZSBsb3Mgbm8gdXN1YXJpb3MsIDQ5LjklIHNvbiBob21icmVzIHkgZW50cmUgbG9zIHVzdWFyaW9zIGVzdGUgcG9yY2VudGFqZSBlcyA0OS43JS4KSGFzdGEgYXF1w60gc2UgaGEgcmVjb25zdHJ1aWRvIGFsZ3Vub3MgcmVzdWx0YWRvcyBkZSBsYSBUYWJsYSAzLjIuCkxvcyBkZW3DoXMgZGF0b3MgcHVlZGVuIHNlZ3VpciBzaWVuZG8gcmVjb25zdHJ1aWRvcyBtZWRpYW50ZSBjb21iaW5hY2lvbmVzIGRlIGxhcyB2YXJpYWJsZXMgZGUgdXN1YXJpb3MgZGUgcmVkZXMgc29jaWFsZXMgeSBsYXMgdmFyaWFibGVzIHNvY2lvZGVtb2dyw6FmaWNhcy4KCiMjIEdyw6FmaWNvIGRlIGJhcnJhcyBkZSBkb3MgdmFyaWFibGVzCgpFbCBjcnVjZSBlbnRyZSB1c3VhcmlvcyBkZSBXaGF0c2FwcCB5IGxhIHZhcmlhYmxlIHVyYmFubyBzZSBwdWVkZSB2ZXIgdGFtYmnDqW4gZW4gdW4gZ3LDoWZpY28gZGUgYmFycmFzIGFncnVwYWRhcy4KTG8gcHJpbWVybyBxdWUgaGFyZW1vcyBlcyBkZWZpbmlyIGxhIHZhcmlhYmxlICJ3YV91c2VyIiBjb21vIGZhY3RvciB5IGV0aXF1ZXRhcmxhLgpMdWVnbywgc2UgcmVxdWllcmUgY3JlYXIgdW5hIHRhYmxhIGNvbiBsb3MgZGF0b3MgYWdydXBhZG9zLgpTZSBwdWVkZSB1c2FyIGVsIGRhdGFmcmFtZSAidDEiIGNyZWFkbyBhbnRlcmlvcm1lbnRlLCBwZXJvIGFxdcOtIHVzYXJlbW9zIG51ZXZhbWVudGUgZWwgb3BlcmFkb3IgInBpcGUiIHBhcmEgcmVjcmVhciBsb3MgZGF0b3MuClBhcmEgZXN0bywgc2UgYWdydXBhIHRhbnRvIHBvciB1c28gZGUgV2hhdHNhcHAgY29tbyBwb3IgdXJiYW5vL3J1cmFsLCBlcyBkZWNpciwgZW4gY3VhdHJvIGNvbWJpbmFjaW9uZXMuCkVuIGNhZGEgc3ViZ3J1cG8gc2UgY2FsY3VsYSBlbCBuLgpEYWRvIHF1ZSBsb3MgJSBzZSB0aWVuZW4gcXVlIGNhbGN1bGFyIHBvciBjYWRhIGdydXBvIGRlICJ3YV91c2VyIiwgc2UgdnVlbHZlIGEgYWdydXBhciB5IHNlIGNhbGN1bGEgbG9zICUgZGUgY2FkYSBzdWJncnVwbywgZGUgdGFsIG1hbmVyYSBxdWUgbG9zIHBvcmNlbnRhamVzIHN1bWVuIDEwMCUgZW4gY2FkYSBzdWJncnVwbyBkZSAid2FfdXNlciIuCgpgYGB7ciB0YWJsYSB3YX0KbGFwb3AxOCR3YV91c2VyID0gYXMuZmFjdG9yKGxhcG9wMTgkd2FfdXNlcikKbGV2ZWxzKGxhcG9wMTgkd2FfdXNlcikgPC0gYygiTm8gdXN1YXJpbyIsICJVc3VhcmlvIikKZmFjZSA8LSBzdWJzZXQobGFwb3AxOCwgIWlzLm5hKHdhX3VzZXIpKSAlPiUgI1NlIHVzYSAhaXMubmEgcGFyYSBxdWUgbm8gc2UgcmVwb3J0ZSBsb3MgTkEgZW4gbGEgdGFibGEKICBncm91cF9ieSh3YV91c2VyLCB1cmJhbikgJT4lICNTZSBjb25maWd1cmFuIGxvcyBncnVwb3MKICBkcGx5cjo6Y291bnQoKSAlPiUgI1NlIGNhbGN1bGEgZWwgbgogIGdyb3VwX2J5KHdhX3VzZXIpICU+JSAjU2UgYWdydXBhIHBvciB1c3VhcmlvIGRlIFdoYXRzYXBwCiAgZHBseXI6Om11dGF0ZShwb3JjZW50YWplID0gcm91bmQobi9zdW0obiksIDMpKjEwMCkgI1NlIGNhbGN1bGEgZWwgcG9yY2VudGFqZSBlbiBjYWRhIGdydXBvIGRlIFdoYXRzYXBwCmZhY2UKYGBgCgpMb3MgZGF0b3Mgc2UgZXN0YSBudWV2YSB0YWJsYSBzb24gZXhhY3RhbWVudGUgaWd1YWxlcyBhIGxvcyBkZSAidDEiLgpDb24gbGEgdGFibGEgbGlzdGEsIHNlIHVzYSBlbCBjb21hbmRvIGBnZ3Bsb3RgIGRlZmluaWVuZG8gcXVlICJ3YV91c2VyIiBzZWEgbGEgdmFyaWFibGUgZW4gZWwgZWplIFgsIHF1ZSBlbCBlamUgWSBzZWEgZWwgcG9yY2VudGFqZSB5IHF1ZSBsb3Mgc3ViZ3J1cG9zIHNlIGZvcm1lbiBwb3IgbGEgdmFyaWFibGUgInVyYmFuIiBjb24gbGEgZXNwZWNpZmljYWNpw7NuIGBmaWxsYC4KU2UgdXNhIGxhIGVzcGVjaWZpY2FjacOzbiBgZG9kZ2VgIGVuIGBnZW9tX2JhcmAgcGFyYSB0ZW5lciBsYXMgYmFycmFzIHNlcGFyYWRhcyBwb3IgY2FkYSBncnVwby4KCmBgYHtyIGdyYWZpY28gYWdydXBhZG99CmdncGxvdChkYXRhPWZhY2UsIGFlcyh4PXdhX3VzZXIsIHk9cG9yY2VudGFqZSwgZmlsbD11cmJhbiwgeW1heD0xMDApKSsKICBnZW9tX2Jhcihwb3NpdGlvbj0iZG9kZ2UiLCBzdGF0PSJpZGVudGl0eSIpKwogIGdlb21fdGV4dChhZXMobGFiZWw9cGFzdGUocG9yY2VudGFqZSwgIiUiLCBzZXA9IiIpKSwgCiAgICAgICAgICAgIHBvc2l0aW9uPXBvc2l0aW9uX2RvZGdlKHdpZHRoPTAuOSksIHZqdXN0PS0wLjI1KSsKICB5bGFiKCJQb3JjZW50YWplIikrCiAgeGxhYigiVXN1YXJpbyBkZSBXaGF0c2FwcCIpCmBgYAoKU2kgcXVpc2nDqXJhbW9zIGhhY2VyIGVsIGdyw6FmaWNvIGNvbiBiYXJyYXMgYXBpbGFkYXMsIHNlIHRpZW5lIHF1ZSBjYW1iaWFyIGxhIGVzcGVjaWZpY2FjacOzbiBkZSBgcG9zaXRpb249InN0YWNrImAgZW4gYGdlb21fYmFyYCB5IGxhIGVzcGVjaWZpY2FjacOzbiBgcG9zaXRpb249cG9zaXRpb25fc3RhY2soKWAgZGUgYGdlb21fdGV4dGAuCgpgYGB7ciBiYXJyYXMgYXBpbGFkYXN9CmdncGxvdChkYXRhPWZhY2UsIGFlcyh4PXdhX3VzZXIsIHk9cG9yY2VudGFqZSwgZmlsbD11cmJhbiwgeW1heD0xMDApKSsKICBnZW9tX2Jhcihwb3NpdGlvbj0ic3RhY2siLCBzdGF0PSJpZGVudGl0eSIpKwogIGdlb21fdGV4dChhZXMobGFiZWw9cGFzdGUocG9yY2VudGFqZSwgIiUiLCBzZXA9IiIpKSwgCiAgICAgICAgICAgIHBvc2l0aW9uPXBvc2l0aW9uX3N0YWNrKCksIHZqdXN0PTIuNSkrCiAgeWxhYigiUG9yY2VudGFqZSIpKwogIHhsYWIoIlVzdWFyaW8gZGUgV2hhdHNhcHAiKQpgYGAKCkVuIGFtYm9zIGNhc29zIGxhcyBiYXJyYXMgY2VsZXN0ZXMgaW5kaWNhbiBsb3MgcG9yY2VudGFqZXMgcmVwb3J0YWRvcyBlbiBsYSB0YWJsYSAzLjIgZGVsIHJlcG9ydGUgeSBjb3JyZXNwb25kZW4gYSBsYSBwcm9wb3JjacOzbiBkZSBwZXJzb25hcyBxdWUgdml2ZW4gZW4gZWwgw6FtYml0byB1cmJhbm8gZW50cmUgbG9zIHVzdWFyaW9zIHkgbG9zIG5vIHVzdWFyaW9zLgoKIyBSZXN1bWVuCgpFbiBlc3RlIGRvY3VtZW50byBzZSBoYSB0cmFiYWphZG8gY29uIHZhcmlhYmxlIGNhdGVnw7NyaWNhcyBvcmRpbmFsZXMsIGNvbW8gbGEgZnJlY3VlbmNpYSBkZSB1c28gZGUgcmVkZXMgc29jaWFsZXMuClRhbWJpw6luIHNlIGhhIGludHJvZHVjaWRvIGFsIHVzbyBkZSB0YWJsYXMgZGUgY29udGluZ2VuY2lhIGRlIGRvcyB2YXJpYWJsZXMgY2F0ZWfDs3JpY2FzIHkgbGEgY3JlYWNpw7NuIGRlIGdyw6FmaWNvcyBkZSBiYXJyYXMgYWdydXBhZGFzIHBhcmEgMiB2YXJpYWJsZXMuCgojIEPDoWxjdWxvcyBpbmNsdXllbmRvIGVsIGVmZWN0byBkZSBkaXNlw7FvCgojIyBFamVtcGxvIGNvbiBkYXRvcyBkZSBsYSByb25kYSAyMDIxCgpDb24gbG9zIGRhdG9zIGRlIGxhIHJvbmRhIDIwMjEgZGVsIEJhcsOzbWV0cm8gZGUgbGFzIEFtw6lyaWNhcywgaGVtb3MgY2FsY3VsYWRvIGxvcyBwb3JjZW50YWplcyBkZSBsYSB2YXJpYWJsZSBxdWUgbWlkZSBzaSBsb3Mgdm90b3Mgc2UgaGFuIGNvbnRhZG8ganVzdGFtZW50ZS4KRWwgZ3LDoWZpY28gcXVlIGhlbW9zIGNyZWFkbyBoYSBzaWRvIHBhcmEgZWwgdG90YWwgZGUgbGEgbXVlc3RyYSwgZXMgZGVjaXIsIGRlIHRvZG9zIGxvcyBwYcOtc2VzLgpFbCBHcsOhZmljbyAyLjUgZGVsIHJlcG9ydGUgRWwgUHVsc28gZGUgbGEgRGVtb2NyYWNpYSBwcmVzZW50YSBsb3MgcmVzdWx0YWRvcyBwYXJhIGNhZGEgcGHDrXMuCgpTaSBjYWxjdWzDoXJhbW9zIGxvcyBwb3JjZW50YWplcyBjb24gZWwgY29tYW5kbyBgdGFibGVgIHkgYHByb3AudGFibGVgIHRlbmRyw61hbW9zIHJlc3VsdGFkb3MgZGlmZXJlbnRlcyBhIGxvcyBtb3N0cmFkb3MgZW4gZWwgZ3LDoWZpY28uClBvciBlamVtcGxvLCBlbiBlbCBwYcOtcyA1LCBxdWUgZXMgTmljYXJhZ3VhLCBsb3MgcmVzdWx0YWRvcyBpbmRpY2FuIHF1ZSAyNiUgZGUgY2l1ZGFkYW5vcyBkZSBlc2UgcGHDrXMgaW5kaWNhIHF1ZSBsb3Mgdm90b3Mgc2llbXByZSBzZSBjdWVudGFuIGp1c3RhbWVudGUsIDQ5JSBxdWUgYWxndW5hcyB2ZWNlcyBzZSBjdWVudGFuIGp1c3RhbWVudGUgeSAyNC40JSBxdWUgbnVuY2EuClNpbiBlbWJhcmdvLCBlbCBncsOhZmljbyBpbmRpY2EgcXVlIGVuIE5pY2FyYWd1YSwgMjklIGluZGljYSBxdWUgbnVuY2EgZGUgY3VlbnRhIGp1c3RhbWVudGUsIDQ1JSBpbmRpY2EgcXVlIGFsZ3VuYXMgdmVjZXMgeSAyNSUgcXVlIHNpZW1wcmUuCkVzdG9zIHBvcmNlbnRhamVzIG5vIGNvcnJlc3BvbmRlbiBhIGxvcyBxdWUgc2UgZW5jdWVudHJhbiBjb24gZXN0b3MgY29tYW5kb3MuCgpgYGB7ciBjcnVjZSBzaW4gcG9uZGVyYXJ9CnJvdW5kKHByb3AudGFibGUodGFibGUobGFwb3AyMSRwYWlzLCBsYXBvcDIxJGNvdW50ZmFpcjFyKSwgMSksIDMpKjEwMApgYGAKCkVzdGEgZGlmZXJlbmNpYSBlcyBkZWJpZG8gYSBxdWUgbG9zIGNvbWFuZG9zIGB0YWJsZWAgeSBgcHJvcC50YWJsZWAgbm8gaW5jbHV5ZW4gZWwgZWZlY3RvIGRlIGRpc2XDsW8geSBlbCBmYWN0b3IgZGUgZXhwYW5zacOzbiBlbiBsb3MgY8OhbGN1bG9zLgpNw6FzIGluZm9ybWFjacOzbiBzb2JyZSBlc3RhcyBkaWZlcmVuY2lhcyBzZSBlbmN1ZW50cmEgW2FxdcOtXShodHRwczovL2FydHVyb21hbGRvbmFkby5naXRodWIuaW8vQmFyb21ldHJvRWR1X1dlYi9FeHBhbnNpb24uaHRtbCkuCgpQYXJhIHJlcGxpY2FyIGxvcyByZXN1bHRhZG9zIGRlbCBHcsOhZmljbyAyLjUgaGF5IGFsZ3VuYXMgb3BjaW9uZXMuCkxhIHByaW1lcmEgZXMgbWVkaWFudGUgbGEgbGlicmVyw61hIGVzcGVjaWFsaXphZGEgYHN1cnZleWAuCgpQYXJhIHBvZGVyIHVzYXIgZXN0YSBsaWJyZXLDrWEsIHByaW1lcm8gZGViZW1vcyBwcmVwYXJhciBsYSBiYXNlIGRlIGRhdG9zLCBlbGltaW5hbmRvIGxvcyB2YWxvcmVzIHBlcmRpZG9zIGRlIGxhcyB2YXJpYWJsZXMgcXVlIGRlZmluZW4gZWwgZGlzZcOxby4KVW4gcGFzbyBhZGljaW9uYWwgZXMgdHJhbnNmb3JtYXIgbGFzIHZhcmlhYmxlcyBkZWwgZGF0YWZyYW1lLgpFc3RvIGVzIGRlYmlkbyBhIHF1ZSBjdWFuZG8gc2UgaW1wb3J0YW4sIGVsIHNpc3RlbWEgbGVlIGxhcyB2YXJpYWJsZXMgY29tbyB0aXBvICJoYXZlbl9sYWJlbGxlZCIsIGVzIGRlY2lyLCBtYW50aWVuZSBsYXMgZXRpcXVldGFzIGRlIGxhcyB2YXJpYWJsZXMsIGNvbiBsbyBxdWUgc2UgcG9kcsOtYSBwcm9kdWNpciB1biBsaWJybyBkZSBjw7NkaWdvcy4KRXN0byBlcyDDunRpbCBlbiBvdHJhcyBvY2FzaW9uZXMsIHBlcm8gZ2VuZXJhIHByb2JsZW1hcyBjb24gbGEgbGlicmVyw61hIGBzdXJ2ZXlgLgpQYXJhIGVzdG8gdHJhbnNmb3JtYW1vcyBsYXMgdmFyaWFibGVzIGEgb3RybyB0aXBvIGNvbiBlbCBjb21hbmRvIGBzYXBwbHlgLgoKYGBge3IgcHJlcGFyYWNpb24gZGUgYmFzZSwgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCByZXN1bHRzPSdoaWRlJ30KbGFwb3AyMSA9IHN1YnNldChsYXBvcDIxLCAhaXMubmEod2VpZ2h0MTUwMCkpCnNhcHBseShsYXBvcDIxLCBoYXZlbjo6emFwX2xhYmVscykKYGBgCgpVbmEgdmV6IHByZXBhcmFkYSBsYSBiYXNlIGRlIGRhdG9zLCBzZSBhY3RpdmEgbGEgbGlicmVyw61hIHkgc2UgZGVmaW5lIGVsIGRpc2XDsW8uCkVuIGVsIG3Ds2R1bG8gYW50ZXJpb3IsIHRhbWJpw6luIHVzYW1vcyBlc3RhIGxpYnJlcsOtYSBwYXJhIGNhbGN1bGFyIGxvcyByZXN1bHRhZG9zIGNvbiBlbCBlZmVjdG8gZGUgZGlzZcOxbyBlbiBsYSByb25kYSAyMDE4LgpBIGRpZmVyZW5jaWEgZGUgZXNlIGPDs2RpZ28sIGVzIHF1ZSBsYSByb25kYSAyMDIxIGRlbCBCYXLDs21ldHJvIGRlIGxhcyBBbcOpcmljYXMgdXRpbGl6w7MgbGEgbW9kYWxpZGFkIHRlbGVmw7NuaWNhLCB5IG5vIGNhcmEgYSBjYXJhLCBwb3IgbG8gcXVlIGFob3JhIGxhIHVuaWRhZCBwcmltYXJpYSBkZSBtdWVzdHJlbyBlcyBlbCBpbmRpdmlkdW8sIHkgYXPDrSBlc3TDoSBkZWZpbmlkYSBlbiBsYSB2YXJpYWJsZSAidXBtIi4KTGEgdmFyaWFibGUgcXVlIGRlZmluZSBsb3MgZXN0cmF0b3MgZXMgInN0cmF0YSIgKHkgbm8gImVzdHJhdG9wcmkiIGNvbW8gZW4gbGEgcm9uZGEgMjAxOCkuCkxhIHZhcmlhYmxlIGRlIHBvbmRlcmFjacOzbiBzaWd1ZSBzaWVuZG8gIndlaWdodDE1MDAiLgoKQ29uIGVzdG9zIGRhdG9zIGNhbGN1bGFtb3MgZ3VhcmRhbW9zIGVsIGRpc2XDsW8gZW4gdW4gb2JqZXRvICJkaXNlbm8yMSIuCgpgYGB7ciBkaXNlbm99CmxpYnJhcnkoc3VydmV5KQpkaXNlbm8yMSA9IHN2eWRlc2lnbihpZHMgPSB+dXBtLCBzdHJhdGEgPSB+c3RyYXRhLCB3ZWlnaHRzID0gfndlaWdodDE1MDAsIG5lc3Q9VFJVRSwgZGF0YT1sYXBvcDIxKQpgYGAKCkxhIGxpYnJlcsOtYSBgc3VydmV5YCBpbmNsdXllIGNvbWFuZG9zIG5hdGl2b3MgcGFyYSBoYWNlciBtw7psdGlwbGVzIG9wZXJhY2lvbmVzIHF1ZSBpbmNsdXlhbiBlbCBlZmVjdG8gZGUgZGlzZcOxby4KVW5vIGRlIGVzb3MgY29tYW5kb3MgZXMgYHN2eXRhYmxlYCBxdWUgbm9zIHBlcm1pdGUgaGFjZXIgbGEgdGFibGEgY3J1emFkYSBlbnRyZSBsYSB2YXJpYWJsZSAiY291bnRmYWlyMXIiIHkgInBhaXMiLCBlc3BlY2lmaWNhbmRvIGVsIGRpc2XDsW8uCkVzdGUgY29tYW5kbyBub3MgZGV2dWVsdmUgbGFzIGZyZWN1ZW5jaWFzIGFic29sdXRhcyBwb25kZXJhZGFzLCBwb3IgbG8gcXVlIHNlIHB1ZWRlIGFuaWRhciBlbiBlbCBjb21hbmRvIGBwcm9wLnRhYmxlYCBwYXJhIGNhbGN1bGFyIGxvcyBwb3JjZW50YWplcyBkZXNkZSBsYXMgZnJlY3VlbmNpYXMgYWJzb2x1dGFzIHBvbmRlcmFkYXMgeSBkZW50cm8gZGVsIGNvbWFuZG8gYGNvdW50YCBwYXJhIHJlZG9uZGVhciBsb3MgcG9yY2VudGFqZXMsIHkgZGVudHJvIGRlbCBjb21hbmRvIGBhcy5kYXRhLnRhYmxlYCBwYXJhIGd1YXJkYXIgbGEgdGFibGEgZW4gdW4gb2JqZXRvICJ2b3RveHBhaXMiIGNvbW8gdW4gZGF0YWZyYW1lLCBxdWUgbm9zIHBlcm1pdGEgbGEgbWFuaXB1bGFjacOzbiBjb24gYGdncGxvdGAgbHVlZ28uCgpgYGB7ciB0YWJsYSBwb25kZXJhZGF9CnZvdG94cGFpcyA9IGFzLmRhdGEuZnJhbWUocm91bmQocHJvcC50YWJsZShzdnl0YWJsZSh+cGFpcytjb3VudGZhaXIxciwgZGVzaWduPWRpc2VubzIxKSwgMSkqMTAwLCAwKSkKdm90b3hwYWlzJHBhaXMgPSBwYWlzCnZvdG94cGFpcwpgYGAKCmBgYHtyIGdyYWZpY28gcG9uZGVyYWRvfQpnZ3Bsb3QoZGF0YT12b3RveHBhaXMsIGFlcyhmaWxsPWNvdW50ZmFpcjFyLCB4PUZyZXEsIHk9cGFpcykpKwogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5Iiwgd2lkdGg9MC4zKSsKICBnZW9tX3RleHQoYWVzKGxhYmVsPXBhc3RlKEZyZXEsICIlIiwgc2VwPSIiKSksIGNvbG9yPSJ3aGl0ZSIsIAogICAgICAgICAgICBwb3NpdGlvbj1wb3NpdGlvbl9zdGFjayh2anVzdD0wLjUpLCBzaXplPTMpKwogIGxhYnMoeD0iUG9yY2VudGFqZSIsIHk9IlBhw61zIiwgZmlsbD0iTG9zIHZvdG9zIHNlIGN1ZW50YW4ganVzdGFtZW50ZSIsCiAgICAgICBjYXB0aW9uPSJCYXLDs21ldHJvIGRlIGxhcyBBbcOpcmljYXMgcG9yIExBUE9QLCAyMDIxIikKYGBgCgpFc3RlIGdyw6FmaWNvIHJlcHJvZHVjZSBleGFjdGFtZW50ZSBsb3MgcmVzdWx0YWRvcyBtb3N0cmFkb3MgZW4gZWwgR3LDoWZpY28gMi41IGRlbCByZXBvcnRlLCBhdW5xdWUgZW4gdW4gb3JkZW4gZGlmZXJlbnRlLgoKIyMgRWplbXBsbyBjb24gbG9zIGRhdG9zIGRlIGxhIHJvbmRhIDIwMTgvMTkKCkxhIHBlcXVlw7FhIGRpZmVyZW5jaWEgZW50cmUgbG9zIHBvcmNlbnRhamVzIHF1ZSBzZSBtdWVzdHJhbiBlbiBlbCBHcsOhZmljbyAzLjMgeSBsb3MgbW9zdHJhZG9zIGVuIGxhIHNlY2Npw7NuICJEZXNjcmliaXIgbGFzIHZhcmlhYmxlcyIgcHVlZGUgZGViZXJzZSBhIHF1ZSBlbiB0b2RvcyBsYXMgdGFibGFzIHkgZ3LDoWZpY29zIGFudGVyaW9yZXMgbm8gc2UgaW5jbHV5ZSBlbCBmYWN0b3IgZGUgZXhwYW5zacOzbi4KU2kgc2UgaW5jbHV5ZXJhLCB1c2FuZG8gbGEgdmFyaWFibGUgIndlaWdodDE1MDAiLCBzZSByZXBsaWNhcsOtYW4gbG9zIHBvcmNlbnRhamVzIG1vc3RyYWRvcyBlbiBlbCBpbmZvcm1lLgpQb3IgZWplbXBsbywgcGFyYSBlbCB1c28gZGUgV2hhdHNhcHAsIHNlIHB1ZWRlIHVzYXIgZWwgY29tYW5kbyBgZnJlcWAgZGUgbGEgbGlicmVyw61hIGBkZXNjcmAgcXVlIHBlcm1pdGUgaW5jbHVpciB1bmEgdmFyaWFibGUgZGUgcG9uZGVyYWNpw7NuLgoKRXN0YSB0YWJsYSBsdWVnbyBzZSBwdWVkZSBndWFyZGFyIGVuIHVuIGRhdGFmcmFtZSwgYWwgcXVlIHNlIGxlIHB1ZWRlbiBlbGltaW5hciBsYXMgZmlsYXMgeSBjb2x1bW5hcyBxdWUgbm8gc2UgcmVxdWllcmVuLCB5IHNlIGxlcyBwdWVkZSBjYW1iaWFyIGVsIG5vbWJyZSBhIGxhcyBjb2x1bW5hcy4KeSBhZ3JlZ2FyIGxhcyBldGlxdWV0YXMuCgpgYGB7ciB3YSBwb25kZXJhZG99CnRhYmxhMiA8LSBhcy5kYXRhLmZyYW1lKGRlc2NyOjpmcmVxKGxhcG9wMTgkc21lZGlhOHIsIGxhcG9wMTgkd2VpZ2h0MTUwMCwgcGxvdD1GKSkKdGFibGEyIDwtIHRhYmxhMlstYyg1LDYpLCAtMl0KY29sbmFtZXModGFibGEyKSA8LSBjKCJmcmVjIiwgInBlciIpCnRhYmxhMiRsYWIgPC0gcm93bmFtZXModGFibGEyKQp0YWJsYTIKYGBgCgpDb24gZXN0ZSBudWV2byBkYXRhZnJhbWUsIHNlIHB1ZWRlIHJlcGxpY2FyIGVsIGdyw6FmaWNvIGRlIFdoYXRzYXBwLCBjb24gbG9zIGRhdG9zIHBvbmRlcmFkb3MsIHVzYW5kbyBlbCBjw7NkaWdvIGRlIGBnZ3Bsb3RgLgoKYGBge3Igd2EgZ3JhZiBwb25kfQpnZ3Bsb3QoZGF0YT10YWJsYTIsIGFlcyh4PTIsIHk9cGVyLCBmaWxsPWxhYikpKwogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IikrCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbD1wYXN0ZShyb3VuZChwZXIsIDEpLCAiJSIsIHNlcD0iIikpLCBjb2xvcj0id2hpdGUiLCAKICAgICAgICAgICAgcG9zaXRpb249cG9zaXRpb25fc3RhY2sodmp1c3Q9MC41KSwgc2l6ZT0zKSsKICBjb29yZF9wb2xhcigieSIpKwogIHRoZW1lX3ZvaWQoKSsKICBzY2FsZV9maWxsX2Rpc2NyZXRlKG5hbWU9IkZyZWN1ZW5jaWEgZGUgdXNvIGRlIEZhY2Vib29rIikrCiAgIGxhYnModGl0bGU9IkZyZWN1ZW5jaWEgY29uIGxhIHF1ZSB2ZSBjb250ZW5pZG8gZW4gRmFjZWJvb2siLCAKICAgICAgICBjYXB0aW9uPSJCYXLDs21ldHJvIGRlIGxhcyBBbcOpcmljYXMgcG9yIExBUE9QLCAyMDE4LzE5IikrCiAgeGxpbSgwLjUsIDIuNSkKYGBgCgpBZGVtw6FzIGRlbCBjb21hbmRvIGBmcmVxYCwgdGFtYmnDqW4gc2UgcHVlZGUgdXNhciBsYSBsaWJyZXLDrWEgYHN1cnZleWAgeSBlbCBjb21hbmRvIG5hdGl2byBgc3Z5dGFibGVgLgoKYGBge3Igc3VydmV5fQpsaWJyYXJ5KHN1cnZleSkKZGlzZW5vMTg8LXN2eWRlc2lnbihpZHMgPSB+dXBtLCBzdHJhdGEgPSB+ZXN0cmF0b3ByaSwgd2VpZ2h0cyA9IH53ZWlnaHQxNTAwLCBuZXN0PVRSVUUsIGRhdGE9bGFwb3AxOCkKYGBgCgpMb3MgcmVzdWx0YWRvcyBxdWUgc2Ugb2J0aWVuZW4gc29uIGlndWFsZXMgcXVlIGNvbiBlbCBtw6l0b2RvIGFudGVyaW9yIHkgYSBsb3MgcHJlc2VudGFkb3MgZW4gZWwgcmVwb3J0ZS4KRXN0b3MgcmVzdWx0YWRvcyB0YW1iacOpbiBzZSBwdWVkZW4gZ3VhcmRhciBlbiB1biAiZGF0YWZyYW1lIiBwYXJhIGhhY2VyIGVsIGdyw6FmaWNvLgoKYGBge3Igc3Z5dGFibGV9CnByb3AudGFibGUoc3Z5dGFibGUofnNtZWRpYThyLCBkZXNpZ249ZGlzZW5vMTgpKSoxMDAKYGBgCgpMYSBzZWNjacOzbiAiQ3J1Y2UgY29uIHZhcmlhYmxlcyIgcHJlc2VudGEgbG9zIGRhdG9zIGRlIHVzdWFyaW9zIGRlIHJlZGVzIHNvY2lhbGVzIHBvciBwYcOtcy4KUGFyYSBjb25zdHJ1aXIgbGEgdGFibGEgY29uc2lkZXJhbmRvIGVsIGVmZWN0byBkZSBkaXNlw7FvLCB0YW1iacOpbiBzZSBwdWVkZSB1c2FyIGVsIGNvbWFuZG8gbmF0aXZvIGBzdnl0YWJsZWAgcXVlIHBlcm1pdGUgY2FsY3VsYXIgdW5hIHRhYmxhIGJpdmFyaWFkYS4KRGUgbGEgbWlzbWEgbWFuZXJhIHF1ZSBlbiBjYXNvIG5vIHBvbmRlcmFkbywgbGFzIHRhYmxhcyBwYXJjaWFsZXMgZGUgY2FkYSByZWQgc29jaWFsIHNlIGd1YXJkYW4gZW4gdW5vcyBvYmpldG9zIGRlIHRpcG8gbGlzdGEsIGxvcyBxdWUgc2UganVudGFuIGNvbW8gdW4gImRhdGFmcmFtZSIgeSBzZSBlZGl0YSBwYXJhIHByZXNlbnRhciBzb2xvIGxvcyBkYXRvcyBkZSBsb3MgdXN1YXJpb3MgZGUgY2FkYSByZWQgc29jaWFsIHBvciBwYcOtcy4KCmBgYHtyIHN2eXRhYmxlIGJpdmFyaWFkYX0KZmJwYWlzXzIgPC0gcm91bmQocHJvcC50YWJsZShzdnl0YWJsZSh+cGFpcytmYl91c2VyLCBkZXNpZ249ZGlzZW5vMTgpLCAxKSwgMykqMTAwCnR3cGFpc18yIDwtIHJvdW5kKHByb3AudGFibGUoc3Z5dGFibGUofnBhaXMrdHdfdXNlciwgZGVzaWduPWRpc2VubzE4KSwgMSksIDMpKjEwMAp3YXBhaXNfMiA8LSByb3VuZChwcm9wLnRhYmxlKHN2eXRhYmxlKH5wYWlzK3dhX3VzZXIsIGRlc2lnbj1kaXNlbm8xOCksIDEpLCAzKSoxMDAKdGFibGFwYWlzXzIgPC0gYXMuZGF0YS5mcmFtZShjYmluZChmYnBhaXNfMiwgdHdwYWlzXzIsIHdhcGFpc18yKSkKdGFibGFwYWlzXzIgPC0gdGFibGFwYWlzXzJbLCBjKC0xLC0zLC01KV0KdmFybmFtZXMgPC0gYygiVXNhIEZhY2Vib29rIiwgIlVzYSBUd2l0dGVyIiwgIlVzYSBXaGF0c2FwcCIpCmNvbG5hbWVzKHRhYmxhcGFpc18yKSA8LSB2YXJuYW1lcwp0YWJsYXBhaXNfMgpgYGAKClBvciDDumx0aW1vLCBsYSBzZWNjacOzbiAiQ3J1Y2UgY29uIHZhcmlhYmxlcyBzb2Npb2RlbW9ncsOhZmljYXMiIHJlcHJvZHVjZSBsb3MgcmVzdWx0YWRvcyBkZSBsYSBUYWJsYSAzLjIgZGVsIHJlcG9ydGUuCkRlIGxhIG1pc21hIG1hbmVyYSBxdWUgZW4gZWwgY2FzbyBhbnRlcmlvciwgc2UgcHVlZGUgdXNhciBlbCBjb21hbmRvIG5hdGl2byBgc3Z5dGFibGVgIHBhcmEgcmVhbGl6YXIgZWwgY3J1Y2UgZW50cmUgbGFzIHZhcmlhYmxlcyBkZSB1c28gZGUgcmVkZXMgc29jaWFsZXMgeSBsYSB2YXJpYWJsZSB1cmJhbm8uCkxvcyByZXN1bHRhZG9zIGRlIGxhIGZpbGEgVXJiYW5vIGVuIGNhZGEgcmVkIHNvY2lhbCBjb3JyZXNwb25kZXLDrWFuIGEgbGEgcHJpbWVyYSBmaWxhIGRlIHJlc3VsdGFkb3MgZGUgbGEgVGFibGEgMy4yLgoKYGBge3J9CnJvdW5kKHByb3AudGFibGUoc3Z5dGFibGUofnVyYmFuK3dhX3VzZXIsIGRlc2lnbj1kaXNlbm8xOCksIDIpLCAzKSoxMDAKcm91bmQocHJvcC50YWJsZShzdnl0YWJsZSh+dXJiYW4rZmJfdXNlciwgZGVzaWduPWRpc2VubzE4KSwgMiksIDMpKjEwMApyb3VuZChwcm9wLnRhYmxlKHN2eXRhYmxlKH51cmJhbit0d191c2VyLCBkZXNpZ249ZGlzZW5vMTgpLCAyKSwgMykqMTAwCmBgYAoKRGUgZXN0YSBtYW5lcmEgc2UgcHVlZGVuIGNhbGN1bGFyIGxhcyB0YWJsYXMgZGUgZGlzdHJpYnVjacOzbiBkZSBmcmVjdWVuY2lhcyB5IGxhcyB0YWJsYXMgYml2YXJpYWRhcyAobyBkZSBjb250aW5nZW5jaWEpIGluY2x1eWVuZG8gZWwgZWZlY3RvIGRlIGRpc2XDsW8gbyBlbCBmYWN0b3IgZGUgZXhwYW5zacOzbi4K