library(caret)
library(doMC)
library(ranger)
library(dplyr)
library(ggplot2)
library(readr)

Dataset for botnet detection

dataset_botnet<-read_csv("https://www.dropbox.com/s/0ziry2zi6gsgpi6/ctu19_filtered_fv.csv?dl=1")
dataset_botnet$label<-dataset_botnet$label %>% as.factor()
test<-dataset_botnet %>% sample_frac(0.2)
train<-setdiff(dataset_botnet,test)

Let’s try 5 models using different numbers of trees using Crossvalidation 3x10

registerDoMC(cores = 4)
ctrl_fast <- trainControl(
  method = "repeatedcv",
  repeats = 3,
  number = 10,
  returnResamp = 'final',
  savePredictions = 'final',
  verboseIter = F,
  classProbs = TRUE,
  allowParallel = T
)
bot_formula<-formula(label ~ sp+wp+wnp+snp+ds+dm+dl+ss+sm+sl)

tgrid <- expand.grid(
  mtry = 3,
  splitrule = "gini",
  min.node.size = 10
)

rfFit10 <- caret::train(bot_formula,
  data = train,
  method = "ranger",
  tuneGrid = tgrid,
  verbose = 2,
  trControl = ctrl_fast,
  num.trees = 10
)

rfFit50 <- caret::train(bot_formula,
  data = train,
  method = "ranger",
  tuneGrid = tgrid,
  verbose = 2,
  trControl = ctrl_fast,
  num.trees = 50
)

rfFit100 <- caret::train(bot_formula,
  data = train,
  method = "ranger",
  tuneGrid = tgrid,
  verbose = 2,
  trControl = ctrl_fast,
  num.trees = 100
)

rfFit500 <- caret::train(bot_formula,
  data = train,
  method = "ranger",
  tuneGrid = tgrid,
  verbose = 2,
  trControl = ctrl_fast,
  num.trees = 500
)


rfFit1000 <- caret::train(bot_formula,
  data = train,
  method = "ranger",
  tuneGrid = tgrid,
  verbose = 2,
  trControl = ctrl_fast,
  num.trees = 1000
)
results<-rbind(rfFit10$results,
               rfFit50$results,
               rfFit100$results,
               rfFit500$results,
               rfFit1000$results)
results<-results %>% tibble::add_column(num.trees=c(10,50,100,500,1000))

results

Plot accuracy and standard deviation

results %>%
  ggplot(aes(x = num.trees %>% as.factor(), y = Accuracy)) +
  geom_point(color = 'red') +
  geom_errorbar(
    aes(ymin = Accuracy - AccuracySD, ymax = Accuracy + AccuracySD),
    width = .02,
    color = 'yellow'
  ) +
  ggdark::dark_theme_bw() +
  xlab("num.trees")+
  labs(title="RF: Mean and Standard deviation after hyper-parameter num.trees tuning")+
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

NA
NA

A function for using ranger se and some other calculations

ranger_rf<-function(n=10,train,test){
  rf <- ranger(label ~ sp+wp+wnp+snp+ds+dm+dl+ss+sm+sl, 
               train, 
               num.trees = n, 
               keep.inbag = TRUE, 
               probability = TRUE)
  pred <- predict(rf, test, type = "se")
  predictions<-pred$predictions %>% 
    as.data.frame() %>% mutate(prob=Botnet)
  predictions$class<-names(pred$predictions %>% 
                             as.data.frame())[max.col(pred$predictions)] 
  predictions$obs<-test$label
  predictions<-predictions %>% 
    select(prob,class,obs) %>% 
    cbind(pred$se %>% as.data.frame()) 
  predictions$se<-predictions %>% 
    apply(MARGIN=1,function(x) x[x['class']])
  predictions<-predictions %>% 
    select(prob,class,se,obs)
  predictions<-predictions %>% 
    mutate(error=ifelse(class!=obs,"uncorrect","correct"))
  accuracy<-predictions %>% 
    summarise(acccuracy=sum(error=='correct')/n()) 
  sensitivity<-predictions %>% 
    summarise( sensitivity=sum( class== 'Botnet' & obs=='Botnet')/sum(obs=='Botnet'))
  specificity<-predictions %>% 
    summarise( sensitivity=sum( class== 'Normal' & obs=='Normal')/sum(obs=='Normal'))
  list(acc = accuracy,
       sens = sensitivity,
       spec = specificity,
       preds = predictions)
}

Now, let’s calculate the SE using Wager’s approach.

rf_10   <- ranger_rf(10,train,test)
rf_50   <- ranger_rf(50,train,test)
rf_100  <- ranger_rf(100,train,test)
rf_500  <- ranger_rf(500,train,test)
rf_1000 <- ranger_rf(1000,train,test)
rf_n10_plot<-ggplot(data=rf_10$preds,aes(x=prob,y=as.numeric(se)))   +
  geom_point(aes(color=error),size=1,alpha=0.5)+
  geom_smooth(se = F,color='red',method = 'loess')+
  labs(caption = paste("Sensitivity:",rf_10$sens,"\nSpecificty:",rf_10$spec,"\nAccuracy:",rf_10$acc))+
  xlim(0,1)+
  ylim(0,3)+
  scale_color_manual(values=c('skyblue','orange'))+
  ggdark::dark_theme_bw()



rf_n50_plot<-ggplot(data=rf_50$preds,aes(x=prob,y=as.numeric(se)))   +
  geom_point(aes(color=error),size=1,alpha=0.5)+
  geom_smooth(se = F,color='red',method = 'loess')+
  labs(caption = paste("Sensitivity:",rf_10$sens,"\nSpecificty:",rf_10$spec,"\nAccuracy:",rf_10$acc))+
  xlim(0,1)+
  ylim(0,1)+
  scale_color_manual(values=c('skyblue','orange'))+
  ggdark::dark_theme_bw()


rf_n100_plot<-ggplot(data=rf_100$preds,aes(x=prob,y=as.numeric(se)))   +
  geom_point(aes(color=error),size=1,alpha=0.5)+
  geom_smooth(se = F,color='red',method = 'loess')+
  labs(caption = paste("Sensitivity:",rf_10$sens,"\nSpecificty:",rf_10$spec,"\nAccuracy:",rf_10$acc))+
  xlim(0,1)+
  ylim(0,1)+
  scale_color_manual(values=c('skyblue','orange'))+
  ggdark::dark_theme_bw()

rf_n500_plot<-ggplot(data=rf_500$preds,aes(x=prob,y=as.numeric(se)))   +
  geom_point(aes(color=error),size=1,alpha=0.5)+
  geom_smooth(se = F,color='red',method = 'loess')+
  labs(caption = paste("Sensitivity:",rf_10$sens,"\nSpecificty:",rf_10$spec,"\nAccuracy:",rf_10$acc))+
  xlim(0,1)+
  ylim(0,1)+
  scale_color_manual(values=c('skyblue','orange'))+
  ggdark::dark_theme_bw()

rf_n1000_plot<-ggplot(data=rf_1000$preds,aes(x=prob,y=as.numeric(se)))   +
  geom_point(aes(color=error),size=1,alpha=0.5)+
  geom_smooth(se = F,color='red',method = 'loess')+
  labs(caption = paste("Sensitivity:",rf_10$sens,"\nSpecificty:",rf_10$spec,"\nAccuracy:",rf_10$acc))+
  xlim(0,1)+
  ylim(0,1)+
  scale_color_manual(values=c('skyblue','orange'))+
  ggdark::dark_theme_bw()
gridExtra::grid.arrange(rf_n50_plot+theme(legend.position = "none")+labs(title="RF ntree=50")+ylab("se"),
                        rf_n100_plot+theme(legend.position = "none")+labs(title="RF ntree=100")+ylab("se"),
                        rf_n500_plot+theme(legend.position = "none")+labs(title="RF ntree=500")+ylab("se"),
                        rf_n1000_plot+theme(legend.position = "none")+labs(title="RF ntree=1000")+ylab("se"),
                        ncol=4)

LS0tCnRpdGxlOiAiUmFuZG9tIEZvcmVzdCBDSSIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoY2FyZXQpCmxpYnJhcnkoZG9NQykKbGlicmFyeShyYW5nZXIpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShyZWFkcikKYGBgCiMgRGF0YXNldCBmb3IgYm90bmV0IGRldGVjdGlvbgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpkYXRhc2V0X2JvdG5ldDwtcmVhZF9jc3YoImh0dHBzOi8vd3d3LmRyb3Bib3guY29tL3MvMHppcnkyemk2Z3NncGk2L2N0dTE5X2ZpbHRlcmVkX2Z2LmNzdj9kbD0xIikKZGF0YXNldF9ib3RuZXQkbGFiZWw8LWRhdGFzZXRfYm90bmV0JGxhYmVsICU+JSBhcy5mYWN0b3IoKQp0ZXN0PC1kYXRhc2V0X2JvdG5ldCAlPiUgc2FtcGxlX2ZyYWMoMC4yKQp0cmFpbjwtc2V0ZGlmZihkYXRhc2V0X2JvdG5ldCx0ZXN0KQpgYGAKCiMgTGV0J3MgdHJ5IDUgbW9kZWxzIHVzaW5nIGRpZmZlcmVudCBudW1iZXJzIG9mIHRyZWVzIHVzaW5nIENyb3NzdmFsaWRhdGlvbiAzeDEwCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CnJlZ2lzdGVyRG9NQyhjb3JlcyA9IDQpCmN0cmxfZmFzdCA8LSB0cmFpbkNvbnRyb2woCiAgbWV0aG9kID0gInJlcGVhdGVkY3YiLAogIHJlcGVhdHMgPSAzLAogIG51bWJlciA9IDEwLAogIHJldHVyblJlc2FtcCA9ICdmaW5hbCcsCiAgc2F2ZVByZWRpY3Rpb25zID0gJ2ZpbmFsJywKICB2ZXJib3NlSXRlciA9IEYsCiAgY2xhc3NQcm9icyA9IFRSVUUsCiAgYWxsb3dQYXJhbGxlbCA9IFQKKQpib3RfZm9ybXVsYTwtZm9ybXVsYShsYWJlbCB+IHNwK3dwK3ducCtzbnArZHMrZG0rZGwrc3Mrc20rc2wpCgp0Z3JpZCA8LSBleHBhbmQuZ3JpZCgKICBtdHJ5ID0gMywKICBzcGxpdHJ1bGUgPSAiZ2luaSIsCiAgbWluLm5vZGUuc2l6ZSA9IDEwCikKCnJmRml0MTAgPC0gY2FyZXQ6OnRyYWluKGJvdF9mb3JtdWxhLAogIGRhdGEgPSB0cmFpbiwKICBtZXRob2QgPSAicmFuZ2VyIiwKICB0dW5lR3JpZCA9IHRncmlkLAogIHZlcmJvc2UgPSAyLAogIHRyQ29udHJvbCA9IGN0cmxfZmFzdCwKICBudW0udHJlZXMgPSAxMAopCgpyZkZpdDUwIDwtIGNhcmV0Ojp0cmFpbihib3RfZm9ybXVsYSwKICBkYXRhID0gdHJhaW4sCiAgbWV0aG9kID0gInJhbmdlciIsCiAgdHVuZUdyaWQgPSB0Z3JpZCwKICB2ZXJib3NlID0gMiwKICB0ckNvbnRyb2wgPSBjdHJsX2Zhc3QsCiAgbnVtLnRyZWVzID0gNTAKKQoKcmZGaXQxMDAgPC0gY2FyZXQ6OnRyYWluKGJvdF9mb3JtdWxhLAogIGRhdGEgPSB0cmFpbiwKICBtZXRob2QgPSAicmFuZ2VyIiwKICB0dW5lR3JpZCA9IHRncmlkLAogIHZlcmJvc2UgPSAyLAogIHRyQ29udHJvbCA9IGN0cmxfZmFzdCwKICBudW0udHJlZXMgPSAxMDAKKQoKcmZGaXQ1MDAgPC0gY2FyZXQ6OnRyYWluKGJvdF9mb3JtdWxhLAogIGRhdGEgPSB0cmFpbiwKICBtZXRob2QgPSAicmFuZ2VyIiwKICB0dW5lR3JpZCA9IHRncmlkLAogIHZlcmJvc2UgPSAyLAogIHRyQ29udHJvbCA9IGN0cmxfZmFzdCwKICBudW0udHJlZXMgPSA1MDAKKQoKCnJmRml0MTAwMCA8LSBjYXJldDo6dHJhaW4oYm90X2Zvcm11bGEsCiAgZGF0YSA9IHRyYWluLAogIG1ldGhvZCA9ICJyYW5nZXIiLAogIHR1bmVHcmlkID0gdGdyaWQsCiAgdmVyYm9zZSA9IDIsCiAgdHJDb250cm9sID0gY3RybF9mYXN0LAogIG51bS50cmVlcyA9IDEwMDAKKQpgYGAKCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpyZXN1bHRzPC1yYmluZChyZkZpdDEwJHJlc3VsdHMsCiAgICAgICAgICAgICAgIHJmRml0NTAkcmVzdWx0cywKICAgICAgICAgICAgICAgcmZGaXQxMDAkcmVzdWx0cywKICAgICAgICAgICAgICAgcmZGaXQ1MDAkcmVzdWx0cywKICAgICAgICAgICAgICAgcmZGaXQxMDAwJHJlc3VsdHMpCnJlc3VsdHM8LXJlc3VsdHMgJT4lIHRpYmJsZTo6YWRkX2NvbHVtbihudW0udHJlZXM9YygxMCw1MCwxMDAsNTAwLDEwMDApKQoKcmVzdWx0cwpgYGAKCiMgUGxvdCBhY2N1cmFjeSBhbmQgc3RhbmRhcmQgZGV2aWF0aW9uCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CnJlc3VsdHMgJT4lCiAgZ2dwbG90KGFlcyh4ID0gbnVtLnRyZWVzICU+JSBhcy5mYWN0b3IoKSwgeSA9IEFjY3VyYWN5KSkgKwogIGdlb21fcG9pbnQoY29sb3IgPSAncmVkJykgKwogIGdlb21fZXJyb3JiYXIoCiAgICBhZXMoeW1pbiA9IEFjY3VyYWN5IC0gQWNjdXJhY3lTRCwgeW1heCA9IEFjY3VyYWN5ICsgQWNjdXJhY3lTRCksCiAgICB3aWR0aCA9IC4wMiwKICAgIGNvbG9yID0gJ3llbGxvdycKICApICsKICBnZ2Rhcms6OmRhcmtfdGhlbWVfYncoKSArCiAgeGxhYigibnVtLnRyZWVzIikrCiAgbGFicyh0aXRsZT0iUkY6IE1lYW4gYW5kIFN0YW5kYXJkIGRldmlhdGlvbiBhZnRlciBoeXBlci1wYXJhbWV0ZXIgbnVtLnRyZWVzIHR1bmluZyIpKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpCgoKYGBgCgoKIyBBIGZ1bmN0aW9uIGZvciB1c2luZyByYW5nZXIgc2UgYW5kIHNvbWUgb3RoZXIgY2FsY3VsYXRpb25zCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpyYW5nZXJfcmY8LWZ1bmN0aW9uKG49MTAsdHJhaW4sdGVzdCl7CiAgcmYgPC0gcmFuZ2VyKGxhYmVsIH4gc3Ard3Ard25wK3NucCtkcytkbStkbCtzcytzbStzbCwgCiAgICAgICAgICAgICAgIHRyYWluLCAKICAgICAgICAgICAgICAgbnVtLnRyZWVzID0gbiwgCiAgICAgICAgICAgICAgIGtlZXAuaW5iYWcgPSBUUlVFLCAKICAgICAgICAgICAgICAgcHJvYmFiaWxpdHkgPSBUUlVFKQogIHByZWQgPC0gcHJlZGljdChyZiwgdGVzdCwgdHlwZSA9ICJzZSIpCiAgcHJlZGljdGlvbnM8LXByZWQkcHJlZGljdGlvbnMgJT4lIAogICAgYXMuZGF0YS5mcmFtZSgpICU+JSBtdXRhdGUocHJvYj1Cb3RuZXQpCiAgcHJlZGljdGlvbnMkY2xhc3M8LW5hbWVzKHByZWQkcHJlZGljdGlvbnMgJT4lIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzLmRhdGEuZnJhbWUoKSlbbWF4LmNvbChwcmVkJHByZWRpY3Rpb25zKV0gCiAgcHJlZGljdGlvbnMkb2JzPC10ZXN0JGxhYmVsCiAgcHJlZGljdGlvbnM8LXByZWRpY3Rpb25zICU+JSAKICAgIHNlbGVjdChwcm9iLGNsYXNzLG9icykgJT4lIAogICAgY2JpbmQocHJlZCRzZSAlPiUgYXMuZGF0YS5mcmFtZSgpKSAKICBwcmVkaWN0aW9ucyRzZTwtcHJlZGljdGlvbnMgJT4lIAogICAgYXBwbHkoTUFSR0lOPTEsZnVuY3Rpb24oeCkgeFt4WydjbGFzcyddXSkKICBwcmVkaWN0aW9uczwtcHJlZGljdGlvbnMgJT4lIAogICAgc2VsZWN0KHByb2IsY2xhc3Msc2Usb2JzKQogIHByZWRpY3Rpb25zPC1wcmVkaWN0aW9ucyAlPiUgCiAgICBtdXRhdGUoZXJyb3I9aWZlbHNlKGNsYXNzIT1vYnMsInVuY29ycmVjdCIsImNvcnJlY3QiKSkKICBhY2N1cmFjeTwtcHJlZGljdGlvbnMgJT4lIAogICAgc3VtbWFyaXNlKGFjY2N1cmFjeT1zdW0oZXJyb3I9PSdjb3JyZWN0JykvbigpKSAKICBzZW5zaXRpdml0eTwtcHJlZGljdGlvbnMgJT4lIAogICAgc3VtbWFyaXNlKCBzZW5zaXRpdml0eT1zdW0oIGNsYXNzPT0gJ0JvdG5ldCcgJiBvYnM9PSdCb3RuZXQnKS9zdW0ob2JzPT0nQm90bmV0JykpCiAgc3BlY2lmaWNpdHk8LXByZWRpY3Rpb25zICU+JSAKICAgIHN1bW1hcmlzZSggc2Vuc2l0aXZpdHk9c3VtKCBjbGFzcz09ICdOb3JtYWwnICYgb2JzPT0nTm9ybWFsJykvc3VtKG9icz09J05vcm1hbCcpKQogIGxpc3QoYWNjID0gYWNjdXJhY3ksCiAgICAgICBzZW5zID0gc2Vuc2l0aXZpdHksCiAgICAgICBzcGVjID0gc3BlY2lmaWNpdHksCiAgICAgICBwcmVkcyA9IHByZWRpY3Rpb25zKQp9CmBgYAoKIyBOb3csIGxldCdzIGNhbGN1bGF0ZSB0aGUgU0UgdXNpbmcgV2FnZXIncyBhcHByb2FjaC4gCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpyZl8xMCAgIDwtIHJhbmdlcl9yZigxMCx0cmFpbix0ZXN0KQpyZl81MCAgIDwtIHJhbmdlcl9yZig1MCx0cmFpbix0ZXN0KQpyZl8xMDAgIDwtIHJhbmdlcl9yZigxMDAsdHJhaW4sdGVzdCkKcmZfNTAwICA8LSByYW5nZXJfcmYoNTAwLHRyYWluLHRlc3QpCnJmXzEwMDAgPC0gcmFuZ2VyX3JmKDEwMDAsdHJhaW4sdGVzdCkKYGBgCgoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KcmZfbjEwX3Bsb3Q8LWdncGxvdChkYXRhPXJmXzEwJHByZWRzLGFlcyh4PXByb2IseT1hcy5udW1lcmljKHNlKSkpICAgKwogIGdlb21fcG9pbnQoYWVzKGNvbG9yPWVycm9yKSxzaXplPTEsYWxwaGE9MC41KSsKICBnZW9tX3Ntb290aChzZSA9IEYsY29sb3I9J3JlZCcsbWV0aG9kID0gJ2xvZXNzJykrCiAgbGFicyhjYXB0aW9uID0gcGFzdGUoIlNlbnNpdGl2aXR5OiIscmZfMTAkc2VucywiXG5TcGVjaWZpY3R5OiIscmZfMTAkc3BlYywiXG5BY2N1cmFjeToiLHJmXzEwJGFjYykpKwogIHhsaW0oMCwxKSsKICB5bGltKDAsMykrCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCdza3libHVlJywnb3JhbmdlJykpKwogIGdnZGFyazo6ZGFya190aGVtZV9idygpCgoKCnJmX241MF9wbG90PC1nZ3Bsb3QoZGF0YT1yZl81MCRwcmVkcyxhZXMoeD1wcm9iLHk9YXMubnVtZXJpYyhzZSkpKSAgICsKICBnZW9tX3BvaW50KGFlcyhjb2xvcj1lcnJvciksc2l6ZT0xLGFscGhhPTAuNSkrCiAgZ2VvbV9zbW9vdGgoc2UgPSBGLGNvbG9yPSdyZWQnLG1ldGhvZCA9ICdsb2VzcycpKwogIGxhYnMoY2FwdGlvbiA9IHBhc3RlKCJTZW5zaXRpdml0eToiLHJmXzEwJHNlbnMsIlxuU3BlY2lmaWN0eToiLHJmXzEwJHNwZWMsIlxuQWNjdXJhY3k6IixyZl8xMCRhY2MpKSsKICB4bGltKDAsMSkrCiAgeWxpbSgwLDEpKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9Yygnc2t5Ymx1ZScsJ29yYW5nZScpKSsKICBnZ2Rhcms6OmRhcmtfdGhlbWVfYncoKQoKCnJmX24xMDBfcGxvdDwtZ2dwbG90KGRhdGE9cmZfMTAwJHByZWRzLGFlcyh4PXByb2IseT1hcy5udW1lcmljKHNlKSkpICAgKwogIGdlb21fcG9pbnQoYWVzKGNvbG9yPWVycm9yKSxzaXplPTEsYWxwaGE9MC41KSsKICBnZW9tX3Ntb290aChzZSA9IEYsY29sb3I9J3JlZCcsbWV0aG9kID0gJ2xvZXNzJykrCiAgbGFicyhjYXB0aW9uID0gcGFzdGUoIlNlbnNpdGl2aXR5OiIscmZfMTAkc2VucywiXG5TcGVjaWZpY3R5OiIscmZfMTAkc3BlYywiXG5BY2N1cmFjeToiLHJmXzEwJGFjYykpKwogIHhsaW0oMCwxKSsKICB5bGltKDAsMSkrCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCdza3libHVlJywnb3JhbmdlJykpKwogIGdnZGFyazo6ZGFya190aGVtZV9idygpCgpyZl9uNTAwX3Bsb3Q8LWdncGxvdChkYXRhPXJmXzUwMCRwcmVkcyxhZXMoeD1wcm9iLHk9YXMubnVtZXJpYyhzZSkpKSAgICsKICBnZW9tX3BvaW50KGFlcyhjb2xvcj1lcnJvciksc2l6ZT0xLGFscGhhPTAuNSkrCiAgZ2VvbV9zbW9vdGgoc2UgPSBGLGNvbG9yPSdyZWQnLG1ldGhvZCA9ICdsb2VzcycpKwogIGxhYnMoY2FwdGlvbiA9IHBhc3RlKCJTZW5zaXRpdml0eToiLHJmXzEwJHNlbnMsIlxuU3BlY2lmaWN0eToiLHJmXzEwJHNwZWMsIlxuQWNjdXJhY3k6IixyZl8xMCRhY2MpKSsKICB4bGltKDAsMSkrCiAgeWxpbSgwLDEpKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9Yygnc2t5Ymx1ZScsJ29yYW5nZScpKSsKICBnZ2Rhcms6OmRhcmtfdGhlbWVfYncoKQoKcmZfbjEwMDBfcGxvdDwtZ2dwbG90KGRhdGE9cmZfMTAwMCRwcmVkcyxhZXMoeD1wcm9iLHk9YXMubnVtZXJpYyhzZSkpKSAgICsKICBnZW9tX3BvaW50KGFlcyhjb2xvcj1lcnJvciksc2l6ZT0xLGFscGhhPTAuNSkrCiAgZ2VvbV9zbW9vdGgoc2UgPSBGLGNvbG9yPSdyZWQnLG1ldGhvZCA9ICdsb2VzcycpKwogIGxhYnMoY2FwdGlvbiA9IHBhc3RlKCJTZW5zaXRpdml0eToiLHJmXzEwJHNlbnMsIlxuU3BlY2lmaWN0eToiLHJmXzEwJHNwZWMsIlxuQWNjdXJhY3k6IixyZl8xMCRhY2MpKSsKICB4bGltKDAsMSkrCiAgeWxpbSgwLDEpKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9Yygnc2t5Ymx1ZScsJ29yYW5nZScpKSsKICBnZ2Rhcms6OmRhcmtfdGhlbWVfYncoKQoKCgoKYGBgCgoKYGBge3IgZmlnLmhlaWdodD00LCBmaWcud2lkdGg9MTQsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmdyaWRFeHRyYTo6Z3JpZC5hcnJhbmdlKHJmX241MF9wbG90K3RoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikrbGFicyh0aXRsZT0iUkYgbnRyZWU9NTAiKSt5bGFiKCJzZSIpLAogICAgICAgICAgICAgICAgICAgICAgICByZl9uMTAwX3Bsb3QrdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKStsYWJzKHRpdGxlPSJSRiBudHJlZT0xMDAiKSt5bGFiKCJzZSIpLAogICAgICAgICAgICAgICAgICAgICAgICByZl9uNTAwX3Bsb3QrdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKStsYWJzKHRpdGxlPSJSRiBudHJlZT01MDAiKSt5bGFiKCJzZSIpLAogICAgICAgICAgICAgICAgICAgICAgICByZl9uMTAwMF9wbG90K3RoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikrbGFicyh0aXRsZT0iUkYgbnRyZWU9MTAwMCIpK3lsYWIoInNlIiksCiAgICAgICAgICAgICAgICAgICAgICAgIG5jb2w9NCkKYGBgCgo=