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)

#install.packages("ggplot2") # graficas
library(ggplot2)

#install.packages("lattice") #crear gráficos
library(lattice)

#install.packages("readxl") # para cargar archivos excel
library(readxl)

#install.packages("DataExplorer") #análisis descriptivo
library(DataExplorer) 

#install.packages("kernlab")
library(kernlab)

#install.packages("randomForest")
library(randomForest)

#install.packages("rpart")
library(rpart)

#install.packages("nnet")
library(nnet)

Importar base de datos

df <- read_excel("/Users/danaraesparzamacias/Desktop/Concentracion AI /heart.xlsx")

# Convertir la variable target a FACTOR para modelo de clasificación
df$target <- as.factor(df$target) 

Entender 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   0:499  
##  1st Qu.:2.000   1:526  
##  Median :2.000          
##  Mean   :2.324          
##  3rd Qu.:3.000          
##  Max.   :3.000
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  : Factor w/ 2 levels "0","1": 1 1 1 1 1 2 1 1 1 1 ...
#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, ] #cuando despues de la coma no hay nada, considera todos los valores 
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 (svnLinear), Radial (svmRadial), Polinómico (svmPoly), etc.
  • Árbol de decisión: nnet
  • Random Forest o bosques aleatorios: rf

Modelo 1. SVM Lineal

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

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 301  37
##          1  99 384
##                                           
##                Accuracy : 0.8343          
##                  95% CI : (0.8071, 0.8592)
##     No Information Rate : 0.5128          
##     P-Value [Acc > NIR] : < 2.2e-16       
##                                           
##                   Kappa : 0.6672          
##                                           
##  Mcnemar's Test P-Value : 1.689e-07       
##                                           
##             Sensitivity : 0.7525          
##             Specificity : 0.9121          
##          Pos Pred Value : 0.8905          
##          Neg Pred Value : 0.7950          
##              Prevalence : 0.4872          
##          Detection Rate : 0.3666          
##    Detection Prevalence : 0.4117          
##       Balanced Accuracy : 0.8323          
##                                           
##        'Positive' Class : 0               
## 
#Matriz de confusión del resultado de la prueba
mcrp1 <- confusionMatrix(resultado_prueba1, prueba$target)
mcrp1
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction  0  1
##          0 78 10
##          1 21 95
##                                           
##                Accuracy : 0.848           
##                  95% CI : (0.7913, 0.8944)
##     No Information Rate : 0.5147          
##     P-Value [Acc > NIR] : < 2e-16         
##                                           
##                   Kappa : 0.6948          
##                                           
##  Mcnemar's Test P-Value : 0.07249         
##                                           
##             Sensitivity : 0.7879          
##             Specificity : 0.9048          
##          Pos Pred Value : 0.8864          
##          Neg Pred Value : 0.8190          
##              Prevalence : 0.4853          
##          Detection Rate : 0.3824          
##    Detection Prevalence : 0.4314          
##       Balanced Accuracy : 0.8463          
##                                           
##        'Positive' Class : 0               
## 

Modelo 2. SVM Radial

modelo2 <- train(target ~ ., data = entrenamiento,
                 method = "svmRadial",
                 preProcess = c("scale", "center"),
                 trControl = trainControl(method = "cv", number = 10),
                 tuneGrid = data.frame(sigma = 0.1, 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 385   7
##          1  15 414
##                                           
##                Accuracy : 0.9732          
##                  95% CI : (0.9597, 0.9831)
##     No Information Rate : 0.5128          
##     P-Value [Acc > NIR] : <2e-16          
##                                           
##                   Kappa : 0.9463          
##                                           
##  Mcnemar's Test P-Value : 0.1356          
##                                           
##             Sensitivity : 0.9625          
##             Specificity : 0.9834          
##          Pos Pred Value : 0.9821          
##          Neg Pred Value : 0.9650          
##              Prevalence : 0.4872          
##          Detection Rate : 0.4689          
##    Detection Prevalence : 0.4775          
##       Balanced Accuracy : 0.9729          
##                                           
##        '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  95   5
##          1   4 100
##                                           
##                Accuracy : 0.9559          
##                  95% CI : (0.9179, 0.9796)
##     No Information Rate : 0.5147          
##     P-Value [Acc > NIR] : <2e-16          
##                                           
##                   Kappa : 0.9117          
##                                           
##  Mcnemar's Test P-Value : 1               
##                                           
##             Sensitivity : 0.9596          
##             Specificity : 0.9524          
##          Pos Pred Value : 0.9500          
##          Neg Pred Value : 0.9615          
##              Prevalence : 0.4853          
##          Detection Rate : 0.4657          
##    Detection Prevalence : 0.4902          
##       Balanced Accuracy : 0.9560          
##                                           
##        'Positive' Class : 0               
## 

Modelo 3. SVM Polinómico

modelo3 <- train(target ~ ., data = entrenamiento,
                 method = "svmPoly",
                 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 398   1
##          1   2 420
##                                           
##                Accuracy : 0.9963          
##                  95% CI : (0.9894, 0.9992)
##     No Information Rate : 0.5128          
##     P-Value [Acc > NIR] : <2e-16          
##                                           
##                   Kappa : 0.9927          
##                                           
##  Mcnemar's Test P-Value : 1               
##                                           
##             Sensitivity : 0.9950          
##             Specificity : 0.9976          
##          Pos Pred Value : 0.9975          
##          Neg Pred Value : 0.9953          
##              Prevalence : 0.4872          
##          Detection Rate : 0.4848          
##    Detection Prevalence : 0.4860          
##       Balanced Accuracy : 0.9963          
##                                           
##        '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  97   2
##          1   2 103
##                                           
##                Accuracy : 0.9804          
##                  95% CI : (0.9506, 0.9946)
##     No Information Rate : 0.5147          
##     P-Value [Acc > NIR] : <2e-16          
##                                           
##                   Kappa : 0.9608          
##                                           
##  Mcnemar's Test P-Value : 1               
##                                           
##             Sensitivity : 0.9798          
##             Specificity : 0.9810          
##          Pos Pred Value : 0.9798          
##          Neg Pred Value : 0.9810          
##              Prevalence : 0.4853          
##          Detection Rate : 0.4755          
##    Detection Prevalence : 0.4853          
##       Balanced Accuracy : 0.9804          
##                                           
##        'Positive' Class : 0               
## 

Modelo 4. Árbol de decisión

modelo4 <- train(target ~ ., data = entrenamiento,
                 method = "rpart",
                 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 360  29
##          1  40 392
##                                          
##                Accuracy : 0.916          
##                  95% CI : (0.8948, 0.934)
##     No Information Rate : 0.5128         
##     P-Value [Acc > NIR] : <2e-16         
##                                          
##                   Kappa : 0.8317         
##                                          
##  Mcnemar's Test P-Value : 0.2286         
##                                          
##             Sensitivity : 0.9000         
##             Specificity : 0.9311         
##          Pos Pred Value : 0.9254         
##          Neg Pred Value : 0.9074         
##              Prevalence : 0.4872         
##          Detection Rate : 0.4385         
##    Detection Prevalence : 0.4738         
##       Balanced Accuracy : 0.9156         
##                                          
##        'Positive' Class : 0              
## 
#Matriz de confusión del resultado de la prueba
mcrp4 <- confusionMatrix(resultado_prueba4, prueba$target)
mcrp4 
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction  0  1
##          0 81  7
##          1 18 98
##                                           
##                Accuracy : 0.8775          
##                  95% CI : (0.8244, 0.9191)
##     No Information Rate : 0.5147          
##     P-Value [Acc > NIR] : <2e-16          
##                                           
##                   Kappa : 0.7539          
##                                           
##  Mcnemar's Test P-Value : 0.0455          
##                                           
##             Sensitivity : 0.8182          
##             Specificity : 0.9333          
##          Pos Pred Value : 0.9205          
##          Neg Pred Value : 0.8448          
##              Prevalence : 0.4853          
##          Detection Rate : 0.3971          
##    Detection Prevalence : 0.4314          
##       Balanced Accuracy : 0.8758          
##                                           
##        'Positive' Class : 0               
## 

Modelo 5. Bosques Aleatorios

modelo5 <- train(target ~ ., data = entrenamiento,
                 method = "rf",
                 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

#Matriz de confusión del resultado de entrenamiento 
mcre5 <- confusionMatrix(resultado_entrenamiento5, entrenamiento$target)
mcre5
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction   0   1
##          0 400   0
##          1   0 421
##                                      
##                Accuracy : 1          
##                  95% CI : (0.9955, 1)
##     No Information Rate : 0.5128     
##     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.4872     
##          Detection Rate : 0.4872     
##    Detection Prevalence : 0.4872     
##       Balanced Accuracy : 1.0000     
##                                      
##        'Positive' Class : 0          
## 
#Matriz de confusión del resultado de la prueba
mcrp5 <- confusionMatrix(resultado_prueba5, prueba$target)
mcrp5
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction   0   1
##          0  99   0
##          1   0 105
##                                      
##                Accuracy : 1          
##                  95% CI : (0.9821, 1)
##     No Information Rate : 0.5147     
##     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.4853     
##          Detection Rate : 0.4853     
##    Detection Prevalence : 0.4853     
##       Balanced Accuracy : 1.0000     
##                                      
##        'Positive' Class : 0          
## 

Modelo 6. Redes neuronalea

modelo6 <- train(target ~ ., data = entrenamiento,
                 method = "nnet",
                 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

#Matriz de confusión del resultado de entrenamiento 
mcre6 <- confusionMatrix(resultado_entrenamiento6, entrenamiento$target)
mcre6
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction   0   1
##          0 386  18
##          1  14 403
##                                           
##                Accuracy : 0.961           
##                  95% CI : (0.9454, 0.9732)
##     No Information Rate : 0.5128          
##     P-Value [Acc > NIR] : <2e-16          
##                                           
##                   Kappa : 0.922           
##                                           
##  Mcnemar's Test P-Value : 0.5959          
##                                           
##             Sensitivity : 0.9650          
##             Specificity : 0.9572          
##          Pos Pred Value : 0.9554          
##          Neg Pred Value : 0.9664          
##              Prevalence : 0.4872          
##          Detection Rate : 0.4702          
##    Detection Prevalence : 0.4921          
##       Balanced Accuracy : 0.9611          
##                                           
##        'Positive' Class : 0               
## 
#Matriz de confusión del resultado de la prueba
mcrp6 <- confusionMatrix(resultado_prueba6, prueba$target)
mcrp6
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction   0   1
##          0  98   5
##          1   1 100
##                                           
##                Accuracy : 0.9706          
##                  95% CI : (0.9371, 0.9891)
##     No Information Rate : 0.5147          
##     P-Value [Acc > NIR] : <2e-16          
##                                           
##                   Kappa : 0.9412          
##                                           
##  Mcnemar's Test P-Value : 0.2207          
##                                           
##             Sensitivity : 0.9899          
##             Specificity : 0.9524          
##          Pos Pred Value : 0.9515          
##          Neg Pred Value : 0.9901          
##              Prevalence : 0.4853          
##          Detection Rate : 0.4804          
##    Detection Prevalence : 0.5049          
##       Balanced Accuracy : 0.9711          
##                                           
##        'Positive' Class : 0               
## 

Tabla de resultados

resultados <- data.frame(
  "Medida" = c("Entrenamiento", "Prueba"),
  "svmLinear" = c(mcre1$overall["Accuracy"], mcrp1$overall["Accuracy"]),
  "svmRadial" = c(mcre2$overall["Accuracy"], mcrp2$overall["Accuracy"]),
  "svmPoly"   = 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"])
)
resultados
##          Medida svmLinear svmRadial   svmPoly     rpart rf      nnet
## 1 Entrenamiento 0.8343484 0.9732034 0.9963459 0.9159562  1 0.9610231
## 2        Prueba 0.8480392 0.9558824 0.9803922 0.8774510  1 0.9705882

Conclusión

En conclusión, el modelo de Bosques Aleatorios (rf) es el recomendado para la predicción de cardiopatías en este conjunto de datos, al alcanzar una precisión (Accuracy) perfecta del 100% en la evaluación con los datos de prueba.

LS0tCnRpdGxlOiAiQ0FSRVQgLSBIZWFydCIKYXV0aG9yOiAiRGFuYXJhIEVzcGFyemEgQTAwODQwNjAyIgpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiCm91dHB1dDogCiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogVFJVRQogICAgdG9jX2Zsb2F0OiBUUlVFCiAgICBjb2RlX2Rvd25sb2FkOiBUUlVFCiAgICB0aGVtZTogY29zbW8KLS0tCgohW10oaHR0cHM6Ly93d3cuaW5mb2JhZS5jb20vcmVzaXplci92Mi9RM0tRTk82TlpWR0FYQUlKWEc0UzdBQjJSSS5qcGc/YXV0aD0zNTk2NmE0YjcxMGFkMGIwOTA5YmJlZWRhNDA4YTFjMjYzNDI2ZDI0OWYzZTVhZWY0MGY5NmFiN2MxYWJmOTJkJnNtYXJ0PXRydWUmd2lkdGg9MTIwMCZoZWlnaHQ9Njc1JnF1YWxpdHk9ODUpIAoKIyA8c3BhbiBzdHlsZT0gImNvbG9yOiBibHVlIj4gVGVvcsOtYSA8L3NwYW4+IApFbCBwYXF1ZXRlICoqQ0FSRVQgKENsYXNzaWZpY2F0aW9uIGFuZCBSZWdyZXNzaW9uIFRyYWluaW5nKSoqIGVzIHVuIHBhcXVldGUgaW50ZWdyYWwgY29uIHVuYSBhbXBsaWEgdmFyaWVkYWQgZGUgYWxnb3JpdG1vcyBwYXJhIGVsIGFwcmVuZGl6YWplIGF1dG9tw6F0aWNvLgoKIyA8c3BhbiBzdHlsZT0gImNvbG9yOiBibHVlIj4gSW5zdGFsYXIgcGFxdWV0ZXMgeSBsbGFtYXIgbGlicmVyw61hcyA8L3NwYW4+CmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiNpbnN0YWxsLnBhY2thZ2VzKCJjYXJldCIpICMgYWxnb3JpdG1vcyBkZSBhcHJlbmRpemFqZSBhdXRvbcOhdGljbwpsaWJyYXJ5KGNhcmV0KQoKI2luc3RhbGwucGFja2FnZXMoImdncGxvdDIiKSAjIGdyYWZpY2FzCmxpYnJhcnkoZ2dwbG90MikKCiNpbnN0YWxsLnBhY2thZ2VzKCJsYXR0aWNlIikgI2NyZWFyIGdyw6FmaWNvcwpsaWJyYXJ5KGxhdHRpY2UpCgojaW5zdGFsbC5wYWNrYWdlcygicmVhZHhsIikgIyBwYXJhIGNhcmdhciBhcmNoaXZvcyBleGNlbApsaWJyYXJ5KHJlYWR4bCkKCiNpbnN0YWxsLnBhY2thZ2VzKCJEYXRhRXhwbG9yZXIiKSAjYW7DoWxpc2lzIGRlc2NyaXB0aXZvCmxpYnJhcnkoRGF0YUV4cGxvcmVyKSAKCiNpbnN0YWxsLnBhY2thZ2VzKCJrZXJubGFiIikKbGlicmFyeShrZXJubGFiKQoKI2luc3RhbGwucGFja2FnZXMoInJhbmRvbUZvcmVzdCIpCmxpYnJhcnkocmFuZG9tRm9yZXN0KQoKI2luc3RhbGwucGFja2FnZXMoInJwYXJ0IikKbGlicmFyeShycGFydCkKCiNpbnN0YWxsLnBhY2thZ2VzKCJubmV0IikKbGlicmFyeShubmV0KQpgYGAKCiMgPHNwYW4gc3R5bGU9ICJjb2xvcjogYmx1ZSI+IEltcG9ydGFyIGJhc2UgZGUgZGF0b3MgPC9zcGFuPgpgYGB7cn0KZGYgPC0gcmVhZF9leGNlbCgiL1VzZXJzL2RhbmFyYWVzcGFyemFtYWNpYXMvRGVza3RvcC9Db25jZW50cmFjaW9uIEFJIC9oZWFydC54bHN4IikKCiMgQ29udmVydGlyIGxhIHZhcmlhYmxlIHRhcmdldCBhIEZBQ1RPUiBwYXJhIG1vZGVsbyBkZSBjbGFzaWZpY2FjacOzbgpkZiR0YXJnZXQgPC0gYXMuZmFjdG9yKGRmJHRhcmdldCkgCgpgYGAKCiMgPHNwYW4gc3R5bGU9ICJjb2xvcjogYmx1ZSI+IEVudGVuZGVyIGJhc2UgZGUgZGF0b3MgPC9zcGFuPiAKYGBge3J9CnN1bW1hcnkoZGYpCnN0cihkZikKI2NyZWF0ZV9yZXBvcnQoZGYpIAoKcGxvdF9taXNzaW5nKGRmKSAKcGxvdF9oaXN0b2dyYW0oZGYpCnBsb3RfY29ycmVsYXRpb24oZGYpCgpgYGAKCiMgPHNwYW4gc3R5bGU9ICJjb2xvcjogYmx1ZSI+IFBhcnRpciBsYSBiYXNlIGRlIGRhdG9zIDwvc3Bhbj4KYGBge3J9CiMgTm9ybWFsbWVudGUgODAtMjAgbyA3MC0zMApzZXQuc2VlZCgxMjMpCnJlbmdsb25lc19lbnRyZW5hbWllbnRvIDwtIGNyZWF0ZURhdGFQYXJ0aXRpb24oZGYkdGFyZ2V0LCBwPTAuOCwgbGlzdD0gRkFMU0UpIAplbnRyZW5hbWllbnRvIDwtIGRmW3Jlbmdsb25lc19lbnRyZW5hbWllbnRvLCBdICNjdWFuZG8gZGVzcHVlcyBkZSBsYSBjb21hIG5vIGhheSBuYWRhLCBjb25zaWRlcmEgdG9kb3MgbG9zIHZhbG9yZXMgCnBydWViYSA8LSBkZlstcmVuZ2xvbmVzX2VudHJlbmFtaWVudG8sIF0gCgpgYGAKCgojIDxzcGFuIHN0eWxlPSAiY29sb3I6IGJsdWUiPiBEaXN0aW50b3MgdGlwb3MgZGUgbcOpdG9kb3MgcGFyYSBNb2RlbGFyIDwvc3Bhbj4KTG9zIG3DqXRvZG9zIG3DoXMgdXRpbGl6YWRvcyBwYXJhIG1vZGVsYXIgYXByZW5kaXphamUgYXV0b23DoXRpY28gc29uOiAKCiogKipTVk0qKjogKlN1cHBvcnQgVmVjdG9yIE1hY2hpbmUqIG8gTcOhcXVpbmEgZGUgVmVjdG9yZXMgZGUgU29wb3J0ZS4gSGF5IHZhcmlvcyBzdWJ0aXBvczogTGluZWFsIChzdm5MaW5lYXIpLCBSYWRpYWwgKHN2bVJhZGlhbCksIFBvbGluw7NtaWNvIChzdm1Qb2x5KSwgZXRjLiAKKiAqKsOBcmJvbCBkZSBkZWNpc2nDs24qKjogbm5ldAoqICoqUmFuZG9tIEZvcmVzdCoqIG8gYm9zcXVlcyBhbGVhdG9yaW9zOiByZgoKIyA8c3BhbiBzdHlsZT0gImNvbG9yOiBibHVlIj4gTW9kZWxvIDEuIFNWTSBMaW5lYWwgPC9zcGFuPgpgYGB7cn0KbW9kZWxvMSA8LSB0cmFpbih0YXJnZXQgfiAuLCBkYXRhID0gZW50cmVuYW1pZW50bywKICAgICAgICAgICAgICAgICBtZXRob2QgPSAic3ZtTGluZWFyIiwKICAgICAgICAgICAgICAgICBwcmVQcm9jZXNzID0gYygic2NhbGUiLCAiY2VudGVyIiksCiAgICAgICAgICAgICAgICAgdHJDb250cm9sID0gdHJhaW5Db250cm9sKG1ldGhvZCA9ICJjdiIsIG51bWJlciA9IDEwKSwKICAgICAgICAgICAgICAgICB0dW5lR3JpZCA9IGRhdGEuZnJhbWUoQyA9IDEpCiAgICAgICAgICAgICAgICAgKSAKCnJlc3VsdGFkb19lbnRyZW5hbWllbnRvMSA8LSBwcmVkaWN0KG1vZGVsbzEsIGVudHJlbmFtaWVudG8pCnJlc3VsdGFkb19wcnVlYmExIDwtIHByZWRpY3QobW9kZWxvMSwgcHJ1ZWJhKSAKCiNNYXRyaXogZGUgY29uZnVzacOzbiAKIyBFcyB1bmEgdGFibGEgZGUgZXZhbHVhY2nDs24gcXVlIGRlc2dsb3NhIGVsIHJlbmRpbWllbnRvIGRlbCBtb2RlbG8gZGUgY2xhc2lmaWNhY2nDs24KCiNNYXRyaXogZGUgY29uZnVzacOzbiBkZWwgcmVzdWx0YWRvIGRlIGVudHJlbmFtaWVudG8gCm1jcmUxIDwtIGNvbmZ1c2lvbk1hdHJpeChyZXN1bHRhZG9fZW50cmVuYW1pZW50bzEsIGVudHJlbmFtaWVudG8kdGFyZ2V0KQptY3JlMQoKI01hdHJpeiBkZSBjb25mdXNpw7NuIGRlbCByZXN1bHRhZG8gZGUgbGEgcHJ1ZWJhCm1jcnAxIDwtIGNvbmZ1c2lvbk1hdHJpeChyZXN1bHRhZG9fcHJ1ZWJhMSwgcHJ1ZWJhJHRhcmdldCkKbWNycDEKYGBgCiMgPHNwYW4gc3R5bGU9ICJjb2xvcjogYmx1ZSI+IE1vZGVsbyAyLiBTVk0gUmFkaWFsIDwvc3Bhbj4KYGBge3J9Cm1vZGVsbzIgPC0gdHJhaW4odGFyZ2V0IH4gLiwgZGF0YSA9IGVudHJlbmFtaWVudG8sCiAgICAgICAgICAgICAgICAgbWV0aG9kID0gInN2bVJhZGlhbCIsCiAgICAgICAgICAgICAgICAgcHJlUHJvY2VzcyA9IGMoInNjYWxlIiwgImNlbnRlciIpLAogICAgICAgICAgICAgICAgIHRyQ29udHJvbCA9IHRyYWluQ29udHJvbChtZXRob2QgPSAiY3YiLCBudW1iZXIgPSAxMCksCiAgICAgICAgICAgICAgICAgdHVuZUdyaWQgPSBkYXRhLmZyYW1lKHNpZ21hID0gMC4xLCBDID0gMSkKICAgICAgICAgICAgICAgICApIAoKcmVzdWx0YWRvX2VudHJlbmFtaWVudG8yIDwtIHByZWRpY3QobW9kZWxvMiwgZW50cmVuYW1pZW50bykKcmVzdWx0YWRvX3BydWViYTIgPC0gcHJlZGljdChtb2RlbG8yLCBwcnVlYmEpIAoKI01hdHJpeiBkZSBjb25mdXNpw7NuIAojIEVzIHVuYSB0YWJsYSBkZSBldmFsdWFjacOzbiBxdWUgZGVzZ2xvc2EgZWwgcmVuZGltaWVudG8gZGVsIG1vZGVsbyBkZSBjbGFzaWZpY2FjacOzbgoKI01hdHJpeiBkZSBjb25mdXNpw7NuIGRlbCByZXN1bHRhZG8gZGUgZW50cmVuYW1pZW50byAKbWNyZTIgPC0gY29uZnVzaW9uTWF0cml4KHJlc3VsdGFkb19lbnRyZW5hbWllbnRvMiwgZW50cmVuYW1pZW50byR0YXJnZXQpCm1jcmUyCgojTWF0cml6IGRlIGNvbmZ1c2nDs24gZGVsIHJlc3VsdGFkbyBkZSBsYSBwcnVlYmEKbWNycDIgPC0gY29uZnVzaW9uTWF0cml4KHJlc3VsdGFkb19wcnVlYmEyLCBwcnVlYmEkdGFyZ2V0KQptY3JwMgpgYGAKCiMgPHNwYW4gc3R5bGU9ICJjb2xvcjogYmx1ZSI+IE1vZGVsbyAzLiBTVk0gUG9saW7Ds21pY28gPC9zcGFuPgpgYGB7cn0KbW9kZWxvMyA8LSB0cmFpbih0YXJnZXQgfiAuLCBkYXRhID0gZW50cmVuYW1pZW50bywKICAgICAgICAgICAgICAgICBtZXRob2QgPSAic3ZtUG9seSIsCiAgICAgICAgICAgICAgICAgcHJlUHJvY2VzcyA9IGMoInNjYWxlIiwgImNlbnRlciIpLAogICAgICAgICAgICAgICAgIHRyQ29udHJvbCA9IHRyYWluQ29udHJvbChtZXRob2QgPSAiY3YiLCBudW1iZXIgPSAxMCksCiAgICAgICAgICAgICAgICAgdHVuZUdyaWQgPSBkYXRhLmZyYW1lKGRlZ3JlZSA9IDIsIHNjYWxlID0gMSwgQyA9IDEpCiAgICAgICAgICAgICAgICAgKSAKCnJlc3VsdGFkb19lbnRyZW5hbWllbnRvMyA8LSBwcmVkaWN0KG1vZGVsbzMsIGVudHJlbmFtaWVudG8pCnJlc3VsdGFkb19wcnVlYmEzIDwtIHByZWRpY3QobW9kZWxvMywgcHJ1ZWJhKSAKCiNNYXRyaXogZGUgY29uZnVzacOzbiAKIyBFcyB1bmEgdGFibGEgZGUgZXZhbHVhY2nDs24gcXVlIGRlc2dsb3NhIGVsIHJlbmRpbWllbnRvIGRlbCBtb2RlbG8gZGUgY2xhc2lmaWNhY2nDs24KCiNNYXRyaXogZGUgY29uZnVzacOzbiBkZWwgcmVzdWx0YWRvIGRlIGVudHJlbmFtaWVudG8gCm1jcmUzIDwtIGNvbmZ1c2lvbk1hdHJpeChyZXN1bHRhZG9fZW50cmVuYW1pZW50bzMsIGVudHJlbmFtaWVudG8kdGFyZ2V0KQptY3JlMwoKI01hdHJpeiBkZSBjb25mdXNpw7NuIGRlbCByZXN1bHRhZG8gZGUgbGEgcHJ1ZWJhCm1jcnAzIDwtIGNvbmZ1c2lvbk1hdHJpeChyZXN1bHRhZG9fcHJ1ZWJhMywgcHJ1ZWJhJHRhcmdldCkKbWNycDMKYGBgCiMgPHNwYW4gc3R5bGU9ICJjb2xvcjogYmx1ZSI+IE1vZGVsbyA0LiDDgXJib2wgZGUgZGVjaXNpw7NuIDwvc3Bhbj4KYGBge3J9Cm1vZGVsbzQgPC0gdHJhaW4odGFyZ2V0IH4gLiwgZGF0YSA9IGVudHJlbmFtaWVudG8sCiAgICAgICAgICAgICAgICAgbWV0aG9kID0gInJwYXJ0IiwKICAgICAgICAgICAgICAgICBwcmVQcm9jZXNzID0gYygic2NhbGUiLCAiY2VudGVyIiksCiAgICAgICAgICAgICAgICAgdHJDb250cm9sID0gdHJhaW5Db250cm9sKG1ldGhvZCA9ICJjdiIsIG51bWJlciA9IDEwKSwKICAgICAgICAgICAgICAgICB0dW5lTGVuZ3RoID0gMTAKICAgICAgICAgICAgICAgICApIAoKcmVzdWx0YWRvX2VudHJlbmFtaWVudG80IDwtIHByZWRpY3QobW9kZWxvNCwgZW50cmVuYW1pZW50bykKcmVzdWx0YWRvX3BydWViYTQgPC0gcHJlZGljdChtb2RlbG80LCBwcnVlYmEpIAoKI01hdHJpeiBkZSBjb25mdXNpw7NuIAojIEVzIHVuYSB0YWJsYSBkZSBldmFsdWFjacOzbiBxdWUgZGVzZ2xvc2EgZWwgcmVuZGltaWVudG8gZGVsIG1vZGVsbyBkZSBjbGFzaWZpY2FjacOzbgoKI01hdHJpeiBkZSBjb25mdXNpw7NuIGRlbCByZXN1bHRhZG8gZGUgZW50cmVuYW1pZW50byAKbWNyZTQgPC0gY29uZnVzaW9uTWF0cml4KHJlc3VsdGFkb19lbnRyZW5hbWllbnRvNCwgZW50cmVuYW1pZW50byR0YXJnZXQpCm1jcmU0CgojTWF0cml6IGRlIGNvbmZ1c2nDs24gZGVsIHJlc3VsdGFkbyBkZSBsYSBwcnVlYmEKbWNycDQgPC0gY29uZnVzaW9uTWF0cml4KHJlc3VsdGFkb19wcnVlYmE0LCBwcnVlYmEkdGFyZ2V0KQptY3JwNCAKYGBgCgojIDxzcGFuIHN0eWxlPSAiY29sb3I6IGJsdWUiPiBNb2RlbG8gNS4gQm9zcXVlcyBBbGVhdG9yaW9zIDwvc3Bhbj4KYGBge3J9Cm1vZGVsbzUgPC0gdHJhaW4odGFyZ2V0IH4gLiwgZGF0YSA9IGVudHJlbmFtaWVudG8sCiAgICAgICAgICAgICAgICAgbWV0aG9kID0gInJmIiwKICAgICAgICAgICAgICAgICBwcmVQcm9jZXNzID0gYygic2NhbGUiLCAiY2VudGVyIiksCiAgICAgICAgICAgICAgICAgdHJDb250cm9sID0gdHJhaW5Db250cm9sKG1ldGhvZCA9ICJjdiIsIG51bWJlciA9IDEwKSwKICAgICAgICAgICAgICAgICB0dW5lR3JpZCA9IGV4cGFuZC5ncmlkKG10cnkgPSBjKDIsIDQsIDYsIDgpKQogICAgICAgICAgICAgICAgICkgCgpyZXN1bHRhZG9fZW50cmVuYW1pZW50bzUgPC0gcHJlZGljdChtb2RlbG81LCBlbnRyZW5hbWllbnRvKQpyZXN1bHRhZG9fcHJ1ZWJhNSA8LSBwcmVkaWN0KG1vZGVsbzUsIHBydWViYSkgCgojTWF0cml6IGRlIGNvbmZ1c2nDs24gCiMgRXMgdW5hIHRhYmxhIGRlIGV2YWx1YWNpw7NuIHF1ZSBkZXNnbG9zYSBlbCByZW5kaW1pZW50byBkZWwgbW9kZWxvIGRlIGNsYXNpZmljYWNpw7NuCgojTWF0cml6IGRlIGNvbmZ1c2nDs24gZGVsIHJlc3VsdGFkbyBkZSBlbnRyZW5hbWllbnRvIAptY3JlNSA8LSBjb25mdXNpb25NYXRyaXgocmVzdWx0YWRvX2VudHJlbmFtaWVudG81LCBlbnRyZW5hbWllbnRvJHRhcmdldCkKbWNyZTUKCiNNYXRyaXogZGUgY29uZnVzacOzbiBkZWwgcmVzdWx0YWRvIGRlIGxhIHBydWViYQptY3JwNSA8LSBjb25mdXNpb25NYXRyaXgocmVzdWx0YWRvX3BydWViYTUsIHBydWViYSR0YXJnZXQpCm1jcnA1CmBgYAojIDxzcGFuIHN0eWxlPSAiY29sb3I6IGJsdWUiPiBNb2RlbG8gNi4gUmVkZXMgbmV1cm9uYWxlYSA8L3NwYW4+CmBgYHtyfQptb2RlbG82IDwtIHRyYWluKHRhcmdldCB+IC4sIGRhdGEgPSBlbnRyZW5hbWllbnRvLAogICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJubmV0IiwKICAgICAgICAgICAgICAgICBwcmVQcm9jZXNzID0gYygic2NhbGUiLCAiY2VudGVyIiksCiAgICAgICAgICAgICAgICAgdHJDb250cm9sID0gdHJhaW5Db250cm9sKG1ldGhvZCA9ICJjdiIsIG51bWJlciA9IDEwKSwKICAgICAgICAgICAgICAgICB0cmFjZSA9IEZBTFNFCiAgICAgICAgICAgICAgICAgKSAKCnJlc3VsdGFkb19lbnRyZW5hbWllbnRvNiA8LSBwcmVkaWN0KG1vZGVsbzYsIGVudHJlbmFtaWVudG8pCnJlc3VsdGFkb19wcnVlYmE2IDwtIHByZWRpY3QobW9kZWxvNiwgcHJ1ZWJhKSAKCiNNYXRyaXogZGUgY29uZnVzacOzbiAKIyBFcyB1bmEgdGFibGEgZGUgZXZhbHVhY2nDs24gcXVlIGRlc2dsb3NhIGVsIHJlbmRpbWllbnRvIGRlbCBtb2RlbG8gZGUgY2xhc2lmaWNhY2nDs24KCiNNYXRyaXogZGUgY29uZnVzacOzbiBkZWwgcmVzdWx0YWRvIGRlIGVudHJlbmFtaWVudG8gCm1jcmU2IDwtIGNvbmZ1c2lvbk1hdHJpeChyZXN1bHRhZG9fZW50cmVuYW1pZW50bzYsIGVudHJlbmFtaWVudG8kdGFyZ2V0KQptY3JlNgoKI01hdHJpeiBkZSBjb25mdXNpw7NuIGRlbCByZXN1bHRhZG8gZGUgbGEgcHJ1ZWJhCm1jcnA2IDwtIGNvbmZ1c2lvbk1hdHJpeChyZXN1bHRhZG9fcHJ1ZWJhNiwgcHJ1ZWJhJHRhcmdldCkKbWNycDYKYGBgCiMgPHNwYW4gc3R5bGU9ICJjb2xvcjogYmx1ZSI+IFRhYmxhIGRlIHJlc3VsdGFkb3MgPC9zcGFuPgpgYGB7cn0KcmVzdWx0YWRvcyA8LSBkYXRhLmZyYW1lKAogICJNZWRpZGEiID0gYygiRW50cmVuYW1pZW50byIsICJQcnVlYmEiKSwKICAic3ZtTGluZWFyIiA9IGMobWNyZTEkb3ZlcmFsbFsiQWNjdXJhY3kiXSwgbWNycDEkb3ZlcmFsbFsiQWNjdXJhY3kiXSksCiAgInN2bVJhZGlhbCIgPSBjKG1jcmUyJG92ZXJhbGxbIkFjY3VyYWN5Il0sIG1jcnAyJG92ZXJhbGxbIkFjY3VyYWN5Il0pLAogICJzdm1Qb2x5IiAgID0gYyhtY3JlMyRvdmVyYWxsWyJBY2N1cmFjeSJdLCBtY3JwMyRvdmVyYWxsWyJBY2N1cmFjeSJdKSwKICAicnBhcnQiICAgICA9IGMobWNyZTQkb3ZlcmFsbFsiQWNjdXJhY3kiXSwgbWNycDQkb3ZlcmFsbFsiQWNjdXJhY3kiXSksCiAgInJmIiAgICAgICAgPSBjKG1jcmU1JG92ZXJhbGxbIkFjY3VyYWN5Il0sIG1jcnA1JG92ZXJhbGxbIkFjY3VyYWN5Il0pLAogICJubmV0IiAgICAgID0gYyhtY3JlNiRvdmVyYWxsWyJBY2N1cmFjeSJdLCBtY3JwNiRvdmVyYWxsWyJBY2N1cmFjeSJdKQopCnJlc3VsdGFkb3MKYGBgCgojIDxzcGFuIHN0eWxlPSAiY29sb3I6IGJsdWUiPiBDb25jbHVzacOzbiA8L3NwYW4+CkVuIGNvbmNsdXNpw7NuLCBlbCBtb2RlbG8gZGUgKipCb3NxdWVzIEFsZWF0b3Jpb3MgKHJmKSoqIGVzIGVsIHJlY29tZW5kYWRvIHBhcmEgbGEgcHJlZGljY2nDs24gZGUgY2FyZGlvcGF0w61hcyBlbiBlc3RlIGNvbmp1bnRvIGRlIGRhdG9zLCBhbCBhbGNhbnphciB1bmEgcHJlY2lzacOzbiAoKkFjY3VyYWN5KikgcGVyZmVjdGEgZGVsICoqMTAwJSoqIGVuIGxhIGV2YWx1YWNpw7NuIGNvbiBsb3MgZGF0b3MgZGUgcHJ1ZWJhLiAK