JAMALLUDIN

25 DESEMBER 2021

1 Credit Risk dan Model Prediksi

1.1 Pendahuluan

Credit risk adalah resiko yang harus ditanggung oleh seorang individu atau lembaga ketika memberikan pinjaman - biasanya dalam bentuk uang - ke individu atau pihak lain.

Resiko ini berupa tidak bisa dibayarkannya pokok dan bunga pinjaman, sehingga mengakibatkan kerugian berikut:

A. gangguan aliran kas (cash flow) sehingga modal kerja terganggu.

B. meningkatkan biaya operasional untuk mengejar pembayaran tersebut (collection).

Untuk memperkecil resiko kredit ini, biasanya dilakukan proses yang disebut dengan credit scoring dan credit rating terhadap pihak peminjam. Hasil proses ini akan menjadi basis untuk menentukan apakah aplikasi pengajuan pinjaman baru diterima atau ditolak.

1.2 Credit Score

Credit score adalah nilai resiko yang diberikan kepada seorang individu atau organisasi yang mengajukan pinjaman berdasarkan rekam jejak pinjaman dan pembayaran yang dilakukan. Proses pemberian credit score ini biasanya disebut sebagai credit scoring.

Perhitungan credit score biasanya dibuat berdasarkan data riwayat lamanya keterlambatan pembayaran dan yang tidak bayar sama sekali (bad debt). Bad debt biasanya mengakibatkan lembaga pemberian kredit harus menyita aset atau melakukan write off. Nilai credit score biasanya bervariasi antar lembaga. Namun banyak yang kemudian mengadopsi model FICO Score yang memiliki rentang nilai 300 - 850. Semakin tinggi nilai yang didapatkan, maka semakin baik tingkat kemampuan seseorang atau sebuah lembaga untuk membayar pinjaman.

Jadi, credit score adalah nilai kemampuan seorang individu atau organisasi untuk melunasi pembayaran berdasarkan rekam jejak historisnya.

1.3 Risk Rating

Kadang banyak lembaga yang menggunakan risk rating atau tingkat resiko. Terbalik dengan credit score, semakin tinggi rating ini menunjukkan resiko yang semakin meningkat.

Selain itu kodifikasi juga dibuat lebih sederhana dibandingkan rentang nilai sehingga keputusan yang bisa diambil lebih cepat. Contoh, misalkan penggunaan kombinasi seperti huruf AAA, AA+, P-1, dan seterusnya. Atau untuk banyak internal lembaga peminjam, kategorisasi hanya menggunakan rentang angka yang kecil misalkan 1 sampai dengan 5.

Kolom risk_rating ini berelasi langsung dengan kolom overdue_average, atau kolom keterlambatan pembayaran.

Jika keterlambatan sampai dengan 30 hari (0 - 30 days) maka diberikan nilai 1. keterlambatan 31 sampai dengan 45 hari (31 - 45 days) maka scoring diberikan nilai 2.dan seterusnya Dari sini juga beberapa kolom juga diambil oleh analis untuk mencari pola keterkaitannya terhadap rating ini, yaitu:

A. pendapatan dalam jutaan per tahun (pendapatan_setahun_juta).

B. durasi pinjaman dalam satuan bulan (durasi_pinjaman_bulan).

C. jumlah tanggungan (jumlah_tanggungan).

D. apakah ada KPR/Kredit Pemilikan Rumah aktif atau tidak (kpr_aktif).

adapaun rekam jejak historis yang digunakan sebagai basis untuk perhitungan credit score dan risk rating yaitu keterlambatan pembyaran pinjaman.

1.4 Analisa dan Model Pengambilan Keputusan

Masih terkait dengan contoh data sebelumnya, dibawah ini diberikan ilustrasi aktivitas tindak lanjut terhadap data dengan contoh skenario berikut.

Seorang analis akan melakukan penelusuran terhadap data sebelumnya untuk mencari pola. Berikut adalah temuannya:

Jika jumlah tanggungan berjumlah lebih dari 4, kecenderungan resikonya sangat tinggi (rating 4 dan 5). Jika durasi pinjaman semakin lama yaitu lebih dari 24 bulan, maka kecenderungan resiko juga meningkat (rating 4 dan 5). Dari kedua temuan ini, analis akan membentuk aturan-aturan untuk menuntun pengambilan keputusan (decision making model) terhadap pengajuan pinjaman baru untuk sebagai berikut:

Jika jumlah tanggungan berjumlah kurang dari 5 orang, dan durasi pinjaman kurang dari 24 bulan maka rating diberikan nilai 2 dan pengajuan pinjaman diterima. Jika jumlah tanggungan berjumlah lebih dari 4 orang dan durasi pinjaman lebih dari 24 bulan maka maka rating diberikan nilai 5 dan pengajuan pinjaman ditolak. Jika jumlah tanggungan berjumlah kurang dari 5, dan durasi pinjaman kurang dari 36 bulan maka maka rating diberikan nilai 3 dan diberikan pinjaman. Nah, tiga aturan itu akan disebut sebagai model untuk memprediksi nilai risk rating dan menjadi basis pengambilan keputusan terhadap aplikasi pinjaman baru.

Dengan model ini, lembaga pinjaman akan semakin cepat mengambil keputusan dan dengan tingkat kesalahan pengambilan keputusan yang lebih minim.

Adapun yang dilakukan oleh analyst dari skenario diatas adalah mencari pola antara credit score dengan variabel lain dan membuat model pengambilan keputusan untuk apliaksi pinjaman baru.

2 Decision Tree

Pemodelan Decision Tree dengan Machine Learning

Dibawah adalah contoh otomatisasi model decision tree dengan menggunakan salah satu algoritma populer di R, yaitu C5.0. Package C5.0 digunakan untuk pengenalan pola dengan pemodelan decision tree dan rule-based. .

library("openxlsx")
library("C50")
summary(modelKu)

Call:
C5.0.default(x = datafeed, y = as.factor(dataCreditRating$risk_rating))


C5.0 [Release 2.07 GPL Edition]     Sat Dec 25 11:42:06 2021
-------------------------------

Class specified by attribute `outcome'

Read 900 cases (4 attributes) from undefined.data

Decision tree:

jumlah_tanggungan > 4:
:...durasi_pinjaman_bulan <= 24: 4 (112/30)
:   durasi_pinjaman_bulan > 24: 5 (140/55)
jumlah_tanggungan <= 4:
:...jumlah_tanggungan > 2: 3 (246/22)
    jumlah_tanggungan <= 2:
    :...durasi_pinjaman_bulan <= 36: 1 (294/86)
        durasi_pinjaman_bulan > 36:
        :...jumlah_tanggungan <= 0: 2 (41/8)
            jumlah_tanggungan > 0: 3 (67/4)


Evaluation on training data (900 cases):

        Decision Tree   
      ----------------  
      Size      Errors  

         6  205(22.8%)   <<


       (a)   (b)   (c)   (d)   (e)    <-classified as
      ----  ----  ----  ----  ----
       208     2     5     6     6    (a): class 1
        86    33    21     6    13    (b): class 2
               4   287                (c): class 3
               2          82    36    (d): class 4
                          18    85    (e): class 5


    Attribute usage:

    100.00% jumlah_tanggungan
     72.67% durasi_pinjaman_bulan


Time: 0.1 secs

Hasil diatas adalah bentuk representasi tree/pohon dalam bentuk teks. Dimana pengecekan akan dimulai dari variabel jumlah_tanggungan. Sebagai contoh, jika jumlah tanggungan lebih dari 4 dan durasi pinjaman sampai dengan maksimal 24 bulan maka rating diberikan nilai 4.

3 Algoritma C5.0

3.1 Apa Itu Algoritma C5.0?

C5.0 adalah kode penamaan suatu algoritma untuk decision tree. Banyak algoritma lain seperti random forest, CART, CHAID, MARS, dan lain-lain. Namun C5.0 adalah algoritma yang sangat populer karena memiliki performa yang sangat baik dari sisi kecepatan maupun akurasi.

Algoritma ini sering dikategorikan sebagai classification, dimana tujuannya adalah untuk mengkategorikan atau mengklasifikan sesuatu - pada contoh risk rating - berdasarkan input dari data-data lain.

3.2 Data Preparation untuk Class Variable

str(dataCreditRating)
'data.frame':   900 obs. of  7 variables:
 $ kode_kontrak           : chr  "AGR-000001" "AGR-000011" "AGR-000030" "AGR-000043" ...
 $ pendapatan_setahun_juta: num  295 271 159 210 165 220 70 88 163 100 ...
 $ kpr_aktif              : chr  "YA" "YA" "TIDAK" "YA" ...
 $ durasi_pinjaman_bulan  : num  48 36 12 12 36 24 36 48 48 36 ...
 $ jumlah_tanggungan      : num  5 5 0 3 0 5 3 3 5 6 ...
 $ rata_rata_overdue      : chr  "61 - 90 days" "61 - 90 days" "0 - 30 days" "46 - 60 days" ...
 $ risk_rating            : Factor w/ 5 levels "1","2","3","4",..: 4 4 1 3 2 1 2 2 2 2 ...

Untuk class variable, yaitu kolom risk_rating ternyata masih dibaca dalam bentuk numerik. Untuk menjadi class variable yang digunakan pada algoritma C5.0, maka perlu dikonversi menjadi factor. Ini bisa dilakukan dengan menggunakan fungsi as.factor() yang disimpan ke dalam variabel yang sudah mengarah ke kolom yang ingin dikonversi.

str(dataCreditRating)
'data.frame':   900 obs. of  7 variables:
 $ kode_kontrak           : chr  "AGR-000001" "AGR-000011" "AGR-000030" "AGR-000043" ...
 $ pendapatan_setahun_juta: num  295 271 159 210 165 220 70 88 163 100 ...
 $ kpr_aktif              : chr  "YA" "YA" "TIDAK" "YA" ...
 $ durasi_pinjaman_bulan  : num  48 36 12 12 36 24 36 48 48 36 ...
 $ jumlah_tanggungan      : num  5 5 0 3 0 5 3 3 5 6 ...
 $ rata_rata_overdue      : chr  "61 - 90 days" "61 - 90 days" "0 - 30 days" "46 - 60 days" ...
 $ risk_rating            : Factor w/ 5 levels "1","2","3","4",..: 4 4 1 3 2 1 2 2 2 2 ...

Maka kolom risk_rating sudah bertipe data factor dan class variabel sudah terbentuk.

3.3 Data Preparation untuk Input Variables

Tidak semua input variabel yang perlu akan gunakan, apalagi yang sangat berkaitan sangat erat dengan risk_rating, yaitu rata_rata_overdue. Input variabel ini akan dibuang. Proses ini disebut sebagai feature selection.

Karena menggunakan data frame sebagai tipe data input untuk C5.0, maka fields/kolom-kolom yang ingin digunakan bisa dimasukkan sebagai vector sebagai filter.

str(datafeed)
'data.frame':   900 obs. of  2 variables:
 $ durasi_pinjaman_bulan: num  48 36 12 12 36 24 36 48 48 36 ...
 $ jumlah_tanggungan    : num  5 5 0 3 0 5 3 3 5 6 ...

Catatan: kode_kontrak harusnya tidak dipilih karena unik untuk keseluruhan data, dan tidak menjadi penentu untuk membentuk pola. Tetapi ini dimasukkan dengan tujuan untuk menunjukkan C5.0 memiliki kemampuan untuk membuang variabel input yang tidak relevan secara otomatis.

3.4 Traning Set dan Testing Set

Untuk proses pembentukan model machine learning dan melihat akurasinya, biasanya dataset perlu dibagi menjadi dua, yaitu:

Training set: adalah porsi dataset yang digunakan oleh algoritma untuk dianalisa dan menjadi input untuk pembentukan model. Testing set: adalah porsi dataset yang tidak digunakan untuk membentuk model, tapi untuk menguji model yang telah dibentuk. Pembentukan ini biasanya menggunakan metode pemilihan acak.

Training dan testing set akan mengambil dari variabel data frame bernama datafeed yang telah dipersiapkan sebelumnya. Jumlah baris dataset adalah 900, dimana akan mengambil 800 baris secara acak sebagai training set dan sisa 100 sebagai testing set.

Dimana:

set.seed(100) adalah perintah untuk menyeragamkan pengambilan bilangan acak di seluruh aplikasi R. sample(900, 800) adalah membuat urutan acak dengan rentang nilai 1 sampai dengan 900, tetapi diambil sebanyak 800 nilai.

Dimana:

input_training_set akan diisi dari data frame datafeed dengan jumlah indeksnya yang terdapat pada variabel indeks_training_set.

class_training_set akan diisi dari data frame dataCreditRating dengan indeks yang isinya terdapat pada variabel indeks_training_set.

input_testing_set akan diisi datafeed dengan indeks yang isinya ‘tidak ada’ di indeks_training_set - perhatikan tanda minus yang ada di depan variabel indeks_training_set.

str(input_training_set)
'data.frame':   800 obs. of  2 variables:
 $ durasi_pinjaman_bulan: num  36 24 36 36 36 24 12 48 48 12 ...
 $ jumlah_tanggungan    : num  1 1 5 1 5 3 3 3 0 0 ...
str(class_training_set)
 Factor w/ 5 levels "1","2","3","4",..: 1 1 4 1 5 3 3 3 2 1 ...
str(input_testing_set)
'data.frame':   100 obs. of  2 variables:
 $ durasi_pinjaman_bulan: num  12 36 48 36 48 48 12 12 12 12 ...
 $ jumlah_tanggungan    : num  0 0 3 3 6 5 0 0 0 4 ...

3.5 Mengahasilkan Model dengan Fungsi C5.0

Fungsi C5.0 mengambil beberapa argumen didalamnya. Contohnya C5.0(x, y, trials = …, …). Baca selengkapnya di sini. Namun tidak semua argumen akan digunakan. Atau agar lebih mudah rumus fungsi C5.0 dalam kasus ini yaitu C5.0(input_variables, class_variables).

Dari variabel sebelumnya, hanya butuh argumen x dan y.

Dimana x dan y di fungsi C5.0:

x: sebuah data frame atau matiks untuk prediksi. y: sebuah faktor yang memiliki 2 level atau lebih. Maksud level disini yaitu banyaknya jumlah data di dalam data frame atau kolom bertipe data factor. Dengan menggunakan dataset yang sudah disiapkan maka perintah untuk membentuk model dengan fungsi C5.0 dan sekaligus menyimpannya dalam satu variabel bernama risk_rating_model adalah sebagai berikut:

summary(risk_rating_model)

Call:
C5.0.default(x = input_training_set, y = class_training_set)


C5.0 [Release 2.07 GPL Edition]     Sat Dec 25 11:43:00 2021
-------------------------------

Class specified by attribute `outcome'

Read 800 cases (3 attributes) from undefined.data

Decision tree:

jumlah_tanggungan > 4:
:...durasi_pinjaman_bulan <= 24: 4 (105/30)
:   durasi_pinjaman_bulan > 24: 5 (120/51)
jumlah_tanggungan <= 4:
:...jumlah_tanggungan > 2: 3 (216/20)
    jumlah_tanggungan <= 2:
    :...durasi_pinjaman_bulan <= 36: 1 (264/80)
        durasi_pinjaman_bulan > 36:
        :...jumlah_tanggungan <= 0: 2 (37/7)
            jumlah_tanggungan > 0: 3 (58/4)


Evaluation on training data (800 cases):

        Decision Tree   
      ----------------  
      Size      Errors  

         6  192(24.0%)   <<


       (a)   (b)   (c)   (d)   (e)    <-classified as
      ----  ----  ----  ----  ----
       184     2     5     6     6    (a): class 1
        80    30    19     6    11    (b): class 2
               3   250                (c): class 3
               2          75    34    (d): class 4
                          18    69    (e): class 5


    Attribute usage:

    100.00% jumlah_tanggungan
     73.00% durasi_pinjaman_bulan


Time: 0.0 secs

Output tersebut menceritakan tingkat pentingnya penggunaan tiap variabel. Disini jumlah_tanggungan menempati urutan pertama dengan nilai 100% dan durasi_pinjaman dengan 73.00%.

3.6 Visualisasi dari Model C5.0

Selain model teks dari output sebelumnya, bisa juga menghasilkan decision tree dalam bentuk grafik.

4 Evaluasi Model

4.1 Pendahuluan

Confusion Matrix yang terdapat pada output model sebelumnya adalah evaluasi model menggunakan training set. Namun, perlu mengevaluasi model ini terhadap testing set yang telah disiapkan.

4.2 Menggunakan Fungsi Predict

Package C50 memiliki fungsi bernama predict, yang bisa digunakan untuk melakukan prediksi berdasarkan input model dan data test. Fungsi lengkapnya terlihat sebagai berikut.

predict(risk_rating_model, input_testing_set)
  [1] 1 1 3 3 5 5 1 1 1 3 1 2 1 1 3 3 1 3 3 3 3 3 1 5 1 1 3 1 3 5 1 1 2 1 5 1 1 5 3 3 3 3 4 3 3 1
 [47] 3 5 2 3 2 5 3 5 1 5 4 5 3 4 1 3 4 4 3 5 5 5 3 1 1 1 1 3 5 1 4 5 3 1 3 3 3 3 3 1 3 3 5 4 5 3
 [93] 3 3 1 1 5 5 3 3
Levels: 1 2 3 4 5

Terlihat hasil prediksi semua sesuai dengan posisi baris data dari testing set. Dan ini juga sesuai dengan rentang nilai risk_rating, yaitu 1 sampai dengan 5.

Model dan data frame dengan input variables adalah yang diperlukan oleh fungsi predict.

4.3 Menggabungkan Hasil Prediksi

Seperti diinformasikan pada subbab sebelumnya, bagaimana cara menyimpan risk_rating dari dataset awal dan hasil prediksi ini ke dalam dua kolom nama yang lain di data frame input_testing_set. Mari namakan kolom tersebut dengan risk_rating dan hasil_prediksi.

Dengan kolom risk_rating dan hasil_prediksi bersampingan, disini bisa langsung bandingkan data awal dengan hasil prediksi. Terlihat ada rating yang sama (prediksi benar) dan berbeda (prediksi salah). Lalu selanjunya akan dievaluasi tingkat akurasi dari kedua kolom ini dengan menghasilkan confusion matrix pada bagian selanjutnya.

4.4 Membuat Tabel Confusion Matrix

Setelah hasil prediksi terhadap testing set selesai, langkah berikutnya coba lihat distribusi mana yang terprediksi dengan benar dan salah. Ini dilakukan dengan confusion matrix.

dcast(hasil_prediksi ~ risk_rating, data=input_testing_set)
Using hasil_prediksi as value column: use value.var to override.
Aggregation function missing: defaulting to length

Diagonal yang angka 24, 3, 37, 7, dan 16 menunjukkan jumlah data yang terprediksi dengan benar, dan sisa angka lainnya menunjukkan data yang salah terprediksi.

Sekilas terlihat jumlah yang terprediksi dengan benar jauh lebih besar porsinya dibandingkan yang salah. Namun untuk yang 4 dan 5 ada sedikit catatan dimana prediksi 5 itu kadang jatuh ke 4. Namun ini bisa diabaikan, karena 4 dan 5 memang beresiko tinggi.

4.5 Jumlah Data dengan Prediksi Benar

Untuk menghitung persentase error, bisa menghitung terlebih dahulu jumlah data dengan prediksi yang benar. Hasil dikatakan benar jika data risk_rating sama dengan hasil_prediksi.

input_testing_set$risk_rating == input_testing_set$hasil_prediksi
  [1]  TRUE FALSE FALSE FALSE FALSE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE  TRUE
 [16]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE  TRUE  TRUE  TRUE FALSE  TRUE  TRUE
 [31]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE
 [46]  TRUE  TRUE  TRUE  TRUE  TRUE FALSE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE
 [61]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE
 [76]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE
 [91]  TRUE  TRUE  TRUE  TRUE  TRUE FALSE  TRUE  TRUE  TRUE  TRUE

Ini artinya kalau TRUE maka data pada posisi tersebut prediksinya benar dan FALSE untuk sebaliknya. Namun ini masih belum dalam bentuk yang diinginkan. Masih perlu beberapa tambahan perintah tersebut untuk mengeluarkan persentase prediksi yang tepat.

Langkah berikutnya, adalah filtering/menyaring data frame tersebut dengan hasil tadi dengan perintah berikut.

Terlihat semua hasil filtering memiliki nilai yang sama untuk kolom risk_rating dan hasil_prediksi. Kemudian akan dihitung jumlah baris filtering ini dengan menambahkan fungsi nrow terhadap perintah di atas.

nrow(input_testing_set[input_testing_set$risk_rating == input_testing_set$hasil_prediksi, ])
[1] 87

Angka 87 ini menunjukkan jumlah data dengan prediksi yang benar terhadap testing set. Karena testing set berjumlah 100, maka persentase prediksi yang benar adalah 87%, dan error rate atau persentase yang salah adalah 13%.

Mari cek kembali hasil tersebut dengan menjumlahkan posisi diagonal pada confusion matrix yang telah dibuat sebelumnya.

dcast(hasil_prediksi ~ risk_rating, data=input_testing_set)
Using hasil_prediksi as value column: use value.var to override.
Aggregation function missing: defaulting to length

Hasilnya juga 87 yang merupakan hasil dari operasi penjumlah 24 + 3 + 37 + 7 + 16.

4.6 Jumlah Data dengan Prediksi Salah

Bagaimana kalau hanya ingin mencari yang tidak sama, dengan kata lain mencari yang salah prediksinya?

nrow(input_testing_set[input_testing_set$risk_rating != input_testing_set$hasil_prediksi,])
[1] 13

Terlihat bahwa jumlah prediksi error ada 13. Hasil ini konsisten jika dibandingkan dengan jumlah 87 dari prediksi yang benar, dimana total keduanya adalah 100 - yang merupakan jumlah data untuk testing set.

4.7 Kesimpulan

menjadi bagian yang sangat penting yaitu untuk pertama kalinya melakukan prediksi dengan function predict terhadap porsi data testing set.

Tujuannya adalah mengukur akurasi dari hasil prediksi dengan data awal. Ini digunakan dengan menggunakan confusion matrix juga dan menghitung jumlah prediksi data yang benar dan salah secara agak “manual”. Pendekatan terakhir adalah membandingkan data awal dan hasil prediksi di data frame dengan operator ==, !=, dan nrow.

Dengan tingkat error 13%, dan walaupun ada catatan untuk klasifikasi 4 dan 5, namun secara garis besar bisa anggap model ini cukup baik. Dan dengan keputusan ini, saatnya mengadopsi model ini untuk melakukan prediksi per data aplikasi kredit baru yang masuk.

5 Menggunakan Model untuk Prediksi

Tahap terakhir setelah melakukan evaluasi dan yakin akan akurasinya, model akan digunakan dalam keseharian untuk melakukan prediksi risk rating dari data baru.

5.1 Mempersiapkan Data Pengajuan Baru

Data pengajuan baru perlu dibentuk sebagai satu data frame dengan input dimana nama-nama variabel yang digunakan harus sama persis. Dari awal pemodelan, sudah menggunakan dua variabel yakni:

jumlah_tanggungan

durasi_pinjaman_bulan

Keduanya dalam bentuk numerik (angka)

Data frame ini akan digunakan sebagai input untuk prediksi

5.2 Melakukan Prediksi terhadap Data Pengajuan Baru

Data aplikasi baru yang telah dibuat sebelumnya akan diprediksi nilai risk_rating nya dengan fungsi predict, dimana cara penggunaannya masih sama.

Maka penyesuaian perintah di atas dengan nama model dan variabel yang digunakan, adalah variabel risk_rating_model sebagai model dan aplikasi_baru sebagai data frame yang akan di prediksi.

predict(risk_rating_model, aplikasi_baru)
[1] 4
Levels: 1 2 3 4 5

Ini artinya hasil prediksi risk_rating untuk aplikasi baru ini adalah 4, dari kemungkinan 1, 2, 3, 4 dan 5. Nilai 4 ini adalah nilai resiko yang cukup tinggi, jadi bisa saja aplikasi baru ini ditolak sesuai dengan kebijakan lembaga peminjam.

5.3 Merubah Durasi Pinjaman

Sekarang dicoba memprediksi dari data yang tidak ada dari data set yang dijadikan model.

Mari coba ganti durasi pinjaman selama 64 bulan.

predict(risk_rating_model, aplikasi_baru)
[1] 5
Levels: 1 2 3 4 5

Ini artinya hasil prediksi risk_rating untuk aplikasi baru ini adalah 5, dari kemungkinan 1, 2, 3, 4 dan 5. Nilai 5 ini adalah nilai resiko yang sangat tinggi dikarenakan durasi peminjaman tidak termasuk dalam data yang di lakukan model.

LS0tDQp0aXRsZTogIkNSRURJVCBSSVNLIEFOQUxZU0lTIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KSkFNQUxMVURJTiANCg0KMjUgREVTRU1CRVIgMjAyMSANCg0KDQoNCiMgMSBDcmVkaXQgUmlzayBkYW4gTW9kZWwgUHJlZGlrc2kNCg0KIyAxLjEgUGVuZGFodWx1YW4NCg0KQ3JlZGl0IHJpc2sgYWRhbGFoIHJlc2lrbyB5YW5nIGhhcnVzIGRpdGFuZ2d1bmcgb2xlaCBzZW9yYW5nIGluZGl2aWR1IGF0YXUgbGVtYmFnYSBrZXRpa2EgbWVtYmVyaWthbiBwaW5qYW1hbiAtIGJpYXNhbnlhIGRhbGFtIGJlbnR1ayB1YW5nIC0ga2UgaW5kaXZpZHUgYXRhdSBwaWhhayBsYWluLg0KDQpSZXNpa28gaW5pIGJlcnVwYSB0aWRhayBiaXNhIGRpYmF5YXJrYW5ueWEgcG9rb2sgZGFuIGJ1bmdhIHBpbmphbWFuLCBzZWhpbmdnYSBtZW5nYWtpYmF0a2FuIGtlcnVnaWFuIGJlcmlrdXQ6DQoNCkEuIGdhbmdndWFuIGFsaXJhbiBrYXMgKGNhc2ggZmxvdykgc2VoaW5nZ2EgbW9kYWwga2VyamEgdGVyZ2FuZ2d1Lg0KDQpCLiBtZW5pbmdrYXRrYW4gYmlheWEgb3BlcmFzaW9uYWwgdW50dWsgbWVuZ2VqYXIgcGVtYmF5YXJhbiB0ZXJzZWJ1dCAoY29sbGVjdGlvbikuDQoNClVudHVrIG1lbXBlcmtlY2lsIHJlc2lrbyBrcmVkaXQgaW5pLCBiaWFzYW55YSBkaWxha3VrYW4gcHJvc2VzIHlhbmcgZGlzZWJ1dCBkZW5nYW4gY3JlZGl0IHNjb3JpbmcgZGFuIGNyZWRpdCByYXRpbmcgdGVyaGFkYXAgcGloYWsgcGVtaW5qYW0uIEhhc2lsIHByb3NlcyBpbmkgYWthbiBtZW5qYWRpIGJhc2lzIHVudHVrIG1lbmVudHVrYW4gYXBha2FoIGFwbGlrYXNpIHBlbmdhanVhbiBwaW5qYW1hbiBiYXJ1IGRpdGVyaW1hIGF0YXUgZGl0b2xhay4NCg0KIyAxLjIgQ3JlZGl0IFNjb3JlDQoNCkNyZWRpdCBzY29yZSBhZGFsYWggbmlsYWkgcmVzaWtvIHlhbmcgZGliZXJpa2FuIGtlcGFkYSBzZW9yYW5nIGluZGl2aWR1IGF0YXUgb3JnYW5pc2FzaSB5YW5nIG1lbmdhanVrYW4gcGluamFtYW4gYmVyZGFzYXJrYW4gcmVrYW0gamVqYWsgcGluamFtYW4gZGFuIHBlbWJheWFyYW4geWFuZyBkaWxha3VrYW4uIFByb3NlcyBwZW1iZXJpYW4gY3JlZGl0IHNjb3JlIGluaSBiaWFzYW55YSBkaXNlYnV0IHNlYmFnYWkgY3JlZGl0IHNjb3JpbmcuDQoNClBlcmhpdHVuZ2FuIGNyZWRpdCBzY29yZSBiaWFzYW55YSBkaWJ1YXQgYmVyZGFzYXJrYW4gZGF0YSByaXdheWF0IGxhbWFueWEga2V0ZXJsYW1iYXRhbiBwZW1iYXlhcmFuIGRhbiB5YW5nIHRpZGFrIGJheWFyIHNhbWEgc2VrYWxpIChiYWQgZGVidCkuIEJhZCBkZWJ0IGJpYXNhbnlhIG1lbmdha2liYXRrYW4gbGVtYmFnYSBwZW1iZXJpYW4ga3JlZGl0IGhhcnVzIG1lbnlpdGEgYXNldCBhdGF1IG1lbGFrdWthbiB3cml0ZSBvZmYuIE5pbGFpIGNyZWRpdCBzY29yZSBiaWFzYW55YSBiZXJ2YXJpYXNpIGFudGFyIGxlbWJhZ2EuIE5hbXVuIGJhbnlhayB5YW5nIGtlbXVkaWFuIG1lbmdhZG9wc2kgbW9kZWwgRklDTyBTY29yZSB5YW5nIG1lbWlsaWtpIHJlbnRhbmcgbmlsYWkgMzAwIC0gODUwLiBTZW1ha2luIHRpbmdnaSBuaWxhaSB5YW5nIGRpZGFwYXRrYW4sIG1ha2Egc2VtYWtpbiBiYWlrIHRpbmdrYXQga2VtYW1wdWFuIHNlc2VvcmFuZyBhdGF1IHNlYnVhaCBsZW1iYWdhIHVudHVrIG1lbWJheWFyIHBpbmphbWFuLg0KDQpKYWRpLCBjcmVkaXQgc2NvcmUgYWRhbGFoIG5pbGFpIGtlbWFtcHVhbiBzZW9yYW5nIGluZGl2aWR1IGF0YXUgb3JnYW5pc2FzaSB1bnR1ayBtZWx1bmFzaSBwZW1iYXlhcmFuIGJlcmRhc2Fya2FuIHJla2FtIGplamFrIGhpc3RvcmlzbnlhLg0KDQoNCiMgMS4zIFJpc2sgUmF0aW5nDQoNCkthZGFuZyBiYW55YWsgbGVtYmFnYSB5YW5nIG1lbmdndW5ha2FuIHJpc2sgcmF0aW5nIGF0YXUgdGluZ2thdCByZXNpa28uIFRlcmJhbGlrIGRlbmdhbiBjcmVkaXQgc2NvcmUsIHNlbWFraW4gdGluZ2dpIHJhdGluZyBpbmkgbWVudW5qdWtrYW4gcmVzaWtvIHlhbmcgc2VtYWtpbiBtZW5pbmdrYXQuDQoNClNlbGFpbiBpdHUga29kaWZpa2FzaSBqdWdhIGRpYnVhdCBsZWJpaCBzZWRlcmhhbmEgZGliYW5kaW5na2FuIHJlbnRhbmcgbmlsYWkgc2VoaW5nZ2Ega2VwdXR1c2FuIHlhbmcgYmlzYSBkaWFtYmlsIGxlYmloIGNlcGF0LiBDb250b2gsIG1pc2Fsa2FuIHBlbmdndW5hYW4ga29tYmluYXNpIHNlcGVydGkgaHVydWYgQUFBLCBBQSssIFAtMSwgZGFuIHNldGVydXNueWEuIEF0YXUgdW50dWsgYmFueWFrIGludGVybmFsIGxlbWJhZ2EgcGVtaW5qYW0sIGthdGVnb3Jpc2FzaSBoYW55YSBtZW5nZ3VuYWthbiByZW50YW5nIGFuZ2thIHlhbmcga2VjaWwgbWlzYWxrYW4gMSBzYW1wYWkgZGVuZ2FuIDUuDQoNCg0KDQpLb2xvbSByaXNrX3JhdGluZyBpbmkgYmVyZWxhc2kgbGFuZ3N1bmcgZGVuZ2FuIGtvbG9tIG92ZXJkdWVfYXZlcmFnZSwgYXRhdSBrb2xvbSBrZXRlcmxhbWJhdGFuIHBlbWJheWFyYW4uDQoNCkppa2Ega2V0ZXJsYW1iYXRhbiBzYW1wYWkgZGVuZ2FuIDMwIGhhcmkgKDAgLSAzMCBkYXlzKSBtYWthIGRpYmVyaWthbiBuaWxhaSAxLg0Ka2V0ZXJsYW1iYXRhbiAzMSBzYW1wYWkgZGVuZ2FuIDQ1IGhhcmkgKDMxIC0gNDUgZGF5cykgbWFrYSBzY29yaW5nIGRpYmVyaWthbiBuaWxhaSAyLmRhbiBzZXRlcnVzbnlhIERhcmkgc2luaSBqdWdhIGJlYmVyYXBhIGtvbG9tIGp1Z2EgZGlhbWJpbCBvbGVoIGFuYWxpcyB1bnR1ayBtZW5jYXJpIHBvbGEga2V0ZXJrYWl0YW5ueWEgdGVyaGFkYXAgcmF0aW5nIGluaSwgeWFpdHU6DQoNCkEuIHBlbmRhcGF0YW4gZGFsYW0ganV0YWFuIHBlciB0YWh1biAocGVuZGFwYXRhbl9zZXRhaHVuX2p1dGEpLg0KDQpCLiBkdXJhc2kgcGluamFtYW4gZGFsYW0gc2F0dWFuIGJ1bGFuIChkdXJhc2lfcGluamFtYW5fYnVsYW4pLg0KDQpDLiBqdW1sYWggdGFuZ2d1bmdhbiAoanVtbGFoX3RhbmdndW5nYW4pLg0KDQpELiBhcGFrYWggYWRhIEtQUi9LcmVkaXQgUGVtaWxpa2FuIFJ1bWFoIGFrdGlmIGF0YXUgdGlkYWsgKGtwcl9ha3RpZikuDQoNCmFkYXBhdW4gcmVrYW0gamVqYWsgaGlzdG9yaXMgeWFuZyBkaWd1bmFrYW4gc2ViYWdhaSBiYXNpcyB1bnR1ayBwZXJoaXR1bmdhbiBjcmVkaXQgc2NvcmUgZGFuIHJpc2sgcmF0aW5nIHlhaXR1IGtldGVybGFtYmF0YW4gcGVtYnlhcmFuIHBpbmphbWFuLg0KDQojIDEuNCBBbmFsaXNhIGRhbiBNb2RlbCBQZW5nYW1iaWxhbiBLZXB1dHVzYW4NCg0KTWFzaWggdGVya2FpdCBkZW5nYW4gY29udG9oIGRhdGEgc2ViZWx1bW55YSwgZGliYXdhaCBpbmkgZGliZXJpa2FuIGlsdXN0cmFzaSBha3Rpdml0YXMgdGluZGFrIGxhbmp1dCB0ZXJoYWRhcCBkYXRhIGRlbmdhbiBjb250b2ggc2tlbmFyaW8gYmVyaWt1dC4NCg0KU2VvcmFuZyBhbmFsaXMgYWthbiBtZWxha3VrYW4gcGVuZWx1c3VyYW4gdGVyaGFkYXAgZGF0YSBzZWJlbHVtbnlhIHVudHVrIG1lbmNhcmkgcG9sYS4gQmVyaWt1dCBhZGFsYWggdGVtdWFubnlhOg0KDQpKaWthIGp1bWxhaCB0YW5nZ3VuZ2FuIGJlcmp1bWxhaCBsZWJpaCBkYXJpIDQsIGtlY2VuZGVydW5nYW4gcmVzaWtvbnlhIHNhbmdhdCB0aW5nZ2kgKHJhdGluZyA0IGRhbiA1KS4NCkppa2EgZHVyYXNpIHBpbmphbWFuIHNlbWFraW4gbGFtYSB5YWl0dSBsZWJpaCBkYXJpIDI0IGJ1bGFuLCBtYWthIGtlY2VuZGVydW5nYW4gcmVzaWtvIGp1Z2EgbWVuaW5na2F0IChyYXRpbmcgNCBkYW4gNSkuDQpEYXJpIGtlZHVhIHRlbXVhbiBpbmksIGFuYWxpcyBha2FuIG1lbWJlbnR1ayBhdHVyYW4tYXR1cmFuIHVudHVrIG1lbnVudHVuIHBlbmdhbWJpbGFuIGtlcHV0dXNhbiAoZGVjaXNpb24gbWFraW5nIG1vZGVsKSB0ZXJoYWRhcCBwZW5nYWp1YW4gcGluamFtYW4gYmFydSB1bnR1ayBzZWJhZ2FpIGJlcmlrdXQ6DQoNCkppa2EganVtbGFoIHRhbmdndW5nYW4gYmVyanVtbGFoIGt1cmFuZyBkYXJpIDUgb3JhbmcsIGRhbiBkdXJhc2kgcGluamFtYW4ga3VyYW5nIGRhcmkgMjQgYnVsYW4gbWFrYSByYXRpbmcgZGliZXJpa2FuIG5pbGFpIDIgZGFuIHBlbmdhanVhbiBwaW5qYW1hbiBkaXRlcmltYS4NCkppa2EganVtbGFoIHRhbmdndW5nYW4gYmVyanVtbGFoIGxlYmloIGRhcmkgNCBvcmFuZyBkYW4gZHVyYXNpIHBpbmphbWFuIGxlYmloIGRhcmkgMjQgYnVsYW4gbWFrYSBtYWthIHJhdGluZyBkaWJlcmlrYW4gbmlsYWkgNSBkYW4gcGVuZ2FqdWFuIHBpbmphbWFuIGRpdG9sYWsuDQpKaWthIGp1bWxhaCB0YW5nZ3VuZ2FuIGJlcmp1bWxhaCBrdXJhbmcgZGFyaSA1LCBkYW4gZHVyYXNpIHBpbmphbWFuIGt1cmFuZyBkYXJpIDM2IGJ1bGFuIG1ha2EgbWFrYSByYXRpbmcgZGliZXJpa2FuIG5pbGFpIDMgZGFuIGRpYmVyaWthbiBwaW5qYW1hbi4NCk5haCwgdGlnYSBhdHVyYW4gaXR1IGFrYW4gZGlzZWJ1dCBzZWJhZ2FpIG1vZGVsIHVudHVrIG1lbXByZWRpa3NpIG5pbGFpIHJpc2sgcmF0aW5nIGRhbiBtZW5qYWRpIGJhc2lzIHBlbmdhbWJpbGFuIGtlcHV0dXNhbiB0ZXJoYWRhcCBhcGxpa2FzaSBwaW5qYW1hbiBiYXJ1Lg0KDQpEZW5nYW4gbW9kZWwgaW5pLCBsZW1iYWdhIHBpbmphbWFuIGFrYW4gc2VtYWtpbiBjZXBhdCBtZW5nYW1iaWwga2VwdXR1c2FuIGRhbiBkZW5nYW4gdGluZ2thdCBrZXNhbGFoYW4gcGVuZ2FtYmlsYW4ga2VwdXR1c2FuIHlhbmcgbGViaWggbWluaW0uDQoNCkFkYXB1biB5YW5nIGRpbGFrdWthbiBvbGVoIGFuYWx5c3QgZGFyaSBza2VuYXJpbyBkaWF0YXMgYWRhbGFoIG1lbmNhcmkgcG9sYSBhbnRhcmEgY3JlZGl0IHNjb3JlIGRlbmdhbiB2YXJpYWJlbCBsYWluIGRhbiBtZW1idWF0IG1vZGVsIHBlbmdhbWJpbGFuIGtlcHV0dXNhbiB1bnR1ayBhcGxpYWtzaSBwaW5qYW1hbiBiYXJ1Lg0KDQoNCiMgMiBEZWNpc2lvbiBUcmVlDQoNClBlbW9kZWxhbiBEZWNpc2lvbiBUcmVlIGRlbmdhbiBNYWNoaW5lIExlYXJuaW5nDQoNCkRpYmF3YWggYWRhbGFoIGNvbnRvaCBvdG9tYXRpc2FzaSBtb2RlbCBkZWNpc2lvbiB0cmVlIGRlbmdhbiBtZW5nZ3VuYWthbiBzYWxhaCBzYXR1IGFsZ29yaXRtYSBwb3B1bGVyIGRpIFIsIHlhaXR1IEM1LjAuIFBhY2thZ2UgQzUuMCBkaWd1bmFrYW4gdW50dWsgcGVuZ2VuYWxhbiBwb2xhIGRlbmdhbiBwZW1vZGVsYW4gZGVjaXNpb24gdHJlZSBkYW4gcnVsZS1iYXNlZC4gLg0KDQpgYGB7cn0NCmxpYnJhcnkoIm9wZW54bHN4IikNCmxpYnJhcnkoIkM1MCIpDQoNCmBgYA0KDQoNCmBgYHtyfQ0KIyBNZW1wZXJzaWFwa2FuIGRhdGENCg0KZGF0YUNyZWRpdFJhdGluZyA8LSByZWFkLnhsc3goeGxzeEZpbGUgPSAiY3JlZGl0X3Njb3JpbmdfZHFsYWIueGxzeCIpDQoNCmRhdGFDcmVkaXRSYXRpbmckcmlza19yYXRpbmcgPC0gYXMuZmFjdG9yKGRhdGFDcmVkaXRSYXRpbmckcmlza19yYXRpbmcpIA0KDQpoZWFkKGRhdGFDcmVkaXRSYXRpbmcpDQpgYGANCg0KDQpgYGB7cn0NCiMgTWVuZ2d1bmFrYW4gQzUuMA0KDQpkcm9wX2NvbHVtbnMgPC0gYygia3ByX2FrdGlmIiwgInBlbmRhcGF0YW5fc2V0YWh1bl9qdXRhIiwgInJpc2tfcmF0aW5nIiwgInJhdGFfcmF0YV9vdmVyZHVlIikNCg0KZGF0YWZlZWQgPC0gZGF0YUNyZWRpdFJhdGluZ1sgLCAhKG5hbWVzKGRhdGFDcmVkaXRSYXRpbmcpICVpbiUgZHJvcF9jb2x1bW5zKV0NCg0KbW9kZWxLdSA8LSBDNS4wKGRhdGFmZWVkLCBhcy5mYWN0b3IoZGF0YUNyZWRpdFJhdGluZyRyaXNrX3JhdGluZykpDQoNCnN1bW1hcnkobW9kZWxLdSkNCmBgYA0KDQpIYXNpbCBkaWF0YXMgYWRhbGFoIGJlbnR1ayByZXByZXNlbnRhc2kgdHJlZS9wb2hvbiBkYWxhbSBiZW50dWsgdGVrcy4gRGltYW5hIHBlbmdlY2VrYW4gYWthbiBkaW11bGFpIGRhcmkgdmFyaWFiZWwganVtbGFoX3RhbmdndW5nYW4uIFNlYmFnYWkgY29udG9oLCBqaWthIGp1bWxhaCB0YW5nZ3VuZ2FuIGxlYmloIGRhcmkgNCBkYW4gZHVyYXNpIHBpbmphbWFuIHNhbXBhaSBkZW5nYW4gbWFrc2ltYWwgMjQgYnVsYW4gbWFrYSByYXRpbmcgZGliZXJpa2FuIG5pbGFpIDQuDQoNCiMgMyBBbGdvcml0bWEgQzUuMA0KDQozLjEgQXBhIEl0dSBBbGdvcml0bWEgQzUuMD8NCg0KQzUuMCBhZGFsYWgga29kZSBwZW5hbWFhbiBzdWF0dSBhbGdvcml0bWEgdW50dWsgZGVjaXNpb24gdHJlZS4gQmFueWFrIGFsZ29yaXRtYSBsYWluIHNlcGVydGkgcmFuZG9tIGZvcmVzdCwgQ0FSVCwgQ0hBSUQsIE1BUlMsIGRhbiBsYWluLWxhaW4uIE5hbXVuIEM1LjAgYWRhbGFoIGFsZ29yaXRtYSB5YW5nIHNhbmdhdCBwb3B1bGVyIGthcmVuYSBtZW1pbGlraSBwZXJmb3JtYSB5YW5nIHNhbmdhdCBiYWlrIGRhcmkgc2lzaSBrZWNlcGF0YW4gbWF1cHVuIGFrdXJhc2kuDQoNCkFsZ29yaXRtYSBpbmkgc2VyaW5nIGRpa2F0ZWdvcmlrYW4gc2ViYWdhaSBjbGFzc2lmaWNhdGlvbiwgZGltYW5hIHR1anVhbm55YSBhZGFsYWggdW50dWsgbWVuZ2thdGVnb3Jpa2FuIGF0YXUgbWVuZ2tsYXNpZmlrYW4gc2VzdWF0dSAtIHBhZGEgY29udG9oIHJpc2sgcmF0aW5nIC0gYmVyZGFzYXJrYW4gaW5wdXQgZGFyaSBkYXRhLWRhdGEgbGFpbi4NCg0KMy4yIERhdGEgUHJlcGFyYXRpb24gdW50dWsgQ2xhc3MgVmFyaWFibGUNCg0KYGBge3J9DQpzdHIoZGF0YUNyZWRpdFJhdGluZykNCmBgYA0KDQpVbnR1ayBjbGFzcyB2YXJpYWJsZSwgeWFpdHUga29sb20gcmlza19yYXRpbmcgdGVybnlhdGEgbWFzaWggZGliYWNhIGRhbGFtIGJlbnR1ayBudW1lcmlrLiBVbnR1ayBtZW5qYWRpIGNsYXNzIHZhcmlhYmxlIHlhbmcgZGlndW5ha2FuIHBhZGEgYWxnb3JpdG1hIEM1LjAsIG1ha2EgcGVybHUgZGlrb252ZXJzaSBtZW5qYWRpIGZhY3Rvci4gSW5pIGJpc2EgZGlsYWt1a2FuIGRlbmdhbiBtZW5nZ3VuYWthbiBmdW5nc2kgYXMuZmFjdG9yKCkgeWFuZyBkaXNpbXBhbiBrZSBkYWxhbSB2YXJpYWJlbCB5YW5nIHN1ZGFoIG1lbmdhcmFoIGtlIGtvbG9tIHlhbmcgaW5naW4gZGlrb252ZXJzaS4NCg0KYGBge3J9DQpkYXRhQ3JlZGl0UmF0aW5nJHJpc2tfcmF0aW5nIDwtIGFzLmZhY3RvcihkYXRhQ3JlZGl0UmF0aW5nJHJpc2tfcmF0aW5nKQ0Kc3RyKGRhdGFDcmVkaXRSYXRpbmcpDQpgYGANCg0KTWFrYSBrb2xvbSByaXNrX3JhdGluZyBzdWRhaCBiZXJ0aXBlIGRhdGEgZmFjdG9yIGRhbiBjbGFzcyB2YXJpYWJlbCBzdWRhaCB0ZXJiZW50dWsuDQoNCg0KMy4zIERhdGEgUHJlcGFyYXRpb24gdW50dWsgSW5wdXQgVmFyaWFibGVzDQoNClRpZGFrIHNlbXVhIGlucHV0IHZhcmlhYmVsIHlhbmcgcGVybHUgYWthbiBndW5ha2FuLCBhcGFsYWdpIHlhbmcgc2FuZ2F0IGJlcmthaXRhbiBzYW5nYXQgZXJhdCBkZW5nYW4gcmlza19yYXRpbmcsIHlhaXR1IHJhdGFfcmF0YV9vdmVyZHVlLiBJbnB1dCB2YXJpYWJlbCBpbmkgYWthbiBkaWJ1YW5nLiBQcm9zZXMgaW5pIGRpc2VidXQgc2ViYWdhaSBmZWF0dXJlIHNlbGVjdGlvbi4NCg0KS2FyZW5hIG1lbmdndW5ha2FuIGRhdGEgZnJhbWUgc2ViYWdhaSB0aXBlIGRhdGEgaW5wdXQgdW50dWsgQzUuMCwgbWFrYSBmaWVsZHMva29sb20ta29sb20geWFuZyBpbmdpbiBkaWd1bmFrYW4gYmlzYSBkaW1hc3Vra2FuIHNlYmFnYWkgdmVjdG9yIHNlYmFnYWkgZmlsdGVyLg0KDQpgYGB7cn0NCiMgTWVuZ2hpbGFuZ2thbiBiZWJlcmFwYSB2YXJpYWJsZSBpbnB1dCBkYXJpIGRhdGFzZXQgDQoNCmlucHV0X2NvbHVtbnMgPC0gYygiZHVyYXNpX3BpbmphbWFuX2J1bGFuIiwgImp1bWxhaF90YW5nZ3VuZ2FuIikNCmRhdGFmZWVkIDwtIGRhdGFDcmVkaXRSYXRpbmdbICwgaW5wdXRfY29sdW1ucyBdDQoNCnN0cihkYXRhZmVlZCkNCmBgYA0KDQpDYXRhdGFuOiBrb2RlX2tvbnRyYWsgaGFydXNueWEgdGlkYWsgZGlwaWxpaCBrYXJlbmEgdW5payB1bnR1ayBrZXNlbHVydWhhbiBkYXRhLCBkYW4gdGlkYWsgbWVuamFkaSBwZW5lbnR1IHVudHVrIG1lbWJlbnR1ayBwb2xhLiBUZXRhcGkgaW5pIGRpbWFzdWtrYW4gZGVuZ2FuIHR1anVhbiB1bnR1ayBtZW51bmp1a2thbiBDNS4wIG1lbWlsaWtpIGtlbWFtcHVhbiB1bnR1ayBtZW1idWFuZyB2YXJpYWJlbCBpbnB1dCB5YW5nIHRpZGFrIHJlbGV2YW4gc2VjYXJhIG90b21hdGlzLg0KDQozLjQgVHJhbmluZyBTZXQgZGFuIFRlc3RpbmcgU2V0DQoNClVudHVrIHByb3NlcyBwZW1iZW50dWthbiBtb2RlbCBtYWNoaW5lIGxlYXJuaW5nIGRhbiBtZWxpaGF0IGFrdXJhc2lueWEsIGJpYXNhbnlhIGRhdGFzZXQgcGVybHUgZGliYWdpIG1lbmphZGkgZHVhLCB5YWl0dToNCg0KVHJhaW5pbmcgc2V0OiBhZGFsYWggcG9yc2kgZGF0YXNldCB5YW5nIGRpZ3VuYWthbiBvbGVoIGFsZ29yaXRtYSB1bnR1ayBkaWFuYWxpc2EgZGFuIG1lbmphZGkgaW5wdXQgdW50dWsgcGVtYmVudHVrYW4gbW9kZWwuDQpUZXN0aW5nIHNldDogYWRhbGFoIHBvcnNpIGRhdGFzZXQgeWFuZyB0aWRhayBkaWd1bmFrYW4gdW50dWsgbWVtYmVudHVrIG1vZGVsLCB0YXBpIHVudHVrIG1lbmd1amkgbW9kZWwgeWFuZyB0ZWxhaCBkaWJlbnR1ay4NClBlbWJlbnR1a2FuIGluaSBiaWFzYW55YSBtZW5nZ3VuYWthbiBtZXRvZGUgcGVtaWxpaGFuIGFjYWsuDQoNClRyYWluaW5nIGRhbiB0ZXN0aW5nIHNldCBha2FuIG1lbmdhbWJpbCBkYXJpIHZhcmlhYmVsIGRhdGEgZnJhbWUgYmVybmFtYSBkYXRhZmVlZCB5YW5nIHRlbGFoIGRpcGVyc2lhcGthbiBzZWJlbHVtbnlhLiBKdW1sYWggYmFyaXMgZGF0YXNldCBhZGFsYWggOTAwLCBkaW1hbmEgYWthbiBtZW5nYW1iaWwgODAwIGJhcmlzIHNlY2FyYSBhY2FrIHNlYmFnYWkgdHJhaW5pbmcgc2V0IGRhbiBzaXNhIDEwMCBzZWJhZ2FpIHRlc3Rpbmcgc2V0Lg0KDQpgYGB7cn0NCg0KIyBNZW1wZXJzaWFwa2FuIHBvcnNpIGluZGV4IGFjYWsgdW50dWsgdHJhaW5pbmcgZGFuIHRlc3Rpbmcgc2V0DQoNCnNldC5zZWVkKDEwMCkNCmluZGVrc190cmFpbmluZ19zZXQgPC0gc2FtcGxlKDkwMCwgODAwKQ0KYGBgDQoNCkRpbWFuYToNCg0Kc2V0LnNlZWQoMTAwKSBhZGFsYWggcGVyaW50YWggdW50dWsgbWVueWVyYWdhbWthbiBwZW5nYW1iaWxhbiBiaWxhbmdhbiBhY2FrIGRpIHNlbHVydWggYXBsaWthc2kgUi4NCnNhbXBsZSg5MDAsIDgwMCkgYWRhbGFoIG1lbWJ1YXQgdXJ1dGFuIGFjYWsgZGVuZ2FuIHJlbnRhbmcgbmlsYWkgMSBzYW1wYWkgZGVuZ2FuIDkwMCwgdGV0YXBpIGRpYW1iaWwgc2ViYW55YWsgODAwIG5pbGFpLg0KDQpgYGB7cn0NCiMgTWVtYnVhdCBkYW4gbWVuYW1waWxrYW4gdHJhaW5pbmcgc2V0IGRhbiB0ZXN0aW5nIHNldA0KDQppbnB1dF90cmFpbmluZ19zZXQgPC0gZGF0YWZlZWRbaW5kZWtzX3RyYWluaW5nX3NldCxdDQpjbGFzc190cmFpbmluZ19zZXQgPC0gZGF0YUNyZWRpdFJhdGluZ1tpbmRla3NfdHJhaW5pbmdfc2V0LF0kcmlza19yYXRpbmcNCmlucHV0X3Rlc3Rpbmdfc2V0IDwtIGRhdGFmZWVkWy1pbmRla3NfdHJhaW5pbmdfc2V0LF0NCmBgYA0KDQpEaW1hbmE6DQoNCmlucHV0X3RyYWluaW5nX3NldCBha2FuIGRpaXNpIGRhcmkgZGF0YSBmcmFtZSBkYXRhZmVlZCBkZW5nYW4ganVtbGFoIGluZGVrc255YSB5YW5nIHRlcmRhcGF0IHBhZGEgdmFyaWFiZWwgaW5kZWtzX3RyYWluaW5nX3NldC4NCg0KY2xhc3NfdHJhaW5pbmdfc2V0IGFrYW4gZGlpc2kgZGFyaSBkYXRhIGZyYW1lIGRhdGFDcmVkaXRSYXRpbmcgZGVuZ2FuIGluZGVrcyB5YW5nIGlzaW55YSB0ZXJkYXBhdCBwYWRhIHZhcmlhYmVsIGluZGVrc190cmFpbmluZ19zZXQuDQoNCmlucHV0X3Rlc3Rpbmdfc2V0IGFrYW4gZGlpc2kgZGF0YWZlZWQgZGVuZ2FuIGluZGVrcyB5YW5nIGlzaW55YSDigJh0aWRhayBhZGHigJkgZGkgaW5kZWtzX3RyYWluaW5nX3NldCAtIHBlcmhhdGlrYW4gdGFuZGEgbWludXMgeWFuZyBhZGEgZGkgZGVwYW4gdmFyaWFiZWwgaW5kZWtzX3RyYWluaW5nX3NldC4NCg0KYGBge3J9DQpzdHIoaW5wdXRfdHJhaW5pbmdfc2V0KQ0KYGBgDQoNCmBgYHtyfQ0Kc3RyKGNsYXNzX3RyYWluaW5nX3NldCkNCmBgYA0KDQpgYGB7cn0NCnN0cihpbnB1dF90ZXN0aW5nX3NldCkNCmBgYA0KDQozLjUgTWVuZ2FoYXNpbGthbiBNb2RlbCBkZW5nYW4gRnVuZ3NpIEM1LjANCg0KRnVuZ3NpIEM1LjAgbWVuZ2FtYmlsIGJlYmVyYXBhIGFyZ3VtZW4gZGlkYWxhbW55YS4gQ29udG9obnlhIEM1LjAoeCwgeSwgdHJpYWxzID0g4oCmLCDigKYpLiBCYWNhIHNlbGVuZ2thcG55YSBkaSBzaW5pLiBOYW11biB0aWRhayBzZW11YSBhcmd1bWVuIGFrYW4gZGlndW5ha2FuLiBBdGF1IGFnYXIgbGViaWggbXVkYWggcnVtdXMgZnVuZ3NpIEM1LjAgZGFsYW0ga2FzdXMgaW5pIHlhaXR1IEM1LjAoaW5wdXRfdmFyaWFibGVzLCBjbGFzc192YXJpYWJsZXMpLg0KDQpEYXJpIHZhcmlhYmVsIHNlYmVsdW1ueWEsIGhhbnlhIGJ1dHVoIGFyZ3VtZW4geCBkYW4geS4NCg0KRGltYW5hIHggZGFuIHkgZGkgZnVuZ3NpIEM1LjA6DQoNCng6IHNlYnVhaCBkYXRhIGZyYW1lIGF0YXUgbWF0aWtzIHVudHVrIHByZWRpa3NpLg0KeTogc2VidWFoIGZha3RvciB5YW5nIG1lbWlsaWtpIDIgbGV2ZWwgYXRhdSBsZWJpaC4gTWFrc3VkIGxldmVsIGRpc2luaSB5YWl0dSBiYW55YWtueWEganVtbGFoIGRhdGEgZGkgZGFsYW0gZGF0YSBmcmFtZSBhdGF1IGtvbG9tIGJlcnRpcGUgZGF0YSBmYWN0b3IuIA0KRGVuZ2FuIG1lbmdndW5ha2FuIGRhdGFzZXQgeWFuZyBzdWRhaCBkaXNpYXBrYW4gbWFrYSBwZXJpbnRhaCB1bnR1ayBtZW1iZW50dWsgbW9kZWwgZGVuZ2FuIGZ1bmdzaSBDNS4wIGRhbiBzZWthbGlndXMgbWVueWltcGFubnlhIGRhbGFtIHNhdHUgdmFyaWFiZWwgYmVybmFtYSByaXNrX3JhdGluZ19tb2RlbCBhZGFsYWggc2ViYWdhaSBiZXJpa3V0Og0KDQpgYGB7cn0NCnJpc2tfcmF0aW5nX21vZGVsIDwtIEM1LjAoaW5wdXRfdHJhaW5pbmdfc2V0LCBjbGFzc190cmFpbmluZ19zZXQpDQoNCiMgTWVsaWhhdCBvdmVydmlldyBkYXJpIG1vZGVsIGRpYXRhcw0KDQpzdW1tYXJ5KHJpc2tfcmF0aW5nX21vZGVsKQ0KYGBgDQoNCk91dHB1dCB0ZXJzZWJ1dCBtZW5jZXJpdGFrYW4gdGluZ2thdCBwZW50aW5nbnlhIHBlbmdndW5hYW4gdGlhcCB2YXJpYWJlbC4gRGlzaW5pIGp1bWxhaF90YW5nZ3VuZ2FuIG1lbmVtcGF0aSB1cnV0YW4gcGVydGFtYSBkZW5nYW4gbmlsYWkgMTAwJSBkYW4gZHVyYXNpX3BpbmphbWFuIGRlbmdhbiA3My4wMCUuDQoNCjMuNiBWaXN1YWxpc2FzaSBkYXJpIE1vZGVsIEM1LjANCg0KU2VsYWluIG1vZGVsIHRla3MgZGFyaSBvdXRwdXQgc2ViZWx1bW55YSwgYmlzYSBqdWdhIG1lbmdoYXNpbGthbiBkZWNpc2lvbiB0cmVlIGRhbGFtIGJlbnR1ayBncmFmaWsuIA0KDQpgYGB7cn0NCnJpc2tfcmF0aW5nX21vZGVsIDwtIEM1LjAoaW5wdXRfdHJhaW5pbmdfc2V0LCBjbGFzc190cmFpbmluZ19zZXQpDQoNCiMgVmlzdWFsaXNhc2kgbW9kZWwNCnBsb3Qocmlza19yYXRpbmdfbW9kZWwpDQpgYGANCg0KIyA0IEV2YWx1YXNpIE1vZGVsDQoNCjQuMSBQZW5kYWh1bHVhbg0KDQpDb25mdXNpb24gTWF0cml4IHlhbmcgdGVyZGFwYXQgcGFkYSBvdXRwdXQgbW9kZWwgc2ViZWx1bW55YSBhZGFsYWggZXZhbHVhc2kgbW9kZWwgbWVuZ2d1bmFrYW4gdHJhaW5pbmcgc2V0LiBOYW11biwgcGVybHUgbWVuZ2V2YWx1YXNpIG1vZGVsIGluaSB0ZXJoYWRhcCB0ZXN0aW5nIHNldCB5YW5nIHRlbGFoIGRpc2lhcGthbi4NCg0KNC4yIE1lbmdndW5ha2FuIEZ1bmdzaSBQcmVkaWN0DQoNClBhY2thZ2UgQzUwIG1lbWlsaWtpIGZ1bmdzaSBiZXJuYW1hIHByZWRpY3QsIHlhbmcgYmlzYSBkaWd1bmFrYW4gdW50dWsgbWVsYWt1a2FuIHByZWRpa3NpIGJlcmRhc2Fya2FuIGlucHV0IG1vZGVsIGRhbiBkYXRhIHRlc3QuIEZ1bmdzaSBsZW5na2FwbnlhIHRlcmxpaGF0IHNlYmFnYWkgYmVyaWt1dC4NCg0KYGBge3J9DQpwcmVkaWN0KHJpc2tfcmF0aW5nX21vZGVsLCBpbnB1dF90ZXN0aW5nX3NldCkNCmBgYA0KDQpUZXJsaWhhdCBoYXNpbCBwcmVkaWtzaSBzZW11YSBzZXN1YWkgZGVuZ2FuIHBvc2lzaSBiYXJpcyBkYXRhIGRhcmkgdGVzdGluZyBzZXQuIERhbiBpbmkganVnYSBzZXN1YWkgZGVuZ2FuIHJlbnRhbmcgbmlsYWkgcmlza19yYXRpbmcsIHlhaXR1IDEgc2FtcGFpIGRlbmdhbiA1Lg0KDQpNb2RlbCBkYW4gZGF0YSBmcmFtZSBkZW5nYW4gaW5wdXQgdmFyaWFibGVzIGFkYWxhaCB5YW5nIGRpcGVybHVrYW4gb2xlaCBmdW5nc2kgcHJlZGljdC4NCg0KNC4zIE1lbmdnYWJ1bmdrYW4gSGFzaWwgUHJlZGlrc2kNCg0KU2VwZXJ0aSBkaWluZm9ybWFzaWthbiBwYWRhIHN1YmJhYiBzZWJlbHVtbnlhLCBiYWdhaW1hbmEgY2FyYSBtZW55aW1wYW4gcmlza19yYXRpbmcgZGFyaSBkYXRhc2V0IGF3YWwgZGFuIGhhc2lsIHByZWRpa3NpIGluaSBrZSBkYWxhbSBkdWEga29sb20gbmFtYSB5YW5nIGxhaW4gZGkgZGF0YSBmcmFtZSBpbnB1dF90ZXN0aW5nX3NldC4gTWFyaSBuYW1ha2FuIGtvbG9tIHRlcnNlYnV0IGRlbmdhbiByaXNrX3JhdGluZyBkYW4gaGFzaWxfcHJlZGlrc2kuDQoNCmBgYHtyfQ0KaW5wdXRfdGVzdGluZ19zZXQkcmlza19yYXRpbmcgPC0gZGF0YUNyZWRpdFJhdGluZ1staW5kZWtzX3RyYWluaW5nX3NldCwgXSRyaXNrX3JhdGluZw0KDQppbnB1dF90ZXN0aW5nX3NldCRoYXNpbF9wcmVkaWtzaSA8LSBwcmVkaWN0KHJpc2tfcmF0aW5nX21vZGVsLCBpbnB1dF90ZXN0aW5nX3NldCkNCg0KaW5wdXRfdGVzdGluZ19zZXQNCmBgYA0KDQpEZW5nYW4ga29sb20gcmlza19yYXRpbmcgZGFuIGhhc2lsX3ByZWRpa3NpIGJlcnNhbXBpbmdhbiwgZGlzaW5pIGJpc2EgbGFuZ3N1bmcgYmFuZGluZ2thbiBkYXRhIGF3YWwgZGVuZ2FuIGhhc2lsIHByZWRpa3NpLiBUZXJsaWhhdCBhZGEgcmF0aW5nIHlhbmcgc2FtYSAocHJlZGlrc2kgYmVuYXIpIGRhbiBiZXJiZWRhIChwcmVkaWtzaSBzYWxhaCkuIExhbHUgc2VsYW5qdW55YSBha2FuIGRpZXZhbHVhc2kgdGluZ2thdCBha3VyYXNpIGRhcmkga2VkdWEga29sb20gaW5pIGRlbmdhbiBtZW5naGFzaWxrYW4gY29uZnVzaW9uIG1hdHJpeCBwYWRhIGJhZ2lhbiBzZWxhbmp1dG55YS4NCg0KDQo0LjQgTWVtYnVhdCBUYWJlbCBDb25mdXNpb24gTWF0cml4DQoNClNldGVsYWggaGFzaWwgcHJlZGlrc2kgdGVyaGFkYXAgdGVzdGluZyBzZXQgc2VsZXNhaSwgbGFuZ2thaCBiZXJpa3V0bnlhIGNvYmEgbGloYXQgZGlzdHJpYnVzaSBtYW5hIHlhbmcgdGVycHJlZGlrc2kgZGVuZ2FuIGJlbmFyIGRhbiBzYWxhaC4gSW5pIGRpbGFrdWthbiBkZW5nYW4gY29uZnVzaW9uIG1hdHJpeC4NCg0KYGBge3J9DQpkY2FzdChoYXNpbF9wcmVkaWtzaSB+IHJpc2tfcmF0aW5nLCBkYXRhPWlucHV0X3Rlc3Rpbmdfc2V0KQ0KYGBgDQoNCkRpYWdvbmFsIHlhbmcgYW5na2EgMjQsIDMsIDM3LCA3LCBkYW4gMTYgbWVudW5qdWtrYW4ganVtbGFoIGRhdGEgeWFuZyB0ZXJwcmVkaWtzaSBkZW5nYW4gYmVuYXIsIGRhbiBzaXNhIGFuZ2thIGxhaW5ueWEgbWVudW5qdWtrYW4gZGF0YSB5YW5nIHNhbGFoIHRlcnByZWRpa3NpLg0KDQpTZWtpbGFzIHRlcmxpaGF0IGp1bWxhaCB5YW5nIHRlcnByZWRpa3NpIGRlbmdhbiBiZW5hciBqYXVoIGxlYmloIGJlc2FyIHBvcnNpbnlhIGRpYmFuZGluZ2thbiB5YW5nIHNhbGFoLiBOYW11biB1bnR1ayB5YW5nIDQgZGFuIDUgYWRhIHNlZGlraXQgY2F0YXRhbiBkaW1hbmEgcHJlZGlrc2kgNSBpdHUga2FkYW5nIGphdHVoIGtlIDQuIE5hbXVuIGluaSBiaXNhIGRpYWJhaWthbiwga2FyZW5hIDQgZGFuIDUgbWVtYW5nIGJlcmVzaWtvIHRpbmdnaS4NCg0KNC41IEp1bWxhaCBEYXRhIGRlbmdhbiBQcmVkaWtzaSBCZW5hcg0KDQpVbnR1ayBtZW5naGl0dW5nIHBlcnNlbnRhc2UgZXJyb3IsIGJpc2EgbWVuZ2hpdHVuZyB0ZXJsZWJpaCBkYWh1bHUganVtbGFoIGRhdGEgZGVuZ2FuIHByZWRpa3NpIHlhbmcgYmVuYXIuIEhhc2lsIGRpa2F0YWthbiBiZW5hciBqaWthIGRhdGEgcmlza19yYXRpbmcgc2FtYSBkZW5nYW4gaGFzaWxfcHJlZGlrc2kuDQoNCmBgYHtyfQ0KaW5wdXRfdGVzdGluZ19zZXQkcmlza19yYXRpbmcgPT0gaW5wdXRfdGVzdGluZ19zZXQkaGFzaWxfcHJlZGlrc2kNCmBgYA0KDQpJbmkgYXJ0aW55YSBrYWxhdSBUUlVFIG1ha2EgZGF0YSBwYWRhIHBvc2lzaSB0ZXJzZWJ1dCBwcmVkaWtzaW55YSBiZW5hciBkYW4gRkFMU0UgdW50dWsgc2ViYWxpa255YS4gTmFtdW4gaW5pIG1hc2loIGJlbHVtIGRhbGFtIGJlbnR1ayB5YW5nIGRpaW5naW5rYW4uIE1hc2loIHBlcmx1IGJlYmVyYXBhIHRhbWJhaGFuIHBlcmludGFoIHRlcnNlYnV0IHVudHVrIG1lbmdlbHVhcmthbiBwZXJzZW50YXNlIHByZWRpa3NpIHlhbmcgdGVwYXQuDQoNCkxhbmdrYWggYmVyaWt1dG55YSwgYWRhbGFoIGZpbHRlcmluZy9tZW55YXJpbmcgZGF0YSBmcmFtZSB0ZXJzZWJ1dCBkZW5nYW4gaGFzaWwgdGFkaSBkZW5nYW4gcGVyaW50YWggYmVyaWt1dC4NCg0KYGBge3J9DQppbnB1dF90ZXN0aW5nX3NldFtpbnB1dF90ZXN0aW5nX3NldCRyaXNrX3JhdGluZyA9PSBpbnB1dF90ZXN0aW5nX3NldCRoYXNpbF9wcmVkaWtzaSwgXQ0KYGBgDQoNClRlcmxpaGF0IHNlbXVhIGhhc2lsIGZpbHRlcmluZyBtZW1pbGlraSBuaWxhaSB5YW5nIHNhbWEgdW50dWsga29sb20gcmlza19yYXRpbmcgZGFuIGhhc2lsX3ByZWRpa3NpLiBLZW11ZGlhbiBha2FuIGRpaGl0dW5nIGp1bWxhaCBiYXJpcyBmaWx0ZXJpbmcgaW5pIGRlbmdhbiBtZW5hbWJhaGthbiBmdW5nc2kgbnJvdyB0ZXJoYWRhcCBwZXJpbnRhaCBkaSBhdGFzLg0KDQpgYGB7cn0NCm5yb3coaW5wdXRfdGVzdGluZ19zZXRbaW5wdXRfdGVzdGluZ19zZXQkcmlza19yYXRpbmcgPT0gaW5wdXRfdGVzdGluZ19zZXQkaGFzaWxfcHJlZGlrc2ksIF0pDQpgYGANCg0KQW5na2EgODcgaW5pIG1lbnVuanVra2FuIGp1bWxhaCBkYXRhIGRlbmdhbiBwcmVkaWtzaSB5YW5nIGJlbmFyIHRlcmhhZGFwIHRlc3Rpbmcgc2V0LiBLYXJlbmEgdGVzdGluZyBzZXQgYmVyanVtbGFoIDEwMCwgbWFrYSBwZXJzZW50YXNlIHByZWRpa3NpIHlhbmcgYmVuYXIgYWRhbGFoIDg3JSwgZGFuIGVycm9yIHJhdGUgYXRhdSBwZXJzZW50YXNlIHlhbmcgc2FsYWggYWRhbGFoIDEzJS4NCg0KTWFyaSBjZWsga2VtYmFsaSBoYXNpbCB0ZXJzZWJ1dCBkZW5nYW4gbWVuanVtbGFoa2FuIHBvc2lzaSBkaWFnb25hbCBwYWRhIGNvbmZ1c2lvbiBtYXRyaXggeWFuZyB0ZWxhaCBkaWJ1YXQgc2ViZWx1bW55YS4NCg0KYGBge3J9DQpkY2FzdChoYXNpbF9wcmVkaWtzaSB+IHJpc2tfcmF0aW5nLCBkYXRhPWlucHV0X3Rlc3Rpbmdfc2V0KQ0KYGBgDQoNCkhhc2lsbnlhIGp1Z2EgODcgeWFuZyBtZXJ1cGFrYW4gaGFzaWwgZGFyaSBvcGVyYXNpIHBlbmp1bWxhaCAyNCArIDMgKyAzNyArIDcgKyAxNi4NCg0KNC42IEp1bWxhaCBEYXRhIGRlbmdhbiBQcmVkaWtzaSBTYWxhaA0KDQpCYWdhaW1hbmEga2FsYXUgaGFueWEgaW5naW4gbWVuY2FyaSB5YW5nIHRpZGFrIHNhbWEsIGRlbmdhbiBrYXRhIGxhaW4gbWVuY2FyaSB5YW5nIHNhbGFoIHByZWRpa3NpbnlhPw0KDQpgYGB7cn0NCm5yb3coaW5wdXRfdGVzdGluZ19zZXRbaW5wdXRfdGVzdGluZ19zZXQkcmlza19yYXRpbmcgIT0gaW5wdXRfdGVzdGluZ19zZXQkaGFzaWxfcHJlZGlrc2ksXSkNCmBgYA0KDQpUZXJsaWhhdCBiYWh3YSBqdW1sYWggcHJlZGlrc2kgZXJyb3IgYWRhIDEzLiBIYXNpbCBpbmkga29uc2lzdGVuIGppa2EgZGliYW5kaW5na2FuIGRlbmdhbiBqdW1sYWggODcgZGFyaSBwcmVkaWtzaSB5YW5nIGJlbmFyLCBkaW1hbmEgdG90YWwga2VkdWFueWEgYWRhbGFoIDEwMCAtIHlhbmcgbWVydXBha2FuIGp1bWxhaCBkYXRhIHVudHVrIHRlc3Rpbmcgc2V0Lg0KDQo0LjcgS2VzaW1wdWxhbg0KDQptZW5qYWRpIGJhZ2lhbiB5YW5nIHNhbmdhdCBwZW50aW5nIHlhaXR1IHVudHVrIHBlcnRhbWEga2FsaW55YSBtZWxha3VrYW4gcHJlZGlrc2kgZGVuZ2FuIGZ1bmN0aW9uIHByZWRpY3QgdGVyaGFkYXAgcG9yc2kgZGF0YSB0ZXN0aW5nIHNldC4NCg0KVHVqdWFubnlhIGFkYWxhaCBtZW5ndWt1ciBha3VyYXNpIGRhcmkgaGFzaWwgcHJlZGlrc2kgZGVuZ2FuIGRhdGEgYXdhbC4gSW5pIGRpZ3VuYWthbiBkZW5nYW4gbWVuZ2d1bmFrYW4gY29uZnVzaW9uIG1hdHJpeCBqdWdhIGRhbiBtZW5naGl0dW5nIGp1bWxhaCBwcmVkaWtzaSBkYXRhIHlhbmcgYmVuYXIgZGFuIHNhbGFoIHNlY2FyYSBhZ2FrIOKAnG1hbnVhbOKAnS4gUGVuZGVrYXRhbiB0ZXJha2hpciBhZGFsYWggbWVtYmFuZGluZ2thbiBkYXRhIGF3YWwgZGFuIGhhc2lsIHByZWRpa3NpIGRpIGRhdGEgZnJhbWUgZGVuZ2FuIG9wZXJhdG9yID09LCAhPSwgZGFuIG5yb3cuDQoNCkRlbmdhbiB0aW5na2F0IGVycm9yIDEzJSwgZGFuIHdhbGF1cHVuIGFkYSBjYXRhdGFuIHVudHVrIGtsYXNpZmlrYXNpIDQgZGFuIDUsIG5hbXVuIHNlY2FyYSBnYXJpcyBiZXNhciBiaXNhIGFuZ2dhcCBtb2RlbCBpbmkgY3VrdXAgYmFpay4gRGFuIGRlbmdhbiBrZXB1dHVzYW4gaW5pLCBzYWF0bnlhIG1lbmdhZG9wc2kgbW9kZWwgaW5pIHVudHVrIG1lbGFrdWthbiBwcmVkaWtzaSBwZXIgZGF0YSBhcGxpa2FzaSBrcmVkaXQgYmFydSB5YW5nIG1hc3VrLg0KDQoNCiMgNSBNZW5nZ3VuYWthbiBNb2RlbCB1bnR1ayBQcmVkaWtzaQ0KDQpUYWhhcCB0ZXJha2hpciBzZXRlbGFoIG1lbGFrdWthbiBldmFsdWFzaSBkYW4geWFraW4gYWthbiBha3VyYXNpbnlhLCBtb2RlbCBha2FuIGRpZ3VuYWthbiBkYWxhbSBrZXNlaGFyaWFuIHVudHVrIG1lbGFrdWthbiBwcmVkaWtzaSByaXNrIHJhdGluZyBkYXJpIGRhdGEgYmFydS4NCg0KNS4xIE1lbXBlcnNpYXBrYW4gRGF0YSBQZW5nYWp1YW4gQmFydQ0KDQpEYXRhIHBlbmdhanVhbiBiYXJ1IHBlcmx1IGRpYmVudHVrIHNlYmFnYWkgc2F0dSBkYXRhIGZyYW1lIGRlbmdhbiBpbnB1dCBkaW1hbmEgbmFtYS1uYW1hIHZhcmlhYmVsIHlhbmcgZGlndW5ha2FuIGhhcnVzIHNhbWEgcGVyc2lzLiBEYXJpIGF3YWwgcGVtb2RlbGFuLCBzdWRhaCBtZW5nZ3VuYWthbiBkdWEgdmFyaWFiZWwgeWFrbmk6DQoNCmp1bWxhaF90YW5nZ3VuZ2FuDQoNCmR1cmFzaV9waW5qYW1hbl9idWxhbg0KDQpLZWR1YW55YSBkYWxhbSBiZW50dWsgbnVtZXJpayAoYW5na2EpDQoNCmBgYHtyfQ0KYXBsaWthc2lfYmFydSA8LSBkYXRhLmZyYW1lKGp1bWxhaF90YW5nZ3VuZ2FuID0gNiwgZHVyYXNpX3BpbmphbWFuX2J1bGFuID0gMTIpDQoNCnByaW50KGFwbGlrYXNpX2JhcnUpDQpgYGANCg0KDQpEYXRhIGZyYW1lIGluaSBha2FuIGRpZ3VuYWthbiBzZWJhZ2FpIGlucHV0IHVudHVrIHByZWRpa3NpDQoNCjUuMiBNZWxha3VrYW4gUHJlZGlrc2kgdGVyaGFkYXAgRGF0YSBQZW5nYWp1YW4gQmFydQ0KDQpEYXRhIGFwbGlrYXNpIGJhcnUgeWFuZyB0ZWxhaCBkaWJ1YXQgc2ViZWx1bW55YSBha2FuIGRpcHJlZGlrc2kgbmlsYWkgcmlza19yYXRpbmcgbnlhIGRlbmdhbiBmdW5nc2kgcHJlZGljdCwgZGltYW5hIGNhcmEgcGVuZ2d1bmFhbm55YSBtYXNpaCBzYW1hLg0KDQpNYWthIHBlbnllc3VhaWFuIHBlcmludGFoIGRpIGF0YXMgZGVuZ2FuIG5hbWEgbW9kZWwgZGFuIHZhcmlhYmVsIHlhbmcgZGlndW5ha2FuLCBhZGFsYWggdmFyaWFiZWwgcmlza19yYXRpbmdfbW9kZWwgc2ViYWdhaSBtb2RlbCBkYW4gYXBsaWthc2lfYmFydSBzZWJhZ2FpIGRhdGEgZnJhbWUgeWFuZyBha2FuIGRpIHByZWRpa3NpLg0KDQpgYGB7cn0NCiMgTWVtYnVhdCBkYXRhIGZyYW1lIGFwbGlrYXNpIGJhcnUNCg0KYXBsaWthc2lfYmFydSA8LSBkYXRhLmZyYW1lKGp1bWxhaF90YW5nZ3VuZ2FuID0gNiwgZHVyYXNpX3BpbmphbWFuX2J1bGFuID0gMTIpDQoNCiMgTWVsYWt1a2FuIHByZWRpa3NpDQoNCnByZWRpY3Qocmlza19yYXRpbmdfbW9kZWwsIGFwbGlrYXNpX2JhcnUpDQpgYGANCg0KSW5pIGFydGlueWEgaGFzaWwgcHJlZGlrc2kgcmlza19yYXRpbmcgdW50dWsgYXBsaWthc2kgYmFydSBpbmkgYWRhbGFoIDQsIGRhcmkga2VtdW5na2luYW4gMSwgMiwgMywgNCBkYW4gNS4gTmlsYWkgNCBpbmkgYWRhbGFoIG5pbGFpIHJlc2lrbyB5YW5nIGN1a3VwIHRpbmdnaSwgamFkaSBiaXNhIHNhamEgYXBsaWthc2kgYmFydSBpbmkgZGl0b2xhayBzZXN1YWkgZGVuZ2FuIGtlYmlqYWthbiBsZW1iYWdhIHBlbWluamFtLg0KDQo1LjMgTWVydWJhaCBEdXJhc2kgUGluamFtYW4NCg0KU2VrYXJhbmcgZGljb2JhIG1lbXByZWRpa3NpIGRhcmkgZGF0YSB5YW5nIHRpZGFrIGFkYSBkYXJpIGRhdGEgc2V0IHlhbmcgZGlqYWRpa2FuIG1vZGVsLg0KDQpNYXJpIGNvYmEgZ2FudGkgZHVyYXNpIHBpbmphbWFuIHNlbGFtYSA2NCBidWxhbi4NCg0KYGBge3J9DQojIE1lbWJ1YXQgZGF0YSBmcmFtZSBhcGxpa2FzaSBiYXJ1DQoNCmFwbGlrYXNpX2JhcnUgPC0gZGF0YS5mcmFtZShqdW1sYWhfdGFuZ2d1bmdhbiA9IDYsIGR1cmFzaV9waW5qYW1hbl9idWxhbiA9IDY0KQ0KDQojIE1lbGFrdWthbiBwcmVkaWtzaQ0KDQpwcmVkaWN0KHJpc2tfcmF0aW5nX21vZGVsLCBhcGxpa2FzaV9iYXJ1KQ0KYGBgDQoNCkluaSBhcnRpbnlhIGhhc2lsIHByZWRpa3NpIHJpc2tfcmF0aW5nIHVudHVrIGFwbGlrYXNpIGJhcnUgaW5pIGFkYWxhaCA1LCBkYXJpIGtlbXVuZ2tpbmFuIDEsIDIsIDMsIDQgZGFuIDUuIE5pbGFpIDUgaW5pIGFkYWxhaCBuaWxhaSByZXNpa28geWFuZyBzYW5nYXQgdGluZ2dpIGRpa2FyZW5ha2FuIGR1cmFzaSBwZW1pbmphbWFuIHRpZGFrIHRlcm1hc3VrIGRhbGFtIGRhdGEgeWFuZyBkaSBsYWt1a2FuIG1vZGVsLg==