1. Introducción

Este es el tercer cuaderno que los estudiantes de Geomatica Basica deben escribir para comenzar con R & RStudio. Su objetivo es aprender a utilizar el paquete dplyr para la “edición” de datos. La edición se refiere a elegir un subconjunto de las variables y / u observaciones en un conjunto de datos, así como a filtrar (seleccionar observaciones en función de sus valores de variables), crear nuevas variables y más. Para tal fin, utilizamos aquí las Evaluaciones Agropecuarias Municipales (EVA), un conjunto de datos estadísticos agrícolas 2007-2018 proporcionados por el Ministerio de Agricultura y Desarrollo.

2. Carga de las bibliotecas necesarias

Comencemos a cargar varias bibliotecas, en particular dplyr:

library(tidyverse)
library(dplyr)
library(ggplot2)

3. Lectura de una tabla con estadísticas agrarias

Una vez que hemos descargado el conjunto de datos de EVA, necesitamos “limpiarlo”, para poder trabajar en R. Esto comprende dos pasos principales: - eliminar símbolos y espacios extraños en los nombres de las columnas - seleccionar solo las filas que pertenecen a el departamento que nos interesa. En mi caso, es Boyacá.

Las dos tareas deben realizarse en Excel.

Ahora, leamos el archivo limpio de estadísticas de EVA para Boyacá. Leeremos el archivo delimitado por csv en un tibble:

eva_valle <- read_csv2(file = "C:/Users/LUISA CARRION/Documents/EVA_final.csv")
i Using ',' as decimal and '.' as grouping mark. Use `read_delim()` for more control.

-- Column specification ----------------------------------------------------------
cols(
  COD_DEPTO = col_double(),
  DEPTO = col_character(),
  COD_MUN = col_double(),
  MUNICIPIO = col_character(),
  GRUPO = col_character(),
  SUBGRUPO = col_character(),
  CULTIVO = col_character(),
  DESAGRE = col_character(),
  YEAR = col_double(),
  PERIODO = col_character(),
  Ha_Siembra = col_double(),
  Ha_cosecha = col_double(),
  ton_Prod = col_double(),
  RENDIM = col_double(),
  CICLO = col_character()
)

Para ver el tipo de archivo que es eva_valle, se escribe:

class(eva_valle)
[1] "spec_tbl_df" "tbl_df"      "tbl"         "data.frame" 

Si se quiere ver los primeros elementos del data frame, se usa el siguiente código:

head(eva_valle)

4. ¿Qué es dplyr?

El paquete dplyr es un paquete bastante nuevo (2014) que intenta proporcionar herramientas sencillas para las tareas de manipulación de datos más comunes. Está construido para trabajar directamente con marcos de datos. El pensamiento detrás de esto se inspiró en gran medida en el paquete plyr, que ha estado en uso durante algún tiempo, pero sufrió por ser lento en algunos casos. Dplyr aborda esto al portar gran parte del cálculo a C ++. Una característica adicional es la capacidad de trabajar con datos almacenados directamente en una base de datos externa. Los beneficios de hacer esto son que los datos se pueden administrar de forma nativa en una base de datos relacional, las consultas se pueden realizar en esa base de datos y solo se devuelven los resultados de la consulta.

Esto resuelve un problema común con R en el sentido de que todas las operaciones se realizan en la memoria y, por lo tanto, la cantidad de datos con los que puede trabajar está limitada por la memoria disponible. Las conexiones de la base de datos eliminan esencialmente esa limitación, ya que puede tener una base de datos de muchos 100 GB, realizar consultas directamente y extraer justo lo que necesita para el análisis en R.

4.1 Seleccionar columnas y filtrar filas

Vamos a aprender algunas de las funciones dplyr más comunes: select (), filter (), mutate (), group_by () y summary ().

Para seleccionar columnas de un marco de datos, use select (). El primer argumento de esta función es el marco de datos (eva_boyaca), y los argumentos siguientes son las columnas que se deb

evita  <-   select(eva_valle, MUNICIPIO, CULTIVO, YEAR, ton_Prod,RENDIM)
evita

Para elegir filas, por ejemplo las estadísticas correspondientes a un solo cultivo, podemos usar filter ():

evita_mora <- filter(evita, CULTIVO == "MORA")
evita_mora

En caso de que estemos interesados en un solo municipio:

evita_cali <- filter(evita, MUNICIPIO == "CALI")
evita_cali

4.2 Tuberías

Pero, ¿y si quisiera seleccionar y filtrar? Hay tres formas de hacer esto: usar pasos intermedios, funciones anidadas o canalizaciones.

Con los pasos intermedios, esencialmente crea un marco de datos temporal y lo usa como entrada para la siguiente función. Esto puede saturar su espacio de trabajo con muchos objetos.

También puede anidar funciones (es decir, una función dentro de otra). Esto es útil, pero puede ser difícil de leer si se anidan demasiadas funciones como proceso de adentro hacia afuera.

La última opción, las tuberías, son una adición bastante reciente a R. Las tuberías le permiten tomar la salida de una función y enviarla directamente a la siguiente, lo cual es útil cuando necesita muchas cosas para el mismo conjunto de datos. Las tuberías en R se ven como%>% y están disponibles a través del paquete magrittr instalado como parte de dply Pero, ¿y si quisiera seleccionar y filtrar? Hay tres formas de hacer esto: usar pasos intermedios, funciones anidadas o canalizaciones.

Con los pasos intermedios, esencialmente crea un marco de datos temporal y lo usa como entrada para la siguiente función. Esto puede saturar su espacio de trabajo con muchos objetos.

También puede anidar funciones (es decir, una función dentro de otra). Esto es útil, pero puede ser difícil de leer si se anidan demasiadas funciones como proceso de adentro hacia afuera.

La última opción, las tuberías, son una adición bastante reciente a R. Las tuberías le permiten tomar la salida de una función y enviarla directamente a la siguiente, lo cual es útil cuando necesita muchas cosas para el mismo conjunto de datos. Las tuberías en R se ven como%>% y están disponibles a través del paquete magrittr instalado como parte de dply

evita_cali_2018 <- evita %>%
  filter(MUNICIPIO == "CALI") %>%
  filter(YEAR == 2018) %>%
  select(CULTIVO, RENDIM)
evita_cali_2018

4.3 Mutar

Con frecuencia queremos crear nuevas columnas basadas en los valores de las columnas existentes, por ejemplo, para hacer conversiones de unidades o encontrar la proporción de valores en dos columnas. Para esto usaremos mutate (). Con frecuencia queremos crear nuevas columnas basadas en los valores de las columnas existentes, por ejemplo, para hacer conversiones de unidades o encontrar la proporción de valores en dos columnas. Para esto usaremos mutate ().

eva_valle %>%
  select(MUNICIPIO, CULTIVO, Ha_Siembra, ton_Prod, RENDIM) %>%
  filter(Ha_Siembra!=0) %>%
  mutate(RENDIM_SIEMBRA = ton_Prod/Ha_Siembra)

4.4 Análisis de datos dividir-aplicar-combinar y la función de resumen ()

Se pueden abordar muchas tareas de análisis de datos utilizando el paradigma dividir-aplicar-combinar: dividir los datos en grupos, aplicar algunos análisis a cada grupo y luego combinar los resultados.

dplyr lo hace muy fácil mediante el uso de la función group_by (), que divide los datos en grupos. Cuando los datos se agrupan de esta manera, se puede usar resume () para contraer cada grupo en un resumen de una sola fila. resume () hace esto aplicando una función de agregación o resumen a cada grupo.

Por ejemplo, si quisiéramos agrupar por cultivo y encontrar la productividad media (Ton / ha) en los municipios de Valle del Cauca, haríamos:

evita %>%
  group_by(CULTIVO) %>%
  summarize(mean_rend = mean(RENDIM, na.rm = TRUE))

Por si acaso, queremos encontrar los municipios con mayor productividad para cada cultivo para cualquier año:

eva_valle %>%
  group_by(CULTIVO, MUNICIPIO) %>%
  summarize(max_rend = max(RENDIM, na.rm = TRUE)) %>%
  slice(which.max(max_rend))
ningun argumento finito para max; retornando -Infningun argumento finito para max; retornando -Infningun argumento finito para max; retornando -Infningun argumento finito para max; retornando -Infningun argumento finito para max; retornando -Infningun argumento finito para max; retornando -Infningun argumento finito para max; retornando -Infningun argumento finito para max; retornando -Infningun argumento finito para max; retornando -Infningun argumento finito para max; retornando -Infningun argumento finito para max; retornando -Infningun argumento finito para max; retornando -Infningun argumento finito para max; retornando -Infningun argumento finito para max; retornando -Inf`summarise()` has grouped output by 'CULTIVO'. You can override using the `.groups` argument.

Para encontrar los municipios con mayor productividad por grupos de cultivos para cualquier año:

eva_valle %>%
  group_by(GRUPO, MUNICIPIO) %>%
  summarize(max_rend = max(RENDIM, na.rm = TRUE)) %>%
  slice(which.max(max_rend))
`summarise()` has grouped output by 'GRUPO'. You can override using the `.groups` argument.

Ahora, busquemos cuáles son los municipios con mayor área cosechada para cada grupo de cultivos en 2018:

eva_valle %>% 
  filter(YEAR==2018) %>% 
  group_by(GRUPO, MUNICIPIO) %>%
  summarize(max_area_cosecha = max(Ha_cosecha, na.rm = TRUE)) %>%
    slice(which.max(max_area_cosecha)) %>%
    arrange(desc(max_area_cosecha)) -> area_cosecha_max
`summarise()` has grouped output by 'GRUPO'. You can override using the `.groups` argument.
area_cosecha_max

Ahora, busquemos la mayor producción para cada cultivo en cualquier año:

eva_valle %>% 
  group_by(GRUPO, MUNICIPIO, YEAR) %>%
  summarize(max_prod = max(ton_Prod, na.rm = TRUE)) %>%
    slice(which.max(max_prod)) %>%
    arrange(desc(max_prod)) -> ton_prod_max
`summarise()` has grouped output by 'GRUPO', 'MUNICIPIO'. You can override using the `.groups` argument.
ton_prod_max

En el 2007, la mayor poducción de Café se concentó en el municipio de Argelia. Por ello, seleccionaremos la producción de otros permanentes (Toneladas) en Argelia para cada año

eva_valle %>% 
  filter(MUNICIPIO=="ARGELIA" & CULTIVO=="CAFE") %>% 
  group_by(YEAR, CULTIVO) %>%
  select(MUNICIPIO, CULTIVO, ton_Prod, YEAR) ->  argelia_cafe

argelia_cafe

5. Gráficos exploratorios:

Hagamos un gráfico rápido de la producción de papa en Tunja para todo el período de tiempo cubierto por el conjunto de datos de EVA:

g <- ggplot(aes(x=YEAR, y=ton_Prod/1000), data = argelia_cafe) + geom_bar(stat='identity', width = 0.5, fill = "coral")+labs(y='Produccion de café [Ton x 1000]')

Basado en datos EVA 0.5

g + ggtitle("Evolución de la Producción de café en Argelia desde 2007 hasta 2018") + labs(caption= "Basado en datos EVA (Minagricultura, 2020)")

6. Estadísticas para Valle:

En el caso del cultivo de café, se quiere conocer la producción total del departamento:

eva_valle %>% 
  filter(CULTIVO=="CAFE") %>% 
  group_by(YEAR, CULTIVO) %>%
  select(CULTIVO, ton_Prod, YEAR) ->  valle_cafe

valle_cafe

En base a los datos obtenidos, se hace una gráfica de la evolución de la producción de café:

g <- ggplot(aes(x=YEAR, y=ton_Prod/1000), data = valle_cafe) + geom_bar(stat='identity',width = 0.5, fill = "brown3") + labs(y='Produccion de café [Ton x 1000]')
g + ggtitle("Evolución de la Producción de café en Valle del Cauca desde 2007 hasta 2018") + labs(caption= "Basado en datos EVA (Minagricultura, 2020)")

Tambien es de interés conocer las estadísticas de producción de aguacate para Valle del Cauca:

eva_valle %>% 
  filter(CULTIVO=="AGUACATE") %>% 
  group_by(YEAR, CULTIVO) %>%
  select(CULTIVO, ton_Prod, YEAR) ->  valle_aguaca

valle_aguaca
g <- ggplot(aes(x=YEAR, y=ton_Prod/1000), data = valle_aguaca) + geom_bar(stat='identity',width = 0.5, fill = "blue4") + labs(y='Produccion de aguacate [Ton x 1000]')
g + ggtitle("Evolución de la producción de aguacate en Valle del Cauca entre 2007 y 2018") + labs(caption= "Basado en datos EVA (Minagricultura, 2020)")

Ahora, se quiere conocer la producción de café en Sevilla, un municipio de Valle del Cauca:

eva_valle %>% 
  filter(MUNICIPIO=="SEVILLA" & CULTIVO=="AGUACATE") %>% 
  group_by(YEAR, CULTIVO) %>%
  select(MUNICIPIO, CULTIVO, ton_Prod, YEAR) ->  sevilla_aguacate

sevilla_aguacate

g <- ggplot(aes(x=YEAR, y=ton_Prod/1000), data = sevilla_aguacate) + geom_bar(stat='identity',width = 0.5, fill = "blueviolet") + labs(y='Produccion de aguacate [Ton x 1000]')
g + ggtitle("Evolución de la producción de aguacate en Sevilla entre 2007 y 2018") + labs(caption= "Basado en datos EVA (Minagricultura, 2020)")

Se quiere conocer la producción de café en Dagua, municipio de Valle del Cauca:

eva_valle %>% 
  filter(MUNICIPIO=="DAGUA" & CULTIVO=="CAFE") %>% 
  group_by(YEAR, CULTIVO) %>%
  select(MUNICIPIO, CULTIVO, ton_Prod, YEAR) ->  dagua_cafe

dagua_cafe
g <- ggplot(aes(x=YEAR, y=ton_Prod/1000), data = dagua_cafe) + geom_bar(stat='identity',width = 0.5, fill = "cadetblue3") + labs(y='Produccion de café [Ton x 1000]')
g + ggtitle("Evolución de la producción de café en Dagua entre 2007 y 2018") + labs(caption= "Basado en datos EVA (Minagricultura, 2020)")

Por ultimo, se quiere conocer la producción de caña de azúcar del departamento debido a que es uno de los productos que sustentan la economía de Valle del Cauca:

eva_valle %>% 
  filter(CULTIVO=="CANA AZUCARERA") %>% 
  group_by(YEAR, CULTIVO) %>%
  select(CULTIVO, ton_Prod, YEAR) ->  valle_cana

valle_cana
g <- ggplot(aes(x=YEAR, y=ton_Prod/1000), data = valle_cana) + geom_bar(stat='identity',width = 0.5, fill = "darkorange2") + labs(y='Produccion de caña de azúcar [Ton x 1000]')
g + ggtitle("Evolución de la Producción de caña de azúcar en Valle del Cauca desde 2007 hasta 2018") + labs(caption= "Basado en datos EVA (Minagricultura, 2020)")

Por último, se quiere conocer la producción de caña de azúcar de Trujillo, Valle del Cauca:

eva_valle %>% 
  filter(MUNICIPIO=="TRUJILLO" & CULTIVO=="CANA AZUCARERA") %>% 
  group_by(YEAR, CULTIVO) %>%
  select(MUNICIPIO, CULTIVO, ton_Prod, YEAR) ->  trujillo_cana

trujillo_cana
g <- ggplot(aes(x=YEAR, y=ton_Prod/1000), data = trujillo_cana) + geom_bar(stat='identity',width = 0.5, fill = "darkolivegreen3") + labs(y='Produccion de caña de azúcar [Ton x 1000]')
g + ggtitle("Evolución de la Producción de caña de azúcar en Trujillo desde 2007 hasta 2018") + labs(caption= "Basado en datos EVA (Minagricultura, 2020)")

LS0tDQp0aXRsZTogIk15IHRoaXJkIFIgTm90ZWJvb2s6IEV2YWx1YWNpb25lcyBBZ3JvcGVjdWFyaWFzIE11bmljaXBhbGVzIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQphdXRob3I6IEx1aXNhIEZlcm5hbmRhIENhcnJpw7NuIFJhbcOtcmV6IHkgTWlndWVsIFNhbnRpYWdvIE1vcmFsZXMgUnXDrXoNCg0KLS0tDQoNCiMjIyAxLiBJbnRyb2R1Y2Npw7NuDQoNCkVzdGUgZXMgZWwgdGVyY2VyIGN1YWRlcm5vIHF1ZSBsb3MgZXN0dWRpYW50ZXMgZGUgR2VvbWF0aWNhIEJhc2ljYSBkZWJlbiBlc2NyaWJpciBwYXJhIGNvbWVuemFyIGNvbiBSICYgUlN0dWRpby4gU3Ugb2JqZXRpdm8gZXMgYXByZW5kZXIgYSB1dGlsaXphciBlbCBwYXF1ZXRlIGRwbHlyIHBhcmEgbGEgImVkaWNpw7NuIiBkZSBkYXRvcy4gTGEgZWRpY2nDs24gc2UgcmVmaWVyZSBhIGVsZWdpciB1biBzdWJjb25qdW50byBkZSBsYXMgdmFyaWFibGVzIHkgLyB1IG9ic2VydmFjaW9uZXMgZW4gdW4gY29uanVudG8gZGUgZGF0b3MsIGFzw60gY29tbyBhIGZpbHRyYXIgKHNlbGVjY2lvbmFyIG9ic2VydmFjaW9uZXMgZW4gZnVuY2nDs24gZGUgc3VzIHZhbG9yZXMgZGUgdmFyaWFibGVzKSwgY3JlYXIgbnVldmFzIHZhcmlhYmxlcyB5IG3DoXMuIFBhcmEgdGFsIGZpbiwgdXRpbGl6YW1vcyBhcXXDrSBsYXMgRXZhbHVhY2lvbmVzIEFncm9wZWN1YXJpYXMgTXVuaWNpcGFsZXMgKEVWQSksIHVuIGNvbmp1bnRvIGRlIGRhdG9zIGVzdGFkw61zdGljb3MgYWdyw61jb2xhcyAyMDA3LTIwMTggcHJvcG9yY2lvbmFkb3MgcG9yIGVsIE1pbmlzdGVyaW8gZGUgQWdyaWN1bHR1cmEgeSBEZXNhcnJvbGxvLg0KDQojIyMgMi4gQ2FyZ2EgZGUgbGFzIGJpYmxpb3RlY2FzIG5lY2VzYXJpYXMNCg0KQ29tZW5jZW1vcyBhIGNhcmdhciB2YXJpYXMgYmlibGlvdGVjYXMsIGVuIHBhcnRpY3VsYXIgZHBseXI6DQoNCmBgYHtyfQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KYGBgDQoNCiMjIyAzLiBMZWN0dXJhIGRlIHVuYSB0YWJsYSBjb24gZXN0YWTDrXN0aWNhcyBhZ3Jhcmlhcw0KDQpVbmEgdmV6IHF1ZSBoZW1vcyBkZXNjYXJnYWRvIGVsIGNvbmp1bnRvIGRlIGRhdG9zIGRlIEVWQSwgbmVjZXNpdGFtb3MgImxpbXBpYXJsbyIsIHBhcmEgcG9kZXIgdHJhYmFqYXIgZW4gUi4gRXN0byBjb21wcmVuZGUgZG9zIHBhc29zIHByaW5jaXBhbGVzOiAtIGVsaW1pbmFyIHPDrW1ib2xvcyB5IGVzcGFjaW9zIGV4dHJhw7FvcyBlbiBsb3Mgbm9tYnJlcyBkZSBsYXMgY29sdW1uYXMgLSBzZWxlY2Npb25hciBzb2xvIGxhcyBmaWxhcyBxdWUgcGVydGVuZWNlbiBhIGVsIGRlcGFydGFtZW50byBxdWUgbm9zIGludGVyZXNhLiBFbiBtaSBjYXNvLCBlcyBCb3lhY8OhLg0KDQpMYXMgZG9zIHRhcmVhcyBkZWJlbiByZWFsaXphcnNlIGVuIEV4Y2VsLg0KDQpBaG9yYSwgbGVhbW9zIGVsIGFyY2hpdm8gbGltcGlvIGRlIGVzdGFkw61zdGljYXMgZGUgRVZBIHBhcmEgQm95YWPDoS4gTGVlcmVtb3MgZWwgYXJjaGl2byBkZWxpbWl0YWRvIHBvciBjc3YgZW4gdW4gdGliYmxlOg0KDQpgYGB7cn0NCmV2YV92YWxsZSA8LSByZWFkX2NzdjIoZmlsZSA9ICJDOi9Vc2Vycy9MVUlTQSBDQVJSSU9OL0RvY3VtZW50cy9FVkFfZmluYWwuY3N2IikNCmBgYA0KUGFyYSB2ZXIgZWwgdGlwbyBkZSBhcmNoaXZvIHF1ZSBlcyBldmFfdmFsbGUsIHNlIGVzY3JpYmU6DQoNCmBgYHtyfQ0KY2xhc3MoZXZhX3ZhbGxlKQ0KYGBgDQpTaSBzZSBxdWllcmUgdmVyIGxvcyBwcmltZXJvcyBlbGVtZW50b3MgZGVsIGRhdGEgZnJhbWUsIHNlIHVzYSBlbCBzaWd1aWVudGUgY8OzZGlnbzoNCg0KYGBge3J9DQpoZWFkKGV2YV92YWxsZSkNCmBgYA0KDQojIyMgNC4gwr9RdcOpIGVzIGRwbHlyPw0KDQpFbCBwYXF1ZXRlIGRwbHlyIGVzIHVuIHBhcXVldGUgYmFzdGFudGUgbnVldm8gKDIwMTQpIHF1ZSBpbnRlbnRhIHByb3BvcmNpb25hciBoZXJyYW1pZW50YXMgc2VuY2lsbGFzIHBhcmEgbGFzIHRhcmVhcyBkZSBtYW5pcHVsYWNpw7NuIGRlIGRhdG9zIG3DoXMgY29tdW5lcy4gRXN0w6EgY29uc3RydWlkbyBwYXJhIHRyYWJhamFyIGRpcmVjdGFtZW50ZSBjb24gbWFyY29zIGRlIGRhdG9zLiBFbCBwZW5zYW1pZW50byBkZXRyw6FzIGRlIGVzdG8gc2UgaW5zcGlyw7MgZW4gZ3JhbiBtZWRpZGEgZW4gZWwgcGFxdWV0ZSBwbHlyLCBxdWUgaGEgZXN0YWRvIGVuIHVzbyBkdXJhbnRlIGFsZ8O6biB0aWVtcG8sIHBlcm8gc3VmcmnDsyBwb3Igc2VyIGxlbnRvIGVuIGFsZ3Vub3MgY2Fzb3MuIERwbHlyIGFib3JkYSBlc3RvIGFsIHBvcnRhciBncmFuIHBhcnRlIGRlbCBjw6FsY3VsbyBhIEMgKysuIFVuYSBjYXJhY3RlcsOtc3RpY2EgYWRpY2lvbmFsIGVzIGxhIGNhcGFjaWRhZCBkZSB0cmFiYWphciBjb24gZGF0b3MgYWxtYWNlbmFkb3MgZGlyZWN0YW1lbnRlIGVuIHVuYSBiYXNlIGRlIGRhdG9zIGV4dGVybmEuIExvcyBiZW5lZmljaW9zIGRlIGhhY2VyIGVzdG8gc29uIHF1ZSBsb3MgZGF0b3Mgc2UgcHVlZGVuIGFkbWluaXN0cmFyIGRlIGZvcm1hIG5hdGl2YSBlbiB1bmEgYmFzZSBkZSBkYXRvcyByZWxhY2lvbmFsLCBsYXMgY29uc3VsdGFzIHNlIHB1ZWRlbiByZWFsaXphciBlbiBlc2EgYmFzZSBkZSBkYXRvcyB5IHNvbG8gc2UgZGV2dWVsdmVuIGxvcyByZXN1bHRhZG9zIGRlIGxhIGNvbnN1bHRhLg0KDQpFc3RvIHJlc3VlbHZlIHVuIHByb2JsZW1hIGNvbcO6biBjb24gUiBlbiBlbCBzZW50aWRvIGRlIHF1ZSB0b2RhcyBsYXMgb3BlcmFjaW9uZXMgc2UgcmVhbGl6YW4gZW4gbGEgbWVtb3JpYSB5LCBwb3IgbG8gdGFudG8sIGxhIGNhbnRpZGFkIGRlIGRhdG9zIGNvbiBsb3MgcXVlIHB1ZWRlIHRyYWJhamFyIGVzdMOhIGxpbWl0YWRhIHBvciBsYSBtZW1vcmlhIGRpc3BvbmlibGUuIExhcyBjb25leGlvbmVzIGRlIGxhIGJhc2UgZGUgZGF0b3MgZWxpbWluYW4gZXNlbmNpYWxtZW50ZSBlc2EgbGltaXRhY2nDs24sIHlhIHF1ZSBwdWVkZSB0ZW5lciB1bmEgYmFzZSBkZSBkYXRvcyBkZSBtdWNob3MgMTAwIEdCLCByZWFsaXphciBjb25zdWx0YXMgZGlyZWN0YW1lbnRlIHkgZXh0cmFlciBqdXN0byBsbyBxdWUgbmVjZXNpdGEgcGFyYSBlbCBhbsOhbGlzaXMgZW4gUi4NCg0KIyMjIyA0LjEgU2VsZWNjaW9uYXIgY29sdW1uYXMgeSBmaWx0cmFyIGZpbGFzDQoNClZhbW9zIGEgYXByZW5kZXIgYWxndW5hcyBkZSBsYXMgZnVuY2lvbmVzIGRwbHlyIG3DoXMgY29tdW5lczogc2VsZWN0ICgpLCBmaWx0ZXIgKCksIG11dGF0ZSAoKSwgZ3JvdXBfYnkgKCkgeSBzdW1tYXJ5ICgpLg0KDQpQYXJhIHNlbGVjY2lvbmFyIGNvbHVtbmFzIGRlIHVuIG1hcmNvIGRlIGRhdG9zLCB1c2Ugc2VsZWN0ICgpLiBFbCBwcmltZXIgYXJndW1lbnRvIGRlIGVzdGEgZnVuY2nDs24gZXMgZWwgbWFyY28gZGUgZGF0b3MgKGV2YV9ib3lhY2EpLCB5IGxvcyBhcmd1bWVudG9zIHNpZ3VpZW50ZXMgc29uIGxhcyBjb2x1bW5hcyBxdWUgc2UgZGViDQoNCmBgYHtyfQ0KZXZpdGEgIDwtICAgc2VsZWN0KGV2YV92YWxsZSwgTVVOSUNJUElPLCBDVUxUSVZPLCBZRUFSLCB0b25fUHJvZCxSRU5ESU0pDQpgYGANCg0KDQpgYGB7cn0NCmV2aXRhDQpgYGANCg0KUGFyYSBlbGVnaXIgZmlsYXMsIHBvciBlamVtcGxvIGxhcyBlc3RhZMOtc3RpY2FzIGNvcnJlc3BvbmRpZW50ZXMgYSB1biBzb2xvIGN1bHRpdm8sIHBvZGVtb3MgdXNhciBmaWx0ZXIgKCk6DQoNCmBgYHtyfQ0KZXZpdGFfbW9yYSA8LSBmaWx0ZXIoZXZpdGEsIENVTFRJVk8gPT0gIk1PUkEiKQ0KYGBgDQoNCmBgYHtyfQ0KZXZpdGFfbW9yYQ0KYGBgDQoNCkVuIGNhc28gZGUgcXVlIGVzdGVtb3MgaW50ZXJlc2Fkb3MgZW4gdW4gc29sbyBtdW5pY2lwaW86DQoNCmBgYHtyfQ0KZXZpdGFfY2FsaSA8LSBmaWx0ZXIoZXZpdGEsIE1VTklDSVBJTyA9PSAiQ0FMSSIpDQpgYGANCg0KYGBge3J9DQpldml0YV9jYWxpDQpgYGANCiMjIyMgNC4yIFR1YmVyw61hcw0KUGVybywgwr95IHNpIHF1aXNpZXJhIHNlbGVjY2lvbmFyIHkgZmlsdHJhcj8gSGF5IHRyZXMgZm9ybWFzIGRlIGhhY2VyIGVzdG86IHVzYXIgcGFzb3MgaW50ZXJtZWRpb3MsIGZ1bmNpb25lcyBhbmlkYWRhcyBvIGNhbmFsaXphY2lvbmVzLg0KDQpDb24gbG9zIHBhc29zIGludGVybWVkaW9zLCBlc2VuY2lhbG1lbnRlIGNyZWEgdW4gbWFyY28gZGUgZGF0b3MgdGVtcG9yYWwgeSBsbyB1c2EgY29tbyBlbnRyYWRhIHBhcmEgbGEgc2lndWllbnRlIGZ1bmNpw7NuLiBFc3RvIHB1ZWRlIHNhdHVyYXIgc3UgZXNwYWNpbyBkZSB0cmFiYWpvIGNvbiBtdWNob3Mgb2JqZXRvcy4NCg0KVGFtYmnDqW4gcHVlZGUgYW5pZGFyIGZ1bmNpb25lcyAoZXMgZGVjaXIsIHVuYSBmdW5jacOzbiBkZW50cm8gZGUgb3RyYSkuIEVzdG8gZXMgw7p0aWwsIHBlcm8gcHVlZGUgc2VyIGRpZsOtY2lsIGRlIGxlZXIgc2kgc2UgYW5pZGFuIGRlbWFzaWFkYXMgZnVuY2lvbmVzIGNvbW8gcHJvY2VzbyBkZSBhZGVudHJvIGhhY2lhIGFmdWVyYS4NCg0KTGEgw7psdGltYSBvcGNpw7NuLCBsYXMgdHViZXLDrWFzLCBzb24gdW5hIGFkaWNpw7NuIGJhc3RhbnRlIHJlY2llbnRlIGEgUi4gTGFzIHR1YmVyw61hcyBsZSBwZXJtaXRlbiB0b21hciBsYSBzYWxpZGEgZGUgdW5hIGZ1bmNpw7NuIHkgZW52aWFybGEgZGlyZWN0YW1lbnRlIGEgbGEgc2lndWllbnRlLCBsbyBjdWFsIGVzIMO6dGlsIGN1YW5kbyBuZWNlc2l0YSBtdWNoYXMgY29zYXMgcGFyYSBlbCBtaXNtbyBjb25qdW50byBkZSBkYXRvcy4gTGFzIHR1YmVyw61hcyBlbiBSIHNlIHZlbiBjb21vJT4lIHkgZXN0w6FuIGRpc3BvbmlibGVzIGEgdHJhdsOpcyBkZWwgcGFxdWV0ZSBtYWdyaXR0ciBpbnN0YWxhZG8gY29tbyBwYXJ0ZSBkZSBkcGx5DQpQZXJvLCDCv3kgc2kgcXVpc2llcmEgc2VsZWNjaW9uYXIgeSBmaWx0cmFyPyBIYXkgdHJlcyBmb3JtYXMgZGUgaGFjZXIgZXN0bzogdXNhciBwYXNvcyBpbnRlcm1lZGlvcywgZnVuY2lvbmVzIGFuaWRhZGFzIG8gY2FuYWxpemFjaW9uZXMuDQoNCkNvbiBsb3MgcGFzb3MgaW50ZXJtZWRpb3MsIGVzZW5jaWFsbWVudGUgY3JlYSB1biBtYXJjbyBkZSBkYXRvcyB0ZW1wb3JhbCB5IGxvIHVzYSBjb21vIGVudHJhZGEgcGFyYSBsYSBzaWd1aWVudGUgZnVuY2nDs24uIEVzdG8gcHVlZGUgc2F0dXJhciBzdSBlc3BhY2lvIGRlIHRyYWJham8gY29uIG11Y2hvcyBvYmpldG9zLg0KDQpUYW1iacOpbiBwdWVkZSBhbmlkYXIgZnVuY2lvbmVzIChlcyBkZWNpciwgdW5hIGZ1bmNpw7NuIGRlbnRybyBkZSBvdHJhKS4gRXN0byBlcyDDunRpbCwgcGVybyBwdWVkZSBzZXIgZGlmw61jaWwgZGUgbGVlciBzaSBzZSBhbmlkYW4gZGVtYXNpYWRhcyBmdW5jaW9uZXMgY29tbyBwcm9jZXNvIGRlIGFkZW50cm8gaGFjaWEgYWZ1ZXJhLg0KDQpMYSDDumx0aW1hIG9wY2nDs24sIGxhcyB0dWJlcsOtYXMsIHNvbiB1bmEgYWRpY2nDs24gYmFzdGFudGUgcmVjaWVudGUgYSBSLiBMYXMgdHViZXLDrWFzIGxlIHBlcm1pdGVuIHRvbWFyIGxhIHNhbGlkYSBkZSB1bmEgZnVuY2nDs24geSBlbnZpYXJsYSBkaXJlY3RhbWVudGUgYSBsYSBzaWd1aWVudGUsIGxvIGN1YWwgZXMgw7p0aWwgY3VhbmRvIG5lY2VzaXRhIG11Y2hhcyBjb3NhcyBwYXJhIGVsIG1pc21vIGNvbmp1bnRvIGRlIGRhdG9zLiBMYXMgdHViZXLDrWFzIGVuIFIgc2UgdmVuIGNvbW8lPiUgeSBlc3TDoW4gZGlzcG9uaWJsZXMgYSB0cmF2w6lzIGRlbCBwYXF1ZXRlIG1hZ3JpdHRyIGluc3RhbGFkbyBjb21vIHBhcnRlIGRlIGRwbHkNCg0KDQpgYGB7cn0NCmV2aXRhX2NhbGlfMjAxOCA8LSBldml0YSAlPiUNCiAgZmlsdGVyKE1VTklDSVBJTyA9PSAiQ0FMSSIpICU+JQ0KICBmaWx0ZXIoWUVBUiA9PSAyMDE4KSAlPiUNCiAgc2VsZWN0KENVTFRJVk8sIFJFTkRJTSkNCmBgYA0KDQpgYGB7cn0NCmV2aXRhX2NhbGlfMjAxOA0KYGBgDQoNCiMjIyMgNC4zIE11dGFyDQoNCkNvbiBmcmVjdWVuY2lhIHF1ZXJlbW9zIGNyZWFyIG51ZXZhcyBjb2x1bW5hcyBiYXNhZGFzIGVuIGxvcyB2YWxvcmVzIGRlIGxhcyBjb2x1bW5hcyBleGlzdGVudGVzLCBwb3IgZWplbXBsbywgcGFyYSBoYWNlciBjb252ZXJzaW9uZXMgZGUgdW5pZGFkZXMgbyBlbmNvbnRyYXIgbGEgcHJvcG9yY2nDs24gZGUgdmFsb3JlcyBlbiBkb3MgY29sdW1uYXMuIFBhcmEgZXN0byB1c2FyZW1vcyBtdXRhdGUgKCkuDQpDb24gZnJlY3VlbmNpYSBxdWVyZW1vcyBjcmVhciBudWV2YXMgY29sdW1uYXMgYmFzYWRhcyBlbiBsb3MgdmFsb3JlcyBkZSBsYXMgY29sdW1uYXMgZXhpc3RlbnRlcywgcG9yIGVqZW1wbG8sIHBhcmEgaGFjZXIgY29udmVyc2lvbmVzIGRlIHVuaWRhZGVzIG8gZW5jb250cmFyIGxhIHByb3BvcmNpw7NuIGRlIHZhbG9yZXMgZW4gZG9zIGNvbHVtbmFzLiBQYXJhIGVzdG8gdXNhcmVtb3MgbXV0YXRlICgpLg0KDQpgYGB7cn0NCmV2YV92YWxsZSAlPiUNCiAgc2VsZWN0KE1VTklDSVBJTywgQ1VMVElWTywgSGFfU2llbWJyYSwgdG9uX1Byb2QsIFJFTkRJTSkgJT4lDQogIGZpbHRlcihIYV9TaWVtYnJhIT0wKSAlPiUNCiAgbXV0YXRlKFJFTkRJTV9TSUVNQlJBID0gdG9uX1Byb2QvSGFfU2llbWJyYSkNCmBgYA0KDQojIyMjIDQuNCBBbsOhbGlzaXMgZGUgZGF0b3MgZGl2aWRpci1hcGxpY2FyLWNvbWJpbmFyIHkgbGEgZnVuY2nDs24gZGUgcmVzdW1lbiAoKQ0KDQpTZSBwdWVkZW4gYWJvcmRhciBtdWNoYXMgdGFyZWFzIGRlIGFuw6FsaXNpcyBkZSBkYXRvcyB1dGlsaXphbmRvIGVsIHBhcmFkaWdtYSBkaXZpZGlyLWFwbGljYXItY29tYmluYXI6IGRpdmlkaXIgbG9zIGRhdG9zIGVuIGdydXBvcywgYXBsaWNhciBhbGd1bm9zIGFuw6FsaXNpcyBhIGNhZGEgZ3J1cG8geSBsdWVnbyBjb21iaW5hciBsb3MgcmVzdWx0YWRvcy4NCg0KZHBseXIgbG8gaGFjZSBtdXkgZsOhY2lsIG1lZGlhbnRlIGVsIHVzbyBkZSBsYSBmdW5jacOzbiBncm91cF9ieSAoKSwgcXVlIGRpdmlkZSBsb3MgZGF0b3MgZW4gZ3J1cG9zLiBDdWFuZG8gbG9zIGRhdG9zIHNlIGFncnVwYW4gZGUgZXN0YSBtYW5lcmEsIHNlIHB1ZWRlIHVzYXIgcmVzdW1lICgpIHBhcmEgY29udHJhZXIgY2FkYSBncnVwbyBlbiB1biByZXN1bWVuIGRlIHVuYSBzb2xhIGZpbGEuIHJlc3VtZSAoKSBoYWNlIGVzdG8gYXBsaWNhbmRvIHVuYSBmdW5jacOzbiBkZSBhZ3JlZ2FjacOzbiBvIHJlc3VtZW4gYSBjYWRhIGdydXBvLg0KDQpQb3IgZWplbXBsbywgc2kgcXVpc2nDqXJhbW9zIGFncnVwYXIgcG9yIGN1bHRpdm8geSBlbmNvbnRyYXIgbGEgcHJvZHVjdGl2aWRhZCBtZWRpYSAoVG9uIC8gaGEpIGVuIGxvcyBtdW5pY2lwaW9zIGRlIFZhbGxlIGRlbCBDYXVjYSwgaGFyw61hbW9zOg0KDQpgYGB7cn0NCmV2aXRhICU+JQ0KICBncm91cF9ieShDVUxUSVZPKSAlPiUNCiAgc3VtbWFyaXplKG1lYW5fcmVuZCA9IG1lYW4oUkVORElNLCBuYS5ybSA9IFRSVUUpKQ0KYGBgDQoNClBvciBzaSBhY2FzbywgcXVlcmVtb3MgZW5jb250cmFyIGxvcyBtdW5pY2lwaW9zIGNvbiBtYXlvciBwcm9kdWN0aXZpZGFkIHBhcmEgY2FkYSBjdWx0aXZvIHBhcmEgY3VhbHF1aWVyIGHDsW86DQoNCmBgYHtyfQ0KZXZhX3ZhbGxlICU+JQ0KICBncm91cF9ieShDVUxUSVZPLCBNVU5JQ0lQSU8pICU+JQ0KICBzdW1tYXJpemUobWF4X3JlbmQgPSBtYXgoUkVORElNLCBuYS5ybSA9IFRSVUUpKSAlPiUNCiAgc2xpY2Uod2hpY2gubWF4KG1heF9yZW5kKSkNCmBgYA0KDQpQYXJhIGVuY29udHJhciBsb3MgbXVuaWNpcGlvcyBjb24gbWF5b3IgcHJvZHVjdGl2aWRhZCBwb3IgZ3J1cG9zIGRlIGN1bHRpdm9zIHBhcmEgY3VhbHF1aWVyIGHDsW86DQoNCmBgYHtyfQ0KZXZhX3ZhbGxlICU+JQ0KICBncm91cF9ieShHUlVQTywgTVVOSUNJUElPKSAlPiUNCiAgc3VtbWFyaXplKG1heF9yZW5kID0gbWF4KFJFTkRJTSwgbmEucm0gPSBUUlVFKSkgJT4lDQogIHNsaWNlKHdoaWNoLm1heChtYXhfcmVuZCkpDQpgYGANCg0KQWhvcmEsIGJ1c3F1ZW1vcyBjdcOhbGVzIHNvbiBsb3MgbXVuaWNpcGlvcyBjb24gbWF5b3Igw6FyZWEgY29zZWNoYWRhIHBhcmEgY2FkYSBncnVwbyBkZSBjdWx0aXZvcyBlbiAyMDE4Og0KDQpgYGB7cn0NCmV2YV92YWxsZSAlPiUgDQogIGZpbHRlcihZRUFSPT0yMDE4KSAlPiUgDQogIGdyb3VwX2J5KEdSVVBPLCBNVU5JQ0lQSU8pICU+JQ0KICBzdW1tYXJpemUobWF4X2FyZWFfY29zZWNoYSA9IG1heChIYV9jb3NlY2hhLCBuYS5ybSA9IFRSVUUpKSAlPiUNCiAgICBzbGljZSh3aGljaC5tYXgobWF4X2FyZWFfY29zZWNoYSkpICU+JQ0KICAgIGFycmFuZ2UoZGVzYyhtYXhfYXJlYV9jb3NlY2hhKSkgLT4gYXJlYV9jb3NlY2hhX21heA0KDQphcmVhX2Nvc2VjaGFfbWF4DQpgYGANCg0KQWhvcmEsIGJ1c3F1ZW1vcyBsYSBtYXlvciBwcm9kdWNjacOzbiBwYXJhIGNhZGEgY3VsdGl2byBlbiBjdWFscXVpZXIgYcOxbzoNCg0KYGBge3J9DQpldmFfdmFsbGUgJT4lIA0KICBncm91cF9ieShHUlVQTywgTVVOSUNJUElPLCBZRUFSKSAlPiUNCiAgc3VtbWFyaXplKG1heF9wcm9kID0gbWF4KHRvbl9Qcm9kLCBuYS5ybSA9IFRSVUUpKSAlPiUNCiAgICBzbGljZSh3aGljaC5tYXgobWF4X3Byb2QpKSAlPiUNCiAgICBhcnJhbmdlKGRlc2MobWF4X3Byb2QpKSAtPiB0b25fcHJvZF9tYXgNCg0KdG9uX3Byb2RfbWF4DQpgYGANCg0KRW4gZWwgMjAwNywgbGEgbWF5b3IgcG9kdWNjacOzbiBkZSBDYWbDqSBzZSBjb25jZW50w7MgZW4gZWwgbXVuaWNpcGlvIGRlIEFyZ2VsaWEuIFBvciBlbGxvLCBzZWxlY2Npb25hcmVtb3MgbGEgcHJvZHVjY2nDs24gZGUgb3Ryb3MgcGVybWFuZW50ZXMgKFRvbmVsYWRhcykgZW4gQXJnZWxpYSBwYXJhIGNhZGEgYcOxbw0KDQpgYGB7cn0NCmV2YV92YWxsZSAlPiUgDQogIGZpbHRlcihNVU5JQ0lQSU89PSJBUkdFTElBIiAmIENVTFRJVk89PSJDQUZFIikgJT4lIA0KICBncm91cF9ieShZRUFSLCBDVUxUSVZPKSAlPiUNCiAgc2VsZWN0KE1VTklDSVBJTywgQ1VMVElWTywgdG9uX1Byb2QsIFlFQVIpIC0+ICBhcmdlbGlhX2NhZmUNCg0KYXJnZWxpYV9jYWZlDQpgYGANCg0KIyMjIDUuIEdyw6FmaWNvcyBleHBsb3JhdG9yaW9zOg0KDQpIYWdhbW9zIHVuIGdyw6FmaWNvIHLDoXBpZG8gZGUgbGEgcHJvZHVjY2nDs24gZGUgcGFwYSBlbiBUdW5qYSBwYXJhIHRvZG8gZWwgcGVyw61vZG8gZGUgdGllbXBvIGN1YmllcnRvIHBvciBlbCBjb25qdW50byBkZSBkYXRvcyBkZSBFVkE6DQoNCmBgYHtyfQ0KZyA8LSBnZ3Bsb3QoYWVzKHg9WUVBUiwgeT10b25fUHJvZC8xMDAwKSwgZGF0YSA9IGFyZ2VsaWFfY2FmZSkgKyBnZW9tX2JhcihzdGF0PSdpZGVudGl0eScsIHdpZHRoID0gMC41LCBmaWxsID0gImNvcmFsIikrbGFicyh5PSdQcm9kdWNjaW9uIGRlIGNhZsOpIFtUb24geCAxMDAwXScpDQpgYGANCg0KQmFzYWRvIGVuIGRhdG9zIEVWQQ0KMC41DQoNCmBgYHtyfQ0KZyArIGdndGl0bGUoIkV2b2x1Y2nDs24gZGUgbGEgUHJvZHVjY2nDs24gZGUgY2Fmw6kgZW4gQXJnZWxpYSBkZXNkZSAyMDA3IGhhc3RhIDIwMTgiKSArIGxhYnMoY2FwdGlvbj0gIkJhc2FkbyBlbiBkYXRvcyBFVkEgKE1pbmFncmljdWx0dXJhLCAyMDIwKSIpDQpgYGANCg0KIyMjIDYuIEVzdGFkw61zdGljYXMgcGFyYSBWYWxsZToNCg0KRW4gZWwgY2FzbyBkZWwgY3VsdGl2byBkZSBjYWbDqSwgc2UgcXVpZXJlIGNvbm9jZXIgbGEgcHJvZHVjY2nDs24gdG90YWwgZGVsIGRlcGFydGFtZW50bzoNCg0KYGBge3J9DQpldmFfdmFsbGUgJT4lIA0KICBmaWx0ZXIoQ1VMVElWTz09IkNBRkUiKSAlPiUgDQogIGdyb3VwX2J5KFlFQVIsIENVTFRJVk8pICU+JQ0KICBzZWxlY3QoQ1VMVElWTywgdG9uX1Byb2QsIFlFQVIpIC0+ICB2YWxsZV9jYWZlDQoNCnZhbGxlX2NhZmUNCmBgYA0KDQpFbiBiYXNlIGEgbG9zIGRhdG9zIG9idGVuaWRvcywgc2UgaGFjZSB1bmEgZ3LDoWZpY2EgZGUgbGEgZXZvbHVjacOzbiBkZSBsYSBwcm9kdWNjacOzbiBkZSBjYWbDqToNCg0KYGBge3J9DQpnIDwtIGdncGxvdChhZXMoeD1ZRUFSLCB5PXRvbl9Qcm9kLzEwMDApLCBkYXRhID0gdmFsbGVfY2FmZSkgKyBnZW9tX2JhcihzdGF0PSdpZGVudGl0eScsd2lkdGggPSAwLjUsIGZpbGwgPSAiYnJvd24zIikgKyBsYWJzKHk9J1Byb2R1Y2Npb24gZGUgY2Fmw6kgW1RvbiB4IDEwMDBdJykNCmBgYA0KDQpgYGB7cn0NCmcgKyBnZ3RpdGxlKCJFdm9sdWNpw7NuIGRlIGxhIFByb2R1Y2Npw7NuIGRlIGNhZsOpIGVuIFZhbGxlIGRlbCBDYXVjYSBkZXNkZSAyMDA3IGhhc3RhIDIwMTgiKSArIGxhYnMoY2FwdGlvbj0gIkJhc2FkbyBlbiBkYXRvcyBFVkEgKE1pbmFncmljdWx0dXJhLCAyMDIwKSIpDQpgYGANCg0KVGFtYmllbiBlcyBkZSBpbnRlcsOpcyBjb25vY2VyIGxhcyBlc3RhZMOtc3RpY2FzIGRlIHByb2R1Y2Npw7NuIGRlIGFndWFjYXRlIHBhcmEgVmFsbGUgZGVsIENhdWNhOg0KDQpgYGB7cn0NCmV2YV92YWxsZSAlPiUgDQogIGZpbHRlcihDVUxUSVZPPT0iQUdVQUNBVEUiKSAlPiUgDQogIGdyb3VwX2J5KFlFQVIsIENVTFRJVk8pICU+JQ0KICBzZWxlY3QoQ1VMVElWTywgdG9uX1Byb2QsIFlFQVIpIC0+ICB2YWxsZV9hZ3VhY2ENCg0KdmFsbGVfYWd1YWNhDQpgYGANCg0KYGBge3J9DQpnIDwtIGdncGxvdChhZXMoeD1ZRUFSLCB5PXRvbl9Qcm9kLzEwMDApLCBkYXRhID0gdmFsbGVfYWd1YWNhKSArIGdlb21fYmFyKHN0YXQ9J2lkZW50aXR5Jyx3aWR0aCA9IDAuNSwgZmlsbCA9ICJibHVlNCIpICsgbGFicyh5PSdQcm9kdWNjaW9uIGRlIGFndWFjYXRlIFtUb24geCAxMDAwXScpDQpgYGANCg0KYGBge3J9DQpnICsgZ2d0aXRsZSgiRXZvbHVjacOzbiBkZSBsYSBwcm9kdWNjacOzbiBkZSBhZ3VhY2F0ZSBlbiBWYWxsZSBkZWwgQ2F1Y2EgZW50cmUgMjAwNyB5IDIwMTgiKSArIGxhYnMoY2FwdGlvbj0gIkJhc2FkbyBlbiBkYXRvcyBFVkEgKE1pbmFncmljdWx0dXJhLCAyMDIwKSIpDQpgYGANCg0KQWhvcmEsIHNlIHF1aWVyZSBjb25vY2VyIGxhIHByb2R1Y2Npw7NuIGRlIGNhZsOpIGVuIFNldmlsbGEsIHVuIG11bmljaXBpbyBkZSBWYWxsZSBkZWwgQ2F1Y2E6DQoNCmBgYHtyfQ0KZXZhX3ZhbGxlICU+JSANCiAgZmlsdGVyKE1VTklDSVBJTz09IlNFVklMTEEiICYgQ1VMVElWTz09IkFHVUFDQVRFIikgJT4lIA0KICBncm91cF9ieShZRUFSLCBDVUxUSVZPKSAlPiUNCiAgc2VsZWN0KE1VTklDSVBJTywgQ1VMVElWTywgdG9uX1Byb2QsIFlFQVIpIC0+ICBzZXZpbGxhX2FndWFjYXRlDQoNCnNldmlsbGFfYWd1YWNhdGUNCmBgYA0KDQpgYGB7cn0NCg0KZyA8LSBnZ3Bsb3QoYWVzKHg9WUVBUiwgeT10b25fUHJvZC8xMDAwKSwgZGF0YSA9IHNldmlsbGFfYWd1YWNhdGUpICsgZ2VvbV9iYXIoc3RhdD0naWRlbnRpdHknLHdpZHRoID0gMC41LCBmaWxsID0gImJsdWV2aW9sZXQiKSArIGxhYnMoeT0nUHJvZHVjY2lvbiBkZSBhZ3VhY2F0ZSBbVG9uIHggMTAwMF0nKQ0KYGBgDQoNCmBgYHtyfQ0KZyArIGdndGl0bGUoIkV2b2x1Y2nDs24gZGUgbGEgcHJvZHVjY2nDs24gZGUgYWd1YWNhdGUgZW4gU2V2aWxsYSBlbnRyZSAyMDA3IHkgMjAxOCIpICsgbGFicyhjYXB0aW9uPSAiQmFzYWRvIGVuIGRhdG9zIEVWQSAoTWluYWdyaWN1bHR1cmEsIDIwMjApIikNCmBgYA0KDQpTZSBxdWllcmUgY29ub2NlciBsYSBwcm9kdWNjacOzbiBkZSBjYWbDqSBlbiBEYWd1YSwgbXVuaWNpcGlvIGRlIFZhbGxlIGRlbCBDYXVjYToNCg0KYGBge3J9DQpldmFfdmFsbGUgJT4lIA0KICBmaWx0ZXIoTVVOSUNJUElPPT0iREFHVUEiICYgQ1VMVElWTz09IkNBRkUiKSAlPiUgDQogIGdyb3VwX2J5KFlFQVIsIENVTFRJVk8pICU+JQ0KICBzZWxlY3QoTVVOSUNJUElPLCBDVUxUSVZPLCB0b25fUHJvZCwgWUVBUikgLT4gIGRhZ3VhX2NhZmUNCg0KZGFndWFfY2FmZQ0KYGBgDQoNCmBgYHtyfQ0KZyA8LSBnZ3Bsb3QoYWVzKHg9WUVBUiwgeT10b25fUHJvZC8xMDAwKSwgZGF0YSA9IGRhZ3VhX2NhZmUpICsgZ2VvbV9iYXIoc3RhdD0naWRlbnRpdHknLHdpZHRoID0gMC41LCBmaWxsID0gImNhZGV0Ymx1ZTMiKSArIGxhYnMoeT0nUHJvZHVjY2lvbiBkZSBjYWbDqSBbVG9uIHggMTAwMF0nKQ0KYGBgDQoNCmBgYHtyfQ0KZyArIGdndGl0bGUoIkV2b2x1Y2nDs24gZGUgbGEgcHJvZHVjY2nDs24gZGUgY2Fmw6kgZW4gRGFndWEgZW50cmUgMjAwNyB5IDIwMTgiKSArIGxhYnMoY2FwdGlvbj0gIkJhc2FkbyBlbiBkYXRvcyBFVkEgKE1pbmFncmljdWx0dXJhLCAyMDIwKSIpDQpgYGANCg0KUG9yIHVsdGltbywgc2UgcXVpZXJlIGNvbm9jZXIgbGEgcHJvZHVjY2nDs24gZGUgY2HDsWEgZGUgYXrDumNhciBkZWwgZGVwYXJ0YW1lbnRvIGRlYmlkbyBhIHF1ZSBlcyB1bm8gZGUgbG9zIHByb2R1Y3RvcyBxdWUgc3VzdGVudGFuIGxhIGVjb25vbcOtYSBkZSBWYWxsZSBkZWwgQ2F1Y2E6DQoNCmBgYHtyfQ0KZXZhX3ZhbGxlICU+JSANCiAgZmlsdGVyKENVTFRJVk89PSJDQU5BIEFaVUNBUkVSQSIpICU+JSANCiAgZ3JvdXBfYnkoWUVBUiwgQ1VMVElWTykgJT4lDQogIHNlbGVjdChDVUxUSVZPLCB0b25fUHJvZCwgWUVBUikgLT4gIHZhbGxlX2NhbmENCg0KdmFsbGVfY2FuYQ0KYGBgDQoNCmBgYHtyfQ0KZyA8LSBnZ3Bsb3QoYWVzKHg9WUVBUiwgeT10b25fUHJvZC8xMDAwKSwgZGF0YSA9IHZhbGxlX2NhbmEpICsgZ2VvbV9iYXIoc3RhdD0naWRlbnRpdHknLHdpZHRoID0gMC41LCBmaWxsID0gImRhcmtvcmFuZ2UyIikgKyBsYWJzKHk9J1Byb2R1Y2Npb24gZGUgY2HDsWEgZGUgYXrDumNhciBbVG9uIHggMTAwMF0nKQ0KYGBgDQoNCmBgYHtyfQ0KZyArIGdndGl0bGUoIkV2b2x1Y2nDs24gZGUgbGEgUHJvZHVjY2nDs24gZGUgY2HDsWEgZGUgYXrDumNhciBlbiBWYWxsZSBkZWwgQ2F1Y2EgZGVzZGUgMjAwNyBoYXN0YSAyMDE4IikgKyBsYWJzKGNhcHRpb249ICJCYXNhZG8gZW4gZGF0b3MgRVZBIChNaW5hZ3JpY3VsdHVyYSwgMjAyMCkiKQ0KYGBgDQoNClBvciDDumx0aW1vLCBzZSBxdWllcmUgY29ub2NlciBsYSBwcm9kdWNjacOzbiBkZSBjYcOxYSBkZSBhesO6Y2FyIGRlIFRydWppbGxvLCBWYWxsZSBkZWwgQ2F1Y2E6DQoNCmBgYHtyfQ0KZXZhX3ZhbGxlICU+JSANCiAgZmlsdGVyKE1VTklDSVBJTz09IlRSVUpJTExPIiAmIENVTFRJVk89PSJDQU5BIEFaVUNBUkVSQSIpICU+JSANCiAgZ3JvdXBfYnkoWUVBUiwgQ1VMVElWTykgJT4lDQogIHNlbGVjdChNVU5JQ0lQSU8sIENVTFRJVk8sIHRvbl9Qcm9kLCBZRUFSKSAtPiAgdHJ1amlsbG9fY2FuYQ0KDQp0cnVqaWxsb19jYW5hDQpgYGANCg0KYGBge3J9DQpnIDwtIGdncGxvdChhZXMoeD1ZRUFSLCB5PXRvbl9Qcm9kLzEwMDApLCBkYXRhID0gdHJ1amlsbG9fY2FuYSkgKyBnZW9tX2JhcihzdGF0PSdpZGVudGl0eScsd2lkdGggPSAwLjUsIGZpbGwgPSAiZGFya29saXZlZ3JlZW4zIikgKyBsYWJzKHk9J1Byb2R1Y2Npb24gZGUgY2HDsWEgZGUgYXrDumNhciBbVG9uIHggMTAwMF0nKQ0KYGBgDQoNCmBgYHtyfQ0KZyArIGdndGl0bGUoIkV2b2x1Y2nDs24gZGUgbGEgUHJvZHVjY2nDs24gZGUgY2HDsWEgZGUgYXrDumNhciBlbiBUcnVqaWxsbyBkZXNkZSAyMDA3IGhhc3RhIDIwMTgiKSArIGxhYnMoY2FwdGlvbj0gIkJhc2FkbyBlbiBkYXRvcyBFVkEgKE1pbmFncmljdWx0dXJhLCAyMDIwKSIpDQpgYGANCg==