1 Read in Bank Loan Default Dataset

Read in dataset.

bank <- read.csv("BankLoanDefaultDataset.csv")
bank2 <- bank
bank$Car_loan <- as.factor(bank$Car_loan)
 bank$Personal_loan <- as.factor(bank$Personal_loan)
 bank$Home_loan <- as.factor(bank$Home_loan)
 bank$Education_loan <- as.factor(bank$Education_loan)
 bank$Gender <- as.factor(bank$Gender)
 bank$Marital_status <- as.factor(bank$Marital_status)
 bank$Emp_status <- as.factor(bank$Emp_status)
 bank$Default <- as.factor(bank$Default)

1.1 Create Missing Values

The original dataset does not have any missing values so I needed to manually create them.

gender.missing.id <- sample(1:1000, 20 , replace = FALSE)
Marital.missing.id <- sample(1:1000, 20, replace = FALSE)
emp.status.missing.id <- sample(1:1000, 20, replace = FALSE)
credit.missing.id <- sample(1:1000, 20, replace = FALSE)
amount.missing.id <- sample(1:1000, 20, replace = FALSE)
emp.duration.missing.id <- sample(1:1000, 20, replace = FALSE)
check.amt.missing.id <- sample(1:1000, 20, replace = FALSE)
age.missing.id <- sample(1:1000, 20, replace = FALSE)

bank$Gender[gender.missing.id] <- NA
bank$Marital_status[Marital.missing.id] <- NA
bank$Emp_status[emp.status.missing.id] <- NA
bank$Credit_score[credit.missing.id] <- NA
bank$Amount[amount.missing.id] <- NA
bank$Emp_duration[emp.duration.missing.id] <- NA
bank$Checking_amount[check.amt.missing.id] <- NA
bank$Age[age.missing.id] <- NA

2 Introduction

Missing values are a normal part of data analysis. To resolve this, we can use imputation. For numerical features, we can impute using the mean or median. For categorical variables, we can impute the mode. We can also use regression methods.

After imputation, we do feature engineering. We can do feature transformation, feature selection, and feature creation.

3 Replacement Imputation for Categorical Features

Gender, marital status, and employment status have missing values. These values can be replaced with the mode.

mode_value_gender <-  which.max(table(bank$Gender)) %>% names()
mode_value_marital_status <- which.max(table(bank$Marital_status)) %>% names()
mode_value_emp_status <- which.max(table(bank$Emp_status)) %>% names()


bank$Gender[is.na(bank$Gender)] <- mode_value_gender
bank$Marital_status[is.na(bank$Marital_status)] <- mode_value_marital_status
bank$Emp_status[is.na(bank$Emp_status)] <- mode_value_emp_status

4 Regression-based Imputation for Numerical Features

Random regression imputation is used in this section for all numerical variables. Regression models are created using features with missing values as the dependent variable and any variable without missing values as a predictor.

bank1 <- bank[ , c('Checking_amount', 'Term','Credit_score', 'Amount','Saving_amount', 'Emp_duration','Age', 'No_of_credit_acc')]
ggpairs(bank1)

pred.mdl1 = lm(Checking_amount ~ Saving_amount, data = bank)
newdata1 = bank[is.na(bank$Checking_amount),]
pred.y1 = predict(pred.mdl1, newdata1 = newdata1)
m1 = sum(is.na(bank$Checking_amount))      # total number of missing values
pred.resid1 = resid(pred.mdl1)    # residual
pred.yrand1 = pred.y1 + sample(pred.resid1, m1, replace = TRUE)
bank$Checking_amount[is.na(bank$Checking_amount)] = pred.yrand1
pred.mdl2 = lm(Credit_score ~ Saving_amount, data = bank)
newdata2 = bank[is.na(bank$Credit_score),]
pred.y2 = predict(pred.mdl2, newdata2 = newdata2)
m2 = sum(is.na(bank$Credit_score))      # total number of missing values
pred.resid2 = resid(pred.mdl2)    # residual
pred.yrand2 = pred.y2 + sample(pred.resid2, m2, replace = TRUE)
bank$Credit_score[is.na(bank$Credit_score)] = pred.yrand2
pred.mdl3 = lm(Amount ~ Checking_amount, data = bank)
newdata3 = bank[is.na(bank$Amount),]
pred.y3 = predict(pred.mdl3, newdata3 = newdata3)
m3 = sum(is.na(bank$Amount))      # total number of missing values
pred.resid3 = resid(pred.mdl3)    # residual
pred.yrand3 = pred.y3 + sample(pred.resid3, m3, replace = TRUE)
bank$Amount[is.na(bank$Amount)] = pred.yrand3
pred.mdl4 = lm(Emp_duration ~ No_of_credit_acc, data = bank)
newdata4 = bank[is.na(bank$Emp_duration),]
pred.y4 = predict(pred.mdl4, newdata4 = newdata4)
m4 = sum(is.na(bank$Emp_duration))      # total number of missing values
pred.resid4 = resid(pred.mdl4)    # residual
pred.yrand4 = pred.y4 + sample(pred.resid4, m4, replace = TRUE)
bank$Emp_duration[is.na(bank$Emp_duration)] = pred.yrand4
pred.mdl5 = lm(Age ~ Saving_amount, data = bank)
newdata5 = bank[is.na(bank1$Age),]
pred.y5 = predict(pred.mdl5, newdata5 = newdata5)
m5 = sum(is.na(bank$Age))      # total number of missing values
pred.resid5 = resid(pred.mdl5)    # residual
pred.yrand5 = pred.y5 + sample(pred.resid5, m5, replace = TRUE)
bank$Age[is.na(bank$Age)] = pred.yrand5

5 Multiple Imputation

We use the MICE imputation method in this section. We impute multiple data sets and combine all the results. The pooled standard error is 19.87721

#bank2 <- bank
init <- mice(bank2, maxit = 0)
init$method 
         Default  Checking_amount             Term     Credit_score 
              ""               ""               ""               "" 
          Gender   Marital_status         Car_loan    Personal_loan 
              ""               ""               ""               "" 
       Home_loan   Education_loan       Emp_status           Amount 
              ""               ""               ""               "" 
   Saving_amount     Emp_duration              Age No_of_credit_acc 
              ""               ""               ""               "" 
imp <- mice(bank2, method = c("","pmm", "", "pmm",  "logreg", "logreg", "", "", "", "", "logreg", "pmm", "", "pmm", "pmm", ""), 
                 maxit = 10,  
                 m = 1,       
                 print=F)     

complete(imp, action = 1L)[1:10,]
   Default Checking_amount Term Credit_score Gender Marital_status Car_loan
1        0             988   15          796 Female         Single        1
2        0             458   15          813 Female         Single        1
3        0             158   14          756 Female         Single        0
4        1             300   25          737 Female         Single        0
5        1              63   24          662 Female         Single        0
6        0            1071   20          828   Male        Married        1
7        0            -192   13          856   Male         Single        1
8        0             172   16          763 Female         Single        1
9        0             585   20          778 Female         Single        1
10       1             189   19          649   Male        Married        1
   Personal_loan Home_loan Education_loan Emp_status Amount Saving_amount
1              0         0              0   employed   1536          3455
2              0         0              0   employed    947          3600
3              1         0              0   employed   1678          3093
4              0         0              1   employed   1804          2449
5              0         0              1 unemployed   1184          2867
6              0         0              0   employed    475          3282
7              0         0              0   employed    626          3398
8              0         0              0   employed   1224          3022
9              0         0              0 unemployed   1162          3475
10             0         0              0   employed    786          2711
   Emp_duration Age No_of_credit_acc
1            12  38                1
2            25  36                1
3            43  34                1
4             0  29                1
5             4  30                1
6            12  32                2
7            11  38                1
8            12  36                1
9            12  36                1
10            0  29                1
imp <- mice(bank2, method = "pmm", 
                  maxit = 10,  
                  m = 2,       
                  seed = 123,  
                  print=F)     
complete(imp, action = "broad")[1:10,]
   Default.1 Checking_amount.1 Term.1 Credit_score.1 Gender.1 Marital_status.1
1          0               988     15            796   Female           Single
2          0               458     15            813   Female           Single
3          0               158     14            756   Female           Single
4          1               300     25            737   Female           Single
5          1                63     24            662   Female           Single
6          0              1071     20            828     Male          Married
7          0              -192     13            856     Male           Single
8          0               172     16            763   Female           Single
9          0               585     20            778   Female           Single
10         1               189     19            649     Male          Married
   Car_loan.1 Personal_loan.1 Home_loan.1 Education_loan.1 Emp_status.1
1           1               0           0                0     employed
2           1               0           0                0     employed
3           0               1           0                0     employed
4           0               0           0                1     employed
5           0               0           0                1   unemployed
6           1               0           0                0     employed
7           1               0           0                0     employed
8           1               0           0                0     employed
9           1               0           0                0   unemployed
10          1               0           0                0     employed
   Amount.1 Saving_amount.1 Emp_duration.1 Age.1 No_of_credit_acc.1 Default.2
1      1536            3455             12    38                  1         0
2       947            3600             25    36                  1         0
3      1678            3093             43    34                  1         0
4      1804            2449              0    29                  1         1
5      1184            2867              4    30                  1         1
6       475            3282             12    32                  2         0
7       626            3398             11    38                  1         0
8      1224            3022             12    36                  1         0
9      1162            3475             12    36                  1         0
10      786            2711              0    29                  1         1
   Checking_amount.2 Term.2 Credit_score.2 Gender.2 Marital_status.2 Car_loan.2
1                988     15            796   Female           Single          1
2                458     15            813   Female           Single          1
3                158     14            756   Female           Single          0
4                300     25            737   Female           Single          0
5                 63     24            662   Female           Single          0
6               1071     20            828     Male          Married          1
7               -192     13            856     Male           Single          1
8                172     16            763   Female           Single          1
9                585     20            778   Female           Single          1
10               189     19            649     Male          Married          1
   Personal_loan.2 Home_loan.2 Education_loan.2 Emp_status.2 Amount.2
1                0           0                0     employed     1536
2                0           0                0     employed      947
3                1           0                0     employed     1678
4                0           0                1     employed     1804
5                0           0                1   unemployed     1184
6                0           0                0     employed      475
7                0           0                0     employed      626
8                0           0                0     employed     1224
9                0           0                0   unemployed     1162
10               0           0                0     employed      786
   Saving_amount.2 Emp_duration.2 Age.2 No_of_credit_acc.2
1             3455             12    38                  1
2             3600             25    36                  1
3             3093             43    34                  1
4             2449              0    29                  1
5             2867              4    30                  1
6             3282             12    32                  2
7             3398             11    38                  1
8             3022             12    36                  1
9             3475             12    36                  1
10            2711              0    29                  1
imp5 <- mice(bank2, method = "pmm", m = 5, maxit = 10, seed = 123, print=F)
plot(imp5)

model5 <- with(imp5, glm(Default ~ Checking_amount + Term + Credit_score + Gender + Marital_status + Car_loan + Personal_loan + Home_loan + Education_loan + Emp_status + Amount + Saving_amount + Emp_duration+ Age + No_of_credit_acc,family='binomial')) 
                                             
                                            
summary.stats = summary(model5)               
                                             
summary.stats 
# A tibble: 80 × 7
   term                 estimate std.error statistic  p.value  nobs df.residual
   <chr>                   <dbl>     <dbl>     <dbl>    <dbl> <int>       <dbl>
 1 (Intercept)          39.6      4.73         8.38  5.13e-17  1000         984
 2 Checking_amount      -0.00509  0.000676    -7.53  5.14e-14  1000         984
 3 Term                  0.170    0.0521       3.27  1.07e- 3  1000         984
 4 Credit_score         -0.0110   0.00207     -5.29  1.21e- 7  1000         984
 5 GenderMale            0.195    0.510        0.383 7.02e- 1  1000         984
 6 Marital_statusSingle  0.335    0.492        0.681 4.96e- 1  1000         984
 7 Car_loan             -0.600    2.76        -0.218 8.28e- 1  1000         984
 8 Personal_loan        -1.55     2.76        -0.563 5.73e- 1  1000         984
 9 Home_loan            -3.57     2.85        -1.25  2.10e- 1  1000         984
10 Education_loan        0.650    2.79         0.233 8.16e- 1  1000         984
# ℹ 70 more rows
summary(pool(model5))
                   term      estimate    std.error   statistic       df
1           (Intercept) 39.6415228699 4.7284136162   8.3836834 981.9055
2       Checking_amount -0.0050880131 0.0006758504  -7.5283125 981.9055
3                  Term  0.1703676065 0.0520728136   3.2717189 981.9055
4          Credit_score -0.0109792626 0.0020746004  -5.2922299 981.9055
5            GenderMale  0.1950805846 0.5095698488   0.3828338 981.9055
6  Marital_statusSingle  0.3351480374 0.4920119886   0.6811786 981.9055
7              Car_loan -0.6004642922 2.7585197474  -0.2176763 981.9055
8         Personal_loan -1.5540876079 2.7585124281  -0.5633789 981.9055
9             Home_loan -3.5684378136 2.8457131490  -1.2539696 981.9055
10       Education_loan  0.6498872918 2.7894964677   0.2329766 981.9055
11 Emp_statusunemployed  0.5872531813 0.3474375980   1.6902407 981.9055
12               Amount  0.0008025898 0.0005113685   1.5694940 981.9055
13        Saving_amount -0.0048212246 0.0006085494  -7.9224872 981.9055
14         Emp_duration  0.0029178043 0.0044391386   0.6572906 981.9055
15                  Age -0.6475369030 0.0646615942 -10.0142428 981.9055
16     No_of_credit_acc -0.0968613571 0.1006466582  -0.9623902 981.9055
        p.value
1  1.764053e-16
2  1.161795e-13
3  1.106195e-03
4  1.490504e-07
5  7.019258e-01
6  4.959191e-01
7  8.277266e-01
8  5.733056e-01
9  2.101515e-01
10 8.158282e-01
11 9.129931e-02
12 1.168551e-01
13 6.284160e-15
14 5.111481e-01
15 1.524709e-22
16 3.360906e-01
beta = summary.stats$estimate[seq(1,15,by=3)]  
beta.var = (summary.stats$std.error[seq(1,15,by=3)])^2 
Q = mean(beta)
U = mean(beta.var)
B = var(beta)
T = U + (6/5)*B
pool.se = sqrt(T)
cbind(pool.se.intercept = pool.se)
     pool.se.intercept
[1,]          19.61562

6 Feature Transforming

The only variable that is skewed is No_of_credit_acc so we use a log transformation.

boxcox_result <- boxcox(lm(bank$No_of_credit_acc ~ 1), lambda = seq(0, 1, by = 0.1))
title("Box-Cox Transformation")

optimal_lambda <- boxcox_result$x[which.max(boxcox_result$y)]
transformed_data <- if (optimal_lambda == 0) {
  log(bank$No_of_credit_acc)
} else {
  (bank$No_of_credit_acc^optimal_lambda - 1) / optimal_lambda
}

pooledData <- data.frame(OriginalData=bank$No_of_credit_acc, TransformedData=transformed_data)

We use standardization for all the numerical variables since these variables have different units. This will help with our modeling later on.

standardize <- function(x) {
  return((x - mean(x)) / sd(x))
}

bank$Checking_amount_standardized <- standardize(bank$Checking_amount)
bank$term_standardized <- standardize(bank$Term)
bank$Credit_score_standardized <- standardize(bank$Credit_score)
bank$amount_standardized <- standardize(bank$Amount)
bank$Saving_amount_standardized <- standardize(bank$Saving_amount)
bank$Emp_duration_standardized <- standardize(bank$Emp_duration)
bank$Age_standardized <- standardize(bank$Age)
bank$No_of_credit_acc_standardized <- standardize(bank$No_of_credit_acc)

7 Feature Selection

To select the most relevant features for our model, we use lasso regularization. Using all the standardized numerical variables, we see that checking amount, term, credit score, amount, saving amount, age, and no of credit accounts are important in determining the Default (target variable).

x <- as.matrix(bank[, c('Checking_amount_standardized', 'term_standardized', 'Credit_score_standardized', 'amount_standardized', 'Saving_amount_standardized', 'Emp_duration_standardized','Age_standardized','No_of_credit_acc_standardized')])
y <- bank$Default

# data$Default <- as.numeric(data$Default)
# data$Gender <- as.numeric(data$Gender)
# data$Marital_status <- as.numeric(data$Marital_status)
# data$Car_loan <- as.numeric(data$Car_loan)
# data$Personal_loan <- as.numeric(data$Personal_loan)
# data$Home_loan <- as.numeric(data$Home_loan)
# data$Education_loan <- as.numeric(data$Education_loan)
# data$Emp_status <- as.numeric(data$Emp_status)


lasso_model <- cv.glmnet(x, y, alpha = 1, family='binomial')
best_lambda <- lasso_model$lambda.min
selected_features <- rownames(coef(lasso_model, s = best_lambda))[coef(lasso_model, s = best_lambda)[,1] != 0]
print(selected_features)
[1] "(Intercept)"                   "Checking_amount_standardized" 
[3] "term_standardized"             "Credit_score_standardized"    
[5] "amount_standardized"           "Saving_amount_standardized"   
[7] "Emp_duration_standardized"     "Age_standardized"             
[9] "No_of_credit_acc_standardized"
best_model <- glmnet(x, y, alpha = 1, lambda = best_lambda, family='binomial')
coef(best_model)
9 x 1 sparse Matrix of class "dgCMatrix"
                                       s0
(Intercept)                   -2.18604138
Checking_amount_standardized  -1.33308370
term_standardized              0.55291905
Credit_score_standardized     -0.85852506
amount_standardized            0.16092370
Saving_amount_standardized    -1.39262931
Emp_duration_standardized     -0.01762975
Age_standardized              -2.13856873
No_of_credit_acc_standardized -0.06042559

8 Feature Creation

Feature creation is not necessary for this dataset. For some variables like Age, it only ranges from 18-42 yrs old so it would be difficult to group all into ‘young’ or ‘old’. For other variables, it would be difficult to group them into different bins because they are measured in units such as months or money.

LS0tDQp0aXRsZTogIkJhbmsgTG9hbiBEZWZhdWx0IERhdGFzZXQgSW1wdXRhdGlvbiBhbmQgRmVhdHVyZSBFbmdpbmVlcmluZyINCmF1dGhvcjogIkVyaWMgWmh1Ig0KZGF0ZTogIjIwMjUtMDItMTkiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6IA0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiA0DQogICAgdG9jX2Zsb2F0OiB5ZXMNCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcw0KICAgIHRvY19jb2xsYXBzZWQ6IHllcw0KICAgIGNvZGVfZm9sZGluZzogaGlkZQ0KICAgIGNvZGVfZG93bmxvYWQ6IHllcw0KICAgIHNtb290aF9zY3JvbGw6IHllcw0KICAgIHRoZW1lOiBsdW1lbg0KICBwZGZfZG9jdW1lbnQ6IA0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiA0DQogICAgZmlnX2NhcHRpb246IHllcw0KICAgIG51bWJlcl9zZWN0aW9uczogbm8NCiAgICBmaWdfd2lkdGg6IDMNCiAgICBmaWdfaGVpZ2h0OiAzDQplZGl0b3Jfb3B0aW9uczogDQogIGNodW5rX291dHB1dF90eXBlOiBpbmxpbmUNCi0tLQ0KDQpgYGB7PWh0bWx9DQoNCjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+DQoNCi8qIENhc2NhZGluZyBTdHlsZSBTaGVldHMgKENTUykgaXMgYSBzdHlsZXNoZWV0IGxhbmd1YWdlIHVzZWQgdG8gZGVzY3JpYmUgdGhlIHByZXNlbnRhdGlvbiBvZiBhIGRvY3VtZW50IHdyaXR0ZW4gaW4gSFRNTCBvciBYTUwuIGl0IGlzIGEgc2ltcGxlIG1lY2hhbmlzbSBmb3IgYWRkaW5nIHN0eWxlIChlLmcuLCBmb250cywgY29sb3JzLCBzcGFjaW5nKSB0byBXZWIgZG9jdW1lbnRzLiAqLw0KDQpoMS50aXRsZSB7ICAvKiBUaXRsZSAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgb2YgdGhlIHJlcG9ydCB0aXRsZSAqLw0KICBmb250LXNpemU6IDI0cHg7DQogIGZvbnQtd2VpZ2h0OiBib2xkOw0KICBjb2xvcjogbmF2eTsNCiAgdGV4dC1hbGlnbjogY2VudGVyOw0KICBmb250LWZhbWlseTogIkdpbGwgU2FucyIsIHNhbnMtc2VyaWY7DQp9DQpoNC5hdXRob3IgeyAvKiBIZWFkZXIgNCAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgZm9yIGF1dGhvcnMgICovDQogIGZvbnQtc2l6ZTogMThweDsNCiAgZm9udC1mYW1pbHk6IHN5c3RlbS11aTsNCiAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogIGNvbG9yOiBuYXZ5Ow0KICB0ZXh0LWFsaWduOiBjZW50ZXI7DQp9DQpoNC5kYXRlIHsgLyogSGVhZGVyIDQgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciB0aGUgZGF0ZSAgKi8NCiAgZm9udC1zaXplOiAxOHB4Ow0KICBmb250LWZhbWlseTogc3lzdGVtLXVpOw0KICBjb2xvcjogRGFya0JsdWU7DQogIHRleHQtYWxpZ246IGNlbnRlcjsNCiAgZm9udC13ZWlnaHQ6IGJvbGQ7DQp9DQpoMSB7IC8qIEhlYWRlciAxIC0gZm9udCBzcGVjaWZpY2F0aW9ucyBmb3IgbGV2ZWwgMSBzZWN0aW9uIHRpdGxlICAqLw0KICAgIGZvbnQtc2l6ZTogMjBweDsNCiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgICBjb2xvcjogbmF2eTsNCiAgICB0ZXh0LWFsaWduOiBjZW50ZXI7DQogICAgZm9udC13ZWlnaHQ6IGJvbGQ7DQp9DQpoMiB7IC8qIEhlYWRlciAyIC0gZm9udCBzcGVjaWZpY2F0aW9ucyBmb3IgbGV2ZWwgMiBzZWN0aW9uIHRpdGxlICovDQogICAgZm9udC1zaXplOiAxOHB4Ow0KICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOw0KICAgIGNvbG9yOiBuYXZ5Ow0KICAgIHRleHQtYWxpZ246IGxlZnQ7DQogICAgZm9udC13ZWlnaHQ6IGJvbGQ7DQp9DQoNCmgzIHsgLyogSGVhZGVyIDMgLSBmb250IHNwZWNpZmljYXRpb25zIG9mIGxldmVsIDMgc2VjdGlvbiB0aXRsZSAgKi8NCiAgICBmb250LXNpemU6IDE2cHg7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IG5hdnk7DQogICAgdGV4dC1hbGlnbjogbGVmdDsNCn0NCg0KaDQgeyAvKiBIZWFkZXIgNCAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgb2YgbGV2ZWwgNCBzZWN0aW9uIHRpdGxlICAqLw0KICAgIGZvbnQtc2l6ZTogMTRweDsNCiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgICBjb2xvcjogZGFya3JlZDsNCiAgICB0ZXh0LWFsaWduOiBsZWZ0Ow0KfQ0KDQpib2R5IHsgYmFja2dyb3VuZC1jb2xvcjp3aGl0ZTsgfQ0KDQouaGlnaGxpZ2h0bWUgeyBiYWNrZ3JvdW5kLWNvbG9yOnllbGxvdzsgfQ0KDQpwIHsgYmFja2dyb3VuZC1jb2xvcjp3aGl0ZTsgfQ0KDQo8L3N0eWxlPg0KYGBgDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0KIyBjb2RlIGNodW5rIHNwZWNpZmllcyB3aGV0aGVyIHRoZSBSIGNvZGUsIHdhcm5pbmdzLCBhbmQgb3V0cHV0IA0KIyB3aWxsIGJlIGluY2x1ZGVkIGluIHRoZSBvdXRwdXQgZmlsZXMuDQppZiAoIXJlcXVpcmUoImtuaXRyIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoImtuaXRyIikNCiAgIGxpYnJhcnkoa25pdHIpDQp9DQppZiAoIXJlcXVpcmUoInRpZHl2ZXJzZSIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dmVyc2UiKQ0KbGlicmFyeSh0aWR5dmVyc2UpDQp9DQppZiAoIXJlcXVpcmUoInJlYWR4bCIpKSB7ICMgU1ZNIG1ldGhvZG9sb2d5DQogICBpbnN0YWxsLnBhY2thZ2VzKCJyZWFkeGwiKQ0KbGlicmFyeShyZWFkeGwpDQp9DQppZiAoIXJlcXVpcmUoImdncGxvdDIiKSkgeyAjIFNWTSBtZXRob2RvbG9neQ0KICAgaW5zdGFsbC5wYWNrYWdlcygiZ2dwbG90MiIpDQpsaWJyYXJ5KGdncGxvdDIpDQp9DQppZiAoIXJlcXVpcmUoIklTTFIiKSkgeyAjIGNvbnRhaW5zIGV4YW1wbGUgZGF0YSBzZXQgIktoYW4iDQogICBpbnN0YWxsLnBhY2thZ2VzKCJJU0xSIikNCmxpYnJhcnkoSVNMUikNCn0NCmlmICghcmVxdWlyZSgiTUFTU0V4dHJhIikpIHsgIyBjb250YWlucyBleGFtcGxlIGRhdGEgc2V0ICJLaGFuIg0KICAgaW5zdGFsbC5wYWNrYWdlcygiTUFTU0V4dHJhIikNCmxpYnJhcnkoSVNMUikNCn0NCmlmICghcmVxdWlyZSgibWlzc01ldGhvZHMiKSkgeyAjIGN1c3RvbWl6ZWQgY29sb3Jpbmcgb2YgcGxvdHMNCiAgIGluc3RhbGwucGFja2FnZXMoIm1pc3NNZXRob2RzIikNCmxpYnJhcnkoUkNvbG9yQnJld2VyKQ0KfQ0KaWYgKCFyZXF1aXJlKCJIbWlzYyIpKSB7ICMgY3VzdG9taXplZCBjb2xvcmluZyBvZiBwbG90cw0KICAgaW5zdGFsbC5wYWNrYWdlcygiSG1pc2MiKQ0KbGlicmFyeShSQ29sb3JCcmV3ZXIpDQp9DQppZiAoIXJlcXVpcmUoImdsbW5ldCIpKSB7ICMgY3VzdG9taXplZCBjb2xvcmluZyBvZiBwbG90cw0KICAgaW5zdGFsbC5wYWNrYWdlcygiZ2xtbmV0IikNCmxpYnJhcnkoUkNvbG9yQnJld2VyKQ0KfQ0KaWYgKCFyZXF1aXJlKCJHR2FsbHkiKSkgeyAjIGN1c3RvbWl6ZWQgY29sb3Jpbmcgb2YgcGxvdHMNCiAgIGluc3RhbGwucGFja2FnZXMoIkdHYWxseSIpDQpsaWJyYXJ5KFJDb2xvckJyZXdlcikNCn0NCmlmICghcmVxdWlyZSgibWljZSIpKSB7ICMgY3VzdG9taXplZCBjb2xvcmluZyBvZiBwbG90cw0KICAgaW5zdGFsbC5wYWNrYWdlcygibWljZSIpDQpsaWJyYXJ5KFJDb2xvckJyZXdlcikNCn0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgICAgICAgIyBpbmNsdWRlIGNvZGUgY2h1bmsgaW4gdGhlIG91dHB1dCBmaWxlDQogICAgICAgICAgICAgICAgICAgICAgd2FybmluZyA9IEZBTFNFLCAgICMgc29tZXRpbWVzLCB5b3UgY29kZSBtYXkgcHJvZHVjZSB3YXJuaW5nIG1lc3NhZ2VzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHlvdSBjYW4gY2hvb3NlIHRvIGluY2x1ZGUgdGhlIHdhcm5pbmcgbWVzc2FnZXMgaW4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB0aGUgb3V0cHV0IGZpbGUuIA0KICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdHMgPSBUUlVFLCAgICAjIHlvdSBjYW4gYWxzbyBkZWNpZGUgd2hldGhlciB0byBpbmNsdWRlIHRoZSBvdXRwdXQNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBpbiB0aGUgb3V0cHV0IGZpbGUuDQogICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZSA9IEZBTFNFLA0KICAgICAgICAgICAgICAgICAgICAgIGNvbW1lbnQgPSBOQQ0KICAgICAgICAgICAgICAgICAgICAgICkgIA0KYGBgDQoNCiMgUmVhZCBpbiBCYW5rIExvYW4gRGVmYXVsdCBEYXRhc2V0DQoNClJlYWQgaW4gZGF0YXNldC4NCg0KYGBge3J9DQpiYW5rIDwtIHJlYWQuY3N2KCJCYW5rTG9hbkRlZmF1bHREYXRhc2V0LmNzdiIpDQpiYW5rMiA8LSBiYW5rDQpiYW5rJENhcl9sb2FuIDwtIGFzLmZhY3RvcihiYW5rJENhcl9sb2FuKQ0KIGJhbmskUGVyc29uYWxfbG9hbiA8LSBhcy5mYWN0b3IoYmFuayRQZXJzb25hbF9sb2FuKQ0KIGJhbmskSG9tZV9sb2FuIDwtIGFzLmZhY3RvcihiYW5rJEhvbWVfbG9hbikNCiBiYW5rJEVkdWNhdGlvbl9sb2FuIDwtIGFzLmZhY3RvcihiYW5rJEVkdWNhdGlvbl9sb2FuKQ0KIGJhbmskR2VuZGVyIDwtIGFzLmZhY3RvcihiYW5rJEdlbmRlcikNCiBiYW5rJE1hcml0YWxfc3RhdHVzIDwtIGFzLmZhY3RvcihiYW5rJE1hcml0YWxfc3RhdHVzKQ0KIGJhbmskRW1wX3N0YXR1cyA8LSBhcy5mYWN0b3IoYmFuayRFbXBfc3RhdHVzKQ0KIGJhbmskRGVmYXVsdCA8LSBhcy5mYWN0b3IoYmFuayREZWZhdWx0KQ0KYGBgDQoNCiMjIENyZWF0ZSBNaXNzaW5nIFZhbHVlcw0KDQpUaGUgb3JpZ2luYWwgZGF0YXNldCBkb2VzIG5vdCBoYXZlIGFueSBtaXNzaW5nIHZhbHVlcyBzbyBJIG5lZWRlZCB0byBtYW51YWxseSBjcmVhdGUgdGhlbS4NCg0KYGBge3J9DQpnZW5kZXIubWlzc2luZy5pZCA8LSBzYW1wbGUoMToxMDAwLCAyMCAsIHJlcGxhY2UgPSBGQUxTRSkNCk1hcml0YWwubWlzc2luZy5pZCA8LSBzYW1wbGUoMToxMDAwLCAyMCwgcmVwbGFjZSA9IEZBTFNFKQ0KZW1wLnN0YXR1cy5taXNzaW5nLmlkIDwtIHNhbXBsZSgxOjEwMDAsIDIwLCByZXBsYWNlID0gRkFMU0UpDQpjcmVkaXQubWlzc2luZy5pZCA8LSBzYW1wbGUoMToxMDAwLCAyMCwgcmVwbGFjZSA9IEZBTFNFKQ0KYW1vdW50Lm1pc3NpbmcuaWQgPC0gc2FtcGxlKDE6MTAwMCwgMjAsIHJlcGxhY2UgPSBGQUxTRSkNCmVtcC5kdXJhdGlvbi5taXNzaW5nLmlkIDwtIHNhbXBsZSgxOjEwMDAsIDIwLCByZXBsYWNlID0gRkFMU0UpDQpjaGVjay5hbXQubWlzc2luZy5pZCA8LSBzYW1wbGUoMToxMDAwLCAyMCwgcmVwbGFjZSA9IEZBTFNFKQ0KYWdlLm1pc3NpbmcuaWQgPC0gc2FtcGxlKDE6MTAwMCwgMjAsIHJlcGxhY2UgPSBGQUxTRSkNCg0KYmFuayRHZW5kZXJbZ2VuZGVyLm1pc3NpbmcuaWRdIDwtIE5BDQpiYW5rJE1hcml0YWxfc3RhdHVzW01hcml0YWwubWlzc2luZy5pZF0gPC0gTkENCmJhbmskRW1wX3N0YXR1c1tlbXAuc3RhdHVzLm1pc3NpbmcuaWRdIDwtIE5BDQpiYW5rJENyZWRpdF9zY29yZVtjcmVkaXQubWlzc2luZy5pZF0gPC0gTkENCmJhbmskQW1vdW50W2Ftb3VudC5taXNzaW5nLmlkXSA8LSBOQQ0KYmFuayRFbXBfZHVyYXRpb25bZW1wLmR1cmF0aW9uLm1pc3NpbmcuaWRdIDwtIE5BDQpiYW5rJENoZWNraW5nX2Ftb3VudFtjaGVjay5hbXQubWlzc2luZy5pZF0gPC0gTkENCmJhbmskQWdlW2FnZS5taXNzaW5nLmlkXSA8LSBOQQ0KYGBgDQoNCiMgSW50cm9kdWN0aW9uDQoNCk1pc3NpbmcgdmFsdWVzIGFyZSBhIG5vcm1hbCBwYXJ0IG9mIGRhdGEgYW5hbHlzaXMuIFRvIHJlc29sdmUgdGhpcywgd2UgY2FuIHVzZSBpbXB1dGF0aW9uLiBGb3IgbnVtZXJpY2FsIGZlYXR1cmVzLCB3ZSBjYW4gaW1wdXRlIHVzaW5nIHRoZSBtZWFuIG9yIG1lZGlhbi4gRm9yIGNhdGVnb3JpY2FsIHZhcmlhYmxlcywgd2UgY2FuIGltcHV0ZSB0aGUgbW9kZS4gV2UgY2FuIGFsc28gdXNlIHJlZ3Jlc3Npb24gbWV0aG9kcy4gDQoNCkFmdGVyIGltcHV0YXRpb24sIHdlIGRvIGZlYXR1cmUgZW5naW5lZXJpbmcuIFdlIGNhbiBkbyBmZWF0dXJlIHRyYW5zZm9ybWF0aW9uLCBmZWF0dXJlIHNlbGVjdGlvbiwgYW5kIGZlYXR1cmUgY3JlYXRpb24uDQoNCiMgUmVwbGFjZW1lbnQgSW1wdXRhdGlvbiBmb3IgQ2F0ZWdvcmljYWwgRmVhdHVyZXMNCg0KR2VuZGVyLCBtYXJpdGFsIHN0YXR1cywgYW5kIGVtcGxveW1lbnQgc3RhdHVzIGhhdmUgbWlzc2luZyB2YWx1ZXMuIFRoZXNlIHZhbHVlcyBjYW4gYmUgcmVwbGFjZWQgd2l0aCB0aGUgbW9kZS4NCg0KYGBge3J9DQptb2RlX3ZhbHVlX2dlbmRlciA8LSAgd2hpY2gubWF4KHRhYmxlKGJhbmskR2VuZGVyKSkgJT4lIG5hbWVzKCkNCm1vZGVfdmFsdWVfbWFyaXRhbF9zdGF0dXMgPC0gd2hpY2gubWF4KHRhYmxlKGJhbmskTWFyaXRhbF9zdGF0dXMpKSAlPiUgbmFtZXMoKQ0KbW9kZV92YWx1ZV9lbXBfc3RhdHVzIDwtIHdoaWNoLm1heCh0YWJsZShiYW5rJEVtcF9zdGF0dXMpKSAlPiUgbmFtZXMoKQ0KDQoNCmJhbmskR2VuZGVyW2lzLm5hKGJhbmskR2VuZGVyKV0gPC0gbW9kZV92YWx1ZV9nZW5kZXINCmJhbmskTWFyaXRhbF9zdGF0dXNbaXMubmEoYmFuayRNYXJpdGFsX3N0YXR1cyldIDwtIG1vZGVfdmFsdWVfbWFyaXRhbF9zdGF0dXMNCmJhbmskRW1wX3N0YXR1c1tpcy5uYShiYW5rJEVtcF9zdGF0dXMpXSA8LSBtb2RlX3ZhbHVlX2VtcF9zdGF0dXMNCmBgYA0KDQoNCiMgUmVncmVzc2lvbi1iYXNlZCBJbXB1dGF0aW9uIGZvciBOdW1lcmljYWwgRmVhdHVyZXMNCg0KUmFuZG9tIHJlZ3Jlc3Npb24gaW1wdXRhdGlvbiBpcyB1c2VkIGluIHRoaXMgc2VjdGlvbiBmb3IgYWxsIG51bWVyaWNhbCB2YXJpYWJsZXMuIFJlZ3Jlc3Npb24gbW9kZWxzIGFyZSBjcmVhdGVkIHVzaW5nIGZlYXR1cmVzIHdpdGggbWlzc2luZyB2YWx1ZXMgYXMgdGhlIGRlcGVuZGVudCB2YXJpYWJsZSBhbmQgYW55IHZhcmlhYmxlIHdpdGhvdXQgbWlzc2luZyB2YWx1ZXMgYXMgYSBwcmVkaWN0b3IuDQoNCmBgYHtyfQ0KYmFuazEgPC0gYmFua1sgLCBjKCdDaGVja2luZ19hbW91bnQnLCAnVGVybScsJ0NyZWRpdF9zY29yZScsICdBbW91bnQnLCdTYXZpbmdfYW1vdW50JywgJ0VtcF9kdXJhdGlvbicsJ0FnZScsICdOb19vZl9jcmVkaXRfYWNjJyldDQpgYGANCg0KYGBge3J9DQpnZ3BhaXJzKGJhbmsxKQ0KYGBgDQoNCmBgYHtyfQ0KcHJlZC5tZGwxID0gbG0oQ2hlY2tpbmdfYW1vdW50IH4gU2F2aW5nX2Ftb3VudCwgZGF0YSA9IGJhbmspDQpuZXdkYXRhMSA9IGJhbmtbaXMubmEoYmFuayRDaGVja2luZ19hbW91bnQpLF0NCnByZWQueTEgPSBwcmVkaWN0KHByZWQubWRsMSwgbmV3ZGF0YTEgPSBuZXdkYXRhMSkNCm0xID0gc3VtKGlzLm5hKGJhbmskQ2hlY2tpbmdfYW1vdW50KSkgICAgICAjIHRvdGFsIG51bWJlciBvZiBtaXNzaW5nIHZhbHVlcw0KcHJlZC5yZXNpZDEgPSByZXNpZChwcmVkLm1kbDEpICAgICMgcmVzaWR1YWwNCnByZWQueXJhbmQxID0gcHJlZC55MSArIHNhbXBsZShwcmVkLnJlc2lkMSwgbTEsIHJlcGxhY2UgPSBUUlVFKQ0KYmFuayRDaGVja2luZ19hbW91bnRbaXMubmEoYmFuayRDaGVja2luZ19hbW91bnQpXSA9IHByZWQueXJhbmQxDQpgYGANCg0KYGBge3J9DQpwcmVkLm1kbDIgPSBsbShDcmVkaXRfc2NvcmUgfiBTYXZpbmdfYW1vdW50LCBkYXRhID0gYmFuaykNCm5ld2RhdGEyID0gYmFua1tpcy5uYShiYW5rJENyZWRpdF9zY29yZSksXQ0KcHJlZC55MiA9IHByZWRpY3QocHJlZC5tZGwyLCBuZXdkYXRhMiA9IG5ld2RhdGEyKQ0KbTIgPSBzdW0oaXMubmEoYmFuayRDcmVkaXRfc2NvcmUpKSAgICAgICMgdG90YWwgbnVtYmVyIG9mIG1pc3NpbmcgdmFsdWVzDQpwcmVkLnJlc2lkMiA9IHJlc2lkKHByZWQubWRsMikgICAgIyByZXNpZHVhbA0KcHJlZC55cmFuZDIgPSBwcmVkLnkyICsgc2FtcGxlKHByZWQucmVzaWQyLCBtMiwgcmVwbGFjZSA9IFRSVUUpDQpiYW5rJENyZWRpdF9zY29yZVtpcy5uYShiYW5rJENyZWRpdF9zY29yZSldID0gcHJlZC55cmFuZDINCmBgYA0KDQpgYGB7cn0NCnByZWQubWRsMyA9IGxtKEFtb3VudCB+IENoZWNraW5nX2Ftb3VudCwgZGF0YSA9IGJhbmspDQpuZXdkYXRhMyA9IGJhbmtbaXMubmEoYmFuayRBbW91bnQpLF0NCnByZWQueTMgPSBwcmVkaWN0KHByZWQubWRsMywgbmV3ZGF0YTMgPSBuZXdkYXRhMykNCm0zID0gc3VtKGlzLm5hKGJhbmskQW1vdW50KSkgICAgICAjIHRvdGFsIG51bWJlciBvZiBtaXNzaW5nIHZhbHVlcw0KcHJlZC5yZXNpZDMgPSByZXNpZChwcmVkLm1kbDMpICAgICMgcmVzaWR1YWwNCnByZWQueXJhbmQzID0gcHJlZC55MyArIHNhbXBsZShwcmVkLnJlc2lkMywgbTMsIHJlcGxhY2UgPSBUUlVFKQ0KYmFuayRBbW91bnRbaXMubmEoYmFuayRBbW91bnQpXSA9IHByZWQueXJhbmQzDQpgYGANCg0KYGBge3J9DQpwcmVkLm1kbDQgPSBsbShFbXBfZHVyYXRpb24gfiBOb19vZl9jcmVkaXRfYWNjLCBkYXRhID0gYmFuaykNCm5ld2RhdGE0ID0gYmFua1tpcy5uYShiYW5rJEVtcF9kdXJhdGlvbiksXQ0KcHJlZC55NCA9IHByZWRpY3QocHJlZC5tZGw0LCBuZXdkYXRhNCA9IG5ld2RhdGE0KQ0KbTQgPSBzdW0oaXMubmEoYmFuayRFbXBfZHVyYXRpb24pKSAgICAgICMgdG90YWwgbnVtYmVyIG9mIG1pc3NpbmcgdmFsdWVzDQpwcmVkLnJlc2lkNCA9IHJlc2lkKHByZWQubWRsNCkgICAgIyByZXNpZHVhbA0KcHJlZC55cmFuZDQgPSBwcmVkLnk0ICsgc2FtcGxlKHByZWQucmVzaWQ0LCBtNCwgcmVwbGFjZSA9IFRSVUUpDQpiYW5rJEVtcF9kdXJhdGlvbltpcy5uYShiYW5rJEVtcF9kdXJhdGlvbildID0gcHJlZC55cmFuZDQNCmBgYA0KDQoNCmBgYHtyfQ0KcHJlZC5tZGw1ID0gbG0oQWdlIH4gU2F2aW5nX2Ftb3VudCwgZGF0YSA9IGJhbmspDQpuZXdkYXRhNSA9IGJhbmtbaXMubmEoYmFuazEkQWdlKSxdDQpwcmVkLnk1ID0gcHJlZGljdChwcmVkLm1kbDUsIG5ld2RhdGE1ID0gbmV3ZGF0YTUpDQptNSA9IHN1bShpcy5uYShiYW5rJEFnZSkpICAgICAgIyB0b3RhbCBudW1iZXIgb2YgbWlzc2luZyB2YWx1ZXMNCnByZWQucmVzaWQ1ID0gcmVzaWQocHJlZC5tZGw1KSAgICAjIHJlc2lkdWFsDQpwcmVkLnlyYW5kNSA9IHByZWQueTUgKyBzYW1wbGUocHJlZC5yZXNpZDUsIG01LCByZXBsYWNlID0gVFJVRSkNCmJhbmskQWdlW2lzLm5hKGJhbmskQWdlKV0gPSBwcmVkLnlyYW5kNQ0KYGBgDQoNCg0KDQojIE11bHRpcGxlIEltcHV0YXRpb24NCg0KV2UgdXNlIHRoZSBNSUNFIGltcHV0YXRpb24gbWV0aG9kIGluIHRoaXMgc2VjdGlvbi4gV2UgaW1wdXRlIG11bHRpcGxlIGRhdGEgc2V0cyBhbmQgY29tYmluZSBhbGwgdGhlIHJlc3VsdHMuIFRoZSBwb29sZWQgc3RhbmRhcmQgZXJyb3IgaXMgMTkuODc3MjENCg0KYGBge3J9DQojYmFuazIgPC0gYmFuaw0KYGBgDQoNCg0KYGBge3J9DQppbml0IDwtIG1pY2UoYmFuazIsIG1heGl0ID0gMCkNCmluaXQkbWV0aG9kIA0KYGBgDQoNCmBgYHtyfQ0KaW1wIDwtIG1pY2UoYmFuazIsIG1ldGhvZCA9IGMoIiIsInBtbSIsICIiLCAicG1tIiwgICJsb2dyZWciLCAibG9ncmVnIiwgIiIsICIiLCAiIiwgIiIsICJsb2dyZWciLCAicG1tIiwgIiIsICJwbW0iLCAicG1tIiwgIiIpLCANCiAgICAgICAgICAgICAgICAgbWF4aXQgPSAxMCwgIA0KICAgICAgICAgICAgICAgICBtID0gMSwgICAgICAgDQogICAgICAgICAgICAgICAgIHByaW50PUYpICAgICANCg0KY29tcGxldGUoaW1wLCBhY3Rpb24gPSAxTClbMToxMCxdDQpgYGANCg0KYGBge3J9DQppbXAgPC0gbWljZShiYW5rMiwgbWV0aG9kID0gInBtbSIsIA0KICAgICAgICAgICAgICAgICAgbWF4aXQgPSAxMCwgIA0KICAgICAgICAgICAgICAgICAgbSA9IDIsICAgICAgIA0KICAgICAgICAgICAgICAgICAgc2VlZCA9IDEyMywgIA0KICAgICAgICAgICAgICAgICAgcHJpbnQ9RikgICAgIA0KY29tcGxldGUoaW1wLCBhY3Rpb24gPSAiYnJvYWQiKVsxOjEwLF0NCmBgYA0KDQpgYGB7cn0NCmltcDUgPC0gbWljZShiYW5rMiwgbWV0aG9kID0gInBtbSIsIG0gPSA1LCBtYXhpdCA9IDEwLCBzZWVkID0gMTIzLCBwcmludD1GKQ0KcGxvdChpbXA1KQ0KYGBgDQpgYGB7cn0NCm1vZGVsNSA8LSB3aXRoKGltcDUsIGdsbShEZWZhdWx0IH4gQ2hlY2tpbmdfYW1vdW50ICsgVGVybSArIENyZWRpdF9zY29yZSArIEdlbmRlciArIE1hcml0YWxfc3RhdHVzICsgQ2FyX2xvYW4gKyBQZXJzb25hbF9sb2FuICsgSG9tZV9sb2FuICsgRWR1Y2F0aW9uX2xvYW4gKyBFbXBfc3RhdHVzICsgQW1vdW50ICsgU2F2aW5nX2Ftb3VudCArIEVtcF9kdXJhdGlvbisgQWdlICsgTm9fb2ZfY3JlZGl0X2FjYyxmYW1pbHk9J2Jpbm9taWFsJykpIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIA0Kc3VtbWFyeS5zdGF0cyA9IHN1bW1hcnkobW9kZWw1KSAgICAgICAgICAgICAgIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgDQpzdW1tYXJ5LnN0YXRzIA0KYGBgDQoNCmBgYHtyfQ0Kc3VtbWFyeShwb29sKG1vZGVsNSkpDQpgYGANCg0KYGBge3J9DQpiZXRhID0gc3VtbWFyeS5zdGF0cyRlc3RpbWF0ZVtzZXEoMSwxNSxieT0zKV0gIA0KYmV0YS52YXIgPSAoc3VtbWFyeS5zdGF0cyRzdGQuZXJyb3Jbc2VxKDEsMTUsYnk9MyldKV4yIA0KUSA9IG1lYW4oYmV0YSkNClUgPSBtZWFuKGJldGEudmFyKQ0KQiA9IHZhcihiZXRhKQ0KVCA9IFUgKyAoNi81KSpCDQpwb29sLnNlID0gc3FydChUKQ0KY2JpbmQocG9vbC5zZS5pbnRlcmNlcHQgPSBwb29sLnNlKQ0KYGBgDQoNCg0KDQojIEZlYXR1cmUgVHJhbnNmb3JtaW5nDQoNClRoZSBvbmx5IHZhcmlhYmxlIHRoYXQgaXMgc2tld2VkIGlzIE5vX29mX2NyZWRpdF9hY2Mgc28gd2UgdXNlIGEgbG9nIHRyYW5zZm9ybWF0aW9uLg0KDQpgYGB7cn0NCmJveGNveF9yZXN1bHQgPC0gYm94Y294KGxtKGJhbmskTm9fb2ZfY3JlZGl0X2FjYyB+IDEpLCBsYW1iZGEgPSBzZXEoMCwgMSwgYnkgPSAwLjEpKQ0KdGl0bGUoIkJveC1Db3ggVHJhbnNmb3JtYXRpb24iKQ0KYGBgDQoNCmBgYHtyfQ0Kb3B0aW1hbF9sYW1iZGEgPC0gYm94Y294X3Jlc3VsdCR4W3doaWNoLm1heChib3hjb3hfcmVzdWx0JHkpXQ0KYGBgDQoNCmBgYHtyfQ0KdHJhbnNmb3JtZWRfZGF0YSA8LSBpZiAob3B0aW1hbF9sYW1iZGEgPT0gMCkgew0KICBsb2coYmFuayROb19vZl9jcmVkaXRfYWNjKQ0KfSBlbHNlIHsNCiAgKGJhbmskTm9fb2ZfY3JlZGl0X2FjY15vcHRpbWFsX2xhbWJkYSAtIDEpIC8gb3B0aW1hbF9sYW1iZGENCn0NCg0KcG9vbGVkRGF0YSA8LSBkYXRhLmZyYW1lKE9yaWdpbmFsRGF0YT1iYW5rJE5vX29mX2NyZWRpdF9hY2MsIFRyYW5zZm9ybWVkRGF0YT10cmFuc2Zvcm1lZF9kYXRhKQ0KYGBgDQoNCg0KV2UgdXNlIHN0YW5kYXJkaXphdGlvbiBmb3IgYWxsIHRoZSBudW1lcmljYWwgdmFyaWFibGVzIHNpbmNlIHRoZXNlIHZhcmlhYmxlcyBoYXZlIGRpZmZlcmVudCB1bml0cy4gVGhpcyB3aWxsIGhlbHAgd2l0aCBvdXIgbW9kZWxpbmcgbGF0ZXIgb24uDQoNCmBgYHtyfQ0KDQoNCnN0YW5kYXJkaXplIDwtIGZ1bmN0aW9uKHgpIHsNCiAgcmV0dXJuKCh4IC0gbWVhbih4KSkgLyBzZCh4KSkNCn0NCg0KYmFuayRDaGVja2luZ19hbW91bnRfc3RhbmRhcmRpemVkIDwtIHN0YW5kYXJkaXplKGJhbmskQ2hlY2tpbmdfYW1vdW50KQ0KYmFuayR0ZXJtX3N0YW5kYXJkaXplZCA8LSBzdGFuZGFyZGl6ZShiYW5rJFRlcm0pDQpiYW5rJENyZWRpdF9zY29yZV9zdGFuZGFyZGl6ZWQgPC0gc3RhbmRhcmRpemUoYmFuayRDcmVkaXRfc2NvcmUpDQpiYW5rJGFtb3VudF9zdGFuZGFyZGl6ZWQgPC0gc3RhbmRhcmRpemUoYmFuayRBbW91bnQpDQpiYW5rJFNhdmluZ19hbW91bnRfc3RhbmRhcmRpemVkIDwtIHN0YW5kYXJkaXplKGJhbmskU2F2aW5nX2Ftb3VudCkNCmJhbmskRW1wX2R1cmF0aW9uX3N0YW5kYXJkaXplZCA8LSBzdGFuZGFyZGl6ZShiYW5rJEVtcF9kdXJhdGlvbikNCmJhbmskQWdlX3N0YW5kYXJkaXplZCA8LSBzdGFuZGFyZGl6ZShiYW5rJEFnZSkNCmJhbmskTm9fb2ZfY3JlZGl0X2FjY19zdGFuZGFyZGl6ZWQgPC0gc3RhbmRhcmRpemUoYmFuayROb19vZl9jcmVkaXRfYWNjKQ0KDQpgYGANCg0KIyBGZWF0dXJlIFNlbGVjdGlvbg0KDQpUbyBzZWxlY3QgdGhlIG1vc3QgcmVsZXZhbnQgZmVhdHVyZXMgZm9yIG91ciBtb2RlbCwgd2UgdXNlIGxhc3NvIHJlZ3VsYXJpemF0aW9uLiBVc2luZyBhbGwgdGhlIHN0YW5kYXJkaXplZCBudW1lcmljYWwgdmFyaWFibGVzLCB3ZSBzZWUgdGhhdCBjaGVja2luZyBhbW91bnQsIHRlcm0sIGNyZWRpdCBzY29yZSwgYW1vdW50LCBzYXZpbmcgYW1vdW50LCBhZ2UsIGFuZCBubyBvZiBjcmVkaXQgYWNjb3VudHMgYXJlIGltcG9ydGFudCBpbiBkZXRlcm1pbmluZyB0aGUgRGVmYXVsdCAodGFyZ2V0IHZhcmlhYmxlKS4NCg0KYGBge3J9DQp4IDwtIGFzLm1hdHJpeChiYW5rWywgYygnQ2hlY2tpbmdfYW1vdW50X3N0YW5kYXJkaXplZCcsICd0ZXJtX3N0YW5kYXJkaXplZCcsICdDcmVkaXRfc2NvcmVfc3RhbmRhcmRpemVkJywgJ2Ftb3VudF9zdGFuZGFyZGl6ZWQnLCAnU2F2aW5nX2Ftb3VudF9zdGFuZGFyZGl6ZWQnLCAnRW1wX2R1cmF0aW9uX3N0YW5kYXJkaXplZCcsJ0FnZV9zdGFuZGFyZGl6ZWQnLCdOb19vZl9jcmVkaXRfYWNjX3N0YW5kYXJkaXplZCcpXSkNCnkgPC0gYmFuayREZWZhdWx0DQoNCiMgZGF0YSREZWZhdWx0IDwtIGFzLm51bWVyaWMoZGF0YSREZWZhdWx0KQ0KIyBkYXRhJEdlbmRlciA8LSBhcy5udW1lcmljKGRhdGEkR2VuZGVyKQ0KIyBkYXRhJE1hcml0YWxfc3RhdHVzIDwtIGFzLm51bWVyaWMoZGF0YSRNYXJpdGFsX3N0YXR1cykNCiMgZGF0YSRDYXJfbG9hbiA8LSBhcy5udW1lcmljKGRhdGEkQ2FyX2xvYW4pDQojIGRhdGEkUGVyc29uYWxfbG9hbiA8LSBhcy5udW1lcmljKGRhdGEkUGVyc29uYWxfbG9hbikNCiMgZGF0YSRIb21lX2xvYW4gPC0gYXMubnVtZXJpYyhkYXRhJEhvbWVfbG9hbikNCiMgZGF0YSRFZHVjYXRpb25fbG9hbiA8LSBhcy5udW1lcmljKGRhdGEkRWR1Y2F0aW9uX2xvYW4pDQojIGRhdGEkRW1wX3N0YXR1cyA8LSBhcy5udW1lcmljKGRhdGEkRW1wX3N0YXR1cykNCg0KDQpsYXNzb19tb2RlbCA8LSBjdi5nbG1uZXQoeCwgeSwgYWxwaGEgPSAxLCBmYW1pbHk9J2Jpbm9taWFsJykNCmJlc3RfbGFtYmRhIDwtIGxhc3NvX21vZGVsJGxhbWJkYS5taW4NCnNlbGVjdGVkX2ZlYXR1cmVzIDwtIHJvd25hbWVzKGNvZWYobGFzc29fbW9kZWwsIHMgPSBiZXN0X2xhbWJkYSkpW2NvZWYobGFzc29fbW9kZWwsIHMgPSBiZXN0X2xhbWJkYSlbLDFdICE9IDBdDQpwcmludChzZWxlY3RlZF9mZWF0dXJlcykNCg0KYmVzdF9tb2RlbCA8LSBnbG1uZXQoeCwgeSwgYWxwaGEgPSAxLCBsYW1iZGEgPSBiZXN0X2xhbWJkYSwgZmFtaWx5PSdiaW5vbWlhbCcpDQpjb2VmKGJlc3RfbW9kZWwpDQpgYGANCg0KDQojIEZlYXR1cmUgQ3JlYXRpb24NCg0KRmVhdHVyZSBjcmVhdGlvbiBpcyBub3QgbmVjZXNzYXJ5IGZvciB0aGlzIGRhdGFzZXQuIEZvciBzb21lIHZhcmlhYmxlcyBsaWtlIEFnZSwgaXQgb25seSByYW5nZXMgZnJvbSAxOC00MiB5cnMgb2xkIHNvIGl0IHdvdWxkIGJlIGRpZmZpY3VsdCB0byBncm91cCBhbGwgaW50byAneW91bmcnIG9yICdvbGQnLiBGb3Igb3RoZXIgdmFyaWFibGVzLCBpdCB3b3VsZCBiZSBkaWZmaWN1bHQgdG8gZ3JvdXAgdGhlbSBpbnRvIGRpZmZlcmVudCBiaW5zIGJlY2F1c2UgdGhleSBhcmUgbWVhc3VyZWQgaW4gdW5pdHMgc3VjaCBhcyBtb250aHMgb3IgbW9uZXkuDQoNCg0KDQoNCg==