Example 1: Wisconsin Breast Cancer Dataset
wbc <- read.table('data/breast_cancer.csv', header=TRUE, sep=',')
wbc$id <- NULL
summary(wbc)
diagnosis radius_mean texture_mean perimeter_mean area_mean smoothness_mean
B:357 Min. : 6.981 Min. : 9.71 Min. : 43.79 Min. : 143.5 Min. :0.05263
M:212 1st Qu.:11.700 1st Qu.:16.17 1st Qu.: 75.17 1st Qu.: 420.3 1st Qu.:0.08637
Median :13.370 Median :18.84 Median : 86.24 Median : 551.1 Median :0.09587
Mean :14.127 Mean :19.29 Mean : 91.97 Mean : 654.9 Mean :0.09636
3rd Qu.:15.780 3rd Qu.:21.80 3rd Qu.:104.10 3rd Qu.: 782.7 3rd Qu.:0.10530
Max. :28.110 Max. :39.28 Max. :188.50 Max. :2501.0 Max. :0.16340
compactness_mean concavity_mean concave.points_mean symmetry_mean
Min. :0.01938 Min. :0.00000 Min. :0.00000 Min. :0.1060
1st Qu.:0.06492 1st Qu.:0.02956 1st Qu.:0.02031 1st Qu.:0.1619
Median :0.09263 Median :0.06154 Median :0.03350 Median :0.1792
Mean :0.10434 Mean :0.08880 Mean :0.04892 Mean :0.1812
3rd Qu.:0.13040 3rd Qu.:0.13070 3rd Qu.:0.07400 3rd Qu.:0.1957
Max. :0.34540 Max. :0.42680 Max. :0.20120 Max. :0.3040
fractal_dimension_mean radius_se texture_se perimeter_se area_se
Min. :0.04996 Min. :0.1115 Min. :0.3602 Min. : 0.757 Min. : 6.802
1st Qu.:0.05770 1st Qu.:0.2324 1st Qu.:0.8339 1st Qu.: 1.606 1st Qu.: 17.850
Median :0.06154 Median :0.3242 Median :1.1080 Median : 2.287 Median : 24.530
Mean :0.06280 Mean :0.4052 Mean :1.2169 Mean : 2.866 Mean : 40.337
3rd Qu.:0.06612 3rd Qu.:0.4789 3rd Qu.:1.4740 3rd Qu.: 3.357 3rd Qu.: 45.190
Max. :0.09744 Max. :2.8730 Max. :4.8850 Max. :21.980 Max. :542.200
smoothness_se compactness_se concavity_se concave.points_se symmetry_se
Min. :0.001713 Min. :0.002252 Min. :0.00000 Min. :0.000000 Min. :0.007882
1st Qu.:0.005169 1st Qu.:0.013080 1st Qu.:0.01509 1st Qu.:0.007638 1st Qu.:0.015160
Median :0.006380 Median :0.020450 Median :0.02589 Median :0.010930 Median :0.018730
Mean :0.007041 Mean :0.025478 Mean :0.03189 Mean :0.011796 Mean :0.020542
3rd Qu.:0.008146 3rd Qu.:0.032450 3rd Qu.:0.04205 3rd Qu.:0.014710 3rd Qu.:0.023480
Max. :0.031130 Max. :0.135400 Max. :0.39600 Max. :0.052790 Max. :0.078950
fractal_dimension_se radius_worst texture_worst perimeter_worst area_worst
Min. :0.0008948 Min. : 7.93 Min. :12.02 Min. : 50.41 Min. : 185.2
1st Qu.:0.0022480 1st Qu.:13.01 1st Qu.:21.08 1st Qu.: 84.11 1st Qu.: 515.3
Median :0.0031870 Median :14.97 Median :25.41 Median : 97.66 Median : 686.5
Mean :0.0037949 Mean :16.27 Mean :25.68 Mean :107.26 Mean : 880.6
3rd Qu.:0.0045580 3rd Qu.:18.79 3rd Qu.:29.72 3rd Qu.:125.40 3rd Qu.:1084.0
Max. :0.0298400 Max. :36.04 Max. :49.54 Max. :251.20 Max. :4254.0
smoothness_worst compactness_worst concavity_worst concave.points_worst symmetry_worst
Min. :0.07117 Min. :0.02729 Min. :0.0000 Min. :0.00000 Min. :0.1565
1st Qu.:0.11660 1st Qu.:0.14720 1st Qu.:0.1145 1st Qu.:0.06493 1st Qu.:0.2504
Median :0.13130 Median :0.21190 Median :0.2267 Median :0.09993 Median :0.2822
Mean :0.13237 Mean :0.25427 Mean :0.2722 Mean :0.11461 Mean :0.2901
3rd Qu.:0.14600 3rd Qu.:0.33910 3rd Qu.:0.3829 3rd Qu.:0.16140 3rd Qu.:0.3179
Max. :0.22260 Max. :1.05800 Max. :1.2520 Max. :0.29100 Max. :0.6638
fractal_dimension_worst
Min. :0.05504
1st Qu.:0.07146
Median :0.08004
Mean :0.08395
3rd Qu.:0.09208
Max. :0.20750
set.seed(1)
train.index <- createDataPartition(wbc$diagnosis, p = .8, list=FALSE)
train <- wbc[ train.index,]
test <- wbc[-train.index,]
summary(train$diagnosis)
B M
286 170
summary(test$diagnosis)
B M
71 42
tree_mod <- rpart(diagnosis ~ ., train, method="class",
control = rpart.control(minsplit = 4,
minbucket = 2,
cp = 0,
maxdepth = 6))
print(tree_mod)
n= 456
node), split, n, loss, yval, (yprob)
* denotes terminal node
1) root 456 170 B (0.62719298 0.37280702)
2) concave.points_worst< 0.1417 299 24 B (0.91973244 0.08026756)
4) radius_worst< 17.72 281 8 B (0.97153025 0.02846975)
8) area_se< 38.605 266 3 B (0.98872180 0.01127820)
16) concave.points_worst< 0.13235 253 0 B (1.00000000 0.00000000) *
17) concave.points_worst>=0.13235 13 3 B (0.76923077 0.23076923)
34) texture_mean< 21.54 10 0 B (1.00000000 0.00000000) *
35) texture_mean>=21.54 3 0 M (0.00000000 1.00000000) *
9) area_se>=38.605 15 5 B (0.66666667 0.33333333)
18) compactness_se>=0.014885 11 1 B (0.90909091 0.09090909) *
19) compactness_se< 0.014885 4 0 M (0.00000000 1.00000000) *
5) radius_worst>=17.72 18 2 M (0.11111111 0.88888889)
10) concavity_worst< 0.1981 4 2 B (0.50000000 0.50000000)
20) texture_mean< 21.26 2 0 B (1.00000000 0.00000000) *
21) texture_mean>=21.26 2 0 M (0.00000000 1.00000000) *
11) concavity_worst>=0.1981 14 0 M (0.00000000 1.00000000) *
3) concave.points_worst>=0.1417 157 11 M (0.07006369 0.92993631)
6) area_worst< 729.55 16 7 B (0.56250000 0.43750000)
12) smoothness_mean< 0.1083 9 0 B (1.00000000 0.00000000) *
13) smoothness_mean>=0.1083 7 0 M (0.00000000 1.00000000) *
7) area_worst>=729.55 141 2 M (0.01418440 0.98581560) *
rpart.plot(tree_mod, extra=1, cex=0.8)

train_pred <- predict(tree_mod, train, type="class")
test_pred <- predict(tree_mod, test, type="class")
cat('Training Accuracy: ', mean(train_pred == train$diagnosis), '\n',
'Test Set Accuracy: ', mean(test_pred == test$diagnosis), sep='')
Training Accuracy: 0.9934211
Test Set Accuracy: 0.9115044
Cross-Validation for Model Evaluation
set.seed(1)
bc_tree_cv <- train(diagnosis ~ ., wbc, method="rpart2",
trControl = trainControl(method="cv", number=10),
tuneGrid = expand.grid(maxdepth=c(6)),
control = rpart.control(minsplit = 4,
minbucket = 2,
cp = 0))
bc_tree_cv
CART
569 samples
30 predictor
2 classes: 'B', 'M'
No pre-processing
Resampling: Cross-Validated (10 fold)
Summary of sample sizes: 513, 512, 511, 512, 511, 513, ...
Resampling results:
Accuracy Kappa
0.9211326 0.8310163
Tuning parameter 'maxdepth' was held constant at a value of 6
Cross-Validation for Tuning Maximum Depth
set.seed(1)
bc_tree_cv_md <- train(diagnosis ~ ., wbc, method="rpart2",
trControl = trainControl(method="cv", number=10),
tuneGrid = expand.grid(maxdepth=1:20),
control = rpart.control(minsplit = 4,
minbucket = 2,
cp = 0))
best_ix = which.max(bc_tree_cv_md$results$Accuracy)
bc_tree_cv_md$results[best_ix, ]
plot(bc_tree_cv_md, pch="")

print(bc_tree_cv_md$finalModel)
n= 569
node), split, n, loss, yval, (yprob)
* denotes terminal node
1) root 569 212 B (0.627416520 0.372583480)
2) radius_worst< 16.795 379 33 B (0.912928760 0.087071240)
4) concave.points_worst< 0.1358 333 5 B (0.984984985 0.015015015)
8) radius_se< 0.6431 328 3 B (0.990853659 0.009146341) *
9) radius_se>=0.6431 5 2 B (0.600000000 0.400000000)
18) compactness_mean>=0.062575 3 0 B (1.000000000 0.000000000) *
19) compactness_mean< 0.062575 2 0 M (0.000000000 1.000000000) *
5) concave.points_worst>=0.1358 46 18 M (0.391304348 0.608695652)
10) texture_worst< 25.67 19 4 B (0.789473684 0.210526316)
20) area_worst< 810.3 15 1 B (0.933333333 0.066666667) *
21) area_worst>=810.3 4 1 M (0.250000000 0.750000000) *
11) texture_worst>=25.67 27 3 M (0.111111111 0.888888889)
22) concavity_mean< 0.09679 6 3 B (0.500000000 0.500000000)
44) texture_mean< 19.435 3 0 B (1.000000000 0.000000000) *
45) texture_mean>=19.435 3 0 M (0.000000000 1.000000000) *
23) concavity_mean>=0.09679 21 0 M (0.000000000 1.000000000) *
3) radius_worst>=16.795 190 11 M (0.057894737 0.942105263)
6) texture_mean< 16.11 17 8 B (0.529411765 0.470588235)
12) concave.points_mean< 0.06626 9 0 B (1.000000000 0.000000000) *
13) concave.points_mean>=0.06626 8 0 M (0.000000000 1.000000000) *
7) texture_mean>=16.11 173 2 M (0.011560694 0.988439306)
14) concavity_worst< 0.1907 5 2 M (0.400000000 0.600000000)
28) texture_mean< 21.26 2 0 B (1.000000000 0.000000000) *
29) texture_mean>=21.26 3 0 M (0.000000000 1.000000000) *
15) concavity_worst>=0.1907 168 0 M (0.000000000 1.000000000) *
rpart.plot(bc_tree_cv_md$finalModel, extra=1, cex=0.8)

Cross-Validation for Tuning Complexity Parameter
set.seed(1)
bc_tree_cv_cp <- train(diagnosis ~ ., wbc, method="rpart",
trControl = trainControl(method="cv", number=10),
tuneGrid = expand.grid(cp=seq(0, 0.1, 0.001)),
control = rpart.control(minsplit = 4,
minbucket = 2,
maxdepth = 30))
best_ix = which.max(bc_tree_cv_cp$results$Accuracy)
bc_tree_cv_cp$results[best_ix, ]
NA
plot(bc_tree_cv_cp, pch="")

Example 2: Iris Dataset
iris <- read.table('data/iris.txt', sep='\t', header=TRUE)
summary(iris)
sepal_length sepal_width petal_length petal_width species
Min. :4.300 Min. :2.000 Min. :1.000 Min. :0.100 setosa :50
1st Qu.:5.100 1st Qu.:2.800 1st Qu.:1.600 1st Qu.:0.300 versicolor:50
Median :5.800 Median :3.000 Median :4.350 Median :1.300 virginica :50
Mean :5.843 Mean :3.057 Mean :3.758 Mean :1.199
3rd Qu.:6.400 3rd Qu.:3.300 3rd Qu.:5.100 3rd Qu.:1.800
Max. :7.900 Max. :4.400 Max. :6.900 Max. :2.500
p1 <- ggplot(iris, aes(x=sepal_length, y=sepal_width, col=species)) +
geom_point(alpha=0.8)
p2 <- ggplot(iris, aes(x=petal_length, y=petal_width, col=species)) +
geom_point(alpha=0.8)
grid.arrange(p1, p2, ncol=2)

Cross-Validation for Tuning Maximum Depth
set.seed(1)
iris_tree_cv <- train(species ~ ., iris, method="rpart2",
trControl = trainControl(method="cv", number=10),
tuneGrid = expand.grid(maxdepth=1:20),
control = rpart.control(minsplit = 1,
minbucket = 1,
cp = 0))
best_ix = which.max(iris_tree_cv$results$Accuracy)
iris_tree_cv$results[best_ix, ]
plot(iris_tree_cv, pch="")

rpart.plot(iris_tree_cv$finalModel, extra=1, cex=1)

Example 3: Wine Quality Dataset
wine <- read.table("data/winequality-white.csv", sep=",", header=TRUE)
wine$grade <- ifelse(wine$quality < 5, "L",
ifelse(wine$quality < 7, "M", "H"))
wine$grade <- factor(wine$grade, levels=c("L", "M", "H"))
wine$quality <- NULL
summary(wine)
fixed.acidity volatile.acidity citric.acid residual.sugar chlorides
Min. : 3.800 Min. :0.0800 Min. :0.0000 Min. : 0.600 Min. :0.00900
1st Qu.: 6.300 1st Qu.:0.2100 1st Qu.:0.2700 1st Qu.: 1.700 1st Qu.:0.03600
Median : 6.800 Median :0.2600 Median :0.3200 Median : 5.200 Median :0.04300
Mean : 6.855 Mean :0.2782 Mean :0.3342 Mean : 6.391 Mean :0.04577
3rd Qu.: 7.300 3rd Qu.:0.3200 3rd Qu.:0.3900 3rd Qu.: 9.900 3rd Qu.:0.05000
Max. :14.200 Max. :1.1000 Max. :1.6600 Max. :65.800 Max. :0.34600
free.sulfur.dioxide total.sulfur.dioxide density pH sulphates
Min. : 2.00 Min. : 9.0 Min. :0.9871 Min. :2.720 Min. :0.2200
1st Qu.: 23.00 1st Qu.:108.0 1st Qu.:0.9917 1st Qu.:3.090 1st Qu.:0.4100
Median : 34.00 Median :134.0 Median :0.9937 Median :3.180 Median :0.4700
Mean : 35.31 Mean :138.4 Mean :0.9940 Mean :3.188 Mean :0.4898
3rd Qu.: 46.00 3rd Qu.:167.0 3rd Qu.:0.9961 3rd Qu.:3.280 3rd Qu.:0.5500
Max. :289.00 Max. :440.0 Max. :1.0390 Max. :3.820 Max. :1.0800
alcohol grade
Min. : 8.00 L: 183
1st Qu.: 9.50 M:3655
Median :10.40 H:1060
Mean :10.51
3rd Qu.:11.40
Max. :14.20
Cross-Validation for Tuning Maximum Depth
set.seed(1)
wine_tree_cv_md <- train(grade ~ ., wine, method="rpart2",
trControl = trainControl(method="cv", number=10),
tuneGrid = expand.grid(maxdepth=seq(50, 300, by=5)),
control = rpart.control(minsplit = 16,
minbucket = 1,
cp = 0))
best_ix = which.max(wine_tree_cv_md$results$Accuracy)
wine_tree_cv_md$results[best_ix, ]
plot(wine_tree_cv_md, pch="")

Cross-Validation for Tuning Complexity Parameter
set.seed(1)
wine_tree_cv_cp <- train(grade ~ ., wine, method="rpart",
trControl = trainControl(method="cv", number=10),
tuneGrid = expand.grid(cp=seq(0, 0.05, 0.001)),
control = rpart.control(minsplit = 32,
minbucket = 4,
maxdepth = 10))
best_ix = which.max(wine_tree_cv_cp$results$Accuracy)
wine_tree_cv_cp$results[best_ix, ]
plot(wine_tree_cv_cp, pch="")

rpart.plot(wine_tree_cv_cp$finalModel, extra=1, cex = 0.8)

Example 4: Diamonds Dataset
diamonds <- read.table("data/diamonds.txt", sep="\t", header=TRUE)
diamonds <- diamonds[,c(1:4,7)]
summary(diamonds)
carat cut color clarity price
Min. :0.2000 Fair : 1610 D: 6775 SI1 :13065 Min. : 326
1st Qu.:0.4000 Good : 4906 E: 9797 VS2 :12258 1st Qu.: 950
Median :0.7000 Ideal :21551 F: 9542 SI2 : 9194 Median : 2401
Mean :0.7979 Premium :13791 G:11292 VS1 : 8171 Mean : 3933
3rd Qu.:1.0400 Very Good:12082 H: 8304 VVS2 : 5066 3rd Qu.: 5324
Max. :5.0100 I: 5422 VVS1 : 3655 Max. :18823
J: 2808 (Other): 2531
Creating a Single Tree Model
set.seed(1)
dmd_tree_mod <- rpart(price ~ ., diamonds, method="anova",
control = rpart.control(minsplit = 32,
minbucket = 16,
cp = 0.001,
maxdepth = 6))
rpart.plot(dmd_tree_mod, extra=1, cex = 0.8)

pred <- predict(dmd_tree_mod, diamonds)
SSE <- sum((diamonds$price - pred)^2)
SST <- sum((diamonds$price - mean(diamonds$price))^2)
r2 <- 1 - SSE / SST
r2
[1] 0.9453158
Cross-Validation for Tuning Maximum Depth
set.seed(1)
dmd_tree_cv_md <- train(price ~ ., diamonds, method="rpart2", metric="Rsquared",
trControl = trainControl(method="cv", number=10),
tuneGrid = expand.grid(maxdepth=seq(50, 600, by=50)),
control = rpart.control(minsplit = 32,
minbucket = 16,
cp = 0.00001))
best_ix = which.max(dmd_tree_cv_md$results$Rsquared)
dmd_tree_cv_md$results[best_ix, ]
plot(dmd_tree_cv_md, pch="")

LS0tDQp0aXRsZTogIkxlc3NvbiA4LjEgLSBEZWNpc2lvbiBUcmVlcyINCmF1dGhvcjogIlJvYmJpZSBCZWFuZSINCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vazoNCiAgICB0aGVtZTogZmxhdGx5DQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZGVwdGg6IDQNCi0tLQ0KDQojIyMgKipMb2FkIFBhY2thZ2VzKioNCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0V9DQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KGdyaWRFeHRyYSkNCmxpYnJhcnkoY2FyZXQpDQpsaWJyYXJ5KHJwYXJ0KQ0KbGlicmFyeShycGFydC5wbG90KQ0KYGBgDQoNCiMjIyAqKkV4YW1wbGUgMTogV2lzY29uc2luIEJyZWFzdCBDYW5jZXIgRGF0YXNldCoqDQoNCmBgYHtyfQ0Kd2JjIDwtIHJlYWQudGFibGUoJ2RhdGEvYnJlYXN0X2NhbmNlci5jc3YnLCBoZWFkZXI9VFJVRSwgc2VwPScsJykNCndiYyRpZCA8LSBOVUxMDQpzdW1tYXJ5KHdiYykNCmBgYA0KDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoMSkNCnRyYWluLmluZGV4IDwtIGNyZWF0ZURhdGFQYXJ0aXRpb24od2JjJGRpYWdub3NpcywgcCA9IC44LCBsaXN0PUZBTFNFKQ0KdHJhaW4gPC0gd2JjWyB0cmFpbi5pbmRleCxdDQp0ZXN0ICA8LSB3YmNbLXRyYWluLmluZGV4LF0NCg0Kc3VtbWFyeSh0cmFpbiRkaWFnbm9zaXMpDQpzdW1tYXJ5KHRlc3QkZGlhZ25vc2lzKQ0KYGBgDQoNCg0KYGBge3J9DQp0cmVlX21vZCA8LSBycGFydChkaWFnbm9zaXMgfiAuLCB0cmFpbiwgbWV0aG9kPSJjbGFzcyIsIA0KICAgICAgICAgICAgICAgICAgY29udHJvbCA9IHJwYXJ0LmNvbnRyb2wobWluc3BsaXQgPSA0LCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pbmJ1Y2tldCA9ICAyLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNwID0gMCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXhkZXB0aCA9IDYpKQ0KDQpwcmludCh0cmVlX21vZCkNCmBgYA0KDQoNCmBgYHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9Nn0NCnJwYXJ0LnBsb3QodHJlZV9tb2QsIGV4dHJhPTEsIGNleD0wLjgpDQpgYGANCg0KDQpgYGB7cn0NCnRyYWluX3ByZWQgPC0gcHJlZGljdCh0cmVlX21vZCwgdHJhaW4sIHR5cGU9ImNsYXNzIikNCnRlc3RfcHJlZCA8LSBwcmVkaWN0KHRyZWVfbW9kLCB0ZXN0LCB0eXBlPSJjbGFzcyIpDQoNCmNhdCgnVHJhaW5pbmcgQWNjdXJhY3k6ICcsIG1lYW4odHJhaW5fcHJlZCA9PSB0cmFpbiRkaWFnbm9zaXMpLCAnXG4nLA0KICAgICdUZXN0IFNldCBBY2N1cmFjeTogJywgbWVhbih0ZXN0X3ByZWQgPT0gdGVzdCRkaWFnbm9zaXMpLCBzZXA9JycpDQpgYGANCg0KIyMjIyAqKkNyb3NzLVZhbGlkYXRpb24gZm9yIE1vZGVsIEV2YWx1YXRpb24qKiANCg0KDQpgYGB7cn0NCnNldC5zZWVkKDEpDQoNCmJjX3RyZWVfY3YgPC0gdHJhaW4oZGlhZ25vc2lzIH4gLiwgd2JjLCBtZXRob2Q9InJwYXJ0MiIsIA0KICAgICAgICAgICAgICAgICB0ckNvbnRyb2wgPSB0cmFpbkNvbnRyb2wobWV0aG9kPSJjdiIsIG51bWJlcj0xMCksDQogICAgICAgICAgICAgICAgIHR1bmVHcmlkID0gZXhwYW5kLmdyaWQobWF4ZGVwdGg9Yyg2KSksDQogICAgICAgICAgICAgICAgIGNvbnRyb2wgPSBycGFydC5jb250cm9sKG1pbnNwbGl0ID0gNCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pbmJ1Y2tldCA9ICAyLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY3AgPSAwKSkNCg0KYmNfdHJlZV9jdg0KYGBgDQoNCiMjIyMgKipDcm9zcy1WYWxpZGF0aW9uIGZvciBUdW5pbmcgTWF4aW11bSBEZXB0aCoqDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoMSkNCg0KYmNfdHJlZV9jdl9tZCA8LSB0cmFpbihkaWFnbm9zaXMgfiAuLCB3YmMsIG1ldGhvZD0icnBhcnQyIiwgDQogICAgICAgICAgICAgICAgICAgICAgIHRyQ29udHJvbCA9IHRyYWluQ29udHJvbChtZXRob2Q9ImN2IiwgbnVtYmVyPTEwKSwNCiAgICAgICAgICAgICAgICAgICAgICAgdHVuZUdyaWQgPSBleHBhbmQuZ3JpZChtYXhkZXB0aD0xOjIwKSwNCiAgICAgICAgICAgICAgICAgICAgICAgY29udHJvbCA9IHJwYXJ0LmNvbnRyb2wobWluc3BsaXQgPSA0LCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWluYnVja2V0ID0gIDIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjcCA9IDApKQ0KDQpiZXN0X2l4ID0gd2hpY2gubWF4KGJjX3RyZWVfY3ZfbWQkcmVzdWx0cyRBY2N1cmFjeSkNCmJjX3RyZWVfY3ZfbWQkcmVzdWx0c1tiZXN0X2l4LCBdDQpgYGANCg0KYGBge3J9DQpwbG90KGJjX3RyZWVfY3ZfbWQsIHBjaD0iIikNCmBgYA0KDQpgYGB7cn0NCnByaW50KGJjX3RyZWVfY3ZfbWQkZmluYWxNb2RlbCkNCmBgYA0KDQpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTZ9DQpycGFydC5wbG90KGJjX3RyZWVfY3ZfbWQkZmluYWxNb2RlbCwgZXh0cmE9MSwgY2V4PTAuOCkNCmBgYA0KDQojIyMjICoqQ3Jvc3MtVmFsaWRhdGlvbiBmb3IgVHVuaW5nIENvbXBsZXhpdHkgUGFyYW1ldGVyKioNCg0KYGBge3J9DQpzZXQuc2VlZCgxKQ0KDQpiY190cmVlX2N2X2NwIDwtIHRyYWluKGRpYWdub3NpcyB+IC4sIHdiYywgbWV0aG9kPSJycGFydCIsIA0KICAgICAgICAgICAgICAgICAgICAgICB0ckNvbnRyb2wgPSB0cmFpbkNvbnRyb2wobWV0aG9kPSJjdiIsIG51bWJlcj0xMCksDQogICAgICAgICAgICAgICAgICAgICAgIHR1bmVHcmlkID0gZXhwYW5kLmdyaWQoY3A9c2VxKDAsIDAuMSwgMC4wMDEpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgY29udHJvbCA9IHJwYXJ0LmNvbnRyb2wobWluc3BsaXQgPSA0LCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWluYnVja2V0ID0gIDIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXhkZXB0aCA9IDMwKSkNCg0KYmVzdF9peCA9IHdoaWNoLm1heChiY190cmVlX2N2X2NwJHJlc3VsdHMkQWNjdXJhY3kpDQpiY190cmVlX2N2X2NwJHJlc3VsdHNbYmVzdF9peCwgXQ0KDQpgYGANCg0KYGBge3J9DQpwbG90KGJjX3RyZWVfY3ZfY3AsIHBjaD0iIikNCmBgYA0KDQoNCiMjIyAqKkV4YW1wbGUgMjogSXJpcyBEYXRhc2V0KioNCg0KYGBge3J9DQppcmlzIDwtIHJlYWQudGFibGUoJ2RhdGEvaXJpcy50eHQnLCBzZXA9J1x0JywgaGVhZGVyPVRSVUUpDQpzdW1tYXJ5KGlyaXMpDQpgYGANCg0KYGBge3IsIGZpZy5oZWlnaHQ9NCwgZmlnLndpZHRoPTh9DQpwMSA8LSBnZ3Bsb3QoaXJpcywgYWVzKHg9c2VwYWxfbGVuZ3RoLCB5PXNlcGFsX3dpZHRoLCBjb2w9c3BlY2llcykpICsNCiAgZ2VvbV9wb2ludChhbHBoYT0wLjgpDQoNCnAyIDwtIGdncGxvdChpcmlzLCBhZXMoeD1wZXRhbF9sZW5ndGgsIHk9cGV0YWxfd2lkdGgsIGNvbD1zcGVjaWVzKSkgKw0KICBnZW9tX3BvaW50KGFscGhhPTAuOCkNCg0KZ3JpZC5hcnJhbmdlKHAxLCBwMiwgbmNvbD0yKQ0KYGBgDQoNCiMjIyMgKipDcm9zcy1WYWxpZGF0aW9uIGZvciBUdW5pbmcgTWF4aW11bSBEZXB0aCoqDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoMSkNCg0KaXJpc190cmVlX2N2IDwtIHRyYWluKHNwZWNpZXMgfiAuLCBpcmlzLCBtZXRob2Q9InJwYXJ0MiIsIA0KICAgICAgICAgICAgICAgICAgICAgIHRyQ29udHJvbCA9IHRyYWluQ29udHJvbChtZXRob2Q9ImN2IiwgbnVtYmVyPTEwKSwNCiAgICAgICAgICAgICAgICAgICAgICB0dW5lR3JpZCA9IGV4cGFuZC5ncmlkKG1heGRlcHRoPTE6MjApLA0KICAgICAgICAgICAgICAgICAgICAgIGNvbnRyb2wgPSBycGFydC5jb250cm9sKG1pbnNwbGl0ID0gMSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWluYnVja2V0ID0gIDEsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNwID0gMCkpDQoNCmJlc3RfaXggPSB3aGljaC5tYXgoaXJpc190cmVlX2N2JHJlc3VsdHMkQWNjdXJhY3kpDQppcmlzX3RyZWVfY3YkcmVzdWx0c1tiZXN0X2l4LCBdDQpgYGANCg0KDQpgYGB7cn0NCnBsb3QoaXJpc190cmVlX2N2LCBwY2g9IiIpDQpgYGANCg0KYGBge3IsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTR9DQpycGFydC5wbG90KGlyaXNfdHJlZV9jdiRmaW5hbE1vZGVsLCBleHRyYT0xLCBjZXg9MSkNCmBgYA0KDQojIyMgKipFeGFtcGxlIDM6IFdpbmUgUXVhbGl0eSBEYXRhc2V0KioNCg0KYGBge3J9DQp3aW5lIDwtIHJlYWQudGFibGUoImRhdGEvd2luZXF1YWxpdHktd2hpdGUuY3N2Iiwgc2VwPSIsIiwgaGVhZGVyPVRSVUUpDQoNCndpbmUkZ3JhZGUgPC0gaWZlbHNlKHdpbmUkcXVhbGl0eSA8IDUsICJMIiwgDQogICAgICAgICAgICAgICAgICAgICBpZmVsc2Uod2luZSRxdWFsaXR5IDwgNywgIk0iLCAiSCIpKQ0KDQp3aW5lJGdyYWRlIDwtIGZhY3Rvcih3aW5lJGdyYWRlLCBsZXZlbHM9YygiTCIsICJNIiwgIkgiKSkNCndpbmUkcXVhbGl0eSA8LSBOVUxMDQoNCnN1bW1hcnkod2luZSkNCmBgYA0KDQoNCiMjIyMgKipDcm9zcy1WYWxpZGF0aW9uIGZvciBUdW5pbmcgTWF4aW11bSBEZXB0aCoqDQoNCg0KYGBge3J9DQpzZXQuc2VlZCgxKQ0KDQp3aW5lX3RyZWVfY3ZfbWQgPC0gdHJhaW4oZ3JhZGUgfiAuLCB3aW5lLCBtZXRob2Q9InJwYXJ0MiIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgIHRyQ29udHJvbCA9IHRyYWluQ29udHJvbChtZXRob2Q9ImN2IiwgbnVtYmVyPTEwKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICB0dW5lR3JpZCA9IGV4cGFuZC5ncmlkKG1heGRlcHRoPXNlcSg1MCwgMzAwLCBieT01KSksDQogICAgICAgICAgICAgICAgICAgICAgICAgY29udHJvbCA9IHJwYXJ0LmNvbnRyb2wobWluc3BsaXQgPSAxNiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWluYnVja2V0ID0gIDEsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNwID0gMCkpDQoNCmJlc3RfaXggPSB3aGljaC5tYXgod2luZV90cmVlX2N2X21kJHJlc3VsdHMkQWNjdXJhY3kpDQp3aW5lX3RyZWVfY3ZfbWQkcmVzdWx0c1tiZXN0X2l4LCBdDQpgYGANCg0KYGBge3J9DQpwbG90KHdpbmVfdHJlZV9jdl9tZCwgcGNoPSIiKQ0KYGBgDQoNCiMjIyMgKipDcm9zcy1WYWxpZGF0aW9uIGZvciBUdW5pbmcgQ29tcGxleGl0eSBQYXJhbWV0ZXIqKg0KDQpgYGB7cn0NCnNldC5zZWVkKDEpDQoNCndpbmVfdHJlZV9jdl9jcCA8LSB0cmFpbihncmFkZSB+IC4sIHdpbmUsIG1ldGhvZD0icnBhcnQiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICB0ckNvbnRyb2wgPSB0cmFpbkNvbnRyb2wobWV0aG9kPSJjdiIsIG51bWJlcj0xMCksDQogICAgICAgICAgICAgICAgICAgICAgICAgdHVuZUdyaWQgPSBleHBhbmQuZ3JpZChjcD1zZXEoMCwgMC4wNSwgMC4wMDEpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICBjb250cm9sID0gcnBhcnQuY29udHJvbChtaW5zcGxpdCA9IDMyLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtaW5idWNrZXQgPSAgNCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWF4ZGVwdGggPSAxMCkpDQoNCmJlc3RfaXggPSB3aGljaC5tYXgod2luZV90cmVlX2N2X2NwJHJlc3VsdHMkQWNjdXJhY3kpDQp3aW5lX3RyZWVfY3ZfY3AkcmVzdWx0c1tiZXN0X2l4LCBdDQpgYGANCg0KYGBge3J9DQpwbG90KHdpbmVfdHJlZV9jdl9jcCwgcGNoPSIiKQ0KYGBgDQoNCg0KYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD0xNX0NCnJwYXJ0LnBsb3Qod2luZV90cmVlX2N2X2NwJGZpbmFsTW9kZWwsIGV4dHJhPTEsIGNleCA9IDAuOCkNCmBgYA0KDQoNCiMjIyAqKkV4YW1wbGUgNDogRGlhbW9uZHMgRGF0YXNldCoqDQoNCmBgYHtyfQ0KZGlhbW9uZHMgPC0gcmVhZC50YWJsZSgiZGF0YS9kaWFtb25kcy50eHQiLCBzZXA9Ilx0IiwgaGVhZGVyPVRSVUUpDQpkaWFtb25kcyA8LSBkaWFtb25kc1ssYygxOjQsNyldDQpzdW1tYXJ5KGRpYW1vbmRzKQ0KYGBgDQoNCiMjIyMgKipDcmVhdGluZyBhIFNpbmdsZSBUcmVlIE1vZGVsKioNCg0KYGBge3IsIGZpZy53aWR0aD0xNCwgZmlnLmhlaWdodD04fQ0Kc2V0LnNlZWQoMSkNCg0KZG1kX3RyZWVfbW9kIDwtIHJwYXJ0KHByaWNlIH4gLiwgZGlhbW9uZHMsIG1ldGhvZD0iYW5vdmEiLCANCiAgICAgICAgICAgICAgICAgICAgICBjb250cm9sID0gcnBhcnQuY29udHJvbChtaW5zcGxpdCA9IDMyLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtaW5idWNrZXQgPSAgMTYsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNwID0gMC4wMDEsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1heGRlcHRoID0gNikpDQoNCnJwYXJ0LnBsb3QoZG1kX3RyZWVfbW9kLCBleHRyYT0xLCBjZXggPSAwLjgpDQpgYGANCg0KYGBge3J9DQpwcmVkIDwtIHByZWRpY3QoZG1kX3RyZWVfbW9kLCBkaWFtb25kcykNClNTRSA8LSBzdW0oKGRpYW1vbmRzJHByaWNlIC0gcHJlZCleMikNClNTVCA8LSBzdW0oKGRpYW1vbmRzJHByaWNlIC0gbWVhbihkaWFtb25kcyRwcmljZSkpXjIpDQpyMiA8LSAxIC0gU1NFIC8gU1NUDQpyMg0KYGBgDQoNCg0KIyMjIyAqKkNyb3NzLVZhbGlkYXRpb24gZm9yIFR1bmluZyBNYXhpbXVtIERlcHRoKioNCg0KYGBge3J9DQpzZXQuc2VlZCgxKQ0KDQpkbWRfdHJlZV9jdl9tZCA8LSB0cmFpbihwcmljZSB+IC4sIGRpYW1vbmRzLCBtZXRob2Q9InJwYXJ0MiIsIG1ldHJpYz0iUnNxdWFyZWQiLCANCiAgICAgICAgICAgICAgICAgICAgICAgIHRyQ29udHJvbCA9IHRyYWluQ29udHJvbChtZXRob2Q9ImN2IiwgbnVtYmVyPTEwKSwNCiAgICAgICAgICAgICAgICAgICAgICAgIHR1bmVHcmlkID0gZXhwYW5kLmdyaWQobWF4ZGVwdGg9c2VxKDUwLCA2MDAsIGJ5PTUwKSksDQogICAgICAgICAgICAgICAgICAgICAgICBjb250cm9sID0gcnBhcnQuY29udHJvbChtaW5zcGxpdCA9IDMyLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pbmJ1Y2tldCA9ICAxNiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY3AgPSAwLjAwMDAxKSkNCg0KYmVzdF9peCA9IHdoaWNoLm1heChkbWRfdHJlZV9jdl9tZCRyZXN1bHRzJFJzcXVhcmVkKQ0KZG1kX3RyZWVfY3ZfbWQkcmVzdWx0c1tiZXN0X2l4LCBdDQpgYGANCg0KYGBge3J9DQpwbG90KGRtZF90cmVlX2N2X21kLCBwY2g9IiIpDQpgYGANCg==