Teoría

El paquete CARET (Classification And Regression Training) es un paquete integral con una amplia variedad de algoritmos para el aprendizaje automático.

Instalar paquetes y llamar librerías

# install.packages("caret") # Algoritmos de aprendizaje automático
library(caret)
## Loading required package: ggplot2
## Loading required package: lattice
# install.packages("ggplot2") # Gráficas
library(ggplot2)
# install.packages("lattice") # Crear gráficos
library(lattice)
# install.packages("datasets") # Usar bases de datos precargadas
library(datasets)
# install.packages("DataExplorer") # Análisis Descriptivo
library(DataExplorer)
# install.packages("kernlab") 
library(kernlab)
## 
## Attaching package: 'kernlab'
## The following object is masked from 'package:ggplot2':
## 
##     alpha
# install.packages("readxl") 
library(readxl)

Cargar la base de datos

# file.choose()
df <- read_excel("/Users/elisarivas/Desktop/IA concentración/M2/heart.xlsx")

Entender la base de datos

summary(df)
##       age             sex               cp            trestbps    
##  Min.   :29.00   Min.   :0.0000   Min.   :0.0000   Min.   : 94.0  
##  1st Qu.:48.00   1st Qu.:0.0000   1st Qu.:0.0000   1st Qu.:120.0  
##  Median :56.00   Median :1.0000   Median :1.0000   Median :130.0  
##  Mean   :54.43   Mean   :0.6956   Mean   :0.9424   Mean   :131.6  
##  3rd Qu.:61.00   3rd Qu.:1.0000   3rd Qu.:2.0000   3rd Qu.:140.0  
##  Max.   :77.00   Max.   :1.0000   Max.   :3.0000   Max.   :200.0  
##       chol          fbs            restecg          thalach     
##  Min.   :126   Min.   :0.0000   Min.   :0.0000   Min.   : 71.0  
##  1st Qu.:211   1st Qu.:0.0000   1st Qu.:0.0000   1st Qu.:132.0  
##  Median :240   Median :0.0000   Median :1.0000   Median :152.0  
##  Mean   :246   Mean   :0.1493   Mean   :0.5298   Mean   :149.1  
##  3rd Qu.:275   3rd Qu.:0.0000   3rd Qu.:1.0000   3rd Qu.:166.0  
##  Max.   :564   Max.   :1.0000   Max.   :2.0000   Max.   :202.0  
##      exang           oldpeak          slope             ca        
##  Min.   :0.0000   Min.   :0.000   Min.   :0.000   Min.   :0.0000  
##  1st Qu.:0.0000   1st Qu.:0.000   1st Qu.:1.000   1st Qu.:0.0000  
##  Median :0.0000   Median :0.800   Median :1.000   Median :0.0000  
##  Mean   :0.3366   Mean   :1.072   Mean   :1.385   Mean   :0.7541  
##  3rd Qu.:1.0000   3rd Qu.:1.800   3rd Qu.:2.000   3rd Qu.:1.0000  
##  Max.   :1.0000   Max.   :6.200   Max.   :2.000   Max.   :4.0000  
##       thal           target      
##  Min.   :0.000   Min.   :0.0000  
##  1st Qu.:2.000   1st Qu.:0.0000  
##  Median :2.000   Median :1.0000  
##  Mean   :2.324   Mean   :0.5132  
##  3rd Qu.:3.000   3rd Qu.:1.0000  
##  Max.   :3.000   Max.   :1.0000
str(df)
## tibble [1,025 × 14] (S3: tbl_df/tbl/data.frame)
##  $ age     : num [1:1025] 52 53 70 61 62 58 58 55 46 54 ...
##  $ sex     : num [1:1025] 1 1 1 1 0 0 1 1 1 1 ...
##  $ cp      : num [1:1025] 0 0 0 0 0 0 0 0 0 0 ...
##  $ trestbps: num [1:1025] 125 140 145 148 138 100 114 160 120 122 ...
##  $ chol    : num [1:1025] 212 203 174 203 294 248 318 289 249 286 ...
##  $ fbs     : num [1:1025] 0 1 0 0 1 0 0 0 0 0 ...
##  $ restecg : num [1:1025] 1 0 1 1 1 0 2 0 0 0 ...
##  $ thalach : num [1:1025] 168 155 125 161 106 122 140 145 144 116 ...
##  $ exang   : num [1:1025] 0 1 1 0 0 0 0 1 0 1 ...
##  $ oldpeak : num [1:1025] 1 3.1 2.6 0 1.9 1 4.4 0.8 0.8 3.2 ...
##  $ slope   : num [1:1025] 2 0 0 2 1 1 0 1 2 1 ...
##  $ ca      : num [1:1025] 2 0 0 1 3 0 3 1 0 2 ...
##  $ thal    : num [1:1025] 3 3 3 3 2 2 1 3 3 2 ...
##  $ target  : num [1:1025] 0 0 0 0 0 1 0 0 0 0 ...
# create_report(df)
plot_missing(df)

plot_histogram(df)

plot_correlation(df)

Partir la base de datos

# Normalmente 80-20 o 70-30
set.seed(123)
renglones_entrenamiento <- createDataPartition(df$target, p=0.8, list=FALSE)
entrenamiento <- df[renglones_entrenamiento, ]
prueba <- df[-renglones_entrenamiento, ]

Distintos tipos de Métodos para Modelar

Los métodos más utilizados para modelar aprendizaje automático son:

  • SVM: Support Vector Machine o Máquina de Vectores de Soporte. Hay varios subtipos: Lineal (svmLinear), Radial (svmRadial), Polinómico (svmPoly), etc.
  • Árbol de Decisión: rpart
  • Redes Neuronales: nnet
  • Random Forest o Bosques Aleatorios: rf

Modelo 1. SVM Lineal

entrenamiento$target <- as.factor(entrenamiento$target)
prueba$target <- as.factor(prueba$target)

modelo1 <- train(target~., data=entrenamiento,
                 method="svmLinear", # Cambiar
                 preProcess = c("scale","center"),
                 trControl = trainControl(method="cv", number=10),
                 tuneGrid = data.frame(C=1) # Cambiar
                 )

resultado_entrenamiento1 <- predict(modelo1,entrenamiento)
resultado_prueba1 <- predict(modelo1,prueba)

# Matriz de Confusión
# Es una tabla de evaluación que desglosa el rendimiento del modelo de clasificación

# Matriz de Confusión del Resultado de Entrenamiento
mcre1 <- confusionMatrix(resultado_entrenamiento1,entrenamiento$target)
mcre1
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction   0   1
##          0 315  40
##          1  89 376
##                                           
##                Accuracy : 0.8427          
##                  95% CI : (0.8159, 0.8669)
##     No Information Rate : 0.5073          
##     P-Value [Acc > NIR] : < 2.2e-16       
##                                           
##                   Kappa : 0.6847          
##                                           
##  Mcnemar's Test P-Value : 2.377e-05       
##                                           
##             Sensitivity : 0.7797          
##             Specificity : 0.9038          
##          Pos Pred Value : 0.8873          
##          Neg Pred Value : 0.8086          
##              Prevalence : 0.4927          
##          Detection Rate : 0.3841          
##    Detection Prevalence : 0.4329          
##       Balanced Accuracy : 0.8418          
##                                           
##        'Positive' Class : 0               
## 
# Matriz de Confusión del Resultado de la Pruebav
mcrp1 <- confusionMatrix(resultado_prueba1, prueba$target)
mcrp1
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction   0   1
##          0  74   7
##          1  21 103
##                                           
##                Accuracy : 0.8634          
##                  95% CI : (0.8087, 0.9073)
##     No Information Rate : 0.5366          
##     P-Value [Acc > NIR] : < 2e-16         
##                                           
##                   Kappa : 0.7226          
##                                           
##  Mcnemar's Test P-Value : 0.01402         
##                                           
##             Sensitivity : 0.7789          
##             Specificity : 0.9364          
##          Pos Pred Value : 0.9136          
##          Neg Pred Value : 0.8306          
##              Prevalence : 0.4634          
##          Detection Rate : 0.3610          
##    Detection Prevalence : 0.3951          
##       Balanced Accuracy : 0.8577          
##                                           
##        'Positive' Class : 0               
## 

Modelo 2. SVM Radial

library(caret)
library(kernlab)

modelo2 <- train(target~., data=entrenamiento,
                 method = "svmRadial", # Cambiar
                 preProcess = c("center", "scale"),
                 trControl = trainControl(method = "cv", number = 10),
                 tuneGrid = expand.grid(sigma = 0.01, C=1)
)

resultado_entrenamiento2 <- predict(modelo2, entrenamiento)
resultado_prueba2 <- predict(modelo2, prueba)
# Matriz de confusión
# Es una tabla de evaluación que desglosa el rendimiento del modelo de clasificación

# Matriz de confusión del resultado de entrenamiento 
mcre2 <- confusionMatrix(resultado_entrenamiento2, entrenamiento$target)
mcre2
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction   0   1
##          0 318  33
##          1  86 383
##                                           
##                Accuracy : 0.8549          
##                  95% CI : (0.8289, 0.8783)
##     No Information Rate : 0.5073          
##     P-Value [Acc > NIR] : < 2.2e-16       
##                                           
##                   Kappa : 0.7091          
##                                           
##  Mcnemar's Test P-Value : 1.871e-06       
##                                           
##             Sensitivity : 0.7871          
##             Specificity : 0.9207          
##          Pos Pred Value : 0.9060          
##          Neg Pred Value : 0.8166          
##              Prevalence : 0.4927          
##          Detection Rate : 0.3878          
##    Detection Prevalence : 0.4280          
##       Balanced Accuracy : 0.8539          
##                                           
##        'Positive' Class : 0               
## 
# Matriz de confusión del resultado de la prueba
mcrp2 <- confusionMatrix(resultado_prueba2, prueba$target)
mcrp2
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction   0   1
##          0  79   4
##          1  16 106
##                                           
##                Accuracy : 0.9024          
##                  95% CI : (0.8533, 0.9394)
##     No Information Rate : 0.5366          
##     P-Value [Acc > NIR] : < 2e-16         
##                                           
##                   Kappa : 0.8021          
##                                           
##  Mcnemar's Test P-Value : 0.01391         
##                                           
##             Sensitivity : 0.8316          
##             Specificity : 0.9636          
##          Pos Pred Value : 0.9518          
##          Neg Pred Value : 0.8689          
##              Prevalence : 0.4634          
##          Detection Rate : 0.3854          
##    Detection Prevalence : 0.4049          
##       Balanced Accuracy : 0.8976          
##                                           
##        'Positive' Class : 0               
## 

Modelo 3. SVM Polinómico

modelo3 <- train(target~., data = entrenamiento,
                 method = "svmPoly", # Cambiar
                 preProcess = c("scale", "center"),
                 trControl = trainControl(method = "cv", number = 10),
                 tuneGrid = data.frame(degree = 2, scale = 1, C=1)
)

resultado_entrenamiento3 <- predict(modelo3, entrenamiento)
resultado_prueba3 <- predict(modelo3, prueba)

# Matriz de Confusión
# Es una tabla de evaluación que desglosa el rendimiento del modelo de clasificación

# Matriz de Confusión del Resultado de Entrenamiento 
mcre3 <- confusionMatrix(resultado_entrenamiento3, entrenamiento$target)
mcre3
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction   0   1
##          0 402   3
##          1   2 413
##                                          
##                Accuracy : 0.9939         
##                  95% CI : (0.9858, 0.998)
##     No Information Rate : 0.5073         
##     P-Value [Acc > NIR] : <2e-16         
##                                          
##                   Kappa : 0.9878         
##                                          
##  Mcnemar's Test P-Value : 1              
##                                          
##             Sensitivity : 0.9950         
##             Specificity : 0.9928         
##          Pos Pred Value : 0.9926         
##          Neg Pred Value : 0.9952         
##              Prevalence : 0.4927         
##          Detection Rate : 0.4902         
##    Detection Prevalence : 0.4939         
##       Balanced Accuracy : 0.9939         
##                                          
##        'Positive' Class : 0              
## 
# Matriz de Confusión del Resultado de la Prueba 
mcrp3 <- confusionMatrix(resultado_prueba3, prueba$target)
mcrp3
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction   0   1
##          0  93   4
##          1   2 106
##                                           
##                Accuracy : 0.9707          
##                  95% CI : (0.9374, 0.9892)
##     No Information Rate : 0.5366          
##     P-Value [Acc > NIR] : <2e-16          
##                                           
##                   Kappa : 0.9412          
##                                           
##  Mcnemar's Test P-Value : 0.6831          
##                                           
##             Sensitivity : 0.9789          
##             Specificity : 0.9636          
##          Pos Pred Value : 0.9588          
##          Neg Pred Value : 0.9815          
##              Prevalence : 0.4634          
##          Detection Rate : 0.4537          
##    Detection Prevalence : 0.4732          
##       Balanced Accuracy : 0.9713          
##                                           
##        'Positive' Class : 0               
## 

Modelo 4. Árbol de Decisión

modelo4 <- train(target~., data = entrenamiento,
                 method = "rpart", # Cambiar
                 preProcess = c("scale", "center"),
                 trControl = trainControl(method = "cv", number = 10),
                 tuneLength = 10
                 )

resultado_entrenamiento4 <- predict(modelo4, entrenamiento)
resultado_prueba4 <- predict(modelo4, prueba)

# Matriz de Confusión
# Es una tabla de evaluación que desglosa el rendimiento del modelo de clasificación 

# Matriz de Confusión del Resultado de Entrenamiento
mcre4 <- confusionMatrix(resultado_entrenamiento4, entrenamiento$target)
mcre4
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction   0   1
##          0 356  33
##          1  48 383
##                                           
##                Accuracy : 0.9012          
##                  95% CI : (0.8787, 0.9208)
##     No Information Rate : 0.5073          
##     P-Value [Acc > NIR] : <2e-16          
##                                           
##                   Kappa : 0.8023          
##                                           
##  Mcnemar's Test P-Value : 0.1198          
##                                           
##             Sensitivity : 0.8812          
##             Specificity : 0.9207          
##          Pos Pred Value : 0.9152          
##          Neg Pred Value : 0.8886          
##              Prevalence : 0.4927          
##          Detection Rate : 0.4341          
##    Detection Prevalence : 0.4744          
##       Balanced Accuracy : 0.9009          
##                                           
##        'Positive' Class : 0               
## 
mcrp4 <- confusionMatrix(resultado_prueba4, prueba$target)
mcrp4
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction  0  1
##          0 85 18
##          1 10 92
##                                           
##                Accuracy : 0.8634          
##                  95% CI : (0.8087, 0.9073)
##     No Information Rate : 0.5366          
##     P-Value [Acc > NIR] : <2e-16          
##                                           
##                   Kappa : 0.7269          
##                                           
##  Mcnemar's Test P-Value : 0.1859          
##                                           
##             Sensitivity : 0.8947          
##             Specificity : 0.8364          
##          Pos Pred Value : 0.8252          
##          Neg Pred Value : 0.9020          
##              Prevalence : 0.4634          
##          Detection Rate : 0.4146          
##    Detection Prevalence : 0.5024          
##       Balanced Accuracy : 0.8656          
##                                           
##        'Positive' Class : 0               
## 

Modelo 5. Bosques Aleatorios

modelo5 <- train(target~., data = entrenamiento,
                 method = "rf", # Cambiar
                 preProcess = c("scale", "center"),
                 trControl = trainControl(method = "cv", number = 10),
                 tuneGrid = expand.grid(mtry = c(2,4,6,8))
                 )

resultado_entrenamiento5 <- predict(modelo5, entrenamiento)
resultado_prueba5 <- predict(modelo5, prueba)

# Matriz de Confusión
# Es una tabla de evaluación que desglosa el rendimiento del modelo de clasificación

mcre5 <- confusionMatrix(resultado_entrenamiento5, entrenamiento$target)
mcre5
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction   0   1
##          0 404   0
##          1   0 416
##                                      
##                Accuracy : 1          
##                  95% CI : (0.9955, 1)
##     No Information Rate : 0.5073     
##     P-Value [Acc > NIR] : < 2.2e-16  
##                                      
##                   Kappa : 1          
##                                      
##  Mcnemar's Test P-Value : NA         
##                                      
##             Sensitivity : 1.0000     
##             Specificity : 1.0000     
##          Pos Pred Value : 1.0000     
##          Neg Pred Value : 1.0000     
##              Prevalence : 0.4927     
##          Detection Rate : 0.4927     
##    Detection Prevalence : 0.4927     
##       Balanced Accuracy : 1.0000     
##                                      
##        'Positive' Class : 0          
## 
mcrp5 <- confusionMatrix(resultado_prueba5, prueba$target)
mcrp5
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction   0   1
##          0  95   3
##          1   0 107
##                                          
##                Accuracy : 0.9854         
##                  95% CI : (0.9578, 0.997)
##     No Information Rate : 0.5366         
##     P-Value [Acc > NIR] : <2e-16         
##                                          
##                   Kappa : 0.9706         
##                                          
##  Mcnemar's Test P-Value : 0.2482         
##                                          
##             Sensitivity : 1.0000         
##             Specificity : 0.9727         
##          Pos Pred Value : 0.9694         
##          Neg Pred Value : 1.0000         
##              Prevalence : 0.4634         
##          Detection Rate : 0.4634         
##    Detection Prevalence : 0.4780         
##       Balanced Accuracy : 0.9864         
##                                          
##        'Positive' Class : 0              
## 

Modelo 6. Redes Neuronales

modelo6 <- train(target~., data = entrenamiento,
                 method = "nnet", # Cambiar
                 preProcess = c("scale", "center"),
                 trControl = trainControl(method = "cv", number = 10),
                 trace = FALSE
                 )

resultado_entrenamiento6 <- predict(modelo6, entrenamiento)
resultado_prueba6 <- predict(modelo6, prueba)

# Matriz de Confusión 
# Es una tabla de evaluación que desglosa el rendimiento del modelo de clasificación 

mcre6 <- confusionMatrix(resultado_entrenamiento6, entrenamiento$target)
mcre6
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction   0   1
##          0 398   3
##          1   6 413
##                                          
##                Accuracy : 0.989          
##                  95% CI : (0.9793, 0.995)
##     No Information Rate : 0.5073         
##     P-Value [Acc > NIR] : <2e-16         
##                                          
##                   Kappa : 0.978          
##                                          
##  Mcnemar's Test P-Value : 0.505          
##                                          
##             Sensitivity : 0.9851         
##             Specificity : 0.9928         
##          Pos Pred Value : 0.9925         
##          Neg Pred Value : 0.9857         
##              Prevalence : 0.4927         
##          Detection Rate : 0.4854         
##    Detection Prevalence : 0.4890         
##       Balanced Accuracy : 0.9890         
##                                          
##        'Positive' Class : 0              
## 
mcrp6 <- confusionMatrix(resultado_prueba6, prueba$target)
mcrp6
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction   0   1
##          0  93   0
##          1   2 110
##                                           
##                Accuracy : 0.9902          
##                  95% CI : (0.9652, 0.9988)
##     No Information Rate : 0.5366          
##     P-Value [Acc > NIR] : <2e-16          
##                                           
##                   Kappa : 0.9804          
##                                           
##  Mcnemar's Test P-Value : 0.4795          
##                                           
##             Sensitivity : 0.9789          
##             Specificity : 1.0000          
##          Pos Pred Value : 1.0000          
##          Neg Pred Value : 0.9821          
##              Prevalence : 0.4634          
##          Detection Rate : 0.4537          
##    Detection Prevalence : 0.4537          
##       Balanced Accuracy : 0.9895          
##                                           
##        'Positive' Class : 0               
## 

Tabla de Resultados

resultados <- data.frame(
  "svmLinear" = c(mcre1$overall["Accuracy"], mcrp1$overall["Accuracy"]),
  "svmRadial" = c(mcre2$overall["Accuracy"], mcrp2$overall["Accuracy"]),
  "svmPolly" = c(mcre3$overall["Accuracy"], mcrp3$overall["Accuracy"]),
  "rpart" = c(mcre4$overall["Accuracy"], mcrp4$overall["Accuracy"]),
  "rf" = c(mcre5$overall["Accuracy"], mcrp5$overall["Accuracy"]),
  "nnet" = c(mcre6$overall["Accuracy"], mcrp6$overall["Accuracy"])
)

rownames(resultados) <- c("Exactitud del Entrenamiento", "Exactitud de la Prueba")
resultados
##                             svmLinear svmRadial  svmPolly     rpart        rf
## Exactitud del Entrenamiento 0.8426829  0.854878 0.9939024 0.9012195 1.0000000
## Exactitud de la Prueba      0.8634146  0.902439 0.9707317 0.8634146 0.9853659
##                                  nnet
## Exactitud del Entrenamiento 0.9890244
## Exactitud de la Prueba      0.9902439

Conclusión

En conclusión, el modelo de Bosques Aleatorios o Random Forest es el recomendado para la clasificación de problemas del corazón.

LS0tCnRpdGxlOiAiQ0FSRVQgLSBIZWFydCIKYXV0aG9yOiAiRWxpc2EgUml2YXMgR8OzbWV6IEEwMTcyMzE2OSIKZGF0ZTogIjIwMjYtMDgtMjYiCm91dHB1dDogCiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogVFJVRQogICAgdG9jX2Zsb2F0OiBUUlVFCiAgICBjb2RlX2Rvd25sb2FkOiBUUlVFCiAgICB0aGVtZTogc2FuZHN0b25lCi0tLQoKIVtdKGh0dHBzOi8vd3d3LmFuaW1hdGlvbnNvZnR3YXJlNy5jb20vaW1nL2FnaWZzL2VmZmVjdDA0LmdpZikKCgojIDxzcGFuIHN0eWxlPSJjb2xvcjogcmVkIj5UZW9yw61hPC9zcGFuPgpFbCBwYXF1ZXRlICoqQ0FSRVQgKENsYXNzaWZpY2F0aW9uIEFuZCBSZWdyZXNzaW9uIFRyYWluaW5nKSoqIGVzIHVuIHBhcXVldGUgaW50ZWdyYWwgY29uIHVuYSBhbXBsaWEgdmFyaWVkYWQgZGUgYWxnb3JpdG1vcyBwYXJhIGVsIGFwcmVuZGl6YWplIGF1dG9tw6F0aWNvLgoKIyA8c3BhbiBzdHlsZT0iY29sb3I6IHJlZCI+SW5zdGFsYXIgcGFxdWV0ZXMgeSBsbGFtYXIgbGlicmVyw61hczwvc3Bhbj4KYGBge3Igd2FybmluZz1GQUxTRX0KIyBpbnN0YWxsLnBhY2thZ2VzKCJjYXJldCIpICMgQWxnb3JpdG1vcyBkZSBhcHJlbmRpemFqZSBhdXRvbcOhdGljbwpsaWJyYXJ5KGNhcmV0KQojIGluc3RhbGwucGFja2FnZXMoImdncGxvdDIiKSAjIEdyw6FmaWNhcwpsaWJyYXJ5KGdncGxvdDIpCiMgaW5zdGFsbC5wYWNrYWdlcygibGF0dGljZSIpICMgQ3JlYXIgZ3LDoWZpY29zCmxpYnJhcnkobGF0dGljZSkKIyBpbnN0YWxsLnBhY2thZ2VzKCJkYXRhc2V0cyIpICMgVXNhciBiYXNlcyBkZSBkYXRvcyBwcmVjYXJnYWRhcwpsaWJyYXJ5KGRhdGFzZXRzKQojIGluc3RhbGwucGFja2FnZXMoIkRhdGFFeHBsb3JlciIpICMgQW7DoWxpc2lzIERlc2NyaXB0aXZvCmxpYnJhcnkoRGF0YUV4cGxvcmVyKQojIGluc3RhbGwucGFja2FnZXMoImtlcm5sYWIiKSAKbGlicmFyeShrZXJubGFiKQojIGluc3RhbGwucGFja2FnZXMoInJlYWR4bCIpIApsaWJyYXJ5KHJlYWR4bCkKYGBgCgojIDxzcGFuIHN0eWxlPSJjb2xvcjogcmVkIj5DYXJnYXIgbGEgYmFzZSBkZSBkYXRvczwvc3Bhbj4KYGBge3J9CiMgZmlsZS5jaG9vc2UoKQpkZiA8LSByZWFkX2V4Y2VsKCIvVXNlcnMvZWxpc2FyaXZhcy9EZXNrdG9wL0lBIGNvbmNlbnRyYWNpb8yBbi9NMi9oZWFydC54bHN4IikKYGBgCgojIDxzcGFuIHN0eWxlPSJjb2xvcjogcmVkIj5FbnRlbmRlciBsYSBiYXNlIGRlIGRhdG9zPC9zcGFuPgpgYGB7cn0Kc3VtbWFyeShkZikKc3RyKGRmKQojIGNyZWF0ZV9yZXBvcnQoZGYpCnBsb3RfbWlzc2luZyhkZikKcGxvdF9oaXN0b2dyYW0oZGYpCnBsb3RfY29ycmVsYXRpb24oZGYpCmBgYAoKIyA8c3BhbiBzdHlsZT0iY29sb3I6IHJlZCI+UGFydGlyIGxhIGJhc2UgZGUgZGF0b3M8L3NwYW4+CmBgYHtyfQojIE5vcm1hbG1lbnRlIDgwLTIwIG8gNzAtMzAKc2V0LnNlZWQoMTIzKQpyZW5nbG9uZXNfZW50cmVuYW1pZW50byA8LSBjcmVhdGVEYXRhUGFydGl0aW9uKGRmJHRhcmdldCwgcD0wLjgsIGxpc3Q9RkFMU0UpCmVudHJlbmFtaWVudG8gPC0gZGZbcmVuZ2xvbmVzX2VudHJlbmFtaWVudG8sIF0KcHJ1ZWJhIDwtIGRmWy1yZW5nbG9uZXNfZW50cmVuYW1pZW50bywgXQpgYGAKCiMgPHNwYW4gc3R5bGU9ImNvbG9yOiByZWQiPkRpc3RpbnRvcyB0aXBvcyBkZSBNw6l0b2RvcyBwYXJhIE1vZGVsYXI8L3NwYW4+CkxvcyBtw6l0b2RvcyBtw6FzIHV0aWxpemFkb3MgcGFyYSBtb2RlbGFyIGFwcmVuZGl6YWplIGF1dG9tw6F0aWNvIHNvbjoKCiogKipTVk0qKjogKlN1cHBvcnQgVmVjdG9yIE1hY2hpbmUqIG8gTcOhcXVpbmEgZGUgVmVjdG9yZXMgZGUgU29wb3J0ZS4gSGF5IHZhcmlvcyBzdWJ0aXBvczogTGluZWFsIChzdm1MaW5lYXIpLCBSYWRpYWwgKHN2bVJhZGlhbCksIFBvbGluw7NtaWNvIChzdm1Qb2x5KSwgZXRjLgoqICoqw4FyYm9sIGRlIERlY2lzacOzbioqOiBycGFydAoqICoqUmVkZXMgTmV1cm9uYWxlcyoqOiBubmV0CiogKipSYW5kb20gRm9yZXN0KiogbyBCb3NxdWVzIEFsZWF0b3Jpb3M6IHJmCgojIDxzcGFuIHN0eWxlPSJjb2xvcjogcmVkIj5Nb2RlbG8gMS4gU1ZNIExpbmVhbDwvc3Bhbj4KYGBge3J9CmVudHJlbmFtaWVudG8kdGFyZ2V0IDwtIGFzLmZhY3RvcihlbnRyZW5hbWllbnRvJHRhcmdldCkKcHJ1ZWJhJHRhcmdldCA8LSBhcy5mYWN0b3IocHJ1ZWJhJHRhcmdldCkKCm1vZGVsbzEgPC0gdHJhaW4odGFyZ2V0fi4sIGRhdGE9ZW50cmVuYW1pZW50bywKICAgICAgICAgICAgICAgICBtZXRob2Q9InN2bUxpbmVhciIsICMgQ2FtYmlhcgogICAgICAgICAgICAgICAgIHByZVByb2Nlc3MgPSBjKCJzY2FsZSIsImNlbnRlciIpLAogICAgICAgICAgICAgICAgIHRyQ29udHJvbCA9IHRyYWluQ29udHJvbChtZXRob2Q9ImN2IiwgbnVtYmVyPTEwKSwKICAgICAgICAgICAgICAgICB0dW5lR3JpZCA9IGRhdGEuZnJhbWUoQz0xKSAjIENhbWJpYXIKICAgICAgICAgICAgICAgICApCgpyZXN1bHRhZG9fZW50cmVuYW1pZW50bzEgPC0gcHJlZGljdChtb2RlbG8xLGVudHJlbmFtaWVudG8pCnJlc3VsdGFkb19wcnVlYmExIDwtIHByZWRpY3QobW9kZWxvMSxwcnVlYmEpCgojIE1hdHJpeiBkZSBDb25mdXNpw7NuCiMgRXMgdW5hIHRhYmxhIGRlIGV2YWx1YWNpw7NuIHF1ZSBkZXNnbG9zYSBlbCByZW5kaW1pZW50byBkZWwgbW9kZWxvIGRlIGNsYXNpZmljYWNpw7NuCgojIE1hdHJpeiBkZSBDb25mdXNpw7NuIGRlbCBSZXN1bHRhZG8gZGUgRW50cmVuYW1pZW50bwptY3JlMSA8LSBjb25mdXNpb25NYXRyaXgocmVzdWx0YWRvX2VudHJlbmFtaWVudG8xLGVudHJlbmFtaWVudG8kdGFyZ2V0KQptY3JlMQoKIyBNYXRyaXogZGUgQ29uZnVzacOzbiBkZWwgUmVzdWx0YWRvIGRlIGxhIFBydWViYXYKbWNycDEgPC0gY29uZnVzaW9uTWF0cml4KHJlc3VsdGFkb19wcnVlYmExLCBwcnVlYmEkdGFyZ2V0KQptY3JwMQpgYGAKCiMgPHNwYW4gc3R5bGUgPSJjb2xvcjogcmVkIj5Nb2RlbG8gMi4gU1ZNIFJhZGlhbDwvc3Bhbj4KYGBge3J9CmxpYnJhcnkoY2FyZXQpCmxpYnJhcnkoa2VybmxhYikKCm1vZGVsbzIgPC0gdHJhaW4odGFyZ2V0fi4sIGRhdGE9ZW50cmVuYW1pZW50bywKICAgICAgICAgICAgICAgICBtZXRob2QgPSAic3ZtUmFkaWFsIiwgIyBDYW1iaWFyCiAgICAgICAgICAgICAgICAgcHJlUHJvY2VzcyA9IGMoImNlbnRlciIsICJzY2FsZSIpLAogICAgICAgICAgICAgICAgIHRyQ29udHJvbCA9IHRyYWluQ29udHJvbChtZXRob2QgPSAiY3YiLCBudW1iZXIgPSAxMCksCiAgICAgICAgICAgICAgICAgdHVuZUdyaWQgPSBleHBhbmQuZ3JpZChzaWdtYSA9IDAuMDEsIEM9MSkKKQoKcmVzdWx0YWRvX2VudHJlbmFtaWVudG8yIDwtIHByZWRpY3QobW9kZWxvMiwgZW50cmVuYW1pZW50bykKcmVzdWx0YWRvX3BydWViYTIgPC0gcHJlZGljdChtb2RlbG8yLCBwcnVlYmEpCiMgTWF0cml6IGRlIGNvbmZ1c2nDs24KIyBFcyB1bmEgdGFibGEgZGUgZXZhbHVhY2nDs24gcXVlIGRlc2dsb3NhIGVsIHJlbmRpbWllbnRvIGRlbCBtb2RlbG8gZGUgY2xhc2lmaWNhY2nDs24KCiMgTWF0cml6IGRlIGNvbmZ1c2nDs24gZGVsIHJlc3VsdGFkbyBkZSBlbnRyZW5hbWllbnRvIAptY3JlMiA8LSBjb25mdXNpb25NYXRyaXgocmVzdWx0YWRvX2VudHJlbmFtaWVudG8yLCBlbnRyZW5hbWllbnRvJHRhcmdldCkKbWNyZTIKCiMgTWF0cml6IGRlIGNvbmZ1c2nDs24gZGVsIHJlc3VsdGFkbyBkZSBsYSBwcnVlYmEKbWNycDIgPC0gY29uZnVzaW9uTWF0cml4KHJlc3VsdGFkb19wcnVlYmEyLCBwcnVlYmEkdGFyZ2V0KQptY3JwMgpgYGAKCiMgPHNwYW4gc3R5bGUgPSJjb2xvcjogcmVkIj5Nb2RlbG8gMy4gU1ZNIFBvbGluw7NtaWNvPC9zcGFuPgpgYGB7cn0KbW9kZWxvMyA8LSB0cmFpbih0YXJnZXR+LiwgZGF0YSA9IGVudHJlbmFtaWVudG8sCiAgICAgICAgICAgICAgICAgbWV0aG9kID0gInN2bVBvbHkiLCAjIENhbWJpYXIKICAgICAgICAgICAgICAgICBwcmVQcm9jZXNzID0gYygic2NhbGUiLCAiY2VudGVyIiksCiAgICAgICAgICAgICAgICAgdHJDb250cm9sID0gdHJhaW5Db250cm9sKG1ldGhvZCA9ICJjdiIsIG51bWJlciA9IDEwKSwKICAgICAgICAgICAgICAgICB0dW5lR3JpZCA9IGRhdGEuZnJhbWUoZGVncmVlID0gMiwgc2NhbGUgPSAxLCBDPTEpCikKCnJlc3VsdGFkb19lbnRyZW5hbWllbnRvMyA8LSBwcmVkaWN0KG1vZGVsbzMsIGVudHJlbmFtaWVudG8pCnJlc3VsdGFkb19wcnVlYmEzIDwtIHByZWRpY3QobW9kZWxvMywgcHJ1ZWJhKQoKIyBNYXRyaXogZGUgQ29uZnVzacOzbgojIEVzIHVuYSB0YWJsYSBkZSBldmFsdWFjacOzbiBxdWUgZGVzZ2xvc2EgZWwgcmVuZGltaWVudG8gZGVsIG1vZGVsbyBkZSBjbGFzaWZpY2FjacOzbgoKIyBNYXRyaXogZGUgQ29uZnVzacOzbiBkZWwgUmVzdWx0YWRvIGRlIEVudHJlbmFtaWVudG8gCm1jcmUzIDwtIGNvbmZ1c2lvbk1hdHJpeChyZXN1bHRhZG9fZW50cmVuYW1pZW50bzMsIGVudHJlbmFtaWVudG8kdGFyZ2V0KQptY3JlMwoKIyBNYXRyaXogZGUgQ29uZnVzacOzbiBkZWwgUmVzdWx0YWRvIGRlIGxhIFBydWViYSAKbWNycDMgPC0gY29uZnVzaW9uTWF0cml4KHJlc3VsdGFkb19wcnVlYmEzLCBwcnVlYmEkdGFyZ2V0KQptY3JwMwpgYGAKCiMgPHNwYW4gc3R5bGUgPSJjb2xvcjogcmVkIj5Nb2RlbG8gNC4gw4FyYm9sIGRlIERlY2lzacOzbjwvc3Bhbj4KYGBge3J9Cm1vZGVsbzQgPC0gdHJhaW4odGFyZ2V0fi4sIGRhdGEgPSBlbnRyZW5hbWllbnRvLAogICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJycGFydCIsICMgQ2FtYmlhcgogICAgICAgICAgICAgICAgIHByZVByb2Nlc3MgPSBjKCJzY2FsZSIsICJjZW50ZXIiKSwKICAgICAgICAgICAgICAgICB0ckNvbnRyb2wgPSB0cmFpbkNvbnRyb2wobWV0aG9kID0gImN2IiwgbnVtYmVyID0gMTApLAogICAgICAgICAgICAgICAgIHR1bmVMZW5ndGggPSAxMAogICAgICAgICAgICAgICAgICkKCnJlc3VsdGFkb19lbnRyZW5hbWllbnRvNCA8LSBwcmVkaWN0KG1vZGVsbzQsIGVudHJlbmFtaWVudG8pCnJlc3VsdGFkb19wcnVlYmE0IDwtIHByZWRpY3QobW9kZWxvNCwgcHJ1ZWJhKQoKIyBNYXRyaXogZGUgQ29uZnVzacOzbgojIEVzIHVuYSB0YWJsYSBkZSBldmFsdWFjacOzbiBxdWUgZGVzZ2xvc2EgZWwgcmVuZGltaWVudG8gZGVsIG1vZGVsbyBkZSBjbGFzaWZpY2FjacOzbiAKCiMgTWF0cml6IGRlIENvbmZ1c2nDs24gZGVsIFJlc3VsdGFkbyBkZSBFbnRyZW5hbWllbnRvCm1jcmU0IDwtIGNvbmZ1c2lvbk1hdHJpeChyZXN1bHRhZG9fZW50cmVuYW1pZW50bzQsIGVudHJlbmFtaWVudG8kdGFyZ2V0KQptY3JlNAoKbWNycDQgPC0gY29uZnVzaW9uTWF0cml4KHJlc3VsdGFkb19wcnVlYmE0LCBwcnVlYmEkdGFyZ2V0KQptY3JwNApgYGAKCiMgPHNwYW4gc3R5bGUgPSJjb2xvcjogcmVkIj5Nb2RlbG8gNS4gQm9zcXVlcyBBbGVhdG9yaW9zPC9zcGFuPgpgYGB7cn0KbW9kZWxvNSA8LSB0cmFpbih0YXJnZXR+LiwgZGF0YSA9IGVudHJlbmFtaWVudG8sCiAgICAgICAgICAgICAgICAgbWV0aG9kID0gInJmIiwgIyBDYW1iaWFyCiAgICAgICAgICAgICAgICAgcHJlUHJvY2VzcyA9IGMoInNjYWxlIiwgImNlbnRlciIpLAogICAgICAgICAgICAgICAgIHRyQ29udHJvbCA9IHRyYWluQ29udHJvbChtZXRob2QgPSAiY3YiLCBudW1iZXIgPSAxMCksCiAgICAgICAgICAgICAgICAgdHVuZUdyaWQgPSBleHBhbmQuZ3JpZChtdHJ5ID0gYygyLDQsNiw4KSkKICAgICAgICAgICAgICAgICApCgpyZXN1bHRhZG9fZW50cmVuYW1pZW50bzUgPC0gcHJlZGljdChtb2RlbG81LCBlbnRyZW5hbWllbnRvKQpyZXN1bHRhZG9fcHJ1ZWJhNSA8LSBwcmVkaWN0KG1vZGVsbzUsIHBydWViYSkKCiMgTWF0cml6IGRlIENvbmZ1c2nDs24KIyBFcyB1bmEgdGFibGEgZGUgZXZhbHVhY2nDs24gcXVlIGRlc2dsb3NhIGVsIHJlbmRpbWllbnRvIGRlbCBtb2RlbG8gZGUgY2xhc2lmaWNhY2nDs24KCm1jcmU1IDwtIGNvbmZ1c2lvbk1hdHJpeChyZXN1bHRhZG9fZW50cmVuYW1pZW50bzUsIGVudHJlbmFtaWVudG8kdGFyZ2V0KQptY3JlNQoKbWNycDUgPC0gY29uZnVzaW9uTWF0cml4KHJlc3VsdGFkb19wcnVlYmE1LCBwcnVlYmEkdGFyZ2V0KQptY3JwNQpgYGAKCiMgPHNwYW4gc3R5bGU9ImNvbG9yOiByZWQiPk1vZGVsbyA2LiBSZWRlcyBOZXVyb25hbGVzPC9zcGFuPgpgYGB7cn0KbW9kZWxvNiA8LSB0cmFpbih0YXJnZXR+LiwgZGF0YSA9IGVudHJlbmFtaWVudG8sCiAgICAgICAgICAgICAgICAgbWV0aG9kID0gIm5uZXQiLCAjIENhbWJpYXIKICAgICAgICAgICAgICAgICBwcmVQcm9jZXNzID0gYygic2NhbGUiLCAiY2VudGVyIiksCiAgICAgICAgICAgICAgICAgdHJDb250cm9sID0gdHJhaW5Db250cm9sKG1ldGhvZCA9ICJjdiIsIG51bWJlciA9IDEwKSwKICAgICAgICAgICAgICAgICB0cmFjZSA9IEZBTFNFCiAgICAgICAgICAgICAgICAgKQoKcmVzdWx0YWRvX2VudHJlbmFtaWVudG82IDwtIHByZWRpY3QobW9kZWxvNiwgZW50cmVuYW1pZW50bykKcmVzdWx0YWRvX3BydWViYTYgPC0gcHJlZGljdChtb2RlbG82LCBwcnVlYmEpCgojIE1hdHJpeiBkZSBDb25mdXNpw7NuIAojIEVzIHVuYSB0YWJsYSBkZSBldmFsdWFjacOzbiBxdWUgZGVzZ2xvc2EgZWwgcmVuZGltaWVudG8gZGVsIG1vZGVsbyBkZSBjbGFzaWZpY2FjacOzbiAKCm1jcmU2IDwtIGNvbmZ1c2lvbk1hdHJpeChyZXN1bHRhZG9fZW50cmVuYW1pZW50bzYsIGVudHJlbmFtaWVudG8kdGFyZ2V0KQptY3JlNgoKbWNycDYgPC0gY29uZnVzaW9uTWF0cml4KHJlc3VsdGFkb19wcnVlYmE2LCBwcnVlYmEkdGFyZ2V0KQptY3JwNgpgYGAKCiMgPHNwYW4gc3R5bGU9ImNvbG9yOiByZWQiPlRhYmxhIGRlIFJlc3VsdGFkb3M8L3NwYW4+CmBgYHtyfQpyZXN1bHRhZG9zIDwtIGRhdGEuZnJhbWUoCiAgInN2bUxpbmVhciIgPSBjKG1jcmUxJG92ZXJhbGxbIkFjY3VyYWN5Il0sIG1jcnAxJG92ZXJhbGxbIkFjY3VyYWN5Il0pLAogICJzdm1SYWRpYWwiID0gYyhtY3JlMiRvdmVyYWxsWyJBY2N1cmFjeSJdLCBtY3JwMiRvdmVyYWxsWyJBY2N1cmFjeSJdKSwKICAic3ZtUG9sbHkiID0gYyhtY3JlMyRvdmVyYWxsWyJBY2N1cmFjeSJdLCBtY3JwMyRvdmVyYWxsWyJBY2N1cmFjeSJdKSwKICAicnBhcnQiID0gYyhtY3JlNCRvdmVyYWxsWyJBY2N1cmFjeSJdLCBtY3JwNCRvdmVyYWxsWyJBY2N1cmFjeSJdKSwKICAicmYiID0gYyhtY3JlNSRvdmVyYWxsWyJBY2N1cmFjeSJdLCBtY3JwNSRvdmVyYWxsWyJBY2N1cmFjeSJdKSwKICAibm5ldCIgPSBjKG1jcmU2JG92ZXJhbGxbIkFjY3VyYWN5Il0sIG1jcnA2JG92ZXJhbGxbIkFjY3VyYWN5Il0pCikKCnJvd25hbWVzKHJlc3VsdGFkb3MpIDwtIGMoIkV4YWN0aXR1ZCBkZWwgRW50cmVuYW1pZW50byIsICJFeGFjdGl0dWQgZGUgbGEgUHJ1ZWJhIikKcmVzdWx0YWRvcwpgYGAKCiMgPHNwYW4gc3R5bGU9ImNvbG9yOiByZWQiPkNvbmNsdXNpw7NuPC9zcGFuPgpFbiBjb25jbHVzacOzbiwgZWwgbW9kZWxvIGRlICoqQm9zcXVlcyBBbGVhdG9yaW9zIG8gUmFuZG9tIEZvcmVzdCoqIGVzIGVsIHJlY29tZW5kYWRvIHBhcmEgbGEgY2xhc2lmaWNhY2nDs24gZGUgcHJvYmxlbWFzIGRlbCBjb3JhesOzbi4K