#Crear un vector de datos

datos <- c(100, 200, 300, 400, 500)

#Calcular el total y promedio

total <- sum(datos)
promedio <- mean(datos)

#Mostrar resultados

cat("Total:", total, "\n")
Total: 1500 
cat("Promedio;", promedio, "\n")
Promedio; 300 

Graficar los datos con ggplot2 library(ggplot2) df <- data.frame (x = 1:5, y = datos) LO antes mencionado es una formula para crear una data.frame donde en “X = 1:5” significa que estoy indicando que serán 1 columna y 5 filas respectivamente.

df <- data.frame (x = 1:5, y = datos) 
df

Paleta de hex color para definir a traves de un codigo el color El simbolo “+” añade una linea de codigo que en ggplot es como una

library("ggplot2")
ggplot(df, aes(x = x, y = y)) +
  geom_bar(stat = "identity", fill = "#a346f0") +
  scale_x_continuous(breaks = 1:5,
                     labels = c("Edad", "Peso", "C", "D", "E")) +
  labs(title = "grafico de barras", x = "Índice", y = "Valores")

PARA RENOMBRAR EN ESCALA: scale_x_continuous(breaks = 1:5, labels = c(“Edad”, “Peso”, “C”, “D”, “E”)) +

PARA RENOMBRAR COMO FACTOR: library(ggplot2)

library(ggplot2)

ggplot(df, aes(x = factor(x, labels = c(“A”, “B”, “C”, “D”, “E”)), y = y)) + geom_bar(stat = “identity”, fill = “#fc03df”) + labs(title = “Gráfico de barras”, x = “Índice”, y = “Valores”)

install.packages("gapminder")
library("gapminder")
#install.packages("tidyverse")
library("tidyverse")

gapdata2007 <- gapminder %>% filter(year == 2007)

gapdata2007$year
  [1] 2007 2007 2007 2007 2007 2007 2007 2007 2007
 [10] 2007 2007 2007 2007 2007 2007 2007 2007 2007
 [19] 2007 2007 2007 2007 2007 2007 2007 2007 2007
 [28] 2007 2007 2007 2007 2007 2007 2007 2007 2007
 [37] 2007 2007 2007 2007 2007 2007 2007 2007 2007
 [46] 2007 2007 2007 2007 2007 2007 2007 2007 2007
 [55] 2007 2007 2007 2007 2007 2007 2007 2007 2007
 [64] 2007 2007 2007 2007 2007 2007 2007 2007 2007
 [73] 2007 2007 2007 2007 2007 2007 2007 2007 2007
 [82] 2007 2007 2007 2007 2007 2007 2007 2007 2007
 [91] 2007 2007 2007 2007 2007 2007 2007 2007 2007
[100] 2007 2007 2007 2007 2007 2007 2007 2007 2007
[109] 2007 2007 2007 2007 2007 2007 2007 2007 2007
[118] 2007 2007 2007 2007 2007 2007 2007 2007 2007
[127] 2007 2007 2007 2007 2007 2007 2007 2007 2007
[136] 2007 2007 2007 2007 2007 2007 2007

Escoger objetos geometricos geom_point() aes()

gapdata2007 %>% 
  ggplot(aes(x = gdpPercap, y =  lifeExp, colour = continent)) +
  geom_point(shape=8)

shape es para elegir como quieres la forma en la que se representan los valores en la grafica 0 cuadrado 1 circulo 2 triangulo 4 x 8 * 15 cuadrado relleno 16 circulo relleno 17 triangulo relleno 21 circulo relleno con contorno 22 cuadrado relleno con contorno 23 rombo relleno con contorno

Se puede separar todo en varias graficas agregando una linea extra al codigo

facet_wrap(continent)

gapdata2007 %>% 
  ggplot(aes(x = gdpPercap, y =  lifeExp, colour = continent)) +
  geom_point(shape=8) +
  facet_wrap(~continent)

Elegir el fondo: default theme_bw theme_dark theme_classic

gapdata2007 %>% 
  ggplot(aes(x = gdpPercap, y =  lifeExp, colour = continent)) +
  geom_point(shape=8) +
  facet_wrap(~continent) +
  theme_bw()

¿Qué son las Estadísticas Vitales? Las estadísticas vitales son métricas clave que permiten analizar el estado de salud de una población. Entre las más comunes se encuentran:

Tasa de mortalidad: Número de muertes por cada 1,000 habitantes.

Tasa de letalidad: Porcentaje de personas fallecidas entre las que padecieron una enfermedad específica.

Crear un Dataset Ficticio Usaremos un conjunto de datos ficticio para realizar los cálculos:

# Crear un conjunto de datos ficticio
datos <- data.frame(
  region = c("Norte", "Sur", "Este", "Oeste"),
  poblacion = c(100000, 75000, 50000, 60000),
  nacimientos = c(1200, 800, 600, 700),
  muertes = c(900, 500, 300, 400),
  casos_enfermedad = c(1500, 1000, 800, 900),
  fallecidos_enfermedad = c(200, 150, 100, 120)
)

# Mostrar los primeros datos
print(datos)

Tasa de Mortalidad ¿Qué es la Tasa de Mortalidad? La Tasa de Mortalidad mide el número de muertes en una población por cada 1,000 habitantes durante un período determinado.

Es una métrica crucial para evaluar el impacto de factores que afectan la salud pública.

Fórmula La fórmula general para calcular la Tasa de Mortalidad es:

Numero de muertes / población total * 1000

Donde:

Población Total: Número total de habitantes en la población.

# Datos
muertes <- 900  # Número de muertes en un año
poblacion_total <- 100000  # Población total en ese año

# Cálculo de la tasa de mortalidad
tasa_mortalidad <- (muertes / poblacion_total) * 1000

# Resultado
cat("Tasa de mortalidad:", tasa_mortalidad, "muertes por cada 1,000 habitantes\n")
Tasa de mortalidad: 9 muertes por cada 1,000 habitantes

Visualización de Tasa de Mortalidad Usamos un gráfico de barras para visualizar las tasas de mortalidad en diferentes regiones:

library(ggplot2)

str(datos)
'data.frame':   4 obs. of  6 variables:
 $ region               : chr  "Norte" "Sur" "Este" "Oeste"
 $ poblacion            : num  100000 75000 50000 60000
 $ nacimientos          : num  1200 800 600 700
 $ muertes              : num  900 500 300 400
 $ casos_enfermedad     : num  1500 1000 800 900
 $ fallecidos_enfermedad: num  200 150 100 120
print(datos)
library(ggplot2)
# Calcular tasas de mortalidad

datos$tasa_mortalidad <- (datos$muertes / datos$poblacion) * 1000

print(datos)

GRAFICOS:

# Gráfico
ggplot(datos, aes(x = region, y = tasa_mortalidad, fill = region)) +
  geom_bar(stat = "identity") +
  labs(title = "Tasa de Mortalidad por Región", x = "Región", y = "Tasa de Mortalidad (por 1,000 habitantes)") +
  theme_classic()

Conclusión La Tasa de Mortalidad es un indicador fundamental para medir el impacto de condiciones de salud y factores socioeconómicos en una población.

Comparar tasas entre regiones puede ayudar a identificar áreas que necesitan mayor atención.

Tasa de Letalidad ¿Qué es la Tasa de Letalidad? La Tasa de Letalidad mide el porcentaje de personas fallecidas entre las que han padecido una enfermedad específica.

Es una métrica crucial para evaluar la gravedad de una enfermedad y la efectividad de las intervenciones médicas.

Fórmula La fórmula general para calcular la Tasa de Letalidad es:

Tasa de letalidad = numero de fallecidos / numero de casos * 100 =

Donde:

-Número de Casos: Total de personas diagnosticadas con la enfermedad.

Implementación en R Calculemos la Tasa de Letalidad usando un ejemplo práctico:

Datos_fallecidos <- 200 # Número de fallecidos por la enfermedad 
casos <- 1500 # Número total de casos registrados

# Cálculo de la tasa de letalidad

tasa_letalidad <- (Datos_fallecidos / casos) * 100

# Resultado

cat("Tasa de letalidad:", tasa_letalidad, "% de los casos fallecen\n")
Tasa de letalidad: 13.33333 % de los casos fallecen
# Calcular tasas de letalidad
datos$tasa_letalidad <- (datos$fallecidos / datos$casos) * 100                
# Gráfico
ggplot(datos, aes(x = region, y = tasa_letalidad, fill = region)) +
  geom_bar(stat = "identity") +
  labs(title = "Tasa de Letalidad por Región",
       x = "Región", y = "Tasa de Letalidad (%)") +
  theme_minimal()

Conclusión La Tasa de Letalidad es un indicador clave para evaluar la severidad de una enfermedad y el impacto de las intervenciones médicas. Un análisis detallado puede ayudar a identificar áreas prioritarias para la asignación de recursos.

Tasa de Natalidad ¿Qué es la Tasa de Natalidad? La Tasa de Natalidad mide el número de nacimientos en una población por cada 1,000 habitantes durante un período determinado.

Es una métrica clave para analizar la dinámica poblacional y evaluar tendencias demográficas.

Fórmula La fórmula general para calcular la Tasa de Natalidad es:

Tasa de natalidad = numero de nacimientos / total de la población * 1000 =

Implementación en R Calculemos la Tasa de Natalidad usando un ejemplo práctico:

# Datos

nacimientos <- 1500  # Número de nacimientos en un año
poblacion_total <- 100000  # Población total en ese año

# Cálculo de la tasa de natalidad
tasa_natalidad <- (nacimientos / poblacion_total) * 1000

# Resultado
cat("Tasa de natalidad:", tasa_natalidad, "nacimientos por cada 1,000 habitantes\n")
Tasa de natalidad: 15 nacimientos por cada 1,000 habitantes
datos
# Calcular tasas de natalidad
datos$tasa_natalidad <- (datos$nacimientos / datos$poblacion) * 1000

# Gráfico
ggplot(datos, aes(x = region, y = tasa_natalidad, fill = region)) +
  geom_bar(stat = "identity") +
  labs(title = "Tasa de Natalidad por Región",
       x = "Región", y = "Tasa de Natalidad (por 1,000 habitantes)") +
  theme_minimal()

Conclusión La Tasa de Natalidad es una herramienta esencial en epidemiología para entender la dinámica poblacional. Usarla en conjunto con otras métricas puede proporcionar una visión completa de las tendencias demográficas y necesidades de salud pública.

Ventajas de flextable: Interactividad: Permite crear tablas fácilmente exportables a Word o PowerPoint.

Estilo Personalizable: Cambia colores, fuentes y bordes sin complicaciones.

Soporte para Reportes: Ideal para informes en formatos reproducibles.

Ambas opciones (kableExtra y flextable) son más simples y versátiles que gtable para este caso de uso.


#install.packages("tidyverse")

library(tidyverse)
install.packages("flextable")
library(flextable)
# Crear el conjunto de datos
datos <- data.frame(
  region = c("Norte", "Sur", "Este", "Oeste"),
  poblacion = c(100000, 75000, 50000, 60000),
  nacimientos = c(1200, 800, 600, 700),
  muertes = c(900, 500, 300, 400),
  casos_enfermedad = c(1500, 1000, 800, 900),
  fallecidos_enfermedad = c(200, 150, 100, 120)
)

# Calcular las tasas
datos$tasa_natalidad <- (datos$nacimientos / datos$poblacion) * 1000
datos$tasa_mortalidad <- (datos$muertes / datos$poblacion) * 1000
datos$tasa_incidencia <- (datos$casos_enfermedad / datos$poblacion) * 1000
datos$tasa_letalidad <- (datos$fallecidos_enfermedad / datos$casos_enfermedad) * 100

# Redondear solo columnas numéricas
datos_redondeados <- datos %>%
  mutate_if(is.numeric, round, 2)

# Crear la tabla con flextable
flextable(datos_redondeados) %>%
  autofit()

region

poblacion

nacimientos

muertes

casos_enfermedad

fallecidos_enfermedad

tasa_natalidad

tasa_mortalidad

tasa_incidencia

tasa_letalidad

Norte

100,000

1,200

900

1,500

200

12.00

9.00

15.00

13.33

Sur

75,000

800

500

1,000

150

10.67

6.67

13.33

15.00

Este

50,000

600

300

800

100

12.00

6.00

16.00

12.50

Oeste

60,000

700

400

900

120

11.67

6.67

15.00

13.33

Cálculo de Frecuencias

El análisis de frecuencias es una técnica básica pero esencial en epidemiología. Permite describir cómo se distribuyen los datos en categorías específicas y es especialmente útil para analizar variables categóricas como sexo, grupo de edad, o estado de enfermedad.

Cargar Librerías Primero, cargamos las librerías necesarias para el análisis:

library(dplyr)    
# Para manipulación de datos %>% %>% %>% %>% %>%  control + shift + m = 
# %>% %>% %>% %>% %>% %>% %>% 
library(ggplot2)   # Para visualización de datos
library(tidyverse)

# control + alt + *+ = ~ 

#install.packages("janitor")
library(janitor)   # Para generar tablas de frecuencias

Crear un Conjunto de Datos de Ejemplo Usaremos un dataset ficticio para calcular las frecuencias:

# Crear un conjunto de datos ficticio
datos <- data.frame(
  sexo = c("F", "M", "F", "F", "M", "F", "M", "M", "F", "M"),
  grupo_edad = c("0-19", "20-39", "20-39", "40-59", "40-59", "60+", "20-39", "60+", "40-59", "0-19")
)

# Mostrar los primeros datos
head(datos)
str(datos)
'data.frame':   10 obs. of  2 variables:
 $ sexo      : chr  "F" "M" "F" "F" ...
 $ grupo_edad: chr  "0-19" "20-39" "20-39" "40-59" ...

Tablas de Frecuencias Tabla de Frecuencias con table Podemos calcular frecuencias simples usando table:

# Calcular frecuencias para la variable "sexo"
tabla_sexo <- table(datos$sexo)
tabla_sexo

F M 
5 5 

Tabla de Frecuencias Cruzadas con table Para analizar dos variables:

# Tabla cruzada entre "sexo" y "grupo_edad"
tabla_cruzada <- table(datos$sexo, datos$grupo_edad)
tabla_cruzada
   
    0-19 20-39 40-59 60+
  F    1     1     2   1
  M    1     2     1   1

Tablas Limpias con janitor El paquete janitor genera tablas de frecuencias ordenadas:

#install.packages("janitor")
library(janitor)
# Tabla de frecuencias con proporciones

tabla_ordenada <- datos %>%
  tabyl(sexo) %>%
  adorn_totals("row") %>%
  adorn_pct_formatting()

tabla_ordenada
  sexo  n percent
     F  5   50.0%
     M  5   50.0%
 Total 10  100.0%
flextable(tabla_ordenada)

sexo

n

percent

F

5

50.0%

M

5

50.0%

Total

10

100.0%

Paquete janitor ¿Qué es janitor? janitor es un paquete diseñado para limpiar datos y realizar análisis exploratorios rápidos. Es particularmente útil para manejar tablas y datos categóricos, como calcular frecuencias y proporciones.

Características Principales de janitor: Limpieza de Nombres de Columnas:

Convierte nombres de columnas en formato limpio y consistente, eliminando espacios o caracteres especiales.

library(janitor)
datos2 <- data.frame("Nombre Columna 1" = c(1, 2), "Otra Columna" = c(3, 4))

datos_clean <- clean_names(datos2) #función clean_names

print(datos_clean)  # nombres: nombre_columna_1, otra_columna

Tablas de Frecuencias:

Genera tablas de frecuencias simples y cruzadas.

Incluye totales y porcentajes para facilitar la interpretación.

library(janitor)
tabla <- datos %>% tabyl(grupo_edad)
print(tabla)
 grupo_edad n percent
       0-19 2     0.2
      20-39 3     0.3
      40-59 3     0.3
        60+ 2     0.2
str(datos)
'data.frame':   10 obs. of  2 variables:
 $ sexo      : chr  "F" "M" "F" "F" ...
 $ grupo_edad: chr  "0-19" "20-39" "20-39" "40-59" ...

Tablas de Frecuencias Tabla de Frecuencias con table Podemos calcular frecuencias simples usando table:

# Calcular frecuencias para la variable "sexo"
tabla_sexo <- table(datos$sexo)
tabla_sexo

F M 
5 5 

Tabla de Frecuencias Cruzadas con table Para analizar dos variables:

# Tabla cruzada entre "sexo" y "grupo_edad"
tabla_cruzada <- table(datos$sexo, datos$grupo_edad)
tabla_cruzada
   
    0-19 20-39 40-59 60+
  F    1     1     2   1
  M    1     2     1   1

Tablas Limpias con janitor El paquete janitor genera tablas de frecuencias ordenadas:

#install.packages("janitor")
library(janitor)
# Tabla de frecuencias con proporciones

tabla_ordenada <- datos %>%
  tabyl(sexo) %>%
  adorn_totals("row") %>%
  adorn_pct_formatting()

tabla_ordenada
  sexo  n percent
     F  5   50.0%
     M  5   50.0%
 Total 10  100.0%
library(flextable)
flextable(tabla_ordenada)

sexo

n

percent

F

5

50.0%

M

5

50.0%

Total

10

100.0%

NA

Paquete janitor ¿Qué es janitor? janitor es un paquete diseñado para limpiar datos y realizar análisis exploratorios rápidos. Es particularmente útil para manejar tablas y datos categóricos, como calcular frecuencias y proporciones.

Características Principales de janitor: Limpieza de Nombres de Columnas:

Convierte nombres de columnas en formato limpio y consistente, eliminando espacios o caracteres especiales.

library(janitor)
datos2 <- data.frame("Nombre Columna 1" = c(1, 2), "Otra Columna" = c(3, 4))

datos_clean <- clean_names(datos2) #función clean_names

print(datos_clean)  # nombres: nombre_columna_1, otra_columna

Tablas de Frecuencias:

Genera tablas de frecuencias simples y cruzadas.

Incluye totales y porcentajes para facilitar la interpretación.

library(janitor)

tabla <- datos %>% tabyl(grupo_edad)

print(tabla)
 grupo_edad n percent
       0-19 2     0.2
      20-39 3     0.3
      40-59 3     0.3
        60+ 2     0.2

Tablas Ordenadas y Proporciones:

Añade totales y porcentajes a tablas de manera automática.

grupoedad<-tabla %>% adorn_totals("row") %>% adorn_pct_formatting()

grupoedad
 grupo_edad  n percent
       0-19  2   20.0%
      20-39  3   30.0%
      40-59  3   30.0%
        60+  2   20.0%
      Total 10  100.0%
flextable(grupoedad)

grupo_edad

n

percent

0-19

2

20.0%

20-39

3

30.0%

40-59

3

30.0%

60+

2

20.0%

Total

10

100.0%

Detección de Duplicados:

Identifica valores duplicados en tus datos.

datos3 <- data.frame(id = c(1, 1, 2, 3), valor = c(10, 10, 20, 30))

datos3
get_dupes(datos3, id)

¿Cuándo usar janitor? Cuando necesitas calcular frecuencias rápidamente.

Para limpiar nombres de columnas automáticamente.

Al trabajar con datos categóricos donde las proporciones y totales son importantes.

Visualización de Frecuencias Gráfico de Barras con ggplot2 Un gráfico de barras es ideal para visualizar frecuencias categóricas:

# Crear un gráfico de barras para "grupo_edad"
ggplot(datos, aes(x = grupo_edad, fill = grupo_edad)) +
  geom_bar() +
  labs(title = "Frecuencia por Grupo de Edad",
       x = "Grupo de Edad",
       y = "Frecuencia") +
  theme_minimal()

datos %>% ggplot(aes(grupo_edad, fill= grupo_edad))+
  geom_bar()

Gráfico de Pastel con ggplot2 Un gráfico de pastel es útil para mostrar proporciones:

# Calcular proporciones
proporciones <- datos %>%
  group_by(sexo) %>%
  summarise(frecuencia = n()) %>%
  mutate(proporcion = frecuencia / sum(frecuencia))

# Crear un gráfico de pastel
ggplot(proporciones, aes(x = "", y = proporcion, fill = sexo)) +
  geom_col(width = 1) +
  coord_polar(theta = "y") +
   theme_void()+
  labs(title = "Distribución por Sexo") 

sexo

n

percent

F

5

50.0%

M

5

50.0%

Total

10

100.0%

Tablas y Gráficos en un Reporte Combinar Resultados en una Tabla Resumen Podemos combinar frecuencias y proporciones en una tabla clara:

# Tabla resumen de frecuencias y proporciones
tabla_resumen <- datos %>%
  group_by(sexo) %>%
  summarise(
    frecuencia = n(),
    proporcion = n() / nrow(datos)
  )

tabla_resumen

Exportar Gráficos y Tablas Guardar gráficos y tablas para incluirlos en reportes:

# Guardar el gráfico de barras como imagen
ggsave("frecuencia_grupo_edad.png")

Medidas de frecuencia Las medidas de frecuencia son herramientas clave en epidemiología para cuantificar el impacto de enfermedades en una población. Las principales métricas son:

Prevalencia: Proporción de individuos enfermos en un momento concreto. Incidencia acumulada: Probabilidad de que un individuo enferme durante un período. Densidad de incidencia: Velocidad con la que aparecen nuevos casos en una población. Dataset Simulado Crear un Dataset Ficticio Usamos un conjunto de datos simulado para calcular estas métricas.

# Crear un conjunto de datos ficticio
datos <- data.frame(
  id = 1:100,  # Identificación de individuos
  inicio_riesgo = c(rep(0, 80), rep(1, 20)),  # 0 = no enfermo al inicio 1= enfermo 
  casos_nuevos = c(rep(0, 90), rep(1, 10)),  # Casos nuevos al final del período
  tiempo_observacion = c(rep(1, 50), rep(2, 30), rep(3, 20))  # Tiempo observado en años
  )

# Mostrar los primeros datos
head(datos)

Cálculo de Prevalencia ¿Qué es la Prevalencia? La prevalencia mide la proporción de personas afectadas por una enfermedad en un momento dado.

Fórmula: Prevalencia = casos existentes / población total

# Calcular prevalencia al inicio del período
poblacion_total <- nrow(datos)
poblacion_total <- 100

casos_existentes <- 20

casos_existentes <- sum(datos$inicio_riesgo == 1)

casos_existentes <- sum(datos$inicio_riesgo != 0)

prevalencia <- casos_existentes / poblacion_total


cat("Prevalencia:", round(prevalencia * 100, 2), "%\n")
Prevalencia: 20 %
cat("Prevalencia:", prevalencia*100, "%\n")
Prevalencia: 20 %

Interpretación: Este valor indica el porcentaje de la población afectada al inicio del estudio.

Cálculo de Incidencia Acumulada ¿Qué es la Incidencia Acumulada? La incidencia acumulada mide la proporción de nuevos casos en una población en riesgo durante un período.

Fórmula:

Incidencia acumulada = casos nuevos observados / población en riesgo al inicio

# Calcular incidencia acumulada
casos_nuevos <- sum(datos$casos_nuevos)

casos_nuevos <- 10

poblacion_riesgo <- sum(datos$inicio_riesgo == 0)

poblacion_riesgo <- sum(datos$inicio_riesgo != 1)

poblacion_riesgo <- 80

incidencia_acumulada <- casos_nuevos / poblacion_riesgo

cat("Incidencia Acumulada:", round(incidencia_acumulada * 100, 2), "%\n")
Incidencia Acumulada: 12.5 %

Interpretación: Este valor indica el riesgo de desarrollar la enfermedad durante el período de observación.

Cálculo de Densidad de Incidencia ¿Qué es la Densidad de Incidencia? La densidad de incidencia mide la velocidad con la que aparecen nuevos casos en una población durante períodos de tiempo acumulados.

Fórmula: densidad de incidencia = casos nuevos observados / ñsuma de los períodes de riesgo

# Calcular densidad de incidencia
tiempo_total <- sum(datos$tiempo_observacion)

densidad_incidencia <- casos_nuevos / tiempo_total

cat("Densidad de Incidencia:", round(densidad_incidencia, 2), "casos por persona-año\n")
Densidad de Incidencia: 0.06 casos por persona-año

Interpretación: Este valor representa cuántos casos nuevos se generan por persona-año.

Visualización de las Métricas Gráfico Comparativo

library(ggplot2)

# Crear datos para el gráfico
resultados <- data.frame(
  medida = c("Prevalencia", "Incidencia Acumulada", "Densidad de Incidencia"),
  valor = c(prevalencia * 100, incidencia_acumulada * 100, densidad_incidencia)
)

# Gráfico de barras
ggplot(resultados, aes(x = medida, y = valor, fill = medida)) +
  geom_bar(stat = "identity") +
  labs(title = "Medidas de Frecuencia Epidemiológica",
       x = "Medida", y = "Valor (%)") +
  theme_minimal()

Herramientas adicionales Número básico de Reproducción R0 Aalgunas herramientas adicionales y modernas que se usan para analizar el impacto de enfermedades en una población incluyen:

El número básico de reproducción R₀ es una métrica clave en epidemiología para estimar cuántos casos secundarios genera un caso inicial en una población completamente susceptible. Es esencial para evaluar el potencial de propagación de enfermedades infecciosas como el COVID-19.

El número básico de reproducción R₀ indica el promedio de casos secundarios generados por un caso inicial en una población completamente susceptible.

Cómo calcular R₀ El cálculo de R₀ puede realizarse usando varias metodologías. Una de las más comunes se basa en la relación:

R0 = β x D

Donde:

β (tasa de transmisión): Probabilidad de que una persona infectada transmita la enfermedad por unidad de tiempo.

D (duración infecciosa): Tiempo promedio durante el cual una persona infectada puede transmitir la enfermedad.

Para calcularlo usando datos reales del COVID-19, podemos usar un data.frame simulado o datos reales si se dispone de ellos.

Usamos un conjunto de datos que simula la propagación del COVID-19 en una población.

## Dataset Simulado de COVID-19

set.seed(123)

datos <- data.frame(
  id = 1:30,
  fecha = seq(as.Date("2020-03-01"), as.Date("2020-03-30"), by = "days"),
  nuevos_casos = round(runif(30, 50, 150)),  # Nuevos casos diarios
  recuperados = round(runif(30, 30, 100)),  # Recuperaciones diarias
  fallecidos = round(runif(30, 1, 10)))      # Fallecimientos diarios


# Mostrar los primeros datos
head(datos)

Calcular R₀: Método Simplificado Definición Para estimar R₀, necesitamos:

Duración infecciosa promedio (D): Asumimos 10 días para COVID-19.

Crecimiento exponencial (λ): Calculado a partir de la tasa de nuevos casos diarios.

Paso 1: Calcular Crecimiento Diario

# Crecimiento diario de casos
datos$crecimiento <- c(NA, diff(datos$nuevos_casos))

# Calcular el promedio del crecimiento diario
lambda <- mean(datos$crecimiento, na.rm = TRUE)

cat("Crecimiento diario promedio (λ):", round(lambda, 2), "casos por día\n")
Crecimiento diario promedio (λ): -0.48 casos por día

Paso 2: Calcular R₀

# Asumimos una duración infecciosa promedio D = 10 días
D <- 10

# Estimación de R₀
R0 <- lambda * D
cat("Número básico de reproducción (R₀):", round(R0, 2), "\n")
Número básico de reproducción (R₀): -4.83 

Visualización del Número R₀ Gráfico de Nuevos Casos

library(ggplot2)

ggplot(datos, aes(x = fecha, y = nuevos_casos)) +
  geom_line(color = "blue", size = 1) +
  geom_point(color = "red") +
  labs(title = "Nuevos Casos Diarios de COVID-19",
       x = "Fecha", y = "Nuevos Casos") +
  theme_minimal()

Reflexión ¿Cómo influye la duración infecciosa promedio (D) en el cálculo de R₀?

¿Qué intervenciones (como cuarentenas o vacunación) pueden reducir β y, por ende, R₀?

¿Qué significa un R₀ > 1 para la propagación de la enfermedad?

Detalles del Ejemplo Duración Infecciosa Promedio (D): Se puede ajustar con base en evidencia clínica, como estudios que estimen cuánto tiempo los pacientes con COVID-19 son infecciosos. Datos Simulados: En este ejemplo, los datos son generados aleatoriamente. Puedes reemplazarlos con datos reales del COVID-19 de fuentes como Our World in Data o bases gubernamentales. Crecimiento Diario (λ): El crecimiento promedio se calcula con la diferencia diaria de nuevos casos.

Uso de Paquetes Adicionales Si deseas usar herramientas específicas para el cálculo de R₀, puedes explorar paquetes como EpiEstim:

install.packages("EpiEstim")
WARNING: Rtools is required to build R packages but is not currently installed. Please download and install the appropriate version of Rtools before proceeding:

https://cran.rstudio.com/bin/windows/Rtools/
probando la URL 'https://cran.rstudio.com/bin/windows/contrib/4.5/SparseM_1.84-2.zip'
probando la URL 'https://cran.rstudio.com/bin/windows/contrib/4.5/MatrixModels_0.5-4.zip'
probando la URL 'https://cran.rstudio.com/bin/windows/contrib/4.5/mcmc_0.9-8.zip'
probando la URL 'https://cran.rstudio.com/bin/windows/contrib/4.5/quantreg_6.1.zip'
probando la URL 'https://cran.rstudio.com/bin/windows/contrib/4.5/MCMCpack_1.7-1.zip'
probando la URL 'https://cran.rstudio.com/bin/windows/contrib/4.5/aweek_1.0.3.zip'
probando la URL 'https://cran.rstudio.com/bin/windows/contrib/4.5/coarseDataTools_0.7.2.zip'
probando la URL 'https://cran.rstudio.com/bin/windows/contrib/4.5/fitdistrplus_1.2-4.zip'
probando la URL 'https://cran.rstudio.com/bin/windows/contrib/4.5/coda_0.19-4.1.zip'
probando la URL 'https://cran.rstudio.com/bin/windows/contrib/4.5/incidence_1.7.6.zip'
probando la URL 'https://cran.rstudio.com/bin/windows/contrib/4.5/EpiEstim_2.2-5.zip'
package ‘SparseM’ successfully unpacked and MD5 sums checked
package ‘MatrixModels’ successfully unpacked and MD5 sums checked
package ‘mcmc’ successfully unpacked and MD5 sums checked
package ‘quantreg’ successfully unpacked and MD5 sums checked
package ‘MCMCpack’ successfully unpacked and MD5 sums checked
package ‘aweek’ successfully unpacked and MD5 sums checked
package ‘coarseDataTools’ successfully unpacked and MD5 sums checked
package ‘fitdistrplus’ successfully unpacked and MD5 sums checked
package ‘coda’ successfully unpacked and MD5 sums checked
package ‘incidence’ successfully unpacked and MD5 sums checked
package ‘EpiEstim’ successfully unpacked and MD5 sums checked

The downloaded binary packages are in
    C:\Users\Silvi\AppData\Local\Temp\RtmpeidSPm\downloaded_packages
library(EpiEstim)

# Calcular R₀ usando EpiEstim (reemplazar con datos reales)
res <- estimate_R(
  incid = datos$nuevos_casos,
  method = "parametric_si",
  config = make_config(list(mean_si = 4, std_si = 2))
)
print(res$R)

Simulamos el brote epidemico (ficticio):

set.seed(123)
dias <- 30

casos <- round(runif(dias, min = 10, max = 100))

datos <- data.frame(
  fecha = seq(as.Date("2020-01-01"), by = "days", length.out = dias),
  nuevos_casos = casos
)

# Mostrar los primeros datos
head(datos)

Configuración del Intervalo Serial Definir el Intervalo Serial El intervalo serial representa el tiempo promedio entre infecciones primarias y secundarias. En COVID-19, estudios sugieren una distribución gamma con:

Media: 4 días

Desviación estándar: 2 días

# Configurar el intervalo serial
config <- make_config(
  list(mean_si = 4, std_si = 2)) # Normalmente, make_config se utiliza para crear o gestionar configuraciones

Calcular el Número de Reproducción Efectivo (Rₑ) Estimar Rₑ Usamos los datos diarios de nuevos casos para calcular Rₑ.

# Estimar Rₑ usando EpiEstim
resultado <- estimate_R(
  incid = datos$nuevos_casos,
  method = "parametric_si",
  config = config
)
# Mostrar resultados
print(resultado$R)

Visualización de Rₑ Gráfico de la Evolución de Rₑ Creamos un gráfico que muestra cómo Rₑ cambia con el tiempo, lo que puede indicar el impacto de intervenciones.

# Gráfico de Rₑ
plot(
  resultado,
  what = "R",
  legend = FALSE,
  main = "Evolución del Número de Reproducción Efectivo (Rₑ)"
)

Interpretación:

Rₑ > 1: La epidemia está en expansión.

Rₑ < 1: La epidemia está disminuyendo.

La función estimate_R del paquete {EpiEstim} en R se utiliza para estimar el número de reproducción efectivo (Rₑ), es una métrica clave en epidemiología para medir la capacidad de transmisión de una enfermedad infecciosa en una población a lo largo del tiempo.

Practica Introducción En esta práctica calcularemos , Rₑ y R para varias enfermedades infecciosas utilizando parámetros reales. Estos valores son cruciales para comprender la dinámica de transmisión y el impacto de medidas de control.

Valores para las enfermedades Infecciosas COVID-19 (SARS-CoV-2):

Tasa de transmisión: ~99.4% (alta transmisión en entornos cerrados) .

Tasa de mortalidad: 0.185% .

Duración infecciosa: ~10 días .

Distanciamiento social: ~30% puede reducir significativamente la transmisión.

Referencia: Estimation of the reproduction number of SARS-CoV-2.

MERS-CoV (Middle East Respiratory Syndrome):

Tasa de transmisión: ~5% (en contactos domiciliarios).

Tasa de mortalidad: ~35%.

Duración infecciosa: ~14 días.

Referencia: Emerging respiratory viral infections: MERS-CoV and influenza.

Influenza Estacional:

Tasa de transmisión: ~50–75%.

Tasa de mortalidad: ~0.1%.

Duración infecciosa: ~7 días.

Referencia: Influenza and tuberculosis co-infection: A systematic review.

Tuberculosis:

Tasa de transmisión: ~30% (en contactos cercanos).

Tasa de mortalidad: ~15%.

Duración infecciosa: Meses a años sin tratamiento.

Referencia: Tuberculosis and Covid-19 Co-Infection.

Sarampión:

Tasa de transmisión: 90–100% (muy alta).

Tasa de mortalidad: ~0.2% en países desarrollados; más alta en contextos sin vacunación.

Duración infecciosa: ~8 días.

Referencia: Molecular and Cellular Mechanisms of M. tuberculosis and SARS-CoV-2 Infections.

Simularemos la propagación de 5 enfermedades infecciosas con diferentes tasas de transmisión, mortalidad y duración infecciosa. Estas simulaciones se basarán en parámetros obtenidos de la literatura científica.

# Parámetros de las enfermedades
enfermedades <- data.frame(
  Enfermedad = c("COVID-19", "MERS-CoV", "Influenza Estacional", "Tuberculosis", "Sarampión"),
  Tasa_Transmision = c(99.4, 5, 60, 30, 95), # Porcentaje
  Tasa_Mortalidad = c(0.185, 35, 0.1, 15, 0.2), # Porcentaje
  Duracion_Infecciosa = c(10, 14, 7, 180, 8), # Días
  Distanciamiento_Social = c(30, 0, 15, 10, 0), # Porcentaje
  Poblacion = c(1000000, 1000000, 1000000, 1000000, 1000000) # Población para simulación
)

Cálculo de R₀ Fórmula de R₀ R₀ (Número básico de reproducción):

Representa el número promedio de infecciones secundarias causadas por un individuo infectado en una población completamente susceptible.

Fórmula: R0 = Tasa de transmisión x Duración infecciosa

# Calcular R₀ para cada enfermedad
enfermedades$R0 <- (enfermedades$Tasa_Transmision / 100) * enfermedades$Duracion_Infecciosa

# Mostrar resultados
enfermedades[c("Enfermedad", "R0")]

Cálculo de Rₑ Fórmula de Rₑ Calcula Re onsiderando que no toda la población es susceptible (debido a inmunidad o intervenciones).

Fórmula:

Re = Ro * susceptibles / población total

# Considerar 70% de la población inicial como susceptibles
enfermedades$Susceptibles <- enfermedades$Poblacion * 0.7

# Calcular Rₑ
enfermedades$Re <- enfermedades$R0 * (enfermedades$Susceptibles / enfermedades$Poblacion)

# Mostrar resultados
enfermedades[c("Enfermedad", "R0", "Re")]

Cálculo de Rₜ Dinámica Temporal de Rₜ Consideramos intervenciones como distanciamiento social y cambios en los susceptibles a lo largo del tiempo.

# Crear un data frame para simular la dinámica temporal
dias <- 30
resultado_rt <- data.frame()

for (i in 1:nrow(enfermedades)) {
  enfermedad <- enfermedades[i, ]
  susceptibles <- enfermedad$Susceptibles
  rt <- numeric(dias)
  
  for (t in 1:dias) {
    rt[t] <- enfermedad$R0 * (susceptibles / enfermedad$Poblacion) * (1 - enfermedad$Distanciamiento_Social / 100)
    susceptibles <- max(0, susceptibles - (susceptibles * enfermedad$Tasa_Transmision / 100)) # Reducir susceptibles
  }
  
  resultado_rt <- rbind(resultado_rt, data.frame(Dia = 1:dias, R_t = rt, Enfermedad = enfermedad$Enfermedad))
}

# Mostrar resultados para una enfermedad
head(resultado_rt[resultado_rt$Enfermedad == "COVID-19", ])

Visualización de Rₜ Gráfico para Cada Enfermedad

library(ggplot2)

# Gráfico de Rₜ
ggplot(resultado_rt, aes(x = Dia, y = R_t, color = Enfermedad)) +
  geom_line(size = 1) +
  geom_hline(yintercept = 1, linetype = "dashed", color = "red") +
  labs(title = "Evolución de Rt por Enfermedad", 
       x = "Días", y = "Rt") +
  theme_minimal() +
  scale_color_brewer(palette = "Set1")

Bonus track: Podemos crear una función en R para simular la enfermedad En el siguiente bloque de codigo se crea la función simular_enfermedad

# Crear una función de simulación
simular_enfermedad <- function(enfermedad, tasa_transmision, tasa_mortalidad, duracion, dist_social, poblacion = 1000) {
  dias <- 30
  infecciosos <- 1  # Casos iniciales
  recuperados <- 0
  fallecidos <- 0
  susceptibles <- poblacion - infecciosos
  
  resultado <- data.frame(Dia = 1:dias, Infectados = NA, Recuperados = NA, Fallecidos = NA, Susceptibles = NA)
  
  for (dia in 1:dias) {
    nuevas_infecciones <- round((infecciosos * tasa_transmision / 100) * (1 - dist_social / 100))
    nuevas_infecciones <- min(nuevas_infecciones, susceptibles)  # No exceder susceptibles
    
    nuevos_recuperados <- ifelse(dia > duracion, infecciosos / duracion, 0)
    nuevos_fallecidos <- round((tasa_mortalidad / 100) * infecciosos)
    
    infecciosos <- infecciosos + nuevas_infecciones - nuevos_recuperados - nuevos_fallecidos
    recuperados <- recuperados + nuevos_recuperados
    fallecidos <- fallecidos + nuevos_fallecidos
    susceptibles <- poblacion - infecciosos - recuperados - fallecidos
    
    resultado[dia, ] <- c(dia, infecciosos, recuperados, fallecidos, susceptibles)
  }
  return(resultado)
}

Una vez creada la función ahora podemos utilizarla: Vamos a crear un objeto que garde los datos simulados de una enfermedad ( puede ser de los datos que ya revisamos en la literatura)

Simulación de ejemplo para COVID-19

sim_covid <- simular_enfermedad( “COVID-19”, tasa_transmision = 99.4, tasa_mortalidad = 20, duracion = 10, dist_social = 30, poblacion = 1000 )

# Simulación de ejemplo para COVID-19
sim_covid <- simular_enfermedad(
  "COVID-19", tasa_transmision = 99.4, tasa_mortalidad = 20, 
  duracion = 10, dist_social = 30, poblacion = 1000
)
library(ggplot2)

# Gráfico para COVID-19
ggplot(sim_covid, aes(x = Dia)) +
  geom_line(aes(y = Infectados, color = "Infectados")) +
  geom_line(aes(y = Recuperados, color = "Recuperados")) +
  geom_line(aes(y = Fallecidos, color = "Fallecidos")) +
  geom_line(aes(y = Susceptibles, color = "Susceptibles")) +
  labs(title = "Simulación de Covid-19", 
       x = "Días", y = "Número de Personas") +
  scale_color_manual(name = "Estado", values = c("Infectados" = "red", 
                                                 "Recuperados" = "green", 
                                                 "Fallecidos" = "black", 
                                                 "Susceptibles" = "blue")) +
  theme_minimal()

Conclusión La simulación permite visualizar la dinámica de transmisión y el impacto de intervenciones como el distanciamiento social. Los resultados pueden usarse para planificar respuestas ante brotes reales.

Además, permite analizar la dinámica de la propagación de enfermedades bajo diferentes escenarios de intervención y parámetros epidemiológicos.

Siguientes pasos:

Ajustar parámetros para escenarios específicos.

Comparar entre diferentes poblaciones y entornos.

Calculo de tamaño muestral Introducción El cálculo del tamaño de muestra es una etapa crucial en el diseño de estudios. Cuando deseamos estimar una proporción, debemos considerar: 1. Nivel de confianza (Zα): Representa la probabilidad de que el intervalo de confianza contenga el valor verdadero. Ejemplo: 95% (( Z = 1.96 )). 2. Precisión deseada (( e )): Diferencia máxima aceptable entre la proporción estimada y la real. 3. Proporción esperada (( p )): Estimación inicial basada en literatura o estudios previos.

En este ejercicio, utilizaremos el paquete pwr para calcular el tamaño de muestra necesario para conocer la prevalencia de diabetes en una población, suponiendo que: - Nivel de confianza: 95% - Precisión deseada: 5% - Proporción esperada: 50% (0.5).

Instalación y configuración de paquetes Primero, asegurémonos de instalar y cargar los paquetes necesarios.

Paquete pwr

# Ejecutar solo una vez si no está instalado:
# install.packages("pwr")

library(pwr)

Fórmula para estimar el tamaño de muestra El cálculo del tamaño de muestra para estimar una proporción se realiza con la fórmula:

Fórmula tamaño de muestra para proporción Donde:

Z: valor correspondiente al nivel de confianza (por ejemplo, Z = 1.96 para 95 %). p: proporción esperada. e: precisión deseada (error máximo permitido). Aunque el paquete pwr simplifica este cálculo, también podemos hacerlo de forma manual paso a paso en R.

Cálculo del tamaño de muestra Método 1: Cálculo manual Utilizando directamente la fórmula clásica para proporciones en población “infinita”:

# Parámetros
Z <- 1.96   # Nivel de confianza para 95 %
p <- 0.5    # Proporción esperada
e <- 0.05   # Precisión deseada (5 %)

# Cálculo del tamaño de muestra
n_manual <- (Z^2 * p * (1 - p)) / (e^2)

cat("Tamaño de muestra necesario (método manual):",
    ceiling(n_manual), "\n")
Tamaño de muestra necesario (método manual): 385 

Método 2: Uso del paquete pwr El paquete pwr proporciona una forma más general, basada en tamaño de efecto (h) y poder estadístico.

Supongamos que queremos detectar una diferencia entre una proporción nula p0 = 0.5 y una proporción alternativa p1 = 0.6 con α = 0.05 y poder 80 %:

# Parámetros para prueba de una proporción
p0 <- 0.5   # Proporción nula
p1 <- 0.6   # Proporción alternativa

# Tamaño de efecto de Cohen (h) para proporciones
h <- ES.h(p1 = p1, p2 = p0)

# Cálculo del tamaño de muestra con pwr.p.test
res_pwr <- pwr.p.test(h = h,
                      sig.level = 0.05,
                      power = 0.80,
                      alternative = "two.sided")

n_pwr <- ceiling(res_pwr$n)

cat("Tamaño de muestra necesario (pwr.p.test):",
    n_pwr, "\n")
Tamaño de muestra necesario (pwr.p.test): 194 

Tamaño de muestra en poblaciones finitas Cuando se conoce el tamaño total de la población, se ajusta el tamaño de muestra mediante la fórmula para poblaciones finitas:

Fórmula población finita Donde:

N: tamaño de la población. Z: valor correspondiente al nivel de confianza. p: proporción esperada. q = 1 − p. d: precisión deseada. En este ejercicio, calculamos el tamaño de muestra para:

N = 10,000 Nivel de confianza: 95 % (Z = 1.96) Proporción esperada: p = 0.05 (5 %) Precisión deseada: d = 0.03 (3 %)

# Parámetros
N <- 10000   # Tamaño de la población
Z <- 1.96    # Nivel de confianza para 95 %
p <- 0.05    # Proporción esperada (5 %)
q <- 1 - p   # Complemento de la proporción
d <- 0.03    # Precisión deseada (3 %)

# Cálculo del tamaño de muestra para población finita
n_finita <- (N * Z^2 * p * q) /
  ((d^2 * (N - 1)) + (Z^2 * p * q))

cat("Tamaño de muestra necesario (N = 10,000):",
    ceiling(n_finita), "\n")
Tamaño de muestra necesario (N = 10,000): 199 

Paso 2: Variación del tamaño de muestra según la precisión Creamos un gráfico que muestre cómo cambia el tamaño de muestra con distintos valores de d:

library(ggplot2)

# Secuencia de precisiones posibles
precisiones <- seq(0.01, 0.10, by = 0.01)

tam_muestra <- sapply(precisiones, function(d){
  (N * Z^2 * p * q) /
    ((d^2 * (N - 1)) + (Z^2 * p * q))
})

# Data frame para graficar
datos_grafico <- data.frame(
  precision      = precisiones,
  tamano_muestra = ceiling(tam_muestra)
)

ggplot(datos_grafico,
       aes(x = precision, y = tamano_muestra)) +
  geom_line() +
  geom_point() +
  labs(
    title = "Relación entre precisión (d) y tamaño de muestra\n(N = 10,000)",
    x     = "Precisión deseada (d)",
    y     = "Tamaño de muestra"
  ) +
  theme_minimal()

Ejemplo adicional: población de 15,000 habitantes Ahora calculamos el tamaño de muestra para estimar la prevalencia de diabetes en una población finita de 15,000 habitantes, con:

Seguridad: 95 % Precisión: 3 % (d = 0.03) Proporción esperada: p = 0.05 (5 %)

# Parámetros
N <- 15000   # Tamaño de la población
Z <- 1.96    # Nivel de confianza para 95 %
p <- 0.05    # Proporción esperada (5 %)
q <- 1 - p
d <- 0.03    # Precisión deseada (3 %)

# Cálculo para población finita
n_finita_15000 <- (N * Z^2 * p * q) /
  ((d^2 * (N - 1)) + (Z^2 * p * q))

cat("Tamaño de muestra necesario (N = 15,000):",
    ceiling(n_finita_15000), "\n")
Tamaño de muestra necesario (N = 15,000): 201 

Exploración gráfica (N = 15,000)

precisiones <- seq(0.01, 0.10, by = 0.01)

tam_muestra <- sapply(precisiones, function(d){
  (N * Z^2 * p * q) /
    ((d^2 * (N - 1)) + (Z^2 * p * q))
})

datos_grafico <- data.frame(
  precision      = precisiones,
  tamano_muestra = ceiling(tam_muestra)
)

ggplot(datos_grafico,
       aes(x = precision, y = tamano_muestra)) +
  geom_line() +
  geom_point() +
  labs(
    title = "Relación entre precisión (d) y tamaño de muestra\n(N = 15,000)",
    x     = "Precisión deseada (d)",
    y     = "Tamaño de muestra"
  ) +
  theme_minimal()

Tamaño de muestra para estimar una media

# Parámetros
Z  <- 1.96   # Nivel de confianza (95 %)
S2 <- 250    # Varianza (S^2)
d  <- 3      # Precisión deseada

# Cálculo del tamaño de muestra
n_media_manual <- (Z^2 * S2) / (d^2)

cat("Tamaño de muestra (media, método manual):",
    ceiling(n_media_manual), "\n")
Tamaño de muestra (media, método manual): 107 

Tamaño de muestra para media con

# Parámetros
Z  <- 1.96   # Nivel de confianza (95 %)
S2 <- 250    # Varianza (S^2)
d  <- 3      # Precisión deseada

# Cálculo del tamaño de muestra
n_manual <- (Z^2 * S2) / (d^2)

cat("Tamaño de muestra (media, método manual):",
    ceiling(n_manual), "\n")
Tamaño de muestra (media, método manual): 107 

Comparación de dos proporciones

# Parámetros
Z_alpha <- 1.645  # Nivel de confianza ~95 % (una cola)
Z_beta  <- 0.842  # Poder estadístico 80 %
p1 <- 0.7         # Proporción 1
p2 <- 0.9         # Proporción 2
p  <- (p1 + p2) / 2  # Promedio de proporciones

# Cálculo del tamaño de muestra (por grupo)
n_dos_prop_manual <- (
  (Z_alpha * sqrt(2 * p * (1 - p)) +
     Z_beta  * sqrt(p1 * (1 - p1) + p2 * (1 - p2)))^2
) / (p1 - p2)^2

cat("Tamaño de muestra por grupo (dos proporciones, manual):",
    ceiling(n_dos_prop_manual), "\n")
Tamaño de muestra por grupo (dos proporciones, manual): 49 
# Cálculo del tamaño del efecto h para dos proporciones
h <- ES.h(p1 = p1, p2 = p2)

# Tamaño de muestra por grupo con pwr.2p.test
res_2p <- pwr.2p.test(
  h         = h,
  sig.level = 0.05,
  power     = 0.80,
  alternative = "two.sided"
)

cat("Tamaño de muestra por grupo (dos proporciones, pwr):",
    ceiling(res_2p$n), "\n")
Tamaño de muestra por grupo (dos proporciones, pwr): 60 

Comparación de dos medias:

# Parámetros
Z_alpha <- 1.645  # Nivel de confianza ~95 % (una cola)
Z_beta  <- 1.282  # Poder estadístico 90 %
S       <- 16     # Desviación estándar
d_mean  <- 15     # Diferencia esperada entre medias

# Cálculo del tamaño de muestra por grupo
n_dos_medias <- (2 * (Z_alpha + Z_beta)^2 * S^2) / (d_mean^2)

cat("Tamaño de muestra por grupo (dos medias, manual):",
    ceiling(n_dos_medias), "\n")
Tamaño de muestra por grupo (dos medias, manual): 20 
LS0tDQp0aXRsZTogIkVzdGFkw61zdGljYXMgVml0YWxlcyBlbiBFcGlkZW1pb2xvZ8OtYSINCmF1dGhvcjogIkxpYy4gU2lsdmlhIE1vbnRlcyINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCiNDcmVhciB1biB2ZWN0b3IgZGUgZGF0b3MNCg0KYGBge3J9DQpkYXRvcyA8LSBjKDEwMCwgMjAwLCAzMDAsIDQwMCwgNTAwKQ0KYGBgDQoNCiNDYWxjdWxhciBlbCB0b3RhbCB5IHByb21lZGlvDQoNCmBgYHtyfQ0KdG90YWwgPC0gc3VtKGRhdG9zKQ0KcHJvbWVkaW8gPC0gbWVhbihkYXRvcykNCmBgYA0KDQojTW9zdHJhciByZXN1bHRhZG9zDQoNCmBgYHtyfQ0KY2F0KCJUb3RhbDoiLCB0b3RhbCwgIlxuIikNCmBgYA0KDQpgYGB7cn0NCmNhdCgiUHJvbWVkaW87IiwgcHJvbWVkaW8sICJcbiIpDQpgYGANCg0KR3JhZmljYXIgbG9zIGRhdG9zIGNvbiBnZ3Bsb3QyIGxpYnJhcnkoZ2dwbG90MikgZGYgXDwtIGRhdGEuZnJhbWUgKHggPSAxOjUsIHkgPSBkYXRvcykgTE8gYW50ZXMgbWVuY2lvbmFkbyBlcyB1bmEgZm9ybXVsYSBwYXJhIGNyZWFyIHVuYSBkYXRhLmZyYW1lIGRvbmRlIGVuICJYID0gMTo1IiBzaWduaWZpY2EgcXVlIGVzdG95IGluZGljYW5kbyBxdWUgc2Vyw6FuIDEgY29sdW1uYSB5IDUgZmlsYXMgcmVzcGVjdGl2YW1lbnRlLg0KDQpgYGB7cn0NCmRmIDwtIGRhdGEuZnJhbWUgKHggPSAxOjUsIHkgPSBkYXRvcykgDQpkZg0KYGBgDQoNClBhbGV0YSBkZSBoZXggY29sb3IgcGFyYSBkZWZpbmlyIGEgdHJhdmVzIGRlIHVuIGNvZGlnbyBlbCBjb2xvciBFbCBzaW1ib2xvICIrIiBhw7FhZGUgdW5hIGxpbmVhIGRlIGNvZGlnbyBxdWUgZW4gZ2dwbG90IGVzIGNvbW8gdW5hDQoNCmBgYHtyfQ0KbGlicmFyeSgiZ2dwbG90MiIpDQpnZ3Bsb3QoZGYsIGFlcyh4ID0geCwgeSA9IHkpKSArDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBmaWxsID0gIiNhMzQ2ZjAiKSArDQogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSAxOjUsDQogICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJFZGFkIiwgIlBlc28iLCAiQyIsICJEIiwgIkUiKSkgKw0KICBsYWJzKHRpdGxlID0gImdyYWZpY28gZGUgYmFycmFzIiwgeCA9ICLDjW5kaWNlIiwgeSA9ICJWYWxvcmVzIikNCmBgYA0KDQpQQVJBIFJFTk9NQlJBUiBFTiBFU0NBTEE6IHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSAxOjUsIGxhYmVscyA9IGMoIkVkYWQiLCAiUGVzbyIsICJDIiwgIkQiLCAiRSIpKSArDQoNClBBUkEgUkVOT01CUkFSIENPTU8gRkFDVE9SOiBsaWJyYXJ5KGdncGxvdDIpDQoNCmxpYnJhcnkoZ2dwbG90MikNCg0KZ2dwbG90KGRmLCBhZXMoeCA9IGZhY3Rvcih4LCBsYWJlbHMgPSBjKCJBIiwgIkIiLCAiQyIsICJEIiwgIkUiKSksIHkgPSB5KSkgKyBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgZmlsbCA9ICIjZmMwM2RmIikgKyBsYWJzKHRpdGxlID0gIkdyw6FmaWNvIGRlIGJhcnJhcyIsIHggPSAiw41uZGljZSIsIHkgPSAiVmFsb3JlcyIpDQoNCmBgYHtyfQ0KaW5zdGFsbC5wYWNrYWdlcygiZ2FwbWluZGVyIikNCmBgYA0KDQpgYGB7cn0NCmxpYnJhcnkoImdhcG1pbmRlciIpDQojaW5zdGFsbC5wYWNrYWdlcygidGlkeXZlcnNlIikNCmxpYnJhcnkoInRpZHl2ZXJzZSIpDQpgYGANCg0KYGBge3J9DQoNCmdhcGRhdGEyMDA3IDwtIGdhcG1pbmRlciAlPiUgZmlsdGVyKHllYXIgPT0gMjAwNykNCg0KZ2FwZGF0YTIwMDckeWVhcg0KYGBgDQoNCkVzY29nZXIgb2JqZXRvcyBnZW9tZXRyaWNvcyBnZW9tX3BvaW50KCkgYWVzKCkNCg0KYGBge3J9DQpnYXBkYXRhMjAwNyAlPiUgDQogIGdncGxvdChhZXMoeCA9IGdkcFBlcmNhcCwgeSA9ICBsaWZlRXhwLCBjb2xvdXIgPSBjb250aW5lbnQpKSArDQogIGdlb21fcG9pbnQoc2hhcGU9OCkNCmBgYA0KDQpzaGFwZSBlcyBwYXJhIGVsZWdpciBjb21vIHF1aWVyZXMgbGEgZm9ybWEgZW4gbGEgcXVlIHNlIHJlcHJlc2VudGFuIGxvcyB2YWxvcmVzIGVuIGxhIGdyYWZpY2EgMCBjdWFkcmFkbyAxIGNpcmN1bG8gMiB0cmlhbmd1bG8gNCB4IDggXCogMTUgY3VhZHJhZG8gcmVsbGVubyAxNiBjaXJjdWxvIHJlbGxlbm8gMTcgdHJpYW5ndWxvIHJlbGxlbm8gMjEgY2lyY3VsbyByZWxsZW5vIGNvbiBjb250b3JubyAyMiBjdWFkcmFkbyByZWxsZW5vIGNvbiBjb250b3JubyAyMyByb21ibyByZWxsZW5vIGNvbiBjb250b3Jubw0KDQpTZSBwdWVkZSBzZXBhcmFyIHRvZG8gZW4gdmFyaWFzIGdyYWZpY2FzIGFncmVnYW5kbyB1bmEgbGluZWEgZXh0cmEgYWwgY29kaWdvDQoNCmZhY2V0X3dyYXAoY29udGluZW50KQ0KDQpgYGB7cn0NCmdhcGRhdGEyMDA3ICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gZ2RwUGVyY2FwLCB5ID0gIGxpZmVFeHAsIGNvbG91ciA9IGNvbnRpbmVudCkpICsNCiAgZ2VvbV9wb2ludChzaGFwZT04KSArDQogIGZhY2V0X3dyYXAofmNvbnRpbmVudCkNCmBgYA0KDQpFbGVnaXIgZWwgZm9uZG86IGRlZmF1bHQgdGhlbWVfYncgdGhlbWVfZGFyayB0aGVtZV9jbGFzc2ljDQoNCmBgYHtyfQ0KZ2FwZGF0YTIwMDcgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSBnZHBQZXJjYXAsIHkgPSAgbGlmZUV4cCwgY29sb3VyID0gY29udGluZW50KSkgKw0KICBnZW9tX3BvaW50KHNoYXBlPTgpICsNCiAgZmFjZXRfd3JhcCh+Y29udGluZW50KSArDQogIHRoZW1lX2J3KCkNCmBgYA0KDQrCv1F1w6kgc29uIGxhcyBFc3RhZMOtc3RpY2FzIFZpdGFsZXM/IExhcyBlc3RhZMOtc3RpY2FzIHZpdGFsZXMgc29uIG3DqXRyaWNhcyBjbGF2ZSBxdWUgcGVybWl0ZW4gYW5hbGl6YXIgZWwgZXN0YWRvIGRlIHNhbHVkIGRlIHVuYSBwb2JsYWNpw7NuLiBFbnRyZSBsYXMgbcOhcyBjb211bmVzIHNlIGVuY3VlbnRyYW46DQoNCi0gICBUYXNhIGRlIG5hdGFsaWRhZDogTsO6bWVybyBkZSBuYWNpbWllbnRvcyBwb3IgY2FkYSAxLDAwMCBoYWJpdGFudGVzLg0KDQpUYXNhIGRlIG1vcnRhbGlkYWQ6IE7Dum1lcm8gZGUgbXVlcnRlcyBwb3IgY2FkYSAxLDAwMCBoYWJpdGFudGVzLg0KDQpUYXNhIGRlIGxldGFsaWRhZDogUG9yY2VudGFqZSBkZSBwZXJzb25hcyBmYWxsZWNpZGFzIGVudHJlIGxhcyBxdWUgcGFkZWNpZXJvbiB1bmEgZW5mZXJtZWRhZCBlc3BlY8OtZmljYS4NCg0KQ3JlYXIgdW4gRGF0YXNldCBGaWN0aWNpbyBVc2FyZW1vcyB1biBjb25qdW50byBkZSBkYXRvcyBmaWN0aWNpbyBwYXJhIHJlYWxpemFyIGxvcyBjw6FsY3Vsb3M6DQoNCmBgYHtyfQ0KIyBDcmVhciB1biBjb25qdW50byBkZSBkYXRvcyBmaWN0aWNpbw0KZGF0b3MgPC0gZGF0YS5mcmFtZSgNCiAgcmVnaW9uID0gYygiTm9ydGUiLCAiU3VyIiwgIkVzdGUiLCAiT2VzdGUiKSwNCiAgcG9ibGFjaW9uID0gYygxMDAwMDAsIDc1MDAwLCA1MDAwMCwgNjAwMDApLA0KICBuYWNpbWllbnRvcyA9IGMoMTIwMCwgODAwLCA2MDAsIDcwMCksDQogIG11ZXJ0ZXMgPSBjKDkwMCwgNTAwLCAzMDAsIDQwMCksDQogIGNhc29zX2VuZmVybWVkYWQgPSBjKDE1MDAsIDEwMDAsIDgwMCwgOTAwKSwNCiAgZmFsbGVjaWRvc19lbmZlcm1lZGFkID0gYygyMDAsIDE1MCwgMTAwLCAxMjApDQopDQoNCiMgTW9zdHJhciBsb3MgcHJpbWVyb3MgZGF0b3MNCnByaW50KGRhdG9zKQ0KYGBgDQoNClRhc2EgZGUgTW9ydGFsaWRhZCDCv1F1w6kgZXMgbGEgVGFzYSBkZSBNb3J0YWxpZGFkPyBMYSBUYXNhIGRlIE1vcnRhbGlkYWQgbWlkZSBlbCBuw7ptZXJvIGRlIG11ZXJ0ZXMgZW4gdW5hIHBvYmxhY2nDs24gcG9yIGNhZGEgMSwwMDAgaGFiaXRhbnRlcyBkdXJhbnRlIHVuIHBlcsOtb2RvIGRldGVybWluYWRvLg0KDQpFcyB1bmEgbcOpdHJpY2EgY3J1Y2lhbCBwYXJhIGV2YWx1YXIgZWwgaW1wYWN0byBkZSBmYWN0b3JlcyBxdWUgYWZlY3RhbiBsYSBzYWx1ZCBww7pibGljYS4NCg0KRsOzcm11bGEgTGEgZsOzcm11bGEgZ2VuZXJhbCBwYXJhIGNhbGN1bGFyIGxhIFRhc2EgZGUgTW9ydGFsaWRhZCBlczoNCg0KTnVtZXJvIGRlIG11ZXJ0ZXMgLyBwb2JsYWNpw7NuIHRvdGFsIFwqIDEwMDANCg0KRG9uZGU6DQoNCi0gICBOw7ptZXJvIGRlIE11ZXJ0ZXM6IFRvdGFsIGRlIHBlcnNvbmFzIGZhbGxlY2lkYXMgZW4gdW4gYcOxby4NCg0KUG9ibGFjacOzbiBUb3RhbDogTsO6bWVybyB0b3RhbCBkZSBoYWJpdGFudGVzIGVuIGxhIHBvYmxhY2nDs24uDQoNCmBgYHtyfQ0KIyBEYXRvcw0KbXVlcnRlcyA8LSA5MDAgICMgTsO6bWVybyBkZSBtdWVydGVzIGVuIHVuIGHDsW8NCnBvYmxhY2lvbl90b3RhbCA8LSAxMDAwMDAgICMgUG9ibGFjacOzbiB0b3RhbCBlbiBlc2UgYcOxbw0KDQojIEPDoWxjdWxvIGRlIGxhIHRhc2EgZGUgbW9ydGFsaWRhZA0KdGFzYV9tb3J0YWxpZGFkIDwtIChtdWVydGVzIC8gcG9ibGFjaW9uX3RvdGFsKSAqIDEwMDANCg0KIyBSZXN1bHRhZG8NCmNhdCgiVGFzYSBkZSBtb3J0YWxpZGFkOiIsIHRhc2FfbW9ydGFsaWRhZCwgIm11ZXJ0ZXMgcG9yIGNhZGEgMSwwMDAgaGFiaXRhbnRlc1xuIikNCmBgYA0KDQpWaXN1YWxpemFjacOzbiBkZSBUYXNhIGRlIE1vcnRhbGlkYWQgVXNhbW9zIHVuIGdyw6FmaWNvIGRlIGJhcnJhcyBwYXJhIHZpc3VhbGl6YXIgbGFzIHRhc2FzIGRlIG1vcnRhbGlkYWQgZW4gZGlmZXJlbnRlcyByZWdpb25lczoNCg0KYGBge3J9DQpsaWJyYXJ5KGdncGxvdDIpDQoNCnN0cihkYXRvcykNCmBgYA0KDQpgYGB7cn0NCnByaW50KGRhdG9zKQ0KYGBgDQoNCmBgYHtyfQ0KbGlicmFyeShnZ3Bsb3QyKQ0KYGBgDQoNCmBgYHtyfQ0KIyBDYWxjdWxhciB0YXNhcyBkZSBtb3J0YWxpZGFkDQoNCmRhdG9zJHRhc2FfbW9ydGFsaWRhZCA8LSAoZGF0b3MkbXVlcnRlcyAvIGRhdG9zJHBvYmxhY2lvbikgKiAxMDAwDQoNCnByaW50KGRhdG9zKQ0KYGBgDQoNCkdSQUZJQ09TOg0KDQpgYGB7cn0NCiMgR3LDoWZpY28NCmdncGxvdChkYXRvcywgYWVzKHggPSByZWdpb24sIHkgPSB0YXNhX21vcnRhbGlkYWQsIGZpbGwgPSByZWdpb24pKSArDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArDQogIGxhYnModGl0bGUgPSAiVGFzYSBkZSBNb3J0YWxpZGFkIHBvciBSZWdpw7NuIiwgeCA9ICJSZWdpw7NuIiwgeSA9ICJUYXNhIGRlIE1vcnRhbGlkYWQgKHBvciAxLDAwMCBoYWJpdGFudGVzKSIpICsNCiAgdGhlbWVfY2xhc3NpYygpDQpgYGANCg0KQ29uY2x1c2nDs24gTGEgVGFzYSBkZSBNb3J0YWxpZGFkIGVzIHVuIGluZGljYWRvciBmdW5kYW1lbnRhbCBwYXJhIG1lZGlyIGVsIGltcGFjdG8gZGUgY29uZGljaW9uZXMgZGUgc2FsdWQgeSBmYWN0b3JlcyBzb2Npb2Vjb27Ds21pY29zIGVuIHVuYSBwb2JsYWNpw7NuLg0KDQpDb21wYXJhciB0YXNhcyBlbnRyZSByZWdpb25lcyBwdWVkZSBheXVkYXIgYSBpZGVudGlmaWNhciDDoXJlYXMgcXVlIG5lY2VzaXRhbiBtYXlvciBhdGVuY2nDs24uDQoNClRhc2EgZGUgTGV0YWxpZGFkIMK/UXXDqSBlcyBsYSBUYXNhIGRlIExldGFsaWRhZD8gTGEgVGFzYSBkZSBMZXRhbGlkYWQgbWlkZSBlbCBwb3JjZW50YWplIGRlIHBlcnNvbmFzIGZhbGxlY2lkYXMgZW50cmUgbGFzIHF1ZSBoYW4gcGFkZWNpZG8gdW5hIGVuZmVybWVkYWQgZXNwZWPDrWZpY2EuDQoNCkVzIHVuYSBtw6l0cmljYSBjcnVjaWFsIHBhcmEgZXZhbHVhciBsYSBncmF2ZWRhZCBkZSB1bmEgZW5mZXJtZWRhZCB5IGxhIGVmZWN0aXZpZGFkIGRlIGxhcyBpbnRlcnZlbmNpb25lcyBtw6lkaWNhcy4NCg0KRsOzcm11bGEgTGEgZsOzcm11bGEgZ2VuZXJhbCBwYXJhIGNhbGN1bGFyIGxhIFRhc2EgZGUgTGV0YWxpZGFkIGVzOg0KDQpUYXNhIGRlIGxldGFsaWRhZCA9IG51bWVybyBkZSBmYWxsZWNpZG9zIC8gbnVtZXJvIGRlIGNhc29zIFwqIDEwMCA9DQoNCkRvbmRlOg0KDQotICAgTsO6bWVybyBkZSBGYWxsZWNpZG9zOiBUb3RhbCBkZSBwZXJzb25hcyBxdWUgaGFuIG11ZXJ0byBkZWJpZG8gYSBsYSBlbmZlcm1lZGFkLiAtDQoNCi1Ow7ptZXJvIGRlIENhc29zOiBUb3RhbCBkZSBwZXJzb25hcyBkaWFnbm9zdGljYWRhcyBjb24gbGEgZW5mZXJtZWRhZC4NCg0KSW1wbGVtZW50YWNpw7NuIGVuIFIgQ2FsY3VsZW1vcyBsYSBUYXNhIGRlIExldGFsaWRhZCB1c2FuZG8gdW4gZWplbXBsbyBwcsOhY3RpY286DQoNCmBgYHtyfQ0KRGF0b3NfZmFsbGVjaWRvcyA8LSAyMDAgIyBOw7ptZXJvIGRlIGZhbGxlY2lkb3MgcG9yIGxhIGVuZmVybWVkYWQgDQpjYXNvcyA8LSAxNTAwICMgTsO6bWVybyB0b3RhbCBkZSBjYXNvcyByZWdpc3RyYWRvcw0KDQojIEPDoWxjdWxvIGRlIGxhIHRhc2EgZGUgbGV0YWxpZGFkDQoNCnRhc2FfbGV0YWxpZGFkIDwtIChEYXRvc19mYWxsZWNpZG9zIC8gY2Fzb3MpICogMTAwDQoNCiMgUmVzdWx0YWRvDQoNCmNhdCgiVGFzYSBkZSBsZXRhbGlkYWQ6IiwgdGFzYV9sZXRhbGlkYWQsICIlIGRlIGxvcyBjYXNvcyBmYWxsZWNlblxuIikNCmBgYA0KDQpgYGB7cn0NCiMgQ2FsY3VsYXIgdGFzYXMgZGUgbGV0YWxpZGFkDQpkYXRvcyR0YXNhX2xldGFsaWRhZCA8LSAoZGF0b3MkZmFsbGVjaWRvcyAvIGRhdG9zJGNhc29zKSAqIDEwMCAgICAgICAgICAgICAgICANCmBgYA0KDQpgYGB7cn0NCiMgR3LDoWZpY28NCmdncGxvdChkYXRvcywgYWVzKHggPSByZWdpb24sIHkgPSB0YXNhX2xldGFsaWRhZCwgZmlsbCA9IHJlZ2lvbikpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsNCiAgbGFicyh0aXRsZSA9ICJUYXNhIGRlIExldGFsaWRhZCBwb3IgUmVnacOzbiIsDQogICAgICAgeCA9ICJSZWdpw7NuIiwgeSA9ICJUYXNhIGRlIExldGFsaWRhZCAoJSkiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KYGBgDQoNCkNvbmNsdXNpw7NuIExhIFRhc2EgZGUgTGV0YWxpZGFkIGVzIHVuIGluZGljYWRvciBjbGF2ZSBwYXJhIGV2YWx1YXIgbGEgc2V2ZXJpZGFkIGRlIHVuYSBlbmZlcm1lZGFkIHkgZWwgaW1wYWN0byBkZSBsYXMgaW50ZXJ2ZW5jaW9uZXMgbcOpZGljYXMuIFVuIGFuw6FsaXNpcyBkZXRhbGxhZG8gcHVlZGUgYXl1ZGFyIGEgaWRlbnRpZmljYXIgw6FyZWFzIHByaW9yaXRhcmlhcyBwYXJhIGxhIGFzaWduYWNpw7NuIGRlIHJlY3Vyc29zLg0KDQpUYXNhIGRlIE5hdGFsaWRhZCDCv1F1w6kgZXMgbGEgVGFzYSBkZSBOYXRhbGlkYWQ/IExhIFRhc2EgZGUgTmF0YWxpZGFkIG1pZGUgZWwgbsO6bWVybyBkZSBuYWNpbWllbnRvcyBlbiB1bmEgcG9ibGFjacOzbiBwb3IgY2FkYSAxLDAwMCBoYWJpdGFudGVzIGR1cmFudGUgdW4gcGVyw61vZG8gZGV0ZXJtaW5hZG8uDQoNCkVzIHVuYSBtw6l0cmljYSBjbGF2ZSBwYXJhIGFuYWxpemFyIGxhIGRpbsOhbWljYSBwb2JsYWNpb25hbCB5IGV2YWx1YXIgdGVuZGVuY2lhcyBkZW1vZ3LDoWZpY2FzLg0KDQpGw7NybXVsYSBMYSBmw7NybXVsYSBnZW5lcmFsIHBhcmEgY2FsY3VsYXIgbGEgVGFzYSBkZSBOYXRhbGlkYWQgZXM6DQoNClRhc2EgZGUgbmF0YWxpZGFkID0gbnVtZXJvIGRlIG5hY2ltaWVudG9zIC8gdG90YWwgZGUgbGEgcG9ibGFjacOzbiBcKiAxMDAwID0NCg0KSW1wbGVtZW50YWNpw7NuIGVuIFIgQ2FsY3VsZW1vcyBsYSBUYXNhIGRlIE5hdGFsaWRhZCB1c2FuZG8gdW4gZWplbXBsbyBwcsOhY3RpY286DQoNCmBgYHtyfQ0KIyBEYXRvcw0KDQpuYWNpbWllbnRvcyA8LSAxNTAwICAjIE7Dum1lcm8gZGUgbmFjaW1pZW50b3MgZW4gdW4gYcOxbw0KcG9ibGFjaW9uX3RvdGFsIDwtIDEwMDAwMCAgIyBQb2JsYWNpw7NuIHRvdGFsIGVuIGVzZSBhw7FvDQoNCiMgQ8OhbGN1bG8gZGUgbGEgdGFzYSBkZSBuYXRhbGlkYWQNCnRhc2FfbmF0YWxpZGFkIDwtIChuYWNpbWllbnRvcyAvIHBvYmxhY2lvbl90b3RhbCkgKiAxMDAwDQoNCiMgUmVzdWx0YWRvDQpjYXQoIlRhc2EgZGUgbmF0YWxpZGFkOiIsIHRhc2FfbmF0YWxpZGFkLCAibmFjaW1pZW50b3MgcG9yIGNhZGEgMSwwMDAgaGFiaXRhbnRlc1xuIikNCmBgYA0KDQpgYGB7cn0NCmRhdG9zDQpgYGANCg0KYGBge3J9DQojIENhbGN1bGFyIHRhc2FzIGRlIG5hdGFsaWRhZA0KZGF0b3MkdGFzYV9uYXRhbGlkYWQgPC0gKGRhdG9zJG5hY2ltaWVudG9zIC8gZGF0b3MkcG9ibGFjaW9uKSAqIDEwMDANCg0KIyBHcsOhZmljbw0KZ2dwbG90KGRhdG9zLCBhZXMoeCA9IHJlZ2lvbiwgeSA9IHRhc2FfbmF0YWxpZGFkLCBmaWxsID0gcmVnaW9uKSkgKw0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKw0KICBsYWJzKHRpdGxlID0gIlRhc2EgZGUgTmF0YWxpZGFkIHBvciBSZWdpw7NuIiwNCiAgICAgICB4ID0gIlJlZ2nDs24iLCB5ID0gIlRhc2EgZGUgTmF0YWxpZGFkIChwb3IgMSwwMDAgaGFiaXRhbnRlcykiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KYGBgDQoNCkNvbmNsdXNpw7NuIExhIFRhc2EgZGUgTmF0YWxpZGFkIGVzIHVuYSBoZXJyYW1pZW50YSBlc2VuY2lhbCBlbiBlcGlkZW1pb2xvZ8OtYSBwYXJhIGVudGVuZGVyIGxhIGRpbsOhbWljYSBwb2JsYWNpb25hbC4gVXNhcmxhIGVuIGNvbmp1bnRvIGNvbiBvdHJhcyBtw6l0cmljYXMgcHVlZGUgcHJvcG9yY2lvbmFyIHVuYSB2aXNpw7NuIGNvbXBsZXRhIGRlIGxhcyB0ZW5kZW5jaWFzIGRlbW9ncsOhZmljYXMgeSBuZWNlc2lkYWRlcyBkZSBzYWx1ZCBww7pibGljYS4NCg0KVmVudGFqYXMgZGUgZmxleHRhYmxlOiBJbnRlcmFjdGl2aWRhZDogUGVybWl0ZSBjcmVhciB0YWJsYXMgZsOhY2lsbWVudGUgZXhwb3J0YWJsZXMgYSBXb3JkIG8gUG93ZXJQb2ludC4NCg0KRXN0aWxvIFBlcnNvbmFsaXphYmxlOiBDYW1iaWEgY29sb3JlcywgZnVlbnRlcyB5IGJvcmRlcyBzaW4gY29tcGxpY2FjaW9uZXMuDQoNClNvcG9ydGUgcGFyYSBSZXBvcnRlczogSWRlYWwgcGFyYSBpbmZvcm1lcyBlbiBmb3JtYXRvcyByZXByb2R1Y2libGVzLg0KDQpBbWJhcyBvcGNpb25lcyAoa2FibGVFeHRyYSB5IGZsZXh0YWJsZSkgc29uIG3DoXMgc2ltcGxlcyB5IHZlcnPDoXRpbGVzIHF1ZSBndGFibGUgcGFyYSBlc3RlIGNhc28gZGUgdXNvLg0KDQpgYGB7cn0NCg0KI2luc3RhbGwucGFja2FnZXMoInRpZHl2ZXJzZSIpDQoNCmxpYnJhcnkodGlkeXZlcnNlKQ0KYGBgDQoNCmBgYHtyfQ0KaW5zdGFsbC5wYWNrYWdlcygiZmxleHRhYmxlIikNCmxpYnJhcnkoZmxleHRhYmxlKQ0KYGBgDQoNCmBgYHtyfQ0KIyBDcmVhciBlbCBjb25qdW50byBkZSBkYXRvcw0KZGF0b3MgPC0gZGF0YS5mcmFtZSgNCiAgcmVnaW9uID0gYygiTm9ydGUiLCAiU3VyIiwgIkVzdGUiLCAiT2VzdGUiKSwNCiAgcG9ibGFjaW9uID0gYygxMDAwMDAsIDc1MDAwLCA1MDAwMCwgNjAwMDApLA0KICBuYWNpbWllbnRvcyA9IGMoMTIwMCwgODAwLCA2MDAsIDcwMCksDQogIG11ZXJ0ZXMgPSBjKDkwMCwgNTAwLCAzMDAsIDQwMCksDQogIGNhc29zX2VuZmVybWVkYWQgPSBjKDE1MDAsIDEwMDAsIDgwMCwgOTAwKSwNCiAgZmFsbGVjaWRvc19lbmZlcm1lZGFkID0gYygyMDAsIDE1MCwgMTAwLCAxMjApDQopDQoNCiMgQ2FsY3VsYXIgbGFzIHRhc2FzDQpkYXRvcyR0YXNhX25hdGFsaWRhZCA8LSAoZGF0b3MkbmFjaW1pZW50b3MgLyBkYXRvcyRwb2JsYWNpb24pICogMTAwMA0KZGF0b3MkdGFzYV9tb3J0YWxpZGFkIDwtIChkYXRvcyRtdWVydGVzIC8gZGF0b3MkcG9ibGFjaW9uKSAqIDEwMDANCmRhdG9zJHRhc2FfaW5jaWRlbmNpYSA8LSAoZGF0b3MkY2Fzb3NfZW5mZXJtZWRhZCAvIGRhdG9zJHBvYmxhY2lvbikgKiAxMDAwDQpkYXRvcyR0YXNhX2xldGFsaWRhZCA8LSAoZGF0b3MkZmFsbGVjaWRvc19lbmZlcm1lZGFkIC8gZGF0b3MkY2Fzb3NfZW5mZXJtZWRhZCkgKiAxMDANCg0KIyBSZWRvbmRlYXIgc29sbyBjb2x1bW5hcyBudW3DqXJpY2FzDQpkYXRvc19yZWRvbmRlYWRvcyA8LSBkYXRvcyAlPiUNCiAgbXV0YXRlX2lmKGlzLm51bWVyaWMsIHJvdW5kLCAyKQ0KDQojIENyZWFyIGxhIHRhYmxhIGNvbiBmbGV4dGFibGUNCmZsZXh0YWJsZShkYXRvc19yZWRvbmRlYWRvcykgJT4lDQogIGF1dG9maXQoKQ0KYGBgDQoNCkPDoWxjdWxvIGRlIEZyZWN1ZW5jaWFzDQoNCkVsIGFuw6FsaXNpcyBkZSBmcmVjdWVuY2lhcyBlcyB1bmEgdMOpY25pY2EgYsOhc2ljYSBwZXJvIGVzZW5jaWFsIGVuIGVwaWRlbWlvbG9nw61hLiBQZXJtaXRlIGRlc2NyaWJpciBjw7NtbyBzZSBkaXN0cmlidXllbiBsb3MgZGF0b3MgZW4gY2F0ZWdvcsOtYXMgZXNwZWPDrWZpY2FzIHkgZXMgZXNwZWNpYWxtZW50ZSDDunRpbCBwYXJhIGFuYWxpemFyIHZhcmlhYmxlcyBjYXRlZ8OzcmljYXMgY29tbyBzZXhvLCBncnVwbyBkZSBlZGFkLCBvIGVzdGFkbyBkZSBlbmZlcm1lZGFkLg0KDQpDYXJnYXIgTGlicmVyw61hcyBQcmltZXJvLCBjYXJnYW1vcyBsYXMgbGlicmVyw61hcyBuZWNlc2FyaWFzIHBhcmEgZWwgYW7DoWxpc2lzOg0KDQpgYGB7cn0NCmxpYnJhcnkoZHBseXIpICAgIA0KIyBQYXJhIG1hbmlwdWxhY2nDs24gZGUgZGF0b3MgJT4lICU+JSAlPiUgJT4lICU+JSAgY29udHJvbCArIHNoaWZ0ICsgbSA9IA0KIyAlPiUgJT4lICU+JSAlPiUgJT4lICU+JSAlPiUgDQpsaWJyYXJ5KGdncGxvdDIpICAgIyBQYXJhIHZpc3VhbGl6YWNpw7NuIGRlIGRhdG9zDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCg0KIyBjb250cm9sICsgYWx0ICsgKisgPSB+IA0KDQojaW5zdGFsbC5wYWNrYWdlcygiamFuaXRvciIpDQpsaWJyYXJ5KGphbml0b3IpICAgIyBQYXJhIGdlbmVyYXIgdGFibGFzIGRlIGZyZWN1ZW5jaWFzDQpgYGANCg0KQ3JlYXIgdW4gQ29uanVudG8gZGUgRGF0b3MgZGUgRWplbXBsbyBVc2FyZW1vcyB1biBkYXRhc2V0IGZpY3RpY2lvIHBhcmEgY2FsY3VsYXIgbGFzIGZyZWN1ZW5jaWFzOg0KDQpgYGB7cn0NCiMgQ3JlYXIgdW4gY29uanVudG8gZGUgZGF0b3MgZmljdGljaW8NCmRhdG9zIDwtIGRhdGEuZnJhbWUoDQogIHNleG8gPSBjKCJGIiwgIk0iLCAiRiIsICJGIiwgIk0iLCAiRiIsICJNIiwgIk0iLCAiRiIsICJNIiksDQogIGdydXBvX2VkYWQgPSBjKCIwLTE5IiwgIjIwLTM5IiwgIjIwLTM5IiwgIjQwLTU5IiwgIjQwLTU5IiwgIjYwKyIsICIyMC0zOSIsICI2MCsiLCAiNDAtNTkiLCAiMC0xOSIpDQopDQoNCiMgTW9zdHJhciBsb3MgcHJpbWVyb3MgZGF0b3MNCmhlYWQoZGF0b3MpDQpgYGANCg0KYGBge3J9DQpzdHIoZGF0b3MpDQpgYGANCg0KVGFibGFzIGRlIEZyZWN1ZW5jaWFzIFRhYmxhIGRlIEZyZWN1ZW5jaWFzIGNvbiB0YWJsZSBQb2RlbW9zIGNhbGN1bGFyIGZyZWN1ZW5jaWFzIHNpbXBsZXMgdXNhbmRvIHRhYmxlOg0KDQpgYGB7cn0NCiMgQ2FsY3VsYXIgZnJlY3VlbmNpYXMgcGFyYSBsYSB2YXJpYWJsZSAic2V4byINCnRhYmxhX3NleG8gPC0gdGFibGUoZGF0b3Mkc2V4bykNCnRhYmxhX3NleG8NCmBgYA0KDQpUYWJsYSBkZSBGcmVjdWVuY2lhcyBDcnV6YWRhcyBjb24gdGFibGUgUGFyYSBhbmFsaXphciBkb3MgdmFyaWFibGVzOg0KDQpgYGB7cn0NCiMgVGFibGEgY3J1emFkYSBlbnRyZSAic2V4byIgeSAiZ3J1cG9fZWRhZCINCnRhYmxhX2NydXphZGEgPC0gdGFibGUoZGF0b3Mkc2V4bywgZGF0b3MkZ3J1cG9fZWRhZCkNCnRhYmxhX2NydXphZGENCmBgYA0KDQpUYWJsYXMgTGltcGlhcyBjb24gamFuaXRvciBFbCBwYXF1ZXRlIGphbml0b3IgZ2VuZXJhIHRhYmxhcyBkZSBmcmVjdWVuY2lhcyBvcmRlbmFkYXM6DQoNCmBgYHtyfQ0KI2luc3RhbGwucGFja2FnZXMoImphbml0b3IiKQ0KbGlicmFyeShqYW5pdG9yKQ0KIyBUYWJsYSBkZSBmcmVjdWVuY2lhcyBjb24gcHJvcG9yY2lvbmVzDQoNCnRhYmxhX29yZGVuYWRhIDwtIGRhdG9zICU+JQ0KICB0YWJ5bChzZXhvKSAlPiUNCiAgYWRvcm5fdG90YWxzKCJyb3ciKSAlPiUNCiAgYWRvcm5fcGN0X2Zvcm1hdHRpbmcoKQ0KDQp0YWJsYV9vcmRlbmFkYQ0KYGBgDQoNCmBgYHtyfQ0KZmxleHRhYmxlKHRhYmxhX29yZGVuYWRhKQ0KYGBgDQoNClBhcXVldGUgamFuaXRvciDCv1F1w6kgZXMgamFuaXRvcj8gamFuaXRvciBlcyB1biBwYXF1ZXRlIGRpc2XDsWFkbyBwYXJhIGxpbXBpYXIgZGF0b3MgeSByZWFsaXphciBhbsOhbGlzaXMgZXhwbG9yYXRvcmlvcyByw6FwaWRvcy4gRXMgcGFydGljdWxhcm1lbnRlIMO6dGlsIHBhcmEgbWFuZWphciB0YWJsYXMgeSBkYXRvcyBjYXRlZ8Ozcmljb3MsIGNvbW8gY2FsY3VsYXIgZnJlY3VlbmNpYXMgeSBwcm9wb3JjaW9uZXMuDQoNCkNhcmFjdGVyw61zdGljYXMgUHJpbmNpcGFsZXMgZGUgamFuaXRvcjogTGltcGllemEgZGUgTm9tYnJlcyBkZSBDb2x1bW5hczoNCg0KQ29udmllcnRlIG5vbWJyZXMgZGUgY29sdW1uYXMgZW4gZm9ybWF0byBsaW1waW8geSBjb25zaXN0ZW50ZSwgZWxpbWluYW5kbyBlc3BhY2lvcyBvIGNhcmFjdGVyZXMgZXNwZWNpYWxlcy4NCg0KYGBge3J9DQpsaWJyYXJ5KGphbml0b3IpDQpkYXRvczIgPC0gZGF0YS5mcmFtZSgiTm9tYnJlIENvbHVtbmEgMSIgPSBjKDEsIDIpLCAiT3RyYSBDb2x1bW5hIiA9IGMoMywgNCkpDQoNCmRhdG9zX2NsZWFuIDwtIGNsZWFuX25hbWVzKGRhdG9zMikgI2Z1bmNpw7NuIGNsZWFuX25hbWVzDQoNCnByaW50KGRhdG9zX2NsZWFuKSAgIyBub21icmVzOiBub21icmVfY29sdW1uYV8xLCBvdHJhX2NvbHVtbmENCmBgYA0KDQpUYWJsYXMgZGUgRnJlY3VlbmNpYXM6DQoNCkdlbmVyYSB0YWJsYXMgZGUgZnJlY3VlbmNpYXMgc2ltcGxlcyB5IGNydXphZGFzLg0KDQpJbmNsdXllIHRvdGFsZXMgeSBwb3JjZW50YWplcyBwYXJhIGZhY2lsaXRhciBsYSBpbnRlcnByZXRhY2nDs24uDQoNCmBgYHtyfQ0KbGlicmFyeShqYW5pdG9yKQ0KdGFibGEgPC0gZGF0b3MgJT4lIHRhYnlsKGdydXBvX2VkYWQpDQpgYGANCg0KYGBge3J9DQpwcmludCh0YWJsYSkNCmBgYA0KDQpgYGB7cn0NCnN0cihkYXRvcykNCmBgYA0KDQpUYWJsYXMgZGUgRnJlY3VlbmNpYXMgVGFibGEgZGUgRnJlY3VlbmNpYXMgY29uIHRhYmxlIFBvZGVtb3MgY2FsY3VsYXIgZnJlY3VlbmNpYXMgc2ltcGxlcyB1c2FuZG8gdGFibGU6DQoNCmBgYHtyfQ0KIyBDYWxjdWxhciBmcmVjdWVuY2lhcyBwYXJhIGxhIHZhcmlhYmxlICJzZXhvIg0KdGFibGFfc2V4byA8LSB0YWJsZShkYXRvcyRzZXhvKQ0KdGFibGFfc2V4bw0KYGBgDQoNClRhYmxhIGRlIEZyZWN1ZW5jaWFzIENydXphZGFzIGNvbiB0YWJsZSBQYXJhIGFuYWxpemFyIGRvcyB2YXJpYWJsZXM6DQoNCmBgYHtyfQ0KIyBUYWJsYSBjcnV6YWRhIGVudHJlICJzZXhvIiB5ICJncnVwb19lZGFkIg0KdGFibGFfY3J1emFkYSA8LSB0YWJsZShkYXRvcyRzZXhvLCBkYXRvcyRncnVwb19lZGFkKQ0KdGFibGFfY3J1emFkYQ0KYGBgDQoNClRhYmxhcyBMaW1waWFzIGNvbiBqYW5pdG9yIEVsIHBhcXVldGUgamFuaXRvciBnZW5lcmEgdGFibGFzIGRlIGZyZWN1ZW5jaWFzIG9yZGVuYWRhczoNCg0KYGBge3J9DQojaW5zdGFsbC5wYWNrYWdlcygiamFuaXRvciIpDQpsaWJyYXJ5KGphbml0b3IpDQojIFRhYmxhIGRlIGZyZWN1ZW5jaWFzIGNvbiBwcm9wb3JjaW9uZXMNCg0KdGFibGFfb3JkZW5hZGEgPC0gZGF0b3MgJT4lDQogIHRhYnlsKHNleG8pICU+JQ0KICBhZG9ybl90b3RhbHMoInJvdyIpICU+JQ0KICBhZG9ybl9wY3RfZm9ybWF0dGluZygpDQoNCnRhYmxhX29yZGVuYWRhDQpgYGANCg0KYGBge3J9DQpsaWJyYXJ5KGZsZXh0YWJsZSkNCmBgYA0KDQpgYGB7cn0NCmZsZXh0YWJsZSh0YWJsYV9vcmRlbmFkYSkNCg0KYGBgDQoNClBhcXVldGUgamFuaXRvciDCv1F1w6kgZXMgamFuaXRvcj8gamFuaXRvciBlcyB1biBwYXF1ZXRlIGRpc2XDsWFkbyBwYXJhIGxpbXBpYXIgZGF0b3MgeSByZWFsaXphciBhbsOhbGlzaXMgZXhwbG9yYXRvcmlvcyByw6FwaWRvcy4gRXMgcGFydGljdWxhcm1lbnRlIMO6dGlsIHBhcmEgbWFuZWphciB0YWJsYXMgeSBkYXRvcyBjYXRlZ8Ozcmljb3MsIGNvbW8gY2FsY3VsYXIgZnJlY3VlbmNpYXMgeSBwcm9wb3JjaW9uZXMuDQoNCkNhcmFjdGVyw61zdGljYXMgUHJpbmNpcGFsZXMgZGUgamFuaXRvcjogTGltcGllemEgZGUgTm9tYnJlcyBkZSBDb2x1bW5hczoNCg0KQ29udmllcnRlIG5vbWJyZXMgZGUgY29sdW1uYXMgZW4gZm9ybWF0byBsaW1waW8geSBjb25zaXN0ZW50ZSwgZWxpbWluYW5kbyBlc3BhY2lvcyBvIGNhcmFjdGVyZXMgZXNwZWNpYWxlcy4NCg0KYGBge3J9DQpsaWJyYXJ5KGphbml0b3IpDQpkYXRvczIgPC0gZGF0YS5mcmFtZSgiTm9tYnJlIENvbHVtbmEgMSIgPSBjKDEsIDIpLCAiT3RyYSBDb2x1bW5hIiA9IGMoMywgNCkpDQoNCmRhdG9zX2NsZWFuIDwtIGNsZWFuX25hbWVzKGRhdG9zMikgI2Z1bmNpw7NuIGNsZWFuX25hbWVzDQoNCnByaW50KGRhdG9zX2NsZWFuKSAgIyBub21icmVzOiBub21icmVfY29sdW1uYV8xLCBvdHJhX2NvbHVtbmENCmBgYA0KDQpUYWJsYXMgZGUgRnJlY3VlbmNpYXM6DQoNCkdlbmVyYSB0YWJsYXMgZGUgZnJlY3VlbmNpYXMgc2ltcGxlcyB5IGNydXphZGFzLg0KDQpJbmNsdXllIHRvdGFsZXMgeSBwb3JjZW50YWplcyBwYXJhIGZhY2lsaXRhciBsYSBpbnRlcnByZXRhY2nDs24uDQoNCmBgYHtyfQ0KbGlicmFyeShqYW5pdG9yKQ0KDQp0YWJsYSA8LSBkYXRvcyAlPiUgdGFieWwoZ3J1cG9fZWRhZCkNCg0KcHJpbnQodGFibGEpDQpgYGANCg0KVGFibGFzIE9yZGVuYWRhcyB5IFByb3BvcmNpb25lczoNCg0KQcOxYWRlIHRvdGFsZXMgeSBwb3JjZW50YWplcyBhIHRhYmxhcyBkZSBtYW5lcmEgYXV0b23DoXRpY2EuDQoNCmBgYHtyfQ0KZ3J1cG9lZGFkPC10YWJsYSAlPiUgYWRvcm5fdG90YWxzKCJyb3ciKSAlPiUgYWRvcm5fcGN0X2Zvcm1hdHRpbmcoKQ0KDQpncnVwb2VkYWQNCmBgYA0KDQpgYGB7cn0NCmZsZXh0YWJsZShncnVwb2VkYWQpDQpgYGANCg0KRGV0ZWNjacOzbiBkZSBEdXBsaWNhZG9zOg0KDQpJZGVudGlmaWNhIHZhbG9yZXMgZHVwbGljYWRvcyBlbiB0dXMgZGF0b3MuDQoNCmBgYHtyfQ0KZGF0b3MzIDwtIGRhdGEuZnJhbWUoaWQgPSBjKDEsIDEsIDIsIDMpLCB2YWxvciA9IGMoMTAsIDEwLCAyMCwgMzApKQ0KDQpkYXRvczMNCmBgYA0KDQpgYGB7cn0NCmdldF9kdXBlcyhkYXRvczMsIGlkKQ0KYGBgDQoNCsK/Q3XDoW5kbyB1c2FyIGphbml0b3I/IEN1YW5kbyBuZWNlc2l0YXMgY2FsY3VsYXIgZnJlY3VlbmNpYXMgcsOhcGlkYW1lbnRlLg0KDQpQYXJhIGxpbXBpYXIgbm9tYnJlcyBkZSBjb2x1bW5hcyBhdXRvbcOhdGljYW1lbnRlLg0KDQpBbCB0cmFiYWphciBjb24gZGF0b3MgY2F0ZWfDs3JpY29zIGRvbmRlIGxhcyBwcm9wb3JjaW9uZXMgeSB0b3RhbGVzIHNvbiBpbXBvcnRhbnRlcy4NCg0KVmlzdWFsaXphY2nDs24gZGUgRnJlY3VlbmNpYXMgR3LDoWZpY28gZGUgQmFycmFzIGNvbiBnZ3Bsb3QyIFVuIGdyw6FmaWNvIGRlIGJhcnJhcyBlcyBpZGVhbCBwYXJhIHZpc3VhbGl6YXIgZnJlY3VlbmNpYXMgY2F0ZWfDs3JpY2FzOg0KDQpgYGB7cn0NCiMgQ3JlYXIgdW4gZ3LDoWZpY28gZGUgYmFycmFzIHBhcmEgImdydXBvX2VkYWQiDQpnZ3Bsb3QoZGF0b3MsIGFlcyh4ID0gZ3J1cG9fZWRhZCwgZmlsbCA9IGdydXBvX2VkYWQpKSArDQogIGdlb21fYmFyKCkgKw0KICBsYWJzKHRpdGxlID0gIkZyZWN1ZW5jaWEgcG9yIEdydXBvIGRlIEVkYWQiLA0KICAgICAgIHggPSAiR3J1cG8gZGUgRWRhZCIsDQogICAgICAgeSA9ICJGcmVjdWVuY2lhIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KDQpgYGB7cn0NCmRhdG9zICU+JSBnZ3Bsb3QoYWVzKGdydXBvX2VkYWQsIGZpbGw9IGdydXBvX2VkYWQpKSsNCiAgZ2VvbV9iYXIoKQ0KYGBgDQoNCkdyw6FmaWNvIGRlIFBhc3RlbCBjb24gZ2dwbG90MiBVbiBncsOhZmljbyBkZSBwYXN0ZWwgZXMgw7p0aWwgcGFyYSBtb3N0cmFyIHByb3BvcmNpb25lczoNCg0KYGBge3J9DQojIENhbGN1bGFyIHByb3BvcmNpb25lcw0KcHJvcG9yY2lvbmVzIDwtIGRhdG9zICU+JQ0KICBncm91cF9ieShzZXhvKSAlPiUNCiAgc3VtbWFyaXNlKGZyZWN1ZW5jaWEgPSBuKCkpICU+JQ0KICBtdXRhdGUocHJvcG9yY2lvbiA9IGZyZWN1ZW5jaWEgLyBzdW0oZnJlY3VlbmNpYSkpDQoNCiMgQ3JlYXIgdW4gZ3LDoWZpY28gZGUgcGFzdGVsDQpnZ3Bsb3QocHJvcG9yY2lvbmVzLCBhZXMoeCA9ICIiLCB5ID0gcHJvcG9yY2lvbiwgZmlsbCA9IHNleG8pKSArDQogIGdlb21fY29sKHdpZHRoID0gMSkgKw0KICBjb29yZF9wb2xhcih0aGV0YSA9ICJ5IikgKw0KICAgdGhlbWVfdm9pZCgpKw0KICBsYWJzKHRpdGxlID0gIkRpc3RyaWJ1Y2nDs24gcG9yIFNleG8iKSANCmBgYA0KDQoNCmBgYHtyfQ0KZmxleHRhYmxlKHRhYmxhX29yZGVuYWRhKQ0KYGBgDQoNClRhYmxhcyB5IEdyw6FmaWNvcyBlbiB1biBSZXBvcnRlDQpDb21iaW5hciBSZXN1bHRhZG9zIGVuIHVuYSBUYWJsYSBSZXN1bWVuDQpQb2RlbW9zIGNvbWJpbmFyIGZyZWN1ZW5jaWFzIHkgcHJvcG9yY2lvbmVzIGVuIHVuYSB0YWJsYSBjbGFyYToNCg0KDQpgYGB7cn0NCiMgVGFibGEgcmVzdW1lbiBkZSBmcmVjdWVuY2lhcyB5IHByb3BvcmNpb25lcw0KdGFibGFfcmVzdW1lbiA8LSBkYXRvcyAlPiUNCiAgZ3JvdXBfYnkoc2V4bykgJT4lDQogIHN1bW1hcmlzZSgNCiAgICBmcmVjdWVuY2lhID0gbigpLA0KICAgIHByb3BvcmNpb24gPSBuKCkgLyBucm93KGRhdG9zKQ0KICApDQoNCnRhYmxhX3Jlc3VtZW4NCmBgYA0KDQpFeHBvcnRhciBHcsOhZmljb3MgeSBUYWJsYXMNCkd1YXJkYXIgZ3LDoWZpY29zIHkgdGFibGFzIHBhcmEgaW5jbHVpcmxvcyBlbiByZXBvcnRlczoNCg0KYGBge3J9DQojIEd1YXJkYXIgZWwgZ3LDoWZpY28gZGUgYmFycmFzIGNvbW8gaW1hZ2VuDQpnZ3NhdmUoImZyZWN1ZW5jaWFfZ3J1cG9fZWRhZC5wbmciKQ0KYGBgDQoNCmBgYHtyfQ0KIyBFeHBvcnRhciBsYSB0YWJsYSByZXN1bWVuIGNvbW8gQ1NWDQp3cml0ZS5jc3YodGFibGFfcmVzdW1lbiwgInRhYmxhX3Jlc3VtZW4uY3N2IikNCmBgYA0KDQoNCk1lZGlkYXMgZGUgZnJlY3VlbmNpYQ0KTGFzIG1lZGlkYXMgZGUgZnJlY3VlbmNpYSBzb24gaGVycmFtaWVudGFzIGNsYXZlIGVuIGVwaWRlbWlvbG9nw61hIHBhcmEgY3VhbnRpZmljYXIgZWwgaW1wYWN0byBkZSBlbmZlcm1lZGFkZXMgZW4gdW5hIHBvYmxhY2nDs24uIExhcyBwcmluY2lwYWxlcyBtw6l0cmljYXMgc29uOg0KDQpQcmV2YWxlbmNpYTogUHJvcG9yY2nDs24gZGUgaW5kaXZpZHVvcyBlbmZlcm1vcyBlbiB1biBtb21lbnRvIGNvbmNyZXRvLg0KSW5jaWRlbmNpYSBhY3VtdWxhZGE6IFByb2JhYmlsaWRhZCBkZSBxdWUgdW4gaW5kaXZpZHVvIGVuZmVybWUgZHVyYW50ZSB1biBwZXLDrW9kby4NCkRlbnNpZGFkIGRlIGluY2lkZW5jaWE6IFZlbG9jaWRhZCBjb24gbGEgcXVlIGFwYXJlY2VuIG51ZXZvcyBjYXNvcyBlbiB1bmEgcG9ibGFjacOzbi4NCkRhdGFzZXQgU2ltdWxhZG8NCkNyZWFyIHVuIERhdGFzZXQgRmljdGljaW8NClVzYW1vcyB1biBjb25qdW50byBkZSBkYXRvcyBzaW11bGFkbyBwYXJhIGNhbGN1bGFyIGVzdGFzIG3DqXRyaWNhcy4NCg0KYGBge3J9DQojIENyZWFyIHVuIGNvbmp1bnRvIGRlIGRhdG9zIGZpY3RpY2lvDQpkYXRvcyA8LSBkYXRhLmZyYW1lKA0KICBpZCA9IDE6MTAwLCAgIyBJZGVudGlmaWNhY2nDs24gZGUgaW5kaXZpZHVvcw0KICBpbmljaW9fcmllc2dvID0gYyhyZXAoMCwgODApLCByZXAoMSwgMjApKSwgICMgMCA9IG5vIGVuZmVybW8gYWwgaW5pY2lvIDE9IGVuZmVybW8gDQogIGNhc29zX251ZXZvcyA9IGMocmVwKDAsIDkwKSwgcmVwKDEsIDEwKSksICAjIENhc29zIG51ZXZvcyBhbCBmaW5hbCBkZWwgcGVyw61vZG8NCiAgdGllbXBvX29ic2VydmFjaW9uID0gYyhyZXAoMSwgNTApLCByZXAoMiwgMzApLCByZXAoMywgMjApKSAgIyBUaWVtcG8gb2JzZXJ2YWRvIGVuIGHDsW9zDQogICkNCg0KIyBNb3N0cmFyIGxvcyBwcmltZXJvcyBkYXRvcw0KaGVhZChkYXRvcykNCmBgYA0KDQpDw6FsY3VsbyBkZSBQcmV2YWxlbmNpYQ0Kwr9RdcOpIGVzIGxhIFByZXZhbGVuY2lhPw0KTGEgcHJldmFsZW5jaWEgbWlkZSBsYSBwcm9wb3JjacOzbiBkZSBwZXJzb25hcyBhZmVjdGFkYXMgcG9yIHVuYSBlbmZlcm1lZGFkIGVuIHVuIG1vbWVudG8gZGFkby4NCg0KRsOzcm11bGE6DQpQcmV2YWxlbmNpYSA9IGNhc29zIGV4aXN0ZW50ZXMgLyBwb2JsYWNpw7NuIHRvdGFsDQoNCmBgYHtyfQ0KIyBDYWxjdWxhciBwcmV2YWxlbmNpYSBhbCBpbmljaW8gZGVsIHBlcsOtb2RvDQpwb2JsYWNpb25fdG90YWwgPC0gbnJvdyhkYXRvcykNCnBvYmxhY2lvbl90b3RhbCA8LSAxMDANCg0KY2Fzb3NfZXhpc3RlbnRlcyA8LSAyMA0KDQpjYXNvc19leGlzdGVudGVzIDwtIHN1bShkYXRvcyRpbmljaW9fcmllc2dvID09IDEpDQoNCmNhc29zX2V4aXN0ZW50ZXMgPC0gc3VtKGRhdG9zJGluaWNpb19yaWVzZ28gIT0gMCkNCg0KcHJldmFsZW5jaWEgPC0gY2Fzb3NfZXhpc3RlbnRlcyAvIHBvYmxhY2lvbl90b3RhbA0KDQoNCmNhdCgiUHJldmFsZW5jaWE6Iiwgcm91bmQocHJldmFsZW5jaWEgKiAxMDAsIDIpLCAiJVxuIikNCmBgYA0KYGBge3J9DQpjYXQoIlByZXZhbGVuY2lhOiIsIHByZXZhbGVuY2lhKjEwMCwgIiVcbiIpDQpgYGANCkludGVycHJldGFjacOzbjogRXN0ZSB2YWxvciBpbmRpY2EgZWwgcG9yY2VudGFqZSBkZSBsYSBwb2JsYWNpw7NuIGFmZWN0YWRhIGFsIGluaWNpbyBkZWwgZXN0dWRpby4NCg0KQ8OhbGN1bG8gZGUgSW5jaWRlbmNpYSBBY3VtdWxhZGENCsK/UXXDqSBlcyBsYSBJbmNpZGVuY2lhIEFjdW11bGFkYT8NCkxhIGluY2lkZW5jaWEgYWN1bXVsYWRhIG1pZGUgbGEgcHJvcG9yY2nDs24gZGUgbnVldm9zIGNhc29zIGVuIHVuYSBwb2JsYWNpw7NuIGVuIHJpZXNnbyBkdXJhbnRlIHVuIHBlcsOtb2RvLg0KDQpGw7NybXVsYToNCg0KSW5jaWRlbmNpYSBhY3VtdWxhZGEgPSBjYXNvcyBudWV2b3Mgb2JzZXJ2YWRvcyAvIHBvYmxhY2nDs24gZW4gcmllc2dvIGFsIGluaWNpbw0KDQpgYGB7cn0NCiMgQ2FsY3VsYXIgaW5jaWRlbmNpYSBhY3VtdWxhZGENCmNhc29zX251ZXZvcyA8LSBzdW0oZGF0b3MkY2Fzb3NfbnVldm9zKQ0KDQpjYXNvc19udWV2b3MgPC0gMTANCg0KcG9ibGFjaW9uX3JpZXNnbyA8LSBzdW0oZGF0b3MkaW5pY2lvX3JpZXNnbyA9PSAwKQ0KDQpwb2JsYWNpb25fcmllc2dvIDwtIHN1bShkYXRvcyRpbmljaW9fcmllc2dvICE9IDEpDQoNCnBvYmxhY2lvbl9yaWVzZ28gPC0gODANCg0KaW5jaWRlbmNpYV9hY3VtdWxhZGEgPC0gY2Fzb3NfbnVldm9zIC8gcG9ibGFjaW9uX3JpZXNnbw0KDQpjYXQoIkluY2lkZW5jaWEgQWN1bXVsYWRhOiIsIHJvdW5kKGluY2lkZW5jaWFfYWN1bXVsYWRhICogMTAwLCAyKSwgIiVcbiIpDQpgYGANCg0KSW50ZXJwcmV0YWNpw7NuOiBFc3RlIHZhbG9yIGluZGljYSBlbCByaWVzZ28gZGUgZGVzYXJyb2xsYXIgbGEgZW5mZXJtZWRhZCBkdXJhbnRlIGVsIHBlcsOtb2RvIGRlIG9ic2VydmFjacOzbi4NCg0KQ8OhbGN1bG8gZGUgRGVuc2lkYWQgZGUgSW5jaWRlbmNpYQ0Kwr9RdcOpIGVzIGxhIERlbnNpZGFkIGRlIEluY2lkZW5jaWE/DQpMYSBkZW5zaWRhZCBkZSBpbmNpZGVuY2lhIG1pZGUgbGEgdmVsb2NpZGFkIGNvbiBsYSBxdWUgYXBhcmVjZW4gbnVldm9zIGNhc29zIGVuIHVuYSBwb2JsYWNpw7NuIGR1cmFudGUgcGVyw61vZG9zIGRlIHRpZW1wbyBhY3VtdWxhZG9zLg0KDQpGw7NybXVsYToNCmRlbnNpZGFkIGRlIGluY2lkZW5jaWEgPSBjYXNvcyBudWV2b3Mgb2JzZXJ2YWRvcyAvIMOxc3VtYSBkZSBsb3MgcGVyw61vZGVzIGRlIHJpZXNnbw0KDQpgYGB7cn0NCiMgQ2FsY3VsYXIgZGVuc2lkYWQgZGUgaW5jaWRlbmNpYQ0KdGllbXBvX3RvdGFsIDwtIHN1bShkYXRvcyR0aWVtcG9fb2JzZXJ2YWNpb24pDQoNCmRlbnNpZGFkX2luY2lkZW5jaWEgPC0gY2Fzb3NfbnVldm9zIC8gdGllbXBvX3RvdGFsDQoNCmNhdCgiRGVuc2lkYWQgZGUgSW5jaWRlbmNpYToiLCByb3VuZChkZW5zaWRhZF9pbmNpZGVuY2lhLCAyKSwgImNhc29zIHBvciBwZXJzb25hLWHDsW9cbiIpDQpgYGANCg0KSW50ZXJwcmV0YWNpw7NuOiBFc3RlIHZhbG9yIHJlcHJlc2VudGEgY3XDoW50b3MgY2Fzb3MgbnVldm9zIHNlIGdlbmVyYW4gcG9yIHBlcnNvbmEtYcOxby4NCg0KVmlzdWFsaXphY2nDs24gZGUgbGFzIE3DqXRyaWNhcw0KR3LDoWZpY28gQ29tcGFyYXRpdm8NCg0KYGBge3J9DQpsaWJyYXJ5KGdncGxvdDIpDQoNCiMgQ3JlYXIgZGF0b3MgcGFyYSBlbCBncsOhZmljbw0KcmVzdWx0YWRvcyA8LSBkYXRhLmZyYW1lKA0KICBtZWRpZGEgPSBjKCJQcmV2YWxlbmNpYSIsICJJbmNpZGVuY2lhIEFjdW11bGFkYSIsICJEZW5zaWRhZCBkZSBJbmNpZGVuY2lhIiksDQogIHZhbG9yID0gYyhwcmV2YWxlbmNpYSAqIDEwMCwgaW5jaWRlbmNpYV9hY3VtdWxhZGEgKiAxMDAsIGRlbnNpZGFkX2luY2lkZW5jaWEpDQopDQoNCiMgR3LDoWZpY28gZGUgYmFycmFzDQpnZ3Bsb3QocmVzdWx0YWRvcywgYWVzKHggPSBtZWRpZGEsIHkgPSB2YWxvciwgZmlsbCA9IG1lZGlkYSkpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsNCiAgbGFicyh0aXRsZSA9ICJNZWRpZGFzIGRlIEZyZWN1ZW5jaWEgRXBpZGVtaW9sw7NnaWNhIiwNCiAgICAgICB4ID0gIk1lZGlkYSIsIHkgPSAiVmFsb3IgKCUpIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KDQoNCkhlcnJhbWllbnRhcyBhZGljaW9uYWxlcw0KTsO6bWVybyBiw6FzaWNvIGRlIFJlcHJvZHVjY2nDs24gUjANCkFhbGd1bmFzIGhlcnJhbWllbnRhcyBhZGljaW9uYWxlcyB5IG1vZGVybmFzIHF1ZSBzZSB1c2FuIHBhcmEgYW5hbGl6YXIgZWwgaW1wYWN0byBkZSBlbmZlcm1lZGFkZXMgZW4gdW5hIHBvYmxhY2nDs24gaW5jbHV5ZW46DQoNCkVsIG7Dum1lcm8gYsOhc2ljbyBkZSByZXByb2R1Y2Npw7NuIFLigoAgZXMgdW5hIG3DqXRyaWNhIGNsYXZlIGVuIGVwaWRlbWlvbG9nw61hIHBhcmEgZXN0aW1hciBjdcOhbnRvcyBjYXNvcyBzZWN1bmRhcmlvcyBnZW5lcmEgdW4gY2FzbyBpbmljaWFsIGVuIHVuYSBwb2JsYWNpw7NuIGNvbXBsZXRhbWVudGUgc3VzY2VwdGlibGUuIEVzIGVzZW5jaWFsIHBhcmEgZXZhbHVhciBlbCBwb3RlbmNpYWwgZGUgcHJvcGFnYWNpw7NuIGRlIGVuZmVybWVkYWRlcyBpbmZlY2Npb3NhcyBjb21vIGVsIENPVklELTE5Lg0KDQpFbCBuw7ptZXJvIGLDoXNpY28gZGUgcmVwcm9kdWNjacOzbiBS4oKAIGluZGljYSBlbCBwcm9tZWRpbyBkZSBjYXNvcyBzZWN1bmRhcmlvcyBnZW5lcmFkb3MgcG9yIHVuIGNhc28gaW5pY2lhbCBlbiB1bmEgcG9ibGFjacOzbiBjb21wbGV0YW1lbnRlIHN1c2NlcHRpYmxlLg0KDQpDw7NtbyBjYWxjdWxhciBS4oKADQpFbCBjw6FsY3VsbyBkZSBS4oKAIHB1ZWRlIHJlYWxpemFyc2UgdXNhbmRvIHZhcmlhcyBtZXRvZG9sb2fDrWFzLiBVbmEgZGUgbGFzIG3DoXMgY29tdW5lcyBzZSBiYXNhIGVuIGxhIHJlbGFjacOzbjoNCg0KUjAgPSDOsiB4IEQNCg0KRG9uZGU6DQoNCs6yICh0YXNhIGRlIHRyYW5zbWlzacOzbik6IFByb2JhYmlsaWRhZCBkZSBxdWUgdW5hIHBlcnNvbmEgaW5mZWN0YWRhIHRyYW5zbWl0YSBsYSBlbmZlcm1lZGFkIHBvciB1bmlkYWQgZGUgdGllbXBvLg0KDQpEIChkdXJhY2nDs24gaW5mZWNjaW9zYSk6IFRpZW1wbyBwcm9tZWRpbyBkdXJhbnRlIGVsIGN1YWwgdW5hIHBlcnNvbmEgaW5mZWN0YWRhIHB1ZWRlIHRyYW5zbWl0aXIgbGEgZW5mZXJtZWRhZC4NCg0KUGFyYSBjYWxjdWxhcmxvIHVzYW5kbyBkYXRvcyByZWFsZXMgZGVsIENPVklELTE5LCBwb2RlbW9zIHVzYXIgdW4gZGF0YS5mcmFtZSBzaW11bGFkbyBvIGRhdG9zIHJlYWxlcyBzaSBzZSBkaXNwb25lIGRlIGVsbG9zLg0KDQpVc2Ftb3MgdW4gY29uanVudG8gZGUgZGF0b3MgcXVlIHNpbXVsYSBsYSBwcm9wYWdhY2nDs24gZGVsIENPVklELTE5IGVuIHVuYSBwb2JsYWNpw7NuLg0KDQpgYGB7cn0NCiMjIERhdGFzZXQgU2ltdWxhZG8gZGUgQ09WSUQtMTkNCg0Kc2V0LnNlZWQoMTIzKQ0KDQpkYXRvcyA8LSBkYXRhLmZyYW1lKA0KICBpZCA9IDE6MzAsDQogIGZlY2hhID0gc2VxKGFzLkRhdGUoIjIwMjAtMDMtMDEiKSwgYXMuRGF0ZSgiMjAyMC0wMy0zMCIpLCBieSA9ICJkYXlzIiksDQogIG51ZXZvc19jYXNvcyA9IHJvdW5kKHJ1bmlmKDMwLCA1MCwgMTUwKSksICAjIE51ZXZvcyBjYXNvcyBkaWFyaW9zDQogIHJlY3VwZXJhZG9zID0gcm91bmQocnVuaWYoMzAsIDMwLCAxMDApKSwgICMgUmVjdXBlcmFjaW9uZXMgZGlhcmlhcw0KICBmYWxsZWNpZG9zID0gcm91bmQocnVuaWYoMzAsIDEsIDEwKSkpICAgICAgIyBGYWxsZWNpbWllbnRvcyBkaWFyaW9zDQoNCg0KIyBNb3N0cmFyIGxvcyBwcmltZXJvcyBkYXRvcw0KaGVhZChkYXRvcykNCmBgYA0KDQpgYGB7cn0NClZpZXcoZGF0b3MpDQpgYGANCg0KQ2FsY3VsYXIgUuKCgDogTcOpdG9kbyBTaW1wbGlmaWNhZG8NCkRlZmluaWNpw7NuDQpQYXJhIGVzdGltYXIgUuKCgCwgbmVjZXNpdGFtb3M6DQoNCkR1cmFjacOzbiBpbmZlY2Npb3NhIHByb21lZGlvIChEKTogQXN1bWltb3MgMTAgZMOtYXMgcGFyYSBDT1ZJRC0xOS4NCg0KQ3JlY2ltaWVudG8gZXhwb25lbmNpYWwgKM67KTogQ2FsY3VsYWRvIGEgcGFydGlyIGRlIGxhIHRhc2EgZGUgbnVldm9zIGNhc29zIGRpYXJpb3MuDQoNClBhc28gMTogQ2FsY3VsYXIgQ3JlY2ltaWVudG8gRGlhcmlvDQoNCg0KYGBge3J9DQojIENyZWNpbWllbnRvIGRpYXJpbyBkZSBjYXNvcw0KZGF0b3MkY3JlY2ltaWVudG8gPC0gYyhOQSwgZGlmZihkYXRvcyRudWV2b3NfY2Fzb3MpKQ0KDQojIENhbGN1bGFyIGVsIHByb21lZGlvIGRlbCBjcmVjaW1pZW50byBkaWFyaW8NCmxhbWJkYSA8LSBtZWFuKGRhdG9zJGNyZWNpbWllbnRvLCBuYS5ybSA9IFRSVUUpDQoNCmNhdCgiQ3JlY2ltaWVudG8gZGlhcmlvIHByb21lZGlvICjOuyk6Iiwgcm91bmQobGFtYmRhLCAyKSwgImNhc29zIHBvciBkw61hXG4iKQ0KYGBgDQoNClBhc28gMjogQ2FsY3VsYXIgUuKCgA0KDQpgYGB7cn0NCiMgQXN1bWltb3MgdW5hIGR1cmFjacOzbiBpbmZlY2Npb3NhIHByb21lZGlvIEQgPSAxMCBkw61hcw0KRCA8LSAxMA0KDQojIEVzdGltYWNpw7NuIGRlIFLigoANClIwIDwtIGxhbWJkYSAqIEQNCmNhdCgiTsO6bWVybyBiw6FzaWNvIGRlIHJlcHJvZHVjY2nDs24gKFLigoApOiIsIHJvdW5kKFIwLCAyKSwgIlxuIikNCmBgYA0KDQpWaXN1YWxpemFjacOzbiBkZWwgTsO6bWVybyBS4oKADQpHcsOhZmljbyBkZSBOdWV2b3MgQ2Fzb3MNCg0KYGBge3J9DQpsaWJyYXJ5KGdncGxvdDIpDQoNCmdncGxvdChkYXRvcywgYWVzKHggPSBmZWNoYSwgeSA9IG51ZXZvc19jYXNvcykpICsNCiAgZ2VvbV9saW5lKGNvbG9yID0gImJsdWUiLCBzaXplID0gMSkgKw0KICBnZW9tX3BvaW50KGNvbG9yID0gInJlZCIpICsNCiAgbGFicyh0aXRsZSA9ICJOdWV2b3MgQ2Fzb3MgRGlhcmlvcyBkZSBDT1ZJRC0xOSIsDQogICAgICAgeCA9ICJGZWNoYSIsIHkgPSAiTnVldm9zIENhc29zIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KDQoNClJlZmxleGnDs24NCsK/Q8OzbW8gaW5mbHV5ZSBsYSBkdXJhY2nDs24gaW5mZWNjaW9zYSBwcm9tZWRpbyAoRCkgZW4gZWwgY8OhbGN1bG8gZGUgUuKCgD8NCg0Kwr9RdcOpIGludGVydmVuY2lvbmVzIChjb21vIGN1YXJlbnRlbmFzIG8gdmFjdW5hY2nDs24pIHB1ZWRlbiByZWR1Y2lyIM6yIHksIHBvciBlbmRlLCBS4oKAPw0KDQrCv1F1w6kgc2lnbmlmaWNhIHVuIFLigoAgPiAxIHBhcmEgbGEgcHJvcGFnYWNpw7NuIGRlIGxhIGVuZmVybWVkYWQ/DQoNCkRldGFsbGVzIGRlbCBFamVtcGxvDQpEdXJhY2nDs24gSW5mZWNjaW9zYSBQcm9tZWRpbyAoRCk6DQpTZSBwdWVkZSBhanVzdGFyIGNvbiBiYXNlIGVuIGV2aWRlbmNpYSBjbMOtbmljYSwgY29tbyBlc3R1ZGlvcyBxdWUgZXN0aW1lbiBjdcOhbnRvIHRpZW1wbyBsb3MgcGFjaWVudGVzIGNvbiBDT1ZJRC0xOSBzb24gaW5mZWNjaW9zb3MuDQpEYXRvcyBTaW11bGFkb3M6DQpFbiBlc3RlIGVqZW1wbG8sIGxvcyBkYXRvcyBzb24gZ2VuZXJhZG9zIGFsZWF0b3JpYW1lbnRlLiBQdWVkZXMgcmVlbXBsYXphcmxvcyBjb24gZGF0b3MgcmVhbGVzIGRlbCBDT1ZJRC0xOSBkZSBmdWVudGVzIGNvbW8gT3VyIFdvcmxkIGluIERhdGEgbyBiYXNlcyBndWJlcm5hbWVudGFsZXMuDQpDcmVjaW1pZW50byBEaWFyaW8gKM67KToNCkVsIGNyZWNpbWllbnRvIHByb21lZGlvIHNlIGNhbGN1bGEgY29uIGxhIGRpZmVyZW5jaWEgZGlhcmlhIGRlIG51ZXZvcyBjYXNvcy4NCg0KVXNvIGRlIFBhcXVldGVzIEFkaWNpb25hbGVzDQpTaSBkZXNlYXMgdXNhciBoZXJyYW1pZW50YXMgZXNwZWPDrWZpY2FzIHBhcmEgZWwgY8OhbGN1bG8gZGUgUuKCgCwgcHVlZGVzIGV4cGxvcmFyIHBhcXVldGVzIGNvbW8gRXBpRXN0aW06DQoNCmBgYHtyfQ0KaW5zdGFsbC5wYWNrYWdlcygiRXBpRXN0aW0iKQ0KbGlicmFyeShFcGlFc3RpbSkNCg0KIyBDYWxjdWxhciBS4oKAIHVzYW5kbyBFcGlFc3RpbSAocmVlbXBsYXphciBjb24gZGF0b3MgcmVhbGVzKQ0KcmVzIDwtIGVzdGltYXRlX1IoDQogIGluY2lkID0gZGF0b3MkbnVldm9zX2Nhc29zLA0KICBtZXRob2QgPSAicGFyYW1ldHJpY19zaSIsDQogIGNvbmZpZyA9IG1ha2VfY29uZmlnKGxpc3QobWVhbl9zaSA9IDQsIHN0ZF9zaSA9IDIpKQ0KKQ0KYGBgDQpgYGB7cn0NCnByaW50KHJlcyRSKQ0KYGBgDQoNCg0KU2ltdWxhbW9zIGVsIGJyb3RlIGVwaWRlbWljbyAoZmljdGljaW8pOg0KDQpgYGB7cn0NCnNldC5zZWVkKDEyMykNCmRpYXMgPC0gMzANCg0KY2Fzb3MgPC0gcm91bmQocnVuaWYoZGlhcywgbWluID0gMTAsIG1heCA9IDEwMCkpDQoNCmRhdG9zIDwtIGRhdGEuZnJhbWUoDQogIGZlY2hhID0gc2VxKGFzLkRhdGUoIjIwMjAtMDEtMDEiKSwgYnkgPSAiZGF5cyIsIGxlbmd0aC5vdXQgPSBkaWFzKSwNCiAgbnVldm9zX2Nhc29zID0gY2Fzb3MNCikNCg0KIyBNb3N0cmFyIGxvcyBwcmltZXJvcyBkYXRvcw0KaGVhZChkYXRvcykNCmBgYA0KDQoNCkNvbmZpZ3VyYWNpw7NuIGRlbCBJbnRlcnZhbG8gU2VyaWFsDQpEZWZpbmlyIGVsIEludGVydmFsbyBTZXJpYWwNCkVsIGludGVydmFsbyBzZXJpYWwgcmVwcmVzZW50YSBlbCB0aWVtcG8gcHJvbWVkaW8gZW50cmUgaW5mZWNjaW9uZXMgcHJpbWFyaWFzIHkgc2VjdW5kYXJpYXMuIEVuIENPVklELTE5LCBlc3R1ZGlvcyBzdWdpZXJlbiB1bmEgZGlzdHJpYnVjacOzbiBnYW1tYSBjb246DQoNCk1lZGlhOiA0IGTDrWFzDQoNCkRlc3ZpYWNpw7NuIGVzdMOhbmRhcjogMiBkw61hcw0KDQpgYGB7cn0NCiMgQ29uZmlndXJhciBlbCBpbnRlcnZhbG8gc2VyaWFsDQpjb25maWcgPC0gbWFrZV9jb25maWcoDQogIGxpc3QobWVhbl9zaSA9IDQsIHN0ZF9zaSA9IDIpKSAjIE5vcm1hbG1lbnRlLCBtYWtlX2NvbmZpZyBzZSB1dGlsaXphIHBhcmEgY3JlYXIgbyBnZXN0aW9uYXIgY29uZmlndXJhY2lvbmVzDQpgYGANCg0KQ2FsY3VsYXIgZWwgTsO6bWVybyBkZSBSZXByb2R1Y2Npw7NuIEVmZWN0aXZvIChS4oKRKQ0KRXN0aW1hciBS4oKRDQpVc2Ftb3MgbG9zIGRhdG9zIGRpYXJpb3MgZGUgbnVldm9zIGNhc29zIHBhcmEgY2FsY3VsYXIgUuKCkS4NCg0KYGBge3J9DQojIEVzdGltYXIgUuKCkSB1c2FuZG8gRXBpRXN0aW0NCnJlc3VsdGFkbyA8LSBlc3RpbWF0ZV9SKA0KICBpbmNpZCA9IGRhdG9zJG51ZXZvc19jYXNvcywNCiAgbWV0aG9kID0gInBhcmFtZXRyaWNfc2kiLA0KICBjb25maWcgPSBjb25maWcNCikNCmBgYA0KDQpgYGB7cn0NCiMgTW9zdHJhciByZXN1bHRhZG9zDQpwcmludChyZXN1bHRhZG8kUikNCmBgYA0KDQoNClZpc3VhbGl6YWNpw7NuIGRlIFLigpENCkdyw6FmaWNvIGRlIGxhIEV2b2x1Y2nDs24gZGUgUuKCkQ0KQ3JlYW1vcyB1biBncsOhZmljbyBxdWUgbXVlc3RyYSBjw7NtbyBS4oKRIGNhbWJpYSBjb24gZWwgdGllbXBvLCBsbyBxdWUgcHVlZGUgaW5kaWNhciBlbCBpbXBhY3RvIGRlIGludGVydmVuY2lvbmVzLg0KDQpgYGB7cn0NCiMgR3LDoWZpY28gZGUgUuKCkQ0KcGxvdCgNCiAgcmVzdWx0YWRvLA0KICB3aGF0ID0gIlIiLA0KICBsZWdlbmQgPSBGQUxTRSwNCiAgbWFpbiA9ICJFdm9sdWNpw7NuIGRlbCBOw7ptZXJvIGRlIFJlcHJvZHVjY2nDs24gRWZlY3Rpdm8gKFLigpEpIg0KKQ0KYGBgDQoNCg0KDQpJbnRlcnByZXRhY2nDs246DQoNClLigpEgPiAxOiBMYSBlcGlkZW1pYSBlc3TDoSBlbiBleHBhbnNpw7NuLg0KDQpS4oKRIDwgMTogTGEgZXBpZGVtaWEgZXN0w6EgZGlzbWludXllbmRvLg0KDQpMYSBmdW5jacOzbiBlc3RpbWF0ZV9SIGRlbCBwYXF1ZXRlIHtFcGlFc3RpbX0gZW4gUiBzZSB1dGlsaXphIHBhcmEgZXN0aW1hciBlbCBuw7ptZXJvIGRlIHJlcHJvZHVjY2nDs24gZWZlY3Rpdm8gKFLigpEpLCBlcyB1bmEgbcOpdHJpY2EgY2xhdmUgZW4gZXBpZGVtaW9sb2fDrWEgcGFyYSBtZWRpciBsYSBjYXBhY2lkYWQgZGUgdHJhbnNtaXNpw7NuIGRlIHVuYSBlbmZlcm1lZGFkIGluZmVjY2lvc2EgZW4gdW5hIHBvYmxhY2nDs24gYSBsbyBsYXJnbyBkZWwgdGllbXBvLg0KDQpQcmFjdGljYQ0KSW50cm9kdWNjacOzbg0KRW4gZXN0YSBwcsOhY3RpY2EgY2FsY3VsYXJlbW9zICwgUuKCkSB5IFIgcGFyYSB2YXJpYXMgZW5mZXJtZWRhZGVzIGluZmVjY2lvc2FzIHV0aWxpemFuZG8gcGFyw6FtZXRyb3MgcmVhbGVzLiBFc3RvcyB2YWxvcmVzIHNvbiBjcnVjaWFsZXMgcGFyYSBjb21wcmVuZGVyIGxhIGRpbsOhbWljYSBkZSB0cmFuc21pc2nDs24geSBlbCBpbXBhY3RvIGRlIG1lZGlkYXMgZGUgY29udHJvbC4NCg0KVmFsb3JlcyBwYXJhIGxhcyBlbmZlcm1lZGFkZXMgSW5mZWNjaW9zYXMNCkNPVklELTE5IChTQVJTLUNvVi0yKToNCg0KVGFzYSBkZSB0cmFuc21pc2nDs246IH45OS40JSAoYWx0YSB0cmFuc21pc2nDs24gZW4gZW50b3Jub3MgY2VycmFkb3MpIC4NCg0KVGFzYSBkZSBtb3J0YWxpZGFkOiAwLjE4NSUgLg0KDQpEdXJhY2nDs24gaW5mZWNjaW9zYTogfjEwIGTDrWFzIC4NCg0KRGlzdGFuY2lhbWllbnRvIHNvY2lhbDogfjMwJSBwdWVkZSByZWR1Y2lyIHNpZ25pZmljYXRpdmFtZW50ZSBsYSB0cmFuc21pc2nDs24uDQoNClJlZmVyZW5jaWE6IEVzdGltYXRpb24gb2YgdGhlIHJlcHJvZHVjdGlvbiBudW1iZXIgb2YgU0FSUy1Db1YtMi4NCg0KTUVSUy1Db1YgKE1pZGRsZSBFYXN0IFJlc3BpcmF0b3J5IFN5bmRyb21lKToNCg0KVGFzYSBkZSB0cmFuc21pc2nDs246IH41JSAoZW4gY29udGFjdG9zIGRvbWljaWxpYXJpb3MpLg0KDQpUYXNhIGRlIG1vcnRhbGlkYWQ6IH4zNSUuDQoNCkR1cmFjacOzbiBpbmZlY2Npb3NhOiB+MTQgZMOtYXMuDQoNClJlZmVyZW5jaWE6IEVtZXJnaW5nIHJlc3BpcmF0b3J5IHZpcmFsIGluZmVjdGlvbnM6IE1FUlMtQ29WIGFuZCBpbmZsdWVuemEuDQoNCkluZmx1ZW56YSBFc3RhY2lvbmFsOg0KDQpUYXNhIGRlIHRyYW5zbWlzacOzbjogfjUw4oCTNzUlLg0KDQpUYXNhIGRlIG1vcnRhbGlkYWQ6IH4wLjElLg0KDQpEdXJhY2nDs24gaW5mZWNjaW9zYTogfjcgZMOtYXMuDQoNClJlZmVyZW5jaWE6IEluZmx1ZW56YSBhbmQgdHViZXJjdWxvc2lzIGNvLWluZmVjdGlvbjogQSBzeXN0ZW1hdGljIHJldmlldy4NCg0KVHViZXJjdWxvc2lzOg0KDQpUYXNhIGRlIHRyYW5zbWlzacOzbjogfjMwJSAoZW4gY29udGFjdG9zIGNlcmNhbm9zKS4NCg0KVGFzYSBkZSBtb3J0YWxpZGFkOiB+MTUlLg0KDQpEdXJhY2nDs24gaW5mZWNjaW9zYTogTWVzZXMgYSBhw7FvcyBzaW4gdHJhdGFtaWVudG8uDQoNClJlZmVyZW5jaWE6IFR1YmVyY3Vsb3NpcyBhbmQgQ292aWQtMTkgQ28tSW5mZWN0aW9uLg0KDQpTYXJhbXBpw7NuOg0KDQpUYXNhIGRlIHRyYW5zbWlzacOzbjogOTDigJMxMDAlIChtdXkgYWx0YSkuDQoNClRhc2EgZGUgbW9ydGFsaWRhZDogfjAuMiUgZW4gcGHDrXNlcyBkZXNhcnJvbGxhZG9zOyBtw6FzIGFsdGEgZW4gY29udGV4dG9zIHNpbiB2YWN1bmFjacOzbi4NCg0KRHVyYWNpw7NuIGluZmVjY2lvc2E6IH44IGTDrWFzLg0KDQpSZWZlcmVuY2lhOiBNb2xlY3VsYXIgYW5kIENlbGx1bGFyIE1lY2hhbmlzbXMgb2YgTS4gdHViZXJjdWxvc2lzIGFuZCBTQVJTLUNvVi0yIEluZmVjdGlvbnMuDQoNClNpbXVsYXJlbW9zIGxhIHByb3BhZ2FjacOzbiBkZSA1IGVuZmVybWVkYWRlcyBpbmZlY2Npb3NhcyBjb24gZGlmZXJlbnRlcyB0YXNhcyBkZSB0cmFuc21pc2nDs24sIG1vcnRhbGlkYWQgeSBkdXJhY2nDs24gaW5mZWNjaW9zYS4gRXN0YXMgc2ltdWxhY2lvbmVzIHNlIGJhc2Fyw6FuIGVuIHBhcsOhbWV0cm9zIG9idGVuaWRvcyBkZSBsYSBsaXRlcmF0dXJhIGNpZW50w61maWNhLg0KDQpgYGB7cn0NCiMgUGFyw6FtZXRyb3MgZGUgbGFzIGVuZmVybWVkYWRlcw0KZW5mZXJtZWRhZGVzIDwtIGRhdGEuZnJhbWUoDQogIEVuZmVybWVkYWQgPSBjKCJDT1ZJRC0xOSIsICJNRVJTLUNvViIsICJJbmZsdWVuemEgRXN0YWNpb25hbCIsICJUdWJlcmN1bG9zaXMiLCAiU2FyYW1wacOzbiIpLA0KICBUYXNhX1RyYW5zbWlzaW9uID0gYyg5OS40LCA1LCA2MCwgMzAsIDk1KSwgIyBQb3JjZW50YWplDQogIFRhc2FfTW9ydGFsaWRhZCA9IGMoMC4xODUsIDM1LCAwLjEsIDE1LCAwLjIpLCAjIFBvcmNlbnRhamUNCiAgRHVyYWNpb25fSW5mZWNjaW9zYSA9IGMoMTAsIDE0LCA3LCAxODAsIDgpLCAjIETDrWFzDQogIERpc3RhbmNpYW1pZW50b19Tb2NpYWwgPSBjKDMwLCAwLCAxNSwgMTAsIDApLCAjIFBvcmNlbnRhamUNCiAgUG9ibGFjaW9uID0gYygxMDAwMDAwLCAxMDAwMDAwLCAxMDAwMDAwLCAxMDAwMDAwLCAxMDAwMDAwKSAjIFBvYmxhY2nDs24gcGFyYSBzaW11bGFjacOzbg0KKQ0KYGBgDQoNCkPDoWxjdWxvIGRlIFLigoANCkbDs3JtdWxhIGRlIFLigoANClLigoAgKE7Dum1lcm8gYsOhc2ljbyBkZSByZXByb2R1Y2Npw7NuKToNCg0KUmVwcmVzZW50YSBlbCBuw7ptZXJvIHByb21lZGlvIGRlIGluZmVjY2lvbmVzIHNlY3VuZGFyaWFzIGNhdXNhZGFzIHBvciB1biBpbmRpdmlkdW8gaW5mZWN0YWRvIGVuIHVuYSBwb2JsYWNpw7NuIGNvbXBsZXRhbWVudGUgc3VzY2VwdGlibGUuDQoNCkbDs3JtdWxhOg0KUjAgPSBUYXNhIGRlIHRyYW5zbWlzacOzbiB4IER1cmFjacOzbiBpbmZlY2Npb3NhDQoNCmBgYHtyfQ0KIyBDYWxjdWxhciBS4oKAIHBhcmEgY2FkYSBlbmZlcm1lZGFkDQplbmZlcm1lZGFkZXMkUjAgPC0gKGVuZmVybWVkYWRlcyRUYXNhX1RyYW5zbWlzaW9uIC8gMTAwKSAqIGVuZmVybWVkYWRlcyREdXJhY2lvbl9JbmZlY2Npb3NhDQoNCiMgTW9zdHJhciByZXN1bHRhZG9zDQplbmZlcm1lZGFkZXNbYygiRW5mZXJtZWRhZCIsICJSMCIpXQ0KYGBgDQoNCkPDoWxjdWxvIGRlIFLigpENCkbDs3JtdWxhIGRlIFLigpENCkNhbGN1bGEgUmUgb25zaWRlcmFuZG8gcXVlIG5vIHRvZGEgbGEgcG9ibGFjacOzbiBlcyBzdXNjZXB0aWJsZSAoZGViaWRvIGEgaW5tdW5pZGFkIG8gaW50ZXJ2ZW5jaW9uZXMpLg0KDQpGw7NybXVsYToNCg0KUmUgPSBSbyAqIHN1c2NlcHRpYmxlcyAvIHBvYmxhY2nDs24gdG90YWwNCg0KYGBge3J9DQojIENvbnNpZGVyYXIgNzAlIGRlIGxhIHBvYmxhY2nDs24gaW5pY2lhbCBjb21vIHN1c2NlcHRpYmxlcw0KZW5mZXJtZWRhZGVzJFN1c2NlcHRpYmxlcyA8LSBlbmZlcm1lZGFkZXMkUG9ibGFjaW9uICogMC43DQoNCiMgQ2FsY3VsYXIgUuKCkQ0KZW5mZXJtZWRhZGVzJFJlIDwtIGVuZmVybWVkYWRlcyRSMCAqIChlbmZlcm1lZGFkZXMkU3VzY2VwdGlibGVzIC8gZW5mZXJtZWRhZGVzJFBvYmxhY2lvbikNCg0KIyBNb3N0cmFyIHJlc3VsdGFkb3MNCmVuZmVybWVkYWRlc1tjKCJFbmZlcm1lZGFkIiwgIlIwIiwgIlJlIildDQpgYGANCg0KQ8OhbGN1bG8gZGUgUuKCnA0KRGluw6FtaWNhIFRlbXBvcmFsIGRlIFLigpwNCkNvbnNpZGVyYW1vcyBpbnRlcnZlbmNpb25lcyBjb21vIGRpc3RhbmNpYW1pZW50byBzb2NpYWwgeSBjYW1iaW9zIGVuIGxvcyBzdXNjZXB0aWJsZXMgYSBsbyBsYXJnbyBkZWwgdGllbXBvLg0KDQpgYGB7cn0NCiMgQ3JlYXIgdW4gZGF0YSBmcmFtZSBwYXJhIHNpbXVsYXIgbGEgZGluw6FtaWNhIHRlbXBvcmFsDQpkaWFzIDwtIDMwDQpyZXN1bHRhZG9fcnQgPC0gZGF0YS5mcmFtZSgpDQoNCmZvciAoaSBpbiAxOm5yb3coZW5mZXJtZWRhZGVzKSkgew0KICBlbmZlcm1lZGFkIDwtIGVuZmVybWVkYWRlc1tpLCBdDQogIHN1c2NlcHRpYmxlcyA8LSBlbmZlcm1lZGFkJFN1c2NlcHRpYmxlcw0KICBydCA8LSBudW1lcmljKGRpYXMpDQogIA0KICBmb3IgKHQgaW4gMTpkaWFzKSB7DQogICAgcnRbdF0gPC0gZW5mZXJtZWRhZCRSMCAqIChzdXNjZXB0aWJsZXMgLyBlbmZlcm1lZGFkJFBvYmxhY2lvbikgKiAoMSAtIGVuZmVybWVkYWQkRGlzdGFuY2lhbWllbnRvX1NvY2lhbCAvIDEwMCkNCiAgICBzdXNjZXB0aWJsZXMgPC0gbWF4KDAsIHN1c2NlcHRpYmxlcyAtIChzdXNjZXB0aWJsZXMgKiBlbmZlcm1lZGFkJFRhc2FfVHJhbnNtaXNpb24gLyAxMDApKSAjIFJlZHVjaXIgc3VzY2VwdGlibGVzDQogIH0NCiAgDQogIHJlc3VsdGFkb19ydCA8LSByYmluZChyZXN1bHRhZG9fcnQsIGRhdGEuZnJhbWUoRGlhID0gMTpkaWFzLCBSX3QgPSBydCwgRW5mZXJtZWRhZCA9IGVuZmVybWVkYWQkRW5mZXJtZWRhZCkpDQp9DQoNCiMgTW9zdHJhciByZXN1bHRhZG9zIHBhcmEgdW5hIGVuZmVybWVkYWQNCmhlYWQocmVzdWx0YWRvX3J0W3Jlc3VsdGFkb19ydCRFbmZlcm1lZGFkID09ICJDT1ZJRC0xOSIsIF0pDQpgYGANCg0KVmlzdWFsaXphY2nDs24gZGUgUuKCnA0KR3LDoWZpY28gcGFyYSBDYWRhIEVuZmVybWVkYWQNCg0KYGBge3J9DQpsaWJyYXJ5KGdncGxvdDIpDQoNCiMgR3LDoWZpY28gZGUgUuKCnA0KZ2dwbG90KHJlc3VsdGFkb19ydCwgYWVzKHggPSBEaWEsIHkgPSBSX3QsIGNvbG9yID0gRW5mZXJtZWRhZCkpICsNCiAgZ2VvbV9saW5lKHNpemUgPSAxKSArDQogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDEsIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gInJlZCIpICsNCiAgbGFicyh0aXRsZSA9ICJFdm9sdWNpw7NuIGRlIFJ0IHBvciBFbmZlcm1lZGFkIiwgDQogICAgICAgeCA9ICJEw61hcyIsIHkgPSAiUnQiKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlID0gIlNldDEiKQ0KYGBgDQoNCg0KQm9udXMgdHJhY2s6DQpQb2RlbW9zIGNyZWFyIHVuYSBmdW5jacOzbiBlbiBSIHBhcmEgc2ltdWxhciBsYSBlbmZlcm1lZGFkDQpFbiBlbCBzaWd1aWVudGUgYmxvcXVlIGRlIGNvZGlnbyBzZSBjcmVhIGxhIGZ1bmNpw7NuIHNpbXVsYXJfZW5mZXJtZWRhZA0KDQpgYGB7cn0NCiMgQ3JlYXIgdW5hIGZ1bmNpw7NuIGRlIHNpbXVsYWNpw7NuDQpzaW11bGFyX2VuZmVybWVkYWQgPC0gZnVuY3Rpb24oZW5mZXJtZWRhZCwgdGFzYV90cmFuc21pc2lvbiwgdGFzYV9tb3J0YWxpZGFkLCBkdXJhY2lvbiwgZGlzdF9zb2NpYWwsIHBvYmxhY2lvbiA9IDEwMDApIHsNCiAgZGlhcyA8LSAzMA0KICBpbmZlY2Npb3NvcyA8LSAxICAjIENhc29zIGluaWNpYWxlcw0KICByZWN1cGVyYWRvcyA8LSAwDQogIGZhbGxlY2lkb3MgPC0gMA0KICBzdXNjZXB0aWJsZXMgPC0gcG9ibGFjaW9uIC0gaW5mZWNjaW9zb3MNCiAgDQogIHJlc3VsdGFkbyA8LSBkYXRhLmZyYW1lKERpYSA9IDE6ZGlhcywgSW5mZWN0YWRvcyA9IE5BLCBSZWN1cGVyYWRvcyA9IE5BLCBGYWxsZWNpZG9zID0gTkEsIFN1c2NlcHRpYmxlcyA9IE5BKQ0KICANCiAgZm9yIChkaWEgaW4gMTpkaWFzKSB7DQogICAgbnVldmFzX2luZmVjY2lvbmVzIDwtIHJvdW5kKChpbmZlY2Npb3NvcyAqIHRhc2FfdHJhbnNtaXNpb24gLyAxMDApICogKDEgLSBkaXN0X3NvY2lhbCAvIDEwMCkpDQogICAgbnVldmFzX2luZmVjY2lvbmVzIDwtIG1pbihudWV2YXNfaW5mZWNjaW9uZXMsIHN1c2NlcHRpYmxlcykgICMgTm8gZXhjZWRlciBzdXNjZXB0aWJsZXMNCiAgICANCiAgICBudWV2b3NfcmVjdXBlcmFkb3MgPC0gaWZlbHNlKGRpYSA+IGR1cmFjaW9uLCBpbmZlY2Npb3NvcyAvIGR1cmFjaW9uLCAwKQ0KICAgIG51ZXZvc19mYWxsZWNpZG9zIDwtIHJvdW5kKCh0YXNhX21vcnRhbGlkYWQgLyAxMDApICogaW5mZWNjaW9zb3MpDQogICAgDQogICAgaW5mZWNjaW9zb3MgPC0gaW5mZWNjaW9zb3MgKyBudWV2YXNfaW5mZWNjaW9uZXMgLSBudWV2b3NfcmVjdXBlcmFkb3MgLSBudWV2b3NfZmFsbGVjaWRvcw0KICAgIHJlY3VwZXJhZG9zIDwtIHJlY3VwZXJhZG9zICsgbnVldm9zX3JlY3VwZXJhZG9zDQogICAgZmFsbGVjaWRvcyA8LSBmYWxsZWNpZG9zICsgbnVldm9zX2ZhbGxlY2lkb3MNCiAgICBzdXNjZXB0aWJsZXMgPC0gcG9ibGFjaW9uIC0gaW5mZWNjaW9zb3MgLSByZWN1cGVyYWRvcyAtIGZhbGxlY2lkb3MNCiAgICANCiAgICByZXN1bHRhZG9bZGlhLCBdIDwtIGMoZGlhLCBpbmZlY2Npb3NvcywgcmVjdXBlcmFkb3MsIGZhbGxlY2lkb3MsIHN1c2NlcHRpYmxlcykNCiAgfQ0KICByZXR1cm4ocmVzdWx0YWRvKQ0KfQ0KYGBgDQoNCg0KVW5hIHZleiBjcmVhZGEgbGEgZnVuY2nDs24gYWhvcmEgcG9kZW1vcyB1dGlsaXphcmxhOg0KVmFtb3MgYSBjcmVhciB1biBvYmpldG8gcXVlIGdhcmRlIGxvcyBkYXRvcyBzaW11bGFkb3MgZGUgdW5hIGVuZmVybWVkYWQgKCBwdWVkZSBzZXIgZGUgbG9zIGRhdG9zIHF1ZSB5YSByZXZpc2Ftb3MgZW4gbGEgbGl0ZXJhdHVyYSkNCg0KDQojIFNpbXVsYWNpw7NuIGRlIGVqZW1wbG8gcGFyYSBDT1ZJRC0xOQ0Kc2ltX2NvdmlkIDwtIHNpbXVsYXJfZW5mZXJtZWRhZCgNCiAgIkNPVklELTE5IiwgdGFzYV90cmFuc21pc2lvbiA9IDk5LjQsIHRhc2FfbW9ydGFsaWRhZCA9IDIwLCANCiAgZHVyYWNpb24gPSAxMCwgZGlzdF9zb2NpYWwgPSAzMCwgcG9ibGFjaW9uID0gMTAwMA0KKQ0KDQpgYGB7cn0NCiMgU2ltdWxhY2nDs24gZGUgZWplbXBsbyBwYXJhIENPVklELTE5DQpzaW1fY292aWQgPC0gc2ltdWxhcl9lbmZlcm1lZGFkKA0KICAiQ09WSUQtMTkiLCB0YXNhX3RyYW5zbWlzaW9uID0gOTkuNCwgdGFzYV9tb3J0YWxpZGFkID0gMjAsIA0KICBkdXJhY2lvbiA9IDEwLCBkaXN0X3NvY2lhbCA9IDMwLCBwb2JsYWNpb24gPSAxMDAwDQopDQpgYGANCg0KDQpgYGB7cn0NCmxpYnJhcnkoZ2dwbG90MikNCg0KIyBHcsOhZmljbyBwYXJhIENPVklELTE5DQpnZ3Bsb3Qoc2ltX2NvdmlkLCBhZXMoeCA9IERpYSkpICsNCiAgZ2VvbV9saW5lKGFlcyh5ID0gSW5mZWN0YWRvcywgY29sb3IgPSAiSW5mZWN0YWRvcyIpKSArDQogIGdlb21fbGluZShhZXMoeSA9IFJlY3VwZXJhZG9zLCBjb2xvciA9ICJSZWN1cGVyYWRvcyIpKSArDQogIGdlb21fbGluZShhZXMoeSA9IEZhbGxlY2lkb3MsIGNvbG9yID0gIkZhbGxlY2lkb3MiKSkgKw0KICBnZW9tX2xpbmUoYWVzKHkgPSBTdXNjZXB0aWJsZXMsIGNvbG9yID0gIlN1c2NlcHRpYmxlcyIpKSArDQogIGxhYnModGl0bGUgPSAiU2ltdWxhY2nDs24gZGUgQ292aWQtMTkiLCANCiAgICAgICB4ID0gIkTDrWFzIiwgeSA9ICJOw7ptZXJvIGRlIFBlcnNvbmFzIikgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwobmFtZSA9ICJFc3RhZG8iLCB2YWx1ZXMgPSBjKCJJbmZlY3RhZG9zIiA9ICJyZWQiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUmVjdXBlcmFkb3MiID0gImdyZWVuIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkZhbGxlY2lkb3MiID0gImJsYWNrIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlN1c2NlcHRpYmxlcyIgPSAiYmx1ZSIpKSArDQogIHRoZW1lX21pbmltYWwoKQ0KYGBgDQoNCkNvbmNsdXNpw7NuDQpMYSBzaW11bGFjacOzbiBwZXJtaXRlIHZpc3VhbGl6YXIgbGEgZGluw6FtaWNhIGRlIHRyYW5zbWlzacOzbiB5IGVsIGltcGFjdG8gZGUgaW50ZXJ2ZW5jaW9uZXMgY29tbyBlbCBkaXN0YW5jaWFtaWVudG8gc29jaWFsLiBMb3MgcmVzdWx0YWRvcyBwdWVkZW4gdXNhcnNlIHBhcmEgcGxhbmlmaWNhciByZXNwdWVzdGFzIGFudGUgYnJvdGVzIHJlYWxlcy4NCg0KQWRlbcOhcywgcGVybWl0ZSBhbmFsaXphciBsYSBkaW7DoW1pY2EgZGUgbGEgcHJvcGFnYWNpw7NuIGRlIGVuZmVybWVkYWRlcyBiYWpvIGRpZmVyZW50ZXMgZXNjZW5hcmlvcyBkZSBpbnRlcnZlbmNpw7NuIHkgcGFyw6FtZXRyb3MgZXBpZGVtaW9sw7NnaWNvcy4NCg0KU2lndWllbnRlcyBwYXNvczoNCg0KQWp1c3RhciBwYXLDoW1ldHJvcyBwYXJhIGVzY2VuYXJpb3MgZXNwZWPDrWZpY29zLg0KDQpDb21wYXJhciBlbnRyZSBkaWZlcmVudGVzIHBvYmxhY2lvbmVzIHkgZW50b3Jub3MuDQoNCkNhbGN1bG8gZGUgdGFtYcOxbyBtdWVzdHJhbA0KSW50cm9kdWNjacOzbg0KRWwgY8OhbGN1bG8gZGVsIHRhbWHDsW8gZGUgbXVlc3RyYSBlcyB1bmEgZXRhcGEgY3J1Y2lhbCBlbiBlbCBkaXNlw7FvIGRlIGVzdHVkaW9zLiBDdWFuZG8gZGVzZWFtb3MgZXN0aW1hciB1bmEgcHJvcG9yY2nDs24sIGRlYmVtb3MgY29uc2lkZXJhcjogMS4gTml2ZWwgZGUgY29uZmlhbnphIChazrEpOiBSZXByZXNlbnRhIGxhIHByb2JhYmlsaWRhZCBkZSBxdWUgZWwgaW50ZXJ2YWxvIGRlIGNvbmZpYW56YSBjb250ZW5nYSBlbCB2YWxvciB2ZXJkYWRlcm8uIEVqZW1wbG86IDk1JSAoKCBaID0gMS45NiApKS4gMi4gUHJlY2lzacOzbiBkZXNlYWRhICgoIGUgKSk6IERpZmVyZW5jaWEgbcOheGltYSBhY2VwdGFibGUgZW50cmUgbGEgcHJvcG9yY2nDs24gZXN0aW1hZGEgeSBsYSByZWFsLiAzLiBQcm9wb3JjacOzbiBlc3BlcmFkYSAoKCBwICkpOiBFc3RpbWFjacOzbiBpbmljaWFsIGJhc2FkYSBlbiBsaXRlcmF0dXJhIG8gZXN0dWRpb3MgcHJldmlvcy4NCg0KRW4gZXN0ZSBlamVyY2ljaW8sIHV0aWxpemFyZW1vcyBlbCBwYXF1ZXRlIHB3ciBwYXJhIGNhbGN1bGFyIGVsIHRhbWHDsW8gZGUgbXVlc3RyYSBuZWNlc2FyaW8gcGFyYSBjb25vY2VyIGxhIHByZXZhbGVuY2lhIGRlIGRpYWJldGVzIGVuIHVuYSBwb2JsYWNpw7NuLCBzdXBvbmllbmRvIHF1ZTogLSBOaXZlbCBkZSBjb25maWFuemE6IDk1JSAtIFByZWNpc2nDs24gZGVzZWFkYTogNSUgLSBQcm9wb3JjacOzbiBlc3BlcmFkYTogNTAlICgwLjUpLg0KDQpJbnN0YWxhY2nDs24geSBjb25maWd1cmFjacOzbiBkZSBwYXF1ZXRlcw0KUHJpbWVybywgYXNlZ3Vyw6ltb25vcyBkZSBpbnN0YWxhciB5IGNhcmdhciBsb3MgcGFxdWV0ZXMgbmVjZXNhcmlvcy4NCg0KUGFxdWV0ZSBwd3INCg0KYGBge3J9DQojIEVqZWN1dGFyIHNvbG8gdW5hIHZleiBzaSBubyBlc3TDoSBpbnN0YWxhZG86DQojIGluc3RhbGwucGFja2FnZXMoInB3ciIpDQoNCmxpYnJhcnkocHdyKQ0KYGBgDQoNCg0KRsOzcm11bGEgcGFyYSBlc3RpbWFyIGVsIHRhbWHDsW8gZGUgbXVlc3RyYQ0KRWwgY8OhbGN1bG8gZGVsIHRhbWHDsW8gZGUgbXVlc3RyYSBwYXJhIGVzdGltYXIgdW5hIHByb3BvcmNpw7NuIHNlIHJlYWxpemEgY29uIGxhIGbDs3JtdWxhOg0KDQoNCg0KRsOzcm11bGEgdGFtYcOxbyBkZSBtdWVzdHJhIHBhcmEgcHJvcG9yY2nDs24NCkRvbmRlOg0KDQpaOiB2YWxvciBjb3JyZXNwb25kaWVudGUgYWwgbml2ZWwgZGUgY29uZmlhbnphIChwb3IgZWplbXBsbywgWiA9IDEuOTYgcGFyYSA5NSAlKS4NCnA6IHByb3BvcmNpw7NuIGVzcGVyYWRhLg0KZTogcHJlY2lzacOzbiBkZXNlYWRhIChlcnJvciBtw6F4aW1vIHBlcm1pdGlkbykuDQpBdW5xdWUgZWwgcGFxdWV0ZSBwd3Igc2ltcGxpZmljYSBlc3RlIGPDoWxjdWxvLCB0YW1iacOpbiBwb2RlbW9zIGhhY2VybG8gZGUgZm9ybWEgbWFudWFsIHBhc28gYSBwYXNvIGVuIFIuDQoNCkPDoWxjdWxvIGRlbCB0YW1hw7FvIGRlIG11ZXN0cmENCk3DqXRvZG8gMTogQ8OhbGN1bG8gbWFudWFsDQpVdGlsaXphbmRvIGRpcmVjdGFtZW50ZSBsYSBmw7NybXVsYSBjbMOhc2ljYSBwYXJhIHByb3BvcmNpb25lcyBlbiBwb2JsYWNpw7NuIOKAnGluZmluaXRh4oCdOg0KDQoNCmBgYHtyfQ0KIyBQYXLDoW1ldHJvcw0KWiA8LSAxLjk2ICAgIyBOaXZlbCBkZSBjb25maWFuemEgcGFyYSA5NSAlDQpwIDwtIDAuNSAgICAjIFByb3BvcmNpw7NuIGVzcGVyYWRhDQplIDwtIDAuMDUgICAjIFByZWNpc2nDs24gZGVzZWFkYSAoNSAlKQ0KDQojIEPDoWxjdWxvIGRlbCB0YW1hw7FvIGRlIG11ZXN0cmENCm5fbWFudWFsIDwtIChaXjIgKiBwICogKDEgLSBwKSkgLyAoZV4yKQ0KDQpjYXQoIlRhbWHDsW8gZGUgbXVlc3RyYSBuZWNlc2FyaW8gKG3DqXRvZG8gbWFudWFsKToiLA0KICAgIGNlaWxpbmcobl9tYW51YWwpLCAiXG4iKQ0KYGBgDQoNCk3DqXRvZG8gMjogVXNvIGRlbCBwYXF1ZXRlIHB3cg0KRWwgcGFxdWV0ZSBwd3IgcHJvcG9yY2lvbmEgdW5hIGZvcm1hIG3DoXMgZ2VuZXJhbCwgYmFzYWRhIGVuIHRhbWHDsW8gZGUgZWZlY3RvIChoKSB5IHBvZGVyIGVzdGFkw61zdGljby4NCg0KU3Vwb25nYW1vcyBxdWUgcXVlcmVtb3MgZGV0ZWN0YXIgdW5hIGRpZmVyZW5jaWEgZW50cmUgdW5hIHByb3BvcmNpw7NuIG51bGEgcDAgPSAwLjUgeSB1bmEgcHJvcG9yY2nDs24gYWx0ZXJuYXRpdmEgcDEgPSAwLjYgY29uIM6xID0gMC4wNSB5IHBvZGVyIDgwICU6DQoNCmBgYHtyfQ0KIyBQYXLDoW1ldHJvcyBwYXJhIHBydWViYSBkZSB1bmEgcHJvcG9yY2nDs24NCnAwIDwtIDAuNSAgICMgUHJvcG9yY2nDs24gbnVsYQ0KcDEgPC0gMC42ICAgIyBQcm9wb3JjacOzbiBhbHRlcm5hdGl2YQ0KDQojIFRhbWHDsW8gZGUgZWZlY3RvIGRlIENvaGVuIChoKSBwYXJhIHByb3BvcmNpb25lcw0KaCA8LSBFUy5oKHAxID0gcDEsIHAyID0gcDApDQoNCiMgQ8OhbGN1bG8gZGVsIHRhbWHDsW8gZGUgbXVlc3RyYSBjb24gcHdyLnAudGVzdA0KcmVzX3B3ciA8LSBwd3IucC50ZXN0KGggPSBoLA0KICAgICAgICAgICAgICAgICAgICAgIHNpZy5sZXZlbCA9IDAuMDUsDQogICAgICAgICAgICAgICAgICAgICAgcG93ZXIgPSAwLjgwLA0KICAgICAgICAgICAgICAgICAgICAgIGFsdGVybmF0aXZlID0gInR3by5zaWRlZCIpDQoNCm5fcHdyIDwtIGNlaWxpbmcocmVzX3B3ciRuKQ0KDQpjYXQoIlRhbWHDsW8gZGUgbXVlc3RyYSBuZWNlc2FyaW8gKHB3ci5wLnRlc3QpOiIsDQogICAgbl9wd3IsICJcbiIpDQpgYGANCg0KVGFtYcOxbyBkZSBtdWVzdHJhIGVuIHBvYmxhY2lvbmVzIGZpbml0YXMNCkN1YW5kbyBzZSBjb25vY2UgZWwgdGFtYcOxbyB0b3RhbCBkZSBsYSBwb2JsYWNpw7NuLCBzZSBhanVzdGEgZWwgdGFtYcOxbyBkZSBtdWVzdHJhIG1lZGlhbnRlIGxhIGbDs3JtdWxhIHBhcmEgcG9ibGFjaW9uZXMgZmluaXRhczoNCg0KDQoNCkbDs3JtdWxhIHBvYmxhY2nDs24gZmluaXRhDQpEb25kZToNCg0KTjogdGFtYcOxbyBkZSBsYSBwb2JsYWNpw7NuLg0KWjogdmFsb3IgY29ycmVzcG9uZGllbnRlIGFsIG5pdmVsIGRlIGNvbmZpYW56YS4NCnA6IHByb3BvcmNpw7NuIGVzcGVyYWRhLg0KcSA9IDEg4oiSIHAuDQpkOiBwcmVjaXNpw7NuIGRlc2VhZGEuDQpFbiBlc3RlIGVqZXJjaWNpbywgY2FsY3VsYW1vcyBlbCB0YW1hw7FvIGRlIG11ZXN0cmEgcGFyYToNCg0KTiA9IDEwLDAwMA0KTml2ZWwgZGUgY29uZmlhbnphOiA5NSAlIChaID0gMS45NikNClByb3BvcmNpw7NuIGVzcGVyYWRhOiBwID0gMC4wNSAoNSAlKQ0KUHJlY2lzacOzbiBkZXNlYWRhOiBkID0gMC4wMyAoMyAlKQ0KDQoNCmBgYHtyfQ0KIyBQYXLDoW1ldHJvcw0KTiA8LSAxMDAwMCAgICMgVGFtYcOxbyBkZSBsYSBwb2JsYWNpw7NuDQpaIDwtIDEuOTYgICAgIyBOaXZlbCBkZSBjb25maWFuemEgcGFyYSA5NSAlDQpwIDwtIDAuMDUgICAgIyBQcm9wb3JjacOzbiBlc3BlcmFkYSAoNSAlKQ0KcSA8LSAxIC0gcCAgICMgQ29tcGxlbWVudG8gZGUgbGEgcHJvcG9yY2nDs24NCmQgPC0gMC4wMyAgICAjIFByZWNpc2nDs24gZGVzZWFkYSAoMyAlKQ0KDQojIEPDoWxjdWxvIGRlbCB0YW1hw7FvIGRlIG11ZXN0cmEgcGFyYSBwb2JsYWNpw7NuIGZpbml0YQ0Kbl9maW5pdGEgPC0gKE4gKiBaXjIgKiBwICogcSkgLw0KICAoKGReMiAqIChOIC0gMSkpICsgKFpeMiAqIHAgKiBxKSkNCg0KY2F0KCJUYW1hw7FvIGRlIG11ZXN0cmEgbmVjZXNhcmlvIChOID0gMTAsMDAwKToiLA0KICAgIGNlaWxpbmcobl9maW5pdGEpLCAiXG4iKQ0KYGBgDQpQYXNvIDI6IFZhcmlhY2nDs24gZGVsIHRhbWHDsW8gZGUgbXVlc3RyYSBzZWfDum4gbGEgcHJlY2lzacOzbg0KQ3JlYW1vcyB1biBncsOhZmljbyBxdWUgbXVlc3RyZSBjw7NtbyBjYW1iaWEgZWwgdGFtYcOxbyBkZSBtdWVzdHJhIGNvbiBkaXN0aW50b3MgdmFsb3JlcyBkZSBkOg0KDQpgYGB7cn0NCmxpYnJhcnkoZ2dwbG90MikNCg0KIyBTZWN1ZW5jaWEgZGUgcHJlY2lzaW9uZXMgcG9zaWJsZXMNCnByZWNpc2lvbmVzIDwtIHNlcSgwLjAxLCAwLjEwLCBieSA9IDAuMDEpDQoNCnRhbV9tdWVzdHJhIDwtIHNhcHBseShwcmVjaXNpb25lcywgZnVuY3Rpb24oZCl7DQogIChOICogWl4yICogcCAqIHEpIC8NCiAgICAoKGReMiAqIChOIC0gMSkpICsgKFpeMiAqIHAgKiBxKSkNCn0pDQoNCiMgRGF0YSBmcmFtZSBwYXJhIGdyYWZpY2FyDQpkYXRvc19ncmFmaWNvIDwtIGRhdGEuZnJhbWUoDQogIHByZWNpc2lvbiAgICAgID0gcHJlY2lzaW9uZXMsDQogIHRhbWFub19tdWVzdHJhID0gY2VpbGluZyh0YW1fbXVlc3RyYSkNCikNCg0KZ2dwbG90KGRhdG9zX2dyYWZpY28sDQogICAgICAgYWVzKHggPSBwcmVjaXNpb24sIHkgPSB0YW1hbm9fbXVlc3RyYSkpICsNCiAgZ2VvbV9saW5lKCkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIlJlbGFjacOzbiBlbnRyZSBwcmVjaXNpw7NuIChkKSB5IHRhbWHDsW8gZGUgbXVlc3RyYVxuKE4gPSAxMCwwMDApIiwNCiAgICB4ICAgICA9ICJQcmVjaXNpw7NuIGRlc2VhZGEgKGQpIiwNCiAgICB5ICAgICA9ICJUYW1hw7FvIGRlIG11ZXN0cmEiDQogICkgKw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KDQpFamVtcGxvIGFkaWNpb25hbDogcG9ibGFjacOzbiBkZSAxNSwwMDAgaGFiaXRhbnRlcw0KQWhvcmEgY2FsY3VsYW1vcyBlbCB0YW1hw7FvIGRlIG11ZXN0cmEgcGFyYSBlc3RpbWFyIGxhIHByZXZhbGVuY2lhIGRlIGRpYWJldGVzIGVuIHVuYSBwb2JsYWNpw7NuIGZpbml0YSBkZSAxNSwwMDAgaGFiaXRhbnRlcywgY29uOg0KDQpTZWd1cmlkYWQ6IDk1ICUNClByZWNpc2nDs246IDMgJSAoZCA9IDAuMDMpDQpQcm9wb3JjacOzbiBlc3BlcmFkYTogcCA9IDAuMDUgKDUgJSkNCg0KYGBge3J9DQojIFBhcsOhbWV0cm9zDQpOIDwtIDE1MDAwICAgIyBUYW1hw7FvIGRlIGxhIHBvYmxhY2nDs24NClogPC0gMS45NiAgICAjIE5pdmVsIGRlIGNvbmZpYW56YSBwYXJhIDk1ICUNCnAgPC0gMC4wNSAgICAjIFByb3BvcmNpw7NuIGVzcGVyYWRhICg1ICUpDQpxIDwtIDEgLSBwDQpkIDwtIDAuMDMgICAgIyBQcmVjaXNpw7NuIGRlc2VhZGEgKDMgJSkNCg0KIyBDw6FsY3VsbyBwYXJhIHBvYmxhY2nDs24gZmluaXRhDQpuX2Zpbml0YV8xNTAwMCA8LSAoTiAqIFpeMiAqIHAgKiBxKSAvDQogICgoZF4yICogKE4gLSAxKSkgKyAoWl4yICogcCAqIHEpKQ0KDQpjYXQoIlRhbWHDsW8gZGUgbXVlc3RyYSBuZWNlc2FyaW8gKE4gPSAxNSwwMDApOiIsDQogICAgY2VpbGluZyhuX2Zpbml0YV8xNTAwMCksICJcbiIpDQpgYGANCkV4cGxvcmFjacOzbiBncsOhZmljYSAoTiA9IDE1LDAwMCkNCg0KYGBge3J9DQpwcmVjaXNpb25lcyA8LSBzZXEoMC4wMSwgMC4xMCwgYnkgPSAwLjAxKQ0KDQp0YW1fbXVlc3RyYSA8LSBzYXBwbHkocHJlY2lzaW9uZXMsIGZ1bmN0aW9uKGQpew0KICAoTiAqIFpeMiAqIHAgKiBxKSAvDQogICAgKChkXjIgKiAoTiAtIDEpKSArIChaXjIgKiBwICogcSkpDQp9KQ0KDQpkYXRvc19ncmFmaWNvIDwtIGRhdGEuZnJhbWUoDQogIHByZWNpc2lvbiAgICAgID0gcHJlY2lzaW9uZXMsDQogIHRhbWFub19tdWVzdHJhID0gY2VpbGluZyh0YW1fbXVlc3RyYSkNCikNCg0KZ2dwbG90KGRhdG9zX2dyYWZpY28sDQogICAgICAgYWVzKHggPSBwcmVjaXNpb24sIHkgPSB0YW1hbm9fbXVlc3RyYSkpICsNCiAgZ2VvbV9saW5lKCkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIlJlbGFjacOzbiBlbnRyZSBwcmVjaXNpw7NuIChkKSB5IHRhbWHDsW8gZGUgbXVlc3RyYVxuKE4gPSAxNSwwMDApIiwNCiAgICB4ICAgICA9ICJQcmVjaXNpw7NuIGRlc2VhZGEgKGQpIiwNCiAgICB5ICAgICA9ICJUYW1hw7FvIGRlIG11ZXN0cmEiDQogICkgKw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KDQoNClRhbWHDsW8gZGUgbXVlc3RyYSBwYXJhIGVzdGltYXIgdW5hIG1lZGlhDQoNCmBgYHtyfQ0KIyBQYXLDoW1ldHJvcw0KWiAgPC0gMS45NiAgICMgTml2ZWwgZGUgY29uZmlhbnphICg5NSAlKQ0KUzIgPC0gMjUwICAgICMgVmFyaWFuemEgKFNeMikNCmQgIDwtIDMgICAgICAjIFByZWNpc2nDs24gZGVzZWFkYQ0KDQojIEPDoWxjdWxvIGRlbCB0YW1hw7FvIGRlIG11ZXN0cmENCm5fbWVkaWFfbWFudWFsIDwtIChaXjIgKiBTMikgLyAoZF4yKQ0KDQpjYXQoIlRhbWHDsW8gZGUgbXVlc3RyYSAobWVkaWEsIG3DqXRvZG8gbWFudWFsKToiLA0KICAgIGNlaWxpbmcobl9tZWRpYV9tYW51YWwpLCAiXG4iKQ0KYGBgDQoNClRhbWHDsW8gZGUgbXVlc3RyYSBwYXJhIG1lZGlhIGNvbg0KDQpgYGB7cn0NCiMgUGFyw6FtZXRyb3MNClogIDwtIDEuOTYgICAjIE5pdmVsIGRlIGNvbmZpYW56YSAoOTUgJSkNClMyIDwtIDI1MCAgICAjIFZhcmlhbnphIChTXjIpDQpkICA8LSAzICAgICAgIyBQcmVjaXNpw7NuIGRlc2VhZGENCg0KIyBDw6FsY3VsbyBkZWwgdGFtYcOxbyBkZSBtdWVzdHJhDQpuX21hbnVhbCA8LSAoWl4yICogUzIpIC8gKGReMikNCg0KY2F0KCJUYW1hw7FvIGRlIG11ZXN0cmEgKG1lZGlhLCBtw6l0b2RvIG1hbnVhbCk6IiwNCiAgICBjZWlsaW5nKG5fbWFudWFsKSwgIlxuIikNCmBgYA0KDQpDb21wYXJhY2nDs24gZGUgZG9zIHByb3BvcmNpb25lcw0KDQpgYGB7cn0NCiMgUGFyw6FtZXRyb3MNClpfYWxwaGEgPC0gMS42NDUgICMgTml2ZWwgZGUgY29uZmlhbnphIH45NSAlICh1bmEgY29sYSkNClpfYmV0YSAgPC0gMC44NDIgICMgUG9kZXIgZXN0YWTDrXN0aWNvIDgwICUNCnAxIDwtIDAuNyAgICAgICAgICMgUHJvcG9yY2nDs24gMQ0KcDIgPC0gMC45ICAgICAgICAgIyBQcm9wb3JjacOzbiAyDQpwICA8LSAocDEgKyBwMikgLyAyICAjIFByb21lZGlvIGRlIHByb3BvcmNpb25lcw0KDQojIEPDoWxjdWxvIGRlbCB0YW1hw7FvIGRlIG11ZXN0cmEgKHBvciBncnVwbykNCm5fZG9zX3Byb3BfbWFudWFsIDwtICgNCiAgKFpfYWxwaGEgKiBzcXJ0KDIgKiBwICogKDEgLSBwKSkgKw0KICAgICBaX2JldGEgICogc3FydChwMSAqICgxIC0gcDEpICsgcDIgKiAoMSAtIHAyKSkpXjINCikgLyAocDEgLSBwMileMg0KDQpjYXQoIlRhbWHDsW8gZGUgbXVlc3RyYSBwb3IgZ3J1cG8gKGRvcyBwcm9wb3JjaW9uZXMsIG1hbnVhbCk6IiwNCiAgICBjZWlsaW5nKG5fZG9zX3Byb3BfbWFudWFsKSwgIlxuIikNCmBgYA0KYGBge3J9DQojIEPDoWxjdWxvIGRlbCB0YW1hw7FvIGRlbCBlZmVjdG8gaCBwYXJhIGRvcyBwcm9wb3JjaW9uZXMNCmggPC0gRVMuaChwMSA9IHAxLCBwMiA9IHAyKQ0KDQojIFRhbWHDsW8gZGUgbXVlc3RyYSBwb3IgZ3J1cG8gY29uIHB3ci4ycC50ZXN0DQpyZXNfMnAgPC0gcHdyLjJwLnRlc3QoDQogIGggICAgICAgICA9IGgsDQogIHNpZy5sZXZlbCA9IDAuMDUsDQogIHBvd2VyICAgICA9IDAuODAsDQogIGFsdGVybmF0aXZlID0gInR3by5zaWRlZCINCikNCg0KY2F0KCJUYW1hw7FvIGRlIG11ZXN0cmEgcG9yIGdydXBvIChkb3MgcHJvcG9yY2lvbmVzLCBwd3IpOiIsDQogICAgY2VpbGluZyhyZXNfMnAkbiksICJcbiIpDQpgYGANCg0KQ29tcGFyYWNpw7NuIGRlIGRvcyBtZWRpYXM6DQoNCmBgYHtyfQ0KIyBQYXLDoW1ldHJvcw0KWl9hbHBoYSA8LSAxLjY0NSAgIyBOaXZlbCBkZSBjb25maWFuemEgfjk1ICUgKHVuYSBjb2xhKQ0KWl9iZXRhICA8LSAxLjI4MiAgIyBQb2RlciBlc3RhZMOtc3RpY28gOTAgJQ0KUyAgICAgICA8LSAxNiAgICAgIyBEZXN2aWFjacOzbiBlc3TDoW5kYXINCmRfbWVhbiAgPC0gMTUgICAgICMgRGlmZXJlbmNpYSBlc3BlcmFkYSBlbnRyZSBtZWRpYXMNCg0KIyBDw6FsY3VsbyBkZWwgdGFtYcOxbyBkZSBtdWVzdHJhIHBvciBncnVwbw0Kbl9kb3NfbWVkaWFzIDwtICgyICogKFpfYWxwaGEgKyBaX2JldGEpXjIgKiBTXjIpIC8gKGRfbWVhbl4yKQ0KDQpjYXQoIlRhbWHDsW8gZGUgbXVlc3RyYSBwb3IgZ3J1cG8gKGRvcyBtZWRpYXMsIG1hbnVhbCk6IiwNCiAgICBjZWlsaW5nKG5fZG9zX21lZGlhcyksICJcbiIpDQpgYGANCg0K