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 R

library(tidyverse)
library(openxlsx)
library(ggthemes)

Loops

Un loop es una estructura de código que nos permite aplicar iterativamente un mismo conjunto de comandos, variando algún elemento sobre el cual se trabajará. Por ejemplo:

for(i in 1:10){
   print(i^2)
}
[1] 1
[1] 4
[1] 9
[1] 16
[1] 25
[1] 36
[1] 49
[1] 64
[1] 81
[1] 100

Esto se lee como : “Recorre cada uno de los valores (i) del vector numérico 1 a 10, y para cada uno de ellos imprimí el cuadrado (i^2)”.
Uno puede especificar la palabra que desee que tomé cada uno de los valores que debe tomar. En el ejemplo anterior fue i, pero bien podría ser la “Valores

for(Valores in 1:10){
   print(Valores^2)
  
}
[1] 1
[1] 4
[1] 9
[1] 16
[1] 25
[1] 36
[1] 49
[1] 64
[1] 81
[1] 100

A su vez, los loops pueden iterar sobre los distintos valores de una variable en un Dataframe. A continuación realizaremos un ejemplo iterando sobre la variable REGION en la base de EPH, para conservar un dataframe por cada Region. Previamente unimos la base con el diccionario de Nombres de Regiones y Códigos.

Individual_t117 <- read.table("../Fuentes/usu_individual_t117.txt",
                              sep=";", dec=",", header = TRUE, fill = TRUE)
Regiones <- read.xlsx("../Fuentes/Regiones.xlsx")
Aglomerados <- read.xlsx("../Fuentes/Aglomerados EPH.xlsx")
Base<- Individual_t117 %>% 
  left_join(Regiones) %>% 
  left_join(Aglomerados)

Para iterar sobre la variable región, no necesitamos recorrer todos los valores de la misma, sino por los valores únicos. Es decir, que vamos a querer realizar un loop que realizará un mismo procedimiento 6 veces. Para ello, utilizaremos la función unique que nos permite obtener un vector con los únicos valores que toma determinada variable.

unique(Base$Region)
[1] "Pampeana"  "Noreste"   "Patagonia" "Cuyo"      "Noroeste"  "GBA"      
for(variable_itera in unique(Base$Region)){
print(variable_itera)# No es necesario, permite ver por que Región estoy trabajando
  
temp <- Base %>% # Aquí filtro la base cuando Region toma el valor de "variable_itera"
  filter(Region == variable_itera)
##Temp es un dataframe que será "pisado" en cada iteración.
#La función assign me permite definir a un objeto utilizando como primer argumento el nombre deseado 
assign(variable_itera,temp)
    }
[1] "Pampeana"
[1] "Noreste"
[1] "Patagonia"
[1] "Cuyo"
[1] "Noroeste"
[1] "GBA"

Estructuras Condicionales

Las estructuras condiconales nos permites ejecutar determinado código, o determinada función sobre una variable, acorde al cumplimiento o no de determinada condición lógica.
## if La función if()nos permite diseñar un segmento de código que solo sera ejecutado en caso de que se cumpla determinada condición. Su funcionamiento es el siguiente:
if(condicion){codigo a ejecutar si se cumple la condición}

if( 2+2 == 4){
  print("Menos Mal")
}
[1] "Menos Mal"
if( 2+2 == 148.24){
  print("R, la estas pifiando")
}

ifelse

La función if_else() sirve para crear o modificar dicotómicamente un objeto/variable/vector a partir del cumplimiento de una o más condiciones lógicas.
Su funcionamiento es el siguiente:
if_else(condicion,función a aplicar si se cumple la condición,función a aplicar si no se cumple la condición)

resultado <- if_else(2+2==4, true = "Joya",false = "Error") #Versión dplyr
ABC_123 <- data.frame(Letras = LETTERS[1:20],Num = 1:20)
ABC_123 %>% 
  mutate(Mayor_o_Menor = ifelse(Num<=5,"Menor o igual que 5","Mayor que 5")) #Versión base

Ejercicio práctico combinando Loops y estructuras condicionales

A continuación haremos un ejercicio para mostrar la aplicación conjunta que podemos dar a los loops y a las estructuras de código condicional. Paticularmente estos son muy útiles a la hora de realizar multiples gráficos.
El ejercicio consistirá en realizar, para cada aglomerado, un gráfico de la distribución por género al interior de cada categoría ocupacional. Sin embargo, utilizaremos la variable MAS_500 para replicar el procedimiento únicamente para los aglomerados de mas de 500.000 habitantes

#Antes de comenzar el Loop, recodifico las variables 
Base_para_loop <- Base %>% 
  mutate(CH04 = case_when(CH04 == 1 ~ "Varon",
                          CH04 == 2 ~ "Mujer"),
         CAT_OCUP = case_when(CAT_OCUP == 1  ~ "Patron",
                              CAT_OCUP == 2  ~ "Cuenta Propia",
                              CAT_OCUP == 3  ~ "Asalariados",
                              CAT_OCUP == 4  ~ "TFSR"))
for(aglom_itera in unique(Base_para_loop$AGLOMERADO)){
  
#Filtro la base restringiendola a un aglomerado
  Base_itera_xaglom <- Base_para_loop %>%
    filter(AGLOMERADO == aglom_itera)
#Exijo los procedimientos a continuación sólo se realicen para aglomerados con más de 500.000 habitantes
  if(unique(Base_itera_xaglom$MAS_500)=="S"){
    
Base_grafico  <- Base_itera_xaglom %>% 
      filter(CAT_OCUP != 0) %>% 
      group_by(Nom_Aglo,CAT_OCUP,CH04) %>% 
      summarise(Cantidad = sum(PONDERA,na.rm = TRUE)) %>% 
      group_by(CAT_OCUP) %>%     
  mutate(Porcentaje = Cantidad/sum(Cantidad))
    
Grafico <- ggplot(Base_grafico, aes(CAT_OCUP, Porcentaje, fill = CH04, 
                      label = sprintf("%1.1f%%", 100*Porcentaje)))+
  geom_col(position = "stack") + 
  geom_text(position = position_stack(vjust = 0.5), size=3)+
  labs(x="",y="Porcentaje",
       title = unique(Base_grafico$Nom_Aglo))+
  theme_minimal()+
  scale_y_continuous()+
  scale_fill_manual(values = c("#009A44", "#FF6600"))+
  theme(legend.position = "bottom",
        legend.title=element_blank(),
        axis.text.x = element_text(angle=25))    
    
Grafico 
ggsave(paste0("../Resultados/Aglomerado ",aglom_itera,".PNG"))
   }
  }
Saving 7 x 7 in image

A continuación se muestra el último de los gráficos relizados por el loop. En la carpeta de Resultados del curso, podran observarse cada uno de los gráficos correspondientes a los algomerados de más de 500.000 habitantes.

Funciones del Usuario

La creación de funciones propias nos permite automatizar todas aquellas partes del código que se repiten mucho. Una vez diseñadas, funcionan igual que cualquier comando. La facilidad para crear las funciones es en buena medida la explicación de que haya tantas contribuciones de usuarios a la expansión del lenguaje.

Por ejemplo, podríamos diseñar una función que agregue una flecha a dos objetos de texto

funcion_prueba <- function(parametro1,parametro2) {
  paste(parametro1, parametro2, sep = " <--> ")
}
funcion_prueba(parametro1 = "A ver", parametro2 = "Que pasa")
[1] "A ver <--> Que pasa"

También podemos asignar un valor por default para los parametros en caso de que el usuario no defina su valor al utilizar la función.

Otra_funcion_prueba <- function(parametro1 ,parametro2 = "Te colgaste en ingresar algo") {
  paste(parametro1, parametro2, sep = " <--> ")
  
}
Otra_funcion_prueba(parametro1 = "Valor 1 ")
[1] "Valor 1  <--> Te colgaste en ingresar algo"

Las funciones que creamos nosotros permanecen en el ambiente de R temporariamente. Cuando removemos los objetos del ambiente, la función deja de existir. Por ende, debemos incorporarla en cada uno de los scripts en la cual la necesitemos. Una buena práctica, es incorporar nuestras funciones útiles al comienzo de cada script junto a la carga de las librerías.

Vale mencionar que lo que ocurre en una función, queda en la función excepto que explícitamente pidamos que devuelva el resultado, con el comando print().

Las funciones siempre devuelven el último objeto que se crea en ellas, o si explicitamente se utiliza el comando return()

MAP1

La función map toma un input, una función para aplicar, y alguna otra cosa (por ejemplo parametros que necesite la función)

  • map(.x, .f, …)
  • map(VECTOR_O_LIST_INPUT, FUNCTION_A_APLICAR, OTROS_OPCIONALES)

Usamos map2 cuando tenemos que pasar dos input, que se aplican sobre una función:

  • map2(.x, .y, .f, …)
  • map2(INPUT_UNO, INPUT_DOS, FUNCTION_A_APLICAR, OTROS_OPCIONALES)

Si tenemos más de dos…

  • pmap(.l, .f, …)
  • pmap(VECTOR_O_LIST_INPUT, FUNCTION_A_APLICAR, OTROS_OPCIONALES)

Por ejemplo. Si queremos utilizar la función prueba sobre los datos del dataframe ABC_123

ABC_123
funcion_prueba
function(parametro1,parametro2) {
  paste(parametro1, parametro2, sep = " <--> ")
}

Si el resultado que queremos es que junte cada fila, necesitamos pasarle dos parámetros: utilizamos map2()

resultado <- map2(ABC_123$Letras,ABC_123$Num,funcion_prueba)
resultado[1:3]
[[1]]
[1] "A <--> 1"

[[2]]
[1] "B <--> 2"

[[3]]
[1] "C <--> 3"

La salida de los map() es una lista, no un vector, por lo que si lo metemos dentro de un dataframe se vería así:

ABC_123 %>% 
  mutate(resultado= map2(Letras,Num,funcion_prueba))

al ponerlo dentro del dataframe desarma la lista y guarda cada elemento por separado. La magia de eso es que podemos guardar cualquier cosa en el dataframe no sólo valores, sino también listas, funciones, dataframes, etc.

Si queremos recuperar los valores originales en este caso podemos usar unlist()

ABC_123 %>% 
  mutate(resultado= unlist(map2(Letras,Num,funcion_prueba)))

Si lo que queríamos era que la función nos haga todas las combinaciones de letras y número, entonces lo que necesitamos es pasarle el segúndo parametro como algo fijo, poniendolo después de la función.

map(ABC_123$Letras,funcion_prueba,ABC_123$Num)[1:2]
[[1]]
 [1] "A <--> 1"  "A <--> 2"  "A <--> 3"  "A <--> 4"  "A <--> 5"  "A <--> 6"  "A <--> 7"  "A <--> 8" 
 [9] "A <--> 9"  "A <--> 10" "A <--> 11" "A <--> 12" "A <--> 13" "A <--> 14" "A <--> 15" "A <--> 16"
[17] "A <--> 17" "A <--> 18" "A <--> 19" "A <--> 20"

[[2]]
 [1] "B <--> 1"  "B <--> 2"  "B <--> 3"  "B <--> 4"  "B <--> 5"  "B <--> 6"  "B <--> 7"  "B <--> 8" 
 [9] "B <--> 9"  "B <--> 10" "B <--> 11" "B <--> 12" "B <--> 13" "B <--> 14" "B <--> 15" "B <--> 16"
[17] "B <--> 17" "B <--> 18" "B <--> 19" "B <--> 20"

En este caso, el map itera sobre cada elemento de letras, y para cada elemento i hace funcion_prueba(i,ABC$Num) y guarda el resultado en la lista

si lo queremos meter en el dataframe

ABC_123 %>% 
  mutate(resultado= map(Letras,funcion_prueba,Num))

Ahora cada fila tiene un vector de 20 elementos guardado en la columna resultado

Lectura y escritura de archivos intermedia

R tiene formatos de archivos propios:

  • Rdata
  • RDS

RData

x <- 1:15
y <- list(a = 1, b = TRUE, c = "oops")
#Para guardar
save(x, y, file = "xy.RData")
#Para leer
load('xy.RData')

Los archivos de tipo RData permiten grabar una imagen de todos los objetos R que querramos.

RDS

x
 [1]  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15
saveRDS(x, "x.RDS")
Z <- readRDS("x.RDS")
Z
 [1]  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15

Los archivos de tipo RDS no guardan el nombre del objeto, por lo que podemos nombrarlos cuando los cargamos (aconsejable)

SPSS, STATA, SAS

A su vez, también R facilmente permite la comunicación con otros softwares estadísticos, o programas destinados al manejo de bases de datos. La librería haven tiene funciones que permiten leer archivos que provienen de otros programas como SPSS, STATA y SAS, entre otros. Los comandos son:

  • read_spss()
  • read_dta()
  • read_sas()

Encoding

Tanto a la hora de leer y escribir archivos, como al trabajar un mismo script desde distintas computadoras, debemos ser cuidadosos con el encoding seteado. El encoding es el sistema mediante el cual el sistema interpreta los caracteres del lenguaje natural. Hay muchos encodings diferentes, que interpretan distinto algunos caracteres, como tildes y signos de puntuación.
Por ende, si el encoding seteado no es el mismo que el de nuestro script/tabla pueden generarse errores. En medida de lo posible, al escribir nuestros scripts es recomendable evitar estos caracteres.

R tiene por default el encoding “ISO-8859-1”, sin embargo el más habitual en América Latina es “UTF-8”.

  • Lectura de archivos : Aglunas de las funciones del tipo read_table, read.xlsx permiten establecer como uno de sus parametros el encoding deseado
  • Encoding utilizado para abrir un script:File -> Reopen with Encoding
  • Encoding default con el que se guardan nuestros Scripts: Tools -> Global Options -> Code -> Saving # Organización scripts

Por último, es aconsejable mantener en todos los script una misma estructura del tipo:

  1. Limpiar la memoria rm(list=ls())
  2. Cargar librerías
  3. Definir funciones
  4. Levantar archivos
    … procesamiento ….
  1. grabar resultados

También es útil organizar las partes del script en capítulos. Para eso

### escribimos el título del capitulo encerrado entre tres o más numerales ###

Ayudas

Hay muchas ayudas, propias del programa, o de usuarios, que pueden ser de ayuda.

  • En el programa, para consultar los parámetros de una función, le escribe ?funcion()

  • Rstudio tiene unos machetes muy útiles

  • Rdocumentation

  • stack overflow conviene llegar desde google

La clave es aprender la terminología para googlear en ingles las dudas, y prestar atención a que las respuestas sean actuales (R es un lenguaje vivo)

Ejercicios para practicar

  • Crear mediante un loop un gráfico de barras por región, donde se compare el ingreso de la ocupación principal promedio de varones y mujeres en el 1er trimestre de 2017.
  • Crear una función llamada HolaMundo que imprima el texto “Hola mundo”
  • Crear una función que devuelva la sumatoria de los números enteros comprendidos entre 1 y un parámetro x a definir

  • Levantar la base Individual del 1er trimestre de 2017, de la EPH
  • Guardar la base Individual del 1er trimestre de 2017 como un archivo de extensión .RDS
  • Volver a levantar la base, pero como .RDS y asignarla con el nombre BaseRDS ¿tarda más o menos?

Ejercicio de tarea

  • Realizar mediante un loop, para cada una de las regiones,un gráfico de barras donde se comparen las tasas de desempleo de varones y mujeres en el 1er trimestre de 2017 (Pista: ver código del cálculo de la tasa en la clase 2, y del gráfico en la nota de clase 3)
LS0tCnRpdGxlOiAiVXRpbGl6YWNpw7NuIGRlbCBsZW5ndWFqZSBSIHBhcmEgYXBsaWNhY2nDs24gZW4gbGEgRW5jdWVzdGEgUGVybWFuZW50ZSBkZSBIb2dhcmVzIgpzdWJ0aXRsZTogIkNsYXNlIDYgLSBSIGludGVybWVkaW8iCmRhdGU6ICIwNy8xMS8yMDE4IgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHllcwotLS0KKkxhcyBub3RhcyBkZWwgcHJlc2VudGUgY3Vyc28gZnVlcm9uIGVsYWJvcmFkYXMgb3JpZ2luYWxtZW50ZSBwb3IgRGllZ28gS296bG93c2tpIHkgR3VpZG8gV2Vrc2xlci4gRW4gbGFzIHN1Y2VzaXZhcyBtb2RpZmljYWNpb25lcyBjb2xhYm9yYXJvbjogTmF0c3VtaSBTaG9raWRhIHkgTWF0w61hcyBMaW9uaSogICAgICAgICAgICAgICAgICAgICAgCgoKPlJlaW5pY2lhciBSCgpgYGB7cn0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkob3Blbnhsc3gpCmxpYnJhcnkoZ2d0aGVtZXMpCmBgYAoKCiMgTG9vcHMKVW4gX19sb29wX18gZXMgdW5hIGVzdHJ1Y3R1cmEgZGUgY8OzZGlnbyBxdWUgbm9zIHBlcm1pdGUgYXBsaWNhciBpdGVyYXRpdmFtZW50ZSB1biBtaXNtbyBjb25qdW50byBkZSBjb21hbmRvcywgdmFyaWFuZG8gYWxnw7puIGVsZW1lbnRvIHNvYnJlIGVsIGN1YWwgc2UgdHJhYmFqYXLDoS4gUG9yIGVqZW1wbG86CgpgYGB7cix3YXJuaW5nPUZBTFNFfQpmb3IoaSBpbiAxOjEwKXsKICAgcHJpbnQoaV4yKQp9CmBgYAoKRXN0byBzZSBsZWUgY29tbyA6ICJSZWNvcnJlIGNhZGEgdW5vIGRlIGxvcyB2YWxvcmVzIChpKSBkZWwgdmVjdG9yIG51bcOpcmljbyAxIGEgMTAsIHkgcGFyYSBjYWRhIHVubyBkZSBlbGxvcyBpbXByaW3DrSBlbCBjdWFkcmFkbyAoaV4yKSIuICAgICAgICAgICAgICAgICAgClVubyBwdWVkZSBlc3BlY2lmaWNhciBsYSBwYWxhYnJhIHF1ZSBkZXNlZSBxdWUgdG9tw6kgY2FkYSB1bm8gZGUgbG9zIHZhbG9yZXMgcXVlIGRlYmUgdG9tYXIuIEVuIGVsIGVqZW1wbG8gYW50ZXJpb3IgZnVlICoqaSoqLCBwZXJvIGJpZW4gcG9kcsOtYSBzZXIgbGEgIioqVmFsb3JlcyoqIiAgCgpgYGB7cn0KZm9yKFZhbG9yZXMgaW4gMToxMCl7CiAgIHByaW50KFZhbG9yZXNeMikKICAKfQpgYGAKCkEgc3UgdmV6LCBsb3MgbG9vcHMgcHVlZGVuIGl0ZXJhciBzb2JyZSBsb3MgZGlzdGludG9zIHZhbG9yZXMgZGUgdW5hIHZhcmlhYmxlIGVuIHVuICoqRGF0YWZyYW1lKiouIApBIGNvbnRpbnVhY2nDs24gcmVhbGl6YXJlbW9zIHVuIGVqZW1wbG8gaXRlcmFuZG8gc29icmUgbGEgdmFyaWFibGUgKlJFR0lPTiogZW4gbGEgYmFzZSBkZSBFUEgsIHBhcmEgY29uc2VydmFyIHVuIGRhdGFmcmFtZSBwb3IgY2FkYSBSZWdpb24uIFByZXZpYW1lbnRlIHVuaW1vcyBsYSBiYXNlIGNvbiBlbCBkaWNjaW9uYXJpbyBkZSBOb21icmVzIGRlIFJlZ2lvbmVzIHkgQ8OzZGlnb3MuICAgCgpgYGB7cixtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9CgpJbmRpdmlkdWFsX3QxMTcgPC0gcmVhZC50YWJsZSgiLi4vRnVlbnRlcy91c3VfaW5kaXZpZHVhbF90MTE3LnR4dCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlcD0iOyIsIGRlYz0iLCIsIGhlYWRlciA9IFRSVUUsIGZpbGwgPSBUUlVFKQpSZWdpb25lcyA8LSByZWFkLnhsc3goIi4uL0Z1ZW50ZXMvUmVnaW9uZXMueGxzeCIpCkFnbG9tZXJhZG9zIDwtIHJlYWQueGxzeCgiLi4vRnVlbnRlcy9BZ2xvbWVyYWRvcyBFUEgueGxzeCIpCgpCYXNlPC0gSW5kaXZpZHVhbF90MTE3ICU+JSAKICBsZWZ0X2pvaW4oUmVnaW9uZXMpICU+JSAKICBsZWZ0X2pvaW4oQWdsb21lcmFkb3MpCmBgYApQYXJhIGl0ZXJhciBzb2JyZSBsYSB2YXJpYWJsZSByZWdpw7NuLCBubyBuZWNlc2l0YW1vcyByZWNvcnJlciB0b2RvcyBsb3MgdmFsb3JlcyBkZSBsYSBtaXNtYSwgc2lubyBwb3IgbG9zICoqdmFsb3JlcyDDum5pY29zKiouIEVzIGRlY2lyLCBxdWUgdmFtb3MgYSBxdWVyZXIgcmVhbGl6YXIgdW4gbG9vcCBxdWUgcmVhbGl6YXLDoSB1biBtaXNtbyBwcm9jZWRpbWllbnRvIDYgdmVjZXMuIFBhcmEgZWxsbywgdXRpbGl6YXJlbW9zIGxhIGZ1bmNpw7NuICoqdW5pcXVlKiogcXVlIG5vcyBwZXJtaXRlIG9idGVuZXIgdW4gdmVjdG9yIGNvbiBsb3Mgw7puaWNvcyB2YWxvcmVzIHF1ZSB0b21hIGRldGVybWluYWRhIHZhcmlhYmxlLgoKYGBge3J9CnVuaXF1ZShCYXNlJFJlZ2lvbikKYGBgCgpgYGB7cn0KCmZvcih2YXJpYWJsZV9pdGVyYSBpbiB1bmlxdWUoQmFzZSRSZWdpb24pKXsKcHJpbnQodmFyaWFibGVfaXRlcmEpIyBObyBlcyBuZWNlc2FyaW8sIHBlcm1pdGUgdmVyIHBvciBxdWUgUmVnacOzbiBlc3RveSB0cmFiYWphbmRvCiAgCnRlbXAgPC0gQmFzZSAlPiUgIyBBcXXDrSBmaWx0cm8gbGEgYmFzZSBjdWFuZG8gUmVnaW9uIHRvbWEgZWwgdmFsb3IgZGUgInZhcmlhYmxlX2l0ZXJhIgogIGZpbHRlcihSZWdpb24gPT0gdmFyaWFibGVfaXRlcmEpCgojI1RlbXAgZXMgdW4gZGF0YWZyYW1lIHF1ZSBzZXLDoSAicGlzYWRvIiBlbiBjYWRhIGl0ZXJhY2nDs24uCiNMYSBmdW5jacOzbiBhc3NpZ24gbWUgcGVybWl0ZSBkZWZpbmlyIGEgdW4gb2JqZXRvIHV0aWxpemFuZG8gY29tbyBwcmltZXIgYXJndW1lbnRvIGVsIG5vbWJyZSBkZXNlYWRvIAphc3NpZ24odmFyaWFibGVfaXRlcmEsdGVtcCkKICAgIH0KCmBgYAoKIyBFc3RydWN0dXJhcyBDb25kaWNpb25hbGVzCgpMYXMgX19lc3RydWN0dXJhcyBjb25kaWNvbmFsZXNfXyBub3MgcGVybWl0ZXMgZWplY3V0YXIgZGV0ZXJtaW5hZG8gY8OzZGlnbywgbyBkZXRlcm1pbmFkYSBmdW5jacOzbiBzb2JyZSB1bmEgdmFyaWFibGUsIGFjb3JkZSBhbCBjdW1wbGltaWVudG8gbyBubyBkZSBkZXRlcm1pbmFkYSBjb25kaWNpw7NuIGzDs2dpY2EuICAgICAKIyMgaWYKTGEgZnVuY2nDs24gYGBgaWYoKWBgYG5vcyBwZXJtaXRlIGRpc2XDsWFyIHVuIHNlZ21lbnRvIGRlIGPDs2RpZ28gcXVlIHNvbG8gc2VyYSBlamVjdXRhZG8gZW4gY2FzbyBkZSBxdWUgc2UgY3VtcGxhIGRldGVybWluYWRhIGNvbmRpY2nDs24uIFN1IGZ1bmNpb25hbWllbnRvIGVzIGVsIHNpZ3VpZW50ZTogICAgIApgYGBpZihjb25kaWNpb24pe2NvZGlnbyBhIGVqZWN1dGFyIHNpIHNlIGN1bXBsZSBsYSBjb25kaWNpw7NufWBgYApgYGB7cix3YXJuaW5nPUZBTFNFfQppZiggMisyID09IDQpewogIHByaW50KCJNZW5vcyBNYWwiKQp9CmBgYAoKYGBge3Isd2FybmluZz1GQUxTRX0KaWYoIDIrMiA9PSAxNDguMjQpewogIHByaW50KCJSLCBsYSBlc3RhcyBwaWZpYW5kbyIpCn0KCmBgYAojIyBpZmVsc2UKTGEgZnVuY2nDs24gYGBgaWZfZWxzZSgpYGBgIHNpcnZlIHBhcmEgY3JlYXIgbyBtb2RpZmljYXIgZGljb3TDs21pY2FtZW50ZSB1biBvYmpldG8vdmFyaWFibGUvdmVjdG9yIGEgcGFydGlyIGRlbCBjdW1wbGltaWVudG8gZGUgdW5hIG8gbcOhcyBjb25kaWNpb25lcyBsw7NnaWNhcy4gIApTdSBmdW5jaW9uYW1pZW50byBlcyBlbCBzaWd1aWVudGU6ICAgICAgCmBgYGlmX2Vsc2UoY29uZGljaW9uLGZ1bmNpw7NuIGEgYXBsaWNhciBzaSBzZSBjdW1wbGUgbGEgY29uZGljacOzbixmdW5jacOzbiBhIGFwbGljYXIgc2kgbm8gc2UgY3VtcGxlIGxhIGNvbmRpY2nDs24pYGBgCgpgYGB7cn0KCgpyZXN1bHRhZG8gPC0gaWZfZWxzZSgyKzI9PTQsIHRydWUgPSAiSm95YSIsZmFsc2UgPSAiRXJyb3IiKSAjVmVyc2nDs24gZHBseXIKCkFCQ18xMjMgPC0gZGF0YS5mcmFtZShMZXRyYXMgPSBMRVRURVJTWzE6MjBdLE51bSA9IDE6MjApCkFCQ18xMjMgJT4lIAogIG11dGF0ZShNYXlvcl9vX01lbm9yID0gaWZlbHNlKE51bTw9NSwiTWVub3IgbyBpZ3VhbCBxdWUgNSIsIk1heW9yIHF1ZSA1IikpICNWZXJzacOzbiBiYXNlCmBgYAoKIyBFamVyY2ljaW8gcHLDoWN0aWNvIGNvbWJpbmFuZG8gTG9vcHMgeSBlc3RydWN0dXJhcyBjb25kaWNpb25hbGVzCkEgY29udGludWFjacOzbiBoYXJlbW9zIHVuIGVqZXJjaWNpbyBwYXJhIG1vc3RyYXIgbGEgYXBsaWNhY2nDs24gY29uanVudGEgcXVlIHBvZGVtb3MgZGFyIGEgbG9zIGxvb3BzIHkgYSBsYXMgZXN0cnVjdHVyYXMgZGUgY8OzZGlnbyBjb25kaWNpb25hbC4gUGF0aWN1bGFybWVudGUgZXN0b3Mgc29uIG11eSDDunRpbGVzIGEgbGEgaG9yYSBkZSByZWFsaXphciBtdWx0aXBsZXMgZ3LDoWZpY29zLiAgICAKRWwgZWplcmNpY2lvIGNvbnNpc3RpcsOhIGVuIHJlYWxpemFyLCBwYXJhIGNhZGEgYWdsb21lcmFkbywgdW4gZ3LDoWZpY28gZGUgbGEgZGlzdHJpYnVjacOzbiBwb3IgZ8OpbmVybyBhbCBpbnRlcmlvciBkZSBjYWRhIGNhdGVnb3LDrWEgb2N1cGFjaW9uYWwuIFNpbiBlbWJhcmdvLCB1dGlsaXphcmVtb3MgbGEgdmFyaWFibGUgKipNQVNfNTAwKiogcGFyYSByZXBsaWNhciBlbCBwcm9jZWRpbWllbnRvIMO6bmljYW1lbnRlIHBhcmEgbG9zIGFnbG9tZXJhZG9zIGRlIG1hcyBkZSA1MDAuMDAwIGhhYml0YW50ZXMKYGBge3J9CiNBbnRlcyBkZSBjb21lbnphciBlbCBMb29wLCByZWNvZGlmaWNvIGxhcyB2YXJpYWJsZXMgCkJhc2VfcGFyYV9sb29wIDwtIEJhc2UgJT4lIAogIG11dGF0ZShDSDA0ID0gY2FzZV93aGVuKENIMDQgPT0gMSB+ICJWYXJvbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgQ0gwNCA9PSAyIH4gIk11amVyIiksCiAgICAgICAgIENBVF9PQ1VQID0gY2FzZV93aGVuKENBVF9PQ1VQID09IDEgIH4gIlBhdHJvbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIENBVF9PQ1VQID09IDIgIH4gIkN1ZW50YSBQcm9waWEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBDQVRfT0NVUCA9PSAzICB+ICJBc2FsYXJpYWRvcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIENBVF9PQ1VQID09IDQgIH4gIlRGU1IiKSkKCgpmb3IoYWdsb21faXRlcmEgaW4gdW5pcXVlKEJhc2VfcGFyYV9sb29wJEFHTE9NRVJBRE8pKXsKICAKI0ZpbHRybyBsYSBiYXNlIHJlc3RyaW5naWVuZG9sYSBhIHVuIGFnbG9tZXJhZG8KICBCYXNlX2l0ZXJhX3hhZ2xvbSA8LSBCYXNlX3BhcmFfbG9vcCAlPiUKICAgIGZpbHRlcihBR0xPTUVSQURPID09IGFnbG9tX2l0ZXJhKQoKI0V4aWpvIGxvcyBwcm9jZWRpbWllbnRvcyBhIGNvbnRpbnVhY2nDs24gc8OzbG8gc2UgcmVhbGljZW4gcGFyYSBhZ2xvbWVyYWRvcyBjb24gbcOhcyBkZSA1MDAuMDAwIGhhYml0YW50ZXMKCiAgaWYodW5pcXVlKEJhc2VfaXRlcmFfeGFnbG9tJE1BU181MDApPT0iUyIpewogICAgCkJhc2VfZ3JhZmljbyAgPC0gQmFzZV9pdGVyYV94YWdsb20gJT4lIAogICAgICBmaWx0ZXIoQ0FUX09DVVAgIT0gMCkgJT4lIAogICAgICBncm91cF9ieShOb21fQWdsbyxDQVRfT0NVUCxDSDA0KSAlPiUgCiAgICAgIHN1bW1hcmlzZShDYW50aWRhZCA9IHN1bShQT05ERVJBLG5hLnJtID0gVFJVRSkpICU+JSAKICAgICAgZ3JvdXBfYnkoQ0FUX09DVVApICU+JSAgICAgCiAgbXV0YXRlKFBvcmNlbnRhamUgPSBDYW50aWRhZC9zdW0oQ2FudGlkYWQpKQogICAgCkdyYWZpY28gPC0gZ2dwbG90KEJhc2VfZ3JhZmljbywgYWVzKENBVF9PQ1VQLCBQb3JjZW50YWplLCBmaWxsID0gQ0gwNCwgCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IHNwcmludGYoIiUxLjFmJSUiLCAxMDAqUG9yY2VudGFqZSkpKSsKICBnZW9tX2NvbChwb3NpdGlvbiA9ICJzdGFjayIpICsgCiAgZ2VvbV90ZXh0KHBvc2l0aW9uID0gcG9zaXRpb25fc3RhY2sodmp1c3QgPSAwLjUpLCBzaXplPTMpKwogIGxhYnMoeD0iIix5PSJQb3JjZW50YWplIiwKICAgICAgIHRpdGxlID0gdW5pcXVlKEJhc2VfZ3JhZmljbyROb21fQWdsbykpKwogIHRoZW1lX21pbmltYWwoKSsKICBzY2FsZV95X2NvbnRpbnVvdXMoKSsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCIjMDA5QTQ0IiwgIiNGRjY2MDAiKSkrCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICAgICAgbGVnZW5kLnRpdGxlPWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT0yNSkpICAgIAogICAgCkdyYWZpY28gCgpnZ3NhdmUocGFzdGUwKCIuLi9SZXN1bHRhZG9zL0FnbG9tZXJhZG8gIixhZ2xvbV9pdGVyYSwiLlBORyIpKQogICB9CiAgfQoKYGBgCkEgY29udGludWFjacOzbiBzZSBtdWVzdHJhIGVsIMO6bHRpbW8gZGUgbG9zIGdyw6FmaWNvcyByZWxpemFkb3MgcG9yIGVsIGxvb3AuIEVuIGxhIGNhcnBldGEgZGUgX19SZXN1bHRhZG9zX18gZGVsIGN1cnNvLCBwb2RyYW4gb2JzZXJ2YXJzZSBjYWRhIHVubyBkZSBsb3MgZ3LDoWZpY29zIGNvcnJlc3BvbmRpZW50ZXMgYSBsb3MgYWxnb21lcmFkb3MgZGUgbcOhcyBkZSA1MDAuMDAwIGhhYml0YW50ZXMuIAoKYGBge3IsZWNobz1GQUxTRX0KR3JhZmljbwpgYGAKCiMgRnVuY2lvbmVzIGRlbCBVc3VhcmlvCgpMYSBjcmVhY2nDs24gZGUgX19mdW5jaW9uZXNfXyBwcm9waWFzIG5vcyBwZXJtaXRlIGF1dG9tYXRpemFyIHRvZGFzIGFxdWVsbGFzIHBhcnRlcyBkZWwgY8OzZGlnbyBxdWUgc2UgcmVwaXRlbiBtdWNoby4gVW5hIHZleiBkaXNlw7FhZGFzLCBmdW5jaW9uYW4gaWd1YWwgcXVlIGN1YWxxdWllciBjb21hbmRvLiBMYSBmYWNpbGlkYWQgcGFyYSBjcmVhciBsYXMgZnVuY2lvbmVzIGVzIGVuIGJ1ZW5hIG1lZGlkYSBsYSBleHBsaWNhY2nDs24gZGUgcXVlIGhheWEgdGFudGFzIGNvbnRyaWJ1Y2lvbmVzIGRlIHVzdWFyaW9zIGEgbGEgZXhwYW5zacOzbiBkZWwgbGVuZ3VhamUuCgpQb3IgZWplbXBsbywgcG9kcsOtYW1vcyBkaXNlw7FhciB1bmEgZnVuY2nDs24gcXVlIGFncmVndWUgdW5hIGZsZWNoYSBhIGRvcyBvYmpldG9zIGRlIHRleHRvCgpgYGB7cn0KZnVuY2lvbl9wcnVlYmEgPC0gZnVuY3Rpb24ocGFyYW1ldHJvMSxwYXJhbWV0cm8yKSB7CiAgcGFzdGUocGFyYW1ldHJvMSwgcGFyYW1ldHJvMiwgc2VwID0gIiA8LS0+ICIpCn0KCmZ1bmNpb25fcHJ1ZWJhKHBhcmFtZXRybzEgPSAiQSB2ZXIiLCBwYXJhbWV0cm8yID0gIlF1ZSBwYXNhIikKCmBgYAoKVGFtYmnDqW4gcG9kZW1vcyBhc2lnbmFyIHVuIHZhbG9yIHBvciBkZWZhdWx0IHBhcmEgbG9zIHBhcmFtZXRyb3MgZW4gY2FzbyBkZSBxdWUgZWwgdXN1YXJpbyBubyBkZWZpbmEgc3UgdmFsb3IgYWwgdXRpbGl6YXIgbGEgZnVuY2nDs24uCgpgYGB7cn0KT3RyYV9mdW5jaW9uX3BydWViYSA8LSBmdW5jdGlvbihwYXJhbWV0cm8xICxwYXJhbWV0cm8yID0gIlRlIGNvbGdhc3RlIGVuIGluZ3Jlc2FyIGFsZ28iKSB7CiAgcGFzdGUocGFyYW1ldHJvMSwgcGFyYW1ldHJvMiwgc2VwID0gIiA8LS0+ICIpCiAgCn0KT3RyYV9mdW5jaW9uX3BydWViYShwYXJhbWV0cm8xID0gIlZhbG9yIDEgIikKCmBgYAoKTGFzIGZ1bmNpb25lcyBxdWUgY3JlYW1vcyBub3NvdHJvcyBwZXJtYW5lY2VuIGVuIGVsIGFtYmllbnRlIGRlIFIgdGVtcG9yYXJpYW1lbnRlLiBDdWFuZG8gcmVtb3ZlbW9zIGxvcyBvYmpldG9zIGRlbCBhbWJpZW50ZSwgbGEgZnVuY2nDs24gZGVqYSBkZSBleGlzdGlyLiBQb3IgZW5kZSwgZGViZW1vcyBpbmNvcnBvcmFybGEgZW4gY2FkYSB1bm8gZGUgbG9zIHNjcmlwdHMgZW4gbGEgY3VhbCBsYSBuZWNlc2l0ZW1vcy4gVW5hIGJ1ZW5hIHByw6FjdGljYSwgZXMgaW5jb3Jwb3JhciBudWVzdHJhcyBmdW5jaW9uZXMgw7p0aWxlcyBhbCBjb21pZW56byBkZSBjYWRhIHNjcmlwdCBqdW50byBhIGxhIGNhcmdhIGRlIGxhcyBsaWJyZXLDrWFzLiAgICAgICAgICAgICAgICAgICAgICAgICAKClZhbGUgbWVuY2lvbmFyIHF1ZSBfX2xvIHF1ZSBvY3VycmUgZW4gdW5hIGZ1bmNpw7NuLCBxdWVkYSBlbiBsYSBmdW5jacOzbl9fIGV4Y2VwdG8gcXVlIGV4cGzDrWNpdGFtZW50ZSBwaWRhbW9zIHF1ZSBkZXZ1ZWx2YSBlbCByZXN1bHRhZG8sIGNvbiBlbCBjb21hbmRvIGBwcmludCgpYC4gCgpMYXMgZnVuY2lvbmVzIHNpZW1wcmUgZGV2dWVsdmVuIGVsIMO6bHRpbW8gb2JqZXRvIHF1ZSBzZSBjcmVhIGVuIGVsbGFzLCBvIHNpIGV4cGxpY2l0YW1lbnRlIHNlIHV0aWxpemEgZWwgY29tYW5kbyBgcmV0dXJuKClgCgoKIyBNQVBeW2Jhc2FkbyBlbiBodHRwczovL2plbm55YmMuZ2l0aHViLmlvL3B1cnJyLXR1dG9yaWFsL2xzMDNfbWFwLWZ1bmN0aW9uLXN5bnRheC5odG1sXQoKTGEgZnVuY2nDs24gX19tYXBfXyB0b21hIHVuIGlucHV0LCB1bmEgZnVuY2nDs24gcGFyYSBhcGxpY2FyLCB5IGFsZ3VuYSBvdHJhIGNvc2EgKHBvciBlamVtcGxvIHBhcmFtZXRyb3MgcXVlIG5lY2VzaXRlIGxhIGZ1bmNpw7NuKQoKLSBtYXAoLngsIC5mLCAuLi4pCi0gbWFwKFZFQ1RPUl9PX0xJU1RfSU5QVVQsIEZVTkNUSU9OX0FfQVBMSUNBUiwgT1RST1NfT1BDSU9OQUxFUykKCgpVc2Ftb3MgX19tYXAyX18gY3VhbmRvIHRlbmVtb3MgcXVlIHBhc2FyIGRvcyBpbnB1dCwgcXVlIHNlIGFwbGljYW4gc29icmUgdW5hIGZ1bmNpw7NuOgoKLSBtYXAyKC54LCAueSwgLmYsIC4uLikKLSBtYXAyKElOUFVUX1VOTywgSU5QVVRfRE9TLCBGVU5DVElPTl9BX0FQTElDQVIsIE9UUk9TX09QQ0lPTkFMRVMpCgpTaSB0ZW5lbW9zIG3DoXMgZGUgZG9zLi4uCgotIHBtYXAoLmwsIC5mLCAuLi4pCi0gcG1hcChWRUNUT1JfT19MSVNUX0lOUFVULCBGVU5DVElPTl9BX0FQTElDQVIsIE9UUk9TX09QQ0lPTkFMRVMpCgoKUG9yIGVqZW1wbG8uIFNpIHF1ZXJlbW9zIHV0aWxpemFyIGxhIGZ1bmNpw7NuIHBydWViYSBzb2JyZSBsb3MgZGF0b3MgZGVsIGRhdGFmcmFtZSBBQkNfMTIzCgoKCgpgYGB7cn0KQUJDXzEyMwpmdW5jaW9uX3BydWViYQpgYGAKClNpIGVsIHJlc3VsdGFkbyBxdWUgcXVlcmVtb3MgZXMgcXVlIGp1bnRlIGNhZGEgZmlsYSwgbmVjZXNpdGFtb3MgcGFzYXJsZSBkb3MgcGFyw6FtZXRyb3M6IHV0aWxpemFtb3MgYG1hcDIoKWAKCgpgYGB7cn0KcmVzdWx0YWRvIDwtIG1hcDIoQUJDXzEyMyRMZXRyYXMsQUJDXzEyMyROdW0sZnVuY2lvbl9wcnVlYmEpCnJlc3VsdGFkb1sxOjNdCmBgYAoKTGEgc2FsaWRhIGRlIGxvcyBgbWFwKClgIGVzIHVuYSBfX2xpc3RhX18sIG5vIHVuIHZlY3RvciwgcG9yIGxvIHF1ZSBzaSBsbyBtZXRlbW9zIGRlbnRybyBkZSB1biBkYXRhZnJhbWUgc2UgdmVyw61hIGFzw606CgpgYGB7cn0KQUJDXzEyMyAlPiUgCiAgbXV0YXRlKHJlc3VsdGFkbz0gbWFwMihMZXRyYXMsTnVtLGZ1bmNpb25fcHJ1ZWJhKSkKCmBgYAoKYWwgcG9uZXJsbyBkZW50cm8gZGVsIGRhdGFmcmFtZSBkZXNhcm1hIGxhIGxpc3RhIHkgZ3VhcmRhIGNhZGEgZWxlbWVudG8gcG9yIHNlcGFyYWRvLgpMYSBtYWdpYSBkZSBlc28gZXMgcXVlIHBvZGVtb3MgX19ndWFyZGFyIGN1YWxxdWllciBjb3NhIGVuIGVsIGRhdGFmcmFtZV9fIG5vIHPDs2xvIHZhbG9yZXMsIHNpbm8gdGFtYmnDqW4gbGlzdGFzLCBmdW5jaW9uZXMsIGRhdGFmcmFtZXMsIGV0Yy4KClNpIHF1ZXJlbW9zIHJlY3VwZXJhciBsb3MgdmFsb3JlcyBvcmlnaW5hbGVzIGVuIGVzdGUgY2FzbyBwb2RlbW9zIHVzYXIgYHVubGlzdCgpYAoKCmBgYHtyfQpBQkNfMTIzICU+JSAKICBtdXRhdGUocmVzdWx0YWRvPSB1bmxpc3QobWFwMihMZXRyYXMsTnVtLGZ1bmNpb25fcHJ1ZWJhKSkpCgpgYGAKCgpTaSBsbyBxdWUgcXVlcsOtYW1vcyBlcmEgcXVlIGxhIGZ1bmNpw7NuIG5vcyBoYWdhIHRvZGFzIGxhcyBjb21iaW5hY2lvbmVzIGRlIGxldHJhcyB5IG7Dum1lcm8sIGVudG9uY2VzIGxvIHF1ZSBuZWNlc2l0YW1vcyBlcyBwYXNhcmxlIGVsIHNlZ8O6bmRvIHBhcmFtZXRybyBjb21vIGFsZ28gX2Zpam9fLCBwb25pZW5kb2xvIGRlc3B1w6lzIGRlIGxhIGZ1bmNpw7NuLiAKCmBgYHtyfQptYXAoQUJDXzEyMyRMZXRyYXMsZnVuY2lvbl9wcnVlYmEsQUJDXzEyMyROdW0pWzE6Ml0KYGBgCgpFbiBlc3RlIGNhc28sIGVsIG1hcCBpdGVyYSBzb2JyZSBjYWRhIGVsZW1lbnRvIGRlIGBsZXRyYXNgLCB5IHBhcmEgY2FkYSBlbGVtZW50byBfaV8gaGFjZSAKYGZ1bmNpb25fcHJ1ZWJhKGksQUJDJE51bSlgIHkgZ3VhcmRhIGVsIHJlc3VsdGFkbyBlbiBsYSBsaXN0YQoKc2kgbG8gcXVlcmVtb3MgbWV0ZXIgZW4gZWwgZGF0YWZyYW1lCgpgYGB7cn0KQUJDXzEyMyAlPiUgCiAgbXV0YXRlKHJlc3VsdGFkbz0gbWFwKExldHJhcyxmdW5jaW9uX3BydWViYSxOdW0pKQpgYGAKCkFob3JhIGNhZGEgZmlsYSB0aWVuZSB1biB2ZWN0b3IgZGUgMjAgZWxlbWVudG9zIGd1YXJkYWRvIGVuIGxhIGNvbHVtbmEgcmVzdWx0YWRvCgoKIyBMZWN0dXJhIHkgZXNjcml0dXJhIGRlIGFyY2hpdm9zIGludGVybWVkaWEKUiB0aWVuZSBmb3JtYXRvcyBkZSBhcmNoaXZvcyBwcm9waW9zOgoKLSBSZGF0YQotIFJEUwoKIyMgUkRhdGEKYGBge3J9CnggPC0gMToxNQp5IDwtIGxpc3QoYSA9IDEsIGIgPSBUUlVFLCBjID0gIm9vcHMiKQoKI1BhcmEgZ3VhcmRhcgpzYXZlKHgsIHksIGZpbGUgPSAieHkuUkRhdGEiKQoKI1BhcmEgbGVlcgpsb2FkKCd4eS5SRGF0YScpCmBgYAoKTG9zIGFyY2hpdm9zIGRlIHRpcG8gX19SRGF0YV9fIHBlcm1pdGVuIGdyYWJhciB1bmEgX2ltYWdlbl8gZGUgdG9kb3MgbG9zIG9iamV0b3MgUiBxdWUgcXVlcnJhbW9zLgoKIyMgUkRTCgpgYGB7cn0KeApzYXZlUkRTKHgsICJ4LlJEUyIpCgpaIDwtIHJlYWRSRFMoInguUkRTIikKWgpgYGAKCkxvcyBhcmNoaXZvcyBkZSB0aXBvIF9fUkRTX18gbm8gZ3VhcmRhbiBlbCBub21icmUgZGVsIG9iamV0bywgcG9yIGxvIHF1ZSBwb2RlbW9zIG5vbWJyYXJsb3MgY3VhbmRvIGxvcyBjYXJnYW1vcyAoYWNvbnNlamFibGUpCgojIyBTUFNTLCBTVEFUQSwgU0FTCgpBIHN1IHZleiwgdGFtYmnDqW4gUiBmYWNpbG1lbnRlIHBlcm1pdGUgbGEgY29tdW5pY2FjacOzbiBjb24gb3Ryb3Mgc29mdHdhcmVzIGVzdGFkw61zdGljb3MsIG8gcHJvZ3JhbWFzIGRlc3RpbmFkb3MgYWwgbWFuZWpvIGRlIGJhc2VzIGRlIGRhdG9zLiBMYSBsaWJyZXLDrWEgX2hhdmVuXyB0aWVuZSBmdW5jaW9uZXMgcXVlIHBlcm1pdGVuIGxlZXIgYXJjaGl2b3MgcXVlIHByb3ZpZW5lbiBkZSBvdHJvcyBwcm9ncmFtYXMgY29tbyBfX1NQU1NfXywgX19TVEFUQV9fIHkgX19TQVNfXywgZW50cmUgb3Ryb3MuIExvcyBjb21hbmRvcyBzb246CgotIHJlYWRfc3BzcygpCi0gcmVhZF9kdGEoKQotIHJlYWRfc2FzKCkKCiMjIEVuY29kaW5nClRhbnRvIGEgbGEgaG9yYSBkZSBsZWVyIHkgZXNjcmliaXIgYXJjaGl2b3MsIGNvbW8gYWwgdHJhYmFqYXIgdW4gbWlzbW8gc2NyaXB0IGRlc2RlIGRpc3RpbnRhcyBjb21wdXRhZG9yYXMsIGRlYmVtb3Mgc2VyIGN1aWRhZG9zb3MgY29uIGVsIF9lbmNvZGluZ18gc2V0ZWFkby4gRWwgX2VuY29kaW5nXyBlcyBlbCBzaXN0ZW1hIG1lZGlhbnRlIGVsIGN1YWwgZWwgc2lzdGVtYSBpbnRlcnByZXRhIGxvcyBjYXJhY3RlcmVzIGRlbCBsZW5ndWFqZSBuYXR1cmFsLiBIYXkgbXVjaG9zIF9lbmNvZGluZ3NfIGRpZmVyZW50ZXMsIHF1ZSBpbnRlcnByZXRhbiBkaXN0aW50byBhbGd1bm9zIGNhcmFjdGVyZXMsIGNvbW8gdGlsZGVzIHkgc2lnbm9zIGRlIHB1bnR1YWNpw7NuLiAgICAgClBvciBlbmRlLCBzaSBlbCBfZW5jb2RpbmdfIHNldGVhZG8gbm8gZXMgZWwgbWlzbW8gcXVlIGVsIGRlIG51ZXN0cm8gc2NyaXB0L3RhYmxhIHB1ZWRlbiBnZW5lcmFyc2UgZXJyb3Jlcy4gRW4gbWVkaWRhIGRlIGxvIHBvc2libGUsIGFsIGVzY3JpYmlyIG51ZXN0cm9zIHNjcmlwdHMgZXMgcmVjb21lbmRhYmxlIGV2aXRhciBlc3RvcyBjYXJhY3RlcmVzLiAKClIgdGllbmUgcG9yIGRlZmF1bHQgZWwgZW5jb2RpbmcgX18iSVNPLTg4NTktMSJfXywgc2luIGVtYmFyZ28gZWwgbcOhcyBoYWJpdHVhbCBlbiBBbcOpcmljYSBMYXRpbmEgZXMgX18iVVRGLTgiX18uIAoKLSAqKkxlY3R1cmEgZGUgYXJjaGl2b3MqKiA6IEFnbHVuYXMgZGUgbGFzIGZ1bmNpb25lcyBkZWwgdGlwbyBfX3JlYWRfdGFibGVfXywgX19yZWFkLnhsc3hfXyBwZXJtaXRlbiBlc3RhYmxlY2VyIGNvbW8gdW5vIGRlIHN1cyBwYXJhbWV0cm9zIGVsIF9lbmNvZGluZ18gZGVzZWFkbyAKLSAqKkVuY29kaW5nIHV0aWxpemFkbyBwYXJhIGFicmlyIHVuIHNjcmlwdCoqOkZpbGUgLT4gUmVvcGVuIHdpdGggRW5jb2RpbmcKLSAqKkVuY29kaW5nIGRlZmF1bHQgY29uIGVsIHF1ZSBzZSBndWFyZGFuIG51ZXN0cm9zIFNjcmlwdHMqKjogVG9vbHMgLT4gR2xvYmFsIE9wdGlvbnMgLT4gQ29kZSAtPiBTYXZpbmcKIyBPcmdhbml6YWNpw7NuIHNjcmlwdHMKClBvciDDumx0aW1vLCBlcyBhY29uc2VqYWJsZSBtYW50ZW5lciBlbiB0b2RvcyBsb3Mgc2NyaXB0IHVuYSBtaXNtYSBlc3RydWN0dXJhIGRlbCB0aXBvOgoKMS4gTGltcGlhciBsYSBtZW1vcmlhIGBgYCBybShsaXN0PWxzKCkpIGBgYCAgICAKMi4gQ2FyZ2FyIGxpYnJlcsOtYXMKNC4gRGVmaW5pciBmdW5jaW9uZXMKNS4gTGV2YW50YXIgYXJjaGl2b3MgICAgIAouLi4gcHJvY2VzYW1pZW50byAuLi4uICAgICAKbi4gZ3JhYmFyIHJlc3VsdGFkb3MKClRhbWJpw6luIGVzIMO6dGlsIG9yZ2FuaXphciBsYXMgcGFydGVzIGRlbCBzY3JpcHQgZW4gY2Fww610dWxvcy4gUGFyYSBlc28gICAKCgpgYGAjIyMgZXNjcmliaW1vcyBlbCB0w610dWxvIGRlbCBjYXBpdHVsbyBlbmNlcnJhZG8gZW50cmUgdHJlcyBvIG3DoXMgbnVtZXJhbGVzICMjI2BgYAoKCiMgQXl1ZGFzCgpIYXkgbXVjaGFzIGF5dWRhcywgcHJvcGlhcyBkZWwgcHJvZ3JhbWEsIG8gZGUgdXN1YXJpb3MsIHF1ZSBwdWVkZW4gc2VyIGRlIGF5dWRhLgoKLSBFbiBlbCBwcm9ncmFtYSwgcGFyYSBjb25zdWx0YXIgbG9zIHBhcsOhbWV0cm9zIGRlIHVuYSBmdW5jacOzbiwgbGUgZXNjcmliZSBgYGA/ZnVuY2lvbigpYGBgCgotIFtSc3R1ZGlvXShodHRwczovL3d3dy5yc3R1ZGlvLmNvbS9yZXNvdXJjZXMvY2hlYXRzaGVldHMvKSB0aWVuZSB1bm9zIG1hY2hldGVzIG11eSDDunRpbGVzCgotIFtSZG9jdW1lbnRhdGlvbl0oaHR0cHM6Ly93d3cucmRvY3VtZW50YXRpb24ub3JnLykKCi0gW3N0YWNrIG92ZXJmbG93XShodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy90YWdnZWQvcikgY29udmllbmUgbGxlZ2FyIGRlc2RlIGdvb2dsZQoKTGEgX2NsYXZlXyBlcyBhcHJlbmRlciBsYSB0ZXJtaW5vbG9nw61hIHBhcmEgZ29vZ2xlYXIgZW4gaW5nbGVzIGxhcyBkdWRhcywgeSBwcmVzdGFyIGF0ZW5jacOzbiBhIHF1ZSBsYXMgcmVzcHVlc3RhcyBzZWFuIGFjdHVhbGVzIChSIGVzIHVuIGxlbmd1YWplIF92aXZvXykKCiMgRWplcmNpY2lvcyBwYXJhIHByYWN0aWNhcgotIENyZWFyIG1lZGlhbnRlIHVuICoqbG9vcCoqIHVuIGdyw6FmaWNvIGRlIGJhcnJhcyBwb3IgcmVnacOzbiwgZG9uZGUgc2UgY29tcGFyZSBlbCBpbmdyZXNvIGRlIGxhIG9jdXBhY2nDs24gcHJpbmNpcGFsIHByb21lZGlvIGRlIHZhcm9uZXMgeSBtdWplcmVzIGVuIGVsIDFlciB0cmltZXN0cmUgZGUgMjAxNy4gICAgICAgICAgIAotIENyZWFyIHVuYSAqKmZ1bmNpw7NuKiogbGxhbWFkYSBfSG9sYU11bmRvXyBxdWUgaW1wcmltYSBlbCB0ZXh0byAiSG9sYSBtdW5kbyIKLSBDcmVhciB1bmEgKipmdW5jacOzbioqIHF1ZSBkZXZ1ZWx2YSBsYSBzdW1hdG9yaWEgZGUgbG9zIG7Dum1lcm9zIGVudGVyb3MgY29tcHJlbmRpZG9zIGVudHJlIDEgeSB1biBwYXLDoW1ldHJvIF94XyBhIGRlZmluaXIKCi0gTGV2YW50YXIgbGEgYmFzZSBJbmRpdmlkdWFsIGRlbCAxZXIgdHJpbWVzdHJlIGRlIDIwMTcsIGRlIGxhIEVQSAotIEd1YXJkYXIgbGEgYmFzZSBJbmRpdmlkdWFsIGRlbCAxZXIgdHJpbWVzdHJlIGRlIDIwMTcgY29tbyB1biBhcmNoaXZvIGRlIGV4dGVuc2nDs24gLlJEUwotIFZvbHZlciBhIGxldmFudGFyIGxhIGJhc2UsIHBlcm8gY29tbyAuUkRTIHkgYXNpZ25hcmxhIGNvbiBlbCBub21icmUgX0Jhc2VSRFNfIMK/dGFyZGEgbcOhcyBvIG1lbm9zPwoKCiMgRWplcmNpY2lvIGRlIHRhcmVhCgotIFJlYWxpemFyIG1lZGlhbnRlIHVuIGxvb3AsIHBhcmEgY2FkYSB1bmEgZGUgbGFzIHJlZ2lvbmVzLHVuIGdyw6FmaWNvIGRlIGJhcnJhcyBkb25kZSBzZSBjb21wYXJlbiBsYXMgdGFzYXMgZGUgZGVzZW1wbGVvIGRlIHZhcm9uZXMgeSBtdWplcmVzIGVuIGVsIDFlciB0cmltZXN0cmUgZGUgMjAxNyAoUGlzdGE6IHZlciBjw7NkaWdvIGRlbCBjw6FsY3VsbyBkZSBsYSB0YXNhIGVuIGxhIGNsYXNlIDIsIHkgZGVsIGdyw6FmaWNvIGVuIGxhIG5vdGEgZGUgY2xhc2UgMykKCgoK