Manipulación de Bases de Datos
A lo largo de esta clase, trabajaremos con el paquete Tidyverse. El mismo agrupa una serie de paquetes que tienen una misma lógica en su diseño y por ende funcionan en armonía.
Entre ellos usaremos principalmente dplyr y tidyr para realizar transformaciones sobre nuestro set de datos, y ggplot para realizar gráficos (éste último se verá en la clase 3).
A continuación cargamos la librería a nuestro ambiente. Para ello debe estar previamente instalada en nuestra pc.
library(tidyverse)
Para mostrar el funcionamiento básico de tydyverse retomaremos el ejemplo de la clase 1, con lo cual volvemos a crear el set de datos del Indice de salarios.
INDICE <- c(100, 100, 100,
101.8, 101.2, 100.73,
102.9, 102.4, 103.2)
FECHA <- c("Oct-16", "Oct-16", "Oct-16",
"Nov-16", "Nov-16", "Nov-16",
"Dic-16", "Dic-16", "Dic-16")
GRUPO <- c("Privado_Registrado","Público","Privado_No_Registrado",
"Privado_Registrado","Público","Privado_No_Registrado",
"Privado_Registrado","Público","Privado_No_Registrado")
Datos <- data.frame(INDICE, FECHA, GRUPO)
Dplyr
El caracter principal para utilizar este paquete es %>%
, pipe (de tubería).
Los %>%
toman el set de datos a su izquierda, y los transforman mediante los comandos a su derecha, en los cuales los elementos de la izquierda están implícitos. Es decír, que una vez específicado el DataFrame con el cual se trabaja, no será necesario nombrarlo nuevamente para referirse a una determinada variable/columna del mismo.
Veamos las principales funciones que pueden utilizarse con la lógica de este paquete:
filter
Permite filtrar la tabla acorde al cumplimiento de condiciones lógicas
Datos %>%
filter(INDICE>101 , GRUPO == "Privado_Registrado")
Nótese que en este caso al separar con una ,
las condiciones se exige el cumplimiento de ambas. En caso de desear que se cumpla una sola condición debe utilizarse el caracter |
Datos %>%
filter(INDICE>101 | GRUPO == "Privado_Registrado")
rename
Permite renombrar una columna de la tabla. Funciona de la siguiente manera: Data %>% rename( nuevo_nombre = viejo_nombre )
Datos %>%
rename(Periodo = FECHA)
Nótese que a diferencia del ejemplo de la función filter donde utilizábamos == para comprobar una condición lógica, en este caso se utiliza sólo un = ya que lo estamos haciendo es asignar un nombre.
mutate
Permite agregar una variable a la tabla (especificando el nombre que tomará esta), que puede ser el resultado de operaciones sobre otras variables de la misma tabla.
En caso de especificar el nombre de una columna existente, el resultado de la operación realizada “sobrescribirá” la información de la columna con dicho nombre
Datos <- Datos %>%
mutate(Doble=INDICE*2)
Datos
case_when
Permite definir una variable, la cual toma un valor particular para cada condición establecida. En caso de no cumplir ninguna de las condiciones establecidas la variable tomara valor NA.
Su funcionamiento es el siguiente:
case_when(condicion1 ~ "Valor1",condicion2 ~ "Valor2",condicion3 ~ "Valor3")
Datos <- Datos %>%
mutate(Caso_cuando = case_when(GRUPO == "Privado_Registrado" ~ INDICE*2,
GRUPO == "Público" ~ INDICE*3,
GRUPO == "Privado_No_Registrado"~ INDICE*5))
Datos
select
Permite especificar la serie de columnas que se desea conservar de un DataFrame. También pueden especificarse las columnas que se desean descartar (agregándoles un -). Muy útil para agilizar el trabajo en bases de datos de gran tamaño.
Datos2 <- Datos %>%
select(INDICE, FECHA, GRUPO)
Datos2
Datos <- Datos %>%
select(-c(Doble,Caso_cuando))
Datos
arrange
Permite ordenar la tabla por los valores de determinada/s variable/s. Es útil cuando luego deben hacerse otras operaciones que requieran del ordenamiento de la tabla
Datos <- Datos %>%
arrange(GRUPO, INDICE)
Datos
summarise
Crea una nueva tabla que resume la información original. Para ello, definimos las variables de resumen y las formas de agregación.
Datos %>%
summarise(Indprom = mean(INDICE))
group_by
Esta función permite realizar operaciones de forma agrupada. Lo que hace la función es “separar” a la tabla según los valores de la variable indicada y realizar las operaciones que se especifican a continuación, de manera independiente para cada una de las “subtablas”. En nuestro ejemplo, sería útil para calcular el promedio de los indices por Fecha
Datos %>%
group_by(FECHA) %>%
summarise(Indprom = mean(INDICE))
Notese que los %>%
pueden usarse encadenados para realizar numerosos procedimientos sobre un dataframe original. Veamos un ejemplo con multiples encadenamietnos
Encadenado <- Datos %>%
filter(GRUPO == "Privado_Registrado") %>%
rename(Periodo = FECHA) %>%
mutate(Doble = INDICE*2) %>%
select(-INDICE)
Joins
Otra implementación muy importante del paquete dplyr son las funciones para unir tablas (joins)
left_join
Veamos un ejemplo de la función left_join (una de las más utilizadas en la práctica).
Para ello crearemos previamente un Dataframe que contenga un Ponderador para cada uno de los Grupos del Dataframe Datos. Aprovecharemos el ejemplo para introducir la función weigthed.mean, y así calcular un Indice Ponderado.
Ponderadores <- data.frame(GRUPO = c("Privado_Registrado","Público","Privado_No_Registrado"),
PONDERADOR = c(50.16,29.91,19.93))
Ponderadores
Datos_join <- Datos %>%
left_join(.,Ponderadores, by = "GRUPO")
Datos_join
Datos_Indice_Gral <- Datos_join %>%
group_by(FECHA) %>%
summarise(Indice_Gral = weighted.mean(INDICE,w = PONDERADOR))
Datos_Indice_Gral
Tidyr
El paquete tidyr esta pensado para facilitar el emprolijamiento de los datos.
Gather es una función que nos permite pasar los datos de forma horizontal a una forma vertical.
spread es una función que nos permite pasar los datos de forma vertical a una forma horizontal.
Retomemos el Dataframe original para mostrar como operan estas funciones:
Datos
Gather y Spread
Datos_Spread <- Datos %>%
spread(., # el . llama a lo que esta atras del %>%
key = GRUPO, #la llave es la variable cuyos valores van a dar los nombres de columnas
value = INDICE) #los valores con que se llenan las celdas
Datos_Spread
##La función opuesta (gather) nos permite obtener un dataframe como el original partiendo de un dataframe como el recién construido.
Datos_gather <- Datos_Spread %>%
gather(., # el . llama a lo que esta atras del %>%
key = GRUPO, # como se llamará la variable que toma los nombres de las columnas
value = INDICE, # como se llamará la variable que toma los valores de las columnas
2:4) #le indico que columnas juntar
Datos_gather
Mercado de Trabajo
Luego de abordar las principales funciones necesarias para operar sobre las bases de datos, trabajaremos a continuación con la EPH, realizando el ejercicio de calcular las tasas básicas del mercado de trabajo, replicando los Cuadros 1.1 y 1.2, del Informe técnico elaborado por EPH-INDEC.
En la carpeta de FUENTES del curso, se encuentra un archivo de “EPH_Concpetos_Actividad” que contiene las definiciones de los Estados ocupacionales a partir de los cuales se construyen las tasas básicas.
Cargamos la librería que usaremos para leer y escribir archivos en excel.
library(openxlsx)
Cuadro 1.1
Creamos una tabla con los niveles de:
- Población
- Ocupados
- Desocupados
- PEA
- Ocupados demandantes
- Subocupados (demandantes, no demandantes y total)
Estos niveles nos van a permitir calcular las tasas de forma sencilla.
Para obtener, por ejemplo la tasa de empleo, como el cociente \(\frac{Ocupados}{Poblacion}\) necesitamos calcular el total (expandido) de la población y de los ocupados.
- Población: Si contaramos cuantos registros tiene la base, simplemente tendríamos el numero de individuos muestral de la EPH, por ende debemos sumar los valores de la variable PONDERA, para contemplar a cuantas personas representa cada individuo encuestado.
- Ocupados: En este caso, debemos agregar un filtro al procedimiento anterior, ya que unicamente queremos sumar los ponderadores de aquellas personas que se encuentran ocupadas. (La lógica seria: “Suma los valores de la columna PONDERA, solo para aquellos registros donde el ESTADO == 1”)
Poblacion_ocupados <- Individual_t117 %>%
summarise(Poblacion = sum(PONDERA),
Ocupados = sum(PONDERA[ESTADO == 1]))
Poblacion_ocupados
La función summarise()
nos permite crear multiples variables de resumen al mismo tiempo, simplemente separando con una ,
cada uno de ellas. A su vez, se pueden crear variables, a partir de las variables creadas por la propia función. De esta forma, podemos, directamente calcular la tasa de empleo a partir del total poblacional y de ocupados.
Empleo <- Individual_t117 %>%
summarise(Poblacion = sum(PONDERA),
Ocupados = sum(PONDERA[ESTADO == 1]),
Tasa_Empleo = Ocupados/Poblacion)
Empleo
Una vez calculada la tasa, incluso podríamos desechar las variables de nivel, para conservar unicamente la tasas
Empleo %>%
select(-(1:2))
Con la misma lógica del ejemplo anterior, podemos construir con solo una porción de código el Cuadro 1.1 del Informe técnico de Mercado de trabajo EPH-INDEC.
####Cuadro 1.1 Principales indicadores. Total 31 aglomerados u
Cuadro_1.1a <- Individual_t117 %>%
summarise(Poblacion = sum(PONDERA),
Ocupados = sum(PONDERA[ESTADO == 1]),
Desocupados = sum(PONDERA[ESTADO == 2]),
PEA = Ocupados + Desocupados,
Ocupados_demand = sum(PONDERA[ESTADO == 1 & PP03J ==1]),
Suboc_demandante = sum(PONDERA[ESTADO == 1 & INTENSI ==1 & PP03J==1]),
Suboc_no_demand = sum(PONDERA[ESTADO == 1 & INTENSI ==1 & PP03J %in% c(2,9)]),
Subocupados = Suboc_demandante + Suboc_no_demand ,
# También podemos llamar a las variables entre comillas, incluyendo nombres compuestos
# A su vez, podemos utilizar la variable recién creada en la definción de otra varible
'Tasa Actividad' = PEA/Poblacion,
'Tasa Empleo' = Ocupados/Poblacion,
'Tasa Desocupacion' = Desocupados/PEA,
'Tasa ocupados demandantes' = Ocupados_demand/PEA,
'Tasa Subocupación' = Subocupados/PEA,
'Tasa Subocupación demandante' = Suboc_demandante/PEA,
'Tasa Subocupación no demandante' = Suboc_no_demand/PEA)
Cuadro_1.1a
Una vez que calculamos las tasas, podemos borrar los niveles poblacionales con un select
Cuadro_1.1a <- Cuadro_1.1a %>%
select(-c(1:8))
Cuadro_1.1a
Con gather podemos dar vuelta la tabla para que quede como en la publicación
Cuadro_1.1a <- Cuadro_1.1a %>%
gather(Tasas, Valor, 1:ncol(.))
Cuadro_1.1a
En caso de querer expresar los resultados como porcentajes, utilizamos la función sprintf. Para ello debemos utilizar mutate para transformar la columna Valor.
Cuadro_1.1a <- Cuadro_1.1a %>%
mutate(Valor = sprintf("%1.1f%%", 100*Valor))
Cuadro_1.1a
Nótese que en este caso, para poder añadir el %, la función transforma a la variable en un Character, por ende debe tenerse en cuenta que se pierde la información del numero completo.
Cuadro 1.2
En este caso, podemos ver que simplemente agregando la función group_by podemos replicar el procedimiento para cada uno de los aglomerados. Y a su vez, podemos realizar en un solo paso los arreglos posteriores sobre nuestra tabla.
Cuadro_1.2a <- Individual_t117 %>%
group_by(AGLOMERADO) %>%
summarise(Poblacion = sum(PONDERA),
Ocupados = sum(PONDERA[ESTADO == 1]),
Desocupados = sum(PONDERA[ESTADO == 2]),
PEA = Ocupados + Desocupados,
Ocupados_demand = sum(PONDERA[ESTADO == 1 & PP03J == 1]),
Suboc_demandante = sum(PONDERA[ESTADO == 1 & INTENSI == 1 & PP03J == 1]),
Suboc_no_demand = sum(PONDERA[ESTADO == 1 & INTENSI == 1 & PP03J %in% c(2, 9)]),
Subocupados = Suboc_demandante + Suboc_no_demand,
'Tasa Actividad' = PEA/Poblacion,
'Tasa Empleo' = Ocupados/Poblacion,
'Tasa Desocupacion' = Desocupados/PEA,
'Tasa ocupados demandantes' = Ocupados_demand/PEA,
'Tasa Subocupación' = Subocupados/PEA,
'Tasa Subocupación demandante' = Suboc_demandante/PEA,
'Tasa Subocupación no demandante' = Suboc_no_demand/PEA)
Cuadro_1.2a
Cuadro_1.2a <- Cuadro_1.2a %>%
select(-c(2:9)) %>% # Eliminamos las variables de nivel
left_join(.,Aglom) %>% # Agregamos el nombre de los aglomerados, que teniamos en otro DF
select(Nom_Aglo,everything(.),-AGLOMERADO) #Eliminamos el código de los aglomerados
Joining, by = "AGLOMERADO"
Cuadro_1.2a
Exportar resultados a Excel
Como vieramos en la clase 1, la función write.xlsx de la libreria openxlsx nos permite exportar los dos dataframes a un mismo archivo. Cabe aclarar que existen numerosas funciones y librerías alternativas para exportar resultados a un excel. En este caso, optamos por openxlsx ya que resulta una de las más sencillas para exportar rapidamente los resultados. Otras librerías permiten también dar formato a las tablas que se exportan, definir si deseamos sobreescribir archivos en caso de que ya existan, etc.
Lista_a_exportar <- list("Cuadro 1.1" = Cuadro_1.1a,
"Cuadro 1.2" = Cuadro_1.2a)
write.xlsx(Lista_a_exportar,"../Resultados/Informe Mercado de Trabajo.xlsx")
LS0tCnRpdGxlOiAiVXRpbGl6YWNpw7NuIGRlbCBsZW5ndWFqZSBSIHBhcmEgYXBsaWNhY2nDs24gZW4gbGEgRW5jdWVzdGEgUGVybWFuZW50ZSBkZSBIb2dhcmVzIgpzdWJ0aXRsZTogIkNsYXNlIDIgLSBNYW5pcHVsYWNpb24gZGUgQmFzZXMgZGUgRGF0b3MgeSBNZXJjYWRvIGRlIFRyYWJham8iCmF1dGhvcjogIkd1aWRvIFdla3NsZXIiCmRhdGU6ICIxMC8xMC8yMDE4IgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHllcwogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IHllcwotLS0KKkxhcyBub3RhcyBkZWwgcHJlc2VudGUgY3Vyc28gZnVlcm9uIGVsYWJvcmFkYXMgb3JpZ2luYWxtZW50ZSBwb3IgRGllZ28gS296bG93c2tpIHkgR3VpZG8gV2Vrc2xlci4gRW4gbGFzIHN1Y2VzaXZhcyBtb2RpZmljYWNpb25lcyBjb2xhYm9yYXJvbjogTmF0c3VtaSBTaG9raWRhIHkgTWF0w61hcyBMaW9uaSogICAgICAgICAgICAgICAgICAgICAgCgoKPiBSZWluaWNpYXIgUgoKIyBNYW5pcHVsYWNpw7NuIGRlIEJhc2VzIGRlIERhdG9zCiAgICAgICAgIAoKQSBsbyBsYXJnbyBkZSBlc3RhIGNsYXNlLCB0cmFiYWphcmVtb3MgY29uIGVsIHBhcXVldGUgW1RpZHl2ZXJzZV0oaHR0cHM6Ly93d3cudGlkeXZlcnNlLm9yZy8pLiBFbCBtaXNtbyBhZ3J1cGEgdW5hIHNlcmllIGRlIHBhcXVldGVzIHF1ZSB0aWVuZW4gdW5hIG1pc21hIGzDs2dpY2EgZW4gc3UgZGlzZcOxbyB5IHBvciBlbmRlIGZ1bmNpb25hbiBlbiBhcm1vbsOtYS4gICAgIApFbnRyZSBlbGxvcyB1c2FyZW1vcyBwcmluY2lwYWxtZW50ZSBfX2RwbHlyX18geSBfX3RpZHlyX18gcGFyYSByZWFsaXphciB0cmFuc2Zvcm1hY2lvbmVzIHNvYnJlIG51ZXN0cm8gc2V0IGRlIGRhdG9zLCB5IF9fZ2dwbG90X18gcGFyYSByZWFsaXphciBncsOhZmljb3MgKMOpc3RlIMO6bHRpbW8gc2UgdmVyw6EgZW4gbGEgY2xhc2UgMykuCgpBIGNvbnRpbnVhY2nDs24gY2FyZ2Ftb3MgbGEgbGlicmVyw61hIGEgbnVlc3RybyBhbWJpZW50ZS4gUGFyYSBlbGxvIGRlYmUgZXN0YXIgcHJldmlhbWVudGUgaW5zdGFsYWRhIGVuIG51ZXN0cmEgcGMuCmBgYHtyLCB3YXJuaW5nPUZBTFNFLG1lc3NhZ2U9RkFMU0V9CmxpYnJhcnkodGlkeXZlcnNlKQpgYGAKClBhcmEgbW9zdHJhciBlbCBmdW5jaW9uYW1pZW50byBiw6FzaWNvIGRlIHR5ZHl2ZXJzZSByZXRvbWFyZW1vcyBlbCBlamVtcGxvIGRlIGxhIGNsYXNlIDEsIGNvbiBsbyBjdWFsIHZvbHZlbW9zIGEgY3JlYXIgZWwgc2V0IGRlIGRhdG9zIGRlbCBbSW5kaWNlIGRlIHNhbGFyaW9zXShodHRwOi8vd3d3LmluZGVjLmdvYi5hci9iYWphckN1YWRyb0VzdGFkaXN0aWNvLmFzcD9pZGM9NDAyMEIzMzQ0MDYwOTQ2MjY1NDU0MkJEMEJDMzIwRjE1MjNEQTBEQzUyQzM5NjIwMURCNERENTg2MUZGRURDOUFEMTQzNjY4MUFDODQxNzkpLgpgYGB7cn0KSU5ESUNFICA8LSBjKDEwMCwgICAxMDAsICAgMTAwLAogICAgICAgICAgICAgMTAxLjgsIDEwMS4yLCAxMDAuNzMsCiAgICAgICAgICAgICAxMDIuOSwgMTAyLjQsIDEwMy4yKQoKRkVDSEEgIDwtICBjKCJPY3QtMTYiLCAiT2N0LTE2IiwgIk9jdC0xNiIsCiAgICAgICAgICAgICAiTm92LTE2IiwgIk5vdi0xNiIsICJOb3YtMTYiLAogICAgICAgICAgICAgIkRpYy0xNiIsICJEaWMtMTYiLCAiRGljLTE2IikKCgpHUlVQTyAgPC0gIGMoIlByaXZhZG9fUmVnaXN0cmFkbyIsIlDDumJsaWNvIiwiUHJpdmFkb19Ob19SZWdpc3RyYWRvIiwKICAgICAgICAgICAgICJQcml2YWRvX1JlZ2lzdHJhZG8iLCJQw7pibGljbyIsIlByaXZhZG9fTm9fUmVnaXN0cmFkbyIsCiAgICAgICAgICAgICAiUHJpdmFkb19SZWdpc3RyYWRvIiwiUMO6YmxpY28iLCJQcml2YWRvX05vX1JlZ2lzdHJhZG8iKQoKRGF0b3MgPC0gZGF0YS5mcmFtZShJTkRJQ0UsIEZFQ0hBLCBHUlVQTykKCgpgYGAKCgojIyBEcGx5cgoKRWwgY2FyYWN0ZXIgcHJpbmNpcGFsIHBhcmEgdXRpbGl6YXIgZXN0ZSBwYXF1ZXRlIGVzIGBgYCU+JWBgYCAsIF9waXBlXyAoZGUgdHViZXLDrWEpLgoKTG9zIGBgYCU+JWBgYCB0b21hbiBlbCBzZXQgZGUgZGF0b3MgYSBzdSBpenF1aWVyZGEsIHkgbG9zIHRyYW5zZm9ybWFuIG1lZGlhbnRlIGxvcyBjb21hbmRvcyBhIHN1IGRlcmVjaGEsIGVuIGxvcyBjdWFsZXMgbG9zIGVsZW1lbnRvcyBkZSBsYSBpenF1aWVyZGEgZXN0w6FuIGltcGzDrWNpdG9zLiBFcyBkZWPDrXIsIHF1ZSB1bmEgdmV6IGVzcGVjw61maWNhZG8gZWwgRGF0YUZyYW1lIGNvbiBlbCBjdWFsIHNlIHRyYWJhamEsIG5vIHNlcsOhIG5lY2VzYXJpbyBub21icmFybG8gbnVldmFtZW50ZSBwYXJhIHJlZmVyaXJzZSBhIHVuYSBkZXRlcm1pbmFkYSB2YXJpYWJsZS9jb2x1bW5hIGRlbCBtaXNtby4KClZlYW1vcyBsYXMgcHJpbmNpcGFsZXMgZnVuY2lvbmVzIHF1ZSBwdWVkZW4gdXRpbGl6YXJzZSBjb24gbGEgbMOzZ2ljYSBkZSBlc3RlIHBhcXVldGU6CgojIyMgZmlsdGVyCgpQZXJtaXRlIGZpbHRyYXIgbGEgdGFibGEgYWNvcmRlIGFsIGN1bXBsaW1pZW50byBkZSBjb25kaWNpb25lcyBsw7NnaWNhcwogCmBgYHtyfQpEYXRvcyAlPiUgCiAgZmlsdGVyKElORElDRT4xMDEgLCBHUlVQTyA9PSAiUHJpdmFkb19SZWdpc3RyYWRvIikKCmBgYApOw7N0ZXNlIHF1ZSBlbiBlc3RlIGNhc28gYWwgc2VwYXJhciBjb24gdW5hICBgYGAsYGBgIGxhcyBjb25kaWNpb25lcyBzZSBleGlnZSBlbCBjdW1wbGltaWVudG8gZGUgYW1iYXMuIEVuIGNhc28gZGUgZGVzZWFyIHF1ZSBzZSBjdW1wbGEgdW5hIHNvbGEgY29uZGljacOzbiBkZWJlIHV0aWxpemFyc2UgZWwgY2FyYWN0ZXIgYGBgfGBgYApgYGB7cn0KRGF0b3MgJT4lIAogIGZpbHRlcihJTkRJQ0U+MTAxIHwgR1JVUE8gPT0gIlByaXZhZG9fUmVnaXN0cmFkbyIpCmBgYAoKIyMjIHJlbmFtZQpQZXJtaXRlIHJlbm9tYnJhciB1bmEgY29sdW1uYSBkZSBsYSB0YWJsYS4gRnVuY2lvbmEgZGUgbGEgc2lndWllbnRlIG1hbmVyYTogCiBgYGBEYXRhICU+JSByZW5hbWUoIG51ZXZvX25vbWJyZSA9IHZpZWpvX25vbWJyZSApYGBgIApgYGB7cn0KRGF0b3MgJT4lIAogIHJlbmFtZShQZXJpb2RvID0gRkVDSEEpCmBgYApOw7N0ZXNlIHF1ZSBhIGRpZmVyZW5jaWEgZGVsIGVqZW1wbG8gZGUgbGEgZnVuY2nDs24gX19maWx0ZXJfXyBkb25kZSB1dGlsaXrDoWJhbW9zIF9fPT1fXyBwYXJhIGNvbXByb2JhciB1bmEgY29uZGljacOzbiBsw7NnaWNhLCBlbiBlc3RlIGNhc28gc2UgdXRpbGl6YSBzw7NsbyB1biBfXz1fXyB5YSBxdWUgbG8gZXN0YW1vcyBoYWNpZW5kbyBlcyBfYXNpZ25hcl8gdW4gbm9tYnJlLgoKIyMjIG11dGF0ZQpQZXJtaXRlIGFncmVnYXIgdW5hIHZhcmlhYmxlIGEgbGEgdGFibGEgKGVzcGVjaWZpY2FuZG8gZWwgbm9tYnJlIHF1ZSB0b21hcsOhIGVzdGEpLCBxdWUgcHVlZGUgc2VyIGVsIHJlc3VsdGFkbyBkZSBvcGVyYWNpb25lcyBzb2JyZSBvdHJhcyB2YXJpYWJsZXMgZGUgbGEgbWlzbWEgdGFibGEuICAgICAgIAoKRW4gY2FzbyBkZSBlc3BlY2lmaWNhciBlbCBub21icmUgZGUgdW5hIGNvbHVtbmEgZXhpc3RlbnRlLCBlbCByZXN1bHRhZG8gZGUgbGEgb3BlcmFjacOzbiByZWFsaXphZGEgInNvYnJlc2NyaWJpcsOhIiBsYSBpbmZvcm1hY2nDs24gZGUgbGEgY29sdW1uYSBjb24gZGljaG8gbm9tYnJlCmBgYHtyfQpEYXRvcyA8LSBEYXRvcyAlPiUgCiAgbXV0YXRlKERvYmxlPUlORElDRSoyKQpEYXRvcwpgYGAKCiMjIyBjYXNlX3doZW4KUGVybWl0ZSBkZWZpbmlyIHVuYSB2YXJpYWJsZSwgbGEgY3VhbCB0b21hIHVuIHZhbG9yIHBhcnRpY3VsYXIgcGFyYSBjYWRhIGNvbmRpY2nDs24gZXN0YWJsZWNpZGEuIEVuIGNhc28gZGUgbm8gY3VtcGxpciBuaW5ndW5hIGRlIGxhcyBjb25kaWNpb25lcyBlc3RhYmxlY2lkYXMgbGEgdmFyaWFibGUgdG9tYXJhIHZhbG9yIF9fTkFfXy4gICAgICAgICAKU3UgZnVuY2lvbmFtaWVudG8gZXMgZWwgc2lndWllbnRlOiAgICAgIApgYGBjYXNlX3doZW4oY29uZGljaW9uMSB+ICJWYWxvcjEiLGNvbmRpY2lvbjIgfiAiVmFsb3IyIixjb25kaWNpb24zIH4gIlZhbG9yMyIpYGBgCgpgYGB7cn0KRGF0b3MgPC0gRGF0b3MgJT4lIAogIG11dGF0ZShDYXNvX2N1YW5kbyA9IGNhc2Vfd2hlbihHUlVQTyA9PSAiUHJpdmFkb19SZWdpc3RyYWRvIiAgIH4gSU5ESUNFKjIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEdSVVBPID09ICJQw7pibGljbyIgICAgICAgICAgICAgIH4gSU5ESUNFKjMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEdSVVBPID09ICJQcml2YWRvX05vX1JlZ2lzdHJhZG8ifiBJTkRJQ0UqNSkpCkRhdG9zCmBgYAoKIyMjIHNlbGVjdApQZXJtaXRlIGVzcGVjaWZpY2FyIGxhIHNlcmllIGRlIGNvbHVtbmFzIHF1ZSBzZSBkZXNlYSBjb25zZXJ2YXIgZGUgdW4gRGF0YUZyYW1lLiBUYW1iacOpbiBwdWVkZW4gZXNwZWNpZmljYXJzZSBsYXMgY29sdW1uYXMgcXVlIHNlIGRlc2VhbiBkZXNjYXJ0YXIgKGFncmVnw6FuZG9sZXMgdW4gXy1fKS4gTXV5IMO6dGlsIHBhcmEgYWdpbGl6YXIgZWwgdHJhYmFqbyBlbiBiYXNlcyBkZSBkYXRvcyBkZSBncmFuIHRhbWHDsW8uCmBgYHtyfQpEYXRvczIgPC0gRGF0b3MgJT4lIAogIHNlbGVjdChJTkRJQ0UsIEZFQ0hBLCBHUlVQTykKRGF0b3MyCgpEYXRvcyA8LSBEYXRvcyAlPiUgCiAgc2VsZWN0KC1jKERvYmxlLENhc29fY3VhbmRvKSkKRGF0b3MKYGBgCgojIyMgYXJyYW5nZQpQZXJtaXRlIG9yZGVuYXIgbGEgdGFibGEgcG9yIGxvcyB2YWxvcmVzIGRlIGRldGVybWluYWRhL3MgdmFyaWFibGUvcy4gRXMgw7p0aWwgY3VhbmRvIGx1ZWdvIGRlYmVuIGhhY2Vyc2Ugb3RyYXMgb3BlcmFjaW9uZXMgcXVlIHJlcXVpZXJhbiBkZWwgb3JkZW5hbWllbnRvIGRlIGxhIHRhYmxhCmBgYHtyfQpEYXRvcyA8LSBEYXRvcyAlPiUgCiAgYXJyYW5nZShHUlVQTywgSU5ESUNFKQpEYXRvcwpgYGAKCiMjIyBzdW1tYXJpc2UKQ3JlYSB1bmEgbnVldmEgdGFibGEgcXVlIHJlc3VtZSBsYSBpbmZvcm1hY2nDs24gb3JpZ2luYWwuIFBhcmEgZWxsbywgZGVmaW5pbW9zIGxhcyB2YXJpYWJsZXMgZGUgcmVzdW1lbiB5IGxhcyBmb3JtYXMgZGUgYWdyZWdhY2nDs24uCmBgYHtyfQpEYXRvcyAlPiUgCiAgc3VtbWFyaXNlKEluZHByb20gPSBtZWFuKElORElDRSkpCgpgYGAKCiMjIyBncm91cF9ieQpFc3RhIGZ1bmNpw7NuIHBlcm1pdGUgcmVhbGl6YXIgb3BlcmFjaW9uZXMgZGUgZm9ybWEgYWdydXBhZGEuIExvIHF1ZSBoYWNlIGxhIGZ1bmNpw7NuIGVzICJzZXBhcmFyIiBhIGxhIHRhYmxhIHNlZ8O6biBsb3MgdmFsb3JlcyBkZSBsYSB2YXJpYWJsZSBpbmRpY2FkYSB5IHJlYWxpemFyIGxhcyBvcGVyYWNpb25lcyBxdWUgc2UgZXNwZWNpZmljYW4gYSAgY29udGludWFjacOzbiwgZGUgbWFuZXJhIGluZGVwZW5kaWVudGUgcGFyYSBjYWRhIHVuYSBkZSBsYXMgInN1YnRhYmxhcyIuIEVuIG51ZXN0cm8gZWplbXBsbywgc2Vyw61hIMO6dGlsIHBhcmEgY2FsY3VsYXIgZWwgcHJvbWVkaW8gZGUgbG9zIGluZGljZXMgcG9yIF9GZWNoYV8gCmBgYHtyfQpEYXRvcyAlPiUgCiAgZ3JvdXBfYnkoRkVDSEEpICU+JQogIHN1bW1hcmlzZShJbmRwcm9tID0gbWVhbihJTkRJQ0UpKQpgYGAKTm90ZXNlIHF1ZSBsb3MgYGBgJT4lYGBgIHB1ZWRlbiB1c2Fyc2UgZW5jYWRlbmFkb3MgcGFyYSByZWFsaXphciBudW1lcm9zb3MgcHJvY2VkaW1pZW50b3Mgc29icmUgdW4gZGF0YWZyYW1lIG9yaWdpbmFsLgpWZWFtb3MgdW4gZWplbXBsbyBjb24gbXVsdGlwbGVzIGVuY2FkZW5hbWlldG5vcwpgYGB7cn0KRW5jYWRlbmFkbyA8LSBEYXRvcyAlPiUgCiAgZmlsdGVyKEdSVVBPID09ICJQcml2YWRvX1JlZ2lzdHJhZG8iKSAlPiUgCiAgcmVuYW1lKFBlcmlvZG8gPSBGRUNIQSkgJT4lIAogIG11dGF0ZShEb2JsZSA9IElORElDRSoyKSAlPiUgCiAgc2VsZWN0KC1JTkRJQ0UpCiAgCmBgYAoKIyMgSm9pbnMKCk90cmEgaW1wbGVtZW50YWNpw7NuIG11eSBpbXBvcnRhbnRlIGRlbCBwYXF1ZXRlIGRwbHlyIHNvbiBsYXMgZnVuY2lvbmVzIHBhcmEgdW5pciB0YWJsYXMgKGpvaW5zKQoKCiFbZnVlbnRlOiBodHRwOi8vcnN0dWRpby1wdWJzLXN0YXRpYy5zMy5hbWF6b25hd3MuY29tLzIyNzE3MV82MThlYmRjZTBiOWQ0NGYzYWY2NTcwMGU4MzM1OTNkYi5odG1sXShqb2lucy5wbmcpICAgICAgICAgCgojIyNsZWZ0X2pvaW4gICAgCgpWZWFtb3MgdW4gZWplbXBsbyBkZSBsYSBmdW5jacOzbiBfX2xlZnRfam9pbl9fICh1bmEgZGUgbGFzIG3DoXMgdXRpbGl6YWRhcyBlbiBsYSBwcsOhY3RpY2EpLiAgICAgICAKUGFyYSBlbGxvIGNyZWFyZW1vcyBwcmV2aWFtZW50ZSB1biBEYXRhZnJhbWUgcXVlIGNvbnRlbmdhIHVuIFBvbmRlcmFkb3IgcGFyYSBjYWRhIHVubyBkZSBsb3MgR3J1cG9zIGRlbCBEYXRhZnJhbWUgX0RhdG9zXy4gQXByb3ZlY2hhcmVtb3MgZWwgZWplbXBsbyBwYXJhIGludHJvZHVjaXIgbGEgZnVuY2nDs24gX193ZWlndGhlZC5tZWFuX18sIHkgYXPDrSBjYWxjdWxhciB1biBJbmRpY2UgUG9uZGVyYWRvLgoKYGBge3J9ClBvbmRlcmFkb3JlcyA8LSBkYXRhLmZyYW1lKEdSVVBPID0gYygiUHJpdmFkb19SZWdpc3RyYWRvIiwiUMO6YmxpY28iLCJQcml2YWRvX05vX1JlZ2lzdHJhZG8iKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFBPTkRFUkFET1IgPSBjKDUwLjE2LDI5LjkxLDE5LjkzKSkKUG9uZGVyYWRvcmVzCmBgYAoKYGBge3J9CkRhdG9zX2pvaW4gPC0gRGF0b3MgJT4lIAogIGxlZnRfam9pbiguLFBvbmRlcmFkb3JlcywgYnkgPSAiR1JVUE8iKQpEYXRvc19qb2luCgpEYXRvc19JbmRpY2VfR3JhbCA8LSBEYXRvc19qb2luICU+JSAKICBncm91cF9ieShGRUNIQSkgJT4lIAogIHN1bW1hcmlzZShJbmRpY2VfR3JhbCA9IHdlaWdodGVkLm1lYW4oSU5ESUNFLHcgPSBQT05ERVJBRE9SKSkKCkRhdG9zX0luZGljZV9HcmFsCmBgYAoKIyMgVGlkeXIKCkVsIHBhcXVldGUgdGlkeXIgZXN0YSBwZW5zYWRvIHBhcmEgZmFjaWxpdGFyIGVsIGVtcHJvbGlqYW1pZW50byBkZSBsb3MgZGF0b3MuCgpfX0dhdGhlcl9fIGVzIHVuYSBmdW5jacOzbiBxdWUgbm9zIHBlcm1pdGUgcGFzYXIgbG9zIGRhdG9zIGRlIGZvcm1hIGhvcml6b250YWwgYSB1bmEgZm9ybWEgdmVydGljYWwuIAoKX19zcHJlYWRfXyBlcyB1bmEgZnVuY2nDs24gcXVlIG5vcyBwZXJtaXRlIHBhc2FyIGxvcyBkYXRvcyBkZSBmb3JtYSB2ZXJ0aWNhbCBhIHVuYSBmb3JtYSBob3Jpem9udGFsLgoKIVtmdWVudGU6IGh0dHA6Ly93d3cuZ2lzLWJsb2cuY29tL2RhdGEtbWFuYWdlbWVudC13aXRoLXItdGlkeXItcGFydC0xL10oc3ByZWFkVlNnYXRoZXIucG5nKQoKClJldG9tZW1vcyBlbCBEYXRhZnJhbWUgb3JpZ2luYWwgcGFyYSBtb3N0cmFyIGNvbW8gb3BlcmFuIGVzdGFzIGZ1bmNpb25lczoKYGBge3J9CkRhdG9zCmBgYAojIyMgR2F0aGVyIHkgU3ByZWFkCmBgYHtyfQpEYXRvc19TcHJlYWQgPC0gRGF0b3MgJT4lIAogIHNwcmVhZCguLCAgICAgICAjIGVsIC4gbGxhbWEgYSBsbyBxdWUgZXN0YSBhdHJhcyBkZWwgJT4lIAogIGtleSA9IEdSVVBPLCAgICAjbGEgbGxhdmUgZXMgbGEgdmFyaWFibGUgY3V5b3MgdmFsb3JlcyB2YW4gYSBkYXIgbG9zIG5vbWJyZXMgZGUgY29sdW1uYXMKICB2YWx1ZSA9IElORElDRSkgI2xvcyB2YWxvcmVzIGNvbiBxdWUgc2UgbGxlbmFuIGxhcyBjZWxkYXMKCkRhdG9zX1NwcmVhZCAgCmBgYAoKYGBge3J9CiMjTGEgZnVuY2nDs24gb3B1ZXN0YSAoZ2F0aGVyKSBub3MgcGVybWl0ZSBvYnRlbmVyIHVuIGRhdGFmcmFtZSBjb21vIGVsIG9yaWdpbmFsIHBhcnRpZW5kbyBkZSB1biBkYXRhZnJhbWUgY29tbyBlbCByZWNpw6luIGNvbnN0cnVpZG8uCiAgCkRhdG9zX2dhdGhlciA8LSBEYXRvc19TcHJlYWQgJT4lICAKICBnYXRoZXIoLiwgICAgICAgICAjIGVsIC4gbGxhbWEgYSBsbyBxdWUgZXN0YSBhdHJhcyBkZWwgJT4lIAogICBrZXkgICA9IEdSVVBPLCAgICMgY29tbyBzZSBsbGFtYXLDoSBsYSB2YXJpYWJsZSBxdWUgdG9tYSBsb3Mgbm9tYnJlcyBkZSBsYXMgY29sdW1uYXMgCiAgIHZhbHVlID0gSU5ESUNFLCAgIyBjb21vIHNlIGxsYW1hcsOhIGxhIHZhcmlhYmxlIHF1ZSB0b21hIGxvcyB2YWxvcmVzIGRlIGxhcyBjb2x1bW5hcwogICAyOjQpICAgICAgICAgICAgICNsZSBpbmRpY28gcXVlIGNvbHVtbmFzIGp1bnRhcgoKRGF0b3NfZ2F0aGVyCmBgYAoKCiMgTWVyY2FkbyBkZSBUcmFiYWpvCkx1ZWdvIGRlIGFib3JkYXIgbGFzIHByaW5jaXBhbGVzIGZ1bmNpb25lcyBuZWNlc2FyaWFzIHBhcmEgb3BlcmFyIHNvYnJlIGxhcyBiYXNlcyBkZSBkYXRvcywgdHJhYmFqYXJlbW9zIGEgY29udGludWFjacOzbiBjb24gbGEgRVBILCByZWFsaXphbmRvIGVsIGVqZXJjaWNpbyBkZSBjYWxjdWxhciBsYXMgdGFzYXMgYsOhc2ljYXMgZGVsIG1lcmNhZG8gZGUgdHJhYmFqbywgcmVwbGljYW5kbyBsb3MgQ3VhZHJvcyAxLjEgeSAxLjIsIGRlbCBbSW5mb3JtZSB0w6ljbmljbyBlbGFib3JhZG8gcG9yIEVQSC1JTkRFQ10oaHR0cDovL3d3dy5pbmRlYy5nb2IuYXIvdXBsb2Fkcy9pbmZvcm1lc2RlcHJlbnNhL0VQSF9jb250XzF0cmltMTcucGRmKS4KCkVuIGxhIGNhcnBldGEgZGUgX0ZVRU5URVNfIGRlbCBjdXJzbywgc2UgZW5jdWVudHJhIHVuIGFyY2hpdm8gZGUgKiJFUEhfQ29uY3BldG9zX0FjdGl2aWRhZCIqIHF1ZSBjb250aWVuZSBsYXMgZGVmaW5pY2lvbmVzIGRlIGxvcyBFc3RhZG9zIG9jdXBhY2lvbmFsZXMgYSBwYXJ0aXIgZGUgbG9zIGN1YWxlcyBzZSBjb25zdHJ1eWVuIGxhcyB0YXNhcyBiw6FzaWNhcy4KCkNhcmdhbW9zIGxhIGxpYnJlcsOtYSBxdWUgdXNhcmVtb3MgcGFyYSBsZWVyIHkgZXNjcmliaXIgYXJjaGl2b3MgZW4gZXhjZWwuIApgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeShvcGVueGxzeCkgCmBgYAoKCiMjIyMgQ2FyZ2EgZGUgSW5mb3JtYWNpb24KCkxhIGZ1bmNpw7NuIF9fbGlzdC5maWxlc19fIG5vcyBwZXJtaXRlIG9ic2VydmFyIGxvcyBhcmNoaXZvcyBxdWUgY29udGllbmUgdW5hIGRldGVybWluYWRhIGNhcnBldGEgICAgICAgICAgICAgCgpgYGB7cn0KbGlzdC5maWxlcygiLi4vRnVlbnRlcy8iKQpgYGAKTGEgZnVuY2nDs24gX19yZWFkLnRhYmxlX18gbm9zIHBlcm1pdGUgbGV2YW50YXIgbG9zIGFyY2hpdm9zIGRlIGV4dGVuc2nDs24gIi50eHQiICAgICAgICAgICAgICAgCkxhIGZ1bmNpw7NuIF9fcmVhZC54bHN4X18gbm9zIHBlcm1pdGUgbGV2YW50YXIgbG9zIGFyY2hpdm9zIGRlIGV4dGVuc2nDs24gIi54bHN4IiAgICAgICAgICAgICAgICAgCgpMZXZhbnRhbW9zIGxhIGJhc2UgaW5kaXZpZHVhbCBkZWwgcHJpbWVyIHRyaW1lc3RyZSBkZSAyMDE3LCB5IHVuIGxpc3RhZG8gcXVlIGNvbnRpZW5lIGxvcyBOb21icmVzIHkgQ8OzZGlnb3MgZGUgbG9zIEFnbG9tZXJhZG9zIEVQSC4KYGBge3J9CkluZGl2aWR1YWxfdDExNyA8LQogIHJlYWQudGFibGUoIi4uL0Z1ZW50ZXMvdXN1X2luZGl2aWR1YWxfdDExNy50eHQiLAogIHNlcCA9ICI7IiwKICBkZWMgPSAiLCIsCiAgaGVhZGVyID0gVFJVRSwKICBmaWxsID0gVFJVRSApCiAgCiAgCkFnbG9tIDwtIHJlYWQueGxzeCgiLi4vRnVlbnRlcy9BZ2xvbWVyYWRvcyBFUEgueGxzeCIpCmBgYAoKCiMjIEN1YWRybyAxLjEgCgpDcmVhbW9zIHVuYSB0YWJsYSBjb24gbG9zIG5pdmVsZXMgZGU6CgotIFBvYmxhY2nDs24KLSBPY3VwYWRvcwotIERlc29jdXBhZG9zCi0gUEVBCi0gT2N1cGFkb3MgZGVtYW5kYW50ZXMKLSBTdWJvY3VwYWRvcyAoZGVtYW5kYW50ZXMsIG5vIGRlbWFuZGFudGVzIHkgdG90YWwpCgpFc3RvcyBuaXZlbGVzIG5vcyB2YW4gYSBwZXJtaXRpciBjYWxjdWxhciBsYXMgdGFzYXMgZGUgZm9ybWEgc2VuY2lsbGEuICAgICAgCgpQYXJhIG9idGVuZXIsIHBvciBlamVtcGxvIGxhIHRhc2EgZGUgZW1wbGVvLCBjb21vIGVsIGNvY2llbnRlICAkXGZyYWN7T2N1cGFkb3N9e1BvYmxhY2lvbn0kIG5lY2VzaXRhbW9zIGNhbGN1bGFyIGVsIHRvdGFsIChleHBhbmRpZG8pIGRlIGxhIHBvYmxhY2nDs24geSBkZSBsb3Mgb2N1cGFkb3MuICAgIAogCiAtIFBvYmxhY2nDs246IFNpIGNvbnRhcmFtb3MgY3VhbnRvcyByZWdpc3Ryb3MgdGllbmUgbGEgYmFzZSwgc2ltcGxlbWVudGUgdGVuZHLDrWFtb3MgZWwgbnVtZXJvIGRlIGluZGl2aWR1b3MgbXVlc3RyYWwgZGUgbGEgRVBILCBwb3IgZW5kZSBkZWJlbW9zICoqc3VtYXIgbG9zIHZhbG9yZXMgZGUgbGEgdmFyaWFibGUgUE9OREVSQSoqLCBwYXJhIGNvbnRlbXBsYXIgYSBjdWFudGFzIHBlcnNvbmFzIHJlcHJlc2VudGEgY2FkYSBpbmRpdmlkdW8gZW5jdWVzdGFkby4gCiAtIE9jdXBhZG9zOiBFbiBlc3RlIGNhc28sIGRlYmVtb3MgYWdyZWdhciB1biAqKmZpbHRybyoqIGFsIHByb2NlZGltaWVudG8gYW50ZXJpb3IsIHlhIHF1ZSB1bmljYW1lbnRlIHF1ZXJlbW9zIHN1bWFyIGxvcyBwb25kZXJhZG9yZXMgZGUgYXF1ZWxsYXMgcGVyc29uYXMgcXVlIHNlIGVuY3VlbnRyYW4gb2N1cGFkYXMuIChMYSBsw7NnaWNhIHNlcmlhOiAiU3VtYSBsb3MgdmFsb3JlcyBkZSBsYSBjb2x1bW5hIFBPTkRFUkEsIHNvbG8gcGFyYSBhcXVlbGxvcyByZWdpc3Ryb3MgZG9uZGUgZWwgRVNUQURPID09IDEiKSAgICAKIApgYGB7cn0KUG9ibGFjaW9uX29jdXBhZG9zIDwtIEluZGl2aWR1YWxfdDExNyAlPiUgCiAgc3VtbWFyaXNlKFBvYmxhY2lvbiAgICAgICAgID0gc3VtKFBPTkRFUkEpLAogICAgICAgICAgICBPY3VwYWRvcyAgICAgICAgICA9IHN1bShQT05ERVJBW0VTVEFETyA9PSAxXSkpCgpQb2JsYWNpb25fb2N1cGFkb3MKYGBgCkxhIGZ1bmNpw7NuICBgYGAgc3VtbWFyaXNlKCkgYGBgIG5vcyBwZXJtaXRlIGNyZWFyIG11bHRpcGxlcyB2YXJpYWJsZXMgZGUgcmVzdW1lbiBhbCBtaXNtbyB0aWVtcG8sICBzaW1wbGVtZW50ZSBzZXBhcmFuZG8gY29uIHVuYSBgYGAgLCBgYGAgY2FkYSB1bm8gZGUgZWxsYXMuIEEgc3UgdmV6LCBzZSBwdWVkZW4gY3JlYXIgdmFyaWFibGVzLCBhIHBhcnRpciBkZSBsYXMgdmFyaWFibGVzIGNyZWFkYXMgcG9yIGxhIHByb3BpYSBmdW5jacOzbi4gRGUgZXN0YSBmb3JtYSwgcG9kZW1vcywgZGlyZWN0YW1lbnRlIGNhbGN1bGFyIGxhICoqdGFzYSBkZSBlbXBsZW8qKiBhIHBhcnRpciBkZWwgdG90YWwgcG9ibGFjaW9uYWwgeSBkZSBvY3VwYWRvcy4gICAKYGBge3J9CkVtcGxlbyA8LSBJbmRpdmlkdWFsX3QxMTcgJT4lIAogIHN1bW1hcmlzZShQb2JsYWNpb24gICAgICAgICA9IHN1bShQT05ERVJBKSwKICAgICAgICAgICAgT2N1cGFkb3MgICAgICAgICAgPSBzdW0oUE9OREVSQVtFU1RBRE8gPT0gMV0pLAogICAgICAgICAgICBUYXNhX0VtcGxlbyAgICA9IE9jdXBhZG9zL1BvYmxhY2lvbikKCkVtcGxlbwpgYGAKClVuYSB2ZXogY2FsY3VsYWRhIGxhIHRhc2EsIGluY2x1c28gcG9kcsOtYW1vcyBkZXNlY2hhciBsYXMgdmFyaWFibGVzIGRlIG5pdmVsLCBwYXJhIGNvbnNlcnZhciB1bmljYW1lbnRlIGxhIHRhc2FzCmBgYHtyfQpFbXBsZW8gJT4lIAogIHNlbGVjdCgtKDE6MikpCmBgYAoKCkNvbiBsYSBtaXNtYSBsw7NnaWNhIGRlbCBlamVtcGxvIGFudGVyaW9yLCBwb2RlbW9zIGNvbnN0cnVpciBjb24gc29sbyB1bmEgcG9yY2nDs24gZGUgY8OzZGlnbyBlbCBDdWFkcm8gMS4xIGRlbCBbSW5mb3JtZSB0w6ljbmljbyBkZSBNZXJjYWRvIGRlIHRyYWJham8gRVBILUlOREVDXShodHRwOi8vd3d3LmluZGVjLmdvYi5hci91cGxvYWRzL2luZm9ybWVzZGVwcmVuc2EvRVBIX2NvbnRfMXRyaW0xNy5wZGYpLiAKYGBge3J9CiMjIyNDdWFkcm8gMS4xIFByaW5jaXBhbGVzIGluZGljYWRvcmVzLiBUb3RhbCAzMSBhZ2xvbWVyYWRvcyB1CgpDdWFkcm9fMS4xYSA8LSBJbmRpdmlkdWFsX3QxMTcgJT4lIAogIHN1bW1hcmlzZShQb2JsYWNpb24gICAgICAgICA9IHN1bShQT05ERVJBKSwKICAgICAgICAgICAgT2N1cGFkb3MgICAgICAgICAgPSBzdW0oUE9OREVSQVtFU1RBRE8gPT0gMV0pLAogICAgICAgICAgICBEZXNvY3VwYWRvcyAgICAgICA9IHN1bShQT05ERVJBW0VTVEFETyA9PSAyXSksCiAgICAgICAgICAgIFBFQSAgICAgICAgICAgICAgID0gT2N1cGFkb3MgKyBEZXNvY3VwYWRvcywKICAgICAgICAgICAgT2N1cGFkb3NfZGVtYW5kICAgPSBzdW0oUE9OREVSQVtFU1RBRE8gPT0gMSAmIFBQMDNKID09MV0pLAogICAgICAgICAgICBTdWJvY19kZW1hbmRhbnRlICA9IHN1bShQT05ERVJBW0VTVEFETyA9PSAxICYgSU5URU5TSSA9PTEgJiBQUDAzSj09MV0pLAogICAgICAgICAgICBTdWJvY19ub19kZW1hbmQgICA9IHN1bShQT05ERVJBW0VTVEFETyA9PSAxICYgSU5URU5TSSA9PTEgJiBQUDAzSiAlaW4lIGMoMiw5KV0pLAogICAgICAgICAgICBTdWJvY3VwYWRvcyAgICAgICA9IFN1Ym9jX2RlbWFuZGFudGUgKyBTdWJvY19ub19kZW1hbmQgLAojIFRhbWJpw6luIHBvZGVtb3MgbGxhbWFyIGEgbGFzIHZhcmlhYmxlcyBlbnRyZSBjb21pbGxhcywgaW5jbHV5ZW5kbyBub21icmVzIGNvbXB1ZXN0b3MKIyBBIHN1IHZleiwgcG9kZW1vcyB1dGlsaXphciBsYSB2YXJpYWJsZSByZWNpw6luIGNyZWFkYSBlbiBsYSBkZWZpbmNpw7NuIGRlIG90cmEgdmFyaWJsZQogICAgICAgICAgICAnVGFzYSBBY3RpdmlkYWQnICAgICAgICAgICAgICAgICAgPSBQRUEvUG9ibGFjaW9uLAogICAgICAgICAgICAnVGFzYSBFbXBsZW8nICAgICAgICAgICAgICAgICAgICAgPSBPY3VwYWRvcy9Qb2JsYWNpb24sCiAgICAgICAgICAgICdUYXNhIERlc29jdXBhY2lvbicgICAgICAgICAgICAgICA9IERlc29jdXBhZG9zL1BFQSwKICAgICAgICAgICAgJ1Rhc2Egb2N1cGFkb3MgZGVtYW5kYW50ZXMnICAgICAgID0gT2N1cGFkb3NfZGVtYW5kL1BFQSwKICAgICAgICAgICAgJ1Rhc2EgU3Vib2N1cGFjacOzbicgICAgICAgICAgICAgICA9IFN1Ym9jdXBhZG9zL1BFQSwKICAgICAgICAgICAgJ1Rhc2EgU3Vib2N1cGFjacOzbiBkZW1hbmRhbnRlJyAgICA9IFN1Ym9jX2RlbWFuZGFudGUvUEVBLAogICAgICAgICAgICAnVGFzYSBTdWJvY3VwYWNpw7NuIG5vIGRlbWFuZGFudGUnID0gU3Vib2Nfbm9fZGVtYW5kL1BFQSkgCkN1YWRyb18xLjFhIApgYGAKClVuYSB2ZXogcXVlIGNhbGN1bGFtb3MgbGFzIHRhc2FzLCBwb2RlbW9zIGJvcnJhciBsb3Mgbml2ZWxlcyBwb2JsYWNpb25hbGVzIGNvbiB1biBfX3NlbGVjdF9fCmBgYHtyfQpDdWFkcm9fMS4xYSA8LSBDdWFkcm9fMS4xYSAlPiUgCiAgc2VsZWN0KC1jKDE6OCkpCgpDdWFkcm9fMS4xYQpgYGAKCkNvbiBfX2dhdGhlcl9fIHBvZGVtb3MgZGFyIHZ1ZWx0YSBsYSB0YWJsYSBwYXJhIHF1ZSBxdWVkZSBjb21vIGVuIGxhIHB1YmxpY2FjacOzbgoKYGBge3J9CgpDdWFkcm9fMS4xYSA8LSBDdWFkcm9fMS4xYSAlPiUgCiAgZ2F0aGVyKFRhc2FzLCBWYWxvciwgMTpuY29sKC4pKQoKCkN1YWRyb18xLjFhCmBgYAoKRW4gY2FzbyBkZSBxdWVyZXIgZXhwcmVzYXIgbG9zIHJlc3VsdGFkb3MgY29tbyBwb3JjZW50YWplcywgdXRpbGl6YW1vcyBsYSBmdW5jacOzbiBfX3NwcmludGZfXy4gUGFyYSBlbGxvIGRlYmVtb3MgdXRpbGl6YXIgX19tdXRhdGVfXyBwYXJhIHRyYW5zZm9ybWFyIGxhIGNvbHVtbmEgVmFsb3IuCmBgYHtyfQpDdWFkcm9fMS4xYSA8LSBDdWFkcm9fMS4xYSAlPiUgCiAgbXV0YXRlKFZhbG9yID0gc3ByaW50ZigiJTEuMWYlJSIsIDEwMCpWYWxvcikpCgpDdWFkcm9fMS4xYQpgYGAKTsOzdGVzZSBxdWUgZW4gZXN0ZSBjYXNvLCBwYXJhIHBvZGVyIGHDsWFkaXIgZWwgJSwgbGEgZnVuY2nDs24gdHJhbnNmb3JtYSBhIGxhIHZhcmlhYmxlIGVuIHVuIENoYXJhY3RlciwgcG9yIGVuZGUgZGViZSB0ZW5lcnNlIGVuIGN1ZW50YSBxdWUgc2UgcGllcmRlIGxhIGluZm9ybWFjacOzbiBkZWwgbnVtZXJvIGNvbXBsZXRvLgoKIyMgQ3VhZHJvIDEuMgoKRW4gZXN0ZSBjYXNvLCBwb2RlbW9zIHZlciBxdWUgc2ltcGxlbWVudGUgYWdyZWdhbmRvIGxhIGZ1bmNpw7NuIF9fZ3JvdXBfYnlfXyBwb2RlbW9zIHJlcGxpY2FyIGVsIHByb2NlZGltaWVudG8gcGFyYSBjYWRhIHVubyBkZSBsb3MgYWdsb21lcmFkb3MuIFkgYSBzdSB2ZXosIHBvZGVtb3MgcmVhbGl6YXIgZW4gdW4gc29sbyBwYXNvIGxvcyBhcnJlZ2xvcyBwb3N0ZXJpb3JlcyBzb2JyZSBudWVzdHJhIHRhYmxhLgoKYGBge3J9CkN1YWRyb18xLjJhIDwtIEluZGl2aWR1YWxfdDExNyAlPiUgCiAgZ3JvdXBfYnkoQUdMT01FUkFETykgJT4lIAogIHN1bW1hcmlzZShQb2JsYWNpb24gICAgICAgICA9IHN1bShQT05ERVJBKSwKICAgICAgICAgICAgT2N1cGFkb3MgICAgICAgICAgPSBzdW0oUE9OREVSQVtFU1RBRE8gPT0gMV0pLAogICAgICAgICAgICBEZXNvY3VwYWRvcyAgICAgICA9IHN1bShQT05ERVJBW0VTVEFETyA9PSAyXSksCiAgICAgICAgICAgIFBFQSAgICAgICAgICAgICAgID0gT2N1cGFkb3MgKyBEZXNvY3VwYWRvcywKICAgICAgICAgICAgT2N1cGFkb3NfZGVtYW5kICAgPSBzdW0oUE9OREVSQVtFU1RBRE8gPT0gMSAmIFBQMDNKID09IDFdKSwKICAgICAgICAgICAgU3Vib2NfZGVtYW5kYW50ZSAgPSBzdW0oUE9OREVSQVtFU1RBRE8gPT0gMSAmIElOVEVOU0kgPT0gMSAmIFBQMDNKID09IDFdKSwKICAgICAgICAgICAgU3Vib2Nfbm9fZGVtYW5kICAgPSBzdW0oUE9OREVSQVtFU1RBRE8gPT0gMSAmIElOVEVOU0kgPT0gMSAmIFBQMDNKICVpbiUgYygyLCA5KV0pLAogICAgICAgICAgICBTdWJvY3VwYWRvcyAgICAgICA9IFN1Ym9jX2RlbWFuZGFudGUgKyBTdWJvY19ub19kZW1hbmQsCiAgICAgICAgICAgICdUYXNhIEFjdGl2aWRhZCcgICAgICAgICAgICAgICAgICA9IFBFQS9Qb2JsYWNpb24sCiAgICAgICAgICAgICdUYXNhIEVtcGxlbycgICAgICAgICAgICAgICAgICAgICA9IE9jdXBhZG9zL1BvYmxhY2lvbiwKICAgICAgICAgICAgJ1Rhc2EgRGVzb2N1cGFjaW9uJyAgICAgICAgICAgICAgID0gRGVzb2N1cGFkb3MvUEVBLAogICAgICAgICAgICAnVGFzYSBvY3VwYWRvcyBkZW1hbmRhbnRlcycgICAgICAgPSBPY3VwYWRvc19kZW1hbmQvUEVBLAogICAgICAgICAgICAnVGFzYSBTdWJvY3VwYWNpw7NuJyAgICAgICAgICAgICAgID0gU3Vib2N1cGFkb3MvUEVBLAogICAgICAgICAgICAnVGFzYSBTdWJvY3VwYWNpw7NuIGRlbWFuZGFudGUnICAgID0gU3Vib2NfZGVtYW5kYW50ZS9QRUEsCiAgICAgICAgICAgICdUYXNhIFN1Ym9jdXBhY2nDs24gbm8gZGVtYW5kYW50ZScgPSBTdWJvY19ub19kZW1hbmQvUEVBKQoKQ3VhZHJvXzEuMmEKYGBgCgoKCmBgYHtyfQpDdWFkcm9fMS4yYSA8LSBDdWFkcm9fMS4yYSAlPiUgCiAgc2VsZWN0KC1jKDI6OSkpICU+JSAgICAjIEVsaW1pbmFtb3MgbGFzIHZhcmlhYmxlcyBkZSBuaXZlbAogIGxlZnRfam9pbiguLEFnbG9tKSAlPiUgIyBBZ3JlZ2Ftb3MgZWwgbm9tYnJlIGRlIGxvcyBhZ2xvbWVyYWRvcywgcXVlIHRlbmlhbW9zIGVuIG90cm8gREYKICBzZWxlY3QoTm9tX0FnbG8sZXZlcnl0aGluZyguKSwtQUdMT01FUkFETykgI0VsaW1pbmFtb3MgZWwgY8OzZGlnbyBkZSBsb3MgYWdsb21lcmFkb3MKCkN1YWRyb18xLjJhCmBgYAoKIyMjIyBFeHBvcnRhciByZXN1bHRhZG9zIGEgIEV4Y2VsCkNvbW8gdmllcmFtb3MgZW4gbGEgY2xhc2UgMSwgbGEgZnVuY2nDs24gX193cml0ZS54bHN4X18gZGUgbGEgbGlicmVyaWEgX29wZW54bHN4XyBub3MgcGVybWl0ZSBleHBvcnRhciBsb3MgZG9zIGRhdGFmcmFtZXMgYSB1biBtaXNtbyBhcmNoaXZvLiBDYWJlIGFjbGFyYXIgcXVlIGV4aXN0ZW4gbnVtZXJvc2FzIGZ1bmNpb25lcyB5IGxpYnJlcsOtYXMgYWx0ZXJuYXRpdmFzIHBhcmEgZXhwb3J0YXIgcmVzdWx0YWRvcyBhIHVuIGV4Y2VsLiBFbiBlc3RlIGNhc28sIG9wdGFtb3MgcG9yIF9vcGVueGxzeF8geWEgcXVlIHJlc3VsdGEgdW5hIGRlIGxhcyBtw6FzIHNlbmNpbGxhcyBwYXJhIGV4cG9ydGFyIHJhcGlkYW1lbnRlIGxvcyByZXN1bHRhZG9zLiBPdHJhcyBsaWJyZXLDrWFzIHBlcm1pdGVuIHRhbWJpw6luIGRhciBmb3JtYXRvIGEgbGFzIHRhYmxhcyBxdWUgc2UgZXhwb3J0YW4sIGRlZmluaXIgc2kgZGVzZWFtb3Mgc29icmVlc2NyaWJpciBhcmNoaXZvcyBlbiBjYXNvIGRlIHF1ZSB5YSBleGlzdGFuLCBldGMuIAoKYGBge3IgZXZhbD1GQUxTRSwgd2FyaW5pbmcgPSBGQUxTRX0KTGlzdGFfYV9leHBvcnRhciA8LSBsaXN0KCJDdWFkcm8gMS4xIiA9IEN1YWRyb18xLjFhLAogICAgICAgICAgICAgICAgICAgICAgIkN1YWRybyAxLjIiID0gQ3VhZHJvXzEuMmEpCgp3cml0ZS54bHN4KExpc3RhX2FfZXhwb3J0YXIsIi4uL1Jlc3VsdGFkb3MvSW5mb3JtZSBNZXJjYWRvIGRlIFRyYWJham8ueGxzeCIpCgpgYGAKCgojIEVqZXJjaWNpb3MgcGFyYSBwcmFjdGljYXIKCi0gTGV2YW50YXIgbGEgw7psdGltYSBiYXNlIGluZGl2aWR1YWwgZGUgRVBICi0gQ3JlYXIgdW4gdmVjdG9yIGxsYW1hZG8gX19WYXJpYWJsZXNfXyBxdWUgY29udGVuZ2EgbG9zIG5vbWJyZXMgZGUgbGFzIHNpZ3VpZW50ZXMgdmFyaWFibGVzIGRlIGludGVyw6lzIHBhcmEgcmVhbGl6YXIgYWxndW5vcyBlamVyY2ljaW9zOgogICAgICAtIEVkYWQsIFNleG8sIEluZ3Jlc28gZGUgbGEgb2N1cGFjacOzbiBwcmluY2lwYWwsIENhdGVnb3LDrWEgb2N1cGFjaW9uYWwsIEVTVEFETywgUE9OREVSQSB5IFBPTkRJSAotIEFjb3RhciBsYSBCYXNlIMO6bmljYW1lbnRlIGEgbGFzIHZhcmlhYmxlcyBkZSBpbnRlcsOpcywgdXRpbGl6YW5kbyBlbCB2ZWN0b3IgX19WYXJpYWJsZXNfXyAKCi0gQ2FsY3VsYXIgbGFzIHRhc2FzIGRlIGFjdGl2aWRhZCwgZW1wbGVvIHkgZGVzZW1wbGVvIHNlZ8O6biBzZXhvLCBwYXJhIGrDs3ZlbmVzIGVudHJlIDE4IHkgMzUgYcOxb3MKLSBDYWxjdWxhciBlbCBzYWxhcmlvIHByb21lZGlvIHBvciBzZXhvLCBwYXJhIGRvcyBncnVwb3MgZGUgZWRhZDogMTggYSAzNSBhw7FvcyB5IDM2IGEgNzAgYcOxb3MuIChSZWNvcmRhdG9yaW86IExhIGJhc2UgZGViZSBmaWx0cmFyc2UgcGFyYSBjb250ZW5lciDDum5pY2FtZW50ZSBPQ1VQQURPUyBBU0FMQVJJQURPUykKLSBHcmFiYXIgbG9zIHJlc3VsdGFkb3MgZW4gdW4gZXhjZWwKCiMgRWplcmNpY2lvcyBkZSB0YXJlYQoKLSBSZXBsaWNhciBlbCBjw6FsY3VsbyBkZSBsYXMgdGFzYXMgbG9ncmFkYXMgZW4gY2xhc2UgcGFyYSBkaXN0aW50b3MgdHJpbWVzdHJlcywgbGV2YW50YW5kbyBsYXMgYmFzZXMgZGVzZGUgZWwgc2VndW5kbyB0cmltZXN0cmUgMjAxNiBoYXN0YSBsYSDDumx0aW1hLiAKICAgIC0gVGlwczoganVudGFyIGxhcyBiYXNlcyBjb24gZWwgY29tYW5kbyBgYGBiaW5kX3Jvd3MoKWBgYAogICAgLSBQcm9iYXIgY29uIGBgYGdhdGhlcigpYGBgIHkgYGBgc3ByZWFkKClgYGAgY29tbyBxdWVkYW4gbWVqb3IgbG9zIHJlc3VsdGFkb3MKICAgIAotIEdyYWJhciBsb3MgcmVzdWx0YWRvcyBlbiB1biBleGNlbCAKCg==