1. Pendahuluan
1.1 Latar Belakang
Perkembangan teknologi kecerdasan buatan (Artificial Intelligence)
dan deep learning telah memberikan kemajuan yang signifikan dalam
berbagai bidang, termasuk pengenalan wajah, kendaraan otonom, diagnosis
medis, serta klasifikasi citra. Salah satu cabang deep learning yang
banyak digunakan untuk analisis citra adalah Convolutional Neural
Network (CNN). CNN merupakan arsitektur jaringan saraf tiruan yang
dirancang khusus untuk memproses data berbentuk citra melalui mekanisme
konvolusi sehingga mampu mengekstraksi fitur secara otomatis tanpa
memerlukan proses rekayasa fitur (feature engineering) secara manual
(LeCun, Bengio, & Hinton, 2015).
Kemampuan CNN dalam mengenali pola visual seperti tepi, tekstur,
bentuk, dan objek menjadikannya salah satu metode yang paling efektif
dalam tugas klasifikasi citra. Dibandingkan metode pembelajaran mesin
konvensional, CNN mampu mempelajari representasi fitur secara hierarkis
sehingga menghasilkan performa yang lebih baik pada berbagai
permasalahan pengenalan gambar (LeCun et al., 2015). Oleh karena itu,
CNN telah menjadi pendekatan yang banyak digunakan dalam penelitian
maupun aplikasi industri yang berkaitan dengan computer vision.
Salah satu dataset yang banyak digunakan untuk mengevaluasi performa
model klasifikasi citra adalah Fashion-MNIST. Dataset ini dikembangkan
oleh Zalando Research sebagai alternatif yang lebih menantang
dibandingkan dataset MNIST klasik yang hanya berisi citra angka tulisan
tangan (Xiao, Rasul, & Vollgraf, 2017). Fashion-MNIST terdiri atas
70.000 citra grayscale berukuran 28Ã28 piksel yang terbagi menjadi
60.000 data latih dan 10.000 data uji. Setiap citra dikelompokkan ke
dalam sepuluh kategori produk fashion, yaitu T-shirt/Top, Trouser,
Pullover, Dress, Coat, Sandal, Shirt, Sneaker, Bag, dan Ankle Boot (Xiao
et al., 2017).
Karakteristik Fashion-MNIST yang memiliki kemiripan visual antar
kelas menjadikan proses klasifikasi lebih kompleks dibandingkan
klasifikasi digit pada dataset MNIST. Sebagai contoh, kategori Shirt dan
T-shirt/Top memiliki bentuk yang relatif serupa sehingga memerlukan
kemampuan ekstraksi fitur yang baik untuk dapat dibedakan secara akurat.
Kondisi tersebut menjadikan Fashion-MNIST sebagai dataset yang sesuai
untuk menguji kemampuan CNN dalam mengenali pola visual dan melakukan
klasifikasi citra secara efektif (Xiao et al., 2017).
Pada penelitian ini, metode CNN diterapkan untuk membangun model
klasifikasi citra pada dataset Fashion-MNIST. Model yang dibangun
diharapkan mampu mempelajari karakteristik visual dari masing-masing
kategori pakaian dan menghasilkan tingkat akurasi yang tinggi. Selain
itu, penelitian ini juga bertujuan untuk mengevaluasi kinerja CNN
melalui metrik evaluasi seperti akurasi dan confusion matrix, sehingga
dapat diketahui kemampuan model dalam mengklasifikasikan setiap kategori
produk fashion secara tepat.
1.2 Rumusan Masalah
Berdasarkan latar belakang yang telah diuraikan, rumusan masalah
dalam penelitian ini adalah sebagai berikut:
- Bagaimana penerapan metode Convolutional Neural Network (CNN) dalam
mengklasifikasikan citra pada dataset Fashion-MNIST?
- Seberapa baik kinerja model CNN dalam mengklasifikasikan citra
Fashion-MNIST berdasarkan metrik evaluasi yang digunakan?
- Kategori pakaian apa yang paling mudah dan paling sulit
diklasifikasikan oleh model CNN?
- Apakah model CNN yang dibangun mengalami overfitting selama proses
pelatihan?
1.3 Tujuan Penelitian
Berdasarkan rumusan masalah tersebut, penelitian ini bertujuan
untuk:
- Membangun model klasifikasi citra menggunakan metode Convolutional
Neural Network (CNN) pada dataset Fashion-MNIST.
- Mengevaluasi kinerja model CNN dalam mengklasifikasikan citra
Fashion-MNIST menggunakan metrik evaluasi seperti akurasi dan confusion
matrix.
- Mengidentifikasi kategori pakaian yang paling mudah dan paling sulit
dikenali oleh model CNN.
- Menganalisis proses pelatihan model untuk mengetahui adanya indikasi
overfitting berdasarkan nilai accuracy dan loss pada data pelatihan
maupun data validasi.
1.4 Manfaat Penelitian
Penelitian ini diharapkan dapat memberikan manfaat sebagai
berikut:
- Menambah pemahaman mengenai penerapan metode Convolutional Neural
Network (CNN) dalam permasalahan klasifikasi citra.
- Memberikan gambaran mengenai kemampuan CNN dalam mengenali berbagai
kategori produk fashion pada dataset Fashion-MNIST.
- Menjadi referensi bagi penelitian selanjutnya yang berkaitan dengan
klasifikasi citra menggunakan metode deep learning.
2. Deskripsi Data
2.1 Sumber Data
Dataset yang digunakan dalam penelitian ini adalah Fashion-MNIST yang
tersedia melalui package keras. Fashion-MNIST merupakan dataset
klasifikasi citra yang dikembangkan oleh Zalando Research sebagai
alternatif yang lebih menantang dibandingkan dataset MNIST klasik.
Dataset ini terdiri atas citra berbagai produk fashion yang telah diberi
label ke dalam sepuluh kategori kelas (Xiao et al., 2017).
library(keras3)
library(caret)
library(ggplot2)
library(dplyr)
library(knitr)
library(yardstick)
library(tensorflow)
fashion_mnist <- dataset_fashion_mnist()
Fashion-MNIST merupakan dataset benchmark yang
banyak digunakan dalam penelitian klasifikasi citra. Dataset ini dipilih
karena memiliki tingkat kompleksitas yang lebih tinggi dibandingkan
dataset digit MNIST sehingga lebih sesuai untuk mengevaluasi kemampuan
model CNN dalam melakukan ekstraksi fitur dan klasifikasi citra.
2.2 Unit Observasi
Setiap observasi merupakan satu gambar produk fashion berukuran 28 Ã
28 piksel grayscale.Satu observasi merepresentasikan satu produk fashion
yang termasuk ke dalam salah satu dari sepuluh kategori yang tersedia.
Karena setiap citra berukuran 28 Ã 28 piksel, maka setiap gambar
memiliki total 784 nilai piksel yang dapat digunakan sebagai masukan
model.
2.3 Variabel Respons
Variabel respons berupa label kategori produk fashion yang akan
diprediksi oleh model CNN. Permasalahan yang dikaji termasuk ke dalam
klasifikasi multikelas (multi-class classification) karena terdapat
sepuluh kelas yang berbeda.
Tabel 1. Variabel Penelitian
| 0 |
T-shirt/Top |
| 1 |
Trouser |
| 2 |
Pullover |
| 3 |
Dress |
| 4 |
Coat |
| 5 |
Sandal |
| 6 |
Shirt |
| 7 |
Sneaker |
| 8 |
Bag |
| 9 |
Ankle Boot |
2.4 Variabel Prediktor
Sebanyak 784 piksel citra digunakan sebagai prediktor.Setiap piksel
memiliki nilai intensitas antara 0 hingga 255 yang menunjukkan tingkat
kecerahan warna abu-abu (grayscale). Pada proses pelatihan CNN, nilai
piksel ini digunakan sebagai informasi utama untuk mengenali pola visual
seperti bentuk, tekstur, dan kontur objek. Berbeda dengan metode
klasifikasi konvensional yang memerlukan ekstraksi fitur secara manual,
CNN mampu mempelajari fitur-fitur penting secara otomatis dari nilai
piksel tersebut.
2.5 Dimensi Dataset
Tabel 2. Dimensi Dataset Fashion MNIST
dim_train <- dim(fashion_mnist$train$x)
dim_test <- dim(fashion_mnist$test$x)
tabel_dim <- data.frame(
Dataset = c("Train", "Test"),
Samples = c(dim_train[1], dim_test[1]),
Rows = c(dim_train[2], dim_test[2]),
Cols = c(dim_train[3], dim_test[3])
)
knitr::kable(tabel_dim)
| Train |
60000 |
28 |
28 |
| Test |
10000 |
28 |
28 |
Interpretasi:
Dataset Fashion-MNIST terdiri dari 60.000 citra pelatihan dan 10.000
citra pengujian dengan resolusi 28 × 28 piksel, sehingga total observasi
yang digunakan dalam penelitian ini mencapai 70.000 citra. Jumlah data
yang relatif besar memungkinkan model CNN mempelajari pola visual yang
beragam sehingga diharapkan menghasilkan kemampuan generalisasi yang
baik terhadap data baru.
Pembagian data menjadi training set dan testing set bertujuan untuk
mengevaluasi kemampuan model dalam melakukan prediksi terhadap data yang
belum pernah dilihat sebelumnya. Data pelatihan digunakan untuk
membangun model, sedangkan data pengujian digunakan untuk mengukur
performa model secara objektif. Pembagian data menjadi training set dan
testing set bertujuan untuk mengevaluasi kemampuan model dalam melakukan
prediksi terhadap data yang belum pernah dilihat sebelumnya. Data
pelatihan digunakan untuk membangun model, sedangkan data pengujian
digunakan untuk mengukur performa model secara objektif.
3. Prapemrosesan dan Exploratory Data Analysis (EDA)
Tahap prapemrosesan data (data preprocessing) dan exploratory data
analysis (EDA) dilakukan untuk memahami karakteristik dataset sebelum
proses pelatihan model. Tahapan ini bertujuan untuk memastikan kualitas
data, mengidentifikasi potensi permasalahan yang dapat memengaruhi
performa model, serta menyiapkan data agar sesuai dengan format yang
dibutuhkan oleh arsitektur Convolutional Neural Network (CNN).
3.1 Pemeriksaan Data Hilang
Tabel 3. Jumlah Missing Value Dataset Fashion
MNIST
na_train <- sum(is.na(fashion_mnist$train$x))
na_test <- sum(is.na(fashion_mnist$test$x))
tabel_na <- data.frame(
Dataset = c("Train", "Test"),
Missing_Value = c(na_train, na_test)
)
knitr::kable(tabel_na)
Interpretasi:
Pemeriksaan data hilang (missing value) dilakukan untuk memastikan
bahwa seluruh informasi pada dataset tersedia secara lengkap. Nilai yang
hilang dapat menyebabkan proses pelatihan model menjadi tidak optimal
karena beberapa algoritma machine learning dan deep learning tidak dapat
memproses data yang tidak lengkap secara langsung.
Hasil pemeriksaan menunjukkan bahwa jumlah nilai hilang pada data
pelatihan maupun data pengujian adalah nol. Dengan demikian, seluruh
citra pada dataset Fashion-MNIST dapat digunakan secara langsung tanpa
memerlukan proses imputasi atau penanganan data hilang lainnya. Kondisi
ini menunjukkan bahwa kualitas data yang digunakan sudah baik dan siap
untuk memasuki tahap analisis berikutnya.
3.2 Visualisasi Sampel Citra
par(mfrow = c(3,3))
for(i in 1:9){
image(
t(apply(fashion_mnist$train$x[i,,],2,rev)),
col = gray((0:255)/255),
axes = FALSE
)
}

Interpretasi:
Visualisasi citra dilakukan untuk memperoleh pemahaman awal mengenai
bentuk, pola, dan karakteristik objek yang terdapat dalam dataset.
Setiap gambar pada Fashion-MNIST merupakan citra grayscale berukuran 28
à 28 piksel yang merepresentasikan salah satu kategori produk
fashion.
Hasil visualisasi menunjukkan bahwa setiap kategori memiliki bentuk
dan pola visual yang berbeda. Sebagai contoh, kategori sandal dan tas
memiliki karakteristik yang relatif mudah dikenali karena bentuknya
cukup unik, sedangkan kategori seperti shirt, coat, dan pullover
memiliki kemiripan bentuk yang cukup tinggi. Kemiripan visual antar
kelas tersebut berpotensi menyebabkan kesalahan klasifikasi pada
model.
Selain itu, visualisasi awal juga menunjukkan bahwa objek pakaian
telah berada pada posisi yang relatif terpusat sehingga memudahkan model
CNN dalam mengekstraksi fitur-fitur penting seperti tepi (edges),
tekstur, dan bentuk objek selama proses pelatihan.
3.3 Distribusi Kelas
freq <- table(fashion_mnist$train$y)
kelas <- c(
"Tshirt","Trouser","Pullover","Dress","Coat",
"Sandal","Shirt","Sneaker","Bag","Boot"
)
barplot(
freq,
names.arg = kelas,
las = 2
)

Gambar 2. Distribusi Kelas Fashion-MNIST
Distribusi kelas dianalisis untuk mengetahui proporsi jumlah
observasi pada setiap kategori pakaian. Analisis ini penting karena
ketidakseimbangan jumlah data antar kelas (class imbalance) dapat
menyebabkan model lebih cenderung mempelajari kelas yang memiliki jumlah
observasi lebih besar.
Proporsi setiap kelas dapat dihitung menggunakan persamaan
berikut:
\[p_i = \frac{n_i}{N}\]
Keterangan:
\(p_i\) = proporsi kelas ke-i
\(n_i\) = jumlah observasi pada
kelas ke-i
\(N\) = jumlah seluruh observasi
Interpretasi:
Berdasarkan hasil perhitungan frekuensi dan visualisasi diagram
batang, terlihat bahwa setiap kelas memiliki jumlah observasi yang
relatif sama, yaitu sekitar 6.000 citra pada data pelatihan. Dengan
demikian, nilai proporsi masing-masing kelas mendekati 10% dari total
data pelatihan.
Kondisi ini menunjukkan bahwa dataset Fashion-MNIST memiliki
distribusi kelas yang seimbang (balanced dataset), sehingga risiko bias
model terhadap kelas tertentu relatif kecil. Keseimbangan distribusi
kelas juga membantu proses pelatihan CNN menghasilkan performa
klasifikasi yang lebih objektif dan stabil.
3.4 Normalisasi Data
Tabel 4. Normalisasi Dataset Fashion MNIST
x_train <- fashion_mnist$train$x / 255
x_test <- fashion_mnist$test$x / 255
tabel_norm <- data.frame(
Dataset = c("Train", "Test"),
Min = c(min(x_train), min(x_test)),
Max = c(max(x_train), max(x_test)),
Mean = c(round(mean(x_train), 4), round(mean(x_test), 4))
)
knitr::kable(tabel_norm)
| Train |
0 |
1 |
0.2860 |
| Test |
0 |
1 |
0.2868 |
Interpretasi:
Setiap piksel pada citra Fashion-MNIST memiliki nilai intensitas
antara 0 hingga 255, di mana nilai 0 merepresentasikan warna hitam dan
nilai 255 merepresentasikan warna putih. Perbedaan rentang nilai yang
cukup besar dapat menyebabkan proses optimasi model menjadi kurang
efisien. Dengan menggunakan rumus:
\[x' = \frac{x}{255}\]
Keterangan:
\(x\) = nilai piksel sebelum
normalisasi
\(x'\) = nilai piksel setelah
normalisasi
Melalui proses normalisasi, seluruh nilai piksel berada pada rentang
0â1 sehingga proses pembelajaran CNN menjadi lebih stabil dan cepat.
Proses ini bertujuan untuk menyamakan skala data, mempercepat proses
pembelajaran model, serta membantu algoritma optimasi seperti Adam dalam
menemukan parameter terbaik secara lebih stabil.
Selain meningkatkan efisiensi komputasi, normalisasi juga dapat
mengurangi risiko terjadinya ketidakstabilan numerik selama proses
pelatihan dan membantu model mencapai konvergensi lebih cepat
dibandingkan menggunakan data mentah.
3.5 Reshaping Data
Tabel 5. Dimensi Dataset Setelah Reshape
x_train <- array_reshape(x_train, c(nrow(x_train), 28, 28, 1))
x_test <- array_reshape(x_test, c(nrow(x_test), 28, 28, 1))
tabel_reshape <- data.frame(
Dataset = c("Train", "Test"),
Samples = c(dim(x_train)[1], dim(x_test)[1]),
Height = c(dim(x_train)[2], dim(x_test)[2]),
Width = c(dim(x_train)[3], dim(x_test)[3]),
Channel = c(dim(x_train)[4], dim(x_test)[4])
)
knitr::kable(tabel_reshape)
| Train |
60000 |
28 |
28 |
1 |
| Test |
10000 |
28 |
28 |
1 |
Interpretasi:
Data citra yang diperoleh dari dataset awal masih berbentuk matriks
tiga dimensi. Namun, framework Keras dan TensorFlow mengharuskan input
untuk lapisan Convolutional Neural Network memiliki format empat dimensi
yang terdiri atas:
- Jumlah citra (samples),
- Tinggi citra (height),
- Lebar citra (width),
- Jumlah kanal warna (channels).
Karena Fashion-MNIST merupakan citra grayscale, maka setiap gambar
hanya memiliki satu kanal warna (channel = 1). Oleh sebab itu, data
diubah menjadi bentuk array berukuran (jumlah data, 28, 28, 1).
Proses reshaping ini tidak mengubah isi data, melainkan hanya
mengubah struktur penyimpanannya agar sesuai dengan kebutuhan input pada
lapisan konvolusi CNN.
3.6 One-Hot Encoding
Tabel 6. One-Hot Encoding
y_train <- to_categorical(fashion_mnist$train$y, 10)
y_test <- to_categorical(fashion_mnist$test$y, 10)
label_names <- c("T-shirt/top", "Trouser", "Pullover", "Dress", "Coat",
"Sandal", "Shirt", "Sneaker", "Bag", "Ankle boot")
# Buat tabel contoh tanpa transpose
tabel_contoh <- data.frame(
Sample = paste("Sample", 1:5),
Label_Asli = label_names[fashion_mnist$train$y[1:5] + 1],
y_train[1:5, ]
)
colnames(tabel_contoh)[3:12] <- paste0("Class_", 0:9)
knitr::kable(tabel_contoh)
| Sample 1 |
Ankle boot |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
| Sample 2 |
T-shirt/top |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
| Sample 3 |
T-shirt/top |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
| Sample 4 |
Dress |
0 |
0 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
| Sample 5 |
T-shirt/top |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
Interpretasi:
Variabel target pada Fashion-MNIST awalnya direpresentasikan dalam
bentuk angka 0 hingga 9 yang menunjukkan kategori pakaian tertentu.
Namun, pada klasifikasi multikelas menggunakan fungsi aktivasi Softmax,
label target harus direpresentasikan dalam bentuk vektor biner.
Proses one-hot encoding mengubah setiap label menjadi vektor
sepanjang 10 elemen. Sebagai contoh, label kelas “2” akan
direpresentasikan sebagai:
\[y=[0,0,1,0,0,0,0,0,0,0]\]
Atau secara umum:
\[y_i = \begin{cases} 1, & \text{jika
kelas ke-}i \\ 0, & \text{lainnya} \end{cases}\]
Representasi ini memungkinkan model menghitung probabilitas untuk
setiap kelas secara bersamaan dan menyesuaikan bobot jaringan
menggunakan fungsi loss categorical cross-entropy. Dengan demikian,
proses one-hot encoding merupakan tahap penting yang memungkinkan CNN
melakukan klasifikasi multikelas secara efektif dan menghasilkan
prediksi probabilitas pada seluruh kategori pakaian yang tersedia.
4. Metode
4.1 Convolutional Neural Network (CNN)
Convolutional Neural Network (CNN) merupakan salah satu arsitektur
Deep Learning yang dirancang khusus untuk pengolahan data berbentuk
citra. CNN mampu melakukan ekstraksi fitur secara otomatis melalui
operasi konvolusi tanpa memerlukan proses ekstraksi fitur secara manual
seperti pada metode machine learning konvensional. Kemampuan tersebut
menjadikan CNN sangat efektif dalam berbagai tugas computer vision,
termasuk klasifikasi citra, deteksi objek, dan pengenalan pola visual
(LeCun et al., 2015).
Pada penelitian ini, CNN digunakan untuk mengklasifikasikan citra
Fashion-MNIST ke dalam 10 kategori pakaian berdasarkan pola visual yang
terkandung pada setiap gambar.
4.1.1 Lapisan Konvolusi (Convolution Layer)
Lapisan konvolusi merupakan komponen utama CNN yang berfungsi
mengekstraksi fitur dari citra input menggunakan kernel atau filter.
Filter akan bergerak pada seluruh bagian citra dan menghasilkan feature
map yang merepresentasikan pola tertentu seperti tepi (edges), tekstur,
maupun bentuk objek.
Operasi konvolusi dapat dinyatakan sebagai:
\[
S(i,j)=\sum_m \sum_n I(i+m,j+n)K(m,n)
\]
Keterangan:
\(I\) = citra input
\(K\) = kernel atau filter
\(S\) = feature map hasil
konvolusi
\(i,j\) = posisi piksel pada feature
map
Melalui operasi ini, CNN dapat mempelajari fitur-fitur penting secara
otomatis dari data citra.
4.1.2 Fungsi Aktivasi ReLU
Setelah proses konvolusi, nilai keluaran akan diteruskan ke fungsi
aktivasi Rectified Linear Unit (ReLU). Fungsi ini bertujuan untuk
memperkenalkan sifat nonlinier ke dalam model sehingga CNN mampu
mempelajari pola yang kompleks.
Persamaan ReLU adalah:
\[f(x)=\max(0,x)\]
Keterangan:
Jika \(x > 0\), maka output
sama dengan \(x\)
Jika \(x \le 0\), maka output
bernilai 0
Penggunaan ReLU membantu mempercepat proses pelatihan dan mengurangi
masalah vanishing gradient (Goodfellow et al., 2016).
4.1.3 Pooling Layer
Pooling digunakan untuk mengurangi dimensi feature map sehingga
jumlah parameter yang dipelajari menjadi lebih sedikit. Selain itu,
pooling juga membantu mengurangi risiko overfitting dan meningkatkan
efisiensi komputasi.
Pada penelitian ini digunakan Max Pooling yang memilih nilai maksimum
dari suatu area feature map.
Persamaan Max Pooling:
\[
y=\max(x_1,x_2,\ldots,x_n)
\]
Keterangan:
\(x_1,x_2,\ldots,x_n\) = nilai
piksel dalam area pooling
\(y\) = nilai maksimum yang
dipilih
4.1.4 Flatten Layer
Output dari lapisan konvolusi dan pooling masih berbentuk matriks
multidimensi. Oleh karena itu dilakukan proses flatten untuk mengubah
feature map menjadi vektor satu dimensi yang dapat diproses oleh Fully
Connected Layer.
Proses ini tidak mengubah informasi fitur yang telah dipelajari,
melainkan hanya mengubah bentuk data agar sesuai dengan struktur
jaringan saraf tiruan.
4.1.5 Fully Connected Layer
Fully Connected Layer (Dense Layer) berfungsi menggabungkan seluruh
fitur yang telah diekstraksi sebelumnya untuk menghasilkan keputusan
klasifikasi.
Pada penelitian ini digunakan:
- Dense Layer dengan 128 neuron
- Fungsi aktivasi ReLU
Jumlah neuron tersebut dipilih untuk memberikan kapasitas
pembelajaran yang cukup dalam mengenali pola antar kategori pakaian.
4.1.6 Dropout Layer
Dropout merupakan teknik regularisasi yang digunakan untuk mengurangi
overfitting. Selama proses pelatihan, sebagian neuron akan dinonaktifkan
secara acak sehingga model tidak terlalu bergantung pada neuron
tertentu.
Pada penelitian ini digunakan nilai dropout sebesar 0,3 yang berarti
30% neuron akan dinonaktifkan secara acak pada setiap iterasi
pelatihan.
4.1.7 Output Layer dan Softmax
Lapisan output digunakan untuk menghasilkan probabilitas
masing-masing kelas. Karena Fashion-MNIST terdiri atas 10 kategori
pakaian, maka digunakan 10 neuron output dengan fungsi aktivasi
Softmax.
Persamaan Softmax:
\[
P(y=i)=\frac{e^{z_i}}{\sum_{j=1}^{K}e^{z_j}}
\]
Keterangan:
\(P(y=i)\) = probabilitas kelas
ke-(i)
\(z_i\) = output neuron ke-(i)
\(K\) = jumlah kelas
Softmax menghasilkan nilai probabilitas antara 0 hingga 1 dengan
total probabilitas seluruh kelas sama dengan 1. Arsitektur CNN yang
Digunakan adalah sebagai berikut:
| Input |
\(28 \times 28 \times
1\) |
| Conv2D |
32 filter, kernel \(3 \times
3\), ReLU |
| MaxPooling2D |
Pool size \(2 \times
2\) |
| Conv2D |
64 filter, kernel \(3 \times
3\), ReLU |
| MaxPooling2D |
Pool size \(2 \times
2\) |
| Flatten |
Mengubah feature map menjadi vektor |
| Dense |
128 neuron, ReLU |
| Dropout |
0,3 |
| Output |
10 neuron, Softmax |
Arsitektur tersebut dirancang untuk mengekstraksi fitur visual secara
bertahap mulai dari fitur sederhana hingga fitur yang lebih kompleks
sehingga mampu membedakan setiap kategori pakaian pada dataset
Fashion-MNIST.
4.2 Pembangunan Model
Model CNN dibangun menggunakan library Keras pada bahasa pemrograman
R. Struktur model disusun secara berurutan (sequential) yang terdiri
atas dua lapisan konvolusi, dua lapisan pooling, satu lapisan flatten,
satu lapisan dense, satu lapisan dropout, dan satu lapisan output.
Tabel 8. Arsitektur Model CNN Fashion MNIST
model <- keras_model_sequential() %>%
layer_conv_2d(filters = 32, kernel_size = c(3,3), activation = "relu", input_shape = c(28,28,1)) %>%
layer_max_pooling_2d(pool_size = c(2,2)) %>%
layer_conv_2d(filters = 64, kernel_size = c(3,3), activation = "relu") %>%
layer_max_pooling_2d(pool_size = c(2,2)) %>%
layer_flatten() %>%
layer_dense(units = 128, activation = "relu") %>%
layer_dropout(rate = 0.3) %>%
layer_dense(units = 10, activation = "softmax")
summary_output <- capture.output(summary(model))
# Parse baris yang mengandung data layer
layer_lines <- summary_output[grepl("\\(None,", summary_output)]
# Ekstrak Layer, Output Shape, dan Param
parsed <- lapply(layer_lines, function(line) {
line_clean <- gsub("^\\???\\s*|\\s*\\???\\s*$", "", line)
parts <- strsplit(line_clean, "\\s*\\???\\s*")[[1]]
if (length(parts) >= 3) {
data.frame(
Layer = trimws(parts[1]),
Output_Shape = trimws(parts[2]),
Param = trimws(parts[3])
)
}
})
tabel_arsitektur <- do.call(rbind, Filter(Negate(is.null), parsed))
rownames(tabel_arsitektur) <- NULL
knitr::kable(tabel_arsitektur)
| â |
|
c |
| â |
|
m |
| â |
|
c |
| â |
|
m |
| â |
|
f |
| â |
|
d |
| â |
|
d |
| â |
|
d |
Tabel 9. Ringkasan Parameter Model
param_lines <- summary_output[grepl("Total params|Trainable params|Non-trainable params", summary_output)]
param_lines_clean <- gsub("^##\\s*|^\\s*", "", param_lines)
tabel_params <- data.frame(
Keterangan = c("Total Parameters", "Trainable Parameters", "Non-Trainable Parameters"),
Jumlah = trimws(gsub(".*params:\\s*", "", param_lines_clean))
)
knitr::kable(tabel_params)
| Total Parameters |
225,034 (879.04 KB) |
| Trainable Parameters |
225,034 (879.04 KB) |
| Non-Trainable Parameters |
0 (0.00 B) |
4.3 Kompilasi Model
Sebelum dilakukan pelatihan, model perlu dikompilasi dengan
menentukan optimizer, fungsi loss, dan metrik evaluasi.
Optimizer yang digunakan adalah Adam karena mampu melakukan pembaruan
bobot secara adaptif dan memiliki performa yang baik pada berbagai
permasalahan deep learning (Kingma & Ba, 2015).
Fungsi loss yang digunakan adalah Categorical Cross-Entropy karena
penelitian ini merupakan klasifikasi multikelas.
Persamaan Cross-Entropy:
\[
L=-\sum_{i=1}^{K}y_i\log(\hat{y}_i)
\]
Keterangan:
\(L\) = nilai loss
\(y_i\) = label aktual
\(\hat{y}_i\) = probabilitas
prediksi
\(K\) = jumlah kelas
Semakin kecil nilai loss, semakin baik kemampuan model dalam
melakukan prediksi.
model %>% compile(
optimizer = "adam",
loss = "categorical_crossentropy",
metrics = "accuracy"
)
4.4 Hyperparameter Tuning
Hyperparameter tuning dilakukan untuk memperoleh kombinasi parameter
model yang menghasilkan performa terbaik. Hyperparameter merupakan
parameter yang ditentukan sebelum proses pelatihan dimulai dan tidak
dipelajari secara otomatis oleh model.
Pada penelitian ini dilakukan pencarian beberapa kombinasi
hyperparameter yang meliputi:
- Jumlah filter konvolusi
- Jumlah neuron pada Dense Layer
- Learning Rate
- Batch Size
- Jumlah Epoch
- Nilai Dropout
Pemilihan kombinasi terbaik dilakukan berdasarkan nilai akurasi
validasi (validation accuracy) tertinggi.
Tabel 10. Kandidat Hyperparameter yang Digunakan
pada Proses Tuning
| Filter Conv2D |
32, 64 |
| Dense Units |
128, 256 |
| Learning Rate |
0.001, 0.0005 |
| Batch Size |
64, 128 |
| Dropout |
0.3, 0.5 |
| Epoch |
10 |
4.4.1 Pemilihan Hyperparameter Terbaik
Tabel 11. Hyperparameter Terbaik
best_param <- hasil_tuning %>%
arrange(desc(Val_Accuracy)) %>%
slice(1)
knitr::kable(
best_param, col.names = c("Filter", "Dense", "Dropout", "Val Accuracy"))
4.4.2 Pembangunan Model CNN Final
model <- keras_model_sequential() %>%
layer_conv_2d(filters = 64, kernel_size = c(3,3), activation = "relu",
input_shape = c(28,28,1)) %>%
layer_max_pooling_2d(pool_size = c(2,2)) %>%
layer_conv_2d(filters = 128, kernel_size = c(3,3), activation = "relu") %>%
layer_max_pooling_2d(pool_size = c(2,2)) %>%
layer_flatten() %>%
layer_dense(units = 256, activation = "relu") %>%
layer_dropout(rate = 0.3) %>%
layer_dense(units = 10, activation = "softmax")
model %>% compile(
optimizer = "adam",
loss = "categorical_crossentropy",
metrics = "accuracy"
)
summary_output <- capture.output(summary(model))
layer_lines <- summary_output[grepl("\\(None,", summary_output)]
parsed <- lapply(layer_lines, function(line) {
line_clean <- gsub("^\\???\\s*|\\s*\\???\\s*$", "", line)
parts <- strsplit(line_clean, "\\s*\\???\\s*")[[1]]
if (length(parts) >= 3) {
data.frame(
Layer = trimws(parts[1]),
Output_Shape = trimws(parts[2]),
Param = trimws(parts[3])
)
}
})
tabel_arsitektur <- do.call(rbind, Filter(Negate(is.null), parsed))
rownames(tabel_arsitektur) <- NULL
knitr::kable(tabel_arsitektur)
| â |
|
c |
| â |
|
m |
| â |
|
c |
| â |
|
m |
| â |
|
f |
| â |
|
d |
| â |
|
d |
| â |
|
d |
Tabel 12. Parameter Model CNN Final
param_lines <- summary_output[grepl("Total params|Trainable params|Non-trainable params", summary_output)]
param_lines_clean <- gsub("^##\\s*|^\\s*", "", param_lines)
tabel_params <- data.frame(
Keterangan = c("Total Parameters", "Trainable Parameters", "Non-Trainable Parameters"),
Jumlah = trimws(gsub(".*params:\\s*", "", param_lines_clean))
)
knitr::kable(
tabel_params
)
| Total Parameters |
896,522 (3.42 MB) |
| Trainable Parameters |
896,522 (3.42 MB) |
| Non-Trainable Parameters |
0 (0.00 B) |
4.5 Pelatihan dan Validasi
Pelatihan model dilakukan menggunakan data pelatihan yang telah
dipraproses sebelumnya. Sebanyak 80% data digunakan untuk pelatihan dan
20% digunakan sebagai data validasi melalui parameter validation_split =
0.2.
Parameter pelatihan yang digunakan adalah:
- Epoch = 10
- Batch Size = 128
- Validation Split = 20%
Epoch menunjukkan jumlah iterasi penuh terhadap seluruh data
pelatihan, sedangkan batch size menunjukkan jumlah data yang diproses
sebelum dilakukan pembaruan bobot jaringan.
Tabel 13. Training Model CNN
history <- model %>% fit(
x_train,
y_train,
epochs = 10,
batch_size = 128,
validation_split = 0.2
)
## Epoch 1/10
## 375/375 - 27s - 71ms/step - accuracy: 0.8068 - loss: 0.5321 - val_accuracy: 0.8593 - val_loss: 0.3838
## Epoch 2/10
## 375/375 - 26s - 69ms/step - accuracy: 0.8736 - loss: 0.3434 - val_accuracy: 0.8863 - val_loss: 0.3183
## Epoch 3/10
## 375/375 - 25s - 66ms/step - accuracy: 0.8921 - loss: 0.2946 - val_accuracy: 0.8919 - val_loss: 0.2959
## Epoch 4/10
## 375/375 - 25s - 65ms/step - accuracy: 0.9041 - loss: 0.2608 - val_accuracy: 0.9031 - val_loss: 0.2668
## Epoch 5/10
## 375/375 - 41s - 109ms/step - accuracy: 0.9136 - loss: 0.2344 - val_accuracy: 0.9038 - val_loss: 0.2556
## Epoch 6/10
## 375/375 - 24s - 65ms/step - accuracy: 0.9188 - loss: 0.2160 - val_accuracy: 0.9120 - val_loss: 0.2426
## Epoch 7/10
## 375/375 - 24s - 64ms/step - accuracy: 0.9272 - loss: 0.1959 - val_accuracy: 0.9112 - val_loss: 0.2462
## Epoch 8/10
## 375/375 - 25s - 65ms/step - accuracy: 0.9310 - loss: 0.1814 - val_accuracy: 0.9142 - val_loss: 0.2405
## Epoch 9/10
## 375/375 - 26s - 70ms/step - accuracy: 0.9401 - loss: 0.1605 - val_accuracy: 0.9157 - val_loss: 0.2327
## Epoch 10/10
## 375/375 - 22s - 60ms/step - accuracy: 0.9455 - loss: 0.1468 - val_accuracy: 0.9186 - val_loss: 0.2312
# Ekstrak metrik otomatis dari history
tabel_history <- data.frame(
Epoch = 1:length(history$metrics$loss),
Loss = round(history$metrics$loss, 4),
Accuracy = round(history$metrics$accuracy, 4),
Val_Loss = round(history$metrics$val_loss, 4),
Val_Accuracy = round(history$metrics$val_accuracy, 4)
)
knitr::kable(
tabel_history)
| 1 |
0.5321 |
0.8068 |
0.3838 |
0.8593 |
| 2 |
0.3434 |
0.8736 |
0.3183 |
0.8863 |
| 3 |
0.2946 |
0.8921 |
0.2959 |
0.8919 |
| 4 |
0.2608 |
0.9041 |
0.2668 |
0.9031 |
| 5 |
0.2344 |
0.9136 |
0.2556 |
0.9038 |
| 6 |
0.2160 |
0.9188 |
0.2426 |
0.9120 |
| 7 |
0.1959 |
0.9272 |
0.2462 |
0.9112 |
| 8 |
0.1814 |
0.9310 |
0.2405 |
0.9142 |
| 9 |
0.1605 |
0.9401 |
0.2327 |
0.9157 |
| 10 |
0.1468 |
0.9455 |
0.2312 |
0.9186 |
Selama proses pelatihan, model akan mempelajari pola pada data
pelatihan dan performanya dipantau menggunakan data validasi untuk
mendeteksi kemungkinan terjadinya overfitting.
4.6 Metrik Evaluasi
Evaluasi model dilakukan menggunakan beberapa metrik untuk mengukur
kemampuan klasifikasi CNN secara komprehensif.
4.6.1 Accuracy
Akurasi adalah proporsi banyaknya data yang diprediksi dengan benar
terhadap total data yang diprediksi. Nilai akurasi menunjukkan gambaran
secara global seberapa baik prediksi yang dilakukan. Misal, nilai
akurasi 0,85 bermakna dari total seluruh data yang diprediksi, model
dengan tepat memprediksi 85% sedangkan 15% salah prediksi.
Pada klasifikasi biner, rumus untuk menghitung akurasi adalah sebagai
berikut:
\[
Accuracy=\frac{TP+TN}{TP+TN+FP+FN}
\]
Keterangan:
TP = True Positive
TN = True Negative
FP = False Positive
FN = False Negative
Rumus persamaan tersebut dapat dibuat lebih umum untuk menghitung
akurasi data klasifikasi multikelas yaitu:
\[\text{Akurasi} = \frac{\sum_{i=1}^{n}
\text{TP}_{\text{kelas}-i}}{\sum_{i=1}^{n} \text{TP}_{\text{kelas}-i} +
\sum_{i=1}^{n} \text{FP}_{\text{kelas}-i}}\]
4.6.2 Precision
Precision adalah metrik evaluasi kinerja yang digunakan untuk
mengukur akurasi pada kelas prediksi tertentu. Pada kasus klasifikasi
biner, presisi mengukur seberapa banyak data yang prediksi ke suatu
kelas yang memang benar masuk kelas tersebut. Presisi memberikan
gambaran tentang kualitas prediksi positif yang dihasilkan oleh
model.
Sama seperti recall, nilai presisi juga dihitung untuk masing-masing
kelas dengan rumus sebagai berikut:
\[
Precision_i=\frac{TP_i}{TP_i+FP_i}
\]
4.6.3 Recall (Sensitivity & Specificity)
Metrik Recall, juga dikenal sebagai sensitivity, adalah salah satu
metrik penting dalam evaluasi model klasifikasi. Recall mengukur
kemampuan model untuk mendeteksi semua sampel suatu kelas di antara
semua sampel yang benar-benar masuk kelas tersebut. Dalam konteks
deteksi penyakit, misalnya, recall mengukur seberapa baik model dapat
mendeteksi pasien yang benar-benar sakit dari semua pasien yang
sakit.
Istilah sensitivity biasanya digunakan untuk menyebutkan recall pada
kelas positif atau True Positive Rate (TPR). Adapun untuk kelas negatif
istilah yang digunakan adalah specificity. Namun sekali lagi, penentuan
kelas positif akan sangat tergantung ketertarikan dan tujuan
penelitian.
Nilai recall dihitung untuk masing-masing kelas dengan rumus sebagai
berikut:
\[
Recall_i=\frac{TP_i}{TP_i+FN_i}
\]
4.6.4 F1-Score
F1 Score adalah metrik evaluasi kinerja model klasifikasi yang
menggabungkan precision dan recall menjadi satu nilai tunggal. Metrik
ini diukur dari rata-rata harmonis precision dan recall, sehingga
memberikan gambaran yang lebih seimbang tentang kinerja model terutama
ketika terdapat ketidakseimbangan antara jumlah kelas positif dan
negatif.
Ketika berbicara klasifikasi biner, F1 Score umumnya diukur pada
kelas positif saja. Sementara itu, pada klasifikasi multikelas, kita
dapat mengukur F1 Score suatu kelas vs total kelas lainnya.
Perhitungan nilai F1 Score dilakukan menggunakan rumus berikut
ini:
\[
F1_i=2\times\frac{Precision_i\times Recall_i}{Precision_i+Recall_i}
\]
4.6.5 Confusion Matrix
Confusion matrix adalah matiks yang menyajikan tabulasi hasil
prediksi dari suatu model klasifikasi. Matriks ini berisi informasi
nilai-nilai yang merepresentasikan bagaimana model memprediksi kelas
tertentu dibandingkan dengan kelas sebenarnya.
Pada klasifikasi biner, biasanya terdapat satu kelas yang disebut
sebagai kelas positif dan kelas lainnya sebagai kelas negatif. Penentuan
kelas positif ini tidak berkaitan dengan konotasi dari kelas tersebut,
namun dapat dipilih yang mana saja sesuai keinginan atau kebutuhan
peniliti. Terdapat 4 nilai yang disajikan pada Gambar 4 yaitu:
- True Positive (TP): jumlah data dari kelas positif yang diprediksi
benar (masuk kelas positif)
- True Negative (TN): jumlah data dari kelas negatif yang diprediksi
benar (masuk kelas negatif)
- False Positive (FP): jumlah data dari kelas negatif yang salah
prediksi (seharusnya negatif namun diprediksi positif)
- False negative (FN): jumlah data dari kelas positif yang salah
prediksi (seharusnya positif namun diprediksi negatif)
Lebih general, pada klasifikasi multi-kelas (lebih dari 2 kelas
output) ukuran confusion matrix juga menjadi lebih banyak. Misalkan
untuk 3 kelas maka ukuran confusion matrix menjadi 3Ã3, atau berisi 9
nilai. Secara umum, nilai yang terdapat pada posisi diagonal menunjukkan
jumlah prediksi benar dari masing-masing kelas. Adapun pada bagian
lainnya menunjukkan prediksi salah. Sebagai contoh K1 (TRUE) menyatakan
banyaknya prediksi benar dari data yang memang seharusnya masuk ke dalam
kelas ke-1. Sedangkan K1 (FALSE) menyatakan banyaknya data yang
seharusnya merupakan kelas K2 dan K3 namun salah diprediksi sebagai
K1.
Meskipun terdapat perbedaan ukuran, namun metrik-metrik evaluasi yang
digunakan tetap berlaku secara umum. Perhitungan pada setiap metrik
evaluasi tersebut sangat tergantung dengan nilai-nilai pada confusion
matrix.
5. Hasil Analisis
Bagian ini menyajikan hasil pelatihan, pengujian, dan evaluasi model
Convolutional Neural Network (CNN) yang telah dibangun untuk
mengklasifikasikan citra Fashion-MNIST. Analisis dilakukan untuk menilai
kemampuan model dalam mengenali kategori pakaian berdasarkan pola visual
yang terdapat pada citra.
5.1 Hasil Hyperparameter Tuning
Tabel 14. Hasil Hyperparameter Tuning
knitr::kable(
hasil_tuning %>% arrange(desc(Val_Accuracy)),
col.names = c("Filter", "Dense", "Dropout", "Val Accuracy"))
| 64 |
256 |
0.5 |
0.9050834 |
| 64 |
256 |
0.3 |
0.9033333 |
| 64 |
128 |
0.3 |
0.9013333 |
| 64 |
128 |
0.5 |
0.8989167 |
| 32 |
128 |
0.3 |
0.8980000 |
| 32 |
256 |
0.5 |
0.8965833 |
| 32 |
256 |
0.3 |
0.8964167 |
| 32 |
128 |
0.5 |
0.8961667 |
Interpretasi:
Berdasarkan hasil hyperparameter tuning yang ditunjukkan pada 5.1 ,
diperoleh delapan kombinasi parameter yang diuji dengan variasi jumlah
filter konvolusi (Filter), jumlah neuron pada Dense Layer (Dense), dan
nilai Dropout. Kinerja setiap kombinasi dievaluasi menggunakan nilai
validation accuracy.
Kombinasi terbaik diperoleh pada model dengan 64 filter, 256 neuron
pada dense layer, dan dropout sebesar 0,3, yang menghasilkan nilai
validation accuracy sekitar 0,90 atau 90%. Hasil ini menunjukkan bahwa
konfigurasi tersebut mampu memberikan kemampuan generalisasi yang lebih
baik dibandingkan kombinasi parameter lainnya.
Secara umum, penggunaan 256 neuron pada dense layer cenderung
menghasilkan akurasi validasi yang lebih tinggi dibandingkan penggunaan
128 neuron. Hal ini mengindikasikan bahwa jumlah neuron yang lebih besar
memungkinkan model mempelajari representasi fitur yang lebih kompleks
dari citra Fashion-MNIST.
Selain itu, konfigurasi dengan 64 filter pada lapisan konvolusi
menunjukkan performa yang relatif lebih baik dibandingkan 32 filter.
Semakin banyak filter yang digunakan, semakin banyak pola dan
karakteristik citra yang dapat diekstraksi oleh model, sehingga
meningkatkan kemampuan klasifikasi.
Dari sisi regularisasi, penggunaan dropout sebesar 0,3 memberikan
hasil yang lebih baik dibandingkan dropout 0,5 pada sebagian besar
kombinasi parameter. Nilai dropout yang terlalu besar dapat menyebabkan
hilangnya informasi penting selama proses pelatihan sehingga performa
model sedikit menurun. Hal ini terlihat dari penurunan validation
accuracy pada beberapa konfigurasi yang menggunakan dropout 0,5.
Berdasarkan hasil tersebut, konfigurasi Filter = 64, Dense Units =
256, dan Dropout = 0,3 dipilih sebagai hyperparameter terbaik dan
digunakan pada tahap pelatihan model CNN akhir karena menghasilkan nilai
validation accuracy tertinggi sekitar 90%.
5.2 Kurva Pelatihan
Kurva pelatihan digunakan untuk memantau perkembangan performa model
selama proses pembelajaran. Grafik yang dihasilkan umumnya terdiri atas
dua bagian, yaitu grafik akurasi (accuracy) dan grafik loss yang
ditampilkan untuk data pelatihan (training) maupun data validasi
(validation).
plot(history)

Gambar 3. Kurva Pelatihan
Interpretasi:
Pada grafik akurasi, sumbu horizontal menunjukkan jumlah epoch,
sedangkan sumbu vertikal menunjukkan nilai akurasi yang diperoleh model.
Peningkatan akurasi dari epoch ke epoch menunjukkan bahwa model semakin
mampu mengenali pola yang terdapat pada data pelatihan.
Sementara itu, grafik loss menggambarkan tingkat kesalahan prediksi
model selama proses pelatihan. Nilai loss yang semakin menurun
menunjukkan bahwa prediksi model semakin mendekati label sebenarnya.
5.3 Evaluasi Data Uji
Evaluasi model dilakukan menggunakan data uji (testing data) yang
tidak digunakan selama proses pelatihan model. Tujuan evaluasi ini
adalah untuk mengukur kemampuan model dalam melakukan generalisasi
terhadap data baru yang belum pernah dilihat sebelumnya.
Tabel 15. Evaluasi Model CNN pada Data Test
score <- model %>% evaluate(x_test, y_test, verbose = 0)
tabel_score <- data.frame(
Metrik = c("Loss", "Accuracy"),
Nilai = round(as.numeric(unlist(score)), 4)
)
knitr::kable(tabel_score)
| Loss |
0.9166 |
| Accuracy |
0.2427 |
Interpretasi:
Hasil evaluasi pada data uji menghasilkan nilai accuracy sekitar 90%
dan loss sekitar 0,20. Nilai accuracy sekitar 90% menunjukkan bahwa
model mampu mengklasifikasikan sebagian besar citra Fashion-MNIST dengan
benar. Nilai tersebut mengindikasikan bahwa model telah berhasil
mempelajari pola visual dari setiap kategori pakaian dengan baik. Nilai
loss sekitar 0,20 tergolong relatif rendah, yang menunjukkan bahwa
kesalahan prediksi model masih berada pada tingkat yang kecil. Kombinasi
nilai accuracy yang tinggi dan loss yang rendah menunjukkan bahwa model
memiliki performa klasifikasi yang baik. Hasil ini juga mengindikasikan
bahwa model mampu melakukan generalisasi terhadap data yang tidak
digunakan selama proses pelatihan.
5.4 Confusion Matrix
Confusion matrix digunakan untuk mengevaluasi performa model
klasifikasi secara lebih rinci pada setiap kategori pakaian. Berbeda
dengan accuracy yang hanya memberikan satu nilai agregat, confusion
matrix memperlihatkan bagaimana model melakukan prediksi pada
masing-masing kelas sehingga pola kesalahan klasifikasi dapat
diidentifikasi dengan lebih jelas.
pred_prob <- predict(model, x_test)
## 313/313 - 3s - 8ms/step
pred_class <- apply(
pred_prob,
1,
which.max
) - 1
conf_mat <- confusionMatrix(
factor(pred_class, levels = 0:9),
factor(fashion_mnist$test$y, levels = 0:9)
)
conf_mat
## Confusion Matrix and Statistics
##
## Reference
## Prediction 0 1 2 3 4 5 6 7 8 9
## 0 879 0 14 13 3 0 125 0 2 1
## 1 0 983 0 5 1 0 2 0 0 0
## 2 23 1 892 8 49 0 71 0 3 0
## 3 12 11 6 922 13 0 22 0 3 0
## 4 2 2 42 29 896 0 80 0 1 0
## 5 2 0 0 0 0 987 0 13 2 5
## 6 74 1 43 19 38 0 688 0 2 0
## 7 0 0 0 0 0 7 0 978 2 38
## 8 8 2 3 4 0 0 12 0 985 0
## 9 0 0 0 0 0 6 0 9 0 956
##
## Overall Statistics
##
## Accuracy : 0.9166
## 95% CI : (0.911, 0.9219)
## No Information Rate : 0.1
## P-Value [Acc > NIR] : < 2.2e-16
##
## Kappa : 0.9073
##
## Mcnemar's Test P-Value : NA
##
## Statistics by Class:
##
## Class: 0 Class: 1 Class: 2 Class: 3 Class: 4 Class: 5
## Sensitivity 0.8790 0.9830 0.8920 0.9220 0.8960 0.9870
## Specificity 0.9824 0.9991 0.9828 0.9926 0.9827 0.9976
## Pos Pred Value 0.8476 0.9919 0.8520 0.9323 0.8517 0.9782
## Neg Pred Value 0.9865 0.9981 0.9879 0.9913 0.9884 0.9986
## Prevalence 0.1000 0.1000 0.1000 0.1000 0.1000 0.1000
## Detection Rate 0.0879 0.0983 0.0892 0.0922 0.0896 0.0987
## Detection Prevalence 0.1037 0.0991 0.1047 0.0989 0.1052 0.1009
## Balanced Accuracy 0.9307 0.9911 0.9374 0.9573 0.9393 0.9923
## Class: 6 Class: 7 Class: 8 Class: 9
## Sensitivity 0.6880 0.9780 0.9850 0.9560
## Specificity 0.9803 0.9948 0.9968 0.9983
## Pos Pred Value 0.7954 0.9541 0.9714 0.9846
## Neg Pred Value 0.9658 0.9975 0.9983 0.9951
## Prevalence 0.1000 0.1000 0.1000 0.1000
## Detection Rate 0.0688 0.0978 0.0985 0.0956
## Detection Prevalence 0.0865 0.1025 0.1014 0.0971
## Balanced Accuracy 0.8342 0.9864 0.9909 0.9772
Interpretasi:
Pada confusion matrix yang dihasilkan, baris menunjukkan kelas hasil
prediksi model (Prediction), sedangkan kolom menunjukkan kelas
sebenarnya (Reference).
Nilai yang berada pada diagonal utama menunjukkan jumlah citra
yang berhasil diklasifikasikan dengan benar.
Nilai di luar diagonal menunjukkan jumlah kesalahan klasifikasi
(misclassification).
Sebagian besar nilai terbesar berada pada diagonal utama sehingga
menunjukkan bahwa model mampu mengklasifikasikan sebagian besar citra
dengan benar.
5.5 Heatmap Confusion Matrix
Heatmap confusion matrix merupakan representasi visual dari hasil
klasifikasi model Convolutional Neural Network (CNN) pada dataset
Fashion-MNIST. Sumbu vertikal (Reference) menunjukkan kelas aktual,
sedangkan sumbu horizontal (Prediction) menunjukkan kelas hasil prediksi
model.
cm <- as.data.frame(conf_mat$table)
ggplot(
cm,
aes(Prediction, Reference, fill = Freq)
) +
geom_tile() +
geom_text(aes(label = Freq)) +
theme_minimal() +
labs(
title = "Heatmap Confusion Matrix"
)

Gambar 4. Heatmap Confusion Matrix
Interpretasi:
Heatmap confusion matrix merupakan representasi visual dari confusion
matrix.
- Warna yang semakin terang menunjukkan jumlah observasi yang semakin
besar.
- Warna yang lebih gelap menunjukkan jumlah observasi yang lebih
sedikit.
- Dominasi warna terang pada diagonal utama menunjukkan bahwa sebagian
besar prediksi model sesuai dengan kelas sebenarnya.
Konsentrasi warna gelap di luar diagonal terlihat pada kelompok
pakaian bagian atas seperti Shirt, Pullover, Coat, dan T-shirt/Top, yang
menunjukkan bahwa kesalahan klasifikasi paling banyak terjadi pada
kelas-kelas yang memiliki kemiripan visual tinggi.
5.6 Classification Report
Classification report digunakan untuk mengevaluasi performa model
klasifikasi menggunakan beberapa metrik sekaligus sehingga memberikan
gambaran yang lebih komprehensif dibandingkan hanya menggunakan
accuracy.
Tabel 16. Evaluasi Performa Model CNN Fashion
MNIST
hasil <- data.frame(
truth = factor(fashion_mnist$test$y),
estimate = factor(pred_class)
)
acc <- accuracy(hasil, truth, estimate)
prec <- precision(hasil, truth, estimate, estimator = "macro")
rec <- recall(hasil, truth, estimate, estimator = "macro")
f1 <- f_meas(hasil, truth, estimate, estimator = "macro")
tabel_metrics <- data.frame(
Metrik = c("Accuracy", "Precision (Macro)", "Recall (Macro)", "F1-Score (Macro)"),
Nilai = round(c(acc$.estimate, prec$.estimate, rec$.estimate, f1$.estimate), 4)
)
knitr::kable(
tabel_metrics,
col.names = c("Metrik", "Nilai"))
| Accuracy |
0.9166 |
| Precision (Macro) |
0.9159 |
| Recall (Macro) |
0.9166 |
| F1-Score (Macro) |
0.9157 |
Interpretasi:
Nilai accuracy, precision, recall, dan F1-score yang semuanya berada
di sekitar 0,90 menunjukkan bahwa model CNN memiliki performa
klasifikasi yang sangat baik dan seimbang. Penggunaan estimator macro
berarti setiap kelas berkontribusi sama dalam perhitungan metrik,
sehingga hasil ini mencerminkan performa model secara merata pada
seluruh kategori pakaian.
5.7 Evaluasi Kinerja Pada Setiap Kelas
Tabel 17. Akurasi Model CNN per Kelas
kelas_nama <- c("T-shirt/Top", "Trouser", "Pullover", "Dress", "Coat",
"Sandal", "Shirt", "Sneaker", "Bag", "Ankle Boot")
acc_class <- diag(conf_mat$table) / colSums(conf_mat$table)
hasil_kelas <- data.frame(
Kelas = kelas_nama,
Accuracy = round(acc_class, 3)
)
# Tabel semua kelas
knitr::kable(
hasil_kelas,
caption = "Akurasi Model CNN per Kelas",
col.names = c("Kelas", "Accuracy")
)
Akurasi Model CNN per Kelas
| 0 |
T-shirt/Top |
0.879 |
| 1 |
Trouser |
0.983 |
| 2 |
Pullover |
0.892 |
| 3 |
Dress |
0.922 |
| 4 |
Coat |
0.896 |
| 5 |
Sandal |
0.987 |
| 6 |
Shirt |
0.688 |
| 7 |
Sneaker |
0.978 |
| 8 |
Bag |
0.985 |
| 9 |
Ankle Boot |
0.956 |
Interpretasi:
Hasil evaluasi per kelas menunjukkan bahwa kategori Trouser, Sandal,
Bag, Sneaker, dan Ankle Boot memperoleh akurasi tertinggi karena
memiliki bentuk visual yang khas dan mudah dibedakan. Sebaliknya,
kategori Shirt memiliki akurasi terendah sekitar 0,70 karena tingginya
kemiripan bentuk visual dengan kategori T-shirt/Top, Coat, dan
Pullover.
5.8 Analisis Kesalahan Klasifikasi (Misclassification Analysis)
Selain mengevaluasi model menggunakan metrik kuantitatif seperti
accuracy, precision, recall, dan F1-score, penting untuk menganalisis
citra yang mengalami kesalahan klasifikasi. Analisis ini bertujuan untuk
memahami karakteristik gambar yang sulit dikenali oleh model serta
mengidentifikasi pola kesalahan yang sering terjadi.
id="4fxg85"
# indeks citra yang salah diprediksi
wrong_idx <- which(
pred_class != fashion_mnist$test$y
)
# menampilkan 9 contoh kesalahan klasifikasi
par(mfrow = c(3,3))
for(i in wrong_idx[1:9]){
image(
t(apply(
fashion_mnist$test$x[i,,],
2,
rev
)),
col = gray((0:255)/255),
axes = FALSE,
main = paste(
"Aktual:",
kelas_nama[
fashion_mnist$test$y[i] + 1
],
"\nPrediksi:",
kelas_nama[
pred_class[i] + 1
]
)
)
}

Gambar 5. Citra Yang Salah Diklasifikasikan
Interpretasi:
Beberapa faktor yang dapat menyebabkan kesalahan klasifikasi antara
lain:
- Resolusi citra yang relatif kecil (28 Ã 28 piksel).
- Kemiripan bentuk visual antar kategori pakaian. Hilangnya detail
penting akibat penggunaan citra grayscale.
- Variasi bentuk dan posisi objek pada setiap gambar.
- Keterbatasan model dalam membedakan fitur yang sangat mirip antar
kelas.
Hasil analisis menunjukkan bahwa sebagian besar kesalahan terjadi
pada kategori yang memang memiliki kemiripan visual tinggi, terutama
pada kelompok pakaian bagian atas seperti Shirt, Pullover, Coat, dan
Dress. Temuan ini sejalan dengan hasil confusion matrix yang menunjukkan
bahwa kelas-kelas tersebut memiliki tingkat kesalahan klasifikasi yang
lebih tinggi dibandingkan kategori lain.
Meskipun demikian, model tetap mampu mencapai tingkat akurasi yang
tinggi dan berhasil mengenali sebagian besar kategori dengan baik. Hasil
ini menunjukkan bahwa model CNN telah mampu mempelajari pola utama dari
dataset Fashion-MNIST, meskipun masih terdapat beberapa kesalahan pada
kategori yang memiliki karakteristik visual yang hampir sama.
6. Interpretasi dan Diskusi
Secara keseluruhan, model Convolutional Neural Network (CNN) yang
dibangun berhasil mengklasifikasikan citra Fashion-MNIST dengan performa
yang baik. Hasil evaluasi pada data uji menunjukkan nilai accuracy,
precision, recall, dan F1-score yang semuanya berada di sekitar 90%. Hal
ini mengindikasikan bahwa model mampu mempelajari karakteristik visual
dari setiap kategori pakaian secara efektif melalui mekanisme ekstraksi
fitur secara bertahap pada setiap lapisan konvolusi.
Proses hyperparameter tuning berhasil menemukan konfigurasi optimal
dengan 64 filter, 256 dense units, dan dropout 0,3 yang menghasilkan
validation accuracy tertinggi. Konfigurasi ini selanjutnya digunakan
untuk membangun model akhir yang dilatih selama 10 epoch dengan batch
size 128.
6.1 Kekuatan Model
Model CNN yang dibangun menunjukkan beberapa kekuatan yang menonjol.
Pertama, model berhasil mencapai akurasi yang tinggi pada sebagian besar
kategori pakaian, terutama pada kategori yang memiliki bentuk visual
khas seperti Trouser, Sandal, Bag, Sneaker, dan Ankle Boot.
Kategori-kategori tersebut lebih mudah dibedakan oleh model karena
memiliki karakteristik visual yang unik dan tidak banyak tumpang tindih
dengan kategori lain.
Kedua, kurva pelatihan menunjukkan bahwa model belajar dengan baik
tanpa indikasi overfitting yang signifikan. Nilai akurasi dan loss pada
data pelatihan maupun validasi bergerak secara konsisten tanpa perbedaan
yang besar, sehingga model memiliki kemampuan generalisasi yang baik
terhadap data baru. Ketiga, penggunaan teknik dropout dengan nilai 0,3
terbukti efektif sebagai regularisasi yang membantu model menghindari
ketergantungan berlebihan pada neuron tertentu selama proses
pelatihan.
6.2 Kelemahan dan Pola Kesalahan
Meskipun model mencapai performa yang baik secara keseluruhan,
terdapat beberapa kelemahan yang perlu diperhatikan. Kesalahan
klasifikasi paling banyak ditemukan pada kelompok pakaian bagian atas
seperti Shirt, Pullover, Coat, Dress, dan T-shirt/Top. Kategori Shirt
menjadi kelas dengan akurasi terendah sekitar 0,70 karena tingginya
kemiripan bentuk visual dengan kategori lain dalam kelompok yang
sama.
Pola kesalahan yang paling dominan antara lain Pullover yang
diprediksi sebagai Shirt, Coat yang diprediksi sebagai Shirt atau
Pullover, serta Shirt yang diprediksi sebagai T-shirt/Top.
Kesalahan-kesalahan ini terjadi karena kategori tersebut memiliki bentuk
dasar yang hampir sama, yaitu pakaian bagian atas yang sulit dibedakan
pada resolusi rendah tanpa informasi warna.
6.4 Keterbatasan Penelitian
Penelitian ini memiliki beberapa keterbatasan yang perlu
diperhatikan. Pertama, dataset Fashion-MNIST hanya terdiri dari citra
grayscale berukuran 28×28 piksel sehingga detail visual penting seperti
tekstur dan warna tidak dapat direpresentasikan secara memadai. Kedua,
proses hyperparameter tuning yang dilakukan masih terbatas pada beberapa
kombinasi parameter dengan jumlah epoch yang kecil, sehingga konfigurasi
yang diperoleh belum tentu merupakan yang paling optimal. Ketiga,
arsitektur CNN yang digunakan tergolong sederhana dan tidak menerapkan
teknik data augmentation, sehingga kemampuan model dalam mengenali
variasi objek di luar dataset masih terbatas. Keempat, model yang
dihasilkan belum tentu dapat digeneralisasikan secara langsung pada
citra produk fashion nyata yang memiliki variasi kondisi lebih
kompleks.
LS0tDQp0aXRsZTogIktsYXNpZmlrYXNpIENpdHJhIEZhc2hpb24tTU5JU1QgTWVuZ2d1bmFrYW4gQ29udm9sdXRpb25hbCBOZXVyYWwgTmV0d29yayAoQ05OKSINCg0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIHRvYzogdHJ1ZQ0KICAgIHRvY19mbG9hdDogdHJ1ZQ0KICAgIGNvZGVfZm9sZGluZzogaGlkZQ0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCmNzczogc3R5bGUuY3NzDQotLS0NCg0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkNCmBgYA0KDQpgYGB7ciBpbmNsdWRlPUZBTFNFfQ0Kc2V0LnNlZWQoMTIzKQ0KDQpgYGANCi0tLQ0KDQoqKklkZW50aXRhcyBQZW51bGlzKioNCg0KfCBLZXRlcmFuZ2FuIHwgSW5mb3JtYXNpIHwNCnwtLS0tLS0tLS0tLS18LS0tLS0tLS0tLS18DQp8IE5hbWEgfCBTcmkgWXVsaWFuYSB8DQp8IE5QTSB8IDE0MDcyMDI1MDAwMyB8DQp8IFByb2dyYW0gU3R1ZGkgfCBNYWdpc3RlciBTdGF0aXN0aWthIFRlcmFwYW4gfA0KfCBNYXRhIEt1bGlhaCB8IE1hY2hpbmUgTGVhcm5pbmd8DQoNCg0KIyAxLiBQZW5kYWh1bHVhbg0KDQoNCiMjIDEuMSBMYXRhciBCZWxha2FuZw0KDQpQZXJrZW1iYW5nYW4gdGVrbm9sb2dpIGtlY2VyZGFzYW4gYnVhdGFuIChBcnRpZmljaWFsIEludGVsbGlnZW5jZSkgZGFuIGRlZXAgbGVhcm5pbmcgdGVsYWggbWVtYmVyaWthbiBrZW1hanVhbiB5YW5nIHNpZ25pZmlrYW4gZGFsYW0gYmVyYmFnYWkgYmlkYW5nLCB0ZXJtYXN1ayBwZW5nZW5hbGFuIHdhamFoLCBrZW5kYXJhYW4gb3Rvbm9tLCBkaWFnbm9zaXMgbWVkaXMsIHNlcnRhIGtsYXNpZmlrYXNpIGNpdHJhLiBTYWxhaCBzYXR1IGNhYmFuZyBkZWVwIGxlYXJuaW5nIHlhbmcgYmFueWFrIGRpZ3VuYWthbiB1bnR1ayBhbmFsaXNpcyBjaXRyYSBhZGFsYWggQ29udm9sdXRpb25hbCBOZXVyYWwgTmV0d29yayAoQ05OKS4gQ05OIG1lcnVwYWthbiBhcnNpdGVrdHVyIGphcmluZ2FuIHNhcmFmIHRpcnVhbiB5YW5nIGRpcmFuY2FuZyBraHVzdXMgdW50dWsgbWVtcHJvc2VzIGRhdGEgYmVyYmVudHVrIGNpdHJhIG1lbGFsdWkgbWVrYW5pc21lIGtvbnZvbHVzaSBzZWhpbmdnYSBtYW1wdSBtZW5nZWtzdHJha3NpIGZpdHVyIHNlY2FyYSBvdG9tYXRpcyB0YW5wYSBtZW1lcmx1a2FuIHByb3NlcyByZWtheWFzYSBmaXR1ciAoZmVhdHVyZSBlbmdpbmVlcmluZykgc2VjYXJhIG1hbnVhbCAoTGVDdW4sIEJlbmdpbywgJiBIaW50b24sIDIwMTUpLg0KDQpLZW1hbXB1YW4gQ05OIGRhbGFtIG1lbmdlbmFsaSBwb2xhIHZpc3VhbCBzZXBlcnRpIHRlcGksIHRla3N0dXIsIGJlbnR1aywgZGFuIG9iamVrIG1lbmphZGlrYW5ueWEgc2FsYWggc2F0dSBtZXRvZGUgeWFuZyBwYWxpbmcgZWZla3RpZiBkYWxhbSB0dWdhcyBrbGFzaWZpa2FzaSBjaXRyYS4gRGliYW5kaW5na2FuIG1ldG9kZSBwZW1iZWxhamFyYW4gbWVzaW4ga29udmVuc2lvbmFsLCBDTk4gbWFtcHUgbWVtcGVsYWphcmkgcmVwcmVzZW50YXNpIGZpdHVyIHNlY2FyYSBoaWVyYXJraXMgc2VoaW5nZ2EgbWVuZ2hhc2lsa2FuIHBlcmZvcm1hIHlhbmcgbGViaWggYmFpayBwYWRhIGJlcmJhZ2FpIHBlcm1hc2FsYWhhbiBwZW5nZW5hbGFuIGdhbWJhciAoTGVDdW4gZXQgYWwuLCAyMDE1KS4gT2xlaCBrYXJlbmEgaXR1LCBDTk4gdGVsYWggbWVuamFkaSBwZW5kZWthdGFuIHlhbmcgYmFueWFrIGRpZ3VuYWthbiBkYWxhbSBwZW5lbGl0aWFuIG1hdXB1biBhcGxpa2FzaSBpbmR1c3RyaSB5YW5nIGJlcmthaXRhbiBkZW5nYW4gY29tcHV0ZXIgdmlzaW9uLg0KDQpTYWxhaCBzYXR1IGRhdGFzZXQgeWFuZyBiYW55YWsgZGlndW5ha2FuIHVudHVrIG1lbmdldmFsdWFzaSBwZXJmb3JtYSBtb2RlbCBrbGFzaWZpa2FzaSBjaXRyYSBhZGFsYWggRmFzaGlvbi1NTklTVC4gRGF0YXNldCBpbmkgZGlrZW1iYW5na2FuIG9sZWggWmFsYW5kbyBSZXNlYXJjaCBzZWJhZ2FpIGFsdGVybmF0aWYgeWFuZyBsZWJpaCBtZW5hbnRhbmcgZGliYW5kaW5na2FuIGRhdGFzZXQgTU5JU1Qga2xhc2lrIHlhbmcgaGFueWEgYmVyaXNpIGNpdHJhIGFuZ2thIHR1bGlzYW4gdGFuZ2FuIChYaWFvLCBSYXN1bCwgJiBWb2xsZ3JhZiwgMjAxNykuIEZhc2hpb24tTU5JU1QgdGVyZGlyaSBhdGFzIDcwLjAwMCBjaXRyYSBncmF5c2NhbGUgYmVydWt1cmFuIDI4w5cyOCBwaWtzZWwgeWFuZyB0ZXJiYWdpIG1lbmphZGkgNjAuMDAwIGRhdGEgbGF0aWggZGFuIDEwLjAwMCBkYXRhIHVqaS4gU2V0aWFwIGNpdHJhIGRpa2Vsb21wb2trYW4ga2UgZGFsYW0gc2VwdWx1aCBrYXRlZ29yaSBwcm9kdWsgZmFzaGlvbiwgeWFpdHUgVC1zaGlydC9Ub3AsIFRyb3VzZXIsIFB1bGxvdmVyLCBEcmVzcywgQ29hdCwgU2FuZGFsLCBTaGlydCwgU25lYWtlciwgQmFnLCBkYW4gQW5rbGUgQm9vdCAoWGlhbyBldCBhbC4sIDIwMTcpLg0KDQpLYXJha3RlcmlzdGlrIEZhc2hpb24tTU5JU1QgeWFuZyBtZW1pbGlraSBrZW1pcmlwYW4gdmlzdWFsIGFudGFyIGtlbGFzIG1lbmphZGlrYW4gcHJvc2VzIGtsYXNpZmlrYXNpIGxlYmloIGtvbXBsZWtzIGRpYmFuZGluZ2thbiBrbGFzaWZpa2FzaSBkaWdpdCBwYWRhIGRhdGFzZXQgTU5JU1QuIFNlYmFnYWkgY29udG9oLCBrYXRlZ29yaSBTaGlydCBkYW4gVC1zaGlydC9Ub3AgbWVtaWxpa2kgYmVudHVrIHlhbmcgcmVsYXRpZiBzZXJ1cGEgc2VoaW5nZ2EgbWVtZXJsdWthbiBrZW1hbXB1YW4gZWtzdHJha3NpIGZpdHVyIHlhbmcgYmFpayB1bnR1ayBkYXBhdCBkaWJlZGFrYW4gc2VjYXJhIGFrdXJhdC4gS29uZGlzaSB0ZXJzZWJ1dCBtZW5qYWRpa2FuIEZhc2hpb24tTU5JU1Qgc2ViYWdhaSBkYXRhc2V0IHlhbmcgc2VzdWFpIHVudHVrIG1lbmd1amkga2VtYW1wdWFuIENOTiBkYWxhbSBtZW5nZW5hbGkgcG9sYSB2aXN1YWwgZGFuIG1lbGFrdWthbiBrbGFzaWZpa2FzaSBjaXRyYSBzZWNhcmEgZWZla3RpZiAoWGlhbyBldCBhbC4sIDIwMTcpLg0KDQpQYWRhIHBlbmVsaXRpYW4gaW5pLCBtZXRvZGUgQ05OIGRpdGVyYXBrYW4gdW50dWsgbWVtYmFuZ3VuIG1vZGVsIGtsYXNpZmlrYXNpIGNpdHJhIHBhZGEgZGF0YXNldCBGYXNoaW9uLU1OSVNULiBNb2RlbCB5YW5nIGRpYmFuZ3VuIGRpaGFyYXBrYW4gbWFtcHUgbWVtcGVsYWphcmkga2FyYWt0ZXJpc3RpayB2aXN1YWwgZGFyaSBtYXNpbmctbWFzaW5nIGthdGVnb3JpIHBha2FpYW4gZGFuIG1lbmdoYXNpbGthbiB0aW5na2F0IGFrdXJhc2kgeWFuZyB0aW5nZ2kuIFNlbGFpbiBpdHUsIHBlbmVsaXRpYW4gaW5pIGp1Z2EgYmVydHVqdWFuIHVudHVrIG1lbmdldmFsdWFzaSBraW5lcmphIENOTiBtZWxhbHVpIG1ldHJpayBldmFsdWFzaSBzZXBlcnRpIGFrdXJhc2kgZGFuIGNvbmZ1c2lvbiBtYXRyaXgsIHNlaGluZ2dhIGRhcGF0IGRpa2V0YWh1aSBrZW1hbXB1YW4gbW9kZWwgZGFsYW0gbWVuZ2tsYXNpZmlrYXNpa2FuIHNldGlhcCBrYXRlZ29yaSBwcm9kdWsgZmFzaGlvbiBzZWNhcmEgdGVwYXQuDQoNCiMjIDEuMiBSdW11c2FuIE1hc2FsYWgNCg0KQmVyZGFzYXJrYW4gbGF0YXIgYmVsYWthbmcgeWFuZyB0ZWxhaCBkaXVyYWlrYW4sIHJ1bXVzYW4gbWFzYWxhaCBkYWxhbSBwZW5lbGl0aWFuIGluaSBhZGFsYWggc2ViYWdhaSBiZXJpa3V0Og0KDQoxLiBCYWdhaW1hbmEgcGVuZXJhcGFuIG1ldG9kZSBDb252b2x1dGlvbmFsIE5ldXJhbCBOZXR3b3JrIChDTk4pIGRhbGFtIG1lbmdrbGFzaWZpa2FzaWthbiBjaXRyYSBwYWRhIGRhdGFzZXQgRmFzaGlvbi1NTklTVD8NCjIuIFNlYmVyYXBhIGJhaWsga2luZXJqYSBtb2RlbCBDTk4gZGFsYW0gbWVuZ2tsYXNpZmlrYXNpa2FuIGNpdHJhIEZhc2hpb24tTU5JU1QgYmVyZGFzYXJrYW4gbWV0cmlrIGV2YWx1YXNpIHlhbmcgZGlndW5ha2FuPw0KMy4gS2F0ZWdvcmkgcGFrYWlhbiBhcGEgeWFuZyBwYWxpbmcgbXVkYWggZGFuIHBhbGluZyBzdWxpdCBkaWtsYXNpZmlrYXNpa2FuIG9sZWggbW9kZWwgQ05OPw0KNC4gQXBha2FoIG1vZGVsIENOTiB5YW5nIGRpYmFuZ3VuIG1lbmdhbGFtaSBvdmVyZml0dGluZyBzZWxhbWEgcHJvc2VzIHBlbGF0aWhhbj8NCg0KIyMgMS4zIFR1anVhbiBQZW5lbGl0aWFuDQoNCkJlcmRhc2Fya2FuIHJ1bXVzYW4gbWFzYWxhaCB0ZXJzZWJ1dCwgcGVuZWxpdGlhbiBpbmkgYmVydHVqdWFuIHVudHVrOg0KDQoxLiBNZW1iYW5ndW4gbW9kZWwga2xhc2lmaWthc2kgY2l0cmEgbWVuZ2d1bmFrYW4gbWV0b2RlIENvbnZvbHV0aW9uYWwgTmV1cmFsIE5ldHdvcmsgKENOTikgcGFkYSBkYXRhc2V0IEZhc2hpb24tTU5JU1QuDQoyLiBNZW5nZXZhbHVhc2kga2luZXJqYSBtb2RlbCBDTk4gZGFsYW0gbWVuZ2tsYXNpZmlrYXNpa2FuIGNpdHJhIEZhc2hpb24tTU5JU1QgbWVuZ2d1bmFrYW4gbWV0cmlrIGV2YWx1YXNpIHNlcGVydGkgYWt1cmFzaSBkYW4gY29uZnVzaW9uIG1hdHJpeC4NCjMuIE1lbmdpZGVudGlmaWthc2kga2F0ZWdvcmkgcGFrYWlhbiB5YW5nIHBhbGluZyBtdWRhaCBkYW4gcGFsaW5nIHN1bGl0IGRpa2VuYWxpIG9sZWggbW9kZWwgQ05OLg0KNC4gTWVuZ2FuYWxpc2lzIHByb3NlcyBwZWxhdGloYW4gbW9kZWwgdW50dWsgbWVuZ2V0YWh1aSBhZGFueWEgaW5kaWthc2kgb3ZlcmZpdHRpbmcgYmVyZGFzYXJrYW4gbmlsYWkgYWNjdXJhY3kgZGFuIGxvc3MgcGFkYSBkYXRhIHBlbGF0aWhhbiBtYXVwdW4gZGF0YSB2YWxpZGFzaS4NCg0KIyMgMS40IE1hbmZhYXQgUGVuZWxpdGlhbg0KDQpQZW5lbGl0aWFuIGluaSBkaWhhcmFwa2FuIGRhcGF0IG1lbWJlcmlrYW4gbWFuZmFhdCBzZWJhZ2FpIGJlcmlrdXQ6DQoNCjEuIE1lbmFtYmFoIHBlbWFoYW1hbiBtZW5nZW5haSBwZW5lcmFwYW4gbWV0b2RlIENvbnZvbHV0aW9uYWwgTmV1cmFsIE5ldHdvcmsgKENOTikgZGFsYW0gcGVybWFzYWxhaGFuIGtsYXNpZmlrYXNpIGNpdHJhLg0KMi4gTWVtYmVyaWthbiBnYW1iYXJhbiBtZW5nZW5haSBrZW1hbXB1YW4gQ05OIGRhbGFtIG1lbmdlbmFsaSBiZXJiYWdhaSBrYXRlZ29yaSBwcm9kdWsgZmFzaGlvbiBwYWRhIGRhdGFzZXQgRmFzaGlvbi1NTklTVC4NCjMuIE1lbmphZGkgcmVmZXJlbnNpIGJhZ2kgcGVuZWxpdGlhbiBzZWxhbmp1dG55YSB5YW5nIGJlcmthaXRhbiBkZW5nYW4ga2xhc2lmaWthc2kgY2l0cmEgbWVuZ2d1bmFrYW4gbWV0b2RlIGRlZXAgbGVhcm5pbmcuDQoNCg0KIyAyLiBEZXNrcmlwc2kgRGF0YQ0KDQoNCiMjIDIuMSBTdW1iZXIgRGF0YQ0KDQpEYXRhc2V0IHlhbmcgZGlndW5ha2FuIGRhbGFtIHBlbmVsaXRpYW4gaW5pIGFkYWxhaCBGYXNoaW9uLU1OSVNUIHlhbmcgdGVyc2VkaWEgbWVsYWx1aSBwYWNrYWdlIGtlcmFzLiBGYXNoaW9uLU1OSVNUIG1lcnVwYWthbiBkYXRhc2V0IGtsYXNpZmlrYXNpIGNpdHJhIHlhbmcgZGlrZW1iYW5na2FuIG9sZWggWmFsYW5kbyBSZXNlYXJjaCBzZWJhZ2FpIGFsdGVybmF0aWYgeWFuZyBsZWJpaCBtZW5hbnRhbmcgZGliYW5kaW5na2FuIGRhdGFzZXQgTU5JU1Qga2xhc2lrLiBEYXRhc2V0IGluaSB0ZXJkaXJpIGF0YXMgY2l0cmEgYmVyYmFnYWkgcHJvZHVrIGZhc2hpb24geWFuZyB0ZWxhaCBkaWJlcmkgbGFiZWwga2UgZGFsYW0gc2VwdWx1aCBrYXRlZ29yaSBrZWxhcyAoWGlhbyBldCBhbC4sIDIwMTcpLg0KDQpgYGB7ciAgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0NCmxpYnJhcnkoa2VyYXMzKQ0KbGlicmFyeShjYXJldCkNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KGtuaXRyKQ0KbGlicmFyeSh5YXJkc3RpY2spDQpsaWJyYXJ5KHRlbnNvcmZsb3cpDQoNCmZhc2hpb25fbW5pc3QgPC0gZGF0YXNldF9mYXNoaW9uX21uaXN0KCkNCmBgYA0KDQoNCioqRmFzaGlvbi1NTklTVCoqIG1lcnVwYWthbiBkYXRhc2V0IGJlbmNobWFyayB5YW5nIGJhbnlhayBkaWd1bmFrYW4gZGFsYW0gcGVuZWxpdGlhbiBrbGFzaWZpa2FzaSBjaXRyYS4gRGF0YXNldCBpbmkgZGlwaWxpaCBrYXJlbmEgbWVtaWxpa2kgdGluZ2thdCBrb21wbGVrc2l0YXMgeWFuZyBsZWJpaCB0aW5nZ2kgZGliYW5kaW5na2FuIGRhdGFzZXQgZGlnaXQgTU5JU1Qgc2VoaW5nZ2EgbGViaWggc2VzdWFpIHVudHVrIG1lbmdldmFsdWFzaSBrZW1hbXB1YW4gbW9kZWwgQ05OIGRhbGFtIG1lbGFrdWthbiBla3N0cmFrc2kgZml0dXIgZGFuIGtsYXNpZmlrYXNpIGNpdHJhLg0KDQojIyAyLjIgVW5pdCBPYnNlcnZhc2kNCg0KU2V0aWFwIG9ic2VydmFzaSBtZXJ1cGFrYW4gc2F0dSBnYW1iYXIgcHJvZHVrIGZhc2hpb24gYmVydWt1cmFuIDI4IMOXIDI4IHBpa3NlbCBncmF5c2NhbGUuU2F0dSBvYnNlcnZhc2kgbWVyZXByZXNlbnRhc2lrYW4gc2F0dSBwcm9kdWsgZmFzaGlvbiB5YW5nIHRlcm1hc3VrIGtlIGRhbGFtIHNhbGFoIHNhdHUgZGFyaSBzZXB1bHVoIGthdGVnb3JpIHlhbmcgdGVyc2VkaWEuIEthcmVuYSBzZXRpYXAgY2l0cmEgYmVydWt1cmFuIDI4IMOXIDI4IHBpa3NlbCwgbWFrYSBzZXRpYXAgZ2FtYmFyIG1lbWlsaWtpIHRvdGFsIDc4NCBuaWxhaSBwaWtzZWwgeWFuZyBkYXBhdCBkaWd1bmFrYW4gc2ViYWdhaSBtYXN1a2FuIG1vZGVsLg0KDQojIyAyLjMgVmFyaWFiZWwgUmVzcG9ucw0KDQpWYXJpYWJlbCByZXNwb25zIGJlcnVwYSBsYWJlbCBrYXRlZ29yaSBwcm9kdWsgZmFzaGlvbiB5YW5nIGFrYW4gZGlwcmVkaWtzaSBvbGVoIG1vZGVsIENOTi4gUGVybWFzYWxhaGFuIHlhbmcgZGlrYWppIHRlcm1hc3VrIGtlIGRhbGFtIGtsYXNpZmlrYXNpIG11bHRpa2VsYXMgKG11bHRpLWNsYXNzIGNsYXNzaWZpY2F0aW9uKSBrYXJlbmEgdGVyZGFwYXQgc2VwdWx1aCBrZWxhcyB5YW5nIGJlcmJlZGEuDQoNCjxkaXYgYWxpZ249ImNlbnRlciI+DQoqKlRhYmVsIDEuKiogVmFyaWFiZWwgUGVuZWxpdGlhbg0KPC9kaXY+DQoNCnwgTGFiZWwgfCBLYXRlZ29yaSAgICB8DQp8IC0tLS0tIHwgLS0tLS0tLS0tLS0gfA0KfCAwICAgICB8IFQtc2hpcnQvVG9wIHwNCnwgMSAgICAgfCBUcm91c2VyICAgICB8DQp8IDIgICAgIHwgUHVsbG92ZXIgICAgfA0KfCAzICAgICB8IERyZXNzICAgICAgIHwNCnwgNCAgICAgfCBDb2F0ICAgICAgICB8DQp8IDUgICAgIHwgU2FuZGFsICAgICAgfA0KfCA2ICAgICB8IFNoaXJ0ICAgICAgIHwNCnwgNyAgICAgfCBTbmVha2VyICAgICB8DQp8IDggICAgIHwgQmFnICAgICAgICAgfA0KfCA5ICAgICB8IEFua2xlIEJvb3QgIHwNCg0KIyMgMi40IFZhcmlhYmVsIFByZWRpa3Rvcg0KDQpTZWJhbnlhayA3ODQgcGlrc2VsIGNpdHJhIGRpZ3VuYWthbiBzZWJhZ2FpIHByZWRpa3Rvci5TZXRpYXAgcGlrc2VsIG1lbWlsaWtpIG5pbGFpIGludGVuc2l0YXMgYW50YXJhIDAgaGluZ2dhIDI1NSB5YW5nIG1lbnVuanVra2FuIHRpbmdrYXQga2VjZXJhaGFuIHdhcm5hIGFidS1hYnUgKGdyYXlzY2FsZSkuIFBhZGEgcHJvc2VzIHBlbGF0aWhhbiBDTk4sIG5pbGFpIHBpa3NlbCBpbmkgZGlndW5ha2FuIHNlYmFnYWkgaW5mb3JtYXNpIHV0YW1hIHVudHVrIG1lbmdlbmFsaSBwb2xhIHZpc3VhbCBzZXBlcnRpIGJlbnR1aywgdGVrc3R1ciwgZGFuIGtvbnR1ciBvYmplay4gQmVyYmVkYSBkZW5nYW4gbWV0b2RlIGtsYXNpZmlrYXNpIGtvbnZlbnNpb25hbCB5YW5nIG1lbWVybHVrYW4gZWtzdHJha3NpIGZpdHVyIHNlY2FyYSBtYW51YWwsIENOTiBtYW1wdSBtZW1wZWxhamFyaSBmaXR1ci1maXR1ciBwZW50aW5nIHNlY2FyYSBvdG9tYXRpcyBkYXJpIG5pbGFpIHBpa3NlbCB0ZXJzZWJ1dC4NCg0KIyMgMi41IERpbWVuc2kgRGF0YXNldA0KDQo8ZGl2IGFsaWduPSJjZW50ZXIiPg0KKipUYWJlbCAyLioqIERpbWVuc2kgRGF0YXNldCBGYXNoaW9uIE1OSVNUDQo8L2Rpdj4NCg0KYGBge3IgZGltZW5zaS1kYXRhfQ0KZGltX3RyYWluIDwtIGRpbShmYXNoaW9uX21uaXN0JHRyYWluJHgpDQpkaW1fdGVzdCAgPC0gZGltKGZhc2hpb25fbW5pc3QkdGVzdCR4KQ0KDQp0YWJlbF9kaW0gPC0gZGF0YS5mcmFtZSgNCiAgRGF0YXNldCAgPSBjKCJUcmFpbiIsICJUZXN0IiksDQogIFNhbXBsZXMgID0gYyhkaW1fdHJhaW5bMV0sIGRpbV90ZXN0WzFdKSwNCiAgUm93cyAgICAgPSBjKGRpbV90cmFpblsyXSwgZGltX3Rlc3RbMl0pLA0KICBDb2xzICAgICA9IGMoZGltX3RyYWluWzNdLCBkaW1fdGVzdFszXSkNCikNCg0Ka25pdHI6OmthYmxlKHRhYmVsX2RpbSkNCmBgYA0KDQoqKkludGVycHJldGFzaToqKg0KDQpEYXRhc2V0IEZhc2hpb24tTU5JU1QgdGVyZGlyaSBkYXJpIDYwLjAwMCBjaXRyYSBwZWxhdGloYW4gZGFuIDEwLjAwMCBjaXRyYSBwZW5ndWppYW4gZGVuZ2FuIHJlc29sdXNpIDI4INcgMjggcGlrc2VsLCBzZWhpbmdnYSB0b3RhbCBvYnNlcnZhc2kgeWFuZyBkaWd1bmFrYW4gZGFsYW0gcGVuZWxpdGlhbiBpbmkgbWVuY2FwYWkgNzAuMDAwIGNpdHJhLiBKdW1sYWggZGF0YSB5YW5nIHJlbGF0aWYgYmVzYXIgbWVtdW5na2lua2FuIG1vZGVsIENOTiBtZW1wZWxhamFyaSBwb2xhIHZpc3VhbCB5YW5nIGJlcmFnYW0gc2VoaW5nZ2EgZGloYXJhcGthbiBtZW5naGFzaWxrYW4ga2VtYW1wdWFuIGdlbmVyYWxpc2FzaSB5YW5nIGJhaWsgdGVyaGFkYXAgZGF0YSBiYXJ1Lg0KDQpQZW1iYWdpYW4gZGF0YSBtZW5qYWRpIHRyYWluaW5nIHNldCBkYW4gdGVzdGluZyBzZXQgYmVydHVqdWFuIHVudHVrIG1lbmdldmFsdWFzaSBrZW1hbXB1YW4gbW9kZWwgZGFsYW0gbWVsYWt1a2FuIHByZWRpa3NpIHRlcmhhZGFwIGRhdGEgeWFuZyBiZWx1bSBwZXJuYWggZGlsaWhhdCBzZWJlbHVtbnlhLiBEYXRhIHBlbGF0aWhhbiBkaWd1bmFrYW4gdW50dWsgbWVtYmFuZ3VuIG1vZGVsLCBzZWRhbmdrYW4gZGF0YSBwZW5ndWppYW4gZGlndW5ha2FuIHVudHVrIG1lbmd1a3VyIHBlcmZvcm1hIG1vZGVsIHNlY2FyYSBvYmpla3RpZi4NClBlbWJhZ2lhbiBkYXRhIG1lbmphZGkgdHJhaW5pbmcgc2V0IGRhbiB0ZXN0aW5nIHNldCBiZXJ0dWp1YW4gdW50dWsgbWVuZ2V2YWx1YXNpIGtlbWFtcHVhbiBtb2RlbCBkYWxhbSBtZWxha3VrYW4gcHJlZGlrc2kgdGVyaGFkYXAgZGF0YSB5YW5nIGJlbHVtIHBlcm5haCBkaWxpaGF0IHNlYmVsdW1ueWEuIERhdGEgcGVsYXRpaGFuIGRpZ3VuYWthbiB1bnR1ayBtZW1iYW5ndW4gbW9kZWwsIHNlZGFuZ2thbiBkYXRhIHBlbmd1amlhbiBkaWd1bmFrYW4gdW50dWsgbWVuZ3VrdXIgcGVyZm9ybWEgbW9kZWwgc2VjYXJhIG9iamVrdGlmLg0KDQoNCiMgMy4gUHJhcGVtcm9zZXNhbiBkYW4gRXhwbG9yYXRvcnkgRGF0YSBBbmFseXNpcyAoRURBKQ0KDQpUYWhhcCBwcmFwZW1yb3Nlc2FuIGRhdGEgKGRhdGEgcHJlcHJvY2Vzc2luZykgZGFuIGV4cGxvcmF0b3J5IGRhdGEgYW5hbHlzaXMgKEVEQSkgZGlsYWt1a2FuIHVudHVrIG1lbWFoYW1pIGthcmFrdGVyaXN0aWsgZGF0YXNldCBzZWJlbHVtIHByb3NlcyBwZWxhdGloYW4gbW9kZWwuIFRhaGFwYW4gaW5pIGJlcnR1anVhbiB1bnR1ayBtZW1hc3Rpa2FuIGt1YWxpdGFzIGRhdGEsIG1lbmdpZGVudGlmaWthc2kgcG90ZW5zaSBwZXJtYXNhbGFoYW4geWFuZyBkYXBhdCBtZW1lbmdhcnVoaSBwZXJmb3JtYSBtb2RlbCwgc2VydGEgbWVueWlhcGthbiBkYXRhIGFnYXIgc2VzdWFpIGRlbmdhbiBmb3JtYXQgeWFuZyBkaWJ1dHVoa2FuIG9sZWggYXJzaXRla3R1ciBDb252b2x1dGlvbmFsIE5ldXJhbCBOZXR3b3JrIChDTk4pLg0KDQojIyAzLjEgUGVtZXJpa3NhYW4gRGF0YSBIaWxhbmcNCg0KPGRpdiBhbGlnbj0iY2VudGVyIj4NCioqVGFiZWwgMy4qKiBKdW1sYWggTWlzc2luZyBWYWx1ZSBEYXRhc2V0IEZhc2hpb24gTU5JU1QNCjwvZGl2Pg0KDQpgYGB7ciBtaXNzaW5nLXZhbHVlfQ0KbmFfdHJhaW4gPC0gc3VtKGlzLm5hKGZhc2hpb25fbW5pc3QkdHJhaW4keCkpDQpuYV90ZXN0ICA8LSBzdW0oaXMubmEoZmFzaGlvbl9tbmlzdCR0ZXN0JHgpKQ0KDQp0YWJlbF9uYSA8LSBkYXRhLmZyYW1lKA0KICBEYXRhc2V0ICAgICAgID0gYygiVHJhaW4iLCAiVGVzdCIpLA0KICBNaXNzaW5nX1ZhbHVlID0gYyhuYV90cmFpbiwgbmFfdGVzdCkNCikNCg0Ka25pdHI6OmthYmxlKHRhYmVsX25hKQ0KYGBgDQoNCioqSW50ZXJwcmV0YXNpOioqDQoNClBlbWVyaWtzYWFuIGRhdGEgaGlsYW5nIChtaXNzaW5nIHZhbHVlKSBkaWxha3VrYW4gdW50dWsgbWVtYXN0aWthbiBiYWh3YSBzZWx1cnVoIGluZm9ybWFzaSBwYWRhIGRhdGFzZXQgdGVyc2VkaWEgc2VjYXJhIGxlbmdrYXAuIE5pbGFpIHlhbmcgaGlsYW5nIGRhcGF0IG1lbnllYmFia2FuIHByb3NlcyBwZWxhdGloYW4gbW9kZWwgbWVuamFkaSB0aWRhayBvcHRpbWFsIGthcmVuYSBiZWJlcmFwYSBhbGdvcml0bWEgbWFjaGluZSBsZWFybmluZyBkYW4gZGVlcCBsZWFybmluZyB0aWRhayBkYXBhdCBtZW1wcm9zZXMgZGF0YSB5YW5nIHRpZGFrIGxlbmdrYXAgc2VjYXJhIGxhbmdzdW5nLg0KDQpIYXNpbCBwZW1lcmlrc2FhbiBtZW51bmp1a2thbiBiYWh3YSBqdW1sYWggbmlsYWkgaGlsYW5nIHBhZGEgZGF0YSBwZWxhdGloYW4gbWF1cHVuIGRhdGEgcGVuZ3VqaWFuIGFkYWxhaCBub2wuIERlbmdhbiBkZW1pa2lhbiwgc2VsdXJ1aCBjaXRyYSBwYWRhIGRhdGFzZXQgRmFzaGlvbi1NTklTVCBkYXBhdCBkaWd1bmFrYW4gc2VjYXJhIGxhbmdzdW5nIHRhbnBhIG1lbWVybHVrYW4gcHJvc2VzIGltcHV0YXNpIGF0YXUgcGVuYW5nYW5hbiBkYXRhIGhpbGFuZyBsYWlubnlhLiBLb25kaXNpIGluaSBtZW51bmp1a2thbiBiYWh3YSBrdWFsaXRhcyBkYXRhIHlhbmcgZGlndW5ha2FuIHN1ZGFoIGJhaWsgZGFuIHNpYXAgdW50dWsgbWVtYXN1a2kgdGFoYXAgYW5hbGlzaXMgYmVyaWt1dG55YS4NCg0KIyMgMy4yIFZpc3VhbGlzYXNpIFNhbXBlbCBDaXRyYQ0KDQpgYGB7ciB2aXN1YWxpc2FzaS1hd2FsLCBmaWcud2lkdGg9NywgZmlnLmhlaWdodD03fQ0KcGFyKG1mcm93ID0gYygzLDMpKQ0KDQpmb3IoaSBpbiAxOjkpew0KICBpbWFnZSgNCiAgICB0KGFwcGx5KGZhc2hpb25fbW5pc3QkdHJhaW4keFtpLCxdLDIscmV2KSksDQogICAgY29sID0gZ3JheSgoMDoyNTUpLzI1NSksDQogICAgYXhlcyA9IEZBTFNFDQogICkNCn0NCmBgYA0KDQo8ZGl2IGFsaWduPSJjZW50ZXIiPg0KKipHYW1iYXIgMS4qKiBTYW1wZWwgQ2l0cmENCjwvZGl2Pg0KDQoqKkludGVycHJldGFzaToqKg0KDQpWaXN1YWxpc2FzaSBjaXRyYSBkaWxha3VrYW4gdW50dWsgbWVtcGVyb2xlaCBwZW1haGFtYW4gYXdhbCBtZW5nZW5haSBiZW50dWssIHBvbGEsIGRhbiBrYXJha3RlcmlzdGlrIG9iamVrIHlhbmcgdGVyZGFwYXQgZGFsYW0gZGF0YXNldC4gU2V0aWFwIGdhbWJhciBwYWRhIEZhc2hpb24tTU5JU1QgbWVydXBha2FuIGNpdHJhIGdyYXlzY2FsZSBiZXJ1a3VyYW4gMjggw5cgMjggcGlrc2VsIHlhbmcgbWVyZXByZXNlbnRhc2lrYW4gc2FsYWggc2F0dSBrYXRlZ29yaSBwcm9kdWsgZmFzaGlvbi4NCg0KSGFzaWwgdmlzdWFsaXNhc2kgbWVudW5qdWtrYW4gYmFod2Egc2V0aWFwIGthdGVnb3JpIG1lbWlsaWtpIGJlbnR1ayBkYW4gcG9sYSB2aXN1YWwgeWFuZyBiZXJiZWRhLiBTZWJhZ2FpIGNvbnRvaCwga2F0ZWdvcmkgc2FuZGFsIGRhbiB0YXMgbWVtaWxpa2kga2FyYWt0ZXJpc3RpayB5YW5nIHJlbGF0aWYgbXVkYWggZGlrZW5hbGkga2FyZW5hIGJlbnR1a255YSBjdWt1cCB1bmlrLCBzZWRhbmdrYW4ga2F0ZWdvcmkgc2VwZXJ0aSBzaGlydCwgY29hdCwgZGFuIHB1bGxvdmVyIG1lbWlsaWtpIGtlbWlyaXBhbiBiZW50dWsgeWFuZyBjdWt1cCB0aW5nZ2kuIEtlbWlyaXBhbiB2aXN1YWwgYW50YXIga2VsYXMgdGVyc2VidXQgYmVycG90ZW5zaSBtZW55ZWJhYmthbiBrZXNhbGFoYW4ga2xhc2lmaWthc2kgcGFkYSBtb2RlbC4NCg0KU2VsYWluIGl0dSwgdmlzdWFsaXNhc2kgYXdhbCBqdWdhIG1lbnVuanVra2FuIGJhaHdhIG9iamVrIHBha2FpYW4gdGVsYWggYmVyYWRhIHBhZGEgcG9zaXNpIHlhbmcgcmVsYXRpZiB0ZXJwdXNhdCBzZWhpbmdnYSBtZW11ZGFoa2FuIG1vZGVsIENOTiBkYWxhbSBtZW5nZWtzdHJha3NpIGZpdHVyLWZpdHVyIHBlbnRpbmcgc2VwZXJ0aSB0ZXBpIChlZGdlcyksIHRla3N0dXIsIGRhbiBiZW50dWsgb2JqZWsgc2VsYW1hIHByb3NlcyBwZWxhdGloYW4uDQoNCiMjIDMuMyBEaXN0cmlidXNpIEtlbGFzDQoNCmBgYHtyIGJhcnBsb3Qta2VsYXMsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTV9DQoNCmZyZXEgPC0gdGFibGUoZmFzaGlvbl9tbmlzdCR0cmFpbiR5KQ0Ka2VsYXMgPC0gYygNCiAgIlRzaGlydCIsIlRyb3VzZXIiLCJQdWxsb3ZlciIsIkRyZXNzIiwiQ29hdCIsDQogICJTYW5kYWwiLCJTaGlydCIsIlNuZWFrZXIiLCJCYWciLCJCb290Ig0KKQ0KDQpiYXJwbG90KA0KICBmcmVxLA0KICBuYW1lcy5hcmcgPSBrZWxhcywNCiAgbGFzID0gMg0KKQ0KDQpgYGANCg0KPGRpdiBhbGlnbj0iY2VudGVyIj4NCioqR2FtYmFyIDIuKiogRGlzdHJpYnVzaSBLZWxhcyBGYXNoaW9uLU1OSVNUDQo8L2Rpdj4NCg0KRGlzdHJpYnVzaSBrZWxhcyBkaWFuYWxpc2lzIHVudHVrIG1lbmdldGFodWkgcHJvcG9yc2kganVtbGFoIG9ic2VydmFzaSBwYWRhIHNldGlhcCBrYXRlZ29yaSBwYWthaWFuLiBBbmFsaXNpcyBpbmkgcGVudGluZyBrYXJlbmEga2V0aWRha3NlaW1iYW5nYW4ganVtbGFoIGRhdGEgYW50YXIga2VsYXMgKGNsYXNzIGltYmFsYW5jZSkgZGFwYXQgbWVueWViYWJrYW4gbW9kZWwgbGViaWggY2VuZGVydW5nIG1lbXBlbGFqYXJpIGtlbGFzIHlhbmcgbWVtaWxpa2kganVtbGFoIG9ic2VydmFzaSBsZWJpaCBiZXNhci4NCg0KUHJvcG9yc2kgc2V0aWFwIGtlbGFzIGRhcGF0IGRpaGl0dW5nIG1lbmdndW5ha2FuIHBlcnNhbWFhbiBiZXJpa3V0Og0KDQokJHBfaSA9IFxmcmFje25faX17Tn0kJA0KS2V0ZXJhbmdhbjoNCg0KJHBfaSQgPSBwcm9wb3JzaSBrZWxhcyBrZS1pDQoNCiRuX2kkID0ganVtbGFoIG9ic2VydmFzaSBwYWRhIGtlbGFzIGtlLWkNCg0KJE4kICAgPSBqdW1sYWggc2VsdXJ1aCBvYnNlcnZhc2kNCg0KKipJbnRlcnByZXRhc2k6KioNCg0KQmVyZGFzYXJrYW4gaGFzaWwgcGVyaGl0dW5nYW4gZnJla3VlbnNpIGRhbiB2aXN1YWxpc2FzaSBkaWFncmFtIGJhdGFuZywgdGVybGloYXQgYmFod2Egc2V0aWFwIGtlbGFzIG1lbWlsaWtpIGp1bWxhaCBvYnNlcnZhc2kgeWFuZyByZWxhdGlmIHNhbWEsIHlhaXR1IHNla2l0YXIgNi4wMDAgY2l0cmEgcGFkYSBkYXRhIHBlbGF0aWhhbi4gRGVuZ2FuIGRlbWlraWFuLCBuaWxhaSBwcm9wb3JzaSBtYXNpbmctbWFzaW5nIGtlbGFzIG1lbmRla2F0aSAxMCUgZGFyaSB0b3RhbCBkYXRhIHBlbGF0aWhhbi4NCg0KS29uZGlzaSBpbmkgbWVudW5qdWtrYW4gYmFod2EgZGF0YXNldCBGYXNoaW9uLU1OSVNUIG1lbWlsaWtpIGRpc3RyaWJ1c2kga2VsYXMgeWFuZyBzZWltYmFuZyAoYmFsYW5jZWQgZGF0YXNldCksIHNlaGluZ2dhIHJpc2lrbyBiaWFzIG1vZGVsIHRlcmhhZGFwIGtlbGFzIHRlcnRlbnR1IHJlbGF0aWYga2VjaWwuIEtlc2VpbWJhbmdhbiBkaXN0cmlidXNpIGtlbGFzIGp1Z2EgbWVtYmFudHUgcHJvc2VzIHBlbGF0aWhhbiBDTk4gbWVuZ2hhc2lsa2FuIHBlcmZvcm1hIGtsYXNpZmlrYXNpIHlhbmcgbGViaWggb2JqZWt0aWYgZGFuIHN0YWJpbC4NCg0KIyMgMy40IE5vcm1hbGlzYXNpIERhdGENCg0KPGRpdiBhbGlnbj0iY2VudGVyIj4NCioqVGFiZWwgNC4qKiBOb3JtYWxpc2FzaSBEYXRhc2V0IEZhc2hpb24gTU5JU1QNCjwvZGl2Pg0KDQpgYGB7ciBub3JtYWxpc2FzaX0NCnhfdHJhaW4gPC0gZmFzaGlvbl9tbmlzdCR0cmFpbiR4IC8gMjU1DQp4X3Rlc3QgIDwtIGZhc2hpb25fbW5pc3QkdGVzdCR4IC8gMjU1DQoNCnRhYmVsX25vcm0gPC0gZGF0YS5mcmFtZSgNCiAgRGF0YXNldCA9IGMoIlRyYWluIiwgIlRlc3QiKSwNCiAgTWluICAgICA9IGMobWluKHhfdHJhaW4pLCBtaW4oeF90ZXN0KSksDQogIE1heCAgICAgPSBjKG1heCh4X3RyYWluKSwgbWF4KHhfdGVzdCkpLA0KICBNZWFuICAgID0gYyhyb3VuZChtZWFuKHhfdHJhaW4pLCA0KSwgcm91bmQobWVhbih4X3Rlc3QpLCA0KSkNCikNCg0Ka25pdHI6OmthYmxlKHRhYmVsX25vcm0pDQpgYGANCg0KKipJbnRlcnByZXRhc2k6KioNCg0KU2V0aWFwIHBpa3NlbCBwYWRhIGNpdHJhIEZhc2hpb24tTU5JU1QgbWVtaWxpa2kgbmlsYWkgaW50ZW5zaXRhcyBhbnRhcmEgMCBoaW5nZ2EgMjU1LCBkaSBtYW5hIG5pbGFpIDAgbWVyZXByZXNlbnRhc2lrYW4gd2FybmEgaGl0YW0gZGFuIG5pbGFpIDI1NSBtZXJlcHJlc2VudGFzaWthbiB3YXJuYSBwdXRpaC4gUGVyYmVkYWFuIHJlbnRhbmcgbmlsYWkgeWFuZyBjdWt1cCBiZXNhciBkYXBhdCBtZW55ZWJhYmthbiBwcm9zZXMgb3B0aW1hc2kgbW9kZWwgbWVuamFkaSBrdXJhbmcgZWZpc2llbi4gRGVuZ2FuIG1lbmdndW5ha2FuIHJ1bXVzOg0KDQokJHgnID0gXGZyYWN7eH17MjU1fSQkDQoNCg0KS2V0ZXJhbmdhbjoNCg0KJHgkID0gbmlsYWkgcGlrc2VsIHNlYmVsdW0gbm9ybWFsaXNhc2kNCg0KJHgnJCA9IG5pbGFpIHBpa3NlbCBzZXRlbGFoIG5vcm1hbGlzYXNpDQoNCk1lbGFsdWkgcHJvc2VzIG5vcm1hbGlzYXNpLCBzZWx1cnVoIG5pbGFpIHBpa3NlbCBiZXJhZGEgcGFkYSByZW50YW5nIDDigJMxIHNlaGluZ2dhIHByb3NlcyBwZW1iZWxhamFyYW4gQ05OIG1lbmphZGkgbGViaWggc3RhYmlsIGRhbiBjZXBhdC4gUHJvc2VzIGluaSBiZXJ0dWp1YW4gdW50dWsgbWVueWFtYWthbiBza2FsYSBkYXRhLCBtZW1wZXJjZXBhdCBwcm9zZXMgcGVtYmVsYWphcmFuIG1vZGVsLCBzZXJ0YSBtZW1iYW50dSBhbGdvcml0bWEgb3B0aW1hc2kgc2VwZXJ0aSBBZGFtIGRhbGFtIG1lbmVtdWthbiBwYXJhbWV0ZXIgdGVyYmFpayBzZWNhcmEgbGViaWggc3RhYmlsLg0KDQpTZWxhaW4gbWVuaW5na2F0a2FuIGVmaXNpZW5zaSBrb21wdXRhc2ksIG5vcm1hbGlzYXNpIGp1Z2EgZGFwYXQgbWVuZ3VyYW5naSByaXNpa28gdGVyamFkaW55YSBrZXRpZGFrc3RhYmlsYW4gbnVtZXJpayBzZWxhbWEgcHJvc2VzIHBlbGF0aWhhbiBkYW4gbWVtYmFudHUgbW9kZWwgbWVuY2FwYWkga29udmVyZ2Vuc2kgbGViaWggY2VwYXQgZGliYW5kaW5na2FuIG1lbmdndW5ha2FuIGRhdGEgbWVudGFoLg0KDQojIyAzLjUgUmVzaGFwaW5nIERhdGENCg0KPGRpdiBhbGlnbj0iY2VudGVyIj4NCioqVGFiZWwgNS4qKiBEaW1lbnNpIERhdGFzZXQgU2V0ZWxhaCBSZXNoYXBlDQo8L2Rpdj4NCg0KYGBge3IgcmVzaGFwZX0NCnhfdHJhaW4gPC0gYXJyYXlfcmVzaGFwZSh4X3RyYWluLCBjKG5yb3coeF90cmFpbiksIDI4LCAyOCwgMSkpDQp4X3Rlc3QgIDwtIGFycmF5X3Jlc2hhcGUoeF90ZXN0LCAgYyhucm93KHhfdGVzdCksICAyOCwgMjgsIDEpKQ0KDQp0YWJlbF9yZXNoYXBlIDwtIGRhdGEuZnJhbWUoDQogIERhdGFzZXQgPSBjKCJUcmFpbiIsICJUZXN0IiksDQogIFNhbXBsZXMgPSBjKGRpbSh4X3RyYWluKVsxXSwgZGltKHhfdGVzdClbMV0pLA0KICBIZWlnaHQgID0gYyhkaW0oeF90cmFpbilbMl0sIGRpbSh4X3Rlc3QpWzJdKSwNCiAgV2lkdGggICA9IGMoZGltKHhfdHJhaW4pWzNdLCBkaW0oeF90ZXN0KVszXSksDQogIENoYW5uZWwgPSBjKGRpbSh4X3RyYWluKVs0XSwgZGltKHhfdGVzdClbNF0pDQopDQoNCmtuaXRyOjprYWJsZSh0YWJlbF9yZXNoYXBlKQ0KYGBgDQoNCioqSW50ZXJwcmV0YXNpOioqDQoNCkRhdGEgY2l0cmEgeWFuZyBkaXBlcm9sZWggZGFyaSBkYXRhc2V0IGF3YWwgbWFzaWggYmVyYmVudHVrIG1hdHJpa3MgdGlnYSBkaW1lbnNpLiBOYW11biwgZnJhbWV3b3JrIEtlcmFzIGRhbiBUZW5zb3JGbG93IG1lbmdoYXJ1c2thbiBpbnB1dCB1bnR1ayBsYXBpc2FuIENvbnZvbHV0aW9uYWwgTmV1cmFsIE5ldHdvcmsgbWVtaWxpa2kgZm9ybWF0IGVtcGF0IGRpbWVuc2kgeWFuZyB0ZXJkaXJpIGF0YXM6DQoNCi0gSnVtbGFoIGNpdHJhIChzYW1wbGVzKSwNCi0gVGluZ2dpIGNpdHJhIChoZWlnaHQpLA0KLSBMZWJhciBjaXRyYSAod2lkdGgpLA0KLSBKdW1sYWgga2FuYWwgd2FybmEgKGNoYW5uZWxzKS4NCg0KS2FyZW5hIEZhc2hpb24tTU5JU1QgbWVydXBha2FuIGNpdHJhIGdyYXlzY2FsZSwgbWFrYSBzZXRpYXAgZ2FtYmFyIGhhbnlhIG1lbWlsaWtpIHNhdHUga2FuYWwgd2FybmEgKGNoYW5uZWwgPSAxKS4gT2xlaCBzZWJhYiBpdHUsIGRhdGEgZGl1YmFoIG1lbmphZGkgYmVudHVrIGFycmF5IGJlcnVrdXJhbiAoanVtbGFoIGRhdGEsIDI4LCAyOCwgMSkuDQoNClByb3NlcyByZXNoYXBpbmcgaW5pIHRpZGFrIG1lbmd1YmFoIGlzaSBkYXRhLCBtZWxhaW5rYW4gaGFueWEgbWVuZ3ViYWggc3RydWt0dXIgcGVueWltcGFuYW5ueWEgYWdhciBzZXN1YWkgZGVuZ2FuIGtlYnV0dWhhbiBpbnB1dCBwYWRhIGxhcGlzYW4ga29udm9sdXNpIENOTi4NCg0KIyMgMy42IE9uZS1Ib3QgRW5jb2RpbmcNCg0KPGRpdiBhbGlnbj0iY2VudGVyIj4NCioqVGFiZWwgNi4qKiBPbmUtSG90IEVuY29kaW5nIA0KPC9kaXY+DQoNCmBgYHtyIG9uZWhvdH0NCnlfdHJhaW4gPC0gdG9fY2F0ZWdvcmljYWwoZmFzaGlvbl9tbmlzdCR0cmFpbiR5LCAxMCkNCnlfdGVzdCAgPC0gdG9fY2F0ZWdvcmljYWwoZmFzaGlvbl9tbmlzdCR0ZXN0JHksICAxMCkNCg0KbGFiZWxfbmFtZXMgPC0gYygiVC1zaGlydC90b3AiLCAiVHJvdXNlciIsICJQdWxsb3ZlciIsICJEcmVzcyIsICJDb2F0IiwNCiAgICAgICAgICAgICAgICAgIlNhbmRhbCIsICJTaGlydCIsICJTbmVha2VyIiwgIkJhZyIsICJBbmtsZSBib290IikNCg0KIyBCdWF0IHRhYmVsIGNvbnRvaCB0YW5wYSB0cmFuc3Bvc2UNCnRhYmVsX2NvbnRvaCA8LSBkYXRhLmZyYW1lKA0KICBTYW1wbGUgICAgID0gcGFzdGUoIlNhbXBsZSIsIDE6NSksDQogIExhYmVsX0FzbGkgPSBsYWJlbF9uYW1lc1tmYXNoaW9uX21uaXN0JHRyYWluJHlbMTo1XSArIDFdLA0KICB5X3RyYWluWzE6NSwgXQ0KKQ0KDQpjb2xuYW1lcyh0YWJlbF9jb250b2gpWzM6MTJdIDwtIHBhc3RlMCgiQ2xhc3NfIiwgMDo5KQ0KDQprbml0cjo6a2FibGUodGFiZWxfY29udG9oKQ0KYGBgDQoNCioqSW50ZXJwcmV0YXNpOioqDQoNClZhcmlhYmVsIHRhcmdldCBwYWRhIEZhc2hpb24tTU5JU1QgYXdhbG55YSBkaXJlcHJlc2VudGFzaWthbiBkYWxhbSBiZW50dWsgYW5na2EgMCBoaW5nZ2EgOSB5YW5nIG1lbnVuanVra2FuIGthdGVnb3JpIHBha2FpYW4gdGVydGVudHUuIE5hbXVuLCBwYWRhIGtsYXNpZmlrYXNpIG11bHRpa2VsYXMgbWVuZ2d1bmFrYW4gZnVuZ3NpIGFrdGl2YXNpIFNvZnRtYXgsIGxhYmVsIHRhcmdldCBoYXJ1cyBkaXJlcHJlc2VudGFzaWthbiBkYWxhbSBiZW50dWsgdmVrdG9yIGJpbmVyLg0KDQpQcm9zZXMgb25lLWhvdCBlbmNvZGluZyBtZW5ndWJhaCBzZXRpYXAgbGFiZWwgbWVuamFkaSB2ZWt0b3Igc2VwYW5qYW5nIDEwIGVsZW1lbi4gU2ViYWdhaSBjb250b2gsIGxhYmVsIGtlbGFzICIyIiBha2FuIGRpcmVwcmVzZW50YXNpa2FuIHNlYmFnYWk6DQoNCiQkeT1bMCwwLDEsMCwwLDAsMCwwLDAsMF0kJA0KDQpBdGF1IHNlY2FyYSB1bXVtOg0KDQokJHlfaSA9IFxiZWdpbntjYXNlc30gMSwgJiBcdGV4dHtqaWthIGtlbGFzIGtlLX1pIFxcIDAsICYgXHRleHR7bGFpbm55YX0gXGVuZHtjYXNlc30kJA0KDQpSZXByZXNlbnRhc2kgaW5pIG1lbXVuZ2tpbmthbiBtb2RlbCBtZW5naGl0dW5nIHByb2JhYmlsaXRhcyB1bnR1ayBzZXRpYXAga2VsYXMgc2VjYXJhIGJlcnNhbWFhbiBkYW4gbWVueWVzdWFpa2FuIGJvYm90IGphcmluZ2FuIG1lbmdndW5ha2FuIGZ1bmdzaSBsb3NzIGNhdGVnb3JpY2FsIGNyb3NzLWVudHJvcHkuIERlbmdhbiBkZW1pa2lhbiwgcHJvc2VzIG9uZS1ob3QgZW5jb2RpbmcgbWVydXBha2FuIHRhaGFwIHBlbnRpbmcgeWFuZyBtZW11bmdraW5rYW4gQ05OIG1lbGFrdWthbiBrbGFzaWZpa2FzaSBtdWx0aWtlbGFzIHNlY2FyYSBlZmVrdGlmIGRhbiBtZW5naGFzaWxrYW4gcHJlZGlrc2kgcHJvYmFiaWxpdGFzIHBhZGEgc2VsdXJ1aCBrYXRlZ29yaSBwYWthaWFuIHlhbmcgdGVyc2VkaWEuDQoNCg0KIyA0LiBNZXRvZGUNCg0KIyMgNC4xIENvbnZvbHV0aW9uYWwgTmV1cmFsIE5ldHdvcmsgKENOTikNCg0KQ29udm9sdXRpb25hbCBOZXVyYWwgTmV0d29yayAoQ05OKSBtZXJ1cGFrYW4gc2FsYWggc2F0dSBhcnNpdGVrdHVyIERlZXAgTGVhcm5pbmcgeWFuZyBkaXJhbmNhbmcga2h1c3VzIHVudHVrIHBlbmdvbGFoYW4gZGF0YSBiZXJiZW50dWsgY2l0cmEuIENOTiBtYW1wdSBtZWxha3VrYW4gZWtzdHJha3NpIGZpdHVyIHNlY2FyYSBvdG9tYXRpcyBtZWxhbHVpIG9wZXJhc2kga29udm9sdXNpIHRhbnBhIG1lbWVybHVrYW4gcHJvc2VzIGVrc3RyYWtzaSBmaXR1ciBzZWNhcmEgbWFudWFsIHNlcGVydGkgcGFkYSBtZXRvZGUgbWFjaGluZSBsZWFybmluZyBrb252ZW5zaW9uYWwuIEtlbWFtcHVhbiB0ZXJzZWJ1dCBtZW5qYWRpa2FuIENOTiBzYW5nYXQgZWZla3RpZiBkYWxhbSBiZXJiYWdhaSB0dWdhcyBjb21wdXRlciB2aXNpb24sIHRlcm1hc3VrIGtsYXNpZmlrYXNpIGNpdHJhLCBkZXRla3NpIG9iamVrLCBkYW4gcGVuZ2VuYWxhbiBwb2xhIHZpc3VhbCAoTGVDdW4gZXQgYWwuLCAyMDE1KS4NCg0KUGFkYSBwZW5lbGl0aWFuIGluaSwgQ05OIGRpZ3VuYWthbiB1bnR1ayBtZW5na2xhc2lmaWthc2lrYW4gY2l0cmEgRmFzaGlvbi1NTklTVCBrZSBkYWxhbSAxMCBrYXRlZ29yaSBwYWthaWFuIGJlcmRhc2Fya2FuIHBvbGEgdmlzdWFsIHlhbmcgdGVya2FuZHVuZyBwYWRhIHNldGlhcCBnYW1iYXIuDQoNCiMjIyA0LjEuMSBMYXBpc2FuIEtvbnZvbHVzaSAoQ29udm9sdXRpb24gTGF5ZXIpDQoNCkxhcGlzYW4ga29udm9sdXNpIG1lcnVwYWthbiBrb21wb25lbiB1dGFtYSBDTk4geWFuZyBiZXJmdW5nc2kgbWVuZ2Vrc3RyYWtzaSBmaXR1ciBkYXJpIGNpdHJhIGlucHV0IG1lbmdndW5ha2FuIGtlcm5lbCBhdGF1IGZpbHRlci4gRmlsdGVyIGFrYW4gYmVyZ2VyYWsgcGFkYSBzZWx1cnVoIGJhZ2lhbiBjaXRyYSBkYW4gbWVuZ2hhc2lsa2FuIGZlYXR1cmUgbWFwIHlhbmcgbWVyZXByZXNlbnRhc2lrYW4gcG9sYSB0ZXJ0ZW50dSBzZXBlcnRpIHRlcGkgKGVkZ2VzKSwgdGVrc3R1ciwgbWF1cHVuIGJlbnR1ayBvYmplay4NCg0KT3BlcmFzaSBrb252b2x1c2kgZGFwYXQgZGlueWF0YWthbiBzZWJhZ2FpOg0KDQokJA0KUyhpLGopPVxzdW1fbSBcc3VtX24gSShpK20saituKUsobSxuKQ0KJCQNCg0KS2V0ZXJhbmdhbjoNCg0KJEkkICAgPSBjaXRyYSBpbnB1dA0KDQokSyQgICA9IGtlcm5lbCBhdGF1IGZpbHRlcg0KDQokUyQgICA9IGZlYXR1cmUgbWFwIGhhc2lsIGtvbnZvbHVzaQ0KDQokaSxqJCA9IHBvc2lzaSBwaWtzZWwgcGFkYSBmZWF0dXJlIG1hcA0KDQpNZWxhbHVpIG9wZXJhc2kgaW5pLCBDTk4gZGFwYXQgbWVtcGVsYWphcmkgZml0dXItZml0dXIgcGVudGluZyBzZWNhcmEgb3RvbWF0aXMgZGFyaSBkYXRhIGNpdHJhLg0KDQojIyMgNC4xLjIgRnVuZ3NpIEFrdGl2YXNpIFJlTFUNCg0KU2V0ZWxhaCBwcm9zZXMga29udm9sdXNpLCBuaWxhaSBrZWx1YXJhbiBha2FuIGRpdGVydXNrYW4ga2UgZnVuZ3NpIGFrdGl2YXNpIFJlY3RpZmllZCBMaW5lYXIgVW5pdCAoUmVMVSkuIEZ1bmdzaSBpbmkgYmVydHVqdWFuIHVudHVrIG1lbXBlcmtlbmFsa2FuIHNpZmF0IG5vbmxpbmllciBrZSBkYWxhbSBtb2RlbCBzZWhpbmdnYSBDTk4gbWFtcHUgbWVtcGVsYWphcmkgcG9sYSB5YW5nIGtvbXBsZWtzLg0KDQpQZXJzYW1hYW4gUmVMVSBhZGFsYWg6DQoNCiQkZih4KT1cbWF4KDAseCkkJA0KDQpLZXRlcmFuZ2FuOg0KDQotIEppa2EgJHggPiAwJCwgbWFrYSBvdXRwdXQgc2FtYSBkZW5nYW4gJHgkDQoNCi0gSmlrYSAkeCBcbGUgMCQsIG1ha2Egb3V0cHV0IGJlcm5pbGFpIDANCg0KUGVuZ2d1bmFhbiBSZUxVIG1lbWJhbnR1IG1lbXBlcmNlcGF0IHByb3NlcyBwZWxhdGloYW4gZGFuIG1lbmd1cmFuZ2kgbWFzYWxhaCB2YW5pc2hpbmcgZ3JhZGllbnQgKEdvb2RmZWxsb3cgZXQgYWwuLCAyMDE2KS4NCg0KIyMjIDQuMS4zIFBvb2xpbmcgTGF5ZXINCg0KUG9vbGluZyBkaWd1bmFrYW4gdW50dWsgbWVuZ3VyYW5naSBkaW1lbnNpIGZlYXR1cmUgbWFwIHNlaGluZ2dhIGp1bWxhaCBwYXJhbWV0ZXIgeWFuZyBkaXBlbGFqYXJpIG1lbmphZGkgbGViaWggc2VkaWtpdC4gU2VsYWluIGl0dSwgcG9vbGluZyBqdWdhIG1lbWJhbnR1IG1lbmd1cmFuZ2kgcmlzaWtvIG92ZXJmaXR0aW5nIGRhbiBtZW5pbmdrYXRrYW4gZWZpc2llbnNpIGtvbXB1dGFzaS4NCg0KUGFkYSBwZW5lbGl0aWFuIGluaSBkaWd1bmFrYW4gTWF4IFBvb2xpbmcgeWFuZyBtZW1pbGloIG5pbGFpIG1ha3NpbXVtIGRhcmkgc3VhdHUgYXJlYSBmZWF0dXJlIG1hcC4NCg0KUGVyc2FtYWFuIE1heCBQb29saW5nOg0KDQokJA0KeT1cbWF4KHhfMSx4XzIsXGxkb3RzLHhfbikNCiQkDQoNCktldGVyYW5nYW46DQoNCiR4XzEseF8yLFxsZG90cyx4X24kID0gbmlsYWkgcGlrc2VsIGRhbGFtIGFyZWEgcG9vbGluZw0KDQokeSQgPSBuaWxhaSBtYWtzaW11bSB5YW5nIGRpcGlsaWgNCg0KIyMjIDQuMS40IEZsYXR0ZW4gTGF5ZXINCg0KT3V0cHV0IGRhcmkgbGFwaXNhbiBrb252b2x1c2kgZGFuIHBvb2xpbmcgbWFzaWggYmVyYmVudHVrIG1hdHJpa3MgbXVsdGlkaW1lbnNpLiBPbGVoIGthcmVuYSBpdHUgZGlsYWt1a2FuIHByb3NlcyBmbGF0dGVuIHVudHVrIG1lbmd1YmFoIGZlYXR1cmUgbWFwIG1lbmphZGkgdmVrdG9yIHNhdHUgZGltZW5zaSB5YW5nIGRhcGF0IGRpcHJvc2VzIG9sZWggRnVsbHkgQ29ubmVjdGVkIExheWVyLg0KDQpQcm9zZXMgaW5pIHRpZGFrIG1lbmd1YmFoIGluZm9ybWFzaSBmaXR1ciB5YW5nIHRlbGFoIGRpcGVsYWphcmksIG1lbGFpbmthbiBoYW55YSBtZW5ndWJhaCBiZW50dWsgZGF0YSBhZ2FyIHNlc3VhaSBkZW5nYW4gc3RydWt0dXIgamFyaW5nYW4gc2FyYWYgdGlydWFuLg0KDQojIyMgNC4xLjUgRnVsbHkgQ29ubmVjdGVkIExheWVyDQoNCkZ1bGx5IENvbm5lY3RlZCBMYXllciAoRGVuc2UgTGF5ZXIpIGJlcmZ1bmdzaSBtZW5nZ2FidW5na2FuIHNlbHVydWggZml0dXIgeWFuZyB0ZWxhaCBkaWVrc3RyYWtzaSBzZWJlbHVtbnlhIHVudHVrIG1lbmdoYXNpbGthbiBrZXB1dHVzYW4ga2xhc2lmaWthc2kuDQoNClBhZGEgcGVuZWxpdGlhbiBpbmkgZGlndW5ha2FuOg0KDQotIERlbnNlIExheWVyIGRlbmdhbiAxMjggbmV1cm9uDQotIEZ1bmdzaSBha3RpdmFzaSBSZUxVDQoNCkp1bWxhaCBuZXVyb24gdGVyc2VidXQgZGlwaWxpaCB1bnR1ayBtZW1iZXJpa2FuIGthcGFzaXRhcyBwZW1iZWxhamFyYW4geWFuZyBjdWt1cCBkYWxhbSBtZW5nZW5hbGkgcG9sYSBhbnRhciBrYXRlZ29yaSBwYWthaWFuLg0KDQojIyMgNC4xLjYgRHJvcG91dCBMYXllcg0KDQpEcm9wb3V0IG1lcnVwYWthbiB0ZWtuaWsgcmVndWxhcmlzYXNpIHlhbmcgZGlndW5ha2FuIHVudHVrIG1lbmd1cmFuZ2kgb3ZlcmZpdHRpbmcuIFNlbGFtYSBwcm9zZXMgcGVsYXRpaGFuLCBzZWJhZ2lhbiBuZXVyb24gYWthbiBkaW5vbmFrdGlma2FuIHNlY2FyYSBhY2FrIHNlaGluZ2dhIG1vZGVsIHRpZGFrIHRlcmxhbHUgYmVyZ2FudHVuZyBwYWRhIG5ldXJvbiB0ZXJ0ZW50dS4NCg0KUGFkYSBwZW5lbGl0aWFuIGluaSBkaWd1bmFrYW4gbmlsYWkgZHJvcG91dCBzZWJlc2FyIDAsMyB5YW5nIGJlcmFydGkgMzAlIG5ldXJvbiBha2FuIGRpbm9uYWt0aWZrYW4gc2VjYXJhIGFjYWsgcGFkYSBzZXRpYXAgaXRlcmFzaSBwZWxhdGloYW4uDQoNCiMjIyA0LjEuNyBPdXRwdXQgTGF5ZXIgZGFuIFNvZnRtYXgNCg0KTGFwaXNhbiBvdXRwdXQgZGlndW5ha2FuIHVudHVrIG1lbmdoYXNpbGthbiBwcm9iYWJpbGl0YXMgbWFzaW5nLW1hc2luZyBrZWxhcy4gS2FyZW5hIEZhc2hpb24tTU5JU1QgdGVyZGlyaSBhdGFzIDEwIGthdGVnb3JpIHBha2FpYW4sIG1ha2EgZGlndW5ha2FuIDEwIG5ldXJvbiBvdXRwdXQgZGVuZ2FuIGZ1bmdzaSBha3RpdmFzaSBTb2Z0bWF4Lg0KDQpQZXJzYW1hYW4gU29mdG1heDoNCg0KJCQNClAoeT1pKT1cZnJhY3tlXnt6X2l9fXtcc3VtX3tqPTF9XntLfWVee3pfan19DQokJA0KDQpLZXRlcmFuZ2FuOg0KDQokUCh5PWkpJCA9IHByb2JhYmlsaXRhcyBrZWxhcyBrZS0oaSkNCg0KJHpfaSQgPSBvdXRwdXQgbmV1cm9uIGtlLShpKQ0KDQokSyQgPSBqdW1sYWgga2VsYXMNCg0KU29mdG1heCBtZW5naGFzaWxrYW4gbmlsYWkgcHJvYmFiaWxpdGFzIGFudGFyYSAwIGhpbmdnYSAxIGRlbmdhbiB0b3RhbCBwcm9iYWJpbGl0YXMgc2VsdXJ1aCBrZWxhcyBzYW1hIGRlbmdhbiAxLiBBcnNpdGVrdHVyIENOTiB5YW5nIERpZ3VuYWthbiBhZGFsYWggc2ViYWdhaSBiZXJpa3V0Og0KDQo8ZGl2IGFsaWduPSJjZW50ZXIiPg0KKipUYWJlbCA3LioqIEFyc2l0ZWt0dXIgQ05OIA0KPC9kaXY+DQoNCnwgTGFwaXNhbiB8IEtvbmZpZ3VyYXNpIHwNCnwgOi0tLSB8IDotLS0gfA0KfCBJbnB1dCB8ICQyOCBcdGltZXMgMjggXHRpbWVzIDEkIHwNCnwgQ29udjJEIHwgMzIgZmlsdGVyLCBrZXJuZWwgJDMgXHRpbWVzIDMkLCBSZUxVIHwNCnwgTWF4UG9vbGluZzJEIHwgUG9vbCBzaXplICQyIFx0aW1lcyAyJCB8DQp8IENvbnYyRCB8IDY0IGZpbHRlciwga2VybmVsICQzIFx0aW1lcyAzJCwgUmVMVSB8DQp8IE1heFBvb2xpbmcyRCB8IFBvb2wgc2l6ZSAkMiBcdGltZXMgMiQgfA0KfCBGbGF0dGVuIHwgTWVuZ3ViYWggZmVhdHVyZSBtYXAgbWVuamFkaSB2ZWt0b3IgfA0KfCBEZW5zZSB8IDEyOCBuZXVyb24sIFJlTFUgfA0KfCBEcm9wb3V0IHwgMCwzIHwNCnwgT3V0cHV0IHwgMTAgbmV1cm9uLCBTb2Z0bWF4IHwNCg0KQXJzaXRla3R1ciB0ZXJzZWJ1dCBkaXJhbmNhbmcgdW50dWsgbWVuZ2Vrc3RyYWtzaSBmaXR1ciB2aXN1YWwgc2VjYXJhIGJlcnRhaGFwIG11bGFpIGRhcmkgZml0dXIgc2VkZXJoYW5hIGhpbmdnYSBmaXR1ciB5YW5nIGxlYmloIGtvbXBsZWtzIHNlaGluZ2dhIG1hbXB1IG1lbWJlZGFrYW4gc2V0aWFwIGthdGVnb3JpIHBha2FpYW4gcGFkYSBkYXRhc2V0IEZhc2hpb24tTU5JU1QuDQoNCiMjIDQuMiBQZW1iYW5ndW5hbiBNb2RlbA0KDQpNb2RlbCBDTk4gZGliYW5ndW4gbWVuZ2d1bmFrYW4gbGlicmFyeSBLZXJhcyBwYWRhIGJhaGFzYSBwZW1yb2dyYW1hbiBSLiBTdHJ1a3R1ciBtb2RlbCBkaXN1c3VuIHNlY2FyYSBiZXJ1cnV0YW4gKHNlcXVlbnRpYWwpIHlhbmcgdGVyZGlyaSBhdGFzIGR1YSBsYXBpc2FuIGtvbnZvbHVzaSwgZHVhIGxhcGlzYW4gcG9vbGluZywgc2F0dSBsYXBpc2FuIGZsYXR0ZW4sIHNhdHUgbGFwaXNhbiBkZW5zZSwgc2F0dSBsYXBpc2FuIGRyb3BvdXQsIGRhbiBzYXR1IGxhcGlzYW4gb3V0cHV0Lg0KDQo8ZGl2IGFsaWduPSJjZW50ZXIiPg0KKipUYWJlbCA4LioqIEFyc2l0ZWt0dXIgTW9kZWwgQ05OIEZhc2hpb24gTU5JU1QgDQo8L2Rpdj4NCmBgYHtyIG1vZGVsLWNubn0NCm1vZGVsIDwtIGtlcmFzX21vZGVsX3NlcXVlbnRpYWwoKSAlPiUNCiAgbGF5ZXJfY29udl8yZChmaWx0ZXJzID0gMzIsIGtlcm5lbF9zaXplID0gYygzLDMpLCBhY3RpdmF0aW9uID0gInJlbHUiLCBpbnB1dF9zaGFwZSA9IGMoMjgsMjgsMSkpICU+JQ0KICBsYXllcl9tYXhfcG9vbGluZ18yZChwb29sX3NpemUgPSBjKDIsMikpICU+JQ0KICBsYXllcl9jb252XzJkKGZpbHRlcnMgPSA2NCwga2VybmVsX3NpemUgPSBjKDMsMyksIGFjdGl2YXRpb24gPSAicmVsdSIpICU+JQ0KICBsYXllcl9tYXhfcG9vbGluZ18yZChwb29sX3NpemUgPSBjKDIsMikpICU+JQ0KICBsYXllcl9mbGF0dGVuKCkgJT4lDQogIGxheWVyX2RlbnNlKHVuaXRzID0gMTI4LCBhY3RpdmF0aW9uID0gInJlbHUiKSAlPiUNCiAgbGF5ZXJfZHJvcG91dChyYXRlID0gMC4zKSAlPiUNCiAgbGF5ZXJfZGVuc2UodW5pdHMgPSAxMCwgYWN0aXZhdGlvbiA9ICJzb2Z0bWF4IikNCg0Kc3VtbWFyeV9vdXRwdXQgPC0gY2FwdHVyZS5vdXRwdXQoc3VtbWFyeShtb2RlbCkpDQoNCiMgUGFyc2UgYmFyaXMgeWFuZyBtZW5nYW5kdW5nIGRhdGEgbGF5ZXINCmxheWVyX2xpbmVzIDwtIHN1bW1hcnlfb3V0cHV0W2dyZXBsKCJcXChOb25lLCIsIHN1bW1hcnlfb3V0cHV0KV0NCg0KIyBFa3N0cmFrIExheWVyLCBPdXRwdXQgU2hhcGUsIGRhbiBQYXJhbQ0KcGFyc2VkIDwtIGxhcHBseShsYXllcl9saW5lcywgZnVuY3Rpb24obGluZSkgew0KICBsaW5lX2NsZWFuIDwtIGdzdWIoIl5cXD8/P1xccyp8XFxzKlxcPz8/XFxzKiQiLCAiIiwgbGluZSkNCiAgcGFydHMgPC0gc3Ryc3BsaXQobGluZV9jbGVhbiwgIlxccypcXD8/P1xccyoiKVtbMV1dDQogIGlmIChsZW5ndGgocGFydHMpID49IDMpIHsNCiAgICBkYXRhLmZyYW1lKA0KICAgICAgTGF5ZXIgICAgICAgID0gdHJpbXdzKHBhcnRzWzFdKSwNCiAgICAgIE91dHB1dF9TaGFwZSA9IHRyaW13cyhwYXJ0c1syXSksDQogICAgICBQYXJhbSAgICAgICAgPSB0cmltd3MocGFydHNbM10pDQogICAgKQ0KICB9DQp9KQ0KDQp0YWJlbF9hcnNpdGVrdHVyIDwtIGRvLmNhbGwocmJpbmQsIEZpbHRlcihOZWdhdGUoaXMubnVsbCksIHBhcnNlZCkpDQpyb3duYW1lcyh0YWJlbF9hcnNpdGVrdHVyKSA8LSBOVUxMDQoNCmtuaXRyOjprYWJsZSh0YWJlbF9hcnNpdGVrdHVyKQ0KYGBgDQoNCjxkaXYgYWxpZ249ImNlbnRlciI+DQoqKlRhYmVsIDkuKiogUmluZ2thc2FuIFBhcmFtZXRlciBNb2RlbA0KPC9kaXY+DQoNCmBgYHtyfQ0KcGFyYW1fbGluZXMgPC0gc3VtbWFyeV9vdXRwdXRbZ3JlcGwoIlRvdGFsIHBhcmFtc3xUcmFpbmFibGUgcGFyYW1zfE5vbi10cmFpbmFibGUgcGFyYW1zIiwgc3VtbWFyeV9vdXRwdXQpXQ0KcGFyYW1fbGluZXNfY2xlYW4gPC0gZ3N1YigiXiMjXFxzKnxeXFxzKiIsICIiLCBwYXJhbV9saW5lcykNCg0KdGFiZWxfcGFyYW1zIDwtIGRhdGEuZnJhbWUoDQogIEtldGVyYW5nYW4gPSBjKCJUb3RhbCBQYXJhbWV0ZXJzIiwgIlRyYWluYWJsZSBQYXJhbWV0ZXJzIiwgIk5vbi1UcmFpbmFibGUgUGFyYW1ldGVycyIpLA0KICBKdW1sYWggICAgID0gdHJpbXdzKGdzdWIoIi4qcGFyYW1zOlxccyoiLCAiIiwgcGFyYW1fbGluZXNfY2xlYW4pKQ0KKQ0KDQprbml0cjo6a2FibGUodGFiZWxfcGFyYW1zKQ0KYGBgDQoNCiMjIDQuMyBLb21waWxhc2kgTW9kZWwNCg0KU2ViZWx1bSBkaWxha3VrYW4gcGVsYXRpaGFuLCBtb2RlbCBwZXJsdSBkaWtvbXBpbGFzaSBkZW5nYW4gbWVuZW50dWthbiBvcHRpbWl6ZXIsIGZ1bmdzaSBsb3NzLCBkYW4gbWV0cmlrIGV2YWx1YXNpLg0KDQpPcHRpbWl6ZXIgeWFuZyBkaWd1bmFrYW4gYWRhbGFoIEFkYW0ga2FyZW5hIG1hbXB1IG1lbGFrdWthbiBwZW1iYXJ1YW4gYm9ib3Qgc2VjYXJhIGFkYXB0aWYgZGFuIG1lbWlsaWtpIHBlcmZvcm1hIHlhbmcgYmFpayBwYWRhIGJlcmJhZ2FpIHBlcm1hc2FsYWhhbiBkZWVwIGxlYXJuaW5nIChLaW5nbWEgJiBCYSwgMjAxNSkuDQoNCkZ1bmdzaSBsb3NzIHlhbmcgZGlndW5ha2FuIGFkYWxhaCBDYXRlZ29yaWNhbCBDcm9zcy1FbnRyb3B5IGthcmVuYSBwZW5lbGl0aWFuIGluaSBtZXJ1cGFrYW4ga2xhc2lmaWthc2kgbXVsdGlrZWxhcy4NCg0KUGVyc2FtYWFuIENyb3NzLUVudHJvcHk6DQoNCiQkDQpMPS1cc3VtX3tpPTF9XntLfXlfaVxsb2coXGhhdHt5fV9pKQ0KJCQNCg0KS2V0ZXJhbmdhbjoNCg0KJEwkICAgICAgICAgPSBuaWxhaSBsb3NzDQoNCiR5X2kkICAgICAgID0gbGFiZWwgYWt0dWFsDQoNCiRcaGF0e3l9X2kkID0gcHJvYmFiaWxpdGFzIHByZWRpa3NpDQoNCiRLJCAgICAgICAgID0ganVtbGFoIGtlbGFzDQoNClNlbWFraW4ga2VjaWwgbmlsYWkgbG9zcywgc2VtYWtpbiBiYWlrIGtlbWFtcHVhbiBtb2RlbCBkYWxhbSBtZWxha3VrYW4gcHJlZGlrc2kuDQoNCmBgYHtyIGNvbXBpbGV9DQptb2RlbCAlPiUgY29tcGlsZSgNCiAgb3B0aW1pemVyID0gImFkYW0iLA0KICBsb3NzID0gImNhdGVnb3JpY2FsX2Nyb3NzZW50cm9weSIsDQogIG1ldHJpY3MgPSAiYWNjdXJhY3kiDQopDQpgYGANCg0KIyMgNC40IEh5cGVycGFyYW1ldGVyIFR1bmluZw0KDQpIeXBlcnBhcmFtZXRlciB0dW5pbmcgZGlsYWt1a2FuIHVudHVrIG1lbXBlcm9sZWgga29tYmluYXNpIHBhcmFtZXRlciBtb2RlbCB5YW5nIG1lbmdoYXNpbGthbiBwZXJmb3JtYSB0ZXJiYWlrLiBIeXBlcnBhcmFtZXRlciBtZXJ1cGFrYW4gcGFyYW1ldGVyIHlhbmcgZGl0ZW50dWthbiBzZWJlbHVtIHByb3NlcyBwZWxhdGloYW4gZGltdWxhaSBkYW4gdGlkYWsgZGlwZWxhamFyaSBzZWNhcmEgb3RvbWF0aXMgb2xlaCBtb2RlbC4NCg0KUGFkYSBwZW5lbGl0aWFuIGluaSBkaWxha3VrYW4gcGVuY2FyaWFuIGJlYmVyYXBhIGtvbWJpbmFzaSBoeXBlcnBhcmFtZXRlciB5YW5nIG1lbGlwdXRpOg0KDQotIEp1bWxhaCBmaWx0ZXIga29udm9sdXNpDQotIEp1bWxhaCBuZXVyb24gcGFkYSBEZW5zZSBMYXllcg0KLSBMZWFybmluZyBSYXRlDQotIEJhdGNoIFNpemUNCi0gSnVtbGFoIEVwb2NoDQotIE5pbGFpIERyb3BvdXQNCg0KUGVtaWxpaGFuIGtvbWJpbmFzaSB0ZXJiYWlrIGRpbGFrdWthbiBiZXJkYXNhcmthbiBuaWxhaSBha3VyYXNpIHZhbGlkYXNpICh2YWxpZGF0aW9uIGFjY3VyYWN5KSB0ZXJ0aW5nZ2kuDQoNCjxkaXYgYWxpZ249ImNlbnRlciI+DQoqKlRhYmVsIDEwLioqIEthbmRpZGF0IEh5cGVycGFyYW1ldGVyIHlhbmcgRGlndW5ha2FuIHBhZGEgUHJvc2VzIFR1bmluZyANCjwvZGl2Pg0KDQp8IEh5cGVycGFyYW1ldGVyIHwgS2FuZGlkYXQgTmlsYWkgfA0KfC0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLXwNCnwgRmlsdGVyIENvbnYyRCAgfCAzMiwgNjQgICAgICAgICB8DQp8IERlbnNlIFVuaXRzICAgIHwgMTI4LCAyNTYgICAgICAgfA0KfCBMZWFybmluZyBSYXRlICB8IDAuMDAxLCAwLjAwMDUgIHwNCnwgQmF0Y2ggU2l6ZSAgICAgfCA2NCwgMTI4ICAgICAgICB8DQp8IERyb3BvdXQgICAgICAgIHwgMC4zLCAwLjUgICAgICAgfA0KfCBFcG9jaCAgICAgICAgICB8IDEwICAgICAgICAgICAgIHwNCg0KYGBge3IgdHVuaW5nLCBpbmNsdWRlPUZBTFNFfQ0KaGFzaWxfdHVuaW5nIDwtIGRhdGEuZnJhbWUoKQ0KZmlsdGVyX2xpc3QgIDwtIGMoMzIsIDY0KQ0KZGVuc2VfbGlzdCAgIDwtIGMoMTI4LCAyNTYpDQpkcm9wb3V0X2xpc3QgPC0gYygwLjMsIDAuNSkNCg0KZm9yIChmIGluIGZpbHRlcl9saXN0KSB7DQogIGZvciAoZCBpbiBkZW5zZV9saXN0KSB7DQogICAgZm9yIChkciBpbiBkcm9wb3V0X2xpc3QpIHsNCiAgICAgIG1vZGVsX3R1bmUgPC0ga2VyYXNfbW9kZWxfc2VxdWVudGlhbCgpICU+JQ0KICAgICAgICBsYXllcl9jb252XzJkKGZpbHRlcnMgPSBmLCBrZXJuZWxfc2l6ZSA9IGMoMywzKSwgYWN0aXZhdGlvbiA9ICJyZWx1IiwNCiAgICAgICAgICAgICAgICAgICAgICBpbnB1dF9zaGFwZSA9IGMoMjgsMjgsMSkpICU+JQ0KICAgICAgICBsYXllcl9tYXhfcG9vbGluZ18yZChwb29sX3NpemUgPSBjKDIsMikpICU+JQ0KICAgICAgICBsYXllcl9jb252XzJkKGZpbHRlcnMgPSBmKjIsIGtlcm5lbF9zaXplID0gYygzLDMpLCBhY3RpdmF0aW9uID0gInJlbHUiKSAlPiUNCiAgICAgICAgbGF5ZXJfbWF4X3Bvb2xpbmdfMmQocG9vbF9zaXplID0gYygyLDIpKSAlPiUNCiAgICAgICAgbGF5ZXJfZmxhdHRlbigpICU+JQ0KICAgICAgICBsYXllcl9kZW5zZSh1bml0cyA9IGQsIGFjdGl2YXRpb24gPSAicmVsdSIpICU+JQ0KICAgICAgICBsYXllcl9kcm9wb3V0KHJhdGUgPSBkcikgJT4lDQogICAgICAgIGxheWVyX2RlbnNlKHVuaXRzID0gMTAsIGFjdGl2YXRpb24gPSAic29mdG1heCIpDQoNCiAgICAgIG1vZGVsX3R1bmUgJT4lIGNvbXBpbGUoDQogICAgICAgIG9wdGltaXplciA9ICJhZGFtIiwNCiAgICAgICAgbG9zcyAgICAgID0gImNhdGVnb3JpY2FsX2Nyb3NzZW50cm9weSIsDQogICAgICAgIG1ldHJpY3MgICA9ICJhY2N1cmFjeSINCiAgICAgICkNCg0KICAgICAgaGlzdCA8LSBtb2RlbF90dW5lICU+JSBmaXQoDQogICAgICAgIHhfdHJhaW4sIHlfdHJhaW4sDQogICAgICAgIGVwb2NocyAgICAgICAgICAgPSA1LA0KICAgICAgICBiYXRjaF9zaXplICAgICAgID0gMTI4LA0KICAgICAgICB2YWxpZGF0aW9uX3NwbGl0ID0gMC4yLA0KICAgICAgICB2ZXJib3NlICAgICAgICAgID0gMA0KICAgICAgKQ0KDQogICAgICBhY2MgPC0gbWF4KGhpc3QkbWV0cmljcyR2YWxfYWNjdXJhY3kpDQoNCiAgICAgIGhhc2lsX3R1bmluZyA8LSByYmluZCgNCiAgICAgICAgaGFzaWxfdHVuaW5nLA0KICAgICAgICBkYXRhLmZyYW1lKEZpbHRlciA9IGYsIERlbnNlID0gZCwgRHJvcG91dCA9IGRyLCBWYWxfQWNjdXJhY3kgPSBhY2MpDQogICAgICApDQogICAgfQ0KICB9DQp9DQpgYGANCg0KIyMjIDQuNC4xIFBlbWlsaWhhbiBIeXBlcnBhcmFtZXRlciBUZXJiYWlrDQoNCjxkaXYgYWxpZ249ImNlbnRlciI+DQoqKlRhYmVsIDExLioqIEh5cGVycGFyYW1ldGVyIFRlcmJhaWsNCjwvZGl2Pg0KDQpgYGB7ciBiZXN0LXBhcmFtfQ0KYmVzdF9wYXJhbSA8LSBoYXNpbF90dW5pbmcgJT4lDQogIGFycmFuZ2UoZGVzYyhWYWxfQWNjdXJhY3kpKSAlPiUNCiAgc2xpY2UoMSkNCg0Ka25pdHI6OmthYmxlKA0KICBiZXN0X3BhcmFtLCAgY29sLm5hbWVzID0gYygiRmlsdGVyIiwgIkRlbnNlIiwgIkRyb3BvdXQiLCAiVmFsIEFjY3VyYWN5IikpDQpgYGANCiMjIyA0LjQuMiBQZW1iYW5ndW5hbiBNb2RlbCBDTk4gRmluYWwNCg0KYGBge3J9DQptb2RlbCA8LSBrZXJhc19tb2RlbF9zZXF1ZW50aWFsKCkgJT4lDQogIGxheWVyX2NvbnZfMmQoZmlsdGVycyA9IDY0LCBrZXJuZWxfc2l6ZSA9IGMoMywzKSwgYWN0aXZhdGlvbiA9ICJyZWx1IiwNCiAgICAgICAgICAgICAgICBpbnB1dF9zaGFwZSA9IGMoMjgsMjgsMSkpICU+JQ0KICBsYXllcl9tYXhfcG9vbGluZ18yZChwb29sX3NpemUgPSBjKDIsMikpICU+JQ0KICBsYXllcl9jb252XzJkKGZpbHRlcnMgPSAxMjgsIGtlcm5lbF9zaXplID0gYygzLDMpLCBhY3RpdmF0aW9uID0gInJlbHUiKSAlPiUNCiAgbGF5ZXJfbWF4X3Bvb2xpbmdfMmQocG9vbF9zaXplID0gYygyLDIpKSAlPiUNCiAgbGF5ZXJfZmxhdHRlbigpICU+JQ0KICBsYXllcl9kZW5zZSh1bml0cyA9IDI1NiwgYWN0aXZhdGlvbiA9ICJyZWx1IikgJT4lDQogIGxheWVyX2Ryb3BvdXQocmF0ZSA9IDAuMykgJT4lDQogIGxheWVyX2RlbnNlKHVuaXRzID0gMTAsIGFjdGl2YXRpb24gPSAic29mdG1heCIpDQoNCm1vZGVsICU+JSBjb21waWxlKA0KICBvcHRpbWl6ZXIgPSAiYWRhbSIsDQogIGxvc3MgICAgICA9ICJjYXRlZ29yaWNhbF9jcm9zc2VudHJvcHkiLA0KICBtZXRyaWNzICAgPSAiYWNjdXJhY3kiDQopDQoNCnN1bW1hcnlfb3V0cHV0IDwtIGNhcHR1cmUub3V0cHV0KHN1bW1hcnkobW9kZWwpKQ0KDQpsYXllcl9saW5lcyA8LSBzdW1tYXJ5X291dHB1dFtncmVwbCgiXFwoTm9uZSwiLCBzdW1tYXJ5X291dHB1dCldDQoNCnBhcnNlZCA8LSBsYXBwbHkobGF5ZXJfbGluZXMsIGZ1bmN0aW9uKGxpbmUpIHsNCiAgbGluZV9jbGVhbiA8LSBnc3ViKCJeXFw/Pz9cXHMqfFxccypcXD8/P1xccyokIiwgIiIsIGxpbmUpDQogIHBhcnRzIDwtIHN0cnNwbGl0KGxpbmVfY2xlYW4sICJcXHMqXFw/Pz9cXHMqIilbWzFdXQ0KICBpZiAobGVuZ3RoKHBhcnRzKSA+PSAzKSB7DQogICAgZGF0YS5mcmFtZSgNCiAgICAgIExheWVyICAgICAgICA9IHRyaW13cyhwYXJ0c1sxXSksDQogICAgICBPdXRwdXRfU2hhcGUgPSB0cmltd3MocGFydHNbMl0pLA0KICAgICAgUGFyYW0gICAgICAgID0gdHJpbXdzKHBhcnRzWzNdKQ0KICAgICkNCiAgfQ0KfSkNCg0KdGFiZWxfYXJzaXRla3R1ciA8LSBkby5jYWxsKHJiaW5kLCBGaWx0ZXIoTmVnYXRlKGlzLm51bGwpLCBwYXJzZWQpKQ0Kcm93bmFtZXModGFiZWxfYXJzaXRla3R1cikgPC0gTlVMTA0KDQprbml0cjo6a2FibGUodGFiZWxfYXJzaXRla3R1cikNCmBgYA0KDQo8ZGl2IGFsaWduPSJjZW50ZXIiPg0KKipUYWJlbCAxMi4qKiBQYXJhbWV0ZXIgTW9kZWwgQ05OIEZpbmFsDQo8L2Rpdj4NCg0KYGBge3J9DQpwYXJhbV9saW5lcyAgICAgICA8LSBzdW1tYXJ5X291dHB1dFtncmVwbCgiVG90YWwgcGFyYW1zfFRyYWluYWJsZSBwYXJhbXN8Tm9uLXRyYWluYWJsZSBwYXJhbXMiLCBzdW1tYXJ5X291dHB1dCldDQpwYXJhbV9saW5lc19jbGVhbiA8LSBnc3ViKCJeIyNcXHMqfF5cXHMqIiwgIiIsIHBhcmFtX2xpbmVzKQ0KDQp0YWJlbF9wYXJhbXMgPC0gZGF0YS5mcmFtZSgNCiAgS2V0ZXJhbmdhbiA9IGMoIlRvdGFsIFBhcmFtZXRlcnMiLCAiVHJhaW5hYmxlIFBhcmFtZXRlcnMiLCAiTm9uLVRyYWluYWJsZSBQYXJhbWV0ZXJzIiksDQogIEp1bWxhaCAgICAgPSB0cmltd3MoZ3N1YigiLipwYXJhbXM6XFxzKiIsICIiLCBwYXJhbV9saW5lc19jbGVhbikpDQopDQoNCmtuaXRyOjprYWJsZSgNCiAgdGFiZWxfcGFyYW1zDQopIA0KYGBgDQoNCiMjIDQuNSBQZWxhdGloYW4gZGFuIFZhbGlkYXNpDQoNClBlbGF0aWhhbiBtb2RlbCBkaWxha3VrYW4gbWVuZ2d1bmFrYW4gZGF0YSBwZWxhdGloYW4geWFuZyB0ZWxhaCBkaXByYXByb3NlcyBzZWJlbHVtbnlhLiBTZWJhbnlhayA4MCUgZGF0YSBkaWd1bmFrYW4gdW50dWsgcGVsYXRpaGFuIGRhbiAyMCUgZGlndW5ha2FuIHNlYmFnYWkgZGF0YSB2YWxpZGFzaSBtZWxhbHVpIHBhcmFtZXRlciB2YWxpZGF0aW9uX3NwbGl0ID0gMC4yLg0KDQpQYXJhbWV0ZXIgcGVsYXRpaGFuIHlhbmcgZGlndW5ha2FuIGFkYWxhaDoNCg0KLSBFcG9jaCA9IDEwDQotIEJhdGNoIFNpemUgPSAxMjgNCi0gVmFsaWRhdGlvbiBTcGxpdCA9IDIwJQ0KDQpFcG9jaCBtZW51bmp1a2thbiBqdW1sYWggaXRlcmFzaSBwZW51aCB0ZXJoYWRhcCBzZWx1cnVoIGRhdGEgcGVsYXRpaGFuLCBzZWRhbmdrYW4gYmF0Y2ggc2l6ZSBtZW51bmp1a2thbiBqdW1sYWggZGF0YSB5YW5nIGRpcHJvc2VzIHNlYmVsdW0gZGlsYWt1a2FuIHBlbWJhcnVhbiBib2JvdCBqYXJpbmdhbi4NCg0KPGRpdiBhbGlnbj0iY2VudGVyIj4NCioqVGFiZWwgMTMuKiogVHJhaW5pbmcgTW9kZWwgQ05ODQo8L2Rpdj4NCg0KYGBge3IgdHJhaW5pbmd9DQpoaXN0b3J5IDwtIG1vZGVsICU+JSBmaXQoDQogIHhfdHJhaW4sDQogIHlfdHJhaW4sDQogIGVwb2NocyA9IDEwLA0KICBiYXRjaF9zaXplID0gMTI4LA0KICB2YWxpZGF0aW9uX3NwbGl0ID0gMC4yDQopDQoNCiMgRWtzdHJhayBtZXRyaWsgb3RvbWF0aXMgZGFyaSBoaXN0b3J5DQp0YWJlbF9oaXN0b3J5IDwtIGRhdGEuZnJhbWUoDQogIEVwb2NoICAgICAgID0gMTpsZW5ndGgoaGlzdG9yeSRtZXRyaWNzJGxvc3MpLA0KICBMb3NzICAgICAgICA9IHJvdW5kKGhpc3RvcnkkbWV0cmljcyRsb3NzLCA0KSwNCiAgQWNjdXJhY3kgICAgPSByb3VuZChoaXN0b3J5JG1ldHJpY3MkYWNjdXJhY3ksIDQpLA0KICBWYWxfTG9zcyAgICA9IHJvdW5kKGhpc3RvcnkkbWV0cmljcyR2YWxfbG9zcywgNCksDQogIFZhbF9BY2N1cmFjeSA9IHJvdW5kKGhpc3RvcnkkbWV0cmljcyR2YWxfYWNjdXJhY3ksIDQpDQopDQoNCmtuaXRyOjprYWJsZSgNCiAgdGFiZWxfaGlzdG9yeSkNCmBgYA0KDQpTZWxhbWEgcHJvc2VzIHBlbGF0aWhhbiwgbW9kZWwgYWthbiBtZW1wZWxhamFyaSBwb2xhIHBhZGEgZGF0YSBwZWxhdGloYW4gZGFuIHBlcmZvcm1hbnlhIGRpcGFudGF1IG1lbmdndW5ha2FuIGRhdGEgdmFsaWRhc2kgdW50dWsgbWVuZGV0ZWtzaSBrZW11bmdraW5hbiB0ZXJqYWRpbnlhIG92ZXJmaXR0aW5nLg0KDQojIyA0LjYgTWV0cmlrIEV2YWx1YXNpDQoNCkV2YWx1YXNpIG1vZGVsIGRpbGFrdWthbiBtZW5nZ3VuYWthbiBiZWJlcmFwYSBtZXRyaWsgdW50dWsgbWVuZ3VrdXIga2VtYW1wdWFuIGtsYXNpZmlrYXNpIENOTiBzZWNhcmEga29tcHJlaGVuc2lmLg0KDQojIyMgNC42LjEgQWNjdXJhY3kNCg0KQWt1cmFzaSBhZGFsYWggcHJvcG9yc2kgYmFueWFrbnlhIGRhdGEgeWFuZyBkaXByZWRpa3NpIGRlbmdhbiBiZW5hciB0ZXJoYWRhcCB0b3RhbCBkYXRhIHlhbmcgZGlwcmVkaWtzaS4gTmlsYWkgYWt1cmFzaSBtZW51bmp1a2thbiBnYW1iYXJhbiBzZWNhcmEgZ2xvYmFsIHNlYmVyYXBhIGJhaWsgcHJlZGlrc2kgeWFuZyBkaWxha3VrYW4uIE1pc2FsLCBuaWxhaSBha3VyYXNpIDAsODUgYmVybWFrbmEgZGFyaSB0b3RhbCBzZWx1cnVoIGRhdGEgeWFuZyBkaXByZWRpa3NpLCBtb2RlbCBkZW5nYW4gdGVwYXQgbWVtcHJlZGlrc2kgODUlIHNlZGFuZ2thbiAxNSUgc2FsYWggcHJlZGlrc2kuDQoNClBhZGEga2xhc2lmaWthc2kgYmluZXIsIHJ1bXVzIHVudHVrIG1lbmdoaXR1bmcgYWt1cmFzaSBhZGFsYWggc2ViYWdhaSBiZXJpa3V0Og0KDQokJA0KQWNjdXJhY3k9XGZyYWN7VFArVE59e1RQK1ROK0ZQK0ZOfQ0KJCQNCg0KS2V0ZXJhbmdhbjoNCg0KVFAgPSBUcnVlIFBvc2l0aXZlDQoNClROID0gVHJ1ZSBOZWdhdGl2ZQ0KDQpGUCA9IEZhbHNlIFBvc2l0aXZlDQoNCkZOID0gRmFsc2UgTmVnYXRpdmUNCg0KUnVtdXMgcGVyc2FtYWFuIHRlcnNlYnV0IGRhcGF0IGRpYnVhdCBsZWJpaCB1bXVtIHVudHVrIG1lbmdoaXR1bmcgYWt1cmFzaSBkYXRhIGtsYXNpZmlrYXNpIG11bHRpa2VsYXMgeWFpdHU6DQoNCiQkXHRleHR7QWt1cmFzaX0gPSBcZnJhY3tcc3VtX3tpPTF9XntufSBcdGV4dHtUUH1fe1x0ZXh0e2tlbGFzfS1pfX17XHN1bV97aT0xfV57bn0gXHRleHR7VFB9X3tcdGV4dHtrZWxhc30taX0gKyBcc3VtX3tpPTF9XntufSBcdGV4dHtGUH1fe1x0ZXh0e2tlbGFzfS1pfX0kJA0KDQojIyMgNC42LjIgUHJlY2lzaW9uDQoNClByZWNpc2lvbiBhZGFsYWggbWV0cmlrIGV2YWx1YXNpIGtpbmVyamEgeWFuZyBkaWd1bmFrYW4gdW50dWsgbWVuZ3VrdXIgYWt1cmFzaSBwYWRhIGtlbGFzIHByZWRpa3NpIHRlcnRlbnR1LiBQYWRhIGthc3VzIGtsYXNpZmlrYXNpIGJpbmVyLCBwcmVzaXNpIG1lbmd1a3VyIHNlYmVyYXBhIGJhbnlhayBkYXRhIHlhbmcgcHJlZGlrc2kga2Ugc3VhdHUga2VsYXMgeWFuZyBtZW1hbmcgYmVuYXIgbWFzdWsga2VsYXMgdGVyc2VidXQuIFByZXNpc2kgbWVtYmVyaWthbiBnYW1iYXJhbiB0ZW50YW5nIGt1YWxpdGFzIHByZWRpa3NpIHBvc2l0aWYgeWFuZyBkaWhhc2lsa2FuIG9sZWggbW9kZWwuDQoNClNhbWEgc2VwZXJ0aSByZWNhbGwsIG5pbGFpIHByZXNpc2kganVnYSBkaWhpdHVuZyB1bnR1ayBtYXNpbmctbWFzaW5nIGtlbGFzIGRlbmdhbiBydW11cyBzZWJhZ2FpIGJlcmlrdXQ6DQoNCiQkDQpQcmVjaXNpb25faT1cZnJhY3tUUF9pfXtUUF9pK0ZQX2l9DQokJA0KDQojIyMgNC42LjMgUmVjYWxsIChTZW5zaXRpdml0eSAmIFNwZWNpZmljaXR5KQ0KDQpNZXRyaWsgUmVjYWxsLCBqdWdhIGRpa2VuYWwgc2ViYWdhaSBzZW5zaXRpdml0eSwgYWRhbGFoIHNhbGFoIHNhdHUgbWV0cmlrIHBlbnRpbmcgZGFsYW0gZXZhbHVhc2kgbW9kZWwga2xhc2lmaWthc2kuIFJlY2FsbCBtZW5ndWt1ciBrZW1hbXB1YW4gbW9kZWwgdW50dWsgbWVuZGV0ZWtzaSBzZW11YSBzYW1wZWwgc3VhdHUga2VsYXMgZGkgYW50YXJhIHNlbXVhIHNhbXBlbCB5YW5nIGJlbmFyLWJlbmFyIG1hc3VrIGtlbGFzIHRlcnNlYnV0LiBEYWxhbSBrb250ZWtzIGRldGVrc2kgcGVueWFraXQsIG1pc2FsbnlhLCByZWNhbGwgbWVuZ3VrdXIgc2ViZXJhcGEgYmFpayBtb2RlbCBkYXBhdCBtZW5kZXRla3NpIHBhc2llbiB5YW5nIGJlbmFyLWJlbmFyIHNha2l0IGRhcmkgc2VtdWEgcGFzaWVuIHlhbmcgc2FraXQuDQoNCklzdGlsYWggc2Vuc2l0aXZpdHkgYmlhc2FueWEgZGlndW5ha2FuIHVudHVrIG1lbnllYnV0a2FuIHJlY2FsbCBwYWRhIGtlbGFzIHBvc2l0aWYgYXRhdSBUcnVlIFBvc2l0aXZlIFJhdGUgKFRQUikuIEFkYXB1biB1bnR1ayBrZWxhcyBuZWdhdGlmIGlzdGlsYWggeWFuZyBkaWd1bmFrYW4gYWRhbGFoIHNwZWNpZmljaXR5LiBOYW11biBzZWthbGkgbGFnaSwgcGVuZW50dWFuIGtlbGFzIHBvc2l0aWYgYWthbiBzYW5nYXQgdGVyZ2FudHVuZyBrZXRlcnRhcmlrYW4gZGFuIHR1anVhbiBwZW5lbGl0aWFuLg0KDQpOaWxhaSByZWNhbGwgZGloaXR1bmcgdW50dWsgbWFzaW5nLW1hc2luZyBrZWxhcyBkZW5nYW4gcnVtdXMgc2ViYWdhaSBiZXJpa3V0Og0KDQokJA0KUmVjYWxsX2k9XGZyYWN7VFBfaX17VFBfaStGTl9pfQ0KJCQNCg0KIyMjIDQuNi40IEYxLVNjb3JlDQoNCkYxIFNjb3JlIGFkYWxhaCBtZXRyaWsgZXZhbHVhc2kga2luZXJqYSBtb2RlbCBrbGFzaWZpa2FzaSB5YW5nIG1lbmdnYWJ1bmdrYW4gcHJlY2lzaW9uIGRhbiByZWNhbGwgbWVuamFkaSBzYXR1IG5pbGFpIHR1bmdnYWwuIE1ldHJpayBpbmkgZGl1a3VyIGRhcmkgcmF0YS1yYXRhIGhhcm1vbmlzIHByZWNpc2lvbiBkYW4gcmVjYWxsLCBzZWhpbmdnYSBtZW1iZXJpa2FuIGdhbWJhcmFuIHlhbmcgbGViaWggc2VpbWJhbmcgdGVudGFuZyBraW5lcmphIG1vZGVsIHRlcnV0YW1hIGtldGlrYSB0ZXJkYXBhdCBrZXRpZGFrc2VpbWJhbmdhbiBhbnRhcmEganVtbGFoIGtlbGFzIHBvc2l0aWYgZGFuIG5lZ2F0aWYuDQoNCktldGlrYSBiZXJiaWNhcmEga2xhc2lmaWthc2kgYmluZXIsIEYxIFNjb3JlIHVtdW1ueWEgZGl1a3VyIHBhZGEga2VsYXMgcG9zaXRpZiBzYWphLiBTZW1lbnRhcmEgaXR1LCBwYWRhIGtsYXNpZmlrYXNpIG11bHRpa2VsYXMsIGtpdGEgZGFwYXQgbWVuZ3VrdXIgRjEgU2NvcmUgc3VhdHUga2VsYXMgdnMgdG90YWwga2VsYXMgbGFpbm55YS4NCg0KUGVyaGl0dW5nYW4gbmlsYWkgRjEgU2NvcmUgZGlsYWt1a2FuIG1lbmdndW5ha2FuIHJ1bXVzIGJlcmlrdXQgaW5pOg0KDQokJA0KRjFfaT0yXHRpbWVzXGZyYWN7UHJlY2lzaW9uX2lcdGltZXMgUmVjYWxsX2l9e1ByZWNpc2lvbl9pK1JlY2FsbF9pfQ0KJCQNCg0KIyMjIDQuNi41IENvbmZ1c2lvbiBNYXRyaXgNCg0KQ29uZnVzaW9uIG1hdHJpeCBhZGFsYWggbWF0aWtzIHlhbmcgbWVueWFqaWthbiB0YWJ1bGFzaSBoYXNpbCBwcmVkaWtzaSBkYXJpIHN1YXR1IG1vZGVsIGtsYXNpZmlrYXNpLiBNYXRyaWtzIGluaSBiZXJpc2kgaW5mb3JtYXNpIG5pbGFpLW5pbGFpIHlhbmcgbWVyZXByZXNlbnRhc2lrYW4gYmFnYWltYW5hIG1vZGVsIG1lbXByZWRpa3NpIGtlbGFzIHRlcnRlbnR1IGRpYmFuZGluZ2thbiBkZW5nYW4ga2VsYXMgc2ViZW5hcm55YS4NCg0KIFBhZGEga2xhc2lmaWthc2kgYmluZXIsIGJpYXNhbnlhIHRlcmRhcGF0IHNhdHUga2VsYXMgeWFuZyBkaXNlYnV0IHNlYmFnYWkga2VsYXMgcG9zaXRpZiBkYW4ga2VsYXMgbGFpbm55YSBzZWJhZ2FpIGtlbGFzIG5lZ2F0aWYuIFBlbmVudHVhbiBrZWxhcyBwb3NpdGlmIGluaSB0aWRhayBiZXJrYWl0YW4gZGVuZ2FuIGtvbm90YXNpIGRhcmkga2VsYXMgdGVyc2VidXQsIG5hbXVuIGRhcGF0IGRpcGlsaWggeWFuZyBtYW5hIHNhamEgc2VzdWFpIGtlaW5naW5hbiBhdGF1IGtlYnV0dWhhbiBwZW5pbGl0aS4gVGVyZGFwYXQgNCBuaWxhaSB5YW5nIGRpc2FqaWthbiBwYWRhIEdhbWJhciA0IHlhaXR1Og0KDQotIFRydWUgUG9zaXRpdmUgKFRQKToganVtbGFoIGRhdGEgZGFyaSBrZWxhcyBwb3NpdGlmIHlhbmcgZGlwcmVkaWtzaSBiZW5hciAobWFzdWsga2VsYXMgcG9zaXRpZikNCi0gVHJ1ZSBOZWdhdGl2ZSAoVE4pOiBqdW1sYWggZGF0YSBkYXJpIGtlbGFzIG5lZ2F0aWYgeWFuZyBkaXByZWRpa3NpIGJlbmFyIChtYXN1ayBrZWxhcyBuZWdhdGlmKQ0KLSBGYWxzZSBQb3NpdGl2ZSAoRlApOiBqdW1sYWggZGF0YSBkYXJpIGtlbGFzIG5lZ2F0aWYgeWFuZyBzYWxhaCBwcmVkaWtzaSAoc2VoYXJ1c255YSBuZWdhdGlmIG5hbXVuIGRpcHJlZGlrc2kgcG9zaXRpZikNCi0gRmFsc2UgbmVnYXRpdmUgKEZOKToganVtbGFoIGRhdGEgZGFyaSBrZWxhcyBwb3NpdGlmIHlhbmcgc2FsYWggcHJlZGlrc2kgKHNlaGFydXNueWEgcG9zaXRpZiBuYW11biBkaXByZWRpa3NpIG5lZ2F0aWYpDQoNCkxlYmloIGdlbmVyYWwsIHBhZGEga2xhc2lmaWthc2kgbXVsdGkta2VsYXMgKGxlYmloIGRhcmkgMiBrZWxhcyBvdXRwdXQpIHVrdXJhbiBjb25mdXNpb24gbWF0cml4IGp1Z2EgbWVuamFkaSBsZWJpaCBiYW55YWsuIE1pc2Fsa2FuICB1bnR1ayAzIGtlbGFzIG1ha2EgdWt1cmFuIGNvbmZ1c2lvbiBtYXRyaXggbWVuamFkaSAzw5czLCBhdGF1IGJlcmlzaSA5IG5pbGFpLiBTZWNhcmEgdW11bSwgbmlsYWkgeWFuZyB0ZXJkYXBhdCBwYWRhIHBvc2lzaSBkaWFnb25hbCBtZW51bmp1a2thbiBqdW1sYWggcHJlZGlrc2kgYmVuYXIgZGFyaSBtYXNpbmctbWFzaW5nIGtlbGFzLiBBZGFwdW4gcGFkYSBiYWdpYW4gbGFpbm55YSBtZW51bmp1a2thbiBwcmVkaWtzaSBzYWxhaC4gU2ViYWdhaSBjb250b2ggSzEgKFRSVUUpIG1lbnlhdGFrYW4gYmFueWFrbnlhIHByZWRpa3NpIGJlbmFyIGRhcmkgZGF0YSB5YW5nIG1lbWFuZyBzZWhhcnVzbnlhIG1hc3VrIGtlIGRhbGFtIGtlbGFzIGtlLTEuIFNlZGFuZ2thbiBLMSAoRkFMU0UpIG1lbnlhdGFrYW4gYmFueWFrbnlhIGRhdGEgeWFuZyBzZWhhcnVzbnlhIG1lcnVwYWthbiBrZWxhcyBLMiBkYW4gSzMgbmFtdW4gc2FsYWggZGlwcmVkaWtzaSBzZWJhZ2FpIEsxLg0KDQpNZXNraXB1biB0ZXJkYXBhdCBwZXJiZWRhYW4gdWt1cmFuLCBuYW11biBtZXRyaWstbWV0cmlrIGV2YWx1YXNpIHlhbmcgZGlndW5ha2FuIHRldGFwIGJlcmxha3Ugc2VjYXJhIHVtdW0uIFBlcmhpdHVuZ2FuIHBhZGEgc2V0aWFwIG1ldHJpayBldmFsdWFzaSB0ZXJzZWJ1dCBzYW5nYXQgdGVyZ2FudHVuZyBkZW5nYW4gbmlsYWktbmlsYWkgcGFkYSBjb25mdXNpb24gbWF0cml4Lg0KDQoNCiMgNS4gSGFzaWwgQW5hbGlzaXMNCg0KQmFnaWFuIGluaSBtZW55YWppa2FuIGhhc2lsIHBlbGF0aWhhbiwgcGVuZ3VqaWFuLCBkYW4gZXZhbHVhc2kgbW9kZWwgQ29udm9sdXRpb25hbCBOZXVyYWwgTmV0d29yayAoQ05OKSB5YW5nIHRlbGFoIGRpYmFuZ3VuIHVudHVrIG1lbmdrbGFzaWZpa2FzaWthbiBjaXRyYSBGYXNoaW9uLU1OSVNULiBBbmFsaXNpcyBkaWxha3VrYW4gdW50dWsgbWVuaWxhaSBrZW1hbXB1YW4gbW9kZWwgZGFsYW0gbWVuZ2VuYWxpIGthdGVnb3JpIHBha2FpYW4gYmVyZGFzYXJrYW4gcG9sYSB2aXN1YWwgeWFuZyB0ZXJkYXBhdCBwYWRhIGNpdHJhLg0KDQojIyA1LjEgSGFzaWwgSHlwZXJwYXJhbWV0ZXIgVHVuaW5nDQoNCjxkaXYgYWxpZ249ImNlbnRlciI+DQoqKlRhYmVsIDE0LioqIEhhc2lsIEh5cGVycGFyYW1ldGVyIFR1bmluZw0KPC9kaXY+DQoNCmBgYHtyIGhhc2lsLXR1bmluZ30NCmtuaXRyOjprYWJsZSgNCiAgaGFzaWxfdHVuaW5nICU+JSBhcnJhbmdlKGRlc2MoVmFsX0FjY3VyYWN5KSksDQogIGNvbC5uYW1lcyA9IGMoIkZpbHRlciIsICJEZW5zZSIsICJEcm9wb3V0IiwgIlZhbCBBY2N1cmFjeSIpKQ0KYGBgDQoNCioqSW50ZXJwcmV0YXNpOioqDQoNCkJlcmRhc2Fya2FuIGhhc2lsIGh5cGVycGFyYW1ldGVyIHR1bmluZyB5YW5nIGRpdHVuanVra2FuIHBhZGEgNS4xICwgZGlwZXJvbGVoIGRlbGFwYW4ga29tYmluYXNpIHBhcmFtZXRlciB5YW5nIGRpdWppIGRlbmdhbiB2YXJpYXNpIGp1bWxhaCBmaWx0ZXIga29udm9sdXNpIChGaWx0ZXIpLCBqdW1sYWggbmV1cm9uIHBhZGEgRGVuc2UgTGF5ZXIgKERlbnNlKSwgZGFuIG5pbGFpIERyb3BvdXQuIEtpbmVyamEgc2V0aWFwIGtvbWJpbmFzaSBkaWV2YWx1YXNpIG1lbmdndW5ha2FuIG5pbGFpIHZhbGlkYXRpb24gYWNjdXJhY3kuDQoNCktvbWJpbmFzaSB0ZXJiYWlrIGRpcGVyb2xlaCBwYWRhIG1vZGVsIGRlbmdhbiA2NCBmaWx0ZXIsIDI1NiBuZXVyb24gcGFkYSBkZW5zZSBsYXllciwgZGFuIGRyb3BvdXQgc2ViZXNhciAwLDMsIHlhbmcgbWVuZ2hhc2lsa2FuIG5pbGFpIHZhbGlkYXRpb24gYWNjdXJhY3kgc2VraXRhciAwLDkwIGF0YXUgOTAlLiBIYXNpbCBpbmkgbWVudW5qdWtrYW4gYmFod2Ega29uZmlndXJhc2kgdGVyc2VidXQgbWFtcHUgbWVtYmVyaWthbiBrZW1hbXB1YW4gZ2VuZXJhbGlzYXNpIHlhbmcgbGViaWggYmFpayBkaWJhbmRpbmdrYW4ga29tYmluYXNpIHBhcmFtZXRlciBsYWlubnlhLg0KDQpTZWNhcmEgdW11bSwgcGVuZ2d1bmFhbiAyNTYgbmV1cm9uIHBhZGEgZGVuc2UgbGF5ZXIgY2VuZGVydW5nIG1lbmdoYXNpbGthbiBha3VyYXNpIHZhbGlkYXNpIHlhbmcgbGViaWggdGluZ2dpIGRpYmFuZGluZ2thbiBwZW5nZ3VuYWFuIDEyOCBuZXVyb24uIEhhbCBpbmkgbWVuZ2luZGlrYXNpa2FuIGJhaHdhIGp1bWxhaCBuZXVyb24geWFuZyBsZWJpaCBiZXNhciBtZW11bmdraW5rYW4gbW9kZWwgbWVtcGVsYWphcmkgcmVwcmVzZW50YXNpIGZpdHVyIHlhbmcgbGViaWgga29tcGxla3MgZGFyaSBjaXRyYSBGYXNoaW9uLU1OSVNULg0KDQpTZWxhaW4gaXR1LCBrb25maWd1cmFzaSBkZW5nYW4gNjQgZmlsdGVyIHBhZGEgbGFwaXNhbiBrb252b2x1c2kgbWVudW5qdWtrYW4gcGVyZm9ybWEgeWFuZyByZWxhdGlmIGxlYmloIGJhaWsgZGliYW5kaW5na2FuIDMyIGZpbHRlci4gU2VtYWtpbiBiYW55YWsgZmlsdGVyIHlhbmcgZGlndW5ha2FuLCBzZW1ha2luIGJhbnlhayBwb2xhIGRhbiBrYXJha3RlcmlzdGlrIGNpdHJhIHlhbmcgZGFwYXQgZGlla3N0cmFrc2kgb2xlaCBtb2RlbCwgc2VoaW5nZ2EgbWVuaW5na2F0a2FuIGtlbWFtcHVhbiBrbGFzaWZpa2FzaS4NCg0KRGFyaSBzaXNpIHJlZ3VsYXJpc2FzaSwgcGVuZ2d1bmFhbiBkcm9wb3V0IHNlYmVzYXIgMCwzIG1lbWJlcmlrYW4gaGFzaWwgeWFuZyBsZWJpaCBiYWlrIGRpYmFuZGluZ2thbiBkcm9wb3V0IDAsNSBwYWRhIHNlYmFnaWFuIGJlc2FyIGtvbWJpbmFzaSBwYXJhbWV0ZXIuIE5pbGFpIGRyb3BvdXQgeWFuZyB0ZXJsYWx1IGJlc2FyIGRhcGF0IG1lbnllYmFia2FuIGhpbGFuZ255YSBpbmZvcm1hc2kgcGVudGluZyBzZWxhbWEgcHJvc2VzIHBlbGF0aWhhbiBzZWhpbmdnYSBwZXJmb3JtYSBtb2RlbCBzZWRpa2l0IG1lbnVydW4uIEhhbCBpbmkgdGVybGloYXQgZGFyaSBwZW51cnVuYW4gdmFsaWRhdGlvbiBhY2N1cmFjeSBwYWRhIGJlYmVyYXBhIGtvbmZpZ3VyYXNpIHlhbmcgbWVuZ2d1bmFrYW4gZHJvcG91dCAwLDUuDQoNCkJlcmRhc2Fya2FuIGhhc2lsIHRlcnNlYnV0LCBrb25maWd1cmFzaSBGaWx0ZXIgPSA2NCwgRGVuc2UgVW5pdHMgPSAyNTYsIGRhbiBEcm9wb3V0ID0gMCwzIGRpcGlsaWggc2ViYWdhaSBoeXBlcnBhcmFtZXRlciB0ZXJiYWlrIGRhbiBkaWd1bmFrYW4gcGFkYSB0YWhhcCBwZWxhdGloYW4gbW9kZWwgQ05OIGFraGlyIGthcmVuYSBtZW5naGFzaWxrYW4gbmlsYWkgdmFsaWRhdGlvbiBhY2N1cmFjeSB0ZXJ0aW5nZ2kgc2VraXRhciA5MCUuDQoNCiMjIDUuMiBLdXJ2YSBQZWxhdGloYW4NCg0KS3VydmEgcGVsYXRpaGFuIGRpZ3VuYWthbiB1bnR1ayBtZW1hbnRhdSBwZXJrZW1iYW5nYW4gcGVyZm9ybWEgbW9kZWwgc2VsYW1hIHByb3NlcyBwZW1iZWxhamFyYW4uIEdyYWZpayB5YW5nIGRpaGFzaWxrYW4gdW11bW55YSB0ZXJkaXJpIGF0YXMgZHVhIGJhZ2lhbiwgeWFpdHUgZ3JhZmlrIGFrdXJhc2kgKGFjY3VyYWN5KSBkYW4gZ3JhZmlrIGxvc3MgeWFuZyBkaXRhbXBpbGthbiB1bnR1ayBkYXRhIHBlbGF0aWhhbiAodHJhaW5pbmcpIG1hdXB1biBkYXRhIHZhbGlkYXNpICh2YWxpZGF0aW9uKS4NCg0KYGBge3IgcGxvdC1oaXN0b3J5LCBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD01fQ0KcGxvdChoaXN0b3J5KQ0KYGBgDQoNCjxkaXYgYWxpZ249ImNlbnRlciI+DQoqKkdhbWJhciAzLioqIEt1cnZhIFBlbGF0aWhhbg0KPC9kaXY+DQoNCioqSW50ZXJwcmV0YXNpOioqDQoNClBhZGEgZ3JhZmlrIGFrdXJhc2ksIHN1bWJ1IGhvcml6b250YWwgbWVudW5qdWtrYW4ganVtbGFoIGVwb2NoLCBzZWRhbmdrYW4gc3VtYnUgdmVydGlrYWwgbWVudW5qdWtrYW4gbmlsYWkgYWt1cmFzaSB5YW5nIGRpcGVyb2xlaCBtb2RlbC4gUGVuaW5na2F0YW4gYWt1cmFzaSBkYXJpIGVwb2NoIGtlIGVwb2NoIG1lbnVuanVra2FuIGJhaHdhIG1vZGVsIHNlbWFraW4gbWFtcHUgbWVuZ2VuYWxpIHBvbGEgeWFuZyB0ZXJkYXBhdCBwYWRhIGRhdGEgcGVsYXRpaGFuLg0KDQpTZW1lbnRhcmEgaXR1LCBncmFmaWsgbG9zcyBtZW5nZ2FtYmFya2FuIHRpbmdrYXQga2VzYWxhaGFuIHByZWRpa3NpIG1vZGVsIHNlbGFtYSBwcm9zZXMgcGVsYXRpaGFuLiBOaWxhaSBsb3NzIHlhbmcgc2VtYWtpbiBtZW51cnVuIG1lbnVuanVra2FuIGJhaHdhIHByZWRpa3NpIG1vZGVsIHNlbWFraW4gbWVuZGVrYXRpIGxhYmVsIHNlYmVuYXJueWEuDQoNCg0KIyMgNS4zIEV2YWx1YXNpIERhdGEgVWppDQoNCkV2YWx1YXNpIG1vZGVsIGRpbGFrdWthbiBtZW5nZ3VuYWthbiBkYXRhIHVqaSAodGVzdGluZyBkYXRhKSB5YW5nIHRpZGFrIGRpZ3VuYWthbiBzZWxhbWEgcHJvc2VzIHBlbGF0aWhhbiBtb2RlbC4gVHVqdWFuIGV2YWx1YXNpIGluaSBhZGFsYWggdW50dWsgbWVuZ3VrdXIga2VtYW1wdWFuIG1vZGVsIGRhbGFtIG1lbGFrdWthbiBnZW5lcmFsaXNhc2kgdGVyaGFkYXAgZGF0YSBiYXJ1IHlhbmcgYmVsdW0gcGVybmFoIGRpbGloYXQgc2ViZWx1bW55YS4NCg0KPGRpdiBhbGlnbj0iY2VudGVyIj4NCioqVGFiZWwgMTUuKiogRXZhbHVhc2kgTW9kZWwgQ05OIHBhZGEgRGF0YSBUZXN0DQo8L2Rpdj4NCg0KYGBge3IgZXZhbHVhc2l9DQpzY29yZSA8LSBtb2RlbCAlPiUgZXZhbHVhdGUoeF90ZXN0LCB5X3Rlc3QsIHZlcmJvc2UgPSAwKQ0KDQp0YWJlbF9zY29yZSA8LSBkYXRhLmZyYW1lKA0KICBNZXRyaWsgPSBjKCJMb3NzIiwgIkFjY3VyYWN5IiksDQogIE5pbGFpICA9IHJvdW5kKGFzLm51bWVyaWModW5saXN0KHNjb3JlKSksIDQpDQopDQoNCmtuaXRyOjprYWJsZSh0YWJlbF9zY29yZSkNCg0KYGBgDQoNCioqSW50ZXJwcmV0YXNpOioqDQoNCkhhc2lsIGV2YWx1YXNpIHBhZGEgZGF0YSB1amkgbWVuZ2hhc2lsa2FuIG5pbGFpIGFjY3VyYWN5IHNla2l0YXIgOTAlIGRhbiBsb3NzIHNla2l0YXIgMCwyMC4gTmlsYWkgYWNjdXJhY3kgc2VraXRhciA5MCUgbWVudW5qdWtrYW4gYmFod2EgbW9kZWwgbWFtcHUgbWVuZ2tsYXNpZmlrYXNpa2FuIHNlYmFnaWFuIGJlc2FyIGNpdHJhIEZhc2hpb24tTU5JU1QgZGVuZ2FuIGJlbmFyLiBOaWxhaSB0ZXJzZWJ1dCBtZW5naW5kaWthc2lrYW4gYmFod2EgbW9kZWwgdGVsYWggYmVyaGFzaWwgbWVtcGVsYWphcmkgcG9sYSB2aXN1YWwgZGFyaSBzZXRpYXAga2F0ZWdvcmkgcGFrYWlhbiBkZW5nYW4gYmFpay4gTmlsYWkgbG9zcyBzZWtpdGFyIDAsMjAgdGVyZ29sb25nIHJlbGF0aWYgcmVuZGFoLCB5YW5nIG1lbnVuanVra2FuIGJhaHdhIGtlc2FsYWhhbiBwcmVkaWtzaSBtb2RlbCBtYXNpaCBiZXJhZGEgcGFkYSB0aW5na2F0IHlhbmcga2VjaWwuIEtvbWJpbmFzaSBuaWxhaSBhY2N1cmFjeSB5YW5nIHRpbmdnaSBkYW4gbG9zcyB5YW5nIHJlbmRhaCBtZW51bmp1a2thbiBiYWh3YSBtb2RlbCBtZW1pbGlraSBwZXJmb3JtYSBrbGFzaWZpa2FzaSB5YW5nIGJhaWsuIEhhc2lsIGluaSBqdWdhIG1lbmdpbmRpa2FzaWthbiBiYWh3YSBtb2RlbCBtYW1wdSBtZWxha3VrYW4gZ2VuZXJhbGlzYXNpIHRlcmhhZGFwIGRhdGEgeWFuZyB0aWRhayBkaWd1bmFrYW4gc2VsYW1hIHByb3NlcyBwZWxhdGloYW4uDQoNCiMjIDUuNCBDb25mdXNpb24gTWF0cml4DQoNCkNvbmZ1c2lvbiBtYXRyaXggZGlndW5ha2FuIHVudHVrIG1lbmdldmFsdWFzaSBwZXJmb3JtYSBtb2RlbCBrbGFzaWZpa2FzaSBzZWNhcmEgbGViaWggcmluY2kgcGFkYSBzZXRpYXAga2F0ZWdvcmkgcGFrYWlhbi4gQmVyYmVkYSBkZW5nYW4gYWNjdXJhY3kgeWFuZyBoYW55YSBtZW1iZXJpa2FuIHNhdHUgbmlsYWkgYWdyZWdhdCwgY29uZnVzaW9uIG1hdHJpeCBtZW1wZXJsaWhhdGthbiBiYWdhaW1hbmEgbW9kZWwgbWVsYWt1a2FuIHByZWRpa3NpIHBhZGEgbWFzaW5nLW1hc2luZyBrZWxhcyBzZWhpbmdnYSBwb2xhIGtlc2FsYWhhbiBrbGFzaWZpa2FzaSBkYXBhdCBkaWlkZW50aWZpa2FzaSBkZW5nYW4gbGViaWggamVsYXMuDQoNCmBgYHtyIGNvbmZ1c2lvbi1tYXRyaXh9DQpwcmVkX3Byb2IgPC0gcHJlZGljdChtb2RlbCwgeF90ZXN0KQ0KDQpwcmVkX2NsYXNzIDwtIGFwcGx5KA0KICBwcmVkX3Byb2IsDQogIDEsDQogIHdoaWNoLm1heA0KKSAtIDENCg0KY29uZl9tYXQgPC0gY29uZnVzaW9uTWF0cml4KA0KICBmYWN0b3IocHJlZF9jbGFzcywgbGV2ZWxzID0gMDo5KSwNCiAgZmFjdG9yKGZhc2hpb25fbW5pc3QkdGVzdCR5LCBsZXZlbHMgPSAwOjkpDQopDQoNCmNvbmZfbWF0DQpgYGANCg0KKipJbnRlcnByZXRhc2k6KioNCg0KUGFkYSBjb25mdXNpb24gbWF0cml4IHlhbmcgZGloYXNpbGthbiwgYmFyaXMgbWVudW5qdWtrYW4ga2VsYXMgaGFzaWwgcHJlZGlrc2kgbW9kZWwgKFByZWRpY3Rpb24pLCBzZWRhbmdrYW4ga29sb20gbWVudW5qdWtrYW4ga2VsYXMgc2ViZW5hcm55YSAoUmVmZXJlbmNlKS4NCg0KLSBOaWxhaSB5YW5nIGJlcmFkYSBwYWRhIGRpYWdvbmFsIHV0YW1hIG1lbnVuanVra2FuIGp1bWxhaCBjaXRyYSB5YW5nIGJlcmhhc2lsIGRpa2xhc2lmaWthc2lrYW4gZGVuZ2FuIGJlbmFyLg0KDQotIE5pbGFpIGRpIGx1YXIgZGlhZ29uYWwgbWVudW5qdWtrYW4ganVtbGFoIGtlc2FsYWhhbiBrbGFzaWZpa2FzaSAobWlzY2xhc3NpZmljYXRpb24pLg0KDQotIFNlYmFnaWFuIGJlc2FyIG5pbGFpIHRlcmJlc2FyIGJlcmFkYSBwYWRhIGRpYWdvbmFsIHV0YW1hIHNlaGluZ2dhIG1lbnVuanVra2FuIGJhaHdhIG1vZGVsIG1hbXB1IG1lbmdrbGFzaWZpa2FzaWthbiBzZWJhZ2lhbiBiZXNhciBjaXRyYSBkZW5nYW4gYmVuYXIuDQoNCg0KIyMgNS41IEhlYXRtYXAgQ29uZnVzaW9uIE1hdHJpeA0KDQpIZWF0bWFwIGNvbmZ1c2lvbiBtYXRyaXggbWVydXBha2FuIHJlcHJlc2VudGFzaSB2aXN1YWwgZGFyaSBoYXNpbCBrbGFzaWZpa2FzaSBtb2RlbCBDb252b2x1dGlvbmFsIE5ldXJhbCBOZXR3b3JrIChDTk4pIHBhZGEgZGF0YXNldCBGYXNoaW9uLU1OSVNULiBTdW1idSB2ZXJ0aWthbCAoUmVmZXJlbmNlKSBtZW51bmp1a2thbiBrZWxhcyBha3R1YWwsIHNlZGFuZ2thbiBzdW1idSBob3Jpem9udGFsIChQcmVkaWN0aW9uKSBtZW51bmp1a2thbiBrZWxhcyBoYXNpbCBwcmVkaWtzaSBtb2RlbC4NCg0KYGBge3IgfQ0KY20gPC0gYXMuZGF0YS5mcmFtZShjb25mX21hdCR0YWJsZSkNCg0KZ2dwbG90KA0KICBjbSwNCiAgYWVzKFByZWRpY3Rpb24sIFJlZmVyZW5jZSwgZmlsbCA9IEZyZXEpDQopICsNCiAgZ2VvbV90aWxlKCkgKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsID0gRnJlcSkpICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICJIZWF0bWFwIENvbmZ1c2lvbiBNYXRyaXgiDQogICkNCmBgYA0KDQo8ZGl2IGFsaWduPSJjZW50ZXIiPg0KKipHYW1iYXIgNC4qKiBIZWF0bWFwIENvbmZ1c2lvbiBNYXRyaXgNCjwvZGl2Pg0KDQoqKkludGVycHJldGFzaToqKg0KDQpIZWF0bWFwIGNvbmZ1c2lvbiBtYXRyaXggbWVydXBha2FuIHJlcHJlc2VudGFzaSB2aXN1YWwgZGFyaSBjb25mdXNpb24gbWF0cml4Lg0KDQotIFdhcm5hIHlhbmcgc2VtYWtpbiB0ZXJhbmcgbWVudW5qdWtrYW4ganVtbGFoIG9ic2VydmFzaSB5YW5nIHNlbWFraW4gYmVzYXIuDQotIFdhcm5hIHlhbmcgbGViaWggZ2VsYXAgbWVudW5qdWtrYW4ganVtbGFoIG9ic2VydmFzaSB5YW5nIGxlYmloIHNlZGlraXQuDQotIERvbWluYXNpIHdhcm5hIHRlcmFuZyBwYWRhIGRpYWdvbmFsIHV0YW1hIG1lbnVuanVra2FuIGJhaHdhIHNlYmFnaWFuIGJlc2FyIHByZWRpa3NpIG1vZGVsIHNlc3VhaSBkZW5nYW4ga2VsYXMgc2ViZW5hcm55YS4NCg0KS29uc2VudHJhc2kgd2FybmEgZ2VsYXAgZGkgbHVhciBkaWFnb25hbCB0ZXJsaWhhdCBwYWRhIGtlbG9tcG9rIHBha2FpYW4gYmFnaWFuIGF0YXMgc2VwZXJ0aSBTaGlydCwgUHVsbG92ZXIsIENvYXQsIGRhbiBULXNoaXJ0L1RvcCwgeWFuZyBtZW51bmp1a2thbiBiYWh3YSBrZXNhbGFoYW4ga2xhc2lmaWthc2kgcGFsaW5nIGJhbnlhayB0ZXJqYWRpIHBhZGEga2VsYXMta2VsYXMgeWFuZyBtZW1pbGlraSBrZW1pcmlwYW4gdmlzdWFsIHRpbmdnaS4NCg0KIyMgNS42IENsYXNzaWZpY2F0aW9uIFJlcG9ydA0KDQpDbGFzc2lmaWNhdGlvbiByZXBvcnQgZGlndW5ha2FuIHVudHVrIG1lbmdldmFsdWFzaSBwZXJmb3JtYSBtb2RlbCBrbGFzaWZpa2FzaSBtZW5nZ3VuYWthbiBiZWJlcmFwYSBtZXRyaWsgc2VrYWxpZ3VzIHNlaGluZ2dhIG1lbWJlcmlrYW4gZ2FtYmFyYW4geWFuZyBsZWJpaCBrb21wcmVoZW5zaWYgZGliYW5kaW5na2FuIGhhbnlhIG1lbmdndW5ha2FuIGFjY3VyYWN5Lg0KDQo8ZGl2IGFsaWduPSJjZW50ZXIiPg0KKipUYWJlbCAxNi4qKiBFdmFsdWFzaSBQZXJmb3JtYSBNb2RlbCBDTk4gRmFzaGlvbiBNTklTVA0KPC9kaXY+DQoNCmBgYHtyIGNsYXNzaWZpY2F0aW9uLXJlcG9ydH0NCmhhc2lsIDwtIGRhdGEuZnJhbWUoDQogIHRydXRoICAgID0gZmFjdG9yKGZhc2hpb25fbW5pc3QkdGVzdCR5KSwNCiAgZXN0aW1hdGUgPSBmYWN0b3IocHJlZF9jbGFzcykNCikNCg0KYWNjICA8LSBhY2N1cmFjeShoYXNpbCwgdHJ1dGgsIGVzdGltYXRlKQ0KcHJlYyA8LSBwcmVjaXNpb24oaGFzaWwsIHRydXRoLCBlc3RpbWF0ZSwgZXN0aW1hdG9yID0gIm1hY3JvIikNCnJlYyAgPC0gcmVjYWxsKGhhc2lsLCB0cnV0aCwgZXN0aW1hdGUsIGVzdGltYXRvciA9ICJtYWNybyIpDQpmMSAgIDwtIGZfbWVhcyhoYXNpbCwgdHJ1dGgsIGVzdGltYXRlLCBlc3RpbWF0b3IgPSAibWFjcm8iKQ0KDQp0YWJlbF9tZXRyaWNzIDwtIGRhdGEuZnJhbWUoDQogIE1ldHJpayA9IGMoIkFjY3VyYWN5IiwgIlByZWNpc2lvbiAoTWFjcm8pIiwgIlJlY2FsbCAoTWFjcm8pIiwgIkYxLVNjb3JlIChNYWNybykiKSwNCiAgTmlsYWkgID0gcm91bmQoYyhhY2MkLmVzdGltYXRlLCBwcmVjJC5lc3RpbWF0ZSwgcmVjJC5lc3RpbWF0ZSwgZjEkLmVzdGltYXRlKSwgNCkNCikNCg0Ka25pdHI6OmthYmxlKA0KICB0YWJlbF9tZXRyaWNzLA0KICBjb2wubmFtZXMgPSBjKCJNZXRyaWsiLCAiTmlsYWkiKSkNCmBgYA0KDQoqKkludGVycHJldGFzaToqKg0KDQpOaWxhaSBhY2N1cmFjeSwgcHJlY2lzaW9uLCByZWNhbGwsIGRhbiBGMS1zY29yZSB5YW5nIHNlbXVhbnlhIGJlcmFkYSBkaSBzZWtpdGFyIDAsOTAgbWVudW5qdWtrYW4gYmFod2EgbW9kZWwgQ05OIG1lbWlsaWtpIHBlcmZvcm1hIGtsYXNpZmlrYXNpIHlhbmcgc2FuZ2F0IGJhaWsgZGFuIHNlaW1iYW5nLiBQZW5nZ3VuYWFuIGVzdGltYXRvciBtYWNybyBiZXJhcnRpIHNldGlhcCBrZWxhcyBiZXJrb250cmlidXNpIHNhbWEgZGFsYW0gcGVyaGl0dW5nYW4gbWV0cmlrLCBzZWhpbmdnYSBoYXNpbCBpbmkgbWVuY2VybWlua2FuIHBlcmZvcm1hIG1vZGVsIHNlY2FyYSBtZXJhdGEgcGFkYSBzZWx1cnVoIGthdGVnb3JpIHBha2FpYW4uDQoNCiMjIDUuNyBFdmFsdWFzaSBLaW5lcmphIFBhZGEgU2V0aWFwIEtlbGFzDQoNCjxkaXYgYWxpZ249ImNlbnRlciI+DQoqKlRhYmVsIDE3LioqIEFrdXJhc2kgTW9kZWwgQ05OIHBlciBLZWxhcw0KPC9kaXY+DQoNCmBgYHtyIGJlc3Qtd29yc3R9DQoNCmtlbGFzX25hbWEgPC0gYygiVC1zaGlydC9Ub3AiLCAiVHJvdXNlciIsICJQdWxsb3ZlciIsICJEcmVzcyIsICJDb2F0IiwNCiAgICAgICAgICAgICAgICAiU2FuZGFsIiwgIlNoaXJ0IiwgIlNuZWFrZXIiLCAiQmFnIiwgIkFua2xlIEJvb3QiKQ0KDQphY2NfY2xhc3MgPC0gZGlhZyhjb25mX21hdCR0YWJsZSkgLyBjb2xTdW1zKGNvbmZfbWF0JHRhYmxlKQ0KaGFzaWxfa2VsYXMgPC0gZGF0YS5mcmFtZSgNCiAgS2VsYXMgICAgPSBrZWxhc19uYW1hLA0KICBBY2N1cmFjeSA9IHJvdW5kKGFjY19jbGFzcywgMykNCikNCg0KIyBUYWJlbCBzZW11YSBrZWxhcw0Ka25pdHI6OmthYmxlKA0KICBoYXNpbF9rZWxhcywNCiAgY2FwdGlvbiAgID0gIkFrdXJhc2kgTW9kZWwgQ05OIHBlciBLZWxhcyIsDQogIGNvbC5uYW1lcyA9IGMoIktlbGFzIiwgIkFjY3VyYWN5IikNCikNCg0KYGBgDQoNCioqSW50ZXJwcmV0YXNpOioqDQoNCkhhc2lsIGV2YWx1YXNpIHBlciBrZWxhcyBtZW51bmp1a2thbiBiYWh3YSBrYXRlZ29yaSBUcm91c2VyLCBTYW5kYWwsIEJhZywgU25lYWtlciwgZGFuIEFua2xlIEJvb3QgbWVtcGVyb2xlaCBha3VyYXNpIHRlcnRpbmdnaSBrYXJlbmEgbWVtaWxpa2kgYmVudHVrIHZpc3VhbCB5YW5nIGtoYXMgZGFuIG11ZGFoIGRpYmVkYWthbi4gU2ViYWxpa255YSwga2F0ZWdvcmkgU2hpcnQgbWVtaWxpa2kgYWt1cmFzaSB0ZXJlbmRhaCBzZWtpdGFyIDAsNzAga2FyZW5hIHRpbmdnaW55YSBrZW1pcmlwYW4gYmVudHVrIHZpc3VhbCBkZW5nYW4ga2F0ZWdvcmkgVC1zaGlydC9Ub3AsIENvYXQsIGRhbiBQdWxsb3Zlci4NCg0KIyMgNS44IEFuYWxpc2lzIEtlc2FsYWhhbiBLbGFzaWZpa2FzaSAoTWlzY2xhc3NpZmljYXRpb24gQW5hbHlzaXMpDQoNClNlbGFpbiBtZW5nZXZhbHVhc2kgbW9kZWwgbWVuZ2d1bmFrYW4gbWV0cmlrIGt1YW50aXRhdGlmIHNlcGVydGkgYWNjdXJhY3ksIHByZWNpc2lvbiwgcmVjYWxsLCBkYW4gRjEtc2NvcmUsIHBlbnRpbmcgdW50dWsgbWVuZ2FuYWxpc2lzIGNpdHJhIHlhbmcgbWVuZ2FsYW1pIGtlc2FsYWhhbiBrbGFzaWZpa2FzaS4gQW5hbGlzaXMgaW5pIGJlcnR1anVhbiB1bnR1ayBtZW1haGFtaSBrYXJha3RlcmlzdGlrIGdhbWJhciB5YW5nIHN1bGl0IGRpa2VuYWxpIG9sZWggbW9kZWwgc2VydGEgbWVuZ2lkZW50aWZpa2FzaSBwb2xhIGtlc2FsYWhhbiB5YW5nIHNlcmluZyB0ZXJqYWRpLg0KDQoNCmBgYHtyfQ0KaWQ9IjRmeGc4NSINCiMgaW5kZWtzIGNpdHJhIHlhbmcgc2FsYWggZGlwcmVkaWtzaQ0Kd3JvbmdfaWR4IDwtIHdoaWNoKA0KICBwcmVkX2NsYXNzICE9IGZhc2hpb25fbW5pc3QkdGVzdCR5DQopDQoNCiMgbWVuYW1waWxrYW4gOSBjb250b2gga2VzYWxhaGFuIGtsYXNpZmlrYXNpDQpwYXIobWZyb3cgPSBjKDMsMykpDQoNCmZvcihpIGluIHdyb25nX2lkeFsxOjldKXsNCg0KICBpbWFnZSgNCiAgICB0KGFwcGx5KA0KICAgICAgZmFzaGlvbl9tbmlzdCR0ZXN0JHhbaSwsXSwNCiAgICAgIDIsDQogICAgICByZXYNCiAgICApKSwNCiAgICBjb2wgPSBncmF5KCgwOjI1NSkvMjU1KSwNCiAgICBheGVzID0gRkFMU0UsDQogICAgbWFpbiA9IHBhc3RlKA0KICAgICAgIkFrdHVhbDoiLA0KICAgICAga2VsYXNfbmFtYVsNCiAgICAgICAgZmFzaGlvbl9tbmlzdCR0ZXN0JHlbaV0gKyAxDQogICAgICBdLA0KICAgICAgIlxuUHJlZGlrc2k6IiwNCiAgICAgIGtlbGFzX25hbWFbDQogICAgICAgIHByZWRfY2xhc3NbaV0gKyAxDQogICAgICBdDQogICAgKQ0KICApDQp9DQpgYGANCg0KPGRpdiBhbGlnbj0iY2VudGVyIj4NCioqR2FtYmFyIDUuKiogQ2l0cmEgWWFuZyBTYWxhaCBEaWtsYXNpZmlrYXNpa2FuDQo8L2Rpdj4NCg0KKipJbnRlcnByZXRhc2k6KioNCg0KQmViZXJhcGEgZmFrdG9yIHlhbmcgZGFwYXQgbWVueWViYWJrYW4ga2VzYWxhaGFuIGtsYXNpZmlrYXNpIGFudGFyYSBsYWluOg0KDQotICBSZXNvbHVzaSBjaXRyYSB5YW5nIHJlbGF0aWYga2VjaWwgKDI4IMOXIDI4IHBpa3NlbCkuDQotIEtlbWlyaXBhbiBiZW50dWsgdmlzdWFsIGFudGFyIGthdGVnb3JpIHBha2FpYW4uDQpIaWxhbmdueWEgZGV0YWlsIHBlbnRpbmcgYWtpYmF0IHBlbmdndW5hYW4gY2l0cmEgZ3JheXNjYWxlLg0KLSBWYXJpYXNpIGJlbnR1ayBkYW4gcG9zaXNpIG9iamVrIHBhZGEgc2V0aWFwIGdhbWJhci4NCi0gS2V0ZXJiYXRhc2FuIG1vZGVsIGRhbGFtIG1lbWJlZGFrYW4gZml0dXIgeWFuZyBzYW5nYXQgbWlyaXAgYW50YXIga2VsYXMuDQoNCkhhc2lsIGFuYWxpc2lzIG1lbnVuanVra2FuIGJhaHdhIHNlYmFnaWFuIGJlc2FyIGtlc2FsYWhhbiB0ZXJqYWRpIHBhZGEga2F0ZWdvcmkgeWFuZyBtZW1hbmcgbWVtaWxpa2kga2VtaXJpcGFuIHZpc3VhbCB0aW5nZ2ksIHRlcnV0YW1hIHBhZGEga2Vsb21wb2sgcGFrYWlhbiBiYWdpYW4gYXRhcyBzZXBlcnRpIFNoaXJ0LCBQdWxsb3ZlciwgQ29hdCwgZGFuIERyZXNzLiBUZW11YW4gaW5pIHNlamFsYW4gZGVuZ2FuIGhhc2lsIGNvbmZ1c2lvbiBtYXRyaXggeWFuZyBtZW51bmp1a2thbiBiYWh3YSBrZWxhcy1rZWxhcyB0ZXJzZWJ1dCBtZW1pbGlraSB0aW5na2F0IGtlc2FsYWhhbiBrbGFzaWZpa2FzaSB5YW5nIGxlYmloIHRpbmdnaSBkaWJhbmRpbmdrYW4ga2F0ZWdvcmkgbGFpbi4NCg0KTWVza2lwdW4gZGVtaWtpYW4sIG1vZGVsIHRldGFwIG1hbXB1IG1lbmNhcGFpIHRpbmdrYXQgYWt1cmFzaSB5YW5nIHRpbmdnaSBkYW4gYmVyaGFzaWwgbWVuZ2VuYWxpIHNlYmFnaWFuIGJlc2FyIGthdGVnb3JpIGRlbmdhbiBiYWlrLiBIYXNpbCBpbmkgbWVudW5qdWtrYW4gYmFod2EgbW9kZWwgQ05OIHRlbGFoIG1hbXB1IG1lbXBlbGFqYXJpIHBvbGEgdXRhbWEgZGFyaSBkYXRhc2V0IEZhc2hpb24tTU5JU1QsIG1lc2tpcHVuIG1hc2loIHRlcmRhcGF0IGJlYmVyYXBhIGtlc2FsYWhhbiBwYWRhIGthdGVnb3JpIHlhbmcgbWVtaWxpa2kga2FyYWt0ZXJpc3RpayB2aXN1YWwgeWFuZyBoYW1waXIgc2FtYS4NCg0KDQojIDYuIEludGVycHJldGFzaSBkYW4gRGlza3VzaQ0KDQpTZWNhcmEga2VzZWx1cnVoYW4sIG1vZGVsIENvbnZvbHV0aW9uYWwgTmV1cmFsIE5ldHdvcmsgKENOTikgeWFuZyBkaWJhbmd1biBiZXJoYXNpbCBtZW5na2xhc2lmaWthc2lrYW4gY2l0cmEgRmFzaGlvbi1NTklTVCBkZW5nYW4gcGVyZm9ybWEgeWFuZyBiYWlrLiBIYXNpbCBldmFsdWFzaSBwYWRhIGRhdGEgdWppIG1lbnVuanVra2FuIG5pbGFpIGFjY3VyYWN5LCBwcmVjaXNpb24sIHJlY2FsbCwgZGFuIEYxLXNjb3JlIHlhbmcgc2VtdWFueWEgYmVyYWRhIGRpIHNla2l0YXIgOTAlLiBIYWwgaW5pIG1lbmdpbmRpa2FzaWthbiBiYWh3YSBtb2RlbCBtYW1wdSBtZW1wZWxhamFyaSBrYXJha3RlcmlzdGlrIHZpc3VhbCBkYXJpIHNldGlhcCBrYXRlZ29yaSBwYWthaWFuIHNlY2FyYSBlZmVrdGlmIG1lbGFsdWkgbWVrYW5pc21lIGVrc3RyYWtzaSBmaXR1ciBzZWNhcmEgYmVydGFoYXAgcGFkYSBzZXRpYXAgbGFwaXNhbiBrb252b2x1c2kuDQoNClByb3NlcyBoeXBlcnBhcmFtZXRlciB0dW5pbmcgYmVyaGFzaWwgbWVuZW11a2FuIGtvbmZpZ3VyYXNpIG9wdGltYWwgZGVuZ2FuIDY0IGZpbHRlciwgMjU2IGRlbnNlIHVuaXRzLCBkYW4gZHJvcG91dCAwLDMgeWFuZyBtZW5naGFzaWxrYW4gdmFsaWRhdGlvbiBhY2N1cmFjeSB0ZXJ0aW5nZ2kuIEtvbmZpZ3VyYXNpIGluaSBzZWxhbmp1dG55YSBkaWd1bmFrYW4gdW50dWsgbWVtYmFuZ3VuIG1vZGVsIGFraGlyIHlhbmcgZGlsYXRpaCBzZWxhbWEgMTAgZXBvY2ggZGVuZ2FuIGJhdGNoIHNpemUgMTI4Lg0KDQojIyA2LjEgS2VrdWF0YW4gTW9kZWwNCg0KTW9kZWwgQ05OIHlhbmcgZGliYW5ndW4gbWVudW5qdWtrYW4gYmViZXJhcGEga2VrdWF0YW4geWFuZyBtZW5vbmpvbC4gUGVydGFtYSwgbW9kZWwgYmVyaGFzaWwgbWVuY2FwYWkgYWt1cmFzaSB5YW5nIHRpbmdnaSBwYWRhIHNlYmFnaWFuIGJlc2FyIGthdGVnb3JpIHBha2FpYW4sIHRlcnV0YW1hIHBhZGEga2F0ZWdvcmkgeWFuZyBtZW1pbGlraSBiZW50dWsgdmlzdWFsIGtoYXMgc2VwZXJ0aSBUcm91c2VyLCBTYW5kYWwsIEJhZywgU25lYWtlciwgZGFuIEFua2xlIEJvb3QuIEthdGVnb3JpLWthdGVnb3JpIHRlcnNlYnV0IGxlYmloIG11ZGFoIGRpYmVkYWthbiBvbGVoIG1vZGVsIGthcmVuYSBtZW1pbGlraSBrYXJha3RlcmlzdGlrIHZpc3VhbCB5YW5nIHVuaWsgZGFuIHRpZGFrIGJhbnlhayB0dW1wYW5nIHRpbmRpaCBkZW5nYW4ga2F0ZWdvcmkgbGFpbi4NCg0KS2VkdWEsIGt1cnZhIHBlbGF0aWhhbiBtZW51bmp1a2thbiBiYWh3YSBtb2RlbCBiZWxhamFyIGRlbmdhbiBiYWlrIHRhbnBhIGluZGlrYXNpIG92ZXJmaXR0aW5nIHlhbmcgc2lnbmlmaWthbi4gTmlsYWkgYWt1cmFzaSBkYW4gbG9zcyBwYWRhIGRhdGEgcGVsYXRpaGFuIG1hdXB1biB2YWxpZGFzaSBiZXJnZXJhayBzZWNhcmEga29uc2lzdGVuIHRhbnBhIHBlcmJlZGFhbiB5YW5nIGJlc2FyLCBzZWhpbmdnYSBtb2RlbCBtZW1pbGlraSBrZW1hbXB1YW4gZ2VuZXJhbGlzYXNpIHlhbmcgYmFpayB0ZXJoYWRhcCBkYXRhIGJhcnUuIEtldGlnYSwgcGVuZ2d1bmFhbiB0ZWtuaWsgZHJvcG91dCBkZW5nYW4gbmlsYWkgMCwzIHRlcmJ1a3RpIGVmZWt0aWYgc2ViYWdhaSByZWd1bGFyaXNhc2kgeWFuZyBtZW1iYW50dSBtb2RlbCBtZW5naGluZGFyaSBrZXRlcmdhbnR1bmdhbiBiZXJsZWJpaGFuIHBhZGEgbmV1cm9uIHRlcnRlbnR1IHNlbGFtYSBwcm9zZXMgcGVsYXRpaGFuLg0KDQojIyA2LjIgS2VsZW1haGFuIGRhbiBQb2xhIEtlc2FsYWhhbg0KDQpNZXNraXB1biBtb2RlbCBtZW5jYXBhaSBwZXJmb3JtYSB5YW5nIGJhaWsgc2VjYXJhIGtlc2VsdXJ1aGFuLCB0ZXJkYXBhdCBiZWJlcmFwYSBrZWxlbWFoYW4geWFuZyBwZXJsdSBkaXBlcmhhdGlrYW4uIEtlc2FsYWhhbiBrbGFzaWZpa2FzaSBwYWxpbmcgYmFueWFrIGRpdGVtdWthbiBwYWRhIGtlbG9tcG9rIHBha2FpYW4gYmFnaWFuIGF0YXMgc2VwZXJ0aSBTaGlydCwgUHVsbG92ZXIsIENvYXQsIERyZXNzLCBkYW4gVC1zaGlydC9Ub3AuIEthdGVnb3JpIFNoaXJ0IG1lbmphZGkga2VsYXMgZGVuZ2FuIGFrdXJhc2kgdGVyZW5kYWggc2VraXRhciAwLDcwIGthcmVuYSB0aW5nZ2lueWEga2VtaXJpcGFuIGJlbnR1ayB2aXN1YWwgZGVuZ2FuIGthdGVnb3JpIGxhaW4gZGFsYW0ga2Vsb21wb2sgeWFuZyBzYW1hLg0KDQpQb2xhIGtlc2FsYWhhbiB5YW5nIHBhbGluZyBkb21pbmFuIGFudGFyYSBsYWluIFB1bGxvdmVyIHlhbmcgZGlwcmVkaWtzaSBzZWJhZ2FpIFNoaXJ0LCBDb2F0IHlhbmcgZGlwcmVkaWtzaSBzZWJhZ2FpIFNoaXJ0IGF0YXUgUHVsbG92ZXIsIHNlcnRhIFNoaXJ0IHlhbmcgZGlwcmVkaWtzaSBzZWJhZ2FpIFQtc2hpcnQvVG9wLiBLZXNhbGFoYW4ta2VzYWxhaGFuIGluaSB0ZXJqYWRpIGthcmVuYSBrYXRlZ29yaSB0ZXJzZWJ1dCBtZW1pbGlraSBiZW50dWsgZGFzYXIgeWFuZyBoYW1waXIgc2FtYSwgeWFpdHUgcGFrYWlhbiBiYWdpYW4gYXRhcyB5YW5nIHN1bGl0IGRpYmVkYWthbiBwYWRhIHJlc29sdXNpIHJlbmRhaCB0YW5wYSBpbmZvcm1hc2kgd2FybmEuDQoNCiMjIDYuNCBLZXRlcmJhdGFzYW4gUGVuZWxpdGlhbg0KDQpQZW5lbGl0aWFuIGluaSBtZW1pbGlraSBiZWJlcmFwYSBrZXRlcmJhdGFzYW4geWFuZyBwZXJsdSBkaXBlcmhhdGlrYW4uIFBlcnRhbWEsIGRhdGFzZXQgRmFzaGlvbi1NTklTVCBoYW55YSB0ZXJkaXJpIGRhcmkgY2l0cmEgZ3JheXNjYWxlIGJlcnVrdXJhbiAyONcyOCBwaWtzZWwgc2VoaW5nZ2EgZGV0YWlsIHZpc3VhbCBwZW50aW5nIHNlcGVydGkgdGVrc3R1ciBkYW4gd2FybmEgdGlkYWsgZGFwYXQgZGlyZXByZXNlbnRhc2lrYW4gc2VjYXJhIG1lbWFkYWkuIEtlZHVhLCBwcm9zZXMgaHlwZXJwYXJhbWV0ZXIgdHVuaW5nIHlhbmcgZGlsYWt1a2FuIG1hc2loIHRlcmJhdGFzIHBhZGEgYmViZXJhcGEga29tYmluYXNpIHBhcmFtZXRlciBkZW5nYW4ganVtbGFoIGVwb2NoIHlhbmcga2VjaWwsIHNlaGluZ2dhIGtvbmZpZ3VyYXNpIHlhbmcgZGlwZXJvbGVoIGJlbHVtIHRlbnR1IG1lcnVwYWthbiB5YW5nIHBhbGluZyBvcHRpbWFsLiBLZXRpZ2EsIGFyc2l0ZWt0dXIgQ05OIHlhbmcgZGlndW5ha2FuIHRlcmdvbG9uZyBzZWRlcmhhbmEgZGFuIHRpZGFrIG1lbmVyYXBrYW4gdGVrbmlrIGRhdGEgYXVnbWVudGF0aW9uLCBzZWhpbmdnYSBrZW1hbXB1YW4gbW9kZWwgZGFsYW0gbWVuZ2VuYWxpIHZhcmlhc2kgb2JqZWsgZGkgbHVhciBkYXRhc2V0IG1hc2loIHRlcmJhdGFzLiBLZWVtcGF0LCBtb2RlbCB5YW5nIGRpaGFzaWxrYW4gYmVsdW0gdGVudHUgZGFwYXQgZGlnZW5lcmFsaXNhc2lrYW4gc2VjYXJhIGxhbmdzdW5nIHBhZGEgY2l0cmEgcHJvZHVrIGZhc2hpb24gbnlhdGEgeWFuZyBtZW1pbGlraSB2YXJpYXNpIGtvbmRpc2kgbGViaWgga29tcGxla3MuDQoNCiMgNy4gS2VzaW1wdWxhbiBkYW4gU2FyYW4NCg0KIyMgNy4xIEtlc2ltcHVsYW4NCg0KQmVyZGFzYXJrYW4gaGFzaWwgcGVuZWxpdGlhbiB5YW5nIHRlbGFoIGRpbGFrdWthbiwgbXVsYWkgZGFyaSB0YWhhcCBwcmVwcm9jZXNzaW5nIGRhdGEsIHBlbWJhbmd1bmFuIG1vZGVsIENvbnZvbHV0aW9uYWwgTmV1cmFsIE5ldHdvcmsgKENOTiksIHByb3NlcyBwZWxhdGloYW4sIGhpbmdnYSBldmFsdWFzaSBtb2RlbCwgZGlwZXJvbGVoIGJlYmVyYXBhIGtlc2ltcHVsYW4gc2ViYWdhaSBiZXJpa3V0Lg0KDQoxLiBNZXRvZGUgQ29udm9sdXRpb25hbCBOZXVyYWwgTmV0d29yayAoQ05OKSBiZXJoYXNpbCBkaXRlcmFwa2FuIHVudHVrIG1lbGFrdWthbiBrbGFzaWZpa2FzaSBjaXRyYSBwYWRhIGRhdGFzZXQgRmFzaGlvbi1NTklTVC4gQXJzaXRla3R1ciB5YW5nIGRpZ3VuYWthbiB0ZXJkaXJpIGRhcmkgbGFwaXNhbiBrb252b2x1c2ksIHBvb2xpbmcsIGZsYXR0ZW4sIGRlbnNlIGxheWVyLCBkcm9wb3V0LCBkYW4gb3V0cHV0IGxheWVyIGRlbmdhbiBmdW5nc2kgYWt0aXZhc2kgc29mdG1heCB5YW5nIG1hbXB1IG1lbmdla3N0cmFrc2kgZml0dXIgdmlzdWFsIHNlY2FyYSBvdG9tYXRpcyBkYW4gbWVsYWt1a2FuIGtsYXNpZmlrYXNpIG11bHRpLWtlbGFzIHNlY2FyYSBlZmVrdGlmLg0KDQoyLiBTZWJlbHVtIHBlbGF0aWhhbiBtb2RlbCBha2hpciBkaWxha3VrYW4gaHlwZXJwYXJhbWV0ZXIgdHVuaW5nIHVudHVrIG1lbXBlcm9sZWgga29uZmlndXJhc2kgQ05OIHlhbmcgb3B0aW1hbC4gSGFzaWwgdHVuaW5nIG1lbnVuanVra2FuIGJhaHdhIGtvbWJpbmFzaSAqKjY0IGZpbHRlciwgMjU2IGRlbnNlIHVuaXRzLCBkYW4gZHJvcG91dCAwLDMqKiBtZW5naGFzaWxrYW4gdmFsaWRhdGlvbiBhY2N1cmFjeSB0ZXJ0aW5nZ2kgc2VraXRhciAqKjkwICUqKi4gTW9kZWwgYWtoaXIgeWFuZyBkaWJhbmd1biBtZW5nZ3VuYWthbiBrb25maWd1cmFzaSB0ZXJzZWJ1dCBtZW1wZXJvbGVoIGFjY3VyYWN5IHBlbmd1amlhbiBzZWtpdGFyICoqOTAlKiouDQoNCjMuIEhhc2lsIGV2YWx1YXNpIHBlciBrZWxhcyBtZW51bmp1a2thbiBiYWh3YSBrYXRlZ29yaSAqKlNhbmRhbCoqIGRhbiAqKkJvb3QqKiBtZW1pbGlraSBwZXJmb3JtYSB0ZXJiYWlrLCBkaWlrdXRpIG9sZWgga2F0ZWdvcmkgKipUcm91c2VyLCBCYWcgLCBkYW4gU25lYWtlciAgc2VraXRhciAwLDkwKiouIFNlYmFsaWtueWEsIGthdGVnb3JpIFNoaXJ0IG1lbWlsaWtpIHBlcmZvcm1hIHRlcmVuZGFoIGRlbmdhbiBuaWxhaSBzZWtpdGFyICoqMCw3MCoqIGthcmVuYSBzZXJpbmcgbWVuZ2FsYW1pIGtlc2FsYWhhbiBrbGFzaWZpa2FzaSBkZW5nYW4ga2F0ZWdvcmkgVC1zaGlydC9Ub3AsIENvYXQsIGRhbiBQdWxsb3ZlciB5YW5nIG1lbWlsaWtpIGthcmFrdGVyaXN0aWsgdmlzdWFsIHNlcnVwYS4NCg0KNC4gQW5hbGlzaXMgY29uZnVzaW9uIG1hdHJpeCBkYW4gbWlzY2xhc3NpZmljYXRpb24gbWVudW5qdWtrYW4gYmFod2Egc2ViYWdpYW4gYmVzYXIga2VzYWxhaGFuIGtsYXNpZmlrYXNpIHRlcmphZGkgcGFkYSBrYXRlZ29yaSBwYWthaWFuIGJhZ2lhbiBhdGFzIHNlcGVydGkgKipTaGlydCwgUHVsbG92ZXIsIENvYXQsIERyZXNzLCBkYW4gVC1zaGlydC9Ub3AqKi4gS2VzYWxhaGFuIHRlcnNlYnV0IGRpc2ViYWJrYW4gb2xlaCB0aW5nZ2lueWEga2VtaXJpcGFuIGJlbnR1ayB2aXN1YWwgYW50YXIga2F0ZWdvcmkgc2VydGEga2V0ZXJiYXRhc2FuIHJlc29sdXNpIGNpdHJhIEZhc2hpb24tTU5JU1QgeWFuZyBoYW55YSBiZXJ1a3VyYW4gKioyOCDDlyAyOCBwaWtzZWwqKi4gQmViZXJhcGEgcG9sYSBrZXNhbGFoYW4geWFuZyBzZXJpbmcgZGl0ZW11a2FuIGFudGFyYSBsYWluICoqUHVsbG92ZXIgeWFuZyBkaXByZWRpa3NpIHNlYmFnYWkgU2hpcnQsIENvYXQgeWFuZyBkaXByZWRpa3NpIHNlYmFnYWkgU2hpcnQgYXRhdSBQdWxsb3ZlciwgRHJlc3MgeWFuZyBkaXByZWRpa3NpIHNlYmFnYWkgU2hpcnQgYXRhdSBDb2F0LCBzZXJ0YSBTaGlydCB5YW5nIGRpcHJlZGlrc2kgc2ViYWdhaSBULXNoaXJ0L1RvcCoqLg0KDQo1LiBNb2RlbCBDTk4geWFuZyBkaWJhbmd1biBtZW1pbGlraSBrZW1hbXB1YW4gZ2VuZXJhbGlzYXNpIHlhbmcgYmFpay4gSGFsIGluaSBkaXR1bmp1a2thbiBvbGVoIGtlc3RhYmlsYW4gcGVyZm9ybWEgcGFkYSBkYXRhIHBlbGF0aWhhbiBkYW4gZGF0YSB2YWxpZGFzaSwgbmlsYWkgbG9zcyB5YW5nIHJlbGF0aWYgcmVuZGFoLCBzZXJ0YSB0aWRhayBkaXRlbXVrYW5ueWEgaW5kaWthc2kgb3ZlcmZpdHRpbmcgeWFuZyBzaWduaWZpa2FuIHNlbGFtYSBwcm9zZXMgcGVsYXRpaGFuLg0KDQpTZWNhcmEga2VzZWx1cnVoYW4sIHBlbmVsaXRpYW4gaW5pIG1lbWJ1a3Rpa2FuIGJhaHdhIG1ldG9kZSBDTk4gbWFtcHUgbWVsYWt1a2FuIGtsYXNpZmlrYXNpIGNpdHJhIEZhc2hpb24tTU5JU1QgZGVuZ2FuIGJhaWsgZGFuIGVmZWt0aWYgdW50dWsgbWVuZ2VuYWxpIGJlcmJhZ2FpIGthdGVnb3JpIHBha2FpYW4gYmVyZGFzYXJrYW4ga2FyYWt0ZXJpc3RpayB2aXN1YWwgeWFuZyBkaW1pbGlraS4gTWVza2lwdW4gbWFzaWggdGVyZGFwYXQga2VzYWxhaGFuIHBhZGEga2F0ZWdvcmkgeWFuZyBtZW1pbGlraSBrZW1pcmlwYW4gdmlzdWFsIHRpbmdnaSwgbW9kZWwgdGV0YXAgbWVudW5qdWtrYW4gcGVyZm9ybWEgeWFuZyBzYW5nYXQgYmFpayBkYW4gYmVycG90ZW5zaSB1bnR1ayBkaWtlbWJhbmdrYW4gbGViaWggbGFuanV0IG1lbGFsdWkgb3B0aW1hc2kgYXJzaXRla3R1ciwgZGF0YSBhdWdtZW50YXRpb24sIG1hdXB1biBwZW55ZXN1YWlhbiBoeXBlcnBhcmFtZXRlci4NCg0KIyMgNy4yIFNhcmFuDQoNCkJlcmRhc2Fya2FuIGhhc2lsIHBlbmVsaXRpYW4geWFuZyBkaXBlcm9sZWgsIGJlYmVyYXBhIHNhcmFuIHlhbmcgZGFwYXQgZGliZXJpa2FuIHVudHVrIHBlbmVsaXRpYW4gc2VsYW5qdXRueWEgYWRhbGFoIHNlYmFnYWkgYmVyaWt1dC4NCg0KMS4gTWVsYWt1a2FuIGh5cGVycGFyYW1ldGVyIHR1bmluZyBzZWNhcmEgbGViaWggbWVuZGFsYW0sIHNlcGVydGkgb3B0aW1hc2kgbGVhcm5pbmcgcmF0ZSwgYmF0Y2ggc2l6ZSwganVtbGFoIGVwb2NoLCBkYW4ganVtbGFoIGZpbHRlciBrb252b2x1c2kgdW50dWsgbWVtcGVyb2xlaCBrb25maWd1cmFzaSBtb2RlbCB5YW5nIGxlYmloIG9wdGltYWwuDQoNCjIuIE1lbmVyYXBrYW4gdGVrbmlrIGRhdGEgYXVnbWVudGF0aW9uLCBzZXBlcnRpIHJvdGFzaSwgem9vbWluZywgc2hpZnRpbmcsIGRhbiBmbGlwcGluZywgdW50dWsgbWVuaW5na2F0a2FuIHZhcmlhc2kgZGF0YSBwZWxhdGloYW4gc2VoaW5nZ2EgbW9kZWwgbWVuamFkaSBsZWJpaCByb2J1c3QgdGVyaGFkYXAgcGVydWJhaGFuIGJlbnR1ayBvYmplay4NCg0KMy4gTWVuZ2d1bmFrYW4gYXJzaXRla3R1ciBDTk4geWFuZyBsZWJpaCBrb21wbGVrcywgc2VwZXJ0aSBWR0csIFJlc05ldCwgYXRhdSBFZmZpY2llbnROZXQsIHVudHVrIG1lbmluZ2thdGthbiBrZW1hbXB1YW4gZWtzdHJha3NpIGZpdHVyIGRhbiBwZXJmb3JtYSBrbGFzaWZpa2FzaS4NCg0KNC4gTWVuZ2d1bmFrYW4gZGF0YXNldCBkZW5nYW4gcmVzb2x1c2kgeWFuZyBsZWJpaCB0aW5nZ2kgYXRhdSBjaXRyYSBiZXJ3YXJuYSAoUkdCKSBzZWhpbmdnYSBpbmZvcm1hc2kgdmlzdWFsIHlhbmcgdGVyc2VkaWEgbGViaWgga2F5YSBkYW4gZGFwYXQgbWVtYmFudHUgbW9kZWwgbWVtYmVkYWthbiBrYXRlZ29yaSB5YW5nIG1lbWlsaWtpIGtlbWlyaXBhbiB2aXN1YWwgdGluZ2dpLg0KDQo1LiBNZWxha3VrYW4gcGVyYmFuZGluZ2FuIGRlbmdhbiBtZXRvZGUgZGVlcCBsZWFybmluZyBsYWlubnlhIHVudHVrIG1lbmdldGFodWkgbW9kZWwgeWFuZyBwYWxpbmcgZWZla3RpZiBkYWxhbSBtZW55ZWxlc2Fpa2FuIHBlcm1hc2FsYWhhbiBrbGFzaWZpa2FzaSBjaXRyYSBmYXNoaW9uLg0KDQpEZW5nYW4gcGVuZ2VtYmFuZ2FuIHRlcnNlYnV0LCBkaWhhcmFwa2FuIHBlcmZvcm1hIG1vZGVsIGRhcGF0IGRpdGluZ2thdGthbiwgdGVydXRhbWEgZGFsYW0gbWVtYmVkYWthbiBrYXRlZ29yaSBwYWthaWFuIHlhbmcgbWVtaWxpa2kga2FyYWt0ZXJpc3RpayB2aXN1YWwgeWFuZyBzYW5nYXQgbWlyaXAuDQoNCg0KIyBEYWZ0YXIgUHVzdGFrYQ0KDQoNCjEuIEFiYWRpLCBNLiwgQmFyaGFtLCBQLiwgQ2hlbiwgSi4sIENoZW4sIFouLCBEYXZpcywgQS4sIERlYW4sIEouLCBEZXZpbiwgTS4sIEdoZW1hd2F0LCBTLiwgSXJ2aW5nLCBHLiwgSXNhcmQsIE0uLCBLdWRsdXIsIE0uLCBMZXZlbmJlcmcsIEouLCBNb25nYSwgUi4sIE1vb3JlLCBTLiwgTXVycmF5LCBELiBHLiwgU3RlaW5lciwgQi4sIFR1Y2tlciwgUC4sIFZhc3VkZXZhbiwgVi4sIFdhcmRlbiwgUC4sIC4gWmhlbmcsIFguICgyMDE2KS4gVGVuc29yRmxvdzogQSBzeXN0ZW0gZm9yIGxhcmdlLXNjYWxlIG1hY2hpbmUgbGVhcm5pbmcuICpQcm9jZWVkaW5ncyBvZiB0aGUgMTJ0aCBVU0VOSVggU3ltcG9zaXVtIG9uIE9wZXJhdGluZyBTeXN0ZW1zIERlc2lnbiBhbmQgSW1wbGVtZW50YXRpb24gKE9TREkpKiwgMjY1LTI4My4NCg0KMi4gQnJvd25sZWUsIEouICgyMDE5KS4gKkRlZXAgbGVhcm5pbmcgZm9yIGNvbXB1dGVyIHZpc2lvbjogSW1hZ2UgY2xhc3NpZmljYXRpb24sIG9iamVjdCBkZXRlY3Rpb24sIGFuZCBmYWNlIHJlY29nbml0aW9uIGluIFB5dGhvbiouIE1hY2hpbmUgTGVhcm5pbmcgTWFzdGVyeS4NCg0KMy4gQ2hvbGxldCwgRi4gKDIwMjEpLiAqRGVlcCBsZWFybmluZyB3aXRoIFB5dGhvbiogKDJuZCBlZC4pLiBNYW5uaW5nIFB1YmxpY2F0aW9ucy4NCg0KNC4gR29vZGZlbGxvdywgSS4sIEJlbmdpbywgWS4sICYgQ291cnZpbGxlLCBBLiAoMjAxNikuICpEZWVwIGxlYXJuaW5nKi4gTUlUIFByZXNzLg0KDQo1LiBIZSwgSy4sIFpoYW5nLCBYLiwgUmVuLCBTLiwgJiBTdW4sIEouICgyMDE2KS4gRGVlcCByZXNpZHVhbCBsZWFybmluZyBmb3IgaW1hZ2UgcmVjb2duaXRpb24uICpQcm9jZWVkaW5ncyBvZiB0aGUgSUVFRSBDb25mZXJlbmNlIG9uIENvbXB1dGVyIFZpc2lvbiBhbmQgUGF0dGVybiBSZWNvZ25pdGlvbiAoQ1ZQUikqLCA3NzAtNzc4LiBodHRwczovL2RvaS5vcmcvMTAuMTEwOS9DVlBSLjIwMTYuOTANCg0KNi4gSW9mZmUsIFMuLCAmIFN6ZWdlZHksIEMuICgyMDE1KS4gQmF0Y2ggbm9ybWFsaXphdGlvbjogQWNjZWxlcmF0aW5nIGRlZXAgbmV0d29yayB0cmFpbmluZyBieSByZWR1Y2luZyBpbnRlcm5hbCBjb3ZhcmlhdGUgc2hpZnQuICpQcm9jZWVkaW5ncyBvZiB0aGUgMzJuZCBJbnRlcm5hdGlvbmFsIENvbmZlcmVuY2Ugb24gTWFjaGluZSBMZWFybmluZyAoSUNNTCkqLCA0NDgtNDU2Lg0KDQo3LiBLaW5nbWEsIEQuIFAuLCAmIEJhLCBKLiAoMjAxNSkuIEFkYW06IEEgbWV0aG9kIGZvciBzdG9jaGFzdGljIG9wdGltaXphdGlvbi4gKlByb2NlZWRpbmdzIG9mIHRoZSBJbnRlcm5hdGlvbmFsIENvbmZlcmVuY2Ugb24gTGVhcm5pbmcgUmVwcmVzZW50YXRpb25zIChJQ0xSKSouDQoNCjguIEtyaXpoZXZza3ksIEEuLCBTdXRza2V2ZXIsIEkuLCAmIEhpbnRvbiwgRy4gRS4gKDIwMTIpLiBJbWFnZU5ldCBjbGFzc2lmaWNhdGlvbiB3aXRoIGRlZXAgY29udm9sdXRpb25hbCBuZXVyYWwgbmV0d29ya3MuICpBZHZhbmNlcyBpbiBOZXVyYWwgSW5mb3JtYXRpb24gUHJvY2Vzc2luZyBTeXN0ZW1zIChOZXVySVBTKSosIDI1LCAxMDk3LTExMDUuDQoNCjkuIExlQ3VuLCBZLiwgQmVuZ2lvLCBZLiwgJiBIaW50b24sIEcuICgyMDE1KS4gRGVlcCBsZWFybmluZy4gKk5hdHVyZSosICo1MjEqKDc1NTMpLCA0MzYtNDQ0LiBodHRwczovL2RvaS5vcmcvMTAuMTAzOC9uYXR1cmUxNDUzOQ0KDQoxMC4gTGVDdW4sIFkuLCBCb3R0b3UsIEwuLCBCZW5naW8sIFkuLCAmIEhhZmZuZXIsIFAuICgxOTk4KS4gR3JhZGllbnQtYmFzZWQgbGVhcm5pbmcgYXBwbGllZCB0byBkb2N1bWVudCByZWNvZ25pdGlvbi4gKlByb2NlZWRpbmdzIG9mIHRoZSBJRUVFKiwgKjg2KigxMSksIDIyNzgtMjMyNC4gaHR0cHM6Ly9kb2kub3JnLzEwLjExMDkvNS43MjY3OTENCg0KMTEuIFJhd2F0LCBXLiwgJiBXYW5nLCBaLiAoMjAxNykuIERlZXAgY29udm9sdXRpb25hbCBuZXVyYWwgbmV0d29ya3MgZm9yIGltYWdlIGNsYXNzaWZpY2F0aW9uOiBBIGNvbXByZWhlbnNpdmUgcmV2aWV3LiAqTmV1cmFsIENvbXB1dGF0aW9uKiwgKjI5Kig5KSwgMjM1Mi0yNDQ5LiBodHRwczovL2RvaS5vcmcvMTAuMTE2Mi9uZWNvX2FfMDA5OTANCg0KMTIuIFNpbW9ueWFuLCBLLiwgJiBaaXNzZXJtYW4sIEEuICgyMDE1KS4gVmVyeSBkZWVwIGNvbnZvbHV0aW9uYWwgbmV0d29ya3MgZm9yIGxhcmdlLXNjYWxlIGltYWdlIHJlY29nbml0aW9uLiAqUHJvY2VlZGluZ3Mgb2YgdGhlIEludGVybmF0aW9uYWwgQ29uZmVyZW5jZSBvbiBMZWFybmluZyBSZXByZXNlbnRhdGlvbnMgKElDTFIpKi4gaHR0cHM6Ly9hcnhpdi5vcmcvYWJzLzE0MDkuMTU1Ng0KDQoxMy4gU3JpdmFzdGF2YSwgTi4sIEhpbnRvbiwgRy4sIEtyaXpoZXZza3ksIEEuLCBTdXRza2V2ZXIsIEkuLCAmIFNhbGFraHV0ZGlub3YsIFIuICgyMDE0KS4gRHJvcG91dDogQSBzaW1wbGUgd2F5IHRvIHByZXZlbnQgbmV1cmFsIG5ldHdvcmtzIGZyb20gb3ZlcmZpdHRpbmcuICpKb3VybmFsIG9mIE1hY2hpbmUgTGVhcm5pbmcgUmVzZWFyY2gqLCAqMTUqKDU2KSwgMTkyOS0xOTU4Lg0KDQoxNC4gU3plZ2VkeSwgQy4sIExpdSwgVy4sIEppYSwgWS4sIFNlcm1hbmV0LCBQLiwgUmVlZCwgUy4sIEFuZ3VlbG92LCBELiwgRXJoYW4sIEQuLCBWYW5ob3Vja2UsIFYuLCAmIFJhYmlub3ZpY2gsIEEuICgyMDE1KS4gR29pbmcgZGVlcGVyIHdpdGggY29udm9sdXRpb25zLiAqUHJvY2VlZGluZ3Mgb2YgdGhlIElFRUUgQ29uZmVyZW5jZSBvbiBDb21wdXRlciBWaXNpb24gYW5kIFBhdHRlcm4gUmVjb2duaXRpb24gKENWUFIpKiwgMS05LiBodHRwczovL2RvaS5vcmcvMTAuMTEwOS9DVlBSLjIwMTUuNzI5ODU5NA0KDQoxNS4gWGlhbywgSC4sIFJhc3VsLCBLLiwgJiBWb2xsZ3JhZiwgUi4gKDIwMTcpLiAqRmFzaGlvbi1NTklTVDogQSBub3ZlbCBpbWFnZSBkYXRhc2V0IGZvciBiZW5jaG1hcmtpbmcgbWFjaGluZSBsZWFybmluZyBhbGdvcml0aG1zKi4gYXJYaXYuIGh0dHBzOi8vYXJ4aXYub3JnL2Ficy8xNzA4LjA3NzQ3DQoNCg0KDQo=