My Codes

#------------------------------------
# Perform some data pre-processing
#------------------------------------

# Clear workspace: 
rm(list = ls())

# Load some packages: 
library(tidyverse)
library(magrittr)

# Import data: 
hmeq <- read.csv("http://www.creditriskanalytics.net/uploads/1/9/5/1/19511601/hmeq.csv")

# Function replaces NA by mean: 
replace_by_mean <- function(x) {
  x[is.na(x)] <- mean(x, na.rm = TRUE)
  return(x)
}

# A function imputes NA observations for categorical variables: 

replace_na_categorical <- function(x) {
  x %>% 
    table() %>% 
    as.data.frame() %>% 
    arrange(-Freq) ->> my_df
  
  n_obs <- sum(my_df$Freq)
  pop <- my_df$. %>% as.character()
  set.seed(29)
  x[is.na(x)] <- sample(pop, sum(is.na(x)), replace = TRUE, prob = my_df$Freq)
  return(x)
}

# Use the two functions: 
df <- hmeq %>% 
  mutate_if(is.factor, as.character) %>% 
  mutate(REASON = case_when(REASON == "" ~ NA_character_, TRUE ~ REASON), 
         JOB = case_when(JOB == "" ~ NA_character_, TRUE ~ JOB)) %>%
  mutate_if(is_character, as.factor) %>% 
  mutate_if(is.numeric, replace_by_mean) %>% 
  mutate_if(is.factor, replace_na_categorical)


# Convert BAD to factor and scale 0 -1 data set: 
df_for_ml <- df %>% 
  mutate(BAD = case_when(BAD == 1 ~ "Bad", TRUE ~ "Good") %>% as.factor()) %>% 
  mutate_if(is.numeric, function(x) {(x - min(x)) / (max(x) - min(x))})

# Split data: 
library(caret)
set.seed(1)
id <- createDataPartition(y = df_for_ml$BAD, p = 0.7, list = FALSE)
df_train_ml <- df_for_ml[id, ]
df_test_ml <- df_for_ml[-id, ]

# Set conditions for training model and cross-validation: 

set.seed(1)
number <- 5
repeats <- 2
control <- trainControl(method = "repeatedcv", 
                        number = number , 
                        repeats = repeats, 
                        classProbs = TRUE, 
                        savePredictions = "final", 
                        index = createResample(df_train_ml$BAD, repeats*number), 
                        summaryFunction = multiClassSummary, 
                        allowParallel = TRUE)

#----------------------------------------------
#  Train Logistic and Support Vector Machine
#----------------------------------------------

# Train Logistic Model: 

logistic <- train(BAD ~ ., 
                  data = df_train_ml, 
                  method = "glm", 
                  trControl = control)

# Train SVM: 

library(kernlab)
svm <- ksvm(BAD ~., 
            data = df_train_ml,
            kernel = "rbfdot", 
            C = 10, 
            epsilon = 0.1, 
            prob.model = TRUE, 
            class.weights = c("Bad" = 10, "Good" = 1), 
            cross = 10)


#-----------------------------------
#  Compare between the two models
#-----------------------------------

# Function calculates probabilities: 

predict_prob <- function(model_selected) {
  predict(model_selected, df_test_ml, type = "prob") %>% 
    as.data.frame() %>% 
    pull(Bad) %>% 
    return()
  }

# Use this function: 

pred_logistic <- predict_prob(logistic)
pred_svm <- predict_prob(svm)

# Function calculates AUC: 

library(pROC) 

test_auc <- function(prob) {
  roc(df_test_ml$BAD, prob)
  }

# Use this function: 
auc_logistic <- test_auc(pred_logistic)
auc_svm <- test_auc(pred_svm)

# Create a data frame for comparing: 

df_auc <- bind_rows(data_frame(TPR = auc_logistic$sensitivities, 
                               FPR = 1 - auc_logistic$specificities, 
                               Model = "Logistic"), 
                    data_frame(TPR = auc_svm$sensitivities, 
                               FPR = 1 - auc_svm$specificities, 
                               Model = "SVM"))

# Plot ROC curves: 
df_auc %>% 
  ggplot(aes(FPR, TPR, color = Model)) +
  geom_line(size = 1) +
  theme_bw() +
  coord_equal() +
  geom_abline(intercept = 0, slope = 1, color = "gray37", size = 1, linetype = "dashed") + 
  labs(x = "FPR (1 - Specificity)", 
       y = "TPR (Sensitivity)", 
       title = "ROC Curve and AUC: Logistic vs SVM")

# Compare AUC: 
lapply(list(auc_logistic, auc_svm), function(x) {x[["auc"]]})
## [[1]]
## Area under the curve: 0.7908
## 
## [[2]]
## Area under the curve: 0.9011
# Results based on test data: 
lapply(list(logistic, svm), 
       function(model) {confusionMatrix(predict(model, df_test_ml), df_test_ml$BAD, positive = "Bad")})
## [[1]]
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction  Bad Good
##       Bad   105   48
##       Good  251 1383
##                                           
##                Accuracy : 0.8327          
##                  95% CI : (0.8146, 0.8497)
##     No Information Rate : 0.8008          
##     P-Value [Acc > NIR] : 0.0003223       
##                                           
##                   Kappa : 0.3326          
##  Mcnemar's Test P-Value : < 2.2e-16       
##                                           
##             Sensitivity : 0.29494         
##             Specificity : 0.96646         
##          Pos Pred Value : 0.68627         
##          Neg Pred Value : 0.84639         
##              Prevalence : 0.19922         
##          Detection Rate : 0.05876         
##    Detection Prevalence : 0.08562         
##       Balanced Accuracy : 0.63070         
##                                           
##        'Positive' Class : Bad             
##                                           
## 
## [[2]]
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction  Bad Good
##       Bad   288  194
##       Good   68 1237
##                                           
##                Accuracy : 0.8534          
##                  95% CI : (0.8361, 0.8695)
##     No Information Rate : 0.8008          
##     P-Value [Acc > NIR] : 4.729e-09       
##                                           
##                   Kappa : 0.5944          
##  Mcnemar's Test P-Value : 1.140e-14       
##                                           
##             Sensitivity : 0.8090          
##             Specificity : 0.8644          
##          Pos Pred Value : 0.5975          
##          Neg Pred Value : 0.9479          
##              Prevalence : 0.1992          
##          Detection Rate : 0.1612          
##    Detection Prevalence : 0.2697          
##       Balanced Accuracy : 0.8367          
##                                           
##        'Positive' Class : Bad             
## 

Family of SVMs

# Use Parallel computing: 
library(doParallel)
registerDoParallel(cores = detectCores() - 1)

# 5 SVM models selected: 

my_models <- c("svmLinearWeights2", "lssvmRadial", 
               "svmLinearWeights", "svmRadial", "svmLinear2")

# Train these ML Models: 
library(caretEnsemble)
set.seed(1)
system.time(model_list1 <- caretList(BAD ~., 
                                     data = df_train_ml,
                                     trControl = control,
                                     metric = "Accuracy", 
                                     methodList = my_models))
##     user   system  elapsed 
## 2487.657 5393.338  268.596
list_of_results <- lapply(my_models, function(x) {model_list1[[x]]$resample})

# Convert to data frame: 
total_df <- do.call("bind_rows", list_of_results)
total_df %<>% mutate(Model = lapply(my_models, function(x) {rep(x, number*repeats)}) %>% unlist())

# Average Accuracy based on 10 samples for these models: 
total_df %>% 
  group_by(Model) %>% 
  summarise(avg_acc = mean(Accuracy)) %>% 
  ungroup() %>% 
  arrange(-avg_acc) %>% 
  mutate_if(is.numeric, function(x) {round(x, 3)}) %>% 
  knitr::kable(caption = "Table 1: Model Performance in decreasing order of Accuracy", 
               col.names = c("Model", "Average Accuracy"))
Table 1: Model Performance in decreasing order of Accuracy
Model Average Accuracy
svmRadial 0.880
lssvmRadial 0.846
svmLinear2 0.833
svmLinearWeights 0.833
svmLinearWeights2 0.833
# Average Sensitivity based on 10 samples for these models: 

total_df %>% 
  group_by(Model) %>% 
  summarise(avg_sen = mean(Sensitivity)) %>% 
  ungroup() %>% 
  arrange(-avg_sen) %>% 
  mutate_if(is.numeric, function(x) {round(x, 3)}) %>% 
  knitr::kable(caption = "Table 2: Model Performance in decreasing order of Sensitivity", 
               col.names = c("Model", "Average Sensitivity"))
Table 2: Model Performance in decreasing order of Sensitivity
Model Average Sensitivity
svmRadial 0.518
lssvmRadial 0.320
svmLinearWeights2 0.245
svmLinear2 0.227
svmLinearWeights 0.227
theme_set(theme_minimal())
total_df %>% 
  select(-Resample, -F1, -Balanced_Accuracy) %>% 
  gather(a, b, -Model) %>% 
  ggplot(aes(Model, b, fill = Model, color = Model)) + 
  geom_boxplot(show.legend = FALSE, alpha = 0.3) + 
  facet_wrap(~ a, scales = "free") + 
  coord_flip() + 
  scale_y_continuous(labels = scales::percent) + 
  theme(plot.margin = unit(c(1, 1, 1, 1), "cm")) + 
  labs(x = NULL, y = NULL, 
       title = "Figure 1: A Short Comparision among SVM Models", 
       subtitle = "Valdation Method Used: Cross-validation, 10 samples")

LS0tCnRpdGxlOiAiTXkgU2VjcmV0IENvZGVzOiBMb2dpc3RpYyB2cyBTdXBwb3J0IFZlY3RvciBNYWNoaW5lIChrZXJubGFiIFBhY2thZ2UpIiAKc3VidGl0bGU6ICJSIGZvciBLaWxsaW5nIFBuZXVtb25pYSIKYXV0aG9yOiAiTmd1eWVuIENoaSBEdW5nIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDogCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgICAjIGNvZGVfZm9sZGluZzogaGlkZQogICAgaGlnaGxpZ2h0OiBweWdtZW50cwogICAgIyBudW1iZXJfc2VjdGlvbnM6IHllcwogICAgdGhlbWU6ICJmbGF0bHkiCiAgICB0b2M6IFRSVUUKICAgIHRvY19mbG9hdDogVFJVRQotLS0KCmBgYHtyIHNldHVwLGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UpCmBgYAoKCiMgTXkgQ29kZXMKCmBgYHtyfQoKCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyBQZXJmb3JtIHNvbWUgZGF0YSBwcmUtcHJvY2Vzc2luZwojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIENsZWFyIHdvcmtzcGFjZTogCnJtKGxpc3QgPSBscygpKQoKIyBMb2FkIHNvbWUgcGFja2FnZXM6IApsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShtYWdyaXR0cikKCiMgSW1wb3J0IGRhdGE6IApobWVxIDwtIHJlYWQuY3N2KCJodHRwOi8vd3d3LmNyZWRpdHJpc2thbmFseXRpY3MubmV0L3VwbG9hZHMvMS85LzUvMS8xOTUxMTYwMS9obWVxLmNzdiIpCgojIEZ1bmN0aW9uIHJlcGxhY2VzIE5BIGJ5IG1lYW46IApyZXBsYWNlX2J5X21lYW4gPC0gZnVuY3Rpb24oeCkgewogIHhbaXMubmEoeCldIDwtIG1lYW4oeCwgbmEucm0gPSBUUlVFKQogIHJldHVybih4KQp9CgojIEEgZnVuY3Rpb24gaW1wdXRlcyBOQSBvYnNlcnZhdGlvbnMgZm9yIGNhdGVnb3JpY2FsIHZhcmlhYmxlczogCgpyZXBsYWNlX25hX2NhdGVnb3JpY2FsIDwtIGZ1bmN0aW9uKHgpIHsKICB4ICU+JSAKICAgIHRhYmxlKCkgJT4lIAogICAgYXMuZGF0YS5mcmFtZSgpICU+JSAKICAgIGFycmFuZ2UoLUZyZXEpIC0+PiBteV9kZgogIAogIG5fb2JzIDwtIHN1bShteV9kZiRGcmVxKQogIHBvcCA8LSBteV9kZiQuICU+JSBhcy5jaGFyYWN0ZXIoKQogIHNldC5zZWVkKDI5KQogIHhbaXMubmEoeCldIDwtIHNhbXBsZShwb3AsIHN1bShpcy5uYSh4KSksIHJlcGxhY2UgPSBUUlVFLCBwcm9iID0gbXlfZGYkRnJlcSkKICByZXR1cm4oeCkKfQoKIyBVc2UgdGhlIHR3byBmdW5jdGlvbnM6IApkZiA8LSBobWVxICU+JSAKICBtdXRhdGVfaWYoaXMuZmFjdG9yLCBhcy5jaGFyYWN0ZXIpICU+JSAKICBtdXRhdGUoUkVBU09OID0gY2FzZV93aGVuKFJFQVNPTiA9PSAiIiB+IE5BX2NoYXJhY3Rlcl8sIFRSVUUgfiBSRUFTT04pLCAKICAgICAgICAgSk9CID0gY2FzZV93aGVuKEpPQiA9PSAiIiB+IE5BX2NoYXJhY3Rlcl8sIFRSVUUgfiBKT0IpKSAlPiUKICBtdXRhdGVfaWYoaXNfY2hhcmFjdGVyLCBhcy5mYWN0b3IpICU+JSAKICBtdXRhdGVfaWYoaXMubnVtZXJpYywgcmVwbGFjZV9ieV9tZWFuKSAlPiUgCiAgbXV0YXRlX2lmKGlzLmZhY3RvciwgcmVwbGFjZV9uYV9jYXRlZ29yaWNhbCkKCgojIENvbnZlcnQgQkFEIHRvIGZhY3RvciBhbmQgc2NhbGUgMCAtMSBkYXRhIHNldDogCmRmX2Zvcl9tbCA8LSBkZiAlPiUgCiAgbXV0YXRlKEJBRCA9IGNhc2Vfd2hlbihCQUQgPT0gMSB+ICJCYWQiLCBUUlVFIH4gIkdvb2QiKSAlPiUgYXMuZmFjdG9yKCkpICU+JSAKICBtdXRhdGVfaWYoaXMubnVtZXJpYywgZnVuY3Rpb24oeCkgeyh4IC0gbWluKHgpKSAvIChtYXgoeCkgLSBtaW4oeCkpfSkKCiMgU3BsaXQgZGF0YTogCmxpYnJhcnkoY2FyZXQpCnNldC5zZWVkKDEpCmlkIDwtIGNyZWF0ZURhdGFQYXJ0aXRpb24oeSA9IGRmX2Zvcl9tbCRCQUQsIHAgPSAwLjcsIGxpc3QgPSBGQUxTRSkKZGZfdHJhaW5fbWwgPC0gZGZfZm9yX21sW2lkLCBdCmRmX3Rlc3RfbWwgPC0gZGZfZm9yX21sWy1pZCwgXQoKIyBTZXQgY29uZGl0aW9ucyBmb3IgdHJhaW5pbmcgbW9kZWwgYW5kIGNyb3NzLXZhbGlkYXRpb246IAoKc2V0LnNlZWQoMSkKbnVtYmVyIDwtIDUKcmVwZWF0cyA8LSAyCmNvbnRyb2wgPC0gdHJhaW5Db250cm9sKG1ldGhvZCA9ICJyZXBlYXRlZGN2IiwgCiAgICAgICAgICAgICAgICAgICAgICAgIG51bWJlciA9IG51bWJlciAsIAogICAgICAgICAgICAgICAgICAgICAgICByZXBlYXRzID0gcmVwZWF0cywgCiAgICAgICAgICAgICAgICAgICAgICAgIGNsYXNzUHJvYnMgPSBUUlVFLCAKICAgICAgICAgICAgICAgICAgICAgICAgc2F2ZVByZWRpY3Rpb25zID0gImZpbmFsIiwgCiAgICAgICAgICAgICAgICAgICAgICAgIGluZGV4ID0gY3JlYXRlUmVzYW1wbGUoZGZfdHJhaW5fbWwkQkFELCByZXBlYXRzKm51bWJlciksIAogICAgICAgICAgICAgICAgICAgICAgICBzdW1tYXJ5RnVuY3Rpb24gPSBtdWx0aUNsYXNzU3VtbWFyeSwgCiAgICAgICAgICAgICAgICAgICAgICAgIGFsbG93UGFyYWxsZWwgPSBUUlVFKQoKIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyAgVHJhaW4gTG9naXN0aWMgYW5kIFN1cHBvcnQgVmVjdG9yIE1hY2hpbmUKIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgVHJhaW4gTG9naXN0aWMgTW9kZWw6IAoKbG9naXN0aWMgPC0gdHJhaW4oQkFEIH4gLiwgCiAgICAgICAgICAgICAgICAgIGRhdGEgPSBkZl90cmFpbl9tbCwgCiAgICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJnbG0iLCAKICAgICAgICAgICAgICAgICAgdHJDb250cm9sID0gY29udHJvbCkKCiMgVHJhaW4gU1ZNOiAKCmxpYnJhcnkoa2VybmxhYikKc3ZtIDwtIGtzdm0oQkFEIH4uLCAKICAgICAgICAgICAgZGF0YSA9IGRmX3RyYWluX21sLAogICAgICAgICAgICBrZXJuZWwgPSAicmJmZG90IiwgCiAgICAgICAgICAgIEMgPSAxMCwgCiAgICAgICAgICAgIGVwc2lsb24gPSAwLjEsIAogICAgICAgICAgICBwcm9iLm1vZGVsID0gVFJVRSwgCiAgICAgICAgICAgIGNsYXNzLndlaWdodHMgPSBjKCJCYWQiID0gMTAsICJHb29kIiA9IDEpLCAKICAgICAgICAgICAgY3Jvc3MgPSAxMCkKCgojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyAgQ29tcGFyZSBiZXR3ZWVuIHRoZSB0d28gbW9kZWxzCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBGdW5jdGlvbiBjYWxjdWxhdGVzIHByb2JhYmlsaXRpZXM6IAoKcHJlZGljdF9wcm9iIDwtIGZ1bmN0aW9uKG1vZGVsX3NlbGVjdGVkKSB7CiAgcHJlZGljdChtb2RlbF9zZWxlY3RlZCwgZGZfdGVzdF9tbCwgdHlwZSA9ICJwcm9iIikgJT4lIAogICAgYXMuZGF0YS5mcmFtZSgpICU+JSAKICAgIHB1bGwoQmFkKSAlPiUgCiAgICByZXR1cm4oKQogIH0KCiMgVXNlIHRoaXMgZnVuY3Rpb246IAoKcHJlZF9sb2dpc3RpYyA8LSBwcmVkaWN0X3Byb2IobG9naXN0aWMpCnByZWRfc3ZtIDwtIHByZWRpY3RfcHJvYihzdm0pCgojIEZ1bmN0aW9uIGNhbGN1bGF0ZXMgQVVDOiAKCmxpYnJhcnkocFJPQykgCgp0ZXN0X2F1YyA8LSBmdW5jdGlvbihwcm9iKSB7CiAgcm9jKGRmX3Rlc3RfbWwkQkFELCBwcm9iKQogIH0KCiMgVXNlIHRoaXMgZnVuY3Rpb246IAphdWNfbG9naXN0aWMgPC0gdGVzdF9hdWMocHJlZF9sb2dpc3RpYykKYXVjX3N2bSA8LSB0ZXN0X2F1YyhwcmVkX3N2bSkKCiMgQ3JlYXRlIGEgZGF0YSBmcmFtZSBmb3IgY29tcGFyaW5nOiAKCmRmX2F1YyA8LSBiaW5kX3Jvd3MoZGF0YV9mcmFtZShUUFIgPSBhdWNfbG9naXN0aWMkc2Vuc2l0aXZpdGllcywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBGUFIgPSAxIC0gYXVjX2xvZ2lzdGljJHNwZWNpZmljaXRpZXMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTW9kZWwgPSAiTG9naXN0aWMiKSwgCiAgICAgICAgICAgICAgICAgICAgZGF0YV9mcmFtZShUUFIgPSBhdWNfc3ZtJHNlbnNpdGl2aXRpZXMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRlBSID0gMSAtIGF1Y19zdm0kc3BlY2lmaWNpdGllcywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBNb2RlbCA9ICJTVk0iKSkKCiMgUGxvdCBST0MgY3VydmVzOiAKZGZfYXVjICU+JSAKICBnZ3Bsb3QoYWVzKEZQUiwgVFBSLCBjb2xvciA9IE1vZGVsKSkgKwogIGdlb21fbGluZShzaXplID0gMSkgKwogIHRoZW1lX2J3KCkgKwogIGNvb3JkX2VxdWFsKCkgKwogIGdlb21fYWJsaW5lKGludGVyY2VwdCA9IDAsIHNsb3BlID0gMSwgY29sb3IgPSAiZ3JheTM3Iiwgc2l6ZSA9IDEsIGxpbmV0eXBlID0gImRhc2hlZCIpICsgCiAgbGFicyh4ID0gIkZQUiAoMSAtIFNwZWNpZmljaXR5KSIsIAogICAgICAgeSA9ICJUUFIgKFNlbnNpdGl2aXR5KSIsIAogICAgICAgdGl0bGUgPSAiUk9DIEN1cnZlIGFuZCBBVUM6IExvZ2lzdGljIHZzIFNWTSIpCgoKIyBDb21wYXJlIEFVQzogCmxhcHBseShsaXN0KGF1Y19sb2dpc3RpYywgYXVjX3N2bSksIGZ1bmN0aW9uKHgpIHt4W1siYXVjIl1dfSkKCgojIFJlc3VsdHMgYmFzZWQgb24gdGVzdCBkYXRhOiAKbGFwcGx5KGxpc3QobG9naXN0aWMsIHN2bSksIAogICAgICAgZnVuY3Rpb24obW9kZWwpIHtjb25mdXNpb25NYXRyaXgocHJlZGljdChtb2RlbCwgZGZfdGVzdF9tbCksIGRmX3Rlc3RfbWwkQkFELCBwb3NpdGl2ZSA9ICJCYWQiKX0pCgpgYGAKCiMgRmFtaWx5IG9mIFNWTXMKCmBgYHtyfQojIFVzZSBQYXJhbGxlbCBjb21wdXRpbmc6IApsaWJyYXJ5KGRvUGFyYWxsZWwpCnJlZ2lzdGVyRG9QYXJhbGxlbChjb3JlcyA9IGRldGVjdENvcmVzKCkgLSAxKQoKIyA1IFNWTSBtb2RlbHMgc2VsZWN0ZWQ6IAoKbXlfbW9kZWxzIDwtIGMoInN2bUxpbmVhcldlaWdodHMyIiwgImxzc3ZtUmFkaWFsIiwgCiAgICAgICAgICAgICAgICJzdm1MaW5lYXJXZWlnaHRzIiwgInN2bVJhZGlhbCIsICJzdm1MaW5lYXIyIikKCiMgVHJhaW4gdGhlc2UgTUwgTW9kZWxzOiAKbGlicmFyeShjYXJldEVuc2VtYmxlKQpzZXQuc2VlZCgxKQpzeXN0ZW0udGltZShtb2RlbF9saXN0MSA8LSBjYXJldExpc3QoQkFEIH4uLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSBkZl90cmFpbl9tbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRyQ29udHJvbCA9IGNvbnRyb2wsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRyaWMgPSAiQWNjdXJhY3kiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZExpc3QgPSBteV9tb2RlbHMpKQoKCmxpc3Rfb2ZfcmVzdWx0cyA8LSBsYXBwbHkobXlfbW9kZWxzLCBmdW5jdGlvbih4KSB7bW9kZWxfbGlzdDFbW3hdXSRyZXNhbXBsZX0pCgojIENvbnZlcnQgdG8gZGF0YSBmcmFtZTogCnRvdGFsX2RmIDwtIGRvLmNhbGwoImJpbmRfcm93cyIsIGxpc3Rfb2ZfcmVzdWx0cykKdG90YWxfZGYgJTw+JSBtdXRhdGUoTW9kZWwgPSBsYXBwbHkobXlfbW9kZWxzLCBmdW5jdGlvbih4KSB7cmVwKHgsIG51bWJlcipyZXBlYXRzKX0pICU+JSB1bmxpc3QoKSkKCiMgQXZlcmFnZSBBY2N1cmFjeSBiYXNlZCBvbiAxMCBzYW1wbGVzIGZvciB0aGVzZSBtb2RlbHM6IAp0b3RhbF9kZiAlPiUgCiAgZ3JvdXBfYnkoTW9kZWwpICU+JSAKICBzdW1tYXJpc2UoYXZnX2FjYyA9IG1lYW4oQWNjdXJhY3kpKSAlPiUgCiAgdW5ncm91cCgpICU+JSAKICBhcnJhbmdlKC1hdmdfYWNjKSAlPiUgCiAgbXV0YXRlX2lmKGlzLm51bWVyaWMsIGZ1bmN0aW9uKHgpIHtyb3VuZCh4LCAzKX0pICU+JSAKICBrbml0cjo6a2FibGUoY2FwdGlvbiA9ICJUYWJsZSAxOiBNb2RlbCBQZXJmb3JtYW5jZSBpbiBkZWNyZWFzaW5nIG9yZGVyIG9mIEFjY3VyYWN5IiwgCiAgICAgICAgICAgICAgIGNvbC5uYW1lcyA9IGMoIk1vZGVsIiwgIkF2ZXJhZ2UgQWNjdXJhY3kiKSkKCgojIEF2ZXJhZ2UgU2Vuc2l0aXZpdHkgYmFzZWQgb24gMTAgc2FtcGxlcyBmb3IgdGhlc2UgbW9kZWxzOiAKCnRvdGFsX2RmICU+JSAKICBncm91cF9ieShNb2RlbCkgJT4lIAogIHN1bW1hcmlzZShhdmdfc2VuID0gbWVhbihTZW5zaXRpdml0eSkpICU+JSAKICB1bmdyb3VwKCkgJT4lIAogIGFycmFuZ2UoLWF2Z19zZW4pICU+JSAKICBtdXRhdGVfaWYoaXMubnVtZXJpYywgZnVuY3Rpb24oeCkge3JvdW5kKHgsIDMpfSkgJT4lIAogIGtuaXRyOjprYWJsZShjYXB0aW9uID0gIlRhYmxlIDI6IE1vZGVsIFBlcmZvcm1hbmNlIGluIGRlY3JlYXNpbmcgb3JkZXIgb2YgU2Vuc2l0aXZpdHkiLCAKICAgICAgICAgICAgICAgY29sLm5hbWVzID0gYygiTW9kZWwiLCAiQXZlcmFnZSBTZW5zaXRpdml0eSIpKQoKCgp0aGVtZV9zZXQodGhlbWVfbWluaW1hbCgpKQp0b3RhbF9kZiAlPiUgCiAgc2VsZWN0KC1SZXNhbXBsZSwgLUYxLCAtQmFsYW5jZWRfQWNjdXJhY3kpICU+JSAKICBnYXRoZXIoYSwgYiwgLU1vZGVsKSAlPiUgCiAgZ2dwbG90KGFlcyhNb2RlbCwgYiwgZmlsbCA9IE1vZGVsLCBjb2xvciA9IE1vZGVsKSkgKyAKICBnZW9tX2JveHBsb3Qoc2hvdy5sZWdlbmQgPSBGQUxTRSwgYWxwaGEgPSAwLjMpICsgCiAgZmFjZXRfd3JhcCh+IGEsIHNjYWxlcyA9ICJmcmVlIikgKyAKICBjb29yZF9mbGlwKCkgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpwZXJjZW50KSArIAogIHRoZW1lKHBsb3QubWFyZ2luID0gdW5pdChjKDEsIDEsIDEsIDEpLCAiY20iKSkgKyAKICBsYWJzKHggPSBOVUxMLCB5ID0gTlVMTCwgCiAgICAgICB0aXRsZSA9ICJGaWd1cmUgMTogQSBTaG9ydCBDb21wYXJpc2lvbiBhbW9uZyBTVk0gTW9kZWxzIiwgCiAgICAgICBzdWJ0aXRsZSA9ICJWYWxkYXRpb24gTWV0aG9kIFVzZWQ6IENyb3NzLXZhbGlkYXRpb24sIDEwIHNhbXBsZXMiKQoKCgpgYGAKCgoKIyBSZWZlcmVuY2VzCgoxLiBodHRwczovL21sci5tbHItb3JnLmNvbS9hcnRpY2xlcy90dXRvcmlhbC90dW5lLmh0bWwKMi4gaHR0cHM6Ly93d3cuci1ibG9nZ2Vycy5jb20vdGhlLTV0aC10cmliZS1zdXBwb3J0LXZlY3Rvci1tYWNoaW5lcy1hbmQtY2FyZXQvIAozLiBodHRwczovL3RvcGVwby5naXRodWIuaW8vY2FyZXQvYXZhaWxhYmxlLW1vZGVscy5odG1sCg==