Menurut Organisasi Kesehatan Dunia (WHO) stroke adalah penyebab kematian nomor dua secara global, bertanggung jawab atas sekitar 11% dari total kematian.

Kumpulan data ini digunakan untuk memprediksi kemungkinan seorang pasien terkena stroke berdasarkan parameter input seperti jenis kelamin, usia, berbagai penyakit, dan status merokok. Setiap baris dalam data memberikan informasi yang relevan tentang pasien.

TABLE OF CONTENT

Berikut adalah daftar isi yang akan coba saya jalankan sesuai rules dari tutor antaralian sebagai berikut :

  1. IMPORTING LIBRARIES

  2. LOADING DATA

  3. DATA CLEANINGS

  4. DATA PREPROCESSING

  5. MODEL BUILDING

  6. CONCLUSION

  7. END

IMPORTING LIBRARIES

Package yang digunakan adalah sebagai berikut :

library(rmarkdown)
library(knitr)
library(dplyr)
library(ggplot2)
library(neuralnet)
library(iml)
library(rpart)
library(readr)
library(magrittr)
library(tibble)
library(skimr)
library(rpart.plot)
library(randomForest)
library(caret)
library(e1071)
library(DALEX)

LOADING DATA

str(Stroke_data)
'data.frame':   5110 obs. of  12 variables:
 $ id               : int  9046 51676 31112 60182 1665 56669 53882 10434 27419 60491 ...
 $ gender           : chr  "Male" "Female" "Male" "Female" ...
 $ age              : num  67 61 80 49 79 81 74 69 59 78 ...
 $ hypertension     : int  0 0 0 0 1 0 1 0 0 0 ...
 $ heart_disease    : int  1 0 1 0 0 0 1 0 0 0 ...
 $ ever_married     : chr  "Yes" "Yes" "Yes" "Yes" ...
 $ work_type        : chr  "Private" "Self-employed" "Private" "Private" ...
 $ Residence_type   : chr  "Urban" "Rural" "Rural" "Urban" ...
 $ avg_glucose_level: num  229 202 106 171 174 ...
 $ bmi              : chr  "36.6" "N/A" "32.5" "34.4" ...
 $ smoking_status   : chr  "formerly smoked" "never smoked" "never smoked" "smokes" ...
 $ stroke           : int  1 1 1 1 1 1 1 1 1 1 ...

VARIABLE USED

  1. id: unique identifier
  2. gender: “Male”, “Female” or “Other”
  3. age: age of the patient
  4. hypertension: 0 if the patient doesn’t have hypertension, 1 if the patient has hypertension
  5. heart_disease: 0 if the patient doesn’t have any heart diseases, 1 if the patient has a heart disease
  6. ever_married: “No” or “Yes”
  7. work_type: “children”, “Govt_jov”, “Never_worked”, “Private” or “Self-employed”
  8. Residence_type: “Rural” or “Urban”
  9. avg_glucose_level: average glucose level in blood
  10. bmi: body mass index
  11. smoking_status: “formerly smoked”, “never smoked”, “smokes” or “Unknown”*
  12. stroke: 1 if the patient had a stroke or 0 if not

DATA FIKS

Dalam fase ini id tidak di masukan karena tidak akan ada pengaruhnya dalam model

DATA CLEANINGS

Tahap ini adalah tahap pengecekan data, apakah sudah bebas dari missing value, data duplicate dan pengecekan apakah data sudah siap di eksekusi.

DATA TYPE CHECKING

str(Stroke_data)
'data.frame':   5110 obs. of  12 variables:
 $ id               : int  9046 51676 31112 60182 1665 56669 53882 10434 27419 60491 ...
 $ gender           : chr  "Male" "Female" "Male" "Female" ...
 $ age              : num  67 61 80 49 79 81 74 69 59 78 ...
 $ hypertension     : int  0 0 0 0 1 0 1 0 0 0 ...
 $ heart_disease    : int  1 0 1 0 0 0 1 0 0 0 ...
 $ ever_married     : chr  "Yes" "Yes" "Yes" "Yes" ...
 $ work_type        : chr  "Private" "Self-employed" "Private" "Private" ...
 $ Residence_type   : chr  "Urban" "Rural" "Rural" "Urban" ...
 $ avg_glucose_level: num  229 202 106 171 174 ...
 $ bmi              : chr  "36.6" "N/A" "32.5" "34.4" ...
 $ smoking_status   : chr  "formerly smoked" "never smoked" "never smoked" "smokes" ...
 $ stroke           : int  1 1 1 1 1 1 1 1 1 1 ...

INTERPRETASI DATA

Dari data di atas terlihat bahwa pada data $BMI terdapat missing value, sehingga akan di lakukan cleaning terlebih dahulu dengan mengisi nilai N/A menjadi nilai dari rata-rata data tersebut.

EDIT BMI DATA

sebenarnya ada berapa nilai N/A pada BMI maari kita cek :

qty_na
[1] 201

untuk mengedit semua data yang bernilai NA dengan cara sebagai berikut :

print(Mean_value)
[1] 28.89324
head(stroke_clean_data$bmi, 20)
 [1] 36.60000 28.89324 32.50000 34.40000 24.00000 29.00000 27.40000 22.80000 28.89324 24.20000
[11] 29.70000 36.80000 27.30000 28.89324 28.20000 30.90000 37.50000 25.80000 37.80000 28.89324

MISSING VALUE

anyNA(stroke_clean_data)
[1] FALSE

DATA DUPLICATE

CHANGE THE DATA STRUCTUR

Untuk ini tidak perlu di lakukan, karena data akan di jadikan dummy untuk di lakukan SCALE, dapat dilihat pada Sub-bab DATA PROCESING.

DATA PROCESING

Tahap ini adalah memulai proses persiapan data

SAMPLING DATA

set.seed(80)
index <- sample( seq_len(nrow(stroke_clean_data)), size = samplesize )

SCALE DATA

scaled <- as.data.frame(scale(stroke_clean_data, center = TRUE, scale = TRUE))
Error in colMeans(x, na.rm = TRUE) : 'x' must be numeric

Eror di atas karenakan data clean_stroke_data harus di ubah menjdi data numerik, sehingga dapat di lakukan dengan sintax berikut

DUMMY DATA

head(Dummy_data, 5)
  genderFemale genderMale genderOther age hypertension heart_disease ever_marriedYes
1            0          1           0  67            0             1               1
2            1          0           0  61            0             0               1
3            0          1           0  80            0             1               1
4            1          0           0  49            0             0               1
5            1          0           0  79            1             0               1
  work_typeGovt_job work_typeNever_worked work_typePrivate work_typeSelf-employed
1                 0                     0                1                      0
2                 0                     0                0                      1
3                 0                     0                1                      0
4                 0                     0                1                      0
5                 0                     0                0                      1
  Residence_typeUrban avg_glucose_level      bmi smoking_statusnever smoked
1                   1            228.69 36.60000                          0
2                   0            202.21 28.89324                          1
3                   0            105.92 32.50000                          1
4                   1            171.23 34.40000                          0
5                   0            174.12 24.00000                          1
  smoking_statussmokes smoking_statusUnknown stroke
1                    0                     0      1
2                    0                     0      1
3                    0                     0      1
4                    1                     0      1
5                    0                     0      1

PROCESSING SCALE

Scaling data sudah selesai. Hal ini penting karena jika tidak, suatu peubah mungkin mempunyai dampak besar pada peubah hasil prediksi hanya karena skalanya. Terkadang peubah yang belum dilakukan proses scaling cenderung menghasilkan hasil yang tidak memiliki makna.

MODELING

ANN dengan neuralnet

Selain terdapat di dalam paket keras dan tensorflow, komunitas R juga mengembangkan model jaringan syaraf tiruan (ANN) pada paket neuralnet. Jika ingin membaca lebih dalam terkait neuralnet bisa di lihat di sini atau dengan Running sintax berikut ?neuralnet pada console R.

PARTISI DATA

prosess membuat training and test set

glimpse(trainNN)
Rows: 3,066
Columns: 18
$ genderFemale                 <dbl> 0.8406001, -1.1893935, -1.1893935, -1.1893935, 0.8406001,…
$ genderMale                   <dbl> -0.8402611, 1.1898733, 1.1898733, 1.1898733, -0.8402611, …
$ genderOther                  <dbl> -0.01398909, -0.01398909, -0.01398909, -0.01398909, -0.01…
$ age                          <dbl> -1.02715152, 1.67045397, 1.31666964, -1.83201086, -0.5406…
$ hypertension                 <dbl> -0.3285697, 3.0428986, -0.3285697, -0.3285697, -0.3285697…
$ heart_disease                <dbl> -0.2389234, 4.1846225, -0.2389234, -0.2389234, -0.2389234…
$ ever_marriedYes              <dbl> -1.3813012, 0.7238134, -1.3813012, -1.3813012, 0.7238134,…
$ work_typeGovt_job            <dbl> -0.3840731, 2.6031618, 2.6031618, -0.3840731, -0.3840731,…
$ work_typeNever_worked        <dbl> -0.06574993, -0.06574993, -0.06574993, -0.06574993, -0.06…
$ work_typePrivate             <dbl> 0.864212, -1.156897, -1.156897, -1.156897, 0.864212, -1.1…
$ `work_typeSelf-employed`     <dbl> -0.4368378, -0.4368378, -0.4368378, -0.4368378, -0.436837…
$ Residence_typeUrban          <dbl> -1.0160784, -1.0160784, -1.0160784, 0.9839834, 0.9839834,…
$ avg_glucose_level            <dbl> -0.05515638, 2.46871762, -0.58647503, 1.04148885, -0.1580…
$ bmi                          <dbl> -1.54497394, -0.62265859, 0.32563748, -1.53198358, 0.2996…
$ `smoking_statusnever smoked` <dbl> -0.7666993, -0.7666993, -0.7666993, -0.7666993, 1.3040371…
$ smoking_statussmokes         <dbl> -0.4272714, -0.4272714, 2.3399745, -0.4272714, -0.4272714…
$ smoking_statusUnknown        <dbl> -0.6579463, -0.6579463, -0.6579463, 1.5195833, -0.6579463…
$ stroke                       <dbl> -0.2263051, -0.2263051, -0.2263051, -0.2263051, -0.226305…

MEMBANGUN MODEL

# fit neural network
set.seed(2)
tessssss <- neuralnet(stroke ~ ever_marriedYes + age + hypertension + avg_glucose_level + bmi + smoking_statussmokes, trainNN,
                      hidden = 3,
                      linear.output = T)

VISUALISASI ANN

PREDIKIS

predict_testNN <- (predict_testNN$net.result * (max(stroke_clean_data$stroke) - min(stroke_clean_data$stroke))) + min(stroke_clean_data$stroke)
Error: object 'predict_testNN' not found
#| fig-height: 4
plot(datatest$stroke, predict_testNN, col='darkred', pch=16, ylab = "predicted rating", xlab = "STROKE")

abline(0,1)

ROOT MEAN SQUARE

cat("Metrik RMSE: ", RMSE.NN)
Metrik RMSE:  0.384983

RMSE memiliki nilai sekitar 0.384983. Ini berarti bahwa, secara rata-rata, perbedaan antara nilai prediksi model Anda dengan nilai aktual (dalam unit yang sama dengan data) adalah sekitar 0.384983. Semakin mendekati nol, semakin baik kinerja model yang di buat.

CONCLUSION

FEATURE INPORTANCE

RESULT IMP

SHAP

END

Terakhir.

Result IMP

feature (fitur): Kolom ini berisi nama fitur-fitur yang dievaluasi dalam model. Ini adalah daftar fitur-fitur yang digunakan dalam model yang di buat.

importance.05: Ini adalah nilai penting fitur (feature importance) pada tingkat 5% (terendah) dalam distribusi. Nilai ini menunjukkan sejauh mana fitur tersebut penting dalam model saat melihat persentil terendah dari distribusi.

importance: Ini adalah nilai penting fitur pada tingkat tengah (median) dalam distribusi. Ini adalah ukuran pentingnya fitur dalam model saat melihat persentil tengah dari distribusi.

importance.95: Ini adalah nilai penting fitur pada tingkat 95% (tertinggi) dalam distribusi. Ini menunjukkan sejauh mana fitur tersebut penting dalam model saat melihat persentil tertinggi dari distribusi.

Dari table IMP di atas terlihat bahwa varible ever_marriedYes sangat penting dalam mempengaruhi model yang di buat, selanjutnya di ikuti age / Usia dan BMI.

Result SHEPLY

Dari nilai IMP di jelaskan bahwa ever_marriedYes dan BMI termasuk dalam bagian yang penting dalam model, namun di lihat dari Interpretasi plot SHEPLY kedua variable ini memiliki nilai yang negatif. artinya prediksi akan cenderung rendah.

Selanjutnya yang memiliki nilai shaply positif adalah variable :

  • avg_glucose_level: average glucose level in blood

  • age: age of the patient

maka dapat di simpulkan jika Client memiliki nilai Glucose (gula / Diabetes) dan usia yang cukup tinggi ini menjadi tanda untuk berdoa lebih banyak, karena peluang terserang stroke sangat tinggi.

KOREKSI

Dear Tutor,

jika ada kesalahan mohon kiranya di koreksi jika sempat.

Demikian di sampaikan, atas bantuan dan ilmunya di ucapkan terimakasih.

REGARDS,

ALBANI

LS0tDQp0aXRsZTogIlNUT0tFIFBSRURJQ1RJT04iDQpvdXRwdXQ6IA0KICBodG1sX25vdGVib29rIDoNCiAgICB0b2MgOiB5ZXMNCiAgICB0b2NfZmxvYXQgOiBUcnVlDQogICAgdGhlbWU6IGZsYXRseQ0KICAgIGhpZ2hsaWdodDogemVuYnVybg0KICAgIHRvY19kZXB0aDogNA0KLS0tDQoNCk1lbnVydXQgT3JnYW5pc2FzaSBLZXNlaGF0YW4gRHVuaWEgKFdITykgc3Ryb2tlIGFkYWxhaCBwZW55ZWJhYiBrZW1hdGlhbiBub21vciBkdWEgc2VjYXJhIGdsb2JhbCwgYmVydGFuZ2d1bmcgamF3YWIgYXRhcyBzZWtpdGFyIDExJSBkYXJpIHRvdGFsIGtlbWF0aWFuLg0KDQpfX0t1bXB1bGFuIGRhdGEgaW5pIGRpZ3VuYWthbiB1bnR1ayBtZW1wcmVkaWtzaSBrZW11bmdraW5hbiBzZW9yYW5nIHBhc2llbiB0ZXJrZW5hIHN0cm9rZSBiZXJkYXNhcmthbiBwYXJhbWV0ZXIgaW5wdXQgc2VwZXJ0aSBqZW5pcyBrZWxhbWluLCB1c2lhLCBiZXJiYWdhaSBwZW55YWtpdCwgZGFuIHN0YXR1cyBtZXJva29rLiBTZXRpYXAgYmFyaXMgZGFsYW0gZGF0YSBtZW1iZXJpa2FuIGluZm9ybWFzaSB5YW5nIHJlbGV2YW4gdGVudGFuZyBwYXNpZW4uX18NCg0KIyMgVEFCTEUgT0YgQ09OVEVOVA0KDQpCZXJpa3V0IGFkYWxhaCBkYWZ0YXIgaXNpIHlhbmcgYWthbiBjb2JhIHNheWEgamFsYW5rYW4gc2VzdWFpIHJ1bGVzIGRhcmkgdHV0b3IgYW50YXJhbGlhbiBzZWJhZ2FpIGJlcmlrdXQgOg0KDQoxLiBJTVBPUlRJTkcgTElCUkFSSUVTDQoNCjIuIExPQURJTkcgREFUQQ0KDQozLiBEQVRBIENMRUFOSU5HUw0KDQo0LiBEQVRBIFBSRVBST0NFU1NJTkcNCg0KNS4gTU9ERUwgQlVJTERJTkcNCg0KNi4gQ09OQ0xVU0lPTg0KDQo3LiBFTkQNCg0KIyMgSU1QT1JUSU5HIExJQlJBUklFUw0KDQpgUGFja2FnZWAgeWFuZyBkaWd1bmFrYW4gYWRhbGFoIHNlYmFnYWkgYmVyaWt1dCA6DQoNCmBgYHtyLCBldmFsPUZBTFNFfQ0KbGlicmFyeShybWFya2Rvd24pDQpsaWJyYXJ5KGtuaXRyKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkobmV1cmFsbmV0KQ0KbGlicmFyeShpbWwpDQpsaWJyYXJ5KHJwYXJ0KQ0KbGlicmFyeShyZWFkcikNCmxpYnJhcnkobWFncml0dHIpDQpsaWJyYXJ5KHRpYmJsZSkNCmxpYnJhcnkoc2tpbXIpDQpsaWJyYXJ5KHJwYXJ0LnBsb3QpDQpsaWJyYXJ5KHJhbmRvbUZvcmVzdCkNCmxpYnJhcnkoY2FyZXQpDQpsaWJyYXJ5KGUxMDcxKQ0KbGlicmFyeShEQUxFWCkNCmBgYA0KDQojIyBMT0FESU5HIERBVEENCg0KYGBge3J9DQpTdHJva2VfZGF0YSA8LSByZWFkLmNzdihmaWxlID0gIkQ6L1BST0pFSy93Ni9oZWFsdGhjYXJlLWRhdGFzZXQtc3Ryb2tlLWRhdGEuY3N2IiwNCiAgICAgICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFRSVUUsDQogICAgICAgICAgICAgICAgICAgICAgICBzZXAgPSAiLCIpDQpzdHIoU3Ryb2tlX2RhdGEpDQpgYGANCg0KIyMjIFZBUklBQkxFIFVTRUQNCg0KMS4gYGlkYDogdW5pcXVlIGlkZW50aWZpZXINCjIuIGBnZW5kZXJgOiAiTWFsZSIsICJGZW1hbGUiIG9yICJPdGhlciINCjMuIGBhZ2VgOiBhZ2Ugb2YgdGhlIHBhdGllbnQNCjQuIGBoeXBlcnRlbnNpb25gOiAwIGlmIHRoZSBwYXRpZW50IGRvZXNuJ3QgaGF2ZSBoeXBlcnRlbnNpb24sIDEgaWYgdGhlIHBhdGllbnQgaGFzIGh5cGVydGVuc2lvbg0KNS4gYGhlYXJ0X2Rpc2Vhc2VgOiAwIGlmIHRoZSBwYXRpZW50IGRvZXNuJ3QgaGF2ZSBhbnkgaGVhcnQgZGlzZWFzZXMsIDEgaWYgdGhlIHBhdGllbnQgaGFzIGEgaGVhcnQgZGlzZWFzZQ0KNi4gYGV2ZXJfbWFycmllZGA6ICJObyIgb3IgIlllcyINCjcuIGB3b3JrX3R5cGVgOiAiY2hpbGRyZW4iLCAiR292dF9qb3YiLCAiTmV2ZXJfd29ya2VkIiwgIlByaXZhdGUiIG9yICJTZWxmLWVtcGxveWVkIg0KOC4gYFJlc2lkZW5jZV90eXBlYDogIlJ1cmFsIiBvciAiVXJiYW4iDQo5LiBgYXZnX2dsdWNvc2VfbGV2ZWxgOiBhdmVyYWdlIGdsdWNvc2UgbGV2ZWwgaW4gYmxvb2QNCjEwLiBgYm1pYDogYm9keSBtYXNzIGluZGV4DQoxMS4gYHNtb2tpbmdfc3RhdHVzYDogImZvcm1lcmx5IHNtb2tlZCIsICJuZXZlciBzbW9rZWQiLCAic21va2VzIiBvciAiVW5rbm93biIqDQoxMi4gYHN0cm9rZWA6IDEgaWYgdGhlIHBhdGllbnQgaGFkIGEgc3Ryb2tlIG9yIDAgaWYgbm90DQoNCiMjIyBEQVRBIEZJS1MNCg0KRGFsYW0gZmFzZSBpbmkgYGlkYCB0aWRhayBkaSBtYXN1a2FuIGthcmVuYSB0aWRhayBha2FuIGFkYSBwZW5nYXJ1aG55YSBkYWxhbSBtb2RlbA0KDQpgYGB7cn0NCnN0cm9rZV9jbGVhbl9kYXRhIDwtIFN0cm9rZV9kYXRhWyAsIDI6MTJdDQpoZWFkKHN0cm9rZV9jbGVhbl9kYXRhLCA1KQ0KYGBgDQoNCiMjIERBVEEgQ0xFQU5JTkdTDQoNClRhaGFwIGluaSBhZGFsYWggdGFoYXAgcGVuZ2VjZWthbiBkYXRhLCBhcGFrYWggc3VkYWggYmViYXMgZGFyaSBtaXNzaW5nIHZhbHVlLCBkYXRhIGR1cGxpY2F0ZSBkYW4gcGVuZ2VjZWthbiBhcGFrYWggZGF0YSBzdWRhaCBzaWFwIGRpIGVrc2VrdXNpLg0KDQojIyMgREFUQSBUWVBFIENIRUNLSU5HDQoNCmBgYHtyfQ0Kc3RyKFN0cm9rZV9kYXRhKQ0KYGBgDQoNCiMjIyBJTlRFUlBSRVRBU0kgREFUQQ0KDQpEYXJpIGRhdGEgZGkgYXRhcyB0ZXJsaWhhdCBiYWh3YSBwYWRhIGRhdGEgYCRCTUlgIHRlcmRhcGF0IG1pc3NpbmcgdmFsdWUsIHNlaGluZ2dhIGFrYW4gZGkgbGFrdWthbiBjbGVhbmluZyB0ZXJsZWJpaCBkYWh1bHUgZGVuZ2FuIG1lbmdpc2kgbmlsYWkgYE4vQWAgbWVuamFkaSBuaWxhaSBkYXJpIHJhdGEtcmF0YSBkYXRhIHRlcnNlYnV0LiANCg0KIyMjIyBFRElUIGBCTUlgIERBVEENCg0Kc2ViZW5hcm55YSBhZGEgYmVyYXBhIG5pbGFpIGBOL0FgIHBhZGEgYEJNSWAgbWFhcmkga2l0YSBjZWsgOg0KYGBge3J9DQpxdHlfbmEgPC0gc3VtKFN0cm9rZV9kYXRhJGJtaSA9PSAiTi9BIikNCnF0eV9uYQ0KYGBgDQoNCnVudHVrIG1lbmdlZGl0IHNlbXVhIGRhdGEgeWFuZyBiZXJuaWxhaSBOQSBkZW5nYW4gY2FyYSBzZWJhZ2FpIGJlcmlrdXQgOg0KDQpgYGB7cn0NCnN0cm9rZV9jbGVhbl9kYXRhJGJtaSA8LSBhcy5udW1lcmljKHN0cm9rZV9jbGVhbl9kYXRhJGJtaSkNCk1lYW5fdmFsdWUgPC0gbWVhbihzdHJva2VfY2xlYW5fZGF0YSRibWksIG5hLnJtID0gVFJVRSkNCnByaW50KE1lYW5fdmFsdWUpDQpgYGANCmBgYHtyfQ0Kc3Ryb2tlX2NsZWFuX2RhdGEkYm1pW2lzLm5hKHN0cm9rZV9jbGVhbl9kYXRhJGJtaSldIDwtIE1lYW5fdmFsdWUNCmhlYWQoc3Ryb2tlX2NsZWFuX2RhdGEkYm1pLCAyMCkNCmBgYA0KDQojIyMjIE1JU1NJTkcgVkFMVUUNCg0KYGBge3J9DQphbnlOQShzdHJva2VfY2xlYW5fZGF0YSkNCmBgYA0KDQojIyMjIERBVEEgRFVQTElDQVRFDQoNCmBgYHtyfQ0KZHVwbGljYXRlX3Jvd3MgPC0gc3Ryb2tlX2NsZWFuX2RhdGFbZHVwbGljYXRlZChzdHJva2VfY2xlYW5fZGF0YSkgfCBkdXBsaWNhdGVkKHN0cm9rZV9jbGVhbl9kYXRhLCBmcm9tTGFzdCA9IFRSVUUpLCBdDQpwcmludChkdXBsaWNhdGVfcm93cykNCmBgYA0KDQojIyMjIENIQU5HRSBUSEUgREFUQSBTVFJVQ1RVUg0KDQpVbnR1ayBpbmkgdGlkYWsgcGVybHUgZGkgbGFrdWthbiwga2FyZW5hIGRhdGEgYWthbiBkaSBqYWRpa2FuIGR1bW15IHVudHVrIGRpIGxha3VrYW4gYFNDQUxFYCwgZGFwYXQgZGlsaWhhdCBwYWRhIFN1Yi1iYWIgREFUQSBQUk9DRVNJTkcuDQoNCg0KIyMgREFUQSBQUk9DRVNJTkcNCg0KVGFoYXAgaW5pIGFkYWxhaCBtZW11bGFpIHByb3NlcyBwZXJzaWFwYW4gZGF0YQ0KDQojIyMgU0FNUExJTkcgREFUQQ0KDQpgYGB7cn0NCiMgUmFuZG9tIHNhbXBsaW5nDQpzYW1wbGVzaXplIDwtIDAuNjAgKiBucm93KHN0cm9rZV9jbGVhbl9kYXRhKQ0KYGBgDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoODApDQppbmRleCA8LSBzYW1wbGUoIHNlcV9sZW4obnJvdyhzdHJva2VfY2xlYW5fZGF0YSkpLCBzaXplID0gc2FtcGxlc2l6ZSApDQpgYGANCg0KYGBge3J9DQojIENyZWF0ZSB0cmFpbmluZyBhbmQgdGVzdCBzZXQNCmRhdGF0cmFpbiA8LSBzdHJva2VfY2xlYW5fZGF0YVsgaW5kZXgsIF0NCmRhdGF0ZXN0IDwtIHN0cm9rZV9jbGVhbl9kYXRhWyAtaW5kZXgsIF0NCmBgYA0KDQoNCiMjIyBTQ0FMRSBEQVRBDQoNCmBgYHtyfQ0KIyMgU2NhbGUgZGF0YSBmb3IgbmV1cmFsIG5ldHdvcmsNCm1heCA8LSBhcHBseShzdHJva2VfY2xlYW5fZGF0YSwgMiAsIG1heCkNCm1pbiA8LSBhcHBseShzdHJva2VfY2xlYW5fZGF0YSwgMiAsIG1pbikNCnNjYWxlZCA8LSBhcy5kYXRhLmZyYW1lKHNjYWxlKHN0cm9rZV9jbGVhbl9kYXRhLCBjZW50ZXIgPSBUUlVFLCBzY2FsZSA9IFRSVUUpKQ0Ka2FibGUoaGVhZChzY2FsZWQpKQ0KYGBgDQoNCkVyb3IgZGkgYXRhcyBrYXJlbmFrYW4gZGF0YSBgY2xlYW5fc3Ryb2tlX2RhdGFgIGhhcnVzIGRpIHViYWggbWVuamRpIGRhdGEgbnVtZXJpaywgc2VoaW5nZ2EgZGFwYXQgZGkgbGFrdWthbiBkZW5nYW4gc2ludGF4IGJlcmlrdXQNCg0KIyMjIyBEVU1NWSBEQVRBDQoNCmBgYHtyfQ0KIyBNZWxha3VrYW4gT25lLUhvdCBFbmNvZGluZyBwYWRhIGtvbG9tIGthdGVnb3Jpa2FsDQpEdW1teV9kYXRhIDwtIG1vZGVsLm1hdHJpeCh+IC4gLSAxLCBkYXRhID0gc3Ryb2tlX2NsZWFuX2RhdGEpDQpoZWFkKER1bW15X2RhdGEsIDUpDQpgYGANCg0KIyMjIyBQUk9DRVNTSU5HIFNDQUxFDQoNCmBgYHtyfQ0KU2NhbGVkMiA8LSBhcy5kYXRhLmZyYW1lKHNjYWxlKER1bW15X2RhdGEsIGNlbnRlciA9IFRSVUUsIHNjYWxlID0gVFJVRSkpDQooaGVhZChTY2FsZWQyKSkNCmBgYA0KDQpTY2FsaW5nIGRhdGEgc3VkYWggc2VsZXNhaS4gSGFsIGluaSBwZW50aW5nIGthcmVuYSBqaWthIHRpZGFrLCBzdWF0dSBwZXViYWggbXVuZ2tpbiBtZW1wdW55YWkgZGFtcGFrIGJlc2FyIHBhZGEgcGV1YmFoIGhhc2lsIHByZWRpa3NpIGhhbnlhIGthcmVuYSBza2FsYW55YS4gVGVya2FkYW5nIHBldWJhaCB5YW5nIGJlbHVtIGRpbGFrdWthbiBwcm9zZXMgYHNjYWxpbmdgIGNlbmRlcnVuZyBtZW5naGFzaWxrYW4gaGFzaWwgeWFuZyB0aWRhayBtZW1pbGlraSBtYWtuYS4NCg0KDQojIyBNT0RFTElORw0KDQojIyMgQU5OIGRlbmdhbiBgbmV1cmFsbmV0YA0KDQpTZWxhaW4gdGVyZGFwYXQgZGkgZGFsYW0gcGFrZXQgYGtlcmFzYCBkYW4gYHRlbnNvcmZsb3dgLCBrb211bml0YXMgUiBqdWdhIG1lbmdlbWJhbmdrYW4gbW9kZWwgamFyaW5nYW4gc3lhcmFmIHRpcnVhbiAoQU5OKSBwYWRhIHBha2V0IGBuZXVyYWxuZXRgLiBKaWthIGluZ2luIG1lbWJhY2EgbGViaWggZGFsYW0gdGVya2FpdCBgbmV1cmFsbmV0YCBiaXNhIGRpIGxpaGF0IFtkaSBzaW5pXShodHRwczovL3d3dy5yZG9jdW1lbnRhdGlvbi5vcmcvcGFja2FnZXMvbmV1cmFsbmV0L3ZlcnNpb25zLzEuNDQuMi90b3BpY3MvbmV1cmFsbmV0KSBhdGF1IGRlbmdhbiBSdW5uaW5nIHNpbnRheCBiZXJpa3V0IGA/bmV1cmFsbmV0YCBwYWRhIF9fY29uc29sZV9fIFIuDQoNCiMjIyBQQVJUSVNJIERBVEENCg0KcHJvc2VzcyBtZW1idWF0IHRyYWluaW5nIGFuZCB0ZXN0IHNldA0KDQpgYGB7cn0NCnRyYWluTk4gPC0gU2NhbGVkMltpbmRleCAsIF0NCnRlc3ROTiA8LSBTY2FsZWQyWy1pbmRleCAsIF0NCmBgYA0KYGBge3J9DQpoZWFkKHRyYWluTk4sIDUpDQpgYGANCmBgYHtyfQ0KaGVhZCh0ZXN0Tk4sIDUpDQpgYGANCmBgYHtyfQ0KZ2xpbXBzZSh0cmFpbk5OKQ0KYGBgDQoNCiMjIyBNRU1CQU5HVU4gTU9ERUwNCg0KYGBge3J9DQojIGZpdCBuZXVyYWwgbmV0d29yaw0Kc2V0LnNlZWQoMikNCnRlc3Nzc3NzIDwtIG5ldXJhbG5ldChzdHJva2UgfiBldmVyX21hcnJpZWRZZXMgKyBhZ2UgKyBoeXBlcnRlbnNpb24gKyBhdmdfZ2x1Y29zZV9sZXZlbCArIGJtaSArIHNtb2tpbmdfc3RhdHVzc21va2VzLCB0cmFpbk5OLA0KICAgICAgICAgICAgICAgICAgICAgIGhpZGRlbiA9IDMsDQogICAgICAgICAgICAgICAgICAgICAgbGluZWFyLm91dHB1dCA9IFQpDQpgYGANCg0KIyMjIFZJU1VBTElTQVNJIEFOTg0KDQpgYGB7cn0NCnBsb3QodGVzc3Nzc3MpDQpgYGANCg0KIyMjIFBSRURJS0lTDQoNCmBgYHtyfQ0KIyMgUHJlZGljdGlvbiB1c2luZyBuZXVyYWwgbmV0d29yaw0KcHJlZGljdF90ZXN0Tk4xIDwtIGNvbXB1dGUodGVzc3Nzc3MsIHRlc3ROTikNCnByZWRpY3RfdGVzdE5OIDwtIChwcmVkaWN0X3Rlc3ROTjEkbmV0LnJlc3VsdCAqIChtYXgoc3Ryb2tlX2NsZWFuX2RhdGEkc3Ryb2tlKSAtIG1pbihzdHJva2VfY2xlYW5fZGF0YSRzdHJva2UpKSkgKyBtaW4oc3Ryb2tlX2NsZWFuX2RhdGEkc3Ryb2tlKQ0KYGBgDQoNCmBgYHtyfQ0KI3wgZmlnLWhlaWdodDogNA0KcGxvdChkYXRhdGVzdCRzdHJva2UsIHByZWRpY3RfdGVzdE5OLCBjb2w9J2RhcmtyZWQnLCBwY2g9MTYsIHlsYWIgPSAicHJlZGljdGVkIHJhdGluZyIsIHhsYWIgPSAiU1RST0tFIikNCg0KYWJsaW5lKDAsMSkNCmBgYA0KDQojIyMgUk9PVCBNRUFOIFNRVUFSRQ0KDQpgYGB7cn0NCiMgQ2FsY3VsYXRlIFJvb3QgTWVhbiBTcXVhcmUgRXJyb3IgKFJNU0UpDQpSTVNFLk5OIDwtIChzdW0oKGRhdGF0ZXN0JHN0cm9rZSAtIHByZWRpY3RfdGVzdE5OKV4yKSAvIG5yb3coZGF0YXRlc3QpKSBeIDAuNQ0KY2F0KCJNZXRyaWsgUk1TRTogIiwgUk1TRS5OTikNCmBgYA0KDQpgUk1TRWAgbWVtaWxpa2kgbmlsYWkgc2VraXRhciAwLjM4NDk4My4gSW5pIGJlcmFydGkgYmFod2EsIHNlY2FyYSByYXRhLXJhdGEsIHBlcmJlZGFhbiBhbnRhcmEgbmlsYWkgcHJlZGlrc2kgbW9kZWwgQW5kYSBkZW5nYW4gbmlsYWkgYWt0dWFsIChkYWxhbSB1bml0IHlhbmcgc2FtYSBkZW5nYW4gZGF0YSkgYWRhbGFoIHNla2l0YXIgMC4zODQ5ODMuIFNlbWFraW4gbWVuZGVrYXRpIG5vbCwgc2VtYWtpbiBiYWlrIGtpbmVyamEgbW9kZWwgeWFuZyBkaSBidWF0Lg0KDQojIyBDT05DTFVTSU9ODQoNCiMjIyBGRUFUVVJFIElOUE9SVEFOQ0UNCg0KYGBge3J9DQojfCBmaWctaGVpZ2h0OiA0DQpYIDwtIHRlc3ROTlt3aGljaChuYW1lcyh0ZXN0Tk4pICE9ICJzdHJva2UiKV0NCnByZWRpY3RvciA8LSBQcmVkaWN0b3IkbmV3KHRlc3Nzc3NzLCBkYXRhID0gWCwgeSA9IHRlc3ROTiRzdHJva2UpDQoNCmltcCA8LSBGZWF0dXJlSW1wJG5ldyhwcmVkaWN0b3IsIGxvc3MgPSAibWFlIikNCnBsb3QoaW1wKQ0KYGBgDQoNCiMjIyBSRVNVTFQgSU1QDQoNCmBgYHtyfQ0KaW1wJHJlc3VsdHMNCmBgYA0KDQojIyMgU0hBUA0KDQpgYGB7cn0NCiN8IGZpZy1oZWlnaHQ6IDQNCnNoYXBsZXkgPC0gU2hhcGxleSRuZXcocHJlZGljdG9yLCB4LmludGVyZXN0ID0gWFsxLCBdKQ0Kc2hhcGxleSRwbG90KCkNCmBgYA0KDQojIyBFTkQNCg0KVGVyYWtoaXIuDQoNCiMjIyMgUmVzdWx0IElNUA0KDQpgZmVhdHVyZSAoZml0dXIpYDogS29sb20gaW5pIGJlcmlzaSBuYW1hIGZpdHVyLWZpdHVyIHlhbmcgZGlldmFsdWFzaSBkYWxhbSBtb2RlbC4gSW5pIGFkYWxhaCBkYWZ0YXIgZml0dXItZml0dXIgeWFuZyBkaWd1bmFrYW4gZGFsYW0gbW9kZWwgeWFuZyBkaSBidWF0Lg0KDQpgaW1wb3J0YW5jZS4wNWA6IEluaSBhZGFsYWggbmlsYWkgcGVudGluZyBmaXR1ciAoZmVhdHVyZSBpbXBvcnRhbmNlKSBwYWRhIHRpbmdrYXQgNSUgKHRlcmVuZGFoKSBkYWxhbSBkaXN0cmlidXNpLiBOaWxhaSBpbmkgbWVudW5qdWtrYW4gc2VqYXVoIG1hbmEgZml0dXIgdGVyc2VidXQgcGVudGluZyBkYWxhbSBtb2RlbCBzYWF0IG1lbGloYXQgcGVyc2VudGlsIHRlcmVuZGFoIGRhcmkgZGlzdHJpYnVzaS4NCg0KYGltcG9ydGFuY2VgOiBJbmkgYWRhbGFoIG5pbGFpIHBlbnRpbmcgZml0dXIgcGFkYSB0aW5na2F0IHRlbmdhaCAobWVkaWFuKSBkYWxhbSBkaXN0cmlidXNpLiBJbmkgYWRhbGFoIHVrdXJhbiBwZW50aW5nbnlhIGZpdHVyIGRhbGFtIG1vZGVsIHNhYXQgbWVsaWhhdCBwZXJzZW50aWwgdGVuZ2FoIGRhcmkgZGlzdHJpYnVzaS4NCg0KYGltcG9ydGFuY2UuOTVgOiBJbmkgYWRhbGFoIG5pbGFpIHBlbnRpbmcgZml0dXIgcGFkYSB0aW5na2F0IDk1JSAodGVydGluZ2dpKSBkYWxhbSBkaXN0cmlidXNpLiBJbmkgbWVudW5qdWtrYW4gc2VqYXVoIG1hbmEgZml0dXIgdGVyc2VidXQgcGVudGluZyBkYWxhbSBtb2RlbCBzYWF0IG1lbGloYXQgcGVyc2VudGlsIHRlcnRpbmdnaSBkYXJpIGRpc3RyaWJ1c2kuDQoNCkRhcmkgdGFibGUgYElNUGAgZGkgYXRhcyB0ZXJsaWhhdCBiYWh3YSB2YXJpYmxlIGBldmVyX21hcnJpZWRZZXNgIHNhbmdhdCBwZW50aW5nIGRhbGFtIG1lbXBlbmdhcnVoaSBtb2RlbCB5YW5nIGRpIGJ1YXQsIHNlbGFuanV0bnlhIGRpIGlrdXRpIGBhZ2VgIC8gVXNpYSBkYW4gYEJNSWAuDQoNCiMjIyMgUmVzdWx0IFNIRVBMWQ0KDQpEYXJpIG5pbGFpIElNUCBkaSBqZWxhc2thbiBiYWh3YSBgZXZlcl9tYXJyaWVkWWVzYCBkYW4gYEJNSWAgdGVybWFzdWsgZGFsYW0gYmFnaWFuIHlhbmcgcGVudGluZyBkYWxhbSBtb2RlbCwgbmFtdW4gZGkgbGloYXQgZGFyaSBJbnRlcnByZXRhc2kgcGxvdCBgU0hFUExZYCBrZWR1YSB2YXJpYWJsZSBpbmkgbWVtaWxpa2kgbmlsYWkgeWFuZyBuZWdhdGlmLiBhcnRpbnlhIHByZWRpa3NpIGFrYW4gY2VuZGVydW5nIHJlbmRhaC4NCg0KDQpTZWxhbmp1dG55YSB5YW5nIG1lbWlsaWtpIG5pbGFpIHNoYXBseSBwb3NpdGlmIGFkYWxhaCB2YXJpYWJsZSA6IA0KDQotIGBhdmdfZ2x1Y29zZV9sZXZlbGA6IGF2ZXJhZ2UgZ2x1Y29zZSBsZXZlbCBpbiBibG9vZA0KDQotIGBhZ2VgOiBhZ2Ugb2YgdGhlIHBhdGllbnQNCg0KbWFrYSBkYXBhdCBkaSBzaW1wdWxrYW4gamlrYSBDbGllbnQgbWVtaWxpa2kgbmlsYWkgR2x1Y29zZSAoZ3VsYSAvIERpYWJldGVzKSBkYW4gdXNpYSB5YW5nIGN1a3VwIHRpbmdnaSBpbmkgbWVuamFkaSB0YW5kYSB1bnR1ayBiZXJkb2EgbGViaWggYmFueWFrLCBrYXJlbmEgcGVsdWFuZyB0ZXJzZXJhbmcgc3Ryb2tlIHNhbmdhdCB0aW5nZ2kuDQoNCiMjIyMgS09SRUtTSQ0KDQpEZWFyIFR1dG9yLA0KDQpqaWthIGFkYSBrZXNhbGFoYW4gbW9ob24ga2lyYW55YSBkaSBrb3Jla3NpIGppa2Egc2VtcGF0Lg0KDQoNCkRlbWlraWFuIGRpIHNhbXBhaWthbiwgYXRhcyBiYW50dWFuIGRhbiBpbG11bnlhIGRpIHVjYXBrYW4gdGVyaW1ha2FzaWguDQoNCl9fUkVHQVJEUyxfXw0KDQpgQUxCQU5JYA0KDQoNCg0K