Teoria

El paquete caret (Clasification and Regression Training) es un paquete integral con una amplia variedad de algoritmos para el aprendizaje automatico.

Instalar paquetes y llamar librerias

# install.packages("ggplot2") #Graficas con mejor diseno
library(ggplot2)
# install.packages("lattice") #Crear Graficos
library(lattice)
# install.packages("caret") # Algoritmos de aprendizaje automatico
library(caret)
# install.packages("datasets") # Usar la base de datos "Iris"
library(datasets)
# install.packages("DataExplorer") # Exploracion de datos
library(DataExplorer)
# install.packages("kernlab") # Paquete con metodos de aprendizaje automatico
library(kernlab)
## 
## Attaching package: 'kernlab'
## The following object is masked from 'package:ggplot2':
## 
##     alpha
# install.packages("randomForest") # Paquete para este metodo de clasificacion 
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:ggplot2':
## 
##     margin
# install.packages("tidyverse")
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.1
## ✔ lubridate 1.9.3     ✔ tibble    3.2.1
## ✔ purrr     1.0.2     ✔ tidyr     1.3.1
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ kernlab::alpha()       masks ggplot2::alpha()
## ✖ dplyr::combine()       masks randomForest::combine()
## ✖ purrr::cross()         masks kernlab::cross()
## ✖ dplyr::filter()        masks stats::filter()
## ✖ dplyr::lag()           masks stats::lag()
## ✖ purrr::lift()          masks caret::lift()
## ✖ randomForest::margin() masks ggplot2::margin()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
# file.choose()
# Cargar el dataset
heart_data <- read.csv("/Users/constantinomilletxacur/Desktop/Concentracion/Modulo 2/heart.csv")

# Convertir variables categóricas a factores
heart_data$sex <- as.factor(heart_data$sex)
heart_data$cp <- as.factor(heart_data$cp)
heart_data$fbs <- as.factor(heart_data$fbs)
heart_data$restecg <- as.factor(heart_data$restecg)
heart_data$exang <- as.factor(heart_data$exang)
heart_data$slope <- as.factor(heart_data$slope)
heart_data$ca <- as.factor(heart_data$ca)
heart_data$thal <- as.factor(heart_data$thal)
heart_data$target <- as.factor(heart_data$target)

Partir los Datos 80-20

set.seed(123)
trainIndex <- createDataPartition(heart_data$target, p = 0.8, list = FALSE)
heart_train <- heart_data[trainIndex, ]
heart_test <- heart_data[-trainIndex, ]

Modelos para Clasificación SVM (Máquina de Vectores de Soporte) SVM con Kernel Lineal

svm_linear_model <- train(target ~ ., data = heart_train, method = "svmLinear", trControl = trainControl(method = "cv", number = 5))

# Predicciones en el conjunto de entrenamiento
svm_linear_train_predictions <- predict(svm_linear_model, newdata = heart_train)
mcre1 <- confusionMatrix(svm_linear_train_predictions, heart_train$target)

# Predicciones en el conjunto de prueba
svm_linear_test_predictions <- predict(svm_linear_model, newdata = heart_test)
mcrp1 <- confusionMatrix(svm_linear_test_predictions, heart_test$target)

mcrp1
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction  0  1
##          0 84  7
##          1 15 98
##                                           
##                Accuracy : 0.8922          
##                  95% CI : (0.8413, 0.9312)
##     No Information Rate : 0.5147          
##     P-Value [Acc > NIR] : <2e-16          
##                                           
##                   Kappa : 0.7836          
##                                           
##  Mcnemar's Test P-Value : 0.1356          
##                                           
##             Sensitivity : 0.8485          
##             Specificity : 0.9333          
##          Pos Pred Value : 0.9231          
##          Neg Pred Value : 0.8673          
##              Prevalence : 0.4853          
##          Detection Rate : 0.4118          
##    Detection Prevalence : 0.4461          
##       Balanced Accuracy : 0.8909          
##                                           
##        'Positive' Class : 0               
## 

SVM con Kernel Radial

svm_radial_model <- train(target ~ ., data = heart_train, method = "svmRadial", trControl = trainControl(method = "cv", number = 5))

# Predicciones en el conjunto de entrenamiento
svm_radial_train_predictions <- predict(svm_radial_model, newdata = heart_train)
mcre2 <- confusionMatrix(svm_radial_train_predictions, heart_train$target)

# Predicciones en el conjunto de prueba
svm_radial_test_predictions <- predict(svm_radial_model, newdata = heart_test)
mcrp2 <- confusionMatrix(svm_radial_test_predictions, heart_test$target)

mcrp2
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction  0  1
##          0 93  6
##          1  6 99
##                                           
##                Accuracy : 0.9412          
##                  95% CI : (0.8995, 0.9692)
##     No Information Rate : 0.5147          
##     P-Value [Acc > NIR] : <2e-16          
##                                           
##                   Kappa : 0.8823          
##                                           
##  Mcnemar's Test P-Value : 1               
##                                           
##             Sensitivity : 0.9394          
##             Specificity : 0.9429          
##          Pos Pred Value : 0.9394          
##          Neg Pred Value : 0.9429          
##              Prevalence : 0.4853          
##          Detection Rate : 0.4559          
##    Detection Prevalence : 0.4853          
##       Balanced Accuracy : 0.9411          
##                                           
##        'Positive' Class : 0               
## 

SVM con Kernel Polinómico

svm_poly_model <- train(target ~ ., data = heart_train, method = "svmPoly", trControl = trainControl(method = "cv", number = 5))

# Predicciones en el conjunto de entrenamiento
svm_poly_train_predictions <- predict(svm_poly_model, newdata = heart_train)
mcre3 <- confusionMatrix(svm_poly_train_predictions, heart_train$target)

# Predicciones en el conjunto de prueba
svm_poly_test_predictions <- predict(svm_poly_model, newdata = heart_test)
mcrp3 <- confusionMatrix(svm_poly_test_predictions, heart_test$target)

mcrp3
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction   0   1
##          0  99   3
##          1   0 102
##                                          
##                Accuracy : 0.9853         
##                  95% CI : (0.9576, 0.997)
##     No Information Rate : 0.5147         
##     P-Value [Acc > NIR] : <2e-16         
##                                          
##                   Kappa : 0.9706         
##                                          
##  Mcnemar's Test P-Value : 0.2482         
##                                          
##             Sensitivity : 1.0000         
##             Specificity : 0.9714         
##          Pos Pred Value : 0.9706         
##          Neg Pred Value : 1.0000         
##              Prevalence : 0.4853         
##          Detection Rate : 0.4853         
##    Detection Prevalence : 0.5000         
##       Balanced Accuracy : 0.9857         
##                                          
##        'Positive' Class : 0              
## 

Árbol de Decisión

rpart_model <- train(target ~ ., data = heart_train, method = "rpart", trControl = trainControl(method = "cv", number = 5))

# Predicciones en el conjunto de entrenamiento
rpart_train_predictions <- predict(rpart_model, newdata = heart_train)
mcre4 <- confusionMatrix(rpart_train_predictions, heart_train$target)

# Predicciones en el conjunto de prueba
rpart_test_predictions <- predict(rpart_model, newdata = heart_test)
mcrp4 <- confusionMatrix(rpart_test_predictions, heart_test$target)

mcrp4
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction  0  1
##          0 74 14
##          1 25 91
##                                           
##                Accuracy : 0.8088          
##                  95% CI : (0.7481, 0.8604)
##     No Information Rate : 0.5147          
##     P-Value [Acc > NIR] : <2e-16          
##                                           
##                   Kappa : 0.6161          
##                                           
##  Mcnemar's Test P-Value : 0.1093          
##                                           
##             Sensitivity : 0.7475          
##             Specificity : 0.8667          
##          Pos Pred Value : 0.8409          
##          Neg Pred Value : 0.7845          
##              Prevalence : 0.4853          
##          Detection Rate : 0.3627          
##    Detection Prevalence : 0.4314          
##       Balanced Accuracy : 0.8071          
##                                           
##        'Positive' Class : 0               
## 

Redes Neuronales

nnet_model <- train(target ~ ., data = heart_train, method = "nnet", trControl = trainControl(method = "cv", number = 5), trace = FALSE)

# Predicciones en el conjunto de entrenamiento
nnet_train_predictions <- predict(nnet_model, newdata = heart_train)
mcre5 <- confusionMatrix(nnet_train_predictions, heart_train$target)

# Predicciones en el conjunto de prueba
nnet_test_predictions <- predict(nnet_model, newdata = heart_test)
mcrp5 <- confusionMatrix(nnet_test_predictions, heart_test$target)

mcrp5
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction  0  1
##          0 92  7
##          1  7 98
##                                          
##                Accuracy : 0.9314         
##                  95% CI : (0.8875, 0.962)
##     No Information Rate : 0.5147         
##     P-Value [Acc > NIR] : <2e-16         
##                                          
##                   Kappa : 0.8626         
##                                          
##  Mcnemar's Test P-Value : 1              
##                                          
##             Sensitivity : 0.9293         
##             Specificity : 0.9333         
##          Pos Pred Value : 0.9293         
##          Neg Pred Value : 0.9333         
##              Prevalence : 0.4853         
##          Detection Rate : 0.4510         
##    Detection Prevalence : 0.4853         
##       Balanced Accuracy : 0.9313         
##                                          
##        'Positive' Class : 0              
## 

Random Forest

rf_model <- train(target ~ ., data = heart_train, method = "rf", trControl = trainControl(method = "cv", number = 5))

# Predicciones en el conjunto de entrenamiento
rf_train_predictions <- predict(rf_model, newdata = heart_train)
mcre6 <- confusionMatrix(rf_train_predictions, heart_train$target)

# Predicciones en el conjunto de prueba
rf_test_predictions <- predict(rf_model, newdata = heart_test)
mcrp6 <- confusionMatrix(rf_test_predictions, heart_test$target)

mcrp6
## 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          
## 
resultados <- data.frame(
  "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"]),
  "nnet"=c(mcre5$overall["Accuracy"], mcrp5$overall["Accuracy"]),
  "rf"=c(mcre6$overall["Accuracy"], mcrp6$overall["Accuracy"])
)
rownames(resultados) <- c("Precisión de Entrenamiento", "Precisión de Prueba")
resultados
##                            svmLinear svmRadial   svmPoly     rpart      nnet rf
## Precisión de Entrenamiento 0.8952497 0.9354446 1.0000000 0.7917174 0.9086480  1
## Precisión de Prueba        0.8921569 0.9411765 0.9852941 0.8088235 0.9313725  1

Conclusiones

Acorde al resumen de resultados, el modelo mejor evaluado es el de svmRadial.

LS0tCnRpdGxlOiAiQ0FSRVQgSEVBUlQiCmF1dGhvcjogIkNvbnN0YW50aW5vIE1pbGxldCBYYWN1ciIKZGF0ZTogIjIwMjQtMDgtMjEiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OiAKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgICB0aGVtZTogY2VydWxlYW4KLS0tCgohW10oL1VzZXJzL2NvbnN0YW50aW5vbWlsbGV0eGFjdXIvRGVza3RvcC9Db25jZW50cmFjaW9uL01vZHVsbyAyL0ZvdG9zIG0yL2hlYXJ0LmpwZykKCgojIDxzcGFuIHN0eWxlPSJjb2xvcjogdmlvbGV0OyI+VGVvcmlhPC9zcGFuPgpFbCBwYXF1ZXRlICpjYXJldCAoQ2xhc2lmaWNhdGlvbiBhbmQgUmVncmVzc2lvbiBUcmFpbmluZykqIGVzIHVuIHBhcXVldGUgaW50ZWdyYWwgY29uIHVuYSBhbXBsaWEgdmFyaWVkYWQgZGUgYWxnb3JpdG1vcyBwYXJhIGVsIGFwcmVuZGl6YWplIGF1dG9tYXRpY28uCgojIDxzcGFuIHN0eWxlPSJjb2xvcjogdmlvbGV0OyI+SW5zdGFsYXIgcGFxdWV0ZXMgeSBsbGFtYXIgbGlicmVyaWFzPC9zcGFuPgpgYGB7cn0KIyBpbnN0YWxsLnBhY2thZ2VzKCJnZ3Bsb3QyIikgI0dyYWZpY2FzIGNvbiBtZWpvciBkaXNlbm8KbGlicmFyeShnZ3Bsb3QyKQojIGluc3RhbGwucGFja2FnZXMoImxhdHRpY2UiKSAjQ3JlYXIgR3JhZmljb3MKbGlicmFyeShsYXR0aWNlKQojIGluc3RhbGwucGFja2FnZXMoImNhcmV0IikgIyBBbGdvcml0bW9zIGRlIGFwcmVuZGl6YWplIGF1dG9tYXRpY28KbGlicmFyeShjYXJldCkKIyBpbnN0YWxsLnBhY2thZ2VzKCJkYXRhc2V0cyIpICMgVXNhciBsYSBiYXNlIGRlIGRhdG9zICJJcmlzIgpsaWJyYXJ5KGRhdGFzZXRzKQojIGluc3RhbGwucGFja2FnZXMoIkRhdGFFeHBsb3JlciIpICMgRXhwbG9yYWNpb24gZGUgZGF0b3MKbGlicmFyeShEYXRhRXhwbG9yZXIpCiMgaW5zdGFsbC5wYWNrYWdlcygia2VybmxhYiIpICMgUGFxdWV0ZSBjb24gbWV0b2RvcyBkZSBhcHJlbmRpemFqZSBhdXRvbWF0aWNvCmxpYnJhcnkoa2VybmxhYikKIyBpbnN0YWxsLnBhY2thZ2VzKCJyYW5kb21Gb3Jlc3QiKSAjIFBhcXVldGUgcGFyYSBlc3RlIG1ldG9kbyBkZSBjbGFzaWZpY2FjaW9uIApsaWJyYXJ5KHJhbmRvbUZvcmVzdCkKIyBpbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dmVyc2UiKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKYGBgCmBgYHtyfQojIGZpbGUuY2hvb3NlKCkKYGBgCgoKYGBge3J9CiMgQ2FyZ2FyIGVsIGRhdGFzZXQKaGVhcnRfZGF0YSA8LSByZWFkLmNzdigiL1VzZXJzL2NvbnN0YW50aW5vbWlsbGV0eGFjdXIvRGVza3RvcC9Db25jZW50cmFjaW9uL01vZHVsbyAyL2hlYXJ0LmNzdiIpCgojIENvbnZlcnRpciB2YXJpYWJsZXMgY2F0ZWfDs3JpY2FzIGEgZmFjdG9yZXMKaGVhcnRfZGF0YSRzZXggPC0gYXMuZmFjdG9yKGhlYXJ0X2RhdGEkc2V4KQpoZWFydF9kYXRhJGNwIDwtIGFzLmZhY3RvcihoZWFydF9kYXRhJGNwKQpoZWFydF9kYXRhJGZicyA8LSBhcy5mYWN0b3IoaGVhcnRfZGF0YSRmYnMpCmhlYXJ0X2RhdGEkcmVzdGVjZyA8LSBhcy5mYWN0b3IoaGVhcnRfZGF0YSRyZXN0ZWNnKQpoZWFydF9kYXRhJGV4YW5nIDwtIGFzLmZhY3RvcihoZWFydF9kYXRhJGV4YW5nKQpoZWFydF9kYXRhJHNsb3BlIDwtIGFzLmZhY3RvcihoZWFydF9kYXRhJHNsb3BlKQpoZWFydF9kYXRhJGNhIDwtIGFzLmZhY3RvcihoZWFydF9kYXRhJGNhKQpoZWFydF9kYXRhJHRoYWwgPC0gYXMuZmFjdG9yKGhlYXJ0X2RhdGEkdGhhbCkKaGVhcnRfZGF0YSR0YXJnZXQgPC0gYXMuZmFjdG9yKGhlYXJ0X2RhdGEkdGFyZ2V0KQoKYGBgCgo8c3BhbiBzdHlsZT0iY29sb3I6IHZpb2xldDsiPlBhcnRpciBsb3MgRGF0b3MgODAtMjA8L3NwYW4+CmBgYHtyfQpzZXQuc2VlZCgxMjMpCnRyYWluSW5kZXggPC0gY3JlYXRlRGF0YVBhcnRpdGlvbihoZWFydF9kYXRhJHRhcmdldCwgcCA9IDAuOCwgbGlzdCA9IEZBTFNFKQpoZWFydF90cmFpbiA8LSBoZWFydF9kYXRhW3RyYWluSW5kZXgsIF0KaGVhcnRfdGVzdCA8LSBoZWFydF9kYXRhWy10cmFpbkluZGV4LCBdCgoKYGBgCjxzcGFuIHN0eWxlPSJjb2xvcjogdmlvbGV0OyI+TW9kZWxvcyBwYXJhIENsYXNpZmljYWNpw7NuPC9zcGFuPgo8c3BhbiBzdHlsZT0iY29sb3I6IHZpb2xldDsiPlNWTSAoTcOhcXVpbmEgZGUgVmVjdG9yZXMgZGUgU29wb3J0ZSk8L3NwYW4+ClNWTSBjb24gS2VybmVsIExpbmVhbAoKYGBge3J9CnN2bV9saW5lYXJfbW9kZWwgPC0gdHJhaW4odGFyZ2V0IH4gLiwgZGF0YSA9IGhlYXJ0X3RyYWluLCBtZXRob2QgPSAic3ZtTGluZWFyIiwgdHJDb250cm9sID0gdHJhaW5Db250cm9sKG1ldGhvZCA9ICJjdiIsIG51bWJlciA9IDUpKQoKIyBQcmVkaWNjaW9uZXMgZW4gZWwgY29uanVudG8gZGUgZW50cmVuYW1pZW50bwpzdm1fbGluZWFyX3RyYWluX3ByZWRpY3Rpb25zIDwtIHByZWRpY3Qoc3ZtX2xpbmVhcl9tb2RlbCwgbmV3ZGF0YSA9IGhlYXJ0X3RyYWluKQptY3JlMSA8LSBjb25mdXNpb25NYXRyaXgoc3ZtX2xpbmVhcl90cmFpbl9wcmVkaWN0aW9ucywgaGVhcnRfdHJhaW4kdGFyZ2V0KQoKIyBQcmVkaWNjaW9uZXMgZW4gZWwgY29uanVudG8gZGUgcHJ1ZWJhCnN2bV9saW5lYXJfdGVzdF9wcmVkaWN0aW9ucyA8LSBwcmVkaWN0KHN2bV9saW5lYXJfbW9kZWwsIG5ld2RhdGEgPSBoZWFydF90ZXN0KQptY3JwMSA8LSBjb25mdXNpb25NYXRyaXgoc3ZtX2xpbmVhcl90ZXN0X3ByZWRpY3Rpb25zLCBoZWFydF90ZXN0JHRhcmdldCkKCm1jcnAxCgoKCmBgYAoKU1ZNIGNvbiBLZXJuZWwgUmFkaWFsCmBgYHtyfQpzdm1fcmFkaWFsX21vZGVsIDwtIHRyYWluKHRhcmdldCB+IC4sIGRhdGEgPSBoZWFydF90cmFpbiwgbWV0aG9kID0gInN2bVJhZGlhbCIsIHRyQ29udHJvbCA9IHRyYWluQ29udHJvbChtZXRob2QgPSAiY3YiLCBudW1iZXIgPSA1KSkKCiMgUHJlZGljY2lvbmVzIGVuIGVsIGNvbmp1bnRvIGRlIGVudHJlbmFtaWVudG8Kc3ZtX3JhZGlhbF90cmFpbl9wcmVkaWN0aW9ucyA8LSBwcmVkaWN0KHN2bV9yYWRpYWxfbW9kZWwsIG5ld2RhdGEgPSBoZWFydF90cmFpbikKbWNyZTIgPC0gY29uZnVzaW9uTWF0cml4KHN2bV9yYWRpYWxfdHJhaW5fcHJlZGljdGlvbnMsIGhlYXJ0X3RyYWluJHRhcmdldCkKCiMgUHJlZGljY2lvbmVzIGVuIGVsIGNvbmp1bnRvIGRlIHBydWViYQpzdm1fcmFkaWFsX3Rlc3RfcHJlZGljdGlvbnMgPC0gcHJlZGljdChzdm1fcmFkaWFsX21vZGVsLCBuZXdkYXRhID0gaGVhcnRfdGVzdCkKbWNycDIgPC0gY29uZnVzaW9uTWF0cml4KHN2bV9yYWRpYWxfdGVzdF9wcmVkaWN0aW9ucywgaGVhcnRfdGVzdCR0YXJnZXQpCgptY3JwMgoKCmBgYAoKU1ZNIGNvbiBLZXJuZWwgUG9saW7Ds21pY28KYGBge3J9CnN2bV9wb2x5X21vZGVsIDwtIHRyYWluKHRhcmdldCB+IC4sIGRhdGEgPSBoZWFydF90cmFpbiwgbWV0aG9kID0gInN2bVBvbHkiLCB0ckNvbnRyb2wgPSB0cmFpbkNvbnRyb2wobWV0aG9kID0gImN2IiwgbnVtYmVyID0gNSkpCgojIFByZWRpY2Npb25lcyBlbiBlbCBjb25qdW50byBkZSBlbnRyZW5hbWllbnRvCnN2bV9wb2x5X3RyYWluX3ByZWRpY3Rpb25zIDwtIHByZWRpY3Qoc3ZtX3BvbHlfbW9kZWwsIG5ld2RhdGEgPSBoZWFydF90cmFpbikKbWNyZTMgPC0gY29uZnVzaW9uTWF0cml4KHN2bV9wb2x5X3RyYWluX3ByZWRpY3Rpb25zLCBoZWFydF90cmFpbiR0YXJnZXQpCgojIFByZWRpY2Npb25lcyBlbiBlbCBjb25qdW50byBkZSBwcnVlYmEKc3ZtX3BvbHlfdGVzdF9wcmVkaWN0aW9ucyA8LSBwcmVkaWN0KHN2bV9wb2x5X21vZGVsLCBuZXdkYXRhID0gaGVhcnRfdGVzdCkKbWNycDMgPC0gY29uZnVzaW9uTWF0cml4KHN2bV9wb2x5X3Rlc3RfcHJlZGljdGlvbnMsIGhlYXJ0X3Rlc3QkdGFyZ2V0KQoKbWNycDMKCmBgYAoKPHNwYW4gc3R5bGU9ImNvbG9yOiB2aW9sZXQ7Ij7DgXJib2wgZGUgRGVjaXNpw7NuPC9zcGFuPgpgYGB7cn0KcnBhcnRfbW9kZWwgPC0gdHJhaW4odGFyZ2V0IH4gLiwgZGF0YSA9IGhlYXJ0X3RyYWluLCBtZXRob2QgPSAicnBhcnQiLCB0ckNvbnRyb2wgPSB0cmFpbkNvbnRyb2wobWV0aG9kID0gImN2IiwgbnVtYmVyID0gNSkpCgojIFByZWRpY2Npb25lcyBlbiBlbCBjb25qdW50byBkZSBlbnRyZW5hbWllbnRvCnJwYXJ0X3RyYWluX3ByZWRpY3Rpb25zIDwtIHByZWRpY3QocnBhcnRfbW9kZWwsIG5ld2RhdGEgPSBoZWFydF90cmFpbikKbWNyZTQgPC0gY29uZnVzaW9uTWF0cml4KHJwYXJ0X3RyYWluX3ByZWRpY3Rpb25zLCBoZWFydF90cmFpbiR0YXJnZXQpCgojIFByZWRpY2Npb25lcyBlbiBlbCBjb25qdW50byBkZSBwcnVlYmEKcnBhcnRfdGVzdF9wcmVkaWN0aW9ucyA8LSBwcmVkaWN0KHJwYXJ0X21vZGVsLCBuZXdkYXRhID0gaGVhcnRfdGVzdCkKbWNycDQgPC0gY29uZnVzaW9uTWF0cml4KHJwYXJ0X3Rlc3RfcHJlZGljdGlvbnMsIGhlYXJ0X3Rlc3QkdGFyZ2V0KQoKbWNycDQKCgpgYGAKCjxzcGFuIHN0eWxlPSJjb2xvcjogdmlvbGV0OyI+UmVkZXMgTmV1cm9uYWxlczwvc3Bhbj4KYGBge3J9Cm5uZXRfbW9kZWwgPC0gdHJhaW4odGFyZ2V0IH4gLiwgZGF0YSA9IGhlYXJ0X3RyYWluLCBtZXRob2QgPSAibm5ldCIsIHRyQ29udHJvbCA9IHRyYWluQ29udHJvbChtZXRob2QgPSAiY3YiLCBudW1iZXIgPSA1KSwgdHJhY2UgPSBGQUxTRSkKCiMgUHJlZGljY2lvbmVzIGVuIGVsIGNvbmp1bnRvIGRlIGVudHJlbmFtaWVudG8Kbm5ldF90cmFpbl9wcmVkaWN0aW9ucyA8LSBwcmVkaWN0KG5uZXRfbW9kZWwsIG5ld2RhdGEgPSBoZWFydF90cmFpbikKbWNyZTUgPC0gY29uZnVzaW9uTWF0cml4KG5uZXRfdHJhaW5fcHJlZGljdGlvbnMsIGhlYXJ0X3RyYWluJHRhcmdldCkKCiMgUHJlZGljY2lvbmVzIGVuIGVsIGNvbmp1bnRvIGRlIHBydWViYQpubmV0X3Rlc3RfcHJlZGljdGlvbnMgPC0gcHJlZGljdChubmV0X21vZGVsLCBuZXdkYXRhID0gaGVhcnRfdGVzdCkKbWNycDUgPC0gY29uZnVzaW9uTWF0cml4KG5uZXRfdGVzdF9wcmVkaWN0aW9ucywgaGVhcnRfdGVzdCR0YXJnZXQpCgptY3JwNQoKCmBgYAoKPHNwYW4gc3R5bGU9ImNvbG9yOiB2aW9sZXQ7Ij5SYW5kb20gRm9yZXN0PC9zcGFuPgpgYGB7cn0KcmZfbW9kZWwgPC0gdHJhaW4odGFyZ2V0IH4gLiwgZGF0YSA9IGhlYXJ0X3RyYWluLCBtZXRob2QgPSAicmYiLCB0ckNvbnRyb2wgPSB0cmFpbkNvbnRyb2wobWV0aG9kID0gImN2IiwgbnVtYmVyID0gNSkpCgojIFByZWRpY2Npb25lcyBlbiBlbCBjb25qdW50byBkZSBlbnRyZW5hbWllbnRvCnJmX3RyYWluX3ByZWRpY3Rpb25zIDwtIHByZWRpY3QocmZfbW9kZWwsIG5ld2RhdGEgPSBoZWFydF90cmFpbikKbWNyZTYgPC0gY29uZnVzaW9uTWF0cml4KHJmX3RyYWluX3ByZWRpY3Rpb25zLCBoZWFydF90cmFpbiR0YXJnZXQpCgojIFByZWRpY2Npb25lcyBlbiBlbCBjb25qdW50byBkZSBwcnVlYmEKcmZfdGVzdF9wcmVkaWN0aW9ucyA8LSBwcmVkaWN0KHJmX21vZGVsLCBuZXdkYXRhID0gaGVhcnRfdGVzdCkKbWNycDYgPC0gY29uZnVzaW9uTWF0cml4KHJmX3Rlc3RfcHJlZGljdGlvbnMsIGhlYXJ0X3Rlc3QkdGFyZ2V0KQoKbWNycDYKCgpgYGAKYGBge3J9CnJlc3VsdGFkb3MgPC0gZGF0YS5mcmFtZSgKICAic3ZtTGluZWFyIj1jKG1jcmUxJG92ZXJhbGxbIkFjY3VyYWN5Il0sIG1jcnAxJG92ZXJhbGxbIkFjY3VyYWN5Il0pLAogICJzdm1SYWRpYWwiPWMobWNyZTIkb3ZlcmFsbFsiQWNjdXJhY3kiXSwgbWNycDIkb3ZlcmFsbFsiQWNjdXJhY3kiXSksCiAgInN2bVBvbHkiPWMobWNyZTMkb3ZlcmFsbFsiQWNjdXJhY3kiXSwgbWNycDMkb3ZlcmFsbFsiQWNjdXJhY3kiXSksCiAgInJwYXJ0Ij1jKG1jcmU0JG92ZXJhbGxbIkFjY3VyYWN5Il0sIG1jcnA0JG92ZXJhbGxbIkFjY3VyYWN5Il0pLAogICJubmV0Ij1jKG1jcmU1JG92ZXJhbGxbIkFjY3VyYWN5Il0sIG1jcnA1JG92ZXJhbGxbIkFjY3VyYWN5Il0pLAogICJyZiI9YyhtY3JlNiRvdmVyYWxsWyJBY2N1cmFjeSJdLCBtY3JwNiRvdmVyYWxsWyJBY2N1cmFjeSJdKQopCnJvd25hbWVzKHJlc3VsdGFkb3MpIDwtIGMoIlByZWNpc2nDs24gZGUgRW50cmVuYW1pZW50byIsICJQcmVjaXNpw7NuIGRlIFBydWViYSIpCnJlc3VsdGFkb3MKYGBgCiMgPHNwYW4gc3R5bGU9ImNvbG9yOiB2aW9sZXQ7Ij5Db25jbHVzaW9uZXM8L3NwYW4+CgpBY29yZGUgYWwgcmVzdW1lbiBkZSByZXN1bHRhZG9zLCBlbCBtb2RlbG8gbWVqb3IgZXZhbHVhZG8gZXMgZWwgZGUgKipzdm1SYWRpYWwqKi4KCg==