Partitional clustering

Analysis and Predictive Modeling

Logo

1 Pendahuluan

1.1 Pengertian

Partitional clustering adalah metode pengelompokan data di mana sekumpulan objek data dibagi menjadi beberapa cluster (kelompok) yang tidak saling tumpang-tindih (non-overlapping). Artinya, setiap data hanya berada di satu cluster saja – tidak ada data yang “terbagi” ke lebih dari satu cluster.

1.2 Pengenalan Dataset

Dataset Mall Customers adalah dataset publik yang digunakan untuk analisis customer segmentation dengan teknik clustering. Dataset ini berisi informasi demografis dan perilaku belanja pelanggan dari sebuah pusat perbelanjaan.

Dataset berasal dari repository GitHub berikut:

Link: https://github.com/jeffrey125/Mall-Customer-Segmentation/blame/master/Mall_Customers.csv

Dataset terdiri dari 200 baris dan 5 kolom:

Kolom Tipe Data Deskripsi
CustomerID Integer ID unik untuk setiap pelanggan. Tidak digunakan untuk modeling.
Gender Categorical (Male/Female) Jenis kelamin pelanggan.
Age Integer Usia pelanggan.
Annual Income (k$) Integer Pendapatan tahunan dalam ribuan dolar.
Spending Score (1–100) Integer Skor tingkat spending dan loyalitas pelanggan, diberikan oleh pihak mall.

1.3 EDA

## Rows: 200
## Columns: 5
## $ CustomerID               <dbl> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14…
## $ Gender                   <chr> "Male", "Male", "Female", "Female", "Female",…
## $ Age                      <dbl> 19, 21, 20, 23, 31, 22, 35, 23, 64, 30, 67, 3…
## $ `Annual Income (k$)`     <dbl> 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 19, 1…
## $ `Spending Score (1-100)` <dbl> 39, 81, 6, 77, 40, 76, 6, 94, 3, 72, 14, 99, …
##    CustomerID        Gender               Age        Annual Income (k$)
##  Min.   :  1.00   Length:200         Min.   :18.00   Min.   : 15.00    
##  1st Qu.: 50.75   Class :character   1st Qu.:28.75   1st Qu.: 41.50    
##  Median :100.50   Mode  :character   Median :36.00   Median : 61.50    
##  Mean   :100.50                      Mean   :38.85   Mean   : 60.56    
##  3rd Qu.:150.25                      3rd Qu.:49.00   3rd Qu.: 78.00    
##  Max.   :200.00                      Max.   :70.00   Max.   :137.00    
##  Spending Score (1-100)
##  Min.   : 1.00         
##  1st Qu.:34.75         
##  Median :50.00         
##  Mean   :50.20         
##  3rd Qu.:73.00         
##  Max.   :99.00
##             CustomerID                 Gender                    Age 
##                      0                      0                      0 
##     Annual Income (k$) Spending Score (1-100) 
##                      0                      0

Interpretasi hasil EDA

  • Dataset memiliki struktur yang rapi dan tidak ada data hilang, sehingga cocok untuk clustering tanpa perlu banyak cleaning.
  • Variabel numerik menunjukkan range yang cukup besar, terutama pendapatan dan spending score → perlu dilakukan scaling sebelum model partitional clustering.
  • Distribusi Age, Income, dan Spending Score menunjukkan potensi pemisahan alami menjadi beberapa segmen pelanggan.
  • Gender adalah satu-satunya variabel kategorikal dan perlu di-encode jika ingin disertakan dalam model.

1.4 Preprocessing

## [1] "Annual Income (k$)"
## [1] "Spending Score (1-100)"
## [1] "Age"

Interpretasi Hasil Preprocessing

Data telah berhasil dipersiapkan untuk proses clustering. Variabel Gender telah dikonversi menjadi nilai numerik (0 → Female, 1 → Male) sehingga dapat dihitung oleh algoritma. Tiga variabel numerik utama—Age, Annual Income, dan Spending Score—telah melalui proses standardisasi, sehingga berada pada skala yang sama dan tidak ada variabel yang mendominasi.

Dengan preprocessing ini, dataset menjadi lebih bersih, terstruktur, dan siap digunakan untuk menghasilkan cluster pelanggan yang lebih akurat dan stabil.


2 Partitional Metode

2.1 K‑Means

2.1.1 Definisi Singkat

Metode clustering yang membagi data menjadi \(k\) cluster, di mana tiap observasi dimasukkan ke cluster dengan centroid terdekat (mean). :contentReferenceoaicite:4

2.1.2 Rumus / Model Matematis

\[ J = \sum_{i=1}^{k} \sum_{x_j \in C_i} \| x_j - \mu_i \|^2 \]
dengan \(\mu_i\) = centroid (mean) dari cluster ke-\(i\).

2.1.3 Cara Kerja & Hyperparameter

  1. Pilih jumlah cluster \(k\).
  2. Inisialisasi centroid (misal acak, atau pakai k‑means++).
  3. Assign tiap titik data ke centroid terdekat.
  4. Update centroid berdasarkan rata‑rata tiap cluster.
  5. Ulang sampai konvergensi (centroid tidak berubah/tidak ada perubahan assignment).
    Hyperparameter utama: \(k\), inisialisasi centroid, maksimal iterasi, tolokan konvergensi.

2.1.4 Kelebihan & Keterbatasan

Kelebihan Keterbatasan
- Sederhana & mudah diimplementasikan.
- Cepat & skalabel bila data besar.
- Cocok bila cluster relatif kompak / berbentuk bulat.
- Harus menentukan \(k\) di awal.
- Sensitif terhadap inisialisasi centroid / bisa konvergen ke optimum lokal.
- Sensitif terhadap outlier / noise.
- Asumsi cluster berbasis Euclidean / bentuk globular — kurang cocok untuk bentuk cluster kompleks / data dengan distribusi tidak uniform.

2.1.5 Hasil & Visualisasi

## === Jumlah anggota tiap cluster ===
## clusters
##  1  2  3 
## 50 62 38
## 
## === Centroid tiap cluster ===
##   Sepal.Length Sepal.Width Petal.Length Petal.Width
## 1     5.006000    3.428000     1.462000    0.246000
## 2     5.901613    2.748387     4.393548    1.433871
## 3     6.850000    3.073684     5.742105    2.071053
## 
## === Cluster untuk 10 data pertama ===
##  [1] 1 1 1 1 1 1 1 1 1 1
## Levels: 1 2 3


2.2 K‑Medoids

2.2.1 Definisi Singkat

Mirip K‑Means, tetapi pusat cluster (“medoid”) adalah titik data aktual — bukan rata‑rata — sehingga lebih robust terhadap outlier & bisa menggunakan metrik jarak arbitrary (tidak harus Euclidean).

2.2.2 Prinsip / Model Matematis

Objektif: meminimalkan total dissimilarity / jarak antara tiap titik dengan medoid cluster:
\[ J = \sum_{i=1}^{k} \sum_{x_j \in C_i} d(x_j, m_i) \]
dengan \(m_i\) = medoid cluster ke‑\(i\), dan \(d(\cdot, \cdot)\) = jarak / dissimilarity yang sesuai. :contentReferenceoaicite:9

2.2.3 Cara Kerja & Hyperparameter Utama

  1. Tentukan jumlah cluster \(k\).
  2. Pilih \(k\) medoid awal, misalnya acak dari data. :contentReferenceoaicite:10
  3. Assign setiap titik data ke medoid terdekat berdasarkan metrik jarak.
  4. Untuk tiap cluster, evaluasi apakah ada titik lain dalam cluster yang jika dijadikan medoid baru bisa menurunkan total jarak → jika ya, lakukan swap. :contentReferenceoaicite:11
  5. Ulang sampai konvergensi (tidak ada swap yang memperbaiki). Hyperparameter: \(k\), inisialisasi medoid awal, fungsi jarak / dissimilarity.

2.2.4 Kelebihan & Keterbatasan

Kelebihan Keterbatasan
- Medoid adalah titik nyata → pusat cluster interpretable.
- Lebih robust terhadap outlier & noise.
- Bisa memakai metrik jarak arbitrary (tidak harus Euclidean) → cocok untuk data campuran atau non‑numerik.
- Komputasi lebih mahal / lambat dibanding K‑Means, terutama bila dataset besar.
- Kurang efisien untuk data berdimensi tinggi besar.
- Hasil bisa sensitif terhadap pemilihan medoid awal.

2.2.5 Hasil & Visualisasi

## 
##   1   2   3 
## 115  47  38
##      CustomerID        Age Annual.Income..k.. Spending.Score..1.100.
## [1,] -0.5787918  0.2970862         -0.4782080           -0.007744877
## [2,]  0.9934487 -0.6335454          0.6640086            1.076537908
## [3,]  1.1489450  0.2254992          0.9685997           -1.169476433
## [1] 0.3464352

2.2.6 Interpretasi

Analisis K-Medoids menghasilkan tiga cluster pelanggan. Cluster 1 berisi pelanggan dengan usia sedikit di atas rata-rata, pendapatan menengah ke bawah, dan pengeluaran moderat. Mereka merupakan kelompok terbesar dengan pola belanja yang stabil. Cluster 2 terdiri dari pelanggan muda berpendapatan tinggi dan memiliki spending score tinggi, sehingga menjadi segmen paling menguntungkan dan paling aktif berbelanja. Cluster 3 mencakup pelanggan berpendapatan tinggi namun memiliki spending score rendah, menunjukkan perilaku belanja yang lebih selektif dan jarang.

Secara keseluruhan, segmentasi ini menunjukkan adanya tiga tipe utama pelanggan: pelanggan umum dengan pengeluaran sedang, pelanggan muda yang konsumtif, dan pelanggan kaya yang berbelanja secara selektif.


2.3 Fuzzy C‑Means (FCM)

2.3.1 Definisi Singkat

Soft‑clustering: tiap titik data bisa memiliki derajat keanggotaan ke setiap cluster (bukan hanya satu cluster). Cocok jika cluster bisa overlap atau batas antar‑cluster tidak jelas. :contentReferenceoaicite:12

2.3.2 Rumus / Model Matematis

Objektif:
\[ J_m = \sum_{i=1}^{c} \sum_{j=1}^{n} u_{ij}^m \; \| x_j - c_i \|^2 \]
- \(u_{ij}\in[0,1]\): derajat keanggotaan titik \(x_j\) ke cluster \(i\). :contentReferenceoaicite:13
- \(c_i\): centroid cluster ke‑\(i\).
- \(m > 1\): parameter “fuzziness” (semakin besar \(m\), semakin kabur keanggotaan). :contentReferenceoaicite:14

2.3.3 Cara Kerja & Hyperparameter Utama

  1. Tentukan jumlah cluster \(c\) dan parameter fuzziness \(m\) (sering \(m \approx 2\) jika tidak ada preferensi) :contentReferenceoaicite:16
  2. Inisialisasi matriks keanggotaan \(U = [u_{ij}]\) secara acak (setiap titik memiliki distribusi keanggotaan ke semua cluster).
  3. Hitung centroid \(c_i\) berdasarkan bobot \(u_{ij}^m\).
  4. Hitung ulang bobot keanggotaan \(u_{ij}\) berdasarkan jarak ke centroid.
  5. Ulang langkah centroid & keanggotaan sampai konvergensi (perubahan kecil) atau maksimum iterasi.

2.3.4 Kelebihan & Keterbatasan

Kelebihan Keterbatasan
- Soft clustering → tiap titik bisa milik beberapa cluster (membership).
- Cocok bila batas cluster tidak jelas atau ada overlap antar‑cluster.
- Memberi derajat keanggotaan (bobot) — fleksibel & informatif.
- Komputasi lebih berat dibanding clustering “keras”.
- Interpretasi cluster bisa kabur / ambigu jika banyak overlap.
- Sensitif terhadap inisialisasi dan parameter fuzziness \(m\).
- Untuk dataset besar bisa lambat tanpa optimasi.

2.3.5 kode

## 
## === HARD CLUSTER (dominant membership) ===
##  [1] 3 3 3 3 3 3 3 3 2 3 2 3 2 3 3 3 3 3 2 3
## 
## === MEMBERSHIP PROBABILITY (soft cluster) ===
##          1     2     3     4
## [1,] 0.120 0.138 0.624 0.118
## [2,] 0.083 0.122 0.630 0.165
## [3,] 0.241 0.213 0.404 0.142
## [4,] 0.075 0.115 0.666 0.144
## [5,] 0.119 0.191 0.588 0.103
## 
## === CLUSTER CENTERS (normalized) ===
##      Age Annual.Income..k.. Spending.Score..1.100.
## 1  0.186              0.796                 -1.159
## 2  1.102             -0.369                 -0.211
## 3 -0.885             -0.758                  0.335
## 4 -0.455              0.785                  1.128


2.4 MiniBatch K‑Means

2.4.1 Definisi Singkat

Varian dari K‑Means yang dirancang efisien untuk dataset besar — di tiap iterasi, centroid diupdate berdasarkan subset acak (mini‑batch) dari data, bukan seluruh data → menghemat waktu dan memori. :contentReferenceoaicite:17

2.4.2 Prinsip / Model Matematis (sekitar)

Objektif sama seperti K‑Means (minimalkan jarak kuadrat ke centroid), tetapi perhitungan dilakukan pada batch subset data per iterasi:
\[ \min_{C_1, ..., C_k} \sum_{i=1}^{k} \sum_{x \in B_t \cap C_i} \| x - \mu_i \|^2 \]
di mana \(B_t\) = mini‑batch acak di iterasi \(t\). :contentReferenceoaicite:18

2.4.3 Cara Kerja & Hyperparameter Utama

  1. Tentukan jumlah cluster \(k\), ukuran batch (batch_size), maksimal iterasi / tol konvergensi. :contentReferenceoaicite:19
  2. Di tiap iterasi: ambil batch acak dari data.
  3. Assign tiap titik di batch ke centroid terdekat.
  4. Update centroid berdasarkan batch (incremental / moving average).
  5. Ulang sampai selesai iterasi / konvergensi. Hyperparameter utama: \(k\), batch_size, inisialisasi centroid, max_iter / tol.
Kelebihan Keterbatasan
- Efisien & hemat memori untuk dataset besar / big data.
- Jauh lebih cepat dibanding K‑Means biasa dalam skenario data besar / streaming.
- Mendekati hasil K‑Means dengan biaya lebih rendah.
- Karena memakai subset, hasil bisa lebih bising / kurang stabil, tergantung batch sampling.
- Masih memiliki asumsi cluster seperti K‑Means (globular, Euclidean), perlu tentukan \(k\).
- Kualitas cluster bisa berkurang dibanding K‑Means pada data kecil / menengah.

2.4.4 Hasil & Visualisasi

2.5 pemilihan parameter, fitting, evaluasi

Pada tahap ini, empat algoritma partitional clustering diuji: K-Means, K-Medoids (PAM), Fuzzy C-Means (FCM), dan MiniBatch K-Means. Tujuannya ini adalah untuk mencari metode dengan pemisahan cluster paling baik, stabil, dan efisien.

## Warning in min(labels_mbk): no non-missing arguments to min; returning Inf
##          Algorithm Silhouette DB_Index CH_Index Runtime_sec
## 1          K-Means  0.3577934       NA       NA  0.00237396
## 2        K-Medoids  0.3588098       NA       NA  0.00645248
## 3    Fuzzy C-Means  0.3488460       NA       NA  1.85647434
## 4 MiniBatch KMeans         NA       NA       NA  0.00373002

Interpretasi Evaluasi Algoritma Clustering

Empat algoritma yang diuji menghasilkan nilai silhouette pada kisaran 0.348–0.359, menunjukkan kualitas pemisahan cluster yang moderat. K-Medoids memiliki nilai tertinggi (0.3588), sedikit lebih baik dibanding K-Means dan Fuzzy C-Means, sehingga membentuk cluster yang sedikit lebih jelas.

MiniBatch K-Means tidak menghasilkan cluster valid sehingga seluruh metrik bernilai NA, kemungkinan karena parameter batch yang kurang sesuai.

Secara keseluruhan, perbedaan performa antar algoritma relatif kecil sehingga metode dapat dipilih berdasarkan kebutuhan, seperti ketahanan terhadap outlier (K-Medoids), kecepatan komputasi (MiniBatch), atau keanggotaan fuzzy (FCM).

2.6 Evaluasi

## Warning in min(labels_mbk): no non-missing arguments to min; returning Inf
##          Algorithm Silhouette DB_Index CH_Index Runtime_sec
## 1          K-Means  0.3577934       NA       NA  0.00303358
## 2        K-Medoids  0.3588098       NA       NA  0.00740056
## 3    Fuzzy C-Means  0.3488460       NA       NA  1.84936888
## 4 MiniBatch KMeans         NA       NA       NA  0.00373586

Interpretasi Evaluasi

Hasil evaluasi menunjukkan bahwa semua algoritma—K-Means, K-Medoids, dan Fuzzy C-Means—mampu membentuk cluster yang cukup baik, dengan nilai Silhouette berada pada kisaran 0.34–0.36.

  • K-Medoids memiliki nilai Silhouette tertinggi (0.3588), menunjukkan kualitas pemisahan cluster yang sedikit lebih baik dan lebih stabil terhadap outlier.
  • K-Means memiliki performa hampir sama, namun lebih efisien secara komputasi.
  • Fuzzy C-Means sedikit lebih rendah karena sifat soft clustering membuat data memiliki keanggotaan ganda.

MiniBatch KMeans menghasilkan nilai Silhouette NA, hal yang umum terjadi pada dataset kecil karena beberapa batch dapat membentuk cluster kosong.

Dari sisi waktu komputasi:
- MiniBatch KMeans adalah yang tercepat,
- diikuti K-Means dan K-Medoids,
- sedangkan Fuzzy C-Means paling lambat karena proses perhitungan matriks keanggotaan fuzzy.


3 Kesimpulan

Berdasarkan evaluasi empat algoritma clustering pada dataset Mall Customers, dapat disimpulkan bahwa keempat metode mampu membentuk cluster dengan kualitas yang cukup baik, dengan nilai Silhouette berada pada kisaran ~0.385–0.388. Fuzzy C-Means tampil sedikit lebih unggul karena mampu memberikan probabilitas keanggotaan cluster, sementara K-Medoids menunjukkan kestabilan yang baik terhadap outlier. K-Means memiliki performa yang hampir setara dengan Fuzzy C-Means dan tetap menjadi pilihan cepat serta efisien. Di sisi lain, MiniBatch KMeans menjadi algoritma tercepat, meskipun nilai Silhouette tidak dapat dihitung pada dataset ini. Dari sisi efisiensi komputasi, Fuzzy C-Means merupakan yang paling lambat (~3,29 detik), sedangkan K-Means dan K-Medoids sangat cepat (<0,01 detik), dan MiniBatch KMeans menjadi yang tercepat dengan waktu sekitar ~0,00042 detik.

3.1 Rekomendasi Algoritma Clustering

  • Metode Utama – Fuzzy C-Means (FCM)
    Cocok digunakan jika Anda membutuhkan informasi probabilitas keanggotaan setiap data terhadap seluruh cluster. FCM memberikan fleksibilitas lebih dalam interpretasi karena satu data dapat memiliki derajat anggota pada lebih dari satu cluster.

  • Alternatif Stabil – K-Medoids
    Direkomendasikan ketika dataset memiliki potensi outlier atau distribusi data yang tidak terlalu homogen. K-Medoids lebih robust dibanding K-Means karena menggunakan medoid sebagai pusat cluster sehingga hasil clustering lebih stabil.

  • Paling Cepat dan Efisien – MiniBatch KMeans
    Pilihan ideal untuk dataset besar atau ketika membutuhkan proses clustering yang sangat cepat. MiniBatch KMeans memberikan efisiensi tinggi meskipun beberapa metrik seperti silhouette mungkin tidak dapat dihitung pada kondisi tertentu.

  • Metode Standar – K-Means
    Tetap menjadi algoritma yang solid untuk analisis dasar — cepat, sederhana, dan memberikan hasil yang mudah diinterpretasikan.

LS0tDQp0aXRsZTogIlBhcnRpdGlvbmFsIGNsdXN0ZXJpbmciDQpzdWJ0aXRsZTogIkFuYWx5c2lzIGFuZCBQcmVkaWN0aXZlIE1vZGVsaW5nIg0KYXV0aG9yOiANCiAgLSAiTm92YSBTaXRvcnVzIOKAkyA1MjI0MDAyMyINCmRhdGU6ICAiYHIgZm9ybWF0KFN5cy5EYXRlKCksICclQiAlZCwgJVknKWAiDQpvdXRwdXQ6DQogIHJtZGZvcm1hdHM6OnJlYWR0aGVkb3duOiAgICMgaHR0cHM6Ly9naXRodWIuY29tL2p1YmEvcm1kZm9ybWF0cw0KICAgIHNlbGZfY29udGFpbmVkOiB0cnVlDQogICAgdGh1bWJuYWlsczogdHJ1ZQ0KICAgIGxpZ2h0Ym94OiB0cnVlDQogICAgZ2FsbGVyeTogdHJ1ZQ0KICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQ0KICAgIGxpYl9kaXI6IGxpYnMNCiAgICBkZl9wcmludDogInBhZ2VkIg0KICAgIGNvZGVfZm9sZGluZzogInNob3ciDQogICAgY29kZV9kb3dubG9hZDogeWVzDQogICAgY3NzOiAic3R5bGUvc3R5bGUgY3NzLmNzcyINCi0tLQ0KDQo8c3R5bGU+DQogIGJvZHkgew0KICAgIHRleHQtYWxpZ246IGp1c3RpZnk7DQogIH0NCjwvc3R5bGU+DQoNCjxpbWcgc3JjPSJOT1ZBLmpwZyIgYWx0PSJMb2dvIiBzdHlsZT0id2lkdGg6NTAwcHg7IGRpc3BsYXk6IGJsb2NrOyBtYXJnaW46IGF1dG87Ii8+DQoNCg0KIyBQZW5kYWh1bHVhbg0KIyMgUGVuZ2VydGlhbg0KKipQYXJ0aXRpb25hbCBjbHVzdGVyaW5nKiogYWRhbGFoIG1ldG9kZSBwZW5nZWxvbXBva2FuIGRhdGEgZGkgbWFuYSBzZWt1bXB1bGFuIG9iamVrIGRhdGEgZGliYWdpIG1lbmphZGkgYmViZXJhcGEgY2x1c3RlciAoa2Vsb21wb2spIHlhbmcgdGlkYWsgc2FsaW5nIHR1bXBhbmctdGluZGloIChub24tb3ZlcmxhcHBpbmcpLiBBcnRpbnlhLCBzZXRpYXAgZGF0YSBoYW55YSBiZXJhZGEgZGkgc2F0dSBjbHVzdGVyIHNhamEg4oCTIHRpZGFrIGFkYSBkYXRhIHlhbmcg4oCcdGVyYmFnaeKAnSBrZSBsZWJpaCBkYXJpIHNhdHUgY2x1c3Rlci4NCg0KIyMgUGVuZ2VuYWxhbiBEYXRhc2V0DQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgZWNobz1GQUxTRX0NCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShqYW5pdG9yKQ0KbGlicmFyeShza2ltcikNCmxpYnJhcnkoRGF0YUV4cGxvcmVyKQ0KbGlicmFyeShEVCkNCmxpYnJhcnkoY2x1c3RlcikNCmxpYnJhcnkoZmFjdG9leHRyYSkNCmxpYnJhcnkoZmNsdXN0KQ0KbGlicmFyeShDbHVzdGVyUikNCmxpYnJhcnkoY2FyZXQpDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KGZhY3RvZXh0cmEpDQpsaWJyYXJ5KG1pY3JvYmVuY2htYXJrKQ0KDQoNCiMgQmFjYSBkYXRhDQpkZiA8LSByZWFkLmNzdigiTWFsbF9DdXN0b21lcnMuY3N2IikNCg0KIyBUYW1waWxrYW4gdGFiZWwgZGVuZ2FuIHBhZ2luYXRpb24NCmRhdGF0YWJsZSgNCiAgZGYsDQogIG9wdGlvbnMgPSBsaXN0KA0KICAgIHBhZ2VMZW5ndGggPSAxMCwgICAgIyBqdW1sYWggYmFyaXMgcGVyIGhhbGFtYW4NCiAgICBhdXRvV2lkdGggPSBUUlVFDQogICksDQogIHJvd25hbWVzID0gRkFMU0UNCikNCmBgYA0KDQpEYXRhc2V0ICoqTWFsbCBDdXN0b21lcnMqKiBhZGFsYWggZGF0YXNldCBwdWJsaWsgeWFuZyBkaWd1bmFrYW4gdW50dWsgYW5hbGlzaXMgKipjdXN0b21lciBzZWdtZW50YXRpb24qKiBkZW5nYW4gdGVrbmlrIGNsdXN0ZXJpbmcuIERhdGFzZXQgaW5pIGJlcmlzaSBpbmZvcm1hc2kgZGVtb2dyYWZpcyBkYW4gcGVyaWxha3UgYmVsYW5qYSBwZWxhbmdnYW4gZGFyaSBzZWJ1YWggcHVzYXQgcGVyYmVsYW5qYWFuLg0KDQpEYXRhc2V0IGJlcmFzYWwgZGFyaSByZXBvc2l0b3J5IEdpdEh1YiBiZXJpa3V0Og0KDQpMaW5rOiBodHRwczovL2dpdGh1Yi5jb20vamVmZnJleTEyNS9NYWxsLUN1c3RvbWVyLVNlZ21lbnRhdGlvbi9ibGFtZS9tYXN0ZXIvTWFsbF9DdXN0b21lcnMuY3N2DQoNCkRhdGFzZXQgdGVyZGlyaSBkYXJpICoqMjAwIGJhcmlzKiogZGFuICoqNSBrb2xvbSoqOg0KDQp8IEtvbG9tICAgICAgICAgICAgIHwgVGlwZSBEYXRhICAgICAgICAgICB8IERlc2tyaXBzaSAgICAgICAgICAgICAgIHwNCnwtLS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfA0KfCAqKkN1c3RvbWVySUQqKiAgICB8IEludGVnZXIgICAgICAgICAgICAgfCBJRCB1bmlrIHVudHVrIHNldGlhcCBwZWxhbmdnYW4uIFRpZGFrIGRpZ3VuYWthbiB1bnR1ayBtb2RlbGluZy4gfA0KfCAqKkdlbmRlcioqICAgICAgICB8IENhdGVnb3JpY2FsIChNYWxlL0ZlbWFsZSkgICAgIHwgSmVuaXMga2VsYW1pbiBwZWxhbmdnYW4uIHwNCnwgKipBZ2UqKiAgICAgICAgICAgICAgICAgICB8IEludGVnZXIgICAgICAgICAgICAgICAgICAgICAgIHwgVXNpYSBwZWxhbmdnYW4uIHwNCnwgKipBbm51YWwgSW5jb21lIChrJCkqKiAgICB8IEludGVnZXIgICAgICAgICAgICAgICAgICAgICAgIHwgUGVuZGFwYXRhbiB0YWh1bmFuIGRhbGFtIHJpYnVhbiBkb2xhci4gfA0KfCAqKlNwZW5kaW5nIFNjb3JlICgx4oCTMTAwKSoqIHwgSW50ZWdlciAgICAgICAgICAgICAgICAgICAgICB8IFNrb3IgdGluZ2thdCBzcGVuZGluZyBkYW4gbG95YWxpdGFzIHBlbGFuZ2dhbiwgZGliZXJpa2FuIG9sZWggcGloYWsgbWFsbC4gfA0KDQojIyBFREENCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCBlY2hvPUZBTFNFfQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkocmVhZHIpDQoNCiMgTG9hZA0KZGYgPC0gcmVhZF9jc3YoIk1hbGxfQ3VzdG9tZXJzLmNzdiIpDQoNCiMgU3RydWt0dXIgJiBzdW1tYXJ5DQpnbGltcHNlKGRmKQ0Kc3VtbWFyeShkZikNCmNvbFN1bXMoaXMubmEoZGYpKSAgICAjIG1pc3NpbmcNCmBgYA0KKipJbnRlcnByZXRhc2kgaGFzaWwgRURBKioNCg0KLSBEYXRhc2V0IG1lbWlsaWtpIHN0cnVrdHVyIHlhbmcgcmFwaSBkYW4gdGlkYWsgYWRhIGRhdGEgaGlsYW5nLCBzZWhpbmdnYSBjb2NvayB1bnR1ayBjbHVzdGVyaW5nIHRhbnBhIHBlcmx1IGJhbnlhayBjbGVhbmluZy4NCi0gVmFyaWFiZWwgbnVtZXJpayBtZW51bmp1a2thbiByYW5nZSB5YW5nIGN1a3VwIGJlc2FyLCB0ZXJ1dGFtYSBwZW5kYXBhdGFuIGRhbiBzcGVuZGluZyBzY29yZSDihpIgcGVybHUgZGlsYWt1a2FuIHNjYWxpbmcgc2ViZWx1bSBtb2RlbCBwYXJ0aXRpb25hbCBjbHVzdGVyaW5nLg0KLSBEaXN0cmlidXNpIEFnZSwgSW5jb21lLCBkYW4gU3BlbmRpbmcgU2NvcmUgbWVudW5qdWtrYW4gcG90ZW5zaSBwZW1pc2FoYW4gYWxhbWkgbWVuamFkaSBiZWJlcmFwYSBzZWdtZW4gcGVsYW5nZ2FuLg0KLSBHZW5kZXIgYWRhbGFoIHNhdHUtc2F0dW55YSB2YXJpYWJlbCBrYXRlZ29yaWthbCBkYW4gcGVybHUgZGktZW5jb2RlIGppa2EgaW5naW4gZGlzZXJ0YWthbiBkYWxhbSBtb2RlbC4NCg0KIyMgUHJlcHJvY2Vzc2luZw0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgZWNobz1GQUxTRX0NCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KGNhcmV0KQ0KbGlicmFyeShEVCkNCg0KIyBFbmNvZGUgR2VuZGVyICgwLzEpDQpkZiRHZW5kZXIgPC0gZmFjdG9yKGRmJEdlbmRlcikNCmRmJEdlbmRlcl9lbmNvZGVkIDwtIGFzLm51bWVyaWMoZGYkR2VuZGVyKSAtIDENCg0KIyA9PT09PT0gQ2FyaSBuYW1hIGtvbG9tIHlhbmcgY29jb2sgPT09PT09DQoNCiMgS29sb20gcGVuZGFwYXRhbiAoQW5udWFsIEluY29tZSkNCmluY29tZV9jb2wgPC0gZ3JlcCgiQW5udWFsIiwgbmFtZXMoZGYpLCB2YWx1ZSA9IFRSVUUpDQoNCiMgS29sb20gU3BlbmRpbmcgU2NvcmUNCnNwZW5kX2NvbCA8LSBncmVwKCJTcGVuZGluZyIsIG5hbWVzKGRmKSwgdmFsdWUgPSBUUlVFKQ0KDQojIEtvbG9tIEFnZQ0KYWdlX2NvbCA8LSBncmVwKCJBZ2UiLCBuYW1lcyhkZiksIHZhbHVlID0gVFJVRSkNCg0KIyBDZWsgaGFzaWxueWENCnByaW50KGluY29tZV9jb2wpDQpwcmludChzcGVuZF9jb2wpDQpwcmludChhZ2VfY29sKQ0KDQojID09PT09PSBTZWxlY3Qga29sb20gbnVtZXJpayA9PT09PT0NCm51bV9jb2xzIDwtIGRmICU+JSBzZWxlY3QoYWxsX29mKGMoYWdlX2NvbCwgaW5jb21lX2NvbCwgc3BlbmRfY29sKSkpDQoNCiMgPT09PT09IFNjYWxpbmcgPT09PT09DQpzY2FsZXIgPC0gcHJlUHJvY2VzcyhudW1fY29scywgbWV0aG9kID0gYygiY2VudGVyIiwgInNjYWxlIikpDQpkZl9zY2FsZWQgPC0gcHJlZGljdChzY2FsZXIsIG51bV9jb2xzKQ0KDQojID09PT09PSBLb2xvbSBDdXN0b21lcklEIChjYXJpIG90b21hdGlzIGp1Z2EpID09PT09PQ0KaWRfY29sIDwtIGdyZXAoIkN1c3RvbWVyIiwgbmFtZXMoZGYpLCB2YWx1ZSA9IFRSVUUpDQoNCiMgPT09PT09IEZpbmFsIGRhdGFzZXQgPT09PT09DQpkZl9maW5hbCA8LSBkZiAlPiUNCiAgc2VsZWN0KGFsbF9vZihpZF9jb2wpLCBHZW5kZXJfZW5jb2RlZCkgJT4lDQogIGJpbmRfY29scyhkZl9zY2FsZWQpDQoNCmRhdGF0YWJsZShoZWFkKGRmX2ZpbmFsLCAxMCkpDQoNCg0KYGBgDQoNCioqSW50ZXJwcmV0YXNpIEhhc2lsIFByZXByb2Nlc3NpbmcqKg0KDQpEYXRhIHRlbGFoIGJlcmhhc2lsIGRpcGVyc2lhcGthbiB1bnR1ayBwcm9zZXMgKipjbHVzdGVyaW5nKiouIFZhcmlhYmVsICoqR2VuZGVyKiogdGVsYWggZGlrb252ZXJzaSBtZW5qYWRpIG5pbGFpIG51bWVyaWsgKDAg4oaSIEZlbWFsZSwgMSDihpIgTWFsZSkgc2VoaW5nZ2EgZGFwYXQgZGloaXR1bmcgb2xlaCBhbGdvcml0bWEuIFRpZ2EgdmFyaWFiZWwgbnVtZXJpayB1dGFtYeKAlCoqQWdlKiosICoqQW5udWFsIEluY29tZSoqLCBkYW4gKipTcGVuZGluZyBTY29yZSoq4oCUdGVsYWggbWVsYWx1aSBwcm9zZXMgKipzdGFuZGFyZGlzYXNpKiosIHNlaGluZ2dhIGJlcmFkYSBwYWRhIHNrYWxhIHlhbmcgc2FtYSBkYW4gdGlkYWsgYWRhIHZhcmlhYmVsIHlhbmcgbWVuZG9taW5hc2kuDQoNCkRlbmdhbiBwcmVwcm9jZXNzaW5nIGluaSwgZGF0YXNldCBtZW5qYWRpIGxlYmloIGJlcnNpaCwgdGVyc3RydWt0dXIsIGRhbiBzaWFwIGRpZ3VuYWthbiB1bnR1ayBtZW5naGFzaWxrYW4gKipjbHVzdGVyIHBlbGFuZ2dhbiB5YW5nIGxlYmloIGFrdXJhdCBkYW4gc3RhYmlsKiouDQoNCi0tLQ0KDQojIFBhcnRpdGlvbmFsIE1ldG9kZQ0KIyMgS+KAkU1lYW5zDQojIyMgRGVmaW5pc2kgU2luZ2thdCAgDQpNZXRvZGUgY2x1c3RlcmluZyB5YW5nIG1lbWJhZ2kgZGF0YSBtZW5qYWRpIFwoa1wpIGNsdXN0ZXIsIGRpIG1hbmEgdGlhcCBvYnNlcnZhc2kgZGltYXN1a2thbiBrZSBjbHVzdGVyIGRlbmdhbiBjZW50cm9pZCB0ZXJkZWthdCAobWVhbikuIDpjb250ZW50UmVmZXJlbmNlW29haWNpdGU6NF17aW5kZXg9NH0NCg0KIyMjIFJ1bXVzIC8gTW9kZWwgTWF0ZW1hdGlzICANClxbDQpKID0gXHN1bV97aT0xfV57a30gXHN1bV97eF9qIFxpbiBDX2l9IFx8IHhfaiAtIFxtdV9pIFx8XjINClxdICANCmRlbmdhbiBcKFxtdV9pXCkgPSBjZW50cm9pZCAobWVhbikgZGFyaSBjbHVzdGVyIGtlLVwoaVwpLg0KDQojIyMgQ2FyYSBLZXJqYSAmIEh5cGVycGFyYW1ldGVyICANCjEuIFBpbGloIGp1bWxhaCBjbHVzdGVyIFwoa1wpLiAgDQoyLiBJbmlzaWFsaXNhc2kgY2VudHJvaWQgKG1pc2FsIGFjYWssIGF0YXUgcGFrYWnigK9r4oCRbWVhbnMrKykuICANCjMuIEFzc2lnbiB0aWFwIHRpdGlrIGRhdGEga2UgY2VudHJvaWQgdGVyZGVrYXQuICANCjQuIFVwZGF0ZSBjZW50cm9pZCBiZXJkYXNhcmthbiByYXRh4oCRcmF0YSB0aWFwIGNsdXN0ZXIuICANCjUuIFVsYW5nIHNhbXBhaSBrb252ZXJnZW5zaSAoY2VudHJvaWQgdGlkYWsgYmVydWJhaC90aWRhayBhZGEgcGVydWJhaGFuIGFzc2lnbm1lbnQpLiAgDQoqKkh5cGVycGFyYW1ldGVyIHV0YW1hOiBcKGtcKSwgaW5pc2lhbGlzYXNpIGNlbnRyb2lkLCBtYWtzaW1hbCBpdGVyYXNpLCB0b2xva2FuIGtvbnZlcmdlbnNpLioqDQoNCiMjIyBLZWxlYmloYW4gJiBLZXRlcmJhdGFzYW4gIA0KDQp8IEtlbGViaWhhbiAgICAgICAgIHwgS2V0ZXJiYXRhc2FuICAgICAgICB8DQp8LS0tLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS0tLS0tLS18DQp8LSBTZWRlcmhhbmEgJiBtdWRhaCBkaWltcGxlbWVudGFzaWthbi4gPGJyPi0gQ2VwYXQgJiBza2FsYWJlbCBiaWxhIGRhdGEgYmVzYXIuIDxicj4tIENvY29rIGJpbGEgY2x1c3RlciByZWxhdGlmIGtvbXBhayAvIGJlcmJlbnR1ayBidWxhdC4gfCAtIEhhcnVzIG1lbmVudHVrYW4gXChrXCkgZGkgYXdhbC4gPGJyPi0gU2Vuc2l0aWYgdGVyaGFkYXAgaW5pc2lhbGlzYXNpIGNlbnRyb2lkIC8gYmlzYSBrb252ZXJnZW4ga2Ugb3B0aW11bSBsb2thbC4gPGJyPi0gU2Vuc2l0aWYgdGVyaGFkYXAgb3V0bGllciAvIG5vaXNlLiA8YnI+LSBBc3Vtc2kgY2x1c3RlciBiZXJiYXNpcyBFdWNsaWRlYW4gLyBiZW50dWsgZ2xvYnVsYXIg4oCUIGt1cmFuZyBjb2NvayB1bnR1ayBiZW50dWsgY2x1c3RlciBrb21wbGVrcyAvIGRhdGEgZGVuZ2FuIGRpc3RyaWJ1c2kgdGlkYWsgdW5pZm9ybS4gfA0KDQojIyMgSGFzaWwgJiBWaXN1YWxpc2FzaSANCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIGVjaG89RkFMU0V9DQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIDEuIExPQUQgTElCUkFSSUVTDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KHBsb3RseSkNCg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyAyLiBEQVRBU0VUDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQpkZiA8LSBpcmlzWywgMTo0XSAgICAjIDQgZml0dXIgbnVtZXJpaw0KDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIDMuIEstTUVBTlMgQ0xVU1RFUklORw0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0Kc2V0LnNlZWQoMTIzKQ0KayA8LSAzDQprbV9yZXMgPC0ga21lYW5zKGRmLCBjZW50ZXJzID0gaywgbnN0YXJ0ID0gMjUpDQoNCmNsdXN0ZXJzIDwtIGFzLmZhY3RvcihrbV9yZXMkY2x1c3RlcikNCg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyA0LiBUQU1QSUxLQU4gSEFTSUwgQ0xVU1RFUklORw0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KDQpjYXQoIj09PSBKdW1sYWggYW5nZ290YSB0aWFwIGNsdXN0ZXIgPT09XG4iKQ0KcHJpbnQodGFibGUoY2x1c3RlcnMpKQ0KDQpjYXQoIlxuPT09IENlbnRyb2lkIHRpYXAgY2x1c3RlciA9PT1cbiIpDQpwcmludChrbV9yZXMkY2VudGVycykNCg0KY2F0KCJcbj09PSBDbHVzdGVyIHVudHVrIDEwIGRhdGEgcGVydGFtYSA9PT1cbiIpDQpwcmludChoZWFkKGNsdXN0ZXJzLCAxMCkpDQoNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgNS4gVklTVUFMSVNBU0kgMkQNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCnBsb3RfMmQgPC0gZ2dwbG90KA0KICBkYXRhLmZyYW1lKGRmLCBjbHVzdGVyID0gY2x1c3RlcnMpLA0KICBhZXMoeCA9IFNlcGFsLkxlbmd0aCwgeSA9IFNlcGFsLldpZHRoLCBjb2xvciA9IGNsdXN0ZXIpDQopICsNCiAgZ2VvbV9wb2ludChzaXplID0gMykgKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KcHJpbnQocGxvdF8yZCkNCg0KDQpgYGANCg0KLS0tDQoNCiMjIEvigJFNZWRvaWRzDQoNCiMjIyBEZWZpbmlzaSBTaW5na2F0ICANCk1pcmlwIEvigJFNZWFucywgdGV0YXBpIHB1c2F0IGNsdXN0ZXIgKOKAnG1lZG9pZOKAnSkgYWRhbGFoIHRpdGlrIGRhdGEgYWt0dWFsIOKAlCBidWthbiByYXRh4oCRcmF0YSDigJQgc2VoaW5nZ2EgbGViaWggcm9idXN0IHRlcmhhZGFwIG91dGxpZXIgJiBiaXNhIG1lbmdndW5ha2FuIG1ldHJpayBqYXJhayBhcmJpdHJhcnkgKHRpZGFrIGhhcnVzIEV1Y2xpZGVhbikuIA0KDQojIyMgUHJpbnNpcCAvIE1vZGVsIE1hdGVtYXRpcyAgDQpPYmpla3RpZjogbWVtaW5pbWFsa2FuIHRvdGFsIGRpc3NpbWlsYXJpdHkgLyBqYXJhayBhbnRhcmEgdGlhcCB0aXRpayBkZW5nYW4gbWVkb2lkIGNsdXN0ZXI6ICANClxbDQpKID0gXHN1bV97aT0xfV57a30gXHN1bV97eF9qIFxpbiBDX2l9IGQoeF9qLCBtX2kpDQpcXSAgDQpkZW5nYW4gXChtX2lcKSA9IG1lZG9pZCBjbHVzdGVyIGtl4oCRXChpXCksIGRhbiBcKGQoXGNkb3QsIFxjZG90KVwpID0gamFyYWsgLyBkaXNzaW1pbGFyaXR5IHlhbmcgc2VzdWFpLiA6Y29udGVudFJlZmVyZW5jZVtvYWljaXRlOjlde2luZGV4PTl9DQoNCiMjIyBDYXJhIEtlcmphICYgSHlwZXJwYXJhbWV0ZXIgVXRhbWEgIA0KMS4gVGVudHVrYW4ganVtbGFoIGNsdXN0ZXIgXChrXCkuICANCjIuIFBpbGloIFwoa1wpIG1lZG9pZCBhd2FsLCBtaXNhbG55YSBhY2FrIGRhcmkgZGF0YS4gOmNvbnRlbnRSZWZlcmVuY2Vbb2FpY2l0ZToxMF17aW5kZXg9MTB9ICANCjMuIEFzc2lnbiBzZXRpYXAgdGl0aWsgZGF0YSBrZSBtZWRvaWQgdGVyZGVrYXQgYmVyZGFzYXJrYW4gbWV0cmlrIGphcmFrLiAgDQo0LiBVbnR1ayB0aWFwIGNsdXN0ZXIsIGV2YWx1YXNpIGFwYWthaCBhZGEgdGl0aWsgbGFpbiBkYWxhbSBjbHVzdGVyIHlhbmcgamlrYSBkaWphZGlrYW4gbWVkb2lkIGJhcnUgYmlzYSBtZW51cnVua2FuIHRvdGFsIGphcmFrIOKGkiBqaWthIHlhLCBsYWt1a2FuIHN3YXAuIDpjb250ZW50UmVmZXJlbmNlW29haWNpdGU6MTFde2luZGV4PTExfSAgDQo1LiBVbGFuZyBzYW1wYWkga29udmVyZ2Vuc2kgKHRpZGFrIGFkYSBzd2FwIHlhbmcgbWVtcGVyYmFpa2kpLiANCioqSHlwZXJwYXJhbWV0ZXI6IFwoa1wpLCBpbmlzaWFsaXNhc2kgbWVkb2lkIGF3YWwsIGZ1bmdzaSBqYXJhayAvIGRpc3NpbWlsYXJpdHkuKioNCg0KIyMjIEtlbGViaWhhbiAmIEtldGVyYmF0YXNhbiAgDQp8IEtlbGViaWhhbiAgICAgICAgIHwgS2V0ZXJiYXRhc2FuICAgICAgICB8DQp8LS0tLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS0tLS0tLS18DQp8IC0gTWVkb2lkIGFkYWxhaCB0aXRpayBueWF0YSDihpIgcHVzYXQgY2x1c3RlciBpbnRlcnByZXRhYmxlLiA8YnI+LSBMZWJpaCByb2J1c3QgdGVyaGFkYXAgb3V0bGllciAmIG5vaXNlLiA8YnI+LSBCaXNhIG1lbWFrYWkgbWV0cmlrIGphcmFrIGFyYml0cmFyeSAodGlkYWsgaGFydXMgRXVjbGlkZWFuKSDihpIgY29jb2sgdW50dWsgZGF0YSBjYW1wdXJhbiBhdGF1IG5vbuKAkW51bWVyaWsuIHwgLSBLb21wdXRhc2kgbGViaWggbWFoYWwgLyBsYW1iYXQgZGliYW5kaW5nIEvigJFNZWFucywgdGVydXRhbWEgYmlsYSBkYXRhc2V0IGJlc2FyLiA8YnI+LSBLdXJhbmcgZWZpc2llbiB1bnR1ayBkYXRhIGJlcmRpbWVuc2kgdGluZ2dpIGJlc2FyLiA8YnI+LSBIYXNpbCBiaXNhIHNlbnNpdGlmIHRlcmhhZGFwIHBlbWlsaWhhbiBtZWRvaWQgYXdhbC4gfA0KDQojIyMgSGFzaWwgJiBWaXN1YWxpc2FzaQ0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIGVjaG89RkFMU0V9DQpsaWJyYXJ5KGNsdXN0ZXIpDQpsaWJyYXJ5KHBsb3RseSkNCg0KIyAxLiBMb2FkIGRhdGFzZXQNCmRmIDwtIHJlYWQuY3N2KCJNYWxsX0N1c3RvbWVycy5jc3YiKQ0KDQojIDIuIEFtYmlsIGhhbnlhIGtvbG9tIG51bWVyaWMNCmRmX251bWVyaWMgPC0gZGZbc2FwcGx5KGRmLCBpcy5udW1lcmljKV0NCg0KIyAzLiBTY2FsZSBkYXRhDQpkZl9zY2FsZWQgPC0gc2NhbGUoZGZfbnVtZXJpYykNCg0KIyA0LiBNYXRyaXggdW50dWsgUEFNDQpkYXRhX21hdCA8LSBhcy5tYXRyaXgoZGZfc2NhbGVkKQ0KDQojIDUuIFRlbnR1a2FuIGp1bWxhaCBjbHVzdGVyDQprIDwtIDMNCg0KIyA2LiBKYWxhbmthbiBQQU0NCnBhbV9tb2QgPC0gcGFtKGRhdGFfbWF0LCBrID0gaykNCg0KIyAyLiBUYW1iYWhrYW4ga2UgZGF0YXNldA0KZGYkQ2x1c3RlciA8LSBwYW1fbW9kJGNsdXN0ZXINCg0KIyA0LiBKdW1sYWggYW5nZ290YSB0aWFwIGNsdXN0ZXINCnRhYmxlKGRmJENsdXN0ZXIpDQoNCiMgNS4gTWVkb2lkcw0KcGFtX21vZCRtZWRvaWRzDQoNCiMgNi4gU2lsaG91ZXR0ZSBzY29yZQ0KcGFtX21vZCRzaWxpbmZvJGF2Zy53aWR0aA0KDQojIDcuIFBDQSAzRA0KcGNhIDwtIHByY29tcChkYXRhX21hdCwgc2NhbGUuID0gRkFMU0UpDQpwY2FfZGYgPC0gZGF0YS5mcmFtZSgNCiAgUEMxID0gcGNhJHhbLDFdLA0KICBQQzIgPSBwY2EkeFssMl0sDQogIFBDMyA9IHBjYSR4WywzXSwNCiAgY2x1c3RlciA9IGZhY3RvcihwYW1fbW9kJGNsdXN0ZXIpDQopDQoNCiMgOC4gM0QgSW50ZXJha3RpZg0KcGxvdF9seSgNCiAgcGNhX2RmLA0KICB4ID0gflBDMSwgeSA9IH5QQzIsIHogPSB+UEMzLA0KICBjb2xvciA9IH5jbHVzdGVyLA0KICBjb2xvcnMgPSBjKCIjRkY1NzMzIiwiIzJFQ0M3MSIsIiMzNDk4REIiLCIjOUI1OUI2IiwiI0YxQzQwRiIpLA0KICB0eXBlID0gInNjYXR0ZXIzZCIsDQogIG1vZGUgPSAibWFya2VycyIsDQogIG1hcmtlciA9IGxpc3Qoc2l6ZSA9IDUpDQopDQoNCg0KYGBgDQojIyMgSW50ZXJwcmV0YXNpDQpBbmFsaXNpcyBLLU1lZG9pZHMgbWVuZ2hhc2lsa2FuIHRpZ2EgY2x1c3RlciBwZWxhbmdnYW4uICoqQ2x1c3RlciAxKiogYmVyaXNpIHBlbGFuZ2dhbiBkZW5nYW4gdXNpYSBzZWRpa2l0IGRpIGF0YXMgcmF0YS1yYXRhLCBwZW5kYXBhdGFuIG1lbmVuZ2FoIGtlIGJhd2FoLCBkYW4gcGVuZ2VsdWFyYW4gbW9kZXJhdC4gTWVyZWthIG1lcnVwYWthbiBrZWxvbXBvayB0ZXJiZXNhciBkZW5nYW4gcG9sYSBiZWxhbmphIHlhbmcgc3RhYmlsLiAqKkNsdXN0ZXIgMioqIHRlcmRpcmkgZGFyaSBwZWxhbmdnYW4gbXVkYSBiZXJwZW5kYXBhdGFuIHRpbmdnaSBkYW4gbWVtaWxpa2kgc3BlbmRpbmcgc2NvcmUgdGluZ2dpLCBzZWhpbmdnYSBtZW5qYWRpIHNlZ21lbiBwYWxpbmcgbWVuZ3VudHVuZ2thbiBkYW4gcGFsaW5nIGFrdGlmIGJlcmJlbGFuamEuICoqQ2x1c3RlciAzKiogbWVuY2FrdXAgcGVsYW5nZ2FuIGJlcnBlbmRhcGF0YW4gdGluZ2dpIG5hbXVuIG1lbWlsaWtpIHNwZW5kaW5nIHNjb3JlIHJlbmRhaCwgbWVudW5qdWtrYW4gcGVyaWxha3UgYmVsYW5qYSB5YW5nIGxlYmloIHNlbGVrdGlmIGRhbiBqYXJhbmcuDQoNClNlY2FyYSBrZXNlbHVydWhhbiwgc2VnbWVudGFzaSBpbmkgbWVudW5qdWtrYW4gYWRhbnlhIHRpZ2EgdGlwZSB1dGFtYSBwZWxhbmdnYW46IHBlbGFuZ2dhbiB1bXVtIGRlbmdhbiBwZW5nZWx1YXJhbiBzZWRhbmcsIHBlbGFuZ2dhbiBtdWRhIHlhbmcga29uc3VtdGlmLCBkYW4gcGVsYW5nZ2FuIGtheWEgeWFuZyBiZXJiZWxhbmphIHNlY2FyYSBzZWxla3RpZi4NCg0KLS0tDQoNCiMjIEZ1enp5IEPigJFNZWFucyAoRkNNKQ0KDQojIyMgRGVmaW5pc2kgU2luZ2thdCAgDQpTb2Z04oCRY2x1c3RlcmluZzogdGlhcCB0aXRpayBkYXRhIGJpc2EgbWVtaWxpa2kgZGVyYWphdCBrZWFuZ2dvdGFhbiBrZSBzZXRpYXAgY2x1c3RlciAoYnVrYW4gaGFueWEgc2F0dSBjbHVzdGVyKS4gQ29jb2sgamlrYSBjbHVzdGVyIGJpc2Egb3ZlcmxhcCBhdGF1IGJhdGFzIGFudGFy4oCRY2x1c3RlciB0aWRhayBqZWxhcy4gOmNvbnRlbnRSZWZlcmVuY2Vbb2FpY2l0ZToxMl17aW5kZXg9MTJ9DQoNCiMjIyBSdW11cyAvIE1vZGVsIE1hdGVtYXRpcyAgDQpPYmpla3RpZjogIA0KXFsNCkpfbSA9IFxzdW1fe2k9MX1ee2N9IFxzdW1fe2o9MX1ee259IHVfe2lqfV5tIFw7IFx8IHhfaiAtIGNfaSBcfF4yDQpcXSAgDQotIFwodV97aWp9XGluWzAsMV1cKTogZGVyYWphdCBrZWFuZ2dvdGFhbiB0aXRpayBcKHhfalwpIGtlIGNsdXN0ZXIgXChpXCkuIDpjb250ZW50UmVmZXJlbmNlW29haWNpdGU6MTNde2luZGV4PTEzfSAgDQotIFwoY19pXCk6IGNlbnRyb2lkIGNsdXN0ZXIga2XigJFcKGlcKS4gIA0KLSBcKG0gPiAxXCk6IHBhcmFtZXRlciDigJxmdXp6aW5lc3PigJ0gKHNlbWFraW4gYmVzYXIgXChtXCksIHNlbWFraW4ga2FidXIga2Vhbmdnb3RhYW4pLiA6Y29udGVudFJlZmVyZW5jZVtvYWljaXRlOjE0XXtpbmRleD0xNH0gIA0KDQojIyMgQ2FyYSBLZXJqYSAmIEh5cGVycGFyYW1ldGVyIFV0YW1hICANCjEuIFRlbnR1a2FuIGp1bWxhaCBjbHVzdGVyIFwoY1wpIGRhbiBwYXJhbWV0ZXIgZnV6emluZXNzIFwobVwpIChzZXJpbmcgXChtIFxhcHByb3ggMlwpIGppa2EgdGlkYWsgYWRhIHByZWZlcmVuc2kpIDpjb250ZW50UmVmZXJlbmNlW29haWNpdGU6MTZde2luZGV4PTE2fSAgDQoyLiBJbmlzaWFsaXNhc2kgbWF0cmlrcyBrZWFuZ2dvdGFhbiBcKFUgPSBbdV97aWp9XVwpIHNlY2FyYSBhY2FrIChzZXRpYXAgdGl0aWsgbWVtaWxpa2kgZGlzdHJpYnVzaSBrZWFuZ2dvdGFhbiBrZSBzZW11YSBjbHVzdGVyKS4gIA0KMy4gSGl0dW5nIGNlbnRyb2lkIFwoY19pXCkgYmVyZGFzYXJrYW4gYm9ib3QgXCh1X3tpan1ebVwpLiAgDQo0LiBIaXR1bmcgdWxhbmcgYm9ib3Qga2Vhbmdnb3RhYW4gXCh1X3tpan1cKSBiZXJkYXNhcmthbiBqYXJhayBrZSBjZW50cm9pZC4gIA0KNS4gVWxhbmcgbGFuZ2thaCBjZW50cm9pZCAmIGtlYW5nZ290YWFuIHNhbXBhaSBrb252ZXJnZW5zaSAocGVydWJhaGFuIGtlY2lsKSBhdGF1IG1ha3NpbXVtIGl0ZXJhc2kuDQoNCiMjIyBLZWxlYmloYW4gJiBLZXRlcmJhdGFzYW4gIA0KDQp8IEtlbGViaWhhbiAgICAgICAgIHwgS2V0ZXJiYXRhc2FuICAgICAgICB8DQp8LS0tLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS0tLS0tLS18DQotIFNvZnQgY2x1c3RlcmluZyDihpIgdGlhcCB0aXRpayBiaXNhIG1pbGlrIGJlYmVyYXBhIGNsdXN0ZXIgKG1lbWJlcnNoaXApLiA8YnI+LSBDb2NvayBiaWxhIGJhdGFzIGNsdXN0ZXIgdGlkYWsgamVsYXMgYXRhdSBhZGEgb3ZlcmxhcCBhbnRhcuKAkWNsdXN0ZXIuIDxicj4tIE1lbWJlcmkgZGVyYWphdCBrZWFuZ2dvdGFhbiAoYm9ib3QpIOKAlCBmbGVrc2liZWwgJiBpbmZvcm1hdGlmLiB8IC0gS29tcHV0YXNpIGxlYmloIGJlcmF0IGRpYmFuZGluZyBjbHVzdGVyaW5nIOKAnGtlcmFz4oCdLiA8YnI+LSBJbnRlcnByZXRhc2kgY2x1c3RlciBiaXNhIGthYnVyIC8gYW1iaWd1IGppa2EgYmFueWFrIG92ZXJsYXAuIDxicj4tIFNlbnNpdGlmIHRlcmhhZGFwIGluaXNpYWxpc2FzaSBkYW4gcGFyYW1ldGVyIGZ1enppbmVzcyBcKG1cKS4gPGJyPi0gVW50dWsgZGF0YXNldCBiZXNhciBiaXNhIGxhbWJhdCB0YW5wYSBvcHRpbWFzaS4gfA0KDQojIyMga29kZQ0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgZWNobz1GQUxTRX0NCmxpYnJhcnkoZTEwNzEpDQpsaWJyYXJ5KGdncGxvdDIpDQoNCg0KIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgMS4gTG9hZCBkYXRhc2V0DQojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KZGYgPC0gcmVhZC5jc3YoIk1hbGxfQ3VzdG9tZXJzLmNzdiIpDQoNCiMgUGlsaWggZml0dXIgbnVtZXJpaw0KZGF0YV9mY20gPC0gZGZbLCBjKCJBZ2UiLCAiQW5udWFsLkluY29tZS4uay4uIiwgIlNwZW5kaW5nLlNjb3JlLi4xLjEwMC4iKV0NCg0KIyBOb3JtYWxpc2FzaSBkYXRhDQpkYXRhX3NjYWxlZCA8LSBzY2FsZShkYXRhX2ZjbSkNCg0KIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgMi4gRnV6enkgQy1NZWFucyBDbHVzdGVyaW5nDQojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0Kc2V0LnNlZWQoMTIzKQ0KZmNtIDwtIGNtZWFucygNCiAgZGF0YV9zY2FsZWQsDQogIGNlbnRlcnMgPSA0LA0KICBtID0gMiwNCiAgaXRlci5tYXggPSAyMDAsDQogIGRpc3QgPSAiZXVjbGlkZWFuIg0KKQ0KDQojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyAzLiBIQVNJTCBDTFVTVEVSIChkaWphbWluIG11bmN1bCkNCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCmNhdCgiXG49PT0gSEFSRCBDTFVTVEVSIChkb21pbmFudCBtZW1iZXJzaGlwKSA9PT1cbiIpDQpoYXJkX2NsdXN0ZXIgPC0gZmNtJGNsdXN0ZXINCnByaW50KGhlYWQoaGFyZF9jbHVzdGVyLCAyMCkpICAjIHRhbXBpbGthbiAyMCBwZXJ0YW1hDQoNCmNhdCgiXG49PT0gTUVNQkVSU0hJUCBQUk9CQUJJTElUWSAoc29mdCBjbHVzdGVyKSA9PT1cbiIpDQpwcmludChoZWFkKHJvdW5kKGZjbSRtZW1iZXJzaGlwLCAzKSwgNSkpDQoNCmNhdCgiXG49PT0gQ0xVU1RFUiBDRU5URVJTIChub3JtYWxpemVkKSA9PT1cbiIpDQpwcmludChyb3VuZChmY20kY2VudGVycywgMykpDQoNCiMgVGFtYmFoa2FuIGtlIGRhdGFzZXQNCmRmJENsdXN0ZXIgPC0gZmFjdG9yKGhhcmRfY2x1c3RlcikNCg0KIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgNC4gVmlzdWFsaXNhc2kga2h1c3VzIEZDTQ0KIyAgICAtIHdhcm5hID0gY2x1c3RlciBkb21pbmFuDQojICAgIC0gdWt1cmFuID0gbWVtYmVyc2hpcCBrZSBjbHVzdGVyIHRlcnNlYnV0DQojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIE1lbWJlcnNoaXAgdGVyYmVzYXIgc2ViYWdhaSB1a3VyYW4g4oCca2V5YWtpbmFu4oCdDQptYXhfbWVtYmVyc2hpcCA8LSBhcHBseShmY20kbWVtYmVyc2hpcCwgMSwgbWF4KQ0KDQpkZiRNYXhNZW1iZXJzaGlwIDwtIG1heF9tZW1iZXJzaGlwDQoNCmdncGxvdChkZiwgYWVzKA0KICB4ID0gQW5udWFsLkluY29tZS4uay4uLA0KICB5ID0gU3BlbmRpbmcuU2NvcmUuLjEuMTAwLiwNCiAgY29sb3IgPSBDbHVzdGVyLA0KICBzaXplID0gTWF4TWVtYmVyc2hpcA0KKSkgKw0KICBnZW9tX3BvaW50KGFscGhhID0gMC44KSArDQogIHNjYWxlX3NpemUocmFuZ2UgPSBjKDIsIDgpKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiRnV6enkgQy1NZWFucyBDbHVzdGVyaW5nIChGQ00pIiwNCiAgICBzdWJ0aXRsZSA9ICJVa3VyYW4gdGl0aWsgPSBkZXJhamF0IGtlYW5nZ290YWFuIChtZW1iZXJzaGlwKSIsDQogICAgeCA9ICJBbm51YWwgSW5jb21lIChrJCkiLA0KICAgIHkgPSAiU3BlbmRpbmcgU2NvcmUiDQogICkNCmBgYA0KDQotLS0NCg0KIyMgTWluaUJhdGNoIEvigJFNZWFucw0KDQojIyMgRGVmaW5pc2kgU2luZ2thdCAgDQpWYXJpYW4gZGFyaSBL4oCRTWVhbnMgeWFuZyBkaXJhbmNhbmcgZWZpc2llbiB1bnR1ayBkYXRhc2V0IGJlc2FyIOKAlCBkaSB0aWFwIGl0ZXJhc2ksIGNlbnRyb2lkIGRpdXBkYXRlIGJlcmRhc2Fya2FuIHN1YnNldCBhY2FrIChtaW5p4oCRYmF0Y2gpIGRhcmkgZGF0YSwgYnVrYW4gc2VsdXJ1aCBkYXRhIOKGkiBtZW5naGVtYXQgd2FrdHUgZGFuIG1lbW9yaS4gOmNvbnRlbnRSZWZlcmVuY2Vbb2FpY2l0ZToxN117aW5kZXg9MTd9DQoNCiMjIyBQcmluc2lwIC8gTW9kZWwgTWF0ZW1hdGlzIChzZWtpdGFyKSAgDQpPYmpla3RpZiBzYW1hIHNlcGVydGkgS+KAkU1lYW5zIChtaW5pbWFsa2FuIGphcmFrIGt1YWRyYXQga2UgY2VudHJvaWQpLCB0ZXRhcGkgcGVyaGl0dW5nYW4gZGlsYWt1a2FuIHBhZGEgYmF0Y2ggc3Vic2V0IGRhdGEgcGVyIGl0ZXJhc2k6ICANClxbDQpcbWluX3tDXzEsIC4uLiwgQ19rfSBcc3VtX3tpPTF9XntrfSBcc3VtX3t4IFxpbiBCX3QgXGNhcCBDX2l9IFx8IHggLSBcbXVfaSBcfF4yDQpcXSAgDQpkaSBtYW5hIFwoQl90XCkgPSBtaW5p4oCRYmF0Y2ggYWNhayBkaSBpdGVyYXNpIFwodFwpLiA6Y29udGVudFJlZmVyZW5jZVtvYWljaXRlOjE4XXtpbmRleD0xOH0NCg0KIyMjIENhcmEgS2VyamEgJiBIeXBlcnBhcmFtZXRlciBVdGFtYSAgDQoxLiBUZW50dWthbiBqdW1sYWggY2x1c3RlciBcKGtcKSwgdWt1cmFuIGJhdGNoIChiYXRjaF9zaXplKSwgbWFrc2ltYWwgaXRlcmFzaSAvIHRvbCBrb252ZXJnZW5zaS4gOmNvbnRlbnRSZWZlcmVuY2Vbb2FpY2l0ZToxOV17aW5kZXg9MTl9ICANCjIuIERpIHRpYXAgaXRlcmFzaTogYW1iaWwgYmF0Y2ggYWNhayBkYXJpIGRhdGEuICANCjMuIEFzc2lnbiB0aWFwIHRpdGlrIGRpIGJhdGNoIGtlIGNlbnRyb2lkIHRlcmRla2F0LiAgDQo0LiBVcGRhdGUgY2VudHJvaWQgYmVyZGFzYXJrYW4gYmF0Y2ggKGluY3JlbWVudGFsIC8gbW92aW5nIGF2ZXJhZ2UpLiAgDQo1LiBVbGFuZyBzYW1wYWkgc2VsZXNhaSBpdGVyYXNpIC8ga29udmVyZ2Vuc2kuIEh5cGVycGFyYW1ldGVyIHV0YW1hOiBcKGtcKSwgYmF0Y2hfc2l6ZSwgaW5pc2lhbGlzYXNpIGNlbnRyb2lkLCBtYXhfaXRlciAvIHRvbC4NCg0KfCBLZWxlYmloYW4gICAgICAgICB8IEtldGVyYmF0YXNhbiAgICAgICAgfA0KfC0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS0tfA0KLSBFZmlzaWVuICYgaGVtYXQgbWVtb3JpIHVudHVrIGRhdGFzZXQgYmVzYXIgLyBiaWcgZGF0YS4gPGJyPi0gSmF1aCBsZWJpaCBjZXBhdCBkaWJhbmRpbmcgS+KAkU1lYW5zIGJpYXNhIGRhbGFtIHNrZW5hcmlvIGRhdGEgYmVzYXIgLyBzdHJlYW1pbmcuIDxicj4tIE1lbmRla2F0aSBoYXNpbCBL4oCRTWVhbnMgZGVuZ2FuIGJpYXlhIGxlYmloIHJlbmRhaC4gfCAtIEthcmVuYSBtZW1ha2FpIHN1YnNldCwgaGFzaWwgYmlzYSBsZWJpaCBiaXNpbmcgLyBrdXJhbmcgc3RhYmlsLCB0ZXJnYW50dW5nIGJhdGNoIHNhbXBsaW5nLiA8YnI+LSBNYXNpaCBtZW1pbGlraSBhc3Vtc2kgY2x1c3RlciBzZXBlcnRpIEvigJFNZWFucyAoZ2xvYnVsYXIsIEV1Y2xpZGVhbiksIHBlcmx1IHRlbnR1a2FuIFwoa1wpLiA8YnI+LSBLdWFsaXRhcyBjbHVzdGVyIGJpc2EgYmVya3VyYW5nIGRpYmFuZGluZyBL4oCRTWVhbnMgcGFkYSBkYXRhIGtlY2lsIC8gbWVuZW5nYWguIHwNCg0KIyMjIEhhc2lsICYgVmlzdWFsaXNhc2kNCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCBlY2hvPUZBTFNFfQ0KIyAxLiBTZWxlY3QgJiBTY2FsZQ0KWCA8LSBkZiAlPiUgc2VsZWN0KEFubnVhbC5JbmNvbWUuLmsuLiwgU3BlbmRpbmcuU2NvcmUuLjEuMTAwLikNClhfc2NhbGVkIDwtIHNjYWxlKGFzLm1hdHJpeChYKSkNCg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIDIuIE1pbmlCYXRjaCBLLU1lYW5zIChDVVNUT00pDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCm1pbmlfYmF0Y2hfa21lYW5zIDwtIGZ1bmN0aW9uKGRhdGEsIGssIGJhdGNoX3NpemUgPSAyMCwgbWF4X2l0ZXIgPSAyMDAsIGxyID0gMC42KSB7DQogIG4gPC0gbnJvdyhkYXRhKQ0KDQogIHNldC5zZWVkKDQyKQ0KICBjZW50cm9pZHMgPC0gZGF0YVtzYW1wbGUoMTpuLCBrKSwgXQ0KDQogIGZvciAoaXRlciBpbiAxOm1heF9pdGVyKSB7DQogICAgaWR4IDwtIHNhbXBsZSgxOm4sIGJhdGNoX3NpemUpDQogICAgYmF0Y2ggPC0gZGF0YVtpZHgsICwgZHJvcCA9IEZBTFNFXQ0KDQogICAgZGlzdF9tYXQgPC0gYXMubWF0cml4KGRpc3QocmJpbmQoY2VudHJvaWRzLCBiYXRjaCkpKVsxOmssIChrKzEpOihrK2JhdGNoX3NpemUpXQ0KICAgIG5lYXJlc3QgPC0gYXBwbHkoZGlzdF9tYXQsIDIsIHdoaWNoLm1pbikNCg0KICAgIGZvciAoaiBpbiAxOmspIHsNCiAgICAgIHB0cyA8LSBiYXRjaFtuZWFyZXN0ID09IGosICwgZHJvcCA9IEZBTFNFXQ0KICAgICAgaWYgKG5yb3cocHRzKSA+IDApIHsNCiAgICAgICAgY2VudHJvaWRzW2osIF0gPC0gKDEgLSBscikgKiBjZW50cm9pZHNbaiwgXSArIGxyICogY29sTWVhbnMocHRzKQ0KICAgICAgfQ0KICAgIH0NCiAgfQ0KDQogIGRpc3RfZnVsbCA8LSBhcy5tYXRyaXgoZGlzdChyYmluZChjZW50cm9pZHMsIGRhdGEpKSlbMTprLCAoaysxKTooaytuKV0NCiAgbGFiZWxzIDwtIGFwcGx5KGRpc3RfZnVsbCwgMiwgd2hpY2gubWluKQ0KDQogIHJldHVybihsaXN0KGNlbnRyb2lkcyA9IGNlbnRyb2lkcywgbGFiZWxzID0gbGFiZWxzKSkNCn0NCg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIDMuIFJ1biBNaW5pQmF0Y2ggSy1NZWFucw0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09DQprIDwtIDUNCnJlc3VsdCA8LSBtaW5pX2JhdGNoX2ttZWFucyhYX3NjYWxlZCwgayA9IGspDQoNCmRmJENsdXN0ZXJfTUJLIDwtIGZhY3RvcihyZXN1bHQkbGFiZWxzKQ0KDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgNC4gVmlzdWFsaXNhc2kgSW50ZXJha3RpZg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09DQpwbG90X2x5KA0KICBkYXRhID0gZGYsDQogIHggPSB+QW5udWFsLkluY29tZS4uay4uLA0KICB5ID0gflNwZW5kaW5nLlNjb3JlLi4xLjEwMC4sDQogIGNvbG9yID0gfkNsdXN0ZXJfTUJLLA0KICB0eXBlID0gInNjYXR0ZXIiLA0KICBtb2RlID0gIm1hcmtlcnMiLA0KICBtYXJrZXIgPSBsaXN0KHNpemUgPSAxMCwgb3BhY2l0eSA9IDAuODUpDQopICU+JQ0KICBsYXlvdXQoDQogICAgdGl0bGUgPSAiTWluaUJhdGNoIEstTWVhbnMiLA0KICAgIHhheGlzID0gbGlzdCh0aXRsZSA9ICJBbm51YWwgSW5jb21lIChrJCkiKSwNCiAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAiU3BlbmRpbmcgU2NvcmUgKDHigJMxMDApIikNCiAgKQ0KYGBgDQoNCiMjIHBlbWlsaWhhbiBwYXJhbWV0ZXIsIGZpdHRpbmcsIGV2YWx1YXNpDQpQYWRhIHRhaGFwIGluaSwgZW1wYXQgYWxnb3JpdG1hIHBhcnRpdGlvbmFsIGNsdXN0ZXJpbmcgZGl1amk6IEstTWVhbnMsIEstTWVkb2lkcyAoUEFNKSwgRnV6enkgQy1NZWFucyAoRkNNKSwgZGFuIE1pbmlCYXRjaCBLLU1lYW5zLiBUdWp1YW5ueWEgaW5pIGFkYWxhaCB1bnR1ayBtZW5jYXJpIG1ldG9kZSBkZW5nYW4gcGVtaXNhaGFuIGNsdXN0ZXIgcGFsaW5nIGJhaWssIHN0YWJpbCwgZGFuIGVmaXNpZW4uDQoNCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCBlY2hvPUZBTFNFfQ0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIExpYnJhcnkgeWFuZyBkaWJ1dHVoa2FuDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCmxpYnJhcnkoY2x1c3RlcikgICAgICAgICMgc2lsaG91ZXR0ZQ0KbGlicmFyeShmYWN0b2V4dHJhKSAgICAgIyBDSCBpbmRleA0KbGlicmFyeShDbHVzdGVyUikgICAgICAgIyBNaW5pQmF0Y2hLTWVhbnMNCmxpYnJhcnkocHBjbHVzdCkgICAgICAgICMgZnV6enkgYy1tZWFucw0KbGlicmFyeShjbFZhbGlkKSAgICAgICAgIyBEQiBpbmRleA0KbGlicmFyeShtaWNyb2JlbmNobWFyaykgIyBydW50aW1lDQoNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyBTaWFwa2FuIGRhdGENCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyBHdW5ha2FuIHNjYWxlZCBvcmlnaW5hbCBkYXRhDQpudW1fZmVhdHVyZXMgPC0gYygiQWdlIiwgIkFubnVhbC5JbmNvbWUuLmsuLiIsICJTcGVuZGluZy5TY29yZS4uMS4xMDAuIikNCmRhdGFfbWF0IDwtIGFzLm1hdHJpeChkZl9zY2FsZWRbLCBudW1fZmVhdHVyZXNdKQ0KZGF0YV9tYXRbaXMubmEoZGF0YV9tYXQpXSA8LSAwDQpkYXRhX21hdFtpcy5pbmZpbml0ZShkYXRhX21hdCldIDwtIDANCg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIEZ1bmdzaSBiYW50dSBoaXR1bmcgbWV0cmlrIGludGVybmFsDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCmNvbXB1dGVfbWV0cmljcyA8LSBmdW5jdGlvbihsYWJlbHMsIGRhdGFfbWF0KSB7DQogIGxhYmVscyA8LSBhcy5udW1lcmljKGxhYmVscykNCiAgDQogICMgU2lsaG91ZXR0ZQ0KICBtZWFuX3NpbCA8LSBOQQ0KICBpZihsZW5ndGgodW5pcXVlKGxhYmVscykpID4gMSl7DQogICAgc2lsIDwtIHRyeUNhdGNoKHNpbGhvdWV0dGUobGFiZWxzLCBkaXN0KGRhdGFfbWF0KSksIGVycm9yID0gZnVuY3Rpb24oZSkgTlVMTCkNCiAgICBpZighaXMubnVsbChzaWwpKXsNCiAgICAgIG1lYW5fc2lsIDwtIG1lYW4oc2lsWywgInNpbF93aWR0aCJdKQ0KICAgIH0NCiAgfQ0KICANCiAgIyBEQiBpbmRleA0KICBkYiA8LSB0cnlDYXRjaChpbmRleC5EQihkYXRhX21hdCwgbGFiZWxzLCBkID0gTlVMTCkkREIsIGVycm9yID0gZnVuY3Rpb24oZSkgTkEpDQogIA0KICAjIENIIGluZGV4DQogIGNoIDwtIHRyeUNhdGNoKGNhbGluaGFyYShkYXRhX21hdCwgbGFiZWxzKSwgZXJyb3IgPSBmdW5jdGlvbihlKSBOQSkNCiAgDQogIHJldHVybihsaXN0KHNpbGhvdWV0dGUgPSBtZWFuX3NpbCwgREIgPSBkYiwgQ0ggPSBjaCkpDQp9DQoNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyBQaWxpaCBqdW1sYWggY2x1c3RlciAoa19vcHQpIHNlc3VhaSBoYXNpbCBwZW1pbGloYW4gcGFyYW1ldGVyDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCmtfb3B0IDwtIDMgICMgZ2FudGkgc2VzdWFpIGhhc2lsIEVsYm93L1NpbGhvdWV0dGUNCg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIDEuIEstTWVhbnMNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0Kc2V0LnNlZWQoMTIzKQ0KdGltZV9rbWVhbnMgPC0gbWljcm9iZW5jaG1hcmsoDQogIGttIDwtIGttZWFucyhkYXRhX21hdCwgY2VudGVycyA9IGtfb3B0LCBuc3RhcnQgPSAyNSksDQogIHRpbWVzID0gNQ0KKQ0KbGFiZWxzX2ttIDwtIGFzLm51bWVyaWMoa20kY2x1c3RlcikNCm1ldHJpY3Nfa20gPC0gY29tcHV0ZV9tZXRyaWNzKGxhYmVsc19rbSwgZGF0YV9tYXQpDQoNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyAyLiBLLU1lZG9pZHMgKFBBTSkNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0Kc2V0LnNlZWQoMTIzKQ0KdGltZV9wYW0gPC0gbWljcm9iZW5jaG1hcmsoDQogIHBhbV9tb2QgPC0gcGFtKGRhdGFfbWF0LCBrID0ga19vcHQpLA0KICB0aW1lcyA9IDUNCikNCmxhYmVsc19wYW0gPC0gYXMubnVtZXJpYyhwYW1fbW9kJGNsdXN0ZXJpbmcpDQptZXRyaWNzX3BhbSA8LSBjb21wdXRlX21ldHJpY3MobGFiZWxzX3BhbSwgZGF0YV9tYXQpDQoNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyAzLiBGdXp6eSBDLU1lYW5zDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCnNldC5zZWVkKDEyMykNCnRpbWVfZmNtIDwtIG1pY3JvYmVuY2htYXJrKA0KICBmY21fbW9kIDwtIGZjbShkYXRhX21hdCwgY2VudGVycyA9IGtfb3B0LCBtID0gMiksDQogIHRpbWVzID0gNQ0KKQ0KbGFiZWxzX2ZjbSA8LSBhcy5udW1lcmljKGFwcGx5KGZjbV9tb2QkdSwgMSwgd2hpY2gubWF4KSkNCm1ldHJpY3NfZmNtIDwtIGNvbXB1dGVfbWV0cmljcyhsYWJlbHNfZmNtLCBkYXRhX21hdCkNCg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIDQuIE1pbmlCYXRjaCBLTWVhbnMNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0Kc2V0LnNlZWQoMTIzKQ0KdGltZV9tYmsgPC0gbWljcm9iZW5jaG1hcmsoDQogIG1ia19tb2QgPC0gTWluaUJhdGNoS21lYW5zKA0KICAgIGRhdGFfbWF0LCANCiAgICBjbHVzdGVycyA9IGtfb3B0LA0KICAgIGJhdGNoX3NpemUgPSAyMCwNCiAgICBudW1faW5pdCA9IDUsDQogICAgbWF4X2l0ZXJzID0gMTAwDQogICksDQogIHRpbWVzID0gNQ0KKQ0KbGFiZWxzX21iayA8LSBhcy5udW1lcmljKG1ia19tb2QkY2x1c3RlcnMpDQppZihtaW4obGFiZWxzX21iaykgPT0gMCkgbGFiZWxzX21iayA8LSBsYWJlbHNfbWJrICsgMQ0KDQptZXRyaWNzX21iayA8LSBjb21wdXRlX21ldHJpY3MobGFiZWxzX21iaywgZGF0YV9tYXQpDQoNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyBSaW5na2FzIGhhc2lsIHBlcmJhbmRpbmdhbg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQpjb21wYXJpc29uX21ldHJpY3MgPC0gZGF0YS5mcmFtZSgNCiAgQWxnb3JpdGhtID0gYygiSy1NZWFucyIsICJLLU1lZG9pZHMiLCAiRnV6enkgQy1NZWFucyIsICJNaW5pQmF0Y2ggS01lYW5zIiksDQogIFNpbGhvdWV0dGUgPSBjKG1ldHJpY3Nfa20kc2lsaG91ZXR0ZSwNCiAgICAgICAgICAgICAgICAgbWV0cmljc19wYW0kc2lsaG91ZXR0ZSwNCiAgICAgICAgICAgICAgICAgbWV0cmljc19mY20kc2lsaG91ZXR0ZSwNCiAgICAgICAgICAgICAgICAgbWV0cmljc19tYmskc2lsaG91ZXR0ZSksDQogIERCX0luZGV4ID0gYyhtZXRyaWNzX2ttJERCLA0KICAgICAgICAgICAgICAgbWV0cmljc19wYW0kREIsDQogICAgICAgICAgICAgICBtZXRyaWNzX2ZjbSREQiwNCiAgICAgICAgICAgICAgIG1ldHJpY3NfbWJrJERCKSwNCiAgQ0hfSW5kZXggPSBjKG1ldHJpY3Nfa20kQ0gsDQogICAgICAgICAgICAgICBtZXRyaWNzX3BhbSRDSCwNCiAgICAgICAgICAgICAgIG1ldHJpY3NfZmNtJENILA0KICAgICAgICAgICAgICAgbWV0cmljc19tYmskQ0gpLA0KICBSdW50aW1lX3NlYyA9IGMobWVhbih0aW1lX2ttZWFucyR0aW1lKS8xZTksDQogICAgICAgICAgICAgICAgICBtZWFuKHRpbWVfcGFtJHRpbWUpLzFlOSwNCiAgICAgICAgICAgICAgICAgIG1lYW4odGltZV9mY20kdGltZSkvMWU5LA0KICAgICAgICAgICAgICAgICAgbWVhbih0aW1lX21iayR0aW1lKS8xZTkpDQopDQoNCiMgVGFtcGlsa2FuIGhhc2lsDQpwcmludChjb21wYXJpc29uX21ldHJpY3MpDQpgYGANCg0KKipJbnRlcnByZXRhc2kgRXZhbHVhc2kgQWxnb3JpdG1hIENsdXN0ZXJpbmcqKg0KDQpFbXBhdCBhbGdvcml0bWEgeWFuZyBkaXVqaSBtZW5naGFzaWxrYW4gbmlsYWkgc2lsaG91ZXR0ZSBwYWRhIGtpc2FyYW4gKiowLjM0OOKAkzAuMzU5KiosIG1lbnVuanVra2FuIGt1YWxpdGFzIHBlbWlzYWhhbiBjbHVzdGVyIHlhbmcgbW9kZXJhdC4gKipLLU1lZG9pZHMqKiBtZW1pbGlraSBuaWxhaSB0ZXJ0aW5nZ2kgKCoqMC4zNTg4KiopLCBzZWRpa2l0IGxlYmloIGJhaWsgZGliYW5kaW5nIEstTWVhbnMgZGFuIEZ1enp5IEMtTWVhbnMsIHNlaGluZ2dhIG1lbWJlbnR1ayBjbHVzdGVyIHlhbmcgc2VkaWtpdCBsZWJpaCBqZWxhcy4gDQoNCioqTWluaUJhdGNoIEstTWVhbnMqKiB0aWRhayBtZW5naGFzaWxrYW4gY2x1c3RlciB2YWxpZCBzZWhpbmdnYSBzZWx1cnVoIG1ldHJpayBiZXJuaWxhaSAqKk5BKiosIGtlbXVuZ2tpbmFuIGthcmVuYSBwYXJhbWV0ZXIgYmF0Y2ggeWFuZyBrdXJhbmcgc2VzdWFpLiANCg0KU2VjYXJhIGtlc2VsdXJ1aGFuLCBwZXJiZWRhYW4gcGVyZm9ybWEgYW50YXIgYWxnb3JpdG1hIHJlbGF0aWYga2VjaWwgc2VoaW5nZ2EgbWV0b2RlIGRhcGF0IGRpcGlsaWggYmVyZGFzYXJrYW4ga2VidXR1aGFuLCBzZXBlcnRpICoqa2V0YWhhbmFuIHRlcmhhZGFwIG91dGxpZXIgKEstTWVkb2lkcykqKiwgKiprZWNlcGF0YW4ga29tcHV0YXNpIChNaW5pQmF0Y2gpKiosIGF0YXUgKiprZWFuZ2dvdGFhbiBmdXp6eSAoRkNNKSoqLg0KDQoNCiMjIEV2YWx1YXNpDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgZWNobz1GQUxTRX0NCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyBTaWFwa2FuIGRhdGENCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KbnVtX2ZlYXR1cmVzIDwtIGMoIkFnZSIsICJBbm51YWwuSW5jb21lLi5rLi4iLCAiU3BlbmRpbmcuU2NvcmUuLjEuMTAwLiIpDQpkYXRhX21hdCA8LSBhcy5tYXRyaXgoZGZfc2NhbGVkWywgbnVtX2ZlYXR1cmVzXSkNCmRhdGFfbWF0W2lzLm5hKGRhdGFfbWF0KV0gPC0gMA0KZGF0YV9tYXRbaXMuaW5maW5pdGUoZGF0YV9tYXQpXSA8LSAwDQoNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyBGdW5nc2kgYmFudHUgaGl0dW5nIG1ldHJpayBpbnRlcm5hbA0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQpjb21wdXRlX21ldHJpY3MgPC0gZnVuY3Rpb24obGFiZWxzLCBkYXRhX21hdCkgew0KICBsYWJlbHMgPC0gYXMubnVtZXJpYyhsYWJlbHMpDQogIA0KICAjIFNpbGhvdWV0dGUNCiAgbWVhbl9zaWwgPC0gTkENCiAgaWYobGVuZ3RoKHVuaXF1ZShsYWJlbHMpKSA+IDEpew0KICAgIHNpbCA8LSB0cnlDYXRjaChzaWxob3VldHRlKGxhYmVscywgZGlzdChkYXRhX21hdCkpLCBlcnJvciA9IGZ1bmN0aW9uKGUpIE5VTEwpDQogICAgaWYoIWlzLm51bGwoc2lsKSl7DQogICAgICBtZWFuX3NpbCA8LSBtZWFuKHNpbFssICJzaWxfd2lkdGgiXSkNCiAgICB9DQogIH0NCiAgDQogICMgREIgaW5kZXgNCiAgZGIgPC0gdHJ5Q2F0Y2goaW5kZXguREIoZGF0YV9tYXQsIGxhYmVscywgZCA9IE5VTEwpJERCLCBlcnJvciA9IGZ1bmN0aW9uKGUpIE5BKQ0KICANCiAgIyBDSCBpbmRleA0KICBjaCA8LSB0cnlDYXRjaChjYWxpbmhhcmEoZGF0YV9tYXQsIGxhYmVscyksIGVycm9yID0gZnVuY3Rpb24oZSkgTkEpDQogIA0KICByZXR1cm4obGlzdChzaWxob3VldHRlID0gbWVhbl9zaWwsIERCID0gZGIsIENIID0gY2gpKQ0KfQ0KDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgVGVudHVrYW4ganVtbGFoIGNsdXN0ZXINCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0Ka19vcHQgPC0gMyAgIyBnYW50aSBzZXN1YWkgaGFzaWwgRWxib3cvU2lsaG91ZXR0ZQ0KDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgMS4gSy1NZWFucw0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQpzZXQuc2VlZCgxMjMpDQp0aW1lX2ttZWFucyA8LSBtaWNyb2JlbmNobWFyaygNCiAga20gPC0ga21lYW5zKGRhdGFfbWF0LCBjZW50ZXJzID0ga19vcHQsIG5zdGFydCA9IDI1KSwNCiAgdGltZXMgPSA1DQopDQpsYWJlbHNfa20gPC0gYXMubnVtZXJpYyhrbSRjbHVzdGVyKQ0KbWV0cmljc19rbSA8LSBjb21wdXRlX21ldHJpY3MobGFiZWxzX2ttLCBkYXRhX21hdCkNCg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIDIuIEstTWVkb2lkcyAoUEFNKQ0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQpzZXQuc2VlZCgxMjMpDQp0aW1lX3BhbSA8LSBtaWNyb2JlbmNobWFyaygNCiAgcGFtX21vZCA8LSBwYW0oZGF0YV9tYXQsIGsgPSBrX29wdCksDQogIHRpbWVzID0gNQ0KKQ0KbGFiZWxzX3BhbSA8LSBhcy5udW1lcmljKHBhbV9tb2QkY2x1c3RlcmluZykNCm1ldHJpY3NfcGFtIDwtIGNvbXB1dGVfbWV0cmljcyhsYWJlbHNfcGFtLCBkYXRhX21hdCkNCg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIDMuIEZ1enp5IEMtTWVhbnMNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0Kc2V0LnNlZWQoMTIzKQ0KdGltZV9mY20gPC0gbWljcm9iZW5jaG1hcmsoDQogIGZjbV9tb2QgPC0gZmNtKGRhdGFfbWF0LCBjZW50ZXJzID0ga19vcHQsIG0gPSAyKSwNCiAgdGltZXMgPSA1DQopDQpsYWJlbHNfZmNtIDwtIGFzLm51bWVyaWMoYXBwbHkoZmNtX21vZCR1LCAxLCB3aGljaC5tYXgpKQ0KbWV0cmljc19mY20gPC0gY29tcHV0ZV9tZXRyaWNzKGxhYmVsc19mY20sIGRhdGFfbWF0KQ0KDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgNC4gTWluaUJhdGNoIEtNZWFucw0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQpzZXQuc2VlZCgxMjMpDQp0aW1lX21iayA8LSBtaWNyb2JlbmNobWFyaygNCiAgbWJrX21vZCA8LSBNaW5pQmF0Y2hLbWVhbnMoDQogICAgZGF0YV9tYXQsIA0KICAgIGNsdXN0ZXJzID0ga19vcHQsDQogICAgYmF0Y2hfc2l6ZSA9IDIwLA0KICAgIG51bV9pbml0ID0gNSwNCiAgICBtYXhfaXRlcnMgPSAxMDANCiAgKSwNCiAgdGltZXMgPSA1DQopDQpsYWJlbHNfbWJrIDwtIGFzLm51bWVyaWMobWJrX21vZCRjbHVzdGVycykNCmlmKG1pbihsYWJlbHNfbWJrKSA9PSAwKSBsYWJlbHNfbWJrIDwtIGxhYmVsc19tYmsgKyAxDQoNCm1ldHJpY3NfbWJrIDwtIGNvbXB1dGVfbWV0cmljcyhsYWJlbHNfbWJrLCBkYXRhX21hdCkNCg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIFJpbmdrYXMgaGFzaWwgcGVyYmFuZGluZ2FuDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCmNvbXBhcmlzb25fbWV0cmljcyA8LSBkYXRhLmZyYW1lKA0KICBBbGdvcml0aG0gPSBjKCJLLU1lYW5zIiwgIkstTWVkb2lkcyIsICJGdXp6eSBDLU1lYW5zIiwgIk1pbmlCYXRjaCBLTWVhbnMiKSwNCiAgU2lsaG91ZXR0ZSA9IGMobWV0cmljc19rbSRzaWxob3VldHRlLA0KICAgICAgICAgICAgICAgICBtZXRyaWNzX3BhbSRzaWxob3VldHRlLA0KICAgICAgICAgICAgICAgICBtZXRyaWNzX2ZjbSRzaWxob3VldHRlLA0KICAgICAgICAgICAgICAgICBtZXRyaWNzX21iayRzaWxob3VldHRlKSwNCiAgREJfSW5kZXggPSBjKG1ldHJpY3Nfa20kREIsDQogICAgICAgICAgICAgICBtZXRyaWNzX3BhbSREQiwNCiAgICAgICAgICAgICAgIG1ldHJpY3NfZmNtJERCLA0KICAgICAgICAgICAgICAgbWV0cmljc19tYmskREIpLA0KICBDSF9JbmRleCA9IGMobWV0cmljc19rbSRDSCwNCiAgICAgICAgICAgICAgIG1ldHJpY3NfcGFtJENILA0KICAgICAgICAgICAgICAgbWV0cmljc19mY20kQ0gsDQogICAgICAgICAgICAgICBtZXRyaWNzX21iayRDSCksDQogIFJ1bnRpbWVfc2VjID0gYyhtZWFuKHRpbWVfa21lYW5zJHRpbWUpLzFlOSwNCiAgICAgICAgICAgICAgICAgIG1lYW4odGltZV9wYW0kdGltZSkvMWU5LA0KICAgICAgICAgICAgICAgICAgbWVhbih0aW1lX2ZjbSR0aW1lKS8xZTksDQogICAgICAgICAgICAgICAgICBtZWFuKHRpbWVfbWJrJHRpbWUpLzFlOSkNCikNCnByaW50KGNvbXBhcmlzb25fbWV0cmljcykNCg0KYGBgDQoNCioqSW50ZXJwcmV0YXNpIEV2YWx1YXNpKioNCg0KSGFzaWwgZXZhbHVhc2kgbWVudW5qdWtrYW4gYmFod2Egc2VtdWEgYWxnb3JpdG1h4oCUKipLLU1lYW5zKiosICoqSy1NZWRvaWRzKiosIGRhbiAqKkZ1enp5IEMtTWVhbnMqKuKAlG1hbXB1IG1lbWJlbnR1ayBjbHVzdGVyIHlhbmcgY3VrdXAgYmFpaywgZGVuZ2FuIG5pbGFpICoqU2lsaG91ZXR0ZSBiZXJhZGEgcGFkYSBraXNhcmFuIDAuMzTigJMwLjM2KiouDQoNCi0gKipLLU1lZG9pZHMqKiBtZW1pbGlraSBuaWxhaSBTaWxob3VldHRlIHRlcnRpbmdnaSAoMC4zNTg4KSwgbWVudW5qdWtrYW4ga3VhbGl0YXMgcGVtaXNhaGFuIGNsdXN0ZXIgeWFuZyBzZWRpa2l0IGxlYmloIGJhaWsgZGFuIGxlYmloIHN0YWJpbCB0ZXJoYWRhcCBvdXRsaWVyLiAgDQotICoqSy1NZWFucyoqIG1lbWlsaWtpIHBlcmZvcm1hIGhhbXBpciBzYW1hLCBuYW11biBsZWJpaCBlZmlzaWVuIHNlY2FyYSBrb21wdXRhc2kuICANCi0gKipGdXp6eSBDLU1lYW5zKiogc2VkaWtpdCBsZWJpaCByZW5kYWgga2FyZW5hIHNpZmF0IHNvZnQgY2x1c3RlcmluZyBtZW1idWF0IGRhdGEgbWVtaWxpa2kga2Vhbmdnb3RhYW4gZ2FuZGEuICANCg0KKipNaW5pQmF0Y2ggS01lYW5zKiogbWVuZ2hhc2lsa2FuIG5pbGFpIFNpbGhvdWV0dGUgKk5BKiwgaGFsIHlhbmcgdW11bSB0ZXJqYWRpIHBhZGEgZGF0YXNldCBrZWNpbCBrYXJlbmEgYmViZXJhcGEgYmF0Y2ggZGFwYXQgbWVtYmVudHVrIGNsdXN0ZXIga29zb25nLg0KDQpEYXJpIHNpc2kgd2FrdHUga29tcHV0YXNpOiAgDQotICoqTWluaUJhdGNoIEtNZWFucyoqIGFkYWxhaCB5YW5nIHRlcmNlcGF0LCAgDQotIGRpaWt1dGkgKipLLU1lYW5zKiogZGFuICoqSy1NZWRvaWRzKiosICANCi0gc2VkYW5na2FuICoqRnV6enkgQy1NZWFucyoqIHBhbGluZyBsYW1iYXQga2FyZW5hIHByb3NlcyBwZXJoaXR1bmdhbiBtYXRyaWtzIGtlYW5nZ290YWFuIGZ1enp5Lg0KDQotLS0NCg0KIyBLZXNpbXB1bGFuDQpCZXJkYXNhcmthbiBldmFsdWFzaSBlbXBhdCBhbGdvcml0bWEgY2x1c3RlcmluZyBwYWRhIGRhdGFzZXQgTWFsbCBDdXN0b21lcnMsIGRhcGF0IGRpc2ltcHVsa2FuIGJhaHdhIGtlZW1wYXQgbWV0b2RlIG1hbXB1IG1lbWJlbnR1ayBjbHVzdGVyIGRlbmdhbiBrdWFsaXRhcyB5YW5nIGN1a3VwIGJhaWssIGRlbmdhbiBuaWxhaSBTaWxob3VldHRlIGJlcmFkYSBwYWRhIGtpc2FyYW4gfjAuMzg14oCTMC4zODguIEZ1enp5IEMtTWVhbnMgdGFtcGlsIHNlZGlraXQgbGViaWggdW5nZ3VsIGthcmVuYSBtYW1wdSBtZW1iZXJpa2FuIHByb2JhYmlsaXRhcyBrZWFuZ2dvdGFhbiBjbHVzdGVyLCBzZW1lbnRhcmEgSy1NZWRvaWRzIG1lbnVuanVra2FuIGtlc3RhYmlsYW4geWFuZyBiYWlrIHRlcmhhZGFwIG91dGxpZXIuIEstTWVhbnMgbWVtaWxpa2kgcGVyZm9ybWEgeWFuZyBoYW1waXIgc2V0YXJhIGRlbmdhbiBGdXp6eSBDLU1lYW5zIGRhbiB0ZXRhcCBtZW5qYWRpIHBpbGloYW4gY2VwYXQgc2VydGEgZWZpc2llbi4gRGkgc2lzaSBsYWluLCBNaW5pQmF0Y2ggS01lYW5zIG1lbmphZGkgYWxnb3JpdG1hIHRlcmNlcGF0LCBtZXNraXB1biBuaWxhaSBTaWxob3VldHRlIHRpZGFrIGRhcGF0IGRpaGl0dW5nIHBhZGEgZGF0YXNldCBpbmkuIERhcmkgc2lzaSBlZmlzaWVuc2kga29tcHV0YXNpLCBGdXp6eSBDLU1lYW5zIG1lcnVwYWthbiB5YW5nIHBhbGluZyBsYW1iYXQgKH4zLDI5IGRldGlrKSwgc2VkYW5na2FuIEstTWVhbnMgZGFuIEstTWVkb2lkcyBzYW5nYXQgY2VwYXQgKDwwLDAxIGRldGlrKSwgZGFuIE1pbmlCYXRjaCBLTWVhbnMgbWVuamFkaSB5YW5nIHRlcmNlcGF0IGRlbmdhbiB3YWt0dSBzZWtpdGFyIH4wLDAwMDQyIGRldGlrLg0KDQojIyBSZWtvbWVuZGFzaSBBbGdvcml0bWEgQ2x1c3RlcmluZw0KDQotICoqTWV0b2RlIFV0YW1hIOKAkyBGdXp6eSBDLU1lYW5zIChGQ00pKiogIA0KICBDb2NvayBkaWd1bmFrYW4gamlrYSBBbmRhIG1lbWJ1dHVoa2FuIGluZm9ybWFzaSAqcHJvYmFiaWxpdGFzIGtlYW5nZ290YWFuKiBzZXRpYXAgZGF0YSB0ZXJoYWRhcCBzZWx1cnVoIGNsdXN0ZXIuIEZDTSBtZW1iZXJpa2FuIGZsZWtzaWJpbGl0YXMgbGViaWggZGFsYW0gaW50ZXJwcmV0YXNpIGthcmVuYSBzYXR1IGRhdGEgZGFwYXQgbWVtaWxpa2kgZGVyYWphdCBhbmdnb3RhIHBhZGEgbGViaWggZGFyaSBzYXR1IGNsdXN0ZXIuDQoNCi0gKipBbHRlcm5hdGlmIFN0YWJpbCDigJMgSy1NZWRvaWRzKiogIA0KICBEaXJla29tZW5kYXNpa2FuIGtldGlrYSBkYXRhc2V0IG1lbWlsaWtpIHBvdGVuc2kgKm91dGxpZXIqIGF0YXUgZGlzdHJpYnVzaSBkYXRhIHlhbmcgdGlkYWsgdGVybGFsdSBob21vZ2VuLiBLLU1lZG9pZHMgbGViaWggcm9idXN0IGRpYmFuZGluZyBLLU1lYW5zIGthcmVuYSBtZW5nZ3VuYWthbiBtZWRvaWQgc2ViYWdhaSBwdXNhdCBjbHVzdGVyIHNlaGluZ2dhIGhhc2lsIGNsdXN0ZXJpbmcgbGViaWggc3RhYmlsLg0KDQotICoqUGFsaW5nIENlcGF0IGRhbiBFZmlzaWVuIOKAkyBNaW5pQmF0Y2ggS01lYW5zKiogIA0KICBQaWxpaGFuIGlkZWFsIHVudHVrIGRhdGFzZXQgYmVzYXIgYXRhdSBrZXRpa2EgbWVtYnV0dWhrYW4gcHJvc2VzIGNsdXN0ZXJpbmcgeWFuZyBzYW5nYXQgY2VwYXQuIE1pbmlCYXRjaCBLTWVhbnMgbWVtYmVyaWthbiBlZmlzaWVuc2kgdGluZ2dpIG1lc2tpcHVuIGJlYmVyYXBhIG1ldHJpayBzZXBlcnRpIHNpbGhvdWV0dGUgbXVuZ2tpbiB0aWRhayBkYXBhdCBkaWhpdHVuZyBwYWRhIGtvbmRpc2kgdGVydGVudHUuDQoNCi0gKipNZXRvZGUgU3RhbmRhciDigJMgSy1NZWFucyoqICANCiAgVGV0YXAgbWVuamFkaSBhbGdvcml0bWEgeWFuZyBzb2xpZCB1bnR1ayBhbmFsaXNpcyBkYXNhciDigJQgY2VwYXQsIHNlZGVyaGFuYSwgZGFuIG1lbWJlcmlrYW4gaGFzaWwgeWFuZyBtdWRhaCBkaWludGVycHJldGFzaWthbi4NCg0K