Librerias

library(ggplot2)
library(lattice)
library(caret)
library(datasets)
library(DataExplorer)
library(kernlab)
## Warning: package 'kernlab' was built under R version 4.3.3
## 
## Attaching package: 'kernlab'
## The following object is masked from 'package:ggplot2':
## 
##     alpha
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
library(e1071)
library(rpart)

Importar base de datos

df <- read.csv("/Users/luisangel/Library/CloudStorage/OneDrive-InstitutoTecnologicoydeEstudiosSuperioresdeMonterrey/7th Season/M2/heart.csv")

Análisis exploratorio

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)
## 'data.frame':    1025 obs. of  14 variables:
##  $ age     : int  52 53 70 61 62 58 58 55 46 54 ...
##  $ sex     : int  1 1 1 1 0 0 1 1 1 1 ...
##  $ cp      : int  0 0 0 0 0 0 0 0 0 0 ...
##  $ trestbps: int  125 140 145 148 138 100 114 160 120 122 ...
##  $ chol    : int  212 203 174 203 294 248 318 289 249 286 ...
##  $ fbs     : int  0 1 0 0 1 0 0 0 0 0 ...
##  $ restecg : int  1 0 1 1 1 0 2 0 0 0 ...
##  $ thalach : int  168 155 125 161 106 122 140 145 144 116 ...
##  $ exang   : int  0 1 1 0 0 0 0 1 0 1 ...
##  $ oldpeak : num  1 3.1 2.6 0 1.9 1 4.4 0.8 0.8 3.2 ...
##  $ slope   : int  2 0 0 2 1 1 0 1 2 1 ...
##  $ ca      : int  2 0 0 1 3 0 3 1 0 2 ...
##  $ thal    : int  3 3 3 3 2 2 1 3 3 2 ...
##  $ target  : int  0 0 0 0 0 1 0 0 0 0 ...
#plot_missing(df)

Partir datos en 80/20

# Definir la proporción de entrenamiento (80%)
set.seed(123) 
train_indices <- sample(1:nrow(df), size = 0.8 * nrow(df))

# Crear el conjunto de entrenamiento y prueba
train_set <- df[train_indices, ]
test_set <- df[-train_indices, ]

test_set$exang <- as.factor(test_set$target)
train_set$exang <- as.factor(train_set$target)
test_set$restecg <- as.factor(test_set$target)
train_set$restecg <- as.factor(train_set$target)
test_set$ca <- as.factor(test_set$target)
train_set$ca <- as.factor(train_set$target)
test_set$slope <- as.factor(test_set$target)
train_set$slope <- as.factor(train_set$target)
test_set$age <- as.factor(test_set$target)
train_set$age <- as.factor(train_set$target)
test_set$cp <- as.factor(test_set$target)
train_set$cp <- as.factor(train_set$target)
test_set$fbs <- as.factor(test_set$target)
train_set$fbs <- as.factor(train_set$target)
train_set$sex <- as.factor(train_set$target)
test_set$sex <- as.factor(test_set$target)
train_set$target <- as.factor(train_set$target)
test_set$target <- as.factor(test_set$target)
names(df)
##  [1] "age"      "sex"      "cp"       "trestbps" "chol"     "fbs"     
##  [7] "restecg"  "thalach"  "exang"    "oldpeak"  "slope"    "ca"      
## [13] "thal"     "target"

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 (symLinear), Radial (svmRadial), Polinómico (symPoly), etc. * Árbol de Decisión: rpart * Redes Neuronales: nnet * Random Forest o Bosques Aleatorios: rf

1. Modelo con el metodo svmLineal

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

resultado_entrenamiento1 <- predict(modelo1, train_set)
resultado_prueba1 <- predict(modelo1, test_set)

# Matriz de Confusión del Resultado del Entrenamiento
mcre <- confusionMatrix(resultado_entrenamiento1, train_set$target)

# Matriz de Confusión del Resultado de la Prueba
mcrp <- confusionMatrix(resultado_prueba1, test_set$target)

2. Modelo con el metodo svmRadial

modelo5<- train(target ~ ., data=train_set,
                method = "svmRadial", #Cambiar
                preProcess=c("scale","center"),
                trControl = trainControl(method="cv", number=10),
                tuneGrid = data.frame(sigma=1,C=1) #Cambiar
                )
resultado_entrenamiento2 <- predict(modelo5,train_set)
resultado_prueba2 <- predict(modelo5,test_set)

# Matriz de Confusión del Resultado de Entrenamiento
mcre2 <- confusionMatrix(resultado_entrenamiento2, train_set$target)

# Matriz de Confusión del Resultado de Prueba
mcrp2 <- confusionMatrix(resultado_prueba2, test_set$target)

3. Modelo con método svmPoly

modelo6<- train(target ~ ., data=train_set,
                method = "svmPoly", #Cambiar
                preProcess=c("scale","center"),
                trControl = trainControl(method="cv", number=10),
                tuneGrid = data.frame(degree=1, scale=1, C=1) #Cambiar
                )
resultado_entrenamiento3 <- predict(modelo6,train_set)
resultado_prueba3 <- predict(modelo6,test_set)

# Matriz de Confusión del Resultado de Entrenamiento
mcre3 <- confusionMatrix(resultado_entrenamiento3, train_set$target)

# Matriz de Confusión del Resultado de Prueba
mcrp3 <- confusionMatrix(resultado_prueba3, test_set$target)

4. Modelo Árboles de Decisión

# Definir la fórmula del modelo
modelo4 <- target ~ .

# Entrenar el modelo de árbol de decisión usando rpart
set.seed(123)  # Para reproducibilidad
arbol_model <- rpart(modelo4, data = train_set, method = "class")

# Resumen del modelo entrenado
print(arbol_model)
## n= 820 
## 
## node), split, n, loss, yval, (yprob)
##       * denotes terminal node
## 
## 1) root 820 404 1 (0.4926829 0.5073171)  
##   2) age=0 404   0 0 (1.0000000 0.0000000) *
##   3) age=1 416   0 1 (0.0000000 1.0000000) *
# Visualizar el árbol de decisión
plot(arbol_model)
text(arbol_model, use.n = TRUE)

# Hacer predicciones en el conjunto de prueba
predicciones <- predict(arbol_model, newdata = test_set, type = "class")

# Evaluar el rendimiento del modelo en el conjunto de prueba
matriz_confusion <- confusionMatrix(predicciones, test_set$target)
print(matriz_confusion)
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction   0   1
##          0  95   0
##          1   0 110
##                                      
##                Accuracy : 1          
##                  95% CI : (0.9822, 1)
##     No Information Rate : 0.5366     
##     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.4634     
##          Detection Rate : 0.4634     
##    Detection Prevalence : 0.4634     
##       Balanced Accuracy : 1.0000     
##                                      
##        'Positive' Class : 0          
## 

5. Modelo de Redes Neuronales

# Definir la fórmula del modelo
modelo5 <- target ~ .

# Entrenar el modelo de redes neuronales usando nnet
set.seed(123)  # Para reproducibilidad
nnet_model <- train(
  modelo5,
  data = train_set,
  method = "nnet",
  trControl = trainControl(method = "none"),
  trace = FALSE,
  linout = FALSE,
  maxit = 200  # Número máximo de iteraciones
)

# Resumen del modelo entrenado
print(nnet_model)
## Neural Network 
## 
## 820 samples
##  13 predictor
##   2 classes: '0', '1' 
## 
## No pre-processing
## Resampling: None
# Hacer predicciones en el conjunto de prueba
predicciones <- predict(nnet_model, newdata = test_set)

# Evaluar el rendimiento del modelo en el conjunto de prueba
matriz_confusion <- confusionMatrix(predicciones, test_set$target)
print(matriz_confusion)
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction   0   1
##          0   0   0
##          1  95 110
##                                           
##                Accuracy : 0.5366          
##                  95% CI : (0.4658, 0.6063)
##     No Information Rate : 0.5366          
##     P-Value [Acc > NIR] : 0.5286          
##                                           
##                   Kappa : 0               
##                                           
##  Mcnemar's Test P-Value : <2e-16          
##                                           
##             Sensitivity : 0.0000          
##             Specificity : 1.0000          
##          Pos Pred Value :    NaN          
##          Neg Pred Value : 0.5366          
##              Prevalence : 0.4634          
##          Detection Rate : 0.0000          
##    Detection Prevalence : 0.0000          
##       Balanced Accuracy : 0.5000          
##                                           
##        'Positive' Class : 0               
## 

6. Modelo de Random Forest

# Construir el modelo Random Forest
modelo6 <- randomForest(target ~ ., data = train_set)


resultado_entrenamiento <- predict(modelo6,train_set)
resultado_prueba <- predict(modelo6,test_set)

# Matriz de Confusión
mcre <- confusionMatrix(resultado_entrenamiento, train_set$target) # matriz de confusión del resultado del entrenamiento

mcrp <- confusionMatrix(resultado_prueba,test_set$target) # matriz de confusión del resultado de la prueba

7. Comparacion de matrices

# Supongamos que ya tienes los modelos entrenados y sus predicciones:
# svm_model, arbol_model, nnet_model, rf_model
# Y sus predicciones:
# pred_svm, pred_arbol, pred_nnet, pred_rf

# Crear una lista para almacenar las matrices de confusión
confusion_matrices <- list()

# Calcular la matriz de confusión para cada modelo
confusion_matrices$SVM <- confusionMatrix(predict(modelo1, newdata = test_set), test_set$target)
confusion_matrices$Decision_Tree <- confusionMatrix(predict(arbol_model, newdata = test_set, type = "class"), test_set$target)
confusion_matrices$Neural_Network <- confusionMatrix(predict(nnet_model, newdata = test_set), test_set$target)
confusion_matrices$Random_Forest <- confusionMatrix(predict(modelo6, newdata = test_set), test_set$target)

# Mostrar las matrices de confusión
print(confusion_matrices)
## $SVM
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction   0   1
##          0  95   0
##          1   0 110
##                                      
##                Accuracy : 1          
##                  95% CI : (0.9822, 1)
##     No Information Rate : 0.5366     
##     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.4634     
##          Detection Rate : 0.4634     
##    Detection Prevalence : 0.4634     
##       Balanced Accuracy : 1.0000     
##                                      
##        'Positive' Class : 0          
##                                      
## 
## $Decision_Tree
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction   0   1
##          0  95   0
##          1   0 110
##                                      
##                Accuracy : 1          
##                  95% CI : (0.9822, 1)
##     No Information Rate : 0.5366     
##     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.4634     
##          Detection Rate : 0.4634     
##    Detection Prevalence : 0.4634     
##       Balanced Accuracy : 1.0000     
##                                      
##        'Positive' Class : 0          
##                                      
## 
## $Neural_Network
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction   0   1
##          0   0   0
##          1  95 110
##                                           
##                Accuracy : 0.5366          
##                  95% CI : (0.4658, 0.6063)
##     No Information Rate : 0.5366          
##     P-Value [Acc > NIR] : 0.5286          
##                                           
##                   Kappa : 0               
##                                           
##  Mcnemar's Test P-Value : <2e-16          
##                                           
##             Sensitivity : 0.0000          
##             Specificity : 1.0000          
##          Pos Pred Value :    NaN          
##          Neg Pred Value : 0.5366          
##              Prevalence : 0.4634          
##          Detection Rate : 0.0000          
##    Detection Prevalence : 0.0000          
##       Balanced Accuracy : 0.5000          
##                                           
##        'Positive' Class : 0               
##                                           
## 
## $Random_Forest
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction   0   1
##          0  95   0
##          1   0 110
##                                      
##                Accuracy : 1          
##                  95% CI : (0.9822, 1)
##     No Information Rate : 0.5366     
##     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.4634     
##          Detection Rate : 0.4634     
##    Detection Prevalence : 0.4634     
##       Balanced Accuracy : 1.0000     
##                                      
##        'Positive' Class : 0          
## 
# Comparar las métricas de los modelos
comparison <- data.frame(
  Modelo = c("SVM", "Decision Tree", "Neural Network", "Random Forest"),
  Accuracy = c(confusion_matrices$SVM$overall['Accuracy'],
               confusion_matrices$Decision_Tree$overall['Accuracy'],
               confusion_matrices$Neural_Network$overall['Accuracy'],
               confusion_matrices$Random_Forest$overall['Accuracy']),
  Kappa = c(confusion_matrices$SVM$overall['Kappa'],
            confusion_matrices$Decision_Tree$overall['Kappa'],
            confusion_matrices$Neural_Network$overall['Kappa'],
            confusion_matrices$Random_Forest$overall['Kappa'])
)

# Mostrar la comparación de métricas
print(comparison)
##           Modelo  Accuracy Kappa
## 1            SVM 1.0000000     1
## 2  Decision Tree 1.0000000     1
## 3 Neural Network 0.5365854     0
## 4  Random Forest 1.0000000     1

Conclusiones

Aunque tres de los modelos parecen estar funcionando perfectamente, estos resultados podrían estar ocultando problemas de sobreajuste o de un conjunto de prueba no representativo. La red neuronal claramente no está funcionando bien y podría requerir ajustes adicionales.

LS0tCnRpdGxlOiAiQ0FSRVQg4oCTIEVuZmVybWVkYWQgQ2FyZGlhY2EiCmF1dGhvcjogIkx1aXMgQW5nZWwgRGlheiIKZGF0ZTogIjIwMjQtMDgtMjEiCm91dHB1dDogCiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogVFJVRQogICAgdG9jX2Zsb2F0OiBUUlVFCiAgICBjb2RlX2Rvd25sb2FkOiBUUlVFCiAgICB0aGVtZTogInJlYWRhYmxlIgogICAgaGlnaGxpZ2h0OiAiaGFkZG9jayIKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQpgYGAKCiFbXSgvVXNlcnMvbHVpc2FuZ2VsL0xpYnJhcnkvQ2xvdWRTdG9yYWdlL09uZURyaXZlLUluc3RpdHV0b1RlY25vbG9naWNveWRlRXN0dWRpb3NTdXBlcmlvcmVzZGVNb250ZXJyZXkvN3RoIFNlYXNvbi9NMi9IRUFSVC5qcGcpCgoKIyMgPHNwYW4gc3R5bGUgPSAiY29sb3I6IHJlZDsgIj5MaWJyZXJpYXM8L3NwYW4+CmBgYHtyfQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkobGF0dGljZSkKbGlicmFyeShjYXJldCkKbGlicmFyeShkYXRhc2V0cykKbGlicmFyeShEYXRhRXhwbG9yZXIpCmxpYnJhcnkoa2VybmxhYikKbGlicmFyeShyYW5kb21Gb3Jlc3QpCmxpYnJhcnkoZTEwNzEpCmxpYnJhcnkocnBhcnQpCmBgYAoKIyMgPHNwYW4gc3R5bGUgPSAiY29sb3I6IHJlZDsgIj5JbXBvcnRhciBiYXNlIGRlIGRhdG9zPC9zcGFuPgpgYGB7cn0KZGYgPC0gcmVhZC5jc3YoIi9Vc2Vycy9sdWlzYW5nZWwvTGlicmFyeS9DbG91ZFN0b3JhZ2UvT25lRHJpdmUtSW5zdGl0dXRvVGVjbm9sb2dpY295ZGVFc3R1ZGlvc1N1cGVyaW9yZXNkZU1vbnRlcnJleS83dGggU2Vhc29uL00yL2hlYXJ0LmNzdiIpCmBgYAoKIyMgPHNwYW4gc3R5bGUgPSAiY29sb3I6IHJlZDsgIj5BbsOhbGlzaXMgZXhwbG9yYXRvcmlvPC9zcGFuPgpgYGB7cn0Kc3VtbWFyeShkZikKc3RyKGRmKQojcGxvdF9taXNzaW5nKGRmKQpgYGAKCiMjIDxzcGFuIHN0eWxlID0gImNvbG9yOiByZWQ7ICI+UGFydGlyIGRhdG9zIGVuIDgwLzIwPC9zcGFuPgpgYGB7cn0KIyBEZWZpbmlyIGxhIHByb3BvcmNpw7NuIGRlIGVudHJlbmFtaWVudG8gKDgwJSkKc2V0LnNlZWQoMTIzKSAKdHJhaW5faW5kaWNlcyA8LSBzYW1wbGUoMTpucm93KGRmKSwgc2l6ZSA9IDAuOCAqIG5yb3coZGYpKQoKIyBDcmVhciBlbCBjb25qdW50byBkZSBlbnRyZW5hbWllbnRvIHkgcHJ1ZWJhCnRyYWluX3NldCA8LSBkZlt0cmFpbl9pbmRpY2VzLCBdCnRlc3Rfc2V0IDwtIGRmWy10cmFpbl9pbmRpY2VzLCBdCgp0ZXN0X3NldCRleGFuZyA8LSBhcy5mYWN0b3IodGVzdF9zZXQkdGFyZ2V0KQp0cmFpbl9zZXQkZXhhbmcgPC0gYXMuZmFjdG9yKHRyYWluX3NldCR0YXJnZXQpCnRlc3Rfc2V0JHJlc3RlY2cgPC0gYXMuZmFjdG9yKHRlc3Rfc2V0JHRhcmdldCkKdHJhaW5fc2V0JHJlc3RlY2cgPC0gYXMuZmFjdG9yKHRyYWluX3NldCR0YXJnZXQpCnRlc3Rfc2V0JGNhIDwtIGFzLmZhY3Rvcih0ZXN0X3NldCR0YXJnZXQpCnRyYWluX3NldCRjYSA8LSBhcy5mYWN0b3IodHJhaW5fc2V0JHRhcmdldCkKdGVzdF9zZXQkc2xvcGUgPC0gYXMuZmFjdG9yKHRlc3Rfc2V0JHRhcmdldCkKdHJhaW5fc2V0JHNsb3BlIDwtIGFzLmZhY3Rvcih0cmFpbl9zZXQkdGFyZ2V0KQp0ZXN0X3NldCRhZ2UgPC0gYXMuZmFjdG9yKHRlc3Rfc2V0JHRhcmdldCkKdHJhaW5fc2V0JGFnZSA8LSBhcy5mYWN0b3IodHJhaW5fc2V0JHRhcmdldCkKdGVzdF9zZXQkY3AgPC0gYXMuZmFjdG9yKHRlc3Rfc2V0JHRhcmdldCkKdHJhaW5fc2V0JGNwIDwtIGFzLmZhY3Rvcih0cmFpbl9zZXQkdGFyZ2V0KQp0ZXN0X3NldCRmYnMgPC0gYXMuZmFjdG9yKHRlc3Rfc2V0JHRhcmdldCkKdHJhaW5fc2V0JGZicyA8LSBhcy5mYWN0b3IodHJhaW5fc2V0JHRhcmdldCkKdHJhaW5fc2V0JHNleCA8LSBhcy5mYWN0b3IodHJhaW5fc2V0JHRhcmdldCkKdGVzdF9zZXQkc2V4IDwtIGFzLmZhY3Rvcih0ZXN0X3NldCR0YXJnZXQpCnRyYWluX3NldCR0YXJnZXQgPC0gYXMuZmFjdG9yKHRyYWluX3NldCR0YXJnZXQpCnRlc3Rfc2V0JHRhcmdldCA8LSBhcy5mYWN0b3IodGVzdF9zZXQkdGFyZ2V0KQpgYGAKCmBgYHtyfQpuYW1lcyhkZikKYGBgCgojIDxzcGFuIHN0eWxlID0gImNvbG9yOiByZWQ7ICI+TcOpdG9kb3MgcGFyYSBNb2RlbGFyPC9zcGFuPgoKTG9zIG3DqXRvZG9zIG3DoXMgdXRpbGl6YWRvcyBwYXJhIG1vZGVsYXIgYXByZW5kaXphamUgYXV0b23DoXRpY28gc29uOgoqKlNWTSoqOgoqU3VwcG9ydCBWZWN0b3IgTWFjaGluZSogbyBNw6FxdWluYSBkZSBWZWN0b3JlcyBkZSBTb3BvcnRlLiBIYXkgdmFyaW9zIHN1YnRpcG9zOgpMaW5lYWwgKHN5bUxpbmVhciksIFJhZGlhbCAoc3ZtUmFkaWFsKSwgUG9saW7Ds21pY28gKHN5bVBvbHkpLCBldGMuCiogKirDgXJib2wgZGUgRGVjaXNpw7NuKio6IHJwYXJ0CiogKipSZWRlcyBOZXVyb25hbGVzKio6IG5uZXQKKiAqKlJhbmRvbSBGb3Jlc3QqKiBvIEJvc3F1ZXMgQWxlYXRvcmlvczogcmYKCiMjIDxzcGFuIHN0eWxlID0gImNvbG9yOiByZWQ7ICI+MS4gTW9kZWxvIGNvbiBlbCBtZXRvZG8gc3ZtTGluZWFsPC9zcGFuPgpgYGB7cn0KbW9kZWxvMSA8LSB0cmFpbih0YXJnZXQgfiAuLCBkYXRhPXRyYWluX3NldCwKICAgICAgICAgICAgICAgICBtZXRob2QgPSAic3ZtTGluZWFyIiwgI0NhbWJpYXIKICAgICAgICAgICAgICAgICBwcmVQcm9jZXNzID0gYygic2NhbGUiLCAiY2VudGVyIiksCiAgICAgICAgICAgICAgICAgdHJDb250cm9sID0gdHJhaW5Db250cm9sIChtZXRob2QgPSJjdiIsIG51bWJlciA9MTApLAogICAgICAgICAgICAgICAgIHR1bmVHcmlkID0gZGF0YS5mcmFtZShDPTEpICNDYW1iaWFyCiAgICAgICAgICAgICAgICAgKQoKcmVzdWx0YWRvX2VudHJlbmFtaWVudG8xIDwtIHByZWRpY3QobW9kZWxvMSwgdHJhaW5fc2V0KQpyZXN1bHRhZG9fcHJ1ZWJhMSA8LSBwcmVkaWN0KG1vZGVsbzEsIHRlc3Rfc2V0KQoKIyBNYXRyaXogZGUgQ29uZnVzacOzbiBkZWwgUmVzdWx0YWRvIGRlbCBFbnRyZW5hbWllbnRvCm1jcmUgPC0gY29uZnVzaW9uTWF0cml4KHJlc3VsdGFkb19lbnRyZW5hbWllbnRvMSwgdHJhaW5fc2V0JHRhcmdldCkKCiMgTWF0cml6IGRlIENvbmZ1c2nDs24gZGVsIFJlc3VsdGFkbyBkZSBsYSBQcnVlYmEKbWNycCA8LSBjb25mdXNpb25NYXRyaXgocmVzdWx0YWRvX3BydWViYTEsIHRlc3Rfc2V0JHRhcmdldCkKYGBgCgoKIyMgPHNwYW4gc3R5bGUgPSAiY29sb3I6IHJlZDsgIj4yLiBNb2RlbG8gY29uIGVsIG1ldG9kbyBzdm1SYWRpYWw8L3NwYW4+CmBgYHtyfQptb2RlbG81PC0gdHJhaW4odGFyZ2V0IH4gLiwgZGF0YT10cmFpbl9zZXQsCiAgICAgICAgICAgICAgICBtZXRob2QgPSAic3ZtUmFkaWFsIiwgI0NhbWJpYXIKICAgICAgICAgICAgICAgIHByZVByb2Nlc3M9Yygic2NhbGUiLCJjZW50ZXIiKSwKICAgICAgICAgICAgICAgIHRyQ29udHJvbCA9IHRyYWluQ29udHJvbChtZXRob2Q9ImN2IiwgbnVtYmVyPTEwKSwKICAgICAgICAgICAgICAgIHR1bmVHcmlkID0gZGF0YS5mcmFtZShzaWdtYT0xLEM9MSkgI0NhbWJpYXIKICAgICAgICAgICAgICAgICkKcmVzdWx0YWRvX2VudHJlbmFtaWVudG8yIDwtIHByZWRpY3QobW9kZWxvNSx0cmFpbl9zZXQpCnJlc3VsdGFkb19wcnVlYmEyIDwtIHByZWRpY3QobW9kZWxvNSx0ZXN0X3NldCkKCiMgTWF0cml6IGRlIENvbmZ1c2nDs24gZGVsIFJlc3VsdGFkbyBkZSBFbnRyZW5hbWllbnRvCm1jcmUyIDwtIGNvbmZ1c2lvbk1hdHJpeChyZXN1bHRhZG9fZW50cmVuYW1pZW50bzIsIHRyYWluX3NldCR0YXJnZXQpCgojIE1hdHJpeiBkZSBDb25mdXNpw7NuIGRlbCBSZXN1bHRhZG8gZGUgUHJ1ZWJhCm1jcnAyIDwtIGNvbmZ1c2lvbk1hdHJpeChyZXN1bHRhZG9fcHJ1ZWJhMiwgdGVzdF9zZXQkdGFyZ2V0KQpgYGAKCiMjIDxzcGFuIHN0eWxlID0gImNvbG9yOiByZWQ7ICI+My4gTW9kZWxvIGNvbiBtw6l0b2RvIHN2bVBvbHk8L3NwYW4+CmBgYHtyfQptb2RlbG82PC0gdHJhaW4odGFyZ2V0IH4gLiwgZGF0YT10cmFpbl9zZXQsCiAgICAgICAgICAgICAgICBtZXRob2QgPSAic3ZtUG9seSIsICNDYW1iaWFyCiAgICAgICAgICAgICAgICBwcmVQcm9jZXNzPWMoInNjYWxlIiwiY2VudGVyIiksCiAgICAgICAgICAgICAgICB0ckNvbnRyb2wgPSB0cmFpbkNvbnRyb2wobWV0aG9kPSJjdiIsIG51bWJlcj0xMCksCiAgICAgICAgICAgICAgICB0dW5lR3JpZCA9IGRhdGEuZnJhbWUoZGVncmVlPTEsIHNjYWxlPTEsIEM9MSkgI0NhbWJpYXIKICAgICAgICAgICAgICAgICkKcmVzdWx0YWRvX2VudHJlbmFtaWVudG8zIDwtIHByZWRpY3QobW9kZWxvNix0cmFpbl9zZXQpCnJlc3VsdGFkb19wcnVlYmEzIDwtIHByZWRpY3QobW9kZWxvNix0ZXN0X3NldCkKCiMgTWF0cml6IGRlIENvbmZ1c2nDs24gZGVsIFJlc3VsdGFkbyBkZSBFbnRyZW5hbWllbnRvCm1jcmUzIDwtIGNvbmZ1c2lvbk1hdHJpeChyZXN1bHRhZG9fZW50cmVuYW1pZW50bzMsIHRyYWluX3NldCR0YXJnZXQpCgojIE1hdHJpeiBkZSBDb25mdXNpw7NuIGRlbCBSZXN1bHRhZG8gZGUgUHJ1ZWJhCm1jcnAzIDwtIGNvbmZ1c2lvbk1hdHJpeChyZXN1bHRhZG9fcHJ1ZWJhMywgdGVzdF9zZXQkdGFyZ2V0KQpgYGAKCiMjIDxzcGFuIHN0eWxlID0gImNvbG9yOiByZWQ7ICI+NC4gTW9kZWxvIMOBcmJvbGVzIGRlIERlY2lzacOzbjwvc3Bhbj4KYGBge3J9CiMgRGVmaW5pciBsYSBmw7NybXVsYSBkZWwgbW9kZWxvCm1vZGVsbzQgPC0gdGFyZ2V0IH4gLgoKIyBFbnRyZW5hciBlbCBtb2RlbG8gZGUgw6FyYm9sIGRlIGRlY2lzacOzbiB1c2FuZG8gcnBhcnQKc2V0LnNlZWQoMTIzKSAgIyBQYXJhIHJlcHJvZHVjaWJpbGlkYWQKYXJib2xfbW9kZWwgPC0gcnBhcnQobW9kZWxvNCwgZGF0YSA9IHRyYWluX3NldCwgbWV0aG9kID0gImNsYXNzIikKCiMgUmVzdW1lbiBkZWwgbW9kZWxvIGVudHJlbmFkbwpwcmludChhcmJvbF9tb2RlbCkKCiMgVmlzdWFsaXphciBlbCDDoXJib2wgZGUgZGVjaXNpw7NuCnBsb3QoYXJib2xfbW9kZWwpCnRleHQoYXJib2xfbW9kZWwsIHVzZS5uID0gVFJVRSkKCiMgSGFjZXIgcHJlZGljY2lvbmVzIGVuIGVsIGNvbmp1bnRvIGRlIHBydWViYQpwcmVkaWNjaW9uZXMgPC0gcHJlZGljdChhcmJvbF9tb2RlbCwgbmV3ZGF0YSA9IHRlc3Rfc2V0LCB0eXBlID0gImNsYXNzIikKCiMgRXZhbHVhciBlbCByZW5kaW1pZW50byBkZWwgbW9kZWxvIGVuIGVsIGNvbmp1bnRvIGRlIHBydWViYQptYXRyaXpfY29uZnVzaW9uIDwtIGNvbmZ1c2lvbk1hdHJpeChwcmVkaWNjaW9uZXMsIHRlc3Rfc2V0JHRhcmdldCkKcHJpbnQobWF0cml6X2NvbmZ1c2lvbikKCmBgYAoKIyMgPHNwYW4gc3R5bGUgPSAiY29sb3I6IHJlZDsgIj41LiBNb2RlbG8gZGUgUmVkZXMgTmV1cm9uYWxlczwvc3Bhbj4KYGBge3J9CiMgRGVmaW5pciBsYSBmw7NybXVsYSBkZWwgbW9kZWxvCm1vZGVsbzUgPC0gdGFyZ2V0IH4gLgoKIyBFbnRyZW5hciBlbCBtb2RlbG8gZGUgcmVkZXMgbmV1cm9uYWxlcyB1c2FuZG8gbm5ldApzZXQuc2VlZCgxMjMpICAjIFBhcmEgcmVwcm9kdWNpYmlsaWRhZApubmV0X21vZGVsIDwtIHRyYWluKAogIG1vZGVsbzUsCiAgZGF0YSA9IHRyYWluX3NldCwKICBtZXRob2QgPSAibm5ldCIsCiAgdHJDb250cm9sID0gdHJhaW5Db250cm9sKG1ldGhvZCA9ICJub25lIiksCiAgdHJhY2UgPSBGQUxTRSwKICBsaW5vdXQgPSBGQUxTRSwKICBtYXhpdCA9IDIwMCAgIyBOw7ptZXJvIG3DoXhpbW8gZGUgaXRlcmFjaW9uZXMKKQoKIyBSZXN1bWVuIGRlbCBtb2RlbG8gZW50cmVuYWRvCnByaW50KG5uZXRfbW9kZWwpCgojIEhhY2VyIHByZWRpY2Npb25lcyBlbiBlbCBjb25qdW50byBkZSBwcnVlYmEKcHJlZGljY2lvbmVzIDwtIHByZWRpY3Qobm5ldF9tb2RlbCwgbmV3ZGF0YSA9IHRlc3Rfc2V0KQoKIyBFdmFsdWFyIGVsIHJlbmRpbWllbnRvIGRlbCBtb2RlbG8gZW4gZWwgY29uanVudG8gZGUgcHJ1ZWJhCm1hdHJpel9jb25mdXNpb24gPC0gY29uZnVzaW9uTWF0cml4KHByZWRpY2Npb25lcywgdGVzdF9zZXQkdGFyZ2V0KQpwcmludChtYXRyaXpfY29uZnVzaW9uKQpgYGAKCgojIyA8c3BhbiBzdHlsZSA9ICJjb2xvcjogcmVkOyAiPjYuIE1vZGVsbyBkZSBSYW5kb20gRm9yZXN0PC9zcGFuPgpgYGB7cn0KIyBDb25zdHJ1aXIgZWwgbW9kZWxvIFJhbmRvbSBGb3Jlc3QKbW9kZWxvNiA8LSByYW5kb21Gb3Jlc3QodGFyZ2V0IH4gLiwgZGF0YSA9IHRyYWluX3NldCkKCgpyZXN1bHRhZG9fZW50cmVuYW1pZW50byA8LSBwcmVkaWN0KG1vZGVsbzYsdHJhaW5fc2V0KQpyZXN1bHRhZG9fcHJ1ZWJhIDwtIHByZWRpY3QobW9kZWxvNix0ZXN0X3NldCkKCiMgTWF0cml6IGRlIENvbmZ1c2nDs24KbWNyZSA8LSBjb25mdXNpb25NYXRyaXgocmVzdWx0YWRvX2VudHJlbmFtaWVudG8sIHRyYWluX3NldCR0YXJnZXQpICMgbWF0cml6IGRlIGNvbmZ1c2nDs24gZGVsIHJlc3VsdGFkbyBkZWwgZW50cmVuYW1pZW50bwoKbWNycCA8LSBjb25mdXNpb25NYXRyaXgocmVzdWx0YWRvX3BydWViYSx0ZXN0X3NldCR0YXJnZXQpICMgbWF0cml6IGRlIGNvbmZ1c2nDs24gZGVsIHJlc3VsdGFkbyBkZSBsYSBwcnVlYmEKCmBgYAoKIyMgPHNwYW4gc3R5bGUgPSAiY29sb3I6IHJlZDsgIj43LiBDb21wYXJhY2lvbiBkZSBtYXRyaWNlczwvc3Bhbj4KYGBge3J9CiMgU3Vwb25nYW1vcyBxdWUgeWEgdGllbmVzIGxvcyBtb2RlbG9zIGVudHJlbmFkb3MgeSBzdXMgcHJlZGljY2lvbmVzOgojIHN2bV9tb2RlbCwgYXJib2xfbW9kZWwsIG5uZXRfbW9kZWwsIHJmX21vZGVsCiMgWSBzdXMgcHJlZGljY2lvbmVzOgojIHByZWRfc3ZtLCBwcmVkX2FyYm9sLCBwcmVkX25uZXQsIHByZWRfcmYKCiMgQ3JlYXIgdW5hIGxpc3RhIHBhcmEgYWxtYWNlbmFyIGxhcyBtYXRyaWNlcyBkZSBjb25mdXNpw7NuCmNvbmZ1c2lvbl9tYXRyaWNlcyA8LSBsaXN0KCkKCiMgQ2FsY3VsYXIgbGEgbWF0cml6IGRlIGNvbmZ1c2nDs24gcGFyYSBjYWRhIG1vZGVsbwpjb25mdXNpb25fbWF0cmljZXMkU1ZNIDwtIGNvbmZ1c2lvbk1hdHJpeChwcmVkaWN0KG1vZGVsbzEsIG5ld2RhdGEgPSB0ZXN0X3NldCksIHRlc3Rfc2V0JHRhcmdldCkKY29uZnVzaW9uX21hdHJpY2VzJERlY2lzaW9uX1RyZWUgPC0gY29uZnVzaW9uTWF0cml4KHByZWRpY3QoYXJib2xfbW9kZWwsIG5ld2RhdGEgPSB0ZXN0X3NldCwgdHlwZSA9ICJjbGFzcyIpLCB0ZXN0X3NldCR0YXJnZXQpCmNvbmZ1c2lvbl9tYXRyaWNlcyROZXVyYWxfTmV0d29yayA8LSBjb25mdXNpb25NYXRyaXgocHJlZGljdChubmV0X21vZGVsLCBuZXdkYXRhID0gdGVzdF9zZXQpLCB0ZXN0X3NldCR0YXJnZXQpCmNvbmZ1c2lvbl9tYXRyaWNlcyRSYW5kb21fRm9yZXN0IDwtIGNvbmZ1c2lvbk1hdHJpeChwcmVkaWN0KG1vZGVsbzYsIG5ld2RhdGEgPSB0ZXN0X3NldCksIHRlc3Rfc2V0JHRhcmdldCkKCiMgTW9zdHJhciBsYXMgbWF0cmljZXMgZGUgY29uZnVzacOzbgpwcmludChjb25mdXNpb25fbWF0cmljZXMpCgojIENvbXBhcmFyIGxhcyBtw6l0cmljYXMgZGUgbG9zIG1vZGVsb3MKY29tcGFyaXNvbiA8LSBkYXRhLmZyYW1lKAogIE1vZGVsbyA9IGMoIlNWTSIsICJEZWNpc2lvbiBUcmVlIiwgIk5ldXJhbCBOZXR3b3JrIiwgIlJhbmRvbSBGb3Jlc3QiKSwKICBBY2N1cmFjeSA9IGMoY29uZnVzaW9uX21hdHJpY2VzJFNWTSRvdmVyYWxsWydBY2N1cmFjeSddLAogICAgICAgICAgICAgICBjb25mdXNpb25fbWF0cmljZXMkRGVjaXNpb25fVHJlZSRvdmVyYWxsWydBY2N1cmFjeSddLAogICAgICAgICAgICAgICBjb25mdXNpb25fbWF0cmljZXMkTmV1cmFsX05ldHdvcmskb3ZlcmFsbFsnQWNjdXJhY3knXSwKICAgICAgICAgICAgICAgY29uZnVzaW9uX21hdHJpY2VzJFJhbmRvbV9Gb3Jlc3Qkb3ZlcmFsbFsnQWNjdXJhY3knXSksCiAgS2FwcGEgPSBjKGNvbmZ1c2lvbl9tYXRyaWNlcyRTVk0kb3ZlcmFsbFsnS2FwcGEnXSwKICAgICAgICAgICAgY29uZnVzaW9uX21hdHJpY2VzJERlY2lzaW9uX1RyZWUkb3ZlcmFsbFsnS2FwcGEnXSwKICAgICAgICAgICAgY29uZnVzaW9uX21hdHJpY2VzJE5ldXJhbF9OZXR3b3JrJG92ZXJhbGxbJ0thcHBhJ10sCiAgICAgICAgICAgIGNvbmZ1c2lvbl9tYXRyaWNlcyRSYW5kb21fRm9yZXN0JG92ZXJhbGxbJ0thcHBhJ10pCikKCiMgTW9zdHJhciBsYSBjb21wYXJhY2nDs24gZGUgbcOpdHJpY2FzCnByaW50KGNvbXBhcmlzb24pCgpgYGAKCiMjIDxzcGFuIHN0eWxlID0gImNvbG9yOiByZWQ7ICI+Q29uY2x1c2lvbmVzPC9zcGFuPgpBdW5xdWUgdHJlcyBkZSBsb3MgbW9kZWxvcyBwYXJlY2VuIGVzdGFyIGZ1bmNpb25hbmRvIHBlcmZlY3RhbWVudGUsIGVzdG9zIHJlc3VsdGFkb3MgcG9kcsOtYW4gZXN0YXIgb2N1bHRhbmRvIHByb2JsZW1hcyBkZSBzb2JyZWFqdXN0ZSBvIGRlIHVuIGNvbmp1bnRvIGRlIHBydWViYSBubyByZXByZXNlbnRhdGl2by4gTGEgcmVkIG5ldXJvbmFsIGNsYXJhbWVudGUgbm8gZXN0w6EgZnVuY2lvbmFuZG8gYmllbiB5IHBvZHLDrWEgcmVxdWVyaXIgYWp1c3RlcyBhZGljaW9uYWxlcy4K