1 Bibliotecas

library(tidyverse) # Manipulación de datos y gráficos
library(janitor)   # Edición de nombres en bases de datos
library(moments)   # Cálculo de coefiente de asimetría y curtosis
library(DT)        # Tablas interactivas en documentos HTML

2 Tipos de variables

2.1 Numéricas o cuantitativas

2.1.1 Discretas

  • Nota: en R numeric y double representan lo mismo, es decir, ambos representan números.
entero1 <- 12
class(entero1)
## [1] "numeric"
typeof(entero1)
## [1] "double"

Hago una suma:

entero1 + 50
## [1] 62
  • ¿Cómo puedo forzar a R para que entiena un número discreto (entero)? Nota: este procedimiento es opcional.
entero2 <- 12L
class(entero2)
## [1] "integer"
typeof(entero2)
## [1] "integer"

2.1.2 Continuas

continua1 <- 12.3
class(continua1)
## [1] "numeric"
typeof(continua1)
## [1] "double"
continua1 + 50
## [1] 62.3
  • Creando un vector de valores continuos:
vector_numerico <- c(12.3, 4.5, 6.8)
class(vector_numerico)
## [1] "numeric"

2.2 Cualitativas o categóricas

  • En R siempre este tipo de variables estarán entre comillas
  • A veces llamadas string
  • En R se conocen como character

2.2.1 Nominales

texto1 <- "Medellín"
class(texto1)
## [1] "character"
  • Podemos crear vectores de texto:
ciudad <- c("Medellín", "Cali", "Bogotá")
class(ciudad)
## [1] "character"

2.2.2 Ordinales

  • En R las variables ordinales se denominan factores
  • Los factores tienen niveles (etiquetas) y orden
  • Los factores en R se crean la función factor()
  • Por defecto R ordena el texto de manera alfabética
factor_desordenado <- factor(c("enero", "febrero", "marzo", "abril", "mayo",
                               "junio"))

factor_desordenado
## [1] enero   febrero marzo   abril   mayo    junio  
## Levels: abril enero febrero junio marzo mayo
  • Podemos forzar a R para que ordene los niveles de un factor:
factor_ordenado <-
  factor(
    c("enero", "febrero", "marzo", "abril", "mayo",
      "junio"),
    levels = c("enero", "febrero", "marzo", "abril",
               "mayo", "junio")
  )

factor_ordenado
## [1] enero   febrero marzo   abril   mayo    junio  
## Levels: enero febrero marzo abril mayo junio

3 EVA-Municipales 2021

eva <- read_csv("Evaluaciones_Agropecuarias_Municipales___EVA._2019_-_2021._Base_Agr_cola.csv")

eva %>% head(n = 5)
  • Podemos usar la función glimpse() para conocer cómo R tipificó cada columna o variable:
eva %>% glimpse()
## Rows: 23,786
## Columns: 18
## $ `Código Dane departamento`      <chr> "05", "05", "05", "05", "05", "05", "0~
## $ Departamento                    <chr> "Antioquia", "Antioquia", "Antioquia",~
## $ `Código Dane municipio`         <chr> "05001", "05001", "05001", "05001", "0~
## $ Municipio                       <chr> "Medellín", "Medellín", "Medellín", "M~
## $ `Grupo cultivo`                 <chr> "Cultivos Tropicales Tradicionales", "~
## $ Subgrupo                        <chr> "Cultivos Tropicales Tradicionales", "~
## $ Cultivo                         <chr> "Café", "Caña", "Limón", "Mandarina", ~
## $ `Desagregación cultivo`         <chr> "Café", "Caña Panelera", "Limón  Tahit~
## $ Año                             <dbl> 2021, 2021, 2021, 2021, 2021, 2021, 20~
## $ Periodo                         <chr> "2021", "2021", "2021", "2021", "2021"~
## $ `Área sembrada`                 <dbl> 437.58, 77.20, 4.50, 14.50, 12.50, 8.5~
## $ `Área cosechada`                <dbl> 378.22, 77.20, 4.50, 14.50, 12.50, 4.5~
## $ Producción                      <dbl> 276.10, 4632.00, 36.00, 87.00, 87.50, ~
## $ Rendimiento                     <dbl> 0.73, 60.00, 8.00, 6.00, 7.00, 6.00, 6~
## $ `Ciclo del cultivo`             <chr> "Permanente", "Permanente", "Permanent~
## $ `Estado físico del cultivo`     <chr> "Pergamino O Seco De Trilla", "Caña O ~
## $ `Código del cultivo`            <dbl> 2030300, 2030402, 2043603, 2044101, 20~
## $ `Nombre científico del cultivo` <chr> "Coffea arabica L.", "Saccharum offici~
  • Podemos “limpiar” (editar) los nombres de las variables con la función clean_names() del paquete janitor
eva_depurada <- eva %>% 
  clean_names()

eva_depurada %>% head()
  • También es posible preguntar la clase de cada variable o columna de la base de datos (dataframe):
class(eva_depurada$rendimiento)
## [1] "numeric"

4 Manipulación de datos

4.1 Procesos frecuentes


4.1.1 Conteo de registros

  • ¿Cuántos registros hay por año?
eva_depurada %>%
  count(ano)
  • ¿Cuántos registros hay por cultivo?
eva_depurada %>% 
  count(cultivo)
  • Hacemos el mismo cálculo anterior pero ordenamos el resultado (n):
eva_depurada %>% 
  count(cultivo) %>% 
  arrange(n)
  • Hacemos el mismo cálculo anterior pero ordenamos el resultado (n) de manera descendente (desc):
eva_depurada %>% 
  count(cultivo) %>% 
  arrange(desc(n))
  • ¿Cuántos registros hay por cada cultivo en cada departamento?
eva_depurada %>% 
  count(cultivo, departamento) %>% 
  arrange(desc(n))

4.1.2 Seleccionar columnas

  • Podemos usar la función select() para seleccionar columnas:
datos_select <-
  eva_depurada %>%
  select(
    departamento,
    municipio,
    cultivo,
    ano,
    periodo,
    area_sembrada,
    area_cosechada,
    rendimiento
  )

datos_select %>% head()

4.1.3 Filtrar filas

  • Podemos filtar filas con la función filter():
    • El igual en R se denota por doble símbolo de igual (==)
    • La diferencia en R se denota por signo de admiración de cierre y el igual (!=)
datos_palmira <- 
  datos_select %>% 
  filter(municipio == "Palmira")

datos_palmira
  • Podemos filtrar sólo algunos cultivos, por ejemplo, Limón y Café:
    • %in%: se lee como “dentro de”
limon_cafe <-
  datos_select %>% 
  filter(cultivo %in% c("Limón", "Café"))

limon_cafe
  • También podemos filtrar filas que cumplan ciertas condiciones para variables numéricas: por ejemplo filas que superen las 50 hectáreas de área sembrada
area_mayor50 <- 
  datos_select %>% 
  filter(area_sembrada > 50)

area_mayor50
  • ¿Cuántos registros para el cultivo de Café superan las 120 hectáreas?
datos_select %>% 
  filter(cultivo == "Café") %>% 
  filter(area_sembrada > 120) 

4.1.4 Mutar columnas

  • Con la función mutate() podemos editar las variables existentes y crear nuevas variables.
  • Podríamos calcular el área perdida como una variable nueva:

\[Área\ perdida (\%) = \left(1 - \frac{Área\ cosechada}{ Área\ sembrada} \right) \times 100\]

datos_perdida <-
  datos_select %>%
  mutate(area_perdida_p = (1 - (area_cosechada / area_sembrada)) * 100) 

datos_perdida %>% head()
  • También podríamos obtener el total de área perdida:

\[Área\ perdida (ha) = Área\ sembrada - Área\ cosechada\]

datos_perdida_ha <-
  datos_perdida %>%
  mutate(area_perdida_ha = area_sembrada - area_cosechada) 

datos_perdida_ha %>% head()

4.1.5 Proceso juntos

  • Podemos usar las funciones count(), select(),filter(), mutate(), arrange() juntas a través de la tubería %>%
eva_antioquia <-
  eva_depurada %>%
  select(
    departamento,
    municipio,
    cultivo,
    ano,
    periodo,
    area_sembrada,
    area_cosechada,
    rendimiento
  ) %>%
  filter(departamento == "Antioquia") %>% 
  mutate(area_perdida_p = (1 - (area_cosechada / area_sembrada)) * 100,
         area_perdida_ha = area_sembrada - area_cosechada) 

eva_antioquia %>% head()

4.1.6 summarise()

  • Podríamos sumar el total de área sembrada en el año 2021 para Antioquia:
eva_antioquia %>% 
  summarise(sum(area_sembrada))

4.1.7 Resumir datos

  • group_by() %>% summarise() %>% ungroup()
  • ¿Cuál fue el cultivo con la mayor área perdida en Antioquia para el año 2021?
eva_antioquia %>% 
  group_by(cultivo) %>% 
  summarise(total_area_ha = sum(area_perdida_ha)) %>% 
  ungroup() %>% # opcional pero es una buena práctica
  arrange(desc(total_area_ha))
  • ¿Cuánto fue el total de área perdida en Antioquia para el año 2021?
eva_antioquia %>% 
  summarise(total_area_ha = sum(area_perdida_ha))
  • Si asumimos que un campo de fútbol tiene aproximadamente \(0.64-0.82\) hectáreas, ¿cuántos “campos de fútbol” se perdieron en Antioquia en el año 2021?
46573.14 / 0.64
## [1] 72770.53
46573.14 / 0.82
## [1] 56796.51
(46573.14 / 1292549) * 100
## [1] 3.603201
  • ¿El cálculo anterior es correcto? ¿Estamos subestimando el área perdida en Antioquia?
# Área perdida: 46573.14
# Área total sembrada: 435373.3

(46573.14 / 435373.3) * 100
## [1] 10.69729

5 Métricas estadísticas

5.1 Funciones en R

Función Descripción Tipo de variable
mean() Calcular promedio cuantitativa
weighted.mean() Calcular promedio ponderado Cunatitativa
median() Calcular mediana Cuantitativa
sd() Calcular desviación estándar cuantitativa
var() Calcular la varianza Cuantitativa
range() Calcular el rango Cuantitativa
IQR() Calcular rango intercuartílico Cuantitativa
quantile() Calcular cuartiles, deciles y percentiles Cuantitativa
min() Valor mínimo Cuantitativa
max() Valor máximo Cuantitativa
* moda() Moda Cuantitativa y cualitativa

5.2 Valores ausentes

ejemplo <- c(1, 2, 3, NA)
ejemplo %>% mean(na.rm = TRUE) # na.rm = TRUE para no tener en cuenta los NA
## [1] 2

5.3 Tendencia central

5.3.1 Media

  • ¿Cuál es el promedio de rendimiento para el cultivo de mango?
eva_depurada %>% 
  filter(cultivo == "Mango") %>% 
  summarise(promedio_rto = mean(rendimiento))
  • Podemos construir una tabla con información que responda a las siguientes preguntas sobre el cultivo de mango:
    • ¿En cuántos departamentos de Colombia se cultivó mango en el 2021?
    • ¿Cuál es el departamento con el mayor rendimiento promedio?
    • ¿Cuál es el total de área sembrada en cada departamento?
    • ¿Con cuántos datos se calculó el promedio para cada departamento?
eva_depurada %>%
  filter(cultivo == "Mango") %>%
  group_by(departamento) %>%
  summarise(
    promedio_rto = mean(rendimiento),
    area_cosechada = sum(area_cosechada),
    N = n()
  ) %>%
  ungroup() %>%
  arrange(desc(promedio_rto))

5.3.2 Media ponderada

  • ¿Cuál es el promedio ponderado por área cosechada para el cultivo de mango?
eva_depurada %>%
  filter(cultivo == "Mango") %>% 
  summarise(prom_rto_pond = weighted.mean(x = rendimiento, w = area_cosechada))
  • Podemos agregar esta métrica a cada departamento:
eva_depurada %>%
  filter(cultivo == "Mango") %>%
  group_by(departamento) %>%
  summarise(
    promedio_rto = mean(rendimiento),
    promedio_rto_pond = weighted.mean(x = rendimiento, w = area_cosechada),
    area_cosechada = sum(area_cosechada),
    N = n()
  ) %>%
  ungroup() %>%
  arrange(desc(promedio_rto))

5.3.3 Mediana

  • Siguiente con el ejemplo del mango, ¿Cuál es la mediana del rendimiento para cada departamento? ¿Difiere del promedio?
eva_depurada %>% 
  filter(cultivo == "Mango") %>%
  group_by(departamento) %>% 
  summarise(mediana_rto = median(rendimiento),
            promedio_rto = mean(rendimiento)) %>% 
  ungroup() %>%
  arrange(desc(mediana_rto))

5.3.4 Moda

  • Como la función moda no está contenida dentro de R, usamos una función personalizada:
moda <- function(x) {
  ux = unique(x)
  tab = tabulate(match(x, ux))
  ux[tab == max(tab)]
}
  • ¿Cuál es la moda de departamento?
eva_depurada %>% 
  summarise(moda_depto = moda(departamento))
  • El resultado anterior es equivalente a obtener las frecuencias absolutas y ordenar de manera descendente:
eva_depurada %>% 
  count(departamento, sort = TRUE)

5.4 Medidas de dispersión

5.4.1 D. Estándar

  • ¿Cuál es la desviación estándar del rendimiento en el cultivo de mango?
eva_depurada %>% 
  filter(cultivo == "Mango") %>% 
  summarise(desv_est_rto = sd(rendimiento))
  • Para el cultivo de mango, ¿Cuál de los departamentos presentó menor dispersión en el rendimiento?
eva_depurada %>% 
  filter(cultivo == "Mango") %>% 
  group_by(departamento) %>% 
  summarise(promedio_rto = mean(rendimiento),
            desv_est_rto = sd(rendimiento),
            cv = desv_est_rto / promedio_rto,
            N = n()) %>% 
  ungroup() %>% 
  arrange(desc(promedio_rto))

5.4.2 Varianza

  • ¿Cuál es la varianza del rendimiento para el cultivo de mango?
eva_depurada %>% 
  filter(cultivo == "Mango") %>%
  summarise(varianza_rto = var(rendimiento))
  • Si obtenemos la raíz cuadrada de la varianza obtenemos la desviación estándar:
30.08333 %>% 
  sqrt()
## [1] 5.484827

5.4.3 Rango

  • ¿Cuál es el rango de rendimiento para el cultivo de mango? ¡CERO es el valor mínimo! 🤔
eva_depurada %>% 
  filter(cultivo == "Mango") %>% 
  filter(rendimiento > 0) %>% 
  summarise(rango = range(rendimiento))
  • Podemos obtener el valor mínimo y máximo en la misma tabla:
eva_depurada %>% 
  filter(cultivo == "Mango") %>% 
  summarise(minimo_rto = min(rendimiento),
            maximo_rto = max(rendimiento))

5.4.4 Rango intercuartílico

  • ¿Cuál es el rango intercuartílico para el cultivo de mango?
eva_depurada %>% 
  filter(cultivo == "Mango") %>% 
  summarise(rango_ic = IQR(rendimiento))

5.5 Medidas de posición

5.5.1 Cuartiles

  • ¿Cuál es el valor de los cuartiles para el cultivo de mango?
eva_depurada %>% 
  filter(cultivo == "Mango") %>% 
  summarise(cuartiles = quantile(rendimiento,
                                 probs = seq(from = 0, to = 1, by = 0.25)))
  • Podemos llegar al mismo resultado de la siguiente manera:
eva_depurada %>% 
  filter(cultivo == "Mango") %>% 
  pull(rendimiento) %>% 
  quantile(probs = seq(from = 0, to = 1, by = 0.25))
##   0%  25%  50%  75% 100% 
##  0.0  6.0  9.0 13.1 30.0

5.5.2 Deciles

  • ¿Cuál es el valor de los deciles para el cultivo de mango?
eva_depurada %>% 
  filter(cultivo == "Mango") %>% 
  pull(rendimiento) %>% 
  quantile(probs = seq(from = 0, to = 1, by = 0.1))
##     0%    10%    20%    30%    40%    50%    60%    70%    80%    90%   100% 
##  0.000  3.174  5.000  6.308  8.000  9.000 10.000 12.000 14.000 16.000 30.000

5.5.3 Percentiles

  • ¿Cuál es el valor de los percentiles para el cultivo de mango?
eva_depurada %>% 
  filter(cultivo == "Mango") %>% 
  pull(rendimiento) %>% 
  quantile(probs = seq(from = 0, to = 1, by = 0.01))
##      0%      1%      2%      3%      4%      5%      6%      7%      8%      9% 
##  0.0000  0.0000  0.0000  0.9740  1.2552  2.0000  2.5232  2.9530  3.0000  3.0000 
##     10%     11%     12%     13%     14%     15%     16%     17%     18%     19% 
##  3.1740  3.5266  3.9900  4.0000  4.1904  5.0000  5.0000  5.0000  5.0000  5.0000 
##     20%     21%     22%     23%     24%     25%     26%     27%     28%     29% 
##  5.0000  5.5000  5.5976  6.0000  6.0000  6.0000  6.0000  6.0000  6.0000  6.0000 
##     30%     31%     32%     33%     34%     35%     36%     37%     38%     39% 
##  6.3080  6.9900  7.0000  7.0000  7.0000  7.0000  7.0000  7.3380  7.5968  8.0000 
##     40%     41%     42%     43%     44%     45%     46%     47%     48%     49% 
##  8.0000  8.0000  8.0000  8.0000  8.0000  8.0000  8.4424  8.7346  9.0000  9.0000 
##     50%     51%     52%     53%     54%     55%     56%     57%     58%     59% 
##  9.0000  9.0000  9.8320 10.0000 10.0000 10.0000 10.0000 10.0000 10.0000 10.0000 
##     60%     61%     62%     63%     64%     65%     66%     67%     68%     69% 
## 10.0000 10.0000 10.1920 11.0000 11.0000 11.6510 12.0000 12.0000 12.0000 12.0000 
##     70%     71%     72%     73%     74%     75%     76%     77%     78%     79% 
## 12.0000 12.0000 12.7792 13.0000 13.0000 13.1000 13.5000 13.9626 14.0000 14.0000 
##     80%     81%     82%     83%     84%     85%     86%     87%     88%     89% 
## 14.0000 14.1862 15.0000 15.0000 15.0000 15.0000 15.0000 15.0000 15.0000 15.0000 
##     90%     91%     92%     93%     94%     95%     96%     97%     98%     99% 
## 16.0000 16.0000 18.0000 18.0000 20.0000 20.0000 20.0000 20.6374 24.8400 25.7268 
##    100% 
## 30.0000
  • Podemos obtener el valor de un percentil específico, por ejemplo, el percentil 95:
eva_depurada %>% 
  filter(cultivo == "Mango") %>% 
  pull(rendimiento) %>% 
  quantile(probs = 0.12)
##  12% 
## 3.99

5.6 Medidas de distribución

5.6.1 Asimetría

  • ¿Exhibe simetría la variable rendimiento para el cultivo de mango?
eva_depurada %>% 
  filter(cultivo == "Mango") %>% 
  group_by(departamento) %>% 
  summarise(coef_asimetria = skewness(rendimiento))

5.6.2 Curtosis

  • ¿Cuál es el coeficiente de curtosis de la variable rendimiento para el cultivo de mango?
eva_depurada %>% 
  filter(cultivo == "Mango") %>% 
  group_by(departamento) %>% 
  summarise(coef_asimetria = kurtosis(rendimiento))

5.7 Tabla descriptiva mango

  • Podemos construir un resumen descriptivo en forma de tabla con todas las métricas que consideremos relevantes. En este caso es un ejemplo para e cultivo de mango por cada departamento:
tabla_resumen_mango <-
  eva_depurada %>%
  filter(cultivo == "Mango") %>%
  group_by(departamento) %>%
  summarise(
    promedio_rto = mean(rendimiento),
    promedio_rto_pond = weighted.mean(rendimiento, area_cosechada),
    mediana_rto = median(rendimiento),
    min_rto = min(rendimiento),
    max_rto = max(rendimiento),
    desv_est_rto = sd(rendimiento),
    percentil5_rto = quantile(rendimiento, probs = 0.05),
    percentil95_rto = quantile(rendimiento, probs = 0.95),
    area_sembrada = sum(area_sembrada),
    area_cosechada = sum(area_cosechada),
    N = n()
  ) %>%
  ungroup() %>%
  arrange(desc(promedio_rto))

tabla_resumen_mango
  • Podemos agregar características que faciliten la visualización de la tabla anterior:
tabla_resumen_mango %>%
  mutate(across(where(is.numeric), round, digits = 2)) %>%
  datatable(
    rownames = FALSE,
    extensions = c('Buttons', 'FixedColumns'),
    options = list(
      dom = 'Bfrtip',
      buttons = c('excel'),
      scrollX = TRUE,
      fixedColumns = list(leftColumns = 1),
      language = list(url = '//cdn.datatables.net/plug-ins/1.10.11/i18n/Spanish.json')
    )
  )
LS0tDQp0aXRsZTogIkVzdGFkw61zdGljYSBkZXNjcmlwdGl2YSBjb24gUiINCnN1YnRpdGxlOiAiVGlwb3MgZGUgdmFyaWFibGVzLCBtw6l0cmljYXMgeSBncsOhZmljb3MiDQphdXRob3I6ICJFZGltZXIgRGF2aWQgSmFyYW1pbGxvIg0KZGF0ZTogIjIwLTA5LTIwMjIiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6DQogICAgdG9jOiB0cnVlDQogICAgdG9jX2RlcHRoOiA1DQogICAgdG9jX2Zsb2F0OiB0cnVlDQogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlDQogICAgdGhlbWU6IGNvc21vDQogICAgaGlnaGxpZ2h0OiBicmVlemVkYXJrDQogICAgZGZfcHJpbnQ6IHBhZ2VkDQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KLS0tDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCBldmFsID0gVFJVRSwgZmlnLmFsaWduID0gImNlbnRlciIsDQogICAgICAgICAgICAgICAgICAgICAgZmlnLndpZHRoID0gNS41LCBmaWcuaGVpZ2h0ID0gNC41LCB3YXJuaW5nID0gRkFMU0UsDQogICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZSA9IEZBTFNFKQ0KYGBgDQoNCiMgQmlibGlvdGVjYXMNCg0KYGBge3J9DQpsaWJyYXJ5KHRpZHl2ZXJzZSkgIyBNYW5pcHVsYWNpw7NuIGRlIGRhdG9zIHkgZ3LDoWZpY29zDQpsaWJyYXJ5KGphbml0b3IpICAgIyBFZGljacOzbiBkZSBub21icmVzIGVuIGJhc2VzIGRlIGRhdG9zDQpsaWJyYXJ5KG1vbWVudHMpICAgIyBDw6FsY3VsbyBkZSBjb2VmaWVudGUgZGUgYXNpbWV0csOtYSB5IGN1cnRvc2lzDQpsaWJyYXJ5KERUKSAgICAgICAgIyBUYWJsYXMgaW50ZXJhY3RpdmFzIGVuIGRvY3VtZW50b3MgSFRNTA0KYGBgDQoNCg0KIyBUaXBvcyBkZSB2YXJpYWJsZXMNCg0KIyMgTnVtw6lyaWNhcyBvIGN1YW50aXRhdGl2YXMNCg0KIyMjIERpc2NyZXRhcw0KDQotICAgKipOb3RhOioqIGVuIFIgbnVtZXJpYyB5IGRvdWJsZSByZXByZXNlbnRhbiBsbyBtaXNtbywgZXMgZGVjaXIsIGFtYm9zIHJlcHJlc2VudGFuIG7Dum1lcm9zLg0KDQpgYGB7cn0NCmVudGVybzEgPC0gMTINCmNsYXNzKGVudGVybzEpDQp0eXBlb2YoZW50ZXJvMSkNCmBgYA0KDQpIYWdvIHVuYSBzdW1hOg0KDQpgYGB7cn0NCmVudGVybzEgKyA1MA0KYGBgDQoNCi0gICDCv0PDs21vIHB1ZWRvIGZvcnphciBhIFIgcGFyYSBxdWUgZW50aWVuYSB1biBuw7ptZXJvIGRpc2NyZXRvIChlbnRlcm8pPyAqKk5vdGE6KiogZXN0ZSBwcm9jZWRpbWllbnRvIGVzICoqb3BjaW9uYWwqKi4NCg0KYGBge3J9DQplbnRlcm8yIDwtIDEyTA0KY2xhc3MoZW50ZXJvMikNCnR5cGVvZihlbnRlcm8yKQ0KYGBgDQoNCiMjIyBDb250aW51YXMNCg0KYGBge3J9DQpjb250aW51YTEgPC0gMTIuMw0KY2xhc3MoY29udGludWExKQ0KdHlwZW9mKGNvbnRpbnVhMSkNCmBgYA0KDQpgYGB7cn0NCmNvbnRpbnVhMSArIDUwDQpgYGANCg0KLSAgIENyZWFuZG8gdW4gdmVjdG9yIGRlIHZhbG9yZXMgY29udGludW9zOg0KDQpgYGB7cn0NCnZlY3Rvcl9udW1lcmljbyA8LSBjKDEyLjMsIDQuNSwgNi44KQ0KY2xhc3ModmVjdG9yX251bWVyaWNvKQ0KYGBgDQoNCiMjIEN1YWxpdGF0aXZhcyBvIGNhdGVnw7NyaWNhcw0KDQotICAgRW4gUiBzaWVtcHJlIGVzdGUgdGlwbyBkZSB2YXJpYWJsZXMgZXN0YXLDoW4gZW50cmUgY29taWxsYXMNCi0gICBBIHZlY2VzIGxsYW1hZGFzICpzdHJpbmcqDQotICAgRW4gUiBzZSBjb25vY2VuIGNvbW8gKmNoYXJhY3RlcioNCg0KIyMjIE5vbWluYWxlcw0KDQpgYGB7cn0NCnRleHRvMSA8LSAiTWVkZWxsw61uIg0KY2xhc3ModGV4dG8xKQ0KYGBgDQoNCi0gICBQb2RlbW9zIGNyZWFyIHZlY3RvcmVzIGRlIHRleHRvOg0KDQpgYGB7cn0NCmNpdWRhZCA8LSBjKCJNZWRlbGzDrW4iLCAiQ2FsaSIsICJCb2dvdMOhIikNCmNsYXNzKGNpdWRhZCkNCmBgYA0KDQojIyMgT3JkaW5hbGVzDQoNCi0gICBFbiBSIGxhcyB2YXJpYWJsZXMgb3JkaW5hbGVzIHNlIGRlbm9taW5hbiAqZmFjdG9yZXMqDQotICAgTG9zIGZhY3RvcmVzIHRpZW5lbiBuaXZlbGVzIChldGlxdWV0YXMpIHkgb3JkZW4NCi0gICBMb3MgZmFjdG9yZXMgZW4gUiBzZSBjcmVhbiBsYSBmdW5jacOzbiAqKmZhY3RvcigpKioNCi0gICBQb3IgZGVmZWN0byBSIG9yZGVuYSBlbCB0ZXh0byBkZSBtYW5lcmEgYWxmYWLDqXRpY2ENCg0KYGBge3J9DQpmYWN0b3JfZGVzb3JkZW5hZG8gPC0gZmFjdG9yKGMoImVuZXJvIiwgImZlYnJlcm8iLCAibWFyem8iLCAiYWJyaWwiLCAibWF5byIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImp1bmlvIikpDQoNCmZhY3Rvcl9kZXNvcmRlbmFkbw0KYGBgDQoNCi0gICBQb2RlbW9zIGZvcnphciBhIFIgcGFyYSBxdWUgb3JkZW5lIGxvcyBuaXZlbGVzIGRlIHVuIGZhY3RvcjoNCg0KYGBge3J9DQpmYWN0b3Jfb3JkZW5hZG8gPC0NCiAgZmFjdG9yKA0KICAgIGMoImVuZXJvIiwgImZlYnJlcm8iLCAibWFyem8iLCAiYWJyaWwiLCAibWF5byIsDQogICAgICAianVuaW8iKSwNCiAgICBsZXZlbHMgPSBjKCJlbmVybyIsICJmZWJyZXJvIiwgIm1hcnpvIiwgImFicmlsIiwNCiAgICAgICAgICAgICAgICJtYXlvIiwgImp1bmlvIikNCiAgKQ0KDQpmYWN0b3Jfb3JkZW5hZG8NCmBgYA0KDQojIEVWQS1NdW5pY2lwYWxlcyAyMDIxDQoNCmBgYHtyfQ0KZXZhIDwtIHJlYWRfY3N2KCJFdmFsdWFjaW9uZXNfQWdyb3BlY3Vhcmlhc19NdW5pY2lwYWxlc19fX0VWQS5fMjAxOV8tXzIwMjEuX0Jhc2VfQWdyX2NvbGEuY3N2IikNCg0KZXZhICU+JSBoZWFkKG4gPSA1KQ0KYGBgDQoNCi0gICBQb2RlbW9zIHVzYXIgbGEgZnVuY2nDs24gKmdsaW1wc2UoKSogcGFyYSBjb25vY2VyIGPDs21vIFIgdGlwaWZpY8OzIGNhZGEgY29sdW1uYSBvIHZhcmlhYmxlOg0KDQpgYGB7cn0NCmV2YSAlPiUgZ2xpbXBzZSgpDQpgYGANCg0KLSAgIFBvZGVtb3MgImxpbXBpYXIiIChlZGl0YXIpIGxvcyBub21icmVzIGRlIGxhcyB2YXJpYWJsZXMgY29uIGxhIGZ1bmNpw7NuICpjbGVhbl9uYW1lcygpKiBkZWwgcGFxdWV0ZSAqKmphbml0b3IqKg0KDQpgYGB7cn0NCmV2YV9kZXB1cmFkYSA8LSBldmEgJT4lIA0KICBjbGVhbl9uYW1lcygpDQoNCmV2YV9kZXB1cmFkYSAlPiUgaGVhZCgpDQpgYGANCg0KLSAgIFRhbWJpw6luIGVzIHBvc2libGUgcHJlZ3VudGFyIGxhIGNsYXNlIGRlIGNhZGEgdmFyaWFibGUgbyBjb2x1bW5hIGRlIGxhIGJhc2UgZGUgZGF0b3MgKGRhdGFmcmFtZSk6DQoNCmBgYHtyfQ0KY2xhc3MoZXZhX2RlcHVyYWRhJHJlbmRpbWllbnRvKQ0KYGBgDQoNCiMgTWFuaXB1bGFjacOzbiBkZSBkYXRvcw0KDQojIyBQcm9jZXNvcyBmcmVjdWVudGVzDQoNCjxjZW50ZXI+DQo8aW1nIHNyYyA9ICJodHRwOi8vcGVyc28uZW5zLWx5b24uZnIvbGlzZS52YXVkb3IvUmZpZ3VyZXMvZHBseXIvZHBseXJfc2NoZW1hLnBuZyIgLz4NCjwvY2VudGVyPg0KDQo8YnI+DQoNCjxjZW50ZXI+DQo8aW1nIHNyYyA9ICJodHRwczovL3N3Y2FycGVudHJ5LmdpdGh1Yi5pby9yLW5vdmljZS1nYXBtaW5kZXIvZmlnLzEzLWRwbHlyLWZpZzIucG5nIiAvPg0KPC9jZW50ZXI+DQoNCiMjIyBDb250ZW8gZGUgcmVnaXN0cm9zDQoNCi0gwr9DdcOhbnRvcyByZWdpc3Ryb3MgaGF5IHBvciBhw7FvPw0KDQpgYGB7cn0NCmV2YV9kZXB1cmFkYSAlPiUNCiAgY291bnQoYW5vKQ0KYGBgDQoNCi0gwr9DdcOhbnRvcyByZWdpc3Ryb3MgaGF5IHBvciBjdWx0aXZvPw0KDQpgYGB7cn0NCmV2YV9kZXB1cmFkYSAlPiUgDQogIGNvdW50KGN1bHRpdm8pDQpgYGANCg0KLSBIYWNlbW9zIGVsIG1pc21vIGPDoWxjdWxvIGFudGVyaW9yIHBlcm8gb3JkZW5hbW9zIGVsIHJlc3VsdGFkbyAobik6DQoNCmBgYHtyfQ0KZXZhX2RlcHVyYWRhICU+JSANCiAgY291bnQoY3VsdGl2bykgJT4lIA0KICBhcnJhbmdlKG4pDQpgYGANCg0KLSBIYWNlbW9zIGVsIG1pc21vIGPDoWxjdWxvIGFudGVyaW9yIHBlcm8gb3JkZW5hbW9zIGVsIHJlc3VsdGFkbyAobikgZGUgbWFuZXJhIGRlc2NlbmRlbnRlIChkZXNjKToNCg0KYGBge3J9DQpldmFfZGVwdXJhZGEgJT4lIA0KICBjb3VudChjdWx0aXZvKSAlPiUgDQogIGFycmFuZ2UoZGVzYyhuKSkNCmBgYA0KDQotIMK/Q3XDoW50b3MgcmVnaXN0cm9zIGhheSBwb3IgY2FkYSBjdWx0aXZvIGVuIGNhZGEgZGVwYXJ0YW1lbnRvPw0KDQpgYGB7cn0NCmV2YV9kZXB1cmFkYSAlPiUgDQogIGNvdW50KGN1bHRpdm8sIGRlcGFydGFtZW50bykgJT4lIA0KICBhcnJhbmdlKGRlc2MobikpDQpgYGANCg0KIyMjIFNlbGVjY2lvbmFyIGNvbHVtbmFzDQoNCi0gUG9kZW1vcyB1c2FyIGxhIGZ1bmNpw7NuIGBzZWxlY3QoKWAgcGFyYSBzZWxlY2Npb25hciBjb2x1bW5hczoNCg0KYGBge3J9DQpkYXRvc19zZWxlY3QgPC0NCiAgZXZhX2RlcHVyYWRhICU+JQ0KICBzZWxlY3QoDQogICAgZGVwYXJ0YW1lbnRvLA0KICAgIG11bmljaXBpbywNCiAgICBjdWx0aXZvLA0KICAgIGFubywNCiAgICBwZXJpb2RvLA0KICAgIGFyZWFfc2VtYnJhZGEsDQogICAgYXJlYV9jb3NlY2hhZGEsDQogICAgcmVuZGltaWVudG8NCiAgKQ0KDQpkYXRvc19zZWxlY3QgJT4lIGhlYWQoKQ0KYGBgDQoNCg0KIyMjIEZpbHRyYXIgZmlsYXMNCg0KLSBQb2RlbW9zIGZpbHRhciBmaWxhcyBjb24gbGEgZnVuY2nDs24gYGZpbHRlcigpYDoNCiAgLSBFbCBpZ3VhbCBlbiBSIHNlIGRlbm90YSBwb3IgZG9ibGUgc8OtbWJvbG8gZGUgaWd1YWwgKD09KQ0KICAtIExhIGRpZmVyZW5jaWEgZW4gUiBzZSBkZW5vdGEgcG9yIHNpZ25vIGRlIGFkbWlyYWNpw7NuIGRlIGNpZXJyZSB5IGVsIGlndWFsICghPSkNCg0KYGBge3J9DQpkYXRvc19wYWxtaXJhIDwtIA0KICBkYXRvc19zZWxlY3QgJT4lIA0KICBmaWx0ZXIobXVuaWNpcGlvID09ICJQYWxtaXJhIikNCg0KZGF0b3NfcGFsbWlyYQ0KYGBgDQoNCi0gUG9kZW1vcyBmaWx0cmFyIHPDs2xvIGFsZ3Vub3MgY3VsdGl2b3MsIHBvciBlamVtcGxvLCBMaW3Ds24geSBDYWbDqToNCiAgLSAlaW4lOiBzZSBsZWUgY29tbyAiZGVudHJvIGRlIg0KDQpgYGB7cn0NCmxpbW9uX2NhZmUgPC0NCiAgZGF0b3Nfc2VsZWN0ICU+JSANCiAgZmlsdGVyKGN1bHRpdm8gJWluJSBjKCJMaW3Ds24iLCAiQ2Fmw6kiKSkNCg0KbGltb25fY2FmZQ0KYGBgDQoNCi0gVGFtYmnDqW4gcG9kZW1vcyBmaWx0cmFyIGZpbGFzIHF1ZSBjdW1wbGFuIGNpZXJ0YXMgY29uZGljaW9uZXMgcGFyYSB2YXJpYWJsZXMgbnVtw6lyaWNhczogcG9yIGVqZW1wbG8gZmlsYXMgcXVlIHN1cGVyZW4gbGFzIDUwIGhlY3TDoXJlYXMgZGUgw6FyZWEgc2VtYnJhZGENCg0KYGBge3J9DQphcmVhX21heW9yNTAgPC0gDQogIGRhdG9zX3NlbGVjdCAlPiUgDQogIGZpbHRlcihhcmVhX3NlbWJyYWRhID4gNTApDQoNCmFyZWFfbWF5b3I1MA0KYGBgDQoNCi0gwr9DdcOhbnRvcyByZWdpc3Ryb3MgcGFyYSBlbCBjdWx0aXZvIGRlIENhZsOpIHN1cGVyYW4gbGFzIDEyMCBoZWN0w6FyZWFzPw0KDQpgYGB7cn0NCmRhdG9zX3NlbGVjdCAlPiUgDQogIGZpbHRlcihjdWx0aXZvID09ICJDYWbDqSIpICU+JSANCiAgZmlsdGVyKGFyZWFfc2VtYnJhZGEgPiAxMjApIA0KYGBgDQoNCg0KIyMjIE11dGFyIGNvbHVtbmFzDQoNCi0gQ29uIGxhIGZ1bmNpw7NuIGBtdXRhdGUoKWAgcG9kZW1vcyBlZGl0YXIgbGFzIHZhcmlhYmxlcyBleGlzdGVudGVzIHkgY3JlYXIgbnVldmFzIHZhcmlhYmxlcy4NCi0gUG9kcsOtYW1vcyBjYWxjdWxhciBlbCDDoXJlYSBwZXJkaWRhIGNvbW8gdW5hIHZhcmlhYmxlIG51ZXZhOg0KDQokJMOBcmVhXCBwZXJkaWRhIChcJSkgPSBcbGVmdCgxIC0gXGZyYWN7w4FyZWFcIGNvc2VjaGFkYX17IMOBcmVhXCBzZW1icmFkYX0gXHJpZ2h0KSBcdGltZXMgMTAwJCQNCg0KDQpgYGB7cn0NCmRhdG9zX3BlcmRpZGEgPC0NCiAgZGF0b3Nfc2VsZWN0ICU+JQ0KICBtdXRhdGUoYXJlYV9wZXJkaWRhX3AgPSAoMSAtIChhcmVhX2Nvc2VjaGFkYSAvIGFyZWFfc2VtYnJhZGEpKSAqIDEwMCkgDQoNCmRhdG9zX3BlcmRpZGEgJT4lIGhlYWQoKQ0KYGBgDQoNCi0gVGFtYmnDqW4gcG9kcsOtYW1vcyBvYnRlbmVyIGVsIHRvdGFsIGRlIMOhcmVhIHBlcmRpZGE6DQoNCiQkw4FyZWFcIHBlcmRpZGEgKGhhKSA9IMOBcmVhXCBzZW1icmFkYSAtIMOBcmVhXCBjb3NlY2hhZGEkJA0KDQpgYGB7cn0NCmRhdG9zX3BlcmRpZGFfaGEgPC0NCiAgZGF0b3NfcGVyZGlkYSAlPiUNCiAgbXV0YXRlKGFyZWFfcGVyZGlkYV9oYSA9IGFyZWFfc2VtYnJhZGEgLSBhcmVhX2Nvc2VjaGFkYSkgDQoNCmRhdG9zX3BlcmRpZGFfaGEgJT4lIGhlYWQoKQ0KYGBgDQoNCiMjIyBQcm9jZXNvIGp1bnRvcw0KDQotIFBvZGVtb3MgdXNhciBsYXMgZnVuY2lvbmVzIGBjb3VudCgpYCwgYHNlbGVjdCgpYCxgZmlsdGVyKClgLCAgYG11dGF0ZSgpYCwgYGFycmFuZ2UoKWAganVudGFzIGEgdHJhdsOpcyBkZSBsYSB0dWJlcsOtYSBgJT4lYA0KDQpgYGB7cn0NCmV2YV9hbnRpb3F1aWEgPC0NCiAgZXZhX2RlcHVyYWRhICU+JQ0KICBzZWxlY3QoDQogICAgZGVwYXJ0YW1lbnRvLA0KICAgIG11bmljaXBpbywNCiAgICBjdWx0aXZvLA0KICAgIGFubywNCiAgICBwZXJpb2RvLA0KICAgIGFyZWFfc2VtYnJhZGEsDQogICAgYXJlYV9jb3NlY2hhZGEsDQogICAgcmVuZGltaWVudG8NCiAgKSAlPiUNCiAgZmlsdGVyKGRlcGFydGFtZW50byA9PSAiQW50aW9xdWlhIikgJT4lIA0KICBtdXRhdGUoYXJlYV9wZXJkaWRhX3AgPSAoMSAtIChhcmVhX2Nvc2VjaGFkYSAvIGFyZWFfc2VtYnJhZGEpKSAqIDEwMCwNCiAgICAgICAgIGFyZWFfcGVyZGlkYV9oYSA9IGFyZWFfc2VtYnJhZGEgLSBhcmVhX2Nvc2VjaGFkYSkgDQoNCmV2YV9hbnRpb3F1aWEgJT4lIGhlYWQoKQ0KYGBgDQoNCiMjIyBzdW1tYXJpc2UoKQ0KDQotIFBvZHLDrWFtb3Mgc3VtYXIgZWwgdG90YWwgZGUgw6FyZWEgc2VtYnJhZGEgZW4gZWwgYcOxbyAyMDIxIHBhcmEgQW50aW9xdWlhOg0KDQpgYGB7cn0NCmV2YV9hbnRpb3F1aWEgJT4lIA0KICBzdW1tYXJpc2Uoc3VtKGFyZWFfc2VtYnJhZGEpKQ0KYGBgDQoNCiMjIyBSZXN1bWlyIGRhdG9zDQoNCi0gYGdyb3VwX2J5KClgIGAlPiVgIGBzdW1tYXJpc2UoKWAgYCU+JWAgYHVuZ3JvdXAoKWANCi0gwr9DdcOhbCBmdWUgZWwgY3VsdGl2byBjb24gbGEgbWF5b3Igw6FyZWEgcGVyZGlkYSBlbiBBbnRpb3F1aWEgcGFyYSBlbCBhw7FvIDIwMjE/DQoNCmBgYHtyfQ0KZXZhX2FudGlvcXVpYSAlPiUgDQogIGdyb3VwX2J5KGN1bHRpdm8pICU+JSANCiAgc3VtbWFyaXNlKHRvdGFsX2FyZWFfaGEgPSBzdW0oYXJlYV9wZXJkaWRhX2hhKSkgJT4lIA0KICB1bmdyb3VwKCkgJT4lICMgb3BjaW9uYWwgcGVybyBlcyB1bmEgYnVlbmEgcHLDoWN0aWNhDQogIGFycmFuZ2UoZGVzYyh0b3RhbF9hcmVhX2hhKSkNCmBgYA0KDQotIMK/Q3XDoW50byBmdWUgZWwgdG90YWwgZGUgw6FyZWEgcGVyZGlkYSBlbiBBbnRpb3F1aWEgcGFyYSBlbCBhw7FvIDIwMjE/DQoNCmBgYHtyfQ0KZXZhX2FudGlvcXVpYSAlPiUgDQogIHN1bW1hcmlzZSh0b3RhbF9hcmVhX2hhID0gc3VtKGFyZWFfcGVyZGlkYV9oYSkpDQpgYGANCg0KLSBTaSBhc3VtaW1vcyBxdWUgdW4gY2FtcG8gZGUgZsO6dGJvbCB0aWVuZSBhcHJveGltYWRhbWVudGUgJDAuNjQtMC44MiQgaGVjdMOhcmVhcywgwr9jdcOhbnRvcyAiY2FtcG9zIGRlIGbDunRib2wiIHNlIHBlcmRpZXJvbiBlbiBBbnRpb3F1aWEgZW4gZWwgYcOxbyAyMDIxPw0KDQpgYGB7cn0NCjQ2NTczLjE0IC8gMC42NA0KDQo0NjU3My4xNCAvIDAuODINCmBgYA0KDQotIFNhYmllbmRvIHF1ZSBbQW50aW9xdWlhIHRpZW5lIGFwcm94aW1hZGFtZW50ZSAgJDYzNjEyXCBrbV4yJF0oaHR0cHM6Ly9lcy53aWtpcGVkaWEub3JnL3dpa2kvQW5leG86RGVwYXJ0YW1lbnRvc19kZV9Db2xvbWJpYV9wb3Jfc3VwZXJmaWNpZSksIGVzIGRlY2lyLCAkNjM2MTIwMFwgaGEkLiBTaSB0b21hbW9zIGxhIGNpZnJhIGRlIDEnMjkyLjU0OSBoZWN0w6FyZWFzIGNvbiBwb3RlbmNpYWwgZW4gQW50aW9xdWlhIMK/UXXDqSBwb3JjZW50YWplIGRlIGVzdGEgw6FyZWEgcG90ZW5jaWFsIGVuIEFudGlvcXVpYSBzZSBwZXJkacOzIGVuIGVsIGHDsW8gMjAyMT8NCi0gW0VsIDY1LDggJSBkZWwgc3VlbG8gYXB0byBkZWwgcGHDrXMgbm8gc2UgYXByb3ZlY2hhXShodHRwczovL3VwcmEuZ292LmNvL3NhbGEtZGUtcHJlbnNhL25vdGljaWFzLy0vYXNzZXRfcHVibGlzaGVyL0dFS3lVdXhIWVNYWi9jb250ZW50L2VsLTY1LTgtZGVsLXN1ZWxvLWFwdG8tZGVsLXBhaXMtbm8tc2UtYXByb3ZlY2hhKQ0KDQpgYGB7cn0NCig0NjU3My4xNCAvIDEyOTI1NDkpICogMTAwDQpgYGANCg0KLSDCv0VsIGPDoWxjdWxvIGFudGVyaW9yIGVzIGNvcnJlY3RvPyDCv0VzdGFtb3Mgc3ViZXN0aW1hbmRvIGVsIMOhcmVhIHBlcmRpZGEgZW4gQW50aW9xdWlhPw0KDQpgYGB7cn0NCiMgw4FyZWEgcGVyZGlkYTogNDY1NzMuMTQNCiMgw4FyZWEgdG90YWwgc2VtYnJhZGE6IDQzNTM3My4zDQoNCig0NjU3My4xNCAvIDQzNTM3My4zKSAqIDEwMA0KYGBgDQoNCiMgTcOpdHJpY2FzIGVzdGFkw61zdGljYXMNCg0KIyMgRnVuY2lvbmVzIGVuIFINCg0KfCBGdW5jacOzbiB8IERlc2NyaXBjacOzbiB8IFRpcG8gZGUgdmFyaWFibGUgfA0KfCA6LS0tIHwgOi0tLS0tLS0tLS0tLS0tLS06IHwgOi0tLS06IHwNCnwgYG1lYW4oKWAgfCBDYWxjdWxhciBwcm9tZWRpbyB8IGN1YW50aXRhdGl2YSB8DQp8IGB3ZWlnaHRlZC5tZWFuKClgIHwgQ2FsY3VsYXIgcHJvbWVkaW8gcG9uZGVyYWRvIHwgQ3VuYXRpdGF0aXZhIHwNCnwgYG1lZGlhbigpYCB8IENhbGN1bGFyIG1lZGlhbmEgfCBDdWFudGl0YXRpdmEgfA0KfCBgc2QoKWAgfCBDYWxjdWxhciBkZXN2aWFjacOzbiBlc3TDoW5kYXIgfCBjdWFudGl0YXRpdmEgfA0KfCBgdmFyKClgIHwgQ2FsY3VsYXIgbGEgdmFyaWFuemEgIHwgQ3VhbnRpdGF0aXZhIHwNCnwgYHJhbmdlKClgIHwgQ2FsY3VsYXIgZWwgcmFuZ28gfCBDdWFudGl0YXRpdmEgfA0KfCBgSVFSKClgIHwgQ2FsY3VsYXIgcmFuZ28gaW50ZXJjdWFydMOtbGljbyB8IEN1YW50aXRhdGl2YSB8DQp8IGBxdWFudGlsZSgpYCB8IENhbGN1bGFyIGN1YXJ0aWxlcywgZGVjaWxlcyB5IHBlcmNlbnRpbGVzIHwgQ3VhbnRpdGF0aXZhIHwNCnwgYG1pbigpYCB8ICBWYWxvciBtw61uaW1vIHwgQ3VhbnRpdGF0aXZhIHwNCnwgYG1heCgpYCB8IFZhbG9yIG3DoXhpbW8gIHwgQ3VhbnRpdGF0aXZhIHwNCnwgKiBgbW9kYSgpYCAgfCBNb2RhIHwgQ3VhbnRpdGF0aXZhIHkgY3VhbGl0YXRpdmEgfA0KDQojIyBWYWxvcmVzIGF1c2VudGVzDQoNCmBgYHtyfQ0KZWplbXBsbyA8LSBjKDEsIDIsIDMsIE5BKQ0KZWplbXBsbyAlPiUgbWVhbihuYS5ybSA9IFRSVUUpICMgbmEucm0gPSBUUlVFIHBhcmEgbm8gdGVuZXIgZW4gY3VlbnRhIGxvcyBOQQ0KYGBgDQoNCiMjIFRlbmRlbmNpYSBjZW50cmFsDQoNCiMjIyBNZWRpYQ0KDQotIMK/Q3XDoWwgZXMgZWwgcHJvbWVkaW8gZGUgcmVuZGltaWVudG8gcGFyYSBlbCBjdWx0aXZvIGRlIG1hbmdvPw0KDQpgYGB7cn0NCmV2YV9kZXB1cmFkYSAlPiUgDQogIGZpbHRlcihjdWx0aXZvID09ICJNYW5nbyIpICU+JSANCiAgc3VtbWFyaXNlKHByb21lZGlvX3J0byA9IG1lYW4ocmVuZGltaWVudG8pKQ0KYGBgDQoNCi0gUG9kZW1vcyBjb25zdHJ1aXIgdW5hIHRhYmxhIGNvbiBpbmZvcm1hY2nDs24gcXVlIHJlc3BvbmRhIGEgbGFzIHNpZ3VpZW50ZXMgcHJlZ3VudGFzIHNvYnJlIGVsIGN1bHRpdm8gZGUgbWFuZ286DQogIC0gwr9FbiBjdcOhbnRvcyBkZXBhcnRhbWVudG9zIGRlIENvbG9tYmlhIHNlIGN1bHRpdsOzIG1hbmdvIGVuIGVsIDIwMjE/DQogIC0gwr9DdcOhbCBlcyBlbCBkZXBhcnRhbWVudG8gY29uIGVsIG1heW9yIHJlbmRpbWllbnRvIHByb21lZGlvPw0KICAtIMK/Q3XDoWwgZXMgZWwgdG90YWwgZGUgw6FyZWEgc2VtYnJhZGEgZW4gY2FkYSBkZXBhcnRhbWVudG8/DQogIC0gwr9Db24gY3XDoW50b3MgZGF0b3Mgc2UgY2FsY3Vsw7MgZWwgcHJvbWVkaW8gcGFyYSBjYWRhIGRlcGFydGFtZW50bz8NCg0KYGBge3J9DQpldmFfZGVwdXJhZGEgJT4lDQogIGZpbHRlcihjdWx0aXZvID09ICJNYW5nbyIpICU+JQ0KICBncm91cF9ieShkZXBhcnRhbWVudG8pICU+JQ0KICBzdW1tYXJpc2UoDQogICAgcHJvbWVkaW9fcnRvID0gbWVhbihyZW5kaW1pZW50byksDQogICAgYXJlYV9jb3NlY2hhZGEgPSBzdW0oYXJlYV9jb3NlY2hhZGEpLA0KICAgIE4gPSBuKCkNCiAgKSAlPiUNCiAgdW5ncm91cCgpICU+JQ0KICBhcnJhbmdlKGRlc2MocHJvbWVkaW9fcnRvKSkNCmBgYA0KDQojIyMgTWVkaWEgcG9uZGVyYWRhDQoNCi0gwr9DdcOhbCBlcyBlbCBwcm9tZWRpbyBwb25kZXJhZG8gcG9yIMOhcmVhIGNvc2VjaGFkYSBwYXJhIGVsIGN1bHRpdm8gZGUgbWFuZ28/DQoNCmBgYHtyfQ0KZXZhX2RlcHVyYWRhICU+JQ0KICBmaWx0ZXIoY3VsdGl2byA9PSAiTWFuZ28iKSAlPiUgDQogIHN1bW1hcmlzZShwcm9tX3J0b19wb25kID0gd2VpZ2h0ZWQubWVhbih4ID0gcmVuZGltaWVudG8sIHcgPSBhcmVhX2Nvc2VjaGFkYSkpDQpgYGANCg0KLSBQb2RlbW9zIGFncmVnYXIgZXN0YSBtw6l0cmljYSBhIGNhZGEgZGVwYXJ0YW1lbnRvOg0KDQpgYGB7cn0NCmV2YV9kZXB1cmFkYSAlPiUNCiAgZmlsdGVyKGN1bHRpdm8gPT0gIk1hbmdvIikgJT4lDQogIGdyb3VwX2J5KGRlcGFydGFtZW50bykgJT4lDQogIHN1bW1hcmlzZSgNCiAgICBwcm9tZWRpb19ydG8gPSBtZWFuKHJlbmRpbWllbnRvKSwNCiAgICBwcm9tZWRpb19ydG9fcG9uZCA9IHdlaWdodGVkLm1lYW4oeCA9IHJlbmRpbWllbnRvLCB3ID0gYXJlYV9jb3NlY2hhZGEpLA0KICAgIGFyZWFfY29zZWNoYWRhID0gc3VtKGFyZWFfY29zZWNoYWRhKSwNCiAgICBOID0gbigpDQogICkgJT4lDQogIHVuZ3JvdXAoKSAlPiUNCiAgYXJyYW5nZShkZXNjKHByb21lZGlvX3J0bykpDQpgYGANCg0KIyMjIE1lZGlhbmENCg0KLSBTaWd1aWVudGUgY29uIGVsIGVqZW1wbG8gZGVsIG1hbmdvLCDCv0N1w6FsIGVzIGxhIG1lZGlhbmEgZGVsIHJlbmRpbWllbnRvIHBhcmEgY2FkYSBkZXBhcnRhbWVudG8/IMK/RGlmaWVyZSBkZWwgcHJvbWVkaW8/DQoNCmBgYHtyfQ0KZXZhX2RlcHVyYWRhICU+JSANCiAgZmlsdGVyKGN1bHRpdm8gPT0gIk1hbmdvIikgJT4lDQogIGdyb3VwX2J5KGRlcGFydGFtZW50bykgJT4lIA0KICBzdW1tYXJpc2UobWVkaWFuYV9ydG8gPSBtZWRpYW4ocmVuZGltaWVudG8pLA0KICAgICAgICAgICAgcHJvbWVkaW9fcnRvID0gbWVhbihyZW5kaW1pZW50bykpICU+JSANCiAgdW5ncm91cCgpICU+JQ0KICBhcnJhbmdlKGRlc2MobWVkaWFuYV9ydG8pKQ0KYGBgDQoNCiMjIyBNb2RhDQoNCi0gQ29tbyBsYSBmdW5jacOzbiAqbW9kYSogbm8gZXN0w6EgY29udGVuaWRhIGRlbnRybyBkZSBSLCB1c2Ftb3MgdW5hIGZ1bmNpw7NuIHBlcnNvbmFsaXphZGE6DQoNCmBgYHtyfQ0KbW9kYSA8LSBmdW5jdGlvbih4KSB7DQogIHV4ID0gdW5pcXVlKHgpDQogIHRhYiA9IHRhYnVsYXRlKG1hdGNoKHgsIHV4KSkNCiAgdXhbdGFiID09IG1heCh0YWIpXQ0KfQ0KYGBgDQoNCi0gwr9DdcOhbCBlcyBsYSBtb2RhIGRlIGRlcGFydGFtZW50bz8NCg0KYGBge3J9DQpldmFfZGVwdXJhZGEgJT4lIA0KICBzdW1tYXJpc2UobW9kYV9kZXB0byA9IG1vZGEoZGVwYXJ0YW1lbnRvKSkNCmBgYA0KDQotIEVsIHJlc3VsdGFkbyBhbnRlcmlvciBlcyBlcXVpdmFsZW50ZSBhIG9idGVuZXIgbGFzIGZyZWN1ZW5jaWFzIGFic29sdXRhcyB5IG9yZGVuYXIgZGUgbWFuZXJhIGRlc2NlbmRlbnRlOg0KDQpgYGB7cn0NCmV2YV9kZXB1cmFkYSAlPiUgDQogIGNvdW50KGRlcGFydGFtZW50bywgc29ydCA9IFRSVUUpDQpgYGANCg0KIyMgTWVkaWRhcyBkZSBkaXNwZXJzacOzbg0KDQojIyMgRC4gRXN0w6FuZGFyDQoNCi0gwr9DdcOhbCBlcyBsYSBkZXN2aWFjacOzbiBlc3TDoW5kYXIgZGVsIHJlbmRpbWllbnRvIGVuIGVsIGN1bHRpdm8gZGUgbWFuZ28/DQoNCmBgYHtyfQ0KZXZhX2RlcHVyYWRhICU+JSANCiAgZmlsdGVyKGN1bHRpdm8gPT0gIk1hbmdvIikgJT4lIA0KICBzdW1tYXJpc2UoZGVzdl9lc3RfcnRvID0gc2QocmVuZGltaWVudG8pKQ0KYGBgDQoNCg0KLSBQYXJhIGVsIGN1bHRpdm8gZGUgbWFuZ28sIMK/Q3XDoWwgZGUgbG9zIGRlcGFydGFtZW50b3MgcHJlc2VudMOzIG1lbm9yIGRpc3BlcnNpw7NuIGVuIGVsIHJlbmRpbWllbnRvPw0KDQpgYGB7cn0NCmV2YV9kZXB1cmFkYSAlPiUgDQogIGZpbHRlcihjdWx0aXZvID09ICJNYW5nbyIpICU+JSANCiAgZ3JvdXBfYnkoZGVwYXJ0YW1lbnRvKSAlPiUgDQogIHN1bW1hcmlzZShwcm9tZWRpb19ydG8gPSBtZWFuKHJlbmRpbWllbnRvKSwNCiAgICAgICAgICAgIGRlc3ZfZXN0X3J0byA9IHNkKHJlbmRpbWllbnRvKSwNCiAgICAgICAgICAgIGN2ID0gZGVzdl9lc3RfcnRvIC8gcHJvbWVkaW9fcnRvLA0KICAgICAgICAgICAgTiA9IG4oKSkgJT4lIA0KICB1bmdyb3VwKCkgJT4lIA0KICBhcnJhbmdlKGRlc2MocHJvbWVkaW9fcnRvKSkNCmBgYA0KDQojIyMgVmFyaWFuemENCg0KLSDCv0N1w6FsIGVzIGxhIHZhcmlhbnphIGRlbCByZW5kaW1pZW50byBwYXJhIGVsIGN1bHRpdm8gZGUgbWFuZ28/DQoNCmBgYHtyfQ0KZXZhX2RlcHVyYWRhICU+JSANCiAgZmlsdGVyKGN1bHRpdm8gPT0gIk1hbmdvIikgJT4lDQogIHN1bW1hcmlzZSh2YXJpYW56YV9ydG8gPSB2YXIocmVuZGltaWVudG8pKQ0KYGBgDQoNCi0gU2kgb2J0ZW5lbW9zIGxhIHJhw616IGN1YWRyYWRhIGRlIGxhIHZhcmlhbnphIG9idGVuZW1vcyBsYSBkZXN2aWFjacOzbiBlc3TDoW5kYXI6DQoNCmBgYHtyfQ0KMzAuMDgzMzMgJT4lIA0KICBzcXJ0KCkNCmBgYA0KDQojIyMgUmFuZ28NCg0KLSDCv0N1w6FsIGVzIGVsIHJhbmdvIGRlIHJlbmRpbWllbnRvIHBhcmEgZWwgY3VsdGl2byBkZSBtYW5nbz8gKirCoUNFUk8gZXMgZWwgdmFsb3IgbcOtbmltbyEqKiDwn6SUDQoNCmBgYHtyfQ0KZXZhX2RlcHVyYWRhICU+JSANCiAgZmlsdGVyKGN1bHRpdm8gPT0gIk1hbmdvIikgJT4lIA0KICBmaWx0ZXIocmVuZGltaWVudG8gPiAwKSAlPiUgDQogIHN1bW1hcmlzZShyYW5nbyA9IHJhbmdlKHJlbmRpbWllbnRvKSkNCmBgYA0KDQotIFBvZGVtb3Mgb2J0ZW5lciBlbCB2YWxvciBtw61uaW1vIHkgbcOheGltbyBlbiBsYSBtaXNtYSB0YWJsYToNCg0KYGBge3J9DQpldmFfZGVwdXJhZGEgJT4lIA0KICBmaWx0ZXIoY3VsdGl2byA9PSAiTWFuZ28iKSAlPiUgDQogIHN1bW1hcmlzZShtaW5pbW9fcnRvID0gbWluKHJlbmRpbWllbnRvKSwNCiAgICAgICAgICAgIG1heGltb19ydG8gPSBtYXgocmVuZGltaWVudG8pKQ0KYGBgDQoNCiMjIyBSYW5nbyBpbnRlcmN1YXJ0w61saWNvDQoNCi0gwr9DdcOhbCBlcyBlbCByYW5nbyBpbnRlcmN1YXJ0w61saWNvIHBhcmEgZWwgY3VsdGl2byBkZSBtYW5nbz8NCg0KYGBge3J9DQpldmFfZGVwdXJhZGEgJT4lIA0KICBmaWx0ZXIoY3VsdGl2byA9PSAiTWFuZ28iKSAlPiUgDQogIHN1bW1hcmlzZShyYW5nb19pYyA9IElRUihyZW5kaW1pZW50bykpDQpgYGANCg0KDQoNCg0KIyMgTWVkaWRhcyBkZSBwb3NpY2nDs24NCg0KIyMjIEN1YXJ0aWxlcw0KDQotIMK/Q3XDoWwgZXMgZWwgdmFsb3IgZGUgbG9zIGN1YXJ0aWxlcyBwYXJhIGVsIGN1bHRpdm8gZGUgbWFuZ28/DQoNCmBgYHtyfQ0KZXZhX2RlcHVyYWRhICU+JSANCiAgZmlsdGVyKGN1bHRpdm8gPT0gIk1hbmdvIikgJT4lIA0KICBzdW1tYXJpc2UoY3VhcnRpbGVzID0gcXVhbnRpbGUocmVuZGltaWVudG8sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcm9icyA9IHNlcShmcm9tID0gMCwgdG8gPSAxLCBieSA9IDAuMjUpKSkNCmBgYA0KDQotIFBvZGVtb3MgbGxlZ2FyIGFsIG1pc21vIHJlc3VsdGFkbyBkZSBsYSBzaWd1aWVudGUgbWFuZXJhOg0KDQpgYGB7cn0NCmV2YV9kZXB1cmFkYSAlPiUgDQogIGZpbHRlcihjdWx0aXZvID09ICJNYW5nbyIpICU+JSANCiAgcHVsbChyZW5kaW1pZW50bykgJT4lIA0KICBxdWFudGlsZShwcm9icyA9IHNlcShmcm9tID0gMCwgdG8gPSAxLCBieSA9IDAuMjUpKQ0KYGBgDQoNCg0KIyMjIERlY2lsZXMNCg0KLSDCv0N1w6FsIGVzIGVsIHZhbG9yIGRlIGxvcyBkZWNpbGVzIHBhcmEgZWwgY3VsdGl2byBkZSBtYW5nbz8NCg0KYGBge3J9DQpldmFfZGVwdXJhZGEgJT4lIA0KICBmaWx0ZXIoY3VsdGl2byA9PSAiTWFuZ28iKSAlPiUgDQogIHB1bGwocmVuZGltaWVudG8pICU+JSANCiAgcXVhbnRpbGUocHJvYnMgPSBzZXEoZnJvbSA9IDAsIHRvID0gMSwgYnkgPSAwLjEpKQ0KYGBgDQoNCiMjIyBQZXJjZW50aWxlcw0KDQotIMK/Q3XDoWwgZXMgZWwgdmFsb3IgZGUgbG9zIHBlcmNlbnRpbGVzIHBhcmEgZWwgY3VsdGl2byBkZSBtYW5nbz8NCg0KYGBge3J9DQpldmFfZGVwdXJhZGEgJT4lIA0KICBmaWx0ZXIoY3VsdGl2byA9PSAiTWFuZ28iKSAlPiUgDQogIHB1bGwocmVuZGltaWVudG8pICU+JSANCiAgcXVhbnRpbGUocHJvYnMgPSBzZXEoZnJvbSA9IDAsIHRvID0gMSwgYnkgPSAwLjAxKSkNCmBgYA0KDQotIFBvZGVtb3Mgb2J0ZW5lciBlbCB2YWxvciBkZSB1biBwZXJjZW50aWwgZXNwZWPDrWZpY28sIHBvciBlamVtcGxvLCBlbCBwZXJjZW50aWwgOTU6DQoNCmBgYHtyfQ0KZXZhX2RlcHVyYWRhICU+JSANCiAgZmlsdGVyKGN1bHRpdm8gPT0gIk1hbmdvIikgJT4lIA0KICBwdWxsKHJlbmRpbWllbnRvKSAlPiUgDQogIHF1YW50aWxlKHByb2JzID0gMC4xMikNCmBgYA0KDQoNCiMjIE1lZGlkYXMgZGUgZGlzdHJpYnVjacOzbg0KDQojIyMgQXNpbWV0csOtYQ0KDQotIMK/RXhoaWJlIHNpbWV0csOtYSBsYSB2YXJpYWJsZSByZW5kaW1pZW50byBwYXJhIGVsIGN1bHRpdm8gZGUgbWFuZ28/DQoNCmBgYHtyfQ0KZXZhX2RlcHVyYWRhICU+JSANCiAgZmlsdGVyKGN1bHRpdm8gPT0gIk1hbmdvIikgJT4lIA0KICBncm91cF9ieShkZXBhcnRhbWVudG8pICU+JSANCiAgc3VtbWFyaXNlKGNvZWZfYXNpbWV0cmlhID0gc2tld25lc3MocmVuZGltaWVudG8pKQ0KYGBgDQoNCiMjIyBDdXJ0b3Npcw0KDQotIMK/Q3XDoWwgZXMgZWwgY29lZmljaWVudGUgZGUgY3VydG9zaXMgZGUgbGEgdmFyaWFibGUgcmVuZGltaWVudG8gcGFyYSBlbCBjdWx0aXZvIGRlIG1hbmdvPw0KDQpgYGB7cn0NCmV2YV9kZXB1cmFkYSAlPiUgDQogIGZpbHRlcihjdWx0aXZvID09ICJNYW5nbyIpICU+JSANCiAgZ3JvdXBfYnkoZGVwYXJ0YW1lbnRvKSAlPiUgDQogIHN1bW1hcmlzZShjb2VmX2FzaW1ldHJpYSA9IGt1cnRvc2lzKHJlbmRpbWllbnRvKSkNCmBgYA0KDQojIyBUYWJsYSBkZXNjcmlwdGl2YSAqbWFuZ28qDQoNCi0gUG9kZW1vcyBjb25zdHJ1aXIgdW4gcmVzdW1lbiBkZXNjcmlwdGl2byBlbiBmb3JtYSBkZSB0YWJsYSBjb24gdG9kYXMgbGFzIG3DqXRyaWNhcyBxdWUgY29uc2lkZXJlbW9zIHJlbGV2YW50ZXMuIEVuIGVzdGUgY2FzbyBlcyB1biBlamVtcGxvIHBhcmEgZSBjdWx0aXZvIGRlIG1hbmdvIHBvciBjYWRhIGRlcGFydGFtZW50bzoNCg0KYGBge3J9DQp0YWJsYV9yZXN1bWVuX21hbmdvIDwtDQogIGV2YV9kZXB1cmFkYSAlPiUNCiAgZmlsdGVyKGN1bHRpdm8gPT0gIk1hbmdvIikgJT4lDQogIGdyb3VwX2J5KGRlcGFydGFtZW50bykgJT4lDQogIHN1bW1hcmlzZSgNCiAgICBwcm9tZWRpb19ydG8gPSBtZWFuKHJlbmRpbWllbnRvKSwNCiAgICBwcm9tZWRpb19ydG9fcG9uZCA9IHdlaWdodGVkLm1lYW4ocmVuZGltaWVudG8sIGFyZWFfY29zZWNoYWRhKSwNCiAgICBtZWRpYW5hX3J0byA9IG1lZGlhbihyZW5kaW1pZW50byksDQogICAgbWluX3J0byA9IG1pbihyZW5kaW1pZW50byksDQogICAgbWF4X3J0byA9IG1heChyZW5kaW1pZW50byksDQogICAgZGVzdl9lc3RfcnRvID0gc2QocmVuZGltaWVudG8pLA0KICAgIHBlcmNlbnRpbDVfcnRvID0gcXVhbnRpbGUocmVuZGltaWVudG8sIHByb2JzID0gMC4wNSksDQogICAgcGVyY2VudGlsOTVfcnRvID0gcXVhbnRpbGUocmVuZGltaWVudG8sIHByb2JzID0gMC45NSksDQogICAgYXJlYV9zZW1icmFkYSA9IHN1bShhcmVhX3NlbWJyYWRhKSwNCiAgICBhcmVhX2Nvc2VjaGFkYSA9IHN1bShhcmVhX2Nvc2VjaGFkYSksDQogICAgTiA9IG4oKQ0KICApICU+JQ0KICB1bmdyb3VwKCkgJT4lDQogIGFycmFuZ2UoZGVzYyhwcm9tZWRpb19ydG8pKQ0KDQp0YWJsYV9yZXN1bWVuX21hbmdvDQpgYGANCg0KLSBQb2RlbW9zIGFncmVnYXIgY2FyYWN0ZXLDrXN0aWNhcyBxdWUgZmFjaWxpdGVuIGxhIHZpc3VhbGl6YWNpw7NuIGRlIGxhIHRhYmxhIGFudGVyaW9yOg0KDQpgYGB7cn0NCnRhYmxhX3Jlc3VtZW5fbWFuZ28gJT4lDQogIG11dGF0ZShhY3Jvc3Mod2hlcmUoaXMubnVtZXJpYyksIHJvdW5kLCBkaWdpdHMgPSAyKSkgJT4lDQogIGRhdGF0YWJsZSgNCiAgICByb3duYW1lcyA9IEZBTFNFLA0KICAgIGV4dGVuc2lvbnMgPSBjKCdCdXR0b25zJywgJ0ZpeGVkQ29sdW1ucycpLA0KICAgIG9wdGlvbnMgPSBsaXN0KA0KICAgICAgZG9tID0gJ0JmcnRpcCcsDQogICAgICBidXR0b25zID0gYygnZXhjZWwnKSwNCiAgICAgIHNjcm9sbFggPSBUUlVFLA0KICAgICAgZml4ZWRDb2x1bW5zID0gbGlzdChsZWZ0Q29sdW1ucyA9IDEpLA0KICAgICAgbGFuZ3VhZ2UgPSBsaXN0KHVybCA9ICcvL2Nkbi5kYXRhdGFibGVzLm5ldC9wbHVnLWlucy8xLjEwLjExL2kxOG4vU3BhbmlzaC5qc29uJykNCiAgICApDQogICkNCmBgYA0KDQo=