Introduction

library(dplyr)
## Warning: package 'dplyr' was built under R version 3.5.3
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
library(caret)
## Warning: package 'caret' was built under R version 3.5.3
## Loading required package: lattice
## Loading required package: ggplot2
## Warning: package 'ggplot2' was built under R version 3.5.3
library(class)
## Warning: package 'class' was built under R version 3.5.3
library(rpart)
library(rpart.plot)
## Warning: package 'rpart.plot' was built under R version 3.5.3
library(randomForest)
## Warning: package 'randomForest' was built under R version 3.5.3
## randomForest 4.6-14
## Type rfNews() to see new features/changes/bug fixes.
## 
## Attaching package: 'randomForest'
## The following object is masked from 'package:ggplot2':
## 
##     margin
## The following object is masked from 'package:dplyr':
## 
##     combine
library(Metrics)
## Warning: package 'Metrics' was built under R version 3.5.3
## 
## Attaching package: 'Metrics'
## The following objects are masked from 'package:caret':
## 
##     precision, recall
library(pROC)
## Warning: package 'pROC' was built under R version 3.5.3
## Type 'citation("pROC")' for a citation.
## 
## Attaching package: 'pROC'
## The following object is masked from 'package:Metrics':
## 
##     auc
## The following objects are masked from 'package:stats':
## 
##     cov, smooth, var
library(tidyr)
## Warning: package 'tidyr' was built under R version 3.5.3
library(corrplot)
## Warning: package 'corrplot' was built under R version 3.5.3
## corrplot 0.84 loaded

Reading the data

Train_data<-read.csv("train.csv")
Test_data<-read.csv("test.csv")

Looking at data

print("The column names of Train data are:")
## [1] "The column names of Train data are:"
names(Train_data)
##  [1] "loan_id"                  "source"                  
##  [3] "financial_institution"    "interest_rate"           
##  [5] "unpaid_principal_bal"     "loan_term"               
##  [7] "origination_date"         "first_payment_date"      
##  [9] "loan_to_value"            "number_of_borrowers"     
## [11] "debt_to_income_ratio"     "borrower_credit_score"   
## [13] "loan_purpose"             "insurance_percent"       
## [15] "co.borrower_credit_score" "insurance_type"          
## [17] "m1"                       "m2"                      
## [19] "m3"                       "m4"                      
## [21] "m5"                       "m6"                      
## [23] "m7"                       "m8"                      
## [25] "m9"                       "m10"                     
## [27] "m11"                      "m12"                     
## [29] "m13"
print("The column names of Test data are:")
## [1] "The column names of Test data are:"
names(Test_data)
##  [1] "loan_id"                  "source"                  
##  [3] "financial_institution"    "interest_rate"           
##  [5] "unpaid_principal_bal"     "loan_term"               
##  [7] "origination_date"         "first_payment_date"      
##  [9] "loan_to_value"            "number_of_borrowers"     
## [11] "debt_to_income_ratio"     "borrower_credit_score"   
## [13] "loan_purpose"             "insurance_percent"       
## [15] "co.borrower_credit_score" "insurance_type"          
## [17] "m1"                       "m2"                      
## [19] "m3"                       "m4"                      
## [21] "m5"                       "m6"                      
## [23] "m7"                       "m8"                      
## [25] "m9"                       "m10"                     
## [27] "m11"                      "m12"

Looking at Dimensions

print("Dimensions od Train data:")
## [1] "Dimensions od Train data:"
dim(Train_data)
## [1] 116058     29
print("Dimensions of Test data")
## [1] "Dimensions of Test data"
dim(Test_data)
## [1] 35866    28

Looks like m13 is our Response variable โ€“Adding variables to combine our Test and Train.

Test_data$m13<-NA
Test_data$Set<-"Test"
Train_data$Set<-"Train"

Combining the Test and Train data for better cleaning

data<-rbind(Train_data,Test_data)

Data Cleaning

Looking at the structure first

str(data)
## 'data.frame':    151924 obs. of  30 variables:
##  $ loan_id                 : num  2.68e+11 6.73e+11 7.43e+11 6.01e+11 2.74e+11 ...
##  $ source                  : Factor w/ 3 levels "X","Y","Z": 3 2 3 1 1 2 1 2 1 1 ...
##  $ financial_institution   : Factor w/ 19 levels "Anderson-Taylor",..: 19 16 18 9 9 2 9 2 2 9 ...
##  $ interest_rate           : num  4.25 4.88 3.25 4.75 4.75 ...
##  $ unpaid_principal_bal    : int  214000 144000 366000 135000 124000 150000 59000 319000 520000 214000 ...
##  $ loan_term               : int  360 360 180 360 360 360 360 300 360 360 ...
##  $ origination_date        : Factor w/ 6 levels "2012-01-01","2012-02-01",..: 3 1 1 2 2 2 2 1 3 1 ...
##  $ first_payment_date      : Factor w/ 8 levels "02/2012","03/2012",..: 4 2 2 3 3 3 3 2 4 2 ...
##  $ loan_to_value           : int  95 72 49 46 80 80 95 62 76 95 ...
##  $ number_of_borrowers     : num  1 1 1 2 1 1 1 1 1 2 ...
##  $ debt_to_income_ratio    : num  22 44 33 44 43 46 44 45 35 41 ...
##  $ borrower_credit_score   : num  694 697 780 633 681 675 723 652 808 702 ...
##  $ loan_purpose            : Factor w/ 3 levels "A23","B12","C86": 3 2 2 2 3 3 3 1 3 1 ...
##  $ insurance_percent       : num  30 0 0 0 0 0 30 0 0 30 ...
##  $ co.borrower_credit_score: num  0 0 0 638 0 0 0 0 0 700 ...
##  $ insurance_type          : num  0 0 0 0 0 0 0 0 0 0 ...
##  $ m1                      : int  0 0 0 0 0 1 0 0 0 0 ...
##  $ m2                      : int  0 0 0 0 1 0 0 1 0 0 ...
##  $ m3                      : int  0 0 0 0 2 0 0 0 0 0 ...
##  $ m4                      : int  0 0 0 0 3 0 0 0 0 0 ...
##  $ m5                      : int  0 0 0 0 4 0 0 0 1 0 ...
##  $ m6                      : int  0 0 0 0 5 0 0 0 0 1 ...
##  $ m7                      : int  1 0 0 0 6 0 0 0 1 1 ...
##  $ m8                      : int  0 0 0 0 7 0 0 0 0 1 ...
##  $ m9                      : int  0 0 0 1 8 0 0 0 1 1 ...
##  $ m10                     : int  0 0 0 1 9 0 0 0 2 1 ...
##  $ m11                     : int  0 1 0 1 10 0 0 0 0 2 ...
##  $ m12                     : int  0 0 0 1 11 0 0 0 1 2 ...
##  $ m13                     : int  1 1 1 1 1 1 1 1 1 1 ...
##  $ Set                     : chr  "Train" "Train" "Train" "Train" ...

Missing Values

missing<-summarise_all(data,funs(sum(is.na(.))/n()))
## Warning: funs() is soft deprecated as of dplyr 0.8.0
## please use list() instead
## 
##   # Before:
##   funs(name = f(.))
## 
##   # After: 
##   list(name = ~ f(.))
## This warning is displayed once per session.
missing<-gather(missing,key="Feature",value="Missing")

g<-ggplot(data=missing,aes(x=reorder(Feature,-Missing),y=Missing))
g<-g+geom_bar(stat="identity")+coord_flip()
g

Origination date

levels(data$origination_date)
## [1] "2012-01-01" "2012-02-01" "2012-03-01" "01/01/12"   "01/02/12"  
## [6] "01/03/12"

Converting levels into same format

levels(data$origination_date)<-c("2012-01-01","2012-02-01","2012-03-01","2012-01-01","2012-02-01","2012-03-01")

Looking at it after changing levels

levels(data$origination_date)
## [1] "2012-01-01" "2012-02-01" "2012-03-01"

Converting to date format

data$origination_date<-strptime(data$origination_date,"%Y-%m-%d")

Converting to Date

data$origination_date<-as.POSIXct(data$origination_date)

First Payment date

levels(data$first_payment_date)
## [1] "02/2012" "03/2012" "04/2012" "05/2012" "Apr-12"  "Feb-12"  "Mar-12" 
## [8] "May-12"

Converting levels into same format

levels(data$first_payment_date)<-c("2012-02-01","2012-03-01","2012-04-01","2012-05-01","2012-04-01","2012-02-01","2012-03-01","2012-05-01")

Looking at levels after changing

levels(data$first_payment_date)
## [1] "2012-02-01" "2012-03-01" "2012-04-01" "2012-05-01"

Converting into date format

data$first_payment_date<-strptime(data$first_payment_date,"%Y-%m-%d")

Converting to a date

data$first_payment_date<-as.POSIXct(data$first_payment_date)

Financial instutiation

levels(data$financial_institution)
##  [1] "Anderson-Taylor"             "Browning-Hart"              
##  [3] "Chapman-Mcmahon"             "Cole, Brooks and Vincent"   
##  [5] "Edwards-Hoffman"             "Martinez, Duffy and Bird"   
##  [7] "Miller, Mcclure and Allen"   "Nicholson Group"            
##  [9] "OTHER"                       "Richards-Walters"           
## [11] "Richardson Ltd"              "Romero, Woods and Johnson"  
## [13] "Sanchez-Robinson"            "Sanchez, Hays and Wilkerson"
## [15] "Suarez Inc"                  "Swanson, Newton and Miller" 
## [17] "Taylor, Hunt and Rodriguez"  "Thornton-Davis"             
## [19] "Turner, Baldwin and Rhodes"
data$financial_institution<-NULL

Looking at the variables again

str(data)
## 'data.frame':    151924 obs. of  28 variables:
##  $ loan_id                 : num  2.68e+11 6.73e+11 7.43e+11 6.01e+11 2.74e+11 ...
##  $ source                  : Factor w/ 3 levels "X","Y","Z": 3 2 3 1 1 2 1 2 1 1 ...
##  $ interest_rate           : num  4.25 4.88 3.25 4.75 4.75 ...
##  $ unpaid_principal_bal    : int  214000 144000 366000 135000 124000 150000 59000 319000 520000 214000 ...
##  $ loan_term               : int  360 360 180 360 360 360 360 300 360 360 ...
##  $ loan_to_value           : int  95 72 49 46 80 80 95 62 76 95 ...
##  $ number_of_borrowers     : num  1 1 1 2 1 1 1 1 1 2 ...
##  $ debt_to_income_ratio    : num  22 44 33 44 43 46 44 45 35 41 ...
##  $ borrower_credit_score   : num  694 697 780 633 681 675 723 652 808 702 ...
##  $ loan_purpose            : Factor w/ 3 levels "A23","B12","C86": 3 2 2 2 3 3 3 1 3 1 ...
##  $ insurance_percent       : num  30 0 0 0 0 0 30 0 0 30 ...
##  $ co.borrower_credit_score: num  0 0 0 638 0 0 0 0 0 700 ...
##  $ insurance_type          : num  0 0 0 0 0 0 0 0 0 0 ...
##  $ m1                      : int  0 0 0 0 0 1 0 0 0 0 ...
##  $ m2                      : int  0 0 0 0 1 0 0 1 0 0 ...
##  $ m3                      : int  0 0 0 0 2 0 0 0 0 0 ...
##  $ m4                      : int  0 0 0 0 3 0 0 0 0 0 ...
##  $ m5                      : int  0 0 0 0 4 0 0 0 1 0 ...
##  $ m6                      : int  0 0 0 0 5 0 0 0 0 1 ...
##  $ m7                      : int  1 0 0 0 6 0 0 0 1 1 ...
##  $ m8                      : int  0 0 0 0 7 0 0 0 0 1 ...
##  $ m9                      : int  0 0 0 1 8 0 0 0 1 1 ...
##  $ m10                     : int  0 0 0 1 9 0 0 0 2 1 ...
##  $ m11                     : int  0 1 0 1 10 0 0 0 0 2 ...
##  $ m12                     : int  0 0 0 1 11 0 0 0 1 2 ...
##  $ m13                     : int  1 1 1 1 1 1 1 1 1 1 ...
##  $ Set                     : chr  "Train" "Train" "Train" "Train" ...
##  $ Paid_After              : num  61 60 60 60 60 60 60 60 61 60 ...

Insurance type

summary(data$insurance_type)
##     Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
## 0.000000 0.000000 0.000000 0.003239 0.000000 1.000000
data$insurance_type<-as.factor(data$insurance_type)
levels(data$insurance_type)
## [1] "0" "1"

loan performance

for(i in 1:nrow(data)){
  data[i,"loan_performance"]<-sum(data[i,c(14:25)])
}
data[,c(14:25)]<-NULL

Number of borrowers

data$number_of_borrowers<-as.factor(data$number_of_borrowers)

Income

data$income<-data$unpaid_principal_bal/data$debt_to_income_ratio
data$unpaid_principal_bal<-NULL
data$debt_to_income_ratio<-NULL

Insurance percent

data$insurance_percent<-NULL

Scaling

data_scale<-data[,-c(1,2,6,8,10,11,12)]
preproc<-preProcess(data_scale,c("center","scale"))
data_scale<-predict(preproc,data_scale)
data[,-c(1,2,6,8,10,11,12)]<-data_scale

Outliers

#data$loan_performance<-ifelse(data$loan_performance>2,mean(data$loan_performance),data$loan_performance)

Looking at the data again

str(data)
## 'data.frame':    151924 obs. of  15 variables:
##  $ loan_id                 : num  2.68e+11 6.73e+11 7.43e+11 6.01e+11 2.74e+11 ...
##  $ source                  : Factor w/ 3 levels "X","Y","Z": 3 2 3 1 1 2 1 2 1 1 ...
##  $ interest_rate           : num  0.825 2.181 -1.345 1.91 1.91 ...
##  $ loan_term               : num  0.753 0.753 -1.255 0.753 0.753 ...
##  $ loan_to_value           : num  1.596 0.265 -1.066 -1.24 0.728 ...
##  $ number_of_borrowers     : Factor w/ 2 levels "1","2": 1 1 1 2 1 1 1 1 1 2 ...
##  $ borrower_credit_score   : num  -1.803 -1.732 0.239 -3.252 -2.112 ...
##  $ loan_purpose            : Factor w/ 3 levels "A23","B12","C86": 3 2 2 2 3 3 3 1 3 1 ...
##  $ co.borrower_credit_score: num  -1.206 -1.206 -1.206 0.465 -1.206 ...
##  $ insurance_type          : Factor w/ 2 levels "0","1": 1 1 1 1 1 1 1 1 1 1 ...
##  $ m13                     : int  1 1 1 1 1 1 1 1 1 1 ...
##  $ Set                     : chr  "Train" "Train" "Train" "Train" ...
##  $ Paid_After              : num  0.0926 -0.0452 -0.0452 -0.0452 -0.0452 ...
##  $ loan_performance        : num  1.0292 1.0292 -0.0549 4.2814 71.4931 ...
##  $ income                  : num  0.324 -0.692 0.538 -0.724 -0.753 ...

Exploratory data analysis

Train<-filter(data,Set=="Train")

Correlation

co<-cor(data_scale)
corrplot(co)

#data[,c(16:25)]<-NULL
data$loan_term<-NULL

Prediction

Preprocessing

Train<-filter(data,Set=="Train")
Train$m13<-as.factor(Train$m13)
Test<-filter(data,Set=="Test")
Train$Set<-NULL
Test$Set<-NULL

split<-sample(nrow(Train),nrow(Train)*.7)
Train<-Train[split,]
Train_Validated<-Train[-split,]

Using random forest

rf<-randomForest(m13~.-loan_id,Train,ntree=500)

Metrics of Prediction

Predicting for Validated set

pred_valid<-predict(rf,Train_Validated,type="response")
confusionMatrix(Train_Validated$m13,pred_valid)
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction     0     1
##          0 24281     0
##          1    25   106
##                                           
##                Accuracy : 0.999           
##                  95% CI : (0.9985, 0.9993)
##     No Information Rate : 0.9957          
##     P-Value [Acc > NIR] : < 2.2e-16       
##                                           
##                   Kappa : 0.894           
##                                           
##  Mcnemar's Test P-Value : 1.587e-06       
##                                           
##             Sensitivity : 0.9990          
##             Specificity : 1.0000          
##          Pos Pred Value : 1.0000          
##          Neg Pred Value : 0.8092          
##              Prevalence : 0.9957          
##          Detection Rate : 0.9946          
##    Detection Prevalence : 0.9946          
##       Balanced Accuracy : 0.9995          
##                                           
##        'Positive' Class : 0               
## 

Predicting for test

dt_Test<-rpart.predict(rf,newdata = Test,type="response")
sub<-data.frame(loan_id=Test$loan_id,m13=dt_Test)
write.csv(sub,"subrf.csv",row.names = F)
LS0tDQp0aXRsZTogIkluZGlhIE1MIEhhY2thdGhvbiINCmF1dGhvcjogIlNoYXNod2F0Ig0Kb3V0cHV0OiANCiAgaHRtbF9kb2N1bWVudDoNCiAgICBrZWVwX21kOiB0cnVlDQogICAgdG9jOiB0cnVlDQogICAgdG9jX2Zsb2F0OiB0cnVlDQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KLS0tDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KA0KICBmaWcucGF0aCA9ICJGaWdzL2ZpZy0iDQopDQpgYGANCg0KIyBJbnRyb2R1Y3Rpb24NCg0KYGBge1J9DQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShjYXJldCkNCmxpYnJhcnkoY2xhc3MpDQpsaWJyYXJ5KHJwYXJ0KQ0KbGlicmFyeShycGFydC5wbG90KQ0KbGlicmFyeShyYW5kb21Gb3Jlc3QpDQpsaWJyYXJ5KE1ldHJpY3MpDQpsaWJyYXJ5KHBST0MpDQpsaWJyYXJ5KHRpZHlyKQ0KbGlicmFyeShjb3JycGxvdCkNCmBgYA0KDQpSZWFkaW5nIHRoZSBkYXRhDQpgYGB7cn0NClRyYWluX2RhdGE8LXJlYWQuY3N2KCJ0cmFpbi5jc3YiKQ0KVGVzdF9kYXRhPC1yZWFkLmNzdigidGVzdC5jc3YiKQ0KYGBgDQoNCkxvb2tpbmcgYXQgZGF0YQ0KDQpgYGB7cn0NCnByaW50KCJUaGUgY29sdW1uIG5hbWVzIG9mIFRyYWluIGRhdGEgYXJlOiIpDQpuYW1lcyhUcmFpbl9kYXRhKQ0KcHJpbnQoIlRoZSBjb2x1bW4gbmFtZXMgb2YgVGVzdCBkYXRhIGFyZToiKQ0KbmFtZXMoVGVzdF9kYXRhKQ0KYGBgDQoNCkxvb2tpbmcgYXQgRGltZW5zaW9ucw0KYGBge3J9DQpwcmludCgiRGltZW5zaW9ucyBvZCBUcmFpbiBkYXRhOiIpDQpkaW0oVHJhaW5fZGF0YSkNCnByaW50KCJEaW1lbnNpb25zIG9mIFRlc3QgZGF0YSIpDQpkaW0oVGVzdF9kYXRhKQ0KYGBgDQoNCkxvb2tzIGxpa2UgbTEzIGlzIG91ciBSZXNwb25zZSB2YXJpYWJsZQ0KLS1BZGRpbmcgdmFyaWFibGVzIHRvIGNvbWJpbmUgb3VyIFRlc3QgYW5kIFRyYWluLg0KDQpgYGB7cn0NClRlc3RfZGF0YSRtMTM8LU5BDQpUZXN0X2RhdGEkU2V0PC0iVGVzdCINClRyYWluX2RhdGEkU2V0PC0iVHJhaW4iDQpgYGANCg0KQ29tYmluaW5nIHRoZSBUZXN0IGFuZCBUcmFpbiBkYXRhIGZvciBiZXR0ZXIgY2xlYW5pbmcNCmBgYHtyfQ0KZGF0YTwtcmJpbmQoVHJhaW5fZGF0YSxUZXN0X2RhdGEpDQpgYGANCg0KI0RhdGEgQ2xlYW5pbmcNCg0KTG9va2luZyBhdCB0aGUgc3RydWN0dXJlIGZpcnN0DQpgYGB7cn0NCnN0cihkYXRhKQ0KYGBgDQojI01pc3NpbmcgVmFsdWVzDQpgYGB7cn0NCm1pc3Npbmc8LXN1bW1hcmlzZV9hbGwoZGF0YSxmdW5zKHN1bShpcy5uYSguKSkvbigpKSkNCm1pc3Npbmc8LWdhdGhlcihtaXNzaW5nLGtleT0iRmVhdHVyZSIsdmFsdWU9Ik1pc3NpbmciKQ0KDQpnPC1nZ3Bsb3QoZGF0YT1taXNzaW5nLGFlcyh4PXJlb3JkZXIoRmVhdHVyZSwtTWlzc2luZykseT1NaXNzaW5nKSkNCmc8LWcrZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiKStjb29yZF9mbGlwKCkNCmcNCmBgYA0KDQojIyBPcmlnaW5hdGlvbiBkYXRlDQpgYGB7Un0NCmxldmVscyhkYXRhJG9yaWdpbmF0aW9uX2RhdGUpDQpgYGANCg0KICBDb252ZXJ0aW5nIGxldmVscyBpbnRvIHNhbWUgZm9ybWF0DQoNCmBgYHtyfQ0KbGV2ZWxzKGRhdGEkb3JpZ2luYXRpb25fZGF0ZSk8LWMoIjIwMTItMDEtMDEiLCIyMDEyLTAyLTAxIiwiMjAxMi0wMy0wMSIsIjIwMTItMDEtMDEiLCIyMDEyLTAyLTAxIiwiMjAxMi0wMy0wMSIpDQpgYGANCg0KICBMb29raW5nIGF0IGl0IGFmdGVyIGNoYW5naW5nIGxldmVscw0KYGBge3J9DQpsZXZlbHMoZGF0YSRvcmlnaW5hdGlvbl9kYXRlKQ0KYGBgDQoNCiAgQ29udmVydGluZyB0byBkYXRlIGZvcm1hdA0KYGBge3J9DQpkYXRhJG9yaWdpbmF0aW9uX2RhdGU8LXN0cnB0aW1lKGRhdGEkb3JpZ2luYXRpb25fZGF0ZSwiJVktJW0tJWQiKQ0KYGBgDQoNCiAgQ29udmVydGluZyB0byBEYXRlDQogIA0KYGBge3J9DQpkYXRhJG9yaWdpbmF0aW9uX2RhdGU8LWFzLlBPU0lYY3QoZGF0YSRvcmlnaW5hdGlvbl9kYXRlKQ0KYGBgDQoNCiMjIEZpcnN0IFBheW1lbnQgZGF0ZQ0KYGBge3J9DQpsZXZlbHMoZGF0YSRmaXJzdF9wYXltZW50X2RhdGUpDQpgYGANCiAgDQogIENvbnZlcnRpbmcgbGV2ZWxzIGludG8gc2FtZSBmb3JtYXQNCmBgYHtyfQ0KbGV2ZWxzKGRhdGEkZmlyc3RfcGF5bWVudF9kYXRlKTwtYygiMjAxMi0wMi0wMSIsIjIwMTItMDMtMDEiLCIyMDEyLTA0LTAxIiwiMjAxMi0wNS0wMSIsIjIwMTItMDQtMDEiLCIyMDEyLTAyLTAxIiwiMjAxMi0wMy0wMSIsIjIwMTItMDUtMDEiKQ0KYGBgDQoNCiAgTG9va2luZyBhdCBsZXZlbHMgYWZ0ZXIgY2hhbmdpbmcNCmBgYHtyfQ0KbGV2ZWxzKGRhdGEkZmlyc3RfcGF5bWVudF9kYXRlKQ0KYGBgDQoNCiAgQ29udmVydGluZyBpbnRvIGRhdGUgZm9ybWF0DQpgYGB7cn0NCmRhdGEkZmlyc3RfcGF5bWVudF9kYXRlPC1zdHJwdGltZShkYXRhJGZpcnN0X3BheW1lbnRfZGF0ZSwiJVktJW0tJWQiKQ0KYGBgDQoNCiAgQ29udmVydGluZyB0byBhIGRhdGUNCmBgYHtSfQ0KZGF0YSRmaXJzdF9wYXltZW50X2RhdGU8LWFzLlBPU0lYY3QoZGF0YSRmaXJzdF9wYXltZW50X2RhdGUpDQpgYGANCg0KIyMgUGFpZCBBZnRlciBEYXlzDQpgYGB7cn0NCmRhdGEkUGFpZF9BZnRlcjwtYXMubnVtZXJpYyhkYXRhJGZpcnN0X3BheW1lbnRfZGF0ZS1kYXRhJG9yaWdpbmF0aW9uX2RhdGUpDQpkYXRhJG9yaWdpbmF0aW9uX2RhdGU8LU5VTEwNCmRhdGEkZmlyc3RfcGF5bWVudF9kYXRlPC1OVUxMDQpgYGANCg0KIyMgRmluYW5jaWFsIGluc3R1dGlhdGlvbg0KYGBge3J9DQpsZXZlbHMoZGF0YSRmaW5hbmNpYWxfaW5zdGl0dXRpb24pDQpkYXRhJGZpbmFuY2lhbF9pbnN0aXR1dGlvbjwtTlVMTA0KYGBgDQoNCiAgTG9va2luZyBhdCB0aGUgdmFyaWFibGVzIGFnYWluDQpgYGB7cn0NCnN0cihkYXRhKQ0KYGBgDQoNCg0KIyMgSW5zdXJhbmNlIHR5cGUNCmBgYHtyfQ0Kc3VtbWFyeShkYXRhJGluc3VyYW5jZV90eXBlKQ0KZGF0YSRpbnN1cmFuY2VfdHlwZTwtYXMuZmFjdG9yKGRhdGEkaW5zdXJhbmNlX3R5cGUpDQpsZXZlbHMoZGF0YSRpbnN1cmFuY2VfdHlwZSkNCmBgYA0KIyMgbG9hbiBwZXJmb3JtYW5jZQ0KYGBge3J9DQpmb3IoaSBpbiAxOm5yb3coZGF0YSkpew0KICBkYXRhW2ksImxvYW5fcGVyZm9ybWFuY2UiXTwtc3VtKGRhdGFbaSxjKDE0OjI1KV0pDQp9DQpgYGANCg0KYGBge3J9DQpkYXRhWyxjKDE0OjI1KV08LU5VTEwNCmBgYA0KDQojIyBOdW1iZXIgb2YgYm9ycm93ZXJzDQpgYGB7cn0NCmRhdGEkbnVtYmVyX29mX2JvcnJvd2VyczwtYXMuZmFjdG9yKGRhdGEkbnVtYmVyX29mX2JvcnJvd2VycykNCmBgYA0KDQojIyBJbmNvbWUNCmBgYHtyfQ0KZGF0YSRpbmNvbWU8LWRhdGEkdW5wYWlkX3ByaW5jaXBhbF9iYWwvZGF0YSRkZWJ0X3RvX2luY29tZV9yYXRpbw0KZGF0YSR1bnBhaWRfcHJpbmNpcGFsX2JhbDwtTlVMTA0KZGF0YSRkZWJ0X3RvX2luY29tZV9yYXRpbzwtTlVMTA0KYGBgDQoNCiMjIEluc3VyYW5jZSBwZXJjZW50DQpgYGB7cn0NCmRhdGEkaW5zdXJhbmNlX3BlcmNlbnQ8LU5VTEwNCmBgYA0KDQojIyBTY2FsaW5nDQpgYGB7cn0NCmRhdGFfc2NhbGU8LWRhdGFbLC1jKDEsMiw2LDgsMTAsMTEsMTIpXQ0KcHJlcHJvYzwtcHJlUHJvY2VzcyhkYXRhX3NjYWxlLGMoImNlbnRlciIsInNjYWxlIikpDQpkYXRhX3NjYWxlPC1wcmVkaWN0KHByZXByb2MsZGF0YV9zY2FsZSkNCmRhdGFbLC1jKDEsMiw2LDgsMTAsMTEsMTIpXTwtZGF0YV9zY2FsZQ0KYGBgDQoNCiMjIE91dGxpZXJzDQpgYGB7cn0NCiNkYXRhJGxvYW5fcGVyZm9ybWFuY2U8LWlmZWxzZShkYXRhJGxvYW5fcGVyZm9ybWFuY2U+MixtZWFuKGRhdGEkbG9hbl9wZXJmb3JtYW5jZSksZGF0YSRsb2FuX3BlcmZvcm1hbmNlKQ0KYGBgDQoNCkxvb2tpbmcgYXQgdGhlIGRhdGEgYWdhaW4NCmBgYHtyfQ0Kc3RyKGRhdGEpDQpgYGANCg0KIyBFeHBsb3JhdG9yeSBkYXRhIGFuYWx5c2lzDQpgYGB7cn0NClRyYWluPC1maWx0ZXIoZGF0YSxTZXQ9PSJUcmFpbiIpDQpgYGANCg0KIyMgQ29ycmVsYXRpb24NCmBgYHtSfQ0KY288LWNvcihkYXRhX3NjYWxlKQ0KY29ycnBsb3QoY28pDQpgYGANCg0KYGBge3J9DQojZGF0YVssYygxNjoyNSldPC1OVUxMDQpkYXRhJGxvYW5fdGVybTwtTlVMTA0KYGBgDQoNCiMgUHJlZGljdGlvbg0KDQojIyBQcmVwcm9jZXNzaW5nDQpgYGB7cn0NClRyYWluPC1maWx0ZXIoZGF0YSxTZXQ9PSJUcmFpbiIpDQpUcmFpbiRtMTM8LWFzLmZhY3RvcihUcmFpbiRtMTMpDQpUZXN0PC1maWx0ZXIoZGF0YSxTZXQ9PSJUZXN0IikNClRyYWluJFNldDwtTlVMTA0KVGVzdCRTZXQ8LU5VTEwNCg0Kc3BsaXQ8LXNhbXBsZShucm93KFRyYWluKSxucm93KFRyYWluKSouNykNClRyYWluPC1UcmFpbltzcGxpdCxdDQpUcmFpbl9WYWxpZGF0ZWQ8LVRyYWluWy1zcGxpdCxdDQpgYGANCg0KIyMgVXNpbmcgcmFuZG9tIGZvcmVzdA0KYGBge3J9DQpyZjwtcmFuZG9tRm9yZXN0KG0xM34uLWxvYW5faWQsVHJhaW4sbnRyZWU9NTAwKQ0KYGBgDQoNCiMjIyBNZXRyaWNzIG9mIFByZWRpY3Rpb24NCg0KUHJlZGljdGluZyBmb3IgVmFsaWRhdGVkIHNldA0KYGBge3J9DQpwcmVkX3ZhbGlkPC1wcmVkaWN0KHJmLFRyYWluX1ZhbGlkYXRlZCx0eXBlPSJyZXNwb25zZSIpDQpjb25mdXNpb25NYXRyaXgoVHJhaW5fVmFsaWRhdGVkJG0xMyxwcmVkX3ZhbGlkKQ0KYGBgDQoNCg0KUHJlZGljdGluZyBmb3IgdGVzdA0KYGBge1J9DQpkdF9UZXN0PC1ycGFydC5wcmVkaWN0KHJmLG5ld2RhdGEgPSBUZXN0LHR5cGU9InJlc3BvbnNlIikNCnN1YjwtZGF0YS5mcmFtZShsb2FuX2lkPVRlc3QkbG9hbl9pZCxtMTM9ZHRfVGVzdCkNCndyaXRlLmNzdihzdWIsInN1YnJmLmNzdiIscm93Lm5hbWVzID0gRikNCmBgYA0KDQo=