Berbelanja di Supermarket

Market Basket Analysis (MBA) adalah bagian dari aturan asosiasi yang berguna untuk memperlihatkan pola perilaku pembelian konsumen. Oleh karena itu, analisis ini dinamakan analisis keranjang pasar. Aturan asosiasi ini diharapkan memenuhi syarat minimum untuk support dan confidence (minimum support dan minimum confidence).

Nah, untuk implementasi MBA dengan software ini, maka diperlukan suatu package yang bernama ‘arules’.

Panggil library ‘arules’

library(arules)

Setelah data sudah terpanggil, maka selanjutnya memanggil data yang akan digunakan dalam pengaplikasian MBA.

transaksi_tabular <- read.csv("https://storage.googleapis.com/dqlab-dataset/data_transaksi.txt", sep="\t")
transaksi_tabular

Data yang digunakan ini masih berbentuk data frame, sementara dalam package data harus berberntuk transaction. Oleh karena itu, kita harus mengubah data yang sebelumnya yaitu data frame menjadi data transaction menggunakan fungsi read.transactions().

transaksi <- read.transactions(file="https://storage.googleapis.com/dqlab-dataset/data_transaksi.txt", format="single", sep="\t", cols=c(1,2), skip=1)

transaksi
transactions in sparse format with
 10 transactions (rows) and
 4 items (columns)

Menampilkan Daftar Kode Transaksi

transaksi@itemInfo

Tampilan Transaksi dalam bentuk Matrix

t(transaksi@data)
10 x 4 sparse Matrix of class "ngCMatrix"
             
 [1,] . | | |
 [2,] | . . |
 [3,] . | | .
 [4,] . . | |
 [5,] | . | |
 [6,] . | . |
 [7,] . | | |
 [8,] . . . |
 [9,] | . . |
[10,] | | | .

Urutan 1 - 4 adalah urutan dalam labels

4 x 10 : artinya terdapat 4 item dan 10 transaksi. [1,] s/d [4,] : artinya index yang mewakili tiap item dalam transaksi.

  • Simbol tanda titik . menunjukkan item tidak ada di transaksi.
  • Simbol garis lurus | menunjukkan bahwa item ada di transaksi.

Selanjutnya menghitung frekuensi dari data transaksi untuk melihat pola dari data.

Item Frequency

data_item <- itemFrequency(transaksi, type="absolute")
data_item
     Gula  Pet Food     Sirup Teh Celup 
        4         5         6         8 

Statistik Top 3

#Melakukan sorting pada data_item
data_item <- sort(data_item, decreasing = TRUE)

#Mengambil 3 item pertama
data_item <- data_item[1:3]

#Konversi data_item menjadi data frame dengan kolom Nama_Produk dan Jumlah
data_item <- data.frame("Nama Produk"=names(data_item), "Jumlah"=data_item, row.names=NULL)
data_item

Insight

Hasil akhirnya frekuensi terbanyak adalah “Teh Celup” yang kemudian disusul oleh “Sirup” dan “Pet Food”.

Output Statistik Top 3 Sebagai File

write.csv(data_item, file="top3_item_retail.txt", eol = "\r\n")

Grafik Item Frequency

itemFrequencyPlot(transaksi)

Melihat Itemset per Transaksi dengan Inspect

Inspect digunakan untuk melihat notasi itemset

inspect(transaksi)

Algoritma apriori adalah algoritma yang digunakan untuk mendapatkan aturan asosiasi. Prinsipnya seperti aturan implikasi yaitu ‘jika-maka’.

Beberapa istilah dalam algoritma apriori yaitu sebagai berikut:

  • Support (dukungan): Istilah ini ditujukkan untuk memperlihatkan peluang pelanggan membeli beberapa produk secara bersamaan dari seluruh transaksi. Misalnya, Support ’X=>Y" artinya peluang 2 kejadian yang harus terjadi secara bersamaan.

  • Confidence (tingkat kepercayaan): Istilah ini ditujukkan untuk memperlihatkan peluang kejadian produk yang dibeli secara bersamaan dimana terdapat keyakinan satu produk sudah pasti dibeli. Misalnya confidence ‘X=>Y’, jika terdapat a transaksi dimana X dibeli, dan ada b transaksi dimana X dan Y dibeli maka peluang/nilai confidance adalah a/b.

  • Minimum support: Istilah yang digunakan untuk membatasi frekuensi kejadian atau pemenuhan jumlah support yang harus dipenuhi dalam suatu data sehingga dapat dijadikan aturan.

  • Minimum confidence: Istilah yang digunakan untuk mendefinisikan tingkat minimum dari confidence yang harus dipenuhi.

  • Itemset: istilah yang didefinisikan untuk kelompok suatu produk.

  • Support count: Istilah yang digunakan dalam mendefinisikan frekuensi kejadian untuk sebuah kelompok produk atau itemset dari seluruh transaksi.

  • dan lainnya

Rumus Support:

\(Support (A) = \frac{A}{N}\)

\(Ket:\)

\(A =\) Jumlah transaksi yang mengandung A

\(N =\) Total transaksi

\(Support (A,B) = \frac{A \cap B}{N}\)

Rumus Confidence:

\(Confidence P(B|A) = \frac{A \cap B}{A}\)

Menghasilkan Rules dengan Apriori

apriori(transaksi)
Apriori

Parameter specification:

Algorithmic control:

Absolute minimum support count: 1 

set item appearances ...[0 item(s)] done [0.00s].
set transactions ...[4 item(s), 10 transaction(s)] done [0.00s].
sorting and recoding items ... [4 item(s)] done [0.00s].
creating transaction tree ... done [0.00s].
checking subsets of size 1 2 3 done [0.00s].
writing ... [3 rule(s)] done [0.00s].
creating S4 object  ... done [0.00s].
set of 3 rules 

Menghasilkan 3 rules

lift ratio adalah alat ukur untuk menentukan apakah suatu produk X benar-benar dibeli bersamaan dengan produk Y. Apabila memang benar adanya makan nilai lift ratio akan bernilai lebih besar dari 1. Nilai ini seringkali disebut sebagai ‘rule positif’.

Sementara itu, sebaliknya jika nilai lift ratio kurang dari 1 maka disebut sebagai ‘rule negatif’. Artinya, rule yang dimiliki adalah rule yang kurang baik untuk memprediksi pola yang sama berdasarkan frekuensi item. Selain itu, ini juga menandakan tidak menunjukkan peluang cross-selling, meskipun nilai support dan confidencenya tinggi.

Berikut adalah rumus lift ratio:

\(lift (A=>B)=\frac{Confidence (A=>B)}{Support(B)}\)

Melihat Rules dengan fungsi inspect

#Menghasilkan association rules dan disimpan sebagai variable mba
mba <- apriori(transaksi)
Apriori

Parameter specification:

Algorithmic control:

Absolute minimum support count: 1 

set item appearances ...[0 item(s)] done [0.00s].
set transactions ...[4 item(s), 10 transaction(s)] done [0.00s].
sorting and recoding items ... [4 item(s)] done [0.00s].
creating transaction tree ... done [0.00s].
checking subsets of size 1 2 3 done [0.00s].
writing ... [3 rule(s)] done [0.00s].
creating S4 object  ... done [0.00s].
#Melihat isi dari rules dengan menggunakan fungsi inspect
inspect(mba)

Pada hasil asosiasi tersebut penjualan pet food dengan sirup memiliki nilai support, confidence dan lift ratio yang lebih baik dibandingkan dengan aturan 1 dan 3. Oleh karena itu aturan 2 dirasa adalah yang terbaik.

Artinya ada beberapa opsi action item yang bisa kita lakukan:

  • rekomendasikan kepada setiap pelanggan yang membeli Pet Food untuk membeli Sirup.
  • rak display Pet Food dan Sirup bisa didekatkan.
  • Pet Food dan Sirup dipaketkan bersama dan dijual dengan harga khusus.

Dalam mengaplikasikan hal ini, kita juga bisa memfilter apa saja yang ingin digabungkan dengan menggunakan perintah %in%.

Filter RHS

inspect(subset(mba, rhs %in% "Sirup"))

filter ini akan menunjukkan bagian kanan (rhs) memiliki item ‘sirup’

Filter LHS

inspect(subset(mba, lhs %in% "Gula"))

filter ini akan menunjukkan bagian kiri (lhs) memiliki item ‘gula’

Filter LHS dan RHS

inspect(subset(mba, lhs %in% "Pet Food" & rhs %in% "Sirup"))

filter ini akan menunjukkan bagian kanan (rhs) dan kiri (lhs) memiliki item ‘pet food’ dan ‘sirup’

Menghasilkan Rules dengan Parameter Support dan Confidence

Pada apriori ini menggunakan parameter dimana terdapat batasan minimal untuk support yaitu 0.1 dan confidence yaitu 0.5.

mba <- apriori(transaksi,parameter = list(supp = 0.1, confidence = 0.5))
Apriori

Parameter specification:

Algorithmic control:

Absolute minimum support count: 1 

set item appearances ...[0 item(s)] done [0.00s].
set transactions ...[4 item(s), 10 transaction(s)] done [0.00s].
sorting and recoding items ... [4 item(s)] done [0.00s].
creating transaction tree ... done [0.00s].
checking subsets of size 1 2 3 done [0.00s].
writing ... [16 rule(s)] done [0.00s].
creating S4 object  ... done [0.00s].
# Mengurutkan
mba <- sort(mba, by = "support", decreasing = T)

Inspeksi Rules Yang Dihasilkan

inspect(mba)

Filter LHS dan RHS (2)

inspect(subset(mba, lhs %in% "Teh Celup" | rhs %in% "Teh Celup"))

Melakukan filter dengan operator ‘atau’.

Filter berdasarkan Lift

inspect(subset(mba, (lhs %in% "Teh Celup" | rhs %in% "Teh Celup") & lift>1))

Melakukan filter dengan operator ‘atau’dan kondisi ’lift>1’.

Rekomendasi - Filter dengan %ain%

inspect(subset(mba, (lhs %ain% c("Pet Food", "Gula" ))))
inspect(subset(mba, lift>1.1))

Melakukan visualisasi

Visualisasi Rules dengan Graph

library(arulesViz)
plot(subset(mba, lift>1.1), method="graph")

plot(mba, method="graph")

plot(mba, method="grouped")

github

Connect with me:)

LS0tCnRpdGxlOiAiTUFSS0VUIEJBU0tFVCBBTkFMWVNJUyIKc3VidGl0bGU6ICdTdW1iZXIgQWN1YW46IERRTEFCJwpvdXRwdXQ6IAogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHllcwogICAgdG9jX2RlcHRoOiAyCiAgICB0b2NfZmxvYXQ6CiAgICAgIGNvbGxhcHNlZDogbm8KICAgICAgc21vb3RoX3Njcm9sbDogbm8KICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogJzInCiAgICBkZl9wcmludDogcGFnZWQKLS0tCiFbQmVyYmVsYW5qYSBkaSBTdXBlcm1hcmtldF0oaHR0cHM6Ly9pbWFnZXMucGV4ZWxzLmNvbS9waG90b3MvMzk4NTA2MC9wZXhlbHMtcGhvdG8tMzk4NTA2MC5qcGVnP2F1dG89Y29tcHJlc3MmY3M9dGlueXNyZ2ImZHByPTImaD02NTAmdz05NDApCgpNYXJrZXQgQmFza2V0IEFuYWx5c2lzIChNQkEpIGFkYWxhaCBiYWdpYW4gZGFyaSBhdHVyYW4gYXNvc2lhc2kgeWFuZyBiZXJndW5hIHVudHVrIG1lbXBlcmxpaGF0a2FuIHBvbGEgcGVyaWxha3UgcGVtYmVsaWFuIGtvbnN1bWVuLiBPbGVoIGthcmVuYSBpdHUsIGFuYWxpc2lzIGluaSBkaW5hbWFrYW4gYW5hbGlzaXMga2VyYW5qYW5nIHBhc2FyLiBBdHVyYW4gYXNvc2lhc2kgaW5pIGRpaGFyYXBrYW4gbWVtZW51aGkgc3lhcmF0IG1pbmltdW0gdW50dWsgc3VwcG9ydCBkYW4gY29uZmlkZW5jZSAobWluaW11bSBzdXBwb3J0IGRhbiBtaW5pbXVtIGNvbmZpZGVuY2UpLiAKCk5haCwgdW50dWsgaW1wbGVtZW50YXNpIE1CQSBkZW5nYW4gc29mdHdhcmUgaW5pLCBtYWthIGRpcGVybHVrYW4gc3VhdHUgcGFja2FnZSB5YW5nIGJlcm5hbWEgJ2FydWxlcycuCgojIFBhbmdnaWwgbGlicmFyeSAnYXJ1bGVzJwoKYGBge3J9CmxpYnJhcnkoYXJ1bGVzKQpgYGAKClNldGVsYWggZGF0YSBzdWRhaCB0ZXJwYW5nZ2lsLCBtYWthIHNlbGFuanV0bnlhIG1lbWFuZ2dpbCBkYXRhIHlhbmcgYWthbiBkaWd1bmFrYW4gZGFsYW0gcGVuZ2FwbGlrYXNpYW4gTUJBLiAKCmBgYHtyfQp0cmFuc2Frc2lfdGFidWxhciA8LSByZWFkLmNzdigiaHR0cHM6Ly9zdG9yYWdlLmdvb2dsZWFwaXMuY29tL2RxbGFiLWRhdGFzZXQvZGF0YV90cmFuc2Frc2kudHh0Iiwgc2VwPSJcdCIpCnRyYW5zYWtzaV90YWJ1bGFyCmBgYAoKRGF0YSB5YW5nIGRpZ3VuYWthbiBpbmkgbWFzaWggYmVyYmVudHVrIGRhdGEgZnJhbWUsIHNlbWVudGFyYSBkYWxhbSBwYWNrYWdlIGRhdGEgaGFydXMgYmVyYmVybnR1ayB0cmFuc2FjdGlvbi4gT2xlaCBrYXJlbmEgaXR1LCBraXRhIGhhcnVzIG1lbmd1YmFoIGRhdGEgeWFuZyBzZWJlbHVtbnlhIHlhaXR1IGRhdGEgZnJhbWUgbWVuamFkaSBkYXRhIHRyYW5zYWN0aW9uIG1lbmdndW5ha2FuIGZ1bmdzaSByZWFkLnRyYW5zYWN0aW9ucygpLgoKCgpgYGB7cn0KdHJhbnNha3NpIDwtIHJlYWQudHJhbnNhY3Rpb25zKGZpbGU9Imh0dHBzOi8vc3RvcmFnZS5nb29nbGVhcGlzLmNvbS9kcWxhYi1kYXRhc2V0L2RhdGFfdHJhbnNha3NpLnR4dCIsIGZvcm1hdD0ic2luZ2xlIiwgc2VwPSJcdCIsIGNvbHM9YygxLDIpLCBza2lwPTEpCgp0cmFuc2Frc2kKYGBgCgojIE1lbmFtcGlsa2FuIERhZnRhciBLb2RlIFRyYW5zYWtzaQpgYGB7cn0KdHJhbnNha3NpQGl0ZW1JbmZvCmBgYAojIFRhbXBpbGFuIFRyYW5zYWtzaSBkYWxhbSBiZW50dWsgTWF0cml4CmBgYHtyfQp0KHRyYW5zYWtzaUBkYXRhKQpgYGAKVXJ1dGFuIDEgLSA0IGFkYWxhaCB1cnV0YW4gZGFsYW0gbGFiZWxzCgo0IHggMTAgOiAgYXJ0aW55YSB0ZXJkYXBhdCA0IGl0ZW0gZGFuIDEwIHRyYW5zYWtzaS4KWzEsXSBzL2QgWzQsXSA6IGFydGlueWEgaW5kZXggeWFuZyBtZXdha2lsaSB0aWFwIGl0ZW0gZGFsYW0gdHJhbnNha3NpLgoKKiBTaW1ib2wgdGFuZGEgdGl0aWsgLiAgbWVudW5qdWtrYW4gaXRlbSB0aWRhayBhZGEgZGkgdHJhbnNha3NpLgoqIFNpbWJvbCBnYXJpcyBsdXJ1cyB8ICBtZW51bmp1a2thbiBiYWh3YSBpdGVtIGFkYSBkaSB0cmFuc2Frc2kuCgpTZWxhbmp1dG55YSBtZW5naGl0dW5nIGZyZWt1ZW5zaSBkYXJpIGRhdGEgdHJhbnNha3NpIHVudHVrIG1lbGloYXQgcG9sYSBkYXJpIGRhdGEuCgojIEl0ZW0gRnJlcXVlbmN5CmBgYHtyfQpkYXRhX2l0ZW0gPC0gaXRlbUZyZXF1ZW5jeSh0cmFuc2Frc2ksIHR5cGU9ImFic29sdXRlIikKZGF0YV9pdGVtCmBgYAojIFN0YXRpc3RpayBUb3AgMwpgYGB7cn0KI01lbGFrdWthbiBzb3J0aW5nIHBhZGEgZGF0YV9pdGVtCmRhdGFfaXRlbSA8LSBzb3J0KGRhdGFfaXRlbSwgZGVjcmVhc2luZyA9IFRSVUUpCgojTWVuZ2FtYmlsIDMgaXRlbSBwZXJ0YW1hCmRhdGFfaXRlbSA8LSBkYXRhX2l0ZW1bMTozXQoKI0tvbnZlcnNpIGRhdGFfaXRlbSBtZW5qYWRpIGRhdGEgZnJhbWUgZGVuZ2FuIGtvbG9tIE5hbWFfUHJvZHVrIGRhbiBKdW1sYWgKZGF0YV9pdGVtIDwtIGRhdGEuZnJhbWUoIk5hbWEgUHJvZHVrIj1uYW1lcyhkYXRhX2l0ZW0pLCAiSnVtbGFoIj1kYXRhX2l0ZW0sIHJvdy5uYW1lcz1OVUxMKQpkYXRhX2l0ZW0KYGBgCgojIyBJbnNpZ2h0IApIYXNpbCBha2hpcm55YSBmcmVrdWVuc2kgdGVyYmFueWFrIGFkYWxhaCAiVGVoIENlbHVwIiB5YW5nIGtlbXVkaWFuIGRpc3VzdWwgb2xlaCAiU2lydXAiIGRhbiAiUGV0IEZvb2QiLgoKIyBPdXRwdXQgU3RhdGlzdGlrIFRvcCAzIFNlYmFnYWkgRmlsZQpgYGB7cn0Kd3JpdGUuY3N2KGRhdGFfaXRlbSwgZmlsZT0idG9wM19pdGVtX3JldGFpbC50eHQiLCBlb2wgPSAiXHJcbiIpCmBgYAoKIyBHcmFmaWsgSXRlbSBGcmVxdWVuY3kKYGBge3J9Cml0ZW1GcmVxdWVuY3lQbG90KHRyYW5zYWtzaSkKYGBgCiMgTWVsaWhhdCBJdGVtc2V0IHBlciBUcmFuc2Frc2kgZGVuZ2FuIEluc3BlY3QKSW5zcGVjdCBkaWd1bmFrYW4gdW50dWsgbWVsaWhhdCBub3Rhc2kgaXRlbXNldAoKYGBge3J9Cmluc3BlY3QodHJhbnNha3NpKQpgYGAKCkFsZ29yaXRtYSBhcHJpb3JpIGFkYWxhaCBhbGdvcml0bWEgeWFuZyBkaWd1bmFrYW4gdW50dWsgbWVuZGFwYXRrYW4gYXR1cmFuIGFzb3NpYXNpLiBQcmluc2lwbnlhIHNlcGVydGkgYXR1cmFuIGltcGxpa2FzaSB5YWl0dSAnamlrYS1tYWthJy4KCkJlYmVyYXBhIGlzdGlsYWggZGFsYW0gYWxnb3JpdG1hIGFwcmlvcmkgeWFpdHUgc2ViYWdhaSBiZXJpa3V0OgoKKiBTdXBwb3J0IChkdWt1bmdhbik6IElzdGlsYWggaW5pIGRpdHVqdWtrYW4gdW50dWsgbWVtcGVybGloYXRrYW4gcGVsdWFuZyBwZWxhbmdnYW4gbWVtYmVsaSBiZWJlcmFwYSBwcm9kdWsgc2VjYXJhIGJlcnNhbWFhbiBkYXJpIHNlbHVydWggdHJhbnNha3NpLiBNaXNhbG55YSwgU3VwcG9ydCAnWD0+WSIgYXJ0aW55YSBwZWx1YW5nIDIga2VqYWRpYW4geWFuZyBoYXJ1cyB0ZXJqYWRpIHNlY2FyYSBiZXJzYW1hYW4uIAoKKiBDb25maWRlbmNlICh0aW5na2F0IGtlcGVyY2F5YWFuKTogSXN0aWxhaCBpbmkgZGl0dWp1a2thbiB1bnR1ayBtZW1wZXJsaWhhdGthbiBwZWx1YW5nIGtlamFkaWFuIHByb2R1ayB5YW5nIGRpYmVsaSBzZWNhcmEgYmVyc2FtYWFuIGRpbWFuYSB0ZXJkYXBhdCBrZXlha2luYW4gc2F0dSBwcm9kdWsgc3VkYWggcGFzdGkgZGliZWxpLiBNaXNhbG55YSBjb25maWRlbmNlICdYPT5ZJywgamlrYSB0ZXJkYXBhdCBhIHRyYW5zYWtzaSBkaW1hbmEgWCBkaWJlbGksIGRhbiBhZGEgYiB0cmFuc2Frc2kgZGltYW5hIFggZGFuIFkgZGliZWxpIG1ha2EgcGVsdWFuZy9uaWxhaSBjb25maWRhbmNlIGFkYWxhaCBhL2IuICAKCiogTWluaW11bSBzdXBwb3J0OiBJc3RpbGFoIHlhbmcgZGlndW5ha2FuIHVudHVrIG1lbWJhdGFzaSBmcmVrdWVuc2kga2VqYWRpYW4gYXRhdSBwZW1lbnVoYW4ganVtbGFoIHN1cHBvcnQgeWFuZyBoYXJ1cyBkaXBlbnVoaSBkYWxhbSBzdWF0dSBkYXRhIHNlaGluZ2dhIGRhcGF0IGRpamFkaWthbiBhdHVyYW4uIAoKKiBNaW5pbXVtIGNvbmZpZGVuY2U6IElzdGlsYWggeWFuZyBkaWd1bmFrYW4gdW50dWsgbWVuZGVmaW5pc2lrYW4gdGluZ2thdCBtaW5pbXVtIGRhcmkgY29uZmlkZW5jZSB5YW5nIGhhcnVzIGRpcGVudWhpLgoKKiBJdGVtc2V0OiBpc3RpbGFoIHlhbmcgZGlkZWZpbmlzaWthbiB1bnR1ayBrZWxvbXBvayBzdWF0dSBwcm9kdWsuCgoqIFN1cHBvcnQgY291bnQ6IElzdGlsYWggeWFuZyBkaWd1bmFrYW4gZGFsYW0gbWVuZGVmaW5pc2lrYW4gZnJla3VlbnNpIGtlamFkaWFuIHVudHVrCnNlYnVhaCBrZWxvbXBvayBwcm9kdWsgYXRhdSBpdGVtc2V0IGRhcmkgc2VsdXJ1aCB0cmFuc2Frc2kuIAoKKiBkYW4gbGFpbm55YQoKUnVtdXMgU3VwcG9ydDoKCiRTdXBwb3J0IChBKSA9IFxmcmFje0F9e059JCAKCiRLZXQ6JCAKCiRBID0kIEp1bWxhaCB0cmFuc2Frc2kgeWFuZyBtZW5nYW5kdW5nIEEKCiROID0kIFRvdGFsIHRyYW5zYWtzaQoKJFN1cHBvcnQgKEEsQikgPSBcZnJhY3tBIFxjYXAgQn17Tn0kIAoKUnVtdXMgQ29uZmlkZW5jZToKCiRDb25maWRlbmNlIFAoQnxBKSA9IFxmcmFje0EgXGNhcCBCfXtBfSQKCgoKIyBNZW5naGFzaWxrYW4gUnVsZXMgZGVuZ2FuIEFwcmlvcmkKYGBge3J9CmFwcmlvcmkodHJhbnNha3NpKQpgYGAKIyMgTWVuZ2hhc2lsa2FuIDMgcnVsZXMgCgpsaWZ0IHJhdGlvIGFkYWxhaCBhbGF0IHVrdXIgdW50dWsgbWVuZW50dWthbiBhcGFrYWggc3VhdHUgcHJvZHVrIFggYmVuYXItYmVuYXIgZGliZWxpIGJlcnNhbWFhbiBkZW5nYW4gcHJvZHVrIFkuIEFwYWJpbGEgbWVtYW5nIGJlbmFyIGFkYW55YSBtYWthbiBuaWxhaSBsaWZ0IHJhdGlvIGFrYW4gYmVybmlsYWkgbGViaWggYmVzYXIgZGFyaSAxLiBOaWxhaSBpbmkgc2VyaW5na2FsaSBkaXNlYnV0IHNlYmFnYWkgJ3J1bGUgcG9zaXRpZicuIAoKU2VtZW50YXJhIGl0dSwgc2ViYWxpa255YSBqaWthIG5pbGFpIGxpZnQgcmF0aW8ga3VyYW5nIGRhcmkgMSBtYWthIGRpc2VidXQgc2ViYWdhaSAncnVsZSBuZWdhdGlmJy4gQXJ0aW55YSwgcnVsZSB5YW5nIGRpbWlsaWtpIGFkYWxhaCBydWxlIHlhbmcga3VyYW5nIGJhaWsgdW50dWsgbWVtcHJlZGlrc2kgcG9sYSB5YW5nIHNhbWEgYmVyZGFzYXJrYW4gZnJla3VlbnNpIGl0ZW0uIFNlbGFpbiBpdHUsIGluaSBqdWdhIG1lbmFuZGFrYW4gdGlkYWsgbWVudW5qdWtrYW4gcGVsdWFuZyBjcm9zcy1zZWxsaW5nLCBtZXNraXB1biBuaWxhaSBzdXBwb3J0IGRhbiBjb25maWRlbmNlbnlhIHRpbmdnaS4gCgpCZXJpa3V0IGFkYWxhaCBydW11cyBsaWZ0IHJhdGlvOgoKJGxpZnQgKEE9PkIpPVxmcmFje0NvbmZpZGVuY2UgKEE9PkIpfXtTdXBwb3J0KEIpfSQKCgojIE1lbGloYXQgUnVsZXMgZGVuZ2FuIGZ1bmdzaSBpbnNwZWN0CmBgYHtyfQojTWVuZ2hhc2lsa2FuIGFzc29jaWF0aW9uIHJ1bGVzIGRhbiBkaXNpbXBhbiBzZWJhZ2FpIHZhcmlhYmxlIG1iYQptYmEgPC0gYXByaW9yaSh0cmFuc2Frc2kpCgojTWVsaWhhdCBpc2kgZGFyaSBydWxlcyBkZW5nYW4gbWVuZ2d1bmFrYW4gZnVuZ3NpIGluc3BlY3QKaW5zcGVjdChtYmEpCmBgYApQYWRhIGhhc2lsIGFzb3NpYXNpIHRlcnNlYnV0IHBlbmp1YWxhbiBwZXQgZm9vZCBkZW5nYW4gc2lydXAgbWVtaWxpa2kgbmlsYWkgc3VwcG9ydCwgY29uZmlkZW5jZSBkYW4gbGlmdCByYXRpbyB5YW5nIGxlYmloIGJhaWsgZGliYW5kaW5na2FuIGRlbmdhbiBhdHVyYW4gMSBkYW4gMy4gT2xlaCBrYXJlbmEgaXR1IGF0dXJhbiAyIGRpcmFzYSBhZGFsYWggeWFuZyB0ZXJiYWlrLiAKCkFydGlueWEgYWRhIGJlYmVyYXBhIG9wc2kgYWN0aW9uIGl0ZW0geWFuZyBiaXNhIGtpdGEgbGFrdWthbjoKCiogcmVrb21lbmRhc2lrYW4ga2VwYWRhIHNldGlhcCBwZWxhbmdnYW4geWFuZyBtZW1iZWxpIFBldCBGb29kIHVudHVrIG1lbWJlbGkgU2lydXAuCiogcmFrIGRpc3BsYXkgUGV0IEZvb2QgZGFuIFNpcnVwIGJpc2EgZGlkZWthdGthbi4KKiBQZXQgRm9vZCBkYW4gU2lydXAgZGlwYWtldGthbiBiZXJzYW1hIGRhbiBkaWp1YWwgZGVuZ2FuIGhhcmdhIGtodXN1cy4KCkRhbGFtIG1lbmdhcGxpa2FzaWthbiBoYWwgaW5pLCBraXRhIGp1Z2EgYmlzYSBtZW1maWx0ZXIgYXBhIHNhamEgeWFuZyBpbmdpbiBkaWdhYnVuZ2thbiBkZW5nYW4gbWVuZ2d1bmFrYW4gcGVyaW50YWggJWluJS4gCgojIEZpbHRlciBSSFMKYGBge3J9Cmluc3BlY3Qoc3Vic2V0KG1iYSwgcmhzICVpbiUgIlNpcnVwIikpCmBgYApmaWx0ZXIgaW5pIGFrYW4gbWVudW5qdWtrYW4gYmFnaWFuIGthbmFuIChyaHMpIG1lbWlsaWtpIGl0ZW0gJ3NpcnVwJwoKIyBGaWx0ZXIgTEhTCmBgYHtyfQppbnNwZWN0KHN1YnNldChtYmEsIGxocyAlaW4lICJHdWxhIikpCmBgYApmaWx0ZXIgaW5pIGFrYW4gbWVudW5qdWtrYW4gYmFnaWFuIGtpcmkgKGxocykgbWVtaWxpa2kgaXRlbSAnZ3VsYScKCiMgRmlsdGVyIExIUyBkYW4gUkhTCmBgYHtyfQppbnNwZWN0KHN1YnNldChtYmEsIGxocyAlaW4lICJQZXQgRm9vZCIgJiByaHMgJWluJSAiU2lydXAiKSkKYGBgCgpmaWx0ZXIgaW5pIGFrYW4gbWVudW5qdWtrYW4gYmFnaWFuIGthbmFuIChyaHMpIGRhbiBraXJpIChsaHMpIG1lbWlsaWtpIGl0ZW0gJ3BldCBmb29kICcgZGFuICdzaXJ1cCcKCiMgTWVuZ2hhc2lsa2FuIFJ1bGVzIGRlbmdhbiBQYXJhbWV0ZXIgU3VwcG9ydCBkYW4gQ29uZmlkZW5jZQoKUGFkYSBhcHJpb3JpIGluaSBtZW5nZ3VuYWthbiBwYXJhbWV0ZXIgZGltYW5hIHRlcmRhcGF0IGJhdGFzYW4gbWluaW1hbCB1bnR1ayBzdXBwb3J0IHlhaXR1IDAuMSBkYW4gY29uZmlkZW5jZSB5YWl0dSAwLjUuCgpgYGB7cn0KbWJhIDwtIGFwcmlvcmkodHJhbnNha3NpLHBhcmFtZXRlciA9IGxpc3Qoc3VwcCA9IDAuMSwgY29uZmlkZW5jZSA9IDAuNSkpCgojIE1lbmd1cnV0a2FuCm1iYSA8LSBzb3J0KG1iYSwgYnkgPSAic3VwcG9ydCIsIGRlY3JlYXNpbmcgPSBUKQpgYGAKCiMgSW5zcGVrc2kgUnVsZXMgWWFuZyBEaWhhc2lsa2FuCmBgYHtyfQppbnNwZWN0KG1iYSkKYGBgCgoKIyBGaWx0ZXIgTEhTIGRhbiBSSFMgKDIpCgpgYGB7cn0KaW5zcGVjdChzdWJzZXQobWJhLCBsaHMgJWluJSAiVGVoIENlbHVwIiB8IHJocyAlaW4lICJUZWggQ2VsdXAiKSkKYGBgCk1lbGFrdWthbiBmaWx0ZXIgZGVuZ2FuIG9wZXJhdG9yICdhdGF1Jy4gCgojIEZpbHRlciBiZXJkYXNhcmthbiBMaWZ0CmBgYHtyfQppbnNwZWN0KHN1YnNldChtYmEsIChsaHMgJWluJSAiVGVoIENlbHVwIiB8IHJocyAlaW4lICJUZWggQ2VsdXAiKSAmIGxpZnQ+MSkpCmBgYApNZWxha3VrYW4gZmlsdGVyIGRlbmdhbiBvcGVyYXRvciAnYXRhdSdkYW4ga29uZGlzaSAnbGlmdD4xJy4KCiMgUmVrb21lbmRhc2kgLSBGaWx0ZXIgZGVuZ2FuICVhaW4lCmBgYHtyfQppbnNwZWN0KHN1YnNldChtYmEsIChsaHMgJWFpbiUgYygiUGV0IEZvb2QiLCAiR3VsYSIgKSkpKQpgYGAKYGBge3J9Cmluc3BlY3Qoc3Vic2V0KG1iYSwgbGlmdD4xLjEpKQpgYGAKCgojIE1lbGFrdWthbiB2aXN1YWxpc2FzaSAKCiMjIFZpc3VhbGlzYXNpIFJ1bGVzIGRlbmdhbiBHcmFwaApgYGB7cn0KbGlicmFyeShhcnVsZXNWaXopCnBsb3Qoc3Vic2V0KG1iYSwgbGlmdD4xLjEpLCBtZXRob2Q9ImdyYXBoIikKYGBgCgpgYGB7cn0KcGxvdChtYmEsIG1ldGhvZD0iZ3JhcGgiKQpgYGAKCmBgYHtyfQpwbG90KG1iYSwgbWV0aG9kPSJncm91cGVkIikKYGBgClsyXTogaHR0cHM6Ly93d3cubGlua2VkaW4uY29tL2luL2t1cm5pYXJhaG1pLwpbIVtnaXRodWJdKGh0dHBzOi8vY2xvdWQuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2Fzc2V0cy8xNzAxNjI5Ny8xODgzOTg0OC8wZmM3ZTc0ZS04M2QyLTExZTYtOGM2YS0yNzdmYzlkNmUwNjcucG5nKV1bMl0KCkNvbm5lY3Qgd2l0aCBtZTopCgoKCgoKCgoKCgoKCgo=