Load Packages and Data
library(class)
library(RColorBrewer)
pima <- read.table('data/diabetes.csv', header=TRUE, sep=',')
summary(pima)
Pregnancies Glucose BloodPressure SkinThickness Insulin
Min. : 0.000 Min. : 0.0 Min. : 0.00 Min. : 0.00 Min. : 0.0
1st Qu.: 1.000 1st Qu.: 99.0 1st Qu.: 62.00 1st Qu.: 0.00 1st Qu.: 0.0
Median : 3.000 Median :117.0 Median : 72.00 Median :23.00 Median : 30.5
Mean : 3.845 Mean :120.9 Mean : 69.11 Mean :20.54 Mean : 79.8
3rd Qu.: 6.000 3rd Qu.:140.2 3rd Qu.: 80.00 3rd Qu.:32.00 3rd Qu.:127.2
Max. :17.000 Max. :199.0 Max. :122.00 Max. :99.00 Max. :846.0
BMI DiabetesPedigreeFunction Age Outcome
Min. : 0.00 Min. :0.0780 Min. :21.00 Min. :0.000
1st Qu.:27.30 1st Qu.:0.2437 1st Qu.:24.00 1st Qu.:0.000
Median :32.00 Median :0.3725 Median :29.00 Median :0.000
Mean :31.99 Mean :0.4719 Mean :33.24 Mean :0.349
3rd Qu.:36.60 3rd Qu.:0.6262 3rd Qu.:41.00 3rd Qu.:1.000
Max. :67.10 Max. :2.4200 Max. :81.00 Max. :1.000
Variation in Validation Accuracy
min_val_acc_by_k <- apply(val_acc_by_split_and_k, 2, min)
max_val_acc_by_k <- apply(val_acc_by_split_and_k, 2, max)
plot(k_range, min_val_acc_by_k, ylim=c(0.5, 0.85), pch=".", col="salmon",
xlab="K", ylab="Accuracy", main="Minimum and Maximum Validation Accuracy")
lines(k_range, min_val_acc_by_k, lty=2, col='black')
lines(k_range, max_val_acc_by_k, lty=2, col='black')

diff_val_acc_by_k <- max_val_acc_by_k - min_val_acc_by_k
cat('K with Max Difference: ', which.max(diff_val_acc_by_k), '\n',
'Maximum Difference: ', max(diff_val_acc_by_k), sep='')
K with Max Difference: 34
Maximum Difference: 0.1168831
m1 <- val_acc_by_split_and_k[3,]
m2 <- val_acc_by_split_and_k[6,]
m3 <- val_acc_by_split_and_k[9,]
plot(k_range, min_val_acc_by_k, ylim=c(0.5, 0.85), pch=".", col="salmon",
xlab="K", ylab="Accuracy", main="Variation in Validation Accuracy")
lines(k_range, min_val_acc_by_k, lty=2, col='salmon')
lines(k_range, max_val_acc_by_k, lty=2, col='salmon')
lines(k_range, m1, col=colors[2], lwd=2)
lines(k_range, m2, col=colors[3], lwd=2)
lines(k_range, m3, col=colors[4], lwd=2)
segments(which.max(m1), 0, which.max(m1), max(m1), lty=2, col=colors[2])
segments(which.max(m2), 0, which.max(m2), max(m2), lty=2, col=colors[3])
segments(which.max(m3), 0, which.max(m3), max(m3), lty=2, col=colors[4])

NA
10-Fold Cross Validation
set.seed(1)
ix <- 1:nrow(pima)
shuffled_ix <- sample(1:nrow(pima))
folds <- split(ix, shuffled_ix%%10)
folds[[1]]
[1] 6 12 34 43 44 64 69 79 83 87 88 105 121 133 140 153 157 161 176 181 220 242 245 259 261 274 281 287 308 310
[31] 328 341 363 369 385 387 396 412 418 420 427 448 451 454 455 466 467 477 479 482 491 494 512 515 529 550 564 590 596 605
[61] 615 633 634 648 654 659 660 676 693 704 710 716 726 746 753 763
cat('Fold', '\t', 'Rows', '\n', sep='')
Fold Rows
cat('------------\n')
------------
for (i in 1:10){
cat(i, '\t', length(folds[[i]]), '\n', sep='')
}
1 76
2 77
3 77
4 77
5 77
6 77
7 77
8 77
9 77
10 76
avg_accuracy_by_K <- c()
for (k in k_range){
total = 0
for (i in 1:10){
X_train_temp <- pima[-folds[[i]], 1:8]
X_valid_temp <- pima[ folds[[i]], 1:8]
y_train_temp <- pima[-folds[[i]], 9]
y_valid_temp <- pima[ folds[[i]], 9]
temp_valid_pred <- knn(X_train_temp, X_valid_temp, y_train_temp, k=k)
temp_valid_acc <- mean(temp_valid_pred == y_valid_temp)
total <- total + temp_valid_acc
}
avg_accuracy_by_K <- c(avg_accuracy_by_K, total / 10)
}
plot(k_range, avg_accuracy_by_K, ylim=c(0.5, 0.85), pch=".", col="salmon",
xlab="K", ylab="Accuracy", main="10-Fold Cross-Validation Accuracy")
lines(k_range, avg_accuracy_by_K, col='black', lwd=2)
lines(k_range, min_val_acc_by_k, lty=2, col='salmon')
lines(k_range, max_val_acc_by_k, lty=2, col='salmon')
segments(which.max(avg_accuracy_by_K), 0,
which.max(avg_accuracy_by_K), max(avg_accuracy_by_K), lty=2)

Variation in 10-Fold Cross Validation Accuracy
set.seed(1)
avg_val_acc_by_split_and_k <- c()
# Create 10 different splits (into 10 folds)
for(i in 1:10){
ix <- 1:nrow(pima)
shuffled_ix <- sample(1:nrow(pima))
folds <- split(ix, shuffled_ix%%10)
# Loop over values of K
avg_accuracy_by_K <- c()
for (k in k_range){
#a Loop over each fold
total = 0
for (f in 1:10){
X_train_temp <- pima[-folds[[f]], 1:8]
X_valid_temp <- pima[ folds[[f]], 1:8]
y_train_temp <- pima[-folds[[f]], 9]
y_valid_temp <- pima[ folds[[f]], 9]
temp_valid_pred <- knn(X_train_temp, X_valid_temp, y_train_temp, k=k)
temp_valid_acc <- mean(temp_valid_pred == y_valid_temp)
total <- total + temp_valid_acc
}
avg_accuracy_by_K <- c(avg_accuracy_by_K, total / 10)
}
avg_val_acc_by_split_and_k <- rbind(avg_val_acc_by_split_and_k, avg_accuracy_by_K)
}
min_avg_val_acc_by_k <- apply(avg_val_acc_by_split_and_k, 2, min)
max_avg_val_acc_by_k <- apply(avg_val_acc_by_split_and_k, 2, max)
plot(k_range, min_avg_val_acc_by_k, ylim=c(0.5, 0.85), pch=".",
col="salmon", xlab="K", ylab="Accuracy",
main="Min and Max 10-Fold Cross Validation Accuracy")
lines(k_range, min_val_acc_by_k, lty=2, col='salmon')
lines(k_range, max_val_acc_by_k, lty=2, col='salmon')
lines(k_range, min_avg_val_acc_by_k, lty=2, col='black')
lines(k_range, max_avg_val_acc_by_k, lty=2, col='black')

diff_avg_val_acc_by_k <- max_avg_val_acc_by_k - min_avg_val_acc_by_k
cat(which.max(diff_val_acc_by_k), '\n', max(diff_avg_val_acc_by_k), sep='')
34
0.04683869
m1 <- avg_val_acc_by_split_and_k[1,]
m2 <- avg_val_acc_by_split_and_k[2,]
m3 <- avg_val_acc_by_split_and_k[3,]
plot(k_range, min_avg_val_acc_by_k, ylim=c(0.5, 0.85), pch=".",
col="salmon", xlab="K", ylab="Accuracy",
main="Variation in 10-Fold Cross Validation Accuracy")
lines(k_range, min_avg_val_acc_by_k, lty=2, col='salmon')
lines(k_range, max_avg_val_acc_by_k, lty=2, col='salmon')
lines(k_range, m1, col=colors[2], lwd=2)
lines(k_range, m2, col=colors[3], lwd=2)
lines(k_range, m3, col=colors[4], lwd=2)
segments(which.max(m1), 0, which.max(m1), max(m1), lty=2, col=colors[2])
segments(which.max(m2), 0, which.max(m2), max(m2), lty=2, col=colors[3])
segments(which.max(m3), 0, which.max(m3), max(m3), lty=2, col=colors[4])

NA
cat('Split', '\t', 'Best K', '\t', 'Max AVg Val Acc', '\n', sep='')
Split Best K Max AVg Val Acc
cat('-------------------------------\n')
-------------------------------
for (i in 1:nrow(val_acc_by_split_and_k)){
cat(i, '\t', which.max(avg_val_acc_by_split_and_k[i, ]), '\t', max(avg_val_acc_by_split_and_k[i, ]), '\n', sep='')
}
1 22 0.7566131
2 17 0.7683014
3 19 0.7538107
4 17 0.7487867
5 17 0.7616712
6 17 0.7654819
7 17 0.7617738
8 16 0.7539815
9 19 0.7551948
10 16 0.7499487
LS0tDQp0aXRsZTogIkxlc3NvbiA1LjEgLSBJbnRyb2R1Y3Rpb24gdG8gQ3Jvc3MgVmFsaWRhdGlvbiINCmF1dGhvcjogIlJvYmJpZSBCZWFuZSINCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vazoNCiAgICB0aGVtZTogZmxhdGx5DQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZGVwdGg6IDQNCi0tLQ0KDQoNCiMjIyAqKkxvYWQgUGFja2FnZXMgYW5kIERhdGEqKg0KDQpgYGB7cn0NCmxpYnJhcnkoY2xhc3MpDQpsaWJyYXJ5KFJDb2xvckJyZXdlcikNCmBgYA0KDQoNCmBgYHtyfQ0KcGltYSA8LSByZWFkLnRhYmxlKCdkYXRhL2RpYWJldGVzLmNzdicsIGhlYWRlcj1UUlVFLCBzZXA9JywnKQ0Kc3VtbWFyeShwaW1hKQ0KYGBgDQoNCmBgYHtyLCBlY2hvPUZBTFNFfQ0KY29sb3JzIDwtIGJyZXdlci5wYWwobiA9IDgsIG5hbWUgPSAiU2V0MSIpDQpgYGANCg0KDQojIyMgKipWYXJpYXRpb24gaW4gVmFsaWRhdGlvbiBBY2N1cmFjeSoqDQoNCmBgYHtyfQ0KY29sb3JzIDwtIGJyZXdlci5wYWwobiA9IDgsIG5hbWUgPSAiU2V0MSIpDQoNCnNldC5zZWVkKDMpDQp2YWxfYWNjX2J5X3NwbGl0X2FuZF9rIDwtIGMoKQ0KDQprX3JhbmdlIDwtIDE6NjANCg0KZm9yKGkgaW4gMToxMCl7DQogIA0KICAjIENyZWF0ZSA3MC8zMCB0cmFpbi92YWxpZGF0aW9uIHNwbGl0DQogIHNlbCA8LSBzYW1wbGUoMTpucm93KHBpbWEpLCAwLjcqbnJvdyhwaW1hKSkNCiAgWF90cmFpbiA8LSBwaW1hW3NlbCwgXVssMTo4XQ0KICBYX3ZhbGlkIDwtIHBpbWFbLXNlbCwgXVssMTo4XQ0KICB5X3RyYWluIDwtIHBpbWFbc2VsLCBdWyw5XQ0KICB5X3ZhbGlkIDwtIHBpbWFbLXNlbCwgXVssOV0NCg0KICB2YWxfYWNjX2J5X2sgPC0gYygpDQogIA0KICBmb3IgKGsgaW4ga19yYW5nZSl7DQogICAgdmFsX3ByZWQgPC0ga25uKFhfdHJhaW4sIFhfdmFsaWQsIHlfdHJhaW4sIGs9aykNCiAgICB2YWxfYWNjX2J5X2sgPC0gYyh2YWxfYWNjX2J5X2ssIG1lYW4odmFsX3ByZWQgPT0geV92YWxpZCkpDQogIH0NCiAgDQogIHZhbF9hY2NfYnlfc3BsaXRfYW5kX2sgPC0gcmJpbmQodmFsX2FjY19ieV9zcGxpdF9hbmRfaywgdmFsX2FjY19ieV9rKQ0KICANCn0NCg0KdmFsX2FjY19ieV9zcGxpdF9hbmRfa1ssIDE6OF0NCmBgYA0KDQoNCmBgYHtyfSANCm1pbl92YWxfYWNjX2J5X2sgPC0gYXBwbHkodmFsX2FjY19ieV9zcGxpdF9hbmRfaywgMiwgbWluKQ0KbWF4X3ZhbF9hY2NfYnlfayA8LSBhcHBseSh2YWxfYWNjX2J5X3NwbGl0X2FuZF9rLCAyLCBtYXgpDQoNCnBsb3Qoa19yYW5nZSwgbWluX3ZhbF9hY2NfYnlfaywgeWxpbT1jKDAuNSwgMC44NSksIHBjaD0iLiIsIGNvbD0ic2FsbW9uIiwgDQogICAgIHhsYWI9IksiLCB5bGFiPSJBY2N1cmFjeSIsIG1haW49Ik1pbmltdW0gYW5kIE1heGltdW0gVmFsaWRhdGlvbiBBY2N1cmFjeSIpDQoNCmxpbmVzKGtfcmFuZ2UsIG1pbl92YWxfYWNjX2J5X2ssIGx0eT0yLCBjb2w9J2JsYWNrJykNCmxpbmVzKGtfcmFuZ2UsIG1heF92YWxfYWNjX2J5X2ssIGx0eT0yLCBjb2w9J2JsYWNrJykNCmBgYA0KDQpgYGB7cn0NCmRpZmZfdmFsX2FjY19ieV9rIDwtIG1heF92YWxfYWNjX2J5X2sgLSBtaW5fdmFsX2FjY19ieV9rDQoNCmNhdCgnSyB3aXRoIE1heCBEaWZmZXJlbmNlOiAnLCB3aGljaC5tYXgoZGlmZl92YWxfYWNjX2J5X2spLCAnXG4nLCANCiAgICAnTWF4aW11bSBEaWZmZXJlbmNlOiAgICAnLCBtYXgoZGlmZl92YWxfYWNjX2J5X2spLCBzZXA9JycpDQpgYGANCg0KDQoNCmBgYHtyfSANCm0xIDwtIHZhbF9hY2NfYnlfc3BsaXRfYW5kX2tbMyxdDQptMiA8LSB2YWxfYWNjX2J5X3NwbGl0X2FuZF9rWzYsXQ0KbTMgPC0gdmFsX2FjY19ieV9zcGxpdF9hbmRfa1s5LF0NCg0KcGxvdChrX3JhbmdlLCBtaW5fdmFsX2FjY19ieV9rLCB5bGltPWMoMC41LCAwLjg1KSwgcGNoPSIuIiwgY29sPSJzYWxtb24iLCANCiAgICAgeGxhYj0iSyIsIHlsYWI9IkFjY3VyYWN5IiwgbWFpbj0iVmFyaWF0aW9uIGluIFZhbGlkYXRpb24gQWNjdXJhY3kiKQ0KDQpsaW5lcyhrX3JhbmdlLCBtaW5fdmFsX2FjY19ieV9rLCBsdHk9MiwgY29sPSdzYWxtb24nKQ0KbGluZXMoa19yYW5nZSwgbWF4X3ZhbF9hY2NfYnlfaywgbHR5PTIsIGNvbD0nc2FsbW9uJykNCg0KbGluZXMoa19yYW5nZSwgbTEsIGNvbD1jb2xvcnNbMl0sIGx3ZD0yKQ0KbGluZXMoa19yYW5nZSwgbTIsIGNvbD1jb2xvcnNbM10sIGx3ZD0yKQ0KbGluZXMoa19yYW5nZSwgbTMsIGNvbD1jb2xvcnNbNF0sIGx3ZD0yKQ0KDQpzZWdtZW50cyh3aGljaC5tYXgobTEpLCAwLCB3aGljaC5tYXgobTEpLCBtYXgobTEpLCBsdHk9MiwgY29sPWNvbG9yc1syXSkNCnNlZ21lbnRzKHdoaWNoLm1heChtMiksIDAsIHdoaWNoLm1heChtMiksIG1heChtMiksIGx0eT0yLCBjb2w9Y29sb3JzWzNdKQ0Kc2VnbWVudHMod2hpY2gubWF4KG0zKSwgMCwgd2hpY2gubWF4KG0zKSwgbWF4KG0zKSwgbHR5PTIsIGNvbD1jb2xvcnNbNF0pDQogIA0KYGBgDQoNCg0KYGBge3J9DQpjYXQoJ01vZGVsJywgJ1x0JywgJ0Jlc3QgSycsICdcdCcsICdNYXggVmFsIEFjYycsICdcbicsIHNlcD0nJykNCmNhdCgnLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4nKQ0KZm9yIChpIGluIDE6bnJvdyh2YWxfYWNjX2J5X3NwbGl0X2FuZF9rKSl7DQogIGNhdChpLCAnXHQnLCB3aGljaC5tYXgodmFsX2FjY19ieV9zcGxpdF9hbmRfa1tpLCBdKSwgJ1x0JywgbWF4KHZhbF9hY2NfYnlfc3BsaXRfYW5kX2tbaSwgXSksICdcbicsIHNlcD0nJykNCn0NCmBgYA0KDQoNCiMjIyAqKjEwLUZvbGQgQ3Jvc3MgVmFsaWRhdGlvbioqDQoNCg0KYGBge3J9DQpzZXQuc2VlZCgxKQ0KaXggPC0gMTpucm93KHBpbWEpDQpzaHVmZmxlZF9peCA8LSBzYW1wbGUoMTpucm93KHBpbWEpKQ0KZm9sZHMgPC0gc3BsaXQoaXgsIHNodWZmbGVkX2l4JSUxMCkNCg0KZm9sZHNbWzFdXQ0KYGBgDQoNCmBgYHtyfQ0KY2F0KCdGb2xkJywgJ1x0JywgJ1Jvd3MnLCAnXG4nLCBzZXA9JycpDQpjYXQoJy0tLS0tLS0tLS0tLVxuJykNCmZvciAoaSBpbiAxOjEwKXsNCiAgY2F0KGksICdcdCcsIGxlbmd0aChmb2xkc1tbaV1dKSwgJ1xuJywgc2VwPScnKQ0KfQ0KYGBgDQoNCmBgYHtyfQ0KYXZnX2FjY3VyYWN5X2J5X0sgPC0gYygpDQoNCmZvciAoayBpbiBrX3JhbmdlKXsNCiAgDQogIHRvdGFsID0gMA0KICBmb3IgKGkgaW4gMToxMCl7DQogICAgWF90cmFpbl90ZW1wIDwtIHBpbWFbLWZvbGRzW1tpXV0sIDE6OF0NCiAgICBYX3ZhbGlkX3RlbXAgPC0gcGltYVsgZm9sZHNbW2ldXSwgMTo4XQ0KICAgIHlfdHJhaW5fdGVtcCA8LSBwaW1hWy1mb2xkc1tbaV1dLCA5XQ0KICAgIHlfdmFsaWRfdGVtcCA8LSBwaW1hWyBmb2xkc1tbaV1dLCA5XQ0KICAgIA0KICAgIHRlbXBfdmFsaWRfcHJlZCA8LSBrbm4oWF90cmFpbl90ZW1wLCBYX3ZhbGlkX3RlbXAsIHlfdHJhaW5fdGVtcCwgaz1rKQ0KICAgIHRlbXBfdmFsaWRfYWNjIDwtIG1lYW4odGVtcF92YWxpZF9wcmVkID09IHlfdmFsaWRfdGVtcCkNCiAgICB0b3RhbCA8LSB0b3RhbCArIHRlbXBfdmFsaWRfYWNjDQogIH0NCiAgYXZnX2FjY3VyYWN5X2J5X0sgPC0gYyhhdmdfYWNjdXJhY3lfYnlfSywgdG90YWwgLyAxMCkgIA0KICANCn0NCg0KcGxvdChrX3JhbmdlLCBhdmdfYWNjdXJhY3lfYnlfSywgeWxpbT1jKDAuNSwgMC44NSksIHBjaD0iLiIsIGNvbD0ic2FsbW9uIiwgDQogICAgIHhsYWI9IksiLCB5bGFiPSJBY2N1cmFjeSIsIG1haW49IjEwLUZvbGQgQ3Jvc3MtVmFsaWRhdGlvbiBBY2N1cmFjeSIpDQoNCmxpbmVzKGtfcmFuZ2UsIGF2Z19hY2N1cmFjeV9ieV9LLCBjb2w9J2JsYWNrJywgbHdkPTIpDQpsaW5lcyhrX3JhbmdlLCBtaW5fdmFsX2FjY19ieV9rLCBsdHk9MiwgY29sPSdzYWxtb24nKQ0KbGluZXMoa19yYW5nZSwgbWF4X3ZhbF9hY2NfYnlfaywgbHR5PTIsIGNvbD0nc2FsbW9uJykNCg0Kc2VnbWVudHMod2hpY2gubWF4KGF2Z19hY2N1cmFjeV9ieV9LKSwgMCwgDQogICAgICAgIHdoaWNoLm1heChhdmdfYWNjdXJhY3lfYnlfSyksIG1heChhdmdfYWNjdXJhY3lfYnlfSyksIGx0eT0yKQ0KYGBgDQoNCg0KIyMjICoqVmFyaWF0aW9uIGluIDEwLUZvbGQgQ3Jvc3MgVmFsaWRhdGlvbiBBY2N1cmFjeSoqDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoMSkNCmF2Z192YWxfYWNjX2J5X3NwbGl0X2FuZF9rIDwtIGMoKQ0KDQoNCiMgQ3JlYXRlIDEwIGRpZmZlcmVudCBzcGxpdHMgKGludG8gMTAgZm9sZHMpDQpmb3IoaSBpbiAxOjEwKXsNCiAgaXggPC0gMTpucm93KHBpbWEpDQogIHNodWZmbGVkX2l4IDwtIHNhbXBsZSgxOm5yb3cocGltYSkpDQogIGZvbGRzIDwtIHNwbGl0KGl4LCBzaHVmZmxlZF9peCUlMTApDQogIA0KICAjIExvb3Agb3ZlciB2YWx1ZXMgb2YgSw0KICBhdmdfYWNjdXJhY3lfYnlfSyA8LSBjKCkNCiAgZm9yIChrIGluIGtfcmFuZ2Upew0KICAgIA0KICAgICNhIExvb3Agb3ZlciBlYWNoIGZvbGQNCiAgICB0b3RhbCA9IDANCiAgICBmb3IgKGYgaW4gMToxMCl7DQogICAgICBYX3RyYWluX3RlbXAgPC0gcGltYVstZm9sZHNbW2ZdXSwgMTo4XQ0KICAgICAgWF92YWxpZF90ZW1wIDwtIHBpbWFbIGZvbGRzW1tmXV0sIDE6OF0NCiAgICAgIHlfdHJhaW5fdGVtcCA8LSBwaW1hWy1mb2xkc1tbZl1dLCA5XQ0KICAgICAgeV92YWxpZF90ZW1wIDwtIHBpbWFbIGZvbGRzW1tmXV0sIDldDQogICAgICANCiAgICAgIHRlbXBfdmFsaWRfcHJlZCA8LSBrbm4oWF90cmFpbl90ZW1wLCBYX3ZhbGlkX3RlbXAsIHlfdHJhaW5fdGVtcCwgaz1rKQ0KICAgICAgdGVtcF92YWxpZF9hY2MgPC0gbWVhbih0ZW1wX3ZhbGlkX3ByZWQgPT0geV92YWxpZF90ZW1wKQ0KICAgICAgdG90YWwgPC0gdG90YWwgKyB0ZW1wX3ZhbGlkX2FjYw0KICAgIH0NCiAgICBhdmdfYWNjdXJhY3lfYnlfSyA8LSBjKGF2Z19hY2N1cmFjeV9ieV9LLCB0b3RhbCAvIDEwKSAgDQogICAgDQogIH0NCiAgDQogIGF2Z192YWxfYWNjX2J5X3NwbGl0X2FuZF9rIDwtIHJiaW5kKGF2Z192YWxfYWNjX2J5X3NwbGl0X2FuZF9rLCBhdmdfYWNjdXJhY3lfYnlfSykNCn0NCmBgYA0KDQoNCg0KDQpgYGB7cn0gDQptaW5fYXZnX3ZhbF9hY2NfYnlfayA8LSBhcHBseShhdmdfdmFsX2FjY19ieV9zcGxpdF9hbmRfaywgMiwgbWluKQ0KbWF4X2F2Z192YWxfYWNjX2J5X2sgPC0gYXBwbHkoYXZnX3ZhbF9hY2NfYnlfc3BsaXRfYW5kX2ssIDIsIG1heCkNCg0KcGxvdChrX3JhbmdlLCBtaW5fYXZnX3ZhbF9hY2NfYnlfaywgeWxpbT1jKDAuNSwgMC44NSksIHBjaD0iLiIsIA0KICAgICBjb2w9InNhbG1vbiIsIHhsYWI9IksiLCB5bGFiPSJBY2N1cmFjeSIsIA0KICAgICBtYWluPSJNaW4gYW5kIE1heCAxMC1Gb2xkIENyb3NzIFZhbGlkYXRpb24gQWNjdXJhY3kiKQ0KDQpsaW5lcyhrX3JhbmdlLCBtaW5fdmFsX2FjY19ieV9rLCBsdHk9MiwgY29sPSdzYWxtb24nKQ0KbGluZXMoa19yYW5nZSwgbWF4X3ZhbF9hY2NfYnlfaywgbHR5PTIsIGNvbD0nc2FsbW9uJykNCg0KbGluZXMoa19yYW5nZSwgbWluX2F2Z192YWxfYWNjX2J5X2ssIGx0eT0yLCBjb2w9J2JsYWNrJykNCmxpbmVzKGtfcmFuZ2UsIG1heF9hdmdfdmFsX2FjY19ieV9rLCBsdHk9MiwgY29sPSdibGFjaycpDQpgYGANCg0KYGBge3J9DQpkaWZmX2F2Z192YWxfYWNjX2J5X2sgPC0gbWF4X2F2Z192YWxfYWNjX2J5X2sgLSBtaW5fYXZnX3ZhbF9hY2NfYnlfaw0KY2F0KHdoaWNoLm1heChkaWZmX3ZhbF9hY2NfYnlfayksICdcbicsIG1heChkaWZmX2F2Z192YWxfYWNjX2J5X2spLCBzZXA9JycpDQpgYGANCg0KDQoNCmBgYHtyfSANCm0xIDwtIGF2Z192YWxfYWNjX2J5X3NwbGl0X2FuZF9rWzEsXQ0KbTIgPC0gYXZnX3ZhbF9hY2NfYnlfc3BsaXRfYW5kX2tbMixdDQptMyA8LSBhdmdfdmFsX2FjY19ieV9zcGxpdF9hbmRfa1szLF0NCg0KcGxvdChrX3JhbmdlLCBtaW5fYXZnX3ZhbF9hY2NfYnlfaywgeWxpbT1jKDAuNSwgMC44NSksIHBjaD0iLiIsIA0KICAgICBjb2w9InNhbG1vbiIsIHhsYWI9IksiLCB5bGFiPSJBY2N1cmFjeSIsIA0KICAgICBtYWluPSJWYXJpYXRpb24gaW4gMTAtRm9sZCBDcm9zcyBWYWxpZGF0aW9uIEFjY3VyYWN5IikNCg0KbGluZXMoa19yYW5nZSwgbWluX2F2Z192YWxfYWNjX2J5X2ssIGx0eT0yLCBjb2w9J3NhbG1vbicpDQpsaW5lcyhrX3JhbmdlLCBtYXhfYXZnX3ZhbF9hY2NfYnlfaywgbHR5PTIsIGNvbD0nc2FsbW9uJykNCg0KbGluZXMoa19yYW5nZSwgbTEsIGNvbD1jb2xvcnNbMl0sIGx3ZD0yKQ0KbGluZXMoa19yYW5nZSwgbTIsIGNvbD1jb2xvcnNbM10sIGx3ZD0yKQ0KbGluZXMoa19yYW5nZSwgbTMsIGNvbD1jb2xvcnNbNF0sIGx3ZD0yKQ0KDQpzZWdtZW50cyh3aGljaC5tYXgobTEpLCAwLCB3aGljaC5tYXgobTEpLCBtYXgobTEpLCBsdHk9MiwgY29sPWNvbG9yc1syXSkNCnNlZ21lbnRzKHdoaWNoLm1heChtMiksIDAsIHdoaWNoLm1heChtMiksIG1heChtMiksIGx0eT0yLCBjb2w9Y29sb3JzWzNdKQ0Kc2VnbWVudHMod2hpY2gubWF4KG0zKSwgMCwgd2hpY2gubWF4KG0zKSwgbWF4KG0zKSwgbHR5PTIsIGNvbD1jb2xvcnNbNF0pDQogIA0KYGBgDQoNCg0KYGBge3J9DQpjYXQoJ1NwbGl0JywgJ1x0JywgJ0Jlc3QgSycsICdcdCcsICdNYXggQVZnIFZhbCBBY2MnLCAnXG4nLCBzZXA9JycpDQpjYXQoJy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbicpDQpmb3IgKGkgaW4gMTpucm93KHZhbF9hY2NfYnlfc3BsaXRfYW5kX2spKXsNCiAgY2F0KGksICdcdCcsIHdoaWNoLm1heChhdmdfdmFsX2FjY19ieV9zcGxpdF9hbmRfa1tpLCBdKSwgJ1x0JywgbWF4KGF2Z192YWxfYWNjX2J5X3NwbGl0X2FuZF9rW2ksIF0pLCAnXG4nLCBzZXA9JycpDQp9DQpgYGANCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg==