1 Pendahuluan

Penyakit kardiovaskular, termasuk gagal jantung, merupakan penyebab utama kematian di seluruh dunia. Organisasi Kesehatan Dunia (WHO) melaporkan bahwa penyakit jantung bertanggung jawab atas sekitar 17,9 juta kematian setiap tahunnya, mewakili 31% dari seluruh kematian global. Gagal jantung adalah kondisi medis serius di mana jantung tidak mampu memompa darah secara efektif untuk memenuhi kebutuhan tubuh, sehingga menyebabkan berbagai komplikasi dan berpotensi fatal.

Pemahaman mendalam mengenai faktor-faktor risiko yang memengaruhi kematian akibat gagal jantung sangat penting untuk pengembangan strategi pencegahan dan penanganan medis yang tepat. Regresi logistik biner merupakan salah satu metode statistika yang paling tepat digunakan untuk memodelkan hubungan antara peubah respon dikotomis (biner) dengan satu atau lebih peubah prediktor. Interpretasi koefisien melalui nilai odds ratio memberikan pemahaman langsung mengenai besaran pengaruh setiap peubah prediktor terhadap peubah respon.

Tujuan penelitian:

  • Mengidentifikasi faktor-faktor klinis yang berpengaruh signifikan terhadap kematian pasien gagal jantung.
  • Membangun model prediksi menggunakan regresi logistik biner.
  • Memilih model terbaik berdasarkan nilai AIC dan akurasi seimbang, serta menguji kelayakannya.

2 Bahan dan Metode

2.1 Data dan Peubah Penelitian

Data yang digunakan merupakan data sekunder dari dataset rekam medis klinis pasien gagal jantung yang dipublikasikan oleh Chicco dan Jurman (2020) pada UCI Machine Learning Repository. Dataset mencakup 299 pasien yang didiagnosis menderita gagal jantung dan dirawat di Faisalabad, Pakistan, antara April–Desember 2015, dengan masa tindak lanjut (follow-up) bervariasi antara 4 hingga 285 hari.

Tautan dataset: https://archive.ics.uci.edu/dataset/519/heart+failure+clinical+records

Peubah respon (\(Y\)) adalah status kematian pasien selama masa tindak lanjut (DEATH_EVENT):

  • \(Y = 0\) : pasien bertahan hidup (203 pasien, 67,89%)
  • \(Y = 1\) : pasien meninggal (96 pasien, 32,11%)

Terdapat 12 peubah prediktor yang mencakup kondisi klinis dan demografis pasien:

Peubah Keterangan Satuan Skala
X1 Usia (Age) Tahun Rasio
X2 Anemia (Anaemia) 0/1 Nominal
X3 Kreatin Fosfokinase (CPK) mcg/L Rasio
X4 Diabetes 0/1 Nominal
X5 Fraksi Ejeksi (Ejection Fraction) % Rasio
X6 Tekanan Darah Tinggi 0/1 Nominal
X7 Trombosit (Platelets) kiloplatelets/mL Rasio
X8 Kreatinin Serum mg/dL Rasio
X9 Natrium Serum mEq/L Rasio
X10 Jenis Kelamin 0=P, 1=L Nominal
X11 Merokok 0/1 Nominal
X12 Waktu Tindak Lanjut Hari Rasio

2.2 Regresi Logistik Biner

Logit dari model logistik berganda dituliskan sebagai:

\[ g(x) = \ln\left(\frac{\pi(x)}{1 - \pi(x)}\right) = \beta_0 + \beta_1 x_1 + \beta_2 x_2 + \cdots + \beta_p x_p \]

Model regresi logistik berganda:

\[ \pi(x) = \frac{e^{g(x)}}{1 + e^{g(x)}} \]

dengan \(\pi(x)\) adalah peluang kelas \(Y = 1\), \(\beta_0\) intersep, \(\beta_j\) koefisien regresi ke-\(j\), dan \(p\) banyaknya peubah prediktor.

2.3 Prosedur Penelitian

  1. Mendeskripsikan data dan karakteristik peubah respon serta prediktor.
  2. Seleksi peubah berdasarkan analisis korelasi dan nilai Variance Inflation Factor (VIF).
  3. Memodelkan dengan 4 pendekatan: model penuh, forward selection, backward elimination, dan model peubah signifikan.
  4. Evaluasi performa (akurasi, sensitivitas, spesifisitas, akurasi seimbang); pemilihan model terbaik berdasarkan AIC terkecil.
  5. Pengujian parameter serentak (Uji G) dan parsial (Uji Wald) pada taraf nyata 5%.
  6. Uji kelayakan model (Hosmer–Lemeshow).
  7. Interpretasi koefisien menggunakan odds ratio.

3 Persiapan: Package dan Import Data

# Daftar package yang digunakan
packages <- c(
  "tidyverse", "psych", "corrplot", "car", "MASS",
  "ResourceSelection", "pscl", "caret", "pROC",
  "patchwork", "broom", "knitr", "gt", "ggcorrplot", "kableExtra"
)

# Menginstall package yang belum terpasang (hanya berjalan jika ada yang kurang)
paket_belum_ada <- packages[!sapply(packages, requireNamespace, quietly = TRUE)]
if (length(paket_belum_ada) > 0) {
  install.packages(paket_belum_ada, repos = "https://cloud.r-project.org")
}

# Memuat seluruh package
invisible(
  lapply(packages, library, character.only = TRUE)
)

alpha <- 0.05
# Membaca dataset
# Catatan: sesuaikan path dengan lokasi file Anda.
# Saat menjalankan interaktif, dapat juga menggunakan: read.csv(file.choose())
data <- read.csv(file.choose())

glimpse(data)
## Rows: 299
## Columns: 13
## $ age                      <int> 75, 55, 65, 50, 65, 90, 75, 60, 65, 80, 75, 6…
## $ anaemia                  <int> 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, …
## $ creatinine_phosphokinase <int> 582, 7861, 146, 111, 160, 47, 246, 315, 157, …
## $ diabetes                 <int> 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, …
## $ ejection_fraction        <int> 20, 38, 20, 20, 20, 40, 15, 60, 65, 35, 38, 2…
## $ high_blood_pressure      <int> 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, …
## $ platelets                <dbl> 265000, 263358, 162000, 210000, 327000, 20400…
## $ serum_creatinine         <dbl> 1.90, 1.10, 1.30, 1.90, 2.70, 2.10, 1.20, 1.1…
## $ serum_sodium             <int> 130, 136, 129, 137, 116, 132, 137, 131, 138, …
## $ sex                      <int> 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, …
## $ smoking                  <int> 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, …
## $ time                     <int> 4, 6, 7, 7, 8, 8, 10, 10, 10, 10, 10, 10, 11,…
## $ DEATH_EVENT              <int> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, …
summary(data)
##       age           anaemia       creatinine_phosphokinase    diabetes     
##  Min.   :40.00   Min.   :0.0000   Min.   :  23.0           Min.   :0.0000  
##  1st Qu.:51.00   1st Qu.:0.0000   1st Qu.: 116.5           1st Qu.:0.0000  
##  Median :60.00   Median :0.0000   Median : 250.0           Median :0.0000  
##  Mean   :60.83   Mean   :0.4314   Mean   : 581.8           Mean   :0.4181  
##  3rd Qu.:70.00   3rd Qu.:1.0000   3rd Qu.: 582.0           3rd Qu.:1.0000  
##  Max.   :95.00   Max.   :1.0000   Max.   :7861.0           Max.   :1.0000  
##  ejection_fraction high_blood_pressure   platelets      serum_creatinine
##  Min.   :14.00     Min.   :0.0000      Min.   : 25100   Min.   :0.500   
##  1st Qu.:30.00     1st Qu.:0.0000      1st Qu.:212500   1st Qu.:0.900   
##  Median :38.00     Median :0.0000      Median :262000   Median :1.100   
##  Mean   :38.08     Mean   :0.3512      Mean   :263358   Mean   :1.394   
##  3rd Qu.:45.00     3rd Qu.:1.0000      3rd Qu.:303500   3rd Qu.:1.400   
##  Max.   :80.00     Max.   :1.0000      Max.   :850000   Max.   :9.400   
##   serum_sodium        sex            smoking            time      
##  Min.   :113.0   Min.   :0.0000   Min.   :0.0000   Min.   :  4.0  
##  1st Qu.:134.0   1st Qu.:0.0000   1st Qu.:0.0000   1st Qu.: 73.0  
##  Median :137.0   Median :1.0000   Median :0.0000   Median :115.0  
##  Mean   :136.6   Mean   :0.6488   Mean   :0.3211   Mean   :130.3  
##  3rd Qu.:140.0   3rd Qu.:1.0000   3rd Qu.:1.0000   3rd Qu.:203.0  
##  Max.   :148.0   Max.   :1.0000   Max.   :1.0000   Max.   :285.0  
##   DEATH_EVENT    
##  Min.   :0.0000  
##  1st Qu.:0.0000  
##  Median :0.0000  
##  Mean   :0.3211  
##  3rd Qu.:1.0000  
##  Max.   :1.0000

Catatan: Untuk menjalankan secara interaktif di RStudio, ganti baris read.csv(...) dengan data <- read.csv(file.choose()) lalu pilih file dataset secara manual.


4 Analisis Deskriptif

4.1 Distribusi Status Kematian Pasien

death_summary <- data %>%
  count(DEATH_EVENT) %>%
  mutate(
    Persentase = round(n / sum(n) * 100, 2),
    Status = c("Tidak Meninggal", "Meninggal")
  )
death_summary
ggplot(death_summary, aes(x = Status, y = n, fill = Status)) +
  geom_col(width = 0.65, show.legend = FALSE) +
  geom_text(
    aes(label = paste0(n, "\n(", Persentase, "%)")),
    size = 5, vjust = 1.2
  ) +
  labs(
    title = "Distribusi Status Kematian Pasien",
    x = "", y = "Jumlah Pasien"
  ) +
  theme_minimal(base_size = 14)

Dari 299 observasi tanpa missing value, sebanyak 203 pasien (67,89%) bertahan hidup dan 96 pasien (32,11%) meninggal.

4.2 Statistik Deskriptif Peubah Kontinu

kontinu <- data %>%
  dplyr::select(
    age, creatinine_phosphokinase, ejection_fraction,
    platelets, serum_creatinine, serum_sodium, time
  )

describe(kontinu)

Untuk peubah biner: 129 pasien (43,1%) mengalami anemia, 125 (41,8%) menderita diabetes, 105 (35,1%) memiliki tekanan darah tinggi, 194 (64,9%) berjenis kelamin laki-laki, dan 96 (32,1%) merupakan perokok.


5 Seleksi Peubah

5.1 Analisis Korelasi Pearson

cor_matrix <- cor(
  data %>% dplyr::select(-DEATH_EVENT),
  use = "complete.obs"
)

nama_variabel <- c(
  "X1_Usia", "X2_Anemia", "X3_Kr.Fosfokinase", "X4_Diabetes",
  "X5_Fraksi Ejeksi", "X6_Tek.Darah Tinggi", "X7_Trombosit",
  "X8_Kr.Serum", "X9_Natrium Serum", "X10_Jenis Kelamin",
  "X11_Merokok", "X12_Waktu"
)
colnames(cor_matrix) <- nama_variabel
rownames(cor_matrix) <- nama_variabel

corrplot(
  cor_matrix, method = "color", type = "upper",
  addCoef.col = "black", number.cex = 0.7,
  tl.col = "black", tl.pos = "td", tl.cex = 0.9, diag = TRUE,
  col = colorRampPalette(c("#B2182B", "white", "#2166AC"))(200)
)
mtext("Plot Korelasi Peubah Prediktor", side = 3, line = 1, cex = 1.4, font = 3)

Tidak ditemukan pasangan peubah dengan korelasi di atas 0,70 (ambang batas kritis). Nilai korelasi tertinggi terdapat antara jenis kelamin (X10) dan merokok (X11) sebesar 0,446, masih di bawah ambang batas, sehingga seluruh peubah dipertahankan.

5.2 Variance Inflation Factor (VIF)

model_full_vif <- glm(
  DEATH_EVENT ~ age + anaemia + creatinine_phosphokinase + diabetes +
    ejection_fraction + high_blood_pressure + platelets +
    serum_creatinine + serum_sodium + sex + smoking + time,
  family = binomial(link = "logit"), data = data
)

round(vif(model_full_vif), 2)
##                      age                  anaemia creatinine_phosphokinase 
##                     1.10                     1.11                     1.09 
##                 diabetes        ejection_fraction      high_blood_pressure 
##                     1.05                     1.17                     1.06 
##                platelets         serum_creatinine             serum_sodium 
##                     1.05                     1.10                     1.07 
##                      sex                  smoking                     time 
##                     1.38                     1.28                     1.15

Seluruh peubah memiliki nilai VIF < 10 (berkisar 1,05–1,38), sehingga tidak terdeteksi masalah multikolinieritas. Semua peubah prediktor tetap dipertahankan dalam pemodelan.


6 Pembentukan Model

6.1 Model Penuh (Full Model)

model_full <- glm(
  DEATH_EVENT ~ age + anaemia + creatinine_phosphokinase + diabetes +
    ejection_fraction + high_blood_pressure + platelets +
    serum_creatinine + serum_sodium + sex + smoking + time,
  family = binomial(link = "logit"), data = data
)

summary(model_full)
## 
## Call:
## glm(formula = DEATH_EVENT ~ age + anaemia + creatinine_phosphokinase + 
##     diabetes + ejection_fraction + high_blood_pressure + platelets + 
##     serum_creatinine + serum_sodium + sex + smoking + time, family = binomial(link = "logit"), 
##     data = data)
## 
## Coefficients:
##                            Estimate Std. Error z value Pr(>|z|)    
## (Intercept)               1.019e+01  5.656e+00   1.802 0.071588 .  
## age                       4.729e-02  1.579e-02   2.995 0.002749 ** 
## anaemia                  -6.938e-03  3.604e-01  -0.019 0.984644    
## creatinine_phosphokinase  2.220e-04  1.779e-04   1.248 0.212035    
## diabetes                  1.452e-01  3.512e-01   0.414 0.679158    
## ejection_fraction        -7.663e-02  1.633e-02  -4.694 2.68e-06 ***
## high_blood_pressure      -1.025e-01  3.587e-01  -0.286 0.775006    
## platelets                -1.198e-06  1.889e-06  -0.634 0.525992    
## serum_creatinine          6.660e-01  1.815e-01   3.670 0.000242 ***
## serum_sodium             -6.699e-02  3.973e-02  -1.686 0.091801 .  
## sex                      -5.330e-01  4.139e-01  -1.288 0.197795    
## smoking                  -1.376e-02  4.126e-01  -0.033 0.973389    
## time                     -2.104e-02  3.014e-03  -6.981 2.94e-12 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 375.35  on 298  degrees of freedom
## Residual deviance: 219.60  on 286  degrees of freedom
## AIC: 245.6
## 
## Number of Fisher Scoring iterations: 6
AIC(model_full)
## [1] 245.6024

6.2 Model Forward Selection

null_model <- glm(DEATH_EVENT ~ 1, family = binomial, data = data)

model_forward <- stepAIC(
  null_model,
  scope = list(lower = null_model, upper = model_full),
  direction = "forward", trace = FALSE
)

summary(model_forward)
## 
## Call:
## glm(formula = DEATH_EVENT ~ time + ejection_fraction + serum_creatinine + 
##     age + serum_sodium, family = binomial, data = data)
## 
## Coefficients:
##                    Estimate Std. Error z value Pr(>|z|)    
## (Intercept)        9.499662   5.405559   1.757  0.07885 .  
## time              -0.020891   0.002916  -7.165 7.76e-13 ***
## ejection_fraction -0.073404   0.015782  -4.651 3.30e-06 ***
## serum_creatinine   0.685916   0.174031   3.941 8.10e-05 ***
## age                0.042352   0.015023   2.819  0.00482 ** 
## serum_sodium      -0.064562   0.038375  -1.682  0.09249 .  
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 375.35  on 298  degrees of freedom
## Residual deviance: 223.53  on 293  degrees of freedom
## AIC: 235.53
## 
## Number of Fisher Scoring iterations: 6
AIC(model_forward)
## [1] 235.5283

6.3 Model Backward Elimination

model_backward <- stepAIC(model_full, direction = "backward", trace = FALSE)

summary(model_backward)
## 
## Call:
## glm(formula = DEATH_EVENT ~ age + ejection_fraction + serum_creatinine + 
##     serum_sodium + time, family = binomial(link = "logit"), data = data)
## 
## Coefficients:
##                    Estimate Std. Error z value Pr(>|z|)    
## (Intercept)        9.499662   5.405559   1.757  0.07885 .  
## age                0.042352   0.015023   2.819  0.00482 ** 
## ejection_fraction -0.073404   0.015782  -4.651 3.30e-06 ***
## serum_creatinine   0.685916   0.174031   3.941 8.10e-05 ***
## serum_sodium      -0.064562   0.038375  -1.682  0.09249 .  
## time              -0.020891   0.002916  -7.165 7.76e-13 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 375.35  on 298  degrees of freedom
## Residual deviance: 223.53  on 293  degrees of freedom
## AIC: 235.53
## 
## Number of Fisher Scoring iterations: 6
AIC(model_backward)
## [1] 235.5283

6.4 Model Peubah Signifikan

model_significant <- glm(
  DEATH_EVENT ~ age + ejection_fraction + serum_creatinine + time,
  family = binomial(link = "logit"), data = data
)

summary(model_significant)
## 
## Call:
## glm(formula = DEATH_EVENT ~ age + ejection_fraction + serum_creatinine + 
##     time, family = binomial(link = "logit"), data = data)
## 
## Coefficients:
##                    Estimate Std. Error z value Pr(>|z|)    
## (Intercept)        0.610280   1.036011   0.589  0.55582    
## age                0.043214   0.014865   2.907  0.00365 ** 
## ejection_fraction -0.074778   0.015552  -4.808 1.52e-06 ***
## serum_creatinine   0.719735   0.174593   4.122 3.75e-05 ***
## time              -0.020607   0.002881  -7.153 8.50e-13 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 375.35  on 298  degrees of freedom
## Residual deviance: 226.34  on 294  degrees of freedom
## AIC: 236.34
## 
## Number of Fisher Scoring iterations: 5
AIC(model_significant)
## [1] 236.3443

7 Pemilihan dan Perbandingan Model

7.1 Perbandingan AIC

aic_table <- data.frame(
  Model = c("Model Penuh", "Model Forward", "Model Backward", "Model Signifikan"),
  AIC = c(
    AIC(model_full), AIC(model_forward),
    AIC(model_backward), AIC(model_significant)
  )
)
aic_table$AIC <- round(aic_table$AIC, 5)

kable(aic_table, caption = "Perbandingan Nilai AIC Antarmodel") %>%
  kable_styling(
    bootstrap_options = c("striped", "hover"),
    full_width = FALSE, position = "center"
  ) %>%
  row_spec(0, bold = TRUE, color = "white", background = "#1a5276")
Perbandingan Nilai AIC Antarmodel
Model AIC
Model Penuh 245.6024
Model Forward 235.5283
Model Backward 235.5283
Model Signifikan 236.3443
best_model <- model_forward

7.2 Perbandingan Kinerja Kandidat Model

model_list <- list(
  "Penuh" = model_full,
  "Forward" = model_forward,
  "Backward" = model_backward,
  "Peubah Signifikan" = model_significant
)

hasil_model <- lapply(names(model_list), function(nama_model) {
  model <- model_list[[nama_model]]
  prob <- predict(model, type = "response")
  pred <- ifelse(prob > 0.5, 1, 0)
  cm <- confusionMatrix(
    factor(pred), factor(data$DEATH_EVENT), positive = "1"
  )
  data.frame(
    Model = nama_model,
    AIC = round(AIC(model), 5),
    A  = round(100 * cm$overall["Accuracy"], 2),
    S  = round(100 * cm$byClass["Sensitivity"], 2),
    Sp = round(100 * cm$byClass["Specificity"], 2),
    BA = round(100 * cm$byClass["Balanced Accuracy"], 2),
    Peubah = paste0(length(coef(model)) - 1, " dari 12")
  )
})

tabel_model <- do.call(rbind, hasil_model)
rownames(tabel_model) <- NULL

kable(
  tabel_model, digits = 2,
  caption = "Perbandingan Kinerja Kandidat Model Regresi Logistik Biner"
) %>%
  kable_styling(
    bootstrap_options = c("striped", "hover", "condensed"),
    full_width = FALSE, position = "center"
  ) %>%
  row_spec(0, bold = TRUE, color = "white", background = "#1a5276") %>%
  row_spec(2, bold = TRUE, background = "#e9f9f4")  # baris model forward (terpilih)
Perbandingan Kinerja Kandidat Model Regresi Logistik Biner
Model AIC A S Sp BA Peubah
Penuh 245.60 85.62 71.88 92.12 82.00 12 dari 12
Forward 235.53 83.61 68.75 90.64 79.70 5 dari 12
Backward 235.53 83.61 68.75 90.64 79.70 5 dari 12
Peubah Signifikan 236.34 83.28 68.75 90.15 79.45 4 dari 12

Model forward dan backward menghasilkan AIC terkecil yang sama, yaitu 235,49, dengan akurasi, sensitivitas, spesifisitas, dan akurasi seimbang yang identik. Berdasarkan prinsip parsimoni, model forward dipilih sebagai model akhir. Model ini memuat lima peubah: usia (X1), fraksi ejeksi (X5), kreatinin serum (X8), natrium serum (X9), dan waktu tindak lanjut (X12).


8 Pengujian Model Terpilih

8.1 Persamaan Model

\[ \text{Logit}[P(Y=1)] = 9{,}4930 + 0{,}0425\,X_1 - 0{,}0734\,X_5 + 0{,}6860\,X_8 - 0{,}0646\,X_9 - 0{,}0209\,X_{12} \]

8.2 Uji Serentak (Uji G / Likelihood Ratio Test)

anova(update(best_model, . ~ 1), best_model, test = "Chisq")

Nilai \(G^2 = 151{,}8625 > \chi^2_{(0,05; 5)} = 11{,}070\), maka \(H_0\) ditolak. Dengan demikian, minimal terdapat satu peubah prediktor yang berpengaruh signifikan terhadap status kematian pasien.

8.3 Uji Parsial (Uji Wald)

summary(best_model)
## 
## Call:
## glm(formula = DEATH_EVENT ~ time + ejection_fraction + serum_creatinine + 
##     age + serum_sodium, family = binomial, data = data)
## 
## Coefficients:
##                    Estimate Std. Error z value Pr(>|z|)    
## (Intercept)        9.499662   5.405559   1.757  0.07885 .  
## time              -0.020891   0.002916  -7.165 7.76e-13 ***
## ejection_fraction -0.073404   0.015782  -4.651 3.30e-06 ***
## serum_creatinine   0.685916   0.174031   3.941 8.10e-05 ***
## age                0.042352   0.015023   2.819  0.00482 ** 
## serum_sodium      -0.064562   0.038375  -1.682  0.09249 .  
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 375.35  on 298  degrees of freedom
## Residual deviance: 223.53  on 293  degrees of freedom
## AIC: 235.53
## 
## Number of Fisher Scoring iterations: 6

Peubah usia (X1), fraksi ejeksi (X5), kreatinin serum (X8), dan waktu tindak lanjut (X12) memiliki nilai-p < 0,05 sehingga berpengaruh signifikan. Peubah natrium serum (X9) memiliki nilai-p = 0,0925 (> 0,05) sehingga tidak signifikan secara parsial, namun tetap dipertahankan karena meningkatkan kualitas model berdasarkan AIC.

8.4 Uji Kelayakan Model Hosmer–Lemeshow

hoslem.test(data$DEATH_EVENT, fitted(best_model))
## 
##  Hosmer and Lemeshow goodness of fit (GOF) test
## 
## data:  data$DEATH_EVENT, fitted(best_model)
## X-squared = 12.959, df = 8, p-value = 0.1133

Nilai-p uji Hosmer–Lemeshow lebih besar dari 0,05, sehingga \(H_0\) tidak ditolak. Model regresi logistik biner yang terpilih layak digunakan pada taraf nyata 5%.

8.5 Pseudo R-squared

pR2(best_model)
## fitting null model for pseudo-r2
##          llh      llhNull           G2     McFadden         r2ML         r2CU 
## -111.7641293 -187.6743883  151.8205180    0.4044785    0.3981584    0.5568469

9 Interpretasi Koefisien: Odds Ratio

OR <- exp(coef(best_model))
CI <- exp(confint(best_model))

OR_table <- data.frame(
  Variabel  = names(OR),
  OddsRatio = round(OR, 4),
  CI_Lower  = round(CI[, 1], 4),
  CI_Upper  = round(CI[, 2], 4)
)
rownames(OR_table) <- NULL

kable(OR_table, caption = "Odds Ratio Model Terpilih") %>%
  kable_styling(
    bootstrap_options = c("striped", "hover", "condensed"),
    full_width = FALSE, position = "center"
  ) %>%
  row_spec(0, bold = TRUE, color = "white", background = "#1a5276")
Odds Ratio Model Terpilih
Variabel OddsRatio CI_Lower CI_Upper
(Intercept) 13355.2162 0.3365 6.980819e+08
time 0.9793 0.9734 9.846000e-01
ejection_fraction 0.9292 0.8995 9.572000e-01
serum_creatinine 1.9856 1.4214 2.874100e+00
age 1.0433 1.0137 1.075500e+00
serum_sodium 0.9375 0.8681 1.011000e+00

9.0.0.1 💡 Interpretasi Utama

  • Usia (OR = 1,0434): setiap kenaikan usia 1 tahun meningkatkan odds kematian ~4,34% (ceteris paribus).
  • Fraksi Ejeksi (OR = 0,9292): setiap kenaikan 1% fraksi ejeksi menurunkan odds kematian ~7,08%.
  • Kreatinin Serum (OR = 1,9857): setiap kenaikan 1 mg/dL meningkatkan odds kematian hampir dua kali lipat — mendukung konsep kardio-renal syndrome.
  • Natrium Serum (OR = 0,9375): kecenderungan menurunkan odds kematian, meski tidak signifikan secara parsial.
  • Waktu Tindak Lanjut (OR = 0,9793): pasien yang bertahan lebih lama cenderung memiliki prognosis lebih baik.

10 Evaluasi Klasifikasi

10.1 Confusion Matrix

prob <- predict(best_model, type = "response")
pred <- ifelse(prob > 0.5, 1, 0)

cm <- confusionMatrix(
  factor(pred), factor(data$DEATH_EVENT), positive = "1"
)
cm
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction   0   1
##          0 184  30
##          1  19  66
##                                           
##                Accuracy : 0.8361          
##                  95% CI : (0.7892, 0.8762)
##     No Information Rate : 0.6789          
##     P-Value [Acc > NIR] : 5.259e-10       
##                                           
##                   Kappa : 0.6124          
##                                           
##  Mcnemar's Test P-Value : 0.1531          
##                                           
##             Sensitivity : 0.6875          
##             Specificity : 0.9064          
##          Pos Pred Value : 0.7765          
##          Neg Pred Value : 0.8598          
##              Prevalence : 0.3211          
##          Detection Rate : 0.2207          
##    Detection Prevalence : 0.2843          
##       Balanced Accuracy : 0.7970          
##                                           
##        'Positive' Class : 1               
## 

Model menghasilkan akurasi 83,61%, sensitivitas 68,75%, spesifisitas 90,64%, dan akurasi seimbang 79,70%.

10.2 Kurva ROC dan AUC

roc_obj <- roc(data$DEATH_EVENT, prob)
auc(roc_obj)
## Area under the curve: 0.8935
plot(
  roc_obj, print.auc = TRUE, legacy.axes = TRUE,
  main = "ROC Curve Model Terbaik"
)
abline(a = 0, b = 1, lty = 2)


11 Visualisasi Probabilitas Model Logistik

Visualisasi kurva sigmoid untuk keempat peubah signifikan dalam model terbaik.

plot_logistic_curve <- function(var_name) {
  label_id <- c(
    age = "Usia",
    ejection_fraction = "Fraksi Ejeksi",
    serum_creatinine = "Kreatinin Serum",
    time = "Waktu Tindak Lanjut"
  )

  newdata <- data.frame(
    age = median(data$age),
    anaemia = median(data$anaemia),
    creatinine_phosphokinase = median(data$creatinine_phosphokinase),
    diabetes = median(data$diabetes),
    ejection_fraction = median(data$ejection_fraction),
    high_blood_pressure = median(data$high_blood_pressure),
    platelets = median(data$platelets),
    serum_creatinine = median(data$serum_creatinine),
    serum_sodium = median(data$serum_sodium),
    sex = median(data$sex),
    smoking = median(data$smoking),
    time = median(data$time)
  )

  newdata <- newdata[rep(1, 100), ]
  newdata[[var_name]] <- seq(
    min(data[[var_name]]), max(data[[var_name]]), length.out = 100
  )

  pred_curve <- predict(
    best_model, newdata = newdata, type = "link", se.fit = TRUE
  )
  newdata$prob  <- plogis(pred_curve$fit)
  newdata$lower <- plogis(pred_curve$fit - 1.96 * pred_curve$se.fit)
  newdata$upper <- plogis(pred_curve$fit + 1.96 * pred_curve$se.fit)

  ggplot() +
    geom_jitter(
      data = data, aes(x = .data[[var_name]], y = DEATH_EVENT),
      width = 0, height = 0.03, color = "#6B7280", alpha = 0.35, size = 2
    ) +
    geom_ribbon(
      data = newdata, aes(x = .data[[var_name]], ymin = lower, ymax = upper),
      fill = "#60A5FA", alpha = 0.25
    ) +
    geom_line(
      data = newdata, aes(x = .data[[var_name]], y = prob),
      linewidth = 1.5, color = "#1D4ED8"
    ) +
    geom_hline(
      yintercept = 0.5, linetype = "dashed", linewidth = 0.8, color = "#DC2626"
    ) +
    labs(
      title = paste("Kurva Sigmoid:", label_id[var_name]),
      x = label_id[var_name], y = "Probabilitas Kematian"
    ) +
    scale_y_continuous(limits = c(0, 1), breaks = seq(0, 1, 0.2)) +
    theme_minimal(base_size = 13) +
    theme(
      plot.title = element_text(face = "bold", size = 14, hjust = 0.5),
      axis.title = element_text(face = "bold"),
      panel.grid.minor = element_blank()
    )
}
p1 <- plot_logistic_curve("age")
p2 <- plot_logistic_curve("ejection_fraction")
p3 <- plot_logistic_curve("serum_creatinine")
p4 <- plot_logistic_curve("time")

(p1 + p2) / (p3 + p4)

  • Usia: probabilitas kematian meningkat seiring bertambahnya usia.
  • Fraksi Ejeksi: hubungan negatif — pompa jantung lebih baik berasosiasi dengan peluang bertahan lebih tinggi.
  • Kreatinin Serum: hubungan positif kuat — gangguan fungsi ginjal memperburuk prognosis.
  • Waktu Tindak Lanjut: hubungan negatif kuat — pasien yang bertahan lebih lama cenderung berkondisi klinis lebih baik.

12 Diskusi

Model terbaik untuk memprediksi risiko kematian akibat gagal jantung diperoleh melalui metode forward selection dengan nilai AIC terkecil 235,49 dan akurasi 83,61%. Uji Hosmer–Lemeshow menunjukkan model layak digunakan.

Sejalan dengan Chicco dan Jurman (2020), fraksi ejeksi jantung dan kreatinin serum merupakan dua parameter krusial. Kreatinin serum menjadi prediktor risiko tertinggi — setiap kenaikan satu satuan meningkatkan kemungkinan kematian hampir dua kali lipat, mendukung konsep medis kardio-renal syndrome. Faktor demografis seperti usia dan lama waktu tindak lanjut juga berpengaruh signifikan. Natrium serum, meski tidak signifikan secara parsial, tetap dipertahankan karena memperbaiki kualitas prediksi model secara keseluruhan.


13 Kesimpulan

13.0.0.1 ✅ Ringkasan Temuan

  • Model terbaik diperoleh melalui prosedur forward selection dengan lima peubah prediktor: usia (X1), fraksi ejeksi (X5), kreatinin serum (X8), natrium serum (X9), dan waktu tindak lanjut (X12), dengan AIC = 235,49.
  • Uji Wald (α = 5%) menunjukkan usia, fraksi ejeksi, kreatinin serum, dan waktu tindak lanjut berpengaruh signifikan; natrium serum tidak signifikan secara parsial namun tetap dipertahankan berdasarkan kriteria AIC.
  • Model memiliki akurasi 83,61%, sensitivitas 68,75%, spesifisitas 90,64%, dan akurasi seimbang 79,70%.
  • Berdasarkan odds ratio, kreatinin serum merupakan faktor risiko terpenting (OR ≈ 1,99), mendukung konsep kardio-renal syndrome.

Saran penelitian lanjutan: menggunakan data dengan observasi lebih besar dan periode tindak lanjut lebih panjang; menambahkan peubah klinis lain (BNP, riwayat rawat inap, kelas NYHA, tekanan darah sistolik); serta membandingkan dengan metode klasifikasi lain seperti Random Forest, Gradient Boosting, SVM, atau XGBoost.


Referensi

  1. World Health Organization. Cardiovascular Diseases (CVDs). WHO Fact Sheet, 2021.
  2. Ponikowski, P. et al. 2016 ESC Guidelines for the diagnosis and treatment of acute and chronic heart failure. Eur. Heart J. 2016, 37, 2129–2200.
  3. Reyes, E.B. et al. Heart failure across Asia. Am. Heart J. 2016, 182, 119–128.
  4. Ahmad, T. et al. Clinical implications of chronic heart failure phenotypes defined by cluster analysis. J. Am. Coll. Cardiol. 2014, 64, 1765–1774.
  5. Hosmer, D.; Lemeshow, S.; Sturdivant, R. Applied Logistic Regression, 3rd ed.; Wiley, 2013.
  6. Agresti, A. An Introduction to Categorical Data Analysis, 3rd ed.; Wiley, 2018.
  7. Chicco, D.; Jurman, G. Machine learning can predict survival of patients with heart failure from serum creatinine and ejection fraction alone. BMC Med. Inform. Decis. Mak. 2020, 20, 16.
  8. Montgomery, D.C.; Peck, E.A.; Vining, G.G. Introduction to Linear Regression Analysis, 5th ed.; Wiley, 2012.
  9. Aiken, L.S.; West, S.G. Multiple Regression: Testing and Interpreting Interactions; Sage, 1991.
  10. Manaf, S.A.R. et al. Faktor-faktor yang memengaruhi permasalahan stunting di Jawa Barat menggunakan regresi logistik biner. J. Statistika 2022, 15, 265–274.
LS0tDQp0aXRsZTogIkFuYWxpc2lzIEZha3Rvci1GYWt0b3IgeWFuZyBNZW1lbmdhcnVoaSBLZW1hdGlhbiBBa2liYXQgR2FnYWwgSmFudHVuZyBNZW5nZ3VuYWthbiBSZWdyZXNpIExvZ2lzdGlrIEJpbmVyIg0KYXV0aG9yOiAiTXVoYW1tYWQgRmFpeiBBbCBSb3p6YW4gQW5hbmRyYXB1dHJhLCBLZXZpbiBQcmFuYW5kaWthIFNhcHV0cmEgU2VqYXRpLCBJIEdlZGUgTnlvbWFuIE1pbmRyYSBKYXlhIg0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIHRoZW1lOiBmbGF0bHkNCiAgICBoaWdobGlnaHQ6IHRhbmdvDQogICAgdG9jOiB0cnVlDQogICAgdG9jX2Zsb2F0Og0KICAgICAgY29sbGFwc2VkOiBmYWxzZQ0KICAgICAgc21vb3RoX3Njcm9sbDogdHJ1ZQ0KICAgIHRvY19kZXB0aDogMw0KICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQ0KICAgIGNvZGVfZm9sZGluZzogc2hvdw0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCiAgICBkZl9wcmludDogcGFnZWQNCmVkaXRvcl9vcHRpb25zOiANCiAgbWFya2Rvd246IA0KICAgIHdyYXA6IDcyDQotLS0NCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoDQogIGVjaG8gPSBUUlVFLA0KICBtZXNzYWdlID0gRkFMU0UsDQogIHdhcm5pbmcgPSBGQUxTRSwNCiAgZmlnLmFsaWduID0gImNlbnRlciIsDQogIGZpZy53aWR0aCA9IDgsDQogIGZpZy5oZWlnaHQgPSA1DQopDQpgYGANCg0KYGBge2NzcyBjdXN0b20tc3R5bGUsIGVjaG89RkFMU0V9DQpAaW1wb3J0IHVybCgnaHR0cHM6Ly9mb250cy5nb29nbGVhcGlzLmNvbS9jc3MyP2ZhbWlseT1Qb3BwaW5zOndnaHRANDAwOzUwMDs2MDA7NzAwJmZhbWlseT1Tb3VyY2UrU2FucyszOndnaHRANDAwOzYwMCZkaXNwbGF5PXN3YXAnKTsNCg0KYm9keSB7DQogIGZvbnQtZmFtaWx5OiAnU291cmNlIFNhbnMgMycsICdTZWdvZSBVSScsIHNhbnMtc2VyaWY7DQogIGZvbnQtc2l6ZTogMTZweDsNCiAgY29sb3I6ICMyZDM0MzY7DQogIGxpbmUtaGVpZ2h0OiAxLjc7DQp9DQoNCmgxLCBoMiwgaDMsIGg0IHsNCiAgZm9udC1mYW1pbHk6ICdQb3BwaW5zJywgc2Fucy1zZXJpZjsNCiAgY29sb3I6ICMxYTUyNzY7DQp9DQoNCmgxLnRpdGxlIHsNCiAgYmFja2dyb3VuZDogbGluZWFyLWdyYWRpZW50KDEzNWRlZywgIzFhNTI3NiAwJSwgIzI5ODBiOSA1NSUsICM0OGM5YjAgMTAwJSk7DQogIGNvbG9yOiAjZmZmZmZmOw0KICBwYWRkaW5nOiAzMHB4IDM1cHg7DQogIGJvcmRlci1yYWRpdXM6IDEycHg7DQogIGZvbnQtc2l6ZTogMjZweDsNCiAgbGluZS1oZWlnaHQ6IDEuNDU7DQogIGJveC1zaGFkb3c6IDAgNnB4IDE4cHggcmdiYSgyNiwgODIsIDExOCwgMC4zNSk7DQp9DQoNCmg0LmF1dGhvciwgaDQuZGF0ZSB7DQogIHRleHQtYWxpZ246IGNlbnRlcjsNCiAgY29sb3I6ICM1ZDZkN2U7DQogIGZvbnQtd2VpZ2h0OiA1MDA7DQp9DQoNCmgxOm5vdCgudGl0bGUpIHsNCiAgYm9yZGVyLWJvdHRvbTogNHB4IHNvbGlkICMyOTgwYjk7DQogIHBhZGRpbmctYm90dG9tOiA4cHg7DQogIG1hcmdpbi10b3A6IDUwcHg7DQp9DQoNCmgyIHsNCiAgYm9yZGVyLWxlZnQ6IDZweCBzb2xpZCAjNDhjOWIwOw0KICBwYWRkaW5nLWxlZnQ6IDEycHg7DQogIG1hcmdpbi10b3A6IDM1cHg7DQp9DQoNCi8qIERhZnRhciBpc2kgbWVsYXlhbmcgKi8NCi50b2NpZnkgew0KICBib3JkZXItcmFkaXVzOiAxMHB4Ow0KICBib3JkZXI6IDFweCBzb2xpZCAjZDZlYWY4Ow0KfQ0KDQoudG9jaWZ5IC50b2NpZnktaGVhZGVyID4gLnRvY2lmeS1pdGVtLmFjdGl2ZSwNCi50b2NpZnkgLnRvY2lmeS1pdGVtLmFjdGl2ZSwNCi5saXN0LWdyb3VwLWl0ZW0uYWN0aXZlLA0KLmxpc3QtZ3JvdXAtaXRlbS5hY3RpdmU6aG92ZXIsDQoubGlzdC1ncm91cC1pdGVtLmFjdGl2ZTpmb2N1cyB7DQogIGJhY2tncm91bmQ6IGxpbmVhci1ncmFkaWVudCgxMzVkZWcsICMxYTUyNzYsICMyOTgwYjkpOw0KICBib3JkZXItY29sb3I6ICMxYTUyNzY7DQogIGNvbG9yOiAjZmZmOw0KfQ0KDQovKiBCbG9rIGtvZGUgKi8NCnByZSB7DQogIGJhY2tncm91bmQtY29sb3I6ICNmNGY5ZmM7DQogIGJvcmRlcjogMXB4IHNvbGlkICNkNmVhZjg7DQogIGJvcmRlci1sZWZ0OiA1cHggc29saWQgIzI5ODBiOTsNCiAgYm9yZGVyLXJhZGl1czogOHB4Ow0KfQ0KDQpjb2RlIHsNCiAgY29sb3I6ICNjMDM5MmI7DQogIGJhY2tncm91bmQtY29sb3I6ICNmZGYyZjA7DQogIGJvcmRlci1yYWRpdXM6IDRweDsNCn0NCg0KcHJlIGNvZGUgew0KICBjb2xvcjogaW5oZXJpdDsNCiAgYmFja2dyb3VuZC1jb2xvcjogdHJhbnNwYXJlbnQ7DQp9DQoNCi8qIFRhYmVsICovDQp0YWJsZSB7DQogIGJveC1zaGFkb3c6IDAgMnB4IDEwcHggcmdiYSgwLCAwLCAwLCAwLjA2KTsNCiAgYm9yZGVyLXJhZGl1czogOHB4Ow0KICBvdmVyZmxvdzogaGlkZGVuOw0KfQ0KDQp0aGVhZCB7DQogIGJhY2tncm91bmQ6IGxpbmVhci1ncmFkaWVudCgxMzVkZWcsICMxYTUyNzYsICMyOTgwYjkpOw0KICBjb2xvcjogI2ZmZmZmZjsNCn0NCg0KLyogS3V0aXBhbiAvIGNhdGF0YW4gKi8NCmJsb2NrcXVvdGUgew0KICBiYWNrZ3JvdW5kLWNvbG9yOiAjZmVmOWU3Ow0KICBib3JkZXItbGVmdDogNnB4IHNvbGlkICNmMWM0MGY7DQogIGJvcmRlci1yYWRpdXM6IDAgOHB4IDhweCAwOw0KICBwYWRkaW5nOiAxMnB4IDE4cHg7DQogIGZvbnQtc2l6ZTogMTVweDsNCiAgY29sb3I6ICM3ZDY2MDg7DQp9DQoNCi8qIEtvdGFrIGludGVycHJldGFzaSAmIGtlc2ltcHVsYW4gKi8NCi5rb3Rhay1pbmZvIHsNCiAgYmFja2dyb3VuZC1jb2xvcjogI2VhZjZmYjsNCiAgYm9yZGVyLWxlZnQ6IDZweCBzb2xpZCAjMjk4MGI5Ow0KICBib3JkZXItcmFkaXVzOiAwIDEwcHggMTBweCAwOw0KICBwYWRkaW5nOiAxNnB4IDIycHg7DQogIG1hcmdpbjogMjJweCAwOw0KfQ0KDQoua290YWstaGFzaWwgew0KICBiYWNrZ3JvdW5kLWNvbG9yOiAjZTlmOWY0Ow0KICBib3JkZXItbGVmdDogNnB4IHNvbGlkICMxNmEwODU7DQogIGJvcmRlci1yYWRpdXM6IDAgMTBweCAxMHB4IDA7DQogIHBhZGRpbmc6IDE2cHggMjJweDsNCiAgbWFyZ2luOiAyMnB4IDA7DQp9DQoNCi5rb3Rhay1pbmZvIGg0LCAua290YWstaGFzaWwgaDQgew0KICBtYXJnaW4tdG9wOiAwOw0KfQ0KDQovKiBUb21ib2wgY29kZSBmb2xkaW5nICovDQouYnRuLWRlZmF1bHQgew0KICBib3JkZXItcmFkaXVzOiA2cHg7DQp9DQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMgUGVuZGFodWx1YW4NCg0KUGVueWFraXQga2FyZGlvdmFza3VsYXIsIHRlcm1hc3VrIGdhZ2FsIGphbnR1bmcsIG1lcnVwYWthbiBwZW55ZWJhYg0KdXRhbWEga2VtYXRpYW4gZGkgc2VsdXJ1aCBkdW5pYS4gT3JnYW5pc2FzaSBLZXNlaGF0YW4gRHVuaWEgKFdITykNCm1lbGFwb3JrYW4gYmFod2EgcGVueWFraXQgamFudHVuZyBiZXJ0YW5nZ3VuZyBqYXdhYiBhdGFzIHNla2l0YXIgKioxNyw5DQpqdXRhIGtlbWF0aWFuKiogc2V0aWFwIHRhaHVubnlhLCBtZXdha2lsaSAqKjMxJSoqIGRhcmkgc2VsdXJ1aCBrZW1hdGlhbg0KZ2xvYmFsLiBHYWdhbCBqYW50dW5nIGFkYWxhaCBrb25kaXNpIG1lZGlzIHNlcml1cyBkaSBtYW5hIGphbnR1bmcgdGlkYWsNCm1hbXB1IG1lbW9tcGEgZGFyYWggc2VjYXJhIGVmZWt0aWYgdW50dWsgbWVtZW51aGkga2VidXR1aGFuIHR1YnVoLA0Kc2VoaW5nZ2EgbWVueWViYWJrYW4gYmVyYmFnYWkga29tcGxpa2FzaSBkYW4gYmVycG90ZW5zaSBmYXRhbC4NCg0KUGVtYWhhbWFuIG1lbmRhbGFtIG1lbmdlbmFpIGZha3Rvci1mYWt0b3IgcmlzaWtvIHlhbmcgbWVtZW5nYXJ1aGkNCmtlbWF0aWFuIGFraWJhdCBnYWdhbCBqYW50dW5nIHNhbmdhdCBwZW50aW5nIHVudHVrIHBlbmdlbWJhbmdhbiBzdHJhdGVnaQ0KcGVuY2VnYWhhbiBkYW4gcGVuYW5nYW5hbiBtZWRpcyB5YW5nIHRlcGF0LiAqKlJlZ3Jlc2kgbG9naXN0aWsgYmluZXIqKg0KbWVydXBha2FuIHNhbGFoIHNhdHUgbWV0b2RlIHN0YXRpc3Rpa2EgeWFuZyBwYWxpbmcgdGVwYXQgZGlndW5ha2FuIHVudHVrDQptZW1vZGVsa2FuIGh1YnVuZ2FuIGFudGFyYSBwZXViYWggcmVzcG9uIGRpa290b21pcyAoYmluZXIpIGRlbmdhbiBzYXR1DQphdGF1IGxlYmloIHBldWJhaCBwcmVkaWt0b3IuIEludGVycHJldGFzaSBrb2VmaXNpZW4gbWVsYWx1aSBuaWxhaSAqb2Rkcw0KcmF0aW8qIG1lbWJlcmlrYW4gcGVtYWhhbWFuIGxhbmdzdW5nIG1lbmdlbmFpIGJlc2FyYW4gcGVuZ2FydWggc2V0aWFwDQpwZXViYWggcHJlZGlrdG9yIHRlcmhhZGFwIHBldWJhaCByZXNwb24uDQoNCioqVHVqdWFuIHBlbmVsaXRpYW46KioNCg0KLSAgIE1lbmdpZGVudGlmaWthc2kgZmFrdG9yLWZha3RvciBrbGluaXMgeWFuZyBiZXJwZW5nYXJ1aCBzaWduaWZpa2FuDQogICAgdGVyaGFkYXAga2VtYXRpYW4gcGFzaWVuIGdhZ2FsIGphbnR1bmcuDQotICAgTWVtYmFuZ3VuIG1vZGVsIHByZWRpa3NpIG1lbmdndW5ha2FuIHJlZ3Jlc2kgbG9naXN0aWsgYmluZXIuDQotICAgTWVtaWxpaCBtb2RlbCB0ZXJiYWlrIGJlcmRhc2Fya2FuIG5pbGFpIEFJQyBkYW4gYWt1cmFzaSBzZWltYmFuZywNCiAgICBzZXJ0YSBtZW5ndWppIGtlbGF5YWthbm55YS4NCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMgQmFoYW4gZGFuIE1ldG9kZQ0KDQojIyBEYXRhIGRhbiBQZXViYWggUGVuZWxpdGlhbg0KDQpEYXRhIHlhbmcgZGlndW5ha2FuIG1lcnVwYWthbiBkYXRhIHNla3VuZGVyIGRhcmkgZGF0YXNldCByZWthbSBtZWRpcw0Ka2xpbmlzIHBhc2llbiBnYWdhbCBqYW50dW5nIHlhbmcgZGlwdWJsaWthc2lrYW4gb2xlaCAqKkNoaWNjbyBkYW4gSnVybWFuDQooMjAyMCkqKiBwYWRhICpVQ0kgTWFjaGluZSBMZWFybmluZyBSZXBvc2l0b3J5Ki4gRGF0YXNldCBtZW5jYWt1cCAqKjI5OQ0KcGFzaWVuKiogeWFuZyBkaWRpYWdub3NpcyBtZW5kZXJpdGEgZ2FnYWwgamFudHVuZyBkYW4gZGlyYXdhdCBkaQ0KRmFpc2FsYWJhZCwgUGFraXN0YW4sIGFudGFyYSBBcHJpbOKAk0Rlc2VtYmVyIDIwMTUsIGRlbmdhbiBtYXNhIHRpbmRhaw0KbGFuanV0ICooZm9sbG93LXVwKSogYmVydmFyaWFzaSBhbnRhcmEgNCBoaW5nZ2EgMjg1IGhhcmkuDQoNClRhdXRhbiBkYXRhc2V0Og0KPGh0dHBzOi8vYXJjaGl2ZS5pY3MudWNpLmVkdS9kYXRhc2V0LzUxOS9oZWFydCtmYWlsdXJlK2NsaW5pY2FsK3JlY29yZHM+DQoNClBldWJhaCByZXNwb24gKCRZJCkgYWRhbGFoIHN0YXR1cyBrZW1hdGlhbiBwYXNpZW4gc2VsYW1hIG1hc2EgdGluZGFrDQpsYW5qdXQgKGBERUFUSF9FVkVOVGApOg0KDQotICAgJFkgPSAwJCA6IHBhc2llbiBiZXJ0YWhhbiBoaWR1cCAoMjAzIHBhc2llbiwgNjcsODklKQ0KLSAgICRZID0gMSQgOiBwYXNpZW4gbWVuaW5nZ2FsICg5NiBwYXNpZW4sIDMyLDExJSkNCg0KVGVyZGFwYXQgKioxMiBwZXViYWggcHJlZGlrdG9yKiogeWFuZyBtZW5jYWt1cCBrb25kaXNpIGtsaW5pcyBkYW4NCmRlbW9ncmFmaXMgcGFzaWVuOg0KDQp8IFBldWJhaCB8IEtldGVyYW5nYW4gICAgICAgICAgICAgICAgICAgICAgICAgIHwgU2F0dWFuICAgICAgICAgICB8IFNrYWxhICAgfA0KfC0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLXwNCnwgWDEgICAgIHwgVXNpYSAoKkFnZSopICAgICAgICAgICAgICAgICAgICAgICAgfCBUYWh1biAgICAgICAgICAgIHwgUmFzaW8gICB8DQp8IFgyICAgICB8IEFuZW1pYSAoKkFuYWVtaWEqKSAgICAgICAgICAgICAgICAgIHwgMC8xICAgICAgICAgICAgICB8IE5vbWluYWwgfA0KfCBYMyAgICAgfCBLcmVhdGluIEZvc2Zva2luYXNlICgqQ1BLKikgICAgICAgICB8IG1jZy9MICAgICAgICAgICAgfCBSYXNpbyAgIHwNCnwgWDQgICAgIHwgRGlhYmV0ZXMgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAwLzEgICAgICAgICAgICAgIHwgTm9taW5hbCB8DQp8IFg1ICAgICB8IEZyYWtzaSBFamVrc2kgKCpFamVjdGlvbiBGcmFjdGlvbiopIHwgXCUgICAgICAgICAgICAgICB8IFJhc2lvICAgfA0KfCBYNiAgICAgfCBUZWthbmFuIERhcmFoIFRpbmdnaSAgICAgICAgICAgICAgICB8IDAvMSAgICAgICAgICAgICAgfCBOb21pbmFsIHwNCnwgWDcgICAgIHwgVHJvbWJvc2l0ICgqUGxhdGVsZXRzKikgICAgICAgICAgICAgfCBraWxvcGxhdGVsZXRzL21MIHwgUmFzaW8gICB8DQp8IFg4ICAgICB8IEtyZWF0aW5pbiBTZXJ1bSAgICAgICAgICAgICAgICAgICAgIHwgbWcvZEwgICAgICAgICAgICB8IFJhc2lvICAgfA0KfCBYOSAgICAgfCBOYXRyaXVtIFNlcnVtICAgICAgICAgICAgICAgICAgICAgICB8IG1FcS9MICAgICAgICAgICAgfCBSYXNpbyAgIHwNCnwgWDEwICAgIHwgSmVuaXMgS2VsYW1pbiAgICAgICAgICAgICAgICAgICAgICAgfCAwPVAsIDE9TCAgICAgICAgIHwgTm9taW5hbCB8DQp8IFgxMSAgICB8IE1lcm9rb2sgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgMC8xICAgICAgICAgICAgICB8IE5vbWluYWwgfA0KfCBYMTIgICAgfCBXYWt0dSBUaW5kYWsgTGFuanV0ICAgICAgICAgICAgICAgICB8IEhhcmkgICAgICAgICAgICAgfCBSYXNpbyAgIHwNCg0KIyMgUmVncmVzaSBMb2dpc3RpayBCaW5lcg0KDQpMb2dpdCBkYXJpIG1vZGVsIGxvZ2lzdGlrIGJlcmdhbmRhIGRpdHVsaXNrYW4gc2ViYWdhaToNCg0KJCQNCmcoeCkgPSBcbG5cbGVmdChcZnJhY3tccGkoeCl9ezEgLSBccGkoeCl9XHJpZ2h0KSA9IFxiZXRhXzAgKyBcYmV0YV8xIHhfMSArIFxiZXRhXzIgeF8yICsgXGNkb3RzICsgXGJldGFfcCB4X3ANCiQkDQoNCk1vZGVsIHJlZ3Jlc2kgbG9naXN0aWsgYmVyZ2FuZGE6DQoNCiQkDQpccGkoeCkgPSBcZnJhY3tlXntnKHgpfX17MSArIGVee2coeCl9fQ0KJCQNCg0KZGVuZ2FuICRccGkoeCkkIGFkYWxhaCBwZWx1YW5nIGtlbGFzICRZID0gMSQsICRcYmV0YV8wJCBpbnRlcnNlcCwNCiRcYmV0YV9qJCBrb2VmaXNpZW4gcmVncmVzaSBrZS0kaiQsIGRhbiAkcCQgYmFueWFrbnlhIHBldWJhaCBwcmVkaWt0b3IuDQoNCiMjIFByb3NlZHVyIFBlbmVsaXRpYW4NCg0KMS4gIE1lbmRlc2tyaXBzaWthbiBkYXRhIGRhbiBrYXJha3RlcmlzdGlrIHBldWJhaCByZXNwb24gc2VydGENCiAgICBwcmVkaWt0b3IuDQoyLiAgU2VsZWtzaSBwZXViYWggYmVyZGFzYXJrYW4gYW5hbGlzaXMga29yZWxhc2kgZGFuIG5pbGFpICpWYXJpYW5jZQ0KICAgIEluZmxhdGlvbiBGYWN0b3IqIChWSUYpLg0KMy4gIE1lbW9kZWxrYW4gZGVuZ2FuIDQgcGVuZGVrYXRhbjogbW9kZWwgcGVudWgsICpmb3J3YXJkIHNlbGVjdGlvbiosDQogICAgKmJhY2t3YXJkIGVsaW1pbmF0aW9uKiwgZGFuIG1vZGVsIHBldWJhaCBzaWduaWZpa2FuLg0KNC4gIEV2YWx1YXNpIHBlcmZvcm1hIChha3VyYXNpLCBzZW5zaXRpdml0YXMsIHNwZXNpZmlzaXRhcywgYWt1cmFzaQ0KICAgIHNlaW1iYW5nKTsgcGVtaWxpaGFuIG1vZGVsIHRlcmJhaWsgYmVyZGFzYXJrYW4gQUlDIHRlcmtlY2lsLg0KNS4gIFBlbmd1amlhbiBwYXJhbWV0ZXIgc2VyZW50YWsgKFVqaSBHKSBkYW4gcGFyc2lhbCAoVWppIFdhbGQpIHBhZGENCiAgICB0YXJhZiBueWF0YSA1JS4NCjYuICBVamkga2VsYXlha2FuIG1vZGVsIChIb3NtZXLigJNMZW1lc2hvdykuDQo3LiAgSW50ZXJwcmV0YXNpIGtvZWZpc2llbiBtZW5nZ3VuYWthbiAqb2RkcyByYXRpbyouDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIFBlcnNpYXBhbjogUGFja2FnZSBkYW4gSW1wb3J0IERhdGENCg0KYGBge3IgcGFja2FnZXN9DQojIERhZnRhciBwYWNrYWdlIHlhbmcgZGlndW5ha2FuDQpwYWNrYWdlcyA8LSBjKA0KICAidGlkeXZlcnNlIiwgInBzeWNoIiwgImNvcnJwbG90IiwgImNhciIsICJNQVNTIiwNCiAgIlJlc291cmNlU2VsZWN0aW9uIiwgInBzY2wiLCAiY2FyZXQiLCAicFJPQyIsDQogICJwYXRjaHdvcmsiLCAiYnJvb20iLCAia25pdHIiLCAiZ3QiLCAiZ2djb3JycGxvdCIsICJrYWJsZUV4dHJhIg0KKQ0KDQojIE1lbmdpbnN0YWxsIHBhY2thZ2UgeWFuZyBiZWx1bSB0ZXJwYXNhbmcgKGhhbnlhIGJlcmphbGFuIGppa2EgYWRhIHlhbmcga3VyYW5nKQ0KcGFrZXRfYmVsdW1fYWRhIDwtIHBhY2thZ2VzWyFzYXBwbHkocGFja2FnZXMsIHJlcXVpcmVOYW1lc3BhY2UsIHF1aWV0bHkgPSBUUlVFKV0NCmlmIChsZW5ndGgocGFrZXRfYmVsdW1fYWRhKSA+IDApIHsNCiAgaW5zdGFsbC5wYWNrYWdlcyhwYWtldF9iZWx1bV9hZGEsIHJlcG9zID0gImh0dHBzOi8vY2xvdWQuci1wcm9qZWN0Lm9yZyIpDQp9DQoNCiMgTWVtdWF0IHNlbHVydWggcGFja2FnZQ0KaW52aXNpYmxlKA0KICBsYXBwbHkocGFja2FnZXMsIGxpYnJhcnksIGNoYXJhY3Rlci5vbmx5ID0gVFJVRSkNCikNCg0KYWxwaGEgPC0gMC4wNQ0KYGBgDQoNCmBgYHtyIGltcG9ydC1kYXRhfQ0KIyBNZW1iYWNhIGRhdGFzZXQNCiMgQ2F0YXRhbjogc2VzdWFpa2FuIHBhdGggZGVuZ2FuIGxva2FzaSBmaWxlIEFuZGEuDQojIFNhYXQgbWVuamFsYW5rYW4gaW50ZXJha3RpZiwgZGFwYXQganVnYSBtZW5nZ3VuYWthbjogcmVhZC5jc3YoZmlsZS5jaG9vc2UoKSkNCmRhdGEgPC0gcmVhZC5jc3YoZmlsZS5jaG9vc2UoKSkNCg0KZ2xpbXBzZShkYXRhKQ0Kc3VtbWFyeShkYXRhKQ0KYGBgDQoNCj4gKipDYXRhdGFuOioqIFVudHVrIG1lbmphbGFua2FuIHNlY2FyYSBpbnRlcmFrdGlmIGRpIFJTdHVkaW8sIGdhbnRpDQo+IGJhcmlzIGByZWFkLmNzdiguLi4pYCBkZW5nYW4gYGRhdGEgPC0gcmVhZC5jc3YoZmlsZS5jaG9vc2UoKSlgIGxhbHUNCj4gcGlsaWggZmlsZSBkYXRhc2V0IHNlY2FyYSBtYW51YWwuDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIEFuYWxpc2lzIERlc2tyaXB0aWYNCg0KIyMgRGlzdHJpYnVzaSBTdGF0dXMgS2VtYXRpYW4gUGFzaWVuDQoNCmBgYHtyIGRpc3RyaWJ1c2kta2VtYXRpYW59DQpkZWF0aF9zdW1tYXJ5IDwtIGRhdGEgJT4lDQogIGNvdW50KERFQVRIX0VWRU5UKSAlPiUNCiAgbXV0YXRlKA0KICAgIFBlcnNlbnRhc2UgPSByb3VuZChuIC8gc3VtKG4pICogMTAwLCAyKSwNCiAgICBTdGF0dXMgPSBjKCJUaWRhayBNZW5pbmdnYWwiLCAiTWVuaW5nZ2FsIikNCiAgKQ0KZGVhdGhfc3VtbWFyeQ0KDQpnZ3Bsb3QoZGVhdGhfc3VtbWFyeSwgYWVzKHggPSBTdGF0dXMsIHkgPSBuLCBmaWxsID0gU3RhdHVzKSkgKw0KICBnZW9tX2NvbCh3aWR0aCA9IDAuNjUsIHNob3cubGVnZW5kID0gRkFMU0UpICsNCiAgZ2VvbV90ZXh0KA0KICAgIGFlcyhsYWJlbCA9IHBhc3RlMChuLCAiXG4oIiwgUGVyc2VudGFzZSwgIiUpIikpLA0KICAgIHNpemUgPSA1LCB2anVzdCA9IDEuMg0KICApICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICJEaXN0cmlidXNpIFN0YXR1cyBLZW1hdGlhbiBQYXNpZW4iLA0KICAgIHggPSAiIiwgeSA9ICJKdW1sYWggUGFzaWVuIg0KICApICsNCiAgdGhlbWVfbWluaW1hbChiYXNlX3NpemUgPSAxNCkNCmBgYA0KDQpEYXJpIDI5OSBvYnNlcnZhc2kgdGFucGEgKm1pc3NpbmcgdmFsdWUqLCBzZWJhbnlhayAqKjIwMyBwYXNpZW4NCig2Nyw4OSUpKiogYmVydGFoYW4gaGlkdXAgZGFuICoqOTYgcGFzaWVuICgzMiwxMSUpKiogbWVuaW5nZ2FsLg0KDQojIyBTdGF0aXN0aWsgRGVza3JpcHRpZiBQZXViYWggS29udGludQ0KDQpgYGB7ciBkZXNrcmlwdGlmLWtvbnRpbnV9DQprb250aW51IDwtIGRhdGEgJT4lDQogIGRwbHlyOjpzZWxlY3QoDQogICAgYWdlLCBjcmVhdGluaW5lX3Bob3NwaG9raW5hc2UsIGVqZWN0aW9uX2ZyYWN0aW9uLA0KICAgIHBsYXRlbGV0cywgc2VydW1fY3JlYXRpbmluZSwgc2VydW1fc29kaXVtLCB0aW1lDQogICkNCg0KZGVzY3JpYmUoa29udGludSkNCmBgYA0KDQpVbnR1ayBwZXViYWggYmluZXI6ICoqMTI5IHBhc2llbiAoNDMsMSUpKiogbWVuZ2FsYW1pIGFuZW1pYSwgKioxMjUNCig0MSw4JSkqKiBtZW5kZXJpdGEgZGlhYmV0ZXMsICoqMTA1ICgzNSwxJSkqKiBtZW1pbGlraSB0ZWthbmFuIGRhcmFoDQp0aW5nZ2ksICoqMTk0ICg2NCw5JSkqKiBiZXJqZW5pcyBrZWxhbWluIGxha2ktbGFraSwgZGFuICoqOTYgKDMyLDElKSoqDQptZXJ1cGFrYW4gcGVyb2tvay4NCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMgU2VsZWtzaSBQZXViYWgNCg0KIyMgQW5hbGlzaXMgS29yZWxhc2kgUGVhcnNvbg0KDQpgYGB7ciBrb3JlbGFzaSwgZmlnLndpZHRoPTksIGZpZy5oZWlnaHQ9OH0NCmNvcl9tYXRyaXggPC0gY29yKA0KICBkYXRhICU+JSBkcGx5cjo6c2VsZWN0KC1ERUFUSF9FVkVOVCksDQogIHVzZSA9ICJjb21wbGV0ZS5vYnMiDQopDQoNCm5hbWFfdmFyaWFiZWwgPC0gYygNCiAgIlgxX1VzaWEiLCAiWDJfQW5lbWlhIiwgIlgzX0tyLkZvc2Zva2luYXNlIiwgIlg0X0RpYWJldGVzIiwNCiAgIlg1X0ZyYWtzaSBFamVrc2kiLCAiWDZfVGVrLkRhcmFoIFRpbmdnaSIsICJYN19Ucm9tYm9zaXQiLA0KICAiWDhfS3IuU2VydW0iLCAiWDlfTmF0cml1bSBTZXJ1bSIsICJYMTBfSmVuaXMgS2VsYW1pbiIsDQogICJYMTFfTWVyb2tvayIsICJYMTJfV2FrdHUiDQopDQpjb2xuYW1lcyhjb3JfbWF0cml4KSA8LSBuYW1hX3ZhcmlhYmVsDQpyb3duYW1lcyhjb3JfbWF0cml4KSA8LSBuYW1hX3ZhcmlhYmVsDQoNCmNvcnJwbG90KA0KICBjb3JfbWF0cml4LCBtZXRob2QgPSAiY29sb3IiLCB0eXBlID0gInVwcGVyIiwNCiAgYWRkQ29lZi5jb2wgPSAiYmxhY2siLCBudW1iZXIuY2V4ID0gMC43LA0KICB0bC5jb2wgPSAiYmxhY2siLCB0bC5wb3MgPSAidGQiLCB0bC5jZXggPSAwLjksIGRpYWcgPSBUUlVFLA0KICBjb2wgPSBjb2xvclJhbXBQYWxldHRlKGMoIiNCMjE4MkIiLCAid2hpdGUiLCAiIzIxNjZBQyIpKSgyMDApDQopDQptdGV4dCgiUGxvdCBLb3JlbGFzaSBQZXViYWggUHJlZGlrdG9yIiwgc2lkZSA9IDMsIGxpbmUgPSAxLCBjZXggPSAxLjQsIGZvbnQgPSAzKQ0KYGBgDQoNClRpZGFrIGRpdGVtdWthbiBwYXNhbmdhbiBwZXViYWggZGVuZ2FuIGtvcmVsYXNpIGRpIGF0YXMgKiowLDcwKiogKGFtYmFuZw0KYmF0YXMga3JpdGlzKS4gTmlsYWkga29yZWxhc2kgdGVydGluZ2dpIHRlcmRhcGF0IGFudGFyYSBqZW5pcyBrZWxhbWluDQooWDEwKSBkYW4gbWVyb2tvayAoWDExKSBzZWJlc2FyICoqMCw0NDYqKiwgbWFzaWggZGkgYmF3YWggYW1iYW5nIGJhdGFzLA0Kc2VoaW5nZ2Egc2VsdXJ1aCBwZXViYWggZGlwZXJ0YWhhbmthbi4NCg0KIyMgVmFyaWFuY2UgSW5mbGF0aW9uIEZhY3RvciAoVklGKQ0KDQpgYGB7ciB2aWZ9DQptb2RlbF9mdWxsX3ZpZiA8LSBnbG0oDQogIERFQVRIX0VWRU5UIH4gYWdlICsgYW5hZW1pYSArIGNyZWF0aW5pbmVfcGhvc3Bob2tpbmFzZSArIGRpYWJldGVzICsNCiAgICBlamVjdGlvbl9mcmFjdGlvbiArIGhpZ2hfYmxvb2RfcHJlc3N1cmUgKyBwbGF0ZWxldHMgKw0KICAgIHNlcnVtX2NyZWF0aW5pbmUgKyBzZXJ1bV9zb2RpdW0gKyBzZXggKyBzbW9raW5nICsgdGltZSwNCiAgZmFtaWx5ID0gYmlub21pYWwobGluayA9ICJsb2dpdCIpLCBkYXRhID0gZGF0YQ0KKQ0KDQpyb3VuZCh2aWYobW9kZWxfZnVsbF92aWYpLCAyKQ0KYGBgDQoNClNlbHVydWggcGV1YmFoIG1lbWlsaWtpIG5pbGFpICoqVklGIFw8IDEwKiogKGJlcmtpc2FyIDEsMDXigJMxLDM4KSwNCnNlaGluZ2dhIHRpZGFrIHRlcmRldGVrc2kgbWFzYWxhaCBtdWx0aWtvbGluaWVyaXRhcy4gU2VtdWEgcGV1YmFoDQpwcmVkaWt0b3IgdGV0YXAgZGlwZXJ0YWhhbmthbiBkYWxhbSBwZW1vZGVsYW4uDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIFBlbWJlbnR1a2FuIE1vZGVsDQoNCiMjIE1vZGVsIFBlbnVoICgqRnVsbCBNb2RlbCopDQoNCmBgYHtyIG1vZGVsLWZ1bGx9DQptb2RlbF9mdWxsIDwtIGdsbSgNCiAgREVBVEhfRVZFTlQgfiBhZ2UgKyBhbmFlbWlhICsgY3JlYXRpbmluZV9waG9zcGhva2luYXNlICsgZGlhYmV0ZXMgKw0KICAgIGVqZWN0aW9uX2ZyYWN0aW9uICsgaGlnaF9ibG9vZF9wcmVzc3VyZSArIHBsYXRlbGV0cyArDQogICAgc2VydW1fY3JlYXRpbmluZSArIHNlcnVtX3NvZGl1bSArIHNleCArIHNtb2tpbmcgKyB0aW1lLA0KICBmYW1pbHkgPSBiaW5vbWlhbChsaW5rID0gImxvZ2l0IiksIGRhdGEgPSBkYXRhDQopDQoNCnN1bW1hcnkobW9kZWxfZnVsbCkNCkFJQyhtb2RlbF9mdWxsKQ0KYGBgDQoNCiMjIE1vZGVsICpGb3J3YXJkIFNlbGVjdGlvbioNCg0KYGBge3IgbW9kZWwtZm9yd2FyZH0NCm51bGxfbW9kZWwgPC0gZ2xtKERFQVRIX0VWRU5UIH4gMSwgZmFtaWx5ID0gYmlub21pYWwsIGRhdGEgPSBkYXRhKQ0KDQptb2RlbF9mb3J3YXJkIDwtIHN0ZXBBSUMoDQogIG51bGxfbW9kZWwsDQogIHNjb3BlID0gbGlzdChsb3dlciA9IG51bGxfbW9kZWwsIHVwcGVyID0gbW9kZWxfZnVsbCksDQogIGRpcmVjdGlvbiA9ICJmb3J3YXJkIiwgdHJhY2UgPSBGQUxTRQ0KKQ0KDQpzdW1tYXJ5KG1vZGVsX2ZvcndhcmQpDQpBSUMobW9kZWxfZm9yd2FyZCkNCmBgYA0KDQojIyBNb2RlbCAqQmFja3dhcmQgRWxpbWluYXRpb24qDQoNCmBgYHtyIG1vZGVsLWJhY2t3YXJkfQ0KbW9kZWxfYmFja3dhcmQgPC0gc3RlcEFJQyhtb2RlbF9mdWxsLCBkaXJlY3Rpb24gPSAiYmFja3dhcmQiLCB0cmFjZSA9IEZBTFNFKQ0KDQpzdW1tYXJ5KG1vZGVsX2JhY2t3YXJkKQ0KQUlDKG1vZGVsX2JhY2t3YXJkKQ0KYGBgDQoNCiMjIE1vZGVsIFBldWJhaCBTaWduaWZpa2FuDQoNCmBgYHtyIG1vZGVsLXNpZ25pZmljYW50fQ0KbW9kZWxfc2lnbmlmaWNhbnQgPC0gZ2xtKA0KICBERUFUSF9FVkVOVCB+IGFnZSArIGVqZWN0aW9uX2ZyYWN0aW9uICsgc2VydW1fY3JlYXRpbmluZSArIHRpbWUsDQogIGZhbWlseSA9IGJpbm9taWFsKGxpbmsgPSAibG9naXQiKSwgZGF0YSA9IGRhdGENCikNCg0Kc3VtbWFyeShtb2RlbF9zaWduaWZpY2FudCkNCkFJQyhtb2RlbF9zaWduaWZpY2FudCkNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyBQZW1pbGloYW4gZGFuIFBlcmJhbmRpbmdhbiBNb2RlbA0KDQojIyBQZXJiYW5kaW5nYW4gQUlDDQoNCmBgYHtyIGFpYy10YWJsZX0NCmFpY190YWJsZSA8LSBkYXRhLmZyYW1lKA0KICBNb2RlbCA9IGMoIk1vZGVsIFBlbnVoIiwgIk1vZGVsIEZvcndhcmQiLCAiTW9kZWwgQmFja3dhcmQiLCAiTW9kZWwgU2lnbmlmaWthbiIpLA0KICBBSUMgPSBjKA0KICAgIEFJQyhtb2RlbF9mdWxsKSwgQUlDKG1vZGVsX2ZvcndhcmQpLA0KICAgIEFJQyhtb2RlbF9iYWNrd2FyZCksIEFJQyhtb2RlbF9zaWduaWZpY2FudCkNCiAgKQ0KKQ0KYWljX3RhYmxlJEFJQyA8LSByb3VuZChhaWNfdGFibGUkQUlDLCA1KQ0KDQprYWJsZShhaWNfdGFibGUsIGNhcHRpb24gPSAiUGVyYmFuZGluZ2FuIE5pbGFpIEFJQyBBbnRhcm1vZGVsIikgJT4lDQogIGthYmxlX3N0eWxpbmcoDQogICAgYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiksDQogICAgZnVsbF93aWR0aCA9IEZBTFNFLCBwb3NpdGlvbiA9ICJjZW50ZXIiDQogICkgJT4lDQogIHJvd19zcGVjKDAsIGJvbGQgPSBUUlVFLCBjb2xvciA9ICJ3aGl0ZSIsIGJhY2tncm91bmQgPSAiIzFhNTI3NiIpDQoNCmJlc3RfbW9kZWwgPC0gbW9kZWxfZm9yd2FyZA0KYGBgDQoNCiMjIFBlcmJhbmRpbmdhbiBLaW5lcmphIEthbmRpZGF0IE1vZGVsDQoNCmBgYHtyIHBlcmJhbmRpbmdhbi1tb2RlbH0NCm1vZGVsX2xpc3QgPC0gbGlzdCgNCiAgIlBlbnVoIiA9IG1vZGVsX2Z1bGwsDQogICJGb3J3YXJkIiA9IG1vZGVsX2ZvcndhcmQsDQogICJCYWNrd2FyZCIgPSBtb2RlbF9iYWNrd2FyZCwNCiAgIlBldWJhaCBTaWduaWZpa2FuIiA9IG1vZGVsX3NpZ25pZmljYW50DQopDQoNCmhhc2lsX21vZGVsIDwtIGxhcHBseShuYW1lcyhtb2RlbF9saXN0KSwgZnVuY3Rpb24obmFtYV9tb2RlbCkgew0KICBtb2RlbCA8LSBtb2RlbF9saXN0W1tuYW1hX21vZGVsXV0NCiAgcHJvYiA8LSBwcmVkaWN0KG1vZGVsLCB0eXBlID0gInJlc3BvbnNlIikNCiAgcHJlZCA8LSBpZmVsc2UocHJvYiA+IDAuNSwgMSwgMCkNCiAgY20gPC0gY29uZnVzaW9uTWF0cml4KA0KICAgIGZhY3RvcihwcmVkKSwgZmFjdG9yKGRhdGEkREVBVEhfRVZFTlQpLCBwb3NpdGl2ZSA9ICIxIg0KICApDQogIGRhdGEuZnJhbWUoDQogICAgTW9kZWwgPSBuYW1hX21vZGVsLA0KICAgIEFJQyA9IHJvdW5kKEFJQyhtb2RlbCksIDUpLA0KICAgIEEgID0gcm91bmQoMTAwICogY20kb3ZlcmFsbFsiQWNjdXJhY3kiXSwgMiksDQogICAgUyAgPSByb3VuZCgxMDAgKiBjbSRieUNsYXNzWyJTZW5zaXRpdml0eSJdLCAyKSwNCiAgICBTcCA9IHJvdW5kKDEwMCAqIGNtJGJ5Q2xhc3NbIlNwZWNpZmljaXR5Il0sIDIpLA0KICAgIEJBID0gcm91bmQoMTAwICogY20kYnlDbGFzc1siQmFsYW5jZWQgQWNjdXJhY3kiXSwgMiksDQogICAgUGV1YmFoID0gcGFzdGUwKGxlbmd0aChjb2VmKG1vZGVsKSkgLSAxLCAiIGRhcmkgMTIiKQ0KICApDQp9KQ0KDQp0YWJlbF9tb2RlbCA8LSBkby5jYWxsKHJiaW5kLCBoYXNpbF9tb2RlbCkNCnJvd25hbWVzKHRhYmVsX21vZGVsKSA8LSBOVUxMDQoNCmthYmxlKA0KICB0YWJlbF9tb2RlbCwgZGlnaXRzID0gMiwNCiAgY2FwdGlvbiA9ICJQZXJiYW5kaW5nYW4gS2luZXJqYSBLYW5kaWRhdCBNb2RlbCBSZWdyZXNpIExvZ2lzdGlrIEJpbmVyIg0KKSAlPiUNCiAga2FibGVfc3R5bGluZygNCiAgICBib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiLCAiY29uZGVuc2VkIiksDQogICAgZnVsbF93aWR0aCA9IEZBTFNFLCBwb3NpdGlvbiA9ICJjZW50ZXIiDQogICkgJT4lDQogIHJvd19zcGVjKDAsIGJvbGQgPSBUUlVFLCBjb2xvciA9ICJ3aGl0ZSIsIGJhY2tncm91bmQgPSAiIzFhNTI3NiIpICU+JQ0KICByb3dfc3BlYygyLCBib2xkID0gVFJVRSwgYmFja2dyb3VuZCA9ICIjZTlmOWY0IikgICMgYmFyaXMgbW9kZWwgZm9yd2FyZCAodGVycGlsaWgpDQpgYGANCg0KTW9kZWwgKipmb3J3YXJkKiogZGFuICoqYmFja3dhcmQqKiBtZW5naGFzaWxrYW4gQUlDIHRlcmtlY2lsIHlhbmcgc2FtYSwNCnlhaXR1ICoqMjM1LDQ5KiosIGRlbmdhbiBha3VyYXNpLCBzZW5zaXRpdml0YXMsIHNwZXNpZmlzaXRhcywgZGFuDQpha3VyYXNpIHNlaW1iYW5nIHlhbmcgaWRlbnRpay4gQmVyZGFzYXJrYW4gKipwcmluc2lwIHBhcnNpbW9uaSoqLCBtb2RlbA0KKipmb3J3YXJkKiogZGlwaWxpaCBzZWJhZ2FpIG1vZGVsIGFraGlyLiBNb2RlbCBpbmkgbWVtdWF0IGxpbWEgcGV1YmFoOg0KdXNpYSAoWDEpLCBmcmFrc2kgZWpla3NpIChYNSksIGtyZWF0aW5pbiBzZXJ1bSAoWDgpLCBuYXRyaXVtIHNlcnVtIChYOSksDQpkYW4gd2FrdHUgdGluZGFrIGxhbmp1dCAoWDEyKS4NCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMgUGVuZ3VqaWFuIE1vZGVsIFRlcnBpbGloDQoNCiMjIFBlcnNhbWFhbiBNb2RlbA0KDQokJA0KXHRleHR7TG9naXR9W1AoWT0xKV0gPSA5eyx9NDkzMCArIDB7LH0wNDI1XCxYXzEgLSAweyx9MDczNFwsWF81ICsgMHssfTY4NjBcLFhfOCAtIDB7LH0wNjQ2XCxYXzkgLSAweyx9MDIwOVwsWF97MTJ9DQokJA0KDQojIyBVamkgU2VyZW50YWsgKFVqaSBHIC8gTGlrZWxpaG9vZCBSYXRpbyBUZXN0KQ0KDQpgYGB7ciB1amktZ30NCmFub3ZhKHVwZGF0ZShiZXN0X21vZGVsLCAuIH4gMSksIGJlc3RfbW9kZWwsIHRlc3QgPSAiQ2hpc3EiKQ0KYGBgDQoNCk5pbGFpICRHXjIgPSAxNTF7LH04NjI1ID4gXGNoaV4yX3soMCwwNTsgNSl9ID0gMTF7LH0wNzAkLCBtYWthICRIXzAkDQpkaXRvbGFrLiBEZW5nYW4gZGVtaWtpYW4sIG1pbmltYWwgdGVyZGFwYXQgc2F0dSBwZXViYWggcHJlZGlrdG9yIHlhbmcNCmJlcnBlbmdhcnVoIHNpZ25pZmlrYW4gdGVyaGFkYXAgc3RhdHVzIGtlbWF0aWFuIHBhc2llbi4NCg0KIyMgVWppIFBhcnNpYWwgKFVqaSBXYWxkKQ0KDQpgYGB7ciB1amktd2FsZH0NCnN1bW1hcnkoYmVzdF9tb2RlbCkNCmBgYA0KDQpQZXViYWggKip1c2lhIChYMSkqKiwgKipmcmFrc2kgZWpla3NpIChYNSkqKiwgKiprcmVhdGluaW4gc2VydW0gKFg4KSoqLA0KZGFuICoqd2FrdHUgdGluZGFrIGxhbmp1dCAoWDEyKSoqIG1lbWlsaWtpIG5pbGFpLXAgXDwgMCwwNSBzZWhpbmdnYQ0KYmVycGVuZ2FydWggc2lnbmlmaWthbi4gUGV1YmFoICoqbmF0cml1bSBzZXJ1bSAoWDkpKiogbWVtaWxpa2kgbmlsYWktcCA9DQowLDA5MjUgKFw+IDAsMDUpIHNlaGluZ2dhIHRpZGFrIHNpZ25pZmlrYW4gc2VjYXJhIHBhcnNpYWwsIG5hbXVuIHRldGFwDQpkaXBlcnRhaGFua2FuIGthcmVuYSBtZW5pbmdrYXRrYW4ga3VhbGl0YXMgbW9kZWwgYmVyZGFzYXJrYW4gQUlDLg0KDQojIyBVamkgS2VsYXlha2FuIE1vZGVsIEhvc21lcuKAk0xlbWVzaG93DQoNCmBgYHtyIGhvc21lci1sZW1lc2hvd30NCmhvc2xlbS50ZXN0KGRhdGEkREVBVEhfRVZFTlQsIGZpdHRlZChiZXN0X21vZGVsKSkNCmBgYA0KDQpOaWxhaS1wIHVqaSBIb3NtZXLigJNMZW1lc2hvdyBsZWJpaCBiZXNhciBkYXJpIDAsMDUsIHNlaGluZ2dhICRIXzAkIHRpZGFrDQpkaXRvbGFrLiBNb2RlbCByZWdyZXNpIGxvZ2lzdGlrIGJpbmVyIHlhbmcgdGVycGlsaWggKipsYXlhayBkaWd1bmFrYW4qKg0KcGFkYSB0YXJhZiBueWF0YSA1JS4NCg0KIyMgUHNldWRvIFItc3F1YXJlZA0KDQpgYGB7ciBwc2V1ZG8tcjJ9DQpwUjIoYmVzdF9tb2RlbCkNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyBJbnRlcnByZXRhc2kgS29lZmlzaWVuOiBPZGRzIFJhdGlvDQoNCmBgYHtyIG9kZHMtcmF0aW99DQpPUiA8LSBleHAoY29lZihiZXN0X21vZGVsKSkNCkNJIDwtIGV4cChjb25maW50KGJlc3RfbW9kZWwpKQ0KDQpPUl90YWJsZSA8LSBkYXRhLmZyYW1lKA0KICBWYXJpYWJlbCAgPSBuYW1lcyhPUiksDQogIE9kZHNSYXRpbyA9IHJvdW5kKE9SLCA0KSwNCiAgQ0lfTG93ZXIgID0gcm91bmQoQ0lbLCAxXSwgNCksDQogIENJX1VwcGVyICA9IHJvdW5kKENJWywgMl0sIDQpDQopDQpyb3duYW1lcyhPUl90YWJsZSkgPC0gTlVMTA0KDQprYWJsZShPUl90YWJsZSwgY2FwdGlvbiA9ICJPZGRzIFJhdGlvIE1vZGVsIFRlcnBpbGloIikgJT4lDQogIGthYmxlX3N0eWxpbmcoDQogICAgYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiwgImNvbmRlbnNlZCIpLA0KICAgIGZ1bGxfd2lkdGggPSBGQUxTRSwgcG9zaXRpb24gPSAiY2VudGVyIg0KICApICU+JQ0KICByb3dfc3BlYygwLCBib2xkID0gVFJVRSwgY29sb3IgPSAid2hpdGUiLCBiYWNrZ3JvdW5kID0gIiMxYTUyNzYiKQ0KYGBgDQoNCjo6OiBrb3Rhay1pbmZvDQojIyMjIPCfkqEgSW50ZXJwcmV0YXNpIFV0YW1hDQoNCi0gICAqKlVzaWEgKE9SID0gMSwwNDM0KToqKiBzZXRpYXAga2VuYWlrYW4gdXNpYSAxIHRhaHVuIG1lbmluZ2thdGthbg0KICAgIG9kZHMga2VtYXRpYW4gXH40LDM0JSAoKmNldGVyaXMgcGFyaWJ1cyopLg0KLSAgICoqRnJha3NpIEVqZWtzaSAoT1IgPSAwLDkyOTIpOioqIHNldGlhcCBrZW5haWthbiAxJSBmcmFrc2kgZWpla3NpDQogICAgbWVudXJ1bmthbiBvZGRzIGtlbWF0aWFuIFx+NywwOCUuDQotICAgKipLcmVhdGluaW4gU2VydW0gKE9SID0gMSw5ODU3KToqKiBzZXRpYXAga2VuYWlrYW4gMSBtZy9kTA0KICAgIG1lbmluZ2thdGthbiBvZGRzIGtlbWF0aWFuIGhhbXBpciAqKmR1YSBrYWxpIGxpcGF0Kiog4oCUIG1lbmR1a3VuZw0KICAgIGtvbnNlcCAqa2FyZGlvLXJlbmFsIHN5bmRyb21lKi4NCi0gICAqKk5hdHJpdW0gU2VydW0gKE9SID0gMCw5Mzc1KToqKiBrZWNlbmRlcnVuZ2FuIG1lbnVydW5rYW4gb2Rkcw0KICAgIGtlbWF0aWFuLCBtZXNraSB0aWRhayBzaWduaWZpa2FuIHNlY2FyYSBwYXJzaWFsLg0KLSAgICoqV2FrdHUgVGluZGFrIExhbmp1dCAoT1IgPSAwLDk3OTMpOioqIHBhc2llbiB5YW5nIGJlcnRhaGFuIGxlYmloDQogICAgbGFtYSBjZW5kZXJ1bmcgbWVtaWxpa2kgcHJvZ25vc2lzIGxlYmloIGJhaWsuDQo6OjoNCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMgRXZhbHVhc2kgS2xhc2lmaWthc2kNCg0KIyMgQ29uZnVzaW9uIE1hdHJpeA0KDQpgYGB7ciBjb25mdXNpb24tbWF0cml4fQ0KcHJvYiA8LSBwcmVkaWN0KGJlc3RfbW9kZWwsIHR5cGUgPSAicmVzcG9uc2UiKQ0KcHJlZCA8LSBpZmVsc2UocHJvYiA+IDAuNSwgMSwgMCkNCg0KY20gPC0gY29uZnVzaW9uTWF0cml4KA0KICBmYWN0b3IocHJlZCksIGZhY3RvcihkYXRhJERFQVRIX0VWRU5UKSwgcG9zaXRpdmUgPSAiMSINCikNCmNtDQpgYGANCg0KTW9kZWwgbWVuZ2hhc2lsa2FuICoqYWt1cmFzaSA4Myw2MSUqKiwgKipzZW5zaXRpdml0YXMgNjgsNzUlKiosDQoqKnNwZXNpZmlzaXRhcyA5MCw2NCUqKiwgZGFuICoqYWt1cmFzaSBzZWltYmFuZyA3OSw3MCUqKi4NCg0KIyMgS3VydmEgUk9DIGRhbiBBVUMNCg0KYGBge3Igcm9jLWF1Y30NCnJvY19vYmogPC0gcm9jKGRhdGEkREVBVEhfRVZFTlQsIHByb2IpDQphdWMocm9jX29iaikNCg0KcGxvdCgNCiAgcm9jX29iaiwgcHJpbnQuYXVjID0gVFJVRSwgbGVnYWN5LmF4ZXMgPSBUUlVFLA0KICBtYWluID0gIlJPQyBDdXJ2ZSBNb2RlbCBUZXJiYWlrIg0KKQ0KYWJsaW5lKGEgPSAwLCBiID0gMSwgbHR5ID0gMikNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyBWaXN1YWxpc2FzaSBQcm9iYWJpbGl0YXMgTW9kZWwgTG9naXN0aWsNCg0KVmlzdWFsaXNhc2kga3VydmEgc2lnbW9pZCB1bnR1ayBrZWVtcGF0IHBldWJhaCBzaWduaWZpa2FuIGRhbGFtIG1vZGVsDQp0ZXJiYWlrLg0KDQpgYGB7ciBmdW5nc2ktc2lnbW9pZH0NCnBsb3RfbG9naXN0aWNfY3VydmUgPC0gZnVuY3Rpb24odmFyX25hbWUpIHsNCiAgbGFiZWxfaWQgPC0gYygNCiAgICBhZ2UgPSAiVXNpYSIsDQogICAgZWplY3Rpb25fZnJhY3Rpb24gPSAiRnJha3NpIEVqZWtzaSIsDQogICAgc2VydW1fY3JlYXRpbmluZSA9ICJLcmVhdGluaW4gU2VydW0iLA0KICAgIHRpbWUgPSAiV2FrdHUgVGluZGFrIExhbmp1dCINCiAgKQ0KDQogIG5ld2RhdGEgPC0gZGF0YS5mcmFtZSgNCiAgICBhZ2UgPSBtZWRpYW4oZGF0YSRhZ2UpLA0KICAgIGFuYWVtaWEgPSBtZWRpYW4oZGF0YSRhbmFlbWlhKSwNCiAgICBjcmVhdGluaW5lX3Bob3NwaG9raW5hc2UgPSBtZWRpYW4oZGF0YSRjcmVhdGluaW5lX3Bob3NwaG9raW5hc2UpLA0KICAgIGRpYWJldGVzID0gbWVkaWFuKGRhdGEkZGlhYmV0ZXMpLA0KICAgIGVqZWN0aW9uX2ZyYWN0aW9uID0gbWVkaWFuKGRhdGEkZWplY3Rpb25fZnJhY3Rpb24pLA0KICAgIGhpZ2hfYmxvb2RfcHJlc3N1cmUgPSBtZWRpYW4oZGF0YSRoaWdoX2Jsb29kX3ByZXNzdXJlKSwNCiAgICBwbGF0ZWxldHMgPSBtZWRpYW4oZGF0YSRwbGF0ZWxldHMpLA0KICAgIHNlcnVtX2NyZWF0aW5pbmUgPSBtZWRpYW4oZGF0YSRzZXJ1bV9jcmVhdGluaW5lKSwNCiAgICBzZXJ1bV9zb2RpdW0gPSBtZWRpYW4oZGF0YSRzZXJ1bV9zb2RpdW0pLA0KICAgIHNleCA9IG1lZGlhbihkYXRhJHNleCksDQogICAgc21va2luZyA9IG1lZGlhbihkYXRhJHNtb2tpbmcpLA0KICAgIHRpbWUgPSBtZWRpYW4oZGF0YSR0aW1lKQ0KICApDQoNCiAgbmV3ZGF0YSA8LSBuZXdkYXRhW3JlcCgxLCAxMDApLCBdDQogIG5ld2RhdGFbW3Zhcl9uYW1lXV0gPC0gc2VxKA0KICAgIG1pbihkYXRhW1t2YXJfbmFtZV1dKSwgbWF4KGRhdGFbW3Zhcl9uYW1lXV0pLCBsZW5ndGgub3V0ID0gMTAwDQogICkNCg0KICBwcmVkX2N1cnZlIDwtIHByZWRpY3QoDQogICAgYmVzdF9tb2RlbCwgbmV3ZGF0YSA9IG5ld2RhdGEsIHR5cGUgPSAibGluayIsIHNlLmZpdCA9IFRSVUUNCiAgKQ0KICBuZXdkYXRhJHByb2IgIDwtIHBsb2dpcyhwcmVkX2N1cnZlJGZpdCkNCiAgbmV3ZGF0YSRsb3dlciA8LSBwbG9naXMocHJlZF9jdXJ2ZSRmaXQgLSAxLjk2ICogcHJlZF9jdXJ2ZSRzZS5maXQpDQogIG5ld2RhdGEkdXBwZXIgPC0gcGxvZ2lzKHByZWRfY3VydmUkZml0ICsgMS45NiAqIHByZWRfY3VydmUkc2UuZml0KQ0KDQogIGdncGxvdCgpICsNCiAgICBnZW9tX2ppdHRlcigNCiAgICAgIGRhdGEgPSBkYXRhLCBhZXMoeCA9IC5kYXRhW1t2YXJfbmFtZV1dLCB5ID0gREVBVEhfRVZFTlQpLA0KICAgICAgd2lkdGggPSAwLCBoZWlnaHQgPSAwLjAzLCBjb2xvciA9ICIjNkI3MjgwIiwgYWxwaGEgPSAwLjM1LCBzaXplID0gMg0KICAgICkgKw0KICAgIGdlb21fcmliYm9uKA0KICAgICAgZGF0YSA9IG5ld2RhdGEsIGFlcyh4ID0gLmRhdGFbW3Zhcl9uYW1lXV0sIHltaW4gPSBsb3dlciwgeW1heCA9IHVwcGVyKSwNCiAgICAgIGZpbGwgPSAiIzYwQTVGQSIsIGFscGhhID0gMC4yNQ0KICAgICkgKw0KICAgIGdlb21fbGluZSgNCiAgICAgIGRhdGEgPSBuZXdkYXRhLCBhZXMoeCA9IC5kYXRhW1t2YXJfbmFtZV1dLCB5ID0gcHJvYiksDQogICAgICBsaW5ld2lkdGggPSAxLjUsIGNvbG9yID0gIiMxRDRFRDgiDQogICAgKSArDQogICAgZ2VvbV9obGluZSgNCiAgICAgIHlpbnRlcmNlcHQgPSAwLjUsIGxpbmV0eXBlID0gImRhc2hlZCIsIGxpbmV3aWR0aCA9IDAuOCwgY29sb3IgPSAiI0RDMjYyNiINCiAgICApICsNCiAgICBsYWJzKA0KICAgICAgdGl0bGUgPSBwYXN0ZSgiS3VydmEgU2lnbW9pZDoiLCBsYWJlbF9pZFt2YXJfbmFtZV0pLA0KICAgICAgeCA9IGxhYmVsX2lkW3Zhcl9uYW1lXSwgeSA9ICJQcm9iYWJpbGl0YXMgS2VtYXRpYW4iDQogICAgKSArDQogICAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoMCwgMSksIGJyZWFrcyA9IHNlcSgwLCAxLCAwLjIpKSArDQogICAgdGhlbWVfbWluaW1hbChiYXNlX3NpemUgPSAxMykgKw0KICAgIHRoZW1lKA0KICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiLCBzaXplID0gMTQsIGhqdXN0ID0gMC41KSwNCiAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiksDQogICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpDQogICAgKQ0KfQ0KYGBgDQoNCmBgYHtyIHBsb3Qtc2lnbW9pZCwgZmlnLndpZHRoPTExLCBmaWcuaGVpZ2h0PTh9DQpwMSA8LSBwbG90X2xvZ2lzdGljX2N1cnZlKCJhZ2UiKQ0KcDIgPC0gcGxvdF9sb2dpc3RpY19jdXJ2ZSgiZWplY3Rpb25fZnJhY3Rpb24iKQ0KcDMgPC0gcGxvdF9sb2dpc3RpY19jdXJ2ZSgic2VydW1fY3JlYXRpbmluZSIpDQpwNCA8LSBwbG90X2xvZ2lzdGljX2N1cnZlKCJ0aW1lIikNCg0KKHAxICsgcDIpIC8gKHAzICsgcDQpDQpgYGANCg0KLSAgICoqVXNpYToqKiBwcm9iYWJpbGl0YXMga2VtYXRpYW4gbWVuaW5na2F0IHNlaXJpbmcgYmVydGFtYmFobnlhIHVzaWEuDQotICAgKipGcmFrc2kgRWpla3NpOioqIGh1YnVuZ2FuIG5lZ2F0aWYg4oCUIHBvbXBhIGphbnR1bmcgbGViaWggYmFpaw0KICAgIGJlcmFzb3NpYXNpIGRlbmdhbiBwZWx1YW5nIGJlcnRhaGFuIGxlYmloIHRpbmdnaS4NCi0gICAqKktyZWF0aW5pbiBTZXJ1bToqKiBodWJ1bmdhbiBwb3NpdGlmIGt1YXQg4oCUIGdhbmdndWFuIGZ1bmdzaSBnaW5qYWwNCiAgICBtZW1wZXJidXJ1ayBwcm9nbm9zaXMuDQotICAgKipXYWt0dSBUaW5kYWsgTGFuanV0OioqIGh1YnVuZ2FuIG5lZ2F0aWYga3VhdCDigJQgcGFzaWVuIHlhbmcNCiAgICBiZXJ0YWhhbiBsZWJpaCBsYW1hIGNlbmRlcnVuZyBiZXJrb25kaXNpIGtsaW5pcyBsZWJpaCBiYWlrLg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyBEaXNrdXNpDQoNCk1vZGVsIHRlcmJhaWsgdW50dWsgbWVtcHJlZGlrc2kgcmlzaWtvIGtlbWF0aWFuIGFraWJhdCBnYWdhbCBqYW50dW5nDQpkaXBlcm9sZWggbWVsYWx1aSBtZXRvZGUgKmZvcndhcmQgc2VsZWN0aW9uKiBkZW5nYW4gbmlsYWkgKipBSUMgdGVya2VjaWwNCjIzNSw0OSoqIGRhbiBha3VyYXNpICoqODMsNjElKiouIFVqaSBIb3NtZXLigJNMZW1lc2hvdyBtZW51bmp1a2thbiBtb2RlbA0KbGF5YWsgZGlndW5ha2FuLg0KDQpTZWphbGFuIGRlbmdhbiBDaGljY28gZGFuIEp1cm1hbiAoMjAyMCksICoqZnJha3NpIGVqZWtzaSBqYW50dW5nKiogZGFuDQoqKmtyZWF0aW5pbiBzZXJ1bSoqIG1lcnVwYWthbiBkdWEgcGFyYW1ldGVyIGtydXNpYWwuIEtyZWF0aW5pbiBzZXJ1bQ0KbWVuamFkaSBwcmVkaWt0b3IgcmlzaWtvIHRlcnRpbmdnaSDigJQgc2V0aWFwIGtlbmFpa2FuIHNhdHUgc2F0dWFuDQptZW5pbmdrYXRrYW4ga2VtdW5na2luYW4ga2VtYXRpYW4gaGFtcGlyIGR1YSBrYWxpIGxpcGF0LCBtZW5kdWt1bmcNCmtvbnNlcCBtZWRpcyAqKiprYXJkaW8tcmVuYWwgc3luZHJvbWUqKiouIEZha3RvciBkZW1vZ3JhZmlzIHNlcGVydGkgdXNpYQ0KZGFuIGxhbWEgd2FrdHUgdGluZGFrIGxhbmp1dCBqdWdhIGJlcnBlbmdhcnVoIHNpZ25pZmlrYW4uIE5hdHJpdW0gc2VydW0sDQptZXNraSB0aWRhayBzaWduaWZpa2FuIHNlY2FyYSBwYXJzaWFsLCB0ZXRhcCBkaXBlcnRhaGFua2FuIGthcmVuYQ0KbWVtcGVyYmFpa2kga3VhbGl0YXMgcHJlZGlrc2kgbW9kZWwgc2VjYXJhIGtlc2VsdXJ1aGFuLg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyBLZXNpbXB1bGFuDQoNCjo6OiBrb3Rhay1oYXNpbA0KIyMjIyDinIUgUmluZ2thc2FuIFRlbXVhbg0KDQotICAgTW9kZWwgdGVyYmFpayBkaXBlcm9sZWggbWVsYWx1aSBwcm9zZWR1ciAqKmZvcndhcmQgc2VsZWN0aW9uKioNCiAgICBkZW5nYW4gbGltYSBwZXViYWggcHJlZGlrdG9yOiB1c2lhIChYMSksIGZyYWtzaSBlamVrc2kgKFg1KSwNCiAgICBrcmVhdGluaW4gc2VydW0gKFg4KSwgbmF0cml1bSBzZXJ1bSAoWDkpLCBkYW4gd2FrdHUgdGluZGFrIGxhbmp1dA0KICAgIChYMTIpLCBkZW5nYW4gKipBSUMgPSAyMzUsNDkqKi4NCi0gICBVamkgV2FsZCAozrEgPSA1JSkgbWVudW5qdWtrYW4gKip1c2lhLCBmcmFrc2kgZWpla3NpLCBrcmVhdGluaW4NCiAgICBzZXJ1bSwgZGFuIHdha3R1IHRpbmRhayBsYW5qdXQqKiBiZXJwZW5nYXJ1aCBzaWduaWZpa2FuOyBuYXRyaXVtDQogICAgc2VydW0gdGlkYWsgc2lnbmlmaWthbiBzZWNhcmEgcGFyc2lhbCBuYW11biB0ZXRhcCBkaXBlcnRhaGFua2FuDQogICAgYmVyZGFzYXJrYW4ga3JpdGVyaWEgQUlDLg0KLSAgIE1vZGVsIG1lbWlsaWtpICoqYWt1cmFzaSA4Myw2MSUqKiwgKipzZW5zaXRpdml0YXMgNjgsNzUlKiosDQogICAgKipzcGVzaWZpc2l0YXMgOTAsNjQlKiosIGRhbiAqKmFrdXJhc2kgc2VpbWJhbmcgNzksNzAlKiouDQotICAgQmVyZGFzYXJrYW4gKm9kZHMgcmF0aW8qLCAqKmtyZWF0aW5pbiBzZXJ1bSoqIG1lcnVwYWthbiBmYWt0b3INCiAgICByaXNpa28gdGVycGVudGluZyAoT1Ig4omIIDEsOTkpLCBtZW5kdWt1bmcga29uc2VwICprYXJkaW8tcmVuYWwNCiAgICBzeW5kcm9tZSouDQo6OjoNCg0KKipTYXJhbiBwZW5lbGl0aWFuIGxhbmp1dGFuOioqIG1lbmdndW5ha2FuIGRhdGEgZGVuZ2FuIG9ic2VydmFzaSBsZWJpaA0KYmVzYXIgZGFuIHBlcmlvZGUgdGluZGFrIGxhbmp1dCBsZWJpaCBwYW5qYW5nOyBtZW5hbWJhaGthbiBwZXViYWgga2xpbmlzDQpsYWluIChCTlAsIHJpd2F5YXQgcmF3YXQgaW5hcCwga2VsYXMgTllIQSwgdGVrYW5hbiBkYXJhaCBzaXN0b2xpayk7DQpzZXJ0YSBtZW1iYW5kaW5na2FuIGRlbmdhbiBtZXRvZGUga2xhc2lmaWthc2kgbGFpbiBzZXBlcnRpIFJhbmRvbQ0KRm9yZXN0LCBHcmFkaWVudCBCb29zdGluZywgU1ZNLCBhdGF1IFhHQm9vc3QuDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIFJlZmVyZW5zaSB7LnVubnVtYmVyZWR9DQoNCjEuICBXb3JsZCBIZWFsdGggT3JnYW5pemF0aW9uLiAqQ2FyZGlvdmFzY3VsYXIgRGlzZWFzZXMgKENWRHMpKi4gV0hPDQogICAgRmFjdCBTaGVldCwgMjAyMS4NCjIuICBQb25pa293c2tpLCBQLiBldCBhbC4gKjIwMTYgRVNDIEd1aWRlbGluZXMgZm9yIHRoZSBkaWFnbm9zaXMgYW5kDQogICAgdHJlYXRtZW50IG9mIGFjdXRlIGFuZCBjaHJvbmljIGhlYXJ0IGZhaWx1cmUqLiBFdXIuIEhlYXJ0IEouIDIwMTYsDQogICAgMzcsIDIxMjnigJMyMjAwLg0KMy4gIFJleWVzLCBFLkIuIGV0IGFsLiAqSGVhcnQgZmFpbHVyZSBhY3Jvc3MgQXNpYSouIEFtLiBIZWFydCBKLiAyMDE2LA0KICAgIDE4MiwgMTE54oCTMTI4Lg0KNC4gIEFobWFkLCBULiBldCBhbC4gKkNsaW5pY2FsIGltcGxpY2F0aW9ucyBvZiBjaHJvbmljIGhlYXJ0IGZhaWx1cmUNCiAgICBwaGVub3R5cGVzIGRlZmluZWQgYnkgY2x1c3RlciBhbmFseXNpcyouIEouIEFtLiBDb2xsLiBDYXJkaW9sLiAyMDE0LA0KICAgIDY0LCAxNzY14oCTMTc3NC4NCjUuICBIb3NtZXIsIEQuOyBMZW1lc2hvdywgUy47IFN0dXJkaXZhbnQsIFIuICpBcHBsaWVkIExvZ2lzdGljDQogICAgUmVncmVzc2lvbiosIDNyZCBlZC47IFdpbGV5LCAyMDEzLg0KNi4gIEFncmVzdGksIEEuICpBbiBJbnRyb2R1Y3Rpb24gdG8gQ2F0ZWdvcmljYWwgRGF0YSBBbmFseXNpcyosIDNyZCBlZC47DQogICAgV2lsZXksIDIwMTguDQo3LiAgQ2hpY2NvLCBELjsgSnVybWFuLCBHLiAqTWFjaGluZSBsZWFybmluZyBjYW4gcHJlZGljdCBzdXJ2aXZhbCBvZg0KICAgIHBhdGllbnRzIHdpdGggaGVhcnQgZmFpbHVyZSBmcm9tIHNlcnVtIGNyZWF0aW5pbmUgYW5kIGVqZWN0aW9uDQogICAgZnJhY3Rpb24gYWxvbmUqLiBCTUMgTWVkLiBJbmZvcm0uIERlY2lzLiBNYWsuIDIwMjAsIDIwLCAxNi4NCjguICBNb250Z29tZXJ5LCBELkMuOyBQZWNrLCBFLkEuOyBWaW5pbmcsIEcuRy4gKkludHJvZHVjdGlvbiB0byBMaW5lYXINCiAgICBSZWdyZXNzaW9uIEFuYWx5c2lzKiwgNXRoIGVkLjsgV2lsZXksIDIwMTIuDQo5LiAgQWlrZW4sIEwuUy47IFdlc3QsIFMuRy4gKk11bHRpcGxlIFJlZ3Jlc3Npb246IFRlc3RpbmcgYW5kDQogICAgSW50ZXJwcmV0aW5nIEludGVyYWN0aW9ucyo7IFNhZ2UsIDE5OTEuDQoxMC4gTWFuYWYsIFMuQS5SLiBldCBhbC4gKkZha3Rvci1mYWt0b3IgeWFuZyBtZW1lbmdhcnVoaSBwZXJtYXNhbGFoYW4NCiAgICBzdHVudGluZyBkaSBKYXdhIEJhcmF0IG1lbmdndW5ha2FuIHJlZ3Jlc2kgbG9naXN0aWsgYmluZXIqLiBKLg0KICAgIFN0YXRpc3Rpa2EgMjAyMiwgMTUsIDI2NeKAkzI3NC4NCg==