Data preparation
#' Helper function drawing rows from a subset of the dataset
#' @param dataset dataset
#' @param test_fraction what fraction of each class should become a test set
draw_train_test <- function(dataset, test_fraction) {
N_row <- nrow(dataset)
set.seed(777)
test_rows <- sample(1:N_row, as.integer(test_fraction * N_row), replace = FALSE)
train_rows <- (1:N_row)[!(1:N_row %in% test_rows)]
list(train = dataset[train_rows, ], test = dataset[test_rows, ])
}
#' Function distributing the data to training and test sets
#' @param dataset dataset
#' @param taget target column
#' @param test_fraction what fraction of each class should become a test set
make_train_test <- function(dataset, target, test_fraction) {
result_train <- NULL
result_test <- NULL
for (target_elem in unique(dataset[[target]])) {
train_test <- draw_train_test(dataset[dataset[[target]] == target_elem, ], test_fraction)
result_train <- bind_rows(result_train, train_test[["train"]])
result_test <- bind_rows(result_test, train_test[["test"]])
}
list(train = result_train, test = result_test)
}
#' Function plotting variable importance
#' @param rpart_model trained rpart model
#' @param var_no number of variables
var_imp_plot <- function(rpart_model, var_no = 10) {
png("var_importance.png", width = 1600, height = 1900, res= 300)
oldpar <- par(mar = c(13, 4, 3, 2))
plot(rpart_model$learner.model$variable.importance[1:var_no], xlab = "",
ylab="", xaxt = "n", pch = 20,
main = "Variable Importance Plot")
title(ylab = "variable importance", mgp = c(2.3, 1, 0))
axis(1, at=1:var_no, labels = names(rpart_model$learner.model$variable.importance)[1:var_no], las=2)
par(oldpar)
dev.off()
}
mood_data <- readRDS('mood_data.RDS')
# split dataset into those with and without mood data
mood_data_all <- mood_data[!is.na(mood_data$mood), ]
mood_data_na <- mood_data[is.na(mood_data$mood), ]
mood_train_test <- make_train_test(mood_data_all, target = "mood", test_fraction = 0.3)
mood_train <- mood_train_test[["train"]]
mood_test <- mood_train_test[["test"]]
saveRDS(mood_test, file = "mood_test_full.RDS")
saveRDS(mood_train, file = "mood_train_full.RDS")
## this is specific to a classification task
severity_train <- mood_train$mood %in% c(5, 6)
severity_test <- mood_test$mood %in% c(5, 6)
mood_data_train_class <- mood_train %>%
select(-c('mood')) %>%
add_column(severity = severity_train, .before = 1)
mood_data_test_class <- mood_test %>%
select(-c('mood')) %>%
add_column(severity = severity_test, .before = 1)
saveRDS(mood_data_train_class, "mood_train.RDS")
saveRDS(mood_data_test_class, "mood_test.RDS")
saveRDS(mood_data_na, "mood_data_na.RDS")
The rpart tree algorithm
Task, Learner and Training
# TUTAJ ZMIENIAJ!
severity_task <- makeClassifTask(data = mood_data_train_class, target = "severity")
Provided data is not a pure data.frame but from class tbl_df, hence it will be converted.
tree_learner <- makeLearner("classif.rpart")
getParamSet(tree_learner)
Hyperparameter tuning
tree_parameter_space <- makeParamSet(
makeIntegerParam("minsplit", lower = 1, upper = 20),
makeIntegerParam("minbucket", lower = 1, upper = 16),
makeNumericParam("cp", lower = 0.01, upper = 0.1),
makeIntegerParam("maxdepth", lower = 3, upper = 10),
makeIntegerParam("maxsurrogate", lower = 90, upper = 110)
)
rand_search <- makeTuneControlRandom(maxit = 200)
cv_for_tuning <- makeResampleDesc("CV", iters = 5, stratify = TRUE)
library(parallel)
library(parallelMap)
print(paste("Detected ", detectCores(), " cores", collapse = ""))
[1] "Detected 4 cores"
parallelStartSocket(cpus = detectCores())
Starting parallelization in mode=socket with cpus=4.
tuned_tree_pars <- tuneParams(tree_learner, task = severity_task,
resampling = cv_for_tuning,
par.set = tree_parameter_space,
control = rand_search)
[Tune] Started tuning learner classif.rpart for parameter set:
With control class: TuneControlRandom
Imputation value: 1
Exporting objects to slaves for mode socket: .mlr.slave.options
Mapping in parallel: mode = socket; level = mlr.tuneParams; cpus = 4; elements = 200.
[Tune] Result: minsplit=20; minbucket=16; cp=0.0157; maxdepth=3; maxsurrogate=101 : mmce.test.mean=0.041190
parallelStop()
Stopped parallelization. All cleaned up.
tuned_tree_pars
Tune result:
Op. pars: minsplit=20; minbucket=16; cp=0.0157; maxdepth=3; maxsurrogate=101
mmce.test.mean=0.041190
Training the model
tuned_tree <- setHyperPars(tree_learner, par.vals = tuned_tree_pars$x)
tuned_tree_model <- train(tuned_tree, severity_task)
saveRDS(tuned_tree_model, file = "tuned_tree_model.RDS")
library(rpart.plot)
Ładowanie wymaganego pakietu: rpart
tree_model_data <- getLearnerModel(tuned_tree_model)
png(filename = "drzewko.png", res = 300, width = 1800, height = 1800)
rpart.plot(tree_model_data, roundint = FALSE,
box.palette = "BuBn",
type = 5,
fallen.leaves = TRUE)
dev.off()
null device
1
severity_task_data <- severity_task$env$data
predicted_mood_tree <- predict(tuned_tree_model, newdata = severity_task_data)
performance <- performance(predicted_mood_tree, measures = list(mmce, acc))
calculateConfusionMatrix(predicted_mood_tree)
predicted
true FALSE TRUE -err.-
FALSE 1490 7 7
TRUE 89 18 89
-err.- 89 7 96
Large tree
large_tree_pars <- list(
maxdepth = 10,
minsplit = tuned_tree_pars$x$minsplit,
maxsurrogate = tuned_tree_pars$x$maxsurrogate,
minbucket = 1,
cp = 0.00
)
large_tree <- setHyperPars(tree_learner, par.vals = large_tree_pars)
large_tree_model <- train(large_tree, severity_task)
saveRDS(large_tree_model, file = "large_tree_model.RDS")
tree_large_model_data <- getLearnerModel(large_tree_model)
png(filename = "drzewko_big.png", res = 300, width = 1800, height = 1800)
rpart.plot(tree_large_model_data, roundint = FALSE,
box.palette = "BuBn",
type = 5,
fallen.leaves = TRUE)
dev.off()
null device
1
predicted_mood_tree <- predict(large_tree_model, newdata = severity_task_data)
performance <- performance(predicted_mood_tree, measures = list(mmce, acc))
print(performance)
mmce acc
0.03990025 0.96009975
calculateConfusionMatrix(predicted_mood_tree)
predicted
true FALSE TRUE -err.-
FALSE 1478 19 19
TRUE 45 62 45
-err.- 45 19 64
Exploring the model
printcp(tree_model_data, digits = 3)
Classification tree:
rpart::rpart(formula = f, data = d, xval = 0, minsplit = 20,
minbucket = 16, cp = 0.0157401523250155, maxdepth = 3, maxsurrogate = 101)
Variables actually used in tree construction:
[1] Q18_6_worthless Q27_comparisons
Root node error: 107/1604 = 0.0667
n= 1604
CP nsplit rel error
1 0.0514 0 1.000
2 0.0157 2 0.897
summary(tree_model_data)
Call:
rpart::rpart(formula = f, data = d, xval = 0, minsplit = 20,
minbucket = 16, cp = 0.0157401523250155, maxdepth = 3, maxsurrogate = 101)
n= 1604
CP nsplit rel error
1 0.05140187 0 1.0000000
2 0.01574015 2 0.8971963
Variable importance
Q18_6_worthless Q27_comparisons Q18_7_felt_bad Q20_self_assessment
53 20 3 2
Q28_peer_group Q18_8_fled Q_52_wasting_time Q13_1_happy
2 2 2 2
Q15_13_nothing Q19_2_health Q19_6_rel_bg Q19_9_time
2 2 2 2
Q2_school_plan_real Q32_victim Q_53_procrastination Q13_4_can_count
2 2 1 1
Q26_school_relationships Q3_edu_fear_4_work Q30_antisocial_behavior Q34_support
1 1 1 1
Q6_father_does_not_work
1
Node number 1: 1604 observations, complexity param=0.05140187
predicted class=FALSE expected loss=0.06670823 P(node) =1
class counts: 1497 107
probabilities: 0.933 0.067
left son=2 (1525 obs) right son=3 (79 obs)
Primary splits:
Q18_6_worthless < 2.5 to the left, improve=19.36646, (14 missing)
Q13_6_time < 2.5 to the right, improve=16.83481, (15 missing)
Q18_5_quarrel < 1.5 to the left, improve=13.99542, (11 missing)
Q13_9_important < 2.5 to the left, improve=12.89911, (9 missing)
Q13_2_not_notice < 3.5 to the right, improve=12.76664, (7 missing)
Surrogate splits:
Q18_7_felt_bad < 2.5 to the left, agree=0.953, adj=0.051, (8 split)
Q18_8_fled < 2.5 to the left, agree=0.953, adj=0.038, (0 split)
Node number 2: 1525 observations
predicted class=FALSE expected loss=0.04918033 P(node) =0.9507481
class counts: 1450 75
probabilities: 0.951 0.049
Node number 3: 79 observations, complexity param=0.05140187
predicted class=FALSE expected loss=0.4050633 P(node) =0.04925187
class counts: 47 32
probabilities: 0.595 0.405
left son=6 (54 obs) right son=7 (25 obs)
Primary splits:
Q27_comparisons < 1.771242 to the right, improve=7.255209, (0 missing)
Q51_1_phone < 2.75 to the left, improve=5.206590, (2 missing)
Q13_1_happy < 3.5 to the left, improve=4.366234, (2 missing)
Q15_8_meet < 0.5 to the right, improve=4.310256, (1 missing)
Q51_2_sms < 3.75 to the left, improve=3.933770, (8 missing)
Surrogate splits:
Q20_self_assessment < 2.166667 to the right, agree=0.722, adj=0.12, (0 split)
Q28_peer_group splits as LR, agree=0.722, adj=0.12, (0 split)
Q2_school_plan_real < 2.5 to the right, agree=0.709, adj=0.08, (0 split)
Q13_1_happy < 3.5 to the left, agree=0.709, adj=0.08, (0 split)
Q15_13_nothing < 0.5 to the left, agree=0.709, adj=0.08, (0 split)
Q19_2_health < 1.5 to the right, agree=0.709, adj=0.08, (0 split)
Q19_6_rel_bg < 3.5 to the right, agree=0.709, adj=0.08, (0 split)
Q19_9_time < 3.5 to the right, agree=0.709, adj=0.08, (0 split)
Q32_victim < 1.083333 to the left, agree=0.709, adj=0.08, (0 split)
Q_52_wasting_time < 2.75 to the left, agree=0.709, adj=0.08, (0 split)
Q3_edu_fear_4_work splits as RL, agree=0.696, adj=0.04, (0 split)
Q6_father_does_not_work < 0.5 to the left, agree=0.696, adj=0.04, (0 split)
Q13_4_can_count < 4.5 to the left, agree=0.696, adj=0.04, (0 split)
Q26_school_relationships < 1.5 to the right, agree=0.696, adj=0.04, (0 split)
Q30_antisocial_behavior < 0.025 to the right, agree=0.696, adj=0.04, (0 split)
Q34_support < 0.5 to the right, agree=0.696, adj=0.04, (0 split)
Q_53_procrastination < 1.083333 to the right, agree=0.696, adj=0.04, (0 split)
Node number 6: 54 observations
predicted class=FALSE expected loss=0.2592593 P(node) =0.03366584
class counts: 40 14
probabilities: 0.741 0.259
Node number 7: 25 observations
predicted class=TRUE expected loss=0.28 P(node) =0.01558603
class counts: 7 18
probabilities: 0.280 0.720
var_imp_plot(large_tree_model, 20)
null device
1
Cross-validation
outer <- makeResampleDesc("CV", iters = 5)
tree_wrapper <- makeTuneWrapper("classif.rpart", resampling = cv_for_tuning,
par.set = tree_parameter_space,
control = rand_search)
parallelStartSocket(cpus = detectCores())
Starting parallelization in mode=socket with cpus=4.
cv_with_tuning <- resample(tree_wrapper, severity_task, resampling = outer)
Exporting objects to slaves for mode socket: .mlr.slave.options
Resampling: cross-validation
Measures: mmce
Mapping in parallel: mode = socket; level = mlr.resample; cpus = 4; elements = 5.
Aggregated Result: mmce.test.mean=0.03990
parallelStop()
Stopped parallelization. All cleaned up.
cv_with_tuning
Resample Result
Task: mood_data_train_class
Learner: classif.rpart.tuned
Aggr perf: mmce.test.mean=0.03990
Runtime: 769.024
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpCmBgYApgYGB7cn0KbGlicmFyeShtbHIpCmxpYnJhcnkobWFncml0dHIpCmxpYnJhcnkodGlkeXZlcnNlKQpgYGAKIyBEYXRhIHByZXBhcmF0aW9uCmBgYHtyfQojJyBIZWxwZXIgZnVuY3Rpb24gZHJhd2luZyByb3dzIGZyb20gYSBzdWJzZXQgb2YgdGhlIGRhdGFzZXQKIycgQHBhcmFtIGRhdGFzZXQgZGF0YXNldAojJyBAcGFyYW0gdGVzdF9mcmFjdGlvbiB3aGF0IGZyYWN0aW9uIG9mIGVhY2ggY2xhc3Mgc2hvdWxkIGJlY29tZSBhIHRlc3Qgc2V0CmRyYXdfdHJhaW5fdGVzdCA8LSBmdW5jdGlvbihkYXRhc2V0LCB0ZXN0X2ZyYWN0aW9uKSB7CiAgTl9yb3cgPC0gbnJvdyhkYXRhc2V0KQogIHNldC5zZWVkKDc3NykKICB0ZXN0X3Jvd3MgPC0gc2FtcGxlKDE6Tl9yb3csIGFzLmludGVnZXIodGVzdF9mcmFjdGlvbiAqIE5fcm93KSwgcmVwbGFjZSA9IEZBTFNFKQogIHRyYWluX3Jvd3MgPC0gKDE6Tl9yb3cpWyEoMTpOX3JvdyAlaW4lIHRlc3Rfcm93cyldCiAgbGlzdCh0cmFpbiA9IGRhdGFzZXRbdHJhaW5fcm93cywgXSwgdGVzdCA9IGRhdGFzZXRbdGVzdF9yb3dzLCBdKQp9CiMnIEZ1bmN0aW9uIGRpc3RyaWJ1dGluZyB0aGUgZGF0YSB0byB0cmFpbmluZyBhbmQgdGVzdCBzZXRzCiMnIEBwYXJhbSBkYXRhc2V0IGRhdGFzZXQKIycgQHBhcmFtIHRhZ2V0IHRhcmdldCBjb2x1bW4KIycgQHBhcmFtIHRlc3RfZnJhY3Rpb24gd2hhdCBmcmFjdGlvbiBvZiBlYWNoIGNsYXNzIHNob3VsZCBiZWNvbWUgYSB0ZXN0IHNldAptYWtlX3RyYWluX3Rlc3QgPC0gZnVuY3Rpb24oZGF0YXNldCwgdGFyZ2V0LCB0ZXN0X2ZyYWN0aW9uKSB7CiAgcmVzdWx0X3RyYWluIDwtIE5VTEwKICByZXN1bHRfdGVzdCA8LSBOVUxMCiAgZm9yICh0YXJnZXRfZWxlbSBpbiB1bmlxdWUoZGF0YXNldFtbdGFyZ2V0XV0pKSB7CiAgICB0cmFpbl90ZXN0IDwtIGRyYXdfdHJhaW5fdGVzdChkYXRhc2V0W2RhdGFzZXRbW3RhcmdldF1dID09IHRhcmdldF9lbGVtLCBdLCB0ZXN0X2ZyYWN0aW9uKQogICAgcmVzdWx0X3RyYWluIDwtIGJpbmRfcm93cyhyZXN1bHRfdHJhaW4sIHRyYWluX3Rlc3RbWyJ0cmFpbiJdXSkKICAgIHJlc3VsdF90ZXN0IDwtIGJpbmRfcm93cyhyZXN1bHRfdGVzdCwgdHJhaW5fdGVzdFtbInRlc3QiXV0pCiAgfQogIGxpc3QodHJhaW4gPSByZXN1bHRfdHJhaW4sIHRlc3QgPSByZXN1bHRfdGVzdCkKfQojJyBGdW5jdGlvbiBwbG90dGluZyB2YXJpYWJsZSBpbXBvcnRhbmNlCiMnIEBwYXJhbSBycGFydF9tb2RlbCB0cmFpbmVkIHJwYXJ0IG1vZGVsCiMnIEBwYXJhbSB2YXJfbm8gbnVtYmVyIG9mIHZhcmlhYmxlcwp2YXJfaW1wX3Bsb3QgPC0gZnVuY3Rpb24ocnBhcnRfbW9kZWwsIHZhcl9ubyA9IDEwKSB7CiAgcG5nKCJ2YXJfaW1wb3J0YW5jZS5wbmciLCB3aWR0aCA9IDE2MDAsIGhlaWdodCA9IDE5MDAsIHJlcz0gMzAwKQogIG9sZHBhciA8LSBwYXIobWFyID0gYygxMywgNCwgMywgMikpCiAgcGxvdChycGFydF9tb2RlbCRsZWFybmVyLm1vZGVsJHZhcmlhYmxlLmltcG9ydGFuY2VbMTp2YXJfbm9dLCB4bGFiID0gIiIsIAogICAgICAgeWxhYj0iIiwgeGF4dCA9ICJuIiwgcGNoID0gMjAsCiAgICAgICBtYWluID0gIlZhcmlhYmxlIEltcG9ydGFuY2UgUGxvdCIpCiAgdGl0bGUoeWxhYiA9ICJ2YXJpYWJsZSBpbXBvcnRhbmNlIiwgbWdwID0gYygyLjMsIDEsIDApKQogIGF4aXMoMSwgYXQ9MTp2YXJfbm8sIGxhYmVscyA9IG5hbWVzKHJwYXJ0X21vZGVsJGxlYXJuZXIubW9kZWwkdmFyaWFibGUuaW1wb3J0YW5jZSlbMTp2YXJfbm9dLCBsYXM9MikKICBwYXIob2xkcGFyKQogIGRldi5vZmYoKQp9CmBgYAoKCmBgYHtyfQptb29kX2RhdGEgPC0gcmVhZFJEUygnbW9vZF9kYXRhLlJEUycpCgojIHNwbGl0IGRhdGFzZXQgaW50byB0aG9zZSB3aXRoIGFuZCB3aXRob3V0IG1vb2QgZGF0YQptb29kX2RhdGFfYWxsIDwtIG1vb2RfZGF0YVshaXMubmEobW9vZF9kYXRhJG1vb2QpLCBdCm1vb2RfZGF0YV9uYSA8LSBtb29kX2RhdGFbaXMubmEobW9vZF9kYXRhJG1vb2QpLCBdCgptb29kX3RyYWluX3Rlc3QgPC0gbWFrZV90cmFpbl90ZXN0KG1vb2RfZGF0YV9hbGwsIHRhcmdldCA9ICJtb29kIiwgdGVzdF9mcmFjdGlvbiA9IDAuMykKbW9vZF90cmFpbiA8LSBtb29kX3RyYWluX3Rlc3RbWyJ0cmFpbiJdXQptb29kX3Rlc3QgPC0gbW9vZF90cmFpbl90ZXN0W1sidGVzdCJdXQpzYXZlUkRTKG1vb2RfdGVzdCwgZmlsZSA9ICJtb29kX3Rlc3RfZnVsbC5SRFMiKQpzYXZlUkRTKG1vb2RfdHJhaW4sIGZpbGUgPSAibW9vZF90cmFpbl9mdWxsLlJEUyIpCgojIyB0aGlzIGlzIHNwZWNpZmljIHRvIGEgY2xhc3NpZmljYXRpb24gdGFzawpzZXZlcml0eV90cmFpbiA8LSBtb29kX3RyYWluJG1vb2QgJWluJSBjKDUsIDYpCnNldmVyaXR5X3Rlc3QgPC0gbW9vZF90ZXN0JG1vb2QgJWluJSBjKDUsIDYpCgptb29kX2RhdGFfdHJhaW5fY2xhc3MgPC0gbW9vZF90cmFpbiAlPiUgCiAgc2VsZWN0KC1jKCdtb29kJykpICU+JSAKICBhZGRfY29sdW1uKHNldmVyaXR5ID0gc2V2ZXJpdHlfdHJhaW4sIC5iZWZvcmUgPSAxKQoKbW9vZF9kYXRhX3Rlc3RfY2xhc3MgPC0gbW9vZF90ZXN0ICU+JSAKICBzZWxlY3QoLWMoJ21vb2QnKSkgJT4lIAogIGFkZF9jb2x1bW4oc2V2ZXJpdHkgPSBzZXZlcml0eV90ZXN0LCAuYmVmb3JlID0gMSkKCnNhdmVSRFMobW9vZF9kYXRhX3RyYWluX2NsYXNzLCAibW9vZF90cmFpbi5SRFMiKQpzYXZlUkRTKG1vb2RfZGF0YV90ZXN0X2NsYXNzLCAibW9vZF90ZXN0LlJEUyIpCnNhdmVSRFMobW9vZF9kYXRhX25hLCAibW9vZF9kYXRhX25hLlJEUyIpCmBgYAoKIyBUaGUgcnBhcnQgdHJlZSBhbGdvcml0aG0KCiMjIFRhc2ssIExlYXJuZXIgYW5kIFRyYWluaW5nCmBgYHtyfQojIFRVVEFKIFpNSUVOSUFKIQpzZXZlcml0eV90YXNrIDwtIG1ha2VDbGFzc2lmVGFzayhkYXRhID0gbW9vZF9kYXRhX3RyYWluX2NsYXNzLCB0YXJnZXQgPSAic2V2ZXJpdHkiKQp0cmVlX2xlYXJuZXIgPC0gbWFrZUxlYXJuZXIoImNsYXNzaWYucnBhcnQiKQpnZXRQYXJhbVNldCh0cmVlX2xlYXJuZXIpCmBgYAoKIyMgSHlwZXJwYXJhbWV0ZXIgdHVuaW5nCgpgYGB7cn0KdHJlZV9wYXJhbWV0ZXJfc3BhY2UgPC0gbWFrZVBhcmFtU2V0KAogIG1ha2VJbnRlZ2VyUGFyYW0oIm1pbnNwbGl0IiwgbG93ZXIgPSAxLCB1cHBlciA9IDIwKSwKICBtYWtlSW50ZWdlclBhcmFtKCJtaW5idWNrZXQiLCBsb3dlciA9IDEsIHVwcGVyID0gMTYpLAogIG1ha2VOdW1lcmljUGFyYW0oImNwIiwgbG93ZXIgPSAwLjAxLCB1cHBlciA9IDAuMSksCiAgbWFrZUludGVnZXJQYXJhbSgibWF4ZGVwdGgiLCBsb3dlciA9IDMsIHVwcGVyID0gMTApLAogIG1ha2VJbnRlZ2VyUGFyYW0oIm1heHN1cnJvZ2F0ZSIsIGxvd2VyID0gOTAsIHVwcGVyID0gMTEwKQopCgpyYW5kX3NlYXJjaCA8LSBtYWtlVHVuZUNvbnRyb2xSYW5kb20obWF4aXQgPSAyMDApCmN2X2Zvcl90dW5pbmcgPC0gbWFrZVJlc2FtcGxlRGVzYygiQ1YiLCBpdGVycyA9IDUsIHN0cmF0aWZ5ID0gVFJVRSkKCmxpYnJhcnkocGFyYWxsZWwpCmxpYnJhcnkocGFyYWxsZWxNYXApCnByaW50KHBhc3RlKCJEZXRlY3RlZCAiLCBkZXRlY3RDb3JlcygpLCAiIGNvcmVzIiwgY29sbGFwc2UgPSAiIikpCnBhcmFsbGVsU3RhcnRTb2NrZXQoY3B1cyA9IGRldGVjdENvcmVzKCkpCnR1bmVkX3RyZWVfcGFycyA8LSB0dW5lUGFyYW1zKHRyZWVfbGVhcm5lciwgdGFzayA9IHNldmVyaXR5X3Rhc2ssCiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXNhbXBsaW5nID0gY3ZfZm9yX3R1bmluZywKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhci5zZXQgPSB0cmVlX3BhcmFtZXRlcl9zcGFjZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb250cm9sID0gcmFuZF9zZWFyY2gpCnBhcmFsbGVsU3RvcCgpCnR1bmVkX3RyZWVfcGFycwpgYGAKIyMgVHJhaW5pbmcgdGhlIG1vZGVsCgpgYGB7cn0KdHVuZWRfdHJlZSA8LSBzZXRIeXBlclBhcnModHJlZV9sZWFybmVyLCBwYXIudmFscyA9IHR1bmVkX3RyZWVfcGFycyR4KQp0dW5lZF90cmVlX21vZGVsIDwtIHRyYWluKHR1bmVkX3RyZWUsIHNldmVyaXR5X3Rhc2spCnNhdmVSRFModHVuZWRfdHJlZV9tb2RlbCwgZmlsZSA9ICJ0dW5lZF90cmVlX21vZGVsLlJEUyIpCmBgYAoKYGBge3J9CmxpYnJhcnkocnBhcnQucGxvdCkKdHJlZV9tb2RlbF9kYXRhIDwtIGdldExlYXJuZXJNb2RlbCh0dW5lZF90cmVlX21vZGVsKQpwbmcoZmlsZW5hbWUgPSAiZHJ6ZXdrby5wbmciLCByZXMgPSAzMDAsIHdpZHRoID0gMTgwMCwgaGVpZ2h0ID0gMTgwMCkKcnBhcnQucGxvdCh0cmVlX21vZGVsX2RhdGEsIHJvdW5kaW50ID0gRkFMU0UsIAogICAgICAgICAgIGJveC5wYWxldHRlID0gIkJ1Qm4iLAogICAgICAgICAgIHR5cGUgPSA1LAogICAgICAgICAgIGZhbGxlbi5sZWF2ZXMgPSBUUlVFKQpkZXYub2ZmKCkKc2V2ZXJpdHlfdGFza19kYXRhIDwtIHNldmVyaXR5X3Rhc2skZW52JGRhdGEKcHJlZGljdGVkX21vb2RfdHJlZSA8LSBwcmVkaWN0KHR1bmVkX3RyZWVfbW9kZWwsIG5ld2RhdGEgPSBzZXZlcml0eV90YXNrX2RhdGEpCnBlcmZvcm1hbmNlIDwtIHBlcmZvcm1hbmNlKHByZWRpY3RlZF9tb29kX3RyZWUsIG1lYXN1cmVzID0gbGlzdChtbWNlLCBhY2MpKQpjYWxjdWxhdGVDb25mdXNpb25NYXRyaXgocHJlZGljdGVkX21vb2RfdHJlZSkKYGBgCiMjIExhcmdlIHRyZWUKYGBge3J9CmxhcmdlX3RyZWVfcGFycyA8LSBsaXN0KAogIG1heGRlcHRoID0gMTAsIAogIG1pbnNwbGl0ID0gdHVuZWRfdHJlZV9wYXJzJHgkbWluc3BsaXQsIAogIG1heHN1cnJvZ2F0ZSA9ICB0dW5lZF90cmVlX3BhcnMkeCRtYXhzdXJyb2dhdGUsICAKICBtaW5idWNrZXQgPSAxLCAKICBjcCA9IDAuMDAKKQoKbGFyZ2VfdHJlZSA8LSBzZXRIeXBlclBhcnModHJlZV9sZWFybmVyLCBwYXIudmFscyA9IGxhcmdlX3RyZWVfcGFycykKbGFyZ2VfdHJlZV9tb2RlbCA8LSB0cmFpbihsYXJnZV90cmVlLCBzZXZlcml0eV90YXNrKQpzYXZlUkRTKGxhcmdlX3RyZWVfbW9kZWwsIGZpbGUgPSAibGFyZ2VfdHJlZV9tb2RlbC5SRFMiKQp0cmVlX2xhcmdlX21vZGVsX2RhdGEgPC0gZ2V0TGVhcm5lck1vZGVsKGxhcmdlX3RyZWVfbW9kZWwpCnBuZyhmaWxlbmFtZSA9ICJkcnpld2tvX2JpZy5wbmciLCByZXMgPSAzMDAsIHdpZHRoID0gMTgwMCwgaGVpZ2h0ID0gMTgwMCkKcnBhcnQucGxvdCh0cmVlX2xhcmdlX21vZGVsX2RhdGEsIHJvdW5kaW50ID0gRkFMU0UsIAogICAgICAgICAgIGJveC5wYWxldHRlID0gIkJ1Qm4iLAogICAgICAgICAgIHR5cGUgPSA1LAogICAgICAgICAgIGZhbGxlbi5sZWF2ZXMgPSBUUlVFKQpkZXYub2ZmKCkKCnByZWRpY3RlZF9tb29kX3RyZWUgPC0gcHJlZGljdChsYXJnZV90cmVlX21vZGVsLCBuZXdkYXRhID0gc2V2ZXJpdHlfdGFza19kYXRhKQpwZXJmb3JtYW5jZSA8LSBwZXJmb3JtYW5jZShwcmVkaWN0ZWRfbW9vZF90cmVlLCBtZWFzdXJlcyA9IGxpc3QobW1jZSwgYWNjKSkKcHJpbnQocGVyZm9ybWFuY2UpCmNhbGN1bGF0ZUNvbmZ1c2lvbk1hdHJpeChwcmVkaWN0ZWRfbW9vZF90cmVlKQpgYGAKCiMjIEV4cGxvcmluZyB0aGUgbW9kZWwKYGBge3J9CnByaW50Y3AodHJlZV9tb2RlbF9kYXRhLCBkaWdpdHMgPSAzKQpgYGAKYGBge3J9CnN1bW1hcnkodHJlZV9tb2RlbF9kYXRhKQoKdmFyX2ltcF9wbG90KGxhcmdlX3RyZWVfbW9kZWwsIDIwKQpgYGAKCiMjIENyb3NzLXZhbGlkYXRpb24KCmBgYHtyfQpvdXRlciA8LSBtYWtlUmVzYW1wbGVEZXNjKCJDViIsIGl0ZXJzID0gNSkKdHJlZV93cmFwcGVyIDwtIG1ha2VUdW5lV3JhcHBlcigiY2xhc3NpZi5ycGFydCIsIHJlc2FtcGxpbmcgPSBjdl9mb3JfdHVuaW5nLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFyLnNldCA9IHRyZWVfcGFyYW1ldGVyX3NwYWNlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29udHJvbCA9IHJhbmRfc2VhcmNoKQpwYXJhbGxlbFN0YXJ0U29ja2V0KGNwdXMgPSBkZXRlY3RDb3JlcygpKQpjdl93aXRoX3R1bmluZyA8LSByZXNhbXBsZSh0cmVlX3dyYXBwZXIsIHNldmVyaXR5X3Rhc2ssIHJlc2FtcGxpbmcgPSBvdXRlcikKcGFyYWxsZWxTdG9wKCkKY3Zfd2l0aF90dW5pbmcKYGBgCg==