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==