Identitas Penulis

Keterangan Informasi
Nama Sri Yuliana
NPM 140720250003
Program Studi Magister Statistika Terapan
Mata Kuliah Machine Learning

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:

  1. Bagaimana penerapan metode Convolutional Neural Network (CNN) dalam mengklasifikasikan citra pada dataset Fashion-MNIST?
  2. Seberapa baik kinerja model CNN dalam mengklasifikasikan citra Fashion-MNIST berdasarkan metrik evaluasi yang digunakan?
  3. Kategori pakaian apa yang paling mudah dan paling sulit diklasifikasikan oleh model CNN?
  4. Apakah model CNN yang dibangun mengalami overfitting selama proses pelatihan?

1.3 Tujuan Penelitian

Berdasarkan rumusan masalah tersebut, penelitian ini bertujuan untuk:

  1. Membangun model klasifikasi citra menggunakan metode Convolutional Neural Network (CNN) pada dataset Fashion-MNIST.
  2. Mengevaluasi kinerja model CNN dalam mengklasifikasikan citra Fashion-MNIST menggunakan metrik evaluasi seperti akurasi dan confusion matrix.
  3. Mengidentifikasi kategori pakaian yang paling mudah dan paling sulit dikenali oleh model CNN.
  4. 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:

  1. Menambah pemahaman mengenai penerapan metode Convolutional Neural Network (CNN) dalam permasalahan klasifikasi citra.
  2. Memberikan gambaran mengenai kemampuan CNN dalam mengenali berbagai kategori produk fashion pada dataset Fashion-MNIST.
  3. 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

Label Kategori
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)
Dataset Samples Rows Cols
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)
Dataset Missing_Value
Train 0
Test 0

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
  )
}

Gambar 1. Sampel Citra

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)
Dataset Min Max Mean
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)
Dataset Samples Height Width Channel
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 Label_Asli Class_0 Class_1 Class_2 Class_3 Class_4 Class_5 Class_6 Class_7 Class_8 Class_9
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:

Tabel 7. Arsitektur CNN

Lapisan Konfigurasi
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)
Layer Output_Shape Param
│ 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)
Keterangan Jumlah
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

Hyperparameter Kandidat Nilai
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"))
Filter Dense Dropout Val Accuracy
64 256 0.5 0.9050834

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)
Layer Output_Shape Param
│ 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
) 
Keterangan Jumlah
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)
Epoch Loss Accuracy Val_Loss Val_Accuracy
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"))
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)
Metrik Nilai
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"))
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
Kelas Accuracy
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.

7. Kesimpulan dan Saran

7.1 Kesimpulan

Berdasarkan hasil penelitian yang telah dilakukan, mulai dari tahap preprocessing data, pembangunan model Convolutional Neural Network (CNN), proses pelatihan, hingga evaluasi model, diperoleh beberapa kesimpulan sebagai berikut.

  1. Metode Convolutional Neural Network (CNN) berhasil diterapkan untuk melakukan klasifikasi citra pada dataset Fashion-MNIST. Arsitektur yang digunakan terdiri dari lapisan konvolusi, pooling, flatten, dense layer, dropout, dan output layer dengan fungsi aktivasi softmax yang mampu mengekstraksi fitur visual secara otomatis dan melakukan klasifikasi multi-kelas secara efektif.

  2. Sebelum pelatihan model akhir dilakukan hyperparameter tuning untuk memperoleh konfigurasi CNN yang optimal. Hasil tuning menunjukkan bahwa kombinasi 64 filter, 256 dense units, dan dropout 0,3 menghasilkan validation accuracy tertinggi sekitar 90 %. Model akhir yang dibangun menggunakan konfigurasi tersebut memperoleh accuracy pengujian sekitar 90%.

  3. Hasil evaluasi per kelas menunjukkan bahwa kategori Sandal dan Boot memiliki performa terbaik, diikuti oleh kategori Trouser, Bag , dan Sneaker sekitar 0,90. Sebaliknya, kategori Shirt memiliki performa terendah dengan nilai sekitar 0,70 karena sering mengalami kesalahan klasifikasi dengan kategori T-shirt/Top, Coat, dan Pullover yang memiliki karakteristik visual serupa.

  4. Analisis confusion matrix dan misclassification menunjukkan bahwa sebagian besar kesalahan klasifikasi terjadi pada kategori pakaian bagian atas seperti Shirt, Pullover, Coat, Dress, dan T-shirt/Top. Kesalahan tersebut disebabkan oleh tingginya kemiripan bentuk visual antar kategori serta keterbatasan resolusi citra Fashion-MNIST yang hanya berukuran 28 × 28 piksel. Beberapa pola kesalahan yang sering ditemukan antara lain Pullover yang diprediksi sebagai Shirt, Coat yang diprediksi sebagai Shirt atau Pullover, Dress yang diprediksi sebagai Shirt atau Coat, serta Shirt yang diprediksi sebagai T-shirt/Top.

  5. Model CNN yang dibangun memiliki kemampuan generalisasi yang baik. Hal ini ditunjukkan oleh kestabilan performa pada data pelatihan dan data validasi, nilai loss yang relatif rendah, serta tidak ditemukannya indikasi overfitting yang signifikan selama proses pelatihan.

Secara keseluruhan, penelitian ini membuktikan bahwa metode CNN mampu melakukan klasifikasi citra Fashion-MNIST dengan baik dan efektif untuk mengenali berbagai kategori pakaian berdasarkan karakteristik visual yang dimiliki. Meskipun masih terdapat kesalahan pada kategori yang memiliki kemiripan visual tinggi, model tetap menunjukkan performa yang sangat baik dan berpotensi untuk dikembangkan lebih lanjut melalui optimasi arsitektur, data augmentation, maupun penyesuaian hyperparameter.

7.2 Saran

Berdasarkan hasil penelitian yang diperoleh, beberapa saran yang dapat diberikan untuk penelitian selanjutnya adalah sebagai berikut.

  1. Melakukan hyperparameter tuning secara lebih mendalam, seperti optimasi learning rate, batch size, jumlah epoch, dan jumlah filter konvolusi untuk memperoleh konfigurasi model yang lebih optimal.

  2. Menerapkan teknik data augmentation, seperti rotasi, zooming, shifting, dan flipping, untuk meningkatkan variasi data pelatihan sehingga model menjadi lebih robust terhadap perubahan bentuk objek.

  3. Menggunakan arsitektur CNN yang lebih kompleks, seperti VGG, ResNet, atau EfficientNet, untuk meningkatkan kemampuan ekstraksi fitur dan performa klasifikasi.

  4. Menggunakan dataset dengan resolusi yang lebih tinggi atau citra berwarna (RGB) sehingga informasi visual yang tersedia lebih kaya dan dapat membantu model membedakan kategori yang memiliki kemiripan visual tinggi.

  5. Melakukan perbandingan dengan metode deep learning lainnya untuk mengetahui model yang paling efektif dalam menyelesaikan permasalahan klasifikasi citra fashion.

Dengan pengembangan tersebut, diharapkan performa model dapat ditingkatkan, terutama dalam membedakan kategori pakaian yang memiliki karakteristik visual yang sangat mirip.

Daftar Pustaka

  1. Abadi, M., Barham, P., Chen, J., Chen, Z., Davis, A., Dean, J., Devin, M., Ghemawat, S., Irving, G., Isard, M., Kudlur, M., Levenberg, J., Monga, R., Moore, S., Murray, D. G., Steiner, B., Tucker, P., Vasudevan, V., Warden, P., . Zheng, X. (2016). TensorFlow: A system for large-scale machine learning. Proceedings of the 12th USENIX Symposium on Operating Systems Design and Implementation (OSDI), 265-283.

  2. Brownlee, J. (2019). Deep learning for computer vision: Image classification, object detection, and face recognition in Python. Machine Learning Mastery.

  3. Chollet, F. (2021). Deep learning with Python (2nd ed.). Manning Publications.

  4. Goodfellow, I., Bengio, Y., & Courville, A. (2016). Deep learning. MIT Press.

  5. He, K., Zhang, X., Ren, S., & Sun, J. (2016). Deep residual learning for image recognition. Proceedings of the IEEE Conference on Computer Vision and Pattern Recognition (CVPR), 770-778. https://doi.org/10.1109/CVPR.2016.90

  6. Ioffe, S., & Szegedy, C. (2015). Batch normalization: Accelerating deep network training by reducing internal covariate shift. Proceedings of the 32nd International Conference on Machine Learning (ICML), 448-456.

  7. Kingma, D. P., & Ba, J. (2015). Adam: A method for stochastic optimization. Proceedings of the International Conference on Learning Representations (ICLR).

  8. Krizhevsky, A., Sutskever, I., & Hinton, G. E. (2012). ImageNet classification with deep convolutional neural networks. Advances in Neural Information Processing Systems (NeurIPS), 25, 1097-1105.

  9. LeCun, Y., Bengio, Y., & Hinton, G. (2015). Deep learning. Nature, 521(7553), 436-444. https://doi.org/10.1038/nature14539

  10. LeCun, Y., Bottou, L., Bengio, Y., & Haffner, P. (1998). Gradient-based learning applied to document recognition. Proceedings of the IEEE, 86(11), 2278-2324. https://doi.org/10.1109/5.726791

  11. Rawat, W., & Wang, Z. (2017). Deep convolutional neural networks for image classification: A comprehensive review. Neural Computation, 29(9), 2352-2449. https://doi.org/10.1162/neco_a_00990

  12. Simonyan, K., & Zisserman, A. (2015). Very deep convolutional networks for large-scale image recognition. Proceedings of the International Conference on Learning Representations (ICLR). https://arxiv.org/abs/1409.1556

  13. Srivastava, N., Hinton, G., Krizhevsky, A., Sutskever, I., & Salakhutdinov, R. (2014). Dropout: A simple way to prevent neural networks from overfitting. Journal of Machine Learning Research, 15(56), 1929-1958.

  14. Szegedy, C., Liu, W., Jia, Y., Sermanet, P., Reed, S., Anguelov, D., Erhan, D., Vanhoucke, V., & Rabinovich, A. (2015). Going deeper with convolutions. Proceedings of the IEEE Conference on Computer Vision and Pattern Recognition (CVPR), 1-9. https://doi.org/10.1109/CVPR.2015.7298594

  15. Xiao, H., Rasul, K., & Vollgraf, R. (2017). Fashion-MNIST: A novel image dataset for benchmarking machine learning algorithms. arXiv. https://arxiv.org/abs/1708.07747

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=