Required packages

Provide the packages required to reproduce the report. Make sure you fulfilled the minimum requirement #10.

library(readr)
library(tidyr)
library(dplyr)
library(lubridate)
library(MVN)
library(car)
library(forecast)
library(caret) 

Executive Summary

To achieve the high accuracy and precision of Machine learning models, data needs to be in clean format. To prepare such data there are number of tasks involved in it. The tasks includes data cleaning, data transformation, dealing with missing values and outliers and correct data inconsistencies. Another important factor is that the data has to be in tidy format.The data has around 19% of the missing values with some of the columns having more than 50% of the data is missing.Caaping method used to deal with the outliers present in the data.Furthermore, there is a huge variation between the ranges of the varibles which normalised using BoxCox transformation.

Data

Two datasets has been selected from kaggle website.[7] The data is about bank loan application. First file(application_train), contains the static data of all applications, one row represents one loan application. Second file(bureau) contains the applicants previous credits provided by other financial institutes & that were reported to credit bureau(for clients who have a loan application in first file). For every record in first file there are multiple records in second file.below are the steps to get the data. * Due to large number of volume in both the file, the data has been filtered on the basis of “SK_ID_CURR” column which is common in both the files and stored in separate CSV files to perform data preprocessing tasks.

##original source files
loan_app_source<-read.csv("application_train.csv")
bureau_source<-read.csv("bureau.csv")

##filter the data
loan_app_filtered<- loan_app_source %>% filter(SK_ID_CURR<=101156)
bureau_filtered<-bureau_source %>% filter(SK_ID_CURR<=101156)

#export filtered data
write.csv(loan_app_filtered, file = "loan_app_filt.csv",row.names=FALSE)
write.csv(bureau_filtered, file = "bureau_filt.csv",row.names=FALSE)
loan_app<-read.csv("loan_app_filt.csv")
bureau<-read.csv("bureau_filt.csv")
#combined two datasets
combined<- loan_app %>% left_join(bureau,by="SK_ID_CURR") 

Understand

To understand the data it is important to check the dimensions and data types of all the variables.

#check the dimention of the data frame
dim(combined)
[1] 4496  109
#check the data types
sapply(combined[1:20],class) # showing only first 20 variables as the compelete lsit long
                SK_ID_CURR                     TARGET         NAME_CONTRACT_TYPE 
                 "integer"                  "integer"                   "factor" 
               CODE_GENDER               FLAG_OWN_CAR            FLAG_OWN_REALTY 
                  "factor"                   "factor"                   "factor" 
              CNT_CHILDREN           AMT_INCOME_TOTAL                 AMT_CREDIT 
                 "integer"                  "numeric"                  "numeric" 
             AMT_ANNUITY.x            AMT_GOODS_PRICE            NAME_TYPE_SUITE 
                 "numeric"                  "numeric"                   "factor" 
          NAME_INCOME_TYPE        NAME_EDUCATION_TYPE         NAME_FAMILY_STATUS 
                  "factor"                   "factor"                   "factor" 
         NAME_HOUSING_TYPE REGION_POPULATION_RELATIVE                 DAYS_BIRTH 
                  "factor"                  "numeric"                  "integer" 
             DAYS_EMPLOYED          DAYS_REGISTRATION 
                 "integer"                  "integer" 
head(combined,5)

The results of the above chunk shows that the data fulfills the minimum requirements 2-4

#datatype conversion
#numeric->factor
combined$FLAG_MOBIL<-factor(combined$FLAG_MOBIL)
combined$FLAG_EMP_PHONE<-factor(combined$FLAG_EMP_PHONE)
combined$FLAG_WORK_PHONE<-factor(combined$FLAG_WORK_PHONE)
#numeric->date
a<-ddays(combined$DAYS_BIRTH)
##assume the date on whoch data is genereted is 18th May 2018
birth_date<-as.Date("2018-05-18")+a
class(birth_date)
[1] "Date"
#factor variable from unordered to ordered
##check the levels of factor variable
levels(combined$WEEKDAY_APPR_PROCESS_START)
[1] "FRIDAY"    "MONDAY"    "SATURDAY"  "SUNDAY"    "THURSDAY"  "TUESDAY"   "WEDNESDAY"
#order the levels
combined$WEEKDAY_APPR_PROCESS_START<- ordered(combined$WEEKDAY_APPR_PROCESS_START,levels=c("MONDAY","TUESDAY","WEDNESDAY","THURSDAY","FRIDAY","SATURDAY","SUNDAY"))
#re check whether the levels has been ordered or not
levels(combined$WEEKDAY_APPR_PROCESS_START)
[1] "MONDAY"    "TUESDAY"   "WEDNESDAY" "THURSDAY"  "FRIDAY"    "SATURDAY"  "SUNDAY"   

Tidy & Manipulate Data I

The data is in tidy format as it shows the characteristics of tidy data as below

Tidy & Manipulate Data II

To arrive at a decision of whether to approve the credit or to disapprove two more important predictors can be created from existing data, AGE and AMOUNT remain in hand after paying any ongoing credit. Hence tried creating new variables on the basis of available information.

#create new column in data frame"AGE" wchich will store th client's age
combined$AGE<-round(interval(start = birth_date, end = "2018-05-18") / 
  duration(num = 1, units = "years"))
#create new variable which will show the remaining annual amount from income after decucting the loan amount
combined<-transform(combined,INCOME_MINUS_ANUITY= AMT_INCOME_TOTAL- AMT_ANNUITY.x)
cols<-c('SK_ID_CURR','DAYS_BIRTH','AGE','AMT_INCOME_TOTAL','AMT_ANNUITY.x','INCOME_MINUS_ANUITY')
tail(combined[cols],5)

Scan I

Below chunk shows the amount of missing values in data and how to deal with them.

#missing values scan
# number of columns having missing values
missing_val_cols<-colnames(combined)[colSums(is.na(combined)) > 0]
length(missing_val_cols)
[1] 74
#total percentage of missing values
sum(is.na(combined)) / (nrow(combined) *ncol(combined))
[1] 0.1965631
#colSums(is.na(combined)) # as the result is long 
# Handeling missing values
#remove the columns where there are more than 50% values are missing
mis_cols_large<-colnames(combined)[colSums(is.na(combined)) > 2200] 
combined<- combined %>% select(-one_of(mis_cols_large))
dim(combined)
[1] 4647  109
#replace missing values with the Median
for(i in which(sapply(combined,is.numeric))){
  combined[is.na(combined[,i]), i] <- median(combined[,i], na.rm = TRUE)
}
missing_val_cols<-colnames(combined)[colSums(is.na(combined)) > 0]
length(missing_val_cols)
[1] 3
dim(combined)
[1] 4647  109
#still all the factor column contains 151 missing values
combined<-combined[complete.cases(combined),]
print('complete cases ')
[1] "complete cases "
dim(combined)
[1] 4496  109
#inconsistancies check
colnames(combined)[apply(combined, 2, function(x) any(is.infinite(x)))=='TRUE']
character(0)
colnames(combined)[apply(combined, 2, function(x) any(is.finite(x)))=='TRUE']
character(0)
colnames(combined)[apply(combined, 2, function(x) any(is.nan(x)))=='TRUE']
character(0)
##check for edit rules

Scan II

To perform outlier detection created subset with 11 numeric columns.

##get the numeric columns
num_cols <- unlist(lapply(combined, is.numeric)) 
combined_sub<-combined[ , num_cols] ##90 NUmeric variables
# creating subset with 12 features to perfrom outliers detection and data normalization
cols<-c("AMT_INCOME_TOTAL","AMT_CREDIT","AMT_ANNUITY.x","AMT_GOODS_PRICE","AGE",
        "DAYS_EMPLOYED","EXT_SOURCE_2","EXT_SOURCE_3","APARTMENTS_AVG",
        "TOTALAREA_MODE","DAYS_CREDIT_ENDDATE")
combined_sub<-combined_sub[cols]
combined_sub<-combined_sub[complete.cases(combined_sub),]
# detect ouliers
par(mar=c(10,6,4,1)+.2)
Boxplot(combined_sub,las=2) 
  [1] "1521" "1522" "1523" "1872" "225"  "226"  "227"  "228"  "229"  "267"  "872"  "1627"
 [13] "1628" "1629" "1630" "1631" "1632" "1633" "1634" "1635" "273"  "274"  "275"  "276" 
 [25] "4052" "4053" "4054" "872"  "4031" "4032" "872"  "1627" "1628" "1629" "1630" "1631"
 [37] "1632" "1633" "1634" "1635" "1255" "1256" "1257" "1258" "1259" "1260" "1261" "1262"
 [49] "1263" "1264" "40"   "41"   "42"   "43"   "53"   "54"   "55"   "56"   "98"   "99"  
 [61] "2895" "2896" "2897" "2898" "2899" "2900" "2901" "2902" "4607" "3994" "2592" "2593"
 [73] "2594" "2595" "2596" "4163" "4629" "4630" "4631" "1868" "2573" "2574" "2575" "2576"
 [85] "2577" "2578" "2579" "2580" "2581" "2582" "1223" "1224" "1225" "1226" "1227" "1228"
 [97] "1229" "1230" "1231" "4629" "2573" "2574" "2575" "2576" "2577" "2578" "2579" "2580"
[109] "2581" "2582" "3185" "3432" "4063" "3451" "2928" "4525" "700"  "933"  "657"  "4159"

#Used capping method to replace outliers in the 
cap <- function(x){
  quantiles <- quantile( x, c(.05, 0.25, 0.75, .95 ) )
  x[ x < quantiles[2] - 1.5*IQR(x) ] <- quantiles[1]
  x[ x > quantiles[3] + 1.5*IQR(x) ] <- quantiles[4]
  x
}
comb_sub_wo_out<-apply(combined_sub, 2, FUN=cap)
#boxplot after outlier capping
par(mar=c(10,6,4,1)+.2)
Boxplot(comb_sub_wo_out,las=2)
 [1] "40"  "41"  "42"  "43"  "53"  "54"  "55"  "56"  "98"  "99"  "1"   "2"   "3"   "4"  
[15] "5"   "6"   "7"   "8"   "78"  "79"  "64"  "65"  "66"  "67"  "68"  "69"  "98"  "99" 
[29] "100" "101" "1"   "2"   "3"   "4"   "5"   "6"   "7"   "8"   "78"  "79"  "64"  "65" 
[43] "66"  "67"  "68"  "69"  "98"  "99"  "100" "101"

Transform

First, selected five variables to see the distribution of them in the data.Plotted histograms to see the distribution.

#check the need for normalization
colnames <- colnames(comb_sub_wo_out)
par(mfrow = c(3, 2))
for (i in 1:5) {
  hist(unlist(comb_sub_wo_out[,i]), main = colnames[i])
}

boxcoxnorm <- apply(comb_sub_wo_out, 2, function(x) BoxCox(x, lambda = "auto"))
NA/Inf replaced by maximum positive value
#histograms after normalisation
par(mfrow = c(3, 2))
for (i in 1:5) {
  hist(unlist(boxcoxnorm[,i]), main = colnames[i])
}

Feature Selection and Extraction

The dataset consists of large set of variables and most of the variables are correlated hence there si need to find few important variables. Principal component analysis is one of the method which extract important variables from large set.

#select only numeric columns
num_cols <- unlist(lapply(combined, is.numeric)) 
combined_mod<-combined[ , num_cols] ##4496
combined_mod <- combined_mod[complete.cases(combined_mod),]
#remove zero variance columns from data
combined_mod<-combined_mod[ , apply(combined_mod, 2, var) != 0]
#remove ID related columns form the data
combined_mod<-combined_mod %>% select(-c(SK_ID_CURR,SK_ID_BUREAU))
dim(combined_mod)
[1] 4496   76
#apply PCA
pca_combined <- prcomp(combined_mod, scale. = T)
#compute standard deviation of each principal component
std_dev <- pca_combined$sdev
#compute variance
pr_var <- std_dev^2
#check variance of first 10 components
pr_var[1:10]
 [1] 9.971499 4.350355 3.673241 3.291408 2.884132 2.759728 2.538433 2.322973 1.949930
[10] 1.786376
#aim is to find maximum variance.higher is the variance, higher will be the information.
#proportion of variance explained
prop_varex <- pr_var/sum(pr_var)
prop_varex[1:10]
 [1] 0.13120394 0.05724151 0.04833212 0.04330800 0.03794911 0.03631220 0.03340044 0.03056543
 [9] 0.02565698 0.02350495
par(mfrow = c(1, 2))
plot(prop_varex, xlab = "Principal Component",
     ylab = "Proportion of Variance Explained",
     type = "b")
#cumulative scree plot
plot(cumsum(prop_varex), xlab = "Principal Component",
       ylab = "Cumulative Proportion of Variance Explained",
       type = "b")

The plots above shows that ~58 components exlains around 98% variance.Same is confirmed with cumulative varaince plot.Therefore, the number of components as 58 can be used to perform modelling.[6]

References:

1.Week6 Demo slides,class worksheets

2.Week7 Demo slides,class worksheets

3.Week9 Demo slides,class worksheets

4.Week10 Demo slides,class worksheets

5.www.rdocumentation.org

6.https://www.analyticsvidhya.com/blog/2016/03/practical-guide-principal-component-analysis-python/

7.https://www.kaggle.com/c/home-credit-default-risk/data



LS0tDQp0aXRsZTogIk1BVEgyMzQ5IFNlbWVzdGVyIDEsIDIwMTgiDQphdXRob3I6ICJNYXlhIGRlcmUtIDM2NzUwNDIiDQpzdWJ0aXRsZTogQXNzaWdubWVudCAzDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQNCi0tLQ0KDQoNCiMjIFJlcXVpcmVkIHBhY2thZ2VzIA0KDQoNClByb3ZpZGUgdGhlIHBhY2thZ2VzIHJlcXVpcmVkIHRvIHJlcHJvZHVjZSB0aGUgcmVwb3J0LiBNYWtlIHN1cmUgeW91IGZ1bGZpbGxlZCB0aGUgbWluaW11bSByZXF1aXJlbWVudCAjMTAuDQoNCmBgYHtyfQ0KbGlicmFyeShyZWFkcikNCmxpYnJhcnkodGlkeXIpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShsdWJyaWRhdGUpDQpsaWJyYXJ5KE1WTikNCmxpYnJhcnkoY2FyKQ0KbGlicmFyeShmb3JlY2FzdCkNCmxpYnJhcnkoY2FyZXQpIA0KDQoNCmBgYA0KDQoNCiMjIEV4ZWN1dGl2ZSBTdW1tYXJ5IA0KDQpUbyBhY2hpZXZlIHRoZSBoaWdoIGFjY3VyYWN5IGFuZCBwcmVjaXNpb24gb2YgTWFjaGluZSBsZWFybmluZyBtb2RlbHMsIGRhdGEgbmVlZHMgdG8gYmUgaW4gY2xlYW4gZm9ybWF0LiBUbyBwcmVwYXJlIHN1Y2ggZGF0YSB0aGVyZSBhcmUgbnVtYmVyIG9mIHRhc2tzIGludm9sdmVkIGluIGl0LiBUaGUgdGFza3MgaW5jbHVkZXMgZGF0YSBjbGVhbmluZywgZGF0YSB0cmFuc2Zvcm1hdGlvbiwgZGVhbGluZyB3aXRoIG1pc3NpbmcgdmFsdWVzIGFuZCBvdXRsaWVycyBhbmQgY29ycmVjdCBkYXRhIGluY29uc2lzdGVuY2llcy4gQW5vdGhlciBpbXBvcnRhbnQgZmFjdG9yIGlzIHRoYXQgdGhlIGRhdGEgaGFzIHRvIGJlIGluIHRpZHkgZm9ybWF0LlRoZSBkYXRhIGhhcyBhcm91bmQgMTklIG9mIHRoZSBtaXNzaW5nIHZhbHVlcyB3aXRoIHNvbWUgb2YgdGhlIGNvbHVtbnMgaGF2aW5nIG1vcmUgdGhhbiA1MCUgb2YgdGhlIGRhdGEgaXMgbWlzc2luZy5DYWFwaW5nIG1ldGhvZCB1c2VkIHRvIGRlYWwgd2l0aCB0aGUgb3V0bGllcnMgcHJlc2VudCBpbiB0aGUgZGF0YS5GdXJ0aGVybW9yZSwgdGhlcmUgaXMgYSBodWdlIHZhcmlhdGlvbiBiZXR3ZWVuIHRoZSByYW5nZXMgb2YgdGhlIHZhcmlibGVzIHdoaWNoIG5vcm1hbGlzZWQgdXNpbmcgQm94Q294IHRyYW5zZm9ybWF0aW9uLg0KDQojIyBEYXRhIA0KDQpUd28gZGF0YXNldHMgaGFzIGJlZW4gc2VsZWN0ZWQgZnJvbSBrYWdnbGUgd2Vic2l0ZS5bN10gVGhlIGRhdGEgaXMgYWJvdXQgYmFuayBsb2FuIGFwcGxpY2F0aW9uLiBGaXJzdCBmaWxlKGFwcGxpY2F0aW9uX3RyYWluKSwgY29udGFpbnMgdGhlIHN0YXRpYyBkYXRhIG9mIGFsbCBhcHBsaWNhdGlvbnMsIG9uZSByb3cgcmVwcmVzZW50cyBvbmUgbG9hbiBhcHBsaWNhdGlvbi4gU2Vjb25kIGZpbGUoYnVyZWF1KSBjb250YWlucyB0aGUgYXBwbGljYW50cyBwcmV2aW91cyBjcmVkaXRzIHByb3ZpZGVkIGJ5IG90aGVyIGZpbmFuY2lhbCBpbnN0aXR1dGVzICYgdGhhdCB3ZXJlIHJlcG9ydGVkIHRvIGNyZWRpdCBidXJlYXUoZm9yIGNsaWVudHMgd2hvIGhhdmUgYSBsb2FuIGFwcGxpY2F0aW9uIGluIGZpcnN0IGZpbGUpLiBGb3IgZXZlcnkgcmVjb3JkIGluIGZpcnN0IGZpbGUgdGhlcmUgYXJlIG11bHRpcGxlIHJlY29yZHMgaW4gc2Vjb25kIGZpbGUuYmVsb3cgYXJlIHRoZSBzdGVwcyB0byBnZXQgdGhlIGRhdGEuDQoqIER1ZSB0byBsYXJnZSBudW1iZXIgb2Ygdm9sdW1lIGluIGJvdGggdGhlIGZpbGUsIHRoZSBkYXRhIGhhcyBiZWVuIGZpbHRlcmVkIG9uIHRoZSBiYXNpcyBvZiAiU0tfSURfQ1VSUiIgY29sdW1uIHdoaWNoIGlzIGNvbW1vbiBpbiBib3RoIHRoZSBmaWxlcyBhbmQgc3RvcmVkIGluIHNlcGFyYXRlIENTViBmaWxlcyB0byBwZXJmb3JtIGRhdGEgcHJlcHJvY2Vzc2luZyB0YXNrcy4NCg0KDQpgYGB7cn0NCiMjb3JpZ2luYWwgc291cmNlIGZpbGVzDQpsb2FuX2FwcF9zb3VyY2U8LXJlYWQuY3N2KCJhcHBsaWNhdGlvbl90cmFpbi5jc3YiKQ0KYnVyZWF1X3NvdXJjZTwtcmVhZC5jc3YoImJ1cmVhdS5jc3YiKQ0KDQojI2ZpbHRlciB0aGUgZGF0YQ0KbG9hbl9hcHBfZmlsdGVyZWQ8LSBsb2FuX2FwcF9zb3VyY2UgJT4lIGZpbHRlcihTS19JRF9DVVJSPD0xMDExNTYpDQpidXJlYXVfZmlsdGVyZWQ8LWJ1cmVhdV9zb3VyY2UgJT4lIGZpbHRlcihTS19JRF9DVVJSPD0xMDExNTYpDQoNCiNleHBvcnQgZmlsdGVyZWQgZGF0YQ0Kd3JpdGUuY3N2KGxvYW5fYXBwX2ZpbHRlcmVkLCBmaWxlID0gImxvYW5fYXBwX2ZpbHQuY3N2Iixyb3cubmFtZXM9RkFMU0UpDQp3cml0ZS5jc3YoYnVyZWF1X2ZpbHRlcmVkLCBmaWxlID0gImJ1cmVhdV9maWx0LmNzdiIscm93Lm5hbWVzPUZBTFNFKQ0KYGBgDQoNCiogSW4gYWJvdmUgY2h1bmsgZGF0YSBoYXMgYmVlbiBmaWx0ZXJlZCB1c2luZyAiU0tfSURfQ1VSUjw9MTAxMTU2IiBjb25kaXRpb24gd2hpY2ggd2lsbCBjb250YWluIGFwcHJveGltYXRlbHkgMTAlIG9mIHRoZSBzb3VyY2UgZGF0YS4NCg0KKiBGaWx0ZXJlZCBkYXRhIGhhcyBiZWVuIGV4cG9ydGVkIGluIHNlcGFyYXRlIENTViBmaWxlIGFuZCB0aGVuIGltcG9ydGVkIHRvIHBlcmZvcm0gZGF0YSBwcmVwcm9jZXNzaW5nIHRhc2tzLg0KCQ0KKiBVc2luZyBsZWZ0IGpvaW4gbWVyZ2VkIHRoZXNlIHR3byBkYXRhIGZyYW1lcw0KDQoNCmBgYHtyfQ0KbG9hbl9hcHA8LXJlYWQuY3N2KCJsb2FuX2FwcF9maWx0LmNzdiIpDQpidXJlYXU8LXJlYWQuY3N2KCJidXJlYXVfZmlsdC5jc3YiKQ0KDQojY29tYmluZWQgdHdvIGRhdGFzZXRzDQpjb21iaW5lZDwtIGxvYW5fYXBwICU+JSBsZWZ0X2pvaW4oYnVyZWF1LGJ5PSJTS19JRF9DVVJSIikgDQpgYGANCg0KIyMgVW5kZXJzdGFuZCANCg0KVG8gdW5kZXJzdGFuZCB0aGUgZGF0YSBpdCBpcyBpbXBvcnRhbnQgdG8gY2hlY2sgdGhlIGRpbWVuc2lvbnMgYW5kIGRhdGEgdHlwZXMgb2YgYWxsIHRoZSB2YXJpYWJsZXMuDQoNCiogVXNlZCBkaW0gZnVuY3Rpb24sIHdoaWNoIHNob3dzIHRoYXQgdGhlIGRhdGFzZXQgaGFzIDQ2NDcgb2JzZXJ2YXRpb25zIGFuZCAxMzggdmFyaWFibGVzLg0KKiBVc2VkIHNhcHBseSBmdW5jdGlvbiB0byBnZXQgdGhlIGRhdGEgdHlwZSBvZiBhbGwgdmFyaWFibGVzLg0KDQoNCmBgYHtyfQ0KI2NoZWNrIHRoZSBkaW1lbnRpb24gb2YgdGhlIGRhdGEgZnJhbWUNCmRpbShjb21iaW5lZCkNCg0KI2NoZWNrIHRoZSBkYXRhIHR5cGVzDQpzYXBwbHkoY29tYmluZWRbMToyMF0sY2xhc3MpICMgc2hvd2luZyBvbmx5IGZpcnN0IDIwIHZhcmlhYmxlcyBhcyB0aGUgY29tcGVsZXRlIGxpc3QgbG9uZw0KDQpoZWFkKGNvbWJpbmVkLDUpDQoNCmBgYA0KVGhlIHJlc3VsdHMgb2YgdGhlIGFib3ZlIGNodW5rIHNob3dzIHRoYXQgdGhlIGRhdGEgZnVsZmlsbHMgdGhlIG1pbmltdW0gcmVxdWlyZW1lbnRzIDItNA0KDQoqIHJlcXVpcmVtZW50IDItIHRoZSBkYXRhIGluY2x1ZGVkIG11bHRpcGxlIGRhdGF0eXBlcyBpbnQsIGZhY3RvciwgbnVtZXJpYy4NCiogcmVxdWlyZW1lbnQgMy0gY29udGFpbnMgdmFyaWFibGVzIG9uIHdoaWNoIGRhdGEgdHlwZSBjb252ZXJzaW9uIGNhbiBiZSBhcHBsaWVkLm51bWVyaWMtPmZhY3RvciBhbmQgbnVtZXJpYy0+IGRhdGUgKGNvZGUgaXMgaW4gbmV4dCBjaHVuaykNCiogcmVxdWlyZW1lbnQgNC0+Y29udGFpbnMgZmFjdG9yIHZhcmlhYmxlIHRoYXQgbmVlZHMgdG8gYmUgb3JkZXJlZChjb2RlIGlzIGluIG5leHQgY2h1bmspDQoNCmBgYHtyfQ0KI2RhdGF0eXBlIGNvbnZlcnNpb24NCiNudW1lcmljLT5mYWN0b3INCmNvbWJpbmVkJEZMQUdfTU9CSUw8LWZhY3Rvcihjb21iaW5lZCRGTEFHX01PQklMKQ0KY29tYmluZWQkRkxBR19FTVBfUEhPTkU8LWZhY3Rvcihjb21iaW5lZCRGTEFHX0VNUF9QSE9ORSkNCmNvbWJpbmVkJEZMQUdfV09SS19QSE9ORTwtZmFjdG9yKGNvbWJpbmVkJEZMQUdfV09SS19QSE9ORSkNCg0KI251bWVyaWMtPmRhdGUNCmE8LWRkYXlzKGNvbWJpbmVkJERBWVNfQklSVEgpDQojI2Fzc3VtZSB0aGUgZGF0ZSBvbiB3aG9jaCBkYXRhIGlzIGdlbmVyZXRlZCBpcyAxOHRoIE1heSAyMDE4DQpiaXJ0aF9kYXRlPC1hcy5EYXRlKCIyMDE4LTA1LTE4IikrYQ0KY2xhc3MoYmlydGhfZGF0ZSkNCg0KDQojZmFjdG9yIHZhcmlhYmxlIGZyb20gdW5vcmRlcmVkIHRvIG9yZGVyZWQNCiMjY2hlY2sgdGhlIGxldmVscyBvZiBmYWN0b3IgdmFyaWFibGUNCmxldmVscyhjb21iaW5lZCRXRUVLREFZX0FQUFJfUFJPQ0VTU19TVEFSVCkNCiNvcmRlciB0aGUgbGV2ZWxzDQpjb21iaW5lZCRXRUVLREFZX0FQUFJfUFJPQ0VTU19TVEFSVDwtIG9yZGVyZWQoY29tYmluZWQkV0VFS0RBWV9BUFBSX1BST0NFU1NfU1RBUlQsbGV2ZWxzPWMoIk1PTkRBWSIsIlRVRVNEQVkiLCJXRURORVNEQVkiLCJUSFVSU0RBWSIsIkZSSURBWSIsIlNBVFVSREFZIiwiU1VOREFZIikpDQojcmUgY2hlY2sgd2hldGhlciB0aGUgbGV2ZWxzIGhhcyBiZWVuIG9yZGVyZWQgb3Igbm90DQpsZXZlbHMoY29tYmluZWQkV0VFS0RBWV9BUFBSX1BST0NFU1NfU1RBUlQpDQoNCmBgYA0KKiBudW1lcmljLT5mYWN0b3I6IHRoZSBmbGFnIGNvbHVtbnMgaGFkIHZhbHVlcyBlaXRoZXIgMCBvciAxLiBBZnRlciBjb252ZXJ0aW5nIGl0IGludG8gZmFjdG9yIGl0ICBjcmVhdGVkIDIgbGV2ZWxzDQoqIG51bWVyaWMtPiBkYXRlOiBEQVlTX0JJUlRIIGNvbHVtbiBoYXMgY2xpZW50J3MgYWdlIGluIG51bWJlciBvZiBkYXlzLiB1c2luZyBkZGF5cyBmdW5jdGlvbiBmcm9tIGx1YnJpZGF0ZSBwYWNrYWdlIGFuZCBzdWJ0cmFjdGluZyB0aGUgdmFsdWUgZnJvbSBjdXJyZW50IGRhdGUgZ2l2ZXMgZGF0ZSBvZiBiaXJ0aCBvZiB0aGUgY2xpZW50IHdoaWNoIGlzIGZ1cnRoZXIgdXNlZCB0byBjb252ZXJ0IGl0IGludG8gYWdlLg0KKiBvcmRlcmVkIGZhY3Rvcjogd2Vla2RheV9hcHByX3Byb2Nlc3Nfc3RhcnQueCBjb2x1bW4gaGFzIDcgbGV2ZWxzIGNvbnRhaW5zIHRoZSB3ZWVrZGF5cyBpbiB1bm9yZGVyZWQgZm9ybWF0LiB1c2luZyBvcmRlcmVkIGZ1bmN0aW9uIGRlZmluZWQgdGhlIG9yZGVyIGZvIHRoZSBsZXZlbHMgYXMgTW9uZGF5PFR1ZXNkYXk8V2VkbmVzZGF5PFRodXJzZGF5PEZyaWRheTxTYXR1cmRheTxTdW5kYXkuDQoNCiMjCVRpZHkgJiBNYW5pcHVsYXRlIERhdGEgSSANCg0KVGhlIGRhdGEgaXMgaW4gdGlkeSBmb3JtYXQgYXMgaXQgc2hvd3MgdGhlIGNoYXJhY3RlcmlzdGljcyBvZiB0aWR5IGRhdGEgYXMgYmVsb3cNCg0KKiBFdmVyeSB2YWx1ZSBpbiBhIGRhdGEgYmVsb25ncyB0byBhbiBvYnNlcnZhdGlvbiBhbmQgYSB2YXJpYWJsZS4NCiogVmFyaWFibGUgY29udGFpbnMgdmFsdWVzIHRoYXQgbWVhc3VyZSB0aGUgc2FtZSBhdHRyaWJ1dGUgYWNyb3NzIHVuaXQuKGxpa2UgaW5jb21lX3RvdGFsLGFtb3VudF9jcmVkaXQpDQoqIEV2ZXJ5IHZhcmlhYmxlIGlzIGNvbHVtbiBhcyBhYmxlIHRvIGV4dHJhY3QgbmVlZGVkIHZhcmlhYmxlcy4gQ3JlYXRlZCBuZXcgdmFyaWFibGVzIHVzaW5nIGV4aXN0aW5nIHZhcmlhYmxlIHdhcyBzbW9vdGggYW5kIHBvc3NpYmxlIGJlY2F1c2Ugb2YgdGlkeSBkYXRhIHN0cnVjdHVyZS4NCg0KDQoNCiMjCVRpZHkgJiBNYW5pcHVsYXRlIERhdGEgSUkgDQoNClRvIGFycml2ZSBhdCBhIGRlY2lzaW9uIG9mIHdoZXRoZXIgdG8gYXBwcm92ZSB0aGUgY3JlZGl0IG9yIHRvIGRpc2FwcHJvdmUgdHdvIG1vcmUgaW1wb3J0YW50IHByZWRpY3RvcnMgY2FuIGJlIGNyZWF0ZWQgZnJvbSBleGlzdGluZyBkYXRhLCBBR0UgYW5kIEFNT1VOVCByZW1haW4gaW4gaGFuZCBhZnRlciBwYXlpbmcgYW55IG9uZ29pbmcgY3JlZGl0LiBIZW5jZSB0cmllZCBjcmVhdGluZyBuZXcgdmFyaWFibGVzIG9uIHRoZSBiYXNpcyBvZiBhdmFpbGFibGUgaW5mb3JtYXRpb24uDQoNCg0KYGBge3J9DQojY3JlYXRlIG5ldyBjb2x1bW4gaW4gZGF0YSBmcmFtZSJBR0UiIHdjaGljaCB3aWxsIHN0b3JlIHRoIGNsaWVudCdzIGFnZQ0KY29tYmluZWQkQUdFPC1yb3VuZChpbnRlcnZhbChzdGFydCA9IGJpcnRoX2RhdGUsIGVuZCA9ICIyMDE4LTA1LTE4IikgLyANCiAgZHVyYXRpb24obnVtID0gMSwgdW5pdHMgPSAieWVhcnMiKSkNCg0KI2NyZWF0ZSBuZXcgdmFyaWFibGUgd2hpY2ggd2lsbCBzaG93IHRoZSByZW1haW5pbmcgYW5udWFsIGFtb3VudCBmcm9tIGluY29tZSBhZnRlciBkZWN1Y3RpbmcgdGhlIGxvYW4gYW1vdW50DQpjb21iaW5lZDwtdHJhbnNmb3JtKGNvbWJpbmVkLElOQ09NRV9NSU5VU19BTlVJVFk9IEFNVF9JTkNPTUVfVE9UQUwtIEFNVF9BTk5VSVRZLngpDQpjb2xzPC1jKCdTS19JRF9DVVJSJywnREFZU19CSVJUSCcsJ0FHRScsJ0FNVF9JTkNPTUVfVE9UQUwnLCdBTVRfQU5OVUlUWS54JywnSU5DT01FX01JTlVTX0FOVUlUWScpDQp0YWlsKGNvbWJpbmVkW2NvbHNdLDUpDQpgYGANCiogQ3JlYXRlZCBuZXcgY29sdW1uICdBR0UnIHdoaWNoIGlzIGNvbXB1dGVkIG9uIHRoZSBiYXNpcyBvZiBiaXJ0aCBkYXRlIGNvbXB1dGVkIGluIGFib3ZlIGNodW5rLiBjb2x1bW4gc2hvd3MgdGhlIGFnZSBvZiB0aGUgY2xpZW50IGluIHllYXJzDQoNCiogSU5DT01FX01JTlVTX0FOTlVJVFkgc2hvd3MgdGhlIGFtb3VudCByZW1haW5pbmcgYWZ0ZXIgcGF5aW5nIHRoZSBsb2FuLg0KDQojIwlTY2FuIEkgDQoNCkJlbG93IGNodW5rIHNob3dzIHRoZSBhbW91bnQgb2YgbWlzc2luZyB2YWx1ZXMgaW4gZGF0YSBhbmQgaG93IHRvIGRlYWwgd2l0aCB0aGVtLg0KDQpgYGB7cn0NCiNtaXNzaW5nIHZhbHVlcyBzY2FuDQojIG51bWJlciBvZiBjb2x1bW5zIGhhdmluZyBtaXNzaW5nIHZhbHVlcw0KbWlzc2luZ192YWxfY29sczwtY29sbmFtZXMoY29tYmluZWQpW2NvbFN1bXMoaXMubmEoY29tYmluZWQpKSA+IDBdDQpsZW5ndGgobWlzc2luZ192YWxfY29scykNCiN0b3RhbCBwZXJjZW50YWdlIG9mIG1pc3NpbmcgdmFsdWVzDQpzdW0oaXMubmEoY29tYmluZWQpKSAvIChucm93KGNvbWJpbmVkKSAqbmNvbChjb21iaW5lZCkpDQoNCiNjb2xTdW1zKGlzLm5hKGNvbWJpbmVkKSkgIyBhcyB0aGUgcmVzdWx0IGlzIGxvbmcgDQojIEhhbmRlbGluZyBtaXNzaW5nIHZhbHVlcw0KI3JlbW92ZSB0aGUgY29sdW1ucyB3aGVyZSB0aGVyZSBhcmUgbW9yZSB0aGFuIDUwJSB2YWx1ZXMgYXJlIG1pc3NpbmcNCm1pc19jb2xzX2xhcmdlPC1jb2xuYW1lcyhjb21iaW5lZClbY29sU3Vtcyhpcy5uYShjb21iaW5lZCkpID4gMjIwMF0gDQpjb21iaW5lZDwtIGNvbWJpbmVkICU+JSBzZWxlY3QoLW9uZV9vZihtaXNfY29sc19sYXJnZSkpDQpkaW0oY29tYmluZWQpDQoNCiNyZXBsYWNlIG1pc3NpbmcgdmFsdWVzIHdpdGggdGhlIE1lZGlhbg0KZm9yKGkgaW4gd2hpY2goc2FwcGx5KGNvbWJpbmVkLGlzLm51bWVyaWMpKSl7DQogIGNvbWJpbmVkW2lzLm5hKGNvbWJpbmVkWyxpXSksIGldIDwtIG1lZGlhbihjb21iaW5lZFssaV0sIG5hLnJtID0gVFJVRSkNCn0NCm1pc3NpbmdfdmFsX2NvbHM8LWNvbG5hbWVzKGNvbWJpbmVkKVtjb2xTdW1zKGlzLm5hKGNvbWJpbmVkKSkgPiAwXQ0KbGVuZ3RoKG1pc3NpbmdfdmFsX2NvbHMpDQpkaW0oY29tYmluZWQpDQojc3RpbGwgYWxsIHRoZSBmYWN0b3IgY29sdW1uIGNvbnRhaW5zIDE1MSBtaXNzaW5nIHZhbHVlcw0KY29tYmluZWQ8LWNvbWJpbmVkW2NvbXBsZXRlLmNhc2VzKGNvbWJpbmVkKSxdDQpwcmludCgnY29tcGxldGUgY2FzZXMgJykNCmRpbShjb21iaW5lZCkNCmBgYA0KKiBPdXQgb2YgMTQwIHZhcmlhYmxlcyA3NCB2YXJpYWJsZXMgaGF2ZSBtaXNzaW5nIHZhbHVlcy4NCiogVG90YWwgcGVyY2VudGFnZSBvZiBtaXNzaW5nIHZhbHVlcyBpcyAxOS42Lg0KKiBjb2xTdW1zIHNob3dzIHRoYXQgc29tZSBvZiB0aGUgY29sdW1ucyBoYXZlIG1vcmUgdGhhbiA1MCUgdmFsdWVzIGFyZSBtaXNzaW5nLg0KKiByZW1vdmVkIGNvbHVtbnMoNDMpIGhhdmluZyBtaXNzaW5nIHZhbHVlIGNvdW50IG1vcmUgdGhhbiA1MCUuIHJlbWFpbmVkIHdpdGggMTA5IHZhcmlhYmxlcy4NCiogZm9yIHJlbWFpbmluZyBjb2x1bW5zIGltcHV0ZWQgd2l0aCB0aGUgbWVkaWFuIG9mIHRoZSBjb2x1bW5zLg0KKiBzdGlsbCB0aGVyZSB3ZXJlIHRocmVlIGZhY3RvciBjb2x1bW5zIGhhdmUgbWlzc2luZyB2YWx1ZXMuDQoqVXNlZCBjb21wbGV0ZS5jYXNlcygpdG8gaWdub3JlIDE1MSBOQSB2YWx1ZSByb3dzWzFdDQoNCmBgYHtyfQ0KI2luY29uc2lzdGFuY2llcyBjaGVjaw0KY29sbmFtZXMoY29tYmluZWQpW2FwcGx5KGNvbWJpbmVkLCAyLCBmdW5jdGlvbih4KSBhbnkoaXMuaW5maW5pdGUoeCkpKT09J1RSVUUnXQ0KY29sbmFtZXMoY29tYmluZWQpW2FwcGx5KGNvbWJpbmVkLCAyLCBmdW5jdGlvbih4KSBhbnkoaXMuZmluaXRlKHgpKSk9PSdUUlVFJ10NCmNvbG5hbWVzKGNvbWJpbmVkKVthcHBseShjb21iaW5lZCwgMiwgZnVuY3Rpb24oeCkgYW55KGlzLm5hbih4KSkpPT0nVFJVRSddDQoNCiMjY2hlY2sgZm9yIGVkaXQgcnVsZXMNCg0KYGBgDQoNCg0KIyMJU2NhbiBJSQ0KVG8gcGVyZm9ybSBvdXRsaWVyIGRldGVjdGlvbiBjcmVhdGVkIHN1YnNldCB3aXRoIDExIG51bWVyaWMgY29sdW1ucy4NCg0KYGBge3IgLGZpZy5hbGlnbiA9ICJ0b3AiICxmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9OH0NCiMjZ2V0IHRoZSBudW1lcmljIGNvbHVtbnMNCm51bV9jb2xzIDwtIHVubGlzdChsYXBwbHkoY29tYmluZWQsIGlzLm51bWVyaWMpKSANCmNvbWJpbmVkX3N1YjwtY29tYmluZWRbICwgbnVtX2NvbHNdICMjOTAgTlVtZXJpYyB2YXJpYWJsZXMNCiMgY3JlYXRpbmcgc3Vic2V0IHdpdGggMTIgZmVhdHVyZXMgdG8gcGVyZnJvbSBvdXRsaWVycyBkZXRlY3Rpb24gYW5kIGRhdGEgbm9ybWFsaXphdGlvbg0KY29sczwtYygiQU1UX0lOQ09NRV9UT1RBTCIsIkFNVF9DUkVESVQiLCJBTVRfQU5OVUlUWS54IiwiQU1UX0dPT0RTX1BSSUNFIiwiQUdFIiwNCiAgICAgICAgIkRBWVNfRU1QTE9ZRUQiLCJFWFRfU09VUkNFXzIiLCJFWFRfU09VUkNFXzMiLCJBUEFSVE1FTlRTX0FWRyIsDQogICAgICAgICJUT1RBTEFSRUFfTU9ERSIsIkRBWVNfQ1JFRElUX0VORERBVEUiKQ0KY29tYmluZWRfc3ViPC1jb21iaW5lZF9zdWJbY29sc10NCmNvbWJpbmVkX3N1YjwtY29tYmluZWRfc3ViW2NvbXBsZXRlLmNhc2VzKGNvbWJpbmVkX3N1YiksXQ0KDQojIGRldGVjdCBvdWxpZXJzDQpwYXIobWFyPWMoMTAsNiw0LDEpKy4yKQ0KQm94cGxvdChjb21iaW5lZF9zdWIsbGFzPTIpIA0KYGBgDQoqIEFjY29yZGluZyB0byBCb3hwbG90IHRoZXJlIGFyZSAxMjAgb3V0bGllcnMgaW4gdGhlc2UgMTIgdmFyaWFibGVzLg0KKiBNb3N0IG9mIHRoZSBvdXRsaWVycyBvYnNlcnZlZCBpbiB0aHJlZSBjb2x1bW5zLg0KKiBUbyBhdm9pZCBzaW1wbHkgcmVtb3ZpbmcgdGhlc2Ugb3V0bGllcnMgcmVjb3JkcyBmcm9tIGRhdGEgZW50aXJlbHkgdXNlZCBjYXBwaW5nIGZ1bmN0aW9uIHRvIHJlcGxhY2UgdGhlIHZhbHVlcyBhYm92ZSA5NXRoIHBlcmNlbnRpbGUgYW5kIGJlbG93IDV0aCBwZXJjZW50aWxlLlsyXQ0KDQpgYGB7cixmaWcuYWxpZ24gPSAidG9wIiAsZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTh9DQojVXNlZCBjYXBwaW5nIG1ldGhvZCB0byByZXBsYWNlIG91dGxpZXJzIGluIHRoZSANCmNhcCA8LSBmdW5jdGlvbih4KXsNCiAgcXVhbnRpbGVzIDwtIHF1YW50aWxlKCB4LCBjKC4wNSwgMC4yNSwgMC43NSwgLjk1ICkgKQ0KICB4WyB4IDwgcXVhbnRpbGVzWzJdIC0gMS41KklRUih4KSBdIDwtIHF1YW50aWxlc1sxXQ0KICB4WyB4ID4gcXVhbnRpbGVzWzNdICsgMS41KklRUih4KSBdIDwtIHF1YW50aWxlc1s0XQ0KICB4DQp9DQoNCmNvbWJfc3ViX3dvX291dDwtYXBwbHkoY29tYmluZWRfc3ViLCAyLCBGVU49Y2FwKQ0KI2JveHBsb3QgYWZ0ZXIgb3V0bGllciBjYXBwaW5nDQpwYXIobWFyPWMoMTAsNiw0LDEpKy4yKQ0KQm94cGxvdChjb21iX3N1Yl93b19vdXQsbGFzPTIpDQpgYGANCiogVGhlIG91dGxpZXJzIHZpc2libGUgaW4gcHJldmlvdXMgYm94IHBsb3QgaGFzIGJlZW4gY2FwcGVkLg0KDQojIwlUcmFuc2Zvcm0gDQpGaXJzdCwgc2VsZWN0ZWQgZml2ZSB2YXJpYWJsZXMgdG8gc2VlIHRoZSBkaXN0cmlidXRpb24gb2YgdGhlbSBpbiB0aGUgZGF0YS5QbG90dGVkIGhpc3RvZ3JhbXMgdG8gc2VlIHRoZSBkaXN0cmlidXRpb24uIA0KDQpgYGB7cixmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9Nn0NCiNjaGVjayB0aGUgbmVlZCBmb3Igbm9ybWFsaXphdGlvbg0KY29sbmFtZXMgPC0gY29sbmFtZXMoY29tYl9zdWJfd29fb3V0KQ0KcGFyKG1mcm93ID0gYygzLCAyKSkNCmZvciAoaSBpbiAxOjUpIHsNCiAgaGlzdCh1bmxpc3QoY29tYl9zdWJfd29fb3V0WyxpXSksIG1haW4gPSBjb2xuYW1lc1tpXSkNCn0NCg0KYGBgDQoqIGl0IGlzIGNsZWFyIGZyb20gdGhlIGhpc3RvZ3JhbXMgdGhlIHJhbmdlIG9mIHRoZSBkYXRhIGZvciBlYWNoIHZhcmlhYmxlIGlzIG5vdCBzYW1lLiBJbiBmYWN0LCB0aGVyZSBpcyBhIGh1Z2UgZGlmZmVyZW5jZS4gRm9yIGV4YW1wbGUsIGFnZSB2YXJpYWJsZSBoYXMgcmFuZ2UgZnJvbSAyMC0xNyB3aGVyZWFzIEFNVF9HT09TX1BSSUNFIGhhcyByYW5nZSBmcm9tIDAtMTQwMDAwMC4NCiogSWYgd2UgcnVuIHRoZSBtb2RlbCBvbiB0aGlzIGRhdGEsIHJlc3VsdHMgd2lsbCBiZSBiaWFzZWQuDQoqIEhlbmNlLCBuZWVkIHRvIG5vcm1hbGl6ZSB0aGUgZGF0YS4NCiogVXNlZCBCb3hDb3ggdHJhbnNmb3JtYXRpb24gdG8gbm9ybWFsaXplIHRoZSBkYXRhLg0KDQoNCmBgYHtyLGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD02fQ0KYm94Y294bm9ybSA8LSBhcHBseShjb21iX3N1Yl93b19vdXQsIDIsIGZ1bmN0aW9uKHgpIEJveENveCh4LCBsYW1iZGEgPSAiYXV0byIpKQ0KI2hpc3RvZ3JhbXMgYWZ0ZXIgbm9ybWFsaXNhdGlvbg0KcGFyKG1mcm93ID0gYygzLCAyKSkNCmZvciAoaSBpbiAxOjUpIHsNCiAgaGlzdCh1bmxpc3QoYm94Y294bm9ybVssaV0pLCBtYWluID0gY29sbmFtZXNbaV0pDQp9DQpgYGANCiogSXQgaXMgY2xlYXIgZnJvbSB0aGUgYWJvdmUgaGlzdG9ncmFtcyB0aGF0IHRoZSBkYXRhIGhhcyBiZWVuIG5vcm1saXNlZC4NCiogVGhlIHNjYWxlIG9mIHRoZSBkYXRhIGhhcyBiZWVuIGNoYW5nZWQuDQoqIHRoZXJlIGlzIG5vdCBtdWNoIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgcmFuZ2VzIGZvciBldmVyeSB2YXJpYWJsZS5bM10NCg0KIyMJRmVhdHVyZSBTZWxlY3Rpb24gYW5kIEV4dHJhY3Rpb24NClRoZSBkYXRhc2V0IGNvbnNpc3RzIG9mIGxhcmdlIHNldCBvZiB2YXJpYWJsZXMgYW5kIG1vc3Qgb2YgdGhlIHZhcmlhYmxlcyBhcmUgY29ycmVsYXRlZCBoZW5jZSB0aGVyZSBzaSBuZWVkIHRvIGZpbmQgZmV3IGltcG9ydGFudCB2YXJpYWJsZXMuIFByaW5jaXBhbCBjb21wb25lbnQgYW5hbHlzaXMgaXMgb25lIG9mIHRoZSBtZXRob2Qgd2hpY2ggZXh0cmFjdCBpbXBvcnRhbnQgdmFyaWFibGVzIGZyb20gbGFyZ2Ugc2V0Lg0KDQoqIFRvIHBlcmZvcm0gUENBIGRhdGEgc2hvdWxkIGJlIG51bWVyaWMgYW5kIHN0YW5kYXJkaXplZCB3aGljaCB3ZSBkaWQgaW4gcHJldmlvdXMgY2h1bmsuDQoqIFNpbmNlLCB0aGUgZGF0YSB3aGljaCB3aWxsIGJlIHVzZWQgYnkgbW9kZWwgY29uc2lzdHMgb2YgNzkgbnVtZXJpYyB2YXJpYWJsZXMuIGkuZSBtb3JlIHRoYW4gMTAwMCBwbG90cyB0byBhbmFseXplIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB2YXJpYWJsZXMuDQoqIHNlbGVjdGVkIG9ubHkgbnVtZXJpYyBjb2x1bW5zIG9mIHRoZSBkYXRhDQoqIGlkZW50aWZpZWQgYW5kIHJlbW92ZWQgemVybyB2YXJpYW5jZSBjb2x1bW5zIGZyb20gZGF0YSBhcyBwcmNvbXAgY2Fubm90IHJlc2NhbGUgYSBjb25zdGFudC96ZXJvIGNvbHVtbiB0byB1bml0IHZhcmlhbmNlLg0KKiByZW1vdmVkIElEIHJlbGF0ZWQgY29sdW1ucyBmcm9tIGRhdGEgYXMgdGhleSBubyBtYWtlIHNlbnNlIHdoZW4gaXQgY29tZXMgdG8gUENBLg0KKiB1c2VkIHByY29tcCB0byBhcHBseSBQQ0Egb24gdGhlIGRhdGEsIHdoaWNoIGJ5IGRlZmF1bHQgY2VudGVycyB0aGUgdmFyaWFibGUgdG8gaGF2ZSBtZWFuIGVxdWFscyB0byB6ZXJvLiBzY2FsZS49VCBwYXJhbWV0ZXIgbm9ybWFsaXplIHRoZSB2YXJpYWJsZXMgdG8gaGF2ZSBTRCBlcXVhbHMgdG8gMS4NCiogVGhpcyBmdW5jdGlvbiByZXN1bHRzIGZpdmUgdXNlZnVsIG1lYXN1cmVzKCJzZGV2Iiwicm90YXRpb24iLCJjZW50ZXIiLCJzY2FsZSIsIngiKVs0LDZdDQoNCmBgYHtyfQ0KI3NlbGVjdCBvbmx5IG51bWVyaWMgY29sdW1ucw0KbnVtX2NvbHMgPC0gdW5saXN0KGxhcHBseShjb21iaW5lZCwgaXMubnVtZXJpYykpIA0KY29tYmluZWRfbW9kPC1jb21iaW5lZFsgLCBudW1fY29sc10gIyM0NDk2DQpjb21iaW5lZF9tb2QgPC0gY29tYmluZWRfbW9kW2NvbXBsZXRlLmNhc2VzKGNvbWJpbmVkX21vZCksXQ0KI3JlbW92ZSB6ZXJvIHZhcmlhbmNlIGNvbHVtbnMgZnJvbSBkYXRhDQpjb21iaW5lZF9tb2Q8LWNvbWJpbmVkX21vZFsgLCBhcHBseShjb21iaW5lZF9tb2QsIDIsIHZhcikgIT0gMF0NCiNyZW1vdmUgSUQgcmVsYXRlZCBjb2x1bW5zIGZvcm0gdGhlIGRhdGENCmNvbWJpbmVkX21vZDwtY29tYmluZWRfbW9kICU+JSBzZWxlY3QoLWMoU0tfSURfQ1VSUixTS19JRF9CVVJFQVUpKQ0KZGltKGNvbWJpbmVkX21vZCkNCiNhcHBseSBQQ0ENCnBjYV9jb21iaW5lZCA8LSBwcmNvbXAoY29tYmluZWRfbW9kLCBzY2FsZS4gPSBUKQ0KI2NvbXB1dGUgc3RhbmRhcmQgZGV2aWF0aW9uIG9mIGVhY2ggcHJpbmNpcGFsIGNvbXBvbmVudA0Kc3RkX2RldiA8LSBwY2FfY29tYmluZWQkc2Rldg0KI2NvbXB1dGUgdmFyaWFuY2UNCnByX3ZhciA8LSBzdGRfZGV2XjINCiNjaGVjayB2YXJpYW5jZSBvZiBmaXJzdCAxMCBjb21wb25lbnRzDQpwcl92YXJbMToxMF0NCiNhaW0gaXMgdG8gZmluZCBtYXhpbXVtIHZhcmlhbmNlLmhpZ2hlciBpcyB0aGUgdmFyaWFuY2UsIGhpZ2hlciB3aWxsIGJlIHRoZSBpbmZvcm1hdGlvbi4NCiNwcm9wb3J0aW9uIG9mIHZhcmlhbmNlIGV4cGxhaW5lZA0KcHJvcF92YXJleCA8LSBwcl92YXIvc3VtKHByX3ZhcikNCnByb3BfdmFyZXhbMToxMF0NCmBgYA0KKiBUaGUgcHJvcG9ydGlvbiBvZiB2YXJpYW5jZSBzaG93cyB0aGF0IHRoZSAxc3QgQ29tcG9uZW50IGV4cGxhaW5zIDEyJSB2YXJpYW5jZSwgMm5kIGNvbXBvbmVudCBleHBsYWlucyA2LjMgJSB2YXJpYW5jZSBhbmQgc28gb24uDQoqIFRvIGRlY2lkZSBob3cgbWFueSBjb21wb25lbmV0cyBzaG91bGQgYmUgc2VsZWN0ZWQgdXNlZCBzY2F0dGVyIHBsb3Qud2hpY2ggc2hvd3MgdGhlIGZhY3RvcnMgd2hpY2ggZXhwbGFpbnMgdGhlIG1vc3Qgb2YgdmFyaWFiaWxpdHkuWzQsNl0NCg0KYGBge3IsZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTV9DQpwYXIobWZyb3cgPSBjKDEsIDIpKQ0KcGxvdChwcm9wX3ZhcmV4LCB4bGFiID0gIlByaW5jaXBhbCBDb21wb25lbnQiLA0KICAgICB5bGFiID0gIlByb3BvcnRpb24gb2YgVmFyaWFuY2UgRXhwbGFpbmVkIiwNCiAgICAgdHlwZSA9ICJiIikNCiNjdW11bGF0aXZlIHNjcmVlIHBsb3QNCnBsb3QoY3Vtc3VtKHByb3BfdmFyZXgpLCB4bGFiID0gIlByaW5jaXBhbCBDb21wb25lbnQiLA0KICAgICAgIHlsYWIgPSAiQ3VtdWxhdGl2ZSBQcm9wb3J0aW9uIG9mIFZhcmlhbmNlIEV4cGxhaW5lZCIsDQogICAgICAgdHlwZSA9ICJiIikNCg0KYGBgDQpUaGUgcGxvdHMgYWJvdmUgc2hvd3MgdGhhdCAgfjU4IGNvbXBvbmVudHMgZXhsYWlucyBhcm91bmQgOTglIHZhcmlhbmNlLlNhbWUgaXMgY29uZmlybWVkIHdpdGggY3VtdWxhdGl2ZSB2YXJhaW5jZSBwbG90LlRoZXJlZm9yZSwgdGhlIG51bWJlciBvZiBjb21wb25lbnRzIGFzIDU4IGNhbiBiZSB1c2VkIHRvIHBlcmZvcm0gbW9kZWxsaW5nLls2XQ0KDQojIyBSZWZlcmVuY2VzOg0KDQoxLldlZWs2IERlbW8gc2xpZGVzLGNsYXNzIHdvcmtzaGVldHMNCg0KMi5XZWVrNyBEZW1vIHNsaWRlcyxjbGFzcyB3b3Jrc2hlZXRzDQoNCjMuV2VlazkgRGVtbyBzbGlkZXMsY2xhc3Mgd29ya3NoZWV0cw0KDQo0LldlZWsxMCBEZW1vIHNsaWRlcyxjbGFzcyB3b3Jrc2hlZXRzDQoNCjUud3d3LnJkb2N1bWVudGF0aW9uLm9yZw0KDQo2Lmh0dHBzOi8vd3d3LmFuYWx5dGljc3ZpZGh5YS5jb20vYmxvZy8yMDE2LzAzL3ByYWN0aWNhbC1ndWlkZS1wcmluY2lwYWwtY29tcG9uZW50LWFuYWx5c2lzLXB5dGhvbi8NCg0KNy5odHRwczovL3d3dy5rYWdnbGUuY29tL2MvaG9tZS1jcmVkaXQtZGVmYXVsdC1yaXNrL2RhdGENCg0KDQo8YnI+DQo8YnI+DQo=