Dataset

Dataset yang digunakan adalah User Knowledge Modeling dari UCI Machine Learning Repository. Dataset ini digunakan untuk menganalisis tingkat pengetahuan pengguna berdasarkan aktivitas belajar.

Variabel dalam dataset ini dapat dijelaskan sebagai berikut:
1. STG (Study Time Goal) = Waktu belajar
2. SCG (Study Count Goal) = Jumlah pengulangan materi
3. STR (Study Time Real) = Waktu belajar aktual
4. LPR (Learning Process Rate) = Tingkat pemahaman selama proses belajar
5. PEG (Performance Evaluation Grade) = Hasil evaluasi belajar
6. UNS (User Knowledge Level) = Tingkat pengetahuan pengguna (Target)

Import Data

library(readxl)
user <- read_excel("C:/Users/priya/Downloads/User Knowledge Modeling.xlsx")
head(user)
str(user)
## tibble [258 × 6] (S3: tbl_df/tbl/data.frame)
##  $ STG: num [1:258] 0 0.08 0.06 0.1 0.08 0.09 0.1 0.15 0.2 0 ...
##  $ SCG: num [1:258] 0 0.08 0.06 0.1 0.08 0.15 0.1 0.02 0.14 0 ...
##  $ STR: num [1:258] 0 0.1 0.05 0.15 0.08 0.4 0.43 0.34 0.35 0.5 ...
##  $ LPR: num [1:258] 0 0.24 0.25 0.65 0.98 0.1 0.29 0.4 0.72 0.2 ...
##  $ PEG: num [1:258] 0 0.9 0.33 0.3 0.24 0.66 0.56 0.01 0.25 0.85 ...
##  $ UNS: chr [1:258] "very_low" "High" "Low" "Middle" ...

Dataset terdiri dari 258 obs dengan 6 var yang menggambarkan aktivitas belajar pengguna.

Preprocessing

Sebelum dilakukannya analisis, variabel UNS harus dipisahkan terlebih dahulu karena clustering termasuk metode unsupervised. Selanjutnya data dinormalisasikan agar setiap variabel memiliki skala yang sebanding sehingga tidak ada variabel yang mendominasi perhitungan.

user_clust <- user[, -which(names(user) == "UNS")]
target <- user$UNS
str(user_clust)
## tibble [258 × 5] (S3: tbl_df/tbl/data.frame)
##  $ STG: num [1:258] 0 0.08 0.06 0.1 0.08 0.09 0.1 0.15 0.2 0 ...
##  $ SCG: num [1:258] 0 0.08 0.06 0.1 0.08 0.15 0.1 0.02 0.14 0 ...
##  $ STR: num [1:258] 0 0.1 0.05 0.15 0.08 0.4 0.43 0.34 0.35 0.5 ...
##  $ LPR: num [1:258] 0 0.24 0.25 0.65 0.98 0.1 0.29 0.4 0.72 0.2 ...
##  $ PEG: num [1:258] 0 0.9 0.33 0.3 0.24 0.66 0.56 0.01 0.25 0.85 ...
user_scaled <- scale(user_clust)

Analisis Clustering

Model 1 - Euclidean Distance

Perhitungan jarak dilakukan untuk mengetahui tingkat kemiripan antar data. Nilai jarak yang kecil menunjukkan bahwa dua data memiliki karakteristik yang mirip, sedangkan nilai yang besar menunjukkan perbedaan yang lebih jauh.

dist_matrix <- dist(user_scaled, method = "euclidean")
dist_mat <- as.matrix(dist_matrix)
dist_mat[1:5, 1:5]
##          1        2        3        4        5
## 1 0.000000 3.718118 1.700049 3.010984 4.108407
## 2 3.718118 0.000000 2.247041 2.883963 3.948448
## 3 1.700049 2.247041 0.000000 1.688235 2.968859
## 4 3.010984 2.883963 1.688235 0.000000 1.386833
## 5 4.108407 3.948448 2.968859 1.386833 0.000000

Model 2 - K-means Clustering

Untuk menentukan jumlah k optimal menggunakan metode Elbow.

wss <- numeric(10)

for (k in 1:10) {
  kmeans_model <- kmeans(user_scaled, centers = k, nstart = 25)
  wss[k] <- kmeans_model$tot.withinss
}

plot(1:10, wss, type="b",
     xlab="Jumlah Cluster",
     ylab="WSS",
     main="Elbow Method")

Interpretasi:
Dari grafik Elbow terlihat bahwa penurunan WSS cukup besar hingga cluster ke-3, sehingga jumlah cluster optimal adalah 3.

Selanjutnya dilakukan clustering menggunakan K-means dengan jumlah cluster sebanyak 3.

set.seed(123)
kmeans_result <- kmeans(user_scaled, centers = 3, nstart = 25)
table(kmeans_result$cluster)
## 
##   1   2   3 
## 119  81  58

Hasil tersebut menunjukkan bahwa data terbagi menjadi 3 kelompok dengan jumlah anggota yang berbeda, di mana cluster 1 terdiri dari 119 observasi, cluster 2 sebanyak 81 observasi, dan cluster 3 sebanyak 58 observasi.

library(factoextra)
## Loading required package: ggplot2
## Welcome! Want to learn more? See two factoextra-related books at https://goo.gl/ve3WBa
fviz_cluster(kmeans_result, data = user_scaled)

Interpretasi:
Visual cluster ini menunjukkan bahwa data terpisah menjadi tiga kelompok yang cukup jelas, meskipun masih terdapat sedikit tumpang tindih antar cluster. Hal ini menunjukkan bahwa metode K-Means mampu menangkap pola utama dalam data dengan cukup baik.

Model 3 - Gaussian Mixture Model

library(mclust)
gmm <- Mclust(user_scaled)
summary(gmm)
## ---------------------------------------------------- 
## Gaussian finite mixture model fitted by EM algorithm 
## ---------------------------------------------------- 
## 
## Mclust EEI (diagonal, equal volume and shape) model with 4 components: 
## 
##  log-likelihood   n df       BIC       ICL
##         -1700.6 258 28 -3556.683 -3585.376
## 
## Clustering table:
##   1   2   3   4 
##  54  28 104  72

Hasil dari GMM menunjukkan bahwa model terbaik membentuk 4 cluster. GMM juga mendapatkan nilai log-likelihood sebesar -1700.6, nilai BIC sebesar -3556.683, dan nilai ICL sebesar -3585.376. Nilai log-likelihood menggambarkan seberapa baik model dalam menyesuaikan data, di mana nilai yang lebih tinggi (atau lebih mendekati nol) menunjukkan model yang lebih baik. Sementara itu, BIC digunakan untuk memilih model terbaik dengan mempertimbangkan kompleksitas model, sehingga nilai BIC yang lebih kecil menunjukkan model yang lebih optimal. Nilai ICL yang mendekati BIC menunjukkan bahwa hasil clustering cukup konsisten dan tidak terlalu ambigu.

plot(gmm, what = "classification")

Interpretasi:
Visual GMM ini menunjukkan bahwa pembagian cluster menjadi lebih detail dan mampu menangkap pola-pola kecil dalam data. Hal ini menunjukkan bahwa pendekatan berbasis probabilitas dapat memberikan representasi yang lebih fleksibel terhadap data.

Model 4 - Fuzzy C-Means

library(e1071)
fuzzy <- cmeans(user_scaled, centers = 3, m = 2)
head(fuzzy$membership)
##              1         2         3
## [1,] 0.3738764 0.3125410 0.3135826
## [2,] 0.2815730 0.3577296 0.3606974
## [3,] 0.3785106 0.3098520 0.3116374
## [4,] 0.4830785 0.2581920 0.2587295
## [5,] 0.4595217 0.2702621 0.2702161
## [6,] 0.2498046 0.3729832 0.3772123

Hasil ini menunjukkan bahwa setiap data memiliki derajat keanggotaan terhadap masing-masing cluster, sehingga tidak hanya masuk ke satu kelompok saja.

fviz_cluster(list(data = user_scaled, cluster = fuzzy$cluster))

Interpretasi:
Visualisasi Fuzzy memperlihatkan adanya area tumpang tindih antar cluster yang cukup jelas. Hal ini menunjukkan bahwa dalam data nyata, karakteristik pengguna tidak selalu dapat dipisahkan secara tegas. Beberapa pengguna memiliki pola belajar yang berada di antara dua kelompok, sehingga metode Fuzzy lebih mampu merepresentasikan kondisi tersebut dibandingkan metode K-Means yang bersifat kaku.

Mencari Model Terbaik

K-Means

table(kmeans_result$cluster, target)
##    target
##     High Low Middle very_low
##   1   38  15     66        0
##   2    0  49      9       23
##   3   25  19     13        1

Secara pola:
Cluster 2 sudah cukup jelas karena didominasi oleh kategori Low dan very_low, artinya model berhasil mengelompokkan pengguna dengan pengetahuan rendah. Namun pada cluster 1 dan 3 masih terlihat campuran antara High, Middle, dan Low sehingga batas antar kelompok belum terlalu tegas.

Artinya, K-Means cukup baik, tetapi belum optimal dalam memisahkan semua tingkat pengetahuan secara jelas.

GMM

gmm_cluster <- gmm$classification
table(gmm_cluster, target)
##            target
## gmm_cluster High Low Middle very_low
##           1    0  40      0       14
##           2   26   0      2        0
##           3   37   0     67        0
##           4    0  43     19       10

Secara pola:
Terdapat cluster yang secara spesifik hanya berisi kategori High, sehingga menunjukkan bahwa model mampu memisahkan pengguna dengan tingkat pengetahuan tinggi secara tegas. Selain itu, terdapat cluster yang didominasi oleh Low dan very_low, yang menandakan bahwa pengguna dengan tingkat pengetahuan rendah juga berhasil dikelompokkan dengan konsisten. Namun, masih terdapat satu cluster yang berisi campuran beberapa kategori, sehingga pemisahan belum sepenuhnya sempurna.

Artinya, GMM mampu menangkap struktur data dengan lebih detail dan memberikan hasil yang lebih mendekati kondisi sebenarnya dibandingkan metode lainnya.

Fuzzy

table(fuzzy$cluster, target)
##    target
##     High Low Middle very_low
##   1    7  64     21       23
##   2   21  15     20        1
##   3   35   4     47        0

Secara pola:
Terlihat bahwa setiap cluster cenderung berisi campuran berbagai kategori seperti High, Middle, Low, dan very_low. Tidak ada cluster yang benar-benar didominasi oleh satu kategori tertentu, sehingga batas antar kelompok menjadi kurang jelas. Hal ini menunjukkan bahwa model tidak mampu memisahkan tingkat pengetahuan secara tegas karena sifatnya yang memperbolehkan satu data berada di beberapa cluster sekaligus.

Artinya, meskipun Fuzzy lebih fleksibel dalam merepresentasikan data, namun untuk tujuan pemisahan kategori UNS, hasil yang diperoleh kurang optimal.

Kesimpulan

Secara keseluruhan, hasil clustering menunjukkan bahwa setiap metode memiliki kemampuan yang berbeda dalam mengelompokkan data berdasarkan tingkat pengetahuan pengguna. K-Means mampu memberikan hasil yang cukup baik dengan struktur cluster yang sederhana, namun masih terdapat campuran kategori dalam beberapa cluster sehingga pemisahan belum sepenuhnya jelas. Fuzzy C-Means menunjukkan sifat data yang saling tumpang tindih, tetapi justru membuat batas antar kategori menjadi kurang tegas dan sulit diinterpretasikan.

Di sisi lain, Gaussian Mixture Model (GMM) menunjukkan hasil yang paling mendekati dengan nilai UNS. Hal ini terlihat dari adanya cluster yang mampu merepresentasikan kategori tertentu secara lebih jelas, seperti pemisahan kategori High serta pengelompokan Low dan very_low yang konsisten. Meskipun masih terdapat sedikit campuran pada salah satu cluster, secara keseluruhan GMM mampu menangkap struktur data dengan lebih baik dan lebih realistis.

Dengan demikian, dapat disimpulkan bahwa model terbaik yang dipilih adalah Gaussian Mixture Model (GMM) karena memiliki kemampuan paling baik dalam merepresentasikan tingkat pengetahuan pengguna serta menghasilkan pengelompokan yang paling sesuai dengan data aktual.

LS0tDQp0aXRsZTogIkNsdXN0ZXJpbmcgLSBVc2VyIEtub3dsZWRnZSBNb2RlbGluZyINCmF1dGhvcjogIlJhZmx5IFByaXlhbnRhbWEgUmFtYWRoYW4gQmFnYXNrYXJhIg0KZGF0ZTogImByIFN5cy5EYXRlKClgIg0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIHRvYzogdHJ1ZQ0KICAgIHRvY19mbG9hdDogdHJ1ZQ0KICAgIGRmX3ByaW50OiAicGFnZWQiDQogICAgY29kZV9mb2xkaW5nOiAiaGlkZSINCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMNCi0tLQ0KDQojIERhdGFzZXQNCg0KRGF0YXNldCB5YW5nIGRpZ3VuYWthbiBhZGFsYWggVXNlciBLbm93bGVkZ2UgTW9kZWxpbmcgZGFyaSBVQ0kgTWFjaGluZSBMZWFybmluZyBSZXBvc2l0b3J5LiBEYXRhc2V0IGluaSBkaWd1bmFrYW4gdW50dWsgbWVuZ2FuYWxpc2lzIHRpbmdrYXQgcGVuZ2V0YWh1YW4gcGVuZ2d1bmEgYmVyZGFzYXJrYW4gYWt0aXZpdGFzIGJlbGFqYXIuDQoNClZhcmlhYmVsIGRhbGFtIGRhdGFzZXQgaW5pIGRhcGF0IGRpamVsYXNrYW4gc2ViYWdhaSBiZXJpa3V0OlwNCjEuICpTVEcgKFN0dWR5IFRpbWUgR29hbCkqID0gV2FrdHUgYmVsYWphclwNCjIuICpTQ0cgKFN0dWR5IENvdW50IEdvYWwpKiA9IEp1bWxhaCBwZW5ndWxhbmdhbiBtYXRlcmlcDQozLiAqU1RSIChTdHVkeSBUaW1lIFJlYWwpKiA9IFdha3R1IGJlbGFqYXIgYWt0dWFsXA0KNC4gKkxQUiAoTGVhcm5pbmcgUHJvY2VzcyBSYXRlKSogPSBUaW5na2F0IHBlbWFoYW1hbiBzZWxhbWEgcHJvc2VzIGJlbGFqYXJcDQo1LiAqUEVHIChQZXJmb3JtYW5jZSBFdmFsdWF0aW9uIEdyYWRlKSogPSBIYXNpbCBldmFsdWFzaSBiZWxhamFyXA0KNi4gKlVOUyAoVXNlciBLbm93bGVkZ2UgTGV2ZWwpKiA9IFRpbmdrYXQgcGVuZ2V0YWh1YW4gcGVuZ2d1bmEgKFRhcmdldCkNCg0KIyBJbXBvcnQgRGF0YQ0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRX0NCmxpYnJhcnkocmVhZHhsKQ0KdXNlciA8LSByZWFkX2V4Y2VsKCJDOi9Vc2Vycy9wcml5YS9Eb3dubG9hZHMvVXNlciBLbm93bGVkZ2UgTW9kZWxpbmcueGxzeCIpDQpoZWFkKHVzZXIpDQpzdHIodXNlcikNCmBgYA0KDQpEYXRhc2V0IHRlcmRpcmkgZGFyaSAyNTggb2JzIGRlbmdhbiA2IHZhciB5YW5nIG1lbmdnYW1iYXJrYW4gYWt0aXZpdGFzIGJlbGFqYXIgcGVuZ2d1bmEuDQoNCiMgUHJlcHJvY2Vzc2luZw0KDQpTZWJlbHVtIGRpbGFrdWthbm55YSBhbmFsaXNpcywgdmFyaWFiZWwgVU5TIGhhcnVzIGRpcGlzYWhrYW4gdGVybGViaWggZGFodWx1IGthcmVuYSBjbHVzdGVyaW5nIHRlcm1hc3VrIG1ldG9kZSB1bnN1cGVydmlzZWQuIFNlbGFuanV0bnlhIGRhdGEgZGlub3JtYWxpc2FzaWthbiBhZ2FyIHNldGlhcCB2YXJpYWJlbCBtZW1pbGlraSBza2FsYSB5YW5nIHNlYmFuZGluZyBzZWhpbmdnYSB0aWRhayBhZGEgdmFyaWFiZWwgeWFuZyBtZW5kb21pbmFzaSBwZXJoaXR1bmdhbi4NCg0KYGBge3J9DQp1c2VyX2NsdXN0IDwtIHVzZXJbLCAtd2hpY2gobmFtZXModXNlcikgPT0gIlVOUyIpXQ0KdGFyZ2V0IDwtIHVzZXIkVU5TDQpzdHIodXNlcl9jbHVzdCkNCnVzZXJfc2NhbGVkIDwtIHNjYWxlKHVzZXJfY2x1c3QpDQpgYGANCg0KIyBBbmFsaXNpcyBDbHVzdGVyaW5nDQoNCiMjIE1vZGVsIDEgLSBFdWNsaWRlYW4gRGlzdGFuY2UNCg0KUGVyaGl0dW5nYW4gamFyYWsgZGlsYWt1a2FuIHVudHVrIG1lbmdldGFodWkgdGluZ2thdCBrZW1pcmlwYW4gYW50YXIgZGF0YS4gTmlsYWkgamFyYWsgeWFuZyBrZWNpbCBtZW51bmp1a2thbiBiYWh3YSBkdWEgZGF0YSBtZW1pbGlraSBrYXJha3RlcmlzdGlrIHlhbmcgbWlyaXAsIHNlZGFuZ2thbiBuaWxhaSB5YW5nIGJlc2FyIG1lbnVuanVra2FuIHBlcmJlZGFhbiB5YW5nIGxlYmloIGphdWguDQoNCmBgYHtyLCBtZXNzYWdlPUZBTFNFfQ0KZGlzdF9tYXRyaXggPC0gZGlzdCh1c2VyX3NjYWxlZCwgbWV0aG9kID0gImV1Y2xpZGVhbiIpDQpkaXN0X21hdCA8LSBhcy5tYXRyaXgoZGlzdF9tYXRyaXgpDQpkaXN0X21hdFsxOjUsIDE6NV0NCmBgYA0KDQojIyBNb2RlbCAyIC0gSy1tZWFucyBDbHVzdGVyaW5nDQoNClVudHVrIG1lbmVudHVrYW4ganVtbGFoIGsgb3B0aW1hbCBtZW5nZ3VuYWthbiBtZXRvZGUgRWxib3cuDQoNCmBgYHtyLCBtZXNzYWdlPUZBTFNFfQ0Kd3NzIDwtIG51bWVyaWMoMTApDQoNCmZvciAoayBpbiAxOjEwKSB7DQogIGttZWFuc19tb2RlbCA8LSBrbWVhbnModXNlcl9zY2FsZWQsIGNlbnRlcnMgPSBrLCBuc3RhcnQgPSAyNSkNCiAgd3NzW2tdIDwtIGttZWFuc19tb2RlbCR0b3Qud2l0aGluc3MNCn0NCg0KcGxvdCgxOjEwLCB3c3MsIHR5cGU9ImIiLA0KICAgICB4bGFiPSJKdW1sYWggQ2x1c3RlciIsDQogICAgIHlsYWI9IldTUyIsDQogICAgIG1haW49IkVsYm93IE1ldGhvZCIpDQpgYGANCg0KKipJbnRlcnByZXRhc2kqKjpcDQpEYXJpIGdyYWZpayBFbGJvdyB0ZXJsaWhhdCBiYWh3YSBwZW51cnVuYW4gV1NTIGN1a3VwIGJlc2FyIGhpbmdnYSBjbHVzdGVyIGtlLTMsIHNlaGluZ2dhIGp1bWxhaCBjbHVzdGVyIG9wdGltYWwgYWRhbGFoIDMuDQoNClNlbGFuanV0bnlhIGRpbGFrdWthbiBjbHVzdGVyaW5nIG1lbmdndW5ha2FuIEstbWVhbnMgZGVuZ2FuIGp1bWxhaCBjbHVzdGVyIHNlYmFueWFrIDMuDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoMTIzKQ0Ka21lYW5zX3Jlc3VsdCA8LSBrbWVhbnModXNlcl9zY2FsZWQsIGNlbnRlcnMgPSAzLCBuc3RhcnQgPSAyNSkNCnRhYmxlKGttZWFuc19yZXN1bHQkY2x1c3RlcikNCmBgYA0KDQpIYXNpbCB0ZXJzZWJ1dCBtZW51bmp1a2thbiBiYWh3YSBkYXRhIHRlcmJhZ2kgbWVuamFkaSAzIGtlbG9tcG9rIGRlbmdhbiBqdW1sYWggYW5nZ290YSB5YW5nIGJlcmJlZGEsIGRpIG1hbmEgY2x1c3RlciAxIHRlcmRpcmkgZGFyaSAxMTkgb2JzZXJ2YXNpLCBjbHVzdGVyIDIgc2ViYW55YWsgODEgb2JzZXJ2YXNpLCBkYW4gY2x1c3RlciAzIHNlYmFueWFrIDU4IG9ic2VydmFzaS4NCg0KYGBge3J9DQpsaWJyYXJ5KGZhY3RvZXh0cmEpDQpmdml6X2NsdXN0ZXIoa21lYW5zX3Jlc3VsdCwgZGF0YSA9IHVzZXJfc2NhbGVkKQ0KYGBgDQoNCioqSW50ZXJwcmV0YXNpKio6XA0KVmlzdWFsIGNsdXN0ZXIgaW5pIG1lbnVuanVra2FuIGJhaHdhIGRhdGEgdGVycGlzYWggbWVuamFkaSB0aWdhIGtlbG9tcG9rIHlhbmcgY3VrdXAgamVsYXMsIG1lc2tpcHVuIG1hc2loIHRlcmRhcGF0IHNlZGlraXQgdHVtcGFuZyB0aW5kaWggYW50YXIgY2x1c3Rlci4gSGFsIGluaSBtZW51bmp1a2thbiBiYWh3YSBtZXRvZGUgSy1NZWFucyBtYW1wdSBtZW5hbmdrYXAgcG9sYSB1dGFtYSBkYWxhbSBkYXRhIGRlbmdhbiBjdWt1cCBiYWlrLg0KDQojIyBNb2RlbCAzIC0gR2F1c3NpYW4gTWl4dHVyZSBNb2RlbA0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRX0NCmxpYnJhcnkobWNsdXN0KQ0KZ21tIDwtIE1jbHVzdCh1c2VyX3NjYWxlZCkNCnN1bW1hcnkoZ21tKQ0KYGBgDQoNCkhhc2lsIGRhcmkgR01NIG1lbnVuanVra2FuIGJhaHdhIG1vZGVsIHRlcmJhaWsgbWVtYmVudHVrIDQgY2x1c3Rlci4gR01NIGp1Z2EgbWVuZGFwYXRrYW4gbmlsYWkgbG9nLWxpa2VsaWhvb2Qgc2ViZXNhciAtMTcwMC42LCBuaWxhaSBCSUMgc2ViZXNhciAtMzU1Ni42ODMsIGRhbiBuaWxhaSBJQ0wgc2ViZXNhciAtMzU4NS4zNzYuIE5pbGFpIGxvZy1saWtlbGlob29kIG1lbmdnYW1iYXJrYW4gc2ViZXJhcGEgYmFpayBtb2RlbCBkYWxhbSBtZW55ZXN1YWlrYW4gZGF0YSwgZGkgbWFuYSBuaWxhaSB5YW5nIGxlYmloIHRpbmdnaSAoYXRhdSBsZWJpaCBtZW5kZWthdGkgbm9sKSBtZW51bmp1a2thbiBtb2RlbCB5YW5nIGxlYmloIGJhaWsuIFNlbWVudGFyYSBpdHUsIEJJQyBkaWd1bmFrYW4gdW50dWsgbWVtaWxpaCBtb2RlbCB0ZXJiYWlrIGRlbmdhbiBtZW1wZXJ0aW1iYW5na2FuIGtvbXBsZWtzaXRhcyBtb2RlbCwgc2VoaW5nZ2EgbmlsYWkgQklDIHlhbmcgbGViaWgga2VjaWwgbWVudW5qdWtrYW4gbW9kZWwgeWFuZyBsZWJpaCBvcHRpbWFsLiBOaWxhaSBJQ0wgeWFuZyBtZW5kZWthdGkgQklDIG1lbnVuanVra2FuIGJhaHdhIGhhc2lsIGNsdXN0ZXJpbmcgY3VrdXAga29uc2lzdGVuIGRhbiB0aWRhayB0ZXJsYWx1IGFtYmlndS4NCg0KYGBge1J9DQpwbG90KGdtbSwgd2hhdCA9ICJjbGFzc2lmaWNhdGlvbiIpDQpgYGANCg0KKipJbnRlcnByZXRhc2kqKjpcDQpWaXN1YWwgR01NIGluaSBtZW51bmp1a2thbiBiYWh3YSBwZW1iYWdpYW4gY2x1c3RlciBtZW5qYWRpIGxlYmloIGRldGFpbCBkYW4gbWFtcHUgbWVuYW5na2FwIHBvbGEtcG9sYSBrZWNpbCBkYWxhbSBkYXRhLiBIYWwgaW5pIG1lbnVuanVra2FuIGJhaHdhIHBlbmRla2F0YW4gYmVyYmFzaXMgcHJvYmFiaWxpdGFzIGRhcGF0IG1lbWJlcmlrYW4gcmVwcmVzZW50YXNpIHlhbmcgbGViaWggZmxla3NpYmVsIHRlcmhhZGFwIGRhdGEuDQoNCiMjIE1vZGVsIDQgLSBGdXp6eSBDLU1lYW5zDQoNCmBgYHtyLCBtZXNzYWdlPUZBTFNFfQ0KbGlicmFyeShlMTA3MSkNCmZ1enp5IDwtIGNtZWFucyh1c2VyX3NjYWxlZCwgY2VudGVycyA9IDMsIG0gPSAyKQ0KaGVhZChmdXp6eSRtZW1iZXJzaGlwKQ0KYGBgDQoNCkhhc2lsIGluaSBtZW51bmp1a2thbiBiYWh3YSBzZXRpYXAgZGF0YSBtZW1pbGlraSBkZXJhamF0IGtlYW5nZ290YWFuIHRlcmhhZGFwIG1hc2luZy1tYXNpbmcgY2x1c3Rlciwgc2VoaW5nZ2EgdGlkYWsgaGFueWEgbWFzdWsga2Ugc2F0dSBrZWxvbXBvayBzYWphLg0KDQpgYGB7cn0NCmZ2aXpfY2x1c3RlcihsaXN0KGRhdGEgPSB1c2VyX3NjYWxlZCwgY2x1c3RlciA9IGZ1enp5JGNsdXN0ZXIpKQ0KYGBgDQoNCioqSW50ZXJwcmV0YXNpKio6XA0KVmlzdWFsaXNhc2kgRnV6enkgbWVtcGVybGloYXRrYW4gYWRhbnlhIGFyZWEgdHVtcGFuZyB0aW5kaWggYW50YXIgY2x1c3RlciB5YW5nIGN1a3VwIGplbGFzLiBIYWwgaW5pIG1lbnVuanVra2FuIGJhaHdhIGRhbGFtIGRhdGEgbnlhdGEsIGthcmFrdGVyaXN0aWsgcGVuZ2d1bmEgdGlkYWsgc2VsYWx1IGRhcGF0IGRpcGlzYWhrYW4gc2VjYXJhIHRlZ2FzLiBCZWJlcmFwYSBwZW5nZ3VuYSBtZW1pbGlraSBwb2xhIGJlbGFqYXIgeWFuZyBiZXJhZGEgZGkgYW50YXJhIGR1YSBrZWxvbXBvaywgc2VoaW5nZ2EgbWV0b2RlIEZ1enp5IGxlYmloIG1hbXB1IG1lcmVwcmVzZW50YXNpa2FuIGtvbmRpc2kgdGVyc2VidXQgZGliYW5kaW5na2FuIG1ldG9kZSBLLU1lYW5zIHlhbmcgYmVyc2lmYXQga2FrdS4NCg0KIyBNZW5jYXJpIE1vZGVsIFRlcmJhaWsNCg0KIyMgSy1NZWFucw0KDQpgYGB7cn0NCnRhYmxlKGttZWFuc19yZXN1bHQkY2x1c3RlciwgdGFyZ2V0KQ0KYGBgDQoNClNlY2FyYSBwb2xhOlwNCkNsdXN0ZXIgMiBzdWRhaCBjdWt1cCBqZWxhcyBrYXJlbmEgZGlkb21pbmFzaSBvbGVoIGthdGVnb3JpIExvdyBkYW4gdmVyeV9sb3csIGFydGlueWEgbW9kZWwgYmVyaGFzaWwgbWVuZ2Vsb21wb2trYW4gcGVuZ2d1bmEgZGVuZ2FuIHBlbmdldGFodWFuIHJlbmRhaC4gTmFtdW4gcGFkYSBjbHVzdGVyIDEgZGFuIDMgbWFzaWggdGVybGloYXQgY2FtcHVyYW4gYW50YXJhIEhpZ2gsIE1pZGRsZSwgZGFuIExvdyBzZWhpbmdnYSBiYXRhcyBhbnRhciBrZWxvbXBvayBiZWx1bSB0ZXJsYWx1IHRlZ2FzLg0KDQpBcnRpbnlhLCBLLU1lYW5zIGN1a3VwIGJhaWssIHRldGFwaSBiZWx1bSBvcHRpbWFsIGRhbGFtIG1lbWlzYWhrYW4gc2VtdWEgdGluZ2thdCBwZW5nZXRhaHVhbiBzZWNhcmEgamVsYXMuDQoNCiMjIEdNTQ0KDQpgYGB7cn0NCmdtbV9jbHVzdGVyIDwtIGdtbSRjbGFzc2lmaWNhdGlvbg0KdGFibGUoZ21tX2NsdXN0ZXIsIHRhcmdldCkNCmBgYA0KDQpTZWNhcmEgcG9sYTpcDQpUZXJkYXBhdCBjbHVzdGVyIHlhbmcgc2VjYXJhIHNwZXNpZmlrIGhhbnlhIGJlcmlzaSBrYXRlZ29yaSBIaWdoLCBzZWhpbmdnYSBtZW51bmp1a2thbiBiYWh3YSBtb2RlbCBtYW1wdSBtZW1pc2Foa2FuIHBlbmdndW5hIGRlbmdhbiB0aW5na2F0IHBlbmdldGFodWFuIHRpbmdnaSBzZWNhcmEgdGVnYXMuIFNlbGFpbiBpdHUsIHRlcmRhcGF0IGNsdXN0ZXIgeWFuZyBkaWRvbWluYXNpIG9sZWggTG93IGRhbiB2ZXJ5X2xvdywgeWFuZyBtZW5hbmRha2FuIGJhaHdhIHBlbmdndW5hIGRlbmdhbiB0aW5na2F0IHBlbmdldGFodWFuIHJlbmRhaCBqdWdhIGJlcmhhc2lsIGRpa2Vsb21wb2trYW4gZGVuZ2FuIGtvbnNpc3Rlbi4gTmFtdW4sIG1hc2loIHRlcmRhcGF0IHNhdHUgY2x1c3RlciB5YW5nIGJlcmlzaSBjYW1wdXJhbiBiZWJlcmFwYSBrYXRlZ29yaSwgc2VoaW5nZ2EgcGVtaXNhaGFuIGJlbHVtIHNlcGVudWhueWEgc2VtcHVybmEuDQoNCkFydGlueWEsIEdNTSBtYW1wdSBtZW5hbmdrYXAgc3RydWt0dXIgZGF0YSBkZW5nYW4gbGViaWggZGV0YWlsIGRhbiBtZW1iZXJpa2FuIGhhc2lsIHlhbmcgbGViaWggbWVuZGVrYXRpIGtvbmRpc2kgc2ViZW5hcm55YSBkaWJhbmRpbmdrYW4gbWV0b2RlIGxhaW5ueWEuDQoNCiMjIEZ1enp5DQoNCmBgYHtyfQ0KdGFibGUoZnV6enkkY2x1c3RlciwgdGFyZ2V0KQ0KYGBgDQoNClNlY2FyYSBwb2xhOlwNClRlcmxpaGF0IGJhaHdhIHNldGlhcCBjbHVzdGVyIGNlbmRlcnVuZyBiZXJpc2kgY2FtcHVyYW4gYmVyYmFnYWkga2F0ZWdvcmkgc2VwZXJ0aSBIaWdoLCBNaWRkbGUsIExvdywgZGFuIHZlcnlfbG93LiBUaWRhayBhZGEgY2x1c3RlciB5YW5nIGJlbmFyLWJlbmFyIGRpZG9taW5hc2kgb2xlaCBzYXR1IGthdGVnb3JpIHRlcnRlbnR1LCBzZWhpbmdnYSBiYXRhcyBhbnRhciBrZWxvbXBvayBtZW5qYWRpIGt1cmFuZyBqZWxhcy4gSGFsIGluaSBtZW51bmp1a2thbiBiYWh3YSBtb2RlbCB0aWRhayBtYW1wdSBtZW1pc2Foa2FuIHRpbmdrYXQgcGVuZ2V0YWh1YW4gc2VjYXJhIHRlZ2FzIGthcmVuYSBzaWZhdG55YSB5YW5nIG1lbXBlcmJvbGVoa2FuIHNhdHUgZGF0YSBiZXJhZGEgZGkgYmViZXJhcGEgY2x1c3RlciBzZWthbGlndXMuDQoNCkFydGlueWEsIG1lc2tpcHVuIEZ1enp5IGxlYmloIGZsZWtzaWJlbCBkYWxhbSBtZXJlcHJlc2VudGFzaWthbiBkYXRhLCBuYW11biB1bnR1ayB0dWp1YW4gcGVtaXNhaGFuIGthdGVnb3JpIFVOUywgaGFzaWwgeWFuZyBkaXBlcm9sZWgga3VyYW5nIG9wdGltYWwuDQoNCiMgS2VzaW1wdWxhbg0KDQpTZWNhcmEga2VzZWx1cnVoYW4sIGhhc2lsIGNsdXN0ZXJpbmcgbWVudW5qdWtrYW4gYmFod2Egc2V0aWFwIG1ldG9kZSBtZW1pbGlraSBrZW1hbXB1YW4geWFuZyBiZXJiZWRhIGRhbGFtIG1lbmdlbG9tcG9ra2FuIGRhdGEgYmVyZGFzYXJrYW4gdGluZ2thdCBwZW5nZXRhaHVhbiBwZW5nZ3VuYS4gSy1NZWFucyBtYW1wdSBtZW1iZXJpa2FuIGhhc2lsIHlhbmcgY3VrdXAgYmFpayBkZW5nYW4gc3RydWt0dXIgY2x1c3RlciB5YW5nIHNlZGVyaGFuYSwgbmFtdW4gbWFzaWggdGVyZGFwYXQgY2FtcHVyYW4ga2F0ZWdvcmkgZGFsYW0gYmViZXJhcGEgY2x1c3RlciBzZWhpbmdnYSBwZW1pc2FoYW4gYmVsdW0gc2VwZW51aG55YSBqZWxhcy4gRnV6enkgQy1NZWFucyBtZW51bmp1a2thbiBzaWZhdCBkYXRhIHlhbmcgc2FsaW5nIHR1bXBhbmcgdGluZGloLCB0ZXRhcGkganVzdHJ1IG1lbWJ1YXQgYmF0YXMgYW50YXIga2F0ZWdvcmkgbWVuamFkaSBrdXJhbmcgdGVnYXMgZGFuIHN1bGl0IGRpaW50ZXJwcmV0YXNpa2FuLg0KDQpEaSBzaXNpIGxhaW4sIEdhdXNzaWFuIE1peHR1cmUgTW9kZWwgKEdNTSkgbWVudW5qdWtrYW4gaGFzaWwgeWFuZyBwYWxpbmcgbWVuZGVrYXRpIGRlbmdhbiBuaWxhaSBVTlMuIEhhbCBpbmkgdGVybGloYXQgZGFyaSBhZGFueWEgY2x1c3RlciB5YW5nIG1hbXB1IG1lcmVwcmVzZW50YXNpa2FuIGthdGVnb3JpIHRlcnRlbnR1IHNlY2FyYSBsZWJpaCBqZWxhcywgc2VwZXJ0aSBwZW1pc2FoYW4ga2F0ZWdvcmkgSGlnaCBzZXJ0YSBwZW5nZWxvbXBva2FuIExvdyBkYW4gdmVyeV9sb3cgeWFuZyBrb25zaXN0ZW4uIE1lc2tpcHVuIG1hc2loIHRlcmRhcGF0IHNlZGlraXQgY2FtcHVyYW4gcGFkYSBzYWxhaCBzYXR1IGNsdXN0ZXIsIHNlY2FyYSBrZXNlbHVydWhhbiBHTU0gbWFtcHUgbWVuYW5na2FwIHN0cnVrdHVyIGRhdGEgZGVuZ2FuIGxlYmloIGJhaWsgZGFuIGxlYmloIHJlYWxpc3Rpcy4NCg0KRGVuZ2FuIGRlbWlraWFuLCBkYXBhdCBkaXNpbXB1bGthbiBiYWh3YSBtb2RlbCB0ZXJiYWlrIHlhbmcgZGlwaWxpaCBhZGFsYWggR2F1c3NpYW4gTWl4dHVyZSBNb2RlbCAoR01NKSBrYXJlbmEgbWVtaWxpa2kga2VtYW1wdWFuIHBhbGluZyBiYWlrIGRhbGFtIG1lcmVwcmVzZW50YXNpa2FuIHRpbmdrYXQgcGVuZ2V0YWh1YW4gcGVuZ2d1bmEgc2VydGEgbWVuZ2hhc2lsa2FuIHBlbmdlbG9tcG9rYW4geWFuZyBwYWxpbmcgc2VzdWFpIGRlbmdhbiBkYXRhIGFrdHVhbC4=