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)
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)
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==