Question 1
heart <- read.csv("HW7data.csv")
a.
heart$target <- factor(heart$target, levels = c("Yes", "No")) #I changed the levels because it hurts my brain when no is the positive class.
heart$sex <- as.character(heart$sex)
heart$cp <- as.character(heart$cp)
heart$fbs <- as.character(heart$fbs)
heart$restecg <- as.character(heart$restecg)
heart$exang <- as.character(heart$exang)
heart$slope <- as.character(heart$slope)
heart$thal <- as.character(heart$thal)
imbalance <- heart %>% group_by(target) %>% summarise(n = n(), p = n / 303)
imbalance
# A tibble: 2 × 3
target n p
<fct> <int> <dbl>
1 Yes 165 0.545
2 No 138 0.455
There are more yes than no’s, but the imbalance is not severe.
b.
set.seed(1)
fitControl <- trainControl(method = "cv", number = 10, classProbs = TRUE, savePredictions = "all", summaryFunction = twoClassSummary)
model.logit <- train(data = heart,
target ~ .,
method = "glm",
family = "binomial",
trControl = fitControl)
model.logit
Generalized Linear Model
303 samples
13 predictor
2 classes: 'Yes', 'No'
No pre-processing
Resampling: Cross-Validated (10 fold)
Summary of sample sizes: 274, 272, 273, 272, 273, 272, ...
Resampling results:
ROC Sens Spec
0.8888474 0.8797794 0.7769231
Test accuracy of 83.31%.
c.
set.seed(1)
freq_class <- table(heart$target)
weights_vector <- ifelse(heart$target == "No", nrow(heart)/(length(freq_class)*freq_class[2]), nrow(heart)/(length(freq_class)*freq_class[1]))
model.logit2 <- train(data = heart,
target ~ .,
method = "glm",
family = "quasibinomial",
weights = weights_vector,
trControl = fitControl)
model.logit2
Generalized Linear Model
303 samples
13 predictor
2 classes: 'Yes', 'No'
No pre-processing
Resampling: Cross-Validated (10 fold)
Summary of sample sizes: 274, 272, 273, 272, 273, 272, ...
Resampling results:
ROC Sens Spec
0.8892069 0.8492647 0.7917582
The new test accuracy is 82.33%. We did worse because there was no
severe class imbalance.
d.
The no class would be the positive class if I hadn’t changed the
levels. I changed the levels so the yes class is the positive class
since your testing if a person has heart disease. Sensitivity measures
the proportion of actual positives (person has heart disease) that the
model correctly identified. So out of the people with heart disease, how
many times did the model correctly label them as ‘Yes’. Specificity
measures the proportion of actual negatives. So out of the people who
didn’t have heart disease, how many times did the model correctly label
them as ‘No’. The sensitivity and specificity for regular log reg were
0.8797794 and 0.7769231. The sensitivity and specificity for weighted
log reg were 0.8492647 and 0.7917582. So the weighted log reg did better
with the people who didn’t have heart disease, which makes sense since
these people were assigned larger weights.
e.
thresholder(model.logit, threshold = 0.50)
parameter prob_threshold Sensitivity Specificity Pos Pred Value
1 none 0.5 0.8797794 0.7769231 0.8287918
Neg Pred Value Precision Recall F1 Prevalence Detection Rate
1 0.8513736 0.8287918 0.8797794 0.8516563 0.5445384 0.4789247
Detection Prevalence Balanced Accuracy Accuracy Kappa J
1 0.5802373 0.8283512 0.8330738 0.6608442 0.6567025
Dist
1 0.2629323
thresholder(model.logit, threshold = 0.25)
parameter prob_threshold Sensitivity Specificity Pos Pred Value
1 none 0.25 0.9272059 0.6434066 0.7649135
Neg Pred Value Precision Recall F1 Prevalence Detection Rate
1 0.8821503 0.7649135 0.9272059 0.836072 0.5445384 0.5049537
Detection Prevalence Balanced Accuracy Accuracy Kappa J
1 0.666548 0.7853062 0.7988209 0.5825124 0.5706125
Dist
1 0.3675353
thresholder(model.logit, threshold = 0.75)
parameter prob_threshold Sensitivity Specificity Pos Pred Value
1 none 0.75 0.6797794 0.9065934 0.8990113
Neg Pred Value Precision Recall F1 Prevalence Detection Rate
1 0.7072092 0.8990113 0.6797794 0.7712848 0.5445384 0.370178
Detection Prevalence Balanced Accuracy Accuracy Kappa J
1 0.412551 0.7931864 0.7832666 0.5735424 0.5863728
Dist
1 0.3413992
When we decrease the threshold (0.25), we make it easier for the
model to label someone as a ‘Yes”. As a result, the sensitivity goes up
and the specificity goes down. When we increase the threshold (0.75), we
make it harder for the model to label someone as a ’Yes’. As a result,
the sensitivity goes down and the specificity goes up.
f.
cpGrid <- expand.grid(cp = 0) # No pruning
set.seed(1)
fitControl <- trainControl(method = "cv", number = 10)
model.tree <- train(target ~ .,
data = heart,
method = "rpart",
trControl = fitControl,
tuneGrid = cpGrid)
par(xpd = NA)
plot(model.tree$finalModel)
text(model.tree$finalModel)

The first decision is when thal is equal to 2. Go to the left if it
equals 2, go to the right otherwise.
g.
p <- prop.table(table(heart$target))
(0.5445545)*(0.4554455) + (0.5445545)*(0.4554455)
[1] 0.4960298
The Gini index with no decisions is 0.4960298.
h.
heart %>%
filter(thal == "2") %>%
group_by(target) %>%
summarize(n = n())
# A tibble: 2 × 2
target n
<fct> <int>
1 Yes 130
2 No 36
heart %>%
filter(thal != "2") %>%
group_by(target) %>%
summarize(n = n())
# A tibble: 2 × 2
target n
<fct> <int>
1 Yes 35
2 No 102
gini_thal2 <- (130/166)*(1 - 130/166) + (36/166)*(1 - 36/166)
gini_not_thal2 <- (35/137)*(1 - 35/137) + (102/137)*(1 - 102/137)
split_gini <- (166/303)*gini_thal2 + (137/303)*gini_not_thal2
split_gini
[1] 0.3580935
They are now more pure.
i.
heart %>%
filter(thal == "3") %>%
group_by(target) %>%
summarize(n = n())
# A tibble: 2 × 2
target n
<fct> <int>
1 Yes 28
2 No 89
heart %>%
filter(thal != "3") %>%
group_by(target) %>%
summarize(n = n())
# A tibble: 2 × 2
target n
<fct> <int>
1 Yes 137
2 No 49
gini_thal3 <- (137/186)*(1 - 137/186) + (49/186)*(1 - 49/186)
gini_not_thal3 <- (28/117)*(1 - 28/117) + (89/117)*(1 - 89/117)
split_gini <- (186/303)*gini_thal3 + (117/303)*gini_not_thal3
split_gini
[1] 0.3788155
Slightly less pure now.
j.
heart %>%
filter(cp == "2") %>%
group_by(target) %>%
summarize(n = n())
# A tibble: 2 × 2
target n
<fct> <int>
1 Yes 69
2 No 18
heart %>%
filter(cp != "2") %>%
group_by(target) %>%
summarize(n = n())
# A tibble: 2 × 2
target n
<fct> <int>
1 Yes 96
2 No 120
gini_cp2 <- (69/87)*(1 - 69/87) + (18/87)*(1 - 18/87)
gini_not_cp2 <- (96/216)*(1 - 96/216) + (120/216)*(1 - 120/216)
split_gini <- (87/303)*gini_cp2 + (216/303)*gini_not_cp2
split_gini
[1] 0.4462653
Less pure then the split on thal2.
k.
set.seed(1)
model.tree.prune <- train(target ~ .,
data = heart,
method = "rpart",
trControl = fitControl,
tuneLength = 10) # Let function pick 10 reasonable tuning parameters to prune the tree
model.tree.prune
CART
303 samples
13 predictor
2 classes: 'Yes', 'No'
No pre-processing
Resampling: Cross-Validated (10 fold)
Summary of sample sizes: 274, 272, 273, 272, 273, 272, ...
Resampling results across tuning parameters:
cp Accuracy Kappa
0.00000000 0.7338821 0.46342416
0.05394525 0.7534594 0.50171008
0.10789050 0.7667927 0.52861591
0.16183575 0.7667927 0.52861591
0.21578100 0.7667927 0.52861591
0.26972625 0.7667927 0.52861591
0.32367150 0.7667927 0.52861591
0.37761675 0.7667927 0.52861591
0.43156200 0.7667927 0.52861591
0.48550725 0.5800222 0.09607575
Accuracy was used to select the optimal model using the largest value.
The final value used for the model was cp = 0.431562.
The accuracy when not pruning was 0.7338821, the best accuracy with
pruning was 0.7667927, so we did better with pruning. This makes sense
since pruning limits the number of splits a tree can do, keeping it from
overfitting and removing unnecessary splits.
l.
par(xpd = NA)
plot(model.tree.prune$finalModel)
text(model.tree.prune$finalModel)

heart %>%
filter(thal == "2") %>%
group_by(target) %>%
summarize(n = n())
# A tibble: 2 × 2
target n
<fct> <int>
1 Yes 130
2 No 36
heart %>%
filter(thal != "2") %>%
group_by(target) %>%
summarize(n = n())
# A tibble: 2 × 2
target n
<fct> <int>
1 Yes 35
2 No 102
232/303
[1] 0.7656766
Everything checks out.
LS0tCnRpdGxlOiAiSG9tZXdvcmsgNyIKYXV0aG9yOiAiQ2hhcmxpZSBNb3JnYW4iCmRhdGU6ICIgRHVlOiAwNS8wMy8yNiIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6IAogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogNAogICAgdG9jX2Zsb2F0OiB5ZXMKICAgIG51bWJlcl9zZWN0aW9uczogbm8KICAgIHRvY19jb2xsYXBzZWQ6IHllcwogICAgY29kZV9mb2xkaW5nOiBoaWRlCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMKICAgIHNtb290aF9zY3JvbGw6IHllcwogICAgdGhlbWU6IGx1bWVuCiAgcGRmX2RvY3VtZW50OiAKICAgIHRvYzogeWVzCiAgICB0b2NfZGVwdGg6IDQKICAgIGZpZ19jYXB0aW9uOiB5ZXMKICAgIG51bWJlcl9zZWN0aW9uczogeWVzCiAgICBmaWdfd2lkdGg6IDMKICAgIGZpZ19oZWlnaHQ6IDMKICB3b3JkX2RvY3VtZW50OiAKICAgIHRvYzogeWVzCiAgICB0b2NfZGVwdGg6IDQKICAgIGZpZ19jYXB0aW9uOiB5ZXMKICAgIGtlZXBfbWQ6IHllcwplZGl0b3Jfb3B0aW9uczogCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQotLS0KCmBgYHtjc3MsIGVjaG8gPSBGQUxTRX0KI1RPQzo6YmVmb3JlIHsKICBjb250ZW50OiAiVGFibGUgb2YgQ29udGVudHMiOwogIGZvbnQtd2VpZ2h0OiBib2xkOwogIGZvbnQtc2l6ZTogMS4yZW07CiAgZGlzcGxheTogYmxvY2s7CiAgY29sb3I6IG5hdnk7CiAgbWFyZ2luLWJvdHRvbTogMTBweDsKfQoKCmRpdiNUT0MgbGkgeyAgICAgLyogdGFibGUgb2YgY29udGVudCAgKi8KICAgIGxpc3Qtc3R5bGU6dXBwZXItcm9tYW47CiAgICBiYWNrZ3JvdW5kLWltYWdlOm5vbmU7CiAgICBiYWNrZ3JvdW5kLXJlcGVhdDpub25lOwogICAgYmFja2dyb3VuZC1wb3NpdGlvbjowOwp9CgpoMS50aXRsZSB7ICAgIC8qIGxldmVsIDEgaGVhZGVyIG9mIHRpdGxlICAqLwogIGZvbnQtc2l6ZTogMjJweDsKICBmb250LXdlaWdodDogYm9sZDsKICBjb2xvcjogRGFya1JlZDsKICB0ZXh0LWFsaWduOiBjZW50ZXI7CiAgZm9udC1mYW1pbHk6ICJHaWxsIFNhbnMiLCBzYW5zLXNlcmlmOwp9CgpoNC5hdXRob3IgeyAvKiBIZWFkZXIgNCAtIGFuZCB0aGUgYXV0aG9yIGFuZCBkYXRhIGhlYWRlcnMgdXNlIHRoaXMgdG9vICAqLwogIGZvbnQtc2l6ZTogMTVweDsKICBmb250LXdlaWdodDogYm9sZDsKICBmb250LWZhbWlseTogc3lzdGVtLXVpOwogIGNvbG9yOiBuYXZ5OwogIHRleHQtYWxpZ246IGNlbnRlcjsKfQoKaDQuZGF0ZSB7IC8qIEhlYWRlciA0IC0gYW5kIHRoZSBhdXRob3IgYW5kIGRhdGEgaGVhZGVycyB1c2UgdGhpcyB0b28gICovCiAgZm9udC1zaXplOiAxOHB4OwogIGZvbnQtd2VpZ2h0OiBib2xkOwogIGZvbnQtZmFtaWx5OiAiR2lsbCBTYW5zIiwgc2Fucy1zZXJpZjsKICBjb2xvcjogRGFya0JsdWU7CiAgdGV4dC1hbGlnbjogY2VudGVyOwp9CgpoMSB7IC8qIEhlYWRlciAxIC0gYW5kIHRoZSBhdXRob3IgYW5kIGRhdGEgaGVhZGVycyB1c2UgdGhpcyB0b28gICovCiAgICBmb250LXNpemU6IDIwcHg7CiAgICBmb250LXdlaWdodDogYm9sZDsKICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOwogICAgY29sb3I6IGRhcmtyZWQ7CiAgICB0ZXh0LWFsaWduOiBjZW50ZXI7Cn0KCmgyIHsgLyogSGVhZGVyIDIgLSBhbmQgdGhlIGF1dGhvciBhbmQgZGF0YSBoZWFkZXJzIHVzZSB0aGlzIHRvbyAgKi8KICAgIGZvbnQtc2l6ZTogMThweDsKICAgIGZvbnQtd2VpZ2h0OiBib2xkOwogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7CiAgICBjb2xvcjogbmF2eTsKICAgIHRleHQtYWxpZ246IGxlZnQ7Cn0KCmgzIHsgLyogSGVhZGVyIDMgLSBhbmQgdGhlIGF1dGhvciBhbmQgZGF0YSBoZWFkZXJzIHVzZSB0aGlzIHRvbyAgKi8KICAgIGZvbnQtc2l6ZTogMTZweDsKICAgIGZvbnQtd2VpZ2h0OiBib2xkOwogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7CiAgICBjb2xvcjogbmF2eTsKICAgIHRleHQtYWxpZ246IGxlZnQ7Cn0KCmg0IHsgLyogSGVhZGVyIDQgLSBhbmQgdGhlIGF1dGhvciBhbmQgZGF0YSBoZWFkZXJzIHVzZSB0aGlzIHRvbyAgKi8KICAgIGZvbnQtc2l6ZTogMTRweDsKICBmb250LXdlaWdodDogYm9sZDsKICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOwogICAgY29sb3I6IGRhcmtyZWQ7CiAgICB0ZXh0LWFsaWduOiBsZWZ0Owp9CgovKiBBZGQgZG90cyBhZnRlciBudW1iZXJlZCBoZWFkZXJzICovCi5oZWFkZXItc2VjdGlvbi1udW1iZXI6OmFmdGVyIHsKICBjb250ZW50OiAiLiI7Cgpib2R5IHsgYmFja2dyb3VuZC1jb2xvcjp3aGl0ZTsgfQoKLmhpZ2hsaWdodG1lIHsgYmFja2dyb3VuZC1jb2xvcjp5ZWxsb3c7IH0KCnAgeyBiYWNrZ3JvdW5kLWNvbG9yOndoaXRlOyB9Cgp9CmBgYAoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CiMgY29kZSBjaHVuayBzcGVjaWZpZXMgd2hldGhlciB0aGUgUiBjb2RlLCB3YXJuaW5ncywgYW5kIG91dHB1dCAKIyB3aWxsIGJlIGluY2x1ZGVkIGluIHRoZSBvdXRwdXQgZmlsZXMuCmlmICghcmVxdWlyZSgia25pdHIiKSkgewogICBpbnN0YWxsLnBhY2thZ2VzKCJrbml0ciIpCiAgIGxpYnJhcnkoa25pdHIpCn0KaWYgKCFyZXF1aXJlKCJmYWN0b2V4dHJhIikpIHsKICBpbnN0YWxsLnBhY2thZ2VzKCJmYWN0b2V4dHJhIikKICBsaWJyYXJ5KGZhY3RvZXh0cmEpCn0KCmlmICghcmVxdWlyZSgidGlkeXZlcnNlIikpIHsKICBpbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dmVyc2UiKQogIGxpYnJhcnkodGlkeXZlcnNlKQp9CgppZiAoIXJlcXVpcmUoIkZhY3RvTWluZVIiKSkgewogIGluc3RhbGwucGFja2FnZXMoIkZhY3RvTWluZVIiKQogIGxpYnJhcnkoRmFjdG9NaW5lUikKfQoKaWYgKCFyZXF1aXJlKCJjb3JycGxvdCIpKSB7CiAgaW5zdGFsbC5wYWNrYWdlcygiY29ycnBsb3QiKQogIGxpYnJhcnkoY29ycnBsb3QpCn0KCmlmICghcmVxdWlyZSgibWljZSIpKSB7CiAgaW5zdGFsbC5wYWNrYWdlcygibWljZSIpCiAgbGlicmFyeShtaWNlKQp9CgppZiAoIXJlcXVpcmUoImthYmxlRXh0cmEiKSkgewogIGluc3RhbGwucGFja2FnZXMoImthYmxlRXh0cmEiKQogIGxpYnJhcnkoa2FibGVFeHRyYSkKfQoKaWYgKCFyZXF1aXJlKCJjbHVzdGVyIikpIHsKICBpbnN0YWxsLnBhY2thZ2VzKCJjbHVzdGVyIikKICBsaWJyYXJ5KGNsdXN0ZXIpCn0KCmlmICghcmVxdWlyZSgibWNsdXN0IikpIHsKICBpbnN0YWxsLnBhY2thZ2VzKCJtY2x1c3QiKQogIGxpYnJhcnkobWNsdXN0KQp9CgppZiAoIXJlcXVpcmUoImRic2NhbiIpKSB7CiAgaW5zdGFsbC5wYWNrYWdlcygiZGJzY2FuIikKICBsaWJyYXJ5KGRic2NhbikKfQoKaWYgKCFyZXF1aXJlKCJjYXJldCIpKSB7CiAgaW5zdGFsbC5wYWNrYWdlcygiY2FyZXQiKQogIGxpYnJhcnkoY2FyZXQpCn0KIyMjIwprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsICAgICAgICMgaW5jbHVkZSBjb2RlIGNodW5rIGluIHRoZSBvdXRwdXQgZmlsZQogICAgICAgICAgICAgICAgICAgICAgd2FybmluZyA9IEZBTFNFLCAgICMgc29tZXRpbWVzLCB5b3UgY29kZSBtYXkgcHJvZHVjZSB3YXJuaW5nIG1lc3NhZ2VzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgeW91IGNhbiBjaG9vc2UgdG8gaW5jbHVkZSB0aGUgd2FybmluZyBtZXNzYWdlcyBpbgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgdGhlIG91dHB1dCBmaWxlLiAKICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdHMgPSBUUlVFLCAgICAjIHlvdSBjYW4gYWxzbyBkZWNpZGUgd2hldGhlciB0byBpbmNsdWRlIHRoZSBvdXRwdXQKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGluIHRoZSBvdXRwdXQgZmlsZS4KICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2UgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgIGNvbW1lbnQgPSBOQQogICAgICAgICAgICAgICAgICAgICAgKSAgCgpgYGAKCiMgUXVlc3Rpb24gMQoKYGBge3J9CmhlYXJ0IDwtIHJlYWQuY3N2KCJIVzdkYXRhLmNzdiIpCmBgYAoKIyMgYS4KYGBge3J9CmhlYXJ0JHRhcmdldCA8LSBmYWN0b3IoaGVhcnQkdGFyZ2V0LCBsZXZlbHMgPSBjKCJZZXMiLCAiTm8iKSkgI0kgY2hhbmdlZCB0aGUgbGV2ZWxzIGJlY2F1c2UgaXQgaHVydHMgbXkgYnJhaW4gd2hlbiBubyBpcyB0aGUgcG9zaXRpdmUgY2xhc3MuCgpoZWFydCRzZXggPC0gYXMuY2hhcmFjdGVyKGhlYXJ0JHNleCkKaGVhcnQkY3AgPC0gYXMuY2hhcmFjdGVyKGhlYXJ0JGNwKQpoZWFydCRmYnMgPC0gYXMuY2hhcmFjdGVyKGhlYXJ0JGZicykKaGVhcnQkcmVzdGVjZyA8LSBhcy5jaGFyYWN0ZXIoaGVhcnQkcmVzdGVjZykKaGVhcnQkZXhhbmcgPC0gYXMuY2hhcmFjdGVyKGhlYXJ0JGV4YW5nKQpoZWFydCRzbG9wZSA8LSBhcy5jaGFyYWN0ZXIoaGVhcnQkc2xvcGUpCmhlYXJ0JHRoYWwgPC0gYXMuY2hhcmFjdGVyKGhlYXJ0JHRoYWwpCgppbWJhbGFuY2UgPC0gaGVhcnQgJT4lIGdyb3VwX2J5KHRhcmdldCkgJT4lIHN1bW1hcmlzZShuID0gbigpLCBwID0gbiAvIDMwMykKaW1iYWxhbmNlCmBgYApUaGVyZSBhcmUgbW9yZSB5ZXMgdGhhbiBubydzLCBidXQgdGhlIGltYmFsYW5jZSBpcyBub3Qgc2V2ZXJlLgoKIyMgYi4KYGBge3J9CnNldC5zZWVkKDEpCgpmaXRDb250cm9sIDwtIHRyYWluQ29udHJvbChtZXRob2QgPSAiY3YiLCBudW1iZXIgPSAxMCwgY2xhc3NQcm9icyA9IFRSVUUsIHNhdmVQcmVkaWN0aW9ucyA9ICJhbGwiLCBzdW1tYXJ5RnVuY3Rpb24gPSB0d29DbGFzc1N1bW1hcnkpCgptb2RlbC5sb2dpdCA8LSB0cmFpbihkYXRhID0gaGVhcnQsCiAgICAgICAgICAgICAgICAgICAgIHRhcmdldCB+IC4sCiAgICAgICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJnbG0iLAogICAgICAgICAgICAgICAgICAgICBmYW1pbHkgPSAiYmlub21pYWwiLAogICAgICAgICAgICAgICAgICAgICB0ckNvbnRyb2wgPSBmaXRDb250cm9sKQptb2RlbC5sb2dpdApgYGAKVGVzdCBhY2N1cmFjeSBvZiA4My4zMSUuCgojIyBjLgpgYGB7cn0Kc2V0LnNlZWQoMSkKCmZyZXFfY2xhc3MgPC0gdGFibGUoaGVhcnQkdGFyZ2V0KQoKd2VpZ2h0c192ZWN0b3IgPC0gaWZlbHNlKGhlYXJ0JHRhcmdldCA9PSAiTm8iLCBucm93KGhlYXJ0KS8obGVuZ3RoKGZyZXFfY2xhc3MpKmZyZXFfY2xhc3NbMl0pLCBucm93KGhlYXJ0KS8obGVuZ3RoKGZyZXFfY2xhc3MpKmZyZXFfY2xhc3NbMV0pKQoKbW9kZWwubG9naXQyIDwtIHRyYWluKGRhdGEgPSBoZWFydCwKICAgICAgICAgICAgICAgICAgICAgdGFyZ2V0IH4gLiwKICAgICAgICAgICAgICAgICAgICAgbWV0aG9kID0gImdsbSIsCiAgICAgICAgICAgICAgICAgICAgIGZhbWlseSA9ICJxdWFzaWJpbm9taWFsIiwKICAgICAgICAgICAgICAgICAgICAgd2VpZ2h0cyA9IHdlaWdodHNfdmVjdG9yLAogICAgICAgICAgICAgICAgICAgICB0ckNvbnRyb2wgPSBmaXRDb250cm9sKQptb2RlbC5sb2dpdDIKYGBgClRoZSBuZXcgdGVzdCBhY2N1cmFjeSBpcyA4Mi4zMyUuIFdlIGRpZCB3b3JzZSBiZWNhdXNlIHRoZXJlIHdhcyBubyBzZXZlcmUgY2xhc3MgaW1iYWxhbmNlLgoKIyMgZC4KVGhlIG5vIGNsYXNzIHdvdWxkIGJlIHRoZSBwb3NpdGl2ZSBjbGFzcyBpZiBJIGhhZG4ndCBjaGFuZ2VkIHRoZSBsZXZlbHMuIEkgY2hhbmdlZCB0aGUgbGV2ZWxzIHNvIHRoZSB5ZXMgY2xhc3MgaXMgdGhlIHBvc2l0aXZlIGNsYXNzIHNpbmNlIHlvdXIgdGVzdGluZyBpZiBhIHBlcnNvbiBoYXMgaGVhcnQgZGlzZWFzZS4gU2Vuc2l0aXZpdHkgbWVhc3VyZXMgdGhlIHByb3BvcnRpb24gb2YgYWN0dWFsIHBvc2l0aXZlcyAocGVyc29uIGhhcyBoZWFydCBkaXNlYXNlKSB0aGF0IHRoZSBtb2RlbCBjb3JyZWN0bHkgaWRlbnRpZmllZC4gU28gb3V0IG9mIHRoZSBwZW9wbGUgd2l0aCBoZWFydCBkaXNlYXNlLCBob3cgbWFueSB0aW1lcyBkaWQgdGhlIG1vZGVsIGNvcnJlY3RseSBsYWJlbCB0aGVtIGFzICdZZXMnLiBTcGVjaWZpY2l0eSBtZWFzdXJlcyB0aGUgcHJvcG9ydGlvbiBvZiBhY3R1YWwgbmVnYXRpdmVzLiBTbyBvdXQgb2YgdGhlIHBlb3BsZSB3aG8gZGlkbid0IGhhdmUgaGVhcnQgZGlzZWFzZSwgaG93IG1hbnkgdGltZXMgZGlkIHRoZSBtb2RlbCBjb3JyZWN0bHkgbGFiZWwgdGhlbSBhcyAnTm8nLiBUaGUgc2Vuc2l0aXZpdHkgYW5kIHNwZWNpZmljaXR5IGZvciByZWd1bGFyIGxvZyByZWcgd2VyZSAwLjg3OTc3OTQgYW5kIDAuNzc2OTIzMS4gVGhlIHNlbnNpdGl2aXR5IGFuZCBzcGVjaWZpY2l0eSBmb3Igd2VpZ2h0ZWQgbG9nIHJlZyB3ZXJlICAwLjg0OTI2NDcgYW5kIDAuNzkxNzU4Mi4gU28gdGhlIHdlaWdodGVkIGxvZyByZWcgZGlkIGJldHRlciB3aXRoIHRoZSBwZW9wbGUgd2hvIGRpZG4ndCBoYXZlIGhlYXJ0IGRpc2Vhc2UsIHdoaWNoIG1ha2VzIHNlbnNlIHNpbmNlIHRoZXNlIHBlb3BsZSB3ZXJlIGFzc2lnbmVkIGxhcmdlciB3ZWlnaHRzLgoKIyMgZS4KYGBge3J9CnRocmVzaG9sZGVyKG1vZGVsLmxvZ2l0LCB0aHJlc2hvbGQgPSAwLjUwKSAKYGBgCmBgYHtyfQp0aHJlc2hvbGRlcihtb2RlbC5sb2dpdCwgdGhyZXNob2xkID0gMC4yNSkgCmBgYApgYGB7cn0KdGhyZXNob2xkZXIobW9kZWwubG9naXQsIHRocmVzaG9sZCA9IDAuNzUpIApgYGAKV2hlbiB3ZSBkZWNyZWFzZSB0aGUgdGhyZXNob2xkICgwLjI1KSwgd2UgbWFrZSBpdCBlYXNpZXIgZm9yIHRoZSBtb2RlbCB0byBsYWJlbCBzb21lb25lIGFzIGEgJ1llcyIuIEFzIGEgcmVzdWx0LCB0aGUgc2Vuc2l0aXZpdHkgZ29lcyB1cCBhbmQgdGhlIHNwZWNpZmljaXR5IGdvZXMgZG93bi4gV2hlbiB3ZSBpbmNyZWFzZSB0aGUgdGhyZXNob2xkICgwLjc1KSwgd2UgbWFrZSBpdCBoYXJkZXIgZm9yIHRoZSBtb2RlbCB0byBsYWJlbCBzb21lb25lIGFzIGEgJ1llcycuIEFzIGEgcmVzdWx0LCB0aGUgc2Vuc2l0aXZpdHkgZ29lcyBkb3duIGFuZCB0aGUgc3BlY2lmaWNpdHkgZ29lcyB1cC4gCgojIyBmLgpgYGB7cn0KY3BHcmlkIDwtIGV4cGFuZC5ncmlkKGNwID0gMCkgIyBObyBwcnVuaW5nCnNldC5zZWVkKDEpCmZpdENvbnRyb2wgPC0gdHJhaW5Db250cm9sKG1ldGhvZCA9ICJjdiIsIG51bWJlciA9IDEwKQoKbW9kZWwudHJlZSA8LSB0cmFpbih0YXJnZXQgfiAuLAogICAgICAgICAgICAgICAgICAgIGRhdGEgPSBoZWFydCwKICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAicnBhcnQiLAogICAgICAgICAgICAgICAgICAgIHRyQ29udHJvbCA9IGZpdENvbnRyb2wsCiAgICAgICAgICAgICAgICAgICAgdHVuZUdyaWQgPSBjcEdyaWQpCnBhcih4cGQgPSBOQSkKcGxvdChtb2RlbC50cmVlJGZpbmFsTW9kZWwpCnRleHQobW9kZWwudHJlZSRmaW5hbE1vZGVsKQpgYGAKClRoZSBmaXJzdCBkZWNpc2lvbiBpcyB3aGVuIHRoYWwgaXMgZXF1YWwgdG8gMi4gR28gdG8gdGhlIGxlZnQgaWYgaXQgZXF1YWxzIDIsIGdvIHRvIHRoZSByaWdodCBvdGhlcndpc2UuCgojIyBnLgpgYGB7cn0KcCA8LSBwcm9wLnRhYmxlKHRhYmxlKGhlYXJ0JHRhcmdldCkpCigwLjU0NDU1NDUpKigwLjQ1NTQ0NTUpICsgKDAuNTQ0NTU0NSkqKDAuNDU1NDQ1NSkKYGBgClRoZSBHaW5pIGluZGV4IHdpdGggbm8gZGVjaXNpb25zIGlzIDAuNDk2MDI5OC4KCiMjIGguCmBgYHtyfQpoZWFydCAlPiUKICBmaWx0ZXIodGhhbCA9PSAiMiIpICU+JQogIGdyb3VwX2J5KHRhcmdldCkgJT4lCiAgc3VtbWFyaXplKG4gPSBuKCkpCmBgYApgYGB7cn0KaGVhcnQgJT4lCiAgZmlsdGVyKHRoYWwgIT0gIjIiKSAlPiUKICBncm91cF9ieSh0YXJnZXQpICU+JQogIHN1bW1hcml6ZShuID0gbigpKQpgYGAKCmBgYHtyfQpnaW5pX3RoYWwyIDwtICgxMzAvMTY2KSooMSAtIDEzMC8xNjYpICsgKDM2LzE2NikqKDEgLSAzNi8xNjYpCmdpbmlfbm90X3RoYWwyIDwtICgzNS8xMzcpKigxIC0gMzUvMTM3KSArICgxMDIvMTM3KSooMSAtIDEwMi8xMzcpCnNwbGl0X2dpbmkgPC0gKDE2Ni8zMDMpKmdpbmlfdGhhbDIgKyAoMTM3LzMwMykqZ2luaV9ub3RfdGhhbDIKc3BsaXRfZ2luaQpgYGAKVGhleSBhcmUgbm93IG1vcmUgcHVyZS4KCiMjIGkuCmBgYHtyfQpoZWFydCAlPiUKICBmaWx0ZXIodGhhbCA9PSAiMyIpICU+JQogIGdyb3VwX2J5KHRhcmdldCkgJT4lCiAgc3VtbWFyaXplKG4gPSBuKCkpCmBgYApgYGB7cn0KaGVhcnQgJT4lCiAgZmlsdGVyKHRoYWwgIT0gIjMiKSAlPiUKICBncm91cF9ieSh0YXJnZXQpICU+JQogIHN1bW1hcml6ZShuID0gbigpKQpgYGAKCmBgYHtyfQpnaW5pX3RoYWwzIDwtICgxMzcvMTg2KSooMSAtIDEzNy8xODYpICsgKDQ5LzE4NikqKDEgLSA0OS8xODYpCmdpbmlfbm90X3RoYWwzIDwtICgyOC8xMTcpKigxIC0gMjgvMTE3KSArICg4OS8xMTcpKigxIC0gODkvMTE3KQpzcGxpdF9naW5pIDwtICgxODYvMzAzKSpnaW5pX3RoYWwzICsgKDExNy8zMDMpKmdpbmlfbm90X3RoYWwzCnNwbGl0X2dpbmkKYGBgClNsaWdodGx5IGxlc3MgcHVyZSBub3cuCgojIyBqLgoKYGBge3J9CmhlYXJ0ICU+JQogIGZpbHRlcihjcCA9PSAiMiIpICU+JQogIGdyb3VwX2J5KHRhcmdldCkgJT4lCiAgc3VtbWFyaXplKG4gPSBuKCkpCmBgYAoKYGBge3J9CmhlYXJ0ICU+JQogIGZpbHRlcihjcCAhPSAiMiIpICU+JQogIGdyb3VwX2J5KHRhcmdldCkgJT4lCiAgc3VtbWFyaXplKG4gPSBuKCkpCmBgYAoKYGBge3J9CmdpbmlfY3AyIDwtICg2OS84NykqKDEgLSA2OS84NykgKyAoMTgvODcpKigxIC0gMTgvODcpCmdpbmlfbm90X2NwMiA8LSAoOTYvMjE2KSooMSAtIDk2LzIxNikgKyAoMTIwLzIxNikqKDEgLSAxMjAvMjE2KQpzcGxpdF9naW5pIDwtICg4Ny8zMDMpKmdpbmlfY3AyICsgKDIxNi8zMDMpKmdpbmlfbm90X2NwMgpzcGxpdF9naW5pCmBgYApMZXNzIHB1cmUgdGhlbiB0aGUgc3BsaXQgb24gdGhhbDIuCgojIyBrLgpgYGB7cn0Kc2V0LnNlZWQoMSkKCm1vZGVsLnRyZWUucHJ1bmUgPC0gdHJhaW4odGFyZ2V0IH4gLiwKICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gaGVhcnQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kID0gInJwYXJ0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICB0ckNvbnRyb2wgPSBmaXRDb250cm9sLAogICAgICAgICAgICAgICAgICAgICAgICAgIHR1bmVMZW5ndGggPSAxMCkgIyBMZXQgZnVuY3Rpb24gcGljayAxMCByZWFzb25hYmxlIHR1bmluZyBwYXJhbWV0ZXJzIHRvIHBydW5lIHRoZSB0cmVlCm1vZGVsLnRyZWUucHJ1bmUKYGBgClRoZSBhY2N1cmFjeSB3aGVuIG5vdCBwcnVuaW5nIHdhcyAwLjczMzg4MjEsIHRoZSBiZXN0IGFjY3VyYWN5IHdpdGggcHJ1bmluZyB3YXMgMC43NjY3OTI3LCBzbyB3ZSBkaWQgYmV0dGVyIHdpdGggcHJ1bmluZy4gVGhpcyBtYWtlcyBzZW5zZSBzaW5jZSBwcnVuaW5nIGxpbWl0cyB0aGUgbnVtYmVyIG9mIHNwbGl0cyBhIHRyZWUgY2FuIGRvLCBrZWVwaW5nIGl0IGZyb20gb3ZlcmZpdHRpbmcgYW5kIHJlbW92aW5nIHVubmVjZXNzYXJ5IHNwbGl0cy4KCiMjIGwuCmBgYHtyfQpwYXIoeHBkID0gTkEpCnBsb3QobW9kZWwudHJlZS5wcnVuZSRmaW5hbE1vZGVsKQp0ZXh0KG1vZGVsLnRyZWUucHJ1bmUkZmluYWxNb2RlbCkKYGBgCmBgYHtyfQpoZWFydCAlPiUKICBmaWx0ZXIodGhhbCA9PSAiMiIpICU+JQogIGdyb3VwX2J5KHRhcmdldCkgJT4lCiAgc3VtbWFyaXplKG4gPSBuKCkpCmBgYApgYGB7cn0KaGVhcnQgJT4lCiAgZmlsdGVyKHRoYWwgIT0gIjIiKSAlPiUKICBncm91cF9ieSh0YXJnZXQpICU+JQogIHN1bW1hcml6ZShuID0gbigpKQpgYGAKYGBge3J9CjIzMi8zMDMKYGBgCkV2ZXJ5dGhpbmcgY2hlY2tzIG91dC4=