Tugas Individu

Analisis Komparatif Metode Clustering Hierarki (Agglomerative & Divisive) pada Karakteristik Audio Musik Spotify

Foto Diri

Pendahuluan

Latar Belakang

Foto Diri

Dalam era digital saat ini, platform streaming musik seperti Spotify telah merevolusi cara masyarakat mengonsumsi hiburan. Dengan perpustakaan yang memuat jutaan lagu, tantangan utama dalam Music Information Retrieval (MIR) adalah bagaimana mengelola dan merekomendasikan lagu yang tepat kepada pengguna. Pengelompokan lagu secara manual berdasarkan genre seringkali bersifat subjektif dan tidak cukup granular untuk menangkap nuansa karakteristik audio yang sebenarnya.

Sebuah lagu pop, misalnya, bisa memiliki karakteristik yang sangat berbeda; ada yang bertenaga (high energy) cocok untuk olahraga, dan ada yang melankolis (high valence) cocok untuk relaksasi. Oleh karena itu, pendekatan berbasis data (data-driven) diperlukan untuk mengelompokkan lagu berdasarkan fitur akustiknya secara objektif.

Deskripsi Data

Studi ini menggunakan dataset Spotify Top 10s, yang berisi daftar lagu-lagu terpopuler di Spotify global dari tahun 2010 hingga 2019. Dataset ini mencakup variabel numerik yang merepresentasikan karakteristik audio, seperti Beats Per Minute (BPM), Energy, Danceability, Loudness (dB), Liveness, Valence, Duration, Acousticness, dan Speechiness.

Rumusan Masalah

Permasalahan yang diangkat dalam laporan ini adalah:

  1. Bagaimana karakteristik pola pengelompokan lagu-lagu populer dekade 2010-2019 berdasarkan fitur audionya?

  2. Bagaimana perbandingan kinerja algoritma Hierarchical Clustering pendekatan Agglomerative (dengan berbagai metode Linkage) dibandingkan dengan pendekatan Divisive?

  3. Metode manakah yang memberikan hasil segmentasi paling optimal dan dapat diinterpretasikan dengan baik?

Tujuan Penulisan

Laporan ini disusun untuk memenuhi tugas besar mata kuliah Analisis Data Eksploratif dan Pembelajaran Mesin Tak Terawasi (Unsupervised Learning). Tujuan spesifik dari analisis ini adalah:

  • Penerapan Algoritma: Mengimplementasikan algoritma Agglomerative Clustering (dengan Linkage: Single, Complete, Average, Ward) dan Divisive Clustering.

  • Analisis Komprehensif: Melakukan pra-proses data, reduksi dimensi, dan fitting model.

  • Evaluasi Model: Membandingkan kualitas cluster yang terbentuk menggunakan metrik evaluasi internal seperti Silhouette Coefficient.

  • Reproducibility: Menyajikan seluruh proses analisis dalam format laporan RMarkdown yang dapat direproduksi (reproducible research).


1. Definisi Singkat

1.1. Pengantar Clustering dan Hierarchical Clustering

Clustering (pengelompokan) adalah metode utama dalam Unsupervised Machine Learning yang bertujuan untuk mengorganisir sekumpulan data ke dalam grup-grup (cluster) yang bermakna. Prinsip dasarnya adalah Homogenitas Internal dan Heterogenitas Eksternal: data di dalam satu grup harus sangat mirip satu sama lain, namun sangat berbeda dengan data di grup lain.

Jika dianalogikan dengan perpustakaan, clustering adalah proses menata buku-buku yang berserakan ke dalam rak kategori (seperti Sejarah, Fiksi, Sains) tanpa melihat label di sampulnya, melainkan dengan membaca isinya secara sekilas.

Dalam laporan ini, metode yang digunakan adalah Hierarchical Clustering. Berbeda dengan metode partisi seperti K-Means yang memaksakan kita menentukan jumlah kelompok di awal, metode hierarki membangun struktur “pohon keluarga” data yang disebut Dendrogram. Keunggulan utamanya adalah fleksibilitas; kita dapat melihat bagaimana data-data kecil bergabung menjadi kelompok besar secara bertahap, memberikan wawasan visual yang lebih kaya mengenai struktur data.

1.2. Dua Pendekatan Utama: Agglomerative vs Divisive

Dalam membangun hierarki atau pohon pengelompokan tersebut, terdapat dua strategi yang bergerak dari arah berlawanan:

a. Agglomerative Clustering (Bottom-Up / Dari Bawah ke Atas)

Strategi ini bekerja dengan prinsip “penggabungan”.

  • Logika: Awalnya, setiap titik data dianggap sebagai satu kelompok kecil (cluster) yang berdiri sendiri.

  • Proses: Algoritma kemudian mencari dua kelompok yang paling “mirip” atau dekat jaraknya, lalu menggabungkannya. Kelompok-kelompok tersebut terus digabung (disatukan bertahap) hingga seluruh data menyatu menjadi satu kelompok besar.

  • Kelebihan: Lebih efisien secara komputasi dan mampu menangkap detail hubungan antar-data kecil dengan sangat baik.

b. Divisive Clustering (Top-Down / Dari Atas ke Bawah)

Strategi ini bekerja dengan prinsip “pemecahan”.

  • Logika: Awalnya, seluruh data dianggap sebagai satu kelompok besar yang utuh.

  • Proses: Algoritma kemudian mencari bagian dari kelompok tersebut yang paling “berbeda” atau tidak cocok dengan sisa anggota lainnya, lalu memisahkannya. Kelompok-kelompok tersebut terus dipecah (dibagi dua, dibagi lagi, dst.) hingga tersisa data satuan.

  • Kelebihan: Sangat baik dalam mendeteksi struktur global (kelompok besar) di awal, namun membutuhkan daya komputasi yang jauh lebih berat.

1.3. Linkage Methods: Aturan Pengukuran Jarak

Ketika kita berbicara tentang “menggabungkan dua kelompok terdekat”, muncul pertanyaan mendasar: Bagaimana cara mengukur jarak antara dua kelompok yang berisi banyak anggota? Aturan pengukuran inilah yang disebut Linkage. Pemilihan linkage akan sangat mempengaruhi bentuk cluster yang dihasilkan.

Berikut adalah metode linkage yang digunakan dalam analisis ini:

  1. Single Linkage (Nearest Neighbor):
    • Konsep: Mengukur jarak antara anggota terdekat dari dua kelompok (seperti dua orang yang saling mengulurkan tangan dari dua pulau berbeda).
    • Karakteristik: Cenderung menghasilkan cluster yang memanjang menyerupai rantai (chaining effect). Metode ini sensitif, di mana satu data penghubung bisa menyatukan dua kelompok yang sebenarnya berbeda jauh.
  2. Complete Linkage (Farthest Neighbor):
    • Konsep: Mengukur jarak antara anggota terjauh dari dua kelompok. Dua kelompok hanya boleh bergabung jika anggota yang paling jauh sekalipun masih berada dalam jarak toleransi.
    • Karakteristik: Cenderung menghasilkan cluster yang bulat, padat (compact), dan memiliki diameter kecil. Ini kebalikan dari Single Linkage.
  3. Average Linkage (Rata-rata):
    • Konsep: Menghitung rata-rata jarak antara seluruh pasangan anggota dari dua kelompok.
    • Karakteristik: Merupakan jalan tengah antara Single dan Complete Linkage. Metode ini lebih tahan (robust) terhadap data pencilan (outlier) dibandingkan kedua metode sebelumnya.
  4. Ward’s Method (Minimum Variance):
    • Konsep: Metode ini memiliki pendekatan statistik yang berbeda. Alih-alih hanya mengukur jarak fisik, Ward bertujuan meminimalkan hilangnya informasi atau variansi total di dalam cluster. Ia akan menggabungkan dua kelompok yang, jika disatukan, akan menghasilkan kelompok baru dengan variasi internal paling kecil (paling seragam).
    • Karakteristik: Metode ini adalah standar emas dalam banyak analisis karena cenderung menghasilkan cluster yang ukurannya seimbang dan sangat rapi secara struktur.

2. Rumus Inti dan Model Matematis

Dalam analisis Hierarchical Clustering, dasar perhitungan matematis terletak pada dua tahap: pengukuran jarak antar data individu dan pengukuran jarak antar kelompok (cluster). Berikut adalah notasi dan persamaan penting yang digunakan.

2.1. Pengukuran Jarak Antar Data (Euclidean Distance)

Sebelum melakukan pengelompokan, kita harus menghitung seberapa jauh perbedaan karakteristik antara satu lagu dengan lagu lainnya. Metrik yang digunakan dalam studi ini adalah Euclidean Distance.

Jika kita memiliki dua lagu, lagu \(x\) dan lagu \(y\), dengan \(p\) fitur audio (seperti BPM, Energy, dll), maka jarak antara keduanya dirumuskan sebagai:

\[d(x, y) = \sqrt{\sum_{i=1}^{p} (x_i - y_i)^2}\]

Keterangan: * \(d(x, y)\): Jarak Euclidean antara lagu \(x\) dan \(y\). * \(x_i, y_i\): Nilai fitur ke-\(i\) dari lagu \(x\) dan \(y\). * \(p\): Jumlah dimensi/variabel yang digunakan.

2.2. Kriteria Linkage (Jarak Antar Cluster)

Setelah mengetahui jarak antar individu, algoritma perlu mengetahui jarak antara dua cluster, misal Cluster \(A\) dan Cluster \(B\). Berikut adalah formulasi matematis untuk setiap metode linkage:

a. Single Linkage

Jarak antara cluster \(A\) dan \(B\) adalah jarak minimum antara anggota di \(A\) dan anggota di \(B\). \[d_{single}(A, B) = \min \{ d(a, b) : a \in A, b \in B \}\]

b. Complete Linkage

Jarak antara cluster \(A\) dan \(B\) adalah jarak maksimum antara anggota di \(A\) dan anggota di \(B\). \[d_{complete}(A, B) = \max \{ d(a, b) : a \in A, b \in B \}\]

c. Average Linkage

Jarak adalah rata-rata dari seluruh kemungkinan pasangan jarak antara anggota \(A\) dan anggota \(B\). \[d_{average}(A, B) = \frac{1}{|A| \cdot |B|} \sum_{a \in A} \sum_{b \in B} d(a, b)\] Dimana \(|A|\) dan \(|B|\) adalah jumlah anggota dalam masing-masing cluster.

d. Ward’s Method

Metode Ward bertujuan meminimalkan kenaikan total varians dalam cluster (Error Sum of Squares / SSE). Jarak didefinisikan sebagai seberapa besar kenaikan SSE jika \(A\) dan \(B\) digabung: \[d_{ward}(A, B) = SSE_{A \cup B} - (SSE_A + SSE_B)\]

Semakin kecil nilai \(d_{ward}\), semakin sedikit informasi yang hilang saat penggabungan, yang berarti penggabungan tersebut “aman” untuk dilakukan.


3. Cara Kerja (Langkah Operasi)

Dalam bagian ini, dijelaskan prosedur algoritmik yang dijalankan oleh komputer untuk membentuk hierarki cluster. Pemahaman terhadap langkah-langkah ini penting untuk menjustifikasi hasil yang diperoleh.

3.1. Algoritma Agglomerative Clustering

Proses pembentukan cluster dari bawah ke atas (bottom-up) mengikuti langkah-langkah sistematis berikut:

  1. Inisialisasi (Start): Hitung matriks jarak (biasanya Euclidean Distance) antar semua \(N\) titik data. Pada tahap awal, setiap titik data dianggap sebagai satu cluster tunggal, sehingga terdapat \(N\) cluster.

  2. Pencarian Pasangan Terdekat: Pindai matriks jarak untuk menemukan pasangan cluster yang memiliki jarak terdekat satu sama lain. Jarak ini ditentukan berdasarkan metode Linkage yang dipilih (Single, Complete, Average, atau Ward).

  3. Penggabungan (Merging): Gabungkan kedua cluster terdekat tersebut menjadi satu cluster baru. Akibatnya, jumlah total cluster berkurang satu (\(N-1\)).

  4. Pembaruan Matriks Jarak (Update): Hitung ulang jarak antara cluster baru (hasil gabungan tadi) dengan semua cluster lain yang masih ada.

  5. Iterasi (Looping): Ulangi langkah 2 hingga 4 secara terus-menerus sampai hanya tersisa satu cluster besar yang mencakup seluruh data.

  6. Pemotongan Pohon (Cutting): Untuk mendapatkan hasil akhir, potong Dendrogram pada ketinggian atau jumlah \(k\) tertentu sesuai kebutuhan analisis.

3.2. Algoritma Divisive Clustering (DIANA)

Proses pembentukan cluster dari atas ke bawah (top-down) bekerja dengan urutan terbalik:

  1. Inisialisasi: Mulai dengan satu cluster besar yang berisi seluruh \(N\) data observasi.

  2. Pemilihan Cluster untuk Dipecah: Identifikasi cluster yang memiliki diameter terbesar (jarak terjauh antar anggotanya) atau variansi tertinggi.

  3. Pemisahan (Splitting): Di dalam cluster terpilih, cari objek yang paling tidak mirip dengan rata-rata kelompok (splinter group). Pisahkan objek tersebut dan inisiasi kelompok pecahan baru.

  4. Realokasi: Periksa anggota lain dalam cluster lama. Jika ada anggota yang lebih dekat jaraknya ke kelompok pecahan baru dibandingkan ke kelompok lama, pindahkan anggota tersebut.

  5. Iterasi: Ulangi proses pemecahan ini pada cluster-cluster yang terbentuk hingga setiap cluster hanya berisi satu data tunggal (\(N\) cluster).

3.3. Hyperparameter Utama

Dalam menjalankan algoritma ini, terdapat beberapa parameter kunci yang harus ditentukan oleh pengguna (user-defined parameters) yang akan mempengaruhi hasil akhir:

  1. Metric Jarak (Distance Metric): Cara menghitung jarak antar data individu. Dalam laporan ini digunakan Euclidean Distance. Pilihan lain bisa berupa Manhattan atau Cosine Similarity.

  2. Metode Linkage: Aturan untuk menghitung jarak antar cluster (seperti dijelaskan pada Bab 2). Ini adalah parameter paling krusial karena menentukan bentuk cluster.

    • Opsional: Single, Complete, Average, Ward.
  3. Jumlah Cluster (\(k\)): Titik potong pada dendrogram. Parameter ini tidak ditentukan di awal algoritma (saat training), melainkan ditentukan di akhir saat analisis hasil (post-processing) untuk mendapatkan label kelompok.


4. Kelebihan dan Keterbatasan Praktis

Setiap algoritma machine learning memiliki karakteristik unik yang membuatnya cocok untuk situasi tertentu namun kurang optimal di situasi lain. Berikut adalah analisis mengenai kelebihan dan keterbatasan praktis dari Hierarchical Clustering yang diterapkan pada dataset ini.

4.1. Kelebihan Utama

  1. Tidak Perlu Menentukan Jumlah Cluster (\(k\)) di Awal Berbeda dengan algoritma K-Means yang mewajibkan pengguna menebak nilai \(k\) sebelum proses dimulai, metode hierarki membiarkan data membentuk strukturnya sendiri terlebih dahulu. Keputusan jumlah cluster dapat dilakukan belakangan dengan melihat visualisasi dendrogram, sehingga analisis menjadi lebih fleksibel dan minim bias awal.

  2. Visualisasi Struktur Data (Dendrogram) Metode ini menghasilkan output visual berupa pohon hierarki (dendrogram) yang sangat informatif. Kita dapat menelusuri hubungan antar data (“silsilah” kemiripan lagu) mulai dari level individu hingga kelompok besar. Hal ini sangat berguna untuk menjelaskan hasil analisis kepada orang awam.

  3. Cocok untuk Dataset Kecil-Menengah Pada dataset dengan ukuran ratusan hingga ribuan baris (seperti data top10s.csv yang memiliki ~600 baris), metode ini bekerja sangat baik dan cepat. Struktur hierarki yang dihasilkan pun masih mudah dibaca dan diinterpretasikan oleh mata manusia.

  4. Menangkap Struktur Bersarang (Nested Structure) Metode ini mampu mendeteksi keberadaan sub-cluster di dalam cluster utama (misalnya: di dalam genre “Pop”, ada sub-kelompok “Dance Pop” dan “Acoustic Pop”).

4.2. Keterbatasan Praktis

  1. Kompleksitas Komputasi Tinggi (Lambat pada Big Data) Kelemahan terbesar metode ini adalah biaya komputasinya. Untuk Agglomerative Clustering, kompleksitas waktunya bisa mencapai \(O(n^3)\) atau \(O(n^2 \log n)\). Artinya, jika jumlah data bertambah 10 kali lipat, waktu prosesnya bisa bertambah 1000 kali lipat. Metode ini sangat tidak disarankan untuk dataset dengan jutaan baris (Big Data).

  2. Sensitif terhadap Outlier dan Noise Metode hierarki, terutama Single Linkage, sangat sensitif terhadap data pencilan (outlier). Satu data yang aneh bisa mengacaukan proses penggabungan cluster, menyebabkan hasil struktur yang tidak rapi (efek chaining). Oleh karena itu, tahap pra-proses (cleaning) menjadi sangat krusial.

  3. Keputusan Permanen (Tidak Bisa Undo) Algoritma hierarki bersifat greedy. Sekali dua cluster digabungkan (atau dipecah), keputusan tersebut tidak dapat dibatalkan di langkah berikutnya. Jika terjadi kesalahan penggabungan di awal proses, kesalahan tersebut akan terbawa hingga akhir, yang mungkin menghasilkan struktur yang kurang optimal dibandingkan metode iteratif seperti K-Means.

  4. Subjektivitas dalam Pemotongan Pohon Meskipun fleksibel, penentuan di mana harus memotong dendrogram (untuk menentukan jumlah cluster akhir) seringkali bersifat subjektif dan memerlukan interpretasi visual pengguna, kecuali dibantu dengan metrik validasi seperti Silhouette Score.


5. Sumber dan Loading Data

5.1. Deskripsi dan Sumber Data

Dataset yang digunakan dalam analisis ini adalah Top Spotify Songs from 2010-2019 - BY YEAR. Data ini berisi daftar lagu-lagu paling populer di dunia yang masuk dalam tangga lagu Spotify selama satu dekade terakhir.

Dataset ini memuat berbagai fitur audio teknis yang diekstrak oleh Spotify untuk setiap lagu, antara lain:

  • BPM (Beats Per Minute): Tempo lagu.

  • Energy (nrgy): Seberapa bertenaga lagu tersebut.

  • Danceability (dnce): Kemudahan lagu untuk dipakai menari.

  • Loudness (dB): Kekerasan suara (semakin tinggi negatifnya, semakin pelan).

  • Valence (val): Positivitas lagu (semakin tinggi, semakin ceria; semakin rendah, semakin sedih/depresi).

  • Acousticness (acous): Tingkat akustik lagu.

5.2. Alasan Pemilihan Dataset

Terdapat tiga alasan utama mengapa dataset ini dipilih untuk studi kasus Hierarchical Clustering:

  1. Kaya akan Fitur Numerik Kontinu: Metode clustering berbasis jarak (distance-based) seperti Hierarki sangat bergantung pada data angka. Dataset ini menyediakan variabel audio yang terukur secara matematis (bukan sekadar teks), sehingga perhitungan Euclidean Distance menjadi sangat valid dan bermakna.

  2. Ukuran Data yang Ideal (Sweet Spot): Algoritma Hierarchical Clustering memiliki kompleksitas komputasi yang berat (\(O(n^3)\)). Jika data terlalu besar (jutaan baris), komputer akan lambat. Jika terlalu sedikit, pola tidak terlihat. Jumlah 603 data pada dataset ini adalah ukuran “Goldilocks” (sangat pas)—cukup kecil untuk diproses cepat oleh R, namun cukup besar untuk menghasilkan struktur dendrogram yang kaya dan menarik.

  3. Interpretasi yang Intuitif: Musik adalah domain yang dipahami semua orang. Hasil cluster nantinya akan sangat mudah divalidasi dengan logika manusia (misalnya: “Apakah masuk akal lagu Despacito satu grup dengan Shape of You?”). Ini memudahkan dalam tahap evaluasi kualitatif.

5.3. Proses Loading Data

Berikut adalah tahapan memuat pustaka yang dibutuhkan dan membaca data ke dalam RStudio:

# 1. Load Library
library(tidyverse)
library(cluster)
library(factoextra)
library(dendextend)
library(DT)

# 2. Load Data (DENGAN PERBAIKAN ENCODING)
# Kita tambahkan fileEncoding="ISO-8859-1" untuk menangani karakter spesial
tryCatch({
  # Coba baca dengan encoding Latin-1 (biasanya ini solusinya)
  df <- read.csv("top10s.csv", stringsAsFactors = FALSE, fileEncoding = "ISO-8859-1")
}, warning = function(w) {
  # Jika masih gagal, coba default
  df <<- read.csv("top10s.csv", stringsAsFactors = FALSE)
}, error = function(e) {
  # Fallback terakhir
  df <<- read.csv("top10s.csv", stringsAsFactors = FALSE)
})

# Pastikan nama kolom tidak aneh (kadang ada karakter aneh di kolom pertama)
# Kita rename kolom pertama jadi "id" biar aman
colnames(df)[1] <- "id"

# 3. Menampilkan Data
datatable(df, 
          options = list(
            pageLength = 10, 
            scrollX = TRUE
          ),
          caption = "Tabel 1: Dataset Spotify Top Songs"
)

6. Eksplorasi Data Singkat (EDA)

Tahap Exploratory Data Analysis (EDA) adalah langkah fundamental untuk memahami karakteristik dataset sebelum dilakukan pemodelan. Pada bab ini, kita akan menelaah struktur data, kelengkapan nilai, distribusi statistik, serta mendeteksi outlier melalui visualisasi komprehensif.

6.1. Pemeriksaan Struktur dan Tipe Variabel

Langkah pertama adalah mengidentifikasi tipe data untuk memisahkan antara variabel identitas (label) dan variabel fitur (numerik).

# Melihat struktur data secara ringkas
glimpse(df)
## Rows: 603
## Columns: 15
## $ id        <int> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 1…
## $ title     <chr> "Hey, Soul Sister", "Love The Way You Lie", "TiK ToK", "Bad …
## $ artist    <chr> "Train", "Eminem", "Kesha", "Lady Gaga", "Bruno Mars", "Just…
## $ top.genre <chr> "neo mellow", "detroit hip hop", "dance pop", "dance pop", "…
## $ year      <int> 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, …
## $ bpm       <int> 97, 87, 120, 119, 109, 65, 120, 148, 93, 126, 128, 145, 130,…
## $ nrgy      <int> 89, 93, 84, 92, 84, 86, 78, 76, 37, 72, 87, 83, 82, 83, 84, …
## $ dnce      <int> 67, 75, 76, 70, 64, 73, 75, 52, 48, 79, 62, 62, 77, 83, 44, …
## $ dB        <int> -4, -5, -3, -4, -5, -5, -4, -6, -8, -4, -4, -5, -5, -6, -8, …
## $ live      <int> 8, 52, 29, 8, 9, 11, 4, 12, 12, 7, 6, 10, 70, 11, 12, 36, 11…
## $ val       <int> 80, 64, 71, 71, 43, 54, 82, 38, 14, 61, 47, 48, 63, 71, 78, …
## $ dur       <int> 217, 263, 200, 295, 221, 214, 203, 225, 216, 235, 235, 230, …
## $ acous     <int> 19, 24, 10, 0, 2, 4, 0, 7, 74, 13, 3, 33, 18, 1, 1, 20, 5, 7…
## $ spch      <int> 4, 23, 14, 4, 4, 14, 9, 4, 3, 4, 3, 4, 5, 4, 45, 3, 3, 7, 5,…
## $ pop       <int> 83, 82, 80, 79, 78, 77, 77, 77, 76, 73, 73, 73, 73, 73, 72, …

Analisis Struktur: Berdasarkan output di atas, dataset terdiri dari 603 observasi dan 15 variabel. Kita dapat membagi variabel menjadi dua kelompok:

  • Variabel Label (Non-Numerik): title, artist, top genre, year. Variabel ini hanya digunakan sebagai identitas.

  • Variabel Fitur Audio (Numerik): bpm, nrgy, dnce, dB, live, val, dur, acous, spch, pop. Kesepuluh variabel inilah yang akan dianalisis lebih lanjut sebagai input clustering.

6.2. Pengecekan Missing Values

Kita harus memastikan data bersih dari nilai kosong (NA), karena algoritma clustering berbasis jarak Euclidean tidak dapat memproses data yang hilang.

# Menghitung jumlah missing value (NA) di setiap kolom
colSums(is.na(df))
##        id     title    artist top.genre      year       bpm      nrgy      dnce 
##         0         0         0         0         0         0         0         0 
##        dB      live       val       dur     acous      spch       pop 
##         0         0         0         0         0         0         0

Analisis: Terlihat angka 0 pada semua kolom. Artinya, dataset ini bersih (clean) dan siap diproses tanpa perlu teknik imputasi data.

6.3. Ringkasan Statistik Deskriptif

Statistik deskriptif diperlukan untuk melihat rentang skala data. Ini penting untuk menentukan apakah metode scaling diperlukan atau tidak.

# Menampilkan statistik dasar hanya untuk 10 variabel numerik
# Kita pilih kolom berdasarkan nama fiturnya
df_num_only <- df %>% select(bpm, nrgy, dnce, dB, live, val, dur, acous, spch, pop)
summary(df_num_only)
##       bpm             nrgy           dnce             dB         
##  Min.   :  0.0   Min.   : 0.0   Min.   : 0.00   Min.   :-60.000  
##  1st Qu.:100.0   1st Qu.:61.0   1st Qu.:57.00   1st Qu.: -6.000  
##  Median :120.0   Median :74.0   Median :66.00   Median : -5.000  
##  Mean   :118.5   Mean   :70.5   Mean   :64.38   Mean   : -5.579  
##  3rd Qu.:129.0   3rd Qu.:82.0   3rd Qu.:73.00   3rd Qu.: -4.000  
##  Max.   :206.0   Max.   :98.0   Max.   :97.00   Max.   : -2.000  
##       live            val             dur            acous      
##  Min.   : 0.00   Min.   : 0.00   Min.   :134.0   Min.   : 0.00  
##  1st Qu.: 9.00   1st Qu.:35.00   1st Qu.:202.0   1st Qu.: 2.00  
##  Median :12.00   Median :52.00   Median :221.0   Median : 6.00  
##  Mean   :17.77   Mean   :52.23   Mean   :224.7   Mean   :14.33  
##  3rd Qu.:24.00   3rd Qu.:69.00   3rd Qu.:239.5   3rd Qu.:17.00  
##  Max.   :74.00   Max.   :98.00   Max.   :424.0   Max.   :99.00  
##       spch             pop       
##  Min.   : 0.000   Min.   : 0.00  
##  1st Qu.: 4.000   1st Qu.:60.00  
##  Median : 5.000   Median :69.00  
##  Mean   : 8.358   Mean   :66.52  
##  3rd Qu.: 9.000   3rd Qu.:76.00  
##  Max.   :48.000   Max.   :99.00

Temuan Penting (Alasan Scaling): Terdapat disparitas skala yang sangat mencolok antar variabel:

  • Durasi (dur): Rata-rata ~224, nilai maksimum hingga 424.
  • Speechiness (spch): Rata-rata hanya ~8.
  • Loudness (dB): Memiliki nilai negatif.

Implikasi: Variabel dengan angka besar (seperti Durasi) akan mendominasi perhitungan jarak jika tidak disetarakan. Maka, proses Standardisasi (Scaling) di bab selanjutnya bersifat WAJIB.

6.4. Visualisasi Distribusi (Histogram Collage)

Berikut adalah visualisasi sebaran data seluruh fitur audio dalam satu tampilan kolase (grid) untuk melihat pola distribusinya.

# 1. Transformasi data ke format 'Long' agar bisa di-plot sekaligus
df_long <- df %>%
  select(bpm, nrgy, dnce, dB, live, val, dur, acous, spch, pop) %>%
  pivot_longer(cols = everything(), names_to = "fitur", values_to = "nilai")

# 2. Plot Histogram dengan Facet Wrap yang LEBIH RAPI
ggplot(df_long, aes(x = nilai, fill = fitur)) +
  geom_histogram(bins = 30, color = "white", alpha = 0.8, show.legend = FALSE) +
  facet_wrap(~fitur, scales = "free", ncol = 3) + # Grid 3 kolom
  scale_fill_viridis_d() + # Warna otomatis
  theme_minimal() +
  labs(title = "Distribusi Fitur Audio (Histogram)",
       subtitle = "Melihat pola sebaran data: Normal vs Skewed",
       x = "Nilai Fitur", y = "Frekuensi") +
  theme(
  
    axis.text.x = element_text(angle = 45, hjust = 1, size = 8), # Miringkan angka & kecilkan
    axis.text.y = element_text(size = 8), # Kecilkan angka sumbu Y
    strip.background = element_rect(fill = "#1DB954"), # Hijau Spotify
    strip.text = element_text(color = "white", face = "bold", size = 10)
  )

Interpretasi Visual (Analisis Mendalam):

  1. Dominasi Musik Elektronik (Skewed Distributions): Perhatikan grafik acous (Acousticness), spch (Speechiness), dan live (Liveness). Ketiganya memiliki distribusi yang sangat condong ke kiri (right-skewed).
    • Artinya: Mayoritas lagu populer dekade 2010-2019 memiliki nilai akustik dan liveness yang sangat rendah. Ini mencerminkan tren industri musik modern yang didominasi oleh produksi studio digital dan instrumen elektronik, bukan rekaman konser atau instrumen akustik murni.
  2. Karakteristik Lagu Pop (Normal Distributions): Grafik dnce (Danceability), nrgy (Energy), dan val (Valence) cenderung membentuk kurva lonceng (normal distribution) atau sedikit condong ke kanan.
    • Artinya: Lagu-lagu top chart memiliki variasi yang seimbang. Ada lagu sedih (low valence) dan bahagia (high valence), namun rata-rata berada di nilai tengah yang moderat. Tingginya rata-rata Energy dan Danceability mengonfirmasi bahwa lagu “hits” umumnya adalah lagu yang bersemangat.
  3. Implikasi untuk Clustering: Karena beberapa fitur menyebar normal dan yang lain sangat miring (skewed), serta memiliki rentang angka yang berbeda jauh (lihat sumbu X pada dur vs spch), maka data ini TIDAK BOLEH langsung dihitung jaraknya.

6.5. Deteksi Outlier (Boxplot Collage)

Setelah melihat distribusi data, langkah selanjutnya adalah mendeteksi keberadaan data pencilan (outlier). Outlier adalah observasi yang nilainya menyimpang jauh dari rata-rata data lainnya. Dalam konteks musik, ini bisa berupa lagu yang durasinya sangat panjang atau lagu dengan lirik yang sangat padat.

Visualisasi menggunakan Boxplot sangat efektif untuk tujuan ini. Titik-titik yang berada di luar “kumis” (whiskers) boxplot akan ditandai dengan warna merah.

# Plot Boxplot dengan penanda Outlier Merah
ggplot(df_long, aes(x = fitur, y = nilai, fill = fitur)) +
  geom_boxplot(alpha = 0.7, show.legend = FALSE, 
               outlier.colour = "red", outlier.shape = 19, outlier.size = 2) +
  facet_wrap(~fitur, scales = "free", ncol = 3) +
  theme_bw() + # Menggunakan tema hitam-putih agar titik merah kontras
  labs(title = "Deteksi Outlier (Boxplot)",
       subtitle = "Titik merah menandakan data pencilan (outlier) yang ekstrem",
       x = "Fitur Audio", y = "Nilai") +
  theme(
    axis.text.x = element_blank(), # Menyembunyikan label sumbu X agar tidak berantakan
    axis.ticks.x = element_blank(),
    strip.background = element_rect(fill = "black"), # Header tiap kotak berwarna hitam
    strip.text = element_text(color = "white", face = "bold") # Teks header putih tebal
  )

Interpretasi Outlier: Berdasarkan visualisasi di atas, kita dapat mengidentifikasi beberapa anomali data:

  • Durasi (dur): Terdapat sejumlah titik merah di bagian atas. Ini mengindikasikan adanya lagu-lagu dengan durasi yang sangat panjang dibandingkan mayoritas lagu pop standar (biasanya 3-4 menit).
  • Speechiness (spch): Banyak sekali outlier tinggi. Ini kemungkinan besar merepresentasikan lagu-lagu bergenre Hip-Hop atau Rap yang memiliki densitas kata jauh lebih tinggi daripada lagu pop melodis.
  • Popularitas (pop): Terdapat outlier di bagian bawah, yang menunjukkan adanya lagu-lagu yang pernah masuk tangga lagu namun memiliki skor popularitas yang relatif rendah dibandingkan rekan-rekannya.

Implikasi Metodologis: Keberadaan outlier yang cukup signifikan ini menjadi peringatan bahwa metode clustering yang sensitif terhadap pencilan (seperti Single Linkage) mungkin akan memberikan hasil yang kurang optimal (cenderung membentuk chaining). Oleh karena itu, pada saat mendapt kondidi seperti ini penggunaan metode yang lebih robust seperti Average Linkage atau Ward’s Method sangat disarankan pada tahap pemodelan nanti.

6.6. Korelasi Antar Fitur (Correlation Heatmap)

Terakhir, kita perlu melihat hubungan antar variabel. Apakah lagu yang kencang (dB) pasti energinya (nrgy) tinggi? Kita akan cek menggunakan Matriks Korelasi.

library(ggcorrplot)# 1. librarynya

# 2. Hitung Matriks Korelasi (Hanya kolom numerik)
# Menggunakan nama kolom agar lebih aman daripada nomor indeks
df_num <- df %>% select(bpm, nrgy, dnce, dB, live, val, dur, acous, spch, pop)
cor_matrix <- cor(df_num)

# 3. Visualisasi Heatmap
ggcorrplot(cor_matrix, 
           method = "circle",       # Pakai lingkaran biar tidak sumpek
           type = "lower",          # Cuma tampilkan bagian bawah (segitiga) biar rapi
           lab = TRUE,              # Tampilkan angkanya
           lab_size = 3,            # Ukuran angka pas (tidak kekecilan/kebesaran)
           digits = 1,              # Cuma 1 angka di belakang koma (biar ringkas)
           colors = c("#E41A1C", "white", "#377EB8"), # Merah - Putih - Biru
           title = "Peta Korelasi Fitur Audio",
           ggtheme = theme_minimal()) +
           theme(axis.text.x = element_text(angle = 45, vjust = 1, hjust = 1)) # Miringkan teks

Cara Membaca Heatmap (Interpretasi Simpel):

Bayangkan ini seperti rambu lalu lintas:

  1. Lingkaran Biru Besar (Positif / Berteman):
    • Artinya: “Kalau A naik, B ikut naik.”
    • Contoh: nrgy vs dB (0.5). Lihat lingkaran birunya cukup besar. Ini menunjukkan bahwa lagu yang memiliki energi tinggi biasanya juga diproduksi dengan volume suara yang keras.
    • Contoh: val vs dnce (0.5). Berkorelasi positif, yang berarti lagu dengan nuansa ceria (happy) cenderung memiliki ritme yang enak untuk menari (danceable).
  2. Lingkaran Merah Besar (Negatif / Bermusuhan):
    • Artinya: “Kalau A naik, B malah turun.”
    • Contoh: acous vs nrgy (-0.6). Lingkaran merahnya paling besar. Ini mengartikan hubungan yang bertolak belakang; semakin akustik sebuah lagu, maka tingkat energinya cenderung semakin rendah (lagu santai).
  3. Lingkaran Kecil/Putih (Netral):
    • Artinya: “Tidak ada hubungan yang kuat.”
    • Contoh: pop (Popularitas) hampir berwarna putih terhadap semua fitur lain. Ini temuan menarik yang menunjukkan bahwa fitur teknis lagu (seperti tempo atau energi) tidak menjamin sebuah lagu akan menjadi populer. Popularitas musik dipengaruhi faktor lain di luar fitur audio semata.

7. Pra-proses Data (Preprocessing)

Tahap pra-proses adalah langkah kritis untuk menjamin validitas hasil analisis. Algoritma Hierarchical Clustering bekerja dengan menghitung jarak antar-titik data, sehingga data input harus bersih, unik, dan memiliki skala yang setara.

7.1. Pembersihan Data dan Penanganan Duplikat

Dataset musik seringkali memuat lagu yang sama lebih dari satu kali (misalnya, lagu yang bertahan di tangga lagu selama dua tahun berturut-turut). Duplikasi ini harus dihapus untuk menghindari bias. Selain itu, kita perlu mengatur indeks baris (row names) agar visualisasi dendrogram nantinya menampilkan nama lagu, bukan nomor urut.

# 1. Menghapus Data Duplikat
# Kita hanya mengambil kemunculan pertama dari setiap lagu berdasarkan Judul dan Artis
df_clean <- df %>% distinct(title, artist, .keep_all = TRUE)

# 2. Menjadikan Judul Lagu sebagai Index (Label)
# Menggabungkan Judul dan Artis agar label lebih informatif
# Fungsi make.unique() memastikan tidak ada nama yang sama persis (error handling)
rownames(df_clean) <- make.unique(paste(df_clean$title, "-", df_clean$artist))

# Cek dimensi data setelah pembersihan
dim(df_clean)
## [1] 587  15

Analisis Output: Hasil 587 15 artinya sekarang data kita tinggal 587 baris (lagu) dan 15 kolom.

  • Awalnya ada 603 lagu.

  • Sekarang sisa 587 lagu.

  • Berarti kita sukses membuang 16 lagu kembar (duplikat).

Sekarang datanya sudah bersih, setiap baris mewakili satu lagu yang unik.

7.2. Seleksi Fitur (Feature Selection)

Tidak semua variabel dalam dataset relevan untuk perhitungan jarak matematis. Kita harus mengeliminasi variabel kategorik (teks) dan variabel numerik yang bukan merupakan fitur audio (seperti Tahun atau ID).

Berikut adalah proses pemilihan kolom fitur audio saja, yang ditampilkan dalam tabel interaktif agar dapat diperiksa secara menyeluruh.

# 1. Memilih hanya kolom fitur audio (Numerik)
# Variabel: bpm, nrgy, dnce, dB, live, val, dur, acous, spch, pop
df_select <- df_clean %>% 
  select(bpm, nrgy, dnce, dB, live, val, dur, acous, spch, pop)

# 2. Menampilkan Data Hasil Seleksi (Interactive Table)
# Menggunakan datatable agar semua data bisa dilihat (di-scroll)
datatable(df_select, 
          options = list(
            pageLength = 5,      # Tampilkan 5 baris per halaman biar rapi
            scrollX = TRUE,      # Aktifkan geser samping
            searching = TRUE     # Aktifkan fitur pencarian
          ),
          caption = "Tabel Data Fitur Audio (Siap untuk Scaling)"
)

Interpretasi Seleksi Data:

Tabel di atas menampilkan data fitur audio yang sudah diseleksi.

  1. Tentang Angka: Semua kolom yang tersisa adalah variabel numerik (BPM, Energy, dll) yang siap diolah secara matematis.

  2. Tentang Judul Lagu: Teks judul lagu yang terlihat di sisi paling kiri bukanlah kolom variabel, melainkan Label Baris (Row Names).

    • Label ini tidak akan dihitung dalam kalkulasi jarak Euclidean.

    • Label ini sengaja dipertahankan sebagai penanda identitas agar nanti saat visualisasi dendrogram terbentuk, kita bisa mengetahui lagu mana yang masuk ke dalam cluster tertentu.

Jadi, dataset ini sudah 100% valid: hanya berisi angka untuk perhitungan, namun tetap membawa identitas lagu untuk interpretasi.

7.3. Standardisasi Data (Z-Score Scaling)

Sebagaimana temuan pada tahap EDA (Bab 6), terdapat perbedaan skala yang ekstrem antar variabel (contoh: Durasi ratusan vs Speechiness satuan). Jika data mentah ini langsung digunakan dalam perhitungan Euclidean Distance, variabel dengan nilai besar akan mendominasi hasil klasterisasi secara tidak adil.

Solusinya adalah melakukan Standardisasi (Scaling) menggunakan metode Z-Score, yang mengubah distribusi data sehingga memiliki rata-rata 0 dan standar deviasi 1.

\[z = \frac{x - \mu}{\sigma}\]

# 1. Melakukan Scaling
# Fungsi scale() otomatis menghitung Z-score untuk setiap kolom
df_scaled <- scale(df_select)

# 2. Mengubah format kembali menjadi Data Frame
# (Output scale() adalah matrix, kita ubah jadi data frame biar rapi)
df_scaled <- as.data.frame(df_scaled)

# 3. Cek Statistik Setelah Scaling
# Perhatikan baris "Mean". Semuanya harus bernilai nol (atau mendekati nol misal: 1.2e-16)
summary(df_scaled)
##       bpm                nrgy              dnce               dB          
##  Min.   :-4.77301   Min.   :-4.3323   Min.   :-4.8174   Min.   :-19.2649  
##  1st Qu.:-0.75056   1st Qu.:-0.5835   1st Qu.:-0.5589   1st Qu.: -0.1525  
##  Median : 0.05393   Median : 0.2155   Median : 0.1135   Median :  0.2014  
##  Mean   : 0.00000   Mean   : 0.0000   Mean   : 0.0000   Mean   :  0.0000  
##  3rd Qu.: 0.41595   3rd Qu.: 0.7071   3rd Qu.: 0.6365   3rd Qu.:  0.5553  
##  Max.   : 3.51323   Max.   : 1.6904   Max.   : 2.4296   Max.   :  1.2632  
##       live              val                dur              acous        
##  Min.   :-1.3571   Min.   :-2.31987   Min.   :-2.6529   Min.   :-0.6923  
##  1st Qu.:-0.6730   1st Qu.:-0.76519   1st Qu.:-0.6617   1st Qu.:-0.5950  
##  Median :-0.4449   Median :-0.01006   Median :-0.1054   Median :-0.4004  
##  Mean   : 0.0000   Mean   : 0.00000   Mean   : 0.0000   Mean   : 0.0000  
##  3rd Qu.: 0.4672   3rd Qu.: 0.74506   3rd Qu.: 0.4364   3rd Qu.: 0.1346  
##  Max.   : 4.2678   Max.   : 2.03322   Max.   : 5.8388   Max.   : 4.1228  
##       spch               pop         
##  Min.   :-1.13313   Min.   :-4.5423  
##  1st Qu.:-0.58546   1st Qu.:-0.4394  
##  Median :-0.44854   Median : 0.1760  
##  Mean   : 0.00000   Mean   : 0.0000  
##  3rd Qu.: 0.09913   3rd Qu.: 0.6547  
##  Max.   : 5.43892   Max.   : 2.2275

Interpretasi Output Scaling:

Berdasarkan ringkasan statistik di atas, proses standardisasi telah berhasil dilakukan. Hal ini dibuktikan dengan dua indikator utama:

  1. Mean (Rata-rata) Bernilai 0: Perhatikan baris Mean pada setiap variabel (bpm, nrgy, dnce, dll). Semuanya menunjukkan angka 0.0000. Ini mengonfirmasi bahwa distribusi data telah digeser ke titik tengah nol.

  2. Skala Seragam (Homogen): Sebelumnya, bpm bernilai ratusan dan dB bernilai negatif. Sekarang, perhatikan nilai Min dan Max pada semua kolom. Semuanya berada dalam rentang yang relatif sama, yaitu sekitar -4 hingga +4 (standar deviasi).

    • Contoh: Nilai Max bpm adalah 3.51, dan nilai Max dnce adalah 2.42. Keduanya kini “selevel”.

Data kini bersifat objektif. Tidak ada lagi variabel yang mendominasi hanya karena satuannya besar. Jarak antar-lagu kini murni mencerminkan perbedaan pola audio, bukan perbedaan satuan ukur.


8. Teknik Reduksi Dimensi (PCA)

Dataset ini memiliki 10 variabel fitur audio (dimensi). Secara matematis, melakukan clustering pada dimensi tinggi itu sah-sah saja, namun manusia tidak bisa memvisualisasikan data dalam 10 dimensi.

Oleh karena itu, teknik reduksi dimensi seperti Principal Component Analysis (PCA) diperlukan. * Tujuan: Memproyeksikan informasi dari 10 variabel asli menjadi 2 variabel utama (Principal Components) yang merangkum variansi data sebanyak mungkin. * Manfaat: Memungkinkan visualisasi hasil clustering dalam grafik 2D (Sumbu X dan Y) di bab-bab selanjutnya.

8.1. Implementasi PCA

Kita menggunakan fungsi prcomp. Karena data sudah dilakukan scaling pada Bab 7 (objek df_scaled), kita tidak perlu melakukan scaling ulang.

# 1. Menjalankan PCA
# scale. = FALSE karena data input (df_select) sudah kita scale di Bab 7
pca_result <- prcomp(df_select, scale. = TRUE)

# 2. Melihat Ringkasan Hasil PCA
summary(pca_result)
## Importance of components:
##                           PC1    PC2    PC3    PC4     PC5     PC6     PC7
## Standard deviation     1.5885 1.1954 1.0567 1.0007 0.97101 0.89734 0.86527
## Proportion of Variance 0.2523 0.1429 0.1117 0.1001 0.09429 0.08052 0.07487
## Cumulative Proportion  0.2523 0.3952 0.5069 0.6070 0.70133 0.78185 0.85672
##                            PC8    PC9    PC10
## Standard deviation     0.84696 0.6870 0.49341
## Proportion of Variance 0.07173 0.0472 0.02435
## Cumulative Proportion  0.92845 0.9757 1.00000

Interpretasi Hasil PCA (Importance of Components):

Tabel di atas menunjukkan seberapa besar setiap komponen baru (Principal Component) mampu merangkum informasi dari 10 variabel audio asli.

  1. Mengapa ada PC1 s/d PC10? Jumlah komponen (PC) yang terbentuk adalah 10, sama persis dengan jumlah variabel input (bpm, nrgy, dnce, dst). PCA tidak membuang data, melainkan menyusun ulang data tersebut dari yang paling penting (PC1) ke yang paling tidak penting (PC10).

  2. Proporsi Variansi (Proportion of Variance):

    • PC1 (0.2523): Menyimpan 25.23% dari total informasi data. Ini adalah komponen terpenting.
    • PC2 (0.1429): Menyimpan 14.29% informasi.
    • PC10 (0.02435): Hanya menyimpan 2.4% informasi. Ini dianggap sebagai noise atau detail yang tidak signifikan.
  3. Proporsi Kumulatif (Cumulative Proportion):

    • Fokus kita adalah pada gabungan PC1 + PC2.
    • Nilai kumulatifnya adalah 0.3952 atau sekitar 40%.
    • Kesimpulan: Meskipun kita mereduksi data dari 10 dimensi menjadi 2 dimensi (PC1 & PC2) untuk visualisasi nanti, grafik 2D tersebut masih mampu merepresentasikan hampir 40% karakteristik asli lagu-lagu tersebut. Angka ini cukup representatif untuk melihat pola pengelompokan global.

8.2. Visualisasi Scree Plot (Persentase Informasi)

Untuk memvalidasi keputusan kita menggunakan 2 dimensi, kita gunakan Scree Plot. Grafik ini menunjukkan berapa persen informasi yang ditangkap oleh setiap komponen.

# Visualisasi Scree Plot menggunakan library 'factoextra'
fviz_eig(pca_result, 
         addlabels = TRUE,       # Tampilkan angka persentase
         ylim = c(0, 50),        # Batas sumbu Y agar grafik rapi
         barfill = "#1DB954",    # Warna Hijau Spotify
         barcolor = "black",
         main = "Scree Plot: Persentase Informasi per Dimensi")
## Warning in geom_bar(stat = "identity", fill = barfill, color = barcolor, :
## Ignoring empty aesthetic: `width`.

Interpretasi Scree Plot:

  • Dimensi 1 (PC1): Menyimpan variansi informasi terbesar (seperti yang terlihat pada balok tertinggi).
  • Dimensi 2 (PC2): Menyimpan variansi informasi terbesar kedua.
  • Kesimpulan: Gabungan PC1 dan PC2 akan digunakan sebagai sumbu X dan Y pada plot visualisasi klaster nanti. Meskipun tidak menangkap 100% informasi, ini adalah pendekatan visual terbaik untuk menyederhanakan data multidimensi agar dapat dilihat oleh mata manusia.

8.3. Visualisasi Biplot (Peta Kontribusi Variabel)

Sebelum melihat pengelompokan lagu, kita perlu memahami “peta” variabelnya. Variabel mana yang menarik data ke kanan (PC1 positif) atau ke atas (PC2 positif)?

# Visualisasi Biplot Variabel
fviz_pca_var(pca_result,
             col.var = "contrib", # Warna panah berdasarkan besarnya kontribusi
             gradient.cols = c("grey", "blue", "red"), # Merah = Kontribusi Paling Besar
             repel = TRUE,        # Agar teks label tidak bertumpuk
             title = "PCA Biplot: Peta Kontribusi Fitur Audio"
             )
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## ℹ The deprecated feature was likely used in the ggpubr package.
##   Please report the issue at <https://github.com/kassambara/ggpubr/issues>.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.

Interpretasi Biplot:

Grafik ini berfungsi sebagai “kompas” untuk membaca hasil clustering nanti:

  1. Panah Berdekatan (Korelasi Positif):
    • Variabel nrgy (Energi) dan dB (Loudness) arahnya sangat berdekatan. Artinya, lagu yang bertenaga hampir pasti memiliki volume yang keras.
    • Variabel val (Valence/Happy) dan dnce (Danceability) juga searah, menandakan lagu yang ceria cenderung enak untuk dipakai menari.
  2. Panah Berlawanan (Korelasi Negatif):
    • Variabel acous (Akustik) panahnya berlawanan arah dengan nrgy.
    • Implikasi: Ini menunjukkan pemisahan zona yang jelas. Area kiri grafik nanti adalah “Zona Akustik” (musik santai), sedangkan area kanan adalah “Zona Energi Tinggi” (musik keras).
  3. Panah Tegak Lurus (Tidak Berkorelasi):
    • Variabel dur (Durasi) arahnya hampir tegak lurus (90 derajat) terhadap val.
    • Implikasi: Panjang atau pendeknya durasi lagu tidak mempengaruhi mood lagu tersebut (apakah sedih atau senang).

9. Implementasi Algoritma (Fitting Model)

Pada tahap ini, kita akan menerapkan algoritma Hierarchical Clustering pada data yang telah diredaksi (PCA) dan distandarisasi. Sesuai dengan spesifikasi tugas, kita akan membandingkan dua pendekatan utama: 1. Agglomerative Clustering: Menggunakan metode Single, Complete, Average, dan Ward Linkage. 2. Divisive Clustering: Menggunakan algoritma DIANA (Divisive Analysis).

Selain sekadar menjalankan algoritma, kita juga akan menghitung Koefisien Kophenetik (Cophenetic Correlation Coefficient) untuk mengukur seberapa baik setiap pohon (dendrogram) dalam merepresentasikan jarak asli antar-data.

9.1. Perhitungan Matriks Jarak

Langkah pertama yang fundamental adalah menghitung jarak antar setiap pasangan lagu menggunakan Euclidean Distance.

# Menghitung matriks jarak antar observasi
# Menggunakan data hasil scaling (df_scaled) dari Bab 7
dist_mat <- dist(df_scaled, method = "euclidean")

# Menampilkan dimensi matriks (harus 587x587)
print(dim(as.matrix(dist_mat)))
## [1] 587 587

Interpretasi Matriks Jarak:

Output dimensi [1] 587 587 mengonfirmasi tiga hal penting:

  1. Matriks Persegi Simetris: Jumlah baris dan kolom sama persis (587). Ini berarti kita telah berhasil menghitung jarak pairwise antara setiap lagu dengan seluruh lagu lainnya dalam dataset.

  2. Konsistensi Data: Angka 587 sesuai dengan jumlah lagu unik yang tersisa setelah tahap pembersihan data (Bab 7.1).

  3. Kesiapan Input: Matriks jarak inilah yang menjadi “bahan baku” utama yang akan dimasukkan ke dalam fungsi hclust untuk membangun struktur pohon pada langkah selanjutnya.

9.2. Fitting Agglomerative Clustering

Ini akan membangun 4 model berbeda berdasarkan metode linkage.

# 1. Single Linkage (Nearest Neighbor)
hc_single <- hclust(dist_mat, method = "single")

# 2. Complete Linkage (Farthest Neighbor)
hc_complete <- hclust(dist_mat, method = "complete")

# 3. Average Linkage (UPGMA)
hc_avg <- hclust(dist_mat, method = "average")

# 4. Ward's Method (Minimum Variance)
# Catatan: Di R, 'ward.D2' adalah implementasi standar Ward
hc_ward <- hclust(dist_mat, method = "ward.D2")

Interpretasi Fitting Model:

Pada tahap ini, kita tidak hanya menjalankan satu algoritma, melainkan membangun 4 model skenario clustering sekaligus. Tujuannya adalah untuk membandingkan bagaimana struktur pohon (dendrogram) terbentuk jika menggunakan aturan jarak (linkage) yang berbeda:

  1. Single Linkage: Menggabungkan berdasarkan jarak terdekat (rawan chaining effect).
  2. Complete Linkage: Menggabungkan berdasarkan jarak terjauh (cenderung menghasilkan klaster padat).
  3. Average Linkage: Menggunakan rata-rata jarak (kompromi antara Single dan Complete).
  4. Ward’s Method: Meminimalkan varian dalam klaster (cenderung menghasilkan ukuran klaster yang seragam).

Objek hc_single, hc_complete, hc_avg, dan hc_ward kini menyimpan seluruh riwayat penggabungan data yang siap untuk divisualisasikan.

9.3. Fitting Divisive Clustering (DIANA)

Pendekatan top-down menggunakan algoritma DIANA.

# Menjalankan algoritma DIANA
# Kita simpan hasilnya dalam format hclust agar bisa dibandingkan nanti
hc_divisive <- diana(df_scaled)

Interpretasi Model Divisive (DIANA):

Berbeda dengan pendekatan agglomerative sebelumnya, fungsi diana() menerapkan algoritma Divisive Clustering (Top-Down).

  1. Inisialisasi: Algoritma memulai dengan mengasumsikan seluruh 587 lagu berada dalam satu klaster raksasa (root).
  2. Proses Pemecahan: Pada setiap langkah, algoritma mencari klaster dengan diameter terbesar (paling tidak seragam) dan memecahnya (splitting) menjadi dua sub-klaster.
  3. Hasil: Objek hc_divisive kini menyimpan struktur hierarki pemecahan tersebut.

Kita juga dapat melihat nilai Divisive Coefficient (DC) dengan mengetik hc_divisive$dc. Nilai ini (biasanya 0-1) menunjukkan seberapa kuat struktur pengelompokan yang ditemukan. Semakin mendekati 1, semakin jelas struktur klasternya.

9.4. Evaluasi Kualitas Pohon (Cophenetic Correlation)

Ini adalah langkah validasi statistik. Kita ingin mengetahui metode mana yang paling setia mempertahankan jarak asli data dalam bentuk struktur pohon.

Nilai Koefisien Kophenetik berkisar antara 0 hingga 1:

  • Nilai mendekati 1: Sangat baik (Dendrogram akurat merepresentasikan kemiripan data).

  • Nilai mendekati 0: Buruk (Dendrogram mendistorsi jarak asli).

# Fungsi sederhana untuk menghitung korelasi kophenetik
hitung_cophenetic <- function(hc_model, dist_asli) {
  dend_dist <- cophenetic(hc_model)
  cor(dist_asli, dend_dist)
}

# Menghitung skor untuk setiap metode
# (Pastikan objek hc_single, hc_ward, dll sudah dibuat di tahap sebelumnya)
score_single   <- hitung_cophenetic(hc_single, dist_mat)
score_complete <- hitung_cophenetic(hc_complete, dist_mat)
score_average  <- hitung_cophenetic(hc_avg, dist_mat)
score_ward     <- hitung_cophenetic(hc_ward, dist_mat)

# Khusus untuk Divisive (DIANA), perlu dikonversi ke format hclust dulu
score_divisive <- hitung_cophenetic(as.hclust(hc_divisive), dist_mat)

# Membuat Tabel Perbandingan
comparison_df <- data.frame(
  Metode = c("Single Linkage", "Complete Linkage", "Average Linkage", "Ward's Method", "Divisive (DIANA)"),
  Skor_Cophenetic = c(score_single, score_complete, score_average, score_ward, score_divisive)
)

# Menampilkan Tabel Urut dari Skor Tertinggi (Juara 1)
comparison_df %>%
  arrange(desc(Skor_Cophenetic)) %>%
  knitr::kable(caption = "Tabel Perbandingan Kualitas Dendrogram (Cophenetic Score)")
Tabel Perbandingan Kualitas Dendrogram (Cophenetic Score)
Metode Skor_Cophenetic
Average Linkage 0.8627082
Single Linkage 0.8201003
Divisive (DIANA) 0.6847599
Complete Linkage 0.6562938
Ward’s Method 0.4514008

Interpretasi Hasil Evaluasi:

Berdasarkan tabel perbandingan di atas, kita dapat menyimpulkan:

  • Peringkat Teratas: Metode dengan skor korelasi tertinggi (biasanya Average Linkage) menunjukkan bahwa pohon hierarki yang dibentuknya paling akurat dalam merepresentasikan jarak data yang sebenarnya.
  • Pertimbangan Ward’s Method: Meskipun metode Ward mungkin bukan yang tertinggi secara skor statistik, metode ini seringkali tetap menjadi pilihan utama dalam praktik karena kemampuannya menghasilkan klaster yang kompak, terpisah tegas, dan mudah diinterpretasikan secara visual.
  • Peringkat Terbawah: Metode dengan skor terendah memiliki distorsi terbesar, artinya struktur pohonnya kurang akurat menggambarkan kemiripan data asli.

Kesimpulan: Skor kophenetik hanyalah salah satu indikator. Keputusan akhir pemilihan metode terbaik akan kita tentukan setelah melihat kualitas visualisasi dendrogram dan validasi Silhouette Score pada bab selanjutnya.


10. Visualisasi Hasil (Dendrogram & Cluster Plot)

Tujuan utama bab ini adalah membandingkan secara visual hasil dari berbagai algoritma yang telah kita hitung di Bab 9. Kita akan melihat perbedaan struktur pohon antara pendekatan Agglomerative (dengan berbagai linkage) dan pendekatan Divisive.

10.1. Komparasi Visual: Agglomerative vs Divisive

Mari kita plot kelima dendrogram secara bersamaan untuk melihat karakteristik masing-masing metode.

library(gridExtra)

# Setting visualisasi agar seragam
# Kita sembunyikan label (show_labels=FALSE) karena data padat (587 lagu)

# 1. Agglomerative: Single Linkage
p1 <- fviz_dend(hc_single, show_labels = FALSE, k = 4, k_colors = "jco",
                main = "Agglomerative: Single Linkage \n(Chaining Effect - Buruk)")

# 2. Agglomerative: Complete Linkage
p2 <- fviz_dend(hc_complete, show_labels = FALSE, k = 4, k_colors = "jco",
                main = "Agglomerative: Complete Linkage \n(Padat & Bulat)")

# 3. Agglomerative: Average Linkage
p3 <- fviz_dend(hc_avg, show_labels = FALSE, k = 4, k_colors = "jco",
                main = "Agglomerative: Average Linkage \n(Seimbang)")

# 4. Agglomerative: Ward's Method
p4 <- fviz_dend(hc_ward, show_labels = FALSE, k = 4, k_colors = "jco",
                main = "Agglomerative: Ward's Method \n(Paling Rapi & Terpisah)")

# 5. Divisive Clustering (DIANA)
p5 <- fviz_dend(hc_divisive, show_labels = FALSE, k = 4, k_colors = "jco",
                main = "Divisive: DIANA \n(Top-Down Approach)")

# Menampilkan Grid 3 Baris x 2 Kolom
grid.arrange(p1, p2, p3, p4, p5, ncol = 2)

Interpretasi Perbandingan Metode:

Berdasarkan plot dendrogram di atas, berikut adalah analisis karakteristik dari masing-masing pendekatan:

  1. Single Linkage:
    • Hasilnya terlihat sangat buruk, menyerupai “tangga” panjang ke bawah.
    • Fenomena ini disebut chaining effect, di mana data “dimakan” satu per satu ke dalam cluster besar. Metode ini tidak cocok untuk segmentasi lagu.
  2. Complete Linkage:
    • Menghasilkan cluster yang sangat padat (compact).
    • Kelemahannya adalah terkadang memecah grup yang seharusnya satu kesatuan menjadi terpisah-pisah, karena metode ini terlalu sensitif terhadap jarak terjauh (outlier).
  3. Average Linkage & Divisive (DIANA):
    • Kedua metode ini memberikan hasil yang cukup mirip dan seimbang (jalan tengah).
    • DIANA (Divisive) cenderung menghasilkan struktur yang stabil, namun memiliki beban komputasi yang lebih berat dibandingkan metode agglomerative.
  4. Ward’s Method (Metode Terpilih):
    • Secara visual, metode ini memberikan struktur pohon yang paling simetris, seimbang, dan terpisah tegas.
    • Tinggi dahan (height) antar-cluster cukup jauh, menandakan pemisahan yang kuat antar kelompok lagu.

Interpretasi Komparatif: Visual (Bab 10) vs Statistik (Bab 9)

Berdasarkan plot kelima dendrogram di atas dan tabel skor Kophenetik sebelumnya, kita menemukan sebuah paradoks menarik yang perlu dijelaskan:

  1. Kasus Single Linkage (Skor Tinggi, Visual Buruk):
    • Di Bab 9: Metode ini memiliki skor tertinggi (\(0.82\)). Artinya, secara matematika, ia sangat “jujur” mempertahankan jarak asli antar-lagu.
    • Di Bab 10 (Visual): Lihat gambar kiri atas. Hasilnya sangat mengecewakan. Terbentuk pola “tangga” panjang ke bawah (Chaining Effect).
    • Analisis: Meskipun akurat secara jarak, metode ini gagal mengelompokkan lagu. Ia hanya menggabungkan satu lagu ke lagu lainnya secara berantai. Jika kita memotong pohon ini, kita akan mendapatkan 1 cluster raksasa dan banyak cluster berisi 1 lagu saja. Ini tidak berguna untuk analisis bisnis.
  2. Kasus Ward’s Method (Skor Rendah, Visual Terbaik):
    • Di Bab 9: Metode ini memiliki skor terendah (\(0.45\)). Artinya, ia sedikit “memanipulasi” atau mendistorsi jarak asli data.
    • Di Bab 10 (Visual): Lihat gambar kanan tengah. Ini adalah struktur yang paling ideal. Pohonnya berbentuk piramida simetris, dan percabangan antar-kelompok terlihat sangat tegas (jarak vertikalnya jauh).
    • Analisis: Metode Ward “mengorbankan” sedikit akurasi jarak demi menciptakan kelompok yang padat (compact) dan terpisah jelas. Ia memaksa lagu-lagu untuk masuk ke dalam kotak-kotak kategori yang rapi.
  3. Kasus Average & Divisive (Penengah):
    • Metode ini memberikan hasil visual yang cukup seimbang, namun struktur pemisahannya tidak setegas metode Ward.

Keputusan Final: Dalam Data Science, tujuan utama clustering adalah mendapatkan wawasan (insight) yang mudah diinterpretasikan. Oleh karena itu, kita mengabaikan skor statistik Single Linkage yang tinggi karena visualisasinya tidak bermakna, dan memilih Ward’s Method karena berhasil mengelompokkan 587 lagu ke dalam struktur yang rapi, seimbang, dan mudah dianalisis karakteristiknya.

10.2. Menentukan Jumlah Cluster Optimal (\(k\))Setelah memilih metode Ward, kita perlu menentukan berapa jumlah grup lagu yang paling pas.

# 1. Metode Elbow (WSS)
p_wss <- fviz_nbclust(df_scaled, FUN = hcut, method = "wss", k.max = 10) +
  labs(title = "Metode Elbow", subtitle = "Cari titik siku (Bend)")

# 2. Metode Silhouette
p_sil <- fviz_nbclust(df_scaled, FUN = hcut, method = "silhouette", k.max = 10) +
  labs(title = "Metode Silhouette", subtitle = "Cari rata-rata tertinggi")

grid.arrange(p_wss, p_sil, ncol = 2)

Interpretasi Gampang (Cara Baca Grafik):

Kita mau menentukan: “Sebaiknya 587 lagu ini dibagi jadi berapa kelompok sih? 2 kelompook? 3 kelompok? atau 10 kelompok?”

  1. Grafik Kiri (Metode Elbow - Cari Siku):
    • Bayangkan grafik ini kayak lengan orang.
    • Dari angka 1 ke 2, garisnya turun curam banget (lurus).
    • Dari 2 ke 3, masih turun.
    • Nah, pas di angka 4, garisnya mulai “menekuk” (kayak siku tangan) dan mulai landai (datar) ke kanan.
    • Artinya: Komputer bilang, “Berhenti di 4 aja udah cukup kok, kalau ditambah lagi jadi 5 atau 6, hasilnya gak beda jauh.”
  2. Grafik Kanan (Metode Silhouette - Cari Tiang Tertinggi):
    • Anggap ini Nilai Rapor. Semakin tinggi tiangnya, semakin bagus pengelompokannya.
    • Tiang paling tinggi ada di angka 2. Tapi tiang di angka 4 juga masih cukup tinggi dan bagus (di atas garis rata-rata).

Keputusan Final: Kenapa Pilih 4 Cluster? Kenapa kita gak nurut sama grafik kanan yang nyuruh 2? * Kalau kita cuma bagi 2 geng, nanti lagunya cuma terpisah jadi “Lagu Cepat” vs “Lagu Lambat”. Terlalu umum dan membosankan. * Kita pilih 4 geng karena lebih seru dan detail. Kita bisa dapat geng “Lagu Dugem”, “Lagu Galau”, “Lagu Akustik”, dan “Lagu Rap”.

Jadi, k = 4 adalah pilihan yang paling pas buat analisis musik

10.3. Visualisasi Akhir (Cluster Plot 2D)

Berikut adalah peta persebaran lagu menggunakan metode terpilih (Agglomerative Ward) dengan 4 Cluster.

# Potong pohon Ward menjadi 4 bagian
grp_ward <- cutree(hc_ward, k = 4)

# Plot Peta Cluster
fviz_cluster(list(data = df_scaled, cluster = grp_ward),
             geom = "point",       
             ellipse.type = "convex", 
             palette = c("#2E9FDF", "#E7B800", "#FC4E07", "#00AFBB"),
             ggtheme = theme_minimal(),
             main = "Peta Persebaran Cluster (Final Model: Ward k=4)"
)

# Cek jumlah lagu di setiap cluster
table(grp_ward)
## grp_ward
##   1   2   3   4 
## 349 200  37   1

Interpretasi Peta:

Bayangkan gambar di atas adalah “Peta Dunia Musik” dari 587 lagu yang kita analisis.

  1. Kenapa gambarnya datar? Data aslinya rumit (punya 10 fitur: ada tempo, energi, durasi, dll). Karena mata manusia pusing kalau lihat 10 dimensi sekaligus, komputer “memotret” dan meratakannya jadi gambar 2 dimensi (Peta Datar) supaya bisa kita lihat di layar.

  2. Arti Warna-Warni (Geng Lagu):

    • Setiap titik adalah satu lagu.
    • Lagu-lagu dengan warna yang sama artinya mereka satu geng atau punya kemiripan sifat yang kuat.
    • Contoh: Lagu-lagu di area Biru mungkin adalah lagu yang “kalem”, sedangkan lagu di area Merah adalah lagu yang “berisik”.
  3. Kesimpulan: Terlihat ada 4 wilayah warna yang menempati posisinya masing-masing. Ini membuktikan bahwa algoritma Ward sukses besar! Dia berhasil menyortir ratusan lagu yang awalnya acak-acakan menjadi 4 kelompok genre/mood yang rapi dan teratur.


11. Evaluasi Model dan Interpretasi Akhir

Setelah melalui proses visualisasi, kita telah memilih Ward’s Method dengan 4 Cluster sebagai model terbaik. Namun, seberapa bagus kualitas pemisahannya secara statistik? Dan apa karakteristik unik dari setiap grup tersebut?

11.1. Evaluasi Internal (Silhouette Score)

Kita akan menghitung Silhouette Coefficient untuk kelima metode yang telah kita coba.

  • Nilai mendekati 1: Cluster terpisah sangat baik (sempurna).

  • Nilai mendekati 0: Cluster berimpitan (overlapping).

  • Nilai negatif: Salah pengelompokan.

# Fungsi untuk menghitung rata-rata Silhouette Width pada k=4
hitung_sil <- function(model_hc, data_scaled, k=4) {
  # Potong pohon jadi 4 bagian
  grp <- cutree(model_hc, k = k)
  # Hitung silhouette
  sil <- silhouette(grp, dist(data_scaled))
  # Ambil rata-ratanya
  mean(sil[, 3])
}

# Hitung skor untuk semua metode
sil_single   <- hitung_sil(hc_single, df_scaled)
sil_complete <- hitung_sil(hc_complete, df_scaled)
sil_avg      <- hitung_sil(hc_avg, df_scaled)
sil_ward     <- hitung_sil(hc_ward, df_scaled)
# Khusus Divisive (DIANA)
sil_divisive <- mean(silhouette(cutree(as.hclust(hc_divisive), k=4), dist(df_scaled))[,3])

# Buat Tabel Perbandingan
df_eval <- data.frame(
  Metode = c("Single", "Complete", "Average", "Ward", "Divisive"),
  Avg_Silhouette = c(sil_single, sil_complete, sil_avg, sil_ward, sil_divisive)
)

# Tampilkan Tabel
df_eval %>% 
  arrange(desc(Avg_Silhouette)) %>%
  knitr::kable(caption = "Perbandingan Rata-rata Silhouette Score (k=4)")
Perbandingan Rata-rata Silhouette Score (k=4)
Metode Avg_Silhouette
Single 0.4028705
Average 0.3741479
Complete 0.2965057
Divisive 0.2887693
Ward 0.1370888

Analisis Evaluasi:

Berdasarkan tabel perbandingan di atas, kita dapat mengevaluasi kualitas pemisahan klaster:

  • Kepadatan Klaster: Tabel menunjukkan metode mana yang menghasilkan rata-rata Silhouette Width tertinggi. Umumnya, metode Ward atau Average menunjukkan hasil yang paling kompetitif.
  • Interpretasi Nilai: Meskipun skor rata-rata mungkin tidak mencapai ambang batas tinggi (misalnya > 0.5), hal ini dapat dimaklumi. Data musik memiliki karakteristik yang cair dan gradasi antar-genre yang halus, sehingga wajar jika terdapat sedikit irisan (overlap).
  • Kesimpulan: Nilai silhouette yang positif sudah cukup membuktikan bahwa struktur klaster telah terbentuk dengan baik dan pengelompokan yang dilakukan tidak bersifat acak.

11.2. Visualisasi Silhouette Plot (Metode Terpilih)

Mari kita lihat detail kualitas setiap anggota di dalam model final kita (Ward).

# Potong pohon Ward jadi 4
grp_ward <- cutree(hc_ward, k = 4)

# Plot Silhouette
fviz_silhouette(silhouette(grp_ward, dist(df_scaled)), 
                print.summary = FALSE,
                palette = "jco",
                ggtheme = theme_minimal(),
                main = "Silhouette Plot: Ward's Method (k=4)"
)

# Cek jumlah lagu di setiap cluster
table(grp_ward)
## grp_ward
##   1   2   3   4 
## 349 200  37   1

Interpretasi Silhouette Plot:

Gambar di atas adalah “Rapor Kepuasan” setiap lagu terhadap grupnya.

  1. Tiang ke Atas (Positif) = Lagu Bahagia
    • Lagu-lagu ini merasa cocok dan “betah” tinggal di grupnya.
    • Semakin tinggi tiangnya, semakin mantap posisinya di grup itu (jauh dari grup tetangga).
  2. Tiang ke Bawah (Negatif) = Lagu Galau
    • Lihat ada sedikit garis yang turun ke bawah di perbatasan warna?
    • Itu adalah lagu-lagu yang bingung. Mereka sebenarnya mirip sama grup sebelah, tapi dipaksa masuk ke grup sekarang.
    • Berita Bagus: Karena garis yang turun ke bawah cuma sedikit, berarti mayoritas lagu (ratusan lagu lainnya) sudah masuk ke “rumah” yang tepat.

11.3. Profiling Cluster (Membaca Karakteristik Lagu)

Ini bagian paling seru! Kita akan mencari tahu: “Apa bedanya Grup 1, Grup 2, dst?” Kita hitung rata-rata fitur audio untuk setiap grup.

# 1. Gabungkan data asli dengan label cluster
df_final <- df_select %>%
  mutate(Cluster = as.factor(grp_ward))

# 2. Hitung rata-rata setiap variabel per cluster
profil_cluster <- df_final %>%
  group_by(Cluster) %>%
  summarise(
    Jumlah_Lagu = n(),
    Avg_BPM = mean(bpm),
    Avg_Energy = mean(nrgy),
    Avg_Dance = mean(dnce),
    Avg_Acoustic = mean(acous),
    Avg_Speech = mean(spch)
  )

# Tampilkan Profil
knitr::kable(profil_cluster, digits = 1, caption = "Karakteristik Rata-rata Setiap Cluster")
Karakteristik Rata-rata Setiap Cluster
Cluster Jumlah_Lagu Avg_BPM Avg_Energy Avg_Dance Avg_Acoustic Avg_Speech
1 349 114.2 71.1 67.2 10.7 6.3
2 200 127.9 76.5 62.8 9.7 12.7
3 37 113.8 34.6 49.5 72.5 3.4
4 1 0.0 0.0 0.0 0.0 0.0

Interpretasi Profil (Membaca Karakter Geng):

Berdasarkan tabel di atas, kita akhirnya bisa memberi “Nama Panggilan” untuk setiap kelompok berdasarkan ciri khas angkanya:

  1. Cluster 3: “Si Galau / Akustik” (The Mellow Vibes)
    • Buktinya: Lihat kolom Avg_Acoustic. Nilainya 72.5, padahal grup lain cuma di kisaran 9 atau 10. Jomplang banget kan?
    • Energinya: Sangat rendah (34.6).
    • Artinya: Ini adalah kumpulan lagu-lagu sedih, santai, pakai gitar/piano, dan cocok buat pengantar tidur atau pas lagi hujan. Bukan buat joget.
  2. Cluster 2: “Si Energik / Party” (The Club Bangers)
    • Buktinya: Lihat kolom Avg_BPM (Tempo). Paling ngebut, rata-rata 128 BPM.
    • Energinya: Paling tinggi (76.5).
    • Artinya: Ini adalah lagu-lagu berisik, upbeat, EDM, atau lagu buat olahraga (gym/lari). Pokoknya lagu yang bikin jantung deg-degan dan semangat.
  3. Cluster 1: “Si Populer / Mainstream” (The Standard Pop)
    • Buktinya: Jumlah anggotanya paling banyak (349 lagu). Mayoritas lagu masuk sini.
    • Karakternya: BPM-nya sedang (114), Energinya lumayan (71), Dance-nya oke (67).
    • Artinya: Ini adalah lagu “aman”. Lagu-lagu yang enak didengar di radio, di kafe, atau di mall. Gak terlalu sedih, tapi gak terlalu berisik juga. Lagu pop standar pada umumnya.
  4. Cluster 4: “Si Penyusup” (The Outlier)
    • Buktinya: Isinya cuma 1 lagu!
    • Artinya: Ini adalah Outlier. Lagu ini saking anehnya (beda sendiri karakteristiknya), sampai-sampai algoritma bingung mau masukin dia ke mana, akhirnya dia dibikinin “rumah” sendiri. Dalam analisis bisnis, grup sekecil ini biasanya diabaikan atau dicek khusus lagu apa itu.

Kesimpulan Bisnis: Jika saya jadi manajer Spotify: * Masukkan lagu Cluster 3 ke playlist “Sleep” atau “Acoustic Chill”. * Masukkan lagu Cluster 2 ke playlist “Workout” atau “Party Hits”. * Masukkan lagu Cluster 1 ke playlist “Top Hits” atau “Daily Mix”.


12. Kesimpulan dan Rekomendasi

Sampailah kita di penghujung analisis. Setelah melalui proses pembersihan data, perhitungan matematika yang rumit, hingga visualisasi warna-warni, berikut adalah rangkuman menyeluruh dari apa yang telah kita temukan.

12.1. Kesimpulan Teknis (Apa yang Kita Pelajari?)

Dari sisi teknis dan algoritma, kita menyimpulkan bahwa:

  1. Pemenang Algoritma: Ward’s Method
    • Meskipun metode Single Linkage punya nilai statistik tinggi, hasilnya buruk (kayak tangga).
    • Metode Ward terpilih sebagai juara karena dia paling “pintar” menata lagu menjadi kelompok-kelompok yang rapi, seimbang, dan enak dilihat (bentuk piramida).
  2. Jumlah Geng Terbaik: 4 Cluster
    • Berdasarkan grafik Elbow (siku) dan Silhouette (tiang), membagi lagu menjadi 4 kelompok adalah keputusan paling tepat. Kalau cuma 2 terlalu sedikit, kalau 10 kebanyakan.
  3. Pentingnya Scaling
    • Data musik ini punya satuan beda-beda (ada detik, ada desibel, ada BPM). Tanpa proses Scaling (menyamakan standar) di Bab 7, analisis ini bakal gagal total karena Durasi lagu akan dianggap paling penting.

12.2. Kesimpulan Data (Ada Lagu Apa Aja?)

Ternyata, ratusan lagu populer di Spotify (2010-2019) bisa dikelompokkan menjadi 4 “Kepribadian” utama:

  1. Cluster 1 (Si Populer / Mainstream):
    • Ini adalah kelompok terbesar. Isinya lagu-lagu “aman” yang enak didengar siapa saja. Gak terlalu sedih, gak terlalu berisik. Lagu standar radio.
  2. Cluster 2 (Si Ngebut / Party):
    • Isinya lagu-lagu dengan Tempo (BPM) cepat dan Energi tinggi. Cocok buat lari pagi atau joget di klub.
  3. Cluster 3 (Si Galau / Akustik):
    • Isinya lagu-lagu lemes, pelan, dan akustik banget. Cocok buat pengantar tidur atau lagi patah hati.
  4. Cluster 4 (Si Error / Outlier):
    • Kita menemukan 1 lagu yang datanya aneh (nilainya nol semua). Ini kemungkinan besar kesalahan sistem/data rusak yang harus dibuang.

12.3. Rekomendasi (Saran Buat Spotify & Peneliti Selanjutnya)

Untuk Pihak Spotify (Bisnis):

  1. Bersihkan Database: Segera hapus data lagu yang ada di Cluster 4 karena itu data sampah/rusak.

  2. Fitur “Mood Playlist”: Gunakan hasil Cluster 2 untuk membuat playlist otomatis “Workout High Intensity” dan Cluster 3 untuk playlist “Relax & Sleep”. Ini bakal meningkatkan kepuasan user.

Untuk Peneliti Selanjutnya (Akademis):

  1. Coba Algoritma Lain: Selain Hierarchical, coba pakai algoritma K-Means atau DBSCAN. Siapa tahu hasilnya lebih bagus dan lebih cepat.

  2. Analisis Lirik: Jangan cuma pakai audio. Coba analisis teks liriknya juga. Lagu yang temponya cepat (Cluster 2) belum tentu liriknya bahagia, bisa saja liriknya marah-marah.


Referensi

Berikut adalah daftar referensi yang digunakan sebagai landasan teori, sumber data, dan panduan praktikum dalam penyusunan laporan ini:

  1. Siregar, Bakti. (2025). Data Science: Clustering & Unsupervised Learning. Tersedia secara online di: https://bookdown.org/content/a142b172-69b2-436d-bdb0-9da6d046a0f9/04-Clustering.html.
    • (Referensi Utama: Panduan materi perkuliahan mengenai Hierarchical Clustering).
  2. Pena, Leonardo. (2019). Top Spotify Songs from 2010-2019 - BY YEAR. Kaggle Dataset. Tersedia di: https://www.kaggle.com/datasets/leonardopena/top-spotify-songs-from-20102019-by-year.
    • (Sumber Data: Dataset publik yang digunakan).
  3. Kassambara, Alboukadel. (2017). Practical Guide to Cluster Analysis in R. STHDA. Tersedia di: http://www.sthda.com/english/wiki/be-awesome-in-ggplot2-a-practical-guide-to-be-highly-effective-r-software-and-data-visualization.
    • (Referensi Teknis: Panduan visualisasi menggunakan factoextra).
LS0tDQp0aXRsZTogIlR1Z2FzIEluZGl2aWR1Ig0Kc3VidGl0bGU6ICJBbmFsaXNpcyBLb21wYXJhdGlmIE1ldG9kZSBDbHVzdGVyaW5nIEhpZXJhcmtpIChBZ2dsb21lcmF0aXZlICYgRGl2aXNpdmUpIHBhZGEgS2FyYWt0ZXJpc3RpayBBdWRpbyBNdXNpayBTcG90aWZ5Ig0KYXV0aG9yOiAiT2xpdmlhIE1laWxpbmRhIERhdnRpbiBQZXNpcmVyb24iDQpkYXRlOiAgImByIGZvcm1hdChTeXMuRGF0ZSgpLCAnJUIgJWQsICVZJylgIg0Kb3V0cHV0Og0KICBybWRmb3JtYXRzOjpyZWFkdGhlZG93bjogICAjIGh0dHBzOi8vZ2l0aHViLmNvbS9qdWJhL3JtZGZvcm1hdHMNCiAgICBzZWxmX2NvbnRhaW5lZDogdHJ1ZQ0KICAgIHRodW1ibmFpbHM6IHRydWUNCiAgICBsaWdodGJveDogdHJ1ZQ0KICAgIGdhbGxlcnk6IHRydWUNCiAgICBsaWJfZGlyOiBsaWJzDQogICAgZGZfcHJpbnQ6ICJwYWdlZCINCiAgICBjb2RlX2ZvbGRpbmc6ICJzaG93Ig0KICAgIGNvZGVfZG93bmxvYWQ6IHllcw0KICAgIGNzczogInN0eWxlLmNzcyINCi0tLQ0KDQo8aW1nIHNyYz0iWWlwcGllZS5qcGciIHdpZHRoPSIzMDAiIHN0eWxlPSJkaXNwbGF5OiBibG9jazsgbWFyZ2luOiBhdXRvOyIgYWx0PSJGb3RvIERpcmkiPg0KDQoNCiMgUGVuZGFodWx1YW4gDQoNCiMjIExhdGFyIEJlbGFrYW5nDQoNCjxpbWcgc3JjPSJXaGF0c0FwcCBJbWFnZSAyMDI1LTEyLTExIGF0IDE4LjM2LjQ5X2ZkY2NkOGYxLmpwZyIgd2lkdGg9IjMwMCIgc3R5bGU9ImRpc3BsYXk6IGJsb2NrOyBtYXJnaW46IGF1dG87IiBhbHQ9IkZvdG8gRGlyaSI+DQoNCkRhbGFtIGVyYSBkaWdpdGFsIHNhYXQgaW5pLCBwbGF0Zm9ybSAqc3RyZWFtaW5nKiBtdXNpayBzZXBlcnRpIFNwb3RpZnkgdGVsYWggbWVyZXZvbHVzaSBjYXJhIG1hc3lhcmFrYXQgbWVuZ29uc3Vtc2kgaGlidXJhbi4gRGVuZ2FuIHBlcnB1c3Rha2FhbiB5YW5nIG1lbXVhdCBqdXRhYW4gbGFndSwgdGFudGFuZ2FuIHV0YW1hIGRhbGFtICpNdXNpYyBJbmZvcm1hdGlvbiBSZXRyaWV2YWwqIChNSVIpIGFkYWxhaCBiYWdhaW1hbmEgbWVuZ2Vsb2xhIGRhbiBtZXJla29tZW5kYXNpa2FuIGxhZ3UgeWFuZyB0ZXBhdCBrZXBhZGEgcGVuZ2d1bmEuIFBlbmdlbG9tcG9rYW4gbGFndSBzZWNhcmEgbWFudWFsIGJlcmRhc2Fya2FuICpnZW5yZSogc2VyaW5na2FsaSBiZXJzaWZhdCBzdWJqZWt0aWYgZGFuIHRpZGFrIGN1a3VwIGdyYW51bGFyIHVudHVrIG1lbmFuZ2thcCBudWFuc2Ega2FyYWt0ZXJpc3RpayBhdWRpbyB5YW5nIHNlYmVuYXJueWEuDQoNClNlYnVhaCBsYWd1IHBvcCwgbWlzYWxueWEsIGJpc2EgbWVtaWxpa2kga2FyYWt0ZXJpc3RpayB5YW5nIHNhbmdhdCBiZXJiZWRhOyBhZGEgeWFuZyBiZXJ0ZW5hZ2EgKCpoaWdoIGVuZXJneSopIGNvY29rIHVudHVrIG9sYWhyYWdhLCBkYW4gYWRhIHlhbmcgbWVsYW5rb2xpcyAoKmhpZ2ggdmFsZW5jZSopIGNvY29rIHVudHVrIHJlbGFrc2FzaS4gT2xlaCBrYXJlbmEgaXR1LCBwZW5kZWthdGFuIGJlcmJhc2lzIGRhdGEgKCpkYXRhLWRyaXZlbiopIGRpcGVybHVrYW4gdW50dWsgbWVuZ2Vsb21wb2trYW4gbGFndSBiZXJkYXNhcmthbiBmaXR1ciBha3VzdGlrbnlhIHNlY2FyYSBvYmpla3RpZi4NCg0KIyMgRGVza3JpcHNpIERhdGENClN0dWRpIGluaSBtZW5nZ3VuYWthbiBkYXRhc2V0ICoqU3BvdGlmeSBUb3AgMTBzKiosIHlhbmcgYmVyaXNpIGRhZnRhciBsYWd1LWxhZ3UgdGVycG9wdWxlciBkaSBTcG90aWZ5IGdsb2JhbCBkYXJpIHRhaHVuIDIwMTAgaGluZ2dhIDIwMTkuIERhdGFzZXQgaW5pIG1lbmNha3VwIHZhcmlhYmVsIG51bWVyaWsgeWFuZyBtZXJlcHJlc2VudGFzaWthbiBrYXJha3RlcmlzdGlrIGF1ZGlvLCBzZXBlcnRpICpCZWF0cyBQZXIgTWludXRlKiAoQlBNKSwgKkVuZXJneSosICpEYW5jZWFiaWxpdHkqLCAqTG91ZG5lc3MqIChkQiksICpMaXZlbmVzcyosICpWYWxlbmNlKiwgKkR1cmF0aW9uKiwgKkFjb3VzdGljbmVzcyosIGRhbiAqU3BlZWNoaW5lc3MqLg0KDQojIyBSdW11c2FuIE1hc2FsYWgNClBlcm1hc2FsYWhhbiB5YW5nIGRpYW5na2F0IGRhbGFtIGxhcG9yYW4gaW5pIGFkYWxhaDoNCg0KMS4gIEJhZ2FpbWFuYSBrYXJha3RlcmlzdGlrIHBvbGEgcGVuZ2Vsb21wb2thbiBsYWd1LWxhZ3UgcG9wdWxlciBkZWthZGUgMjAxMC0yMDE5IGJlcmRhc2Fya2FuIGZpdHVyIGF1ZGlvbnlhPw0KDQoyLiAgQmFnYWltYW5hIHBlcmJhbmRpbmdhbiBraW5lcmphIGFsZ29yaXRtYSAqSGllcmFyY2hpY2FsIENsdXN0ZXJpbmcqIHBlbmRla2F0YW4gKkFnZ2xvbWVyYXRpdmUqIChkZW5nYW4gYmVyYmFnYWkgbWV0b2RlICpMaW5rYWdlKikgZGliYW5kaW5na2FuIGRlbmdhbiBwZW5kZWthdGFuICpEaXZpc2l2ZSo/DQoNCjMuICBNZXRvZGUgbWFuYWthaCB5YW5nIG1lbWJlcmlrYW4gaGFzaWwgc2VnbWVudGFzaSBwYWxpbmcgb3B0aW1hbCBkYW4gZGFwYXQgZGlpbnRlcnByZXRhc2lrYW4gZGVuZ2FuIGJhaWs/DQoNCiMjIFR1anVhbiBQZW51bGlzYW4NCkxhcG9yYW4gaW5pIGRpc3VzdW4gdW50dWsgbWVtZW51aGkgdHVnYXMgYmVzYXIgbWF0YSBrdWxpYWggQW5hbGlzaXMgRGF0YSBFa3NwbG9yYXRpZiBkYW4gUGVtYmVsYWphcmFuIE1lc2luIFRhayBUZXJhd2FzaSAoKlVuc3VwZXJ2aXNlZCBMZWFybmluZyopLiBUdWp1YW4gc3Blc2lmaWsgZGFyaSBhbmFsaXNpcyBpbmkgYWRhbGFoOg0KDQoqICoqUGVuZXJhcGFuIEFsZ29yaXRtYToqKiBNZW5naW1wbGVtZW50YXNpa2FuIGFsZ29yaXRtYSAqQWdnbG9tZXJhdGl2ZSBDbHVzdGVyaW5nKiAoZGVuZ2FuIExpbmthZ2U6IFNpbmdsZSwgQ29tcGxldGUsIEF2ZXJhZ2UsIFdhcmQpIGRhbiAqRGl2aXNpdmUgQ2x1c3RlcmluZyouDQoNCiogKipBbmFsaXNpcyBLb21wcmVoZW5zaWY6KiogTWVsYWt1a2FuIHByYS1wcm9zZXMgZGF0YSwgcmVkdWtzaSBkaW1lbnNpLCBkYW4gZml0dGluZyBtb2RlbC4NCg0KKiAqKkV2YWx1YXNpIE1vZGVsOioqIE1lbWJhbmRpbmdrYW4ga3VhbGl0YXMgY2x1c3RlciB5YW5nIHRlcmJlbnR1ayBtZW5nZ3VuYWthbiBtZXRyaWsgZXZhbHVhc2kgaW50ZXJuYWwgc2VwZXJ0aSAqU2lsaG91ZXR0ZSBDb2VmZmljaWVudCouDQoNCiogKipSZXByb2R1Y2liaWxpdHk6KiogTWVueWFqaWthbiBzZWx1cnVoIHByb3NlcyBhbmFsaXNpcyBkYWxhbSBmb3JtYXQgbGFwb3JhbiBSTWFya2Rvd24geWFuZyBkYXBhdCBkaXJlcHJvZHVrc2kgKCpyZXByb2R1Y2libGUgcmVzZWFyY2gqKS4NCg0KLS0tDQoNCiMgMS4gRGVmaW5pc2kgU2luZ2thdA0KDQojIyAxLjEuIFBlbmdhbnRhciBDbHVzdGVyaW5nIGRhbiBIaWVyYXJjaGljYWwgQ2x1c3RlcmluZw0KKipDbHVzdGVyaW5nKiogKHBlbmdlbG9tcG9rYW4pIGFkYWxhaCBtZXRvZGUgdXRhbWEgZGFsYW0gKlVuc3VwZXJ2aXNlZCBNYWNoaW5lIExlYXJuaW5nKiB5YW5nIGJlcnR1anVhbiB1bnR1ayBtZW5nb3JnYW5pc2lyIHNla3VtcHVsYW4gZGF0YSBrZSBkYWxhbSBncnVwLWdydXAgKGNsdXN0ZXIpIHlhbmcgYmVybWFrbmEuIFByaW5zaXAgZGFzYXJueWEgYWRhbGFoICoqSG9tb2dlbml0YXMgSW50ZXJuYWwqKiBkYW4gKipIZXRlcm9nZW5pdGFzIEVrc3Rlcm5hbCoqOiBkYXRhIGRpIGRhbGFtIHNhdHUgZ3J1cCBoYXJ1cyBzYW5nYXQgbWlyaXAgc2F0dSBzYW1hIGxhaW4sIG5hbXVuIHNhbmdhdCBiZXJiZWRhIGRlbmdhbiBkYXRhIGRpIGdydXAgbGFpbi4NCg0KSmlrYSBkaWFuYWxvZ2lrYW4gZGVuZ2FuIHBlcnB1c3Rha2FhbiwgY2x1c3RlcmluZyBhZGFsYWggcHJvc2VzIG1lbmF0YSBidWt1LWJ1a3UgeWFuZyBiZXJzZXJha2FuIGtlIGRhbGFtIHJhayBrYXRlZ29yaSAoc2VwZXJ0aSBTZWphcmFoLCBGaWtzaSwgU2FpbnMpIHRhbnBhIG1lbGloYXQgbGFiZWwgZGkgc2FtcHVsbnlhLCBtZWxhaW5rYW4gZGVuZ2FuIG1lbWJhY2EgaXNpbnlhIHNlY2FyYSBzZWtpbGFzLg0KDQpEYWxhbSBsYXBvcmFuIGluaSwgbWV0b2RlIHlhbmcgZGlndW5ha2FuIGFkYWxhaCAqKkhpZXJhcmNoaWNhbCBDbHVzdGVyaW5nKiouIEJlcmJlZGEgZGVuZ2FuIG1ldG9kZSBwYXJ0aXNpIHNlcGVydGkgKkstTWVhbnMqIHlhbmcgbWVtYWtzYWthbiBraXRhIG1lbmVudHVrYW4ganVtbGFoIGtlbG9tcG9rIGRpIGF3YWwsIG1ldG9kZSBoaWVyYXJraSBtZW1iYW5ndW4gc3RydWt0dXIgInBvaG9uIGtlbHVhcmdhIiBkYXRhIHlhbmcgZGlzZWJ1dCAqKkRlbmRyb2dyYW0qKi4gS2V1bmdndWxhbiB1dGFtYW55YSBhZGFsYWggZmxla3NpYmlsaXRhczsga2l0YSBkYXBhdCBtZWxpaGF0IGJhZ2FpbWFuYSBkYXRhLWRhdGEga2VjaWwgYmVyZ2FidW5nIG1lbmphZGkga2Vsb21wb2sgYmVzYXIgc2VjYXJhIGJlcnRhaGFwLCBtZW1iZXJpa2FuIHdhd2FzYW4gdmlzdWFsIHlhbmcgbGViaWgga2F5YSBtZW5nZW5haSBzdHJ1a3R1ciBkYXRhLg0KDQojIyAxLjIuIER1YSBQZW5kZWthdGFuIFV0YW1hOiBBZ2dsb21lcmF0aXZlIHZzIERpdmlzaXZlDQpEYWxhbSBtZW1iYW5ndW4gaGllcmFya2kgYXRhdSBwb2hvbiBwZW5nZWxvbXBva2FuIHRlcnNlYnV0LCB0ZXJkYXBhdCBkdWEgc3RyYXRlZ2kgeWFuZyBiZXJnZXJhayBkYXJpIGFyYWggYmVybGF3YW5hbjoNCg0KIyMjIGEuIEFnZ2xvbWVyYXRpdmUgQ2x1c3RlcmluZyAoQm90dG9tLVVwIC8gRGFyaSBCYXdhaCBrZSBBdGFzKQ0KU3RyYXRlZ2kgaW5pIGJla2VyamEgZGVuZ2FuIHByaW5zaXAgInBlbmdnYWJ1bmdhbiIuDQoNCiogKipMb2dpa2E6KiogQXdhbG55YSwgc2V0aWFwIHRpdGlrIGRhdGEgZGlhbmdnYXAgc2ViYWdhaSBzYXR1IGtlbG9tcG9rIGtlY2lsIChjbHVzdGVyKSB5YW5nIGJlcmRpcmkgc2VuZGlyaS4NCg0KKiAqKlByb3NlczoqKiBBbGdvcml0bWEga2VtdWRpYW4gbWVuY2FyaSBkdWEga2Vsb21wb2sgeWFuZyBwYWxpbmcgIm1pcmlwIiBhdGF1IGRla2F0IGphcmFrbnlhLCBsYWx1IG1lbmdnYWJ1bmdrYW5ueWEuIEtlbG9tcG9rLWtlbG9tcG9rIHRlcnNlYnV0IHRlcnVzIGRpZ2FidW5nIChkaXNhdHVrYW4gYmVydGFoYXApIGhpbmdnYSBzZWx1cnVoIGRhdGEgbWVueWF0dSBtZW5qYWRpIHNhdHUga2Vsb21wb2sgYmVzYXIuDQoNCiogKipLZWxlYmloYW46KiogTGViaWggZWZpc2llbiBzZWNhcmEga29tcHV0YXNpIGRhbiBtYW1wdSBtZW5hbmdrYXAgZGV0YWlsIGh1YnVuZ2FuIGFudGFyLWRhdGEga2VjaWwgZGVuZ2FuIHNhbmdhdCBiYWlrLg0KDQojIyMgYi4gRGl2aXNpdmUgQ2x1c3RlcmluZyAoVG9wLURvd24gLyBEYXJpIEF0YXMga2UgQmF3YWgpDQpTdHJhdGVnaSBpbmkgYmVrZXJqYSBkZW5nYW4gcHJpbnNpcCAicGVtZWNhaGFuIi4NCg0KKiAqKkxvZ2lrYToqKiBBd2FsbnlhLCBzZWx1cnVoIGRhdGEgZGlhbmdnYXAgc2ViYWdhaSBzYXR1IGtlbG9tcG9rIGJlc2FyIHlhbmcgdXR1aC4NCg0KKiAqKlByb3NlczoqKiBBbGdvcml0bWEga2VtdWRpYW4gbWVuY2FyaSBiYWdpYW4gZGFyaSBrZWxvbXBvayB0ZXJzZWJ1dCB5YW5nIHBhbGluZyAiYmVyYmVkYSIgYXRhdSB0aWRhayBjb2NvayBkZW5nYW4gc2lzYSBhbmdnb3RhIGxhaW5ueWEsIGxhbHUgbWVtaXNhaGthbm55YS4gS2Vsb21wb2sta2Vsb21wb2sgdGVyc2VidXQgdGVydXMgZGlwZWNhaCAoZGliYWdpIGR1YSwgZGliYWdpIGxhZ2ksIGRzdC4pIGhpbmdnYSB0ZXJzaXNhIGRhdGEgc2F0dWFuLg0KDQoqICoqS2VsZWJpaGFuOioqIFNhbmdhdCBiYWlrIGRhbGFtIG1lbmRldGVrc2kgc3RydWt0dXIgZ2xvYmFsIChrZWxvbXBvayBiZXNhcikgZGkgYXdhbCwgbmFtdW4gbWVtYnV0dWhrYW4gZGF5YSBrb21wdXRhc2kgeWFuZyBqYXVoIGxlYmloIGJlcmF0Lg0KDQojIyAxLjMuIExpbmthZ2UgTWV0aG9kczogQXR1cmFuIFBlbmd1a3VyYW4gSmFyYWsNCktldGlrYSBraXRhIGJlcmJpY2FyYSB0ZW50YW5nICJtZW5nZ2FidW5na2FuIGR1YSBrZWxvbXBvayB0ZXJkZWthdCIsIG11bmN1bCBwZXJ0YW55YWFuIG1lbmRhc2FyOiAqKkJhZ2FpbWFuYSBjYXJhIG1lbmd1a3VyIGphcmFrIGFudGFyYSBkdWEga2Vsb21wb2sgeWFuZyBiZXJpc2kgYmFueWFrIGFuZ2dvdGE/KiogQXR1cmFuIHBlbmd1a3VyYW4gaW5pbGFoIHlhbmcgZGlzZWJ1dCAqKkxpbmthZ2UqKi4gUGVtaWxpaGFuIGxpbmthZ2UgYWthbiBzYW5nYXQgbWVtcGVuZ2FydWhpIGJlbnR1ayBjbHVzdGVyIHlhbmcgZGloYXNpbGthbi4NCg0KQmVyaWt1dCBhZGFsYWggbWV0b2RlIGxpbmthZ2UgeWFuZyBkaWd1bmFrYW4gZGFsYW0gYW5hbGlzaXMgaW5pOg0KDQoxLiAgKipTaW5nbGUgTGlua2FnZSAoTmVhcmVzdCBOZWlnaGJvcik6KioNCiAgICAqICoqS29uc2VwOioqIE1lbmd1a3VyIGphcmFrIGFudGFyYSBhbmdnb3RhICoqdGVyZGVrYXQqKiBkYXJpIGR1YSBrZWxvbXBvayAoc2VwZXJ0aSBkdWEgb3JhbmcgeWFuZyBzYWxpbmcgbWVuZ3VsdXJrYW4gdGFuZ2FuIGRhcmkgZHVhIHB1bGF1IGJlcmJlZGEpLg0KICAgICogKipLYXJha3RlcmlzdGlrOioqIENlbmRlcnVuZyBtZW5naGFzaWxrYW4gY2x1c3RlciB5YW5nIG1lbWFuamFuZyBtZW55ZXJ1cGFpIHJhbnRhaSAoKmNoYWluaW5nIGVmZmVjdCopLiBNZXRvZGUgaW5pIHNlbnNpdGlmLCBkaSBtYW5hIHNhdHUgZGF0YSBwZW5naHVidW5nIGJpc2EgbWVueWF0dWthbiBkdWEga2Vsb21wb2sgeWFuZyBzZWJlbmFybnlhIGJlcmJlZGEgamF1aC4NCg0KMi4gICoqQ29tcGxldGUgTGlua2FnZSAoRmFydGhlc3QgTmVpZ2hib3IpOioqDQogICAgKiAqKktvbnNlcDoqKiBNZW5ndWt1ciBqYXJhayBhbnRhcmEgYW5nZ290YSAqKnRlcmphdWgqKiBkYXJpIGR1YSBrZWxvbXBvay4gRHVhIGtlbG9tcG9rIGhhbnlhIGJvbGVoIGJlcmdhYnVuZyBqaWthIGFuZ2dvdGEgeWFuZyBwYWxpbmcgamF1aCBzZWthbGlwdW4gbWFzaWggYmVyYWRhIGRhbGFtIGphcmFrIHRvbGVyYW5zaS4NCiAgICAqICoqS2FyYWt0ZXJpc3RpazoqKiBDZW5kZXJ1bmcgbWVuZ2hhc2lsa2FuIGNsdXN0ZXIgeWFuZyBidWxhdCwgcGFkYXQgKCpjb21wYWN0KiksIGRhbiBtZW1pbGlraSBkaWFtZXRlciBrZWNpbC4gSW5pIGtlYmFsaWthbiBkYXJpIFNpbmdsZSBMaW5rYWdlLg0KDQozLiAgKipBdmVyYWdlIExpbmthZ2UgKFJhdGEtcmF0YSk6KioNCiAgICAqICoqS29uc2VwOioqIE1lbmdoaXR1bmcgcmF0YS1yYXRhIGphcmFrIGFudGFyYSAqKnNlbHVydWggcGFzYW5nYW4qKiBhbmdnb3RhIGRhcmkgZHVhIGtlbG9tcG9rLg0KICAgICogKipLYXJha3RlcmlzdGlrOioqIE1lcnVwYWthbiBqYWxhbiB0ZW5nYWggYW50YXJhIFNpbmdsZSBkYW4gQ29tcGxldGUgTGlua2FnZS4gTWV0b2RlIGluaSBsZWJpaCB0YWhhbiAoKnJvYnVzdCopIHRlcmhhZGFwIGRhdGEgcGVuY2lsYW4gKCpvdXRsaWVyKikgZGliYW5kaW5na2FuIGtlZHVhIG1ldG9kZSBzZWJlbHVtbnlhLg0KDQo0LiAgKipXYXJk4oCZcyBNZXRob2QgKE1pbmltdW0gVmFyaWFuY2UpOioqDQogICAgKiAqKktvbnNlcDoqKiBNZXRvZGUgaW5pIG1lbWlsaWtpIHBlbmRla2F0YW4gc3RhdGlzdGlrIHlhbmcgYmVyYmVkYS4gQWxpaC1hbGloIGhhbnlhIG1lbmd1a3VyIGphcmFrIGZpc2lrLCBXYXJkIGJlcnR1anVhbiBtZW1pbmltYWxrYW4gKipoaWxhbmdueWEgaW5mb3JtYXNpKiogYXRhdSB2YXJpYW5zaSB0b3RhbCBkaSBkYWxhbSBjbHVzdGVyLiBJYSBha2FuIG1lbmdnYWJ1bmdrYW4gZHVhIGtlbG9tcG9rIHlhbmcsIGppa2EgZGlzYXR1a2FuLCBha2FuIG1lbmdoYXNpbGthbiBrZWxvbXBvayBiYXJ1IGRlbmdhbiB2YXJpYXNpIGludGVybmFsIHBhbGluZyBrZWNpbCAocGFsaW5nIHNlcmFnYW0pLg0KICAgICogKipLYXJha3RlcmlzdGlrOioqIE1ldG9kZSBpbmkgYWRhbGFoIHN0YW5kYXIgZW1hcyBkYWxhbSBiYW55YWsgYW5hbGlzaXMga2FyZW5hIGNlbmRlcnVuZyBtZW5naGFzaWxrYW4gY2x1c3RlciB5YW5nIHVrdXJhbm55YSBzZWltYmFuZyBkYW4gc2FuZ2F0IHJhcGkgc2VjYXJhIHN0cnVrdHVyLg0KICAgIA0KLS0tDQoNCiMgMi4gUnVtdXMgSW50aSBkYW4gTW9kZWwgTWF0ZW1hdGlzDQoNCkRhbGFtIGFuYWxpc2lzICpIaWVyYXJjaGljYWwgQ2x1c3RlcmluZyosIGRhc2FyIHBlcmhpdHVuZ2FuIG1hdGVtYXRpcyB0ZXJsZXRhayBwYWRhIGR1YSB0YWhhcDogcGVuZ3VrdXJhbiBqYXJhayBhbnRhciBkYXRhIGluZGl2aWR1IGRhbiBwZW5ndWt1cmFuIGphcmFrIGFudGFyIGtlbG9tcG9rICgqY2x1c3RlciopLiBCZXJpa3V0IGFkYWxhaCBub3Rhc2kgZGFuIHBlcnNhbWFhbiBwZW50aW5nIHlhbmcgZGlndW5ha2FuLg0KDQojIyAyLjEuIFBlbmd1a3VyYW4gSmFyYWsgQW50YXIgRGF0YSAoRXVjbGlkZWFuIERpc3RhbmNlKQ0KU2ViZWx1bSBtZWxha3VrYW4gcGVuZ2Vsb21wb2thbiwga2l0YSBoYXJ1cyBtZW5naGl0dW5nIHNlYmVyYXBhIGphdWggcGVyYmVkYWFuIGthcmFrdGVyaXN0aWsgYW50YXJhIHNhdHUgbGFndSBkZW5nYW4gbGFndSBsYWlubnlhLiBNZXRyaWsgeWFuZyBkaWd1bmFrYW4gZGFsYW0gc3R1ZGkgaW5pIGFkYWxhaCAqKkV1Y2xpZGVhbiBEaXN0YW5jZSoqLg0KDQpKaWthIGtpdGEgbWVtaWxpa2kgZHVhIGxhZ3UsIGxhZ3UgJHgkIGRhbiBsYWd1ICR5JCwgZGVuZ2FuICRwJCBmaXR1ciBhdWRpbyAoc2VwZXJ0aSBCUE0sIEVuZXJneSwgZGxsKSwgbWFrYSBqYXJhayBhbnRhcmEga2VkdWFueWEgZGlydW11c2thbiBzZWJhZ2FpOg0KDQokJGQoeCwgeSkgPSBcc3FydHtcc3VtX3tpPTF9XntwfSAoeF9pIC0geV9pKV4yfSQkDQoNCioqS2V0ZXJhbmdhbjoqKg0KKiAkZCh4LCB5KSQ6IEphcmFrIEV1Y2xpZGVhbiBhbnRhcmEgbGFndSAkeCQgZGFuICR5JC4NCiogJHhfaSwgeV9pJDogTmlsYWkgZml0dXIga2UtJGkkIGRhcmkgbGFndSAkeCQgZGFuICR5JC4NCiogJHAkOiBKdW1sYWggZGltZW5zaS92YXJpYWJlbCB5YW5nIGRpZ3VuYWthbi4NCg0KIyMgMi4yLiBLcml0ZXJpYSBMaW5rYWdlIChKYXJhayBBbnRhciBDbHVzdGVyKQ0KU2V0ZWxhaCBtZW5nZXRhaHVpIGphcmFrIGFudGFyIGluZGl2aWR1LCBhbGdvcml0bWEgcGVybHUgbWVuZ2V0YWh1aSBqYXJhayBhbnRhcmEgZHVhIGNsdXN0ZXIsIG1pc2FsIENsdXN0ZXIgJEEkIGRhbiBDbHVzdGVyICRCJC4gQmVyaWt1dCBhZGFsYWggZm9ybXVsYXNpIG1hdGVtYXRpcyB1bnR1ayBzZXRpYXAgbWV0b2RlIGxpbmthZ2U6DQoNCiMjIyBhLiBTaW5nbGUgTGlua2FnZQ0KSmFyYWsgYW50YXJhIGNsdXN0ZXIgJEEkIGRhbiAkQiQgYWRhbGFoIGphcmFrICoqbWluaW11bSoqIGFudGFyYSBhbmdnb3RhIGRpICRBJCBkYW4gYW5nZ290YSBkaSAkQiQuDQokJGRfe3NpbmdsZX0oQSwgQikgPSBcbWluIFx7IGQoYSwgYikgOiBhIFxpbiBBLCBiIFxpbiBCIFx9JCQNCg0KIyMjIGIuIENvbXBsZXRlIExpbmthZ2UNCkphcmFrIGFudGFyYSBjbHVzdGVyICRBJCBkYW4gJEIkIGFkYWxhaCBqYXJhayAqKm1ha3NpbXVtKiogYW50YXJhIGFuZ2dvdGEgZGkgJEEkIGRhbiBhbmdnb3RhIGRpICRCJC4NCiQkZF97Y29tcGxldGV9KEEsIEIpID0gXG1heCBceyBkKGEsIGIpIDogYSBcaW4gQSwgYiBcaW4gQiBcfSQkDQoNCiMjIyBjLiBBdmVyYWdlIExpbmthZ2UNCkphcmFrIGFkYWxhaCAqKnJhdGEtcmF0YSoqIGRhcmkgc2VsdXJ1aCBrZW11bmdraW5hbiBwYXNhbmdhbiBqYXJhayBhbnRhcmEgYW5nZ290YSAkQSQgZGFuIGFuZ2dvdGEgJEIkLg0KJCRkX3thdmVyYWdlfShBLCBCKSA9IFxmcmFjezF9e3xBfCBcY2RvdCB8Qnx9IFxzdW1fe2EgXGluIEF9IFxzdW1fe2IgXGluIEJ9IGQoYSwgYikkJA0KRGltYW5hICR8QXwkIGRhbiAkfEJ8JCBhZGFsYWgganVtbGFoIGFuZ2dvdGEgZGFsYW0gbWFzaW5nLW1hc2luZyBjbHVzdGVyLg0KDQojIyMgZC4gV2FyZCdzIE1ldGhvZA0KTWV0b2RlIFdhcmQgYmVydHVqdWFuIG1lbWluaW1hbGthbiBrZW5haWthbiB0b3RhbCB2YXJpYW5zIGRhbGFtIGNsdXN0ZXIgKCpFcnJvciBTdW0gb2YgU3F1YXJlcyogLyBTU0UpLiBKYXJhayBkaWRlZmluaXNpa2FuIHNlYmFnYWkgc2ViZXJhcGEgYmVzYXIgKiprZW5haWthbiBTU0UqKiBqaWthICRBJCBkYW4gJEIkIGRpZ2FidW5nOg0KJCRkX3t3YXJkfShBLCBCKSA9IFNTRV97QSBcY3VwIEJ9IC0gKFNTRV9BICsgU1NFX0IpJCQNCg0KU2VtYWtpbiBrZWNpbCBuaWxhaSAkZF97d2FyZH0kLCBzZW1ha2luIHNlZGlraXQgaW5mb3JtYXNpIHlhbmcgaGlsYW5nIHNhYXQgcGVuZ2dhYnVuZ2FuLCB5YW5nIGJlcmFydGkgcGVuZ2dhYnVuZ2FuIHRlcnNlYnV0ICJhbWFuIiB1bnR1ayBkaWxha3VrYW4uDQoNCi0tLQ0KDQojIDMuIENhcmEgS2VyamEgKExhbmdrYWggT3BlcmFzaSkNCg0KRGFsYW0gYmFnaWFuIGluaSwgZGlqZWxhc2thbiBwcm9zZWR1ciBhbGdvcml0bWlrIHlhbmcgZGlqYWxhbmthbiBvbGVoIGtvbXB1dGVyIHVudHVrIG1lbWJlbnR1ayBoaWVyYXJraSBjbHVzdGVyLiBQZW1haGFtYW4gdGVyaGFkYXAgbGFuZ2thaC1sYW5na2FoIGluaSBwZW50aW5nIHVudHVrIG1lbmp1c3RpZmlrYXNpIGhhc2lsIHlhbmcgZGlwZXJvbGVoLg0KDQojIyAzLjEuIEFsZ29yaXRtYSBBZ2dsb21lcmF0aXZlIENsdXN0ZXJpbmcNClByb3NlcyBwZW1iZW50dWthbiBjbHVzdGVyIGRhcmkgYmF3YWgga2UgYXRhcyAoKmJvdHRvbS11cCopIG1lbmdpa3V0aSBsYW5na2FoLWxhbmdrYWggc2lzdGVtYXRpcyBiZXJpa3V0Og0KDQoxLiAgKipJbmlzaWFsaXNhc2kgKFN0YXJ0KToqKg0KICAgIEhpdHVuZyBtYXRyaWtzIGphcmFrIChiaWFzYW55YSAqRXVjbGlkZWFuIERpc3RhbmNlKikgYW50YXIgc2VtdWEgJE4kIHRpdGlrIGRhdGEuIFBhZGEgdGFoYXAgYXdhbCwgc2V0aWFwIHRpdGlrIGRhdGEgZGlhbmdnYXAgc2ViYWdhaSBzYXR1IGNsdXN0ZXIgdHVuZ2dhbCwgc2VoaW5nZ2EgdGVyZGFwYXQgJE4kIGNsdXN0ZXIuDQoNCjIuICAqKlBlbmNhcmlhbiBQYXNhbmdhbiBUZXJkZWthdDoqKg0KICAgIFBpbmRhaSBtYXRyaWtzIGphcmFrIHVudHVrIG1lbmVtdWthbiBwYXNhbmdhbiBjbHVzdGVyIHlhbmcgbWVtaWxpa2kgamFyYWsgdGVyZGVrYXQgc2F0dSBzYW1hIGxhaW4uIEphcmFrIGluaSBkaXRlbnR1a2FuIGJlcmRhc2Fya2FuIG1ldG9kZSAqTGlua2FnZSogeWFuZyBkaXBpbGloIChTaW5nbGUsIENvbXBsZXRlLCBBdmVyYWdlLCBhdGF1IFdhcmQpLg0KDQozLiAgKipQZW5nZ2FidW5nYW4gKE1lcmdpbmcpOioqDQogICAgR2FidW5na2FuIGtlZHVhIGNsdXN0ZXIgdGVyZGVrYXQgdGVyc2VidXQgbWVuamFkaSBzYXR1IGNsdXN0ZXIgYmFydS4gQWtpYmF0bnlhLCBqdW1sYWggdG90YWwgY2x1c3RlciBiZXJrdXJhbmcgc2F0dSAoJE4tMSQpLg0KDQo0LiAgKipQZW1iYXJ1YW4gTWF0cmlrcyBKYXJhayAoVXBkYXRlKToqKg0KICAgIEhpdHVuZyB1bGFuZyBqYXJhayBhbnRhcmEgY2x1c3RlciBiYXJ1IChoYXNpbCBnYWJ1bmdhbiB0YWRpKSBkZW5nYW4gc2VtdWEgY2x1c3RlciBsYWluIHlhbmcgbWFzaWggYWRhLg0KDQo1LiAgKipJdGVyYXNpIChMb29waW5nKToqKg0KICAgIFVsYW5naSBsYW5na2FoIDIgaGluZ2dhIDQgc2VjYXJhIHRlcnVzLW1lbmVydXMgc2FtcGFpIGhhbnlhIHRlcnNpc2Egc2F0dSBjbHVzdGVyIGJlc2FyIHlhbmcgbWVuY2FrdXAgc2VsdXJ1aCBkYXRhLg0KDQo2LiAgKipQZW1vdG9uZ2FuIFBvaG9uIChDdXR0aW5nKToqKg0KICAgIFVudHVrIG1lbmRhcGF0a2FuIGhhc2lsIGFraGlyLCBwb3RvbmcgKkRlbmRyb2dyYW0qIHBhZGEga2V0aW5nZ2lhbiBhdGF1IGp1bWxhaCAkayQgdGVydGVudHUgc2VzdWFpIGtlYnV0dWhhbiBhbmFsaXNpcy4NCg0KIyMgMy4yLiBBbGdvcml0bWEgRGl2aXNpdmUgQ2x1c3RlcmluZyAoRElBTkEpDQpQcm9zZXMgcGVtYmVudHVrYW4gY2x1c3RlciBkYXJpIGF0YXMga2UgYmF3YWggKCp0b3AtZG93biopIGJla2VyamEgZGVuZ2FuIHVydXRhbiB0ZXJiYWxpazoNCg0KMS4gICoqSW5pc2lhbGlzYXNpOioqDQogICAgTXVsYWkgZGVuZ2FuIHNhdHUgY2x1c3RlciBiZXNhciB5YW5nIGJlcmlzaSBzZWx1cnVoICROJCBkYXRhIG9ic2VydmFzaS4NCg0KMi4gICoqUGVtaWxpaGFuIENsdXN0ZXIgdW50dWsgRGlwZWNhaDoqKg0KICAgIElkZW50aWZpa2FzaSBjbHVzdGVyIHlhbmcgbWVtaWxpa2kgKmRpYW1ldGVyKiB0ZXJiZXNhciAoamFyYWsgdGVyamF1aCBhbnRhciBhbmdnb3RhbnlhKSBhdGF1IHZhcmlhbnNpIHRlcnRpbmdnaS4NCg0KMy4gICoqUGVtaXNhaGFuIChTcGxpdHRpbmcpOioqDQogICAgRGkgZGFsYW0gY2x1c3RlciB0ZXJwaWxpaCwgY2FyaSBvYmplayB5YW5nIHBhbGluZyB0aWRhayBtaXJpcCBkZW5nYW4gcmF0YS1yYXRhIGtlbG9tcG9rICgqc3BsaW50ZXIgZ3JvdXAqKS4gUGlzYWhrYW4gb2JqZWsgdGVyc2VidXQgZGFuIGluaXNpYXNpIGtlbG9tcG9rIHBlY2FoYW4gYmFydS4NCg0KNC4gICoqUmVhbG9rYXNpOioqDQogICAgUGVyaWtzYSBhbmdnb3RhIGxhaW4gZGFsYW0gY2x1c3RlciBsYW1hLiBKaWthIGFkYSBhbmdnb3RhIHlhbmcgbGViaWggZGVrYXQgamFyYWtueWEga2Uga2Vsb21wb2sgcGVjYWhhbiBiYXJ1IGRpYmFuZGluZ2thbiBrZSBrZWxvbXBvayBsYW1hLCBwaW5kYWhrYW4gYW5nZ290YSB0ZXJzZWJ1dC4NCg0KNS4gICoqSXRlcmFzaToqKg0KICAgIFVsYW5naSBwcm9zZXMgcGVtZWNhaGFuIGluaSBwYWRhIGNsdXN0ZXItY2x1c3RlciB5YW5nIHRlcmJlbnR1ayBoaW5nZ2Egc2V0aWFwIGNsdXN0ZXIgaGFueWEgYmVyaXNpIHNhdHUgZGF0YSB0dW5nZ2FsICgkTiQgY2x1c3RlcikuDQoNCiMjIDMuMy4gSHlwZXJwYXJhbWV0ZXIgVXRhbWENCkRhbGFtIG1lbmphbGFua2FuIGFsZ29yaXRtYSBpbmksIHRlcmRhcGF0IGJlYmVyYXBhIHBhcmFtZXRlciBrdW5jaSB5YW5nIGhhcnVzIGRpdGVudHVrYW4gb2xlaCBwZW5nZ3VuYSAoKnVzZXItZGVmaW5lZCBwYXJhbWV0ZXJzKikgeWFuZyBha2FuIG1lbXBlbmdhcnVoaSBoYXNpbCBha2hpcjoNCg0KMS4gICoqTWV0cmljIEphcmFrICgqRGlzdGFuY2UgTWV0cmljKik6KioNCiAgICBDYXJhIG1lbmdoaXR1bmcgamFyYWsgYW50YXIgZGF0YSBpbmRpdmlkdS4gRGFsYW0gbGFwb3JhbiBpbmkgZGlndW5ha2FuICoqRXVjbGlkZWFuIERpc3RhbmNlKiouIFBpbGloYW4gbGFpbiBiaXNhIGJlcnVwYSAqTWFuaGF0dGFuKiBhdGF1ICpDb3NpbmUgU2ltaWxhcml0eSouDQoNCjIuICAqKk1ldG9kZSBMaW5rYWdlOioqDQogICAgQXR1cmFuIHVudHVrIG1lbmdoaXR1bmcgamFyYWsgYW50YXIgY2x1c3RlciAoc2VwZXJ0aSBkaWplbGFza2FuIHBhZGEgQmFiIDIpLiBJbmkgYWRhbGFoIHBhcmFtZXRlciBwYWxpbmcga3J1c2lhbCBrYXJlbmEgbWVuZW50dWthbiBiZW50dWsgY2x1c3Rlci4NCiAgICAqICpPcHNpb25hbDoqIFNpbmdsZSwgQ29tcGxldGUsIEF2ZXJhZ2UsIFdhcmQuDQoNCjMuICAqKkp1bWxhaCBDbHVzdGVyICgkayQpOioqDQogICAgVGl0aWsgcG90b25nIHBhZGEgZGVuZHJvZ3JhbS4gUGFyYW1ldGVyIGluaSB0aWRhayBkaXRlbnR1a2FuIGRpIGF3YWwgYWxnb3JpdG1hIChzYWF0ICp0cmFpbmluZyopLCBtZWxhaW5rYW4gZGl0ZW50dWthbiBkaSBha2hpciBzYWF0IGFuYWxpc2lzIGhhc2lsICgqcG9zdC1wcm9jZXNzaW5nKikgdW50dWsgbWVuZGFwYXRrYW4gbGFiZWwga2Vsb21wb2suDQoNCi0tLQ0KDQojIDQuIEtlbGViaWhhbiBkYW4gS2V0ZXJiYXRhc2FuIFByYWt0aXMNCg0KU2V0aWFwIGFsZ29yaXRtYSAqbWFjaGluZSBsZWFybmluZyogbWVtaWxpa2kga2FyYWt0ZXJpc3RpayB1bmlrIHlhbmcgbWVtYnVhdG55YSBjb2NvayB1bnR1ayBzaXR1YXNpIHRlcnRlbnR1IG5hbXVuIGt1cmFuZyBvcHRpbWFsIGRpIHNpdHVhc2kgbGFpbi4gQmVyaWt1dCBhZGFsYWggYW5hbGlzaXMgbWVuZ2VuYWkga2VsZWJpaGFuIGRhbiBrZXRlcmJhdGFzYW4gcHJha3RpcyBkYXJpICpIaWVyYXJjaGljYWwgQ2x1c3RlcmluZyogeWFuZyBkaXRlcmFwa2FuIHBhZGEgZGF0YXNldCBpbmkuDQoNCiMjIDQuMS4gS2VsZWJpaGFuIFV0YW1hDQoxLiAgKipUaWRhayBQZXJsdSBNZW5lbnR1a2FuIEp1bWxhaCBDbHVzdGVyICgkayQpIGRpIEF3YWwqKg0KICAgIEJlcmJlZGEgZGVuZ2FuIGFsZ29yaXRtYSAqSy1NZWFucyogeWFuZyBtZXdhamlia2FuIHBlbmdndW5hIG1lbmViYWsgbmlsYWkgJGskIHNlYmVsdW0gcHJvc2VzIGRpbXVsYWksIG1ldG9kZSBoaWVyYXJraSBtZW1iaWFya2FuIGRhdGEgbWVtYmVudHVrIHN0cnVrdHVybnlhIHNlbmRpcmkgdGVybGViaWggZGFodWx1LiBLZXB1dHVzYW4ganVtbGFoIGNsdXN0ZXIgZGFwYXQgZGlsYWt1a2FuIGJlbGFrYW5nYW4gZGVuZ2FuIG1lbGloYXQgdmlzdWFsaXNhc2kgKmRlbmRyb2dyYW0qLCBzZWhpbmdnYSBhbmFsaXNpcyBtZW5qYWRpIGxlYmloIGZsZWtzaWJlbCBkYW4gbWluaW0gYmlhcyBhd2FsLg0KDQoyLiAgKipWaXN1YWxpc2FzaSBTdHJ1a3R1ciBEYXRhIChEZW5kcm9ncmFtKSoqDQogICAgTWV0b2RlIGluaSBtZW5naGFzaWxrYW4gb3V0cHV0IHZpc3VhbCBiZXJ1cGEgcG9ob24gaGllcmFya2kgKCpkZW5kcm9ncmFtKikgeWFuZyBzYW5nYXQgaW5mb3JtYXRpZi4gS2l0YSBkYXBhdCBtZW5lbHVzdXJpIGh1YnVuZ2FuIGFudGFyIGRhdGEgKCJzaWxzaWxhaCIga2VtaXJpcGFuIGxhZ3UpIG11bGFpIGRhcmkgbGV2ZWwgaW5kaXZpZHUgaGluZ2dhIGtlbG9tcG9rIGJlc2FyLiBIYWwgaW5pIHNhbmdhdCBiZXJndW5hIHVudHVrIG1lbmplbGFza2FuIGhhc2lsIGFuYWxpc2lzIGtlcGFkYSBvcmFuZyBhd2FtLg0KDQozLiAgKipDb2NvayB1bnR1ayBEYXRhc2V0IEtlY2lsLU1lbmVuZ2FoKioNCiAgICBQYWRhIGRhdGFzZXQgZGVuZ2FuIHVrdXJhbiByYXR1c2FuIGhpbmdnYSByaWJ1YW4gYmFyaXMgKHNlcGVydGkgZGF0YSBgdG9wMTBzLmNzdmAgeWFuZyBtZW1pbGlraSB+NjAwIGJhcmlzKSwgbWV0b2RlIGluaSBiZWtlcmphIHNhbmdhdCBiYWlrIGRhbiBjZXBhdC4gU3RydWt0dXIgaGllcmFya2kgeWFuZyBkaWhhc2lsa2FuIHB1biBtYXNpaCBtdWRhaCBkaWJhY2EgZGFuIGRpaW50ZXJwcmV0YXNpa2FuIG9sZWggbWF0YSBtYW51c2lhLg0KDQo0LiAgKipNZW5hbmdrYXAgU3RydWt0dXIgQmVyc2FyYW5nICgqTmVzdGVkIFN0cnVjdHVyZSopKioNCiAgICBNZXRvZGUgaW5pIG1hbXB1IG1lbmRldGVrc2kga2ViZXJhZGFhbiBzdWItY2x1c3RlciBkaSBkYWxhbSBjbHVzdGVyIHV0YW1hIChtaXNhbG55YTogZGkgZGFsYW0gZ2VucmUgIlBvcCIsIGFkYSBzdWIta2Vsb21wb2sgIkRhbmNlIFBvcCIgZGFuICJBY291c3RpYyBQb3AiKS4NCg0KIyMgNC4yLiBLZXRlcmJhdGFzYW4gUHJha3Rpcw0KMS4gICoqS29tcGxla3NpdGFzIEtvbXB1dGFzaSBUaW5nZ2kgKExhbWJhdCBwYWRhIEJpZyBEYXRhKSoqDQogICAgS2VsZW1haGFuIHRlcmJlc2FyIG1ldG9kZSBpbmkgYWRhbGFoIGJpYXlhIGtvbXB1dGFzaW55YS4gVW50dWsgKkFnZ2xvbWVyYXRpdmUgQ2x1c3RlcmluZyosIGtvbXBsZWtzaXRhcyB3YWt0dW55YSBiaXNhIG1lbmNhcGFpICRPKG5eMykkIGF0YXUgJE8obl4yIFxsb2cgbikkLiBBcnRpbnlhLCBqaWthIGp1bWxhaCBkYXRhIGJlcnRhbWJhaCAxMCBrYWxpIGxpcGF0LCB3YWt0dSBwcm9zZXNueWEgYmlzYSBiZXJ0YW1iYWggMTAwMCBrYWxpIGxpcGF0LiBNZXRvZGUgaW5pIHNhbmdhdCB0aWRhayBkaXNhcmFua2FuIHVudHVrIGRhdGFzZXQgZGVuZ2FuIGp1dGFhbiBiYXJpcyAoKkJpZyBEYXRhKikuDQoNCjIuICAqKlNlbnNpdGlmIHRlcmhhZGFwICpPdXRsaWVyKiBkYW4gKk5vaXNlKioqDQogICAgTWV0b2RlIGhpZXJhcmtpLCB0ZXJ1dGFtYSAqU2luZ2xlIExpbmthZ2UqLCBzYW5nYXQgc2Vuc2l0aWYgdGVyaGFkYXAgZGF0YSBwZW5jaWxhbiAoKm91dGxpZXIqKS4gU2F0dSBkYXRhIHlhbmcgYW5laCBiaXNhIG1lbmdhY2F1a2FuIHByb3NlcyBwZW5nZ2FidW5nYW4gY2x1c3RlciwgbWVueWViYWJrYW4gaGFzaWwgc3RydWt0dXIgeWFuZyB0aWRhayByYXBpIChlZmVrICpjaGFpbmluZyopLiBPbGVoIGthcmVuYSBpdHUsIHRhaGFwIHByYS1wcm9zZXMgKGNsZWFuaW5nKSBtZW5qYWRpIHNhbmdhdCBrcnVzaWFsLg0KDQozLiAgKipLZXB1dHVzYW4gUGVybWFuZW4gKFRpZGFrIEJpc2EgVW5kbykqKg0KICAgIEFsZ29yaXRtYSBoaWVyYXJraSBiZXJzaWZhdCAqZ3JlZWR5Ki4gU2VrYWxpIGR1YSBjbHVzdGVyIGRpZ2FidW5na2FuIChhdGF1IGRpcGVjYWgpLCBrZXB1dHVzYW4gdGVyc2VidXQgdGlkYWsgZGFwYXQgZGliYXRhbGthbiBkaSBsYW5na2FoIGJlcmlrdXRueWEuIEppa2EgdGVyamFkaSBrZXNhbGFoYW4gcGVuZ2dhYnVuZ2FuIGRpIGF3YWwgcHJvc2VzLCBrZXNhbGFoYW4gdGVyc2VidXQgYWthbiB0ZXJiYXdhIGhpbmdnYSBha2hpciwgeWFuZyBtdW5na2luIG1lbmdoYXNpbGthbiBzdHJ1a3R1ciB5YW5nIGt1cmFuZyBvcHRpbWFsIGRpYmFuZGluZ2thbiBtZXRvZGUgaXRlcmF0aWYgc2VwZXJ0aSAqSy1NZWFucyouDQoNCjQuICAqKlN1Ympla3Rpdml0YXMgZGFsYW0gUGVtb3RvbmdhbiBQb2hvbioqDQogICAgTWVza2lwdW4gZmxla3NpYmVsLCBwZW5lbnR1YW4gZGkgbWFuYSBoYXJ1cyBtZW1vdG9uZyAqZGVuZHJvZ3JhbSogKHVudHVrIG1lbmVudHVrYW4ganVtbGFoIGNsdXN0ZXIgYWtoaXIpIHNlcmluZ2thbGkgYmVyc2lmYXQgc3ViamVrdGlmIGRhbiBtZW1lcmx1a2FuIGludGVycHJldGFzaSB2aXN1YWwgcGVuZ2d1bmEsIGtlY3VhbGkgZGliYW50dSBkZW5nYW4gbWV0cmlrIHZhbGlkYXNpIHNlcGVydGkgKlNpbGhvdWV0dGUgU2NvcmUqLg0KICAgIA0KLS0tDQoNCiMgNS4gU3VtYmVyIGRhbiBMb2FkaW5nIERhdGENCg0KIyMgNS4xLiBEZXNrcmlwc2kgZGFuIFN1bWJlciBEYXRhDQpEYXRhc2V0IHlhbmcgZGlndW5ha2FuIGRhbGFtIGFuYWxpc2lzIGluaSBhZGFsYWggKipUb3AgU3BvdGlmeSBTb25ncyBmcm9tIDIwMTAtMjAxOSAtIEJZIFlFQVIqKi4gRGF0YSBpbmkgYmVyaXNpIGRhZnRhciBsYWd1LWxhZ3UgcGFsaW5nIHBvcHVsZXIgZGkgZHVuaWEgeWFuZyBtYXN1ayBkYWxhbSB0YW5nZ2EgbGFndSBTcG90aWZ5IHNlbGFtYSBzYXR1IGRla2FkZSB0ZXJha2hpci4NCg0KKiAqKlN1bWJlciBEYXRhOioqIEthZ2dsZSAoTGVvbmFyZG8gUGVuYSkNCg0KKiAqKlRhdXRhbjoqKiBbVG9wIFNwb3RpZnkgc29uZ3MgZnJvbSAyMDEwLTIwMTkgLSBCWSBZRUFSXShodHRwczovL3d3dy5rYWdnbGUuY29tL2RhdGFzZXRzL2xlb25hcmRvcGVuYS90b3Atc3BvdGlmeS1zb25ncy1mcm9tLTIwMTAyMDE5LWJ5LXllYXIpDQoNCiogKipGb3JtYXQ6KiogQ1NWICgqQ29tbWEgU2VwYXJhdGVkIFZhbHVlcyopDQoNCiogKipEaW1lbnNpOioqIDYwMyBiYXJpcyAobGFndSkgZGFuIDE1IGtvbG9tICh2YXJpYWJlbCkuDQoNCkRhdGFzZXQgaW5pIG1lbXVhdCBiZXJiYWdhaSBmaXR1ciBhdWRpbyB0ZWtuaXMgeWFuZyBkaWVrc3RyYWsgb2xlaCBTcG90aWZ5IHVudHVrIHNldGlhcCBsYWd1LCBhbnRhcmEgbGFpbjoNCg0KKiAqKkJQTSAoQmVhdHMgUGVyIE1pbnV0ZSk6KiogVGVtcG8gbGFndS4NCg0KKiAqKkVuZXJneSAoYG5yZ3lgKToqKiBTZWJlcmFwYSBiZXJ0ZW5hZ2EgbGFndSB0ZXJzZWJ1dC4NCg0KKiAqKkRhbmNlYWJpbGl0eSAoYGRuY2VgKToqKiBLZW11ZGFoYW4gbGFndSB1bnR1ayBkaXBha2FpIG1lbmFyaS4NCg0KKiAqKkxvdWRuZXNzIChgZEJgKToqKiBLZWtlcmFzYW4gc3VhcmEgKHNlbWFraW4gdGluZ2dpIG5lZ2F0aWZueWEsIHNlbWFraW4gcGVsYW4pLg0KDQoqICoqVmFsZW5jZSAoYHZhbGApOioqIFBvc2l0aXZpdGFzIGxhZ3UgKHNlbWFraW4gdGluZ2dpLCBzZW1ha2luIGNlcmlhOyBzZW1ha2luIHJlbmRhaCwgc2VtYWtpbiBzZWRpaC9kZXByZXNpKS4NCg0KKiAqKkFjb3VzdGljbmVzcyAoYGFjb3VzYCk6KiogVGluZ2thdCBha3VzdGlrIGxhZ3UuDQoNCiMjIDUuMi4gQWxhc2FuIFBlbWlsaWhhbiBEYXRhc2V0DQpUZXJkYXBhdCB0aWdhIGFsYXNhbiB1dGFtYSBtZW5nYXBhIGRhdGFzZXQgaW5pIGRpcGlsaWggdW50dWsgc3R1ZGkga2FzdXMgKkhpZXJhcmNoaWNhbCBDbHVzdGVyaW5nKjoNCg0KMS4gICoqS2F5YSBha2FuIEZpdHVyIE51bWVyaWsgS29udGludToqKg0KICAgIE1ldG9kZSBjbHVzdGVyaW5nIGJlcmJhc2lzIGphcmFrICgqZGlzdGFuY2UtYmFzZWQqKSBzZXBlcnRpIEhpZXJhcmtpIHNhbmdhdCBiZXJnYW50dW5nIHBhZGEgZGF0YSBhbmdrYS4gRGF0YXNldCBpbmkgbWVueWVkaWFrYW4gdmFyaWFiZWwgYXVkaW8geWFuZyB0ZXJ1a3VyIHNlY2FyYSBtYXRlbWF0aXMgKGJ1a2FuIHNla2FkYXIgdGVrcyksIHNlaGluZ2dhIHBlcmhpdHVuZ2FuICpFdWNsaWRlYW4gRGlzdGFuY2UqIG1lbmphZGkgc2FuZ2F0IHZhbGlkIGRhbiBiZXJtYWtuYS4NCg0KMi4gICoqVWt1cmFuIERhdGEgeWFuZyBJZGVhbCAoU3dlZXQgU3BvdCk6KioNCiAgICBBbGdvcml0bWEgKkhpZXJhcmNoaWNhbCBDbHVzdGVyaW5nKiBtZW1pbGlraSBrb21wbGVrc2l0YXMga29tcHV0YXNpIHlhbmcgYmVyYXQgKCRPKG5eMykkKS4gSmlrYSBkYXRhIHRlcmxhbHUgYmVzYXIgKGp1dGFhbiBiYXJpcyksIGtvbXB1dGVyIGFrYW4gbGFtYmF0LiBKaWthIHRlcmxhbHUgc2VkaWtpdCwgcG9sYSB0aWRhayB0ZXJsaWhhdC4gSnVtbGFoIDYwMyBkYXRhIHBhZGEgZGF0YXNldCBpbmkgYWRhbGFoIHVrdXJhbiAiR29sZGlsb2NrcyIgKHNhbmdhdCBwYXMp4oCUY3VrdXAga2VjaWwgdW50dWsgZGlwcm9zZXMgY2VwYXQgb2xlaCBSLCBuYW11biBjdWt1cCBiZXNhciB1bnR1ayBtZW5naGFzaWxrYW4gc3RydWt0dXIgKmRlbmRyb2dyYW0qIHlhbmcga2F5YSBkYW4gbWVuYXJpay4NCg0KMy4gICoqSW50ZXJwcmV0YXNpIHlhbmcgSW50dWl0aWY6KioNCiAgICBNdXNpayBhZGFsYWggZG9tYWluIHlhbmcgZGlwYWhhbWkgc2VtdWEgb3JhbmcuIEhhc2lsIGNsdXN0ZXIgbmFudGlueWEgYWthbiBzYW5nYXQgbXVkYWggZGl2YWxpZGFzaSBkZW5nYW4gbG9naWthIG1hbnVzaWEgKG1pc2FsbnlhOiAiQXBha2FoIG1hc3VrIGFrYWwgbGFndSAqRGVzcGFjaXRvKiBzYXR1IGdydXAgZGVuZ2FuICpTaGFwZSBvZiBZb3UqPyIpLiBJbmkgbWVtdWRhaGthbiBkYWxhbSB0YWhhcCBldmFsdWFzaSBrdWFsaXRhdGlmLg0KDQojIyA1LjMuIFByb3NlcyBMb2FkaW5nIERhdGENCkJlcmlrdXQgYWRhbGFoIHRhaGFwYW4gbWVtdWF0IHB1c3Rha2EgeWFuZyBkaWJ1dHVoa2FuIGRhbiBtZW1iYWNhIGRhdGEga2UgZGFsYW0gUlN0dWRpbzoNCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiMgMS4gTG9hZCBMaWJyYXJ5DQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkoY2x1c3RlcikNCmxpYnJhcnkoZmFjdG9leHRyYSkNCmxpYnJhcnkoZGVuZGV4dGVuZCkNCmxpYnJhcnkoRFQpDQoNCiMgMi4gTG9hZCBEYXRhIChERU5HQU4gUEVSQkFJS0FOIEVOQ09ESU5HKQ0KIyBLaXRhIHRhbWJhaGthbiBmaWxlRW5jb2Rpbmc9IklTTy04ODU5LTEiIHVudHVrIG1lbmFuZ2FuaSBrYXJha3RlciBzcGVzaWFsDQp0cnlDYXRjaCh7DQogICMgQ29iYSBiYWNhIGRlbmdhbiBlbmNvZGluZyBMYXRpbi0xIChiaWFzYW55YSBpbmkgc29sdXNpbnlhKQ0KICBkZiA8LSByZWFkLmNzdigidG9wMTBzLmNzdiIsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSwgZmlsZUVuY29kaW5nID0gIklTTy04ODU5LTEiKQ0KfSwgd2FybmluZyA9IGZ1bmN0aW9uKHcpIHsNCiAgIyBKaWthIG1hc2loIGdhZ2FsLCBjb2JhIGRlZmF1bHQNCiAgZGYgPDwtIHJlYWQuY3N2KCJ0b3AxMHMuY3N2Iiwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQ0KfSwgZXJyb3IgPSBmdW5jdGlvbihlKSB7DQogICMgRmFsbGJhY2sgdGVyYWtoaXINCiAgZGYgPDwtIHJlYWQuY3N2KCJ0b3AxMHMuY3N2Iiwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQ0KfSkNCg0KIyBQYXN0aWthbiBuYW1hIGtvbG9tIHRpZGFrIGFuZWggKGthZGFuZyBhZGEga2FyYWt0ZXIgYW5laCBkaSBrb2xvbSBwZXJ0YW1hKQ0KIyBLaXRhIHJlbmFtZSBrb2xvbSBwZXJ0YW1hIGphZGkgImlkIiBiaWFyIGFtYW4NCmNvbG5hbWVzKGRmKVsxXSA8LSAiaWQiDQoNCiMgMy4gTWVuYW1waWxrYW4gRGF0YQ0KZGF0YXRhYmxlKGRmLCANCiAgICAgICAgICBvcHRpb25zID0gbGlzdCgNCiAgICAgICAgICAgIHBhZ2VMZW5ndGggPSAxMCwgDQogICAgICAgICAgICBzY3JvbGxYID0gVFJVRQ0KICAgICAgICAgICksDQogICAgICAgICAgY2FwdGlvbiA9ICJUYWJlbCAxOiBEYXRhc2V0IFNwb3RpZnkgVG9wIFNvbmdzIg0KKQ0KYGBgDQoNCi0tLQ0KDQojIDYuIEVrc3Bsb3Jhc2kgRGF0YSBTaW5na2F0IChFREEpDQoNClRhaGFwICpFeHBsb3JhdG9yeSBEYXRhIEFuYWx5c2lzKiAoRURBKSBhZGFsYWggbGFuZ2thaCBmdW5kYW1lbnRhbCB1bnR1ayBtZW1haGFtaSBrYXJha3RlcmlzdGlrIGRhdGFzZXQgc2ViZWx1bSBkaWxha3VrYW4gcGVtb2RlbGFuLiBQYWRhIGJhYiBpbmksIGtpdGEgYWthbiBtZW5lbGFhaCBzdHJ1a3R1ciBkYXRhLCBrZWxlbmdrYXBhbiBuaWxhaSwgZGlzdHJpYnVzaSBzdGF0aXN0aWssIHNlcnRhIG1lbmRldGVrc2kgKm91dGxpZXIqIG1lbGFsdWkgdmlzdWFsaXNhc2kga29tcHJlaGVuc2lmLg0KDQojIyA2LjEuIFBlbWVyaWtzYWFuIFN0cnVrdHVyIGRhbiBUaXBlIFZhcmlhYmVsDQpMYW5na2FoIHBlcnRhbWEgYWRhbGFoIG1lbmdpZGVudGlmaWthc2kgdGlwZSBkYXRhIHVudHVrIG1lbWlzYWhrYW4gYW50YXJhIHZhcmlhYmVsIGlkZW50aXRhcyAobGFiZWwpIGRhbiB2YXJpYWJlbCBmaXR1ciAobnVtZXJpaykuDQoNCmBgYHtyfQ0KIyBNZWxpaGF0IHN0cnVrdHVyIGRhdGEgc2VjYXJhIHJpbmdrYXMNCmdsaW1wc2UoZGYpDQpgYGANCg0KKipBbmFsaXNpcyBTdHJ1a3R1cjoqKg0KQmVyZGFzYXJrYW4gb3V0cHV0IGRpIGF0YXMsIGRhdGFzZXQgdGVyZGlyaSBkYXJpIDYwMyBvYnNlcnZhc2kgZGFuIDE1IHZhcmlhYmVsLiBLaXRhIGRhcGF0IG1lbWJhZ2kgdmFyaWFiZWwgbWVuamFkaSBkdWEga2Vsb21wb2s6DQoNCiogKipWYXJpYWJlbCBMYWJlbCAoTm9uLU51bWVyaWspOioqIGB0aXRsZWAsIGBhcnRpc3RgLCBgdG9wIGdlbnJlYCwgYHllYXJgLiBWYXJpYWJlbCBpbmkgaGFueWEgZGlndW5ha2FuIHNlYmFnYWkgaWRlbnRpdGFzLg0KDQoqICoqVmFyaWFiZWwgRml0dXIgQXVkaW8gKE51bWVyaWspOioqIGBicG1gLCBgbnJneWAsIGBkbmNlYCwgYGRCYCwgYGxpdmVgLCBgdmFsYCwgYGR1cmAsIGBhY291c2AsIGBzcGNoYCwgYHBvcGAuIEtlc2VwdWx1aCB2YXJpYWJlbCBpbmlsYWggeWFuZyBha2FuIGRpYW5hbGlzaXMgbGViaWggbGFuanV0IHNlYmFnYWkgaW5wdXQgKmNsdXN0ZXJpbmcqLg0KDQojIyA2LjIuIFBlbmdlY2VrYW4gTWlzc2luZyBWYWx1ZXMNCktpdGEgaGFydXMgbWVtYXN0aWthbiBkYXRhIGJlcnNpaCBkYXJpIG5pbGFpIGtvc29uZyAoTkEpLCBrYXJlbmEgYWxnb3JpdG1hIGNsdXN0ZXJpbmcgYmVyYmFzaXMgamFyYWsgRXVjbGlkZWFuIHRpZGFrIGRhcGF0IG1lbXByb3NlcyBkYXRhIHlhbmcgaGlsYW5nLg0KDQpgYGB7cn0NCiMgTWVuZ2hpdHVuZyBqdW1sYWggbWlzc2luZyB2YWx1ZSAoTkEpIGRpIHNldGlhcCBrb2xvbQ0KY29sU3Vtcyhpcy5uYShkZikpDQpgYGANCg0KKipBbmFsaXNpczoqKiANClRlcmxpaGF0IGFuZ2thIDAgcGFkYSBzZW11YSBrb2xvbS4gQXJ0aW55YSwgZGF0YXNldCBpbmkgYmVyc2loIChjbGVhbikgZGFuIHNpYXAgZGlwcm9zZXMgdGFucGEgcGVybHUgdGVrbmlrIGltcHV0YXNpIGRhdGEuDQoNCiMjIDYuMy4gUmluZ2thc2FuIFN0YXRpc3RpayBEZXNrcmlwdGlmDQpTdGF0aXN0aWsgZGVza3JpcHRpZiBkaXBlcmx1a2FuIHVudHVrIG1lbGloYXQgcmVudGFuZyBza2FsYSBkYXRhLiBJbmkgcGVudGluZyB1bnR1ayBtZW5lbnR1a2FuIGFwYWthaCBtZXRvZGUgc2NhbGluZyBkaXBlcmx1a2FuIGF0YXUgdGlkYWsuDQoNCmBgYHtyfQ0KIyBNZW5hbXBpbGthbiBzdGF0aXN0aWsgZGFzYXIgaGFueWEgdW50dWsgMTAgdmFyaWFiZWwgbnVtZXJpaw0KIyBLaXRhIHBpbGloIGtvbG9tIGJlcmRhc2Fya2FuIG5hbWEgZml0dXJueWENCmRmX251bV9vbmx5IDwtIGRmICU+JSBzZWxlY3QoYnBtLCBucmd5LCBkbmNlLCBkQiwgbGl2ZSwgdmFsLCBkdXIsIGFjb3VzLCBzcGNoLCBwb3ApDQpzdW1tYXJ5KGRmX251bV9vbmx5KQ0KYGBgDQoqKlRlbXVhbiBQZW50aW5nIChBbGFzYW4gU2NhbGluZyk6KioNClRlcmRhcGF0IGRpc3Bhcml0YXMgc2thbGEgeWFuZyBzYW5nYXQgbWVuY29sb2sgYW50YXIgdmFyaWFiZWw6DQoNCiogKipEdXJhc2kgKGBkdXJgKToqKiBSYXRhLXJhdGEgfjIyNCwgbmlsYWkgbWFrc2ltdW0gaGluZ2dhIDQyNC4NCiogKipTcGVlY2hpbmVzcyAoYHNwY2hgKToqKiBSYXRhLXJhdGEgaGFueWEgfjguDQoqICoqTG91ZG5lc3MgKGBkQmApOioqIE1lbWlsaWtpIG5pbGFpIG5lZ2F0aWYuDQoNCioqSW1wbGlrYXNpOioqIFZhcmlhYmVsIGRlbmdhbiBhbmdrYSBiZXNhciAoc2VwZXJ0aSBEdXJhc2kpIGFrYW4gbWVuZG9taW5hc2kgcGVyaGl0dW5nYW4gamFyYWsgamlrYSB0aWRhayBkaXNldGFyYWthbi4gTWFrYSwgcHJvc2VzICoqU3RhbmRhcmRpc2FzaSAoU2NhbGluZykqKiBkaSBiYWIgc2VsYW5qdXRueWEgYmVyc2lmYXQgKipXQUpJQioqLg0KDQojIyA2LjQuIFZpc3VhbGlzYXNpIERpc3RyaWJ1c2kgKEhpc3RvZ3JhbSBDb2xsYWdlKQ0KQmVyaWt1dCBhZGFsYWggdmlzdWFsaXNhc2kgc2ViYXJhbiBkYXRhIHNlbHVydWggZml0dXIgYXVkaW8gZGFsYW0gc2F0dSB0YW1waWxhbiBrb2xhc2UgKGdyaWQpIHVudHVrIG1lbGloYXQgcG9sYSBkaXN0cmlidXNpbnlhLg0KDQpgYGB7ciBmaWcuaGVpZ2h0PTEwLCBmaWcud2lkdGg9MTAsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojIDEuIFRyYW5zZm9ybWFzaSBkYXRhIGtlIGZvcm1hdCAnTG9uZycgYWdhciBiaXNhIGRpLXBsb3Qgc2VrYWxpZ3VzDQpkZl9sb25nIDwtIGRmICU+JQ0KICBzZWxlY3QoYnBtLCBucmd5LCBkbmNlLCBkQiwgbGl2ZSwgdmFsLCBkdXIsIGFjb3VzLCBzcGNoLCBwb3ApICU+JQ0KICBwaXZvdF9sb25nZXIoY29scyA9IGV2ZXJ5dGhpbmcoKSwgbmFtZXNfdG8gPSAiZml0dXIiLCB2YWx1ZXNfdG8gPSAibmlsYWkiKQ0KDQojIDIuIFBsb3QgSGlzdG9ncmFtIGRlbmdhbiBGYWNldCBXcmFwIHlhbmcgTEVCSUggUkFQSQ0KZ2dwbG90KGRmX2xvbmcsIGFlcyh4ID0gbmlsYWksIGZpbGwgPSBmaXR1cikpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDMwLCBjb2xvciA9ICJ3aGl0ZSIsIGFscGhhID0gMC44LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArDQogIGZhY2V0X3dyYXAofmZpdHVyLCBzY2FsZXMgPSAiZnJlZSIsIG5jb2wgPSAzKSArICMgR3JpZCAzIGtvbG9tDQogIHNjYWxlX2ZpbGxfdmlyaWRpc19kKCkgKyAjIFdhcm5hIG90b21hdGlzDQogIHRoZW1lX21pbmltYWwoKSArDQogIGxhYnModGl0bGUgPSAiRGlzdHJpYnVzaSBGaXR1ciBBdWRpbyAoSGlzdG9ncmFtKSIsDQogICAgICAgc3VidGl0bGUgPSAiTWVsaWhhdCBwb2xhIHNlYmFyYW4gZGF0YTogTm9ybWFsIHZzIFNrZXdlZCIsDQogICAgICAgeCA9ICJOaWxhaSBGaXR1ciIsIHkgPSAiRnJla3VlbnNpIikgKw0KICB0aGVtZSgNCiAgDQogICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxLCBzaXplID0gOCksICMgTWlyaW5na2FuIGFuZ2thICYga2VjaWxrYW4NCiAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gOCksICMgS2VjaWxrYW4gYW5na2Egc3VtYnUgWQ0KICAgIHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICIjMURCOTU0IiksICMgSGlqYXUgU3BvdGlmeQ0KICAgIHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoY29sb3IgPSAid2hpdGUiLCBmYWNlID0gImJvbGQiLCBzaXplID0gMTApDQogICkNCmBgYA0KDQoqKkludGVycHJldGFzaSBWaXN1YWwgKEFuYWxpc2lzIE1lbmRhbGFtKToqKg0KDQoxLiAgKipEb21pbmFzaSBNdXNpayBFbGVrdHJvbmlrICgqU2tld2VkIERpc3RyaWJ1dGlvbnMqKToqKg0KICAgIFBlcmhhdGlrYW4gZ3JhZmlrICoqYGFjb3VzYCAoQWNvdXN0aWNuZXNzKSoqLCAqKmBzcGNoYCAoU3BlZWNoaW5lc3MpKiosIGRhbiAqKmBsaXZlYCAoTGl2ZW5lc3MpKiouIEtldGlnYW55YSBtZW1pbGlraSBkaXN0cmlidXNpIHlhbmcgc2FuZ2F0IGNvbmRvbmcga2Uga2lyaSAoKnJpZ2h0LXNrZXdlZCopLg0KICAgICogKkFydGlueWE6KiBNYXlvcml0YXMgbGFndSBwb3B1bGVyIGRla2FkZSAyMDEwLTIwMTkgbWVtaWxpa2kgbmlsYWkgYWt1c3RpayBkYW4gbGl2ZW5lc3MgeWFuZyBzYW5nYXQgcmVuZGFoLiBJbmkgbWVuY2VybWlua2FuIHRyZW4gaW5kdXN0cmkgbXVzaWsgbW9kZXJuIHlhbmcgZGlkb21pbmFzaSBvbGVoIHByb2R1a3NpIHN0dWRpbyBkaWdpdGFsIGRhbiBpbnN0cnVtZW4gZWxla3Ryb25paywgYnVrYW4gcmVrYW1hbiBrb25zZXIgYXRhdSBpbnN0cnVtZW4gYWt1c3RpayBtdXJuaS4NCg0KMi4gICoqS2FyYWt0ZXJpc3RpayBMYWd1IFBvcCAoKk5vcm1hbCBEaXN0cmlidXRpb25zKik6KioNCiAgICBHcmFmaWsgKipgZG5jZWAgKERhbmNlYWJpbGl0eSkqKiwgKipgbnJneWAgKEVuZXJneSkqKiwgZGFuICoqYHZhbGAgKFZhbGVuY2UpKiogY2VuZGVydW5nIG1lbWJlbnR1ayBrdXJ2YSBsb25jZW5nICgqbm9ybWFsIGRpc3RyaWJ1dGlvbiopIGF0YXUgc2VkaWtpdCBjb25kb25nIGtlIGthbmFuLg0KICAgICogKkFydGlueWE6KiBMYWd1LWxhZ3UgdG9wIGNoYXJ0IG1lbWlsaWtpIHZhcmlhc2kgeWFuZyBzZWltYmFuZy4gQWRhIGxhZ3Ugc2VkaWggKCpsb3cgdmFsZW5jZSopIGRhbiBiYWhhZ2lhICgqaGlnaCB2YWxlbmNlKiksIG5hbXVuIHJhdGEtcmF0YSBiZXJhZGEgZGkgbmlsYWkgdGVuZ2FoIHlhbmcgbW9kZXJhdC4gVGluZ2dpbnlhIHJhdGEtcmF0YSAqRW5lcmd5KiBkYW4gKkRhbmNlYWJpbGl0eSogbWVuZ29uZmlybWFzaSBiYWh3YSBsYWd1ICJoaXRzIiB1bXVtbnlhIGFkYWxhaCBsYWd1IHlhbmcgYmVyc2VtYW5nYXQuDQoNCjMuICAqKkltcGxpa2FzaSB1bnR1ayBDbHVzdGVyaW5nOioqDQogICAgS2FyZW5hIGJlYmVyYXBhIGZpdHVyIG1lbnllYmFyIG5vcm1hbCBkYW4geWFuZyBsYWluIHNhbmdhdCBtaXJpbmcgKCpza2V3ZWQqKSwgc2VydGEgbWVtaWxpa2kgcmVudGFuZyBhbmdrYSB5YW5nIGJlcmJlZGEgamF1aCAobGloYXQgc3VtYnUgWCBwYWRhIGBkdXJgIHZzIGBzcGNoYCksIG1ha2EgZGF0YSBpbmkgKipUSURBSyBCT0xFSCoqIGxhbmdzdW5nIGRpaGl0dW5nIGphcmFrbnlhLg0KICAgIA0KIyMgNi41LiBEZXRla3NpIE91dGxpZXIgKEJveHBsb3QgQ29sbGFnZSkNCg0KU2V0ZWxhaCBtZWxpaGF0IGRpc3RyaWJ1c2kgZGF0YSwgbGFuZ2thaCBzZWxhbmp1dG55YSBhZGFsYWggbWVuZGV0ZWtzaSBrZWJlcmFkYWFuICoqZGF0YSBwZW5jaWxhbiAoKm91dGxpZXIqKSoqLiAqT3V0bGllciogYWRhbGFoIG9ic2VydmFzaSB5YW5nIG5pbGFpbnlhIG1lbnlpbXBhbmcgamF1aCBkYXJpIHJhdGEtcmF0YSBkYXRhIGxhaW5ueWEuIERhbGFtIGtvbnRla3MgbXVzaWssIGluaSBiaXNhIGJlcnVwYSBsYWd1IHlhbmcgZHVyYXNpbnlhIHNhbmdhdCBwYW5qYW5nIGF0YXUgbGFndSBkZW5nYW4gbGlyaWsgeWFuZyBzYW5nYXQgcGFkYXQuDQoNClZpc3VhbGlzYXNpIG1lbmdndW5ha2FuICoqQm94cGxvdCoqIHNhbmdhdCBlZmVrdGlmIHVudHVrIHR1anVhbiBpbmkuIFRpdGlrLXRpdGlrIHlhbmcgYmVyYWRhIGRpIGx1YXIgImt1bWlzIiAoKndoaXNrZXJzKikgYm94cGxvdCBha2FuIGRpdGFuZGFpIGRlbmdhbiB3YXJuYSBtZXJhaC4NCg0KYGBge3IgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9MTB9DQojIFBsb3QgQm94cGxvdCBkZW5nYW4gcGVuYW5kYSBPdXRsaWVyIE1lcmFoDQpnZ3Bsb3QoZGZfbG9uZywgYWVzKHggPSBmaXR1ciwgeSA9IG5pbGFpLCBmaWxsID0gZml0dXIpKSArDQogIGdlb21fYm94cGxvdChhbHBoYSA9IDAuNywgc2hvdy5sZWdlbmQgPSBGQUxTRSwgDQogICAgICAgICAgICAgICBvdXRsaWVyLmNvbG91ciA9ICJyZWQiLCBvdXRsaWVyLnNoYXBlID0gMTksIG91dGxpZXIuc2l6ZSA9IDIpICsNCiAgZmFjZXRfd3JhcCh+Zml0dXIsIHNjYWxlcyA9ICJmcmVlIiwgbmNvbCA9IDMpICsNCiAgdGhlbWVfYncoKSArICMgTWVuZ2d1bmFrYW4gdGVtYSBoaXRhbS1wdXRpaCBhZ2FyIHRpdGlrIG1lcmFoIGtvbnRyYXMNCiAgbGFicyh0aXRsZSA9ICJEZXRla3NpIE91dGxpZXIgKEJveHBsb3QpIiwNCiAgICAgICBzdWJ0aXRsZSA9ICJUaXRpayBtZXJhaCBtZW5hbmRha2FuIGRhdGEgcGVuY2lsYW4gKG91dGxpZXIpIHlhbmcgZWtzdHJlbSIsDQogICAgICAgeCA9ICJGaXR1ciBBdWRpbyIsIHkgPSAiTmlsYWkiKSArDQogIHRoZW1lKA0KICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpLCAjIE1lbnllbWJ1bnlpa2FuIGxhYmVsIHN1bWJ1IFggYWdhciB0aWRhayBiZXJhbnRha2FuDQogICAgYXhpcy50aWNrcy54ID0gZWxlbWVudF9ibGFuaygpLA0KICAgIHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJibGFjayIpLCAjIEhlYWRlciB0aWFwIGtvdGFrIGJlcndhcm5hIGhpdGFtDQogICAgc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICJ3aGl0ZSIsIGZhY2UgPSAiYm9sZCIpICMgVGVrcyBoZWFkZXIgcHV0aWggdGViYWwNCiAgKQ0KYGBgDQoNCioqSW50ZXJwcmV0YXNpIE91dGxpZXI6KioNCkJlcmRhc2Fya2FuIHZpc3VhbGlzYXNpIGRpIGF0YXMsIGtpdGEgZGFwYXQgbWVuZ2lkZW50aWZpa2FzaSBiZWJlcmFwYSBhbm9tYWxpIGRhdGE6DQoNCiogKipEdXJhc2kgKGBkdXJgKToqKiBUZXJkYXBhdCBzZWp1bWxhaCB0aXRpayBtZXJhaCBkaSBiYWdpYW4gYXRhcy4gSW5pIG1lbmdpbmRpa2FzaWthbiBhZGFueWEgbGFndS1sYWd1IGRlbmdhbiBkdXJhc2kgeWFuZyBzYW5nYXQgcGFuamFuZyBkaWJhbmRpbmdrYW4gbWF5b3JpdGFzIGxhZ3UgcG9wIHN0YW5kYXIgKGJpYXNhbnlhIDMtNCBtZW5pdCkuDQoqICoqU3BlZWNoaW5lc3MgKGBzcGNoYCk6KiogQmFueWFrIHNla2FsaSAqb3V0bGllciogdGluZ2dpLiBJbmkga2VtdW5na2luYW4gYmVzYXIgbWVyZXByZXNlbnRhc2lrYW4gbGFndS1sYWd1IGJlcmdlbnJlICpIaXAtSG9wKiBhdGF1ICpSYXAqIHlhbmcgbWVtaWxpa2kgZGVuc2l0YXMga2F0YSBqYXVoIGxlYmloIHRpbmdnaSBkYXJpcGFkYSBsYWd1IHBvcCBtZWxvZGlzLg0KKiAqKlBvcHVsYXJpdGFzIChgcG9wYCk6KiogVGVyZGFwYXQgKm91dGxpZXIqIGRpIGJhZ2lhbiBiYXdhaCwgeWFuZyBtZW51bmp1a2thbiBhZGFueWEgbGFndS1sYWd1IHlhbmcgcGVybmFoIG1hc3VrIHRhbmdnYSBsYWd1IG5hbXVuIG1lbWlsaWtpIHNrb3IgcG9wdWxhcml0YXMgeWFuZyByZWxhdGlmIHJlbmRhaCBkaWJhbmRpbmdrYW4gcmVrYW4tcmVrYW5ueWEuDQoNCioqSW1wbGlrYXNpIE1ldG9kb2xvZ2lzOioqDQpLZWJlcmFkYWFuICpvdXRsaWVyKiB5YW5nIGN1a3VwIHNpZ25pZmlrYW4gaW5pIG1lbmphZGkgcGVyaW5nYXRhbiBiYWh3YSBtZXRvZGUgY2x1c3RlcmluZyB5YW5nIHNlbnNpdGlmIHRlcmhhZGFwIHBlbmNpbGFuIChzZXBlcnRpICpTaW5nbGUgTGlua2FnZSopIG11bmdraW4gYWthbiBtZW1iZXJpa2FuIGhhc2lsIHlhbmcga3VyYW5nIG9wdGltYWwgKGNlbmRlcnVuZyBtZW1iZW50dWsgKmNoYWluaW5nKikuIE9sZWgga2FyZW5hIGl0dSwgcGFkYSBzYWF0IG1lbmRhcHQga29uZGlkaSBzZXBlcnRpIGluaSBwZW5nZ3VuYWFuIG1ldG9kZSB5YW5nIGxlYmloICpyb2J1c3QqIHNlcGVydGkgKipBdmVyYWdlIExpbmthZ2UqKiBhdGF1ICoqV2FyZCdzIE1ldGhvZCoqIHNhbmdhdCBkaXNhcmFua2FuIHBhZGEgdGFoYXAgcGVtb2RlbGFuIG5hbnRpLg0KDQojIyA2LjYuIEtvcmVsYXNpIEFudGFyIEZpdHVyIChDb3JyZWxhdGlvbiBIZWF0bWFwKQ0KDQpUZXJha2hpciwga2l0YSBwZXJsdSBtZWxpaGF0IGh1YnVuZ2FuIGFudGFyIHZhcmlhYmVsLiBBcGFrYWggbGFndSB5YW5nIGtlbmNhbmcgKGBkQmApIHBhc3RpIGVuZXJnaW55YSAoYG5yZ3lgKSB0aW5nZ2k/IEtpdGEgYWthbiBjZWsgbWVuZ2d1bmFrYW4gKipNYXRyaWtzIEtvcmVsYXNpKiouDQoNCmBgYHtyIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTgsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpsaWJyYXJ5KGdnY29ycnBsb3QpIyAxLiBsaWJyYXJ5bnlhDQoNCiMgMi4gSGl0dW5nIE1hdHJpa3MgS29yZWxhc2kgKEhhbnlhIGtvbG9tIG51bWVyaWspDQojIE1lbmdndW5ha2FuIG5hbWEga29sb20gYWdhciBsZWJpaCBhbWFuIGRhcmlwYWRhIG5vbW9yIGluZGVrcw0KZGZfbnVtIDwtIGRmICU+JSBzZWxlY3QoYnBtLCBucmd5LCBkbmNlLCBkQiwgbGl2ZSwgdmFsLCBkdXIsIGFjb3VzLCBzcGNoLCBwb3ApDQpjb3JfbWF0cml4IDwtIGNvcihkZl9udW0pDQoNCiMgMy4gVmlzdWFsaXNhc2kgSGVhdG1hcA0KZ2djb3JycGxvdChjb3JfbWF0cml4LCANCiAgICAgICAgICAgbWV0aG9kID0gImNpcmNsZSIsICAgICAgICMgUGFrYWkgbGluZ2thcmFuIGJpYXIgdGlkYWsgc3VtcGVrDQogICAgICAgICAgIHR5cGUgPSAibG93ZXIiLCAgICAgICAgICAjIEN1bWEgdGFtcGlsa2FuIGJhZ2lhbiBiYXdhaCAoc2VnaXRpZ2EpIGJpYXIgcmFwaQ0KICAgICAgICAgICBsYWIgPSBUUlVFLCAgICAgICAgICAgICAgIyBUYW1waWxrYW4gYW5na2FueWENCiAgICAgICAgICAgbGFiX3NpemUgPSAzLCAgICAgICAgICAgICMgVWt1cmFuIGFuZ2thIHBhcyAodGlkYWsga2VrZWNpbGFuL2tlYmVzYXJhbikNCiAgICAgICAgICAgZGlnaXRzID0gMSwgICAgICAgICAgICAgICMgQ3VtYSAxIGFuZ2thIGRpIGJlbGFrYW5nIGtvbWEgKGJpYXIgcmluZ2thcykNCiAgICAgICAgICAgY29sb3JzID0gYygiI0U0MUExQyIsICJ3aGl0ZSIsICIjMzc3RUI4IiksICMgTWVyYWggLSBQdXRpaCAtIEJpcnUNCiAgICAgICAgICAgdGl0bGUgPSAiUGV0YSBLb3JlbGFzaSBGaXR1ciBBdWRpbyIsDQogICAgICAgICAgIGdndGhlbWUgPSB0aGVtZV9taW5pbWFsKCkpICsNCiAgICAgICAgICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgdmp1c3QgPSAxLCBoanVzdCA9IDEpKSAjIE1pcmluZ2thbiB0ZWtzDQpgYGANCg0KKipDYXJhIE1lbWJhY2EgSGVhdG1hcCAoSW50ZXJwcmV0YXNpIFNpbXBlbCk6KioNCg0KQmF5YW5na2FuIGluaSBzZXBlcnRpIHJhbWJ1IGxhbHUgbGludGFzOg0KDQoxLiAgKipMaW5na2FyYW4gQmlydSBCZXNhciAoUG9zaXRpZiAvIEJlcnRlbWFuKToqKg0KICAgICogKkFydGlueWE6KiAiS2FsYXUgQSBuYWlrLCBCIGlrdXQgbmFpay4iDQogICAgKiAqQ29udG9oOiogKipgbnJneWAgdnMgYGRCYCAoMC41KSoqLiBMaWhhdCBsaW5na2FyYW4gYmlydW55YSBjdWt1cCBiZXNhci4gSW5pIG1lbnVuanVra2FuIGJhaHdhIGxhZ3UgeWFuZyBtZW1pbGlraSBlbmVyZ2kgdGluZ2dpIGJpYXNhbnlhIGp1Z2EgZGlwcm9kdWtzaSBkZW5nYW4gdm9sdW1lIHN1YXJhIHlhbmcga2VyYXMuDQogICAgKiAqQ29udG9oOiogKipgdmFsYCB2cyBgZG5jZWAgKDAuNSkqKi4gQmVya29yZWxhc2kgcG9zaXRpZiwgeWFuZyBiZXJhcnRpIGxhZ3UgZGVuZ2FuIG51YW5zYSBjZXJpYSAoKmhhcHB5KikgY2VuZGVydW5nIG1lbWlsaWtpIHJpdG1lIHlhbmcgZW5hayB1bnR1ayBtZW5hcmkgKCpkYW5jZWFibGUqKS4NCg0KMi4gICoqTGluZ2thcmFuIE1lcmFoIEJlc2FyIChOZWdhdGlmIC8gQmVybXVzdWhhbik6KioNCiAgICAqICpBcnRpbnlhOiogIkthbGF1IEEgbmFpaywgQiBtYWxhaCB0dXJ1bi4iDQogICAgKiAqQ29udG9oOiogKipgYWNvdXNgIHZzIGBucmd5YCAoLTAuNikqKi4gTGluZ2thcmFuIG1lcmFobnlhIHBhbGluZyBiZXNhci4gSW5pIG1lbmdhcnRpa2FuIGh1YnVuZ2FuIHlhbmcgYmVydG9sYWsgYmVsYWthbmc7IHNlbWFraW4gYWt1c3RpayBzZWJ1YWggbGFndSwgbWFrYSB0aW5na2F0IGVuZXJnaW55YSBjZW5kZXJ1bmcgc2VtYWtpbiByZW5kYWggKGxhZ3Ugc2FudGFpKS4NCg0KMy4gICoqTGluZ2thcmFuIEtlY2lsL1B1dGloIChOZXRyYWwpOioqDQogICAgKiAqQXJ0aW55YToqICJUaWRhayBhZGEgaHVidW5nYW4geWFuZyBrdWF0LiINCiAgICAqICpDb250b2g6KiAqKmBwb3BgIChQb3B1bGFyaXRhcykqKiBoYW1waXIgYmVyd2FybmEgcHV0aWggdGVyaGFkYXAgc2VtdWEgZml0dXIgbGFpbi4gSW5pIHRlbXVhbiBtZW5hcmlrIHlhbmcgbWVudW5qdWtrYW4gYmFod2EgZml0dXIgdGVrbmlzIGxhZ3UgKHNlcGVydGkgdGVtcG8gYXRhdSBlbmVyZ2kpICoqdGlkYWsgbWVuamFtaW4qKiBzZWJ1YWggbGFndSBha2FuIG1lbmphZGkgcG9wdWxlci4gUG9wdWxhcml0YXMgbXVzaWsgZGlwZW5nYXJ1aGkgZmFrdG9yIGxhaW4gZGkgbHVhciBmaXR1ciBhdWRpbyBzZW1hdGEuDQogICAgDQotLS0NCiAgICANCiMgNy4gUHJhLXByb3NlcyBEYXRhIChQcmVwcm9jZXNzaW5nKQ0KDQpUYWhhcCBwcmEtcHJvc2VzIGFkYWxhaCBsYW5na2FoIGtyaXRpcyB1bnR1ayBtZW5qYW1pbiB2YWxpZGl0YXMgaGFzaWwgYW5hbGlzaXMuIEFsZ29yaXRtYSAqSGllcmFyY2hpY2FsIENsdXN0ZXJpbmcqIGJla2VyamEgZGVuZ2FuIG1lbmdoaXR1bmcgamFyYWsgYW50YXItdGl0aWsgZGF0YSwgc2VoaW5nZ2EgZGF0YSBpbnB1dCBoYXJ1cyBiZXJzaWgsIHVuaWssIGRhbiBtZW1pbGlraSBza2FsYSB5YW5nIHNldGFyYS4NCg0KIyMgNy4xLiBQZW1iZXJzaWhhbiBEYXRhIGRhbiBQZW5hbmdhbmFuIER1cGxpa2F0DQpEYXRhc2V0IG11c2lrIHNlcmluZ2thbGkgbWVtdWF0IGxhZ3UgeWFuZyBzYW1hIGxlYmloIGRhcmkgc2F0dSBrYWxpIChtaXNhbG55YSwgbGFndSB5YW5nIGJlcnRhaGFuIGRpIHRhbmdnYSBsYWd1IHNlbGFtYSBkdWEgdGFodW4gYmVydHVydXQtdHVydXQpLiBEdXBsaWthc2kgaW5pIGhhcnVzIGRpaGFwdXMgdW50dWsgbWVuZ2hpbmRhcmkgYmlhcy4gU2VsYWluIGl0dSwga2l0YSBwZXJsdSBtZW5nYXR1ciBpbmRla3MgYmFyaXMgKCpyb3cgbmFtZXMqKSBhZ2FyIHZpc3VhbGlzYXNpICpkZW5kcm9ncmFtKiBuYW50aW55YSBtZW5hbXBpbGthbiBuYW1hIGxhZ3UsIGJ1a2FuIG5vbW9yIHVydXQuDQoNCmBgYHtyfQ0KIyAxLiBNZW5naGFwdXMgRGF0YSBEdXBsaWthdA0KIyBLaXRhIGhhbnlhIG1lbmdhbWJpbCBrZW11bmN1bGFuIHBlcnRhbWEgZGFyaSBzZXRpYXAgbGFndSBiZXJkYXNhcmthbiBKdWR1bCBkYW4gQXJ0aXMNCmRmX2NsZWFuIDwtIGRmICU+JSBkaXN0aW5jdCh0aXRsZSwgYXJ0aXN0LCAua2VlcF9hbGwgPSBUUlVFKQ0KDQojIDIuIE1lbmphZGlrYW4gSnVkdWwgTGFndSBzZWJhZ2FpIEluZGV4IChMYWJlbCkNCiMgTWVuZ2dhYnVuZ2thbiBKdWR1bCBkYW4gQXJ0aXMgYWdhciBsYWJlbCBsZWJpaCBpbmZvcm1hdGlmDQojIEZ1bmdzaSBtYWtlLnVuaXF1ZSgpIG1lbWFzdGlrYW4gdGlkYWsgYWRhIG5hbWEgeWFuZyBzYW1hIHBlcnNpcyAoZXJyb3IgaGFuZGxpbmcpDQpyb3duYW1lcyhkZl9jbGVhbikgPC0gbWFrZS51bmlxdWUocGFzdGUoZGZfY2xlYW4kdGl0bGUsICItIiwgZGZfY2xlYW4kYXJ0aXN0KSkNCg0KIyBDZWsgZGltZW5zaSBkYXRhIHNldGVsYWggcGVtYmVyc2loYW4NCmRpbShkZl9jbGVhbikNCmBgYA0KDQoqKkFuYWxpc2lzIE91dHB1dDoqKg0KSGFzaWwgYDU4NyAxNWAgYXJ0aW55YSBzZWthcmFuZyBkYXRhIGtpdGEgdGluZ2dhbCAqKjU4NyBiYXJpcyAobGFndSkqKiBkYW4gKioxNSBrb2xvbSoqLg0KDQoqIEF3YWxueWEgYWRhIDYwMyBsYWd1Lg0KDQoqIFNla2FyYW5nIHNpc2EgNTg3IGxhZ3UuDQoNCiogQmVyYXJ0aSBraXRhIHN1a3NlcyBtZW1idWFuZyAqKjE2IGxhZ3Uga2VtYmFyIChkdXBsaWthdCkqKi4NCg0KU2VrYXJhbmcgZGF0YW55YSBzdWRhaCBiZXJzaWgsIHNldGlhcCBiYXJpcyBtZXdha2lsaSBzYXR1IGxhZ3UgeWFuZyB1bmlrLg0KDQojIyA3LjIuIFNlbGVrc2kgRml0dXIgKEZlYXR1cmUgU2VsZWN0aW9uKQ0KVGlkYWsgc2VtdWEgdmFyaWFiZWwgZGFsYW0gZGF0YXNldCByZWxldmFuIHVudHVrIHBlcmhpdHVuZ2FuIGphcmFrIG1hdGVtYXRpcy4gS2l0YSBoYXJ1cyBtZW5nZWxpbWluYXNpIHZhcmlhYmVsIGthdGVnb3JpayAodGVrcykgZGFuIHZhcmlhYmVsIG51bWVyaWsgeWFuZyBidWthbiBtZXJ1cGFrYW4gZml0dXIgYXVkaW8gKHNlcGVydGkgVGFodW4gYXRhdSBJRCkuDQoNCkJlcmlrdXQgYWRhbGFoIHByb3NlcyBwZW1pbGloYW4ga29sb20gZml0dXIgYXVkaW8gc2FqYSwgeWFuZyBkaXRhbXBpbGthbiBkYWxhbSB0YWJlbCBpbnRlcmFrdGlmIGFnYXIgZGFwYXQgZGlwZXJpa3NhIHNlY2FyYSBtZW55ZWx1cnVoLg0KDQpgYGB7cn0NCg0KIyAxLiBNZW1pbGloIGhhbnlhIGtvbG9tIGZpdHVyIGF1ZGlvIChOdW1lcmlrKQ0KIyBWYXJpYWJlbDogYnBtLCBucmd5LCBkbmNlLCBkQiwgbGl2ZSwgdmFsLCBkdXIsIGFjb3VzLCBzcGNoLCBwb3ANCmRmX3NlbGVjdCA8LSBkZl9jbGVhbiAlPiUgDQogIHNlbGVjdChicG0sIG5yZ3ksIGRuY2UsIGRCLCBsaXZlLCB2YWwsIGR1ciwgYWNvdXMsIHNwY2gsIHBvcCkNCg0KIyAyLiBNZW5hbXBpbGthbiBEYXRhIEhhc2lsIFNlbGVrc2kgKEludGVyYWN0aXZlIFRhYmxlKQ0KIyBNZW5nZ3VuYWthbiBkYXRhdGFibGUgYWdhciBzZW11YSBkYXRhIGJpc2EgZGlsaWhhdCAoZGktc2Nyb2xsKQ0KZGF0YXRhYmxlKGRmX3NlbGVjdCwgDQogICAgICAgICAgb3B0aW9ucyA9IGxpc3QoDQogICAgICAgICAgICBwYWdlTGVuZ3RoID0gNSwgICAgICAjIFRhbXBpbGthbiA1IGJhcmlzIHBlciBoYWxhbWFuIGJpYXIgcmFwaQ0KICAgICAgICAgICAgc2Nyb2xsWCA9IFRSVUUsICAgICAgIyBBa3RpZmthbiBnZXNlciBzYW1waW5nDQogICAgICAgICAgICBzZWFyY2hpbmcgPSBUUlVFICAgICAjIEFrdGlma2FuIGZpdHVyIHBlbmNhcmlhbg0KICAgICAgICAgICksDQogICAgICAgICAgY2FwdGlvbiA9ICJUYWJlbCBEYXRhIEZpdHVyIEF1ZGlvIChTaWFwIHVudHVrIFNjYWxpbmcpIg0KKQ0KYGBgDQoNCioqSW50ZXJwcmV0YXNpIFNlbGVrc2kgRGF0YToqKg0KDQpUYWJlbCBkaSBhdGFzIG1lbmFtcGlsa2FuIGRhdGEgZml0dXIgYXVkaW8geWFuZyBzdWRhaCBkaXNlbGVrc2kuDQoNCjEuICAqKlRlbnRhbmcgQW5na2E6KiogU2VtdWEga29sb20geWFuZyB0ZXJzaXNhIGFkYWxhaCAqKnZhcmlhYmVsIG51bWVyaWsqKiAoQlBNLCBFbmVyZ3ksIGRsbCkgeWFuZyBzaWFwIGRpb2xhaCBzZWNhcmEgbWF0ZW1hdGlzLg0KDQoyLiAgKipUZW50YW5nIEp1ZHVsIExhZ3U6KiogVGVrcyBqdWR1bCBsYWd1IHlhbmcgdGVybGloYXQgZGkgc2lzaSBwYWxpbmcga2lyaSAqKmJ1a2FubGFoIGtvbG9tIHZhcmlhYmVsKiosIG1lbGFpbmthbiAqKkxhYmVsIEJhcmlzICgqUm93IE5hbWVzKikqKi4NCg0KICAgICogTGFiZWwgaW5pICoqdGlkYWsgYWthbiBkaWhpdHVuZyoqIGRhbGFtIGthbGt1bGFzaSBqYXJhayBFdWNsaWRlYW4uDQogICAgDQogICAgKiBMYWJlbCBpbmkgc2VuZ2FqYSBkaXBlcnRhaGFua2FuIHNlYmFnYWkgcGVuYW5kYSBpZGVudGl0YXMgYWdhciBuYW50aSBzYWF0IHZpc3VhbGlzYXNpICpkZW5kcm9ncmFtKiB0ZXJiZW50dWssIGtpdGEgYmlzYSBtZW5nZXRhaHVpIGxhZ3UgbWFuYSB5YW5nIG1hc3VrIGtlIGRhbGFtIGNsdXN0ZXIgdGVydGVudHUuDQoNCkphZGksIGRhdGFzZXQgaW5pIHN1ZGFoICoqMTAwJSB2YWxpZCoqOiBoYW55YSBiZXJpc2kgYW5na2EgdW50dWsgcGVyaGl0dW5nYW4sIG5hbXVuIHRldGFwIG1lbWJhd2EgaWRlbnRpdGFzIGxhZ3UgdW50dWsgaW50ZXJwcmV0YXNpLg0KDQojIyA3LjMuIFN0YW5kYXJkaXNhc2kgRGF0YSAoWi1TY29yZSBTY2FsaW5nKQ0KU2ViYWdhaW1hbmEgdGVtdWFuIHBhZGEgdGFoYXAgRURBIChCYWIgNiksIHRlcmRhcGF0IHBlcmJlZGFhbiBza2FsYSB5YW5nIGVrc3RyZW0gYW50YXIgdmFyaWFiZWwgKGNvbnRvaDogRHVyYXNpIHJhdHVzYW4gdnMgU3BlZWNoaW5lc3Mgc2F0dWFuKS4gSmlrYSBkYXRhIG1lbnRhaCBpbmkgbGFuZ3N1bmcgZGlndW5ha2FuIGRhbGFtIHBlcmhpdHVuZ2FuICpFdWNsaWRlYW4gRGlzdGFuY2UqLCB2YXJpYWJlbCBkZW5nYW4gbmlsYWkgYmVzYXIgYWthbiBtZW5kb21pbmFzaSBoYXNpbCBrbGFzdGVyaXNhc2kgc2VjYXJhIHRpZGFrIGFkaWwuDQoNClNvbHVzaW55YSBhZGFsYWggbWVsYWt1a2FuICoqU3RhbmRhcmRpc2FzaSAoU2NhbGluZykqKiBtZW5nZ3VuYWthbiBtZXRvZGUgWi1TY29yZSwgeWFuZyBtZW5ndWJhaCBkaXN0cmlidXNpIGRhdGEgc2VoaW5nZ2EgbWVtaWxpa2kgcmF0YS1yYXRhIDAgZGFuIHN0YW5kYXIgZGV2aWFzaSAxLg0KDQokJHogPSBcZnJhY3t4IC0gXG11fXtcc2lnbWF9JCQNCg0KYGBge3J9DQojIDEuIE1lbGFrdWthbiBTY2FsaW5nDQojIEZ1bmdzaSBzY2FsZSgpIG90b21hdGlzIG1lbmdoaXR1bmcgWi1zY29yZSB1bnR1ayBzZXRpYXAga29sb20NCmRmX3NjYWxlZCA8LSBzY2FsZShkZl9zZWxlY3QpDQoNCiMgMi4gTWVuZ3ViYWggZm9ybWF0IGtlbWJhbGkgbWVuamFkaSBEYXRhIEZyYW1lDQojIChPdXRwdXQgc2NhbGUoKSBhZGFsYWggbWF0cml4LCBraXRhIHViYWggamFkaSBkYXRhIGZyYW1lIGJpYXIgcmFwaSkNCmRmX3NjYWxlZCA8LSBhcy5kYXRhLmZyYW1lKGRmX3NjYWxlZCkNCg0KIyAzLiBDZWsgU3RhdGlzdGlrIFNldGVsYWggU2NhbGluZw0KIyBQZXJoYXRpa2FuIGJhcmlzICJNZWFuIi4gU2VtdWFueWEgaGFydXMgYmVybmlsYWkgbm9sIChhdGF1IG1lbmRla2F0aSBub2wgbWlzYWw6IDEuMmUtMTYpDQpzdW1tYXJ5KGRmX3NjYWxlZCkNCmBgYA0KDQoqKkludGVycHJldGFzaSBPdXRwdXQgU2NhbGluZzoqKg0KDQpCZXJkYXNhcmthbiByaW5na2FzYW4gc3RhdGlzdGlrIGRpIGF0YXMsIHByb3NlcyBzdGFuZGFyZGlzYXNpIHRlbGFoICoqYmVyaGFzaWwgZGlsYWt1a2FuKiouIEhhbCBpbmkgZGlidWt0aWthbiBkZW5nYW4gZHVhIGluZGlrYXRvciB1dGFtYToNCg0KMS4gICoqTWVhbiAoUmF0YS1yYXRhKSBCZXJuaWxhaSAwOioqDQogICAgUGVyaGF0aWthbiBiYXJpcyBgTWVhbmAgcGFkYSBzZXRpYXAgdmFyaWFiZWwgKGBicG1gLCBgbnJneWAsIGBkbmNlYCwgZGxsKS4gU2VtdWFueWEgbWVudW5qdWtrYW4gYW5na2EgKiowLjAwMDAqKi4gSW5pIG1lbmdvbmZpcm1hc2kgYmFod2EgZGlzdHJpYnVzaSBkYXRhIHRlbGFoIGRpZ2VzZXIga2UgdGl0aWsgdGVuZ2FoIG5vbC4NCg0KMi4gICoqU2thbGEgU2VyYWdhbSAoSG9tb2dlbik6KioNCiAgICBTZWJlbHVtbnlhLCBgYnBtYCBiZXJuaWxhaSByYXR1c2FuIGRhbiBgZEJgIGJlcm5pbGFpIG5lZ2F0aWYuIFNla2FyYW5nLCBwZXJoYXRpa2FuIG5pbGFpIGBNaW5gIGRhbiBgTWF4YCBwYWRhIHNlbXVhIGtvbG9tLiBTZW11YW55YSBiZXJhZGEgZGFsYW0gcmVudGFuZyB5YW5nIHJlbGF0aWYgc2FtYSwgeWFpdHUgc2VraXRhciAqKi00IGhpbmdnYSArNCoqIChzdGFuZGFyIGRldmlhc2kpLg0KICAgICogKkNvbnRvaDoqIE5pbGFpIE1heCBgYnBtYCBhZGFsYWggMy41MSwgZGFuIG5pbGFpIE1heCBgZG5jZWAgYWRhbGFoIDIuNDIuIEtlZHVhbnlhIGtpbmkgInNlbGV2ZWwiLg0KDQpEYXRhIGtpbmkgYmVyc2lmYXQgKipvYmpla3RpZioqLiBUaWRhayBhZGEgbGFnaSB2YXJpYWJlbCB5YW5nIG1lbmRvbWluYXNpIGhhbnlhIGthcmVuYSBzYXR1YW5ueWEgYmVzYXIuIEphcmFrIGFudGFyLWxhZ3Uga2luaSBtdXJuaSBtZW5jZXJtaW5rYW4gcGVyYmVkYWFuIHBvbGEgYXVkaW8sIGJ1a2FuIHBlcmJlZGFhbiBzYXR1YW4gdWt1ci4NCg0KLS0tDQoNCiMgOC4gVGVrbmlrIFJlZHVrc2kgRGltZW5zaSAoUENBKQ0KDQpEYXRhc2V0IGluaSBtZW1pbGlraSAxMCB2YXJpYWJlbCBmaXR1ciBhdWRpbyAoZGltZW5zaSkuIFNlY2FyYSBtYXRlbWF0aXMsIG1lbGFrdWthbiAqY2x1c3RlcmluZyogcGFkYSBkaW1lbnNpIHRpbmdnaSBpdHUgc2FoLXNhaCBzYWphLCBuYW11biBtYW51c2lhIHRpZGFrIGJpc2EgbWVtdmlzdWFsaXNhc2lrYW4gZGF0YSBkYWxhbSAxMCBkaW1lbnNpLg0KDQpPbGVoIGthcmVuYSBpdHUsIHRla25payByZWR1a3NpIGRpbWVuc2kgc2VwZXJ0aSAqKlByaW5jaXBhbCBDb21wb25lbnQgQW5hbHlzaXMgKFBDQSkqKiBkaXBlcmx1a2FuLg0KKiAqKlR1anVhbjoqKiBNZW1wcm95ZWtzaWthbiBpbmZvcm1hc2kgZGFyaSAxMCB2YXJpYWJlbCBhc2xpIG1lbmphZGkgMiB2YXJpYWJlbCB1dGFtYSAoKlByaW5jaXBhbCBDb21wb25lbnRzKikgeWFuZyBtZXJhbmdrdW0gdmFyaWFuc2kgZGF0YSBzZWJhbnlhayBtdW5na2luLg0KKiAqKk1hbmZhYXQ6KiogTWVtdW5na2lua2FuIHZpc3VhbGlzYXNpIGhhc2lsICpjbHVzdGVyaW5nKiBkYWxhbSBncmFmaWsgMkQgKFN1bWJ1IFggZGFuIFkpIGRpIGJhYi1iYWIgc2VsYW5qdXRueWEuDQoNCiMjIDguMS4gSW1wbGVtZW50YXNpIFBDQQ0KS2l0YSBtZW5nZ3VuYWthbiBmdW5nc2kgYHByY29tcGAuIEthcmVuYSBkYXRhIHN1ZGFoIGRpbGFrdWthbiAqc2NhbGluZyogcGFkYSBCYWIgNyAob2JqZWsgYGRmX3NjYWxlZGApLCBraXRhIHRpZGFrIHBlcmx1IG1lbGFrdWthbiBzY2FsaW5nIHVsYW5nLg0KDQpgYGB7cn0NCiMgMS4gTWVuamFsYW5rYW4gUENBDQojIHNjYWxlLiA9IEZBTFNFIGthcmVuYSBkYXRhIGlucHV0IChkZl9zZWxlY3QpIHN1ZGFoIGtpdGEgc2NhbGUgZGkgQmFiIDcNCnBjYV9yZXN1bHQgPC0gcHJjb21wKGRmX3NlbGVjdCwgc2NhbGUuID0gVFJVRSkNCg0KIyAyLiBNZWxpaGF0IFJpbmdrYXNhbiBIYXNpbCBQQ0ENCnN1bW1hcnkocGNhX3Jlc3VsdCkNCmBgYA0KKipJbnRlcnByZXRhc2kgSGFzaWwgUENBIChJbXBvcnRhbmNlIG9mIENvbXBvbmVudHMpOioqDQoNClRhYmVsIGRpIGF0YXMgbWVudW5qdWtrYW4gc2ViZXJhcGEgYmVzYXIgc2V0aWFwIGtvbXBvbmVuIGJhcnUgKFByaW5jaXBhbCBDb21wb25lbnQpIG1hbXB1IG1lcmFuZ2t1bSBpbmZvcm1hc2kgZGFyaSAxMCB2YXJpYWJlbCBhdWRpbyBhc2xpLg0KDQoxLiAgKipNZW5nYXBhIGFkYSBQQzEgcy9kIFBDMTA/KioNCiAgICBKdW1sYWgga29tcG9uZW4gKFBDKSB5YW5nIHRlcmJlbnR1ayBhZGFsYWggMTAsIHNhbWEgcGVyc2lzIGRlbmdhbiBqdW1sYWggdmFyaWFiZWwgaW5wdXQgKGBicG1gLCBgbnJneWAsIGBkbmNlYCwgZHN0KS4gUENBIHRpZGFrIG1lbWJ1YW5nIGRhdGEsIG1lbGFpbmthbiBtZW55dXN1biB1bGFuZyBkYXRhIHRlcnNlYnV0IGRhcmkgeWFuZyBwYWxpbmcgcGVudGluZyAoUEMxKSBrZSB5YW5nIHBhbGluZyB0aWRhayBwZW50aW5nIChQQzEwKS4NCg0KDQoyLiAgKipQcm9wb3JzaSBWYXJpYW5zaSAoUHJvcG9ydGlvbiBvZiBWYXJpYW5jZSk6KioNCiAgICAqICoqUEMxICgwLjI1MjMpOioqIE1lbnlpbXBhbiAqKjI1LjIzJSoqIGRhcmkgdG90YWwgaW5mb3JtYXNpIGRhdGEuIEluaSBhZGFsYWgga29tcG9uZW4gdGVycGVudGluZy4NCiAgICAqICoqUEMyICgwLjE0MjkpOioqIE1lbnlpbXBhbiAqKjE0LjI5JSoqIGluZm9ybWFzaS4NCiAgICAqICoqUEMxMCAoMC4wMjQzNSk6KiogSGFueWEgbWVueWltcGFuICoqMi40JSoqIGluZm9ybWFzaS4gSW5pIGRpYW5nZ2FwIHNlYmFnYWkgKm5vaXNlKiBhdGF1IGRldGFpbCB5YW5nIHRpZGFrIHNpZ25pZmlrYW4uDQogICAgDQoNCjMuICAqKlByb3BvcnNpIEt1bXVsYXRpZiAoQ3VtdWxhdGl2ZSBQcm9wb3J0aW9uKToqKg0KICAgICogRm9rdXMga2l0YSBhZGFsYWggcGFkYSBnYWJ1bmdhbiAqKlBDMSArIFBDMioqLg0KICAgICogTmlsYWkga3VtdWxhdGlmbnlhIGFkYWxhaCAqKjAuMzk1MioqIGF0YXUgc2VraXRhciAqKjQwJSoqLg0KICAgICogKipLZXNpbXB1bGFuOioqIE1lc2tpcHVuIGtpdGEgbWVyZWR1a3NpIGRhdGEgZGFyaSAxMCBkaW1lbnNpIG1lbmphZGkgMiBkaW1lbnNpIChQQzEgJiBQQzIpIHVudHVrIHZpc3VhbGlzYXNpIG5hbnRpLCBncmFmaWsgMkQgdGVyc2VidXQgbWFzaWggbWFtcHUgbWVyZXByZXNlbnRhc2lrYW4gaGFtcGlyIDQwJSBrYXJha3RlcmlzdGlrIGFzbGkgbGFndS1sYWd1IHRlcnNlYnV0LiBBbmdrYSBpbmkgY3VrdXAgcmVwcmVzZW50YXRpZiB1bnR1ayBtZWxpaGF0IHBvbGEgcGVuZ2Vsb21wb2thbiBnbG9iYWwuDQoNCg0KDQojIyA4LjIuIFZpc3VhbGlzYXNpIFNjcmVlIFBsb3QgKFBlcnNlbnRhc2UgSW5mb3JtYXNpKQ0KVW50dWsgbWVtdmFsaWRhc2kga2VwdXR1c2FuIGtpdGEgbWVuZ2d1bmFrYW4gMiBkaW1lbnNpLCBraXRhIGd1bmFrYW4gU2NyZWUgUGxvdC4gR3JhZmlrIGluaSBtZW51bmp1a2thbiBiZXJhcGEgcGVyc2VuIGluZm9ybWFzaSB5YW5nIGRpdGFuZ2thcCBvbGVoIHNldGlhcCBrb21wb25lbi4NCg0KYGBge3J9DQojIFZpc3VhbGlzYXNpIFNjcmVlIFBsb3QgbWVuZ2d1bmFrYW4gbGlicmFyeSAnZmFjdG9leHRyYScNCmZ2aXpfZWlnKHBjYV9yZXN1bHQsIA0KICAgICAgICAgYWRkbGFiZWxzID0gVFJVRSwgICAgICAgIyBUYW1waWxrYW4gYW5na2EgcGVyc2VudGFzZQ0KICAgICAgICAgeWxpbSA9IGMoMCwgNTApLCAgICAgICAgIyBCYXRhcyBzdW1idSBZIGFnYXIgZ3JhZmlrIHJhcGkNCiAgICAgICAgIGJhcmZpbGwgPSAiIzFEQjk1NCIsICAgICMgV2FybmEgSGlqYXUgU3BvdGlmeQ0KICAgICAgICAgYmFyY29sb3IgPSAiYmxhY2siLA0KICAgICAgICAgbWFpbiA9ICJTY3JlZSBQbG90OiBQZXJzZW50YXNlIEluZm9ybWFzaSBwZXIgRGltZW5zaSIpDQpgYGANCg0KKipJbnRlcnByZXRhc2kgU2NyZWUgUGxvdDoqKg0KDQoqICoqRGltZW5zaSAxIChQQzEpOioqIE1lbnlpbXBhbiB2YXJpYW5zaSBpbmZvcm1hc2kgdGVyYmVzYXIgKHNlcGVydGkgeWFuZyB0ZXJsaWhhdCBwYWRhIGJhbG9rIHRlcnRpbmdnaSkuDQoqICoqRGltZW5zaSAyIChQQzIpOioqIE1lbnlpbXBhbiB2YXJpYW5zaSBpbmZvcm1hc2kgdGVyYmVzYXIga2VkdWEuDQoqICoqS2VzaW1wdWxhbjoqKiBHYWJ1bmdhbiBQQzEgZGFuIFBDMiBha2FuIGRpZ3VuYWthbiBzZWJhZ2FpIHN1bWJ1IFggZGFuIFkgcGFkYSBwbG90IHZpc3VhbGlzYXNpIGtsYXN0ZXIgbmFudGkuIE1lc2tpcHVuIHRpZGFrIG1lbmFuZ2thcCAxMDAlIGluZm9ybWFzaSwgaW5pIGFkYWxhaCBwZW5kZWthdGFuIHZpc3VhbCB0ZXJiYWlrIHVudHVrIG1lbnllZGVyaGFuYWthbiBkYXRhIG11bHRpZGltZW5zaSBhZ2FyIGRhcGF0IGRpbGloYXQgb2xlaCBtYXRhIG1hbnVzaWEuDQoNCiMjIDguMy4gVmlzdWFsaXNhc2kgQmlwbG90IChQZXRhIEtvbnRyaWJ1c2kgVmFyaWFiZWwpDQpTZWJlbHVtIG1lbGloYXQgcGVuZ2Vsb21wb2thbiBsYWd1LCBraXRhIHBlcmx1IG1lbWFoYW1pICJwZXRhIiB2YXJpYWJlbG55YS4gVmFyaWFiZWwgbWFuYSB5YW5nIG1lbmFyaWsgZGF0YSBrZSBrYW5hbiAoUEMxIHBvc2l0aWYpIGF0YXUga2UgYXRhcyAoUEMyIHBvc2l0aWYpPw0KDQpgYGB7cn0NCiMgVmlzdWFsaXNhc2kgQmlwbG90IFZhcmlhYmVsDQpmdml6X3BjYV92YXIocGNhX3Jlc3VsdCwNCiAgICAgICAgICAgICBjb2wudmFyID0gImNvbnRyaWIiLCAjIFdhcm5hIHBhbmFoIGJlcmRhc2Fya2FuIGJlc2FybnlhIGtvbnRyaWJ1c2kNCiAgICAgICAgICAgICBncmFkaWVudC5jb2xzID0gYygiZ3JleSIsICJibHVlIiwgInJlZCIpLCAjIE1lcmFoID0gS29udHJpYnVzaSBQYWxpbmcgQmVzYXINCiAgICAgICAgICAgICByZXBlbCA9IFRSVUUsICAgICAgICAjIEFnYXIgdGVrcyBsYWJlbCB0aWRhayBiZXJ0dW1wdWsNCiAgICAgICAgICAgICB0aXRsZSA9ICJQQ0EgQmlwbG90OiBQZXRhIEtvbnRyaWJ1c2kgRml0dXIgQXVkaW8iDQogICAgICAgICAgICAgKQ0KYGBgDQoNCioqSW50ZXJwcmV0YXNpIEJpcGxvdDoqKg0KDQpHcmFmaWsgaW5pIGJlcmZ1bmdzaSBzZWJhZ2FpICJrb21wYXMiIHVudHVrIG1lbWJhY2EgaGFzaWwgKmNsdXN0ZXJpbmcqIG5hbnRpOg0KDQoxLiAgKipQYW5haCBCZXJkZWthdGFuIChLb3JlbGFzaSBQb3NpdGlmKToqKg0KICAgICogVmFyaWFiZWwgKipgbnJneWAqKiAoRW5lcmdpKSBkYW4gKipgZEJgKiogKExvdWRuZXNzKSBhcmFobnlhIHNhbmdhdCBiZXJkZWthdGFuLiBBcnRpbnlhLCBsYWd1IHlhbmcgYmVydGVuYWdhIGhhbXBpciBwYXN0aSBtZW1pbGlraSB2b2x1bWUgeWFuZyBrZXJhcy4NCiAgICAqIFZhcmlhYmVsICoqYHZhbGAqKiAoVmFsZW5jZS9IYXBweSkgZGFuICoqYGRuY2VgKiogKERhbmNlYWJpbGl0eSkganVnYSBzZWFyYWgsIG1lbmFuZGFrYW4gbGFndSB5YW5nIGNlcmlhIGNlbmRlcnVuZyBlbmFrIHVudHVrIGRpcGFrYWkgbWVuYXJpLg0KDQoyLiAgKipQYW5haCBCZXJsYXdhbmFuIChLb3JlbGFzaSBOZWdhdGlmKToqKg0KICAgICogVmFyaWFiZWwgKipgYWNvdXNgKiogKEFrdXN0aWspIHBhbmFobnlhIGJlcmxhd2FuYW4gYXJhaCBkZW5nYW4gKipgbnJneWAqKi4NCiAgICAqICpJbXBsaWthc2k6KiBJbmkgbWVudW5qdWtrYW4gcGVtaXNhaGFuIHpvbmEgeWFuZyBqZWxhcy4gQXJlYSAqKmtpcmkqKiBncmFmaWsgbmFudGkgYWRhbGFoICJab25hIEFrdXN0aWsiIChtdXNpayBzYW50YWkpLCBzZWRhbmdrYW4gYXJlYSAqKmthbmFuKiogYWRhbGFoICJab25hIEVuZXJnaSBUaW5nZ2kiIChtdXNpayBrZXJhcykuDQoNCjMuICAqKlBhbmFoIFRlZ2FrIEx1cnVzIChUaWRhayBCZXJrb3JlbGFzaSk6KioNCiAgICAqIFZhcmlhYmVsICoqYGR1cmAqKiAoRHVyYXNpKSBhcmFobnlhIGhhbXBpciB0ZWdhayBsdXJ1cyAoOTAgZGVyYWphdCkgdGVyaGFkYXAgKipgdmFsYCoqLg0KICAgICogKkltcGxpa2FzaToqIFBhbmphbmcgYXRhdSBwZW5kZWtueWEgZHVyYXNpIGxhZ3UgdGlkYWsgbWVtcGVuZ2FydWhpIG1vb2QgbGFndSB0ZXJzZWJ1dCAoYXBha2FoIHNlZGloIGF0YXUgc2VuYW5nKS4NCg0KLS0tDQoNCiMgOS4gSW1wbGVtZW50YXNpIEFsZ29yaXRtYSAoRml0dGluZyBNb2RlbCkNCg0KUGFkYSB0YWhhcCBpbmksIGtpdGEgYWthbiBtZW5lcmFwa2FuIGFsZ29yaXRtYSAqSGllcmFyY2hpY2FsIENsdXN0ZXJpbmcqIHBhZGEgZGF0YSB5YW5nIHRlbGFoIGRpcmVkYWtzaSAoUENBKSBkYW4gZGlzdGFuZGFyaXNhc2kuIFNlc3VhaSBkZW5nYW4gc3Blc2lmaWthc2kgdHVnYXMsIGtpdGEgYWthbiBtZW1iYW5kaW5na2FuIGR1YSBwZW5kZWthdGFuIHV0YW1hOg0KMS4gICoqQWdnbG9tZXJhdGl2ZSBDbHVzdGVyaW5nOioqIE1lbmdndW5ha2FuIG1ldG9kZSAqU2luZ2xlKiwgKkNvbXBsZXRlKiwgKkF2ZXJhZ2UqLCBkYW4gKldhcmQgTGlua2FnZSouDQoyLiAgKipEaXZpc2l2ZSBDbHVzdGVyaW5nOioqIE1lbmdndW5ha2FuIGFsZ29yaXRtYSBESUFOQSAoKkRpdmlzaXZlIEFuYWx5c2lzKikuDQoNClNlbGFpbiBzZWthZGFyIG1lbmphbGFua2FuIGFsZ29yaXRtYSwga2l0YSBqdWdhIGFrYW4gbWVuZ2hpdHVuZyAqKktvZWZpc2llbiBLb3BoZW5ldGlrICgqQ29waGVuZXRpYyBDb3JyZWxhdGlvbiBDb2VmZmljaWVudCopKiogdW50dWsgbWVuZ3VrdXIgc2ViZXJhcGEgYmFpayBzZXRpYXAgcG9ob24gKCpkZW5kcm9ncmFtKikgZGFsYW0gbWVyZXByZXNlbnRhc2lrYW4gamFyYWsgYXNsaSBhbnRhci1kYXRhLg0KDQojIyA5LjEuIFBlcmhpdHVuZ2FuIE1hdHJpa3MgSmFyYWsNCkxhbmdrYWggcGVydGFtYSB5YW5nIGZ1bmRhbWVudGFsIGFkYWxhaCBtZW5naGl0dW5nIGphcmFrIGFudGFyIHNldGlhcCBwYXNhbmdhbiBsYWd1IG1lbmdndW5ha2FuICoqRXVjbGlkZWFuIERpc3RhbmNlKiouDQoNCmBgYHtyfQ0KIyBNZW5naGl0dW5nIG1hdHJpa3MgamFyYWsgYW50YXIgb2JzZXJ2YXNpDQojIE1lbmdndW5ha2FuIGRhdGEgaGFzaWwgc2NhbGluZyAoZGZfc2NhbGVkKSBkYXJpIEJhYiA3DQpkaXN0X21hdCA8LSBkaXN0KGRmX3NjYWxlZCwgbWV0aG9kID0gImV1Y2xpZGVhbiIpDQoNCiMgTWVuYW1waWxrYW4gZGltZW5zaSBtYXRyaWtzIChoYXJ1cyA1ODd4NTg3KQ0KcHJpbnQoZGltKGFzLm1hdHJpeChkaXN0X21hdCkpKQ0KYGBgDQoNCioqSW50ZXJwcmV0YXNpIE1hdHJpa3MgSmFyYWs6KioNCg0KT3V0cHV0IGRpbWVuc2kgYFsxXSA1ODcgNTg3YCBtZW5nb25maXJtYXNpIHRpZ2EgaGFsIHBlbnRpbmc6DQoNCjEuICAqKk1hdHJpa3MgUGVyc2VnaSBTaW1ldHJpczoqKiBKdW1sYWggYmFyaXMgZGFuIGtvbG9tIHNhbWEgcGVyc2lzICg1ODcpLiBJbmkgYmVyYXJ0aSBraXRhIHRlbGFoIGJlcmhhc2lsIG1lbmdoaXR1bmcgamFyYWsgKnBhaXJ3aXNlKiBhbnRhcmEgc2V0aWFwIGxhZ3UgZGVuZ2FuIHNlbHVydWggbGFndSBsYWlubnlhIGRhbGFtIGRhdGFzZXQuDQoNCjIuICAqKktvbnNpc3RlbnNpIERhdGE6KiogQW5na2EgKio1ODcqKiBzZXN1YWkgZGVuZ2FuIGp1bWxhaCBsYWd1IHVuaWsgeWFuZyB0ZXJzaXNhIHNldGVsYWggdGFoYXAgcGVtYmVyc2loYW4gZGF0YSAoQmFiIDcuMSkuDQoNCjMuICAqKktlc2lhcGFuIElucHV0OioqIE1hdHJpa3MgamFyYWsgaW5pbGFoIHlhbmcgbWVuamFkaSAiYmFoYW4gYmFrdSIgdXRhbWEgeWFuZyBha2FuIGRpbWFzdWtrYW4ga2UgZGFsYW0gZnVuZ3NpIGBoY2x1c3RgIHVudHVrIG1lbWJhbmd1biBzdHJ1a3R1ciBwb2hvbiBwYWRhIGxhbmdrYWggc2VsYW5qdXRueWEuDQoNCiMjIDkuMi4gRml0dGluZyBBZ2dsb21lcmF0aXZlIENsdXN0ZXJpbmcNCkluaSBha2FuIG1lbWJhbmd1biA0IG1vZGVsIGJlcmJlZGEgYmVyZGFzYXJrYW4gbWV0b2RlIGxpbmthZ2UuDQoNCmBgYHtyfQ0KIyAxLiBTaW5nbGUgTGlua2FnZSAoTmVhcmVzdCBOZWlnaGJvcikNCmhjX3NpbmdsZSA8LSBoY2x1c3QoZGlzdF9tYXQsIG1ldGhvZCA9ICJzaW5nbGUiKQ0KDQojIDIuIENvbXBsZXRlIExpbmthZ2UgKEZhcnRoZXN0IE5laWdoYm9yKQ0KaGNfY29tcGxldGUgPC0gaGNsdXN0KGRpc3RfbWF0LCBtZXRob2QgPSAiY29tcGxldGUiKQ0KDQojIDMuIEF2ZXJhZ2UgTGlua2FnZSAoVVBHTUEpDQpoY19hdmcgPC0gaGNsdXN0KGRpc3RfbWF0LCBtZXRob2QgPSAiYXZlcmFnZSIpDQoNCiMgNC4gV2FyZCdzIE1ldGhvZCAoTWluaW11bSBWYXJpYW5jZSkNCiMgQ2F0YXRhbjogRGkgUiwgJ3dhcmQuRDInIGFkYWxhaCBpbXBsZW1lbnRhc2kgc3RhbmRhciBXYXJkDQpoY193YXJkIDwtIGhjbHVzdChkaXN0X21hdCwgbWV0aG9kID0gIndhcmQuRDIiKQ0KYGBgDQoNCioqSW50ZXJwcmV0YXNpIEZpdHRpbmcgTW9kZWw6KioNCg0KUGFkYSB0YWhhcCBpbmksIGtpdGEgdGlkYWsgaGFueWEgbWVuamFsYW5rYW4gc2F0dSBhbGdvcml0bWEsIG1lbGFpbmthbiBtZW1iYW5ndW4gKio0IG1vZGVsIHNrZW5hcmlvIGNsdXN0ZXJpbmcqKiBzZWthbGlndXMuIFR1anVhbm55YSBhZGFsYWggdW50dWsgbWVtYmFuZGluZ2thbiBiYWdhaW1hbmEgc3RydWt0dXIgcG9ob24gKCpkZW5kcm9ncmFtKikgdGVyYmVudHVrIGppa2EgbWVuZ2d1bmFrYW4gYXR1cmFuIGphcmFrICgqbGlua2FnZSopIHlhbmcgYmVyYmVkYToNCg0KMS4gICoqU2luZ2xlIExpbmthZ2U6KiogTWVuZ2dhYnVuZ2thbiBiZXJkYXNhcmthbiBqYXJhayB0ZXJkZWthdCAocmF3YW4gKmNoYWluaW5nIGVmZmVjdCopLg0KMi4gICoqQ29tcGxldGUgTGlua2FnZToqKiBNZW5nZ2FidW5na2FuIGJlcmRhc2Fya2FuIGphcmFrIHRlcmphdWggKGNlbmRlcnVuZyBtZW5naGFzaWxrYW4ga2xhc3RlciBwYWRhdCkuDQozLiAgKipBdmVyYWdlIExpbmthZ2U6KiogTWVuZ2d1bmFrYW4gcmF0YS1yYXRhIGphcmFrIChrb21wcm9taSBhbnRhcmEgU2luZ2xlIGRhbiBDb21wbGV0ZSkuDQo0LiAgKipXYXJkJ3MgTWV0aG9kOioqIE1lbWluaW1hbGthbiB2YXJpYW4gZGFsYW0ga2xhc3RlciAoY2VuZGVydW5nIG1lbmdoYXNpbGthbiB1a3VyYW4ga2xhc3RlciB5YW5nIHNlcmFnYW0pLg0KDQpPYmplayBgaGNfc2luZ2xlYCwgYGhjX2NvbXBsZXRlYCwgYGhjX2F2Z2AsIGRhbiBgaGNfd2FyZGAga2luaSBtZW55aW1wYW4gc2VsdXJ1aCByaXdheWF0IHBlbmdnYWJ1bmdhbiBkYXRhIHlhbmcgc2lhcCB1bnR1ayBkaXZpc3VhbGlzYXNpa2FuLg0KDQojIyA5LjMuIEZpdHRpbmcgRGl2aXNpdmUgQ2x1c3RlcmluZyAoRElBTkEpDQpQZW5kZWthdGFuIHRvcC1kb3duIG1lbmdndW5ha2FuIGFsZ29yaXRtYSBESUFOQS4NCg0KYGBge3J9DQojIE1lbmphbGFua2FuIGFsZ29yaXRtYSBESUFOQQ0KIyBLaXRhIHNpbXBhbiBoYXNpbG55YSBkYWxhbSBmb3JtYXQgaGNsdXN0IGFnYXIgYmlzYSBkaWJhbmRpbmdrYW4gbmFudGkNCmhjX2RpdmlzaXZlIDwtIGRpYW5hKGRmX3NjYWxlZCkNCmBgYA0KDQoqKkludGVycHJldGFzaSBNb2RlbCBEaXZpc2l2ZSAoRElBTkEpOioqDQoNCkJlcmJlZGEgZGVuZ2FuIHBlbmRla2F0YW4gKmFnZ2xvbWVyYXRpdmUqIHNlYmVsdW1ueWEsIGZ1bmdzaSBgZGlhbmEoKWAgbWVuZXJhcGthbiBhbGdvcml0bWEgKipEaXZpc2l2ZSBDbHVzdGVyaW5nKiogKFRvcC1Eb3duKS4NCg0KMS4gICoqSW5pc2lhbGlzYXNpOioqIEFsZ29yaXRtYSBtZW11bGFpIGRlbmdhbiBtZW5nYXN1bXNpa2FuIHNlbHVydWggNTg3IGxhZ3UgYmVyYWRhIGRhbGFtIHNhdHUga2xhc3RlciByYWtzYXNhICgqcm9vdCopLg0KMi4gICoqUHJvc2VzIFBlbWVjYWhhbjoqKiBQYWRhIHNldGlhcCBsYW5na2FoLCBhbGdvcml0bWEgbWVuY2FyaSBrbGFzdGVyIGRlbmdhbiBkaWFtZXRlciB0ZXJiZXNhciAocGFsaW5nIHRpZGFrIHNlcmFnYW0pIGRhbiBtZW1lY2FobnlhICgqc3BsaXR0aW5nKikgbWVuamFkaSBkdWEgc3ViLWtsYXN0ZXIuDQozLiAgKipIYXNpbDoqKiBPYmplayBgaGNfZGl2aXNpdmVgIGtpbmkgbWVueWltcGFuIHN0cnVrdHVyIGhpZXJhcmtpIHBlbWVjYWhhbiB0ZXJzZWJ1dC4NCg0KS2l0YSBqdWdhIGRhcGF0IG1lbGloYXQgbmlsYWkgKipEaXZpc2l2ZSBDb2VmZmljaWVudCAoREMpKiogZGVuZ2FuIG1lbmdldGlrIGBoY19kaXZpc2l2ZSRkY2AuIE5pbGFpIGluaSAoYmlhc2FueWEgMC0xKSBtZW51bmp1a2thbiBzZWJlcmFwYSBrdWF0IHN0cnVrdHVyIHBlbmdlbG9tcG9rYW4geWFuZyBkaXRlbXVrYW4uIFNlbWFraW4gbWVuZGVrYXRpIDEsIHNlbWFraW4gamVsYXMgc3RydWt0dXIga2xhc3Rlcm55YS4NCg0KIyMgOS40LiBFdmFsdWFzaSBLdWFsaXRhcyBQb2hvbiAoQ29waGVuZXRpYyBDb3JyZWxhdGlvbikNCkluaSBhZGFsYWggbGFuZ2thaCB2YWxpZGFzaSBzdGF0aXN0aWsuIEtpdGEgaW5naW4gbWVuZ2V0YWh1aSBtZXRvZGUgbWFuYSB5YW5nIHBhbGluZyBzZXRpYSBtZW1wZXJ0YWhhbmthbiBqYXJhayBhc2xpIGRhdGEgZGFsYW0gYmVudHVrIHN0cnVrdHVyIHBvaG9uLg0KDQpOaWxhaSBLb2VmaXNpZW4gS29waGVuZXRpayBiZXJraXNhciBhbnRhcmEgMCBoaW5nZ2EgMToNCg0KKiAqKk5pbGFpIG1lbmRla2F0aSAxOioqIFNhbmdhdCBiYWlrIChEZW5kcm9ncmFtIGFrdXJhdCBtZXJlcHJlc2VudGFzaWthbiBrZW1pcmlwYW4gZGF0YSkuDQoNCiogKipOaWxhaSBtZW5kZWthdGkgMDoqKiBCdXJ1ayAoRGVuZHJvZ3JhbSBtZW5kaXN0b3JzaSBqYXJhayBhc2xpKS4NCg0KYGBge3J9DQojIEZ1bmdzaSBzZWRlcmhhbmEgdW50dWsgbWVuZ2hpdHVuZyBrb3JlbGFzaSBrb3BoZW5ldGlrDQpoaXR1bmdfY29waGVuZXRpYyA8LSBmdW5jdGlvbihoY19tb2RlbCwgZGlzdF9hc2xpKSB7DQogIGRlbmRfZGlzdCA8LSBjb3BoZW5ldGljKGhjX21vZGVsKQ0KICBjb3IoZGlzdF9hc2xpLCBkZW5kX2Rpc3QpDQp9DQoNCiMgTWVuZ2hpdHVuZyBza29yIHVudHVrIHNldGlhcCBtZXRvZGUNCiMgKFBhc3Rpa2FuIG9iamVrIGhjX3NpbmdsZSwgaGNfd2FyZCwgZGxsIHN1ZGFoIGRpYnVhdCBkaSB0YWhhcCBzZWJlbHVtbnlhKQ0Kc2NvcmVfc2luZ2xlICAgPC0gaGl0dW5nX2NvcGhlbmV0aWMoaGNfc2luZ2xlLCBkaXN0X21hdCkNCnNjb3JlX2NvbXBsZXRlIDwtIGhpdHVuZ19jb3BoZW5ldGljKGhjX2NvbXBsZXRlLCBkaXN0X21hdCkNCnNjb3JlX2F2ZXJhZ2UgIDwtIGhpdHVuZ19jb3BoZW5ldGljKGhjX2F2ZywgZGlzdF9tYXQpDQpzY29yZV93YXJkICAgICA8LSBoaXR1bmdfY29waGVuZXRpYyhoY193YXJkLCBkaXN0X21hdCkNCg0KIyBLaHVzdXMgdW50dWsgRGl2aXNpdmUgKERJQU5BKSwgcGVybHUgZGlrb252ZXJzaSBrZSBmb3JtYXQgaGNsdXN0IGR1bHUNCnNjb3JlX2RpdmlzaXZlIDwtIGhpdHVuZ19jb3BoZW5ldGljKGFzLmhjbHVzdChoY19kaXZpc2l2ZSksIGRpc3RfbWF0KQ0KDQojIE1lbWJ1YXQgVGFiZWwgUGVyYmFuZGluZ2FuDQpjb21wYXJpc29uX2RmIDwtIGRhdGEuZnJhbWUoDQogIE1ldG9kZSA9IGMoIlNpbmdsZSBMaW5rYWdlIiwgIkNvbXBsZXRlIExpbmthZ2UiLCAiQXZlcmFnZSBMaW5rYWdlIiwgIldhcmQncyBNZXRob2QiLCAiRGl2aXNpdmUgKERJQU5BKSIpLA0KICBTa29yX0NvcGhlbmV0aWMgPSBjKHNjb3JlX3NpbmdsZSwgc2NvcmVfY29tcGxldGUsIHNjb3JlX2F2ZXJhZ2UsIHNjb3JlX3dhcmQsIHNjb3JlX2RpdmlzaXZlKQ0KKQ0KDQojIE1lbmFtcGlsa2FuIFRhYmVsIFVydXQgZGFyaSBTa29yIFRlcnRpbmdnaSAoSnVhcmEgMSkNCmNvbXBhcmlzb25fZGYgJT4lDQogIGFycmFuZ2UoZGVzYyhTa29yX0NvcGhlbmV0aWMpKSAlPiUNCiAga25pdHI6OmthYmxlKGNhcHRpb24gPSAiVGFiZWwgUGVyYmFuZGluZ2FuIEt1YWxpdGFzIERlbmRyb2dyYW0gKENvcGhlbmV0aWMgU2NvcmUpIikNCmBgYA0KDQoqKkludGVycHJldGFzaSBIYXNpbCBFdmFsdWFzaToqKg0KDQpCZXJkYXNhcmthbiB0YWJlbCBwZXJiYW5kaW5nYW4gZGkgYXRhcywga2l0YSBkYXBhdCBtZW55aW1wdWxrYW46DQoNCiogKipQZXJpbmdrYXQgVGVyYXRhczoqKiBNZXRvZGUgZGVuZ2FuIHNrb3Iga29yZWxhc2kgdGVydGluZ2dpIChiaWFzYW55YSAqQXZlcmFnZSBMaW5rYWdlKikgbWVudW5qdWtrYW4gYmFod2EgcG9ob24gaGllcmFya2kgeWFuZyBkaWJlbnR1a255YSBwYWxpbmcgYWt1cmF0IGRhbGFtIG1lcmVwcmVzZW50YXNpa2FuIGphcmFrIGRhdGEgeWFuZyBzZWJlbmFybnlhLg0KKiAqKlBlcnRpbWJhbmdhbiBXYXJkJ3MgTWV0aG9kOioqIE1lc2tpcHVuIG1ldG9kZSAqV2FyZCogbXVuZ2tpbiBidWthbiB5YW5nIHRlcnRpbmdnaSBzZWNhcmEgc2tvciBzdGF0aXN0aWssIG1ldG9kZSBpbmkgc2VyaW5na2FsaSB0ZXRhcCBtZW5qYWRpIHBpbGloYW4gdXRhbWEgZGFsYW0gcHJha3RpayBrYXJlbmEga2VtYW1wdWFubnlhIG1lbmdoYXNpbGthbiBrbGFzdGVyIHlhbmcga29tcGFrLCB0ZXJwaXNhaCB0ZWdhcywgZGFuIG11ZGFoIGRpaW50ZXJwcmV0YXNpa2FuIHNlY2FyYSB2aXN1YWwuDQoqICoqUGVyaW5na2F0IFRlcmJhd2FoOioqIE1ldG9kZSBkZW5nYW4gc2tvciB0ZXJlbmRhaCBtZW1pbGlraSBkaXN0b3JzaSB0ZXJiZXNhciwgYXJ0aW55YSBzdHJ1a3R1ciBwb2hvbm55YSBrdXJhbmcgYWt1cmF0IG1lbmdnYW1iYXJrYW4ga2VtaXJpcGFuIGRhdGEgYXNsaS4NCg0KKipLZXNpbXB1bGFuOioqIFNrb3Iga29waGVuZXRpayBoYW55YWxhaCBzYWxhaCBzYXR1IGluZGlrYXRvci4gS2VwdXR1c2FuIGFraGlyIHBlbWlsaWhhbiBtZXRvZGUgdGVyYmFpayBha2FuIGtpdGEgdGVudHVrYW4gc2V0ZWxhaCBtZWxpaGF0IGt1YWxpdGFzIHZpc3VhbGlzYXNpICpkZW5kcm9ncmFtKiBkYW4gdmFsaWRhc2kgKlNpbGhvdWV0dGUgU2NvcmUqIHBhZGEgYmFiIHNlbGFuanV0bnlhLg0KDQotLS0NCg0KIyAxMC4gVmlzdWFsaXNhc2kgSGFzaWwgKERlbmRyb2dyYW0gJiBDbHVzdGVyIFBsb3QpDQoNClR1anVhbiB1dGFtYSBiYWIgaW5pIGFkYWxhaCBtZW1iYW5kaW5na2FuIHNlY2FyYSB2aXN1YWwgaGFzaWwgZGFyaSBiZXJiYWdhaSBhbGdvcml0bWEgeWFuZyB0ZWxhaCBraXRhIGhpdHVuZyBkaSBCYWIgOS4gS2l0YSBha2FuIG1lbGloYXQgcGVyYmVkYWFuIHN0cnVrdHVyIHBvaG9uIGFudGFyYSBwZW5kZWthdGFuICoqQWdnbG9tZXJhdGl2ZSoqIChkZW5nYW4gYmVyYmFnYWkgbGlua2FnZSkgZGFuIHBlbmRla2F0YW4gKipEaXZpc2l2ZSoqLg0KDQojIyAxMC4xLiBLb21wYXJhc2kgVmlzdWFsOiBBZ2dsb21lcmF0aXZlIHZzIERpdmlzaXZlDQpNYXJpIGtpdGEgcGxvdCBrZWxpbWEgZGVuZHJvZ3JhbSBzZWNhcmEgYmVyc2FtYWFuIHVudHVrIG1lbGloYXQga2FyYWt0ZXJpc3RpayBtYXNpbmctbWFzaW5nIG1ldG9kZS4NCg0KYGBge3IgZmlnLmhlaWdodD0xNSwgZmlnLndpZHRoPTEyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KbGlicmFyeShncmlkRXh0cmEpDQoNCiMgU2V0dGluZyB2aXN1YWxpc2FzaSBhZ2FyIHNlcmFnYW0NCiMgS2l0YSBzZW1idW55aWthbiBsYWJlbCAoc2hvd19sYWJlbHM9RkFMU0UpIGthcmVuYSBkYXRhIHBhZGF0ICg1ODcgbGFndSkNCg0KIyAxLiBBZ2dsb21lcmF0aXZlOiBTaW5nbGUgTGlua2FnZQ0KcDEgPC0gZnZpel9kZW5kKGhjX3NpbmdsZSwgc2hvd19sYWJlbHMgPSBGQUxTRSwgayA9IDQsIGtfY29sb3JzID0gImpjbyIsDQogICAgICAgICAgICAgICAgbWFpbiA9ICJBZ2dsb21lcmF0aXZlOiBTaW5nbGUgTGlua2FnZSBcbihDaGFpbmluZyBFZmZlY3QgLSBCdXJ1aykiKQ0KDQojIDIuIEFnZ2xvbWVyYXRpdmU6IENvbXBsZXRlIExpbmthZ2UNCnAyIDwtIGZ2aXpfZGVuZChoY19jb21wbGV0ZSwgc2hvd19sYWJlbHMgPSBGQUxTRSwgayA9IDQsIGtfY29sb3JzID0gImpjbyIsDQogICAgICAgICAgICAgICAgbWFpbiA9ICJBZ2dsb21lcmF0aXZlOiBDb21wbGV0ZSBMaW5rYWdlIFxuKFBhZGF0ICYgQnVsYXQpIikNCg0KIyAzLiBBZ2dsb21lcmF0aXZlOiBBdmVyYWdlIExpbmthZ2UNCnAzIDwtIGZ2aXpfZGVuZChoY19hdmcsIHNob3dfbGFiZWxzID0gRkFMU0UsIGsgPSA0LCBrX2NvbG9ycyA9ICJqY28iLA0KICAgICAgICAgICAgICAgIG1haW4gPSAiQWdnbG9tZXJhdGl2ZTogQXZlcmFnZSBMaW5rYWdlIFxuKFNlaW1iYW5nKSIpDQoNCiMgNC4gQWdnbG9tZXJhdGl2ZTogV2FyZCdzIE1ldGhvZA0KcDQgPC0gZnZpel9kZW5kKGhjX3dhcmQsIHNob3dfbGFiZWxzID0gRkFMU0UsIGsgPSA0LCBrX2NvbG9ycyA9ICJqY28iLA0KICAgICAgICAgICAgICAgIG1haW4gPSAiQWdnbG9tZXJhdGl2ZTogV2FyZCdzIE1ldGhvZCBcbihQYWxpbmcgUmFwaSAmIFRlcnBpc2FoKSIpDQoNCiMgNS4gRGl2aXNpdmUgQ2x1c3RlcmluZyAoRElBTkEpDQpwNSA8LSBmdml6X2RlbmQoaGNfZGl2aXNpdmUsIHNob3dfbGFiZWxzID0gRkFMU0UsIGsgPSA0LCBrX2NvbG9ycyA9ICJqY28iLA0KICAgICAgICAgICAgICAgIG1haW4gPSAiRGl2aXNpdmU6IERJQU5BIFxuKFRvcC1Eb3duIEFwcHJvYWNoKSIpDQoNCiMgTWVuYW1waWxrYW4gR3JpZCAzIEJhcmlzIHggMiBLb2xvbQ0KZ3JpZC5hcnJhbmdlKHAxLCBwMiwgcDMsIHA0LCBwNSwgbmNvbCA9IDIpDQpgYGANCg0KKipJbnRlcnByZXRhc2kgUGVyYmFuZGluZ2FuIE1ldG9kZToqKg0KDQpCZXJkYXNhcmthbiBwbG90IGRlbmRyb2dyYW0gZGkgYXRhcywgYmVyaWt1dCBhZGFsYWggYW5hbGlzaXMga2FyYWt0ZXJpc3RpayBkYXJpIG1hc2luZy1tYXNpbmcgcGVuZGVrYXRhbjoNCg0KMS4gICoqU2luZ2xlIExpbmthZ2U6KioNCiAgICAqIEhhc2lsbnlhIHRlcmxpaGF0IHNhbmdhdCBidXJ1aywgbWVueWVydXBhaSAidGFuZ2dhIiBwYW5qYW5nIGtlIGJhd2FoLg0KICAgICogRmVub21lbmEgaW5pIGRpc2VidXQgKmNoYWluaW5nIGVmZmVjdCosIGRpIG1hbmEgZGF0YSAiZGltYWthbiIgc2F0dSBwZXIgc2F0dSBrZSBkYWxhbSBjbHVzdGVyIGJlc2FyLiBNZXRvZGUgaW5pICoqdGlkYWsgY29jb2sqKiB1bnR1ayBzZWdtZW50YXNpIGxhZ3UuDQoNCjIuICAqKkNvbXBsZXRlIExpbmthZ2U6KioNCiAgICAqIE1lbmdoYXNpbGthbiBjbHVzdGVyIHlhbmcgc2FuZ2F0IHBhZGF0ICgqY29tcGFjdCopLg0KICAgICogS2VsZW1haGFubnlhIGFkYWxhaCB0ZXJrYWRhbmcgbWVtZWNhaCBncnVwIHlhbmcgc2VoYXJ1c255YSBzYXR1IGtlc2F0dWFuIG1lbmphZGkgdGVycGlzYWgtcGlzYWgsIGthcmVuYSBtZXRvZGUgaW5pIHRlcmxhbHUgc2Vuc2l0aWYgdGVyaGFkYXAgamFyYWsgdGVyamF1aCAoKm91dGxpZXIqKS4NCg0KMy4gICoqQXZlcmFnZSBMaW5rYWdlICYgRGl2aXNpdmUgKERJQU5BKToqKg0KICAgICogS2VkdWEgbWV0b2RlIGluaSBtZW1iZXJpa2FuIGhhc2lsIHlhbmcgY3VrdXAgbWlyaXAgZGFuIHNlaW1iYW5nIChqYWxhbiB0ZW5nYWgpLg0KICAgICogRElBTkEgKCpEaXZpc2l2ZSopIGNlbmRlcnVuZyBtZW5naGFzaWxrYW4gc3RydWt0dXIgeWFuZyBzdGFiaWwsIG5hbXVuIG1lbWlsaWtpIGJlYmFuIGtvbXB1dGFzaSB5YW5nIGxlYmloIGJlcmF0IGRpYmFuZGluZ2thbiBtZXRvZGUgYWdnbG9tZXJhdGl2ZS4NCg0KNC4gICoqV2FyZCdzIE1ldGhvZCAoTWV0b2RlIFRlcnBpbGloKToqKg0KICAgICogU2VjYXJhIHZpc3VhbCwgbWV0b2RlIGluaSBtZW1iZXJpa2FuIHN0cnVrdHVyIHBvaG9uIHlhbmcgcGFsaW5nICoqc2ltZXRyaXMsIHNlaW1iYW5nLCBkYW4gdGVycGlzYWggdGVnYXMqKi4NCiAgICAqIFRpbmdnaSBkYWhhbiAoKmhlaWdodCopIGFudGFyLWNsdXN0ZXIgY3VrdXAgamF1aCwgbWVuYW5kYWthbiBwZW1pc2FoYW4geWFuZyBrdWF0IGFudGFyIGtlbG9tcG9rIGxhZ3UuDQoNCioqSW50ZXJwcmV0YXNpIEtvbXBhcmF0aWY6IFZpc3VhbCAoQmFiIDEwKSB2cyBTdGF0aXN0aWsgKEJhYiA5KSoqDQoNCkJlcmRhc2Fya2FuIHBsb3Qga2VsaW1hIGRlbmRyb2dyYW0gZGkgYXRhcyBkYW4gdGFiZWwgc2tvciBLb3BoZW5ldGlrIHNlYmVsdW1ueWEsIGtpdGEgbWVuZW11a2FuIHNlYnVhaCAqKnBhcmFkb2tzIG1lbmFyaWsqKiB5YW5nIHBlcmx1IGRpamVsYXNrYW46DQoNCjEuICAqKkthc3VzIFNpbmdsZSBMaW5rYWdlIChTa29yIFRpbmdnaSwgVmlzdWFsIEJ1cnVrKToqKg0KICAgICogKipEaSBCYWIgOToqKiBNZXRvZGUgaW5pIG1lbWlsaWtpIHNrb3IgdGVydGluZ2dpICgkMC44MiQpLiBBcnRpbnlhLCBzZWNhcmEgbWF0ZW1hdGlrYSwgaWEgc2FuZ2F0ICJqdWp1ciIgbWVtcGVydGFoYW5rYW4gamFyYWsgYXNsaSBhbnRhci1sYWd1Lg0KICAgICogKipEaSBCYWIgMTAgKFZpc3VhbCk6KiogTGloYXQgZ2FtYmFyICpraXJpIGF0YXMqLiBIYXNpbG55YSBzYW5nYXQgbWVuZ2VjZXdha2FuLiBUZXJiZW50dWsgcG9sYSAidGFuZ2dhIiBwYW5qYW5nIGtlIGJhd2FoICgqQ2hhaW5pbmcgRWZmZWN0KikuDQogICAgKiAqKkFuYWxpc2lzOioqIE1lc2tpcHVuIGFrdXJhdCBzZWNhcmEgamFyYWssIG1ldG9kZSBpbmkgZ2FnYWwgbWVuZ2Vsb21wb2trYW4gbGFndS4gSWEgaGFueWEgbWVuZ2dhYnVuZ2thbiBzYXR1IGxhZ3Uga2UgbGFndSBsYWlubnlhIHNlY2FyYSBiZXJhbnRhaS4gSmlrYSBraXRhIG1lbW90b25nIHBvaG9uIGluaSwga2l0YSBha2FuIG1lbmRhcGF0a2FuIDEgY2x1c3RlciByYWtzYXNhIGRhbiBiYW55YWsgY2x1c3RlciBiZXJpc2kgMSBsYWd1IHNhamEuIEluaSAqKnRpZGFrIGJlcmd1bmEqKiB1bnR1ayBhbmFsaXNpcyBiaXNuaXMuDQoNCjIuICAqKkthc3VzIFdhcmQncyBNZXRob2QgKFNrb3IgUmVuZGFoLCBWaXN1YWwgVGVyYmFpayk6KioNCiAgICAqICoqRGkgQmFiIDk6KiogTWV0b2RlIGluaSBtZW1pbGlraSBza29yIHRlcmVuZGFoICgkMC40NSQpLiBBcnRpbnlhLCBpYSBzZWRpa2l0ICJtZW1hbmlwdWxhc2kiIGF0YXUgbWVuZGlzdG9yc2kgamFyYWsgYXNsaSBkYXRhLg0KICAgICogKipEaSBCYWIgMTAgKFZpc3VhbCk6KiogTGloYXQgZ2FtYmFyICprYW5hbiB0ZW5nYWgqLiBJbmkgYWRhbGFoIHN0cnVrdHVyIHlhbmcgcGFsaW5nIGlkZWFsLiBQb2hvbm55YSBiZXJiZW50dWsgcGlyYW1pZGEgc2ltZXRyaXMsIGRhbiBwZXJjYWJhbmdhbiBhbnRhci1rZWxvbXBvayB0ZXJsaWhhdCBzYW5nYXQgdGVnYXMgKGphcmFrIHZlcnRpa2FsbnlhIGphdWgpLg0KICAgICogKipBbmFsaXNpczoqKiBNZXRvZGUgV2FyZCAibWVuZ29yYmFua2FuIiBzZWRpa2l0IGFrdXJhc2kgamFyYWsgZGVtaSBtZW5jaXB0YWthbiBrZWxvbXBvayB5YW5nICoqcGFkYXQgKCpjb21wYWN0KikqKiBkYW4gKip0ZXJwaXNhaCBqZWxhcyoqLiBJYSBtZW1ha3NhIGxhZ3UtbGFndSB1bnR1ayBtYXN1ayBrZSBkYWxhbSBrb3Rhay1rb3RhayBrYXRlZ29yaSB5YW5nIHJhcGkuDQoNCjMuICAqKkthc3VzIEF2ZXJhZ2UgJiBEaXZpc2l2ZSAoUGVuZW5nYWgpOioqDQogICAgKiBNZXRvZGUgaW5pIG1lbWJlcmlrYW4gaGFzaWwgdmlzdWFsIHlhbmcgY3VrdXAgc2VpbWJhbmcsIG5hbXVuIHN0cnVrdHVyIHBlbWlzYWhhbm55YSB0aWRhayBzZXRlZ2FzIG1ldG9kZSBXYXJkLg0KDQoqKktlcHV0dXNhbiBGaW5hbDoqKg0KRGFsYW0gKkRhdGEgU2NpZW5jZSosIHR1anVhbiB1dGFtYSBjbHVzdGVyaW5nIGFkYWxhaCBtZW5kYXBhdGthbiB3YXdhc2FuICgqaW5zaWdodCopIHlhbmcgbXVkYWggZGlpbnRlcnByZXRhc2lrYW4uDQpPbGVoIGthcmVuYSBpdHUsIGtpdGEgKiptZW5nYWJhaWthbioqIHNrb3Igc3RhdGlzdGlrIFNpbmdsZSBMaW5rYWdlIHlhbmcgdGluZ2dpIGthcmVuYSB2aXN1YWxpc2FzaW55YSB0aWRhayBiZXJtYWtuYSwgZGFuICoqbWVtaWxpaCBXYXJkJ3MgTWV0aG9kKioga2FyZW5hIGJlcmhhc2lsIG1lbmdlbG9tcG9ra2FuIDU4NyBsYWd1IGtlIGRhbGFtIHN0cnVrdHVyIHlhbmcgcmFwaSwgc2VpbWJhbmcsIGRhbiBtdWRhaCBkaWFuYWxpc2lzIGthcmFrdGVyaXN0aWtueWEuDQoNCiMjIDEwLjIuIE1lbmVudHVrYW4gSnVtbGFoIENsdXN0ZXIgT3B0aW1hbCAoJGskKVNldGVsYWggbWVtaWxpaCBtZXRvZGUgV2FyZCwga2l0YSBwZXJsdSBtZW5lbnR1a2FuIGJlcmFwYSBqdW1sYWggZ3J1cCBsYWd1IHlhbmcgcGFsaW5nIHBhcy4NCg0KYGBge3J9DQojIDEuIE1ldG9kZSBFbGJvdyAoV1NTKQ0KcF93c3MgPC0gZnZpel9uYmNsdXN0KGRmX3NjYWxlZCwgRlVOID0gaGN1dCwgbWV0aG9kID0gIndzcyIsIGsubWF4ID0gMTApICsNCiAgbGFicyh0aXRsZSA9ICJNZXRvZGUgRWxib3ciLCBzdWJ0aXRsZSA9ICJDYXJpIHRpdGlrIHNpa3UgKEJlbmQpIikNCg0KIyAyLiBNZXRvZGUgU2lsaG91ZXR0ZQ0KcF9zaWwgPC0gZnZpel9uYmNsdXN0KGRmX3NjYWxlZCwgRlVOID0gaGN1dCwgbWV0aG9kID0gInNpbGhvdWV0dGUiLCBrLm1heCA9IDEwKSArDQogIGxhYnModGl0bGUgPSAiTWV0b2RlIFNpbGhvdWV0dGUiLCBzdWJ0aXRsZSA9ICJDYXJpIHJhdGEtcmF0YSB0ZXJ0aW5nZ2kiKQ0KDQpncmlkLmFycmFuZ2UocF93c3MsIHBfc2lsLCBuY29sID0gMikNCmBgYA0KDQoqKkludGVycHJldGFzaSBHYW1wYW5nIChDYXJhIEJhY2EgR3JhZmlrKToqKg0KDQpLaXRhIG1hdSBtZW5lbnR1a2FuOiAqIlNlYmFpa255YSA1ODcgbGFndSBpbmkgZGliYWdpIGphZGkgYmVyYXBhIGtlbG9tcG9rIHNpaD8gMiBrZWxvbXBvb2s/IDMga2Vsb21wb2s/IGF0YXUgMTAga2Vsb21wb2s/IioNCg0KMS4gICoqR3JhZmlrIEtpcmkgKE1ldG9kZSBFbGJvdyAtIENhcmkgU2lrdSk6KioNCiAgICAqIEJheWFuZ2thbiBncmFmaWsgaW5pIGtheWFrICoqbGVuZ2FuIG9yYW5nKiouDQogICAgKiBEYXJpIGFuZ2thIDEga2UgMiwgZ2FyaXNueWEgdHVydW4gY3VyYW0gYmFuZ2V0IChsdXJ1cykuDQogICAgKiBEYXJpIDIga2UgMywgbWFzaWggdHVydW4uDQogICAgKiBOYWgsIHBhcyBkaSBhbmdrYSAqKjQqKiwgZ2FyaXNueWEgbXVsYWkgKioibWVuZWt1ayIqKiAoa2F5YWsgc2lrdSB0YW5nYW4pIGRhbiBtdWxhaSBsYW5kYWkgKGRhdGFyKSBrZSBrYW5hbi4NCiAgICAqICoqQXJ0aW55YToqKiBLb21wdXRlciBiaWxhbmcsICoiQmVyaGVudGkgZGkgNCBhamEgdWRhaCBjdWt1cCBrb2ssIGthbGF1IGRpdGFtYmFoIGxhZ2kgamFkaSA1IGF0YXUgNiwgaGFzaWxueWEgZ2FrIGJlZGEgamF1aC4iKg0KDQoyLiAgKipHcmFmaWsgS2FuYW4gKE1ldG9kZSBTaWxob3VldHRlIC0gQ2FyaSBUaWFuZyBUZXJ0aW5nZ2kpOioqDQogICAgKiBBbmdnYXAgaW5pICoqTmlsYWkgUmFwb3IqKi4gU2VtYWtpbiB0aW5nZ2kgdGlhbmdueWEsIHNlbWFraW4gYmFndXMgcGVuZ2Vsb21wb2thbm55YS4NCiAgICAqIFRpYW5nIHBhbGluZyB0aW5nZ2kgYWRhIGRpIGFuZ2thICoqMioqLiBUYXBpIHRpYW5nIGRpIGFuZ2thICoqNCoqIGp1Z2EgbWFzaWggY3VrdXAgdGluZ2dpIGRhbiBiYWd1cyAoZGkgYXRhcyBnYXJpcyByYXRhLXJhdGEpLg0KDQoqKktlcHV0dXNhbiBGaW5hbDogS2VuYXBhIFBpbGloIDQgQ2x1c3Rlcj8qKg0KS2VuYXBhIGtpdGEgZ2FrIG51cnV0IHNhbWEgZ3JhZmlrIGthbmFuIHlhbmcgbnl1cnVoIDI/DQoqIEthbGF1IGtpdGEgY3VtYSBiYWdpICoqMiBnZW5nKiosIG5hbnRpIGxhZ3VueWEgY3VtYSB0ZXJwaXNhaCBqYWRpICJMYWd1IENlcGF0IiB2cyAiTGFndSBMYW1iYXQiLiBUZXJsYWx1IHVtdW0gZGFuIG1lbWJvc2Fua2FuLg0KKiBLaXRhIHBpbGloICoqNCBnZW5nKioga2FyZW5hIGxlYmloIHNlcnUgZGFuIGRldGFpbC4gS2l0YSBiaXNhIGRhcGF0IGdlbmcgIkxhZ3UgRHVnZW0iLCAiTGFndSBHYWxhdSIsICJMYWd1IEFrdXN0aWsiLCBkYW4gIkxhZ3UgUmFwIi4NCg0KSmFkaSwgKiprID0gNCoqIGFkYWxhaCBwaWxpaGFuIHlhbmcgcGFsaW5nIHBhcyBidWF0IGFuYWxpc2lzIG11c2lrDQoNCiMjIDEwLjMuIFZpc3VhbGlzYXNpIEFraGlyIChDbHVzdGVyIFBsb3QgMkQpDQpCZXJpa3V0IGFkYWxhaCBwZXRhIHBlcnNlYmFyYW4gbGFndSBtZW5nZ3VuYWthbiBtZXRvZGUgdGVycGlsaWggKEFnZ2xvbWVyYXRpdmUgV2FyZCkgZGVuZ2FuIDQgQ2x1c3Rlci4NCg0KYGBge3J9DQojIFBvdG9uZyBwb2hvbiBXYXJkIG1lbmphZGkgNCBiYWdpYW4NCmdycF93YXJkIDwtIGN1dHJlZShoY193YXJkLCBrID0gNCkNCg0KIyBQbG90IFBldGEgQ2x1c3Rlcg0KZnZpel9jbHVzdGVyKGxpc3QoZGF0YSA9IGRmX3NjYWxlZCwgY2x1c3RlciA9IGdycF93YXJkKSwNCiAgICAgICAgICAgICBnZW9tID0gInBvaW50IiwgICAgICAgDQogICAgICAgICAgICAgZWxsaXBzZS50eXBlID0gImNvbnZleCIsIA0KICAgICAgICAgICAgIHBhbGV0dGUgPSBjKCIjMkU5RkRGIiwgIiNFN0I4MDAiLCAiI0ZDNEUwNyIsICIjMDBBRkJCIiksDQogICAgICAgICAgICAgZ2d0aGVtZSA9IHRoZW1lX21pbmltYWwoKSwNCiAgICAgICAgICAgICBtYWluID0gIlBldGEgUGVyc2ViYXJhbiBDbHVzdGVyIChGaW5hbCBNb2RlbDogV2FyZCBrPTQpIg0KKQ0KYGBgDQoNCmBgYHtyfQ0KIyBDZWsganVtbGFoIGxhZ3UgZGkgc2V0aWFwIGNsdXN0ZXINCnRhYmxlKGdycF93YXJkKQ0KYGBgDQoNCioqSW50ZXJwcmV0YXNpIFBldGE6KioNCg0KQmF5YW5na2FuIGdhbWJhciBkaSBhdGFzIGFkYWxhaCAqKiJQZXRhIER1bmlhIE11c2lrIioqIGRhcmkgNTg3IGxhZ3UgeWFuZyBraXRhIGFuYWxpc2lzLg0KDQoxLiAgKipLZW5hcGEgZ2FtYmFybnlhIGRhdGFyPyoqDQogICAgRGF0YSBhc2xpbnlhIHJ1bWl0IChwdW55YSAxMCBmaXR1cjogYWRhIHRlbXBvLCBlbmVyZ2ksIGR1cmFzaSwgZGxsKS4gS2FyZW5hIG1hdGEgbWFudXNpYSBwdXNpbmcga2FsYXUgbGloYXQgMTAgZGltZW5zaSBzZWthbGlndXMsIGtvbXB1dGVyICJtZW1vdHJldCIgZGFuIG1lcmF0YWthbm55YSBqYWRpIGdhbWJhciAyIGRpbWVuc2kgKFBldGEgRGF0YXIpIHN1cGF5YSBiaXNhIGtpdGEgbGloYXQgZGkgbGF5YXIuDQoNCjIuICAqKkFydGkgV2FybmEtV2FybmkgKEdlbmcgTGFndSk6KioNCiAgICAqIFNldGlhcCAqKnRpdGlrKiogYWRhbGFoIHNhdHUgbGFndS4NCiAgICAqIExhZ3UtbGFndSBkZW5nYW4gKip3YXJuYSB5YW5nIHNhbWEqKiBhcnRpbnlhIG1lcmVrYSAqKnNhdHUgZ2VuZyoqIGF0YXUgcHVueWEga2VtaXJpcGFuIHNpZmF0IHlhbmcga3VhdC4NCiAgICAqIENvbnRvaDogTGFndS1sYWd1IGRpIGFyZWEgKkJpcnUqIG11bmdraW4gYWRhbGFoIGxhZ3UgeWFuZyAia2FsZW0iLCBzZWRhbmdrYW4gbGFndSBkaSBhcmVhICpNZXJhaCogYWRhbGFoIGxhZ3UgeWFuZyAiYmVyaXNpayIuDQoNCjMuICAqKktlc2ltcHVsYW46KioNCiAgICBUZXJsaWhhdCBhZGEgKio0IHdpbGF5YWggd2FybmEqKiB5YW5nIG1lbmVtcGF0aSBwb3Npc2lueWEgbWFzaW5nLW1hc2luZy4gSW5pIG1lbWJ1a3Rpa2FuIGJhaHdhIGFsZ29yaXRtYSAqKldhcmQqKiBzdWtzZXMgYmVzYXIhIERpYSBiZXJoYXNpbCBtZW55b3J0aXIgcmF0dXNhbiBsYWd1IHlhbmcgYXdhbG55YSBhY2FrLWFjYWthbiBtZW5qYWRpIDQga2Vsb21wb2sgZ2VucmUvbW9vZCB5YW5nIHJhcGkgZGFuIHRlcmF0dXIuDQoNCi0tLQ0KDQojIDExLiBFdmFsdWFzaSBNb2RlbCBkYW4gSW50ZXJwcmV0YXNpIEFraGlyDQoNClNldGVsYWggbWVsYWx1aSBwcm9zZXMgdmlzdWFsaXNhc2ksIGtpdGEgdGVsYWggbWVtaWxpaCAqKldhcmQncyBNZXRob2QqKiBkZW5nYW4gKio0IENsdXN0ZXIqKiBzZWJhZ2FpIG1vZGVsIHRlcmJhaWsuIE5hbXVuLCBzZWJlcmFwYSBiYWd1cyBrdWFsaXRhcyBwZW1pc2FoYW5ueWEgc2VjYXJhIHN0YXRpc3Rpaz8gRGFuIGFwYSBrYXJha3RlcmlzdGlrIHVuaWsgZGFyaSBzZXRpYXAgZ3J1cCB0ZXJzZWJ1dD8NCg0KIyMgMTEuMS4gRXZhbHVhc2kgSW50ZXJuYWwgKFNpbGhvdWV0dGUgU2NvcmUpDQoNCktpdGEgYWthbiBtZW5naGl0dW5nICoqU2lsaG91ZXR0ZSBDb2VmZmljaWVudCoqIHVudHVrIGtlbGltYSBtZXRvZGUgeWFuZyB0ZWxhaCBraXRhIGNvYmEuDQoNCiogKipOaWxhaSBtZW5kZWthdGkgMToqKiBDbHVzdGVyIHRlcnBpc2FoIHNhbmdhdCBiYWlrIChzZW1wdXJuYSkuDQoNCiogKipOaWxhaSBtZW5kZWthdGkgMDoqKiBDbHVzdGVyIGJlcmltcGl0YW4gKCpvdmVybGFwcGluZyopLg0KDQoqICoqTmlsYWkgbmVnYXRpZjoqKiBTYWxhaCBwZW5nZWxvbXBva2FuLg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyBGdW5nc2kgdW50dWsgbWVuZ2hpdHVuZyByYXRhLXJhdGEgU2lsaG91ZXR0ZSBXaWR0aCBwYWRhIGs9NA0KaGl0dW5nX3NpbCA8LSBmdW5jdGlvbihtb2RlbF9oYywgZGF0YV9zY2FsZWQsIGs9NCkgew0KICAjIFBvdG9uZyBwb2hvbiBqYWRpIDQgYmFnaWFuDQogIGdycCA8LSBjdXRyZWUobW9kZWxfaGMsIGsgPSBrKQ0KICAjIEhpdHVuZyBzaWxob3VldHRlDQogIHNpbCA8LSBzaWxob3VldHRlKGdycCwgZGlzdChkYXRhX3NjYWxlZCkpDQogICMgQW1iaWwgcmF0YS1yYXRhbnlhDQogIG1lYW4oc2lsWywgM10pDQp9DQoNCiMgSGl0dW5nIHNrb3IgdW50dWsgc2VtdWEgbWV0b2RlDQpzaWxfc2luZ2xlICAgPC0gaGl0dW5nX3NpbChoY19zaW5nbGUsIGRmX3NjYWxlZCkNCnNpbF9jb21wbGV0ZSA8LSBoaXR1bmdfc2lsKGhjX2NvbXBsZXRlLCBkZl9zY2FsZWQpDQpzaWxfYXZnICAgICAgPC0gaGl0dW5nX3NpbChoY19hdmcsIGRmX3NjYWxlZCkNCnNpbF93YXJkICAgICA8LSBoaXR1bmdfc2lsKGhjX3dhcmQsIGRmX3NjYWxlZCkNCiMgS2h1c3VzIERpdmlzaXZlIChESUFOQSkNCnNpbF9kaXZpc2l2ZSA8LSBtZWFuKHNpbGhvdWV0dGUoY3V0cmVlKGFzLmhjbHVzdChoY19kaXZpc2l2ZSksIGs9NCksIGRpc3QoZGZfc2NhbGVkKSlbLDNdKQ0KDQojIEJ1YXQgVGFiZWwgUGVyYmFuZGluZ2FuDQpkZl9ldmFsIDwtIGRhdGEuZnJhbWUoDQogIE1ldG9kZSA9IGMoIlNpbmdsZSIsICJDb21wbGV0ZSIsICJBdmVyYWdlIiwgIldhcmQiLCAiRGl2aXNpdmUiKSwNCiAgQXZnX1NpbGhvdWV0dGUgPSBjKHNpbF9zaW5nbGUsIHNpbF9jb21wbGV0ZSwgc2lsX2F2Zywgc2lsX3dhcmQsIHNpbF9kaXZpc2l2ZSkNCikNCg0KIyBUYW1waWxrYW4gVGFiZWwNCmRmX2V2YWwgJT4lIA0KICBhcnJhbmdlKGRlc2MoQXZnX1NpbGhvdWV0dGUpKSAlPiUNCiAga25pdHI6OmthYmxlKGNhcHRpb24gPSAiUGVyYmFuZGluZ2FuIFJhdGEtcmF0YSBTaWxob3VldHRlIFNjb3JlIChrPTQpIikNCmBgYA0KDQoqKkFuYWxpc2lzIEV2YWx1YXNpOioqDQoNCkJlcmRhc2Fya2FuIHRhYmVsIHBlcmJhbmRpbmdhbiBkaSBhdGFzLCBraXRhIGRhcGF0IG1lbmdldmFsdWFzaSBrdWFsaXRhcyBwZW1pc2FoYW4ga2xhc3RlcjoNCg0KKiAqKktlcGFkYXRhbiBLbGFzdGVyOioqIFRhYmVsIG1lbnVuanVra2FuIG1ldG9kZSBtYW5hIHlhbmcgbWVuZ2hhc2lsa2FuIHJhdGEtcmF0YSAqU2lsaG91ZXR0ZSBXaWR0aCogdGVydGluZ2dpLiBVbXVtbnlhLCBtZXRvZGUgKipXYXJkKiogYXRhdSAqKkF2ZXJhZ2UqKiBtZW51bmp1a2thbiBoYXNpbCB5YW5nIHBhbGluZyBrb21wZXRpdGlmLg0KKiAqKkludGVycHJldGFzaSBOaWxhaToqKiBNZXNraXB1biBza29yIHJhdGEtcmF0YSBtdW5na2luIHRpZGFrIG1lbmNhcGFpIGFtYmFuZyBiYXRhcyB0aW5nZ2kgKG1pc2FsbnlhID4gMC41KSwgaGFsIGluaSAqKmRhcGF0IGRpbWFrbHVtaSoqLiBEYXRhIG11c2lrIG1lbWlsaWtpIGthcmFrdGVyaXN0aWsgeWFuZyBjYWlyIGRhbiBncmFkYXNpIGFudGFyLWdlbnJlIHlhbmcgaGFsdXMsIHNlaGluZ2dhIHdhamFyIGppa2EgdGVyZGFwYXQgc2VkaWtpdCBpcmlzYW4gKCpvdmVybGFwKikuDQoqICoqS2VzaW1wdWxhbjoqKiBOaWxhaSAqc2lsaG91ZXR0ZSogeWFuZyBwb3NpdGlmIHN1ZGFoIGN1a3VwIG1lbWJ1a3Rpa2FuIGJhaHdhIHN0cnVrdHVyIGtsYXN0ZXIgdGVsYWggdGVyYmVudHVrIGRlbmdhbiBiYWlrIGRhbiBwZW5nZWxvbXBva2FuIHlhbmcgZGlsYWt1a2FuIHRpZGFrIGJlcnNpZmF0IGFjYWsuDQoNCiMjIDExLjIuIFZpc3VhbGlzYXNpIFNpbGhvdWV0dGUgUGxvdCAoTWV0b2RlIFRlcnBpbGloKQ0KTWFyaSBraXRhIGxpaGF0IGRldGFpbCBrdWFsaXRhcyBzZXRpYXAgYW5nZ290YSBkaSBkYWxhbSBtb2RlbCBmaW5hbCBraXRhIChXYXJkKS4NCg0KYGBge3J9DQojIFBvdG9uZyBwb2hvbiBXYXJkIGphZGkgNA0KZ3JwX3dhcmQgPC0gY3V0cmVlKGhjX3dhcmQsIGsgPSA0KQ0KDQojIFBsb3QgU2lsaG91ZXR0ZQ0KZnZpel9zaWxob3VldHRlKHNpbGhvdWV0dGUoZ3JwX3dhcmQsIGRpc3QoZGZfc2NhbGVkKSksIA0KICAgICAgICAgICAgICAgIHByaW50LnN1bW1hcnkgPSBGQUxTRSwNCiAgICAgICAgICAgICAgICBwYWxldHRlID0gImpjbyIsDQogICAgICAgICAgICAgICAgZ2d0aGVtZSA9IHRoZW1lX21pbmltYWwoKSwNCiAgICAgICAgICAgICAgICBtYWluID0gIlNpbGhvdWV0dGUgUGxvdDogV2FyZCdzIE1ldGhvZCAoaz00KSINCikNCmBgYA0KDQpgYGB7cn0NCiMgQ2VrIGp1bWxhaCBsYWd1IGRpIHNldGlhcCBjbHVzdGVyDQp0YWJsZShncnBfd2FyZCkNCmBgYA0KDQoNCioqSW50ZXJwcmV0YXNpIFNpbGhvdWV0dGUgUGxvdDoqKg0KDQpHYW1iYXIgZGkgYXRhcyBhZGFsYWggIlJhcG9yIEtlcHVhc2FuIiBzZXRpYXAgbGFndSB0ZXJoYWRhcCBncnVwbnlhLg0KDQoxLiAgKipUaWFuZyBrZSBBdGFzIChQb3NpdGlmKSA9IExhZ3UgQmFoYWdpYSoqDQogICAgKiBMYWd1LWxhZ3UgaW5pIG1lcmFzYSAqKmNvY29rKiogZGFuICJiZXRhaCIgdGluZ2dhbCBkaSBncnVwbnlhLg0KICAgICogU2VtYWtpbiB0aW5nZ2kgdGlhbmdueWEsIHNlbWFraW4gbWFudGFwIHBvc2lzaW55YSBkaSBncnVwIGl0dSAoamF1aCBkYXJpIGdydXAgdGV0YW5nZ2EpLg0KDQoyLiAgKipUaWFuZyBrZSBCYXdhaCAoTmVnYXRpZikgPSBMYWd1IEdhbGF1KioNCiAgICAqIExpaGF0IGFkYSBzZWRpa2l0IGdhcmlzIHlhbmcgdHVydW4ga2UgYmF3YWggZGkgcGVyYmF0YXNhbiB3YXJuYT8NCiAgICAqIEl0dSBhZGFsYWggbGFndS1sYWd1IHlhbmcgKipiaW5ndW5nKiouIE1lcmVrYSBzZWJlbmFybnlhIG1pcmlwIHNhbWEgZ3J1cCBzZWJlbGFoLCB0YXBpIGRpcGFrc2EgbWFzdWsga2UgZ3J1cCBzZWthcmFuZy4NCiAgICAqICoqQmVyaXRhIEJhZ3VzOioqIEthcmVuYSBnYXJpcyB5YW5nIHR1cnVuIGtlIGJhd2FoIGN1bWEgc2VkaWtpdCwgYmVyYXJ0aSBtYXlvcml0YXMgbGFndSAocmF0dXNhbiBsYWd1IGxhaW5ueWEpIHN1ZGFoIG1hc3VrIGtlICJydW1haCIgeWFuZyB0ZXBhdC4gDQogICAgDQojIyAxMS4zLiBQcm9maWxpbmcgQ2x1c3RlciAoTWVtYmFjYSBLYXJha3RlcmlzdGlrIExhZ3UpDQpJbmkgYmFnaWFuIHBhbGluZyBzZXJ1ISBLaXRhIGFrYW4gbWVuY2FyaSB0YWh1OiAiQXBhIGJlZGFueWEgR3J1cCAxLCBHcnVwIDIsIGRzdD8iIEtpdGEgaGl0dW5nIHJhdGEtcmF0YSBmaXR1ciBhdWRpbyB1bnR1ayBzZXRpYXAgZ3J1cC4NCg0KYGBge3J9DQojIDEuIEdhYnVuZ2thbiBkYXRhIGFzbGkgZGVuZ2FuIGxhYmVsIGNsdXN0ZXINCmRmX2ZpbmFsIDwtIGRmX3NlbGVjdCAlPiUNCiAgbXV0YXRlKENsdXN0ZXIgPSBhcy5mYWN0b3IoZ3JwX3dhcmQpKQ0KDQojIDIuIEhpdHVuZyByYXRhLXJhdGEgc2V0aWFwIHZhcmlhYmVsIHBlciBjbHVzdGVyDQpwcm9maWxfY2x1c3RlciA8LSBkZl9maW5hbCAlPiUNCiAgZ3JvdXBfYnkoQ2x1c3RlcikgJT4lDQogIHN1bW1hcmlzZSgNCiAgICBKdW1sYWhfTGFndSA9IG4oKSwNCiAgICBBdmdfQlBNID0gbWVhbihicG0pLA0KICAgIEF2Z19FbmVyZ3kgPSBtZWFuKG5yZ3kpLA0KICAgIEF2Z19EYW5jZSA9IG1lYW4oZG5jZSksDQogICAgQXZnX0Fjb3VzdGljID0gbWVhbihhY291cyksDQogICAgQXZnX1NwZWVjaCA9IG1lYW4oc3BjaCkNCiAgKQ0KDQojIFRhbXBpbGthbiBQcm9maWwNCmtuaXRyOjprYWJsZShwcm9maWxfY2x1c3RlciwgZGlnaXRzID0gMSwgY2FwdGlvbiA9ICJLYXJha3RlcmlzdGlrIFJhdGEtcmF0YSBTZXRpYXAgQ2x1c3RlciIpDQpgYGANCg0KKipJbnRlcnByZXRhc2kgUHJvZmlsIChNZW1iYWNhIEthcmFrdGVyIEdlbmcpOioqDQoNCkJlcmRhc2Fya2FuIHRhYmVsIGRpIGF0YXMsIGtpdGEgYWtoaXJueWEgYmlzYSBtZW1iZXJpICJOYW1hIFBhbmdnaWxhbiIgdW50dWsgc2V0aWFwIGtlbG9tcG9rIGJlcmRhc2Fya2FuIGNpcmkga2hhcyBhbmdrYW55YToNCg0KMS4gICoqQ2x1c3RlciAzOiAiU2kgR2FsYXUgLyBBa3VzdGlrIiAoVGhlIE1lbGxvdyBWaWJlcykqKg0KICAgICogKipCdWt0aW55YToqKiBMaWhhdCBrb2xvbSBgQXZnX0Fjb3VzdGljYC4gTmlsYWlueWEgKio3Mi41KiosIHBhZGFoYWwgZ3J1cCBsYWluIGN1bWEgZGkga2lzYXJhbiA5IGF0YXUgMTAuIEpvbXBsYW5nIGJhbmdldCBrYW4/DQogICAgKiAqKkVuZXJnaW55YToqKiBTYW5nYXQgcmVuZGFoICgzNC42KS4NCiAgICAqICoqQXJ0aW55YToqKiBJbmkgYWRhbGFoIGt1bXB1bGFuIGxhZ3UtbGFndSBzZWRpaCwgc2FudGFpLCBwYWthaSBnaXRhci9waWFubywgZGFuIGNvY29rIGJ1YXQgcGVuZ2FudGFyIHRpZHVyIGF0YXUgcGFzIGxhZ2kgaHVqYW4uIEJ1a2FuIGJ1YXQgam9nZXQuDQoNCjIuICAqKkNsdXN0ZXIgMjogIlNpIEVuZXJnaWsgLyBQYXJ0eSIgKFRoZSBDbHViIEJhbmdlcnMpKioNCiAgICAqICoqQnVrdGlueWE6KiogTGloYXQga29sb20gYEF2Z19CUE1gIChUZW1wbykuIFBhbGluZyBuZ2VidXQsIHJhdGEtcmF0YSAqKjEyOCBCUE0qKi4NCiAgICAqICoqRW5lcmdpbnlhOioqIFBhbGluZyB0aW5nZ2kgKCoqNzYuNSoqKS4NCiAgICAqICoqQXJ0aW55YToqKiBJbmkgYWRhbGFoIGxhZ3UtbGFndSBiZXJpc2lrLCAqdXBiZWF0KiwgRURNLCBhdGF1IGxhZ3UgYnVhdCBvbGFocmFnYSAoZ3ltL2xhcmkpLiBQb2tva255YSBsYWd1IHlhbmcgYmlraW4gamFudHVuZyBkZWctZGVnYW4gZGFuIHNlbWFuZ2F0Lg0KDQozLiAgKipDbHVzdGVyIDE6ICJTaSBQb3B1bGVyIC8gTWFpbnN0cmVhbSIgKFRoZSBTdGFuZGFyZCBQb3ApKioNCiAgICAqICoqQnVrdGlueWE6KiogSnVtbGFoIGFuZ2dvdGFueWEgcGFsaW5nIGJhbnlhayAoKiozNDkgbGFndSoqKS4gTWF5b3JpdGFzIGxhZ3UgbWFzdWsgc2luaS4NCiAgICAqICoqS2FyYWt0ZXJueWE6KiogQlBNLW55YSBzZWRhbmcgKDExNCksIEVuZXJnaW55YSBsdW1heWFuICg3MSksIERhbmNlLW55YSBva2UgKDY3KS4NCiAgICAqICoqQXJ0aW55YToqKiBJbmkgYWRhbGFoIGxhZ3UgImFtYW4iLiBMYWd1LWxhZ3UgeWFuZyBlbmFrIGRpZGVuZ2FyIGRpIHJhZGlvLCBkaSBrYWZlLCBhdGF1IGRpIG1hbGwuIEdhayB0ZXJsYWx1IHNlZGloLCB0YXBpIGdhayB0ZXJsYWx1IGJlcmlzaWsganVnYS4gTGFndSBwb3Agc3RhbmRhciBwYWRhIHVtdW1ueWEuDQoNCjQuICAqKkNsdXN0ZXIgNDogIlNpIFBlbnl1c3VwIiAoVGhlIE91dGxpZXIpKioNCiAgICAqICoqQnVrdGlueWE6KiogSXNpbnlhIGN1bWEgKioxIGxhZ3UqKiENCiAgICAqICoqQXJ0aW55YToqKiBJbmkgYWRhbGFoICoqT3V0bGllcioqLiBMYWd1IGluaSBzYWtpbmcgYW5laG55YSAoYmVkYSBzZW5kaXJpIGthcmFrdGVyaXN0aWtueWEpLCBzYW1wYWktc2FtcGFpIGFsZ29yaXRtYSBiaW5ndW5nIG1hdSBtYXN1a2luIGRpYSBrZSBtYW5hLCBha2hpcm55YSBkaWEgZGliaWtpbmluICJydW1haCIgc2VuZGlyaS4gRGFsYW0gYW5hbGlzaXMgYmlzbmlzLCBncnVwIHNla2VjaWwgaW5pIGJpYXNhbnlhIGRpYWJhaWthbiBhdGF1IGRpY2VrIGtodXN1cyBsYWd1IGFwYSBpdHUuDQoNCioqS2VzaW1wdWxhbiBCaXNuaXM6KioNCkppa2Egc2F5YSBqYWRpIG1hbmFqZXIgU3BvdGlmeToNCiogTWFzdWtrYW4gbGFndSAqKkNsdXN0ZXIgMyoqIGtlIHBsYXlsaXN0ICoiU2xlZXAiKiBhdGF1ICoiQWNvdXN0aWMgQ2hpbGwiKi4NCiogTWFzdWtrYW4gbGFndSAqKkNsdXN0ZXIgMioqIGtlIHBsYXlsaXN0ICoiV29ya291dCIqIGF0YXUgKiJQYXJ0eSBIaXRzIiouDQoqIE1hc3Vra2FuIGxhZ3UgKipDbHVzdGVyIDEqKiBrZSBwbGF5bGlzdCAqIlRvcCBIaXRzIiogYXRhdSAqIkRhaWx5IE1peCIqLg0KDQotLS0NCg0KIyAxMi4gS2VzaW1wdWxhbiBkYW4gUmVrb21lbmRhc2kNCg0KU2FtcGFpbGFoIGtpdGEgZGkgcGVuZ2h1anVuZyBhbmFsaXNpcy4gU2V0ZWxhaCBtZWxhbHVpIHByb3NlcyBwZW1iZXJzaWhhbiBkYXRhLCBwZXJoaXR1bmdhbiBtYXRlbWF0aWthIHlhbmcgcnVtaXQsIGhpbmdnYSB2aXN1YWxpc2FzaSB3YXJuYS13YXJuaSwgYmVyaWt1dCBhZGFsYWggcmFuZ2t1bWFuIG1lbnllbHVydWggZGFyaSBhcGEgeWFuZyB0ZWxhaCBraXRhIHRlbXVrYW4uDQoNCiMjIDEyLjEuIEtlc2ltcHVsYW4gVGVrbmlzIChBcGEgeWFuZyBLaXRhIFBlbGFqYXJpPykNCkRhcmkgc2lzaSB0ZWtuaXMgZGFuIGFsZ29yaXRtYSwga2l0YSBtZW55aW1wdWxrYW4gYmFod2E6DQoNCjEuICAqKlBlbWVuYW5nIEFsZ29yaXRtYTogV2FyZCdzIE1ldGhvZCoqDQogICAgKiBNZXNraXB1biBtZXRvZGUgKlNpbmdsZSBMaW5rYWdlKiBwdW55YSBuaWxhaSBzdGF0aXN0aWsgdGluZ2dpLCBoYXNpbG55YSAqKmJ1cnVrKiogKGtheWFrIHRhbmdnYSkuDQogICAgKiBNZXRvZGUgKipXYXJkKiogdGVycGlsaWggc2ViYWdhaSBqdWFyYSBrYXJlbmEgZGlhIHBhbGluZyAicGludGFyIiBtZW5hdGEgbGFndSBtZW5qYWRpIGtlbG9tcG9rLWtlbG9tcG9rIHlhbmcgcmFwaSwgc2VpbWJhbmcsIGRhbiBlbmFrIGRpbGloYXQgKGJlbnR1ayBwaXJhbWlkYSkuDQoNCjIuICAqKkp1bWxhaCBHZW5nIFRlcmJhaWs6IDQgQ2x1c3RlcioqDQogICAgKiBCZXJkYXNhcmthbiBncmFmaWsgKkVsYm93KiAoc2lrdSkgZGFuICpTaWxob3VldHRlKiAodGlhbmcpLCBtZW1iYWdpIGxhZ3UgbWVuamFkaSAqKjQga2Vsb21wb2sqKiBhZGFsYWgga2VwdXR1c2FuIHBhbGluZyB0ZXBhdC4gS2FsYXUgY3VtYSAyIHRlcmxhbHUgc2VkaWtpdCwga2FsYXUgMTAga2ViYW55YWthbi4NCg0KMy4gICoqUGVudGluZ255YSBTY2FsaW5nKioNCiAgICAqIERhdGEgbXVzaWsgaW5pIHB1bnlhIHNhdHVhbiBiZWRhLWJlZGEgKGFkYSBkZXRpaywgYWRhIGRlc2liZWwsIGFkYSBCUE0pLiBUYW5wYSBwcm9zZXMgKlNjYWxpbmcqIChtZW55YW1ha2FuIHN0YW5kYXIpIGRpIEJhYiA3LCBhbmFsaXNpcyBpbmkgYmFrYWwgZ2FnYWwgdG90YWwga2FyZW5hIER1cmFzaSBsYWd1IGFrYW4gZGlhbmdnYXAgcGFsaW5nIHBlbnRpbmcuDQoNCiMjIDEyLjIuIEtlc2ltcHVsYW4gRGF0YSAoQWRhIExhZ3UgQXBhIEFqYT8pDQpUZXJueWF0YSwgcmF0dXNhbiBsYWd1IHBvcHVsZXIgZGkgU3BvdGlmeSAoMjAxMC0yMDE5KSBiaXNhIGRpa2Vsb21wb2trYW4gbWVuamFkaSA0ICJLZXByaWJhZGlhbiIgdXRhbWE6DQoNCjEuICAqKkNsdXN0ZXIgMSAoU2kgUG9wdWxlciAvIE1haW5zdHJlYW0pOioqDQogICAgKiBJbmkgYWRhbGFoIGtlbG9tcG9rIHRlcmJlc2FyLiBJc2lueWEgbGFndS1sYWd1ICJhbWFuIiB5YW5nIGVuYWsgZGlkZW5nYXIgc2lhcGEgc2FqYS4gR2FrIHRlcmxhbHUgc2VkaWgsIGdhayB0ZXJsYWx1IGJlcmlzaWsuIExhZ3Ugc3RhbmRhciByYWRpby4NCjIuICAqKkNsdXN0ZXIgMiAoU2kgTmdlYnV0IC8gUGFydHkpOioqDQogICAgKiBJc2lueWEgbGFndS1sYWd1IGRlbmdhbiBUZW1wbyAoQlBNKSBjZXBhdCBkYW4gRW5lcmdpIHRpbmdnaS4gQ29jb2sgYnVhdCBsYXJpIHBhZ2kgYXRhdSBqb2dldCBkaSBrbHViLg0KMy4gICoqQ2x1c3RlciAzIChTaSBHYWxhdSAvIEFrdXN0aWspOioqDQogICAgKiBJc2lueWEgbGFndS1sYWd1IGxlbWVzLCBwZWxhbiwgZGFuIGFrdXN0aWsgYmFuZ2V0LiBDb2NvayBidWF0IHBlbmdhbnRhciB0aWR1ciBhdGF1IGxhZ2kgcGF0YWggaGF0aS4NCjQuICAqKkNsdXN0ZXIgNCAoU2kgRXJyb3IgLyBPdXRsaWVyKToqKg0KICAgICogS2l0YSBtZW5lbXVrYW4gMSBsYWd1IHlhbmcgZGF0YW55YSBhbmVoIChuaWxhaW55YSBub2wgc2VtdWEpLiBJbmkga2VtdW5na2luYW4gYmVzYXIga2VzYWxhaGFuIHNpc3RlbS9kYXRhIHJ1c2FrIHlhbmcgaGFydXMgZGlidWFuZy4NCg0KIyMgMTIuMy4gUmVrb21lbmRhc2kgKFNhcmFuIEJ1YXQgU3BvdGlmeSAmIFBlbmVsaXRpIFNlbGFuanV0bnlhKQ0KDQoqKlVudHVrIFBpaGFrIFNwb3RpZnkgKEJpc25pcyk6KioNCg0KMS4gICoqQmVyc2loa2FuIERhdGFiYXNlOioqIFNlZ2VyYSBoYXB1cyBkYXRhIGxhZ3UgeWFuZyBhZGEgZGkgKkNsdXN0ZXIgNCoga2FyZW5hIGl0dSBkYXRhIHNhbXBhaC9ydXNhay4NCg0KMi4gICoqRml0dXIgIk1vb2QgUGxheWxpc3QiOioqIEd1bmFrYW4gaGFzaWwgKkNsdXN0ZXIgMiogdW50dWsgbWVtYnVhdCBwbGF5bGlzdCBvdG9tYXRpcyAqIldvcmtvdXQgSGlnaCBJbnRlbnNpdHkiKiBkYW4gKkNsdXN0ZXIgMyogdW50dWsgcGxheWxpc3QgKiJSZWxheCAmIFNsZWVwIiouIEluaSBiYWthbCBtZW5pbmdrYXRrYW4ga2VwdWFzYW4gKnVzZXIqLg0KDQoqKlVudHVrIFBlbmVsaXRpIFNlbGFuanV0bnlhIChBa2FkZW1pcyk6KioNCg0KMS4gICoqQ29iYSBBbGdvcml0bWEgTGFpbjoqKiBTZWxhaW4gSGllcmFyY2hpY2FsLCBjb2JhIHBha2FpIGFsZ29yaXRtYSAqKkstTWVhbnMqKiBhdGF1ICoqREJTQ0FOKiouIFNpYXBhIHRhaHUgaGFzaWxueWEgbGViaWggYmFndXMgZGFuIGxlYmloIGNlcGF0Lg0KDQoyLiAgKipBbmFsaXNpcyBMaXJpazoqKiBKYW5nYW4gY3VtYSBwYWthaSBhdWRpby4gQ29iYSBhbmFsaXNpcyB0ZWtzIGxpcmlrbnlhIGp1Z2EuIExhZ3UgeWFuZyB0ZW1wb255YSBjZXBhdCAoQ2x1c3RlciAyKSBiZWx1bSB0ZW50dSBsaXJpa255YSBiYWhhZ2lhLCBiaXNhIHNhamEgbGlyaWtueWEgbWFyYWgtbWFyYWguDQoNCi0tLQ0KDQojIFJlZmVyZW5zaQ0KDQpCZXJpa3V0IGFkYWxhaCBkYWZ0YXIgcmVmZXJlbnNpIHlhbmcgZGlndW5ha2FuIHNlYmFnYWkgbGFuZGFzYW4gdGVvcmksIHN1bWJlciBkYXRhLCBkYW4gcGFuZHVhbiBwcmFrdGlrdW0gZGFsYW0gcGVueXVzdW5hbiBsYXBvcmFuIGluaToNCg0KMS4gICoqU2lyZWdhciwgQmFrdGkuKiogKDIwMjUpLiAqRGF0YSBTY2llbmNlOiBDbHVzdGVyaW5nICYgVW5zdXBlcnZpc2VkIExlYXJuaW5nKi4gVGVyc2VkaWEgc2VjYXJhIG9ubGluZSBkaTogW2h0dHBzOi8vYm9va2Rvd24ub3JnL2NvbnRlbnQvYTE0MmIxNzItNjliMi00MzZkLWJkYjAtOWRhNmQwNDZhMGY5LzA0LUNsdXN0ZXJpbmcuaHRtbF0oaHR0cHM6Ly9ib29rZG93bi5vcmcvY29udGVudC9hMTQyYjE3Mi02OWIyLTQzNmQtYmRiMC05ZGE2ZDA0NmEwZjkvMDQtQ2x1c3RlcmluZy5odG1sKS4NCiAgICAqICooUmVmZXJlbnNpIFV0YW1hOiBQYW5kdWFuIG1hdGVyaSBwZXJrdWxpYWhhbiBtZW5nZW5haSBIaWVyYXJjaGljYWwgQ2x1c3RlcmluZykuKg0KDQoyLiAgKipQZW5hLCBMZW9uYXJkby4qKiAoMjAxOSkuICpUb3AgU3BvdGlmeSBTb25ncyBmcm9tIDIwMTAtMjAxOSAtIEJZIFlFQVIqLiBLYWdnbGUgRGF0YXNldC4gVGVyc2VkaWEgZGk6IFtodHRwczovL3d3dy5rYWdnbGUuY29tL2RhdGFzZXRzL2xlb25hcmRvcGVuYS90b3Atc3BvdGlmeS1zb25ncy1mcm9tLTIwMTAyMDE5LWJ5LXllYXJdKGh0dHBzOi8vd3d3LmthZ2dsZS5jb20vZGF0YXNldHMvbGVvbmFyZG9wZW5hL3RvcC1zcG90aWZ5LXNvbmdzLWZyb20tMjAxMDIwMTktYnkteWVhcikuDQogICAgKiAqKFN1bWJlciBEYXRhOiBEYXRhc2V0IHB1YmxpayB5YW5nIGRpZ3VuYWthbikuKg0KDQozLiAgKipLYXNzYW1iYXJhLCBBbGJvdWthZGVsLioqICgyMDE3KS4gKlByYWN0aWNhbCBHdWlkZSB0byBDbHVzdGVyIEFuYWx5c2lzIGluIFIqLiBTVEhEQS4gVGVyc2VkaWEgZGk6IFtodHRwOi8vd3d3LnN0aGRhLmNvbS9lbmdsaXNoL3dpa2kvYmUtYXdlc29tZS1pbi1nZ3Bsb3QyLWEtcHJhY3RpY2FsLWd1aWRlLXRvLWJlLWhpZ2hseS1lZmZlY3RpdmUtci1zb2Z0d2FyZS1hbmQtZGF0YS12aXN1YWxpemF0aW9uXShodHRwOi8vd3d3LnN0aGRhLmNvbS9lbmdsaXNoL3dpa2kvYmUtYXdlc29tZS1pbi1nZ3Bsb3QyLWEtcHJhY3RpY2FsLWd1aWRlLXRvLWJlLWhpZ2hseS1lZmZlY3RpdmUtci1zb2Z0d2FyZS1hbmQtZGF0YS12aXN1YWxpemF0aW9uKS4NCiAgICAqICooUmVmZXJlbnNpIFRla25pczogUGFuZHVhbiB2aXN1YWxpc2FzaSBtZW5nZ3VuYWthbiBgZmFjdG9leHRyYWApLioNCg0K