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)
In above chunk data has been filtered using “SK_ID_CURR<=101156” condition which will contain approximately 10% of the source data.
Filtered data has been exported in separate CSV file and then imported to perform data preprocessing tasks.
Using left join merged these two data frames
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.
- Used dim function, which shows that the dataset has 4647 observations and 138 variables.
- Used sapply function to get the data type of all 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
- requirement 2- the data included multiple datatypes int, factor, numeric.
- requirement 3- contains variables on which data type conversion can be applied.numeric->factor and numeric-> date (code is in next chunk)
- requirement 4->contains factor variable that needs to be ordered(code is in next chunk)
#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"
- numeric->factor: the flag columns had values either 0 or 1. After converting it into factor it created 2 levels
- numeric-> date: DAYS_BIRTH column has client’s age in number of days. using ddays function from lubridate package and subtracting the value from current date gives date of birth of the client which is further used to convert it into age.
- ordered factor: weekday_appr_process_start.x column has 7 levels contains the weekdays in unordered format. using ordered function defined the order fo the levels as 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
- Every value in a data belongs to an observation and a variable.
- Variable contains values that measure the same attribute across unit.(like income_total,amount_credit)
- Every variable is column as able to extract needed variables. Created new variables using existing variable was smooth and possible because of tidy data structure.
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)
Created new column ‘AGE’ which is computed on the basis of birth date computed in above chunk. column shows the age of the client in years
INCOME_MINUS_ANNUITY shows the amount remaining after paying the loan.
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
- Out of 140 variables 74 variables have missing values.
- Total percentage of missing values is 19.6.
- colSums shows that some of the columns have more than 50% values are missing.
- removed columns(43) having missing value count more than 50%. remained with 109 variables.
- for remaining columns imputed with the median of the columns.
- still there were three factor columns have missing values. *Used complete.cases()to ignore 151 NA value rows[1]
#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"

- According to Boxplot there are 120 outliers in these 12 variables.
- Most of the outliers observed in three columns.
- To avoid simply removing these outliers records from data entirely used capping function to replace the values above 95th percentile and below 5th percentile.[2]
#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"

- The outliers visible in previous box plot has been capped.
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.
- To perform PCA data should be numeric and standardized which we did in previous chunk.
- Since, the data which will be used by model consists of 79 numeric variables. i.e more than 1000 plots to analyze the relationship between variables.
- selected only numeric columns of the data
- identified and removed zero variance columns from data as prcomp cannot rescale a constant/zero column to unit variance.
- removed ID related columns from data as they no make sense when it comes to PCA.
- used prcomp to apply PCA on the data, which by default centers the variable to have mean equals to zero. scale.=T parameter normalize the variables to have SD equals to 1.
- This function results five useful measures(“sdev”,“rotation”,“center”,“scale”,“x”)[4,6]
#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
- The proportion of variance shows that the 1st Component explains 12% variance, 2nd component explains 6.3 % variance and so on.
- To decide how many componenets should be selected used scatter plot.which shows the factors which explains the most of variability.[4,6]
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]
LS0tDQp0aXRsZTogIk1BVEgyMzQ5IFNlbWVzdGVyIDEsIDIwMTgiDQphdXRob3I6ICJNYXlhIGRlcmUtIDM2NzUwNDIiDQpzdWJ0aXRsZTogQXNzaWdubWVudCAzDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQNCi0tLQ0KDQoNCiMjIFJlcXVpcmVkIHBhY2thZ2VzIA0KDQoNClByb3ZpZGUgdGhlIHBhY2thZ2VzIHJlcXVpcmVkIHRvIHJlcHJvZHVjZSB0aGUgcmVwb3J0LiBNYWtlIHN1cmUgeW91IGZ1bGZpbGxlZCB0aGUgbWluaW11bSByZXF1aXJlbWVudCAjMTAuDQoNCmBgYHtyfQ0KbGlicmFyeShyZWFkcikNCmxpYnJhcnkodGlkeXIpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShsdWJyaWRhdGUpDQpsaWJyYXJ5KE1WTikNCmxpYnJhcnkoY2FyKQ0KbGlicmFyeShmb3JlY2FzdCkNCmxpYnJhcnkoY2FyZXQpIA0KDQoNCmBgYA0KDQoNCiMjIEV4ZWN1dGl2ZSBTdW1tYXJ5IA0KDQpUbyBhY2hpZXZlIHRoZSBoaWdoIGFjY3VyYWN5IGFuZCBwcmVjaXNpb24gb2YgTWFjaGluZSBsZWFybmluZyBtb2RlbHMsIGRhdGEgbmVlZHMgdG8gYmUgaW4gY2xlYW4gZm9ybWF0LiBUbyBwcmVwYXJlIHN1Y2ggZGF0YSB0aGVyZSBhcmUgbnVtYmVyIG9mIHRhc2tzIGludm9sdmVkIGluIGl0LiBUaGUgdGFza3MgaW5jbHVkZXMgZGF0YSBjbGVhbmluZywgZGF0YSB0cmFuc2Zvcm1hdGlvbiwgZGVhbGluZyB3aXRoIG1pc3NpbmcgdmFsdWVzIGFuZCBvdXRsaWVycyBhbmQgY29ycmVjdCBkYXRhIGluY29uc2lzdGVuY2llcy4gQW5vdGhlciBpbXBvcnRhbnQgZmFjdG9yIGlzIHRoYXQgdGhlIGRhdGEgaGFzIHRvIGJlIGluIHRpZHkgZm9ybWF0LlRoZSBkYXRhIGhhcyBhcm91bmQgMTklIG9mIHRoZSBtaXNzaW5nIHZhbHVlcyB3aXRoIHNvbWUgb2YgdGhlIGNvbHVtbnMgaGF2aW5nIG1vcmUgdGhhbiA1MCUgb2YgdGhlIGRhdGEgaXMgbWlzc2luZy5DYWFwaW5nIG1ldGhvZCB1c2VkIHRvIGRlYWwgd2l0aCB0aGUgb3V0bGllcnMgcHJlc2VudCBpbiB0aGUgZGF0YS5GdXJ0aGVybW9yZSwgdGhlcmUgaXMgYSBodWdlIHZhcmlhdGlvbiBiZXR3ZWVuIHRoZSByYW5nZXMgb2YgdGhlIHZhcmlibGVzIHdoaWNoIG5vcm1hbGlzZWQgdXNpbmcgQm94Q294IHRyYW5zZm9ybWF0aW9uLg0KDQojIyBEYXRhIA0KDQpUd28gZGF0YXNldHMgaGFzIGJlZW4gc2VsZWN0ZWQgZnJvbSBrYWdnbGUgd2Vic2l0ZS5bN10gVGhlIGRhdGEgaXMgYWJvdXQgYmFuayBsb2FuIGFwcGxpY2F0aW9uLiBGaXJzdCBmaWxlKGFwcGxpY2F0aW9uX3RyYWluKSwgY29udGFpbnMgdGhlIHN0YXRpYyBkYXRhIG9mIGFsbCBhcHBsaWNhdGlvbnMsIG9uZSByb3cgcmVwcmVzZW50cyBvbmUgbG9hbiBhcHBsaWNhdGlvbi4gU2Vjb25kIGZpbGUoYnVyZWF1KSBjb250YWlucyB0aGUgYXBwbGljYW50cyBwcmV2aW91cyBjcmVkaXRzIHByb3ZpZGVkIGJ5IG90aGVyIGZpbmFuY2lhbCBpbnN0aXR1dGVzICYgdGhhdCB3ZXJlIHJlcG9ydGVkIHRvIGNyZWRpdCBidXJlYXUoZm9yIGNsaWVudHMgd2hvIGhhdmUgYSBsb2FuIGFwcGxpY2F0aW9uIGluIGZpcnN0IGZpbGUpLiBGb3IgZXZlcnkgcmVjb3JkIGluIGZpcnN0IGZpbGUgdGhlcmUgYXJlIG11bHRpcGxlIHJlY29yZHMgaW4gc2Vjb25kIGZpbGUuYmVsb3cgYXJlIHRoZSBzdGVwcyB0byBnZXQgdGhlIGRhdGEuDQoqIER1ZSB0byBsYXJnZSBudW1iZXIgb2Ygdm9sdW1lIGluIGJvdGggdGhlIGZpbGUsIHRoZSBkYXRhIGhhcyBiZWVuIGZpbHRlcmVkIG9uIHRoZSBiYXNpcyBvZiAiU0tfSURfQ1VSUiIgY29sdW1uIHdoaWNoIGlzIGNvbW1vbiBpbiBib3RoIHRoZSBmaWxlcyBhbmQgc3RvcmVkIGluIHNlcGFyYXRlIENTViBmaWxlcyB0byBwZXJmb3JtIGRhdGEgcHJlcHJvY2Vzc2luZyB0YXNrcy4NCg0KDQpgYGB7cn0NCiMjb3JpZ2luYWwgc291cmNlIGZpbGVzDQpsb2FuX2FwcF9zb3VyY2U8LXJlYWQuY3N2KCJhcHBsaWNhdGlvbl90cmFpbi5jc3YiKQ0KYnVyZWF1X3NvdXJjZTwtcmVhZC5jc3YoImJ1cmVhdS5jc3YiKQ0KDQojI2ZpbHRlciB0aGUgZGF0YQ0KbG9hbl9hcHBfZmlsdGVyZWQ8LSBsb2FuX2FwcF9zb3VyY2UgJT4lIGZpbHRlcihTS19JRF9DVVJSPD0xMDExNTYpDQpidXJlYXVfZmlsdGVyZWQ8LWJ1cmVhdV9zb3VyY2UgJT4lIGZpbHRlcihTS19JRF9DVVJSPD0xMDExNTYpDQoNCiNleHBvcnQgZmlsdGVyZWQgZGF0YQ0Kd3JpdGUuY3N2KGxvYW5fYXBwX2ZpbHRlcmVkLCBmaWxlID0gImxvYW5fYXBwX2ZpbHQuY3N2Iixyb3cubmFtZXM9RkFMU0UpDQp3cml0ZS5jc3YoYnVyZWF1X2ZpbHRlcmVkLCBmaWxlID0gImJ1cmVhdV9maWx0LmNzdiIscm93Lm5hbWVzPUZBTFNFKQ0KYGBgDQoNCiogSW4gYWJvdmUgY2h1bmsgZGF0YSBoYXMgYmVlbiBmaWx0ZXJlZCB1c2luZyAiU0tfSURfQ1VSUjw9MTAxMTU2IiBjb25kaXRpb24gd2hpY2ggd2lsbCBjb250YWluIGFwcHJveGltYXRlbHkgMTAlIG9mIHRoZSBzb3VyY2UgZGF0YS4NCg0KKiBGaWx0ZXJlZCBkYXRhIGhhcyBiZWVuIGV4cG9ydGVkIGluIHNlcGFyYXRlIENTViBmaWxlIGFuZCB0aGVuIGltcG9ydGVkIHRvIHBlcmZvcm0gZGF0YSBwcmVwcm9jZXNzaW5nIHRhc2tzLg0KCQ0KKiBVc2luZyBsZWZ0IGpvaW4gbWVyZ2VkIHRoZXNlIHR3byBkYXRhIGZyYW1lcw0KDQoNCmBgYHtyfQ0KbG9hbl9hcHA8LXJlYWQuY3N2KCJsb2FuX2FwcF9maWx0LmNzdiIpDQpidXJlYXU8LXJlYWQuY3N2KCJidXJlYXVfZmlsdC5jc3YiKQ0KDQojY29tYmluZWQgdHdvIGRhdGFzZXRzDQpjb21iaW5lZDwtIGxvYW5fYXBwICU+JSBsZWZ0X2pvaW4oYnVyZWF1LGJ5PSJTS19JRF9DVVJSIikgDQpgYGANCg0KIyMgVW5kZXJzdGFuZCANCg0KVG8gdW5kZXJzdGFuZCB0aGUgZGF0YSBpdCBpcyBpbXBvcnRhbnQgdG8gY2hlY2sgdGhlIGRpbWVuc2lvbnMgYW5kIGRhdGEgdHlwZXMgb2YgYWxsIHRoZSB2YXJpYWJsZXMuDQoNCiogVXNlZCBkaW0gZnVuY3Rpb24sIHdoaWNoIHNob3dzIHRoYXQgdGhlIGRhdGFzZXQgaGFzIDQ2NDcgb2JzZXJ2YXRpb25zIGFuZCAxMzggdmFyaWFibGVzLg0KKiBVc2VkIHNhcHBseSBmdW5jdGlvbiB0byBnZXQgdGhlIGRhdGEgdHlwZSBvZiBhbGwgdmFyaWFibGVzLg0KDQoNCmBgYHtyfQ0KI2NoZWNrIHRoZSBkaW1lbnRpb24gb2YgdGhlIGRhdGEgZnJhbWUNCmRpbShjb21iaW5lZCkNCg0KI2NoZWNrIHRoZSBkYXRhIHR5cGVzDQpzYXBwbHkoY29tYmluZWRbMToyMF0sY2xhc3MpICMgc2hvd2luZyBvbmx5IGZpcnN0IDIwIHZhcmlhYmxlcyBhcyB0aGUgY29tcGVsZXRlIGxpc3QgbG9uZw0KDQpoZWFkKGNvbWJpbmVkLDUpDQoNCmBgYA0KVGhlIHJlc3VsdHMgb2YgdGhlIGFib3ZlIGNodW5rIHNob3dzIHRoYXQgdGhlIGRhdGEgZnVsZmlsbHMgdGhlIG1pbmltdW0gcmVxdWlyZW1lbnRzIDItNA0KDQoqIHJlcXVpcmVtZW50IDItIHRoZSBkYXRhIGluY2x1ZGVkIG11bHRpcGxlIGRhdGF0eXBlcyBpbnQsIGZhY3RvciwgbnVtZXJpYy4NCiogcmVxdWlyZW1lbnQgMy0gY29udGFpbnMgdmFyaWFibGVzIG9uIHdoaWNoIGRhdGEgdHlwZSBjb252ZXJzaW9uIGNhbiBiZSBhcHBsaWVkLm51bWVyaWMtPmZhY3RvciBhbmQgbnVtZXJpYy0+IGRhdGUgKGNvZGUgaXMgaW4gbmV4dCBjaHVuaykNCiogcmVxdWlyZW1lbnQgNC0+Y29udGFpbnMgZmFjdG9yIHZhcmlhYmxlIHRoYXQgbmVlZHMgdG8gYmUgb3JkZXJlZChjb2RlIGlzIGluIG5leHQgY2h1bmspDQoNCmBgYHtyfQ0KI2RhdGF0eXBlIGNvbnZlcnNpb24NCiNudW1lcmljLT5mYWN0b3INCmNvbWJpbmVkJEZMQUdfTU9CSUw8LWZhY3Rvcihjb21iaW5lZCRGTEFHX01PQklMKQ0KY29tYmluZWQkRkxBR19FTVBfUEhPTkU8LWZhY3Rvcihjb21iaW5lZCRGTEFHX0VNUF9QSE9ORSkNCmNvbWJpbmVkJEZMQUdfV09SS19QSE9ORTwtZmFjdG9yKGNvbWJpbmVkJEZMQUdfV09SS19QSE9ORSkNCg0KI251bWVyaWMtPmRhdGUNCmE8LWRkYXlzKGNvbWJpbmVkJERBWVNfQklSVEgpDQojI2Fzc3VtZSB0aGUgZGF0ZSBvbiB3aG9jaCBkYXRhIGlzIGdlbmVyZXRlZCBpcyAxOHRoIE1heSAyMDE4DQpiaXJ0aF9kYXRlPC1hcy5EYXRlKCIyMDE4LTA1LTE4IikrYQ0KY2xhc3MoYmlydGhfZGF0ZSkNCg0KDQojZmFjdG9yIHZhcmlhYmxlIGZyb20gdW5vcmRlcmVkIHRvIG9yZGVyZWQNCiMjY2hlY2sgdGhlIGxldmVscyBvZiBmYWN0b3IgdmFyaWFibGUNCmxldmVscyhjb21iaW5lZCRXRUVLREFZX0FQUFJfUFJPQ0VTU19TVEFSVCkNCiNvcmRlciB0aGUgbGV2ZWxzDQpjb21iaW5lZCRXRUVLREFZX0FQUFJfUFJPQ0VTU19TVEFSVDwtIG9yZGVyZWQoY29tYmluZWQkV0VFS0RBWV9BUFBSX1BST0NFU1NfU1RBUlQsbGV2ZWxzPWMoIk1PTkRBWSIsIlRVRVNEQVkiLCJXRURORVNEQVkiLCJUSFVSU0RBWSIsIkZSSURBWSIsIlNBVFVSREFZIiwiU1VOREFZIikpDQojcmUgY2hlY2sgd2hldGhlciB0aGUgbGV2ZWxzIGhhcyBiZWVuIG9yZGVyZWQgb3Igbm90DQpsZXZlbHMoY29tYmluZWQkV0VFS0RBWV9BUFBSX1BST0NFU1NfU1RBUlQpDQoNCmBgYA0KKiBudW1lcmljLT5mYWN0b3I6IHRoZSBmbGFnIGNvbHVtbnMgaGFkIHZhbHVlcyBlaXRoZXIgMCBvciAxLiBBZnRlciBjb252ZXJ0aW5nIGl0IGludG8gZmFjdG9yIGl0ICBjcmVhdGVkIDIgbGV2ZWxzDQoqIG51bWVyaWMtPiBkYXRlOiBEQVlTX0JJUlRIIGNvbHVtbiBoYXMgY2xpZW50J3MgYWdlIGluIG51bWJlciBvZiBkYXlzLiB1c2luZyBkZGF5cyBmdW5jdGlvbiBmcm9tIGx1YnJpZGF0ZSBwYWNrYWdlIGFuZCBzdWJ0cmFjdGluZyB0aGUgdmFsdWUgZnJvbSBjdXJyZW50IGRhdGUgZ2l2ZXMgZGF0ZSBvZiBiaXJ0aCBvZiB0aGUgY2xpZW50IHdoaWNoIGlzIGZ1cnRoZXIgdXNlZCB0byBjb252ZXJ0IGl0IGludG8gYWdlLg0KKiBvcmRlcmVkIGZhY3Rvcjogd2Vla2RheV9hcHByX3Byb2Nlc3Nfc3RhcnQueCBjb2x1bW4gaGFzIDcgbGV2ZWxzIGNvbnRhaW5zIHRoZSB3ZWVrZGF5cyBpbiB1bm9yZGVyZWQgZm9ybWF0LiB1c2luZyBvcmRlcmVkIGZ1bmN0aW9uIGRlZmluZWQgdGhlIG9yZGVyIGZvIHRoZSBsZXZlbHMgYXMgTW9uZGF5PFR1ZXNkYXk8V2VkbmVzZGF5PFRodXJzZGF5PEZyaWRheTxTYXR1cmRheTxTdW5kYXkuDQoNCiMjCVRpZHkgJiBNYW5pcHVsYXRlIERhdGEgSSANCg0KVGhlIGRhdGEgaXMgaW4gdGlkeSBmb3JtYXQgYXMgaXQgc2hvd3MgdGhlIGNoYXJhY3RlcmlzdGljcyBvZiB0aWR5IGRhdGEgYXMgYmVsb3cNCg0KKiBFdmVyeSB2YWx1ZSBpbiBhIGRhdGEgYmVsb25ncyB0byBhbiBvYnNlcnZhdGlvbiBhbmQgYSB2YXJpYWJsZS4NCiogVmFyaWFibGUgY29udGFpbnMgdmFsdWVzIHRoYXQgbWVhc3VyZSB0aGUgc2FtZSBhdHRyaWJ1dGUgYWNyb3NzIHVuaXQuKGxpa2UgaW5jb21lX3RvdGFsLGFtb3VudF9jcmVkaXQpDQoqIEV2ZXJ5IHZhcmlhYmxlIGlzIGNvbHVtbiBhcyBhYmxlIHRvIGV4dHJhY3QgbmVlZGVkIHZhcmlhYmxlcy4gQ3JlYXRlZCBuZXcgdmFyaWFibGVzIHVzaW5nIGV4aXN0aW5nIHZhcmlhYmxlIHdhcyBzbW9vdGggYW5kIHBvc3NpYmxlIGJlY2F1c2Ugb2YgdGlkeSBkYXRhIHN0cnVjdHVyZS4NCg0KDQoNCiMjCVRpZHkgJiBNYW5pcHVsYXRlIERhdGEgSUkgDQoNClRvIGFycml2ZSBhdCBhIGRlY2lzaW9uIG9mIHdoZXRoZXIgdG8gYXBwcm92ZSB0aGUgY3JlZGl0IG9yIHRvIGRpc2FwcHJvdmUgdHdvIG1vcmUgaW1wb3J0YW50IHByZWRpY3RvcnMgY2FuIGJlIGNyZWF0ZWQgZnJvbSBleGlzdGluZyBkYXRhLCBBR0UgYW5kIEFNT1VOVCByZW1haW4gaW4gaGFuZCBhZnRlciBwYXlpbmcgYW55IG9uZ29pbmcgY3JlZGl0LiBIZW5jZSB0cmllZCBjcmVhdGluZyBuZXcgdmFyaWFibGVzIG9uIHRoZSBiYXNpcyBvZiBhdmFpbGFibGUgaW5mb3JtYXRpb24uDQoNCg0KYGBge3J9DQojY3JlYXRlIG5ldyBjb2x1bW4gaW4gZGF0YSBmcmFtZSJBR0UiIHdjaGljaCB3aWxsIHN0b3JlIHRoIGNsaWVudCdzIGFnZQ0KY29tYmluZWQkQUdFPC1yb3VuZChpbnRlcnZhbChzdGFydCA9IGJpcnRoX2RhdGUsIGVuZCA9ICIyMDE4LTA1LTE4IikgLyANCiAgZHVyYXRpb24obnVtID0gMSwgdW5pdHMgPSAieWVhcnMiKSkNCg0KI2NyZWF0ZSBuZXcgdmFyaWFibGUgd2hpY2ggd2lsbCBzaG93IHRoZSByZW1haW5pbmcgYW5udWFsIGFtb3VudCBmcm9tIGluY29tZSBhZnRlciBkZWN1Y3RpbmcgdGhlIGxvYW4gYW1vdW50DQpjb21iaW5lZDwtdHJhbnNmb3JtKGNvbWJpbmVkLElOQ09NRV9NSU5VU19BTlVJVFk9IEFNVF9JTkNPTUVfVE9UQUwtIEFNVF9BTk5VSVRZLngpDQpjb2xzPC1jKCdTS19JRF9DVVJSJywnREFZU19CSVJUSCcsJ0FHRScsJ0FNVF9JTkNPTUVfVE9UQUwnLCdBTVRfQU5OVUlUWS54JywnSU5DT01FX01JTlVTX0FOVUlUWScpDQp0YWlsKGNvbWJpbmVkW2NvbHNdLDUpDQpgYGANCiogQ3JlYXRlZCBuZXcgY29sdW1uICdBR0UnIHdoaWNoIGlzIGNvbXB1dGVkIG9uIHRoZSBiYXNpcyBvZiBiaXJ0aCBkYXRlIGNvbXB1dGVkIGluIGFib3ZlIGNodW5rLiBjb2x1bW4gc2hvd3MgdGhlIGFnZSBvZiB0aGUgY2xpZW50IGluIHllYXJzDQoNCiogSU5DT01FX01JTlVTX0FOTlVJVFkgc2hvd3MgdGhlIGFtb3VudCByZW1haW5pbmcgYWZ0ZXIgcGF5aW5nIHRoZSBsb2FuLg0KDQojIwlTY2FuIEkgDQoNCkJlbG93IGNodW5rIHNob3dzIHRoZSBhbW91bnQgb2YgbWlzc2luZyB2YWx1ZXMgaW4gZGF0YSBhbmQgaG93IHRvIGRlYWwgd2l0aCB0aGVtLg0KDQpgYGB7cn0NCiNtaXNzaW5nIHZhbHVlcyBzY2FuDQojIG51bWJlciBvZiBjb2x1bW5zIGhhdmluZyBtaXNzaW5nIHZhbHVlcw0KbWlzc2luZ192YWxfY29sczwtY29sbmFtZXMoY29tYmluZWQpW2NvbFN1bXMoaXMubmEoY29tYmluZWQpKSA+IDBdDQpsZW5ndGgobWlzc2luZ192YWxfY29scykNCiN0b3RhbCBwZXJjZW50YWdlIG9mIG1pc3NpbmcgdmFsdWVzDQpzdW0oaXMubmEoY29tYmluZWQpKSAvIChucm93KGNvbWJpbmVkKSAqbmNvbChjb21iaW5lZCkpDQoNCiNjb2xTdW1zKGlzLm5hKGNvbWJpbmVkKSkgIyBhcyB0aGUgcmVzdWx0IGlzIGxvbmcgDQojIEhhbmRlbGluZyBtaXNzaW5nIHZhbHVlcw0KI3JlbW92ZSB0aGUgY29sdW1ucyB3aGVyZSB0aGVyZSBhcmUgbW9yZSB0aGFuIDUwJSB2YWx1ZXMgYXJlIG1pc3NpbmcNCm1pc19jb2xzX2xhcmdlPC1jb2xuYW1lcyhjb21iaW5lZClbY29sU3Vtcyhpcy5uYShjb21iaW5lZCkpID4gMjIwMF0gDQpjb21iaW5lZDwtIGNvbWJpbmVkICU+JSBzZWxlY3QoLW9uZV9vZihtaXNfY29sc19sYXJnZSkpDQpkaW0oY29tYmluZWQpDQoNCiNyZXBsYWNlIG1pc3NpbmcgdmFsdWVzIHdpdGggdGhlIE1lZGlhbg0KZm9yKGkgaW4gd2hpY2goc2FwcGx5KGNvbWJpbmVkLGlzLm51bWVyaWMpKSl7DQogIGNvbWJpbmVkW2lzLm5hKGNvbWJpbmVkWyxpXSksIGldIDwtIG1lZGlhbihjb21iaW5lZFssaV0sIG5hLnJtID0gVFJVRSkNCn0NCm1pc3NpbmdfdmFsX2NvbHM8LWNvbG5hbWVzKGNvbWJpbmVkKVtjb2xTdW1zKGlzLm5hKGNvbWJpbmVkKSkgPiAwXQ0KbGVuZ3RoKG1pc3NpbmdfdmFsX2NvbHMpDQpkaW0oY29tYmluZWQpDQojc3RpbGwgYWxsIHRoZSBmYWN0b3IgY29sdW1uIGNvbnRhaW5zIDE1MSBtaXNzaW5nIHZhbHVlcw0KY29tYmluZWQ8LWNvbWJpbmVkW2NvbXBsZXRlLmNhc2VzKGNvbWJpbmVkKSxdDQpwcmludCgnY29tcGxldGUgY2FzZXMgJykNCmRpbShjb21iaW5lZCkNCmBgYA0KKiBPdXQgb2YgMTQwIHZhcmlhYmxlcyA3NCB2YXJpYWJsZXMgaGF2ZSBtaXNzaW5nIHZhbHVlcy4NCiogVG90YWwgcGVyY2VudGFnZSBvZiBtaXNzaW5nIHZhbHVlcyBpcyAxOS42Lg0KKiBjb2xTdW1zIHNob3dzIHRoYXQgc29tZSBvZiB0aGUgY29sdW1ucyBoYXZlIG1vcmUgdGhhbiA1MCUgdmFsdWVzIGFyZSBtaXNzaW5nLg0KKiByZW1vdmVkIGNvbHVtbnMoNDMpIGhhdmluZyBtaXNzaW5nIHZhbHVlIGNvdW50IG1vcmUgdGhhbiA1MCUuIHJlbWFpbmVkIHdpdGggMTA5IHZhcmlhYmxlcy4NCiogZm9yIHJlbWFpbmluZyBjb2x1bW5zIGltcHV0ZWQgd2l0aCB0aGUgbWVkaWFuIG9mIHRoZSBjb2x1bW5zLg0KKiBzdGlsbCB0aGVyZSB3ZXJlIHRocmVlIGZhY3RvciBjb2x1bW5zIGhhdmUgbWlzc2luZyB2YWx1ZXMuDQoqVXNlZCBjb21wbGV0ZS5jYXNlcygpdG8gaWdub3JlIDE1MSBOQSB2YWx1ZSByb3dzWzFdDQoNCmBgYHtyfQ0KI2luY29uc2lzdGFuY2llcyBjaGVjaw0KY29sbmFtZXMoY29tYmluZWQpW2FwcGx5KGNvbWJpbmVkLCAyLCBmdW5jdGlvbih4KSBhbnkoaXMuaW5maW5pdGUoeCkpKT09J1RSVUUnXQ0KY29sbmFtZXMoY29tYmluZWQpW2FwcGx5KGNvbWJpbmVkLCAyLCBmdW5jdGlvbih4KSBhbnkoaXMuZmluaXRlKHgpKSk9PSdUUlVFJ10NCmNvbG5hbWVzKGNvbWJpbmVkKVthcHBseShjb21iaW5lZCwgMiwgZnVuY3Rpb24oeCkgYW55KGlzLm5hbih4KSkpPT0nVFJVRSddDQoNCiMjY2hlY2sgZm9yIGVkaXQgcnVsZXMNCg0KYGBgDQoNCg0KIyMJU2NhbiBJSQ0KVG8gcGVyZm9ybSBvdXRsaWVyIGRldGVjdGlvbiBjcmVhdGVkIHN1YnNldCB3aXRoIDExIG51bWVyaWMgY29sdW1ucy4NCg0KYGBge3IgLGZpZy5hbGlnbiA9ICJ0b3AiICxmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9OH0NCiMjZ2V0IHRoZSBudW1lcmljIGNvbHVtbnMNCm51bV9jb2xzIDwtIHVubGlzdChsYXBwbHkoY29tYmluZWQsIGlzLm51bWVyaWMpKSANCmNvbWJpbmVkX3N1YjwtY29tYmluZWRbICwgbnVtX2NvbHNdICMjOTAgTlVtZXJpYyB2YXJpYWJsZXMNCiMgY3JlYXRpbmcgc3Vic2V0IHdpdGggMTIgZmVhdHVyZXMgdG8gcGVyZnJvbSBvdXRsaWVycyBkZXRlY3Rpb24gYW5kIGRhdGEgbm9ybWFsaXphdGlvbg0KY29sczwtYygiQU1UX0lOQ09NRV9UT1RBTCIsIkFNVF9DUkVESVQiLCJBTVRfQU5OVUlUWS54IiwiQU1UX0dPT0RTX1BSSUNFIiwiQUdFIiwNCiAgICAgICAgIkRBWVNfRU1QTE9ZRUQiLCJFWFRfU09VUkNFXzIiLCJFWFRfU09VUkNFXzMiLCJBUEFSVE1FTlRTX0FWRyIsDQogICAgICAgICJUT1RBTEFSRUFfTU9ERSIsIkRBWVNfQ1JFRElUX0VORERBVEUiKQ0KY29tYmluZWRfc3ViPC1jb21iaW5lZF9zdWJbY29sc10NCmNvbWJpbmVkX3N1YjwtY29tYmluZWRfc3ViW2NvbXBsZXRlLmNhc2VzKGNvbWJpbmVkX3N1YiksXQ0KDQojIGRldGVjdCBvdWxpZXJzDQpwYXIobWFyPWMoMTAsNiw0LDEpKy4yKQ0KQm94cGxvdChjb21iaW5lZF9zdWIsbGFzPTIpIA0KYGBgDQoqIEFjY29yZGluZyB0byBCb3hwbG90IHRoZXJlIGFyZSAxMjAgb3V0bGllcnMgaW4gdGhlc2UgMTIgdmFyaWFibGVzLg0KKiBNb3N0IG9mIHRoZSBvdXRsaWVycyBvYnNlcnZlZCBpbiB0aHJlZSBjb2x1bW5zLg0KKiBUbyBhdm9pZCBzaW1wbHkgcmVtb3ZpbmcgdGhlc2Ugb3V0bGllcnMgcmVjb3JkcyBmcm9tIGRhdGEgZW50aXJlbHkgdXNlZCBjYXBwaW5nIGZ1bmN0aW9uIHRvIHJlcGxhY2UgdGhlIHZhbHVlcyBhYm92ZSA5NXRoIHBlcmNlbnRpbGUgYW5kIGJlbG93IDV0aCBwZXJjZW50aWxlLlsyXQ0KDQpgYGB7cixmaWcuYWxpZ24gPSAidG9wIiAsZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTh9DQojVXNlZCBjYXBwaW5nIG1ldGhvZCB0byByZXBsYWNlIG91dGxpZXJzIGluIHRoZSANCmNhcCA8LSBmdW5jdGlvbih4KXsNCiAgcXVhbnRpbGVzIDwtIHF1YW50aWxlKCB4LCBjKC4wNSwgMC4yNSwgMC43NSwgLjk1ICkgKQ0KICB4WyB4IDwgcXVhbnRpbGVzWzJdIC0gMS41KklRUih4KSBdIDwtIHF1YW50aWxlc1sxXQ0KICB4WyB4ID4gcXVhbnRpbGVzWzNdICsgMS41KklRUih4KSBdIDwtIHF1YW50aWxlc1s0XQ0KICB4DQp9DQoNCmNvbWJfc3ViX3dvX291dDwtYXBwbHkoY29tYmluZWRfc3ViLCAyLCBGVU49Y2FwKQ0KI2JveHBsb3QgYWZ0ZXIgb3V0bGllciBjYXBwaW5nDQpwYXIobWFyPWMoMTAsNiw0LDEpKy4yKQ0KQm94cGxvdChjb21iX3N1Yl93b19vdXQsbGFzPTIpDQpgYGANCiogVGhlIG91dGxpZXJzIHZpc2libGUgaW4gcHJldmlvdXMgYm94IHBsb3QgaGFzIGJlZW4gY2FwcGVkLg0KDQojIwlUcmFuc2Zvcm0gDQpGaXJzdCwgc2VsZWN0ZWQgZml2ZSB2YXJpYWJsZXMgdG8gc2VlIHRoZSBkaXN0cmlidXRpb24gb2YgdGhlbSBpbiB0aGUgZGF0YS5QbG90dGVkIGhpc3RvZ3JhbXMgdG8gc2VlIHRoZSBkaXN0cmlidXRpb24uIA0KDQpgYGB7cixmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9Nn0NCiNjaGVjayB0aGUgbmVlZCBmb3Igbm9ybWFsaXphdGlvbg0KY29sbmFtZXMgPC0gY29sbmFtZXMoY29tYl9zdWJfd29fb3V0KQ0KcGFyKG1mcm93ID0gYygzLCAyKSkNCmZvciAoaSBpbiAxOjUpIHsNCiAgaGlzdCh1bmxpc3QoY29tYl9zdWJfd29fb3V0WyxpXSksIG1haW4gPSBjb2xuYW1lc1tpXSkNCn0NCg0KYGBgDQoqIGl0IGlzIGNsZWFyIGZyb20gdGhlIGhpc3RvZ3JhbXMgdGhlIHJhbmdlIG9mIHRoZSBkYXRhIGZvciBlYWNoIHZhcmlhYmxlIGlzIG5vdCBzYW1lLiBJbiBmYWN0LCB0aGVyZSBpcyBhIGh1Z2UgZGlmZmVyZW5jZS4gRm9yIGV4YW1wbGUsIGFnZSB2YXJpYWJsZSBoYXMgcmFuZ2UgZnJvbSAyMC0xNyB3aGVyZWFzIEFNVF9HT09TX1BSSUNFIGhhcyByYW5nZSBmcm9tIDAtMTQwMDAwMC4NCiogSWYgd2UgcnVuIHRoZSBtb2RlbCBvbiB0aGlzIGRhdGEsIHJlc3VsdHMgd2lsbCBiZSBiaWFzZWQuDQoqIEhlbmNlLCBuZWVkIHRvIG5vcm1hbGl6ZSB0aGUgZGF0YS4NCiogVXNlZCBCb3hDb3ggdHJhbnNmb3JtYXRpb24gdG8gbm9ybWFsaXplIHRoZSBkYXRhLg0KDQoNCmBgYHtyLGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD02fQ0KYm94Y294bm9ybSA8LSBhcHBseShjb21iX3N1Yl93b19vdXQsIDIsIGZ1bmN0aW9uKHgpIEJveENveCh4LCBsYW1iZGEgPSAiYXV0byIpKQ0KI2hpc3RvZ3JhbXMgYWZ0ZXIgbm9ybWFsaXNhdGlvbg0KcGFyKG1mcm93ID0gYygzLCAyKSkNCmZvciAoaSBpbiAxOjUpIHsNCiAgaGlzdCh1bmxpc3QoYm94Y294bm9ybVssaV0pLCBtYWluID0gY29sbmFtZXNbaV0pDQp9DQpgYGANCiogSXQgaXMgY2xlYXIgZnJvbSB0aGUgYWJvdmUgaGlzdG9ncmFtcyB0aGF0IHRoZSBkYXRhIGhhcyBiZWVuIG5vcm1saXNlZC4NCiogVGhlIHNjYWxlIG9mIHRoZSBkYXRhIGhhcyBiZWVuIGNoYW5nZWQuDQoqIHRoZXJlIGlzIG5vdCBtdWNoIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgcmFuZ2VzIGZvciBldmVyeSB2YXJpYWJsZS5bM10NCg0KIyMJRmVhdHVyZSBTZWxlY3Rpb24gYW5kIEV4dHJhY3Rpb24NClRoZSBkYXRhc2V0IGNvbnNpc3RzIG9mIGxhcmdlIHNldCBvZiB2YXJpYWJsZXMgYW5kIG1vc3Qgb2YgdGhlIHZhcmlhYmxlcyBhcmUgY29ycmVsYXRlZCBoZW5jZSB0aGVyZSBzaSBuZWVkIHRvIGZpbmQgZmV3IGltcG9ydGFudCB2YXJpYWJsZXMuIFByaW5jaXBhbCBjb21wb25lbnQgYW5hbHlzaXMgaXMgb25lIG9mIHRoZSBtZXRob2Qgd2hpY2ggZXh0cmFjdCBpbXBvcnRhbnQgdmFyaWFibGVzIGZyb20gbGFyZ2Ugc2V0Lg0KDQoqIFRvIHBlcmZvcm0gUENBIGRhdGEgc2hvdWxkIGJlIG51bWVyaWMgYW5kIHN0YW5kYXJkaXplZCB3aGljaCB3ZSBkaWQgaW4gcHJldmlvdXMgY2h1bmsuDQoqIFNpbmNlLCB0aGUgZGF0YSB3aGljaCB3aWxsIGJlIHVzZWQgYnkgbW9kZWwgY29uc2lzdHMgb2YgNzkgbnVtZXJpYyB2YXJpYWJsZXMuIGkuZSBtb3JlIHRoYW4gMTAwMCBwbG90cyB0byBhbmFseXplIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB2YXJpYWJsZXMuDQoqIHNlbGVjdGVkIG9ubHkgbnVtZXJpYyBjb2x1bW5zIG9mIHRoZSBkYXRhDQoqIGlkZW50aWZpZWQgYW5kIHJlbW92ZWQgemVybyB2YXJpYW5jZSBjb2x1bW5zIGZyb20gZGF0YSBhcyBwcmNvbXAgY2Fubm90IHJlc2NhbGUgYSBjb25zdGFudC96ZXJvIGNvbHVtbiB0byB1bml0IHZhcmlhbmNlLg0KKiByZW1vdmVkIElEIHJlbGF0ZWQgY29sdW1ucyBmcm9tIGRhdGEgYXMgdGhleSBubyBtYWtlIHNlbnNlIHdoZW4gaXQgY29tZXMgdG8gUENBLg0KKiB1c2VkIHByY29tcCB0byBhcHBseSBQQ0Egb24gdGhlIGRhdGEsIHdoaWNoIGJ5IGRlZmF1bHQgY2VudGVycyB0aGUgdmFyaWFibGUgdG8gaGF2ZSBtZWFuIGVxdWFscyB0byB6ZXJvLiBzY2FsZS49VCBwYXJhbWV0ZXIgbm9ybWFsaXplIHRoZSB2YXJpYWJsZXMgdG8gaGF2ZSBTRCBlcXVhbHMgdG8gMS4NCiogVGhpcyBmdW5jdGlvbiByZXN1bHRzIGZpdmUgdXNlZnVsIG1lYXN1cmVzKCJzZGV2Iiwicm90YXRpb24iLCJjZW50ZXIiLCJzY2FsZSIsIngiKVs0LDZdDQoNCmBgYHtyfQ0KI3NlbGVjdCBvbmx5IG51bWVyaWMgY29sdW1ucw0KbnVtX2NvbHMgPC0gdW5saXN0KGxhcHBseShjb21iaW5lZCwgaXMubnVtZXJpYykpIA0KY29tYmluZWRfbW9kPC1jb21iaW5lZFsgLCBudW1fY29sc10gIyM0NDk2DQpjb21iaW5lZF9tb2QgPC0gY29tYmluZWRfbW9kW2NvbXBsZXRlLmNhc2VzKGNvbWJpbmVkX21vZCksXQ0KI3JlbW92ZSB6ZXJvIHZhcmlhbmNlIGNvbHVtbnMgZnJvbSBkYXRhDQpjb21iaW5lZF9tb2Q8LWNvbWJpbmVkX21vZFsgLCBhcHBseShjb21iaW5lZF9tb2QsIDIsIHZhcikgIT0gMF0NCiNyZW1vdmUgSUQgcmVsYXRlZCBjb2x1bW5zIGZvcm0gdGhlIGRhdGENCmNvbWJpbmVkX21vZDwtY29tYmluZWRfbW9kICU+JSBzZWxlY3QoLWMoU0tfSURfQ1VSUixTS19JRF9CVVJFQVUpKQ0KZGltKGNvbWJpbmVkX21vZCkNCiNhcHBseSBQQ0ENCnBjYV9jb21iaW5lZCA8LSBwcmNvbXAoY29tYmluZWRfbW9kLCBzY2FsZS4gPSBUKQ0KI2NvbXB1dGUgc3RhbmRhcmQgZGV2aWF0aW9uIG9mIGVhY2ggcHJpbmNpcGFsIGNvbXBvbmVudA0Kc3RkX2RldiA8LSBwY2FfY29tYmluZWQkc2Rldg0KI2NvbXB1dGUgdmFyaWFuY2UNCnByX3ZhciA8LSBzdGRfZGV2XjINCiNjaGVjayB2YXJpYW5jZSBvZiBmaXJzdCAxMCBjb21wb25lbnRzDQpwcl92YXJbMToxMF0NCiNhaW0gaXMgdG8gZmluZCBtYXhpbXVtIHZhcmlhbmNlLmhpZ2hlciBpcyB0aGUgdmFyaWFuY2UsIGhpZ2hlciB3aWxsIGJlIHRoZSBpbmZvcm1hdGlvbi4NCiNwcm9wb3J0aW9uIG9mIHZhcmlhbmNlIGV4cGxhaW5lZA0KcHJvcF92YXJleCA8LSBwcl92YXIvc3VtKHByX3ZhcikNCnByb3BfdmFyZXhbMToxMF0NCmBgYA0KKiBUaGUgcHJvcG9ydGlvbiBvZiB2YXJpYW5jZSBzaG93cyB0aGF0IHRoZSAxc3QgQ29tcG9uZW50IGV4cGxhaW5zIDEyJSB2YXJpYW5jZSwgMm5kIGNvbXBvbmVudCBleHBsYWlucyA2LjMgJSB2YXJpYW5jZSBhbmQgc28gb24uDQoqIFRvIGRlY2lkZSBob3cgbWFueSBjb21wb25lbmV0cyBzaG91bGQgYmUgc2VsZWN0ZWQgdXNlZCBzY2F0dGVyIHBsb3Qud2hpY2ggc2hvd3MgdGhlIGZhY3RvcnMgd2hpY2ggZXhwbGFpbnMgdGhlIG1vc3Qgb2YgdmFyaWFiaWxpdHkuWzQsNl0NCg0KYGBge3IsZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTV9DQpwYXIobWZyb3cgPSBjKDEsIDIpKQ0KcGxvdChwcm9wX3ZhcmV4LCB4bGFiID0gIlByaW5jaXBhbCBDb21wb25lbnQiLA0KICAgICB5bGFiID0gIlByb3BvcnRpb24gb2YgVmFyaWFuY2UgRXhwbGFpbmVkIiwNCiAgICAgdHlwZSA9ICJiIikNCiNjdW11bGF0aXZlIHNjcmVlIHBsb3QNCnBsb3QoY3Vtc3VtKHByb3BfdmFyZXgpLCB4bGFiID0gIlByaW5jaXBhbCBDb21wb25lbnQiLA0KICAgICAgIHlsYWIgPSAiQ3VtdWxhdGl2ZSBQcm9wb3J0aW9uIG9mIFZhcmlhbmNlIEV4cGxhaW5lZCIsDQogICAgICAgdHlwZSA9ICJiIikNCg0KYGBgDQpUaGUgcGxvdHMgYWJvdmUgc2hvd3MgdGhhdCAgfjU4IGNvbXBvbmVudHMgZXhsYWlucyBhcm91bmQgOTglIHZhcmlhbmNlLlNhbWUgaXMgY29uZmlybWVkIHdpdGggY3VtdWxhdGl2ZSB2YXJhaW5jZSBwbG90LlRoZXJlZm9yZSwgdGhlIG51bWJlciBvZiBjb21wb25lbnRzIGFzIDU4IGNhbiBiZSB1c2VkIHRvIHBlcmZvcm0gbW9kZWxsaW5nLls2XQ0KDQojIyBSZWZlcmVuY2VzOg0KDQoxLldlZWs2IERlbW8gc2xpZGVzLGNsYXNzIHdvcmtzaGVldHMNCg0KMi5XZWVrNyBEZW1vIHNsaWRlcyxjbGFzcyB3b3Jrc2hlZXRzDQoNCjMuV2VlazkgRGVtbyBzbGlkZXMsY2xhc3Mgd29ya3NoZWV0cw0KDQo0LldlZWsxMCBEZW1vIHNsaWRlcyxjbGFzcyB3b3Jrc2hlZXRzDQoNCjUud3d3LnJkb2N1bWVudGF0aW9uLm9yZw0KDQo2Lmh0dHBzOi8vd3d3LmFuYWx5dGljc3ZpZGh5YS5jb20vYmxvZy8yMDE2LzAzL3ByYWN0aWNhbC1ndWlkZS1wcmluY2lwYWwtY29tcG9uZW50LWFuYWx5c2lzLXB5dGhvbi8NCg0KNy5odHRwczovL3d3dy5rYWdnbGUuY29tL2MvaG9tZS1jcmVkaXQtZGVmYXVsdC1yaXNrL2RhdGENCg0KDQo8YnI+DQo8YnI+DQo=