Customer Segmentation

Pengantar

Menurut Shopify, definisi dari Customer Segmentation/Segmentasi Pelanggan adalah proses membagi pelanggan menjadi beberapa kelompok berdasarkan karakteristik umum sehingga perusahaan dapat memasarkan kepada setiap kelompok secara efektif dan tepat.
Dalam marketing B2B (Business-to-business), perusahaan atau sebuah badan munngkin akan mengelompokkan konsumen berdasarkan beberapa faktor:
a.Industri yang dijalankan b.Jumlah karyawan/SDM c.Produk-produk yang dijual sebelumnya d.Lokasi
Dalam marketing B2C (Business-to-consumer), perusahaan atau badan sering mengelompokkan berdasarkan beberapa demografisnya:
a.Usia b.Jenis kelamin c.Status hubungan (Menikah, lajang, dll) d.Lokasi (Perkotaan, pinggiran kota, pedesaan) e.Tipe profesi atau pekerjaan
Kenapa Customer Segmentation penting?
a.Dapat membuat pesan pemasaran yang lebih mengena ke setiap pelanggan b.Bisa lebih mengenal customer/pelanggan c.Biaya pemasaran bisa menjadi lebih rendah
Kenapa perlu bantuan algoritma untuk Customer Segmentation?
a.Agar dapat membagi-bagi segmen customer dengan mudah b.Dapat membantu kita menentukan jumlah clustering yang optimal c.Dapat memberikan hasil yang lebih cepat d.Karena bisa memberikan best practice
Kesimpulan

Customer segmentation adalah proses penting yang diperlukan di bisnis untuk mengenal konsumen dengan lebih baik Dengan demikian proses bisnis di marketing (pemasaran) dan CRM (customer relationship management) bisa dilakukan lebih tajam. Contoh: pesan marketing bisa lebih personal untuk setiap segment dengan biaya lebih optimal. Dengan proses yang lebih tajam, performa bisnis berpotensi tinggi menjadi lebih baik juga. Untuk menemukan segmentasi yang baik, perlu proses analisa data dari profile customer yang cukup banyak dan rutin. Ini bisa dibantu dengan algoritma komputer.
Persiapan Data
Dataset Customer Segments

Konversi Data dengan Fungsi data.matrix()

Isi data dari tiga kolom pelanggan diatas yaitu “Jenis.Kelamin”, “Profesi” dan “Tipe.Residen” merupakan data kategori yang berupa teks.
Untuk fungsi k-means, ketiga kolom ini tidak bisa digunakan kecuali isi dikonversi menjadi numerik. Salah satu caranya adalah dengan menggunakan fungsi data.matrix().
head(customer_matrix)
Jenis.Kelamin Profesi Tipe.Residen
[1,] 1 5 2
[2,] 2 3 1
[3,] 1 4 1
[4,] 1 4 1
[5,] 2 5 1
[6,] 2 4 1
maka hasilnya akan berupa numerik yang mewakili suatu nilai di suatu kolom. Jenis.Kelami, pria = 1 dan wanita = 2. Dan seterusnya untuk setiap kolom.
Menggabungkan Hasil Konversi

Setelah dikonversi, perlu juga mengetahui bagaimana menggabungkan kembali data tersebut ke variable asal.
Menormalisasikan Nilai Belanja

Kali ini perhatikan kolom “NilaiBelanjaSetahun”. Isi datanya bernilai jutaan. Ketika kolom ini digunakan untuk clustering, perhitungan sum of squared errors akan menjadi sangat besar.
customer$NilaiBelanjaSetahun
[1] 9.497927 2.722700 5.286429 5.204498 10.615206 5.215541 9.837260 5.223569
[9] 5.993218 5.257448 5.987367 5.941914 9.333168 9.471615 10.365668 5.262521
[17] 5.677762 5.340690 10.884508 2.896845 9.222070 5.298157 5.239290 10.259572
[25] 10.721998 5.269392 9.114159 6.631680 5.271845 5.020976 3.042773 10.663179
[33] 3.047926 9.759822 5.962575 9.678994 5.972787 10.477127 5.257775 2.861855
[41] 6.820976 9.880607 5.268410 9.339737 5.211041 10.099807 6.130724 10.390732
[49] 4.992585 10.569316
Membuat Data Master

Setelah penggabungan data, disini jadi mengetahui sebenarnya teks kategori dikonversi menjadi angka numerik berapa.
Clustering dan Algoritma K-Means
Apa itu Clustering dan algoritma K-Means?

Clustering adalah proses pembagian objek-objek ke dalam beberapa kelompok (cluster) berdasarkan tingkat kemiripan antara satu objek dengan yang lain.
Beberapa contoh clustering:
- Pengelompokan manusia berdasarkan umur: bayi, balita, anak, remaja, dewasa, tua.
- Pengelompokan customer berdasarkan daya belinya: regular dan premium.
- Pengelompokan makanan berdasarkan kandungan gizinya: biji-bijian, sayuran, buah-buahan, minyak, protein, dan lain-lain.
Banyak algoritma telah dikembangkan untuk melakukan clustering secara otomatis, salah satu yang sangat populer adalah K-Means yang akan menjadi fokus utama latihan ini.
K-means adalah algoritma yang membagi data menjadi sejumlah partisi dengan cara sederhana: mencari kedekatan dari tiap titik pada suatu cluster dengan sejumlah nilai rata-rata atau mean.
Ada dua konsep kunci yang juga menjadi nama asal k-means:
- Jumlah partisi yang diinginkan, diwakili oleh huruf k
- Mencari “jarak” kedekatan tiap titik ke sejumlah nilai rata-rata cluster yang diamati, diwakili oleh means. Ini biasa juga disebut centroid dilambang oleh simbol segitiga.
Fungsi K-means

Fungsi kmeans memerlukan minimal 2 parameter, yaitu:
- x: data yang digunakan, dimana semua isi datanya harus berupa numerik.
- centers: jumlah cluster yang diinginkan.
Mean atau nilai rata-rata disini sering disebut juga dengan centroid pada berbagai literatur data science.
Dan fungsi kmeans ini biasanya disertai dengan pemanggilan function seet.seed(). Ini berguna agar dapat “menyeragamkan” daftar nilai acak yang sama dari kmeans sehingga mendapatkan output yang sama.
#tampilkan hasil k-means
segmentasi
K-means clustering with 5 clusters of sizes 5, 12, 14, 9, 10
Cluster means:
Jenis.Kelamin.1 Umur Profesi.1 Tipe.Residen.1
1 1.40 61.80000 4.200000 1.400000
2 1.75 31.58333 3.916667 1.250000
3 2.00 20.07143 3.571429 1.357143
4 2.00 42.33333 4.000000 1.555556
5 1.70 52.50000 3.800000 1.300000
NilaiBelanjaSetahun
1 8.696132
2 7.330958
3 5.901089
4 8.804791
5 6.018321
Clustering vector:
[1] 1 3 5 5 4 3 1 5 2 2 5 5 1 1 3 2 2 1 2 3 4 5 2 4 2 5 2 4 5 4 3 4
[33] 3 3 4 2 3 4 3 3 3 2 2 3 3 3 5 4 2 5
Within cluster sum of squares by cluster:
[1] 58.21123 174.85164 316.73367 171.67372 108.49735
(between_SS / total_SS = 92.4 %)
Available components:
[1] "cluster" "centers" "totss" "withinss"
[5] "tot.withinss" "betweenss" "size" "iter"
[9] "ifault"
Analisa Hasil Clustering Vektor
segmentasi$cluster
[1] 1 3 5 5 4 3 1 5 2 2 5 5 1 1 3 2 2 1 2 3 4 5 2 4 2 5 2 4 5 4 3 4
[33] 3 3 4 2 3 4 3 3 3 2 2 3 3 3 5 4 2 5
Clustering vektor ini adalah rangkaian vektor yang berisi angka cluster. Dari hasil ini, vektor berisi angka 1 sampai dengan 5, maksimum sesuai dengan jumlah cluster yang diinginkan.
Vektor ini dimulai dari angka 1, yang artinya data pertama dari dataset akan dialokasikan pada nomor cluster 1 Dari gambar juga terlihat isi vektor kedua bernlai 3, ini artinya data kedua dari dataset dialokasikan pada nomor cluster 3, dan seterusnya. Posisi data terakhir (ke-50) memiliki nomor cluster 5.
Analisa Hasil Cluster Size

Tahap berikutnya, akan dianalisa hasil pada bagian pertama.
K-means clustering with 5 clusters of sizes 5, 12, 14, 9, 10
ni artinya dengan k-means nya telah membagi dataset pelanggan dengan 5 klaster, dimana:
Cluster ke-1 memiliki 5 data Cluster ke-2 memiliki 12 data Cluster ke-3 memiliki 14 data Cluster ke-4 memiliki 9 data Cluster ke-5 memiliki 10 data
Dengan jumlah total 50 data, yang juga merupakan jumlah data total pelanggan.
Melihat Data pada Cluster ke-N
Sejauh ini, belum terlihat data hasil klaster. Dapat dilihat klaster ke-N atau istilahnya melihat klaster dengan kondisi tertentu. Misal melihat klaster ke-1, maka dalam R digunakan operator “==” atau equal/sama dengan.
customer[which(customer$cluster == 1),]
Dimana ada 5 data denangan seluruh data dominan berjenis kelamin pria dan umur antara 58 s/d 64 tahun. Penghasilan, profesi, nilai belanja dan tipe residen cukup bervariasi.
Baris kode diatas yaitu dimana memanggil semua kolom di dataset “customer” dengan kondisi dimana setiap rows/baris yang memiliki klaster ke-1.
coba untuk merubah perintah melihat klaster ke-2.
customer[which(customer$cluster == 2),]
Hasil diatas terlihat umur mayoritas sudah masuk usia 20 tahun ke atas dan kebanyakan adalah wiraswasta dan profesional. Dan rata-rata nilai belanja adalah sekitar 5 juta sampai 9 juta.
Analisa Hasil Cluster Means

Klaster means adalah hasil nilai rata-rata atau titik sentral (centroid) dari seluruh titip tiap klaster.
segmentasi$centers
Jenis.Kelamin.1 Umur Profesi.1 Tipe.Residen.1 NilaiBelanjaSetahun
1 1.40 61.80000 4.200000 1.400000 8.696132
2 1.75 31.58333 3.916667 1.250000 7.330958
3 2.00 20.07143 3.571429 1.357143 5.901089
4 2.00 42.33333 4.000000 1.555556 8.804791
5 1.70 52.50000 3.800000 1.300000 6.018321
Apa penjelasan hasil diatas?
Kolom pertama yang berisi angka 1 sampai dengan 5 adalah mewakili nomor cluster. Kolom Jenis.Kelamin.1 menunjukkan nilai rata-rata dari data jenis kelamin yang telah dikonversi menjadi numerik, dengan angka 1 mewakili Pria dan angka 2 mewakili wanita.
pada klaster ke-1 berupa angka 1.40 artinya data bersifat campuran namun cenderung ke Pria (1). sedangkan klaster ke-2 1.75 berifat campuarn namun cenderung ke wanita (2).
Kedua interpretasi angka ini sesuai dengan hasil praktek “Melihat Data pada Cluster-N”.
Kolom Umur adalah representasi dari dataset awal tanpa mengalami konversi. Terlihat untuk klaster ke-1 umur rata-rata adalah 61 tahun, umur 31 tahun untuk klaster ke-2, dan seterusnya. Kolom .1 menunjukkan nilai rata-rata data Profesi untuk tiap klaster yang telah dikonversi menjadi numerik.
Angka 1, 2, 3, 4 dan 5 masing-masing mewakili profesi; Wiraswasta, Pelajar, Professional, Ibu Rumah Tangga dan Mahasiswa.
Terlihat untuk seluruh klaster profesi cenderung ke Professional terutama untuk klaster ke-4.
Kolom Tipe.Residen.1 menunjukkan representasi data Tipe.Residen yang telah dikonversi menjadi numerik dengan angka 1 mewakili Cluster dan 2 mewakili Sector. Ini juga didapatkan dari hasil konversi data menjadi numerik pada sebelumnya. Terlihat untuk seluruh klaster, terlihat data cukup tersebar antara Sector dan Cluster terutama untuk klaster ke-4 dimana nilai kolom ini di angka 1.555.
Terakhir, kolom NilaiBelanjaSetahun cukup signifikan pembagiannya untuk tiap klaster. Dimana klaster ke-1 dan ke-4 memiliki nilai belanja lebih tinggi dibandingkan ketiga klaster lainnya.
Ini mungkin target customer yang bisa lebih disasar melalui marketing campaign, karena klaster ke-1 saat ini hanya berisi 5 data. Cukup kecil proporsinya, dan ingin ditingkatkan
Analisa Hasil Sum of Squares

#Within cluster sum of squares by cluster:
#[1] 58.21123 174.85164 316.73367 171.67372 108.49735
# (between_SS / total_SS = 92.4 %)
Konsep sum of squares (SS) adalah jumlah “jarak kuadrat” perbedaan tiap titik data dengan mean atau centroidnya. SS ini bisa dengan mean atau centroid untuk tiap klaster atau secara keseluruhan data. Sum of squares dalam literatur data science lain sering disebut dengan Sum of Squared Errors (SSE).
Semakin besar nilai SS menyatakan semakin lebarnya perbedaan antar tiap titik data di dalam klaster tersebut.
Berdasarkan konsep tersebut, berikut adalah penjelasan untuk hasil output kmeans di atas:
1.Nilai 58.21123 adalah SS untuk klaster ke-1, 174.85164 adalah SS untuk klaster ke-2, dan seterusnya. Semakin besar nilainya berpotensi semakin baik.
2.total_SS: adalah SS untuk seluruh titik terhadap nilai rata-rata global, bukan untuk per klaster. Nilai ini selalu tetap dan tidak terpengaruh dengan jumlah klaster.
3.between_SS: adalah total_SS dikurangi dengan jumlah nilai SS seluruh klaster. (between_SS / total_SS) adalah rasio antara between_SS dibagi dengan total_SS. Semakin besar persentasenya, umumnya semakin baik.
Ini adalah metrik yang bisa digunakan untuk menjawab seberapa baik jumlah klaster yang dibentuk? Apakah dibagi 2, 5, 10 atau 30?
Teknik penggunaan metrik ini cukup panjang, namun untuk kepentingan praktis kali ini hanya melihat perbedaan nilai ini
kmeans(x = customer[field_yang_digunakan], centers = 2, nstart = 25)
K-means clustering with 2 clusters of sizes 23, 27
Cluster means:
Jenis.Kelamin.1 Umur Profesi.1 Tipe.Residen.1 NilaiBelanjaSetahun
1 1.739130 51.17391 3.913043 1.434783 7.551518
2 1.888889 25.85185 3.777778 1.296296 6.659586
Clustering vector:
[1] 1 2 1 1 1 2 1 1 2 2 1 1 1 1 2 2 2 1 2 2 2 1 2 1 2 1 2 1 1 1 2 1 2 2 1 2 2 1 2 2 2 2 2
[44] 2 2 2 1 1 2 1
Within cluster sum of squares by cluster:
[1] 1492.481 1524.081
(between_SS / total_SS = 72.6 %)
Available components:
[1] "cluster" "centers" "totss" "withinss" "tot.withinss"
[6] "betweenss" "size" "iter" "ifault"
kmeans(x = customer[field_yang_digunakan], centers = 5, nstart = 25)
K-means clustering with 5 clusters of sizes 5, 12, 14, 9, 10
Cluster means:
Jenis.Kelamin.1 Umur Profesi.1 Tipe.Residen.1
1 1.40 61.80000 4.200000 1.400000
2 1.75 31.58333 3.916667 1.250000
3 2.00 20.07143 3.571429 1.357143
4 2.00 42.33333 4.000000 1.555556
5 1.70 52.50000 3.800000 1.300000
NilaiBelanjaSetahun
1 8.696132
2 7.330958
3 5.901089
4 8.804791
5 6.018321
Clustering vector:
[1] 1 3 5 5 4 3 1 5 2 2 5 5 1 1 3 2 2 1 2 3 4 5 2 4 2 5 2 4 5 4 3 4
[33] 3 3 4 2 3 4 3 3 3 2 2 3 3 3 5 4 2 5
Within cluster sum of squares by cluster:
[1] 58.21123 174.85164 316.73367 171.67372 108.49735
(between_SS / total_SS = 92.4 %)
Available components:
[1] "cluster" "centers" "totss" "withinss"
[5] "tot.withinss" "betweenss" "size" "iter"
[9] "ifault"
Penjelasan hasil diatas untuk K=2 total_ss nya 72.6 % sedangakan hasil k=5 total-ss nya 92.4 %.
Terlihat untuk 2 klaster (k=2), SS per klaster lebih besar dibandingkan jika data dibagi menjadi 5 klaster (k=5). Perhatikan juga persentase rasio antara between_SS dan total_SS, dimana k=5 juga lebih besar.
Menentukan Jumlah Cluster Terbaik
Jumlah Cluster dan Sum of Square (SS) dan Grafik Elbow Effect

Dari informasi yang dihasilkan oleh fungsi kmeans, metrick Sum of Squares (SS) atau sering disebut Sum of Squared Errors (SSE) sangat penting untuk dijadikan dasar dalam menentukan jumlah cluster yang paling optimal.
Secara teoritis, berikut adalah beberapa hal yang bisa diaamati dengan SS:
a.Semakin sedikit jumlah cluster yang dihasilkan maka semakin besar nilai SS.
b.Begitu juga sebaliknya, semakin banyak jumlah cluster yang dihasilkan maka semakin kecil nilai SS nya.
c.Karena sifatnya kuadratik, jika terdapat perbedaan yang cukup signifikan antara tiap kombinasi cluster maka perbedaan nilai SS akan semakin besar. Dan seiring bertambahnya jumlah cluster, perbedaan tiap SS ini akan semakin kecil.
Jika dimasukkan ke dalam grafik garis, maka plotting dari total SS untuk tiap cluster berbentuk sebagai berikut.
ggplot(ssdata, aes(x = cluster,y = sse)) +
geom_line(color = "red") +
geom_point() +
theme(panel.background = element_blank(),
axis.line = element_line(colour = "black")) +
labs(title = "Sum of Squares",
subtitle = "Sum of Squares (SS) pada range cluster 1:10",
caption = "Sumber data: DQLab",
x = "Jumlah Cluster",
y = "Within Cluster Sum of Squares") +
geom_text(aes(label = format(round(sse, 2), nsmall = 2)),
hjust = 0.1,
vjust = -0.8,
size = 2.5) +
scale_x_discrete(limits = c(1:jumlah_cluster_max))
Warning: Continuous limits supplied to discrete scale.
Did you mean `limits = factor(...)` or `scale_*_continuous()`?

keterangan data grafik diaatas :
Titik paling kiri adalah jumlah SS untuk 1 jumlah cluster, titik kedua adalah untuk 2 jumlah cluster, dan seterusnya. Perhatikan semakin ke kanan perbedaan jarak antar tiap titik semakin mengecil.
Grafik garis ini memiliki bentuk seperti siku tangan, dan untuk optimal jumlah cluster biasanya dengan mengambil titik sikunya. Pada contoh di atas bisa mengambil 4 atau 5.
Proses pengambilan keputusan berdasarkan plotting siku ini biasa disebut Elbow Effect atau Elbow Method.
Kesimpulan
Dengan memanfaatkan nilai Sum of Squares (SS) atau Sum of Squared Errors (SSE) dapat mengambil keputusan jumlah segmentasi optimal yang digunakan.
Ini dilakukan dengan membuat simulasi iterasi jumlah klaster dari 1 sampai dengan jumlah maksimum yang diinginkan. Pada kasus, menggunakan angka iterasi 1 sampai dengan 10.
Setelah mendapatkan nilai SS dari tiap jumlah klaster, langsung bisa plotting ke grafik garis dan menggunakan elbow method untuk menentukan jumlah klaster optimal.
“Pemaketan” Model K-Means
Pengantar

Referensi hasil konversi dan objek kmeans ini supaya bisa digunakan untuk mengolah data baru dan berguna di bisnis.
Untuk ini tahapannya adalah sebagai berikut:
- Menamakan klaster dengan karakteristik yang lebih mudah dimengerti.
b.Penamaan ini disimpan dalam variabel “Segmen.customer”.
c.Menggabungkan variable Segmen.Pelanggan, Profesi, Jenis.Kelamin, Tipe.Residen, dan Segmentasi ke dalam satu objek bertipe list ke dalam variabel “Identitas.Cluster”.
d.Menyimpan objek Identitas.Cluster dalam bentuk file sehingga dapat digunakan kemudian, ini bisa disebut model.
Menamakan Segmen

Pada bagian ini, dimana akan menamakan segmen sesuai dengan karakteristiknya. Untuk membantu, gambar berikut menunjukkan nilai mean tiap kolom yang digunakan tiap klaster dan juga nilai kolom sebelum konversi.
segmentasi$centers
Jenis.Kelamin.1 Umur Profesi.1 Tipe.Residen.1 NilaiBelanjaSetahun
1 1.40 61.80000 4.200000 1.400000 8.696132
2 1.75 31.58333 3.916667 1.250000 7.330958
3 2.00 20.07143 3.571429 1.357143 5.901089
4 2.00 42.33333 4.000000 1.555556 8.804791
5 1.70 52.50000 3.800000 1.300000 6.018321
Berikut menamakan klaster 1 s/d 5:
Cluster 1 : Diamond Senior Member: alasannya adalah karena umurnya rata-rata adalah 61 tahun dan pembelanjaan di atas 8 juta.
Cluster 2 : Gold Young Professional: alasannya adalah karena umurnya rata-rata adalah 31 tahun, professional dan pembelanjaan cukup besar.
Cluster 3 : Silver Youth Gals: alasannya adalah karena umurnya rata-rata adalah 20, wanita semua, profesinya bercampur antar pelajar dan professional serta pembelanjaan sekitar 6 juta.
Cluster 4 : Diamond Profesional: alasannya adalah karena umurnya rata-rata adalah 42 tahun, pembelanjaan paling tinggi dan semuanya professional.
Cluster 5 : Silver Mid Professional: alasannya adalah karena umurnya rata-rata adalah 52 tahun dan pembelanjaan sekitar 6 juta.
Nama-nama klaster ini bisa dimasukkan ke dalam data frame. Agar setiap klaster memiliki nama-namanya sendiri.
Menggabungkan Referensi

Akan sangat baik jika semuanya digabungkan di satu variabel dengan tipe list, dan ini akan jadi model yang dapat disimpan ke dalam file dan digunakan ketika diperlukan. Layaknya seperti function pada pemrograman atau stored procedure pada SQL.
Identitas.Cluster
$Profesi
$Jenis.Kelamin
$Tipe.Residen
$segmentasi
K-means clustering with 5 clusters of sizes 5, 12, 14, 9, 10
Cluster means:
Jenis.Kelamin.1 Umur Profesi.1 Tipe.Residen.1 NilaiBelanjaSetahun
1 1.40 61.80000 4.200000 1.400000 8.696132
2 1.75 31.58333 3.916667 1.250000 7.330958
3 2.00 20.07143 3.571429 1.357143 5.901089
4 2.00 42.33333 4.000000 1.555556 8.804791
5 1.70 52.50000 3.800000 1.300000 6.018321
Clustering vector:
[1] 1 3 5 5 4 3 1 5 2 2 5 5 1 1 3 2 2 1 2 3 4 5 2 4 2 5 2 4 5 4 3 4 3 3 4 2 3 4 3 3 3 2 2
[44] 3 3 3 5 4 2 5
Within cluster sum of squares by cluster:
[1] 58.21123 174.85164 316.73367 171.67372 108.49735
(between_SS / total_SS = 92.4 %)
Available components:
[1] "cluster" "centers" "totss" "withinss" "tot.withinss"
[6] "betweenss" "size" "iter" "ifault"
$Segmen_customer
$field_yang_digunakan
[1] "Jenis.Kelamin.1" "Umur" "Profesi.1"
[4] "Tipe.Residen.1" "NilaiBelanjaSetahun"
Menyimpan Objek dalam Bentuk File

Objek yang sudah digabungkan pada sebelumnya sudah memiliki semua aset yang diperlukan untuk mengalokasikan data baru ke segmen yang sesuai.
Untuk menyimpan objek ini ke dalam file menggunakan fungsi saveRDS(). File ini kemudian dapat dibuka kembali sebagai objek ke depannya.
Catatan: Hasil file akan disimpan di direktori yang sama dimana project R disimpan di local file.
Kesimpulan

Model ini adalah objek yang bisa digunakan untuk mengolah data baru dan terdiri dari objek kmeans, variable referensi hasil konversi teks ke numerik, dan juga penamaan klaster.
Mengoperasionalkan Model K-Means
Pengantar

bagaimana data baru dapat otomatis membantu tim marketing dan CRM untuk mengidentifikasi segmen mana pelanggan tersebut berada dengan cepat.
Dengan kecepatan identifikasi, maka organisasi atau bisnis dapat dengan cepat bergerak dengan pesan marketing yang efektif dan memenangkan persaingan.
Data Baru

Pada teks sebelumnya, disebutkan data pelanggan baru harus cepat dipetakan ke segmen.
Dengan asumsi tiap data pelanggan baru diinput ke dalam sistem, maka pengolahan adalah per record/baris. Kali ini, akan dibuat data.frame dengan satu data dimana nama-nama kolomnya persis dengan dataset awal.
Membuat Objek Clustering dari File

Maksud disini adalah membuka/membaca file yang telah disimpan simpan sebelumnya dengan sebuah fungsi, dan supaya dikenali di R sebagai objek yang akan digunakan untuk mengolah data baru.
Untuk membuka file tersebut, menggunakan fungsi readRDS().
Perintahnya sangat sederhana, berikut adalah untuk membuka file cluster.rds yang telah disimpan sebelumnya.
Identitas.Cluster
$Profesi
$Jenis.Kelamin
$Tipe.Residen
$segmentasi
K-means clustering with 5 clusters of sizes 5, 12, 14, 9, 10
Cluster means:
Jenis.Kelamin.1 Umur Profesi.1 Tipe.Residen.1 NilaiBelanjaSetahun
1 1.40 61.80000 4.200000 1.400000 8.696132
2 1.75 31.58333 3.916667 1.250000 7.330958
3 2.00 20.07143 3.571429 1.357143 5.901089
4 2.00 42.33333 4.000000 1.555556 8.804791
5 1.70 52.50000 3.800000 1.300000 6.018321
Clustering vector:
[1] 1 3 5 5 4 3 1 5 2 2 5 5 1 1 3 2 2 1 2 3 4 5 2 4 2 5 2 4 5 4 3 4 3 3 4 2 3 4 3 3 3 2 2
[44] 3 3 3 5 4 2 5
Within cluster sum of squares by cluster:
[1] 58.21123 174.85164 316.73367 171.67372 108.49735
(between_SS / total_SS = 92.4 %)
Available components:
[1] "cluster" "centers" "totss" "withinss" "tot.withinss"
[6] "betweenss" "size" "iter" "ifault"
$Segmen_customer
$field_yang_digunakan
[1] "Jenis.Kelamin.1" "Umur" "Profesi.1"
[4] "Tipe.Residen.1" "NilaiBelanjaSetahun"
Merge dengan Data Referensi

Dengan adanya data baru dan objek yang berisi data referensi telah dibaca kembali, bisa menggabungkan data baru ini untuk mendapatkan konversi numerik dari field Jenis.Kelamin, Profesi dan Tipe.Residen.
Tujuannya adalah agar bisa mencari segmen pelanggannya dengan data numerik hasil penggabungan.
Menentukan Cluster

Tahap ini merupakan yang terpenting bagi bisnis; data baru ini masuk ke segmen mana?
Mudah! Yaitu dengan tahapan berikut:
a.mencari jarak kuadrat minimum atau terdekat
b.dari kolom numerik data baru tersebut
c.ke centroid (nilai rata-rata) kolom terkait dari seluruh klaster yang ada
Terlihat lebih jelas ternyata saya masuk dari kolom Nama.Segmen ke angka 3/klaster ke-3 berisi nilai Silver Young Professional.
Kesimpulan

Praktek terakhir menunjukkan bagaimana data pelanggan baru dianalisa oleh model yang dibuat dan mengeluarkan nomor klaster atau segmen.
LS0tDQp0aXRsZTogIkNVU1RPTUVSIFNFR01FTlRBVElPTiINCmF1dGhvcjogIlxVMDAwMUY1RTMgSmFtYWxsdWRpbiINCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vazoNCiAgICB0b2M6IHllcw0KICBodG1sX2RvY3VtZW50Og0KICAgIG51bWJlcl9zZWN0aW9uczogeWVzDQogICAgdG9jOiB5ZXMNCiAgICBmaWdfd2lkdGg6IDcNCiAgICBmaWdfaGVpZ2h0OiA0LjUNCiAgICB0aGVtZTogcmVhZGFibGUNCiAgICBoaWdobGlnaHQ6IHRhbmdvDQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQotLS0NCg0KPGNlbnRlcj48aW1nIHNyYz0iaHR0cHM6Ly9naXRodWIuY29tL2lzaGFyYW5lcmFuamFuYS9rYWdnbGVfZ2lmL2Jsb2IvbWFpbi9rbWVhbi5naWY/cmF3PXRydWUgInN0eWxlPSJib3JkZXItcmFkaXVzOiAyMHB4OyI+PC9jZW50ZXI+DQoNCi0tLQ0KDQoNCiMgIEN1c3RvbWVyIFNlZ21lbnRhdGlvbg0KDQohW10oamFtYWwucG5nKQ0KDQojIyBQZW5nYW50YXINCg0KIVtdKG1lbGloYXQgZGF0YSBjbHVzdGVyaW5nLnBuZykNCg0KTWVudXJ1dCBTaG9waWZ5LCBkZWZpbmlzaSBkYXJpIEN1c3RvbWVyIFNlZ21lbnRhdGlvbi9TZWdtZW50YXNpIFBlbGFuZ2dhbiBhZGFsYWggcHJvc2VzIG1lbWJhZ2kgcGVsYW5nZ2FuIG1lbmphZGkgYmViZXJhcGEga2Vsb21wb2sgYmVyZGFzYXJrYW4ga2FyYWt0ZXJpc3RpayB1bXVtIHNlaGluZ2dhIHBlcnVzYWhhYW4gZGFwYXQgbWVtYXNhcmthbiBrZXBhZGEgc2V0aWFwIGtlbG9tcG9rIHNlY2FyYSBlZmVrdGlmIGRhbiB0ZXBhdC4NCg0KRGFsYW0gbWFya2V0aW5nIEIyQiAoQnVzaW5lc3MtdG8tYnVzaW5lc3MpLCBwZXJ1c2FoYWFuIGF0YXUgc2VidWFoIGJhZGFuIG11bm5na2luIGFrYW4gbWVuZ2Vsb21wb2trYW4ga29uc3VtZW4gYmVyZGFzYXJrYW4gYmViZXJhcGEgZmFrdG9yOg0KDQphLkluZHVzdHJpIHlhbmcgZGlqYWxhbmthbg0KYi5KdW1sYWgga2FyeWF3YW4vU0RNDQpjLlByb2R1ay1wcm9kdWsgeWFuZyBkaWp1YWwgc2ViZWx1bW55YQ0KZC5Mb2thc2kNCg0KRGFsYW0gbWFya2V0aW5nIEIyQyAoQnVzaW5lc3MtdG8tY29uc3VtZXIpLCBwZXJ1c2FoYWFuIGF0YXUgYmFkYW4gc2VyaW5nIG1lbmdlbG9tcG9ra2FuIGJlcmRhc2Fya2FuIGJlYmVyYXBhIGRlbW9ncmFmaXNueWE6DQoNCmEuVXNpYQ0KYi5KZW5pcyBrZWxhbWluDQpjLlN0YXR1cyBodWJ1bmdhbiAoTWVuaWthaCwgbGFqYW5nLCBkbGwpDQpkLkxva2FzaSAoUGVya290YWFuLCBwaW5nZ2lyYW4ga290YSwgcGVkZXNhYW4pDQplLlRpcGUgcHJvZmVzaSBhdGF1IHBla2VyamFhbg0KDQpLZW5hcGEgQ3VzdG9tZXIgU2VnbWVudGF0aW9uIHBlbnRpbmc/DQoNCmEuRGFwYXQgbWVtYnVhdCBwZXNhbiBwZW1hc2FyYW4geWFuZyBsZWJpaCBtZW5nZW5hIGtlIHNldGlhcCBwZWxhbmdnYW4NCmIuQmlzYSBsZWJpaCBtZW5nZW5hbCBjdXN0b21lci9wZWxhbmdnYW4NCmMuQmlheWEgcGVtYXNhcmFuIGJpc2EgbWVuamFkaSBsZWJpaCByZW5kYWgNCg0KS2VuYXBhIHBlcmx1IGJhbnR1YW4gYWxnb3JpdG1hIHVudHVrIEN1c3RvbWVyIFNlZ21lbnRhdGlvbj8NCg0KYS5BZ2FyIGRhcGF0IG1lbWJhZ2ktYmFnaSBzZWdtZW4gY3VzdG9tZXIgZGVuZ2FuIG11ZGFoDQpiLkRhcGF0IG1lbWJhbnR1IGtpdGEgbWVuZW50dWthbiBqdW1sYWggY2x1c3RlcmluZyB5YW5nIG9wdGltYWwNCmMuRGFwYXQgbWVtYmVyaWthbiBoYXNpbCB5YW5nIGxlYmloIGNlcGF0DQpkLkthcmVuYSBiaXNhIG1lbWJlcmlrYW4gYmVzdCBwcmFjdGljZQ0KDQojIyBLZXNpbXB1bGFuDQoNCiFbXShrZXNpbXB1bGFuLnBuZykNCg0KQ3VzdG9tZXIgc2VnbWVudGF0aW9uIGFkYWxhaCBwcm9zZXMgcGVudGluZyB5YW5nIGRpcGVybHVrYW4gZGkgYmlzbmlzIHVudHVrIG1lbmdlbmFsIGtvbnN1bWVuIGRlbmdhbiBsZWJpaCBiYWlrDQpEZW5nYW4gZGVtaWtpYW4gcHJvc2VzIGJpc25pcyBkaSBtYXJrZXRpbmcgKHBlbWFzYXJhbikgZGFuIENSTSAoY3VzdG9tZXIgcmVsYXRpb25zaGlwIG1hbmFnZW1lbnQpIGJpc2EgZGlsYWt1a2FuIGxlYmloIHRhamFtLiBDb250b2g6IHBlc2FuIG1hcmtldGluZyBiaXNhIGxlYmloIHBlcnNvbmFsIHVudHVrIHNldGlhcCBzZWdtZW50IGRlbmdhbiBiaWF5YSBsZWJpaCBvcHRpbWFsLg0KRGVuZ2FuIHByb3NlcyB5YW5nIGxlYmloIHRhamFtLCBwZXJmb3JtYSBiaXNuaXMgYmVycG90ZW5zaSB0aW5nZ2kgbWVuamFkaSBsZWJpaCBiYWlrIGp1Z2EuDQpVbnR1ayBtZW5lbXVrYW4gc2VnbWVudGFzaSB5YW5nIGJhaWssIHBlcmx1IHByb3NlcyBhbmFsaXNhIGRhdGEgZGFyaSBwcm9maWxlIGN1c3RvbWVyIHlhbmcgY3VrdXAgYmFueWFrIGRhbiBydXRpbi4gSW5pIGJpc2EgZGliYW50dSBkZW5nYW4gYWxnb3JpdG1hIGtvbXB1dGVyLg0KDQoNCiMgUGVyc2lhcGFuIERhdGENCg0KIyMgRGF0YXNldCBDdXN0b21lciBTZWdtZW50cw0KDQohW10oamFtYWwucG5nKQ0KDQoNCmBgYHtyfQ0KY3VzdG9tZXIgPC0gcmVhZC5jc3YoIkJvb2sxLnR4dCIsIHNlcCA9ICJcdCIpDQpoZWFkKGN1c3RvbWVyKQ0KYGBgDQoNCiMjIEtvbnZlcnNpIERhdGEgZGVuZ2FuIEZ1bmdzaSBkYXRhLm1hdHJpeCgpDQoNCiFbXShrb252ZXJzaS5wbmcpDQoNCklzaSBkYXRhIGRhcmkgdGlnYSBrb2xvbSBwZWxhbmdnYW4gZGlhdGFzIHlhaXR1IOKAnEplbmlzLktlbGFtaW7igJ0sIOKAnFByb2Zlc2nigJ0gZGFuIOKAnFRpcGUuUmVzaWRlbuKAnSBtZXJ1cGFrYW4gZGF0YSBrYXRlZ29yaSB5YW5nIGJlcnVwYSB0ZWtzLg0KDQpVbnR1ayBmdW5nc2kgay1tZWFucywga2V0aWdhIGtvbG9tIGluaSB0aWRhayBiaXNhIGRpZ3VuYWthbiBrZWN1YWxpIGlzaSBkaWtvbnZlcnNpIG1lbmphZGkgbnVtZXJpay4gU2FsYWggc2F0dSBjYXJhbnlhIGFkYWxhaCBkZW5nYW4gbWVuZ2d1bmFrYW4gZnVuZ3NpIGRhdGEubWF0cml4KCkuDQoNCmBgYHtyfQ0KIyBLb252ZXJzaSBkYXRhIG1lbmphZGkgbnVtZXJpaw0KDQpjdXN0b21lcl9tYXRyaXggPC0gZGF0YS5tYXRyaXgoY3VzdG9tZXJbYygiSmVuaXMuS2VsYW1pbiIsICJQcm9mZXNpIiwgIlRpcGUuUmVzaWRlbiIpXSkNCg0KaGVhZChjdXN0b21lcl9tYXRyaXgpDQpgYGANCg0KbWFrYSBoYXNpbG55YSBha2FuIGJlcnVwYSBudW1lcmlrIHlhbmcgbWV3YWtpbGkgc3VhdHUgbmlsYWkgZGkgc3VhdHUga29sb20uDQoqKkplbmlzLktlbGFtaSoqLCBwcmlhID0gMSBkYW4gd2FuaXRhID0gMi4gRGFuIHNldGVydXNueWEgdW50dWsgc2V0aWFwIGtvbG9tLg0KIA0KIA0KIyMgTWVuZ2dhYnVuZ2thbiBIYXNpbCBLb252ZXJzaQ0KDQohW10oa29udmVyc2kucG5nKQ0KDQpTZXRlbGFoIGRpa29udmVyc2ksIHBlcmx1IGp1Z2EgbWVuZ2V0YWh1aSBiYWdhaW1hbmEgbWVuZ2dhYnVuZ2thbiBrZW1iYWxpIGRhdGEgdGVyc2VidXQga2UgdmFyaWFibGUgYXNhbC4NCg0KYGBge3J9DQojIFBlbmdnYWJ1bmdhbiBkYXRhDQoNCmN1c3RvbWVyIDwtIGRhdGEuZnJhbWUoY3VzdG9tZXIsIGN1c3RvbWVyX21hdHJpeCkNCg0KIyBPdXRwdXQga2VtYmFsaSBkYXRhIGhhc2lsIHBlbmdnYWJ1bmdhbg0KDQpoZWFkKGN1c3RvbWVyKQ0KYGBgDQoNCiMjIE1lbm9ybWFsaXNhc2lrYW4gTmlsYWkgQmVsYW5qYQ0KDQohW10obmlsYSBiZWxhbmphLnBuZykNCg0KS2FsaSBpbmkgcGVyaGF0aWthbiBrb2xvbSDigJxOaWxhaUJlbGFuamFTZXRhaHVu4oCdLiBJc2kgZGF0YW55YSBiZXJuaWxhaSBqdXRhYW4uIEtldGlrYSBrb2xvbSBpbmkgZGlndW5ha2FuIHVudHVrIGNsdXN0ZXJpbmcsIHBlcmhpdHVuZ2FuIHN1bSBvZiBzcXVhcmVkIGVycm9ycyBha2FuIG1lbmphZGkgc2FuZ2F0IGJlc2FyLg0KDQpgYGB7cn0NCiMgTm9ybWFsaXNhc2kgTmlsYWkNCg0KY3VzdG9tZXIkTmlsYWlCZWxhbmphU2V0YWh1biA8LSBjdXN0b21lciROaWxhaUJlbGFuamFTZXRhaHVuLzEwMDAwMDANCg0KIyBPdXRwdXQgdmFyaWFiZWwgcGVsYW5nZ2FuJE5pbGFpQmVsYW5qYVNldGFodW4NCg0KY3VzdG9tZXIkTmlsYWlCZWxhbmphU2V0YWh1bg0KYGBgDQoNCg0KIyMgTWVtYnVhdCBEYXRhIE1hc3Rlcg0KDQohW10oZGF0YSBtYXN0ZXIucG5nKQ0KDQpTZXRlbGFoIHBlbmdnYWJ1bmdhbiBkYXRhLCBkaXNpbmkgamFkaSBtZW5nZXRhaHVpIHNlYmVuYXJueWEgdGVrcyBrYXRlZ29yaSANCmRpa29udmVyc2kgbWVuamFkaSBhbmdrYSBudW1lcmlrIGJlcmFwYS4NCg0KYGBge3J9DQp1bmlxdWUoY3VzdG9tZXJbYygiUHJvZmVzaSIsICJQcm9mZXNpLjEiKV0pDQpgYGANCg0KYGBge3J9DQp1bmlxdWUoY3VzdG9tZXJbYygiSmVuaXMuS2VsYW1pbiIsICJKZW5pcy5LZWxhbWluLjEiKV0pDQpgYGANCg0KYGBge3J9DQp1bmlxdWUoY3VzdG9tZXJbYygiVGlwZS5SZXNpZGVuIiwgIlRpcGUuUmVzaWRlbi4xIildKQ0KYGBgDQoNCiMgQ2x1c3RlcmluZyBkYW4gQWxnb3JpdG1hIEstTWVhbnMNCg0KIyMgQXBhIGl0dSBDbHVzdGVyaW5nIGRhbiBhbGdvcml0bWEgSy1NZWFucz8NCg0KIVtdKGstbWVuYXMucG5nKQ0KDQpDbHVzdGVyaW5nIGFkYWxhaCBwcm9zZXMgcGVtYmFnaWFuIG9iamVrLW9iamVrIGtlIGRhbGFtIGJlYmVyYXBhIGtlbG9tcG9rIChjbHVzdGVyKSBiZXJkYXNhcmthbiB0aW5na2F0IGtlbWlyaXBhbiBhbnRhcmEgc2F0dSBvYmplayBkZW5nYW4geWFuZyBsYWluLg0KDQpCZWJlcmFwYSBjb250b2ggY2x1c3RlcmluZzoNCg0KYS4gUGVuZ2Vsb21wb2thbiBtYW51c2lhIGJlcmRhc2Fya2FuIHVtdXI6IGJheWksIGJhbGl0YSwgYW5haywgcmVtYWphLCBkZXdhc2EsIHR1YS4NCmIuIFBlbmdlbG9tcG9rYW4gY3VzdG9tZXIgYmVyZGFzYXJrYW4gZGF5YSBiZWxpbnlhOiByZWd1bGFyIGRhbiBwcmVtaXVtLg0KYy4gUGVuZ2Vsb21wb2thbiBtYWthbmFuIGJlcmRhc2Fya2FuIGthbmR1bmdhbiBnaXppbnlhOiBiaWppLWJpamlhbiwgc2F5dXJhbiwgYnVhaC1idWFoYW4sIG1pbnlhaywgcHJvdGVpbiwgZGFuIGxhaW4tbGFpbi4NCg0KQmFueWFrIGFsZ29yaXRtYSB0ZWxhaCBkaWtlbWJhbmdrYW4gdW50dWsgbWVsYWt1a2FuIGNsdXN0ZXJpbmcgc2VjYXJhIG90b21hdGlzLCBzYWxhaCBzYXR1IHlhbmcgc2FuZ2F0IHBvcHVsZXIgYWRhbGFoIEstTWVhbnMgeWFuZyBha2FuIG1lbmphZGkgZm9rdXMgdXRhbWEgbGF0aWhhbiBpbmkuDQoNCkstbWVhbnMgYWRhbGFoIGFsZ29yaXRtYSB5YW5nIG1lbWJhZ2kgZGF0YSBtZW5qYWRpIHNlanVtbGFoIHBhcnRpc2kgZGVuZ2FuIGNhcmEgc2VkZXJoYW5hOiBtZW5jYXJpIGtlZGVrYXRhbiBkYXJpIHRpYXAgdGl0aWsgcGFkYSBzdWF0dSBjbHVzdGVyIGRlbmdhbiBzZWp1bWxhaCBuaWxhaSByYXRhLXJhdGEgYXRhdSBtZWFuLg0KDQpBZGEgZHVhIGtvbnNlcCBrdW5jaSB5YW5nIGp1Z2EgbWVuamFkaSBuYW1hIGFzYWwgay1tZWFuczoNCg0KYS4gSnVtbGFoIHBhcnRpc2kgeWFuZyBkaWluZ2lua2FuLCBkaXdha2lsaSBvbGVoIGh1cnVmIGsNCmIuIE1lbmNhcmkg4oCcamFyYWvigJ0ga2VkZWthdGFuIHRpYXAgdGl0aWsga2Ugc2VqdW1sYWggbmlsYWkgcmF0YS1yYXRhIGNsdXN0ZXIgeWFuZyBkaWFtYXRpLCBkaXdha2lsaSBvbGVoIG1lYW5zLiBJbmkgYmlhc2EganVnYSBkaXNlYnV0IGNlbnRyb2lkIGRpbGFtYmFuZyBvbGVoIHNpbWJvbCBzZWdpdGlnYS4NCg0KIyMgRnVuZ3NpIEstbWVhbnMNCg0KIVtdKGZ1bmdzaSBrLW1lbmFzLnBuZykNCg0KRnVuZ3NpIGttZWFucyBtZW1lcmx1a2FuIG1pbmltYWwgMiBwYXJhbWV0ZXIsIHlhaXR1Og0KDQphLiB4OiBkYXRhIHlhbmcgZGlndW5ha2FuLCBkaW1hbmEgc2VtdWEgaXNpIGRhdGFueWEgaGFydXMgYmVydXBhIG51bWVyaWsuDQpiLiBjZW50ZXJzOiBqdW1sYWggY2x1c3RlciB5YW5nIGRpaW5naW5rYW4uDQoNCk1lYW4gYXRhdSBuaWxhaSByYXRhLXJhdGEgZGlzaW5pIHNlcmluZyBkaXNlYnV0IGp1Z2EgZGVuZ2FuIGNlbnRyb2lkIHBhZGEgYmVyYmFnYWkgbGl0ZXJhdHVyIGRhdGEgc2NpZW5jZS4NCg0KRGFuIGZ1bmdzaSBrbWVhbnMgaW5pIGJpYXNhbnlhIGRpc2VydGFpIGRlbmdhbiBwZW1hbmdnaWxhbiBmdW5jdGlvbiBzZWV0LnNlZWQoKS4gSW5pIGJlcmd1bmEgYWdhciBkYXBhdCDigJxtZW55ZXJhZ2Fta2Fu4oCdIGRhZnRhciBuaWxhaSBhY2FrIHlhbmcgc2FtYSBkYXJpIGttZWFucyBzZWhpbmdnYSBtZW5kYXBhdGthbiBvdXRwdXQgeWFuZyBzYW1hLg0KDQpgYGB7cn0NCmZpZWxkX3lhbmdfZGlndW5ha2FuID0gYygiSmVuaXMuS2VsYW1pbi4xIiwgIlVtdXIiLCAiUHJvZmVzaS4xIiwgIlRpcGUuUmVzaWRlbi4xIiwiTmlsYWlCZWxhbmphU2V0YWh1biIpDQoNCiMgQmFnaWFuIEstbWVhbnMNCnNldC5zZWVkKDEwMCkNCg0KIyBGdW5jdGlvbiBrbWVhbnMgdW50dWsgbWVtYmVudHVrIDUgY2x1c3RlciBkZW5nYW4gMjUgc2tlbmFyaW8gcmFuZG9tIGRhbiBzaW1wYW4ga2UgZGFsYW0gdmFyaWFibGUgc2VnbWVudGFzaQ0Kc2VnbWVudGFzaSA8LSBrbWVhbnMoeCA9IGN1c3RvbWVyW2ZpZWxkX3lhbmdfZGlndW5ha2FuXSwgY2VudGVycyA9IDUsIG5zdGFydCA9IDI1KQ0KDQojdGFtcGlsa2FuIGhhc2lsIGstbWVhbnMNCnNlZ21lbnRhc2kNCmBgYA0KDQojIEFuYWxpc2EgSGFzaWwgQ2x1c3RlcmluZyBWZWt0b3INCg0KYGBge3J9DQpzZWdtZW50YXNpJGNsdXN0ZXINCmBgYA0KDQoNCkNsdXN0ZXJpbmcgdmVrdG9yIGluaSBhZGFsYWggcmFuZ2thaWFuIHZla3RvciB5YW5nIGJlcmlzaSBhbmdrYSBjbHVzdGVyLiBEYXJpIGhhc2lsIGluaSwgdmVrdG9yIGJlcmlzaSBhbmdrYSAxIHNhbXBhaSBkZW5nYW4gNSwgbWFrc2ltdW0gc2VzdWFpIGRlbmdhbiBqdW1sYWggY2x1c3RlciB5YW5nIGRpaW5naW5rYW4uDQoNClZla3RvciBpbmkgZGltdWxhaSBkYXJpIGFuZ2thIDEsIHlhbmcgYXJ0aW55YSBkYXRhIHBlcnRhbWEgZGFyaSBkYXRhc2V0IGFrYW4gZGlhbG9rYXNpa2FuIHBhZGEgbm9tb3IgY2x1c3RlciAxIERhcmkgZ2FtYmFyIGp1Z2EgdGVybGloYXQgaXNpIHZla3RvciBrZWR1YSBiZXJubGFpIDMsIGluaSBhcnRpbnlhIGRhdGEga2VkdWEgZGFyaSBkYXRhc2V0IGRpYWxva2FzaWthbiBwYWRhIG5vbW9yIGNsdXN0ZXIgMywgZGFuIHNldGVydXNueWEuIFBvc2lzaSBkYXRhIHRlcmFraGlyIChrZS01MCkgbWVtaWxpa2kgbm9tb3IgY2x1c3RlciA1Lg0KDQoNCg0KIyMgIEFuYWxpc2EgSGFzaWwgQ2x1c3RlciBTaXplDQoNCiFbXShjbHVzdGVyIHNpemUucG5nKQ0KDQpUYWhhcCBiZXJpa3V0bnlhLCBha2FuIGRpYW5hbGlzYSBoYXNpbCBwYWRhIGJhZ2lhbiBwZXJ0YW1hLg0KDQoNCkstbWVhbnMgY2x1c3RlcmluZyB3aXRoIDUgY2x1c3RlcnMgb2Ygc2l6ZXMgNSwgMTIsIDE0LCA5LCAxMA0KDQoNCm5pIGFydGlueWEgZGVuZ2FuIGstbWVhbnMgbnlhIHRlbGFoIG1lbWJhZ2kgZGF0YXNldCBwZWxhbmdnYW4gZGVuZ2FuIDUga2xhc3RlciwgZGltYW5hOg0KDQpDbHVzdGVyIGtlLTEgbWVtaWxpa2kgNSBkYXRhDQpDbHVzdGVyIGtlLTIgbWVtaWxpa2kgMTIgZGF0YQ0KQ2x1c3RlciBrZS0zIG1lbWlsaWtpIDE0IGRhdGENCkNsdXN0ZXIga2UtNCBtZW1pbGlraSA5IGRhdGENCkNsdXN0ZXIga2UtNSBtZW1pbGlraSAxMCBkYXRhDQoNCkRlbmdhbiBqdW1sYWggdG90YWwgNTAgZGF0YSwgeWFuZyBqdWdhIG1lcnVwYWthbiBqdW1sYWggZGF0YSB0b3RhbCBwZWxhbmdnYW4uDQoNCiMjIE1lbGloYXQgRGF0YSBwYWRhIENsdXN0ZXIga2UtTg0KDQpTZWphdWggaW5pLCBiZWx1bSB0ZXJsaWhhdCBkYXRhIGhhc2lsIGtsYXN0ZXIuIERhcGF0IGRpbGloYXQga2xhc3RlciBrZS1OIGF0YXUgaXN0aWxhaG55YSBtZWxpaGF0IGtsYXN0ZXIgZGVuZ2FuIGtvbmRpc2kgdGVydGVudHUuIE1pc2FsIG1lbGloYXQga2xhc3RlciBrZS0xLCBtYWthIGRhbGFtIFIgZGlndW5ha2FuIG9wZXJhdG9yIOKAnD094oCdIGF0YXUgZXF1YWwvc2FtYSBkZW5nYW4uDQoNCmBgYHtyfQ0KY3VzdG9tZXJbd2hpY2goY3VzdG9tZXIkY2x1c3RlciA9PSAxKSxdDQpgYGANCg0KRGltYW5hIGFkYSA1IGRhdGEgZGVuYW5nYW4gc2VsdXJ1aCBkYXRhIGRvbWluYW4gYmVyamVuaXMga2VsYW1pbiBwcmlhIGRhbiB1bXVyIGFudGFyYSA1OCBzL2QgNjQgdGFodW4uIFBlbmdoYXNpbGFuLCBwcm9mZXNpLCBuaWxhaSBiZWxhbmphIGRhbiB0aXBlIHJlc2lkZW4gY3VrdXAgYmVydmFyaWFzaS4NCg0KQmFyaXMga29kZSBkaWF0YXMgeWFpdHUgZGltYW5hIG1lbWFuZ2dpbCBzZW11YSBrb2xvbSBkaSBkYXRhc2V0IOKAnGN1c3RvbWVy4oCdIGRlbmdhbiBrb25kaXNpIGRpbWFuYSBzZXRpYXAgcm93cy9iYXJpcyB5YW5nIG1lbWlsaWtpIGtsYXN0ZXIga2UtMS4NCg0KY29iYSB1bnR1ayBtZXJ1YmFoIHBlcmludGFoIG1lbGloYXQga2xhc3RlciBrZS0yLg0KDQpgYGB7cn0NCmN1c3RvbWVyW3doaWNoKGN1c3RvbWVyJGNsdXN0ZXIgPT0gMiksXQ0KYGBgDQoNCkhhc2lsIGRpYXRhcyB0ZXJsaWhhdCB1bXVyIG1heW9yaXRhcyBzdWRhaCBtYXN1ayB1c2lhIDIwIHRhaHVuIGtlIGF0YXMgZGFuIGtlYmFueWFrYW4gYWRhbGFoIHdpcmFzd2FzdGEgZGFuIHByb2Zlc2lvbmFsLiBEYW4gcmF0YS1yYXRhIG5pbGFpIGJlbGFuamEgYWRhbGFoIHNla2l0YXIgNSBqdXRhIHNhbXBhaSA5IGp1dGEuDQoNCiMjIEFuYWxpc2EgSGFzaWwgQ2x1c3RlciBNZWFucw0KDQohW10oYW5hbGlzYS5wbmcpDQoNCktsYXN0ZXIgbWVhbnMgYWRhbGFoIGhhc2lsIG5pbGFpIHJhdGEtcmF0YSBhdGF1IHRpdGlrIHNlbnRyYWwgKGNlbnRyb2lkKSBkYXJpIHNlbHVydWggdGl0aXAgdGlhcCBrbGFzdGVyLg0KDQpgYGB7cn0NCnNlZ21lbnRhc2kkY2VudGVycw0KYGBgDQoNCkFwYSBwZW5qZWxhc2FuIGhhc2lsIGRpYXRhcz8NCg0KS29sb20gcGVydGFtYSB5YW5nIGJlcmlzaSBhbmdrYSAxIHNhbXBhaSBkZW5nYW4gNSBhZGFsYWggbWV3YWtpbGkgbm9tb3IgY2x1c3Rlci4NCktvbG9tIEplbmlzLktlbGFtaW4uMSBtZW51bmp1a2thbiBuaWxhaSByYXRhLXJhdGEgZGFyaSBkYXRhIGplbmlzIGtlbGFtaW4geWFuZyB0ZWxhaCBkaWtvbnZlcnNpIG1lbmphZGkgbnVtZXJpaywgZGVuZ2FuIGFuZ2thIDEgbWV3YWtpbGkgUHJpYSBkYW4gYW5na2EgMiBtZXdha2lsaSB3YW5pdGEuDQoNCnBhZGEgIGtsYXN0ZXIga2UtMSBiZXJ1cGEgYW5na2EgMS40MCBhcnRpbnlhIGRhdGEgYmVyc2lmYXQgY2FtcHVyYW4gbmFtdW4gY2VuZGVydW5nIGtlIFByaWEgKDEpLiBzZWRhbmdrYW4ga2xhc3RlciBrZS0yIDEuNzUgYmVyaWZhdCBjYW1wdWFybiBuYW11biBjZW5kZXJ1bmcga2Ugd2FuaXRhICgyKS4NCg0KS2VkdWEgaW50ZXJwcmV0YXNpIGFuZ2thIGluaSBzZXN1YWkgZGVuZ2FuIGhhc2lsIHByYWt0ZWsg4oCcTWVsaWhhdCBEYXRhIHBhZGEgQ2x1c3Rlci1O4oCdLg0KDQpLb2xvbSBVbXVyIGFkYWxhaCByZXByZXNlbnRhc2kgZGFyaSBkYXRhc2V0IGF3YWwgdGFucGEgbWVuZ2FsYW1pIGtvbnZlcnNpLiBUZXJsaWhhdCB1bnR1ayBrbGFzdGVyIGtlLTEgdW11ciByYXRhLXJhdGEgYWRhbGFoIDYxIHRhaHVuLCB1bXVyIDMxIHRhaHVuIHVudHVrIGtsYXN0ZXIga2UtMiwgZGFuIHNldGVydXNueWEuDQpLb2xvbSAuMSBtZW51bmp1a2thbiBuaWxhaSByYXRhLXJhdGEgZGF0YSBQcm9mZXNpIHVudHVrIHRpYXAga2xhc3RlciB5YW5nIHRlbGFoIGRpa29udmVyc2kgbWVuamFkaSBudW1lcmlrLg0KDQpBbmdrYSAxLCAyLCAzLCA0IGRhbiA1IG1hc2luZy1tYXNpbmcgbWV3YWtpbGkgcHJvZmVzaTsgV2lyYXN3YXN0YSwgUGVsYWphciwgUHJvZmVzc2lvbmFsLCBJYnUgUnVtYWggVGFuZ2dhIGRhbiBNYWhhc2lzd2EuDQoNClRlcmxpaGF0IHVudHVrIHNlbHVydWgga2xhc3RlciBwcm9mZXNpIGNlbmRlcnVuZyBrZSBQcm9mZXNzaW9uYWwgdGVydXRhbWEgdW50dWsga2xhc3RlciBrZS00Lg0KDQpLb2xvbSBUaXBlLlJlc2lkZW4uMSBtZW51bmp1a2thbiByZXByZXNlbnRhc2kgZGF0YSBUaXBlLlJlc2lkZW4geWFuZyB0ZWxhaCBkaWtvbnZlcnNpIG1lbmphZGkgbnVtZXJpayBkZW5nYW4gYW5na2EgMSBtZXdha2lsaSBDbHVzdGVyIGRhbiAyIG1ld2FraWxpIFNlY3Rvci4gSW5pIGp1Z2EgZGlkYXBhdGthbiBkYXJpIGhhc2lsIGtvbnZlcnNpIGRhdGEgbWVuamFkaSBudW1lcmlrIHBhZGEgc2ViZWx1bW55YS4gVGVybGloYXQgdW50dWsgc2VsdXJ1aCBrbGFzdGVyLCB0ZXJsaWhhdCBkYXRhIGN1a3VwIHRlcnNlYmFyIGFudGFyYSBTZWN0b3IgZGFuIENsdXN0ZXIgdGVydXRhbWEgdW50dWsga2xhc3RlciBrZS00IGRpbWFuYSBuaWxhaSBrb2xvbSBpbmkgZGkgYW5na2EgMS41NTUuDQoNClRlcmFraGlyLCBrb2xvbSBOaWxhaUJlbGFuamFTZXRhaHVuIGN1a3VwIHNpZ25pZmlrYW4gcGVtYmFnaWFubnlhIHVudHVrIHRpYXAga2xhc3Rlci4gRGltYW5hIGtsYXN0ZXIga2UtMSBkYW4ga2UtNCBtZW1pbGlraSBuaWxhaSBiZWxhbmphIGxlYmloIHRpbmdnaSBkaWJhbmRpbmdrYW4ga2V0aWdhIGtsYXN0ZXIgbGFpbm55YS4NCg0KDQoqKkluaSBtdW5na2luIHRhcmdldCBjdXN0b21lciB5YW5nIGJpc2EgbGViaWggZGlzYXNhciBtZWxhbHVpIG1hcmtldGluZyBjYW1wYWlnbiwga2FyZW5hIGtsYXN0ZXIga2UtMSBzYWF0IGluaSBoYW55YSBiZXJpc2kgNSBkYXRhLiBDdWt1cCBrZWNpbCBwcm9wb3JzaW55YSwgZGFuIGluZ2luIGRpdGluZ2thdGthbioqDQoNCg0KIyMgQW5hbGlzYSBIYXNpbCBTdW0gb2YgU3F1YXJlcw0KDQohW10oYW5hbGlzYS5wbmcpDQoNCmBgYHtyfQ0KI1dpdGhpbiBjbHVzdGVyIHN1bSBvZiBzcXVhcmVzIGJ5IGNsdXN0ZXI6DQojWzFdICA1OC4yMTEyMyAxNzQuODUxNjQgMzE2LjczMzY3IDE3MS42NzM3MiAxMDguNDk3MzUNCiMgKGJldHdlZW5fU1MgLyB0b3RhbF9TUyA9ICA5Mi40ICUpDQpgYGANCg0KS29uc2VwIHN1bSBvZiBzcXVhcmVzIChTUykgYWRhbGFoIGp1bWxhaCDigJxqYXJhayBrdWFkcmF04oCdIHBlcmJlZGFhbiB0aWFwIHRpdGlrIGRhdGEgZGVuZ2FuIG1lYW4gYXRhdSBjZW50cm9pZG55YS4gU1MgaW5pIGJpc2EgZGVuZ2FuIG1lYW4gYXRhdSBjZW50cm9pZCB1bnR1ayB0aWFwIGtsYXN0ZXIgYXRhdSBzZWNhcmEga2VzZWx1cnVoYW4gZGF0YS4gU3VtIG9mIHNxdWFyZXMgZGFsYW0gbGl0ZXJhdHVyIGRhdGEgc2NpZW5jZSBsYWluIHNlcmluZyBkaXNlYnV0IGRlbmdhbiBTdW0gb2YgU3F1YXJlZCBFcnJvcnMgKFNTRSkuDQoNClNlbWFraW4gYmVzYXIgbmlsYWkgU1MgbWVueWF0YWthbiBzZW1ha2luIGxlYmFybnlhIHBlcmJlZGFhbiBhbnRhciB0aWFwIHRpdGlrIGRhdGEgZGkgZGFsYW0ga2xhc3RlciB0ZXJzZWJ1dC4NCg0KQmVyZGFzYXJrYW4ga29uc2VwIHRlcnNlYnV0LCBiZXJpa3V0IGFkYWxhaCBwZW5qZWxhc2FuIHVudHVrIGhhc2lsIG91dHB1dCBrbWVhbnMgZGkgYXRhczoNCg0KMS5OaWxhaSA1OC4yMTEyMyBhZGFsYWggU1MgdW50dWsga2xhc3RlciBrZS0xLCAxNzQuODUxNjQgYWRhbGFoIFNTIHVudHVrIGtsYXN0ZXIga2UtMiwgZGFuIHNldGVydXNueWEuIFNlbWFraW4gYmVzYXIgbmlsYWlueWEgYmVycG90ZW5zaSBzZW1ha2luIGJhaWsuDQoNCjIudG90YWxfU1M6IGFkYWxhaCBTUyB1bnR1ayBzZWx1cnVoIHRpdGlrIHRlcmhhZGFwIG5pbGFpIHJhdGEtcmF0YSBnbG9iYWwsIGJ1a2FuIHVudHVrIHBlciBrbGFzdGVyLiBOaWxhaSBpbmkgc2VsYWx1IHRldGFwIGRhbiB0aWRhayB0ZXJwZW5nYXJ1aCBkZW5nYW4ganVtbGFoIGtsYXN0ZXIuDQoNCjMuYmV0d2Vlbl9TUzogYWRhbGFoIHRvdGFsX1NTIGRpa3VyYW5naSBkZW5nYW4ganVtbGFoIG5pbGFpIFNTIHNlbHVydWgga2xhc3Rlci4NCihiZXR3ZWVuX1NTIC8gdG90YWxfU1MpIGFkYWxhaCByYXNpbyBhbnRhcmEgYmV0d2Vlbl9TUyBkaWJhZ2kgZGVuZ2FuIHRvdGFsX1NTLiBTZW1ha2luIGJlc2FyIHBlcnNlbnRhc2VueWEsIHVtdW1ueWEgc2VtYWtpbiBiYWlrLg0KDQpJbmkgYWRhbGFoIG1ldHJpayB5YW5nIGJpc2EgZGlndW5ha2FuIHVudHVrIG1lbmphd2FiIHNlYmVyYXBhIGJhaWsganVtbGFoIGtsYXN0ZXIgeWFuZyBkaWJlbnR1az8gQXBha2FoIGRpYmFnaSAyLCA1LCAxMCBhdGF1IDMwPw0KDQpUZWtuaWsgcGVuZ2d1bmFhbiBtZXRyaWsgaW5pIGN1a3VwIHBhbmphbmcsIG5hbXVuIHVudHVrIGtlcGVudGluZ2FuIHByYWt0aXMga2FsaSBpbmkgaGFueWEgbWVsaWhhdCBwZXJiZWRhYW4gbmlsYWkgaW5pIA0KDQpgYGB7cn0NCiMgTWVtYmFuZGluZ2thbiBkZW5nYW4gMiBjbHVzdGVyIGttZWFucywgbWFzaW5nLW1hc2luZyAyIGRhbiA1DQpzZXQuc2VlZCgxMDApDQprbWVhbnMoeCA9IGN1c3RvbWVyW2ZpZWxkX3lhbmdfZGlndW5ha2FuXSwgY2VudGVycyA9IDIsIG5zdGFydCA9IDI1KQ0KYGBgDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoMTAwKQ0Ka21lYW5zKHggPSBjdXN0b21lcltmaWVsZF95YW5nX2RpZ3VuYWthbl0sIGNlbnRlcnMgPSA1LCBuc3RhcnQgPSAyNSkNCmBgYA0KDQpQZW5qZWxhc2FuIGhhc2lsIGRpYXRhcyB1bnR1ayBLPTIgdG90YWxfc3MgbnlhIDcyLjYgJSBzZWRhbmdha2FuIGhhc2lsIGs9NSB0b3RhbC1zcyBueWEgOTIuNCAlLg0KDQpUZXJsaWhhdCB1bnR1ayAyIGtsYXN0ZXIgKGs9MiksIFNTIHBlciBrbGFzdGVyIGxlYmloIGJlc2FyIGRpYmFuZGluZ2thbiBqaWthIGRhdGEgZGliYWdpIG1lbmphZGkgNSBrbGFzdGVyIChrPTUpLiBQZXJoYXRpa2FuIGp1Z2EgcGVyc2VudGFzZSByYXNpbyBhbnRhcmEgYmV0d2Vlbl9TUyBkYW4gdG90YWxfU1MsIGRpbWFuYSBrPTUganVnYSBsZWJpaCBiZXNhci4NCg0KDQojIE1lbmVudHVrYW4gSnVtbGFoIENsdXN0ZXIgVGVyYmFpaw0KDQojIyAgSnVtbGFoIENsdXN0ZXIgZGFuIFN1bSBvZiBTcXVhcmUgKFNTKSBkYW4gR3JhZmlrIEVsYm93IEVmZmVjdA0KDQohW10oamFtYWwucG5nKQ0KDQpEYXJpIGluZm9ybWFzaSB5YW5nIGRpaGFzaWxrYW4gb2xlaCBmdW5nc2kga21lYW5zLCBtZXRyaWNrIFN1bSBvZiBTcXVhcmVzIChTUykgYXRhdSBzZXJpbmcgZGlzZWJ1dCBTdW0gb2YgU3F1YXJlZCBFcnJvcnMgKFNTRSkgc2FuZ2F0IHBlbnRpbmcgdW50dWsgZGlqYWRpa2FuIGRhc2FyIGRhbGFtIG1lbmVudHVrYW4ganVtbGFoIGNsdXN0ZXIgeWFuZyBwYWxpbmcgb3B0aW1hbC4NCg0KU2VjYXJhIHRlb3JpdGlzLCBiZXJpa3V0IGFkYWxhaCBiZWJlcmFwYSBoYWwgeWFuZyBiaXNhIGRpYWFtYXRpIGRlbmdhbiBTUzoNCg0KYS5TZW1ha2luIHNlZGlraXQganVtbGFoIGNsdXN0ZXIgeWFuZyBkaWhhc2lsa2FuIG1ha2Egc2VtYWtpbiBiZXNhciBuaWxhaSBTUy4NCg0KYi5CZWdpdHUganVnYSBzZWJhbGlrbnlhLCBzZW1ha2luIGJhbnlhayBqdW1sYWggY2x1c3RlciB5YW5nIGRpaGFzaWxrYW4gbWFrYSBzZW1ha2luIGtlY2lsIG5pbGFpIFNTIG55YS4NCg0KYy5LYXJlbmEgc2lmYXRueWEga3VhZHJhdGlrLCBqaWthIHRlcmRhcGF0IHBlcmJlZGFhbiB5YW5nIGN1a3VwIHNpZ25pZmlrYW4gYW50YXJhIHRpYXAga29tYmluYXNpIGNsdXN0ZXIgbWFrYSBwZXJiZWRhYW4gbmlsYWkgU1MgYWthbiBzZW1ha2luIGJlc2FyLg0KRGFuIHNlaXJpbmcgYmVydGFtYmFobnlhIGp1bWxhaCBjbHVzdGVyLCBwZXJiZWRhYW4gdGlhcCBTUyBpbmkgYWthbiBzZW1ha2luIGtlY2lsLg0KDQpKaWthIGRpbWFzdWtrYW4ga2UgZGFsYW0gZ3JhZmlrIGdhcmlzLCBtYWthIHBsb3R0aW5nIGRhcmkgdG90YWwgU1MgdW50dWsgdGlhcCBjbHVzdGVyIGJlcmJlbnR1ayBzZWJhZ2FpIGJlcmlrdXQuDQoNCmBgYHtyfQ0KbGlicmFyeShnZ3Bsb3QyKQ0KDQpzZXQuc2VlZCgxMDApDQpzc2UgPC0gc2FwcGx5KDE6MTAsIGZ1bmN0aW9uKHBhcmFtX2spe2ttZWFucyhjdXN0b21lcltmaWVsZF95YW5nX2RpZ3VuYWthbl0sIHBhcmFtX2ssIG5zdGFydD0yNSkkdG90LndpdGhpbnNzfSkNCg0KanVtbGFoX2NsdXN0ZXJfbWF4IDwtIDEwDQpzc2RhdGEgPSBkYXRhLmZyYW1lKGNsdXN0ZXIgPSBjKDE6anVtbGFoX2NsdXN0ZXJfbWF4KSwgc3NlKQ0KDQpnZ3Bsb3Qoc3NkYXRhLCBhZXMoeCA9IGNsdXN0ZXIseSA9IHNzZSkpICsNCiAgZ2VvbV9saW5lKGNvbG9yID0gInJlZCIpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgdGhlbWUocGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJibGFjayIpKSArDQogIGxhYnModGl0bGUgPSAiU3VtIG9mIFNxdWFyZXMiLA0KICAgICAgIHN1YnRpdGxlID0gIlN1bSBvZiBTcXVhcmVzIChTUykgcGFkYSByYW5nZSBjbHVzdGVyIDE6MTAiLA0KICAgICAgIGNhcHRpb24gPSAiU3VtYmVyIGRhdGE6IERRTGFiIiwNCiAgICAgICB4ID0gIkp1bWxhaCBDbHVzdGVyIiwNCiAgICAgICB5ID0gIldpdGhpbiBDbHVzdGVyIFN1bSBvZiBTcXVhcmVzIikgKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsID0gZm9ybWF0KHJvdW5kKHNzZSwgMiksIG5zbWFsbCA9IDIpKSwNCiAgICAgICAgICAgIGhqdXN0ID0gMC4xLA0KICAgICAgICAgICAgdmp1c3QgPSAtMC44LA0KICAgICAgICAgICAgc2l6ZSA9IDIuNSkgKw0KICBzY2FsZV94X2Rpc2NyZXRlKGxpbWl0cyA9IGMoMTpqdW1sYWhfY2x1c3Rlcl9tYXgpKQ0KYGBgDQoNCg0Ka2V0ZXJhbmdhbiBkYXRhIGdyYWZpayBkaWFhdGFzIDoNCg0KVGl0aWsgcGFsaW5nIGtpcmkgYWRhbGFoIGp1bWxhaCBTUyB1bnR1ayAxIGp1bWxhaCBjbHVzdGVyLCB0aXRpayBrZWR1YSBhZGFsYWggdW50dWsgMiBqdW1sYWggY2x1c3RlciwgZGFuIHNldGVydXNueWEuIFBlcmhhdGlrYW4gc2VtYWtpbiBrZSBrYW5hbiBwZXJiZWRhYW4gamFyYWsgYW50YXIgdGlhcCB0aXRpayBzZW1ha2luIG1lbmdlY2lsLg0KDQpHcmFmaWsgZ2FyaXMgaW5pIG1lbWlsaWtpIGJlbnR1ayBzZXBlcnRpIHNpa3UgdGFuZ2FuLCBkYW4gdW50dWsgb3B0aW1hbCBqdW1sYWggY2x1c3RlciBiaWFzYW55YSBkZW5nYW4gbWVuZ2FtYmlsIHRpdGlrIHNpa3VueWEuIFBhZGEgY29udG9oIGRpIGF0YXMgYmlzYSBtZW5nYW1iaWwgNCBhdGF1IDUuDQoNClByb3NlcyBwZW5nYW1iaWxhbiBrZXB1dHVzYW4gYmVyZGFzYXJrYW4gcGxvdHRpbmcgc2lrdSBpbmkgYmlhc2EgZGlzZWJ1dCBFbGJvdyBFZmZlY3QgYXRhdSBFbGJvdyBNZXRob2QuDQoNCg0KIyMgS2VzaW1wdWxhbg0KDQohW10oa2VzaW1wdWxhbi5wbmcpDQpEZW5nYW4gbWVtYW5mYWF0a2FuIG5pbGFpIFN1bSBvZiBTcXVhcmVzIChTUykgYXRhdSBTdW0gb2YgU3F1YXJlZCBFcnJvcnMgKFNTRSkgZGFwYXQgbWVuZ2FtYmlsIGtlcHV0dXNhbiBqdW1sYWggc2VnbWVudGFzaSBvcHRpbWFsIHlhbmcgZGlndW5ha2FuLg0KDQpJbmkgZGlsYWt1a2FuIGRlbmdhbiBtZW1idWF0IHNpbXVsYXNpIGl0ZXJhc2kganVtbGFoIGtsYXN0ZXIgZGFyaSAxIHNhbXBhaSBkZW5nYW4ganVtbGFoIG1ha3NpbXVtIHlhbmcgZGlpbmdpbmthbi4gUGFkYSBrYXN1cywgbWVuZ2d1bmFrYW4gYW5na2EgaXRlcmFzaSAxIHNhbXBhaSBkZW5nYW4gMTAuDQoNClNldGVsYWggbWVuZGFwYXRrYW4gbmlsYWkgU1MgZGFyaSB0aWFwIGp1bWxhaCBrbGFzdGVyLCBsYW5nc3VuZyBiaXNhIHBsb3R0aW5nIGtlIGdyYWZpayBnYXJpcyBkYW4gbWVuZ2d1bmFrYW4gZWxib3cgbWV0aG9kIHVudHVrIG1lbmVudHVrYW4ganVtbGFoIGtsYXN0ZXIgb3B0aW1hbC4NCg0KDQojIOKAnFBlbWFrZXRhbuKAnSBNb2RlbCBLLU1lYW5zDQoNCiMjIFBlbmdhbnRhcg0KDQohW10ocGVuZ2FudGFyLnBuZykNCg0KUmVmZXJlbnNpIGhhc2lsIGtvbnZlcnNpIGRhbiBvYmplayBrbWVhbnMgaW5pIHN1cGF5YSBiaXNhIGRpZ3VuYWthbiB1bnR1ayBtZW5nb2xhaCBkYXRhIGJhcnUgZGFuIGJlcmd1bmEgZGkgYmlzbmlzLg0KDQpVbnR1ayBpbmkgdGFoYXBhbm55YSBhZGFsYWggc2ViYWdhaSBiZXJpa3V0Og0KDQphLiBNZW5hbWFrYW4ga2xhc3RlciBkZW5nYW4ga2FyYWt0ZXJpc3RpayB5YW5nIGxlYmloIG11ZGFoIGRpbWVuZ2VydGkuIA0KDQpiLlBlbmFtYWFuIGluaSBkaXNpbXBhbiBkYWxhbSB2YXJpYWJlbCDigJxTZWdtZW4uY3VzdG9tZXLigJ0uDQoNCmMuTWVuZ2dhYnVuZ2thbiB2YXJpYWJsZSBTZWdtZW4uUGVsYW5nZ2FuLCBQcm9mZXNpLCBKZW5pcy5LZWxhbWluLCBUaXBlLlJlc2lkZW4sIGRhbiBTZWdtZW50YXNpIGtlIGRhbGFtIHNhdHUgb2JqZWsgYmVydGlwZSBsaXN0IGtlIGRhbGFtIHZhcmlhYmVsIOKAnElkZW50aXRhcy5DbHVzdGVy4oCdLg0KDQpkLk1lbnlpbXBhbiBvYmplayBJZGVudGl0YXMuQ2x1c3RlciBkYWxhbSBiZW50dWsgZmlsZSBzZWhpbmdnYSBkYXBhdCBkaWd1bmFrYW4ga2VtdWRpYW4sIGluaSBiaXNhIGRpc2VidXQgbW9kZWwuDQoNCg0KIyMgTWVuYW1ha2FuIFNlZ21lbg0KDQohW10obWVuZ2FidW5na2FuLnBuZykNCg0KUGFkYSBiYWdpYW4gaW5pLCBkaW1hbmEgYWthbiBtZW5hbWFrYW4gc2VnbWVuIHNlc3VhaSBkZW5nYW4ga2FyYWt0ZXJpc3Rpa255YS4gVW50dWsgbWVtYmFudHUsIGdhbWJhciBiZXJpa3V0IG1lbnVuanVra2FuIG5pbGFpIG1lYW4gdGlhcCBrb2xvbSB5YW5nIGRpZ3VuYWthbiB0aWFwIGtsYXN0ZXIgZGFuIGp1Z2EgbmlsYWkga29sb20gc2ViZWx1bSBrb252ZXJzaS4NCg0KYGBge3J9DQpzZWdtZW50YXNpJGNlbnRlcnMNCmBgYA0KDQpCZXJpa3V0IG1lbmFtYWthbiBrbGFzdGVyIDEgcy9kIDU6DQoNCg0KQ2x1c3RlciAxIDogRGlhbW9uZCBTZW5pb3IgTWVtYmVyOiBhbGFzYW5ueWEgYWRhbGFoIGthcmVuYSB1bXVybnlhIHJhdGEtcmF0YSBhZGFsYWggNjEgdGFodW4gZGFuIHBlbWJlbGFuamFhbiBkaSBhdGFzIDgganV0YS4NCg0KQ2x1c3RlciAyIDogR29sZCBZb3VuZyBQcm9mZXNzaW9uYWw6IGFsYXNhbm55YSBhZGFsYWgga2FyZW5hIHVtdXJueWEgcmF0YS1yYXRhIGFkYWxhaCAzMSB0YWh1biwgcHJvZmVzc2lvbmFsIGRhbiBwZW1iZWxhbmphYW4gY3VrdXAgYmVzYXIuDQoNCkNsdXN0ZXIgMyA6IFNpbHZlciBZb3V0aCBHYWxzOiBhbGFzYW5ueWEgYWRhbGFoIGthcmVuYSB1bXVybnlhIHJhdGEtcmF0YSBhZGFsYWggMjAsIHdhbml0YSBzZW11YSwgcHJvZmVzaW55YSBiZXJjYW1wdXIgYW50YXIgcGVsYWphciBkYW4gcHJvZmVzc2lvbmFsIHNlcnRhIHBlbWJlbGFuamFhbiBzZWtpdGFyIDYganV0YS4NCg0KQ2x1c3RlciA0IDogRGlhbW9uZCBQcm9mZXNpb25hbDogYWxhc2FubnlhIGFkYWxhaCBrYXJlbmEgdW11cm55YSByYXRhLXJhdGEgYWRhbGFoIDQyIHRhaHVuLCBwZW1iZWxhbmphYW4gcGFsaW5nIHRpbmdnaSBkYW4gc2VtdWFueWEgcHJvZmVzc2lvbmFsLg0KDQpDbHVzdGVyIDUgOiBTaWx2ZXIgTWlkIFByb2Zlc3Npb25hbDogYWxhc2FubnlhIGFkYWxhaCBrYXJlbmEgdW11cm55YSByYXRhLXJhdGEgYWRhbGFoIDUyIHRhaHVuIGRhbiBwZW1iZWxhbmphYW4gc2VraXRhciA2IGp1dGEuDQoNCk5hbWEtbmFtYSBrbGFzdGVyIGluaSBiaXNhIGRpbWFzdWtrYW4ga2UgZGFsYW0gZGF0YSBmcmFtZS4gQWdhciBzZXRpYXAga2xhc3RlciBtZW1pbGlraSBuYW1hLW5hbWFueWEgc2VuZGlyaS4gDQoNCmBgYHtyfQ0Kc2VnbWVuX2N1c3RvbWVyIDwtIGRhdGEuZnJhbWUoY2x1c3RlciA9IGMoMSwyLDMsNCw1KSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBOYW1hLlNlZ21lbiA9IGMoIkRpYW1vbmQgU2VuaW9yIE1lbWJlciIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJHb2xkIFlvdW5nIFByb2Zlc3Npb25hbCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTaWx2ZXIgWW91bmcgUHJvZmVzc2lvbmFsIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkRpYW1vbmQgUHJvZmVzc2lvbmFsIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlNpbHZlciBNaWQgUHJvZmVzc2lvbmFsIg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQ0KDQpwcmludChzZWdtZW5fY3VzdG9tZXIpDQpgYGANCg0KIyMgTWVuZ2dhYnVuZ2thbiBSZWZlcmVuc2kNCg0KIVtdKG1lbmdhYnVuZ2thbi5wbmcpDQoNCkFrYW4gc2FuZ2F0IGJhaWsgamlrYSBzZW11YW55YSBkaWdhYnVuZ2thbiBkaSBzYXR1IHZhcmlhYmVsIGRlbmdhbiB0aXBlIGxpc3QsIGRhbiBpbmkgYWthbiBqYWRpIG1vZGVsIHlhbmcgZGFwYXQgZGlzaW1wYW4ga2UgZGFsYW0gZmlsZSBkYW4gZGlndW5ha2FuIGtldGlrYSBkaXBlcmx1a2FuLiBMYXlha255YSBzZXBlcnRpIGZ1bmN0aW9uIHBhZGEgcGVtcm9ncmFtYW4gYXRhdSBzdG9yZWQgcHJvY2VkdXJlIHBhZGEgU1FMLg0KDQpgYGB7cn0NCiNNZW5nZ2FidW5na2FuIHNlbHVydWggYXNldCBrZSBkYWxhbSB2YXJpYWJsZSBJZGVudGl0YXMuQ2x1c3RlciBkZW5nYW4gdGlwZSAibGlzdCINCg0KI01lbmdnYWJ1bmdrYW4gc2VsdXJ1aCBhc2V0IGtlIGRhbGFtIHZhcmlhYmxlIElkZW50aXRhcy5DbHVzdGVyIGRlbmdhbiB0aXBlICJsaXN0Ig0KDQpJZGVudGl0YXMuQ2x1c3RlciA8LSBsaXN0KFByb2Zlc2kgPSBQcm9mZXNpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBKZW5pcy5LZWxhbWluID0gSmVuaXMuS2VsYW1pbiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgVGlwZS5SZXNpZGVuID0gVGlwZS5SZXNpZGVuLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBzZWdtZW50YXNpID0gc2VnbWVudGFzaSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgU2VnbWVuX2N1c3RvbWVyID0gc2VnbWVuX2N1c3RvbWVyLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBmaWVsZF95YW5nX2RpZ3VuYWthbiA9IGZpZWxkX3lhbmdfZGlndW5ha2FuDQopDQoNCg0KSWRlbnRpdGFzLkNsdXN0ZXINCmBgYA0KDQojIyBNZW55aW1wYW4gT2JqZWsgZGFsYW0gQmVudHVrIEZpbGUNCg0KDQohW10ocGVyc2lwYW4gZGF0YS5wbmcpDQoNCk9iamVrIHlhbmcgc3VkYWggZGlnYWJ1bmdrYW4gcGFkYSBzZWJlbHVtbnlhIHN1ZGFoIG1lbWlsaWtpIHNlbXVhIGFzZXQgeWFuZyBkaXBlcmx1a2FuIHVudHVrIG1lbmdhbG9rYXNpa2FuIGRhdGEgYmFydSBrZSBzZWdtZW4geWFuZyBzZXN1YWkuDQoNClVudHVrIG1lbnlpbXBhbiBvYmplayBpbmkga2UgZGFsYW0gZmlsZSBtZW5nZ3VuYWthbiBmdW5nc2kgc2F2ZVJEUygpLiBGaWxlIGluaSBrZW11ZGlhbiBkYXBhdCBkaWJ1a2Ega2VtYmFsaSBzZWJhZ2FpIG9iamVrIGtlIGRlcGFubnlhLg0KDQpgYGB7cn0NCnNhdmVSRFMob2JqZWN0ID0gSWRlbnRpdGFzLkNsdXN0ZXIsIGZpbGUgPSAiY2x1c3Rlci5yZHMiKQ0KYGBgDQoNCkNhdGF0YW46IEhhc2lsIGZpbGUgYWthbiBkaXNpbXBhbiBkaSBkaXJla3RvcmkgeWFuZyBzYW1hIGRpbWFuYSBwcm9qZWN0IFIgZGlzaW1wYW4gZGkgbG9jYWwgZmlsZS4NCg0KIyMgS2VzaW1wdWxhbg0KDQohW10oa2VzaW1wdWxhbi5wbmcpDQoNCk1vZGVsIGluaSBhZGFsYWggb2JqZWsgeWFuZyBiaXNhIGRpZ3VuYWthbiB1bnR1ayBtZW5nb2xhaCBkYXRhIGJhcnUgZGFuIHRlcmRpcmkgZGFyaSBvYmplayBrbWVhbnMsIHZhcmlhYmxlIHJlZmVyZW5zaSBoYXNpbCBrb252ZXJzaSB0ZWtzIGtlIG51bWVyaWssIGRhbiBqdWdhIHBlbmFtYWFuIGtsYXN0ZXIuDQoNCg0KIyBNZW5nb3BlcmFzaW9uYWxrYW4gTW9kZWwgSy1NZWFucw0KDQojIyBQZW5nYW50YXINCg0KIVtdKHBlbmdhbnRhci5wbmcpDQoNCmJhZ2FpbWFuYSBkYXRhIGJhcnUgZGFwYXQgb3RvbWF0aXMgbWVtYmFudHUgdGltIG1hcmtldGluZyBkYW4gQ1JNIHVudHVrIG1lbmdpZGVudGlmaWthc2kgc2VnbWVuIG1hbmEgcGVsYW5nZ2FuIHRlcnNlYnV0IGJlcmFkYSBkZW5nYW4gY2VwYXQuDQoNCkRlbmdhbiBrZWNlcGF0YW4gaWRlbnRpZmlrYXNpLCBtYWthIG9yZ2FuaXNhc2kgYXRhdSBiaXNuaXMgZGFwYXQgZGVuZ2FuIGNlcGF0IGJlcmdlcmFrIGRlbmdhbiBwZXNhbiBtYXJrZXRpbmcgeWFuZyBlZmVrdGlmIGRhbiBtZW1lbmFuZ2thbiBwZXJzYWluZ2FuLg0KDQojIyBEYXRhIEJhcnUNCg0KIVtdKGRhdGFiYXJ1LnBuZykNCg0KUGFkYSB0ZWtzIHNlYmVsdW1ueWEsIGRpc2VidXRrYW4gZGF0YSBwZWxhbmdnYW4gYmFydSBoYXJ1cyBjZXBhdCBkaXBldGFrYW4ga2Ugc2VnbWVuLg0KDQpEZW5nYW4gYXN1bXNpIHRpYXAgZGF0YSBwZWxhbmdnYW4gYmFydSBkaWlucHV0IGtlIGRhbGFtIHNpc3RlbSwgbWFrYSBwZW5nb2xhaGFuIGFkYWxhaCBwZXIgcmVjb3JkL2JhcmlzLiBLYWxpIGluaSwgYWthbiBkaWJ1YXQgZGF0YS5mcmFtZSBkZW5nYW4gc2F0dSBkYXRhIGRpbWFuYSBuYW1hLW5hbWEga29sb21ueWEgcGVyc2lzIGRlbmdhbiBkYXRhc2V0IGF3YWwuDQoNCmBgYHtyfQ0KSWRlbnRpdGFzLkNsdXN0ZXIgPC0gbGlzdChQcm9mZXNpID0gUHJvZmVzaSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgSmVuaXMuS2VsYW1pbiA9IEplbmlzLktlbGFtaW4sDQogICAgICAgICAgICAgICAgICAgICAgICAgIFRpcGUuUmVzaWRlbiA9IFRpcGUuUmVzaWRlbiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgc2VnbWVudGFzaSA9IHNlZ21lbnRhc2ksDQogICAgICAgICAgICAgICAgICAgICAgICAgIFNlZ21lbl9jdXN0b21lciA9IHNlZ21lbl9jdXN0b21lciwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgZmllbGRfeWFuZ19kaWd1bmFrYW4gPSBmaWVsZF95YW5nX2RpZ3VuYWthbg0KKQ0KDQpkYXRhYmFydSA8LSBkYXRhLmZyYW1lKEN1c3RvbWVyX0lEID0gIkNVU1QtMTAwIiwNCiAgICAgICAgICAgICAgICAgICAgICAgTmFtYS5QZWxhbmdnYW4gPSAiSmFtYWxsdWRpbiIsDQogICAgICAgICAgICAgICAgICAgICAgIFVtdXIgPSAyMCwNCiAgICAgICAgICAgICAgICAgICAgICAgSmVuaXMuS2VsYW1pbiA9ICJQcmlhIiwNCiAgICAgICAgICAgICAgICAgICAgICAgUHJvZmVzaSA9ICJQZWxhamFyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgVGlwZS5SZXNpZGVuID0gIkNsdXN0ZXIiLA0KICAgICAgICAgICAgICAgICAgICAgICBOaWxhaUJlbGFuamFTZXRhaHVuID0gMy41KQ0KZGF0YWJhcnUNCg0KYGBgDQoNCg0KIyMgTWVtYnVhdCBPYmplayBDbHVzdGVyaW5nIGRhcmkgRmlsZQ0KDQohW10oY2x1c3RlcmluZy5wbmcpDQoNCk1ha3N1ZCBkaXNpbmkgYWRhbGFoIG1lbWJ1a2EvbWVtYmFjYSBmaWxlIHlhbmcgdGVsYWggZGlzaW1wYW4gc2ltcGFuIHNlYmVsdW1ueWEgZGVuZ2FuIHNlYnVhaCBmdW5nc2ksIGRhbiBzdXBheWEgZGlrZW5hbGkgZGkgUiBzZWJhZ2FpIG9iamVrIHlhbmcgYWthbiBkaWd1bmFrYW4gdW50dWsgbWVuZ29sYWggZGF0YSBiYXJ1Lg0KDQpVbnR1ayBtZW1idWthIGZpbGUgdGVyc2VidXQsIG1lbmdndW5ha2FuIGZ1bmdzaSByZWFkUkRTKCkuDQoNClBlcmludGFobnlhIHNhbmdhdCBzZWRlcmhhbmEsIGJlcmlrdXQgYWRhbGFoIHVudHVrIG1lbWJ1a2EgZmlsZSBjbHVzdGVyLnJkcyB5YW5nIHRlbGFoIGRpc2ltcGFuIHNlYmVsdW1ueWEuDQoNCmBgYHtyfQ0Kc2F2ZVJEUyhvYmplY3QgPSBJZGVudGl0YXMuQ2x1c3RlciwgZmlsZSA9ICJjbHVzdGVyLnJkcyIpDQoNCklkZW50aXRhcy5DbHVzdGVyIDwtIHJlYWRSRFMoZmlsZSA9ICJjbHVzdGVyLnJkcyIpDQoNCklkZW50aXRhcy5DbHVzdGVyDQoNCmBgYA0KDQoNCiMjIE1lcmdlIGRlbmdhbiBEYXRhIFJlZmVyZW5zaQ0KDQohW10oYW5hbGlzYS5wbmcpDQoNCkRlbmdhbiBhZGFueWEgZGF0YSBiYXJ1IGRhbiBvYmplayB5YW5nIGJlcmlzaSBkYXRhIHJlZmVyZW5zaSB0ZWxhaCBkaWJhY2Ega2VtYmFsaSwgYmlzYSBtZW5nZ2FidW5na2FuIGRhdGEgYmFydSBpbmkgdW50dWsgbWVuZGFwYXRrYW4ga29udmVyc2kgbnVtZXJpayBkYXJpIGZpZWxkIEplbmlzLktlbGFtaW4sIFByb2Zlc2kgZGFuIFRpcGUuUmVzaWRlbi4NCg0KVHVqdWFubnlhIGFkYWxhaCBhZ2FyIGJpc2EgbWVuY2FyaSBzZWdtZW4gcGVsYW5nZ2FubnlhIGRlbmdhbiBkYXRhIG51bWVyaWsgaGFzaWwgcGVuZ2dhYnVuZ2FuLg0KDQpgYGB7cn0NCg0KbWVyZ2UoZGF0YWJhcnUsIElkZW50aXRhcy5DbHVzdGVyJFByb2Zlc2kpDQoNCmBgYA0KDQpgYGB7cn0NCg0KZGF0YWJhcnUgPC0gbWVyZ2UoZGF0YWJhcnUsIElkZW50aXRhcy5DbHVzdGVyJFByb2Zlc2kpDQpkYXRhYmFydSA8LSBtZXJnZShkYXRhYmFydSwgSWRlbnRpdGFzLkNsdXN0ZXIkSmVuaXMuS2VsYW1pbikNCmRhdGFiYXJ1IDwtIG1lcmdlKGRhdGFiYXJ1LCBJZGVudGl0YXMuQ2x1c3RlciRUaXBlLlJlc2lkZW4pDQoNCmRhdGFiYXJ1DQoNCmBgYA0KDQojIyBNZW5lbnR1a2FuIENsdXN0ZXINCg0KIVtdKG1lbmV0dWthbiB0ZXJiYWlrLnBuZykNCg0KVGFoYXAgaW5pIG1lcnVwYWthbiB5YW5nIHRlcnBlbnRpbmcgYmFnaSBiaXNuaXM7IGRhdGEgYmFydSBpbmkgbWFzdWsga2Ugc2VnbWVuIG1hbmE/DQoNCk11ZGFoISBZYWl0dSBkZW5nYW4gdGFoYXBhbiBiZXJpa3V0Og0KDQphLm1lbmNhcmkgamFyYWsga3VhZHJhdCBtaW5pbXVtIGF0YXUgdGVyZGVrYXQNCg0KYi5kYXJpIGtvbG9tIG51bWVyaWsgZGF0YSBiYXJ1IHRlcnNlYnV0DQoNCmMua2UgY2VudHJvaWQgKG5pbGFpIHJhdGEtcmF0YSkga29sb20gdGVya2FpdCBkYXJpIHNlbHVydWgga2xhc3RlciB5YW5nIGFkYQ0KDQpgYGB7cn0NCg0KSWRlbnRpdGFzLkNsdXN0ZXIkU2VnbWVuX2N1c3RvbWVyW3doaWNoLm1pbihzYXBwbHkoMTo1LCBmdW5jdGlvbih4KSBzdW0oKGRhdGFiYXJ1W0lkZW50aXRhcy5DbHVzdGVyJGZpZWxkX3lhbmdfZGlndW5ha2FuXSAtIElkZW50aXRhcy5DbHVzdGVyJHNlZ21lbnRhc2kkY2VudGVyc1t4LF0pXjIgKSkpLF0NCg0KYGBgDQoNClRlcmxpaGF0IGxlYmloIGplbGFzIHRlcm55YXRhIHNheWEgbWFzdWsgZGFyaSBrb2xvbSBOYW1hLlNlZ21lbiBrZSBhbmdrYSAzL2tsYXN0ZXIga2UtMyBiZXJpc2kgbmlsYWkgKipTaWx2ZXIgWW91bmcgUHJvZmVzc2lvbmFsKiouDQoNCg0KIyMgS2VzaW1wdWxhbg0KDQohW10oa2VzaW1wdWxhbi5wbmcpDQoNClByYWt0ZWsgdGVyYWtoaXIgbWVudW5qdWtrYW4gYmFnYWltYW5hIGRhdGEgcGVsYW5nZ2FuIGJhcnUgZGlhbmFsaXNhIG9sZWggbW9kZWwgeWFuZyBkaWJ1YXQgZGFuIG1lbmdlbHVhcmthbiBub21vciBrbGFzdGVyIGF0YXUgc2VnbWVuLg==