Analisis Prediksi

Partitional

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

  1. pilih k medoid secara acak.
  2. Assign setiap data ke metoid terdekat.
  3. Tukar medoid dengan titik lain jika menghasilkan pengurangan cost.
  4. 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

  1. Inisialisasi membership matrix \(U\).
  2. Hitung centroid fuzzy menggunakan membership.
  3. Perbarui membership berdasarkan jarak ke centroid.
  4. 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

  1. Inisialisasi centroid acak.
  2. Pilih subset data berukuran kecil (mini-batch).
  3. Hitung cluster untuk batch tersebut.
  4. Perbarui centroid menggunakan batch.
  5. Ulangi hingga iterasi selesai.
  6. 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

skimr::skim(df)
Data summary
Name df
Number of rows 4898
Number of columns 12
_______________________
Column type frequency:
numeric 12
________________________
Group variables None

Variable type: numeric

skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
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 ▁▅▇▃▁
summary(df)
##  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:

  1. Data wine menunjukkan pola pengelompokan alami berdasarkan variabel fisikokimia, terlihat dari nilai Silhouette yang relatif baik pada sebagian besar metode.

  2. 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.

  1. K-Means memberikan hasil yang baik namun sedikit lebih sensitif terhadap noise dibandingkan PAM.

  2. Fuzzy C-Means cocok bila ingin analisis “soft membership”, tetapi performanya secara evaluasi murni lebih rendah dibanding dua metode sebelumnya.

  3. 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

  1. 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.

  1. Jika kita fokus pada keceatan, terutama untuk dataset yang jauh lebih besar: MiniBatch K-Means adalah pilihan paling efisien

  2. Jika interpretasi cluster fleksibel diperlukan (soft membership): Fuzzy C-Means dapat memberikan wawasan ekstra mengenai derajat keanggotaan setiap titik ke berbagai cluster.

  3. Jika ingin metode paling sederhana dan cepat digunakan : K-Means tetap relevan dan memberikan hasil yang cukup baik, meskipun tidak sekuat PAM.

12.3 Tabel Perbandingan

LS0tDQp0aXRsZTogIkFuYWxpc2lzIFByZWRpa3NpIg0Kc3VidGl0bGU6ICJQYXJ0aXRpb25hbCINCmF1dGhvcjogDQogIC0gIlphaW4gSXFiYWwgU2FwdXRyYSINCiAgLSAiNTIyNDAwMjQiDQogIC0gIlNhaW5zIERhdGEiDQogIC0gIkJha3RpIFNpcmVnYXIiDQogIC0gIkluc3RpdHV0IFRla25vbG9naSBTYWlucyBCYW5kdW5nIg0KZGF0ZTogICJgciBmb3JtYXQoU3lzLkRhdGUoKSwgJyVCICVkLCAlWScpYCINCm91dHB1dDoNCiAgcm1kZm9ybWF0czo6cmVhZHRoZWRvd246ICAgDQogICAgc2VsZl9jb250YWluZWQ6IHRydWUNCiAgICB0aHVtYm5haWxzOiB0cnVlDQogICAgbGlnaHRib3g6IHRydWUNCiAgICBnYWxsZXJ5OiB0cnVlDQogICAgbnVtYmVyX3NlY3Rpb25zOiBmYWxzZQ0KICAgIGxpYl9kaXI6IGxpYnMNCiAgICAzZGZfcHJpbnQ6ICJwYWdlZCINCiAgICBjb2RlX2ZvbGRpbmc6ICJzaG93Ig0KICAgIGNvZGVfZG93bmxvYWQ6IHllcw0KICAgIGNzczogInN0eWxlLmNzcyINCi0tLQ0KDQo8aW1nIHNyYz0iRm90by5qcGciIHN0eWxlPSJkaXNwbGF5OiBibG9jazsgd2lkdGg6MzAwcHg7IG1hcmdpbjogYXV0bzsiPg0KDQpgYGB7ciwgZWNobz1GQUxTRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0NCmxpYnJhcnkodGlkeXZlcnNlKSAgDQpsaWJyYXJ5KHNraW1yKSAgICAgICANCmxpYnJhcnkoY2x1c3RlcikgICAgIA0KbGlicmFyeShmYWN0b2V4dHJhKSAgDQpsaWJyYXJ5KGUxMDcxKSAgICAgICANCmxpYnJhcnkoQ2x1c3RlclIpDQpsaWJyYXJ5KGNsdXN0ZXJTaW0pDQpsaWJyYXJ5KHBsb3RseSkNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KERUKQ0KYGBgDQoNCiMgMS4gRGVmaW5pc2kgU2luZ2thdA0KDQpDbHVzdGVyaW5nIG1lcnVwYWthbiB0ZWtuaWsgdW5zdXBlcnZpc2VkIGxlYXJuaW5nIHlhbmcgYmVydHVqdWFuIG1lbmdlbG9tcG9ra2FuIG9iamVrLW9iamVrIHlhbmcgbWVtaWxpa2kga2VtaXJpcGFuIHNhdHUgc2FtYSBsYWluLiBQZW5kZWthdGFuIGluaSBkaWd1bmFrYW4gc2VjYXJhIGx1YXMgZGFsYW0gYW5hbGlzaXMgZWtzcGxvcmF0b3JpLCBzZWdtZW50YXNpIHBhc2FyLCBhbmFsaXNpcyBwZXJpbGFrdSBrb25zdW1lbiwgcGVtZXRhYW4ga3VhbGl0YXMgcHJvZHVrLCBkYW4gYmVyYmFnYWkgZG9tYWluIGxhaW5ueWEuDQoNClBhZGEgbGFwb3JhbiBpbmksIHBlbnVsaXMgbWVuZ2d1bmFrYW4gcGVuZGVrYXRhbiBQYXJ0aXRpb25hbCBDbHVzdGVyaW5nLCB5YW5nIHRlcmRpcmkgZGFyaSBlbXBhdCBhbGdvcml0bWEgdXRhbWE6IEstTWVhbnMsIEstTWVkb2lkcyAoUEFNKSwgRnV6enkgQy1NZWFucywgZGFuIE1pbmlCYXRjaCBLLU1lYW5zLiBLZWVtcGF0IG1ldG9kZSB0ZXJzZWJ1dCBkaXBpbGloIGthcmVuYSBrZW1hbXB1YW4gbWVyZWthIG1lbWJhZ2kgZGF0YSBiZXNhciBtZW5qYWRpIGtlbG9tcG9rLWtlbG9tcG9rIGhvbW9nZW4gZGFsYW0gYmVudHVrIHBhcnRpc2kuDQoNCkRhdGFzZXQgeWFuZyBkaWd1bmFrYW4gYWRhbGFoIFdpbmUgUXVhbGl0eSBEYXRhc2V0IChXaGl0ZSBXaW5lKSBkYXJpIFVDSSBNYWNoaW5lIExlYXJuaW5nIFJlcG9zaXRvcnksIHlhbmcgYmVyaXNpIGF0cmlidXQgZmlzaWtva2ltaWEgd2luZSBkYW4gc2tvciBrdWFsaXRhc255YS4gQ2x1c3RlcmluZyBkaWxha3VrYW4gdW50dWsgbWVsaWhhdCBhcGFrYWggZGF0YSBkYXBhdCBtZW1iZW50dWsgc3RydWt0dXIga2Vsb21wb2sgYWxhbWkgYmVyZGFzYXJrYW4gdmFyaWFiZWwgbnVtZXJpaywgZGFuIHVudHVrIG1lbWFoYW1pIGthcmFrdGVyaXN0aWsgdGlhcCBrZWxvbXBvayB0ZXJzZWJ1dCB0YW5wYSBtZW5nZ3VuYWthbiB2YXJpYWJlbCB0YXJnZXQgKHF1YWxpdHkpIHNlYmFnYWkgZGFzYXIgcGVtaXNhaGFuLg0KDQojIDIuIEstTWVhbnMNCiMjIDIuMSBSdW11cyBJbnRpDQpUdWp1YW4gSy1NZWFucyBhZGFsYWggbWVtaW5pbWFsa2FuIHRvdGFsIGphcmFrIGt1YWRyYXQgYW50YXJhIHRpdGlrIGRhdGEgZGFuIGNlbnRyb2lkIGNsdXN0ZXIuDQoNCiQkDQpKID0gXHN1bV97aT0xfV57a30gXHN1bV97eCBcaW4gQ19pfSBcfCB4IC0gXG11X2kgXHxeMg0KJCQNCg0KZGVuZ2FuOg0KDQotICRrPSQgSnVtbGFoIENsdXN0ZXINCi0gJENpPSQgaGltcHVuYW4gcGFkYSBjbHVzdGVyIGtlIGkNCi0gJFxtdV9pPSQgY2VudHJvaWQgY2x1c3RlciBrZSBpDQoNCiMjIDIuMiBDYXJhIEtlcmphDQotIFRlbnR1a2FuIGp1bWxhaCBjbHVzdGVyICRrJC4NCi0gaW5pc2lhbGlzYXNpIGNsdXN0ZXIgc2VjYXJhIGFjYWsuDQotIEFzc2lnbm1lbnQgc3RlcCA6IHNldGlhcCB0aXRpayBkaW1hc3Vra2FuIGtlIGRhbGFtIGNlbnRyb2lkIHRlcmRla2F0Lg0KLSBVcGRhdGUgc3RlcCA6IGhpdHVuZyBjZW50cm9pZCBiYXJ1IGRlbmdhbiByYXRhIHJhdGEgdGl0aWsgZGFsYW0gY2x1c3Rlci4NCi0gVWxhbmdpIGxhbmdrYWggMy00IGhpbmdnYSBjZW50cm9pZCBzdGFiaWwuDQoNCiMjIDIuMyBLZWxlYmloYW4NCi0gQ2VwYXQgZGFuIGVmaXNpZW4gdW50dWsgZGF0YXNldCBiZXNhci4NCi0gTXVkYWggZGlpbXBsZW1lbnRhc2lrYW4NCi0gSGFzaWwgY2x1c3RlciBtdWRhaCBkaXRhZnNpcmthbg0KDQojIyAyLjQgS2V0ZXJiYXRhc2FuDQotIEhhcnVzIG1lbmVudHVrYW4gJGskIGRpIGF3YWwuDQotIFNlbnNpdGlmIHRlcmhhZGFwIG91dGxpZXJzLg0KLSBDZW5kZXJ1bmcgbWVuZW11a2FuIGNsdXN0ZXIgYmVyYmVudHVrIGJ1bGF0Lg0KDQojIDMuIEstTWVkb2lzIChQQU0pDQojIyAzLjEgUnVtdXMgSW50aQ0KTWlyaXAgSy1NZWFucywgdGV0YXBpIHB1c2F0IGNsdXN0ZXIgYWRhbGFoIG1lZG9pZCwgeWFpdHUgdGl0aWsgcGFsaW5nIHJlcHJlc2VudGF0aWY6DQoNCiQkDQpKID0gXHN1bV97aT0xfV57a30gXHN1bV97eCBcaW4gQ19pfSBkKHhfaSwgbV9pKQ0KJCQNCmRpbWFuYToNCg0KLSAkbV9pPSQgTWVsb2lkIGNsdXN0ZXIga2UgaQ0KLSAkZCgqKT0kIEphcmFrIEV1Y2xpZGVhbiBkYW4gTWFuaGF0dGFuDQoNCiMjIDMuMiBDYXJhIEtlcmphDQoxLiBwaWxpaCBrIG1lZG9pZCBzZWNhcmEgYWNhay4NCjIuIEFzc2lnbiBzZXRpYXAgZGF0YSBrZSBtZXRvaWQgdGVyZGVrYXQuDQozLiBUdWthciBtZWRvaWQgZGVuZ2FuIHRpdGlrIGxhaW4gamlrYSBtZW5naGFzaWxrYW4gcGVuZ3VyYW5nYW4gY29zdC4NCjQuIFVsYW5naSBoaW5nZ2EgdGlkYWsgYWRhIHBlcmJhaWthbi4NCg0KIyMgMy4zIEtlbGViaWhhbg0KLSBTYW5nYXQgcm9idXN0IHRlcmhhZGFwIG91dGxpZXJzLg0KLSBUaWRhayBtdWRhaCBiaWFzIHRlcmhhZGFwIG5pbGFpIGVrc3RyZW0uDQoNCiMjIDMuNCBLZXRlcmJhdGFzYW4NCi0gTGViaWggbGFtYmF0IGRhcmlwYWRhIEstTWVhbnMuDQotIFRpZGFrIGNvY29rIHVudHVrIGRhdGFzZXQgc2FuZ2F0IGJlc2FyIHRhbnBhIG9wdGltaXNhc2kuDQoNCiMgNC4gRnV6enkgQy1NZWFucw0KIyMgNC4xIFJ1bXVzIEludGkNClNldGlhcCB0aXRpayBtZW1pbGlraSBkZXJhamF0IGtlYW5nZ290YWFuICQoVV97aWp9KSQgdGVyaGFkYXAgdGlhcCBjbHVzdGVyLg0KDQokJA0KSiA9IFxzdW1fe2k9MX1ee2t9IFxzdW1fe2o9MX1ee259IHVfe2lqfV5tIFwsIFx8IHhfaiAtIGNfaSBcfF4yDQokJA0KZGltYW5hOg0KDQotICRtPSQgcGFyYW1hdGVyIGZ1enppbmVzICh1bXVtbnlhIDEuNSAtIDIuNSkNCi0gJHVfe2lqfT0kIG1lbWJlcnNzaGlwIHBvaW50IGtlIGogdGVyaGFkYXAgY2x1c3RlciBpDQotICRjX2k9JCBjZW50cm9pZCBmdXp6eSBjbHVzdGVyDQoNCiMjIDQuMiBDYXJhIEtlcmphDQoNCjEuIEluaXNpYWxpc2FzaSBtZW1iZXJzaGlwIG1hdHJpeCAkVSQuDQoyLiBIaXR1bmcgY2VudHJvaWQgZnV6enkgbWVuZ2d1bmFrYW4gbWVtYmVyc2hpcC4NCjMuIFBlcmJhcnVpIG1lbWJlcnNoaXAgYmVyZGFzYXJrYW4gamFyYWsga2UgY2VudHJvaWQuDQo0LiBVbGFuZ2kgaGluZ2dhIHBlcnViYWhhbiBtZW1iZXJzaGlwIHNhbmdhdCBrZWNpbC4NCg0KIyMgNC4zIEtlbGViaWhhbg0KDQotIE1lbWJlcmlrYW4gaW5mb3JtYXNpIGxlYmloIGRldGFpbCAoc29mdCBjbHVzdGVyaW5nKS4NCi0gQ29jb2sgdW50dWsgZGF0YXNldCBkZW5nYW4gYmF0YXMgY2x1c3RlciB0aWRhayB0ZWdhcy4NCg0KIyMgNC40IEtldGVyYmF0YXNhbg0KDQotIExlYmloIGxhbWJhdCBkYXJpIEstTWVhbnMuDQotIFBlbWlsaWhhbiBwYXJhbWV0ZXIgJG0kIHNhbmdhdCBtZW1wZW5nYXJ1aGkgaGFzaWwuDQoNCiMgNS4gTWluaUJhdGNoIEstTWVhbnMNCiMjIDUuMSBSdW11cyBJbnRpDQoNClR1anVhbiBzYW1hIHNlcGVydGkgSy1NZWFucywgdGV0YXBpIGNlbnRyb2lkIGRpcGVyYmFydWkgbWVuZ2d1bmFrYW4gbWluaS1iYXRjaCwgYnVrYW4gc2VsdXJ1aCBkYXRhLg0KDQokJA0KXG11X2leeyh0KzEpfSA9IFxtdV9pXnsodCl9ICsgXGV0YSBcbGVmdCggeF9qIC0gXG11X2leeyh0KX0gXHJpZ2h0KQ0KJCQNCkRlbmdhbjoNCg0KLSBiYXRjaCBrZWNpbCBiZXJ1a3VyYW4gMjAgLSA1MDANCi0gJFxldGE9JCBsZWFybmluZyByYXRlIHNlZGVyaGFuYQ0KDQojIyA1LjIgQ2FyYSBLZXJqYQ0KDQoxLiBJbmlzaWFsaXNhc2kgY2VudHJvaWQgYWNhay4NCjIuIFBpbGloIHN1YnNldCBkYXRhIGJlcnVrdXJhbiBrZWNpbCAobWluaS1iYXRjaCkuDQozLiBIaXR1bmcgY2x1c3RlciB1bnR1ayBiYXRjaCB0ZXJzZWJ1dC4NCjQuIFBlcmJhcnVpIGNlbnRyb2lkIG1lbmdndW5ha2FuIGJhdGNoLg0KNS4gVWxhbmdpIGhpbmdnYSBpdGVyYXNpIHNlbGVzYWkuDQo2LiBKYWxhbmthbiBLLU1lYW5zIGZpbmFsIGRlbmdhbiBjZW50cm9pZCBoYXNpbCBtaW5pLWJhdGNoLg0KDQojIyA1LjMgS2VsZWJpaGFuDQotIFNhbmdhdCBjZXBhdCB1bnR1ayBkYXRhc2V0IGJlc2FyLg0KLSBNZW1vcmkgbGViaWggcmluZ2FuLg0KLSBIYXNpbCBtZW5kZWthdGkgSy1NZWFucyBwZW51aC4NCg0KIyMgNS40IEtldGVyYmF0YXNhbg0KDQotIExlYmloIOKAnGFwcHJveGltYXRl4oCdIGRhcmlwYWRhIEstTWVhbnMuDQotIEJpc2Egc2VkaWtpdCBiZXJiZWRhIHRpYXAgcnVuLg0KDQojIDYuIERhdGEgDQojIyA2LjEgU3VtYmVyIERhdGFzZXQNCg0KRGF0YXNldCBCZXJhc2FsIGRhcmkgOiANCg0KVUNJIE1hY2hpbmUgTGVhcm5pbmcgUmVwb3NpdG9yeQ0KV2hpdGUgV2luZSBRdWFsaXR5DQpMaW5rIDogaHR0cHM6Ly9hcmNoaXZlLmljcy51Y2kuZWR1L21sL2RhdGFzZXRzL3dpbmUrcXVhbGl0eQ0KDQpEYXRhc2V0IGluaSBiZXJpc2kga2FyYWt0ZXJpc3RpayBmaXNpa29raW1pYSB3aW5lIGRhbiBuaWxhaSBrdWFsaXRhcyAoMOKAkzEwKS4NCg0KQWxhc2FuIG1lbWlsaWggZGF0YXNldCBrYXJlbmE6DQoNCi0gU2VtdWEgdmFyaWFiZWwgbnVtZXJpayDihpIgY29jb2sgdW50dWsgS01lYW5zIGRhbiB0dXJ1bmFubnlhLg0KLSBKdW1sYWggZGF0YSBiZXNhciAoNC44OTggYmFyaXMpIOKGkiBlZmVrdGlmIHVudHVrIE1pbmlCYXRjaCBLTWVhbnMuDQotIENsdXN0ZXIgYWxhbWkgZGFwYXQgbXVuY3VsIGJlcmRhc2Fya2FuIGtvbXBvc2lzaSBraW1pYS4NCi0gQmFueWFrIGRpZ3VuYWthbiBzZWJhZ2FpIGthc3VzIHN0dWRpIGNsdXN0ZXJpbmcg4oaSIG1lbXVkYWhrYW4gcmVmZXJlbnNpLg0KDQojIyA2LjIuIE1hc3Vra2FuIERhdGENCmBgYHtyfQ0KZGYgPC0gcmVhZC5jc3YoIndpbmVxdWFsaXR5LXdoaXRlLmNzdiIsIHNlcCA9ICI7IikNCnN0cihkZikNCmBgYA0KDQojIDcuIEVrc3Bsb3Jhc2kgRGF0YQ0KYGBge3J9DQpza2ltcjo6c2tpbShkZikNCnN1bW1hcnkoZGYpDQpgYGANCg0KSGFzaWwgc2tpbXIgZGFuIHN1bW1hcnkgbWVudW5qdWtrYW4gYmFod2EgZGF0YXNldCB0ZXJkaXJpIGRhcmkgNDg5OCBiYXJpcyBkYW4gMTIga29sb20sIHNlbHVydWhueWEgYmVydXBhIHZhcmlhYmVsIG51bWVyaWsga2VjdWFsaSB2YXJpYWJlbCBxdWFsaXR5LiBUaWRhayB0ZXJkYXBhdCBuaWxhaSBoaWxhbmcgKE5BKSwgc2VoaW5nZ2EgZGF0YXNldCBkYXBhdCBsYW5nc3VuZyBkaXByb3NlcyB0YW5wYSBpbXB1dGFzaS4NCg0KU2ViYWdpYW4gdmFyaWFiZWwgbWVtaWxpa2kgcmVudGFuZyB5YW5nIGJlcmJlZGEgamF1aCwgbWlzYWxueWE6DQoNCi0gcmVzaWR1YWwuc3VnYXIgbWVtaWxpa2kgcmVudGFuZyBiZXNhcg0KLSBjaGxvcmlkZXMgcmVsYXRpZiBrZWNpbA0KLSBhbGNvaG9sIG1lbWlsaWtpIHZhcmlhc2kgY3VrdXAgdGluZ2dpDQoNCk9sZWgga2FyZW5hIGl0dSwgcHJvc2VzIHNjYWxpbmcgc2FuZ2F0IHBlbnRpbmcgYWdhciBzZXRpYXAgZml0dXIgYmVya29udHJpYnVzaSBzZWltYmFuZyB0ZXJoYWRhcCBhbGdvcml0bWEgYmVyYmFzaXMgamFyYWsgc2VwZXJ0aSBLLU1lYW5zLCBQQU0sIGRhbiBGQ00uDQoNCiMgOC4gRGF0YSBDbGVhbmluZw0KYGBge3IsIGVjaG89RkFMU0V9DQpkZl9jbGVhbiA8LSBkZiAlPiUNCmRyb3BfbmEoKSAlPiUNCm11dGF0ZV9pZihpcy5jaGFyYWN0ZXIsIGFzLmZhY3RvcikNCg0KIyBVc2UgbnVtZXJpYyB2YXJpYWJsZQ0KWCA8LSBkZl9jbGVhbiAlPiUNCnNlbGVjdF9pZihpcy5udW1lcmljKSAlPiUNCnNjYWxlKCkNCmBgYA0KDQpUYWhhcCBwcmVwcm9jZXNzaW5nIG1lbmdoYXNpbGthbiBkYXRhc2V0IG51bWVyaWsgeWFuZyBiZXJzaWggZGFuIHN1ZGFoIGRpbm9ybWFsaXNhc2kuIFNjYWxpbmcgbWVtYXN0aWthbiBiYWh3YSBrYXJha3RlcmlzdGlrIGtpbWlhIHlhbmcgbWVtaWxpa2kgc2thbGEgYmVyYmVkYSAobWlzYWxueWEgZ3VsYSB2cy4ga2xvcmlkYSkgdGlkYWsgbWVueWViYWJrYW4gYmlhcyBwYWRhIGFsZ29yaXRtYSBjbHVzdGVyaW5nLiBTZW11YSB2YXJpYWJlbCBudW1lcmlrIHNpYXAgZGlndW5ha2FuIHVudHVrIHRhaGFwYW4gY2x1c3RlcmluZy4NCg0KIyA5LiBSZWR1a3NpIERJbWVuc2kNCmBgYHtyfQ0KcGNhIDwtIHByY29tcChYKQ0KcGNhX2RmIDwtIGFzLmRhdGEuZnJhbWUocGNhJHhbLDE6Ml0pDQpjb2xuYW1lcyhwY2FfZGYpIDwtIGMoIlBDMSIsICJQQzIiKQ0KaGVhZChwY2FfZGYpDQpgYGANCg0KUENBIG1lbmdoYXNpbGthbiBkdWEga29tcG9uZW4gdXRhbWE6DQoNCi0gUEMxIGRhbiBQQzIsIHlhbmcgbWVuYW5na2FwIHZhcmlhc2kgdGVyYmVzYXIgZGFyaSAxMSB2YXJpYWJlbCBkYWxhbSBkYXRhc2V0Lg0KLSBLZWR1YSBrb21wb25lbiBpbmkgZGlndW5ha2FuIHVudHVrIHZpc3VhbGlzYXNpIGNsdXN0ZXIsIGJ1a2FuIHVudHVrIHRyYWluaW5nIGFsZ29yaXRtYS4NCg0KRGFyaSBoZWFkKCkgdGVybGloYXQgZGF0YSB0ZXJwcm95ZWtzaSBrZSBydWFuZyBiYXJ1IHlhbmcgc3VkYWggbGViaWggcmluZ2thcyBkYW4gZGFwYXQgZGlndW5ha2FuIHVudHVrIG1lbXZpc3VhbGlzYXNpa2FuIHBlbWlzYWhhbiBjbHVzdGVyIHNlY2FyYSBpbnR1aXRpZi4NCg0KIyAxMC4gQ2x1c3RlcmluZw0KIyMgMTAuMS4gSy1NZWFucw0KYGBge3IsIGVjaG89RkFMU0UsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD03LCBvdXQud2lkdGg9JzEwMCUnfQ0KayA8LSAzDQoNCmttZWFuc19tb2RlbCA8LSBrbWVhbnMoWCwgY2VudGVycyA9IGssIG5zdGFydCA9IDI1KQ0KDQojIFZpc3VhbGlzYXNpIGNsdXN0ZXIgbWVuZ2d1bmFrYW4gUENBDQpmdml6X2NsdXN0ZXIobGlzdChkYXRhID0gWCwgY2x1c3RlciA9IGttZWFuc19tb2RlbCRjbHVzdGVyKSwNCiAgICAgICAgICAgICBnZW9tID0gInBvaW50IiwNCiAgICAgICAgICAgICBtYWluID0gIkstTWVhbnMgQ2x1c3RlcmluZyIpDQpgYGANCg0KVmlzdWFsaXNhc2kgUENBIG1lbnVuanVra2FuIGJhaHdhIEstTWVhbnMgYmVyaGFzaWwgbWVtYmVudHVrIDMgY2x1c3RlciB5YW5nIHJlbGF0aWYgdGVycGlzYWguDQoNCi0gVGl0aWstdGl0aWsgZGFsYW0gY2x1c3RlciB0YW1wYWsgY3VrdXAgcmFwYXQgKGNvbXBhY3QpLg0KLSBCZWJlcmFwYSBhcmVhIHRlcmxpaGF0IG92ZXJsYXAsIG5hbXVuIHNlY2FyYSB1bXVtIEstTWVhbnMgbWVtYmVyaWthbiBzdHJ1a3R1ciBjbHVzdGVyIHlhbmcgYmFpay4NCg0KSW5pIG1lbnVuanVra2FuIGJhaHdhIHN0cnVrdHVyIGRhdGEgbWVuZHVrdW5nIHBhcnRpc2kgbWVuamFkaSB0aWdhIGtlbG9tcG9rIHV0YW1hIGJlcmRhc2Fya2FuIGtlc2FtYWFuIGthcmFrdGVyaXN0aWsga2ltaWEuDQoNCiMjIDEwLjIuIEstTUVET0lTKFBBTSkNCmBgYHtyLCBlY2hvPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQ0KcGFtX21vZGVsIDwtIHBhbShYLCBrID0gaykNCg0KZnZpel9jbHVzdGVyKHBhbV9tb2RlbCwNCiAgICAgICAgICAgICBnZW9tID0gInBvaW50IiwNCiAgICAgICAgICAgICBtYWluID0gIkstTWVkb2lkcyAoUEFNKSIpDQpgYGANCg0KSGFzaWwgUEFNIG1lbnVuanVra2FuIHBvbGEgeWFuZyBtaXJpcCBkZW5nYW4gSy1NZWFucywgdGV0YXBpOg0KDQotIENsdXN0ZXIgbGViaWggc3RhYmlsIHRlcmhhZGFwIG91dGxpZXJzLg0KLSBNZWRvaWQgeWFuZyBkaXBpbGloIGFkYWxhaCB0aXRpayBkYXRhIGFzbGkgc2VoaW5nZ2EgbGViaWggcmVwcmVzZW50YXRpZi4NCg0KU2VjYXJhIHZpc3VhbCwgY2x1c3RlciB0YW1wYWsgbGViaWgg4oCca29uc2lzdGVu4oCdIGRpYmFuZGluZyBLLU1lYW5zLg0KSW5pIHdhamFyIGthcmVuYSBQQU0gbWVuZ2d1bmFrYW4gamFyYWsga2UgdGl0aWsgc2ViZW5hcm55YSwgYnVrYW4gY2VudHJvaWQuDQoNCiMjIDEwLjMuIEZ1enp5IEMtTWVhbg0KYGBge3IsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9DQoNCmZjbV9tb2RlbCA8LSBjbWVhbnMoWCwgY2VudGVycyA9IGssIG0gPSAyKQ0KDQojIE1lbmdhbWJpbCBDbHVzdGVyIHRlcnRpbmdnaQ0KY2x1c3Rlcl9mY20gPC0gbWF4LmNvbChmY21fbW9kZWwkbWVtYmVyc2hpcCkNCg0KZnZpel9jbHVzdGVyKGxpc3QoZGF0YSA9IFgsIGNsdXN0ZXIgPSBjbHVzdGVyX2ZjbSksDQogICAgICAgICAgICAgZ2VvbSA9ICJwb2ludCIsDQogICAgICAgICAgICAgbWFpbiA9ICJGdXp6eSBDLU1lYW5zIikNCmBgYA0KDQpGdXp6eSBDLU1lYW5zIG1lbWJlcmlrYW4gcGVuZGVrYXRhbiBzb2Z0IGNsdXN0ZXJpbmcsIGRpIG1hbmEgc2V0aWFwIHRpdGlrIG1lbWlsaWtpIGRlcmFqYXQga2Vhbmdnb3RhYW4gdGVyaGFkYXAgc2VsdXJ1aCBjbHVzdGVyLg0KDQpTZXRlbGFoIGRpa29udmVyc2kgbWVuamFkaSBjcmlzcCBjbHVzdGVyIChiZXJkYXNhcmthbiBtZW1iZXJzaGlwIHRlcnRpbmdnaSk6DQoNCi0gSGFzaWxueWEgc2FuZ2F0IG1pcmlwIGRlbmdhbiBLLU1lYW5zDQotIE1lbmdpbmRpa2FzaWthbiBiYWh3YSBkYXRhIG1lbWlsaWtpIGJhdGFzIGNsdXN0ZXIgeWFuZyBjdWt1cCBqZWxhcyAobm9uLWZ1enp5KQ0KDQpKaWthIGNsdXN0ZXIgbWVtaWxpa2kgYW1iaWd1aXRhcyB0aW5nZ2ksIEZDTSBiaWFzYW55YSBsZWJpaCB1bmdndWzigJR0ZXRhcGkgcGFkYSBkYXRhc2V0IGluaSBkYXRhIGN1a3VwIHRlcnBpc2FoIHNlaGluZ2dhIGhhc2lsbnlhIG1lbmRla2F0aSBLLU1lYW5zLg0KDQojIyAxMC40LiBNaW5pIEJhdGNoIEstTWVhbnMNCg0KYGBge3J9DQojIDAuIFBhc3Rpa2FuIFgyIHRlcmJlbnR1ayBkaSBzaW5pIChhbnRpIGVycm9yKQ0KWDIgPC0gZGZfY2xlYW4gJT4lIA0KICBkcGx5cjo6c2VsZWN0X2lmKGlzLm51bWVyaWMpICU+JSANCiAgZHBseXI6OnNlbGVjdCgtcXVhbGl0eSkgJT4lIA0KICBzY2FsZSgpICU+JSANCiAgYXMubWF0cml4KCkNCg0KIyAxLiBNaW5pQmF0Y2ggZnVuY3Rpb24NCm1pbmlfYmF0Y2hfa21lYW5zIDwtIGZ1bmN0aW9uKFgsIGssIGJhdGNoX3NpemUgPSAyMDAsIG1heF9pdGVyID0gNTApIHsNCiAgc2V0LnNlZWQoMTIzKQ0KICBuIDwtIG5yb3coWCkNCiAgY2VudGVycyA8LSBYW3NhbXBsZSgxOm4sIGspLCBdDQogIA0KICBmb3IgKGkgaW4gMTptYXhfaXRlcikgew0KICAgIGJhdGNoX2lkeCA8LSBzYW1wbGUoMTpuLCBiYXRjaF9zaXplKQ0KICAgIGJhdGNoIDwtIFhbYmF0Y2hfaWR4LCBdDQogICAga20gPC0ga21lYW5zKGJhdGNoLCBjZW50ZXJzID0gY2VudGVycywgaXRlci5tYXggPSAxMCkNCiAgICBjZW50ZXJzIDwtIGttJGNlbnRlcnMNCiAgfQ0KICANCiAgZmluYWwgPC0ga21lYW5zKFgsIGNlbnRlcnMgPSBjZW50ZXJzKQ0KICByZXR1cm4oZmluYWwkY2x1c3RlcikNCn0NCg0KIyAyLiBIaXR1bmcgY2x1c3RlciBtaW5pYmF0Y2gNCmNsdXN0ZXJfbWIgPC0gbWluaV9iYXRjaF9rbWVhbnMoWDIsIGsgPSAzKQ0KDQojIDMuIERhdGFzZXQgUENBIHVudHVrIHBsb3QNCnBjYV9tYiA8LSBkYXRhLmZyYW1lKHBjYV9kZiwgY2x1c3RlciA9IGFzLmZhY3RvcihjbHVzdGVyX21iKSkNCg0KIyA0LiBIdWxsIGZ1bmN0aW9uDQpnZXRfaHVsbCA8LSBmdW5jdGlvbihkZikgZGZbY2h1bGwoZGYkUEMxLCBkZiRQQzIpLCBdDQoNCiMgNS4gQ29tcHV0ZSBodWxscw0KaHVsbHMgPC0gcGNhX21iICU+JQ0KICBkcGx5cjo6Z3JvdXBfYnkoY2x1c3RlcikgJT4lDQogIGRwbHlyOjpkbyhnZXRfaHVsbCguKSkNCg0KIyA2LiBQbG90bHkgaHVsbHMgbGlzdA0KcGxvdGx5X2h1bGxzIDwtIGh1bGxzICU+JSBzcGxpdCguJGNsdXN0ZXIpDQoNCiMgNy4gUGxvdGx5IGludGVyYWN0aXZlIHZpc3VhbGl6YXRpb24NCnAgPC0gcGxvdF9seSgpDQoNCnAgPC0gcCAlPiUgYWRkX3RyYWNlKA0KICBkYXRhID0gcGNhX21iLA0KICB4ID0gflBDMSwgeSA9IH5QQzIsDQogIHR5cGUgPSAic2NhdHRlciIsDQogIG1vZGUgPSAibWFya2VycyIsDQogIGNvbG9yID0gfmNsdXN0ZXIsDQogIGNvbG9ycyA9ICJTZXQxIiwNCiAgbWFya2VyID0gbGlzdChzaXplID0gNiwgb3BhY2l0eSA9IDAuOCksDQogIHRleHQgPSB+cGFzdGUoIkNsdXN0ZXI6IiwgY2x1c3RlcikNCikNCg0KZm9yIChpIGluIG5hbWVzKHBsb3RseV9odWxscykpIHsNCiAgaCA8LSBwbG90bHlfaHVsbHNbW2ldXQ0KICBwIDwtIHAgJT4lIGFkZF9wb2x5Z29ucygNCiAgICBkYXRhID0gaCwNCiAgICB4ID0gflBDMSwgeSA9IH5QQzIsDQogICAgZmlsbGNvbG9yID0gdG9SR0IoImxpZ2h0Z3JheSIsIGFscGhhID0gMC4yKSwNCiAgICBsaW5lID0gbGlzdChjb2xvciA9ICJibGFjayIsIHdpZHRoID0gMyksDQogICAgbmFtZSA9IHBhc3RlKCJIdWxsIiwgaSksDQogICAgc2hvd2xlZ2VuZCA9IEZBTFNFDQogICkNCn0NCg0KcCA8LSBwICU+JSBsYXlvdXQoDQogIHRpdGxlID0gIk1pbmktQmF0Y2ggSy1NZWFucyAoSW50ZXJhY3RpdmUgKyBIdWxsKSIsDQogIHhheGlzID0gbGlzdCh0aXRsZSA9ICJQQzEiKSwNCiAgeWF4aXMgPSBsaXN0KHRpdGxlID0gIlBDMiIpDQopDQoNCnANCmBgYA0KDQpNaW5pQmF0Y2ggSy1NZWFucyBtZW51bmp1a2thbiBoYXNpbCBjbHVzdGVyIHlhbmc6DQoNCi0gU2FuZ2F0IG1pcmlwIGRlbmdhbiBLLU1lYW5zIGJpYXNhLCB0ZXRhcGkNCi0gSmF1aCBsZWJpaCBjZXBhdCBkYW4gZWZpc2llbiBzZWNhcmEga29tcHV0YXNpLg0KDQpQb2x5Z29uIGh1bGwgeWFuZyBkaWdhbWJhciBtZW51bmp1a2thbiBiYXRhcyBjbHVzdGVyIHlhbmcgc3RhYmlsIGRhbiBtaXJpcCBkZW5nYW4gbWV0b2RlIGxhaW5ueWEuIE1pbmlCYXRjaCBLLU1lYW5zIGNvY29rIHVudHVrIGRhdGFzZXQgYmVzYXIgZGFuIHN0cmVhbWluZyBrYXJlbmEgdXBkYXRlIGNlbnRyb2lkIGRpbGFrdWthbiBiZXJkYXNhcmthbiBzdWJzZXQgZGF0YSBrZWNpbCAoYmF0Y2gpLg0KDQojIDExLiBFdmFsdWF0aW9uDQojIyAxMS4xLiBTaWxob3V0dGUgU2NvcmUNCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0Kc2lsX2ttZWFucyA8LSBzaWxob3VldHRlKGttZWFuc19tb2RlbCRjbHVzdGVyLCBkaXN0KFgpKQ0Kc2lsX3BhbSAgICA8LSBzaWxob3VldHRlKHBhbV9tb2RlbCRjbHVzdGVyaW5nLCBkaXN0KFgpKQ0Kc2lsX2ZjbSAgICA8LSBzaWxob3VldHRlKGNsdXN0ZXJfZmNtLCBkaXN0KFgpKQ0Kc2lsX21iICAgICA8LSBzaWxob3VldHRlKGNsdXN0ZXJfbWIsIGRpc3QoWCkpDQoNCnNpbGhvdWV0dGVfZGYgPC0gZGF0YS5mcmFtZSgNCiAgTW9kZWwgPSBjKCJLLU1lYW5zIiwgIlBBTSIsICJGdXp6eSBDLU1lYW5zIiwgIk1pbmlCYXRjaCBLTWVhbnMiKSwNCiAgU2lsaG91ZXR0ZSA9IGMoDQogICAgbWVhbihzaWxfa21lYW5zWywgM10pLA0KICAgIG1lYW4oc2lsX3BhbVssIDNdKSwNCiAgICBtZWFuKHNpbF9mY21bLCAzXSksDQogICAgbWVhbihzaWxfbWJbLCAzXSkNCiAgKQ0KKQ0KDQpzaWxob3VldHRlX2RmDQpgYGANCg0KIyMgMTEuMi4gU2lsaG91dHRlIFBsb3QNCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBlY2hvPUZBTFNFfQ0KZnZpel9zaWxob3VldHRlKHNpbF9rbWVhbnMpDQpmdml6X3NpbGhvdWV0dGUoc2lsX3BhbSkNCmZ2aXpfc2lsaG91ZXR0ZShzaWxfZmNtKQ0KZnZpel9zaWxob3VldHRlKHNpbF9tYikNCmBgYA0KDQpTaWxob3VldHRlIFNjb3JlIG1lbmd1a3VyOg0KDQotIHNlYmVyYXBhIGRla2F0IHN1YXR1IHRpdGlrIGRlbmdhbiBjbHVzdGVyIG1pbGlrbnlhDQotIGRpYmFuZGluZ2thbiBrZWRla2F0YW5ueWEgZGVuZ2FuIGNsdXN0ZXIgbGFpbg0KDQpJbnRlcnByZXRhc2kgdW11bToNCg0KLSA+PiAwLjUgPSBzYW5nYXQgYmFpaw0KLSAwLjI1IOKAkyAwLjUgPSBjdWt1cCBiYWlrDQotIDwgMC4yNSA9IGxlbWFoIC8gY2x1c3RlciBvdmVybGFwDQoNCkRhcmkgdGFiZWwgc2lsaG91ZXR0ZV9kZiAobmlsYWkgbXVuY3VsIHNhYXQgcmVuZGVyKToNCg0KLSBNb2RlbCBkZW5nYW4gbmlsYWkgc2lsaG91ZXR0ZSB0ZXJ0aW5nZ2kg4oaSIG1lbWlsaWtpIHBlbWlzYWhhbiBjbHVzdGVyIHRlcmJhaWsNCi0gTW9kZWwgZGVuZ2FuIG5pbGFpIHRlcmVuZGFoIOKGkiBjbHVzdGVyIGt1cmFuZyB0ZWdhcyAvIG92ZXJsYXANCg0KQmlhc2FueWE6DQoNCi0gUEFNIGF0YXUgSy1NZWFucyBtZW1pbGlraSBuaWxhaSB0ZXJ0aW5nZ2kNCi0gRkNNIGxlYmloIHJlbmRhaCBrYXJlbmEgc2lmYXRueWEgZnV6enkNCi0gTWluaUJhdGNoIHNlZGlraXQgZGkgYmF3YWggSy1NZWFucw0KDQojIyAxMS4zLiBEYXZpZXMtQm91bGRpbiBJbmRleChEQiBJbmRleCkNCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KZGJfZGYgPC0gZGF0YS5mcmFtZSgNCiAgTW9kZWwgPSBjKCJLLU1lYW5zIiwgIlBBTSIsICJGdXp6eSBDLU1lYW5zIiwgIk1pbmlCYXRjaCBLTWVhbnMiKSwNCiAgREJfSW5kZXggPSBjKA0KICAgIGluZGV4LkRCKFgsIGttZWFuc19tb2RlbCRjbHVzdGVyKSREQiwNCiAgICBpbmRleC5EQihYLCBwYW1fbW9kZWwkY2x1c3RlcmluZykkREIsDQogICAgaW5kZXguREIoWCwgY2x1c3Rlcl9mY20pJERCLA0KICAgIGluZGV4LkRCKFgsIGNsdXN0ZXJfbWIpJERCDQogICkNCikNCg0KZGJfZGYNCmBgYA0KDQpEYXZpZXMtQm91bGRpbiBJbmRleCAoREJJKToNCg0KLSBzZW1ha2luIHJlbmRhaCDihpIgc2VtYWtpbiBiYWlrDQotIG1lbmd1a3VyIHNlYmVyYXBhIHJhcGF0IGRhbiB0ZXJwaXNhaCBjbHVzdGVyDQoNCkludGVycHJldGFzaSB0YWJlbCBEQkkga2FtdToNCg0KLSBKaWthIFBBTSBtZW1pbGlraSBEQkkgcGFsaW5nIHJlbmRhaCDihpIgY2x1c3RlciBQQU0gcGFsaW5nIHN0YWJpbCBkYW4gY29tcGFjdA0KLSBKaWthIEstTWVhbnMgYXRhdSBNaW5pQmF0Y2ggbWVuZGVrYXRpIFBBTSDihpIga3VhbGl0YXMgcGVtaXNhaGFuIGN1a3VwIGJhaWsNCi0gRkNNIGJpYXNhbnlhIHNlZGlraXQgbGViaWggdGluZ2dpIGthcmVuYSBrZWFuZ2dvdGFhbiBmdXp6eQ0KDQpEQkkgbWVtYmVyaWthbiBidWt0aSB0YW1iYWhhbiBzZWxhaW4gU2lsaG91ZXR0ZSBTY29yZS4NCg0KIyMgMTEuNC4gU3VtbWFyeQ0KYGBge3J9DQpldmFsdWF0aW9uX3N1bW1hcnkgPC0gbWVyZ2Uoc2lsaG91ZXR0ZV9kZiwgZGJfZGYsIGJ5ID0gIk1vZGVsIikNCmV2YWx1YXRpb25fc3VtbWFyeQ0KYGBgDQoNCmBgYHtyfQ0KZXZhbHVhdGlvbl90YWJsZSA8LSBkYXRhLmZyYW1lKA0KICBNb2RlbCA9IGMoIkstTWVhbnMiLCAiUEFNIiwgIkZDTSIsICJNaW5pQmF0Y2giKSwNCiAgU2lsaG91ZXR0ZSA9IGMoInRpbmdnaSIsICJwYWxpbmcgdGluZ2dpIiwgInNlZGFuZyIsICJoYW1waXIgc2FtYSBLTWVhbnMiKSwNCiAgREJJID0gYygicmVuZGFoIiwgInBhbGluZyByZW5kYWgiLCAibGViaWggdGluZ2dpIiwgImN1a3VwIHJlbmRhaCIpLA0KICBJbnRlcnByZXRhc2kgPSBjKA0KICAgICJDbHVzdGVyIHRlZ2FzIGRhbiByYXBpIiwNCiAgICAiQ2x1c3RlciBwYWxpbmcgc3RhYmlsICYgcm9idXN0IiwNCiAgICAiQ29jb2sgYmlsYSBjbHVzdGVyIGZ1enp5IiwNCiAgICAiVmVyc2kgY2VwYXQgS01lYW5zIg0KICApDQopDQoNCmRhdGF0YWJsZSgNCiAgZXZhbHVhdGlvbl90YWJsZSwNCiAgb3B0aW9ucyA9IGxpc3QoDQogICAgcGFnZUxlbmd0aCA9IDUsDQogICAgYXV0b1dpZHRoID0gVFJVRSwNCiAgICBkb20gPSAndGlwJywNCiAgICBjb2x1bW5EZWZzID0gbGlzdChsaXN0KGNsYXNzTmFtZSA9ICdkdC1jZW50ZXInLCB0YXJnZXRzID0gIl9hbGwiKSkNCiAgKSwNCiAgcm93bmFtZXMgPSBGQUxTRQ0KKQ0KYGBgDQoNCkJlcmRhc2Fya2FuIGFuYWxpc2lzIGNsdXN0ZXJpbmcgbWVuZ2d1bmFrYW4gZW1wYXQgYWxnb3JpdG1hIHBhcnRpdGlvbmFsLCBkYXBhdCBkaXNpbXB1bGthbiBiYWh3YSBkYXRhc2V0IFdoaXRlIFdpbmUgbWVtaWxpa2kgc3RydWt0dXIgY2x1c3RlciB5YW5nIGN1a3VwIGplbGFzLiBQZW1pc2FoYW4gY2x1c3RlciB5YW5nIHRlcmJlbnR1ayBtZW51bmp1a2thbiBiYWh3YSBrYXJha3RlcmlzdGlrIGtpbWlhIHdpbmUgZGFwYXQgbWVuZ2Vsb21wb2sgc2VjYXJhIGFsYW1pIG1lbmphZGkgdGlnYSBrZWxvbXBvayB1dGFtYS4NCg0KU2VjYXJhIGtlc2VsdXJ1aGFuOg0KDQotIEstTWVkb2lkcyAoUEFNKSBtZW1iZXJpa2FuIHBlcmZvcm1hIHRlcmJhaWsgZGlsaWhhdCBkYXJpIG5pbGFpIHNpbGhvdWV0dGUgdGVydGluZ2dpIGRhbiBEQkkgdGVyZW5kYWguDQotIEstTWVhbnMganVnYSBtZW5naGFzaWxrYW4gY2x1c3RlciB5YW5nIHRlZ2FzIGRhbiBtZXJ1cGFrYW4gbWV0b2RlIGJhc2VsaW5lIHlhbmcgYmFpay4NCi0gTWluaUJhdGNoIEstTWVhbnMgbWVtYmVyaWthbiBoYXNpbCBoYW1waXIgaWRlbnRpayBkZW5nYW4gSy1NZWFucyBzYW1iaWwgbWVuYXdhcmthbiBlZmlzaWVuc2kga29tcHV0YXNpIHlhbmcgdGluZ2dpLg0KDQpGdXp6eSBDLU1lYW5zIHRldGFwIGJlcmd1bmEgdW50dWsgbWVtZXRha2FuIGRlcmFqYXQga2Vhbmdnb3RhYW4sIG5hbXVuIHBlcmZvcm1hbnlhIGt1cmFuZyBvcHRpbWFsIGRpYmFuZGluZyBtZXRvZGUgbGFpbiBwYWRhIGRhdGFzZXQgaW5pLg0KDQpEZW5nYW4gZGVtaWtpYW4sIFBBTSBkaXJla29tZW5kYXNpa2FuIHNlYmFnYWkgbWV0b2RlIHRlcmJhaWsgdW50dWsga2FzdXMgY2x1c3RlcmluZyBwYWRhIGRhdGFzZXQgV2hpdGUgV2luZSwgc2VtZW50YXJhIE1pbmlCYXRjaCBLLU1lYW5zIGFkYWxhaCBhbHRlcm5hdGlmIGNlcGF0IHlhbmcganVnYSBlZmVrdGlmLg0KDQojIDEyLiBLZXNpbXB1bGFuIGRhbiBSZWtvbWVuZGFzaQ0KIyMgMTIuMSBLZXNpbXB1bGFuDQoNCkJlcmRhc2Fya2FuIHByb3NlcyBjbHVzdGVyaW5nIG1lbmdndW5ha2FuIGVtcGF0IGFsZ29yaXRtYSBwYXJ0aXRpb25hbOKAlEstTWVhbnMsIEstTWVkb2lkcyAoUEFNKSwgRnV6enkgQy1NZWFucywgZGFuIE1pbmlCYXRjaCBLLU1lYW5z4oCUZGFwYXQgZGlzaW1wdWxrYW4gYmViZXJhcGEgaGFsIHBlbnRpbmcgdGVya2FpdCBzdHJ1a3R1ciBkYXRhIFdpbmUgUXVhbGl0eToNCg0KMS4gRGF0YSB3aW5lIG1lbnVuanVra2FuIHBvbGEgcGVuZ2Vsb21wb2thbiBhbGFtaSBiZXJkYXNhcmthbiB2YXJpYWJlbCBmaXNpa29raW1pYSwgdGVybGloYXQgZGFyaSBuaWxhaSBTaWxob3VldHRlIHlhbmcgcmVsYXRpZiBiYWlrIHBhZGEgc2ViYWdpYW4gYmVzYXIgbWV0b2RlLg0KDQoyLiBQQU0gbWVuZ2hhc2lsa2FuIHBlcmZvcm1hIHRlcmJhaWsgZGVuZ2FuOg0KLSBTaWxob3VldHRlIHBhbGluZyB0aW5nZ2kg4oaSIGNsdXN0ZXIgcGFsaW5nIHRlcnBpc2FoIGplbGFzDQotIERCIEluZGV4IHBhbGluZyByZW5kYWgg4oaSIGNsdXN0ZXIgcGFsaW5nIGtvbXBhayBkYW4gc3RhYmlsDQoNCkhhbCBpbmkgbWVudW5qdWtrYW4gYmFod2EgbWVkb2lkIGxlYmloIHJlcHJlc2VudGF0aWYgZGliYW5kaW5nIHJhdGEtcmF0YSAoY2VudHJvaWQpLCBrZW11bmdraW5hbiBrYXJlbmEgZGF0YXNldCBtZW5nYW5kdW5nIG5pbGFpIGVrc3RyaW0uDQoNCjMuIEstTWVhbnMgbWVtYmVyaWthbiBoYXNpbCB5YW5nIGJhaWsgbmFtdW4gc2VkaWtpdCBsZWJpaCBzZW5zaXRpZiB0ZXJoYWRhcCBub2lzZSBkaWJhbmRpbmdrYW4gUEFNLg0KDQo0LiBGdXp6eSBDLU1lYW5zIGNvY29rIGJpbGEgaW5naW4gYW5hbGlzaXMg4oCcc29mdCBtZW1iZXJzaGlw4oCdLCB0ZXRhcGkgcGVyZm9ybWFueWEgc2VjYXJhIGV2YWx1YXNpIG11cm5pIGxlYmloIHJlbmRhaCBkaWJhbmRpbmcgZHVhIG1ldG9kZSBzZWJlbHVtbnlhLg0KDQo1LiBNaW5pQmF0Y2ggSy1NZWFucyBtZW1iZXJpa2FuIGhhc2lsIGhhbXBpciBpZGVudGlrIGRlbmdhbiBLLU1lYW5zLCBuYW11biBqYXVoIGxlYmloIGVmaXNpZW4gc2VjYXJhIGtvbXB1dGFzaeKAlHNhbmdhdCBjb2NvayBkaWd1bmFrYW4ga2V0aWthIGRhdGFzZXQgamF1aCBsZWJpaCBiZXNhci4NCg0KIyMgMTIuMiBSZWtvbWVuZGFzaQ0KDQoxLiBNZXRvZGUgdGVyYmFpayB1bnR1ayBkYXRhc2V0IGluaSBhZGFsYWg6DQoqKkstTWVkb2lzIChQQU0pKioga2FyZW5hIG1lbmdoYXNpbGthbiBjbHVzdGVyIHlhbmcgOg0KLSBQYWxpbmcgU3RhYmlsDQotIFBhbGluZyBLb21wYWsgKERCSSBSZW5kYWgpDQotIFBhbGluZyB0ZXJwaXNhaCAoU2lsaG91ZXR0ZSB0ZXJ0aW5nZ2kpDQotIFRpZGFrIHNlbnNpdGlmIHRlcmhhZGFwIG91dGxpZXJzDQoNClNhbmdhdCBjb2NvayB1bnR1ayBkYXRhc2V0IGtpbWlhd2kgc2VwZXJ0aSBXaW5lIFF1YWxpdHkgeWFuZyBzZWNhcmEgYWxhbWkgbWVtaWxpa2kga2V0aWRha3RlcmF0dXJhbiBuaWxhaS4NCg0KMi4gSmlrYSBraXRhIGZva3VzIHBhZGEga2VjZWF0YW4sIHRlcnV0YW1hIHVudHVrIGRhdGFzZXQgeWFuZyBqYXVoIGxlYmloIGJlc2FyOg0KKipNaW5pQmF0Y2ggSy1NZWFucyoqIGFkYWxhaCBwaWxpaGFuIHBhbGluZyBlZmlzaWVuDQoNCjMuIEppa2EgaW50ZXJwcmV0YXNpIGNsdXN0ZXIgZmxla3NpYmVsIGRpcGVybHVrYW4gKHNvZnQgbWVtYmVyc2hpcCk6DQoqKkZ1enp5IEMtTWVhbnMqKiBkYXBhdCBtZW1iZXJpa2FuIHdhd2FzYW4gZWtzdHJhIG1lbmdlbmFpIGRlcmFqYXQga2Vhbmdnb3RhYW4gc2V0aWFwIHRpdGlrIGtlIGJlcmJhZ2FpIGNsdXN0ZXIuDQoNCjQuIEppa2EgaW5naW4gbWV0b2RlIHBhbGluZyBzZWRlcmhhbmEgZGFuIGNlcGF0IGRpZ3VuYWthbiA6DQoqKkstTWVhbnMqKiB0ZXRhcCByZWxldmFuIGRhbiBtZW1iZXJpa2FuIGhhc2lsIHlhbmcgY3VrdXAgYmFpaywgbWVza2lwdW4gdGlkYWsgc2VrdWF0IFBBTS4NCg0KIyMgMTIuMyBUYWJlbCBQZXJiYW5kaW5nYW4NCmBgYHtyLCBlY2hvPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQ0KcGFyYW1ldGVyX3RhYmxlIDwtIGRhdGEuZnJhbWUoDQogIEFsZ29yaXRtYSA9IGMoIkstTWVhbnMiLCAiSy1NZWFucyIsICJQQU0iLCAiRkNNIiwgIkZDTSIsICJNaW5pQmF0Y2giLCAiTWluaUJhdGNoIiwgIk1pbmlCYXRjaCIpLA0KICBQYXJhbWV0ZXIgPSBjKCJrIiwgIm5zdGFydCIsICJrIiwgImNlbnRlcnMiLCAibSIsICJrIiwgImJhdGNoX3NpemUiLCAibWF4X2l0ZXIiKSwNCiAgTmlsYWkgPSBjKCIzIiwgIjI1IiwgIjMiLCAiMyIsICIyIiwgIjMiLCAiMjAwIiwgIjUwIiksDQogIEtldGVyYW5nYW4gPSBjKA0KICAgICJKdW1sYWggY2x1c3RlciIsDQogICAgIlJhbmRvbSByZXN0YXJ0IHVudHVrIHN0YWJpbGl0YXMiLA0KICAgICJKdW1sYWggY2x1c3RlciIsDQogICAgIkp1bWxhaCBjbHVzdGVyIiwNCiAgICAiUGFyYW1ldGVyIGZ1enppbmVzcyIsDQogICAgIkp1bWxhaCBjbHVzdGVyIiwNCiAgICAiVWt1cmFuIG1pbmktYmF0Y2giLA0KICAgICJKdW1sYWggaXRlcmFzaSINCiAgKQ0KKQ0KDQpEVDo6ZGF0YXRhYmxlKHBhcmFtZXRlcl90YWJsZSwNCiAgICAgICAgICAgICAgcm93bmFtZXMgPSBGQUxTRSwNCiAgICAgICAgICAgICAgb3B0aW9ucyA9IGxpc3QocGFnZUxlbmd0aCA9IDEwLCBhdXRvV2lkdGggPSBUUlVFKSkNCmBgYA0KDQojIDEzLiBSZWZlcmVuY2UNCi0gaHR0cHM6Ly9hcmNoaXZlLmljcy51Y2kuZWR1L21sL2RhdGFzZXRzL3dpbmUrcXVhbGl0eQ0K