install.packages('tidyverse')
Installing package into ‘/cloud/lib/x86_64-pc-linux-gnu-library/4.3’
(as ‘lib’ is unspecified)
trying URL 'http://rspm/default/__linux__/focal/latest/src/contrib/tidyverse_2.0.0.tar.gz'
Content type 'application/x-gzip' length 425237 bytes (415 KB)
==================================================
downloaded 415 KB
* installing *binary* package ‘tidyverse’ ...
* DONE (tidyverse)
The downloaded source packages are in
‘/tmp/Rtmp3bw7Hk/downloaded_packages’
install.packages("rstatix")
Installing package into ‘/cloud/lib/x86_64-pc-linux-gnu-library/4.3’
(as ‘lib’ is unspecified)
trying URL 'http://rspm/default/__linux__/focal/latest/src/contrib/rstatix_0.7.2.tar.gz'
Content type 'application/x-gzip' length 604777 bytes (590 KB)
==================================================
downloaded 590 KB
* installing *binary* package ‘rstatix’ ...
* DONE (rstatix)
The downloaded source packages are in
‘/tmp/Rtmp3bw7Hk/downloaded_packages’
library(tidyverse)
── Attaching core tidyverse packages ────────────────────────────────────── tidyverse 2.0.0 ──
✔ dplyr 1.1.4 ✔ readr 2.1.4
✔ forcats 1.0.0 ✔ stringr 1.5.1
✔ ggplot2 3.4.4 ✔ tibble 3.2.1
✔ lubridate 1.9.3 ✔ tidyr 1.3.0
✔ purrr 1.0.2 ── Conflicts ──────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag() masks stats::lag()
ℹ Use the ]8;;http://conflicted.r-lib.org/conflicted package]8;; to force all conflicts to become errors
library(rstatix)
Attaching package: ‘rstatix’
The following object is masked from ‘package:stats’:
filter
Introduccion a R
1. Vectores
Primero agregamos valores de edades
edad = c(23,26,27,28,32,34,28,29,31,25)
Luego agregamos nombres
nombres = c("Maria", "Gustavo", "Ramon", "Silvia", "Alma", "Eduardo", "Enrique", "Erick", "Hugo", "Jesus")
Para revisarlos
nombres
[1] "Maria" "Gustavo" "Ramon" "Silvia" "Alma" "Eduardo" "Enrique" "Erick" "Hugo"
[10] "Jesus"
Generación de vectores aleatorios. Genera 10 datos aleatorios en un
rango entre 8,000 a 30,000.
salario = sample(8000:30000,size = 10, replace = FALSE)
salario
[1] 17998 9212 11821 22614 29972 14395 22702 14514 14754 21666
Nota: Si replace = FALSE, entonces no hay reemplazo.
Ahora agregamos 10 datos aleatorios referentes a sexo
sexo = sample(c("H", "M"), size = 10, replace = TRUE)
sexo
[1] "M" "H" "M" "H" "H" "H" "H" "M" "H" "M"
Tenemos diferentes variables - Cualitativas - Nominales - Ordinales -
Cuantitativas - Discretas - Continuas
2. Bases de datos (data frames)
Tenemos bases de datos - Externas - Internas
Este sería un ejemplo de bases de datos internas de R
ejemplo_1 <- datasets::state.x77
head(ejemplo_1)
Population Income Illiteracy Life Exp Murder HS Grad Frost Area
Alabama 3615 3624 2.1 69.05 15.1 41.3 20 50708
Alaska 365 6315 1.5 69.31 11.3 66.7 152 566432
Arizona 2212 4530 1.8 70.55 7.8 58.1 15 113417
Arkansas 2110 3378 1.9 70.66 10.1 39.9 65 51945
California 21198 5114 1.1 71.71 10.3 62.6 20 156361
Colorado 2541 4884 0.7 72.06 6.8 63.9 166 103766
La forma de leer una base de datos externa, archivo separado por
comas .csv, es con la función
read.csv(directorio.csv)
datos1 <- read.csv("/cloud/project/Bioestadistica_R/datos_1.csv")
head(datos1) #Con la función head() se observan los primeros 6 valores del data frame
str(datos1)
'data.frame': 200 obs. of 11 variables:
$ Clave : chr "A001" "A002" "A003" "A004" ...
$ Sexo : chr "M" "H" "M" "H" ...
$ Edad : int 21 35 38 18 32 21 39 23 37 35 ...
$ Estatus : chr "Control" "Caso" "Control" "Control" ...
$ Exposicion : chr "Sin exposicion" "Humo de biomasa" "Fumador" "Fumador" ...
$ Enfermedad : chr "Sin alteraciones" "Capacidad pulmonar alterada" "Sin alteraciones" "Sin alteraciones" ...
$ Cotinina : num 1 8 183.2 225.5 2.3 ...
$ Anos.de.exposicion: int 0 5 20 4 0 3 15 5 20 32 ...
$ Talla : num 1.65 1.56 1.66 1.77 1.5 1.78 1.8 1.55 1.68 1.7 ...
$ Peso : int 88 55 68 104 56 115 98 74 99 65 ...
$ Padres.fumadores : chr "No" "No" "Si" "No" ...
Para cargar la segunda base de datos
datos2 <- read_csv("/cloud/project/Bioestadistica_R/datos_2.csv") #Aquí usamos la función read_csv del paquete tidyverse
Rows: 200 Columns: 6── Column specification ──────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (1): Clave
dbl (5): Cigarros al dia, Cartuchos al dia, TNFA, CRP, PvCO2
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
head(datos2)
Creamos una base de datos a partir de los vectores realizados
previamente
datos3 <- data.frame(nombres, edad, sexo, salario)
as_tibble(datos3)
Para cambiar el nombre de una variable utilizamos
rename()
rename(datos3, nombre = nombres) # primero ponemos el (dataframe, nombre nuevo = nombre viejo)
Recordar que ejercicio2 = datos1 y ejercicio3=datos2
Revisar algunas funciones de tidyverse Función
select
ejercicio5 <- datos1 %>%
select(Clave, Edad, Cotinina, Anos.de.exposicion, Talla, Peso)
ejercicio5
Función filter
ejercicio6 <- datos1 %>%
filter(Edad>=25)
ejercicio6
Función filter con variable cualitativa
ejercicio6 <- datos1 %>%
filter(Exposicion=="Fumador"|Exposicion=="Vapeador"|Exposicion=="Sin exposicion")
ejercicio6
Ver la función mutate para crear variables nuevas a
partir de variables existentes
ejercicio2 <- datos1 %>%
mutate(IMC = Peso / Talla^2)
ejercicio2
Podemos cambiar el tipo de variable de cuantitativa a cualitativa con
la función mutate
ejercicio2 <- ejercicio2 %>%
mutate(IMC_escala = case_when(IMC<18 ~ "Desnutricion",
IMC<25 ~ "Normopeso",
IMC<30 ~ "Sobrepeso",
IMC>=30 ~ "Obesidad"))
ejercicio2
Ahora veremos las rutas de análisis
- Depuración de bases de datos
- Datos perdidos(
na.omit)
- Filtrar datos (
filter)
- Seleccionar las variables de uso (
select)
- Conversiones / Correcciones (
mutate)
- Analizar la estructura de los datos (
str)
str(ejercicio2)
'data.frame': 200 obs. of 13 variables:
$ Clave : chr "A001" "A002" "A003" "A004" ...
$ Sexo : chr "M" "H" "M" "H" ...
$ Edad : int 21 35 38 18 32 21 39 23 37 35 ...
$ Estatus : chr "Control" "Caso" "Control" "Control" ...
$ Exposicion : chr "Sin exposicion" "Humo de biomasa" "Fumador" "Fumador" ...
$ Enfermedad : chr "Sin alteraciones" "Capacidad pulmonar alterada" "Sin alteraciones" "Sin alteraciones" ...
$ Cotinina : num 1 8 183.2 225.5 2.3 ...
$ Anos.de.exposicion: int 0 5 20 4 0 3 15 5 20 32 ...
$ Talla : num 1.65 1.56 1.66 1.77 1.5 1.78 1.8 1.55 1.68 1.7 ...
$ Peso : int 88 55 68 104 56 115 98 74 99 65 ...
$ Padres.fumadores : chr "No" "No" "Si" "No" ...
$ IMC : num 32.3 22.6 24.7 33.2 24.9 ...
$ IMC_escala : chr "Obesidad" "Normopeso" "Normopeso" "Obesidad" ...
La datos que salen es el análisis de estructura
También podemos cambiar el tipo de variable. Por ejemplo, la variable
Sexo está categorizada como chr (character),
pero la cambiamos a factor. Conviene cambiarlas para
realizar adecuadamente el análisis estadístico, ya que las variables
tipo chr no son útiles para ese análisis.
ejercicio2$Sexo <- as.factor(ejercicio2$Sexo)
ejercicio2$Estatus <- as.factor(ejercicio2$Estatus)
ejercicio2$Exposicion <- as.factor(ejercicio2$Exposicion)
ejercicio2$Enfermedad <- as.factor(ejercicio2$Enfermedad)
ejercicio2$Padres.fumadores <- as.factor(ejercicio2$Padres.fumadores)
ejercicio2$IMC_escala <- as.factor(ejercicio2$IMC_escala)
str(ejercicio2)
'data.frame': 200 obs. of 13 variables:
$ Clave : chr "A001" "A002" "A003" "A004" ...
$ Sexo : Factor w/ 2 levels "H","M": 2 1 2 1 2 1 1 2 1 2 ...
$ Edad : int 21 35 38 18 32 21 39 23 37 35 ...
$ Estatus : Factor w/ 2 levels "Caso","Control": 2 1 2 2 1 2 2 1 1 2 ...
$ Exposicion : Factor w/ 4 levels "Fumador","Humo de biomasa",..: 3 2 1 1 3 1 1 4 1 2 ...
$ Enfermedad : Factor w/ 5 levels "Asma","Bronquitis",..: 5 3 5 5 2 5 5 2 2 5 ...
$ Cotinina : num 1 8 183.2 225.5 2.3 ...
$ Anos.de.exposicion: int 0 5 20 4 0 3 15 5 20 32 ...
$ Talla : num 1.65 1.56 1.66 1.77 1.5 1.78 1.8 1.55 1.68 1.7 ...
$ Peso : int 88 55 68 104 56 115 98 74 99 65 ...
$ Padres.fumadores : Factor w/ 2 levels "No","Si": 1 1 2 1 2 2 2 1 1 2 ...
$ IMC : num 32.3 22.6 24.7 33.2 24.9 ...
$ IMC_escala : Factor w/ 4 levels "Desnutricion",..: 3 2 2 3 2 3 3 3 3 2 ...
Procedemos a hacer un análisis estadístico
resultados <- ejercicio2 %>%
get_summary_stats() #Solo es con el paquete rstatix
resultados
Para hacer un análisis estratificado se requiere una variable
cualitativa como factor
resultados <- ejercicio2 %>%
group_by(Enfermedad) %>%
get_summary_stats() #Solo es con el paquete rstatix
resultados
Para realizar la tarea abrimos el archivo de datos de framingham,
luego seleccionamos las variables de las cuales queremos obtener las
medidas de tendencia central y las medidas de dispersión utilizando el
paquete rstatix
datos_framingham <- read_csv("/cloud/project/Bioestadistica_R/framingham.csv")
Rows: 4238 Columns: 16── Column specification ──────────────────────────────────────────────────────────────────────
Delimiter: ","
dbl (16): male, age, education, currentSmoker, cigsPerDay, BPMeds, prevalentStroke, preval...
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
View(datos_framingham)
str(datos_framingham)
spc_tbl_ [4,238 × 16] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
$ male : num [1:4238] 1 0 1 0 0 0 0 0 1 1 ...
$ age : num [1:4238] 39 46 48 61 46 43 63 45 52 43 ...
$ education : num [1:4238] 4 2 1 3 3 2 1 2 1 1 ...
$ currentSmoker : num [1:4238] 0 0 1 1 1 0 0 1 0 1 ...
$ cigsPerDay : num [1:4238] 0 0 20 30 23 0 0 20 0 30 ...
$ BPMeds : num [1:4238] 0 0 0 0 0 0 0 0 0 0 ...
$ prevalentStroke: num [1:4238] 0 0 0 0 0 0 0 0 0 0 ...
$ prevalentHyp : num [1:4238] 0 0 0 1 0 1 0 0 1 1 ...
$ diabetes : num [1:4238] 0 0 0 0 0 0 0 0 0 0 ...
$ totChol : num [1:4238] 195 250 245 225 285 228 205 313 260 225 ...
$ sysBP : num [1:4238] 106 121 128 150 130 ...
$ diaBP : num [1:4238] 70 81 80 95 84 110 71 71 89 107 ...
$ BMI : num [1:4238] 27 28.7 25.3 28.6 23.1 ...
$ heartRate : num [1:4238] 80 95 75 65 85 77 60 79 76 93 ...
$ glucose : num [1:4238] 77 76 70 103 85 99 85 78 79 88 ...
$ TenYearCHD : num [1:4238] 0 0 0 1 0 0 1 0 0 0 ...
- attr(*, "spec")=
.. cols(
.. male = col_double(),
.. age = col_double(),
.. education = col_double(),
.. currentSmoker = col_double(),
.. cigsPerDay = col_double(),
.. BPMeds = col_double(),
.. prevalentStroke = col_double(),
.. prevalentHyp = col_double(),
.. diabetes = col_double(),
.. totChol = col_double(),
.. sysBP = col_double(),
.. diaBP = col_double(),
.. BMI = col_double(),
.. heartRate = col_double(),
.. glucose = col_double(),
.. TenYearCHD = col_double()
.. )
- attr(*, "problems")=<externalptr>
datos_framingham <- rename(datos_framingham, sexos = male)
datos_framingham
resultados_framingham <- datos_framingham %>%
select(age, sexos, cigsPerDay, totChol, sysBP, diaBP, BMI, heartRate, glucose) %>%
filter(!complete.cases(.)) %>%
group_by(sexos) %>%
get_summary_stats()
View(resultados_framingham)
Ahora obtenemos el rango de manera manual calculando la diferencia
entre el máximo y el mínimo.
rango_age <- max(datos_framingham$age, na.rm = TRUE) - min(datos_framingham$age, na.rm = TRUE)
rango_cigs <- max(datos_framingham$cigsPerDay, na.rm = TRUE) - min(datos_framingham$cigsPerDay, na.rm = TRUE)
rango_chol <- max(datos_framingham$totChol, na.rm = TRUE) - min(datos_framingham$totChol, na.rm = TRUE)
rango_sbp <- max(datos_framingham$sysBP, na.rm = TRUE) - min(datos_framingham$sysBP, na.rm = TRUE)
rango_dbp <- max(datos_framingham$diaBP, na.rm = TRUE) - min(datos_framingham$diaBP, na.rm = TRUE)
rango_BMI <- max(datos_framingham$BMI, na.rm = TRUE) - min(datos_framingham$BMI, na.rm = TRUE)
rango_hr <- max(datos_framingham$heartRate, na.rm = TRUE) - min(datos_framingham$heartRate, na.rm = TRUE)
rango_gluc <- max(datos_framingham$glucose, na.rm = TRUE) - min(datos_framingham$glucose, na.rm = TRUE)
rango_age
[1] 38
rango_cigs
[1] 70
rango_chol
[1] 589
rango_sbp
[1] 211.5
3. Anális descriptivo y normalidad (Sesión 3), con enfoque en el
análisis de normalidad
Tener precaución para utilizar read.csvo
read_csvporque el manejo de las tablas es diferente
library(tidyverse)
tarea_1 <- read.csv("/cloud/project/Bioestadistica_R/framingham.csv")
str(tarea_1)
'data.frame': 4238 obs. of 16 variables:
$ male : int 1 0 1 0 0 0 0 0 1 1 ...
$ age : int 39 46 48 61 46 43 63 45 52 43 ...
$ education : int 4 2 1 3 3 2 1 2 1 1 ...
$ currentSmoker : int 0 0 1 1 1 0 0 1 0 1 ...
$ cigsPerDay : int 0 0 20 30 23 0 0 20 0 30 ...
$ BPMeds : int 0 0 0 0 0 0 0 0 0 0 ...
$ prevalentStroke: int 0 0 0 0 0 0 0 0 0 0 ...
$ prevalentHyp : int 0 0 0 1 0 1 0 0 1 1 ...
$ diabetes : int 0 0 0 0 0 0 0 0 0 0 ...
$ totChol : int 195 250 245 225 285 228 205 313 260 225 ...
$ sysBP : num 106 121 128 150 130 ...
$ diaBP : num 70 81 80 95 84 110 71 71 89 107 ...
$ BMI : num 27 28.7 25.3 28.6 23.1 ...
$ heartRate : int 80 95 75 65 85 77 60 79 76 93 ...
$ glucose : int 77 76 70 103 85 99 85 78 79 88 ...
$ TenYearCHD : int 0 0 0 1 0 0 1 0 0 0 ...
Cambiamos el tipo de variable malecomo factor
tarea_1$male <- as.factor(tarea_1$male)
str(tarea_1)
'data.frame': 4238 obs. of 16 variables:
$ male : Factor w/ 2 levels "0","1": 2 1 2 1 1 1 1 1 2 2 ...
$ age : int 39 46 48 61 46 43 63 45 52 43 ...
$ education : int 4 2 1 3 3 2 1 2 1 1 ...
$ currentSmoker : int 0 0 1 1 1 0 0 1 0 1 ...
$ cigsPerDay : int 0 0 20 30 23 0 0 20 0 30 ...
$ BPMeds : int 0 0 0 0 0 0 0 0 0 0 ...
$ prevalentStroke: int 0 0 0 0 0 0 0 0 0 0 ...
$ prevalentHyp : int 0 0 0 1 0 1 0 0 1 1 ...
$ diabetes : int 0 0 0 0 0 0 0 0 0 0 ...
$ totChol : int 195 250 245 225 285 228 205 313 260 225 ...
$ sysBP : num 106 121 128 150 130 ...
$ diaBP : num 70 81 80 95 84 110 71 71 89 107 ...
$ BMI : num 27 28.7 25.3 28.6 23.1 ...
$ heartRate : int 80 95 75 65 85 77 60 79 76 93 ...
$ glucose : int 77 76 70 103 85 99 85 78 79 88 ...
$ TenYearCHD : int 0 0 0 1 0 0 1 0 0 0 ...
tarea_1 <- tarea_1 %>%
mutate(male = case_when(male == "0" ~ "M",
male == "1" ~ "H"))
str(tarea_1)
'data.frame': 4238 obs. of 16 variables:
$ male : chr "H" "M" "H" "M" ...
$ age : int 39 46 48 61 46 43 63 45 52 43 ...
$ education : int 4 2 1 3 3 2 1 2 1 1 ...
$ currentSmoker : int 0 0 1 1 1 0 0 1 0 1 ...
$ cigsPerDay : int 0 0 20 30 23 0 0 20 0 30 ...
$ BPMeds : int 0 0 0 0 0 0 0 0 0 0 ...
$ prevalentStroke: int 0 0 0 0 0 0 0 0 0 0 ...
$ prevalentHyp : int 0 0 0 1 0 1 0 0 1 1 ...
$ diabetes : int 0 0 0 0 0 0 0 0 0 0 ...
$ totChol : int 195 250 245 225 285 228 205 313 260 225 ...
$ sysBP : num 106 121 128 150 130 ...
$ diaBP : num 70 81 80 95 84 110 71 71 89 107 ...
$ BMI : num 27 28.7 25.3 28.6 23.1 ...
$ heartRate : int 80 95 75 65 85 77 60 79 76 93 ...
$ glucose : int 77 76 70 103 85 99 85 78 79 88 ...
$ TenYearCHD : int 0 0 0 1 0 0 1 0 0 0 ...
View(tarea_1)
Realizamos el análisis descriptivo
resultados_tarea_1 <- tarea_1 %>%
group_by(male) %>%
get_summary_stats(age, cigsPerDay, totChol, sysBP, diaBP, BMI, heartRate, glucose)
resultados_tarea_1 <- resultados_tarea_1 %>%
mutate(Rango_1 = max - min)
View(resultados_tarea_1)
Para crear un archivo .csv
write.csv(resultados_tarea_1, file = "resultados_tarea1")
Temas de estadística
Definir población y muestra. La
muestra es el conjunto de elementos representativos de la
población y se debe seleccionar aleatoriamente.
Escalas de medición: 4 niveles de escala
Nominal: Se utilzan nombres, símbolos o numerales, que son
empleados como etiquetas. Sirve para separar por grupos. No se pueden
aplicar operaciones matemáticas.
Ordinal: Los números o etiquetas utilizadas indican un orden.
Sirve para organizar.
Intervalos: Establece un orden determinado. El 0 significa
ausencia de valor. Se puede utilizar para operaciones
matemáticas.
Proporción/cociente/razón: Son secuencias numéricas. Se pueden
realizar operaciones matemáticas. Es la escala que permite comparar de
mejor manera los datos.
Distribución de datos
- Distribución normal
- Distribución de Poisson
- Distribución binomial
Asimetría de datos
- Negativa
- Simétrica
- Positiva
Curtosis
- Leptocúrtica
- Mesocúrtica
- Platicúrtica
4. La normalidad…
Puede ser evaluada mediante 3 modelos
- Modelos gráficos: histogramas y qqplot
- Media = Mediana = Moda
- Pruebas de normalidad:
- Shapiro-Wild: para grupos con menos de 50
observaciones
- Kolmogorov-Smirnov/Lillifors: para grupos con más de 50
observaciones
Representación de los datos a partir de las medidas de tendencia
central
Utilizamos el valor de la edad
ejercicio_1 <- tarea_1 %>%
get_summary_stats(age)
conteo <- tarea_1 %>%
count(age)
#mean: 49.58
#median: 49
#moda: 40
Solo con estos parámetros se podría decir que la muestra no cumple
con la normalidad. Veamos su historgrama con la función
hist(), del cual podemos modificar su título, las etiquetas
y el color.
hist(tarea_1$age,
main = "Histograma de edad",
ylab = "Frecuencia",
xlab = "Edad (años)",
col = "salmon",
breaks = 50)

Podemos realizar las gráficas con el paquete ggplot que
permite crear gráficos más estéticos
library(ggplot)
Error in library(ggplot) : there is no package called ‘ggplot’
Para graficar el histograma

Construyamos los gráficos de densidad

Revisar la asimetría y la curtosis
library(car)
Loading required package: carData
Attaching package: ‘car’
The following object is masked from ‘package:psych’:
logit
La curtosis ideal debería ser cercano a 0 para decir que es
mesocúrtica. Es decir,
- k = 0 -> mesocúrtica
- k > 0 -> leptocúrtica
- k < 0 -> platicúrtica
Para calcular la asimetría, decimos que
- a = 0 (simétrico)
- a > 0 (asimetría positiva)
- a < 0 (asimetría negativa)
Para calcular la curtosis utilizamos el paquete
psych
kurtosi(tarea_1$age)
[1] -0.991
Cuando el valor de curtosis es < 2 o > 2, entonces no
tiene un comportamiento normal
Calculamos la curtosis y el sesgo para hombres y para mujeres
kurtosi(tarea_1$age[tarea_1$male == "H"])
[1] -0.963
kurtosi(tarea_1$age[tarea_1$male == "M"])
[1] -1.01
#Para calcular el sesgo (simetría o asimetría)
skew(tarea_1$age)
[1] 0.228
skew(tarea_1$age[tarea_1$male == "H"])
[1] 0.272
skew(tarea_1$age[tarea_1$male == "M"])
[1] 0.195
Veamos los QQ-plot
qqPlot(tarea_1$age,
main = "QQ plot edad",
ylab = "Edad (años)",
xlab = "Cuantiles")
[1] 1625 3138

Combinar análisis de asimetría con histograma, y curtosis con
QQ-plot. Los QQplot permiten observar el peso de las colas. Si es una
distribución normal, entonces se debería de comportar como una línea
recta.
Podemos realizar el QQplot estratificado por hombres y mujeres.
qqPlot(tarea_1$age[tarea_1$male == "H"],
main = "QQ plot edad",
ylab = "Edad (años)",
xlab = "Cuantiles",
col = "blue",
col.lines = "red")
Gráficos de cajas y bigotes son importantes para el análisis

Utilizando ggplot

Pruebas de normalidad
- Shapiro-Wilks: Se utiliza cuando n < 50 (incluso menor a
40). Cuando hay menos de 10 datos ya no se recomienda usar.
- Kolmogorov-Smirnov: Se utiliza cuando n > 50
Para realizar la de Kolmogorov-Smirnov…Con los requisitos mínimo
Tiene como \(H_0\): La distribución
real y la distribución teórica son iguales o similares
(normalidad) \(H_a\): La
distribución real y la distribución teórica son diferentes (no
normalidad)
Cuando p > 0.05 no se rechaza la \(H_0\)
OJO: ya no se usa así, se usa una mejorada
ks.test(tarea_1$age, "pnorm",
mean = 49.6, sd = 8.57,
alternative = "two.sided")
Warning: ties should not be present for the Kolmogorov-Smirnov test
Asymptotic one-sample Kolmogorov-Smirnov test
data: tarea_1$age
D = 0.08, p-value <2e-16
alternative hypothesis: two-sided
Como el el valor de p obtenido es < 0.05 (p-value <
2e-16), se rechaza la hipótesis nula y concluimos que la muestra no
proviene de una distribución normal.
La prueba de Kolmogorov-Smirnov-Lilliefors es la corregida
# Se utiliza la librería de nortest
lillie.test(tarea_1$age[tarea_1$male == "H"])
Lilliefors (Kolmogorov-Smirnov) normality test
data: tarea_1$age[tarea_1$male == "H"]
D = 0.09, p-value <2e-16
Para hacer la de Shapiro-Wilk
shapiro.test(tarea_1$age[tarea_1$male == "H"])
Shapiro-Wilk normality test
data: tarea_1$age[tarea_1$male == "H"]
W = 1, p-value <2e-16
shapiro.test(tarea_1$age[tarea_1$male == "M"])
Shapiro-Wilk normality test
data: tarea_1$age[tarea_1$male == "M"]
W = 1, p-value <2e-16
4. Pruebas de contraste estadístico
Pruebas paramétricas (t de student y ANOVA)
Requieren cumplir varios supuestos
- Normalidad
- Igualdad de varianzas (Prueba de Levene): Requiere el planteamiento
de las siguientes hipótesis
- \(H_0\): la varianza de la variable
es igual o semejante en casos y controles (\(p>0.05\))
- \(H_a\): las varianzas son
diferentes entre grupos (\(p<0.05\))
Las hipotesis planteadas en las pruebas de comparación de medias
son:
- \(H_0: \mu_1 = \mu_2\)
- \(H_a: \mu_1 \neq \mu_2\)
Pruebas no paramétricas
- Signos de Wilcoxon
- U de Mann-Whitney
- Friedman
- Kruskal-Wallis
Empecemos con las pruebas paramétricas
Cargamos nuestra base de datos
sesion_4 <- read.csv("/cloud/project/Bioestadistica_R/datos_1.csv")
View(sesion_4)
Pasos a seguir para realizar la prueba:
- Realizar el análisis de normalidad
- Análisis de igualdad varianza (Prueba de Levene)
- Prueba de t de student
- Cálculo de tamaño de efecto
- Conclusión
Comparemos la talla entre casos y controles. ¿Existe diferencia en
esta variable entre ambos grupos?
- Análisis de normalidad
summary(sesion_4$Estatus) #Debe de estar en tipo factor
Caso Control
100 100
Dado que tenemos > 50 observaciones para cada grupo, utilizamos la
prueba de Lilliefors. El análisis debe de ser realizado para cada grupo
por separado, recordando que
\(H_0\): La distribución real y
teórica son iguales ($p > 0.05) \(H_a\): La distribución real y teórica son
diferentes ($p < 0.05)
lillie.test(sesion_4$Talla[sesion_4$Estatus == "Control"])
Lilliefors (Kolmogorov-Smirnov) normality test
data: sesion_4$Talla[sesion_4$Estatus == "Control"]
D = 0.1, p-value = 0.02
lillie.test(sesion_4$Talla[sesion_4$Estatus == "Caso"])
Lilliefors (Kolmogorov-Smirnov) normality test
data: sesion_4$Talla[sesion_4$Estatus == "Caso"]
D = 0.1, p-value = 0.006
Dado que \(p-value < 0.05\), se
recomienda análisis no paramétrico para comparar grupos.
- Ahora verificamos el análisis de igualdad de varianza (Prueba de
Levene) \(H_0\): Las varianzas entre
grupos son iguales o similares ($p > 0.05) \(H_a\): Las varianzas entre grupos son
diferentes ($p < 0.05)
leveneTest(sesion_4$Talla ~ sesion_4$Estatus,
center = "median")
Levene's Test for Homogeneity of Variance (center = "median")
Df F value Pr(>F)
group 1 0 0.96
198
leveneTest(sesion_4$Talla ~ sesion_4$Estatus,
center = "mean")
Levene's Test for Homogeneity of Variance (center = "mean")
Df F value Pr(>F)
group 1 0 0.99
198
El p-value (Pr(>F)) > 0.05, entonces
se acepta la \(H_0\). Las varianzas
entre grupos son iguales o similares.
- Ahora realizamos la prueba de T
Las hipótesis son
\(H_0: \mu_1 = \mu_2\) \(H_a: \mu_1 \neq \mu_2\)
#Si var.eq = TRUE, entonces hace la t de student normal
t.test(sesion_4$Talla ~ sesion_4$Estatus,
alternative = "two.sided",
conf.level = 0.95,
var.equal = TRUE,
paired = FALSE)
Two Sample t-test
data: sesion_4$Talla by sesion_4$Estatus
t = -0.6, df = 198, p-value = 0.5
alternative hypothesis: true difference in means between group Caso and group Control is not equal to 0
95 percent confidence interval:
-0.0324 0.0164
sample estimates:
mean in group Caso mean in group Control
1.67 1.67
#Si var.eq = FALSE, entonces hace la corrección de Welch porque las varianzas entre grupos son diferentes
#Esta prueba es para varianzas NO iguales
t.test(sesion_4$Talla ~ sesion_4$Estatus,
alternative = "two.sided",
conf.level = 0.95,
var.equal = FALSE,
paired = FALSE)
Welch Two Sample t-test
data: sesion_4$Talla by sesion_4$Estatus
t = -0.6, df = 198, p-value = 0.5
alternative hypothesis: true difference in means between group Caso and group Control is not equal to 0
95 percent confidence interval:
-0.0324 0.0164
sample estimates:
mean in group Caso mean in group Control
1.67 1.67
#Pacticamente esta es igual a la anterior
t.test(sesion_4$Talla ~ sesion_4$Estatus)
Welch Two Sample t-test
data: sesion_4$Talla by sesion_4$Estatus
t = -0.6, df = 198, p-value = 0.5
alternative hypothesis: true difference in means between group Caso and group Control is not equal to 0
95 percent confidence interval:
-0.0324 0.0164
sample estimates:
mean in group Caso mean in group Control
1.67 1.67
El intervalo de confianza (IC) son -0.0324 y 0.0164
Dado que el p-valor (0.5) > 0.05, entonces no se rechaza
la \(H_0\) y las medias entre ambos
grupos son iguales. El cálculo del tamaño del efecto NO
se aplica cuando p > 0.05, pero vamos a verificarlo
cohen.d(sesion_4$Talla, sesion_4$Estatus)
Call: cohen.d(x = sesion_4$Talla, group = sesion_4$Estatus)
Cohen d statistic of difference between two means
lower effect upper
[1,] -0.19 0.09 0.37
Multivariate (Mahalanobis) distance between groups
[1] 0.092
r equivalent of difference between two means
data
0.05
Otra opción de anális usando rstatix
library(rstatix)
library(tidyverse)
var_igualdad <- sesion_4 %>%
levene_test(Talla ~ Estatus, center = "median")
efecto <- sesion_4 %>%
cohens_d(Talla ~ Estatus)
5. ANOVA (Sesión 5)
Cargamos la base de datos
Preparación de la base de datos
str(pulmonar)
'data.frame': 130 obs. of 10 variables:
$ Clave : chr "P001" "P002" "P003" "P004" ...
$ Estatus : Factor w/ 2 levels "Case","Control": 2 2 1 2 1 2 2 2 1 1 ...
$ Sexo : Factor w/ 2 levels "H","M": 1 2 1 2 2 2 2 1 2 1 ...
$ Dosis : Factor w/ 3 levels "Clásico","Nuevo",..: 3 1 1 2 3 3 2 3 1 3 ...
$ Edad : int 62 55 79 69 70 63 58 50 47 88 ...
$ Peso : num 65.9 68.5 72.1 92.9 84.9 ...
$ Talla : num 2.28 2.1 1.52 2.1 1.86 1.55 1.73 2.71 1.79 1.93 ...
$ Fun_pulm : num 86.9 89.3 63.6 86.3 107.8 ...
$ PvCO2 : num 13.5 20.2 28.8 19.8 42.1 ...
$ Neutrofilos: num 8.66 7.35 23.05 7.44 21.79 ...
head(pulmonar)
Empecemos con el proceso de análisis.
- Primero contemos el número de datos, n.
Dado que para cada uno de los grupos su n es < 50,
utilizamos la prueba de Shapiro-Wilks
p <- pulmonar %>%
ggplot(pulmonar)+
geom_bar(aes(x = Dosis, y = PvCO2))
Error in `ggplot()`:
! `mapping` should be created with `aes()`.
✖ You've supplied a <data.frame> object
Backtrace:
1. pulmonar %>% ggplot(pulmonar)
3. ggplot2:::ggplot.default(., pulmonar)
Las hipótesis son
- \(H_0\): La distribución real \(=\) la distribución teórica
(normalidad, p-value > 0.05)
- \(H_a\): La distribución real \(\neq\) la distribución teórica son
(no normalidad, p-value < 0.05)
Dado que el análisis de normalidad indicó que el valor p de
la variable PvCO2 para cada uno de los grupos fue menor que
0.05, entonces se rechaza la hipótesis nula y aceptamos la \(H_a\), que es que no provienen de una
distribución normal. Ahorita omitimos que sean no normales.
Para realizar la ANOVA, todos los grupos deben cumplir el supuesto de
normalidad
EL siguiente paso es comprobar la igualdad de varianzas
- Igualdad de varianzas. Para este caso se usa la paquetería
car y carData
leveneTest(pulmonar$PvCO2 ~ pulmonar$Dosis,
center = "median")
Levene's Test for Homogeneity of Variance (center = "median")
Df F value Pr(>F)
group 2 0.41 0.67
127
Las hipótesis son - \(H_0\): Las
varianzas de PvCO2 \(=\) en todos los
grupos (p-value > 0.05) - \(H_a\): Las varianzas de PvCO2 \(\neq\) en al menos un grupo (no
normalidad, p-value < 0.05)
Usando el paquete rstatix
pulmonar %>%
levene_test(PvCO2 ~ Dosis, center = "median")
Dado que el p-value es > 0.05 (p = 0.667),
entonces la conclusión del analisis de igualdad de varianzas es que las
varianzas son similares o iguales. Procedemos a realizas la prueba de
ANOVA
- ANOVA Las hipótesis son
- \(H_0: \mu_1 = \mu_2 = \mu_3\)
- \(H_a: \mu_1 \neq \mu_2 \neq
\mu_3\):
La \(H_a\) dice que hay diferencias
en la media de PvCO2 entre alguno de los grupos
anova
ANOVA Table (type II tests)
Effect DFn DFd F p p<.05 ges
1 Dosis 2 127 1.4 0.25 0.022
Dado que p = 0.25, que es > 0.05, entonces aceptamos la
\(H_0\) y diríamos que no hay
diferencia entre grupos. Haremos las pruebas post-hoc
Finalmente, realizamos la conclusión
- Los tratamientos no provocan una diferencia en la PvCO2 (p-value =
0.25)
ANOVA de 2 vías
El análisis consiste
1. Normalidad
pulmonar %>%
group_by(Dosis) %>%
shapiro_test(Neutrofilos)
summary(pulmonar$Estatus)
Case Control
40 90
#Para los casos n=40, usamos la de Shapiro-Wilks
shapiro_test(pulmonar$Neutrofilos[pulmonar$Estatus == "Case"])
#Para controles n=90 usamos la de lillieforce
library(nortest)
lillie.test(pulmonar$Neutrofilos[pulmonar$Estatus == "Control"])
Lilliefors (Kolmogorov-Smirnov) normality test
data: pulmonar$Neutrofilos[pulmonar$Estatus == "Control"]
D = 0.06, p-value = 0.6
Para el % de neutrofilos estratificado por estatus cumplen la
normalidad. SIn embargo, al estratificar por el tratamiento no
cumple.
normalidad_condicionada <- pulmonar %>%
group_by(Dosis, Estatus) %>%
summary() %>%
shapiro_test(Neutrofilos)
Error in UseMethod("select") :
no applicable method for 'select' applied to an object of class "table"
Cuando se condiciona el análisis de normalidad de dos factores, el %
de neutrófilos cumple con el supuesto de normalidad
2. Igualdad de varianzas
La varianza de % de neutrófilos es diferente entre los grupos
(Estatus). La varianza de neutrófilos es igual entre grupos (Dosis)
3. ANOVA
result_2vias <- pulmonar %>%
anova_test(Neutrofilos ~ Dosis*Estatus,
effect.size = "ges") #Eta cuadrada generalizada
Con el análisis post-hoc
ppst_2vias <-pulmonar %>%
tukey_hsd(Neutrofilos ~ Dosis*Estatus)
4. Conclusión Analizando los subgrupos dosis:casos
se encontró que al comparar los múltiples tratamientos entre casos y
controles, no hay diferencias significativas. Al realizar un análisis
individual de cada factor, se detectó una gran diferencia en el factor
Estatus (p < 0.01, ges = 0.66), por lo que
se sospecha que las diferencias que puedan encontrarse son atribuibles
al factor Estatus.
ANOVA de 2 vías de medidas repetidas
Cargamos la base de datos y convertimos las variables caracter a
factor.
str(seguimiento)
'data.frame': 84 obs. of 10 variables:
$ Clave : chr "C-001" "C-002" "C-004" "C-005" ...
$ Tiempo : Factor w/ 3 levels "Antes","Despues",..: 1 1 1 1 1 1 1 1 1 1 ...
$ Sexo : Factor w/ 2 levels "H","M": 2 1 2 1 2 2 1 2 1 1 ...
$ Fumadores : Factor w/ 2 levels "No","Si": 2 2 2 2 2 2 2 2 2 2 ...
$ Edad.de.inicio : int 24 21 23 19 18 15 14 21 18 23 ...
$ Cigarros.al.día : int 43 11 45 39 38 30 44 27 49 28 ...
$ Padres.fumadores : Factor w/ 2 levels "No","Si": 2 2 1 2 2 1 2 2 2 2 ...
$ Problemas.cardiacos: Factor w/ 2 levels "No","Si": 2 1 2 1 1 2 2 1 2 2 ...
$ CHRNA5 : Factor w/ 2 levels "No","Si": 2 2 1 2 1 2 2 1 2 1 ...
$ rs578776 : Factor w/ 3 levels "CC","CT","TT": 2 2 3 3 1 2 2 1 3 1 ...
Hay que asegurarnos que están todos los datos completos de la base de
datos. Es decir, que la n sea la misma para todos los tiempos
.
Analicemos la normalidad
Los valores de Mantenimiento no provienen de una
distribución normal. En este caso, no podríamos continuar con la ANOVA y
tendríamos que hacer otra prueba no paramétrica. Sin embargo, vamos a
continuar con la ANOVA.
Ahora realicemos la prueba de igualdad de varianzas. Prueba de
Levene
Dado el valor de p > 0.05 (p-value = 0.143), podemos
decir que las varianzas son iguales/similares en todos los grupos.
Planteemos la prueba de ANOVA
seguimiento %>%
anova_test(dv = Cigarros.al.día,
wid = Clave,
within = Tiempo)
ANOVA Table (type III tests)
$ANOVA
Effect DFn DFd F p p<.05 ges
1 Tiempo 2 54 40.2 2.06e-11 * 0.362
$`Mauchly's Test for Sphericity`
Effect W p p<.05
1 Tiempo 0.901 0.256
$`Sphericity Corrections`
Effect GGe DF[GG] p[GG] p[GG]<.05 HFe DF[HF] p[HF] p[HF]<.05
1 Tiempo 0.91 1.82, 49.12 1.4e-10 * 0.972 1.94, 52.47 3.75e-11 *
Ya que el valor de p de ANOVA es < 0.05 (p-value =
2.06e-11), podemos decir que hay diferencias entre grupos.
Para el test de esfericidad
- \(H_0\): los datos no varian en
cada nivel de tiempo (p >0.05)
- \(H_a\): los datos varían en cada
nivel de tiempo (p < 0.05)
Si no se cumple el supuesto de esfericidad por el test de Mauchly,
habrá que ver los datos de la corrección de esfericidad. Habrá que
considerar el valor de p de p[GG] del test de corrección de
esfericidad como el que se tiene que reportar de la prueba de ANOVA.
Hasta aquí concluimos parcialmente que existe diferencia en el número
de cigarros por día después de cada etapa de tratamiento.
##6. Pruebas no paramétricas
- Son pruebas para comparar datos con distribuciones no normales
- No son sensibles a datos extremos atípicos, por lo que su presencia
no afectará el análisis en conjunto
- Fundament: ordena los datos de menor a mayor, cada dato se le asigna
posición y un valor numérico (rango de 1 a n)
- Comparaciones cuantitativas: prueba de Wilcoxon, U de Mann-Whitney,
Kruskal-Wallis, Friedman
- Comparaciones cualitativas: chi cuadrada, Fisher
str(sesion_6)
'data.frame': 200 obs. of 11 variables:
$ Clave : chr "A001" "A002" "A003" "A004" ...
$ Sexo : chr "M" "H" "M" "H" ...
$ Edad : int 21 35 38 18 32 21 39 23 37 35 ...
$ Estatus : chr "Control" "Caso" "Control" "Control" ...
$ Exposicion : Factor w/ 4 levels "Fumador","Humo de biomasa",..: 3 2 1 1 3 1 1 4 1 2 ...
$ Enfermedad : chr "Sin alteraciones" "Capacidad pulmonar alterada" "Sin alteraciones" "Sin alteraciones" ...
$ Cotinina : num 1 8 183.2 225.5 2.3 ...
$ Anos.de.exposicion: int 0 5 20 4 0 3 15 5 20 32 ...
$ Talla : num 1.65 1.56 1.66 1.77 1.5 1.78 1.8 1.55 1.68 1.7 ...
$ Peso : int 88 55 68 104 56 115 98 74 99 65 ...
$ Padres.fumadores : chr "No" "No" "Si" "No" ...
Hay que probar la asimetría (skew)
skew(sesion_6$Cotinina[sesion_6$Exposicion== "Sin exposicion"])
[1] 0.908
skew(sesion_6$Cotinina[sesion_6$Exposicion== "Fumador"])
[1] -0.115
skew(sesion_6$Cotinina[sesion_6$Exposicion== "Vapeador"])
[1] 1.99
skew(sesion_6$Cotinina[sesion_6$Exposicion== "Humo de biomasa"])
[1] 0.734
Asimetría < -2 o > 2 ya era una simetría muy marcada. Se espera
tener una asimetría = 0 (simétricos), o cuando \(a \neq 0\) (asimétricos )
Realizamos el análisis de igualdad de varianzas

Realizamos la prueba de U de Mann-Whitney para datos no
relacionados
Existe diferencia entre el grupo sin exposición vs fumadores (se
report el p.adj.signif), y sin exposicion vs vapeadores.
La prueba post-hoc de Kruskal-Wallis es la siguiente
Para responder a la pregunta de qué tan grande es la diferencia,
entonces calculamos el tamaño del efecto
Los niveles de cotinina varían entre el grupo sin exposición vs
fumadores (p = 3.19e-16) y sin exposición vs vapeadores. Las diferencias
encontradas son grandes según la prueba del tamaño del efecto.
Revisemos como se comparan entre todos los grupos en los niveles de
Cotinina. Realizamos la prueba de Kruskal-Wallis
No sabemos entre que grupos hay diferencia, pero si existe porque el
valor de p arrojado por la prueba de Kruskal-Wallis (p = 2.27e-32). Para
saber entre que grupos hacemos la prueba post-hoc de dunn
O hacemos una prueba post-hoc
Para ver el tamaño del efecto
Pruebas cualitativas
Realicemos una tabla de contingencia (principalmente para variables
cualitativas)
estatus_sexo
Estatus
Sexo Caso Control
H 58 58
M 42 42
Una tabla de nx2 (Primero filas, luego columnas)
table(sesion_6$Sexo, sesion_6$Exposicion,
dnn = c("Sexo", "Exposicion"))
Exposicion
Sexo Fumador Humo de biomasa Sin exposicion Vapeador
H 55 17 18 26
M 35 20 14 15
Para realizar la prueba
chisq.test(estatus_exposicion)
Pearson's Chi-squared test
data: estatus_exposicion
X-squared = 3, df = 3, p-value = 0.4
cuando p > 0.05 la freucuencia de datos es igual, cuando p <
0.05 la frecuenca de datos es diferente
LS0tCnRpdGxlOiAiQmlvZXN0YWTDrXN0aWNhIGFwbGljYWRhIGEgbGEgaW52ZXN0aWdhY2nDs24iCmF1dGhvcjogIlJvbW1lbCBTw6FuY2hleiIKZGF0ZTogIjIwMjMtMTEtMjUiCm91dHB1dDoKICBodG1sX25vdGVib29rOiBkZWZhdWx0CiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0CiAgaHRtbF9kb2N1bWVudDoKICAgIGRmX3ByaW50OiBwYWdlZAplZGl0b3Jfb3B0aW9uczogCiAgbWFya2Rvd246IAogICAgd3JhcDogNzIKLS0tCgpgYGB7cn0KaW5zdGFsbC5wYWNrYWdlcygndGlkeXZlcnNlJykKaW5zdGFsbC5wYWNrYWdlcygicnN0YXRpeCIpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHJzdGF0aXgpCmBgYAoKIyMgSW50cm9kdWNjaW9uIGEgUgoKIyMgMS4gVmVjdG9yZXMKClByaW1lcm8gYWdyZWdhbW9zIHZhbG9yZXMgZGUgZWRhZGVzCgpgYGB7cn0KZWRhZCA9IGMoMjMsMjYsMjcsMjgsMzIsMzQsMjgsMjksMzEsMjUpCmBgYAoKTHVlZ28gYWdyZWdhbW9zIG5vbWJyZXMKCmBgYHtyfQpub21icmVzID0gYygiTWFyaWEiLCAiR3VzdGF2byIsICJSYW1vbiIsICJTaWx2aWEiLCAiQWxtYSIsICJFZHVhcmRvIiwgIkVucmlxdWUiLCAiRXJpY2siLCAiSHVnbyIsICJKZXN1cyIpCmBgYAoKUGFyYSByZXZpc2FybG9zCgpgYGB7cn0Kbm9tYnJlcwpgYGAKCkdlbmVyYWNpw7NuIGRlIHZlY3RvcmVzIGFsZWF0b3Jpb3MuIEdlbmVyYSAxMCBkYXRvcyBhbGVhdG9yaW9zIGVuIHVuCnJhbmdvIGVudHJlIDgsMDAwIGEgMzAsMDAwLgoKYGBge3J9CnNhbGFyaW8gPSBzYW1wbGUoODAwMDozMDAwMCxzaXplID0gMTAsIHJlcGxhY2UgPSBGQUxTRSkKc2FsYXJpbwpgYGAKCk5vdGE6IFNpIGByZXBsYWNlID0gRkFMU0VgLCBlbnRvbmNlcyBubyBoYXkgcmVlbXBsYXpvLgoKQWhvcmEgYWdyZWdhbW9zIDEwIGRhdG9zIGFsZWF0b3Jpb3MgcmVmZXJlbnRlcyBhIHNleG8KCmBgYHtyfQpzZXhvID0gc2FtcGxlKGMoIkgiLCAiTSIpLCBzaXplID0gMTAsIHJlcGxhY2UgPSBUUlVFKQpzZXhvCmBgYAoKVGVuZW1vcyBkaWZlcmVudGVzIHZhcmlhYmxlcyAtIEN1YWxpdGF0aXZhcyAtIE5vbWluYWxlcyAtIE9yZGluYWxlcyAtCkN1YW50aXRhdGl2YXMgLSBEaXNjcmV0YXMgLSBDb250aW51YXMKCiMjIDIuIEJhc2VzIGRlIGRhdG9zIChkYXRhIGZyYW1lcykKClRlbmVtb3MgYmFzZXMgZGUgZGF0b3MgLSBFeHRlcm5hcyAtIEludGVybmFzCgpFc3RlIHNlcsOtYSB1biBlamVtcGxvIGRlIGJhc2VzIGRlIGRhdG9zIGludGVybmFzIGRlIFIKCmBgYHtyfQplamVtcGxvXzEgPC0gZGF0YXNldHM6OnN0YXRlLng3NwpoZWFkKGVqZW1wbG9fMSkKYGBgCgpMYSBmb3JtYSBkZSBsZWVyIHVuYSBiYXNlIGRlIGRhdG9zIGV4dGVybmEsIGFyY2hpdm8gc2VwYXJhZG8gcG9yIGNvbWFzCmAuY3N2YCwgZXMgY29uIGxhIGZ1bmNpw7NuIGByZWFkLmNzdihkaXJlY3RvcmlvLmNzdilgCgpgYGB7cn0KZGF0b3MxIDwtIHJlYWQuY3N2KCIvY2xvdWQvcHJvamVjdC9CaW9lc3RhZGlzdGljYV9SL2RhdG9zXzEuY3N2IikKaGVhZChkYXRvczEpICNDb24gbGEgZnVuY2nDs24gaGVhZCgpIHNlIG9ic2VydmFuIGxvcyBwcmltZXJvcyA2IHZhbG9yZXMgZGVsIGRhdGEgZnJhbWUKc3RyKGRhdG9zMSkKYGBgCgpQYXJhIGNhcmdhciBsYSBzZWd1bmRhIGJhc2UgZGUgZGF0b3MKCmBgYHtyfQpkYXRvczIgPC0gcmVhZF9jc3YoIi9jbG91ZC9wcm9qZWN0L0Jpb2VzdGFkaXN0aWNhX1IvZGF0b3NfMi5jc3YiKSAgICAgI0FxdcOtIHVzYW1vcyBsYSBmdW5jacOzbiByZWFkX2NzdiBkZWwgcGFxdWV0ZSB0aWR5dmVyc2UKaGVhZChkYXRvczIpCmBgYAoKQ3JlYW1vcyB1bmEgYmFzZSBkZSBkYXRvcyBhIHBhcnRpciBkZSBsb3MgdmVjdG9yZXMgcmVhbGl6YWRvcwpwcmV2aWFtZW50ZQoKYGBge3J9CmRhdG9zMyA8LSBkYXRhLmZyYW1lKG5vbWJyZXMsIGVkYWQsIHNleG8sIHNhbGFyaW8pIAphc190aWJibGUoZGF0b3MzKQpgYGAKClBhcmEgY2FtYmlhciBlbCBub21icmUgZGUgdW5hIHZhcmlhYmxlIHV0aWxpemFtb3MgYHJlbmFtZSgpYAoKYGBge3J9CnJlbmFtZShkYXRvczMsIG5vbWJyZSA9IG5vbWJyZXMpICAgIyBwcmltZXJvIHBvbmVtb3MgZWwgKGRhdGFmcmFtZSwgbm9tYnJlIG51ZXZvID0gbm9tYnJlIHZpZWpvKQpgYGAKClJlY29yZGFyIHF1ZSBlamVyY2ljaW8yID0gZGF0b3MxIHkgZWplcmNpY2lvMz1kYXRvczIKClJldmlzYXIgYWxndW5hcyBmdW5jaW9uZXMgZGUgYHRpZHl2ZXJzZWAgRnVuY2nDs24gYHNlbGVjdGAKCmBgYHtyfQplamVyY2ljaW81IDwtIGRhdG9zMSAlPiUgCiAgc2VsZWN0KENsYXZlLCBFZGFkLCBDb3RpbmluYSwgQW5vcy5kZS5leHBvc2ljaW9uLCBUYWxsYSwgUGVzbykKZWplcmNpY2lvNQpgYGAKCkZ1bmNpw7NuIGBmaWx0ZXJgCgpgYGB7cn0KZWplcmNpY2lvNiA8LSBkYXRvczEgJT4lIAogIGZpbHRlcihFZGFkPj0yNSkKZWplcmNpY2lvNgpgYGAKCkZ1bmNpw7NuIGBmaWx0ZXJgIGNvbiB2YXJpYWJsZSBjdWFsaXRhdGl2YQoKYGBge3J9CmVqZXJjaWNpbzYgPC0gZGF0b3MxICU+JSAKICBmaWx0ZXIoRXhwb3NpY2lvbj09IkZ1bWFkb3IifEV4cG9zaWNpb249PSJWYXBlYWRvciJ8RXhwb3NpY2lvbj09IlNpbiBleHBvc2ljaW9uIikKZWplcmNpY2lvNgpgYGAKClZlciBsYSBmdW5jacOzbiBgbXV0YXRlYCBwYXJhIGNyZWFyIHZhcmlhYmxlcyBudWV2YXMgYSBwYXJ0aXIgZGUKdmFyaWFibGVzIGV4aXN0ZW50ZXMKCmBgYHtyfQplamVyY2ljaW8yIDwtIGRhdG9zMSAlPiUgCiAgbXV0YXRlKElNQyA9IFBlc28gLyBUYWxsYV4yKQplamVyY2ljaW8yCmBgYAoKUG9kZW1vcyBjYW1iaWFyIGVsIHRpcG8gZGUgdmFyaWFibGUgZGUgY3VhbnRpdGF0aXZhIGEgY3VhbGl0YXRpdmEgY29uIGxhCmZ1bmNpw7NuIGBtdXRhdGVgCgpgYGB7cn0KZWplcmNpY2lvMiA8LSBlamVyY2ljaW8yICU+JSAKICBtdXRhdGUoSU1DX2VzY2FsYSA9IGNhc2Vfd2hlbihJTUM8MTggfiAiRGVzbnV0cmljaW9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJTUM8MjUgfiAiTm9ybW9wZXNvIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJTUM8MzAgfiAiU29icmVwZXNvIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJTUM+PTMwIH4gIk9iZXNpZGFkIikpCmVqZXJjaWNpbzIKYGBgCgpBaG9yYSB2ZXJlbW9zIGxhcyBydXRhcyBkZSBhbsOhbGlzaXMKCi0gICBEZXB1cmFjacOzbiBkZSBiYXNlcyBkZSBkYXRvcwogICAgLSAgIERhdG9zIHBlcmRpZG9zKGBuYS5vbWl0YCkKICAgIC0gICBGaWx0cmFyIGRhdG9zIChgZmlsdGVyYCkKICAgIC0gICBTZWxlY2Npb25hciBsYXMgdmFyaWFibGVzIGRlIHVzbyAoYHNlbGVjdGApCiAgICAtICAgQ29udmVyc2lvbmVzIC8gQ29ycmVjY2lvbmVzIChgbXV0YXRlYCkKICAgIC0gICBBbmFsaXphciBsYSBlc3RydWN0dXJhIGRlIGxvcyBkYXRvcyAoYHN0cmApCgpgYGB7cn0Kc3RyKGVqZXJjaWNpbzIpICAgIApgYGAKCkxhIGRhdG9zIHF1ZSBzYWxlbiBlcyBlbCAqKmFuw6FsaXNpcyBkZSBlc3RydWN0dXJhKioKClRhbWJpw6luIHBvZGVtb3MgY2FtYmlhciBlbCB0aXBvIGRlIHZhcmlhYmxlLiBQb3IgZWplbXBsbywgbGEgdmFyaWFibGUKKlNleG8qIGVzdMOhIGNhdGVnb3JpemFkYSBjb21vICoqY2hyKiogKGNoYXJhY3RlciksIHBlcm8gbGEgY2FtYmlhbW9zIGEKKipmYWN0b3IqKi4gQ29udmllbmUgY2FtYmlhcmxhcyBwYXJhIHJlYWxpemFyIGFkZWN1YWRhbWVudGUgZWwgYW7DoWxpc2lzCmVzdGFkw61zdGljbywgeWEgcXVlIGxhcyB2YXJpYWJsZXMgdGlwbyAqY2hyKiBubyBzb24gw7p0aWxlcyBwYXJhIGVzZQphbsOhbGlzaXMuCgpgYGB7cn0KZWplcmNpY2lvMiRTZXhvIDwtIGFzLmZhY3RvcihlamVyY2ljaW8yJFNleG8pCmVqZXJjaWNpbzIkRXN0YXR1cyA8LSBhcy5mYWN0b3IoZWplcmNpY2lvMiRFc3RhdHVzKQplamVyY2ljaW8yJEV4cG9zaWNpb24gPC0gYXMuZmFjdG9yKGVqZXJjaWNpbzIkRXhwb3NpY2lvbikKZWplcmNpY2lvMiRFbmZlcm1lZGFkIDwtIGFzLmZhY3RvcihlamVyY2ljaW8yJEVuZmVybWVkYWQpCmVqZXJjaWNpbzIkUGFkcmVzLmZ1bWFkb3JlcyA8LSBhcy5mYWN0b3IoZWplcmNpY2lvMiRQYWRyZXMuZnVtYWRvcmVzKQplamVyY2ljaW8yJElNQ19lc2NhbGEgPC0gYXMuZmFjdG9yKGVqZXJjaWNpbzIkSU1DX2VzY2FsYSkKc3RyKGVqZXJjaWNpbzIpIApgYGAKClByb2NlZGVtb3MgYSBoYWNlciB1biBhbsOhbGlzaXMgZXN0YWTDrXN0aWNvCgpgYGB7cn0KcmVzdWx0YWRvcyA8LSBlamVyY2ljaW8yICU+JSAKICBnZXRfc3VtbWFyeV9zdGF0cygpICAgICNTb2xvIGVzIGNvbiBlbCBwYXF1ZXRlIHJzdGF0aXgKcmVzdWx0YWRvcwpgYGAKClBhcmEgaGFjZXIgdW4gYW7DoWxpc2lzIGVzdHJhdGlmaWNhZG8gc2UgcmVxdWllcmUgdW5hIHZhcmlhYmxlCmN1YWxpdGF0aXZhIGNvbW8gYGZhY3RvcmAKCmBgYHtyfQpyZXN1bHRhZG9zIDwtIGVqZXJjaWNpbzIgJT4lIAogIGdyb3VwX2J5KEVuZmVybWVkYWQpICU+JSAKICBnZXRfc3VtbWFyeV9zdGF0cygpICAgICNTb2xvIGVzIGNvbiBlbCBwYXF1ZXRlIHJzdGF0aXgKcmVzdWx0YWRvcwpgYGAKClBhcmEgcmVhbGl6YXIgbGEgdGFyZWEgYWJyaW1vcyBlbCBhcmNoaXZvIGRlIGRhdG9zIGRlIGZyYW1pbmdoYW0sIGx1ZWdvCnNlbGVjY2lvbmFtb3MgbGFzIHZhcmlhYmxlcyBkZSBsYXMgY3VhbGVzIHF1ZXJlbW9zIG9idGVuZXIgbGFzIG1lZGlkYXMKZGUgdGVuZGVuY2lhIGNlbnRyYWwgeSBsYXMgbWVkaWRhcyBkZSBkaXNwZXJzacOzbiB1dGlsaXphbmRvIGVsIHBhcXVldGUKYHJzdGF0aXhgCgpgYGB7cn0KZGF0b3NfZnJhbWluZ2hhbSA8LSByZWFkX2NzdigiL2Nsb3VkL3Byb2plY3QvQmlvZXN0YWRpc3RpY2FfUi9mcmFtaW5naGFtLmNzdiIpClZpZXcoZGF0b3NfZnJhbWluZ2hhbSkKCnN0cihkYXRvc19mcmFtaW5naGFtKQoKZGF0b3NfZnJhbWluZ2hhbSA8LSByZW5hbWUoZGF0b3NfZnJhbWluZ2hhbSwgc2V4b3MgPSBtYWxlKQpkYXRvc19mcmFtaW5naGFtCgpyZXN1bHRhZG9zX2ZyYW1pbmdoYW0gPC0gZGF0b3NfZnJhbWluZ2hhbSAlPiUgCiAgc2VsZWN0KGFnZSwgc2V4b3MsIGNpZ3NQZXJEYXksIHRvdENob2wsIHN5c0JQLCBkaWFCUCwgQk1JLCBoZWFydFJhdGUsIGdsdWNvc2UpICU+JSAKICBmaWx0ZXIoIWNvbXBsZXRlLmNhc2VzKC4pKSAlPiUKICBncm91cF9ieShzZXhvcykgJT4lIAogIGdldF9zdW1tYXJ5X3N0YXRzKCkKClZpZXcocmVzdWx0YWRvc19mcmFtaW5naGFtKQpgYGAKCkFob3JhIG9idGVuZW1vcyBlbCByYW5nbyBkZSBtYW5lcmEgbWFudWFsIGNhbGN1bGFuZG8gbGEgZGlmZXJlbmNpYSBlbnRyZQplbCBtw6F4aW1vIHkgZWwgbcOtbmltby4KCmBgYHtyfQpyYW5nb19hZ2UgPC0gbWF4KGRhdG9zX2ZyYW1pbmdoYW0kYWdlLCBuYS5ybSA9IFRSVUUpIC0gbWluKGRhdG9zX2ZyYW1pbmdoYW0kYWdlLCBuYS5ybSA9IFRSVUUpCnJhbmdvX2NpZ3MgPC0gbWF4KGRhdG9zX2ZyYW1pbmdoYW0kY2lnc1BlckRheSwgbmEucm0gPSBUUlVFKSAtIG1pbihkYXRvc19mcmFtaW5naGFtJGNpZ3NQZXJEYXksIG5hLnJtID0gVFJVRSkKcmFuZ29fY2hvbCA8LSBtYXgoZGF0b3NfZnJhbWluZ2hhbSR0b3RDaG9sLCBuYS5ybSA9IFRSVUUpIC0gbWluKGRhdG9zX2ZyYW1pbmdoYW0kdG90Q2hvbCwgbmEucm0gPSBUUlVFKQpyYW5nb19zYnAgPC0gbWF4KGRhdG9zX2ZyYW1pbmdoYW0kc3lzQlAsIG5hLnJtID0gVFJVRSkgLSBtaW4oZGF0b3NfZnJhbWluZ2hhbSRzeXNCUCwgbmEucm0gPSBUUlVFKQpyYW5nb19kYnAgPC0gbWF4KGRhdG9zX2ZyYW1pbmdoYW0kZGlhQlAsIG5hLnJtID0gVFJVRSkgLSBtaW4oZGF0b3NfZnJhbWluZ2hhbSRkaWFCUCwgbmEucm0gPSBUUlVFKQpyYW5nb19CTUkgPC0gbWF4KGRhdG9zX2ZyYW1pbmdoYW0kQk1JLCBuYS5ybSA9IFRSVUUpIC0gbWluKGRhdG9zX2ZyYW1pbmdoYW0kQk1JLCBuYS5ybSA9IFRSVUUpCnJhbmdvX2hyIDwtIG1heChkYXRvc19mcmFtaW5naGFtJGhlYXJ0UmF0ZSwgbmEucm0gPSBUUlVFKSAtIG1pbihkYXRvc19mcmFtaW5naGFtJGhlYXJ0UmF0ZSwgbmEucm0gPSBUUlVFKQpyYW5nb19nbHVjIDwtIG1heChkYXRvc19mcmFtaW5naGFtJGdsdWNvc2UsIG5hLnJtID0gVFJVRSkgLSBtaW4oZGF0b3NfZnJhbWluZ2hhbSRnbHVjb3NlLCBuYS5ybSA9IFRSVUUpCgpyYW5nb19hZ2UKcmFuZ29fY2lncwpyYW5nb19jaG9sCnJhbmdvX3NicApgYGAKCiMjIDMuIEFuw6FsaXMgZGVzY3JpcHRpdm8geSBub3JtYWxpZGFkIChTZXNpw7NuIDMpLCBjb24gZW5mb3F1ZSBlbiBlbCBhbsOhbGlzaXMgZGUgbm9ybWFsaWRhZAoKVGVuZXIgcHJlY2F1Y2nDs24gcGFyYSB1dGlsaXphciBgcmVhZC5jc3ZgbyBgcmVhZF9jc3ZgcG9ycXVlIGVsIG1hbmVqbyBkZQpsYXMgdGFibGFzIGVzIGRpZmVyZW50ZQoKYGBge3J9CmxpYnJhcnkodGlkeXZlcnNlKQoKdGFyZWFfMSA8LSByZWFkLmNzdigiL2Nsb3VkL3Byb2plY3QvQmlvZXN0YWRpc3RpY2FfUi9mcmFtaW5naGFtLmNzdiIpCnN0cih0YXJlYV8xKQoKYGBgCgpDYW1iaWFtb3MgZWwgdGlwbyBkZSB2YXJpYWJsZSBgbWFsZWBjb21vIGZhY3RvcgoKYGBge3J9CnRhcmVhXzEkbWFsZSA8LSBhcy5mYWN0b3IodGFyZWFfMSRtYWxlKQpzdHIodGFyZWFfMSkKCnRhcmVhXzEgPC0gdGFyZWFfMSAlPiUgCiAgbXV0YXRlKG1hbGUgPSBjYXNlX3doZW4obWFsZSA9PSAiMCIgfiAiTSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgbWFsZSA9PSAiMSIgfiAiSCIpKQoKc3RyKHRhcmVhXzEpCgpWaWV3KHRhcmVhXzEpCmBgYAoKUmVhbGl6YW1vcyBlbCBhbsOhbGlzaXMgZGVzY3JpcHRpdm8KCmBgYHtyfQpyZXN1bHRhZG9zX3RhcmVhXzEgPC0gdGFyZWFfMSAlPiUgCiAgZ3JvdXBfYnkobWFsZSkgJT4lIAogIGdldF9zdW1tYXJ5X3N0YXRzKGFnZSwgY2lnc1BlckRheSwgdG90Q2hvbCwgc3lzQlAsIGRpYUJQLCBCTUksIGhlYXJ0UmF0ZSwgZ2x1Y29zZSkKCnJlc3VsdGFkb3NfdGFyZWFfMSA8LSByZXN1bHRhZG9zX3RhcmVhXzEgJT4lIAogIG11dGF0ZShSYW5nb18xID0gbWF4IC0gbWluKQogIApWaWV3KHJlc3VsdGFkb3NfdGFyZWFfMSkKYGBgCgpQYXJhIGNyZWFyIHVuIGFyY2hpdm8gYC5jc3ZgCgpgYGB7cn0Kd3JpdGUuY3N2KHJlc3VsdGFkb3NfdGFyZWFfMSwgZmlsZSA9ICJyZXN1bHRhZG9zX3RhcmVhMSIpCmBgYAoKIyMjIFRlbWFzIGRlIGVzdGFkw61zdGljYQoKRGVmaW5pciAqKnBvYmxhY2nDs24qKiB5ICoqbXVlc3RyYSoqLiBMYSAqbXVlc3RyYSogZXMgZWwgY29uanVudG8gZGUKZWxlbWVudG9zIHJlcHJlc2VudGF0aXZvcyBkZSBsYSBwb2JsYWNpw7NuIHkgc2UgZGViZSBzZWxlY2Npb25hcgphbGVhdG9yaWFtZW50ZS4KCkVzY2FsYXMgZGUgbWVkaWNpw7NuOiA0IG5pdmVsZXMgZGUgZXNjYWxhCgotICAgTm9taW5hbDogU2UgdXRpbHphbiBub21icmVzLCBzw61tYm9sb3MgbyBudW1lcmFsZXMsIHF1ZSBzb24gZW1wbGVhZG9zCiAgICBjb21vIGV0aXF1ZXRhcy4gU2lydmUgcGFyYSBzZXBhcmFyIHBvciBncnVwb3MuIE5vIHNlIHB1ZWRlbiBhcGxpY2FyCiAgICBvcGVyYWNpb25lcyBtYXRlbcOhdGljYXMuCgotICAgT3JkaW5hbDogTG9zIG7Dum1lcm9zIG8gZXRpcXVldGFzIHV0aWxpemFkYXMgaW5kaWNhbiB1biBvcmRlbi4gU2lydmUKICAgIHBhcmEgb3JnYW5pemFyLgoKLSAgIEludGVydmFsb3M6IEVzdGFibGVjZSB1biBvcmRlbiBkZXRlcm1pbmFkby4gRWwgMCBzaWduaWZpY2EgYXVzZW5jaWEKICAgIGRlIHZhbG9yLiBTZSBwdWVkZSB1dGlsaXphciBwYXJhIG9wZXJhY2lvbmVzIG1hdGVtw6F0aWNhcy4KCi0gICBQcm9wb3JjacOzbi9jb2NpZW50ZS9yYXrDs246IFNvbiBzZWN1ZW5jaWFzIG51bcOpcmljYXMuIFNlIHB1ZWRlbgogICAgcmVhbGl6YXIgb3BlcmFjaW9uZXMgbWF0ZW3DoXRpY2FzLiBFcyBsYSBlc2NhbGEgcXVlIHBlcm1pdGUgY29tcGFyYXIKICAgIGRlIG1lam9yIG1hbmVyYSBsb3MgZGF0b3MuCgojIyMgRGlzdHJpYnVjacOzbiBkZSBkYXRvcwoKLSAgIERpc3RyaWJ1Y2nDs24gbm9ybWFsCi0gICBEaXN0cmlidWNpw7NuIGRlIFBvaXNzb24KLSAgIERpc3RyaWJ1Y2nDs24gYmlub21pYWwKCiMjIyBBc2ltZXRyw61hIGRlIGRhdG9zCgotICAgTmVnYXRpdmEKLSAgIFNpbcOpdHJpY2EKLSAgIFBvc2l0aXZhCgojIyMgQ3VydG9zaXMKCi0gICBMZXB0b2PDunJ0aWNhCi0gICBNZXNvY8O6cnRpY2EKLSAgIFBsYXRpY8O6cnRpY2EKCiMjIDQuIExhIG5vcm1hbGlkYWQuLi4KClB1ZWRlIHNlciBldmFsdWFkYSBtZWRpYW50ZSAzIG1vZGVsb3MKCi0gICBNb2RlbG9zIGdyw6FmaWNvczogaGlzdG9ncmFtYXMgeSBxcXBsb3QKLSAgIE1lZGlhID0gTWVkaWFuYSA9IE1vZGEKLSAgIFBydWViYXMgZGUgbm9ybWFsaWRhZDoKICAgIC0gICBTaGFwaXJvLVdpbGQ6IHBhcmEgZ3J1cG9zIGNvbiAqbWVub3MgZGUgNTAgb2JzZXJ2YWNpb25lcyoKICAgIC0gICBLb2xtb2dvcm92LVNtaXJub3YvTGlsbGlmb3JzOiBwYXJhIGdydXBvcyBjb24gKm3DoXMgZGUgNTAKICAgICAgICBvYnNlcnZhY2lvbmVzKgoKIyMjIFJlcHJlc2VudGFjacOzbiBkZSBsb3MgZGF0b3MgYSBwYXJ0aXIgZGUgbGFzIG1lZGlkYXMgZGUgdGVuZGVuY2lhIGNlbnRyYWwKClV0aWxpemFtb3MgZWwgdmFsb3IgZGUgbGEgZWRhZAoKYGBge3J9CmVqZXJjaWNpb18xIDwtIHRhcmVhXzEgJT4lIAogIGdldF9zdW1tYXJ5X3N0YXRzKGFnZSkKCmNvbnRlbyA8LSB0YXJlYV8xICU+JSAKICBjb3VudChhZ2UpCgojbWVhbjogNDkuNTgKI21lZGlhbjogNDkKI21vZGE6IDQwCmBgYAoKU29sbyBjb24gZXN0b3MgcGFyw6FtZXRyb3Mgc2UgcG9kcsOtYSBkZWNpciBxdWUgbGEgbXVlc3RyYSBubyBjdW1wbGUgY29uCmxhIG5vcm1hbGlkYWQuIFZlYW1vcyBzdSBoaXN0b3JncmFtYSBjb24gbGEgZnVuY2nDs24gYGhpc3QoKWAsIGRlbCBjdWFsCnBvZGVtb3MgbW9kaWZpY2FyIHN1IHTDrXR1bG8sIGxhcyBldGlxdWV0YXMgeSBlbCBjb2xvci4KCmBgYHtyfQpoaXN0KHRhcmVhXzEkYWdlLAogICAgIG1haW4gPSAiSGlzdG9ncmFtYSBkZSBlZGFkIiwKICAgICB5bGFiID0gIkZyZWN1ZW5jaWEiLAogICAgIHhsYWIgPSAiRWRhZCAoYcOxb3MpIiwKICAgICBjb2wgPSAic2FsbW9uIiwKICAgICBicmVha3MgPSA1MCkKYGBgCgpQb2RlbW9zIHJlYWxpemFyIGxhcyBncsOhZmljYXMgY29uIGVsIHBhcXVldGUgYGdncGxvdGAgcXVlIHBlcm1pdGUgY3JlYXIKZ3LDoWZpY29zIG3DoXMgZXN0w6l0aWNvcwoKYGBge3J9CmxpYnJhcnkoZ2dwbG90MikKYGBgCgpQYXJhIGdyYWZpY2FyIGVsIGhpc3RvZ3JhbWEKCmBgYHtyfQpncmFmaWNhXzEgPC0gZ2dwbG90KHRhcmVhXzEsIGFlcyh4PWFnZSwgZmlsbCA9IG1hbGUpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAxKQoKZ3JhZmljYV8xCmBgYAoKQ29uc3RydXlhbW9zIGxvcyBncsOhZmljb3MgZGUgZGVuc2lkYWQKCmBgYHtyfQpncmFmaWNhXzIgPC0gZ2dwbG90KHRhcmVhXzEsIGFlcyh4PWFnZSwgZmlsbD0gbWFsZSwgY29sb3VyID0gbWFsZSkpKwogIGdlb21fZGVuc2l0eShhbHBoYT0wLjEpKwogIHhsaW0oMzAsNzUpICsKICBsYWJzKHg9IkVkYWQiLCB5PSAiRGVuc2lkYWQiLAogICAgICAgdGl0bGUgPSAiRGVuc2lkYWQgZGUgZGF0b3MgZWRhZCIsCiAgICAgICBjYXB0aW9uID0gIlRleHRvIGRlIHBydWViYSBjb21vIHBpZSBkZSBmaWd1cmEiKSArCiAgYW5ub3RhdGUoInRleHQiLCB4ID0gNjUsIHkgPSAwLjAzNSwgbGFiZWwgPSAiU29tZSB0ZXh0IikKZ3JhZmljYV8yCmBgYAoKIyMjIFJldmlzYXIgbGEgYXNpbWV0csOtYSB5IGxhIGN1cnRvc2lzCgpgYGB7cn0KaW5zdGFsbC5wYWNrYWdlcygicHN5Y2giKQppbnN0YWxsLnBhY2thZ2VzKCJjYXIiKQppbnN0YWxsLnBhY2thZ2VzKCJub3J0ZXN0IikKbGlicmFyeShwc3ljaCkKbGlicmFyeShjYXIpICAgICAgICAjUGFxdWV0ZXLDrWEgcGFyYSBldmFsdWFyIGRpc3RyaWJ1Y2nDs24gZGUgZGF0b3MKbGlicmFyeShjYXJEYXRhKQpsaWJyYXJ5KG5vcnRlc3QpICAgICMKYGBgCgpMYSBjdXJ0b3NpcyBpZGVhbCBkZWJlcsOtYSBzZXIgY2VyY2FubyBhIDAgcGFyYSBkZWNpciBxdWUgZXMKKm1lc29jw7pydGljYSouIEVzIGRlY2lyLAoKLSAgIGsgPSAwIC1cPiBtZXNvY8O6cnRpY2EKLSAgIGsgXD4gMCAtXD4gbGVwdG9jw7pydGljYQotICAgayBcPCAwIC1cPiBwbGF0aWPDunJ0aWNhCgpQYXJhIGNhbGN1bGFyIGxhIGFzaW1ldHLDrWEsIGRlY2ltb3MgcXVlCgotICAgYSA9IDAgKHNpbcOpdHJpY28pCi0gICBhIFw+IDAgKGFzaW1ldHLDrWEgcG9zaXRpdmEpCi0gICBhIFw8IDAgKGFzaW1ldHLDrWEgbmVnYXRpdmEpCgpQYXJhIGNhbGN1bGFyIGxhIGN1cnRvc2lzIHV0aWxpemFtb3MgZWwgcGFxdWV0ZSBgcHN5Y2hgCgpgYGB7cn0Ka3VydG9zaSh0YXJlYV8xJGFnZSkKYGBgCgpDdWFuZG8gZWwgdmFsb3IgZGUgY3VydG9zaXMgZXMgXDwgMiBvIFw+IDIsIGVudG9uY2VzICoqbm8gdGllbmUgdW4gY29tcG9ydGFtaWVudG8gbm9ybWFsKioKCkNhbGN1bGFtb3MgbGEgY3VydG9zaXMgeSBlbCBzZXNnbyBwYXJhIGhvbWJyZXMgeSBwYXJhIG11amVyZXMKCmBgYHtyfQojUGFyYSBjYWxjdWxhciBsYSBjdXJ0b3NpcwprdXJ0b3NpKHRhcmVhXzEkYWdlW3RhcmVhXzEkbWFsZSA9PSAiSCJdKQprdXJ0b3NpKHRhcmVhXzEkYWdlW3RhcmVhXzEkbWFsZSA9PSAiTSJdKQoKI1BhcmEgY2FsY3VsYXIgZWwgc2VzZ28gKHNpbWV0csOtYSBvIGFzaW1ldHLDrWEpCnNrZXcodGFyZWFfMSRhZ2UpCnNrZXcodGFyZWFfMSRhZ2VbdGFyZWFfMSRtYWxlID09ICJIIl0pCnNrZXcodGFyZWFfMSRhZ2VbdGFyZWFfMSRtYWxlID09ICJNIl0pCmBgYApWZWFtb3MgbG9zIFFRLXBsb3QKCmBgYHtyfQpxcVBsb3QodGFyZWFfMSRhZ2UsCiAgICAgICBtYWluID0gIlFRIHBsb3QgZWRhZCIsCiAgICAgICB5bGFiID0gIkVkYWQgKGHDsW9zKSIsCiAgICAgICB4bGFiID0gIkN1YW50aWxlcyIpCmBgYApDb21iaW5hciBhbsOhbGlzaXMgZGUgYXNpbWV0csOtYSBjb24gaGlzdG9ncmFtYSwgeSBjdXJ0b3NpcyBjb24gUVEtcGxvdC4gTG9zIFFRcGxvdCBwZXJtaXRlbiBvYnNlcnZhciBlbCBwZXNvIGRlIGxhcyBjb2xhcy4gU2kgZXMgdW5hIGRpc3RyaWJ1Y2nDs24gbm9ybWFsLCBlbnRvbmNlcyBzZSBkZWJlcsOtYSBkZSBjb21wb3J0YXIgY29tbyB1bmEgbMOtbmVhIHJlY3RhLiAKClBvZGVtb3MgcmVhbGl6YXIgZWwgUVFwbG90IGVzdHJhdGlmaWNhZG8gcG9yIGhvbWJyZXMgeSBtdWplcmVzLiAKYGBge3J9CnFxUGxvdCh0YXJlYV8xJGFnZVt0YXJlYV8xJG1hbGUgPT0gIkgiXSwKICAgICAgIG1haW4gPSAiUVEgcGxvdCBlZGFkIiwKICAgICAgIHlsYWIgPSAiRWRhZCAoYcOxb3MpIiwKICAgICAgIHhsYWIgPSAiQ3VhbnRpbGVzIiwKICAgICAgIGNvbCA9ICJibHVlIiwKICAgICAgIGNvbC5saW5lcyA9ICJyZWQiKQpgYGAKCkdyw6FmaWNvcyBkZSBjYWphcyB5IGJpZ290ZXMgc29uIGltcG9ydGFudGVzIHBhcmEgZWwgYW7DoWxpc2lzCmBgYHtyfQpib3hwbG90KHRhcmVhXzEkYWdlKSAjcGFyYSB0b2RvcyBsb3MgZGF0b3MKYm94cGxvdCh0YXJlYV8xJGFnZSB+IHRhcmVhXzEkbWFsZSwKICAgICAgICBtYWluID0gIkNvbXBhcmFjacOzbiBkZSBlZGFkIEggdnMgTSIsCiAgICAgICAgeWxhYiA9ICJFZGFkIChhw7FvcykiLAogICAgICAgIHhsYWIgPSAiR3J1cG9zIiwKICAgICAgICBjb2wgPSBjKCJsaWdodGdyZWVuIiwgInB1cnBsZSIpKSAjZXN0cmF0aWZpY2FkbyBwb3Igc2V4bwpgYGAKVXRpbGl6YW5kbyBgZ2dwbG90YApgYGB7cn0KcCA8LSBnZ3Bsb3QodGFyZWFfMSwgYWVzKHggPSBtYWxlLCB5ID0gYWdlLCBmaWxsID0gbWFsZSkpKwogIGdlb21fYm94cGxvdCgpKwogIGxhYnMoeD0iR3J1cG9zIiwgeT0gIkVkYWQiLAogICAgICAgdGl0bGUgPSAiQ29tcGFyYWNpw7NuIGRlIGVkYWQgSCB2cyBNIiwKICAgICAgIGNhcHRpb24gPSAiVGV4dG8gZGUgcHJ1ZWJhIGNvbW8gcGllIGRlIGZpZ3VyYSIpCnAKYGBgCiMjIyBQcnVlYmFzIGRlIG5vcm1hbGlkYWQKCi0gU2hhcGlyby1XaWxrczogU2UgdXRpbGl6YSBjdWFuZG8gKm4qIDwgNTAgKGluY2x1c28gbWVub3IgYSA0MCkuIEN1YW5kbyBoYXkgbWVub3MgZGUgMTAgZGF0b3MgeWEgbm8gc2UgcmVjb21pZW5kYSB1c2FyLiAKLSBLb2xtb2dvcm92LVNtaXJub3Y6IFNlIHV0aWxpemEgY3VhbmRvICpuKiA+IDUwCgpQYXJhIHJlYWxpemFyIGxhIGRlIEtvbG1vZ29yb3YtU21pcm5vdi4uLkNvbiBsb3MgcmVxdWlzaXRvcyBtw61uaW1vClRpZW5lIGNvbW8KJEhfMCQ6IExhIGRpc3RyaWJ1Y2nDs24gcmVhbCB5IGxhIGRpc3RyaWJ1Y2nDs24gdGXDs3JpY2Egc29uICoqaWd1YWxlcyBvIHNpbWlsYXJlcyAobm9ybWFsaWRhZCkqKgokSF9hJDogTGEgZGlzdHJpYnVjacOzbiByZWFsIHkgbGEgZGlzdHJpYnVjacOzbiB0ZcOzcmljYSBzb24gKipkaWZlcmVudGVzIChubyBub3JtYWxpZGFkKSoqCgpDdWFuZG8gKnAgPiAwLjA1KiBubyBzZSByZWNoYXphIGxhICoqJEhfMCQqKgoKKipPSk86IHlhIG5vIHNlIHVzYSBhc8OtLCBzZSB1c2EgdW5hIG1lam9yYWRhKioKYGBge3J9CmtzLnRlc3QodGFyZWFfMSRhZ2UsICJwbm9ybSIsCiAgICAgICAgbWVhbiA9IDQ5LjYsIHNkID0gOC41NywKICAgICAgICBhbHRlcm5hdGl2ZSA9ICJ0d28uc2lkZWQiKQoKbWVhbih0YXJlYV8xJGFnZSkKc2QodGFyZWFfMSRhZ2UpCmBgYApDb21vIGVsIGVsIHZhbG9yIGRlICpwKiBvYnRlbmlkbyBlcyA8IDAuMDUgKHAtdmFsdWUgPCAyZS0xNiksIHNlIHJlY2hhemEgbGEgaGlww7N0ZXNpcyBudWxhIHkgY29uY2x1aW1vcyBxdWUgbGEgbXVlc3RyYSBubyBwcm92aWVuZSBkZSB1bmEgZGlzdHJpYnVjacOzbiBub3JtYWwuIAoKTGEgcHJ1ZWJhIGRlIEtvbG1vZ29yb3YtU21pcm5vdi1MaWxsaWVmb3JzIGVzIGxhIGNvcnJlZ2lkYQpgYGB7cn0KIyBTZSB1dGlsaXphIGxhIGxpYnJlcsOtYSBkZSBub3J0ZXN0CmxpbGxpZS50ZXN0KHRhcmVhXzEkYWdlW3RhcmVhXzEkbWFsZSA9PSAiSCJdKQpgYGAKUGFyYSBoYWNlciBsYSBkZSBTaGFwaXJvLVdpbGsKYGBge3J9CnNoYXBpcm8udGVzdCh0YXJlYV8xJGFnZVt0YXJlYV8xJG1hbGUgPT0gIkgiXSkKc2hhcGlyby50ZXN0KHRhcmVhXzEkYWdlW3RhcmVhXzEkbWFsZSA9PSAiTSJdKQpgYGAKCiMjIDQuIFBydWViYXMgZGUgY29udHJhc3RlIGVzdGFkw61zdGljbwoKIyMjIFBydWViYXMgcGFyYW3DqXRyaWNhcyAodCBkZSBzdHVkZW50IHkgQU5PVkEpClJlcXVpZXJlbiBjdW1wbGlyIHZhcmlvcyBzdXB1ZXN0b3MKCi0gTm9ybWFsaWRhZAotIElndWFsZGFkIGRlIHZhcmlhbnphcyAoUHJ1ZWJhIGRlIExldmVuZSk6IFJlcXVpZXJlIGVsIHBsYW50ZWFtaWVudG8gZGUgbGFzIHNpZ3VpZW50ZXMgaGlww7N0ZXNpcwogIC0gJEhfMCQ6IGxhIHZhcmlhbnphIGRlIGxhIHZhcmlhYmxlIGVzIGlndWFsIG8gc2VtZWphbnRlIGVuIGNhc29zIHkgY29udHJvbGVzICgkcD4wLjA1JCkKICAtICRIX2EkOiBsYXMgdmFyaWFuemFzIHNvbiBkaWZlcmVudGVzIGVudHJlIGdydXBvcyAoJHA8MC4wNSQpCgpMYXMgaGlwb3Rlc2lzIHBsYW50ZWFkYXMgZW4gbGFzIHBydWViYXMgZGUgY29tcGFyYWNpw7NuIGRlIG1lZGlhcyBzb246CgotICRIXzA6IFxtdV8xID0gXG11XzIkCi0gJEhfYTogXG11XzEgXG5lcSBcbXVfMiQKCgojIyBQcnVlYmFzIG5vIHBhcmFtw6l0cmljYXMKLSBTaWdub3MgZGUgV2lsY294b24KLSBVIGRlIE1hbm4tV2hpdG5leQotIEZyaWVkbWFuCi0gS3J1c2thbC1XYWxsaXMKCgpFbXBlY2Vtb3MgY29uIGxhcyBwcnVlYmFzIHBhcmFtw6l0cmljYXMKCkNhcmdhbW9zIG51ZXN0cmEgYmFzZSBkZSBkYXRvcwpgYGB7cn0Kc2VzaW9uXzQgPC0gcmVhZC5jc3YoIi9jbG91ZC9wcm9qZWN0L0Jpb2VzdGFkaXN0aWNhX1IvZGF0b3NfMS5jc3YiKQpWaWV3KHNlc2lvbl80KQpgYGAKClBhc29zIGEgc2VndWlyIHBhcmEgcmVhbGl6YXIgbGEgcHJ1ZWJhOgoKMS4gUmVhbGl6YXIgZWwgYW7DoWxpc2lzIGRlIG5vcm1hbGlkYWQKMi4gQW7DoWxpc2lzIGRlIGlndWFsZGFkIHZhcmlhbnphIChQcnVlYmEgZGUgTGV2ZW5lKQozLiBQcnVlYmEgZGUgdCBkZSBzdHVkZW50CjQuIEPDoWxjdWxvIGRlIHRhbWHDsW8gZGUgZWZlY3RvCjUuIENvbmNsdXNpw7NuCiAKCkNvbXBhcmVtb3MgbGEgdGFsbGEgZW50cmUgY2Fzb3MgeSBjb250cm9sZXMuIMK/RXhpc3RlIGRpZmVyZW5jaWEgZW4gZXN0YSB2YXJpYWJsZSBlbnRyZSBhbWJvcyBncnVwb3M/CgoxLiBBbsOhbGlzaXMgZGUgbm9ybWFsaWRhZApgYGB7cn0Kc3RyKHNlc2lvbl80KQpzZXNpb25fNCRFc3RhdHVzIDwtIGFzLmZhY3RvcihzZXNpb25fNCRFc3RhdHVzKQpzdW1tYXJ5KHNlc2lvbl80JEVzdGF0dXMpICAjU29sbyBzaXJ2ZSBwYXJhIHZhcmlhYmxlcyBjdWFudGl0YXRpdmFzIG8gZGUgdGlwbyBmYWN0b3IKCmBgYApEYWRvIHF1ZSB0ZW5lbW9zID4gNTAgb2JzZXJ2YWNpb25lcyBwYXJhIGNhZGEgZ3J1cG8sIHV0aWxpemFtb3MgbGEgcHJ1ZWJhIGRlIExpbGxpZWZvcnMuIEVsIGFuw6FsaXNpcyBkZWJlIGRlIHNlciByZWFsaXphZG8gcGFyYSBjYWRhIGdydXBvIHBvciBzZXBhcmFkbywgcmVjb3JkYW5kbyBxdWUKCiRIXzAkOiBMYSBkaXN0cmlidWNpw7NuIHJlYWwgeSB0ZcOzcmljYSBzb24gaWd1YWxlcyAoJHAgPiAwLjA1KQokSF9hJDogTGEgZGlzdHJpYnVjacOzbiByZWFsIHkgdGXDs3JpY2Egc29uIGRpZmVyZW50ZXMgKCRwIDwgMC4wNSkKCmBgYHtyfQpsaWxsaWUudGVzdChzZXNpb25fNCRUYWxsYVtzZXNpb25fNCRFc3RhdHVzID09ICJDb250cm9sIl0pCmxpbGxpZS50ZXN0KHNlc2lvbl80JFRhbGxhW3Nlc2lvbl80JEVzdGF0dXMgPT0gIkNhc28iXSkKYGBgCgpEYWRvIHF1ZSAkcC12YWx1ZSA8IDAuMDUkLCBzZSByZWNvbWllbmRhIGFuw6FsaXNpcyBubyBwYXJhbcOpdHJpY28gcGFyYSBjb21wYXJhciBncnVwb3MuCgoyLiBBaG9yYSB2ZXJpZmljYW1vcyBlbCBhbsOhbGlzaXMgZGUgaWd1YWxkYWQgZGUgdmFyaWFuemEgKFBydWViYSBkZSBMZXZlbmUpCiRIXzAkOiBMYXMgdmFyaWFuemFzIGVudHJlIGdydXBvcyBzb24gaWd1YWxlcyBvIHNpbWlsYXJlcyAoJHAgPiAwLjA1KQokSF9hJDogTGFzIHZhcmlhbnphcyBlbnRyZSBncnVwb3Mgc29uIGRpZmVyZW50ZXMgKCRwIDwgMC4wNSkKCmBgYHtyfQpsZXZlbmVUZXN0KHNlc2lvbl80JFRhbGxhIH4gc2VzaW9uXzQkRXN0YXR1cywKICAgICAgICAgICAgY2VudGVyID0gIm1lZGlhbiIpICNDb21vIG5vIHNlIGN1bXBsZSBsYSBub3JtYWxpZGFkLCBlbCBwYXLDoW1ldHJvIHF1ZSBjb3JyZXNwb25kZSBlcyBsYSBtZWRpYW5hCgpsZXZlbmVUZXN0KHNlc2lvbl80JFRhbGxhIH4gc2VzaW9uXzQkRXN0YXR1cywKICAgICAgICAgICAgY2VudGVyID0gIm1lYW4iKQpgYGAKRWwgKnAtdmFsdWUqICgqKlByKD5GKSoqKSA+IDAuMDUsIGVudG9uY2VzIHNlIGFjZXB0YSBsYSAkSF8wJC4gTGFzIHZhcmlhbnphcyBlbnRyZSBncnVwb3Mgc29uIGlndWFsZXMgbyBzaW1pbGFyZXMuCgozLiBBaG9yYSByZWFsaXphbW9zIGxhIHBydWViYSBkZSBUCgpMYXMgaGlww7N0ZXNpcyBzb24KCiRIXzA6IFxtdV8xID0gXG11XzIkCiRIX2E6IFxtdV8xIFxuZXEgXG11XzIkCgpgYGB7cn0KCiNTaSB2YXIuZXEgPSBUUlVFLCBlbnRvbmNlcyBoYWNlIGxhIHQgZGUgc3R1ZGVudCBub3JtYWwKdC50ZXN0KHNlc2lvbl80JFRhbGxhIH4gc2VzaW9uXzQkRXN0YXR1cywKICAgICAgIGFsdGVybmF0aXZlID0gInR3by5zaWRlZCIsICNwdWVkZSBzZXIgZ3JlYXRlciBvIGxlc3MsIGRlcGVuZGllbmRvIGRlIHNpIGVzIHVuYSBjb2xhCiAgICAgICBjb25mLmxldmVsID0gMC45NSwKICAgICAgIHZhci5lcXVhbCA9IFRSVUUsCiAgICAgICBwYWlyZWQgPSBGQUxTRSkKCiNTaSB2YXIuZXEgPSBGQUxTRSwgZW50b25jZXMgaGFjZSBsYSBjb3JyZWNjacOzbiBkZSBXZWxjaCBwb3JxdWUgbGFzIHZhcmlhbnphcyBlbnRyZSBncnVwb3Mgc29uIGRpZmVyZW50ZXMKI0VzdGEgcHJ1ZWJhIGVzIHBhcmEgdmFyaWFuemFzIE5PIGlndWFsZXMKdC50ZXN0KHNlc2lvbl80JFRhbGxhIH4gc2VzaW9uXzQkRXN0YXR1cywKICAgICAgIGFsdGVybmF0aXZlID0gInR3by5zaWRlZCIsCiAgICAgICBjb25mLmxldmVsID0gMC45NSwKICAgICAgIHZhci5lcXVhbCA9IEZBTFNFLAogICAgICAgcGFpcmVkID0gRkFMU0UpCgojUHLDoWN0aWNhbWVudGUgZXN0YSBlcyBpZ3VhbCBhIGxhIGFudGVyaW9yLCBjdWFuZG8gdmFyLmVxdWFsID0gRkFMU0UKdC50ZXN0KHNlc2lvbl80JFRhbGxhIH4gc2VzaW9uXzQkRXN0YXR1cykKYGBgCgpFbCBpbnRlcnZhbG8gZGUgY29uZmlhbnphIChJQykgc29uIC0wLjAzMjQgeSAwLjAxNjQKCkRhZG8gcXVlIGVsICpwLXZhbG9yKiAoMC41KSA+IDAuMDUsIGVudG9uY2VzIG5vIHNlIHJlY2hhemEgbGEgJEhfMCQgeSBsYXMgbWVkaWFzIGVudHJlIGFtYm9zIGdydXBvcyBzb24gaWd1YWxlcy4gCkVsIGPDoWxjdWxvIGRlbCB0YW1hw7FvIGRlbCBlZmVjdG8gKipOTyoqIHNlIGFwbGljYSBjdWFuZG8gcCA+IDAuMDUsIHBlcm8gdmFtb3MgYSB2ZXJpZmljYXJsbwoKYGBge3J9CmNvaGVuLmQoc2VzaW9uXzQkVGFsbGEsIHNlc2lvbl80JEVzdGF0dXMpCiNPYnNlcnZhciBlbCB2YWxvciBxdWUgZGljZSBlZmZlY3QKCiMgQ3VhbmRvCiNkIDwgMC4yOiBwZXF1ZcOxbwojZCA8IDAuODogbWVkaWFubwojZCA+IDAuODogZ3JhbmRlCmBgYApPdHJhIG9wY2nDs24gZGUgYW7DoWxpcyB1c2FuZG8gYHJzdGF0aXhgCmBgYHtyfQpsaWJyYXJ5KHJzdGF0aXgpCmxpYnJhcnkodGlkeXZlcnNlKQp2YXJfaWd1YWxkYWQgPC0gc2VzaW9uXzQgJT4lIAogIGxldmVuZV90ZXN0KFRhbGxhIH4gRXN0YXR1cywgY2VudGVyID0gIm1lZGlhbiIpCgplZmVjdG8gPC0gc2VzaW9uXzQgJT4lIAogIGNvaGVuc19kKFRhbGxhIH4gRXN0YXR1cykKYGBgCgoKIyMgNS4gQU5PVkEgKFNlc2nDs24gNSkKCkNhcmdhbW9zIGxhIGJhc2UgZGUgZGF0b3MKYGBge3J9CnB1bG1vbmFyIDwtIHJlYWQuY3N2KCIvY2xvdWQvcHJvamVjdC9CaW9lc3RhZGlzdGljYV9SL2VuZmVybWVkYWRfcHVsbW9uYXIuY3N2IikKaGVhZChwdWxtb25hcikKCiNDaGVjYXIgZWwgYXJjaGl2byBxdWUgZXN0w6kgZ3VhcmRhZG8gY29tbyBDU1YgVVRGLTgKYGBgCgpQcmVwYXJhY2nDs24gZGUgbGEgYmFzZSBkZSBkYXRvcwpgYGB7cn0Kc3RyKHB1bG1vbmFyKQpwdWxtb25hciRFc3RhdHVzIDwtIGFzLmZhY3RvcihwdWxtb25hciRFc3RhdHVzKQpwdWxtb25hciREb3NpcyA8LSBhcy5mYWN0b3IocHVsbW9uYXIkRG9zaXMpCnB1bG1vbmFyJFNleG8gPC0gYXMuZmFjdG9yKHB1bG1vbmFyJFNleG8pCgpzdHIocHVsbW9uYXIpCmhlYWQocHVsbW9uYXIpCmBgYApFbXBlY2Vtb3MgY29uIGVsIHByb2Nlc28gZGUgYW7DoWxpc2lzLiAKCjEuIFByaW1lcm8gY29udGVtb3MgZWwgbsO6bWVybyBkZSBkYXRvcywgKm4qLgpgYGB7cn0KbGlicmFyeSh0aWR5dmVyc2UpCgpwdWxtb25hciAlPiUgCiAgY291bnQoRG9zaXMpCgpgYGAKRGFkbyBxdWUgcGFyYSBjYWRhIHVubyBkZSBsb3MgZ3J1cG9zIHN1ICpuKiBlcyA8IDUwLCB1dGlsaXphbW9zIGxhIHBydWViYSBkZSBTaGFwaXJvLVdpbGtzCgpgYGB7cn0KbGlicmFyeShyc3RhdGl4KQoKIyBTb2xvIGxhIHBydWViYSBkZSBTaGFwaXJvIHB1ZWRlIGFuYWxpemFyc2UgbWVkaWFudGUgZXN0ZSBwcm9jZXNvCm5vcm1hbGlkYWQgPC0gcHVsbW9uYXIgJT4lIAogIGdyb3VwX2J5KERvc2lzKSAlPiUgCiAgc2hhcGlyb190ZXN0KFB2Q08yKSAgICAgICAjUG9kZW1vcyByZWFsaXphciBsYSBwcnVlYmEgcGFyYSBtw6FzIGRlIHVuYSB2YXJpYWJsZQoKbm9ybWFsaWRhZAoKcXFub3JtKHB1bG1vbmFyJFB2Q08yKQpxcWxpbmUocHVsbW9uYXIkUHZDTzIpCgoKYGBgCkxhcyBoaXDDs3Rlc2lzIHNvbgoKLSAkSF8wJDogTGEgZGlzdHJpYnVjacOzbiByZWFsICQ9JCBsYSBkaXN0cmlidWNpw7NuIHRlw7NyaWNhICAqKihub3JtYWxpZGFkLCBwLXZhbHVlID4gMC4wNSkqKgotICRIX2EkOiBMYSBkaXN0cmlidWNpw7NuIHJlYWwgJFxuZXEkIGxhIGRpc3RyaWJ1Y2nDs24gdGXDs3JpY2Egc29uICoqKG5vIG5vcm1hbGlkYWQsIHAtdmFsdWUgPCAwLjA1KSoqCgpEYWRvIHF1ZSBlbCBhbsOhbGlzaXMgZGUgbm9ybWFsaWRhZCBpbmRpY8OzIHF1ZSBlbCB2YWxvciAqcCogZGUgbGEgdmFyaWFibGUgKlB2Q08yKiBwYXJhIGNhZGEgdW5vIGRlIGxvcyBncnVwb3MgZnVlIG1lbm9yIHF1ZSAwLjA1LCBlbnRvbmNlcyBzZSByZWNoYXphIGxhIGhpcMOzdGVzaXMgbnVsYSB5IGFjZXB0YW1vcyBsYSAkSF9hJCwgcXVlIGVzIHF1ZSBubyBwcm92aWVuZW4gZGUgdW5hIGRpc3RyaWJ1Y2nDs24gbm9ybWFsLiBBaG9yaXRhIG9taXRpbW9zIHF1ZSBzZWFuICpubyBub3JtYWxlcyouIFBhcmEgcmVhbGl6YXIgbGEgQU5PVkEsIHRvZG9zIGxvcyBncnVwb3MgZGViZW4gY3VtcGxpciBlbCBzdXB1ZXN0byBkZSBub3JtYWxpZGFkCgpFTCBzaWd1aWVudGUgcGFzbyBlcyBjb21wcm9iYXIgbGEgaWd1YWxkYWQgZGUgdmFyaWFuemFzCgoyLiBJZ3VhbGRhZCBkZSB2YXJpYW56YXMuIFBhcmEgZXN0ZSBjYXNvIHNlIHVzYSBsYSBwYXF1ZXRlcsOtYSBgY2FyYCB5IGBjYXJEYXRhYApgYGB7cn0KbGlicmFyeShjYXIpCmxpYnJhcnkoY2FyRGF0YSkKCmxldmVuZVRlc3QocHVsbW9uYXIkUHZDTzIgfiBwdWxtb25hciREb3NpcywgCiAgICAgICAgICAgY2VudGVyID0gIm1lZGlhbiIpCgoKYGBgCkxhcyBoaXDDs3Rlc2lzIHNvbiAKLSAkSF8wJDogTGFzIHZhcmlhbnphcyBkZSBQdkNPMiAkPSQgZW4gdG9kb3MgbG9zIGdydXBvcyAqKihwLXZhbHVlID4gMC4wNSkqKgotICRIX2EkOiBMYXMgdmFyaWFuemFzIGRlIFB2Q08yICAkXG5lcSQgZW4gYWwgbWVub3MgdW4gZ3J1cG8gKioobm8gbm9ybWFsaWRhZCwgcC12YWx1ZSA8IDAuMDUpKioKClVzYW5kbyBlbCBwYXF1ZXRlIGByc3RhdGl4YApgYGB7cn0KcHVsbW9uYXIgJT4lIAogIGxldmVuZV90ZXN0KFB2Q08yIH4gRG9zaXMsIGNlbnRlciA9ICJtZWRpYW4iKQoKIyBEZjogZGVncmVlcyBvZiBmcmVlZG9tCiMgRiB2YWx1ZTogdmFsb3IgZGVsIGVzdGFkw61zdGljbyBGCiMgcDogcC12YWx1ZQpgYGAKCkRhZG8gcXVlIGVsICpwLXZhbHVlKiBlcyA+IDAuMDUgKCpwKiA9IDAuNjY3KSwgZW50b25jZXMgbGEgY29uY2x1c2nDs24gZGVsIGFuYWxpc2lzIGRlIGlndWFsZGFkIGRlIHZhcmlhbnphcyBlcyBxdWUgbGFzIHZhcmlhbnphcyBzb24gc2ltaWxhcmVzIG8gaWd1YWxlcy4gUHJvY2VkZW1vcyBhIHJlYWxpemFzIGxhIHBydWViYSBkZSBBTk9WQQoKCjMuIEFOT1ZBCkxhcyBoaXDDs3Rlc2lzIHNvbgoKLSAkSF8wOiBcbXVfMSA9IFxtdV8yID0gXG11XzMkCi0gJEhfYTogXG11XzEgXG5lcSBcbXVfMiBcbmVxIFxtdV8zJDogCgpMYSAkSF9hJCBkaWNlIHF1ZSBoYXkgZGlmZXJlbmNpYXMgZW4gbGEgbWVkaWEgZGUgUHZDTzIgZW50cmUgYWxndW5vIGRlIGxvcyBncnVwb3MgCgpgYGB7cn0KYW5vdmEgPC0gcHVsbW9uYXIgJT4lIAogIGFub3ZhX3Rlc3QoUHZDTzIgfiBEb3NpcywgZWZmZWN0LnNpemUgPSAiZ2VzIikKCmFub3ZhCmBgYApEYWRvIHF1ZSAqcCA9IDAuMjUqLCBxdWUgZXMgPiAwLjA1LCBlbnRvbmNlcyBhY2VwdGFtb3MgbGEgJEhfMCQgeSBkaXLDrWFtb3MgcXVlIG5vIGhheSBkaWZlcmVuY2lhIGVudHJlIGdydXBvcy4gSGFyZW1vcyBsYXMgcHJ1ZWJhcyBwb3N0LWhvYyAKCmBgYHtyfQphbm92YSA8LSBwdWxtb25hciAlPiUgCiAgdHVrZXlfaHNkKFB2Q08yIH4gRG9zaXMpICModmFyaWFibGUgY3VhbnRpdGF0aXZhIH4gdmFyaWFibGUgZGUgYWdydXBhbWllbnRvKQoKYW5vdmEKYGBgCgpGaW5hbG1lbnRlLCByZWFsaXphbW9zIGxhIGNvbmNsdXNpw7NuCgo1LiBMb3MgdHJhdGFtaWVudG9zIG5vIHByb3ZvY2FuIHVuYSBkaWZlcmVuY2lhIGVuIGxhIFB2Q08yIChwLXZhbHVlID0gMC4yNSkKCgojIyMgQU5PVkEgZGUgMiB2w61hcwoKRWwgYW7DoWxpc2lzIGNvbnNpc3RlCgogKioxLiBOb3JtYWxpZGFkKioKYGBge3J9CgpwdWxtb25hciAlPiUgCiAgZ3JvdXBfYnkoRG9zaXMpICU+JSAKICBzaGFwaXJvX3Rlc3QoTmV1dHJvZmlsb3MpCgpzdW1tYXJ5KHB1bG1vbmFyJEVzdGF0dXMpCgojUGFyYSBsb3MgY2Fzb3Mgbj00MCwgdXNhbW9zIGxhIGRlIFNoYXBpcm8tV2lsa3MKc2hhcGlyb190ZXN0KHB1bG1vbmFyJE5ldXRyb2ZpbG9zW3B1bG1vbmFyJEVzdGF0dXMgPT0gIkNhc2UiXSkKCgojUGFyYSBjb250cm9sZXMgbj05MCB1c2Ftb3MgbGEgZGUgbGlsbGllZm9yY2UKbGlicmFyeShub3J0ZXN0KQpsaWxsaWUudGVzdChwdWxtb25hciROZXV0cm9maWxvc1twdWxtb25hciRFc3RhdHVzID09ICJDb250cm9sIl0pCgpgYGAKUGFyYSBlbCAlIGRlIG5ldXRyb2ZpbG9zIGVzdHJhdGlmaWNhZG8gcG9yIGVzdGF0dXMgY3VtcGxlbiBsYSBub3JtYWxpZGFkLiBTSW4gZW1iYXJnbywgYWwgZXN0cmF0aWZpY2FyIHBvciBlbCB0cmF0YW1pZW50byBubyBjdW1wbGUuCgpgYGB7cn0Kbm9ybWFsaWRhZF9jb25kaWNpb25hZGEgPC0gcHVsbW9uYXIgJT4lIAogIGdyb3VwX2J5KERvc2lzLCBFc3RhdHVzKSAlPiUgCiAgc2hhcGlyb190ZXN0KE5ldXRyb2ZpbG9zKQpgYGAKCkN1YW5kbyBzZSBjb25kaWNpb25hIGVsIGFuw6FsaXNpcyBkZSBub3JtYWxpZGFkIGRlIGRvcyBmYWN0b3JlcywgZWwgJSBkZSBuZXV0csOzZmlsb3MgY3VtcGxlIGNvbiBlbCBzdXB1ZXN0byBkZSBub3JtYWxpZGFkCgogKioyLiBJZ3VhbGRhZCBkZSB2YXJpYW56YXMqKgpgYGB7cn0KcHVsbW9uYXIgJT4lIAogIGxldmVuZV90ZXN0KE5ldXRyb2ZpbG9zIH4gRXN0YXR1cywgY2VudGVyID0gIm1lYW4iKQpgYGAKYGBge3J9CnB1bG1vbmFyICU+JSAKICBsZXZlbmVfdGVzdChOZXV0cm9maWxvcyB+IERvc2lzLCBjZW50ZXIgPSAibWVkaWFuIikKYGBgCkxhIHZhcmlhbnphIGRlICUgZGUgbmV1dHLDs2ZpbG9zIGVzIGRpZmVyZW50ZSBlbnRyZSBsb3MgZ3J1cG9zIChFc3RhdHVzKS4gTGEgdmFyaWFuemEgZGUgbmV1dHLDs2ZpbG9zIGVzIGlndWFsIGVudHJlIGdydXBvcyAoRG9zaXMpIAogCiAqKjMuIEFOT1ZBKiogCmBgYHtyfQpyZXN1bHRfMnZpYXMgPC0gcHVsbW9uYXIgJT4lIAogIGFub3ZhX3Rlc3QoTmV1dHJvZmlsb3MgfiBEb3NpcypFc3RhdHVzLCAKICAgICAgICAgICAgIGVmZmVjdC5zaXplID0gImdlcyIpICNFdGEgY3VhZHJhZGEgZ2VuZXJhbGl6YWRhCmBgYAoKQ29uIGVsIGFuw6FsaXNpcyBwb3N0LWhvYwpgYGB7cn0KcHBzdF8ydmlhcyA8LXB1bG1vbmFyICU+JSAKICB0dWtleV9oc2QoTmV1dHJvZmlsb3MgfiBEb3NpcypFc3RhdHVzKQpgYGAKIAogKio0LiBDb25jbHVzacOzbioqCkFuYWxpemFuZG8gbG9zIHN1YmdydXBvcyBkb3NpczpjYXNvcyBzZSBlbmNvbnRyw7MgcXVlIGFsIGNvbXBhcmFyIGxvcyBtw7psdGlwbGVzIHRyYXRhbWllbnRvcyBlbnRyZSBjYXNvcyB5IGNvbnRyb2xlcywgbm8gaGF5IGRpZmVyZW5jaWFzIHNpZ25pZmljYXRpdmFzLiBBbCByZWFsaXphciB1biBhbsOhbGlzaXMgaW5kaXZpZHVhbCBkZSBjYWRhIGZhY3Rvciwgc2UgZGV0ZWN0w7MgdW5hIGdyYW4gZGlmZXJlbmNpYSBlbiBlbCBmYWN0b3IgKipFc3RhdHVzICoqICgqcCogPCAwLjAxLCBnZXMgPSAwLjY2KSwgcG9yIGxvIHF1ZSBzZSBzb3NwZWNoYSBxdWUgbGFzIGRpZmVyZW5jaWFzIHF1ZSBwdWVkYW4gZW5jb250cmFyc2Ugc29uIGF0cmlidWlibGVzIGFsIGZhY3RvciAqKkVzdGF0dXMqKi4KCgoKIyMjIEFOT1ZBIGRlIDIgdsOtYXMgZGUgbWVkaWRhcyByZXBldGlkYXMKCkNhcmdhbW9zIGxhIGJhc2UgZGUgZGF0b3MgeSBjb252ZXJ0aW1vcyBsYXMgdmFyaWFibGVzIGNhcmFjdGVyIGEgZmFjdG9yLiAKCmBgYHtyfQpzZWd1aW1pZW50byA8LSByZWFkLmNzdigiL2Nsb3VkL3Byb2plY3QvQmlvZXN0YWRpc3RpY2FfUi9TZWd1aW1pZW50b190ZXJhcGlhLmNzdiIpCmhlYWQoc2VndWltaWVudG8pCnN0cihzZWd1aW1pZW50bykKCnNlZ3VpbWllbnRvJFRpZW1wbyA8LSBhcy5mYWN0b3Ioc2VndWltaWVudG8kVGllbXBvKQpzZWd1aW1pZW50byRTZXhvIDwtIGFzLmZhY3RvcihzZWd1aW1pZW50byRTZXhvKQpzZWd1aW1pZW50byRGdW1hZG9yZXMgPC0gYXMuZmFjdG9yKHNlZ3VpbWllbnRvJEZ1bWFkb3JlcykKc2VndWltaWVudG8kVGllbXBvIDwtIGFzLmZhY3RvcihzZWd1aW1pZW50byRUaWVtcG8pCnNlZ3VpbWllbnRvJFBhZHJlcy5mdW1hZG9yZXMgPC0gYXMuZmFjdG9yKHNlZ3VpbWllbnRvJFBhZHJlcy5mdW1hZG9yZXMpCnNlZ3VpbWllbnRvJFByb2JsZW1hcy5jYXJkaWFjb3MgPC0gYXMuZmFjdG9yKHNlZ3VpbWllbnRvJFByb2JsZW1hcy5jYXJkaWFjb3MpCnNlZ3VpbWllbnRvJENIUk5BNSA8LSBhcy5mYWN0b3Ioc2VndWltaWVudG8kQ0hSTkE1KQpzZWd1aW1pZW50byRyczU3ODc3NiA8LSBhcy5mYWN0b3Ioc2VndWltaWVudG8kcnM1Nzg3NzYpCgpzdHIoc2VndWltaWVudG8pCmBgYApIYXkgcXVlIGFzZWd1cmFybm9zIHF1ZSBlc3TDoW4gdG9kb3MgbG9zIGRhdG9zIGNvbXBsZXRvcyBkZSBsYSBiYXNlIGRlIGRhdG9zLiBFcyBkZWNpciwgcXVlIGxhICpuKiBzZWEgbGEgbWlzbWEgcGFyYSB0b2RvcyBsb3MgdGllbXBvcyAuCgpBbmFsaWNlbW9zIGxhIG5vcm1hbGlkYWQKYGBge3J9CnJlc3VsdGFkb3NfNCA8LSBzZWd1aW1pZW50byAlPiUgCiAgZ3JvdXBfYnkoVGllbXBvKSAlPiUgCiAgc2hhcGlyb190ZXN0KENpZ2Fycm9zLmFsLmTDrWEpCgpyZXN1bHRhZG9zXzQKYGBgCkxvcyB2YWxvcmVzIGRlICoqTWFudGVuaW1pZW50byoqIG5vIHByb3ZpZW5lbiBkZSB1bmEgZGlzdHJpYnVjacOzbiBub3JtYWwuIEVuIGVzdGUgY2Fzbywgbm8gcG9kcsOtYW1vcyBjb250aW51YXIgY29uIGxhIEFOT1ZBIHkgdGVuZHLDrWFtb3MgcXVlIGhhY2VyIG90cmEgcHJ1ZWJhIG5vIHBhcmFtw6l0cmljYS4gU2luIGVtYmFyZ28sIHZhbW9zIGEgY29udGludWFyIGNvbiBsYSBBTk9WQS4KCkFob3JhIHJlYWxpY2Vtb3MgbGEgcHJ1ZWJhIGRlIGlndWFsZGFkIGRlIHZhcmlhbnphcy4gUHJ1ZWJhIGRlIExldmVuZQpgYGB7cn0Kc2VndWltaWVudG8gJT4lIAogIGxldmVuZV90ZXN0KENpZ2Fycm9zLmFsLmTDrWEgfiBUaWVtcG8sIGNlbnRlciA9ICJtZWRpYW4iKQpgYGAKRGFkbyBlbCB2YWxvciBkZSAqcCogPiAwLjA1IChwLXZhbHVlID0gMC4xNDMpLCBwb2RlbW9zIGRlY2lyIHF1ZSBsYXMgdmFyaWFuemFzIHNvbiBpZ3VhbGVzL3NpbWlsYXJlcyBlbiB0b2RvcyBsb3MgZ3J1cG9zLgoKUGxhbnRlZW1vcyBsYSBwcnVlYmEgZGUgQU5PVkEKCmBgYHtyfQpzZWd1aW1pZW50byAlPiUgCiAgYW5vdmFfdGVzdChkdiA9IENpZ2Fycm9zLmFsLmTDrWEsIAogICAgICAgICAgICAgd2lkID0gQ2xhdmUsCiAgICAgICAgICAgICB3aXRoaW4gPSBUaWVtcG8pCmBgYAoKWWEgcXVlIGVsIHZhbG9yIGRlICpwKiBkZSBBTk9WQSBlcyA8IDAuMDUgKHAtdmFsdWUgPSAyLjA2ZS0xMSksIHBvZGVtb3MgZGVjaXIgcXVlIGhheSBkaWZlcmVuY2lhcyBlbnRyZSBncnVwb3MuCgpQYXJhIGVsIHRlc3QgZGUgZXNmZXJpY2lkYWQKCi0gJEhfMCQ6IGxvcyBkYXRvcyBubyB2YXJpYW4gZW4gY2FkYSBuaXZlbCBkZSB0aWVtcG8gKHAgPjAuMDUpCi0gJEhfYSQ6IGxvcyBkYXRvcyB2YXLDrWFuIGVuIGNhZGEgbml2ZWwgZGUgdGllbXBvIChwIDwgMC4wNSkKClNpIG5vIHNlIGN1bXBsZSBlbCBzdXB1ZXN0byBkZSBlc2ZlcmljaWRhZCBwb3IgZWwgdGVzdCBkZSBNYXVjaGx5LCBoYWJyw6EgcXVlIHZlciBsb3MgZGF0b3MgZGUgbGEgY29ycmVjY2nDs24gZGUgZXNmZXJpY2lkYWQuIEhhYnLDoSBxdWUgY29uc2lkZXJhciBlbCB2YWxvciBkZSAqcCogZGUgcFtHR10gZGVsIHRlc3QgZGUgY29ycmVjY2nDs24gZGUgZXNmZXJpY2lkYWQgY29tbyBlbCBxdWUgc2UgdGllbmUgcXVlIHJlcG9ydGFyIGRlIGxhIHBydWViYSBkZSBBTk9WQS4KCkhhc3RhIGFxdcOtIGNvbmNsdWltb3MgcGFyY2lhbG1lbnRlIHF1ZSBleGlzdGUgZGlmZXJlbmNpYSBlbiBlbCBuw7ptZXJvIGRlIGNpZ2Fycm9zIHBvciBkw61hIGRlc3B1w6lzIGRlIGNhZGEgZXRhcGEgZGUgdHJhdGFtaWVudG8uCgojIzYuIFBydWViYXMgbm8gcGFyYW3DqXRyaWNhcwoKMS4gU29uIHBydWViYXMgcGFyYSBjb21wYXJhciBkYXRvcyBjb24gZGlzdHJpYnVjaW9uZXMgbm8gbm9ybWFsZXMKMi4gTm8gc29uIHNlbnNpYmxlcyBhIGRhdG9zIGV4dHJlbW9zIGF0w61waWNvcywgcG9yIGxvIHF1ZSBzdSBwcmVzZW5jaWEgbm8gYWZlY3RhcsOhIGVsIGFuw6FsaXNpcyBlbiBjb25qdW50bwozLiBGdW5kYW1lbnQ6IG9yZGVuYSBsb3MgZGF0b3MgZGUgbWVub3IgYSBtYXlvciwgIGNhZGEgZGF0byBzZSBsZSBhc2lnbmEgcG9zaWNpw7NuIHkgdW4gdmFsb3IgbnVtw6lyaWNvIChyYW5nbyBkZSAxIGEgKm4qKQoKLSBDb21wYXJhY2lvbmVzIGN1YW50aXRhdGl2YXM6IHBydWViYSBkZSBXaWxjb3hvbiwgVSBkZSBNYW5uLVdoaXRuZXksIEtydXNrYWwtV2FsbGlzLCBGcmllZG1hbgotIENvbXBhcmFjaW9uZXMgY3VhbGl0YXRpdmFzOiBjaGkgY3VhZHJhZGEsIEZpc2hlcgoKYGBge3J9CnNlc2lvbl82IDwtIHJlYWQuY3N2KCIvY2xvdWQvcHJvamVjdC9CaW9lc3RhZGlzdGljYV9SL2RhdG9zXzEuY3N2IikKc3RyKHNlc2lvbl82KQoKc2VzaW9uXzYkRXhwb3NpY2lvbiA8LSBhcy5mYWN0b3Ioc2VzaW9uXzYkRXhwb3NpY2lvbikKc3RyKHNlc2lvbl82KQpgYGAKSGF5IHF1ZSBwcm9iYXIgbGEgYXNpbWV0csOtYSAoc2tldykKYGBge3J9CmxpYnJhcnkocHN5Y2gpCnNrZXcoc2VzaW9uXzYkQ290aW5pbmFbc2VzaW9uXzYkRXhwb3NpY2lvbj09ICJTaW4gZXhwb3NpY2lvbiJdKQpza2V3KHNlc2lvbl82JENvdGluaW5hW3Nlc2lvbl82JEV4cG9zaWNpb249PSAiRnVtYWRvciJdKQpza2V3KHNlc2lvbl82JENvdGluaW5hW3Nlc2lvbl82JEV4cG9zaWNpb249PSAiVmFwZWFkb3IiXSkKc2tldyhzZXNpb25fNiRDb3RpbmluYVtzZXNpb25fNiRFeHBvc2ljaW9uPT0gIkh1bW8gZGUgYmlvbWFzYSJdKQpgYGAKQXNpbWV0csOtYSA8IC0yIG8gPiAyIHlhIGVyYSB1bmEgc2ltZXRyw61hIG11eSBtYXJjYWRhLiBTZSBlc3BlcmEgdGVuZXIgdW5hIGFzaW1ldHLDrWEgPSAwIChzaW3DqXRyaWNvcyksIG8gY3VhbmRvICRhIFxuZXEgMCQgKGFzaW3DqXRyaWNvcyApCgpSZWFsaXphbW9zIGVsIGFuw6FsaXNpcyBkZSBpZ3VhbGRhZCBkZSB2YXJpYW56YXMKYGBge3J9CnNlc2lvbl82ICU+JSAKICBsZXZlbmVfdGVzdChDb3RpbmluYSB+IEV4cG9zaWNpb24sIGNlbnRlciA9ICJtZWRpYW4iKQpgYGAKYGBge3J9CmJveHBsb3Qoc2VzaW9uXzYkQ290aW5pbmEgfiBzZXNpb25fNiRFeHBvc2ljaW9uKQoKcCA8LSBnZ3Bsb3Qoc2VzaW9uXzYsIG1hcHBpbmcgPSBhZXMoeD1FeHBvc2ljaW9uLCB5ID0gQ290aW5pbmEsIGZpbGwgPSBFeHBvc2ljaW9uKSkKcCArIGdlb21fYm94cGxvdCgpCmBgYAoKUmVhbGl6YW1vcyBsYSBwcnVlYmEgZGUgVSBkZSBNYW5uLVdoaXRuZXkgcGFyYSBkYXRvcyBubyByZWxhY2lvbmFkb3MKYGBge3J9CnNlc2lvbl82ICU+JSAKICB3aWxjb3hfdGVzdChDb3RpbmluYSB+IEV4cG9zaWNpb24sCiAgICAgICAgICAgICAgcmVmLmdyb3VwID0gIlNpbiBleHBvc2ljaW9uIiwKICAgICAgICAgICAgICBwLmFkanVzdC5tZXRob2QgPSAiYm9uZmVycm9uaSIsCiAgICAgICAgICAgICAgZXhhY3QgPSBUUlVFLAogICAgICAgICAgICAgIHBhaXJlZCA9IEZBTFNFLAogICAgICAgICAgICAgIGNvbmYubGV2ZWwgPSAwLjk1LAogICAgICAgICAgICAgIGFsdGVybmF0aXZlID0gInR3by5zaWRlZCIpCmBgYApFeGlzdGUgZGlmZXJlbmNpYSBlbnRyZSBlbCBncnVwbyBzaW4gZXhwb3NpY2nDs24gdnMgZnVtYWRvcmVzIChzZSByZXBvcnQgZWwgcC5hZGouc2lnbmlmKSwgeSBzaW4gZXhwb3NpY2lvbiB2cyB2YXBlYWRvcmVzLgoKTGEgcHJ1ZWJhIHBvc3QtaG9jIGRlIEtydXNrYWwtV2FsbGlzIGVzIGxhIHNpZ3VpZW50ZQpgYGB7cn0Kc2VzaW9uXzYgJT4lIAogIHBhaXJ3aXNlX3dpbGNveF90ZXN0KENvdGluaW5hIH4gRXhwb3NpY2lvbiwKICAgICAgICAgICAgICAgICAgICAgICBwLmFkanVzdC5tZXRob2QgPSAiYm9uZmVycm9uaSIsCiAgICAgICAgICAgICAgICAgICAgICAgcGFpcmVkID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgY29uZi5sZXZlbCA9IDAuOTUsCiAgICAgICAgICAgICAgICAgICAgICAgYWx0ZXJuYXRpdmUgPSAidHdvLnNpZGVkIikKYGBgCgpQYXJhIHJlc3BvbmRlciBhIGxhIHByZWd1bnRhIGRlIHF1w6kgdGFuIGdyYW5kZSBlcyBsYSBkaWZlcmVuY2lhLCBlbnRvbmNlcyBjYWxjdWxhbW9zIGVsIHRhbWHDsW8gZGVsIGVmZWN0byAKYGBge3J9Cmluc3RhbGwucGFja2FnZXMoJ2NvaW4nKQpzZXNpb25fNiAlPiUgCiAgd2lsY294X2VmZnNpemUoQ290aW5pbmEgfiBFeHBvc2ljaW9uKQpgYGAKCkxvcyBuaXZlbGVzIGRlIGNvdGluaW5hIHZhcsOtYW4gZW50cmUgZWwgZ3J1cG8gc2luIGV4cG9zaWNpw7NuIHZzIGZ1bWFkb3JlcyAocCA9IDMuMTllLTE2KSB5IHNpbiBleHBvc2ljacOzbiB2cyB2YXBlYWRvcmVzLiBMYXMgZGlmZXJlbmNpYXMgZW5jb250cmFkYXMgc29uIGdyYW5kZXMgc2Vnw7puIGxhIHBydWViYSBkZWwgdGFtYcOxbyBkZWwgZWZlY3RvLgoKUmV2aXNlbW9zIGNvbW8gc2UgY29tcGFyYW4gZW50cmUgdG9kb3MgbG9zIGdydXBvcyBlbiBsb3Mgbml2ZWxlcyBkZSBDb3RpbmluYS4gUmVhbGl6YW1vcyBsYSBwcnVlYmEgZGUgS3J1c2thbC1XYWxsaXMKYGBge3J9CnNlc2lvbl82ICU+JSAKICBrcnVza2FsX3Rlc3QoQ290aW5pbmEgfiBFeHBvc2ljaW9uKQpgYGAKTm8gc2FiZW1vcyBlbnRyZSBxdWUgZ3J1cG9zIGhheSBkaWZlcmVuY2lhLCBwZXJvIHNpIGV4aXN0ZSBwb3JxdWUgZWwgdmFsb3IgZGUgcCBhcnJvamFkbyBwb3IgbGEgcHJ1ZWJhIGRlIEtydXNrYWwtV2FsbGlzIChwID0gMi4yN2UtMzIpLiBQYXJhIHNhYmVyIGVudHJlIHF1ZSBncnVwb3MgaGFjZW1vcyBsYSBwcnVlYmEgcG9zdC1ob2MgZGUgZHVubgoKYGBge3J9CnNlc2lvbl82ICU+JSAKICBkdW5uX3Rlc3QoQ290aW5pbmEgfiBFeHBvc2ljaW9uKSAKYGBgCk8gaGFjZW1vcyB1bmEgcHJ1ZWJhIHBvc3QtaG9jIApgYGB7cn0Kc2VzaW9uXzYgJT4lIAogIHBhaXJ3aXNlX3dpbGNveF90ZXN0KENvdGluaW5hIH4gRXhwb3NpY2lvbiwKICAgICAgICAgICAgICAgICAgICAgICBwLmFkanVzdC5tZXRob2QgPSAiYm9uZmVycm9uaSIsCiAgICAgICAgICAgICAgICAgICAgICAgZXhhY3QgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgIHBhaXJlZCA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgIGNvbmYubGV2ZWwgPSAwLjk1LAogICAgICAgICAgICAgICAgICAgICAgIGFsdGVybmF0aXZlID0gInR3by5zaWRlZCIpCmBgYAoKUGFyYSB2ZXIgZWwgdGFtYcOxbyBkZWwgZWZlY3RvCmBgYHtyfQpzZXNpb25fNiAlPiUgCiAga3J1c2thbF9lZmZzaXplKENvdGluaW5hIH4gRXhwb3NpY2lvbikKYGBgCgojIyMgUHJ1ZWJhcyBjdWFsaXRhdGl2YXMKClJlYWxpY2Vtb3MgdW5hIHRhYmxhIGRlIGNvbnRpbmdlbmNpYSAocHJpbmNpcGFsbWVudGUgcGFyYSB2YXJpYWJsZXMgY3VhbGl0YXRpdmFzKQpgYGB7cn0KZXN0YXR1c19zZXhvIDwtIHRhYmxlKHNlc2lvbl82JFNleG8sIHNlc2lvbl82JEVzdGF0dXMsCiAgICAgICAgICAgICAgICAgICAgICBkbm4gPSBjKCJTZXhvIiwgIkVzdGF0dXMiKSkKZXN0YXR1c19zZXhvCmBgYApVbmEgdGFibGEgZGUgbngyIChQcmltZXJvIGZpbGFzLCBsdWVnbyBjb2x1bW5hcykKYGBge3J9CmVzdGF0dXNfZXhwb3NpY2lvbiA8LSB0YWJsZShzZXNpb25fNiRFeHBvc2ljaW9uLCBzZXNpb25fNiRFc3RhdHVzLAogICAgICBkbm4gPSBjKCJFeHBvc2ljaW9uIiwgIkVzdGF0dXMiKSkKCmV4cG9zaWNpb25fc2V4byA8LSB0YWJsZShzZXNpb25fNiRTZXhvLCBzZXNpb25fNiRFeHBvc2ljaW9uLAogICAgICBkbm4gPSBjKCJTZXhvIiwgIkV4cG9zaWNpb24iKSkKYGBgClBhcmEgcmVhbGl6YXIgbGEgcHJ1ZWJhCmBgYHtyfQpmaXNoZXIudGVzdChlc3RhdHVzX3NleG8pCmVzdGF0dXNfc2V4bwoKY2hpc3EudGVzdChlc3RhdHVzX2V4cG9zaWNpb24pCmBgYApjdWFuZG8gcCA+IDAuMDUgbGEgZnJldWN1ZW5jaWEgZGUgZGF0b3MgZXMgaWd1YWwsIGN1YW5kbyBwIDwgMC4wNSBsYSBmcmVjdWVuY2EgZGUgZGF0b3MgZXMgZGlmZXJlbnRlCg==