Cómo usar esta guía

Esta guía acompaña la primera jornada del taller. Sirve para dos cosas: seguir la clase en vivo y repasar después por tu cuenta. No necesitas saber programar.

Qué vas a lograr hoy

  • Instalar R y RStudio y entender para qué sirve cada uno.
  • Escribir y ejecutar tus primeras instrucciones en R.
  • Abrir la base de datos de Telesalud y revisarla con criterio.
  • Hacer tus primeras tablas y tu primer gráfico.
  • Pedirle código a una IA y, sobre todo, revisarlo antes de confiar en él.

Cómo está escrita

Cada tema sigue el mismo orden: primero una explicación corta, luego un bloque de código para ejecutar y, al final, qué deberías observar en el resultado. Las cajas de colores significan siempre lo mismo:

Verde · Control. Algo que debes comprobar antes de seguir.

Naranja · Atención. Un error frecuente o un detalle que suele confundir.

Azul · IA. Cómo usar un asistente de IA en ese punto.

Rojo · Privacidad. Reglas sobre los datos de salud.

Morado · Tu turno. Un ejercicio corto, con la solución oculta debajo.

Cómo ejecutar el código

Este archivo es un R Markdown (.Rmd): un documento que mezcla texto y código. En RStudio, cada bloque gris de código tiene un botón verde ▶ arriba a la derecha que ejecuta ese bloque. También puedes poner el cursor en una línea y pulsar Ctrl + Enter (en Mac, Cmd + Enter) para ejecutar solo esa línea.

Avanza en orden, de arriba hacia abajo: los bloques posteriores usan lo que crean los anteriores. Si es tu primera vez, empieza por la Parte 0.


Parte 0 · Instalar y conocer las herramientas

0.1 R y RStudio: dos programas distintos

  • R es el lenguaje: el motor que hace los cálculos.
  • RStudio es el entorno donde trabajamos: el tablero desde el que manejamos ese motor.

Se instalan los dos, y en ese orden: primero R, después RStudio. Ambos son gratuitos.

0.2 Instalar R

  1. Entra a https://cran.r-project.org
  2. Elige tu sistema operativo:
    • Windows: Download R for Windows → base → Download R for Windows.
    • Mac: Download R for macOS → elige el archivo .pkg que corresponda a tu equipo (Apple Silicon M1/M2/M3 o Intel).
    • Linux: Download R for Linux y sigue las instrucciones de tu distribución.
  3. Abre el instalador y acepta las opciones por defecto (Siguiente, Siguiente, Finalizar).

0.3 Instalar RStudio

  1. Entra a https://posit.co/download/rstudio-desktop
  2. Descarga RStudio Desktop (la página detecta tu sistema operativo).
  3. Instala con las opciones por defecto.
  4. Abre RStudio (no “R”). A partir de ahora siempre trabajaremos desde RStudio.

¿No puedes instalar programas en tu computadora? Usa Posit Cloud (https://posit.cloud): es RStudio dentro del navegador. Crea una cuenta gratuita, abre un proyecto nuevo y sube los archivos de datos con el botón Upload del panel Files.

0.4 Recorrido por RStudio

Al abrir RStudio verás la ventana dividida en paneles:

Panel Dónde está Para qué sirve
Editor (Source) Arriba a la izquierda Aquí se escriben y guardan los scripts y documentos como este
Consola (Console) Abajo a la izquierda Aquí R responde: resultados, avisos y errores
Environment Arriba a la derecha Lista de los objetos que has creado (tus bases, tablas, valores)
Files / Plots / Help Abajo a la derecha Archivos de tu carpeta, gráficos y páginas de ayuda

Si no ves el editor, crea un archivo nuevo con File › New File › R Script.

Atajos de teclado que más usarás

Atajo (Windows / Linux) En Mac Qué hace
Ctrl + Enter Cmd + Enter Ejecuta la línea donde está el cursor
Alt + - Option + - Escribe la flecha de asignación <-
Ctrl + Shift + M Cmd + Shift + M Escribe el pipe %>%
Ctrl + S Cmd + S Guarda el archivo
Tab Tab Autocompleta nombres
F1 F1 Abre la ayuda de la función bajo el cursor

0.5 Preparar la carpeta de trabajo

R necesita saber en qué carpeta están tus archivos. La forma más ordenada es crear un proyecto:

  1. Descomprime el ZIP de datos del taller en una carpeta, por ejemplo telesalud_taller.
  2. Copia esta guía (.Rmd) dentro de esa misma carpeta.
  3. En RStudio: File › New Project › Existing Directory → elige la carpeta → Create Project.
  4. En el panel Files (abajo a la derecha) deberías ver los archivos .csv y esta guía. Ábrela desde ahí.

Un proyecto hace que R trabaje siempre dentro de esa carpeta, sin tener que escribir rutas largas como C:/Users/....

0.6 Comprobar que todo funciona

Ejecuta este bloque. Es tu primera instrucción en R.

R.version.string
#> [1] "R version 4.5.3 (2026-03-11 ucrt)"

Deberías ver un texto con la versión de R instalada. Cualquier versión 4.x sirve para este taller.

Ahora preguntemos a R en qué carpeta está trabajando y qué archivos ve ahí:

getwd()        # get working directory: la carpeta de trabajo actual
#> [1] "C:/Users/USER/Downloads/carpeta de trabajo taller de R/Bases_R_Telesalud"
list.files()   # los archivos que hay en esa carpeta
#>  [1] "00_importar_y_explorar.R"             
#>  [2] "02_laboratorio_guiado_dia1.html"      
#>  [3] "atenciones_anonimizadas_completas.csv"
#>  [4] "atenciones_muestra_docente_10000.csv" 
#>  [5] "control_calidad.csv"                  
#>  [6] "Correo_para_docentes.txt"             
#>  [7] "diccionario_variables.csv"            
#>  [8] "Guia_Dia1_R_IA_Telesalud.Rmd"         
#>  [9] "Guia_y_diccionarios_R_2026.xlsx"      
#> [10] "LEEME_ANTES_DE_USAR.txt"              
#> [11] "resultados"                           
#> [12] "teleeducacion_anual.csv"              
#> [13] "teleeducacion_mensual.csv"

getwd() devuelve la ruta de la carpeta. list.files() muestra su contenido. En esa lista deben aparecer los archivos del taller.

file.exists(c("atenciones_muestra_docente_10000.csv",
              "teleeducacion_mensual.csv",
              "diccionario_variables.csv"))
#> [1] TRUE TRUE TRUE

Control. Los tres valores deben ser TRUE. Si alguno es FALSE, R está mirando otra carpeta: vuelve al punto 0.5. Solución rápida: Session › Set Working Directory › To Source File Location.


Parte 1 · Fundamentos de R

En esta parte no usamos todavía la base de datos completa. Aprendemos las piezas básicas del lenguaje con ejemplos pequeños, casi todos con cifras reales de Telesalud.

1.1 R como calculadora

Lo más simple que hace R es calcular. Ejecuta cada línea y mira la consola.

2 + 2
#> [1] 4
29548 + 32877        # atenciones de junio + atenciones de julio
#> [1] 62425
106321 / 158046      # atenciones de mujeres dividido por el total
#> [1] 0.6727219

El [1] que aparece antes de cada resultado solo indica que es el primer elemento de la respuesta. Puedes ignorarlo.

Todo lo que va después de un # es un comentario: R no lo ejecuta. Sirve para explicar qué hace el código. Comentar bien es parte de escribir buen código.

1.2 Objetos: guardar valores con un nombre

Calcular y perder el resultado no sirve de mucho. En R guardamos los valores en objetos, que son nombres que apuntan a un valor. Se crean con la flecha <-, que se lee “recibe”.

atenciones_junio <- 29548     # atenciones_junio "recibe" 29548
atenciones_julio <- 32877

Estas líneas no muestran nada en la consola: solo guardan. Mira el panel Environment: ahí aparecieron los dos objetos. Para ver su valor, escribe su nombre:

atenciones_junio
#> [1] 29548

Y lo más útil: los objetos se pueden usar en cálculos y guardar el resultado en otro objeto.

total_jun_jul <- atenciones_junio + atenciones_julio
total_jun_jul
#> [1] 62425

Reglas para poner nombres

  • Sin espacios ni tildes. Usa guion bajo: atenciones_junio (este estilo se llama snake_case).
  • No pueden empezar con un número.
  • R distingue mayúsculas de minúsculas: total y Total son objetos distintos.

Veamos esto último. El siguiente bloque produce un error a propósito:

Total_jun_jul
#> Error:
#> ! objeto 'Total_jun_jul' no encontrado

Leer errores es una habilidad. object 'Total_jun_jul' not found significa “no existe ningún objeto con ese nombre”. Casi siempre es un error de tipeo o una mayúscula. Un error no rompe nada: corriges y vuelves a ejecutar.

Tu turno. En agosto hubo 51753 atenciones y en septiembre 43868. Crea dos objetos con esos valores y un tercero llamado total_ago_sep con la suma.

Ver solución
atenciones_agosto     <- 51753
atenciones_septiembre <- 43868
total_ago_sep <- atenciones_agosto + atenciones_septiembre
total_ago_sep
#> [1] 95621

1.3 Tipos de datos

No todos los datos son números. R distingue varios tipos, y saber cuál tienes importa porque cada tipo permite operaciones distintas.

mes            <- 8               # numérico (numeric)
departamento   <- "COCHABAMBA"    # texto (character): siempre entre comillas
usa_glucometro <- TRUE            # lógico (logical): TRUE o FALSE

class(mes)
#> [1] "numeric"
class(departamento)
#> [1] "character"
class(usa_glucometro)
#> [1] "logical"

La función class() te dice de qué tipo es un objeto.

Tipo Ejemplo En la base de Telesalud
Numérico (numeric, integer) 8, 29548 mes, n_diagnosticos
Texto (character) "COCHABAMBA" departamento, proceso, sexo
Lógico (logical) TRUE, FALSE resultado de preguntas como “¿es mujer?”
Factor (factor) texto con categorías fijas lo usaremos mañana para grupo_edad

Comillas. "8" con comillas es un texto, no un número: no se puede sumar. Y un texto sin comillas, como COCHABAMBA, R lo interpreta como el nombre de un objeto y dará error si no existe.

1.4 NA: el dato faltante

NA (Not Available) significa “no sabemos”. Es uno de los conceptos más importantes al trabajar con datos de salud, porque un dato faltante no es un cero.

pesos <- c(62, NA, 75)    # el segundo peso no se registró

sum(pesos)                # ¿cuánto suman?
#> [1] NA

El resultado es NA. Tiene sentido: si no conocemos un valor, no conocemos la suma. R nos obliga a decidir qué hacer:

sum(pesos, na.rm = TRUE)     # suma ignorando los faltantes
#> [1] 137
mean(pesos, na.rm = TRUE)    # promedio de los valores conocidos
#> [1] 68.5
is.na(pesos)                 # ¿cuáles son faltantes?
#> [1] FALSE  TRUE FALSE
sum(is.na(pesos))            # ¿cuántos faltantes hay?
#> [1] 1

na.rm = TRUE significa “quita los NA antes de calcular” (NA remove). is.na() pregunta, valor por valor, si es faltante.

Regla de oro. Nunca reemplaces un NA por 0 sin una razón explícita. “No se registró el peso” y “pesa cero kilos” son afirmaciones muy distintas.

1.5 Vectores: varios valores juntos

Un vector es una serie de valores del mismo tipo. Se crea con la función c() (de combinar). Ya usaste uno en el bloque anterior.

meses          <- c(6, 7, 8, 9)
atenciones_mes <- c(29548, 32877, 51753, 43868)   # atenciones por mes, base completa
nombres_mes    <- c("Junio", "Julio", "Agosto", "Septiembre")

Con un vector puedes hacer resúmenes:

length(atenciones_mes)    # cuántos elementos tiene
#> [1] 4
sum(atenciones_mes)       # total: las 158.046 atenciones de la base
#> [1] 158046
mean(atenciones_mes)      # promedio mensual
#> [1] 39511.5
max(atenciones_mes)       # el valor más alto
#> [1] 51753

Y también operar con todos sus elementos a la vez. Aquí calculamos qué porcentaje del total representa cada mes:

porcentaje_mes <- round(100 * atenciones_mes / sum(atenciones_mes), 1)
porcentaje_mes
#> [1] 18.7 20.8 32.7 27.8

R dividió cada mes por el total, multiplicó por 100 y redondeó a un decimal. Agosto concentra casi un tercio de las atenciones.

Para sacar un elemento concreto se usan corchetes con su posición:

atenciones_mes[3]     # el tercer elemento: agosto
#> [1] 51753
nombres_mes[3]
#> [1] "Agosto"

1.6 Funciones y argumentos

Ya usaste varias funciones: sum(), mean(), round(), class(). Una función es una instrucción con nombre que recibe datos entre paréntesis y devuelve un resultado.

Lo que va dentro del paréntesis son los argumentos, separados por comas.

round(32.7183)               # un argumento: redondea a entero
#> [1] 33
round(32.7183, digits = 1)   # dos argumentos: redondea a 1 decimal
#> [1] 32.7
round(32.7183, 2)            # el nombre del argumento se puede omitir si respetas el orden
#> [1] 32.72

¿Cómo saber qué argumentos acepta una función? Con la ayuda:

?round          # abre la página de ayuda de round()

También puedes poner el cursor sobre el nombre de la función y pulsar F1.

1.7 Data frames: las tablas de R

Un data frame es una tabla: filas y columnas, como una hoja de cálculo, pero con reglas.

  • Cada fila es una observación.
  • Cada columna es una variable, y todos sus valores son del mismo tipo.
  • Cada celda tiene un solo valor (o NA).

Podemos construir uno pequeño uniendo los vectores de antes:

resumen_mensual <- data.frame(
  mes        = meses,
  nombre_mes = nombres_mes,
  atenciones = atenciones_mes,
  porcentaje = porcentaje_mes
)

resumen_mensual

Cada vector se convirtió en una columna. Para explorar una tabla hay unas pocas funciones que usarás siempre:

nrow(resumen_mensual)     # número de filas
#> [1] 4
ncol(resumen_mensual)     # número de columnas
#> [1] 4
names(resumen_mensual)    # nombres de las columnas
#> [1] "mes"        "nombre_mes" "atenciones" "porcentaje"

Para extraer una columna se usa el signo $:

resumen_mensual$atenciones
#> [1] 29548 32877 51753 43868
sum(resumen_mensual$atenciones)
#> [1] 158046

La base de Telesalud que abriremos en la Parte 2 es exactamente esto: un data frame, solo que con miles de filas y 33 columnas.

1.8 Paquetes: ampliar lo que R sabe hacer

R trae funciones básicas. Los paquetes agregan funciones nuevas hechas por la comunidad. Hoy usamos tres:

Paquete Para qué sirve
readr Importar archivos (CSV)
dplyr Transformar tablas: contar, filtrar, ordenar, resumir
ggplot2 Hacer gráficos

Con los paquetes hay dos pasos, y conviene no confundirlos:

  • Instalar (install.packages()): se hace una sola vez por computadora. Necesita internet.
  • Cargar (library()): se hace cada vez que abres R.

Una analogía: instalar es comprar un libro; cargar es sacarlo del estante para usarlo.

El siguiente bloque instala solo los paquetes que te falten. La primera vez puede tardar unos minutos.

paquetes <- c("readr", "dplyr", "ggplot2")
faltan   <- paquetes[!paquetes %in% rownames(installed.packages())]
if (length(faltan) > 0) install.packages(faltan)

Y este los carga:

library(readr)
library(dplyr)
library(ggplot2)

Error típico. there is no package called 'dplyr' significa que falta instalarlo. could not find function "%>%" significa que está instalado pero no lo cargaste con library(dplyr) en esta sesión.

1.9 El pipe %>%: encadenar pasos

Casi todo análisis es una secuencia: toma la tabla, luego filtra, luego cuenta, luego ordena. El pipe %>% permite escribirlo en ese mismo orden. Se lee “y luego”.

Sin pipe, las funciones se anidan y hay que leer de adentro hacia afuera:

arrange(resumen_mensual, desc(atenciones))

Con pipe, se lee de arriba hacia abajo, como una receta:

resumen_mensual %>%               # toma la tabla resumen_mensual
  arrange(desc(atenciones))       # y luego ordénala de mayor a menor

El resultado es el mismo. arrange() ordena filas y desc() indica orden descendente. El pipe se escribe con Ctrl + Shift + M.

Versiones recientes de R también aceptan el pipe nativo |>. Para lo que haremos funcionan igual; en esta guía usamos %>%.

Tu turno. Usa el pipe para ordenar resumen_mensual de menor a mayor número de atenciones. Pista: arrange() ordena de menor a mayor si no usas desc().

Ver solución
resumen_mensual %>%
  arrange(atenciones)

Parte 2 · La base de datos de Telesalud

2.1 Qué datos tenemos

Trabajaremos toda la semana con datos de la Plataforma de Telesalud, preparados para docencia:

Archivo Contenido
atenciones_anonimizadas_completas.csv 158.046 registros de atención, 33 variables (junio a septiembre de 2026)
atenciones_muestra_docente_10000.csv Muestra de 10.000 registros para practicar con rapidez
teleeducacion_mensual.csv 1.404 filas: actividades educativas por mes y departamento, 2020–2026
teleeducacion_anual.csv 117 filas: lo mismo, resumido por año
diccionario_variables.csv Qué significa cada variable y cuántos faltantes tiene
control_calidad.csv Problemas detectados en los datos, sin corregir

Reglas de uso de estos datos.

  • Se retiraron nombres, CI, teléfonos, fechas exactas y otros identificadores. Aun así, son datos de salud a nivel de registro y el riesgo de reidentificación no es cero.
  • Úsalos solo para el taller y para análisis autorizado.
  • No publiques los archivos ni intentes identificar personas.
  • No subas los CSV ni pegues filas en asistentes públicos de IA. A la IA se le describe la estructura (nombres y tipos de columnas), no se le entregan los datos.

2.2 Importar la base

Importar es leer un archivo y convertirlo en un data frame dentro de R. Usamos read_csv() del paquete readr.

Primero elegimos con qué archivo trabajar. usar_muestra funciona como un interruptor: con TRUE usamos la muestra de 10.000 registros; con FALSE, la base completa.

usar_muestra <- TRUE

archivo <- if (usar_muestra) {
  "atenciones_muestra_docente_10000.csv"
} else {
  "atenciones_anonimizadas_completas.csv"
}

archivo
#> [1] "atenciones_muestra_docente_10000.csv"

La estructura if (...) {...} else {...} significa: si la condición es verdadera, usa lo primero; si no, lo segundo. El objeto archivo ahora contiene el nombre del archivo elegido.

Ahora sí, importamos:

atenciones <- read_csv(archivo,
                       na = c("", "NA"),
                       show_col_types = FALSE,
                       locale = locale(encoding = "UTF-8"))

Qué hace cada argumento:

Argumento Significado
archivo El nombre del archivo a leer
na = c("", "NA") Las celdas vacías se leen como dato faltante (NA)
show_col_types = FALSE Evita un mensaje largo al importar
locale(encoding = "UTF-8") Lee correctamente tildes y caracteres especiales

Mira el panel Environment: apareció atenciones, con su número de filas (obs.) y columnas (variables).

Importamos también la base de teleeducación y el diccionario, que usaremos más adelante:

tele_mensual <- read_csv("teleeducacion_mensual.csv", na = c("", "NA"),
                         show_col_types = FALSE,
                         locale = locale(encoding = "UTF-8"))

diccionario <- read_csv("diccionario_variables.csv", show_col_types = FALSE,
                        locale = locale(encoding = "UTF-8"))

2.3 Primer vistazo: ¿qué tengo delante?

Antes de analizar, se mira. Las tres preguntas iniciales son: ¿cuántas filas?, ¿cuántas columnas?, ¿cómo se llaman?

nrow(atenciones)
#> [1] 10000
ncol(atenciones)
#> [1] 33
names(atenciones)
#>  [1] "registro_id"            "anio"                   "mes"                   
#>  [4] "departamento"           "proceso"                "sexo"                  
#>  [7] "grupo_etario_fuente"    "grupo_edad"             "tipo_captacion"        
#> [10] "origen_atencion"        "referido_por"           "especialidad"          
#> [13] "tipo_atencion"          "destino_interconsulta"  "cobertura"             
#> [16] "tipo_consulta"          "modalidad_tiempo"       "capitulo_cie10"        
#> [19] "n_diagnosticos"         "imc_categoria"          "n_dispositivos"        
#> [22] "usa_electrocardiografo" "usa_otoscopio"          "usa_monitor_sv"        
#> [25] "usa_glucometro"         "usa_ecografo"           "eje_ent"               
#> [28] "eje_et"                 "eje_salud_infantil"     "eje_salud_materna"     
#> [31] "eje_discapacidad"       "eje_otros"              "telemetria_n"

Control. Con la muestra deben salir 10.000 filas; con la base completa, 158.046. En ambos casos, 33 columnas.

glimpse() da la vista más completa: una línea por variable, con su tipo y sus primeros valores.

glimpse(atenciones)
#> Rows: 10,000
#> Columns: 33
#> $ registro_id            <chr> "Re173e3e454bf61bd", "R925359eb7f124b01", "Rbb9…
#> $ anio                   <dbl> 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026,…
#> $ mes                    <dbl> 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,…
#> $ departamento           <chr> "ORURO", "CHUQUISACA", "CHUQUISACA", "CHUQUISAC…
#> $ proceso                <chr> "TLM", "TLM", "TLC", "TLC", "TLM", "TLC", "TLC"…
#> $ sexo                   <chr> "M", "F", "F", "F", "F", "M", "F", "M", "F", "M…
#> $ grupo_etario_fuente    <chr> "60_mas", "20-39", "50-59", "50-59", "20-39", "…
#> $ grupo_edad             <chr> "60_mas", "20-39", "40-59", "40-59", "20-39", "…
#> $ tipo_captacion         <chr> NA, NA, "CONSULTA MEDICINA GENERAL", "CONSULTA …
#> $ origen_atencion        <chr> "INSTITUCIONAL", "CAMPANA", "INSTITUCIONAL", "I…
#> $ referido_por           <chr> "TELESALUD", "TELESALUD", "TELESALUD", "TELESAL…
#> $ especialidad           <chr> NA, NA, "MEDICINA GENERAL", "MEDICINA GENERAL",…
#> $ tipo_atencion          <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
#> $ destino_interconsulta  <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
#> $ cobertura              <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
#> $ tipo_consulta          <chr> "SEGUIMIENTO", "NUEVO", "SEGUIMIENTO", "SEGUIMI…
#> $ modalidad_tiempo       <chr> NA, NA, "REAL", "REAL", NA, "REAL", "REAL", NA,…
#> $ capitulo_cie10         <chr> "VII_VIII_OJO_OIDO", "XXI_CONTACTO_SERVICIOS", …
#> $ n_diagnosticos         <dbl> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2,…
#> $ imc_categoria          <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
#> $ n_dispositivos         <dbl> 2, 2, 0, 0, 2, 0, 0, 2, 2, 0, 2, 0, 2, 0, 1, 1,…
#> $ usa_electrocardiografo <dbl> 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1,…
#> $ usa_otoscopio          <dbl> 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
#> $ usa_monitor_sv         <dbl> 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0,…
#> $ usa_glucometro         <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,…
#> $ usa_ecografo           <dbl> 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
#> $ eje_ent                <dbl> 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1,…
#> $ eje_et                 <dbl> 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1,…
#> $ eje_salud_infantil     <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
#> $ eje_salud_materna      <dbl> 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
#> $ eje_discapacidad       <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
#> $ eje_otros              <dbl> 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0,…
#> $ telemetria_n           <dbl> 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1,…

Cómo leer esta salida: <chr> es texto, <dbl> es número. Fíjate que mes es numérico y departamento es texto, y que en varias columnas ya se ven NA.

Para ver las primeras filas como tabla:

head(atenciones, 5)

Si quieres ver toda la tabla como hoja de cálculo, ejecuta View(atenciones) en la consola (con V mayúscula) o haz clic en su nombre en el panel Environment.

2.4 El diccionario de variables

Ningún análisis serio empieza sin saber qué significa cada columna. El diccionario es otra tabla, y la consultamos con las mismas herramientas:

diccionario %>%
  filter(conjunto == "atenciones") %>%
  select(variable, tipo_R_sugerido, definicion)

Aquí aparecen dos funciones nuevas de dplyr:

  • filter() se queda con las filas que cumplen una condición. conjunto == "atenciones" significa “donde la columna conjunto sea igual a atenciones”. Atención al doble signo igual ==: se usa para comparar. Un solo = sirve para asignar argumentos.
  • select() se queda con las columnas que nombres.

Las 33 variables se agrupan así:

Grupo Variables
Tiempo y lugar anio, mes, departamento
Persona sexo, grupo_etario_fuente, grupo_edad, cobertura
Proceso y servicio proceso, tipo_captacion, origen_atencion, referido_por, especialidad, tipo_atencion, destino_interconsulta, tipo_consulta, modalidad_tiempo
Clínico capitulo_cie10, n_diagnosticos, imc_categoria
Dispositivos n_dispositivos, usa_electrocardiografo, usa_otoscopio, usa_monitor_sv, usa_glucometro, usa_ecografo
Ejes programáticos eje_ent, eje_et, eje_salud_infantil, eje_salud_materna, eje_discapacidad, eje_otros
Otros telemetria_n, registro_id

2.5 La unidad de análisis

Pregunta clave: ¿qué representa una fila? En esta base, cada fila es un registro de atención, no un paciente. Una misma persona puede aparecer varias veces.

n_distinct(atenciones$registro_id)    # cuántos identificadores distintos hay
#> [1] 10000
nrow(atenciones)                      # cuántas filas hay
#> [1] 10000

Los dos números coinciden: hay un registro_id por fila. Ese identificador es un código aleatorio creado para proteger los datos; no permite saber qué registros pertenecen a la misma persona.

Consecuencia para interpretar. Decimos “10.000 atenciones”, nunca “10.000 pacientes”. Lo mismo en teleeducación: los beneficiarios son conteos de asistencia, no necesariamente personas distintas.

También conviene confirmar el periodo que cubren los datos:

atenciones %>% count(anio)
atenciones %>% count(mes)

Solo hay meses 6 a 9 de 2026. Son cuatro meses, no un año: cualquier conclusión “anual” sería una extrapolación.

2.6 Contar: la pregunta más básica

count() responde “¿cuántos hay de cada uno?”. Es la función que más usarás.

atenciones %>%
  count(departamento)

La tabla resultante tiene una fila por departamento y una columna n con el número de atenciones. Con sort = TRUE sale ordenada de mayor a menor:

atenciones %>%
  count(departamento, sort = TRUE)

Un conteo solo dice poco; casi siempre queremos también el porcentaje. Para crear columnas nuevas se usa mutate():

atenciones %>%
  count(departamento, sort = TRUE) %>%
  mutate(porcentaje = round(100 * n / sum(n), 1))

Léelo como una receta: toma atenciones, y luego cuenta por departamento, y luego agrega una columna con el porcentaje. La Paz, Cochabamba y Santa Cruz concentran cerca del 60% de los registros.

Se puede contar por dos variables a la vez. Veamos la variable proceso, que tiene cuatro códigos operacionales de la fuente (HCD, HIC, TLC, TLM), cruzada con el mes:

atenciones %>%
  count(proceso, mes)

Observa las filas de TLM: en el mes 8 hay un salto muy grande respecto de los otros meses. Lo llamaremos el misterio de agosto y lo investigarás en el desafío final.

Control de consistencia. En cualquier conteo, la suma de n debe ser igual al total de filas. Si no cuadra, algo se perdió en el camino. Acostúmbrate a verificarlo.

atenciones %>%
  count(proceso) %>%
  summarise(total = sum(n))

summarise() reduce una tabla a un resumen; aquí, la suma de la columna n. El total coincide con nrow(atenciones).

Tu turno. ¿Cuántas atenciones hay por sexo, con su porcentaje? ¿Y por grupo_edad?

Ver solución
atenciones %>%
  count(sexo) %>%
  mutate(porcentaje = round(100 * n / sum(n), 1))

atenciones %>%
  count(grupo_edad) %>%
  mutate(porcentaje = round(100 * n / sum(n), 1))

Aproximadamente dos tercios de las atenciones corresponden a mujeres, y cerca de una de cada cuatro a personas de 60 años o más.

2.7 Filtrar: quedarse con una parte

filter() selecciona filas según una condición. Por ejemplo, solo las atenciones de Cochabamba:

atenciones %>%
  filter(departamento == "COCHABAMBA") %>%
  count(proceso, sort = TRUE)

Las condiciones se pueden combinar. Con coma (o &) deben cumplirse todas:

atenciones %>%
  filter(departamento == "COCHABAMBA", mes == 8) %>%
  count(capitulo_cie10, sort = TRUE) %>%
  head(5)

Este es el perfil clínico (por capítulo CIE-10) de Cochabamba en agosto. head(5) muestra solo las cinco primeras filas.

Operadores de comparación más usados:

Operador Significado Ejemplo
== igual a sexo == "F"
!= distinto de proceso != "TLM"
> >= < <= mayor, mayor o igual, menor, menor o igual mes >= 8
%in% está en una lista departamento %in% c("ORURO", "POTOSI")
is.na() es faltante is.na(especialidad)
! negación !is.na(especialidad)

El texto debe coincidir exactamente. En esta base los departamentos están en mayúsculas y sin tildes: "POTOSI", no "Potosí". Si un filtro devuelve cero filas, revisa primero cómo está escrito el valor con count().

2.8 Medir los faltantes

Antes de sacar conclusiones hay que saber cuánto falta y dónde. Empecemos por una variable:

sum(is.na(atenciones$especialidad))          # cuántos registros sin especialidad
#> [1] 3630
mean(is.na(atenciones$especialidad)) * 100   # qué porcentaje representan
#> [1] 36.3

Un truco útil: mean(is.na(x)) da directamente la proporción de faltantes, porque R trata TRUE como 1 y FALSE como 0.

Ahora lo mismo para todas las variables a la vez. Este bloque es más avanzado; léelo con calma:

faltantes <- tibble(
  variable    = names(atenciones),
  n_faltantes = sapply(atenciones, function(x) sum(is.na(x))),
  porcentaje  = round(100 * n_faltantes / nrow(atenciones), 1)
) %>%
  arrange(desc(porcentaje))

faltantes %>% filter(n_faltantes > 0)

Qué hace: tibble() crea una tabla nueva con tres columnas; sapply() aplica a cada columna de atenciones la operación “contar los NA”; después se calcula el porcentaje y se ordena.

Buen momento para usar la IA. Si una línea como la de sapply() no se entiende, pídele una explicación: “Explícame línea por línea qué hace este código de R, en lenguaje simple: [pega el código]”. Pegar código es seguro; lo que no se pega son los datos.

El resultado muestra variables con muchísimos faltantes: imc_categoria supera el 95%, cobertura ronda el 88%. Con ese nivel de ausencia, no sirven para conclusiones generales.

Pero no todos los faltantes son iguales. Veamos si la falta de especialidad depende del proceso:

atenciones %>%
  group_by(proceso) %>%
  summarise(
    registros            = n(),
    pct_sin_especialidad = round(mean(is.na(especialidad)) * 100, 1),
    pct_sin_modalidad    = round(mean(is.na(modalidad_tiempo)) * 100, 1)
  )

Dos funciones nuevas que trabajan juntas: group_by() divide la tabla en grupos (aquí, uno por proceso) y summarise() calcula un resumen para cada grupo. n() cuenta las filas del grupo.

Hallazgo. especialidad y modalidad_tiempo faltan en el 100% de los registros TLM y en el 0% de los demás procesos. No es un descuido de registro: el formulario de TLM no pide esos campos. Es un faltante por diseño. No se rellena; se explica.

Otra trampa de esta base. En las variables de dispositivos (usa_glucometro, usa_otoscopio, etc.), el valor 0 significa “sin registro del dispositivo”, no necesariamente “no se usó”. Redacta siempre “figura registrado en X% de las atenciones”.

2.9 Tu primer gráfico con ggplot2

ggplot2 construye gráficos por capas. Todo gráfico necesita tres cosas:

  1. Datos: la tabla que se va a graficar.
  2. aes(): qué variable va en cada eje, y cuál define el color.
  3. geom_...(): la forma: barras (geom_col), líneas (geom_line), puntos (geom_point).

Empecemos con lo mínimo: atenciones por mes.

atenciones %>%
  count(mes) %>%
  ggplot(aes(x = factor(mes), y = n)) +
  geom_col()

Primero contamos por mes; esa tabla pasa a ggplot(). En aes() decimos: el mes en el eje X, el conteo n en el eje Y. factor(mes) le indica a R que trate el mes como categoría y no como número continuo.

El error más común del primer día. Dentro de un gráfico, las capas se unen con +, no con %>%. El pipe lleva los datos hasta ggplot(); desde ahí, todo se suma con +.

Ahora lo mejoramos: color por proceso, títulos claros y un estilo más limpio.

atenciones %>%
  count(mes, proceso) %>%
  ggplot(aes(x = factor(mes), y = n, fill = proceso)) +
  geom_col(position = "dodge") +
  scale_fill_manual(values = c(HCD = "#8C95A6", HIC = "#75AADB",
                               TLC = "#276DC3", TLM = "#E4032E")) +
  labs(title = "Atenciones por mes y proceso",
       subtitle = "Plataforma de Telesalud, junio a septiembre de 2026",
       x = "Mes", y = "Número de atenciones", fill = "Proceso") +
  theme_minimal(base_size = 13)

Qué agregó cada capa:

Capa Qué hace
fill = proceso Colorea las barras según el proceso
position = "dodge" Pone las barras lado a lado en vez de apiladas
scale_fill_manual() Asigna un color concreto a cada proceso
labs() Título, subtítulo y nombres de ejes
theme_minimal() Un estilo visual sobrio

El gráfico hace evidente lo que la tabla insinuaba: el salto de TLM en agosto.

Un segundo ejemplo, con barras horizontales ordenadas, útil cuando las etiquetas son largas:

atenciones %>%
  count(departamento) %>%
  ggplot(aes(x = reorder(departamento, n), y = n)) +
  geom_col(fill = "#276DC3") +
  coord_flip() +
  labs(title = "Atenciones por departamento",
       x = NULL, y = "Número de atenciones") +
  theme_minimal(base_size = 13)

reorder(departamento, n) ordena los departamentos según su número de atenciones y coord_flip() gira el gráfico.

Leer con cuidado. Más atenciones en un departamento puede significar más población, más necesidad o simplemente mejor registro. Sin un denominador (población, establecimientos), el gráfico muestra volumen, no desempeño. Lo trabajaremos en el Día 3.

Para guardar el último gráfico como imagen:

dir.create("resultados", showWarnings = FALSE)
ggsave("resultados/atenciones_por_departamento.png", width = 9, height = 5, dpi = 150)

dir.create() crea la carpeta resultados si no existe, y ggsave() guarda ahí el gráfico.

2.10 La segunda base: teleeducación

La base de teleeducación tiene otra unidad de análisis: cada fila es una combinación de tipo de actividad × año × mes × departamento.

glimpse(tele_mensual)
#> Rows: 1,404
#> Columns: 11
#> $ tipo_actividad            <chr> "telecapacitacion", "telecapacitacion", "tel…
#> $ anio                      <dbl> 2026, 2026, 2026, 2026, 2026, 2026, 2026, 20…
#> $ mes                       <dbl> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 1, 2,…
#> $ departamento              <chr> "BENI", "BENI", "BENI", "BENI", "BENI", "BEN…
#> $ actividades               <dbl> 2, 3, 3, 3, 3, 2, 4, 5, 2, NA, NA, NA, 0, 18…
#> $ beneficiarios_hombres     <dbl> 48, 47, 93, 64, 83, 132, 159, 144, 74, NA, N…
#> $ beneficiarios_mujeres     <dbl> 62, 86, 173, 121, 155, 181, 336, 339, 129, N…
#> $ beneficiarios_total       <dbl> 110, 133, 266, 185, 238, 313, 495, 483, 203,…
#> $ estado_reporte            <chr> "reportado", "reportado", "reportado", "repo…
#> $ anio_inferido             <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
#> $ inconsistencia_sexo_total <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…

Antes de sumar beneficiarios, hay que mirar la columna estado_reporte:

tele_mensual %>% count(estado_reporte)

Hay meses marcados como sin_reporte. No son meses con cero actividades: son meses de los que no hay información. Por eso los excluimos explícitamente antes de graficar:

tele_mensual %>%
  filter(estado_reporte == "reportado",
         !is.na(beneficiarios_total),
         tipo_actividad == "telecapacitacion") %>%
  group_by(anio) %>%
  summarise(beneficiarios = sum(beneficiarios_total)) %>%
  ggplot(aes(x = factor(anio), y = beneficiarios)) +
  geom_col(fill = "#E4032E") +
  labs(title = "Beneficiarios reportados de telecapacitación por año",
       subtitle = "2026 incompleto: los datos llegan hasta septiembre",
       x = "Año", y = "Beneficiarios (conteos de asistencia)") +
  theme_minimal(base_size = 13)

Tres advertencias de esta base. (1) Los beneficiarios son conteos de asistencia, no personas únicas. (2) El año 2026 está incompleto: no lo compares como año entero. (3) En telepromoción, el bloque de 2025 tiene el año inferido (anio_inferido = 1) y conviene validarlo con la fuente.


Parte 3 · La IA como copiloto

La IA puede escribir, explicar y corregir código R con mucha rapidez. Eso no elimina tu trabajo: lo cambia. La pregunta, los datos, la validación y la interpretación siguen siendo tu responsabilidad.

3.1 El ciclo de trabajo

  1. Formular: ¿qué necesito saber?
  2. Contextualizar: ¿qué datos tengo y qué significan?
  3. Solicitar: pedir el código con un buen prompt.
  4. Ejecutar: correrlo y entender qué hace.
  5. Validar: ¿el resultado es correcto?
  6. Interpretar: ¿qué muestran realmente los datos?
  7. Comunicar: ¿quién va a decidir con esto?
  8. Reproducir: guardar el script para repetirlo con datos nuevos.

3.2 Anatomía de un buen prompt

Un prompt que produce código útil tiene seis piezas:

Pieza Ejemplo
Contexto Soy analista de Telesalud y trabajo en R con dplyr y ggplot2.
Objetivo Quiero contar las atenciones por departamento y mes.
Datos Tengo un data frame atenciones con las columnas mes (numérico, 6 a 9) y departamento (texto en mayúsculas).
Restricciones Usa solo dplyr y ggplot2. No reemplaces NA por 0.
Formato de salida Comenta cada línea del código.
Pruebas Agrega un control: la suma debe ser igual a nrow(atenciones).

Compara. Prompt vago: “hazme un gráfico de los datos de telesalud”. La IA tendrá que inventar nombres de columnas y elegir el gráfico por ti.

Plantilla para copiar y completar

Soy analista de Telesalud y trabajo en R con dplyr y ggplot2. Tengo un data frame llamado atenciones con estas columnas: [nombres y tipos de las columnas que necesitas]. Quiero [tu pregunta]. No reemplaces NA por 0. Comenta cada línea del código y agrega un control que verifique que la suma de los grupos es igual a nrow(atenciones).

Para obtener rápidamente los nombres y tipos de las columnas que vas a describir:

sapply(atenciones[, c("mes", "departamento", "proceso", "origen_atencion")], class)
#>             mes    departamento         proceso origen_atencion 
#>       "numeric"     "character"     "character"     "character"

Eso es lo que se comparte con la IA: nombres y tipos. Nunca las filas.

3.3 Semáforo de privacidad

Qué Ejemplos
🟢 Sí Estructura y código Nombres y tipos de columnas, tu código, mensajes de error
🟡 Con cuidado Resultados agregados Tablas con conteos muy pequeños, datos institucionales no publicados
🔴 Nunca Microdatos Filas de la base, archivos CSV, cualquier dato identificable

Usa siempre las herramientas de IA autorizadas por tu institución.

3.4 Checklist: antes de aceptar código de la IA

3.5 Cuando R da error

Mensaje Causa más probable
could not find function "%>%" Falta ejecutar library(dplyr)
object 'atenciones' not found No ejecutaste el bloque de importación, o hay un error de tipeo
... does not exist in current working directory El archivo no está en la carpeta de trabajo: revisa getwd()
unexpected symbol / unexpected ')' Falta o sobra una coma, un paréntesis o unas comillas
there is no package called '...' Falta instalar el paquete

Prompt de depuración

Estoy aprendiendo R. Ejecuté este código: [tu código]. Obtuve este error: [mensaje completo]. Explícame en lenguaje simple qué significa, cuál es la causa más probable y cómo lo corrijo.


Parte 4 · Desafío: tu pregunta, tu código, tu auditoría

Ahora te toca a ti recorrer el ciclo completo.

Protocolo de 25 minutos

  1. Formula (5 min). Escribe tu pregunta en una frase y qué resultado esperas ver: ¿una tabla?, ¿un gráfico?
  2. Pide (5 min). Arma el prompt con las seis piezas. Describe columnas; no pegues datos.
  3. Ejecuta y audita (10 min). Corre el código línea por línea y aplica el checklist.
  4. Interpreta (5 min). Escribe una frase con el hallazgo y otra con su límite: ¿qué no permiten concluir estos datos?

Elige una de estas preguntas o trae la tuya. Cada una incluye un punto de partida que puedes ejecutar y luego ampliar.

Pregunta 1 · Detective

¿Qué explica el salto de TLM en agosto? Pista: cruza el proceso con el mes y con origen_atencion.

atenciones %>%
  filter(proceso == "TLM") %>%
  count(mes, origen_atencion) %>%
  arrange(origen_atencion, mes)

Preguntas para tu interpretación: ¿qué origen cambia más entre julio y agosto? Eso que observas, ¿es un hallazgo o una hipótesis? ¿Con quién habría que hablar para confirmarlo?

Pregunta 2 · Territorio

¿Qué tres especialidades concentran más atenciones en tu departamento?

mi_departamento <- "COCHABAMBA"     # cámbialo por el tuyo, en mayúsculas y sin tildes

atenciones %>%
  filter(departamento == mi_departamento, !is.na(especialidad)) %>%
  count(especialidad, sort = TRUE) %>%
  head(3)

Cuidado: al excluir los NA de especialidad dejas fuera todo el proceso TLM. Dilo en tu interpretación.

Pregunta 3 · Personas

¿La proporción de consultas de seguimiento cambia según el grupo de edad?

atenciones %>%
  filter(!is.na(tipo_consulta)) %>%
  group_by(grupo_edad) %>%
  summarise(atenciones      = n(),
            pct_seguimiento = round(100 * mean(tipo_consulta == "SEGUIMIENTO"), 1))

Pregunta 4 · Programas

¿Qué porcentaje de atenciones tiene marcado el eje ENT en cada departamento?

atenciones %>%
  group_by(departamento) %>%
  summarise(pct_eje_ent = round(100 * mean(eje_ent == 1), 1)) %>%
  arrange(desc(pct_eje_ent))

Recuerda: 0 significa “sin marcación”, no “no corresponde a ENT”.

Pregunta 5 · Teleeducación

¿Cómo evolucionan por mes los beneficiarios de telecapacitación en tu departamento?

tele_mensual %>%
  filter(departamento == "COCHABAMBA",
         tipo_actividad == "telecapacitacion",
         estado_reporte == "reportado",
         !is.na(beneficiarios_total)) %>%
  ggplot(aes(x = mes, y = beneficiarios_total)) +
  geom_line(color = "#276DC3") +
  geom_point(color = "#276DC3") +
  facet_wrap(~ anio) +
  scale_x_continuous(breaks = 1:12) +
  labs(title = "Beneficiarios mensuales de telecapacitación",
       x = "Mes", y = "Beneficiarios") +
  theme_minimal()

facet_wrap(~ anio) dibuja un panel pequeño por cada año.

Pregunta 6 · Calidad

¿Hay especialidades que parecen la misma con distinto nombre?

atenciones %>%
  filter(!is.na(especialidad)) %>%
  count(especialidad) %>%
  arrange(especialidad) %>%
  print(n = Inf)

Recorre la lista y anota los pares sospechosos. No los corrijas todavía: mañana decidiremos juntos qué hacer con ellos y cómo documentarlo.


Parte 5 · Cerrar la sesión con orden

5.1 Guardar resultados

Los datos originales no se modifican nunca. Los resultados se guardan aparte, en la carpeta resultados:

dir.create("resultados", showWarnings = FALSE)

frecuencia_departamento <- atenciones %>%
  count(departamento, sort = TRUE) %>%
  mutate(porcentaje = round(100 * n / sum(n), 1))

write_csv(frecuencia_departamento, "resultados/frecuencia_departamento_dia1.csv")
write_csv(faltantes, "resultados/faltantes_por_variable_dia1.csv")

write_csv() es el inverso de read_csv(): guarda una tabla como archivo CSV. Puedes abrirlo luego en Excel.

5.2 Registrar con qué trabajaste

sessionInfo()
#> R version 4.5.3 (2026-03-11 ucrt)
#> Platform: x86_64-w64-mingw32/x64
#> Running under: Windows 11 x64 (build 26200)
#> 
#> Matrix products: default
#>   LAPACK version 3.12.1
#> 
#> locale:
#> [1] LC_COLLATE=Spanish_Bolivia.utf8  LC_CTYPE=Spanish_Bolivia.utf8   
#> [3] LC_MONETARY=Spanish_Bolivia.utf8 LC_NUMERIC=C                    
#> [5] LC_TIME=Spanish_Bolivia.utf8    
#> 
#> time zone: America/La_Paz
#> tzcode source: internal
#> 
#> attached base packages:
#> [1] stats     graphics  grDevices utils     datasets  methods   base     
#> 
#> other attached packages:
#> [1] ggplot2_4.0.3 dplyr_1.1.4   readr_2.2.0  
#> 
#> loaded via a namespace (and not attached):
#>  [1] bit_4.6.0          gtable_0.3.6       jsonlite_2.0.0     crayon_1.5.3      
#>  [5] compiler_4.5.3     tidyselect_1.2.1   parallel_4.5.3     jquerylib_0.1.4   
#>  [9] textshaping_1.0.1  systemfonts_1.3.1  scales_1.4.0       yaml_2.3.10       
#> [13] fastmap_1.2.0      R6_2.6.1           labeling_0.4.3     generics_0.1.4    
#> [17] httr2_1.2.1        knitr_1.51         tibble_3.3.0       ellmer_0.3.2      
#> [21] bslib_0.9.0        pillar_1.11.1      RColorBrewer_1.1-3 tzdb_0.5.0        
#> [25] rlang_1.1.6        cachem_1.1.0       xfun_0.52          sass_0.4.10       
#> [29] S7_0.2.0           bit64_4.6.0-1      otel_0.2.0         cli_3.6.5         
#> [33] withr_3.0.2        magrittr_2.0.3     digest_0.6.37      grid_4.5.3        
#> [37] vroom_1.7.0        rstudioapi_0.17.1  rappdirs_0.3.3     hms_1.1.4         
#> [41] lifecycle_1.0.4    coro_1.1.0         vctrs_0.6.5        evaluate_1.0.5    
#> [45] glue_1.8.0         farver_2.1.2       ragg_1.4.0         rmarkdown_2.31    
#> [49] tools_4.5.3        pkgconfig_2.0.3    htmltools_0.5.8.1

sessionInfo() muestra la versión de R y de los paquetes cargados. Es el registro que permite a otra persona, o a ti dentro de unos meses, reproducir el análisis en las mismas condiciones.

5.3 Generar tu informe y cerrar

  • Guarda este archivo con Ctrl + S.
  • Pulsa el botón Knit (arriba del editor) para convertir este documento en un informe HTML con todos tus resultados y gráficos. Ese informe es tu producto del día.
  • Al cerrar RStudio, si pregunta Save workspace image?, responde Don’t Save. Lo que hace reproducible tu trabajo es el script, no la memoria guardada. Mañana abres el proyecto y ejecutas de nuevo.

5.4 Para practicar por tu cuenta

  1. Cambia usar_muestra <- TRUE por FALSE en el punto 2.2, vuelve a ejecutar todo y compara los porcentajes por departamento y por proceso. ¿La muestra representa bien a la base completa?
  2. Repite el gráfico de atenciones por mes y proceso, pero solo para tu departamento.
  3. Calcula qué porcentaje de atenciones tiene registrado cada dispositivo. Pista: mean(atenciones$usa_glucometro) * 100.

Anexo · Chuleta del Día 1

Funciones que usamos hoy

Función Qué hace Ejemplo
<- Guarda un valor en un objeto x <- 5
c() Combina valores en un vector c(6, 7, 8, 9)
class() Tipo de un objeto class(x)
sum(), mean(), max(), round() Resúmenes numéricos mean(x, na.rm = TRUE)
is.na() ¿Es faltante? sum(is.na(x))
getwd(), list.files() Carpeta de trabajo y su contenido getwd()
install.packages() Instala un paquete (una vez) install.packages("dplyr")
library() Carga un paquete (cada sesión) library(dplyr)
read_csv(), write_csv() Importar y exportar CSV read_csv("archivo.csv")
nrow(), ncol(), names() Tamaño y nombres de una tabla nrow(atenciones)
glimpse(), head(), View() Mirar una tabla glimpse(atenciones)
%>% “Y luego” datos %>% count(x)
count() Cuenta filas por categoría count(departamento, sort = TRUE)
filter() Selecciona filas filter(mes == 8)
select() Selecciona columnas select(mes, proceso)
arrange() Ordena filas arrange(desc(n))
mutate() Crea o modifica columnas mutate(pct = 100 * n / sum(n))
group_by() + summarise() Resumen por grupo group_by(proceso) %>% summarise(n = n())
n_distinct() Cuenta valores distintos n_distinct(registro_id)
ggplot() + aes() + geom_col() Gráfico de barras ver punto 2.9
labs(), theme_minimal() Títulos y estilo del gráfico ver punto 2.9
ggsave() Guarda el último gráfico ggsave("grafico.png")

Glosario

Término Significado
Script Archivo de texto con instrucciones de R, guardado para repetirlas
Objeto Un nombre que guarda un valor, un vector o una tabla
Vector Serie de valores del mismo tipo
Data frame Tabla de datos: filas son observaciones, columnas son variables
Función Instrucción con nombre que recibe argumentos y devuelve un resultado
Argumento Dato u opción que se entrega a una función, dentro del paréntesis
Paquete Conjunto de funciones adicionales
NA Dato faltante: “no sabemos”
Pipe El operador %>%, que encadena pasos
Directorio de trabajo Carpeta donde R busca y guarda archivos
Unidad de análisis Lo que representa cada fila de una tabla
Reproducible Que otra persona puede repetir el análisis y obtener lo mismo

Lo que viene

Día 2 · Calidad, limpieza y transformación. Aprenderás a evaluar la calidad de los datos, limpiar categorías inconsistentes y dejar una base lista para construir indicadores.

LS0tDQp0aXRsZTogIkd1w61hIGRlbCBEw61hIDEgwrcgUiBkZXNkZSBjZXJvICsgSUEgY29tbyBjb3BpbG90byINCnN1YnRpdGxlOiAiUiArIEludGVsaWdlbmNpYSBBcnRpZmljaWFsIGFwbGljYWRhIGEgVGVsZXNhbHVkIg0KYXV0aG9yOiAiRHIuIEpvc2UgTHVpcyBWaWxsY2EgVmlsbGVnYXMgwrcgVU5JRlJBTlogLyBPUFMtT01TIEJvbGl2aWEiDQpkYXRlOiAiTHVuZXMgOSBkZSBub3ZpZW1icmUgZGUgMjAyNiINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZmxvYXQ6IHRydWUNCiAgICB0b2NfZGVwdGg6IDINCiAgICB0aGVtZTogZmxhdGx5DQogICAgaGlnaGxpZ2h0OiB0YW5nbw0KICAgIGRmX3ByaW50OiBwYWdlZA0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0UsDQogICAgICAgICAgICAgICAgICAgICAgZmlnLndpZHRoID0gOSwgZmlnLmhlaWdodCA9IDUsIGZpZy5hbGlnbiA9ICJjZW50ZXIiLA0KICAgICAgICAgICAgICAgICAgICAgIGNvbW1lbnQgPSAiIz4iKQ0Kb3B0aW9ucyhkcGx5ci5zdW1tYXJpc2UuaW5mb3JtID0gRkFMU0UsDQogICAgICAgIHJlcG9zID0gYyhDUkFOID0gImh0dHBzOi8vY2xvdWQuci1wcm9qZWN0Lm9yZyIpKQ0KYGBgDQoNCmBgYHs9aHRtbH0NCjxzdHlsZT4NCmgxLnRpdGxlIHsgY29sb3I6ICMwQjFBMzM7IGZvbnQtd2VpZ2h0OiA4MDA7IH0NCmgzLnN1YnRpdGxlIHsgY29sb3I6ICNFNDAzMkU7IH0NCmgxIHsgY29sb3I6ICMwQjFBMzM7IGJvcmRlci1ib3R0b206IDRweCBzb2xpZCAjRTQwMzJFOyBwYWRkaW5nLWJvdHRvbTogNnB4OyBtYXJnaW4tdG9wOiA0OHB4OyB9DQpoMiB7IGNvbG9yOiAjMjc2REMzOyBtYXJnaW4tdG9wOiAzMnB4OyB9DQpoMyB7IGNvbG9yOiAjMEIxQTMzOyB9DQouY2FqYSB7IHBhZGRpbmc6IDE0cHggMThweDsgYm9yZGVyLXJhZGl1czogMTBweDsgbWFyZ2luOiAxNnB4IDA7IH0NCi5pYSAgICAgIHsgYmFja2dyb3VuZDogI0VFRjNGQTsgYm9yZGVyLWxlZnQ6IDZweCBzb2xpZCAjMjc2REMzOyB9DQouYWxlcnRhICB7IGJhY2tncm91bmQ6ICNGRkY0RTU7IGJvcmRlci1sZWZ0OiA2cHggc29saWQgI0YyQTU0MTsgfQ0KLnByaXZhZG8geyBiYWNrZ3JvdW5kOiAjRkRFQ0VGOyBib3JkZXItbGVmdDogNnB4IHNvbGlkICNFNDAzMkU7IH0NCi5jb250cm9sIHsgYmFja2dyb3VuZDogI0U5RjdGMTsgYm9yZGVyLWxlZnQ6IDZweCBzb2xpZCAjMUZBNjdBOyB9DQoudHVybm8gICB7IGJhY2tncm91bmQ6ICNGNEYxRkI7IGJvcmRlci1sZWZ0OiA2cHggc29saWQgIzZCNEZCQjsgfQ0KZGV0YWlscyB7IG1hcmdpbjogOHB4IDAgMTZweCAwOyB9DQpzdW1tYXJ5IHsgY3Vyc29yOiBwb2ludGVyOyBjb2xvcjogIzI3NkRDMzsgZm9udC13ZWlnaHQ6IDYwMDsgfQ0Ka2JkIHsgYmFja2dyb3VuZDogI0VFRjNGQTsgYm9yZGVyOiAxcHggc29saWQgI2M5ZDJkZjsgYm9yZGVyLXJhZGl1czogNHB4OyBwYWRkaW5nOiAxcHggNnB4OyBjb2xvcjogIzBCMUEzMzsgfQ0KPC9zdHlsZT4NCmBgYA0KDQojIEPDs21vIHVzYXIgZXN0YSBndcOtYSB7LnVubnVtYmVyZWR9DQoNCkVzdGEgZ3XDrWEgYWNvbXBhw7FhIGxhIHByaW1lcmEgam9ybmFkYSBkZWwgdGFsbGVyLiBTaXJ2ZSBwYXJhIGRvcyBjb3Nhczogc2VndWlyIGxhIGNsYXNlIGVuIHZpdm8geSByZXBhc2FyIGRlc3B1w6lzIHBvciB0dSBjdWVudGEuIE5vIG5lY2VzaXRhcyBzYWJlciBwcm9ncmFtYXIuDQoNCioqUXXDqSB2YXMgYSBsb2dyYXIgaG95KioNCg0KLSBJbnN0YWxhciBSIHkgUlN0dWRpbyB5IGVudGVuZGVyIHBhcmEgcXXDqSBzaXJ2ZSBjYWRhIHVuby4NCi0gRXNjcmliaXIgeSBlamVjdXRhciB0dXMgcHJpbWVyYXMgaW5zdHJ1Y2Npb25lcyBlbiBSLg0KLSBBYnJpciBsYSBiYXNlIGRlIGRhdG9zIGRlIFRlbGVzYWx1ZCB5IHJldmlzYXJsYSBjb24gY3JpdGVyaW8uDQotIEhhY2VyIHR1cyBwcmltZXJhcyB0YWJsYXMgeSB0dSBwcmltZXIgZ3LDoWZpY28uDQotIFBlZGlybGUgY8OzZGlnbyBhIHVuYSBJQSB5LCBzb2JyZSB0b2RvLCAqKnJldmlzYXJsbyBhbnRlcyBkZSBjb25maWFyIGVuIMOpbCoqLg0KDQoqKkPDs21vIGVzdMOhIGVzY3JpdGEqKg0KDQpDYWRhIHRlbWEgc2lndWUgZWwgbWlzbW8gb3JkZW46IHByaW1lcm8gdW5hIGV4cGxpY2FjacOzbiBjb3J0YSwgbHVlZ28gdW4gYmxvcXVlIGRlIGPDs2RpZ28gcGFyYSBlamVjdXRhciB5LCBhbCBmaW5hbCwgcXXDqSBkZWJlcsOtYXMgb2JzZXJ2YXIgZW4gZWwgcmVzdWx0YWRvLiBMYXMgY2FqYXMgZGUgY29sb3JlcyBzaWduaWZpY2FuIHNpZW1wcmUgbG8gbWlzbW86DQoNCjo6OiB7LmNhamEgLmNvbnRyb2x9DQoqKlZlcmRlIMK3IENvbnRyb2wuKiogQWxnbyBxdWUgZGViZXMgY29tcHJvYmFyIGFudGVzIGRlIHNlZ3Vpci4NCjo6Og0KDQo6Ojogey5jYWphIC5hbGVydGF9DQoqKk5hcmFuamEgwrcgQXRlbmNpw7NuLioqIFVuIGVycm9yIGZyZWN1ZW50ZSBvIHVuIGRldGFsbGUgcXVlIHN1ZWxlIGNvbmZ1bmRpci4NCjo6Og0KDQo6Ojogey5jYWphIC5pYX0NCioqQXp1bCDCtyBJQS4qKiBDw7NtbyB1c2FyIHVuIGFzaXN0ZW50ZSBkZSBJQSBlbiBlc2UgcHVudG8uDQo6OjoNCg0KOjo6IHsuY2FqYSAucHJpdmFkb30NCioqUm9qbyDCtyBQcml2YWNpZGFkLioqIFJlZ2xhcyBzb2JyZSBsb3MgZGF0b3MgZGUgc2FsdWQuDQo6OjoNCg0KOjo6IHsuY2FqYSAudHVybm99DQoqKk1vcmFkbyDCtyBUdSB0dXJuby4qKiBVbiBlamVyY2ljaW8gY29ydG8sIGNvbiBsYSBzb2x1Y2nDs24gb2N1bHRhIGRlYmFqby4NCjo6Og0KDQoqKkPDs21vIGVqZWN1dGFyIGVsIGPDs2RpZ28qKg0KDQpFc3RlIGFyY2hpdm8gZXMgdW4gKlIgTWFya2Rvd24qIChgLlJtZGApOiB1biBkb2N1bWVudG8gcXVlIG1lemNsYSB0ZXh0byB5IGPDs2RpZ28uIEVuIFJTdHVkaW8sIGNhZGEgYmxvcXVlIGdyaXMgZGUgY8OzZGlnbyB0aWVuZSB1biBib3TDs24gdmVyZGUg4pa2IGFycmliYSBhIGxhIGRlcmVjaGEgcXVlIGVqZWN1dGEgZXNlIGJsb3F1ZS4gVGFtYmnDqW4gcHVlZGVzIHBvbmVyIGVsIGN1cnNvciBlbiB1bmEgbMOtbmVhIHkgcHVsc2FyIDxrYmQ+Q3RybDwva2JkPiArIDxrYmQ+RW50ZXI8L2tiZD4gKGVuIE1hYywgPGtiZD5DbWQ8L2tiZD4gKyA8a2JkPkVudGVyPC9rYmQ+KSBwYXJhIGVqZWN1dGFyIHNvbG8gZXNhIGzDrW5lYS4NCg0KQXZhbnphIGVuIG9yZGVuLCBkZSBhcnJpYmEgaGFjaWEgYWJham86IGxvcyBibG9xdWVzIHBvc3RlcmlvcmVzIHVzYW4gbG8gcXVlIGNyZWFuIGxvcyBhbnRlcmlvcmVzLiBTaSBlcyB0dSBwcmltZXJhIHZleiwgZW1waWV6YSBwb3IgbGEgUGFydGUgMC4NCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMgUGFydGUgMCDCtyBJbnN0YWxhciB5IGNvbm9jZXIgbGFzIGhlcnJhbWllbnRhcw0KDQojIyAwLjEgUiB5IFJTdHVkaW86IGRvcyBwcm9ncmFtYXMgZGlzdGludG9zDQoNCi0gKipSKiogZXMgZWwgbGVuZ3VhamU6IGVsIG1vdG9yIHF1ZSBoYWNlIGxvcyBjw6FsY3Vsb3MuDQotICoqUlN0dWRpbyoqIGVzIGVsIGVudG9ybm8gZG9uZGUgdHJhYmFqYW1vczogZWwgdGFibGVybyBkZXNkZSBlbCBxdWUgbWFuZWphbW9zIGVzZSBtb3Rvci4NCg0KU2UgaW5zdGFsYW4gbG9zIGRvcywgeSAqKmVuIGVzZSBvcmRlbioqOiBwcmltZXJvIFIsIGRlc3B1w6lzIFJTdHVkaW8uIEFtYm9zIHNvbiBncmF0dWl0b3MuDQoNCiMjIDAuMiBJbnN0YWxhciBSDQoNCjEuICBFbnRyYSBhIFsqKmh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnKipdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnKXsudXJpfQ0KMi4gIEVsaWdlIHR1IHNpc3RlbWEgb3BlcmF0aXZvOg0KICAgIC0gKipXaW5kb3dzOioqICpEb3dubG9hZCBSIGZvciBXaW5kb3dzKiDihpIgKmJhc2UqIOKGkiAqRG93bmxvYWQgUiBmb3IgV2luZG93cyouDQogICAgLSAqKk1hYzoqKiAqRG93bmxvYWQgUiBmb3IgbWFjT1MqIOKGkiBlbGlnZSBlbCBhcmNoaXZvIGAucGtnYCBxdWUgY29ycmVzcG9uZGEgYSB0dSBlcXVpcG8gKEFwcGxlIFNpbGljb24gTTEvTTIvTTMgbyBJbnRlbCkuDQogICAgLSAqKkxpbnV4OioqICpEb3dubG9hZCBSIGZvciBMaW51eCogeSBzaWd1ZSBsYXMgaW5zdHJ1Y2Npb25lcyBkZSB0dSBkaXN0cmlidWNpw7NuLg0KMy4gIEFicmUgZWwgaW5zdGFsYWRvciB5IGFjZXB0YSBsYXMgb3BjaW9uZXMgcG9yIGRlZmVjdG8gKFNpZ3VpZW50ZSwgU2lndWllbnRlLCBGaW5hbGl6YXIpLg0KDQojIyAwLjMgSW5zdGFsYXIgUlN0dWRpbw0KDQoxLiAgRW50cmEgYSBbKipodHRwczovL3Bvc2l0LmNvL2Rvd25sb2FkL3JzdHVkaW8tZGVza3RvcCoqXShodHRwczovL3Bvc2l0LmNvL2Rvd25sb2FkL3JzdHVkaW8tZGVza3RvcCl7LnVyaX0NCjIuICBEZXNjYXJnYSAqUlN0dWRpbyBEZXNrdG9wKiAobGEgcMOhZ2luYSBkZXRlY3RhIHR1IHNpc3RlbWEgb3BlcmF0aXZvKS4NCjMuICBJbnN0YWxhIGNvbiBsYXMgb3BjaW9uZXMgcG9yIGRlZmVjdG8uDQo0LiAgQWJyZSAqKlJTdHVkaW8qKiAobm8gIlIiKS4gQSBwYXJ0aXIgZGUgYWhvcmEgc2llbXByZSB0cmFiYWphcmVtb3MgZGVzZGUgUlN0dWRpby4NCg0KOjo6IHsuY2FqYSAuYWxlcnRhfQ0KKirCv05vIHB1ZWRlcyBpbnN0YWxhciBwcm9ncmFtYXMgZW4gdHUgY29tcHV0YWRvcmE/KiogVXNhICoqUG9zaXQgQ2xvdWQqKiAoPGh0dHBzOi8vcG9zaXQuY2xvdWQ+KTogZXMgUlN0dWRpbyBkZW50cm8gZGVsIG5hdmVnYWRvci4gQ3JlYSB1bmEgY3VlbnRhIGdyYXR1aXRhLCBhYnJlIHVuIHByb3llY3RvIG51ZXZvIHkgc3ViZSBsb3MgYXJjaGl2b3MgZGUgZGF0b3MgY29uIGVsIGJvdMOzbiAqVXBsb2FkKiBkZWwgcGFuZWwgKkZpbGVzKi4NCjo6Og0KDQojIyAwLjQgUmVjb3JyaWRvIHBvciBSU3R1ZGlvDQoNCkFsIGFicmlyIFJTdHVkaW8gdmVyw6FzIGxhIHZlbnRhbmEgZGl2aWRpZGEgZW4gcGFuZWxlczoNCg0KfCBQYW5lbCB8IETDs25kZSBlc3TDoSB8IFBhcmEgcXXDqSBzaXJ2ZSB8DQp8LS0tLXwtLS0tfC0tLS18DQp8ICoqRWRpdG9yIChTb3VyY2UpKiogfCBBcnJpYmEgYSBsYSBpenF1aWVyZGEgfCBBcXXDrSBzZSBlc2NyaWJlbiB5IGd1YXJkYW4gbG9zIHNjcmlwdHMgeSBkb2N1bWVudG9zIGNvbW8gZXN0ZSB8DQp8ICoqQ29uc29sYSAoQ29uc29sZSkqKiB8IEFiYWpvIGEgbGEgaXpxdWllcmRhIHwgQXF1w60gUiByZXNwb25kZTogcmVzdWx0YWRvcywgYXZpc29zIHkgZXJyb3JlcyB8DQp8ICoqRW52aXJvbm1lbnQqKiB8IEFycmliYSBhIGxhIGRlcmVjaGEgfCBMaXN0YSBkZSBsb3Mgb2JqZXRvcyBxdWUgaGFzIGNyZWFkbyAodHVzIGJhc2VzLCB0YWJsYXMsIHZhbG9yZXMpIHwNCnwgKipGaWxlcyAvIFBsb3RzIC8gSGVscCoqIHwgQWJham8gYSBsYSBkZXJlY2hhIHwgQXJjaGl2b3MgZGUgdHUgY2FycGV0YSwgZ3LDoWZpY29zIHkgcMOhZ2luYXMgZGUgYXl1ZGEgfA0KDQpTaSBubyB2ZXMgZWwgZWRpdG9yLCBjcmVhIHVuIGFyY2hpdm8gbnVldm8gY29uICpGaWxlIOKAuiBOZXcgRmlsZSDigLogUiBTY3JpcHQqLg0KDQoqKkF0YWpvcyBkZSB0ZWNsYWRvIHF1ZSBtw6FzIHVzYXLDoXMqKg0KDQp8IEF0YWpvIChXaW5kb3dzIC8gTGludXgpIHwgRW4gTWFjIHwgUXXDqSBoYWNlIHwNCnwtLS0tfC0tLS18LS0tLXwNCnwgPGtiZD5DdHJsPC9rYmQ+ICsgPGtiZD5FbnRlcjwva2JkPiB8IDxrYmQ+Q21kPC9rYmQ+ICsgPGtiZD5FbnRlcjwva2JkPiB8IEVqZWN1dGEgbGEgbMOtbmVhIGRvbmRlIGVzdMOhIGVsIGN1cnNvciB8DQp8IDxrYmQ+QWx0PC9rYmQ+ICsgPGtiZD4tPC9rYmQ+IHwgPGtiZD5PcHRpb248L2tiZD4gKyA8a2JkPi08L2tiZD4gfCBFc2NyaWJlIGxhIGZsZWNoYSBkZSBhc2lnbmFjacOzbiBgPC1gIHwNCnwgPGtiZD5DdHJsPC9rYmQ+ICsgPGtiZD5TaGlmdDwva2JkPiArIDxrYmQ+TTwva2JkPiB8IDxrYmQ+Q21kPC9rYmQ+ICsgPGtiZD5TaGlmdDwva2JkPiArIDxrYmQ+TTwva2JkPiB8IEVzY3JpYmUgZWwgcGlwZSBgJT4lYCB8DQp8IDxrYmQ+Q3RybDwva2JkPiArIDxrYmQ+Uzwva2JkPiB8IDxrYmQ+Q21kPC9rYmQ+ICsgPGtiZD5TPC9rYmQ+IHwgR3VhcmRhIGVsIGFyY2hpdm8gfA0KfCA8a2JkPlRhYjwva2JkPiB8IDxrYmQ+VGFiPC9rYmQ+IHwgQXV0b2NvbXBsZXRhIG5vbWJyZXMgfA0KfCA8a2JkPkYxPC9rYmQ+IHwgPGtiZD5GMTwva2JkPiB8IEFicmUgbGEgYXl1ZGEgZGUgbGEgZnVuY2nDs24gYmFqbyBlbCBjdXJzb3IgfA0KDQojIyAwLjUgUHJlcGFyYXIgbGEgY2FycGV0YSBkZSB0cmFiYWpvDQoNClIgbmVjZXNpdGEgc2FiZXIgKiplbiBxdcOpIGNhcnBldGEqKiBlc3TDoW4gdHVzIGFyY2hpdm9zLiBMYSBmb3JtYSBtw6FzIG9yZGVuYWRhIGVzIGNyZWFyIHVuICpwcm95ZWN0byo6DQoNCjEuICBEZXNjb21wcmltZSBlbCBaSVAgZGUgZGF0b3MgZGVsIHRhbGxlciBlbiB1bmEgY2FycGV0YSwgcG9yIGVqZW1wbG8gYHRlbGVzYWx1ZF90YWxsZXJgLg0KMi4gIENvcGlhIGVzdGEgZ3XDrWEgKGAuUm1kYCkgZGVudHJvIGRlICoqZXNhIG1pc21hIGNhcnBldGEqKi4NCjMuICBFbiBSU3R1ZGlvOiAqRmlsZSDigLogTmV3IFByb2plY3Qg4oC6IEV4aXN0aW5nIERpcmVjdG9yeSog4oaSIGVsaWdlIGxhIGNhcnBldGEg4oaSICpDcmVhdGUgUHJvamVjdCouDQo0LiAgRW4gZWwgcGFuZWwgKkZpbGVzKiAoYWJham8gYSBsYSBkZXJlY2hhKSBkZWJlcsOtYXMgdmVyIGxvcyBhcmNoaXZvcyBgLmNzdmAgeSBlc3RhIGd1w61hLiDDgWJyZWxhIGRlc2RlIGFow60uDQoNClVuIHByb3llY3RvIGhhY2UgcXVlIFIgdHJhYmFqZSBzaWVtcHJlIGRlbnRybyBkZSBlc2EgY2FycGV0YSwgc2luIHRlbmVyIHF1ZSBlc2NyaWJpciBydXRhcyBsYXJnYXMgY29tbyBgQzovVXNlcnMvLi4uYC4NCg0KIyMgMC42IENvbXByb2JhciBxdWUgdG9kbyBmdW5jaW9uYQ0KDQpFamVjdXRhIGVzdGUgYmxvcXVlLiBFcyB0dSBwcmltZXJhIGluc3RydWNjacOzbiBlbiBSLg0KDQpgYGB7ciBwMC12ZXJzaW9ufQ0KUi52ZXJzaW9uLnN0cmluZw0KYGBgDQoNCkRlYmVyw61hcyB2ZXIgdW4gdGV4dG8gY29uIGxhIHZlcnNpw7NuIGRlIFIgaW5zdGFsYWRhLiBDdWFscXVpZXIgdmVyc2nDs24gNC54IHNpcnZlIHBhcmEgZXN0ZSB0YWxsZXIuDQoNCkFob3JhIHByZWd1bnRlbW9zIGEgUiBlbiBxdcOpIGNhcnBldGEgZXN0w6EgdHJhYmFqYW5kbyB5IHF1w6kgYXJjaGl2b3MgdmUgYWjDrToNCg0KYGBge3IgcDAtY2FycGV0YX0NCmdldHdkKCkgICAgICAgICMgZ2V0IHdvcmtpbmcgZGlyZWN0b3J5OiBsYSBjYXJwZXRhIGRlIHRyYWJham8gYWN0dWFsDQpsaXN0LmZpbGVzKCkgICAjIGxvcyBhcmNoaXZvcyBxdWUgaGF5IGVuIGVzYSBjYXJwZXRhDQpgYGANCg0KYGdldHdkKClgIGRldnVlbHZlIGxhIHJ1dGEgZGUgbGEgY2FycGV0YS4gYGxpc3QuZmlsZXMoKWAgbXVlc3RyYSBzdSBjb250ZW5pZG8uIEVuIGVzYSBsaXN0YSBkZWJlbiBhcGFyZWNlciBsb3MgYXJjaGl2b3MgZGVsIHRhbGxlci4NCg0KYGBge3IgcDAtYXJjaGl2b3N9DQpmaWxlLmV4aXN0cyhjKCJhdGVuY2lvbmVzX211ZXN0cmFfZG9jZW50ZV8xMDAwMC5jc3YiLA0KICAgICAgICAgICAgICAidGVsZWVkdWNhY2lvbl9tZW5zdWFsLmNzdiIsDQogICAgICAgICAgICAgICJkaWNjaW9uYXJpb192YXJpYWJsZXMuY3N2IikpDQpgYGANCg0KOjo6IHsuY2FqYSAuY29udHJvbH0NCioqQ29udHJvbC4qKiBMb3MgdHJlcyB2YWxvcmVzIGRlYmVuIHNlciBgVFJVRWAuIFNpIGFsZ3VubyBlcyBgRkFMU0VgLCBSIGVzdMOhIG1pcmFuZG8gb3RyYSBjYXJwZXRhOiB2dWVsdmUgYWwgcHVudG8gMC41LiBTb2x1Y2nDs24gcsOhcGlkYTogKlNlc3Npb24g4oC6IFNldCBXb3JraW5nIERpcmVjdG9yeSDigLogVG8gU291cmNlIEZpbGUgTG9jYXRpb24qLg0KOjo6DQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIFBhcnRlIDEgwrcgRnVuZGFtZW50b3MgZGUgUg0KDQpFbiBlc3RhIHBhcnRlIG5vIHVzYW1vcyB0b2RhdsOtYSBsYSBiYXNlIGRlIGRhdG9zIGNvbXBsZXRhLiBBcHJlbmRlbW9zIGxhcyBwaWV6YXMgYsOhc2ljYXMgZGVsIGxlbmd1YWplIGNvbiBlamVtcGxvcyBwZXF1ZcOxb3MsIGNhc2kgdG9kb3MgY29uIGNpZnJhcyByZWFsZXMgZGUgVGVsZXNhbHVkLg0KDQojIyAxLjEgUiBjb21vIGNhbGN1bGFkb3JhDQoNCkxvIG3DoXMgc2ltcGxlIHF1ZSBoYWNlIFIgZXMgY2FsY3VsYXIuIEVqZWN1dGEgY2FkYSBsw61uZWEgeSBtaXJhIGxhIGNvbnNvbGEuDQoNCmBgYHtyIGYtY2FsY3VsYWRvcmF9DQoyICsgMg0KMjk1NDggKyAzMjg3NyAgICAgICAgIyBhdGVuY2lvbmVzIGRlIGp1bmlvICsgYXRlbmNpb25lcyBkZSBqdWxpbw0KMTA2MzIxIC8gMTU4MDQ2ICAgICAgIyBhdGVuY2lvbmVzIGRlIG11amVyZXMgZGl2aWRpZG8gcG9yIGVsIHRvdGFsDQpgYGANCg0KRWwgYFsxXWAgcXVlIGFwYXJlY2UgYW50ZXMgZGUgY2FkYSByZXN1bHRhZG8gc29sbyBpbmRpY2EgcXVlIGVzIGVsIHByaW1lciBlbGVtZW50byBkZSBsYSByZXNwdWVzdGEuIFB1ZWRlcyBpZ25vcmFybG8uDQoNClRvZG8gbG8gcXVlIHZhIGRlc3B1w6lzIGRlIHVuIGAjYCBlcyB1biAqKmNvbWVudGFyaW8qKjogUiBubyBsbyBlamVjdXRhLiBTaXJ2ZSBwYXJhIGV4cGxpY2FyIHF1w6kgaGFjZSBlbCBjw7NkaWdvLiBDb21lbnRhciBiaWVuIGVzIHBhcnRlIGRlIGVzY3JpYmlyIGJ1ZW4gY8OzZGlnby4NCg0KIyMgMS4yIE9iamV0b3M6IGd1YXJkYXIgdmFsb3JlcyBjb24gdW4gbm9tYnJlDQoNCkNhbGN1bGFyIHkgcGVyZGVyIGVsIHJlc3VsdGFkbyBubyBzaXJ2ZSBkZSBtdWNoby4gRW4gUiBndWFyZGFtb3MgbG9zIHZhbG9yZXMgZW4gKipvYmpldG9zKiosIHF1ZSBzb24gbm9tYnJlcyBxdWUgYXB1bnRhbiBhIHVuIHZhbG9yLiBTZSBjcmVhbiBjb24gbGEgZmxlY2hhIGA8LWAsIHF1ZSBzZSBsZWUgInJlY2liZSIuDQoNCmBgYHtyIGYtb2JqZXRvc30NCmF0ZW5jaW9uZXNfanVuaW8gPC0gMjk1NDggICAgICMgYXRlbmNpb25lc19qdW5pbyAicmVjaWJlIiAyOTU0OA0KYXRlbmNpb25lc19qdWxpbyA8LSAzMjg3Nw0KYGBgDQoNCkVzdGFzIGzDrW5lYXMgbm8gbXVlc3RyYW4gbmFkYSBlbiBsYSBjb25zb2xhOiBzb2xvIGd1YXJkYW4uIE1pcmEgZWwgcGFuZWwgKipFbnZpcm9ubWVudCoqOiBhaMOtIGFwYXJlY2llcm9uIGxvcyBkb3Mgb2JqZXRvcy4gUGFyYSB2ZXIgc3UgdmFsb3IsIGVzY3JpYmUgc3Ugbm9tYnJlOg0KDQpgYGB7ciBmLW9iamV0b3MyfQ0KYXRlbmNpb25lc19qdW5pbw0KYGBgDQoNClkgbG8gbcOhcyDDunRpbDogbG9zIG9iamV0b3Mgc2UgcHVlZGVuIHVzYXIgZW4gY8OhbGN1bG9zIHkgZ3VhcmRhciBlbCByZXN1bHRhZG8gZW4gb3RybyBvYmpldG8uDQoNCmBgYHtyIGYtb2JqZXRvczN9DQp0b3RhbF9qdW5fanVsIDwtIGF0ZW5jaW9uZXNfanVuaW8gKyBhdGVuY2lvbmVzX2p1bGlvDQp0b3RhbF9qdW5fanVsDQpgYGANCg0KKipSZWdsYXMgcGFyYSBwb25lciBub21icmVzKioNCg0KLSBTaW4gZXNwYWNpb3MgbmkgdGlsZGVzLiBVc2EgZ3Vpb24gYmFqbzogYGF0ZW5jaW9uZXNfanVuaW9gIChlc3RlIGVzdGlsbyBzZSBsbGFtYSAqc25ha2VfY2FzZSopLg0KLSBObyBwdWVkZW4gZW1wZXphciBjb24gdW4gbsO6bWVyby4NCi0gUiBkaXN0aW5ndWUgbWF5w7pzY3VsYXMgZGUgbWluw7pzY3VsYXM6IGB0b3RhbGAgeSBgVG90YWxgIHNvbiBvYmpldG9zIGRpc3RpbnRvcy4NCg0KVmVhbW9zIGVzdG8gw7psdGltby4gRWwgc2lndWllbnRlIGJsb3F1ZSBwcm9kdWNlIHVuIGVycm9yIGEgcHJvcMOzc2l0bzoNCg0KYGBge3IgZi1lcnJvciwgZXJyb3I9VFJVRX0NClRvdGFsX2p1bl9qdWwNCmBgYA0KDQo6Ojogey5jYWphIC5hbGVydGF9DQoqKkxlZXIgZXJyb3JlcyBlcyB1bmEgaGFiaWxpZGFkLioqIGBvYmplY3QgJ1RvdGFsX2p1bl9qdWwnIG5vdCBmb3VuZGAgc2lnbmlmaWNhICJubyBleGlzdGUgbmluZ8O6biBvYmpldG8gY29uIGVzZSBub21icmUiLiBDYXNpIHNpZW1wcmUgZXMgdW4gZXJyb3IgZGUgdGlwZW8gbyB1bmEgbWF5w7pzY3VsYS4gVW4gZXJyb3Igbm8gcm9tcGUgbmFkYTogY29ycmlnZXMgeSB2dWVsdmVzIGEgZWplY3V0YXIuDQo6OjoNCg0KOjo6IHsuY2FqYSAudHVybm99DQoqKlR1IHR1cm5vLioqIEVuIGFnb3N0byBodWJvIDUxNzUzIGF0ZW5jaW9uZXMgeSBlbiBzZXB0aWVtYnJlIDQzODY4LiBDcmVhIGRvcyBvYmpldG9zIGNvbiBlc29zIHZhbG9yZXMgeSB1biB0ZXJjZXJvIGxsYW1hZG8gYHRvdGFsX2Fnb19zZXBgIGNvbiBsYSBzdW1hLg0KDQo8ZGV0YWlscz4NCg0KPHN1bW1hcnk+VmVyIHNvbHVjacOzbjwvc3VtbWFyeT4NCg0KYGBgIHINCmF0ZW5jaW9uZXNfYWdvc3RvICAgICA8LSA1MTc1Mw0KYXRlbmNpb25lc19zZXB0aWVtYnJlIDwtIDQzODY4DQp0b3RhbF9hZ29fc2VwIDwtIGF0ZW5jaW9uZXNfYWdvc3RvICsgYXRlbmNpb25lc19zZXB0aWVtYnJlDQp0b3RhbF9hZ29fc2VwDQojPiBbMV0gOTU2MjENCmBgYA0KDQo8L2RldGFpbHM+DQo6OjoNCg0KIyMgMS4zIFRpcG9zIGRlIGRhdG9zDQoNCk5vIHRvZG9zIGxvcyBkYXRvcyBzb24gbsO6bWVyb3MuIFIgZGlzdGluZ3VlIHZhcmlvcyAqKnRpcG9zKiosIHkgc2FiZXIgY3XDoWwgdGllbmVzIGltcG9ydGEgcG9ycXVlIGNhZGEgdGlwbyBwZXJtaXRlIG9wZXJhY2lvbmVzIGRpc3RpbnRhcy4NCg0KYGBge3IgZi10aXBvc30NCm1lcyAgICAgICAgICAgIDwtIDggICAgICAgICAgICAgICAjIG51bcOpcmljbyAobnVtZXJpYykNCmRlcGFydGFtZW50byAgIDwtICJDT0NIQUJBTUJBIiAgICAjIHRleHRvIChjaGFyYWN0ZXIpOiBzaWVtcHJlIGVudHJlIGNvbWlsbGFzDQp1c2FfZ2x1Y29tZXRybyA8LSBUUlVFICAgICAgICAgICAgIyBsw7NnaWNvIChsb2dpY2FsKTogVFJVRSBvIEZBTFNFDQoNCmNsYXNzKG1lcykNCmNsYXNzKGRlcGFydGFtZW50bykNCmNsYXNzKHVzYV9nbHVjb21ldHJvKQ0KYGBgDQoNCkxhIGZ1bmNpw7NuIGBjbGFzcygpYCB0ZSBkaWNlIGRlIHF1w6kgdGlwbyBlcyB1biBvYmpldG8uDQoNCnwgVGlwbyB8IEVqZW1wbG8gfCBFbiBsYSBiYXNlIGRlIFRlbGVzYWx1ZCB8DQp8LS0tLXwtLS0tfC0tLS18DQp8IE51bcOpcmljbyAoYG51bWVyaWNgLCBgaW50ZWdlcmApIHwgYDhgLCBgMjk1NDhgIHwgYG1lc2AsIGBuX2RpYWdub3N0aWNvc2AgfA0KfCBUZXh0byAoYGNoYXJhY3RlcmApIHwgYCJDT0NIQUJBTUJBImAgfCBgZGVwYXJ0YW1lbnRvYCwgYHByb2Nlc29gLCBgc2V4b2AgfA0KfCBMw7NnaWNvIChgbG9naWNhbGApIHwgYFRSVUVgLCBgRkFMU0VgIHwgcmVzdWx0YWRvIGRlIHByZWd1bnRhcyBjb21vICLCv2VzIG11amVyPyIgfA0KfCBGYWN0b3IgKGBmYWN0b3JgKSB8IHRleHRvIGNvbiBjYXRlZ29yw61hcyBmaWphcyB8IGxvIHVzYXJlbW9zIG1hw7FhbmEgcGFyYSBgZ3J1cG9fZWRhZGAgfA0KDQo6Ojogey5jYWphIC5hbGVydGF9DQoqKkNvbWlsbGFzLioqIGAiOCJgIGNvbiBjb21pbGxhcyBlcyB1biB0ZXh0bywgbm8gdW4gbsO6bWVybzogbm8gc2UgcHVlZGUgc3VtYXIuIFkgdW4gdGV4dG8gc2luIGNvbWlsbGFzLCBjb21vIGBDT0NIQUJBTUJBYCwgUiBsbyBpbnRlcnByZXRhIGNvbW8gZWwgbm9tYnJlIGRlIHVuIG9iamV0byB5IGRhcsOhIGVycm9yIHNpIG5vIGV4aXN0ZS4NCjo6Og0KDQojIyAxLjQgTkE6IGVsIGRhdG8gZmFsdGFudGUNCg0KYE5BYCAoKk5vdCBBdmFpbGFibGUqKSBzaWduaWZpY2EgKioibm8gc2FiZW1vcyIqKi4gRXMgdW5vIGRlIGxvcyBjb25jZXB0b3MgbcOhcyBpbXBvcnRhbnRlcyBhbCB0cmFiYWphciBjb24gZGF0b3MgZGUgc2FsdWQsIHBvcnF1ZSB1biBkYXRvIGZhbHRhbnRlICoqbm8gZXMgdW4gY2VybyoqLg0KDQpgYGB7ciBmLW5hfQ0KcGVzb3MgPC0gYyg2MiwgTkEsIDc1KSAgICAjIGVsIHNlZ3VuZG8gcGVzbyBubyBzZSByZWdpc3Ryw7MNCg0Kc3VtKHBlc29zKSAgICAgICAgICAgICAgICAjIMK/Y3XDoW50byBzdW1hbj8NCmBgYA0KDQpFbCByZXN1bHRhZG8gZXMgYE5BYC4gVGllbmUgc2VudGlkbzogc2kgbm8gY29ub2NlbW9zIHVuIHZhbG9yLCBubyBjb25vY2Vtb3MgbGEgc3VtYS4gUiBub3Mgb2JsaWdhIGEgZGVjaWRpciBxdcOpIGhhY2VyOg0KDQpgYGB7ciBmLW5hMn0NCnN1bShwZXNvcywgbmEucm0gPSBUUlVFKSAgICAgIyBzdW1hIGlnbm9yYW5kbyBsb3MgZmFsdGFudGVzDQptZWFuKHBlc29zLCBuYS5ybSA9IFRSVUUpICAgICMgcHJvbWVkaW8gZGUgbG9zIHZhbG9yZXMgY29ub2NpZG9zDQppcy5uYShwZXNvcykgICAgICAgICAgICAgICAgICMgwr9jdcOhbGVzIHNvbiBmYWx0YW50ZXM/DQpzdW0oaXMubmEocGVzb3MpKSAgICAgICAgICAgICMgwr9jdcOhbnRvcyBmYWx0YW50ZXMgaGF5Pw0KYGBgDQoNCmBuYS5ybSA9IFRSVUVgIHNpZ25pZmljYSAicXVpdGEgbG9zIE5BIGFudGVzIGRlIGNhbGN1bGFyIiAoKk5BIHJlbW92ZSopLiBgaXMubmEoKWAgcHJlZ3VudGEsIHZhbG9yIHBvciB2YWxvciwgc2kgZXMgZmFsdGFudGUuDQoNCjo6OiB7LmNhamEgLmFsZXJ0YX0NCioqUmVnbGEgZGUgb3JvLioqIE51bmNhIHJlZW1wbGFjZXMgdW4gYE5BYCBwb3IgMCBzaW4gdW5hIHJhesOzbiBleHBsw61jaXRhLiAiTm8gc2UgcmVnaXN0csOzIGVsIHBlc28iIHkgInBlc2EgY2VybyBraWxvcyIgc29uIGFmaXJtYWNpb25lcyBtdXkgZGlzdGludGFzLg0KOjo6DQoNCiMjIDEuNSBWZWN0b3JlczogdmFyaW9zIHZhbG9yZXMganVudG9zDQoNClVuICoqdmVjdG9yKiogZXMgdW5hIHNlcmllIGRlIHZhbG9yZXMgZGVsIG1pc21vIHRpcG8uIFNlIGNyZWEgY29uIGxhIGZ1bmNpw7NuIGBjKClgIChkZSAqY29tYmluYXIqKS4gWWEgdXNhc3RlIHVubyBlbiBlbCBibG9xdWUgYW50ZXJpb3IuDQoNCmBgYHtyIGYtdmVjdG9yZXN9DQptZXNlcyAgICAgICAgICA8LSBjKDYsIDcsIDgsIDkpDQphdGVuY2lvbmVzX21lcyA8LSBjKDI5NTQ4LCAzMjg3NywgNTE3NTMsIDQzODY4KSAgICMgYXRlbmNpb25lcyBwb3IgbWVzLCBiYXNlIGNvbXBsZXRhDQpub21icmVzX21lcyAgICA8LSBjKCJKdW5pbyIsICJKdWxpbyIsICJBZ29zdG8iLCAiU2VwdGllbWJyZSIpDQpgYGANCg0KQ29uIHVuIHZlY3RvciBwdWVkZXMgaGFjZXIgcmVzw7ptZW5lczoNCg0KYGBge3IgZi12ZWN0b3JlczJ9DQpsZW5ndGgoYXRlbmNpb25lc19tZXMpICAgICMgY3XDoW50b3MgZWxlbWVudG9zIHRpZW5lDQpzdW0oYXRlbmNpb25lc19tZXMpICAgICAgICMgdG90YWw6IGxhcyAxNTguMDQ2IGF0ZW5jaW9uZXMgZGUgbGEgYmFzZQ0KbWVhbihhdGVuY2lvbmVzX21lcykgICAgICAjIHByb21lZGlvIG1lbnN1YWwNCm1heChhdGVuY2lvbmVzX21lcykgICAgICAgIyBlbCB2YWxvciBtw6FzIGFsdG8NCmBgYA0KDQpZIHRhbWJpw6luIG9wZXJhciBjb24gdG9kb3Mgc3VzIGVsZW1lbnRvcyBhIGxhIHZlei4gQXF1w60gY2FsY3VsYW1vcyBxdcOpIHBvcmNlbnRhamUgZGVsIHRvdGFsIHJlcHJlc2VudGEgY2FkYSBtZXM6DQoNCmBgYHtyIGYtdmVjdG9yZXMzfQ0KcG9yY2VudGFqZV9tZXMgPC0gcm91bmQoMTAwICogYXRlbmNpb25lc19tZXMgLyBzdW0oYXRlbmNpb25lc19tZXMpLCAxKQ0KcG9yY2VudGFqZV9tZXMNCmBgYA0KDQpSIGRpdmlkacOzIGNhZGEgbWVzIHBvciBlbCB0b3RhbCwgbXVsdGlwbGljw7MgcG9yIDEwMCB5IHJlZG9uZGXDsyBhIHVuIGRlY2ltYWwuIEFnb3N0byBjb25jZW50cmEgY2FzaSB1biB0ZXJjaW8gZGUgbGFzIGF0ZW5jaW9uZXMuDQoNClBhcmEgc2FjYXIgdW4gZWxlbWVudG8gY29uY3JldG8gc2UgdXNhbiBjb3JjaGV0ZXMgY29uIHN1IHBvc2ljacOzbjoNCg0KYGBge3IgZi12ZWN0b3JlczR9DQphdGVuY2lvbmVzX21lc1szXSAgICAgIyBlbCB0ZXJjZXIgZWxlbWVudG86IGFnb3N0bw0Kbm9tYnJlc19tZXNbM10NCmBgYA0KDQojIyAxLjYgRnVuY2lvbmVzIHkgYXJndW1lbnRvcw0KDQpZYSB1c2FzdGUgdmFyaWFzICoqZnVuY2lvbmVzKio6IGBzdW0oKWAsIGBtZWFuKClgLCBgcm91bmQoKWAsIGBjbGFzcygpYC4gVW5hIGZ1bmNpw7NuIGVzIHVuYSBpbnN0cnVjY2nDs24gY29uIG5vbWJyZSBxdWUgcmVjaWJlIGRhdG9zIGVudHJlIHBhcsOpbnRlc2lzIHkgZGV2dWVsdmUgdW4gcmVzdWx0YWRvLg0KDQpMbyBxdWUgdmEgZGVudHJvIGRlbCBwYXLDqW50ZXNpcyBzb24gbG9zICoqYXJndW1lbnRvcyoqLCBzZXBhcmFkb3MgcG9yIGNvbWFzLg0KDQpgYGB7ciBmLWZ1bmNpb25lc30NCnJvdW5kKDMyLjcxODMpICAgICAgICAgICAgICAgIyB1biBhcmd1bWVudG86IHJlZG9uZGVhIGEgZW50ZXJvDQpyb3VuZCgzMi43MTgzLCBkaWdpdHMgPSAxKSAgICMgZG9zIGFyZ3VtZW50b3M6IHJlZG9uZGVhIGEgMSBkZWNpbWFsDQpyb3VuZCgzMi43MTgzLCAyKSAgICAgICAgICAgICMgZWwgbm9tYnJlIGRlbCBhcmd1bWVudG8gc2UgcHVlZGUgb21pdGlyIHNpIHJlc3BldGFzIGVsIG9yZGVuDQpgYGANCg0Kwr9Dw7NtbyBzYWJlciBxdcOpIGFyZ3VtZW50b3MgYWNlcHRhIHVuYSBmdW5jacOzbj8gQ29uIGxhIGF5dWRhOg0KDQpgYGB7ciBmLWF5dWRhLCBldmFsPUZBTFNFfQ0KP3JvdW5kICAgICAgICAgICMgYWJyZSBsYSBww6FnaW5hIGRlIGF5dWRhIGRlIHJvdW5kKCkNCmBgYA0KDQpUYW1iacOpbiBwdWVkZXMgcG9uZXIgZWwgY3Vyc29yIHNvYnJlIGVsIG5vbWJyZSBkZSBsYSBmdW5jacOzbiB5IHB1bHNhciA8a2JkPkYxPC9rYmQ+Lg0KDQojIyAxLjcgRGF0YSBmcmFtZXM6IGxhcyB0YWJsYXMgZGUgUg0KDQpVbiAqKmRhdGEgZnJhbWUqKiBlcyB1bmEgdGFibGE6IGZpbGFzIHkgY29sdW1uYXMsIGNvbW8gdW5hIGhvamEgZGUgY8OhbGN1bG8sIHBlcm8gY29uIHJlZ2xhcy4NCg0KLSBDYWRhICoqZmlsYSoqIGVzIHVuYSBvYnNlcnZhY2nDs24uDQotIENhZGEgKipjb2x1bW5hKiogZXMgdW5hIHZhcmlhYmxlLCB5IHRvZG9zIHN1cyB2YWxvcmVzIHNvbiBkZWwgbWlzbW8gdGlwby4NCi0gQ2FkYSAqKmNlbGRhKiogdGllbmUgdW4gc29sbyB2YWxvciAobyBgTkFgKS4NCg0KUG9kZW1vcyBjb25zdHJ1aXIgdW5vIHBlcXVlw7FvIHVuaWVuZG8gbG9zIHZlY3RvcmVzIGRlIGFudGVzOg0KDQpgYGB7ciBmLWRmfQ0KcmVzdW1lbl9tZW5zdWFsIDwtIGRhdGEuZnJhbWUoDQogIG1lcyAgICAgICAgPSBtZXNlcywNCiAgbm9tYnJlX21lcyA9IG5vbWJyZXNfbWVzLA0KICBhdGVuY2lvbmVzID0gYXRlbmNpb25lc19tZXMsDQogIHBvcmNlbnRhamUgPSBwb3JjZW50YWplX21lcw0KKQ0KDQpyZXN1bWVuX21lbnN1YWwNCmBgYA0KDQpDYWRhIHZlY3RvciBzZSBjb252aXJ0acOzIGVuIHVuYSBjb2x1bW5hLiBQYXJhIGV4cGxvcmFyIHVuYSB0YWJsYSBoYXkgdW5hcyBwb2NhcyBmdW5jaW9uZXMgcXVlIHVzYXLDoXMgc2llbXByZToNCg0KYGBge3IgZi1kZjJ9DQpucm93KHJlc3VtZW5fbWVuc3VhbCkgICAgICMgbsO6bWVybyBkZSBmaWxhcw0KbmNvbChyZXN1bWVuX21lbnN1YWwpICAgICAjIG7Dum1lcm8gZGUgY29sdW1uYXMNCm5hbWVzKHJlc3VtZW5fbWVuc3VhbCkgICAgIyBub21icmVzIGRlIGxhcyBjb2x1bW5hcw0KYGBgDQoNClBhcmEgZXh0cmFlciB1bmEgY29sdW1uYSBzZSB1c2EgZWwgc2lnbm8gYCRgOg0KDQpgYGB7ciBmLWRmM30NCnJlc3VtZW5fbWVuc3VhbCRhdGVuY2lvbmVzDQpzdW0ocmVzdW1lbl9tZW5zdWFsJGF0ZW5jaW9uZXMpDQpgYGANCg0KTGEgYmFzZSBkZSBUZWxlc2FsdWQgcXVlIGFicmlyZW1vcyBlbiBsYSBQYXJ0ZSAyIGVzIGV4YWN0YW1lbnRlIGVzdG86IHVuIGRhdGEgZnJhbWUsIHNvbG8gcXVlIGNvbiBtaWxlcyBkZSBmaWxhcyB5IDMzIGNvbHVtbmFzLg0KDQojIyAxLjggUGFxdWV0ZXM6IGFtcGxpYXIgbG8gcXVlIFIgc2FiZSBoYWNlcg0KDQpSIHRyYWUgZnVuY2lvbmVzIGLDoXNpY2FzLiBMb3MgKipwYXF1ZXRlcyoqIGFncmVnYW4gZnVuY2lvbmVzIG51ZXZhcyBoZWNoYXMgcG9yIGxhIGNvbXVuaWRhZC4gSG95IHVzYW1vcyB0cmVzOg0KDQp8IFBhcXVldGUgICB8IFBhcmEgcXXDqSBzaXJ2ZSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8LS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwNCnwgYHJlYWRyYCAgIHwgSW1wb3J0YXIgYXJjaGl2b3MgKENTVikgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCBgZHBseXJgICAgfCBUcmFuc2Zvcm1hciB0YWJsYXM6IGNvbnRhciwgZmlsdHJhciwgb3JkZW5hciwgcmVzdW1pciB8DQp8IGBnZ3Bsb3QyYCB8IEhhY2VyIGdyw6FmaWNvcyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQoNCkNvbiBsb3MgcGFxdWV0ZXMgaGF5IGRvcyBwYXNvcywgeSBjb252aWVuZSBubyBjb25mdW5kaXJsb3M6DQoNCi0gKipJbnN0YWxhcioqIChgaW5zdGFsbC5wYWNrYWdlcygpYCk6IHNlIGhhY2UgKip1bmEgc29sYSB2ZXoqKiBwb3IgY29tcHV0YWRvcmEuIE5lY2VzaXRhIGludGVybmV0Lg0KLSAqKkNhcmdhcioqIChgbGlicmFyeSgpYCk6IHNlIGhhY2UgKipjYWRhIHZleioqIHF1ZSBhYnJlcyBSLg0KDQpVbmEgYW5hbG9nw61hOiBpbnN0YWxhciBlcyBjb21wcmFyIHVuIGxpYnJvOyBjYXJnYXIgZXMgc2FjYXJsbyBkZWwgZXN0YW50ZSBwYXJhIHVzYXJsby4NCg0KRWwgc2lndWllbnRlIGJsb3F1ZSBpbnN0YWxhIHNvbG8gbG9zIHBhcXVldGVzIHF1ZSB0ZSBmYWx0ZW4uIExhIHByaW1lcmEgdmV6IHB1ZWRlIHRhcmRhciB1bm9zIG1pbnV0b3MuDQoNCmBgYHtyIGYtaW5zdGFsYXJ9DQpwYXF1ZXRlcyA8LSBjKCJyZWFkciIsICJkcGx5ciIsICJnZ3Bsb3QyIikNCmZhbHRhbiAgIDwtIHBhcXVldGVzWyFwYXF1ZXRlcyAlaW4lIHJvd25hbWVzKGluc3RhbGxlZC5wYWNrYWdlcygpKV0NCmlmIChsZW5ndGgoZmFsdGFuKSA+IDApIGluc3RhbGwucGFja2FnZXMoZmFsdGFuKQ0KYGBgDQoNClkgZXN0ZSBsb3MgY2FyZ2E6DQoNCmBgYHtyIGYtY2FyZ2FyfQ0KbGlicmFyeShyZWFkcikNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KGdncGxvdDIpDQpgYGANCg0KOjo6IHsuY2FqYSAuYWxlcnRhfQ0KKipFcnJvciB0w61waWNvLioqIGB0aGVyZSBpcyBubyBwYWNrYWdlIGNhbGxlZCAnZHBseXInYCBzaWduaWZpY2EgcXVlIGZhbHRhIGluc3RhbGFybG8uIGBjb3VsZCBub3QgZmluZCBmdW5jdGlvbiAiJT4lImAgc2lnbmlmaWNhIHF1ZSBlc3TDoSBpbnN0YWxhZG8gcGVybyBubyBsbyBjYXJnYXN0ZSBjb24gYGxpYnJhcnkoZHBseXIpYCBlbiBlc3RhIHNlc2nDs24uDQo6OjoNCg0KIyMgMS45IEVsIHBpcGUgYCU+JWA6IGVuY2FkZW5hciBwYXNvcw0KDQpDYXNpIHRvZG8gYW7DoWxpc2lzIGVzIHVuYSBzZWN1ZW5jaWE6ICp0b21hIGxhIHRhYmxhLCBsdWVnbyBmaWx0cmEsIGx1ZWdvIGN1ZW50YSwgbHVlZ28gb3JkZW5hKi4gRWwgKipwaXBlKiogYCU+JWAgcGVybWl0ZSBlc2NyaWJpcmxvIGVuIGVzZSBtaXNtbyBvcmRlbi4gU2UgbGVlICoqInkgbHVlZ28iKiouDQoNClNpbiBwaXBlLCBsYXMgZnVuY2lvbmVzIHNlIGFuaWRhbiB5IGhheSBxdWUgbGVlciBkZSBhZGVudHJvIGhhY2lhIGFmdWVyYToNCg0KYGBge3IgZi1waXBlMX0NCmFycmFuZ2UocmVzdW1lbl9tZW5zdWFsLCBkZXNjKGF0ZW5jaW9uZXMpKQ0KYGBgDQoNCkNvbiBwaXBlLCBzZSBsZWUgZGUgYXJyaWJhIGhhY2lhIGFiYWpvLCBjb21vIHVuYSByZWNldGE6DQoNCmBgYHtyIGYtcGlwZTJ9DQpyZXN1bWVuX21lbnN1YWwgJT4lICAgICAgICAgICAgICAgIyB0b21hIGxhIHRhYmxhIHJlc3VtZW5fbWVuc3VhbA0KICBhcnJhbmdlKGRlc2MoYXRlbmNpb25lcykpICAgICAgICMgeSBsdWVnbyBvcmTDqW5hbGEgZGUgbWF5b3IgYSBtZW5vcg0KYGBgDQoNCkVsIHJlc3VsdGFkbyBlcyBlbCBtaXNtby4gYGFycmFuZ2UoKWAgb3JkZW5hIGZpbGFzIHkgYGRlc2MoKWAgaW5kaWNhIG9yZGVuIGRlc2NlbmRlbnRlLiBFbCBwaXBlIHNlIGVzY3JpYmUgY29uIDxrYmQ+Q3RybDwva2JkPiArIDxrYmQ+U2hpZnQ8L2tiZD4gKyA8a2JkPk08L2tiZD4uDQoNClZlcnNpb25lcyByZWNpZW50ZXMgZGUgUiB0YW1iacOpbiBhY2VwdGFuIGVsIHBpcGUgbmF0aXZvIGB8PmAuIFBhcmEgbG8gcXVlIGhhcmVtb3MgZnVuY2lvbmFuIGlndWFsOyBlbiBlc3RhIGd1w61hIHVzYW1vcyBgJT4lYC4NCg0KOjo6IHsuY2FqYSAudHVybm99DQoqKlR1IHR1cm5vLioqIFVzYSBlbCBwaXBlIHBhcmEgb3JkZW5hciBgcmVzdW1lbl9tZW5zdWFsYCBkZSAqKm1lbm9yIGEgbWF5b3IqKiBuw7ptZXJvIGRlIGF0ZW5jaW9uZXMuIFBpc3RhOiBgYXJyYW5nZSgpYCBvcmRlbmEgZGUgbWVub3IgYSBtYXlvciBzaSBubyB1c2FzIGBkZXNjKClgLg0KDQo8ZGV0YWlscz4NCg0KPHN1bW1hcnk+VmVyIHNvbHVjacOzbjwvc3VtbWFyeT4NCg0KYGBgIHINCnJlc3VtZW5fbWVuc3VhbCAlPiUNCiAgYXJyYW5nZShhdGVuY2lvbmVzKQ0KYGBgDQoNCjwvZGV0YWlscz4NCjo6Og0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyBQYXJ0ZSAyIMK3IExhIGJhc2UgZGUgZGF0b3MgZGUgVGVsZXNhbHVkDQoNCiMjIDIuMSBRdcOpIGRhdG9zIHRlbmVtb3MNCg0KVHJhYmFqYXJlbW9zIHRvZGEgbGEgc2VtYW5hIGNvbiBkYXRvcyBkZSBsYSBQbGF0YWZvcm1hIGRlIFRlbGVzYWx1ZCwgcHJlcGFyYWRvcyBwYXJhIGRvY2VuY2lhOg0KDQp8IEFyY2hpdm8gfCBDb250ZW5pZG8gfA0KfC0tLS18LS0tLXwNCnwgYGF0ZW5jaW9uZXNfYW5vbmltaXphZGFzX2NvbXBsZXRhcy5jc3ZgIHwgMTU4LjA0NiByZWdpc3Ryb3MgZGUgYXRlbmNpw7NuLCAzMyB2YXJpYWJsZXMgKGp1bmlvIGEgc2VwdGllbWJyZSBkZSAyMDI2KSB8DQp8IGBhdGVuY2lvbmVzX211ZXN0cmFfZG9jZW50ZV8xMDAwMC5jc3ZgIHwgTXVlc3RyYSBkZSAxMC4wMDAgcmVnaXN0cm9zIHBhcmEgcHJhY3RpY2FyIGNvbiByYXBpZGV6IHwNCnwgYHRlbGVlZHVjYWNpb25fbWVuc3VhbC5jc3ZgIHwgMS40MDQgZmlsYXM6IGFjdGl2aWRhZGVzIGVkdWNhdGl2YXMgcG9yIG1lcyB5IGRlcGFydGFtZW50bywgMjAyMOKAkzIwMjYgfA0KfCBgdGVsZWVkdWNhY2lvbl9hbnVhbC5jc3ZgIHwgMTE3IGZpbGFzOiBsbyBtaXNtbywgcmVzdW1pZG8gcG9yIGHDsW8gfA0KfCBgZGljY2lvbmFyaW9fdmFyaWFibGVzLmNzdmAgfCBRdcOpIHNpZ25pZmljYSBjYWRhIHZhcmlhYmxlIHkgY3XDoW50b3MgZmFsdGFudGVzIHRpZW5lIHwNCnwgYGNvbnRyb2xfY2FsaWRhZC5jc3ZgIHwgUHJvYmxlbWFzIGRldGVjdGFkb3MgZW4gbG9zIGRhdG9zLCBzaW4gY29ycmVnaXIgfA0KDQo6Ojogey5jYWphIC5wcml2YWRvfQ0KKipSZWdsYXMgZGUgdXNvIGRlIGVzdG9zIGRhdG9zLioqDQoNCi0gU2UgcmV0aXJhcm9uIG5vbWJyZXMsIENJLCB0ZWzDqWZvbm9zLCBmZWNoYXMgZXhhY3RhcyB5IG90cm9zIGlkZW50aWZpY2Fkb3Jlcy4gQXVuIGFzw60sIHNvbiBkYXRvcyBkZSBzYWx1ZCBhIG5pdmVsIGRlIHJlZ2lzdHJvIHkgZWwgcmllc2dvIGRlIHJlaWRlbnRpZmljYWNpw7NuIG5vIGVzIGNlcm8uDQotIMOac2Fsb3Mgc29sbyBwYXJhIGVsIHRhbGxlciB5IHBhcmEgYW7DoWxpc2lzIGF1dG9yaXphZG8uDQotIE5vIHB1YmxpcXVlcyBsb3MgYXJjaGl2b3MgbmkgaW50ZW50ZXMgaWRlbnRpZmljYXIgcGVyc29uYXMuDQotICoqTm8gc3ViYXMgbG9zIENTViBuaSBwZWd1ZXMgZmlsYXMgZW4gYXNpc3RlbnRlcyBww7pibGljb3MgZGUgSUEuKiogQSBsYSBJQSBzZSBsZSBkZXNjcmliZSBsYSBlc3RydWN0dXJhIChub21icmVzIHkgdGlwb3MgZGUgY29sdW1uYXMpLCBubyBzZSBsZSBlbnRyZWdhbiBsb3MgZGF0b3MuDQo6OjoNCg0KIyMgMi4yIEltcG9ydGFyIGxhIGJhc2UNCg0KSW1wb3J0YXIgZXMgbGVlciB1biBhcmNoaXZvIHkgY29udmVydGlybG8gZW4gdW4gZGF0YSBmcmFtZSBkZW50cm8gZGUgUi4gVXNhbW9zIGByZWFkX2NzdigpYCBkZWwgcGFxdWV0ZSBgcmVhZHJgLg0KDQpQcmltZXJvIGVsZWdpbW9zIGNvbiBxdcOpIGFyY2hpdm8gdHJhYmFqYXIuIGB1c2FyX211ZXN0cmFgIGZ1bmNpb25hIGNvbW8gdW4gaW50ZXJydXB0b3I6IGNvbiBgVFJVRWAgdXNhbW9zIGxhIG11ZXN0cmEgZGUgMTAuMDAwIHJlZ2lzdHJvczsgY29uIGBGQUxTRWAsIGxhIGJhc2UgY29tcGxldGEuDQoNCmBgYHtyIGItZWxlZ2lyfQ0KdXNhcl9tdWVzdHJhIDwtIFRSVUUNCg0KYXJjaGl2byA8LSBpZiAodXNhcl9tdWVzdHJhKSB7DQogICJhdGVuY2lvbmVzX211ZXN0cmFfZG9jZW50ZV8xMDAwMC5jc3YiDQp9IGVsc2Ugew0KICAiYXRlbmNpb25lc19hbm9uaW1pemFkYXNfY29tcGxldGFzLmNzdiINCn0NCg0KYXJjaGl2bw0KYGBgDQoNCkxhIGVzdHJ1Y3R1cmEgYGlmICguLi4pIHsuLi59IGVsc2Ugey4uLn1gIHNpZ25pZmljYTogKnNpIGxhIGNvbmRpY2nDs24gZXMgdmVyZGFkZXJhLCB1c2EgbG8gcHJpbWVybzsgc2kgbm8sIGxvIHNlZ3VuZG8qLiBFbCBvYmpldG8gYGFyY2hpdm9gIGFob3JhIGNvbnRpZW5lIGVsIG5vbWJyZSBkZWwgYXJjaGl2byBlbGVnaWRvLg0KDQpBaG9yYSBzw60sIGltcG9ydGFtb3M6DQoNCmBgYHtyIGItaW1wb3J0YXJ9DQphdGVuY2lvbmVzIDwtIHJlYWRfY3N2KGFyY2hpdm8sDQogICAgICAgICAgICAgICAgICAgICAgIG5hID0gYygiIiwgIk5BIiksDQogICAgICAgICAgICAgICAgICAgICAgIHNob3dfY29sX3R5cGVzID0gRkFMU0UsDQogICAgICAgICAgICAgICAgICAgICAgIGxvY2FsZSA9IGxvY2FsZShlbmNvZGluZyA9ICJVVEYtOCIpKQ0KYGBgDQoNClF1w6kgaGFjZSBjYWRhIGFyZ3VtZW50bzoNCg0KfCBBcmd1bWVudG8gfCBTaWduaWZpY2FkbyB8DQp8LS0tLXwtLS0tfA0KfCBgYXJjaGl2b2AgfCBFbCBub21icmUgZGVsIGFyY2hpdm8gYSBsZWVyIHwNCnwgYG5hID0gYygiIiwgIk5BIilgIHwgTGFzIGNlbGRhcyB2YWPDrWFzIHNlIGxlZW4gY29tbyBkYXRvIGZhbHRhbnRlIChgTkFgKSB8DQp8IGBzaG93X2NvbF90eXBlcyA9IEZBTFNFYCB8IEV2aXRhIHVuIG1lbnNhamUgbGFyZ28gYWwgaW1wb3J0YXIgfA0KfCBgbG9jYWxlKGVuY29kaW5nID0gIlVURi04IilgIHwgTGVlIGNvcnJlY3RhbWVudGUgdGlsZGVzIHkgY2FyYWN0ZXJlcyBlc3BlY2lhbGVzIHwNCg0KTWlyYSBlbCBwYW5lbCAqKkVudmlyb25tZW50Kio6IGFwYXJlY2nDsyBgYXRlbmNpb25lc2AsIGNvbiBzdSBuw7ptZXJvIGRlIGZpbGFzICgqb2JzLiopIHkgY29sdW1uYXMgKCp2YXJpYWJsZXMqKS4NCg0KSW1wb3J0YW1vcyB0YW1iacOpbiBsYSBiYXNlIGRlIHRlbGVlZHVjYWNpw7NuIHkgZWwgZGljY2lvbmFyaW8sIHF1ZSB1c2FyZW1vcyBtw6FzIGFkZWxhbnRlOg0KDQpgYGB7ciBiLWltcG9ydGFyMn0NCnRlbGVfbWVuc3VhbCA8LSByZWFkX2NzdigidGVsZWVkdWNhY2lvbl9tZW5zdWFsLmNzdiIsIG5hID0gYygiIiwgIk5BIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgc2hvd19jb2xfdHlwZXMgPSBGQUxTRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICBsb2NhbGUgPSBsb2NhbGUoZW5jb2RpbmcgPSAiVVRGLTgiKSkNCg0KZGljY2lvbmFyaW8gPC0gcmVhZF9jc3YoImRpY2Npb25hcmlvX3ZhcmlhYmxlcy5jc3YiLCBzaG93X2NvbF90eXBlcyA9IEZBTFNFLA0KICAgICAgICAgICAgICAgICAgICAgICAgbG9jYWxlID0gbG9jYWxlKGVuY29kaW5nID0gIlVURi04IikpDQpgYGANCg0KIyMgMi4zIFByaW1lciB2aXN0YXpvOiDCv3F1w6kgdGVuZ28gZGVsYW50ZT8NCg0KQW50ZXMgZGUgYW5hbGl6YXIsIHNlIG1pcmEuIExhcyB0cmVzIHByZWd1bnRhcyBpbmljaWFsZXMgc29uOiDCv2N1w6FudGFzIGZpbGFzPywgwr9jdcOhbnRhcyBjb2x1bW5hcz8sIMK/Y8OzbW8gc2UgbGxhbWFuPw0KDQpgYGB7ciBiLXRhbWFub30NCm5yb3coYXRlbmNpb25lcykNCm5jb2woYXRlbmNpb25lcykNCm5hbWVzKGF0ZW5jaW9uZXMpDQpgYGANCg0KOjo6IHsuY2FqYSAuY29udHJvbH0NCioqQ29udHJvbC4qKiBDb24gbGEgbXVlc3RyYSBkZWJlbiBzYWxpciAxMC4wMDAgZmlsYXM7IGNvbiBsYSBiYXNlIGNvbXBsZXRhLCAxNTguMDQ2LiBFbiBhbWJvcyBjYXNvcywgMzMgY29sdW1uYXMuDQo6OjoNCg0KYGdsaW1wc2UoKWAgZGEgbGEgdmlzdGEgbcOhcyBjb21wbGV0YTogdW5hIGzDrW5lYSBwb3IgdmFyaWFibGUsIGNvbiBzdSB0aXBvIHkgc3VzIHByaW1lcm9zIHZhbG9yZXMuDQoNCmBgYHtyIGItZ2xpbXBzZX0NCmdsaW1wc2UoYXRlbmNpb25lcykNCmBgYA0KDQpDw7NtbyBsZWVyIGVzdGEgc2FsaWRhOiBgPGNocj5gIGVzIHRleHRvLCBgPGRibD5gIGVzIG7Dum1lcm8uIEbDrWphdGUgcXVlIGBtZXNgIGVzIG51bcOpcmljbyB5IGBkZXBhcnRhbWVudG9gIGVzIHRleHRvLCB5IHF1ZSBlbiB2YXJpYXMgY29sdW1uYXMgeWEgc2UgdmVuIGBOQWAuDQoNClBhcmEgdmVyIGxhcyBwcmltZXJhcyBmaWxhcyBjb21vIHRhYmxhOg0KDQpgYGB7ciBiLWhlYWR9DQpoZWFkKGF0ZW5jaW9uZXMsIDUpDQpgYGANCg0KU2kgcXVpZXJlcyB2ZXIgdG9kYSBsYSB0YWJsYSBjb21vIGhvamEgZGUgY8OhbGN1bG8sIGVqZWN1dGEgYFZpZXcoYXRlbmNpb25lcylgIGVuIGxhIGNvbnNvbGEgKGNvbiBWIG1hecO6c2N1bGEpIG8gaGF6IGNsaWMgZW4gc3Ugbm9tYnJlIGVuIGVsIHBhbmVsIEVudmlyb25tZW50Lg0KDQojIyAyLjQgRWwgZGljY2lvbmFyaW8gZGUgdmFyaWFibGVzDQoNCk5pbmfDum4gYW7DoWxpc2lzIHNlcmlvIGVtcGllemEgc2luIHNhYmVyIHF1w6kgc2lnbmlmaWNhIGNhZGEgY29sdW1uYS4gRWwgZGljY2lvbmFyaW8gZXMgb3RyYSB0YWJsYSwgeSBsYSBjb25zdWx0YW1vcyBjb24gbGFzIG1pc21hcyBoZXJyYW1pZW50YXM6DQoNCmBgYHtyIGItZGljY2lvbmFyaW99DQpkaWNjaW9uYXJpbyAlPiUNCiAgZmlsdGVyKGNvbmp1bnRvID09ICJhdGVuY2lvbmVzIikgJT4lDQogIHNlbGVjdCh2YXJpYWJsZSwgdGlwb19SX3N1Z2VyaWRvLCBkZWZpbmljaW9uKQ0KYGBgDQoNCkFxdcOtIGFwYXJlY2VuIGRvcyBmdW5jaW9uZXMgbnVldmFzIGRlIGBkcGx5cmA6DQoNCi0gYGZpbHRlcigpYCBzZSBxdWVkYSBjb24gbGFzICoqZmlsYXMqKiBxdWUgY3VtcGxlbiB1bmEgY29uZGljacOzbi4gYGNvbmp1bnRvID09ICJhdGVuY2lvbmVzImAgc2lnbmlmaWNhICJkb25kZSBsYSBjb2x1bW5hIGNvbmp1bnRvIHNlYSBpZ3VhbCBhIGF0ZW5jaW9uZXMiLiBBdGVuY2nDs24gYWwgKipkb2JsZSBzaWdubyBpZ3VhbCoqIGA9PWA6IHNlIHVzYSBwYXJhIGNvbXBhcmFyLiBVbiBzb2xvIGA9YCBzaXJ2ZSBwYXJhIGFzaWduYXIgYXJndW1lbnRvcy4NCi0gYHNlbGVjdCgpYCBzZSBxdWVkYSBjb24gbGFzICoqY29sdW1uYXMqKiBxdWUgbm9tYnJlcy4NCg0KTGFzIDMzIHZhcmlhYmxlcyBzZSBhZ3J1cGFuIGFzw606DQoNCnwgR3J1cG8gfCBWYXJpYWJsZXMgfA0KfC0tLS18LS0tLXwNCnwgVGllbXBvIHkgbHVnYXIgfCBgYW5pb2AsIGBtZXNgLCBgZGVwYXJ0YW1lbnRvYCB8DQp8IFBlcnNvbmEgfCBgc2V4b2AsIGBncnVwb19ldGFyaW9fZnVlbnRlYCwgYGdydXBvX2VkYWRgLCBgY29iZXJ0dXJhYCB8DQp8IFByb2Nlc28geSBzZXJ2aWNpbyB8IGBwcm9jZXNvYCwgYHRpcG9fY2FwdGFjaW9uYCwgYG9yaWdlbl9hdGVuY2lvbmAsIGByZWZlcmlkb19wb3JgLCBgZXNwZWNpYWxpZGFkYCwgYHRpcG9fYXRlbmNpb25gLCBgZGVzdGlub19pbnRlcmNvbnN1bHRhYCwgYHRpcG9fY29uc3VsdGFgLCBgbW9kYWxpZGFkX3RpZW1wb2AgfA0KfCBDbMOtbmljbyB8IGBjYXBpdHVsb19jaWUxMGAsIGBuX2RpYWdub3N0aWNvc2AsIGBpbWNfY2F0ZWdvcmlhYCB8DQp8IERpc3Bvc2l0aXZvcyB8IGBuX2Rpc3Bvc2l0aXZvc2AsIGB1c2FfZWxlY3Ryb2NhcmRpb2dyYWZvYCwgYHVzYV9vdG9zY29waW9gLCBgdXNhX21vbml0b3Jfc3ZgLCBgdXNhX2dsdWNvbWV0cm9gLCBgdXNhX2Vjb2dyYWZvYCB8DQp8IEVqZXMgcHJvZ3JhbcOhdGljb3MgfCBgZWplX2VudGAsIGBlamVfZXRgLCBgZWplX3NhbHVkX2luZmFudGlsYCwgYGVqZV9zYWx1ZF9tYXRlcm5hYCwgYGVqZV9kaXNjYXBhY2lkYWRgLCBgZWplX290cm9zYCB8DQp8IE90cm9zIHwgYHRlbGVtZXRyaWFfbmAsIGByZWdpc3Ryb19pZGAgfA0KDQojIyAyLjUgTGEgdW5pZGFkIGRlIGFuw6FsaXNpcw0KDQpQcmVndW50YSBjbGF2ZTogKirCv3F1w6kgcmVwcmVzZW50YSB1bmEgZmlsYT8qKiBFbiBlc3RhIGJhc2UsIGNhZGEgZmlsYSBlcyAqKnVuIHJlZ2lzdHJvIGRlIGF0ZW5jacOzbioqLCBubyB1biBwYWNpZW50ZS4gVW5hIG1pc21hIHBlcnNvbmEgcHVlZGUgYXBhcmVjZXIgdmFyaWFzIHZlY2VzLg0KDQpgYGB7ciBiLXVuaWRhZH0NCm5fZGlzdGluY3QoYXRlbmNpb25lcyRyZWdpc3Ryb19pZCkgICAgIyBjdcOhbnRvcyBpZGVudGlmaWNhZG9yZXMgZGlzdGludG9zIGhheQ0KbnJvdyhhdGVuY2lvbmVzKSAgICAgICAgICAgICAgICAgICAgICAjIGN1w6FudGFzIGZpbGFzIGhheQ0KYGBgDQoNCkxvcyBkb3MgbsO6bWVyb3MgY29pbmNpZGVuOiBoYXkgdW4gYHJlZ2lzdHJvX2lkYCBwb3IgZmlsYS4gRXNlIGlkZW50aWZpY2Fkb3IgZXMgdW4gY8OzZGlnbyBhbGVhdG9yaW8gY3JlYWRvIHBhcmEgcHJvdGVnZXIgbG9zIGRhdG9zOyBubyBwZXJtaXRlIHNhYmVyIHF1w6kgcmVnaXN0cm9zIHBlcnRlbmVjZW4gYSBsYSBtaXNtYSBwZXJzb25hLg0KDQo6Ojogey5jYWphIC5hbGVydGF9DQoqKkNvbnNlY3VlbmNpYSBwYXJhIGludGVycHJldGFyLioqIERlY2ltb3MgIjEwLjAwMCBhdGVuY2lvbmVzIiwgbnVuY2EgIjEwLjAwMCBwYWNpZW50ZXMiLiBMbyBtaXNtbyBlbiB0ZWxlZWR1Y2FjacOzbjogbG9zICpiZW5lZmljaWFyaW9zKiBzb24gY29udGVvcyBkZSBhc2lzdGVuY2lhLCBubyBuZWNlc2FyaWFtZW50ZSBwZXJzb25hcyBkaXN0aW50YXMuDQo6OjoNCg0KVGFtYmnDqW4gY29udmllbmUgY29uZmlybWFyIGVsIHBlcmlvZG8gcXVlIGN1YnJlbiBsb3MgZGF0b3M6DQoNCmBgYHtyIGItcGVyaW9kb30NCmF0ZW5jaW9uZXMgJT4lIGNvdW50KGFuaW8pDQphdGVuY2lvbmVzICU+JSBjb3VudChtZXMpDQpgYGANCg0KU29sbyBoYXkgbWVzZXMgNiBhIDkgZGUgMjAyNi4gU29uIGN1YXRybyBtZXNlcywgbm8gdW4gYcOxbzogY3VhbHF1aWVyIGNvbmNsdXNpw7NuICJhbnVhbCIgc2Vyw61hIHVuYSBleHRyYXBvbGFjacOzbi4NCg0KIyMgMi42IENvbnRhcjogbGEgcHJlZ3VudGEgbcOhcyBiw6FzaWNhDQoNCmBjb3VudCgpYCByZXNwb25kZSAiwr9jdcOhbnRvcyBoYXkgZGUgY2FkYSB1bm8/Ii4gRXMgbGEgZnVuY2nDs24gcXVlIG3DoXMgdXNhcsOhcy4NCg0KYGBge3IgYi1jb3VudDF9DQphdGVuY2lvbmVzICU+JQ0KICBjb3VudChkZXBhcnRhbWVudG8pDQpgYGANCg0KTGEgdGFibGEgcmVzdWx0YW50ZSB0aWVuZSB1bmEgZmlsYSBwb3IgZGVwYXJ0YW1lbnRvIHkgdW5hIGNvbHVtbmEgYG5gIGNvbiBlbCBuw7ptZXJvIGRlIGF0ZW5jaW9uZXMuIENvbiBgc29ydCA9IFRSVUVgIHNhbGUgb3JkZW5hZGEgZGUgbWF5b3IgYSBtZW5vcjoNCg0KYGBge3IgYi1jb3VudDJ9DQphdGVuY2lvbmVzICU+JQ0KICBjb3VudChkZXBhcnRhbWVudG8sIHNvcnQgPSBUUlVFKQ0KYGBgDQoNClVuIGNvbnRlbyBzb2xvIGRpY2UgcG9jbzsgY2FzaSBzaWVtcHJlIHF1ZXJlbW9zIHRhbWJpw6luIGVsIHBvcmNlbnRhamUuIFBhcmEgY3JlYXIgY29sdW1uYXMgbnVldmFzIHNlIHVzYSBgbXV0YXRlKClgOg0KDQpgYGB7ciBiLWNvdW50M30NCmF0ZW5jaW9uZXMgJT4lDQogIGNvdW50KGRlcGFydGFtZW50bywgc29ydCA9IFRSVUUpICU+JQ0KICBtdXRhdGUocG9yY2VudGFqZSA9IHJvdW5kKDEwMCAqIG4gLyBzdW0obiksIDEpKQ0KYGBgDQoNCkzDqWVsbyBjb21vIHVuYSByZWNldGE6ICp0b21hIGF0ZW5jaW9uZXMsIHkgbHVlZ28gY3VlbnRhIHBvciBkZXBhcnRhbWVudG8sIHkgbHVlZ28gYWdyZWdhIHVuYSBjb2x1bW5hIGNvbiBlbCBwb3JjZW50YWplKi4gTGEgUGF6LCBDb2NoYWJhbWJhIHkgU2FudGEgQ3J1eiBjb25jZW50cmFuIGNlcmNhIGRlbCA2MCUgZGUgbG9zIHJlZ2lzdHJvcy4NCg0KU2UgcHVlZGUgY29udGFyIHBvciBkb3MgdmFyaWFibGVzIGEgbGEgdmV6LiBWZWFtb3MgbGEgdmFyaWFibGUgYHByb2Nlc29gLCBxdWUgdGllbmUgY3VhdHJvIGPDs2RpZ29zIG9wZXJhY2lvbmFsZXMgZGUgbGEgZnVlbnRlIChIQ0QsIEhJQywgVExDLCBUTE0pLCBjcnV6YWRhIGNvbiBlbCBtZXM6DQoNCmBgYHtyIGItY291bnQ0fQ0KYXRlbmNpb25lcyAlPiUNCiAgY291bnQocHJvY2VzbywgbWVzKQ0KYGBgDQoNCk9ic2VydmEgbGFzIGZpbGFzIGRlIFRMTTogZW4gZWwgbWVzIDggaGF5IHVuIHNhbHRvIG11eSBncmFuZGUgcmVzcGVjdG8gZGUgbG9zIG90cm9zIG1lc2VzLiBMbyBsbGFtYXJlbW9zICoqZWwgbWlzdGVyaW8gZGUgYWdvc3RvKiogeSBsbyBpbnZlc3RpZ2Fyw6FzIGVuIGVsIGRlc2Fmw61vIGZpbmFsLg0KDQo6Ojogey5jYWphIC5jb250cm9sfQ0KKipDb250cm9sIGRlIGNvbnNpc3RlbmNpYS4qKiBFbiBjdWFscXVpZXIgY29udGVvLCBsYSBzdW1hIGRlIGBuYCBkZWJlIHNlciBpZ3VhbCBhbCB0b3RhbCBkZSBmaWxhcy4gU2kgbm8gY3VhZHJhLCBhbGdvIHNlIHBlcmRpw7MgZW4gZWwgY2FtaW5vLiBBY29zdMO6bWJyYXRlIGEgdmVyaWZpY2FybG8uDQo6OjoNCg0KYGBge3IgYi1jb250cm9sfQ0KYXRlbmNpb25lcyAlPiUNCiAgY291bnQocHJvY2VzbykgJT4lDQogIHN1bW1hcmlzZSh0b3RhbCA9IHN1bShuKSkNCmBgYA0KDQpgc3VtbWFyaXNlKClgIHJlZHVjZSB1bmEgdGFibGEgYSB1biByZXN1bWVuOyBhcXXDrSwgbGEgc3VtYSBkZSBsYSBjb2x1bW5hIGBuYC4gRWwgdG90YWwgY29pbmNpZGUgY29uIGBucm93KGF0ZW5jaW9uZXMpYC4NCg0KOjo6IHsuY2FqYSAudHVybm99DQoqKlR1IHR1cm5vLioqIMK/Q3XDoW50YXMgYXRlbmNpb25lcyBoYXkgcG9yIGBzZXhvYCwgY29uIHN1IHBvcmNlbnRhamU/IMK/WSBwb3IgYGdydXBvX2VkYWRgPw0KDQo8ZGV0YWlscz4NCg0KPHN1bW1hcnk+VmVyIHNvbHVjacOzbjwvc3VtbWFyeT4NCg0KYGBgIHINCmF0ZW5jaW9uZXMgJT4lDQogIGNvdW50KHNleG8pICU+JQ0KICBtdXRhdGUocG9yY2VudGFqZSA9IHJvdW5kKDEwMCAqIG4gLyBzdW0obiksIDEpKQ0KDQphdGVuY2lvbmVzICU+JQ0KICBjb3VudChncnVwb19lZGFkKSAlPiUNCiAgbXV0YXRlKHBvcmNlbnRhamUgPSByb3VuZCgxMDAgKiBuIC8gc3VtKG4pLCAxKSkNCmBgYA0KDQpBcHJveGltYWRhbWVudGUgZG9zIHRlcmNpb3MgZGUgbGFzIGF0ZW5jaW9uZXMgY29ycmVzcG9uZGVuIGEgbXVqZXJlcywgeSBjZXJjYSBkZSB1bmEgZGUgY2FkYSBjdWF0cm8gYSBwZXJzb25hcyBkZSA2MCBhw7FvcyBvIG3DoXMuDQoNCjwvZGV0YWlscz4NCjo6Og0KDQojIyAyLjcgRmlsdHJhcjogcXVlZGFyc2UgY29uIHVuYSBwYXJ0ZQ0KDQpgZmlsdGVyKClgIHNlbGVjY2lvbmEgZmlsYXMgc2Vnw7puIHVuYSBjb25kaWNpw7NuLiBQb3IgZWplbXBsbywgc29sbyBsYXMgYXRlbmNpb25lcyBkZSBDb2NoYWJhbWJhOg0KDQpgYGB7ciBiLWZpbHRlcjF9DQphdGVuY2lvbmVzICU+JQ0KICBmaWx0ZXIoZGVwYXJ0YW1lbnRvID09ICJDT0NIQUJBTUJBIikgJT4lDQogIGNvdW50KHByb2Nlc28sIHNvcnQgPSBUUlVFKQ0KYGBgDQoNCkxhcyBjb25kaWNpb25lcyBzZSBwdWVkZW4gY29tYmluYXIuIENvbiBjb21hIChvIGAmYCkgZGViZW4gY3VtcGxpcnNlIHRvZGFzOg0KDQpgYGB7ciBiLWZpbHRlcjJ9DQphdGVuY2lvbmVzICU+JQ0KICBmaWx0ZXIoZGVwYXJ0YW1lbnRvID09ICJDT0NIQUJBTUJBIiwgbWVzID09IDgpICU+JQ0KICBjb3VudChjYXBpdHVsb19jaWUxMCwgc29ydCA9IFRSVUUpICU+JQ0KICBoZWFkKDUpDQpgYGANCg0KRXN0ZSBlcyBlbCBwZXJmaWwgY2zDrW5pY28gKHBvciBjYXDDrXR1bG8gQ0lFLTEwKSBkZSBDb2NoYWJhbWJhIGVuIGFnb3N0by4gYGhlYWQoNSlgIG11ZXN0cmEgc29sbyBsYXMgY2luY28gcHJpbWVyYXMgZmlsYXMuDQoNCk9wZXJhZG9yZXMgZGUgY29tcGFyYWNpw7NuIG3DoXMgdXNhZG9zOg0KDQp8IE9wZXJhZG9yIHwgU2lnbmlmaWNhZG8gfCBFamVtcGxvIHwNCnwtLS0tfC0tLS18LS0tLXwNCnwgYD09YCB8IGlndWFsIGEgfCBgc2V4byA9PSAiRiJgIHwNCnwgYCE9YCB8IGRpc3RpbnRvIGRlIHwgYHByb2Nlc28gIT0gIlRMTSJgIHwNCnwgYD5gIGA+PWAgYDxgIGA8PWAgfCBtYXlvciwgbWF5b3IgbyBpZ3VhbCwgbWVub3IsIG1lbm9yIG8gaWd1YWwgfCBgbWVzID49IDhgIHwNCnwgYCVpbiVgIHwgZXN0w6EgZW4gdW5hIGxpc3RhIHwgYGRlcGFydGFtZW50byAlaW4lIGMoIk9SVVJPIiwgIlBPVE9TSSIpYCB8DQp8IGBpcy5uYSgpYCB8IGVzIGZhbHRhbnRlIHwgYGlzLm5hKGVzcGVjaWFsaWRhZClgIHwNCnwgYCFgIHwgbmVnYWNpw7NuIHwgYCFpcy5uYShlc3BlY2lhbGlkYWQpYCB8DQoNCjo6OiB7LmNhamEgLmFsZXJ0YX0NCioqRWwgdGV4dG8gZGViZSBjb2luY2lkaXIgZXhhY3RhbWVudGUuKiogRW4gZXN0YSBiYXNlIGxvcyBkZXBhcnRhbWVudG9zIGVzdMOhbiBlbiBtYXnDunNjdWxhcyB5IHNpbiB0aWxkZXM6IGAiUE9UT1NJImAsIG5vIGAiUG90b3PDrSJgLiBTaSB1biBmaWx0cm8gZGV2dWVsdmUgY2VybyBmaWxhcywgcmV2aXNhIHByaW1lcm8gY8OzbW8gZXN0w6EgZXNjcml0byBlbCB2YWxvciBjb24gYGNvdW50KClgLg0KOjo6DQoNCiMjIDIuOCBNZWRpciBsb3MgZmFsdGFudGVzDQoNCkFudGVzIGRlIHNhY2FyIGNvbmNsdXNpb25lcyBoYXkgcXVlIHNhYmVyICoqY3XDoW50byBmYWx0YSB5IGTDs25kZSoqLiBFbXBlY2Vtb3MgcG9yIHVuYSB2YXJpYWJsZToNCg0KYGBge3IgYi1uYTF9DQpzdW0oaXMubmEoYXRlbmNpb25lcyRlc3BlY2lhbGlkYWQpKSAgICAgICAgICAjIGN1w6FudG9zIHJlZ2lzdHJvcyBzaW4gZXNwZWNpYWxpZGFkDQptZWFuKGlzLm5hKGF0ZW5jaW9uZXMkZXNwZWNpYWxpZGFkKSkgKiAxMDAgICAjIHF1w6kgcG9yY2VudGFqZSByZXByZXNlbnRhbg0KYGBgDQoNClVuIHRydWNvIMO6dGlsOiBgbWVhbihpcy5uYSh4KSlgIGRhIGRpcmVjdGFtZW50ZSBsYSBwcm9wb3JjacOzbiBkZSBmYWx0YW50ZXMsIHBvcnF1ZSBSIHRyYXRhIGBUUlVFYCBjb21vIDEgeSBgRkFMU0VgIGNvbW8gMC4NCg0KQWhvcmEgbG8gbWlzbW8gcGFyYSAqKnRvZGFzKiogbGFzIHZhcmlhYmxlcyBhIGxhIHZlei4gRXN0ZSBibG9xdWUgZXMgbcOhcyBhdmFuemFkbzsgbMOpZWxvIGNvbiBjYWxtYToNCg0KYGBge3IgYi1uYTJ9DQpmYWx0YW50ZXMgPC0gdGliYmxlKA0KICB2YXJpYWJsZSAgICA9IG5hbWVzKGF0ZW5jaW9uZXMpLA0KICBuX2ZhbHRhbnRlcyA9IHNhcHBseShhdGVuY2lvbmVzLCBmdW5jdGlvbih4KSBzdW0oaXMubmEoeCkpKSwNCiAgcG9yY2VudGFqZSAgPSByb3VuZCgxMDAgKiBuX2ZhbHRhbnRlcyAvIG5yb3coYXRlbmNpb25lcyksIDEpDQopICU+JQ0KICBhcnJhbmdlKGRlc2MocG9yY2VudGFqZSkpDQoNCmZhbHRhbnRlcyAlPiUgZmlsdGVyKG5fZmFsdGFudGVzID4gMCkNCmBgYA0KDQpRdcOpIGhhY2U6IGB0aWJibGUoKWAgY3JlYSB1bmEgdGFibGEgbnVldmEgY29uIHRyZXMgY29sdW1uYXM7IGBzYXBwbHkoKWAgYXBsaWNhIGEgY2FkYSBjb2x1bW5hIGRlIGBhdGVuY2lvbmVzYCBsYSBvcGVyYWNpw7NuICJjb250YXIgbG9zIE5BIjsgZGVzcHXDqXMgc2UgY2FsY3VsYSBlbCBwb3JjZW50YWplIHkgc2Ugb3JkZW5hLg0KDQo6Ojogey5jYWphIC5pYX0NCioqQnVlbiBtb21lbnRvIHBhcmEgdXNhciBsYSBJQS4qKiBTaSB1bmEgbMOtbmVhIGNvbW8gbGEgZGUgYHNhcHBseSgpYCBubyBzZSBlbnRpZW5kZSwgcMOtZGVsZSB1bmEgZXhwbGljYWNpw7NuOiAqIkV4cGzDrWNhbWUgbMOtbmVhIHBvciBsw61uZWEgcXXDqSBoYWNlIGVzdGUgY8OzZGlnbyBkZSBSLCBlbiBsZW5ndWFqZSBzaW1wbGU6IFtwZWdhIGVsIGPDs2RpZ29dIiouIFBlZ2FyIGPDs2RpZ28gZXMgc2VndXJvOyBsbyBxdWUgbm8gc2UgcGVnYSBzb24gbG9zIGRhdG9zLg0KOjo6DQoNCkVsIHJlc3VsdGFkbyBtdWVzdHJhIHZhcmlhYmxlcyBjb24gbXVjaMOtc2ltb3MgZmFsdGFudGVzOiBgaW1jX2NhdGVnb3JpYWAgc3VwZXJhIGVsIDk1JSwgYGNvYmVydHVyYWAgcm9uZGEgZWwgODglLiBDb24gZXNlIG5pdmVsIGRlIGF1c2VuY2lhLCBubyBzaXJ2ZW4gcGFyYSBjb25jbHVzaW9uZXMgZ2VuZXJhbGVzLg0KDQpQZXJvIG5vIHRvZG9zIGxvcyBmYWx0YW50ZXMgc29uIGlndWFsZXMuIFZlYW1vcyBzaSBsYSBmYWx0YSBkZSBgZXNwZWNpYWxpZGFkYCBkZXBlbmRlIGRlbCBwcm9jZXNvOg0KDQpgYGB7ciBiLW5hM30NCmF0ZW5jaW9uZXMgJT4lDQogIGdyb3VwX2J5KHByb2Nlc28pICU+JQ0KICBzdW1tYXJpc2UoDQogICAgcmVnaXN0cm9zICAgICAgICAgICAgPSBuKCksDQogICAgcGN0X3Npbl9lc3BlY2lhbGlkYWQgPSByb3VuZChtZWFuKGlzLm5hKGVzcGVjaWFsaWRhZCkpICogMTAwLCAxKSwNCiAgICBwY3Rfc2luX21vZGFsaWRhZCAgICA9IHJvdW5kKG1lYW4oaXMubmEobW9kYWxpZGFkX3RpZW1wbykpICogMTAwLCAxKQ0KICApDQpgYGANCg0KRG9zIGZ1bmNpb25lcyBudWV2YXMgcXVlIHRyYWJhamFuIGp1bnRhczogYGdyb3VwX2J5KClgIGRpdmlkZSBsYSB0YWJsYSBlbiBncnVwb3MgKGFxdcOtLCB1bm8gcG9yIHByb2Nlc28pIHkgYHN1bW1hcmlzZSgpYCBjYWxjdWxhIHVuIHJlc3VtZW4gKipwYXJhIGNhZGEgZ3J1cG8qKi4gYG4oKWAgY3VlbnRhIGxhcyBmaWxhcyBkZWwgZ3J1cG8uDQoNCjo6OiB7LmNhamEgLmNvbnRyb2x9DQoqKkhhbGxhemdvLioqIGBlc3BlY2lhbGlkYWRgIHkgYG1vZGFsaWRhZF90aWVtcG9gIGZhbHRhbiBlbiBlbCAqKjEwMCUgZGUgbG9zIHJlZ2lzdHJvcyBUTE0qKiB5IGVuIGVsICoqMCUqKiBkZSBsb3MgZGVtw6FzIHByb2Nlc29zLiBObyBlcyB1biBkZXNjdWlkbyBkZSByZWdpc3RybzogZWwgZm9ybXVsYXJpbyBkZSBUTE0gbm8gcGlkZSBlc29zIGNhbXBvcy4gRXMgdW4gZmFsdGFudGUgKipwb3IgZGlzZcOxbyoqLiBObyBzZSByZWxsZW5hOyBzZSBleHBsaWNhLg0KOjo6DQoNCjo6OiB7LmNhamEgLmFsZXJ0YX0NCioqT3RyYSB0cmFtcGEgZGUgZXN0YSBiYXNlLioqIEVuIGxhcyB2YXJpYWJsZXMgZGUgZGlzcG9zaXRpdm9zIChgdXNhX2dsdWNvbWV0cm9gLCBgdXNhX290b3Njb3Bpb2AsIGV0Yy4pLCBlbCB2YWxvciAwIHNpZ25pZmljYSAic2luIHJlZ2lzdHJvIGRlbCBkaXNwb3NpdGl2byIsIG5vIG5lY2VzYXJpYW1lbnRlICJubyBzZSB1c8OzIi4gUmVkYWN0YSBzaWVtcHJlICJmaWd1cmEgcmVnaXN0cmFkbyBlbiBYJSBkZSBsYXMgYXRlbmNpb25lcyIuDQo6OjoNCg0KIyMgMi45IFR1IHByaW1lciBncsOhZmljbyBjb24gZ2dwbG90Mg0KDQpgZ2dwbG90MmAgY29uc3RydXllIGdyw6FmaWNvcyBwb3IgY2FwYXMuIFRvZG8gZ3LDoWZpY28gbmVjZXNpdGEgdHJlcyBjb3NhczoNCg0KMS4gICoqRGF0b3M6KiogbGEgdGFibGEgcXVlIHNlIHZhIGEgZ3JhZmljYXIuDQoyLiAgKipgYWVzKClgOioqIHF1w6kgdmFyaWFibGUgdmEgZW4gY2FkYSBlamUsIHkgY3XDoWwgZGVmaW5lIGVsIGNvbG9yLg0KMy4gICoqYGdlb21fLi4uKClgOioqIGxhIGZvcm1hOiBiYXJyYXMgKGBnZW9tX2NvbGApLCBsw61uZWFzIChgZ2VvbV9saW5lYCksIHB1bnRvcyAoYGdlb21fcG9pbnRgKS4NCg0KRW1wZWNlbW9zIGNvbiBsbyBtw61uaW1vOiBhdGVuY2lvbmVzIHBvciBtZXMuDQoNCmBgYHtyIGctMX0NCmF0ZW5jaW9uZXMgJT4lDQogIGNvdW50KG1lcykgJT4lDQogIGdncGxvdChhZXMoeCA9IGZhY3RvcihtZXMpLCB5ID0gbikpICsNCiAgZ2VvbV9jb2woKQ0KYGBgDQoNClByaW1lcm8gY29udGFtb3MgcG9yIG1lczsgZXNhIHRhYmxhIHBhc2EgYSBgZ2dwbG90KClgLiBFbiBgYWVzKClgIGRlY2ltb3M6IGVsIG1lcyBlbiBlbCBlamUgWCwgZWwgY29udGVvIGBuYCBlbiBlbCBlamUgWS4gYGZhY3RvcihtZXMpYCBsZSBpbmRpY2EgYSBSIHF1ZSB0cmF0ZSBlbCBtZXMgY29tbyBjYXRlZ29yw61hIHkgbm8gY29tbyBuw7ptZXJvIGNvbnRpbnVvLg0KDQo6Ojogey5jYWphIC5hbGVydGF9DQoqKkVsIGVycm9yIG3DoXMgY29tw7puIGRlbCBwcmltZXIgZMOtYS4qKiBEZW50cm8gZGUgdW4gZ3LDoWZpY28sIGxhcyBjYXBhcyBzZSB1bmVuIGNvbiBgK2AsIG5vIGNvbiBgJT4lYC4gRWwgcGlwZSBsbGV2YSBsb3MgZGF0b3MgKipoYXN0YSoqIGBnZ3Bsb3QoKWA7IGRlc2RlIGFow60sIHRvZG8gc2Ugc3VtYSBjb24gYCtgLg0KOjo6DQoNCkFob3JhIGxvIG1lam9yYW1vczogY29sb3IgcG9yIHByb2Nlc28sIHTDrXR1bG9zIGNsYXJvcyB5IHVuIGVzdGlsbyBtw6FzIGxpbXBpby4NCg0KYGBge3IgZy0yfQ0KYXRlbmNpb25lcyAlPiUNCiAgY291bnQobWVzLCBwcm9jZXNvKSAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gZmFjdG9yKG1lcyksIHkgPSBuLCBmaWxsID0gcHJvY2VzbykpICsNCiAgZ2VvbV9jb2wocG9zaXRpb24gPSAiZG9kZ2UiKSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoSENEID0gIiM4Qzk1QTYiLCBISUMgPSAiIzc1QUFEQiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVExDID0gIiMyNzZEQzMiLCBUTE0gPSAiI0U0MDMyRSIpKSArDQogIGxhYnModGl0bGUgPSAiQXRlbmNpb25lcyBwb3IgbWVzIHkgcHJvY2VzbyIsDQogICAgICAgc3VidGl0bGUgPSAiUGxhdGFmb3JtYSBkZSBUZWxlc2FsdWQsIGp1bmlvIGEgc2VwdGllbWJyZSBkZSAyMDI2IiwNCiAgICAgICB4ID0gIk1lcyIsIHkgPSAiTsO6bWVybyBkZSBhdGVuY2lvbmVzIiwgZmlsbCA9ICJQcm9jZXNvIikgKw0KICB0aGVtZV9taW5pbWFsKGJhc2Vfc2l6ZSA9IDEzKQ0KYGBgDQoNClF1w6kgYWdyZWfDsyBjYWRhIGNhcGE6DQoNCnwgQ2FwYSAgICAgICAgICAgICAgICAgIHwgUXXDqSBoYWNlICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwNCnwgYGZpbGwgPSBwcm9jZXNvYCAgICAgIHwgQ29sb3JlYSBsYXMgYmFycmFzIHNlZ8O6biBlbCBwcm9jZXNvICAgICAgICAgICAgfA0KfCBgcG9zaXRpb24gPSAiZG9kZ2UiYCAgfCBQb25lIGxhcyBiYXJyYXMgbGFkbyBhIGxhZG8gZW4gdmV6IGRlIGFwaWxhZGFzIHwNCnwgYHNjYWxlX2ZpbGxfbWFudWFsKClgIHwgQXNpZ25hIHVuIGNvbG9yIGNvbmNyZXRvIGEgY2FkYSBwcm9jZXNvICAgICAgICB8DQp8IGBsYWJzKClgICAgICAgICAgICAgICB8IFTDrXR1bG8sIHN1YnTDrXR1bG8geSBub21icmVzIGRlIGVqZXMgICAgICAgICAgICB8DQp8IGB0aGVtZV9taW5pbWFsKClgICAgICB8IFVuIGVzdGlsbyB2aXN1YWwgc29icmlvICAgICAgICAgICAgICAgICAgICAgICAgfA0KDQpFbCBncsOhZmljbyBoYWNlIGV2aWRlbnRlIGxvIHF1ZSBsYSB0YWJsYSBpbnNpbnVhYmE6IGVsIHNhbHRvIGRlIFRMTSBlbiBhZ29zdG8uDQoNClVuIHNlZ3VuZG8gZWplbXBsbywgY29uIGJhcnJhcyBob3Jpem9udGFsZXMgb3JkZW5hZGFzLCDDunRpbCBjdWFuZG8gbGFzIGV0aXF1ZXRhcyBzb24gbGFyZ2FzOg0KDQpgYGB7ciBnLTN9DQphdGVuY2lvbmVzICU+JQ0KICBjb3VudChkZXBhcnRhbWVudG8pICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSByZW9yZGVyKGRlcGFydGFtZW50bywgbiksIHkgPSBuKSkgKw0KICBnZW9tX2NvbChmaWxsID0gIiMyNzZEQzMiKSArDQogIGNvb3JkX2ZsaXAoKSArDQogIGxhYnModGl0bGUgPSAiQXRlbmNpb25lcyBwb3IgZGVwYXJ0YW1lbnRvIiwNCiAgICAgICB4ID0gTlVMTCwgeSA9ICJOw7ptZXJvIGRlIGF0ZW5jaW9uZXMiKSArDQogIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gMTMpDQpgYGANCg0KYHJlb3JkZXIoZGVwYXJ0YW1lbnRvLCBuKWAgb3JkZW5hIGxvcyBkZXBhcnRhbWVudG9zIHNlZ8O6biBzdSBuw7ptZXJvIGRlIGF0ZW5jaW9uZXMgeSBgY29vcmRfZmxpcCgpYCBnaXJhIGVsIGdyw6FmaWNvLg0KDQo6Ojogey5jYWphIC5hbGVydGF9DQoqKkxlZXIgY29uIGN1aWRhZG8uKiogTcOhcyBhdGVuY2lvbmVzIGVuIHVuIGRlcGFydGFtZW50byBwdWVkZSBzaWduaWZpY2FyIG3DoXMgcG9ibGFjacOzbiwgbcOhcyBuZWNlc2lkYWQgbyBzaW1wbGVtZW50ZSBtZWpvciByZWdpc3Ryby4gU2luIHVuIGRlbm9taW5hZG9yIChwb2JsYWNpw7NuLCBlc3RhYmxlY2ltaWVudG9zKSwgZWwgZ3LDoWZpY28gbXVlc3RyYSB2b2x1bWVuLCBubyBkZXNlbXBlw7FvLiBMbyB0cmFiYWphcmVtb3MgZW4gZWwgRMOtYSAzLg0KOjo6DQoNClBhcmEgZ3VhcmRhciBlbCDDumx0aW1vIGdyw6FmaWNvIGNvbW8gaW1hZ2VuOg0KDQpgYGB7ciBnLWd1YXJkYXJ9DQpkaXIuY3JlYXRlKCJyZXN1bHRhZG9zIiwgc2hvd1dhcm5pbmdzID0gRkFMU0UpDQpnZ3NhdmUoInJlc3VsdGFkb3MvYXRlbmNpb25lc19wb3JfZGVwYXJ0YW1lbnRvLnBuZyIsIHdpZHRoID0gOSwgaGVpZ2h0ID0gNSwgZHBpID0gMTUwKQ0KYGBgDQoNCmBkaXIuY3JlYXRlKClgIGNyZWEgbGEgY2FycGV0YSBgcmVzdWx0YWRvc2Agc2kgbm8gZXhpc3RlLCB5IGBnZ3NhdmUoKWAgZ3VhcmRhIGFow60gZWwgZ3LDoWZpY28uDQoNCiMjIDIuMTAgTGEgc2VndW5kYSBiYXNlOiB0ZWxlZWR1Y2FjacOzbg0KDQpMYSBiYXNlIGRlIHRlbGVlZHVjYWNpw7NuIHRpZW5lIG90cmEgdW5pZGFkIGRlIGFuw6FsaXNpczogY2FkYSBmaWxhIGVzIHVuYSBjb21iaW5hY2nDs24gZGUgKip0aXBvIGRlIGFjdGl2aWRhZCDDlyBhw7FvIMOXIG1lcyDDlyBkZXBhcnRhbWVudG8qKi4NCg0KYGBge3IgdC0xfQ0KZ2xpbXBzZSh0ZWxlX21lbnN1YWwpDQpgYGANCg0KQW50ZXMgZGUgc3VtYXIgYmVuZWZpY2lhcmlvcywgaGF5IHF1ZSBtaXJhciBsYSBjb2x1bW5hIGBlc3RhZG9fcmVwb3J0ZWA6DQoNCmBgYHtyIHQtMn0NCnRlbGVfbWVuc3VhbCAlPiUgY291bnQoZXN0YWRvX3JlcG9ydGUpDQpgYGANCg0KSGF5IG1lc2VzIG1hcmNhZG9zIGNvbW8gYHNpbl9yZXBvcnRlYC4gTm8gc29uIG1lc2VzIGNvbiBjZXJvIGFjdGl2aWRhZGVzOiBzb24gbWVzZXMgZGUgbG9zIHF1ZSAqKm5vIGhheSBpbmZvcm1hY2nDs24qKi4gUG9yIGVzbyBsb3MgZXhjbHVpbW9zIGV4cGzDrWNpdGFtZW50ZSBhbnRlcyBkZSBncmFmaWNhcjoNCg0KYGBge3IgdC0zfQ0KdGVsZV9tZW5zdWFsICU+JQ0KICBmaWx0ZXIoZXN0YWRvX3JlcG9ydGUgPT0gInJlcG9ydGFkbyIsDQogICAgICAgICAhaXMubmEoYmVuZWZpY2lhcmlvc190b3RhbCksDQogICAgICAgICB0aXBvX2FjdGl2aWRhZCA9PSAidGVsZWNhcGFjaXRhY2lvbiIpICU+JQ0KICBncm91cF9ieShhbmlvKSAlPiUNCiAgc3VtbWFyaXNlKGJlbmVmaWNpYXJpb3MgPSBzdW0oYmVuZWZpY2lhcmlvc190b3RhbCkpICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBmYWN0b3IoYW5pbyksIHkgPSBiZW5lZmljaWFyaW9zKSkgKw0KICBnZW9tX2NvbChmaWxsID0gIiNFNDAzMkUiKSArDQogIGxhYnModGl0bGUgPSAiQmVuZWZpY2lhcmlvcyByZXBvcnRhZG9zIGRlIHRlbGVjYXBhY2l0YWNpw7NuIHBvciBhw7FvIiwNCiAgICAgICBzdWJ0aXRsZSA9ICIyMDI2IGluY29tcGxldG86IGxvcyBkYXRvcyBsbGVnYW4gaGFzdGEgc2VwdGllbWJyZSIsDQogICAgICAgeCA9ICJBw7FvIiwgeSA9ICJCZW5lZmljaWFyaW9zIChjb250ZW9zIGRlIGFzaXN0ZW5jaWEpIikgKw0KICB0aGVtZV9taW5pbWFsKGJhc2Vfc2l6ZSA9IDEzKQ0KYGBgDQoNCjo6OiB7LmNhamEgLmFsZXJ0YX0NCioqVHJlcyBhZHZlcnRlbmNpYXMgZGUgZXN0YSBiYXNlLioqICgxKSBMb3MgYmVuZWZpY2lhcmlvcyBzb24gY29udGVvcyBkZSBhc2lzdGVuY2lhLCBubyBwZXJzb25hcyDDum5pY2FzLiAoMikgRWwgYcOxbyAyMDI2IGVzdMOhIGluY29tcGxldG86IG5vIGxvIGNvbXBhcmVzIGNvbW8gYcOxbyBlbnRlcm8uICgzKSBFbiB0ZWxlcHJvbW9jacOzbiwgZWwgYmxvcXVlIGRlIDIwMjUgdGllbmUgZWwgYcOxbyBpbmZlcmlkbyAoYGFuaW9faW5mZXJpZG8gPSAxYCkgeSBjb252aWVuZSB2YWxpZGFybG8gY29uIGxhIGZ1ZW50ZS4NCjo6Og0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyBQYXJ0ZSAzIMK3IExhIElBIGNvbW8gY29waWxvdG8NCg0KTGEgSUEgcHVlZGUgZXNjcmliaXIsIGV4cGxpY2FyIHkgY29ycmVnaXIgY8OzZGlnbyBSIGNvbiBtdWNoYSByYXBpZGV6LiBFc28gbm8gZWxpbWluYSB0dSB0cmFiYWpvOiBsbyBjYW1iaWEuIExhIHByZWd1bnRhLCBsb3MgZGF0b3MsIGxhIHZhbGlkYWNpw7NuIHkgbGEgaW50ZXJwcmV0YWNpw7NuIHNpZ3VlbiBzaWVuZG8gdHUgcmVzcG9uc2FiaWxpZGFkLg0KDQojIyAzLjEgRWwgY2ljbG8gZGUgdHJhYmFqbw0KDQoxLiAgKipGb3JtdWxhcjoqKiDCv3F1w6kgbmVjZXNpdG8gc2FiZXI/DQoyLiAgKipDb250ZXh0dWFsaXphcjoqKiDCv3F1w6kgZGF0b3MgdGVuZ28geSBxdcOpIHNpZ25pZmljYW4/DQozLiAgKipTb2xpY2l0YXI6KiogcGVkaXIgZWwgY8OzZGlnbyBjb24gdW4gYnVlbiBwcm9tcHQuDQo0LiAgKipFamVjdXRhcjoqKiBjb3JyZXJsbyB5IGVudGVuZGVyIHF1w6kgaGFjZS4NCjUuICAqKlZhbGlkYXI6Kiogwr9lbCByZXN1bHRhZG8gZXMgY29ycmVjdG8/DQo2LiAgKipJbnRlcnByZXRhcjoqKiDCv3F1w6kgbXVlc3RyYW4gcmVhbG1lbnRlIGxvcyBkYXRvcz8NCjcuICAqKkNvbXVuaWNhcjoqKiDCv3F1acOpbiB2YSBhIGRlY2lkaXIgY29uIGVzdG8/DQo4LiAgKipSZXByb2R1Y2lyOioqIGd1YXJkYXIgZWwgc2NyaXB0IHBhcmEgcmVwZXRpcmxvIGNvbiBkYXRvcyBudWV2b3MuDQoNCiMjIDMuMiBBbmF0b23DrWEgZGUgdW4gYnVlbiBwcm9tcHQNCg0KVW4gcHJvbXB0IHF1ZSBwcm9kdWNlIGPDs2RpZ28gw7p0aWwgdGllbmUgc2VpcyBwaWV6YXM6DQoNCnwgUGllemEgfCBFamVtcGxvIHwNCnwtLS0tfC0tLS18DQp8ICoqQ29udGV4dG8qKiB8IFNveSBhbmFsaXN0YSBkZSBUZWxlc2FsdWQgeSB0cmFiYWpvIGVuIFIgY29uIGRwbHlyIHkgZ2dwbG90Mi4gfA0KfCAqKk9iamV0aXZvKiogfCBRdWllcm8gY29udGFyIGxhcyBhdGVuY2lvbmVzIHBvciBkZXBhcnRhbWVudG8geSBtZXMuIHwNCnwgKipEYXRvcyoqIHwgVGVuZ28gdW4gZGF0YSBmcmFtZSBgYXRlbmNpb25lc2AgY29uIGxhcyBjb2x1bW5hcyBgbWVzYCAobnVtw6lyaWNvLCA2IGEgOSkgeSBgZGVwYXJ0YW1lbnRvYCAodGV4dG8gZW4gbWF5w7pzY3VsYXMpLiB8DQp8ICoqUmVzdHJpY2Npb25lcyoqIHwgVXNhIHNvbG8gZHBseXIgeSBnZ3Bsb3QyLiBObyByZWVtcGxhY2VzIE5BIHBvciAwLiB8DQp8ICoqRm9ybWF0byBkZSBzYWxpZGEqKiB8IENvbWVudGEgY2FkYSBsw61uZWEgZGVsIGPDs2RpZ28uIHwNCnwgKipQcnVlYmFzKiogfCBBZ3JlZ2EgdW4gY29udHJvbDogbGEgc3VtYSBkZWJlIHNlciBpZ3VhbCBhIGBucm93KGF0ZW5jaW9uZXMpYC4gfA0KDQoqKkNvbXBhcmEuKiogUHJvbXB0IHZhZ286ICoiaGF6bWUgdW4gZ3LDoWZpY28gZGUgbG9zIGRhdG9zIGRlIHRlbGVzYWx1ZCIqLiBMYSBJQSB0ZW5kcsOhIHF1ZSBpbnZlbnRhciBub21icmVzIGRlIGNvbHVtbmFzIHkgZWxlZ2lyIGVsIGdyw6FmaWNvIHBvciB0aS4NCg0KOjo6IHsuY2FqYSAuaWF9DQoqKlBsYW50aWxsYSBwYXJhIGNvcGlhciB5IGNvbXBsZXRhcioqDQoNCj4gU295IGFuYWxpc3RhIGRlIFRlbGVzYWx1ZCB5IHRyYWJham8gZW4gUiBjb24gZHBseXIgeSBnZ3Bsb3QyLiBUZW5nbyB1biBkYXRhIGZyYW1lIGxsYW1hZG8gYGF0ZW5jaW9uZXNgIGNvbiBlc3RhcyBjb2x1bW5hczogYFtub21icmVzIHkgdGlwb3MgZGUgbGFzIGNvbHVtbmFzIHF1ZSBuZWNlc2l0YXNdYC4gUXVpZXJvIGBbdHUgcHJlZ3VudGFdYC4gTm8gcmVlbXBsYWNlcyBOQSBwb3IgMC4gQ29tZW50YSBjYWRhIGzDrW5lYSBkZWwgY8OzZGlnbyB5IGFncmVnYSB1biBjb250cm9sIHF1ZSB2ZXJpZmlxdWUgcXVlIGxhIHN1bWEgZGUgbG9zIGdydXBvcyBlcyBpZ3VhbCBhIGBucm93KGF0ZW5jaW9uZXMpYC4NCjo6Og0KDQpQYXJhIG9idGVuZXIgcsOhcGlkYW1lbnRlIGxvcyBub21icmVzIHkgdGlwb3MgZGUgbGFzIGNvbHVtbmFzIHF1ZSB2YXMgYSBkZXNjcmliaXI6DQoNCmBgYHtyIGlhLWNvbHVtbmFzfQ0Kc2FwcGx5KGF0ZW5jaW9uZXNbLCBjKCJtZXMiLCAiZGVwYXJ0YW1lbnRvIiwgInByb2Nlc28iLCAib3JpZ2VuX2F0ZW5jaW9uIildLCBjbGFzcykNCmBgYA0KDQpFc28gZXMgbG8gcXVlIHNlIGNvbXBhcnRlIGNvbiBsYSBJQTogbm9tYnJlcyB5IHRpcG9zLiBOdW5jYSBsYXMgZmlsYXMuDQoNCiMjIDMuMyBTZW3DoWZvcm8gZGUgcHJpdmFjaWRhZA0KDQp8ICAgfCBRdcOpIHwgRWplbXBsb3MgfA0KfC0tLS18LS0tLXwtLS0tfA0KfCDwn5+iICoqU8OtKiogfCBFc3RydWN0dXJhIHkgY8OzZGlnbyB8IE5vbWJyZXMgeSB0aXBvcyBkZSBjb2x1bW5hcywgdHUgY8OzZGlnbywgbWVuc2FqZXMgZGUgZXJyb3IgfA0KfCDwn5+hICoqQ29uIGN1aWRhZG8qKiB8IFJlc3VsdGFkb3MgYWdyZWdhZG9zIHwgVGFibGFzIGNvbiBjb250ZW9zIG11eSBwZXF1ZcOxb3MsIGRhdG9zIGluc3RpdHVjaW9uYWxlcyBubyBwdWJsaWNhZG9zIHwNCnwg8J+UtCAqKk51bmNhKiogfCBNaWNyb2RhdG9zIHwgRmlsYXMgZGUgbGEgYmFzZSwgYXJjaGl2b3MgQ1NWLCBjdWFscXVpZXIgZGF0byBpZGVudGlmaWNhYmxlIHwNCg0KVXNhIHNpZW1wcmUgbGFzIGhlcnJhbWllbnRhcyBkZSBJQSBhdXRvcml6YWRhcyBwb3IgdHUgaW5zdGl0dWNpw7NuLg0KDQojIyAzLjQgQ2hlY2tsaXN0OiBhbnRlcyBkZSBhY2VwdGFyIGPDs2RpZ28gZGUgbGEgSUENCg0KLSBbIF0gKirCv0NvcnJlIHNpbiBlcnJvcmVzPyoqIEVqZWPDunRhbG8gbMOtbmVhIHBvciBsw61uZWEsIG5vIHRvZG8gZGUgZ29scGUuDQotIFsgXSAqKsK/VXNhIGNvbHVtbmFzIHF1ZSBleGlzdGVuPyoqIENvbXBhcmEgY29uIGBuYW1lcyhhdGVuY2lvbmVzKWAgeSBjb24gZWwgZGljY2lvbmFyaW8uDQotIFsgXSAqKsK/UXXDqSBoYWNlIGNvbiBsb3MgTkE/Kiogwr9Mb3MgZWxpbWluYSwgbG9zIGN1ZW50YSBvIGxvcyBjb252aWVydGUgZW4gMCBzaW4gYXZpc2FyPw0KLSBbIF0gKirCv0xvcyBmaWx0cm9zIHNvbiBsb3MgcXVlIHBlZGlzdGU/KiogUmV2aXNhIGNhZGEgYGZpbHRlcigpYDogY2F0ZWdvcsOtYXMsIG1hecO6c2N1bGFzLCBtZXNlcy4NCi0gWyBdICoqwr9FbCB0b3RhbCBjdWFkcmE/KiogTGEgc3VtYSBkZSBsb3MgZ3J1cG9zIGRlYmUgZGFyIGVsIHRvdGFsIGVzcGVyYWRvLg0KLSBbIF0gKirCv1B1ZWRlcyBleHBsaWNhcmxvPyoqIFNpIG5vIHB1ZWRlcyBleHBsaWNhciBjYWRhIGzDrW5lYSwgdG9kYXbDrWEgbm8gZXMgdHUgY8OzZGlnby4NCg0KIyMgMy41IEN1YW5kbyBSIGRhIGVycm9yDQoNCnwgTWVuc2FqZSB8IENhdXNhIG3DoXMgcHJvYmFibGUgfA0KfC0tLS18LS0tLXwNCnwgYGNvdWxkIG5vdCBmaW5kIGZ1bmN0aW9uICIlPiUiYCB8IEZhbHRhIGVqZWN1dGFyIGBsaWJyYXJ5KGRwbHlyKWAgfA0KfCBgb2JqZWN0ICdhdGVuY2lvbmVzJyBub3QgZm91bmRgIHwgTm8gZWplY3V0YXN0ZSBlbCBibG9xdWUgZGUgaW1wb3J0YWNpw7NuLCBvIGhheSB1biBlcnJvciBkZSB0aXBlbyB8DQp8IGAuLi4gZG9lcyBub3QgZXhpc3QgaW4gY3VycmVudCB3b3JraW5nIGRpcmVjdG9yeWAgfCBFbCBhcmNoaXZvIG5vIGVzdMOhIGVuIGxhIGNhcnBldGEgZGUgdHJhYmFqbzogcmV2aXNhIGBnZXR3ZCgpYCB8DQp8IGB1bmV4cGVjdGVkIHN5bWJvbGAgLyBgdW5leHBlY3RlZCAnKSdgIHwgRmFsdGEgbyBzb2JyYSB1bmEgY29tYSwgdW4gcGFyw6ludGVzaXMgbyB1bmFzIGNvbWlsbGFzIHwNCnwgYHRoZXJlIGlzIG5vIHBhY2thZ2UgY2FsbGVkICcuLi4nYCB8IEZhbHRhIGluc3RhbGFyIGVsIHBhcXVldGUgfA0KDQo6Ojogey5jYWphIC5pYX0NCioqUHJvbXB0IGRlIGRlcHVyYWNpw7NuKioNCg0KPiBFc3RveSBhcHJlbmRpZW5kbyBSLiBFamVjdXTDqSBlc3RlIGPDs2RpZ286IGBbdHUgY8OzZGlnb11gLiBPYnR1dmUgZXN0ZSBlcnJvcjogYFttZW5zYWplIGNvbXBsZXRvXWAuIEV4cGzDrWNhbWUgZW4gbGVuZ3VhamUgc2ltcGxlIHF1w6kgc2lnbmlmaWNhLCBjdcOhbCBlcyBsYSBjYXVzYSBtw6FzIHByb2JhYmxlIHkgY8OzbW8gbG8gY29ycmlqby4NCjo6Og0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyBQYXJ0ZSA0IMK3IERlc2Fmw61vOiB0dSBwcmVndW50YSwgdHUgY8OzZGlnbywgdHUgYXVkaXRvcsOtYQ0KDQpBaG9yYSB0ZSB0b2NhIGEgdGkgcmVjb3JyZXIgZWwgY2ljbG8gY29tcGxldG8uDQoNCioqUHJvdG9jb2xvIGRlIDI1IG1pbnV0b3MqKg0KDQoxLiAgKipGb3JtdWxhICg1IG1pbikuKiogRXNjcmliZSB0dSBwcmVndW50YSBlbiB1bmEgZnJhc2UgeSBxdcOpIHJlc3VsdGFkbyBlc3BlcmFzIHZlcjogwr91bmEgdGFibGE/LCDCv3VuIGdyw6FmaWNvPw0KMi4gICoqUGlkZSAoNSBtaW4pLioqIEFybWEgZWwgcHJvbXB0IGNvbiBsYXMgc2VpcyBwaWV6YXMuIERlc2NyaWJlIGNvbHVtbmFzOyBubyBwZWd1ZXMgZGF0b3MuDQozLiAgKipFamVjdXRhIHkgYXVkaXRhICgxMCBtaW4pLioqIENvcnJlIGVsIGPDs2RpZ28gbMOtbmVhIHBvciBsw61uZWEgeSBhcGxpY2EgZWwgY2hlY2tsaXN0Lg0KNC4gICoqSW50ZXJwcmV0YSAoNSBtaW4pLioqIEVzY3JpYmUgdW5hIGZyYXNlIGNvbiBlbCBoYWxsYXpnbyB5IG90cmEgY29uIHN1IGzDrW1pdGU6IMK/cXXDqSAqKm5vKiogcGVybWl0ZW4gY29uY2x1aXIgZXN0b3MgZGF0b3M/DQoNCkVsaWdlIHVuYSBkZSBlc3RhcyBwcmVndW50YXMgbyB0cmFlIGxhIHR1eWEuIENhZGEgdW5hIGluY2x1eWUgdW4gcHVudG8gZGUgcGFydGlkYSBxdWUgcHVlZGVzIGVqZWN1dGFyIHkgbHVlZ28gYW1wbGlhci4NCg0KIyMgUHJlZ3VudGEgMSDCtyBEZXRlY3RpdmUNCg0KKirCv1F1w6kgZXhwbGljYSBlbCBzYWx0byBkZSBUTE0gZW4gYWdvc3RvPyoqIFBpc3RhOiBjcnV6YSBlbCBwcm9jZXNvIGNvbiBlbCBtZXMgeSBjb24gYG9yaWdlbl9hdGVuY2lvbmAuDQoNCmBgYHtyIGQxLCBldmFsPUZBTFNFfQ0KYXRlbmNpb25lcyAlPiUNCiAgZmlsdGVyKHByb2Nlc28gPT0gIlRMTSIpICU+JQ0KICBjb3VudChtZXMsIG9yaWdlbl9hdGVuY2lvbikgJT4lDQogIGFycmFuZ2Uob3JpZ2VuX2F0ZW5jaW9uLCBtZXMpDQpgYGANCg0KUHJlZ3VudGFzIHBhcmEgdHUgaW50ZXJwcmV0YWNpw7NuOiDCv3F1w6kgb3JpZ2VuIGNhbWJpYSBtw6FzIGVudHJlIGp1bGlvIHkgYWdvc3RvPyBFc28gcXVlIG9ic2VydmFzLCDCv2VzIHVuIGhhbGxhemdvIG8gdW5hIGhpcMOzdGVzaXM/IMK/Q29uIHF1acOpbiBoYWJyw61hIHF1ZSBoYWJsYXIgcGFyYSBjb25maXJtYXJsbz8NCg0KIyMgUHJlZ3VudGEgMiDCtyBUZXJyaXRvcmlvDQoNCioqwr9RdcOpIHRyZXMgZXNwZWNpYWxpZGFkZXMgY29uY2VudHJhbiBtw6FzIGF0ZW5jaW9uZXMgZW4gdHUgZGVwYXJ0YW1lbnRvPyoqDQoNCmBgYHtyIGQyLCBldmFsPUZBTFNFfQ0KbWlfZGVwYXJ0YW1lbnRvIDwtICJDT0NIQUJBTUJBIiAgICAgIyBjw6FtYmlhbG8gcG9yIGVsIHR1eW8sIGVuIG1hecO6c2N1bGFzIHkgc2luIHRpbGRlcw0KDQphdGVuY2lvbmVzICU+JQ0KICBmaWx0ZXIoZGVwYXJ0YW1lbnRvID09IG1pX2RlcGFydGFtZW50bywgIWlzLm5hKGVzcGVjaWFsaWRhZCkpICU+JQ0KICBjb3VudChlc3BlY2lhbGlkYWQsIHNvcnQgPSBUUlVFKSAlPiUNCiAgaGVhZCgzKQ0KYGBgDQoNCkN1aWRhZG86IGFsIGV4Y2x1aXIgbG9zIGBOQWAgZGUgYGVzcGVjaWFsaWRhZGAgZGVqYXMgZnVlcmEgdG9kbyBlbCBwcm9jZXNvIFRMTS4gRGlsbyBlbiB0dSBpbnRlcnByZXRhY2nDs24uDQoNCiMjIFByZWd1bnRhIDMgwrcgUGVyc29uYXMNCg0KKirCv0xhIHByb3BvcmNpw7NuIGRlIGNvbnN1bHRhcyBkZSBzZWd1aW1pZW50byBjYW1iaWEgc2Vnw7puIGVsIGdydXBvIGRlIGVkYWQ/KioNCg0KYGBge3IgZDMsIGV2YWw9RkFMU0V9DQphdGVuY2lvbmVzICU+JQ0KICBmaWx0ZXIoIWlzLm5hKHRpcG9fY29uc3VsdGEpKSAlPiUNCiAgZ3JvdXBfYnkoZ3J1cG9fZWRhZCkgJT4lDQogIHN1bW1hcmlzZShhdGVuY2lvbmVzICAgICAgPSBuKCksDQogICAgICAgICAgICBwY3Rfc2VndWltaWVudG8gPSByb3VuZCgxMDAgKiBtZWFuKHRpcG9fY29uc3VsdGEgPT0gIlNFR1VJTUlFTlRPIiksIDEpKQ0KYGBgDQoNCiMjIFByZWd1bnRhIDQgwrcgUHJvZ3JhbWFzDQoNCioqwr9RdcOpIHBvcmNlbnRhamUgZGUgYXRlbmNpb25lcyB0aWVuZSBtYXJjYWRvIGVsIGVqZSBFTlQgZW4gY2FkYSBkZXBhcnRhbWVudG8/KioNCg0KYGBge3IgZDQsIGV2YWw9RkFMU0V9DQphdGVuY2lvbmVzICU+JQ0KICBncm91cF9ieShkZXBhcnRhbWVudG8pICU+JQ0KICBzdW1tYXJpc2UocGN0X2VqZV9lbnQgPSByb3VuZCgxMDAgKiBtZWFuKGVqZV9lbnQgPT0gMSksIDEpKSAlPiUNCiAgYXJyYW5nZShkZXNjKHBjdF9lamVfZW50KSkNCmBgYA0KDQpSZWN1ZXJkYTogMCBzaWduaWZpY2EgInNpbiBtYXJjYWNpw7NuIiwgbm8gIm5vIGNvcnJlc3BvbmRlIGEgRU5UIi4NCg0KIyMgUHJlZ3VudGEgNSDCtyBUZWxlZWR1Y2FjacOzbg0KDQoqKsK/Q8OzbW8gZXZvbHVjaW9uYW4gcG9yIG1lcyBsb3MgYmVuZWZpY2lhcmlvcyBkZSB0ZWxlY2FwYWNpdGFjacOzbiBlbiB0dSBkZXBhcnRhbWVudG8/KioNCg0KYGBge3IgZDUsIGV2YWw9RkFMU0V9DQp0ZWxlX21lbnN1YWwgJT4lDQogIGZpbHRlcihkZXBhcnRhbWVudG8gPT0gIkNPQ0hBQkFNQkEiLA0KICAgICAgICAgdGlwb19hY3RpdmlkYWQgPT0gInRlbGVjYXBhY2l0YWNpb24iLA0KICAgICAgICAgZXN0YWRvX3JlcG9ydGUgPT0gInJlcG9ydGFkbyIsDQogICAgICAgICAhaXMubmEoYmVuZWZpY2lhcmlvc190b3RhbCkpICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBtZXMsIHkgPSBiZW5lZmljaWFyaW9zX3RvdGFsKSkgKw0KICBnZW9tX2xpbmUoY29sb3IgPSAiIzI3NkRDMyIpICsNCiAgZ2VvbV9wb2ludChjb2xvciA9ICIjMjc2REMzIikgKw0KICBmYWNldF93cmFwKH4gYW5pbykgKw0KICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gMToxMikgKw0KICBsYWJzKHRpdGxlID0gIkJlbmVmaWNpYXJpb3MgbWVuc3VhbGVzIGRlIHRlbGVjYXBhY2l0YWNpw7NuIiwNCiAgICAgICB4ID0gIk1lcyIsIHkgPSAiQmVuZWZpY2lhcmlvcyIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQpgYGANCg0KYGZhY2V0X3dyYXAofiBhbmlvKWAgZGlidWphIHVuIHBhbmVsIHBlcXVlw7FvIHBvciBjYWRhIGHDsW8uDQoNCiMjIFByZWd1bnRhIDYgwrcgQ2FsaWRhZA0KDQoqKsK/SGF5IGVzcGVjaWFsaWRhZGVzIHF1ZSBwYXJlY2VuIGxhIG1pc21hIGNvbiBkaXN0aW50byBub21icmU/KioNCg0KYGBge3IgZDYsIGV2YWw9RkFMU0V9DQphdGVuY2lvbmVzICU+JQ0KICBmaWx0ZXIoIWlzLm5hKGVzcGVjaWFsaWRhZCkpICU+JQ0KICBjb3VudChlc3BlY2lhbGlkYWQpICU+JQ0KICBhcnJhbmdlKGVzcGVjaWFsaWRhZCkgJT4lDQogIHByaW50KG4gPSBJbmYpDQpgYGANCg0KUmVjb3JyZSBsYSBsaXN0YSB5IGFub3RhIGxvcyBwYXJlcyBzb3NwZWNob3Nvcy4gTm8gbG9zIGNvcnJpamFzIHRvZGF2w61hOiBtYcOxYW5hIGRlY2lkaXJlbW9zIGp1bnRvcyBxdcOpIGhhY2VyIGNvbiBlbGxvcyB5IGPDs21vIGRvY3VtZW50YXJsby4NCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMgUGFydGUgNSDCtyBDZXJyYXIgbGEgc2VzacOzbiBjb24gb3JkZW4NCg0KIyMgNS4xIEd1YXJkYXIgcmVzdWx0YWRvcw0KDQpMb3MgZGF0b3Mgb3JpZ2luYWxlcyBubyBzZSBtb2RpZmljYW4gbnVuY2EuIExvcyByZXN1bHRhZG9zIHNlIGd1YXJkYW4gYXBhcnRlLCBlbiBsYSBjYXJwZXRhIGByZXN1bHRhZG9zYDoNCg0KYGBge3IgYy1ndWFyZGFyfQ0KZGlyLmNyZWF0ZSgicmVzdWx0YWRvcyIsIHNob3dXYXJuaW5ncyA9IEZBTFNFKQ0KDQpmcmVjdWVuY2lhX2RlcGFydGFtZW50byA8LSBhdGVuY2lvbmVzICU+JQ0KICBjb3VudChkZXBhcnRhbWVudG8sIHNvcnQgPSBUUlVFKSAlPiUNCiAgbXV0YXRlKHBvcmNlbnRhamUgPSByb3VuZCgxMDAgKiBuIC8gc3VtKG4pLCAxKSkNCg0Kd3JpdGVfY3N2KGZyZWN1ZW5jaWFfZGVwYXJ0YW1lbnRvLCAicmVzdWx0YWRvcy9mcmVjdWVuY2lhX2RlcGFydGFtZW50b19kaWExLmNzdiIpDQp3cml0ZV9jc3YoZmFsdGFudGVzLCAicmVzdWx0YWRvcy9mYWx0YW50ZXNfcG9yX3ZhcmlhYmxlX2RpYTEuY3N2IikNCmBgYA0KDQpgd3JpdGVfY3N2KClgIGVzIGVsIGludmVyc28gZGUgYHJlYWRfY3N2KClgOiBndWFyZGEgdW5hIHRhYmxhIGNvbW8gYXJjaGl2byBDU1YuIFB1ZWRlcyBhYnJpcmxvIGx1ZWdvIGVuIEV4Y2VsLg0KDQojIyA1LjIgUmVnaXN0cmFyIGNvbiBxdcOpIHRyYWJhamFzdGUNCg0KYGBge3IgYy1zZXNpb259DQpzZXNzaW9uSW5mbygpDQpgYGANCg0KYHNlc3Npb25JbmZvKClgIG11ZXN0cmEgbGEgdmVyc2nDs24gZGUgUiB5IGRlIGxvcyBwYXF1ZXRlcyBjYXJnYWRvcy4gRXMgZWwgcmVnaXN0cm8gcXVlIHBlcm1pdGUgYSBvdHJhIHBlcnNvbmEsIG8gYSB0aSBkZW50cm8gZGUgdW5vcyBtZXNlcywgcmVwcm9kdWNpciBlbCBhbsOhbGlzaXMgZW4gbGFzIG1pc21hcyBjb25kaWNpb25lcy4NCg0KIyMgNS4zIEdlbmVyYXIgdHUgaW5mb3JtZSB5IGNlcnJhcg0KDQotICoqR3VhcmRhKiogZXN0ZSBhcmNoaXZvIGNvbiA8a2JkPkN0cmw8L2tiZD4gKyA8a2JkPlM8L2tiZD4uDQotIFB1bHNhIGVsIGJvdMOzbiAqKktuaXQqKiAoYXJyaWJhIGRlbCBlZGl0b3IpIHBhcmEgY29udmVydGlyIGVzdGUgZG9jdW1lbnRvIGVuIHVuIGluZm9ybWUgSFRNTCBjb24gdG9kb3MgdHVzIHJlc3VsdGFkb3MgeSBncsOhZmljb3MuIEVzZSBpbmZvcm1lIGVzIHR1IHByb2R1Y3RvIGRlbCBkw61hLg0KLSBBbCBjZXJyYXIgUlN0dWRpbywgc2kgcHJlZ3VudGEgKlNhdmUgd29ya3NwYWNlIGltYWdlPyosIHJlc3BvbmRlICoqRG9uJ3QgU2F2ZSoqLiBMbyBxdWUgaGFjZSByZXByb2R1Y2libGUgdHUgdHJhYmFqbyBlcyBlbCBzY3JpcHQsIG5vIGxhIG1lbW9yaWEgZ3VhcmRhZGEuIE1hw7FhbmEgYWJyZXMgZWwgcHJveWVjdG8geSBlamVjdXRhcyBkZSBudWV2by4NCg0KIyMgNS40IFBhcmEgcHJhY3RpY2FyIHBvciB0dSBjdWVudGENCg0KMS4gIENhbWJpYSBgdXNhcl9tdWVzdHJhIDwtIFRSVUVgIHBvciBgRkFMU0VgIGVuIGVsIHB1bnRvIDIuMiwgdnVlbHZlIGEgZWplY3V0YXIgdG9kbyB5IGNvbXBhcmEgbG9zIHBvcmNlbnRhamVzIHBvciBkZXBhcnRhbWVudG8geSBwb3IgcHJvY2Vzby4gwr9MYSBtdWVzdHJhIHJlcHJlc2VudGEgYmllbiBhIGxhIGJhc2UgY29tcGxldGE/DQoyLiAgUmVwaXRlIGVsIGdyw6FmaWNvIGRlIGF0ZW5jaW9uZXMgcG9yIG1lcyB5IHByb2Nlc28sIHBlcm8gc29sbyBwYXJhIHR1IGRlcGFydGFtZW50by4NCjMuICBDYWxjdWxhIHF1w6kgcG9yY2VudGFqZSBkZSBhdGVuY2lvbmVzIHRpZW5lIHJlZ2lzdHJhZG8gY2FkYSBkaXNwb3NpdGl2by4gUGlzdGE6IGBtZWFuKGF0ZW5jaW9uZXMkdXNhX2dsdWNvbWV0cm8pICogMTAwYC4NCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMgQW5leG8gwrcgQ2h1bGV0YSBkZWwgRMOtYSAxDQoNCiMjIEZ1bmNpb25lcyBxdWUgdXNhbW9zIGhveQ0KDQp8IEZ1bmNpw7NuIHwgUXXDqSBoYWNlIHwgRWplbXBsbyB8DQp8LS0tLXwtLS0tfC0tLS18DQp8IGA8LWAgfCBHdWFyZGEgdW4gdmFsb3IgZW4gdW4gb2JqZXRvIHwgYHggPC0gNWAgfA0KfCBgYygpYCB8IENvbWJpbmEgdmFsb3JlcyBlbiB1biB2ZWN0b3IgfCBgYyg2LCA3LCA4LCA5KWAgfA0KfCBgY2xhc3MoKWAgfCBUaXBvIGRlIHVuIG9iamV0byB8IGBjbGFzcyh4KWAgfA0KfCBgc3VtKClgLCBgbWVhbigpYCwgYG1heCgpYCwgYHJvdW5kKClgIHwgUmVzw7ptZW5lcyBudW3DqXJpY29zIHwgYG1lYW4oeCwgbmEucm0gPSBUUlVFKWAgfA0KfCBgaXMubmEoKWAgfCDCv0VzIGZhbHRhbnRlPyB8IGBzdW0oaXMubmEoeCkpYCB8DQp8IGBnZXR3ZCgpYCwgYGxpc3QuZmlsZXMoKWAgfCBDYXJwZXRhIGRlIHRyYWJham8geSBzdSBjb250ZW5pZG8gfCBgZ2V0d2QoKWAgfA0KfCBgaW5zdGFsbC5wYWNrYWdlcygpYCB8IEluc3RhbGEgdW4gcGFxdWV0ZSAodW5hIHZleikgfCBgaW5zdGFsbC5wYWNrYWdlcygiZHBseXIiKWAgfA0KfCBgbGlicmFyeSgpYCB8IENhcmdhIHVuIHBhcXVldGUgKGNhZGEgc2VzacOzbikgfCBgbGlicmFyeShkcGx5cilgIHwNCnwgYHJlYWRfY3N2KClgLCBgd3JpdGVfY3N2KClgIHwgSW1wb3J0YXIgeSBleHBvcnRhciBDU1YgfCBgcmVhZF9jc3YoImFyY2hpdm8uY3N2IilgIHwNCnwgYG5yb3coKWAsIGBuY29sKClgLCBgbmFtZXMoKWAgfCBUYW1hw7FvIHkgbm9tYnJlcyBkZSB1bmEgdGFibGEgfCBgbnJvdyhhdGVuY2lvbmVzKWAgfA0KfCBgZ2xpbXBzZSgpYCwgYGhlYWQoKWAsIGBWaWV3KClgIHwgTWlyYXIgdW5hIHRhYmxhIHwgYGdsaW1wc2UoYXRlbmNpb25lcylgIHwNCnwgYCU+JWAgfCAiWSBsdWVnbyIgfCBgZGF0b3MgJT4lIGNvdW50KHgpYCB8DQp8IGBjb3VudCgpYCB8IEN1ZW50YSBmaWxhcyBwb3IgY2F0ZWdvcsOtYSB8IGBjb3VudChkZXBhcnRhbWVudG8sIHNvcnQgPSBUUlVFKWAgfA0KfCBgZmlsdGVyKClgIHwgU2VsZWNjaW9uYSBmaWxhcyB8IGBmaWx0ZXIobWVzID09IDgpYCB8DQp8IGBzZWxlY3QoKWAgfCBTZWxlY2Npb25hIGNvbHVtbmFzIHwgYHNlbGVjdChtZXMsIHByb2Nlc28pYCB8DQp8IGBhcnJhbmdlKClgIHwgT3JkZW5hIGZpbGFzIHwgYGFycmFuZ2UoZGVzYyhuKSlgIHwNCnwgYG11dGF0ZSgpYCB8IENyZWEgbyBtb2RpZmljYSBjb2x1bW5hcyB8IGBtdXRhdGUocGN0ID0gMTAwICogbiAvIHN1bShuKSlgIHwNCnwgYGdyb3VwX2J5KClgICsgYHN1bW1hcmlzZSgpYCB8IFJlc3VtZW4gcG9yIGdydXBvIHwgYGdyb3VwX2J5KHByb2Nlc28pICU+JSBzdW1tYXJpc2UobiA9IG4oKSlgIHwNCnwgYG5fZGlzdGluY3QoKWAgfCBDdWVudGEgdmFsb3JlcyBkaXN0aW50b3MgfCBgbl9kaXN0aW5jdChyZWdpc3Ryb19pZClgIHwNCnwgYGdncGxvdCgpYCArIGBhZXMoKWAgKyBgZ2VvbV9jb2woKWAgfCBHcsOhZmljbyBkZSBiYXJyYXMgfCB2ZXIgcHVudG8gMi45IHwNCnwgYGxhYnMoKWAsIGB0aGVtZV9taW5pbWFsKClgIHwgVMOtdHVsb3MgeSBlc3RpbG8gZGVsIGdyw6FmaWNvIHwgdmVyIHB1bnRvIDIuOSB8DQp8IGBnZ3NhdmUoKWAgfCBHdWFyZGEgZWwgw7psdGltbyBncsOhZmljbyB8IGBnZ3NhdmUoImdyYWZpY28ucG5nIilgIHwNCg0KIyMgR2xvc2FyaW8NCg0KfCBUw6lybWlubyB8IFNpZ25pZmljYWRvIHwNCnwtLS0tfC0tLS18DQp8ICoqU2NyaXB0KiogfCBBcmNoaXZvIGRlIHRleHRvIGNvbiBpbnN0cnVjY2lvbmVzIGRlIFIsIGd1YXJkYWRvIHBhcmEgcmVwZXRpcmxhcyB8DQp8ICoqT2JqZXRvKiogfCBVbiBub21icmUgcXVlIGd1YXJkYSB1biB2YWxvciwgdW4gdmVjdG9yIG8gdW5hIHRhYmxhIHwNCnwgKipWZWN0b3IqKiB8IFNlcmllIGRlIHZhbG9yZXMgZGVsIG1pc21vIHRpcG8gfA0KfCAqKkRhdGEgZnJhbWUqKiB8IFRhYmxhIGRlIGRhdG9zOiBmaWxhcyBzb24gb2JzZXJ2YWNpb25lcywgY29sdW1uYXMgc29uIHZhcmlhYmxlcyB8DQp8ICoqRnVuY2nDs24qKiB8IEluc3RydWNjacOzbiBjb24gbm9tYnJlIHF1ZSByZWNpYmUgYXJndW1lbnRvcyB5IGRldnVlbHZlIHVuIHJlc3VsdGFkbyB8DQp8ICoqQXJndW1lbnRvKiogfCBEYXRvIHUgb3BjacOzbiBxdWUgc2UgZW50cmVnYSBhIHVuYSBmdW5jacOzbiwgZGVudHJvIGRlbCBwYXLDqW50ZXNpcyB8DQp8ICoqUGFxdWV0ZSoqIHwgQ29uanVudG8gZGUgZnVuY2lvbmVzIGFkaWNpb25hbGVzIHwNCnwgKipOQSoqIHwgRGF0byBmYWx0YW50ZTogIm5vIHNhYmVtb3MiIHwNCnwgKipQaXBlKiogfCBFbCBvcGVyYWRvciBgJT4lYCwgcXVlIGVuY2FkZW5hIHBhc29zIHwNCnwgKipEaXJlY3RvcmlvIGRlIHRyYWJham8qKiB8IENhcnBldGEgZG9uZGUgUiBidXNjYSB5IGd1YXJkYSBhcmNoaXZvcyB8DQp8ICoqVW5pZGFkIGRlIGFuw6FsaXNpcyoqIHwgTG8gcXVlIHJlcHJlc2VudGEgY2FkYSBmaWxhIGRlIHVuYSB0YWJsYSB8DQp8ICoqUmVwcm9kdWNpYmxlKiogfCBRdWUgb3RyYSBwZXJzb25hIHB1ZWRlIHJlcGV0aXIgZWwgYW7DoWxpc2lzIHkgb2J0ZW5lciBsbyBtaXNtbyB8DQoNCiMjIExvIHF1ZSB2aWVuZQ0KDQoqKkTDrWEgMiDCtyBDYWxpZGFkLCBsaW1waWV6YSB5IHRyYW5zZm9ybWFjacOzbi4qKiBBcHJlbmRlcsOhcyBhIGV2YWx1YXIgbGEgY2FsaWRhZCBkZSBsb3MgZGF0b3MsIGxpbXBpYXIgY2F0ZWdvcsOtYXMgaW5jb25zaXN0ZW50ZXMgeSBkZWphciB1bmEgYmFzZSBsaXN0YSBwYXJhIGNvbnN0cnVpciBpbmRpY2Fkb3Jlcy4NCg==