Limpiamos la memoria y creamos los directorios de trabajo

rm(list=ls())
# Funcion para obtener la direccion del Script Obtengo la ubicación del Script en el Disco
dir <- paste0(dirname(rstudioapi::getActiveDocumentContext()$path),"/")
# A partir del objeto dir, creo un objeto con la dirección de la carpeta que contiene los microdatos y otro con la que contendrá los resultados.
bases.dir      <-  paste0(dirname(dir),"/Fuentes/")
resultados.dir <- paste0(dirname(dir),"/Resultados/")

Gráficos Básicos en R

Rbase tiene algunos comandos genéricos para realizar gráficos, que se adaptan al tipo de información que se le pide graficar, por ejemplo:

  • plot()
  • hist()
# iris es un set de datos clásico, que ya viene incorporado en R
plot(iris)

data(iris)
plot(iris$Sepal.Length,type = "p")

plot(iris$Sepal.Length,type = "l")

plot(iris$Sepal.Length,type = "b")

hist(iris$Sepal.Length, col = "lightsalmon1", main = "Histograma")

Si queremos grabar los gráficos en la carpeta resultados. utilizamos la variable que creamos resultados.dir y la función PNG

archivo <- paste0(resultados.dir, "grafico1.PNG")
archivo
[1] "C:/temp/Trabajos/Curso R/Resultados/grafico1.PNG"
png(archivo)
plot(iris$Sepal.Length,type = "b")
dev.off()
null device 
          1 

La función png() abre el dispositivo de imagen, luego podemos hacer los gráficos, y a continuación, con dev.off() se cierra el dispositivo y se graban los gráficos.

Los gráficos del R base son útiles para escribir de forma rápida y obtener alguna información mientras trabajamos. Muchos paquetes estadísticos permiten mostrar los resultados de forma gráfica con el comando plot (por ejemplo, las regresiones lineales lm()).

Sin embargo, existen librerías mucho mejores para crear gráficos de nivel de publicación. La más importante es ggplot2, que a su vez tiene extensiones mediante otras librerías.

Ggplot2

ggplot tiene su sintaxis propia. La idea central es pensar los gráficos como una sucesión de capas, que se construyen una a la vez.

  • El operador + nos permite incorporar nuevas capas al gráfico.

  • El comando ggplot() nos permite definir los datos y las variables (x,y,color,forma,etc).

  • Las sucesivas capas nos permiten definir:
    • Uno o más tipos de gráficos (de columnas, geom_col(), de línea, geom_line(), de puntos,geom_point(), boxplot, geom_boxplot())
    • Títulos labs()
    • Estilo del gráfico theme()
    • Escalas de los ejes scale_y_continuous,scale_x_discrete
    • División en subconjuntos facet_wrap(),facet_grid()

ggplot tiene muchos comandos, y no tiene sentido saberlos de memoria, es siempre útil reutilizar gráficos viejos y tener a mano el machete.

library(ggplot2)
ggplot(data = iris, aes(x = Sepal.Length, fill = Species))+
  geom_histogram(alpha=0.75, binwidth = .5)+
  facet_wrap(~Species)+
  labs(title = "Histograma por especie")+
  theme(legend.position = 'none')

Distribución del Ingreso

A continuación replicaremos dos gráficos del Informe Técnico de distribución del ingreso ingresos.

Gráfico 1

Cargo las librerías que voy a necesitar

library(tidyverse) # tiene ggplot, dplyr, tidyr, y otros
library(ggthemes)  # estilos de gráficos
library(ggrepel)   # etiquetas de texto más prolijas que las de ggplot
library(scales)    # tiene la función 'percent()'

Levanto las bases. Como el gráfico de Gini tiene trimestres anteriores, necesito los datos de varios trimestres. Para evitar cargar mucha información demás, al mismo tiempo que lo levanto, selecciono las columnas que necesito

Individual_t117 <- read.table(paste0(bases.dir,"usu_individual_t117.txt"),
                              sep=";", dec=",", header = TRUE, fill = TRUE)
Individual_t216 <- read.table(paste0(bases.dir,"usu_individual_t216.txt"),
                              sep=";", dec=",", header = TRUE, fill = TRUE) %>% 
  select(ANO4,TRIMESTRE, IPCF, PONDIH)
Individual_t316 <- read.table(paste0(bases.dir,"usu_individual_t316.txt"),
                              sep=";", dec=",", header = TRUE, fill = TRUE)%>% 
  select(ANO4,TRIMESTRE, IPCF, PONDIH)
Individual_t416 <- read.table(paste0(bases.dir,"usu_individual_t416.txt"),
                              sep=";", dec=",", header = TRUE, fill = TRUE)%>% 
  select(ANO4,TRIMESTRE, IPCF, PONDIH)

Junto todas las bases en una sola tabla y calculo el gini para cada período

library(reldist) #para la función 'gini'
gini <- bind_rows(Individual_t216, 
                  Individual_t316,
                  Individual_t416,
                  Individual_t117) %>%
  select(ANO4,TRIMESTRE, IPCF, PONDIH) %>% 
  mutate(periodo = paste(ANO4, TRIMESTRE, sep = "\n")) %>% 
  group_by(periodo) %>% 
  summarise(gn = gini(IPCF, weights= PONDIH)) %>% 
  ungroup()

Con los gini calculados, podemos graficar

ggplot(data = gini, aes(x = periodo, y = gn)) + 
  geom_point()

Con algunos chiches más

ggplot(gini, aes(x = periodo, y = gn, group = 'gini', label= round(gn,3))) + 
  labs(x = "Trimestre", y = "Coeficiente de Gini", title = "Coeficiente de Gini", subtitle = "Según trimestre", caption = "Fuente: EPH")+
  geom_line( size= 1 )+
  geom_point(aes(shape = periodo, color = periodo),size= 3)+ #puedo definir aes() en cada tipo de gráfico
  geom_text_repel(nudge_x = .2)+
  theme_minimal()+
  theme(legend.position = "none")
ggsave(filename = paste0(resultados.dir, "gini.png"))
Saving 7.23 x 4.46 in image

Gráfico 2

Gráfico 2. Población total según escala de ingreso individual por fuente y sexo. Total aglomerados urbanos. Primer trimestre 2017

TVI : MONTO TOTAL DE INGRESOS NO LABORALES
Totp12+P21 MONTO DE INGRESO DE LA OCUPACIÓN PRINCIPAL.+ MONTO DE INGRESO DE OTRAS OCUPACIONES.

Tengo que calcular el decil de ingreso de P47T (MONTO DE INGRESO TOTAL INDIVIDUAL). Para ello, primero hay que perturbar la variable para que no queden muchas personas con el mismo ingreso exacto.

Luego, calculo la proporción del ingreso laboral (expandido por PONDII) y del ingreso no laboral (también expandido), según decil y género

library(statar) # Para la función xtile
  datagraf_2 <-Individual_t117 %>% 
    select(P47T,T_VI, TOT_P12, P21 , PONDII, CH04) %>% 
    filter(!is.na(P47T), P47T > 0 ) %>% 
    mutate(P47T_decil         = P47T+runif(nrow(.),min = -.01,max =.01), # Perturbo la variable
           DECINDR            = xtile(P47T_decil,n=10,w = PONDII),
           ingreso_laboral    = as.numeric(TOT_P12 + P21),
           ingreso_no_laboral = as.numeric(T_VI),
           ingreso_total      = ingreso_laboral + ingreso_no_laboral,
           CH04               = case_when(CH04 == 1 ~ "Varon",
                                          CH04 == 2 ~ "Mujer",
                                          FALSE     ~ "Otro") ) %>% 
  group_by(DECINDR, CH04) %>% 
  summarise('ingreso laboral'    = sum(ingreso_laboral*PONDII)/sum(ingreso_total*PONDII),
            'ingreso no laboral' = sum(ingreso_no_laboral * PONDII)/sum(ingreso_total*PONDII)) %>% 
gather(tipo_ingreso, monto,3:4 ) 
datagraf_2
ggplot(datagraf_2, aes(CH04, monto, fill = tipo_ingreso, 
                      label = sprintf("%1.1f%%", 100*monto)))+
  geom_col(position = "stack", alpha=0.6) + 
  geom_text(position = position_stack(vjust = 0.5), size=3)+
  labs(x="",y="Porcentaje")+
  theme_tufte()+
  scale_fill_fivethirtyeight()+
  scale_y_continuous(labels = percent)+
  theme(legend.position = "bottom",
        legend.title=element_blank(),
        axis.text.x = element_text(angle=25))+
  facet_grid(.~DECINDR)
ggsave(filename = paste0(resultados.dir, "ingreso por decil.png"),scale = 2)
Saving 14.5 x 8.92 in image

En los gráficos utilizamos extensiones de ggplot:

  • ggrepel geom_text_repel() y scale_fill_fivethirtyeight()
  • ggthemes theme_tufte()

simplemente debemos recordar cargar las librerías si queremos utilizar esas funciones.

Otros gráficos

Los gráficos de barras, línea o de torta son fácilmente reproducibles en un excel, con información agregada. Sin embargo, hay cosas que no son replicables en excel:

  • Gráficos que necesitan la información a nivel de microdatos. puntos, boxplots, Kernels, etc.
  • Abrir el mismo gráfico según alguna variable discreta: facet_wrap()
  • Parametrizar otras variables, para aumentar la dimensionalidad del gráficos.
    • color color =
    • rellenofill =
    • forma shape =
    • tamaño size =
    • transparencia alpha =

Esto permite tener, en el plano, gráficos de muchas dimensiones de análisis

Cuando queremos utilizar estos parámetros para representar una variable, los definimos dentro del aes(), aes(... color = ingresos), cuando queremos simplemente mejorar el diseño, se asignan por fuera, o dentro de cada tipo de gráficos, geom_col(color = 'green').

Boxplots

Hacemos un procesamiento simple: Sacamos los ingresos iguales a cero y las no respuestas de nivel educativo.

Las variables sexo( CH04 ) y Nivel educativo están codificadas como números, y el R las entiende como numéricas.

class(Individual_t117$NIVEL_ED)
[1] "integer"
class(Individual_t117$CH04)
[1] "integer"

Es importante que las variables sean del tipo que conceptualmente les corresponde (el nivel educativo es una variable categórica, no continua), para que el ggplot pueda graficarlo correctamente.

ggdata <- Individual_t117 %>% 
  filter(P21>0, !is.na(NIVEL_ED)) %>% 
  mutate(NIVEL_ED = as.factor(NIVEL_ED),
         CH04     = as.factor(CH04))

si queremos hacer un boxplot del ingreso para cada nivel educativo, asignamos esta variable a x, group y fill

ggplot(ggdata, aes(x = NIVEL_ED, y = P21, group = NIVEL_ED, fill = NIVEL_ED )) +
  geom_boxplot()+
  scale_y_continuous(limits = c(0, 40000))

Si queremos agregar la dimensión sexo, podemos hacer un facet_wrap()

ggplot(ggdata, aes(x= NIVEL_ED, y = P21, group = NIVEL_ED, fill = NIVEL_ED )) +
  geom_boxplot()+
  scale_y_continuous(limits = c(0, 40000))+
  facet_wrap(~ CH04, labeller = "label_both")

En este gráfico, el foco de atención sigue puesto en las diferencias de nivel educativo, pero neutralizamos el efecto de la variable sexo.

Si lo que queremos hacer es poner el foco de atención en las diferencias por sexo, neutralizamos el efecto del nivel educativo, facetiando por nivel educativo.

ggplot(ggdata, aes(x= CH04, y = P21, group = CH04, fill = CH04 )) +
  geom_boxplot()+
  scale_y_continuous(limits = c(0, 40000))+
  facet_wrap(~ NIVEL_ED, labeller = "label_both")

Kernels

Podemos hacer una nueva versión del gráfico 2. Utilizando un procesamiento similar al que hicimos antes.

datagraf <-Individual_t117 %>% 
  select(REGION,P47T,T_VI, TOT_P12, P21 , PONDII, CH04) %>% 
  filter(!is.na(P47T), P47T > 0 ) %>% 
  mutate(REGION             = case_when(REGION == 1    ~ 'GBA',
                                        REGION == 40   ~ 'NOA',
                                        REGION == 41   ~ 'NEA',
                                        REGION == 42   ~ 'Cuyo',
                                        REGION == 43   ~ 'Peampeana',
                                        REGION == 44   ~ 'Patagonia',
                                        FALSE          ~ 'otro'),
         ingreso_laboral    = as.numeric(TOT_P12 + P21),
         ingreso_no_laboral = as.numeric(T_VI),
         CH04               = case_when(CH04 == 1 ~ "Varon",
                                        CH04 == 2 ~ "Mujer",
                                        FALSE     ~ "Otro") ) %>% 
  gather(., key = Tipo_ingreso, Ingreso, c((ncol(.)-1):ncol(.)))
datagraf  

Con los Kernels, no necesitamos dividir a la población en deciles, porque podemos tener una mirada completa de la forma de la distribución.

Para este gráfico, quiero eliminar los ingresos = 0

datagraf2 <- datagraf %>% filter( Ingreso !=0)
  
  
ggplot(datagraf2, aes(
  x = Ingreso,
  weights = PONDII,
  group = Tipo_ingreso,
  fill = Tipo_ingreso)) +
  geom_density(alpha=0.7,adjust =2)+
  labs(x="Distribución del ingreso", y="",
       title=" Total según tipo de ingreso y género", 
       caption = "Fuente: Encuesta Permanente de Hogares")+
  scale_x_continuous(limits = c(0,50000))+
  theme_tufte()+
  scale_fill_gdocs()+
  theme(legend.position = "bottom",
        plot.title      = element_text(size=12))+
  facet_wrap(~ CH04, scales = "free")
ggsave(filename = paste0(resultados.dir, "Kernel_1.png"),scale = 2)
Saving 14.5 x 8.92 in image

En este tipo de gráficos, importa mucho qué variable se utiliza para facetear y qué variable para agrupar, ya que la construcción de la distribución es diferente.

ggplot(datagraf2, aes(
  x = Ingreso,
  weights = PONDII,
  group = CH04,
  fill = CH04)) +
  geom_density(alpha=0.7,adjust =2)+
  labs(x="Distribución del ingreso", y="",
       title=" Total según tipo de ingreso y género", 
       caption = "Fuente: Encuesta Permanente de Hogares")+
  scale_x_continuous(limits = c(0,50000))+
  theme_tufte()+
  scale_fill_gdocs()+
  theme(legend.position = "bottom",
        plot.title      = element_text(size=12))+
  facet_wrap(~Tipo_ingreso, scales = "free")
ggsave(filename = paste0(resultados.dir, "Kernel_1.png"),scale = 2)
Saving 14.5 x 8.92 in image

El eje y no tiene demasiada interpretabilidad en los Kernel, porque hace a la forma en que se construyen las distribuciones.

Extensión de ggplot: librería : ggjoy

Esta extensión aprovecha que el eje y no es importante, y lo utiliza para apilar las distribuciones que se están comparando de forma más prolija.

Si por ejemplo queremos comparar la distribución del ingreso laboral según región:

library(ggjoy)  
datagraf3 <- datagraf2 %>% filter(Tipo_ingreso == "ingreso_laboral")
ggplot(datagraf3, aes(
           x = Ingreso,
           y = REGION,
           weights = PONDII,
           group = REGION,
           fill = REGION
           )) +
  geom_joy(alpha=0.6, bandwidth = 1500)+
  labs(x="", y="",
       title="Distribución del ingreso laboral según región", 
       caption = "Fuente: Encuesta Permanente de Hogares")+
  scale_x_continuous(limits = c(0,35000))+
  theme_tufte()+
  scale_fill_gdocs()+
  theme(legend.position = "none",
        legend.title = element_blank(),
        plot.title   = element_text(size = 12))
ggsave(filename = paste0(resultados.dir, "Kernel_2.png"),scale = 2)
Saving 14.5 x 8.92 in image

Tendencia

Para realizar estos gráficos, vamos a modificar un poco los datos:

  • filtramos los ingresos iguales a 0.
  • eliminamos las no respuestas de nivel educativo y las personas con educación especial.
  • eliminamos las respuestas de tipo de establecimiento = ‘otros’.
  • recodificamos las variables para que tengan nombres más sugestivos:
    • Nivel educativo además la convertimos a factor, porque queremos explicitarle el orden de los valores con levels(). El “\n”" es un caracter especial que permite que el string continúe en la siguiente línea.
    • Sexo.
    • Tipo de establecimiento.
ggdata <- Individual_t117 %>% 
  filter(P21>0,
         !is.na(NIVEL_ED),
         NIVEL_ED!=7, 
         PP04A !=3) %>% 
  mutate(NIVEL_ED = factor(case_when(NIVEL_ED == 1  ~ 'Primaria \n Incompleta', # '\n' significa carriage return, o enter
                                     NIVEL_ED == 2  ~ 'Primaria \n Completa',
                                     NIVEL_ED == 3  ~ 'Secundaria \nIncompleta',
                                     NIVEL_ED == 4  ~ 'Secundaria \nCompleta',
                                     NIVEL_ED == 5  ~ 'Superior \nUniversitaria \nIncompleta',
                                     NIVEL_ED == 6  ~ 'Superior \nUniversitaria \nCompleta',
                                     FALSE          ~ 'Otro'),
                           levels= c('Primaria \n Incompleta',
                                     'Primaria \n Completa',
                                     'Secundaria \nIncompleta',
                                     'Secundaria \nCompleta',
                                     'Superior \nUniversitaria \nIncompleta',
                                     'Superior \nUniversitaria \nCompleta')),
         Sexo     = case_when(CH04 == 1 ~ 'Varón',
                              CH04 == 2 ~ 'Mujer'),
         Establecimiento    = case_when(PP04A == 1 ~ 'Estatal',
                                        PP04A == 2 ~ 'Privado',
                                        FALSE      ~ 'Otro'))
ggdata

Para graficar un suavizado de las series, se utiliza la función geom_smooth(). Con suavizado nos referimos al gráfico de un modelo realizado sobre los datos, que estima el valor en el punto x,y (para el grupo). Las regresiones lineales son un ejemplo de esto, aunque no el único, ni el que viene por default.

ggplot(ggdata, aes(CH06, P21, colour = Sexo, shape = Sexo, alpha = P21))+
  geom_smooth() + 
  labs(
    x = 'Edad',
    y = 'ingreso',
    title = 'Ingreso por ocupación principal',
    subtitle = 'Según edad, nivel educativo y sexo') +
  theme_minimal()+
  scale_y_continuous(labels = comma)+
  scale_alpha(guide = FALSE)+
  facet_grid(.~NIVEL_ED)

Si corremos el comando geom_smooth() por default, nos advierte que esta utilizando el método GAM, de general additive models.

el sombreado gris que envuelve cada línea es el intervalo de confianza de dicho punto (95% por default).

También podemos utilizar métodos lineales, agregando el parámetro method = 'lm'. Haciendo esto, el gráfico muestra una regresión lineal simple. Si queremos otro tipo de regresión lineal, le podemos explicitar la fórmula.
En el ejemplo siguiente, utilizamos la formula $y = _0 +_1x +_2 x^2 $.

ggplot(ggdata, aes(CH06, P21, colour = Sexo, weight = PONDIIO)) +
  geom_smooth(method = "lm", formula = y ~ poly(x, 2)) +
  labs(x = 'Edad',
       y = 'ingreso',
       title = 'Regresion cuadrática del Ingreso por ocupación principal respecto de la Edad',
       subtitle = 'Según Nivel educativo y sexo') +
  theme_minimal()+
  facet_grid(. ~ NIVEL_ED)

Si quisiéramos, además de ver la relación entre ingreso, Edad, Sexo y Nivel educativo, incorporar el tipo de establecimiento,público o privado. Podemos facetear el gráfico por dos variables en lugar de una, lo que crea una matriz de gráficos según los cruces.

ggplot(ggdata, aes(CH06, P21, colour = Establecimiento, weight = PONDIIO)) +
  geom_smooth(method = "lm") +
  labs(
  x = 'Edad',
  y = 'ingreso',
  title = 'Tendencia del ingreso por ocupación principal',
  subtitle = 'Según edad, nivel educativo, sexo y tipo de establecimiento') +
  theme_minimal()+
  facet_grid(Sexo ~ NIVEL_ED)

ggsave(filename = paste0(resultados.dir, "regresion lineal.png"),scale = 2)
Saving 16 x 10 in image

Ejercicios para practicar

  • Graficar la distribución del ingreso por ocupación principal según categoría ocupacional, con el tipo de gráfico Kernel.
  • Incorporar en el gráfico anterior la condición de precariedad laboral (PP07H).
  • Quedarse sólo con los asalariados, y graficar la relación entre ingreso por ocupación principal, precariedad laboral y tamaño del establecimiento.
  • Hacer boxplots del ingreso por ocuapción principal, según sexo y condición de precariedad.
  • Incluir en el gráfico anteriro la dimensión de tamaño del establecimiento.
LS0tDQp0aXRsZTogIkNsYXNlIDMuIEdyw6FmaWNvcyB5IERpc3RyaWJ1Y2nDs24gZGVsIEluZ3Jlc28iDQphdXRob3I6ICJHdWlkbyBXZWtzbGVyIHkgRGllZ28gS296bG93c2tpIg0KZGF0ZTogIjIwIGRlIE9jdHVicmUgMjAxNyINCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vazogDQogICAgdG9jOiB0cnVlDQogICAgdG9jX2Zsb2F0OiB0cnVlIA0KLS0tDQoNCkxpbXBpYW1vcyBsYSBtZW1vcmlhIHkgY3JlYW1vcyBsb3MgZGlyZWN0b3Jpb3MgZGUgdHJhYmFqbyANCg0KYGBge3J9DQpybShsaXN0PWxzKCkpDQoNCiMgRnVuY2lvbiBwYXJhIG9idGVuZXIgbGEgZGlyZWNjaW9uIGRlbCBTY3JpcHQgT2J0ZW5nbyBsYSB1YmljYWNpw7NuIGRlbCBTY3JpcHQgZW4gZWwgRGlzY28NCmRpciA8LSBwYXN0ZTAoZGlybmFtZShyc3R1ZGlvYXBpOjpnZXRBY3RpdmVEb2N1bWVudENvbnRleHQoKSRwYXRoKSwiLyIpDQojIEEgcGFydGlyIGRlbCBvYmpldG8gZGlyLCBjcmVvIHVuIG9iamV0byBjb24gbGEgZGlyZWNjacOzbiBkZSBsYSBjYXJwZXRhIHF1ZSBjb250aWVuZSBsb3MgbWljcm9kYXRvcyB5IG90cm8gY29uIGxhIHF1ZSBjb250ZW5kcsOhIGxvcyByZXN1bHRhZG9zLg0KYmFzZXMuZGlyICAgICAgPC0gIHBhc3RlMChkaXJuYW1lKGRpciksIi9GdWVudGVzLyIpDQpyZXN1bHRhZG9zLmRpciA8LSBwYXN0ZTAoZGlybmFtZShkaXIpLCIvUmVzdWx0YWRvcy8iKQ0KDQpgYGANCg0KDQojIEdyw6FmaWNvcyBCw6FzaWNvcyBlbiBSDQoNClJiYXNlICB0aWVuZSBhbGd1bm9zIGNvbWFuZG9zIGdlbsOpcmljb3MgcGFyYSByZWFsaXphciBncsOhZmljb3MsIHF1ZSBzZSBhZGFwdGFuIGFsIHRpcG8gZGUgaW5mb3JtYWNpw7NuIHF1ZSBzZSBsZSBwaWRlIGdyYWZpY2FyLCBwb3IgZWplbXBsbzoNCg0KLSBwbG90KCkNCi0gaGlzdCgpDQoNCmBgYHtyIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTh9DQojIGlyaXMgZXMgdW4gc2V0IGRlIGRhdG9zIGNsw6FzaWNvLCBxdWUgeWEgdmllbmUgaW5jb3Jwb3JhZG8gZW4gUg0KcGxvdChpcmlzKQ0KYGBgDQpgYGB7cn0NCmRhdGEoaXJpcykNCnBsb3QoaXJpcyRTZXBhbC5MZW5ndGgsdHlwZSA9ICJwIikNCnBsb3QoaXJpcyRTZXBhbC5MZW5ndGgsdHlwZSA9ICJsIikNCnBsb3QoaXJpcyRTZXBhbC5MZW5ndGgsdHlwZSA9ICJiIikNCmhpc3QoaXJpcyRTZXBhbC5MZW5ndGgsIGNvbCA9ICJsaWdodHNhbG1vbjEiLCBtYWluID0gIkhpc3RvZ3JhbWEiKQ0KYGBgDQoNCg0KU2kgcXVlcmVtb3MgZ3JhYmFyIGxvcyBncsOhZmljb3MgZW4gbGEgY2FycGV0YSByZXN1bHRhZG9zLiB1dGlsaXphbW9zIGxhIHZhcmlhYmxlIHF1ZSBjcmVhbW9zIF9yZXN1bHRhZG9zLmRpcl8geSBsYSBmdW5jacOzbiBQTkcNCmBgYHtyfQ0KYXJjaGl2byA8LSBwYXN0ZTAocmVzdWx0YWRvcy5kaXIsICJncmFmaWNvMS5QTkciKQ0KYXJjaGl2bw0KcG5nKGFyY2hpdm8pDQpwbG90KGlyaXMkU2VwYWwuTGVuZ3RoLHR5cGUgPSAiYiIpDQpkZXYub2ZmKCkNCmBgYA0KDQpMYSBmdW5jacOzbiBgYGBwbmcoKWBgYCBfYWJyZSBlbCBkaXNwb3NpdGl2byBkZSBpbWFnZW5fLCBsdWVnbyBwb2RlbW9zIGhhY2VyIGxvcyBncsOhZmljb3MsIHkgYSBjb250aW51YWNpw7NuLCBjb24gYGBgZGV2Lm9mZigpYGBgIHNlIF9jaWVycmEgZWwgZGlzcG9zaXRpdm9fIHkgc2UgZ3JhYmFuIGxvcyBncsOhZmljb3MuIA0KDQpMb3MgZ3LDoWZpY29zIGRlbCBSIGJhc2Ugc29uIMO6dGlsZXMgcGFyYSBlc2NyaWJpciBkZSBmb3JtYSByw6FwaWRhIHkgb2J0ZW5lciBhbGd1bmEgaW5mb3JtYWNpw7NuIG1pZW50cmFzIHRyYWJhamFtb3MuIE11Y2hvcyBwYXF1ZXRlcyBlc3RhZMOtc3RpY29zIHBlcm1pdGVuIG1vc3RyYXIgbG9zIHJlc3VsdGFkb3MgZGUgZm9ybWEgZ3LDoWZpY2EgY29uIGVsIGNvbWFuZG8gcGxvdCAocG9yIGVqZW1wbG8sIGxhcyByZWdyZXNpb25lcyBsaW5lYWxlcyBgYGBsbSgpYGBgKS4gICAgICAgDQogDQpTaW4gZW1iYXJnbywgZXhpc3RlbiBsaWJyZXLDrWFzIG11Y2hvIG1lam9yZXMgcGFyYSBjcmVhciBncsOhZmljb3MgZGUgbml2ZWwgZGUgcHVibGljYWNpw7NuLiBMYSBtw6FzIGltcG9ydGFudGUgZXMgX19nZ3Bsb3QyX18sIHF1ZSBhIHN1IHZleiB0aWVuZSBleHRlbnNpb25lcyBtZWRpYW50ZSBvdHJhcyBsaWJyZXLDrWFzLg0KDQoNCiNbR2dwbG90Ml0oaHR0cHM6Ly93d3cucnN0dWRpby5jb20vd3AtY29udGVudC91cGxvYWRzLzIwMTUvMDMvZ2dwbG90Mi1jaGVhdHNoZWV0LnBkZikNCg0KDQpnZ3Bsb3QgdGllbmUgc3Ugc2ludGF4aXMgcHJvcGlhLiBMYSBpZGVhIGNlbnRyYWwgZXMgcGVuc2FyIGxvcyBncsOhZmljb3MgY29tbyB1bmEgc3VjZXNpw7NuIGRlIGNhcGFzLCBxdWUgc2UgY29uc3RydXllbiB1bmEgYSBsYSB2ZXouICAgIA0KDQotIEVsIG9wZXJhZG9yIF9fYGBgK2BgYF9fIG5vcyBwZXJtaXRlIGluY29ycG9yYXIgbnVldmFzIGNhcGFzIGFsIGdyw6FmaWNvLg0KDQotIEVsIGNvbWFuZG8gYGBgZ2dwbG90KClgYGAgbm9zIHBlcm1pdGUgZGVmaW5pciBsb3MgX19kYXRvc19fIHkgbGFzIF9fdmFyaWFibGVzX18gKHgseSxjb2xvcixmb3JtYSxldGMpLiANCg0KLSBMYXMgc3VjZXNpdmFzIGNhcGFzIG5vcyBwZXJtaXRlbiBkZWZpbmlyOg0KICAgIC0gVW5vIG8gbcOhcyB0aXBvcyBkZSBncsOhZmljb3MgKGRlIGNvbHVtbmFzLCBgYGBnZW9tX2NvbCgpYGBgLCBkZSBsw61uZWEsIGBgYGdlb21fbGluZSgpYGBgLCBkZSBwdW50b3MsYGBgZ2VvbV9wb2ludCgpYGBgLCBib3hwbG90LCBgYGBnZW9tX2JveHBsb3QoKWBgYCkNCiAgICAtIFTDrXR1bG9zIGBgYGxhYnMoKWBgYA0KICAgIC0gRXN0aWxvIGRlbCBncsOhZmljbyBgYGB0aGVtZSgpYGBgDQogICAgLSBFc2NhbGFzIGRlIGxvcyBlamVzIGBgYHNjYWxlX3lfY29udGludW91c2BgYCxgYGBzY2FsZV94X2Rpc2NyZXRlYGBgIA0KICAgIC0gRGl2aXNpw7NuIGVuIHN1YmNvbmp1bnRvcyBgYGBmYWNldF93cmFwKClgYGAsYGBgZmFjZXRfZ3JpZCgpYGBgDQoNCmdncGxvdCB0aWVuZSBfX211Y2hvc19fIGNvbWFuZG9zLCB5IG5vIHRpZW5lIHNlbnRpZG8gc2FiZXJsb3MgZGUgbWVtb3JpYSwgZXMgc2llbXByZSDDunRpbCByZXV0aWxpemFyIGdyw6FmaWNvcyB2aWVqb3MgeSB0ZW5lciBhIG1hbm8gZWwgW21hY2hldGVdKGh0dHBzOi8vd3d3LnJzdHVkaW8uY29tL3dwLWNvbnRlbnQvdXBsb2Fkcy8yMDE2LzExL2dncGxvdDItY2hlYXRzaGVldC0yLjEucGRmKS4NCg0KYGBge3J9DQpsaWJyYXJ5KGdncGxvdDIpDQpnZ3Bsb3QoZGF0YSA9IGlyaXMsIGFlcyh4ID0gU2VwYWwuTGVuZ3RoLCBmaWxsID0gU3BlY2llcykpKw0KICBnZW9tX2hpc3RvZ3JhbShhbHBoYT0wLjc1LCBiaW53aWR0aCA9IC41KSsNCiAgZmFjZXRfd3JhcCh+U3BlY2llcykrDQogIGxhYnModGl0bGUgPSAiSGlzdG9ncmFtYSBwb3IgZXNwZWNpZSIpKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAnbm9uZScpDQpgYGANCg0KDQoNCiMgRGlzdHJpYnVjacOzbiBkZWwgSW5ncmVzbw0KDQpBIGNvbnRpbnVhY2nDs24gcmVwbGljYXJlbW9zIGRvcyBncsOhZmljb3MgZGVsIFtJbmZvcm1lIFTDqWNuaWNvIGRlIGRpc3RyaWJ1Y2nDs24gZGVsIGluZ3Jlc28gaW5ncmVzb3NdKGh0dHA6Ly93d3cuaW5kZWMuZ29iLmFyL3VwbG9hZHMvaW5mb3JtZXNkZXByZW5zYS9pbmdyZXNvc18xdHJpbTE3LnBkZikuDQoNCiMjIEdyw6FmaWNvIDENCg0KDQpDYXJnbyBsYXMgbGlicmVyw61hcyBxdWUgdm95IGEgbmVjZXNpdGFyDQoNCmBgYHtyfQ0KbGlicmFyeSh0aWR5dmVyc2UpICMgdGllbmUgZ2dwbG90LCBkcGx5ciwgdGlkeXIsIHkgb3Ryb3MNCmxpYnJhcnkoZ2d0aGVtZXMpICAjIGVzdGlsb3MgZGUgZ3LDoWZpY29zDQpsaWJyYXJ5KGdncmVwZWwpICAgIyBldGlxdWV0YXMgZGUgdGV4dG8gbcOhcyBwcm9saWphcyBxdWUgbGFzIGRlIGdncGxvdA0KbGlicmFyeShzY2FsZXMpICAgICMgdGllbmUgbGEgZnVuY2nDs24gJ3BlcmNlbnQoKScNCg0KYGBgDQoNCkxldmFudG8gbGFzIGJhc2VzLiBDb21vIGVsIGdyw6FmaWNvIGRlIEdpbmkgdGllbmUgdHJpbWVzdHJlcyBhbnRlcmlvcmVzLCBuZWNlc2l0byBsb3MgZGF0b3MgZGUgdmFyaW9zIHRyaW1lc3RyZXMuIFBhcmEgZXZpdGFyIGNhcmdhciBtdWNoYSBpbmZvcm1hY2nDs24gZGVtw6FzLCBhbCBtaXNtbyB0aWVtcG8gcXVlIGxvIGxldmFudG8sIHNlbGVjY2lvbm8gbGFzIGNvbHVtbmFzIHF1ZSBuZWNlc2l0bw0KDQpgYGB7cn0NCg0KSW5kaXZpZHVhbF90MTE3IDwtIHJlYWQudGFibGUocGFzdGUwKGJhc2VzLmRpciwidXN1X2luZGl2aWR1YWxfdDExNy50eHQiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlcD0iOyIsIGRlYz0iLCIsIGhlYWRlciA9IFRSVUUsIGZpbGwgPSBUUlVFKQ0KSW5kaXZpZHVhbF90MjE2IDwtIHJlYWQudGFibGUocGFzdGUwKGJhc2VzLmRpciwidXN1X2luZGl2aWR1YWxfdDIxNi50eHQiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlcD0iOyIsIGRlYz0iLCIsIGhlYWRlciA9IFRSVUUsIGZpbGwgPSBUUlVFKSAlPiUgDQogIHNlbGVjdChBTk80LFRSSU1FU1RSRSwgSVBDRiwgUE9ORElIKQ0KDQpJbmRpdmlkdWFsX3QzMTYgPC0gcmVhZC50YWJsZShwYXN0ZTAoYmFzZXMuZGlyLCJ1c3VfaW5kaXZpZHVhbF90MzE2LnR4dCIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VwPSI7IiwgZGVjPSIsIiwgaGVhZGVyID0gVFJVRSwgZmlsbCA9IFRSVUUpJT4lIA0KICBzZWxlY3QoQU5PNCxUUklNRVNUUkUsIElQQ0YsIFBPTkRJSCkNCg0KSW5kaXZpZHVhbF90NDE2IDwtIHJlYWQudGFibGUocGFzdGUwKGJhc2VzLmRpciwidXN1X2luZGl2aWR1YWxfdDQxNi50eHQiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlcD0iOyIsIGRlYz0iLCIsIGhlYWRlciA9IFRSVUUsIGZpbGwgPSBUUlVFKSU+JSANCiAgc2VsZWN0KEFOTzQsVFJJTUVTVFJFLCBJUENGLCBQT05ESUgpDQoNCmBgYA0KDQpKdW50byB0b2RhcyBsYXMgYmFzZXMgZW4gdW5hIHNvbGEgdGFibGEgeSBjYWxjdWxvIGVsIGdpbmkgcGFyYSBjYWRhIHBlcsOtb2RvDQoNCmBgYHtyIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTZ9DQpsaWJyYXJ5KHJlbGRpc3QpICNwYXJhIGxhIGZ1bmNpw7NuICdnaW5pJw0KDQoNCmdpbmkgPC0gYmluZF9yb3dzKEluZGl2aWR1YWxfdDIxNiwgDQogICAgICAgICAgICAgICAgICBJbmRpdmlkdWFsX3QzMTYsDQogICAgICAgICAgICAgICAgICBJbmRpdmlkdWFsX3Q0MTYsDQogICAgICAgICAgICAgICAgICBJbmRpdmlkdWFsX3QxMTcpICU+JQ0KICBzZWxlY3QoQU5PNCxUUklNRVNUUkUsIElQQ0YsIFBPTkRJSCkgJT4lIA0KICBtdXRhdGUocGVyaW9kbyA9IHBhc3RlKEFOTzQsIFRSSU1FU1RSRSwgc2VwID0gIlxuIikpICU+JSANCiAgZ3JvdXBfYnkocGVyaW9kbykgJT4lIA0KICBzdW1tYXJpc2UoZ24gPSBnaW5pKElQQ0YsIHdlaWdodHM9IFBPTkRJSCkpICU+JSANCiAgdW5ncm91cCgpDQoNCg0KYGBgDQoNCkNvbiBsb3MgZ2luaSBjYWxjdWxhZG9zLCBwb2RlbW9zIGdyYWZpY2FyDQpgYGB7cn0NCmdncGxvdChkYXRhID0gZ2luaSwgYWVzKHggPSBwZXJpb2RvLCB5ID0gZ24pKSArIA0KICBnZW9tX3BvaW50KCkNCg0KDQpgYGANCg0KQ29uIGFsZ3Vub3MgY2hpY2hlcyBtw6FzDQpgYGB7cn0NCg0KZ2dwbG90KGdpbmksIGFlcyh4ID0gcGVyaW9kbywgeSA9IGduLCBncm91cCA9ICdnaW5pJywgbGFiZWw9IHJvdW5kKGduLDMpKSkgKyANCiAgbGFicyh4ID0gIlRyaW1lc3RyZSIsIHkgPSAiQ29lZmljaWVudGUgZGUgR2luaSIsIHRpdGxlID0gIkNvZWZpY2llbnRlIGRlIEdpbmkiLCBzdWJ0aXRsZSA9ICJTZWfDum4gdHJpbWVzdHJlIiwgY2FwdGlvbiA9ICJGdWVudGU6IEVQSCIpKw0KICBnZW9tX2xpbmUoIHNpemU9IDEgKSsNCiAgZ2VvbV9wb2ludChhZXMoc2hhcGUgPSBwZXJpb2RvLCBjb2xvciA9IHBlcmlvZG8pLHNpemU9IDMpKyAjcHVlZG8gZGVmaW5pciBhZXMoKSBlbiBjYWRhIHRpcG8gZGUgZ3LDoWZpY28NCiAgZ2VvbV90ZXh0X3JlcGVsKG51ZGdlX3ggPSAuMikrDQogIHRoZW1lX21pbmltYWwoKSsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQ0KDQpnZ3NhdmUoZmlsZW5hbWUgPSBwYXN0ZTAocmVzdWx0YWRvcy5kaXIsICJnaW5pLnBuZyIpKQ0KYGBgDQoNCg0KIyMgR3LDoWZpY28gMg0KDQpHcsOhZmljbyAyLiBQb2JsYWNpw7NuIHRvdGFsIHNlZ8O6biBlc2NhbGEgZGUgaW5ncmVzbyBpbmRpdmlkdWFsIHBvciBmdWVudGUgeSBzZXhvLiANCiAgICAgICAgICAgVG90YWwgYWdsb21lcmFkb3MgdXJiYW5vcy4gUHJpbWVyIHRyaW1lc3RyZSAyMDE3DQoNCl9UVklfIDogTU9OVE8gVE9UQUwgREUgSU5HUkVTT1MgTk8gTEFCT1JBTEVTICAgICAgIA0KX1RvdHAxMitQMjFfIE1PTlRPIERFIElOR1JFU08gREUgTEEgT0NVUEFDScOTTiBQUklOQ0lQQUwuKyBNT05UTyBERSBJTkdSRVNPIERFIE9UUkFTIE9DVVBBQ0lPTkVTLg0KDQoNClRlbmdvIHF1ZSBjYWxjdWxhciBlbCBkZWNpbCBkZSBpbmdyZXNvIGRlIFA0N1QgKE1PTlRPIERFIElOR1JFU08gVE9UQUwgSU5ESVZJRFVBTCkuIFBhcmEgZWxsbywgcHJpbWVybyBoYXkgcXVlIF9wZXJ0dXJiYXIgbGEgdmFyaWFibGVfIHBhcmEgcXVlIG5vIHF1ZWRlbiBtdWNoYXMgcGVyc29uYXMgY29uIGVsIG1pc21vIGluZ3Jlc28gZXhhY3RvLg0KDQpMdWVnbywgY2FsY3VsbyBsYSBwcm9wb3JjacOzbiBkZWwgaW5ncmVzbyBsYWJvcmFsIChleHBhbmRpZG8gcG9yIFBPTkRJSSkgeSBkZWwgaW5ncmVzbyBubyBsYWJvcmFsICh0YW1iacOpbiBleHBhbmRpZG8pLCBzZWfDum4gZGVjaWwgeSBnw6luZXJvDQoNCmBgYHtyIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTh9DQpsaWJyYXJ5KHN0YXRhcikgIyBQYXJhIGxhIGZ1bmNpw7NuIHh0aWxlDQoNCiAgZGF0YWdyYWZfMiA8LUluZGl2aWR1YWxfdDExNyAlPiUgDQogICAgc2VsZWN0KFA0N1QsVF9WSSwgVE9UX1AxMiwgUDIxICwgUE9ORElJLCBDSDA0KSAlPiUgDQogICAgZmlsdGVyKCFpcy5uYShQNDdUKSwgUDQ3VCA+IDAgKSAlPiUgDQogICAgbXV0YXRlKFA0N1RfZGVjaWwgICAgICAgICA9IFA0N1QrcnVuaWYobnJvdyguKSxtaW4gPSAtLjAxLG1heCA9LjAxKSwgIyBQZXJ0dXJibyBsYSB2YXJpYWJsZQ0KICAgICAgICAgICBERUNJTkRSICAgICAgICAgICAgPSB4dGlsZShQNDdUX2RlY2lsLG49MTAsdyA9IFBPTkRJSSksDQogICAgICAgICAgIGluZ3Jlc29fbGFib3JhbCAgICA9IGFzLm51bWVyaWMoVE9UX1AxMiArIFAyMSksDQogICAgICAgICAgIGluZ3Jlc29fbm9fbGFib3JhbCA9IGFzLm51bWVyaWMoVF9WSSksDQogICAgICAgICAgIGluZ3Jlc29fdG90YWwgICAgICA9IGluZ3Jlc29fbGFib3JhbCArIGluZ3Jlc29fbm9fbGFib3JhbCwNCiAgICAgICAgICAgQ0gwNCAgICAgICAgICAgICAgID0gY2FzZV93aGVuKENIMDQgPT0gMSB+ICJWYXJvbiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBDSDA0ID09IDIgfiAiTXVqZXIiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRkFMU0UgICAgIH4gIk90cm8iKSApICU+JSANCiAgZ3JvdXBfYnkoREVDSU5EUiwgQ0gwNCkgJT4lIA0KICBzdW1tYXJpc2UoJ2luZ3Jlc28gbGFib3JhbCcgICAgPSBzdW0oaW5ncmVzb19sYWJvcmFsKlBPTkRJSSkvc3VtKGluZ3Jlc29fdG90YWwqUE9ORElJKSwNCiAgICAgICAgICAgICdpbmdyZXNvIG5vIGxhYm9yYWwnID0gc3VtKGluZ3Jlc29fbm9fbGFib3JhbCAqIFBPTkRJSSkvc3VtKGluZ3Jlc29fdG90YWwqUE9ORElJKSkgJT4lIA0KZ2F0aGVyKHRpcG9faW5ncmVzbywgbW9udG8sMzo0ICkgDQoNCmRhdGFncmFmXzINCg0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGFncmFmXzIsIGFlcyhDSDA0LCBtb250bywgZmlsbCA9IHRpcG9faW5ncmVzbywgDQogICAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSBzcHJpbnRmKCIlMS4xZiUlIiwgMTAwKm1vbnRvKSkpKw0KICBnZW9tX2NvbChwb3NpdGlvbiA9ICJzdGFjayIsIGFscGhhPTAuNikgKyANCiAgZ2VvbV90ZXh0KHBvc2l0aW9uID0gcG9zaXRpb25fc3RhY2sodmp1c3QgPSAwLjUpLCBzaXplPTMpKw0KICBsYWJzKHg9IiIseT0iUG9yY2VudGFqZSIpKw0KICB0aGVtZV90dWZ0ZSgpKw0KICBzY2FsZV9maWxsX2ZpdmV0aGlydHllaWdodCgpKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gcGVyY2VudCkrDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLA0KICAgICAgICBsZWdlbmQudGl0bGU9ZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT0yNSkpKw0KICBmYWNldF9ncmlkKC5+REVDSU5EUikNCg0KZ2dzYXZlKGZpbGVuYW1lID0gcGFzdGUwKHJlc3VsdGFkb3MuZGlyLCAiaW5ncmVzbyBwb3IgZGVjaWwucG5nIiksc2NhbGUgPSAyKQ0KYGBgDQoNCkVuIGxvcyBncsOhZmljb3MgdXRpbGl6YW1vcyBleHRlbnNpb25lcyBkZSBnZ3Bsb3Q6IA0KDQotIGdncmVwZWwgYGBgZ2VvbV90ZXh0X3JlcGVsKClgYGAgeSBgYGBzY2FsZV9maWxsX2ZpdmV0aGlydHllaWdodCgpYGBgDQotIGdndGhlbWVzIGBgYHRoZW1lX3R1ZnRlKClgYGANCg0Kc2ltcGxlbWVudGUgZGViZW1vcyByZWNvcmRhciBjYXJnYXIgbGFzIGxpYnJlcsOtYXMgc2kgcXVlcmVtb3MgdXRpbGl6YXIgZXNhcyBmdW5jaW9uZXMuDQoNCiMjIE90cm9zIGdyw6FmaWNvcw0KDQpMb3MgZ3LDoWZpY29zIGRlIGJhcnJhcywgbMOtbmVhIG8gZGUgdG9ydGEgc29uIGbDoWNpbG1lbnRlIHJlcHJvZHVjaWJsZXMgZW4gdW4gZXhjZWwsIGNvbiBpbmZvcm1hY2nDs24gYWdyZWdhZGEuIFNpbiBlbWJhcmdvLCBoYXkgY29zYXMgcXVlIG5vIHNvbiByZXBsaWNhYmxlcyBlbiBleGNlbDoNCg0KLSBHcsOhZmljb3MgcXVlIG5lY2VzaXRhbiBsYSBpbmZvcm1hY2nDs24gYSBuaXZlbCBkZSBtaWNyb2RhdG9zLiBfX3B1bnRvc19fLCAgX19ib3hwbG90c19fLCBfX0tlcm5lbHNfXywgZXRjLg0KLSBBYnJpciBlbCBtaXNtbyBncsOhZmljbyBzZWfDum4gYWxndW5hIHZhcmlhYmxlIGRpc2NyZXRhOiBgYGBmYWNldF93cmFwKClgYGANCi0gUGFyYW1ldHJpemFyIG90cmFzIHZhcmlhYmxlcywgcGFyYSBhdW1lbnRhciBsYSBkaW1lbnNpb25hbGlkYWQgZGVsIGdyw6FmaWNvcy4NCiAgICAtIFtfX2NvbG9yX19dKGh0dHA6Ly93d3cuc3RhdC5jb2x1bWJpYS5lZHUvfnR6aGVuZy9maWxlcy9SY29sb3IucGRmKSBgYGBjb2xvciA9IGBgYA0KICAgIC0gX19yZWxsZW5vX19gYGBmaWxsID0gYGBgDQogICAgLSBfX2Zvcm1hX18gYGBgc2hhcGUgPSBgYGANCiAgICAtIF9fdGFtYcOxb19fIGBgYHNpemUgPSBgYGANCiAgICAtIF9fdHJhbnNwYXJlbmNpYV9fIGBgYGFscGhhID0gYGBgDQoNCkVzdG8gcGVybWl0ZSB0ZW5lciwgZW4gZWwgcGxhbm8sIGdyw6FmaWNvcyBkZSBtdWNoYXMgZGltZW5zaW9uZXMgZGUgYW7DoWxpc2lzDQoNCkN1YW5kbyBxdWVyZW1vcyB1dGlsaXphciBlc3RvcyBwYXLDoW1ldHJvcyBwYXJhIHJlcHJlc2VudGFyIHVuYSB2YXJpYWJsZSwgbG9zIGRlZmluaW1vcyBfX2RlbnRybyBkZWwgYWVzKClfXywgYGBgYWVzKC4uLiBjb2xvciA9IGluZ3Jlc29zKWBgYCwgY3VhbmRvIHF1ZXJlbW9zIHNpbXBsZW1lbnRlIG1lam9yYXIgZWwgZGlzZcOxbywgc2UgYXNpZ25hbiBwb3IgZnVlcmEsIG8gZGVudHJvIGRlIGNhZGEgdGlwbyBkZSBncsOhZmljb3MsIGBgYGdlb21fY29sKGNvbG9yID0gJ2dyZWVuJylgYGAuDQoNCiMjIyBCb3hwbG90cw0KDQoNCkhhY2Vtb3MgdW4gcHJvY2VzYW1pZW50byBzaW1wbGU6IFNhY2Ftb3MgbG9zIGluZ3Jlc29zIGlndWFsZXMgYSBjZXJvIHkgbGFzIG5vIHJlc3B1ZXN0YXMgZGUgbml2ZWwgZWR1Y2F0aXZvLiAgICANCg0KTGFzIHZhcmlhYmxlcyBzZXhvKCBDSDA0ICkgeSBOaXZlbCBlZHVjYXRpdm8gZXN0w6FuIGNvZGlmaWNhZGFzIGNvbW8gbsO6bWVyb3MsIHkgZWwgUiBsYXMgZW50aWVuZGUgY29tbyBudW3DqXJpY2FzLg0KDQpgYGB7cn0NCmNsYXNzKEluZGl2aWR1YWxfdDExNyROSVZFTF9FRCkNCmNsYXNzKEluZGl2aWR1YWxfdDExNyRDSDA0KQ0KDQpgYGANCg0KRXMgaW1wb3J0YW50ZSBxdWUgbGFzIHZhcmlhYmxlcyBzZWFuIGRlbCB0aXBvIHF1ZSBjb25jZXB0dWFsbWVudGUgbGVzIGNvcnJlc3BvbmRlIChlbCBuaXZlbCBlZHVjYXRpdm8gZXMgdW5hIHZhcmlhYmxlIGNhdGVnw7NyaWNhLCBubyBjb250aW51YSksIHBhcmEgcXVlIGVsIGdncGxvdCBwdWVkYSBncmFmaWNhcmxvIGNvcnJlY3RhbWVudGUuIA0KDQpgYGB7cn0NCg0KZ2dkYXRhIDwtIEluZGl2aWR1YWxfdDExNyAlPiUgDQogIGZpbHRlcihQMjE+MCwgIWlzLm5hKE5JVkVMX0VEKSkgJT4lIA0KICBtdXRhdGUoTklWRUxfRUQgPSBhcy5mYWN0b3IoTklWRUxfRUQpLA0KICAgICAgICAgQ0gwNCAgICAgPSBhcy5mYWN0b3IoQ0gwNCkpDQpgYGANCg0KDQpzaSBxdWVyZW1vcyBoYWNlciB1biBib3hwbG90IGRlbCBpbmdyZXNvIHBhcmEgY2FkYSBuaXZlbCBlZHVjYXRpdm8sIGFzaWduYW1vcyBlc3RhIHZhcmlhYmxlIGEgX3gsIGdyb3VwIHkgZmlsbF8NCmBgYHtyfQ0KDQpnZ3Bsb3QoZ2dkYXRhLCBhZXMoeCA9IE5JVkVMX0VELCB5ID0gUDIxLCBncm91cCA9IE5JVkVMX0VELCBmaWxsID0gTklWRUxfRUQgKSkgKw0KICBnZW9tX2JveHBsb3QoKSsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoMCwgNDAwMDApKQ0KYGBgDQoNClNpIHF1ZXJlbW9zIGFncmVnYXIgbGEgZGltZW5zacOzbiBfc2V4b18sIHBvZGVtb3MgaGFjZXIgdW4gYGBgZmFjZXRfd3JhcCgpYGBgDQoNCmBgYHtyfQ0KDQpnZ3Bsb3QoZ2dkYXRhLCBhZXMoeD0gTklWRUxfRUQsIHkgPSBQMjEsIGdyb3VwID0gTklWRUxfRUQsIGZpbGwgPSBOSVZFTF9FRCApKSArDQogIGdlb21fYm94cGxvdCgpKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYygwLCA0MDAwMCkpKw0KICBmYWNldF93cmFwKH4gQ0gwNCwgbGFiZWxsZXIgPSAibGFiZWxfYm90aCIpDQpgYGANCg0KRW4gZXN0ZSBncsOhZmljbywgZWwgZm9jbyBkZSBhdGVuY2nDs24gc2lndWUgcHVlc3RvIGVuIGxhcyBkaWZlcmVuY2lhcyBkZSBuaXZlbCBlZHVjYXRpdm8sIHBlcm8gX25ldXRyYWxpemFtb3NfIGVsIGVmZWN0byBkZSBsYSB2YXJpYWJsZSBzZXhvLiAgICAgDQoNClNpIGxvIHF1ZSBxdWVyZW1vcyBoYWNlciBlcyBwb25lciBlbCBmb2NvIGRlIGF0ZW5jacOzbiBlbiBsYXMgZGlmZXJlbmNpYXMgcG9yIHNleG8sIF9uZXV0cmFsaXphbW9zXyBlbCBlZmVjdG8gZGVsIG5pdmVsIGVkdWNhdGl2bywgZmFjZXRpYW5kbyBwb3Igbml2ZWwgZWR1Y2F0aXZvLg0KDQoNCmBgYHtyfQ0KZ2dwbG90KGdnZGF0YSwgYWVzKHg9IENIMDQsIHkgPSBQMjEsIGdyb3VwID0gQ0gwNCwgZmlsbCA9IENIMDQgKSkgKw0KICBnZW9tX2JveHBsb3QoKSsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoMCwgNDAwMDApKSsNCiAgZmFjZXRfd3JhcCh+IE5JVkVMX0VELCBsYWJlbGxlciA9ICJsYWJlbF9ib3RoIikNCg0KYGBgDQoNCg0KIyMjIEtlcm5lbHMNCg0KUG9kZW1vcyBoYWNlciB1bmEgbnVldmEgdmVyc2nDs24gZGVsIGdyw6FmaWNvIDIuIFV0aWxpemFuZG8gdW4gcHJvY2VzYW1pZW50byBzaW1pbGFyIGFsIHF1ZSBoaWNpbW9zIGFudGVzLg0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFfQ0KDQpkYXRhZ3JhZiA8LUluZGl2aWR1YWxfdDExNyAlPiUgDQogIHNlbGVjdChSRUdJT04sUDQ3VCxUX1ZJLCBUT1RfUDEyLCBQMjEgLCBQT05ESUksIENIMDQpICU+JSANCiAgZmlsdGVyKCFpcy5uYShQNDdUKSwgUDQ3VCA+IDAgKSAlPiUgDQogIG11dGF0ZShSRUdJT04gICAgICAgICAgICAgPSBjYXNlX3doZW4oUkVHSU9OID09IDEgICAgfiAnR0JBJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBSRUdJT04gPT0gNDAgICB+ICdOT0EnLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFJFR0lPTiA9PSA0MSAgIH4gJ05FQScsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUkVHSU9OID09IDQyICAgfiAnQ3V5bycsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUkVHSU9OID09IDQzICAgfiAnUGVhbXBlYW5hJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBSRUdJT04gPT0gNDQgICB+ICdQYXRhZ29uaWEnLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEZBTFNFICAgICAgICAgIH4gJ290cm8nKSwNCiAgICAgICAgIGluZ3Jlc29fbGFib3JhbCAgICA9IGFzLm51bWVyaWMoVE9UX1AxMiArIFAyMSksDQogICAgICAgICBpbmdyZXNvX25vX2xhYm9yYWwgPSBhcy5udW1lcmljKFRfVkkpLA0KICAgICAgICAgQ0gwNCAgICAgICAgICAgICAgID0gY2FzZV93aGVuKENIMDQgPT0gMSB+ICJWYXJvbiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQ0gwNCA9PSAyIH4gIk11amVyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBGQUxTRSAgICAgfiAiT3RybyIpICkgJT4lIA0KICBnYXRoZXIoLiwga2V5ID0gVGlwb19pbmdyZXNvLCBJbmdyZXNvLCBjKChuY29sKC4pLTEpOm5jb2woLikpKQ0KZGF0YWdyYWYgIA0KYGBgDQoNCkNvbiBsb3MgS2VybmVscywgbm8gbmVjZXNpdGFtb3MgZGl2aWRpciBhIGxhIHBvYmxhY2nDs24gZW4gZGVjaWxlcywgcG9ycXVlIHBvZGVtb3MgdGVuZXIgdW5hIG1pcmFkYSBjb21wbGV0YSBkZSBsYSBmb3JtYSBkZSBsYSBkaXN0cmlidWNpw7NuLiAgICANCg0KUGFyYSBlc3RlIGdyw6FmaWNvLCBxdWllcm8gZWxpbWluYXIgbG9zIGluZ3Jlc29zID0gMA0KDQpgYGB7cn0NCmRhdGFncmFmMiA8LSBkYXRhZ3JhZiAlPiUgZmlsdGVyKCBJbmdyZXNvICE9MCkNCiAgDQogIA0KZ2dwbG90KGRhdGFncmFmMiwgYWVzKA0KICB4ID0gSW5ncmVzbywNCiAgd2VpZ2h0cyA9IFBPTkRJSSwNCiAgZ3JvdXAgPSBUaXBvX2luZ3Jlc28sDQogIGZpbGwgPSBUaXBvX2luZ3Jlc28pKSArDQogIGdlb21fZGVuc2l0eShhbHBoYT0wLjcsYWRqdXN0ID0yKSsNCiAgbGFicyh4PSJEaXN0cmlidWNpw7NuIGRlbCBpbmdyZXNvIiwgeT0iIiwNCiAgICAgICB0aXRsZT0iIFRvdGFsIHNlZ8O6biB0aXBvIGRlIGluZ3Jlc28geSBnw6luZXJvIiwgDQogICAgICAgY2FwdGlvbiA9ICJGdWVudGU6IEVuY3Vlc3RhIFBlcm1hbmVudGUgZGUgSG9nYXJlcyIpKw0KICBzY2FsZV94X2NvbnRpbnVvdXMobGltaXRzID0gYygwLDUwMDAwKSkrDQogIHRoZW1lX3R1ZnRlKCkrDQogIHNjYWxlX2ZpbGxfZ2RvY3MoKSsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsDQogICAgICAgIHBsb3QudGl0bGUgICAgICA9IGVsZW1lbnRfdGV4dChzaXplPTEyKSkrDQogIGZhY2V0X3dyYXAofiBDSDA0LCBzY2FsZXMgPSAiZnJlZSIpDQoNCmdnc2F2ZShmaWxlbmFtZSA9IHBhc3RlMChyZXN1bHRhZG9zLmRpciwgIktlcm5lbF8xLnBuZyIpLHNjYWxlID0gMikNCg0KYGBgDQoNCkVuIGVzdGUgdGlwbyBkZSBncsOhZmljb3MsIGltcG9ydGEgbXVjaG8gcXXDqSB2YXJpYWJsZSBzZSB1dGlsaXphIHBhcmEgX2ZhY2V0ZWFyXyB5IHF1w6kgdmFyaWFibGUgcGFyYSBhZ3J1cGFyLCB5YSBxdWUgbGEgY29uc3RydWNjacOzbiBkZSBsYSBkaXN0cmlidWNpw7NuIGVzIGRpZmVyZW50ZS4gDQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGFncmFmMiwgYWVzKA0KICB4ID0gSW5ncmVzbywNCiAgd2VpZ2h0cyA9IFBPTkRJSSwNCiAgZ3JvdXAgPSBDSDA0LA0KICBmaWxsID0gQ0gwNCkpICsNCiAgZ2VvbV9kZW5zaXR5KGFscGhhPTAuNyxhZGp1c3QgPTIpKw0KICBsYWJzKHg9IkRpc3RyaWJ1Y2nDs24gZGVsIGluZ3Jlc28iLCB5PSIiLA0KICAgICAgIHRpdGxlPSIgVG90YWwgc2Vnw7puIHRpcG8gZGUgaW5ncmVzbyB5IGfDqW5lcm8iLCANCiAgICAgICBjYXB0aW9uID0gIkZ1ZW50ZTogRW5jdWVzdGEgUGVybWFuZW50ZSBkZSBIb2dhcmVzIikrDQogIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKDAsNTAwMDApKSsNCiAgdGhlbWVfdHVmdGUoKSsNCiAgc2NhbGVfZmlsbF9nZG9jcygpKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwNCiAgICAgICAgcGxvdC50aXRsZSAgICAgID0gZWxlbWVudF90ZXh0KHNpemU9MTIpKSsNCiAgZmFjZXRfd3JhcCh+VGlwb19pbmdyZXNvLCBzY2FsZXMgPSAiZnJlZSIpDQoNCmdnc2F2ZShmaWxlbmFtZSA9IHBhc3RlMChyZXN1bHRhZG9zLmRpciwgIktlcm5lbF8xLnBuZyIpLHNjYWxlID0gMikNCg0KYGBgDQoNCg0KDQoNCkVsIGVqZSB5IG5vIHRpZW5lIGRlbWFzaWFkYSBpbnRlcnByZXRhYmlsaWRhZCBlbiBsb3MgS2VybmVsLCBwb3JxdWUgaGFjZSBhIGxhIGZvcm1hIGVuIHF1ZSBzZSBjb25zdHJ1eWVuIGxhcyBkaXN0cmlidWNpb25lcy4gDQoNCg0KX19FeHRlbnNpw7NuIGRlIGdncGxvdF9fOiBsaWJyZXLDrWEgOiBfZ2dqb3lfDQoNCkVzdGEgZXh0ZW5zacOzbiBhcHJvdmVjaGEgcXVlIGVsIGVqZSB5IG5vIGVzIGltcG9ydGFudGUsIHkgbG8gdXRpbGl6YSBwYXJhIGFwaWxhciBsYXMgZGlzdHJpYnVjaW9uZXMgcXVlIHNlIGVzdMOhbiBjb21wYXJhbmRvIGRlIGZvcm1hIG3DoXMgcHJvbGlqYS4gDQoNClNpIHBvciBlamVtcGxvIHF1ZXJlbW9zIGNvbXBhcmFyIGxhIGRpc3RyaWJ1Y2nDs24gZGVsIGluZ3Jlc28gbGFib3JhbCBzZWfDum4gcmVnacOzbjoNCiANCmBgYHtyfQ0KbGlicmFyeShnZ2pveSkgIA0KDQoNCmRhdGFncmFmMyA8LSBkYXRhZ3JhZjIgJT4lIGZpbHRlcihUaXBvX2luZ3Jlc28gPT0gImluZ3Jlc29fbGFib3JhbCIpDQoNCmdncGxvdChkYXRhZ3JhZjMsIGFlcygNCiAgICAgICAgICAgeCA9IEluZ3Jlc28sDQogICAgICAgICAgIHkgPSBSRUdJT04sDQogICAgICAgICAgIHdlaWdodHMgPSBQT05ESUksDQogICAgICAgICAgIGdyb3VwID0gUkVHSU9OLA0KICAgICAgICAgICBmaWxsID0gUkVHSU9ODQogICAgICAgICAgICkpICsNCiAgZ2VvbV9qb3koYWxwaGE9MC42LCBiYW5kd2lkdGggPSAxNTAwKSsNCiAgbGFicyh4PSIiLCB5PSIiLA0KICAgICAgIHRpdGxlPSJEaXN0cmlidWNpw7NuIGRlbCBpbmdyZXNvIGxhYm9yYWwgc2Vnw7puIHJlZ2nDs24iLCANCiAgICAgICBjYXB0aW9uID0gIkZ1ZW50ZTogRW5jdWVzdGEgUGVybWFuZW50ZSBkZSBIb2dhcmVzIikrDQogIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKDAsMzUwMDApKSsNCiAgdGhlbWVfdHVmdGUoKSsNCiAgc2NhbGVfZmlsbF9nZG9jcygpKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsDQogICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgcGxvdC50aXRsZSAgID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMikpDQoNCmdnc2F2ZShmaWxlbmFtZSA9IHBhc3RlMChyZXN1bHRhZG9zLmRpciwgIktlcm5lbF8yLnBuZyIpLHNjYWxlID0gMikNCg0KYGBgDQoNCiMjIyBUZW5kZW5jaWENCg0KDQpQYXJhIHJlYWxpemFyIGVzdG9zIGdyw6FmaWNvcywgdmFtb3MgYSBtb2RpZmljYXIgdW4gcG9jbyBsb3MgZGF0b3M6DQoNCi0gZmlsdHJhbW9zIGxvcyBpbmdyZXNvcyBpZ3VhbGVzIGEgMC4NCi0gZWxpbWluYW1vcyBsYXMgbm8gcmVzcHVlc3RhcyBkZSBuaXZlbCBlZHVjYXRpdm8geSBsYXMgcGVyc29uYXMgY29uIGVkdWNhY2nDs24gZXNwZWNpYWwuDQotIGVsaW1pbmFtb3MgbGFzIHJlc3B1ZXN0YXMgZGUgdGlwbyBkZSBlc3RhYmxlY2ltaWVudG8gPSAnb3Ryb3MnLg0KLSByZWNvZGlmaWNhbW9zIGxhcyB2YXJpYWJsZXMgcGFyYSBxdWUgdGVuZ2FuIG5vbWJyZXMgbcOhcyBzdWdlc3Rpdm9zOg0KICAgIC0gX19OaXZlbCBlZHVjYXRpdm9fXyBhZGVtw6FzIGxhIGNvbnZlcnRpbW9zIGEgZmFjdG9yLCBwb3JxdWUgcXVlcmVtb3MgZXhwbGljaXRhcmxlIGVsIG9yZGVuIGRlIGxvcyB2YWxvcmVzIGNvbiBgYGBsZXZlbHMoKWBgYC4gRWwgIlxcbiIiIGVzIHVuIF9jYXJhY3RlciBlc3BlY2lhbF8gcXVlIHBlcm1pdGUgcXVlIGVsIHN0cmluZyBjb250aW7DumUgZW4gbGEgc2lndWllbnRlIGzDrW5lYS4NCiAgICAtIFNleG8uDQogICAgLSBUaXBvIGRlIGVzdGFibGVjaW1pZW50by4NCiAgICANCiAgICANCmBgYHtyIH0NCmdnZGF0YSA8LSBJbmRpdmlkdWFsX3QxMTcgJT4lIA0KICBmaWx0ZXIoUDIxPjAsDQogICAgICAgICAhaXMubmEoTklWRUxfRUQpLA0KICAgICAgICAgTklWRUxfRUQhPTcsIA0KICAgICAgICAgUFAwNEEgIT0zKSAlPiUgDQogIG11dGF0ZShOSVZFTF9FRCA9IGZhY3RvcihjYXNlX3doZW4oTklWRUxfRUQgPT0gMSAgfiAnUHJpbWFyaWEgXG4gSW5jb21wbGV0YScsICMgJ1xuJyBzaWduaWZpY2EgY2FycmlhZ2UgcmV0dXJuLCBvIGVudGVyDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTklWRUxfRUQgPT0gMiAgfiAnUHJpbWFyaWEgXG4gQ29tcGxldGEnLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE5JVkVMX0VEID09IDMgIH4gJ1NlY3VuZGFyaWEgXG5JbmNvbXBsZXRhJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBOSVZFTF9FRCA9PSA0ICB+ICdTZWN1bmRhcmlhIFxuQ29tcGxldGEnLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE5JVkVMX0VEID09IDUgIH4gJ1N1cGVyaW9yIFxuVW5pdmVyc2l0YXJpYSBcbkluY29tcGxldGEnLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE5JVkVMX0VEID09IDYgIH4gJ1N1cGVyaW9yIFxuVW5pdmVyc2l0YXJpYSBcbkNvbXBsZXRhJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBGQUxTRSAgICAgICAgICB+ICdPdHJvJyksDQogICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHM9IGMoJ1ByaW1hcmlhIFxuIEluY29tcGxldGEnLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdQcmltYXJpYSBcbiBDb21wbGV0YScsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1NlY3VuZGFyaWEgXG5JbmNvbXBsZXRhJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnU2VjdW5kYXJpYSBcbkNvbXBsZXRhJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnU3VwZXJpb3IgXG5Vbml2ZXJzaXRhcmlhIFxuSW5jb21wbGV0YScsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1N1cGVyaW9yIFxuVW5pdmVyc2l0YXJpYSBcbkNvbXBsZXRhJykpLA0KICAgICAgICAgU2V4byAgICAgPSBjYXNlX3doZW4oQ0gwNCA9PSAxIH4gJ1ZhcsOzbicsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBDSDA0ID09IDIgfiAnTXVqZXInKSwNCiAgICAgICAgIEVzdGFibGVjaW1pZW50byAgICA9IGNhc2Vfd2hlbihQUDA0QSA9PSAxIH4gJ0VzdGF0YWwnLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFBQMDRBID09IDIgfiAnUHJpdmFkbycsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRkFMU0UgICAgICB+ICdPdHJvJykpDQoNCmdnZGF0YQ0KYGBgDQoNClBhcmEgZ3JhZmljYXIgdW4gc3Vhdml6YWRvIGRlIGxhcyBzZXJpZXMsIHNlIHV0aWxpemEgbGEgZnVuY2nDs24gW2BgYGdlb21fc21vb3RoKClgYGBdKGh0dHA6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL2dlb21fc21vb3RoLmh0bWwpLiBDb24gc3Vhdml6YWRvIG5vcyByZWZlcmltb3MgYWwgZ3LDoWZpY28gZGUgdW4gbW9kZWxvIHJlYWxpemFkbyBzb2JyZSBsb3MgZGF0b3MsIHF1ZSBlc3RpbWEgZWwgdmFsb3IgZW4gZWwgcHVudG8geCx5IChwYXJhIGVsIGdydXBvKS4gTGFzIHJlZ3Jlc2lvbmVzIGxpbmVhbGVzIHNvbiB1biBlamVtcGxvIGRlIGVzdG8sIGF1bnF1ZSBubyBlbCDDum5pY28sIG5pIGVsIHF1ZSB2aWVuZSBwb3IgZGVmYXVsdC4NCg0KYGBge3IgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9OH0NCmdncGxvdChnZ2RhdGEsIGFlcyhDSDA2LCBQMjEsIGNvbG91ciA9IFNleG8sIHNoYXBlID0gU2V4bywgYWxwaGEgPSBQMjEpKSsNCiAgZ2VvbV9zbW9vdGgoKSArIA0KICBsYWJzKA0KICAgIHggPSAnRWRhZCcsDQogICAgeSA9ICdpbmdyZXNvJywNCiAgICB0aXRsZSA9ICdJbmdyZXNvIHBvciBvY3VwYWNpw7NuIHByaW5jaXBhbCcsDQogICAgc3VidGl0bGUgPSAnU2Vnw7puIGVkYWQsIG5pdmVsIGVkdWNhdGl2byB5IHNleG8nKSArDQogIHRoZW1lX21pbmltYWwoKSsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSsNCiAgc2NhbGVfYWxwaGEoZ3VpZGUgPSBGQUxTRSkrDQogIGZhY2V0X2dyaWQoLn5OSVZFTF9FRCkNCmBgYA0KDQoNClNpIGNvcnJlbW9zIGVsIGNvbWFuZG8gYGBgZ2VvbV9zbW9vdGgoKWBgYCAgcG9yIGRlZmF1bHQsIG5vcyBhZHZpZXJ0ZSBxdWUgZXN0YSB1dGlsaXphbmRvIGVsIG3DqXRvZG8gR0FNLCBkZSBbZ2VuZXJhbCBhZGRpdGl2ZSBtb2RlbHNdKGh0dHBzOi8vbS1jbGFyay5naXRodWIuaW8vZG9jcy9HQU0uaHRtbCkuICAgICAgDQoNCmVsIF9fc29tYnJlYWRvIGdyaXNfXyBxdWUgZW52dWVsdmUgY2FkYSBsw61uZWEgZXMgZWwgaW50ZXJ2YWxvIGRlIGNvbmZpYW56YSBkZSBkaWNobyBwdW50byAoOTUlIHBvciBkZWZhdWx0KS4NCg0KVGFtYmnDqW4gcG9kZW1vcyB1dGlsaXphciBtw6l0b2RvcyBsaW5lYWxlcywgYWdyZWdhbmRvIGVsIHBhcsOhbWV0cm8gYGBgbWV0aG9kID0gJ2xtJ2BgYC4gSGFjaWVuZG8gZXN0bywgZWwgZ3LDoWZpY28gbXVlc3RyYSB1bmEgcmVncmVzacOzbiBsaW5lYWwgc2ltcGxlLiBTaSBxdWVyZW1vcyBvdHJvIHRpcG8gZGUgcmVncmVzacOzbiBsaW5lYWwsIGxlIHBvZGVtb3MgZXhwbGljaXRhciBsYSBmw7NybXVsYS4gICAgDQpFbiBlbCBlamVtcGxvIHNpZ3VpZW50ZSwgdXRpbGl6YW1vcyBsYSBmb3JtdWxhICR5ID0gXGJldGFfMCArXGJldGFfMXggK1xiZXRhXzIgeF4yICQuDQoNCmBgYHtyIGZpZy5oZWlnaHQ9NSwgZmlnLndpZHRoPTh9DQoNCmdncGxvdChnZ2RhdGEsIGFlcyhDSDA2LCBQMjEsIGNvbG91ciA9IFNleG8sIHdlaWdodCA9IFBPTkRJSU8pKSArDQogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIGZvcm11bGEgPSB5IH4gcG9seSh4LCAyKSkgKw0KICBsYWJzKHggPSAnRWRhZCcsDQogICAgICAgeSA9ICdpbmdyZXNvJywNCiAgICAgICB0aXRsZSA9ICdSZWdyZXNpb24gY3VhZHLDoXRpY2EgZGVsIEluZ3Jlc28gcG9yIG9jdXBhY2nDs24gcHJpbmNpcGFsIHJlc3BlY3RvIGRlIGxhIEVkYWQnLA0KICAgICAgIHN1YnRpdGxlID0gJ1NlZ8O6biBOaXZlbCBlZHVjYXRpdm8geSBzZXhvJykgKw0KICB0aGVtZV9taW5pbWFsKCkrDQogIGZhY2V0X2dyaWQoLiB+IE5JVkVMX0VEKQ0KYGBgDQoNCg0KU2kgcXVpc2nDqXJhbW9zLCBhZGVtw6FzIGRlIHZlciBsYSByZWxhY2nDs24gZW50cmUgaW5ncmVzbywgRWRhZCwgU2V4byB5IE5pdmVsIGVkdWNhdGl2bywgaW5jb3Jwb3JhciBlbCB0aXBvIGRlIGVzdGFibGVjaW1pZW50byxww7pibGljbyBvIHByaXZhZG8uIFBvZGVtb3MgZmFjZXRlYXIgZWwgZ3LDoWZpY28gcG9yIGRvcyB2YXJpYWJsZXMgZW4gbHVnYXIgZGUgdW5hLCBsbyBxdWUgY3JlYSB1bmEgX19tYXRyaXogZGUgZ3LDoWZpY29zX18gc2Vnw7puIGxvcyBjcnVjZXMuDQoNCmBgYHtyIGZpZy5oZWlnaHQ9NSwgZmlnLndpZHRoPTh9DQpnZ3Bsb3QoZ2dkYXRhLCBhZXMoQ0gwNiwgUDIxLCBjb2xvdXIgPSBFc3RhYmxlY2ltaWVudG8sIHdlaWdodCA9IFBPTkRJSU8pKSArDQogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIpICsNCiAgbGFicygNCiAgeCA9ICdFZGFkJywNCiAgeSA9ICdpbmdyZXNvJywNCiAgdGl0bGUgPSAnVGVuZGVuY2lhIGRlbCBpbmdyZXNvIHBvciBvY3VwYWNpw7NuIHByaW5jaXBhbCcsDQogIHN1YnRpdGxlID0gJ1NlZ8O6biBlZGFkLCBuaXZlbCBlZHVjYXRpdm8sIHNleG8geSB0aXBvIGRlIGVzdGFibGVjaW1pZW50bycpICsNCiAgdGhlbWVfbWluaW1hbCgpKw0KICBmYWNldF9ncmlkKFNleG8gfiBOSVZFTF9FRCkNCg0KZ2dzYXZlKGZpbGVuYW1lID0gcGFzdGUwKHJlc3VsdGFkb3MuZGlyLCAicmVncmVzaW9uIGxpbmVhbC5wbmciKSxzY2FsZSA9IDIpDQoNCmBgYA0KDQoNCiMgRWplcmNpY2lvcyBwYXJhIHByYWN0aWNhcg0KDQotIEdyYWZpY2FyIGxhIGRpc3RyaWJ1Y2nDs24gZGVsIGluZ3Jlc28gcG9yIG9jdXBhY2nDs24gcHJpbmNpcGFsIHNlZ8O6biBjYXRlZ29yw61hIG9jdXBhY2lvbmFsLCBjb24gZWwgdGlwbyBkZSBncsOhZmljbyBLZXJuZWwuDQotIEluY29ycG9yYXIgZW4gZWwgZ3LDoWZpY28gYW50ZXJpb3IgbGEgY29uZGljacOzbiBkZSBwcmVjYXJpZWRhZCBsYWJvcmFsIChQUDA3SCkuDQotIFF1ZWRhcnNlIHPDs2xvIGNvbiBsb3MgYXNhbGFyaWFkb3MsIHkgZ3JhZmljYXIgbGEgcmVsYWNpw7NuIGVudHJlIGluZ3Jlc28gcG9yIG9jdXBhY2nDs24gcHJpbmNpcGFsLCBwcmVjYXJpZWRhZCBsYWJvcmFsIHkgdGFtYcOxbyBkZWwgZXN0YWJsZWNpbWllbnRvLg0KLSBIYWNlciBib3hwbG90cyBkZWwgaW5ncmVzbyBwb3Igb2N1YXBjacOzbiBwcmluY2lwYWwsIHNlZ8O6biBzZXhvIHkgY29uZGljacOzbiBkZSBwcmVjYXJpZWRhZC4NCi0gSW5jbHVpciBlbiBlbCBncsOhZmljbyBhbnRlcmlybyBsYSBkaW1lbnNpw7NuIGRlIHRhbWHDsW8gZGVsIGVzdGFibGVjaW1pZW50by4NCg0KDQoNCg==