Analisis de Accidentes Cerebrovasculares

Problema a abordar

El trabajo consiste en realizar una comparación entre dos modelos predictivos: por un lado por medio de una regresión logística y por otro lado un modelo de arboles de decisión llamado Random Forest. El objetivo es identificar los principales predictores de los ACVs y en segundo lugar evalular el rendimiento entre los dos modelos.

Fuente de información: “stroke.csv” extraido de Kaggle. Muestra de 5110 observaciones.

Los datos incluyen información sobre:

-Características demográficas (edad, género)

-Condiciones de salud (hipertensión, enfermedad cardíaca)

-Habitos de vida (estado de fumador)

-Medidas clínicas (nivel promedio de glucosa)

Variables

id: Identicador de cada observación

gender: Variable Categórica con valores “Male” (masculino) y “Female” (femenino).

age: Una variable numérica que representa la edad del individuo en años.

hypertension: Hipertensión expresado de manera binaria (0 o 1), como presencia o ausencia.

heart_disease: Una variable binaria (0 o 1) que indica si el individuo tiene enfermedad cardíaca (1) o no (0)

ever_married: Una variable categórica que indica si el individuo ha estado casado alguna vez, con valores “Yes” (sí) o “No”.

work_type: variable categórica que describe el tipo de trabajo del individuo, con valores como “Private” (privado), Govt_job (trabajo gubernamental) y “Self-employed” (autónomo).

Residence_type: variable categórica que indica el tipo de residencia del individuo, con valores “Urban” (urbano) o “Rural”

avg_glucose_level: representa el nivel promedio de glucosa en sangre del individuo.

bmi: Rerepresenta el indice de Masa Corporal (IMC) del individuo.

smoking_status: variable categórica que indica el estado de fumador del individuo, con valores como “formerly smoked” (ex fumador), “never smoked” (nunca fumó) y “smokes” (fuma).

stroke Una variable binaria (0 o 1) que indica si el individuo ha sufrido un accidente cerebrovascular (1) o no (0).

library(missRanger)
library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.4     ✔ readr     2.1.5
## ✔ forcats   1.0.0     ✔ stringr   1.5.0
## ✔ ggplot2   3.4.4     ✔ tibble    3.2.1
## ✔ lubridate 1.9.3     ✔ tidyr     1.3.0
## ✔ purrr     1.0.1     
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(caret)
## Loading required package: lattice
## 
## Attaching package: 'caret'
## 
## The following object is masked from 'package:purrr':
## 
##     lift
library(randomForest)
## randomForest 4.7-1.1
## Type rfNews() to see new features/changes/bug fixes.
## 
## Attaching package: 'randomForest'
## 
## The following object is masked from 'package:dplyr':
## 
##     combine
## 
## The following object is masked from 'package:ggplot2':
## 
##     margin
library(MASS)
## 
## Attaching package: 'MASS'
## 
## The following object is masked from 'package:dplyr':
## 
##     select
library(pROC)
## Type 'citation("pROC")' for a citation.
## 
## Attaching package: 'pROC'
## 
## The following objects are masked from 'package:stats':
## 
##     cov, smooth, var
library(dplyr)
library(fastDummies)
## Thank you for using fastDummies!
## To acknowledge our work, please cite the package:
## Kaplan, J. & Schlegel, B. (2023). fastDummies: Fast Creation of Dummy (Binary) Columns and Rows from Categorical Variables. Version 1.7.1. URL: https://github.com/jacobkap/fastDummies, https://jacobkap.github.io/fastDummies/.
library(caret)
library(glmnet)
## Loading required package: Matrix
## 
## Attaching package: 'Matrix'
## 
## The following objects are masked from 'package:tidyr':
## 
##     expand, pack, unpack
## 
## Loaded glmnet 4.1-8
library(DataExplorer)
library(car)
## Loading required package: carData
## 
## Attaching package: 'car'
## 
## The following object is masked from 'package:dplyr':
## 
##     recode
## 
## The following object is masked from 'package:purrr':
## 
##     some

1.Carga de Datos

datos <- read.csv("C:/Users/lluiselli/Downloads/stroke.csv")
datos$bmi[datos$bmi == "N/A"] <- NA
datos$smoking_status[datos$smoking_status == "Unknown"] <- NA

2.Analisis Exploratorio

head(datos)
##      id gender age hypertension heart_disease ever_married     work_type
## 1  9046   Male  67            0             1          Yes       Private
## 2 51676 Female  61            0             0          Yes Self-employed
## 3 31112   Male  80            0             1          Yes       Private
## 4 60182 Female  49            0             0          Yes       Private
## 5  1665 Female  79            1             0          Yes Self-employed
## 6 56669   Male  81            0             0          Yes       Private
##   Residence_type avg_glucose_level  bmi  smoking_status stroke
## 1          Urban            228.69 36.6 formerly smoked      1
## 2          Rural            202.21 <NA>    never smoked      1
## 3          Rural            105.92 32.5    never smoked      1
## 4          Urban            171.23 34.4          smokes      1
## 5          Rural            174.12   24    never smoked      1
## 6          Urban            186.21   29 formerly smoked      1
plot_intro(datos)

plot_missing(datos)

##Imputación de Valores faltantes

#Ante la presencia de valores nulos, se procede a imputar valores faltantes por medio de MissRanger

datos$gender <- as.factor(datos$gender)
datos$hypertension <- as.factor(datos$hypertension)
datos$ever_married <-  as.factor(datos$ever_married)
datos$work_type <-  as.factor(datos$work_type)
datos$Residence_type <-  as.factor(datos$Residence_type)
datos$bmi <- as.numeric(datos$bmi)
datos$smoking_status <-  as.factor(datos$smoking_status)

# Imputar valores faltantes con missRanger
datos_imputados <- missRanger(datos)
## 
## Missing value imputation by random forests
## 
##   Variables to impute:       bmi, smoking_status
##   Variables used to impute:  id, gender, age, hypertension, heart_disease, ever_married, work_type, Residence_type, avg_glucose_level, bmi, smoking_status, stroke
## 
## iter 1
## 
  |                                                                            
  |                                                                      |   0%
  |                                                                            
  |===================================                                   |  50%
  |                                                                            
  |======================================================================| 100%
## iter 2
## 
  |                                                                            
  |                                                                      |   0%
  |                                                                            
  |===================================                                   |  50%
  |                                                                            
  |======================================================================| 100%
## iter 3
## 
  |                                                                            
  |                                                                      |   0%
  |                                                                            
  |===================================                                   |  50%
  |                                                                            
  |======================================================================| 100%
sum(is.na(datos_imputados))
## [1] 0
#Comparación
table(datos$smoking_status)
## 
## formerly smoked    never smoked          smokes 
##             885            1892             789
table(datos_imputados$smoking_status)
## 
## formerly smoked    never smoked          smokes 
##             968            3335             807
plot_missing(datos_imputados)

# EDA de datos limpios

#Veamos la distribución de cada variable
plot_bar(datos_imputados)

plot_histogram(datos_imputados[,-1])

3.Analisis de Regresión Logísitica

# Seleccionar el mejor modelo en funcion a Akaike

full_model <- glm(stroke ~ ., data=datos_imputados, family=binomial)
step_model <- step(full_model, direction="both")
## Start:  AIC=1608.57
## stroke ~ id + gender + age + hypertension + heart_disease + ever_married + 
##     work_type + Residence_type + avg_glucose_level + bmi + smoking_status
## 
##                     Df Deviance    AIC
## - gender             2   1574.6 1604.6
## - bmi                1   1574.7 1606.7
## - id                 1   1574.8 1606.8
## - Residence_type     1   1574.9 1606.9
## - work_type          4   1581.2 1607.2
## - ever_married       1   1575.4 1607.4
## - heart_disease      1   1576.3 1608.3
## <none>                   1574.6 1608.6
## - hypertension       1   1580.2 1612.2
## - smoking_status     2   1583.6 1613.6
## - avg_glucose_level  1   1584.6 1616.6
## - age                1   1773.9 1805.9
## 
## Step:  AIC=1604.64
## stroke ~ id + age + hypertension + heart_disease + ever_married + 
##     work_type + Residence_type + avg_glucose_level + bmi + smoking_status
## 
##                     Df Deviance    AIC
## - bmi                1   1574.7 1602.7
## - id                 1   1574.9 1602.9
## - Residence_type     1   1574.9 1602.9
## - work_type          4   1581.3 1603.3
## - ever_married       1   1575.5 1603.5
## - heart_disease      1   1576.4 1604.4
## <none>                   1574.6 1604.6
## - hypertension       1   1580.3 1608.3
## + gender             2   1574.6 1608.6
## - smoking_status     2   1583.7 1609.7
## - avg_glucose_level  1   1584.6 1612.6
## - age                1   1774.3 1802.3
## 
## Step:  AIC=1602.73
## stroke ~ id + age + hypertension + heart_disease + ever_married + 
##     work_type + Residence_type + avg_glucose_level + smoking_status
## 
##                     Df Deviance    AIC
## - id                 1   1575.0 1601.0
## - Residence_type     1   1575.0 1601.0
## - work_type          4   1581.3 1601.3
## - ever_married       1   1575.6 1601.6
## - heart_disease      1   1576.4 1602.4
## <none>                   1574.7 1602.7
## + bmi                1   1574.6 1604.6
## - hypertension       1   1580.6 1606.6
## + gender             2   1574.7 1606.7
## - smoking_status     2   1583.8 1607.8
## - avg_glucose_level  1   1585.6 1611.6
## - age                1   1778.3 1804.3
## 
## Step:  AIC=1600.97
## stroke ~ age + hypertension + heart_disease + ever_married + 
##     work_type + Residence_type + avg_glucose_level + smoking_status
## 
##                     Df Deviance    AIC
## - Residence_type     1   1575.2 1599.2
## - work_type          4   1581.6 1599.6
## - ever_married       1   1575.8 1599.8
## - heart_disease      1   1576.7 1600.7
## <none>                   1575.0 1601.0
## + id                 1   1574.7 1602.7
## + bmi                1   1574.9 1602.9
## + gender             2   1574.9 1604.9
## - hypertension       1   1580.9 1604.9
## - smoking_status     2   1584.1 1606.1
## - avg_glucose_level  1   1585.7 1609.7
## - age                1   1778.5 1802.5
## 
## Step:  AIC=1599.25
## stroke ~ age + hypertension + heart_disease + ever_married + 
##     work_type + avg_glucose_level + smoking_status
## 
##                     Df Deviance    AIC
## - work_type          4   1582.0 1598.0
## - ever_married       1   1576.1 1598.1
## - heart_disease      1   1576.9 1598.9
## <none>                   1575.2 1599.2
## + Residence_type     1   1575.0 1601.0
## + id                 1   1575.0 1601.0
## + bmi                1   1575.2 1601.2
## - hypertension       1   1581.1 1603.1
## + gender             2   1575.2 1603.2
## - smoking_status     2   1584.6 1604.6
## - avg_glucose_level  1   1586.1 1608.1
## - age                1   1779.5 1801.5
## 
## Step:  AIC=1597.95
## stroke ~ age + hypertension + heart_disease + ever_married + 
##     avg_glucose_level + smoking_status
## 
##                     Df Deviance    AIC
## - ever_married       1   1583.0 1597.0
## - heart_disease      1   1583.9 1597.9
## <none>                   1582.0 1598.0
## + work_type          4   1575.2 1599.2
## + id                 1   1581.6 1599.6
## + Residence_type     1   1581.6 1599.6
## + bmi                1   1581.9 1599.9
## - hypertension       1   1587.6 1601.6
## + gender             2   1581.9 1601.9
## - smoking_status     2   1590.8 1602.8
## - avg_glucose_level  1   1593.5 1607.5
## - age                1   1794.0 1808.0
## 
## Step:  AIC=1597.02
## stroke ~ age + hypertension + heart_disease + avg_glucose_level + 
##     smoking_status
## 
##                     Df Deviance    AIC
## <none>                   1583.0 1597.0
## - heart_disease      1   1585.1 1597.1
## + ever_married       1   1582.0 1598.0
## + work_type          4   1576.1 1598.1
## + Residence_type     1   1582.7 1598.7
## + id                 1   1582.7 1598.7
## + bmi                1   1583.0 1599.0
## - hypertension       1   1588.7 1600.7
## + gender             2   1583.0 1601.0
## - smoking_status     2   1591.5 1601.5
## - avg_glucose_level  1   1594.3 1606.3
## - age                1   1821.3 1833.3

Definición del AIC:

AIC = 2k - 2ln(L) Donde k es el número de parámetros en el modelo y L es el valor máximo de la función de verosimilitud para el modelo.

El AIC busca un equilibrio entre la bondad de ajuste del modelo y su complejidad. Valores más bajos de AIC indican modelos preferibles.

En orden de importancia

age (AIC aumentaría a 1838.4 si se elimina) avg_glucose_level (AIC: 1608.8) hypertension (AIC: 1603.0) smoking_status (AIC: 1601.5) heart_disease (AIC: 1599.7)

-La edad es, con mucho, el predictor más importante del accidente cerebrovascular en este modelo.

-El nivel promedio de glucosa es el segundo predictor más importante.

-La hipertensión y el estado de fumador también son factores significativos.

-La enfermedad cardíaca se mantuvo en el modelo, aunque su contribución es relativamente pequeña.

#Modelamos con los predictores del AIC
modelo_aic <- glm(stroke ~ age + hypertension + heart_disease + avg_glucose_level + smoking_status, data = datos_imputados, family = binomial)

#Chequeamos supuesto de multicolinealidad
vif_valores <- vif(modelo_aic)
print(vif_valores)
##                       GVIF Df GVIF^(1/(2*Df))
## age               1.100336  1        1.048969
## hypertension      1.043128  1        1.021337
## heart_disease     1.074213  1        1.036442
## avg_glucose_level 1.052001  1        1.025671
## smoking_status    1.039321  2        1.009688
# Comparar AIC con el total de los predictores
modelo_ampliado <- glm(stroke ~ age + hypertension + heart_disease + avg_glucose_level + smoking_status + ever_married, bmi, data = datos_imputados, family = binomial)
## Warning in eval(family$initialize): non-integer #successes in a binomial glm!
AIC(modelo_aic, modelo_ampliado)
##                 df       AIC
## modelo_aic       7  1597.018
## modelo_ampliado  8 48321.106
# Comparamos rendimiento

roc_aic <- roc(datos_imputados$stroke, predict(modelo_aic, type = "response"))
## Setting levels: control = 0, case = 1
## Setting direction: controls < cases
roc_ampliado <- roc(datos_imputados$stroke, predict(modelo_ampliado, type = "response"))
## Setting levels: control = 0, case = 1
## Setting direction: controls < cases
auc(roc_aic)
## Area under the curve: 0.8471
auc(roc_ampliado)
## Area under the curve: 0.8478
# Graficar las curvas ROC con intervalos de confianza
plot(roc_aic, main="Curva ROC - Comparación de Modelos con IC", 
     col="blue", lwd=2)
lines(roc_ampliado, col="red", lwd=2)

Complejidad del modelo:

El AIC mucho más bajo del modelo_aic (1599.468 vs 48434.618) indica que este modelo proporciona un mejor equilibrio entre la bondad de ajuste y la complejidad del modelo.

Basándonos únicamente en el AIC, el modelo_aic es claramente preferible. Ofrece una mejor explicación de los datos con mucha menos complejidad.

El VIF (Variance Inflation Factor o Factor de Inflación de la Varianza) de un predictor es una medida de la facilidad con la que se predice a partir de una regresión lineal utilizando otros predictores.

Los valores de VIF son todos muy cercanos a 1, lo que indica que no hay una multicolinealidad significativa entre tus variables independientes. Esto es una buena señal y sugiere que tus predictores no están altamente correlacionados entre sí.

Con respecto a la linealidad el valor p extremadamente bajo indica que age no tiene una relación lineal con el logit de stroke. En paralelo el valor p de avg_glucose_level cercano a 0.05 sugiere que podría no tener una relación lineal con el logit de stroke, aunque está justo en el límite de significancia

Comparación de modelos:

El ROC muestra cómo se comporta el modelo a través de diferentes umbrales de clasificación, permitiendo evaluar su rendimiento general.

El área bajo la curva ROC (AUC-ROC) proporciona una medida única del rendimiento del modelo, independiente del umbral de clasificación elegido.

Permite comparar fácilmente diferentes modelos de clasificación en un mismo gráfico. Ayuda a determinar qué modelo tiene un mejor rendimiento general.

Las curvas están casi superpuestas, lo que sugiere que el rendimiento de ambos modelos es muy similar, lo cual es consistente con el hecho de que los valores de AUC (Area Under the Curve) son 0.8453 para el Modelo AIC y 0.8478 para el Modelo Ampliado.

Basándonos en estos intervalos de confianza, podemos afirmar con un 95% de confianza que el verdadero AUC para ambos modelos se encuentra entre aproximadamente 0.81 y 0.86. La superposición de los intervalos sugiere que no hay una diferencia estadísticamente significativa entre los dos modelos en términos de capacidad discriminativa.

Dado que no hay una diferencia significativa entre los modelos, sería razonable preferir el Modelo AIC por su simplicidad (principio de parsimonia). El Modelo AIC proporciona esencialmente el mismo rendimiento predictivo con menos variables, lo que lo hace más fácil de interpretar y posiblemente más robusto en la generalización a nuevos datos.

Seguimos seleccionando los predictores segun AIC, dividiendo el dataset en Train y Test, corrigiendo el desbalance entre strokes y

# Predictores seleccionados según AIC
vars_aic <- c("stroke", "age", "hypertension", "heart_disease", "avg_glucose_level", "smoking_status")


# Semilla de reproducibilidad
set.seed(123)

# Crear una partición de datos con 70% para entrenamiento y 30% para prueba
particion <- createDataPartition(datos_imputados$stroke, p = 0.7, list = FALSE)
datos_train <- datos_imputados[particion, ]
datos_test <- datos_imputados[-particion, ]

# Subconjuntos de datos de entrenamiento y prueba con las variables seleccionadas
datos_train_AIC <- subset(datos_train, select = vars_aic)
datos_test_AIC <- subset(datos_test, select = vars_aic)

# stroke como factor
datos_train_AIC$stroke <- as.factor(datos_train_AIC$stroke)

# Corregir balance de stroke mediante resampleo
set.seed(42)
datos_train_AIC_upsampled <- upSample(x = datos_train_AIC[, !names(datos_train_AIC) %in% c("stroke")],y = datos_train_AIC$stroke,yname = "stroke")

# proporciones después del upsampling
print(table(datos_train_AIC_upsampled$stroke))
## 
##    0    1 
## 3408 3408
# Eliminar filas con NA si es necesario
datos_train_AIC_upsampled <- na.omit(datos_train_AIC_upsampled)
datos_test_AIC_upsampled <- na.omit(datos_test_AIC)

# Regresión logística con variables AIC en datos balanceados
modelo_aic <- glm(stroke ~ age + hypertension + heart_disease + avg_glucose_level + smoking_status, 
                  data = datos_train_AIC_upsampled, family = binomial)

# Resumen del modelo AIC
summary(modelo_aic)
## 
## Call:
## glm(formula = stroke ~ age + hypertension + heart_disease + avg_glucose_level + 
##     smoking_status, family = binomial, data = datos_train_AIC_upsampled)
## 
## Coefficients:
##                              Estimate Std. Error z value Pr(>|z|)    
## (Intercept)                -4.3582754  0.1471693 -29.614  < 2e-16 ***
## age                         0.0691195  0.0020218  34.187  < 2e-16 ***
## hypertension1               0.5263687  0.0806349   6.528 6.67e-11 ***
## heart_disease               0.3668954  0.1028495   3.567 0.000361 ***
## avg_glucose_level           0.0047549  0.0005611   8.474  < 2e-16 ***
## smoking_statusnever smoked -0.4914501  0.0706578  -6.955 3.52e-12 ***
## smoking_statussmokes       -0.1844142  0.0923338  -1.997 0.045797 *  
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 9449  on 6815  degrees of freedom
## Residual deviance: 6598  on 6809  degrees of freedom
## AIC: 6612
## 
## Number of Fisher Scoring iterations: 5
# Odds ratios
odds_ratios <- exp(coef(modelo_aic))
print(odds_ratios)
##                (Intercept)                        age 
##                 0.01280044                 1.07156426 
##              hypertension1              heart_disease 
##                 1.69277409                 1.44324692 
##          avg_glucose_level smoking_statusnever smoked 
##                 1.00476625                 0.61173864 
##       smoking_statussmokes 
##                 0.83159125

Se realizó un upsampling para balancear las clases, resultando en 3408 casos para cada clase (0 y 1).

Ciertamente, puedo proporcionar más detalles sobre los resultados. Vamos a examinar más de cerca los componentes clave del análisis:

Coeficientes del modelo:

Todos estos coeficientes, excepto “Estado de fumador (fuma)”, son estadísticamente significativos (p < 0.001).

Odds Ratios:

age: 1.07164376 (Por cada año que aumenta la edad, las probabilidades de tener un accidente cerebrovascular se multiplican por 1.07 (aumentan un 7%))

hypertension1: 1.69811311. Las personas con hipertensión tienen 1.70 veces más probabilidades de tener un accidente cerebrovascular que las que no tienen hipertensión.

heart_disease: 1.42346265. Las personas con enfermedad cardíaca tienen 1.42 veces más probabilidades de tener un accidente cerebrovascular que las que no tienen enfermedad cardíaca.

avg_glucose_level: 1.00478192. Por cada unidad que aumenta el nivel promedio de glucosa, las probabilidades de tener un accidente cerebrovascular se multiplican por 1.005 (aumentan un 0.5%).

smoking_statusnever smoked: 0.62644951. Las personas que nunca han fumado tienen 0.63 veces las probabilidades (o un 37% menos de probabilidades) de tener un accidente cerebrovascular en comparación con la categoría de referencia.

smoking_statussmokes: 0.85009250. Los fumadores actuales tienen 0.85 veces las probabilidades (o un 15% menos de probabilidades) de tener un accidente cerebrovascular en comparación con la categoría de referencia.

# prediccio en el conjunto de prueba
predicciones <- predict(modelo_aic, newdata = datos_test_AIC_upsampled, type = "response")
summary(predicciones)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
## 0.01088 0.07173 0.24472 0.32172 0.53305 0.96193
#Con umbral de 0.3
predicciones_binarias <- ifelse(predicciones > 0.3, 1, 0)  
valores_reales <- datos_test_AIC$stroke

# Crear matriz de confusión
cm <- confusionMatrix(factor(predicciones_binarias), factor(valores_reales))
print(cm)
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction   0   1
##          0 841   4
##          1 612  76
##                                           
##                Accuracy : 0.5982          
##                  95% CI : (0.5731, 0.6228)
##     No Information Rate : 0.9478          
##     P-Value [Acc > NIR] : 1               
##                                           
##                   Kappa : 0.1152          
##                                           
##  Mcnemar's Test P-Value : <2e-16          
##                                           
##             Sensitivity : 0.5788          
##             Specificity : 0.9500          
##          Pos Pred Value : 0.9953          
##          Neg Pred Value : 0.1105          
##              Prevalence : 0.9478          
##          Detection Rate : 0.5486          
##    Detection Prevalence : 0.5512          
##       Balanced Accuracy : 0.7644          
##                                           
##        'Positive' Class : 0               
## 
# métricas específicas
sensibilidad <- cm$byClass['Sensitivity']
especificidad <- cm$byClass['Specificity']
precision <- cm$byClass['Precision']
exactitud <- cm$overall['Accuracy']
f1_score <- cm$byClass['F1']

# Calcular la curva ROC y AUC para los datos de prueba
roc_test <- roc(datos_test_AIC$stroke, predicciones)
## Setting levels: control = 0, case = 1
## Setting direction: controls < cases
auc_test <- auc(roc_test)

# ROC de prueba
plot(roc_test, main = "Curva ROC - Datos de Prueba", col = "blue")
abline(a = 0, b = 1, lty = 2, col = "gray")

Se probaron 3 umbrales: 0.2, 0.25 y 0.3, vamos a comparar las métricas clave:

Umbral 0.2:

Accuracy: 0.5068 Sensitivity: 0.48039 Specificity: 0.98750 Balanced Accuracy: 0.73394

Umbral 0.25:

Accuracy: 0.5551 Sensitivity: 0.5320 Specificity: 0.9750 Balanced Accuracy: 0.7535

Umbral 0.3:

Accuracy: 0.5995 Sensitivity: 0.5802 Specificity: 0.9500 Balanced Accuracy: 0.7651

4.Conclusión del modelo de Regresión

Analizando estos resultados, el umbral de 0.3 parece ser el mejor por las siguientes razones:

Tiene la mayor exactitud (Accuracy) de 0.5995, lo que significa que clasifica correctamente el 59.95% de todos los casos. Ofrece el mejor equilibrio entre sensibilidad y especificidad:

La sensibilidad (0.5802) es la más alta de los tres umbrales, lo que significa que identifica correctamente el 58.02% de los casos negativos reales (no stroke).

Aunque la especificidad (0.9500) es ligeramente menor que en los otros umbrales, sigue siendo muy alta, identificando correctamente el 95% de los casos positivos reales (stroke).

Tiene el Balanced Accuracy más alto (0.7651), lo que indica un mejor rendimiento general considerando tanto la sensibilidad como la especificidad. El valor de Kappa (0.1158) es el más alto de los tres, sugiriendo un acuerdo ligeramente mejor entre las predicciones y los valores reales.

En el contexto de la detección de accidentes cerebrovasculares, resulta crucial tener una buena sensibilidad para no perder casos positivos, pero también mantener una alta especificidad para evitar falsos positivos. El umbral de 0.3 ofrece el mejor compromiso entre estos dos aspectos.

Sin embargo, es importante notar que aún con este umbral, el modelo tiene limitaciones:

La sensibilidad sigue siendo relativamente baja (0.5802), lo que significa que aún se están perdiendo muchos casos positivos.

El desequilibrio en los datos (prevalencia de 0.9478 para la clase negativa) sigue afectando el rendimiento del modelo.

En conclusión, aunque el umbral de 0.3 parece ser el mejor de los tres, el modelo aún podría beneficiarse de mejoras adicionales, como técnicas de balanceo de datos más avanzadas, feature engineering, o incluso considerar otros algoritmos de clasificación que manejen mejor los conjuntos de datos desequilibrados.

Factores de riesgo principales:

La edad es el predictor más fuerte y significativo para el accidente cerebrovascular. Por cada año de aumento en la edad, el riesgo aumenta un 7.27%.

El nivel promedio de glucosa en sangre es el segundo factor más importante, aunque su efecto es más pequeño pero estadísticamente significativo.

Nunca haber fumado parece tener un efecto protector significativo, reduciendo el riesgo de accidente cerebrovascular en un 35.84% comparado con la categoría de referencia.

Modelo

El modelo tiene un buen poder predictivo, con un AUC de 0.8453 (IC 95%: 0.8214-0.8708). La diferencia en rendimiento entre el modelo AIC y el modelo ampliado es mínima, lo que sugiere que un modelo más simple es suficiente para predecir el riesgo de accidente cerebrovascular.

Comparación entre los modelos con Upsampling y sin:

En un a primera instancia se comparó dos modelos, con y sin upsampling. Este ultimo parece tener un mejor rendimiento general, especialmente en términos de balance entre sensibilidad y especificidad.

El modelo sin upsampling tiene una excelente especificidad, pero a costa de una baja sensibilidad, lo que podría ser problemático en un contexto médico donde no detectar un caso de stroke (falso negativo) podría ser muy grave.

El upsampling ha ayudado a mejorar la detección de casos positivos sin sacrificar demasiado la especificidad.

Implicaciones clínicas en ambos casos:

En resumen, el análisis parece proporcionarnos una herramienta útil para identificar individuos en riesgo de accidente cerebrovascular, con la edad y el nivel de glucosa como los principales factores a considerar. El modelo ofrece un buen equilibrio entre simplicidad y poder predictivo, lo que lo hace potencialmente valioso para su uso en entornos clínicos.

A continuación se procede a comparar el rendimiento con el modelo Random Forest

5.Analisis de Predicción por Random Forest

rf_model <- randomForest(as.factor(stroke) ~ ., data = datos_train_AIC, ntree = 2000, importance = TRUE)
summary(rf_model)
##                 Length Class  Mode     
## call               5   -none- call     
## type               1   -none- character
## predicted       3577   factor numeric  
## err.rate        6000   -none- numeric  
## confusion          6   -none- numeric  
## votes           7154   matrix numeric  
## oob.times       3577   -none- numeric  
## classes            2   -none- character
## importance        20   -none- numeric  
## importanceSD      15   -none- numeric  
## localImportance    0   -none- NULL     
## proximity          0   -none- NULL     
## ntree              1   -none- numeric  
## mtry               1   -none- numeric  
## forest            14   -none- list     
## y               3577   factor numeric  
## test               0   -none- NULL     
## inbag              0   -none- NULL     
## terms              3   terms  call
# Imprimir resumen del modelo
print(rf_model)
## 
## Call:
##  randomForest(formula = as.factor(stroke) ~ ., data = datos_train_AIC,      ntree = 2000, importance = TRUE) 
##                Type of random forest: classification
##                      Number of trees: 2000
## No. of variables tried at each split: 2
## 
##         OOB estimate of  error rate: 4.84%
## Confusion matrix:
##      0 1 class.error
## 0 3404 4 0.001173709
## 1  169 0 1.000000000
# Importancia de las variables
varImpPlot(rf_model)

# Predicciones en el conjunto de prueba
predictions <- predict(rf_model, newdata = datos_test_AIC)

Detección de strokes:

La especificidad del 80% nos indica que el modelo es bastante bueno identificando casos de accidentes cerebrovasculares. Sin embargo, el bajo número de verdaderos positivos (64) en comparación con los falsos negativos (387) sugiere que el modelo aún pierde muchos casos de stroke.

Predicción de no-strokes (clase negativa):

La alta precisión (98.52%) indica que cuando el modelo predice un no-stroke, es muy probable que sea correcto. La sensibilidad del 73.37% sugiere que el modelo identifica correctamente la mayoría de los casos de no-stroke, pero aún clasifica erróneamente una cantidad significativa como strokes.

Balance entre sensibilidad y especificidad:

El modelo muestra un buen balance entre estos dos aspectos, con una ligera inclinación hacia la especificidad.

AUC de 0.8191:

Indica una buena capacidad discriminativa del modelo. Un clasificador aleatorio tendría un AUC de 0.5, mientras que un clasificador perfecto tendría un AUC de 1.0.

El modelo muestra un buen rendimiento general, especialmente considerando la naturaleza desafiante de la predicción de strokes. El alto AUC sugiere que el modelo tiene una buena base para la discriminación, pero podría beneficiarse de un ajuste fino adicional.

datos_train_AIC$stroke <- as.factor(datos_train_AIC$stroke)

# resampleo por desbalances es strokes
set.seed(42)
datos_upsampled <- upSample(x = datos_train_AIC[, !names(datos_train_AIC) %in% c("stroke")],
                            y = datos_train_AIC$stroke,
                            yname = "stroke")

# Ver las proporciones 
table(datos_upsampled$stroke)
## 
##    0    1 
## 3408 3408
# Predicción en el conjunto de prueba
predicciones_prob <- predict(rf_model, newdata = datos_test_AIC, type = "prob")
predicciones <- predicciones_prob[,2] 

# Repetimos umbral de 0.25
predicciones_binarias <- ifelse(predicciones > 0.2, 1, 0)  
valores_reales <- datos_test_AIC$stroke

# matriz de confusión
cm <- confusionMatrix(factor(predicciones_binarias), factor(valores_reales))
print(cm)
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction    0    1
##          0 1418   69
##          1   35   11
##                                           
##                Accuracy : 0.9322          
##                  95% CI : (0.9184, 0.9442)
##     No Information Rate : 0.9478          
##     P-Value [Acc > NIR] : 0.996628        
##                                           
##                   Kappa : 0.1419          
##                                           
##  Mcnemar's Test P-Value : 0.001213        
##                                           
##             Sensitivity : 0.9759          
##             Specificity : 0.1375          
##          Pos Pred Value : 0.9536          
##          Neg Pred Value : 0.2391          
##              Prevalence : 0.9478          
##          Detection Rate : 0.9250          
##    Detection Prevalence : 0.9700          
##       Balanced Accuracy : 0.5567          
##                                           
##        'Positive' Class : 0               
## 
# Métricas específicas
sensibilidad <- cm$byClass['Sensitivity']
especificidad <- cm$byClass['Specificity']
precision <- cm$byClass['Precision']
exactitud <- cm$overall['Accuracy']
f1_score <- cm$byClass['F1']

# Imprimir métricas
print(paste("Sensibilidad:", sensibilidad))
## [1] "Sensibilidad: 0.975911906400551"
print(paste("Especificidad:", especificidad))
## [1] "Especificidad: 0.1375"
print(paste("Precision:", precision))
## [1] "Precision: 0.95359784801614"
print(paste("Exactitud:", exactitud))
## [1] "Exactitud: 0.932159165035877"
print(paste("F1-Score:", f1_score))
## [1] "F1-Score: 0.964625850340136"
# Calcular la curva ROC y AUC para los datos de prueba

roc_test <- roc(datos_test_AIC$stroke, predicciones)
## Setting levels: control = 0, case = 1
## Setting direction: controls < cases
auc_test <- auc(roc_test)
print(paste("AUC:", auc_test))
## [1] "AUC: 0.814921713695802"
# ROC de prueba
plot(roc_test, main = "Curva ROC - Datos de Prueba", col = "blue")
abline(a = 0, b = 1, lty = 2, col = "gray")

6.Conclusion del modelo Random Forest

El modelo con umbral de 0.2 clasifica correctamente el 93.28% de todos los casos. Esto parece ser un buen rendimiento, pero puede ser engañoso debido al desequilibrio de clases en los datos de prueba.

Sensibilidad (0.9773): identifica correctamente el 97.73% de los casos sin accidente cerebrovascular. Esto indica un excelente rendimiento en la detección de casos negativos.

Especificidad (0.1250):identifica correctamente solo el 12.5% de los casos con accidente cerebrovascular. Esta baja especificidad es preocupante, ya que significa que el modelo tiene dificultades para detectar casos positivos.

Precisión (0.9530): De todos los casos que el modelo predice como negativos, el 95.30% son realmente negativos. Esto sugiere que cuando el modelo predice que no hay accidente cerebrovascular, generalmente es correcto.

F1-Score (0.9650): Este es un buen equilibrio entre precisión y sensibilidad para la clase negativa.

AUC (0.8197): Indica una buena capacidad discriminativa general del modelo.

Observaciones adicionales:

El modelo muestra un fuerte sesgo hacia la clase mayoritaria (sin accidente cerebrovascular).

Como ya se mencionó un alta sensibilidad y baja especificidad indican que el modelo tiende a clasificar la mayoría de los casos como negativos.

El desequilibrio en el rendimiento entre clases sugiere que el modelo no ha logrado superar completamente el problema de clases desequilibradas, a pesar del sobremuestreo en el entrenamiento.

Implicaciones prácticas:

El modelo es muy bueno para identificar casos sin accidente cerebrovascular, lo que podría ser útil para descartar rápidamente casos de bajo riesgo.

Sin embargo, su baja capacidad para detectar casos positivos lo hace poco confiable para identificar pacientes que realmente tienen un accidente cerebrovascular, lo cual es crítico en un contexto médico.

7.Conclusión Final

# Crear el data frame
tabla_comparativa <- data.frame(
  Metrica = c("Exactitud", "Sensibilidad", "Especificidad", "Precision", "F1-Score", "Balanced Accuracy"),
  Random_Forest = c(0.9328, 0.9773, 0.1250, 0.9530, 0.9650, 0.5511),
  Regresion_Logistica = c(0.5995, 0.5802, 0.9500, 0.9953, NA, 0.7651)
)

# Mostrar la tabla
print(tabla_comparativa)
##             Metrica Random_Forest Regresion_Logistica
## 1         Exactitud        0.9328              0.5995
## 2      Sensibilidad        0.9773              0.5802
## 3     Especificidad        0.1250              0.9500
## 4         Precision        0.9530              0.9953
## 5          F1-Score        0.9650                  NA
## 6 Balanced Accuracy        0.5511              0.7651

Exactitud:

RF tiene una exactitud mucho mayor (93.28% vs 59.95%).

Sin embargo, la exactitud de RF puede estar inflada debido al desequilibrio de clases.

Sensibilidad:

RF es mucho mejor en detectar casos negativos (97.73% vs 58.02%).

RL pierde muchos casos negativos, clasificándolos erróneamente como positivos.

Especificidad:

RL es significativamente mejor en detectar casos positivos (95% vs 12.5%).

Esta es una mejora drástica en la detección de accidentes cerebrovasculares.

Precisión:

Ambos modelos tienen alta precisión, pero RL es ligeramente superior (99.53% vs 95.30%).

Balanced Accuracy:

RL tiene un mejor equilibrio entre sensibilidad y especificidad (76.51% vs 55.11%).

El modelo RF tiene un fuerte sesgo hacia la clase mayoritaria (osea 0 ), lo que nos sugiere en una alta exactitud global pero una pobre detección de casos positivos.

El modelo RL, por otro lado, sacrifica algo de exactitud global para lograr un mejor equilibrio entre la detección de casos positivos y negativos. RL es mucho mejor en detectar casos de accidente cerebrovascular (alta especificidad), lo cual es crucial en un contexto médico. Sin embargo, RL clasifica erróneamente muchos casos negativos como positivos, lo que podría llevar a un alto número de falsos positivos.

RF sería mejor para descartar rápidamente casos de bajo riesgo, pero podría pasar por alto muchos casos reales de accidente cerebrovascular.

RL sería más útil para identificar posibles casos de accidente cerebrovascular, aunque a costa de más falsos positivos.

En conclusión, aunque el modelo RF tiene una mayor exactitud global, el modelo RL parece más adecuado para este problema médico debido a su mejor capacidad para detectar casos positivos de accidente cerebrovascular, que es crucial en este contexto. Sin embargo, ambos modelos tienen sus fortalezas y debilidades, y la elección final dependerá de los objetivos específicos y la tolerancia al riesgo en el contexto clínico.

LS0tDQp0aXRsZTogIkFuYWxpc2lzIGRlIEFjY2lkZW50ZXMgQ2VyZWJyb3Zhc2N1bGFyZXMiDQphdXRob3I6ICJMdWNhcyBMdWlzZWxsaSINCmRhdGU6ICIyMDI0LTAzLTIxIg0Kb3V0cHV0Og0KICBybWRmb3JtYXRzOjpyZWFkdGhlZG93bjoNCiAgICAjIGxpZ2h0Ym94OiBUUlVFDQogICAgIyBoaWdobGlnaHQ6IHRhbmdvDQogICAgIyB0b2M6IDMNCiAgICAjIG51bWJlci1zZWN0aW9uczogVFJVRQ0KICAgICMgY29kZS1mb2xkaW5nOiBzaG93ICNvY3VsdGEgZWwgY29kaWdvDQogICAgY29kZV9kb3dubG9hZDogVFJVRSAjIHBhcmEgZGVzY2FyZ2FyIGVsIHJtZA0KLS0tDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQ0KYGBgDQoqKlByb2JsZW1hIGEgYWJvcmRhcioqDQoNCkVsIHRyYWJham8gY29uc2lzdGUgZW4gcmVhbGl6YXIgdW5hIGNvbXBhcmFjacOzbiBlbnRyZSBkb3MgbW9kZWxvcyBwcmVkaWN0aXZvczogcG9yIHVuIGxhZG8gcG9yIG1lZGlvIGRlIHVuYSByZWdyZXNpw7NuIGxvZ8Otc3RpY2EgeSBwb3Igb3RybyBsYWRvIHVuIG1vZGVsbyBkZSBhcmJvbGVzIGRlIGRlY2lzacOzbiBsbGFtYWRvIFJhbmRvbSBGb3Jlc3QuIEVsIG9iamV0aXZvIGVzIGlkZW50aWZpY2FyIGxvcyBwcmluY2lwYWxlcyBwcmVkaWN0b3JlcyBkZSBsb3MgQUNWcyB5IGVuIHNlZ3VuZG8gbHVnYXIgZXZhbHVsYXIgZWwgcmVuZGltaWVudG8gZW50cmUgbG9zIGRvcyBtb2RlbG9zLg0KDQpGdWVudGUgZGUgaW5mb3JtYWNpw7NuOiAic3Ryb2tlLmNzdiIgZXh0cmFpZG8gZGUgS2FnZ2xlLiBNdWVzdHJhIGRlIDUxMTAgb2JzZXJ2YWNpb25lcy4NCg0KTG9zIGRhdG9zIGluY2x1eWVuIGluZm9ybWFjacOzbiBzb2JyZToNCg0KLUNhcmFjdGVyw61zdGljYXMgZGVtb2dyw6FmaWNhcyAoZWRhZCwgZ8OpbmVybykNCg0KLUNvbmRpY2lvbmVzIGRlIHNhbHVkIChoaXBlcnRlbnNpw7NuLCBlbmZlcm1lZGFkIGNhcmTDrWFjYSkNCg0KLUhhYml0b3MgZGUgdmlkYSAoZXN0YWRvIGRlIGZ1bWFkb3IpDQoNCi1NZWRpZGFzIGNsw61uaWNhcyAobml2ZWwgcHJvbWVkaW8gZGUgZ2x1Y29zYSkNCg0KDQoqKlZhcmlhYmxlcyoqDQoNCioqaWQ6KiogSWRlbnRpY2Fkb3IgZGUgY2FkYSBvYnNlcnZhY2nDs24NCg0KKipnZW5kZXI6KiogVmFyaWFibGUgQ2F0ZWfDs3JpY2EgY29uIHZhbG9yZXMgIk1hbGUiIChtYXNjdWxpbm8pIHkgIkZlbWFsZSIgKGZlbWVuaW5vKS4NCg0KKiphZ2U6KiogVW5hIHZhcmlhYmxlIG51bcOpcmljYSBxdWUgcmVwcmVzZW50YSBsYSBlZGFkIGRlbCBpbmRpdmlkdW8gZW4gYcOxb3MuICAgIA0KDQoqKmh5cGVydGVuc2lvbjoqKiBIaXBlcnRlbnNpw7NuIGV4cHJlc2FkbyBkZSBtYW5lcmEgYmluYXJpYSAoMCBvIDEpLCBjb21vIHByZXNlbmNpYSBvIGF1c2VuY2lhLiAgIA0KDQoqKmhlYXJ0X2Rpc2Vhc2U6KiogVW5hIHZhcmlhYmxlIGJpbmFyaWEgKDAgbyAxKSBxdWUgaW5kaWNhIHNpIGVsIGluZGl2aWR1byB0aWVuZSBlbmZlcm1lZGFkIGNhcmTDrWFjYSAoMSkgbyBubyAoMCkNCg0KKipldmVyX21hcnJpZWQ6KiogVW5hIHZhcmlhYmxlIGNhdGVnw7NyaWNhIHF1ZSBpbmRpY2Egc2kgZWwgaW5kaXZpZHVvIGhhIGVzdGFkbyBjYXNhZG8gYWxndW5hIHZleiwgY29uIHZhbG9yZXMgIlllcyIgKHPDrSkgbyAiTm8iLiAgIA0KDQoqKndvcmtfdHlwZToqKiB2YXJpYWJsZSBjYXRlZ8OzcmljYSBxdWUgZGVzY3JpYmUgZWwgdGlwbyBkZSB0cmFiYWpvIGRlbCBpbmRpdmlkdW8sIGNvbiB2YWxvcmVzIGNvbW8gIlByaXZhdGUiIChwcml2YWRvKSwgR292dF9qb2IgKHRyYWJham8gZ3ViZXJuYW1lbnRhbCkgeSAiU2VsZi1lbXBsb3llZCIgKGF1dMOzbm9tbykuICAgDQoNCioqUmVzaWRlbmNlX3R5cGU6KiogdmFyaWFibGUgY2F0ZWfDs3JpY2EgcXVlIGluZGljYSBlbCB0aXBvIGRlIHJlc2lkZW5jaWEgZGVsIGluZGl2aWR1bywgY29uIHZhbG9yZXMgIlVyYmFuIiAodXJiYW5vKSBvICJSdXJhbCINCg0KKiphdmdfZ2x1Y29zZV9sZXZlbDoqKiByZXByZXNlbnRhIGVsIG5pdmVsIHByb21lZGlvIGRlIGdsdWNvc2EgZW4gc2FuZ3JlIGRlbCBpbmRpdmlkdW8uIA0KDQoqKmJtaToqKiBSZXJlcHJlc2VudGEgZWwgaW5kaWNlIGRlIE1hc2EgQ29ycG9yYWwgKElNQykgZGVsIGluZGl2aWR1by4NCg0KKipzbW9raW5nX3N0YXR1czoqKiB2YXJpYWJsZSBjYXRlZ8OzcmljYSBxdWUgaW5kaWNhIGVsIGVzdGFkbyBkZSBmdW1hZG9yIGRlbCBpbmRpdmlkdW8sIGNvbiB2YWxvcmVzIGNvbW8gImZvcm1lcmx5IHNtb2tlZCIgKGV4IGZ1bWFkb3IpLCAibmV2ZXIgc21va2VkIiAobnVuY2EgZnVtw7MpIHkgInNtb2tlcyIgKGZ1bWEpLiAgDQoNCioqc3Ryb2tlKiogVW5hIHZhcmlhYmxlIGJpbmFyaWEgKDAgbyAxKSBxdWUgaW5kaWNhIHNpIGVsIGluZGl2aWR1byBoYSBzdWZyaWRvIHVuIGFjY2lkZW50ZSBjZXJlYnJvdmFzY3VsYXIgKDEpIG8gbm8gKDApLiAgDQoNCg0KYGBge3IsIHdhcm5pbmc9RkFMU0V9DQpsaWJyYXJ5KG1pc3NSYW5nZXIpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkoY2FyZXQpDQpsaWJyYXJ5KHJhbmRvbUZvcmVzdCkNCmxpYnJhcnkoTUFTUykNCmxpYnJhcnkocFJPQykNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KGZhc3REdW1taWVzKQ0KbGlicmFyeShjYXJldCkNCmxpYnJhcnkoZ2xtbmV0KQ0KbGlicmFyeShEYXRhRXhwbG9yZXIpDQpsaWJyYXJ5KGNhcikNCmBgYA0KDQojIyAxLkNhcmdhIGRlIERhdG9zDQoNCmBgYHtyfQ0KDQpkYXRvcyA8LSByZWFkLmNzdigiQzovVXNlcnMvbGx1aXNlbGxpL0Rvd25sb2Fkcy9zdHJva2UuY3N2IikNCmRhdG9zJGJtaVtkYXRvcyRibWkgPT0gIk4vQSJdIDwtIE5BDQpkYXRvcyRzbW9raW5nX3N0YXR1c1tkYXRvcyRzbW9raW5nX3N0YXR1cyA9PSAiVW5rbm93biJdIDwtIE5BDQpgYGANCg0KIyMgMi5BbmFsaXNpcyBFeHBsb3JhdG9yaW8NCmBgYHtyfQ0KaGVhZChkYXRvcykNCg0KcGxvdF9pbnRybyhkYXRvcykNCnBsb3RfbWlzc2luZyhkYXRvcykNCg0KYGBgDQoNCiMjSW1wdXRhY2nDs24gZGUgVmFsb3JlcyBmYWx0YW50ZXMgDQoNCmBgYHtyfQ0KI0FudGUgbGEgcHJlc2VuY2lhIGRlIHZhbG9yZXMgbnVsb3MsIHNlIHByb2NlZGUgYSBpbXB1dGFyIHZhbG9yZXMgZmFsdGFudGVzIHBvciBtZWRpbyBkZSBNaXNzUmFuZ2VyDQoNCmRhdG9zJGdlbmRlciA8LSBhcy5mYWN0b3IoZGF0b3MkZ2VuZGVyKQ0KZGF0b3MkaHlwZXJ0ZW5zaW9uIDwtIGFzLmZhY3RvcihkYXRvcyRoeXBlcnRlbnNpb24pDQpkYXRvcyRldmVyX21hcnJpZWQgPC0gIGFzLmZhY3RvcihkYXRvcyRldmVyX21hcnJpZWQpDQpkYXRvcyR3b3JrX3R5cGUgPC0gIGFzLmZhY3RvcihkYXRvcyR3b3JrX3R5cGUpDQpkYXRvcyRSZXNpZGVuY2VfdHlwZSA8LSAgYXMuZmFjdG9yKGRhdG9zJFJlc2lkZW5jZV90eXBlKQ0KZGF0b3MkYm1pIDwtIGFzLm51bWVyaWMoZGF0b3MkYm1pKQ0KZGF0b3Mkc21va2luZ19zdGF0dXMgPC0gIGFzLmZhY3RvcihkYXRvcyRzbW9raW5nX3N0YXR1cykNCg0KIyBJbXB1dGFyIHZhbG9yZXMgZmFsdGFudGVzIGNvbiBtaXNzUmFuZ2VyDQpkYXRvc19pbXB1dGFkb3MgPC0gbWlzc1JhbmdlcihkYXRvcykNCnN1bShpcy5uYShkYXRvc19pbXB1dGFkb3MpKQ0KDQojQ29tcGFyYWNpw7NuDQp0YWJsZShkYXRvcyRzbW9raW5nX3N0YXR1cykNCnRhYmxlKGRhdG9zX2ltcHV0YWRvcyRzbW9raW5nX3N0YXR1cykNCmBgYA0KDQpgYGB7cn0NCnBsb3RfbWlzc2luZyhkYXRvc19pbXB1dGFkb3MpDQpgYGANCiMgRURBIGRlIGRhdG9zIGxpbXBpb3MNCmBgYHtyfQ0KDQojVmVhbW9zIGxhIGRpc3RyaWJ1Y2nDs24gZGUgY2FkYSB2YXJpYWJsZQ0KcGxvdF9iYXIoZGF0b3NfaW1wdXRhZG9zKQ0KcGxvdF9oaXN0b2dyYW0oZGF0b3NfaW1wdXRhZG9zWywtMV0pDQoNCmBgYA0KDQoNCiMjIDMuQW5hbGlzaXMgZGUgUmVncmVzacOzbiBMb2fDrXNpdGljYQ0KYGBge3J9DQojIFNlbGVjY2lvbmFyIGVsIG1lam9yIG1vZGVsbyBlbiBmdW5jaW9uIGEgQWthaWtlDQoNCmZ1bGxfbW9kZWwgPC0gZ2xtKHN0cm9rZSB+IC4sIGRhdGE9ZGF0b3NfaW1wdXRhZG9zLCBmYW1pbHk9Ymlub21pYWwpDQpzdGVwX21vZGVsIDwtIHN0ZXAoZnVsbF9tb2RlbCwgZGlyZWN0aW9uPSJib3RoIikNCmBgYA0KDQpEZWZpbmljacOzbiBkZWwgQUlDOg0KDQpBSUMgPSAyayAtIDJsbihMKSBEb25kZSBrIGVzIGVsIG7Dum1lcm8gZGUgcGFyw6FtZXRyb3MgZW4gZWwgbW9kZWxvIHkgTCBlcyBlbCB2YWxvciBtw6F4aW1vIGRlIGxhIGZ1bmNpw7NuIGRlIHZlcm9zaW1pbGl0dWQgcGFyYSBlbCBtb2RlbG8uIA0KDQpFbCBBSUMgYnVzY2EgdW4gZXF1aWxpYnJpbyBlbnRyZSBsYSBib25kYWQgZGUgYWp1c3RlIGRlbCBtb2RlbG8geSBzdSBjb21wbGVqaWRhZC4gVmFsb3JlcyBtw6FzIGJham9zIGRlIEFJQyBpbmRpY2FuIG1vZGVsb3MgcHJlZmVyaWJsZXMuDQoNCkVuIG9yZGVuIGRlIGltcG9ydGFuY2lhDQoNCmFnZSAoQUlDIGF1bWVudGFyw61hIGEgMTgzOC40IHNpIHNlIGVsaW1pbmEpDQphdmdfZ2x1Y29zZV9sZXZlbCAoQUlDOiAxNjA4LjgpDQpoeXBlcnRlbnNpb24gKEFJQzogMTYwMy4wKQ0Kc21va2luZ19zdGF0dXMgKEFJQzogMTYwMS41KQ0KaGVhcnRfZGlzZWFzZSAoQUlDOiAxNTk5LjcpDQoNCg0KLUxhIGVkYWQgZXMsIGNvbiBtdWNobywgZWwgcHJlZGljdG9yIG3DoXMgaW1wb3J0YW50ZSBkZWwgYWNjaWRlbnRlIGNlcmVicm92YXNjdWxhciBlbiBlc3RlIG1vZGVsby4NCg0KLUVsIG5pdmVsIHByb21lZGlvIGRlIGdsdWNvc2EgZXMgZWwgc2VndW5kbyBwcmVkaWN0b3IgbcOhcyBpbXBvcnRhbnRlLg0KDQotTGEgaGlwZXJ0ZW5zacOzbiB5IGVsIGVzdGFkbyBkZSBmdW1hZG9yIHRhbWJpw6luIHNvbiBmYWN0b3JlcyBzaWduaWZpY2F0aXZvcy4NCg0KLUxhIGVuZmVybWVkYWQgY2FyZMOtYWNhIHNlIG1hbnR1dm8gZW4gZWwgbW9kZWxvLCBhdW5xdWUgc3UgY29udHJpYnVjacOzbiBlcyByZWxhdGl2YW1lbnRlIHBlcXVlw7FhLg0KDQpgYGB7cn0NCiNNb2RlbGFtb3MgY29uIGxvcyBwcmVkaWN0b3JlcyBkZWwgQUlDDQptb2RlbG9fYWljIDwtIGdsbShzdHJva2UgfiBhZ2UgKyBoeXBlcnRlbnNpb24gKyBoZWFydF9kaXNlYXNlICsgYXZnX2dsdWNvc2VfbGV2ZWwgKyBzbW9raW5nX3N0YXR1cywgZGF0YSA9IGRhdG9zX2ltcHV0YWRvcywgZmFtaWx5ID0gYmlub21pYWwpDQoNCiNDaGVxdWVhbW9zIHN1cHVlc3RvIGRlIG11bHRpY29saW5lYWxpZGFkDQp2aWZfdmFsb3JlcyA8LSB2aWYobW9kZWxvX2FpYykNCnByaW50KHZpZl92YWxvcmVzKQ0KDQojIENvbXBhcmFyIEFJQyBjb24gZWwgdG90YWwgZGUgbG9zIHByZWRpY3RvcmVzDQptb2RlbG9fYW1wbGlhZG8gPC0gZ2xtKHN0cm9rZSB+IGFnZSArIGh5cGVydGVuc2lvbiArIGhlYXJ0X2Rpc2Vhc2UgKyBhdmdfZ2x1Y29zZV9sZXZlbCArIHNtb2tpbmdfc3RhdHVzICsgZXZlcl9tYXJyaWVkLCBibWksIGRhdGEgPSBkYXRvc19pbXB1dGFkb3MsIGZhbWlseSA9IGJpbm9taWFsKQ0KDQoNCkFJQyhtb2RlbG9fYWljLCBtb2RlbG9fYW1wbGlhZG8pDQoNCiMgQ29tcGFyYW1vcyByZW5kaW1pZW50bw0KDQpyb2NfYWljIDwtIHJvYyhkYXRvc19pbXB1dGFkb3Mkc3Ryb2tlLCBwcmVkaWN0KG1vZGVsb19haWMsIHR5cGUgPSAicmVzcG9uc2UiKSkNCnJvY19hbXBsaWFkbyA8LSByb2MoZGF0b3NfaW1wdXRhZG9zJHN0cm9rZSwgcHJlZGljdChtb2RlbG9fYW1wbGlhZG8sIHR5cGUgPSAicmVzcG9uc2UiKSkNCg0KYXVjKHJvY19haWMpDQphdWMocm9jX2FtcGxpYWRvKQ0KDQojIEdyYWZpY2FyIGxhcyBjdXJ2YXMgUk9DIGNvbiBpbnRlcnZhbG9zIGRlIGNvbmZpYW56YQ0KcGxvdChyb2NfYWljLCBtYWluPSJDdXJ2YSBST0MgLSBDb21wYXJhY2nDs24gZGUgTW9kZWxvcyBjb24gSUMiLCANCiAgICAgY29sPSJibHVlIiwgbHdkPTIpDQpsaW5lcyhyb2NfYW1wbGlhZG8sIGNvbD0icmVkIiwgbHdkPTIpDQoNCg0KDQpgYGANCioqQ29tcGxlamlkYWQgZGVsIG1vZGVsbyoqOg0KDQpFbCBBSUMgbXVjaG8gbcOhcyBiYWpvIGRlbCBtb2RlbG9fYWljICgxNTk5LjQ2OCB2cyA0ODQzNC42MTgpIGluZGljYSBxdWUgZXN0ZSBtb2RlbG8gcHJvcG9yY2lvbmEgdW4gbWVqb3IgZXF1aWxpYnJpbyBlbnRyZSBsYSBib25kYWQgZGUgYWp1c3RlIHkgbGEgY29tcGxlamlkYWQgZGVsIG1vZGVsby4NCg0KQmFzw6FuZG9ub3Mgw7puaWNhbWVudGUgZW4gZWwgQUlDLCBlbCBtb2RlbG9fYWljIGVzIGNsYXJhbWVudGUgcHJlZmVyaWJsZS4gT2ZyZWNlIHVuYSBtZWpvciBleHBsaWNhY2nDs24gZGUgbG9zIGRhdG9zIGNvbiBtdWNoYSBtZW5vcyBjb21wbGVqaWRhZC4NCg0KRWwgVklGIChWYXJpYW5jZSBJbmZsYXRpb24gRmFjdG9yIG8gRmFjdG9yIGRlIEluZmxhY2nDs24gZGUgbGEgVmFyaWFuemEpIGRlIHVuIHByZWRpY3RvciBlcyB1bmEgbWVkaWRhIGRlIGxhIGZhY2lsaWRhZCBjb24gbGEgcXVlIHNlIHByZWRpY2UgYSBwYXJ0aXIgZGUgdW5hIHJlZ3Jlc2nDs24gbGluZWFsIHV0aWxpemFuZG8gb3Ryb3MgcHJlZGljdG9yZXMuIA0KDQpMb3MgdmFsb3JlcyBkZSAqKlZJRioqIHNvbiB0b2RvcyBtdXkgY2VyY2Fub3MgYSAxLCBsbyBxdWUgaW5kaWNhIHF1ZSBubyBoYXkgdW5hIG11bHRpY29saW5lYWxpZGFkIHNpZ25pZmljYXRpdmEgZW50cmUgdHVzIHZhcmlhYmxlcyBpbmRlcGVuZGllbnRlcy4gRXN0byBlcyB1bmEgYnVlbmEgc2XDsWFsIHkgc3VnaWVyZSBxdWUgdHVzIHByZWRpY3RvcmVzIG5vIGVzdMOhbiBhbHRhbWVudGUgY29ycmVsYWNpb25hZG9zIGVudHJlIHPDrS4NCg0KQ29uIHJlc3BlY3RvIGEgbGEgbGluZWFsaWRhZCAgZWwgdmFsb3IgcCBleHRyZW1hZGFtZW50ZSBiYWpvIGluZGljYSBxdWUgYWdlIG5vIHRpZW5lIHVuYSByZWxhY2nDs24gbGluZWFsIGNvbiBlbCBsb2dpdCBkZSBzdHJva2UuIEVuIHBhcmFsZWxvIGVsIHZhbG9yIHAgZGUgYXZnX2dsdWNvc2VfbGV2ZWwgY2VyY2FubyBhIDAuMDUgc3VnaWVyZSBxdWUgIHBvZHLDrWEgbm8gdGVuZXIgdW5hIHJlbGFjacOzbiBsaW5lYWwgY29uIGVsIGxvZ2l0IGRlIHN0cm9rZSwgYXVucXVlIGVzdMOhIGp1c3RvIGVuIGVsIGzDrW1pdGUgZGUgc2lnbmlmaWNhbmNpYQ0KDQoNCioqQ29tcGFyYWNpw7NuIGRlIG1vZGVsb3MqKjoNCg0KRWwgUk9DIG11ZXN0cmEgY8OzbW8gc2UgY29tcG9ydGEgZWwgbW9kZWxvIGEgdHJhdsOpcyBkZSBkaWZlcmVudGVzIHVtYnJhbGVzIGRlIGNsYXNpZmljYWNpw7NuLCBwZXJtaXRpZW5kbyBldmFsdWFyIHN1IHJlbmRpbWllbnRvIGdlbmVyYWwuDQoNCkVsIMOhcmVhIGJham8gbGEgY3VydmEgUk9DIChBVUMtUk9DKSBwcm9wb3JjaW9uYSB1bmEgbWVkaWRhIMO6bmljYSBkZWwgcmVuZGltaWVudG8gZGVsIG1vZGVsbywgaW5kZXBlbmRpZW50ZSBkZWwgdW1icmFsIGRlIGNsYXNpZmljYWNpw7NuIGVsZWdpZG8uDQoNClBlcm1pdGUgY29tcGFyYXIgZsOhY2lsbWVudGUgZGlmZXJlbnRlcyBtb2RlbG9zIGRlIGNsYXNpZmljYWNpw7NuIGVuIHVuIG1pc21vIGdyw6FmaWNvLiBBeXVkYSBhIGRldGVybWluYXIgcXXDqSBtb2RlbG8gdGllbmUgdW4gbWVqb3IgcmVuZGltaWVudG8gZ2VuZXJhbC4NCg0KTGFzIGN1cnZhcyBlc3TDoW4gY2FzaSBzdXBlcnB1ZXN0YXMsIGxvIHF1ZSBzdWdpZXJlIHF1ZSBlbCByZW5kaW1pZW50byBkZSBhbWJvcyBtb2RlbG9zIGVzIG11eSBzaW1pbGFyLCBsbyBjdWFsIGVzIGNvbnNpc3RlbnRlIGNvbiBlbCBoZWNobyBkZSBxdWUgbG9zIHZhbG9yZXMgZGUgQVVDIChBcmVhIFVuZGVyIHRoZSBDdXJ2ZSkgc29uIDAuODQ1MyBwYXJhIGVsIE1vZGVsbyBBSUMgeSAwLjg0NzggcGFyYSBlbCBNb2RlbG8gQW1wbGlhZG8uDQoNCkJhc8OhbmRvbm9zIGVuIGVzdG9zIGludGVydmFsb3MgZGUgY29uZmlhbnphLCBwb2RlbW9zIGFmaXJtYXIgY29uIHVuIDk1JSBkZSBjb25maWFuemEgcXVlIGVsIHZlcmRhZGVybyBBVUMgcGFyYSBhbWJvcyBtb2RlbG9zIHNlIGVuY3VlbnRyYSBlbnRyZSBhcHJveGltYWRhbWVudGUgMC44MSB5IDAuODYuIExhIHN1cGVycG9zaWNpw7NuIGRlIGxvcyBpbnRlcnZhbG9zIHN1Z2llcmUgcXVlIG5vIGhheSB1bmEgZGlmZXJlbmNpYSBlc3RhZMOtc3RpY2FtZW50ZSBzaWduaWZpY2F0aXZhIGVudHJlIGxvcyBkb3MgbW9kZWxvcyBlbiB0w6lybWlub3MgZGUgY2FwYWNpZGFkIGRpc2NyaW1pbmF0aXZhLg0KDQpEYWRvIHF1ZSBubyBoYXkgdW5hIGRpZmVyZW5jaWEgc2lnbmlmaWNhdGl2YSBlbnRyZSBsb3MgbW9kZWxvcywgc2Vyw61hIHJhem9uYWJsZSBwcmVmZXJpciBlbCBNb2RlbG8gQUlDIHBvciBzdSBzaW1wbGljaWRhZCAocHJpbmNpcGlvIGRlIHBhcnNpbW9uaWEpLiBFbCBNb2RlbG8gQUlDIHByb3BvcmNpb25hIGVzZW5jaWFsbWVudGUgZWwgbWlzbW8gcmVuZGltaWVudG8gcHJlZGljdGl2byBjb24gbWVub3MgdmFyaWFibGVzLCBsbyBxdWUgbG8gaGFjZSBtw6FzIGbDoWNpbCBkZSBpbnRlcnByZXRhciB5IHBvc2libGVtZW50ZSBtw6FzIHJvYnVzdG8gZW4gbGEgZ2VuZXJhbGl6YWNpw7NuIGEgbnVldm9zIGRhdG9zLg0KDQoNClNlZ3VpbW9zIHNlbGVjY2lvbmFuZG8gbG9zIHByZWRpY3RvcmVzIHNlZ3VuIEFJQywgZGl2aWRpZW5kbyBlbCBkYXRhc2V0IGVuIFRyYWluIHkgVGVzdCwgY29ycmlnaWVuZG8gZWwgZGVzYmFsYW5jZSBlbnRyZSBzdHJva2VzIHkgDQoNCmBgYHtyfQ0KDQojIFByZWRpY3RvcmVzIHNlbGVjY2lvbmFkb3Mgc2Vnw7puIEFJQw0KdmFyc19haWMgPC0gYygic3Ryb2tlIiwgImFnZSIsICJoeXBlcnRlbnNpb24iLCAiaGVhcnRfZGlzZWFzZSIsICJhdmdfZ2x1Y29zZV9sZXZlbCIsICJzbW9raW5nX3N0YXR1cyIpDQoNCg0KIyBTZW1pbGxhIGRlIHJlcHJvZHVjaWJpbGlkYWQNCnNldC5zZWVkKDEyMykNCg0KIyBDcmVhciB1bmEgcGFydGljacOzbiBkZSBkYXRvcyBjb24gNzAlIHBhcmEgZW50cmVuYW1pZW50byB5IDMwJSBwYXJhIHBydWViYQ0KcGFydGljaW9uIDwtIGNyZWF0ZURhdGFQYXJ0aXRpb24oZGF0b3NfaW1wdXRhZG9zJHN0cm9rZSwgcCA9IDAuNywgbGlzdCA9IEZBTFNFKQ0KZGF0b3NfdHJhaW4gPC0gZGF0b3NfaW1wdXRhZG9zW3BhcnRpY2lvbiwgXQ0KZGF0b3NfdGVzdCA8LSBkYXRvc19pbXB1dGFkb3NbLXBhcnRpY2lvbiwgXQ0KDQojIFN1YmNvbmp1bnRvcyBkZSBkYXRvcyBkZSBlbnRyZW5hbWllbnRvIHkgcHJ1ZWJhIGNvbiBsYXMgdmFyaWFibGVzIHNlbGVjY2lvbmFkYXMNCmRhdG9zX3RyYWluX0FJQyA8LSBzdWJzZXQoZGF0b3NfdHJhaW4sIHNlbGVjdCA9IHZhcnNfYWljKQ0KZGF0b3NfdGVzdF9BSUMgPC0gc3Vic2V0KGRhdG9zX3Rlc3QsIHNlbGVjdCA9IHZhcnNfYWljKQ0KDQojIHN0cm9rZSBjb21vIGZhY3Rvcg0KZGF0b3NfdHJhaW5fQUlDJHN0cm9rZSA8LSBhcy5mYWN0b3IoZGF0b3NfdHJhaW5fQUlDJHN0cm9rZSkNCg0KIyBDb3JyZWdpciBiYWxhbmNlIGRlIHN0cm9rZSBtZWRpYW50ZSByZXNhbXBsZW8NCnNldC5zZWVkKDQyKQ0KZGF0b3NfdHJhaW5fQUlDX3Vwc2FtcGxlZCA8LSB1cFNhbXBsZSh4ID0gZGF0b3NfdHJhaW5fQUlDWywgIW5hbWVzKGRhdG9zX3RyYWluX0FJQykgJWluJSBjKCJzdHJva2UiKV0seSA9IGRhdG9zX3RyYWluX0FJQyRzdHJva2UseW5hbWUgPSAic3Ryb2tlIikNCg0KIyBwcm9wb3JjaW9uZXMgZGVzcHXDqXMgZGVsIHVwc2FtcGxpbmcNCnByaW50KHRhYmxlKGRhdG9zX3RyYWluX0FJQ191cHNhbXBsZWQkc3Ryb2tlKSkNCg0KIyBFbGltaW5hciBmaWxhcyBjb24gTkEgc2kgZXMgbmVjZXNhcmlvDQpkYXRvc190cmFpbl9BSUNfdXBzYW1wbGVkIDwtIG5hLm9taXQoZGF0b3NfdHJhaW5fQUlDX3Vwc2FtcGxlZCkNCmRhdG9zX3Rlc3RfQUlDX3Vwc2FtcGxlZCA8LSBuYS5vbWl0KGRhdG9zX3Rlc3RfQUlDKQ0KDQojIFJlZ3Jlc2nDs24gbG9nw61zdGljYSBjb24gdmFyaWFibGVzIEFJQyBlbiBkYXRvcyBiYWxhbmNlYWRvcw0KbW9kZWxvX2FpYyA8LSBnbG0oc3Ryb2tlIH4gYWdlICsgaHlwZXJ0ZW5zaW9uICsgaGVhcnRfZGlzZWFzZSArIGF2Z19nbHVjb3NlX2xldmVsICsgc21va2luZ19zdGF0dXMsIA0KICAgICAgICAgICAgICAgICAgZGF0YSA9IGRhdG9zX3RyYWluX0FJQ191cHNhbXBsZWQsIGZhbWlseSA9IGJpbm9taWFsKQ0KDQojIFJlc3VtZW4gZGVsIG1vZGVsbyBBSUMNCnN1bW1hcnkobW9kZWxvX2FpYykNCg0KIyBPZGRzIHJhdGlvcw0Kb2Rkc19yYXRpb3MgPC0gZXhwKGNvZWYobW9kZWxvX2FpYykpDQpwcmludChvZGRzX3JhdGlvcykNCg0KYGBgDQoNClNlIHJlYWxpesOzIHVuIHVwc2FtcGxpbmcgcGFyYSBiYWxhbmNlYXIgbGFzIGNsYXNlcywgcmVzdWx0YW5kbyBlbiAzNDA4IGNhc29zIHBhcmEgY2FkYSBjbGFzZSAoMCB5IDEpLg0KDQpDaWVydGFtZW50ZSwgcHVlZG8gcHJvcG9yY2lvbmFyIG3DoXMgZGV0YWxsZXMgc29icmUgbG9zIHJlc3VsdGFkb3MuIFZhbW9zIGEgZXhhbWluYXIgbcOhcyBkZSBjZXJjYSBsb3MgY29tcG9uZW50ZXMgY2xhdmUgZGVsIGFuw6FsaXNpczoNCg0KKipDb2VmaWNpZW50ZXMgZGVsIG1vZGVsbyoqOg0KDQpUb2RvcyBlc3RvcyBjb2VmaWNpZW50ZXMsIGV4Y2VwdG8gIkVzdGFkbyBkZSBmdW1hZG9yIChmdW1hKSIsIHNvbiBlc3RhZMOtc3RpY2FtZW50ZSBzaWduaWZpY2F0aXZvcyAocCA8IDAuMDAxKS4NCg0KKipPZGRzIFJhdGlvcyoqOg0KDQphZ2U6IDEuMDcxNjQzNzYgKFBvciBjYWRhIGHDsW8gcXVlIGF1bWVudGEgbGEgZWRhZCwgbGFzIHByb2JhYmlsaWRhZGVzIGRlIHRlbmVyIHVuIGFjY2lkZW50ZSBjZXJlYnJvdmFzY3VsYXIgc2UgbXVsdGlwbGljYW4gcG9yIDEuMDcgKGF1bWVudGFuIHVuIDclKSkNCg0KaHlwZXJ0ZW5zaW9uMTogMS42OTgxMTMxMS4gTGFzIHBlcnNvbmFzIGNvbiBoaXBlcnRlbnNpw7NuIHRpZW5lbiAxLjcwIHZlY2VzIG3DoXMgcHJvYmFiaWxpZGFkZXMgZGUgdGVuZXIgdW4gYWNjaWRlbnRlIGNlcmVicm92YXNjdWxhciBxdWUgbGFzIHF1ZSBubyB0aWVuZW4gaGlwZXJ0ZW5zacOzbi4NCg0KaGVhcnRfZGlzZWFzZTogMS40MjM0NjI2NS4gTGFzIHBlcnNvbmFzIGNvbiBlbmZlcm1lZGFkIGNhcmTDrWFjYSB0aWVuZW4gMS40MiB2ZWNlcyBtw6FzIHByb2JhYmlsaWRhZGVzIGRlIHRlbmVyIHVuIGFjY2lkZW50ZSBjZXJlYnJvdmFzY3VsYXIgcXVlIGxhcyBxdWUgbm8gdGllbmVuIGVuZmVybWVkYWQgY2FyZMOtYWNhLg0KDQphdmdfZ2x1Y29zZV9sZXZlbDogMS4wMDQ3ODE5Mi4gUG9yIGNhZGEgdW5pZGFkIHF1ZSBhdW1lbnRhIGVsIG5pdmVsIHByb21lZGlvIGRlIGdsdWNvc2EsIGxhcyBwcm9iYWJpbGlkYWRlcyBkZSB0ZW5lciB1biBhY2NpZGVudGUgY2VyZWJyb3Zhc2N1bGFyIHNlIG11bHRpcGxpY2FuIHBvciAxLjAwNSAoYXVtZW50YW4gdW4gMC41JSkuDQoNCnNtb2tpbmdfc3RhdHVzbmV2ZXIgc21va2VkOiAwLjYyNjQ0OTUxLiBMYXMgcGVyc29uYXMgcXVlIG51bmNhIGhhbiBmdW1hZG8gdGllbmVuIDAuNjMgdmVjZXMgbGFzIHByb2JhYmlsaWRhZGVzIChvIHVuIDM3JSBtZW5vcyBkZSBwcm9iYWJpbGlkYWRlcykgZGUgdGVuZXIgdW4gYWNjaWRlbnRlIGNlcmVicm92YXNjdWxhciBlbiBjb21wYXJhY2nDs24gY29uIGxhIGNhdGVnb3LDrWEgZGUgcmVmZXJlbmNpYS4NCg0Kc21va2luZ19zdGF0dXNzbW9rZXM6IDAuODUwMDkyNTAuIExvcyBmdW1hZG9yZXMgYWN0dWFsZXMgdGllbmVuIDAuODUgdmVjZXMgbGFzIHByb2JhYmlsaWRhZGVzIChvIHVuIDE1JSBtZW5vcyBkZSBwcm9iYWJpbGlkYWRlcykgZGUgdGVuZXIgdW4gYWNjaWRlbnRlIGNlcmVicm92YXNjdWxhciBlbiBjb21wYXJhY2nDs24gY29uIGxhIGNhdGVnb3LDrWEgZGUgcmVmZXJlbmNpYS4NCg0KDQpgYGB7cn0NCiMgcHJlZGljY2lvIGVuIGVsIGNvbmp1bnRvIGRlIHBydWViYQ0KcHJlZGljY2lvbmVzIDwtIHByZWRpY3QobW9kZWxvX2FpYywgbmV3ZGF0YSA9IGRhdG9zX3Rlc3RfQUlDX3Vwc2FtcGxlZCwgdHlwZSA9ICJyZXNwb25zZSIpDQpzdW1tYXJ5KHByZWRpY2Npb25lcykNCg0KI0NvbiB1bWJyYWwgZGUgMC4zDQpwcmVkaWNjaW9uZXNfYmluYXJpYXMgPC0gaWZlbHNlKHByZWRpY2Npb25lcyA+IDAuMywgMSwgMCkgIA0KdmFsb3Jlc19yZWFsZXMgPC0gZGF0b3NfdGVzdF9BSUMkc3Ryb2tlDQoNCiMgQ3JlYXIgbWF0cml6IGRlIGNvbmZ1c2nDs24NCmNtIDwtIGNvbmZ1c2lvbk1hdHJpeChmYWN0b3IocHJlZGljY2lvbmVzX2JpbmFyaWFzKSwgZmFjdG9yKHZhbG9yZXNfcmVhbGVzKSkNCnByaW50KGNtKQ0KDQojIG3DqXRyaWNhcyBlc3BlY8OtZmljYXMNCnNlbnNpYmlsaWRhZCA8LSBjbSRieUNsYXNzWydTZW5zaXRpdml0eSddDQplc3BlY2lmaWNpZGFkIDwtIGNtJGJ5Q2xhc3NbJ1NwZWNpZmljaXR5J10NCnByZWNpc2lvbiA8LSBjbSRieUNsYXNzWydQcmVjaXNpb24nXQ0KZXhhY3RpdHVkIDwtIGNtJG92ZXJhbGxbJ0FjY3VyYWN5J10NCmYxX3Njb3JlIDwtIGNtJGJ5Q2xhc3NbJ0YxJ10NCg0KIyBDYWxjdWxhciBsYSBjdXJ2YSBST0MgeSBBVUMgcGFyYSBsb3MgZGF0b3MgZGUgcHJ1ZWJhDQpyb2NfdGVzdCA8LSByb2MoZGF0b3NfdGVzdF9BSUMkc3Ryb2tlLCBwcmVkaWNjaW9uZXMpDQphdWNfdGVzdCA8LSBhdWMocm9jX3Rlc3QpDQoNCiMgUk9DIGRlIHBydWViYQ0KcGxvdChyb2NfdGVzdCwgbWFpbiA9ICJDdXJ2YSBST0MgLSBEYXRvcyBkZSBQcnVlYmEiLCBjb2wgPSAiYmx1ZSIpDQphYmxpbmUoYSA9IDAsIGIgPSAxLCBsdHkgPSAyLCBjb2wgPSAiZ3JheSIpDQoNCmBgYA0KDQpTZSBwcm9iYXJvbiAzIHVtYnJhbGVzOiAwLjIsIDAuMjUgeSAwLjMsIHZhbW9zIGEgY29tcGFyYXIgbGFzIG3DqXRyaWNhcyBjbGF2ZToNCg0KVW1icmFsIDAuMjoNCg0KQWNjdXJhY3k6IDAuNTA2OA0KU2Vuc2l0aXZpdHk6IDAuNDgwMzkNClNwZWNpZmljaXR5OiAwLjk4NzUwDQpCYWxhbmNlZCBBY2N1cmFjeTogMC43MzM5NA0KDQoNClVtYnJhbCAwLjI1Og0KDQpBY2N1cmFjeTogMC41NTUxDQpTZW5zaXRpdml0eTogMC41MzIwDQpTcGVjaWZpY2l0eTogMC45NzUwDQpCYWxhbmNlZCBBY2N1cmFjeTogMC43NTM1DQoNCg0KVW1icmFsIDAuMzoNCg0KQWNjdXJhY3k6IDAuNTk5NQ0KU2Vuc2l0aXZpdHk6IDAuNTgwMg0KU3BlY2lmaWNpdHk6IDAuOTUwMA0KQmFsYW5jZWQgQWNjdXJhY3k6IDAuNzY1MQ0KDQoNCiMjIDQuQ29uY2x1c2nDs24gZGVsIG1vZGVsbyBkZSBSZWdyZXNpw7NuDQoNCkFuYWxpemFuZG8gZXN0b3MgcmVzdWx0YWRvcywgZWwgdW1icmFsIGRlIDAuMyBwYXJlY2Ugc2VyIGVsIG1lam9yIHBvciBsYXMgc2lndWllbnRlcyByYXpvbmVzOg0KDQpUaWVuZSBsYSBtYXlvciBleGFjdGl0dWQgKEFjY3VyYWN5KSBkZSAwLjU5OTUsIGxvIHF1ZSBzaWduaWZpY2EgcXVlIGNsYXNpZmljYSBjb3JyZWN0YW1lbnRlIGVsIDU5Ljk1JSBkZSB0b2RvcyBsb3MgY2Fzb3MuDQpPZnJlY2UgZWwgbWVqb3IgZXF1aWxpYnJpbyBlbnRyZSBzZW5zaWJpbGlkYWQgeSBlc3BlY2lmaWNpZGFkOg0KDQpMYSBzZW5zaWJpbGlkYWQgKDAuNTgwMikgZXMgbGEgbcOhcyBhbHRhIGRlIGxvcyB0cmVzIHVtYnJhbGVzLCBsbyBxdWUgc2lnbmlmaWNhIHF1ZSBpZGVudGlmaWNhIGNvcnJlY3RhbWVudGUgZWwgNTguMDIlIGRlIGxvcyBjYXNvcyBuZWdhdGl2b3MgcmVhbGVzIChubyBzdHJva2UpLg0KDQpBdW5xdWUgbGEgZXNwZWNpZmljaWRhZCAoMC45NTAwKSBlcyBsaWdlcmFtZW50ZSBtZW5vciBxdWUgZW4gbG9zIG90cm9zIHVtYnJhbGVzLCBzaWd1ZSBzaWVuZG8gbXV5IGFsdGEsIGlkZW50aWZpY2FuZG8gY29ycmVjdGFtZW50ZSBlbCA5NSUgZGUgbG9zIGNhc29zIHBvc2l0aXZvcyByZWFsZXMgKHN0cm9rZSkuDQoNClRpZW5lIGVsIEJhbGFuY2VkIEFjY3VyYWN5IG3DoXMgYWx0byAoMC43NjUxKSwgbG8gcXVlIGluZGljYSB1biBtZWpvciByZW5kaW1pZW50byBnZW5lcmFsIGNvbnNpZGVyYW5kbyB0YW50byBsYSBzZW5zaWJpbGlkYWQgY29tbyBsYSBlc3BlY2lmaWNpZGFkLg0KRWwgdmFsb3IgZGUgS2FwcGEgKDAuMTE1OCkgZXMgZWwgbcOhcyBhbHRvIGRlIGxvcyB0cmVzLCBzdWdpcmllbmRvIHVuIGFjdWVyZG8gbGlnZXJhbWVudGUgbWVqb3IgZW50cmUgbGFzIHByZWRpY2Npb25lcyB5IGxvcyB2YWxvcmVzIHJlYWxlcy4NCg0KRW4gZWwgY29udGV4dG8gZGUgbGEgZGV0ZWNjacOzbiBkZSBhY2NpZGVudGVzIGNlcmVicm92YXNjdWxhcmVzLCByZXN1bHRhIGNydWNpYWwgdGVuZXIgdW5hIGJ1ZW5hIHNlbnNpYmlsaWRhZCBwYXJhIG5vIHBlcmRlciBjYXNvcyBwb3NpdGl2b3MsIHBlcm8gdGFtYmnDqW4gbWFudGVuZXIgdW5hIGFsdGEgZXNwZWNpZmljaWRhZCBwYXJhIGV2aXRhciBmYWxzb3MgcG9zaXRpdm9zLiBFbCB1bWJyYWwgZGUgMC4zIG9mcmVjZSBlbCBtZWpvciBjb21wcm9taXNvIGVudHJlIGVzdG9zIGRvcyBhc3BlY3Rvcy4NCg0KU2luIGVtYmFyZ28sIGVzIGltcG9ydGFudGUgbm90YXIgcXVlIGHDum4gY29uIGVzdGUgdW1icmFsLCBlbCBtb2RlbG8gdGllbmUgbGltaXRhY2lvbmVzOg0KDQpMYSBzZW5zaWJpbGlkYWQgc2lndWUgc2llbmRvIHJlbGF0aXZhbWVudGUgYmFqYSAoMC41ODAyKSwgbG8gcXVlIHNpZ25pZmljYSBxdWUgYcO6biBzZSBlc3TDoW4gcGVyZGllbmRvIG11Y2hvcyBjYXNvcyBwb3NpdGl2b3MuDQoNCkVsIGRlc2VxdWlsaWJyaW8gZW4gbG9zIGRhdG9zIChwcmV2YWxlbmNpYSBkZSAwLjk0NzggcGFyYSBsYSBjbGFzZSBuZWdhdGl2YSkgc2lndWUgYWZlY3RhbmRvIGVsIHJlbmRpbWllbnRvIGRlbCBtb2RlbG8uDQoNCkVuIGNvbmNsdXNpw7NuLCBhdW5xdWUgZWwgdW1icmFsIGRlIDAuMyBwYXJlY2Ugc2VyIGVsIG1lam9yIGRlIGxvcyB0cmVzLCBlbCBtb2RlbG8gYcO6biBwb2Ryw61hIGJlbmVmaWNpYXJzZSBkZSBtZWpvcmFzIGFkaWNpb25hbGVzLCBjb21vIHTDqWNuaWNhcyBkZSBiYWxhbmNlbyBkZSBkYXRvcyBtw6FzIGF2YW56YWRhcywgZmVhdHVyZSBlbmdpbmVlcmluZywgbyBpbmNsdXNvIGNvbnNpZGVyYXIgb3Ryb3MgYWxnb3JpdG1vcyBkZSBjbGFzaWZpY2FjacOzbiBxdWUgbWFuZWplbiBtZWpvciBsb3MgY29uanVudG9zIGRlIGRhdG9zIGRlc2VxdWlsaWJyYWRvcy4NCg0KKipGYWN0b3JlcyBkZSByaWVzZ28gcHJpbmNpcGFsZXM6KioNCg0KTGEgZWRhZCBlcyBlbCBwcmVkaWN0b3IgbcOhcyBmdWVydGUgeSBzaWduaWZpY2F0aXZvIHBhcmEgZWwgYWNjaWRlbnRlIGNlcmVicm92YXNjdWxhci4gUG9yIGNhZGEgYcOxbyBkZSBhdW1lbnRvIGVuIGxhIGVkYWQsIGVsIHJpZXNnbyBhdW1lbnRhIHVuIDcuMjclLg0KDQpFbCBuaXZlbCBwcm9tZWRpbyBkZSBnbHVjb3NhIGVuIHNhbmdyZSBlcyBlbCBzZWd1bmRvIGZhY3RvciBtw6FzIGltcG9ydGFudGUsIGF1bnF1ZSBzdSBlZmVjdG8gZXMgbcOhcyBwZXF1ZcOxbyBwZXJvIGVzdGFkw61zdGljYW1lbnRlIHNpZ25pZmljYXRpdm8uDQoNCk51bmNhIGhhYmVyIGZ1bWFkbyBwYXJlY2UgdGVuZXIgdW4gZWZlY3RvIHByb3RlY3RvciBzaWduaWZpY2F0aXZvLCByZWR1Y2llbmRvIGVsIHJpZXNnbyBkZSBhY2NpZGVudGUgY2VyZWJyb3Zhc2N1bGFyIGVuIHVuIDM1Ljg0JSBjb21wYXJhZG8gY29uIGxhIGNhdGVnb3LDrWEgZGUgcmVmZXJlbmNpYS4NCg0KKipNb2RlbG8qKg0KDQpFbCBtb2RlbG8gdGllbmUgdW4gYnVlbiBwb2RlciBwcmVkaWN0aXZvLCBjb24gdW4gQVVDIGRlIDAuODQ1MyAoSUMgOTUlOiAwLjgyMTQtMC44NzA4KS4NCkxhIGRpZmVyZW5jaWEgZW4gcmVuZGltaWVudG8gZW50cmUgZWwgbW9kZWxvIEFJQyB5IGVsIG1vZGVsbyBhbXBsaWFkbyBlcyBtw61uaW1hLCBsbyBxdWUgc3VnaWVyZSBxdWUgdW4gbW9kZWxvIG3DoXMgc2ltcGxlIGVzIHN1ZmljaWVudGUgcGFyYSBwcmVkZWNpciBlbCByaWVzZ28gZGUgYWNjaWRlbnRlIGNlcmVicm92YXNjdWxhci4NCg0KDQoqKkNvbXBhcmFjacOzbiBlbnRyZSBsb3MgbW9kZWxvcyBjb24gVXBzYW1wbGluZyB5IHNpbioqOg0KDQpFbiB1biBhIHByaW1lcmEgaW5zdGFuY2lhIHNlIGNvbXBhcsOzIGRvcyBtb2RlbG9zLCBjb24geSBzaW4gdXBzYW1wbGluZy4gRXN0ZSB1bHRpbW8gcGFyZWNlIHRlbmVyIHVuIG1lam9yIHJlbmRpbWllbnRvIGdlbmVyYWwsIGVzcGVjaWFsbWVudGUgZW4gdMOpcm1pbm9zIGRlIGJhbGFuY2UgZW50cmUgc2Vuc2liaWxpZGFkIHkgZXNwZWNpZmljaWRhZC4NCg0KRWwgbW9kZWxvIHNpbiB1cHNhbXBsaW5nIHRpZW5lIHVuYSBleGNlbGVudGUgZXNwZWNpZmljaWRhZCwgcGVybyBhIGNvc3RhIGRlIHVuYSBiYWphIHNlbnNpYmlsaWRhZCwgbG8gcXVlIHBvZHLDrWEgc2VyIHByb2JsZW3DoXRpY28gZW4gdW4gY29udGV4dG8gbcOpZGljbyBkb25kZSBubyBkZXRlY3RhciB1biBjYXNvIGRlIHN0cm9rZSAoZmFsc28gbmVnYXRpdm8pIHBvZHLDrWEgc2VyIG11eSBncmF2ZS4NCg0KRWwgdXBzYW1wbGluZyBoYSBheXVkYWRvIGEgbWVqb3JhciBsYSBkZXRlY2Npw7NuIGRlIGNhc29zIHBvc2l0aXZvcyBzaW4gc2FjcmlmaWNhciBkZW1hc2lhZG8gbGEgZXNwZWNpZmljaWRhZC4NCg0KKipJbXBsaWNhY2lvbmVzIGNsw61uaWNhcyBlbiBhbWJvcyBjYXNvczoqKg0KDQpFbiByZXN1bWVuLCBlbCBhbsOhbGlzaXMgcGFyZWNlIHByb3BvcmNpb25hcm5vcyB1bmEgaGVycmFtaWVudGEgw7p0aWwgcGFyYSBpZGVudGlmaWNhciBpbmRpdmlkdW9zIGVuIHJpZXNnbyBkZSBhY2NpZGVudGUgY2VyZWJyb3Zhc2N1bGFyLCBjb24gbGEgZWRhZCB5IGVsIG5pdmVsIGRlIGdsdWNvc2EgY29tbyBsb3MgcHJpbmNpcGFsZXMgZmFjdG9yZXMgYSBjb25zaWRlcmFyLiBFbCBtb2RlbG8gb2ZyZWNlIHVuIGJ1ZW4gZXF1aWxpYnJpbyBlbnRyZSBzaW1wbGljaWRhZCB5IHBvZGVyIHByZWRpY3Rpdm8sIGxvIHF1ZSBsbyBoYWNlIHBvdGVuY2lhbG1lbnRlIHZhbGlvc28gcGFyYSBzdSB1c28gZW4gZW50b3Jub3MgY2zDrW5pY29zLg0KDQoNCkEgY29udGludWFjacOzbiBzZSBwcm9jZWRlIGEgY29tcGFyYXIgZWwgcmVuZGltaWVudG8gY29uIGVsIG1vZGVsbyBSYW5kb20gRm9yZXN0DQoNCiMjIDUuQW5hbGlzaXMgZGUgUHJlZGljY2nDs24gcG9yIFJhbmRvbSBGb3Jlc3QNCg0KYGBge3J9DQoNCnJmX21vZGVsIDwtIHJhbmRvbUZvcmVzdChhcy5mYWN0b3Ioc3Ryb2tlKSB+IC4sIGRhdGEgPSBkYXRvc190cmFpbl9BSUMsIG50cmVlID0gMjAwMCwgaW1wb3J0YW5jZSA9IFRSVUUpDQpzdW1tYXJ5KHJmX21vZGVsKQ0KDQojIEltcHJpbWlyIHJlc3VtZW4gZGVsIG1vZGVsbw0KcHJpbnQocmZfbW9kZWwpDQoNCiMgSW1wb3J0YW5jaWEgZGUgbGFzIHZhcmlhYmxlcw0KdmFySW1wUGxvdChyZl9tb2RlbCkNCg0KIyBQcmVkaWNjaW9uZXMgZW4gZWwgY29uanVudG8gZGUgcHJ1ZWJhDQpwcmVkaWN0aW9ucyA8LSBwcmVkaWN0KHJmX21vZGVsLCBuZXdkYXRhID0gZGF0b3NfdGVzdF9BSUMpDQoNCg0KYGBgDQoqKkRldGVjY2nDs24gZGUgc3Ryb2tlcyoqOg0KDQpMYSBlc3BlY2lmaWNpZGFkIGRlbCA4MCUgbm9zIGluZGljYSBxdWUgZWwgbW9kZWxvIGVzIGJhc3RhbnRlIGJ1ZW5vIGlkZW50aWZpY2FuZG8gY2Fzb3MgZGUgYWNjaWRlbnRlcyBjZXJlYnJvdmFzY3VsYXJlcy4gU2luIGVtYmFyZ28sIGVsIGJham8gbsO6bWVybyBkZSB2ZXJkYWRlcm9zIHBvc2l0aXZvcyAoNjQpIGVuIGNvbXBhcmFjacOzbiBjb24gbG9zIGZhbHNvcyBuZWdhdGl2b3MgKDM4Nykgc3VnaWVyZSBxdWUgZWwgbW9kZWxvIGHDum4gcGllcmRlIG11Y2hvcyBjYXNvcyBkZSBzdHJva2UuDQoNCg0KUHJlZGljY2nDs24gZGUgbm8tc3Ryb2tlcyAoY2xhc2UgbmVnYXRpdmEpOg0KDQpMYSBhbHRhIHByZWNpc2nDs24gKDk4LjUyJSkgaW5kaWNhIHF1ZSBjdWFuZG8gZWwgbW9kZWxvIHByZWRpY2UgdW4gbm8tc3Ryb2tlLCBlcyBtdXkgcHJvYmFibGUgcXVlIHNlYSBjb3JyZWN0by4NCkxhIHNlbnNpYmlsaWRhZCBkZWwgNzMuMzclIHN1Z2llcmUgcXVlIGVsIG1vZGVsbyBpZGVudGlmaWNhIGNvcnJlY3RhbWVudGUgbGEgbWF5b3LDrWEgZGUgbG9zIGNhc29zIGRlIG5vLXN0cm9rZSwgcGVybyBhw7puIGNsYXNpZmljYSBlcnLDs25lYW1lbnRlIHVuYSBjYW50aWRhZCBzaWduaWZpY2F0aXZhIGNvbW8gc3Ryb2tlcy4NCg0KDQoqKkJhbGFuY2UgZW50cmUgc2Vuc2liaWxpZGFkIHkgZXNwZWNpZmljaWRhZCoqOg0KDQpFbCBtb2RlbG8gbXVlc3RyYSB1biBidWVuIGJhbGFuY2UgZW50cmUgZXN0b3MgZG9zIGFzcGVjdG9zLCBjb24gdW5hIGxpZ2VyYSBpbmNsaW5hY2nDs24gaGFjaWEgbGEgZXNwZWNpZmljaWRhZC4NCg0KQVVDIGRlIDAuODE5MToNCg0KSW5kaWNhIHVuYSBidWVuYSBjYXBhY2lkYWQgZGlzY3JpbWluYXRpdmEgZGVsIG1vZGVsby4gVW4gY2xhc2lmaWNhZG9yIGFsZWF0b3JpbyB0ZW5kcsOtYSB1biBBVUMgZGUgMC41LCBtaWVudHJhcyBxdWUgdW4gY2xhc2lmaWNhZG9yIHBlcmZlY3RvIHRlbmRyw61hIHVuIEFVQyBkZSAxLjAuDQoNCkVsIG1vZGVsbyBtdWVzdHJhIHVuIGJ1ZW4gcmVuZGltaWVudG8gZ2VuZXJhbCwgZXNwZWNpYWxtZW50ZSBjb25zaWRlcmFuZG8gbGEgbmF0dXJhbGV6YSBkZXNhZmlhbnRlIGRlIGxhIHByZWRpY2Npw7NuIGRlIHN0cm9rZXMuIEVsIGFsdG8gQVVDIHN1Z2llcmUgcXVlIGVsIG1vZGVsbyB0aWVuZSB1bmEgYnVlbmEgYmFzZSBwYXJhIGxhIGRpc2NyaW1pbmFjacOzbiwgcGVybyBwb2Ryw61hIGJlbmVmaWNpYXJzZSBkZSB1biBhanVzdGUgZmlubyBhZGljaW9uYWwuDQoNCg0KYGBge3J9DQpkYXRvc190cmFpbl9BSUMkc3Ryb2tlIDwtIGFzLmZhY3RvcihkYXRvc190cmFpbl9BSUMkc3Ryb2tlKQ0KDQojIHJlc2FtcGxlbyBwb3IgZGVzYmFsYW5jZXMgZXMgc3Ryb2tlcw0Kc2V0LnNlZWQoNDIpDQpkYXRvc191cHNhbXBsZWQgPC0gdXBTYW1wbGUoeCA9IGRhdG9zX3RyYWluX0FJQ1ssICFuYW1lcyhkYXRvc190cmFpbl9BSUMpICVpbiUgYygic3Ryb2tlIildLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBkYXRvc190cmFpbl9BSUMkc3Ryb2tlLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIHluYW1lID0gInN0cm9rZSIpDQoNCiMgVmVyIGxhcyBwcm9wb3JjaW9uZXMgDQp0YWJsZShkYXRvc191cHNhbXBsZWQkc3Ryb2tlKQ0KDQojIFByZWRpY2Npw7NuIGVuIGVsIGNvbmp1bnRvIGRlIHBydWViYQ0KcHJlZGljY2lvbmVzX3Byb2IgPC0gcHJlZGljdChyZl9tb2RlbCwgbmV3ZGF0YSA9IGRhdG9zX3Rlc3RfQUlDLCB0eXBlID0gInByb2IiKQ0KcHJlZGljY2lvbmVzIDwtIHByZWRpY2Npb25lc19wcm9iWywyXSANCg0KIyBSZXBldGltb3MgdW1icmFsIGRlIDAuMjUNCnByZWRpY2Npb25lc19iaW5hcmlhcyA8LSBpZmVsc2UocHJlZGljY2lvbmVzID4gMC4yLCAxLCAwKSAgDQp2YWxvcmVzX3JlYWxlcyA8LSBkYXRvc190ZXN0X0FJQyRzdHJva2UNCg0KIyBtYXRyaXogZGUgY29uZnVzacOzbg0KY20gPC0gY29uZnVzaW9uTWF0cml4KGZhY3RvcihwcmVkaWNjaW9uZXNfYmluYXJpYXMpLCBmYWN0b3IodmFsb3Jlc19yZWFsZXMpKQ0KcHJpbnQoY20pDQoNCiMgTcOpdHJpY2FzIGVzcGVjw61maWNhcw0Kc2Vuc2liaWxpZGFkIDwtIGNtJGJ5Q2xhc3NbJ1NlbnNpdGl2aXR5J10NCmVzcGVjaWZpY2lkYWQgPC0gY20kYnlDbGFzc1snU3BlY2lmaWNpdHknXQ0KcHJlY2lzaW9uIDwtIGNtJGJ5Q2xhc3NbJ1ByZWNpc2lvbiddDQpleGFjdGl0dWQgPC0gY20kb3ZlcmFsbFsnQWNjdXJhY3knXQ0KZjFfc2NvcmUgPC0gY20kYnlDbGFzc1snRjEnXQ0KDQojIEltcHJpbWlyIG3DqXRyaWNhcw0KcHJpbnQocGFzdGUoIlNlbnNpYmlsaWRhZDoiLCBzZW5zaWJpbGlkYWQpKQ0KcHJpbnQocGFzdGUoIkVzcGVjaWZpY2lkYWQ6IiwgZXNwZWNpZmljaWRhZCkpDQpwcmludChwYXN0ZSgiUHJlY2lzaW9uOiIsIHByZWNpc2lvbikpDQpwcmludChwYXN0ZSgiRXhhY3RpdHVkOiIsIGV4YWN0aXR1ZCkpDQpwcmludChwYXN0ZSgiRjEtU2NvcmU6IiwgZjFfc2NvcmUpKQ0KDQojIENhbGN1bGFyIGxhIGN1cnZhIFJPQyB5IEFVQyBwYXJhIGxvcyBkYXRvcyBkZSBwcnVlYmENCg0Kcm9jX3Rlc3QgPC0gcm9jKGRhdG9zX3Rlc3RfQUlDJHN0cm9rZSwgcHJlZGljY2lvbmVzKQ0KYXVjX3Rlc3QgPC0gYXVjKHJvY190ZXN0KQ0KcHJpbnQocGFzdGUoIkFVQzoiLCBhdWNfdGVzdCkpDQoNCiMgUk9DIGRlIHBydWViYQ0KcGxvdChyb2NfdGVzdCwgbWFpbiA9ICJDdXJ2YSBST0MgLSBEYXRvcyBkZSBQcnVlYmEiLCBjb2wgPSAiYmx1ZSIpDQphYmxpbmUoYSA9IDAsIGIgPSAxLCBsdHkgPSAyLCBjb2wgPSAiZ3JheSIpDQpgYGANCg0KDQojIyA2LkNvbmNsdXNpb24gZGVsIG1vZGVsbyBSYW5kb20gRm9yZXN0DQoNCkVsIG1vZGVsbyBjb24gdW1icmFsIGRlIDAuMiBjbGFzaWZpY2EgY29ycmVjdGFtZW50ZSBlbCA5My4yOCUgZGUgdG9kb3MgbG9zIGNhc29zLiBFc3RvIHBhcmVjZSBzZXIgdW4gYnVlbiByZW5kaW1pZW50bywgcGVybyBwdWVkZSBzZXIgZW5nYcOxb3NvIGRlYmlkbyBhbCBkZXNlcXVpbGlicmlvIGRlIGNsYXNlcyBlbiBsb3MgZGF0b3MgZGUgcHJ1ZWJhLg0KDQpTZW5zaWJpbGlkYWQgKDAuOTc3Myk6IGlkZW50aWZpY2EgY29ycmVjdGFtZW50ZSBlbCA5Ny43MyUgZGUgbG9zIGNhc29zIHNpbiBhY2NpZGVudGUgY2VyZWJyb3Zhc2N1bGFyLg0KRXN0byBpbmRpY2EgdW4gZXhjZWxlbnRlIHJlbmRpbWllbnRvIGVuIGxhIGRldGVjY2nDs24gZGUgY2Fzb3MgbmVnYXRpdm9zLg0KDQpFc3BlY2lmaWNpZGFkICgwLjEyNTApOmlkZW50aWZpY2EgY29ycmVjdGFtZW50ZSBzb2xvIGVsIDEyLjUlIGRlIGxvcyBjYXNvcyBjb24gYWNjaWRlbnRlIGNlcmVicm92YXNjdWxhci4gRXN0YSBiYWphIGVzcGVjaWZpY2lkYWQgZXMgcHJlb2N1cGFudGUsIHlhIHF1ZSBzaWduaWZpY2EgcXVlIGVsIG1vZGVsbyB0aWVuZSBkaWZpY3VsdGFkZXMgcGFyYSBkZXRlY3RhciBjYXNvcyBwb3NpdGl2b3MuDQoNClByZWNpc2nDs24gKDAuOTUzMCk6IERlIHRvZG9zIGxvcyBjYXNvcyBxdWUgZWwgbW9kZWxvIHByZWRpY2UgY29tbyBuZWdhdGl2b3MsIGVsIDk1LjMwJSBzb24gcmVhbG1lbnRlIG5lZ2F0aXZvcy4NCkVzdG8gc3VnaWVyZSBxdWUgY3VhbmRvIGVsIG1vZGVsbyBwcmVkaWNlIHF1ZSBubyBoYXkgYWNjaWRlbnRlIGNlcmVicm92YXNjdWxhciwgZ2VuZXJhbG1lbnRlIGVzIGNvcnJlY3RvLg0KDQpGMS1TY29yZSAoMC45NjUwKTogRXN0ZSBlcyB1biBidWVuIGVxdWlsaWJyaW8gZW50cmUgcHJlY2lzacOzbiB5IHNlbnNpYmlsaWRhZCBwYXJhIGxhIGNsYXNlIG5lZ2F0aXZhLg0KDQpBVUMgKDAuODE5Nyk6IEluZGljYSB1bmEgYnVlbmEgY2FwYWNpZGFkIGRpc2NyaW1pbmF0aXZhIGdlbmVyYWwgZGVsIG1vZGVsby4gDQoNCg0KT2JzZXJ2YWNpb25lcyBhZGljaW9uYWxlczoNCg0KRWwgbW9kZWxvIG11ZXN0cmEgdW4gZnVlcnRlIHNlc2dvIGhhY2lhIGxhIGNsYXNlIG1heW9yaXRhcmlhIChzaW4gYWNjaWRlbnRlIGNlcmVicm92YXNjdWxhcikuDQoNCkNvbW8geWEgc2UgbWVuY2lvbsOzIHVuIGFsdGEgc2Vuc2liaWxpZGFkIHkgYmFqYSBlc3BlY2lmaWNpZGFkIGluZGljYW4gcXVlIGVsIG1vZGVsbyB0aWVuZGUgYSBjbGFzaWZpY2FyIGxhIG1heW9yw61hIGRlIGxvcyBjYXNvcyBjb21vIG5lZ2F0aXZvcy4NCg0KRWwgZGVzZXF1aWxpYnJpbyBlbiBlbCByZW5kaW1pZW50byBlbnRyZSBjbGFzZXMgc3VnaWVyZSBxdWUgZWwgbW9kZWxvIG5vIGhhIGxvZ3JhZG8gc3VwZXJhciBjb21wbGV0YW1lbnRlIGVsIHByb2JsZW1hIGRlIGNsYXNlcyBkZXNlcXVpbGlicmFkYXMsIGEgcGVzYXIgZGVsIHNvYnJlbXVlc3RyZW8gZW4gZWwgZW50cmVuYW1pZW50by4NCg0KDQpJbXBsaWNhY2lvbmVzIHByw6FjdGljYXM6DQoNCkVsIG1vZGVsbyBlcyBtdXkgYnVlbm8gcGFyYSBpZGVudGlmaWNhciBjYXNvcyBzaW4gYWNjaWRlbnRlIGNlcmVicm92YXNjdWxhciwgbG8gcXVlIHBvZHLDrWEgc2VyIMO6dGlsIHBhcmEgZGVzY2FydGFyIHLDoXBpZGFtZW50ZSBjYXNvcyBkZSBiYWpvIHJpZXNnby4NCg0KU2luIGVtYmFyZ28sIHN1IGJhamEgY2FwYWNpZGFkIHBhcmEgZGV0ZWN0YXIgY2Fzb3MgcG9zaXRpdm9zIGxvIGhhY2UgcG9jbyBjb25maWFibGUgcGFyYSBpZGVudGlmaWNhciBwYWNpZW50ZXMgcXVlIHJlYWxtZW50ZSB0aWVuZW4gdW4gYWNjaWRlbnRlIGNlcmVicm92YXNjdWxhciwgbG8gY3VhbCBlcyBjcsOtdGljbyBlbiB1biBjb250ZXh0byBtw6lkaWNvLg0KDQojIyA3LkNvbmNsdXNpw7NuIEZpbmFsDQoNCmBgYHtyfQ0KIyBDcmVhciBlbCBkYXRhIGZyYW1lDQp0YWJsYV9jb21wYXJhdGl2YSA8LSBkYXRhLmZyYW1lKA0KICBNZXRyaWNhID0gYygiRXhhY3RpdHVkIiwgIlNlbnNpYmlsaWRhZCIsICJFc3BlY2lmaWNpZGFkIiwgIlByZWNpc2lvbiIsICJGMS1TY29yZSIsICJCYWxhbmNlZCBBY2N1cmFjeSIpLA0KICBSYW5kb21fRm9yZXN0ID0gYygwLjkzMjgsIDAuOTc3MywgMC4xMjUwLCAwLjk1MzAsIDAuOTY1MCwgMC41NTExKSwNCiAgUmVncmVzaW9uX0xvZ2lzdGljYSA9IGMoMC41OTk1LCAwLjU4MDIsIDAuOTUwMCwgMC45OTUzLCBOQSwgMC43NjUxKQ0KKQ0KDQojIE1vc3RyYXIgbGEgdGFibGENCnByaW50KHRhYmxhX2NvbXBhcmF0aXZhKQ0KYGBgDQoqKkV4YWN0aXR1ZCoqOg0KDQpSRiB0aWVuZSB1bmEgZXhhY3RpdHVkIG11Y2hvIG1heW9yICg5My4yOCUgdnMgNTkuOTUlKS4NCg0KU2luIGVtYmFyZ28sIGxhIGV4YWN0aXR1ZCBkZSBSRiBwdWVkZSBlc3RhciBpbmZsYWRhIGRlYmlkbyBhbCBkZXNlcXVpbGlicmlvIGRlIGNsYXNlcy4NCg0KKipTZW5zaWJpbGlkYWQqKjoNCg0KUkYgZXMgbXVjaG8gbWVqb3IgZW4gZGV0ZWN0YXIgY2Fzb3MgbmVnYXRpdm9zICg5Ny43MyUgdnMgNTguMDIlKS4NCg0KUkwgcGllcmRlIG11Y2hvcyBjYXNvcyBuZWdhdGl2b3MsIGNsYXNpZmljw6FuZG9sb3MgZXJyw7NuZWFtZW50ZSBjb21vIHBvc2l0aXZvcy4NCg0KKipFc3BlY2lmaWNpZGFkKio6DQoNClJMIGVzIHNpZ25pZmljYXRpdmFtZW50ZSBtZWpvciBlbiBkZXRlY3RhciBjYXNvcyBwb3NpdGl2b3MgKDk1JSB2cyAxMi41JSkuDQoNCkVzdGEgZXMgdW5hIG1lam9yYSBkcsOhc3RpY2EgZW4gbGEgZGV0ZWNjacOzbiBkZSBhY2NpZGVudGVzIGNlcmVicm92YXNjdWxhcmVzLg0KDQoqKlByZWNpc2nDs24qKjoNCg0KQW1ib3MgbW9kZWxvcyB0aWVuZW4gYWx0YSBwcmVjaXNpw7NuLCBwZXJvIFJMIGVzIGxpZ2VyYW1lbnRlIHN1cGVyaW9yICg5OS41MyUgdnMgOTUuMzAlKS4NCg0KKipCYWxhbmNlZCBBY2N1cmFjeSoqOg0KDQpSTCB0aWVuZSB1biBtZWpvciBlcXVpbGlicmlvIGVudHJlIHNlbnNpYmlsaWRhZCB5IGVzcGVjaWZpY2lkYWQgKDc2LjUxJSB2cyA1NS4xMSUpLg0KDQpFbCBtb2RlbG8gUkYgdGllbmUgdW4gZnVlcnRlIHNlc2dvIGhhY2lhIGxhIGNsYXNlIG1heW9yaXRhcmlhIChvc2VhIDAgKSwgbG8gcXVlIG5vcyBzdWdpZXJlIGVuIHVuYSBhbHRhIGV4YWN0aXR1ZCBnbG9iYWwgcGVybyB1bmEgcG9icmUgZGV0ZWNjacOzbiBkZSBjYXNvcyBwb3NpdGl2b3MuDQoNCkVsIG1vZGVsbyBSTCwgcG9yIG90cm8gbGFkbywgc2FjcmlmaWNhIGFsZ28gZGUgZXhhY3RpdHVkIGdsb2JhbCBwYXJhIGxvZ3JhciB1biBtZWpvciBlcXVpbGlicmlvIGVudHJlIGxhIGRldGVjY2nDs24gZGUgY2Fzb3MgcG9zaXRpdm9zIHkgbmVnYXRpdm9zLg0KUkwgZXMgbXVjaG8gbWVqb3IgZW4gZGV0ZWN0YXIgY2Fzb3MgZGUgYWNjaWRlbnRlIGNlcmVicm92YXNjdWxhciAoYWx0YSBlc3BlY2lmaWNpZGFkKSwgbG8gY3VhbCBlcyBjcnVjaWFsIGVuIHVuIGNvbnRleHRvIG3DqWRpY28uDQpTaW4gZW1iYXJnbywgUkwgY2xhc2lmaWNhIGVycsOzbmVhbWVudGUgbXVjaG9zIGNhc29zIG5lZ2F0aXZvcyBjb21vIHBvc2l0aXZvcywgbG8gcXVlIHBvZHLDrWEgbGxldmFyIGEgdW4gYWx0byBuw7ptZXJvIGRlIGZhbHNvcyBwb3NpdGl2b3MuDQoNClJGIHNlcsOtYSBtZWpvciBwYXJhIGRlc2NhcnRhciByw6FwaWRhbWVudGUgY2Fzb3MgZGUgYmFqbyByaWVzZ28sIHBlcm8gcG9kcsOtYSBwYXNhciBwb3IgYWx0byBtdWNob3MgY2Fzb3MgcmVhbGVzIGRlIGFjY2lkZW50ZSBjZXJlYnJvdmFzY3VsYXIuDQoNClJMIHNlcsOtYSBtw6FzIMO6dGlsIHBhcmEgaWRlbnRpZmljYXIgcG9zaWJsZXMgY2Fzb3MgZGUgYWNjaWRlbnRlIGNlcmVicm92YXNjdWxhciwgYXVucXVlIGEgY29zdGEgZGUgbcOhcyBmYWxzb3MgcG9zaXRpdm9zLg0KDQpFbiBjb25jbHVzacOzbiwgYXVucXVlIGVsIG1vZGVsbyBSRiB0aWVuZSB1bmEgbWF5b3IgZXhhY3RpdHVkIGdsb2JhbCwgZWwgbW9kZWxvIFJMIHBhcmVjZSBtw6FzIGFkZWN1YWRvIHBhcmEgZXN0ZSBwcm9ibGVtYSBtw6lkaWNvIGRlYmlkbyBhIHN1IG1lam9yIGNhcGFjaWRhZCBwYXJhIGRldGVjdGFyIGNhc29zIHBvc2l0aXZvcyBkZSBhY2NpZGVudGUgY2VyZWJyb3Zhc2N1bGFyLCBxdWUgZXMgY3J1Y2lhbCBlbiBlc3RlIGNvbnRleHRvLiBTaW4gZW1iYXJnbywgYW1ib3MgbW9kZWxvcyB0aWVuZW4gc3VzIGZvcnRhbGV6YXMgeSBkZWJpbGlkYWRlcywgeSBsYSBlbGVjY2nDs24gZmluYWwgZGVwZW5kZXLDoSBkZSBsb3Mgb2JqZXRpdm9zIGVzcGVjw61maWNvcyB5IGxhIHRvbGVyYW5jaWEgYWwgcmllc2dvIGVuIGVsIGNvbnRleHRvIGNsw61uaWNvLg0KDQo=