Meta del curso. Aprender a organizar un proyecto, importar una base real, entender qué contiene, transformarla con cuidado, calcular porcentajes y producir un gráfico que otra persona pueda leer sin conocer R.

Este curso propone una primera aproximación y parte desde los conceptos básicos. Vamos a trabajar con la base de personas de la Encuesta Permanente de Hogares (EPH), primer trimestre de 2025, publicada por el INDEC.

El análisis responderá una pregunta concreta:

¿Qué porcentaje de las personas de 18 a 65 años está económicamente activo en la muestra y cómo cambia ese porcentaje según sexo y nivel educativo agrupado?

Usaremos dos recorridos. Primero veremos cada idea con R base, las herramientas que vienen con R. Después mostraremos una alternativa con paquetes como dplyr, janitor y ggplot2. Comparar las dos sintaxis permite reconocer distintas formas de resolver una misma tarea y elegir la más clara para cada situación.

Límite. En este curso trabajamos con porcentajes sin ponderar. Por lo tanto, los resultados describen los casos observados después del filtro y no representan tasas oficiales ni estimaciones de toda la población. La EPH cubre 31 aglomerados urbanos y su análisis poblacional requiere considerar el diseño de la encuesta.

1 Antes de empezar

1.1 R y RStudio cumplen funciones diferentes

R es el lenguaje y el programa que interpreta nuestras instrucciones. RStudio es un entorno de trabajo que reúne un editor, la consola, los objetos, los archivos, la ayuda y los gráficos en una misma ventana.

En RStudio suelen verse cuatro zonas:

Zona Para qué sirve
Editor Escribir y guardar scripts o documentos R Markdown.
Consola Ejecutar instrucciones y mostrar resultados o errores.
Environment Ver los objetos creados durante la sesión.
Files, Plots, Packages, Help Navegar archivos, gráficos, paquetes y documentación.

La consola sirve para probar. El script o el R Markdown sirven para conservar y reproducir el trabajo. Si un análisis existe solamente en la consola, será difícil reconstruirlo más adelante.

1.2 Cómo ejecutar código

En un script, colocamos el cursor en una línea y usamos Ctrl + Enter en Windows o Linux, o Cmd + Enter en macOS. En un R Markdown también podemos usar el botón verde de cada bloque.

Probemos tres instrucciones sencillas:

2 + 2
#> [1] 4

edad <- 30
edad + 5
#> [1] 35

mean(c(20, 30, 40))
#> [1] 30

La flecha <- asigna un valor a un objeto. Se lee “edad recibe 30”. R distingue mayúsculas de minúsculas: edad, Edad y EDAD serían tres nombres diferentes.

Práctica 1. Creá un objeto llamado ingresos con los valores 100, 200 y 300. Después calculá su promedio.

Ver una solución
ingresos <- c(100, 200, 300)
mean(ingresos)
#> [1] 200
c() reúne valores en un vector y mean() calcula su media.

2 Pensar antes de programar

2.1 Qué es el pseudocódigo

El pseudocódigo es una lista ordenada de acciones expresadas con palabras comunes, independiente de la sintaxis de R. Su función es separar dos problemas:

  1. Qué queremos hacer.
  2. Cómo se escribe en R.

Para nuestra pregunta, el plan será:

  1. Abrir la base de personas.
  2. Revisar qué representa cada fila y cada columna.
  3. Elegir sexo, edad, educación y condición de actividad.
  4. Conservar personas de 18 a 65 años con códigos válidos.
  5. Transformar códigos en categorías comprensibles.
  6. Crear una variable que indique si la persona está activa.
  7. Contar casos y calcular porcentajes.
  8. Comparar grupos con una tabla y un gráfico.
  9. Describir los resultados sin afirmar causalidad.

El pseudocódigo también permite detectar decisiones ocultas. Por ejemplo, “conservar personas adultas” requiere indicar qué edades consideraremos y por qué.

Práctica 2. Reescribí los pasos 6 a 9 para estudiar personas ocupadas en lugar de personas activas.

Ver una solución posible
  1. Crear una variable que valga 1 cuando la persona está ocupada y 0 cuando está desocupada o inactiva.
  2. Contar casos y calcular el porcentaje de personas ocupadas.
  3. Comparar el porcentaje por sexo y educación.
  4. Graficar y describir las diferencias observadas, aclarando que son porcentajes sin ponderar.

3 Objetos, operadores y funciones

3.1 Objetos y tipos de datos

Un objeto es un nombre que usamos para guardar algo. Puede contener un número, texto, una secuencia o una tabla completa.

cantidad <- 12
curso <- "Introducción a R"
aprobado <- TRUE
edades <- c(18, 25, 40, NA)

class(cantidad)
#> [1] "numeric"
class(curso)
#> [1] "character"
class(aprobado)
#> [1] "logical"

NA significa que un valor está ausente o no disponible y se diferencia de cero. Muchas funciones necesitan que indiquemos qué hacer con esos valores:

mean(edades)
#> [1] NA
mean(edades, na.rm = TRUE)
#> [1] 27.66667

El argumento na.rm = TRUE pide omitir los NA durante el cálculo y conserva intacto el objeto original.

3.2 Operadores frecuentes

Operador Pregunta que representa Ejemplo
== ¿Es igual a? edad == 30
!= ¿Es distinto de? sexo != 9
> y < ¿Es mayor o menor? edad > 18
>= y <= ¿Incluye el límite? edad >= 18
%in% ¿Pertenece a este conjunto? estado %in% c(1, 2)
& ¿Se cumplen ambas condiciones? edad >= 18 & edad <= 65
| ¿Se cumple al menos una? estado == 1 | estado == 2
! Negación !is.na(edad)

Un error común es escribir = cuando queremos comparar. Para preguntar si dos valores son iguales usamos ==.

3.3 Cómo leer una función

Una función recibe entradas, realiza una tarea y devuelve una salida. Para leer una llamada conviene avanzar desde el nombre hacia el interior de los paréntesis. En esta expresión:

round(mean(edades, na.rm = TRUE), digits = 1)
  • round() es la función exterior y recibe el resultado de mean().
  • mean() es la función interior y se ejecuta primero.
  • edades es el primer argumento de mean() y aporta los datos.
  • na.rm = TRUE indica que la función debe omitir valores ausentes.
  • digits = 1 indica que round() debe conservar un decimal.

Una llamada puede combinar argumentos por posición y argumentos por nombre:

mean(edades, na.rm = TRUE)
#> [1] 27.66667
mean(x = edades, na.rm = TRUE)
#> [1] 27.66667

Las dos líneas producen el mismo resultado. En la primera, R reconoce edades por su posición; en la segunda, x = nombra explícitamente el argumento. Nombrar las opciones mejora la lectura y evita depender del orden.

La ayuda de una función muestra su forma general, los argumentos, el tipo de resultado y ejemplos. Podemos abrirla con ?mean o help(mean). También podemos inspeccionar los argumentos desde la consola:

args(mean)
#> function (x, ...) 
#> NULL

La expresión que aparece en la ayuda suele incluir:

  • argumentos obligatorios, como x, que debemos proporcionar;
  • valores predeterminados, que la función usa si no indicamos otra opción;
  • ..., que permite transmitir argumentos adicionales;
  • una sección Value, que explica qué devuelve la función;
  • una sección Examples, con llamadas que podemos adaptar.

Antes de ejecutar una función nueva, conviene responder cuatro preguntas:

  1. ¿Qué objeto recibe?
  2. ¿Qué argumentos modifican su comportamiento?
  3. ¿Qué tipo de objeto devuelve?
  4. ¿La función transforma el objeto original o crea un resultado nuevo?

El prefijo paquete:: también forma parte de la lectura. Por ejemplo, dplyr::filter(datos, CH06 >= 18) indica con claridad que usamos la función filter() del paquete dplyr sobre el objeto datos.

Práctica 3. Leé la expresión round(mean(c(10, 12, NA, 15), na.rm = TRUE), digits = 2) desde adentro hacia afuera. Identificá los datos, las opciones y el orden de ejecución. Después anticipá qué tipo de resultado devolverá.

Ver una solución
  1. c() reúne los cuatro valores en un vector numérico.
  2. mean() calcula el promedio y na.rm = TRUE omite el valor ausente.
  3. round() recibe ese promedio y digits = 2 lo redondea a dos decimales.
  4. El resultado es un único valor numérico: 12.33.

4 Proyecto, carpeta y rutas

4.1 Una estructura reproducible

La carpeta principal debe contener este documento y la subcarpeta con los datos:

markdown/
├── curso_introductorio_R_EPH_1T2025.Rmd
└── EPH_usu_1er_Trim_2025_txt/
    ├── usu_individual_T125.txt
    └── usu_hogar_T125.txt

La ruta de trabajo es la dirección que R usa como punto de partida. getwd() muestra esa carpeta y list.files() enumera su contenido.

getwd()
list.files()

El bloque queda sin ejecutar para mantener fuera del documento la ruta personal de la computadora que lo tejió.

La ruta relativa que usaremos es:

ruta_datos
#> [1] "EPH_usu_1er_Trim_2025_txt/usu_individual_T125.txt"

Una ruta relativa funciona dentro del proyecto aunque la carpeta completa se copie a otra computadora. En cambio, una ruta como C:/Users/Ana/Desktop/... depende de una persona y una máquina específicas.

Buena práctica. Abrí el proyecto o el archivo desde su carpeta principal. Las rutas relativas mantienen el código portable. setwd() fija una ruta personal y file.choose() detiene el tejido automático mientras espera un clic.

5 Paquetes

Los paquetes agregan funciones a R. Hay dos pasos diferentes:

  1. Instalar: se hace una vez por computadora.
  2. Cargar: se repite al iniciar una sesión que usa el paquete.
# Ejecutar una sola vez y sin el signo # inicial:
install.packages(c("dplyr", "janitor", "ggplot2", "readxl", "rmarkdown"))
library(dplyr)
library(janitor)
library(ggplot2)
library(readxl)

También podemos escribir dplyr::filter(). Los dos puntos dobles indican de qué paquete viene la función y evitan dudas cuando dos paquetes tienen funciones con el mismo nombre.

6 Importar datos

6.1 Antes de elegir una función

Para elegir una función de importación debemos conocer:

  • el formato: TXT, CSV o XLSX;
  • el separador entre columnas;
  • el símbolo decimal;
  • la codificación de caracteres;
  • si la primera fila contiene nombres de variables.

El TXT de la EPH usa punto y coma como separador y coma decimal. Por eso utilizamos read.csv2().

6.2 Importar el TXT disponible

eph <- read.csv2(
  file = ruta_datos,
  header = TRUE,
  stringsAsFactors = FALSE,
  fileEncoding = "Latin1",
  na.strings = c("", "NA"),
  check.names = FALSE
)

dim(eph)
#> [1] 45425   235

La importación conserva el TXT original y crea un objeto llamado eph en la sesión de R. En este caso hay 45.425 filas y 235 columnas.

6.3 Si el archivo fuera CSV o Excel

Estos ejemplos quedan sin ejecutar porque los archivos no están en la carpeta. La lógica se mantiene: ruta, función y objeto de destino.

# CSV separado por comas
eph_csv <- read.csv(
  "datos/eph_individual_T125.csv",
  stringsAsFactors = FALSE,
  fileEncoding = "UTF-8"
)

# Excel: requiere el paquete readxl
eph_excel <- readxl::read_excel(
  "datos/eph_individual_T125.xlsx",
  sheet = 1
)

Práctica 4. Sin ejecutar código, señalá qué parte habría que cambiar para importar un archivo llamado personas.csv guardado en la subcarpeta datos.

Ver una solución La ruta sería "datos/personas.csv". También habría que comprobar el separador: usaríamos read.csv() si es coma y read.csv2() si es punto y coma.

7 Explorar antes de transformar

Cada fila de la base individual representa una persona. Las columnas son variables. Antes de modificar algo, revisamos dimensiones, nombres y tipos.

nrow(eph)
#> [1] 45425
ncol(eph)
#> [1] 235
names(eph)[1:15]
#>  [1] "CODUSU"     "ANO4"       "TRIMESTRE"  "NRO_HOGAR"  "COMPONENTE"
#>  [6] "H15"        "REGION"     "MAS_500"    "AGLOMERADO" "PONDERA"   
#> [11] "CH03"       "CH04"       "CH05"       "CH06"       "CH07"

Las variables centrales del ejercicio son:

Variable Significado para este curso
CH04 Sexo registrado: 1 varón, 2 mujer.
CH06 Edad en años cumplidos.
NIVEL_ED Nivel educativo codificado.
ESTADO 1 ocupado, 2 desocupado, 3 inactivo; otros códigos quedan fuera del ejercicio.
PONDERA Factor de expansión, excluido de este ejercicio introductorio.
variables_interes <- c("CH04", "CH06", "NIVEL_ED", "ESTADO", "PONDERA")

head(eph[variables_interes])
str(eph[variables_interes])
#> 'data.frame':    45425 obs. of  5 variables:
#>  $ CH04    : int  2 2 1 1 2 2 1 2 1 2 ...
#>  $ CH06    : int  25 7 4 -1 29 13 5 39 41 2 ...
#>  $ NIVEL_ED: int  4 1 7 7 5 1 7 6 3 7 ...
#>  $ ESTADO  : int  1 4 4 4 1 3 4 1 1 4 ...
#>  $ PONDERA : int  927 927 927 927 1294 1294 1294 727 727 727 ...
summary(eph[variables_interes])
#>       CH04           CH06           NIVEL_ED         ESTADO     
#>  Min.   :1.00   Min.   : -1.00   Min.   :1.000   Min.   :0.000  
#>  1st Qu.:1.00   1st Qu.: 18.00   1st Qu.:3.000   1st Qu.:1.000  
#>  Median :2.00   Median : 35.00   Median :4.000   Median :3.000  
#>  Mean   :1.52   Mean   : 36.79   Mean   :3.757   Mean   :2.191  
#>  3rd Qu.:2.00   3rd Qu.: 54.00   3rd Qu.:5.000   3rd Qu.:3.000  
#>  Max.   :2.00   Max.   :103.00   Max.   :7.000   Max.   :4.000  
#>     PONDERA      
#>  Min.   :  21.0  
#>  1st Qu.: 181.0  
#>  Median : 319.0  
#>  Mean   : 657.1  
#>  3rd Qu.: 684.0  
#>  Max.   :8257.0

Una alternativa compacta es glimpse():

dplyr::glimpse(eph[variables_interes], width = 72)
#> Rows: 45,425
#> Columns: 5
#> $ CH04     <int> 2, 2, 1, 1, 2, 2, 1, 2, 1, 2, 2, 1, 1, 1, 1, 2, 2, 1,…
#> $ CH06     <int> 25, 7, 4, -1, 29, 13, 5, 39, 41, 2, 51, 28, 24, 19, 3…
#> $ NIVEL_ED <int> 4, 1, 7, 7, 5, 1, 7, 6, 3, 7, 3, 3, 5, 5, 4, 4, 7, 1,…
#> $ ESTADO   <int> 1, 4, 4, 4, 1, 3, 4, 1, 1, 4, 1, 1, 1, 3, 1, 3, 4, 4,…
#> $ PONDERA  <int> 927, 927, 927, 927, 1294, 1294, 1294, 727, 727, 727, …

Estas funciones responden preguntas diferentes:

  • dim(), nrow() y ncol(): ¿qué tamaño tiene la base?
  • names(): ¿cómo se llaman las variables?
  • head(): ¿cómo se ven algunos registros?
  • str() y glimpse(): ¿qué tipo tiene cada variable?
  • summary(): ¿qué rangos y distribuciones básicas aparecen?

El diseño de registros del INDEC permite interpretar el significado de cada código con respaldo documental.

8 Seleccionar variables y filtrar casos

Seleccionar columnas y filtrar filas son tareas distintas. Primero conservaremos las cuatro variables necesarias.

8.1 R base

columnas <- c("CH04", "CH06", "NIVEL_ED", "ESTADO")
datos <- eph[, columnas]

ncol(datos)
#> [1] 4

En eph[filas, columnas], lo que está antes de la coma corresponde a filas y lo que está después corresponde a columnas.

Ahora construimos una condición que sea verdadera solo para los casos válidos del ejercicio.

condicion <- !is.na(datos$CH06) &
  datos$CH06 >= 18 &
  datos$CH06 <= 65 &
  datos$CH04 %in% c(1, 2) &
  datos$NIVEL_ED %in% 1:7 &
  datos$ESTADO %in% c(1, 2, 3)

datos <- datos[condicion, , drop = FALSE]

nrow(datos)
#> [1] 28719
range(datos$CH06)
#> [1] 18 65

8.2 Alternativa con dplyr

datos_alt <- dplyr::select(eph, CH04, CH06, NIVEL_ED, ESTADO)

datos_alt <- dplyr::filter(
  datos_alt,
  !is.na(CH06),
  CH06 >= 18,
  CH06 <= 65,
  CH04 %in% c(1, 2),
  NIVEL_ED %in% 1:7,
  ESTADO %in% c(1, 2, 3)
)

nrow(datos_alt)
#> [1] 28719

Comprobamos que ambos caminos seleccionaron los mismos registros:

stopifnot(identical(
  lapply(datos, unname),
  lapply(as.data.frame(datos_alt), unname)
))

Si stopifnot() no muestra nada, la comprobación fue exitosa. Comparamos el contenido de las columnas porque R base conserva los números de fila originales y dplyr los reinicia. Esa diferencia de etiquetas no cambia los datos. Si la condición fuera falsa, R detendría la ejecución.

Práctica 5. Creá datos_25_60 con personas de 25 a 60 años, sin modificar datos.

Ver soluciones en R base y dplyr
datos_25_60 <- datos[
  datos$CH06 >= 25 & datos$CH06 <= 60,
  ,
  drop = FALSE
]

datos_25_60_alt <- dplyr::filter(datos, CH06 >= 25, CH06 <= 60)

nrow(datos_25_60)
#> [1] 21354
stopifnot(nrow(datos_25_60) == nrow(datos_25_60_alt))

9 Recodificar y crear variables

Recodificar significa construir una representación más útil a partir de códigos existentes. Conservaremos las columnas originales para poder revisar nuestras decisiones.

9.1 Sexo

datos$sexo <- factor(
  datos$CH04,
  levels = c(1, 2),
  labels = c("Varones", "Mujeres")
)

9.2 Educación

Para este ejercicio agrupamos los códigos 4, 5 y 6 como “Secundaria completa o más”. Los códigos 1, 2, 3 y 7 forman “Hasta secundaria incompleta”. Es una agrupación didáctica, no una nueva categoría oficial del INDEC.

datos$educacion <- ifelse(
  datos$NIVEL_ED %in% c(4, 5, 6),
  "Secundaria completa o más",
  "Hasta secundaria incompleta"
)

niveles_educacion <- c(
  "Hasta secundaria incompleta",
  "Secundaria completa o más"
)

datos$educacion <- factor(
  datos$educacion,
  levels = niveles_educacion
)

9.3 Actividad

Una persona económicamente activa puede estar ocupada o desocupada. Por eso activo vale 1 cuando ESTADO es 1 o 2, y 0 cuando es 3.

datos$activo <- ifelse(datos$ESTADO %in% c(1, 2), 1, 0)

table(datos$sexo, useNA = "ifany")
#> 
#> Varones Mujeres 
#>   13828   14891
table(datos$educacion, useNA = "ifany")
#> 
#> Hasta secundaria incompleta   Secundaria completa o más 
#>                        8964                       19755
table(datos$activo, useNA = "ifany")
#> 
#>     0     1 
#>  8047 20672

9.4 Alternativa con dplyr

datos_alt <- dplyr::mutate(
  datos_alt,
  sexo = factor(
    CH04,
    levels = c(1, 2),
    labels = c("Varones", "Mujeres")
  ),
  educacion = dplyr::if_else(
    NIVEL_ED %in% c(4, 5, 6),
    "Secundaria completa o más",
    "Hasta secundaria incompleta"
  ),
  educacion = factor(educacion, levels = niveles_educacion),
  activo = dplyr::if_else(ESTADO %in% c(1, 2), 1, 0)
)

stopifnot(
  identical(datos$sexo, datos_alt$sexo),
  identical(datos$educacion, datos_alt$educacion),
  identical(datos$activo, datos_alt$activo)
)

Práctica 6. Creá una variable ocupado que valga 1 cuando ESTADO == 1 y 0 cuando ESTADO sea 2 o 3. ¿Por qué no es igual a activo?

Ver una solución
datos$ocupado <- ifelse(datos$ESTADO == 1, 1, 0)
table(datos$ocupado)
#> 
#>     0     1 
#>  9391 19328
activo incluye personas ocupadas y desocupadas; ocupado incluye solamente a quienes tienen código 1.

10 Frecuencias y porcentajes

10.1 Contar casos

frecuencias_sexo <- table(datos$sexo)
frecuencias_sexo
#> 
#> Varones Mujeres 
#>   13828   14891
janitor::tabyl(datos, sexo)

Una frecuencia absoluta es un conteo. Una proporción divide cada conteo por el total y un porcentaje multiplica esa proporción por 100.

round(100 * prop.table(frecuencias_sexo), 1)
#> 
#> Varones Mujeres 
#>    48.1    51.9

10.2 Porcentaje de personas activas

Cuando una variable solo contiene 0 y 1, su media es la proporción de valores iguales a 1.

porcentaje_activos <- round(100 * mean(datos$activo, na.rm = TRUE), 1)
porcentaje_activos
#> [1] 72

El cálculo funciona porque activo fue definida expresamente como una variable binaria: su media es la proporción de valores iguales a 1. Multiplicar por 100 convierte esa proporción en porcentaje.

10.3 Comparar sexo y educación con R base

tabla_base <- aggregate(
  activo ~ sexo + educacion,
  data = datos,
  FUN = mean
)

casos_base <- aggregate(
  activo ~ sexo + educacion,
  data = datos,
  FUN = length
)

tabla_base$porcentaje <- round(100 * tabla_base$activo, 1)
tabla_base$casos <- casos_base$activo
tabla_base <- tabla_base[, c("sexo", "educacion", "casos", "porcentaje")]

tabla_base

10.4 La misma operación con dplyr

grupos <- dplyr::group_by(datos, sexo, educacion)

tabla_alt <- dplyr::summarise(
  grupos,
  casos = dplyr::n(),
  porcentaje = round(100 * mean(activo), 1),
  .groups = "drop"
)

tabla_alt

Verificamos que los resultados coincidan después de ordenarlos:

tabla_base_ordenada <- tabla_base[order(tabla_base$sexo, tabla_base$educacion), ]
tabla_alt_ordenada <- as.data.frame(
  dplyr::arrange(tabla_alt, sexo, educacion)
)

stopifnot(
  identical(tabla_base_ordenada$casos, tabla_alt_ordenada$casos),
  identical(tabla_base_ordenada$porcentaje, tabla_alt_ordenada$porcentaje),
  all(tabla_base_ordenada$porcentaje >= 0),
  all(tabla_base_ordenada$porcentaje <= 100),
  nrow(tabla_base_ordenada) == 4
)

knitr::kable(
  tabla_alt_ordenada,
  col.names = c("Sexo", "Educación", "Casos", "Personas activas (%)"),
  caption = "Actividad por sexo y educación en la muestra seleccionada"
)
Actividad por sexo y educación en la muestra seleccionada
Sexo Educación Casos Personas activas (%)
Varones Hasta secundaria incompleta 4913 80.0
Varones Secundaria completa o más 8915 83.0
Mujeres Hasta secundaria incompleta 4051 49.2
Mujeres Secundaria completa o más 10840 67.8

El denominador cambia entre filas: cada porcentaje se calcula dentro de su combinación de sexo y educación.

Práctica 7. Calculá el porcentaje de personas activas por sexo, primero con R base y luego con dplyr. Comprobá que coincidan.

Ver una solución
actividad_sexo_base <- aggregate(activo ~ sexo, data = datos, FUN = mean)
actividad_sexo_base$porcentaje <- round(100 * actividad_sexo_base$activo, 1)

actividad_sexo_alt <- dplyr::summarise(
  dplyr::group_by(datos, sexo),
  porcentaje = round(100 * mean(activo), 1),
  .groups = "drop"
)

actividad_sexo_base[, c("sexo", "porcentaje")]
actividad_sexo_alt
stopifnot(identical(
  actividad_sexo_base$porcentaje,
  actividad_sexo_alt$porcentaje
))

11 Construir gráficos legibles

Un gráfico legible permite responder estas preguntas con rapidez:

  • ¿Qué se está midiendo?
  • ¿Qué grupos se comparan?
  • ¿Cuál es la unidad del eje vertical?
  • ¿Qué período y universo aparecen?
  • ¿Cuál es la fuente?
  • ¿Qué limitación importante debemos recordar?

11.1 Gráfico con R base

Para barplot() convertimos la tabla a una matriz. Usamos dos colores distinguibles, una escala de 0 a 100, etiquetas directas y una leyenda fuera de las barras.

matriz <- xtabs(porcentaje ~ educacion + sexo, data = tabla_base)

op <- par(mar = c(5, 5, 4, 1), las = 1)

posiciones <- barplot(
  matriz,
  beside = TRUE,
  ylim = c(0, 100),
  col = c("#0072B2", "#D55E00"),
  border = NA,
  main = "Actividad según sexo y educación",
  sub = "Personas de 18 a 65 años; porcentajes sin ponderar",
  xlab = "Sexo",
  ylab = "Personas activas en la muestra (%)",
  legend.text = rownames(matriz),
  args.legend = list(
    x = "topleft",
    bty = "n",
    cex = 0.85,
    inset = c(0.01, 0.01)
  )
)

text(
  x = posiciones,
  y = matriz,
  labels = paste0(format(matriz, decimal.mark = ",", nsmall = 1), "%"),
  pos = 3,
  cex = 0.85
)
Porcentaje de personas activas por sexo y educación. Resultados sin ponderar.

Porcentaje de personas activas por sexo y educación. Resultados sin ponderar.


par(op)

11.2 Gráfico con ggplot2

ggplot2 organiza la construcción en capas. Cada capa cumple una función específica y se suma con +:

Componente Pregunta que responde Ejemplo
Datos ¿Qué tabla usamos? tabla_alt
Mapeos estéticos ¿Qué variables ocupan cada canal visual? aes(x = sexo, y = porcentaje, fill = educacion)
Geometría ¿Cómo representamos las observaciones? geom_col()
Escala ¿Cómo se traducen valores a posiciones, colores o etiquetas? scale_y_continuous()
Etiquetas ¿Qué contexto necesita quien lee? labs()
Tema ¿Cómo se presentan los elementos que rodean a los datos? theme_minimal() y theme()

Dentro de aes() escribimos relaciones con variables de la tabla. Fuera de aes() fijamos una propiedad para todos los elementos. Por ejemplo, aes(fill = educacion) asigna un color a cada nivel educativo, mientras que colour = "white" aplicaría el mismo borde blanco a todas las barras.

El siguiente gráfico se puede leer de arriba hacia abajo: datos y mapeos generales, geometrías, escala, textos y aspecto visual.

grafico <- ggplot(
  tabla_alt,
  aes(x = sexo, y = porcentaje, fill = educacion)
) +
  geom_col(
    position = position_dodge(width = 0.82),
    width = 0.72
  ) +
  geom_text(
    aes(label = paste0(format(porcentaje, decimal.mark = ",", nsmall = 1), "%")),
    position = position_dodge(width = 0.82),
    vjust = -0.45,
    size = 4
  ) +
  scale_fill_manual(values = c("#0072B2", "#D55E00")) +
  scale_y_continuous(
    limits = c(0, 100),
    breaks = seq(0, 100, 20),
    labels = function(x) paste0(x, "%"),
    expand = expansion(mult = c(0, 0.06))
  ) +
  labs(
    title = "Actividad según sexo y educación",
    subtitle = "Personas de 18 a 65 años; porcentajes sin ponderar",
    x = NULL,
    y = "Personas activas en la muestra",
    fill = "Educación",
    caption = paste(
      "Fuente: INDEC, EPH, primer trimestre de 2025.",
      "Cálculos sobre la muestra; no son tasas oficiales."
    )
  ) +
  theme_minimal(base_size = 13) +
  theme(
    legend.position = "bottom",
    legend.title = element_text(face = "bold"),
    panel.grid.minor = element_blank(),
    panel.grid.major.x = element_blank(),
    plot.title = element_text(face = "bold", size = 16),
    plot.subtitle = element_text(color = "#444444"),
    plot.caption = element_text(hjust = 0, color = "#555555"),
    axis.text.x = element_text(face = "bold")
  )

grafico
Porcentaje de personas activas por sexo y educación. Fuente: INDEC, EPH 1T2025.

Porcentaje de personas activas por sexo y educación. Fuente: INDEC, EPH 1T2025.

La tabla ya contiene los porcentajes calculados, por eso usamos geom_col(). geom_bar() se reserva habitualmente para contar observaciones. position_dodge() coloca los grupos lado a lado y usa el mismo ancho en las barras y sus etiquetas para mantenerlas alineadas.

11.2.1 Una alternativa contemporánea: gráfico de puntos

Las barras funcionan bien para introducir comparaciones desde cero. Un gráfico de puntos ocupa menos tinta y deja los valores sobre una escala común, una opción útil cuando el interés principal está en comparar porcentajes.

grafico_puntos <- ggplot(
  tabla_alt,
  aes(x = porcentaje, y = educacion, colour = sexo)
) +
  geom_line(
    aes(group = educacion),
    colour = "#D9D9D9",
    linewidth = 1.2
  ) +
  geom_point(size = 4) +
  geom_text(
    aes(label = paste0(format(porcentaje, decimal.mark = ",", nsmall = 1), "%")),
    hjust = -0.25,
    show.legend = FALSE
  ) +
  scale_colour_manual(values = c("Varones" = "#0072B2", "Mujeres" = "#D55E00")) +
  scale_x_continuous(
    limits = c(0, 100),
    breaks = seq(0, 100, 20),
    labels = function(x) paste0(x, "%"),
    expand = expansion(mult = c(0, 0.05))
  ) +
  labs(
    title = "La actividad es mayor entre los varones en ambos grupos educativos",
    subtitle = "Personas de 18 a 65 años; porcentajes sin ponderar",
    x = "Personas activas en la muestra",
    y = NULL,
    colour = "Sexo",
    caption = "Fuente: INDEC, EPH, primer trimestre de 2025. Cálculos sobre la muestra."
  ) +
  theme_minimal(base_size = 13) +
  theme(
    legend.position = "top",
    legend.title = element_text(face = "bold"),
    panel.grid.minor = element_blank(),
    panel.grid.major.y = element_blank(),
    plot.title = element_text(face = "bold", size = 15),
    plot.caption = element_text(hjust = 0, colour = "#555555")
  )

grafico_puntos
Porcentaje de personas activas por sexo y educación. Alternativa con puntos; resultados sin ponderar.

Porcentaje de personas activas por sexo y educación. Alternativa con puntos; resultados sin ponderar.

El título comunica el hallazgo principal y el subtítulo delimita el universo. Esta jerarquía orienta la lectura, mientras que el epígrafe conserva la fuente y el alcance del cálculo. Elegir entre barras y puntos depende de la pregunta y del espacio disponible; comparar ambas versiones ayuda a evaluar cuál hace más evidente el patrón.

11.3 Guardar el gráfico

ggsave(
  filename = "grafico_actividad_EPH_1T2025.png",
  plot = grafico,
  width = 10,
  height = 6,
  units = "in",
  dpi = 180,
  bg = "white"
)

La extensión del archivo define el formato. width y height controlan el tamaño físico; dpi controla la resolución de una imagen rasterizada como PNG.

11.4 Interpretar con cautela

Una lectura descriptiva podría decir:

En esta muestra de personas de 18 a 65 años, el porcentaje de actividad es mayor entre los varones que entre las mujeres en los dos grupos educativos. La diferencia más amplia se observa en el grupo de hasta secundaria incompleta.

El gráfico muestra asociaciones descriptivas entre las variables. Una afirmación causal requeriría otro diseño de análisis. Además, los porcentajes caracterizan la muestra seleccionada: el cálculo usa un universo de edad particular y prescinde de ponderadores.

Práctica 8. Cambiá el título del gráfico para que mencione el indicador, el período y el grupo de edad. Después escribí una oración descriptiva y una limitación.

Ver una respuesta posible

Título: “Porcentaje de actividad por sexo y educación, EPH 1T2025, 18 a 65 años”.

Descripción: “En los dos grupos educativos, las barras de los varones son más altas que las de las mujeres”.

Limitación: “Los porcentajes describen los casos seleccionados; una estimación oficial requiere aplicar los ponderadores y considerar el diseño de la encuesta”.

12 Errores frecuentes y cómo resolverlos

Un mensaje de error informa el punto en el que una instrucción dejó de ser comprensible para R o la tarea quedó incompleta.

12.1 Estrategia general

  1. Leer el último mensaje completo.
  2. Identificar el nombre del objeto, archivo, función o paquete mencionado.
  3. Revisar ortografía, mayúsculas, comillas, comas y paréntesis.
  4. Ejecutar los bloques desde arriba, en orden.
  5. Comprobar el tipo y el contenido con class(), str(), names() o head().
  6. Reducir el problema a una línea pequeña.
  7. Consultar la ayuda con ?funcion.

12.2 Guía rápida

Mensaje o síntoma Causa habitual Qué revisar
object 'x' not found El objeto no fue creado o el nombre no coincide. Ejecutar el bloque anterior; revisar mayúsculas.
could not find function Falta cargar un paquete o hay un error en el nombre. Usar library() o paquete::funcion().
there is no package called... El paquete no está instalado. Ejecutar install.packages() una vez.
cannot open the connection La ruta o el archivo no existen. Usar file.exists() y revisar la carpeta del proyecto.
unexpected symbol o unexpected end Falta una coma, comilla o paréntesis. Revisar la línea y también la anterior.
non-numeric argument Se intentó calcular con texto o categorías. Revisar class() y la importación.
El resultado es NA Hay valores faltantes. Decidir si corresponde na.rm = TRUE.
Todo queda en una columna El separador de importación es incorrecto. Comparar read.csv() y read.csv2().
Las tildes se ven mal La codificación no coincide. Probar fileEncoding = "Latin1" o "UTF-8".
Funciona por líneas pero no al tejer Dependía de objetos previos o rutas personales. Reiniciar R y ejecutar desde arriba con rutas relativas.
Las etiquetas del gráfico se superponen Hay poco espacio o demasiado texto. Aumentar tamaño, abreviar, girar o reubicar etiquetas.

Podemos comprobar una ruta sin intentar abrirla:

file.exists(ruta_datos)
#> [1] TRUE

También es útil reiniciar R desde Session > Restart R y ejecutar todo desde el principio. Una sesión limpia revela dependencias invisibles.

13 Para seguir practicando

El recorrido aplicado en este curso queda disponible como una secuencia reutilizable:

pregunta → pseudocódigo → proyecto → importación → exploración → filtro → recodificación → control → porcentaje → gráfico → interpretación

Para afianzar el proceso, elegí una variación de la pregunta original: cambiá el grupo de edad, reemplazá actividad por ocupación o compará otro nivel de agrupación educativa. Registrá la decisión en el pseudocódigo, adaptá el análisis y revisá cómo cambia la tabla y cuál de los dos gráficos comunica mejor el resultado.

Al terminar, escribí una breve nota que explique la modificación realizada, el principal resultado y el límite del análisis.

LS0tCnRpdGxlOiAiQ3Vyc28gaW50cm9kdWN0b3JpbyBkZSBSIGNvbiBtaWNyb2RhdG9zIGRlIGxhIEVQSCIKc3VidGl0bGU6ICJBbsOhbGlzaXMgZGUgbWljcm9kYXRvcyBkZSBsYSBFUEggY29uIFIiCmxhbmc6IGVzCmRhdGU6ICJgciBmb3JtYXQoU3lzLkRhdGUoKSwgJyVkLyVtLyVZJylgIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OgogICAgICBjb2xsYXBzZWQ6IHRydWUKICAgICAgc21vb3RoX3Njcm9sbDogdHJ1ZQogICAgdG9jX2RlcHRoOiAzCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICAgIGNvZGVfZm9sZGluZzogc2hvdwogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgc2VsZl9jb250YWluZWQ6IHRydWUKICAgIHRoZW1lOiBmbGF0bHkKICAgIGhpZ2hsaWdodDogdGFuZ28KICAgIG1hdGhqYXg6IG51bGwKICAgIGZpZ19jYXB0aW9uOiB0cnVlCiAgICBkZl9wcmludDogcGFnZWQKLS0tCgpgYGB7ciBjb25maWd1cmFjaW9uLCBpbmNsdWRlPUZBTFNFfQpwYXF1ZXRlcyA8LSBjKCJkcGx5ciIsICJqYW5pdG9yIiwgImdncGxvdDIiLCAicmVhZHhsIiwgImtuaXRyIikKZmFsdGFudGVzIDwtIHBhcXVldGVzWyF2YXBwbHkocGFxdWV0ZXMsIHJlcXVpcmVOYW1lc3BhY2UsIGxvZ2ljYWwoMSksIHF1aWV0bHkgPSBUUlVFKV0KCmlmIChsZW5ndGgoZmFsdGFudGVzKSA+IDApIHsKICBzdG9wKAogICAgIkZhbHRhbiBlc3RvcyBwYXF1ZXRlczogIiwgcGFzdGUoZmFsdGFudGVzLCBjb2xsYXBzZSA9ICIsICIpLAogICAgIi4gSW5zdGFsYWxvcyBhbnRlcyBkZSB0ZWplciBlbCBkb2N1bWVudG8uIgogICkKfQoKa25pdHI6Om9wdHNfY2h1bmskc2V0KAogIGVjaG8gPSBUUlVFLAogIG1lc3NhZ2UgPSBGQUxTRSwKICB3YXJuaW5nID0gRkFMU0UsCiAgY29sbGFwc2UgPSBUUlVFLAogIGNvbW1lbnQgPSAiIz4iLAogIGZpZy5hbGlnbiA9ICJjZW50ZXIiLAogIGZpZy53aWR0aCA9IDksCiAgZmlnLmhlaWdodCA9IDUuNSwKICBmaWcucmV0aW5hID0gMiwKICBvdXQud2lkdGggPSAiOTUlIgopCgpvcHRpb25zKHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSwgc2NpcGVuID0gOTk5KQoKcnV0YV9kYXRvcyA8LSBmaWxlLnBhdGgoCiAgIkVQSF91c3VfMWVyX1RyaW1fMjAyNV90eHQiLAogICJ1c3VfaW5kaXZpZHVhbF9UMTI1LnR4dCIKKQoKaWYgKCFmaWxlLmV4aXN0cyhydXRhX2RhdG9zKSkgewogIHN0b3AoCiAgICAiTm8gc2UgZW5jb250csOzIGVsIGFyY2hpdm8gZGUgZGF0b3MgZW46ICIsIHJ1dGFfZGF0b3MsCiAgICAiLiBSZXZpc8OhIHF1ZSBlbCBSIE1hcmtkb3duIGVzdMOpIGVuIGxhIGNhcnBldGEgcHJpbmNpcGFsIGRlbCBwcm95ZWN0by4iCiAgKQp9CmBgYAoKPHN0eWxlPgpib2R5IHsgZm9udC1zaXplOiAxN3B4OyBsaW5lLWhlaWdodDogMS42NTsgfQpoMSwgaDIsIGgzIHsgbGluZS1oZWlnaHQ6IDEuMjU7IH0KcHJlIHsgYm9yZGVyLXJhZGl1czogNXB4OyB9CmNvZGUgeyBmb250LXNpemU6IDAuOTJlbTsgfQoub2JqZXRpdm8sIC5pbXBvcnRhbnRlLCAucHJhY3RpY2EgewogIHBhZGRpbmc6IDAuODVyZW0gMXJlbTsKICBtYXJnaW46IDFyZW0gMDsKICBib3JkZXItbGVmdDogNXB4IHNvbGlkICMyYzdmYjg7CiAgYmFja2dyb3VuZDogI2Y0ZjlmYzsKfQouaW1wb3J0YW50ZSB7IGJvcmRlci1sZWZ0LWNvbG9yOiAjZDk1ZjBlOyBiYWNrZ3JvdW5kOiAjZmZmOGYyOyB9Ci5wcmFjdGljYSB7IGJvcmRlci1sZWZ0LWNvbG9yOiAjMjM4YjQ1OyBiYWNrZ3JvdW5kOiAjZjNmYWY1OyB9CmRldGFpbHMgewogIHBhZGRpbmc6IDAuNzVyZW0gMXJlbTsKICBtYXJnaW46IDAuOHJlbSAwIDEuMnJlbSAwOwogIGJvcmRlcjogMXB4IHNvbGlkICNjZmQ4ZGM7CiAgYm9yZGVyLXJhZGl1czogNXB4OwogIGJhY2tncm91bmQ6ICNmYWZhZmE7Cn0Kc3VtbWFyeSB7IGN1cnNvcjogcG9pbnRlcjsgZm9udC13ZWlnaHQ6IDYwMDsgfQp0YWJsZSB7IG1hcmdpbi1ib3R0b206IDEuNXJlbTsgfQpjYXB0aW9uIHsgY29sb3I6ICM0NDQ7IGZvbnQtd2VpZ2h0OiA2MDA7IH0KQG1lZGlhIChtYXgtd2lkdGg6IDc2OHB4KSB7CiAgYm9keSB7IGZvbnQtc2l6ZTogMTZweDsgfQogIC5tYWluLWNvbnRhaW5lciB7CiAgICB3aWR0aDogMTAwJSAhaW1wb3J0YW50OwogICAgbWF4LXdpZHRoOiAxMDAlICFpbXBvcnRhbnQ7CiAgICBtYXJnaW46IDAgIWltcG9ydGFudDsKICAgIHBhZGRpbmctbGVmdDogMTZweDsKICAgIHBhZGRpbmctcmlnaHQ6IDE2cHg7CiAgfQogIC5idG4tZ3JvdXAucHVsbC1yaWdodCB7CiAgICBmbG9hdDogbm9uZSAhaW1wb3J0YW50OwogICAgZGlzcGxheTogYmxvY2s7CiAgICBtYXJnaW4tYm90dG9tOiAxcmVtOwogIH0KICB0YWJsZSB7IGRpc3BsYXk6IGJsb2NrOyBvdmVyZmxvdy14OiBhdXRvOyB9CiAgaW1nIHsgbWF4LXdpZHRoOiAxMDAlOyBoZWlnaHQ6IGF1dG87IH0KfQo8L3N0eWxlPgoKPGRpdiBjbGFzcz0ib2JqZXRpdm8iPgoqKk1ldGEgZGVsIGN1cnNvLioqIEFwcmVuZGVyIGEgb3JnYW5pemFyIHVuIHByb3llY3RvLCBpbXBvcnRhciB1bmEgYmFzZSByZWFsLCBlbnRlbmRlciBxdcOpIGNvbnRpZW5lLCB0cmFuc2Zvcm1hcmxhIGNvbiBjdWlkYWRvLCBjYWxjdWxhciBwb3JjZW50YWplcyB5IHByb2R1Y2lyIHVuIGdyw6FmaWNvIHF1ZSBvdHJhIHBlcnNvbmEgcHVlZGEgbGVlciBzaW4gY29ub2NlciBSLgo8L2Rpdj4KCkVzdGUgY3Vyc28gcHJvcG9uZSB1bmEgcHJpbWVyYSBhcHJveGltYWNpw7NuIHkgcGFydGUgZGVzZGUgbG9zIGNvbmNlcHRvcyBiw6FzaWNvcy4gVmFtb3MgYSB0cmFiYWphciBjb24gbGEgYmFzZSBkZSBwZXJzb25hcyBkZSBsYSAqKkVuY3Vlc3RhIFBlcm1hbmVudGUgZGUgSG9nYXJlcyAoRVBIKSwgcHJpbWVyIHRyaW1lc3RyZSBkZSAyMDI1KiosIHB1YmxpY2FkYSBwb3IgZWwgSU5ERUMuCgpFbCBhbsOhbGlzaXMgcmVzcG9uZGVyw6EgdW5hIHByZWd1bnRhIGNvbmNyZXRhOgoKPiDCv1F1w6kgcG9yY2VudGFqZSBkZSBsYXMgcGVyc29uYXMgZGUgMTggYSA2NSBhw7FvcyBlc3TDoSBlY29uw7NtaWNhbWVudGUgYWN0aXZvIGVuIGxhIG11ZXN0cmEgeSBjw7NtbyBjYW1iaWEgZXNlIHBvcmNlbnRhamUgc2Vnw7puIHNleG8geSBuaXZlbCBlZHVjYXRpdm8gYWdydXBhZG8/CgpVc2FyZW1vcyBkb3MgcmVjb3JyaWRvcy4gUHJpbWVybyB2ZXJlbW9zIGNhZGEgaWRlYSBjb24gKipSIGJhc2UqKiwgbGFzIGhlcnJhbWllbnRhcyBxdWUgdmllbmVuIGNvbiBSLiBEZXNwdcOpcyBtb3N0cmFyZW1vcyB1bmEgYWx0ZXJuYXRpdmEgY29uIHBhcXVldGVzIGNvbW8gYGRwbHlyYCwgYGphbml0b3JgIHkgYGdncGxvdDJgLiBDb21wYXJhciBsYXMgZG9zIHNpbnRheGlzIHBlcm1pdGUgcmVjb25vY2VyIGRpc3RpbnRhcyBmb3JtYXMgZGUgcmVzb2x2ZXIgdW5hIG1pc21hIHRhcmVhIHkgZWxlZ2lyIGxhIG3DoXMgY2xhcmEgcGFyYSBjYWRhIHNpdHVhY2nDs24uCgo8ZGl2IGNsYXNzPSJpbXBvcnRhbnRlIj4KKipMw61taXRlLioqIEVuIGVzdGUgY3Vyc28gdHJhYmFqYW1vcyBjb24gcG9yY2VudGFqZXMgc2luIHBvbmRlcmFyLiBQb3IgbG8gdGFudG8sIGxvcyByZXN1bHRhZG9zIGRlc2NyaWJlbiBsb3MgY2Fzb3Mgb2JzZXJ2YWRvcyBkZXNwdcOpcyBkZWwgZmlsdHJvIHkgbm8gcmVwcmVzZW50YW4gdGFzYXMgb2ZpY2lhbGVzIG5pIGVzdGltYWNpb25lcyBkZSB0b2RhIGxhIHBvYmxhY2nDs24uIExhIEVQSCBjdWJyZSAzMSBhZ2xvbWVyYWRvcyB1cmJhbm9zIHkgc3UgYW7DoWxpc2lzIHBvYmxhY2lvbmFsIHJlcXVpZXJlIGNvbnNpZGVyYXIgZWwgZGlzZcOxbyBkZSBsYSBlbmN1ZXN0YS4KPC9kaXY+CgojIEFudGVzIGRlIGVtcGV6YXIKCiMjIFIgeSBSU3R1ZGlvIGN1bXBsZW4gZnVuY2lvbmVzIGRpZmVyZW50ZXMKCioqUioqIGVzIGVsIGxlbmd1YWplIHkgZWwgcHJvZ3JhbWEgcXVlIGludGVycHJldGEgbnVlc3RyYXMgaW5zdHJ1Y2Npb25lcy4gKipSU3R1ZGlvKiogZXMgdW4gZW50b3JubyBkZSB0cmFiYWpvIHF1ZSByZcO6bmUgdW4gZWRpdG9yLCBsYSBjb25zb2xhLCBsb3Mgb2JqZXRvcywgbG9zIGFyY2hpdm9zLCBsYSBheXVkYSB5IGxvcyBncsOhZmljb3MgZW4gdW5hIG1pc21hIHZlbnRhbmEuCgpFbiBSU3R1ZGlvIHN1ZWxlbiB2ZXJzZSBjdWF0cm8gem9uYXM6Cgp8IFpvbmEgfCBQYXJhIHF1w6kgc2lydmUgfAp8Oi0tfDotLXwKfCBFZGl0b3IgfCBFc2NyaWJpciB5IGd1YXJkYXIgc2NyaXB0cyBvIGRvY3VtZW50b3MgUiBNYXJrZG93bi4gfAp8IENvbnNvbGEgfCBFamVjdXRhciBpbnN0cnVjY2lvbmVzIHkgbW9zdHJhciByZXN1bHRhZG9zIG8gZXJyb3Jlcy4gfAp8IEVudmlyb25tZW50IHwgVmVyIGxvcyBvYmpldG9zIGNyZWFkb3MgZHVyYW50ZSBsYSBzZXNpw7NuLiB8CnwgRmlsZXMsIFBsb3RzLCBQYWNrYWdlcywgSGVscCB8IE5hdmVnYXIgYXJjaGl2b3MsIGdyw6FmaWNvcywgcGFxdWV0ZXMgeSBkb2N1bWVudGFjacOzbi4gfAoKTGEgY29uc29sYSBzaXJ2ZSBwYXJhIHByb2Jhci4gRWwgc2NyaXB0IG8gZWwgUiBNYXJrZG93biBzaXJ2ZW4gcGFyYSAqKmNvbnNlcnZhciB5IHJlcHJvZHVjaXIqKiBlbCB0cmFiYWpvLiBTaSB1biBhbsOhbGlzaXMgZXhpc3RlIHNvbGFtZW50ZSBlbiBsYSBjb25zb2xhLCBzZXLDoSBkaWbDrWNpbCByZWNvbnN0cnVpcmxvIG3DoXMgYWRlbGFudGUuCgojIyBDw7NtbyBlamVjdXRhciBjw7NkaWdvCgpFbiB1biBzY3JpcHQsIGNvbG9jYW1vcyBlbCBjdXJzb3IgZW4gdW5hIGzDrW5lYSB5IHVzYW1vcyAqKkN0cmwgKyBFbnRlcioqIGVuIFdpbmRvd3MgbyBMaW51eCwgbyAqKkNtZCArIEVudGVyKiogZW4gbWFjT1MuIEVuIHVuIFIgTWFya2Rvd24gdGFtYmnDqW4gcG9kZW1vcyB1c2FyIGVsIGJvdMOzbiB2ZXJkZSBkZSBjYWRhIGJsb3F1ZS4KClByb2JlbW9zIHRyZXMgaW5zdHJ1Y2Npb25lcyBzZW5jaWxsYXM6CgpgYGB7ciBwcmltZXItY29udGFjdG99CjIgKyAyCgplZGFkIDwtIDMwCmVkYWQgKyA1CgptZWFuKGMoMjAsIDMwLCA0MCkpCmBgYAoKTGEgZmxlY2hhIGA8LWAgYXNpZ25hIHVuIHZhbG9yIGEgdW4gb2JqZXRvLiBTZSBsZWUg4oCcZWRhZCByZWNpYmUgMzDigJ0uIFIgZGlzdGluZ3VlIG1hecO6c2N1bGFzIGRlIG1pbsO6c2N1bGFzOiBgZWRhZGAsIGBFZGFkYCB5IGBFREFEYCBzZXLDrWFuIHRyZXMgbm9tYnJlcyBkaWZlcmVudGVzLgoKPGRpdiBjbGFzcz0icHJhY3RpY2EiPgoqKlByw6FjdGljYSAxLioqIENyZcOhIHVuIG9iamV0byBsbGFtYWRvIGBpbmdyZXNvc2AgY29uIGxvcyB2YWxvcmVzIDEwMCwgMjAwIHkgMzAwLiBEZXNwdcOpcyBjYWxjdWzDoSBzdSBwcm9tZWRpby4KPC9kaXY+Cgo8ZGV0YWlscz4KPHN1bW1hcnk+VmVyIHVuYSBzb2x1Y2nDs248L3N1bW1hcnk+CgpgYGB7ciBzb2x1Y2lvbi0xfQppbmdyZXNvcyA8LSBjKDEwMCwgMjAwLCAzMDApCm1lYW4oaW5ncmVzb3MpCmBgYAoKYGMoKWAgcmXDum5lIHZhbG9yZXMgZW4gdW4gdmVjdG9yIHkgYG1lYW4oKWAgY2FsY3VsYSBzdSBtZWRpYS4KPC9kZXRhaWxzPgoKIyBQZW5zYXIgYW50ZXMgZGUgcHJvZ3JhbWFyCgojIyBRdcOpIGVzIGVsIHBzZXVkb2PDs2RpZ28KCkVsIHBzZXVkb2PDs2RpZ28gZXMgdW5hIGxpc3RhIG9yZGVuYWRhIGRlIGFjY2lvbmVzIGV4cHJlc2FkYXMgY29uIHBhbGFicmFzIGNvbXVuZXMsIGluZGVwZW5kaWVudGUgZGUgbGEgc2ludGF4aXMgZGUgUi4gU3UgZnVuY2nDs24gZXMgc2VwYXJhciBkb3MgcHJvYmxlbWFzOgoKMS4gKipRdcOpIHF1ZXJlbW9zIGhhY2VyLioqCjIuICoqQ8OzbW8gc2UgZXNjcmliZSBlbiBSLioqCgpQYXJhIG51ZXN0cmEgcHJlZ3VudGEsIGVsIHBsYW4gc2Vyw6E6CgoxLiBBYnJpciBsYSBiYXNlIGRlIHBlcnNvbmFzLgoyLiBSZXZpc2FyIHF1w6kgcmVwcmVzZW50YSBjYWRhIGZpbGEgeSBjYWRhIGNvbHVtbmEuCjMuIEVsZWdpciBzZXhvLCBlZGFkLCBlZHVjYWNpw7NuIHkgY29uZGljacOzbiBkZSBhY3RpdmlkYWQuCjQuIENvbnNlcnZhciBwZXJzb25hcyBkZSAxOCBhIDY1IGHDsW9zIGNvbiBjw7NkaWdvcyB2w6FsaWRvcy4KNS4gVHJhbnNmb3JtYXIgY8OzZGlnb3MgZW4gY2F0ZWdvcsOtYXMgY29tcHJlbnNpYmxlcy4KNi4gQ3JlYXIgdW5hIHZhcmlhYmxlIHF1ZSBpbmRpcXVlIHNpIGxhIHBlcnNvbmEgZXN0w6EgYWN0aXZhLgo3LiBDb250YXIgY2Fzb3MgeSBjYWxjdWxhciBwb3JjZW50YWplcy4KOC4gQ29tcGFyYXIgZ3J1cG9zIGNvbiB1bmEgdGFibGEgeSB1biBncsOhZmljby4KOS4gRGVzY3JpYmlyIGxvcyByZXN1bHRhZG9zIHNpbiBhZmlybWFyIGNhdXNhbGlkYWQuCgpFbCBwc2V1ZG9jw7NkaWdvIHRhbWJpw6luIHBlcm1pdGUgZGV0ZWN0YXIgZGVjaXNpb25lcyBvY3VsdGFzLiBQb3IgZWplbXBsbywg4oCcY29uc2VydmFyIHBlcnNvbmFzIGFkdWx0YXPigJ0gcmVxdWllcmUgaW5kaWNhciBxdcOpIGVkYWRlcyBjb25zaWRlcmFyZW1vcyB5IHBvciBxdcOpLgoKPGRpdiBjbGFzcz0icHJhY3RpY2EiPgoqKlByw6FjdGljYSAyLioqIFJlZXNjcmliw60gbG9zIHBhc29zIDYgYSA5IHBhcmEgZXN0dWRpYXIgcGVyc29uYXMgb2N1cGFkYXMgZW4gbHVnYXIgZGUgcGVyc29uYXMgYWN0aXZhcy4KPC9kaXY+Cgo8ZGV0YWlscz4KPHN1bW1hcnk+VmVyIHVuYSBzb2x1Y2nDs24gcG9zaWJsZTwvc3VtbWFyeT4KCjEuIENyZWFyIHVuYSB2YXJpYWJsZSBxdWUgdmFsZ2EgMSBjdWFuZG8gbGEgcGVyc29uYSBlc3TDoSBvY3VwYWRhIHkgMCBjdWFuZG8gZXN0w6EgZGVzb2N1cGFkYSBvIGluYWN0aXZhLgoyLiBDb250YXIgY2Fzb3MgeSBjYWxjdWxhciBlbCBwb3JjZW50YWplIGRlIHBlcnNvbmFzIG9jdXBhZGFzLgozLiBDb21wYXJhciBlbCBwb3JjZW50YWplIHBvciBzZXhvIHkgZWR1Y2FjacOzbi4KNC4gR3JhZmljYXIgeSBkZXNjcmliaXIgbGFzIGRpZmVyZW5jaWFzIG9ic2VydmFkYXMsIGFjbGFyYW5kbyBxdWUgc29uIHBvcmNlbnRhamVzIHNpbiBwb25kZXJhci4KPC9kZXRhaWxzPgoKIyBPYmpldG9zLCBvcGVyYWRvcmVzIHkgZnVuY2lvbmVzCgojIyBPYmpldG9zIHkgdGlwb3MgZGUgZGF0b3MKClVuICoqb2JqZXRvKiogZXMgdW4gbm9tYnJlIHF1ZSB1c2Ftb3MgcGFyYSBndWFyZGFyIGFsZ28uIFB1ZWRlIGNvbnRlbmVyIHVuIG7Dum1lcm8sIHRleHRvLCB1bmEgc2VjdWVuY2lhIG8gdW5hIHRhYmxhIGNvbXBsZXRhLgoKYGBge3IgdGlwb3MtZGUtZGF0b3N9CmNhbnRpZGFkIDwtIDEyCmN1cnNvIDwtICJJbnRyb2R1Y2Npw7NuIGEgUiIKYXByb2JhZG8gPC0gVFJVRQplZGFkZXMgPC0gYygxOCwgMjUsIDQwLCBOQSkKCmNsYXNzKGNhbnRpZGFkKQpjbGFzcyhjdXJzbykKY2xhc3MoYXByb2JhZG8pCmBgYAoKYE5BYCBzaWduaWZpY2EgcXVlIHVuIHZhbG9yIGVzdMOhIGF1c2VudGUgbyBubyBkaXNwb25pYmxlIHkgc2UgZGlmZXJlbmNpYSBkZSBjZXJvLiBNdWNoYXMgZnVuY2lvbmVzIG5lY2VzaXRhbiBxdWUgaW5kaXF1ZW1vcyBxdcOpIGhhY2VyIGNvbiBlc29zIHZhbG9yZXM6CgpgYGB7ciB2YWxvcmVzLWZhbHRhbnRlc30KbWVhbihlZGFkZXMpCm1lYW4oZWRhZGVzLCBuYS5ybSA9IFRSVUUpCmBgYAoKRWwgYXJndW1lbnRvIGBuYS5ybSA9IFRSVUVgIHBpZGUgb21pdGlyIGxvcyBgTkFgIGR1cmFudGUgZWwgY8OhbGN1bG8geSBjb25zZXJ2YSBpbnRhY3RvIGVsIG9iamV0byBvcmlnaW5hbC4KCiMjIE9wZXJhZG9yZXMgZnJlY3VlbnRlcwoKfCBPcGVyYWRvciB8IFByZWd1bnRhIHF1ZSByZXByZXNlbnRhIHwgRWplbXBsbyB8Cnw6LS06fDotLXw6LS18CnwgYD09YCB8IMK/RXMgaWd1YWwgYT8gfCBgZWRhZCA9PSAzMGAgfAp8IGAhPWAgfCDCv0VzIGRpc3RpbnRvIGRlPyB8IGBzZXhvICE9IDlgIHwKfCBgPmAgeSBgPGAgfCDCv0VzIG1heW9yIG8gbWVub3I/IHwgYGVkYWQgPiAxOGAgfAp8IGA+PWAgeSBgPD1gIHwgwr9JbmNsdXllIGVsIGzDrW1pdGU/IHwgYGVkYWQgPj0gMThgIHwKfCBgJWluJWAgfCDCv1BlcnRlbmVjZSBhIGVzdGUgY29uanVudG8/IHwgYGVzdGFkbyAlaW4lIGMoMSwgMilgIHwKfCBgJmAgfCDCv1NlIGN1bXBsZW4gYW1iYXMgY29uZGljaW9uZXM/IHwgYGVkYWQgPj0gMTggJiBlZGFkIDw9IDY1YCB8CnwgYHxgIHwgwr9TZSBjdW1wbGUgYWwgbWVub3MgdW5hPyB8IGBlc3RhZG8gPT0gMSB8IGVzdGFkbyA9PSAyYCB8CnwgYCFgIHwgTmVnYWNpw7NuIHwgYCFpcy5uYShlZGFkKWAgfAoKVW4gZXJyb3IgY29tw7puIGVzIGVzY3JpYmlyIGA9YCBjdWFuZG8gcXVlcmVtb3MgY29tcGFyYXIuIFBhcmEgcHJlZ3VudGFyIHNpIGRvcyB2YWxvcmVzIHNvbiBpZ3VhbGVzIHVzYW1vcyBgPT1gLgoKIyMgQ8OzbW8gbGVlciB1bmEgZnVuY2nDs24KClVuYSBmdW5jacOzbiByZWNpYmUgZW50cmFkYXMsIHJlYWxpemEgdW5hIHRhcmVhIHkgZGV2dWVsdmUgdW5hIHNhbGlkYS4gUGFyYSBsZWVyIHVuYSBsbGFtYWRhIGNvbnZpZW5lIGF2YW56YXIgZGVzZGUgZWwgbm9tYnJlIGhhY2lhIGVsIGludGVyaW9yIGRlIGxvcyBwYXLDqW50ZXNpcy4gRW4gZXN0YSBleHByZXNpw7NuOgoKYGBgcgpyb3VuZChtZWFuKGVkYWRlcywgbmEucm0gPSBUUlVFKSwgZGlnaXRzID0gMSkKYGBgCgotIGByb3VuZCgpYCBlcyBsYSBmdW5jacOzbiBleHRlcmlvciB5IHJlY2liZSBlbCByZXN1bHRhZG8gZGUgYG1lYW4oKWAuCi0gYG1lYW4oKWAgZXMgbGEgZnVuY2nDs24gaW50ZXJpb3IgeSBzZSBlamVjdXRhIHByaW1lcm8uCi0gYGVkYWRlc2AgZXMgZWwgcHJpbWVyIGFyZ3VtZW50byBkZSBgbWVhbigpYCB5IGFwb3J0YSBsb3MgZGF0b3MuCi0gYG5hLnJtID0gVFJVRWAgaW5kaWNhIHF1ZSBsYSBmdW5jacOzbiBkZWJlIG9taXRpciB2YWxvcmVzIGF1c2VudGVzLgotIGBkaWdpdHMgPSAxYCBpbmRpY2EgcXVlIGByb3VuZCgpYCBkZWJlIGNvbnNlcnZhciB1biBkZWNpbWFsLgoKVW5hIGxsYW1hZGEgcHVlZGUgY29tYmluYXIgKiphcmd1bWVudG9zIHBvciBwb3NpY2nDs24qKiB5ICoqYXJndW1lbnRvcyBwb3Igbm9tYnJlKio6CgpgYGB7ciBsZWVyLWFyZ3VtZW50b3N9Cm1lYW4oZWRhZGVzLCBuYS5ybSA9IFRSVUUpCm1lYW4oeCA9IGVkYWRlcywgbmEucm0gPSBUUlVFKQpgYGAKCkxhcyBkb3MgbMOtbmVhcyBwcm9kdWNlbiBlbCBtaXNtbyByZXN1bHRhZG8uIEVuIGxhIHByaW1lcmEsIFIgcmVjb25vY2UgYGVkYWRlc2AgcG9yIHN1IHBvc2ljacOzbjsgZW4gbGEgc2VndW5kYSwgYHggPWAgbm9tYnJhIGV4cGzDrWNpdGFtZW50ZSBlbCBhcmd1bWVudG8uIE5vbWJyYXIgbGFzIG9wY2lvbmVzIG1lam9yYSBsYSBsZWN0dXJhIHkgZXZpdGEgZGVwZW5kZXIgZGVsIG9yZGVuLgoKTGEgYXl1ZGEgZGUgdW5hIGZ1bmNpw7NuIG11ZXN0cmEgc3UgZm9ybWEgZ2VuZXJhbCwgbG9zIGFyZ3VtZW50b3MsIGVsIHRpcG8gZGUgcmVzdWx0YWRvIHkgZWplbXBsb3MuIFBvZGVtb3MgYWJyaXJsYSBjb24gYD9tZWFuYCBvIGBoZWxwKG1lYW4pYC4gVGFtYmnDqW4gcG9kZW1vcyBpbnNwZWNjaW9uYXIgbG9zIGFyZ3VtZW50b3MgZGVzZGUgbGEgY29uc29sYToKCmBgYHtyIGluc3BlY2Npb25hci1mdW5jaW9ufQphcmdzKG1lYW4pCmBgYAoKTGEgZXhwcmVzacOzbiBxdWUgYXBhcmVjZSBlbiBsYSBheXVkYSBzdWVsZSBpbmNsdWlyOgoKLSBhcmd1bWVudG9zIG9ibGlnYXRvcmlvcywgY29tbyBgeGAsIHF1ZSBkZWJlbW9zIHByb3BvcmNpb25hcjsKLSB2YWxvcmVzIHByZWRldGVybWluYWRvcywgcXVlIGxhIGZ1bmNpw7NuIHVzYSBzaSBubyBpbmRpY2Ftb3Mgb3RyYSBvcGNpw7NuOwotIGAuLi5gLCBxdWUgcGVybWl0ZSB0cmFuc21pdGlyIGFyZ3VtZW50b3MgYWRpY2lvbmFsZXM7Ci0gdW5hIHNlY2Npw7NuICoqVmFsdWUqKiwgcXVlIGV4cGxpY2EgcXXDqSBkZXZ1ZWx2ZSBsYSBmdW5jacOzbjsKLSB1bmEgc2VjY2nDs24gKipFeGFtcGxlcyoqLCBjb24gbGxhbWFkYXMgcXVlIHBvZGVtb3MgYWRhcHRhci4KCkFudGVzIGRlIGVqZWN1dGFyIHVuYSBmdW5jacOzbiBudWV2YSwgY29udmllbmUgcmVzcG9uZGVyIGN1YXRybyBwcmVndW50YXM6CgoxLiDCv1F1w6kgb2JqZXRvIHJlY2liZT8KMi4gwr9RdcOpIGFyZ3VtZW50b3MgbW9kaWZpY2FuIHN1IGNvbXBvcnRhbWllbnRvPwozLiDCv1F1w6kgdGlwbyBkZSBvYmpldG8gZGV2dWVsdmU/CjQuIMK/TGEgZnVuY2nDs24gdHJhbnNmb3JtYSBlbCBvYmpldG8gb3JpZ2luYWwgbyBjcmVhIHVuIHJlc3VsdGFkbyBudWV2bz8KCkVsIHByZWZpam8gYHBhcXVldGU6OmAgdGFtYmnDqW4gZm9ybWEgcGFydGUgZGUgbGEgbGVjdHVyYS4gUG9yIGVqZW1wbG8sIGBkcGx5cjo6ZmlsdGVyKGRhdG9zLCBDSDA2ID49IDE4KWAgaW5kaWNhIGNvbiBjbGFyaWRhZCBxdWUgdXNhbW9zIGxhIGZ1bmNpw7NuIGBmaWx0ZXIoKWAgZGVsIHBhcXVldGUgYGRwbHlyYCBzb2JyZSBlbCBvYmpldG8gYGRhdG9zYC4KCjxkaXYgY2xhc3M9InByYWN0aWNhIj4KKipQcsOhY3RpY2EgMy4qKiBMZcOpIGxhIGV4cHJlc2nDs24gYHJvdW5kKG1lYW4oYygxMCwgMTIsIE5BLCAxNSksIG5hLnJtID0gVFJVRSksIGRpZ2l0cyA9IDIpYCBkZXNkZSBhZGVudHJvIGhhY2lhIGFmdWVyYS4gSWRlbnRpZmljw6EgbG9zIGRhdG9zLCBsYXMgb3BjaW9uZXMgeSBlbCBvcmRlbiBkZSBlamVjdWNpw7NuLiBEZXNwdcOpcyBhbnRpY2lww6EgcXXDqSB0aXBvIGRlIHJlc3VsdGFkbyBkZXZvbHZlcsOhLgo8L2Rpdj4KCjxkZXRhaWxzPgo8c3VtbWFyeT5WZXIgdW5hIHNvbHVjacOzbjwvc3VtbWFyeT4KCjEuIGBjKClgIHJlw7puZSBsb3MgY3VhdHJvIHZhbG9yZXMgZW4gdW4gdmVjdG9yIG51bcOpcmljby4KMi4gYG1lYW4oKWAgY2FsY3VsYSBlbCBwcm9tZWRpbyB5IGBuYS5ybSA9IFRSVUVgIG9taXRlIGVsIHZhbG9yIGF1c2VudGUuCjMuIGByb3VuZCgpYCByZWNpYmUgZXNlIHByb21lZGlvIHkgYGRpZ2l0cyA9IDJgIGxvIHJlZG9uZGVhIGEgZG9zIGRlY2ltYWxlcy4KNC4gRWwgcmVzdWx0YWRvIGVzIHVuIMO6bmljbyB2YWxvciBudW3DqXJpY286IGAxMi4zM2AuCjwvZGV0YWlscz4KCiMgUHJveWVjdG8sIGNhcnBldGEgeSBydXRhcwoKIyMgVW5hIGVzdHJ1Y3R1cmEgcmVwcm9kdWNpYmxlCgpMYSBjYXJwZXRhIHByaW5jaXBhbCBkZWJlIGNvbnRlbmVyIGVzdGUgZG9jdW1lbnRvIHkgbGEgc3ViY2FycGV0YSBjb24gbG9zIGRhdG9zOgoKYGBgdGV4dAptYXJrZG93bi8K4pSc4pSA4pSAIGN1cnNvX2ludHJvZHVjdG9yaW9fUl9FUEhfMVQyMDI1LlJtZArilJTilIDilIAgRVBIX3VzdV8xZXJfVHJpbV8yMDI1X3R4dC8KICAgIOKUnOKUgOKUgCB1c3VfaW5kaXZpZHVhbF9UMTI1LnR4dAogICAg4pSU4pSA4pSAIHVzdV9ob2dhcl9UMTI1LnR4dApgYGAKCkxhICoqcnV0YSBkZSB0cmFiYWpvKiogZXMgbGEgZGlyZWNjacOzbiBxdWUgUiB1c2EgY29tbyBwdW50byBkZSBwYXJ0aWRhLiBgZ2V0d2QoKWAgbXVlc3RyYSBlc2EgY2FycGV0YSB5IGBsaXN0LmZpbGVzKClgIGVudW1lcmEgc3UgY29udGVuaWRvLgoKYGBge3IgcnV0YXMtZGVtb3N0cmFjaW9uLCBldmFsPUZBTFNFfQpnZXR3ZCgpCmxpc3QuZmlsZXMoKQpgYGAKCkVsIGJsb3F1ZSBxdWVkYSBzaW4gZWplY3V0YXIgcGFyYSBtYW50ZW5lciBmdWVyYSBkZWwgZG9jdW1lbnRvIGxhIHJ1dGEgcGVyc29uYWwgZGUgbGEgY29tcHV0YWRvcmEgcXVlIGxvIHRlamnDsy4KCkxhIHJ1dGEgcmVsYXRpdmEgcXVlIHVzYXJlbW9zIGVzOgoKYGBge3IgbW9zdHJhci1ydXRhfQpydXRhX2RhdG9zCmBgYAoKVW5hIHJ1dGEgcmVsYXRpdmEgZnVuY2lvbmEgZGVudHJvIGRlbCBwcm95ZWN0byBhdW5xdWUgbGEgY2FycGV0YSBjb21wbGV0YSBzZSBjb3BpZSBhIG90cmEgY29tcHV0YWRvcmEuIEVuIGNhbWJpbywgdW5hIHJ1dGEgY29tbyBgQzovVXNlcnMvQW5hL0Rlc2t0b3AvLi4uYCBkZXBlbmRlIGRlIHVuYSBwZXJzb25hIHkgdW5hIG3DoXF1aW5hIGVzcGVjw61maWNhcy4KCjxkaXYgY2xhc3M9ImltcG9ydGFudGUiPgoqKkJ1ZW5hIHByw6FjdGljYS4qKiBBYnLDrSBlbCBwcm95ZWN0byBvIGVsIGFyY2hpdm8gZGVzZGUgc3UgY2FycGV0YSBwcmluY2lwYWwuIExhcyBydXRhcyByZWxhdGl2YXMgbWFudGllbmVuIGVsIGPDs2RpZ28gcG9ydGFibGUuIGBzZXR3ZCgpYCBmaWphIHVuYSBydXRhIHBlcnNvbmFsIHkgYGZpbGUuY2hvb3NlKClgIGRldGllbmUgZWwgdGVqaWRvIGF1dG9tw6F0aWNvIG1pZW50cmFzIGVzcGVyYSB1biBjbGljLgo8L2Rpdj4KCiMgUGFxdWV0ZXMKCkxvcyBwYXF1ZXRlcyBhZ3JlZ2FuIGZ1bmNpb25lcyBhIFIuIEhheSBkb3MgcGFzb3MgZGlmZXJlbnRlczoKCjEuICoqSW5zdGFsYXI6Kiogc2UgaGFjZSB1bmEgdmV6IHBvciBjb21wdXRhZG9yYS4KMi4gKipDYXJnYXI6Kiogc2UgcmVwaXRlIGFsIGluaWNpYXIgdW5hIHNlc2nDs24gcXVlIHVzYSBlbCBwYXF1ZXRlLgoKYGBge3IgaW5zdGFsYXItcGFxdWV0ZXMsIGV2YWw9RkFMU0V9CiMgRWplY3V0YXIgdW5hIHNvbGEgdmV6IHkgc2luIGVsIHNpZ25vICMgaW5pY2lhbDoKaW5zdGFsbC5wYWNrYWdlcyhjKCJkcGx5ciIsICJqYW5pdG9yIiwgImdncGxvdDIiLCAicmVhZHhsIiwgInJtYXJrZG93biIpKQpgYGAKCmBgYHtyIGNhcmdhci1wYXF1ZXRlc30KbGlicmFyeShkcGx5cikKbGlicmFyeShqYW5pdG9yKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkocmVhZHhsKQpgYGAKClRhbWJpw6luIHBvZGVtb3MgZXNjcmliaXIgYGRwbHlyOjpmaWx0ZXIoKWAuIExvcyBkb3MgcHVudG9zIGRvYmxlcyBpbmRpY2FuIGRlIHF1w6kgcGFxdWV0ZSB2aWVuZSBsYSBmdW5jacOzbiB5IGV2aXRhbiBkdWRhcyBjdWFuZG8gZG9zIHBhcXVldGVzIHRpZW5lbiBmdW5jaW9uZXMgY29uIGVsIG1pc21vIG5vbWJyZS4KCiMgSW1wb3J0YXIgZGF0b3MKCiMjIEFudGVzIGRlIGVsZWdpciB1bmEgZnVuY2nDs24KClBhcmEgZWxlZ2lyIHVuYSBmdW5jacOzbiBkZSBpbXBvcnRhY2nDs24gZGViZW1vcyBjb25vY2VyOgoKLSBlbCBmb3JtYXRvOiBUWFQsIENTViBvIFhMU1g7Ci0gZWwgc2VwYXJhZG9yIGVudHJlIGNvbHVtbmFzOwotIGVsIHPDrW1ib2xvIGRlY2ltYWw7Ci0gbGEgY29kaWZpY2FjacOzbiBkZSBjYXJhY3RlcmVzOwotIHNpIGxhIHByaW1lcmEgZmlsYSBjb250aWVuZSBub21icmVzIGRlIHZhcmlhYmxlcy4KCkVsIFRYVCBkZSBsYSBFUEggdXNhIHB1bnRvIHkgY29tYSBjb21vIHNlcGFyYWRvciB5IGNvbWEgZGVjaW1hbC4gUG9yIGVzbyB1dGlsaXphbW9zIGByZWFkLmNzdjIoKWAuCgojIyBJbXBvcnRhciBlbCBUWFQgZGlzcG9uaWJsZQoKYGBge3IgaW1wb3J0YXItdHh0fQplcGggPC0gcmVhZC5jc3YyKAogIGZpbGUgPSBydXRhX2RhdG9zLAogIGhlYWRlciA9IFRSVUUsCiAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFLAogIGZpbGVFbmNvZGluZyA9ICJMYXRpbjEiLAogIG5hLnN0cmluZ3MgPSBjKCIiLCAiTkEiKSwKICBjaGVjay5uYW1lcyA9IEZBTFNFCikKCmRpbShlcGgpCmBgYAoKTGEgaW1wb3J0YWNpw7NuIGNvbnNlcnZhIGVsIFRYVCBvcmlnaW5hbCB5IGNyZWEgdW4gb2JqZXRvIGxsYW1hZG8gYGVwaGAgZW4gbGEgc2VzacOzbiBkZSBSLiBFbiBlc3RlIGNhc28gaGF5IGByIGZvcm1hdChucm93KGVwaCksIGJpZy5tYXJrID0gIi4iLCBkZWNpbWFsLm1hcmsgPSAiLCIpYCBmaWxhcyB5IGByIG5jb2woZXBoKWAgY29sdW1uYXMuCgojIyBTaSBlbCBhcmNoaXZvIGZ1ZXJhIENTViBvIEV4Y2VsCgpFc3RvcyBlamVtcGxvcyBxdWVkYW4gc2luIGVqZWN1dGFyIHBvcnF1ZSBsb3MgYXJjaGl2b3Mgbm8gZXN0w6FuIGVuIGxhIGNhcnBldGEuIExhIGzDs2dpY2Egc2UgbWFudGllbmU6IHJ1dGEsIGZ1bmNpw7NuIHkgb2JqZXRvIGRlIGRlc3Rpbm8uCgpgYGB7ciBpbXBvcnRhci1vdHJvcy1mb3JtYXRvcywgZXZhbD1GQUxTRX0KIyBDU1Ygc2VwYXJhZG8gcG9yIGNvbWFzCmVwaF9jc3YgPC0gcmVhZC5jc3YoCiAgImRhdG9zL2VwaF9pbmRpdmlkdWFsX1QxMjUuY3N2IiwKICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UsCiAgZmlsZUVuY29kaW5nID0gIlVURi04IgopCgojIEV4Y2VsOiByZXF1aWVyZSBlbCBwYXF1ZXRlIHJlYWR4bAplcGhfZXhjZWwgPC0gcmVhZHhsOjpyZWFkX2V4Y2VsKAogICJkYXRvcy9lcGhfaW5kaXZpZHVhbF9UMTI1Lnhsc3giLAogIHNoZWV0ID0gMQopCmBgYAoKPGRpdiBjbGFzcz0icHJhY3RpY2EiPgoqKlByw6FjdGljYSA0LioqIFNpbiBlamVjdXRhciBjw7NkaWdvLCBzZcOxYWzDoSBxdcOpIHBhcnRlIGhhYnLDrWEgcXVlIGNhbWJpYXIgcGFyYSBpbXBvcnRhciB1biBhcmNoaXZvIGxsYW1hZG8gYHBlcnNvbmFzLmNzdmAgZ3VhcmRhZG8gZW4gbGEgc3ViY2FycGV0YSBgZGF0b3NgLgo8L2Rpdj4KCjxkZXRhaWxzPgo8c3VtbWFyeT5WZXIgdW5hIHNvbHVjacOzbjwvc3VtbWFyeT4KCkxhIHJ1dGEgc2Vyw61hIGAiZGF0b3MvcGVyc29uYXMuY3N2ImAuIFRhbWJpw6luIGhhYnLDrWEgcXVlIGNvbXByb2JhciBlbCBzZXBhcmFkb3I6IHVzYXLDrWFtb3MgYHJlYWQuY3N2KClgIHNpIGVzIGNvbWEgeSBgcmVhZC5jc3YyKClgIHNpIGVzIHB1bnRvIHkgY29tYS4KPC9kZXRhaWxzPgoKIyBFeHBsb3JhciBhbnRlcyBkZSB0cmFuc2Zvcm1hcgoKQ2FkYSBmaWxhIGRlIGxhIGJhc2UgaW5kaXZpZHVhbCByZXByZXNlbnRhIHVuYSBwZXJzb25hLiBMYXMgY29sdW1uYXMgc29uIHZhcmlhYmxlcy4gQW50ZXMgZGUgbW9kaWZpY2FyIGFsZ28sIHJldmlzYW1vcyBkaW1lbnNpb25lcywgbm9tYnJlcyB5IHRpcG9zLgoKYGBge3IgZXhwbG9yYXItYmFzZX0KbnJvdyhlcGgpCm5jb2woZXBoKQpuYW1lcyhlcGgpWzE6MTVdCmBgYAoKTGFzIHZhcmlhYmxlcyBjZW50cmFsZXMgZGVsIGVqZXJjaWNpbyBzb246Cgp8IFZhcmlhYmxlIHwgU2lnbmlmaWNhZG8gcGFyYSBlc3RlIGN1cnNvIHwKfDotLXw6LS18CnwgYENIMDRgIHwgU2V4byByZWdpc3RyYWRvOiAxIHZhcsOzbiwgMiBtdWplci4gfAp8IGBDSDA2YCB8IEVkYWQgZW4gYcOxb3MgY3VtcGxpZG9zLiB8CnwgYE5JVkVMX0VEYCB8IE5pdmVsIGVkdWNhdGl2byBjb2RpZmljYWRvLiB8CnwgYEVTVEFET2AgfCAxIG9jdXBhZG8sIDIgZGVzb2N1cGFkbywgMyBpbmFjdGl2bzsgb3Ryb3MgY8OzZGlnb3MgcXVlZGFuIGZ1ZXJhIGRlbCBlamVyY2ljaW8uIHwKfCBgUE9OREVSQWAgfCBGYWN0b3IgZGUgZXhwYW5zacOzbiwgZXhjbHVpZG8gZGUgZXN0ZSBlamVyY2ljaW8gaW50cm9kdWN0b3Jpby4gfAoKYGBge3IgZXhwbG9yYXItdmFyaWFibGVzfQp2YXJpYWJsZXNfaW50ZXJlcyA8LSBjKCJDSDA0IiwgIkNIMDYiLCAiTklWRUxfRUQiLCAiRVNUQURPIiwgIlBPTkRFUkEiKQoKaGVhZChlcGhbdmFyaWFibGVzX2ludGVyZXNdKQpzdHIoZXBoW3ZhcmlhYmxlc19pbnRlcmVzXSkKc3VtbWFyeShlcGhbdmFyaWFibGVzX2ludGVyZXNdKQpgYGAKClVuYSBhbHRlcm5hdGl2YSBjb21wYWN0YSBlcyBgZ2xpbXBzZSgpYDoKCmBgYHtyIGV4cGxvcmFyLWdsaW1wc2V9CmRwbHlyOjpnbGltcHNlKGVwaFt2YXJpYWJsZXNfaW50ZXJlc10sIHdpZHRoID0gNzIpCmBgYAoKRXN0YXMgZnVuY2lvbmVzIHJlc3BvbmRlbiBwcmVndW50YXMgZGlmZXJlbnRlczoKCi0gYGRpbSgpYCwgYG5yb3coKWAgeSBgbmNvbCgpYDogwr9xdcOpIHRhbWHDsW8gdGllbmUgbGEgYmFzZT8KLSBgbmFtZXMoKWA6IMK/Y8OzbW8gc2UgbGxhbWFuIGxhcyB2YXJpYWJsZXM/Ci0gYGhlYWQoKWA6IMK/Y8OzbW8gc2UgdmVuIGFsZ3Vub3MgcmVnaXN0cm9zPwotIGBzdHIoKWAgeSBgZ2xpbXBzZSgpYDogwr9xdcOpIHRpcG8gdGllbmUgY2FkYSB2YXJpYWJsZT8KLSBgc3VtbWFyeSgpYDogwr9xdcOpIHJhbmdvcyB5IGRpc3RyaWJ1Y2lvbmVzIGLDoXNpY2FzIGFwYXJlY2VuPwoKRWwgZGlzZcOxbyBkZSByZWdpc3Ryb3MgZGVsIElOREVDIHBlcm1pdGUgaW50ZXJwcmV0YXIgZWwgc2lnbmlmaWNhZG8gZGUgY2FkYSBjw7NkaWdvIGNvbiByZXNwYWxkbyBkb2N1bWVudGFsLgoKIyBTZWxlY2Npb25hciB2YXJpYWJsZXMgeSBmaWx0cmFyIGNhc29zCgpTZWxlY2Npb25hciBjb2x1bW5hcyB5IGZpbHRyYXIgZmlsYXMgc29uIHRhcmVhcyBkaXN0aW50YXMuIFByaW1lcm8gY29uc2VydmFyZW1vcyBsYXMgY3VhdHJvIHZhcmlhYmxlcyBuZWNlc2FyaWFzLgoKIyMgUiBiYXNlCgpgYGB7ciBzZWxlY2Npb25hci1iYXNlfQpjb2x1bW5hcyA8LSBjKCJDSDA0IiwgIkNIMDYiLCAiTklWRUxfRUQiLCAiRVNUQURPIikKZGF0b3MgPC0gZXBoWywgY29sdW1uYXNdCgpuY29sKGRhdG9zKQpgYGAKCkVuIGBlcGhbZmlsYXMsIGNvbHVtbmFzXWAsIGxvIHF1ZSBlc3TDoSBhbnRlcyBkZSBsYSBjb21hIGNvcnJlc3BvbmRlIGEgZmlsYXMgeSBsbyBxdWUgZXN0w6EgZGVzcHXDqXMgY29ycmVzcG9uZGUgYSBjb2x1bW5hcy4KCkFob3JhIGNvbnN0cnVpbW9zIHVuYSBjb25kaWNpw7NuIHF1ZSBzZWEgdmVyZGFkZXJhIHNvbG8gcGFyYSBsb3MgY2Fzb3MgdsOhbGlkb3MgZGVsIGVqZXJjaWNpby4KCmBgYHtyIGZpbHRyYXItYmFzZX0KY29uZGljaW9uIDwtICFpcy5uYShkYXRvcyRDSDA2KSAmCiAgZGF0b3MkQ0gwNiA+PSAxOCAmCiAgZGF0b3MkQ0gwNiA8PSA2NSAmCiAgZGF0b3MkQ0gwNCAlaW4lIGMoMSwgMikgJgogIGRhdG9zJE5JVkVMX0VEICVpbiUgMTo3ICYKICBkYXRvcyRFU1RBRE8gJWluJSBjKDEsIDIsIDMpCgpkYXRvcyA8LSBkYXRvc1tjb25kaWNpb24sICwgZHJvcCA9IEZBTFNFXQoKbnJvdyhkYXRvcykKcmFuZ2UoZGF0b3MkQ0gwNikKYGBgCgojIyBBbHRlcm5hdGl2YSBjb24gZHBseXIKCmBgYHtyIHNlbGVjY2lvbmFyLWZpbHRyYXItZHBseXJ9CmRhdG9zX2FsdCA8LSBkcGx5cjo6c2VsZWN0KGVwaCwgQ0gwNCwgQ0gwNiwgTklWRUxfRUQsIEVTVEFETykKCmRhdG9zX2FsdCA8LSBkcGx5cjo6ZmlsdGVyKAogIGRhdG9zX2FsdCwKICAhaXMubmEoQ0gwNiksCiAgQ0gwNiA+PSAxOCwKICBDSDA2IDw9IDY1LAogIENIMDQgJWluJSBjKDEsIDIpLAogIE5JVkVMX0VEICVpbiUgMTo3LAogIEVTVEFETyAlaW4lIGMoMSwgMiwgMykKKQoKbnJvdyhkYXRvc19hbHQpCmBgYAoKQ29tcHJvYmFtb3MgcXVlIGFtYm9zIGNhbWlub3Mgc2VsZWNjaW9uYXJvbiBsb3MgbWlzbW9zIHJlZ2lzdHJvczoKCmBgYHtyIGNvbXByb2Jhci1maWx0cm99CnN0b3BpZm5vdChpZGVudGljYWwoCiAgbGFwcGx5KGRhdG9zLCB1bm5hbWUpLAogIGxhcHBseShhcy5kYXRhLmZyYW1lKGRhdG9zX2FsdCksIHVubmFtZSkKKSkKYGBgCgpTaSBgc3RvcGlmbm90KClgIG5vIG11ZXN0cmEgbmFkYSwgbGEgY29tcHJvYmFjacOzbiBmdWUgZXhpdG9zYS4gQ29tcGFyYW1vcyBlbCBjb250ZW5pZG8gZGUgbGFzIGNvbHVtbmFzIHBvcnF1ZSBSIGJhc2UgY29uc2VydmEgbG9zIG7Dum1lcm9zIGRlIGZpbGEgb3JpZ2luYWxlcyB5IGBkcGx5cmAgbG9zIHJlaW5pY2lhLiBFc2EgZGlmZXJlbmNpYSBkZSBldGlxdWV0YXMgbm8gY2FtYmlhIGxvcyBkYXRvcy4gU2kgbGEgY29uZGljacOzbiBmdWVyYSBmYWxzYSwgUiBkZXRlbmRyw61hIGxhIGVqZWN1Y2nDs24uCgo8ZGl2IGNsYXNzPSJwcmFjdGljYSI+CioqUHLDoWN0aWNhIDUuKiogQ3Jlw6EgYGRhdG9zXzI1XzYwYCBjb24gcGVyc29uYXMgZGUgMjUgYSA2MCBhw7Fvcywgc2luIG1vZGlmaWNhciBgZGF0b3NgLgo8L2Rpdj4KCjxkZXRhaWxzPgo8c3VtbWFyeT5WZXIgc29sdWNpb25lcyBlbiBSIGJhc2UgeSBkcGx5cjwvc3VtbWFyeT4KCmBgYHtyIHNvbHVjaW9uLTV9CmRhdG9zXzI1XzYwIDwtIGRhdG9zWwogIGRhdG9zJENIMDYgPj0gMjUgJiBkYXRvcyRDSDA2IDw9IDYwLAogICwKICBkcm9wID0gRkFMU0UKXQoKZGF0b3NfMjVfNjBfYWx0IDwtIGRwbHlyOjpmaWx0ZXIoZGF0b3MsIENIMDYgPj0gMjUsIENIMDYgPD0gNjApCgpucm93KGRhdG9zXzI1XzYwKQpzdG9waWZub3QobnJvdyhkYXRvc18yNV82MCkgPT0gbnJvdyhkYXRvc18yNV82MF9hbHQpKQpgYGAKPC9kZXRhaWxzPgoKIyBSZWNvZGlmaWNhciB5IGNyZWFyIHZhcmlhYmxlcwoKUmVjb2RpZmljYXIgc2lnbmlmaWNhIGNvbnN0cnVpciB1bmEgcmVwcmVzZW50YWNpw7NuIG3DoXMgw7p0aWwgYSBwYXJ0aXIgZGUgY8OzZGlnb3MgZXhpc3RlbnRlcy4gQ29uc2VydmFyZW1vcyBsYXMgY29sdW1uYXMgb3JpZ2luYWxlcyBwYXJhIHBvZGVyIHJldmlzYXIgbnVlc3RyYXMgZGVjaXNpb25lcy4KCiMjIFNleG8KCmBgYHtyIHJlY29kaWZpY2FyLXNleG99CmRhdG9zJHNleG8gPC0gZmFjdG9yKAogIGRhdG9zJENIMDQsCiAgbGV2ZWxzID0gYygxLCAyKSwKICBsYWJlbHMgPSBjKCJWYXJvbmVzIiwgIk11amVyZXMiKQopCmBgYAoKIyMgRWR1Y2FjacOzbgoKUGFyYSBlc3RlIGVqZXJjaWNpbyBhZ3J1cGFtb3MgbG9zIGPDs2RpZ29zIDQsIDUgeSA2IGNvbW8g4oCcU2VjdW5kYXJpYSBjb21wbGV0YSBvIG3DoXPigJ0uIExvcyBjw7NkaWdvcyAxLCAyLCAzIHkgNyBmb3JtYW4g4oCcSGFzdGEgc2VjdW5kYXJpYSBpbmNvbXBsZXRh4oCdLiBFcyB1bmEgYWdydXBhY2nDs24gZGlkw6FjdGljYSwgbm8gdW5hIG51ZXZhIGNhdGVnb3LDrWEgb2ZpY2lhbCBkZWwgSU5ERUMuCgpgYGB7ciByZWNvZGlmaWNhci1lZHVjYWNpb259CmRhdG9zJGVkdWNhY2lvbiA8LSBpZmVsc2UoCiAgZGF0b3MkTklWRUxfRUQgJWluJSBjKDQsIDUsIDYpLAogICJTZWN1bmRhcmlhIGNvbXBsZXRhIG8gbcOhcyIsCiAgIkhhc3RhIHNlY3VuZGFyaWEgaW5jb21wbGV0YSIKKQoKbml2ZWxlc19lZHVjYWNpb24gPC0gYygKICAiSGFzdGEgc2VjdW5kYXJpYSBpbmNvbXBsZXRhIiwKICAiU2VjdW5kYXJpYSBjb21wbGV0YSBvIG3DoXMiCikKCmRhdG9zJGVkdWNhY2lvbiA8LSBmYWN0b3IoCiAgZGF0b3MkZWR1Y2FjaW9uLAogIGxldmVscyA9IG5pdmVsZXNfZWR1Y2FjaW9uCikKYGBgCgojIyBBY3RpdmlkYWQKClVuYSBwZXJzb25hIGVjb27Ds21pY2FtZW50ZSBhY3RpdmEgcHVlZGUgZXN0YXIgb2N1cGFkYSBvIGRlc29jdXBhZGEuIFBvciBlc28gYGFjdGl2b2AgdmFsZSAxIGN1YW5kbyBgRVNUQURPYCBlcyAxIG8gMiwgeSAwIGN1YW5kbyBlcyAzLgoKYGBge3IgY3JlYXItYWN0aXZvfQpkYXRvcyRhY3Rpdm8gPC0gaWZlbHNlKGRhdG9zJEVTVEFETyAlaW4lIGMoMSwgMiksIDEsIDApCgp0YWJsZShkYXRvcyRzZXhvLCB1c2VOQSA9ICJpZmFueSIpCnRhYmxlKGRhdG9zJGVkdWNhY2lvbiwgdXNlTkEgPSAiaWZhbnkiKQp0YWJsZShkYXRvcyRhY3Rpdm8sIHVzZU5BID0gImlmYW55IikKYGBgCgojIyBBbHRlcm5hdGl2YSBjb24gZHBseXIKCmBgYHtyIHJlY29kaWZpY2FyLWRwbHlyfQpkYXRvc19hbHQgPC0gZHBseXI6Om11dGF0ZSgKICBkYXRvc19hbHQsCiAgc2V4byA9IGZhY3RvcigKICAgIENIMDQsCiAgICBsZXZlbHMgPSBjKDEsIDIpLAogICAgbGFiZWxzID0gYygiVmFyb25lcyIsICJNdWplcmVzIikKICApLAogIGVkdWNhY2lvbiA9IGRwbHlyOjppZl9lbHNlKAogICAgTklWRUxfRUQgJWluJSBjKDQsIDUsIDYpLAogICAgIlNlY3VuZGFyaWEgY29tcGxldGEgbyBtw6FzIiwKICAgICJIYXN0YSBzZWN1bmRhcmlhIGluY29tcGxldGEiCiAgKSwKICBlZHVjYWNpb24gPSBmYWN0b3IoZWR1Y2FjaW9uLCBsZXZlbHMgPSBuaXZlbGVzX2VkdWNhY2lvbiksCiAgYWN0aXZvID0gZHBseXI6OmlmX2Vsc2UoRVNUQURPICVpbiUgYygxLCAyKSwgMSwgMCkKKQoKc3RvcGlmbm90KAogIGlkZW50aWNhbChkYXRvcyRzZXhvLCBkYXRvc19hbHQkc2V4byksCiAgaWRlbnRpY2FsKGRhdG9zJGVkdWNhY2lvbiwgZGF0b3NfYWx0JGVkdWNhY2lvbiksCiAgaWRlbnRpY2FsKGRhdG9zJGFjdGl2bywgZGF0b3NfYWx0JGFjdGl2bykKKQpgYGAKCjxkaXYgY2xhc3M9InByYWN0aWNhIj4KKipQcsOhY3RpY2EgNi4qKiBDcmXDoSB1bmEgdmFyaWFibGUgYG9jdXBhZG9gIHF1ZSB2YWxnYSAxIGN1YW5kbyBgRVNUQURPID09IDFgIHkgMCBjdWFuZG8gYEVTVEFET2Agc2VhIDIgbyAzLiDCv1BvciBxdcOpIG5vIGVzIGlndWFsIGEgYGFjdGl2b2A/CjwvZGl2PgoKPGRldGFpbHM+CjxzdW1tYXJ5PlZlciB1bmEgc29sdWNpw7NuPC9zdW1tYXJ5PgoKYGBge3Igc29sdWNpb24tNn0KZGF0b3Mkb2N1cGFkbyA8LSBpZmVsc2UoZGF0b3MkRVNUQURPID09IDEsIDEsIDApCnRhYmxlKGRhdG9zJG9jdXBhZG8pCmBgYAoKYGFjdGl2b2AgaW5jbHV5ZSBwZXJzb25hcyBvY3VwYWRhcyB5IGRlc29jdXBhZGFzOyBgb2N1cGFkb2AgaW5jbHV5ZSBzb2xhbWVudGUgYSBxdWllbmVzIHRpZW5lbiBjw7NkaWdvIDEuCjwvZGV0YWlscz4KCiMgRnJlY3VlbmNpYXMgeSBwb3JjZW50YWplcwoKIyMgQ29udGFyIGNhc29zCgpgYGB7ciBmcmVjdWVuY2lhcy1iYXNlfQpmcmVjdWVuY2lhc19zZXhvIDwtIHRhYmxlKGRhdG9zJHNleG8pCmZyZWN1ZW5jaWFzX3NleG8KYGBgCgpgYGB7ciBmcmVjdWVuY2lhcy1qYW5pdG9yfQpqYW5pdG9yOjp0YWJ5bChkYXRvcywgc2V4bykKYGBgCgpVbmEgZnJlY3VlbmNpYSBhYnNvbHV0YSBlcyB1biBjb250ZW8uIFVuYSBwcm9wb3JjacOzbiBkaXZpZGUgY2FkYSBjb250ZW8gcG9yIGVsIHRvdGFsIHkgdW4gcG9yY2VudGFqZSBtdWx0aXBsaWNhIGVzYSBwcm9wb3JjacOzbiBwb3IgMTAwLgoKYGBge3IgcG9yY2VudGFqZXMtc2V4b30Kcm91bmQoMTAwICogcHJvcC50YWJsZShmcmVjdWVuY2lhc19zZXhvKSwgMSkKYGBgCgojIyBQb3JjZW50YWplIGRlIHBlcnNvbmFzIGFjdGl2YXMKCkN1YW5kbyB1bmEgdmFyaWFibGUgc29sbyBjb250aWVuZSAwIHkgMSwgc3UgbWVkaWEgZXMgbGEgcHJvcG9yY2nDs24gZGUgdmFsb3JlcyBpZ3VhbGVzIGEgMS4KCmBgYHtyIHBvcmNlbnRhamUtYWN0aXZvc30KcG9yY2VudGFqZV9hY3Rpdm9zIDwtIHJvdW5kKDEwMCAqIG1lYW4oZGF0b3MkYWN0aXZvLCBuYS5ybSA9IFRSVUUpLCAxKQpwb3JjZW50YWplX2FjdGl2b3MKYGBgCgpFbCBjw6FsY3VsbyBmdW5jaW9uYSBwb3JxdWUgYGFjdGl2b2AgZnVlIGRlZmluaWRhIGV4cHJlc2FtZW50ZSBjb21vIHVuYSB2YXJpYWJsZSBiaW5hcmlhOiBzdSBtZWRpYSBlcyBsYSBwcm9wb3JjacOzbiBkZSB2YWxvcmVzIGlndWFsZXMgYSAxLiBNdWx0aXBsaWNhciBwb3IgMTAwIGNvbnZpZXJ0ZSBlc2EgcHJvcG9yY2nDs24gZW4gcG9yY2VudGFqZS4KCiMjIENvbXBhcmFyIHNleG8geSBlZHVjYWNpw7NuIGNvbiBSIGJhc2UKCmBgYHtyIHRhYmxhLWdydXBvcy1iYXNlfQp0YWJsYV9iYXNlIDwtIGFnZ3JlZ2F0ZSgKICBhY3Rpdm8gfiBzZXhvICsgZWR1Y2FjaW9uLAogIGRhdGEgPSBkYXRvcywKICBGVU4gPSBtZWFuCikKCmNhc29zX2Jhc2UgPC0gYWdncmVnYXRlKAogIGFjdGl2byB+IHNleG8gKyBlZHVjYWNpb24sCiAgZGF0YSA9IGRhdG9zLAogIEZVTiA9IGxlbmd0aAopCgp0YWJsYV9iYXNlJHBvcmNlbnRhamUgPC0gcm91bmQoMTAwICogdGFibGFfYmFzZSRhY3Rpdm8sIDEpCnRhYmxhX2Jhc2UkY2Fzb3MgPC0gY2Fzb3NfYmFzZSRhY3Rpdm8KdGFibGFfYmFzZSA8LSB0YWJsYV9iYXNlWywgYygic2V4byIsICJlZHVjYWNpb24iLCAiY2Fzb3MiLCAicG9yY2VudGFqZSIpXQoKdGFibGFfYmFzZQpgYGAKCiMjIExhIG1pc21hIG9wZXJhY2nDs24gY29uIGRwbHlyCgpgYGB7ciB0YWJsYS1ncnVwb3MtZHBseXJ9CmdydXBvcyA8LSBkcGx5cjo6Z3JvdXBfYnkoZGF0b3MsIHNleG8sIGVkdWNhY2lvbikKCnRhYmxhX2FsdCA8LSBkcGx5cjo6c3VtbWFyaXNlKAogIGdydXBvcywKICBjYXNvcyA9IGRwbHlyOjpuKCksCiAgcG9yY2VudGFqZSA9IHJvdW5kKDEwMCAqIG1lYW4oYWN0aXZvKSwgMSksCiAgLmdyb3VwcyA9ICJkcm9wIgopCgp0YWJsYV9hbHQKYGBgCgpWZXJpZmljYW1vcyBxdWUgbG9zIHJlc3VsdGFkb3MgY29pbmNpZGFuIGRlc3B1w6lzIGRlIG9yZGVuYXJsb3M6CgpgYGB7ciBjb21wcm9iYXItdGFibGFzfQp0YWJsYV9iYXNlX29yZGVuYWRhIDwtIHRhYmxhX2Jhc2Vbb3JkZXIodGFibGFfYmFzZSRzZXhvLCB0YWJsYV9iYXNlJGVkdWNhY2lvbiksIF0KdGFibGFfYWx0X29yZGVuYWRhIDwtIGFzLmRhdGEuZnJhbWUoCiAgZHBseXI6OmFycmFuZ2UodGFibGFfYWx0LCBzZXhvLCBlZHVjYWNpb24pCikKCnN0b3BpZm5vdCgKICBpZGVudGljYWwodGFibGFfYmFzZV9vcmRlbmFkYSRjYXNvcywgdGFibGFfYWx0X29yZGVuYWRhJGNhc29zKSwKICBpZGVudGljYWwodGFibGFfYmFzZV9vcmRlbmFkYSRwb3JjZW50YWplLCB0YWJsYV9hbHRfb3JkZW5hZGEkcG9yY2VudGFqZSksCiAgYWxsKHRhYmxhX2Jhc2Vfb3JkZW5hZGEkcG9yY2VudGFqZSA+PSAwKSwKICBhbGwodGFibGFfYmFzZV9vcmRlbmFkYSRwb3JjZW50YWplIDw9IDEwMCksCiAgbnJvdyh0YWJsYV9iYXNlX29yZGVuYWRhKSA9PSA0CikKCmtuaXRyOjprYWJsZSgKICB0YWJsYV9hbHRfb3JkZW5hZGEsCiAgY29sLm5hbWVzID0gYygiU2V4byIsICJFZHVjYWNpw7NuIiwgIkNhc29zIiwgIlBlcnNvbmFzIGFjdGl2YXMgKCUpIiksCiAgY2FwdGlvbiA9ICJBY3RpdmlkYWQgcG9yIHNleG8geSBlZHVjYWNpw7NuIGVuIGxhIG11ZXN0cmEgc2VsZWNjaW9uYWRhIgopCmBgYAoKRWwgZGVub21pbmFkb3IgY2FtYmlhIGVudHJlIGZpbGFzOiBjYWRhIHBvcmNlbnRhamUgc2UgY2FsY3VsYSBkZW50cm8gZGUgc3UgY29tYmluYWNpw7NuIGRlIHNleG8geSBlZHVjYWNpw7NuLgoKPGRpdiBjbGFzcz0icHJhY3RpY2EiPgoqKlByw6FjdGljYSA3LioqIENhbGN1bMOhIGVsIHBvcmNlbnRhamUgZGUgcGVyc29uYXMgYWN0aXZhcyBwb3Igc2V4bywgcHJpbWVybyBjb24gUiBiYXNlIHkgbHVlZ28gY29uIGBkcGx5cmAuIENvbXByb2LDoSBxdWUgY29pbmNpZGFuLgo8L2Rpdj4KCjxkZXRhaWxzPgo8c3VtbWFyeT5WZXIgdW5hIHNvbHVjacOzbjwvc3VtbWFyeT4KCmBgYHtyIHNvbHVjaW9uLTd9CmFjdGl2aWRhZF9zZXhvX2Jhc2UgPC0gYWdncmVnYXRlKGFjdGl2byB+IHNleG8sIGRhdGEgPSBkYXRvcywgRlVOID0gbWVhbikKYWN0aXZpZGFkX3NleG9fYmFzZSRwb3JjZW50YWplIDwtIHJvdW5kKDEwMCAqIGFjdGl2aWRhZF9zZXhvX2Jhc2UkYWN0aXZvLCAxKQoKYWN0aXZpZGFkX3NleG9fYWx0IDwtIGRwbHlyOjpzdW1tYXJpc2UoCiAgZHBseXI6Omdyb3VwX2J5KGRhdG9zLCBzZXhvKSwKICBwb3JjZW50YWplID0gcm91bmQoMTAwICogbWVhbihhY3Rpdm8pLCAxKSwKICAuZ3JvdXBzID0gImRyb3AiCikKCmFjdGl2aWRhZF9zZXhvX2Jhc2VbLCBjKCJzZXhvIiwgInBvcmNlbnRhamUiKV0KYWN0aXZpZGFkX3NleG9fYWx0CnN0b3BpZm5vdChpZGVudGljYWwoCiAgYWN0aXZpZGFkX3NleG9fYmFzZSRwb3JjZW50YWplLAogIGFjdGl2aWRhZF9zZXhvX2FsdCRwb3JjZW50YWplCikpCmBgYAo8L2RldGFpbHM+CgojIENvbnN0cnVpciBncsOhZmljb3MgbGVnaWJsZXMKClVuIGdyw6FmaWNvIGxlZ2libGUgcGVybWl0ZSByZXNwb25kZXIgZXN0YXMgcHJlZ3VudGFzIGNvbiByYXBpZGV6OgoKLSDCv1F1w6kgc2UgZXN0w6EgbWlkaWVuZG8/Ci0gwr9RdcOpIGdydXBvcyBzZSBjb21wYXJhbj8KLSDCv0N1w6FsIGVzIGxhIHVuaWRhZCBkZWwgZWplIHZlcnRpY2FsPwotIMK/UXXDqSBwZXLDrW9kbyB5IHVuaXZlcnNvIGFwYXJlY2VuPwotIMK/Q3XDoWwgZXMgbGEgZnVlbnRlPwotIMK/UXXDqSBsaW1pdGFjacOzbiBpbXBvcnRhbnRlIGRlYmVtb3MgcmVjb3JkYXI/CgojIyBHcsOhZmljbyBjb24gUiBiYXNlCgpQYXJhIGBiYXJwbG90KClgIGNvbnZlcnRpbW9zIGxhIHRhYmxhIGEgdW5hIG1hdHJpei4gVXNhbW9zIGRvcyBjb2xvcmVzIGRpc3Rpbmd1aWJsZXMsIHVuYSBlc2NhbGEgZGUgMCBhIDEwMCwgZXRpcXVldGFzIGRpcmVjdGFzIHkgdW5hIGxleWVuZGEgZnVlcmEgZGUgbGFzIGJhcnJhcy4KCmBgYHtyIGdyYWZpY28tYmFzZSwgZmlnLmNhcD0iUG9yY2VudGFqZSBkZSBwZXJzb25hcyBhY3RpdmFzIHBvciBzZXhvIHkgZWR1Y2FjacOzbi4gUmVzdWx0YWRvcyBzaW4gcG9uZGVyYXIuIn0KbWF0cml6IDwtIHh0YWJzKHBvcmNlbnRhamUgfiBlZHVjYWNpb24gKyBzZXhvLCBkYXRhID0gdGFibGFfYmFzZSkKCm9wIDwtIHBhcihtYXIgPSBjKDUsIDUsIDQsIDEpLCBsYXMgPSAxKQoKcG9zaWNpb25lcyA8LSBiYXJwbG90KAogIG1hdHJpeiwKICBiZXNpZGUgPSBUUlVFLAogIHlsaW0gPSBjKDAsIDEwMCksCiAgY29sID0gYygiIzAwNzJCMiIsICIjRDU1RTAwIiksCiAgYm9yZGVyID0gTkEsCiAgbWFpbiA9ICJBY3RpdmlkYWQgc2Vnw7puIHNleG8geSBlZHVjYWNpw7NuIiwKICBzdWIgPSAiUGVyc29uYXMgZGUgMTggYSA2NSBhw7FvczsgcG9yY2VudGFqZXMgc2luIHBvbmRlcmFyIiwKICB4bGFiID0gIlNleG8iLAogIHlsYWIgPSAiUGVyc29uYXMgYWN0aXZhcyBlbiBsYSBtdWVzdHJhICglKSIsCiAgbGVnZW5kLnRleHQgPSByb3duYW1lcyhtYXRyaXopLAogIGFyZ3MubGVnZW5kID0gbGlzdCgKICAgIHggPSAidG9wbGVmdCIsCiAgICBidHkgPSAibiIsCiAgICBjZXggPSAwLjg1LAogICAgaW5zZXQgPSBjKDAuMDEsIDAuMDEpCiAgKQopCgp0ZXh0KAogIHggPSBwb3NpY2lvbmVzLAogIHkgPSBtYXRyaXosCiAgbGFiZWxzID0gcGFzdGUwKGZvcm1hdChtYXRyaXosIGRlY2ltYWwubWFyayA9ICIsIiwgbnNtYWxsID0gMSksICIlIiksCiAgcG9zID0gMywKICBjZXggPSAwLjg1CikKCnBhcihvcCkKYGBgCgojIyBHcsOhZmljbyBjb24gZ2dwbG90MgoKYGdncGxvdDJgIG9yZ2FuaXphIGxhIGNvbnN0cnVjY2nDs24gZW4gY2FwYXMuIENhZGEgY2FwYSBjdW1wbGUgdW5hIGZ1bmNpw7NuIGVzcGVjw61maWNhIHkgc2Ugc3VtYSBjb24gYCtgOgoKfCBDb21wb25lbnRlIHwgUHJlZ3VudGEgcXVlIHJlc3BvbmRlIHwgRWplbXBsbyB8Cnw6LS18Oi0tfDotLXwKfCBEYXRvcyB8IMK/UXXDqSB0YWJsYSB1c2Ftb3M/IHwgYHRhYmxhX2FsdGAgfAp8IE1hcGVvcyBlc3TDqXRpY29zIHwgwr9RdcOpIHZhcmlhYmxlcyBvY3VwYW4gY2FkYSBjYW5hbCB2aXN1YWw/IHwgYGFlcyh4ID0gc2V4bywgeSA9IHBvcmNlbnRhamUsIGZpbGwgPSBlZHVjYWNpb24pYCB8CnwgR2VvbWV0csOtYSB8IMK/Q8OzbW8gcmVwcmVzZW50YW1vcyBsYXMgb2JzZXJ2YWNpb25lcz8gfCBgZ2VvbV9jb2woKWAgfAp8IEVzY2FsYSB8IMK/Q8OzbW8gc2UgdHJhZHVjZW4gdmFsb3JlcyBhIHBvc2ljaW9uZXMsIGNvbG9yZXMgbyBldGlxdWV0YXM/IHwgYHNjYWxlX3lfY29udGludW91cygpYCB8CnwgRXRpcXVldGFzIHwgwr9RdcOpIGNvbnRleHRvIG5lY2VzaXRhIHF1aWVuIGxlZT8gfCBgbGFicygpYCB8CnwgVGVtYSB8IMK/Q8OzbW8gc2UgcHJlc2VudGFuIGxvcyBlbGVtZW50b3MgcXVlIHJvZGVhbiBhIGxvcyBkYXRvcz8gfCBgdGhlbWVfbWluaW1hbCgpYCB5IGB0aGVtZSgpYCB8CgpEZW50cm8gZGUgYGFlcygpYCBlc2NyaWJpbW9zIHJlbGFjaW9uZXMgY29uIHZhcmlhYmxlcyBkZSBsYSB0YWJsYS4gRnVlcmEgZGUgYGFlcygpYCBmaWphbW9zIHVuYSBwcm9waWVkYWQgcGFyYSB0b2RvcyBsb3MgZWxlbWVudG9zLiBQb3IgZWplbXBsbywgYGFlcyhmaWxsID0gZWR1Y2FjaW9uKWAgYXNpZ25hIHVuIGNvbG9yIGEgY2FkYSBuaXZlbCBlZHVjYXRpdm8sIG1pZW50cmFzIHF1ZSBgY29sb3VyID0gIndoaXRlImAgYXBsaWNhcsOtYSBlbCBtaXNtbyBib3JkZSBibGFuY28gYSB0b2RhcyBsYXMgYmFycmFzLgoKRWwgc2lndWllbnRlIGdyw6FmaWNvIHNlIHB1ZWRlIGxlZXIgZGUgYXJyaWJhIGhhY2lhIGFiYWpvOiBkYXRvcyB5IG1hcGVvcyBnZW5lcmFsZXMsIGdlb21ldHLDrWFzLCBlc2NhbGEsIHRleHRvcyB5IGFzcGVjdG8gdmlzdWFsLgoKYGBge3IgZ3JhZmljby1nZ3Bsb3QsIGZpZy5jYXA9IlBvcmNlbnRhamUgZGUgcGVyc29uYXMgYWN0aXZhcyBwb3Igc2V4byB5IGVkdWNhY2nDs24uIEZ1ZW50ZTogSU5ERUMsIEVQSCAxVDIwMjUuIn0KZ3JhZmljbyA8LSBnZ3Bsb3QoCiAgdGFibGFfYWx0LAogIGFlcyh4ID0gc2V4bywgeSA9IHBvcmNlbnRhamUsIGZpbGwgPSBlZHVjYWNpb24pCikgKwogIGdlb21fY29sKAogICAgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aCA9IDAuODIpLAogICAgd2lkdGggPSAwLjcyCiAgKSArCiAgZ2VvbV90ZXh0KAogICAgYWVzKGxhYmVsID0gcGFzdGUwKGZvcm1hdChwb3JjZW50YWplLCBkZWNpbWFsLm1hcmsgPSAiLCIsIG5zbWFsbCA9IDEpLCAiJSIpKSwKICAgIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2Uod2lkdGggPSAwLjgyKSwKICAgIHZqdXN0ID0gLTAuNDUsCiAgICBzaXplID0gNAogICkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIiMwMDcyQjIiLCAiI0Q1NUUwMCIpKSArCiAgc2NhbGVfeV9jb250aW51b3VzKAogICAgbGltaXRzID0gYygwLCAxMDApLAogICAgYnJlYWtzID0gc2VxKDAsIDEwMCwgMjApLAogICAgbGFiZWxzID0gZnVuY3Rpb24oeCkgcGFzdGUwKHgsICIlIiksCiAgICBleHBhbmQgPSBleHBhbnNpb24obXVsdCA9IGMoMCwgMC4wNikpCiAgKSArCiAgbGFicygKICAgIHRpdGxlID0gIkFjdGl2aWRhZCBzZWfDum4gc2V4byB5IGVkdWNhY2nDs24iLAogICAgc3VidGl0bGUgPSAiUGVyc29uYXMgZGUgMTggYSA2NSBhw7FvczsgcG9yY2VudGFqZXMgc2luIHBvbmRlcmFyIiwKICAgIHggPSBOVUxMLAogICAgeSA9ICJQZXJzb25hcyBhY3RpdmFzIGVuIGxhIG11ZXN0cmEiLAogICAgZmlsbCA9ICJFZHVjYWNpw7NuIiwKICAgIGNhcHRpb24gPSBwYXN0ZSgKICAgICAgIkZ1ZW50ZTogSU5ERUMsIEVQSCwgcHJpbWVyIHRyaW1lc3RyZSBkZSAyMDI1LiIsCiAgICAgICJDw6FsY3Vsb3Mgc29icmUgbGEgbXVlc3RyYTsgbm8gc29uIHRhc2FzIG9maWNpYWxlcy4iCiAgICApCiAgKSArCiAgdGhlbWVfbWluaW1hbChiYXNlX3NpemUgPSAxMykgKwogIHRoZW1lKAogICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiksCiAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgcGFuZWwuZ3JpZC5tYWpvci54ID0gZWxlbWVudF9ibGFuaygpLAogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiLCBzaXplID0gMTYpLAogICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICIjNDQ0NDQ0IiksCiAgICBwbG90LmNhcHRpb24gPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLCBjb2xvciA9ICIjNTU1NTU1IiksCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiKQogICkKCmdyYWZpY28KYGBgCgpMYSB0YWJsYSB5YSBjb250aWVuZSBsb3MgcG9yY2VudGFqZXMgY2FsY3VsYWRvcywgcG9yIGVzbyB1c2Ftb3MgYGdlb21fY29sKClgLiBgZ2VvbV9iYXIoKWAgc2UgcmVzZXJ2YSBoYWJpdHVhbG1lbnRlIHBhcmEgY29udGFyIG9ic2VydmFjaW9uZXMuIGBwb3NpdGlvbl9kb2RnZSgpYCBjb2xvY2EgbG9zIGdydXBvcyBsYWRvIGEgbGFkbyB5IHVzYSBlbCBtaXNtbyBhbmNobyBlbiBsYXMgYmFycmFzIHkgc3VzIGV0aXF1ZXRhcyBwYXJhIG1hbnRlbmVybGFzIGFsaW5lYWRhcy4KCiMjIyBVbmEgYWx0ZXJuYXRpdmEgY29udGVtcG9yw6FuZWE6IGdyw6FmaWNvIGRlIHB1bnRvcwoKTGFzIGJhcnJhcyBmdW5jaW9uYW4gYmllbiBwYXJhIGludHJvZHVjaXIgY29tcGFyYWNpb25lcyBkZXNkZSBjZXJvLiBVbiBncsOhZmljbyBkZSBwdW50b3Mgb2N1cGEgbWVub3MgdGludGEgeSBkZWphIGxvcyB2YWxvcmVzIHNvYnJlIHVuYSBlc2NhbGEgY29tw7puLCB1bmEgb3BjacOzbiDDunRpbCBjdWFuZG8gZWwgaW50ZXLDqXMgcHJpbmNpcGFsIGVzdMOhIGVuIGNvbXBhcmFyIHBvcmNlbnRhamVzLgoKYGBge3IgZ3JhZmljby1wdW50b3MsIGZpZy5jYXA9IlBvcmNlbnRhamUgZGUgcGVyc29uYXMgYWN0aXZhcyBwb3Igc2V4byB5IGVkdWNhY2nDs24uIEFsdGVybmF0aXZhIGNvbiBwdW50b3M7IHJlc3VsdGFkb3Mgc2luIHBvbmRlcmFyLiJ9CmdyYWZpY29fcHVudG9zIDwtIGdncGxvdCgKICB0YWJsYV9hbHQsCiAgYWVzKHggPSBwb3JjZW50YWplLCB5ID0gZWR1Y2FjaW9uLCBjb2xvdXIgPSBzZXhvKQopICsKICBnZW9tX2xpbmUoCiAgICBhZXMoZ3JvdXAgPSBlZHVjYWNpb24pLAogICAgY29sb3VyID0gIiNEOUQ5RDkiLAogICAgbGluZXdpZHRoID0gMS4yCiAgKSArCiAgZ2VvbV9wb2ludChzaXplID0gNCkgKwogIGdlb21fdGV4dCgKICAgIGFlcyhsYWJlbCA9IHBhc3RlMChmb3JtYXQocG9yY2VudGFqZSwgZGVjaW1hbC5tYXJrID0gIiwiLCBuc21hbGwgPSAxKSwgIiUiKSksCiAgICBoanVzdCA9IC0wLjI1LAogICAgc2hvdy5sZWdlbmQgPSBGQUxTRQogICkgKwogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gYygiVmFyb25lcyIgPSAiIzAwNzJCMiIsICJNdWplcmVzIiA9ICIjRDU1RTAwIikpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoCiAgICBsaW1pdHMgPSBjKDAsIDEwMCksCiAgICBicmVha3MgPSBzZXEoMCwgMTAwLCAyMCksCiAgICBsYWJlbHMgPSBmdW5jdGlvbih4KSBwYXN0ZTAoeCwgIiUiKSwKICAgIGV4cGFuZCA9IGV4cGFuc2lvbihtdWx0ID0gYygwLCAwLjA1KSkKICApICsKICBsYWJzKAogICAgdGl0bGUgPSAiTGEgYWN0aXZpZGFkIGVzIG1heW9yIGVudHJlIGxvcyB2YXJvbmVzIGVuIGFtYm9zIGdydXBvcyBlZHVjYXRpdm9zIiwKICAgIHN1YnRpdGxlID0gIlBlcnNvbmFzIGRlIDE4IGEgNjUgYcOxb3M7IHBvcmNlbnRhamVzIHNpbiBwb25kZXJhciIsCiAgICB4ID0gIlBlcnNvbmFzIGFjdGl2YXMgZW4gbGEgbXVlc3RyYSIsCiAgICB5ID0gTlVMTCwKICAgIGNvbG91ciA9ICJTZXhvIiwKICAgIGNhcHRpb24gPSAiRnVlbnRlOiBJTkRFQywgRVBILCBwcmltZXIgdHJpbWVzdHJlIGRlIDIwMjUuIEPDoWxjdWxvcyBzb2JyZSBsYSBtdWVzdHJhLiIKICApICsKICB0aGVtZV9taW5pbWFsKGJhc2Vfc2l6ZSA9IDEzKSArCiAgdGhlbWUoCiAgICBsZWdlbmQucG9zaXRpb24gPSAidG9wIiwKICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiKSwKICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ncmlkLm1ham9yLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIsIHNpemUgPSAxNSksCiAgICBwbG90LmNhcHRpb24gPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLCBjb2xvdXIgPSAiIzU1NTU1NSIpCiAgKQoKZ3JhZmljb19wdW50b3MKYGBgCgpFbCB0w610dWxvIGNvbXVuaWNhIGVsIGhhbGxhemdvIHByaW5jaXBhbCB5IGVsIHN1YnTDrXR1bG8gZGVsaW1pdGEgZWwgdW5pdmVyc28uIEVzdGEgamVyYXJxdcOtYSBvcmllbnRhIGxhIGxlY3R1cmEsIG1pZW50cmFzIHF1ZSBlbCBlcMOtZ3JhZmUgY29uc2VydmEgbGEgZnVlbnRlIHkgZWwgYWxjYW5jZSBkZWwgY8OhbGN1bG8uIEVsZWdpciBlbnRyZSBiYXJyYXMgeSBwdW50b3MgZGVwZW5kZSBkZSBsYSBwcmVndW50YSB5IGRlbCBlc3BhY2lvIGRpc3BvbmlibGU7IGNvbXBhcmFyIGFtYmFzIHZlcnNpb25lcyBheXVkYSBhIGV2YWx1YXIgY3XDoWwgaGFjZSBtw6FzIGV2aWRlbnRlIGVsIHBhdHLDs24uCgojIyBHdWFyZGFyIGVsIGdyw6FmaWNvCgpgYGB7ciBndWFyZGFyLWdyYWZpY28sIGV2YWw9RkFMU0V9Cmdnc2F2ZSgKICBmaWxlbmFtZSA9ICJncmFmaWNvX2FjdGl2aWRhZF9FUEhfMVQyMDI1LnBuZyIsCiAgcGxvdCA9IGdyYWZpY28sCiAgd2lkdGggPSAxMCwKICBoZWlnaHQgPSA2LAogIHVuaXRzID0gImluIiwKICBkcGkgPSAxODAsCiAgYmcgPSAid2hpdGUiCikKYGBgCgpMYSBleHRlbnNpw7NuIGRlbCBhcmNoaXZvIGRlZmluZSBlbCBmb3JtYXRvLiBgd2lkdGhgIHkgYGhlaWdodGAgY29udHJvbGFuIGVsIHRhbWHDsW8gZsOtc2ljbzsgYGRwaWAgY29udHJvbGEgbGEgcmVzb2x1Y2nDs24gZGUgdW5hIGltYWdlbiByYXN0ZXJpemFkYSBjb21vIFBORy4KCiMjIEludGVycHJldGFyIGNvbiBjYXV0ZWxhCgpVbmEgbGVjdHVyYSBkZXNjcmlwdGl2YSBwb2Ryw61hIGRlY2lyOgoKPiBFbiBlc3RhIG11ZXN0cmEgZGUgcGVyc29uYXMgZGUgMTggYSA2NSBhw7FvcywgZWwgcG9yY2VudGFqZSBkZSBhY3RpdmlkYWQgZXMgbWF5b3IgZW50cmUgbG9zIHZhcm9uZXMgcXVlIGVudHJlIGxhcyBtdWplcmVzIGVuIGxvcyBkb3MgZ3J1cG9zIGVkdWNhdGl2b3MuIExhIGRpZmVyZW5jaWEgbcOhcyBhbXBsaWEgc2Ugb2JzZXJ2YSBlbiBlbCBncnVwbyBkZSBoYXN0YSBzZWN1bmRhcmlhIGluY29tcGxldGEuCgpFbCBncsOhZmljbyBtdWVzdHJhIGFzb2NpYWNpb25lcyBkZXNjcmlwdGl2YXMgZW50cmUgbGFzIHZhcmlhYmxlcy4gVW5hIGFmaXJtYWNpw7NuIGNhdXNhbCByZXF1ZXJpcsOtYSBvdHJvIGRpc2XDsW8gZGUgYW7DoWxpc2lzLiBBZGVtw6FzLCBsb3MgcG9yY2VudGFqZXMgY2FyYWN0ZXJpemFuIGxhIG11ZXN0cmEgc2VsZWNjaW9uYWRhOiBlbCBjw6FsY3VsbyB1c2EgdW4gdW5pdmVyc28gZGUgZWRhZCBwYXJ0aWN1bGFyIHkgcHJlc2NpbmRlIGRlIHBvbmRlcmFkb3Jlcy4KCjxkaXYgY2xhc3M9InByYWN0aWNhIj4KKipQcsOhY3RpY2EgOC4qKiBDYW1iacOhIGVsIHTDrXR1bG8gZGVsIGdyw6FmaWNvIHBhcmEgcXVlIG1lbmNpb25lIGVsIGluZGljYWRvciwgZWwgcGVyw61vZG8geSBlbCBncnVwbyBkZSBlZGFkLiBEZXNwdcOpcyBlc2NyaWLDrSB1bmEgb3JhY2nDs24gZGVzY3JpcHRpdmEgeSB1bmEgbGltaXRhY2nDs24uCjwvZGl2PgoKPGRldGFpbHM+CjxzdW1tYXJ5PlZlciB1bmEgcmVzcHVlc3RhIHBvc2libGU8L3N1bW1hcnk+CgoqKlTDrXR1bG86Kiog4oCcUG9yY2VudGFqZSBkZSBhY3RpdmlkYWQgcG9yIHNleG8geSBlZHVjYWNpw7NuLCBFUEggMVQyMDI1LCAxOCBhIDY1IGHDsW9z4oCdLgoKKipEZXNjcmlwY2nDs246Kiog4oCcRW4gbG9zIGRvcyBncnVwb3MgZWR1Y2F0aXZvcywgbGFzIGJhcnJhcyBkZSBsb3MgdmFyb25lcyBzb24gbcOhcyBhbHRhcyBxdWUgbGFzIGRlIGxhcyBtdWplcmVz4oCdLgoKKipMaW1pdGFjacOzbjoqKiDigJxMb3MgcG9yY2VudGFqZXMgZGVzY3JpYmVuIGxvcyBjYXNvcyBzZWxlY2Npb25hZG9zOyB1bmEgZXN0aW1hY2nDs24gb2ZpY2lhbCByZXF1aWVyZSBhcGxpY2FyIGxvcyBwb25kZXJhZG9yZXMgeSBjb25zaWRlcmFyIGVsIGRpc2XDsW8gZGUgbGEgZW5jdWVzdGHigJ0uCjwvZGV0YWlscz4KCiMgRXJyb3JlcyBmcmVjdWVudGVzIHkgY8OzbW8gcmVzb2x2ZXJsb3MKClVuIG1lbnNhamUgZGUgZXJyb3IgaW5mb3JtYSBlbCBwdW50byBlbiBlbCBxdWUgdW5hIGluc3RydWNjacOzbiBkZWrDsyBkZSBzZXIgY29tcHJlbnNpYmxlIHBhcmEgUiBvIGxhIHRhcmVhIHF1ZWTDsyBpbmNvbXBsZXRhLgoKIyMgRXN0cmF0ZWdpYSBnZW5lcmFsCgoxLiBMZWVyIGVsIMO6bHRpbW8gbWVuc2FqZSBjb21wbGV0by4KMi4gSWRlbnRpZmljYXIgZWwgbm9tYnJlIGRlbCBvYmpldG8sIGFyY2hpdm8sIGZ1bmNpw7NuIG8gcGFxdWV0ZSBtZW5jaW9uYWRvLgozLiBSZXZpc2FyIG9ydG9ncmFmw61hLCBtYXnDunNjdWxhcywgY29taWxsYXMsIGNvbWFzIHkgcGFyw6ludGVzaXMuCjQuIEVqZWN1dGFyIGxvcyBibG9xdWVzIGRlc2RlIGFycmliYSwgZW4gb3JkZW4uCjUuIENvbXByb2JhciBlbCB0aXBvIHkgZWwgY29udGVuaWRvIGNvbiBgY2xhc3MoKWAsIGBzdHIoKWAsIGBuYW1lcygpYCBvIGBoZWFkKClgLgo2LiBSZWR1Y2lyIGVsIHByb2JsZW1hIGEgdW5hIGzDrW5lYSBwZXF1ZcOxYS4KNy4gQ29uc3VsdGFyIGxhIGF5dWRhIGNvbiBgP2Z1bmNpb25gLgoKIyMgR3XDrWEgcsOhcGlkYQoKfCBNZW5zYWplIG8gc8OtbnRvbWEgfCBDYXVzYSBoYWJpdHVhbCB8IFF1w6kgcmV2aXNhciB8Cnw6LS18Oi0tfDotLXwKfCBgb2JqZWN0ICd4JyBub3QgZm91bmRgIHwgRWwgb2JqZXRvIG5vIGZ1ZSBjcmVhZG8gbyBlbCBub21icmUgbm8gY29pbmNpZGUuIHwgRWplY3V0YXIgZWwgYmxvcXVlIGFudGVyaW9yOyByZXZpc2FyIG1hecO6c2N1bGFzLiB8CnwgYGNvdWxkIG5vdCBmaW5kIGZ1bmN0aW9uYCB8IEZhbHRhIGNhcmdhciB1biBwYXF1ZXRlIG8gaGF5IHVuIGVycm9yIGVuIGVsIG5vbWJyZS4gfCBVc2FyIGBsaWJyYXJ5KClgIG8gYHBhcXVldGU6OmZ1bmNpb24oKWAuIHwKfCBgdGhlcmUgaXMgbm8gcGFja2FnZSBjYWxsZWQuLi5gIHwgRWwgcGFxdWV0ZSBubyBlc3TDoSBpbnN0YWxhZG8uIHwgRWplY3V0YXIgYGluc3RhbGwucGFja2FnZXMoKWAgdW5hIHZlei4gfAp8IGBjYW5ub3Qgb3BlbiB0aGUgY29ubmVjdGlvbmAgfCBMYSBydXRhIG8gZWwgYXJjaGl2byBubyBleGlzdGVuLiB8IFVzYXIgYGZpbGUuZXhpc3RzKClgIHkgcmV2aXNhciBsYSBjYXJwZXRhIGRlbCBwcm95ZWN0by4gfAp8IGB1bmV4cGVjdGVkIHN5bWJvbGAgbyBgdW5leHBlY3RlZCBlbmRgIHwgRmFsdGEgdW5hIGNvbWEsIGNvbWlsbGEgbyBwYXLDqW50ZXNpcy4gfCBSZXZpc2FyIGxhIGzDrW5lYSB5IHRhbWJpw6luIGxhIGFudGVyaW9yLiB8CnwgYG5vbi1udW1lcmljIGFyZ3VtZW50YCB8IFNlIGludGVudMOzIGNhbGN1bGFyIGNvbiB0ZXh0byBvIGNhdGVnb3LDrWFzLiB8IFJldmlzYXIgYGNsYXNzKClgIHkgbGEgaW1wb3J0YWNpw7NuLiB8CnwgRWwgcmVzdWx0YWRvIGVzIGBOQWAgfCBIYXkgdmFsb3JlcyBmYWx0YW50ZXMuIHwgRGVjaWRpciBzaSBjb3JyZXNwb25kZSBgbmEucm0gPSBUUlVFYC4gfAp8IFRvZG8gcXVlZGEgZW4gdW5hIGNvbHVtbmEgfCBFbCBzZXBhcmFkb3IgZGUgaW1wb3J0YWNpw7NuIGVzIGluY29ycmVjdG8uIHwgQ29tcGFyYXIgYHJlYWQuY3N2KClgIHkgYHJlYWQuY3N2MigpYC4gfAp8IExhcyB0aWxkZXMgc2UgdmVuIG1hbCB8IExhIGNvZGlmaWNhY2nDs24gbm8gY29pbmNpZGUuIHwgUHJvYmFyIGBmaWxlRW5jb2RpbmcgPSAiTGF0aW4xImAgbyBgIlVURi04ImAuIHwKfCBGdW5jaW9uYSBwb3IgbMOtbmVhcyBwZXJvIG5vIGFsIHRlamVyIHwgRGVwZW5kw61hIGRlIG9iamV0b3MgcHJldmlvcyBvIHJ1dGFzIHBlcnNvbmFsZXMuIHwgUmVpbmljaWFyIFIgeSBlamVjdXRhciBkZXNkZSBhcnJpYmEgY29uIHJ1dGFzIHJlbGF0aXZhcy4gfAp8IExhcyBldGlxdWV0YXMgZGVsIGdyw6FmaWNvIHNlIHN1cGVycG9uZW4gfCBIYXkgcG9jbyBlc3BhY2lvIG8gZGVtYXNpYWRvIHRleHRvLiB8IEF1bWVudGFyIHRhbWHDsW8sIGFicmV2aWFyLCBnaXJhciBvIHJldWJpY2FyIGV0aXF1ZXRhcy4gfAoKUG9kZW1vcyBjb21wcm9iYXIgdW5hIHJ1dGEgc2luIGludGVudGFyIGFicmlybGE6CgpgYGB7ciBjb21wcm9iYXItcnV0YX0KZmlsZS5leGlzdHMocnV0YV9kYXRvcykKYGBgCgpUYW1iacOpbiBlcyDDunRpbCByZWluaWNpYXIgUiBkZXNkZSAqKlNlc3Npb24gPiBSZXN0YXJ0IFIqKiB5IGVqZWN1dGFyIHRvZG8gZGVzZGUgZWwgcHJpbmNpcGlvLiBVbmEgc2VzacOzbiBsaW1waWEgcmV2ZWxhIGRlcGVuZGVuY2lhcyBpbnZpc2libGVzLgoKIyBQYXJhIHNlZ3VpciBwcmFjdGljYW5kbwoKRWwgcmVjb3JyaWRvIGFwbGljYWRvIGVuIGVzdGUgY3Vyc28gcXVlZGEgZGlzcG9uaWJsZSBjb21vIHVuYSBzZWN1ZW5jaWEgcmV1dGlsaXphYmxlOgoKPiBwcmVndW50YSDihpIgcHNldWRvY8OzZGlnbyDihpIgcHJveWVjdG8g4oaSIGltcG9ydGFjacOzbiDihpIgZXhwbG9yYWNpw7NuIOKGkiBmaWx0cm8g4oaSIHJlY29kaWZpY2FjacOzbiDihpIgY29udHJvbCDihpIgcG9yY2VudGFqZSDihpIgZ3LDoWZpY28g4oaSIGludGVycHJldGFjacOzbgoKUGFyYSBhZmlhbnphciBlbCBwcm9jZXNvLCBlbGVnw60gdW5hIHZhcmlhY2nDs24gZGUgbGEgcHJlZ3VudGEgb3JpZ2luYWw6IGNhbWJpw6EgZWwgZ3J1cG8gZGUgZWRhZCwgcmVlbXBsYXrDoSBhY3RpdmlkYWQgcG9yIG9jdXBhY2nDs24gbyBjb21wYXLDoSBvdHJvIG5pdmVsIGRlIGFncnVwYWNpw7NuIGVkdWNhdGl2YS4gUmVnaXN0csOhIGxhIGRlY2lzacOzbiBlbiBlbCBwc2V1ZG9jw7NkaWdvLCBhZGFwdMOhIGVsIGFuw6FsaXNpcyB5IHJldmlzw6EgY8OzbW8gY2FtYmlhIGxhIHRhYmxhIHkgY3XDoWwgZGUgbG9zIGRvcyBncsOhZmljb3MgY29tdW5pY2EgbWVqb3IgZWwgcmVzdWx0YWRvLgoKQWwgdGVybWluYXIsIGVzY3JpYsOtIHVuYSBicmV2ZSBub3RhIHF1ZSBleHBsaXF1ZSBsYSBtb2RpZmljYWNpw7NuIHJlYWxpemFkYSwgZWwgcHJpbmNpcGFsIHJlc3VsdGFkbyB5IGVsIGzDrW1pdGUgZGVsIGFuw6FsaXNpcy4KCiMgRnVlbnRlcyB5IHJlY3Vyc29zCgotIElOREVDLiBbQmFzZXMgZGUgZGF0b3MgZGUgbGEgRW5jdWVzdGEgUGVybWFuZW50ZSBkZSBIb2dhcmVzXShodHRwczovL3d3dy5pbmRlYy5nb2IuYXIvaW5kZWMvd2ViL0luc3RpdHVjaW9uYWwtSW5kZWMtQmFzZXNEZURhdG9zLTgpLgotIElOREVDLiBbRGlzZcOxbyBkZSByZWdpc3Ryb3MgeSBlc3RydWN0dXJhIGRlIGxhcyBiYXNlcyBIb2dhciB5IFBlcnNvbmFzLCBwcmltZXIgdHJpbWVzdHJlIGRlIDIwMjVdKGh0dHBzOi8vd3d3LmluZGVjLmdvYi5hci9mdHAvY3VhZHJvcy9tZW51c3VwZXJpb3IvZXBoL0VQSF9yZWdpc3Ryb18xVDIwMjUucGRmKS4KLSBSIFByb2plY3QuIFtEZXNjYXJnYXIgUl0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvKS4KLSBQb3NpdC4gW0Rlc2NhcmdhciBSU3R1ZGlvIERlc2t0b3BdKGh0dHBzOi8vcG9zaXQuY28vZG93bmxvYWQvcnN0dWRpby1kZXNrdG9wLykuCi0gZ2dwbG90Mi4gW0ludHJvZHVjY2nDs246IGNhcGFzLCBlc2NhbGFzLCBmYWNldGFzIHkgdGVtYXNdKGh0dHBzOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnL2FydGljbGVzL2dncGxvdDIuaHRtbCkuCi0gZ2dwbG90Mi4gW1JlZmVyZW5jaWEgZGUgZ3LDoWZpY29zIGRlIGJhcnJhc10oaHR0cHM6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL2dlb21fYmFyLmh0bWwpLgotIGdncGxvdDIuIFtUZW1hcyBjb21wbGV0b3NdKGh0dHBzOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS9nZ3RoZW1lLmh0bWwpLgoKYGBge3IgdmFsaWRhY2lvbmVzLWZpbmFsZXMsIGluY2x1ZGU9RkFMU0V9CmVzcGVyYWRvcyA8LSBjKDgzLjAsIDgwLjAsIDY3LjgsIDQ5LjIpCm9ic2VydmFkb3MgPC0gc29ydCh0YWJsYV9hbHRfb3JkZW5hZGEkcG9yY2VudGFqZSwgZGVjcmVhc2luZyA9IFRSVUUpCgpzdG9waWZub3QoCiAgbnJvdyhlcGgpID09IDQ1NDI1LAogIG5jb2woZXBoKSA9PSAyMzUsCiAgbnJvdyhkYXRvcykgPT0gMjg3MTksCiAgaWRlbnRpY2FsKG9ic2VydmFkb3MsIGVzcGVyYWRvcyksCiAgZmlsZS5leGlzdHMocnV0YV9kYXRvcykKKQpgYGAK