Las notas del presente curso fueron elaboradas originalmente por Diego Kozlowski y Guido Weksler. En las sucesivas modificaciones colaboraron: Natsumi Shokida y Matías Lioni

Reiniciar Sesión

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
iris
plot(iris)

#Al especificar una variable, puedo ver el valor que toma cada uno de sus registros (Index)
plot(iris$Sepal.Length,type = "p") # Un punto por cada valor

plot(iris$Sepal.Length,type = "l") # Una linea que una cada valor

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

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

png

La función png() nos permite grabar una imagen en el disco. Lleva como argumento principal la ruta completa a donde se desea guardar la misma, incluyendo el nombre que queremos dar al archivo. A su vez pueden especificarse otros argumetnos como el ancho y largo de la imagen, entre otros.

ruta_archivo <- "../Resultados/grafico1.PNG"
ruta_archivo
[1] "../Resultados/grafico1.PNG"
png(ruta_archivo)
plot(iris$Sepal.Length,type = "b")
dev.off()
null device 
          1 

La función png() abre el dispositivo de imagen en el directorio especificado. Luego creamos el gráfico que deseamos (o llamamos a uno previamente construido), el cual se desplegará en la ventana inferior derecha de la pantalla de Rstudio. Finalmente 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 la fuente de datos y las variables que determinaran los ejes del grafico (x,y), así como el color y la forma de las líneas o puntos,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.

Gráfico de Puntos

A continuación se desplega un gráfico de varias capas de construcción, con su correspondiente porción de código. En el mismo se buscará visualizar, a partir de la base de datos iris la relación entre el ancho y el largo de los petalos, mediante un gráfico de puntos.

library(ggplot2) # cargamos la librería
ggplot(data = iris, aes(x = Petal.Length, Petal.Width, color = Species))+
  geom_point(alpha=0.75)+
  labs(title = "Medidas de los pétalos por especie")+
  theme(legend.position = 'none')+
  facet_wrap(~Species)

Capas del Gráfico

Veamos ahora, el “paso a paso” del armado del mismo.

En primera instancia solo defino los ejes. Y en este caso un color particular para cada Especie.

g <- ggplot(data = iris, aes(x = Petal.Length, Petal.Width, color = Species))
g

Luego, defino el tipo de gráfico. El alpha me permite definir la intensidad de los puntos

g <- g +  geom_point(alpha=0.25)
g

Las siguientes tres capas me permiten respectivamente:

  • Definir el título del gráfico
  • Quitar la leyenda
  • Abrir el gráfico en tres fragmentos, uno para cada especie
g <- g +
  labs(title = "Medidas de los pétalos por especie")+
  theme(legend.position = 'none')+
  facet_wrap(~Species)
g

Graficos Ingresos - EPH

A continuación utilzaremos los conceptos abordados, para realizar gráficos a partir de las variables de ingresos.

Una de las ventajas del R respecto a otros softwares estadísticos, es la facilidad con la que podemos trabajar en simultaneo con multiples bases de datos. Aprovechando esta potencialidad, levantaremos a continuación 4 bases individuales de la EPH para realizar gráficos de series temporales.

#Cargamos las librerías a utilizar
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

Para mayor practicidad, al momento de levantar las bases de datos, seleccionamos las variables necesarias (A excepción de la base del t1_2017 con la que trabajaremos numerosas variables)

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

Ingreso de la ocupación principal

  • Nuestro primer ejercicio consistirá en analizar la evolución del ingreso por la ocupación principal. Observando el diseño de registro podremos ver que dicha variable está codificada como P21.

  • Para minimizar la volatilidad que la no respuesta de ingresos podría generar en los resultados de unos y otros trimestres, la EPH asigna a los no respondentes el comportamiento de los respondentes por estrato de la muestra. A partir de esto las variables de ingresos presentan distintos factores de expansión.

  • En el caso del ingreso de la ocupación principal debemos trabajar con el expansor PONDIIO.

#Unimos las bases y creamos una variable que concatena el año y el trimestre
Union_Bases <- bind_rows(Individual_t216, 
                  Individual_t316,
                  Individual_t416,
                  Individual_t117) %>% 
  mutate(periodo = paste(ANO4, TRIMESTRE, sep = "_")) 
  • Calculamos el ingreso per capita promedio, utilizando el ponderador correspondiente.
  • Importante: Debemos exigir ingresos positivos, ya que tenemos numerosos casos con ingreso 0 y otros codificados con -9 (cuando no corresponde la pregunta por la ocupación principal)
IOppal <-Union_Bases   %>% 
  filter(P21>0) %>% 
  group_by(periodo) %>% 
  summarise(IOppal_prom = weighted.mean(P21, PONDIIO)) 
IOppal

Ahora podemos utilizar este nuevo dataframe para graficar

ggplot(data = IOppal, aes(x = periodo, y = IOppal_prom)) + 
  geom_point()

Agregando algunos parámetros más …

  1. Definimos las variables del gráfico
g <- IOppal %>% #Podemos usar los "pipes" para llamar al Dataframe que continen la info
ggplot(aes(x = periodo,
           y = IOppal_prom,
           #Agrupar nos permitirá generar las lineas del gráfico
           group = 'IOppal_prom',
           #Agregamos una etiqueta a los datos (Redondeando la variable a 2 posiciones decimales)
           label= round(IOppal_prom,2)))
  1. Agregamos titulo y modificamos ejes
g <- g +
  labs(x = "Trimestre",
       y = "IPCF promedio",
       title = "Ingreso promedio por la ocupación principal",
       subtitle = "Serie 2trim_2016 - 1trim_2017",
       caption = "Fuente: EPH")
  1. Agregamos puntos y lineas
g <- g +
  geom_point(size= 3)+ #puedo definir tamaño de las lineas
  geom_line( size= 1 )
  1. Agrego etiquetas con el texto. Las corro hacia arriba (nudge_y) y a la izquierda(nudge_x)
  2. Agrego un tema
g <- g +
  geom_text_repel(nudge_y = 500, nudge_x = 0.25)+
  theme_minimal()
  1. muestro el gráfico (guardado en g)
g
ggsave(filename = "../Resultados/IPCF_prom.png") #Guardo el Grafico
Saving 6.78 x 4.19 in image

Ingreso per capita familiar

La variable IPCF representa el ingreso per capita del hogar, del individuo encuestado. Su formula es la siguiente: \[\frac{\sum_{i=1}^n ING_i}{n}\]

Donde:

  • ING_i es el ingreso del miembro i del hogar en cuestión y
  • n es el total de miembros del hogar

Para trabajar con ingresos de los Hogares debemos utilizar el ponderador PONDIH. A continuación realizamos la unión de las cuatro bases a trabajar, para luego calculara para cada período el Ingreso per capita familiar promedio.

Ing_prom_hogar <- 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(IPCF_prom = weighted.mean(IPCF, PONDIH)) %>% 
  ungroup()
Ing_prom_hogar
Ing_prom_hogar %>% 
ggplot(aes(x = periodo,
           y = IPCF_prom,
           group = 'IPCF_prom',
           label= round(IPCF_prom,2))) +
  labs(x = "Trimestre",
       y = "IPCF promedio",
       title = "Ingreso per capita familiar promedio", 
       subtitle = "Serie 2trim_2016 - 1trim_2017", 
       caption = "Fuente: EPH")+ 
  geom_line( size= 1 )+
  geom_point(size= 3)+ 
  geom_label_repel(nudge_y = 250,fill="gray80")+ #Aplico otro tipo de etiqueta,
  theme_light()+ #Elijo otro tema para el grafico
  theme(legend.position = "none")
ggsave(filename = "../Resultados/IPCF_prom.png") #Guardo el Grafico
Saving 6.78 x 4.19 in image

Distribución de los ingresos laborales y no laborales por sexo

  • Ingresos no laborales : TVI
  • Ingreso laborales:
    • P21 MONTO DE INGRESO DE LA OCUPACIÓN PRINCIPAL
    • Totp12 MONTO DE INGRESO DE OTRAS OCUPACIONES.
  datagraf_2 <-Individual_t117 %>% 
  #eligo las variables que necesito
    select(P47T,T_VI, TOT_P12, P21 , PONDII, CH04,NIVEL_ED) %>% 
  # Me quedo con los que tienen ingreso total individual (P47) positivo
    filter(!is.na(P47T), P47T > 0 ) %>% 
    mutate(ingreso_laboral    = TOT_P12 + P21,
           ingreso_no_laboral = T_VI,
           ingreso_total      = ingreso_laboral + ingreso_no_laboral,
           CH04               = case_when(CH04 == 1 ~ "Varon",
                                          CH04 == 2 ~ "Mujer")) %>% 
  group_by(CH04) %>% 
  summarise('ingreso laboral'    = sum(ingreso_laboral*PONDII)/sum(ingreso_total*PONDII),
            'ingreso no laboral' = sum(ingreso_no_laboral*PONDII)/sum(ingreso_total*PONDII)) 
datagraf_2  

Doy vuelta la tabla para poder graficar

datagrafico <- datagraf_2 %>%
gather(tipo_ingreso, monto,2:3 ) 
datagrafico
ggplot(datagrafico, 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=5)+
  labs(x="",y="Porcentaje")+
  theme_tufte()+
  scale_y_continuous()+
  theme(legend.position = "bottom",
        legend.title=element_blank(),
        axis.text.x = element_text(angle=25))

  
ggsave(filename = "../Resultados/ingresos laborales y no laborales.png",scale = 2)
Saving 16 x 16 in image

Podemos agregar también grupos de edad como otra dimensión del análisis. Optamos por restringuir la población de análisis a la comprendida entre 18 y 60 años

##Agrego al procedimiento anterior una clasificación de las edades
  datagraf_3 <-Individual_t117 %>% 
    select(P47T,T_VI, TOT_P12, P21 , PONDII, CH04,CH06) %>% 
    filter(!is.na(P47T), P47T > 0 , CH06 %in% c(18:60)) %>% 
    mutate(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"),
           EDAD = case_when(CH06 %in% c(18:30) ~ "18 a 30",      #<<
                            CH06 %in% c(31:45) ~"31 a 45",       #<<
                            CH06 %in% c(46:60) ~ "46 a 60")) %>% #<<
  group_by(CH04,EDAD) %>%                                        #<<
  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_3
ggplot(datagraf_3, 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_y_continuous()+
  theme(legend.position = "bottom",
        legend.title=element_blank(),
        axis.text.x = element_text(angle=25))+
  facet_wrap(~EDAD)

En los gráficos utilizamos extensiones de ggplot:

  • ggrepel geom_text_repel()
  • ggthemes theme_tufte()

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

Otros gráficos

Los gráficos hasta aquí realizados (barras, línea) son fácilmente reproducibles en un excel ya que utilizan la información agregada. Sin embargo, la gran ventaja del R se manifiesta a a la hora de realizar:

  • Gráficos que necesitan la información a nivel de microdatos. puntos, boxplots, Kernels, etc.
  • Abrir un 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

  • Si el color representa una variable lo definimos dentro del aes(), aes(... color = ingresos)
  • Cuando queremos simplemente mejorar el diseño (es fijo), se asigna por fuera, o dentro de cada tipo de gráficos, geom_col(color = 'green').

Boxplots

  • Los gráficos Boxplot representan una única variable (univariados).
  • Están compuestos por una caja, cuyo límite inferior es el valor donde se alcanza el 25% de la distribución
  • Su límite superior es el valor donde se alcanza el 75% de la misma.
  • A su vez, también el gráfico marca los valores “outliers” (datos que se encuentran a una distancia de al menos 1,5 veces el tamaño de la caja del límite inferior o superior de la caja, según corresponda)

Boxplot de ingresos de la ocupación principal, según nivel educativo

Hacemos un procesamiento simple: Sacamos los ingresos iguales a cero y las no respuestas de nivel educativo.
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.

# 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"
ggdata <- Individual_t117 %>% 
  filter(P21>0, !is.na(NIVEL_ED)) %>% 
  mutate(NIVEL_ED = as.factor(NIVEL_ED),
         CH04     = as.factor(CH04))
ggplot(ggdata, aes(x = NIVEL_ED, y = P21)) +
  geom_boxplot()+
  scale_y_continuous(limits = c(0, 40000))#Restrinjo el gráfico hasta ingresos de $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")

Por la forma en que está presentado el gráfico, el foco de atención sigue puesto en las diferencias de ingresos entre niveles educativo. Simplemente se agrega un corte por la variable de sexo.

Si lo que queremos hacer es poner el foco de atención en las diferencias por sexo, simplemente basta con invertir la variable x especificada con la variable utilizada en el facet_wrap

ggplot(ggdata, aes(x= CH04, y = P21, group = CH04, fill = CH04 )) +
  geom_boxplot()+
  scale_y_continuous(limits = c(0, 40000))+
  facet_grid(~ NIVEL_ED, labeller = "label_both") +
  theme(legend.position = "none")

Histogramas

Otra forma de mostrar la distribución de una variable es utilizar un histograma. Este tipo de gráficos agrupa las observaciones en bins: intervalos dentro del rango de la variable. Luego cuenta la cantidad de observaciones que caen dentro de cada uno de estos bins.

Por ejemplo, si observamos el ingreso de la ocupación principal:

hist_data <-Individual_t117 %>%
  filter(P21>0) 
ggplot(hist_data, aes(x = P21,weights = PONDIIO))+ 
geom_histogram()+
scale_x_continuous(limits = c(0,50000))

En este gráfico, los posibles valores de p21 se dividen en 30 bins consecutivos y el gráfico muestra cuantas observaciones caen en cada uno de ellos

Kernels

La función geom_density() nos permite construir kernels de la distribución. Esto es, un suavizado sobre los histogramas que se basa en alguna distribución supuesta dentro de cada bin. Es particularmente útil cuando tenemos una variable continua, dado que los histogramas rompen esa sensación de continuidad.

Veamos un ejemplo sencillo con los ingresos de la ocupación principal. Luego iremos complejizandolo

kernel_data <-Individual_t117 %>%
  filter(P21>0) 
ggplot(kernel_data, aes(x = P21,weights = PONDIIO))+ 
geom_density()+
scale_x_continuous(limits = c(0,50000))

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

El parametro adjust, dentro de la función geom_densitynos permite reducir o ampliar el rango de suavizado de la distribución. Su valor por default es 1. Veamos que sucede si lo seteamos en 2

ggplot(kernel_data, aes(x = P21,weights = PONDIIO))+ 
geom_density(adjust = 2)+
scale_x_continuous(limits = c(0,50000))

Como es esperable, la distribución del ingreso tiene “picos” en los valores redondos, ya que la gente suele declarar un valor aproximado al ingreso efectivo que percibe. Nadie declara ingresos de 30001. Al suavizar la serie con un kernel, eliminamos ese efecto.Si seteamos el rango para el suavizado en valores menores a 1, podemos observar estos picos.

ggplot(kernel_data, aes(x = P21,weights = PONDIIO))+ 
geom_density(adjust = 0.01)+
scale_x_continuous(limits = c(0,50000))

Ahora bien, como en todo grafico de R, podemos seguir agregando dimensiones para enriquecer el análisis.

kernel_data_2 <- kernel_data %>% 
  mutate(CH04= case_when(CH04 == 1 ~ "Varon",
                         CH04 == 2 ~ "Mujer"))
  
ggplot(kernel_data_2, aes(x = P21,
  weights = PONDIIO,
  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))
ggsave(filename = "../Resultados/Kernel_1.png",scale = 2)
Saving 13.6 x 8.38 in image

Podemos agregar aún la dimensión de ingreso laboral respecto del no laboral

kernel_data_3 <-kernel_data_2 %>% 
  select(REGION,P47T,T_VI, TOT_P12, P21 , PONDII, CH04) %>% 
  filter(!is.na(P47T), P47T > 0 ) %>% 
  mutate(ingreso_laboral    = TOT_P12 + P21,
         ingreso_no_laboral = T_VI) %>%
  gather(., key = Tipo_ingreso, Ingreso, c((ncol(.)-1):ncol(.))) %>%
  filter( Ingreso !=0)# Para este gráfico, quiero eliminar los ingresos = 0
kernel_data_3
  ggplot(kernel_data_3, 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 = "../Resultados/Kernel_1.png",scale = 2)
Saving 13.6 x 8.38 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(kernel_data_3, 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 = "../Resultados/Kernel_1.png",scale = 2)
Saving 13.6 x 8.38 in image

Ejercicios para practicar

  • Calcular el promedio del ingreso por ocupación principal (Variable P21) para asalariados con y sin descuento jubilatorio (Variable PP07H). Luego realizar un gráfico de barras donde se comparen ambos valores (para el 1er trimestre de 2017).
    Pistas: Se deben filtrar previamente los ingresos mayores a 0 (P21>0).Chequear que ponderador corresponde utilizar

  • Graficar la distribución del ingreso por ocupación principal para Asalariados, Cuentapropistas y Patrones, con el tipo de gráfico Kernel
    Pista: Usar la función facet_wrap para separar a cada una de las categorías ocupacionales)
    Sugerencia: incorporar la línea scale_x_continuous(limits = c(0,50000)) entre las capas del gráfico. ¿Qué cambió?

Ejercicios de tarea

  • Hacer un gráfico boxplot de la distribución de edades de los asalariados con descuento jubilatorio, y de los asalariados sin descuento jubilatorio.

  • Uniendo las bases de los distintos trimestres, calcular el procentaje de asalariados sin descuento jubilatorio como \(\frac{Asal. s/ desc jubil}{Asal. c/ desc jubil+ Asal.s/ desc jubil}\). Luego realizar un gráfico de linea con la evolución de este indicador

LS0tCnRpdGxlOiAiVXRpbGl6YWNpw7NuIGRlbCBsZW5ndWFqZSBSIHBhcmEgYXBsaWNhY2nDs24gZW4gbGEgRW5jdWVzdGEgUGVybWFuZW50ZSBkZSBIb2dhcmVzIgpzdWJ0aXRsZTogIkNsYXNlICA0IC0gR3LDoWZpY29zIHkgRGlzdHJpYnVjacOzbiBkZWwgSW5ncmVzbyIKZGF0ZTogIjYvMTEvMjAxOCIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6IAogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUgCi0tLQoKKkxhcyBub3RhcyBkZWwgcHJlc2VudGUgY3Vyc28gZnVlcm9uIGVsYWJvcmFkYXMgb3JpZ2luYWxtZW50ZSBwb3IgRGllZ28gS296bG93c2tpIHkgR3VpZG8gV2Vrc2xlci4gRW4gbGFzIHN1Y2VzaXZhcyBtb2RpZmljYWNpb25lcyBjb2xhYm9yYXJvbjogTmF0c3VtaSBTaG9raWRhIHkgTWF0w61hcyBMaW9uaSogCgoKPiBSZWluaWNpYXIgU2VzacOzbgoKIyBHcsOhZmljb3MgQsOhc2ljb3MgZW4gUgoKUmJhc2UgIHRpZW5lIGFsZ3Vub3MgY29tYW5kb3MgZ2Vuw6lyaWNvcyBwYXJhIHJlYWxpemFyIGdyw6FmaWNvcywgcXVlIHNlIGFkYXB0YW4gYWwgdGlwbyBkZSBpbmZvcm1hY2nDs24gcXVlIHNlIGxlIHBpZGUgZ3JhZmljYXIsIHBvciBlamVtcGxvOgoKLSBwbG90KCkKLSBoaXN0KCkKCmBgYHtyIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTh9CiMgaXJpcyBlcyB1biBzZXQgZGUgZGF0b3MgY2zDoXNpY28sIHF1ZSB5YSB2aWVuZSBpbmNvcnBvcmFkbyBlbiBSCmlyaXMKcGxvdChpcmlzKQpgYGAKYGBge3J9CiNBbCBlc3BlY2lmaWNhciB1bmEgdmFyaWFibGUsIHB1ZWRvIHZlciBlbCB2YWxvciBxdWUgdG9tYSBjYWRhIHVubyBkZSBzdXMgcmVnaXN0cm9zIChJbmRleCkKcGxvdChpcmlzJFNlcGFsLkxlbmd0aCx0eXBlID0gInAiKSAjIFVuIHB1bnRvIHBvciBjYWRhIHZhbG9yCnBsb3QoaXJpcyRTZXBhbC5MZW5ndGgsdHlwZSA9ICJsIikgIyBVbmEgbGluZWEgcXVlIHVuYSBjYWRhIHZhbG9yCnBsb3QoaXJpcyRTZXBhbC5MZW5ndGgsdHlwZSA9ICJiIikgI0FtYmFzCmhpc3QoaXJpcyRTZXBhbC5MZW5ndGgsIGNvbCA9ICJsaWdodHNhbG1vbjEiLCBtYWluID0gIkhpc3RvZ3JhbWEiKQpgYGAKCiMjIHBuZwpMYSBmdW5jacOzbiBgYGBwbmcoKWBgYCBub3MgcGVybWl0ZSBncmFiYXIgdW5hIGltYWdlbiBlbiBlbCBkaXNjby4gTGxldmEgY29tbyBhcmd1bWVudG8gcHJpbmNpcGFsIGxhIHJ1dGEgY29tcGxldGEgYSBkb25kZSBzZSBkZXNlYSBndWFyZGFyIGxhIG1pc21hLCBpbmNsdXllbmRvIGVsIG5vbWJyZSBxdWUgcXVlcmVtb3MgZGFyIGFsIGFyY2hpdm8uIEEgc3UgdmV6IHB1ZWRlbiBlc3BlY2lmaWNhcnNlIG90cm9zIGFyZ3VtZXRub3MgY29tbyBlbCBhbmNobyB5IGxhcmdvIGRlIGxhIGltYWdlbiwgZW50cmUgb3Ryb3MuICAKCmBgYHtyfQpydXRhX2FyY2hpdm8gPC0gIi4uL1Jlc3VsdGFkb3MvZ3JhZmljbzEuUE5HIgpydXRhX2FyY2hpdm8KcG5nKHJ1dGFfYXJjaGl2bykKcGxvdChpcmlzJFNlcGFsLkxlbmd0aCx0eXBlID0gImIiKQpkZXYub2ZmKCkKYGBgCgpMYSBmdW5jacOzbiBgYGBwbmcoKWBgYCBfYWJyZSBlbCBkaXNwb3NpdGl2byBkZSBpbWFnZW5fIGVuIGVsIGRpcmVjdG9yaW8gZXNwZWNpZmljYWRvLiBMdWVnbyBjcmVhbW9zIGVsIGdyw6FmaWNvIHF1ZSBkZXNlYW1vcyAobyBsbGFtYW1vcyBhIHVubyBwcmV2aWFtZW50ZSBjb25zdHJ1aWRvKSwgZWwgY3VhbCBzZSBkZXNwbGVnYXLDoSBlbiBsYSB2ZW50YW5hIGluZmVyaW9yIGRlcmVjaGEgZGUgbGEgcGFudGFsbGEgZGUgUnN0dWRpby4gRmluYWxtZW50ZSBjb24gYGBgZGV2Lm9mZigpYGBgIHNlIF9jaWVycmEgZWwgZGlzcG9zaXRpdm9fIHkgc2UgZ3JhYmFuIGxvcyBncsOhZmljb3MuIAogICAgICAgICAgIApMb3MgZ3LDoWZpY29zIGRlbCBSIGJhc2Ugc29uIMO6dGlsZXMgcGFyYSBlc2NyaWJpciBkZSBmb3JtYSByw6FwaWRhIHkgb2J0ZW5lciBhbGd1bmEgaW5mb3JtYWNpw7NuIG1pZW50cmFzIHRyYWJhamFtb3MuIE11Y2hvcyBwYXF1ZXRlcyBlc3RhZMOtc3RpY29zIHBlcm1pdGVuIG1vc3RyYXIgbG9zIHJlc3VsdGFkb3MgZGUgZm9ybWEgZ3LDoWZpY2EgY29uIGVsIGNvbWFuZG8gcGxvdCAocG9yIGVqZW1wbG8sIGxhcyByZWdyZXNpb25lcyBsaW5lYWxlcyBgYGBsbSgpYGBgKS4gICAgICAgCiAKU2luIGVtYmFyZ28sIGV4aXN0ZW4gbGlicmVyw61hcyBtdWNobyBtZWpvcmVzIHBhcmEgY3JlYXIgZ3LDoWZpY29zIGRlIG5pdmVsIGRlIHB1YmxpY2FjacOzbi4gTGEgbcOhcyBpbXBvcnRhbnRlIGVzIF9fZ2dwbG90Ml9fLCBxdWUgYSBzdSB2ZXogdGllbmUgZXh0ZW5zaW9uZXMgbWVkaWFudGUgb3RyYXMgbGlicmVyw61hcy4KCgojIFtHZ3Bsb3QyXShodHRwOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS8pCgoKZ2dwbG90IHRpZW5lIHN1IHNpbnRheGlzIHByb3BpYS4gTGEgaWRlYSBjZW50cmFsIGVzIHBlbnNhciBsb3MgZ3LDoWZpY29zIGNvbW8gdW5hIHN1Y2VzacOzbiBkZSBjYXBhcywgcXVlIHNlIGNvbnN0cnV5ZW4gdW5hIGEgbGEgdmV6LiAgICAKCi0gRWwgb3BlcmFkb3IgX19gYGArYGBgX18gbm9zIHBlcm1pdGUgaW5jb3Jwb3JhciBudWV2YXMgY2FwYXMgYWwgZ3LDoWZpY28uCgotIEVsIGNvbWFuZG8gYGBgZ2dwbG90KClgYGAgbm9zIHBlcm1pdGUgZGVmaW5pciBsYSBmdWVudGUgZGUgX19kYXRvc19fIHkgbGFzIF9fdmFyaWFibGVzX18gcXVlIGRldGVybWluYXJhbiBsb3MgZWplcyBkZWwgZ3JhZmljbyAoeCx5KSwgYXPDrSBjb21vIGVsIGNvbG9yIHkgbGEgZm9ybWEgZGUgbGFzIGzDrW5lYXMgbyBwdW50b3MsZXRjLiAKCi0gTGFzIHN1Y2VzaXZhcyBjYXBhcyBub3MgcGVybWl0ZW4gZGVmaW5pcjoKICAgCiAgICAtIFVubyBvIG3DoXMgdGlwb3MgZGUgZ3LDoWZpY29zIChkZSBjb2x1bW5hcywgYGBgZ2VvbV9jb2woKWBgYCwgZGUgbMOtbmVhLCBgYGBnZW9tX2xpbmUoKWBgYCwgZGUgcHVudG9zLGBgYGdlb21fcG9pbnQoKWBgYCwgYm94cGxvdCwgYGBgZ2VvbV9ib3hwbG90KClgYGApCiAgICAtIFTDrXR1bG9zIGBgYGxhYnMoKWBgYAogICAgLSBFc3RpbG8gZGVsIGdyw6FmaWNvIGBgYHRoZW1lKClgYGAKICAgIC0gRXNjYWxhcyBkZSBsb3MgZWplcyBgYGBzY2FsZV95X2NvbnRpbnVvdXNgYGAsYGBgc2NhbGVfeF9kaXNjcmV0ZWBgYCAKICAgIC0gRGl2aXNpw7NuIGVuIHN1YmNvbmp1bnRvcyBgYGBmYWNldF93cmFwKClgYGAsYGBgZmFjZXRfZ3JpZCgpYGBgCgpnZ3Bsb3QgdGllbmUgX19tdWNob3NfXyBjb21hbmRvcywgeSBubyB0aWVuZSBzZW50aWRvIHNhYmVybG9zIGRlIG1lbW9yaWEsIGVzIHNpZW1wcmUgw7p0aWwgcmV1dGlsaXphciBncsOhZmljb3Mgdmllam9zIHkgdGVuZXIgYSBtYW5vIGVsIFttYWNoZXRlXShodHRwczovL3d3dy5yc3R1ZGlvLmNvbS93cC1jb250ZW50L3VwbG9hZHMvMjAxNi8xMS9nZ3Bsb3QyLWNoZWF0c2hlZXQtMi4xLnBkZikuICAgIAoKIyMgR3LDoWZpY28gZGUgUHVudG9zCgpBIGNvbnRpbnVhY2nDs24gc2UgZGVzcGxlZ2EgdW4gZ3LDoWZpY28gZGUgdmFyaWFzIGNhcGFzIGRlIGNvbnN0cnVjY2nDs24sIGNvbiBzdSBjb3JyZXNwb25kaWVudGUgcG9yY2nDs24gZGUgY8OzZGlnby4gRW4gZWwgbWlzbW8gc2UgYnVzY2Fyw6EgdmlzdWFsaXphciwgYSBwYXJ0aXIgZGUgbGEgYmFzZSBkZSBkYXRvcyAqKmlyaXMqKiBsYSByZWxhY2nDs24gZW50cmUgZWwgYW5jaG8geSBlbCBsYXJnbyBkZSBsb3MgcGV0YWxvcywgbWVkaWFudGUgdW4gZ3LDoWZpY28gZGUgcHVudG9zLgoKYGBge3IsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoZ2dwbG90MikgIyBjYXJnYW1vcyBsYSBsaWJyZXLDrWEKZ2dwbG90KGRhdGEgPSBpcmlzLCBhZXMoeCA9IFBldGFsLkxlbmd0aCwgUGV0YWwuV2lkdGgsIGNvbG9yID0gU3BlY2llcykpKwogIGdlb21fcG9pbnQoYWxwaGE9MC43NSkrCiAgbGFicyh0aXRsZSA9ICJNZWRpZGFzIGRlIGxvcyBww6l0YWxvcyBwb3IgZXNwZWNpZSIpKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICdub25lJykrCiAgZmFjZXRfd3JhcCh+U3BlY2llcykKCmBgYAojIyBDYXBhcyBkZWwgR3LDoWZpY28KVmVhbW9zIGFob3JhLCBlbCAicGFzbyBhIHBhc28iIGRlbCBhcm1hZG8gZGVsIG1pc21vLiAgICAgICAgICAKCkVuIHByaW1lcmEgaW5zdGFuY2lhIHNvbG8gZGVmaW5vIGxvcyBlamVzLiBZIGVuIGVzdGUgY2FzbyB1biBjb2xvciBwYXJ0aWN1bGFyIHBhcmEgY2FkYSBFc3BlY2llLgpgYGB7cn0KZyA8LSBnZ3Bsb3QoZGF0YSA9IGlyaXMsIGFlcyh4ID0gUGV0YWwuTGVuZ3RoLCBQZXRhbC5XaWR0aCwgY29sb3IgPSBTcGVjaWVzKSkKZwpgYGAKTHVlZ28sIGRlZmlubyBlbCB0aXBvIGRlIGdyw6FmaWNvLiBFbCAqYWxwaGEqIG1lIHBlcm1pdGUgZGVmaW5pciBsYSBpbnRlbnNpZGFkIGRlIGxvcyBwdW50b3MKYGBge3J9CmcgPC0gZyArICBnZW9tX3BvaW50KGFscGhhPTAuMjUpCmcKYGBgICAKTGFzIHNpZ3VpZW50ZXMgdHJlcyBjYXBhcyBtZSBwZXJtaXRlbiByZXNwZWN0aXZhbWVudGU6IAogCiAtIERlZmluaXIgZWwgdMOtdHVsbyBkZWwgZ3LDoWZpY28KIC0gUXVpdGFyIGxhIGxleWVuZGEKIC0gQWJyaXIgZWwgZ3LDoWZpY28gZW4gdHJlcyBmcmFnbWVudG9zLCB1bm8gcGFyYSBjYWRhIGVzcGVjaWUKCmBgYHtyfSAKZyA8LSBnICsKICBsYWJzKHRpdGxlID0gIk1lZGlkYXMgZGUgbG9zIHDDqXRhbG9zIHBvciBlc3BlY2llIikrCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gJ25vbmUnKSsKICBmYWNldF93cmFwKH5TcGVjaWVzKQpnCgpgYGAKCiMgR3JhZmljb3MgSW5ncmVzb3MgLSBFUEgKCkEgY29udGludWFjacOzbiB1dGlsemFyZW1vcyBsb3MgY29uY2VwdG9zIGFib3JkYWRvcywgcGFyYSByZWFsaXphciBncsOhZmljb3MgYSBwYXJ0aXIgZGUgbGFzIHZhcmlhYmxlcyBkZSBpbmdyZXNvcy4gICAgICAgIAoKVW5hIGRlIGxhcyB2ZW50YWphcyBkZWwgKipSKiogcmVzcGVjdG8gYSBvdHJvcyBzb2Z0d2FyZXMgZXN0YWTDrXN0aWNvcywgZXMgbGEgZmFjaWxpZGFkIGNvbiBsYSBxdWUgcG9kZW1vcyB0cmFiYWphciBlbiBzaW11bHRhbmVvIGNvbiBtdWx0aXBsZXMgYmFzZXMgZGUgZGF0b3MuIEFwcm92ZWNoYW5kbyBlc3RhIHBvdGVuY2lhbGlkYWQsIGxldmFudGFyZW1vcyBhIGNvbnRpbnVhY2nDs24gNCBiYXNlcyBpbmRpdmlkdWFsZXMgZGUgbGEgX19FUEhfXyBwYXJhIHJlYWxpemFyIGdyw6FmaWNvcyBkZSBzZXJpZXMgdGVtcG9yYWxlcy4KCgpgYGB7ciwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KI0NhcmdhbW9zIGxhcyBsaWJyZXLDrWFzIGEgdXRpbGl6YXIKCmxpYnJhcnkodGlkeXZlcnNlKSAjIHRpZW5lIGdncGxvdCwgZHBseXIsIHRpZHlyLCB5IG90cm9zCmxpYnJhcnkoZ2d0aGVtZXMpICAjIGVzdGlsb3MgZGUgZ3LDoWZpY29zCmxpYnJhcnkoZ2dyZXBlbCkgICAjIGV0aXF1ZXRhcyBkZSB0ZXh0byBtw6FzIHByb2xpamFzIHF1ZSBsYXMgZGUgZ2dwbG90CgpgYGAKClBhcmEgbWF5b3IgcHJhY3RpY2lkYWQsIGFsIG1vbWVudG8gZGUgbGV2YW50YXIgbGFzIGJhc2VzIGRlIGRhdG9zLCBzZWxlY2Npb25hbW9zIGxhcyB2YXJpYWJsZXMgbmVjZXNhcmlhcyAoQSBleGNlcGNpw7NuIGRlIGxhIGJhc2UgZGVsIHQxXzIwMTcgY29uIGxhIHF1ZSB0cmFiYWphcmVtb3MgbnVtZXJvc2FzIHZhcmlhYmxlcykKCmBgYHtyIHN0cmlwLndoaXRlPUZBTFNFfQoKSW5kaXZpZHVhbF90MTE3IDwtIHJlYWQudGFibGUoIi4uL0Z1ZW50ZXMvdXN1X2luZGl2aWR1YWxfdDExNy50eHQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZXA9IjsiLCBkZWM9IiwiLCBoZWFkZXIgPSBUUlVFLCBmaWxsID0gVFJVRSkKCkluZGl2aWR1YWxfdDIxNiA8LSByZWFkLnRhYmxlKCIuLi9GdWVudGVzL3VzdV9pbmRpdmlkdWFsX3QyMTYudHh0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VwPSI7IiwgZGVjPSIsIiwgaGVhZGVyID0gVFJVRSwgZmlsbCA9IFRSVUUpICU+JSAKICBzZWxlY3QoQU5PNCxUUklNRVNUUkUsUDIxLFBPTkRJSU8sSVBDRiwgUE9ORElIKQoKSW5kaXZpZHVhbF90MzE2IDwtIHJlYWQudGFibGUoIi4uL0Z1ZW50ZXMvdXN1X2luZGl2aWR1YWxfdDMxNi50eHQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZXA9IjsiLCBkZWM9IiwiLCBoZWFkZXIgPSBUUlVFLCBmaWxsID0gVFJVRSklPiUgCiAgc2VsZWN0KEFOTzQsVFJJTUVTVFJFLFAyMSxQT05ESUlPLCBJUENGLCBQT05ESUgpCgpJbmRpdmlkdWFsX3Q0MTYgPC0gcmVhZC50YWJsZSgiLi4vRnVlbnRlcy91c3VfaW5kaXZpZHVhbF90NDE2LnR4dCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlcD0iOyIsIGRlYz0iLCIsIGhlYWRlciA9IFRSVUUsIGZpbGwgPSBUUlVFKSU+JSAKICBzZWxlY3QoQU5PNCxUUklNRVNUUkUsUDIxLFBPTkRJSU8sIElQQ0YsIFBPTkRJSCkKCmBgYAojIyBJbmdyZXNvIGRlIGxhIG9jdXBhY2nDs24gcHJpbmNpcGFsCgotIE51ZXN0cm8gcHJpbWVyIGVqZXJjaWNpbyBjb25zaXN0aXLDoSBlbiBhbmFsaXphciBsYSBldm9sdWNpw7NuIGRlbCBpbmdyZXNvIHBvciBsYSBvY3VwYWNpw7NuIHByaW5jaXBhbC4gT2JzZXJ2YW5kbyBlbCBkaXNlw7FvIGRlIHJlZ2lzdHJvIHBvZHJlbW9zIHZlciBxdWUgZGljaGEgdmFyaWFibGUgZXN0w6EgY29kaWZpY2FkYSBjb21vIGBgYCBQMjEgYGBgLiAgICAgICAgCgoKLSBQYXJhIG1pbmltaXphciBsYSB2b2xhdGlsaWRhZCBxdWUgbGEgKm5vIHJlc3B1ZXN0YSBkZSBpbmdyZXNvcyogcG9kcsOtYSBnZW5lcmFyIGVuIGxvcyByZXN1bHRhZG9zIGRlIHVub3MgeSBvdHJvcyB0cmltZXN0cmVzLCBsYSBFUEggYXNpZ25hIGEgbG9zIG5vIHJlc3BvbmRlbnRlcyBlbCBjb21wb3J0YW1pZW50byBkZSBsb3MgcmVzcG9uZGVudGVzIHBvciBlc3RyYXRvIGRlIGxhIG11ZXN0cmEuIEEgcGFydGlyIGRlIGVzdG8gKipsYXMgdmFyaWFibGVzIGRlIGluZ3Jlc29zIHByZXNlbnRhbiBkaXN0aW50b3MgZmFjdG9yZXMgZGUgZXhwYW5zacOzbioqLiAgICAgIAoKLSBFbiBlbCBjYXNvIGRlbCBpbmdyZXNvIGRlIGxhIG9jdXBhY2nDs24gcHJpbmNpcGFsIGRlYmVtb3MgdHJhYmFqYXIgY29uIGVsIGV4cGFuc29yIGBgYFBPTkRJSU9gYGAuICAgICAgCgpgYGB7ciBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD02LCB3YXJuaW5nPUZBTFNFfQojVW5pbW9zIGxhcyBiYXNlcyB5IGNyZWFtb3MgdW5hIHZhcmlhYmxlIHF1ZSBjb25jYXRlbmEgZWwgYcOxbyB5IGVsIHRyaW1lc3RyZQpVbmlvbl9CYXNlcyA8LSBiaW5kX3Jvd3MoSW5kaXZpZHVhbF90MjE2LCAKICAgICAgICAgICAgICAgICAgSW5kaXZpZHVhbF90MzE2LAogICAgICAgICAgICAgICAgICBJbmRpdmlkdWFsX3Q0MTYsCiAgICAgICAgICAgICAgICAgIEluZGl2aWR1YWxfdDExNykgJT4lIAogIG11dGF0ZShwZXJpb2RvID0gcGFzdGUoQU5PNCwgVFJJTUVTVFJFLCBzZXAgPSAiXyIpKSAKYGBgCgotIENhbGN1bGFtb3MgZWwgaW5ncmVzbyBwZXIgY2FwaXRhIHByb21lZGlvLCB1dGlsaXphbmRvIGVsIHBvbmRlcmFkb3IgY29ycmVzcG9uZGllbnRlLiAKLSAqKkltcG9ydGFudGU6IERlYmVtb3MgZXhpZ2lyIGluZ3Jlc29zIHBvc2l0aXZvcywgeWEgcXVlIHRlbmVtb3MgbnVtZXJvc29zIGNhc29zIGNvbiBpbmdyZXNvIDAgeSBvdHJvcyBjb2RpZmljYWRvcyBjb24gIC05IChjdWFuZG8gbm8gY29ycmVzcG9uZGUgbGEgcHJlZ3VudGEgcG9yIGxhIG9jdXBhY2nDs24gcHJpbmNpcGFsKSoqIAoKYGBge3IsIHdhcm5pbmc9RkFMU0V9CklPcHBhbCA8LVVuaW9uX0Jhc2VzICAgJT4lIAogIGZpbHRlcihQMjE+MCkgJT4lIAogIGdyb3VwX2J5KHBlcmlvZG8pICU+JSAKICBzdW1tYXJpc2UoSU9wcGFsX3Byb20gPSB3ZWlnaHRlZC5tZWFuKFAyMSwgUE9ORElJTykpIAoKSU9wcGFsCmBgYAoKQWhvcmEgcG9kZW1vcyB1dGlsaXphciBlc3RlIG51ZXZvIGRhdGFmcmFtZSBwYXJhIGdyYWZpY2FyCgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBJT3BwYWwsIGFlcyh4ID0gcGVyaW9kbywgeSA9IElPcHBhbF9wcm9tKSkgKyAKICBnZW9tX3BvaW50KCkKYGBgCgpBZ3JlZ2FuZG8gYWxndW5vcyBwYXLDoW1ldHJvcyBtw6FzIC4uLgoKMS4gRGVmaW5pbW9zIGxhcyB2YXJpYWJsZXMgZGVsIGdyw6FmaWNvCgpgYGB7cn0KZyA8LSBJT3BwYWwgJT4lICNQb2RlbW9zIHVzYXIgbG9zICJwaXBlcyIgcGFyYSBsbGFtYXIgYWwgRGF0YWZyYW1lIHF1ZSBjb250aW5lbiBsYSBpbmZvCmdncGxvdChhZXMoeCA9IHBlcmlvZG8sCiAgICAgICAgICAgeSA9IElPcHBhbF9wcm9tLAogICAgICAgICAgICNBZ3J1cGFyIG5vcyBwZXJtaXRpcsOhIGdlbmVyYXIgbGFzIGxpbmVhcyBkZWwgZ3LDoWZpY28KICAgICAgICAgICBncm91cCA9ICdJT3BwYWxfcHJvbScsCiAgICAgICAgICAgI0FncmVnYW1vcyB1bmEgZXRpcXVldGEgYSBsb3MgZGF0b3MgKFJlZG9uZGVhbmRvIGxhIHZhcmlhYmxlIGEgMiBwb3NpY2lvbmVzIGRlY2ltYWxlcykKICAgICAgICAgICBsYWJlbD0gcm91bmQoSU9wcGFsX3Byb20sMikpKQpgYGAKCjIuIEFncmVnYW1vcyB0aXR1bG8geSBtb2RpZmljYW1vcyAgZWplcwoKYGBge3J9CmcgPC0gZyArCiAgbGFicyh4ID0gIlRyaW1lc3RyZSIsCiAgICAgICB5ID0gIklQQ0YgcHJvbWVkaW8iLAogICAgICAgdGl0bGUgPSAiSW5ncmVzbyBwcm9tZWRpbyBwb3IgbGEgb2N1cGFjacOzbiBwcmluY2lwYWwiLAogICAgICAgc3VidGl0bGUgPSAiU2VyaWUgMnRyaW1fMjAxNiAtIDF0cmltXzIwMTciLAogICAgICAgY2FwdGlvbiA9ICJGdWVudGU6IEVQSCIpCmBgYAoKMy4gQWdyZWdhbW9zIHB1bnRvcyB5IGxpbmVhcwoKYGBge3J9CmcgPC0gZyArCiAgZ2VvbV9wb2ludChzaXplPSAzKSsgI3B1ZWRvIGRlZmluaXIgdGFtYcOxbyBkZSBsYXMgbGluZWFzCiAgZ2VvbV9saW5lKCBzaXplPSAxICkKYGBgCgo0LiBBZ3JlZ28gZXRpcXVldGFzIGNvbiBlbCB0ZXh0by4gTGFzIGNvcnJvIGhhY2lhIGFycmliYSAobnVkZ2VfeSkgeSBhIGxhIGl6cXVpZXJkYShudWRnZV94KQo1LiBBZ3JlZ28gdW4gdGVtYQoKYGBge3J9CmcgPC0gZyArCiAgZ2VvbV90ZXh0X3JlcGVsKG51ZGdlX3kgPSA1MDAsIG51ZGdlX3ggPSAwLjI1KSsKICB0aGVtZV9taW5pbWFsKCkKYGBgCgo2LiBtdWVzdHJvIGVsIGdyw6FmaWNvIChndWFyZGFkbyBlbiBgZ2ApCgpgYGB7cn0KCmcKZ2dzYXZlKGZpbGVuYW1lID0gIi4uL1Jlc3VsdGFkb3MvSVBDRl9wcm9tLnBuZyIpICNHdWFyZG8gZWwgR3JhZmljbwoKYGBgCgoKIyMgSW5ncmVzbyBwZXIgY2FwaXRhIGZhbWlsaWFyCkxhIHZhcmlhYmxlIGBgYCBJUENGIGBgYCByZXByZXNlbnRhIGVsIGluZ3Jlc28gcGVyIGNhcGl0YSBkZWwgaG9nYXIsIGRlbCBpbmRpdmlkdW8gZW5jdWVzdGFkby4gU3UgZm9ybXVsYSBlcyBsYSBzaWd1aWVudGU6IAokJFxmcmFje1xzdW1fe2k9MX1ebiBJTkdfaX17bn0kJCAgICAgIAoKRG9uZGU6ICAgCiAKIC0gSU5HX2kgZXMgZWwgaW5ncmVzbyBkZWwgbWllbWJybyBpIGRlbCBob2dhciBlbiBjdWVzdGnDs24geSAKIC0gbiBlcyBlbCB0b3RhbCBkZSBtaWVtYnJvcyBkZWwgaG9nYXIKClBhcmEgdHJhYmFqYXIgY29uIGluZ3Jlc29zIGRlIGxvcyAqKkhvZ2FyZXMqKiBkZWJlbW9zIHV0aWxpemFyIGVsIHBvbmRlcmFkb3IgYGBgUE9ORElIYGBgLiAgQSBjb250aW51YWNpw7NuIHJlYWxpemFtb3MgbGEgdW5pw7NuIGRlIGxhcyBjdWF0cm8gYmFzZXMgYSB0cmFiYWphciwgcGFyYSBsdWVnbyBjYWxjdWxhcmEgcGFyYSBjYWRhIHBlcsOtb2RvIGVsIEluZ3Jlc28gcGVyIGNhcGl0YSBmYW1pbGlhciBwcm9tZWRpby4KCmBgYHtyIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTZ9CkluZ19wcm9tX2hvZ2FyIDwtIGJpbmRfcm93cyhJbmRpdmlkdWFsX3QyMTYsIAogICAgICAgICAgICAgICAgICBJbmRpdmlkdWFsX3QzMTYsCiAgICAgICAgICAgICAgICAgIEluZGl2aWR1YWxfdDQxNiwKICAgICAgICAgICAgICAgICAgSW5kaXZpZHVhbF90MTE3KSAlPiUKICBzZWxlY3QoQU5PNCxUUklNRVNUUkUsIElQQ0YsIFBPTkRJSCkgJT4lIAogIG11dGF0ZShwZXJpb2RvID0gcGFzdGUoQU5PNCwgVFJJTUVTVFJFLCBzZXAgPSAiXG4iKSkgJT4lIAogIGdyb3VwX2J5KHBlcmlvZG8pICU+JSAKICBzdW1tYXJpc2UoSVBDRl9wcm9tID0gd2VpZ2h0ZWQubWVhbihJUENGLCBQT05ESUgpKSAlPiUgCiAgdW5ncm91cCgpCgpJbmdfcHJvbV9ob2dhcgpgYGAKCgpgYGB7cn0KSW5nX3Byb21faG9nYXIgJT4lIApnZ3Bsb3QoYWVzKHggPSBwZXJpb2RvLAogICAgICAgICAgIHkgPSBJUENGX3Byb20sCiAgICAgICAgICAgZ3JvdXAgPSAnSVBDRl9wcm9tJywKICAgICAgICAgICBsYWJlbD0gcm91bmQoSVBDRl9wcm9tLDIpKSkgKwogIGxhYnMoeCA9ICJUcmltZXN0cmUiLAogICAgICAgeSA9ICJJUENGIHByb21lZGlvIiwKICAgICAgIHRpdGxlID0gIkluZ3Jlc28gcGVyIGNhcGl0YSBmYW1pbGlhciBwcm9tZWRpbyIsIAogICAgICAgc3VidGl0bGUgPSAiU2VyaWUgMnRyaW1fMjAxNiAtIDF0cmltXzIwMTciLCAKICAgICAgIGNhcHRpb24gPSAiRnVlbnRlOiBFUEgiKSsgCiAgZ2VvbV9saW5lKCBzaXplPSAxICkrCiAgZ2VvbV9wb2ludChzaXplPSAzKSsgCiAgZ2VvbV9sYWJlbF9yZXBlbChudWRnZV95ID0gMjUwLGZpbGw9ImdyYXk4MCIpKyAjQXBsaWNvIG90cm8gdGlwbyBkZSBldGlxdWV0YSwKICB0aGVtZV9saWdodCgpKyAjRWxpam8gb3RybyB0ZW1hIHBhcmEgZWwgZ3JhZmljbwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCmdnc2F2ZShmaWxlbmFtZSA9ICIuLi9SZXN1bHRhZG9zL0lQQ0ZfcHJvbS5wbmciKSAjR3VhcmRvIGVsIEdyYWZpY28KYGBgCgoKIyMgRGlzdHJpYnVjacOzbiBkZSBsb3MgaW5ncmVzb3MgbGFib3JhbGVzIHkgbm8gbGFib3JhbGVzIHBvciBzZXhvCgoKLSBJbmdyZXNvcyBubyBsYWJvcmFsZXMgOiBfVFZJXwotIEluZ3Jlc28gbGFib3JhbGVzOgogIC0gX1AyMV8gTU9OVE8gREUgSU5HUkVTTyBERSBMQSBPQ1VQQUNJw5NOIFBSSU5DSVBBTAogIC0gX1RvdHAxMl8gIE1PTlRPIERFIElOR1JFU08gREUgT1RSQVMgT0NVUEFDSU9ORVMuCgpgYGB7ciBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD04fQogIGRhdGFncmFmXzIgPC1JbmRpdmlkdWFsX3QxMTcgJT4lIAogICNlbGlnbyBsYXMgdmFyaWFibGVzIHF1ZSBuZWNlc2l0bwogICAgc2VsZWN0KFA0N1QsVF9WSSwgVE9UX1AxMiwgUDIxICwgUE9ORElJLCBDSDA0LE5JVkVMX0VEKSAlPiUgCiAgIyBNZSBxdWVkbyBjb24gbG9zIHF1ZSB0aWVuZW4gaW5ncmVzbyB0b3RhbCBpbmRpdmlkdWFsIChQNDcpIHBvc2l0aXZvCiAgICBmaWx0ZXIoIWlzLm5hKFA0N1QpLCBQNDdUID4gMCApICU+JSAKICAgIG11dGF0ZShpbmdyZXNvX2xhYm9yYWwgICAgPSBUT1RfUDEyICsgUDIxLAogICAgICAgICAgIGluZ3Jlc29fbm9fbGFib3JhbCA9IFRfVkksCiAgICAgICAgICAgaW5ncmVzb190b3RhbCAgICAgID0gaW5ncmVzb19sYWJvcmFsICsgaW5ncmVzb19ub19sYWJvcmFsLAogICAgICAgICAgIENIMDQgICAgICAgICAgICAgICA9IGNhc2Vfd2hlbihDSDA0ID09IDEgfiAiVmFyb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBDSDA0ID09IDIgfiAiTXVqZXIiKSkgJT4lIAogIGdyb3VwX2J5KENIMDQpICU+JSAKICBzdW1tYXJpc2UoJ2luZ3Jlc28gbGFib3JhbCcgICAgPSBzdW0oaW5ncmVzb19sYWJvcmFsKlBPTkRJSSkvc3VtKGluZ3Jlc29fdG90YWwqUE9ORElJKSwKICAgICAgICAgICAgJ2luZ3Jlc28gbm8gbGFib3JhbCcgPSBzdW0oaW5ncmVzb19ub19sYWJvcmFsKlBPTkRJSSkvc3VtKGluZ3Jlc29fdG90YWwqUE9ORElJKSkgCgpkYXRhZ3JhZl8yICAKYGBgCgpEb3kgdnVlbHRhIGxhIHRhYmxhIHBhcmEgcG9kZXIgZ3JhZmljYXIKCmBgYHtyfQpkYXRhZ3JhZmljbyA8LSBkYXRhZ3JhZl8yICU+JQpnYXRoZXIodGlwb19pbmdyZXNvLCBtb250bywyOjMgKSAKZGF0YWdyYWZpY28KYGBgCgpgYGB7ciBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD04fSAgCgpnZ3Bsb3QoZGF0YWdyYWZpY28sIGFlcyhDSDA0LCBtb250bywgZmlsbCA9IHRpcG9faW5ncmVzbywgCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IHNwcmludGYoIiUxLjFmJSUiLCAxMDAqbW9udG8pKSkrCiAgZ2VvbV9jb2wocG9zaXRpb24gPSAic3RhY2siLCBhbHBoYT0wLjYpICsgCiAgZ2VvbV90ZXh0KHBvc2l0aW9uID0gcG9zaXRpb25fc3RhY2sodmp1c3QgPSAwLjUpLCBzaXplPTUpKwogIGxhYnMoeD0iIix5PSJQb3JjZW50YWplIikrCiAgdGhlbWVfdHVmdGUoKSsKICBzY2FsZV95X2NvbnRpbnVvdXMoKSsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwKICAgICAgICBsZWdlbmQudGl0bGU9ZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlPTI1KSkKICAKCmdnc2F2ZShmaWxlbmFtZSA9ICIuLi9SZXN1bHRhZG9zL2luZ3Jlc29zIGxhYm9yYWxlcyB5IG5vIGxhYm9yYWxlcy5wbmciLHNjYWxlID0gMikKYGBgCgpQb2RlbW9zIGFncmVnYXIgdGFtYmnDqW4gZ3J1cG9zIGRlIGVkYWQgY29tbyBvdHJhIGRpbWVuc2nDs24gZGVsIGFuw6FsaXNpcy4gT3B0YW1vcyBwb3IgIHJlc3RyaW5ndWlyIGxhIHBvYmxhY2nDs24gZGUgYW7DoWxpc2lzIGEgbGEgY29tcHJlbmRpZGEgZW50cmUgMTggeSA2MCBhw7FvcwoKCgpgYGB7cn0KIyNBZ3JlZ28gYWwgcHJvY2VkaW1pZW50byBhbnRlcmlvciB1bmEgY2xhc2lmaWNhY2nDs24gZGUgbGFzIGVkYWRlcwogIGRhdGFncmFmXzMgPC1JbmRpdmlkdWFsX3QxMTcgJT4lIAogICAgc2VsZWN0KFA0N1QsVF9WSSwgVE9UX1AxMiwgUDIxICwgUE9ORElJLCBDSDA0LENIMDYpICU+JSAKICAgIGZpbHRlcighaXMubmEoUDQ3VCksIFA0N1QgPiAwICwgQ0gwNiAlaW4lIGMoMTg6NjApKSAlPiUgCiAgICBtdXRhdGUoaW5ncmVzb19sYWJvcmFsICAgID0gYXMubnVtZXJpYyhUT1RfUDEyICsgUDIxKSwKICAgICAgICAgICBpbmdyZXNvX25vX2xhYm9yYWwgPSBhcy5udW1lcmljKFRfVkkpLAogICAgICAgICAgIGluZ3Jlc29fdG90YWwgICAgICA9IGluZ3Jlc29fbGFib3JhbCArIGluZ3Jlc29fbm9fbGFib3JhbCwKICAgICAgICAgICBDSDA0ICAgICAgICAgICAgICAgPSBjYXNlX3doZW4oQ0gwNCA9PSAxIH4gIlZhcm9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQ0gwNCA9PSAyIH4gIk11amVyIiksCiAgICAgICAgICAgRURBRCA9IGNhc2Vfd2hlbihDSDA2ICVpbiUgYygxODozMCkgfiAiMTggYSAzMCIsICAgICAgIzw8CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBDSDA2ICVpbiUgYygzMTo0NSkgfiIzMSBhIDQ1IiwgICAgICAgIzw8CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBDSDA2ICVpbiUgYyg0Njo2MCkgfiAiNDYgYSA2MCIpKSAlPiUgIzw8CiAgZ3JvdXBfYnkoQ0gwNCxFREFEKSAlPiUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIzw8CiAgc3VtbWFyaXNlKCdpbmdyZXNvIGxhYm9yYWwnICAgID0gc3VtKGluZ3Jlc29fbGFib3JhbCpQT05ESUkpL3N1bShpbmdyZXNvX3RvdGFsKlBPTkRJSSksCiAgICAgICAgICAgICdpbmdyZXNvIG5vIGxhYm9yYWwnID0gc3VtKGluZ3Jlc29fbm9fbGFib3JhbCpQT05ESUkpL3N1bShpbmdyZXNvX3RvdGFsKlBPTkRJSSkpICU+JQpnYXRoZXIodGlwb19pbmdyZXNvLCBtb250bywzOjQpIAoKZGF0YWdyYWZfMwpgYGAKCmBgYHtyfQoKCmdncGxvdChkYXRhZ3JhZl8zLCBhZXMoQ0gwNCwgbW9udG8sIGZpbGwgPSB0aXBvX2luZ3Jlc28sIAogICAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSBzcHJpbnRmKCIlMS4xZiUlIiwgMTAwKm1vbnRvKSkpKwogIGdlb21fY29sKHBvc2l0aW9uID0gInN0YWNrIiwgYWxwaGE9MC42KSArIAogIGdlb21fdGV4dChwb3NpdGlvbiA9IHBvc2l0aW9uX3N0YWNrKHZqdXN0ID0gMC41KSwgc2l6ZT0zKSsKICBsYWJzKHg9IiIseT0iUG9yY2VudGFqZSIpKwogIHRoZW1lX3R1ZnRlKCkrCiAgc2NhbGVfeV9jb250aW51b3VzKCkrCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICAgICAgbGVnZW5kLnRpdGxlPWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT0yNSkpKwogIGZhY2V0X3dyYXAofkVEQUQpCgpgYGAKCkVuIGxvcyBncsOhZmljb3MgdXRpbGl6YW1vcyBleHRlbnNpb25lcyBkZSBnZ3Bsb3Q6IAoKLSBnZ3JlcGVsIGBgYGdlb21fdGV4dF9yZXBlbCgpYGBgCi0gZ2d0aGVtZXMgYGBgdGhlbWVfdHVmdGUoKWBgYAoKc2ltcGxlbWVudGUgZGViZW1vcyByZWNvcmRhciBjYXJnYXIgbGFzIGxpYnJlcsOtYXMgc2kgcXVlcmVtb3MgdXRpbGl6YXIgZXNhcyBmdW5jaW9uZXMuCgojIyBPdHJvcyBncsOhZmljb3MKCkxvcyBncsOhZmljb3MgaGFzdGEgYXF1w60gcmVhbGl6YWRvcyAoYmFycmFzLCBsw61uZWEpIHNvbiBmw6FjaWxtZW50ZSByZXByb2R1Y2libGVzIGVuIHVuIGV4Y2VsIHlhIHF1ZSB1dGlsaXphbiBsYSBpbmZvcm1hY2nDs24gYWdyZWdhZGEuIFNpbiBlbWJhcmdvLCBsYSBncmFuIHZlbnRhamEgZGVsICoqUioqIHNlIG1hbmlmaWVzdGEgYSBhIGxhIGhvcmEgZGUgcmVhbGl6YXI6CgotIEdyw6FmaWNvcyBxdWUgbmVjZXNpdGFuIGxhIGluZm9ybWFjacOzbiBhIG5pdmVsIGRlIG1pY3JvZGF0b3MuIF9fcHVudG9zX18sICBfX2JveHBsb3RzX18sIF9fS2VybmVsc19fLCBldGMuCi0gQWJyaXIgdW4gbWlzbW8gZ3LDoWZpY28gc2Vnw7puIGFsZ3VuYSB2YXJpYWJsZSBkaXNjcmV0YTogYGBgZmFjZXRfd3JhcCgpYGBgCi0gUGFyYW1ldHJpemFyIG90cmFzIHZhcmlhYmxlcywgcGFyYSBhdW1lbnRhciBsYSBkaW1lbnNpb25hbGlkYWQgZGVsIGdyw6FmaWNvcy4KICAgIC0gW19fY29sb3JfX10oaHR0cDovL3d3dy5zdGF0LmNvbHVtYmlhLmVkdS9+dHpoZW5nL2ZpbGVzL1Jjb2xvci5wZGYpIGBgYGNvbG9yID0gYGBgCiAgICAtIF9fcmVsbGVub19fYGBgZmlsbCA9IGBgYAogICAgLSBfX2Zvcm1hX18gYGBgc2hhcGUgPSBgYGAKICAgIC0gX190YW1hw7FvX18gYGBgc2l6ZSA9IGBgYAogICAgLSBfX3RyYW5zcGFyZW5jaWFfXyBgYGBhbHBoYSA9IGBgYAoKRXN0byBwZXJtaXRlIHRlbmVyLCBlbiBlbCBwbGFubywgZ3LDoWZpY29zIGRlIG11Y2hhcyBkaW1lbnNpb25lcyBkZSBhbsOhbGlzaXMKCi0gU2kgZWwgY29sb3IgcmVwcmVzZW50YSB1bmEgdmFyaWFibGUgbG8gZGVmaW5pbW9zIF9fZGVudHJvIGRlbCBhZXMoKV9fLCBgYGBhZXMoLi4uIGNvbG9yID0gaW5ncmVzb3MpYGBgCi0gQ3VhbmRvIHF1ZXJlbW9zIHNpbXBsZW1lbnRlIG1lam9yYXIgZWwgZGlzZcOxbyAoZXMgZmlqbyksIHNlIGFzaWduYSBwb3IgZnVlcmEsIG8gZGVudHJvIGRlIGNhZGEgdGlwbyBkZSBncsOhZmljb3MsIGBgYGdlb21fY29sKGNvbG9yID0gJ2dyZWVuJylgYGAuCgojIyMgW19fQm94cGxvdHNfX10oaHR0cHM6Ly9mbG93aW5nZGF0YS5jb20vMjAwOC8wMi8xNS9ob3ctdG8tcmVhZC1hbmQtdXNlLWEtYm94LWFuZC13aGlza2VyLXBsb3QvKSAKCi0gTG9zIGdyw6FmaWNvcyBCb3hwbG90IHJlcHJlc2VudGFuIHVuYSDDum5pY2EgdmFyaWFibGUgKHVuaXZhcmlhZG9zKS4KLSBFc3TDoW4gY29tcHVlc3RvcyBwb3IgdW5hIGNhamEsIGN1eW8gbMOtbWl0ZSBpbmZlcmlvciBlcyBlbCB2YWxvciBkb25kZSBzZSBhbGNhbnphIGVsIDI1JSBkZSBsYSBkaXN0cmlidWNpw7NuCi0gU3UgbMOtbWl0ZSBzdXBlcmlvciBlcyBlbCB2YWxvciBkb25kZSBzZSBhbGNhbnphIGVsIDc1JSBkZSBsYSBtaXNtYS4KLSBBIHN1IHZleiwgdGFtYmnDqW4gZWwgZ3LDoWZpY28gbWFyY2EgbG9zIHZhbG9yZXMgIm91dGxpZXJzIiAoZGF0b3MgcXVlIHNlIGVuY3VlbnRyYW4gYSB1bmEgZGlzdGFuY2lhIGRlIGFsIG1lbm9zIDEsNSB2ZWNlcyBlbCB0YW1hw7FvIGRlIGxhIGNhamEgZGVsIGzDrW1pdGUgaW5mZXJpb3IgbyBzdXBlcmlvciBkZSBsYSBjYWphLCBzZWfDum4gY29ycmVzcG9uZGEpCgojIyMjIEJveHBsb3QgZGUgaW5ncmVzb3MgZGUgbGEgb2N1cGFjacOzbiBwcmluY2lwYWwsIHNlZ8O6biBuaXZlbCBlZHVjYXRpdm8KCkhhY2Vtb3MgdW4gcHJvY2VzYW1pZW50byBzaW1wbGU6IFNhY2Ftb3MgbG9zIGluZ3Jlc29zIGlndWFsZXMgYSBjZXJvIHkgbGFzIG5vIHJlc3B1ZXN0YXMgZGUgbml2ZWwgZWR1Y2F0aXZvLiAgICAKRXMgaW1wb3J0YW50ZSBxdWUgbGFzIHZhcmlhYmxlcyBzZWFuIGRlbCB0aXBvIHF1ZSBjb25jZXB0dWFsbWVudGUgbGVzIGNvcnJlc3BvbmRlIChlbCBuaXZlbCBlZHVjYXRpdm8gZXMgdW5hIHZhcmlhYmxlIGNhdGVnw7NyaWNhLCBubyBjb250aW51YSksIHBhcmEgcXVlIGVsIGdncGxvdCBwdWVkYSBncmFmaWNhcmxvIGNvcnJlY3RhbWVudGUuIAoKYGBge3J9CiMgTGFzIHZhcmlhYmxlcyBzZXhvKCBDSDA0ICkgeSBOaXZlbCBlZHVjYXRpdm8gZXN0w6FuIGNvZGlmaWNhZGFzIGNvbW8gbsO6bWVyb3MsIHkgZWwgUiBsYXMgZW50aWVuZGUgY29tbyBudW3DqXJpY2FzLgpjbGFzcyhJbmRpdmlkdWFsX3QxMTckTklWRUxfRUQpCmNsYXNzKEluZGl2aWR1YWxfdDExNyRDSDA0KQoKZ2dkYXRhIDwtIEluZGl2aWR1YWxfdDExNyAlPiUgCiAgZmlsdGVyKFAyMT4wLCAhaXMubmEoTklWRUxfRUQpKSAlPiUgCiAgbXV0YXRlKE5JVkVMX0VEID0gYXMuZmFjdG9yKE5JVkVMX0VEKSwKICAgICAgICAgQ0gwNCAgICAgPSBhcy5mYWN0b3IoQ0gwNCkpCmBgYAoKYGBge3J9CgpnZ3Bsb3QoZ2dkYXRhLCBhZXMoeCA9IE5JVkVMX0VELCB5ID0gUDIxKSkgKwogIGdlb21fYm94cGxvdCgpKwogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKDAsIDQwMDAwKSkjUmVzdHJpbmpvIGVsIGdyw6FmaWNvIGhhc3RhIGluZ3Jlc29zIGRlICQ0MDAwMApgYGAKClNpIHF1ZXJlbW9zIGFncmVnYXIgbGEgZGltZW5zacOzbiBfc2V4b18sIHBvZGVtb3MgaGFjZXIgdW4gYGBgZmFjZXRfd3JhcCgpYGBgCgpgYGB7cn0KCmdncGxvdChnZ2RhdGEsIGFlcyh4PSBOSVZFTF9FRCwgeSA9IFAyMSwgZ3JvdXAgPSBOSVZFTF9FRCwgZmlsbCA9IE5JVkVMX0VEICkpICsKICBnZW9tX2JveHBsb3QoKSsKICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYygwLCA0MDAwMCkpKwogIGZhY2V0X3dyYXAofiBDSDA0LCBsYWJlbGxlciA9ICJsYWJlbF9ib3RoIikKYGBgCgpQb3IgbGEgZm9ybWEgZW4gcXVlIGVzdMOhIHByZXNlbnRhZG8gZWwgZ3LDoWZpY28sIGVsIGZvY28gZGUgYXRlbmNpw7NuIHNpZ3VlIHB1ZXN0byBlbiBsYXMgZGlmZXJlbmNpYXMgZGUgaW5ncmVzb3MgZW50cmUgbml2ZWxlcyBlZHVjYXRpdm8uIFNpbXBsZW1lbnRlIHNlIGFncmVnYSB1biBjb3J0ZSBwb3IgbGEgdmFyaWFibGUgZGUgc2V4by4KClNpIGxvIHF1ZSBxdWVyZW1vcyBoYWNlciBlcyBwb25lciBlbCBmb2NvIGRlIGF0ZW5jacOzbiBlbiBsYXMgZGlmZXJlbmNpYXMgcG9yIHNleG8sIHNpbXBsZW1lbnRlIGJhc3RhIGNvbiBpbnZlcnRpciBsYSB2YXJpYWJsZSB4IGVzcGVjaWZpY2FkYSBjb24gbGEgdmFyaWFibGUgdXRpbGl6YWRhIGVuIGVsIGBgYGZhY2V0X3dyYXBgYGAKCgpgYGB7cn0KZ2dwbG90KGdnZGF0YSwgYWVzKHg9IENIMDQsIHkgPSBQMjEsIGdyb3VwID0gQ0gwNCwgZmlsbCA9IENIMDQgKSkgKwogIGdlb21fYm94cGxvdCgpKwogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKDAsIDQwMDAwKSkrCiAgZmFjZXRfZ3JpZCh+IE5JVkVMX0VELCBsYWJlbGxlciA9ICJsYWJlbF9ib3RoIikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCmBgYAoKIyMjIFtIaXN0b2dyYW1hc10oaHR0cHM6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL2dlb21faGlzdG9ncmFtLmh0bWwpCgpPdHJhIGZvcm1hIGRlIG1vc3RyYXIgbGEgZGlzdHJpYnVjacOzbiBkZSB1bmEgdmFyaWFibGUgZXMgdXRpbGl6YXIgdW4gaGlzdG9ncmFtYS4gRXN0ZSB0aXBvIGRlIGdyw6FmaWNvcyBhZ3J1cGEgbGFzIG9ic2VydmFjaW9uZXMgZW4gX19iaW5zX186IGludGVydmFsb3MgZGVudHJvIGRlbCByYW5nbyBkZSBsYSB2YXJpYWJsZS4gTHVlZ28gY3VlbnRhIGxhIGNhbnRpZGFkIGRlIG9ic2VydmFjaW9uZXMgcXVlIGNhZW4gZGVudHJvIGRlIGNhZGEgdW5vIGRlIGVzdG9zIGJpbnMuCgpQb3IgZWplbXBsbywgc2kgb2JzZXJ2YW1vcyBlbCBpbmdyZXNvIGRlIGxhIG9jdXBhY2nDs24gcHJpbmNpcGFsOgoKYGBge3Igd2FybmluZz1GQUxTRX0KaGlzdF9kYXRhIDwtSW5kaXZpZHVhbF90MTE3ICU+JQogIGZpbHRlcihQMjE+MCkgCgpnZ3Bsb3QoaGlzdF9kYXRhLCBhZXMoeCA9IFAyMSx3ZWlnaHRzID0gUE9ORElJTykpKyAKZ2VvbV9oaXN0b2dyYW0oKSsKc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cyA9IGMoMCw1MDAwMCkpCmBgYAoKRW4gZXN0ZSBncsOhZmljbywgbG9zIHBvc2libGVzIHZhbG9yZXMgZGUgcDIxIHNlIGRpdmlkZW4gZW4gMzAgX19iaW5zX18gY29uc2VjdXRpdm9zIHkgZWwgZ3LDoWZpY28gbXVlc3RyYSBjdWFudGFzIG9ic2VydmFjaW9uZXMgY2FlbiBlbiBjYWRhIHVubyBkZSBlbGxvcwoKCiMjIyBbS2VybmVsc10oaHR0cHM6Ly9wbG90Lmx5L2dncGxvdDIvZ2VvbV9kZW5zaXR5LykKCkxhIGZ1bmNpw7NuICBgYGBnZW9tX2RlbnNpdHkoKWBgYCBub3MgcGVybWl0ZSBjb25zdHJ1aXIgX19rZXJuZWxzX18gZGUgbGEgZGlzdHJpYnVjacOzbi4gRXN0byBlcywgdW4gc3Vhdml6YWRvIHNvYnJlIGxvcyBoaXN0b2dyYW1hcyBxdWUgc2UgYmFzYSBlbiBhbGd1bmEgZGlzdHJpYnVjacOzbiBzdXB1ZXN0YSBkZW50cm8gZGUgY2FkYSBiaW4uIEVzIHBhcnRpY3VsYXJtZW50ZSDDunRpbCBjdWFuZG8gdGVuZW1vcyB1bmEgdmFyaWFibGUgY29udGludWEsIGRhZG8gcXVlIGxvcyBoaXN0b2dyYW1hcyByb21wZW4gZXNhIHNlbnNhY2nDs24gZGUgY29udGludWlkYWQuCgoKVmVhbW9zIHVuIGVqZW1wbG8gc2VuY2lsbG8gY29uIGxvcyBpbmdyZXNvcyBkZSBsYSBvY3VwYWNpw7NuIHByaW5jaXBhbC4gTHVlZ28gaXJlbW9zIGNvbXBsZWppemFuZG9sbyAKCgpgYGB7ciB3YXJuaW5nPUZBTFNFfQprZXJuZWxfZGF0YSA8LUluZGl2aWR1YWxfdDExNyAlPiUKICBmaWx0ZXIoUDIxPjApIAoKZ2dwbG90KGtlcm5lbF9kYXRhLCBhZXMoeCA9IFAyMSx3ZWlnaHRzID0gUE9ORElJTykpKyAKZ2VvbV9kZW5zaXR5KCkrCnNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKDAsNTAwMDApKQpgYGAKKipFbCBlamUgeSBubyB0aWVuZSBkZW1hc2lhZGEgaW50ZXJwcmV0YWJpbGlkYWQgZW4gbG9zIEtlcm5lbCwgcG9ycXVlIGhhY2UgYSBsYSBmb3JtYSBlbiBxdWUgc2UgY29uc3RydXllbiBsYXMgZGlzdHJpYnVjaW9uZXMqKi4gCgpFbCBwYXJhbWV0cm8gYWRqdXN0LCBkZW50cm8gZGUgbGEgZnVuY2nDs24gYGBgZ2VvbV9kZW5zaXR5YGBgbm9zIHBlcm1pdGUgcmVkdWNpciBvIGFtcGxpYXIgZWwgcmFuZ28gZGUgc3Vhdml6YWRvIGRlIGxhIGRpc3RyaWJ1Y2nDs24uIFN1IHZhbG9yIHBvciBkZWZhdWx0IGVzIDEuIFZlYW1vcyBxdWUgc3VjZWRlIHNpIGxvIHNldGVhbW9zIGVuIDIgCmBgYHtyIHdhcm5pbmc9RkFMU0V9CmdncGxvdChrZXJuZWxfZGF0YSwgYWVzKHggPSBQMjEsd2VpZ2h0cyA9IFBPTkRJSU8pKSsgCmdlb21fZGVuc2l0eShhZGp1c3QgPSAyKSsKc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cyA9IGMoMCw1MDAwMCkpCgpgYGAKCkNvbW8gZXMgZXNwZXJhYmxlLCBsYSBkaXN0cmlidWNpw7NuIGRlbCBpbmdyZXNvIHRpZW5lICJwaWNvcyIgZW4gbG9zIHZhbG9yZXMgcmVkb25kb3MsIHlhIHF1ZSBsYSBnZW50ZSBzdWVsZSBkZWNsYXJhciB1biB2YWxvciBhcHJveGltYWRvIGFsIGluZ3Jlc28gZWZlY3Rpdm8gcXVlIHBlcmNpYmUuIE5hZGllIGRlY2xhcmEgaW5ncmVzb3MgZGUgMzAwMDEuIEFsIHN1YXZpemFyIGxhIHNlcmllIGNvbiB1biBrZXJuZWwsIGVsaW1pbmFtb3MgZXNlIGVmZWN0by5TaSBzZXRlYW1vcyBlbCByYW5nbyBwYXJhIGVsIHN1YXZpemFkbyBlbiB2YWxvcmVzIG1lbm9yZXMgYSAxLCBwb2RlbW9zIG9ic2VydmFyIGVzdG9zIHBpY29zLgoKYGBge3Igd2FybmluZz1GQUxTRX0KZ2dwbG90KGtlcm5lbF9kYXRhLCBhZXMoeCA9IFAyMSx3ZWlnaHRzID0gUE9ORElJTykpKyAKZ2VvbV9kZW5zaXR5KGFkanVzdCA9IDAuMDEpKwpzY2FsZV94X2NvbnRpbnVvdXMobGltaXRzID0gYygwLDUwMDAwKSkKYGBgCgpBaG9yYSBiaWVuLCBjb21vIGVuIHRvZG8gZ3JhZmljbyBkZSBSLCBwb2RlbW9zIHNlZ3VpciBhZ3JlZ2FuZG8gZGltZW5zaW9uZXMgcGFyYSBlbnJpcXVlY2VyIGVsIGFuw6FsaXNpcy4gCmBgYHtyIHdhcm5pbmc9RkFMU0V9Cmtlcm5lbF9kYXRhXzIgPC0ga2VybmVsX2RhdGEgJT4lIAogIG11dGF0ZShDSDA0PSBjYXNlX3doZW4oQ0gwNCA9PSAxIH4gIlZhcm9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgIENIMDQgPT0gMiB+ICJNdWplciIpKQogIApnZ3Bsb3Qoa2VybmVsX2RhdGFfMiwgYWVzKHggPSBQMjEsCiAgd2VpZ2h0cyA9IFBPTkRJSU8sCiAgZ3JvdXAgPSBDSDA0LAogIGZpbGwgPSBDSDA0KSkgKwogIGdlb21fZGVuc2l0eShhbHBoYT0wLjcsYWRqdXN0ID0yKSsKICBsYWJzKHg9IkRpc3RyaWJ1Y2nDs24gZGVsIGluZ3Jlc28iLCB5PSIiLAogICAgICAgdGl0bGU9IiBUb3RhbCBzZWfDum4gdGlwbyBkZSBpbmdyZXNvIHkgZ8OpbmVybyIsIAogICAgICAgY2FwdGlvbiA9ICJGdWVudGU6IEVuY3Vlc3RhIFBlcm1hbmVudGUgZGUgSG9nYXJlcyIpKwogIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKDAsNTAwMDApKSsKICB0aGVtZV90dWZ0ZSgpKwogIHNjYWxlX2ZpbGxfZ2RvY3MoKSsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwKICAgICAgICBwbG90LnRpdGxlICAgICAgPSBlbGVtZW50X3RleHQoc2l6ZT0xMikpCgpnZ3NhdmUoZmlsZW5hbWUgPSAiLi4vUmVzdWx0YWRvcy9LZXJuZWxfMS5wbmciLHNjYWxlID0gMikKCmBgYAoKUG9kZW1vcyBhZ3JlZ2FyIGHDum4gbGEgZGltZW5zacOzbiBkZSBpbmdyZXNvIGxhYm9yYWwgcmVzcGVjdG8gZGVsIG5vIGxhYm9yYWwKCmBgYHtyIHdhcm5pbmc9RkFMU0V9CgprZXJuZWxfZGF0YV8zIDwta2VybmVsX2RhdGFfMiAlPiUgCiAgc2VsZWN0KFJFR0lPTixQNDdULFRfVkksIFRPVF9QMTIsIFAyMSAsIFBPTkRJSSwgQ0gwNCkgJT4lIAogIGZpbHRlcighaXMubmEoUDQ3VCksIFA0N1QgPiAwICkgJT4lIAogIG11dGF0ZShpbmdyZXNvX2xhYm9yYWwgICAgPSBUT1RfUDEyICsgUDIxLAogICAgICAgICBpbmdyZXNvX25vX2xhYm9yYWwgPSBUX1ZJKSAlPiUKICBnYXRoZXIoLiwga2V5ID0gVGlwb19pbmdyZXNvLCBJbmdyZXNvLCBjKChuY29sKC4pLTEpOm5jb2woLikpKSAlPiUKICBmaWx0ZXIoIEluZ3Jlc28gIT0wKSMgUGFyYSBlc3RlIGdyw6FmaWNvLCBxdWllcm8gZWxpbWluYXIgbG9zIGluZ3Jlc29zID0gMAoKa2VybmVsX2RhdGFfMwpgYGAKCgpgYGB7ciB3YXJuaW5nPUZBTFNFfQogIGdncGxvdChrZXJuZWxfZGF0YV8zLCBhZXMoCiAgeCA9IEluZ3Jlc28sCiAgd2VpZ2h0cyA9IFBPTkRJSSwKICBncm91cCA9IFRpcG9faW5ncmVzbywKICBmaWxsID0gVGlwb19pbmdyZXNvKSkgKwogIGdlb21fZGVuc2l0eShhbHBoYT0wLjcsYWRqdXN0ID0yKSsKICBsYWJzKHg9IkRpc3RyaWJ1Y2nDs24gZGVsIGluZ3Jlc28iLCB5PSIiLAogICAgICAgdGl0bGU9IiBUb3RhbCBzZWfDum4gdGlwbyBkZSBpbmdyZXNvIHkgZ8OpbmVybyIsIAogICAgICAgY2FwdGlvbiA9ICJGdWVudGU6IEVuY3Vlc3RhIFBlcm1hbmVudGUgZGUgSG9nYXJlcyIpKwogIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKDAsNTAwMDApKSsKICB0aGVtZV90dWZ0ZSgpKwogIHNjYWxlX2ZpbGxfZ2RvY3MoKSsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwKICAgICAgICBwbG90LnRpdGxlICAgICAgPSBlbGVtZW50X3RleHQoc2l6ZT0xMikpKwogIGZhY2V0X3dyYXAofiBDSDA0LCBzY2FsZXMgPSAiZnJlZSIpCgpnZ3NhdmUoZmlsZW5hbWUgPSAiLi4vUmVzdWx0YWRvcy9LZXJuZWxfMS5wbmciLHNjYWxlID0gMikKCmBgYAoKRW4gZXN0ZSB0aXBvIGRlIGdyw6FmaWNvcywgaW1wb3J0YSBtdWNobyBxdcOpIHZhcmlhYmxlIHNlIHV0aWxpemEgcGFyYSBfZmFjZXRlYXJfIHkgcXXDqSB2YXJpYWJsZSBwYXJhIGFncnVwYXIsIHlhIHF1ZSBsYSBjb25zdHJ1Y2Npw7NuIGRlIGxhIGRpc3RyaWJ1Y2nDs24gZXMgZGlmZXJlbnRlLiAKCmBgYHtyLHdhcm5pbmc9RkFMU0V9CmdncGxvdChrZXJuZWxfZGF0YV8zLCBhZXMoCiAgeCA9IEluZ3Jlc28sCiAgd2VpZ2h0cyA9IFBPTkRJSSwKICBncm91cCA9IENIMDQsCiAgZmlsbCA9IENIMDQpKSArCiAgZ2VvbV9kZW5zaXR5KGFscGhhPTAuNyxhZGp1c3QgPTIpKwogIGxhYnMoeD0iRGlzdHJpYnVjacOzbiBkZWwgaW5ncmVzbyIsIHk9IiIsCiAgICAgICB0aXRsZT0iIFRvdGFsIHNlZ8O6biB0aXBvIGRlIGluZ3Jlc28geSBnw6luZXJvIiwgCiAgICAgICBjYXB0aW9uID0gIkZ1ZW50ZTogRW5jdWVzdGEgUGVybWFuZW50ZSBkZSBIb2dhcmVzIikrCiAgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cyA9IGMoMCw1MDAwMCkpKwogIHRoZW1lX3R1ZnRlKCkrCiAgc2NhbGVfZmlsbF9nZG9jcygpKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLAogICAgICAgIHBsb3QudGl0bGUgICAgICA9IGVsZW1lbnRfdGV4dChzaXplPTEyKSkrCiAgZmFjZXRfd3JhcCh+VGlwb19pbmdyZXNvLCBzY2FsZXMgPSAiZnJlZSIpCgpnZ3NhdmUoZmlsZW5hbWUgPSAiLi4vUmVzdWx0YWRvcy9LZXJuZWxfMS5wbmciLHNjYWxlID0gMikKCmBgYAoKIyBFamVyY2ljaW9zIHBhcmEgcHJhY3RpY2FyCiogQ2FsY3VsYXIgZWwgcHJvbWVkaW8gZGVsIGluZ3Jlc28gcG9yIG9jdXBhY2nDs24gcHJpbmNpcGFsIChWYXJpYWJsZSAqKlAyMSoqKSAgcGFyYSAgKiphc2FsYXJpYWRvcyoqIGNvbiB5IHNpbiAqKmRlc2N1ZW50byBqdWJpbGF0b3JpbyoqIChWYXJpYWJsZSAqKlBQMDdIKiopLiBMdWVnbyByZWFsaXphciB1biBncsOhZmljbyBkZSBiYXJyYXMgZG9uZGUgc2UgY29tcGFyZW4gYW1ib3MgdmFsb3JlcyAocGFyYSBlbCAxZXIgdHJpbWVzdHJlIGRlIDIwMTcpLiAgICAgICAgICAgICAgICAgICAKICBQaXN0YXM6IFNlIGRlYmVuIGZpbHRyYXIgcHJldmlhbWVudGUgbG9zIGluZ3Jlc29zIG1heW9yZXMgYSAwICgqKlAyMT4wKiopLkNoZXF1ZWFyIHF1ZSBwb25kZXJhZG9yIGNvcnJlc3BvbmRlIHV0aWxpemFyICAgICAgICAgICAKICAgICAgICAgICAgICAgCi0gR3JhZmljYXIgbGEgZGlzdHJpYnVjacOzbiBkZWwgaW5ncmVzbyBwb3Igb2N1cGFjacOzbiBwcmluY2lwYWwgcGFyYSBBc2FsYXJpYWRvcywgQ3VlbnRhcHJvcGlzdGFzIHkgUGF0cm9uZXMsIGNvbiBlbCB0aXBvIGRlIGdyw6FmaWNvIEtlcm5lbCAgICAgICAgICAgICAgICAgCiAgIFBpc3RhOiBVc2FyIGxhIGZ1bmNpw7NuICoqZmFjZXRfd3JhcCoqIHBhcmEgc2VwYXJhciBhIGNhZGEgdW5hIGRlIGxhcyBjYXRlZ29yw61hcyBvY3VwYWNpb25hbGVzKSAgICAgICAgICAgICAgICAKICAgU3VnZXJlbmNpYTogaW5jb3Jwb3JhciBsYSBsw61uZWEgYGBgIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKDAsNTAwMDApKSBgYGAgZW50cmUgbGFzIGNhcGFzIGRlbCBncsOhZmljby4gwr9RdcOpIGNhbWJpw7M/CgoKCiMgRWplcmNpY2lvcyBkZSB0YXJlYQoKLSBIYWNlciB1biBncsOhZmljbyBib3hwbG90IGRlIGxhIGRpc3RyaWJ1Y2nDs24gZGUgZWRhZGVzIGRlIGxvcyBhc2FsYXJpYWRvcyBjb24gZGVzY3VlbnRvIGp1YmlsYXRvcmlvLCB5IGRlIGxvcyBhc2FsYXJpYWRvcyBzaW4gZGVzY3VlbnRvIGp1YmlsYXRvcmlvLgoKLSBVbmllbmRvIGxhcyBiYXNlcyBkZSBsb3MgZGlzdGludG9zIHRyaW1lc3RyZXMsIGNhbGN1bGFyIGVsIHByb2NlbnRhamUgZGUgYXNhbGFyaWFkb3Mgc2luIGRlc2N1ZW50byBqdWJpbGF0b3JpbyBjb21vICRcZnJhY3tBc2FsLiBzLyBkZXNjIGp1YmlsfXtBc2FsLiBjLyBkZXNjIGp1YmlsKyBBc2FsLnMvIGRlc2MganViaWx9JC4gTHVlZ28gcmVhbGl6YXIgdW4gZ3LDoWZpY28gZGUgbGluZWEgY29uIGxhIGV2b2x1Y2nDs24gZGUgZXN0ZSBpbmRpY2Fkb3IKCgoK