club <- read.csv("C:/Users/Public/LC_Week9_Data.csv")
head(club)
summary(club)

a) Comb_Risk_One: Create a binary column by combining categories A and B (Low

Risk) into one category and all the remaining categories in another (High Risk). ## b) Comb_Risk_Two: Create a binary column by combining categories A, B and C (Low Risk) into one category and all the remaining categories in another (High Risk).

library(dplyr)
club<-club %>%
 mutate(Comb_Risk_One = ifelse(grade=='A' | grade=='B', 0, 1))
head(club)
club<-club%>%mutate(Comb_Risk_Two = ifelse(grade=='A' | grade=='B' |grade=='C',
0, 1))

break the file into two files filtering out data for 2012, 13, and 14 in one

file and 2015, 16 and 17 in another file.

club_12_13_14<- subset(club,club$issue_Year %in% c(2012,2013,2014))
club_15_16_17<- subset(club,club$issue_Year %in% c(2015,2016,2017))

##Check for null values.Impute the missing values.

summary(club_12_13_14)
summary(club_15_16_17)
club_12_13_14$mths_since_last_delinq[is.na(club_12_13_14$mths_since_last_delinq)
]=
 mean(club_12_13_14$mths_since_last_delinq,na.rm=T)
club_15_16_17$mths_since_last_delinq[is.na(club_15_16_17$mths_since_last_delinq)
]=
 mean(club_15_16_17$mths_since_last_delinq,na.rm=T)

predict Low and High-risk categories (for the two new response variables)

using various modeling techniques like Naïve Bayes’, KNN, Logistic Regression, and CART model

library(dplyr)
club_12_13_14 <- club_12_13_14 %>% mutate(int_rate=as.numeric(gsub("%", "",
int_rate)))
club_12_13_14<-club_12_13_14[complete.cases(club_12_13_14), ]

BASIC EDA

Relation between annual income and total payment.

library(ggplot2)
ggplot(club_12_13_14) + geom_point(aes(x = annual_inc, y = total_pymnt), colour
= "navy", alpha = 0.7)

barchart of home ownership vs loan amount

data.for.plot <- aggregate(club_12_13_14$loan_amnt, by =
list(club_12_13_14$home_ownership), FUN = mean)
barplot(data.for.plot$x, names.arg = data.for.plot$Group.1,
xlab = "Home ownership", ylab = "Mean loan amount")

Histogram of loan amount

hist(club_12_13_14$loan_amnt, xlab = "Loan Amount")

Find the outliers

FindOutliers <- function(data) {
 lowerq = quantile(data)[2]
 upperq = quantile(data)[4]
 iqr = upperq - lowerq
 ### identify extreme outliers
 extreme.threshold.upper = (iqr * 3) + upperq
 extreme.threshold.lower = lowerq - (iqr * 3)
 result <- which(data > extreme.threshold.upper | data <
extreme.threshold.lower)
 length(result)
}
df <-
subset(club_12_13_14,select=c(loan_amnt,int_rate,annual_inc,dti,total_pymnt,total_rec_int,last_pymnt_amnt,tot_cur_bal,Orig..Index))
apply(df, 2, FindOutliers)

Check for multicollinearity

Use correlation function for correlation analysis.Variables with high

correlation leads to multicollinearity.These variables need to be dropped.

M<-cor(df)

total_pymnt,loan_amnt and total_rec_int are correlated.We can remove 2 of the

3 variables.

df<-select(df,-total_pymnt,-total_rec_int)

###Scaling and standardization of predictors.

df.scaled = scale(df, center= TRUE, scale=TRUE)
df.scaled<-as.data.frame(df.scaled)

##combine scaled numerical columns with categorical column in original dataframe

club_12_13_14.dummy<-
select(club_12_13_14,term,emp_length,home_ownership,loan_status,purpose,
       addr_sta
te,application_type)
library(forcats)
library(dummies)
library(dplyr)
club_12_13_14.dummy$term<-(fct_lump(club_12_13_14.dummy$term, n=5))
club_12_13_14.dummy$emp_length<-fct_lump(club_12_13_14.dummy$emp_length, n=5)
club_12_13_14.dummy$home_ownership<-fct_lump(club_12_13_14.dummy$home_ownership,
n = 5)
club_12_13_14.dummy$loan_status<-fct_lump(club_12_13_14.dummy$loan_status, n =
5)
club_12_13_14.dummy$purpose<-fct_lump(club_12_13_14.dummy$purpose, n=5)
club_12_13_14.dummy$addr_state<-fct_lump(club_12_13_14.dummy$addr_state,n=5)
club_12_13_14.dummy$application_type<-
fct_lump(club_12_13_14.dummy$application_type, n=5)
df.dummy<-dummy.data.frame(club_12_13_14.dummy)
club_12_13_14.nw<-
cbind(df.dummy,df.scaled,club_12_13_14$Comb_Risk_One,club_12_13_14$Comb_Risk_Two
)
club_12_13_14.nw$`club_12_13_14$Comb_Risk_One`<-
as.factor(club_12_13_14.nw$`club_12_13_14$Comb_Risk_One`)
club_12_13_14.nw$`club_12_13_14$Comb_Risk_Two`<-
as.factor(club_12_13_14.nw$`club_12_13_14$Comb_Risk_Two`)
club_12_13_14.nw<-club_12_13_14.nw %>%mutate_if(is.integer,as.factor)

train-test split and build different models.

set.seed(111)
train.index <- sample(row.names(club_12_13_14.nw), 0.6*dim(club_12_13_14.nw)[1])
valid.index <- setdiff(row.names(club_12_13_14.nw), train.index)
train.df <- club_12_13_14.nw[train.index, ]
valid.df <- club_12_13_14.nw[valid.index, ]

KNN for a) Comb_Risk_One

library(class)
nn <- knn(train.df,valid.df,cl=train.df[, 39],k=13)
tab <- table(nn,valid.df[, 39])
accuracy <- function(x){sum(diag(x)/(sum(rowSums(x)))) * 100}
accuracy(tab)

KNN for b) Comb_Risk_Two

library(class)
nn1 <- knn(train.df,valid.df,cl=train.df[, 40],k=13)
tab1 <- table(nn1,valid.df[, 40])
accuracy <- function(x){sum(diag(x)/(sum(rowSums(x)))) * 100}
accuracy(tab1)

##Naïve Bayes’ for a) Comb_Risk_One

library(e1071)
library(caret)
nb<- naiveBayes(train.df[, 39] ~ ., data = train.df)
pred.class <- predict(nb, newdata = valid.df,type="class")
confusionMatrix(pred.class, valid.df[, 39])

##Naïve Bayes’ for a) Comb_Risk_Two

library(caret)
nb<- naiveBayes(train.df[, 40] ~ ., data = train.df)
pred.class <- predict(nb, newdata = valid.df)
confusionMatrix(pred.class, valid.df[, 40])

##Logistic regression for a)Comb_Risk_One

log <- glm(`club_12_13_14$Comb_Risk_One` ~ ., data = train.df, family =
"binomial")
options(scipen=999)
summary(log)
pred <- predict(log, valid.df, type = "response")
confusionMatrix(as.factor(ifelse(pred > 0.5, 1, 0)),
valid.df$`club_12_13_14$Comb_Risk_One`)

##Logistic regression for b)Comb_Risk_Two

log <- glm(`club_12_13_14$Comb_Risk_Two` ~ ., data = train.df, family =
"binomial")
options(scipen=999)
summary(log)
pred <- predict(log, valid.df, type = "response")
confusionMatrix(as.factor(ifelse(pred > 0.5, 1, 0)),
valid.df$`club_12_13_14$Comb_Risk_Two`)

##CART for a)Comb_Risk_One

library(rpart)
library(caret)
ct <- rpart(`club_12_13_14$Comb_Risk_One` ~ ., data = train.df, method =
"class", cp = 0, minsplit = 1)
pred.val <- predict(ct,valid.df,type = "class")
# generate confusion matrix for training data
confusionMatrix(pred.val, valid.df$`club_12_13_14$Comb_Risk_One`)

##CART for a)Comb_Risk_Two

library(rpart)
ct <- rpart(`club_12_13_14$Comb_Risk_Two` ~ ., data = train.df, method =
"class", cp = 0, minsplit = 1)
pred.val <- predict(ct,valid.df,type = "class")
# generate confusion matrix for training data
confusionMatrix(pred.val, valid.df$`club_12_13_14$Comb_Risk_Two`)

From the accuracy results it can be seen that models Naive Bayes,KNN and CART

perform really well.Of this Naive Bayes classifier performs slightly better. #For period 2015-2017 ## predict Low and High-risk categories (for the two new response variables) using various modeling techniques like Naïve Bayes’, KNN, Logistic Regression, and CART model

club_15_16_17 <- club_15_16_17 %>% mutate(int_rate=as.numeric(gsub("%", "",
int_rate)))
club_15_16_17<-club_15_16_17[complete.cases(club_15_16_17), ]

BASIC EDA

Relation between annual income and total payment.

library(ggplot2)
ggplot(club_15_16_17) + geom_point(aes(x = annual_inc, y = total_pymnt), colour
= "navy", alpha = 0.7)

barchart of home ownership vs loan amount

data.for.plot <- aggregate(club_15_16_17$loan_amnt, by =
list(club_15_16_17$home_ownership), FUN = mean)
barplot(data.for.plot$x, names.arg = data.for.plot$Group.1,
xlab = "Home ownership", ylab = "Mean loan amount")

Histogram of loan amount

hist(club_15_16_17$loan_amnt, xlab = "Loan Amount")

Find the outliers

FindOutliers <- function(data) {
 lowerq = quantile(data)[2]
 upperq = quantile(data)[4]
 iqr = upperq - lowerq
 ### identify extreme outliers
 extreme.threshold.upper = (iqr * 3) + upperq
 extreme.threshold.lower = lowerq - (iqr * 3)
 result <- which(data > extreme.threshold.upper | data <
extreme.threshold.lower)
 length(result)
}
df <-
subset(club_15_16_17,select=c(loan_amnt,int_rate,annual_inc,dti,total_pymnt,total_rec_int,last_pymnt_amnt,tot_cur_bal,Orig..Index)) 
apply(df, 2, FindOutliers)

Check for multicollinearity

Use correlation function for correlation analysis.Variables with high

correlation leads to multicollinearity.These variables need to be dropped.

M<-cor(df)

total_pymnt,loan_amnt and total_rec_int are correlated.We can remove 2 of the

3 variables.

df<-select(df,-total_pymnt,-total_rec_int)

###Scaling and standardization of predictors.

df.scaled = scale(df, center= TRUE, scale=TRUE)
df.scaled=as.data.frame(df.scaled)

##combine scaled numerical columns with categorical column in original dataframe

club_15_16_17.dummy<-
select(club_15_16_17,term,emp_length,home_ownership,loan_status,purpose,
       addr_sta
te,application_type)
library(forcats)
library(dummies)
library(dplyr)
club_15_16_17.dummy$term<-(fct_lump(club_15_16_17.dummy$term, n=5))
club_15_16_17.dummy$emp_length<-fct_lump(club_15_16_17.dummy$emp_length, n=5)
club_15_16_17.dummy$home_ownership<-fct_lump(club_15_16_17.dummy$home_ownership,
n = 5)
club_15_16_17.dummy$loan_status<-fct_lump(club_15_16_17.dummy$loan_status, n =
5)
club_15_16_17.dummy$purpose<-fct_lump(club_15_16_17.dummy$purpose, n=5)
club_15_16_17.dummy$addr_state<-fct_lump(club_15_16_17.dummy$addr_state,n=5)
club_15_16_17.dummy$application_type<-
fct_lump(club_15_16_17.dummy$application_type, n=5)
df.dummy<-dummy.data.frame(club_15_16_17.dummy)
club_15_16_17.nw<-
cbind(df.dummy,df.scaled,club_15_16_17$Comb_Risk_One,club_15_16_17$Comb_Risk_Two
)
club_15_16_17.nw$`club_15_16_17$Comb_Risk_One`<-
as.factor(club_15_16_17.nw$`club_15_16_17$Comb_Risk_One`)
club_15_16_17.nw$`club_15_16_17$Comb_Risk_Two`<-
as.factor(club_15_16_17.nw$`club_15_16_17$Comb_Risk_Two`)
club_15_16_17.nw<-club_15_16_17.nw %>%mutate_if(is.integer,as.factor)

train-test split and build different models.

set.seed(111)
train.index <- sample(row.names(club_15_16_17.nw), 0.6*dim(club_15_16_17.nw)[1])
valid.index <- setdiff(row.names(club_15_16_17.nw), train.index)
train.df <- club_15_16_17.nw[train.index, ]
valid.df <- club_15_16_17.nw[valid.index, ]

##Naïve Bayes’ for a) Comb_Risk_One

library(e1071)
library(caret)
nb<- naiveBayes(train.df[, 40] ~ ., data = train.df)
pred.class <- predict(nb, newdata = valid.df,type="class")
confusionMatrix(pred.class, valid.df[, 40])

##Naïve Bayes’ for a) Comb_Risk_Two

library(caret)
nb<- naiveBayes(train.df[, 41] ~ ., data = train.df)
pred.class <- predict(nb, newdata = valid.df)
confusionMatrix(pred.class, valid.df[, 41])

##Logistic regression for a)Comb_Risk_One

log <- glm(`club_15_16_17$Comb_Risk_One` ~ ., data = train.df, family =
"binomial")
options(scipen=999)
summary(log)
pred <- predict(log, valid.df, type = "response")
confusionMatrix(as.factor(ifelse(pred > 0.5, 1, 0)),
valid.df$`club_15_16_17$Comb_Risk_One`)

##Logistic regression for b)Comb_Risk_Two

log <- glm(`club_15_16_17$Comb_Risk_Two` ~ ., data = train.df, family =
"binomial")
options(scipen=999)
summary(log)
pred <- predict(log, valid.df, type = "response")
confusionMatrix(as.factor(ifelse(pred > 0.5, 1, 0)),
valid.df$`club_15_16_17$Comb_Risk_Two`)

##CART for a)Comb_Risk_One

library(rpart)
ct <- rpart(`club_15_16_17$Comb_Risk_One` ~ ., data = train.df, method =
"class", cp = 0, minsplit = 1)
pred.val <- predict(ct,valid.df,type = "class")
# generate confusion matrix for training data

##CART for a)Comb_Risk_Two

library(rpart)
ct <- rpart(`club_15_16_17$Comb_Risk_Two` ~ ., data = train.df, method =
"class", cp = 0, minsplit = 1) pred.val <- predict(ct,valid.df,type = "class")
# generate confusion matrix for training data
confusionMatrix(pred.val, valid.df$`club_15_16_17$Comb_Risk_Two`)

From the accuracy results it can be seen that models Naive Bayes,KNN and CART

perform really well.Of this Naive Bayes and CART classifier performs slightly better.

LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQpgYGB7cn0NCmNsdWIgPC0gcmVhZC5jc3YoIkM6L1VzZXJzL1B1YmxpYy9MQ19XZWVrOV9EYXRhLmNzdiIpDQpoZWFkKGNsdWIpDQpzdW1tYXJ5KGNsdWIpDQpgYGANCiMjIGEpIENvbWJfUmlza19PbmU6IENyZWF0ZSBhIGJpbmFyeSBjb2x1bW4gYnkgY29tYmluaW5nIGNhdGVnb3JpZXMgQSBhbmQgQiAoTG93DQpSaXNrKSBpbnRvIG9uZSBjYXRlZ29yeSBhbmQgYWxsIHRoZSByZW1haW5pbmcgY2F0ZWdvcmllcyBpbiBhbm90aGVyIChIaWdoIFJpc2spLg0KIyMgYikgQ29tYl9SaXNrX1R3bzogQ3JlYXRlIGEgYmluYXJ5IGNvbHVtbiBieSBjb21iaW5pbmcgY2F0ZWdvcmllcyBBLCBCIGFuZCBDDQooTG93IFJpc2spIGludG8gb25lIGNhdGVnb3J5IGFuZCBhbGwgdGhlIHJlbWFpbmluZyBjYXRlZ29yaWVzIGluIGFub3RoZXIgKEhpZ2gNClJpc2spLg0KYGBge3J9DQpsaWJyYXJ5KGRwbHlyKQ0KY2x1YjwtY2x1YiAlPiUNCiBtdXRhdGUoQ29tYl9SaXNrX09uZSA9IGlmZWxzZShncmFkZT09J0EnIHwgZ3JhZGU9PSdCJywgMCwgMSkpDQpoZWFkKGNsdWIpDQpjbHViPC1jbHViJT4lbXV0YXRlKENvbWJfUmlza19Ud28gPSBpZmVsc2UoZ3JhZGU9PSdBJyB8IGdyYWRlPT0nQicgfGdyYWRlPT0nQycsDQowLCAxKSkNCmBgYA0KIyMgYnJlYWsgdGhlIGZpbGUgaW50byB0d28gZmlsZXMgZmlsdGVyaW5nIG91dCBkYXRhIGZvciAyMDEyLCAxMywgYW5kIDE0IGluIG9uZQ0KZmlsZSBhbmQgMjAxNSwgMTYgYW5kIDE3IGluIGFub3RoZXIgZmlsZS4NCmBgYHtyfQ0KY2x1Yl8xMl8xM18xNDwtIHN1YnNldChjbHViLGNsdWIkaXNzdWVfWWVhciAlaW4lIGMoMjAxMiwyMDEzLDIwMTQpKQ0KY2x1Yl8xNV8xNl8xNzwtIHN1YnNldChjbHViLGNsdWIkaXNzdWVfWWVhciAlaW4lIGMoMjAxNSwyMDE2LDIwMTcpKQ0KYGBgDQojI0NoZWNrIGZvciBudWxsIHZhbHVlcy5JbXB1dGUgdGhlIG1pc3NpbmcgdmFsdWVzLg0KYGBge3J9DQpzdW1tYXJ5KGNsdWJfMTJfMTNfMTQpDQpzdW1tYXJ5KGNsdWJfMTVfMTZfMTcpDQpjbHViXzEyXzEzXzE0JG10aHNfc2luY2VfbGFzdF9kZWxpbnFbaXMubmEoY2x1Yl8xMl8xM18xNCRtdGhzX3NpbmNlX2xhc3RfZGVsaW5xKQ0KXT0NCiBtZWFuKGNsdWJfMTJfMTNfMTQkbXRoc19zaW5jZV9sYXN0X2RlbGlucSxuYS5ybT1UKQ0KY2x1Yl8xNV8xNl8xNyRtdGhzX3NpbmNlX2xhc3RfZGVsaW5xW2lzLm5hKGNsdWJfMTVfMTZfMTckbXRoc19zaW5jZV9sYXN0X2RlbGlucSkNCl09DQogbWVhbihjbHViXzE1XzE2XzE3JG10aHNfc2luY2VfbGFzdF9kZWxpbnEsbmEucm09VCkNCmBgYA0KIyMgcHJlZGljdCBMb3cgYW5kIEhpZ2gtcmlzayBjYXRlZ29yaWVzIChmb3IgdGhlIHR3byBuZXcgcmVzcG9uc2UgdmFyaWFibGVzKQ0KdXNpbmcgdmFyaW91cyBtb2RlbGluZyB0ZWNobmlxdWVzIGxpa2UgTmHDr3ZlIEJheWVz4oCZLCBLTk4sIExvZ2lzdGljIFJlZ3Jlc3Npb24sDQphbmQgQ0FSVCBtb2RlbA0KYGBge3J9DQpsaWJyYXJ5KGRwbHlyKQ0KY2x1Yl8xMl8xM18xNCA8LSBjbHViXzEyXzEzXzE0ICU+JSBtdXRhdGUoaW50X3JhdGU9YXMubnVtZXJpYyhnc3ViKCIlIiwgIiIsDQppbnRfcmF0ZSkpKQ0KY2x1Yl8xMl8xM18xNDwtY2x1Yl8xMl8xM18xNFtjb21wbGV0ZS5jYXNlcyhjbHViXzEyXzEzXzE0KSwgXQ0KYGBgDQojIyBCQVNJQyBFREENCiMjIFJlbGF0aW9uIGJldHdlZW4gYW5udWFsIGluY29tZSBhbmQgdG90YWwgcGF5bWVudC4NCmBgYHtyfQ0KbGlicmFyeShnZ3Bsb3QyKQ0KZ2dwbG90KGNsdWJfMTJfMTNfMTQpICsgZ2VvbV9wb2ludChhZXMoeCA9IGFubnVhbF9pbmMsIHkgPSB0b3RhbF9weW1udCksIGNvbG91cg0KPSAibmF2eSIsIGFscGhhID0gMC43KQ0KYGBgDQojIyBiYXJjaGFydCBvZiBob21lIG93bmVyc2hpcCB2cyBsb2FuIGFtb3VudA0KYGBge3J9DQpkYXRhLmZvci5wbG90IDwtIGFnZ3JlZ2F0ZShjbHViXzEyXzEzXzE0JGxvYW5fYW1udCwgYnkgPQ0KbGlzdChjbHViXzEyXzEzXzE0JGhvbWVfb3duZXJzaGlwKSwgRlVOID0gbWVhbikNCmJhcnBsb3QoZGF0YS5mb3IucGxvdCR4LCBuYW1lcy5hcmcgPSBkYXRhLmZvci5wbG90JEdyb3VwLjEsDQp4bGFiID0gIkhvbWUgb3duZXJzaGlwIiwgeWxhYiA9ICJNZWFuIGxvYW4gYW1vdW50IikNCmBgYA0KIyMgSGlzdG9ncmFtIG9mIGxvYW4gYW1vdW50DQpgYGB7cn0NCmhpc3QoY2x1Yl8xMl8xM18xNCRsb2FuX2FtbnQsIHhsYWIgPSAiTG9hbiBBbW91bnQiKQ0KYGBgDQojIyBGaW5kIHRoZSBvdXRsaWVycw0KYGBge3J9DQpGaW5kT3V0bGllcnMgPC0gZnVuY3Rpb24oZGF0YSkgew0KIGxvd2VycSA9IHF1YW50aWxlKGRhdGEpWzJdDQogdXBwZXJxID0gcXVhbnRpbGUoZGF0YSlbNF0NCiBpcXIgPSB1cHBlcnEgLSBsb3dlcnENCiAjIyMgaWRlbnRpZnkgZXh0cmVtZSBvdXRsaWVycw0KIGV4dHJlbWUudGhyZXNob2xkLnVwcGVyID0gKGlxciAqIDMpICsgdXBwZXJxDQogZXh0cmVtZS50aHJlc2hvbGQubG93ZXIgPSBsb3dlcnEgLSAoaXFyICogMykNCiByZXN1bHQgPC0gd2hpY2goZGF0YSA+IGV4dHJlbWUudGhyZXNob2xkLnVwcGVyIHwgZGF0YSA8DQpleHRyZW1lLnRocmVzaG9sZC5sb3dlcikNCiBsZW5ndGgocmVzdWx0KQ0KfQ0KZGYgPC0NCnN1YnNldChjbHViXzEyXzEzXzE0LHNlbGVjdD1jKGxvYW5fYW1udCxpbnRfcmF0ZSxhbm51YWxfaW5jLGR0aSx0b3RhbF9weW1udCx0b3RhbF9yZWNfaW50LGxhc3RfcHltbnRfYW1udCx0b3RfY3VyX2JhbCxPcmlnLi5JbmRleCkpDQphcHBseShkZiwgMiwgRmluZE91dGxpZXJzKQ0KYGBgDQojIyBDaGVjayBmb3IgbXVsdGljb2xsaW5lYXJpdHkNCiMjIyBVc2UgY29ycmVsYXRpb24gZnVuY3Rpb24gZm9yIGNvcnJlbGF0aW9uIGFuYWx5c2lzLlZhcmlhYmxlcyB3aXRoIGhpZ2gNCmNvcnJlbGF0aW9uIGxlYWRzIHRvIG11bHRpY29sbGluZWFyaXR5LlRoZXNlIHZhcmlhYmxlcyBuZWVkIHRvIGJlIGRyb3BwZWQuDQpgYGB7cn0NCk08LWNvcihkZikNCmBgYA0KIyMgdG90YWxfcHltbnQsbG9hbl9hbW50IGFuZCB0b3RhbF9yZWNfaW50IGFyZSBjb3JyZWxhdGVkLldlIGNhbiByZW1vdmUgMiBvZiB0aGUNCjMgdmFyaWFibGVzLg0KYGBge3J9DQpkZjwtc2VsZWN0KGRmLC10b3RhbF9weW1udCwtdG90YWxfcmVjX2ludCkNCmBgYA0KIyMjU2NhbGluZyBhbmQgc3RhbmRhcmRpemF0aW9uIG9mIHByZWRpY3RvcnMuDQpgYGB7cn0NCmRmLnNjYWxlZCA9IHNjYWxlKGRmLCBjZW50ZXI9IFRSVUUsIHNjYWxlPVRSVUUpDQpkZi5zY2FsZWQ8LWFzLmRhdGEuZnJhbWUoZGYuc2NhbGVkKQ0KYGBgDQojI2NvbWJpbmUgc2NhbGVkIG51bWVyaWNhbCBjb2x1bW5zIHdpdGggY2F0ZWdvcmljYWwgY29sdW1uIGluIG9yaWdpbmFsIGRhdGFmcmFtZQ0KYGBge3J9DQpjbHViXzEyXzEzXzE0LmR1bW15PC0NCnNlbGVjdChjbHViXzEyXzEzXzE0LHRlcm0sZW1wX2xlbmd0aCxob21lX293bmVyc2hpcCxsb2FuX3N0YXR1cyxwdXJwb3NlLA0KICAgICAgIGFkZHJfc3RhDQp0ZSxhcHBsaWNhdGlvbl90eXBlKQ0KbGlicmFyeShmb3JjYXRzKQ0KbGlicmFyeShkdW1taWVzKQ0KbGlicmFyeShkcGx5cikNCmNsdWJfMTJfMTNfMTQuZHVtbXkkdGVybTwtKGZjdF9sdW1wKGNsdWJfMTJfMTNfMTQuZHVtbXkkdGVybSwgbj01KSkNCmNsdWJfMTJfMTNfMTQuZHVtbXkkZW1wX2xlbmd0aDwtZmN0X2x1bXAoY2x1Yl8xMl8xM18xNC5kdW1teSRlbXBfbGVuZ3RoLCBuPTUpDQpjbHViXzEyXzEzXzE0LmR1bW15JGhvbWVfb3duZXJzaGlwPC1mY3RfbHVtcChjbHViXzEyXzEzXzE0LmR1bW15JGhvbWVfb3duZXJzaGlwLA0KbiA9IDUpDQpjbHViXzEyXzEzXzE0LmR1bW15JGxvYW5fc3RhdHVzPC1mY3RfbHVtcChjbHViXzEyXzEzXzE0LmR1bW15JGxvYW5fc3RhdHVzLCBuID0NCjUpDQpjbHViXzEyXzEzXzE0LmR1bW15JHB1cnBvc2U8LWZjdF9sdW1wKGNsdWJfMTJfMTNfMTQuZHVtbXkkcHVycG9zZSwgbj01KQ0KY2x1Yl8xMl8xM18xNC5kdW1teSRhZGRyX3N0YXRlPC1mY3RfbHVtcChjbHViXzEyXzEzXzE0LmR1bW15JGFkZHJfc3RhdGUsbj01KQ0KY2x1Yl8xMl8xM18xNC5kdW1teSRhcHBsaWNhdGlvbl90eXBlPC0NCmZjdF9sdW1wKGNsdWJfMTJfMTNfMTQuZHVtbXkkYXBwbGljYXRpb25fdHlwZSwgbj01KQ0KZGYuZHVtbXk8LWR1bW15LmRhdGEuZnJhbWUoY2x1Yl8xMl8xM18xNC5kdW1teSkNCmNsdWJfMTJfMTNfMTQubnc8LQ0KY2JpbmQoZGYuZHVtbXksZGYuc2NhbGVkLGNsdWJfMTJfMTNfMTQkQ29tYl9SaXNrX09uZSxjbHViXzEyXzEzXzE0JENvbWJfUmlza19Ud28NCikNCmNsdWJfMTJfMTNfMTQubnckYGNsdWJfMTJfMTNfMTQkQ29tYl9SaXNrX09uZWA8LQ0KYXMuZmFjdG9yKGNsdWJfMTJfMTNfMTQubnckYGNsdWJfMTJfMTNfMTQkQ29tYl9SaXNrX09uZWApDQpjbHViXzEyXzEzXzE0Lm53JGBjbHViXzEyXzEzXzE0JENvbWJfUmlza19Ud29gPC0NCmFzLmZhY3RvcihjbHViXzEyXzEzXzE0Lm53JGBjbHViXzEyXzEzXzE0JENvbWJfUmlza19Ud29gKQ0KY2x1Yl8xMl8xM18xNC5udzwtY2x1Yl8xMl8xM18xNC5udyAlPiVtdXRhdGVfaWYoaXMuaW50ZWdlcixhcy5mYWN0b3IpDQpgYGANCg0KIyMgdHJhaW4tdGVzdCBzcGxpdCBhbmQgYnVpbGQgZGlmZmVyZW50IG1vZGVscy4NCmBgYHtyfQ0Kc2V0LnNlZWQoMTExKQ0KdHJhaW4uaW5kZXggPC0gc2FtcGxlKHJvdy5uYW1lcyhjbHViXzEyXzEzXzE0Lm53KSwgMC42KmRpbShjbHViXzEyXzEzXzE0Lm53KVsxXSkNCnZhbGlkLmluZGV4IDwtIHNldGRpZmYocm93Lm5hbWVzKGNsdWJfMTJfMTNfMTQubncpLCB0cmFpbi5pbmRleCkNCnRyYWluLmRmIDwtIGNsdWJfMTJfMTNfMTQubndbdHJhaW4uaW5kZXgsIF0NCnZhbGlkLmRmIDwtIGNsdWJfMTJfMTNfMTQubndbdmFsaWQuaW5kZXgsIF0NCmBgYA0KIyMgS05OIGZvciBhKSBDb21iX1Jpc2tfT25lDQpgYGB7cn0NCmxpYnJhcnkoY2xhc3MpDQpubiA8LSBrbm4odHJhaW4uZGYsdmFsaWQuZGYsY2w9dHJhaW4uZGZbLCAzOV0saz0xMykNCnRhYiA8LSB0YWJsZShubix2YWxpZC5kZlssIDM5XSkNCmFjY3VyYWN5IDwtIGZ1bmN0aW9uKHgpe3N1bShkaWFnKHgpLyhzdW0ocm93U3Vtcyh4KSkpKSAqIDEwMH0NCmFjY3VyYWN5KHRhYikNCmBgYA0KIyMgS05OIGZvciBiKSBDb21iX1Jpc2tfVHdvDQpgYGB7cn0NCmxpYnJhcnkoY2xhc3MpDQpubjEgPC0ga25uKHRyYWluLmRmLHZhbGlkLmRmLGNsPXRyYWluLmRmWywgNDBdLGs9MTMpDQp0YWIxIDwtIHRhYmxlKG5uMSx2YWxpZC5kZlssIDQwXSkNCmFjY3VyYWN5IDwtIGZ1bmN0aW9uKHgpe3N1bShkaWFnKHgpLyhzdW0ocm93U3Vtcyh4KSkpKSAqIDEwMH0NCmFjY3VyYWN5KHRhYjEpDQpgYGANCiMjTmHDr3ZlIEJheWVz4oCZIGZvciBhKSBDb21iX1Jpc2tfT25lDQpgYGB7cn0NCmxpYnJhcnkoZTEwNzEpDQpsaWJyYXJ5KGNhcmV0KQ0KbmI8LSBuYWl2ZUJheWVzKHRyYWluLmRmWywgMzldIH4gLiwgZGF0YSA9IHRyYWluLmRmKQ0KcHJlZC5jbGFzcyA8LSBwcmVkaWN0KG5iLCBuZXdkYXRhID0gdmFsaWQuZGYsdHlwZT0iY2xhc3MiKQ0KY29uZnVzaW9uTWF0cml4KHByZWQuY2xhc3MsIHZhbGlkLmRmWywgMzldKQ0KYGBgDQojI05hw692ZSBCYXllc+KAmSBmb3IgYSkgQ29tYl9SaXNrX1R3bw0KYGBge3J9DQpsaWJyYXJ5KGNhcmV0KQ0KbmI8LSBuYWl2ZUJheWVzKHRyYWluLmRmWywgNDBdIH4gLiwgZGF0YSA9IHRyYWluLmRmKQ0KcHJlZC5jbGFzcyA8LSBwcmVkaWN0KG5iLCBuZXdkYXRhID0gdmFsaWQuZGYpDQpjb25mdXNpb25NYXRyaXgocHJlZC5jbGFzcywgdmFsaWQuZGZbLCA0MF0pDQpgYGANCiMjTG9naXN0aWMgcmVncmVzc2lvbiBmb3IgYSlDb21iX1Jpc2tfT25lDQpgYGB7cn0NCmxvZyA8LSBnbG0oYGNsdWJfMTJfMTNfMTQkQ29tYl9SaXNrX09uZWAgfiAuLCBkYXRhID0gdHJhaW4uZGYsIGZhbWlseSA9DQoiYmlub21pYWwiKQ0Kb3B0aW9ucyhzY2lwZW49OTk5KQ0Kc3VtbWFyeShsb2cpDQpwcmVkIDwtIHByZWRpY3QobG9nLCB2YWxpZC5kZiwgdHlwZSA9ICJyZXNwb25zZSIpDQpjb25mdXNpb25NYXRyaXgoYXMuZmFjdG9yKGlmZWxzZShwcmVkID4gMC41LCAxLCAwKSksDQp2YWxpZC5kZiRgY2x1Yl8xMl8xM18xNCRDb21iX1Jpc2tfT25lYCkNCmBgYA0KIyNMb2dpc3RpYyByZWdyZXNzaW9uIGZvciBiKUNvbWJfUmlza19Ud28NCmBgYHtyfQ0KbG9nIDwtIGdsbShgY2x1Yl8xMl8xM18xNCRDb21iX1Jpc2tfVHdvYCB+IC4sIGRhdGEgPSB0cmFpbi5kZiwgZmFtaWx5ID0NCiJiaW5vbWlhbCIpDQpvcHRpb25zKHNjaXBlbj05OTkpDQpzdW1tYXJ5KGxvZykNCnByZWQgPC0gcHJlZGljdChsb2csIHZhbGlkLmRmLCB0eXBlID0gInJlc3BvbnNlIikNCmNvbmZ1c2lvbk1hdHJpeChhcy5mYWN0b3IoaWZlbHNlKHByZWQgPiAwLjUsIDEsIDApKSwNCnZhbGlkLmRmJGBjbHViXzEyXzEzXzE0JENvbWJfUmlza19Ud29gKQ0KYGBgDQojI0NBUlQgZm9yIGEpQ29tYl9SaXNrX09uZQ0KYGBge3J9DQpsaWJyYXJ5KHJwYXJ0KQ0KbGlicmFyeShjYXJldCkNCmN0IDwtIHJwYXJ0KGBjbHViXzEyXzEzXzE0JENvbWJfUmlza19PbmVgIH4gLiwgZGF0YSA9IHRyYWluLmRmLCBtZXRob2QgPQ0KImNsYXNzIiwgY3AgPSAwLCBtaW5zcGxpdCA9IDEpDQpwcmVkLnZhbCA8LSBwcmVkaWN0KGN0LHZhbGlkLmRmLHR5cGUgPSAiY2xhc3MiKQ0KIyBnZW5lcmF0ZSBjb25mdXNpb24gbWF0cml4IGZvciB0cmFpbmluZyBkYXRhDQpjb25mdXNpb25NYXRyaXgocHJlZC52YWwsIHZhbGlkLmRmJGBjbHViXzEyXzEzXzE0JENvbWJfUmlza19PbmVgKQ0KYGBgDQojI0NBUlQgZm9yIGEpQ29tYl9SaXNrX1R3bw0KDQpgYGB7cn0NCmxpYnJhcnkocnBhcnQpDQpjdCA8LSBycGFydChgY2x1Yl8xMl8xM18xNCRDb21iX1Jpc2tfVHdvYCB+IC4sIGRhdGEgPSB0cmFpbi5kZiwgbWV0aG9kID0NCiJjbGFzcyIsIGNwID0gMCwgbWluc3BsaXQgPSAxKQ0KcHJlZC52YWwgPC0gcHJlZGljdChjdCx2YWxpZC5kZix0eXBlID0gImNsYXNzIikNCiMgZ2VuZXJhdGUgY29uZnVzaW9uIG1hdHJpeCBmb3IgdHJhaW5pbmcgZGF0YQ0KY29uZnVzaW9uTWF0cml4KHByZWQudmFsLCB2YWxpZC5kZiRgY2x1Yl8xMl8xM18xNCRDb21iX1Jpc2tfVHdvYCkNCmBgYA0KIyMgRnJvbSB0aGUgYWNjdXJhY3kgcmVzdWx0cyBpdCBjYW4gYmUgc2VlbiB0aGF0IG1vZGVscyBOYWl2ZSBCYXllcyxLTk4gYW5kIENBUlQNCnBlcmZvcm0gcmVhbGx5IHdlbGwuT2YgdGhpcyBOYWl2ZSBCYXllcyBjbGFzc2lmaWVyIHBlcmZvcm1zIHNsaWdodGx5IGJldHRlci4NCiNGb3IgcGVyaW9kIDIwMTUtMjAxNw0KIyMgcHJlZGljdCBMb3cgYW5kIEhpZ2gtcmlzayBjYXRlZ29yaWVzIChmb3IgdGhlIHR3byBuZXcgcmVzcG9uc2UgdmFyaWFibGVzKQ0KdXNpbmcgdmFyaW91cyBtb2RlbGluZyB0ZWNobmlxdWVzIGxpa2UgTmHDr3ZlIEJheWVz4oCZLCBLTk4sIExvZ2lzdGljIFJlZ3Jlc3Npb24sDQphbmQgQ0FSVCBtb2RlbA0KYGBge3J9DQpjbHViXzE1XzE2XzE3IDwtIGNsdWJfMTVfMTZfMTcgJT4lIG11dGF0ZShpbnRfcmF0ZT1hcy5udW1lcmljKGdzdWIoIiUiLCAiIiwNCmludF9yYXRlKSkpDQpjbHViXzE1XzE2XzE3PC1jbHViXzE1XzE2XzE3W2NvbXBsZXRlLmNhc2VzKGNsdWJfMTVfMTZfMTcpLCBdDQpgYGANCiMjIEJBU0lDIEVEQQ0KIyMgUmVsYXRpb24gYmV0d2VlbiBhbm51YWwgaW5jb21lIGFuZCB0b3RhbCBwYXltZW50Lg0KYGBge3J9DQpsaWJyYXJ5KGdncGxvdDIpDQpnZ3Bsb3QoY2x1Yl8xNV8xNl8xNykgKyBnZW9tX3BvaW50KGFlcyh4ID0gYW5udWFsX2luYywgeSA9IHRvdGFsX3B5bW50KSwgY29sb3VyDQo9ICJuYXZ5IiwgYWxwaGEgPSAwLjcpDQpgYGANCiMjIGJhcmNoYXJ0IG9mIGhvbWUgb3duZXJzaGlwIHZzIGxvYW4gYW1vdW50DQpgYGB7cn0NCmRhdGEuZm9yLnBsb3QgPC0gYWdncmVnYXRlKGNsdWJfMTVfMTZfMTckbG9hbl9hbW50LCBieSA9DQpsaXN0KGNsdWJfMTVfMTZfMTckaG9tZV9vd25lcnNoaXApLCBGVU4gPSBtZWFuKQ0KYmFycGxvdChkYXRhLmZvci5wbG90JHgsIG5hbWVzLmFyZyA9IGRhdGEuZm9yLnBsb3QkR3JvdXAuMSwNCnhsYWIgPSAiSG9tZSBvd25lcnNoaXAiLCB5bGFiID0gIk1lYW4gbG9hbiBhbW91bnQiKQ0KYGBgDQojIyBIaXN0b2dyYW0gb2YgbG9hbiBhbW91bnQNCmBgYHtyfQ0KaGlzdChjbHViXzE1XzE2XzE3JGxvYW5fYW1udCwgeGxhYiA9ICJMb2FuIEFtb3VudCIpDQpgYGANCiMjIEZpbmQgdGhlIG91dGxpZXJzDQpgYGB7cn0NCkZpbmRPdXRsaWVycyA8LSBmdW5jdGlvbihkYXRhKSB7DQogbG93ZXJxID0gcXVhbnRpbGUoZGF0YSlbMl0NCiB1cHBlcnEgPSBxdWFudGlsZShkYXRhKVs0XQ0KIGlxciA9IHVwcGVycSAtIGxvd2VycQ0KICMjIyBpZGVudGlmeSBleHRyZW1lIG91dGxpZXJzDQogZXh0cmVtZS50aHJlc2hvbGQudXBwZXIgPSAoaXFyICogMykgKyB1cHBlcnENCiBleHRyZW1lLnRocmVzaG9sZC5sb3dlciA9IGxvd2VycSAtIChpcXIgKiAzKQ0KIHJlc3VsdCA8LSB3aGljaChkYXRhID4gZXh0cmVtZS50aHJlc2hvbGQudXBwZXIgfCBkYXRhIDwNCmV4dHJlbWUudGhyZXNob2xkLmxvd2VyKQ0KIGxlbmd0aChyZXN1bHQpDQp9DQpkZiA8LQ0Kc3Vic2V0KGNsdWJfMTVfMTZfMTcsc2VsZWN0PWMobG9hbl9hbW50LGludF9yYXRlLGFubnVhbF9pbmMsZHRpLHRvdGFsX3B5bW50LHRvdGFsX3JlY19pbnQsbGFzdF9weW1udF9hbW50LHRvdF9jdXJfYmFsLE9yaWcuLkluZGV4KSkgDQphcHBseShkZiwgMiwgRmluZE91dGxpZXJzKQ0KYGBgDQojIyBDaGVjayBmb3IgbXVsdGljb2xsaW5lYXJpdHkNCiMjIyBVc2UgY29ycmVsYXRpb24gZnVuY3Rpb24gZm9yIGNvcnJlbGF0aW9uIGFuYWx5c2lzLlZhcmlhYmxlcyB3aXRoIGhpZ2gNCmNvcnJlbGF0aW9uIGxlYWRzIHRvIG11bHRpY29sbGluZWFyaXR5LlRoZXNlIHZhcmlhYmxlcyBuZWVkIHRvIGJlIGRyb3BwZWQuDQpgYGB7cn0NCk08LWNvcihkZikNCmBgYA0KIyMgdG90YWxfcHltbnQsbG9hbl9hbW50IGFuZCB0b3RhbF9yZWNfaW50IGFyZSBjb3JyZWxhdGVkLldlIGNhbiByZW1vdmUgMiBvZiB0aGUNCjMgdmFyaWFibGVzLg0KYGBge3J9DQpkZjwtc2VsZWN0KGRmLC10b3RhbF9weW1udCwtdG90YWxfcmVjX2ludCkNCmBgYA0KIyMjU2NhbGluZyBhbmQgc3RhbmRhcmRpemF0aW9uIG9mIHByZWRpY3RvcnMuDQpgYGB7cn0NCmRmLnNjYWxlZCA9IHNjYWxlKGRmLCBjZW50ZXI9IFRSVUUsIHNjYWxlPVRSVUUpDQpkZi5zY2FsZWQ9YXMuZGF0YS5mcmFtZShkZi5zY2FsZWQpDQpgYGANCiMjY29tYmluZSBzY2FsZWQgbnVtZXJpY2FsIGNvbHVtbnMgd2l0aCBjYXRlZ29yaWNhbCBjb2x1bW4gaW4gb3JpZ2luYWwgZGF0YWZyYW1lDQpgYGB7cn0NCmNsdWJfMTVfMTZfMTcuZHVtbXk8LQ0Kc2VsZWN0KGNsdWJfMTVfMTZfMTcsdGVybSxlbXBfbGVuZ3RoLGhvbWVfb3duZXJzaGlwLGxvYW5fc3RhdHVzLHB1cnBvc2UsDQogICAgICAgYWRkcl9zdGENCnRlLGFwcGxpY2F0aW9uX3R5cGUpDQpsaWJyYXJ5KGZvcmNhdHMpDQpsaWJyYXJ5KGR1bW1pZXMpDQpsaWJyYXJ5KGRwbHlyKQ0KY2x1Yl8xNV8xNl8xNy5kdW1teSR0ZXJtPC0oZmN0X2x1bXAoY2x1Yl8xNV8xNl8xNy5kdW1teSR0ZXJtLCBuPTUpKQ0KY2x1Yl8xNV8xNl8xNy5kdW1teSRlbXBfbGVuZ3RoPC1mY3RfbHVtcChjbHViXzE1XzE2XzE3LmR1bW15JGVtcF9sZW5ndGgsIG49NSkNCmNsdWJfMTVfMTZfMTcuZHVtbXkkaG9tZV9vd25lcnNoaXA8LWZjdF9sdW1wKGNsdWJfMTVfMTZfMTcuZHVtbXkkaG9tZV9vd25lcnNoaXAsDQpuID0gNSkNCmNsdWJfMTVfMTZfMTcuZHVtbXkkbG9hbl9zdGF0dXM8LWZjdF9sdW1wKGNsdWJfMTVfMTZfMTcuZHVtbXkkbG9hbl9zdGF0dXMsIG4gPQ0KNSkNCmNsdWJfMTVfMTZfMTcuZHVtbXkkcHVycG9zZTwtZmN0X2x1bXAoY2x1Yl8xNV8xNl8xNy5kdW1teSRwdXJwb3NlLCBuPTUpDQpjbHViXzE1XzE2XzE3LmR1bW15JGFkZHJfc3RhdGU8LWZjdF9sdW1wKGNsdWJfMTVfMTZfMTcuZHVtbXkkYWRkcl9zdGF0ZSxuPTUpDQpjbHViXzE1XzE2XzE3LmR1bW15JGFwcGxpY2F0aW9uX3R5cGU8LQ0KZmN0X2x1bXAoY2x1Yl8xNV8xNl8xNy5kdW1teSRhcHBsaWNhdGlvbl90eXBlLCBuPTUpDQpkZi5kdW1teTwtZHVtbXkuZGF0YS5mcmFtZShjbHViXzE1XzE2XzE3LmR1bW15KQ0KY2x1Yl8xNV8xNl8xNy5udzwtDQpjYmluZChkZi5kdW1teSxkZi5zY2FsZWQsY2x1Yl8xNV8xNl8xNyRDb21iX1Jpc2tfT25lLGNsdWJfMTVfMTZfMTckQ29tYl9SaXNrX1R3bw0KKQ0KY2x1Yl8xNV8xNl8xNy5udyRgY2x1Yl8xNV8xNl8xNyRDb21iX1Jpc2tfT25lYDwtDQphcy5mYWN0b3IoY2x1Yl8xNV8xNl8xNy5udyRgY2x1Yl8xNV8xNl8xNyRDb21iX1Jpc2tfT25lYCkNCmNsdWJfMTVfMTZfMTcubnckYGNsdWJfMTVfMTZfMTckQ29tYl9SaXNrX1R3b2A8LQ0KYXMuZmFjdG9yKGNsdWJfMTVfMTZfMTcubnckYGNsdWJfMTVfMTZfMTckQ29tYl9SaXNrX1R3b2ApDQpjbHViXzE1XzE2XzE3Lm53PC1jbHViXzE1XzE2XzE3Lm53ICU+JW11dGF0ZV9pZihpcy5pbnRlZ2VyLGFzLmZhY3RvcikNCmBgYA0KIyMgdHJhaW4tdGVzdCBzcGxpdCBhbmQgYnVpbGQgZGlmZmVyZW50IG1vZGVscy4NCmBgYHtyfQ0Kc2V0LnNlZWQoMTExKQ0KdHJhaW4uaW5kZXggPC0gc2FtcGxlKHJvdy5uYW1lcyhjbHViXzE1XzE2XzE3Lm53KSwgMC42KmRpbShjbHViXzE1XzE2XzE3Lm53KVsxXSkNCnZhbGlkLmluZGV4IDwtIHNldGRpZmYocm93Lm5hbWVzKGNsdWJfMTVfMTZfMTcubncpLCB0cmFpbi5pbmRleCkNCnRyYWluLmRmIDwtIGNsdWJfMTVfMTZfMTcubndbdHJhaW4uaW5kZXgsIF0NCnZhbGlkLmRmIDwtIGNsdWJfMTVfMTZfMTcubndbdmFsaWQuaW5kZXgsIF0NCmBgYA0KIyNOYcOvdmUgQmF5ZXPigJkgZm9yIGEpIENvbWJfUmlza19PbmUNCmBgYHtyfQ0KbGlicmFyeShlMTA3MSkNCmxpYnJhcnkoY2FyZXQpDQpuYjwtIG5haXZlQmF5ZXModHJhaW4uZGZbLCA0MF0gfiAuLCBkYXRhID0gdHJhaW4uZGYpDQpwcmVkLmNsYXNzIDwtIHByZWRpY3QobmIsIG5ld2RhdGEgPSB2YWxpZC5kZix0eXBlPSJjbGFzcyIpDQpjb25mdXNpb25NYXRyaXgocHJlZC5jbGFzcywgdmFsaWQuZGZbLCA0MF0pDQpgYGANCiMjTmHDr3ZlIEJheWVz4oCZIGZvciBhKSBDb21iX1Jpc2tfVHdvDQpgYGB7cn0NCmxpYnJhcnkoY2FyZXQpDQpuYjwtIG5haXZlQmF5ZXModHJhaW4uZGZbLCA0MV0gfiAuLCBkYXRhID0gdHJhaW4uZGYpDQpwcmVkLmNsYXNzIDwtIHByZWRpY3QobmIsIG5ld2RhdGEgPSB2YWxpZC5kZikNCmNvbmZ1c2lvbk1hdHJpeChwcmVkLmNsYXNzLCB2YWxpZC5kZlssIDQxXSkNCmBgYA0KIyNMb2dpc3RpYyByZWdyZXNzaW9uIGZvciBhKUNvbWJfUmlza19PbmUNCmBgYHtyfQ0KbG9nIDwtIGdsbShgY2x1Yl8xNV8xNl8xNyRDb21iX1Jpc2tfT25lYCB+IC4sIGRhdGEgPSB0cmFpbi5kZiwgZmFtaWx5ID0NCiJiaW5vbWlhbCIpDQpvcHRpb25zKHNjaXBlbj05OTkpDQpzdW1tYXJ5KGxvZykNCnByZWQgPC0gcHJlZGljdChsb2csIHZhbGlkLmRmLCB0eXBlID0gInJlc3BvbnNlIikNCmNvbmZ1c2lvbk1hdHJpeChhcy5mYWN0b3IoaWZlbHNlKHByZWQgPiAwLjUsIDEsIDApKSwNCnZhbGlkLmRmJGBjbHViXzE1XzE2XzE3JENvbWJfUmlza19PbmVgKQ0KYGBgDQojI0xvZ2lzdGljIHJlZ3Jlc3Npb24gZm9yIGIpQ29tYl9SaXNrX1R3bw0KYGBge3J9DQpsb2cgPC0gZ2xtKGBjbHViXzE1XzE2XzE3JENvbWJfUmlza19Ud29gIH4gLiwgZGF0YSA9IHRyYWluLmRmLCBmYW1pbHkgPQ0KImJpbm9taWFsIikNCm9wdGlvbnMoc2NpcGVuPTk5OSkNCnN1bW1hcnkobG9nKQ0KcHJlZCA8LSBwcmVkaWN0KGxvZywgdmFsaWQuZGYsIHR5cGUgPSAicmVzcG9uc2UiKQ0KY29uZnVzaW9uTWF0cml4KGFzLmZhY3RvcihpZmVsc2UocHJlZCA+IDAuNSwgMSwgMCkpLA0KdmFsaWQuZGYkYGNsdWJfMTVfMTZfMTckQ29tYl9SaXNrX1R3b2ApDQpgYGANCiMjQ0FSVCBmb3IgYSlDb21iX1Jpc2tfT25lDQpgYGB7cn0NCmxpYnJhcnkocnBhcnQpDQpjdCA8LSBycGFydChgY2x1Yl8xNV8xNl8xNyRDb21iX1Jpc2tfT25lYCB+IC4sIGRhdGEgPSB0cmFpbi5kZiwgbWV0aG9kID0NCiJjbGFzcyIsIGNwID0gMCwgbWluc3BsaXQgPSAxKQ0KcHJlZC52YWwgPC0gcHJlZGljdChjdCx2YWxpZC5kZix0eXBlID0gImNsYXNzIikNCiMgZ2VuZXJhdGUgY29uZnVzaW9uIG1hdHJpeCBmb3IgdHJhaW5pbmcgZGF0YQ0KYGBgDQojI0NBUlQgZm9yIGEpQ29tYl9SaXNrX1R3bw0KYGBge3J9DQpsaWJyYXJ5KHJwYXJ0KQ0KY3QgPC0gcnBhcnQoYGNsdWJfMTVfMTZfMTckQ29tYl9SaXNrX1R3b2AgfiAuLCBkYXRhID0gdHJhaW4uZGYsIG1ldGhvZCA9DQoiY2xhc3MiLCBjcCA9IDAsIG1pbnNwbGl0ID0gMSkgcHJlZC52YWwgPC0gcHJlZGljdChjdCx2YWxpZC5kZix0eXBlID0gImNsYXNzIikNCiMgZ2VuZXJhdGUgY29uZnVzaW9uIG1hdHJpeCBmb3IgdHJhaW5pbmcgZGF0YQ0KY29uZnVzaW9uTWF0cml4KHByZWQudmFsLCB2YWxpZC5kZiRgY2x1Yl8xNV8xNl8xNyRDb21iX1Jpc2tfVHdvYCkNCmBgYA0KIyMgRnJvbSB0aGUgYWNjdXJhY3kgcmVzdWx0cyBpdCBjYW4gYmUgc2VlbiB0aGF0IG1vZGVscyBOYWl2ZSBCYXllcyxLTk4gYW5kIENBUlQNCnBlcmZvcm0gcmVhbGx5IHdlbGwuT2YgdGhpcyBOYWl2ZSBCYXllcyBhbmQgQ0FSVCBjbGFzc2lmaWVyIHBlcmZvcm1zIHNsaWdodGx5DQpiZXR0ZXIuDQoNCg==