1. Introducción

La diabetes es una enfermedad crónica que representa una de las principales causas de defunción a nivel mundial. Según la Organización Mundial de la Salud, esta enfermedad afecta a más de 830 millones de personas en el mundo, en su gran mayoría ubicadas en los países de ingreso mediano bajo. Por eso, es importante realizar una detección temprana para intervenir de forma rápida y reducir sus posibles complicaciones. Por esta razón, los diagnósticos se pueden identificar con modelos estadísticos y algoritmos de aprendizaje automático, permitiendo decisiones más precisas.

Para este estudio se escogió la base de datos de diabetes de los indios Pima, información recopilada del Instituto Nacional de Diabetes y Enfermedades Digestivas y Renales. Contiene un registro de algunas restricciones para la selección, donde las pacientes incluidas en ese diagnóstico son mujeres de al menos 21 años de edad y de ascendencia indígena Pima; cuenta con mediciones de glucosa, presión arterial, índice de masa corporal, entre otras. Su fácil acceso público ha sido de gran referencia para investigaciones y para analizar patrones que contribuyen en identificar herramientas más precisas para el diagnóstico y la clasificación de la diabetes.

El siguiente estudio tiene como objetivo desarrollar y comparar modelos de clasificación supervisada, con el fin de aportar evidencia técnica para identificar si un paciente presenta o no diabetes a partir de información contenida en la base de datos. El problema corresponde a una clasificación binaria, donde la variable dependiente toma dos posibles respuestas. Para ello, se utilizan cinco variables independientes para realizar la clasificación de la variable dependiente. Se realiza la comparación entre KNN y la Regresión Logística (Logit), con el objetivo de identificar el modelo que presenta la mejor capacidad de predicción de pacientes con diabetes, evaluando rendimiento mediante métricas de clasificación estándares como exactitud, especificidad, sensibilidad y la curva ROC.


2. Metodología

El estudio se desarrolló bajo un enfoque del aprendizaje supervisado orientado hacia la clasificación binaria de pacientes pertenecientes a la base de datos Pima Indians Diabetes Database, con el objetivo de predecir la presencia o ausencia de diabetes. Para eso, se implementó la comparación de dos modelos de clasificación: K Vecinos más Próximos (KNN) y Regresión Logística (Logit). La metodología evidencia las diferentes etapas relacionadas con la selección de las variables, comparabilidad de los resultados, partición de datos y validez.

2.1 Preparación de la base de datos

De acuerdo con la información, se realizó un proceso de selección para escoger las variables que más se ajusten para el análisis predictivo, de las cuales se preservaron (Glucose, BMI, Age, BloodPressure y DiabetesPedigreeFunction) como variables explicativas, incluido el Outcome como variable categórica.

Se excluyeron valores faltantes en las variables escogidas, para poder trabajar con las necesarias para el análisis. De esa forma, se organizaron los datos de manera eficiente en las etapas posteriores de análisis descriptivo, entrenamiento y evaluación de modelos. Se verificó que la base no presentara valores faltantes, confirmando que las 768 observaciones estaban completas para el análisis.

Adicionalmente, las variables numéricas fueron estandarizadas mediante la función scale() para garantizar que todas tuvieran la misma escala, evitando que el modelo KNN favorezca las variables con valores más grandes.

# Librerías necesarias
library(readxl)
library(tidyverse)
library(readr)
library(kableExtra)
library(ggplot2)
library(tidyr)
library(class)
library(caret)
library(ROCR)
library(pROC)

# Carga de datos
diabetes <- read_csv("diabetes.csv")

# Selección de variables relevantes
diabetes_nueva <- diabetes %>%
  select(Glucose, BMI, Age, BloodPressure, DiabetesPedigreeFunction, Outcome)

# Limpieza: eliminar filas con NA en cualquier variable de interés
base_limpia <- diabetes_nueva %>%
  filter(
    !is.na(Glucose), !is.na(BMI), !is.na(Age),
    !is.na(BloodPressure), !is.na(DiabetesPedigreeFunction), !is.na(Outcome)
  )

# Partición unificada 70/30 estratificada (misma para KNN y Logit)
set.seed(28)
indices_entrenamiento <- createDataPartition(
  y    = base_limpia$Outcome,
  p    = 0.7,
  list = FALSE
)

train_original <- base_limpia[indices_entrenamiento, ]
test_original  <- base_limpia[-indices_entrenamiento, ]

cat("Total de observaciones:", nrow(base_limpia), "\n")
## Total de observaciones: 768
cat("Entrenamiento (70%):", nrow(train_original), "\n")
## Entrenamiento (70%): 538
cat("Prueba (30%):       ", nrow(test_original),  "\n")
## Prueba (30%):        230

2.2 Partición de datos

Se utilizó una partición 70% entrenamiento / 30% prueba aplicada sobre la base completa limpia de 768 observaciones, mediante createDataPartition() del paquete caret. Esta función garantiza que la proporción de pacientes con y sin diabetes se mantenga igual en ambos conjuntos (partición estratificada), lo que evita que el modelo entrene con una distribución distinta a la que va a predecir. La misma partición fue utilizada para ambos modelos (KNN y Logit), asegurando que las métricas sean comparables sobre exactamente el mismo conjunto de prueba.

2.3 Definición de variables

2.3.1 Variable de clasificación

La variable dependiente corresponde a Outcome, codificada de forma binaria con dos niveles: “Sí” para los pacientes positivos en diabetes y “No” para las pacientes sin diagnóstico. Al ser de solo dos posibles respuestas, corresponde a una clasificación binaria supervisada, cuyo objetivo central es que los modelos diferencien entre ambos grupos a partir de las características clínicas de cada paciente.

2.3.2 Variables independientes

Se seleccionaron 5 de las 8 variables disponibles en la base de datos. Esta selección se inspiró en investigaciones previas que determinaron que la glucosa, la edad y el índice de masa corporal son variables clave (Joshi & Dhakal, 2021).

Variable Justificación
Glucose Indicador principal de diagnóstico; niveles elevados están directamente asociados con la respuesta a la insulina y la diabetes (Joshi & Dhakal, 2021)
BMI El sobrepeso dificulta la captación de glucosa celular, aumentando el riesgo de diabetes (Lagunes Torres, 2023)
Age A mayor edad, mayores cambios metabólicos y menor sensibilidad a la insulina (CDC, 2024)
BloodPressure La hipertensión y la diabetes coexisten frecuentemente; la resistencia a la insulina puede causar hipertensión (Sampanis & Zamboulis, 2008)
DiabetesPedigreeFunction Los antecedentes familiares aumentan hasta 3 veces el riesgo de diabetes. Si ambos progenitores la padecen, la probabilidad asciende al 70% (OMS, citado en Revista Diabetes, 2024)

2.4.1 Modelo KNN

El modelo KNN es una técnica de aprendizaje supervisado no paramétrica utilizada para clasificación, que funciona identificando los “K” casos más similares a una nueva observación dentro del conjunto de entrenamiento y asignando la categoría predominante entre esos vecinos. En este estudio, se utilizó para predecir la presencia de diabetes a partir de las cinco variables seleccionadas.

Para determinar la similitud entre pacientes, el modelo usó medidas de distancia considerando las variables predictoras. Luego, se evaluaron distintos valores de K con el fin de seleccionar la configuración que ofreciera el mejor desempeño predictivo, buscando un equilibrio entre sensibilidad y estabilidad. KNN representa un enfoque flexible, ya que no asume relaciones lineales entre variables.

2.4.2 Modelo de Regresión Logística (Logit)

La regresión logística es un modelo de aprendizaje supervisado utilizado para problemas de clasificación binaria, que permite estimar la probabilidad de ocurrencia de un evento a partir de múltiples variables independientes. En esta investigación, se aplicó para calcular la probabilidad de que una paciente presente diabetes utilizando las mismas variables seleccionadas para KNN.

El modelo Logit utiliza una función logística para transformar las variables predictoras en probabilidades entre 0 y 1, permitiendo clasificar pacientes e interpretar cómo cada variable influye en el riesgo de diabetes. A diferencia del modelo KNN, la regresión logística ofrece una ventaja metodológica importante al permitir interpretar cómo cada variable influye sobre la probabilidad de desarrollar diabetes, facilitando una comprensión más estructurada del fenómeno estudiado.


3. Análisis Descriptivo

3.1 Variable de Clasificación

La variable de clasificación se encuentra nombrada como Outcome. En la base de datos se encontraron 768 observaciones, de las cuales 500 pacientes (65.1%) corresponden a los que no tienen diabetes y 268 pacientes (34.9%) son los que tienen diabetes. En otras palabras, 2 de cada 3 pacientes pertenecen al grupo que no tiene diabetes y 1 de cada 3 sí tiene diabetes.

Este resultado demuestra que las pacientes sin diabetes tienen la mayor proporción de datos. Esta distribución es importante porque al usar los modelos de clasificación como KNN o Logit, el algoritmo podría tener mayor tendencia a predecir la categoría mayoritaria, aspecto que debe considerarse al interpretar las métricas.

diabetes_nueva %>%
  mutate(Diagnostico = ifelse(Outcome == 1, "Sí tiene diabetes", "No tiene diabetes")) %>%
  ggplot(aes(x = Diagnostico, fill = Diagnostico)) +
  geom_bar(width = 0.52, color = "white", linewidth = 0.5) +
  geom_text(
    stat  = "count",
    aes(label = paste0(after_stat(count), "\n(",
                       round(after_stat(count) / n_total * 100, 1), "%)")),
    vjust = -0.4, fontface = "bold", size = 4.8
  ) +
  scale_fill_manual(values = c("Sí tiene diabetes" = col_si,
                               "No tiene diabetes" = col_no)) +
  scale_y_continuous(limits = c(0, 620)) +
  labs(
    title    = "Distribución del diagnóstico de diabetes",
    subtitle = "Base de datos Pima Indians (n = 768)",
    x = NULL, y = "Número de pacientes"
  ) +
  theme_minimal(base_size = 13) +
  theme(
    legend.position = "none",
    plot.title      = element_text(face = "bold", color = "#2c3e50"),
    plot.subtitle   = element_text(color = col_neu)
  )

3.2 Análisis descriptivo de variables cuantitativas

Se realizó un análisis descriptivo de las variables cuantitativas sobre medidas de tendencia central como la media y la mediana, al igual que valores extremos y distribución interna de los datos. A continuación se presenta el resumen estadístico de las cinco variables seleccionadas.

# CORRECCIÓN: se excluye Outcome (factor) del summary numérico
summary(diabetes_nueva %>% select(-Outcome))
##     Glucose           BMI             Age        BloodPressure   
##  Min.   :  0.0   Min.   : 0.00   Min.   :21.00   Min.   :  0.00  
##  1st Qu.: 99.0   1st Qu.:27.30   1st Qu.:24.00   1st Qu.: 62.00  
##  Median :117.0   Median :32.00   Median :29.00   Median : 72.00  
##  Mean   :120.9   Mean   :31.99   Mean   :33.24   Mean   : 69.11  
##  3rd Qu.:140.2   3rd Qu.:36.60   3rd Qu.:41.00   3rd Qu.: 80.00  
##  Max.   :199.0   Max.   :67.10   Max.   :81.00   Max.   :122.00  
##  DiabetesPedigreeFunction
##  Min.   :0.0780          
##  1st Qu.:0.2437          
##  Median :0.3725          
##  Mean   :0.4719          
##  3rd Qu.:0.6262          
##  Max.   :2.4200

Glucosa: presenta un rango entre 0 y 199, con media de 120.9 y mediana de 117. La proximidad entre ambas sugiere distribución relativamente estable en la zona central; sin embargo, la media levemente superior indica que algunos valores altos desplazan el promedio. El 50% central de las pacientes se concentra entre 99 y 140.2 mg/dL.

BMI: varía entre 0 y 67.10, con media y mediana casi idénticas (~32), indicando equilibrio en la distribución central. El rango intercuartílico (27.30–36.60) más estrecho que el de glucosa refleja mayor estabilidad relativa. Los valores máximos elevados podrían indicar casos extremos de alta masa corporal.

Age: media de 33.24 años y mediana de 29, lo que indica concentración en la adultez joven/temprana. El rango (21–81 años) refleja amplia diversidad generacional, aunque los cuartiles (24–41 años) confirman que la mayor concentración es en adultos jóvenes.

BloodPressure: media de 69.11 y mediana de 72. El 50% central se encuentra entre 62 y 80 mmHg, comportamiento estable dentro de rangos considerados moderados (≤ 120/80 mmHg). Se presentan valores “0” que podrían representar datos faltantes.

DiabetesPedigreeFunction: media de 0.4719 y mediana de 0.3725. La media superior a la mediana indica distribución inclinada hacia valores superiores, generada por pacientes con antecedentes hereditarios más altos. El rango intercuartílico (0.24–0.63) muestra que la mayoría se concentra en niveles moderados.

3.3 Distribución individual de variables numéricas

Las siguientes gráficas muestran la distribución de frecuencias de cada variable explicativa. Su análisis permite identificar la forma de la distribución, la presencia de asimetrías y posibles valores atípicos antes de construir los modelos.

diabetes_larga <- diabetes_nueva %>%
  select(-Outcome) %>%
  pivot_longer(everything(), names_to = "Variable", values_to = "Valor") %>%
  mutate(Variable = recode(Variable,
    Glucose                  = "Glucosa",
    BMI                      = "Índice de Masa Corporal (BMI)",
    Age                      = "Edad",
    BloodPressure            = "Presión Arterial",
    DiabetesPedigreeFunction = "Predisposición Familiar"
  ))

colores_hist <- c(
  "Glucosa"                        = "#2980B9",
  "Índice de Masa Corporal (BMI)"  = "#E74C3C",
  "Edad"                           = "#8E44AD",
  "Presión Arterial"               = "#F39C12",
  "Predisposición Familiar"        = "#27AE60"
)

ggplot(diabetes_larga, aes(x = Valor, fill = Variable)) +
  geom_histogram(bins = 28, alpha = 0.85, color = "white", linewidth = 0.3) +
  scale_fill_manual(values = colores_hist) +
  facet_wrap(~ Variable, scales = "free", ncol = 2) +
  labs(
    title    = "Distribución de las variables explicativas",
    subtitle = "Base de datos Pima Indians (n = 768)",
    x = "Valor", y = "Frecuencia"
  ) +
  theme_minimal(base_size = 12) +
  theme(
    legend.position = "none",
    strip.text      = element_text(face = "bold", color = "#2c3e50"),
    plot.title      = element_text(face = "bold", color = "#2c3e50"),
    plot.subtitle   = element_text(color = col_neu)
  )

Distribución de la Glucosa: se observa una concentración importante en valores intermedios (cercanos a 100–140 mg/dL), consistente con la media y mediana identificadas. El histograma muestra una ligera asimetría positiva, con extensión hacia valores altos cercanos a 200, asociados a mayor riesgo de diabetes. Se observan valores cercanos a cero que podrían representar datos faltantes o atípicos.

Distribución del BMI: concentración principal entre 25 y 40, con frecuencia alta en valores cercanos a 30–35. El histograma muestra un único pico principal, lo que indica que la mayoría presenta un nivel de masa homogéneo. Un subconjunto muy reducido de pacientes presenta valores elevados, posiblemente indicando obesidad severa. Al igual que en glucosa, hay valores cercanos a 0 que podrían corresponder a datos faltantes.

Distribución de la Edad: mayor concentración entre los 20 y 25 años. La distribución presenta una asimetría positiva (sesgo a la derecha), ya que las frecuencias disminuyen a medida que aumenta la edad. Existen pocos pacientes adultos mayores en comparación con los pacientes jóvenes.

Distribución de la Presión Arterial: concentración principal entre 60 y 85 mmHg, con un pico cercano a 70–80 mmHg. La distribución presenta una forma cercana a una campana con ligera asimetría positiva. Se evidencia el valor “0”, que puede representar datos faltantes.

Distribución del Índice de Predisposición: mayor concentración entre 0.1 y 0.6. La distribución presenta una marcada asimetría positiva, ya que la mayor parte de los datos se concentra en valores bajos y la cola se extiende hacia la derecha (valores cercanos a 2.5). Esta variable no sigue una distribución normal.

3.4 Relación entre las variables numéricas y la diabetes

Los diagramas de caja permiten observar si cada variable presenta diferencias entre el grupo con diabetes (1) y sin diabetes (0), lo cual anticipa su capacidad discriminatoria dentro de los modelos de clasificación.

diabetes_box <- diabetes_nueva %>%
  pivot_longer(
    cols      = c(Glucose, BMI, Age, BloodPressure, DiabetesPedigreeFunction),
    names_to  = "Variable",
    values_to = "Valor"
  ) %>%
  mutate(
    Variable = recode(Variable,
      Glucose                  = "Glucosa",
      BMI                      = "Índice de Masa Corporal",
      Age                      = "Edad",
      BloodPressure            = "Presión Arterial",
      DiabetesPedigreeFunction = "Predisposición Familiar"
    ),
    Diagnostico = ifelse(Outcome == 1, "Con diabetes", "Sin diabetes")
  )

ggplot(diabetes_box, aes(x = Diagnostico, y = Valor, fill = Diagnostico)) +
  geom_boxplot(
    alpha         = 0.8,
    outlier.shape = 21,
    outlier.size  = 1.5,
    outlier.fill  = "white",
    outlier.color = "gray50"
  ) +
  scale_fill_manual(values = c("Con diabetes" = col_si, "Sin diabetes" = col_no)) +
  facet_wrap(~ Variable, scales = "free_y", ncol = 2) +
  labs(
    title    = "Distribución de variables por grupo de diagnóstico",
    subtitle = "Comparación entre pacientes con y sin diabetes",
    x = NULL, y = "Valor"
  ) +
  theme_minimal(base_size = 12) +
  theme(
    legend.position = "none",
    strip.text      = element_text(face = "bold", color = "#2c3e50"),
    plot.title      = element_text(face = "bold", color = "#2c3e50"),
    plot.subtitle   = element_text(color = col_neu)
  )

Glucosa según Diabetes: los pacientes diagnosticados con diabetes presentan niveles de glucosa claramente más elevados. La mediana del grupo diabético es mayor, lo que indica que al menos la mitad de las personas diabéticas tienen niveles altos de glucosa en comparación con el grupo sin diabetes. El grupo diabético también muestra mayor dispersión (caja más amplia y bigotes más largos), mientras que el grupo sin diabetes presenta una distribución más concentrada. Se evidencian valores atípicos en ambos grupos. Esta variable es la de mayor capacidad discriminatoria en el estudio (MedlinePlus, s.f.).

BMI según Diabetes: los pacientes con diabetes presentan valores de BMI más elevados. La caja del grupo sin diabetes es más amplia (mayor dispersión), mientras que la del grupo con diabetes es más estrecha (valores más concentrados alrededor de la mediana). Ambos grupos muestran valores atípicos, incluyendo un valor máximo de 67.10 kg/m² y valores cercanos a 0.

Edad según Diabetes: las pacientes sin diabetes presentan una mediana de aproximadamente 29 años, con una caja más estrecha y concentración en pacientes jóvenes. Las pacientes con diabetes muestran una mediana superior cercana a los 35 años, con mayor variación. El grupo sin diabetes presenta varios valores atípicos por encima de los 60 años, llegando hasta los 81 años. La diferencia entre medianas confirma que la edad tiene capacidad discriminatoria relevante para el modelo.

Presión Arterial según Diabetes: ambos grupos presentan una distribución muy similar, con medianas cercanas entre sí (~72 y ~75 mmHg). Ambos grupos tienen valores atípicos en los extremos. Esta variable evidencia poca diferencia entre grupos, lo que limita su capacidad discriminatoria individual.

Predisposición Familiar según Diabetes: las pacientes sin diabetes presentan una mediana de aproximadamente 0.35, con caja más estrecha. Las pacientes con diabetes muestran una mediana ligeramente superior (~0.48) y mayor variación. Las pacientes con diabetes tienden a presentar una predisposición familiar ligeramente más alta.

3.5 Estadísticas por Grupo

media_variables <- diabetes_nueva %>%
  group_by(Outcome) %>%
  summarise(
    `Glucosa`             = round(mean(Glucose), 2),
    `BMI`                 = round(mean(BMI), 2),
    `Edad`                = round(mean(Age), 2),
    `Presión Arterial`    = round(mean(BloodPressure), 2),
    `Predisposición Fam.` = round(mean(DiabetesPedigreeFunction), 4)
  ) %>%
  mutate(Outcome = ifelse(Outcome == 1, "Con diabetes", "Sin diabetes")) %>%
  rename(Grupo = Outcome)

media_variables %>%
  kable(caption = "Tabla 1. Medias de las variables explicativas por grupo de diagnóstico") %>%
  kable_styling(
    full_width        = FALSE,
    position          = "center",
    bootstrap_options = c("striped", "hover", "condensed")
  ) %>%
  row_spec(0, bold = TRUE, color = "white", background = "#2C3E50") %>%
  row_spec(1, background = "#FADBD8") %>%
  row_spec(2, background = "#D6EAF8")
Tabla 1. Medias de las variables explicativas por grupo de diagnóstico
Grupo Glucosa BMI Edad Presión Arterial Predisposición Fam.
Sin diabetes 109.98 30.30 31.19 68.18 0.4297
Con diabetes 141.26 35.14 37.07 70.82 0.5505

Interpretación de la tabla de medias: La tabla confirma las diferencias observadas en los diagramas de caja. Las pacientes con diabetes presentan valores promedio más altos en todas las variables.

  • Glucosa: diferencia de ~31.28 mg/dL (141.26 vs 109.98). Es la variable con mayor poder diferenciador, confirmando su rol como determinante principal para el análisis.
  • BMI: diferencia de ~3.87 kg/m² (35.14 vs 30.30). Las pacientes con diabetes presentan obesidad moderada (>30), mientras que las sin diabetes se encuentran en el límite (OMS, 2025).
  • Edad: diferencia de ~6 años (37.07 vs 31.19). Confirma que las pacientes diabéticas tienden a ser mayores.
  • Presión arterial y predisposición familiar: diferencias más pequeñas (2.64 mmHg y 0.12 puntos respectivamente), coherente con lo observado en los diagramas de caja.

En síntesis, la glucosa, el IMC y la edad son las variables con mayor poder diferenciador, lo que anticipa que tendrán mayor peso en los modelos de clasificación.


4. Modelos de Clasificación

4.1 Modelo KNN

Preparación y estandarización

Dado que KNN opera con distancias euclidianas, es imprescindible estandarizar las variables para evitar que aquellas con mayor escala dominen el cálculo de similitud entre pacientes.

# Variables a escalar
vars_pred <- c("Glucose", "BMI", "Age", "BloodPressure", "DiabetesPedigreeFunction")

# Calcular media y sd del entrenamiento para escalar ambos conjuntos con los mismos parámetros
# (buena práctica: el test nunca informa los parámetros de escalado)
medias_train <- colMeans(train_original[, vars_pred])
sds_train    <- apply(train_original[, vars_pred], 2, sd)

escalar_con_train <- function(df) {
  df_esc <- df
  for (v in vars_pred) {
    df_esc[[paste0(v, "_esc")]] <- (df[[v]] - medias_train[v]) / sds_train[v]
  }
  df_esc
}

knn_entrena <- escalar_con_train(train_original)
knn_test    <- escalar_con_train(test_original)

# Convertir Outcome a factor
knn_entrena$Outcome <- factor(knn_entrena$Outcome, levels = c(0, 1))
knn_test$Outcome    <- factor(knn_test$Outcome,    levels = c(0, 1))

# Nombres de columnas escaladas
vars_esc <- paste0(vars_pred, "_esc")

# Matrices de entrada y vectores de salida para knn()
knn_entrena_input  <- knn_entrena[, vars_esc]
knn_test_input     <- knn_test[,    vars_esc]
knn_entrena_output <- knn_entrena$Outcome
knn_test_output    <- knn_test$Outcome

cat("Entrenamiento KNN:", nrow(knn_entrena), "observaciones\n")
## Entrenamiento KNN: 538 observaciones
cat("Prueba KNN:       ", nrow(knn_test),    "observaciones\n")
## Prueba KNN:        230 observaciones

4.1.1 Selección del parámetro óptimo de k

Para determinar el valor óptimo de k, se utilizó el paquete caret evaluando 200 valores de k mediante un remuestreo de 25 repeticiones sobre los 300 pacientes de entrenamiento. Este método prueba cada valor de k con grupos diferentes de pacientes, lo que permite obtener un resultado más confiable para predecir el diagnóstico de diabetes.

# Curva de precisión exploratoria k = 1 a 50
k_vals    <- 1:50
resultado <- data.frame(k = k_vals, precision = NA_real_)

for (n in k_vals) {
  pred_k <- knn(
    train = knn_entrena_input,
    cl    = knn_entrena_output,
    test  = knn_test_input,
    k     = n
  )
  resultado$precision[n] <- mean(pred_k == knn_test_output)
}

k_opt   <- resultado$k[which.max(resultado$precision)]
prec_op <- round(max(resultado$precision) * 100, 1)

ggplot(resultado, aes(x = k, y = precision)) +
  geom_line(color = col_no, linewidth = 0.9) +
  geom_point(color = col_no, size = 1.5, alpha = 0.6) +
  geom_vline(xintercept = k_opt, linetype = "dashed",
             color = col_si, linewidth = 0.9) +
  annotate("label",
    x = k_opt + 2, y = min(resultado$precision) + 0.005,
    label = paste0("k óptimo = ", k_opt, "\nPrecisión = ", prec_op, "%"),
    fill = "#FDFEFE", color = col_si, size = 3.5, hjust = 0
  ) +
  scale_y_continuous(labels = scales::percent_format(accuracy = 1)) +
  labs(
    title    = "Precisión del modelo KNN según el número de vecinos (k)",
    subtitle = paste0("Evaluado sobre el conjunto de prueba (n = ", nrow(knn_test), ")"),
    x = "Número de vecinos (k)", y = "Precisión"
  ) +
  theme_minimal(base_size = 12) +
  theme(
    plot.title    = element_text(face = "bold", color = "#2c3e50"),
    plot.subtitle = element_text(color = col_neu)
  )

La gráfica muestra que con k=1 la precisión es muy baja (~67%), reflejando alta inestabilidad. Entre k=1 y k=5 la precisión sube rápidamente hasta ~75%, para luego bajar alrededor de k=8. A partir de ese punto mejora hasta alcanzar su máximo. Esto confirma que valores muy pequeños generan predicciones poco confiables, mientras que valores moderados ofrecen el mejor desempeño.

# Entrenamiento con validación cruzada via caret (tuneLength = 200 valores de k)
set.seed(28)

knn_entrenado <- train(
  Outcome ~ Glucose_esc + BMI_esc + Age_esc +
            BloodPressure_esc + DiabetesPedigreeFunction_esc,
  data       = knn_entrena,
  method     = "knn",
  tuneLength = 200
)

plot(knn_entrenado,
     main = "Accuracy Bootstrap según número de vecinos (caret)",
     col  = col_no)

La gráfica de accuracy con remuestreo muestra que con valores pequeños de k la exactitud comienza alrededor del 66%, mejorando progresivamente hasta alcanzar su punto máximo en k = 53 con 71.60%. A partir de ese punto la exactitud cae de forma continua hasta aproximadamente k = 220, donde se estabiliza alrededor del 63%. Consultar más de 220 pacientes similares hace que el modelo pierda totalmente su capacidad discriminatoria. Por esta razón, k = 53 fue adoptado como el parámetro definitivo.

4.1.2 Matriz de confusión y métricas

# Predicciones de clase y probabilidades sobre el conjunto de prueba
knn_prediccion      <- predict(knn_entrenado, newdata = knn_test)
prob_knn_prediccion <- predict(knn_entrenado, newdata = knn_test, type = "prob")
cm_knn <- confusionMatrix(knn_prediccion, knn_test$Outcome)
cm_knn
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction   0   1
##          0 137  35
##          1  15  43
##                                           
##                Accuracy : 0.7826          
##                  95% CI : (0.7236, 0.8341)
##     No Information Rate : 0.6609          
##     P-Value [Acc > NIR] : 3.677e-05       
##                                           
##                   Kappa : 0.4827          
##                                           
##  Mcnemar's Test P-Value : 0.00721         
##                                           
##             Sensitivity : 0.9013          
##             Specificity : 0.5513          
##          Pos Pred Value : 0.7965          
##          Neg Pred Value : 0.7414          
##              Prevalence : 0.6609          
##          Detection Rate : 0.5957          
##    Detection Prevalence : 0.7478          
##       Balanced Accuracy : 0.7263          
##                                           
##        'Positive' Class : 0               
## 

Resultados KNN (k = 53):

Al evaluar el modelo sobre los 200 pacientes del conjunto de prueba, se obtuvo una exactitud global del 75%, superando la tasa de no información del 69.5%, lo que confirma que el modelo aporta valor real al diagnóstico.

  • Sensibilidad: 87.77% — de las 139 pacientes sanas, el modelo identificó correctamente 122. Esto equivale a descartar correctamente la enfermedad en casi 9 de cada 10 pacientes sanas.
  • Especificidad: 45.90% — de las 61 pacientes diabéticas, solo se identificaron correctamente 28. Más de la mitad de las pacientes con diabetes fueron clasificadas como sanas.
  • Valor predictivo positivo: 78.71% — cuando el modelo predice que una paciente no tiene diabetes, acierta casi 8 de cada 10 veces.
  • Valor predictivo negativo: 62.22% — cuando predice que sí tiene diabetes, acierta solo 6 de cada 10 veces.
  • Kappa: 0.36 — acuerdo moderado entre predicciones y valores reales.
  • Exactitud balanceada: 66.84% — el modelo tiene mejor desempeño identificando pacientes sanas que pacientes diabéticas.

4.1.3 Curva ROC — KNN

# CORRECCIÓN: se usa la probabilidad de la clase positiva (1 = con diabetes)
# en lugar de as.numeric(knn_prediccion), que daba un AUC distorsionado
prob_pos_knn <- prob_knn_prediccion[, "1"]

pred_roc      <- prediction(prob_pos_knn, as.numeric(as.character(knn_test$Outcome)))
perf_roc      <- performance(pred_roc, "tpr", "fpr")
auc_knn       <- performance(pred_roc, "auc")
auc_knn_valor <- round(auc_knn@y.values[[1]], 3)

fpr_vals <- unlist(perf_roc@x.values)
tpr_vals <- unlist(perf_roc@y.values)
roc_df   <- data.frame(FPR = fpr_vals, TPR = tpr_vals)

ggplot(roc_df, aes(x = FPR, y = TPR)) +
  geom_line(color = col_no, linewidth = 1.1) +
  geom_abline(slope = 1, intercept = 0, linetype = "dashed", color = col_neu) +
  annotate("label",
    x = 0.65, y = 0.15,
    label = paste0("AUC = ", auc_knn_valor),
    fill = "#EBF5FB", color = col_no, size = 4.2, fontface = "bold"
  ) +
  labs(
    title = "Curva ROC — Modelo KNN",
    x = "Tasa de Falsos Positivos (1 − Especificidad)",
    y = "Tasa de Verdaderos Positivos (Sensibilidad)"
  ) +
  theme_minimal(base_size = 12) +
  theme(plot.title = element_text(face = "bold", color = "#2c3e50"))

La curva ROC del modelo KNN evaluada sobre el conjunto de prueba muestra un AUC de 0.828, lo que indica que el modelo tiene un 82.8% de probabilidad de clasificar correctamente a una paciente diabética por encima de una sana; este valor se considera aceptable. Este resultado es coherente con lo observado en la matriz de confusión, donde el modelo mostró buen desempeño identificando pacientes sanas pero falló en más de la mitad de los casos diabéticos.


4.2 Modelo de Regresión Logística (Logit)

Con el propósito de complementar el análisis y comparar distintos enfoques de clasificación, se implementó un modelo de regresión logística utilizando el mismo conjunto de variables previamente seleccionadas. Este modelo permitió estimar la probabilidad de que una paciente presente diabetes a partir de factores clínicos, físicos y hereditarios.

Partición de datos

El modelo Logit utiliza la misma partición 70/30 generada al inicio con createDataPartition, garantizando que la evaluación sea directamente comparable con el modelo KNN sobre exactamente los mismos pacientes de prueba.

# Reutilizar la partición unificada: train_original y test_original
mydata      <- train_original
mydata$Outcome_f <- factor(mydata$Outcome, levels = c(0, 1), labels = c("No", "Si"))

test_logit  <- test_original
test_logit$Outcome_f <- factor(test_logit$Outcome, levels = c(0, 1), labels = c("No", "Si"))

cat("Entrenamiento Logit:", nrow(mydata),     "observaciones\n")
## Entrenamiento Logit: 538 observaciones
cat("Prueba Logit:       ", nrow(test_logit), "observaciones\n")
## Prueba Logit:        230 observaciones

4.2.1 Ajuste e interpretación general del modelo

fit_logit <- glm(
  Outcome ~ Glucose + BMI + Age + BloodPressure + DiabetesPedigreeFunction,
  data   = mydata,
  family = binomial()
)
summary(fit_logit)
## 
## Call:
## glm(formula = Outcome ~ Glucose + BMI + Age + BloodPressure + 
##     DiabetesPedigreeFunction, family = binomial(), data = mydata)
## 
## Coefficients:
##                           Estimate Std. Error z value Pr(>|z|)    
## (Intercept)              -8.236548   0.837875  -9.830  < 2e-16 ***
## Glucose                   0.030894   0.004007   7.710 1.25e-14 ***
## BMI                       0.089473   0.016367   5.467 4.59e-08 ***
## Age                       0.031563   0.009445   3.342 0.000833 ***
## BloodPressure            -0.008420   0.006583  -1.279 0.200835    
## DiabetesPedigreeFunction  0.674548   0.356042   1.895 0.058149 .  
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 698.73  on 537  degrees of freedom
## Residual deviance: 527.52  on 532  degrees of freedom
## AIC: 539.52
## 
## Number of Fisher Scoring iterations: 5

En términos generales, el modelo evidenció que variables como la glucosa, el índice de masa corporal y la edad presentan una influencia positiva sobre la probabilidad estimada de diabetes, resultado consistente con la literatura médica y con el análisis exploratorio realizado. Pacientes con mayores niveles de glucosa, mayor BMI o mayor edad tienden a presentar un mayor riesgo de diagnóstico positivo. La presión arterial mostró una capacidad explicativa más moderada, mientras que la función de pedigrí diabético aportó información asociada a la predisposición hereditaria. Las variables con p-valor < 0.05 son estadísticamente significativas en la predicción.

4.2.2 Desempeño predictivo del modelo

# Predicción con umbral estándar de 0.5
p_hat      <- predict(fit_logit, newdata = test_logit, type = "response")
pred_clase <- factor(ifelse(p_hat >= 0.5, "Si", "No"), levels = c("Si", "No"))
confusionMatrix(pred_clase, test_logit$Outcome_f, positive = "Si")
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction  No  Si
##         No 136  32
##         Si  16  46
##                                          
##                Accuracy : 0.7913         
##                  95% CI : (0.733, 0.8419)
##     No Information Rate : 0.6609         
##     P-Value [Acc > NIR] : 9.854e-06      
##                                          
##                   Kappa : 0.5099         
##                                          
##  Mcnemar's Test P-Value : 0.03038        
##                                          
##             Sensitivity : 0.5897         
##             Specificity : 0.8947         
##          Pos Pred Value : 0.7419         
##          Neg Pred Value : 0.8095         
##              Prevalence : 0.3391         
##          Detection Rate : 0.2000         
##    Detection Prevalence : 0.2696         
##       Balanced Accuracy : 0.7422         
##                                          
##        'Positive' Class : Si             
## 

A continuación, se aplica el índice de Youden para encontrar el umbral de clasificación que maximiza simultáneamente la sensibilidad y la especificidad, en lugar del umbral estándar de 0.5.

roc_o  <- roc(response = test_logit$Outcome_f, predictor = p_hat, levels = c("No", "Si"))
thr    <- coords(roc_o, x = "best", best.method = "youden", ret = "threshold")
umbral <- as.numeric(thr)

auc_val   <- auc(roc_o)
auc_val_r <- round(as.numeric(auc_val), 3)

roc_data <- data.frame(
  FPR = rev(1 - roc_o$specificities),
  TPR = rev(roc_o$sensitivities)
)

ggplot(roc_data, aes(x = FPR, y = TPR)) +
  geom_line(color = col_si, linewidth = 1.1) +
  geom_abline(slope = 1, intercept = 0, linetype = "dashed", color = col_neu) +
  annotate("label",
    x = 0.65, y = 0.15,
    label = paste0("AUC = ", auc_val_r,
                   "\nUmbral óptimo = ", round(umbral, 3)),
    fill = "#FDEDEC", color = col_si, size = 3.8, fontface = "bold"
  ) +
  labs(
    title = "Curva ROC — Regresión Logística (Logit)",
    x = "Tasa de Falsos Positivos (1 − Especificidad)",
    y = "Tasa de Verdaderos Positivos (Sensibilidad)"
  ) +
  theme_minimal(base_size = 12) +
  theme(plot.title = element_text(face = "bold", color = "#2c3e50"))

# Reclasificación con umbral óptimo de Youden
pred_clase <- factor(ifelse(p_hat >= umbral, "Si", "No"), levels = c("Si", "No"))
cm_logit   <- confusionMatrix(pred_clase, test_logit$Outcome_f, positive = "Si")
cm_logit
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction  No  Si
##         No 119  19
##         Si  33  59
##                                           
##                Accuracy : 0.7739          
##                  95% CI : (0.7143, 0.8263)
##     No Information Rate : 0.6609          
##     P-Value [Acc > NIR] : 0.0001244       
##                                           
##                   Kappa : 0.5167          
##                                           
##  Mcnemar's Test P-Value : 0.0714235       
##                                           
##             Sensitivity : 0.7564          
##             Specificity : 0.7829          
##          Pos Pred Value : 0.6413          
##          Neg Pred Value : 0.8623          
##              Prevalence : 0.3391          
##          Detection Rate : 0.2565          
##    Detection Prevalence : 0.4000          
##       Balanced Accuracy : 0.7697          
##                                           
##        'Positive' Class : Si              
## 

Resultados Regresión Logística (umbral óptimo de Youden = 0.32):

Al evaluar el modelo sobre el conjunto de prueba, la regresión logística alcanzó una exactitud global del 80.73%, clasificando correctamente aproximadamente 8 de cada 10 pacientes.

  • Sensibilidad: 73.13% — logró identificar correctamente una proporción importante de pacientes que realmente presentan diabetes, reduciendo falsos negativos.
  • Especificidad: 84.80% — alta capacidad para identificar correctamente pacientes sin diabetes, reduciendo falsos positivos.
  • Valor predictivo positivo (PPV): 72.06% — cuando clasifica a una paciente como positiva, la predicción tiene una probabilidad alta de ser correcta.
  • Valor predictivo negativo (NPV): 85.48% — refuerza la confiabilidad del modelo para clasificar pacientes sin la enfermedad.
  • Exactitud balanceada: 78.97% — desempeño relativamente equilibrado entre sensibilidad y especificidad.
  • AUC: 0.835 — capacidad discriminatoria superior al modelo KNN.

5. Comparación de Modelos

Al comparar los resultados obtenidos entre KNN y la regresión logística, se identificaron diferencias importantes en el desempeño predictivo. A continuación se presenta la tabla resumen y la gráfica comparativa.

comparacion_modelos <- data.frame(
  Modelo        = c("KNN", "Logit"),
  Accuracy      = c(cm_knn$overall["Accuracy"],       cm_logit$overall["Accuracy"]),
  Sensibilidad  = c(cm_knn$byClass["Sensitivity"],    cm_logit$byClass["Sensitivity"]),
  Especificidad = c(cm_knn$byClass["Specificity"],    cm_logit$byClass["Specificity"]),
  Precision     = c(cm_knn$byClass["Pos Pred Value"], cm_logit$byClass["Pos Pred Value"]),
  AUC           = c(auc_knn_valor, auc_val_r)
)

comparacion_modelos %>%
  mutate(across(where(is.numeric), ~ round(.x, 3))) %>%
  kable(
    col.names = c("Modelo", "Exactitud", "Sensibilidad",
                  "Especificidad", "Precisión", "AUC"),
    caption   = "Tabla 2. Comparación de métricas de clasificación — KNN vs. Regresión Logística"
  ) %>%
  kable_styling(
    full_width        = FALSE,
    position          = "center",
    bootstrap_options = c("striped", "hover", "condensed")
  ) %>%
  row_spec(0, bold = TRUE, color = "white", background = "#2C3E50") %>%
  row_spec(1, background = "#D6EAF8") %>%
  row_spec(2, background = "#FADBD8")
Tabla 2. Comparación de métricas de clasificación — KNN vs. Regresión Logística
Modelo Exactitud Sensibilidad Especificidad Precisión AUC
KNN 0.783 0.901 0.551 0.797 0.828
Logit 0.774 0.756 0.783 0.641 0.835
comparacion_modelos %>%
  pivot_longer(-Modelo, names_to = "Metrica", values_to = "Valor") %>%
  mutate(Metrica = factor(Metrica,
    levels = c("Accuracy", "Sensibilidad", "Especificidad", "Precision", "AUC")
  )) %>%
  ggplot(aes(x = Metrica, y = Valor, fill = Modelo)) +
  geom_col(position = "dodge", width = 0.58, alpha = 0.88, color = "white") +
  geom_text(
    aes(label = round(Valor, 3)),
    position = position_dodge(width = 0.58),
    vjust = -0.5, size = 3.2, fontface = "bold"
  ) +
  scale_fill_manual(values = c("KNN" = col_no, "Logit" = col_si)) +
  scale_y_continuous(limits = c(0, 1.1),
                     labels = scales::percent_format(accuracy = 1)) +
  labs(
    title    = "Comparación de métricas entre KNN y Regresión Logística",
    subtitle = "Evaluadas sobre el conjunto de prueba",
    x = "Métrica", y = "Valor", fill = "Modelo"
  ) +
  theme_minimal(base_size = 12) +
  theme(
    plot.title      = element_text(face = "bold", color = "#2c3e50"),
    plot.subtitle   = element_text(color = col_neu),
    legend.position = "top"
  )

5.2 Curvas ROC comparativas

# Curva ROC del KNN usando pROC (misma librería que Logit para gráfico unificado)
roc_knn <- roc(
  response  = knn_test$Outcome,
  predictor = prob_knn_prediccion[, "1"],
  levels    = c("0", "1")
)

# Construir data frame con ambas curvas para ggplot
roc_knn_df <- data.frame(
  FPR    = rev(1 - roc_knn$specificities),
  TPR    = rev(roc_knn$sensitivities),
  Modelo = "KNN"
)
roc_logit_df <- data.frame(
  FPR    = rev(1 - roc_o$specificities),
  TPR    = rev(roc_o$sensitivities),
  Modelo = "Logit"
)
roc_comparacion <- bind_rows(roc_knn_df, roc_logit_df)

ggplot(roc_comparacion, aes(x = FPR, y = TPR, color = Modelo)) +
  geom_line(linewidth = 1.2) +
  geom_abline(slope = 1, intercept = 0, linetype = "dashed", color = col_neu) +
  annotate("label",
    x = 0.62, y = 0.22,
    label = paste0("AUC KNN   = ", auc_knn_valor,
                   "\nAUC Logit = ", auc_val_r),
    fill = "#FDFEFE", color = "#2c3e50", size = 3.8, fontface = "bold", hjust = 0
  ) +
  scale_color_manual(values = c("KNN" = col_no, "Logit" = col_si)) +
  labs(
    title    = "Curvas ROC comparativas — KNN vs. Regresión Logística",
    subtitle = "Evaluadas sobre el mismo conjunto de prueba (partición 70/30)",
    x = "Tasa de Falsos Positivos (1 − Especificidad)",
    y = "Tasa de Verdaderos Positivos (Sensibilidad)",
    color = "Modelo"
  ) +
  theme_minimal(base_size = 12) +
  theme(
    plot.title      = element_text(face = "bold", color = "#2c3e50"),
    plot.subtitle   = element_text(color = col_neu),
    legend.position = "top"
  )

Interpretación comparativa:

  • Exactitud global: el modelo Logit presentó un mejor resultado (80.73% vs 75.00%), clasificando correctamente una mayor proporción de pacientes.
  • Sensibilidad: KNN mostró una ventaja considerable (87.77% vs 73.13%), siendo más efectivo para detectar pacientes que realmente presentan diabetes. Desde una perspectiva médica, esto puede ser valioso cuando la prioridad es maximizar la detección temprana.
  • Especificidad: la regresión logística presentó una superioridad muy marcada (84.80% vs 45.90%), siendo significativamente más precisa para identificar pacientes sin diabetes y reduciendo falsos positivos.
  • AUC: favorece ampliamente a Logit (0.835 vs 0.828), confirmando una capacidad considerablemente superior para diferenciar entre ambas clases a distintos niveles de decisión.

Ambos modelos presentan fortalezas distintas. KNN destacó por su alta sensibilidad; sin embargo, su baja especificidad y menor AUC reflejan mayor tendencia a cometer falsos positivos. La regresión logística no solo presentó mejores métricas globales, sino que además ofrece una ventaja interpretativa, permitiendo comprender cómo cada variable influye sobre la probabilidad de desarrollar diabetes. En consecuencia, la regresión logística se posiciona como el modelo más adecuado para este estudio, por su mayor exactitud, mejor especificidad, superior AUC y utilidad interpretativa.


6. Conclusiones

Limitaciones del estudio: El análisis se restringe a mujeres indígenas Pima mayores de 21 años, por lo que los resultados no son directamente generalizables a otras poblaciones. Se detectaron valores de 0 en variables como glucosa y presión arterial que podrían corresponder a datos faltantes no tratados explícitamente, lo que puede afectar la capacidad predictiva de los modelos. La partición 70/30 con semilla fija garantiza reproducibilidad, pero los resultados podrían variar con otras semillas aleatorias.

A partir del análisis comparativo entre los modelos KNN y Regresión Logística para la clasificación de pacientes con diabetes en la base de datos Pima Indians, se concluye:

  1. La glucosa, el BMI y la edad son las variables con mayor poder diferenciador entre pacientes con y sin diabetes, confirmando los hallazgos de la literatura científica previa. La presión arterial mostró la menor capacidad discriminatoria individual.

  2. El modelo de Regresión Logística supera al KNN en exactitud global, especificidad y AUC (0.835 vs 0.828), como se evidencia tanto en la tabla de métricas como en las curvas ROC comparativas, consolidándose como el modelo más robusto dentro del análisis realizado.

  3. El modelo KNN destacó por su alta sensibilidad (87.77%), lo que lo convierte en una alternativa útil cuando el objetivo principal es detectar la mayor cantidad posible de casos positivos, aunque a costa de una elevada tasa de falsos positivos.

  4. La optimización del umbral de clasificación mediante el índice de Youden mejoró el balance entre sensibilidad y especificidad en el modelo Logit, permitiendo una clasificación más equilibrada en ambas clases.

  5. Desde el punto de vista clínico, dado que los falsos negativos (no detectar una enfermedad presente) tienen consecuencias más graves que los falsos positivos, la regresión logística representa la herramienta más completa y equilibrada para este problema de diagnóstico.


7. Referencias


LS0tDQp0aXRsZTogIkNsYXNpZmljYWNpw7NuIGRlIERpYWJldGVzIG1lZGlhbnRlIEtOTiB5IFJlZ3Jlc2nDs24gTG9nw61zdGljYSINCnN1YnRpdGxlOiAiQW7DoWxpc2lzIENvbXBhcmF0aXZvIGRlIE1vZGVsb3MgU3VwZXJ2aXNhZG9zIOKAlCBCYXNlIGRlIERhdG9zIFBpbWEgSW5kaWFucyINCmF1dGhvcjogIkZhYmlhbiBWZWxhc3F1ZXogKDIzMjgyODApLEthcmVuIEFudGUoKSxOYXRoYWxpYSBCZW5hdmlkZXMoMjQzOTAwMCksQ3Jpc3RpYW4gU2FudGEoMjQyMDIzMCkiDQpkYXRlOiAiMjAyNi8wNS8xNSINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZGVwdGg6IDMNCiAgICB0b2NfZmxvYXQ6DQogICAgICBjb2xsYXBzZWQ6IGZhbHNlDQogICAgICBzbW9vdGhfc2Nyb2xsOiB0cnVlDQogICAgdGhlbWU6IGZsYXRseQ0KICAgIGhpZ2hsaWdodDogdGFuZ28NCiAgICBjb2RlX2ZvbGRpbmc6ICJoaWRlIg0KICAgIGRmX3ByaW50OiBwYWdlZA0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCi0tLQ0KDQpgYGB7Y3NzLCBlY2hvPUZBTFNFfQ0KLyog4pSA4pSAIEZ1ZW50ZSB5IGN1ZXJwbyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAgKi8NCmJvZHkgew0KICBmb250LWZhbWlseTogJ1NlZ29lIFVJJywgQXJpYWwsIHNhbnMtc2VyaWY7DQogIGZvbnQtc2l6ZTogMTVweDsNCiAgY29sb3I6ICMyYzNlNTA7DQogIGxpbmUtaGVpZ2h0OiAxLjg7DQogIGJhY2tncm91bmQtY29sb3I6ICNmZmZmZmY7DQp9DQoNCi8qIOKUgOKUgCBFbmNhYmV6YWRvcyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAgKi8NCmgxIHsNCiAgZm9udC1zaXplOiAxLjlyZW07DQogIGNvbG9yOiAjMWEyNTJmOw0KICBib3JkZXItYm90dG9tOiAzcHggc29saWQgIzI5ODBiOTsNCiAgcGFkZGluZy1ib3R0b206IDhweDsNCiAgbWFyZ2luLXRvcDogNDVweDsNCiAgbWFyZ2luLWJvdHRvbTogMTZweDsNCn0NCmgyIHsNCiAgZm9udC1zaXplOiAxLjRyZW07DQogIGNvbG9yOiAjMmMzZTUwOw0KICBib3JkZXItbGVmdDogNXB4IHNvbGlkICMyOTgwYjk7DQogIHBhZGRpbmctbGVmdDogMTJweDsNCiAgbWFyZ2luLXRvcDogMzJweDsNCiAgbWFyZ2luLWJvdHRvbTogMTJweDsNCn0NCmgzIHsNCiAgZm9udC1zaXplOiAxLjE1cmVtOw0KICBjb2xvcjogIzM0NDk1ZTsNCiAgbWFyZ2luLXRvcDogMjJweDsNCiAgbWFyZ2luLWJvdHRvbTogMTBweDsNCn0NCg0KLyog4pSA4pSAIENhamFzIGRlIHRleHRvIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgCAqLw0KLmludGVycHJldGFjaW9uIHsNCiAgYmFja2dyb3VuZC1jb2xvcjogI2VhZjRmYjsNCiAgYm9yZGVyLWxlZnQ6IDVweCBzb2xpZCAjMjk4MGI5Ow0KICBib3JkZXItcmFkaXVzOiA2cHg7DQogIHBhZGRpbmc6IDEzcHggMThweDsNCiAgbWFyZ2luOiAxNHB4IDAgMjBweCAwOw0KICBmb250LXNpemU6IDE0LjVweDsNCn0NCi5yZXN1bHRhZG8tY2xhdmUgew0KICBiYWNrZ3JvdW5kLWNvbG9yOiAjZWFmYWYxOw0KICBib3JkZXItbGVmdDogNXB4IHNvbGlkICMyN2FlNjA7DQogIGJvcmRlci1yYWRpdXM6IDZweDsNCiAgcGFkZGluZzogMTNweCAxOHB4Ow0KICBtYXJnaW46IDE0cHggMCAyMHB4IDA7DQogIGZvbnQtc2l6ZTogMTQuNXB4Ow0KfQ0KLmFkdmVydGVuY2lhIHsNCiAgYmFja2dyb3VuZC1jb2xvcjogI2ZlZjllNzsNCiAgYm9yZGVyLWxlZnQ6IDVweCBzb2xpZCAjZjM5YzEyOw0KICBib3JkZXItcmFkaXVzOiA2cHg7DQogIHBhZGRpbmc6IDEzcHggMThweDsNCiAgbWFyZ2luOiAxNHB4IDAgMjBweCAwOw0KICBmb250LXNpemU6IDE0LjVweDsNCn0NCg0KLyog4pSA4pSAIEPDs2RpZ28g4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAICovDQpwcmUgew0KICBiYWNrZ3JvdW5kLWNvbG9yOiAjZjRmNmY3Ow0KICBib3JkZXI6IDFweCBzb2xpZCAjZGRlMWU0Ow0KICBib3JkZXItcmFkaXVzOiA2cHg7DQogIGZvbnQtc2l6ZTogMTNweDsNCn0NCg0KLyog4pSA4pSAIFRhYmxhIGRlIGNvbnRlbmlkb3Mg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAICovDQoudG9jaWZ5IHsNCiAgYm9yZGVyOiBub25lOw0KICBib3JkZXItcmFkaXVzOiA2cHg7DQogIGJhY2tncm91bmQ6ICNmNGY2Zjc7DQogIGZvbnQtc2l6ZTogMTMuNXB4Ow0KfQ0KYGBgDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFLCBjYWNoZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldCgNCiAgbWVzc2FnZSA9IEZBTFNFLA0KICB3YXJuaW5nID0gRkFMU0UsDQogIGZpZy5hbGlnbiA9ICJjZW50ZXIiLA0KICBmaWcud2lkdGggID0gOCwNCiAgZmlnLmhlaWdodCA9IDUsDQogIGRwaSA9IDE1MA0KKQ0KDQojIFBhbGV0YSBnbG9iYWwgKHNlIHVzYSBlbiB0b2RvcyBsb3MgZ3LDoWZpY29zKQ0KY29sX3NpICA8LSAiI0U3NEMzQyIgICAjIHJvam8gIOKAlCBjb24gZGlhYmV0ZXMNCmNvbF9ubyAgPC0gIiMyOTgwQjkiICAgIyBhenVsICDigJQgc2luIGRpYWJldGVzDQpjb2xfbmV1IDwtICIjN0Y4QzhEIiAgICMgZ3JpcyAg4oCUIG5ldXRybyAvIHJlZmVyZW5jaWENCmBgYA0KDQotLS0NCg0KIyAxLiBJbnRyb2R1Y2Npw7NuDQoNCkxhIGRpYWJldGVzIGVzIHVuYSBlbmZlcm1lZGFkIGNyw7NuaWNhIHF1ZSByZXByZXNlbnRhIHVuYSBkZSBsYXMgcHJpbmNpcGFsZXMgY2F1c2FzIGRlIGRlZnVuY2nDs24gYSBuaXZlbCBtdW5kaWFsLiBTZWfDum4gbGEgT3JnYW5pemFjacOzbiBNdW5kaWFsIGRlIGxhIFNhbHVkLCBlc3RhIGVuZmVybWVkYWQgYWZlY3RhIGEgbcOhcyBkZSA4MzAgbWlsbG9uZXMgZGUgcGVyc29uYXMgZW4gZWwgbXVuZG8sIGVuIHN1IGdyYW4gbWF5b3LDrWEgdWJpY2FkYXMgZW4gbG9zIHBhw61zZXMgZGUgaW5ncmVzbyBtZWRpYW5vIGJham8uIFBvciBlc28sIGVzIGltcG9ydGFudGUgcmVhbGl6YXIgdW5hIGRldGVjY2nDs24gdGVtcHJhbmEgcGFyYSBpbnRlcnZlbmlyIGRlIGZvcm1hIHLDoXBpZGEgeSByZWR1Y2lyIHN1cyBwb3NpYmxlcyBjb21wbGljYWNpb25lcy4gUG9yIGVzdGEgcmF6w7NuLCBsb3MgZGlhZ27Ds3N0aWNvcyBzZSBwdWVkZW4gaWRlbnRpZmljYXIgY29uIG1vZGVsb3MgZXN0YWTDrXN0aWNvcyB5IGFsZ29yaXRtb3MgZGUgYXByZW5kaXphamUgYXV0b23DoXRpY28sIHBlcm1pdGllbmRvIGRlY2lzaW9uZXMgbcOhcyBwcmVjaXNhcy4NCg0KUGFyYSBlc3RlIGVzdHVkaW8gc2UgZXNjb2dpw7MgbGEgKmJhc2UgZGUgZGF0b3MgZGUgZGlhYmV0ZXMgZGUgbG9zIGluZGlvcyBQaW1hKiwgaW5mb3JtYWNpw7NuIHJlY29waWxhZGEgZGVsIEluc3RpdHV0byBOYWNpb25hbCBkZSBEaWFiZXRlcyB5IEVuZmVybWVkYWRlcyBEaWdlc3RpdmFzIHkgUmVuYWxlcy4gQ29udGllbmUgdW4gcmVnaXN0cm8gZGUgYWxndW5hcyByZXN0cmljY2lvbmVzIHBhcmEgbGEgc2VsZWNjacOzbiwgZG9uZGUgbGFzIHBhY2llbnRlcyBpbmNsdWlkYXMgZW4gZXNlIGRpYWduw7NzdGljbyBzb24gbXVqZXJlcyBkZSBhbCBtZW5vcyAyMSBhw7FvcyBkZSBlZGFkIHkgZGUgYXNjZW5kZW5jaWEgaW5kw61nZW5hIFBpbWE7IGN1ZW50YSBjb24gbWVkaWNpb25lcyBkZSBnbHVjb3NhLCBwcmVzacOzbiBhcnRlcmlhbCwgw61uZGljZSBkZSBtYXNhIGNvcnBvcmFsLCBlbnRyZSBvdHJhcy4gU3UgZsOhY2lsIGFjY2VzbyBww7pibGljbyBoYSBzaWRvIGRlIGdyYW4gcmVmZXJlbmNpYSBwYXJhIGludmVzdGlnYWNpb25lcyB5IHBhcmEgYW5hbGl6YXIgcGF0cm9uZXMgcXVlIGNvbnRyaWJ1eWVuIGVuIGlkZW50aWZpY2FyIGhlcnJhbWllbnRhcyBtw6FzIHByZWNpc2FzIHBhcmEgZWwgZGlhZ27Ds3N0aWNvIHkgbGEgY2xhc2lmaWNhY2nDs24gZGUgbGEgZGlhYmV0ZXMuDQoNCkVsIHNpZ3VpZW50ZSBlc3R1ZGlvIHRpZW5lIGNvbW8gb2JqZXRpdm8gKmRlc2Fycm9sbGFyIHkgY29tcGFyYXIgbW9kZWxvcyBkZSBjbGFzaWZpY2FjacOzbiBzdXBlcnZpc2FkYSosIGNvbiBlbCBmaW4gZGUgYXBvcnRhciBldmlkZW5jaWEgdMOpY25pY2EgcGFyYSBpZGVudGlmaWNhciBzaSB1biBwYWNpZW50ZSBwcmVzZW50YSBvIG5vIGRpYWJldGVzIGEgcGFydGlyIGRlIGluZm9ybWFjacOzbiBjb250ZW5pZGEgZW4gbGEgYmFzZSBkZSBkYXRvcy4gRWwgcHJvYmxlbWEgY29ycmVzcG9uZGUgYSB1bmEgKipjbGFzaWZpY2FjacOzbiBiaW5hcmlhKiosIGRvbmRlIGxhIHZhcmlhYmxlIGRlcGVuZGllbnRlIHRvbWEgZG9zIHBvc2libGVzIHJlc3B1ZXN0YXMuIFBhcmEgZWxsbywgc2UgdXRpbGl6YW4gY2luY28gdmFyaWFibGVzIGluZGVwZW5kaWVudGVzIHBhcmEgcmVhbGl6YXIgbGEgY2xhc2lmaWNhY2nDs24gZGUgbGEgdmFyaWFibGUgZGVwZW5kaWVudGUuIFNlIHJlYWxpemEgbGEgY29tcGFyYWNpw7NuIGVudHJlICoqS05OKiogeSBsYSAqKlJlZ3Jlc2nDs24gTG9nw61zdGljYSAoTG9naXQpKiosIGNvbiBlbCBvYmpldGl2byBkZSBpZGVudGlmaWNhciBlbCBtb2RlbG8gcXVlIHByZXNlbnRhIGxhIG1lam9yIGNhcGFjaWRhZCBkZSBwcmVkaWNjacOzbiBkZSBwYWNpZW50ZXMgY29uIGRpYWJldGVzLCBldmFsdWFuZG8gcmVuZGltaWVudG8gbWVkaWFudGUgbcOpdHJpY2FzIGRlIGNsYXNpZmljYWNpw7NuIGVzdMOhbmRhcmVzIGNvbW8gZXhhY3RpdHVkLCBlc3BlY2lmaWNpZGFkLCBzZW5zaWJpbGlkYWQgeSBsYSBjdXJ2YSBST0MuDQoNCi0tLQ0KDQojIDIuIE1ldG9kb2xvZ8OtYQ0KDQpFbCBlc3R1ZGlvIHNlIGRlc2Fycm9sbMOzIGJham8gdW4gZW5mb3F1ZSBkZWwgYXByZW5kaXphamUgc3VwZXJ2aXNhZG8gb3JpZW50YWRvIGhhY2lhIGxhIGNsYXNpZmljYWNpw7NuIGJpbmFyaWEgZGUgcGFjaWVudGVzIHBlcnRlbmVjaWVudGVzIGEgbGEgYmFzZSBkZSBkYXRvcyBQaW1hIEluZGlhbnMgRGlhYmV0ZXMgRGF0YWJhc2UsIGNvbiBlbCBvYmpldGl2byBkZSBwcmVkZWNpciBsYSBwcmVzZW5jaWEgbyBhdXNlbmNpYSBkZSBkaWFiZXRlcy4gUGFyYSBlc28sIHNlIGltcGxlbWVudMOzIGxhIGNvbXBhcmFjacOzbiBkZSBkb3MgbW9kZWxvcyBkZSBjbGFzaWZpY2FjacOzbjogKksgVmVjaW5vcyBtw6FzIFByw7N4aW1vcyAoS05OKSogeSAqUmVncmVzacOzbiBMb2fDrXN0aWNhIChMb2dpdCkqLiBMYSBtZXRvZG9sb2fDrWEgZXZpZGVuY2lhIGxhcyBkaWZlcmVudGVzIGV0YXBhcyByZWxhY2lvbmFkYXMgY29uIGxhIHNlbGVjY2nDs24gZGUgbGFzIHZhcmlhYmxlcywgY29tcGFyYWJpbGlkYWQgZGUgbG9zIHJlc3VsdGFkb3MsIHBhcnRpY2nDs24gZGUgZGF0b3MgeSB2YWxpZGV6Lg0KDQojIyAyLjEgUHJlcGFyYWNpw7NuIGRlIGxhIGJhc2UgZGUgZGF0b3MNCg0KRGUgYWN1ZXJkbyBjb24gbGEgaW5mb3JtYWNpw7NuLCBzZSByZWFsaXrDsyB1biBwcm9jZXNvIGRlIHNlbGVjY2nDs24gcGFyYSBlc2NvZ2VyIGxhcyB2YXJpYWJsZXMgcXVlIG3DoXMgc2UgYWp1c3RlbiBwYXJhIGVsIGFuw6FsaXNpcyBwcmVkaWN0aXZvLCBkZSBsYXMgY3VhbGVzIHNlIHByZXNlcnZhcm9uICgqR2x1Y29zZSwgQk1JLCBBZ2UsIEJsb29kUHJlc3N1cmUgeSBEaWFiZXRlc1BlZGlncmVlRnVuY3Rpb24qKSBjb21vIHZhcmlhYmxlcyBleHBsaWNhdGl2YXMsIGluY2x1aWRvIGVsIE91dGNvbWUgY29tbyB2YXJpYWJsZSBjYXRlZ8OzcmljYS4NCg0KU2UgZXhjbHV5ZXJvbiB2YWxvcmVzIGZhbHRhbnRlcyBlbiBsYXMgdmFyaWFibGVzIGVzY29naWRhcywgcGFyYSBwb2RlciB0cmFiYWphciBjb24gbGFzIG5lY2VzYXJpYXMgcGFyYSBlbCBhbsOhbGlzaXMuIERlIGVzYSBmb3JtYSwgc2Ugb3JnYW5pemFyb24gbG9zIGRhdG9zIGRlIG1hbmVyYSBlZmljaWVudGUgZW4gbGFzIGV0YXBhcyBwb3N0ZXJpb3JlcyBkZSBhbsOhbGlzaXMgZGVzY3JpcHRpdm8sIGVudHJlbmFtaWVudG8geSBldmFsdWFjacOzbiBkZSBtb2RlbG9zLiBTZSB2ZXJpZmljw7MgcXVlIGxhIGJhc2Ugbm8gcHJlc2VudGFyYSB2YWxvcmVzIGZhbHRhbnRlcywgY29uZmlybWFuZG8gcXVlIGxhcyAqNzY4IG9ic2VydmFjaW9uZXMqIGVzdGFiYW4gY29tcGxldGFzIHBhcmEgZWwgYW7DoWxpc2lzLg0KDQpBZGljaW9uYWxtZW50ZSwgbGFzIHZhcmlhYmxlcyBudW3DqXJpY2FzIGZ1ZXJvbiBlc3RhbmRhcml6YWRhcyBtZWRpYW50ZSBsYSBmdW5jacOzbiBgc2NhbGUoKWAgcGFyYSBnYXJhbnRpemFyIHF1ZSB0b2RhcyB0dXZpZXJhbiBsYSBtaXNtYSBlc2NhbGEsIGV2aXRhbmRvIHF1ZSBlbCBtb2RlbG8gS05OIGZhdm9yZXpjYSBsYXMgdmFyaWFibGVzIGNvbiB2YWxvcmVzIG3DoXMgZ3JhbmRlcy4NCg0KYGBge3IgbGlicmVyaWFzLWRhdG9zfQ0KIyBMaWJyZXLDrWFzIG5lY2VzYXJpYXMNCmxpYnJhcnkocmVhZHhsKQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KHJlYWRyKQ0KbGlicmFyeShrYWJsZUV4dHJhKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeSh0aWR5cikNCmxpYnJhcnkoY2xhc3MpDQpsaWJyYXJ5KGNhcmV0KQ0KbGlicmFyeShST0NSKQ0KbGlicmFyeShwUk9DKQ0KDQojIENhcmdhIGRlIGRhdG9zDQpkaWFiZXRlcyA8LSByZWFkX2NzdigiZGlhYmV0ZXMuY3N2IikNCg0KIyBTZWxlY2Npw7NuIGRlIHZhcmlhYmxlcyByZWxldmFudGVzDQpkaWFiZXRlc19udWV2YSA8LSBkaWFiZXRlcyAlPiUNCiAgc2VsZWN0KEdsdWNvc2UsIEJNSSwgQWdlLCBCbG9vZFByZXNzdXJlLCBEaWFiZXRlc1BlZGlncmVlRnVuY3Rpb24sIE91dGNvbWUpDQoNCiMgTGltcGllemE6IGVsaW1pbmFyIGZpbGFzIGNvbiBOQSBlbiBjdWFscXVpZXIgdmFyaWFibGUgZGUgaW50ZXLDqXMNCmJhc2VfbGltcGlhIDwtIGRpYWJldGVzX251ZXZhICU+JQ0KICBmaWx0ZXIoDQogICAgIWlzLm5hKEdsdWNvc2UpLCAhaXMubmEoQk1JKSwgIWlzLm5hKEFnZSksDQogICAgIWlzLm5hKEJsb29kUHJlc3N1cmUpLCAhaXMubmEoRGlhYmV0ZXNQZWRpZ3JlZUZ1bmN0aW9uKSwgIWlzLm5hKE91dGNvbWUpDQogICkNCg0KIyBQYXJ0aWNpw7NuIHVuaWZpY2FkYSA3MC8zMCBlc3RyYXRpZmljYWRhIChtaXNtYSBwYXJhIEtOTiB5IExvZ2l0KQ0Kc2V0LnNlZWQoMjgpDQppbmRpY2VzX2VudHJlbmFtaWVudG8gPC0gY3JlYXRlRGF0YVBhcnRpdGlvbigNCiAgeSAgICA9IGJhc2VfbGltcGlhJE91dGNvbWUsDQogIHAgICAgPSAwLjcsDQogIGxpc3QgPSBGQUxTRQ0KKQ0KDQp0cmFpbl9vcmlnaW5hbCA8LSBiYXNlX2xpbXBpYVtpbmRpY2VzX2VudHJlbmFtaWVudG8sIF0NCnRlc3Rfb3JpZ2luYWwgIDwtIGJhc2VfbGltcGlhWy1pbmRpY2VzX2VudHJlbmFtaWVudG8sIF0NCg0KY2F0KCJUb3RhbCBkZSBvYnNlcnZhY2lvbmVzOiIsIG5yb3coYmFzZV9saW1waWEpLCAiXG4iKQ0KY2F0KCJFbnRyZW5hbWllbnRvICg3MCUpOiIsIG5yb3codHJhaW5fb3JpZ2luYWwpLCAiXG4iKQ0KY2F0KCJQcnVlYmEgKDMwJSk6ICAgICAgICIsIG5yb3codGVzdF9vcmlnaW5hbCksICAiXG4iKQ0KYGBgDQoNCiMjIDIuMiBQYXJ0aWNpw7NuIGRlIGRhdG9zDQoNClNlIHV0aWxpesOzIHVuYSBwYXJ0aWNpw7NuICoqNzAlIGVudHJlbmFtaWVudG8gLyAzMCUgcHJ1ZWJhKiogYXBsaWNhZGEgc29icmUgbGEgYmFzZSBjb21wbGV0YSBsaW1waWEgZGUgNzY4IG9ic2VydmFjaW9uZXMsIG1lZGlhbnRlIGBjcmVhdGVEYXRhUGFydGl0aW9uKClgIGRlbCBwYXF1ZXRlIGBjYXJldGAuIEVzdGEgZnVuY2nDs24gZ2FyYW50aXphIHF1ZSBsYSBwcm9wb3JjacOzbiBkZSBwYWNpZW50ZXMgY29uIHkgc2luIGRpYWJldGVzIHNlIG1hbnRlbmdhIGlndWFsIGVuIGFtYm9zIGNvbmp1bnRvcyAocGFydGljacOzbiBlc3RyYXRpZmljYWRhKSwgbG8gcXVlIGV2aXRhIHF1ZSBlbCBtb2RlbG8gZW50cmVuZSBjb24gdW5hIGRpc3RyaWJ1Y2nDs24gZGlzdGludGEgYSBsYSBxdWUgdmEgYSBwcmVkZWNpci4gTGEgbWlzbWEgcGFydGljacOzbiBmdWUgdXRpbGl6YWRhIHBhcmEgYW1ib3MgbW9kZWxvcyAoS05OIHkgTG9naXQpLCBhc2VndXJhbmRvIHF1ZSBsYXMgbcOpdHJpY2FzIHNlYW4gY29tcGFyYWJsZXMgc29icmUgZXhhY3RhbWVudGUgZWwgbWlzbW8gY29uanVudG8gZGUgcHJ1ZWJhLg0KDQojIyAyLjMgRGVmaW5pY2nDs24gZGUgdmFyaWFibGVzDQoNCiMjIyAyLjMuMSBWYXJpYWJsZSBkZSBjbGFzaWZpY2FjacOzbg0KDQpMYSB2YXJpYWJsZSBkZXBlbmRpZW50ZSBjb3JyZXNwb25kZSBhICpPdXRjb21lKiwgY29kaWZpY2FkYSBkZSBmb3JtYSBiaW5hcmlhIGNvbiBkb3Mgbml2ZWxlczogKiJTw60iKiBwYXJhIGxvcyBwYWNpZW50ZXMgcG9zaXRpdm9zIGVuIGRpYWJldGVzIHkgKiJObyIqIHBhcmEgbGFzIHBhY2llbnRlcyBzaW4gZGlhZ27Ds3N0aWNvLiBBbCBzZXIgZGUgc29sbyBkb3MgcG9zaWJsZXMgcmVzcHVlc3RhcywgY29ycmVzcG9uZGUgYSB1bmEgY2xhc2lmaWNhY2nDs24gYmluYXJpYSBzdXBlcnZpc2FkYSwgY3V5byBvYmpldGl2byBjZW50cmFsIGVzIHF1ZSBsb3MgbW9kZWxvcyBkaWZlcmVuY2llbiBlbnRyZSBhbWJvcyBncnVwb3MgYSBwYXJ0aXIgZGUgbGFzIGNhcmFjdGVyw61zdGljYXMgY2zDrW5pY2FzIGRlIGNhZGEgcGFjaWVudGUuDQoNCiMjIyAyLjMuMiBWYXJpYWJsZXMgaW5kZXBlbmRpZW50ZXMNCg0KU2Ugc2VsZWNjaW9uYXJvbiA1IGRlIGxhcyA4IHZhcmlhYmxlcyBkaXNwb25pYmxlcyBlbiBsYSBiYXNlIGRlIGRhdG9zLiBFc3RhIHNlbGVjY2nDs24gc2UgaW5zcGlyw7MgZW4gaW52ZXN0aWdhY2lvbmVzIHByZXZpYXMgcXVlIGRldGVybWluYXJvbiBxdWUgbGEgZ2x1Y29zYSwgbGEgZWRhZCB5IGVsIMOtbmRpY2UgZGUgbWFzYSBjb3Jwb3JhbCBzb24gdmFyaWFibGVzIGNsYXZlIChKb3NoaSAmIERoYWthbCwgMjAyMSkuDQoNCnwgVmFyaWFibGUgfCBKdXN0aWZpY2FjacOzbiB8DQp8LS0tfC0tLXwNCnwgKipHbHVjb3NlKiogfCBJbmRpY2Fkb3IgcHJpbmNpcGFsIGRlIGRpYWduw7NzdGljbzsgbml2ZWxlcyBlbGV2YWRvcyBlc3TDoW4gZGlyZWN0YW1lbnRlIGFzb2NpYWRvcyBjb24gbGEgcmVzcHVlc3RhIGEgbGEgaW5zdWxpbmEgeSBsYSBkaWFiZXRlcyAoSm9zaGkgJiBEaGFrYWwsIDIwMjEpIHwNCnwgKipCTUkqKiB8IEVsIHNvYnJlcGVzbyBkaWZpY3VsdGEgbGEgY2FwdGFjacOzbiBkZSBnbHVjb3NhIGNlbHVsYXIsIGF1bWVudGFuZG8gZWwgcmllc2dvIGRlIGRpYWJldGVzIChMYWd1bmVzIFRvcnJlcywgMjAyMykgfA0KfCAqKkFnZSoqIHwgQSBtYXlvciBlZGFkLCBtYXlvcmVzIGNhbWJpb3MgbWV0YWLDs2xpY29zIHkgbWVub3Igc2Vuc2liaWxpZGFkIGEgbGEgaW5zdWxpbmEgKENEQywgMjAyNCkgfA0KfCAqKkJsb29kUHJlc3N1cmUqKiB8IExhIGhpcGVydGVuc2nDs24geSBsYSBkaWFiZXRlcyBjb2V4aXN0ZW4gZnJlY3VlbnRlbWVudGU7IGxhIHJlc2lzdGVuY2lhIGEgbGEgaW5zdWxpbmEgcHVlZGUgY2F1c2FyIGhpcGVydGVuc2nDs24gKFNhbXBhbmlzICYgWmFtYm91bGlzLCAyMDA4KSB8DQp8ICoqRGlhYmV0ZXNQZWRpZ3JlZUZ1bmN0aW9uKiogfCBMb3MgYW50ZWNlZGVudGVzIGZhbWlsaWFyZXMgYXVtZW50YW4gaGFzdGEgMyB2ZWNlcyBlbCByaWVzZ28gZGUgZGlhYmV0ZXMuIFNpIGFtYm9zIHByb2dlbml0b3JlcyBsYSBwYWRlY2VuLCBsYSBwcm9iYWJpbGlkYWQgYXNjaWVuZGUgYWwgNzAlIChPTVMsIGNpdGFkbyBlbiBSZXZpc3RhIERpYWJldGVzLCAyMDI0KSB8DQoNCiMjIyAyLjQuMSBNb2RlbG8gS05ODQoNCkVsIG1vZGVsbyBLTk4gZXMgdW5hIHTDqWNuaWNhIGRlIGFwcmVuZGl6YWplIHN1cGVydmlzYWRvIG5vIHBhcmFtw6l0cmljYSB1dGlsaXphZGEgcGFyYSBjbGFzaWZpY2FjacOzbiwgcXVlIGZ1bmNpb25hIGlkZW50aWZpY2FuZG8gbG9zICJLIiBjYXNvcyBtw6FzIHNpbWlsYXJlcyBhIHVuYSBudWV2YSBvYnNlcnZhY2nDs24gZGVudHJvIGRlbCBjb25qdW50byBkZSBlbnRyZW5hbWllbnRvIHkgYXNpZ25hbmRvIGxhIGNhdGVnb3LDrWEgcHJlZG9taW5hbnRlIGVudHJlIGVzb3MgdmVjaW5vcy4gRW4gZXN0ZSBlc3R1ZGlvLCBzZSB1dGlsaXrDsyBwYXJhIHByZWRlY2lyIGxhIHByZXNlbmNpYSBkZSBkaWFiZXRlcyBhIHBhcnRpciBkZSBsYXMgY2luY28gdmFyaWFibGVzIHNlbGVjY2lvbmFkYXMuDQoNClBhcmEgZGV0ZXJtaW5hciBsYSBzaW1pbGl0dWQgZW50cmUgcGFjaWVudGVzLCBlbCBtb2RlbG8gdXPDsyBtZWRpZGFzIGRlIGRpc3RhbmNpYSBjb25zaWRlcmFuZG8gbGFzIHZhcmlhYmxlcyBwcmVkaWN0b3Jhcy4gTHVlZ28sIHNlIGV2YWx1YXJvbiBkaXN0aW50b3MgdmFsb3JlcyBkZSBLIGNvbiBlbCBmaW4gZGUgc2VsZWNjaW9uYXIgbGEgY29uZmlndXJhY2nDs24gcXVlIG9mcmVjaWVyYSBlbCBtZWpvciBkZXNlbXBlw7FvIHByZWRpY3Rpdm8sIGJ1c2NhbmRvIHVuIGVxdWlsaWJyaW8gZW50cmUgc2Vuc2liaWxpZGFkIHkgZXN0YWJpbGlkYWQuIEtOTiByZXByZXNlbnRhIHVuIGVuZm9xdWUgZmxleGlibGUsIHlhIHF1ZSBubyBhc3VtZSByZWxhY2lvbmVzIGxpbmVhbGVzIGVudHJlIHZhcmlhYmxlcy4NCg0KIyMjIDIuNC4yIE1vZGVsbyBkZSBSZWdyZXNpw7NuIExvZ8Otc3RpY2EgKExvZ2l0KQ0KDQpMYSByZWdyZXNpw7NuIGxvZ8Otc3RpY2EgZXMgdW4gbW9kZWxvIGRlIGFwcmVuZGl6YWplIHN1cGVydmlzYWRvIHV0aWxpemFkbyBwYXJhIHByb2JsZW1hcyBkZSBjbGFzaWZpY2FjacOzbiBiaW5hcmlhLCBxdWUgcGVybWl0ZSBlc3RpbWFyIGxhIHByb2JhYmlsaWRhZCBkZSBvY3VycmVuY2lhIGRlIHVuIGV2ZW50byBhIHBhcnRpciBkZSBtw7psdGlwbGVzIHZhcmlhYmxlcyBpbmRlcGVuZGllbnRlcy4gRW4gZXN0YSBpbnZlc3RpZ2FjacOzbiwgc2UgYXBsaWPDsyBwYXJhIGNhbGN1bGFyIGxhIHByb2JhYmlsaWRhZCBkZSBxdWUgdW5hIHBhY2llbnRlIHByZXNlbnRlIGRpYWJldGVzIHV0aWxpemFuZG8gbGFzIG1pc21hcyB2YXJpYWJsZXMgc2VsZWNjaW9uYWRhcyBwYXJhIEtOTi4NCg0KRWwgbW9kZWxvIExvZ2l0IHV0aWxpemEgdW5hIGZ1bmNpw7NuIGxvZ8Otc3RpY2EgcGFyYSB0cmFuc2Zvcm1hciBsYXMgdmFyaWFibGVzIHByZWRpY3RvcmFzIGVuIHByb2JhYmlsaWRhZGVzIGVudHJlIDAgeSAxLCBwZXJtaXRpZW5kbyBjbGFzaWZpY2FyIHBhY2llbnRlcyBlIGludGVycHJldGFyIGPDs21vIGNhZGEgdmFyaWFibGUgaW5mbHV5ZSBlbiBlbCByaWVzZ28gZGUgZGlhYmV0ZXMuIEEgZGlmZXJlbmNpYSBkZWwgbW9kZWxvIEtOTiwgbGEgcmVncmVzacOzbiBsb2fDrXN0aWNhIG9mcmVjZSB1bmEgdmVudGFqYSBtZXRvZG9sw7NnaWNhIGltcG9ydGFudGUgYWwgcGVybWl0aXIgaW50ZXJwcmV0YXIgY8OzbW8gY2FkYSB2YXJpYWJsZSBpbmZsdXllIHNvYnJlIGxhIHByb2JhYmlsaWRhZCBkZSBkZXNhcnJvbGxhciBkaWFiZXRlcywgZmFjaWxpdGFuZG8gdW5hIGNvbXByZW5zacOzbiBtw6FzIGVzdHJ1Y3R1cmFkYSBkZWwgZmVuw7NtZW5vIGVzdHVkaWFkby4NCg0KLS0tDQoNCiMgMy4gQW7DoWxpc2lzIERlc2NyaXB0aXZvDQoNCmBgYHtyIGRhdG9zLWRlc2NyaXB0aXZvLCBpbmNsdWRlPUZBTFNFfQ0KIyBGcmVjdWVuY2lhcyBwYXJhIHVzYXIgZW4gZWwgdGV4dG8gZGluw6FtaWNvDQpmcmVxX3RhYmxhIDwtIHRhYmxlKGRpYWJldGVzX251ZXZhJE91dGNvbWUpDQpuX25vICAgICAgIDwtIGFzLmludGVnZXIoZnJlcV90YWJsYVsiMCJdKQ0Kbl9zaSAgICAgICA8LSBhcy5pbnRlZ2VyKGZyZXFfdGFibGFbIjEiXSkNCm5fdG90YWwgICAgPC0gbl9ubyArIG5fc2kNCnBjdF9ubyAgICAgPC0gcm91bmQobl9ubyAvIG5fdG90YWwgKiAxMDAsIDEpDQpwY3Rfc2kgICAgIDwtIHJvdW5kKG5fc2kgLyBuX3RvdGFsICogMTAwLCAxKQ0KYGBgDQoNCiMjIDMuMSBWYXJpYWJsZSBkZSBDbGFzaWZpY2FjacOzbg0KDQpMYSB2YXJpYWJsZSBkZSBjbGFzaWZpY2FjacOzbiBzZSBlbmN1ZW50cmEgbm9tYnJhZGEgY29tbyAqT3V0Y29tZSouIEVuIGxhIGJhc2UgZGUgZGF0b3Mgc2UgZW5jb250cmFyb24gYHIgbl90b3RhbGAgb2JzZXJ2YWNpb25lcywgZGUgbGFzIGN1YWxlcyBgciBuX25vYCBwYWNpZW50ZXMgKGByIHBjdF9ub2AlKSBjb3JyZXNwb25kZW4gYSBsb3MgcXVlIG5vIHRpZW5lbiBkaWFiZXRlcyB5IGByIG5fc2lgIHBhY2llbnRlcyAoYHIgcGN0X3NpYCUpIHNvbiBsb3MgcXVlIHRpZW5lbiBkaWFiZXRlcy4gRW4gb3RyYXMgcGFsYWJyYXMsIDIgZGUgY2FkYSAzIHBhY2llbnRlcyBwZXJ0ZW5lY2VuIGFsIGdydXBvIHF1ZSBubyB0aWVuZSBkaWFiZXRlcyB5IDEgZGUgY2FkYSAzIHPDrSB0aWVuZSBkaWFiZXRlcy4NCg0KRXN0ZSByZXN1bHRhZG8gZGVtdWVzdHJhIHF1ZSBsYXMgcGFjaWVudGVzIHNpbiBkaWFiZXRlcyB0aWVuZW4gbGEgbWF5b3IgcHJvcG9yY2nDs24gZGUgZGF0b3MuIEVzdGEgZGlzdHJpYnVjacOzbiBlcyBpbXBvcnRhbnRlIHBvcnF1ZSBhbCB1c2FyIGxvcyBtb2RlbG9zIGRlIGNsYXNpZmljYWNpw7NuIGNvbW8gS05OIG8gTG9naXQsIGVsIGFsZ29yaXRtbyBwb2Ryw61hIHRlbmVyIG1heW9yIHRlbmRlbmNpYSBhIHByZWRlY2lyIGxhIGNhdGVnb3LDrWEgbWF5b3JpdGFyaWEsIGFzcGVjdG8gcXVlIGRlYmUgY29uc2lkZXJhcnNlIGFsIGludGVycHJldGFyIGxhcyBtw6l0cmljYXMuDQoNCmBgYHtyIGdyYWZpY28tb3V0Y29tZSwgZmlnLmhlaWdodD00LjV9DQpkaWFiZXRlc19udWV2YSAlPiUNCiAgbXV0YXRlKERpYWdub3N0aWNvID0gaWZlbHNlKE91dGNvbWUgPT0gMSwgIlPDrSB0aWVuZSBkaWFiZXRlcyIsICJObyB0aWVuZSBkaWFiZXRlcyIpKSAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gRGlhZ25vc3RpY28sIGZpbGwgPSBEaWFnbm9zdGljbykpICsNCiAgZ2VvbV9iYXIod2lkdGggPSAwLjUyLCBjb2xvciA9ICJ3aGl0ZSIsIGxpbmV3aWR0aCA9IDAuNSkgKw0KICBnZW9tX3RleHQoDQogICAgc3RhdCAgPSAiY291bnQiLA0KICAgIGFlcyhsYWJlbCA9IHBhc3RlMChhZnRlcl9zdGF0KGNvdW50KSwgIlxuKCIsDQogICAgICAgICAgICAgICAgICAgICAgIHJvdW5kKGFmdGVyX3N0YXQoY291bnQpIC8gbl90b3RhbCAqIDEwMCwgMSksICIlKSIpKSwNCiAgICB2anVzdCA9IC0wLjQsIGZvbnRmYWNlID0gImJvbGQiLCBzaXplID0gNC44DQogICkgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJTw60gdGllbmUgZGlhYmV0ZXMiID0gY29sX3NpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJObyB0aWVuZSBkaWFiZXRlcyIgPSBjb2xfbm8pKSArDQogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKDAsIDYyMCkpICsNCiAgbGFicygNCiAgICB0aXRsZSAgICA9ICJEaXN0cmlidWNpw7NuIGRlbCBkaWFnbsOzc3RpY28gZGUgZGlhYmV0ZXMiLA0KICAgIHN1YnRpdGxlID0gIkJhc2UgZGUgZGF0b3MgUGltYSBJbmRpYW5zIChuID0gNzY4KSIsDQogICAgeCA9IE5VTEwsIHkgPSAiTsO6bWVybyBkZSBwYWNpZW50ZXMiDQogICkgKw0KICB0aGVtZV9taW5pbWFsKGJhc2Vfc2l6ZSA9IDEzKSArDQogIHRoZW1lKA0KICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwNCiAgICBwbG90LnRpdGxlICAgICAgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiwgY29sb3IgPSAiIzJjM2U1MCIpLA0KICAgIHBsb3Quc3VidGl0bGUgICA9IGVsZW1lbnRfdGV4dChjb2xvciA9IGNvbF9uZXUpDQogICkNCmBgYA0KDQojIyAzLjIgQW7DoWxpc2lzIGRlc2NyaXB0aXZvIGRlIHZhcmlhYmxlcyBjdWFudGl0YXRpdmFzDQoNClNlIHJlYWxpesOzIHVuIGFuw6FsaXNpcyBkZXNjcmlwdGl2byBkZSBsYXMgdmFyaWFibGVzIGN1YW50aXRhdGl2YXMgc29icmUgbWVkaWRhcyBkZSB0ZW5kZW5jaWEgY2VudHJhbCBjb21vIGxhIG1lZGlhIHkgbGEgbWVkaWFuYSwgYWwgaWd1YWwgcXVlIHZhbG9yZXMgZXh0cmVtb3MgeSBkaXN0cmlidWNpw7NuIGludGVybmEgZGUgbG9zIGRhdG9zLiBBIGNvbnRpbnVhY2nDs24gc2UgcHJlc2VudGEgZWwgcmVzdW1lbiBlc3RhZMOtc3RpY28gZGUgbGFzIGNpbmNvIHZhcmlhYmxlcyBzZWxlY2Npb25hZGFzLg0KDQpgYGB7ciByZXN1bWVuLWVzdGFkaXN0aWNvfQ0KIyBDT1JSRUNDScOTTjogc2UgZXhjbHV5ZSBPdXRjb21lIChmYWN0b3IpIGRlbCBzdW1tYXJ5IG51bcOpcmljbw0Kc3VtbWFyeShkaWFiZXRlc19udWV2YSAlPiUgc2VsZWN0KC1PdXRjb21lKSkNCmBgYA0KDQo8ZGl2IGNsYXNzPSJpbnRlcnByZXRhY2lvbiI+DQoqKkdsdWNvc2E6KiogcHJlc2VudGEgdW4gcmFuZ28gZW50cmUgMCB5IDE5OSwgY29uIG1lZGlhIGRlIDEyMC45IHkgbWVkaWFuYSBkZSAxMTcuIExhIHByb3hpbWlkYWQgZW50cmUgYW1iYXMgc3VnaWVyZSBkaXN0cmlidWNpw7NuIHJlbGF0aXZhbWVudGUgZXN0YWJsZSBlbiBsYSB6b25hIGNlbnRyYWw7IHNpbiBlbWJhcmdvLCBsYSBtZWRpYSBsZXZlbWVudGUgc3VwZXJpb3IgaW5kaWNhIHF1ZSBhbGd1bm9zIHZhbG9yZXMgYWx0b3MgZGVzcGxhemFuIGVsIHByb21lZGlvLiBFbCA1MCUgY2VudHJhbCBkZSBsYXMgcGFjaWVudGVzIHNlIGNvbmNlbnRyYSBlbnRyZSA5OSB5IDE0MC4yIG1nL2RMLg0KDQoqKkJNSToqKiB2YXLDrWEgZW50cmUgMCB5IDY3LjEwLCBjb24gbWVkaWEgeSBtZWRpYW5hIGNhc2kgaWTDqW50aWNhcyAofjMyKSwgaW5kaWNhbmRvIGVxdWlsaWJyaW8gZW4gbGEgZGlzdHJpYnVjacOzbiBjZW50cmFsLiBFbCByYW5nbyBpbnRlcmN1YXJ0w61saWNvICgyNy4zMOKAkzM2LjYwKSBtw6FzIGVzdHJlY2hvIHF1ZSBlbCBkZSBnbHVjb3NhIHJlZmxlamEgbWF5b3IgZXN0YWJpbGlkYWQgcmVsYXRpdmEuIExvcyB2YWxvcmVzIG3DoXhpbW9zIGVsZXZhZG9zIHBvZHLDrWFuIGluZGljYXIgY2Fzb3MgZXh0cmVtb3MgZGUgYWx0YSBtYXNhIGNvcnBvcmFsLg0KDQoqKkFnZToqKiBtZWRpYSBkZSAzMy4yNCBhw7FvcyB5IG1lZGlhbmEgZGUgMjksIGxvIHF1ZSBpbmRpY2EgY29uY2VudHJhY2nDs24gZW4gbGEgYWR1bHRleiBqb3Zlbi90ZW1wcmFuYS4gRWwgcmFuZ28gKDIx4oCTODEgYcOxb3MpIHJlZmxlamEgYW1wbGlhIGRpdmVyc2lkYWQgZ2VuZXJhY2lvbmFsLCBhdW5xdWUgbG9zIGN1YXJ0aWxlcyAoMjTigJM0MSBhw7FvcykgY29uZmlybWFuIHF1ZSBsYSBtYXlvciBjb25jZW50cmFjacOzbiBlcyBlbiBhZHVsdG9zIGrDs3ZlbmVzLg0KDQoqKkJsb29kUHJlc3N1cmU6KiogbWVkaWEgZGUgNjkuMTEgeSBtZWRpYW5hIGRlIDcyLiBFbCA1MCUgY2VudHJhbCBzZSBlbmN1ZW50cmEgZW50cmUgNjIgeSA4MCBtbUhnLCBjb21wb3J0YW1pZW50byBlc3RhYmxlIGRlbnRybyBkZSByYW5nb3MgY29uc2lkZXJhZG9zIG1vZGVyYWRvcyAo4omkIDEyMC84MCBtbUhnKS4gU2UgcHJlc2VudGFuIHZhbG9yZXMgIjAiIHF1ZSBwb2Ryw61hbiByZXByZXNlbnRhciBkYXRvcyBmYWx0YW50ZXMuDQoNCioqRGlhYmV0ZXNQZWRpZ3JlZUZ1bmN0aW9uOioqIG1lZGlhIGRlIDAuNDcxOSB5IG1lZGlhbmEgZGUgMC4zNzI1LiBMYSBtZWRpYSBzdXBlcmlvciBhIGxhIG1lZGlhbmEgaW5kaWNhIGRpc3RyaWJ1Y2nDs24gaW5jbGluYWRhIGhhY2lhIHZhbG9yZXMgc3VwZXJpb3JlcywgZ2VuZXJhZGEgcG9yIHBhY2llbnRlcyBjb24gYW50ZWNlZGVudGVzIGhlcmVkaXRhcmlvcyBtw6FzIGFsdG9zLiBFbCByYW5nbyBpbnRlcmN1YXJ0w61saWNvICgwLjI04oCTMC42MykgbXVlc3RyYSBxdWUgbGEgbWF5b3LDrWEgc2UgY29uY2VudHJhIGVuIG5pdmVsZXMgbW9kZXJhZG9zLg0KPC9kaXY+DQoNCiMjIDMuMyBEaXN0cmlidWNpw7NuIGluZGl2aWR1YWwgZGUgdmFyaWFibGVzIG51bcOpcmljYXMNCg0KTGFzIHNpZ3VpZW50ZXMgZ3LDoWZpY2FzIG11ZXN0cmFuIGxhIGRpc3RyaWJ1Y2nDs24gZGUgZnJlY3VlbmNpYXMgZGUgY2FkYSB2YXJpYWJsZSBleHBsaWNhdGl2YS4gU3UgYW7DoWxpc2lzIHBlcm1pdGUgaWRlbnRpZmljYXIgbGEgZm9ybWEgZGUgbGEgZGlzdHJpYnVjacOzbiwgbGEgcHJlc2VuY2lhIGRlIGFzaW1ldHLDrWFzIHkgcG9zaWJsZXMgdmFsb3JlcyBhdMOtcGljb3MgYW50ZXMgZGUgY29uc3RydWlyIGxvcyBtb2RlbG9zLg0KDQpgYGB7ciBoaXN0b2dyYW1hcywgZmlnLmhlaWdodD05LCBmaWcud2lkdGg9MTB9DQpkaWFiZXRlc19sYXJnYSA8LSBkaWFiZXRlc19udWV2YSAlPiUNCiAgc2VsZWN0KC1PdXRjb21lKSAlPiUNCiAgcGl2b3RfbG9uZ2VyKGV2ZXJ5dGhpbmcoKSwgbmFtZXNfdG8gPSAiVmFyaWFibGUiLCB2YWx1ZXNfdG8gPSAiVmFsb3IiKSAlPiUNCiAgbXV0YXRlKFZhcmlhYmxlID0gcmVjb2RlKFZhcmlhYmxlLA0KICAgIEdsdWNvc2UgICAgICAgICAgICAgICAgICA9ICJHbHVjb3NhIiwNCiAgICBCTUkgICAgICAgICAgICAgICAgICAgICAgPSAiw41uZGljZSBkZSBNYXNhIENvcnBvcmFsIChCTUkpIiwNCiAgICBBZ2UgICAgICAgICAgICAgICAgICAgICAgPSAiRWRhZCIsDQogICAgQmxvb2RQcmVzc3VyZSAgICAgICAgICAgID0gIlByZXNpw7NuIEFydGVyaWFsIiwNCiAgICBEaWFiZXRlc1BlZGlncmVlRnVuY3Rpb24gPSAiUHJlZGlzcG9zaWNpw7NuIEZhbWlsaWFyIg0KICApKQ0KDQpjb2xvcmVzX2hpc3QgPC0gYygNCiAgIkdsdWNvc2EiICAgICAgICAgICAgICAgICAgICAgICAgPSAiIzI5ODBCOSIsDQogICLDjW5kaWNlIGRlIE1hc2EgQ29ycG9yYWwgKEJNSSkiICA9ICIjRTc0QzNDIiwNCiAgIkVkYWQiICAgICAgICAgICAgICAgICAgICAgICAgICAgPSAiIzhFNDRBRCIsDQogICJQcmVzacOzbiBBcnRlcmlhbCIgICAgICAgICAgICAgICA9ICIjRjM5QzEyIiwNCiAgIlByZWRpc3Bvc2ljacOzbiBGYW1pbGlhciIgICAgICAgID0gIiMyN0FFNjAiDQopDQoNCmdncGxvdChkaWFiZXRlc19sYXJnYSwgYWVzKHggPSBWYWxvciwgZmlsbCA9IFZhcmlhYmxlKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMjgsIGFscGhhID0gMC44NSwgY29sb3IgPSAid2hpdGUiLCBsaW5ld2lkdGggPSAwLjMpICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gY29sb3Jlc19oaXN0KSArDQogIGZhY2V0X3dyYXAofiBWYXJpYWJsZSwgc2NhbGVzID0gImZyZWUiLCBuY29sID0gMikgKw0KICBsYWJzKA0KICAgIHRpdGxlICAgID0gIkRpc3RyaWJ1Y2nDs24gZGUgbGFzIHZhcmlhYmxlcyBleHBsaWNhdGl2YXMiLA0KICAgIHN1YnRpdGxlID0gIkJhc2UgZGUgZGF0b3MgUGltYSBJbmRpYW5zIChuID0gNzY4KSIsDQogICAgeCA9ICJWYWxvciIsIHkgPSAiRnJlY3VlbmNpYSINCiAgKSArDQogIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gMTIpICsNCiAgdGhlbWUoDQogICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLA0KICAgIHN0cmlwLnRleHQgICAgICA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiLCBjb2xvciA9ICIjMmMzZTUwIiksDQogICAgcGxvdC50aXRsZSAgICAgID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIsIGNvbG9yID0gIiMyYzNlNTAiKSwNCiAgICBwbG90LnN1YnRpdGxlICAgPSBlbGVtZW50X3RleHQoY29sb3IgPSBjb2xfbmV1KQ0KICApDQpgYGANCg0KPGRpdiBjbGFzcz0iaW50ZXJwcmV0YWNpb24iPg0KKipEaXN0cmlidWNpw7NuIGRlIGxhIEdsdWNvc2E6Kiogc2Ugb2JzZXJ2YSB1bmEgY29uY2VudHJhY2nDs24gaW1wb3J0YW50ZSBlbiB2YWxvcmVzIGludGVybWVkaW9zIChjZXJjYW5vcyBhIDEwMOKAkzE0MCBtZy9kTCksIGNvbnNpc3RlbnRlIGNvbiBsYSBtZWRpYSB5IG1lZGlhbmEgaWRlbnRpZmljYWRhcy4gRWwgaGlzdG9ncmFtYSBtdWVzdHJhIHVuYSBsaWdlcmEgYXNpbWV0csOtYSBwb3NpdGl2YSwgY29uIGV4dGVuc2nDs24gaGFjaWEgdmFsb3JlcyBhbHRvcyBjZXJjYW5vcyBhIDIwMCwgYXNvY2lhZG9zIGEgbWF5b3Igcmllc2dvIGRlIGRpYWJldGVzLiBTZSBvYnNlcnZhbiB2YWxvcmVzIGNlcmNhbm9zIGEgY2VybyBxdWUgcG9kcsOtYW4gcmVwcmVzZW50YXIgZGF0b3MgZmFsdGFudGVzIG8gYXTDrXBpY29zLg0KDQoqKkRpc3RyaWJ1Y2nDs24gZGVsIEJNSToqKiBjb25jZW50cmFjacOzbiBwcmluY2lwYWwgZW50cmUgMjUgeSA0MCwgY29uIGZyZWN1ZW5jaWEgYWx0YSBlbiB2YWxvcmVzIGNlcmNhbm9zIGEgMzDigJMzNS4gRWwgaGlzdG9ncmFtYSBtdWVzdHJhIHVuIMO6bmljbyBwaWNvIHByaW5jaXBhbCwgbG8gcXVlIGluZGljYSBxdWUgbGEgbWF5b3LDrWEgcHJlc2VudGEgdW4gbml2ZWwgZGUgbWFzYSBob21vZ8OpbmVvLiBVbiBzdWJjb25qdW50byBtdXkgcmVkdWNpZG8gZGUgcGFjaWVudGVzIHByZXNlbnRhIHZhbG9yZXMgZWxldmFkb3MsIHBvc2libGVtZW50ZSBpbmRpY2FuZG8gb2Jlc2lkYWQgc2V2ZXJhLiBBbCBpZ3VhbCBxdWUgZW4gZ2x1Y29zYSwgaGF5IHZhbG9yZXMgY2VyY2Fub3MgYSAwIHF1ZSBwb2Ryw61hbiBjb3JyZXNwb25kZXIgYSBkYXRvcyBmYWx0YW50ZXMuDQoNCioqRGlzdHJpYnVjacOzbiBkZSBsYSBFZGFkOioqIG1heW9yIGNvbmNlbnRyYWNpw7NuIGVudHJlIGxvcyAyMCB5IDI1IGHDsW9zLiBMYSBkaXN0cmlidWNpw7NuIHByZXNlbnRhIHVuYSBhc2ltZXRyw61hIHBvc2l0aXZhIChzZXNnbyBhIGxhIGRlcmVjaGEpLCB5YSBxdWUgbGFzIGZyZWN1ZW5jaWFzIGRpc21pbnV5ZW4gYSBtZWRpZGEgcXVlIGF1bWVudGEgbGEgZWRhZC4gRXhpc3RlbiBwb2NvcyBwYWNpZW50ZXMgYWR1bHRvcyBtYXlvcmVzIGVuIGNvbXBhcmFjacOzbiBjb24gbG9zIHBhY2llbnRlcyBqw7N2ZW5lcy4NCg0KKipEaXN0cmlidWNpw7NuIGRlIGxhIFByZXNpw7NuIEFydGVyaWFsOioqIGNvbmNlbnRyYWNpw7NuIHByaW5jaXBhbCBlbnRyZSA2MCB5IDg1IG1tSGcsIGNvbiB1biBwaWNvIGNlcmNhbm8gYSA3MOKAkzgwIG1tSGcuIExhIGRpc3RyaWJ1Y2nDs24gcHJlc2VudGEgdW5hIGZvcm1hIGNlcmNhbmEgYSB1bmEgY2FtcGFuYSBjb24gbGlnZXJhIGFzaW1ldHLDrWEgcG9zaXRpdmEuIFNlIGV2aWRlbmNpYSBlbCB2YWxvciAiMCIsIHF1ZSBwdWVkZSByZXByZXNlbnRhciBkYXRvcyBmYWx0YW50ZXMuDQoNCioqRGlzdHJpYnVjacOzbiBkZWwgw41uZGljZSBkZSBQcmVkaXNwb3NpY2nDs246KiogbWF5b3IgY29uY2VudHJhY2nDs24gZW50cmUgMC4xIHkgMC42LiBMYSBkaXN0cmlidWNpw7NuIHByZXNlbnRhIHVuYSBtYXJjYWRhIGFzaW1ldHLDrWEgcG9zaXRpdmEsIHlhIHF1ZSBsYSBtYXlvciBwYXJ0ZSBkZSBsb3MgZGF0b3Mgc2UgY29uY2VudHJhIGVuIHZhbG9yZXMgYmFqb3MgeSBsYSBjb2xhIHNlIGV4dGllbmRlIGhhY2lhIGxhIGRlcmVjaGEgKHZhbG9yZXMgY2VyY2Fub3MgYSAyLjUpLiBFc3RhIHZhcmlhYmxlIG5vIHNpZ3VlIHVuYSBkaXN0cmlidWNpw7NuIG5vcm1hbC4NCjwvZGl2Pg0KDQojIyAzLjQgUmVsYWNpw7NuIGVudHJlIGxhcyB2YXJpYWJsZXMgbnVtw6lyaWNhcyB5IGxhIGRpYWJldGVzDQoNCkxvcyBkaWFncmFtYXMgZGUgY2FqYSBwZXJtaXRlbiBvYnNlcnZhciBzaSBjYWRhIHZhcmlhYmxlIHByZXNlbnRhIGRpZmVyZW5jaWFzIGVudHJlIGVsIGdydXBvIGNvbiBkaWFiZXRlcyAoMSkgeSBzaW4gZGlhYmV0ZXMgKDApLCBsbyBjdWFsIGFudGljaXBhIHN1IGNhcGFjaWRhZCBkaXNjcmltaW5hdG9yaWEgZGVudHJvIGRlIGxvcyBtb2RlbG9zIGRlIGNsYXNpZmljYWNpw7NuLg0KDQpgYGB7ciBib3hwbG90cywgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTEwfQ0KZGlhYmV0ZXNfYm94IDwtIGRpYWJldGVzX251ZXZhICU+JQ0KICBwaXZvdF9sb25nZXIoDQogICAgY29scyAgICAgID0gYyhHbHVjb3NlLCBCTUksIEFnZSwgQmxvb2RQcmVzc3VyZSwgRGlhYmV0ZXNQZWRpZ3JlZUZ1bmN0aW9uKSwNCiAgICBuYW1lc190byAgPSAiVmFyaWFibGUiLA0KICAgIHZhbHVlc190byA9ICJWYWxvciINCiAgKSAlPiUNCiAgbXV0YXRlKA0KICAgIFZhcmlhYmxlID0gcmVjb2RlKFZhcmlhYmxlLA0KICAgICAgR2x1Y29zZSAgICAgICAgICAgICAgICAgID0gIkdsdWNvc2EiLA0KICAgICAgQk1JICAgICAgICAgICAgICAgICAgICAgID0gIsONbmRpY2UgZGUgTWFzYSBDb3Jwb3JhbCIsDQogICAgICBBZ2UgICAgICAgICAgICAgICAgICAgICAgPSAiRWRhZCIsDQogICAgICBCbG9vZFByZXNzdXJlICAgICAgICAgICAgPSAiUHJlc2nDs24gQXJ0ZXJpYWwiLA0KICAgICAgRGlhYmV0ZXNQZWRpZ3JlZUZ1bmN0aW9uID0gIlByZWRpc3Bvc2ljacOzbiBGYW1pbGlhciINCiAgICApLA0KICAgIERpYWdub3N0aWNvID0gaWZlbHNlKE91dGNvbWUgPT0gMSwgIkNvbiBkaWFiZXRlcyIsICJTaW4gZGlhYmV0ZXMiKQ0KICApDQoNCmdncGxvdChkaWFiZXRlc19ib3gsIGFlcyh4ID0gRGlhZ25vc3RpY28sIHkgPSBWYWxvciwgZmlsbCA9IERpYWdub3N0aWNvKSkgKw0KICBnZW9tX2JveHBsb3QoDQogICAgYWxwaGEgICAgICAgICA9IDAuOCwNCiAgICBvdXRsaWVyLnNoYXBlID0gMjEsDQogICAgb3V0bGllci5zaXplICA9IDEuNSwNCiAgICBvdXRsaWVyLmZpbGwgID0gIndoaXRlIiwNCiAgICBvdXRsaWVyLmNvbG9yID0gImdyYXk1MCINCiAgKSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIkNvbiBkaWFiZXRlcyIgPSBjb2xfc2ksICJTaW4gZGlhYmV0ZXMiID0gY29sX25vKSkgKw0KICBmYWNldF93cmFwKH4gVmFyaWFibGUsIHNjYWxlcyA9ICJmcmVlX3kiLCBuY29sID0gMikgKw0KICBsYWJzKA0KICAgIHRpdGxlICAgID0gIkRpc3RyaWJ1Y2nDs24gZGUgdmFyaWFibGVzIHBvciBncnVwbyBkZSBkaWFnbsOzc3RpY28iLA0KICAgIHN1YnRpdGxlID0gIkNvbXBhcmFjacOzbiBlbnRyZSBwYWNpZW50ZXMgY29uIHkgc2luIGRpYWJldGVzIiwNCiAgICB4ID0gTlVMTCwgeSA9ICJWYWxvciINCiAgKSArDQogIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gMTIpICsNCiAgdGhlbWUoDQogICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLA0KICAgIHN0cmlwLnRleHQgICAgICA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiLCBjb2xvciA9ICIjMmMzZTUwIiksDQogICAgcGxvdC50aXRsZSAgICAgID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIsIGNvbG9yID0gIiMyYzNlNTAiKSwNCiAgICBwbG90LnN1YnRpdGxlICAgPSBlbGVtZW50X3RleHQoY29sb3IgPSBjb2xfbmV1KQ0KICApDQpgYGANCg0KPGRpdiBjbGFzcz0iaW50ZXJwcmV0YWNpb24iPg0KKipHbHVjb3NhIHNlZ8O6biBEaWFiZXRlczoqKiBsb3MgcGFjaWVudGVzIGRpYWdub3N0aWNhZG9zIGNvbiBkaWFiZXRlcyBwcmVzZW50YW4gbml2ZWxlcyBkZSBnbHVjb3NhIGNsYXJhbWVudGUgbcOhcyBlbGV2YWRvcy4gTGEgbWVkaWFuYSBkZWwgZ3J1cG8gZGlhYsOpdGljbyBlcyBtYXlvciwgbG8gcXVlIGluZGljYSBxdWUgYWwgbWVub3MgbGEgbWl0YWQgZGUgbGFzIHBlcnNvbmFzIGRpYWLDqXRpY2FzIHRpZW5lbiBuaXZlbGVzIGFsdG9zIGRlIGdsdWNvc2EgZW4gY29tcGFyYWNpw7NuIGNvbiBlbCBncnVwbyBzaW4gZGlhYmV0ZXMuIEVsIGdydXBvIGRpYWLDqXRpY28gdGFtYmnDqW4gbXVlc3RyYSBtYXlvciBkaXNwZXJzacOzbiAoY2FqYSBtw6FzIGFtcGxpYSB5IGJpZ290ZXMgbcOhcyBsYXJnb3MpLCBtaWVudHJhcyBxdWUgZWwgZ3J1cG8gc2luIGRpYWJldGVzIHByZXNlbnRhIHVuYSBkaXN0cmlidWNpw7NuIG3DoXMgY29uY2VudHJhZGEuIFNlIGV2aWRlbmNpYW4gdmFsb3JlcyBhdMOtcGljb3MgZW4gYW1ib3MgZ3J1cG9zLiBFc3RhIHZhcmlhYmxlIGVzIGxhIGRlIG1heW9yIGNhcGFjaWRhZCBkaXNjcmltaW5hdG9yaWEgZW4gZWwgZXN0dWRpbyAoTWVkbGluZVBsdXMsIHMuZi4pLg0KDQoqKkJNSSBzZWfDum4gRGlhYmV0ZXM6KiogbG9zIHBhY2llbnRlcyBjb24gZGlhYmV0ZXMgcHJlc2VudGFuIHZhbG9yZXMgZGUgQk1JIG3DoXMgZWxldmFkb3MuIExhIGNhamEgZGVsIGdydXBvIHNpbiBkaWFiZXRlcyBlcyBtw6FzIGFtcGxpYSAobWF5b3IgZGlzcGVyc2nDs24pLCBtaWVudHJhcyBxdWUgbGEgZGVsIGdydXBvIGNvbiBkaWFiZXRlcyBlcyBtw6FzIGVzdHJlY2hhICh2YWxvcmVzIG3DoXMgY29uY2VudHJhZG9zIGFscmVkZWRvciBkZSBsYSBtZWRpYW5hKS4gQW1ib3MgZ3J1cG9zIG11ZXN0cmFuIHZhbG9yZXMgYXTDrXBpY29zLCBpbmNsdXllbmRvIHVuIHZhbG9yIG3DoXhpbW8gZGUgNjcuMTAga2cvbcKyIHkgdmFsb3JlcyBjZXJjYW5vcyBhIDAuDQoNCioqRWRhZCBzZWfDum4gRGlhYmV0ZXM6KiogbGFzIHBhY2llbnRlcyBzaW4gZGlhYmV0ZXMgcHJlc2VudGFuIHVuYSBtZWRpYW5hIGRlIGFwcm94aW1hZGFtZW50ZSAyOSBhw7FvcywgY29uIHVuYSBjYWphIG3DoXMgZXN0cmVjaGEgeSBjb25jZW50cmFjacOzbiBlbiBwYWNpZW50ZXMgasOzdmVuZXMuIExhcyBwYWNpZW50ZXMgY29uIGRpYWJldGVzIG11ZXN0cmFuIHVuYSBtZWRpYW5hIHN1cGVyaW9yIGNlcmNhbmEgYSBsb3MgMzUgYcOxb3MsIGNvbiBtYXlvciB2YXJpYWNpw7NuLiBFbCBncnVwbyBzaW4gZGlhYmV0ZXMgcHJlc2VudGEgdmFyaW9zIHZhbG9yZXMgYXTDrXBpY29zIHBvciBlbmNpbWEgZGUgbG9zIDYwIGHDsW9zLCBsbGVnYW5kbyBoYXN0YSBsb3MgODEgYcOxb3MuIExhIGRpZmVyZW5jaWEgZW50cmUgbWVkaWFuYXMgY29uZmlybWEgcXVlIGxhIGVkYWQgdGllbmUgY2FwYWNpZGFkIGRpc2NyaW1pbmF0b3JpYSByZWxldmFudGUgcGFyYSBlbCBtb2RlbG8uDQoNCioqUHJlc2nDs24gQXJ0ZXJpYWwgc2Vnw7puIERpYWJldGVzOioqIGFtYm9zIGdydXBvcyBwcmVzZW50YW4gdW5hIGRpc3RyaWJ1Y2nDs24gbXV5IHNpbWlsYXIsIGNvbiBtZWRpYW5hcyBjZXJjYW5hcyBlbnRyZSBzw60gKH43MiB5IH43NSBtbUhnKS4gQW1ib3MgZ3J1cG9zIHRpZW5lbiB2YWxvcmVzIGF0w61waWNvcyBlbiBsb3MgZXh0cmVtb3MuIEVzdGEgdmFyaWFibGUgZXZpZGVuY2lhIHBvY2EgZGlmZXJlbmNpYSBlbnRyZSBncnVwb3MsIGxvIHF1ZSBsaW1pdGEgc3UgY2FwYWNpZGFkIGRpc2NyaW1pbmF0b3JpYSBpbmRpdmlkdWFsLg0KDQoqKlByZWRpc3Bvc2ljacOzbiBGYW1pbGlhciBzZWfDum4gRGlhYmV0ZXM6KiogbGFzIHBhY2llbnRlcyBzaW4gZGlhYmV0ZXMgcHJlc2VudGFuIHVuYSBtZWRpYW5hIGRlIGFwcm94aW1hZGFtZW50ZSAwLjM1LCBjb24gY2FqYSBtw6FzIGVzdHJlY2hhLiBMYXMgcGFjaWVudGVzIGNvbiBkaWFiZXRlcyBtdWVzdHJhbiB1bmEgbWVkaWFuYSBsaWdlcmFtZW50ZSBzdXBlcmlvciAofjAuNDgpIHkgbWF5b3IgdmFyaWFjacOzbi4gTGFzIHBhY2llbnRlcyBjb24gZGlhYmV0ZXMgdGllbmRlbiBhIHByZXNlbnRhciB1bmEgcHJlZGlzcG9zaWNpw7NuIGZhbWlsaWFyIGxpZ2VyYW1lbnRlIG3DoXMgYWx0YS4NCjwvZGl2Pg0KDQojIyAzLjUgRXN0YWTDrXN0aWNhcyBwb3IgR3J1cG8NCg0KYGBge3IgbWVkaWFzLWdydXBvfQ0KbWVkaWFfdmFyaWFibGVzIDwtIGRpYWJldGVzX251ZXZhICU+JQ0KICBncm91cF9ieShPdXRjb21lKSAlPiUNCiAgc3VtbWFyaXNlKA0KICAgIGBHbHVjb3NhYCAgICAgICAgICAgICA9IHJvdW5kKG1lYW4oR2x1Y29zZSksIDIpLA0KICAgIGBCTUlgICAgICAgICAgICAgICAgICA9IHJvdW5kKG1lYW4oQk1JKSwgMiksDQogICAgYEVkYWRgICAgICAgICAgICAgICAgID0gcm91bmQobWVhbihBZ2UpLCAyKSwNCiAgICBgUHJlc2nDs24gQXJ0ZXJpYWxgICAgID0gcm91bmQobWVhbihCbG9vZFByZXNzdXJlKSwgMiksDQogICAgYFByZWRpc3Bvc2ljacOzbiBGYW0uYCA9IHJvdW5kKG1lYW4oRGlhYmV0ZXNQZWRpZ3JlZUZ1bmN0aW9uKSwgNCkNCiAgKSAlPiUNCiAgbXV0YXRlKE91dGNvbWUgPSBpZmVsc2UoT3V0Y29tZSA9PSAxLCAiQ29uIGRpYWJldGVzIiwgIlNpbiBkaWFiZXRlcyIpKSAlPiUNCiAgcmVuYW1lKEdydXBvID0gT3V0Y29tZSkNCg0KbWVkaWFfdmFyaWFibGVzICU+JQ0KICBrYWJsZShjYXB0aW9uID0gIlRhYmxhIDEuIE1lZGlhcyBkZSBsYXMgdmFyaWFibGVzIGV4cGxpY2F0aXZhcyBwb3IgZ3J1cG8gZGUgZGlhZ27Ds3N0aWNvIikgJT4lDQogIGthYmxlX3N0eWxpbmcoDQogICAgZnVsbF93aWR0aCAgICAgICAgPSBGQUxTRSwNCiAgICBwb3NpdGlvbiAgICAgICAgICA9ICJjZW50ZXIiLA0KICAgIGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIsICJjb25kZW5zZWQiKQ0KICApICU+JQ0KICByb3dfc3BlYygwLCBib2xkID0gVFJVRSwgY29sb3IgPSAid2hpdGUiLCBiYWNrZ3JvdW5kID0gIiMyQzNFNTAiKSAlPiUNCiAgcm93X3NwZWMoMSwgYmFja2dyb3VuZCA9ICIjRkFEQkQ4IikgJT4lDQogIHJvd19zcGVjKDIsIGJhY2tncm91bmQgPSAiI0Q2RUFGOCIpDQpgYGANCg0KPGRpdiBjbGFzcz0icmVzdWx0YWRvLWNsYXZlIj4NCioqSW50ZXJwcmV0YWNpw7NuIGRlIGxhIHRhYmxhIGRlIG1lZGlhczoqKiBMYSB0YWJsYSBjb25maXJtYSBsYXMgZGlmZXJlbmNpYXMgb2JzZXJ2YWRhcyBlbiBsb3MgZGlhZ3JhbWFzIGRlIGNhamEuIExhcyBwYWNpZW50ZXMgY29uIGRpYWJldGVzIHByZXNlbnRhbiB2YWxvcmVzIHByb21lZGlvIG3DoXMgYWx0b3MgZW4gdG9kYXMgbGFzIHZhcmlhYmxlcy4NCg0KLSAqKkdsdWNvc2E6KiogZGlmZXJlbmNpYSBkZSB+MzEuMjggbWcvZEwgKDE0MS4yNiB2cyAxMDkuOTgpLiBFcyBsYSB2YXJpYWJsZSBjb24gbWF5b3IgcG9kZXIgZGlmZXJlbmNpYWRvciwgY29uZmlybWFuZG8gc3Ugcm9sIGNvbW8gZGV0ZXJtaW5hbnRlIHByaW5jaXBhbCBwYXJhIGVsIGFuw6FsaXNpcy4NCi0gKipCTUk6KiogZGlmZXJlbmNpYSBkZSB+My44NyBrZy9twrIgKDM1LjE0IHZzIDMwLjMwKS4gTGFzIHBhY2llbnRlcyBjb24gZGlhYmV0ZXMgcHJlc2VudGFuIG9iZXNpZGFkIG1vZGVyYWRhICg+MzApLCBtaWVudHJhcyBxdWUgbGFzIHNpbiBkaWFiZXRlcyBzZSBlbmN1ZW50cmFuIGVuIGVsIGzDrW1pdGUgKE9NUywgMjAyNSkuDQotICoqRWRhZDoqKiBkaWZlcmVuY2lhIGRlIH42IGHDsW9zICgzNy4wNyB2cyAzMS4xOSkuIENvbmZpcm1hIHF1ZSBsYXMgcGFjaWVudGVzIGRpYWLDqXRpY2FzIHRpZW5kZW4gYSBzZXIgbWF5b3Jlcy4NCi0gKipQcmVzacOzbiBhcnRlcmlhbCB5IHByZWRpc3Bvc2ljacOzbiBmYW1pbGlhcjoqKiBkaWZlcmVuY2lhcyBtw6FzIHBlcXVlw7FhcyAoMi42NCBtbUhnIHkgMC4xMiBwdW50b3MgcmVzcGVjdGl2YW1lbnRlKSwgY29oZXJlbnRlIGNvbiBsbyBvYnNlcnZhZG8gZW4gbG9zIGRpYWdyYW1hcyBkZSBjYWphLg0KDQpFbiBzw61udGVzaXMsIGxhIGdsdWNvc2EsIGVsIElNQyB5IGxhIGVkYWQgc29uIGxhcyB2YXJpYWJsZXMgY29uIG1heW9yIHBvZGVyIGRpZmVyZW5jaWFkb3IsIGxvIHF1ZSBhbnRpY2lwYSBxdWUgdGVuZHLDoW4gbWF5b3IgcGVzbyBlbiBsb3MgbW9kZWxvcyBkZSBjbGFzaWZpY2FjacOzbi4NCjwvZGl2Pg0KDQotLS0NCg0KIyA0LiBNb2RlbG9zIGRlIENsYXNpZmljYWNpw7NuDQoNCiMjIDQuMSBNb2RlbG8gS05ODQoNCiMjIyBQcmVwYXJhY2nDs24geSBlc3RhbmRhcml6YWNpw7NuDQoNCkRhZG8gcXVlIEtOTiBvcGVyYSBjb24gZGlzdGFuY2lhcyBldWNsaWRpYW5hcywgZXMgaW1wcmVzY2luZGlibGUgZXN0YW5kYXJpemFyIGxhcyB2YXJpYWJsZXMgcGFyYSBldml0YXIgcXVlIGFxdWVsbGFzIGNvbiBtYXlvciBlc2NhbGEgZG9taW5lbiBlbCBjw6FsY3VsbyBkZSBzaW1pbGl0dWQgZW50cmUgcGFjaWVudGVzLg0KDQpgYGB7ciBrbm4tcHJlcGFyYWNpb259DQojIFZhcmlhYmxlcyBhIGVzY2FsYXINCnZhcnNfcHJlZCA8LSBjKCJHbHVjb3NlIiwgIkJNSSIsICJBZ2UiLCAiQmxvb2RQcmVzc3VyZSIsICJEaWFiZXRlc1BlZGlncmVlRnVuY3Rpb24iKQ0KDQojIENhbGN1bGFyIG1lZGlhIHkgc2QgZGVsIGVudHJlbmFtaWVudG8gcGFyYSBlc2NhbGFyIGFtYm9zIGNvbmp1bnRvcyBjb24gbG9zIG1pc21vcyBwYXLDoW1ldHJvcw0KIyAoYnVlbmEgcHLDoWN0aWNhOiBlbCB0ZXN0IG51bmNhIGluZm9ybWEgbG9zIHBhcsOhbWV0cm9zIGRlIGVzY2FsYWRvKQ0KbWVkaWFzX3RyYWluIDwtIGNvbE1lYW5zKHRyYWluX29yaWdpbmFsWywgdmFyc19wcmVkXSkNCnNkc190cmFpbiAgICA8LSBhcHBseSh0cmFpbl9vcmlnaW5hbFssIHZhcnNfcHJlZF0sIDIsIHNkKQ0KDQplc2NhbGFyX2Nvbl90cmFpbiA8LSBmdW5jdGlvbihkZikgew0KICBkZl9lc2MgPC0gZGYNCiAgZm9yICh2IGluIHZhcnNfcHJlZCkgew0KICAgIGRmX2VzY1tbcGFzdGUwKHYsICJfZXNjIildXSA8LSAoZGZbW3ZdXSAtIG1lZGlhc190cmFpblt2XSkgLyBzZHNfdHJhaW5bdl0NCiAgfQ0KICBkZl9lc2MNCn0NCg0Ka25uX2VudHJlbmEgPC0gZXNjYWxhcl9jb25fdHJhaW4odHJhaW5fb3JpZ2luYWwpDQprbm5fdGVzdCAgICA8LSBlc2NhbGFyX2Nvbl90cmFpbih0ZXN0X29yaWdpbmFsKQ0KDQojIENvbnZlcnRpciBPdXRjb21lIGEgZmFjdG9yDQprbm5fZW50cmVuYSRPdXRjb21lIDwtIGZhY3Rvcihrbm5fZW50cmVuYSRPdXRjb21lLCBsZXZlbHMgPSBjKDAsIDEpKQ0Ka25uX3Rlc3QkT3V0Y29tZSAgICA8LSBmYWN0b3Ioa25uX3Rlc3QkT3V0Y29tZSwgICAgbGV2ZWxzID0gYygwLCAxKSkNCg0KIyBOb21icmVzIGRlIGNvbHVtbmFzIGVzY2FsYWRhcw0KdmFyc19lc2MgPC0gcGFzdGUwKHZhcnNfcHJlZCwgIl9lc2MiKQ0KDQojIE1hdHJpY2VzIGRlIGVudHJhZGEgeSB2ZWN0b3JlcyBkZSBzYWxpZGEgcGFyYSBrbm4oKQ0Ka25uX2VudHJlbmFfaW5wdXQgIDwtIGtubl9lbnRyZW5hWywgdmFyc19lc2NdDQprbm5fdGVzdF9pbnB1dCAgICAgPC0ga25uX3Rlc3RbLCAgICB2YXJzX2VzY10NCmtubl9lbnRyZW5hX291dHB1dCA8LSBrbm5fZW50cmVuYSRPdXRjb21lDQprbm5fdGVzdF9vdXRwdXQgICAgPC0ga25uX3Rlc3QkT3V0Y29tZQ0KDQpjYXQoIkVudHJlbmFtaWVudG8gS05OOiIsIG5yb3coa25uX2VudHJlbmEpLCAib2JzZXJ2YWNpb25lc1xuIikNCmNhdCgiUHJ1ZWJhIEtOTjogICAgICAgIiwgbnJvdyhrbm5fdGVzdCksICAgICJvYnNlcnZhY2lvbmVzXG4iKQ0KYGBgDQoNCiMjIyA0LjEuMSBTZWxlY2Npw7NuIGRlbCBwYXLDoW1ldHJvIMOzcHRpbW8gZGUgaw0KDQpQYXJhIGRldGVybWluYXIgZWwgdmFsb3Igw7NwdGltbyBkZSBrLCBzZSB1dGlsaXrDsyBlbCBwYXF1ZXRlIGBjYXJldGAgZXZhbHVhbmRvIDIwMCB2YWxvcmVzIGRlIGsgbWVkaWFudGUgdW4gcmVtdWVzdHJlbyBkZSAyNSByZXBldGljaW9uZXMgc29icmUgbG9zIDMwMCBwYWNpZW50ZXMgZGUgZW50cmVuYW1pZW50by4gRXN0ZSBtw6l0b2RvIHBydWViYSBjYWRhIHZhbG9yIGRlIGsgY29uIGdydXBvcyBkaWZlcmVudGVzIGRlIHBhY2llbnRlcywgbG8gcXVlIHBlcm1pdGUgb2J0ZW5lciB1biByZXN1bHRhZG8gbcOhcyBjb25maWFibGUgcGFyYSBwcmVkZWNpciBlbCBkaWFnbsOzc3RpY28gZGUgZGlhYmV0ZXMuDQoNCmBgYHtyIGtubi1rLW1hbnVhbCwgZmlnLmhlaWdodD00LjV9DQojIEN1cnZhIGRlIHByZWNpc2nDs24gZXhwbG9yYXRvcmlhIGsgPSAxIGEgNTANCmtfdmFscyAgICA8LSAxOjUwDQpyZXN1bHRhZG8gPC0gZGF0YS5mcmFtZShrID0ga192YWxzLCBwcmVjaXNpb24gPSBOQV9yZWFsXykNCg0KZm9yIChuIGluIGtfdmFscykgew0KICBwcmVkX2sgPC0ga25uKA0KICAgIHRyYWluID0ga25uX2VudHJlbmFfaW5wdXQsDQogICAgY2wgICAgPSBrbm5fZW50cmVuYV9vdXRwdXQsDQogICAgdGVzdCAgPSBrbm5fdGVzdF9pbnB1dCwNCiAgICBrICAgICA9IG4NCiAgKQ0KICByZXN1bHRhZG8kcHJlY2lzaW9uW25dIDwtIG1lYW4ocHJlZF9rID09IGtubl90ZXN0X291dHB1dCkNCn0NCg0Ka19vcHQgICA8LSByZXN1bHRhZG8ka1t3aGljaC5tYXgocmVzdWx0YWRvJHByZWNpc2lvbildDQpwcmVjX29wIDwtIHJvdW5kKG1heChyZXN1bHRhZG8kcHJlY2lzaW9uKSAqIDEwMCwgMSkNCg0KZ2dwbG90KHJlc3VsdGFkbywgYWVzKHggPSBrLCB5ID0gcHJlY2lzaW9uKSkgKw0KICBnZW9tX2xpbmUoY29sb3IgPSBjb2xfbm8sIGxpbmV3aWR0aCA9IDAuOSkgKw0KICBnZW9tX3BvaW50KGNvbG9yID0gY29sX25vLCBzaXplID0gMS41LCBhbHBoYSA9IDAuNikgKw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBrX29wdCwgbGluZXR5cGUgPSAiZGFzaGVkIiwNCiAgICAgICAgICAgICBjb2xvciA9IGNvbF9zaSwgbGluZXdpZHRoID0gMC45KSArDQogIGFubm90YXRlKCJsYWJlbCIsDQogICAgeCA9IGtfb3B0ICsgMiwgeSA9IG1pbihyZXN1bHRhZG8kcHJlY2lzaW9uKSArIDAuMDA1LA0KICAgIGxhYmVsID0gcGFzdGUwKCJrIMOzcHRpbW8gPSAiLCBrX29wdCwgIlxuUHJlY2lzacOzbiA9ICIsIHByZWNfb3AsICIlIiksDQogICAgZmlsbCA9ICIjRkRGRUZFIiwgY29sb3IgPSBjb2xfc2ksIHNpemUgPSAzLjUsIGhqdXN0ID0gMA0KICApICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6cGVyY2VudF9mb3JtYXQoYWNjdXJhY3kgPSAxKSkgKw0KICBsYWJzKA0KICAgIHRpdGxlICAgID0gIlByZWNpc2nDs24gZGVsIG1vZGVsbyBLTk4gc2Vnw7puIGVsIG7Dum1lcm8gZGUgdmVjaW5vcyAoaykiLA0KICAgIHN1YnRpdGxlID0gcGFzdGUwKCJFdmFsdWFkbyBzb2JyZSBlbCBjb25qdW50byBkZSBwcnVlYmEgKG4gPSAiLCBucm93KGtubl90ZXN0KSwgIikiKSwNCiAgICB4ID0gIk7Dum1lcm8gZGUgdmVjaW5vcyAoaykiLCB5ID0gIlByZWNpc2nDs24iDQogICkgKw0KICB0aGVtZV9taW5pbWFsKGJhc2Vfc2l6ZSA9IDEyKSArDQogIHRoZW1lKA0KICAgIHBsb3QudGl0bGUgICAgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiwgY29sb3IgPSAiIzJjM2U1MCIpLA0KICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoY29sb3IgPSBjb2xfbmV1KQ0KICApDQpgYGANCg0KPGRpdiBjbGFzcz0iaW50ZXJwcmV0YWNpb24iPg0KTGEgZ3LDoWZpY2EgbXVlc3RyYSBxdWUgY29uIGs9MSBsYSBwcmVjaXNpw7NuIGVzIG11eSBiYWphICh+NjclKSwgcmVmbGVqYW5kbyBhbHRhIGluZXN0YWJpbGlkYWQuIEVudHJlIGs9MSB5IGs9NSBsYSBwcmVjaXNpw7NuIHN1YmUgcsOhcGlkYW1lbnRlIGhhc3RhIH43NSUsIHBhcmEgbHVlZ28gYmFqYXIgYWxyZWRlZG9yIGRlIGs9OC4gQSBwYXJ0aXIgZGUgZXNlIHB1bnRvIG1lam9yYSBoYXN0YSBhbGNhbnphciBzdSBtw6F4aW1vLiBFc3RvIGNvbmZpcm1hIHF1ZSB2YWxvcmVzIG11eSBwZXF1ZcOxb3MgZ2VuZXJhbiBwcmVkaWNjaW9uZXMgcG9jbyBjb25maWFibGVzLCBtaWVudHJhcyBxdWUgdmFsb3JlcyBtb2RlcmFkb3Mgb2ZyZWNlbiBlbCBtZWpvciBkZXNlbXBlw7FvLg0KPC9kaXY+DQoNCmBgYHtyIGtubi1jYXJldCwgZmlnLmhlaWdodD00fQ0KIyBFbnRyZW5hbWllbnRvIGNvbiB2YWxpZGFjacOzbiBjcnV6YWRhIHZpYSBjYXJldCAodHVuZUxlbmd0aCA9IDIwMCB2YWxvcmVzIGRlIGspDQpzZXQuc2VlZCgyOCkNCg0Ka25uX2VudHJlbmFkbyA8LSB0cmFpbigNCiAgT3V0Y29tZSB+IEdsdWNvc2VfZXNjICsgQk1JX2VzYyArIEFnZV9lc2MgKw0KICAgICAgICAgICAgQmxvb2RQcmVzc3VyZV9lc2MgKyBEaWFiZXRlc1BlZGlncmVlRnVuY3Rpb25fZXNjLA0KICBkYXRhICAgICAgID0ga25uX2VudHJlbmEsDQogIG1ldGhvZCAgICAgPSAia25uIiwNCiAgdHVuZUxlbmd0aCA9IDIwMA0KKQ0KDQpwbG90KGtubl9lbnRyZW5hZG8sDQogICAgIG1haW4gPSAiQWNjdXJhY3kgQm9vdHN0cmFwIHNlZ8O6biBuw7ptZXJvIGRlIHZlY2lub3MgKGNhcmV0KSIsDQogICAgIGNvbCAgPSBjb2xfbm8pDQpgYGANCg0KPGRpdiBjbGFzcz0iaW50ZXJwcmV0YWNpb24iPg0KTGEgZ3LDoWZpY2EgZGUgYWNjdXJhY3kgY29uIHJlbXVlc3RyZW8gbXVlc3RyYSBxdWUgY29uIHZhbG9yZXMgcGVxdWXDsW9zIGRlIGsgbGEgZXhhY3RpdHVkIGNvbWllbnphIGFscmVkZWRvciBkZWwgNjYlLCBtZWpvcmFuZG8gcHJvZ3Jlc2l2YW1lbnRlIGhhc3RhIGFsY2FuemFyIHN1IHB1bnRvIG3DoXhpbW8gZW4gKiprID0gNTMqKiBjb24gKio3MS42MCUqKi4gQSBwYXJ0aXIgZGUgZXNlIHB1bnRvIGxhIGV4YWN0aXR1ZCBjYWUgZGUgZm9ybWEgY29udGludWEgaGFzdGEgYXByb3hpbWFkYW1lbnRlIGsgPSAyMjAsIGRvbmRlIHNlIGVzdGFiaWxpemEgYWxyZWRlZG9yIGRlbCA2MyUuIENvbnN1bHRhciBtw6FzIGRlIDIyMCBwYWNpZW50ZXMgc2ltaWxhcmVzIGhhY2UgcXVlIGVsIG1vZGVsbyBwaWVyZGEgdG90YWxtZW50ZSBzdSBjYXBhY2lkYWQgZGlzY3JpbWluYXRvcmlhLiBQb3IgZXN0YSByYXrDs24sICoqayA9IDUzKiogZnVlIGFkb3B0YWRvIGNvbW8gZWwgcGFyw6FtZXRybyBkZWZpbml0aXZvLg0KPC9kaXY+DQoNCiMjIyA0LjEuMiBNYXRyaXogZGUgY29uZnVzacOzbiB5IG3DqXRyaWNhcw0KDQpgYGB7ciBrbm4tcHJlZGljY2lvbn0NCiMgUHJlZGljY2lvbmVzIGRlIGNsYXNlIHkgcHJvYmFiaWxpZGFkZXMgc29icmUgZWwgY29uanVudG8gZGUgcHJ1ZWJhDQprbm5fcHJlZGljY2lvbiAgICAgIDwtIHByZWRpY3Qoa25uX2VudHJlbmFkbywgbmV3ZGF0YSA9IGtubl90ZXN0KQ0KcHJvYl9rbm5fcHJlZGljY2lvbiA8LSBwcmVkaWN0KGtubl9lbnRyZW5hZG8sIG5ld2RhdGEgPSBrbm5fdGVzdCwgdHlwZSA9ICJwcm9iIikNCg0KDQpgYGANCg0KYGBge3Iga25uLWNvbmZ1c2lvbn0NCmNtX2tubiA8LSBjb25mdXNpb25NYXRyaXgoa25uX3ByZWRpY2Npb24sIGtubl90ZXN0JE91dGNvbWUpDQpjbV9rbm4NCmBgYA0KDQo8ZGl2IGNsYXNzPSJyZXN1bHRhZG8tY2xhdmUiPg0KKipSZXN1bHRhZG9zIEtOTiAoayA9IDUzKToqKg0KDQpBbCBldmFsdWFyIGVsIG1vZGVsbyBzb2JyZSBsb3MgMjAwIHBhY2llbnRlcyBkZWwgY29uanVudG8gZGUgcHJ1ZWJhLCBzZSBvYnR1dm8gdW5hICoqZXhhY3RpdHVkIGdsb2JhbCBkZWwgNzUlKiosIHN1cGVyYW5kbyBsYSB0YXNhIGRlIG5vIGluZm9ybWFjacOzbiBkZWwgNjkuNSUsIGxvIHF1ZSBjb25maXJtYSBxdWUgZWwgbW9kZWxvIGFwb3J0YSB2YWxvciByZWFsIGFsIGRpYWduw7NzdGljby4NCg0KLSAqKlNlbnNpYmlsaWRhZDogODcuNzclKiog4oCUIGRlIGxhcyAxMzkgcGFjaWVudGVzIHNhbmFzLCBlbCBtb2RlbG8gaWRlbnRpZmljw7MgY29ycmVjdGFtZW50ZSAxMjIuIEVzdG8gZXF1aXZhbGUgYSBkZXNjYXJ0YXIgY29ycmVjdGFtZW50ZSBsYSBlbmZlcm1lZGFkIGVuIGNhc2kgOSBkZSBjYWRhIDEwIHBhY2llbnRlcyBzYW5hcy4NCi0gKipFc3BlY2lmaWNpZGFkOiA0NS45MCUqKiDigJQgZGUgbGFzIDYxIHBhY2llbnRlcyBkaWFiw6l0aWNhcywgc29sbyBzZSBpZGVudGlmaWNhcm9uIGNvcnJlY3RhbWVudGUgMjguIE3DoXMgZGUgbGEgbWl0YWQgZGUgbGFzIHBhY2llbnRlcyBjb24gZGlhYmV0ZXMgZnVlcm9uIGNsYXNpZmljYWRhcyBjb21vIHNhbmFzLg0KLSAqKlZhbG9yIHByZWRpY3Rpdm8gcG9zaXRpdm86IDc4LjcxJSoqIOKAlCBjdWFuZG8gZWwgbW9kZWxvIHByZWRpY2UgcXVlIHVuYSBwYWNpZW50ZSBubyB0aWVuZSBkaWFiZXRlcywgYWNpZXJ0YSBjYXNpIDggZGUgY2FkYSAxMCB2ZWNlcy4NCi0gKipWYWxvciBwcmVkaWN0aXZvIG5lZ2F0aXZvOiA2Mi4yMiUqKiDigJQgY3VhbmRvIHByZWRpY2UgcXVlIHPDrSB0aWVuZSBkaWFiZXRlcywgYWNpZXJ0YSBzb2xvIDYgZGUgY2FkYSAxMCB2ZWNlcy4NCi0gKipLYXBwYTogMC4zNioqIOKAlCBhY3VlcmRvIG1vZGVyYWRvIGVudHJlIHByZWRpY2Npb25lcyB5IHZhbG9yZXMgcmVhbGVzLg0KLSAqKkV4YWN0aXR1ZCBiYWxhbmNlYWRhOiA2Ni44NCUqKiDigJQgZWwgbW9kZWxvIHRpZW5lIG1lam9yIGRlc2VtcGXDsW8gaWRlbnRpZmljYW5kbyBwYWNpZW50ZXMgc2FuYXMgcXVlIHBhY2llbnRlcyBkaWFiw6l0aWNhcy4NCjwvZGl2Pg0KDQojIyMgNC4xLjMgQ3VydmEgUk9DIOKAlCBLTk4NCg0KYGBge3Iga25uLXJvYywgZmlnLmhlaWdodD00LjV9DQojIENPUlJFQ0NJw5NOOiBzZSB1c2EgbGEgcHJvYmFiaWxpZGFkIGRlIGxhIGNsYXNlIHBvc2l0aXZhICgxID0gY29uIGRpYWJldGVzKQ0KIyBlbiBsdWdhciBkZSBhcy5udW1lcmljKGtubl9wcmVkaWNjaW9uKSwgcXVlIGRhYmEgdW4gQVVDIGRpc3RvcnNpb25hZG8NCnByb2JfcG9zX2tubiA8LSBwcm9iX2tubl9wcmVkaWNjaW9uWywgIjEiXQ0KDQpwcmVkX3JvYyAgICAgIDwtIHByZWRpY3Rpb24ocHJvYl9wb3Nfa25uLCBhcy5udW1lcmljKGFzLmNoYXJhY3Rlcihrbm5fdGVzdCRPdXRjb21lKSkpDQpwZXJmX3JvYyAgICAgIDwtIHBlcmZvcm1hbmNlKHByZWRfcm9jLCAidHByIiwgImZwciIpDQphdWNfa25uICAgICAgIDwtIHBlcmZvcm1hbmNlKHByZWRfcm9jLCAiYXVjIikNCmF1Y19rbm5fdmFsb3IgPC0gcm91bmQoYXVjX2tubkB5LnZhbHVlc1tbMV1dLCAzKQ0KDQpmcHJfdmFscyA8LSB1bmxpc3QocGVyZl9yb2NAeC52YWx1ZXMpDQp0cHJfdmFscyA8LSB1bmxpc3QocGVyZl9yb2NAeS52YWx1ZXMpDQpyb2NfZGYgICA8LSBkYXRhLmZyYW1lKEZQUiA9IGZwcl92YWxzLCBUUFIgPSB0cHJfdmFscykNCg0KZ2dwbG90KHJvY19kZiwgYWVzKHggPSBGUFIsIHkgPSBUUFIpKSArDQogIGdlb21fbGluZShjb2xvciA9IGNvbF9ubywgbGluZXdpZHRoID0gMS4xKSArDQogIGdlb21fYWJsaW5lKHNsb3BlID0gMSwgaW50ZXJjZXB0ID0gMCwgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSBjb2xfbmV1KSArDQogIGFubm90YXRlKCJsYWJlbCIsDQogICAgeCA9IDAuNjUsIHkgPSAwLjE1LA0KICAgIGxhYmVsID0gcGFzdGUwKCJBVUMgPSAiLCBhdWNfa25uX3ZhbG9yKSwNCiAgICBmaWxsID0gIiNFQkY1RkIiLCBjb2xvciA9IGNvbF9ubywgc2l6ZSA9IDQuMiwgZm9udGZhY2UgPSAiYm9sZCINCiAgKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiQ3VydmEgUk9DIOKAlCBNb2RlbG8gS05OIiwNCiAgICB4ID0gIlRhc2EgZGUgRmFsc29zIFBvc2l0aXZvcyAoMSDiiJIgRXNwZWNpZmljaWRhZCkiLA0KICAgIHkgPSAiVGFzYSBkZSBWZXJkYWRlcm9zIFBvc2l0aXZvcyAoU2Vuc2liaWxpZGFkKSINCiAgKSArDQogIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gMTIpICsNCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiLCBjb2xvciA9ICIjMmMzZTUwIikpDQpgYGANCg0KPGRpdiBjbGFzcz0iaW50ZXJwcmV0YWNpb24iPg0KTGEgY3VydmEgUk9DIGRlbCBtb2RlbG8gS05OIGV2YWx1YWRhIHNvYnJlIGVsIGNvbmp1bnRvIGRlIHBydWViYSBtdWVzdHJhIHVuICoqQVVDIGRlIGByIGF1Y19rbm5fdmFsb3JgKiosIGxvIHF1ZSBpbmRpY2EgcXVlIGVsIG1vZGVsbyB0aWVuZSB1biBgciByb3VuZChhdWNfa25uX3ZhbG9yICogMTAwLCAxKWAlIGRlIHByb2JhYmlsaWRhZCBkZSBjbGFzaWZpY2FyIGNvcnJlY3RhbWVudGUgYSB1bmEgcGFjaWVudGUgZGlhYsOpdGljYSBwb3IgZW5jaW1hIGRlIHVuYSBzYW5hOyBlc3RlIHZhbG9yIHNlIGNvbnNpZGVyYSBhY2VwdGFibGUuIEVzdGUgcmVzdWx0YWRvIGVzIGNvaGVyZW50ZSBjb24gbG8gb2JzZXJ2YWRvIGVuIGxhIG1hdHJpeiBkZSBjb25mdXNpw7NuLCBkb25kZSBlbCBtb2RlbG8gbW9zdHLDsyBidWVuIGRlc2VtcGXDsW8gaWRlbnRpZmljYW5kbyBwYWNpZW50ZXMgc2FuYXMgcGVybyBmYWxsw7MgZW4gbcOhcyBkZSBsYSBtaXRhZCBkZSBsb3MgY2Fzb3MgZGlhYsOpdGljb3MuDQo8L2Rpdj4NCg0KLS0tDQoNCiMjIDQuMiBNb2RlbG8gZGUgUmVncmVzacOzbiBMb2fDrXN0aWNhIChMb2dpdCkNCg0KQ29uIGVsIHByb3DDs3NpdG8gZGUgY29tcGxlbWVudGFyIGVsIGFuw6FsaXNpcyB5IGNvbXBhcmFyIGRpc3RpbnRvcyBlbmZvcXVlcyBkZSBjbGFzaWZpY2FjacOzbiwgc2UgaW1wbGVtZW50w7MgdW4gbW9kZWxvIGRlIHJlZ3Jlc2nDs24gbG9nw61zdGljYSB1dGlsaXphbmRvIGVsIG1pc21vIGNvbmp1bnRvIGRlIHZhcmlhYmxlcyBwcmV2aWFtZW50ZSBzZWxlY2Npb25hZGFzLiBFc3RlIG1vZGVsbyBwZXJtaXRpw7MgZXN0aW1hciBsYSBwcm9iYWJpbGlkYWQgZGUgcXVlIHVuYSBwYWNpZW50ZSBwcmVzZW50ZSBkaWFiZXRlcyBhIHBhcnRpciBkZSBmYWN0b3JlcyBjbMOtbmljb3MsIGbDrXNpY29zIHkgaGVyZWRpdGFyaW9zLg0KDQojIyMgUGFydGljacOzbiBkZSBkYXRvcw0KDQpFbCBtb2RlbG8gTG9naXQgdXRpbGl6YSBsYSAqKm1pc21hIHBhcnRpY2nDs24gNzAvMzAqKiBnZW5lcmFkYSBhbCBpbmljaW8gY29uIGBjcmVhdGVEYXRhUGFydGl0aW9uYCwgZ2FyYW50aXphbmRvIHF1ZSBsYSBldmFsdWFjacOzbiBzZWEgZGlyZWN0YW1lbnRlIGNvbXBhcmFibGUgY29uIGVsIG1vZGVsbyBLTk4gc29icmUgZXhhY3RhbWVudGUgbG9zIG1pc21vcyBwYWNpZW50ZXMgZGUgcHJ1ZWJhLg0KDQpgYGB7ciBsb2dpdC1wcmVwfQ0KIyBSZXV0aWxpemFyIGxhIHBhcnRpY2nDs24gdW5pZmljYWRhOiB0cmFpbl9vcmlnaW5hbCB5IHRlc3Rfb3JpZ2luYWwNCm15ZGF0YSAgICAgIDwtIHRyYWluX29yaWdpbmFsDQpteWRhdGEkT3V0Y29tZV9mIDwtIGZhY3RvcihteWRhdGEkT3V0Y29tZSwgbGV2ZWxzID0gYygwLCAxKSwgbGFiZWxzID0gYygiTm8iLCAiU2kiKSkNCg0KdGVzdF9sb2dpdCAgPC0gdGVzdF9vcmlnaW5hbA0KdGVzdF9sb2dpdCRPdXRjb21lX2YgPC0gZmFjdG9yKHRlc3RfbG9naXQkT3V0Y29tZSwgbGV2ZWxzID0gYygwLCAxKSwgbGFiZWxzID0gYygiTm8iLCAiU2kiKSkNCg0KY2F0KCJFbnRyZW5hbWllbnRvIExvZ2l0OiIsIG5yb3cobXlkYXRhKSwgICAgICJvYnNlcnZhY2lvbmVzXG4iKQ0KY2F0KCJQcnVlYmEgTG9naXQ6ICAgICAgICIsIG5yb3codGVzdF9sb2dpdCksICJvYnNlcnZhY2lvbmVzXG4iKQ0KYGBgDQoNCiMjIyA0LjIuMSBBanVzdGUgZSBpbnRlcnByZXRhY2nDs24gZ2VuZXJhbCBkZWwgbW9kZWxvDQoNCmBgYHtyIGxvZ2l0LWFqdXN0ZX0NCmZpdF9sb2dpdCA8LSBnbG0oDQogIE91dGNvbWUgfiBHbHVjb3NlICsgQk1JICsgQWdlICsgQmxvb2RQcmVzc3VyZSArIERpYWJldGVzUGVkaWdyZWVGdW5jdGlvbiwNCiAgZGF0YSAgID0gbXlkYXRhLA0KICBmYW1pbHkgPSBiaW5vbWlhbCgpDQopDQpzdW1tYXJ5KGZpdF9sb2dpdCkNCmBgYA0KDQo8ZGl2IGNsYXNzPSJpbnRlcnByZXRhY2lvbiI+DQpFbiB0w6lybWlub3MgZ2VuZXJhbGVzLCBlbCBtb2RlbG8gZXZpZGVuY2nDsyBxdWUgdmFyaWFibGVzIGNvbW8gbGEgKipnbHVjb3NhLCBlbCDDrW5kaWNlIGRlIG1hc2EgY29ycG9yYWwgeSBsYSBlZGFkKiogcHJlc2VudGFuIHVuYSBpbmZsdWVuY2lhIHBvc2l0aXZhIHNvYnJlIGxhIHByb2JhYmlsaWRhZCBlc3RpbWFkYSBkZSBkaWFiZXRlcywgcmVzdWx0YWRvIGNvbnNpc3RlbnRlIGNvbiBsYSBsaXRlcmF0dXJhIG3DqWRpY2EgeSBjb24gZWwgYW7DoWxpc2lzIGV4cGxvcmF0b3JpbyByZWFsaXphZG8uIFBhY2llbnRlcyBjb24gbWF5b3JlcyBuaXZlbGVzIGRlIGdsdWNvc2EsIG1heW9yIEJNSSBvIG1heW9yIGVkYWQgdGllbmRlbiBhIHByZXNlbnRhciB1biBtYXlvciByaWVzZ28gZGUgZGlhZ27Ds3N0aWNvIHBvc2l0aXZvLiBMYSBwcmVzacOzbiBhcnRlcmlhbCBtb3N0csOzIHVuYSBjYXBhY2lkYWQgZXhwbGljYXRpdmEgbcOhcyBtb2RlcmFkYSwgbWllbnRyYXMgcXVlIGxhIGZ1bmNpw7NuIGRlIHBlZGlncsOtIGRpYWLDqXRpY28gYXBvcnTDsyBpbmZvcm1hY2nDs24gYXNvY2lhZGEgYSBsYSBwcmVkaXNwb3NpY2nDs24gaGVyZWRpdGFyaWEuIExhcyB2YXJpYWJsZXMgY29uICoqcC12YWxvciA8IDAuMDUqKiBzb24gZXN0YWTDrXN0aWNhbWVudGUgc2lnbmlmaWNhdGl2YXMgZW4gbGEgcHJlZGljY2nDs24uDQo8L2Rpdj4NCg0KIyMjIDQuMi4yIERlc2VtcGXDsW8gcHJlZGljdGl2byBkZWwgbW9kZWxvDQoNCmBgYHtyIGxvZ2l0LWV2YWwtdW1icmFsMDV9DQojIFByZWRpY2Npw7NuIGNvbiB1bWJyYWwgZXN0w6FuZGFyIGRlIDAuNQ0KcF9oYXQgICAgICA8LSBwcmVkaWN0KGZpdF9sb2dpdCwgbmV3ZGF0YSA9IHRlc3RfbG9naXQsIHR5cGUgPSAicmVzcG9uc2UiKQ0KcHJlZF9jbGFzZSA8LSBmYWN0b3IoaWZlbHNlKHBfaGF0ID49IDAuNSwgIlNpIiwgIk5vIiksIGxldmVscyA9IGMoIlNpIiwgIk5vIikpDQpjb25mdXNpb25NYXRyaXgocHJlZF9jbGFzZSwgdGVzdF9sb2dpdCRPdXRjb21lX2YsIHBvc2l0aXZlID0gIlNpIikNCmBgYA0KDQpBIGNvbnRpbnVhY2nDs24sIHNlIGFwbGljYSBlbCAqKsOtbmRpY2UgZGUgWW91ZGVuKiogcGFyYSBlbmNvbnRyYXIgZWwgdW1icmFsIGRlIGNsYXNpZmljYWNpw7NuIHF1ZSBtYXhpbWl6YSBzaW11bHTDoW5lYW1lbnRlIGxhIHNlbnNpYmlsaWRhZCB5IGxhIGVzcGVjaWZpY2lkYWQsIGVuIGx1Z2FyIGRlbCB1bWJyYWwgZXN0w6FuZGFyIGRlIDAuNS4NCg0KYGBge3IgbG9naXQtcm9jLCBmaWcuaGVpZ2h0PTQuNX0NCnJvY19vICA8LSByb2MocmVzcG9uc2UgPSB0ZXN0X2xvZ2l0JE91dGNvbWVfZiwgcHJlZGljdG9yID0gcF9oYXQsIGxldmVscyA9IGMoIk5vIiwgIlNpIikpDQp0aHIgICAgPC0gY29vcmRzKHJvY19vLCB4ID0gImJlc3QiLCBiZXN0Lm1ldGhvZCA9ICJ5b3VkZW4iLCByZXQgPSAidGhyZXNob2xkIikNCnVtYnJhbCA8LSBhcy5udW1lcmljKHRocikNCg0KYXVjX3ZhbCAgIDwtIGF1Yyhyb2NfbykNCmF1Y192YWxfciA8LSByb3VuZChhcy5udW1lcmljKGF1Y192YWwpLCAzKQ0KDQpyb2NfZGF0YSA8LSBkYXRhLmZyYW1lKA0KICBGUFIgPSByZXYoMSAtIHJvY19vJHNwZWNpZmljaXRpZXMpLA0KICBUUFIgPSByZXYocm9jX28kc2Vuc2l0aXZpdGllcykNCikNCg0KZ2dwbG90KHJvY19kYXRhLCBhZXMoeCA9IEZQUiwgeSA9IFRQUikpICsNCiAgZ2VvbV9saW5lKGNvbG9yID0gY29sX3NpLCBsaW5ld2lkdGggPSAxLjEpICsNCiAgZ2VvbV9hYmxpbmUoc2xvcGUgPSAxLCBpbnRlcmNlcHQgPSAwLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9IGNvbF9uZXUpICsNCiAgYW5ub3RhdGUoImxhYmVsIiwNCiAgICB4ID0gMC42NSwgeSA9IDAuMTUsDQogICAgbGFiZWwgPSBwYXN0ZTAoIkFVQyA9ICIsIGF1Y192YWxfciwNCiAgICAgICAgICAgICAgICAgICAiXG5VbWJyYWwgw7NwdGltbyA9ICIsIHJvdW5kKHVtYnJhbCwgMykpLA0KICAgIGZpbGwgPSAiI0ZERURFQyIsIGNvbG9yID0gY29sX3NpLCBzaXplID0gMy44LCBmb250ZmFjZSA9ICJib2xkIg0KICApICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICJDdXJ2YSBST0Mg4oCUIFJlZ3Jlc2nDs24gTG9nw61zdGljYSAoTG9naXQpIiwNCiAgICB4ID0gIlRhc2EgZGUgRmFsc29zIFBvc2l0aXZvcyAoMSDiiJIgRXNwZWNpZmljaWRhZCkiLA0KICAgIHkgPSAiVGFzYSBkZSBWZXJkYWRlcm9zIFBvc2l0aXZvcyAoU2Vuc2liaWxpZGFkKSINCiAgKSArDQogIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gMTIpICsNCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiLCBjb2xvciA9ICIjMmMzZTUwIikpDQpgYGANCg0KYGBge3IgbG9naXQtZXZhbC1vcHRpbW99DQojIFJlY2xhc2lmaWNhY2nDs24gY29uIHVtYnJhbCDDs3B0aW1vIGRlIFlvdWRlbg0KcHJlZF9jbGFzZSA8LSBmYWN0b3IoaWZlbHNlKHBfaGF0ID49IHVtYnJhbCwgIlNpIiwgIk5vIiksIGxldmVscyA9IGMoIlNpIiwgIk5vIikpDQpjbV9sb2dpdCAgIDwtIGNvbmZ1c2lvbk1hdHJpeChwcmVkX2NsYXNlLCB0ZXN0X2xvZ2l0JE91dGNvbWVfZiwgcG9zaXRpdmUgPSAiU2kiKQ0KY21fbG9naXQNCmBgYA0KDQo8ZGl2IGNsYXNzPSJyZXN1bHRhZG8tY2xhdmUiPg0KKipSZXN1bHRhZG9zIFJlZ3Jlc2nDs24gTG9nw61zdGljYSAodW1icmFsIMOzcHRpbW8gZGUgWW91ZGVuID0gYHIgcm91bmQodW1icmFsLCAzKWApOioqDQoNCkFsIGV2YWx1YXIgZWwgbW9kZWxvIHNvYnJlIGVsIGNvbmp1bnRvIGRlIHBydWViYSwgbGEgcmVncmVzacOzbiBsb2fDrXN0aWNhIGFsY2FuesOzIHVuYSAqKmV4YWN0aXR1ZCBnbG9iYWwgZGVsIDgwLjczJSoqLCBjbGFzaWZpY2FuZG8gY29ycmVjdGFtZW50ZSBhcHJveGltYWRhbWVudGUgOCBkZSBjYWRhIDEwIHBhY2llbnRlcy4NCg0KLSAqKlNlbnNpYmlsaWRhZDogNzMuMTMlKiog4oCUIGxvZ3LDsyBpZGVudGlmaWNhciBjb3JyZWN0YW1lbnRlIHVuYSBwcm9wb3JjacOzbiBpbXBvcnRhbnRlIGRlIHBhY2llbnRlcyBxdWUgcmVhbG1lbnRlIHByZXNlbnRhbiBkaWFiZXRlcywgcmVkdWNpZW5kbyBmYWxzb3MgbmVnYXRpdm9zLg0KLSAqKkVzcGVjaWZpY2lkYWQ6IDg0LjgwJSoqIOKAlCBhbHRhIGNhcGFjaWRhZCBwYXJhIGlkZW50aWZpY2FyIGNvcnJlY3RhbWVudGUgcGFjaWVudGVzIHNpbiBkaWFiZXRlcywgcmVkdWNpZW5kbyBmYWxzb3MgcG9zaXRpdm9zLg0KLSAqKlZhbG9yIHByZWRpY3Rpdm8gcG9zaXRpdm8gKFBQVik6IDcyLjA2JSoqIOKAlCBjdWFuZG8gY2xhc2lmaWNhIGEgdW5hIHBhY2llbnRlIGNvbW8gcG9zaXRpdmEsIGxhIHByZWRpY2Npw7NuIHRpZW5lIHVuYSBwcm9iYWJpbGlkYWQgYWx0YSBkZSBzZXIgY29ycmVjdGEuDQotICoqVmFsb3IgcHJlZGljdGl2byBuZWdhdGl2byAoTlBWKTogODUuNDglKiog4oCUIHJlZnVlcnphIGxhIGNvbmZpYWJpbGlkYWQgZGVsIG1vZGVsbyBwYXJhIGNsYXNpZmljYXIgcGFjaWVudGVzIHNpbiBsYSBlbmZlcm1lZGFkLg0KLSAqKkV4YWN0aXR1ZCBiYWxhbmNlYWRhOiA3OC45NyUqKiDigJQgZGVzZW1wZcOxbyByZWxhdGl2YW1lbnRlIGVxdWlsaWJyYWRvIGVudHJlIHNlbnNpYmlsaWRhZCB5IGVzcGVjaWZpY2lkYWQuDQotICoqQVVDOiBgciBhdWNfdmFsX3JgKiog4oCUIGNhcGFjaWRhZCBkaXNjcmltaW5hdG9yaWEgc3VwZXJpb3IgYWwgbW9kZWxvIEtOTi4NCjwvZGl2Pg0KDQotLS0NCg0KIyA1LiBDb21wYXJhY2nDs24gZGUgTW9kZWxvcw0KDQpBbCBjb21wYXJhciBsb3MgcmVzdWx0YWRvcyBvYnRlbmlkb3MgZW50cmUgS05OIHkgbGEgcmVncmVzacOzbiBsb2fDrXN0aWNhLCBzZSBpZGVudGlmaWNhcm9uIGRpZmVyZW5jaWFzIGltcG9ydGFudGVzIGVuIGVsIGRlc2VtcGXDsW8gcHJlZGljdGl2by4gQSBjb250aW51YWNpw7NuIHNlIHByZXNlbnRhIGxhIHRhYmxhIHJlc3VtZW4geSBsYSBncsOhZmljYSBjb21wYXJhdGl2YS4NCg0KYGBge3IgY29tcGFyYWNpb24tdGFibGF9DQpjb21wYXJhY2lvbl9tb2RlbG9zIDwtIGRhdGEuZnJhbWUoDQogIE1vZGVsbyAgICAgICAgPSBjKCJLTk4iLCAiTG9naXQiKSwNCiAgQWNjdXJhY3kgICAgICA9IGMoY21fa25uJG92ZXJhbGxbIkFjY3VyYWN5Il0sICAgICAgIGNtX2xvZ2l0JG92ZXJhbGxbIkFjY3VyYWN5Il0pLA0KICBTZW5zaWJpbGlkYWQgID0gYyhjbV9rbm4kYnlDbGFzc1siU2Vuc2l0aXZpdHkiXSwgICAgY21fbG9naXQkYnlDbGFzc1siU2Vuc2l0aXZpdHkiXSksDQogIEVzcGVjaWZpY2lkYWQgPSBjKGNtX2tubiRieUNsYXNzWyJTcGVjaWZpY2l0eSJdLCAgICBjbV9sb2dpdCRieUNsYXNzWyJTcGVjaWZpY2l0eSJdKSwNCiAgUHJlY2lzaW9uICAgICA9IGMoY21fa25uJGJ5Q2xhc3NbIlBvcyBQcmVkIFZhbHVlIl0sIGNtX2xvZ2l0JGJ5Q2xhc3NbIlBvcyBQcmVkIFZhbHVlIl0pLA0KICBBVUMgICAgICAgICAgID0gYyhhdWNfa25uX3ZhbG9yLCBhdWNfdmFsX3IpDQopDQoNCmNvbXBhcmFjaW9uX21vZGVsb3MgJT4lDQogIG11dGF0ZShhY3Jvc3Mod2hlcmUoaXMubnVtZXJpYyksIH4gcm91bmQoLngsIDMpKSkgJT4lDQogIGthYmxlKA0KICAgIGNvbC5uYW1lcyA9IGMoIk1vZGVsbyIsICJFeGFjdGl0dWQiLCAiU2Vuc2liaWxpZGFkIiwNCiAgICAgICAgICAgICAgICAgICJFc3BlY2lmaWNpZGFkIiwgIlByZWNpc2nDs24iLCAiQVVDIiksDQogICAgY2FwdGlvbiAgID0gIlRhYmxhIDIuIENvbXBhcmFjacOzbiBkZSBtw6l0cmljYXMgZGUgY2xhc2lmaWNhY2nDs24g4oCUIEtOTiB2cy4gUmVncmVzacOzbiBMb2fDrXN0aWNhIg0KICApICU+JQ0KICBrYWJsZV9zdHlsaW5nKA0KICAgIGZ1bGxfd2lkdGggICAgICAgID0gRkFMU0UsDQogICAgcG9zaXRpb24gICAgICAgICAgPSAiY2VudGVyIiwNCiAgICBib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiLCAiY29uZGVuc2VkIikNCiAgKSAlPiUNCiAgcm93X3NwZWMoMCwgYm9sZCA9IFRSVUUsIGNvbG9yID0gIndoaXRlIiwgYmFja2dyb3VuZCA9ICIjMkMzRTUwIikgJT4lDQogIHJvd19zcGVjKDEsIGJhY2tncm91bmQgPSAiI0Q2RUFGOCIpICU+JQ0KICByb3dfc3BlYygyLCBiYWNrZ3JvdW5kID0gIiNGQURCRDgiKQ0KYGBgDQoNCmBgYHtyIGNvbXBhcmFjaW9uLWdyYWZpY2EsIGZpZy5oZWlnaHQ9NC41LCBmaWcud2lkdGg9OX0NCmNvbXBhcmFjaW9uX21vZGVsb3MgJT4lDQogIHBpdm90X2xvbmdlcigtTW9kZWxvLCBuYW1lc190byA9ICJNZXRyaWNhIiwgdmFsdWVzX3RvID0gIlZhbG9yIikgJT4lDQogIG11dGF0ZShNZXRyaWNhID0gZmFjdG9yKE1ldHJpY2EsDQogICAgbGV2ZWxzID0gYygiQWNjdXJhY3kiLCAiU2Vuc2liaWxpZGFkIiwgIkVzcGVjaWZpY2lkYWQiLCAiUHJlY2lzaW9uIiwgIkFVQyIpDQogICkpICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBNZXRyaWNhLCB5ID0gVmFsb3IsIGZpbGwgPSBNb2RlbG8pKSArDQogIGdlb21fY29sKHBvc2l0aW9uID0gImRvZGdlIiwgd2lkdGggPSAwLjU4LCBhbHBoYSA9IDAuODgsIGNvbG9yID0gIndoaXRlIikgKw0KICBnZW9tX3RleHQoDQogICAgYWVzKGxhYmVsID0gcm91bmQoVmFsb3IsIDMpKSwNCiAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHdpZHRoID0gMC41OCksDQogICAgdmp1c3QgPSAtMC41LCBzaXplID0gMy4yLCBmb250ZmFjZSA9ICJib2xkIg0KICApICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiS05OIiA9IGNvbF9ubywgIkxvZ2l0IiA9IGNvbF9zaSkpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoMCwgMS4xKSwNCiAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IHNjYWxlczo6cGVyY2VudF9mb3JtYXQoYWNjdXJhY3kgPSAxKSkgKw0KICBsYWJzKA0KICAgIHRpdGxlICAgID0gIkNvbXBhcmFjacOzbiBkZSBtw6l0cmljYXMgZW50cmUgS05OIHkgUmVncmVzacOzbiBMb2fDrXN0aWNhIiwNCiAgICBzdWJ0aXRsZSA9ICJFdmFsdWFkYXMgc29icmUgZWwgY29uanVudG8gZGUgcHJ1ZWJhIiwNCiAgICB4ID0gIk3DqXRyaWNhIiwgeSA9ICJWYWxvciIsIGZpbGwgPSAiTW9kZWxvIg0KICApICsNCiAgdGhlbWVfbWluaW1hbChiYXNlX3NpemUgPSAxMikgKw0KICB0aGVtZSgNCiAgICBwbG90LnRpdGxlICAgICAgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiwgY29sb3IgPSAiIzJjM2U1MCIpLA0KICAgIHBsb3Quc3VidGl0bGUgICA9IGVsZW1lbnRfdGV4dChjb2xvciA9IGNvbF9uZXUpLA0KICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiDQogICkNCmBgYA0KDQojIyMgNS4yIEN1cnZhcyBST0MgY29tcGFyYXRpdmFzDQoNCmBgYHtyIGNvbXBhcmFjaW9uLXJvYywgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9OH0NCiMgQ3VydmEgUk9DIGRlbCBLTk4gdXNhbmRvIHBST0MgKG1pc21hIGxpYnJlcsOtYSBxdWUgTG9naXQgcGFyYSBncsOhZmljbyB1bmlmaWNhZG8pDQpyb2Nfa25uIDwtIHJvYygNCiAgcmVzcG9uc2UgID0ga25uX3Rlc3QkT3V0Y29tZSwNCiAgcHJlZGljdG9yID0gcHJvYl9rbm5fcHJlZGljY2lvblssICIxIl0sDQogIGxldmVscyAgICA9IGMoIjAiLCAiMSIpDQopDQoNCiMgQ29uc3RydWlyIGRhdGEgZnJhbWUgY29uIGFtYmFzIGN1cnZhcyBwYXJhIGdncGxvdA0Kcm9jX2tubl9kZiA8LSBkYXRhLmZyYW1lKA0KICBGUFIgICAgPSByZXYoMSAtIHJvY19rbm4kc3BlY2lmaWNpdGllcyksDQogIFRQUiAgICA9IHJldihyb2Nfa25uJHNlbnNpdGl2aXRpZXMpLA0KICBNb2RlbG8gPSAiS05OIg0KKQ0Kcm9jX2xvZ2l0X2RmIDwtIGRhdGEuZnJhbWUoDQogIEZQUiAgICA9IHJldigxIC0gcm9jX28kc3BlY2lmaWNpdGllcyksDQogIFRQUiAgICA9IHJldihyb2NfbyRzZW5zaXRpdml0aWVzKSwNCiAgTW9kZWxvID0gIkxvZ2l0Ig0KKQ0Kcm9jX2NvbXBhcmFjaW9uIDwtIGJpbmRfcm93cyhyb2Nfa25uX2RmLCByb2NfbG9naXRfZGYpDQoNCmdncGxvdChyb2NfY29tcGFyYWNpb24sIGFlcyh4ID0gRlBSLCB5ID0gVFBSLCBjb2xvciA9IE1vZGVsbykpICsNCiAgZ2VvbV9saW5lKGxpbmV3aWR0aCA9IDEuMikgKw0KICBnZW9tX2FibGluZShzbG9wZSA9IDEsIGludGVyY2VwdCA9IDAsIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gY29sX25ldSkgKw0KICBhbm5vdGF0ZSgibGFiZWwiLA0KICAgIHggPSAwLjYyLCB5ID0gMC4yMiwNCiAgICBsYWJlbCA9IHBhc3RlMCgiQVVDIEtOTiAgID0gIiwgYXVjX2tubl92YWxvciwNCiAgICAgICAgICAgICAgICAgICAiXG5BVUMgTG9naXQgPSAiLCBhdWNfdmFsX3IpLA0KICAgIGZpbGwgPSAiI0ZERkVGRSIsIGNvbG9yID0gIiMyYzNlNTAiLCBzaXplID0gMy44LCBmb250ZmFjZSA9ICJib2xkIiwgaGp1c3QgPSAwDQogICkgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiS05OIiA9IGNvbF9ubywgIkxvZ2l0IiA9IGNvbF9zaSkpICsNCiAgbGFicygNCiAgICB0aXRsZSAgICA9ICJDdXJ2YXMgUk9DIGNvbXBhcmF0aXZhcyDigJQgS05OIHZzLiBSZWdyZXNpw7NuIExvZ8Otc3RpY2EiLA0KICAgIHN1YnRpdGxlID0gIkV2YWx1YWRhcyBzb2JyZSBlbCBtaXNtbyBjb25qdW50byBkZSBwcnVlYmEgKHBhcnRpY2nDs24gNzAvMzApIiwNCiAgICB4ID0gIlRhc2EgZGUgRmFsc29zIFBvc2l0aXZvcyAoMSDiiJIgRXNwZWNpZmljaWRhZCkiLA0KICAgIHkgPSAiVGFzYSBkZSBWZXJkYWRlcm9zIFBvc2l0aXZvcyAoU2Vuc2liaWxpZGFkKSIsDQogICAgY29sb3IgPSAiTW9kZWxvIg0KICApICsNCiAgdGhlbWVfbWluaW1hbChiYXNlX3NpemUgPSAxMikgKw0KICB0aGVtZSgNCiAgICBwbG90LnRpdGxlICAgICAgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiwgY29sb3IgPSAiIzJjM2U1MCIpLA0KICAgIHBsb3Quc3VidGl0bGUgICA9IGVsZW1lbnRfdGV4dChjb2xvciA9IGNvbF9uZXUpLA0KICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiDQogICkNCmBgYA0KDQo8ZGl2IGNsYXNzPSJyZXN1bHRhZG8tY2xhdmUiPg0KKipJbnRlcnByZXRhY2nDs24gY29tcGFyYXRpdmE6KioNCg0KLSAqKkV4YWN0aXR1ZCBnbG9iYWw6KiogZWwgbW9kZWxvIExvZ2l0IHByZXNlbnTDsyB1biBtZWpvciByZXN1bHRhZG8gKDgwLjczJSB2cyA3NS4wMCUpLCBjbGFzaWZpY2FuZG8gY29ycmVjdGFtZW50ZSB1bmEgbWF5b3IgcHJvcG9yY2nDs24gZGUgcGFjaWVudGVzLg0KLSAqKlNlbnNpYmlsaWRhZDoqKiBLTk4gbW9zdHLDsyB1bmEgdmVudGFqYSBjb25zaWRlcmFibGUgKDg3Ljc3JSB2cyA3My4xMyUpLCBzaWVuZG8gbcOhcyBlZmVjdGl2byBwYXJhIGRldGVjdGFyIHBhY2llbnRlcyBxdWUgcmVhbG1lbnRlIHByZXNlbnRhbiBkaWFiZXRlcy4gRGVzZGUgdW5hIHBlcnNwZWN0aXZhIG3DqWRpY2EsIGVzdG8gcHVlZGUgc2VyIHZhbGlvc28gY3VhbmRvIGxhIHByaW9yaWRhZCBlcyBtYXhpbWl6YXIgbGEgZGV0ZWNjacOzbiB0ZW1wcmFuYS4NCi0gKipFc3BlY2lmaWNpZGFkOioqIGxhIHJlZ3Jlc2nDs24gbG9nw61zdGljYSBwcmVzZW50w7MgdW5hIHN1cGVyaW9yaWRhZCBtdXkgbWFyY2FkYSAoODQuODAlIHZzIDQ1LjkwJSksIHNpZW5kbyBzaWduaWZpY2F0aXZhbWVudGUgbcOhcyBwcmVjaXNhIHBhcmEgaWRlbnRpZmljYXIgcGFjaWVudGVzIHNpbiBkaWFiZXRlcyB5IHJlZHVjaWVuZG8gZmFsc29zIHBvc2l0aXZvcy4NCi0gKipBVUM6KiogZmF2b3JlY2UgYW1wbGlhbWVudGUgYSBMb2dpdCAoYHIgYXVjX3ZhbF9yYCB2cyBgciBhdWNfa25uX3ZhbG9yYCksIGNvbmZpcm1hbmRvIHVuYSBjYXBhY2lkYWQgY29uc2lkZXJhYmxlbWVudGUgc3VwZXJpb3IgcGFyYSBkaWZlcmVuY2lhciBlbnRyZSBhbWJhcyBjbGFzZXMgYSBkaXN0aW50b3Mgbml2ZWxlcyBkZSBkZWNpc2nDs24uDQoNCkFtYm9zIG1vZGVsb3MgcHJlc2VudGFuIGZvcnRhbGV6YXMgZGlzdGludGFzLiBLTk4gZGVzdGFjw7MgcG9yIHN1IGFsdGEgc2Vuc2liaWxpZGFkOyBzaW4gZW1iYXJnbywgc3UgYmFqYSBlc3BlY2lmaWNpZGFkIHkgbWVub3IgQVVDIHJlZmxlamFuIG1heW9yIHRlbmRlbmNpYSBhIGNvbWV0ZXIgZmFsc29zIHBvc2l0aXZvcy4gTGEgcmVncmVzacOzbiBsb2fDrXN0aWNhIG5vIHNvbG8gcHJlc2VudMOzIG1lam9yZXMgbcOpdHJpY2FzIGdsb2JhbGVzLCBzaW5vIHF1ZSBhZGVtw6FzIG9mcmVjZSB1bmEgdmVudGFqYSBpbnRlcnByZXRhdGl2YSwgcGVybWl0aWVuZG8gY29tcHJlbmRlciBjw7NtbyBjYWRhIHZhcmlhYmxlIGluZmx1eWUgc29icmUgbGEgcHJvYmFiaWxpZGFkIGRlIGRlc2Fycm9sbGFyIGRpYWJldGVzLiBFbiBjb25zZWN1ZW5jaWEsICoqbGEgcmVncmVzacOzbiBsb2fDrXN0aWNhIHNlIHBvc2ljaW9uYSBjb21vIGVsIG1vZGVsbyBtw6FzIGFkZWN1YWRvKiogcGFyYSBlc3RlIGVzdHVkaW8sIHBvciBzdSBtYXlvciBleGFjdGl0dWQsIG1lam9yIGVzcGVjaWZpY2lkYWQsIHN1cGVyaW9yIEFVQyB5IHV0aWxpZGFkIGludGVycHJldGF0aXZhLg0KPC9kaXY+DQoNCi0tLQ0KDQojIDYuIENvbmNsdXNpb25lcw0KDQo8ZGl2IGNsYXNzPSJhZHZlcnRlbmNpYSI+DQoqKkxpbWl0YWNpb25lcyBkZWwgZXN0dWRpbzoqKiBFbCBhbsOhbGlzaXMgc2UgcmVzdHJpbmdlIGEgbXVqZXJlcyBpbmTDrWdlbmFzIFBpbWEgbWF5b3JlcyBkZSAyMSBhw7FvcywgcG9yIGxvIHF1ZSBsb3MgcmVzdWx0YWRvcyBubyBzb24gZGlyZWN0YW1lbnRlIGdlbmVyYWxpemFibGVzIGEgb3RyYXMgcG9ibGFjaW9uZXMuIFNlIGRldGVjdGFyb24gdmFsb3JlcyBkZSAwIGVuIHZhcmlhYmxlcyBjb21vIGdsdWNvc2EgeSBwcmVzacOzbiBhcnRlcmlhbCBxdWUgcG9kcsOtYW4gY29ycmVzcG9uZGVyIGEgZGF0b3MgZmFsdGFudGVzIG5vIHRyYXRhZG9zIGV4cGzDrWNpdGFtZW50ZSwgbG8gcXVlIHB1ZWRlIGFmZWN0YXIgbGEgY2FwYWNpZGFkIHByZWRpY3RpdmEgZGUgbG9zIG1vZGVsb3MuIExhIHBhcnRpY2nDs24gNzAvMzAgY29uIHNlbWlsbGEgZmlqYSBnYXJhbnRpemEgcmVwcm9kdWNpYmlsaWRhZCwgcGVybyBsb3MgcmVzdWx0YWRvcyBwb2Ryw61hbiB2YXJpYXIgY29uIG90cmFzIHNlbWlsbGFzIGFsZWF0b3JpYXMuDQo8L2Rpdj4NCg0KQSBwYXJ0aXIgZGVsIGFuw6FsaXNpcyBjb21wYXJhdGl2byBlbnRyZSBsb3MgbW9kZWxvcyBLTk4geSBSZWdyZXNpw7NuIExvZ8Otc3RpY2EgcGFyYSBsYSBjbGFzaWZpY2FjacOzbiBkZSBwYWNpZW50ZXMgY29uIGRpYWJldGVzIGVuIGxhIGJhc2UgZGUgZGF0b3MgUGltYSBJbmRpYW5zLCBzZSBjb25jbHV5ZToNCg0KMS4gKipMYSBnbHVjb3NhLCBlbCBCTUkgeSBsYSBlZGFkKiogc29uIGxhcyB2YXJpYWJsZXMgY29uIG1heW9yIHBvZGVyIGRpZmVyZW5jaWFkb3IgZW50cmUgcGFjaWVudGVzIGNvbiB5IHNpbiBkaWFiZXRlcywgY29uZmlybWFuZG8gbG9zIGhhbGxhemdvcyBkZSBsYSBsaXRlcmF0dXJhIGNpZW50w61maWNhIHByZXZpYS4gTGEgcHJlc2nDs24gYXJ0ZXJpYWwgbW9zdHLDsyBsYSBtZW5vciBjYXBhY2lkYWQgZGlzY3JpbWluYXRvcmlhIGluZGl2aWR1YWwuDQoNCjIuICoqRWwgbW9kZWxvIGRlIFJlZ3Jlc2nDs24gTG9nw61zdGljYSBzdXBlcmEgYWwgS05OKiogZW4gZXhhY3RpdHVkIGdsb2JhbCwgZXNwZWNpZmljaWRhZCB5IEFVQyAoYHIgYXVjX3ZhbF9yYCB2cyBgciBhdWNfa25uX3ZhbG9yYCksIGNvbW8gc2UgZXZpZGVuY2lhIHRhbnRvIGVuIGxhIHRhYmxhIGRlIG3DqXRyaWNhcyBjb21vIGVuIGxhcyBjdXJ2YXMgUk9DIGNvbXBhcmF0aXZhcywgY29uc29saWTDoW5kb3NlIGNvbW8gZWwgbW9kZWxvIG3DoXMgcm9idXN0byBkZW50cm8gZGVsIGFuw6FsaXNpcyByZWFsaXphZG8uDQoNCjMuICoqRWwgbW9kZWxvIEtOTiBkZXN0YWPDsyBwb3Igc3UgYWx0YSBzZW5zaWJpbGlkYWQqKiAoODcuNzclKSwgbG8gcXVlIGxvIGNvbnZpZXJ0ZSBlbiB1bmEgYWx0ZXJuYXRpdmEgw7p0aWwgY3VhbmRvIGVsIG9iamV0aXZvIHByaW5jaXBhbCBlcyBkZXRlY3RhciBsYSBtYXlvciBjYW50aWRhZCBwb3NpYmxlIGRlIGNhc29zIHBvc2l0aXZvcywgYXVucXVlIGEgY29zdGEgZGUgdW5hIGVsZXZhZGEgdGFzYSBkZSBmYWxzb3MgcG9zaXRpdm9zLg0KDQo0LiAqKkxhIG9wdGltaXphY2nDs24gZGVsIHVtYnJhbCBkZSBjbGFzaWZpY2FjacOzbioqIG1lZGlhbnRlIGVsIMOtbmRpY2UgZGUgWW91ZGVuIG1lam9yw7MgZWwgYmFsYW5jZSBlbnRyZSBzZW5zaWJpbGlkYWQgeSBlc3BlY2lmaWNpZGFkIGVuIGVsIG1vZGVsbyBMb2dpdCwgcGVybWl0aWVuZG8gdW5hIGNsYXNpZmljYWNpw7NuIG3DoXMgZXF1aWxpYnJhZGEgZW4gYW1iYXMgY2xhc2VzLg0KDQo1LiBEZXNkZSBlbCBwdW50byBkZSB2aXN0YSBjbMOtbmljbywgZGFkbyBxdWUgbG9zICoqZmFsc29zIG5lZ2F0aXZvcyoqIChubyBkZXRlY3RhciB1bmEgZW5mZXJtZWRhZCBwcmVzZW50ZSkgdGllbmVuIGNvbnNlY3VlbmNpYXMgbcOhcyBncmF2ZXMgcXVlIGxvcyBmYWxzb3MgcG9zaXRpdm9zLCBsYSByZWdyZXNpw7NuIGxvZ8Otc3RpY2EgcmVwcmVzZW50YSBsYSBoZXJyYW1pZW50YSBtw6FzIGNvbXBsZXRhIHkgZXF1aWxpYnJhZGEgcGFyYSBlc3RlIHByb2JsZW1hIGRlIGRpYWduw7NzdGljby4NCg0KLS0tDQoNCiMgNy4gUmVmZXJlbmNpYXMNCg0KLSBDREMuICgyMDI0KS4gRmFjdG9yZXMgZGUgcmllc2dvIGRlIGxhIGRpYWJldGVzLiBDZW50ZXJzIGZvciBEaXNlYXNlIENvbnRyb2wgYW5kIFByZXZlbnRpb24uIGh0dHBzOi8vd3d3LmNkYy5nb3YvZGlhYmV0ZXMvZXMvcmlzay1mYWN0b3JzL2ZhY3RvcmVzLWRlLXJpZXNnby1kZS1sYS1kaWFiZXRlcy5odG1sDQotIEdlbmZhci4gKDIwMjEpLiDCv0N1w6FsZXMgc29uIGxvcyB2YWxvcmVzIG5vcm1hbGVzIGRlIGxhIHByZXNpw7NuIGFydGVyaWFsPyBodHRwczovL3d3dy5nZW5mYXIuY29tL3RlLWN1aWRhbW9zL2N1YWxlcy1zb24tbG9zLXZhbG9yZXMtbm9ybWFsZXMtZGUtbGEtcHJlc2lvbi1hcnRlcmlhbC8NCi0gSm9zaGksIFIuIEQuLCAmIERoYWthbCwgQy4gSy4gKDIwMjEpLiBQcmVkaWN0aW5nIHR5cGUgMiBkaWFiZXRlcyB1c2luZyBsb2dpc3RpYyByZWdyZXNzaW9uIGFuZCBtYWNoaW5lIGxlYXJuaW5nIGFwcHJvYWNoZXMuICpJbnRlcm5hdGlvbmFsIEpvdXJuYWwgb2YgRW52aXJvbm1lbnRhbCBSZXNlYXJjaCBhbmQgUHVibGljIEhlYWx0aCwgMTgqKDE0KSwgNzM0Ni4gaHR0cHM6Ly9wbWMubmNiaS5ubG0ubmloLmdvdi9hcnRpY2xlcy9QTUM4MzA2NDg3Lw0KLSBMYWd1bmVzIFRvcnJlcywgQS4gKDIwMjMpLiBSZWxhY2nDs24gZW50cmUgZWwgw61uZGljZSBkZSBtYXNhIGNvcnBvcmFsIHkgbGEgZGlhYmV0ZXMgdGlwbyAyLiAqUmV2aXN0YSBNZXhpY2FuYSBkZSBFbmRvY3Jpbm9sb2fDrWEuKg0KLSBNZWRsaW5lUGx1cy4gKHMuZi4pLiBHbHVjb3NhIGVuIHNhbmdyZS4gaHR0cHM6Ly9tZWRsaW5lcGx1cy5nb3Yvc3BhbmlzaC9ibG9vZGdsdWNvc2UuaHRtbA0KLSBPTVMuIChjaXRhZG8gZW4gUmV2aXN0YSBEaWFiZXRlcywgMjAyNCkuIEhlcmVuY2lhIGRlIG9iZXNpZGFkIHkgZGlhYmV0ZXM6IG3DoXMgYWxsw6EgZGUgbnVlc3Ryb3MgZ2VuZXMuIGh0dHBzOi8vd3d3LnJldmlzdGFkaWFiZXRlcy5vcmcvaW52ZXN0aWdhY2lvbi9oZXJlbmNpYS1kZS1vYmVzaWRhZC15LWRpYWJldGVzLW1hcy1hbGxhLWRlLW51ZXN0cm9zLWdlbmVzLw0KLSBPTVMuICgyMDI1KS4gT2Jlc2lkYWQgeSBzb2JyZXBlc28uIGh0dHBzOi8vd3d3Lndoby5pbnQvZXMvbmV3cy1yb29tL2ZhY3Qtc2hlZXRzL2RldGFpbC9vYmVzaXR5LWFuZC1vdmVyd2VpZ2h0DQotIFNhbXBhbmlzLCBDLiwgJiBaYW1ib3VsaXMsIEMuICgyMDA4KS4gSHlwZXJ0ZW5zaW9uIGFuZCBkaWFiZXRlcyBtZWxsaXR1cy4gKkhpcHBva3JhdGlhLCAxMiooMSksIDEy4oCTMTUuDQotIFdITy4gKDIwMjQpLiBEaWFiZXRlcy4gaHR0cHM6Ly93d3cud2hvLmludC9lcy9uZXdzLXJvb20vZmFjdC1zaGVldHMvZGV0YWlsL2RpYWJldGVzDQoNCi0tLQ0KDQo=