This is an R Markdown Notebook. When you execute code within the notebook, the results appear beneath the code.

Try executing this chunk by clicking the Run button within the chunk or by placing your cursor inside it and pressing Ctrl+Shift+Enter.

# ============================================================
# I. EXPLORACIÓN DE LA BASE DE DATOS 
# ============================================================

datos <- read_excel("salud_cardiovascular.xlsx")
datos <- as.data.frame(datos)
dim(datos) 
[1] 500  10
names(datos)
 [1] "id"                    "edad"                  "sexo"                 
 [4] "imc"                   "presion_sistolica"     "glucosa_ayuno"        
 [7] "actividad_fisica"      "fumador"               "riesgo_cardiovascular"
[10] "riesgo_alto"          
head(datos)

# Revisamos la estructura de la base como qué variables son numéricas, cuáles son texto, factores, etc. 
str(datos)
'data.frame':   500 obs. of  10 variables:
 $ id                   : num  1 2 3 4 5 6 7 8 9 10 ...
 $ edad                 : num  19 24 44 74 31 31 47 70 46 69 ...
 $ sexo                 : chr  "F" "M" "F" "F" ...
 $ imc                  : num  23.3 17.8 25 33.1 28.9 33.1 25.2 31.2 26.4 25.4 ...
 $ presion_sistolica    : num  102 138 116 149 80 118 132 123 117 118 ...
 $ glucosa_ayuno        : num  73 98 84 91 84 97 104 115 92 76 ...
 $ actividad_fisica     : chr  "activo" "moderado" "activo" "moderado" ...
 $ fumador              : chr  "si" "no" "no" "no" ...
 $ riesgo_cardiovascular: num  18.4 20.6 17.1 73.8 28.6 27.1 42 63.7 25.7 73.4 ...
 $ riesgo_alto          : chr  "bajo" "bajo" "bajo" "alto" ...
# Resumen general de todas las variables.
summary(datos)
       id             edad          sexo                imc       
 Min.   :  1.0   Min.   :18.0   Length:500         Min.   :16.00  
 1st Qu.:125.8   1st Qu.:33.0   Class :character   1st Qu.:23.90  
 Median :250.5   Median :48.0   Mode  :character   Median :27.15  
 Mean   :250.5   Mean   :48.4                      Mean   :27.26  
 3rd Qu.:375.2   3rd Qu.:64.0                      3rd Qu.:30.70  
 Max.   :500.0   Max.   :80.0                      Max.   :41.40  
 presion_sistolica glucosa_ayuno   actividad_fisica     fumador         
 Min.   : 80.0     Min.   : 60.0   Length:500         Length:500        
 1st Qu.:109.0     1st Qu.: 81.0   Class :character   Class :character  
 Median :120.0     Median : 95.0   Mode  :character   Mode  :character  
 Mean   :120.0     Mean   : 95.2                                        
 3rd Qu.:130.2     3rd Qu.:110.0                                        
 Max.   :159.0     Max.   :157.0                                        
 riesgo_cardiovascular riesgo_alto       
 Min.   :  0.00        Length:500        
 1st Qu.: 26.00        Class :character  
 Median : 37.75        Mode  :character  
 Mean   : 40.12                          
 3rd Qu.: 53.23                          
 Max.   :100.00                          
# Para ver cuántos datos faltantes hay por variable.
colSums(is.na(datos))
                   id                  edad                  sexo 
                    0                     0                     0 
                  imc     presion_sistolica         glucosa_ayuno 
                    0                     0                     0 
     actividad_fisica               fumador riesgo_cardiovascular 
                    0                     0                     0 
          riesgo_alto 
                    0 
# ============================================================
# II. DEFINIR VARIABLE DEPENDIENTE E INDEPENDIENTES
# ============================================================

# Para hacerla factor para que R la trate como categoría.
datos$riesgo_alto <- factor(datos$riesgo_alto)

# También las hago a factor las variables categóricas independientes
datos$sexo <- factor(datos$sexo)
datos$actividad_fisica <- factor(datos$actividad_fisica)
datos$fumador <- factor(datos$fumador)

# cuántas personas hay en cada categoría de riesgo.
table(datos$riesgo_alto)

alto bajo 
 149  351 
# el porcentaje de personas en cada categoría.
prop.table(table(datos$riesgo_alto))

 alto  bajo 
0.298 0.702 
# ============================================================
# III. VARIABLES QUE SÍ SE USARÁN COMO INDEPENDIENTES
# ============================================================

# Variables numéricas de salud.
variables_numericas <- c(
  "edad",
  "imc",
  "presion_sistolica",
  "glucosa_ayuno"
)

# Variables categóricas.
variables_categoricas <- c(
  "sexo",
  "actividad_fisica",
  "fumador"
)

# Solo para ver que quedaron bien guardadas. 
variables_numericas
[1] "edad"              "imc"               "presion_sistolica"
[4] "glucosa_ayuno"    
variables_categoricas
[1] "sexo"             "actividad_fisica" "fumador"         
# ============================================================
# IV. ANÁLISIS VISUAL 1: DISTRIBUCIÓN DE LA VARIABLE DEPENDIENTE
# ============================================================
barplot(
  table(datos$riesgo_alto),
  main = "Distribución del riesgo cardiovascular",
  xlab = "Nivel de riesgo",
  ylab = "Número de personas",
  col = "purple"
)

# ============================================================
# V. ANÁLISIS VISUAL 2: VARIABLES NUMÉRICAS VS RIESGO ALTO
# ============================================================

# Edad según nivel de riesgo.
boxplot(
  edad ~ riesgo_alto,
  data = datos,
  main = "Edad según riesgo cardiovascular",
  xlab = "Riesgo cardiovascular",
  ylab = "Edad",
  col = "cyan"
)


# IMC según nivel de riesgo.
boxplot(
  imc ~ riesgo_alto,
  data = datos,
  main = "IMC según riesgo cardiovascular",
  xlab = "Riesgo cardiovascular",
  ylab = "IMC",
  col = "magenta"
)


# Presión sistólica según nivel de riesgo.
boxplot(
  presion_sistolica ~ riesgo_alto,
  data = datos,
  main = "Presión sistólica según riesgo cardiovascular",
  xlab = "Riesgo cardiovascular",
  ylab = "Presión sistólica",
  col = "yellow"
)


# Glucosa en ayuno según nivel de riesgo.
boxplot(
  glucosa_ayuno ~ riesgo_alto,
  data = datos,
  main = "Glucosa en ayuno según riesgo cardiovascular",
  xlab = "Riesgo cardiovascular",
  ylab = "Glucosa en ayuno",
  col = "orange"
)

# ============================================================
# VI. ANÁLISIS VISUAL 3: VARIABLES CATEGÓRICAS VS RIESGO ALTO
# ============================================================

# *****************************
# Sexo y riesgo cardiovascular
# *****************************

# Tabla de conteos.
table(datos$sexo, datos$riesgo_alto)
   
    alto bajo
  F   70  192
  M   79  159
# Tabla de porcentajes por sexo.
prop.table(table(datos$sexo, datos$riesgo_alto), 1)
   
         alto      bajo
  F 0.2671756 0.7328244
  M 0.3319328 0.6680672
# Gráfica de barras.
barplot(
  table(datos$sexo, datos$riesgo_alto),
  beside = TRUE,
  legend.text = TRUE,
  main = "Sexo y riesgo cardiovascular",
  xlab = "Riesgo cardiovascular",
  ylab = "Número de personas",
  col = c("lightgreen", "darkgreen")
)



# ****************************************
# Actividad física y riesgo cardiovascular
# ****************************************

# Tabla de conteos.
table(datos$actividad_fisica, datos$riesgo_alto)
            
             alto bajo
  activo       23   73
  moderado     57  128
  sedentario   69  150
# Tabla de porcentajes por actividad física.
prop.table(table(datos$actividad_fisica, datos$riesgo_alto), 1)
            
                  alto      bajo
  activo     0.2395833 0.7604167
  moderado   0.3081081 0.6918919
  sedentario 0.3150685 0.6849315
# Gráfica de barras.
barplot(
  table(datos$actividad_fisica, datos$riesgo_alto),
  beside = TRUE,
  legend.text = TRUE,
  main = "Actividad física y riesgo cardiovascular",
  xlab = "Riesgo cardiovascular",
  ylab = "Número de personas",
  col = c("skyblue", "steelblue", "darkblue")
)



# ********************************
# Fumador y riesgo cardiovascular
# ********************************

# Tabla de conteos.
table(datos$fumador, datos$riesgo_alto)
    
     alto bajo
  no  105  262
  si   44   89
# Tabla de porcentajes por fumador.
prop.table(table(datos$fumador, datos$riesgo_alto), 1)
    
          alto      bajo
  no 0.2861035 0.7138965
  si 0.3308271 0.6691729
# Gráfica de barras.
barplot(
  table(datos$fumador, datos$riesgo_alto),
  beside = TRUE, #para comparar mejor las categorías y que salgan pegaditas 
  legend.text = TRUE,
  main = "Fumador y riesgo cardiovascular",
  xlab = "Riesgo cardiovascular",
  ylab = "Número de personas",
  col = c("firebrick", "darkred")
)

# ================================================================================
# VII. EXPLORAR LA RELACIÓN ENTRE CADA VARIABLE INDEPENDIENTE Y LA VARIABLE DEPENDIENTE
# ================================================================================

# Creamos una versión numérica de la variable dependiente.
# riesgo_num vale 1 si la persona tiene riesgo alto.
# riesgo_num vale 0 si la persona tiene riesgo bajo.
datos$riesgo_num <- 0
datos$riesgo_num[datos$riesgo_alto == "alto"] <- 1

# Solo para revisar que sí se hizo la conversión 
table(datos$riesgo_alto, datos$riesgo_num) 
      
         0   1
  alto   0 149
  bajo 351   0
# **********************************************
# a. MODELO 1: RELACIÓN ENTRE EDAD Y RIESGO ALTO
# **********************************************

# Queremos ver cómo cambia el riesgo alto conforme cambia la edad.
modelo_loess_edad <- loess(riesgo_num ~ edad, data = datos, span = 0.75)

# Creamos una secuencia de edades para poder dibujar la curva suavizada.
x_edad <- seq(
  min(datos$edad),
  max(datos$edad),
  length.out = 100
) 

# Calculamos las predicciones del modelo LOESS para esas edades.
pred_edad <- predict(
  modelo_loess_edad,
  newdata = data.frame(edad = x_edad)
)

# Graficamos los puntos reales.
# Como riesgo_num solo vale 0 o 1, los puntos se verán abajo o arriba.
plot(
  datos$edad,
  datos$riesgo_num,
  main = "LOESS: Edad y riesgo cardiovascular alto",
  xlab = "Edad",
  ylab = "Riesgo alto: 1 = alto, 0 = bajo",
  pch = 19,
  col = "gray",
  ylim = c(-0.1, 1.1)
)

# Agregamos la curva LOESS.
lines(
  x_edad,
  pred_edad,
  col = "cyan",
  lwd = 3
)


# ***************************************
# b. MODELO 2: IMC Y RIESGO ALTO CON LOESS
# ***************************************

# Ajustamos el modelo LOESS para IMC.
modelo_loess_imc <- loess(riesgo_num ~ imc, data = datos, span = 0.75)

# Creamos una secuencia de valores de IMC.
x_imc <- seq(
  min(datos$imc),
  max(datos$imc),
  length.out = 100
)

# Calculamos predicciones.
pred_imc <- predict(
  modelo_loess_imc,
  newdata = data.frame(imc = x_imc)
)

# Graficamos puntos y curva.
plot(
  datos$imc,
  datos$riesgo_num,
  main = "LOESS: IMC y riesgo cardiovascular alto",
  xlab = "IMC",
  ylab = "Riesgo alto: 1 = alto, 0 = bajo",
  pch = 19,
  col = "gray",
  ylim = c(-0.1, 1.1)
)

lines(
  x_imc,
  pred_imc,
  col = "magenta",
  lwd = 3
)


# *****************************************************
# c. MODELO 3: PRESIÓN SISTÓLICA Y RIESGO ALTO CON LOESS
# *****************************************************

# Ajustamos el modelo LOESS para presión sistólica.
modelo_loess_presion <- loess(riesgo_num ~ presion_sistolica, data = datos, span = 0.75)

# Creamos una secuencia de valores de presión.
x_presion <- seq(
  min(datos$presion_sistolica),
  max(datos$presion_sistolica),
  length.out = 100
)

# Calculamos predicciones.
pred_presion <- predict(
  modelo_loess_presion,
  newdata = data.frame(presion_sistolica = x_presion)
)

# Graficamos puntos y curva.
plot(
  datos$presion_sistolica,
  datos$riesgo_num,
  main = "LOESS: Presión sistólica y riesgo cardiovascular alto",
  xlab = "Presión sistólica",
  ylab = "Riesgo alto: 1 = alto, 0 = bajo",
  pch = 19,
  col = "gray",
  ylim = c(-0.1, 1.1)
)

lines(
  x_presion,
  pred_presion,
  col = "yellow",
  lwd = 3
)


# *****************************************************
# d. MODELO 4: GLUCOSA EN AYUNO Y RIESGO ALTO CON LOESS
# *****************************************************

# Ajustamos el modelo LOESS para glucosa en ayuno.
modelo_loess_glucosa <- loess(riesgo_num ~ glucosa_ayuno, data = datos, span = 0.75)

# Creamos una secuencia de valores de glucosa.
x_glucosa <- seq(
  min(datos$glucosa_ayuno),
  max(datos$glucosa_ayuno),
  length.out = 100
)

# Calculamos predicciones.
pred_glucosa <- predict(
  modelo_loess_glucosa,
  newdata = data.frame(glucosa_ayuno = x_glucosa)
)

# Graficamos puntos y curva.
plot(
  datos$glucosa_ayuno,
  datos$riesgo_num,
  main = "LOESS: Glucosa en ayuno y riesgo cardiovascular alto",
  xlab = "Glucosa en ayuno",
  ylab = "Riesgo alto: 1 = alto, 0 = bajo",
  pch = 19,
  col = "gray",
  ylim = c(-0.1, 1.1)
)

lines(
  x_glucosa,
  pred_glucosa,
  col = "orange",
  lwd = 3
)

# ============================================================
# VIII. REVISIÓN DE LA SENSIBILIDAD DEL SPAN POR VARIABLE
# ============================================================

# **********************************************
# a. MODELO 1: RELACIÓN ENTRE EDAD Y RIESGO ALTO
# **********************************************

# 0.4 = curva más flexible.
# 0.75 = curva intermedia, la que ya usamos.
# 0.9 = curva más suave.
loess_edad_04 <- loess(riesgo_num ~ edad, data = datos, span = 0.4)
loess_edad_075 <- loess(riesgo_num ~ edad, data = datos, span = 0.75)
loess_edad_09 <- loess(riesgo_num ~ edad, data = datos, span = 0.9)

# Secuencia de edades para graficar las curvas.
x_edad <- seq(min(datos$edad), max(datos$edad), length.out = 100)

# Predicciones para cada modelo.
pred_edad_04 <- predict(loess_edad_04, newdata = data.frame(edad = x_edad))
pred_edad_075 <- predict(loess_edad_075, newdata = data.frame(edad = x_edad))
pred_edad_09 <- predict(loess_edad_09, newdata = data.frame(edad = x_edad))

# Graficamos los puntos reales.
plot(
  datos$edad,
  datos$riesgo_num,
  main = "Comparación de span en LOESS: Edad",
  xlab = "Edad",
  ylab = "Riesgo alto: 1 = alto, 0 = bajo",
  pch = 19,
  col = "gray",
  ylim = c(-0.1, 1.1)
)

# Agregamos las tres curvas.
lines(x_edad, pred_edad_04, col = "navy", lwd = 2)
lines(x_edad, pred_edad_075, col = "cyan", lwd = 3)
lines(x_edad, pred_edad_09, col = "brown", lwd = 2)

# Agregamos leyenda.
legend(
  "topleft",
  legend = c("span = 0.4", "span = 0.75", "span = 0.9"),
  col = c("navy", "cyan", "brown"),
  lwd = c(2, 3, 2)
)


# ***************************************
# b. MODELO 2: IMC Y RIESGO ALTO CON LOESS
# ***************************************

loess_imc_04 <- loess(riesgo_num ~ imc, data = datos, span = 0.4)
loess_imc_075 <- loess(riesgo_num ~ imc, data = datos, span = 0.75)
loess_imc_09 <- loess(riesgo_num ~ imc, data = datos, span = 0.9)

x_imc <- seq(min(datos$imc), max(datos$imc), length.out = 100)

pred_imc_04 <- predict(loess_imc_04, newdata = data.frame(imc = x_imc))
pred_imc_075 <- predict(loess_imc_075, newdata = data.frame(imc = x_imc))
pred_imc_09 <- predict(loess_imc_09, newdata = data.frame(imc = x_imc))

plot(
  datos$imc,
  datos$riesgo_num,
  main = "Comparación de span en LOESS: IMC",
  xlab = "IMC",
  ylab = "Riesgo alto: 1 = alto, 0 = bajo",
  pch = 19,
  col = "gray",
  ylim = c(-0.1, 1.1)
)

lines(x_imc, pred_imc_04, col = "navy", lwd = 2)
lines(x_imc, pred_imc_075, col = "magenta", lwd = 3)
lines(x_imc, pred_imc_09, col = "brown", lwd = 2)

legend(
  "topleft",
  legend = c("span = 0.4", "span = 0.75", "span = 0.9"),
  col = c("navy", "magenta", "brown"),
  lwd = c(2, 3, 2)
)


# *****************************************************
# c. MODELO 3: PRESIÓN SISTÓLICA Y RIESGO ALTO CON LOESS
# *****************************************************

loess_presion_04 <- loess(riesgo_num ~ presion_sistolica, data = datos, span = 0.4)
loess_presion_075 <- loess(riesgo_num ~ presion_sistolica, data = datos, span = 0.75)
loess_presion_09 <- loess(riesgo_num ~ presion_sistolica, data = datos, span = 0.9)

x_presion <- seq(min(datos$presion_sistolica), max(datos$presion_sistolica), length.out = 100)

pred_presion_04 <- predict(loess_presion_04, newdata = data.frame(presion_sistolica = x_presion))
pred_presion_075 <- predict(loess_presion_075, newdata = data.frame(presion_sistolica = x_presion))
pred_presion_09 <- predict(loess_presion_09, newdata = data.frame(presion_sistolica = x_presion))

plot(
  datos$presion_sistolica,
  datos$riesgo_num,
  main = "Comparación de span en LOESS: Presión sistólica",
  xlab = "Presión sistólica",
  ylab = "Riesgo alto: 1 = alto, 0 = bajo",
  pch = 19,
  col = "gray",
  ylim = c(-0.1, 1.1)
)

lines(x_presion, pred_presion_04, col = "navy", lwd = 2)
lines(x_presion, pred_presion_075, col = "yellow", lwd = 3)
lines(x_presion, pred_presion_09, col = "brown", lwd = 2)

legend(
  "topleft",
  legend = c("span = 0.4", "span = 0.75", "span = 0.9"),
  col = c("navy", "yellow", "brown"),
  lwd = c(2, 3, 2)
)


# *****************************************************
# d. MODELO 4: GLUCOSA EN AYUNO Y RIESGO ALTO CON LOESS
# *****************************************************

loess_glucosa_04 <- loess(riesgo_num ~ glucosa_ayuno, data = datos, span = 0.4)
loess_glucosa_075 <- loess(riesgo_num ~ glucosa_ayuno, data = datos, span = 0.75)
loess_glucosa_09 <- loess(riesgo_num ~ glucosa_ayuno, data = datos, span = 0.9)

x_glucosa <- seq(min(datos$glucosa_ayuno), max(datos$glucosa_ayuno), length.out = 100)

pred_glucosa_04 <- predict(loess_glucosa_04, newdata = data.frame(glucosa_ayuno = x_glucosa))
pred_glucosa_075 <- predict(loess_glucosa_075, newdata = data.frame(glucosa_ayuno = x_glucosa))
pred_glucosa_09 <- predict(loess_glucosa_09, newdata = data.frame(glucosa_ayuno = x_glucosa))

plot(
  datos$glucosa_ayuno,
  datos$riesgo_num,
  main = "Comparación de span en LOESS: Glucosa en ayuno",
  xlab = "Glucosa en ayuno",
  ylab = "Riesgo alto: 1 = alto, 0 = bajo",
  pch = 19,
  col = "gray",
  ylim = c(-0.1, 1.1)
)

lines(x_glucosa, pred_glucosa_04, col = "navy", lwd = 2)
lines(x_glucosa, pred_glucosa_075, col = "orange", lwd = 3)
lines(x_glucosa, pred_glucosa_09, col = "brown", lwd = 2)

legend(
  "topleft",
  legend = c("span = 0.4", "span = 0.75", "span = 0.9"),
  col = c("navy", "orange", "brown"),
  lwd = c(2, 3, 2)
)

# ============================================================
# X. REVISIÓN DE ERRORES DE LOS MODELOS LOESS
# ============================================================

# Calculamos las predicciones de cada modelo sobre los mismos datos.
# Esto nos permite comparar lo que el modelo predijo contra el valor real.
pred_edad_datos <- predict(modelo_loess_edad)
pred_imc_datos <- predict(modelo_loess_imc)
pred_presion_datos <- predict(modelo_loess_presion)
pred_glucosa_datos <- predict(modelo_loess_glucosa)

# MSE = promedio de los errores al cuadrado.
mse <- function(real, predicho) {
  mean((real - predicho)^2)
}

# RMSE = raíz cuadrada del MSE.
rmse <- function(real, predicho) {
  sqrt(mean((real - predicho)^2))
}

# Tabla para comparar los errores de cada modelo.
tabla_errores_loess <- data.frame(
  variable = c("edad", "imc", "presion_sistolica", "glucosa_ayuno"),
  MSE = c(
    mse(datos$riesgo_num, pred_edad_datos),
    mse(datos$riesgo_num, pred_imc_datos),
    mse(datos$riesgo_num, pred_presion_datos),
    mse(datos$riesgo_num, pred_glucosa_datos)
  ),
  RMSE = c(
    rmse(datos$riesgo_num, pred_edad_datos),
    rmse(datos$riesgo_num, pred_imc_datos),
    rmse(datos$riesgo_num, pred_presion_datos),
    rmse(datos$riesgo_num, pred_glucosa_datos)
  )
)

# Mostramos la tabla de errores.
tabla_errores_loess
# ============================================================
# IX. GRÁFICAS DE RESIDUOS DE LOS MODELOS LOESS
# ============================================================

# Residuos del modelo con edad.
plot(
  datos$edad,
  modelo_loess_edad$residuals,
  main = "Residuos LOESS: Edad",
  xlab = "Edad",
  ylab = "Residuo",
  pch = 19,
  col = "gray"
)
abline(h = 0, col = "cyan", lty = 2, lwd = 2)



# Residuos del modelo con IMC.
plot(
  datos$imc,
  modelo_loess_imc$residuals,
  main = "Residuos LOESS: IMC",
  xlab = "IMC",
  ylab = "Residuo",
  pch = 19,
  col = "gray"
)
abline(h = 0, col = "magenta", lty = 2, lwd = 2)



# Residuos del modelo con presión sistólica.
plot(
  datos$presion_sistolica,
  modelo_loess_presion$residuals,
  main = "Residuos LOESS: Presión sistólica",
  xlab = "Presión sistólica",
  ylab = "Residuo",
  pch = 19,
  col = "gray"
)
abline(h = 0, col = "yellow", lty = 2, lwd = 2)



# Residuos del modelo con glucosa en ayuno.
plot(
  datos$glucosa_ayuno,
  modelo_loess_glucosa$residuals,
  main = "Residuos LOESS: Glucosa en ayuno",
  xlab = "Glucosa en ayuno",
  ylab = "Residuo",
  pch = 19,
  col = "gray"
)
abline(h = 0, col = "orange", lty = 2, lwd = 2)

# ============================================================
# XII. INCISO 2: MODELO GAM CON TODAS LAS VARIABLES INDEPENDIENTES
# ============================================================

library(mgcv)
datos$riesgo_alto <- factor(datos$riesgo_alto)
datos$sexo <- factor(datos$sexo)
datos$actividad_fisica <- factor(datos$actividad_fisica)
datos$fumador <- factor(datos$fumador)

# Solo para confirmar la variable numérica de riesgo.
# riesgo_num = 1 si la persona tiene riesgo alto.
# riesgo_num = 0 si la persona tiene riesgo bajo.
datos$riesgo_num <- 0
datos$riesgo_num[datos$riesgo_alto == "alto"] <- 1

# Ajusto el modelo GAM.
# Las variables numéricas entran con s(), porque quiero permitir relaciones no lineales suaves.
# Las variables categóricas entran directo como factores.
modelo_gam <- gam(
  riesgo_num ~ 
    s(edad, k = 10) +
    s(imc, k = 10) +
    s(presion_sistolica, k = 10) +
    s(glucosa_ayuno, k = 10) +
    sexo +
    actividad_fisica +
    fumador,
  data = datos,
  family = binomial(), #en lugar de family = gaussian()
  method = "REML" 
)

summary(modelo_gam)

Family: binomial 
Link function: logit 

Formula:
riesgo_num ~ s(edad, k = 10) + s(imc, k = 10) + s(presion_sistolica, 
    k = 10) + s(glucosa_ayuno, k = 10) + sexo + actividad_fisica + 
    fumador

Parametric coefficients:
                           Estimate Std. Error z value Pr(>|z|)    
(Intercept)                 -8.1698     1.2247  -6.671 2.55e-11 ***
sexoM                        1.0234     0.4998   2.048 0.040593 *  
actividad_fisicamoderado     1.5616     0.7021   2.224 0.026135 *  
actividad_fisicasedentario   2.6142     0.7609   3.436 0.000591 ***
fumadorsi                    3.2808     0.7169   4.576 4.73e-06 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Approximate significance of smooth terms:
                       edf Ref.df Chi.sq  p-value    
s(edad)              1.000  1.000 54.614  < 2e-16 ***
s(imc)               3.231  4.042 36.038 4.37e-07 ***
s(presion_sistolica) 3.682  4.592 15.658  0.00573 ** 
s(glucosa_ayuno)     1.000  1.000  2.238  0.13472    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

R-sq.(adj) =  0.832   Deviance explained = 81.8%
-REML = 62.379  Scale est. = 1         n = 500
# ============================================================
# XIII. GRÁFICAS DE FUNCIONES SUAVES DEL GAM
# ============================================================

# Para ver la contribución parcial de cada variable numérica.
par(mfrow = c(2, 2))

plot(
  modelo_gam,
  shade = TRUE,
  main = "Funciones suaves del modelo GAM"
)

par(mfrow = c(1, 1)) 


# para ver si la flexibilidad elegida para las funciones suaves parece adecuada.
gam.check(modelo_gam)


Method: REML   Optimizer: outer newton
full convergence after 12 iterations.
Gradient range [-1.097051e-05,1.838552e-06]
(score 62.37901 & scale 1).
Hessian positive definite, eigenvalue range [5.504187e-06,0.6052596].
Model rank =  41 / 41 

Basis dimension (k) checking results. Low p-value (k-index<1) may
indicate that k is too low, especially if edf is close to k'.

                       k'  edf k-index p-value
s(edad)              9.00 1.00    1.00    0.41
s(imc)               9.00 3.23    1.01    0.54
s(presion_sistolica) 9.00 3.68    0.95    0.10
s(glucosa_ayuno)     9.00 1.00    0.97    0.23

# ============================================================
# XIV. PREDICCIÓN Y EVALUACIÓN DEL GAM
# ============================================================

# Para mí: type = "response" hace que las predicciones salgan entre 0 y 1.
# Valores cercanos a 1 indican mayor tendencia de riesgo alto.
pred_gam <- predict(
  modelo_gam,
  type = "response"
)

summary(pred_gam)
     Min.   1st Qu.    Median      Mean   3rd Qu.      Max. 
0.000e+00 8.420e-06 7.048e-03 2.980e-01 7.717e-01 1.000e+00 
# Convertimos las predicciones numéricas en categorías.
# Si la predicción es mayor o igual a 0.5, decimos "alto".
# Si es menor a 0.5, decimos "bajo".
pred_gam_categoria <- ifelse(
  pred_gam >= 0.5,
  "alto",
  "bajo"
)

# Convertimos la predicción a factor.
pred_gam_categoria <- factor(
  pred_gam_categoria,
  levels = c("bajo", "alto")
)

# Nos aseguramos de que la variable real tenga el mismo orden.
datos$riesgo_alto <- factor(
  datos$riesgo_alto,
  levels = c("bajo", "alto")
)

# Hacemos una tabla para comparar la categoría real
# contra la categoría predicha por el modelo.
tabla_prediccion_gam <- table(
  Real = datos$riesgo_alto,
  Predicho = pred_gam_categoria
)

tabla_prediccion_gam
      Predicho
Real   bajo alto
  bajo  343    8
  alto   12  137
# Calculamos el porcentaje total de aciertos.
porcentaje_aciertos_gam <- sum(diag(tabla_prediccion_gam)) / sum(tabla_prediccion_gam)

porcentaje_aciertos_gam
[1] 0.96
# Revisamos porcentajes por fila.
# Esto nos ayuda a ver qué porcentaje de "bajo" y qué porcentaje de "alto"
# fueron clasificados correctamente.
prop.table(tabla_prediccion_gam, 1)
      Predicho
Real         bajo       alto
  bajo 0.97720798 0.02279202
  alto 0.08053691 0.91946309
# ============================================================
# XVI. INCISO 3: ÁRBOL DE DECISIÓN
# ============================================================

library(rpart)

# NOTAS PARA MÍ: 
# 1. method = "class" se usa porque la variable dependiente es categórica.
# 2. maxdepth controla qué tan profundo puede crecer el árbol.
# 3. minsplit indica el mínimo de observaciones necesarias para intentar dividir un nodo.
# 4. cp controla la complejidad del árbol y ayuda a evitar que crezca demasiado.
arbol_decision <- rpart(
  riesgo_alto ~ edad + imc + presion_sistolica + glucosa_ayuno +
    sexo + actividad_fisica + fumador,
  data = datos,
  method = "class",
  control = rpart.control(
    maxdepth = 4,
    minsplit = 10,
    cp = 0.01
  )
)

# Mostramos el árbol en formato de texto.
arbol_decision
n= 500 

node), split, n, loss, yval, (yprob)
      * denotes terminal node

 1) root 500 149 bajo (0.70200000 0.29800000)  
   2) edad< 65.5 385  43 bajo (0.88831169 0.11168831)  
     4) edad< 51.5 286   5 bajo (0.98251748 0.01748252) *
     5) edad>=51.5 99  38 bajo (0.61616162 0.38383838)  
      10) imc< 28.95 67  15 bajo (0.77611940 0.22388060)  
        20) fumador=no 50   5 bajo (0.90000000 0.10000000) *
        21) fumador=si 17   7 alto (0.41176471 0.58823529) *
      11) imc>=28.95 32   9 alto (0.28125000 0.71875000) *
   3) edad>=65.5 115   9 alto (0.07826087 0.92173913)  
     6) imc< 21.05 11   5 alto (0.45454545 0.54545455)  
      12) actividad_fisica=activo 3   0 bajo (1.00000000 0.00000000) *
      13) actividad_fisica=moderado,sedentario 8   2 alto (0.25000000 0.75000000) *
     7) imc>=21.05 104   4 alto (0.03846154 0.96153846) *
plot(
  arbol_decision,
  uniform = TRUE,
  main = "Árbol de decisión para riesgo cardiovascular",
  margin = 0.1
)

text(
  arbol_decision,
  use.n = TRUE,
  cex = 0.7,
  pretty = 0
)

# ============================================================
# XVII. PREDICCIÓN Y EVALUACIÓN DEL ÁRBOL
# ============================================================

pred_arbol <- predict(
  arbol_decision,
  type = "class"
)

# Creamos una tabla para comparar: 
# filas son las categorías reales
# columnas las categorías predichas.
tabla_arbol <- table(
  Real = datos$riesgo_alto,
  Predicho = pred_arbol
)

tabla_arbol
      Predicho
Real   bajo alto
  bajo  329   22
  alto   10  139
# Para ver el porcentaje total de aciertos.
porcentaje_aciertos_arbol <- sum(diag(tabla_arbol)) / sum(tabla_arbol)

porcentaje_aciertos_arbol
[1] 0.936
# Para ver los aciertos individuales en porcentajes. 
prop.table(tabla_arbol, 1)
      Predicho
Real         bajo       alto
  bajo 0.93732194 0.06267806
  alto 0.06711409 0.93288591
# Para ver qué variables fueron más importantes para construir las divisiones del árbol.
arbol_decision$variable.importance
             edad               imc           fumador presion_sistolica 
      136.2827130        14.8698338         6.0482880         2.8833992 
 actividad_fisica     glucosa_ayuno 
        2.4545455         0.9944251 
LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQpUaGlzIGlzIGFuIFtSIE1hcmtkb3duXShodHRwOi8vcm1hcmtkb3duLnJzdHVkaW8uY29tKSBOb3RlYm9vay4gV2hlbiB5b3UgZXhlY3V0ZSBjb2RlIHdpdGhpbiB0aGUgbm90ZWJvb2ssIHRoZSByZXN1bHRzIGFwcGVhciBiZW5lYXRoIHRoZSBjb2RlLiANCg0KVHJ5IGV4ZWN1dGluZyB0aGlzIGNodW5rIGJ5IGNsaWNraW5nIHRoZSAqUnVuKiBidXR0b24gd2l0aGluIHRoZSBjaHVuayBvciBieSBwbGFjaW5nIHlvdXIgY3Vyc29yIGluc2lkZSBpdCBhbmQgcHJlc3NpbmcgKkN0cmwrU2hpZnQrRW50ZXIqLiANCg0KYGBge3J9DQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyBJLiBFWFBMT1JBQ0nDk04gREUgTEEgQkFTRSBERSBEQVRPUyANCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQoNCmRhdG9zIDwtIHJlYWRfZXhjZWwoInNhbHVkX2NhcmRpb3Zhc2N1bGFyLnhsc3giKQ0KZGF0b3MgPC0gYXMuZGF0YS5mcmFtZShkYXRvcykNCmRpbShkYXRvcykgDQpuYW1lcyhkYXRvcykNCmhlYWQoZGF0b3MpDQoNCiMgUmV2aXNhbW9zIGxhIGVzdHJ1Y3R1cmEgZGUgbGEgYmFzZSBjb21vIHF1w6kgdmFyaWFibGVzIHNvbiBudW3DqXJpY2FzLCBjdcOhbGVzIHNvbiB0ZXh0bywgZmFjdG9yZXMsIGV0Yy4gDQpzdHIoZGF0b3MpDQoNCiMgUmVzdW1lbiBnZW5lcmFsIGRlIHRvZGFzIGxhcyB2YXJpYWJsZXMuDQpzdW1tYXJ5KGRhdG9zKQ0KDQojIFBhcmEgdmVyIGN1w6FudG9zIGRhdG9zIGZhbHRhbnRlcyBoYXkgcG9yIHZhcmlhYmxlLg0KY29sU3Vtcyhpcy5uYShkYXRvcykpDQpgYGANCmBgYHtyfQ0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgSUkuIERFRklOSVIgVkFSSUFCTEUgREVQRU5ESUVOVEUgRSBJTkRFUEVORElFTlRFUw0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCg0KIyBQYXJhIGhhY2VybGEgZmFjdG9yIHBhcmEgcXVlIFIgbGEgdHJhdGUgY29tbyBjYXRlZ29yw61hLg0KZGF0b3Mkcmllc2dvX2FsdG8gPC0gZmFjdG9yKGRhdG9zJHJpZXNnb19hbHRvKQ0KDQojIFRhbWJpw6luIGxhcyBoYWdvIGEgZmFjdG9yIGxhcyB2YXJpYWJsZXMgY2F0ZWfDs3JpY2FzIGluZGVwZW5kaWVudGVzDQpkYXRvcyRzZXhvIDwtIGZhY3RvcihkYXRvcyRzZXhvKQ0KZGF0b3MkYWN0aXZpZGFkX2Zpc2ljYSA8LSBmYWN0b3IoZGF0b3MkYWN0aXZpZGFkX2Zpc2ljYSkNCmRhdG9zJGZ1bWFkb3IgPC0gZmFjdG9yKGRhdG9zJGZ1bWFkb3IpDQoNCiMgY3XDoW50YXMgcGVyc29uYXMgaGF5IGVuIGNhZGEgY2F0ZWdvcsOtYSBkZSByaWVzZ28uDQp0YWJsZShkYXRvcyRyaWVzZ29fYWx0bykNCg0KIyBlbCBwb3JjZW50YWplIGRlIHBlcnNvbmFzIGVuIGNhZGEgY2F0ZWdvcsOtYS4NCnByb3AudGFibGUodGFibGUoZGF0b3Mkcmllc2dvX2FsdG8pKQ0KYGBgDQpgYGB7cn0NCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIElJSS4gVkFSSUFCTEVTIFFVRSBTw40gU0UgVVNBUsOBTiBDT01PIElOREVQRU5ESUVOVEVTDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KDQojIFZhcmlhYmxlcyBudW3DqXJpY2FzIGRlIHNhbHVkLg0KdmFyaWFibGVzX251bWVyaWNhcyA8LSBjKA0KICAiZWRhZCIsDQogICJpbWMiLA0KICAicHJlc2lvbl9zaXN0b2xpY2EiLA0KICAiZ2x1Y29zYV9heXVubyINCikNCg0KIyBWYXJpYWJsZXMgY2F0ZWfDs3JpY2FzLg0KdmFyaWFibGVzX2NhdGVnb3JpY2FzIDwtIGMoDQogICJzZXhvIiwNCiAgImFjdGl2aWRhZF9maXNpY2EiLA0KICAiZnVtYWRvciINCikNCg0KIyBTb2xvIHBhcmEgdmVyIHF1ZSBxdWVkYXJvbiBiaWVuIGd1YXJkYWRhcy4gDQp2YXJpYWJsZXNfbnVtZXJpY2FzDQp2YXJpYWJsZXNfY2F0ZWdvcmljYXMNCmBgYA0KYGBge3J9DQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyBJVi4gQU7DgUxJU0lTIFZJU1VBTCAxOiBESVNUUklCVUNJw5NOIERFIExBIFZBUklBQkxFIERFUEVORElFTlRFDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KYmFycGxvdCgNCiAgdGFibGUoZGF0b3Mkcmllc2dvX2FsdG8pLA0KICBtYWluID0gIkRpc3RyaWJ1Y2nDs24gZGVsIHJpZXNnbyBjYXJkaW92YXNjdWxhciIsDQogIHhsYWIgPSAiTml2ZWwgZGUgcmllc2dvIiwNCiAgeWxhYiA9ICJOw7ptZXJvIGRlIHBlcnNvbmFzIiwNCiAgY29sID0gInB1cnBsZSINCikNCmBgYA0KYGBge3J9DQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyBWLiBBTsOBTElTSVMgVklTVUFMIDI6IFZBUklBQkxFUyBOVU3DiVJJQ0FTIFZTIFJJRVNHTyBBTFRPDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KDQojIEVkYWQgc2Vnw7puIG5pdmVsIGRlIHJpZXNnby4NCmJveHBsb3QoDQogIGVkYWQgfiByaWVzZ29fYWx0bywNCiAgZGF0YSA9IGRhdG9zLA0KICBtYWluID0gIkVkYWQgc2Vnw7puIHJpZXNnbyBjYXJkaW92YXNjdWxhciIsDQogIHhsYWIgPSAiUmllc2dvIGNhcmRpb3Zhc2N1bGFyIiwNCiAgeWxhYiA9ICJFZGFkIiwNCiAgY29sID0gImN5YW4iDQopDQoNCiMgSU1DIHNlZ8O6biBuaXZlbCBkZSByaWVzZ28uDQpib3hwbG90KA0KICBpbWMgfiByaWVzZ29fYWx0bywNCiAgZGF0YSA9IGRhdG9zLA0KICBtYWluID0gIklNQyBzZWfDum4gcmllc2dvIGNhcmRpb3Zhc2N1bGFyIiwNCiAgeGxhYiA9ICJSaWVzZ28gY2FyZGlvdmFzY3VsYXIiLA0KICB5bGFiID0gIklNQyIsDQogIGNvbCA9ICJtYWdlbnRhIg0KKQ0KDQojIFByZXNpw7NuIHNpc3TDs2xpY2Egc2Vnw7puIG5pdmVsIGRlIHJpZXNnby4NCmJveHBsb3QoDQogIHByZXNpb25fc2lzdG9saWNhIH4gcmllc2dvX2FsdG8sDQogIGRhdGEgPSBkYXRvcywNCiAgbWFpbiA9ICJQcmVzacOzbiBzaXN0w7NsaWNhIHNlZ8O6biByaWVzZ28gY2FyZGlvdmFzY3VsYXIiLA0KICB4bGFiID0gIlJpZXNnbyBjYXJkaW92YXNjdWxhciIsDQogIHlsYWIgPSAiUHJlc2nDs24gc2lzdMOzbGljYSIsDQogIGNvbCA9ICJ5ZWxsb3ciDQopDQoNCiMgR2x1Y29zYSBlbiBheXVubyBzZWfDum4gbml2ZWwgZGUgcmllc2dvLg0KYm94cGxvdCgNCiAgZ2x1Y29zYV9heXVubyB+IHJpZXNnb19hbHRvLA0KICBkYXRhID0gZGF0b3MsDQogIG1haW4gPSAiR2x1Y29zYSBlbiBheXVubyBzZWfDum4gcmllc2dvIGNhcmRpb3Zhc2N1bGFyIiwNCiAgeGxhYiA9ICJSaWVzZ28gY2FyZGlvdmFzY3VsYXIiLA0KICB5bGFiID0gIkdsdWNvc2EgZW4gYXl1bm8iLA0KICBjb2wgPSAib3JhbmdlIg0KKQ0KYGBgDQpgYGB7cn0NCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIFZJLiBBTsOBTElTSVMgVklTVUFMIDM6IFZBUklBQkxFUyBDQVRFR8OTUklDQVMgVlMgUklFU0dPIEFMVE8NCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQoNCiMgKioqKioqKioqKioqKioqKioqKioqKioqKioqKioNCiMgU2V4byB5IHJpZXNnbyBjYXJkaW92YXNjdWxhcg0KIyAqKioqKioqKioqKioqKioqKioqKioqKioqKioqKg0KDQojIFRhYmxhIGRlIGNvbnRlb3MuDQp0YWJsZShkYXRvcyRzZXhvLCBkYXRvcyRyaWVzZ29fYWx0bykNCg0KIyBUYWJsYSBkZSBwb3JjZW50YWplcyBwb3Igc2V4by4NCnByb3AudGFibGUodGFibGUoZGF0b3Mkc2V4bywgZGF0b3Mkcmllc2dvX2FsdG8pLCAxKQ0KDQojIEdyw6FmaWNhIGRlIGJhcnJhcy4NCmJhcnBsb3QoDQogIHRhYmxlKGRhdG9zJHNleG8sIGRhdG9zJHJpZXNnb19hbHRvKSwNCiAgYmVzaWRlID0gVFJVRSwNCiAgbGVnZW5kLnRleHQgPSBUUlVFLA0KICBtYWluID0gIlNleG8geSByaWVzZ28gY2FyZGlvdmFzY3VsYXIiLA0KICB4bGFiID0gIlJpZXNnbyBjYXJkaW92YXNjdWxhciIsDQogIHlsYWIgPSAiTsO6bWVybyBkZSBwZXJzb25hcyIsDQogIGNvbCA9IGMoImxpZ2h0Z3JlZW4iLCAiZGFya2dyZWVuIikNCikNCg0KDQojICoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioNCiMgQWN0aXZpZGFkIGbDrXNpY2EgeSByaWVzZ28gY2FyZGlvdmFzY3VsYXINCiMgKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKg0KDQojIFRhYmxhIGRlIGNvbnRlb3MuDQp0YWJsZShkYXRvcyRhY3RpdmlkYWRfZmlzaWNhLCBkYXRvcyRyaWVzZ29fYWx0bykNCg0KIyBUYWJsYSBkZSBwb3JjZW50YWplcyBwb3IgYWN0aXZpZGFkIGbDrXNpY2EuDQpwcm9wLnRhYmxlKHRhYmxlKGRhdG9zJGFjdGl2aWRhZF9maXNpY2EsIGRhdG9zJHJpZXNnb19hbHRvKSwgMSkNCg0KIyBHcsOhZmljYSBkZSBiYXJyYXMuDQpiYXJwbG90KA0KICB0YWJsZShkYXRvcyRhY3RpdmlkYWRfZmlzaWNhLCBkYXRvcyRyaWVzZ29fYWx0byksDQogIGJlc2lkZSA9IFRSVUUsDQogIGxlZ2VuZC50ZXh0ID0gVFJVRSwNCiAgbWFpbiA9ICJBY3RpdmlkYWQgZsOtc2ljYSB5IHJpZXNnbyBjYXJkaW92YXNjdWxhciIsDQogIHhsYWIgPSAiUmllc2dvIGNhcmRpb3Zhc2N1bGFyIiwNCiAgeWxhYiA9ICJOw7ptZXJvIGRlIHBlcnNvbmFzIiwNCiAgY29sID0gYygic2t5Ymx1ZSIsICJzdGVlbGJsdWUiLCAiZGFya2JsdWUiKQ0KKQ0KDQoNCiMgKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioNCiMgRnVtYWRvciB5IHJpZXNnbyBjYXJkaW92YXNjdWxhcg0KIyAqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKg0KDQojIFRhYmxhIGRlIGNvbnRlb3MuDQp0YWJsZShkYXRvcyRmdW1hZG9yLCBkYXRvcyRyaWVzZ29fYWx0bykNCg0KIyBUYWJsYSBkZSBwb3JjZW50YWplcyBwb3IgZnVtYWRvci4NCnByb3AudGFibGUodGFibGUoZGF0b3MkZnVtYWRvciwgZGF0b3Mkcmllc2dvX2FsdG8pLCAxKQ0KDQojIEdyw6FmaWNhIGRlIGJhcnJhcy4NCmJhcnBsb3QoDQogIHRhYmxlKGRhdG9zJGZ1bWFkb3IsIGRhdG9zJHJpZXNnb19hbHRvKSwNCiAgYmVzaWRlID0gVFJVRSwgI3BhcmEgY29tcGFyYXIgbWVqb3IgbGFzIGNhdGVnb3LDrWFzIHkgcXVlIHNhbGdhbiBwZWdhZGl0YXMgDQogIGxlZ2VuZC50ZXh0ID0gVFJVRSwNCiAgbWFpbiA9ICJGdW1hZG9yIHkgcmllc2dvIGNhcmRpb3Zhc2N1bGFyIiwNCiAgeGxhYiA9ICJSaWVzZ28gY2FyZGlvdmFzY3VsYXIiLA0KICB5bGFiID0gIk7Dum1lcm8gZGUgcGVyc29uYXMiLA0KICBjb2wgPSBjKCJmaXJlYnJpY2siLCAiZGFya3JlZCIpDQopDQpgYGANCmBgYHtyfQ0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyBWSUkuIEVYUExPUkFSIExBIFJFTEFDScOTTiBFTlRSRSBDQURBIFZBUklBQkxFIElOREVQRU5ESUVOVEUgWSBMQSBWQVJJQUJMRSBERVBFTkRJRU5URQ0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KDQojIENyZWFtb3MgdW5hIHZlcnNpw7NuIG51bcOpcmljYSBkZSBsYSB2YXJpYWJsZSBkZXBlbmRpZW50ZS4NCiMgcmllc2dvX251bSB2YWxlIDEgc2kgbGEgcGVyc29uYSB0aWVuZSByaWVzZ28gYWx0by4NCiMgcmllc2dvX251bSB2YWxlIDAgc2kgbGEgcGVyc29uYSB0aWVuZSByaWVzZ28gYmFqby4NCmRhdG9zJHJpZXNnb19udW0gPC0gMA0KZGF0b3Mkcmllc2dvX251bVtkYXRvcyRyaWVzZ29fYWx0byA9PSAiYWx0byJdIDwtIDENCg0KIyBTb2xvIHBhcmEgcmV2aXNhciBxdWUgc8OtIHNlIGhpem8gbGEgY29udmVyc2nDs24gDQp0YWJsZShkYXRvcyRyaWVzZ29fYWx0bywgZGF0b3Mkcmllc2dvX251bSkgDQoNCiMgKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKg0KIyBhLiBNT0RFTE8gMTogUkVMQUNJw5NOIEVOVFJFIEVEQUQgWSBSSUVTR08gQUxUTw0KIyAqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqDQoNCiMgUXVlcmVtb3MgdmVyIGPDs21vIGNhbWJpYSBlbCByaWVzZ28gYWx0byBjb25mb3JtZSBjYW1iaWEgbGEgZWRhZC4NCm1vZGVsb19sb2Vzc19lZGFkIDwtIGxvZXNzKHJpZXNnb19udW0gfiBlZGFkLCBkYXRhID0gZGF0b3MsIHNwYW4gPSAwLjc1KQ0KDQojIENyZWFtb3MgdW5hIHNlY3VlbmNpYSBkZSBlZGFkZXMgcGFyYSBwb2RlciBkaWJ1amFyIGxhIGN1cnZhIHN1YXZpemFkYS4NCnhfZWRhZCA8LSBzZXEoDQogIG1pbihkYXRvcyRlZGFkKSwNCiAgbWF4KGRhdG9zJGVkYWQpLA0KICBsZW5ndGgub3V0ID0gMTAwDQopIA0KDQojIENhbGN1bGFtb3MgbGFzIHByZWRpY2Npb25lcyBkZWwgbW9kZWxvIExPRVNTIHBhcmEgZXNhcyBlZGFkZXMuDQpwcmVkX2VkYWQgPC0gcHJlZGljdCgNCiAgbW9kZWxvX2xvZXNzX2VkYWQsDQogIG5ld2RhdGEgPSBkYXRhLmZyYW1lKGVkYWQgPSB4X2VkYWQpDQopDQoNCiMgR3JhZmljYW1vcyBsb3MgcHVudG9zIHJlYWxlcy4NCiMgQ29tbyByaWVzZ29fbnVtIHNvbG8gdmFsZSAwIG8gMSwgbG9zIHB1bnRvcyBzZSB2ZXLDoW4gYWJham8gbyBhcnJpYmEuDQpwbG90KA0KICBkYXRvcyRlZGFkLA0KICBkYXRvcyRyaWVzZ29fbnVtLA0KICBtYWluID0gIkxPRVNTOiBFZGFkIHkgcmllc2dvIGNhcmRpb3Zhc2N1bGFyIGFsdG8iLA0KICB4bGFiID0gIkVkYWQiLA0KICB5bGFiID0gIlJpZXNnbyBhbHRvOiAxID0gYWx0bywgMCA9IGJham8iLA0KICBwY2ggPSAxOSwNCiAgY29sID0gImdyYXkiLA0KICB5bGltID0gYygtMC4xLCAxLjEpDQopDQoNCiMgQWdyZWdhbW9zIGxhIGN1cnZhIExPRVNTLg0KbGluZXMoDQogIHhfZWRhZCwNCiAgcHJlZF9lZGFkLA0KICBjb2wgPSAiY3lhbiIsDQogIGx3ZCA9IDMNCikNCg0KIyAqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioNCiMgYi4gTU9ERUxPIDI6IElNQyBZIFJJRVNHTyBBTFRPIENPTiBMT0VTUw0KIyAqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioNCg0KIyBBanVzdGFtb3MgZWwgbW9kZWxvIExPRVNTIHBhcmEgSU1DLg0KbW9kZWxvX2xvZXNzX2ltYyA8LSBsb2VzcyhyaWVzZ29fbnVtIH4gaW1jLCBkYXRhID0gZGF0b3MsIHNwYW4gPSAwLjc1KQ0KDQojIENyZWFtb3MgdW5hIHNlY3VlbmNpYSBkZSB2YWxvcmVzIGRlIElNQy4NCnhfaW1jIDwtIHNlcSgNCiAgbWluKGRhdG9zJGltYyksDQogIG1heChkYXRvcyRpbWMpLA0KICBsZW5ndGgub3V0ID0gMTAwDQopDQoNCiMgQ2FsY3VsYW1vcyBwcmVkaWNjaW9uZXMuDQpwcmVkX2ltYyA8LSBwcmVkaWN0KA0KICBtb2RlbG9fbG9lc3NfaW1jLA0KICBuZXdkYXRhID0gZGF0YS5mcmFtZShpbWMgPSB4X2ltYykNCikNCg0KIyBHcmFmaWNhbW9zIHB1bnRvcyB5IGN1cnZhLg0KcGxvdCgNCiAgZGF0b3MkaW1jLA0KICBkYXRvcyRyaWVzZ29fbnVtLA0KICBtYWluID0gIkxPRVNTOiBJTUMgeSByaWVzZ28gY2FyZGlvdmFzY3VsYXIgYWx0byIsDQogIHhsYWIgPSAiSU1DIiwNCiAgeWxhYiA9ICJSaWVzZ28gYWx0bzogMSA9IGFsdG8sIDAgPSBiYWpvIiwNCiAgcGNoID0gMTksDQogIGNvbCA9ICJncmF5IiwNCiAgeWxpbSA9IGMoLTAuMSwgMS4xKQ0KKQ0KDQpsaW5lcygNCiAgeF9pbWMsDQogIHByZWRfaW1jLA0KICBjb2wgPSAibWFnZW50YSIsDQogIGx3ZCA9IDMNCikNCg0KIyAqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKg0KIyBjLiBNT0RFTE8gMzogUFJFU0nDk04gU0lTVMOTTElDQSBZIFJJRVNHTyBBTFRPIENPTiBMT0VTUw0KIyAqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKg0KDQojIEFqdXN0YW1vcyBlbCBtb2RlbG8gTE9FU1MgcGFyYSBwcmVzacOzbiBzaXN0w7NsaWNhLg0KbW9kZWxvX2xvZXNzX3ByZXNpb24gPC0gbG9lc3Mocmllc2dvX251bSB+IHByZXNpb25fc2lzdG9saWNhLCBkYXRhID0gZGF0b3MsIHNwYW4gPSAwLjc1KQ0KDQojIENyZWFtb3MgdW5hIHNlY3VlbmNpYSBkZSB2YWxvcmVzIGRlIHByZXNpw7NuLg0KeF9wcmVzaW9uIDwtIHNlcSgNCiAgbWluKGRhdG9zJHByZXNpb25fc2lzdG9saWNhKSwNCiAgbWF4KGRhdG9zJHByZXNpb25fc2lzdG9saWNhKSwNCiAgbGVuZ3RoLm91dCA9IDEwMA0KKQ0KDQojIENhbGN1bGFtb3MgcHJlZGljY2lvbmVzLg0KcHJlZF9wcmVzaW9uIDwtIHByZWRpY3QoDQogIG1vZGVsb19sb2Vzc19wcmVzaW9uLA0KICBuZXdkYXRhID0gZGF0YS5mcmFtZShwcmVzaW9uX3Npc3RvbGljYSA9IHhfcHJlc2lvbikNCikNCg0KIyBHcmFmaWNhbW9zIHB1bnRvcyB5IGN1cnZhLg0KcGxvdCgNCiAgZGF0b3MkcHJlc2lvbl9zaXN0b2xpY2EsDQogIGRhdG9zJHJpZXNnb19udW0sDQogIG1haW4gPSAiTE9FU1M6IFByZXNpw7NuIHNpc3TDs2xpY2EgeSByaWVzZ28gY2FyZGlvdmFzY3VsYXIgYWx0byIsDQogIHhsYWIgPSAiUHJlc2nDs24gc2lzdMOzbGljYSIsDQogIHlsYWIgPSAiUmllc2dvIGFsdG86IDEgPSBhbHRvLCAwID0gYmFqbyIsDQogIHBjaCA9IDE5LA0KICBjb2wgPSAiZ3JheSIsDQogIHlsaW0gPSBjKC0wLjEsIDEuMSkNCikNCg0KbGluZXMoDQogIHhfcHJlc2lvbiwNCiAgcHJlZF9wcmVzaW9uLA0KICBjb2wgPSAieWVsbG93IiwNCiAgbHdkID0gMw0KKQ0KDQojICoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqDQojIGQuIE1PREVMTyA0OiBHTFVDT1NBIEVOIEFZVU5PIFkgUklFU0dPIEFMVE8gQ09OIExPRVNTDQojICoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqDQoNCiMgQWp1c3RhbW9zIGVsIG1vZGVsbyBMT0VTUyBwYXJhIGdsdWNvc2EgZW4gYXl1bm8uDQptb2RlbG9fbG9lc3NfZ2x1Y29zYSA8LSBsb2VzcyhyaWVzZ29fbnVtIH4gZ2x1Y29zYV9heXVubywgZGF0YSA9IGRhdG9zLCBzcGFuID0gMC43NSkNCg0KIyBDcmVhbW9zIHVuYSBzZWN1ZW5jaWEgZGUgdmFsb3JlcyBkZSBnbHVjb3NhLg0KeF9nbHVjb3NhIDwtIHNlcSgNCiAgbWluKGRhdG9zJGdsdWNvc2FfYXl1bm8pLA0KICBtYXgoZGF0b3MkZ2x1Y29zYV9heXVubyksDQogIGxlbmd0aC5vdXQgPSAxMDANCikNCg0KIyBDYWxjdWxhbW9zIHByZWRpY2Npb25lcy4NCnByZWRfZ2x1Y29zYSA8LSBwcmVkaWN0KA0KICBtb2RlbG9fbG9lc3NfZ2x1Y29zYSwNCiAgbmV3ZGF0YSA9IGRhdGEuZnJhbWUoZ2x1Y29zYV9heXVubyA9IHhfZ2x1Y29zYSkNCikNCg0KIyBHcmFmaWNhbW9zIHB1bnRvcyB5IGN1cnZhLg0KcGxvdCgNCiAgZGF0b3MkZ2x1Y29zYV9heXVubywNCiAgZGF0b3Mkcmllc2dvX251bSwNCiAgbWFpbiA9ICJMT0VTUzogR2x1Y29zYSBlbiBheXVubyB5IHJpZXNnbyBjYXJkaW92YXNjdWxhciBhbHRvIiwNCiAgeGxhYiA9ICJHbHVjb3NhIGVuIGF5dW5vIiwNCiAgeWxhYiA9ICJSaWVzZ28gYWx0bzogMSA9IGFsdG8sIDAgPSBiYWpvIiwNCiAgcGNoID0gMTksDQogIGNvbCA9ICJncmF5IiwNCiAgeWxpbSA9IGMoLTAuMSwgMS4xKQ0KKQ0KDQpsaW5lcygNCiAgeF9nbHVjb3NhLA0KICBwcmVkX2dsdWNvc2EsDQogIGNvbCA9ICJvcmFuZ2UiLA0KICBsd2QgPSAzDQopDQpgYGANCmBgYHtyfQ0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgVklJSS4JUkVWSVNJw5NOIERFIExBIFNFTlNJQklMSURBRCBERUwgU1BBTiBQT1IgVkFSSUFCTEUNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQoNCiMgKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKg0KIyBhLiBNT0RFTE8gMTogUkVMQUNJw5NOIEVOVFJFIEVEQUQgWSBSSUVTR08gQUxUTw0KIyAqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqDQoNCiMgMC40ID0gY3VydmEgbcOhcyBmbGV4aWJsZS4NCiMgMC43NSA9IGN1cnZhIGludGVybWVkaWEsIGxhIHF1ZSB5YSB1c2Ftb3MuDQojIDAuOSA9IGN1cnZhIG3DoXMgc3VhdmUuDQpsb2Vzc19lZGFkXzA0IDwtIGxvZXNzKHJpZXNnb19udW0gfiBlZGFkLCBkYXRhID0gZGF0b3MsIHNwYW4gPSAwLjQpDQpsb2Vzc19lZGFkXzA3NSA8LSBsb2VzcyhyaWVzZ29fbnVtIH4gZWRhZCwgZGF0YSA9IGRhdG9zLCBzcGFuID0gMC43NSkNCmxvZXNzX2VkYWRfMDkgPC0gbG9lc3Mocmllc2dvX251bSB+IGVkYWQsIGRhdGEgPSBkYXRvcywgc3BhbiA9IDAuOSkNCg0KIyBTZWN1ZW5jaWEgZGUgZWRhZGVzIHBhcmEgZ3JhZmljYXIgbGFzIGN1cnZhcy4NCnhfZWRhZCA8LSBzZXEobWluKGRhdG9zJGVkYWQpLCBtYXgoZGF0b3MkZWRhZCksIGxlbmd0aC5vdXQgPSAxMDApDQoNCiMgUHJlZGljY2lvbmVzIHBhcmEgY2FkYSBtb2RlbG8uDQpwcmVkX2VkYWRfMDQgPC0gcHJlZGljdChsb2Vzc19lZGFkXzA0LCBuZXdkYXRhID0gZGF0YS5mcmFtZShlZGFkID0geF9lZGFkKSkNCnByZWRfZWRhZF8wNzUgPC0gcHJlZGljdChsb2Vzc19lZGFkXzA3NSwgbmV3ZGF0YSA9IGRhdGEuZnJhbWUoZWRhZCA9IHhfZWRhZCkpDQpwcmVkX2VkYWRfMDkgPC0gcHJlZGljdChsb2Vzc19lZGFkXzA5LCBuZXdkYXRhID0gZGF0YS5mcmFtZShlZGFkID0geF9lZGFkKSkNCg0KIyBHcmFmaWNhbW9zIGxvcyBwdW50b3MgcmVhbGVzLg0KcGxvdCgNCiAgZGF0b3MkZWRhZCwNCiAgZGF0b3Mkcmllc2dvX251bSwNCiAgbWFpbiA9ICJDb21wYXJhY2nDs24gZGUgc3BhbiBlbiBMT0VTUzogRWRhZCIsDQogIHhsYWIgPSAiRWRhZCIsDQogIHlsYWIgPSAiUmllc2dvIGFsdG86IDEgPSBhbHRvLCAwID0gYmFqbyIsDQogIHBjaCA9IDE5LA0KICBjb2wgPSAiZ3JheSIsDQogIHlsaW0gPSBjKC0wLjEsIDEuMSkNCikNCg0KIyBBZ3JlZ2Ftb3MgbGFzIHRyZXMgY3VydmFzLg0KbGluZXMoeF9lZGFkLCBwcmVkX2VkYWRfMDQsIGNvbCA9ICJuYXZ5IiwgbHdkID0gMikNCmxpbmVzKHhfZWRhZCwgcHJlZF9lZGFkXzA3NSwgY29sID0gImN5YW4iLCBsd2QgPSAzKQ0KbGluZXMoeF9lZGFkLCBwcmVkX2VkYWRfMDksIGNvbCA9ICJicm93biIsIGx3ZCA9IDIpDQoNCiMgQWdyZWdhbW9zIGxleWVuZGEuDQpsZWdlbmQoDQogICJ0b3BsZWZ0IiwNCiAgbGVnZW5kID0gYygic3BhbiA9IDAuNCIsICJzcGFuID0gMC43NSIsICJzcGFuID0gMC45IiksDQogIGNvbCA9IGMoIm5hdnkiLCAiY3lhbiIsICJicm93biIpLA0KICBsd2QgPSBjKDIsIDMsIDIpDQopDQoNCiMgKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqDQojIGIuIE1PREVMTyAyOiBJTUMgWSBSSUVTR08gQUxUTyBDT04gTE9FU1MNCiMgKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqDQoNCmxvZXNzX2ltY18wNCA8LSBsb2VzcyhyaWVzZ29fbnVtIH4gaW1jLCBkYXRhID0gZGF0b3MsIHNwYW4gPSAwLjQpDQpsb2Vzc19pbWNfMDc1IDwtIGxvZXNzKHJpZXNnb19udW0gfiBpbWMsIGRhdGEgPSBkYXRvcywgc3BhbiA9IDAuNzUpDQpsb2Vzc19pbWNfMDkgPC0gbG9lc3Mocmllc2dvX251bSB+IGltYywgZGF0YSA9IGRhdG9zLCBzcGFuID0gMC45KQ0KDQp4X2ltYyA8LSBzZXEobWluKGRhdG9zJGltYyksIG1heChkYXRvcyRpbWMpLCBsZW5ndGgub3V0ID0gMTAwKQ0KDQpwcmVkX2ltY18wNCA8LSBwcmVkaWN0KGxvZXNzX2ltY18wNCwgbmV3ZGF0YSA9IGRhdGEuZnJhbWUoaW1jID0geF9pbWMpKQ0KcHJlZF9pbWNfMDc1IDwtIHByZWRpY3QobG9lc3NfaW1jXzA3NSwgbmV3ZGF0YSA9IGRhdGEuZnJhbWUoaW1jID0geF9pbWMpKQ0KcHJlZF9pbWNfMDkgPC0gcHJlZGljdChsb2Vzc19pbWNfMDksIG5ld2RhdGEgPSBkYXRhLmZyYW1lKGltYyA9IHhfaW1jKSkNCg0KcGxvdCgNCiAgZGF0b3MkaW1jLA0KICBkYXRvcyRyaWVzZ29fbnVtLA0KICBtYWluID0gIkNvbXBhcmFjacOzbiBkZSBzcGFuIGVuIExPRVNTOiBJTUMiLA0KICB4bGFiID0gIklNQyIsDQogIHlsYWIgPSAiUmllc2dvIGFsdG86IDEgPSBhbHRvLCAwID0gYmFqbyIsDQogIHBjaCA9IDE5LA0KICBjb2wgPSAiZ3JheSIsDQogIHlsaW0gPSBjKC0wLjEsIDEuMSkNCikNCg0KbGluZXMoeF9pbWMsIHByZWRfaW1jXzA0LCBjb2wgPSAibmF2eSIsIGx3ZCA9IDIpDQpsaW5lcyh4X2ltYywgcHJlZF9pbWNfMDc1LCBjb2wgPSAibWFnZW50YSIsIGx3ZCA9IDMpDQpsaW5lcyh4X2ltYywgcHJlZF9pbWNfMDksIGNvbCA9ICJicm93biIsIGx3ZCA9IDIpDQoNCmxlZ2VuZCgNCiAgInRvcGxlZnQiLA0KICBsZWdlbmQgPSBjKCJzcGFuID0gMC40IiwgInNwYW4gPSAwLjc1IiwgInNwYW4gPSAwLjkiKSwNCiAgY29sID0gYygibmF2eSIsICJtYWdlbnRhIiwgImJyb3duIiksDQogIGx3ZCA9IGMoMiwgMywgMikNCikNCg0KIyAqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKg0KIyBjLiBNT0RFTE8gMzogUFJFU0nDk04gU0lTVMOTTElDQSBZIFJJRVNHTyBBTFRPIENPTiBMT0VTUw0KIyAqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKg0KDQpsb2Vzc19wcmVzaW9uXzA0IDwtIGxvZXNzKHJpZXNnb19udW0gfiBwcmVzaW9uX3Npc3RvbGljYSwgZGF0YSA9IGRhdG9zLCBzcGFuID0gMC40KQ0KbG9lc3NfcHJlc2lvbl8wNzUgPC0gbG9lc3Mocmllc2dvX251bSB+IHByZXNpb25fc2lzdG9saWNhLCBkYXRhID0gZGF0b3MsIHNwYW4gPSAwLjc1KQ0KbG9lc3NfcHJlc2lvbl8wOSA8LSBsb2VzcyhyaWVzZ29fbnVtIH4gcHJlc2lvbl9zaXN0b2xpY2EsIGRhdGEgPSBkYXRvcywgc3BhbiA9IDAuOSkNCg0KeF9wcmVzaW9uIDwtIHNlcShtaW4oZGF0b3MkcHJlc2lvbl9zaXN0b2xpY2EpLCBtYXgoZGF0b3MkcHJlc2lvbl9zaXN0b2xpY2EpLCBsZW5ndGgub3V0ID0gMTAwKQ0KDQpwcmVkX3ByZXNpb25fMDQgPC0gcHJlZGljdChsb2Vzc19wcmVzaW9uXzA0LCBuZXdkYXRhID0gZGF0YS5mcmFtZShwcmVzaW9uX3Npc3RvbGljYSA9IHhfcHJlc2lvbikpDQpwcmVkX3ByZXNpb25fMDc1IDwtIHByZWRpY3QobG9lc3NfcHJlc2lvbl8wNzUsIG5ld2RhdGEgPSBkYXRhLmZyYW1lKHByZXNpb25fc2lzdG9saWNhID0geF9wcmVzaW9uKSkNCnByZWRfcHJlc2lvbl8wOSA8LSBwcmVkaWN0KGxvZXNzX3ByZXNpb25fMDksIG5ld2RhdGEgPSBkYXRhLmZyYW1lKHByZXNpb25fc2lzdG9saWNhID0geF9wcmVzaW9uKSkNCg0KcGxvdCgNCiAgZGF0b3MkcHJlc2lvbl9zaXN0b2xpY2EsDQogIGRhdG9zJHJpZXNnb19udW0sDQogIG1haW4gPSAiQ29tcGFyYWNpw7NuIGRlIHNwYW4gZW4gTE9FU1M6IFByZXNpw7NuIHNpc3TDs2xpY2EiLA0KICB4bGFiID0gIlByZXNpw7NuIHNpc3TDs2xpY2EiLA0KICB5bGFiID0gIlJpZXNnbyBhbHRvOiAxID0gYWx0bywgMCA9IGJham8iLA0KICBwY2ggPSAxOSwNCiAgY29sID0gImdyYXkiLA0KICB5bGltID0gYygtMC4xLCAxLjEpDQopDQoNCmxpbmVzKHhfcHJlc2lvbiwgcHJlZF9wcmVzaW9uXzA0LCBjb2wgPSAibmF2eSIsIGx3ZCA9IDIpDQpsaW5lcyh4X3ByZXNpb24sIHByZWRfcHJlc2lvbl8wNzUsIGNvbCA9ICJ5ZWxsb3ciLCBsd2QgPSAzKQ0KbGluZXMoeF9wcmVzaW9uLCBwcmVkX3ByZXNpb25fMDksIGNvbCA9ICJicm93biIsIGx3ZCA9IDIpDQoNCmxlZ2VuZCgNCiAgInRvcGxlZnQiLA0KICBsZWdlbmQgPSBjKCJzcGFuID0gMC40IiwgInNwYW4gPSAwLjc1IiwgInNwYW4gPSAwLjkiKSwNCiAgY29sID0gYygibmF2eSIsICJ5ZWxsb3ciLCAiYnJvd24iKSwNCiAgbHdkID0gYygyLCAzLCAyKQ0KKQ0KDQojICoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqDQojIGQuIE1PREVMTyA0OiBHTFVDT1NBIEVOIEFZVU5PIFkgUklFU0dPIEFMVE8gQ09OIExPRVNTDQojICoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqDQoNCmxvZXNzX2dsdWNvc2FfMDQgPC0gbG9lc3Mocmllc2dvX251bSB+IGdsdWNvc2FfYXl1bm8sIGRhdGEgPSBkYXRvcywgc3BhbiA9IDAuNCkNCmxvZXNzX2dsdWNvc2FfMDc1IDwtIGxvZXNzKHJpZXNnb19udW0gfiBnbHVjb3NhX2F5dW5vLCBkYXRhID0gZGF0b3MsIHNwYW4gPSAwLjc1KQ0KbG9lc3NfZ2x1Y29zYV8wOSA8LSBsb2VzcyhyaWVzZ29fbnVtIH4gZ2x1Y29zYV9heXVubywgZGF0YSA9IGRhdG9zLCBzcGFuID0gMC45KQ0KDQp4X2dsdWNvc2EgPC0gc2VxKG1pbihkYXRvcyRnbHVjb3NhX2F5dW5vKSwgbWF4KGRhdG9zJGdsdWNvc2FfYXl1bm8pLCBsZW5ndGgub3V0ID0gMTAwKQ0KDQpwcmVkX2dsdWNvc2FfMDQgPC0gcHJlZGljdChsb2Vzc19nbHVjb3NhXzA0LCBuZXdkYXRhID0gZGF0YS5mcmFtZShnbHVjb3NhX2F5dW5vID0geF9nbHVjb3NhKSkNCnByZWRfZ2x1Y29zYV8wNzUgPC0gcHJlZGljdChsb2Vzc19nbHVjb3NhXzA3NSwgbmV3ZGF0YSA9IGRhdGEuZnJhbWUoZ2x1Y29zYV9heXVubyA9IHhfZ2x1Y29zYSkpDQpwcmVkX2dsdWNvc2FfMDkgPC0gcHJlZGljdChsb2Vzc19nbHVjb3NhXzA5LCBuZXdkYXRhID0gZGF0YS5mcmFtZShnbHVjb3NhX2F5dW5vID0geF9nbHVjb3NhKSkNCg0KcGxvdCgNCiAgZGF0b3MkZ2x1Y29zYV9heXVubywNCiAgZGF0b3Mkcmllc2dvX251bSwNCiAgbWFpbiA9ICJDb21wYXJhY2nDs24gZGUgc3BhbiBlbiBMT0VTUzogR2x1Y29zYSBlbiBheXVubyIsDQogIHhsYWIgPSAiR2x1Y29zYSBlbiBheXVubyIsDQogIHlsYWIgPSAiUmllc2dvIGFsdG86IDEgPSBhbHRvLCAwID0gYmFqbyIsDQogIHBjaCA9IDE5LA0KICBjb2wgPSAiZ3JheSIsDQogIHlsaW0gPSBjKC0wLjEsIDEuMSkNCikNCg0KbGluZXMoeF9nbHVjb3NhLCBwcmVkX2dsdWNvc2FfMDQsIGNvbCA9ICJuYXZ5IiwgbHdkID0gMikNCmxpbmVzKHhfZ2x1Y29zYSwgcHJlZF9nbHVjb3NhXzA3NSwgY29sID0gIm9yYW5nZSIsIGx3ZCA9IDMpDQpsaW5lcyh4X2dsdWNvc2EsIHByZWRfZ2x1Y29zYV8wOSwgY29sID0gImJyb3duIiwgbHdkID0gMikNCg0KbGVnZW5kKA0KICAidG9wbGVmdCIsDQogIGxlZ2VuZCA9IGMoInNwYW4gPSAwLjQiLCAic3BhbiA9IDAuNzUiLCAic3BhbiA9IDAuOSIpLA0KICBjb2wgPSBjKCJuYXZ5IiwgIm9yYW5nZSIsICJicm93biIpLA0KICBsd2QgPSBjKDIsIDMsIDIpDQopDQpgYGANCmBgYHtyfQ0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgWC4gUkVWSVNJw5NOIERFIEVSUk9SRVMgREUgTE9TIE1PREVMT1MgTE9FU1MNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQoNCiMgQ2FsY3VsYW1vcyBsYXMgcHJlZGljY2lvbmVzIGRlIGNhZGEgbW9kZWxvIHNvYnJlIGxvcyBtaXNtb3MgZGF0b3MuDQojIEVzdG8gbm9zIHBlcm1pdGUgY29tcGFyYXIgbG8gcXVlIGVsIG1vZGVsbyBwcmVkaWpvIGNvbnRyYSBlbCB2YWxvciByZWFsLg0KcHJlZF9lZGFkX2RhdG9zIDwtIHByZWRpY3QobW9kZWxvX2xvZXNzX2VkYWQpDQpwcmVkX2ltY19kYXRvcyA8LSBwcmVkaWN0KG1vZGVsb19sb2Vzc19pbWMpDQpwcmVkX3ByZXNpb25fZGF0b3MgPC0gcHJlZGljdChtb2RlbG9fbG9lc3NfcHJlc2lvbikNCnByZWRfZ2x1Y29zYV9kYXRvcyA8LSBwcmVkaWN0KG1vZGVsb19sb2Vzc19nbHVjb3NhKQ0KDQojIE1TRSA9IHByb21lZGlvIGRlIGxvcyBlcnJvcmVzIGFsIGN1YWRyYWRvLg0KbXNlIDwtIGZ1bmN0aW9uKHJlYWwsIHByZWRpY2hvKSB7DQogIG1lYW4oKHJlYWwgLSBwcmVkaWNobyleMikNCn0NCg0KIyBSTVNFID0gcmHDrXogY3VhZHJhZGEgZGVsIE1TRS4NCnJtc2UgPC0gZnVuY3Rpb24ocmVhbCwgcHJlZGljaG8pIHsNCiAgc3FydChtZWFuKChyZWFsIC0gcHJlZGljaG8pXjIpKQ0KfQ0KDQojIFRhYmxhIHBhcmEgY29tcGFyYXIgbG9zIGVycm9yZXMgZGUgY2FkYSBtb2RlbG8uDQp0YWJsYV9lcnJvcmVzX2xvZXNzIDwtIGRhdGEuZnJhbWUoDQogIHZhcmlhYmxlID0gYygiZWRhZCIsICJpbWMiLCAicHJlc2lvbl9zaXN0b2xpY2EiLCAiZ2x1Y29zYV9heXVubyIpLA0KICBNU0UgPSBjKA0KICAgIG1zZShkYXRvcyRyaWVzZ29fbnVtLCBwcmVkX2VkYWRfZGF0b3MpLA0KICAgIG1zZShkYXRvcyRyaWVzZ29fbnVtLCBwcmVkX2ltY19kYXRvcyksDQogICAgbXNlKGRhdG9zJHJpZXNnb19udW0sIHByZWRfcHJlc2lvbl9kYXRvcyksDQogICAgbXNlKGRhdG9zJHJpZXNnb19udW0sIHByZWRfZ2x1Y29zYV9kYXRvcykNCiAgKSwNCiAgUk1TRSA9IGMoDQogICAgcm1zZShkYXRvcyRyaWVzZ29fbnVtLCBwcmVkX2VkYWRfZGF0b3MpLA0KICAgIHJtc2UoZGF0b3Mkcmllc2dvX251bSwgcHJlZF9pbWNfZGF0b3MpLA0KICAgIHJtc2UoZGF0b3Mkcmllc2dvX251bSwgcHJlZF9wcmVzaW9uX2RhdG9zKSwNCiAgICBybXNlKGRhdG9zJHJpZXNnb19udW0sIHByZWRfZ2x1Y29zYV9kYXRvcykNCiAgKQ0KKQ0KDQojIE1vc3RyYW1vcyBsYSB0YWJsYSBkZSBlcnJvcmVzLg0KdGFibGFfZXJyb3Jlc19sb2Vzcw0KYGBgDQpgYGB7cn0NCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIElYLiBHUsOBRklDQVMgREUgUkVTSURVT1MgREUgTE9TIE1PREVMT1MgTE9FU1MNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQoNCiMgUmVzaWR1b3MgZGVsIG1vZGVsbyBjb24gZWRhZC4NCnBsb3QoDQogIGRhdG9zJGVkYWQsDQogIG1vZGVsb19sb2Vzc19lZGFkJHJlc2lkdWFscywNCiAgbWFpbiA9ICJSZXNpZHVvcyBMT0VTUzogRWRhZCIsDQogIHhsYWIgPSAiRWRhZCIsDQogIHlsYWIgPSAiUmVzaWR1byIsDQogIHBjaCA9IDE5LA0KICBjb2wgPSAiZ3JheSINCikNCmFibGluZShoID0gMCwgY29sID0gImN5YW4iLCBsdHkgPSAyLCBsd2QgPSAyKQ0KDQoNCiMgUmVzaWR1b3MgZGVsIG1vZGVsbyBjb24gSU1DLg0KcGxvdCgNCiAgZGF0b3MkaW1jLA0KICBtb2RlbG9fbG9lc3NfaW1jJHJlc2lkdWFscywNCiAgbWFpbiA9ICJSZXNpZHVvcyBMT0VTUzogSU1DIiwNCiAgeGxhYiA9ICJJTUMiLA0KICB5bGFiID0gIlJlc2lkdW8iLA0KICBwY2ggPSAxOSwNCiAgY29sID0gImdyYXkiDQopDQphYmxpbmUoaCA9IDAsIGNvbCA9ICJtYWdlbnRhIiwgbHR5ID0gMiwgbHdkID0gMikNCg0KDQojIFJlc2lkdW9zIGRlbCBtb2RlbG8gY29uIHByZXNpw7NuIHNpc3TDs2xpY2EuDQpwbG90KA0KICBkYXRvcyRwcmVzaW9uX3Npc3RvbGljYSwNCiAgbW9kZWxvX2xvZXNzX3ByZXNpb24kcmVzaWR1YWxzLA0KICBtYWluID0gIlJlc2lkdW9zIExPRVNTOiBQcmVzacOzbiBzaXN0w7NsaWNhIiwNCiAgeGxhYiA9ICJQcmVzacOzbiBzaXN0w7NsaWNhIiwNCiAgeWxhYiA9ICJSZXNpZHVvIiwNCiAgcGNoID0gMTksDQogIGNvbCA9ICJncmF5Ig0KKQ0KYWJsaW5lKGggPSAwLCBjb2wgPSAieWVsbG93IiwgbHR5ID0gMiwgbHdkID0gMikNCg0KDQojIFJlc2lkdW9zIGRlbCBtb2RlbG8gY29uIGdsdWNvc2EgZW4gYXl1bm8uDQpwbG90KA0KICBkYXRvcyRnbHVjb3NhX2F5dW5vLA0KICBtb2RlbG9fbG9lc3NfZ2x1Y29zYSRyZXNpZHVhbHMsDQogIG1haW4gPSAiUmVzaWR1b3MgTE9FU1M6IEdsdWNvc2EgZW4gYXl1bm8iLA0KICB4bGFiID0gIkdsdWNvc2EgZW4gYXl1bm8iLA0KICB5bGFiID0gIlJlc2lkdW8iLA0KICBwY2ggPSAxOSwNCiAgY29sID0gImdyYXkiDQopDQphYmxpbmUoaCA9IDAsIGNvbCA9ICJvcmFuZ2UiLCBsdHkgPSAyLCBsd2QgPSAyKQ0KYGBgDQpgYGB7cn0NCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIFhJSS4gSU5DSVNPIDI6IE1PREVMTyBHQU0gQ09OIFRPREFTIExBUyBWQVJJQUJMRVMgSU5ERVBFTkRJRU5URVMNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQoNCmxpYnJhcnkobWdjdikNCmRhdG9zJHJpZXNnb19hbHRvIDwtIGZhY3RvcihkYXRvcyRyaWVzZ29fYWx0bykNCmRhdG9zJHNleG8gPC0gZmFjdG9yKGRhdG9zJHNleG8pDQpkYXRvcyRhY3RpdmlkYWRfZmlzaWNhIDwtIGZhY3RvcihkYXRvcyRhY3RpdmlkYWRfZmlzaWNhKQ0KZGF0b3MkZnVtYWRvciA8LSBmYWN0b3IoZGF0b3MkZnVtYWRvcikNCg0KIyBTb2xvIHBhcmEgY29uZmlybWFyIGxhIHZhcmlhYmxlIG51bcOpcmljYSBkZSByaWVzZ28uDQojIHJpZXNnb19udW0gPSAxIHNpIGxhIHBlcnNvbmEgdGllbmUgcmllc2dvIGFsdG8uDQojIHJpZXNnb19udW0gPSAwIHNpIGxhIHBlcnNvbmEgdGllbmUgcmllc2dvIGJham8uDQpkYXRvcyRyaWVzZ29fbnVtIDwtIDANCmRhdG9zJHJpZXNnb19udW1bZGF0b3Mkcmllc2dvX2FsdG8gPT0gImFsdG8iXSA8LSAxDQoNCiMgQWp1c3RvIGVsIG1vZGVsbyBHQU0uDQojIExhcyB2YXJpYWJsZXMgbnVtw6lyaWNhcyBlbnRyYW4gY29uIHMoKSwgcG9ycXVlIHF1aWVybyBwZXJtaXRpciByZWxhY2lvbmVzIG5vIGxpbmVhbGVzIHN1YXZlcy4NCiMgTGFzIHZhcmlhYmxlcyBjYXRlZ8OzcmljYXMgZW50cmFuIGRpcmVjdG8gY29tbyBmYWN0b3Jlcy4NCm1vZGVsb19nYW0gPC0gZ2FtKA0KICByaWVzZ29fbnVtIH4gDQogICAgcyhlZGFkLCBrID0gMTApICsNCiAgICBzKGltYywgayA9IDEwKSArDQogICAgcyhwcmVzaW9uX3Npc3RvbGljYSwgayA9IDEwKSArDQogICAgcyhnbHVjb3NhX2F5dW5vLCBrID0gMTApICsNCiAgICBzZXhvICsNCiAgICBhY3RpdmlkYWRfZmlzaWNhICsNCiAgICBmdW1hZG9yLA0KICBkYXRhID0gZGF0b3MsDQogIGZhbWlseSA9IGJpbm9taWFsKCksICNlbiBsdWdhciBkZSBmYW1pbHkgPSBnYXVzc2lhbigpDQogIG1ldGhvZCA9ICJSRU1MIiANCikNCg0Kc3VtbWFyeShtb2RlbG9fZ2FtKQ0KYGBgDQpgYGB7cn0NCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIFhJSUkuIEdSw4FGSUNBUyBERSBGVU5DSU9ORVMgU1VBVkVTIERFTCBHQU0NCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQoNCiMgUGFyYSB2ZXIgbGEgY29udHJpYnVjacOzbiBwYXJjaWFsIGRlIGNhZGEgdmFyaWFibGUgbnVtw6lyaWNhLg0KcGFyKG1mcm93ID0gYygyLCAyKSkNCg0KcGxvdCgNCiAgbW9kZWxvX2dhbSwNCiAgc2hhZGUgPSBUUlVFLA0KICBtYWluID0gIkZ1bmNpb25lcyBzdWF2ZXMgZGVsIG1vZGVsbyBHQU0iDQopDQoNCnBhcihtZnJvdyA9IGMoMSwgMSkpIA0KDQojIHBhcmEgdmVyIHNpIGxhIGZsZXhpYmlsaWRhZCBlbGVnaWRhIHBhcmEgbGFzIGZ1bmNpb25lcyBzdWF2ZXMgcGFyZWNlIGFkZWN1YWRhLg0KZ2FtLmNoZWNrKG1vZGVsb19nYW0pDQpgYGANCmBgYHtyfQ0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgWElWLiBQUkVESUNDScOTTiBZIEVWQUxVQUNJw5NOIERFTCBHQU0NCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQoNCiMgUGFyYSBtw606IHR5cGUgPSAicmVzcG9uc2UiIGhhY2UgcXVlIGxhcyBwcmVkaWNjaW9uZXMgc2FsZ2FuIGVudHJlIDAgeSAxLg0KIyBWYWxvcmVzIGNlcmNhbm9zIGEgMSBpbmRpY2FuIG1heW9yIHRlbmRlbmNpYSBkZSByaWVzZ28gYWx0by4NCnByZWRfZ2FtIDwtIHByZWRpY3QoDQogIG1vZGVsb19nYW0sDQogIHR5cGUgPSAicmVzcG9uc2UiDQopDQoNCnN1bW1hcnkocHJlZF9nYW0pDQoNCiMgQ29udmVydGltb3MgbGFzIHByZWRpY2Npb25lcyBudW3DqXJpY2FzIGVuIGNhdGVnb3LDrWFzLg0KIyBTaSBsYSBwcmVkaWNjacOzbiBlcyBtYXlvciBvIGlndWFsIGEgMC41LCBkZWNpbW9zICJhbHRvIi4NCiMgU2kgZXMgbWVub3IgYSAwLjUsIGRlY2ltb3MgImJham8iLg0KcHJlZF9nYW1fY2F0ZWdvcmlhIDwtIGlmZWxzZSgNCiAgcHJlZF9nYW0gPj0gMC41LA0KICAiYWx0byIsDQogICJiYWpvIg0KKQ0KDQojIENvbnZlcnRpbW9zIGxhIHByZWRpY2Npw7NuIGEgZmFjdG9yLg0KcHJlZF9nYW1fY2F0ZWdvcmlhIDwtIGZhY3RvcigNCiAgcHJlZF9nYW1fY2F0ZWdvcmlhLA0KICBsZXZlbHMgPSBjKCJiYWpvIiwgImFsdG8iKQ0KKQ0KDQojIE5vcyBhc2VndXJhbW9zIGRlIHF1ZSBsYSB2YXJpYWJsZSByZWFsIHRlbmdhIGVsIG1pc21vIG9yZGVuLg0KZGF0b3Mkcmllc2dvX2FsdG8gPC0gZmFjdG9yKA0KICBkYXRvcyRyaWVzZ29fYWx0bywNCiAgbGV2ZWxzID0gYygiYmFqbyIsICJhbHRvIikNCikNCg0KIyBIYWNlbW9zIHVuYSB0YWJsYSBwYXJhIGNvbXBhcmFyIGxhIGNhdGVnb3LDrWEgcmVhbA0KIyBjb250cmEgbGEgY2F0ZWdvcsOtYSBwcmVkaWNoYSBwb3IgZWwgbW9kZWxvLg0KdGFibGFfcHJlZGljY2lvbl9nYW0gPC0gdGFibGUoDQogIFJlYWwgPSBkYXRvcyRyaWVzZ29fYWx0bywNCiAgUHJlZGljaG8gPSBwcmVkX2dhbV9jYXRlZ29yaWENCikNCg0KdGFibGFfcHJlZGljY2lvbl9nYW0NCg0KIyBDYWxjdWxhbW9zIGVsIHBvcmNlbnRhamUgdG90YWwgZGUgYWNpZXJ0b3MuDQpwb3JjZW50YWplX2FjaWVydG9zX2dhbSA8LSBzdW0oZGlhZyh0YWJsYV9wcmVkaWNjaW9uX2dhbSkpIC8gc3VtKHRhYmxhX3ByZWRpY2Npb25fZ2FtKQ0KDQpwb3JjZW50YWplX2FjaWVydG9zX2dhbQ0KDQojIFJldmlzYW1vcyBwb3JjZW50YWplcyBwb3IgZmlsYS4NCiMgRXN0byBub3MgYXl1ZGEgYSB2ZXIgcXXDqSBwb3JjZW50YWplIGRlICJiYWpvIiB5IHF1w6kgcG9yY2VudGFqZSBkZSAiYWx0byINCiMgZnVlcm9uIGNsYXNpZmljYWRvcyBjb3JyZWN0YW1lbnRlLg0KcHJvcC50YWJsZSh0YWJsYV9wcmVkaWNjaW9uX2dhbSwgMSkNCmBgYA0KYGBge3J9DQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyBYVkkuIElOQ0lTTyAzOiDDgVJCT0wgREUgREVDSVNJw5NODQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KDQpsaWJyYXJ5KHJwYXJ0KQ0KDQojIE5PVEFTIFBBUkEgTcONOiANCiMgMS4gbWV0aG9kID0gImNsYXNzIiBzZSB1c2EgcG9ycXVlIGxhIHZhcmlhYmxlIGRlcGVuZGllbnRlIGVzIGNhdGVnw7NyaWNhLg0KIyAyLiBtYXhkZXB0aCBjb250cm9sYSBxdcOpIHRhbiBwcm9mdW5kbyBwdWVkZSBjcmVjZXIgZWwgw6FyYm9sLg0KIyAzLiBtaW5zcGxpdCBpbmRpY2EgZWwgbcOtbmltbyBkZSBvYnNlcnZhY2lvbmVzIG5lY2VzYXJpYXMgcGFyYSBpbnRlbnRhciBkaXZpZGlyIHVuIG5vZG8uDQojIDQuIGNwIGNvbnRyb2xhIGxhIGNvbXBsZWppZGFkIGRlbCDDoXJib2wgeSBheXVkYSBhIGV2aXRhciBxdWUgY3JlemNhIGRlbWFzaWFkby4NCmFyYm9sX2RlY2lzaW9uIDwtIHJwYXJ0KA0KICByaWVzZ29fYWx0byB+IGVkYWQgKyBpbWMgKyBwcmVzaW9uX3Npc3RvbGljYSArIGdsdWNvc2FfYXl1bm8gKw0KICAgIHNleG8gKyBhY3RpdmlkYWRfZmlzaWNhICsgZnVtYWRvciwNCiAgZGF0YSA9IGRhdG9zLA0KICBtZXRob2QgPSAiY2xhc3MiLA0KICBjb250cm9sID0gcnBhcnQuY29udHJvbCgNCiAgICBtYXhkZXB0aCA9IDQsDQogICAgbWluc3BsaXQgPSAxMCwNCiAgICBjcCA9IDAuMDENCiAgKQ0KKQ0KDQojIE1vc3RyYW1vcyBlbCDDoXJib2wgZW4gZm9ybWF0byBkZSB0ZXh0by4NCmFyYm9sX2RlY2lzaW9uDQoNCnBsb3QoDQogIGFyYm9sX2RlY2lzaW9uLA0KICB1bmlmb3JtID0gVFJVRSwNCiAgbWFpbiA9ICLDgXJib2wgZGUgZGVjaXNpw7NuIHBhcmEgcmllc2dvIGNhcmRpb3Zhc2N1bGFyIiwNCiAgbWFyZ2luID0gMC4xDQopDQoNCnRleHQoDQogIGFyYm9sX2RlY2lzaW9uLA0KICB1c2UubiA9IFRSVUUsDQogIGNleCA9IDAuNywNCiAgcHJldHR5ID0gMA0KKQ0KYGBgDQpgYGB7cn0NCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIFhWSUkuIFBSRURJQ0NJw5NOIFkgRVZBTFVBQ0nDk04gREVMIMOBUkJPTA0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCg0KcHJlZF9hcmJvbCA8LSBwcmVkaWN0KA0KICBhcmJvbF9kZWNpc2lvbiwNCiAgdHlwZSA9ICJjbGFzcyINCikNCg0KIyBDcmVhbW9zIHVuYSB0YWJsYSBwYXJhIGNvbXBhcmFyOiANCiMgZmlsYXMgc29uIGxhcyBjYXRlZ29yw61hcyByZWFsZXMNCiMgY29sdW1uYXMgbGFzIGNhdGVnb3LDrWFzIHByZWRpY2hhcy4NCnRhYmxhX2FyYm9sIDwtIHRhYmxlKA0KICBSZWFsID0gZGF0b3Mkcmllc2dvX2FsdG8sDQogIFByZWRpY2hvID0gcHJlZF9hcmJvbA0KKQ0KDQojIGEuIA0KdGFibGFfYXJib2wNCg0KIyBQYXJhIHZlciBlbCBwb3JjZW50YWplIHRvdGFsIGRlIGFjaWVydG9zLg0KcG9yY2VudGFqZV9hY2llcnRvc19hcmJvbCA8LSBzdW0oZGlhZyh0YWJsYV9hcmJvbCkpIC8gc3VtKHRhYmxhX2FyYm9sKQ0KDQpwb3JjZW50YWplX2FjaWVydG9zX2FyYm9sDQoNCiMgUGFyYSB2ZXIgbG9zIGFjaWVydG9zIGluZGl2aWR1YWxlcyBlbiBwb3JjZW50YWplcy4gDQpwcm9wLnRhYmxlKHRhYmxhX2FyYm9sLCAxKQ0KYGBgDQpgYGB7cn0NCiMgYi4gUGFyYSB2ZXIgcXXDqSB2YXJpYWJsZXMgZnVlcm9uIG3DoXMgaW1wb3J0YW50ZXMgcGFyYSBjb25zdHJ1aXIgbGFzIGRpdmlzaW9uZXMgZGVsIMOhcmJvbC4NCmFyYm9sX2RlY2lzaW9uJHZhcmlhYmxlLmltcG9ydGFuY2UNCmBgYA0KDQo=