Clustering

Clustering merupakan salah satu metode Unsupervised Learning yang bertujuan untuk melakukan pengelompokan data berdasarkan kemiripan/jarak antar data. Clustering memiliki karakteristik dimana anggota dalam satu cluster memiliki kemiripan yang sama atau jarak yang dekat, sementara anggota antar cluster memiliki kemiripan yang sangat berbeda atau jarak yang sangat jauh (Tan et al., 2006). Metode clustering dibagi menjadi dua jenis, yaitu Hierarchical Clustering dan Partitional Clustering.

A. Hierarchical Clustering

Hierarchical Clustering adalah metode pengelompokan data yang dilakukan dengan membuat suatu bagan hirarki (dendrogram) dengan tujuan menunjukkan kemiripan antar data.

Approach

Secara umum, hierarchical clustering dibagi menjadi dua jenis yaitu Agglomerative dan Divisive. Kedua metode ini dibedakan berdasarkan pendekatan dalam melakukan pengelompokkan data hingga membentuk dendrogram, menggunakan bottom-up atau top-down manner.

1. Agglomerative Clustering

Agglomerative Clustering biasa disebut juga sebagai agglomerative nesting (AGNES) dimana cara kerja dalam melakukan pengelompokan data menggunakan bottom-up manner. Prosesnya dimulai dengan menganggap setiap data sebagai satu cluster kecil (leaf) yang hanya memiliki satu anggota saja, lalu pada tahap selanjutnya dua cluster yang memiliki kemiripan akan dikelompokkan menjadi satu cluster yang lebih besar (nodes) dan proses ini akan dilakukan terus menerus hingga semua data menjadi satu cluster besar (root).

2. Divisive Hierarchical Clustering

Divisive Hierarchical Clustering biasa disebut juga sebagai divisive analysis (DIANA) di mana cara kerja dalam melakukan pengelompokan data menggunakan top-down manner. Prosesnya dimulai dengan menganggap satu set data sebagai satu cluster besar (root), lalu dalam setiap iterasinya setiap data yang memiliki karakteristik yang berbeda akan dipecah menjadi dua cluster yang lebih kecil (nodes) dan proses akan terus berjalan hingga setiap data menjadi satu cluster kecil (leaf) yang hanya memiliki satu anggota saja.

Berikut adalah ilustrasi mengenai bagaimana Agglomerative dan Disive Clustering bekerja

knitr::include_graphics("download.png")

Linkage Method

Dalam hierarchical clustering, selain menghitung jarak antar data, diperlukan juga cara untuk menghitung jarak antar cluster sehingga dapat terbentuk dendrogram dari cluster-cluster yang memiliki kedekatan. Proses penggabungan cluster-cluster kecil menjadi satu dendrogram utuh dilakukan melalui beberapa pendekatan yang disebut Linkage Method.

Berikut ini beberapa linkage method yang sering digunakan pada agglomerative approach:

  1. Complete Linkage/Maximum Linkage
  2. Single Linkage/Minimum Linkage
  3. Average Linkage
  4. Centroid Linkage

1. Complete/Maximum Linkage

Pada metode complete linkage, jarak antar cluster ditentukan berdasarkan jarak terbesar (maximum distance) antara pasangan data dari dua cluster yang berbeda. Artinya, semua pasangan data antar cluster dihitung jaraknya terlebih dahulu, kemudian dipilih jarak yang paling besar sebagai jarak antar cluster.

Metode ini biasanya menghasilkan cluster yang lebih terpisah dan lebih kompak (compact).

Formula:

\[d_{12} = \max_{i,j} d(X_i, Y_j)\]

Keterangan:

  • \(X_1,X_2, ..., X_k\) : observasi pada cluster 1
  • \(Y_1,Y_2, ..., Y_k\) : observasi pada cluster 2

  • \(d(X_i,Y_j)\) : jarak antara data pada cluster 1 dan cluster 2

2. Single/Minimum Linkage

Pada metode single linkage, jarak antar cluster ditentukan berdasarkan jarak terkecil (minimum distance) antara pasangan data dari dua cluster yang berbeda.

Metode ini cenderung menghasilkan cluster yang lebih longgar (loose) karena penggabungan cluster hanya bergantung pada pasangan data yang lebih dekat.

Formula:

\[d_{12} = \min_{i,j} d(X_i, Y_j)\]

3. Average Linkage

Pada metode average linkage, jarak antar cluster dihitung dengan mengambil rata-rata jarak dari semua pasangan data yang berasal dari dua cluster yang berbeda.

Metode ini biasanya menghasilkan cluster yang tidak terlalu rapat (compact) maupun terlalu longgar (loose).

Formula:

\[d_{12} = \frac{1}{kl}\sum_{i=1}^{k}\sum_{j=1}^{l} d(X_i, Y_j)\]

4. Centroid Linkage

Pada metode centroid linkage, jarak antar cluster dihitung berdasarkan jarak antara centroid (titik pusat) dari masing-masing cluster. Centroid dihitung menggunakan rata-rata dari data pada cluster tersebut.

Cluster yang memiliki jarak centroid paling kecil akan digabungkan terlebih dahulu dalam proses pembentukan dendrogram.

Formula:

\[d_{12} = d(\bar{X}, \bar{Y})\]

Study Case: USArrests Analysis

Mengaplikasikan cara kerja hierarchical clustering AGNES untuk menganalisis kedekatan Negara Bagian AS berdasarkan tingkat kriminalitasnya. Analisis dilakukan menggunakan dataset USArrests yang menyimpan informasi statistik terkait jumlah kriminalitas assault,murder, dan rape per 100,000 penduduk pada 50 negara bagian Amerika Serikat pada tahun 1973.

1. Dataset

head(USArrests)

2. Preprocessing Data

# Cek missing value
anyNA(USArrests)
## [1] FALSE
# Cek skala tiap variable data
summary(USArrests)
##      Murder          Assault         UrbanPop          Rape      
##  Min.   : 0.800   Min.   : 45.0   Min.   :32.00   Min.   : 7.30  
##  1st Qu.: 4.075   1st Qu.:109.0   1st Qu.:54.50   1st Qu.:15.07  
##  Median : 7.250   Median :159.0   Median :66.00   Median :20.10  
##  Mean   : 7.788   Mean   :170.8   Mean   :65.54   Mean   :21.23  
##  3rd Qu.:11.250   3rd Qu.:249.0   3rd Qu.:77.75   3rd Qu.:26.18  
##  Max.   :17.400   Max.   :337.0   Max.   :91.00   Max.   :46.00
# Standarisasi
us <- scale(USArrests)
head(us)
##                Murder   Assault   UrbanPop         Rape
## Alabama    1.24256408 0.7828393 -0.5209066 -0.003416473
## Alaska     0.50786248 1.1068225 -1.2117642  2.484202941
## Arizona    0.07163341 1.4788032  0.9989801  1.042878388
## Arkansas   0.23234938 0.2308680 -1.0735927 -0.184916602
## California 0.27826823 1.2628144  1.7589234  2.067820292
## Colorado   0.02571456 0.3988593  0.8608085  1.864967207

3. Mengukur Jarak Antar Data

# Menghitung distance
us_dist <- dist(x=us,method = "euclidean")
as.matrix(us_dist)[1:6,1:6]
##             Alabama   Alaska  Arizona Arkansas California Colorado
## Alabama    0.000000 2.703754 2.293520 1.289810   3.263110 2.651067
## Alaska     2.703754 0.000000 2.700643 2.826039   3.012541 2.326519
## Arizona    2.293520 2.700643 0.000000 2.717758   1.310484 1.365031
## Arkansas   1.289810 2.826039 2.717758 0.000000   3.763641 2.831051
## California 3.263110 3.012541 1.310484 3.763641   0.000000 1.287619
## Colorado   2.651067 2.326519 1.365031 2.831051   1.287619 0.000000

4. Dendrogram

library(cluster)
library(factoextra)
library(dplyr)
Complete Linkage
us_hc_complete <- hclust(us_dist, method = "complete")

fviz_dend(us_hc_complete,
          cex = 0.5,
          main = "Cluster Dendrogram Complete Linkage")

complete_coph <- cophenetic(us_hc_complete)
cor(complete_coph, us_dist)
## [1] 0.6979437
complete_clust <- cutree(us_hc_complete, k = 4)

Berikut ini banyak anggota dari masing-masing cluster yang terbentuk.

table(complete_clust)
## complete_clust
##  1  2  3  4 
##  8 11 21 10

Anggota pada masing-masing cluster cukup merata dengan cluster yang paling banyak memiliki anggota adalah cluster 3. Berikut hasil akhir cluster yang terbentuk apabila dilihat dari dendrogram.

fviz_dend(us_hc_complete,
          k = 4,
          cex = 0.5,
          k_colors = "jco",
          rect = TRUE,
          main = "Complete Linkage Cluster")

Cara membaca cluster pada dendrogram sebagai berikut :

  • Cluster 1 digambarkan oleh dendrogram berwarna merah.
  • Cluster 2 digambarkan oleh dendrogram berwarna abu-abu.
  • Cluster 3 digambarkan oleh dendrogram berwarna kuning.
  • Cluster 4 digambarkan oleh dendrogram berwarna biru.
Single Linkage
us_hc_single <- hclust(d = us_dist, method = "single")

fviz_dend(us_hc_single, cex = 0.5, 
          main = "Cluster Dendrogram Single Linkage")

single_coph <- cophenetic(us_hc_single)
cor(single_coph, us_dist)
## [1] 0.541272

korelasi yang diperoleh rendah, artinya cluster yang terbentuk kurang merepresentasikan kondisi data yang ada. Sehingga menggunakan single linkage untuk data USArrest kurang relevan.

single_clust <- cutree(us_hc_single, k = 4)

Berikut ini banyak anggota dari masing-masing cluster yang terbentuk.

table(single_clust)
## single_clust
##  1  2  3  4 
## 46  1  2  1

Anggota pada cluster yang terbentuk sangat berbeda secara signifikan. Cluster 1 memiliki anggota yang paling banyak hampir sebanyak total observasi pada data. Hal ini yang dikarenakan terlalu rapatnya antar observasi sehingga sulit dipisahkan perbedaannya.

fviz_dend(us_hc_single, k = 4, k_colors = "jco", rect = T, 
          main = "Single Linkage Cluster")

Cara membaca cluster pada dendrogram yaitu sebagai berikut :

  • Cluster 1 digambarkan oleh dendrogram berwarna merah.
  • Cluster 2 digambarkan oleh dendrogram berwarna biru.
  • Cluster 3 digambarkan oleh dendrogram berwarna abu-abu.
  • Cluster 4 digambarkan oleh dendrogram berwarna kuning.
Average Linkage
us_hc_avg <- hclust(d = us_dist, method = "average")

fviz_dend(us_hc_avg, cex = 0.5, main = "Cluster Dendrogram Average Linkage")

avg_coph <- cophenetic(us_hc_avg)
cor(avg_coph, us_dist)
## [1] 0.7180382
avg_clust <- cutree(us_hc_avg, k = 4)

Berikut ini banyak anggota dari masing-masing cluster yang terbentuk.

table(avg_clust)
## avg_clust
##  1  2  3  4 
##  7  1 12 30

Anggota cluster paling banyak diperoleh pada cluster 4 sebanyak 30 cluster. Namun terdapat cluster yang hanya memiliki 1 anggota saja yaitu pada cluster 2. Hal ini dapat dijadikan indikasi bahwa observasi pada cluster 2 yaitu “Alaska” merupakan anomali data.

fviz_dend(us_hc_avg, k = 4, k_colors = "jco", rect = T, 
          main = "Average Linkage Cluster")

Cara membaca cluster pada dendrogram yaitu sebagai berikut :

  • Cluster 1 digambarkan oleh dendrogram berwarna abu-abu.
  • Cluster 2 digambarkan oleh dendrogram berwarna kuning.
  • Cluster 3 digambarkan oleh dendrogram berwarna merah.
  • Cluster 4 digambarkan oleh dendrogram berwarna biru.
Centroid Linkage
us_hc_centroid <- hclust(d = us_dist, method = "centroid")

fviz_dend(us_hc_centroid, cex = 0.5, main = "Cluster Dendrogram Centroid Linkage")

centroid_coph <- cophenetic(us_hc_centroid)
cor(centroid_coph, us_dist)
## [1] 0.6074717
centroid_clust <- cutree(us_hc_centroid, k = 4)

Berikut ini banyak anggota dari masing-masing cluster yang terbentuk.

table(centroid_clust)
## centroid_clust
##  1  2  3  4 
## 47  1  1  1

Anggota cluster didominasi pada cluster 1 sebanyak 47 observasi, namun untuk cluster yang lain hanya memiliki 1 observasi saja. Berikut visualiasi cluster yang terbentuk untuk masing-masing dendrogramnya.

fviz_dend(us_hc_centroid, k = 4, k_colors = "jco", rect = T, 
          main = "Centroid Linkage Cluster")
## Warning in get_col(col, k): Length of color vector was shorter than the number
## of clusters - color vector was recycled

Hasil partisi cluster yang terbentuk tidak sesuai dengan yang seharusnya. Berikut deskripsi anggota cluster yang seharusnya:

  • Cluster 1 terdiri dari negara New Jersey hingga California.
  • Cluster 2 beranggotakan North California.
  • Cluster 3 beranggotakan Alaska.
  • Cluster 4 beranggotakan Vermont.
Dendogram Terpilih

Dari keempat dendrogram yang sudah dibuat, dapat memilih salah satu yang ingin digunakan. Pemilihannya dapat berdasarkan hasil validasi cluster tree menggunakan korelasi cophenetic distance dengan jarak original masing-masing observasi yang tadi telah dihitung:

data.frame(complete = cor(complete_coph, us_dist),
          single = cor(single_coph, us_dist),
          average = cor(avg_coph, us_dist),
          centroid = cor(centroid_coph, us_dist)) %>% 
  tidyr::pivot_longer(cols = colnames(.),names_to = "method", values_to = "correlation")

Dapat terlihat bahwa nilai korelasi yang dihasilkan average linkage paling tinggi dibanding metode lainnya, disusul oleh complete linkage. Mempertimbangkan pula dendrogram yang dihasilkan, dendrogram dari average linkage menghasilkan cluster outlier yang hanya berisi negara bagian Alaska, dan ditakutkan membuat hasil clustering tidak optimal. Oleh karena itu, akan dipilih metode complete linkage dengan pertimbangan hasil correlation yang tinggi dan menghasilkan dendrogram yang cukup mudah diinterpretasikan.

B. Partitional Clustering

Partitional Clustering adalah metode pengelompokan data di mana sekumpulan objek data dibagi menjadi beberapa cluster (kelompok) yang tidak saling tumpang-tindih (non-overlapping). Setiap data hanya butuh berada di satu cluster saja, sehingga tidak ada data yang lebih dari satu cluster.

Tujuan partitional clustering adalah membentuk cluster yang anggotanya memiliki kemiripan tinggi di dalam cluster yang sama dan memiliki perbedaan yang cukup besar dengan cluster lain. Pada metode ini, jumlah cluster biasanya harus ditentukan terlebih dahulu sebelum proses clustering dilakukan.

Beberapa metode yang termasuk ke dalam partitional clustering antara lain K-Means, K-Medoids, dan Fuzzy C-Means.

Partitional Metode

1. K‑Means

Definisi Singkat

K-Means adalah metode clustering yang membagi data menjadi \(k\) cluster, di mana setiap observasi dimasukkan ke cluster dengan centroid terdekat. Centroid merupakan nilai rata-rata dari seluruh data pada cluster tersebut.

Formula:

\[ J = \sum_{i=1}^{k} \sum_{x_j \in C_i} \| x_j - \mu_i \|^2 \]

dengan:

  • \(k\) = jumlah cluster
  • \(C_i\) = cluster ke-\(i\)
  • \(x_j\) = data ke-\(j\)
  • \(\mu_i\) = centroid (mean) dari cluster ke-\(i\)

Cara Kerja & Hyperparameter

Langkah-langkah K-Means adalah sebagai berikut:

  1. Tentukan jumlah cluster \(k\).
  2. Pilih centroid awal.
  3. Hitung jarak setiap data ke masing-masing centroid.
  4. Tempatkan setiap data ke cluster dengan centroid terdekat.
  5. Hitung ulang centroid berdasarkan rata-rata anggota cluster.
  6. Ulangi langkah tersebut sampai hasil cluster stabil.

Hyperparameter utama:

  • jumlah cluster \(k\)
  • inisialisasi centroid
  • jumlah iterasi maksimum

Kelebihan & Keterbatasan

Kelebihan Keterbatasan
Sederhana & mudah diimplementasikan.
Cepat untuk dataset besar.
Cocok bila cluster relatif kompak / berbentuk bulat.
Mudah dipahami.
Jumlah cluster harus ditentukan di awal.
Sensitif terhadap inisialisasi centroid.
Sensitif terhadap outlier.
Kurang cocok untuk bentuk cluster kompleks.

Hasil & Visualisasi

library(ggplot2)

# Ambil variabel numerik dari dataset iris
df <- iris[, 1:4]

# K-Means clustering
set.seed(123)
k <- 3
km_res <- kmeans(df, centers = k, nstart = 25)

# Tambahkan hasil cluster
iris_km <- iris
iris_km$Cluster <- factor(km_res$cluster)

# Jumlah anggota tiap cluster
table(iris_km$Cluster)
## 
##  1  2  3 
## 50 62 38
# Centroid tiap cluster
km_res$centers
##   Sepal.Length Sepal.Width Petal.Length Petal.Width
## 1     5.006000    3.428000     1.462000    0.246000
## 2     5.901613    2.748387     4.393548    1.433871
## 3     6.850000    3.073684     5.742105    2.071053
# Cluster untuk 10 data pertama
head(iris_km$Cluster, 10)
##  [1] 1 1 1 1 1 1 1 1 1 1
## Levels: 1 2 3
# Visualisasi
ggplot(iris_km, aes(x = Sepal.Length, y = Sepal.Width, color = Cluster)) +
  geom_point(size = 3) +
  theme_minimal() +
  labs(
    title = "K-Means Clustering pada Dataset Iris",
    x = "Sepal Length",
    y = "Sepal Width"
  )

Interpretasi

Hasil K-Means membagi data iris ke dalam tiga cluster. Masing-masing cluster menunjukkan kelompok bunga dengan karakteristik ukuran sepal dan petal yang berbeda. Visualisasi memperlihatkan bahwa sebagian kelompok dapat dipisahkan dengan cukup jelas, meskipun masih terdapat beberapa titik yang berdekatan antar cluster.

2. K‑Medoids

Definisi Singkat

K-Medoids adalah metode clustering yang mirip dengan K-Means, tetapi pusat cluster yang digunakan adalah medoid, yaitu salah satu data asli dalam cluster. Karena menggunakan data asli sebagai pusat cluster, K-Medoids cenderung lebih tahan terhadap outlier.

Formula:

Tujuan K-Medoids adalah meminimalkan total jarak antara setiap data dengan medoid cluster-nya:

\[ J = \sum_{i=1}^{k} \sum_{x_j \in C_i} d(x_j, m_i) \]

dengan:

  • \(m_i\) = medoid cluster ke‑\(i\)

  • \(d(x_j, m_i)\) = jarak antara data ke-\(j\) dan medoid cluster ke-\(i\)

Cara Kerja & Hyperparameter

Langkah-langkah K-Medoids:

  1. Tentukan jumlah cluster \(k\).
  2. Pilih \(k\) data sebagai medoid awal.
  3. Hitung jarak setiap data ke medoid.
  4. Tempatkan data ke cluster dengan medoid terdekat.
  5. Evaluasi apakah ada data lain yang lebih baik menjadi medoid.
  6. Ulangi sampai medoid tidak berubah lagi.

Hyperparameter Utama:

  • jumlah cluster (\(k\))
  • pemilihan medoid awal
  • metrik jarak

Kelebihan & Keterbatasan

Kelebihan Keterbatasan
Lebih robust terhadap outlier.
Medoid adalah data nyata sehingga mudah diinterpretasi.
Dapat memakai berbagai ukuran jarak.
Lebih lambat dibanding K-Means.
Kurang efisien untuk dataset besar.
Sensitif terhadap medoid awal.

Hasil & Visualisasi

library(cluster)
library(ggplot2)

# Ambil variabel numerik
df <- iris[, 1:4]

# Standarisasi data
df_scaled <- scale(df)

# K-Medoids dengan PAM
set.seed(123)
pam_res <- pam(df_scaled, k = 3)

# Tambahkan hasil cluster
iris_pam <- iris
iris_pam$Cluster <- factor(pam_res$clustering)

# Jumlah anggota tiap cluster
table(iris_pam$Cluster)
## 
##  1  2  3 
## 50 45 55
# Medoid tiap cluster
pam_res$medoids
##      Sepal.Length Sepal.Width Petal.Length Petal.Width
## [1,]   -1.0184372   0.7861738   -1.2791040  -1.3110521
## [2,]    1.1553023  -0.1315388    0.9868021   1.1816087
## [3,]   -0.1730941  -0.5903951    0.4203256   0.1320673
# Silhouette width rata-rata
pam_res$silinfo$avg.width
## [1] 0.4566432
# Visualisasi
ggplot(iris_pam, aes(x = Petal.Length, y = Petal.Width, color = Cluster)) +
  geom_point(size = 3) +
  theme_minimal() +
  labs(
    title = "K-Medoids Clustering pada Dataset Iris",
    x = "Petal Length",
    y = "Petal Width"
  )

Interpretasi

Hasil K-Medoids menunjukkan pembagian data ke dalam tiga cluster berdasarkan kedekatan terhadap medoid. Karena pusat cluster berupa data nyata, hasil clustering ini lebih stabil terhadap nilai ekstrem dibandingkan K-Means. Pada dataset iris, pemisahan cluster umumnya terlihat cukup baik, terutama jika menggunakan variabel petal.

3. Fuzzy C‑Means (FCM)

Definisi Singkat

Fuzzy C-Means adalah metode clustering di mana setiap data tidak harus menjadi anggota mutlak satu cluster saja, tetapi dapat memiliki derajat keanggotaan pada beberapa cluster sekaligus. Metode ini cocok digunakan ketika batas antar cluster tidak terlalu tegas.

Formula:

Tujuan Fuzzy C-Means adalah meminimalkan fungsi objektif berikut:

\[ J_m = \sum_{i=1}^{c} \sum_{j=1}^{n} u_{ij}^m \; \| x_j - c_i \|^2 \]

dengan:

  • \(u_{ij}\) = derajat keanggotaan data ke-\(j\) pada cluster ke-\(i\)
  • \(c_i\) = centroid cluster ke-\(i\)
  • \(m\) - parameter fuzziness
  • \(n\) = jumlah data
  • \(c\) = jumlah cluster

Cara Kerja & Hyperparameter

Langkah-langkah Fuzzy C-Means:

  1. Tentukan jumlah cluster \(c\).
  2. Tentukan parameter fuzziness \(m\).
  3. Inisialisasi derajat keanggotaan setiap data.
  4. Hitung centroid cluster berdasarkan bobot keanggotaan.
  5. Perbarui nilai keanggotaan berdasarkan jarak ke centroid.
  6. Ulangi sampai konvergen.

Hyperparameter Utama:

  • jumlah cluster
  • parameter fuzziness (\(m\))
  • jumlah iterasi maksimum

Kelebihan & Keterbatasan

Kelebihan Keterbatasan
Data dapat memiliki keanggotaan pada lebih dari satu cluster.
Cocok untuk batas cluster yang tidak tegas.
Memberikan informasi derajat keanggotaan.
Interpretasi lebih rumit.
Lebih lambat dibanding hard clustering.
Sensitif terhadap inisialisasi dan parameter \(m\).

Hasil & Visualisasi

library(e1071)
library(ggplot2)

# Ambil variabel numerik
df <- iris[, 1:4]

# Standarisasi data
df_scaled <- scale(df)

# Fuzzy C-Means
set.seed(123)
fcm_res <- cmeans(df_scaled, centers = 3, m = 2, iter.max = 200, dist = "euclidean")

# Hard cluster dari membership terbesar
hard_cluster <- fcm_res$cluster

# Tambahkan hasil cluster
iris_fcm <- iris
iris_fcm$Cluster <- factor(hard_cluster)

# Derajat keanggotaan terbesar
iris_fcm$MaxMembership <- apply(fcm_res$membership, 1, max)

# Tampilkan hasil
head(iris_fcm$Cluster, 10)
##  [1] 2 2 2 2 2 2 2 2 2 2
## Levels: 1 2 3
head(round(fcm_res$membership, 3), 5)
##          1     2     3
## [1,] 0.005 0.992 0.003
## [2,] 0.119 0.829 0.052
## [3,] 0.047 0.929 0.023
## [4,] 0.089 0.870 0.041
## [5,] 0.016 0.974 0.010
round(fcm_res$centers, 3)
##   Sepal.Length Sepal.Width Petal.Length Petal.Width
## 1       -0.038      -0.816        0.322       0.231
## 2       -1.001       0.844       -1.280      -1.235
## 3        1.066       0.037        0.967       1.026
# Visualisasi
ggplot(iris_fcm, aes(
  x = Petal.Length,
  y = Petal.Width,
  color = Cluster,
  size = MaxMembership
)) +
  geom_point(alpha = 0.8) +
  scale_size(range = c(2, 7)) +
  theme_minimal() +
  labs(
    title = "Fuzzy C-Means pada Dataset Iris",
    subtitle = "Ukuran titik menunjukkan derajat keanggotaan terbesar",
    x = "Petal Length",
    y = "Petal Width"
  )

Interpretasi

Hasil Fuzzy C-Means menunjukkan bahwa setiap data memiliki derajat keanggotaan terhadap setiap cluster. Semakin besar nilai membership pada suatu cluster, semakin kuat data tersebut termasuk ke dalam cluster tersebut. Pada visualisasi, ukuran titik menunjukkan tingkat keanggotaan dominan, sehingga data dengan keanggotaan yang lebih tegas akan tampak lebih besar.

LS0tDQp0aXRsZTogIkhpZXJhcmNoaWNhbCBWcyBQYXJ0aXRpb24gQ2x1c3RlcmluZyINCmF1dGhvcjogIlJhZmx5IFByaXlhbnRhbWEgUmFtYWRoYW4gQmFnYXNrYXJhIg0KZGF0ZTogImByIFN5cy5EYXRlKClgIg0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIHRvYzogdHJ1ZQ0KICAgIHRvY19mbG9hdDogdHJ1ZQ0KICAgIGRmX3ByaW50OiAicGFnZWQiDQogICAgY29kZV9mb2xkaW5nOiAiaGlkZSINCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMNCiAgICBjc3M6IHN0eWxlcy5jc3MNCi0tLQ0KDQojICoqQ2x1c3RlcmluZyoqDQoNCkNsdXN0ZXJpbmcgbWVydXBha2FuIHNhbGFoIHNhdHUgbWV0b2RlIFVuc3VwZXJ2aXNlZCBMZWFybmluZyB5YW5nIGJlcnR1anVhbiB1bnR1ayBtZWxha3VrYW4gcGVuZ2Vsb21wb2thbiBkYXRhIGJlcmRhc2Fya2FuIGtlbWlyaXBhbi9qYXJhayBhbnRhciBkYXRhLiBDbHVzdGVyaW5nIG1lbWlsaWtpIGthcmFrdGVyaXN0aWsgZGltYW5hIGFuZ2dvdGEgZGFsYW0gc2F0dSBjbHVzdGVyIG1lbWlsaWtpIGtlbWlyaXBhbiB5YW5nIHNhbWEgYXRhdSBqYXJhayB5YW5nIGRla2F0LCBzZW1lbnRhcmEgYW5nZ290YSBhbnRhciBjbHVzdGVyIG1lbWlsaWtpIGtlbWlyaXBhbiB5YW5nIHNhbmdhdCBiZXJiZWRhIGF0YXUgamFyYWsgeWFuZyBzYW5nYXQgamF1aCAoVGFuIGV0IGFsLiwgMjAwNikuIE1ldG9kZSBjbHVzdGVyaW5nIGRpYmFnaSBtZW5qYWRpIGR1YSBqZW5pcywgeWFpdHUgKipIaWVyYXJjaGljYWwgQ2x1c3RlcmluZyoqIGRhbiAqKlBhcnRpdGlvbmFsIENsdXN0ZXJpbmcqKi4NCg0KIyMgKipBLiBIaWVyYXJjaGljYWwgQ2x1c3RlcmluZyoqDQoNCioqSGllcmFyY2hpY2FsIENsdXN0ZXJpbmcqKiBhZGFsYWggbWV0b2RlIHBlbmdlbG9tcG9rYW4gZGF0YSB5YW5nIGRpbGFrdWthbiBkZW5nYW4gbWVtYnVhdCBzdWF0dSBiYWdhbiBoaXJhcmtpICgqKmRlbmRyb2dyYW0qKikgZGVuZ2FuIHR1anVhbiBtZW51bmp1a2thbiBrZW1pcmlwYW4gYW50YXIgZGF0YS4NCg0KIyMjICoqQXBwcm9hY2gqKg0KDQpTZWNhcmEgdW11bSwgKmhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nKiBkaWJhZ2kgbWVuamFkaSBkdWEgamVuaXMgeWFpdHUgKipBZ2dsb21lcmF0aXZlKiogZGFuICoqRGl2aXNpdmUqKi4gS2VkdWEgbWV0b2RlIGluaSBkaWJlZGFrYW4gYmVyZGFzYXJrYW4gcGVuZGVrYXRhbiBkYWxhbSBtZWxha3VrYW4gcGVuZ2Vsb21wb2trYW4gZGF0YSBoaW5nZ2EgbWVtYmVudHVrIGRlbmRyb2dyYW0sIG1lbmdndW5ha2FuICpib3R0b20tdXAqIGF0YXUgKnRvcC1kb3duIG1hbm5lciouDQoNCiMjIyMgKioxLiBBZ2dsb21lcmF0aXZlIENsdXN0ZXJpbmcqKg0KDQoqQWdnbG9tZXJhdGl2ZSBDbHVzdGVyaW5nKiBiaWFzYSBkaXNlYnV0IGp1Z2Egc2ViYWdhaSBhZ2dsb21lcmF0aXZlIG5lc3RpbmcgKCoqQUdORVMqKikgZGltYW5hIGNhcmEga2VyamEgZGFsYW0gbWVsYWt1a2FuIHBlbmdlbG9tcG9rYW4gZGF0YSBtZW5nZ3VuYWthbiAqKmJvdHRvbS11cCBtYW5uZXIqKi4gUHJvc2VzbnlhIGRpbXVsYWkgZGVuZ2FuIG1lbmdhbmdnYXAgc2V0aWFwIGRhdGEgc2ViYWdhaSBzYXR1IGNsdXN0ZXIga2VjaWwgKCpsZWFmKikgeWFuZyBoYW55YSBtZW1pbGlraSBzYXR1IGFuZ2dvdGEgc2FqYSwgbGFsdSBwYWRhIHRhaGFwIHNlbGFuanV0bnlhIGR1YSBjbHVzdGVyIHlhbmcgbWVtaWxpa2kga2VtaXJpcGFuIGFrYW4gZGlrZWxvbXBva2thbiBtZW5qYWRpIHNhdHUgY2x1c3RlciB5YW5nIGxlYmloIGJlc2FyICgqbm9kZXMqKSBkYW4gcHJvc2VzIGluaSBha2FuIGRpbGFrdWthbiB0ZXJ1cyBtZW5lcnVzIGhpbmdnYSBzZW11YSBkYXRhIG1lbmphZGkgc2F0dSBjbHVzdGVyIGJlc2FyICgqcm9vdCopLg0KDQojIyMjICoqMi4gRGl2aXNpdmUgSGllcmFyY2hpY2FsIENsdXN0ZXJpbmcqKg0KDQoqRGl2aXNpdmUgSGllcmFyY2hpY2FsIENsdXN0ZXJpbmcqIGJpYXNhIGRpc2VidXQganVnYSBzZWJhZ2FpIGRpdmlzaXZlIGFuYWx5c2lzICgqKkRJQU5BKiopIGRpIG1hbmEgY2FyYSBrZXJqYSBkYWxhbSBtZWxha3VrYW4gcGVuZ2Vsb21wb2thbiBkYXRhIG1lbmdndW5ha2FuICoqdG9wLWRvd24gbWFubmVyKiouIFByb3Nlc255YSBkaW11bGFpIGRlbmdhbiBtZW5nYW5nZ2FwIHNhdHUgc2V0IGRhdGEgc2ViYWdhaSBzYXR1IGNsdXN0ZXIgYmVzYXIgKCpyb290KiksIGxhbHUgZGFsYW0gc2V0aWFwIGl0ZXJhc2lueWEgc2V0aWFwIGRhdGEgeWFuZyBtZW1pbGlraSBrYXJha3RlcmlzdGlrIHlhbmcgYmVyYmVkYSBha2FuIGRpcGVjYWggbWVuamFkaSBkdWEgY2x1c3RlciB5YW5nIGxlYmloIGtlY2lsICgqbm9kZXMqKSBkYW4gcHJvc2VzIGFrYW4gdGVydXMgYmVyamFsYW4gaGluZ2dhIHNldGlhcCBkYXRhIG1lbmphZGkgc2F0dSBjbHVzdGVyIGtlY2lsICgqbGVhZiopIHlhbmcgaGFueWEgbWVtaWxpa2kgc2F0dSBhbmdnb3RhIHNhamEuDQoNCkJlcmlrdXQgYWRhbGFoIGlsdXN0cmFzaSBtZW5nZW5haSBiYWdhaW1hbmEgQWdnbG9tZXJhdGl2ZSBkYW4gRGlzaXZlIENsdXN0ZXJpbmcgYmVrZXJqYQ0KDQpgYGB7cn0NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJkb3dubG9hZC5wbmciKQ0KYGBgDQoNCiMjIyAqKkxpbmthZ2UgTWV0aG9kKioNCg0KRGFsYW0gKmhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nKiwgc2VsYWluIG1lbmdoaXR1bmcgamFyYWsgYW50YXIgZGF0YSwgZGlwZXJsdWthbiBqdWdhIGNhcmEgdW50dWsgbWVuZ2hpdHVuZyBqYXJhayBhbnRhciBjbHVzdGVyIHNlaGluZ2dhIGRhcGF0IHRlcmJlbnR1ayBkZW5kcm9ncmFtIGRhcmkgY2x1c3Rlci1jbHVzdGVyIHlhbmcgbWVtaWxpa2kga2VkZWthdGFuLiBQcm9zZXMgcGVuZ2dhYnVuZ2FuIGNsdXN0ZXItY2x1c3RlciBrZWNpbCBtZW5qYWRpIHNhdHUgZGVuZHJvZ3JhbSB1dHVoIGRpbGFrdWthbiBtZWxhbHVpIGJlYmVyYXBhIHBlbmRla2F0YW4geWFuZyBkaXNlYnV0ICoqTGlua2FnZSBNZXRob2QqKi4NCg0KQmVyaWt1dCBpbmkgYmViZXJhcGEgbGlua2FnZSBtZXRob2QgeWFuZyBzZXJpbmcgZGlndW5ha2FuIHBhZGEgYWdnbG9tZXJhdGl2ZSBhcHByb2FjaDoNCg0KMS4gIENvbXBsZXRlIExpbmthZ2UvTWF4aW11bSBMaW5rYWdlDQoyLiAgU2luZ2xlIExpbmthZ2UvTWluaW11bSBMaW5rYWdlDQozLiAgQXZlcmFnZSBMaW5rYWdlDQo0LiAgQ2VudHJvaWQgTGlua2FnZQ0KDQojIyMjICoqMS4gQ29tcGxldGUvTWF4aW11bSBMaW5rYWdlKioNCg0KUGFkYSBtZXRvZGUgKipjb21wbGV0ZSBsaW5rYWdlKiosIGphcmFrIGFudGFyIGNsdXN0ZXIgZGl0ZW50dWthbiBiZXJkYXNhcmthbiBqYXJhayB0ZXJiZXNhciAobWF4aW11bSBkaXN0YW5jZSkgYW50YXJhIHBhc2FuZ2FuIGRhdGEgZGFyaSBkdWEgY2x1c3RlciB5YW5nIGJlcmJlZGEuIEFydGlueWEsIHNlbXVhIHBhc2FuZ2FuIGRhdGEgYW50YXIgY2x1c3RlciBkaWhpdHVuZyBqYXJha255YSB0ZXJsZWJpaCBkYWh1bHUsIGtlbXVkaWFuIGRpcGlsaWggamFyYWsgeWFuZyBwYWxpbmcgYmVzYXIgc2ViYWdhaSBqYXJhayBhbnRhciBjbHVzdGVyLg0KDQpNZXRvZGUgaW5pIGJpYXNhbnlhIG1lbmdoYXNpbGthbiBjbHVzdGVyIHlhbmcgbGViaWggdGVycGlzYWggZGFuIGxlYmloIGtvbXBhayAoY29tcGFjdCkuDQoNCioqRm9ybXVsYToqKg0KDQokJGRfezEyfSA9IFxtYXhfe2ksan0gZChYX2ksIFlfaikkJA0KDQpLZXRlcmFuZ2FuOg0KDQotICAgJFhfMSxYXzIsIC4uLiwgWF9rJCA6IG9ic2VydmFzaSBwYWRhIGNsdXN0ZXIgMQ0KDQo8IS0tIC0tPg0KDQotICAgJFlfMSxZXzIsIC4uLiwgWV9rJCA6IG9ic2VydmFzaSBwYWRhIGNsdXN0ZXIgMg0KDQotICAgJGQoWF9pLFlfaikkIDogamFyYWsgYW50YXJhIGRhdGEgcGFkYSBjbHVzdGVyIDEgZGFuIGNsdXN0ZXIgMg0KDQojIyMjICoqMi4gU2luZ2xlL01pbmltdW0gTGlua2FnZSoqDQoNClBhZGEgbWV0b2RlICoqc2luZ2xlIGxpbmthZ2UqKiwgamFyYWsgYW50YXIgY2x1c3RlciBkaXRlbnR1a2FuIGJlcmRhc2Fya2FuIGphcmFrIHRlcmtlY2lsIChtaW5pbXVtIGRpc3RhbmNlKSBhbnRhcmEgcGFzYW5nYW4gZGF0YSBkYXJpIGR1YSBjbHVzdGVyIHlhbmcgYmVyYmVkYS4NCg0KTWV0b2RlIGluaSBjZW5kZXJ1bmcgbWVuZ2hhc2lsa2FuIGNsdXN0ZXIgeWFuZyBsZWJpaCBsb25nZ2FyIChsb29zZSkga2FyZW5hIHBlbmdnYWJ1bmdhbiBjbHVzdGVyIGhhbnlhIGJlcmdhbnR1bmcgcGFkYSBwYXNhbmdhbiBkYXRhIHlhbmcgbGViaWggZGVrYXQuDQoNCioqRm9ybXVsYToqKg0KDQokJGRfezEyfSA9IFxtaW5fe2ksan0gZChYX2ksIFlfaikkJA0KDQojIyMjICoqMy4gQXZlcmFnZSBMaW5rYWdlKioNCg0KUGFkYSBtZXRvZGUgKiphdmVyYWdlIGxpbmthZ2UqKiwgamFyYWsgYW50YXIgY2x1c3RlciBkaWhpdHVuZyBkZW5nYW4gbWVuZ2FtYmlsIHJhdGEtcmF0YSBqYXJhayBkYXJpIHNlbXVhIHBhc2FuZ2FuIGRhdGEgeWFuZyBiZXJhc2FsIGRhcmkgZHVhIGNsdXN0ZXIgeWFuZyBiZXJiZWRhLg0KDQpNZXRvZGUgaW5pIGJpYXNhbnlhIG1lbmdoYXNpbGthbiBjbHVzdGVyIHlhbmcgdGlkYWsgdGVybGFsdSByYXBhdCAoY29tcGFjdCkgbWF1cHVuIHRlcmxhbHUgbG9uZ2dhciAobG9vc2UpLg0KDQoqKkZvcm11bGE6KioNCg0KJCRkX3sxMn0gPSBcZnJhY3sxfXtrbH1cc3VtX3tpPTF9XntrfVxzdW1fe2o9MX1ee2x9IGQoWF9pLCBZX2opJCQNCg0KIyMjIyAqKjQuIENlbnRyb2lkIExpbmthZ2UqKg0KDQpQYWRhIG1ldG9kZSBjZW50cm9pZCBsaW5rYWdlLCBqYXJhayBhbnRhciBjbHVzdGVyIGRpaGl0dW5nIGJlcmRhc2Fya2FuIGphcmFrIGFudGFyYSBjZW50cm9pZCAodGl0aWsgcHVzYXQpIGRhcmkgbWFzaW5nLW1hc2luZyBjbHVzdGVyLiBDZW50cm9pZCBkaWhpdHVuZyBtZW5nZ3VuYWthbiByYXRhLXJhdGEgZGFyaSBkYXRhIHBhZGEgY2x1c3RlciB0ZXJzZWJ1dC4NCg0KQ2x1c3RlciB5YW5nIG1lbWlsaWtpIGphcmFrIGNlbnRyb2lkIHBhbGluZyBrZWNpbCBha2FuIGRpZ2FidW5na2FuIHRlcmxlYmloIGRhaHVsdSBkYWxhbSBwcm9zZXMgcGVtYmVudHVrYW4gZGVuZHJvZ3JhbS4NCg0KKipGb3JtdWxhOioqDQoNCiQkZF97MTJ9ID0gZChcYmFye1h9LCBcYmFye1l9KSQkDQoNCiMjIyAqKlN0dWR5IENhc2U6IGBVU0FycmVzdHNgIEFuYWx5c2lzKioNCg0KTWVuZ2FwbGlrYXNpa2FuIGNhcmEga2VyamEgaGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcgKipBR05FUyoqIHVudHVrIG1lbmdhbmFsaXNpcyBrZWRla2F0YW4gTmVnYXJhIEJhZ2lhbiBBUyBiZXJkYXNhcmthbiB0aW5na2F0IGtyaW1pbmFsaXRhc255YS4gQW5hbGlzaXMgZGlsYWt1a2FuIG1lbmdndW5ha2FuIGRhdGFzZXQgYFVTQXJyZXN0c2AgeWFuZyBtZW55aW1wYW4gaW5mb3JtYXNpIHN0YXRpc3RpayB0ZXJrYWl0IGp1bWxhaCBrcmltaW5hbGl0YXMgYGFzc2F1bHRgLGBtdXJkZXJgLCBkYW4gYHJhcGVgIHBlciAxMDAsMDAwIHBlbmR1ZHVrIHBhZGEgNTAgbmVnYXJhIGJhZ2lhbiBBbWVyaWthIFNlcmlrYXQgcGFkYSB0YWh1biAxOTczLg0KDQojIyMjICoqMS4gRGF0YXNldCoqDQoNCmBgYHtyfQ0KaGVhZChVU0FycmVzdHMpDQpgYGANCg0KIyMjIyAqKjIuIFByZXByb2Nlc3NpbmcgRGF0YSoqDQoNCmBgYHtyfQ0KIyBDZWsgbWlzc2luZyB2YWx1ZQ0KYW55TkEoVVNBcnJlc3RzKQ0KDQojIENlayBza2FsYSB0aWFwIHZhcmlhYmxlIGRhdGENCnN1bW1hcnkoVVNBcnJlc3RzKQ0KDQojIFN0YW5kYXJpc2FzaQ0KdXMgPC0gc2NhbGUoVVNBcnJlc3RzKQ0KaGVhZCh1cykNCmBgYA0KDQojIyMjICoqMy4gTWVuZ3VrdXIgSmFyYWsgQW50YXIgRGF0YSoqDQoNCmBgYHtyfQ0KIyBNZW5naGl0dW5nIGRpc3RhbmNlDQp1c19kaXN0IDwtIGRpc3QoeD11cyxtZXRob2QgPSAiZXVjbGlkZWFuIikNCmFzLm1hdHJpeCh1c19kaXN0KVsxOjYsMTo2XQ0KYGBgDQoNCiMjIyMgKio0LiBEZW5kcm9ncmFtKiogey50YWJzZXR9DQoNCmBgYHtyLG1lc3NhZ2U9RkFMU0V9DQpsaWJyYXJ5KGNsdXN0ZXIpDQpsaWJyYXJ5KGZhY3RvZXh0cmEpDQpsaWJyYXJ5KGRwbHlyKQ0KYGBgDQoNCiMjIyMjICoqQ29tcGxldGUgTGlua2FnZSoqDQoNCmBgYHtyLCB3YXJuaW5nPUZBTFNFfQ0KdXNfaGNfY29tcGxldGUgPC0gaGNsdXN0KHVzX2Rpc3QsIG1ldGhvZCA9ICJjb21wbGV0ZSIpDQoNCmZ2aXpfZGVuZCh1c19oY19jb21wbGV0ZSwNCiAgICAgICAgICBjZXggPSAwLjUsDQogICAgICAgICAgbWFpbiA9ICJDbHVzdGVyIERlbmRyb2dyYW0gQ29tcGxldGUgTGlua2FnZSIpDQoNCmNvbXBsZXRlX2NvcGggPC0gY29waGVuZXRpYyh1c19oY19jb21wbGV0ZSkNCmNvcihjb21wbGV0ZV9jb3BoLCB1c19kaXN0KQ0KDQpjb21wbGV0ZV9jbHVzdCA8LSBjdXRyZWUodXNfaGNfY29tcGxldGUsIGsgPSA0KQ0KYGBgDQoNCkJlcmlrdXQgaW5pIGJhbnlhayBhbmdnb3RhIGRhcmkgbWFzaW5nLW1hc2luZyBjbHVzdGVyIHlhbmcgdGVyYmVudHVrLg0KDQpgYGB7cn0NCnRhYmxlKGNvbXBsZXRlX2NsdXN0KQ0KYGBgDQoNCkFuZ2dvdGEgcGFkYSBtYXNpbmctbWFzaW5nIGNsdXN0ZXIgY3VrdXAgbWVyYXRhIGRlbmdhbiBjbHVzdGVyIHlhbmcgcGFsaW5nIGJhbnlhayBtZW1pbGlraSBhbmdnb3RhIGFkYWxhaCBjbHVzdGVyIDMuIEJlcmlrdXQgaGFzaWwgYWtoaXIgY2x1c3RlciB5YW5nIHRlcmJlbnR1ayBhcGFiaWxhIGRpbGloYXQgZGFyaSBkZW5kcm9ncmFtLg0KDQpgYGB7cn0NCmZ2aXpfZGVuZCh1c19oY19jb21wbGV0ZSwNCiAgICAgICAgICBrID0gNCwNCiAgICAgICAgICBjZXggPSAwLjUsDQogICAgICAgICAga19jb2xvcnMgPSAiamNvIiwNCiAgICAgICAgICByZWN0ID0gVFJVRSwNCiAgICAgICAgICBtYWluID0gIkNvbXBsZXRlIExpbmthZ2UgQ2x1c3RlciIpDQpgYGANCg0KQ2FyYSBtZW1iYWNhIGNsdXN0ZXIgcGFkYSBkZW5kcm9ncmFtIHNlYmFnYWkgYmVyaWt1dCA6DQoNCi0gICBDbHVzdGVyIDEgZGlnYW1iYXJrYW4gb2xlaCBkZW5kcm9ncmFtIGJlcndhcm5hIG1lcmFoLg0KLSAgIENsdXN0ZXIgMiBkaWdhbWJhcmthbiBvbGVoIGRlbmRyb2dyYW0gYmVyd2FybmEgYWJ1LWFidS4NCi0gICBDbHVzdGVyIDMgZGlnYW1iYXJrYW4gb2xlaCBkZW5kcm9ncmFtIGJlcndhcm5hIGt1bmluZy4NCi0gICBDbHVzdGVyIDQgZGlnYW1iYXJrYW4gb2xlaCBkZW5kcm9ncmFtIGJlcndhcm5hIGJpcnUuDQoNCiMjIyMjICoqU2luZ2xlIExpbmthZ2UqKg0KDQpgYGB7cn0NCnVzX2hjX3NpbmdsZSA8LSBoY2x1c3QoZCA9IHVzX2Rpc3QsIG1ldGhvZCA9ICJzaW5nbGUiKQ0KDQpmdml6X2RlbmQodXNfaGNfc2luZ2xlLCBjZXggPSAwLjUsIA0KICAgICAgICAgIG1haW4gPSAiQ2x1c3RlciBEZW5kcm9ncmFtIFNpbmdsZSBMaW5rYWdlIikNCg0Kc2luZ2xlX2NvcGggPC0gY29waGVuZXRpYyh1c19oY19zaW5nbGUpDQpjb3Ioc2luZ2xlX2NvcGgsIHVzX2Rpc3QpDQpgYGANCg0Ka29yZWxhc2kgeWFuZyBkaXBlcm9sZWggcmVuZGFoLCBhcnRpbnlhIGNsdXN0ZXIgeWFuZyB0ZXJiZW50dWsga3VyYW5nIG1lcmVwcmVzZW50YXNpa2FuIGtvbmRpc2kgZGF0YSB5YW5nIGFkYS4gU2VoaW5nZ2EgbWVuZ2d1bmFrYW4gc2luZ2xlIGxpbmthZ2UgdW50dWsgZGF0YSBVU0FycmVzdCBrdXJhbmcgcmVsZXZhbi4NCg0KYGBge3J9DQpzaW5nbGVfY2x1c3QgPC0gY3V0cmVlKHVzX2hjX3NpbmdsZSwgayA9IDQpDQpgYGANCg0KQmVyaWt1dCBpbmkgYmFueWFrIGFuZ2dvdGEgZGFyaSBtYXNpbmctbWFzaW5nIGNsdXN0ZXIgeWFuZyB0ZXJiZW50dWsuDQoNCmBgYHtyfQ0KdGFibGUoc2luZ2xlX2NsdXN0KQ0KYGBgDQoNCkFuZ2dvdGEgcGFkYSBjbHVzdGVyIHlhbmcgdGVyYmVudHVrIHNhbmdhdCBiZXJiZWRhIHNlY2FyYSBzaWduaWZpa2FuLiBDbHVzdGVyIDEgbWVtaWxpa2kgYW5nZ290YSB5YW5nIHBhbGluZyBiYW55YWsgaGFtcGlyIHNlYmFueWFrIHRvdGFsIG9ic2VydmFzaSBwYWRhIGRhdGEuIEhhbCBpbmkgeWFuZyBkaWthcmVuYWthbiB0ZXJsYWx1IHJhcGF0bnlhIGFudGFyIG9ic2VydmFzaSBzZWhpbmdnYSBzdWxpdCBkaXBpc2Foa2FuIHBlcmJlZGFhbm55YS4NCg0KYGBge3J9DQpmdml6X2RlbmQodXNfaGNfc2luZ2xlLCBrID0gNCwga19jb2xvcnMgPSAiamNvIiwgcmVjdCA9IFQsIA0KICAgICAgICAgIG1haW4gPSAiU2luZ2xlIExpbmthZ2UgQ2x1c3RlciIpDQpgYGANCg0KQ2FyYSBtZW1iYWNhIGNsdXN0ZXIgcGFkYSBkZW5kcm9ncmFtIHlhaXR1IHNlYmFnYWkgYmVyaWt1dCA6DQoNCi0gICBDbHVzdGVyIDEgZGlnYW1iYXJrYW4gb2xlaCBkZW5kcm9ncmFtIGJlcndhcm5hIG1lcmFoLg0KLSAgIENsdXN0ZXIgMiBkaWdhbWJhcmthbiBvbGVoIGRlbmRyb2dyYW0gYmVyd2FybmEgYmlydS4NCi0gICBDbHVzdGVyIDMgZGlnYW1iYXJrYW4gb2xlaCBkZW5kcm9ncmFtIGJlcndhcm5hIGFidS1hYnUuDQotICAgQ2x1c3RlciA0IGRpZ2FtYmFya2FuIG9sZWggZGVuZHJvZ3JhbSBiZXJ3YXJuYSBrdW5pbmcuDQoNCiMjIyMjICoqQXZlcmFnZSBMaW5rYWdlKioNCg0KYGBge3J9DQp1c19oY19hdmcgPC0gaGNsdXN0KGQgPSB1c19kaXN0LCBtZXRob2QgPSAiYXZlcmFnZSIpDQoNCmZ2aXpfZGVuZCh1c19oY19hdmcsIGNleCA9IDAuNSwgbWFpbiA9ICJDbHVzdGVyIERlbmRyb2dyYW0gQXZlcmFnZSBMaW5rYWdlIikNCg0KYXZnX2NvcGggPC0gY29waGVuZXRpYyh1c19oY19hdmcpDQpjb3IoYXZnX2NvcGgsIHVzX2Rpc3QpDQoNCmF2Z19jbHVzdCA8LSBjdXRyZWUodXNfaGNfYXZnLCBrID0gNCkNCmBgYA0KDQpCZXJpa3V0IGluaSBiYW55YWsgYW5nZ290YSBkYXJpIG1hc2luZy1tYXNpbmcgY2x1c3RlciB5YW5nIHRlcmJlbnR1ay4NCg0KYGBge3J9DQp0YWJsZShhdmdfY2x1c3QpDQpgYGANCg0KQW5nZ290YSBjbHVzdGVyIHBhbGluZyBiYW55YWsgZGlwZXJvbGVoIHBhZGEgY2x1c3RlciA0IHNlYmFueWFrIDMwIGNsdXN0ZXIuIE5hbXVuIHRlcmRhcGF0IGNsdXN0ZXIgeWFuZyBoYW55YSBtZW1pbGlraSAxIGFuZ2dvdGEgc2FqYSB5YWl0dSBwYWRhIGNsdXN0ZXIgMi4gSGFsIGluaSBkYXBhdCBkaWphZGlrYW4gaW5kaWthc2kgYmFod2Egb2JzZXJ2YXNpIHBhZGEgY2x1c3RlciAyIHlhaXR1IOKAnEFsYXNrYeKAnSBtZXJ1cGFrYW4gYW5vbWFsaSBkYXRhLg0KDQpgYGB7cn0NCmZ2aXpfZGVuZCh1c19oY19hdmcsIGsgPSA0LCBrX2NvbG9ycyA9ICJqY28iLCByZWN0ID0gVCwgDQogICAgICAgICAgbWFpbiA9ICJBdmVyYWdlIExpbmthZ2UgQ2x1c3RlciIpDQpgYGANCg0KQ2FyYSBtZW1iYWNhIGNsdXN0ZXIgcGFkYSBkZW5kcm9ncmFtIHlhaXR1IHNlYmFnYWkgYmVyaWt1dCA6DQoNCi0gICBDbHVzdGVyIDEgZGlnYW1iYXJrYW4gb2xlaCBkZW5kcm9ncmFtIGJlcndhcm5hIGFidS1hYnUuDQotICAgQ2x1c3RlciAyIGRpZ2FtYmFya2FuIG9sZWggZGVuZHJvZ3JhbSBiZXJ3YXJuYSBrdW5pbmcuDQotICAgQ2x1c3RlciAzIGRpZ2FtYmFya2FuIG9sZWggZGVuZHJvZ3JhbSBiZXJ3YXJuYSBtZXJhaC4NCi0gICBDbHVzdGVyIDQgZGlnYW1iYXJrYW4gb2xlaCBkZW5kcm9ncmFtIGJlcndhcm5hIGJpcnUuDQoNCiMjIyMjICoqQ2VudHJvaWQgTGlua2FnZSoqDQoNCmBgYHtyfQ0KdXNfaGNfY2VudHJvaWQgPC0gaGNsdXN0KGQgPSB1c19kaXN0LCBtZXRob2QgPSAiY2VudHJvaWQiKQ0KDQpmdml6X2RlbmQodXNfaGNfY2VudHJvaWQsIGNleCA9IDAuNSwgbWFpbiA9ICJDbHVzdGVyIERlbmRyb2dyYW0gQ2VudHJvaWQgTGlua2FnZSIpDQoNCmNlbnRyb2lkX2NvcGggPC0gY29waGVuZXRpYyh1c19oY19jZW50cm9pZCkNCmNvcihjZW50cm9pZF9jb3BoLCB1c19kaXN0KQ0KDQpjZW50cm9pZF9jbHVzdCA8LSBjdXRyZWUodXNfaGNfY2VudHJvaWQsIGsgPSA0KQ0KYGBgDQoNCkJlcmlrdXQgaW5pIGJhbnlhayBhbmdnb3RhIGRhcmkgbWFzaW5nLW1hc2luZyBjbHVzdGVyIHlhbmcgdGVyYmVudHVrLg0KDQpgYGB7cn0NCnRhYmxlKGNlbnRyb2lkX2NsdXN0KQ0KYGBgDQoNCkFuZ2dvdGEgY2x1c3RlciBkaWRvbWluYXNpIHBhZGEgY2x1c3RlciAxIHNlYmFueWFrIDQ3IG9ic2VydmFzaSwgbmFtdW4gdW50dWsgY2x1c3RlciB5YW5nIGxhaW4gaGFueWEgbWVtaWxpa2kgMSBvYnNlcnZhc2kgc2FqYS4gQmVyaWt1dCB2aXN1YWxpYXNpIGNsdXN0ZXIgeWFuZyB0ZXJiZW50dWsgdW50dWsgbWFzaW5nLW1hc2luZyBkZW5kcm9ncmFtbnlhLg0KDQpgYGB7cn0NCmZ2aXpfZGVuZCh1c19oY19jZW50cm9pZCwgayA9IDQsIGtfY29sb3JzID0gImpjbyIsIHJlY3QgPSBULCANCiAgICAgICAgICBtYWluID0gIkNlbnRyb2lkIExpbmthZ2UgQ2x1c3RlciIpDQpgYGANCg0KSGFzaWwgcGFydGlzaSBjbHVzdGVyIHlhbmcgdGVyYmVudHVrIHRpZGFrIHNlc3VhaSBkZW5nYW4geWFuZyBzZWhhcnVzbnlhLiBCZXJpa3V0IGRlc2tyaXBzaSBhbmdnb3RhIGNsdXN0ZXIgeWFuZyBzZWhhcnVzbnlhOg0KDQotICAgQ2x1c3RlciAxIHRlcmRpcmkgZGFyaSBuZWdhcmEgTmV3IEplcnNleSBoaW5nZ2EgQ2FsaWZvcm5pYS4NCi0gICBDbHVzdGVyIDIgYmVyYW5nZ290YWthbiBOb3J0aCBDYWxpZm9ybmlhLg0KLSAgIENsdXN0ZXIgMyBiZXJhbmdnb3Rha2FuIEFsYXNrYS4NCi0gICBDbHVzdGVyIDQgYmVyYW5nZ290YWthbiBWZXJtb250Lg0KDQojIyMjIyAqKkRlbmRvZ3JhbSBUZXJwaWxpaCoqDQoNCkRhcmkga2VlbXBhdCBkZW5kcm9ncmFtIHlhbmcgc3VkYWggZGlidWF0LCBkYXBhdCBtZW1pbGloIHNhbGFoIHNhdHUgeWFuZyBpbmdpbiBkaWd1bmFrYW4uIFBlbWlsaWhhbm55YSBkYXBhdCBiZXJkYXNhcmthbiBoYXNpbCB2YWxpZGFzaSBjbHVzdGVyIHRyZWUgbWVuZ2d1bmFrYW4ga29yZWxhc2kgY29waGVuZXRpYyBkaXN0YW5jZSBkZW5nYW4gamFyYWsgb3JpZ2luYWwgbWFzaW5nLW1hc2luZyBvYnNlcnZhc2kgeWFuZyB0YWRpIHRlbGFoIGRpaGl0dW5nOg0KDQpgYGB7cn0NCmRhdGEuZnJhbWUoY29tcGxldGUgPSBjb3IoY29tcGxldGVfY29waCwgdXNfZGlzdCksDQogICAgICAgICAgc2luZ2xlID0gY29yKHNpbmdsZV9jb3BoLCB1c19kaXN0KSwNCiAgICAgICAgICBhdmVyYWdlID0gY29yKGF2Z19jb3BoLCB1c19kaXN0KSwNCiAgICAgICAgICBjZW50cm9pZCA9IGNvcihjZW50cm9pZF9jb3BoLCB1c19kaXN0KSkgJT4lIA0KICB0aWR5cjo6cGl2b3RfbG9uZ2VyKGNvbHMgPSBjb2xuYW1lcyguKSxuYW1lc190byA9ICJtZXRob2QiLCB2YWx1ZXNfdG8gPSAiY29ycmVsYXRpb24iKQ0KYGBgDQoNCkRhcGF0IHRlcmxpaGF0IGJhaHdhIG5pbGFpIGtvcmVsYXNpIHlhbmcgZGloYXNpbGthbiBhdmVyYWdlIGxpbmthZ2UgcGFsaW5nIHRpbmdnaSBkaWJhbmRpbmcgbWV0b2RlIGxhaW5ueWEsIGRpc3VzdWwgb2xlaCBjb21wbGV0ZSBsaW5rYWdlLiBNZW1wZXJ0aW1iYW5na2FuIHB1bGEgZGVuZHJvZ3JhbSB5YW5nIGRpaGFzaWxrYW4sIGRlbmRyb2dyYW0gZGFyaSBhdmVyYWdlIGxpbmthZ2UgbWVuZ2hhc2lsa2FuIGNsdXN0ZXIgb3V0bGllciB5YW5nIGhhbnlhIGJlcmlzaSBuZWdhcmEgYmFnaWFuIEFsYXNrYSwgZGFuIGRpdGFrdXRrYW4gbWVtYnVhdCBoYXNpbCBjbHVzdGVyaW5nIHRpZGFrIG9wdGltYWwuIE9sZWgga2FyZW5hIGl0dSwgYWthbiBkaXBpbGloIG1ldG9kZSBjb21wbGV0ZSBsaW5rYWdlIGRlbmdhbiBwZXJ0aW1iYW5nYW4gaGFzaWwgY29ycmVsYXRpb24geWFuZyB0aW5nZ2kgZGFuIG1lbmdoYXNpbGthbiBkZW5kcm9ncmFtIHlhbmcgY3VrdXAgbXVkYWggZGlpbnRlcnByZXRhc2lrYW4uDQoNCiMjICoqQi4gUGFydGl0aW9uYWwgQ2x1c3RlcmluZyoqDQoNCioqUGFydGl0aW9uYWwgQ2x1c3RlcmluZyoqIGFkYWxhaCBtZXRvZGUgcGVuZ2Vsb21wb2thbiBkYXRhIGRpIG1hbmEgc2VrdW1wdWxhbiBvYmplayBkYXRhIGRpYmFnaSBtZW5qYWRpIGJlYmVyYXBhIGNsdXN0ZXIgKGtlbG9tcG9rKSB5YW5nIHRpZGFrIHNhbGluZyB0dW1wYW5nLXRpbmRpaCAoKm5vbi1vdmVybGFwcGluZyopLiBTZXRpYXAgZGF0YSBoYW55YSBidXR1aCBiZXJhZGEgZGkgc2F0dSBjbHVzdGVyIHNhamEsIHNlaGluZ2dhIHRpZGFrIGFkYSBkYXRhIHlhbmcgbGViaWggZGFyaSBzYXR1IGNsdXN0ZXIuDQoNClR1anVhbiBwYXJ0aXRpb25hbCBjbHVzdGVyaW5nIGFkYWxhaCBtZW1iZW50dWsgY2x1c3RlciB5YW5nIGFuZ2dvdGFueWEgbWVtaWxpa2kga2VtaXJpcGFuIHRpbmdnaSBkaSBkYWxhbSBjbHVzdGVyIHlhbmcgc2FtYSBkYW4gbWVtaWxpa2kgcGVyYmVkYWFuIHlhbmcgY3VrdXAgYmVzYXIgZGVuZ2FuIGNsdXN0ZXIgbGFpbi4gUGFkYSBtZXRvZGUgaW5pLCBqdW1sYWggY2x1c3RlciBiaWFzYW55YSBoYXJ1cyBkaXRlbnR1a2FuIHRlcmxlYmloIGRhaHVsdSBzZWJlbHVtIHByb3NlcyBjbHVzdGVyaW5nIGRpbGFrdWthbi4NCg0KQmViZXJhcGEgbWV0b2RlIHlhbmcgdGVybWFzdWsga2UgZGFsYW0gcGFydGl0aW9uYWwgY2x1c3RlcmluZyBhbnRhcmEgbGFpbiBLLU1lYW5zLCBLLU1lZG9pZHMsIGRhbiBGdXp6eSBDLU1lYW5zLg0KDQojIyMgKipQYXJ0aXRpb25hbCBNZXRvZGUqKg0KDQojIyMjICoqMS4gS+KAkU1lYW5zKioNCg0KKipEZWZpbmlzaSBTaW5na2F0KioNCg0KKipLLU1lYW5zKiogYWRhbGFoIG1ldG9kZSBjbHVzdGVyaW5nIHlhbmcgbWVtYmFnaSBkYXRhIG1lbmphZGkgJGskIGNsdXN0ZXIsIGRpIG1hbmEgc2V0aWFwIG9ic2VydmFzaSBkaW1hc3Vra2FuIGtlIGNsdXN0ZXIgZGVuZ2FuIGNlbnRyb2lkIHRlcmRla2F0LiBDZW50cm9pZCBtZXJ1cGFrYW4gbmlsYWkgcmF0YS1yYXRhIGRhcmkgc2VsdXJ1aCBkYXRhIHBhZGEgY2x1c3RlciB0ZXJzZWJ1dC4NCg0KKipGb3JtdWxhOioqDQoNCiQkDQpKID0gXHN1bV97aT0xfV57a30gXHN1bV97eF9qIFxpbiBDX2l9IFx8IHhfaiAtIFxtdV9pIFx8XjINCiQkDQoNCmRlbmdhbjoNCg0KLSAgICRrJCA9IGp1bWxhaCBjbHVzdGVyDQotICAgJENfaSQgPSBjbHVzdGVyIGtlLSRpJA0KLSAgICR4X2okID0gZGF0YSBrZS0kaiQNCi0gICAkXG11X2kkID0gY2VudHJvaWQgKG1lYW4pIGRhcmkgY2x1c3RlciBrZS0kaSQNCg0KKipDYXJhIEtlcmphICYgSHlwZXJwYXJhbWV0ZXIqKg0KDQpMYW5na2FoLWxhbmdrYWggSy1NZWFucyBhZGFsYWggc2ViYWdhaSBiZXJpa3V0Og0KDQoxLiAgVGVudHVrYW4ganVtbGFoIGNsdXN0ZXIgJGskLg0KMi4gIFBpbGloIGNlbnRyb2lkIGF3YWwuDQozLiAgSGl0dW5nIGphcmFrIHNldGlhcCBkYXRhIGtlIG1hc2luZy1tYXNpbmcgY2VudHJvaWQuDQo0LiAgVGVtcGF0a2FuIHNldGlhcCBkYXRhIGtlIGNsdXN0ZXIgZGVuZ2FuIGNlbnRyb2lkIHRlcmRla2F0Lg0KNS4gIEhpdHVuZyB1bGFuZyBjZW50cm9pZCBiZXJkYXNhcmthbiByYXRhLXJhdGEgYW5nZ290YSBjbHVzdGVyLg0KNi4gIFVsYW5naSBsYW5na2FoIHRlcnNlYnV0IHNhbXBhaSBoYXNpbCBjbHVzdGVyIHN0YWJpbC4NCg0KSHlwZXJwYXJhbWV0ZXIgdXRhbWE6DQoNCi0gICBqdW1sYWggY2x1c3RlciAkayQNCi0gICBpbmlzaWFsaXNhc2kgY2VudHJvaWQNCi0gICBqdW1sYWggaXRlcmFzaSBtYWtzaW11bQ0KDQoqKktlbGViaWhhbiAmIEtldGVyYmF0YXNhbioqDQoNCnwgKipLZWxlYmloYW4qKiB8ICoqS2V0ZXJiYXRhc2FuKiogfA0KfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18DQp8IFNlZGVyaGFuYSAmIG11ZGFoIGRpaW1wbGVtZW50YXNpa2FuLiA8YnI+Q2VwYXQgdW50dWsgZGF0YXNldCBiZXNhci4gPGJyPkNvY29rIGJpbGEgY2x1c3RlciByZWxhdGlmIGtvbXBhayAvIGJlcmJlbnR1ayBidWxhdC48YnI+TXVkYWggZGlwYWhhbWkuIHwgSnVtbGFoIGNsdXN0ZXIgaGFydXMgZGl0ZW50dWthbiBkaSBhd2FsLiA8YnI+U2Vuc2l0aWYgdGVyaGFkYXAgaW5pc2lhbGlzYXNpIGNlbnRyb2lkLiA8YnI+U2Vuc2l0aWYgdGVyaGFkYXAgb3V0bGllci4gPGJyPkt1cmFuZyBjb2NvayB1bnR1ayBiZW50dWsgY2x1c3RlciBrb21wbGVrcy4gfA0KDQoqKkhhc2lsICYgVmlzdWFsaXNhc2kqKg0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRX0NCmxpYnJhcnkoZ2dwbG90MikNCg0KIyBBbWJpbCB2YXJpYWJlbCBudW1lcmlrIGRhcmkgZGF0YXNldCBpcmlzDQpkZiA8LSBpcmlzWywgMTo0XQ0KDQojIEstTWVhbnMgY2x1c3RlcmluZw0Kc2V0LnNlZWQoMTIzKQ0KayA8LSAzDQprbV9yZXMgPC0ga21lYW5zKGRmLCBjZW50ZXJzID0gaywgbnN0YXJ0ID0gMjUpDQoNCiMgVGFtYmFoa2FuIGhhc2lsIGNsdXN0ZXINCmlyaXNfa20gPC0gaXJpcw0KaXJpc19rbSRDbHVzdGVyIDwtIGZhY3RvcihrbV9yZXMkY2x1c3RlcikNCg0KIyBKdW1sYWggYW5nZ290YSB0aWFwIGNsdXN0ZXINCnRhYmxlKGlyaXNfa20kQ2x1c3RlcikNCg0KIyBDZW50cm9pZCB0aWFwIGNsdXN0ZXINCmttX3JlcyRjZW50ZXJzDQoNCiMgQ2x1c3RlciB1bnR1ayAxMCBkYXRhIHBlcnRhbWENCmhlYWQoaXJpc19rbSRDbHVzdGVyLCAxMCkNCg0KIyBWaXN1YWxpc2FzaQ0KZ2dwbG90KGlyaXNfa20sIGFlcyh4ID0gU2VwYWwuTGVuZ3RoLCB5ID0gU2VwYWwuV2lkdGgsIGNvbG9yID0gQ2x1c3RlcikpICsNCiAgZ2VvbV9wb2ludChzaXplID0gMykgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIkstTWVhbnMgQ2x1c3RlcmluZyBwYWRhIERhdGFzZXQgSXJpcyIsDQogICAgeCA9ICJTZXBhbCBMZW5ndGgiLA0KICAgIHkgPSAiU2VwYWwgV2lkdGgiDQogICkNCmBgYA0KDQoqKkludGVycHJldGFzaSoqDQoNCkhhc2lsIEstTWVhbnMgbWVtYmFnaSBkYXRhIGlyaXMga2UgZGFsYW0gdGlnYSBjbHVzdGVyLiBNYXNpbmctbWFzaW5nIGNsdXN0ZXIgbWVudW5qdWtrYW4ga2Vsb21wb2sgYnVuZ2EgZGVuZ2FuIGthcmFrdGVyaXN0aWsgdWt1cmFuIHNlcGFsIGRhbiBwZXRhbCB5YW5nIGJlcmJlZGEuIFZpc3VhbGlzYXNpIG1lbXBlcmxpaGF0a2FuIGJhaHdhIHNlYmFnaWFuIGtlbG9tcG9rIGRhcGF0IGRpcGlzYWhrYW4gZGVuZ2FuIGN1a3VwIGplbGFzLCBtZXNraXB1biBtYXNpaCB0ZXJkYXBhdCBiZWJlcmFwYSB0aXRpayB5YW5nIGJlcmRla2F0YW4gYW50YXIgY2x1c3Rlci4NCg0KIyMjIyAqKjIuIEvigJFNZWRvaWRzKioNCg0KKipEZWZpbmlzaSBTaW5na2F0KioNCg0KKipLLU1lZG9pZHMqKiBhZGFsYWggbWV0b2RlIGNsdXN0ZXJpbmcgeWFuZyBtaXJpcCBkZW5nYW4gSy1NZWFucywgdGV0YXBpIHB1c2F0IGNsdXN0ZXIgeWFuZyBkaWd1bmFrYW4gYWRhbGFoIG1lZG9pZCwgeWFpdHUgc2FsYWggc2F0dSBkYXRhIGFzbGkgZGFsYW0gY2x1c3Rlci4gS2FyZW5hIG1lbmdndW5ha2FuIGRhdGEgYXNsaSBzZWJhZ2FpIHB1c2F0IGNsdXN0ZXIsIEstTWVkb2lkcyBjZW5kZXJ1bmcgbGViaWggdGFoYW4gdGVyaGFkYXAgb3V0bGllci4NCg0KKipGb3JtdWxhOioqDQoNClR1anVhbiBLLU1lZG9pZHMgYWRhbGFoIG1lbWluaW1hbGthbiB0b3RhbCBqYXJhayBhbnRhcmEgc2V0aWFwIGRhdGEgZGVuZ2FuIG1lZG9pZCBjbHVzdGVyLW55YToNCg0KJCQNCkogPSBcc3VtX3tpPTF9XntrfSBcc3VtX3t4X2ogXGluIENfaX0gZCh4X2osIG1faSkNCiQkDQoNCmRlbmdhbjoNCg0KLSAgICRtX2kkID0gbWVkb2lkIGNsdXN0ZXIga2XigJEkaSQNCg0KLSAgICRkKHhfaiwgbV9pKSQgPSBqYXJhayBhbnRhcmEgZGF0YSBrZS0kaiQgZGFuIG1lZG9pZCBjbHVzdGVyIGtlLSRpJA0KDQoqKkNhcmEgS2VyamEgJiBIeXBlcnBhcmFtZXRlcioqDQoNCkxhbmdrYWgtbGFuZ2thaCBLLU1lZG9pZHM6DQoNCjEuICBUZW50dWthbiBqdW1sYWggY2x1c3RlciAkayQuDQoyLiAgUGlsaWggJGskIGRhdGEgc2ViYWdhaSBtZWRvaWQgYXdhbC4NCjMuICBIaXR1bmcgamFyYWsgc2V0aWFwIGRhdGEga2UgbWVkb2lkLg0KNC4gIFRlbXBhdGthbiBkYXRhIGtlIGNsdXN0ZXIgZGVuZ2FuIG1lZG9pZCB0ZXJkZWthdC4NCjUuICBFdmFsdWFzaSBhcGFrYWggYWRhIGRhdGEgbGFpbiB5YW5nIGxlYmloIGJhaWsgbWVuamFkaSBtZWRvaWQuDQo2LiAgVWxhbmdpIHNhbXBhaSBtZWRvaWQgdGlkYWsgYmVydWJhaCBsYWdpLg0KDQpIeXBlcnBhcmFtZXRlciBVdGFtYToNCg0KLSAgIGp1bWxhaCBjbHVzdGVyICgkayQpDQotICAgcGVtaWxpaGFuIG1lZG9pZCBhd2FsDQotICAgbWV0cmlrIGphcmFrDQoNCioqS2VsZWJpaGFuICYgS2V0ZXJiYXRhc2FuKioNCg0KfCAqKktlbGViaWhhbioqIHwgKipLZXRlcmJhdGFzYW4qKiB8DQp8LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwNCnwgTGViaWggcm9idXN0IHRlcmhhZGFwIG91dGxpZXIuIDxicj5NZWRvaWQgYWRhbGFoIGRhdGEgbnlhdGEgc2VoaW5nZ2EgbXVkYWggZGlpbnRlcnByZXRhc2kuIDxicj5EYXBhdCBtZW1ha2FpIGJlcmJhZ2FpIHVrdXJhbiBqYXJhay4gfCBMZWJpaCBsYW1iYXQgZGliYW5kaW5nIEstTWVhbnMuIDxicj5LdXJhbmcgZWZpc2llbiB1bnR1ayBkYXRhc2V0IGJlc2FyLiA8YnI+U2Vuc2l0aWYgdGVyaGFkYXAgbWVkb2lkIGF3YWwuIHwNCg0KKipIYXNpbCAmIFZpc3VhbGlzYXNpKioNCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0V9DQpsaWJyYXJ5KGNsdXN0ZXIpDQpsaWJyYXJ5KGdncGxvdDIpDQoNCiMgQW1iaWwgdmFyaWFiZWwgbnVtZXJpaw0KZGYgPC0gaXJpc1ssIDE6NF0NCg0KIyBTdGFuZGFyaXNhc2kgZGF0YQ0KZGZfc2NhbGVkIDwtIHNjYWxlKGRmKQ0KDQojIEstTWVkb2lkcyBkZW5nYW4gUEFNDQpzZXQuc2VlZCgxMjMpDQpwYW1fcmVzIDwtIHBhbShkZl9zY2FsZWQsIGsgPSAzKQ0KDQojIFRhbWJhaGthbiBoYXNpbCBjbHVzdGVyDQppcmlzX3BhbSA8LSBpcmlzDQppcmlzX3BhbSRDbHVzdGVyIDwtIGZhY3RvcihwYW1fcmVzJGNsdXN0ZXJpbmcpDQoNCiMgSnVtbGFoIGFuZ2dvdGEgdGlhcCBjbHVzdGVyDQp0YWJsZShpcmlzX3BhbSRDbHVzdGVyKQ0KDQojIE1lZG9pZCB0aWFwIGNsdXN0ZXINCnBhbV9yZXMkbWVkb2lkcw0KDQojIFNpbGhvdWV0dGUgd2lkdGggcmF0YS1yYXRhDQpwYW1fcmVzJHNpbGluZm8kYXZnLndpZHRoDQoNCiMgVmlzdWFsaXNhc2kNCmdncGxvdChpcmlzX3BhbSwgYWVzKHggPSBQZXRhbC5MZW5ndGgsIHkgPSBQZXRhbC5XaWR0aCwgY29sb3IgPSBDbHVzdGVyKSkgKw0KICBnZW9tX3BvaW50KHNpemUgPSAzKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiSy1NZWRvaWRzIENsdXN0ZXJpbmcgcGFkYSBEYXRhc2V0IElyaXMiLA0KICAgIHggPSAiUGV0YWwgTGVuZ3RoIiwNCiAgICB5ID0gIlBldGFsIFdpZHRoIg0KICApDQpgYGANCg0KKipJbnRlcnByZXRhc2kqKg0KDQpIYXNpbCBLLU1lZG9pZHMgbWVudW5qdWtrYW4gcGVtYmFnaWFuIGRhdGEga2UgZGFsYW0gdGlnYSBjbHVzdGVyIGJlcmRhc2Fya2FuIGtlZGVrYXRhbiB0ZXJoYWRhcCBtZWRvaWQuIEthcmVuYSBwdXNhdCBjbHVzdGVyIGJlcnVwYSBkYXRhIG55YXRhLCBoYXNpbCBjbHVzdGVyaW5nIGluaSBsZWJpaCBzdGFiaWwgdGVyaGFkYXAgbmlsYWkgZWtzdHJlbSBkaWJhbmRpbmdrYW4gSy1NZWFucy4gUGFkYSBkYXRhc2V0IGlyaXMsIHBlbWlzYWhhbiBjbHVzdGVyIHVtdW1ueWEgdGVybGloYXQgY3VrdXAgYmFpaywgdGVydXRhbWEgamlrYSBtZW5nZ3VuYWthbiB2YXJpYWJlbCBwZXRhbC4NCg0KIyMjIyAqKjMuIEZ1enp5IEPigJFNZWFucyAoRkNNKSoqDQoNCioqRGVmaW5pc2kgU2luZ2thdCoqDQoNCioqRnV6enkgQy1NZWFucyoqIGFkYWxhaCBtZXRvZGUgY2x1c3RlcmluZyBkaSBtYW5hIHNldGlhcCBkYXRhIHRpZGFrIGhhcnVzIG1lbmphZGkgYW5nZ290YSBtdXRsYWsgc2F0dSBjbHVzdGVyIHNhamEsIHRldGFwaSBkYXBhdCBtZW1pbGlraSBkZXJhamF0IGtlYW5nZ290YWFuIHBhZGEgYmViZXJhcGEgY2x1c3RlciBzZWthbGlndXMuIE1ldG9kZSBpbmkgY29jb2sgZGlndW5ha2FuIGtldGlrYSBiYXRhcyBhbnRhciBjbHVzdGVyIHRpZGFrIHRlcmxhbHUgdGVnYXMuDQoNCioqRm9ybXVsYToqKg0KDQpUdWp1YW4gRnV6enkgQy1NZWFucyBhZGFsYWggbWVtaW5pbWFsa2FuIGZ1bmdzaSBvYmpla3RpZiBiZXJpa3V0Og0KDQokJA0KSl9tID0gXHN1bV97aT0xfV57Y30gXHN1bV97aj0xfV57bn0gdV97aWp9Xm0gXDsgXHwgeF9qIC0gY19pIFx8XjINCiQkDQoNCmRlbmdhbjoNCg0KLSAgICR1X3tpan0kID0gZGVyYWphdCBrZWFuZ2dvdGFhbiBkYXRhIGtlLSRqJCBwYWRhIGNsdXN0ZXIga2UtJGkkDQotICAgJGNfaSQgPSBjZW50cm9pZCBjbHVzdGVyIGtlLSRpJA0KLSAgICRtJCAtIHBhcmFtZXRlciBmdXp6aW5lc3MNCi0gICAkbiQgPSBqdW1sYWggZGF0YQ0KLSAgICRjJCA9IGp1bWxhaCBjbHVzdGVyDQoNCioqQ2FyYSBLZXJqYSAmIEh5cGVycGFyYW1ldGVyKioNCg0KTGFuZ2thaC1sYW5na2FoIEZ1enp5IEMtTWVhbnM6DQoNCjEuICBUZW50dWthbiBqdW1sYWggY2x1c3RlciAkYyQuDQoyLiAgVGVudHVrYW4gcGFyYW1ldGVyIGZ1enppbmVzcyAkbSQuDQozLiAgSW5pc2lhbGlzYXNpIGRlcmFqYXQga2Vhbmdnb3RhYW4gc2V0aWFwIGRhdGEuDQo0LiAgSGl0dW5nIGNlbnRyb2lkIGNsdXN0ZXIgYmVyZGFzYXJrYW4gYm9ib3Qga2Vhbmdnb3RhYW4uDQo1LiAgUGVyYmFydWkgbmlsYWkga2Vhbmdnb3RhYW4gYmVyZGFzYXJrYW4gamFyYWsga2UgY2VudHJvaWQuDQo2LiAgVWxhbmdpIHNhbXBhaSBrb252ZXJnZW4uDQoNCkh5cGVycGFyYW1ldGVyIFV0YW1hOg0KDQotICAganVtbGFoIGNsdXN0ZXINCi0gICBwYXJhbWV0ZXIgZnV6emluZXNzICgkbSQpDQotICAganVtbGFoIGl0ZXJhc2kgbWFrc2ltdW0NCg0KKipLZWxlYmloYW4gJiBLZXRlcmJhdGFzYW4qKg0KDQp8ICoqS2VsZWJpaGFuKiogfCAqKktldGVyYmF0YXNhbioqIHwNCnwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfA0KfCBEYXRhIGRhcGF0IG1lbWlsaWtpIGtlYW5nZ290YWFuIHBhZGEgbGViaWggZGFyaSBzYXR1IGNsdXN0ZXIuIDxicj5Db2NvayB1bnR1ayBiYXRhcyBjbHVzdGVyIHlhbmcgdGlkYWsgdGVnYXMuIDxicj5NZW1iZXJpa2FuIGluZm9ybWFzaSBkZXJhamF0IGtlYW5nZ290YWFuLiB8IEludGVycHJldGFzaSBsZWJpaCBydW1pdC4gPGJyPkxlYmloIGxhbWJhdCBkaWJhbmRpbmcgaGFyZCBjbHVzdGVyaW5nLiA8YnI+U2Vuc2l0aWYgdGVyaGFkYXAgaW5pc2lhbGlzYXNpIGRhbiBwYXJhbWV0ZXIgJG0kLiB8DQoNCioqSGFzaWwgJiBWaXN1YWxpc2FzaSoqDQoNCmBgYHtyLCBtZXNzYWdlPUZBTFNFfQ0KbGlicmFyeShlMTA3MSkNCmxpYnJhcnkoZ2dwbG90MikNCg0KIyBBbWJpbCB2YXJpYWJlbCBudW1lcmlrDQpkZiA8LSBpcmlzWywgMTo0XQ0KDQojIFN0YW5kYXJpc2FzaSBkYXRhDQpkZl9zY2FsZWQgPC0gc2NhbGUoZGYpDQoNCiMgRnV6enkgQy1NZWFucw0Kc2V0LnNlZWQoMTIzKQ0KZmNtX3JlcyA8LSBjbWVhbnMoZGZfc2NhbGVkLCBjZW50ZXJzID0gMywgbSA9IDIsIGl0ZXIubWF4ID0gMjAwLCBkaXN0ID0gImV1Y2xpZGVhbiIpDQoNCiMgSGFyZCBjbHVzdGVyIGRhcmkgbWVtYmVyc2hpcCB0ZXJiZXNhcg0KaGFyZF9jbHVzdGVyIDwtIGZjbV9yZXMkY2x1c3Rlcg0KDQojIFRhbWJhaGthbiBoYXNpbCBjbHVzdGVyDQppcmlzX2ZjbSA8LSBpcmlzDQppcmlzX2ZjbSRDbHVzdGVyIDwtIGZhY3RvcihoYXJkX2NsdXN0ZXIpDQoNCiMgRGVyYWphdCBrZWFuZ2dvdGFhbiB0ZXJiZXNhcg0KaXJpc19mY20kTWF4TWVtYmVyc2hpcCA8LSBhcHBseShmY21fcmVzJG1lbWJlcnNoaXAsIDEsIG1heCkNCg0KIyBUYW1waWxrYW4gaGFzaWwNCmhlYWQoaXJpc19mY20kQ2x1c3RlciwgMTApDQpoZWFkKHJvdW5kKGZjbV9yZXMkbWVtYmVyc2hpcCwgMyksIDUpDQpyb3VuZChmY21fcmVzJGNlbnRlcnMsIDMpDQoNCiMgVmlzdWFsaXNhc2kNCmdncGxvdChpcmlzX2ZjbSwgYWVzKA0KICB4ID0gUGV0YWwuTGVuZ3RoLA0KICB5ID0gUGV0YWwuV2lkdGgsDQogIGNvbG9yID0gQ2x1c3RlciwNCiAgc2l6ZSA9IE1heE1lbWJlcnNoaXANCikpICsNCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuOCkgKw0KICBzY2FsZV9zaXplKHJhbmdlID0gYygyLCA3KSkgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIkZ1enp5IEMtTWVhbnMgcGFkYSBEYXRhc2V0IElyaXMiLA0KICAgIHN1YnRpdGxlID0gIlVrdXJhbiB0aXRpayBtZW51bmp1a2thbiBkZXJhamF0IGtlYW5nZ290YWFuIHRlcmJlc2FyIiwNCiAgICB4ID0gIlBldGFsIExlbmd0aCIsDQogICAgeSA9ICJQZXRhbCBXaWR0aCINCiAgKQ0KYGBgDQoNCioqSW50ZXJwcmV0YXNpKioNCg0KSGFzaWwgRnV6enkgQy1NZWFucyBtZW51bmp1a2thbiBiYWh3YSBzZXRpYXAgZGF0YSBtZW1pbGlraSBkZXJhamF0IGtlYW5nZ290YWFuIHRlcmhhZGFwIHNldGlhcCBjbHVzdGVyLiBTZW1ha2luIGJlc2FyIG5pbGFpIG1lbWJlcnNoaXAgcGFkYSBzdWF0dSBjbHVzdGVyLCBzZW1ha2luIGt1YXQgZGF0YSB0ZXJzZWJ1dCB0ZXJtYXN1ayBrZSBkYWxhbSBjbHVzdGVyIHRlcnNlYnV0LiBQYWRhIHZpc3VhbGlzYXNpLCB1a3VyYW4gdGl0aWsgbWVudW5qdWtrYW4gdGluZ2thdCBrZWFuZ2dvdGFhbiBkb21pbmFuLCBzZWhpbmdnYSBkYXRhIGRlbmdhbiBrZWFuZ2dvdGFhbiB5YW5nIGxlYmloIHRlZ2FzIGFrYW4gdGFtcGFrIGxlYmloIGJlc2FyLg0K