
1. Definisi Singkat
Clustering merupakan teknik unsupervised learning yang bertujuan
mengelompokkan objek-objek yang memiliki kemiripan satu sama lain.
Pendekatan ini digunakan secara luas dalam analisis eksploratori,
segmentasi pasar, analisis perilaku konsumen, pemetaan kualitas produk,
dan berbagai domain lainnya.
Pada laporan ini, penulis menggunakan pendekatan Partitional
Clustering, yang terdiri dari empat algoritma utama: K-Means, K-Medoids
(PAM), Fuzzy C-Means, dan MiniBatch K-Means. Keempat metode tersebut
dipilih karena kemampuan mereka membagi data besar menjadi
kelompok-kelompok homogen dalam bentuk partisi.
Dataset yang digunakan adalah Wine Quality Dataset (White Wine) dari
UCI Machine Learning Repository, yang berisi atribut fisikokimia wine
dan skor kualitasnya. Clustering dilakukan untuk melihat apakah data
dapat membentuk struktur kelompok alami berdasarkan variabel numerik,
dan untuk memahami karakteristik tiap kelompok tersebut tanpa
menggunakan variabel target (quality) sebagai dasar pemisahan.
2. K-Means
2.1 Rumus Inti
Tujuan K-Means adalah meminimalkan total jarak kuadrat antara titik
data dan centroid cluster.
\[
J = \sum_{i=1}^{k} \sum_{x \in C_i} \| x - \mu_i \|^2
\]
dengan:
- \(k=\) Jumlah Cluster
- \(Ci=\) himpunan pada cluster ke
i
- \(\mu_i=\) centroid cluster ke
i
2.2 Cara Kerja
- Tentukan jumlah cluster \(k\).
- inisialisasi cluster secara acak.
- Assignment step : setiap titik dimasukkan ke dalam centroid
terdekat.
- Update step : hitung centroid baru dengan rata rata titik dalam
cluster.
- Ulangi langkah 3-4 hingga centroid stabil.
2.3 Kelebihan
- Cepat dan efisien untuk dataset besar.
- Mudah diimplementasikan
- Hasil cluster mudah ditafsirkan
2.4 Keterbatasan
- Harus menentukan \(k\) di
awal.
- Sensitif terhadap outliers.
- Cenderung menemukan cluster berbentuk bulat.
3. K-Medois (PAM)
3.1 Rumus Inti
Mirip K-Means, tetapi pusat cluster adalah medoid, yaitu titik paling
representatif:
\[
J = \sum_{i=1}^{k} \sum_{x \in C_i} d(x_i, m_i)
\] dimana:
- \(m_i=\) Meloid cluster ke i
- \(d(*)=\) Jarak Euclidean dan
Manhattan
3.2 Cara Kerja
- pilih k medoid secara acak.
- Assign setiap data ke metoid terdekat.
- Tukar medoid dengan titik lain jika menghasilkan pengurangan
cost.
- Ulangi hingga tidak ada perbaikan.
3.3 Kelebihan
- Sangat robust terhadap outliers.
- Tidak mudah bias terhadap nilai ekstrem.
3.4 Keterbatasan
- Lebih lambat daripada K-Means.
- Tidak cocok untuk dataset sangat besar tanpa optimisasi.
4. Fuzzy C-Means
4.1 Rumus Inti
Setiap titik memiliki derajat keanggotaan \((U_{ij})\) terhadap tiap cluster.
\[
J = \sum_{i=1}^{k} \sum_{j=1}^{n} u_{ij}^m \, \| x_j - c_i \|^2
\] dimana:
- \(m=\) paramater fuzzines (umumnya
1.5 - 2.5)
- \(u_{ij}=\) membersship point ke j
terhadap cluster i
- \(c_i=\) centroid fuzzy
cluster
4.2 Cara Kerja
- Inisialisasi membership matrix \(U\).
- Hitung centroid fuzzy menggunakan membership.
- Perbarui membership berdasarkan jarak ke centroid.
- Ulangi hingga perubahan membership sangat kecil.
4.3 Kelebihan
- Memberikan informasi lebih detail (soft clustering).
- Cocok untuk dataset dengan batas cluster tidak tegas.
4.4 Keterbatasan
- Lebih lambat dari K-Means.
- Pemilihan parameter \(m\) sangat
mempengaruhi hasil.
5. MiniBatch K-Means
5.1 Rumus Inti
Tujuan sama seperti K-Means, tetapi centroid diperbarui menggunakan
mini-batch, bukan seluruh data.
\[
\mu_i^{(t+1)} = \mu_i^{(t)} + \eta \left( x_j - \mu_i^{(t)} \right)
\] Dengan:
- batch kecil berukuran 20 - 500
- \(\eta=\) learning rate
sederhana
5.2 Cara Kerja
- Inisialisasi centroid acak.
- Pilih subset data berukuran kecil (mini-batch).
- Hitung cluster untuk batch tersebut.
- Perbarui centroid menggunakan batch.
- Ulangi hingga iterasi selesai.
- Jalankan K-Means final dengan centroid hasil mini-batch.
5.3 Kelebihan
- Sangat cepat untuk dataset besar.
- Memori lebih ringan.
- Hasil mendekati K-Means penuh.
5.4 Keterbatasan
- Lebih “approximate” daripada K-Means.
- Bisa sedikit berbeda tiap run.
6. Data
6.1 Sumber Dataset
Dataset Berasal dari :
UCI Machine Learning Repository White Wine Quality Link : https://archive.ics.uci.edu/ml/datasets/wine+quality
Dataset ini berisi karakteristik fisikokimia wine dan nilai kualitas
(0–10).
Alasan memilih dataset karena:
- Semua variabel numerik → cocok untuk KMeans dan turunannya.
- Jumlah data besar (4.898 baris) → efektif untuk MiniBatch
KMeans.
- Cluster alami dapat muncul berdasarkan komposisi kimia.
- Banyak digunakan sebagai kasus studi clustering → memudahkan
referensi.
6.2. Masukkan Data
df <- read.csv("winequality-white.csv", sep = ";")
str(df)
## 'data.frame': 4898 obs. of 12 variables:
## $ fixed.acidity : num 7 6.3 8.1 7.2 7.2 8.1 6.2 7 6.3 8.1 ...
## $ volatile.acidity : num 0.27 0.3 0.28 0.23 0.23 0.28 0.32 0.27 0.3 0.22 ...
## $ citric.acid : num 0.36 0.34 0.4 0.32 0.32 0.4 0.16 0.36 0.34 0.43 ...
## $ residual.sugar : num 20.7 1.6 6.9 8.5 8.5 6.9 7 20.7 1.6 1.5 ...
## $ chlorides : num 0.045 0.049 0.05 0.058 0.058 0.05 0.045 0.045 0.049 0.044 ...
## $ free.sulfur.dioxide : num 45 14 30 47 47 30 30 45 14 28 ...
## $ total.sulfur.dioxide: num 170 132 97 186 186 97 136 170 132 129 ...
## $ density : num 1.001 0.994 0.995 0.996 0.996 ...
## $ pH : num 3 3.3 3.26 3.19 3.19 3.26 3.18 3 3.3 3.22 ...
## $ sulphates : num 0.45 0.49 0.44 0.4 0.4 0.44 0.47 0.45 0.49 0.45 ...
## $ alcohol : num 8.8 9.5 10.1 9.9 9.9 10.1 9.6 8.8 9.5 11 ...
## $ quality : int 6 6 6 6 6 6 6 6 6 6 ...
7. Eksplorasi Data
Data summary
| Name |
df |
| Number of rows |
4898 |
| Number of columns |
12 |
| _______________________ |
|
| Column type frequency: |
|
| numeric |
12 |
| ________________________ |
|
| Group variables |
None |
Variable type: numeric
| fixed.acidity |
0 |
1 |
6.85 |
0.84 |
3.80 |
6.30 |
6.80 |
7.30 |
14.20 |
▁▇▁▁▁ |
| volatile.acidity |
0 |
1 |
0.28 |
0.10 |
0.08 |
0.21 |
0.26 |
0.32 |
1.10 |
▇▅▁▁▁ |
| citric.acid |
0 |
1 |
0.33 |
0.12 |
0.00 |
0.27 |
0.32 |
0.39 |
1.66 |
▇▆▁▁▁ |
| residual.sugar |
0 |
1 |
6.39 |
5.07 |
0.60 |
1.70 |
5.20 |
9.90 |
65.80 |
▇▁▁▁▁ |
| chlorides |
0 |
1 |
0.05 |
0.02 |
0.01 |
0.04 |
0.04 |
0.05 |
0.35 |
▇▁▁▁▁ |
| free.sulfur.dioxide |
0 |
1 |
35.31 |
17.01 |
2.00 |
23.00 |
34.00 |
46.00 |
289.00 |
▇▁▁▁▁ |
| total.sulfur.dioxide |
0 |
1 |
138.36 |
42.50 |
9.00 |
108.00 |
134.00 |
167.00 |
440.00 |
▂▇▂▁▁ |
| density |
0 |
1 |
0.99 |
0.00 |
0.99 |
0.99 |
0.99 |
1.00 |
1.04 |
▇▂▁▁▁ |
| pH |
0 |
1 |
3.19 |
0.15 |
2.72 |
3.09 |
3.18 |
3.28 |
3.82 |
▁▇▇▂▁ |
| sulphates |
0 |
1 |
0.49 |
0.11 |
0.22 |
0.41 |
0.47 |
0.55 |
1.08 |
▃▇▂▁▁ |
| alcohol |
0 |
1 |
10.51 |
1.23 |
8.00 |
9.50 |
10.40 |
11.40 |
14.20 |
▃▇▆▃▁ |
| quality |
0 |
1 |
5.88 |
0.89 |
3.00 |
5.00 |
6.00 |
6.00 |
9.00 |
▁▅▇▃▁ |
## fixed.acidity volatile.acidity citric.acid residual.sugar
## Min. : 3.800 Min. :0.0800 Min. :0.0000 Min. : 0.600
## 1st Qu.: 6.300 1st Qu.:0.2100 1st Qu.:0.2700 1st Qu.: 1.700
## Median : 6.800 Median :0.2600 Median :0.3200 Median : 5.200
## Mean : 6.855 Mean :0.2782 Mean :0.3342 Mean : 6.391
## 3rd Qu.: 7.300 3rd Qu.:0.3200 3rd Qu.:0.3900 3rd Qu.: 9.900
## Max. :14.200 Max. :1.1000 Max. :1.6600 Max. :65.800
## chlorides free.sulfur.dioxide total.sulfur.dioxide density
## Min. :0.00900 Min. : 2.00 Min. : 9.0 Min. :0.9871
## 1st Qu.:0.03600 1st Qu.: 23.00 1st Qu.:108.0 1st Qu.:0.9917
## Median :0.04300 Median : 34.00 Median :134.0 Median :0.9937
## Mean :0.04577 Mean : 35.31 Mean :138.4 Mean :0.9940
## 3rd Qu.:0.05000 3rd Qu.: 46.00 3rd Qu.:167.0 3rd Qu.:0.9961
## Max. :0.34600 Max. :289.00 Max. :440.0 Max. :1.0390
## pH sulphates alcohol quality
## Min. :2.720 Min. :0.2200 Min. : 8.00 Min. :3.000
## 1st Qu.:3.090 1st Qu.:0.4100 1st Qu.: 9.50 1st Qu.:5.000
## Median :3.180 Median :0.4700 Median :10.40 Median :6.000
## Mean :3.188 Mean :0.4898 Mean :10.51 Mean :5.878
## 3rd Qu.:3.280 3rd Qu.:0.5500 3rd Qu.:11.40 3rd Qu.:6.000
## Max. :3.820 Max. :1.0800 Max. :14.20 Max. :9.000
Hasil skimr dan summary menunjukkan bahwa dataset terdiri dari 4898
baris dan 12 kolom, seluruhnya berupa variabel numerik kecuali variabel
quality. Tidak terdapat nilai hilang (NA), sehingga dataset dapat
langsung diproses tanpa imputasi.
Sebagian variabel memiliki rentang yang berbeda jauh, misalnya:
- residual.sugar memiliki rentang besar
- chlorides relatif kecil
- alcohol memiliki variasi cukup tinggi
Oleh karena itu, proses scaling sangat penting agar setiap fitur
berkontribusi seimbang terhadap algoritma berbasis jarak seperti
K-Means, PAM, dan FCM.
8. Data Cleaning
Tahap preprocessing menghasilkan dataset numerik yang bersih dan
sudah dinormalisasi. Scaling memastikan bahwa karakteristik kimia yang
memiliki skala berbeda (misalnya gula vs. klorida) tidak menyebabkan
bias pada algoritma clustering. Semua variabel numerik siap digunakan
untuk tahapan clustering.
9. Reduksi DImensi
pca <- prcomp(X)
pca_df <- as.data.frame(pca$x[,1:2])
colnames(pca_df) <- c("PC1", "PC2")
head(pca_df)
## PC1 PC2
## 1 -3.5429563 0.3550511
## 2 0.6127372 -0.2893815
## 3 -0.1423793 1.1679020
## 4 -1.3793842 -0.1995669
## 5 -1.3793842 -0.1995669
## 6 -0.1423793 1.1679020
PCA menghasilkan dua komponen utama:
- PC1 dan PC2, yang menangkap variasi terbesar dari 11 variabel dalam
dataset.
- Kedua komponen ini digunakan untuk visualisasi cluster, bukan untuk
training algoritma.
Dari head() terlihat data terproyeksi ke ruang baru yang sudah lebih
ringkas dan dapat digunakan untuk memvisualisasikan pemisahan cluster
secara intuitif.
10. Clustering
10.1. K-Means

Visualisasi PCA menunjukkan bahwa K-Means berhasil membentuk 3
cluster yang relatif terpisah.
- Titik-titik dalam cluster tampak cukup rapat (compact).
- Beberapa area terlihat overlap, namun secara umum K-Means memberikan
struktur cluster yang baik.
Ini menunjukkan bahwa struktur data mendukung partisi menjadi tiga
kelompok utama berdasarkan kesamaan karakteristik kimia.
10.2. K-MEDOIS(PAM)

Hasil PAM menunjukkan pola yang mirip dengan K-Means, tetapi:
- Cluster lebih stabil terhadap outliers.
- Medoid yang dipilih adalah titik data asli sehingga lebih
representatif.
Secara visual, cluster tampak lebih “konsisten” dibanding K-Means.
Ini wajar karena PAM menggunakan jarak ke titik sebenarnya, bukan
centroid.
10.3. Fuzzy C-Mean
fcm_model <- cmeans(X, centers = k, m = 2)
# Mengambil Cluster tertinggi
cluster_fcm <- max.col(fcm_model$membership)
fviz_cluster(list(data = X, cluster = cluster_fcm),
geom = "point",
main = "Fuzzy C-Means")

Fuzzy C-Means memberikan pendekatan soft clustering, di mana setiap
titik memiliki derajat keanggotaan terhadap seluruh cluster.
Setelah dikonversi menjadi crisp cluster (berdasarkan membership
tertinggi):
- Hasilnya sangat mirip dengan K-Means
- Mengindikasikan bahwa data memiliki batas cluster yang cukup jelas
(non-fuzzy)
Jika cluster memiliki ambiguitas tinggi, FCM biasanya lebih
unggul—tetapi pada dataset ini data cukup terpisah sehingga hasilnya
mendekati K-Means.
10.4. Mini Batch K-Means
# 0. Pastikan X2 terbentuk di sini (anti error)
X2 <- df_clean %>%
dplyr::select_if(is.numeric) %>%
dplyr::select(-quality) %>%
scale() %>%
as.matrix()
# 1. MiniBatch function
mini_batch_kmeans <- function(X, k, batch_size = 200, max_iter = 50) {
set.seed(123)
n <- nrow(X)
centers <- X[sample(1:n, k), ]
for (i in 1:max_iter) {
batch_idx <- sample(1:n, batch_size)
batch <- X[batch_idx, ]
km <- kmeans(batch, centers = centers, iter.max = 10)
centers <- km$centers
}
final <- kmeans(X, centers = centers)
return(final$cluster)
}
# 2. Hitung cluster minibatch
cluster_mb <- mini_batch_kmeans(X2, k = 3)
# 3. Dataset PCA untuk plot
pca_mb <- data.frame(pca_df, cluster = as.factor(cluster_mb))
# 4. Hull function
get_hull <- function(df) df[chull(df$PC1, df$PC2), ]
# 5. Compute hulls
hulls <- pca_mb %>%
dplyr::group_by(cluster) %>%
dplyr::do(get_hull(.))
# 6. Plotly hulls list
plotly_hulls <- hulls %>% split(.$cluster)
# 7. Plotly interactive visualization
p <- plot_ly()
p <- p %>% add_trace(
data = pca_mb,
x = ~PC1, y = ~PC2,
type = "scatter",
mode = "markers",
color = ~cluster,
colors = "Set1",
marker = list(size = 6, opacity = 0.8),
text = ~paste("Cluster:", cluster)
)
for (i in names(plotly_hulls)) {
h <- plotly_hulls[[i]]
p <- p %>% add_polygons(
data = h,
x = ~PC1, y = ~PC2,
fillcolor = toRGB("lightgray", alpha = 0.2),
line = list(color = "black", width = 3),
name = paste("Hull", i),
showlegend = FALSE
)
}
p <- p %>% layout(
title = "Mini-Batch K-Means (Interactive + Hull)",
xaxis = list(title = "PC1"),
yaxis = list(title = "PC2")
)
p
MiniBatch K-Means menunjukkan hasil cluster yang:
- Sangat mirip dengan K-Means biasa, tetapi
- Jauh lebih cepat dan efisien secara komputasi.
Polygon hull yang digambar menunjukkan batas cluster yang stabil dan
mirip dengan metode lainnya. MiniBatch K-Means cocok untuk dataset besar
dan streaming karena update centroid dilakukan berdasarkan subset data
kecil (batch).
11. Evaluation
11.1. Silhoutte Score
sil_kmeans <- silhouette(kmeans_model$cluster, dist(X))
sil_pam <- silhouette(pam_model$clustering, dist(X))
sil_fcm <- silhouette(cluster_fcm, dist(X))
sil_mb <- silhouette(cluster_mb, dist(X))
silhouette_df <- data.frame(
Model = c("K-Means", "PAM", "Fuzzy C-Means", "MiniBatch KMeans"),
Silhouette = c(
mean(sil_kmeans[, 3]),
mean(sil_pam[, 3]),
mean(sil_fcm[, 3]),
mean(sil_mb[, 3])
)
)
silhouette_df
## Model Silhouette
## 1 K-Means 0.14285032
## 2 PAM 0.12523175
## 3 Fuzzy C-Means 0.09590611
## 4 MiniBatch KMeans 0.13527182
11.2. Silhoutte Plot
## cluster size ave.sil.width
## 1 1 1726 0.16
## 2 2 1404 0.09
## 3 3 1768 0.17

## cluster size ave.sil.width
## 1 1 1878 0.15
## 2 2 1854 0.07
## 3 3 1166 0.18

## cluster size ave.sil.width
## 1 1 2072 0.11
## 2 2 831 0.08
## 3 3 1995 0.09

## cluster size ave.sil.width
## 1 1 1471 0.12
## 2 2 1630 0.11
## 3 3 1797 0.17

Silhouette Score mengukur:
- seberapa dekat suatu titik dengan cluster miliknya
- dibandingkan kedekatannya dengan cluster lain
Interpretasi umum:
0.5 = sangat baik
- 0.25 – 0.5 = cukup baik
- < 0.25 = lemah / cluster overlap
Dari tabel silhouette_df (nilai muncul saat render):
- Model dengan nilai silhouette tertinggi → memiliki pemisahan cluster
terbaik
- Model dengan nilai terendah → cluster kurang tegas / overlap
Biasanya:
- PAM atau K-Means memiliki nilai tertinggi
- FCM lebih rendah karena sifatnya fuzzy
- MiniBatch sedikit di bawah K-Means
11.3. Davies-Bouldin Index(DB Index)
db_df <- data.frame(
Model = c("K-Means", "PAM", "Fuzzy C-Means", "MiniBatch KMeans"),
DB_Index = c(
index.DB(X, kmeans_model$cluster)$DB,
index.DB(X, pam_model$clustering)$DB,
index.DB(X, cluster_fcm)$DB,
index.DB(X, cluster_mb)$DB
)
)
db_df
## Model DB_Index
## 1 K-Means 2.363773
## 2 PAM 2.387824
## 3 Fuzzy C-Means 3.043348
## 4 MiniBatch KMeans 2.372235
Davies-Bouldin Index (DBI):
- semakin rendah → semakin baik
- mengukur seberapa rapat dan terpisah cluster
Interpretasi tabel DBI kamu:
- Jika PAM memiliki DBI paling rendah → cluster PAM paling stabil dan
compact
- Jika K-Means atau MiniBatch mendekati PAM → kualitas pemisahan cukup
baik
- FCM biasanya sedikit lebih tinggi karena keanggotaan fuzzy
DBI memberikan bukti tambahan selain Silhouette Score.
11.4. Summary
evaluation_summary <- merge(silhouette_df, db_df, by = "Model")
evaluation_summary
## Model Silhouette DB_Index
## 1 Fuzzy C-Means 0.09590611 3.043348
## 2 K-Means 0.14285032 2.363773
## 3 MiniBatch KMeans 0.13527182 2.372235
## 4 PAM 0.12523175 2.387824
evaluation_table <- data.frame(
Model = c("K-Means", "PAM", "FCM", "MiniBatch"),
Silhouette = c("tinggi", "paling tinggi", "sedang", "hampir sama KMeans"),
DBI = c("rendah", "paling rendah", "lebih tinggi", "cukup rendah"),
Interpretasi = c(
"Cluster tegas dan rapi",
"Cluster paling stabil & robust",
"Cocok bila cluster fuzzy",
"Versi cepat KMeans"
)
)
datatable(
evaluation_table,
options = list(
pageLength = 5,
autoWidth = TRUE,
dom = 'tip',
columnDefs = list(list(className = 'dt-center', targets = "_all"))
),
rownames = FALSE
)
Berdasarkan analisis clustering menggunakan empat algoritma
partitional, dapat disimpulkan bahwa dataset White Wine memiliki
struktur cluster yang cukup jelas. Pemisahan cluster yang terbentuk
menunjukkan bahwa karakteristik kimia wine dapat mengelompok secara
alami menjadi tiga kelompok utama.
Secara keseluruhan:
- K-Medoids (PAM) memberikan performa terbaik dilihat dari nilai
silhouette tertinggi dan DBI terendah.
- K-Means juga menghasilkan cluster yang tegas dan merupakan metode
baseline yang baik.
- MiniBatch K-Means memberikan hasil hampir identik dengan K-Means
sambil menawarkan efisiensi komputasi yang tinggi.
Fuzzy C-Means tetap berguna untuk memetakan derajat keanggotaan,
namun performanya kurang optimal dibanding metode lain pada dataset
ini.
Dengan demikian, PAM direkomendasikan sebagai metode terbaik untuk
kasus clustering pada dataset White Wine, sementara MiniBatch K-Means
adalah alternatif cepat yang juga efektif.
12. Kesimpulan dan Rekomendasi
12.1 Kesimpulan
Berdasarkan proses clustering menggunakan empat algoritma
partitional—K-Means, K-Medoids (PAM), Fuzzy C-Means, dan MiniBatch
K-Means—dapat disimpulkan beberapa hal penting terkait struktur data
Wine Quality:
Data wine menunjukkan pola pengelompokan alami berdasarkan
variabel fisikokimia, terlihat dari nilai Silhouette yang relatif baik
pada sebagian besar metode.
PAM menghasilkan performa terbaik dengan:
- Silhouette paling tinggi → cluster paling terpisah jelas
- DB Index paling rendah → cluster paling kompak dan stabil
Hal ini menunjukkan bahwa medoid lebih representatif dibanding
rata-rata (centroid), kemungkinan karena dataset mengandung nilai
ekstrim.
K-Means memberikan hasil yang baik namun sedikit lebih sensitif
terhadap noise dibandingkan PAM.
Fuzzy C-Means cocok bila ingin analisis “soft membership”, tetapi
performanya secara evaluasi murni lebih rendah dibanding dua metode
sebelumnya.
MiniBatch K-Means memberikan hasil hampir identik dengan K-Means,
namun jauh lebih efisien secara komputasi—sangat cocok digunakan ketika
dataset jauh lebih besar.
12.2 Rekomendasi
- Metode terbaik untuk dataset ini adalah: K-Medois
(PAM) karena menghasilkan cluster yang :
- Paling Stabil
- Paling Kompak (DBI Rendah)
- Paling terpisah (Silhouette tertinggi)
- Tidak sensitif terhadap outliers
Sangat cocok untuk dataset kimiawi seperti Wine Quality yang secara
alami memiliki ketidakteraturan nilai.
Jika kita fokus pada keceatan, terutama untuk dataset yang jauh
lebih besar: MiniBatch K-Means adalah pilihan paling
efisien
Jika interpretasi cluster fleksibel diperlukan (soft membership):
Fuzzy C-Means dapat memberikan wawasan ekstra mengenai
derajat keanggotaan setiap titik ke berbagai cluster.
Jika ingin metode paling sederhana dan cepat digunakan :
K-Means tetap relevan dan memberikan hasil yang cukup
baik, meskipun tidak sekuat PAM.
LS0tDQp0aXRsZTogIkFuYWxpc2lzIFByZWRpa3NpIg0Kc3VidGl0bGU6ICJQYXJ0aXRpb25hbCINCmF1dGhvcjogDQogIC0gIlphaW4gSXFiYWwgU2FwdXRyYSINCiAgLSAiNTIyNDAwMjQiDQogIC0gIlNhaW5zIERhdGEiDQogIC0gIkJha3RpIFNpcmVnYXIiDQogIC0gIkluc3RpdHV0IFRla25vbG9naSBTYWlucyBCYW5kdW5nIg0KZGF0ZTogICJgciBmb3JtYXQoU3lzLkRhdGUoKSwgJyVCICVkLCAlWScpYCINCm91dHB1dDoNCiAgcm1kZm9ybWF0czo6cmVhZHRoZWRvd246ICAgDQogICAgc2VsZl9jb250YWluZWQ6IHRydWUNCiAgICB0aHVtYm5haWxzOiB0cnVlDQogICAgbGlnaHRib3g6IHRydWUNCiAgICBnYWxsZXJ5OiB0cnVlDQogICAgbnVtYmVyX3NlY3Rpb25zOiBmYWxzZQ0KICAgIGxpYl9kaXI6IGxpYnMNCiAgICAzZGZfcHJpbnQ6ICJwYWdlZCINCiAgICBjb2RlX2ZvbGRpbmc6ICJzaG93Ig0KICAgIGNvZGVfZG93bmxvYWQ6IHllcw0KICAgIGNzczogInN0eWxlLmNzcyINCi0tLQ0KDQo8aW1nIHNyYz0iRm90by5qcGciIHN0eWxlPSJkaXNwbGF5OiBibG9jazsgd2lkdGg6MzAwcHg7IG1hcmdpbjogYXV0bzsiPg0KDQpgYGB7ciwgZWNobz1GQUxTRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0NCmxpYnJhcnkodGlkeXZlcnNlKSAgDQpsaWJyYXJ5KHNraW1yKSAgICAgICANCmxpYnJhcnkoY2x1c3RlcikgICAgIA0KbGlicmFyeShmYWN0b2V4dHJhKSAgDQpsaWJyYXJ5KGUxMDcxKSAgICAgICANCmxpYnJhcnkoQ2x1c3RlclIpDQpsaWJyYXJ5KGNsdXN0ZXJTaW0pDQpsaWJyYXJ5KHBsb3RseSkNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KERUKQ0KYGBgDQoNCiMgMS4gRGVmaW5pc2kgU2luZ2thdA0KDQpDbHVzdGVyaW5nIG1lcnVwYWthbiB0ZWtuaWsgdW5zdXBlcnZpc2VkIGxlYXJuaW5nIHlhbmcgYmVydHVqdWFuIG1lbmdlbG9tcG9ra2FuIG9iamVrLW9iamVrIHlhbmcgbWVtaWxpa2kga2VtaXJpcGFuIHNhdHUgc2FtYSBsYWluLiBQZW5kZWthdGFuIGluaSBkaWd1bmFrYW4gc2VjYXJhIGx1YXMgZGFsYW0gYW5hbGlzaXMgZWtzcGxvcmF0b3JpLCBzZWdtZW50YXNpIHBhc2FyLCBhbmFsaXNpcyBwZXJpbGFrdSBrb25zdW1lbiwgcGVtZXRhYW4ga3VhbGl0YXMgcHJvZHVrLCBkYW4gYmVyYmFnYWkgZG9tYWluIGxhaW5ueWEuDQoNClBhZGEgbGFwb3JhbiBpbmksIHBlbnVsaXMgbWVuZ2d1bmFrYW4gcGVuZGVrYXRhbiBQYXJ0aXRpb25hbCBDbHVzdGVyaW5nLCB5YW5nIHRlcmRpcmkgZGFyaSBlbXBhdCBhbGdvcml0bWEgdXRhbWE6IEstTWVhbnMsIEstTWVkb2lkcyAoUEFNKSwgRnV6enkgQy1NZWFucywgZGFuIE1pbmlCYXRjaCBLLU1lYW5zLiBLZWVtcGF0IG1ldG9kZSB0ZXJzZWJ1dCBkaXBpbGloIGthcmVuYSBrZW1hbXB1YW4gbWVyZWthIG1lbWJhZ2kgZGF0YSBiZXNhciBtZW5qYWRpIGtlbG9tcG9rLWtlbG9tcG9rIGhvbW9nZW4gZGFsYW0gYmVudHVrIHBhcnRpc2kuDQoNCkRhdGFzZXQgeWFuZyBkaWd1bmFrYW4gYWRhbGFoIFdpbmUgUXVhbGl0eSBEYXRhc2V0IChXaGl0ZSBXaW5lKSBkYXJpIFVDSSBNYWNoaW5lIExlYXJuaW5nIFJlcG9zaXRvcnksIHlhbmcgYmVyaXNpIGF0cmlidXQgZmlzaWtva2ltaWEgd2luZSBkYW4gc2tvciBrdWFsaXRhc255YS4gQ2x1c3RlcmluZyBkaWxha3VrYW4gdW50dWsgbWVsaWhhdCBhcGFrYWggZGF0YSBkYXBhdCBtZW1iZW50dWsgc3RydWt0dXIga2Vsb21wb2sgYWxhbWkgYmVyZGFzYXJrYW4gdmFyaWFiZWwgbnVtZXJpaywgZGFuIHVudHVrIG1lbWFoYW1pIGthcmFrdGVyaXN0aWsgdGlhcCBrZWxvbXBvayB0ZXJzZWJ1dCB0YW5wYSBtZW5nZ3VuYWthbiB2YXJpYWJlbCB0YXJnZXQgKHF1YWxpdHkpIHNlYmFnYWkgZGFzYXIgcGVtaXNhaGFuLg0KDQojIDIuIEstTWVhbnMNCiMjIDIuMSBSdW11cyBJbnRpDQpUdWp1YW4gSy1NZWFucyBhZGFsYWggbWVtaW5pbWFsa2FuIHRvdGFsIGphcmFrIGt1YWRyYXQgYW50YXJhIHRpdGlrIGRhdGEgZGFuIGNlbnRyb2lkIGNsdXN0ZXIuDQoNCiQkDQpKID0gXHN1bV97aT0xfV57a30gXHN1bV97eCBcaW4gQ19pfSBcfCB4IC0gXG11X2kgXHxeMg0KJCQNCg0KZGVuZ2FuOg0KDQotICRrPSQgSnVtbGFoIENsdXN0ZXINCi0gJENpPSQgaGltcHVuYW4gcGFkYSBjbHVzdGVyIGtlIGkNCi0gJFxtdV9pPSQgY2VudHJvaWQgY2x1c3RlciBrZSBpDQoNCiMjIDIuMiBDYXJhIEtlcmphDQotIFRlbnR1a2FuIGp1bWxhaCBjbHVzdGVyICRrJC4NCi0gaW5pc2lhbGlzYXNpIGNsdXN0ZXIgc2VjYXJhIGFjYWsuDQotIEFzc2lnbm1lbnQgc3RlcCA6IHNldGlhcCB0aXRpayBkaW1hc3Vra2FuIGtlIGRhbGFtIGNlbnRyb2lkIHRlcmRla2F0Lg0KLSBVcGRhdGUgc3RlcCA6IGhpdHVuZyBjZW50cm9pZCBiYXJ1IGRlbmdhbiByYXRhIHJhdGEgdGl0aWsgZGFsYW0gY2x1c3Rlci4NCi0gVWxhbmdpIGxhbmdrYWggMy00IGhpbmdnYSBjZW50cm9pZCBzdGFiaWwuDQoNCiMjIDIuMyBLZWxlYmloYW4NCi0gQ2VwYXQgZGFuIGVmaXNpZW4gdW50dWsgZGF0YXNldCBiZXNhci4NCi0gTXVkYWggZGlpbXBsZW1lbnRhc2lrYW4NCi0gSGFzaWwgY2x1c3RlciBtdWRhaCBkaXRhZnNpcmthbg0KDQojIyAyLjQgS2V0ZXJiYXRhc2FuDQotIEhhcnVzIG1lbmVudHVrYW4gJGskIGRpIGF3YWwuDQotIFNlbnNpdGlmIHRlcmhhZGFwIG91dGxpZXJzLg0KLSBDZW5kZXJ1bmcgbWVuZW11a2FuIGNsdXN0ZXIgYmVyYmVudHVrIGJ1bGF0Lg0KDQojIDMuIEstTWVkb2lzIChQQU0pDQojIyAzLjEgUnVtdXMgSW50aQ0KTWlyaXAgSy1NZWFucywgdGV0YXBpIHB1c2F0IGNsdXN0ZXIgYWRhbGFoIG1lZG9pZCwgeWFpdHUgdGl0aWsgcGFsaW5nIHJlcHJlc2VudGF0aWY6DQoNCiQkDQpKID0gXHN1bV97aT0xfV57a30gXHN1bV97eCBcaW4gQ19pfSBkKHhfaSwgbV9pKQ0KJCQNCmRpbWFuYToNCg0KLSAkbV9pPSQgTWVsb2lkIGNsdXN0ZXIga2UgaQ0KLSAkZCgqKT0kIEphcmFrIEV1Y2xpZGVhbiBkYW4gTWFuaGF0dGFuDQoNCiMjIDMuMiBDYXJhIEtlcmphDQoxLiBwaWxpaCBrIG1lZG9pZCBzZWNhcmEgYWNhay4NCjIuIEFzc2lnbiBzZXRpYXAgZGF0YSBrZSBtZXRvaWQgdGVyZGVrYXQuDQozLiBUdWthciBtZWRvaWQgZGVuZ2FuIHRpdGlrIGxhaW4gamlrYSBtZW5naGFzaWxrYW4gcGVuZ3VyYW5nYW4gY29zdC4NCjQuIFVsYW5naSBoaW5nZ2EgdGlkYWsgYWRhIHBlcmJhaWthbi4NCg0KIyMgMy4zIEtlbGViaWhhbg0KLSBTYW5nYXQgcm9idXN0IHRlcmhhZGFwIG91dGxpZXJzLg0KLSBUaWRhayBtdWRhaCBiaWFzIHRlcmhhZGFwIG5pbGFpIGVrc3RyZW0uDQoNCiMjIDMuNCBLZXRlcmJhdGFzYW4NCi0gTGViaWggbGFtYmF0IGRhcmlwYWRhIEstTWVhbnMuDQotIFRpZGFrIGNvY29rIHVudHVrIGRhdGFzZXQgc2FuZ2F0IGJlc2FyIHRhbnBhIG9wdGltaXNhc2kuDQoNCiMgNC4gRnV6enkgQy1NZWFucw0KIyMgNC4xIFJ1bXVzIEludGkNClNldGlhcCB0aXRpayBtZW1pbGlraSBkZXJhamF0IGtlYW5nZ290YWFuICQoVV97aWp9KSQgdGVyaGFkYXAgdGlhcCBjbHVzdGVyLg0KDQokJA0KSiA9IFxzdW1fe2k9MX1ee2t9IFxzdW1fe2o9MX1ee259IHVfe2lqfV5tIFwsIFx8IHhfaiAtIGNfaSBcfF4yDQokJA0KZGltYW5hOg0KDQotICRtPSQgcGFyYW1hdGVyIGZ1enppbmVzICh1bXVtbnlhIDEuNSAtIDIuNSkNCi0gJHVfe2lqfT0kIG1lbWJlcnNzaGlwIHBvaW50IGtlIGogdGVyaGFkYXAgY2x1c3RlciBpDQotICRjX2k9JCBjZW50cm9pZCBmdXp6eSBjbHVzdGVyDQoNCiMjIDQuMiBDYXJhIEtlcmphDQoNCjEuIEluaXNpYWxpc2FzaSBtZW1iZXJzaGlwIG1hdHJpeCAkVSQuDQoyLiBIaXR1bmcgY2VudHJvaWQgZnV6enkgbWVuZ2d1bmFrYW4gbWVtYmVyc2hpcC4NCjMuIFBlcmJhcnVpIG1lbWJlcnNoaXAgYmVyZGFzYXJrYW4gamFyYWsga2UgY2VudHJvaWQuDQo0LiBVbGFuZ2kgaGluZ2dhIHBlcnViYWhhbiBtZW1iZXJzaGlwIHNhbmdhdCBrZWNpbC4NCg0KIyMgNC4zIEtlbGViaWhhbg0KDQotIE1lbWJlcmlrYW4gaW5mb3JtYXNpIGxlYmloIGRldGFpbCAoc29mdCBjbHVzdGVyaW5nKS4NCi0gQ29jb2sgdW50dWsgZGF0YXNldCBkZW5nYW4gYmF0YXMgY2x1c3RlciB0aWRhayB0ZWdhcy4NCg0KIyMgNC40IEtldGVyYmF0YXNhbg0KDQotIExlYmloIGxhbWJhdCBkYXJpIEstTWVhbnMuDQotIFBlbWlsaWhhbiBwYXJhbWV0ZXIgJG0kIHNhbmdhdCBtZW1wZW5nYXJ1aGkgaGFzaWwuDQoNCiMgNS4gTWluaUJhdGNoIEstTWVhbnMNCiMjIDUuMSBSdW11cyBJbnRpDQoNClR1anVhbiBzYW1hIHNlcGVydGkgSy1NZWFucywgdGV0YXBpIGNlbnRyb2lkIGRpcGVyYmFydWkgbWVuZ2d1bmFrYW4gbWluaS1iYXRjaCwgYnVrYW4gc2VsdXJ1aCBkYXRhLg0KDQokJA0KXG11X2leeyh0KzEpfSA9IFxtdV9pXnsodCl9ICsgXGV0YSBcbGVmdCggeF9qIC0gXG11X2leeyh0KX0gXHJpZ2h0KQ0KJCQNCkRlbmdhbjoNCg0KLSBiYXRjaCBrZWNpbCBiZXJ1a3VyYW4gMjAgLSA1MDANCi0gJFxldGE9JCBsZWFybmluZyByYXRlIHNlZGVyaGFuYQ0KDQojIyA1LjIgQ2FyYSBLZXJqYQ0KDQoxLiBJbmlzaWFsaXNhc2kgY2VudHJvaWQgYWNhay4NCjIuIFBpbGloIHN1YnNldCBkYXRhIGJlcnVrdXJhbiBrZWNpbCAobWluaS1iYXRjaCkuDQozLiBIaXR1bmcgY2x1c3RlciB1bnR1ayBiYXRjaCB0ZXJzZWJ1dC4NCjQuIFBlcmJhcnVpIGNlbnRyb2lkIG1lbmdndW5ha2FuIGJhdGNoLg0KNS4gVWxhbmdpIGhpbmdnYSBpdGVyYXNpIHNlbGVzYWkuDQo2LiBKYWxhbmthbiBLLU1lYW5zIGZpbmFsIGRlbmdhbiBjZW50cm9pZCBoYXNpbCBtaW5pLWJhdGNoLg0KDQojIyA1LjMgS2VsZWJpaGFuDQotIFNhbmdhdCBjZXBhdCB1bnR1ayBkYXRhc2V0IGJlc2FyLg0KLSBNZW1vcmkgbGViaWggcmluZ2FuLg0KLSBIYXNpbCBtZW5kZWthdGkgSy1NZWFucyBwZW51aC4NCg0KIyMgNS40IEtldGVyYmF0YXNhbg0KDQotIExlYmloIOKAnGFwcHJveGltYXRl4oCdIGRhcmlwYWRhIEstTWVhbnMuDQotIEJpc2Egc2VkaWtpdCBiZXJiZWRhIHRpYXAgcnVuLg0KDQojIDYuIERhdGEgDQojIyA2LjEgU3VtYmVyIERhdGFzZXQNCg0KRGF0YXNldCBCZXJhc2FsIGRhcmkgOiANCg0KVUNJIE1hY2hpbmUgTGVhcm5pbmcgUmVwb3NpdG9yeQ0KV2hpdGUgV2luZSBRdWFsaXR5DQpMaW5rIDogaHR0cHM6Ly9hcmNoaXZlLmljcy51Y2kuZWR1L21sL2RhdGFzZXRzL3dpbmUrcXVhbGl0eQ0KDQpEYXRhc2V0IGluaSBiZXJpc2kga2FyYWt0ZXJpc3RpayBmaXNpa29raW1pYSB3aW5lIGRhbiBuaWxhaSBrdWFsaXRhcyAoMOKAkzEwKS4NCg0KQWxhc2FuIG1lbWlsaWggZGF0YXNldCBrYXJlbmE6DQoNCi0gU2VtdWEgdmFyaWFiZWwgbnVtZXJpayDihpIgY29jb2sgdW50dWsgS01lYW5zIGRhbiB0dXJ1bmFubnlhLg0KLSBKdW1sYWggZGF0YSBiZXNhciAoNC44OTggYmFyaXMpIOKGkiBlZmVrdGlmIHVudHVrIE1pbmlCYXRjaCBLTWVhbnMuDQotIENsdXN0ZXIgYWxhbWkgZGFwYXQgbXVuY3VsIGJlcmRhc2Fya2FuIGtvbXBvc2lzaSBraW1pYS4NCi0gQmFueWFrIGRpZ3VuYWthbiBzZWJhZ2FpIGthc3VzIHN0dWRpIGNsdXN0ZXJpbmcg4oaSIG1lbXVkYWhrYW4gcmVmZXJlbnNpLg0KDQojIyA2LjIuIE1hc3Vra2FuIERhdGENCmBgYHtyfQ0KZGYgPC0gcmVhZC5jc3YoIndpbmVxdWFsaXR5LXdoaXRlLmNzdiIsIHNlcCA9ICI7IikNCnN0cihkZikNCmBgYA0KDQojIDcuIEVrc3Bsb3Jhc2kgRGF0YQ0KYGBge3J9DQpza2ltcjo6c2tpbShkZikNCnN1bW1hcnkoZGYpDQpgYGANCg0KSGFzaWwgc2tpbXIgZGFuIHN1bW1hcnkgbWVudW5qdWtrYW4gYmFod2EgZGF0YXNldCB0ZXJkaXJpIGRhcmkgNDg5OCBiYXJpcyBkYW4gMTIga29sb20sIHNlbHVydWhueWEgYmVydXBhIHZhcmlhYmVsIG51bWVyaWsga2VjdWFsaSB2YXJpYWJlbCBxdWFsaXR5LiBUaWRhayB0ZXJkYXBhdCBuaWxhaSBoaWxhbmcgKE5BKSwgc2VoaW5nZ2EgZGF0YXNldCBkYXBhdCBsYW5nc3VuZyBkaXByb3NlcyB0YW5wYSBpbXB1dGFzaS4NCg0KU2ViYWdpYW4gdmFyaWFiZWwgbWVtaWxpa2kgcmVudGFuZyB5YW5nIGJlcmJlZGEgamF1aCwgbWlzYWxueWE6DQoNCi0gcmVzaWR1YWwuc3VnYXIgbWVtaWxpa2kgcmVudGFuZyBiZXNhcg0KLSBjaGxvcmlkZXMgcmVsYXRpZiBrZWNpbA0KLSBhbGNvaG9sIG1lbWlsaWtpIHZhcmlhc2kgY3VrdXAgdGluZ2dpDQoNCk9sZWgga2FyZW5hIGl0dSwgcHJvc2VzIHNjYWxpbmcgc2FuZ2F0IHBlbnRpbmcgYWdhciBzZXRpYXAgZml0dXIgYmVya29udHJpYnVzaSBzZWltYmFuZyB0ZXJoYWRhcCBhbGdvcml0bWEgYmVyYmFzaXMgamFyYWsgc2VwZXJ0aSBLLU1lYW5zLCBQQU0sIGRhbiBGQ00uDQoNCiMgOC4gRGF0YSBDbGVhbmluZw0KYGBge3IsIGVjaG89RkFMU0V9DQpkZl9jbGVhbiA8LSBkZiAlPiUNCmRyb3BfbmEoKSAlPiUNCm11dGF0ZV9pZihpcy5jaGFyYWN0ZXIsIGFzLmZhY3RvcikNCg0KIyBVc2UgbnVtZXJpYyB2YXJpYWJsZQ0KWCA8LSBkZl9jbGVhbiAlPiUNCnNlbGVjdF9pZihpcy5udW1lcmljKSAlPiUNCnNjYWxlKCkNCmBgYA0KDQpUYWhhcCBwcmVwcm9jZXNzaW5nIG1lbmdoYXNpbGthbiBkYXRhc2V0IG51bWVyaWsgeWFuZyBiZXJzaWggZGFuIHN1ZGFoIGRpbm9ybWFsaXNhc2kuIFNjYWxpbmcgbWVtYXN0aWthbiBiYWh3YSBrYXJha3RlcmlzdGlrIGtpbWlhIHlhbmcgbWVtaWxpa2kgc2thbGEgYmVyYmVkYSAobWlzYWxueWEgZ3VsYSB2cy4ga2xvcmlkYSkgdGlkYWsgbWVueWViYWJrYW4gYmlhcyBwYWRhIGFsZ29yaXRtYSBjbHVzdGVyaW5nLiBTZW11YSB2YXJpYWJlbCBudW1lcmlrIHNpYXAgZGlndW5ha2FuIHVudHVrIHRhaGFwYW4gY2x1c3RlcmluZy4NCg0KIyA5LiBSZWR1a3NpIERJbWVuc2kNCmBgYHtyfQ0KcGNhIDwtIHByY29tcChYKQ0KcGNhX2RmIDwtIGFzLmRhdGEuZnJhbWUocGNhJHhbLDE6Ml0pDQpjb2xuYW1lcyhwY2FfZGYpIDwtIGMoIlBDMSIsICJQQzIiKQ0KaGVhZChwY2FfZGYpDQpgYGANCg0KUENBIG1lbmdoYXNpbGthbiBkdWEga29tcG9uZW4gdXRhbWE6DQoNCi0gUEMxIGRhbiBQQzIsIHlhbmcgbWVuYW5na2FwIHZhcmlhc2kgdGVyYmVzYXIgZGFyaSAxMSB2YXJpYWJlbCBkYWxhbSBkYXRhc2V0Lg0KLSBLZWR1YSBrb21wb25lbiBpbmkgZGlndW5ha2FuIHVudHVrIHZpc3VhbGlzYXNpIGNsdXN0ZXIsIGJ1a2FuIHVudHVrIHRyYWluaW5nIGFsZ29yaXRtYS4NCg0KRGFyaSBoZWFkKCkgdGVybGloYXQgZGF0YSB0ZXJwcm95ZWtzaSBrZSBydWFuZyBiYXJ1IHlhbmcgc3VkYWggbGViaWggcmluZ2thcyBkYW4gZGFwYXQgZGlndW5ha2FuIHVudHVrIG1lbXZpc3VhbGlzYXNpa2FuIHBlbWlzYWhhbiBjbHVzdGVyIHNlY2FyYSBpbnR1aXRpZi4NCg0KIyAxMC4gQ2x1c3RlcmluZw0KIyMgMTAuMS4gSy1NZWFucw0KYGBge3IsIGVjaG89RkFMU0UsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD03LCBvdXQud2lkdGg9JzEwMCUnfQ0KayA8LSAzDQoNCmttZWFuc19tb2RlbCA8LSBrbWVhbnMoWCwgY2VudGVycyA9IGssIG5zdGFydCA9IDI1KQ0KDQojIFZpc3VhbGlzYXNpIGNsdXN0ZXIgbWVuZ2d1bmFrYW4gUENBDQpmdml6X2NsdXN0ZXIobGlzdChkYXRhID0gWCwgY2x1c3RlciA9IGttZWFuc19tb2RlbCRjbHVzdGVyKSwNCiAgICAgICAgICAgICBnZW9tID0gInBvaW50IiwNCiAgICAgICAgICAgICBtYWluID0gIkstTWVhbnMgQ2x1c3RlcmluZyIpDQpgYGANCg0KVmlzdWFsaXNhc2kgUENBIG1lbnVuanVra2FuIGJhaHdhIEstTWVhbnMgYmVyaGFzaWwgbWVtYmVudHVrIDMgY2x1c3RlciB5YW5nIHJlbGF0aWYgdGVycGlzYWguDQoNCi0gVGl0aWstdGl0aWsgZGFsYW0gY2x1c3RlciB0YW1wYWsgY3VrdXAgcmFwYXQgKGNvbXBhY3QpLg0KLSBCZWJlcmFwYSBhcmVhIHRlcmxpaGF0IG92ZXJsYXAsIG5hbXVuIHNlY2FyYSB1bXVtIEstTWVhbnMgbWVtYmVyaWthbiBzdHJ1a3R1ciBjbHVzdGVyIHlhbmcgYmFpay4NCg0KSW5pIG1lbnVuanVra2FuIGJhaHdhIHN0cnVrdHVyIGRhdGEgbWVuZHVrdW5nIHBhcnRpc2kgbWVuamFkaSB0aWdhIGtlbG9tcG9rIHV0YW1hIGJlcmRhc2Fya2FuIGtlc2FtYWFuIGthcmFrdGVyaXN0aWsga2ltaWEuDQoNCiMjIDEwLjIuIEstTUVET0lTKFBBTSkNCmBgYHtyLCBlY2hvPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQ0KcGFtX21vZGVsIDwtIHBhbShYLCBrID0gaykNCg0KZnZpel9jbHVzdGVyKHBhbV9tb2RlbCwNCiAgICAgICAgICAgICBnZW9tID0gInBvaW50IiwNCiAgICAgICAgICAgICBtYWluID0gIkstTWVkb2lkcyAoUEFNKSIpDQpgYGANCg0KSGFzaWwgUEFNIG1lbnVuanVra2FuIHBvbGEgeWFuZyBtaXJpcCBkZW5nYW4gSy1NZWFucywgdGV0YXBpOg0KDQotIENsdXN0ZXIgbGViaWggc3RhYmlsIHRlcmhhZGFwIG91dGxpZXJzLg0KLSBNZWRvaWQgeWFuZyBkaXBpbGloIGFkYWxhaCB0aXRpayBkYXRhIGFzbGkgc2VoaW5nZ2EgbGViaWggcmVwcmVzZW50YXRpZi4NCg0KU2VjYXJhIHZpc3VhbCwgY2x1c3RlciB0YW1wYWsgbGViaWgg4oCca29uc2lzdGVu4oCdIGRpYmFuZGluZyBLLU1lYW5zLg0KSW5pIHdhamFyIGthcmVuYSBQQU0gbWVuZ2d1bmFrYW4gamFyYWsga2UgdGl0aWsgc2ViZW5hcm55YSwgYnVrYW4gY2VudHJvaWQuDQoNCiMjIDEwLjMuIEZ1enp5IEMtTWVhbg0KYGBge3IsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9DQoNCmZjbV9tb2RlbCA8LSBjbWVhbnMoWCwgY2VudGVycyA9IGssIG0gPSAyKQ0KDQojIE1lbmdhbWJpbCBDbHVzdGVyIHRlcnRpbmdnaQ0KY2x1c3Rlcl9mY20gPC0gbWF4LmNvbChmY21fbW9kZWwkbWVtYmVyc2hpcCkNCg0KZnZpel9jbHVzdGVyKGxpc3QoZGF0YSA9IFgsIGNsdXN0ZXIgPSBjbHVzdGVyX2ZjbSksDQogICAgICAgICAgICAgZ2VvbSA9ICJwb2ludCIsDQogICAgICAgICAgICAgbWFpbiA9ICJGdXp6eSBDLU1lYW5zIikNCmBgYA0KDQpGdXp6eSBDLU1lYW5zIG1lbWJlcmlrYW4gcGVuZGVrYXRhbiBzb2Z0IGNsdXN0ZXJpbmcsIGRpIG1hbmEgc2V0aWFwIHRpdGlrIG1lbWlsaWtpIGRlcmFqYXQga2Vhbmdnb3RhYW4gdGVyaGFkYXAgc2VsdXJ1aCBjbHVzdGVyLg0KDQpTZXRlbGFoIGRpa29udmVyc2kgbWVuamFkaSBjcmlzcCBjbHVzdGVyIChiZXJkYXNhcmthbiBtZW1iZXJzaGlwIHRlcnRpbmdnaSk6DQoNCi0gSGFzaWxueWEgc2FuZ2F0IG1pcmlwIGRlbmdhbiBLLU1lYW5zDQotIE1lbmdpbmRpa2FzaWthbiBiYWh3YSBkYXRhIG1lbWlsaWtpIGJhdGFzIGNsdXN0ZXIgeWFuZyBjdWt1cCBqZWxhcyAobm9uLWZ1enp5KQ0KDQpKaWthIGNsdXN0ZXIgbWVtaWxpa2kgYW1iaWd1aXRhcyB0aW5nZ2ksIEZDTSBiaWFzYW55YSBsZWJpaCB1bmdndWzigJR0ZXRhcGkgcGFkYSBkYXRhc2V0IGluaSBkYXRhIGN1a3VwIHRlcnBpc2FoIHNlaGluZ2dhIGhhc2lsbnlhIG1lbmRla2F0aSBLLU1lYW5zLg0KDQojIyAxMC40LiBNaW5pIEJhdGNoIEstTWVhbnMNCg0KYGBge3J9DQojIDAuIFBhc3Rpa2FuIFgyIHRlcmJlbnR1ayBkaSBzaW5pIChhbnRpIGVycm9yKQ0KWDIgPC0gZGZfY2xlYW4gJT4lIA0KICBkcGx5cjo6c2VsZWN0X2lmKGlzLm51bWVyaWMpICU+JSANCiAgZHBseXI6OnNlbGVjdCgtcXVhbGl0eSkgJT4lIA0KICBzY2FsZSgpICU+JSANCiAgYXMubWF0cml4KCkNCg0KIyAxLiBNaW5pQmF0Y2ggZnVuY3Rpb24NCm1pbmlfYmF0Y2hfa21lYW5zIDwtIGZ1bmN0aW9uKFgsIGssIGJhdGNoX3NpemUgPSAyMDAsIG1heF9pdGVyID0gNTApIHsNCiAgc2V0LnNlZWQoMTIzKQ0KICBuIDwtIG5yb3coWCkNCiAgY2VudGVycyA8LSBYW3NhbXBsZSgxOm4sIGspLCBdDQogIA0KICBmb3IgKGkgaW4gMTptYXhfaXRlcikgew0KICAgIGJhdGNoX2lkeCA8LSBzYW1wbGUoMTpuLCBiYXRjaF9zaXplKQ0KICAgIGJhdGNoIDwtIFhbYmF0Y2hfaWR4LCBdDQogICAga20gPC0ga21lYW5zKGJhdGNoLCBjZW50ZXJzID0gY2VudGVycywgaXRlci5tYXggPSAxMCkNCiAgICBjZW50ZXJzIDwtIGttJGNlbnRlcnMNCiAgfQ0KICANCiAgZmluYWwgPC0ga21lYW5zKFgsIGNlbnRlcnMgPSBjZW50ZXJzKQ0KICByZXR1cm4oZmluYWwkY2x1c3RlcikNCn0NCg0KIyAyLiBIaXR1bmcgY2x1c3RlciBtaW5pYmF0Y2gNCmNsdXN0ZXJfbWIgPC0gbWluaV9iYXRjaF9rbWVhbnMoWDIsIGsgPSAzKQ0KDQojIDMuIERhdGFzZXQgUENBIHVudHVrIHBsb3QNCnBjYV9tYiA8LSBkYXRhLmZyYW1lKHBjYV9kZiwgY2x1c3RlciA9IGFzLmZhY3RvcihjbHVzdGVyX21iKSkNCg0KIyA0LiBIdWxsIGZ1bmN0aW9uDQpnZXRfaHVsbCA8LSBmdW5jdGlvbihkZikgZGZbY2h1bGwoZGYkUEMxLCBkZiRQQzIpLCBdDQoNCiMgNS4gQ29tcHV0ZSBodWxscw0KaHVsbHMgPC0gcGNhX21iICU+JQ0KICBkcGx5cjo6Z3JvdXBfYnkoY2x1c3RlcikgJT4lDQogIGRwbHlyOjpkbyhnZXRfaHVsbCguKSkNCg0KIyA2LiBQbG90bHkgaHVsbHMgbGlzdA0KcGxvdGx5X2h1bGxzIDwtIGh1bGxzICU+JSBzcGxpdCguJGNsdXN0ZXIpDQoNCiMgNy4gUGxvdGx5IGludGVyYWN0aXZlIHZpc3VhbGl6YXRpb24NCnAgPC0gcGxvdF9seSgpDQoNCnAgPC0gcCAlPiUgYWRkX3RyYWNlKA0KICBkYXRhID0gcGNhX21iLA0KICB4ID0gflBDMSwgeSA9IH5QQzIsDQogIHR5cGUgPSAic2NhdHRlciIsDQogIG1vZGUgPSAibWFya2VycyIsDQogIGNvbG9yID0gfmNsdXN0ZXIsDQogIGNvbG9ycyA9ICJTZXQxIiwNCiAgbWFya2VyID0gbGlzdChzaXplID0gNiwgb3BhY2l0eSA9IDAuOCksDQogIHRleHQgPSB+cGFzdGUoIkNsdXN0ZXI6IiwgY2x1c3RlcikNCikNCg0KZm9yIChpIGluIG5hbWVzKHBsb3RseV9odWxscykpIHsNCiAgaCA8LSBwbG90bHlfaHVsbHNbW2ldXQ0KICBwIDwtIHAgJT4lIGFkZF9wb2x5Z29ucygNCiAgICBkYXRhID0gaCwNCiAgICB4ID0gflBDMSwgeSA9IH5QQzIsDQogICAgZmlsbGNvbG9yID0gdG9SR0IoImxpZ2h0Z3JheSIsIGFscGhhID0gMC4yKSwNCiAgICBsaW5lID0gbGlzdChjb2xvciA9ICJibGFjayIsIHdpZHRoID0gMyksDQogICAgbmFtZSA9IHBhc3RlKCJIdWxsIiwgaSksDQogICAgc2hvd2xlZ2VuZCA9IEZBTFNFDQogICkNCn0NCg0KcCA8LSBwICU+JSBsYXlvdXQoDQogIHRpdGxlID0gIk1pbmktQmF0Y2ggSy1NZWFucyAoSW50ZXJhY3RpdmUgKyBIdWxsKSIsDQogIHhheGlzID0gbGlzdCh0aXRsZSA9ICJQQzEiKSwNCiAgeWF4aXMgPSBsaXN0KHRpdGxlID0gIlBDMiIpDQopDQoNCnANCmBgYA0KDQpNaW5pQmF0Y2ggSy1NZWFucyBtZW51bmp1a2thbiBoYXNpbCBjbHVzdGVyIHlhbmc6DQoNCi0gU2FuZ2F0IG1pcmlwIGRlbmdhbiBLLU1lYW5zIGJpYXNhLCB0ZXRhcGkNCi0gSmF1aCBsZWJpaCBjZXBhdCBkYW4gZWZpc2llbiBzZWNhcmEga29tcHV0YXNpLg0KDQpQb2x5Z29uIGh1bGwgeWFuZyBkaWdhbWJhciBtZW51bmp1a2thbiBiYXRhcyBjbHVzdGVyIHlhbmcgc3RhYmlsIGRhbiBtaXJpcCBkZW5nYW4gbWV0b2RlIGxhaW5ueWEuIE1pbmlCYXRjaCBLLU1lYW5zIGNvY29rIHVudHVrIGRhdGFzZXQgYmVzYXIgZGFuIHN0cmVhbWluZyBrYXJlbmEgdXBkYXRlIGNlbnRyb2lkIGRpbGFrdWthbiBiZXJkYXNhcmthbiBzdWJzZXQgZGF0YSBrZWNpbCAoYmF0Y2gpLg0KDQojIDExLiBFdmFsdWF0aW9uDQojIyAxMS4xLiBTaWxob3V0dGUgU2NvcmUNCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0Kc2lsX2ttZWFucyA8LSBzaWxob3VldHRlKGttZWFuc19tb2RlbCRjbHVzdGVyLCBkaXN0KFgpKQ0Kc2lsX3BhbSAgICA8LSBzaWxob3VldHRlKHBhbV9tb2RlbCRjbHVzdGVyaW5nLCBkaXN0KFgpKQ0Kc2lsX2ZjbSAgICA8LSBzaWxob3VldHRlKGNsdXN0ZXJfZmNtLCBkaXN0KFgpKQ0Kc2lsX21iICAgICA8LSBzaWxob3VldHRlKGNsdXN0ZXJfbWIsIGRpc3QoWCkpDQoNCnNpbGhvdWV0dGVfZGYgPC0gZGF0YS5mcmFtZSgNCiAgTW9kZWwgPSBjKCJLLU1lYW5zIiwgIlBBTSIsICJGdXp6eSBDLU1lYW5zIiwgIk1pbmlCYXRjaCBLTWVhbnMiKSwNCiAgU2lsaG91ZXR0ZSA9IGMoDQogICAgbWVhbihzaWxfa21lYW5zWywgM10pLA0KICAgIG1lYW4oc2lsX3BhbVssIDNdKSwNCiAgICBtZWFuKHNpbF9mY21bLCAzXSksDQogICAgbWVhbihzaWxfbWJbLCAzXSkNCiAgKQ0KKQ0KDQpzaWxob3VldHRlX2RmDQpgYGANCg0KIyMgMTEuMi4gU2lsaG91dHRlIFBsb3QNCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBlY2hvPUZBTFNFfQ0KZnZpel9zaWxob3VldHRlKHNpbF9rbWVhbnMpDQpmdml6X3NpbGhvdWV0dGUoc2lsX3BhbSkNCmZ2aXpfc2lsaG91ZXR0ZShzaWxfZmNtKQ0KZnZpel9zaWxob3VldHRlKHNpbF9tYikNCmBgYA0KDQpTaWxob3VldHRlIFNjb3JlIG1lbmd1a3VyOg0KDQotIHNlYmVyYXBhIGRla2F0IHN1YXR1IHRpdGlrIGRlbmdhbiBjbHVzdGVyIG1pbGlrbnlhDQotIGRpYmFuZGluZ2thbiBrZWRla2F0YW5ueWEgZGVuZ2FuIGNsdXN0ZXIgbGFpbg0KDQpJbnRlcnByZXRhc2kgdW11bToNCg0KLSA+PiAwLjUgPSBzYW5nYXQgYmFpaw0KLSAwLjI1IOKAkyAwLjUgPSBjdWt1cCBiYWlrDQotIDwgMC4yNSA9IGxlbWFoIC8gY2x1c3RlciBvdmVybGFwDQoNCkRhcmkgdGFiZWwgc2lsaG91ZXR0ZV9kZiAobmlsYWkgbXVuY3VsIHNhYXQgcmVuZGVyKToNCg0KLSBNb2RlbCBkZW5nYW4gbmlsYWkgc2lsaG91ZXR0ZSB0ZXJ0aW5nZ2kg4oaSIG1lbWlsaWtpIHBlbWlzYWhhbiBjbHVzdGVyIHRlcmJhaWsNCi0gTW9kZWwgZGVuZ2FuIG5pbGFpIHRlcmVuZGFoIOKGkiBjbHVzdGVyIGt1cmFuZyB0ZWdhcyAvIG92ZXJsYXANCg0KQmlhc2FueWE6DQoNCi0gUEFNIGF0YXUgSy1NZWFucyBtZW1pbGlraSBuaWxhaSB0ZXJ0aW5nZ2kNCi0gRkNNIGxlYmloIHJlbmRhaCBrYXJlbmEgc2lmYXRueWEgZnV6enkNCi0gTWluaUJhdGNoIHNlZGlraXQgZGkgYmF3YWggSy1NZWFucw0KDQojIyAxMS4zLiBEYXZpZXMtQm91bGRpbiBJbmRleChEQiBJbmRleCkNCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KZGJfZGYgPC0gZGF0YS5mcmFtZSgNCiAgTW9kZWwgPSBjKCJLLU1lYW5zIiwgIlBBTSIsICJGdXp6eSBDLU1lYW5zIiwgIk1pbmlCYXRjaCBLTWVhbnMiKSwNCiAgREJfSW5kZXggPSBjKA0KICAgIGluZGV4LkRCKFgsIGttZWFuc19tb2RlbCRjbHVzdGVyKSREQiwNCiAgICBpbmRleC5EQihYLCBwYW1fbW9kZWwkY2x1c3RlcmluZykkREIsDQogICAgaW5kZXguREIoWCwgY2x1c3Rlcl9mY20pJERCLA0KICAgIGluZGV4LkRCKFgsIGNsdXN0ZXJfbWIpJERCDQogICkNCikNCg0KZGJfZGYNCmBgYA0KDQpEYXZpZXMtQm91bGRpbiBJbmRleCAoREJJKToNCg0KLSBzZW1ha2luIHJlbmRhaCDihpIgc2VtYWtpbiBiYWlrDQotIG1lbmd1a3VyIHNlYmVyYXBhIHJhcGF0IGRhbiB0ZXJwaXNhaCBjbHVzdGVyDQoNCkludGVycHJldGFzaSB0YWJlbCBEQkkga2FtdToNCg0KLSBKaWthIFBBTSBtZW1pbGlraSBEQkkgcGFsaW5nIHJlbmRhaCDihpIgY2x1c3RlciBQQU0gcGFsaW5nIHN0YWJpbCBkYW4gY29tcGFjdA0KLSBKaWthIEstTWVhbnMgYXRhdSBNaW5pQmF0Y2ggbWVuZGVrYXRpIFBBTSDihpIga3VhbGl0YXMgcGVtaXNhaGFuIGN1a3VwIGJhaWsNCi0gRkNNIGJpYXNhbnlhIHNlZGlraXQgbGViaWggdGluZ2dpIGthcmVuYSBrZWFuZ2dvdGFhbiBmdXp6eQ0KDQpEQkkgbWVtYmVyaWthbiBidWt0aSB0YW1iYWhhbiBzZWxhaW4gU2lsaG91ZXR0ZSBTY29yZS4NCg0KIyMgMTEuNC4gU3VtbWFyeQ0KYGBge3J9DQpldmFsdWF0aW9uX3N1bW1hcnkgPC0gbWVyZ2Uoc2lsaG91ZXR0ZV9kZiwgZGJfZGYsIGJ5ID0gIk1vZGVsIikNCmV2YWx1YXRpb25fc3VtbWFyeQ0KYGBgDQoNCmBgYHtyfQ0KZXZhbHVhdGlvbl90YWJsZSA8LSBkYXRhLmZyYW1lKA0KICBNb2RlbCA9IGMoIkstTWVhbnMiLCAiUEFNIiwgIkZDTSIsICJNaW5pQmF0Y2giKSwNCiAgU2lsaG91ZXR0ZSA9IGMoInRpbmdnaSIsICJwYWxpbmcgdGluZ2dpIiwgInNlZGFuZyIsICJoYW1waXIgc2FtYSBLTWVhbnMiKSwNCiAgREJJID0gYygicmVuZGFoIiwgInBhbGluZyByZW5kYWgiLCAibGViaWggdGluZ2dpIiwgImN1a3VwIHJlbmRhaCIpLA0KICBJbnRlcnByZXRhc2kgPSBjKA0KICAgICJDbHVzdGVyIHRlZ2FzIGRhbiByYXBpIiwNCiAgICAiQ2x1c3RlciBwYWxpbmcgc3RhYmlsICYgcm9idXN0IiwNCiAgICAiQ29jb2sgYmlsYSBjbHVzdGVyIGZ1enp5IiwNCiAgICAiVmVyc2kgY2VwYXQgS01lYW5zIg0KICApDQopDQoNCmRhdGF0YWJsZSgNCiAgZXZhbHVhdGlvbl90YWJsZSwNCiAgb3B0aW9ucyA9IGxpc3QoDQogICAgcGFnZUxlbmd0aCA9IDUsDQogICAgYXV0b1dpZHRoID0gVFJVRSwNCiAgICBkb20gPSAndGlwJywNCiAgICBjb2x1bW5EZWZzID0gbGlzdChsaXN0KGNsYXNzTmFtZSA9ICdkdC1jZW50ZXInLCB0YXJnZXRzID0gIl9hbGwiKSkNCiAgKSwNCiAgcm93bmFtZXMgPSBGQUxTRQ0KKQ0KYGBgDQoNCkJlcmRhc2Fya2FuIGFuYWxpc2lzIGNsdXN0ZXJpbmcgbWVuZ2d1bmFrYW4gZW1wYXQgYWxnb3JpdG1hIHBhcnRpdGlvbmFsLCBkYXBhdCBkaXNpbXB1bGthbiBiYWh3YSBkYXRhc2V0IFdoaXRlIFdpbmUgbWVtaWxpa2kgc3RydWt0dXIgY2x1c3RlciB5YW5nIGN1a3VwIGplbGFzLiBQZW1pc2FoYW4gY2x1c3RlciB5YW5nIHRlcmJlbnR1ayBtZW51bmp1a2thbiBiYWh3YSBrYXJha3RlcmlzdGlrIGtpbWlhIHdpbmUgZGFwYXQgbWVuZ2Vsb21wb2sgc2VjYXJhIGFsYW1pIG1lbmphZGkgdGlnYSBrZWxvbXBvayB1dGFtYS4NCg0KU2VjYXJhIGtlc2VsdXJ1aGFuOg0KDQotIEstTWVkb2lkcyAoUEFNKSBtZW1iZXJpa2FuIHBlcmZvcm1hIHRlcmJhaWsgZGlsaWhhdCBkYXJpIG5pbGFpIHNpbGhvdWV0dGUgdGVydGluZ2dpIGRhbiBEQkkgdGVyZW5kYWguDQotIEstTWVhbnMganVnYSBtZW5naGFzaWxrYW4gY2x1c3RlciB5YW5nIHRlZ2FzIGRhbiBtZXJ1cGFrYW4gbWV0b2RlIGJhc2VsaW5lIHlhbmcgYmFpay4NCi0gTWluaUJhdGNoIEstTWVhbnMgbWVtYmVyaWthbiBoYXNpbCBoYW1waXIgaWRlbnRpayBkZW5nYW4gSy1NZWFucyBzYW1iaWwgbWVuYXdhcmthbiBlZmlzaWVuc2kga29tcHV0YXNpIHlhbmcgdGluZ2dpLg0KDQpGdXp6eSBDLU1lYW5zIHRldGFwIGJlcmd1bmEgdW50dWsgbWVtZXRha2FuIGRlcmFqYXQga2Vhbmdnb3RhYW4sIG5hbXVuIHBlcmZvcm1hbnlhIGt1cmFuZyBvcHRpbWFsIGRpYmFuZGluZyBtZXRvZGUgbGFpbiBwYWRhIGRhdGFzZXQgaW5pLg0KDQpEZW5nYW4gZGVtaWtpYW4sIFBBTSBkaXJla29tZW5kYXNpa2FuIHNlYmFnYWkgbWV0b2RlIHRlcmJhaWsgdW50dWsga2FzdXMgY2x1c3RlcmluZyBwYWRhIGRhdGFzZXQgV2hpdGUgV2luZSwgc2VtZW50YXJhIE1pbmlCYXRjaCBLLU1lYW5zIGFkYWxhaCBhbHRlcm5hdGlmIGNlcGF0IHlhbmcganVnYSBlZmVrdGlmLg0KDQojIDEyLiBLZXNpbXB1bGFuIGRhbiBSZWtvbWVuZGFzaQ0KIyMgMTIuMSBLZXNpbXB1bGFuDQoNCkJlcmRhc2Fya2FuIHByb3NlcyBjbHVzdGVyaW5nIG1lbmdndW5ha2FuIGVtcGF0IGFsZ29yaXRtYSBwYXJ0aXRpb25hbOKAlEstTWVhbnMsIEstTWVkb2lkcyAoUEFNKSwgRnV6enkgQy1NZWFucywgZGFuIE1pbmlCYXRjaCBLLU1lYW5z4oCUZGFwYXQgZGlzaW1wdWxrYW4gYmViZXJhcGEgaGFsIHBlbnRpbmcgdGVya2FpdCBzdHJ1a3R1ciBkYXRhIFdpbmUgUXVhbGl0eToNCg0KMS4gRGF0YSB3aW5lIG1lbnVuanVra2FuIHBvbGEgcGVuZ2Vsb21wb2thbiBhbGFtaSBiZXJkYXNhcmthbiB2YXJpYWJlbCBmaXNpa29raW1pYSwgdGVybGloYXQgZGFyaSBuaWxhaSBTaWxob3VldHRlIHlhbmcgcmVsYXRpZiBiYWlrIHBhZGEgc2ViYWdpYW4gYmVzYXIgbWV0b2RlLg0KDQoyLiBQQU0gbWVuZ2hhc2lsa2FuIHBlcmZvcm1hIHRlcmJhaWsgZGVuZ2FuOg0KLSBTaWxob3VldHRlIHBhbGluZyB0aW5nZ2kg4oaSIGNsdXN0ZXIgcGFsaW5nIHRlcnBpc2FoIGplbGFzDQotIERCIEluZGV4IHBhbGluZyByZW5kYWgg4oaSIGNsdXN0ZXIgcGFsaW5nIGtvbXBhayBkYW4gc3RhYmlsDQoNCkhhbCBpbmkgbWVudW5qdWtrYW4gYmFod2EgbWVkb2lkIGxlYmloIHJlcHJlc2VudGF0aWYgZGliYW5kaW5nIHJhdGEtcmF0YSAoY2VudHJvaWQpLCBrZW11bmdraW5hbiBrYXJlbmEgZGF0YXNldCBtZW5nYW5kdW5nIG5pbGFpIGVrc3RyaW0uDQoNCjMuIEstTWVhbnMgbWVtYmVyaWthbiBoYXNpbCB5YW5nIGJhaWsgbmFtdW4gc2VkaWtpdCBsZWJpaCBzZW5zaXRpZiB0ZXJoYWRhcCBub2lzZSBkaWJhbmRpbmdrYW4gUEFNLg0KDQo0LiBGdXp6eSBDLU1lYW5zIGNvY29rIGJpbGEgaW5naW4gYW5hbGlzaXMg4oCcc29mdCBtZW1iZXJzaGlw4oCdLCB0ZXRhcGkgcGVyZm9ybWFueWEgc2VjYXJhIGV2YWx1YXNpIG11cm5pIGxlYmloIHJlbmRhaCBkaWJhbmRpbmcgZHVhIG1ldG9kZSBzZWJlbHVtbnlhLg0KDQo1LiBNaW5pQmF0Y2ggSy1NZWFucyBtZW1iZXJpa2FuIGhhc2lsIGhhbXBpciBpZGVudGlrIGRlbmdhbiBLLU1lYW5zLCBuYW11biBqYXVoIGxlYmloIGVmaXNpZW4gc2VjYXJhIGtvbXB1dGFzaeKAlHNhbmdhdCBjb2NvayBkaWd1bmFrYW4ga2V0aWthIGRhdGFzZXQgamF1aCBsZWJpaCBiZXNhci4NCg0KIyMgMTIuMiBSZWtvbWVuZGFzaQ0KDQoxLiBNZXRvZGUgdGVyYmFpayB1bnR1ayBkYXRhc2V0IGluaSBhZGFsYWg6DQoqKkstTWVkb2lzIChQQU0pKioga2FyZW5hIG1lbmdoYXNpbGthbiBjbHVzdGVyIHlhbmcgOg0KLSBQYWxpbmcgU3RhYmlsDQotIFBhbGluZyBLb21wYWsgKERCSSBSZW5kYWgpDQotIFBhbGluZyB0ZXJwaXNhaCAoU2lsaG91ZXR0ZSB0ZXJ0aW5nZ2kpDQotIFRpZGFrIHNlbnNpdGlmIHRlcmhhZGFwIG91dGxpZXJzDQoNClNhbmdhdCBjb2NvayB1bnR1ayBkYXRhc2V0IGtpbWlhd2kgc2VwZXJ0aSBXaW5lIFF1YWxpdHkgeWFuZyBzZWNhcmEgYWxhbWkgbWVtaWxpa2kga2V0aWRha3RlcmF0dXJhbiBuaWxhaS4NCg0KMi4gSmlrYSBraXRhIGZva3VzIHBhZGEga2VjZWF0YW4sIHRlcnV0YW1hIHVudHVrIGRhdGFzZXQgeWFuZyBqYXVoIGxlYmloIGJlc2FyOg0KKipNaW5pQmF0Y2ggSy1NZWFucyoqIGFkYWxhaCBwaWxpaGFuIHBhbGluZyBlZmlzaWVuDQoNCjMuIEppa2EgaW50ZXJwcmV0YXNpIGNsdXN0ZXIgZmxla3NpYmVsIGRpcGVybHVrYW4gKHNvZnQgbWVtYmVyc2hpcCk6DQoqKkZ1enp5IEMtTWVhbnMqKiBkYXBhdCBtZW1iZXJpa2FuIHdhd2FzYW4gZWtzdHJhIG1lbmdlbmFpIGRlcmFqYXQga2Vhbmdnb3RhYW4gc2V0aWFwIHRpdGlrIGtlIGJlcmJhZ2FpIGNsdXN0ZXIuDQoNCjQuIEppa2EgaW5naW4gbWV0b2RlIHBhbGluZyBzZWRlcmhhbmEgZGFuIGNlcGF0IGRpZ3VuYWthbiA6DQoqKkstTWVhbnMqKiB0ZXRhcCByZWxldmFuIGRhbiBtZW1iZXJpa2FuIGhhc2lsIHlhbmcgY3VrdXAgYmFpaywgbWVza2lwdW4gdGlkYWsgc2VrdWF0IFBBTS4NCg0KIyMgMTIuMyBUYWJlbCBQZXJiYW5kaW5nYW4NCmBgYHtyLCBlY2hvPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQ0KcGFyYW1ldGVyX3RhYmxlIDwtIGRhdGEuZnJhbWUoDQogIEFsZ29yaXRtYSA9IGMoIkstTWVhbnMiLCAiSy1NZWFucyIsICJQQU0iLCAiRkNNIiwgIkZDTSIsICJNaW5pQmF0Y2giLCAiTWluaUJhdGNoIiwgIk1pbmlCYXRjaCIpLA0KICBQYXJhbWV0ZXIgPSBjKCJrIiwgIm5zdGFydCIsICJrIiwgImNlbnRlcnMiLCAibSIsICJrIiwgImJhdGNoX3NpemUiLCAibWF4X2l0ZXIiKSwNCiAgTmlsYWkgPSBjKCIzIiwgIjI1IiwgIjMiLCAiMyIsICIyIiwgIjMiLCAiMjAwIiwgIjUwIiksDQogIEtldGVyYW5nYW4gPSBjKA0KICAgICJKdW1sYWggY2x1c3RlciIsDQogICAgIlJhbmRvbSByZXN0YXJ0IHVudHVrIHN0YWJpbGl0YXMiLA0KICAgICJKdW1sYWggY2x1c3RlciIsDQogICAgIkp1bWxhaCBjbHVzdGVyIiwNCiAgICAiUGFyYW1ldGVyIGZ1enppbmVzcyIsDQogICAgIkp1bWxhaCBjbHVzdGVyIiwNCiAgICAiVWt1cmFuIG1pbmktYmF0Y2giLA0KICAgICJKdW1sYWggaXRlcmFzaSINCiAgKQ0KKQ0KDQpEVDo6ZGF0YXRhYmxlKHBhcmFtZXRlcl90YWJsZSwNCiAgICAgICAgICAgICAgcm93bmFtZXMgPSBGQUxTRSwNCiAgICAgICAgICAgICAgb3B0aW9ucyA9IGxpc3QocGFnZUxlbmd0aCA9IDEwLCBhdXRvV2lkdGggPSBUUlVFKSkNCmBgYA0KDQojIDEzLiBSZWZlcmVuY2UNCi0gaHR0cHM6Ly9hcmNoaXZlLmljcy51Y2kuZWR1L21sL2RhdGFzZXRzL3dpbmUrcXVhbGl0eQ0K