Introducción
En este documento veremos aspectos básicos de cómo describir una
variable numérica. Para eso, vamos a seguir 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, por país y por ciertas características
sociodemográficas.
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 una base de datos recortada. Esta base de datos se encuentra
alojada en el repositorio “materials_edu” de la cuenta de LAPOP en
GitHub. Se recomienda limpiar el Environment antes de comenzar este
módulo.
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)
Descriptivos para una variable numérica
En la tabla 3.2 del reporte “El pulso de la democracia” se presentan
los promedios generales de las variables edad (“q2” en la base de datos)
y años de estudio (“ed” en la base de datos) para la población
general.
Se usa el comando mean
para calcular el promedio y se
usa na.rm=T
debido a que estas variables cuentan con
valores perdidos.
mean(lapop18$q2, na.rm=T)
## [1] 39.99204
mean(lapop18$ed, na.rm=T)
## [1] 9.934748
En la sección donde trabajamos con variables cualitativas (o de
factor, en el lenguaje de R), vimos que se podía describir las variables
“hombre” y “urbano” definiendo estas variables como factor,
etiquetándolas y haciendo una tabla de frecuencias de estas variables.
Otra manera de encontrar el porcentaje de personas que son hombres o que
viven en el área urbana es trabajar con estas variables, pero no
definirlas como factor. Cuando se crean las variables, ambas son
definidas por defecto como numéricas. En este caso, además se ser
numéricas, son variables de tipo dummy, es decir con valores 0 y 1. En
el caso de la variable “hombre” se ha definido 0=Mujer y 1=Hombre; y en
el caso de la variable “urbano” se ha definido 0=Rural y 1=Urbano. Es
una buena práctica nombrar a la variable dummy con un nombre que refiere
a la categoría 1. Con variables dummy, cuando se calcula el promedio, el
resultado es el mismo que el porcentaje de la categoría 1. Entonces, si
se calcula mean(lapop$hombre, na.rm=T)
, esta operación nos
arroja el porcentaje de la categoría 1, es decir de hombres. Se
multiplica por 100 para ponerlo en formato de 0 a 100.
lapop18$hombre <- 2-lapop18$q1
lapop18$urban <- 2-lapop18$ur
mean(lapop18$hombre, na.rm=T)*100
## [1] 49.74846
mean(lapop18$urban, na.rm=T)*100
## [1] 71.15398
Estos son los datos que se presentan en la primera columna de
resultados de la población general, excepto para la variable riqueza
(“quintall”) que no está disponible en esta versión recortada de la base
de datos.
Gráficos descriptivos
Luego de describir una variable numérica, también puede incluir
algunas gráficas básicas, por ejemplo, usando el comando
hist
se puede producir el histograma de la variable “años
de educación” (ed).
hist(lapop18$ed)
Este mismo gráfico se puede reproducir usando el comando
ggplot
. Con este comando se tiene más flexibilidad con las
opciones gráficas. En primer lugar, se define el dataframe que se usará
y la variable “ed” en el eje X. Luego con la especificación
geom_histogram()
se define usar un histograma. Se define el
ancho de la barra del histograma con banwidth=1
.
Finalmente, este código permite etiquetar el eje X e Y e incluir un tema
en blanco y negro, con theme_bw()
.
library(ggplot2)
ggplot(lapop18, aes(x=ed))+
geom_histogram(binwidth = 1)+
xlab("Años de educación")+
ylab("Frecuencia")+
theme_bw()
Descriptivos de una variable numérica por grupos
Otra manera de describir una variable numérica es usando el comando
summary
. Este comando reporta los estadísticos descriptivos
más usados para una variable numérica: mínimo, máximo, cuartiles, media
y mediana. Todos estos estadísticos permiten una comparación mejor entre
ambos grupos, de usuarios y no usuarios de Facebook. Dentro de este
comando se puede incluir la especificación digits=3
para
redondear los resultados, lo que evita tener que usar
round
, por ejemplo.
summary(lapop18$ed[lapop18$fb_user==0], na.rm=T, digits=3)
## Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
## 0.00 5.00 8.00 8.06 11.00 18.00 1374
summary(lapop18$ed[lapop18$fb_user==1], na.rm=T, digits=3)
## Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
## 0.0 9.0 12.0 11.4 14.0 18.0 1240
Sin embargo, el comando summary
no brinda un estadístico
importante como la desviación estándar, una medida de dispersión o
heterogeneidad. Para poder tener los estadísticos anteriores y que se
incluya la desviación estándar, entre otras medidas adicionales, se
puede usar el comando describeBy
, que es parte de la
librería psych
. Este comando pide la variable a describir
(“ed”) y la variable que forma los grupos (“fb_user”) y brinda la media,
la desviación estándar, la mediana, la media recortada, la desviación
absoluta de la mediana, el mínimo y máximo.
library(psych)
describeBy(lapop18$ed, lapop18$fb_user)
##
## Descriptive statistics by group
## group: 0
## vars n mean sd median trimmed mad min max range skew kurtosis se
## X1 1 11540 8.06 4.3 8 7.99 4.45 0 18 18 0.13 -0.52 0.04
## ------------------------------------------------------------
## group: 1
## vars n mean sd median trimmed mad min max range skew kurtosis se
## X1 1 14998 11.45 3.59 12 11.52 2.97 0 18 18 -0.24 0 0.03
Esta misma información se puede obtener usando el modo de códigos del
tidyverse (con el operador pype %>%
) y se puede guardar
en una tabla. Esta tabla puede guardar los datos de la edad promedio
para los usuarios y no usuarios de Whatsapp y además la desviación
estándar de cada grupo. En primer lugar definimos con qué dataframe se
trabaja. Luego, se indica que no se usen internamente los valores
perdidos de la variable usuarios de Whatsapp con
filter(!is.na(wa_user))
. A continuación se indica que se va
a trabajar en grupos de la variable usuarios de Whatsapp con
group_by(wa_user)
. Finalmente, se indica que en cada grupo
se calculará la media y la desviación estándar, con
summarise
.
library(dplyr)
whatxedad <- lapop18 %>%
filter(!is.na(wa_user)) %>%
group_by(wa_user) %>%
summarise(promedio = mean(q2, na.rm=T), sd = sd(q2, na.rm=T))
whatxedad
Gráficos descriptivos por grupos
El reporte no lo muestra, pero se pueden presentar gráficos para cada
grupo para facilitar la comparación de una variable. Para hacer estos
gráficos comparativos por grupo, vamos a seguir usando el tidyverse.
Igual que en la tabla anterior, se define el dataframe y se indica que
no se tome en cuenta los valores perdidos de la variable “wa_user”.
Luego, se indica que se haga un gráfico, con ggplot
que
tenga la variable “q2” en el eje X. Se define que este gráfico sea un
histograma con geom_histogram()
. Una novedad es que, con la
especificación facet_wrap(~wa_user)
se puede indicar que se
hagan gráficos por cada grupo de esa variable. Finalmente, se etiquetan
los ejes.
lapop18 %>%
filter(!is.na(wa_user)) %>%
ggplot(aes(x=q2))+
geom_histogram()+
facet_wrap(~wa_user)+
xlab("Edad")+
ylab("Frecuencia")
Este gráfico, sin embargo, muestra los valores 0 y 1 de la variable
“wa_user” en el encabezado de ambos gráficos. Esto es debido a que esta
variable, cuando se creó, se definió por defecto como numérica. Para que
aparezcan las etiquetas de la variable, se tiene que transformar
“wa_user” en factor y etiquetarla.
lapop18$wa_user = as.factor(lapop18$wa_user)
levels(lapop18$wa_user) <- c("No usuario", "Usuario")
Otra forma de comparar la distribución de edad por grupos de usuarios
o no usuarios de Whatsapp es mediante un gráfico de cajas o boxplot. Con
el comando boxplot
se puede hacer estos gráficos. El
comando pide primero la variable en el eje Y, luego la variable que
define los grupos y el dataframe. Se puede etiquetar el eje X y Y con
los nombres de las variables. Como la variable “wa_user” ha sido
transformada a factor y etiquetada, ahora aparecen las etiquetas.
boxplot(q2 ~ wa_user, data=lapop18, xlab ="Usuario de Whatsapp", ylab="Edad")
Resumen
En este documento se ha trabajado con variables numéricas, como edad
o años de estudio. Se ha calculado estadísticos descriptivos, como la
media o la desviación estándar para toda la población o por grupos.
Finalmente, se ha presentado formas de graficar estas variables,
mediante histogramas o boxplots.
Cálculos incluyendo el efecto de diseño
Los resultados anteriores no incluyen el factor de expansión. Para
incluirlo en los cálculos se puede usar el comando
weighted.mean
, que es parte de la librería
stats
, que viene precargada con R, por lo que no hay que
instalarla.
weighted.mean(lapop18$q2, lapop18$weight1500, na.rm=T)
## [1] 39.98095
weighted.mean(lapop18$ed, lapop18$weight1500, na.rm=T)
## [1] 9.931417
weighted.mean(lapop18$hombre, lapop18$weight1500, na.rm=T)*100
## [1] 49.74826
weighted.mean(lapop18$urban, lapop18$weight1500, na.rm=T)*100
## [1] 71.11895
Otra forma de calcular la media incluyendo el factor de expansión es
mediante de el uso de la librería survey
y el comando
nativo svymean
. Para esto se tiene que definir el diseño
muestral con el comando svydesign
y guardar este diseño en
un objeto, aquí llamado “lapop.design”.
library(survey)
diseno18 <-svydesign(ids = ~upm, strata = ~estratopri, weights = ~weight1500, nest=TRUE, data=lapop18)
Para calcular el promedio, se usa el comando svymean
y
se usa la especificación na.rm=T
debido a que estas
variables cuentan con valores perdidos.
svymean(~q2, diseno18, na.rm=T)
## mean SE
## q2 39.981 0.0535
svymean(~ed, diseno18, na.rm=T)
## mean SE
## ed 9.9314 0.04
Para las variables dummies el procedimiento es el mismo, salvo que se
le multiplica por 100 para presentarlo en formato de porcentaje
svymean(~hombre, diseno18, na.rm =T)*100
## mean SE
## hombre 49.748 8e-04
svymean(~urban, diseno18, na.rm=T)*100
## mean SE
## urban 71.119 0.0076
El paquete survey
también tiene comandos para replicar
gráficos. Por ejemplo, para calcular un histograma simple.
svyhist(~ed, diseno18, freq = T)
Para calcular estadísticos descriptivos por grupos, se puede usar el
comando svyby
, que permite definir la variable numérica que
se quiere describir, la variable que define los grupos y el estadístico
ponderado que se quiere calcular.
svyby(~ed, ~fb_user, diseno18, svymean, na.rm=T)
Para reproducir un gráfico descriptivo por grupos, se puede usar el
comando svyboxplot
para comparar la distribución de la
variable edad entre grupos de una variable de tipo factor, como usuarios
de Whatsapp.
svyboxplot(~q2~factor(wa_user), diseno18, all.outliers = T)
LS0tCnRpdGxlOiAiRXN0YWTDrXN0aWNhIGRlc2NyaXB0aXZhIHVzYW5kbyBlbCBCYXLDs21ldHJvIGRlIGxhcyBBbcOpcmljYXMgKDMpIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICBjb2xsYXBzZWQ6IGZhbHNlCiAgICBudW1iZXJfc2VjdGlvbnM6IGZhbHNlCiAgICB0b2NfZGVwdGg6IDEKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgIHRoZW1lOiBmbGF0bHkKICAgIGRmX3ByaW50OiBwYWdlZAogICAgc2VsZl9jb250YWluZWQ6IG5vCiAgICBrZWVwX21kOiB5ZXMKICAgICNjb2RlX2ZvbGRpbmc6IGhpZGUKZWRpdG9yX29wdGlvbnM6IAogIG1hcmtkb3duOiAKICAgIHdyYXA6IHNlbnRlbmNlCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0UsIGNhY2hlPVRSVUUpCmBgYAoKYGBge2NzcyBjb2xvciwgZWNobz1GQUxTRX0KLmNvbHVtbnMge2Rpc3BsYXk6IGZsZXg7fQpoMSB7Y29sb3I6ICMzMzY2Q0M7fQpgYGAKCiMgSW50cm9kdWNjacOzbgoKRW4gZXN0ZSBkb2N1bWVudG8gdmVyZW1vcyBhc3BlY3RvcyBiw6FzaWNvcyBkZSBjw7NtbyBkZXNjcmliaXIgdW5hIHZhcmlhYmxlIG51bcOpcmljYS4KUGFyYSBlc28sIHZhbW9zIGEgc2VndWlyIHVzYW5kbyBlbCDDumx0aW1vIGluZm9ybWUgcmVnaW9uYWwgIkVsIHB1bHNvIGRlIGxhIGRlbW9jcmFjaWEiLCBkaXNwb25pYmxlIFthcXXDrV0oaHR0cHM6Ly93d3cudmFuZGVyYmlsdC5lZHUvbGFwb3AvYWIyMDE4LzIwMTgtMTlfQW1lcmljYXNCYXJvbWV0ZXJfUmVnaW9uYWxfUmVwb3J0X1NwYW5pc2hfV18wMy4yNy4yMC5wZGYpLCBkb25kZSBzZSBwcmVzZW50YW4gbG9zIHByaW5jaXBhbGVzIGhhbGxhemdvcyBkZSBsYSByb25kYSAyMDE4LzE5IGRlbCBCYXLDs21ldHJvIGRlIGxhcyBBbcOpcmljYXMuClVuYSBkZSBsYXMgc2VjY2lvbmVzIGRlIGVzdGUgaW5mb3JtZSwgcmVwb3J0YSBsb3MgZGF0b3Mgc29icmUgcmVkZXMgc29jaWFsZXMgeSBhY3RpdHVkZXMgcG9sw610aWNhcy4KRW4gZXN0YSBzZWNjacOzbiwgc2UgcHJlc2VudGFuIGRhdG9zIHNvYnJlIGVsIHVzbyBkZSBpbnRlcm5ldCB5IGVsIHVzbyBkZSByZWRlcyBzb2NpYWxlcywgZW4gZ2VuZXJhbCwgcG9yIHBhw61zIHkgcG9yIGNpZXJ0YXMgY2FyYWN0ZXLDrXN0aWNhcyBzb2Npb2RlbW9ncsOhZmljYXMuCgojIFNvYnJlIGxhIGJhc2UgZGUgZGF0b3MKCkxvcyBkYXRvcyBxdWUgdmFtb3MgYSB1c2FyIGRlYmVuIGNpdGFyc2UgZGUgbGEgc2lndWllbnRlIG1hbmVyYTogRnVlbnRlOiBCYXLDs21ldHJvIGRlIGxhcyBBbcOpcmljYXMgcG9yIGVsIFByb3llY3RvIGRlIE9waW5pw7NuIFDDumJsaWNhIGRlIEFtw6lyaWNhIExhdGluYSAoTEFQT1ApLCB3d3d3LkxhcG9wU3VydmV5cy5vcmcuCkVuIGVzdGUgZG9jdW1lbnRvIHNlIGNhcmdhIHVuYSBiYXNlIGRlIGRhdG9zIHJlY29ydGFkYS4KRXN0YSBiYXNlIGRlIGRhdG9zIHNlIGVuY3VlbnRyYSBhbG9qYWRhIGVuIGVsIHJlcG9zaXRvcmlvICJtYXRlcmlhbHNfZWR1IiBkZSBsYSBjdWVudGEgZGUgTEFQT1AgZW4gR2l0SHViLgpTZSByZWNvbWllbmRhIGxpbXBpYXIgZWwgRW52aXJvbm1lbnQgYW50ZXMgZGUgY29tZW56YXIgZXN0ZSBtw7NkdWxvLgoKTWVkaWFudGUgbGEgbGlicmVyw61hIGByaW9gIHkgZWwgY29tYW5kbyBgaW1wb3J0YCBzZSBwdWVkZSBpbXBvcnRhciBlc3RhIGJhc2UgZGUgZGF0b3MgZGVzZGUgZXN0ZSByZXBvc2l0b3Jpby4KQWRlbcOhcywgc2Ugc2VsZWNjaW9uYW4gbG9zIGRhdG9zIGRlIHBhw61zZXMgY29uIGPDs2RpZ29zIG1lbm9yZXMgbyBpZ3VhbGVzIGEgMzUsIGVzIGRlY2lyLCBzZSBlbGltaW5hIGxhcyBvYnNlcnZhY2lvbmVzIGRlIEVzdGFkb3MgVW5pZG9zIHkgQ2FuYWTDoS4KCmBgYHtyIGJhc2V9CmxpYnJhcnkocmlvKQpsYXBvcDE4IDwtIGltcG9ydCgiaHR0cHM6Ly9yYXcuZ2l0aHViLmNvbS9sYXBvcC1jZW50cmFsL21hdGVyaWFsc19lZHUvbWFpbi9MQVBPUF9BQl9NZXJnZV8yMDE4X3YxLjAuc2F2IikKbGFwb3AxOCA8LSBzdWJzZXQobGFwb3AxOCwgcGFpczw9MzUpCmBgYAoKIyBEZXNjcmlwdGl2b3MgcGFyYSB1bmEgdmFyaWFibGUgbnVtw6lyaWNhCgpFbiBsYSB0YWJsYSAzLjIgZGVsIHJlcG9ydGUgIkVsIHB1bHNvIGRlIGxhIGRlbW9jcmFjaWEiIHNlIHByZXNlbnRhbiBsb3MgcHJvbWVkaW9zIGdlbmVyYWxlcyBkZSBsYXMgdmFyaWFibGVzIGVkYWQgKCJxMiIgZW4gbGEgYmFzZSBkZSBkYXRvcykgeSBhw7FvcyBkZSBlc3R1ZGlvICgiZWQiIGVuIGxhIGJhc2UgZGUgZGF0b3MpIHBhcmEgbGEgcG9ibGFjacOzbiBnZW5lcmFsLgoKIVtdKFRhYmxhMy4yLnBuZyl7d2lkdGg9IjY5MSJ9CgpTZSB1c2EgZWwgY29tYW5kbyBgbWVhbmAgcGFyYSBjYWxjdWxhciBlbCBwcm9tZWRpbyB5IHNlIHVzYSBgbmEucm09VGAgZGViaWRvIGEgcXVlIGVzdGFzIHZhcmlhYmxlcyBjdWVudGFuIGNvbiB2YWxvcmVzIHBlcmRpZG9zLgoKYGBge3IgbWVkaWF9Cm1lYW4obGFwb3AxOCRxMiwgbmEucm09VCkKbWVhbihsYXBvcDE4JGVkLCBuYS5ybT1UKQpgYGAKCkVuIGxhIHNlY2Npw7NuIGRvbmRlIHRyYWJhamFtb3MgY29uIHZhcmlhYmxlcyBjdWFsaXRhdGl2YXMgKG8gZGUgZmFjdG9yLCBlbiBlbCBsZW5ndWFqZSBkZSBSKSwgdmltb3MgcXVlIHNlIHBvZMOtYSBkZXNjcmliaXIgbGFzIHZhcmlhYmxlcyAiaG9tYnJlIiB5ICJ1cmJhbm8iIGRlZmluaWVuZG8gZXN0YXMgdmFyaWFibGVzIGNvbW8gZmFjdG9yLCBldGlxdWV0w6FuZG9sYXMgeSBoYWNpZW5kbyB1bmEgdGFibGEgZGUgZnJlY3VlbmNpYXMgZGUgZXN0YXMgdmFyaWFibGVzLgpPdHJhIG1hbmVyYSBkZSBlbmNvbnRyYXIgZWwgcG9yY2VudGFqZSBkZSBwZXJzb25hcyBxdWUgc29uIGhvbWJyZXMgbyBxdWUgdml2ZW4gZW4gZWwgw6FyZWEgdXJiYW5hIGVzIHRyYWJhamFyIGNvbiBlc3RhcyB2YXJpYWJsZXMsIHBlcm8gbm8gZGVmaW5pcmxhcyBjb21vIGZhY3Rvci4KQ3VhbmRvIHNlIGNyZWFuIGxhcyB2YXJpYWJsZXMsIGFtYmFzIHNvbiBkZWZpbmlkYXMgcG9yIGRlZmVjdG8gY29tbyBudW3DqXJpY2FzLgpFbiBlc3RlIGNhc28sIGFkZW3DoXMgc2Ugc2VyIG51bcOpcmljYXMsIHNvbiB2YXJpYWJsZXMgZGUgdGlwbyBkdW1teSwgZXMgZGVjaXIgY29uIHZhbG9yZXMgMCB5IDEuCkVuIGVsIGNhc28gZGUgbGEgdmFyaWFibGUgImhvbWJyZSIgc2UgaGEgZGVmaW5pZG8gMD1NdWplciB5IDE9SG9tYnJlOyB5IGVuIGVsIGNhc28gZGUgbGEgdmFyaWFibGUgInVyYmFubyIgc2UgaGEgZGVmaW5pZG8gMD1SdXJhbCB5IDE9VXJiYW5vLgpFcyB1bmEgYnVlbmEgcHLDoWN0aWNhIG5vbWJyYXIgYSBsYSB2YXJpYWJsZSBkdW1teSBjb24gdW4gbm9tYnJlIHF1ZSByZWZpZXJlIGEgbGEgY2F0ZWdvcsOtYSAxLgpDb24gdmFyaWFibGVzIGR1bW15LCBjdWFuZG8gc2UgY2FsY3VsYSBlbCBwcm9tZWRpbywgZWwgcmVzdWx0YWRvIGVzIGVsIG1pc21vIHF1ZSBlbCBwb3JjZW50YWplIGRlIGxhIGNhdGVnb3LDrWEgMS4KRW50b25jZXMsIHNpIHNlIGNhbGN1bGEgYG1lYW4obGFwb3AkaG9tYnJlLCBuYS5ybT1UKWAsIGVzdGEgb3BlcmFjacOzbiBub3MgYXJyb2phIGVsIHBvcmNlbnRhamUgZGUgbGEgY2F0ZWdvcsOtYSAxLCBlcyBkZWNpciBkZSBob21icmVzLgpTZSBtdWx0aXBsaWNhIHBvciAxMDAgcGFyYSBwb25lcmxvIGVuIGZvcm1hdG8gZGUgMCBhIDEwMC4KCmBgYHtyIHByb21lZGlvIGR1bW15fQpsYXBvcDE4JGhvbWJyZSA8LSAyLWxhcG9wMTgkcTEKbGFwb3AxOCR1cmJhbiA8LSAyLWxhcG9wMTgkdXIKbWVhbihsYXBvcDE4JGhvbWJyZSwgbmEucm09VCkqMTAwCm1lYW4obGFwb3AxOCR1cmJhbiwgbmEucm09VCkqMTAwCmBgYAoKRXN0b3Mgc29uIGxvcyBkYXRvcyBxdWUgc2UgcHJlc2VudGFuIGVuIGxhIHByaW1lcmEgY29sdW1uYSBkZSByZXN1bHRhZG9zIGRlIGxhIHBvYmxhY2nDs24gZ2VuZXJhbCwgZXhjZXB0byBwYXJhIGxhIHZhcmlhYmxlIHJpcXVlemEgKCJxdWludGFsbCIpIHF1ZSBubyBlc3TDoSBkaXNwb25pYmxlIGVuIGVzdGEgdmVyc2nDs24gcmVjb3J0YWRhIGRlIGxhIGJhc2UgZGUgZGF0b3MuCgojIEdyw6FmaWNvcyBkZXNjcmlwdGl2b3MKCkx1ZWdvIGRlIGRlc2NyaWJpciB1bmEgdmFyaWFibGUgbnVtw6lyaWNhLCB0YW1iacOpbiBwdWVkZSBpbmNsdWlyIGFsZ3VuYXMgZ3LDoWZpY2FzIGLDoXNpY2FzLCBwb3IgZWplbXBsbywgdXNhbmRvIGVsIGNvbWFuZG8gYGhpc3RgIHNlIHB1ZWRlIHByb2R1Y2lyIGVsIGhpc3RvZ3JhbWEgZGUgbGEgdmFyaWFibGUgImHDsW9zIGRlIGVkdWNhY2nDs24iIChlZCkuCgpgYGB7ciBoaXN0b2dyYW1hIHNpbXBsZX0KaGlzdChsYXBvcDE4JGVkKQpgYGAKCkVzdGUgbWlzbW8gZ3LDoWZpY28gc2UgcHVlZGUgcmVwcm9kdWNpciB1c2FuZG8gZWwgY29tYW5kbyBgZ2dwbG90YC4KQ29uIGVzdGUgY29tYW5kbyBzZSB0aWVuZSBtw6FzIGZsZXhpYmlsaWRhZCBjb24gbGFzIG9wY2lvbmVzIGdyw6FmaWNhcy4KRW4gcHJpbWVyIGx1Z2FyLCBzZSBkZWZpbmUgZWwgZGF0YWZyYW1lIHF1ZSBzZSB1c2Fyw6EgeSBsYSB2YXJpYWJsZSAiZWQiIGVuIGVsIGVqZSBYLgpMdWVnbyBjb24gbGEgZXNwZWNpZmljYWNpw7NuIGBnZW9tX2hpc3RvZ3JhbSgpYCBzZSBkZWZpbmUgdXNhciB1biBoaXN0b2dyYW1hLgpTZSBkZWZpbmUgZWwgYW5jaG8gZGUgbGEgYmFycmEgZGVsIGhpc3RvZ3JhbWEgY29uIGBiYW53aWR0aD0xYC4KRmluYWxtZW50ZSwgZXN0ZSBjw7NkaWdvIHBlcm1pdGUgZXRpcXVldGFyIGVsIGVqZSBYIGUgWSBlIGluY2x1aXIgdW4gdGVtYSBlbiBibGFuY28geSBuZWdybywgY29uIGB0aGVtZV9idygpYC4KCmBgYHtyIGdnaGlzdCwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeShnZ3Bsb3QyKQpnZ3Bsb3QobGFwb3AxOCwgYWVzKHg9ZWQpKSsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDEpKwogIHhsYWIoIkHDsW9zIGRlIGVkdWNhY2nDs24iKSsKICB5bGFiKCJGcmVjdWVuY2lhIikrCiAgdGhlbWVfYncoKQpgYGAKCiMgTWVkaWEgcG9yIGdydXBvcwoKRW4gbGEgVGFibGEzLjIgZGVsIHJlcG9ydGUsIHNlIHByZXNlbnRhbiBsYSBtZWRpYSBkZSBlc3RhcyB2YXJpYWJsZXMgbnVtw6lyaWNhcyBwb3IgZ3J1cG9zIGRlIGxhcyB2YXJpYWJsZXMgcmVsYWNpb25hZGFzIGEgbGFzIHJlZGVzIHNvY2lhbGVzLgpFcyBkZWNpciwgcG9yIGVqZW1wbG8sIGVsIHByb21lZGlvIGRlIGHDsW9zIGRlIGVzdHVkaW8gcGFyYSBsb3MgdXN1YXJpb3MgZGUgRmFjZWJvb2sgeSBwYXJhIGxvcyBubyB1c3VhcmlvcyBkZSBGYWNlYm9vay4KU2kgcXVlcmVtb3MgY2FsY3VsYXIgZWwgcHJvbWVkaW8gZGUgYcOxb3MgZGUgZXN0dWRpbyBwYXJhIGxvcyB1c3VhcmlvcyBkZSBGYWNlYm9vaywgcHJpbWVybyBzZSBjYWxjdWxhIGVzdGEgdmFyaWFibGUsIGRlIGxhIG1pc21hIG1hbmVyYSBxdWUgZW4gc2VjY2lvbmVzIGFudGVyaW9yZXMsIGNvbiBlbCBjb21hbmRvIGBpZmVsc2VgLgoKYGBge3IgdXN1YXJpb3N9CmxhcG9wMTgkZmJfdXNlciA8LSBpZmVsc2UobGFwb3AxOCRzbWVkaWExPT0xICYgbGFwb3AxOCRzbWVkaWEyPD00LCAxLCAwKQpsYXBvcDE4JHR3X3VzZXIgPC0gaWZlbHNlKGxhcG9wMTgkc21lZGlhND09MSAmIGxhcG9wMTgkc21lZGlhNTw9NCwgMSwgMCkKbGFwb3AxOCR3YV91c2VyIDwtIGlmZWxzZShsYXBvcDE4JHNtZWRpYTc9PTEgJiBsYXBvcDE4JHNtZWRpYTg8PTQsIDEsIDApCmBgYAoKRWwgY8OhbGN1bG8gZGVsIHByb21lZGlvIGRlIGHDsW9zIHBhcmEgbG9zIHVzdWFyaW9zIHkgbm8gdXN1YXJpb3MgZGUgRmFjZWJvb2sgc2UgcHVlZGUgaGFjZXIgZGUgbXVjaGFzIG1hbmVyYXMuClVuYSBwcmltZXJhIGVzIHVzYW5kbyBsb3MgY29yY2hldGVzIGBbLi4uXWAuCkVuIGVzdGUgY2FzbywgY2FsY3VsYXJlbW9zIGVsIHByb21lZGlvIGRlIGHDsW9zIGRlIGVzdHVkaW8gcG9yIGdydXBvcyBkZSB1c3VhcmlvcyBgW2xhcG9wMTgkZmJfdXNlcj09MV1gIHkgbm8gdXN1YXJpb3MgZGUgRmFjZWJvb2sgYFtsYXBvcDE4JGZiX3VzZXI9PTBdYC4KCmBgYHtyIGHDsW9zIGRlIGVzdHVkaW8gZGUgRmJ9Cm1lYW4obGFwb3AxOCRlZFtsYXBvcDE4JGZiX3VzZXI9PTBdLCBuYS5ybT1UKQptZWFuKGxhcG9wMTgkZWRbbGFwb3AxOCRmYl91c2VyPT0xXSwgbmEucm09VCkKYGBgCgojIERlc2NyaXB0aXZvcyBkZSB1bmEgdmFyaWFibGUgbnVtw6lyaWNhIHBvciBncnVwb3MKCk90cmEgbWFuZXJhIGRlIGRlc2NyaWJpciB1bmEgdmFyaWFibGUgbnVtw6lyaWNhIGVzIHVzYW5kbyBlbCBjb21hbmRvIGBzdW1tYXJ5YC4KRXN0ZSBjb21hbmRvIHJlcG9ydGEgbG9zIGVzdGFkw61zdGljb3MgZGVzY3JpcHRpdm9zIG3DoXMgdXNhZG9zIHBhcmEgdW5hIHZhcmlhYmxlIG51bcOpcmljYTogbcOtbmltbywgbcOheGltbywgY3VhcnRpbGVzLCBtZWRpYSB5IG1lZGlhbmEuClRvZG9zIGVzdG9zIGVzdGFkw61zdGljb3MgcGVybWl0ZW4gdW5hIGNvbXBhcmFjacOzbiBtZWpvciBlbnRyZSBhbWJvcyBncnVwb3MsIGRlIHVzdWFyaW9zIHkgbm8gdXN1YXJpb3MgZGUgRmFjZWJvb2suCkRlbnRybyBkZSBlc3RlIGNvbWFuZG8gc2UgcHVlZGUgaW5jbHVpciBsYSBlc3BlY2lmaWNhY2nDs24gYGRpZ2l0cz0zYCBwYXJhIHJlZG9uZGVhciBsb3MgcmVzdWx0YWRvcywgbG8gcXVlIGV2aXRhIHRlbmVyIHF1ZSB1c2FyIGByb3VuZGAsIHBvciBlamVtcGxvLgoKYGBge3J9CnN1bW1hcnkobGFwb3AxOCRlZFtsYXBvcDE4JGZiX3VzZXI9PTBdLCBuYS5ybT1ULCBkaWdpdHM9MykKc3VtbWFyeShsYXBvcDE4JGVkW2xhcG9wMTgkZmJfdXNlcj09MV0sIG5hLnJtPVQsIGRpZ2l0cz0zKQpgYGAKClNpbiBlbWJhcmdvLCBlbCBjb21hbmRvIGBzdW1tYXJ5YCBubyBicmluZGEgdW4gZXN0YWTDrXN0aWNvIGltcG9ydGFudGUgY29tbyBsYSBkZXN2aWFjacOzbiBlc3TDoW5kYXIsIHVuYSBtZWRpZGEgZGUgZGlzcGVyc2nDs24gbyBoZXRlcm9nZW5laWRhZC4KUGFyYSBwb2RlciB0ZW5lciBsb3MgZXN0YWTDrXN0aWNvcyBhbnRlcmlvcmVzIHkgcXVlIHNlIGluY2x1eWEgbGEgZGVzdmlhY2nDs24gZXN0w6FuZGFyLCBlbnRyZSBvdHJhcyBtZWRpZGFzIGFkaWNpb25hbGVzLCBzZSBwdWVkZSB1c2FyIGVsIGNvbWFuZG8gYGRlc2NyaWJlQnlgLCBxdWUgZXMgcGFydGUgZGUgbGEgbGlicmVyw61hIGBwc3ljaGAuCkVzdGUgY29tYW5kbyBwaWRlIGxhIHZhcmlhYmxlIGEgZGVzY3JpYmlyICgiZWQiKSB5IGxhIHZhcmlhYmxlIHF1ZSBmb3JtYSBsb3MgZ3J1cG9zICgiZmJfdXNlciIpIHkgYnJpbmRhIGxhIG1lZGlhLCBsYSBkZXN2aWFjacOzbiBlc3TDoW5kYXIsIGxhIG1lZGlhbmEsIGxhIG1lZGlhIHJlY29ydGFkYSwgbGEgZGVzdmlhY2nDs24gYWJzb2x1dGEgZGUgbGEgbWVkaWFuYSwgZWwgbcOtbmltbyB5IG3DoXhpbW8uCgpgYGB7ciBtZWRpYSBhw7FvcyBkZSBlc3R1ZGlvIHBvciBGQiwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeShwc3ljaCkKZGVzY3JpYmVCeShsYXBvcDE4JGVkLCBsYXBvcDE4JGZiX3VzZXIpCmBgYAoKRXN0YSBtaXNtYSBpbmZvcm1hY2nDs24gc2UgcHVlZGUgb2J0ZW5lciB1c2FuZG8gZWwgbW9kbyBkZSBjw7NkaWdvcyBkZWwgdGlkeXZlcnNlIChjb24gZWwgb3BlcmFkb3IgcHlwZSBgJT4lYCkgeSBzZSBwdWVkZSBndWFyZGFyIGVuIHVuYSB0YWJsYS4KRXN0YSB0YWJsYSBwdWVkZSBndWFyZGFyIGxvcyBkYXRvcyBkZSBsYSBlZGFkIHByb21lZGlvIHBhcmEgbG9zIHVzdWFyaW9zIHkgbm8gdXN1YXJpb3MgZGUgV2hhdHNhcHAgeSBhZGVtw6FzIGxhIGRlc3ZpYWNpw7NuIGVzdMOhbmRhciBkZSBjYWRhIGdydXBvLgpFbiBwcmltZXIgbHVnYXIgZGVmaW5pbW9zIGNvbiBxdcOpIGRhdGFmcmFtZSBzZSB0cmFiYWphLgpMdWVnbywgc2UgaW5kaWNhIHF1ZSBubyBzZSB1c2VuIGludGVybmFtZW50ZSBsb3MgdmFsb3JlcyBwZXJkaWRvcyBkZSBsYSB2YXJpYWJsZSB1c3VhcmlvcyBkZSBXaGF0c2FwcCBjb24gYGZpbHRlcighaXMubmEod2FfdXNlcikpYC4KQSBjb250aW51YWNpw7NuIHNlIGluZGljYSBxdWUgc2UgdmEgYSB0cmFiYWphciBlbiBncnVwb3MgZGUgbGEgdmFyaWFibGUgdXN1YXJpb3MgZGUgV2hhdHNhcHAgY29uIGBncm91cF9ieSh3YV91c2VyKWAuCkZpbmFsbWVudGUsIHNlIGluZGljYSBxdWUgZW4gY2FkYSBncnVwbyBzZSBjYWxjdWxhcsOhIGxhIG1lZGlhIHkgbGEgZGVzdmlhY2nDs24gZXN0w6FuZGFyLCBjb24gYHN1bW1hcmlzZWAuCgpgYGB7ciBtZWRpYSBhw7FvcyB4IFdoYXRzYXBwLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KGRwbHlyKQp3aGF0eGVkYWQgPC0gbGFwb3AxOCAlPiUKICBmaWx0ZXIoIWlzLm5hKHdhX3VzZXIpKSAlPiUKICBncm91cF9ieSh3YV91c2VyKSAlPiUKICBzdW1tYXJpc2UocHJvbWVkaW8gPSBtZWFuKHEyLCBuYS5ybT1UKSwgc2QgPSBzZChxMiwgbmEucm09VCkpCndoYXR4ZWRhZApgYGAKCiMgR3LDoWZpY29zIGRlc2NyaXB0aXZvcyBwb3IgZ3J1cG9zCgpFbCByZXBvcnRlIG5vIGxvIG11ZXN0cmEsIHBlcm8gc2UgcHVlZGVuIHByZXNlbnRhciBncsOhZmljb3MgcGFyYSBjYWRhIGdydXBvIHBhcmEgZmFjaWxpdGFyIGxhIGNvbXBhcmFjacOzbiBkZSB1bmEgdmFyaWFibGUuClBhcmEgaGFjZXIgZXN0b3MgZ3LDoWZpY29zIGNvbXBhcmF0aXZvcyBwb3IgZ3J1cG8sIHZhbW9zIGEgc2VndWlyIHVzYW5kbyBlbCB0aWR5dmVyc2UuCklndWFsIHF1ZSBlbiBsYSB0YWJsYSBhbnRlcmlvciwgc2UgZGVmaW5lIGVsIGRhdGFmcmFtZSB5IHNlIGluZGljYSBxdWUgbm8gc2UgdG9tZSBlbiBjdWVudGEgbG9zIHZhbG9yZXMgcGVyZGlkb3MgZGUgbGEgdmFyaWFibGUgIndhX3VzZXIiLgpMdWVnbywgc2UgaW5kaWNhIHF1ZSBzZSBoYWdhIHVuIGdyw6FmaWNvLCBjb24gYGdncGxvdGAgcXVlIHRlbmdhIGxhIHZhcmlhYmxlICJxMiIgZW4gZWwgZWplIFguClNlIGRlZmluZSBxdWUgZXN0ZSBncsOhZmljbyBzZWEgdW4gaGlzdG9ncmFtYSBjb24gYGdlb21faGlzdG9ncmFtKClgLgpVbmEgbm92ZWRhZCBlcyBxdWUsIGNvbiBsYSBlc3BlY2lmaWNhY2nDs24gYGZhY2V0X3dyYXAofndhX3VzZXIpYCBzZSBwdWVkZSBpbmRpY2FyIHF1ZSBzZSBoYWdhbiBncsOhZmljb3MgcG9yIGNhZGEgZ3J1cG8gZGUgZXNhIHZhcmlhYmxlLgpGaW5hbG1lbnRlLCBzZSBldGlxdWV0YW4gbG9zIGVqZXMuCgpgYGB7ciBoaXN0IGVkYWR4d2hhdCwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGFwb3AxOCAlPiUKICBmaWx0ZXIoIWlzLm5hKHdhX3VzZXIpKSAlPiUKICBnZ3Bsb3QoYWVzKHg9cTIpKSsKICBnZW9tX2hpc3RvZ3JhbSgpKwogIGZhY2V0X3dyYXAofndhX3VzZXIpKwogIHhsYWIoIkVkYWQiKSsKICB5bGFiKCJGcmVjdWVuY2lhIikKYGBgCgpFc3RlIGdyw6FmaWNvLCBzaW4gZW1iYXJnbywgbXVlc3RyYSBsb3MgdmFsb3JlcyAwIHkgMSBkZSBsYSB2YXJpYWJsZSAid2FfdXNlciIgZW4gZWwgZW5jYWJlemFkbyBkZSBhbWJvcyBncsOhZmljb3MuCkVzdG8gZXMgZGViaWRvIGEgcXVlIGVzdGEgdmFyaWFibGUsIGN1YW5kbyBzZSBjcmXDsywgc2UgZGVmaW5pw7MgcG9yIGRlZmVjdG8gY29tbyBudW3DqXJpY2EuClBhcmEgcXVlIGFwYXJlemNhbiBsYXMgZXRpcXVldGFzIGRlIGxhIHZhcmlhYmxlLCBzZSB0aWVuZSBxdWUgdHJhbnNmb3JtYXIgIndhX3VzZXIiIGVuIGZhY3RvciB5IGV0aXF1ZXRhcmxhLgoKYGBge3Igd2EgZmFjdG9yfQpsYXBvcDE4JHdhX3VzZXIgPSBhcy5mYWN0b3IobGFwb3AxOCR3YV91c2VyKQpsZXZlbHMobGFwb3AxOCR3YV91c2VyKSA8LSBjKCJObyB1c3VhcmlvIiwgIlVzdWFyaW8iKQpgYGAKCk90cmEgZm9ybWEgZGUgY29tcGFyYXIgbGEgZGlzdHJpYnVjacOzbiBkZSBlZGFkIHBvciBncnVwb3MgZGUgdXN1YXJpb3MgbyBubyB1c3VhcmlvcyBkZSBXaGF0c2FwcCBlcyBtZWRpYW50ZSB1biBncsOhZmljbyBkZSBjYWphcyBvIGJveHBsb3QuCkNvbiBlbCBjb21hbmRvIGBib3hwbG90YCBzZSBwdWVkZSBoYWNlciBlc3RvcyBncsOhZmljb3MuCkVsIGNvbWFuZG8gcGlkZSBwcmltZXJvIGxhIHZhcmlhYmxlIGVuIGVsIGVqZSBZLCBsdWVnbyBsYSB2YXJpYWJsZSBxdWUgZGVmaW5lIGxvcyBncnVwb3MgeSBlbCBkYXRhZnJhbWUuClNlIHB1ZWRlIGV0aXF1ZXRhciBlbCBlamUgWCB5IFkgY29uIGxvcyBub21icmVzIGRlIGxhcyB2YXJpYWJsZXMuCkNvbW8gbGEgdmFyaWFibGUgIndhX3VzZXIiIGhhIHNpZG8gdHJhbnNmb3JtYWRhIGEgZmFjdG9yIHkgZXRpcXVldGFkYSwgYWhvcmEgYXBhcmVjZW4gbGFzIGV0aXF1ZXRhcy4KCmBgYHtyIGJveHBsb3QgZWRhZHhXaGEsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmJveHBsb3QocTIgfiB3YV91c2VyLCBkYXRhPWxhcG9wMTgsIHhsYWIgPSJVc3VhcmlvIGRlIFdoYXRzYXBwIiwgeWxhYj0iRWRhZCIpCmBgYAoKIyBSZXN1bWVuCgpFbiBlc3RlIGRvY3VtZW50byBzZSBoYSB0cmFiYWphZG8gY29uIHZhcmlhYmxlcyBudW3DqXJpY2FzLCBjb21vIGVkYWQgbyBhw7FvcyBkZSBlc3R1ZGlvLgpTZSBoYSBjYWxjdWxhZG8gZXN0YWTDrXN0aWNvcyBkZXNjcmlwdGl2b3MsIGNvbW8gbGEgbWVkaWEgbyBsYSBkZXN2aWFjacOzbiBlc3TDoW5kYXIgcGFyYSB0b2RhIGxhIHBvYmxhY2nDs24gbyBwb3IgZ3J1cG9zLgpGaW5hbG1lbnRlLCBzZSBoYSBwcmVzZW50YWRvIGZvcm1hcyBkZSBncmFmaWNhciBlc3RhcyB2YXJpYWJsZXMsIG1lZGlhbnRlIGhpc3RvZ3JhbWFzIG8gYm94cGxvdHMuCgojIEPDoWxjdWxvcyBpbmNsdXllbmRvIGVsIGVmZWN0byBkZSBkaXNlw7FvCgpMb3MgcmVzdWx0YWRvcyBhbnRlcmlvcmVzIG5vIGluY2x1eWVuIGVsIGZhY3RvciBkZSBleHBhbnNpw7NuLgpQYXJhIGluY2x1aXJsbyBlbiBsb3MgY8OhbGN1bG9zIHNlIHB1ZWRlIHVzYXIgZWwgY29tYW5kbyBgd2VpZ2h0ZWQubWVhbmAsIHF1ZSBlcyBwYXJ0ZSBkZSBsYSBsaWJyZXLDrWEgYHN0YXRzYCwgcXVlIHZpZW5lIHByZWNhcmdhZGEgY29uIFIsIHBvciBsbyBxdWUgbm8gaGF5IHF1ZSBpbnN0YWxhcmxhLgoKYGBge3IgY29tYW5kbyB3ZWlnaHRlZH0Kd2VpZ2h0ZWQubWVhbihsYXBvcDE4JHEyLCBsYXBvcDE4JHdlaWdodDE1MDAsIG5hLnJtPVQpCndlaWdodGVkLm1lYW4obGFwb3AxOCRlZCwgbGFwb3AxOCR3ZWlnaHQxNTAwLCBuYS5ybT1UKQp3ZWlnaHRlZC5tZWFuKGxhcG9wMTgkaG9tYnJlLCBsYXBvcDE4JHdlaWdodDE1MDAsIG5hLnJtPVQpKjEwMAp3ZWlnaHRlZC5tZWFuKGxhcG9wMTgkdXJiYW4sIGxhcG9wMTgkd2VpZ2h0MTUwMCwgbmEucm09VCkqMTAwCmBgYAoKT3RyYSBmb3JtYSBkZSBjYWxjdWxhciBsYSBtZWRpYSBpbmNsdXllbmRvIGVsIGZhY3RvciBkZSBleHBhbnNpw7NuIGVzIG1lZGlhbnRlIGRlIGVsIHVzbyBkZSBsYSBsaWJyZXLDrWEgYHN1cnZleWAgeSBlbCBjb21hbmRvIG5hdGl2byBgc3Z5bWVhbmAuClBhcmEgZXN0byBzZSB0aWVuZSBxdWUgZGVmaW5pciBlbCBkaXNlw7FvIG11ZXN0cmFsIGNvbiBlbCBjb21hbmRvIGBzdnlkZXNpZ25gIHkgZ3VhcmRhciBlc3RlIGRpc2XDsW8gZW4gdW4gb2JqZXRvLCBhcXXDrSBsbGFtYWRvICJsYXBvcC5kZXNpZ24iLgoKYGBge3Igc3VydmV5LCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KHN1cnZleSkKZGlzZW5vMTggPC1zdnlkZXNpZ24oaWRzID0gfnVwbSwgc3RyYXRhID0gfmVzdHJhdG9wcmksIHdlaWdodHMgPSB+d2VpZ2h0MTUwMCwgbmVzdD1UUlVFLCBkYXRhPWxhcG9wMTgpCmBgYAoKUGFyYSBjYWxjdWxhciBlbCBwcm9tZWRpbywgc2UgdXNhIGVsIGNvbWFuZG8gYHN2eW1lYW5gIHkgc2UgdXNhIGxhIGVzcGVjaWZpY2FjacOzbiBgbmEucm09VGAgZGViaWRvIGEgcXVlIGVzdGFzIHZhcmlhYmxlcyBjdWVudGFuIGNvbiB2YWxvcmVzIHBlcmRpZG9zLgoKYGBge3Igd2VpZ2h0ZWQgbWVhbiBlZGFkIHkgZXN0dWRpb3N9CnN2eW1lYW4ofnEyLCBkaXNlbm8xOCwgbmEucm09VCkKc3Z5bWVhbih+ZWQsIGRpc2VubzE4LCBuYS5ybT1UKQpgYGAKClBhcmEgbGFzIHZhcmlhYmxlcyBkdW1taWVzIGVsIHByb2NlZGltaWVudG8gZXMgZWwgbWlzbW8sIHNhbHZvIHF1ZSBzZSBsZSBtdWx0aXBsaWNhIHBvciAxMDAgcGFyYSBwcmVzZW50YXJsbyBlbiBmb3JtYXRvIGRlIHBvcmNlbnRhamUKCmBgYHtyIHdlaWdodGVkIG1lYW4gaG9tYnJlIHkgdXJiYW5vfQpzdnltZWFuKH5ob21icmUsIGRpc2VubzE4LCBuYS5ybSA9VCkqMTAwCnN2eW1lYW4ofnVyYmFuLCBkaXNlbm8xOCwgbmEucm09VCkqMTAwCmBgYAoKRWwgcGFxdWV0ZSBgc3VydmV5YCB0YW1iacOpbiB0aWVuZSBjb21hbmRvcyBwYXJhIHJlcGxpY2FyIGdyw6FmaWNvcy4KUG9yIGVqZW1wbG8sIHBhcmEgY2FsY3VsYXIgdW4gaGlzdG9ncmFtYSBzaW1wbGUuCgpgYGB7ciB3ZWlnaHRlZCBoaXN0fQpzdnloaXN0KH5lZCwgZGlzZW5vMTgsIGZyZXEgPSBUKQpgYGAKClBhcmEgY2FsY3VsYXIgZXN0YWTDrXN0aWNvcyBkZXNjcmlwdGl2b3MgcG9yIGdydXBvcywgc2UgcHVlZGUgdXNhciBlbCBjb21hbmRvIGBzdnlieWAsIHF1ZSBwZXJtaXRlIGRlZmluaXIgbGEgdmFyaWFibGUgbnVtw6lyaWNhIHF1ZSBzZSBxdWllcmUgZGVzY3JpYmlyLCBsYSB2YXJpYWJsZSBxdWUgZGVmaW5lIGxvcyBncnVwb3MgeSBlbCBlc3RhZMOtc3RpY28gcG9uZGVyYWRvIHF1ZSBzZSBxdWllcmUgY2FsY3VsYXIuCgpgYGB7ciB3ZWlnaHRlZCBlZCBwb3IgZ3J1cG9zfQpzdnlieSh+ZWQsIH5mYl91c2VyLCBkaXNlbm8xOCwgc3Z5bWVhbiwgbmEucm09VCkKYGBgCgpQYXJhIHJlcHJvZHVjaXIgdW4gZ3LDoWZpY28gZGVzY3JpcHRpdm8gcG9yIGdydXBvcywgc2UgcHVlZGUgdXNhciBlbCBjb21hbmRvIGBzdnlib3hwbG90YCBwYXJhIGNvbXBhcmFyIGxhIGRpc3RyaWJ1Y2nDs24gZGUgbGEgdmFyaWFibGUgZWRhZCBlbnRyZSBncnVwb3MgZGUgdW5hIHZhcmlhYmxlIGRlIHRpcG8gZmFjdG9yLCBjb21vIHVzdWFyaW9zIGRlIFdoYXRzYXBwLgoKYGBge3Igd2VpZ2h0ZWQgYm94cGxvdCBwb3IgZ3J1cG9zfQpzdnlib3hwbG90KH5xMn5mYWN0b3Iod2FfdXNlciksIGRpc2VubzE4LCBhbGwub3V0bGllcnMgPSBUKQpgYGAK