

Email : joseph.hermawan@matanauniversity.ac.id
RPubs
: https://rpubs.com/JoHndes9
Department : Business
Statistics
Address : ARA Center, Matana University
Tower
Jl. CBD Barat Kav, RT.1, Curug
Sangereng, Kelapa Dua, Tangerang, Banten 15810.
Pendahuluan
Klasifikasi adalah proses pengelompokan objek atau data ke dalam
kategori atau kelas berdasarkan ciri-ciri atau atribut tertentu. Metode
klasifikasi merupakan algoritma atau teknik yang digunakan untuk
melakukan proses klasifikasi tersebut.
Keterangan umum terkait metode Klasifikasi adalah sebagai
berikut:
- K-Nearest Neighbors (K-NN): Metode ini
mengklasifikasikan objek berdasarkan mayoritas kelas tetangga
terdekatnya. Jika suatu objek memiliki sebagian besar tetangga dari
suatu kelas tertentu, maka objek tersebut diklasifikasikan ke dalam
kelas tersebut.
- Decision Trees: Decision trees adalah model
prediktif yang menggunakan struktur pohon keputusan untuk melakukan
klasifikasi. Pada setiap simpul pohon, keputusan dibuat berdasarkan
nilai dari suatu atribut.
- Naive Bayes: Metode klasifikasi ini berdasarkan
teorema Bayes dengan asumsi independensi antara setiap pasangan atribut.
Meskipun sederhana, Naive Bayes sering kali efektif dan cepat, terutama
untuk dataset dengan jumlah atribut yang besar.
- Support Vector Machines (SVM): SVM mencari
hyperplane terbaik yang memisahkan dua kelas dalam ruang atribut. Tujuan
utamanya adalah untuk mencari hyperplane yang memiliki margin terbesar
antara dua kelas.
- Random Forest: Random Forest adalah teknik ensemble
learning yang menggabungkan banyak decision trees ke dalam satu model.
Setiap decision tree dibangun secara independen, dan hasil klasifikasi
akhir diperoleh dengan mengambil mayoritas suara dari semua pohon.
- Neural Networks: Jaringan saraf tiruan (Neural
Networks) adalah model komputasi yang terdiri dari neuron-neuron yang
saling terhubung. Dalam konteks klasifikasi, neural networks dapat
digunakan sebagai metode yang sangat kuat untuk mempelajari pola-pola
yang kompleks dari data.
- Logistic Regression: Logistic regression adalah
metode statistik yang digunakan untuk klasifikasi biner, di mana model
berusaha memprediksi probabilitas masuk ke dalam salah satu dari dua
kelas.
- Gradient Boosting Machines (GBM): Menggabungkan
beberapa model lemah (misalnya, decision trees) secara bertahap untuk
meningkatkan kinerja.
- AdaBoost: Membuat model yang berurutan, di mana
setiap model mencoba untuk memperbaiki kesalahan yang dilakukan oleh
model sebelumnyaGradient Boosting Machines (GBM): Menggabungkan beberapa
model lemah (misalnya, decision trees) secara bertahap untuk
meningkatkan kinerja.
- Ensemble Methods: Ensemble methods menggabungkan
prediksi dari beberapa model klasifikasi untuk meningkatkan kinerja
prediksi. Contoh lain dari ensemble methods adalah Boosting dan
Bagging.
Setiap metode klasifikasi memiliki kelebihan dan kelemahan tertentu
tergantung pada sifat data yang dihadapi dan tujuan dari klasifikasi
tersebut. Pemilihan metode klasifikasi yang tepat memerlukan pemahaman
yang baik tentang data yang digunakan serta eksperimen untuk menentukan
metode yang paling cocok untuk kasus tertentu.
Catatan: Matakuliah Pengantar Sains Data ini, fokus
membahas K-Nearest Neighbors (K-NN) dan Decision Trees
Saja.
K-Nearest Neighbors
(K-NN)
K-Nearest Neighbors (K-NN) adalah salah satu metode klasifikasi yang
sederhana dan intuitif. Metode ini beroperasi dengan prinsip bahwa objek
yang memiliki atribut serupa cenderung berada dalam kelas yang sama.
Proses Kerja KNN
K-NN melakukan klasifikasi dengan mencari sejumlah K tetangga
terdekat dari data yang akan diprediksi dan menentukan mayoritas kelas
dari tetangga tersebut.

Prosesnya dapat dijelaskan sebagai berikut:
- Menentukan titik awal berdasarkan jumlah klasifikasi yang ada.
- Mengukur jarak: K-NN mengukur jarak antara data yang akan diprediksi
dengan semua data latih.
- Memilih K tetangga terdekat: K-NN memilih K tetangga terdekat
berdasarkan jarak yang diukur.
- Menentukan mayoritas kelas: K-NN menentukan mayoritas kelas dari K
tetangga terdekat dan mengklasifikasikan data yang akan diprediksi ke
dalam kelas tersebut.
Penggunaan KNN
Berikut adalah contoh penggunaan K-NN dalam R:
library(class) # untuk model Klasifikasi
# Contoh data
data <- data.frame(x1 = c(1, 2, 3, 4, 5), x2 = c(2, 3, 4, 5, 6), class = c(0, 0, 1, 1, 1))
# Training data
train_data <- data[, -3]
# Target class
target_class <- data[, 3]
# Prediksi menggunakan K-NN dengan K = 3
knn_pred <- knn(train_data, train_data, target_class, k = 3)
# Print hasil prediksi
print(knn_pred)
## [1] 0 0 1 1 1
## Levels: 0 1
Evaluasi Model
Evaluasi model klasifikasi adalah langkah penting dalam pengembangan
model untuk memastikan kinerja yang baik dalam memprediksi kelas dari
data baru. Dalam evaluasi model klasifikasi, terdapat beberapa metrik
evaluasi yang umum digunakan adalah dengan Matriks kebingungan
menampilkan jumlah prediksi yang benar dan yang salah untuk setiap
kelas.
- Akurasi (Accuracy):
- Akurasi mengukur seberapa sering model memberikan prediksi yang
benar dari total prediksi yang dilakukan.
- Formula: Akurasi = (TP + TN) / (TP + TN + FP + FN)
- Presisi (Precision):
- Presisi mengukur seberapa sering prediksi positif dari model adalah
benar.
- Formula: Presisi = TP / (TP + FP)
- Recall (Sensitivity atau True Positive Rate):
- Recall mengukur seberapa sering model berhasil mendeteksi kelas
positif.
- Formula: Recall = TP / (TP + FN)
- F1-Score:
- F1-score adalah rata-rata harmonik dari presisi dan recall. Ini
memberikan keseimbangan antara kedua metrik ini.
- Formula: F1-score = 2 * (Presisi * Recall) / (Presisi + Recall)
Keterangan:
- TP: True Positives
- TN: True Negatives
- FP: False Positives
- FN: False Negatives
library(caret) # Evaluasi Model
# Menghitung akurasi
accuracy <- sum(knn_pred == target_class) / length(target_class)
# Menghitung presisi
precision <- sum(knn_pred == 1 & target_class == 1) / sum(knn_pred == 1)
# Menghitung recall
recall <- sum(knn_pred == 1 & target_class == 1) / sum(target_class == 1)
# Menghitung F1-score
f1_score <- 2 * (precision * recall) / (precision + recall)
# Menampilkan hasil evaluasi
print(paste("Akurasi:", accuracy))
## [1] "Akurasi: 1"
print(paste("Presisi:", precision))
## [1] "Presisi: 1"
print(paste("Recall:", recall))
## [1] "Recall: 1"
print(paste("F1-score:", f1_score))
## [1] "F1-score: 1"
Berikut ini adalah cara lain untuk melakukan evaluasi model dengan
R.
# Matriks kebingungan
confusion_matrix <- confusionMatrix(knn_pred, as.factor(target_class))
# Akurasi
accuracy <- confusion_matrix$overall['Accuracy']
# Presisi
precision <- confusion_matrix$byClass['Pos Pred Value']
# Recall
recall <- confusion_matrix$byClass['Sensitivity']
# F1-Score
f1_score <- confusion_matrix$byClass['F1']
# Menampilkan hasil evaluasi
print(paste("Akurasi:", accuracy))
## [1] "Akurasi: 1"
print(paste("Presisi:", precision))
## [1] "Presisi: 1"
print(paste("Recall:", recall))
## [1] "Recall: 1"
print(paste("F1-Score:", f1_score))
## [1] "F1-Score: 1"
Latihan 1
Gunakan dataset “Iris” yang tersedia dalam paket
datasets di R. Dataset ini berisi informasi tentang
atribut-atribut bunga Iris (panjang dan lebar sepal dan petal) serta
label kelas (jenis spesies bunga Iris). Lakukan proses klasifikasi
dengan menggunakan KNN!
Pelatihan
Model
library(class)
data("iris") #memasukan data iris
training_data=iris[,-5] #membuat data untuk melatih model
target=iris[,5] #membuat data target
prediksi_knn=knn(training_data,training_data,target,k=5) #membuat model knn
print(prediksi_knn)
## [1] setosa setosa setosa setosa setosa setosa
## [7] setosa setosa setosa setosa setosa setosa
## [13] setosa setosa setosa setosa setosa setosa
## [19] setosa setosa setosa setosa setosa setosa
## [25] setosa setosa setosa setosa setosa setosa
## [31] setosa setosa setosa setosa setosa setosa
## [37] setosa setosa setosa setosa setosa setosa
## [43] setosa setosa setosa setosa setosa setosa
## [49] setosa setosa versicolor versicolor versicolor versicolor
## [55] versicolor versicolor versicolor versicolor versicolor versicolor
## [61] versicolor versicolor versicolor versicolor versicolor versicolor
## [67] versicolor versicolor versicolor versicolor virginica versicolor
## [73] virginica versicolor versicolor versicolor versicolor versicolor
## [79] versicolor versicolor versicolor versicolor versicolor virginica
## [85] versicolor versicolor versicolor versicolor versicolor versicolor
## [91] versicolor versicolor versicolor versicolor versicolor versicolor
## [97] versicolor versicolor versicolor versicolor virginica virginica
## [103] virginica virginica virginica virginica versicolor virginica
## [109] virginica virginica virginica virginica virginica virginica
## [115] virginica virginica virginica virginica virginica versicolor
## [121] virginica virginica virginica virginica virginica virginica
## [127] virginica virginica virginica virginica virginica virginica
## [133] virginica virginica virginica virginica virginica virginica
## [139] virginica virginica virginica virginica virginica virginica
## [145] virginica virginica virginica virginica virginica virginica
## Levels: setosa versicolor virginica
Evaluasi Model
library(caret)
akurasi= sum(prediksi_knn==target)/length(target) #menghitung akurasi model
paste("Akurasi:",akurasi)
## [1] "Akurasi: 0.966666666666667"
presisi=(sum(prediksi_knn =="setosa" & target=="setosa")+sum(prediksi_knn =="versicolor" & target=="versicolor")+
sum(prediksi_knn =="virginica" & target=="virginica"))/(sum(prediksi_knn =="setosa")+sum(prediksi_knn =="versicolor")+sum(prediksi_knn =="virginica")) #menghitung presisi model
paste("Presisi:",presisi)
## [1] "Presisi: 0.966666666666667"
recal=(sum(prediksi_knn =="setosa" & target=="setosa")+sum(prediksi_knn =="versicolor" & target=="versicolor")+
sum(prediksi_knn =="virginica" & target=="virginica"))/ (sum(target=="setosa")+sum(target=="versicolor")+sum(target=="virginica")) #menghitung recall model
paste("recall:",recal)
## [1] "recall: 0.966666666666667"
f1=(2*(presisi*recal))/(presisi+recal) #menghitung F-1 score model
paste("F-1 Score;",f1)
## [1] "F-1 Score; 0.966666666666667"
Decision Trees
Decision Trees (Pohon Keputusan) adalah model pembelajaran mesin yang
sangat populer untuk tugas klasifikasi dan regresi. Mereka menghasilkan
model yang mudah dipahami dan mudah diinterpretasikan, mirip dengan
logika manusia.

Proses Kerja
Decision Trees
Decision Trees (Pohon Keputusan) adalah algoritma pembelajaran mesin
yang menggunakan struktur pohon berhierarki untuk melakukan prediksi. Di
bawah ini adalah langkah-langkah umum tentang cara kerja algoritma
Decision Trees:
Pemilihan Fitur Algoritma Decision Trees memilih fitur mana yang
akan digunakan untuk membagi data menjadi subgrup yang lebih kecil.
Pemilihan fitur ini dilakukan berdasarkan kriteria tertentu seperti Gini
Impurity atau Information Gain.
Pembagian Data Setelah fitur dipilih, algoritma membagi data
menjadi dua atau lebih subgrup berdasarkan nilai fitur yang dipilih.
Setiap subgrup akan mewakili cabang-cabang dari pohon
keputusan.
Pengulangan Proses pembagian data di atas diulangi pada setiap
cabang pohon secara rekursif hingga satu atau lebih kondisi berhenti
terpenuhi, seperti mencapai jumlah data minimum dalam satu cabang atau
kedalaman maksimum pohon.
Penentuan Label Setelah pembagian data selesai, algoritma
menentukan label (kelas atau nilai) untuk setiap daun pohon berdasarkan
mayoritas kelas atau nilai dalam setiap subgrup.
Pruning (Pemangkasan) Beberapa algoritma Decision Trees dapat
melakukan pruning setelah pembangunan pohon untuk mencegah overfitting,
yaitu dengan menghapus cabang-cabang yang tidak signifikan.
Berikut adalah beberapa konsep penting yang digunakan dalam algoritma
Decision Trees:
- Gini Impurity: Metrik yang mengukur seberapa
seragam sebuah himpunan sampel dari kelas tertentu.
- Information Gain: Metrik yang mengukur penurunan
ketidakpastian setelah pemisahan himpunan sampel.
- Entropy: Konsep dari teori informasi yang digunakan
untuk menghitung Information Gain.
Penggunaan Decision
Trees
Kita akan menggunakan dataset “Iris” yang tersedia dalam paket
datasets di R. Dataset ini berisi informasi tentang
atribut-atribut bunga Iris (panjang dan lebar sepal dan petal) serta
label kelas (jenis spesies bunga Iris).
Persiapan Data
Pertama-tama, kita perlu memuat dataset dan mempersiapkan data:
# Load dataset
data(iris)
# Lihat struktur dataset
str(iris)
## 'data.frame': 150 obs. of 5 variables:
## $ Sepal.Length: num 5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
## $ Sepal.Width : num 3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
## $ Petal.Length: num 1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
## $ Petal.Width : num 0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
## $ Species : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...
Pelatihan
Model
Selanjutnya, kita melatih model klasifikasi menggunakan algoritma
Decision Trees:
# Pisahkan fitur dan target
X <- iris[, 1:4]
y <- iris[, 5]
# Pelatihan model Decision Trees
library(rpart)
tree_model <- rpart(y ~ ., data = X, method = "class")
Evaluasi Model
Setelah melatih model, kita dapat mengevaluasi kinerjanya dengan
menggunakan dataset yang sama:
# Prediksi kelas
prediksi <- predict(tree_model, X, type = "class")
# Hitung akurasi
akurasi <- sum(prediksi == y) / length(y)
print(paste("Akurasi model Decision Trees:", akurasi))
## [1] "Akurasi model Decision Trees: 0.96"
presisiom=(sum(prediksi =="setosa" & y=="setosa")+sum(prediksi =="versicolor" & y=="versicolor")+
sum(prediksi =="virginica" & y=="virginica"))/(sum(prediksi =="setosa")+sum(prediksi =="versicolor")+sum(prediksi =="virginica")) #menghitung presisi model
paste("Presisi model Decision Trees:",presisiom)
## [1] "Presisi model Decision Trees: 0.96"
recell=(sum(prediksi =="setosa" & y=="setosa")+sum(prediksi =="versicolor" & y=="versicolor")+
sum(prediksi =="virginica" & y=="virginica"))/(sum(y =="setosa")+sum(y =="versicolor")+sum(y =="virginica")) #menghitung recall model
paste("recall model Decision Trees:",recell)
## [1] "recall model Decision Trees: 0.96"
f_1=(2*(presisiom*recell))/(presisiom+recell) #menghitung F-1 score model
paste("F-1 Scoremodel Decision Trees:",f_1)
## [1] "F-1 Scoremodel Decision Trees: 0.96"
Visualisasi
Model
Kita juga dapat memvisualisasikan model Decision Trees yang telah
kita latih
# Load library untuk plotting
library(rpart.plot)
# Plot Decision Trees
rpart.plot(tree_model)

Interpretasi
Hasil
Berdasarkan hasil evaluasi, kita dapat mengevaluasi seberapa baik
model kita dalam memprediksi jenis spesies bunga Iris berdasarkan
atribut-atribut yang ada dalam dataset. Selain itu, kita juga dapat
menginterpretasi struktur pohon keputusan untuk memahami bagaimana model
membuat keputusan
LS0tDQp0aXRsZTogIlBlbmdhbnRhciBEYXRhIFNhaW5zIg0Kc3VidGl0bGU6ICJNZXRvZGUgS2xhc2lmaWthc2kiDQphdXRob3I6ICJKb3NlcGggRmlsaXVzIEhlcm1hd2FuIg0KZGF0ZTogImByIGZvcm1hdChTeXMuRGF0ZSgpLCAnJUIgJWQsICVZJylgIg0Kb3V0cHV0OiANCiAgaHRtbF9kb2N1bWVudDogDQogICAgaHRtbF9kb2N1bWVudDogbnVsbA0KICAgIGNvZGVfZm9sZGluZzogaGlkZQ0KICAgIGNvZGVfZG93bmxvYWQ6IHllcw0KICAgIHRvYzogeWVzDQogICAgdG9jX2Zsb2F0Og0KICAgICAgY29sbGFwc2VkOiB5ZXMNCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcw0KICAgIHRoZW1lOiBzYW5kc3RvbmUNCiAgICBjc3M6IHN0eWxlLmNzcw0KICAgIGhpZ2hsaWdodDogbW9ub2Nocm9tZQ0KLS0tDQoNCmBgYHtyIGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoY2xhc3Muc291cmNlID0gIm5vY29weSIsDQogICAgICAgICAgICAgICAgICAgICAgY2xhc3Mub3V0cHV0ID0gIm5vY29weSIsDQogICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZSA9IEYsDQogICAgICAgICAgICAgICAgICAgICAgd2FybmluZyA9IEYpDQpgYGANCg0KPGltZyBzdHlsZT0iZmxvYXQ6IHJpZ2h0OyBtYXJnaW46IDBweCAxMDBweCAwcHggMHB4OyB3aWR0aDoyNSUiIHNyYz0iaW1hZ2VzL21lLnBuZyIvPiANCg0KYGBge3IgbWUsIGVjaG89RkFMU0UsZmlnLmFsaWduPSdjZW50ZXInLCBvdXQud2lkdGggPSAnMzAlJ30NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJpbWFnZXMvbG9nby5wbmciKQ0KYGBgDQoNCkVtYWlsICZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyAmbmJzcDsgJm5ic3A7ICZuYnNwOyZuYnNwOzogIGpvc2VwaC5oZXJtYXdhbkBtYXRhbmF1bml2ZXJzaXR5LmFjLmlkIDxicj4NClJQdWJzICAmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsgJm5ic3A7ICZuYnNwOyAmbmJzcDs6IGh0dHBzOi8vcnB1YnMuY29tL0pvSG5kZXM5IDxicj4NCkRlcGFydG1lbnQgJm5ic3A7OiBbQnVzaW5lc3MgU3RhdGlzdGljc10oaHR0cHM6Ly9tYXRhbmF1bml2ZXJzaXR5LmFjLmlkLz9seT1hY2FkZW1pYyZjPXNiKSA8YnI+DQpBZGRyZXNzICAmbmJzcDsgJm5ic3A7ICZuYnNwOyAmbmJzcDsgOiBBUkEgQ2VudGVyLCBNYXRhbmEgVW5pdmVyc2l0eSBUb3dlciA8YnI+DQombmJzcDsgJm5ic3A7ICZuYnNwOyAmbmJzcDsgJm5ic3A7ICZuYnNwOyAmbmJzcDsgJm5ic3A7ICZuYnNwOyAmbmJzcDsgJm5ic3A7ICZuYnNwOyZuYnNwOyBKbC4gQ0JEIEJhcmF0IEthdiwgUlQuMSwgQ3VydWcgU2FuZ2VyZW5nLCBLZWxhcGEgRHVhLCBUYW5nZXJhbmcsIEJhbnRlbiAxNTgxMC4NCjxicj4NCg0KKioqDQoNCiMgUGVuZGFodWx1YW4NCg0KS2xhc2lmaWthc2kgYWRhbGFoIHByb3NlcyBwZW5nZWxvbXBva2FuIG9iamVrIGF0YXUgZGF0YSBrZSBkYWxhbSBrYXRlZ29yaSBhdGF1IGtlbGFzIGJlcmRhc2Fya2FuIGNpcmktY2lyaSBhdGF1IGF0cmlidXQgdGVydGVudHUuIE1ldG9kZSBrbGFzaWZpa2FzaSBtZXJ1cGFrYW4gYWxnb3JpdG1hIGF0YXUgdGVrbmlrIHlhbmcgZGlndW5ha2FuIHVudHVrIG1lbGFrdWthbiBwcm9zZXMga2xhc2lmaWthc2kgdGVyc2VidXQuIA0KDQoNCmBgYHtyIGVjaG89RkFMU0V9DQpsaWJyYXJ5KERpYWdyYW1tZVIpDQpsaWJyYXJ5KERpYWdyYW1tZVJzdmcpDQoNCiMgQ3JlYXRlIGEgbmV3IERpYWdyYW1tZVJzdmcgb2JqZWN0IHdpdGggY3VzdG9taXplZCBzdHlsZQ0KbWluZG1hcCA8LSBnclZpeigNCiAgImRpZ3JhcGggew0KICAgIGdyYXBoIFtsYXlvdXQgPSBkb3QsIGZvbnRzaXplID0gMjYsIHNwbGluZXMgPSB0cnVlLCBvdmVybGFwID0gZmFsc2UsIHNlcCA9IDAuMiwgYmdjb2xvciA9IHRyYW5zcGFyZW50LCBtYXJnaW4gPSAwLjMsIHJhbmtkaXIgPSBUQl0NCiAgICBub2RlIFtzaGFwZSA9IHJlY3RhbmdsZSwgc3R5bGUgPSBmaWxsZWQsIGZpbGxjb2xvciA9ICcjRkZDQzY2JywgZm9udHNpemUgPSAyNl0NCiAgICBlZGdlIFtjb2xvciA9ICcjNzc3Nzc3J10NCiAgICBNZXRvZGVLbGFzaWZpa2FzaSBbbGFiZWwgPSAnTWV0b2RlIEtsYXNpZmlrYXNpJywgZm9udHNpemUgPSAzMCwgd2lkdGggPSAxLCBoZWlnaHQgPSAxLjIsIHNoYXBlID0gYm94LCBmaWxsY29sb3IgPSAnIzk5Q0NGRiddDQogICAgS05OOyBEZWNpc2lvblRyZWVzOyBSYW5kb21Gb3Jlc3Q7IFNWTTsgTmFpdmVCYXllczsgTmV1cmFsTmV0d29ya3M7IExvZ2lzdGljUmVncmVzc2lvbjsgR0JNOyBBZGFCb29zdDsgRW5zZW1ibGVMZWFybmluZyBbZmlsbGNvbG9yID0gJyM2NkNDOTknXQ0KDQogICAgTWV0b2RlS2xhc2lmaWthc2kgLT4gew0KICAgICAgS05OOyBEZWNpc2lvblRyZWVzOyBSYW5kb21Gb3Jlc3Q7IFNWTTsgTmFpdmVCYXllczsgTmV1cmFsTmV0d29ya3M7IExvZ2lzdGljUmVncmVzc2lvbjsgR0JNOyBBZGFCb29zdDsgRW5zZW1ibGVMZWFybmluZw0KICAgIH0NCiAgfSIsDQogIGhlaWdodCA9IDIwMCwgd2lkdGggPSA4MDAgICMgTWVuZW50dWthbiB0aW5nZ2kgZGFuIGxlYmFyIGdhbWJhcg0KKQ0KDQojIERpc3BsYXkgdGhlIG1pbmRtYXANCm1pbmRtYXANCmBgYA0KDQpLZXRlcmFuZ2FuIHVtdW0gdGVya2FpdCBtZXRvZGUgS2xhc2lmaWthc2kgYWRhbGFoIHNlYmFnYWkgYmVyaWt1dDoNCg0KKiAqKkstTmVhcmVzdCBOZWlnaGJvcnMgKEstTk4pOioqIE1ldG9kZSBpbmkgbWVuZ2tsYXNpZmlrYXNpa2FuIG9iamVrIGJlcmRhc2Fya2FuIG1heW9yaXRhcyBrZWxhcyB0ZXRhbmdnYSB0ZXJkZWthdG55YS4gSmlrYSBzdWF0dSBvYmplayBtZW1pbGlraSBzZWJhZ2lhbiBiZXNhciB0ZXRhbmdnYSBkYXJpIHN1YXR1IGtlbGFzIHRlcnRlbnR1LCBtYWthIG9iamVrIHRlcnNlYnV0IGRpa2xhc2lmaWthc2lrYW4ga2UgZGFsYW0ga2VsYXMgdGVyc2VidXQuDQoqICoqRGVjaXNpb24gVHJlZXM6KiogRGVjaXNpb24gdHJlZXMgYWRhbGFoIG1vZGVsIHByZWRpa3RpZiB5YW5nIG1lbmdndW5ha2FuIHN0cnVrdHVyIHBvaG9uIGtlcHV0dXNhbiB1bnR1ayBtZWxha3VrYW4ga2xhc2lmaWthc2kuIFBhZGEgc2V0aWFwIHNpbXB1bCBwb2hvbiwga2VwdXR1c2FuIGRpYnVhdCBiZXJkYXNhcmthbiBuaWxhaSBkYXJpIHN1YXR1IGF0cmlidXQuDQoqICoqTmFpdmUgQmF5ZXM6KiogTWV0b2RlIGtsYXNpZmlrYXNpIGluaSBiZXJkYXNhcmthbiB0ZW9yZW1hIEJheWVzIGRlbmdhbiBhc3Vtc2kgaW5kZXBlbmRlbnNpIGFudGFyYSBzZXRpYXAgcGFzYW5nYW4gYXRyaWJ1dC4gTWVza2lwdW4gc2VkZXJoYW5hLCBOYWl2ZSBCYXllcyBzZXJpbmcga2FsaSBlZmVrdGlmIGRhbiBjZXBhdCwgdGVydXRhbWEgdW50dWsgZGF0YXNldCBkZW5nYW4ganVtbGFoIGF0cmlidXQgeWFuZyBiZXNhci4NCiogKipTdXBwb3J0IFZlY3RvciBNYWNoaW5lcyAoU1ZNKToqKiBTVk0gbWVuY2FyaSBoeXBlcnBsYW5lIHRlcmJhaWsgeWFuZyBtZW1pc2Foa2FuIGR1YSBrZWxhcyBkYWxhbSBydWFuZyBhdHJpYnV0LiBUdWp1YW4gdXRhbWFueWEgYWRhbGFoIHVudHVrIG1lbmNhcmkgaHlwZXJwbGFuZSB5YW5nIG1lbWlsaWtpIG1hcmdpbiB0ZXJiZXNhciBhbnRhcmEgZHVhIGtlbGFzLg0KKiAqKlJhbmRvbSBGb3Jlc3Q6KiogUmFuZG9tIEZvcmVzdCBhZGFsYWggdGVrbmlrIGVuc2VtYmxlIGxlYXJuaW5nIHlhbmcgbWVuZ2dhYnVuZ2thbiBiYW55YWsgZGVjaXNpb24gdHJlZXMga2UgZGFsYW0gc2F0dSBtb2RlbC4gU2V0aWFwIGRlY2lzaW9uIHRyZWUgZGliYW5ndW4gc2VjYXJhIGluZGVwZW5kZW4sIGRhbiBoYXNpbCBrbGFzaWZpa2FzaSBha2hpciBkaXBlcm9sZWggZGVuZ2FuIG1lbmdhbWJpbCBtYXlvcml0YXMgc3VhcmEgZGFyaSBzZW11YSBwb2hvbi4NCiogKipOZXVyYWwgTmV0d29ya3M6KiogSmFyaW5nYW4gc2FyYWYgdGlydWFuIChOZXVyYWwgTmV0d29ya3MpIGFkYWxhaCBtb2RlbCBrb21wdXRhc2kgeWFuZyB0ZXJkaXJpIGRhcmkgbmV1cm9uLW5ldXJvbiB5YW5nIHNhbGluZyB0ZXJodWJ1bmcuIERhbGFtIGtvbnRla3Mga2xhc2lmaWthc2ksIG5ldXJhbCBuZXR3b3JrcyBkYXBhdCBkaWd1bmFrYW4gc2ViYWdhaSBtZXRvZGUgeWFuZyBzYW5nYXQga3VhdCB1bnR1ayBtZW1wZWxhamFyaSBwb2xhLXBvbGEgeWFuZyBrb21wbGVrcyBkYXJpIGRhdGEuDQoqICoqTG9naXN0aWMgUmVncmVzc2lvbjoqKiBMb2dpc3RpYyByZWdyZXNzaW9uIGFkYWxhaCBtZXRvZGUgc3RhdGlzdGlrIHlhbmcgZGlndW5ha2FuIHVudHVrIGtsYXNpZmlrYXNpIGJpbmVyLCBkaSBtYW5hIG1vZGVsIGJlcnVzYWhhIG1lbXByZWRpa3NpIHByb2JhYmlsaXRhcyBtYXN1ayBrZSBkYWxhbSBzYWxhaCBzYXR1IGRhcmkgZHVhIGtlbGFzLg0KKiAqKkdyYWRpZW50IEJvb3N0aW5nIE1hY2hpbmVzIChHQk0pOioqIE1lbmdnYWJ1bmdrYW4gYmViZXJhcGEgbW9kZWwgbGVtYWggKG1pc2FsbnlhLCBkZWNpc2lvbiB0cmVlcykgc2VjYXJhIGJlcnRhaGFwIHVudHVrIG1lbmluZ2thdGthbiBraW5lcmphLg0KKiAqKkFkYUJvb3N0OioqIE1lbWJ1YXQgbW9kZWwgeWFuZyBiZXJ1cnV0YW4sIGRpIG1hbmEgc2V0aWFwIG1vZGVsIG1lbmNvYmEgdW50dWsgbWVtcGVyYmFpa2kga2VzYWxhaGFuIHlhbmcgZGlsYWt1a2FuIG9sZWggbW9kZWwgc2ViZWx1bW55YUdyYWRpZW50IEJvb3N0aW5nIE1hY2hpbmVzIChHQk0pOiBNZW5nZ2FidW5na2FuIGJlYmVyYXBhIG1vZGVsIGxlbWFoIChtaXNhbG55YSwgZGVjaXNpb24gdHJlZXMpIHNlY2FyYSBiZXJ0YWhhcCB1bnR1ayBtZW5pbmdrYXRrYW4ga2luZXJqYS4NCiogKipFbnNlbWJsZSBNZXRob2RzOioqIEVuc2VtYmxlIG1ldGhvZHMgbWVuZ2dhYnVuZ2thbiBwcmVkaWtzaSBkYXJpIGJlYmVyYXBhIG1vZGVsIGtsYXNpZmlrYXNpIHVudHVrIG1lbmluZ2thdGthbiBraW5lcmphIHByZWRpa3NpLiBDb250b2ggbGFpbiBkYXJpIGVuc2VtYmxlIG1ldGhvZHMgYWRhbGFoIEJvb3N0aW5nIGRhbiBCYWdnaW5nLg0KDQpTZXRpYXAgbWV0b2RlIGtsYXNpZmlrYXNpIG1lbWlsaWtpIGtlbGViaWhhbiBkYW4ga2VsZW1haGFuIHRlcnRlbnR1IHRlcmdhbnR1bmcgcGFkYSBzaWZhdCBkYXRhIHlhbmcgZGloYWRhcGkgZGFuIHR1anVhbiBkYXJpIGtsYXNpZmlrYXNpIHRlcnNlYnV0LiBQZW1pbGloYW4gbWV0b2RlIGtsYXNpZmlrYXNpIHlhbmcgdGVwYXQgbWVtZXJsdWthbiBwZW1haGFtYW4geWFuZyBiYWlrIHRlbnRhbmcgZGF0YSB5YW5nIGRpZ3VuYWthbiBzZXJ0YSBla3NwZXJpbWVuIHVudHVrIG1lbmVudHVrYW4gbWV0b2RlIHlhbmcgcGFsaW5nIGNvY29rIHVudHVrIGthc3VzIHRlcnRlbnR1Lg0KDQoqKkNhdGF0YW46KiogTWF0YWt1bGlhaCBQZW5nYW50YXIgU2FpbnMgRGF0YSBpbmksIGZva3VzIG1lbWJhaGFzICpLLU5lYXJlc3QgTmVpZ2hib3JzIChLLU5OKSogZGFuICpEZWNpc2lvbiBUcmVlcyogU2FqYS4NCg0KIyBLLU5lYXJlc3QgTmVpZ2hib3JzIChLLU5OKQ0KDQpLLU5lYXJlc3QgTmVpZ2hib3JzIChLLU5OKSBhZGFsYWggc2FsYWggc2F0dSBtZXRvZGUga2xhc2lmaWthc2kgeWFuZyBzZWRlcmhhbmEgZGFuIGludHVpdGlmLiBNZXRvZGUgaW5pIGJlcm9wZXJhc2kgZGVuZ2FuIHByaW5zaXAgYmFod2Egb2JqZWsgeWFuZyBtZW1pbGlraSBhdHJpYnV0IHNlcnVwYSBjZW5kZXJ1bmcgYmVyYWRhIGRhbGFtIGtlbGFzIHlhbmcgc2FtYS4gDQoNCiMjIFByb3NlcyBLZXJqYSBLTk4NCg0KSy1OTiBtZWxha3VrYW4ga2xhc2lmaWthc2kgZGVuZ2FuIG1lbmNhcmkgc2VqdW1sYWggSyB0ZXRhbmdnYSB0ZXJkZWthdCBkYXJpIGRhdGEgeWFuZyBha2FuIGRpcHJlZGlrc2kgZGFuIG1lbmVudHVrYW4gbWF5b3JpdGFzIGtlbGFzIGRhcmkgdGV0YW5nZ2EgdGVyc2VidXQuIA0KDQpgYGB7ciBLbm4sIGVjaG89RkFMU0UsZmlnLmFsaWduPSdjZW50ZXInLCBvdXQud2lkdGggPSAnMTAwJSd9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiaW1hZ2VzL0tOTl9BbGdvcml0aG0uZ2lmIikNCmBgYA0KDQpQcm9zZXNueWEgZGFwYXQgZGlqZWxhc2thbiBzZWJhZ2FpIGJlcmlrdXQ6DQoNCjEuIE1lbmVudHVrYW4gdGl0aWsgYXdhbCBiZXJkYXNhcmthbiBqdW1sYWgga2xhc2lmaWthc2kgeWFuZyBhZGEuDQoyLiBNZW5ndWt1ciBqYXJhazogSy1OTiBtZW5ndWt1ciBqYXJhayBhbnRhcmEgZGF0YSB5YW5nIGFrYW4gZGlwcmVkaWtzaSBkZW5nYW4gc2VtdWEgZGF0YSBsYXRpaC4NCjEuIE1lbWlsaWggSyB0ZXRhbmdnYSB0ZXJkZWthdDogSy1OTiBtZW1pbGloIEsgdGV0YW5nZ2EgdGVyZGVrYXQgYmVyZGFzYXJrYW4gamFyYWsgeWFuZyBkaXVrdXIuDQozLiBNZW5lbnR1a2FuIG1heW9yaXRhcyBrZWxhczogSy1OTiBtZW5lbnR1a2FuIG1heW9yaXRhcyBrZWxhcyBkYXJpIEsgdGV0YW5nZ2EgdGVyZGVrYXQgZGFuIG1lbmdrbGFzaWZpa2FzaWthbiBkYXRhIHlhbmcgYWthbiBkaXByZWRpa3NpIGtlIGRhbGFtIGtlbGFzIHRlcnNlYnV0Lg0KDQojIyBQZW5nZ3VuYWFuIEtOTg0KDQpCZXJpa3V0IGFkYWxhaCBjb250b2ggcGVuZ2d1bmFhbiBLLU5OIGRhbGFtIFI6DQoNCmBgYHtyfQ0KbGlicmFyeShjbGFzcykgICAgICAjIHVudHVrIG1vZGVsIEtsYXNpZmlrYXNpDQoNCiMgQ29udG9oIGRhdGENCmRhdGEgPC0gZGF0YS5mcmFtZSh4MSA9IGMoMSwgMiwgMywgNCwgNSksIHgyID0gYygyLCAzLCA0LCA1LCA2KSwgY2xhc3MgPSBjKDAsIDAsIDEsIDEsIDEpKQ0KDQojIFRyYWluaW5nIGRhdGENCnRyYWluX2RhdGEgPC0gZGF0YVssIC0zXQ0KDQojIFRhcmdldCBjbGFzcw0KdGFyZ2V0X2NsYXNzIDwtIGRhdGFbLCAzXQ0KDQojIFByZWRpa3NpIG1lbmdndW5ha2FuIEstTk4gZGVuZ2FuIEsgPSAzDQprbm5fcHJlZCA8LSBrbm4odHJhaW5fZGF0YSwgdHJhaW5fZGF0YSwgdGFyZ2V0X2NsYXNzLCBrID0gMykNCg0KIyBQcmludCBoYXNpbCBwcmVkaWtzaQ0KcHJpbnQoa25uX3ByZWQpDQpgYGANCg0KIyMgRXZhbHVhc2kgTW9kZWwNCg0KRXZhbHVhc2kgbW9kZWwga2xhc2lmaWthc2kgYWRhbGFoIGxhbmdrYWggcGVudGluZyBkYWxhbSBwZW5nZW1iYW5nYW4gbW9kZWwgdW50dWsgbWVtYXN0aWthbiBraW5lcmphIHlhbmcgYmFpayBkYWxhbSBtZW1wcmVkaWtzaSBrZWxhcyBkYXJpIGRhdGEgYmFydS4gRGFsYW0gZXZhbHVhc2kgbW9kZWwga2xhc2lmaWthc2ksIHRlcmRhcGF0IGJlYmVyYXBhIG1ldHJpayBldmFsdWFzaSB5YW5nIHVtdW0gZGlndW5ha2FuIGFkYWxhaCBkZW5nYW4gTWF0cmlrcyBrZWJpbmd1bmdhbiBtZW5hbXBpbGthbiBqdW1sYWggcHJlZGlrc2kgeWFuZyBiZW5hciBkYW4geWFuZyBzYWxhaCB1bnR1ayBzZXRpYXAga2VsYXMuDQoNCjEuICoqQWt1cmFzaSAoQWNjdXJhY3kpKio6DQogICAtIEFrdXJhc2kgbWVuZ3VrdXIgc2ViZXJhcGEgc2VyaW5nIG1vZGVsIG1lbWJlcmlrYW4gcHJlZGlrc2kgeWFuZyBiZW5hciBkYXJpIHRvdGFsIHByZWRpa3NpIHlhbmcgZGlsYWt1a2FuLg0KICAgLSBGb3JtdWxhOiBBa3VyYXNpID0gKFRQICsgVE4pIC8gKFRQICsgVE4gKyBGUCArIEZOKQ0KDQoyLiAqKlByZXNpc2kgKFByZWNpc2lvbikqKjoNCiAgIC0gUHJlc2lzaSBtZW5ndWt1ciBzZWJlcmFwYSBzZXJpbmcgcHJlZGlrc2kgcG9zaXRpZiBkYXJpIG1vZGVsIGFkYWxhaCBiZW5hci4NCiAgIC0gRm9ybXVsYTogUHJlc2lzaSA9IFRQIC8gKFRQICsgRlApDQoNCjMuICoqUmVjYWxsIChTZW5zaXRpdml0eSBhdGF1IFRydWUgUG9zaXRpdmUgUmF0ZSkqKjoNCiAgIC0gUmVjYWxsIG1lbmd1a3VyIHNlYmVyYXBhIHNlcmluZyBtb2RlbCBiZXJoYXNpbCBtZW5kZXRla3NpIGtlbGFzIHBvc2l0aWYuDQogICAtIEZvcm11bGE6IFJlY2FsbCA9IFRQIC8gKFRQICsgRk4pDQoNCjQuICoqRjEtU2NvcmUqKjoNCiAgIC0gRjEtc2NvcmUgYWRhbGFoIHJhdGEtcmF0YSBoYXJtb25payBkYXJpIHByZXNpc2kgZGFuIHJlY2FsbC4gSW5pIG1lbWJlcmlrYW4ga2VzZWltYmFuZ2FuIGFudGFyYSBrZWR1YSBtZXRyaWsgaW5pLg0KICAgLSBGb3JtdWxhOiBGMS1zY29yZSA9IDIgKiAoUHJlc2lzaSAqIFJlY2FsbCkgLyAoUHJlc2lzaSArIFJlY2FsbCkNCg0KKipLZXRlcmFuZ2FuOioqDQoNCi0gVFA6IFRydWUgUG9zaXRpdmVzDQotIFROOiBUcnVlIE5lZ2F0aXZlcw0KLSBGUDogRmFsc2UgUG9zaXRpdmVzDQotIEZOOiBGYWxzZSBOZWdhdGl2ZXMNCg0KDQpgYGB7cn0NCmxpYnJhcnkoY2FyZXQpICAgICAgIyBFdmFsdWFzaSBNb2RlbA0KDQojIE1lbmdoaXR1bmcgYWt1cmFzaQ0KYWNjdXJhY3kgPC0gc3VtKGtubl9wcmVkID09IHRhcmdldF9jbGFzcykgLyBsZW5ndGgodGFyZ2V0X2NsYXNzKQ0KDQojIE1lbmdoaXR1bmcgcHJlc2lzaQ0KcHJlY2lzaW9uIDwtIHN1bShrbm5fcHJlZCA9PSAxICYgdGFyZ2V0X2NsYXNzID09IDEpIC8gc3VtKGtubl9wcmVkID09IDEpDQoNCiMgTWVuZ2hpdHVuZyByZWNhbGwNCnJlY2FsbCA8LSBzdW0oa25uX3ByZWQgPT0gMSAmIHRhcmdldF9jbGFzcyA9PSAxKSAvIHN1bSh0YXJnZXRfY2xhc3MgPT0gMSkNCg0KIyBNZW5naGl0dW5nIEYxLXNjb3JlDQpmMV9zY29yZSA8LSAyICogKHByZWNpc2lvbiAqIHJlY2FsbCkgLyAocHJlY2lzaW9uICsgcmVjYWxsKQ0KDQojIE1lbmFtcGlsa2FuIGhhc2lsIGV2YWx1YXNpDQpwcmludChwYXN0ZSgiQWt1cmFzaToiLCBhY2N1cmFjeSkpDQpwcmludChwYXN0ZSgiUHJlc2lzaToiLCBwcmVjaXNpb24pKQ0KcHJpbnQocGFzdGUoIlJlY2FsbDoiLCByZWNhbGwpKQ0KcHJpbnQocGFzdGUoIkYxLXNjb3JlOiIsIGYxX3Njb3JlKSkNCmBgYA0KDQpCZXJpa3V0IGluaSBhZGFsYWggY2FyYSBsYWluIHVudHVrIG1lbGFrdWthbiBldmFsdWFzaSBtb2RlbCBkZW5nYW4gUi4NCg0KYGBge3J9DQojIE1hdHJpa3Mga2ViaW5ndW5nYW4NCmNvbmZ1c2lvbl9tYXRyaXggPC0gY29uZnVzaW9uTWF0cml4KGtubl9wcmVkLCBhcy5mYWN0b3IodGFyZ2V0X2NsYXNzKSkNCg0KIyBBa3VyYXNpDQphY2N1cmFjeSA8LSBjb25mdXNpb25fbWF0cml4JG92ZXJhbGxbJ0FjY3VyYWN5J10NCg0KIyBQcmVzaXNpDQpwcmVjaXNpb24gPC0gY29uZnVzaW9uX21hdHJpeCRieUNsYXNzWydQb3MgUHJlZCBWYWx1ZSddDQoNCiMgUmVjYWxsDQpyZWNhbGwgPC0gY29uZnVzaW9uX21hdHJpeCRieUNsYXNzWydTZW5zaXRpdml0eSddDQoNCiMgRjEtU2NvcmUNCmYxX3Njb3JlIDwtIGNvbmZ1c2lvbl9tYXRyaXgkYnlDbGFzc1snRjEnXQ0KDQojIE1lbmFtcGlsa2FuIGhhc2lsIGV2YWx1YXNpDQpwcmludChwYXN0ZSgiQWt1cmFzaToiLCBhY2N1cmFjeSkpDQpwcmludChwYXN0ZSgiUHJlc2lzaToiLCBwcmVjaXNpb24pKQ0KcHJpbnQocGFzdGUoIlJlY2FsbDoiLCByZWNhbGwpKQ0KcHJpbnQocGFzdGUoIkYxLVNjb3JlOiIsIGYxX3Njb3JlKSkNCmBgYA0KDQojIyBMYXRpaGFuIDENCg0KR3VuYWthbiBkYXRhc2V0ICJJcmlzIiB5YW5nIHRlcnNlZGlhIGRhbGFtIHBha2V0IGBkYXRhc2V0c2AgZGkgUi4gRGF0YXNldCBpbmkgYmVyaXNpIGluZm9ybWFzaSB0ZW50YW5nIGF0cmlidXQtYXRyaWJ1dCBidW5nYSBJcmlzIChwYW5qYW5nIGRhbiBsZWJhciBzZXBhbCBkYW4gcGV0YWwpIHNlcnRhIGxhYmVsIGtlbGFzIChqZW5pcyBzcGVzaWVzIGJ1bmdhIElyaXMpLiBMYWt1a2FuIHByb3NlcyBrbGFzaWZpa2FzaSBkZW5nYW4gbWVuZ2d1bmFrYW4gS05OIQ0KDQojIyMgUGVsYXRpaGFuIE1vZGVsDQoNCmBgYHtyfQ0KbGlicmFyeShjbGFzcykNCg0KDQpkYXRhKCJpcmlzIikgI21lbWFzdWthbiBkYXRhIGlyaXMNCg0KdHJhaW5pbmdfZGF0YT1pcmlzWywtNV0gI21lbWJ1YXQgZGF0YSB1bnR1ayBtZWxhdGloIG1vZGVsDQoNCnRhcmdldD1pcmlzWyw1XSAjbWVtYnVhdCBkYXRhIHRhcmdldA0KDQpwcmVkaWtzaV9rbm49a25uKHRyYWluaW5nX2RhdGEsdHJhaW5pbmdfZGF0YSx0YXJnZXQsaz01KSAjbWVtYnVhdCBtb2RlbCBrbm4NCnByaW50KHByZWRpa3NpX2tubikNCmBgYA0KDQojIyMgRXZhbHVhc2kgTW9kZWwNCg0KYGBge3J9DQpsaWJyYXJ5KGNhcmV0KQ0KDQpha3VyYXNpPSBzdW0ocHJlZGlrc2lfa25uPT10YXJnZXQpL2xlbmd0aCh0YXJnZXQpICNtZW5naGl0dW5nIGFrdXJhc2kgbW9kZWwNCnBhc3RlKCJBa3VyYXNpOiIsYWt1cmFzaSkNCg0KcHJlc2lzaT0oc3VtKHByZWRpa3NpX2tubiA9PSJzZXRvc2EiICYgdGFyZ2V0PT0ic2V0b3NhIikrc3VtKHByZWRpa3NpX2tubiA9PSJ2ZXJzaWNvbG9yIiAmIHRhcmdldD09InZlcnNpY29sb3IiKSsNCiAgICAgICAgICBzdW0ocHJlZGlrc2lfa25uID09InZpcmdpbmljYSIgJiB0YXJnZXQ9PSJ2aXJnaW5pY2EiKSkvKHN1bShwcmVkaWtzaV9rbm4gPT0ic2V0b3NhIikrc3VtKHByZWRpa3NpX2tubiA9PSJ2ZXJzaWNvbG9yIikrc3VtKHByZWRpa3NpX2tubiA9PSJ2aXJnaW5pY2EiKSkgI21lbmdoaXR1bmcgcHJlc2lzaSBtb2RlbA0KcGFzdGUoIlByZXNpc2k6IixwcmVzaXNpKQ0KDQpyZWNhbD0oc3VtKHByZWRpa3NpX2tubiA9PSJzZXRvc2EiICYgdGFyZ2V0PT0ic2V0b3NhIikrc3VtKHByZWRpa3NpX2tubiA9PSJ2ZXJzaWNvbG9yIiAmIHRhcmdldD09InZlcnNpY29sb3IiKSsNCiAgICAgICAgICBzdW0ocHJlZGlrc2lfa25uID09InZpcmdpbmljYSIgJiB0YXJnZXQ9PSJ2aXJnaW5pY2EiKSkvIChzdW0odGFyZ2V0PT0ic2V0b3NhIikrc3VtKHRhcmdldD09InZlcnNpY29sb3IiKStzdW0odGFyZ2V0PT0idmlyZ2luaWNhIikpICNtZW5naGl0dW5nIHJlY2FsbCBtb2RlbA0KcGFzdGUoInJlY2FsbDoiLHJlY2FsKSANCg0KZjE9KDIqKHByZXNpc2kqcmVjYWwpKS8ocHJlc2lzaStyZWNhbCkgI21lbmdoaXR1bmcgRi0xIHNjb3JlIG1vZGVsDQpwYXN0ZSgiRi0xIFNjb3JlOyIsZjEpDQpgYGANCg0KDQojIERlY2lzaW9uIFRyZWVzDQoNCkRlY2lzaW9uIFRyZWVzIChQb2hvbiBLZXB1dHVzYW4pIGFkYWxhaCBtb2RlbCBwZW1iZWxhamFyYW4gbWVzaW4geWFuZyBzYW5nYXQgcG9wdWxlciB1bnR1ayB0dWdhcyBrbGFzaWZpa2FzaSBkYW4gcmVncmVzaS4gTWVyZWthIG1lbmdoYXNpbGthbiBtb2RlbCB5YW5nIG11ZGFoIGRpcGFoYW1pIGRhbiBtdWRhaCBkaWludGVycHJldGFzaWthbiwgbWlyaXAgZGVuZ2FuIGxvZ2lrYSBtYW51c2lhLg0KDQpgYGB7ciBkZWNpc2lvbl90cmVlLCBlY2hvPUZBTFNFLGZpZy5hbGlnbj0nY2VudGVyJywgb3V0LndpZHRoID0gJzEwMCUnfQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoImltYWdlcy9EZWNpc2lvbl9UcmVlLmdpZiIpDQpgYGANCg0KIyMgUHJvc2VzIEtlcmphICpEZWNpc2lvbiBUcmVlcyoNCg0KRGVjaXNpb24gVHJlZXMgKFBvaG9uIEtlcHV0dXNhbikgYWRhbGFoIGFsZ29yaXRtYSBwZW1iZWxhamFyYW4gbWVzaW4geWFuZyBtZW5nZ3VuYWthbiBzdHJ1a3R1ciBwb2hvbiBiZXJoaWVyYXJraSB1bnR1ayBtZWxha3VrYW4gcHJlZGlrc2kuIERpIGJhd2FoIGluaSBhZGFsYWggbGFuZ2thaC1sYW5na2FoIHVtdW0gdGVudGFuZyBjYXJhIGtlcmphIGFsZ29yaXRtYSBEZWNpc2lvbiBUcmVlczoNCg0KMS4gUGVtaWxpaGFuIEZpdHVyDQpBbGdvcml0bWEgRGVjaXNpb24gVHJlZXMgbWVtaWxpaCBmaXR1ciBtYW5hIHlhbmcgYWthbiBkaWd1bmFrYW4gdW50dWsgbWVtYmFnaSBkYXRhIG1lbmphZGkgc3ViZ3J1cCB5YW5nIGxlYmloIGtlY2lsLiBQZW1pbGloYW4gZml0dXIgaW5pIGRpbGFrdWthbiBiZXJkYXNhcmthbiBrcml0ZXJpYSB0ZXJ0ZW50dSBzZXBlcnRpIEdpbmkgSW1wdXJpdHkgYXRhdSBJbmZvcm1hdGlvbiBHYWluLg0KDQoyLiBQZW1iYWdpYW4gRGF0YQ0KU2V0ZWxhaCBmaXR1ciBkaXBpbGloLCBhbGdvcml0bWEgbWVtYmFnaSBkYXRhIG1lbmphZGkgZHVhIGF0YXUgbGViaWggc3ViZ3J1cCBiZXJkYXNhcmthbiBuaWxhaSBmaXR1ciB5YW5nIGRpcGlsaWguIFNldGlhcCBzdWJncnVwIGFrYW4gbWV3YWtpbGkgY2FiYW5nLWNhYmFuZyBkYXJpIHBvaG9uIGtlcHV0dXNhbi4NCg0KMy4gUGVuZ3VsYW5nYW4NClByb3NlcyBwZW1iYWdpYW4gZGF0YSBkaSBhdGFzIGRpdWxhbmdpIHBhZGEgc2V0aWFwIGNhYmFuZyBwb2hvbiBzZWNhcmEgcmVrdXJzaWYgaGluZ2dhIHNhdHUgYXRhdSBsZWJpaCBrb25kaXNpIGJlcmhlbnRpIHRlcnBlbnVoaSwgc2VwZXJ0aSBtZW5jYXBhaSBqdW1sYWggZGF0YSBtaW5pbXVtIGRhbGFtIHNhdHUgY2FiYW5nIGF0YXUga2VkYWxhbWFuIG1ha3NpbXVtIHBvaG9uLg0KDQo0LiBQZW5lbnR1YW4gTGFiZWwNClNldGVsYWggcGVtYmFnaWFuIGRhdGEgc2VsZXNhaSwgYWxnb3JpdG1hIG1lbmVudHVrYW4gbGFiZWwgKGtlbGFzIGF0YXUgbmlsYWkpIHVudHVrIHNldGlhcCBkYXVuIHBvaG9uIGJlcmRhc2Fya2FuIG1heW9yaXRhcyBrZWxhcyBhdGF1IG5pbGFpIGRhbGFtIHNldGlhcCBzdWJncnVwLg0KDQo1LiBQcnVuaW5nIChQZW1hbmdrYXNhbikNCkJlYmVyYXBhIGFsZ29yaXRtYSBEZWNpc2lvbiBUcmVlcyBkYXBhdCBtZWxha3VrYW4gcHJ1bmluZyBzZXRlbGFoIHBlbWJhbmd1bmFuIHBvaG9uIHVudHVrIG1lbmNlZ2FoIG92ZXJmaXR0aW5nLCB5YWl0dSBkZW5nYW4gbWVuZ2hhcHVzIGNhYmFuZy1jYWJhbmcgeWFuZyB0aWRhayBzaWduaWZpa2FuLg0KDQpCZXJpa3V0IGFkYWxhaCBiZWJlcmFwYSBrb25zZXAgcGVudGluZyB5YW5nIGRpZ3VuYWthbiBkYWxhbSBhbGdvcml0bWEgRGVjaXNpb24gVHJlZXM6DQoNCi0gKipHaW5pIEltcHVyaXR5Kio6IE1ldHJpayB5YW5nIG1lbmd1a3VyIHNlYmVyYXBhIHNlcmFnYW0gc2VidWFoIGhpbXB1bmFuIHNhbXBlbCBkYXJpIGtlbGFzIHRlcnRlbnR1Lg0KLSAqKkluZm9ybWF0aW9uIEdhaW4qKjogTWV0cmlrIHlhbmcgbWVuZ3VrdXIgcGVudXJ1bmFuIGtldGlkYWtwYXN0aWFuIHNldGVsYWggcGVtaXNhaGFuIGhpbXB1bmFuIHNhbXBlbC4NCi0gKipFbnRyb3B5Kio6IEtvbnNlcCBkYXJpIHRlb3JpIGluZm9ybWFzaSB5YW5nIGRpZ3VuYWthbiB1bnR1ayBtZW5naGl0dW5nIEluZm9ybWF0aW9uIEdhaW4uDQoNCiMjIFBlbmdndW5hYW4gRGVjaXNpb24gVHJlZXMNCg0KS2l0YSBha2FuIG1lbmdndW5ha2FuIGRhdGFzZXQgIklyaXMiIHlhbmcgdGVyc2VkaWEgZGFsYW0gcGFrZXQgYGRhdGFzZXRzYCBkaSBSLiBEYXRhc2V0IGluaSBiZXJpc2kgaW5mb3JtYXNpIHRlbnRhbmcgYXRyaWJ1dC1hdHJpYnV0IGJ1bmdhIElyaXMgKHBhbmphbmcgZGFuIGxlYmFyIHNlcGFsIGRhbiBwZXRhbCkgc2VydGEgbGFiZWwga2VsYXMgKGplbmlzIHNwZXNpZXMgYnVuZ2EgSXJpcykuDQoNCiMjIyBQZXJzaWFwYW4gRGF0YQ0KDQpQZXJ0YW1hLXRhbWEsIGtpdGEgcGVybHUgbWVtdWF0IGRhdGFzZXQgZGFuIG1lbXBlcnNpYXBrYW4gZGF0YToNCg0KYGBge3J9DQojIExvYWQgZGF0YXNldA0KZGF0YShpcmlzKQ0KDQojIExpaGF0IHN0cnVrdHVyIGRhdGFzZXQNCnN0cihpcmlzKQ0KYGBgDQoNCiMjIyBQZWxhdGloYW4gTW9kZWwNCg0KU2VsYW5qdXRueWEsIGtpdGEgbWVsYXRpaCBtb2RlbCBrbGFzaWZpa2FzaSBtZW5nZ3VuYWthbiBhbGdvcml0bWEgRGVjaXNpb24gVHJlZXM6DQoNCmBgYHtyfQ0KIyBQaXNhaGthbiBmaXR1ciBkYW4gdGFyZ2V0DQpYIDwtIGlyaXNbLCAxOjRdDQp5IDwtIGlyaXNbLCA1XQ0KDQojIFBlbGF0aWhhbiBtb2RlbCBEZWNpc2lvbiBUcmVlcw0KbGlicmFyeShycGFydCkNCnRyZWVfbW9kZWwgPC0gcnBhcnQoeSB+IC4sIGRhdGEgPSBYLCBtZXRob2QgPSAiY2xhc3MiKQ0KDQpgYGANCg0KIyMjIEV2YWx1YXNpIE1vZGVsDQoNClNldGVsYWggbWVsYXRpaCBtb2RlbCwga2l0YSBkYXBhdCBtZW5nZXZhbHVhc2kga2luZXJqYW55YSBkZW5nYW4gbWVuZ2d1bmFrYW4gZGF0YXNldCB5YW5nIHNhbWE6DQoNCmBgYHtyfQ0KIyBQcmVkaWtzaSBrZWxhcw0KcHJlZGlrc2kgPC0gcHJlZGljdCh0cmVlX21vZGVsLCBYLCB0eXBlID0gImNsYXNzIikNCg0KIyBIaXR1bmcgYWt1cmFzaQ0KYWt1cmFzaSA8LSBzdW0ocHJlZGlrc2kgPT0geSkgLyBsZW5ndGgoeSkNCnByaW50KHBhc3RlKCJBa3VyYXNpIG1vZGVsIERlY2lzaW9uIFRyZWVzOiIsIGFrdXJhc2kpKQ0KDQpwcmVzaXNpb209KHN1bShwcmVkaWtzaSA9PSJzZXRvc2EiICYgeT09InNldG9zYSIpK3N1bShwcmVkaWtzaSA9PSJ2ZXJzaWNvbG9yIiAmIHk9PSJ2ZXJzaWNvbG9yIikrDQogICAgICAgICAgc3VtKHByZWRpa3NpID09InZpcmdpbmljYSIgJiB5PT0idmlyZ2luaWNhIikpLyhzdW0ocHJlZGlrc2kgPT0ic2V0b3NhIikrc3VtKHByZWRpa3NpID09InZlcnNpY29sb3IiKStzdW0ocHJlZGlrc2kgPT0idmlyZ2luaWNhIikpICNtZW5naGl0dW5nIHByZXNpc2kgbW9kZWwNCnBhc3RlKCJQcmVzaXNpIG1vZGVsIERlY2lzaW9uIFRyZWVzOiIscHJlc2lzaW9tKQ0KDQpyZWNlbGw9KHN1bShwcmVkaWtzaSA9PSJzZXRvc2EiICYgeT09InNldG9zYSIpK3N1bShwcmVkaWtzaSA9PSJ2ZXJzaWNvbG9yIiAmIHk9PSJ2ZXJzaWNvbG9yIikrDQogICAgICAgICAgc3VtKHByZWRpa3NpID09InZpcmdpbmljYSIgJiB5PT0idmlyZ2luaWNhIikpLyhzdW0oeSA9PSJzZXRvc2EiKStzdW0oeSA9PSJ2ZXJzaWNvbG9yIikrc3VtKHkgPT0idmlyZ2luaWNhIikpICNtZW5naGl0dW5nIHJlY2FsbCBtb2RlbA0KcGFzdGUoInJlY2FsbCBtb2RlbCBEZWNpc2lvbiBUcmVlczoiLHJlY2VsbCkNCg0KZl8xPSgyKihwcmVzaXNpb20qcmVjZWxsKSkvKHByZXNpc2lvbStyZWNlbGwpICNtZW5naGl0dW5nIEYtMSBzY29yZSBtb2RlbA0KcGFzdGUoIkYtMSBTY29yZW1vZGVsIERlY2lzaW9uIFRyZWVzOiIsZl8xKQ0KYGBgDQoNCiMjIyBWaXN1YWxpc2FzaSBNb2RlbA0KDQpLaXRhIGp1Z2EgZGFwYXQgbWVtdmlzdWFsaXNhc2lrYW4gbW9kZWwgRGVjaXNpb24gVHJlZXMgeWFuZyB0ZWxhaCBraXRhIGxhdGloDQoNCmBgYHtyfQ0KIyBMb2FkIGxpYnJhcnkgdW50dWsgcGxvdHRpbmcNCmxpYnJhcnkocnBhcnQucGxvdCkNCg0KIyBQbG90IERlY2lzaW9uIFRyZWVzDQpycGFydC5wbG90KHRyZWVfbW9kZWwpDQpgYGANCg0KIyMjIEludGVycHJldGFzaSBIYXNpbA0KDQpCZXJkYXNhcmthbiBoYXNpbCBldmFsdWFzaSwga2l0YSBkYXBhdCBtZW5nZXZhbHVhc2kgc2ViZXJhcGEgYmFpayBtb2RlbCBraXRhIGRhbGFtIG1lbXByZWRpa3NpIGplbmlzIHNwZXNpZXMgYnVuZ2EgSXJpcyBiZXJkYXNhcmthbiBhdHJpYnV0LWF0cmlidXQgeWFuZyBhZGEgZGFsYW0gZGF0YXNldC4gU2VsYWluIGl0dSwga2l0YSBqdWdhIGRhcGF0IG1lbmdpbnRlcnByZXRhc2kgc3RydWt0dXIgcG9ob24ga2VwdXR1c2FuIHVudHVrIG1lbWFoYW1pIGJhZ2FpbWFuYSBtb2RlbCBtZW1idWF0IGtlcHV0dXNhbg==