BACKGROUND

Algoritma apriori adalah salah satu algoritma yang merupakan penerapan praktis dari Market Basket Analysis (MBA). Algoritma ini digunakan untuk menganalisa banyaknya kombinasi produk yang terjadi di dalam transaksi ritel, yang akan sulit dan lama jika dilakukan secara manual.

Secara teknis, algoritma apriori akan mencari tingkat asosiasi antar item di dalam banyak kombinasi kelompok data secara otomatis. Kombinasi ini juga bisa disusun dengan suatu aturan (rule) asosiasi “Jika membeli ini produk A maka akan membeli produk B”, sehingga algoritma ini dikategorikan sebagai Association Rules di ranah machine learning.

Dengan menemukan paket produk yang asosiasinya kuat, Anda sebagai seorang data scientist dapat menyarankan kepada bisnis dapat melakukan berbagai action item seperti membuat paket produk dengan penawaran khusus, mendekatkan produk-produk tersebut saling berdekatan dalam satu rak, mengeluarkan rekomendasi produk di sistem e-commerce, mengurangi masalah stok, dan lain-lain.

Data yang digunakan diambil dari Weka dataset dapat diakses pada data supermarke.csv, merupakan dataset yang berisis daftar pembelian barang setiap transaksinya.

DATA PREPARATION

DATA OVERVIEW

Summary data dan dilihat data secara keseluruhan

summary(supermarket)
      TID           name          
 Min.   :   1   Length:79626      
 1st Qu.:1162   Class :character  
 Median :2324   Mode  :character  
 Mean   :2317                     
 3rd Qu.:3476                     
 Max.   :4627                     

EXPLORATOY ANALYSIS

Saya akan memulai dengan menunjukkan barang yang paling banyak dibeli di supermarket ini.

BUILD RULES

Setelah itu saya membuah sebuah rules algoritma apriori. pertama, ubah data frame menjadi sebuah list

head(sup_list)
$`1`
 [1] "baby needs "        "bread and cake "    "baking needs "      "juice sat cord ms" 
 [5] "biscuits"           "canned vegetables " "cleaners polishers" "coffee"            
 [9] "sauces gravy pkle"  "confectionary"      "dishcloths scour"   "frozen foods "     
[13] "razor blades "      "party snack foods " "tissues paper prd " "wrapping"          
[17] "mens toiletries "   "cheese"             "milk cream"         "margarine"         
[21] "small goods "       "fruit"              "vegetables"         "750ml white nz "   

$`2`
 [1] "canned fish meat "  "canned fruit "      "canned vegetables " "sauces gravy pkle" 
 [5] "deod disinfectant"  "frozen foods "      "pet foods "         "laundry needs "    
 [9] "tissues paper prd " "deodorants soap"    "haircare"           "milk cream"        
[13] "fruit"              "vegetables"        

$`3`
 [1] "bread and cake "    "baking needs "      "juice sat cord ms"  "biscuits"          
 [5] "canned fruit "      "sauces gravy pkle"  "puddings deserts"   "wrapping"          
 [9] "health food other " "small goods "       "dairy foods "       "beef"              
[13] "lamb"               "fruit"              "vegetables"         "stationary"        

$`4`
 [1] "bread and cake "    "baking needs "      "juice sat cord ms"  "biscuits"          
 [5] "canned vegetables " "breakfast food "    "cleaners polishers" "frozen foods "     
 [9] "jams spreads"       "pet foods "         "party snack foods " "tissues paper prd "
[13] "deodorants soap"    "mens toiletries "   "cheese"             "margarine"         
[17] "dairy foods "       "beef"               "stationary"         "prepared meals "   

$`5`
 [1] "bread and cake "    "baking needs "      "juice sat cord ms"  "tea"               
 [5] "cleaners polishers" "coffee"             "sauces gravy pkle"  "frozen foods "     
 [9] "jams spreads"       "laundry needs "     "wrapping"           "deodorants soap"   
[13] "haircare"           "dental needs "      "meat misc "         "milk cream"        
[17] "margarine"          "beef"               "poultry"            "potatoes"          
[21] "vegetables"         "condiments"         "small goods2 "     

$`6`
 [1] "bread and cake "    "baking needs "      "juice sat cord ms"  "tea"               
 [5] "biscuits"           "canned vegetables " "breakfast food "    "confectionary"     
 [9] "frozen foods "      "spices"             "party snack foods " "tissues paper prd "
[13] "deodorants soap"    "margarine"          "dairy foods "       "fruit"             
[17] "potatoes"           "vegetables"         "stationary"         "bake off products "

kemudian, buat class list menjadi transactions. Hal ini akan mempermudah untuk melakukan algoritma apriori.

sup_transaction %>% 
  head(3) %>% 
  inspect()
    items                 transactionID
[1] {750ml white nz ,                  
     baby needs ,                      
     baking needs ,                    
     biscuits,                         
     bread and cake ,                  
     canned vegetables ,               
     cheese,                           
     cleaners polishers,               
     coffee,                           
     confectionary,                    
     dishcloths scour,                 
     frozen foods ,                    
     fruit,                            
     juice sat cord ms,                
     margarine,                        
     mens toiletries ,                 
     milk cream,                       
     party snack foods ,               
     razor blades ,                    
     sauces gravy pkle,                
     small goods ,                     
     tissues paper prd ,               
     vegetables,                       
     wrapping}                        1
[2] {canned fish meat ,                
     canned fruit ,                    
     canned vegetables ,               
     deod disinfectant,                
     deodorants soap,                  
     frozen foods ,                    
     fruit,                            
     haircare,                         
     laundry needs ,                   
     milk cream,                       
     pet foods ,                       
     sauces gravy pkle,                
     tissues paper prd ,               
     vegetables}                      2
[3] {baking needs ,                    
     beef,                             
     biscuits,                         
     bread and cake ,                  
     canned fruit ,                    
     dairy foods ,                     
     fruit,                            
     health food other ,               
     juice sat cord ms,                
     lamb,                             
     puddings deserts,                 
     sauces gravy pkle,                
     small goods ,                     
     stationary,                       
     vegetables,                       
     wrapping}                        3
sup_transaction %>% 
  dim()
[1] 4601  100

Buat rules menggunakan fungsi apriori dengan parameter supp = 0.1 dan conf = 0.75

sup_rules <- apriori(data = sup_transaction, parameter = list(supp = 0.1, 
                                                              conf = 0.75))
Apriori

Parameter specification:

Algorithmic control:

Absolute minimum support count: 460 

set item appearances ...[0 item(s)] done [0.00s].
set transactions ...[100 item(s), 4601 transaction(s)] done [0.09s].
sorting and recoding items ... [47 item(s)] done [0.01s].
creating transaction tree ... done [0.01s].
checking subsets of size 1 2 3 4 5 6 7 done [0.32s].
writing ... [9958 rule(s)] done [0.32s].
creating S4 object  ... done [0.09s].

Lakukan summary untuk rules

summary(sup_rules)
set of 9958 rules

rule length distribution (lhs + rhs):sizes
   2    3    4    5    6    7 
  41  720 3440 4367 1320   70 

   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  2.000   4.000   5.000   4.644   5.000   7.000 

summary of quality measures:
    support         confidence        coverage           lift           count       
 Min.   :0.1002   Min.   :0.7500   Min.   :0.1098   Min.   :1.042   Min.   : 461.0  
 1st Qu.:0.1067   1st Qu.:0.7716   1st Qu.:0.1324   1st Qu.:1.196   1st Qu.: 491.0  
 Median :0.1161   Median :0.7995   Median :0.1452   Median :1.252   Median : 534.0  
 Mean   :0.1259   Mean   :0.8080   Mean   :0.1563   Mean   :1.260   Mean   : 579.2  
 3rd Qu.:0.1332   3rd Qu.:0.8408   3rd Qu.:0.1674   3rd Qu.:1.313   3rd Qu.: 613.0  
 Max.   :0.5079   Max.   :0.9205   Max.   :0.6438   Max.   :1.594   Max.   :2337.0  

mining info:

RULES INTERPRETATION

Badingkan rules berdasarkan confidence, support, dan lift.

inspect(sup_conf)
    lhs                      rhs                 support confidence  coverage     lift count
[1] {biscuits,                                                                              
     frozen foods ,                                                                         
     milk cream,                                                                            
     pet foods ,                                                                            
     vegetables}          => {bread and cake } 0.1032384  0.9205426 0.1121495 1.271897   475
[2] {baking needs ,                                                                         
     biscuits,                                                                              
     fruit,                                                                                 
     margarine,                                                                             
     milk cream,                                                                            
     vegetables}          => {bread and cake } 0.1008476  0.9188119 0.1097587 1.269506   464
[3] {biscuits,                                                                              
     frozen foods ,                                                                         
     margarine,                                                                             
     milk cream,                                                                            
     vegetables}          => {bread and cake } 0.1167138  0.9179487 0.1271463 1.268313   537
[4] {biscuits,                                                                              
     canned vegetables ,                                                                    
     frozen foods ,                                                                         
     fruit,                                                                                 
     vegetables}          => {bread and cake } 0.1069333  0.9179104 0.1164964 1.268260   492
[5] {baking needs ,                                                                         
     frozen foods ,                                                                         
     fruit,                                                                                 
     margarine,                                                                             
     milk cream,                                                                            
     vegetables}          => {bread and cake } 0.1030211  0.9168279 0.1123669 1.266764   474

Nilai confindence yang tinggi menunjukkan peluang untuk terbelinya barang B jika sudah membeli barang A. Pada data frame di atas, dapat dilihat bahwa nilai confidence tertinggi ada pada item bread and cake. hal ini menjelaskan bahwa peluang iterm bread and cake terbeli sanggat tinggi. Banyak kombinasi pebelian item yang akan membuat seorang pelanggan membeli bread dan cake.

Nilai rasio support menunjukkan peluang sesorang pembeli membeli sebuah atau kombinasi barang. Dengan kata lain, support adalah rasio terbelinya suatu barang terhadap total pembelian milik cream ==> bread and cake memiliki peluang tertinggi dibandingkan barang lainya yaitu sebesaar 0.5. situasi ini menjelaskan bahwa ketika seorang membeili milk cream ia akan membeli bread and cake juga.

inspect(sup_lift)
    lhs                     rhs                    support confidence  coverage     lift count
[1] {baking needs ,                                                                           
     biscuits,                                                                                
     bread and cake ,                                                                         
     juice sat cord ms,                                                                       
     sauces gravy pkle}  => {party snack foods } 0.1010650  0.8072917 0.1251902 1.594141   465
[2] {laundry needs ,                                                                          
     wrapping}           => {tissues paper prd } 0.1038905  0.7697262 0.1349707 1.576106   478
[3] {biscuits,                                                                                
     bread and cake ,                                                                         
     frozen foods ,                                                                           
     juice sat cord ms,                                                                       
     sauces gravy pkle}  => {party snack foods } 0.1056292  0.7928222 0.1332319 1.565569   486
[4] {biscuits,                                                                                
     margarine,                                                                               
     wrapping}           => {tissues paper prd } 0.1006303  0.7565359 0.1330146 1.549097   463
[5] {baking needs ,                                                                           
     biscuits,                                                                                
     cheese,                                                                                  
     tissues paper prd } => {margarine}          0.1019344  0.7701149 0.1323625 1.548645   469

Nilai lift adalah tingkat kemampuan suatu peristiwa untuk mendorong terjadinya peristiwa lain. Dalam kasus ini, nilai lift dapat di jelaskan dengan kemapuan pembeilan suatu barang untuk meningkatkan peulang terbelinya brang lain. contoh pada data frame diatas, nilai lift dari nomer 1 adalah sebesar 1.5. Nilai lift > 1 berarti pembelian baking needs, biscuits, bread and cake, juice sar cord ms, dan sauces gravt pkele

Memiliki kemampuan untuk meningkatkan peluang pembelian party snack foods.

VISUALISASI

Agar mudah dianalisa maka harus menggunak Visualisasi

Berdasarkan rules yang telah dibuat pada fungsi apriori() tersebut, berikut visualisasinya

plot(sup_rules, 
     method = "graph",
     measure = "lift", 
     engine = "htmlwidget")
Warning: Too many rules supplied. Only plotting the best 100 using ‘lift’ (change control parameter max if needed).
Registered S3 method overwritten by 'htmlwidgets':
  method           from         
  print.htmlwidget tools:rstudio

Grafik diatas menunjukkan hubungan atau keterkaitan pembelian suatu barang terdapat pembelian barang lain. Panah menunjukkan barang yang dibeli selanjutnya. Disini, kita dapat melihat barang mana yang paling banyak dibeli dan memiliki keterkaitan terhadap pembelian barang lainya. Metode ini sangat baik digunakan di industri retail karena dapat membantu dalam penyediaan barang (supply chain) dan juga meningkatkan revenue tiap bulanya. Dengan mengetahui penjulan suat barang tertentu, pihak supilier dapat menentukan item yang memiliki potensi untuk dijual selanjutnya.

Interpretasi: dengan Support 0.1 dan Confidence 0.75, sebanyak 9958 rules generated. Pada plot di atas terlihat cluster yang terbentuk hanya 1 dengan produk-produk saling berkaitan antara satu dengan lainnya. Parameter Support dan Confidence yang tinggi membuat produk2 yang paling banyak dibeli dan juga secara bersamaan muncul sebagai produk2 yang saling terkait satu dan lainnya. Kejadian kemunculan cluster besar ini dapat terjadi dalam toko yang memiliki kategori produk groceries atau produk-produk yang kemungkinan besar akan dibeli bersamaan setiap bulan. Pada grafik ini, dapat dilihat node di tengah yaitu snack party food dan biscuit tampak paling banyak terhubung dengan rules di sekitarnya. Dapat disimpulkan secara primordial bahwa kedua kategori produk tersebut paling umum dibeli oleh konsumen sehingga, sebagai contoh, promo bundling berisi produk tersebut bersama dengan produk yang jarang dibeli dapat meningkatkan awareness maupun konversi pada sales produk dengan stock tersisa banyak maupun yang telah mendekati kedaluarsa.

LS0tDQp0aXRsZTogIk1BUktFVCBCQUtTRVQgQU5BTFlTSVMgfCBBTEdPUklUTUEgQVBSSU9SSSINCmF1dGhvcjogIlxVMDAwMUY1RTMgSmFtYWxsdWRpbiINCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vazoNCiAgICB0b2M6IHllcw0KICBodG1sX2RvY3VtZW50Og0KICAgIG51bWJlcl9zZWN0aW9uczogeWVzDQogICAgdG9jOiB5ZXMNCiAgICBmaWdfd2lkdGg6IDcNCiAgICBmaWdfaGVpZ2h0OiA0LjUNCiAgICB0aGVtZTogcmVhZGFibGUNCiAgICBoaWdobGlnaHQ6IHRhbmdvDQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQotLS0NCg0KDQohW10oTUFSS0VULnBuZykNCg0KIyBCQUNLR1JPVU5EDQoNCiFbXShEQVRBIDEucG5nKQ0KDQpBbGdvcml0bWEgYXByaW9yaSBhZGFsYWggc2FsYWggc2F0dSBhbGdvcml0bWEgeWFuZyBtZXJ1cGFrYW4gcGVuZXJhcGFuIHByYWt0aXMgZGFyaSBNYXJrZXQgQmFza2V0IEFuYWx5c2lzIChNQkEpLiBBbGdvcml0bWEgaW5pIGRpZ3VuYWthbiB1bnR1ayBtZW5nYW5hbGlzYSBiYW55YWtueWEga29tYmluYXNpIHByb2R1ayB5YW5nIHRlcmphZGkgZGkgZGFsYW0gdHJhbnNha3NpIHJpdGVsLCB5YW5nIGFrYW4gc3VsaXQgZGFuIGxhbWEgamlrYSBkaWxha3VrYW4gc2VjYXJhIG1hbnVhbC4NCg0KU2VjYXJhIHRla25pcywgYWxnb3JpdG1hIGFwcmlvcmkgYWthbiBtZW5jYXJpIHRpbmdrYXQgYXNvc2lhc2kgYW50YXIgaXRlbSBkaSBkYWxhbSBiYW55YWsga29tYmluYXNpIGtlbG9tcG9rIGRhdGEgc2VjYXJhIG90b21hdGlzLiBLb21iaW5hc2kgaW5pIGp1Z2EgYmlzYSBkaXN1c3VuIGRlbmdhbiBzdWF0dSBhdHVyYW4gKHJ1bGUpIGFzb3NpYXNpIOKAnEppa2EgbWVtYmVsaSBpbmkgcHJvZHVrIEEgbWFrYSBha2FuIG1lbWJlbGkgcHJvZHVrIELigJ0sIHNlaGluZ2dhIGFsZ29yaXRtYSBpbmkgZGlrYXRlZ29yaWthbiBzZWJhZ2FpIEFzc29jaWF0aW9uIFJ1bGVzIGRpIHJhbmFoIG1hY2hpbmUgbGVhcm5pbmcuDQoNCkRlbmdhbiBtZW5lbXVrYW4gcGFrZXQgcHJvZHVrIHlhbmcgYXNvc2lhc2lueWEga3VhdCwgQW5kYSBzZWJhZ2FpIHNlb3JhbmcgZGF0YSBzY2llbnRpc3QgZGFwYXQgbWVueWFyYW5rYW4ga2VwYWRhIGJpc25pcyBkYXBhdCBtZWxha3VrYW4gYmVyYmFnYWkgYWN0aW9uIGl0ZW0gc2VwZXJ0aSBtZW1idWF0IHBha2V0IHByb2R1ayBkZW5nYW4gcGVuYXdhcmFuIGtodXN1cywgbWVuZGVrYXRrYW4gcHJvZHVrLXByb2R1ayB0ZXJzZWJ1dCBzYWxpbmcgYmVyZGVrYXRhbiBkYWxhbSBzYXR1IHJhaywgbWVuZ2VsdWFya2FuIHJla29tZW5kYXNpIHByb2R1ayBkaSBzaXN0ZW0gZS1jb21tZXJjZSwgbWVuZ3VyYW5naSBtYXNhbGFoIHN0b2ssIGRhbiBsYWluLWxhaW4uDQoNCkRhdGEgeWFuZyBkaWd1bmFrYW4gZGlhbWJpbCBkYXJpIFdla2EgZGF0YXNldCBkYXBhdCBkaWFrc2VzIHBhZGEgZGF0YSBzdXBlcm1hcmtlLmNzdiwgbWVydXBha2FuIGRhdGFzZXQgeWFuZyBiZXJpc2lzIGRhZnRhciBwZW1iZWxpYW4gYmFyYW5nIHNldGlhcCB0cmFuc2Frc2lueWEuDQoNCg0KIyBEQVRBIFBSRVBBUkFUSU9OIA0KDQohW10oREFUQSAyLnBuZykNCg0KYGBge3J9DQojIFBBQ0tQQUdFIFlBTkcgRElHVU5BS0FOIA0KDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShhcnVsZXMpDQpsaWJyYXJ5KGFydWxlc1ZpeikNCg0KDQpzdXBlcm1hcmtldCA8LSByZWFkLmNzdigic3VwZXJtYXJrZXQuY3N2IikNCmhlYWQoc3VwZXJtYXJrZXQpDQpgYGANCg0KDQojIERBVEEgT1ZFUlZJRVcNCg0KIVtdKERBVEEgMi5wbmcpDQoNClN1bW1hcnkgZGF0YSBkYW4gZGlsaWhhdCBkYXRhIHNlY2FyYSBrZXNlbHVydWhhbiANCg0KYGBge3J9DQpzdW1tYXJ5KHN1cGVybWFya2V0KQ0KYGBgDQoNCg0KIyBFWFBMT1JBVE9ZIEFOQUxZU0lTDQoNCiFbXShEQVRBIDMucG5nKQ0KDQpTYXlhIGFrYW4gbWVtdWxhaSBkZW5nYW4gbWVudW5qdWtrYW4gYmFyYW5nIHlhbmcgcGFsaW5nIGJhbnlhayBkaWJlbGkgZGkgc3VwZXJtYXJrZXQgaW5pLg0KDQpgYGB7cn0NCnN1cGVybWFya2V0ICU+JSANCiAgZ3JvdXBfYnkobmFtZSkgJT4lIA0KICBzdW1tYXJpc2UoanVtbGFoID0gbigpKSAlPiUgDQogIGFycmFuZ2UoZGVzYyhqdW1sYWgpKQ0KYGBgDQoNCg0KIyBCVUlMRCBSVUxFUyANCg0KIVtdKERBVEEgNC5wbmcpDQoNClNldGVsYWggaXR1IHNheWEgbWVtYnVhaCBzZWJ1YWggcnVsZXMgYWxnb3JpdG1hIGFwcmlvcmkuIHBlcnRhbWEsIHViYWggZGF0YSBmcmFtZSBtZW5qYWRpIHNlYnVhaCBsaXN0IA0KDQpgYGB7cn0NCnN1cF9saXN0IDwtIHNwbGl0KHN1cGVybWFya2V0JG5hbWUsIHN1cGVybWFya2V0JFRJRCkNCmhlYWQoc3VwX2xpc3QpDQpgYGANCg0Ka2VtdWRpYW4sIGJ1YXQgY2xhc3MgbGlzdCBtZW5qYWRpIHRyYW5zYWN0aW9ucy4gSGFsIGluaSBha2FuIG1lbXBlcm11ZGFoIHVudHVrIG1lbGFrdWthbiBhbGdvcml0bWEgYXByaW9yaS4NCg0KYGBge3J9DQpzdXBfdHJhbnNhY3Rpb24gPC0gYXMoc3VwX2xpc3QsICJ0cmFuc2FjdGlvbnMiKQ0Kc3VwX3RyYW5zYWN0aW9uICU+JSANCiAgaGVhZCgzKSAlPiUgDQogIGluc3BlY3QoKQ0KYGBgDQoNCg0KYGBge3J9DQpzdXBfdHJhbnNhY3Rpb24gJT4lIA0KICBkaW0oKQ0KYGBgDQoNCkJ1YXQgcnVsZXMgbWVuZ2d1bmFrYW4gZnVuZ3NpIGFwcmlvcmkgZGVuZ2FuIHBhcmFtZXRlciBzdXBwID0gMC4xIGRhbiBjb25mID0gMC43NQ0KDQpgYGB7cn0NCnN1cF9ydWxlcyA8LSBhcHJpb3JpKGRhdGEgPSBzdXBfdHJhbnNhY3Rpb24sIHBhcmFtZXRlciA9IGxpc3Qoc3VwcCA9IDAuMSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbmYgPSAwLjc1KSkNCmBgYA0KDQoNCkxha3VrYW4gc3VtbWFyeSB1bnR1ayBydWxlcyANCg0KYGBge3J9DQpzdW1tYXJ5KHN1cF9ydWxlcykNCmBgYA0KDQoNCiMgUlVMRVMgSU5URVJQUkVUQVRJT04gDQoNCiFbXShEQVRBIDUucG5nKQ0KDQpCYWRpbmdrYW4gcnVsZXMgYmVyZGFzYXJrYW4gY29uZmlkZW5jZSwgc3VwcG9ydCwgZGFuIGxpZnQuDQoNCmBgYHtyfQ0Kc3VwX2NvbmYgPC0gaGVhZChzdXBfcnVsZXMsIDUsIA0KICAgICAgICAgICAgICAgICBieSA9ICJjb25maWRlbmNlIikNCmluc3BlY3Qoc3VwX2NvbmYpDQpgYGANCg0KTmlsYWkgY29uZmluZGVuY2UgeWFuZyB0aW5nZ2kgbWVudW5qdWtrYW4gcGVsdWFuZyB1bnR1ayB0ZXJiZWxpbnlhIGJhcmFuZyBCIGppa2Egc3VkYWggbWVtYmVsaSBiYXJhbmcgQS4gUGFkYSBkYXRhIGZyYW1lIGRpIGF0YXMsIGRhcGF0IGRpbGloYXQgYmFod2EgbmlsYWkgY29uZmlkZW5jZSB0ZXJ0aW5nZ2kgYWRhIHBhZGEgaXRlbSBicmVhZCBhbmQgY2FrZS4gaGFsIGluaSBtZW5qZWxhc2thbiBiYWh3YSBwZWx1YW5nIGl0ZXJtIGJyZWFkIGFuZCBjYWtlIHRlcmJlbGkgc2FuZ2dhdCB0aW5nZ2kuIEJhbnlhayBrb21iaW5hc2kgcGViZWxpYW4gaXRlbSB5YW5nIGFrYW4gbWVtYnVhdCBzZW9yYW5nIHBlbGFuZ2dhbiBtZW1iZWxpIGJyZWFkIGRhbiBjYWtlLg0KDQoNCmBgYHtyfQ0Kc3VwX3N1cHAgPC0gaGVhZChzdXBfcnVsZXMsIA0KICAgICAgICAgICAgICAgICA1LCANCiAgICAgICAgICAgICAgICAgYnkgPSAic3VwcG9ydCIpDQoNCmluc3BlY3Qoc3VwX3N1cHApDQpgYGANCg0KTmlsYWkgcmFzaW8gc3VwcG9ydCBtZW51bmp1a2thbiBwZWx1YW5nIHNlc29yYW5nIHBlbWJlbGkgbWVtYmVsaSBzZWJ1YWggYXRhdSBrb21iaW5hc2kgYmFyYW5nLiBEZW5nYW4ga2F0YSBsYWluLCBzdXBwb3J0IGFkYWxhaCByYXNpbyB0ZXJiZWxpbnlhIHN1YXR1IGJhcmFuZyB0ZXJoYWRhcCB0b3RhbCBwZW1iZWxpYW4gbWlsaWsgY3JlYW0gPT0+IGJyZWFkIGFuZCBjYWtlIG1lbWlsaWtpIHBlbHVhbmcgdGVydGluZ2dpIGRpYmFuZGluZ2thbiBiYXJhbmcgbGFpbnlhIHlhaXR1IHNlYmVzYWFyIDAuNS4gc2l0dWFzaSBpbmkgbWVuamVsYXNrYW4gYmFod2Ega2V0aWthIHNlb3JhbmcgbWVtYmVpbGkgbWlsayBjcmVhbSBpYSBha2FuIG1lbWJlbGkgYnJlYWQgYW5kIGNha2UganVnYS4gDQoNCg0KYGBge3J9DQpzdXBfbGlmdCA8LSBoZWFkKHN1cF9ydWxlcywNCiAgICAgICAgICAgICAgICAgNSwgDQogICAgICAgICAgICAgICAgIGJ5ID0gImxpZnQiKQ0KaW5zcGVjdChzdXBfbGlmdCkNCmBgYA0KDQpOaWxhaSBsaWZ0IGFkYWxhaCB0aW5na2F0IGtlbWFtcHVhbiBzdWF0dSBwZXJpc3Rpd2EgdW50dWsgbWVuZG9yb25nIHRlcmphZGlueWEgcGVyaXN0aXdhIGxhaW4uIERhbGFtIGthc3VzIGluaSwgbmlsYWkgbGlmdCBkYXBhdCBkaSBqZWxhc2thbiBkZW5nYW4ga2VtYXB1YW4gcGVtYmVpbGFuIHN1YXR1IGJhcmFuZyB1bnR1ayBtZW5pbmdrYXRrYW4gcGV1bGFuZyB0ZXJiZWxpbnlhIGJyYW5nIGxhaW4uIA0KY29udG9oIHBhZGEgZGF0YSBmcmFtZSBkaWF0YXMsIG5pbGFpIGxpZnQgZGFyaSBub21lciAxIGFkYWxhaCBzZWJlc2FyIDEuNS4gTmlsYWkgbGlmdCA+IDEgYmVyYXJ0aSBwZW1iZWxpYW4gYmFraW5nIG5lZWRzLCBiaXNjdWl0cywgYnJlYWQgYW5kIGNha2UsIGp1aWNlIHNhciBjb3JkIG1zLCBkYW4gc2F1Y2VzIGdyYXZ0IHBrZWxlDQoNCk1lbWlsaWtpIGtlbWFtcHVhbiB1bnR1ayBtZW5pbmdrYXRrYW4gcGVsdWFuZyBwZW1iZWxpYW4gcGFydHkgc25hY2sgZm9vZHMuIA0KDQoNCiMgVklTVUFMSVNBU0kgDQoNCiFbXShEQVRBIDYucG5nKQ0KDQpBZ2FyIG11ZGFoIGRpYW5hbGlzYSBtYWthIGhhcnVzIG1lbmdndW5hayBWaXN1YWxpc2FzaQ0KDQpCZXJkYXNhcmthbiBydWxlcyB5YW5nIHRlbGFoIGRpYnVhdCBwYWRhIGZ1bmdzaSBhcHJpb3JpKCkgdGVyc2VidXQsIGJlcmlrdXQgdmlzdWFsaXNhc2lueWENCg0KDQoNCmBgYHtyfQ0KcGxvdChzdXBfcnVsZXMsIA0KICAgICBtZXRob2QgPSAiZ3JhcGgiLA0KICAgICBtZWFzdXJlID0gImxpZnQiLCANCiAgICAgZW5naW5lID0gImh0bWx3aWRnZXQiKQ0KYGBgDQoNCg0KR3JhZmlrIGRpYXRhcyBtZW51bmp1a2thbiBodWJ1bmdhbiBhdGF1IGtldGVya2FpdGFuIHBlbWJlbGlhbiBzdWF0dSBiYXJhbmcgdGVyZGFwYXQgcGVtYmVsaWFuIGJhcmFuZyBsYWluLiBQYW5haCBtZW51bmp1a2thbiBiYXJhbmcgeWFuZyBkaWJlbGkgc2VsYW5qdXRueWEuIERpc2luaSwga2l0YSBkYXBhdCBtZWxpaGF0IGJhcmFuZyBtYW5hIHlhbmcgcGFsaW5nIGJhbnlhayBkaWJlbGkgZGFuIG1lbWlsaWtpIGtldGVya2FpdGFuIHRlcmhhZGFwIHBlbWJlbGlhbiBiYXJhbmcgbGFpbnlhLiBNZXRvZGUgaW5pIHNhbmdhdCBiYWlrIGRpZ3VuYWthbiBkaSBpbmR1c3RyaSByZXRhaWwga2FyZW5hIGRhcGF0IG1lbWJhbnR1IGRhbGFtIHBlbnllZGlhYW4gYmFyYW5nIChzdXBwbHkgY2hhaW4pIGRhbiBqdWdhIG1lbmluZ2thdGthbiByZXZlbnVlIHRpYXAgYnVsYW55YS4gRGVuZ2FuIG1lbmdldGFodWkgcGVuanVsYW4gc3VhdCBiYXJhbmcgdGVydGVudHUsIHBpaGFrIHN1cGlsaWVyIGRhcGF0IG1lbmVudHVrYW4gaXRlbSB5YW5nIG1lbWlsaWtpIHBvdGVuc2kgdW50dWsgZGlqdWFsIHNlbGFuanV0bnlhLiANCg0KSW50ZXJwcmV0YXNpOiBkZW5nYW4gU3VwcG9ydCAwLjEgZGFuIENvbmZpZGVuY2UgMC43NSwgc2ViYW55YWsgOTk1OCBydWxlcyBnZW5lcmF0ZWQuIFBhZGEgcGxvdCBkaSBhdGFzIHRlcmxpaGF0IGNsdXN0ZXIgeWFuZyB0ZXJiZW50dWsgaGFueWEgMSBkZW5nYW4gcHJvZHVrLXByb2R1ayBzYWxpbmcgYmVya2FpdGFuIGFudGFyYSBzYXR1IGRlbmdhbiBsYWlubnlhLiBQYXJhbWV0ZXIgU3VwcG9ydCBkYW4gQ29uZmlkZW5jZSB5YW5nIHRpbmdnaSBtZW1idWF0IHByb2R1azIgeWFuZyBwYWxpbmcgYmFueWFrIGRpYmVsaSBkYW4ganVnYSBzZWNhcmEgYmVyc2FtYWFuIG11bmN1bCBzZWJhZ2FpIHByb2R1azIgeWFuZyBzYWxpbmcgdGVya2FpdCBzYXR1IGRhbiBsYWlubnlhLiBLZWphZGlhbiBrZW11bmN1bGFuIGNsdXN0ZXIgYmVzYXIgaW5pIGRhcGF0IHRlcmphZGkgZGFsYW0gdG9rbyB5YW5nIG1lbWlsaWtpIGthdGVnb3JpIHByb2R1ayBncm9jZXJpZXMgYXRhdSBwcm9kdWstcHJvZHVrIHlhbmcga2VtdW5na2luYW4gYmVzYXIgYWthbiBkaWJlbGkgYmVyc2FtYWFuIHNldGlhcCBidWxhbi4gUGFkYSBncmFmaWsgaW5pLCBkYXBhdCBkaWxpaGF0IG5vZGUgZGkgdGVuZ2FoIHlhaXR1IHNuYWNrIHBhcnR5IGZvb2QgZGFuIGJpc2N1aXQgdGFtcGFrIHBhbGluZyBiYW55YWsgdGVyaHVidW5nIGRlbmdhbiBydWxlcyBkaSBzZWtpdGFybnlhLiBEYXBhdCBkaXNpbXB1bGthbiBzZWNhcmEgcHJpbW9yZGlhbCBiYWh3YSBrZWR1YSBrYXRlZ29yaSBwcm9kdWsgdGVyc2VidXQgcGFsaW5nIHVtdW0gZGliZWxpIG9sZWgga29uc3VtZW4gc2VoaW5nZ2EsIHNlYmFnYWkgY29udG9oLCBwcm9tbyBidW5kbGluZyBiZXJpc2kgcHJvZHVrIHRlcnNlYnV0IGJlcnNhbWEgZGVuZ2FuIHByb2R1ayB5YW5nIGphcmFuZyBkaWJlbGkgZGFwYXQgbWVuaW5na2F0a2FuIGF3YXJlbmVzcyBtYXVwdW4ga29udmVyc2kgcGFkYSBzYWxlcyBwcm9kdWsgZGVuZ2FuIHN0b2NrIHRlcnNpc2EgYmFueWFrIG1hdXB1biB5YW5nIHRlbGFoIG1lbmRla2F0aSBrZWRhbHVhcnNhLg0K