Problem Description

Given 800 data from DataTrain_Tugas3_AI.csv that contains five attributes \(X_1\), \(X_2\), \(X_3\), \(X_4\), and \(X_4\). It also has a single output that contains four labels (named \(Y\)) \(0\), \(1\), \(2\), and \(3\). We need to build a classification system with k-Nearest Neighbors (kNN) for determining a label from data testing in DataTest_Tugas3_AI.csv. Then, create a file TebakanTugas3.csv which is contains a single column about 200 rows of labels (\(0\), \(1\), \(2\), or \(3\)).

Implementation

Suppose that several helpers function already included. Source code available on wisn/knncpp.

Preparation

First, collect all data train from DataTrain_Tugas3_AI.csv file.

Data data_train = readCSV("data/DataTrain_Tugas3_AI.csv");

\[ \\ \]

Second, collect all data test from DataTest_Tugas3_AI.csv file.

Data data_test = readCSV("data/DataTest_Tugas3_AI.csv");

\[ \\ \]

Lastly, build a function to calculate distance between object. We will using Euclidean distance.

// Included in kNN abstract data type
  double distance(Datum a, Datum b) {
    double ret = 0;
    ret += pow(a.x1 - b.x1, 2);
    ret += pow(a.x2 - b.x2, 2);
    ret += pow(a.x3 - b.x3, 2);
    ret += pow(a.x4 - b.x4, 2);
    ret += pow(a.x5 - b.x5, 2);

    return sqrt(ret);
  }

\[ \\ \]

kNN Prediction

This is our main focus. We are trying to predict the classification result based on kNN algorithm with Euclidean distance.

// Included in kNN abstract data type
  char predictions(Datum test) {
    int train_size = this->train.size();
    
    vector<Point> points;
    for (int j = 0; j < train_size; j++) {
      Datum train = this->train[j];

      Point point;
      point.label = train.y;
      point.distance = this->distance(test, train);

      points.push_back(point);
    }

    auto comparator = [](Point x, Point y) {
      if (x.distance == y.distance)
        return x.label < y.label;

      return x.distance < y.distance;
    };

    sort(points.begin(), points.end(), comparator);

    int labels[4];
    fill(labels, labels + 4, 0);
    for (int i = 0; i < this->k; i++) {
      labels[points[i].label - '0'] += 1;
    }

    int label = 0;
    int best = labels[0];
    for (int i = 0; i < 4; i++) {
      if (labels[i] > best) {
        best = labels[i];
        label = i;
      }
    }

    return '0' + label;
  }

\[ \\ \]

Now, try to gets some data based on \(K = 5\) value.

cout << '[';

kNN knn (data_train, 5);
for (int i = 0; i < (int) data_test.size(); i++) {
  if (i > 0) cout << ", ";

  Datum test = data_test[i];
  cout << knn.predictions(test);
}

cout << ']' << endl;
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 2, 0, 0, 2, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 3, 1, 1, 0, 0, 0, 0, 0, 0, 1, 3, 0, 0, 0, 1, 0, 0, 2, 2, 3, 3, 2, 2, 3, 3, 2, 3, 3, 0, 3, 3, 2, 2, 2, 2, 2, 0, 2, 3, 2, 2, 3, 3, 2, 2, 3, 2, 2, 3, 0, 3, 3, 2, 2, 2, 2, 2, 2, 0, 3, 2, 2, 2, 3, 3, 3, 2, 2, 3, 3, 3, 3, 2, 3, 2, 2, 3, 2, 3, 3, 2, 3, 2, 3, 2, 2, 2, 2, 3, 0, 2, 2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 1, 2, 3, 2, 3, 3, 2, 3, 2, 3, 3, 3, 3, 3, 3, 3]

\[ \\ \]

Our kNN prediction function works just as expected. However, does \(K = 5\) the right answer? We don’t know! Hence, we need to build a validation set to find a better \(K\) value. In this case, let’s just use k-fold cross validation and calculate the accuracy based on it. Why k-fold cross validation? Well, it just happened that I understand this one.

kNN Validation

Data validation_data = randomnize(data_train);
Data validation_train, validation_test;

int n = data_train.size();
int m = data_test.size();
for (int i = 0; i < (n - m); i++)
  validation_train.push_back(validation_data[i]);

for (int i = (n - m); i < n; i++)
  validation_test.push_back(validation_data[i]);

int max_k = m;
vector<pair<int, double>> accuracy (max_k);
for (int k = 1; k <= max_k; k++) {
  kNN knn (validation_train, k);
  int match = 0;

  for (int i = 0; i < m; i++) {
    Datum test = validation_test[i];
    int predictions = knn.predictions(test);
    if (predictions == test.y)
      match += 1;
  }

  accuracy[k - 1] = {k, (match / (double) m) * 100};
}

cout << "Accuracy calculation:" << endl;
cout << '[';
for (int i = 0; i < max_k; i++) {
  if (i > 0) cout << ", ";

  cout << "(K = " << accuracy[i].first << ", " << accuracy[i].second << "%)";
}

cout << ']' << endl;
Accuracy calculation:
[(K = 1, 81%), (K = 2, 80.5%), (K = 3, 83%), (K = 4, 82%), (K = 5, 84.5%), (K = 6, 83%), (K = 7, 86%), (K = 8, 84.5%), (K = 9, 85.5%), (K = 10, 85.5%), (K = 11, 86%), (K = 12, 85.5%), (K = 13, 86.5%), (K = 14, 86.5%), (K = 15, 87.5%), (K = 16, 86.5%), (K = 17, 86%), (K = 18, 85.5%), (K = 19, 85%), (K = 20, 85.5%), (K = 21, 86%), (K = 22, 85.5%), (K = 23, 85%), (K = 24, 83.5%), (K = 25, 83%), (K = 26, 83.5%), (K = 27, 84%), (K = 28, 83%), (K = 29, 84%), (K = 30, 85%), (K = 31, 84%), (K = 32, 85%), (K = 33, 84%), (K = 34, 84%), (K = 35, 83.5%), (K = 36, 83.5%), (K = 37, 82.5%), (K = 38, 84%), (K = 39, 84.5%), (K = 40, 84%), (K = 41, 83.5%), (K = 42, 84%), (K = 43, 84%), (K = 44, 84%), (K = 45, 85%), (K = 46, 85%), (K = 47, 85%), (K = 48, 84%), (K = 49, 84%), (K = 50, 83.5%), (K = 51, 83.5%), (K = 52, 83.5%), (K = 53, 83.5%), (K = 54, 83.5%), (K = 55, 83%), (K = 56, 82.5%), (K = 57, 82.5%), (K = 58, 82.5%), (K = 59, 83%), (K = 60, 82.5%), (K = 61, 84%), (K = 62, 82.5%), (K = 63, 82.5%), (K = 64, 82.5%), (K = 65, 81.5%), (K = 66, 81.5%), (K = 67, 80.5%), (K = 68, 81%), (K = 69, 81%), (K = 70, 81%), (K = 71, 81%), (K = 72, 81%), (K = 73, 81%), (K = 74, 82%), (K = 75, 81%), (K = 76, 81.5%), (K = 77, 81.5%), (K = 78, 82%), (K = 79, 82%), (K = 80, 82.5%), (K = 81, 81.5%), (K = 82, 82%), (K = 83, 83%), (K = 84, 82%), (K = 85, 82.5%), (K = 86, 83%), (K = 87, 82.5%), (K = 88, 82%), (K = 89, 82%), (K = 90, 82.5%), (K = 91, 82%), (K = 92, 82.5%), (K = 93, 81.5%), (K = 94, 81%), (K = 95, 81.5%), (K = 96, 80.5%), (K = 97, 81%), (K = 98, 81%), (K = 99, 81%), (K = 100, 81%), (K = 101, 81%), (K = 102, 80%), (K = 103, 80.5%), (K = 104, 81%), (K = 105, 80.5%), (K = 106, 80%), (K = 107, 80%), (K = 108, 80%), (K = 109, 79.5%), (K = 110, 79.5%), (K = 111, 80%), (K = 112, 80%), (K = 113, 80.5%), (K = 114, 80.5%), (K = 115, 80%), (K = 116, 79%), (K = 117, 79.5%), (K = 118, 78.5%), (K = 119, 78.5%), (K = 120, 79%), (K = 121, 79%), (K = 122, 79%), (K = 123, 79%), (K = 124, 78.5%), (K = 125, 79%), (K = 126, 78%), (K = 127, 79%), (K = 128, 79.5%), (K = 129, 79.5%), (K = 130, 79%), (K = 131, 79%), (K = 132, 78%), (K = 133, 78.5%), (K = 134, 78.5%), (K = 135, 78.5%), (K = 136, 78.5%), (K = 137, 78.5%), (K = 138, 78.5%), (K = 139, 78.5%), (K = 140, 78%), (K = 141, 77%), (K = 142, 76.5%), (K = 143, 76.5%), (K = 144, 76.5%), (K = 145, 77%), (K = 146, 76.5%), (K = 147, 76.5%), (K = 148, 76%), (K = 149, 76%), (K = 150, 75.5%), (K = 151, 75%), (K = 152, 75%), (K = 153, 75.5%), (K = 154, 75.5%), (K = 155, 75.5%), (K = 156, 74.5%), (K = 157, 75%), (K = 158, 74.5%), (K = 159, 75%), (K = 160, 74.5%), (K = 161, 74.5%), (K = 162, 73%), (K = 163, 73%), (K = 164, 73%), (K = 165, 73%), (K = 166, 72%), (K = 167, 72%), (K = 168, 72%), (K = 169, 72.5%), (K = 170, 71.5%), (K = 171, 71%), (K = 172, 71%), (K = 173, 71%), (K = 174, 70.5%), (K = 175, 70.5%), (K = 176, 70%), (K = 177, 69%), (K = 178, 69.5%), (K = 179, 69%), (K = 180, 69%), (K = 181, 69%), (K = 182, 68.5%), (K = 183, 69%), (K = 184, 69%), (K = 185, 67.5%), (K = 186, 67.5%), (K = 187, 66.5%), (K = 188, 65.5%), (K = 189, 64%), (K = 190, 63.5%), (K = 191, 64%), (K = 192, 62%), (K = 193, 62.5%), (K = 194, 61.5%), (K = 195, 61%), (K = 196, 60.5%), (K = 197, 60.5%), (K = 198, 59.5%), (K = 199, 58%), (K = 200, 58%)]

\[ \\ \]

Now, based on the accuracy table above, we could determine which \(K\) value is better.

int current_k = accuracy[0].first;
double current_accuracy = accuracy[0].second;
for (int i = 0; i < max_k; i++) {
  if (accuracy[i].second > current_accuracy) {
    current_accuracy = accuracy[i].second;
    current_k = accuracy[i].first;
  }
}

K = current_k;
cout << K << " with " << current_accuracy << " percentile." << endl;
K = 15 with 87.5 percentile.

\[ \\ \]

kNN Solution

We now already retrieved the best \(K\) value. Apply it to the kNN predictions function.

vector<char> labels;
kNN knn (data_train, K);
int n = data_test.size();
for (int i = 0; i < n; i++) {
  Datum test = data_test[i];
  labels.push_back(knn.predictions(test));
}

cout << "Retrieved solution:" << endl;
cout << '[';

for (int i = 0; i < n; i++) {
  if (i > 0) cout << ", ";

  cout << labels[i];
}

cout << ']' << endl;
Retrieved solution:
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 2, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 3, 1, 0, 0, 1, 1, 0, 1, 2, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 2, 2, 3, 3, 2, 2, 3, 3, 2, 3, 3, 0, 3, 3, 2, 2, 2, 2, 2, 2, 0, 3, 2, 2, 3, 3, 2, 2, 3, 2, 2, 3, 2, 3, 3, 2, 2, 2, 2, 2, 2, 0, 3, 2, 2, 2, 3, 3, 3, 2, 2, 3, 3, 3, 3, 2, 1, 0, 2, 3, 2, 3, 3, 2, 3, 2, 3, 2, 2, 2, 2, 3, 0, 2, 2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 3, 2, 3, 3, 2, 3, 2, 3, 3, 3, 3, 3, 3, 3]

\[ \\ \]

Store the solution to TebakanTugas3.csv and we are all set!

LS0tCnRpdGxlOiAiRGF0YSBDbGFzc2lmaWNhdGlvbiB3aXRoIGstTmVhcmVzdCBOZWlnaGJvcnMgKGtOTikgaW4gQysrMTEiCmF1dGhvcjogIldpc251IEFkaSBOdXJjYWh5byIKZGF0ZTogIkRlY2VtYmVyIDJuZCwgMjAxOCIKCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgY3NzOiBwcmVzZW50YXRpb24uY3NzCi0tLQoKIyMgUHJvYmxlbSBEZXNjcmlwdGlvbgoKR2l2ZW4gODAwIGRhdGEgZnJvbSBgRGF0YVRyYWluX1R1Z2FzM19BSS5jc3ZgIHRoYXQgY29udGFpbnMgZml2ZSBhdHRyaWJ1dGVzICRYXzEkLCAkWF8yJCwgJFhfMyQsICRYXzQkLCBhbmQgJFhfNCQuCkl0IGFsc28gaGFzIGEgc2luZ2xlIG91dHB1dCB0aGF0IGNvbnRhaW5zIGZvdXIgbGFiZWxzIChuYW1lZCAkWSQpICQwJCwgJDEkLCAkMiQsIGFuZCAkMyQuCldlIG5lZWQgdG8gYnVpbGQgYSBjbGFzc2lmaWNhdGlvbiBzeXN0ZW0gd2l0aCBrLU5lYXJlc3QgTmVpZ2hib3JzIChrTk4pIGZvciBkZXRlcm1pbmluZyBhIGxhYmVsIGZyb20gZGF0YSB0ZXN0aW5nIGluIGBEYXRhVGVzdF9UdWdhczNfQUkuY3N2YC4KVGhlbiwgY3JlYXRlIGEgZmlsZSBgVGViYWthblR1Z2FzMy5jc3ZgIHdoaWNoIGlzIGNvbnRhaW5zIGEgc2luZ2xlIGNvbHVtbiBhYm91dCAyMDAgcm93cyBvZiBsYWJlbHMgKCQwJCwgJDEkLCAkMiQsIG9yICQzJCkuCgojIyBJbXBsZW1lbnRhdGlvbgoKU3VwcG9zZSB0aGF0IHNldmVyYWwgaGVscGVycyBmdW5jdGlvbiBhbHJlYWR5IGluY2x1ZGVkLgpTb3VyY2UgY29kZSBhdmFpbGFibGUgb24gW3dpc24va25uY3BwXShodHRwczovL2dpdGh1Yi5jb20vd2lzbi9rbm5jcHAvYmxvYi9tYXN0ZXIva25uLmNwcCkuCgojIyMgUHJlcGFyYXRpb24KCkZpcnN0LCBjb2xsZWN0IGFsbCBkYXRhIHRyYWluIGZyb20gYERhdGFUcmFpbl9UdWdhczNfQUkuY3N2YCBmaWxlLgoKYGBge2NwcH0KRGF0YSBkYXRhX3RyYWluID0gcmVhZENTVigiZGF0YS9EYXRhVHJhaW5fVHVnYXMzX0FJLmNzdiIpOwpgYGAKCiQkClxcCiQkCgpTZWNvbmQsIGNvbGxlY3QgYWxsIGRhdGEgdGVzdCBmcm9tIGBEYXRhVGVzdF9UdWdhczNfQUkuY3N2YCBmaWxlLgoKYGBge2NwcH0KRGF0YSBkYXRhX3Rlc3QgPSByZWFkQ1NWKCJkYXRhL0RhdGFUZXN0X1R1Z2FzM19BSS5jc3YiKTsKYGBgCgokJApcXAokJAoKTGFzdGx5LCBidWlsZCBhIGZ1bmN0aW9uIHRvIGNhbGN1bGF0ZSBkaXN0YW5jZSBiZXR3ZWVuIG9iamVjdC4KV2Ugd2lsbCB1c2luZyBFdWNsaWRlYW4gZGlzdGFuY2UuCgpgYGB7Y3BwfQovLyBJbmNsdWRlZCBpbiBrTk4gYWJzdHJhY3QgZGF0YSB0eXBlCiAgZG91YmxlIGRpc3RhbmNlKERhdHVtIGEsIERhdHVtIGIpIHsKICAgIGRvdWJsZSByZXQgPSAwOwogICAgcmV0ICs9IHBvdyhhLngxIC0gYi54MSwgMik7CiAgICByZXQgKz0gcG93KGEueDIgLSBiLngyLCAyKTsKICAgIHJldCArPSBwb3coYS54MyAtIGIueDMsIDIpOwogICAgcmV0ICs9IHBvdyhhLng0IC0gYi54NCwgMik7CiAgICByZXQgKz0gcG93KGEueDUgLSBiLng1LCAyKTsKCiAgICByZXR1cm4gc3FydChyZXQpOwogIH0KYGBgCgokJApcXAokJAoKIyMjIGtOTiBQcmVkaWN0aW9uCgpUaGlzIGlzIG91ciBtYWluIGZvY3VzLgpXZSBhcmUgdHJ5aW5nIHRvIHByZWRpY3QgdGhlIGNsYXNzaWZpY2F0aW9uIHJlc3VsdCBiYXNlZCBvbiBrTk4gYWxnb3JpdGhtIHdpdGggRXVjbGlkZWFuIGRpc3RhbmNlLgoKYGBge2NwcH0KLy8gSW5jbHVkZWQgaW4ga05OIGFic3RyYWN0IGRhdGEgdHlwZQogIGNoYXIgcHJlZGljdGlvbnMoRGF0dW0gdGVzdCkgewogICAgaW50IHRyYWluX3NpemUgPSB0aGlzLT50cmFpbi5zaXplKCk7CiAgICAKICAgIHZlY3RvcjxQb2ludD4gcG9pbnRzOwogICAgZm9yIChpbnQgaiA9IDA7IGogPCB0cmFpbl9zaXplOyBqKyspIHsKICAgICAgRGF0dW0gdHJhaW4gPSB0aGlzLT50cmFpbltqXTsKCiAgICAgIFBvaW50IHBvaW50OwogICAgICBwb2ludC5sYWJlbCA9IHRyYWluLnk7CiAgICAgIHBvaW50LmRpc3RhbmNlID0gdGhpcy0+ZGlzdGFuY2UodGVzdCwgdHJhaW4pOwoKICAgICAgcG9pbnRzLnB1c2hfYmFjayhwb2ludCk7CiAgICB9CgogICAgYXV0byBjb21wYXJhdG9yID0gW10oUG9pbnQgeCwgUG9pbnQgeSkgewogICAgICBpZiAoeC5kaXN0YW5jZSA9PSB5LmRpc3RhbmNlKQogICAgICAgIHJldHVybiB4LmxhYmVsIDwgeS5sYWJlbDsKCiAgICAgIHJldHVybiB4LmRpc3RhbmNlIDwgeS5kaXN0YW5jZTsKICAgIH07CgogICAgc29ydChwb2ludHMuYmVnaW4oKSwgcG9pbnRzLmVuZCgpLCBjb21wYXJhdG9yKTsKCiAgICBpbnQgbGFiZWxzWzRdOwogICAgZmlsbChsYWJlbHMsIGxhYmVscyArIDQsIDApOwogICAgZm9yIChpbnQgaSA9IDA7IGkgPCB0aGlzLT5rOyBpKyspIHsKICAgICAgbGFiZWxzW3BvaW50c1tpXS5sYWJlbCAtICcwJ10gKz0gMTsKICAgIH0KCiAgICBpbnQgbGFiZWwgPSAwOwogICAgaW50IGJlc3QgPSBsYWJlbHNbMF07CiAgICBmb3IgKGludCBpID0gMDsgaSA8IDQ7IGkrKykgewogICAgICBpZiAobGFiZWxzW2ldID4gYmVzdCkgewogICAgICAgIGJlc3QgPSBsYWJlbHNbaV07CiAgICAgICAgbGFiZWwgPSBpOwogICAgICB9CiAgICB9CgogICAgcmV0dXJuICcwJyArIGxhYmVsOwogIH0KYGBgCgokJApcXAokJAoKTm93LCB0cnkgdG8gZ2V0cyBzb21lIGRhdGEgYmFzZWQgb24gJEsgPSA1JCB2YWx1ZS4KCmBgYHtjcHB9CmNvdXQgPDwgJ1snOwoKa05OIGtubiAoZGF0YV90cmFpbiwgNSk7CmZvciAoaW50IGkgPSAwOyBpIDwgKGludCkgZGF0YV90ZXN0LnNpemUoKTsgaSsrKSB7CiAgaWYgKGkgPiAwKSBjb3V0IDw8ICIsICI7CgogIERhdHVtIHRlc3QgPSBkYXRhX3Rlc3RbaV07CiAgY291dCA8PCBrbm4ucHJlZGljdGlvbnModGVzdCk7Cn0KCmNvdXQgPDwgJ10nIDw8IGVuZGw7CmBgYAoKYGBgClsxLCAxLCAxLCAxLCAxLCAxLCAxLCAxLCAxLCAxLCAwLCAwLCAxLCAxLCAwLCAxLCAwLCAwLCAwLCAwLCAxLCAwLCAwLCAxLCAxLCAwLCAwLCAwLCAxLCAwLCAwLCAxLCAwLCAxLCAxLCAxLCAxLCAxLCAwLCAxLCAwLCAwLCAyLCAwLCAwLCAyLCAwLCAxLCAwLCAxLCAxLCAxLCAwLCAxLCAxLCAxLCAxLCAxLCAxLCAxLCAxLCAxLCAxLCAxLCAwLCAxLCAwLCAxLCAwLCAxLCAwLCAwLCAxLCAxLCAxLCAxLCAwLCAwLCAxLCAxLCAxLCAwLCAwLCAzLCAxLCAxLCAwLCAwLCAwLCAwLCAwLCAwLCAxLCAzLCAwLCAwLCAwLCAxLCAwLCAwLCAyLCAyLCAzLCAzLCAyLCAyLCAzLCAzLCAyLCAzLCAzLCAwLCAzLCAzLCAyLCAyLCAyLCAyLCAyLCAwLCAyLCAzLCAyLCAyLCAzLCAzLCAyLCAyLCAzLCAyLCAyLCAzLCAwLCAzLCAzLCAyLCAyLCAyLCAyLCAyLCAyLCAwLCAzLCAyLCAyLCAyLCAzLCAzLCAzLCAyLCAyLCAzLCAzLCAzLCAzLCAyLCAzLCAyLCAyLCAzLCAyLCAzLCAzLCAyLCAzLCAyLCAzLCAyLCAyLCAyLCAyLCAzLCAwLCAyLCAyLCAyLCAyLCAyLCAzLCAzLCAzLCAzLCAyLCAyLCAxLCAyLCAzLCAyLCAzLCAzLCAyLCAzLCAyLCAzLCAzLCAzLCAzLCAzLCAzLCAzXQpgYGAKCiQkClxcCiQkCgpPdXIga05OIHByZWRpY3Rpb24gZnVuY3Rpb24gd29ya3MganVzdCBhcyBleHBlY3RlZC4KSG93ZXZlciwgZG9lcyAkSyA9IDUkIHRoZSByaWdodCBhbnN3ZXI/CldlIGRvbid0IGtub3chCkhlbmNlLCB3ZSBuZWVkIHRvIGJ1aWxkIGEgdmFsaWRhdGlvbiBzZXQgdG8gZmluZCBhIGJldHRlciAkSyQgdmFsdWUuCkluIHRoaXMgY2FzZSwgbGV0J3MganVzdCB1c2Ugay1mb2xkIGNyb3NzIHZhbGlkYXRpb24gYW5kIGNhbGN1bGF0ZSB0aGUgYWNjdXJhY3kgYmFzZWQgb24gaXQuCldoeSBrLWZvbGQgY3Jvc3MgdmFsaWRhdGlvbj8KV2VsbCwgaXQganVzdCBoYXBwZW5lZCB0aGF0IEkgdW5kZXJzdGFuZCB0aGlzIG9uZS4KCiMjIyBrTk4gVmFsaWRhdGlvbgoKYGBge2NwcH0KRGF0YSB2YWxpZGF0aW9uX2RhdGEgPSByYW5kb21uaXplKGRhdGFfdHJhaW4pOwpEYXRhIHZhbGlkYXRpb25fdHJhaW4sIHZhbGlkYXRpb25fdGVzdDsKCmludCBuID0gZGF0YV90cmFpbi5zaXplKCk7CmludCBtID0gZGF0YV90ZXN0LnNpemUoKTsKZm9yIChpbnQgaSA9IDA7IGkgPCAobiAtIG0pOyBpKyspCiAgdmFsaWRhdGlvbl90cmFpbi5wdXNoX2JhY2sodmFsaWRhdGlvbl9kYXRhW2ldKTsKCmZvciAoaW50IGkgPSAobiAtIG0pOyBpIDwgbjsgaSsrKQogIHZhbGlkYXRpb25fdGVzdC5wdXNoX2JhY2sodmFsaWRhdGlvbl9kYXRhW2ldKTsKCmludCBtYXhfayA9IG07CnZlY3RvcjxwYWlyPGludCwgZG91YmxlPj4gYWNjdXJhY3kgKG1heF9rKTsKZm9yIChpbnQgayA9IDE7IGsgPD0gbWF4X2s7IGsrKykgewogIGtOTiBrbm4gKHZhbGlkYXRpb25fdHJhaW4sIGspOwogIGludCBtYXRjaCA9IDA7CgogIGZvciAoaW50IGkgPSAwOyBpIDwgbTsgaSsrKSB7CiAgICBEYXR1bSB0ZXN0ID0gdmFsaWRhdGlvbl90ZXN0W2ldOwogICAgaW50IHByZWRpY3Rpb25zID0ga25uLnByZWRpY3Rpb25zKHRlc3QpOwogICAgaWYgKHByZWRpY3Rpb25zID09IHRlc3QueSkKICAgICAgbWF0Y2ggKz0gMTsKICB9CgogIGFjY3VyYWN5W2sgLSAxXSA9IHtrLCAobWF0Y2ggLyAoZG91YmxlKSBtKSAqIDEwMH07Cn0KCmNvdXQgPDwgIkFjY3VyYWN5IGNhbGN1bGF0aW9uOiIgPDwgZW5kbDsKY291dCA8PCAnWyc7CmZvciAoaW50IGkgPSAwOyBpIDwgbWF4X2s7IGkrKykgewogIGlmIChpID4gMCkgY291dCA8PCAiLCAiOwoKICBjb3V0IDw8ICIoSyA9ICIgPDwgYWNjdXJhY3lbaV0uZmlyc3QgPDwgIiwgIiA8PCBhY2N1cmFjeVtpXS5zZWNvbmQgPDwgIiUpIjsKfQoKY291dCA8PCAnXScgPDwgZW5kbDsKYGBgCgpgYGAKQWNjdXJhY3kgY2FsY3VsYXRpb246ClsoSyA9IDEsIDgxJSksIChLID0gMiwgODAuNSUpLCAoSyA9IDMsIDgzJSksIChLID0gNCwgODIlKSwgKEsgPSA1LCA4NC41JSksIChLID0gNiwgODMlKSwgKEsgPSA3LCA4NiUpLCAoSyA9IDgsIDg0LjUlKSwgKEsgPSA5LCA4NS41JSksIChLID0gMTAsIDg1LjUlKSwgKEsgPSAxMSwgODYlKSwgKEsgPSAxMiwgODUuNSUpLCAoSyA9IDEzLCA4Ni41JSksIChLID0gMTQsIDg2LjUlKSwgKEsgPSAxNSwgODcuNSUpLCAoSyA9IDE2LCA4Ni41JSksIChLID0gMTcsIDg2JSksIChLID0gMTgsIDg1LjUlKSwgKEsgPSAxOSwgODUlKSwgKEsgPSAyMCwgODUuNSUpLCAoSyA9IDIxLCA4NiUpLCAoSyA9IDIyLCA4NS41JSksIChLID0gMjMsIDg1JSksIChLID0gMjQsIDgzLjUlKSwgKEsgPSAyNSwgODMlKSwgKEsgPSAyNiwgODMuNSUpLCAoSyA9IDI3LCA4NCUpLCAoSyA9IDI4LCA4MyUpLCAoSyA9IDI5LCA4NCUpLCAoSyA9IDMwLCA4NSUpLCAoSyA9IDMxLCA4NCUpLCAoSyA9IDMyLCA4NSUpLCAoSyA9IDMzLCA4NCUpLCAoSyA9IDM0LCA4NCUpLCAoSyA9IDM1LCA4My41JSksIChLID0gMzYsIDgzLjUlKSwgKEsgPSAzNywgODIuNSUpLCAoSyA9IDM4LCA4NCUpLCAoSyA9IDM5LCA4NC41JSksIChLID0gNDAsIDg0JSksIChLID0gNDEsIDgzLjUlKSwgKEsgPSA0MiwgODQlKSwgKEsgPSA0MywgODQlKSwgKEsgPSA0NCwgODQlKSwgKEsgPSA0NSwgODUlKSwgKEsgPSA0NiwgODUlKSwgKEsgPSA0NywgODUlKSwgKEsgPSA0OCwgODQlKSwgKEsgPSA0OSwgODQlKSwgKEsgPSA1MCwgODMuNSUpLCAoSyA9IDUxLCA4My41JSksIChLID0gNTIsIDgzLjUlKSwgKEsgPSA1MywgODMuNSUpLCAoSyA9IDU0LCA4My41JSksIChLID0gNTUsIDgzJSksIChLID0gNTYsIDgyLjUlKSwgKEsgPSA1NywgODIuNSUpLCAoSyA9IDU4LCA4Mi41JSksIChLID0gNTksIDgzJSksIChLID0gNjAsIDgyLjUlKSwgKEsgPSA2MSwgODQlKSwgKEsgPSA2MiwgODIuNSUpLCAoSyA9IDYzLCA4Mi41JSksIChLID0gNjQsIDgyLjUlKSwgKEsgPSA2NSwgODEuNSUpLCAoSyA9IDY2LCA4MS41JSksIChLID0gNjcsIDgwLjUlKSwgKEsgPSA2OCwgODElKSwgKEsgPSA2OSwgODElKSwgKEsgPSA3MCwgODElKSwgKEsgPSA3MSwgODElKSwgKEsgPSA3MiwgODElKSwgKEsgPSA3MywgODElKSwgKEsgPSA3NCwgODIlKSwgKEsgPSA3NSwgODElKSwgKEsgPSA3NiwgODEuNSUpLCAoSyA9IDc3LCA4MS41JSksIChLID0gNzgsIDgyJSksIChLID0gNzksIDgyJSksIChLID0gODAsIDgyLjUlKSwgKEsgPSA4MSwgODEuNSUpLCAoSyA9IDgyLCA4MiUpLCAoSyA9IDgzLCA4MyUpLCAoSyA9IDg0LCA4MiUpLCAoSyA9IDg1LCA4Mi41JSksIChLID0gODYsIDgzJSksIChLID0gODcsIDgyLjUlKSwgKEsgPSA4OCwgODIlKSwgKEsgPSA4OSwgODIlKSwgKEsgPSA5MCwgODIuNSUpLCAoSyA9IDkxLCA4MiUpLCAoSyA9IDkyLCA4Mi41JSksIChLID0gOTMsIDgxLjUlKSwgKEsgPSA5NCwgODElKSwgKEsgPSA5NSwgODEuNSUpLCAoSyA9IDk2LCA4MC41JSksIChLID0gOTcsIDgxJSksIChLID0gOTgsIDgxJSksIChLID0gOTksIDgxJSksIChLID0gMTAwLCA4MSUpLCAoSyA9IDEwMSwgODElKSwgKEsgPSAxMDIsIDgwJSksIChLID0gMTAzLCA4MC41JSksIChLID0gMTA0LCA4MSUpLCAoSyA9IDEwNSwgODAuNSUpLCAoSyA9IDEwNiwgODAlKSwgKEsgPSAxMDcsIDgwJSksIChLID0gMTA4LCA4MCUpLCAoSyA9IDEwOSwgNzkuNSUpLCAoSyA9IDExMCwgNzkuNSUpLCAoSyA9IDExMSwgODAlKSwgKEsgPSAxMTIsIDgwJSksIChLID0gMTEzLCA4MC41JSksIChLID0gMTE0LCA4MC41JSksIChLID0gMTE1LCA4MCUpLCAoSyA9IDExNiwgNzklKSwgKEsgPSAxMTcsIDc5LjUlKSwgKEsgPSAxMTgsIDc4LjUlKSwgKEsgPSAxMTksIDc4LjUlKSwgKEsgPSAxMjAsIDc5JSksIChLID0gMTIxLCA3OSUpLCAoSyA9IDEyMiwgNzklKSwgKEsgPSAxMjMsIDc5JSksIChLID0gMTI0LCA3OC41JSksIChLID0gMTI1LCA3OSUpLCAoSyA9IDEyNiwgNzglKSwgKEsgPSAxMjcsIDc5JSksIChLID0gMTI4LCA3OS41JSksIChLID0gMTI5LCA3OS41JSksIChLID0gMTMwLCA3OSUpLCAoSyA9IDEzMSwgNzklKSwgKEsgPSAxMzIsIDc4JSksIChLID0gMTMzLCA3OC41JSksIChLID0gMTM0LCA3OC41JSksIChLID0gMTM1LCA3OC41JSksIChLID0gMTM2LCA3OC41JSksIChLID0gMTM3LCA3OC41JSksIChLID0gMTM4LCA3OC41JSksIChLID0gMTM5LCA3OC41JSksIChLID0gMTQwLCA3OCUpLCAoSyA9IDE0MSwgNzclKSwgKEsgPSAxNDIsIDc2LjUlKSwgKEsgPSAxNDMsIDc2LjUlKSwgKEsgPSAxNDQsIDc2LjUlKSwgKEsgPSAxNDUsIDc3JSksIChLID0gMTQ2LCA3Ni41JSksIChLID0gMTQ3LCA3Ni41JSksIChLID0gMTQ4LCA3NiUpLCAoSyA9IDE0OSwgNzYlKSwgKEsgPSAxNTAsIDc1LjUlKSwgKEsgPSAxNTEsIDc1JSksIChLID0gMTUyLCA3NSUpLCAoSyA9IDE1MywgNzUuNSUpLCAoSyA9IDE1NCwgNzUuNSUpLCAoSyA9IDE1NSwgNzUuNSUpLCAoSyA9IDE1NiwgNzQuNSUpLCAoSyA9IDE1NywgNzUlKSwgKEsgPSAxNTgsIDc0LjUlKSwgKEsgPSAxNTksIDc1JSksIChLID0gMTYwLCA3NC41JSksIChLID0gMTYxLCA3NC41JSksIChLID0gMTYyLCA3MyUpLCAoSyA9IDE2MywgNzMlKSwgKEsgPSAxNjQsIDczJSksIChLID0gMTY1LCA3MyUpLCAoSyA9IDE2NiwgNzIlKSwgKEsgPSAxNjcsIDcyJSksIChLID0gMTY4LCA3MiUpLCAoSyA9IDE2OSwgNzIuNSUpLCAoSyA9IDE3MCwgNzEuNSUpLCAoSyA9IDE3MSwgNzElKSwgKEsgPSAxNzIsIDcxJSksIChLID0gMTczLCA3MSUpLCAoSyA9IDE3NCwgNzAuNSUpLCAoSyA9IDE3NSwgNzAuNSUpLCAoSyA9IDE3NiwgNzAlKSwgKEsgPSAxNzcsIDY5JSksIChLID0gMTc4LCA2OS41JSksIChLID0gMTc5LCA2OSUpLCAoSyA9IDE4MCwgNjklKSwgKEsgPSAxODEsIDY5JSksIChLID0gMTgyLCA2OC41JSksIChLID0gMTgzLCA2OSUpLCAoSyA9IDE4NCwgNjklKSwgKEsgPSAxODUsIDY3LjUlKSwgKEsgPSAxODYsIDY3LjUlKSwgKEsgPSAxODcsIDY2LjUlKSwgKEsgPSAxODgsIDY1LjUlKSwgKEsgPSAxODksIDY0JSksIChLID0gMTkwLCA2My41JSksIChLID0gMTkxLCA2NCUpLCAoSyA9IDE5MiwgNjIlKSwgKEsgPSAxOTMsIDYyLjUlKSwgKEsgPSAxOTQsIDYxLjUlKSwgKEsgPSAxOTUsIDYxJSksIChLID0gMTk2LCA2MC41JSksIChLID0gMTk3LCA2MC41JSksIChLID0gMTk4LCA1OS41JSksIChLID0gMTk5LCA1OCUpLCAoSyA9IDIwMCwgNTglKV0KYGBgCgokJApcXAokJAoKTm93LCBiYXNlZCBvbiB0aGUgYWNjdXJhY3kgdGFibGUgYWJvdmUsIHdlIGNvdWxkIGRldGVybWluZSB3aGljaCAkSyQgdmFsdWUgaXMgYmV0dGVyLgoKYGBge2NwcH0KaW50IGN1cnJlbnRfayA9IGFjY3VyYWN5WzBdLmZpcnN0Owpkb3VibGUgY3VycmVudF9hY2N1cmFjeSA9IGFjY3VyYWN5WzBdLnNlY29uZDsKZm9yIChpbnQgaSA9IDA7IGkgPCBtYXhfazsgaSsrKSB7CiAgaWYgKGFjY3VyYWN5W2ldLnNlY29uZCA+IGN1cnJlbnRfYWNjdXJhY3kpIHsKICAgIGN1cnJlbnRfYWNjdXJhY3kgPSBhY2N1cmFjeVtpXS5zZWNvbmQ7CiAgICBjdXJyZW50X2sgPSBhY2N1cmFjeVtpXS5maXJzdDsKICB9Cn0KCksgPSBjdXJyZW50X2s7CmNvdXQgPDwgSyA8PCAiIHdpdGggIiA8PCBjdXJyZW50X2FjY3VyYWN5IDw8ICIgcGVyY2VudGlsZS4iIDw8IGVuZGw7CmBgYAoKYGBgCksgPSAxNSB3aXRoIDg3LjUgcGVyY2VudGlsZS4KYGBgCgokJApcXAokJAoKIyMjIGtOTiBTb2x1dGlvbgoKV2Ugbm93IGFscmVhZHkgcmV0cmlldmVkIHRoZSBiZXN0ICRLJCB2YWx1ZS4KQXBwbHkgaXQgdG8gdGhlIGtOTiBwcmVkaWN0aW9ucyBmdW5jdGlvbi4KCmBgYHtjcHB9CnZlY3RvcjxjaGFyPiBsYWJlbHM7CmtOTiBrbm4gKGRhdGFfdHJhaW4sIEspOwppbnQgbiA9IGRhdGFfdGVzdC5zaXplKCk7CmZvciAoaW50IGkgPSAwOyBpIDwgbjsgaSsrKSB7CiAgRGF0dW0gdGVzdCA9IGRhdGFfdGVzdFtpXTsKICBsYWJlbHMucHVzaF9iYWNrKGtubi5wcmVkaWN0aW9ucyh0ZXN0KSk7Cn0KCmNvdXQgPDwgIlJldHJpZXZlZCBzb2x1dGlvbjoiIDw8IGVuZGw7CmNvdXQgPDwgJ1snOwoKZm9yIChpbnQgaSA9IDA7IGkgPCBuOyBpKyspIHsKICBpZiAoaSA+IDApIGNvdXQgPDwgIiwgIjsKCiAgY291dCA8PCBsYWJlbHNbaV07Cn0KCmNvdXQgPDwgJ10nIDw8IGVuZGw7CmBgYAoKYGBgClJldHJpZXZlZCBzb2x1dGlvbjoKWzEsIDEsIDEsIDEsIDEsIDEsIDEsIDEsIDEsIDEsIDEsIDAsIDEsIDEsIDAsIDEsIDAsIDAsIDAsIDAsIDEsIDAsIDAsIDEsIDEsIDAsIDAsIDAsIDEsIDAsIDAsIDEsIDAsIDEsIDEsIDEsIDEsIDEsIDAsIDEsIDAsIDAsIDIsIDAsIDAsIDAsIDAsIDEsIDAsIDEsIDEsIDEsIDAsIDEsIDEsIDMsIDEsIDEsIDEsIDEsIDEsIDEsIDEsIDEsIDAsIDEsIDAsIDEsIDMsIDEsIDAsIDAsIDEsIDEsIDAsIDEsIDIsIDAsIDEsIDEsIDEsIDAsIDAsIDEsIDEsIDEsIDAsIDAsIDAsIDAsIDAsIDAsIDEsIDEsIDAsIDAsIDAsIDEsIDAsIDAsIDIsIDIsIDMsIDMsIDIsIDIsIDMsIDMsIDIsIDMsIDMsIDAsIDMsIDMsIDIsIDIsIDIsIDIsIDIsIDIsIDAsIDMsIDIsIDIsIDMsIDMsIDIsIDIsIDMsIDIsIDIsIDMsIDIsIDMsIDMsIDIsIDIsIDIsIDIsIDIsIDIsIDAsIDMsIDIsIDIsIDIsIDMsIDMsIDMsIDIsIDIsIDMsIDMsIDMsIDMsIDIsIDEsIDAsIDIsIDMsIDIsIDMsIDMsIDIsIDMsIDIsIDMsIDIsIDIsIDIsIDIsIDMsIDAsIDIsIDIsIDIsIDIsIDIsIDMsIDMsIDMsIDMsIDIsIDIsIDIsIDIsIDMsIDIsIDMsIDMsIDIsIDMsIDIsIDMsIDMsIDMsIDMsIDMsIDMsIDNdCmBgYAoKJCQKXFwKJCQKClN0b3JlIHRoZSBzb2x1dGlvbiB0byBgVGViYWthblR1Z2FzMy5jc3ZgIGFuZCB3ZSBhcmUgYWxsIHNldCEK