Hoy vamos a estudiar una de las pruebas de hipótesis más populares: ANOVA (ANalysis Of VAriance).

Esta es una prueba que utilizamos cuando la variable dependiente es continua y la variable independiente es categórica con tres o más niveles. Recuerden que si tenemos dos categóricas utilizamos ji-cuadrada y si tenemos continua con categórica de 2 niveles utilizamos t-test.

Existen múltiples tipos de ANOVA según las comparaciones que se pueden realizar pero nosotros nos quedaremos con la versión más simple: one-way ANOVA

Seguiremos con nuestro análisis de la escala de depresión en la ENCOVID. Sólo que ahora nuestra pregunta es distinta:

¿Tenemos diferencias significativas en depresión por nivel de escolaridad? ¿Quién dirían que reporta mayor depresión alta o baja educación?

Vamos a explorar nuestros datos

library(tidyverse)
There were 22 warnings (use warnings() to see them)
library(skimr)
library(infer)
library(ggridges)
library(car)

library(readr)
base_covid_abril2020_final <- read_csv("BD/base_covid_abril2020_final.csv")

abril <-base_covid_abril2020_final


#sexo
abril <- abril %>% 
  mutate(mujer = ordered(sexo, levels = c("0", "1"), labels= c("Varones", "Mujeres")))

abril <- abril %>% 
  mutate(educ= case_when(
    p60_socio==1 ~ 1, #sin
    p60_socio==2 ~ 1, #Preescolar
    p60_socio==3 ~ 1, #Primaria
    p60_socio==4 ~ 2, #Secundaria
    p60_socio==5 ~ 3, #Preparatoria
    p60_socio==6 ~ 4, #Superior
    p60_socio==7 ~ 4, #Maestria
    p60_socio==8 ~ 4, #Doctorado 
    p60_socio==9 ~ NA_real_, #NS/NR
  ))


abril <- abril %>% 
  mutate(educ= ordered(educ, levels=c("1", "2", "3", "4"), labels= c("Primaria", "Secundaria", "Preparatoria", "Superior")))

table(abril$educ, useNA = "ifany")

    Primaria   Secundaria Preparatoria     Superior         <NA> 
         203          246          135          236           13 
# CESD-7

abril <- abril %>% 
  rename(
    d_tristeza="p40_depresion",
    d_concentracion="p41_depresion",
    d_depresion="p42_depresion",  
    d_esfuerzo="p43_depresion",
    d_dormir="p44_depresion",
    d_disfrutar="p45_depresion",
    d_triste="p46_depresion")


abril <- abril %>% 
  mutate(d_total = d_tristeza + d_concentracion + d_depresion + d_esfuerzo + d_dormir + d_disfrutar + d_triste)

table(abril$d_total)

  0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18  19  20  21 
153  60  56  85  42  50  53  33  26  37  21  21  20  21  20  10   8  17  17   6   5  10 
summary(abril$d_total)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
  0.000   1.000   4.000   5.792   9.000  21.000      62 
abril <- abril %>% 
  mutate(dep_level= if_else(d_total >=9, 1, 0))  #Punto de corte en >=9


abril <- abril %>% 
  mutate(dep_level2= ordered(dep_level, levels=c("0", "1"), labels= c("Sin Síntomas", "Con Síntomas")))

Ahora que tenemos datos definimos:

variable dependiente: d_total (min=0; max=21)

variable independiente: educ (4 niveles)

Vamos a primero entender el problema. Veamos la distribución del puntaje de depresión con su media= 5.79

Otra forma de ver los mismos datos. Aquí en lugar de contar, R calcula la proporción de respuestas como proporción de área bajo la curva.

abril %>% 
  ggplot(aes(x= d_total))+
  geom_density(fill="#69b3a2", color="#e9ecef", alpha=0.8) +
  geom_vline(xintercept = 5.79, color= "red")+
  theme_minimal()

Y ahora las stats por grupo.

Ahora tenemos 4 medias. ¿Qué grupo tiene mayor depresión? La pregunta, claro está, es si esta diferencia es estadísticamente significativa.

Veamos otra representación del problema, porque no sólo estamos comprando 4 medias, sino 4 distribuciones

abril %>%
  dplyr::select(d_total,educ) %>% 
  drop_na() %>% 
  ggplot(aes(x = d_total, y = educ, fill = educ)) +
  stat_density_ridges(quantile_lines = TRUE,
                      quantile_fun=function(x,...)mean(x), # mean
                      quantiles = 2, scale = 3, color = "black",
                      alpha = .7)+
  xlim(0, 22)+
  scale_x_continuous(breaks=seq(0,22,1))+
  geom_vline(xintercept = 5.79, color= "red")+
  labs(x = "Puntaje depresion", y = "nivel educativo")+
  scale_fill_brewer(palette = "Set2")+
  theme_minimal()+
  theme(legend.position = "none")

El tema con la ANOVA es que estamos haciendo dos comparaciones simultáneas. Primero vemos si hay diferencias en la varianza entre-grupos (between groups) y luego intra-grupos.

Lo que hace ANOVA es dividir between/ within. Si la varianza between es mayor (> 1), entonces las diferencias entre grupos son significativas. Si la varianza within es mayor (< 1), entonces no podemos afirmar que hay diferencias entre grupos. Y claro que después enfrentamos el problema de definir qué tan grande debe de ser el resultado de esa división para concluir que no es un resultado atribuible al azar.

La ANOVA se caracteriza por apoyarse en la distribución F. Esta distribución permite examinar como stat la razón entre between y within.

Como en toda prueba de hipótesis, definimos la nula como punto de partida

Noten que la HA no es concluyente porque basta con que alguno sea diferente, pero no sabremos cuál de ellos.

Hagamos primero la forma computacional con infer.


abril1 <- abril %>%
    select(d_total,educ) %>% 
  drop_na() %>% 
  mutate(educ1= as.character(educ))


f_observado <- abril1 %>%
  specify(d_total ~ educ1) %>%
  calculate(stat = "F")


null_dist <- abril1 %>%
  specify(d_total ~ educ1) %>%
  hypothesize(null = "independence") %>%
  generate(reps = 1000, type = "permute") %>%
  calculate(stat = "F")

null_dist
NA

Y ahora comparemos con la estat observada

null_dist %>%
  visualize() + 
  shade_p_value(f_observado,
                direction = "greater")

y el valor_p

p_value <- null_dist %>%
  get_p_value(obs_stat = f_observado,
              direction = "greater")
Please be cautious in reporting a p-value of 0. This result is an approximation based on the number of `reps` chosen in the `generate()` step. See `?get_p_value()` for more information.
p_value

¿Qué interpretamos?

Para ANOVA tomemos la ruta tradicional porque queremos explorar más detalles.

Les recomiendo mucho este tutorial

Lo primero es que las pruebas de hipótesis tienen supuestos.

Vamos a ver con otra gráfica los últimos dos supuestos.

abril %>%
  select(d_total,educ) %>% 
  drop_na() %>%
  ggplot( aes(x=educ, y=d_total, fill=educ)) +
    geom_boxplot() +
    geom_jitter(color="grey", size=0.7, alpha=0.5) +
  stat_summary(fun=mean, geom="point", shape=23, size=2, fill= "red")+
  theme_minimal()

Aquí hay muchas cosas sucediendo. Primero, interpretamos los boxplots

A mi los outliers no me preocupan. Pero la varianza realmente no lo sé gráficamente.

Vemos formalmente con una prueba de Levene de homogeneidad de varianza

leveneTest(d_total ~ educ, data = abril) # noten que volví a abril normal
Levene's Test for Homogeneity of Variance (center = median)
       Df F value    Pr(>F)    
group   3  6.2477 0.0003425 ***
      756                      
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

El valor-p es 0.00034. Si la H0 es que hay homogeneidad de varianza, entonces este resultado indica que nuestros grupos tienen heterogenidad de varianza (HA).

Lo correcto al correr una ANOVA, dado esta violación a un supuesto, es lo siguiente:

oneway.test(d_total ~ educ,
  data = abril,
  var.equal = FALSE) # non-equal variances

    One-way analysis of means (not assuming equal variances)

data:  d_total and educ
F = 11.861, num df = 3.00, denom df = 374.53, p-value = 1.945e-07

Tenemos un valor p= 0.00000001945. Es bajo. Nos quedamos con HA.

Siento que la varianza no era tan diferente. Y la ANOVA es robusta a violaciones de supuestos.

Vamos a ver qué pasa si lo reptimos con una ANOVA normal.

oneway.test(d_total ~ educ,
  data = abril,
  var.equal = TRUE) # equal variances

    One-way analysis of means

data:  d_total and educ
F = 11.516, num df = 3, denom df = 756, p-value = 2.179e-07

Noten cómo el estadístico F es casi igual. Y el valor-p sigue siendo MUY bajo. La decisión no cambia

Como los resultados se ven muy parecidos voy a usar otro comando que hace la misma ANOVA pero también puede hacer unos trucos extra. Vean que la sintaxix es casi igual. Sólo no nos deja especificar el supuesto de varianza.

anova_educ <- aov(d_total ~ educ,
  data = abril)

anova_educ # veamos qué hay adentro. Es un objeto de tipo modelo estadístico (list)
Call:
   aov(formula = d_total ~ educ, data = abril)

Terms:
                    educ Residuals
Sum of Squares   1023.15  22389.15
Deg. of Freedom        3       756

Residual standard error: 5.441993
Estimated effects may be unbalanced
73 observations deleted due to missingness

Lo interesante de estos objetos es que tienen MUCHAS cosas dentro que podemos extraer.

summary(anova_educ)
             Df Sum Sq Mean Sq F value   Pr(>F)    
educ          3   1023   341.1   11.52 2.18e-07 ***
Residuals   756  22389    29.6                     
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
73 observations deleted due to missingness

Ahí esta el mismo estadístico F. Y un valor muy pequeño. Misma decisión (HA)

Vean algo padre si tienen dudas de cómo interpretar esto. Esas son las cosas que van surgiendo en R. Les debo el concepto de tamaño del efecto.

(Recuerden que la primera vez lo deben instalar. Solo una vez. Borren el gato previo a install y corranlo. Luego vuelvan a poner el # para que ignore ese comando.)

#install.packages("remotes")
#remotes::install_github("easystats/report")

library(report)

report(anova_educ)
The ANOVA (formula: d_total ~ educ) suggests that:

  - The main effect of educ is statistically significant and small (F(3, 756) = 11.52, p < .001; Eta2 = 0.04, 95% CI [0.02, 1.00])

Effect sizes were labelled following Field's (2013) recommendations.

De tarea exploren esta página; cap 6 ANOVA

Sigamos. Vamos a verificar si violamos el supuesto de normalidad. Si fuera nomal, todas se acostarían sobre la diagonal. Si se separan mucho de la diagonal, se viola.

plot(anova_educ, which = 2)

Otra prueba similar, pero con IC. Sí se sale de los intervalos…

library(car) #instalenlo

qqPlot(anova_educ$residuals,
  id = FALSE)

La prueba formal. H0 los residuales del modelo tienen una distribución normal. HA no es una distribución normal y se viola el supuesto.

shapiro.test(anova_educ$residuals)

    Shapiro-Wilk normality test

data:  anova_educ$residuals
W = 0.91915, p-value < 2.2e-16

El valor p es muuuuy pequeño. así que esos 0 de depresión parece que sí pesan.

Debemos usar una prueba no-paramétrica: kruskal wallis. Comparemos mejor medianas (misma sintaxis, misma H0)

kruskal.test(d_total ~ educ, data = abril)

    Kruskal-Wallis rank sum test

data:  d_total by educ
Kruskal-Wallis chi-squared = 33.379, df = 3, p-value = 2.679e-07

Otro valor p muy pequeño. Otra vez nos quedamos con HA

Nuestra conclusión se va fortaleciendo. Varias pruebas, misma decisión

Vean cómo a pesar de violar el supuesto la ANOVA normal nos dice bastante.

Por eso me voy a quedar con el objeto que me da más cositas.

Vamos ahora a algo aún más interesante: ¿Qué grupos difieren entre sí?

Para eso necesitamos hacer muchas pruebas simultaneas.

… y así con las demás…

El problema es que al hacer muchas comparaciones es fácil encontrar falsos positivos por casualidad. Lo bueno es que esto se corrige si se hacen todas las pruebas simulataneamente.

Vamos a usar los que se conocen como pruebas post-hoc de Tukey (famoso estadístico, inventó los boxplots)

TukeyHSD(anova_educ) #volví a mi objeto anova
  Tukey multiple comparisons of means
    95% family-wise confidence level

Fit: aov(formula = d_total ~ educ, data = abril)

$educ
                             diff       lwr        upr     p adj
Secundaria-Primaria     -1.179745 -2.573886  0.2143958 0.1300643
Preparatoria-Primaria   -1.832112 -3.455971 -0.2082541 0.0197337
Superior-Primaria       -3.106716 -4.503611 -1.7098199 0.0000001
Preparatoria-Secundaria -0.652367 -2.208988  0.9042539 0.7024513
Superior-Secundaria     -1.926970 -3.245102 -0.6088386 0.0010325
Superior-Preparatoria   -1.274603 -2.833692  0.2844852 0.1524115

Ahora sí tenemos resultados más interesantes…

  1. Vean la columna abajo de $educ. Son las comparaciones 1:1. Empecemos por el primer renglón. ¿son diferentes los puntajes de depresión entre secundaria y primaria?

  2. La segunda columna es el estadístico “diff”. Sí, es una prueba de diferencia de medias (t-test). Primaria tiene, en promedio, 1.17 más puntos en depresión que secundaria.

  3. Las siguientes dos columnas son nuestros queridos intervalos de confianza. Noten cómo el estadísticos de diff tiene un intervalo de -2.57 hasta 0.21. ¿Notan algo sospechoso? Sí, el intervalo incluye el 0. Esto significa que si la muestra se hubiera repetido 100 veces, en más de 5 ocasiones no hubiera habido diferencia entre estos niveles. De hecho, en varias de ellas secundaria habría sido mayor.

  4. Nuestro famoso valor-p. Está ajustado porque Tukey está cuidando el problema de falsos positivos cuando se hacen varias pruebas simultáeas. El valor-p es 0.13. Significa que hay un 13% de probabilidad de que H= ocurra. En otras palabras, si repetimos la muestra 100 veces, en 13 no encontraríamos diferencias. Esas son muchas. Más de las 5 que estamos dispuestos a tolerar. Y ese 0.13 ciertamente es mayor al .05 que definimos a priori como frontera del 95% de confianza. Así que no rechazamos H0: no hay diferencia entre secundaria y primaria.

¿En qué niveles sí hay diferencias?

Vean las combinaciones en las que el valor-p es menor a 0.05.

¿Les hace sentido teóricamente? ¿Dónde marcan la frontera? ¿Cuál es la zona gris?

Vamos a verlo de otra forma. No es la más atractiva, pero sí útil. Están en el mismo orden que arriba.

¿qué interpretamos?


plot(TukeyHSD(anova_educ))

Si el ala del avión del IC cruza el 0, entonces esa comparación no es estadísticamente significativa.

A veces queremos hacer menos comparaciones para tener mayor poder estadístico. Y usamos una categoría de referencia. Voy a ver todos contra Primaria, la del puntaje más alto. En ese caso uso las prubeas de Dunett.

library(multcomp) #intalar

dunnett <- glht(anova_educ,
  linfct = mcp(educ = "Dunnett"))

summary(dunnett)

     Simultaneous Tests for General Linear Hypotheses

Multiple Comparisons of Means: Dunnett Contrasts


Fit: aov(formula = d_total ~ educ, data = abril)

Linear Hypotheses:
                             Estimate Std. Error t value Pr(>|t|)    
Secundaria - Primaria == 0    -1.1797     0.5415  -2.179   0.0768 .  
Preparatoria - Primaria == 0  -1.8321     0.6307  -2.905   0.0107 *  
Superior - Primaria == 0      -3.1067     0.5425  -5.726   <0.001 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
(Adjusted p values reported -- single-step method)

Ahora la gráfica. De nuevo, sigan el orden del output previo.

plot(dunnett)

Yo creo que con estos resultados ya es más claro que hay dos grupos primaria-secundaria y luego preparatoria-superior. Estas ya son conslusiones más sólidas

Y ahora una de pilón… Una joyita más o menos reciente en R.

library(ggstatsplot)
package ‘ggstatsplot’ was built under R version 4.0.2You can cite this package as:
     Patil, I. (2021). Visualizations with statistical details: The 'ggstatsplot' approach.
     Journal of Open Source Software, 6(61), 3167, doi:10.21105/joss.03167
ggbetweenstats(
  data = abril,
  x = educ,
  y = d_total,
  type = "parametric", # ANOVA or Kruskal-Wallis
  var.equal = FALSE, # ANOVA or Welch ANOVA
  plot.type = "box",
  pairwise.comparisons = TRUE,
  pairwise.display = "significant",
  centrality.plotting = FALSE,
  bf.message = FALSE
)

Ahora con kruskall-Wallis. Vean Primaria vs Preparatoria

ggbetweenstats(
  data = abril,
  x = educ,
  y = d_total,
  type = "nonparametric", # ANOVA or Kruskal-Wallis
  var.equal = FALSE, # ANOVA or Welch ANOVA
  plot.type = "box",
  pairwise.comparisons = TRUE,
  pairwise.display = "significant",
  centrality.plotting = FALSE,
  bf.message = FALSE
)

Por último, les dejo un recurso donde pueden consultar las muchas pruebas existentes según necesiten.

Recuerden que la decisión depende de sus tipos de variables y si usan paramétrica o no.

Este es un buen resumen

LS0tCnRpdGxlOiAiQU5PVkEiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCkhveSB2YW1vcyBhIGVzdHVkaWFyIHVuYSBkZSBsYXMgcHJ1ZWJhcyBkZSBoaXDDs3Rlc2lzIG3DoXMgcG9wdWxhcmVzOiBBTk9WQSAoQU5hbHlzaXMgT2YgVkFyaWFuY2UpLiAKCkVzdGEgZXMgdW5hIHBydWViYSBxdWUgdXRpbGl6YW1vcyBjdWFuZG8gbGEgdmFyaWFibGUgZGVwZW5kaWVudGUgZXMgX19jb250aW51YV9fIHkgbGEgdmFyaWFibGUgaW5kZXBlbmRpZW50ZSBlcyBfX2NhdGVnw7NyaWNhX18gY29uIHRyZXMgbyBtw6FzIG5pdmVsZXMuIFJlY3VlcmRlbiBxdWUgc2kgdGVuZW1vcyBkb3MgY2F0ZWfDs3JpY2FzIHV0aWxpemFtb3MgamktY3VhZHJhZGEgeSBzaSB0ZW5lbW9zIGNvbnRpbnVhIGNvbiBjYXRlZ8OzcmljYSBkZSAyIG5pdmVsZXMgdXRpbGl6YW1vcyB0LXRlc3QuIAoKRXhpc3RlbiBtw7psdGlwbGVzIHRpcG9zIGRlIEFOT1ZBIHNlZ8O6biBsYXMgY29tcGFyYWNpb25lcyBxdWUgc2UgcHVlZGVuIHJlYWxpemFyIHBlcm8gbm9zb3Ryb3Mgbm9zIHF1ZWRhcmVtb3MgY29uIGxhIHZlcnNpw7NuIG3DoXMgc2ltcGxlOiBvbmUtd2F5IEFOT1ZBCgpTZWd1aXJlbW9zIGNvbiBudWVzdHJvIGFuw6FsaXNpcyBkZSBsYSBlc2NhbGEgZGUgZGVwcmVzacOzbiBlbiBsYSBFTkNPVklELiBTw7NsbyBxdWUgYWhvcmEgbnVlc3RyYSBwcmVndW50YSBlcyBkaXN0aW50YTogCgo+IMK/VGVuZW1vcyBkaWZlcmVuY2lhcyBzaWduaWZpY2F0aXZhcyBlbiBkZXByZXNpw7NuIHBvciBuaXZlbCBkZSBlc2NvbGFyaWRhZD8gwr9RdWnDqW4gZGlyw61hbiBxdWUgcmVwb3J0YSBtYXlvciBkZXByZXNpw7NuIGFsdGEgbyBiYWphIGVkdWNhY2nDs24/CgpWYW1vcyBhIGV4cGxvcmFyIG51ZXN0cm9zIGRhdG9zIAoKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHNraW1yKQpsaWJyYXJ5KGluZmVyKQpsaWJyYXJ5KGdncmlkZ2VzKQpsaWJyYXJ5KGNhcikKCmxpYnJhcnkocmVhZHIpCmJhc2VfY292aWRfYWJyaWwyMDIwX2ZpbmFsIDwtIHJlYWRfY3N2KCJCRC9iYXNlX2NvdmlkX2FicmlsMjAyMF9maW5hbC5jc3YiKQoKYWJyaWwgPC1iYXNlX2NvdmlkX2FicmlsMjAyMF9maW5hbAoKCiNzZXhvCmFicmlsIDwtIGFicmlsICU+JSAKICBtdXRhdGUobXVqZXIgPSBvcmRlcmVkKHNleG8sIGxldmVscyA9IGMoIjAiLCAiMSIpLCBsYWJlbHM9IGMoIlZhcm9uZXMiLCAiTXVqZXJlcyIpKSkKCmFicmlsIDwtIGFicmlsICU+JSAKICBtdXRhdGUoZWR1Yz0gY2FzZV93aGVuKAogICAgcDYwX3NvY2lvPT0xIH4gMSwgI3NpbgogICAgcDYwX3NvY2lvPT0yIH4gMSwgI1ByZWVzY29sYXIKICAgIHA2MF9zb2Npbz09MyB+IDEsICNQcmltYXJpYQogICAgcDYwX3NvY2lvPT00IH4gMiwgI1NlY3VuZGFyaWEKICAgIHA2MF9zb2Npbz09NSB+IDMsICNQcmVwYXJhdG9yaWEKICAgIHA2MF9zb2Npbz09NiB+IDQsICNTdXBlcmlvcgogICAgcDYwX3NvY2lvPT03IH4gNCwgI01hZXN0cmlhCiAgICBwNjBfc29jaW89PTggfiA0LCAjRG9jdG9yYWRvIAogICAgcDYwX3NvY2lvPT05IH4gTkFfcmVhbF8sICNOUy9OUgogICkpCgoKYWJyaWwgPC0gYWJyaWwgJT4lIAogIG11dGF0ZShlZHVjPSBvcmRlcmVkKGVkdWMsIGxldmVscz1jKCIxIiwgIjIiLCAiMyIsICI0IiksIGxhYmVscz0gYygiUHJpbWFyaWEiLCAiU2VjdW5kYXJpYSIsICJQcmVwYXJhdG9yaWEiLCAiU3VwZXJpb3IiKSkpCgp0YWJsZShhYnJpbCRlZHVjLCB1c2VOQSA9ICJpZmFueSIpCgojIENFU0QtNwoKYWJyaWwgPC0gYWJyaWwgJT4lIAogIHJlbmFtZSgKICAgIGRfdHJpc3RlemE9InA0MF9kZXByZXNpb24iLAogICAgZF9jb25jZW50cmFjaW9uPSJwNDFfZGVwcmVzaW9uIiwKICAgIGRfZGVwcmVzaW9uPSJwNDJfZGVwcmVzaW9uIiwgIAogICAgZF9lc2Z1ZXJ6bz0icDQzX2RlcHJlc2lvbiIsCiAgICBkX2Rvcm1pcj0icDQ0X2RlcHJlc2lvbiIsCiAgICBkX2Rpc2ZydXRhcj0icDQ1X2RlcHJlc2lvbiIsCiAgICBkX3RyaXN0ZT0icDQ2X2RlcHJlc2lvbiIpCgoKYWJyaWwgPC0gYWJyaWwgJT4lIAogIG11dGF0ZShkX3RvdGFsID0gZF90cmlzdGV6YSArIGRfY29uY2VudHJhY2lvbiArIGRfZGVwcmVzaW9uICsgZF9lc2Z1ZXJ6byArIGRfZG9ybWlyICsgZF9kaXNmcnV0YXIgKyBkX3RyaXN0ZSkKCnRhYmxlKGFicmlsJGRfdG90YWwpCgpzdW1tYXJ5KGFicmlsJGRfdG90YWwpCgphYnJpbCA8LSBhYnJpbCAlPiUgCiAgbXV0YXRlKGRlcF9sZXZlbD0gaWZfZWxzZShkX3RvdGFsID49OSwgMSwgMCkpICAjUHVudG8gZGUgY29ydGUgZW4gPj05CgoKYWJyaWwgPC0gYWJyaWwgJT4lIAogIG11dGF0ZShkZXBfbGV2ZWwyPSBvcmRlcmVkKGRlcF9sZXZlbCwgbGV2ZWxzPWMoIjAiLCAiMSIpLCBsYWJlbHM9IGMoIlNpbiBTw61udG9tYXMiLCAiQ29uIFPDrW50b21hcyIpKSkKCmBgYAoKQWhvcmEgcXVlIHRlbmVtb3MgZGF0b3MgZGVmaW5pbW9zOgoKID4gdmFyaWFibGUgZGVwZW5kaWVudGU6IGRfdG90YWwgKG1pbj0wOyBtYXg9MjEpCgogPiB2YXJpYWJsZSBpbmRlcGVuZGllbnRlOiBlZHVjICg0IG5pdmVsZXMpCgpWYW1vcyBhIHByaW1lcm8gZW50ZW5kZXIgZWwgcHJvYmxlbWEuIFZlYW1vcyBsYSBkaXN0cmlidWNpw7NuIGRlbCBwdW50YWplIGRlIGRlcHJlc2nDs24gY29uIHN1IG1lZGlhPSA1Ljc5CgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQphYnJpbCAlPiUgCiAgZ2dwbG90KGFlcyh4PSBkX3RvdGFsKSkrCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAxLCBmaWxsPSAiY29yYWwiLCBjb2xvcj0id2hpdGUiKSsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSA1Ljc5LCBjb2xvcj0gInJlZCIpKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3M9c2VxKDAsMjIsMSkpKwogIHRoZW1lX21pbmltYWwoKQpgYGAKCk90cmEgZm9ybWEgZGUgdmVyIGxvcyBtaXNtb3MgZGF0b3MuIEFxdcOtIGVuIGx1Z2FyIGRlIGNvbnRhciwgUiBjYWxjdWxhIGxhIHByb3BvcmNpw7NuIGRlIHJlc3B1ZXN0YXMgY29tbyBwcm9wb3JjacOzbiBkZSDDoXJlYSBiYWpvIGxhIGN1cnZhLiAKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmFicmlsICU+JSAKICBnZ3Bsb3QoYWVzKHg9IGRfdG90YWwpKSsKICBnZW9tX2RlbnNpdHkoZmlsbD0iIzY5YjNhMiIsIGNvbG9yPSIjZTllY2VmIiwgYWxwaGE9MC44KSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gNS43OSwgY29sb3I9ICJyZWQiKSsKICB0aGVtZV9taW5pbWFsKCkKYGBgCgoKWSBhaG9yYSBsYXMgc3RhdHMgcG9yIGdydXBvLiAKCmBgYHtyfQoKYWJyaWwgJT4lCiAgZHBseXI6OnNlbGVjdChlZHVjLCBkX3RvdGFsKSAlPiUgCiAgZHJvcF9uYSgpICU+JSAKICBncm91cF9ieShlZHVjKSAlPiUgCiAgc3VtbWFyaXplKG4oKSwgbWVkaWE9IG1lYW4oZF90b3RhbCksIHNkPSBzZChkX3RvdGFsKSwgbWVkaWFuYT0gbWVkaWFuKGRfdG90YWwpKSAKYGBgCgpBaG9yYSB0ZW5lbW9zIDQgbWVkaWFzLiDCv1F1w6kgZ3J1cG8gdGllbmUgbWF5b3IgZGVwcmVzacOzbj8gTGEgcHJlZ3VudGEsIGNsYXJvIGVzdMOhLCBlcyBzaSBlc3RhIGRpZmVyZW5jaWEgZXMgZXN0YWTDrXN0aWNhbWVudGUgc2lnbmlmaWNhdGl2YS4gCgpWZWFtb3Mgb3RyYSByZXByZXNlbnRhY2nDs24gZGVsIHByb2JsZW1hLCBwb3JxdWUgbm8gc8OzbG8gZXN0YW1vcyBjb21wcmFuZG8gNCBtZWRpYXMsIHNpbm8gX180IGRpc3RyaWJ1Y2lvbmVzX18gCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQphYnJpbCAlPiUKICBkcGx5cjo6c2VsZWN0KGRfdG90YWwsZWR1YykgJT4lIAogIGRyb3BfbmEoKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gZF90b3RhbCwgeSA9IGVkdWMsIGZpbGwgPSBlZHVjKSkgKwogIHN0YXRfZGVuc2l0eV9yaWRnZXMocXVhbnRpbGVfbGluZXMgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgcXVhbnRpbGVfZnVuPWZ1bmN0aW9uKHgsLi4uKW1lYW4oeCksICMgbWVhbgogICAgICAgICAgICAgICAgICAgICAgcXVhbnRpbGVzID0gMiwgc2NhbGUgPSAzLCBjb2xvciA9ICJibGFjayIsCiAgICAgICAgICAgICAgICAgICAgICBhbHBoYSA9IC43KSsKICB4bGltKDAsIDIyKSsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzPXNlcSgwLDIyLDEpKSsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSA1Ljc5LCBjb2xvcj0gInJlZCIpKwogIGxhYnMoeCA9ICJQdW50YWplIGRlcHJlc2lvbiIsIHkgPSAibml2ZWwgZWR1Y2F0aXZvIikrCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJTZXQyIikrCiAgdGhlbWVfbWluaW1hbCgpKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKYGBgCgoKRWwgdGVtYSBjb24gbGEgQU5PVkEgZXMgcXVlIGVzdGFtb3MgaGFjaWVuZG8gZG9zIGNvbXBhcmFjaW9uZXMgc2ltdWx0w6FuZWFzLiBQcmltZXJvIHZlbW9zIHNpIGhheSBkaWZlcmVuY2lhcyBlbiBsYSB2YXJpYW56YSBfX2VudHJlLWdydXBvc19fIChiZXR3ZWVuIGdyb3VwcykgeSBsdWVnbyBfX2ludHJhLWdydXBvc19fLiAKCiogVmFyaWFuemEgZW50cmUgZ3J1cG9zOiB0ZW5lbW9zIHF1ZSB2ZXIgbGEgZGlzdGFuY2lhIHF1ZSBoYXkgZW50cmUgbGEgZ3JhbiBtZWRpYSAoNS43OSkgeSBsYXMgbWVkaWFzIGRlIGNhZGEgZ3J1cG8gKDcuNCwgNi4yLCA1LjUsIDQuMikuIAoKKiBWYXJpYW56YSBpbnRyYSBncnVwb3M6IHRhbWJpw6luIHZlbW9zIGxhIGRpc3RhbmNpYSBxdWUgaGF5IGVudHJlIGxhIG1lZGlhIGdydXBhbCAoNy40KSB5IGxvcyBwdW50YWplcyBkZSBjYWRhIHBlcnNvbmEgY29uIHByaW1hcmlhIG8gbWVub3MuIAoKTG8gcXVlIGhhY2UgQU5PVkEgZXMgZGl2aWRpciBiZXR3ZWVuLyB3aXRoaW4uIFNpIGxhIHZhcmlhbnphIGJldHdlZW4gZXMgbWF5b3IgKD4gMSksIGVudG9uY2VzIGxhcyBkaWZlcmVuY2lhcyBlbnRyZSBncnVwb3Mgc29uIHNpZ25pZmljYXRpdmFzLiBTaSBsYSB2YXJpYW56YSB3aXRoaW4gZXMgbWF5b3IgKDwgMSksIGVudG9uY2VzIG5vIHBvZGVtb3MgYWZpcm1hciBxdWUgaGF5IGRpZmVyZW5jaWFzIGVudHJlIGdydXBvcy4gWSBjbGFybyBxdWUgZGVzcHXDqXMgZW5mcmVudGFtb3MgZWwgcHJvYmxlbWEgZGUgZGVmaW5pciBxdcOpIHRhbiBncmFuZGUgZGViZSBkZSBzZXIgZWwgcmVzdWx0YWRvIGRlIGVzYSBkaXZpc2nDs24gcGFyYSBjb25jbHVpciBxdWUgbm8gZXMgdW4gcmVzdWx0YWRvIGF0cmlidWlibGUgYWwgYXphci4KCkxhIEFOT1ZBIHNlIGNhcmFjdGVyaXphIHBvciBhcG95YXJzZSBlbiBsYSBkaXN0cmlidWNpw7NuIEYuIEVzdGEgZGlzdHJpYnVjacOzbiBwZXJtaXRlIGV4YW1pbmFyIGNvbW8gc3RhdCBsYSByYXrDs24gZW50cmUgYmV0d2VlbiB5IHdpdGhpbi4gCgpDb21vIGVuIHRvZGEgcHJ1ZWJhIGRlIGhpcMOzdGVzaXMsIGRlZmluaW1vcyBsYSBudWxhIGNvbW8gcHVudG8gZGUgcGFydGlkYQoKKiBIMDogbm8gaGF5IGRpZmVyZW5jaWFzIGVudHJlIGdydXBvcwoqIEhBOiBhbCBtZW5vcyB1biBncnVwbyBkaWZpZXJlIGRlIGxvcyBkZW3DoXMgZ3J1cG9zCgpOb3RlbiBxdWUgbGEgSEEgbm8gZXMgY29uY2x1eWVudGUgcG9ycXVlIGJhc3RhIGNvbiBxdWUgYWxndW5vIHNlYSBkaWZlcmVudGUsIHBlcm8gbm8gc2FicmVtb3MgY3XDoWwgZGUgZWxsb3MuIAoKSGFnYW1vcyBwcmltZXJvIGxhIGZvcm1hIGNvbXB1dGFjaW9uYWwgY29uIGluZmVyLiAKCmBgYHtyfQoKYWJyaWwxIDwtIGFicmlsICU+JQogICAgc2VsZWN0KGRfdG90YWwsZWR1YykgJT4lIAogIGRyb3BfbmEoKSAlPiUgCiAgbXV0YXRlKGVkdWMxPSBhcy5jaGFyYWN0ZXIoZWR1YykpCgoKZl9vYnNlcnZhZG8gPC0gYWJyaWwxICU+JQogIHNwZWNpZnkoZF90b3RhbCB+IGVkdWMxKSAlPiUKICBjYWxjdWxhdGUoc3RhdCA9ICJGIikKCgpudWxsX2Rpc3QgPC0gYWJyaWwxICU+JQogIHNwZWNpZnkoZF90b3RhbCB+IGVkdWMxKSAlPiUKICBoeXBvdGhlc2l6ZShudWxsID0gImluZGVwZW5kZW5jZSIpICU+JQogIGdlbmVyYXRlKHJlcHMgPSAxMDAwLCB0eXBlID0gInBlcm11dGUiKSAlPiUKICBjYWxjdWxhdGUoc3RhdCA9ICJGIikKCm51bGxfZGlzdAoKYGBgCgpZIGFob3JhIGNvbXBhcmVtb3MgY29uIGxhIGVzdGF0IG9ic2VydmFkYQoKYGBge3J9Cm51bGxfZGlzdCAlPiUKICB2aXN1YWxpemUoKSArIAogIHNoYWRlX3BfdmFsdWUoZl9vYnNlcnZhZG8sCiAgICAgICAgICAgICAgICBkaXJlY3Rpb24gPSAiZ3JlYXRlciIpCmBgYAoKeSBlbCB2YWxvcl9wCgpgYGB7cn0KcF92YWx1ZSA8LSBudWxsX2Rpc3QgJT4lCiAgZ2V0X3BfdmFsdWUob2JzX3N0YXQgPSBmX29ic2VydmFkbywKICAgICAgICAgICAgICBkaXJlY3Rpb24gPSAiZ3JlYXRlciIpCgpwX3ZhbHVlCmBgYAoKwr9RdcOpIGludGVycHJldGFtb3M/CgoKUGFyYSBBTk9WQSB0b21lbW9zIGxhIHJ1dGEgdHJhZGljaW9uYWwgcG9ycXVlIHF1ZXJlbW9zIGV4cGxvcmFyIG3DoXMgZGV0YWxsZXMuIAoKTGVzIHJlY29taWVuZG8gbXVjaG8gZXN0ZSBbdHV0b3JpYWxdKGh0dHBzOi8vc3RhdHNhbmRyLmNvbS9ibG9nL2Fub3ZhLWluLXIvKQoKTG8gcHJpbWVybyBlcyBxdWUgbGFzIHBydWViYXMgZGUgaGlww7N0ZXNpcyB0aWVuZW4gc3VwdWVzdG9zLgoKKiBJbmRlcGVuZGVuY2lhOiDCv2hheSByZWxhY2nDs24gZW50cmUgbG8gcXVlIHJlc3BvbmRlIHVuYSBwZXJzb25hIGNvbiBwcmltYXJpYSB5IGxvIHF1ZSByZXNwb25kZSBjb24gc2VjdW5kYXJpYT8gTm8sIG5vIHRpZW5lIG5hZGEgcXVlIHZlcjogc29uIGluZGVwZW5kaWVudGVzLiBEaXNlw7FvcyBwcmUtcG9zdCBzZXLDrWFuIGRlcGVuZGllbnRlcyBvIHBhcmVhZGFzCgoqIE5vcm1hbGlkYWQ6IGxhcyBkaXN0cmlidWNpb25lcyBfX2RlIGNhZGEgZ3J1cG9fXyBhcHJveGltYW4gdW5hIGRpc3RyaWJ1Y2nDs24gbm9ybWFsLiBObyBkZWwgdG9kbyBwZXJvIG3DoXMgbyBtZW5vcyBhc2VtZWphbiB1bmEgY3VydmE7IGF1bnF1ZSBoYXkgbXVjaG9zIGNlcm9zLiAKCiogRXF1aXZhbGVuY2lhIGRlIHZhcmlhbnphOiBhbCBpbnRlcmlvciBkZSBjYWRhIGdydXBvLCBsYSB2YXJpYW56YSBlcyBzaW1pbGFyICh3aXRoaW4pCgoqIE91dGxpZXJzOiBubyB0ZW5lbW9zIG9ic2VydmFjaW9uZXMgZXh0cmVtYXMgZW4gbmluZ8O6biBncnVwby4gCgpWYW1vcyBhIHZlciBjb24gb3RyYSBncsOhZmljYSBsb3Mgw7psdGltb3MgZG9zIHN1cHVlc3Rvcy4gCgpgYGB7cn0KYWJyaWwgJT4lCiAgc2VsZWN0KGRfdG90YWwsZWR1YykgJT4lIAogIGRyb3BfbmEoKSAlPiUKICBnZ3Bsb3QoIGFlcyh4PWVkdWMsIHk9ZF90b3RhbCwgZmlsbD1lZHVjKSkgKwogICAgZ2VvbV9ib3hwbG90KCkgKwogICAgZ2VvbV9qaXR0ZXIoY29sb3I9ImdyZXkiLCBzaXplPTAuNywgYWxwaGE9MC41KSArCiAgc3RhdF9zdW1tYXJ5KGZ1bj1tZWFuLCBnZW9tPSJwb2ludCIsIHNoYXBlPTIzLCBzaXplPTIsIGZpbGw9ICJyZWQiKSsKICB0aGVtZV9taW5pbWFsKCkKYGBgCgpBcXXDrSBoYXkgbXVjaGFzIGNvc2FzIHN1Y2VkaWVuZG8uIFByaW1lcm8sIGludGVycHJldGFtb3MgbG9zIFtib3hwbG90c10oaHR0cHM6Ly93d3cuZGF0YS10by12aXouY29tL2NhdmVhdC9ib3hwbG90Lmh0bWwpCgoqIMK/UXXDqSBub3MgZGljZW4gbGFzIGNhamFzPyBsw61taXRlIGluZmVyaW9yLCBzdXBlcmlvciB5IHJheWEgZW4gbWVkaW8KKiBZbyBsZSBhw7FhZMOtIGVzZSBkaWFtYW50ZSByb2pvOiDCv3F1w6kgY3JlZW4gcXVlIGVzPwoqIMK/UXXDqSBzb24gbG9zIHB1bnRpdG9zIGdyaXNlcyBjbGFybyAtICBsb3MgY29wb3MgZGUgbmlldmU/Ciogwr9RdcOpIHNvbiBsb3MgcHVudG9zIG5lZ3JvcyBhcnJpYmE/IAoqIMK/UXXDqSBub3MgZGljZSBlc3RhIGdyw6FmaWNhIHNvYnJlIGxhIGhvbW9nZW5laWRhZCBkZSB2YXJpYW56YT8gCgpBIG1pIGxvcyBvdXRsaWVycyBubyBtZSBwcmVvY3VwYW4uIFBlcm8gbGEgdmFyaWFuemEgcmVhbG1lbnRlIG5vIGxvIHPDqSBncsOhZmljYW1lbnRlLiAKClZlbW9zIGZvcm1hbG1lbnRlIGNvbiB1bmEgcHJ1ZWJhIGRlIExldmVuZSBkZSBob21vZ2VuZWlkYWQgZGUgdmFyaWFuemEKCmBgYHtyfQpsZXZlbmVUZXN0KGRfdG90YWwgfiBlZHVjLCBkYXRhID0gYWJyaWwpICMgbm90ZW4gcXVlIHZvbHbDrSBhIGFicmlsIG5vcm1hbApgYGAKCkVsIHZhbG9yLXAgZXMgMC4wMDAzNC4gU2kgbGEgSDAgZXMgcXVlIGhheSBob21vZ2VuZWlkYWQgZGUgdmFyaWFuemEsIGVudG9uY2VzIGVzdGUgcmVzdWx0YWRvIGluZGljYSBxdWUgbnVlc3Ryb3MgZ3J1cG9zIHRpZW5lbiBoZXRlcm9nZW5pZGFkIGRlIHZhcmlhbnphIChIQSkuIAoKTG8gY29ycmVjdG8gYWwgY29ycmVyIHVuYSBBTk9WQSwgZGFkbyBlc3RhIHZpb2xhY2nDs24gYSB1biBzdXB1ZXN0bywgZXMgbG8gc2lndWllbnRlOgoKYGBge3J9Cm9uZXdheS50ZXN0KGRfdG90YWwgfiBlZHVjLAogIGRhdGEgPSBhYnJpbCwKICB2YXIuZXF1YWwgPSBGQUxTRSkgIyBub24tZXF1YWwgdmFyaWFuY2VzCgpgYGAKClRlbmVtb3MgdW4gdmFsb3IgcD0gMC4wMDAwMDAwMTk0NS4gRXMgYmFqby4gTm9zIHF1ZWRhbW9zIGNvbiBIQS4gCgpTaWVudG8gcXVlIGxhIHZhcmlhbnphIG5vIGVyYSB0YW4gZGlmZXJlbnRlLiBZIGxhIEFOT1ZBIGVzIHJvYnVzdGEgYSB2aW9sYWNpb25lcyBkZSBzdXB1ZXN0b3MuCgpWYW1vcyBhIHZlciBxdcOpIHBhc2Egc2kgbG8gcmVwdGltb3MgY29uIHVuYSBBTk9WQSBub3JtYWwuCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpvbmV3YXkudGVzdChkX3RvdGFsIH4gZWR1YywKICBkYXRhID0gYWJyaWwsCiAgdmFyLmVxdWFsID0gVFJVRSkgIyBlcXVhbCB2YXJpYW5jZXMKYGBgCgpOb3RlbiBjw7NtbyBlbCBlc3RhZMOtc3RpY28gRiBlcyBjYXNpIGlndWFsLiBZIGVsIHZhbG9yLXAgc2lndWUgc2llbmRvIE1VWSBiYWpvLiBfX0xhIGRlY2lzacOzbiBubyBjYW1iaWFfXwoKQ29tbyBsb3MgcmVzdWx0YWRvcyBzZSB2ZW4gbXV5IHBhcmVjaWRvcyB2b3kgYSB1c2FyIG90cm8gY29tYW5kbyBxdWUgaGFjZSBfX2xhIG1pc21hIEFOT1ZBX18gcGVybyB0YW1iacOpbiBwdWVkZSBoYWNlciB1bm9zIHRydWNvcyBleHRyYS4gVmVhbiBxdWUgbGEgc2ludGF4aXggZXMgY2FzaSBpZ3VhbC4gU8OzbG8gbm8gbm9zIGRlamEgZXNwZWNpZmljYXIgZWwgc3VwdWVzdG8gZGUgdmFyaWFuemEuIAoKYGBge3J9CmFub3ZhX2VkdWMgPC0gYW92KGRfdG90YWwgfiBlZHVjLCBkYXRhID0gYWJyaWwpCgphbm92YV9lZHVjICMgdmVhbW9zIHF1w6kgaGF5IGFkZW50cm8uIEVzIHVuIG9iamV0byBkZSB0aXBvIG1vZGVsbyBlc3RhZMOtc3RpY28gKGxpc3QpCgpgYGAKCkxvIGludGVyZXNhbnRlIGRlIGVzdG9zIG9iamV0b3MgZXMgcXVlIHRpZW5lbiBNVUNIQVMgY29zYXMgZGVudHJvIHF1ZSBwb2RlbW9zIGV4dHJhZXIuIAoKYGBge3J9CnN1bW1hcnkoYW5vdmFfZWR1YykKYGBgCgpBaMOtIGVzdGEgZWwgbWlzbW8gZXN0YWTDrXN0aWNvIEYuIFkgdW4gdmFsb3IgbXV5IHBlcXVlw7FvLiBNaXNtYSBkZWNpc2nDs24gKEhBKSAKClZlYW4gYWxnbyBwYWRyZSBzaSB0aWVuZW4gZHVkYXMgZGUgY8OzbW8gaW50ZXJwcmV0YXIgZXN0by4gRXNhcyBzb24gbGFzIGNvc2FzIHF1ZSB2YW4gc3VyZ2llbmRvIGVuIFIuIExlcyBkZWJvIGVsIGNvbmNlcHRvIGRlIHRhbWHDsW8gZGVsIGVmZWN0by4gCgooUmVjdWVyZGVuIHF1ZSBsYSBwcmltZXJhIHZleiBsbyBkZWJlbiBpbnN0YWxhci4gU29sbyB1bmEgdmV6LiBCb3JyZW4gZWwgZ2F0byBwcmV2aW8gYSBpbnN0YWxsIHkgY29ycmFubG8uIEx1ZWdvIHZ1ZWx2YW4gYSBwb25lciBlbCAjIHBhcmEgcXVlIGlnbm9yZSBlc2UgY29tYW5kby4pCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojaW5zdGFsbC5wYWNrYWdlcygicmVtb3RlcyIpCiNyZW1vdGVzOjppbnN0YWxsX2dpdGh1YigiZWFzeXN0YXRzL3JlcG9ydCIpCgpsaWJyYXJ5KHJlcG9ydCkKCnJlcG9ydChhbm92YV9lZHVjKQoKYGBgCgoKPiBEZSB0YXJlYSBleHBsb3JlbiBlc3RhIFtww6FnaW5hOyBjYXAgNiBBTk9WQV0oaHR0cHM6Ly9zZWVpbmctdGhlb3J5LmJyb3duLmVkdS9yZWdyZXNzaW9uLWFuYWx5c2lzL2luZGV4Lmh0bWwjc2VjdGlvbjMpCgpTaWdhbW9zLiBWYW1vcyBhIHZlcmlmaWNhciBzaSB2aW9sYW1vcyBlbCBzdXB1ZXN0byBkZSBub3JtYWxpZGFkLiBTaSBmdWVyYSBub21hbCwgdG9kYXMgc2UgYWNvc3RhcsOtYW4gc29icmUgbGEgZGlhZ29uYWwuIFNpIHNlIHNlcGFyYW4gbXVjaG8gZGUgbGEgZGlhZ29uYWwsIHNlIHZpb2xhLiAKIApgYGB7cn0KcGxvdChhbm92YV9lZHVjLCB3aGljaCA9IDIpCmBgYAogCk90cmEgcHJ1ZWJhIHNpbWlsYXIsIHBlcm8gY29uIElDLiBTw60gc2Ugc2FsZSBkZSBsb3MgaW50ZXJ2YWxvcy4uLiAKCmBgYHtyfQpsaWJyYXJ5KGNhcikgI2luc3RhbGVubG8KCnFxUGxvdChhbm92YV9lZHVjJHJlc2lkdWFscywKICBpZCA9IEZBTFNFKQpgYGAKCkxhIHBydWViYSBmb3JtYWwuIEgwIGxvcyByZXNpZHVhbGVzIGRlbCBtb2RlbG8gdGllbmVuIHVuYSBkaXN0cmlidWNpw7NuIG5vcm1hbC4gSEEgbm8gZXMgdW5hIGRpc3RyaWJ1Y2nDs24gbm9ybWFsIHkgc2UgdmlvbGEgZWwgc3VwdWVzdG8uIAoKYGBge3J9CnNoYXBpcm8udGVzdChhbm92YV9lZHVjJHJlc2lkdWFscykKYGBgCgpFbCB2YWxvciBwIGVzIG11dXV1eSBwZXF1ZcOxby4gYXPDrSBxdWUgZXNvcyAwIGRlIGRlcHJlc2nDs24gcGFyZWNlIHF1ZSBzw60gcGVzYW4uIAoKRGViZW1vcyB1c2FyIHVuYSBwcnVlYmEgbm8tcGFyYW3DqXRyaWNhOiBrcnVza2FsIHdhbGxpcy4gQ29tcGFyZW1vcyBtZWpvciBtZWRpYW5hcyAobWlzbWEgc2ludGF4aXMsIG1pc21hIEgwKQoKYGBge3J9CmtydXNrYWwudGVzdChkX3RvdGFsIH4gZWR1YywgZGF0YSA9IGFicmlsKQpgYGAKCk90cm8gdmFsb3IgcCBtdXkgcGVxdWXDsW8uIE90cmEgdmV6IG5vcyBxdWVkYW1vcyBjb24gSEEKCk51ZXN0cmEgY29uY2x1c2nDs24gc2UgdmEgZm9ydGFsZWNpZW5kby4gVmFyaWFzIHBydWViYXMsIG1pc21hIGRlY2lzacOzbgoKVmVhbiBjw7NtbyBhIHBlc2FyIGRlIHZpb2xhciBlbCBzdXB1ZXN0byBsYSBBTk9WQSBub3JtYWwgbm9zIGRpY2UgYmFzdGFudGUuIAoKUG9yIGVzbyBtZSB2b3kgYSBxdWVkYXIgY29uIGVsIG9iamV0byBxdWUgbWUgZGEgbcOhcyBjb3NpdGFzLiAKClZhbW9zIGFob3JhIGEgYWxnbyBhw7puIG3DoXMgaW50ZXJlc2FudGU6IF9fwr9RdcOpIGdydXBvcyBkaWZpZXJlbiBlbnRyZSBzw60/X18KClBhcmEgZXNvIG5lY2VzaXRhbW9zIGhhY2VyIG11Y2hhcyBwcnVlYmFzIHNpbXVsdGFuZWFzLiAKCiogUHJpbWFyaWEgdnMgc2VjdW5kYXJpYQoqIFByaW1hcmlhIHZzIHByZXByYXRvcmlhCiogUHJpbWFyaWEgdnMgc3VwZXJpb3IKCi4uLiB5IGFzw60gY29uIGxhcyBkZW3DoXMuLi4gCgpFbCBwcm9ibGVtYSBlcyBxdWUgYWwgaGFjZXIgbXVjaGFzIGNvbXBhcmFjaW9uZXMgZXMgZsOhY2lsIGVuY29udHJhciBmYWxzb3MgcG9zaXRpdm9zIHBvciBjYXN1YWxpZGFkLiBMbyBidWVubyBlcyBxdWUgZXN0byBzZSBjb3JyaWdlIHNpIHNlIGhhY2VuIHRvZGFzIGxhcyBwcnVlYmFzIF9fc2ltdWxhdGFuZWFtZW50ZV9fLgoKVmFtb3MgYSB1c2FyIGxvcyBxdWUgc2UgY29ub2NlbiBjb21vIHBydWViYXMgX3Bvc3QtaG9jXyBkZSBUdWtleSAoZmFtb3NvIGVzdGFkw61zdGljbywgaW52ZW50w7MgbG9zIGJveHBsb3RzKQoKYGBge3J9ClR1a2V5SFNEKGFub3ZhX2VkdWMpICN2b2x2w60gYSBtaSBvYmpldG8gYW5vdmEKYGBgCgpBaG9yYSBzw60gdGVuZW1vcyByZXN1bHRhZG9zIG3DoXMgaW50ZXJlc2FudGVzLi4uIAoKMS4gVmVhbiBsYSBjb2x1bW5hIGFiYWpvIGRlICRlZHVjLiBTb24gbGFzIGNvbXBhcmFjaW9uZXMgMToxLiBFbXBlY2Vtb3MgcG9yIGVsIHByaW1lciByZW5nbMOzbi4gwr9zb24gZGlmZXJlbnRlcyBsb3MgcHVudGFqZXMgZGUgZGVwcmVzacOzbiBlbnRyZSBzZWN1bmRhcmlhIHkgcHJpbWFyaWE/IAoKMi4gTGEgc2VndW5kYSBjb2x1bW5hIGVzIGVsIGVzdGFkw61zdGljbyAiZGlmZiIuIFPDrSwgZXMgdW5hIHBydWViYSBkZSBkaWZlcmVuY2lhIGRlIG1lZGlhcyAodC10ZXN0KS4gUHJpbWFyaWEgdGllbmUsIGVuIHByb21lZGlvLCAxLjE3IG3DoXMgcHVudG9zIGVuIGRlcHJlc2nDs24gcXVlIHNlY3VuZGFyaWEuIAoKMy4gTGFzIHNpZ3VpZW50ZXMgZG9zIGNvbHVtbmFzIHNvbiBudWVzdHJvcyBxdWVyaWRvcyBpbnRlcnZhbG9zIGRlIGNvbmZpYW56YS4gTm90ZW4gY8OzbW8gZWwgZXN0YWTDrXN0aWNvcyBkZSBkaWZmIHRpZW5lIHVuIGludGVydmFsbyBkZSAtMi41NyBoYXN0YSAwLjIxLiDCv05vdGFuIGFsZ28gc29zcGVjaG9zbz8gU8OtLCBlbCBpbnRlcnZhbG8gaW5jbHV5ZSBlbCAwLiBFc3RvIHNpZ25pZmljYSBxdWUgc2kgbGEgbXVlc3RyYSBzZSBodWJpZXJhIHJlcGV0aWRvIDEwMCB2ZWNlcywgZW4gbcOhcyBkZSA1IG9jYXNpb25lcyBubyBodWJpZXJhIGhhYmlkbyBkaWZlcmVuY2lhIGVudHJlIGVzdG9zIG5pdmVsZXMuIERlIGhlY2hvLCBlbiB2YXJpYXMgZGUgZWxsYXMgc2VjdW5kYXJpYSBoYWJyw61hIHNpZG8gbWF5b3IuIAoKNC4gTnVlc3RybyBmYW1vc28gdmFsb3ItcC4gRXN0w6EgYWp1c3RhZG8gcG9ycXVlIFR1a2V5IGVzdMOhIGN1aWRhbmRvIGVsIHByb2JsZW1hIGRlIGZhbHNvcyBwb3NpdGl2b3MgY3VhbmRvIHNlIGhhY2VuIHZhcmlhcyBwcnVlYmFzIHNpbXVsdMOhZWFzLiBFbCB2YWxvci1wIGVzIDAuMTMuIFNpZ25pZmljYSBxdWUgaGF5IHVuIDEzJSBkZSBwcm9iYWJpbGlkYWQgZGUgcXVlIEg9IG9jdXJyYS4gRW4gb3RyYXMgcGFsYWJyYXMsIHNpIHJlcGV0aW1vcyBsYSBtdWVzdHJhIDEwMCB2ZWNlcywgZW4gMTMgbm8gZW5jb250cmFyw61hbW9zIGRpZmVyZW5jaWFzLiBFc2FzIHNvbiAgbXVjaGFzLiBNw6FzIGRlIGxhcyA1IHF1ZSBlc3RhbW9zIGRpc3B1ZXN0b3MgYSB0b2xlcmFyLiBZIGVzZSAwLjEzIGNpZXJ0YW1lbnRlIGVzIG1heW9yIGFsIC4wNSBxdWUgZGVmaW5pbW9zIGEgcHJpb3JpIGNvbW8gZnJvbnRlcmEgZGVsIDk1JSBkZSBjb25maWFuemEuIEFzw60gcXVlIG5vIHJlY2hhemFtb3MgSDA6IG5vIGhheSBkaWZlcmVuY2lhIGVudHJlIHNlY3VuZGFyaWEgeSBwcmltYXJpYS4gCgpfX8K/RW4gcXXDqSBuaXZlbGVzIHPDrSBoYXkgZGlmZXJlbmNpYXM/X18KClZlYW4gbGFzIGNvbWJpbmFjaW9uZXMgZW4gbGFzIHF1ZSBlbCB2YWxvci1wIGVzIG1lbm9yIGEgMC4wNS4gCgrCv0xlcyBoYWNlIHNlbnRpZG8gX3Rlw7NyaWNhbWVudGVfPyDCv0TDs25kZSBtYXJjYW4gbGEgZnJvbnRlcmE/IMK/Q3XDoWwgZXMgbGEgem9uYSBncmlzPyAKClZhbW9zIGEgdmVybG8gZGUgb3RyYSBmb3JtYS4gTm8gZXMgbGEgbcOhcyBhdHJhY3RpdmEsIHBlcm8gc8OtIMO6dGlsLiBFc3TDoW4gZW4gZWwgbWlzbW8gb3JkZW4gcXVlIGFycmliYS4gCgrCv3F1w6kgaW50ZXJwcmV0YW1vcz8KCmBgYHtyfQoKcGxvdChUdWtleUhTRChhbm92YV9lZHVjKSkKYGBgCgoKU2kgZWwgYWxhIGRlbCBhdmnDs24gZGVsIElDIGNydXphIGVsIDAsIGVudG9uY2VzIGVzYSBjb21wYXJhY2nDs24gbm8gZXMgZXN0YWTDrXN0aWNhbWVudGUgc2lnbmlmaWNhdGl2YS4KCkEgdmVjZXMgcXVlcmVtb3MgaGFjZXIgbWVub3MgY29tcGFyYWNpb25lcyBwYXJhIHRlbmVyIG1heW9yIHBvZGVyIGVzdGFkw61zdGljby4gWSB1c2Ftb3MgdW5hIGNhdGVnb3LDrWEgZGUgcmVmZXJlbmNpYS4gVm95IGEgdmVyIHRvZG9zIGNvbnRyYSBQcmltYXJpYSwgbGEgZGVsIHB1bnRhamUgbcOhcyBhbHRvLiBFbiBlc2UgY2FzbyB1c28gbGFzIHBydWJlYXMgZGUgRHVuZXR0LiAKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkobXVsdGNvbXApICNpbnRhbGFyCgpkdW5uZXR0IDwtIGdsaHQoYW5vdmFfZWR1YywKICBsaW5mY3QgPSBtY3AoZWR1YyA9ICJEdW5uZXR0IikpCgpzdW1tYXJ5KGR1bm5ldHQpCmBgYAoKQWhvcmEgbGEgZ3LDoWZpY2EuIERlIG51ZXZvLCBzaWdhbiBlbCBvcmRlbiBkZWwgb3V0cHV0IHByZXZpby4gCgpgYGB7cn0KcGxvdChkdW5uZXR0KQpgYGAKCllvIGNyZW8gcXVlIGNvbiBlc3RvcyByZXN1bHRhZG9zIHlhIGVzIG3DoXMgY2xhcm8gcXVlIGhheSBkb3MgZ3J1cG9zIHByaW1hcmlhLXNlY3VuZGFyaWEgeSBsdWVnbyBwcmVwYXJhdG9yaWEtc3VwZXJpb3IuIEVzdGFzIHlhIHNvbiBjb25zbHVzaW9uZXMgbcOhcyBzw7NsaWRhcwoKWSBhaG9yYSB1bmEgZGUgcGlsw7NuLi4uIFVuYSBqb3lpdGEgbcOhcyBvIG1lbm9zIHJlY2llbnRlIGVuIFIuIAoKYGBge3J9CmxpYnJhcnkoZ2dzdGF0c3Bsb3QpCgpnZ2JldHdlZW5zdGF0cygKICBkYXRhID0gYWJyaWwsCiAgeCA9IGVkdWMsCiAgeSA9IGRfdG90YWwsCiAgdHlwZSA9ICJwYXJhbWV0cmljIiwgIyBBTk9WQSBvciBLcnVza2FsLVdhbGxpcwogIHZhci5lcXVhbCA9IEZBTFNFLCAjIEFOT1ZBIG9yIFdlbGNoIEFOT1ZBCiAgcGxvdC50eXBlID0gImJveCIsCiAgcGFpcndpc2UuY29tcGFyaXNvbnMgPSBUUlVFLAogIHBhaXJ3aXNlLmRpc3BsYXkgPSAic2lnbmlmaWNhbnQiLAogIGNlbnRyYWxpdHkucGxvdHRpbmcgPSBGQUxTRSwKICBiZi5tZXNzYWdlID0gRkFMU0UKKQpgYGAKCkFob3JhIGNvbiBrcnVza2FsbC1XYWxsaXMuIFZlYW4gUHJpbWFyaWEgdnMgUHJlcGFyYXRvcmlhCgpgYGB7cn0KZ2diZXR3ZWVuc3RhdHMoCiAgZGF0YSA9IGFicmlsLAogIHggPSBlZHVjLAogIHkgPSBkX3RvdGFsLAogIHR5cGUgPSAibm9ucGFyYW1ldHJpYyIsICMgQU5PVkEgb3IgS3J1c2thbC1XYWxsaXMKICB2YXIuZXF1YWwgPSBGQUxTRSwgIyBBTk9WQSBvciBXZWxjaCBBTk9WQQogIHBsb3QudHlwZSA9ICJib3giLAogIHBhaXJ3aXNlLmNvbXBhcmlzb25zID0gVFJVRSwKICBwYWlyd2lzZS5kaXNwbGF5ID0gInNpZ25pZmljYW50IiwKICBjZW50cmFsaXR5LnBsb3R0aW5nID0gRkFMU0UsCiAgYmYubWVzc2FnZSA9IEZBTFNFCikKYGBgCgpQb3Igw7psdGltbywgbGVzIGRlam8gdW4gcmVjdXJzbyBkb25kZSBwdWVkZW4gY29uc3VsdGFyIGxhcyBtdWNoYXMgcHJ1ZWJhcyBleGlzdGVudGVzIHNlZ8O6biBuZWNlc2l0ZW4uIAoKUmVjdWVyZGVuIHF1ZSBsYSBkZWNpc2nDs24gZGVwZW5kZSBkZSBzdXMgdGlwb3MgZGUgdmFyaWFibGVzIHkgc2kgdXNhbiBwYXJhbcOpdHJpY2EgbyBuby4gCgpFc3RlIGVzIHVuIGJ1ZW4gW3Jlc3VtZW5dKGh0dHBzOi8vc3RhdHMuaWRyZS51Y2xhLmVkdS9yL3doYXRzdGF0L3doYXQtc3RhdGlzdGljYWwtYW5hbHlzaXMtc2hvdWxkLWktdXNlc3RhdGlzdGljYWwtYW5hbHlzZXMtdXNpbmctci8pCgo=