Required packages
library(readr)
library(tidyr)
library(dplyr)
library(Hmisc)
library(outliers)
library(lubridate)
library(stringr)
library(deductive)
library(validate)
library(forecast)
Executive Summary
Main objective of this report is to apply the functions that we have learnt throughout Data Preprocessing course to a real world dataset in order to fulfill Assignment 3 requirements. A dataset was collected from world bank dataset which contained 5 .csv files, but two were enough for our investigation. Dataset contained character, numeric and factor variables which is a major requirement for the assignment. Both the selected datasets had to be cleaned priior to joining. Renaming, Gather and Spread functions were used to tidy up the datasets which were joined later. Joined dataset was amalyzed using the structure of the variables and necessary transformations were performed such as character -> numeric, character -> factor, charactor -> ordered factor in order to maintain a relaiable dataset to perform any statistical analysis. Dataset was a tidy dataset after all and created a new variable using mutate function. All the NAs in all the columns were identified and performed imputation for required variables by getting the mean value of the variable with the group by function. Possible outliers were detected using z-scores function for all the numeric variables and imputation was performed by replacing the mean value. Finally, log transformation was performed to make the distibution of the selected variable to be normal which was acutually right skewed when inpecting histogram.
Data
Poverty Dataset was extracted from World Bank website which contains different countries across globe.
Data Source: https://datacatalog.worldbank.org/dataset/poverty-and-equity-database
There were 5 .csv files but we Selected two of the datasets which we thought are more important perform data preprocessing.
- PovStatsCountry.csv contains 30 columns including Country_code, SHORT_NAME, ALPHA_CODE, Currency_Unit,Region, Income_Group. Dimensional Data with country code, short name of the country, cureency unit of the country, Region of the country in the world, Income group suggests whether it is Low, Medium or High income group can be understood from those columns. Data types of major columns in the dataset can be identified as follow.
Country_code -> Character SHORT_NAME -> Character ALPHA_CODE -> Character Currency_Unit -> Character Region -> Character (Supposed to be a factor) Income_Group -> Character (Supposed to be an ordered factor)
- PovStatsData.csv contains 48 columns including Country_code, Indicator_code, and year column from 1974 - 2018. Measure or Fact data with country code, country name, Indicator code as measure type and 1974 to 2017 measures can be identified from these columns.
country code -> Character country name -> Character Indicator code -> Character Year column from 1974 - 2018 -> Numeric
#Import Data from PovStatsCountry.csv
POV_COUNTRY <- read_csv("PovStatsCountry.csv")
Missing column names filled in: 'X31' [31]Parsed with column specification:
cols(
.default = col_character(),
`National accounts reference year` = col_integer(),
`Latest industrial data` = col_integer(),
`Latest trade data` = col_integer()
)
See spec(...) for full column specifications.
head(POV_COUNTRY)
#Selection of required variables from the dataset and Renaming variables
POV_COUNTRY_RENAME <- POV_COUNTRY %>% select(Country_code = "Country Code", SHORT_NAME = "Short Name" ,ALPHA_CODE = "2-alpha code" , Currency_Unit = "Currency Unit",Region = "Region" , Income_Group = "Income Group" )
head(POV_COUNTRY_RENAME)
#Import Data from PovStatsData.csv
POV_STATS_DATA <- read_csv("PovStatsData.csv")
Missing column names filled in: 'X49' [49]Parsed with column specification:
cols(
.default = col_double(),
`Country Name` = col_character(),
`Country Code` = col_character(),
`Indicator Name` = col_character(),
`Indicator Code` = col_character(),
X49 = col_character()
)
See spec(...) for full column specifications.
head(POV_STATS_DATA)
#Selection of required variables from the dataset
POV_STATS_SELECT <- POV_STATS_DATA %>% select (2, 4, 41:44)
head(POV_STATS_SELECT)
#Renaming of some of the selected variables
POV_STATS_RENAME <- POV_STATS_SELECT %>% rename(Country_code = 'Country Code', Indicator_code = 'Indicator Code' )
head(POV_STATS_RENAME)
#Filteration to select only observations based on the Income.
POV_STATS_FILTER = POV_STATS_RENAME %>% filter(Indicator_code %in% c("SI.POV.NOP1" , "SI.POV.LMIC.NO", "SI.POV.UMIC.NO" , "SP.POP.TOTL" ))
head(POV_STATS_FILTER)
#Implemented data tidy techniques like Gather, Spread to tidy up data
POV_STATS_GATHER <- POV_STATS_FILTER %>% gather ('2010','2011','2012','2013', key = year, value = 'value' )
head(POV_STATS_GATHER)
POV_STATS_SPREAD <- POV_STATS_GATHER %>% spread(key = Indicator_code, value = 'value')
head(POV_STATS_SPREAD)
#Renaming of some of the selected variables
POV_SPREAD_RENAME <- POV_STATS_SPREAD %>% rename(POOR_199c_DAY=SI.POV.NOP1,POOR_320c_DAY = SI.POV.LMIC.NO, POOR_550c_DAY=SI.POV.UMIC.NO, POP_TOTL = SP.POP.TOTL )
head(POV_SPREAD_RENAME)
#Joining of both the data sets using Country code to create final data set.
POV_COUNTRY_STATS_JOIN <- POV_COUNTRY_RENAME %>% inner_join (POV_SPREAD_RENAME, by = 'Country_code')
head(POV_COUNTRY_STATS_JOIN)
Both PovStatsCountry.csv and PovStatsData.csv datasets were imported and only selected columns which are required to perform an analysis.
Some of the selected columns from both the datasets were renamed for better identification.
Filteration was performed to select values of ‘Indicator Code’ to be included as columns.
Gather, Spread functions were used to tidy up the untidy data in both the datasets.
Renaming of some of the columns was performed after tyding up daatasets.
Finally both the datasets were joined using inner join.
Understand
#Description of the joined data set
str(POV_COUNTRY_STATS_JOIN)
Classes tbl_df, tbl and 'data.frame': 736 obs. of 11 variables:
$ Country_code : chr "AFG" "AFG" "AFG" "AFG" ...
$ SHORT_NAME : chr "Afghanistan" "Afghanistan" "Afghanistan" "Afghanistan" ...
$ ALPHA_CODE : chr "AF" "AF" "AF" "AF" ...
$ Currency_Unit: chr "Afghan afghani" "Afghan afghani" "Afghan afghani" "Afghan afghani" ...
$ Region : chr "South Asia" "South Asia" "South Asia" "South Asia" ...
$ Income_Group : chr "Low income" "Low income" "Low income" "Low income" ...
$ year : chr "2010" "2011" "2012" "2013" ...
$ POOR_320c_DAY: num NA NA NA NA NA NA NA NA NA NA ...
$ POOR_199c_DAY: num NA NA NA NA NA NA NA NA NA NA ...
$ POOR_550c_DAY: num NA NA NA NA NA NA NA NA NA NA ...
$ POP_TOTL : num 28803167 29708599 30696958 31731688 23369131 ...
#Converting year to numeric
POV_COUNTRY_STATS_JOIN$year = as.numeric(POV_COUNTRY_STATS_JOIN$year)
#Converting Region, Income Group to factor
POV_COUNTRY_STATS_JOIN$Region = as.factor(POV_COUNTRY_STATS_JOIN$Region)
POV_COUNTRY_STATS_JOIN$Income_Group = as.factor(POV_COUNTRY_STATS_JOIN$Income_Group)
#Converting Income Group factor variable to ordered factor variable
POV_COUNTRY_STATS_JOIN$Income_Group<- factor(POV_COUNTRY_STATS_JOIN$Income_Group,levels=c("High income","Upper middle income","Lower middle income","Low income"),labels=c("High income","Upper middle income","Lower middle income","Low income"),ordered=TRUE)
# The final structure of the dataset
str(POV_COUNTRY_STATS_JOIN)
Classes tbl_df, tbl and 'data.frame': 736 obs. of 11 variables:
$ Country_code : chr "AFG" "AFG" "AFG" "AFG" ...
$ SHORT_NAME : chr "Afghanistan" "Afghanistan" "Afghanistan" "Afghanistan" ...
$ ALPHA_CODE : chr "AF" "AF" "AF" "AF" ...
$ Currency_Unit: chr "Afghan afghani" "Afghan afghani" "Afghan afghani" "Afghan afghani" ...
$ Region : Factor w/ 7 levels "East Asia & Pacific",..: 6 6 6 6 7 7 7 7 2 2 ...
$ Income_Group : Ord.factor w/ 4 levels "High income"<..: 4 4 4 4 3 3 3 3 2 2 ...
$ year : num 2010 2011 2012 2013 2010 ...
$ POOR_320c_DAY: num NA NA NA NA NA NA NA NA NA NA ...
$ POOR_199c_DAY: num NA NA NA NA NA NA NA NA NA NA ...
$ POOR_550c_DAY: num NA NA NA NA NA NA NA NA NA NA ...
$ POP_TOTL : num 28803167 29708599 30696958 31731688 23369131 ...
Structure of the joined dataset was reviewed.
Year column was converted to a numeric column which was a character column before.
Region column and Income_Group columns were converted to factor variables which were in character format.
At the end, Income_Group factor variable was convereted to an ordered factor.
Tidy & Manipulate Data I
This part have been done in The first section of the assignment (Data). Therefore, the POV_COUNTRY_STATS_JOIN is in tidy format because of the following reasons:
1)Every variable in its own column.
2)Every observation in its own row.
3)Every value have its own cell for easy understanding and further analysis.
Tidy & Manipulate Data II
#Creation of a new column to represent country wise population earning more than 550 cents per day.
POV_STATS_MUTATE = POV_COUNTRY_STATS_JOIN %>% mutate( POP_ABOVE_550c_DAY = POP_TOTL - (POOR_550c_DAY)*1000000)
head(POV_STATS_MUTATE)
POOR_550c_DAY represents cumulative sum of population who are earning less than are equal to 550 cents (i.e. This variable included who are earning 199c per day and 320 cents per day) in millions.
We calculated a new variable to identify who are earning more than 550 cents per day by subracting POOR_550c_DAY multiplied by a million from total population.
Used mutate to include new variable “POP_ABOVE_550c_DAY”"
Scan I
#Checking for total values existing in each column in the above data set
colSums(is.na(POV_STATS_MUTATE))
Country_code SHORT_NAME ALPHA_CODE Currency_Unit Region
0 0 4 60 60
Income_Group year POOR_320c_DAY POOR_199c_DAY POOR_550c_DAY
60 0 372 372 372
POP_TOTL POP_ABOVE_550c_DAY
2 372
#Checking for inconsistencies in numeric columns
sum(is.nan(POV_STATS_MUTATE$year))
[1] 0
sum(is.nan(POV_STATS_MUTATE$POOR_199c_DAY))
[1] 0
sum(is.nan(POV_STATS_MUTATE$POOR_320c_DAY))
[1] 0
sum(is.nan(POV_STATS_MUTATE$POOR_550c_DAY))
[1] 0
sum(is.nan(POV_STATS_MUTATE$POP_TOTL))
[1] 0
#Convertion of NA values with mean of the measure group by year and Income to get the most appropriate mean value of similar contries.
POV_STATS_IMPUTE <- POV_STATS_MUTATE %>% group_by(Income_Group, year) %>% mutate(POOR_199c_DAY = ifelse(is.na(POOR_199c_DAY) ,mean(POOR_199c_DAY,na.rm = TRUE), POOR_199c_DAY ), POOR_320c_DAY = ifelse(is.na(POOR_320c_DAY) ,mean(POOR_320c_DAY,na.rm = TRUE), POOR_320c_DAY ), POOR_550c_DAY = ifelse(is.na(POOR_550c_DAY) ,mean(POOR_550c_DAY,na.rm = TRUE), POOR_550c_DAY ), POP_ABOVE_550c_DAY = ifelse(is.na(POP_ABOVE_550c_DAY) ,mean(POP_ABOVE_550c_DAY,na.rm = TRUE), POP_ABOVE_550c_DAY ))
# Convertion of NA values with mean of the measure group by year Country_code to get the most appropriate mean value of same country
POV_STATS_IMPUTE_FINAL <- POV_STATS_IMPUTE %>% group_by(Country_code) %>% mutate(POP_TOTL = ifelse(is.na(POP_TOTL) ,mean(POP_TOTL,na.rm = TRUE), POP_TOTL ))
head(POV_STATS_IMPUTE_FINAL)
#Checking the NAs after Imputation
colSums(is.na(POV_STATS_IMPUTE_FINAL))
Country_code SHORT_NAME ALPHA_CODE Currency_Unit Region
0 0 4 60 60
Income_Group year POOR_320c_DAY POOR_199c_DAY POOR_550c_DAY
60 0 0 0 0
POP_TOTL POP_ABOVE_550c_DAY
0 0
Scanned data set for NA, inconsistencies and errors using is.na, is.nan functions.
Imputed na values with mean of variable group by year, Income Group for POOR_199c_DAY, POOR_320c_DAY, POOR_550c_DAY, POP_ABOVE_550c_DAY variables. Because it is better to get values according to the year and Income Group rather than Region which does not make any sense and not adequate.
Imputed na values with mean of variable group by Country_code for POP_TOTL variable. Because imputing values with other variables is not going to be accurate.
Neglected imputation of other column which are not having a statistical impact according to the scenario.
Scan II
#Finding the locations of outliers in the POOR_320c_DAY variable
POOR_320c_DAY_outliers <- POV_STATS_IMPUTE_FINAL$POOR_320c_DAY%>% scores(type = "z")
which( abs(POOR_320c_DAY_outliers) >3 )
[1] 381 382 383 384 385 386 387 388 557 558 559 709 710 711 712
length (which( abs(POOR_320c_DAY_outliers) >3 ))
[1] 15
#Finding the locations of outliers in the POOR_199c_DAY variable
POOR_199c_DAY_outliers <- POV_STATS_IMPUTE_FINAL$POOR_199c_DAY%>% scores(type = "z")
which( abs(POOR_199c_DAY_outliers) >3 )
[1] 281 282 283 284 381 382 383 384 385 386 387 388 557 589 590 591 592 709 710 711 712
length (which( abs(POOR_199c_DAY_outliers) >3 ))
[1] 21
#Finding the locations of outliers in the POOR_550c_DAY variable
POOR_550c_DAY_outliers <- POV_STATS_IMPUTE_FINAL$POOR_550c_DAY%>% scores(type = "z")
which( abs(POOR_550c_DAY_outliers) >3 )
[1] 381 382 383 384 385 386 387 388 709 710 711 712
length (which( abs(POOR_550c_DAY_outliers) >3 ))
[1] 12
#Finding the locations of outliers in the POP_TOTL variable
POP_TOTL_outliers <- POV_STATS_IMPUTE_FINAL$POP_TOTL%>% scores(type = "z")
which( abs(POP_TOTL_outliers) >3 )
[1] 384 385 386 387 388 429 430 431 432 709 710 711 712
length (which( abs(POP_TOTL_outliers) >3 ))
[1] 13
#Finding the locations of outliers in the POP_ABOVE_550c_DAY variable
POP_ABOVE_550c_DAY_outliers <- POV_STATS_IMPUTE_FINAL$POP_ABOVE_550c_DAY%>% scores(type = "z")
which( abs(POP_ABOVE_550c_DAY_outliers) >3 )
[1] 170 171 172 385 386 387 388 681 682 683 684 709 710 711 712
length (which( abs(POP_ABOVE_550c_DAY_outliers) >3 ))
[1] 15
#Imputing Outliers of numeric variables with mean assuming that outliers are a result of data entry/processing error.
POV_STATS_IMPUTE_FINAL$POOR_320c_DAY [which(abs(POOR_320c_DAY_outliers) >3 )] <- mean(POV_STATS_IMPUTE_FINAL$POOR_320c_DAY, na.rm = TRUE)
POOR_320c_DAY_outliers_imputed <- POV_STATS_IMPUTE_FINAL$POOR_320c_DAY%>% scores(type = "z")
length (which( abs(POOR_320c_DAY_outliers_imputed) >3 ))
[1] 23
#Imputing Outliers of numeric variables with mean assuming that outliers are a result of data entry/processing error.
POV_STATS_IMPUTE_FINAL$POOR_199c_DAY [which(abs(POOR_199c_DAY_outliers) >3 )] <- mean(POV_STATS_IMPUTE_FINAL$POOR_199c_DAY, na.rm = TRUE)
POOR_199c_DAY_outliers_imputed <- POV_STATS_IMPUTE_FINAL$POOR_199c_DAY%>% scores(type = "z")
length (which( abs(POOR_199c_DAY_outliers_imputed) >3 ))
[1] 24
#Imputing Outliers of numeric variables with mean assuming that outliers are a result of data entry/processing error.
POV_STATS_IMPUTE_FINAL$POOR_550c_DAY [which(abs(POOR_550c_DAY_outliers) >3 )] <- mean(POV_STATS_IMPUTE_FINAL$POOR_550c_DAY, na.rm = TRUE)
POOR_550c_DAY_outliers_imputed <- POV_STATS_IMPUTE_FINAL$POOR_550c_DAY%>% scores(type = "z")
length (which( abs(POOR_550c_DAY_outliers_imputed) >3 ))
[1] 26
#Imputing Outliers of numeric variables with mean assuming that outliers are a result of data entry/processing error.
POV_STATS_IMPUTE_FINAL$POP_TOTL [which(abs(POP_TOTL_outliers) >3 )] <- mean(POV_STATS_IMPUTE_FINAL$POP_TOTL, na.rm = TRUE)
POP_TOTL_outliers_imputed <- POV_STATS_IMPUTE_FINAL$POP_TOTL%>% scores(type = "z")
length (which( abs(POP_TOTL_outliers_imputed) >3 ))
[1] 26
#Imputing Outliers of numeric variables with mean assuming that outliers are a result of data entry/processing error.
POV_STATS_IMPUTE_FINAL$POP_ABOVE_550c_DAY [which(abs(POP_ABOVE_550c_DAY_outliers) >3 )] <- mean(POV_STATS_IMPUTE_FINAL$POP_ABOVE_550c_DAY, na.rm = TRUE)
POP_ABOVE_550c_DAY_outliers_imputed <- POV_STATS_IMPUTE_FINAL$POP_ABOVE_550c_DAY %>% scores(type = "z")
length (which( abs(POP_ABOVE_550c_DAY_outliers_imputed) >3 ))
[1] 20
Found the outliers of all the numeric variables using z-scores method except ‘year’ column which is not requrired to be considered for searching outliers.
There were some ouliers which might had been due to data entering.
imputation was used to remove the outliers of all the numeric vatiables by respective mean values.
However, for some reason outlier count got increased after the imputation. It might be due to the fact that data entry error might contain far larger values than existing values.
LS0tDQp0aXRsZTogIk1BVEgyMzQ5IFNlbWVzdGVyIDIsIDIwMTgiDQphdXRob3I6ICJIZXdheWFsYWdlIFZpc2h2YSBMYWhpcnUgS2FudGhhIEFiZXlyYXRobmUgKFMzNzM1MTk1KSwgVmlzaHdhbmFkaGFtIEtvZHVrdWxhIChTMzc0OTg1NSksIEtvZGl0aHV3YWtrdSBBcmFjaGNoaWdlIElyZXNoIFVkYXJhIEthdXNoYWx5YSAoUzM3MDQ3NjkpICIgDQpzdWJ0aXRsZTogQXNzaWdubWVudCAzDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQNCi0tLQ0KDQojIyBSZXF1aXJlZCBwYWNrYWdlcyANCg0KYGBge3J9DQoNCmxpYnJhcnkocmVhZHIpDQpsaWJyYXJ5KHRpZHlyKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkoSG1pc2MpDQpsaWJyYXJ5KG91dGxpZXJzKQ0KbGlicmFyeShsdWJyaWRhdGUpDQpsaWJyYXJ5KHN0cmluZ3IpDQpsaWJyYXJ5KGRlZHVjdGl2ZSkNCmxpYnJhcnkodmFsaWRhdGUpDQpsaWJyYXJ5KGZvcmVjYXN0KQ0KYGBgDQoNCg0KIyMgRXhlY3V0aXZlIFN1bW1hcnkgDQoNCk1haW4gb2JqZWN0aXZlIG9mIHRoaXMgcmVwb3J0IGlzIHRvIGFwcGx5IHRoZSBmdW5jdGlvbnMgdGhhdCB3ZSBoYXZlIGxlYXJudCB0aHJvdWdob3V0IERhdGEgUHJlcHJvY2Vzc2luZyBjb3Vyc2UgdG8gYSByZWFsIHdvcmxkIGRhdGFzZXQgaW4gb3JkZXIgdG8gZnVsZmlsbCBBc3NpZ25tZW50IDMgcmVxdWlyZW1lbnRzLiBBIGRhdGFzZXQgd2FzIGNvbGxlY3RlZCBmcm9tIHdvcmxkIGJhbmsgZGF0YXNldCB3aGljaCBjb250YWluZWQgNSAuY3N2IGZpbGVzLCBidXQgdHdvIHdlcmUgZW5vdWdoIGZvciBvdXIgaW52ZXN0aWdhdGlvbi4gRGF0YXNldCBjb250YWluZWQgY2hhcmFjdGVyLCBudW1lcmljIGFuZCBmYWN0b3IgdmFyaWFibGVzIHdoaWNoIGlzIGEgbWFqb3IgcmVxdWlyZW1lbnQgZm9yIHRoZSBhc3NpZ25tZW50LiBCb3RoIHRoZSBzZWxlY3RlZCBkYXRhc2V0cyBoYWQgdG8gYmUgY2xlYW5lZCBwcmlpb3IgdG8gam9pbmluZy4gUmVuYW1pbmcsIEdhdGhlciBhbmQgU3ByZWFkIGZ1bmN0aW9ucyB3ZXJlIHVzZWQgdG8gdGlkeSB1cCB0aGUgZGF0YXNldHMgd2hpY2ggd2VyZSBqb2luZWQgbGF0ZXIuIEpvaW5lZCBkYXRhc2V0IHdhcyBhbWFseXplZCB1c2luZyB0aGUgc3RydWN0dXJlIG9mIHRoZSB2YXJpYWJsZXMgYW5kIG5lY2Vzc2FyeSB0cmFuc2Zvcm1hdGlvbnMgd2VyZSBwZXJmb3JtZWQgc3VjaCBhcyBjaGFyYWN0ZXIgLT4gbnVtZXJpYywgY2hhcmFjdGVyIC0+IGZhY3RvciwgY2hhcmFjdG9yIC0+IG9yZGVyZWQgZmFjdG9yIGluIG9yZGVyIHRvIG1haW50YWluIGEgcmVsYWlhYmxlIGRhdGFzZXQgdG8gcGVyZm9ybSBhbnkgc3RhdGlzdGljYWwgYW5hbHlzaXMuIERhdGFzZXQgd2FzIGEgdGlkeSBkYXRhc2V0IGFmdGVyIGFsbCBhbmQgY3JlYXRlZCBhIG5ldyB2YXJpYWJsZSB1c2luZyBtdXRhdGUgZnVuY3Rpb24uIEFsbCB0aGUgTkFzIGluIGFsbCB0aGUgY29sdW1ucyB3ZXJlIGlkZW50aWZpZWQgYW5kIHBlcmZvcm1lZCBpbXB1dGF0aW9uIGZvciByZXF1aXJlZCB2YXJpYWJsZXMgYnkgZ2V0dGluZyB0aGUgbWVhbiB2YWx1ZSBvZiB0aGUgdmFyaWFibGUgd2l0aCB0aGUgZ3JvdXAgYnkgZnVuY3Rpb24uIFBvc3NpYmxlIG91dGxpZXJzIHdlcmUgZGV0ZWN0ZWQgdXNpbmcgei1zY29yZXMgZnVuY3Rpb24gZm9yIGFsbCB0aGUgbnVtZXJpYyB2YXJpYWJsZXMgYW5kIGltcHV0YXRpb24gd2FzIHBlcmZvcm1lZCBieSByZXBsYWNpbmcgdGhlIG1lYW4gdmFsdWUuIEZpbmFsbHksIGxvZyB0cmFuc2Zvcm1hdGlvbiB3YXMgcGVyZm9ybWVkIHRvIG1ha2UgdGhlIGRpc3RpYnV0aW9uIG9mIHRoZSBzZWxlY3RlZCB2YXJpYWJsZSB0byBiZSBub3JtYWwgd2hpY2ggd2FzIGFjdXR1YWxseSByaWdodCBza2V3ZWQgd2hlbiBpbnBlY3RpbmcgaGlzdG9ncmFtLg0KDQoNCg0KIyMgRGF0YSANCg0KDQpQb3ZlcnR5IERhdGFzZXQgd2FzIGV4dHJhY3RlZCBmcm9tIFdvcmxkIEJhbmsgd2Vic2l0ZSB3aGljaCBjb250YWlucyBkaWZmZXJlbnQgY291bnRyaWVzIGFjcm9zcyBnbG9iZS4NCg0KRGF0YSBTb3VyY2U6IGh0dHBzOi8vZGF0YWNhdGFsb2cud29ybGRiYW5rLm9yZy9kYXRhc2V0L3BvdmVydHktYW5kLWVxdWl0eS1kYXRhYmFzZQ0KDQpUaGVyZSB3ZXJlIDUgLmNzdiBmaWxlcyBidXQgd2UgU2VsZWN0ZWQgdHdvIG9mIHRoZSBkYXRhc2V0cyB3aGljaCB3ZSB0aG91Z2h0IGFyZSBtb3JlIGltcG9ydGFudCBwZXJmb3JtIGRhdGEgcHJlcHJvY2Vzc2luZy4NCg0KICAxKSBQb3ZTdGF0c0NvdW50cnkuY3N2IGNvbnRhaW5zIDMwIGNvbHVtbnMgaW5jbHVkaW5nIENvdW50cnlfY29kZSwgU0hPUlRfTkFNRSwgQUxQSEFfQ09ERSwgQ3VycmVuY3lfVW5pdCxSZWdpb24sIEluY29tZV9Hcm91cC4gRGltZW5zaW9uYWwgRGF0YSB3aXRoIGNvdW50cnkgY29kZSwgc2hvcnQgbmFtZSBvZiB0aGUgY291bnRyeSwgY3VyZWVuY3kgdW5pdCBvZiB0aGUgY291bnRyeSwgIFJlZ2lvbiBvZiB0aGUgY291bnRyeSBpbiB0aGUgd29ybGQsIEluY29tZSBncm91cCBzdWdnZXN0cyB3aGV0aGVyIGl0IGlzIExvdywgTWVkaXVtIG9yIEhpZ2ggaW5jb21lIGdyb3VwIGNhbiBiZSB1bmRlcnN0b29kIGZyb20gdGhvc2UgY29sdW1ucy4gRGF0YSB0eXBlcyBvZiBtYWpvciBjb2x1bW5zIGluIHRoZSBkYXRhc2V0IGNhbiBiZSBpZGVudGlmaWVkIGFzIGZvbGxvdy4NCiAgDQogIENvdW50cnlfY29kZSAtPiBDaGFyYWN0ZXINCiAgU0hPUlRfTkFNRSAgLT4gQ2hhcmFjdGVyDQogIEFMUEhBX0NPREUgIC0+IENoYXJhY3Rlcg0KICBDdXJyZW5jeV9Vbml0ICAtPiBDaGFyYWN0ZXINCiAgUmVnaW9uICAtPiBDaGFyYWN0ZXIgKFN1cHBvc2VkIHRvIGJlIGEgZmFjdG9yKQ0KICBJbmNvbWVfR3JvdXAgIC0+IENoYXJhY3RlciAoU3VwcG9zZWQgdG8gYmUgYW4gb3JkZXJlZCBmYWN0b3IpDQogIA0KICAyKSBQb3ZTdGF0c0RhdGEuY3N2IGNvbnRhaW5zIDQ4IGNvbHVtbnMgaW5jbHVkaW5nIENvdW50cnlfY29kZSwgSW5kaWNhdG9yX2NvZGUsIGFuZCB5ZWFyIGNvbHVtbiBmcm9tIDE5NzQgLSAyMDE4LiBNZWFzdXJlIG9yIEZhY3QgZGF0YSB3aXRoIGNvdW50cnkgY29kZSwgY291bnRyeSBuYW1lLCBJbmRpY2F0b3IgY29kZSBhcyBtZWFzdXJlIHR5cGUgYW5kIDE5NzQgdG8gMjAxNyBtZWFzdXJlcyBjYW4gYmUgaWRlbnRpZmllZCBmcm9tIHRoZXNlIGNvbHVtbnMuDQogIA0KICBjb3VudHJ5IGNvZGUgLT4gQ2hhcmFjdGVyDQogIGNvdW50cnkgbmFtZSAtPiBDaGFyYWN0ZXINCiAgSW5kaWNhdG9yIGNvZGUgLT4gQ2hhcmFjdGVyDQogIFllYXIgY29sdW1uIGZyb20gMTk3NCAtIDIwMTggLT4gTnVtZXJpYw0KDQoNCmBgYHtyfQ0KDQojSW1wb3J0IERhdGEgZnJvbSBQb3ZTdGF0c0NvdW50cnkuY3N2DQpQT1ZfQ09VTlRSWSA8LSByZWFkX2NzdigiUG92U3RhdHNDb3VudHJ5LmNzdiIpDQpoZWFkKFBPVl9DT1VOVFJZKQ0KDQojU2VsZWN0aW9uIG9mIHJlcXVpcmVkIHZhcmlhYmxlcyBmcm9tIHRoZSBkYXRhc2V0IGFuZCBSZW5hbWluZyB2YXJpYWJsZXMNClBPVl9DT1VOVFJZX1JFTkFNRSA8LSBQT1ZfQ09VTlRSWSAlPiUgc2VsZWN0KENvdW50cnlfY29kZSA9ICJDb3VudHJ5IENvZGUiLCBTSE9SVF9OQU1FID0gIlNob3J0IE5hbWUiICxBTFBIQV9DT0RFID0gIjItYWxwaGEgY29kZSIgLCBDdXJyZW5jeV9Vbml0ID0gIkN1cnJlbmN5IFVuaXQiLFJlZ2lvbiA9ICJSZWdpb24iICwgSW5jb21lX0dyb3VwID0gIkluY29tZSBHcm91cCIgKQ0KaGVhZChQT1ZfQ09VTlRSWV9SRU5BTUUpDQoNCiNJbXBvcnQgRGF0YSBmcm9tIFBvdlN0YXRzRGF0YS5jc3YNClBPVl9TVEFUU19EQVRBIDwtIHJlYWRfY3N2KCJQb3ZTdGF0c0RhdGEuY3N2IikNCmhlYWQoUE9WX1NUQVRTX0RBVEEpDQoNCiNTZWxlY3Rpb24gb2YgcmVxdWlyZWQgdmFyaWFibGVzIGZyb20gdGhlIGRhdGFzZXQNClBPVl9TVEFUU19TRUxFQ1QgPC0gUE9WX1NUQVRTX0RBVEEgJT4lIHNlbGVjdCAoMiwgNCwgNDE6NDQpDQpoZWFkKFBPVl9TVEFUU19TRUxFQ1QpDQoNCiNSZW5hbWluZyBvZiBzb21lIG9mIHRoZSBzZWxlY3RlZCB2YXJpYWJsZXMNClBPVl9TVEFUU19SRU5BTUUgPC0gUE9WX1NUQVRTX1NFTEVDVCAlPiUgcmVuYW1lKENvdW50cnlfY29kZSA9ICdDb3VudHJ5IENvZGUnLCBJbmRpY2F0b3JfY29kZSA9ICdJbmRpY2F0b3IgQ29kZScgKQ0KaGVhZChQT1ZfU1RBVFNfUkVOQU1FKQ0KDQojRmlsdGVyYXRpb24gdG8gc2VsZWN0IG9ubHkgb2JzZXJ2YXRpb25zIGJhc2VkIG9uIHRoZSBJbmNvbWUuDQpQT1ZfU1RBVFNfRklMVEVSID0gUE9WX1NUQVRTX1JFTkFNRSAlPiUgZmlsdGVyKEluZGljYXRvcl9jb2RlICVpbiUgYygiU0kuUE9WLk5PUDEiICwgIlNJLlBPVi5MTUlDLk5PIiwgIlNJLlBPVi5VTUlDLk5PIiAsICJTUC5QT1AuVE9UTCIgKSkNCmhlYWQoUE9WX1NUQVRTX0ZJTFRFUikNCg0KI0ltcGxlbWVudGVkIGRhdGEgdGlkeSB0ZWNobmlxdWVzIGxpa2UgR2F0aGVyLCBTcHJlYWQgdG8gdGlkeSB1cCBkYXRhDQpQT1ZfU1RBVFNfR0FUSEVSIDwtIFBPVl9TVEFUU19GSUxURVIgJT4lIGdhdGhlciAoJzIwMTAnLCcyMDExJywnMjAxMicsJzIwMTMnLCBrZXkgPSB5ZWFyLCB2YWx1ZSA9ICd2YWx1ZScgKQ0KaGVhZChQT1ZfU1RBVFNfR0FUSEVSKQ0KDQpQT1ZfU1RBVFNfU1BSRUFEIDwtIFBPVl9TVEFUU19HQVRIRVIgJT4lIHNwcmVhZChrZXkgPSBJbmRpY2F0b3JfY29kZSwgdmFsdWUgPSAndmFsdWUnKSANCmhlYWQoUE9WX1NUQVRTX1NQUkVBRCkNCg0KI1JlbmFtaW5nIG9mIHNvbWUgb2YgdGhlIHNlbGVjdGVkIHZhcmlhYmxlcw0KUE9WX1NQUkVBRF9SRU5BTUUgPC0gUE9WX1NUQVRTX1NQUkVBRCAlPiUgcmVuYW1lKFBPT1JfMTk5Y19EQVk9U0kuUE9WLk5PUDEsUE9PUl8zMjBjX0RBWSA9IFNJLlBPVi5MTUlDLk5PLCBQT09SXzU1MGNfREFZPVNJLlBPVi5VTUlDLk5PLCBQT1BfVE9UTCA9IFNQLlBPUC5UT1RMICkNCmhlYWQoUE9WX1NQUkVBRF9SRU5BTUUpDQoNCiNKb2luaW5nIG9mIGJvdGggdGhlIGRhdGEgc2V0cyB1c2luZyBDb3VudHJ5IGNvZGUgdG8gY3JlYXRlIGZpbmFsIGRhdGEgc2V0Lg0KUE9WX0NPVU5UUllfU1RBVFNfSk9JTiA8LSBQT1ZfQ09VTlRSWV9SRU5BTUUgJT4lIGlubmVyX2pvaW4gKFBPVl9TUFJFQURfUkVOQU1FLCBieSA9ICdDb3VudHJ5X2NvZGUnKQ0KaGVhZChQT1ZfQ09VTlRSWV9TVEFUU19KT0lOKQ0KDQpgYGANCiogQm90aCBQb3ZTdGF0c0NvdW50cnkuY3N2IGFuZCBQb3ZTdGF0c0RhdGEuY3N2IGRhdGFzZXRzIHdlcmUgaW1wb3J0ZWQgYW5kIG9ubHkgc2VsZWN0ZWQgY29sdW1ucyB3aGljaCBhcmUgcmVxdWlyZWQgICB0byBwZXJmb3JtIGFuIGFuYWx5c2lzLg0KDQoqIFNvbWUgb2YgdGhlIHNlbGVjdGVkIGNvbHVtbnMgZnJvbSBib3RoIHRoZSBkYXRhc2V0cyB3ZXJlIHJlbmFtZWQgZm9yIGJldHRlciBpZGVudGlmaWNhdGlvbi4NCg0KKiBGaWx0ZXJhdGlvbiB3YXMgcGVyZm9ybWVkIHRvIHNlbGVjdCB2YWx1ZXMgb2YgJ0luZGljYXRvciBDb2RlJyB0byBiZSBpbmNsdWRlZCBhcyBjb2x1bW5zLg0KDQoqIEdhdGhlciwgU3ByZWFkIGZ1bmN0aW9ucyB3ZXJlIHVzZWQgdG8gdGlkeSB1cCB0aGUgdW50aWR5IGRhdGEgaW4gYm90aCB0aGUgZGF0YXNldHMuDQoNCiogUmVuYW1pbmcgb2Ygc29tZSBvZiB0aGUgY29sdW1ucyB3YXMgcGVyZm9ybWVkIGFmdGVyIHR5ZGluZyB1cCBkYWF0YXNldHMuDQoNCiogRmluYWxseSBib3RoIHRoZSBkYXRhc2V0cyB3ZXJlIGpvaW5lZCB1c2luZyBpbm5lciBqb2luLg0KDQoNCiMjIFVuZGVyc3RhbmQgDQoNCmBgYHtyfQ0KDQojRGVzY3JpcHRpb24gb2YgdGhlIGpvaW5lZCBkYXRhIHNldA0Kc3RyKFBPVl9DT1VOVFJZX1NUQVRTX0pPSU4pDQoNCiNDb252ZXJ0aW5nIHllYXIgdG8gbnVtZXJpYw0KUE9WX0NPVU5UUllfU1RBVFNfSk9JTiR5ZWFyID0gYXMubnVtZXJpYyhQT1ZfQ09VTlRSWV9TVEFUU19KT0lOJHllYXIpDQoNCiNDb252ZXJ0aW5nIFJlZ2lvbiwgSW5jb21lIEdyb3VwIHRvIGZhY3Rvcg0KUE9WX0NPVU5UUllfU1RBVFNfSk9JTiRSZWdpb24gPSBhcy5mYWN0b3IoUE9WX0NPVU5UUllfU1RBVFNfSk9JTiRSZWdpb24pDQpQT1ZfQ09VTlRSWV9TVEFUU19KT0lOJEluY29tZV9Hcm91cCA9IGFzLmZhY3RvcihQT1ZfQ09VTlRSWV9TVEFUU19KT0lOJEluY29tZV9Hcm91cCkNCg0KI0NvbnZlcnRpbmcgSW5jb21lIEdyb3VwIGZhY3RvciB2YXJpYWJsZSB0byBvcmRlcmVkIGZhY3RvciB2YXJpYWJsZQ0KUE9WX0NPVU5UUllfU1RBVFNfSk9JTiRJbmNvbWVfR3JvdXA8LSBmYWN0b3IoUE9WX0NPVU5UUllfU1RBVFNfSk9JTiRJbmNvbWVfR3JvdXAsbGV2ZWxzPWMoIkhpZ2ggaW5jb21lIiwiVXBwZXIgbWlkZGxlIGluY29tZSIsIkxvd2VyIG1pZGRsZSBpbmNvbWUiLCJMb3cgaW5jb21lIiksbGFiZWxzPWMoIkhpZ2ggaW5jb21lIiwiVXBwZXIgbWlkZGxlIGluY29tZSIsIkxvd2VyIG1pZGRsZSBpbmNvbWUiLCJMb3cgaW5jb21lIiksb3JkZXJlZD1UUlVFKQ0KDQojIFRoZSBmaW5hbCBzdHJ1Y3R1cmUgb2YgdGhlIGRhdGFzZXQNCnN0cihQT1ZfQ09VTlRSWV9TVEFUU19KT0lOKSANCg0KYGBgDQoqIFN0cnVjdHVyZSBvZiB0aGUgam9pbmVkIGRhdGFzZXQgd2FzIHJldmlld2VkLg0KDQoqIFllYXIgY29sdW1uIHdhcyBjb252ZXJ0ZWQgdG8gYSBudW1lcmljIGNvbHVtbiB3aGljaCB3YXMgYSBjaGFyYWN0ZXIgY29sdW1uIGJlZm9yZS4NCg0KKiBSZWdpb24gY29sdW1uIGFuZCBJbmNvbWVfR3JvdXAgY29sdW1ucyB3ZXJlIGNvbnZlcnRlZCB0byBmYWN0b3IgdmFyaWFibGVzIHdoaWNoIHdlcmUgaW4gY2hhcmFjdGVyIGZvcm1hdC4NCg0KKiBBdCB0aGUgZW5kLCBJbmNvbWVfR3JvdXAgZmFjdG9yIHZhcmlhYmxlIHdhcyBjb252ZXJldGVkIHRvIGFuIG9yZGVyZWQgZmFjdG9yLiANCg0KIyMJVGlkeSAmIE1hbmlwdWxhdGUgRGF0YSBJIA0KDQoNClRoaXMgcGFydCBoYXZlIGJlZW4gZG9uZSBpbiBUaGUgZmlyc3Qgc2VjdGlvbiBvZiB0aGUgYXNzaWdubWVudCAoRGF0YSkuIFRoZXJlZm9yZSwgdGhlIFBPVl9DT1VOVFJZX1NUQVRTX0pPSU4gaXMgaW4gdGlkeSBmb3JtYXQgYmVjYXVzZSBvZiB0aGUgZm9sbG93aW5nIHJlYXNvbnM6DQoNCjEpRXZlcnkgdmFyaWFibGUgaW4gaXRzIG93biBjb2x1bW4uDQoNCjIpRXZlcnkgb2JzZXJ2YXRpb24gaW4gaXRzIG93biByb3cuDQoNCjMpRXZlcnkgdmFsdWUgaGF2ZSBpdHMgb3duIGNlbGwgZm9yIGVhc3kgdW5kZXJzdGFuZGluZyBhbmQgZnVydGhlciBhbmFseXNpcy4NCg0KYGBge3J9DQoNCmBgYA0KDQojIwlUaWR5ICYgTWFuaXB1bGF0ZSBEYXRhIElJIA0KDQoNCmBgYHtyfQ0KI0NyZWF0aW9uIG9mIGEgbmV3IGNvbHVtbiB0byByZXByZXNlbnQgY291bnRyeSB3aXNlIHBvcHVsYXRpb24gZWFybmluZyBtb3JlIHRoYW4gNTUwIGNlbnRzIHBlciBkYXkuDQpQT1ZfU1RBVFNfTVVUQVRFID0gUE9WX0NPVU5UUllfU1RBVFNfSk9JTiAlPiUgbXV0YXRlKCBQT1BfQUJPVkVfNTUwY19EQVkgPSBQT1BfVE9UTCAtIChQT09SXzU1MGNfREFZKSoxMDAwMDAwKQ0KaGVhZChQT1ZfU1RBVFNfTVVUQVRFKSANCg0KYGBgDQoNCiogUE9PUl81NTBjX0RBWSByZXByZXNlbnRzIGN1bXVsYXRpdmUgc3VtIG9mIHBvcHVsYXRpb24gd2hvIGFyZSBlYXJuaW5nIGxlc3MgdGhhbiAgICBhcmUgZXF1YWwgdG8gNTUwIGNlbnRzIChpLmUuICAgICBUaGlzIHZhcmlhYmxlIGluY2x1ZGVkIHdobyBhcmUgZWFybmluZyAxOTljIHBlciBkYXkgICBhbmQgMzIwIGNlbnRzIHBlciBkYXkpIGluIG1pbGxpb25zLg0KDQoqIFdlIGNhbGN1bGF0ZWQgYSBuZXcgdmFyaWFibGUgdG8gaWRlbnRpZnkgd2hvIGFyZSBlYXJuaW5nIG1vcmUgdGhhbiA1NTAgY2VudHMgcGVyICAgZGF5IGJ5IHN1YnJhY3RpbmcgUE9PUl81NTBjX0RBWSAgIG11bHRpcGxpZWQgYnkgYSBtaWxsaW9uIGZyb20gdG90YWwgcG9wdWxhdGlvbi4NCg0KKiBVc2VkIG11dGF0ZSB0byBpbmNsdWRlIG5ldyB2YXJpYWJsZSAiUE9QX0FCT1ZFXzU1MGNfREFZIiINCg0KIyMJU2NhbiBJIA0KDQoNCmBgYHtyfQ0KDQojQ2hlY2tpbmcgZm9yICB0b3RhbCB2YWx1ZXMgZXhpc3RpbmcgaW4gZWFjaCBjb2x1bW4gaW4gdGhlIGFib3ZlIGRhdGEgc2V0DQpjb2xTdW1zKGlzLm5hKFBPVl9TVEFUU19NVVRBVEUpKQ0KDQojQ2hlY2tpbmcgZm9yICBpbmNvbnNpc3RlbmNpZXMgaW4gbnVtZXJpYyBjb2x1bW5zDQpzdW0oaXMubmFuKFBPVl9TVEFUU19NVVRBVEUkeWVhcikpDQpzdW0oaXMubmFuKFBPVl9TVEFUU19NVVRBVEUkUE9PUl8xOTljX0RBWSkpDQpzdW0oaXMubmFuKFBPVl9TVEFUU19NVVRBVEUkUE9PUl8zMjBjX0RBWSkpDQpzdW0oaXMubmFuKFBPVl9TVEFUU19NVVRBVEUkUE9PUl81NTBjX0RBWSkpDQpzdW0oaXMubmFuKFBPVl9TVEFUU19NVVRBVEUkUE9QX1RPVEwpKQ0KDQojQ29udmVydGlvbiBvZiBOQSB2YWx1ZXMgd2l0aCBtZWFuIG9mIHRoZSBtZWFzdXJlIGdyb3VwIGJ5IHllYXIgYW5kIEluY29tZSAgdG8gZ2V0IHRoZSBtb3N0IGFwcHJvcHJpYXRlIG1lYW4gdmFsdWUgb2Ygc2ltaWxhciBjb250cmllcy4NClBPVl9TVEFUU19JTVBVVEUgPC0gUE9WX1NUQVRTX01VVEFURSAlPiUgZ3JvdXBfYnkoSW5jb21lX0dyb3VwLCB5ZWFyKSAlPiUgbXV0YXRlKFBPT1JfMTk5Y19EQVkgPSAgaWZlbHNlKGlzLm5hKFBPT1JfMTk5Y19EQVkpICxtZWFuKFBPT1JfMTk5Y19EQVksbmEucm0gPSBUUlVFKSwgUE9PUl8xOTljX0RBWSApLCBQT09SXzMyMGNfREFZID0gIGlmZWxzZShpcy5uYShQT09SXzMyMGNfREFZKSAsbWVhbihQT09SXzMyMGNfREFZLG5hLnJtID0gVFJVRSksIFBPT1JfMzIwY19EQVkgKSwgUE9PUl81NTBjX0RBWSA9ICBpZmVsc2UoaXMubmEoUE9PUl81NTBjX0RBWSkgLG1lYW4oUE9PUl81NTBjX0RBWSxuYS5ybSA9IFRSVUUpLCBQT09SXzU1MGNfREFZICksIFBPUF9BQk9WRV81NTBjX0RBWSA9ICBpZmVsc2UoaXMubmEoUE9QX0FCT1ZFXzU1MGNfREFZKSAsbWVhbihQT1BfQUJPVkVfNTUwY19EQVksbmEucm0gPSBUUlVFKSwgUE9QX0FCT1ZFXzU1MGNfREFZICkpIA0KDQojIENvbnZlcnRpb24gb2YgTkEgdmFsdWVzIHdpdGggbWVhbiBvZiB0aGUgbWVhc3VyZSBncm91cCBieSB5ZWFyIENvdW50cnlfY29kZSB0byBnZXQgdGhlIG1vc3QgYXBwcm9wcmlhdGUgbWVhbiB2YWx1ZSBvZiBzYW1lIGNvdW50cnkNClBPVl9TVEFUU19JTVBVVEVfRklOQUwgPC0gUE9WX1NUQVRTX0lNUFVURSAlPiUgZ3JvdXBfYnkoQ291bnRyeV9jb2RlKSAlPiUgbXV0YXRlKFBPUF9UT1RMID0gIGlmZWxzZShpcy5uYShQT1BfVE9UTCkgLG1lYW4oUE9QX1RPVEwsbmEucm0gPSBUUlVFKSwgUE9QX1RPVEwgKSkNCmhlYWQoUE9WX1NUQVRTX0lNUFVURV9GSU5BTCkNCg0KI0NoZWNraW5nIHRoZSBOQXMgYWZ0ZXIgSW1wdXRhdGlvbg0KY29sU3Vtcyhpcy5uYShQT1ZfU1RBVFNfSU1QVVRFX0ZJTkFMKSkNCg0KYGBgDQogDQoqIFNjYW5uZWQgZGF0YSBzZXQgZm9yIE5BLCBpbmNvbnNpc3RlbmNpZXMgYW5kIGVycm9ycyB1c2luZyBpcy5uYSwgaXMubmFuIGZ1bmN0aW9ucy4NCg0KKiBJbXB1dGVkIG5hIHZhbHVlcyB3aXRoIG1lYW4gb2YgdmFyaWFibGUgZ3JvdXAgYnkgeWVhciwgSW5jb21lIEdyb3VwIGZvciBQT09SXzE5OWNfREFZLCBQT09SXzMyMGNfREFZLCBQT09SXzU1MGNfREFZLCBQT1BfQUJPVkVfNTUwY19EQVkgdmFyaWFibGVzLiAgICAgICAgIEJlY2F1c2UgaXQgaXMgYmV0dGVyIHRvIGdldCB2YWx1ZXMgYWNjb3JkaW5nIHRvIHRoZSB5ZWFyIGFuZCBJbmNvbWUgR3JvdXAgcmF0aGVyICAgdGhhbiBSZWdpb24gd2hpY2ggZG9lcyBub3QgbWFrZSBhbnkgc2Vuc2UgYW5kIG5vdCBhZGVxdWF0ZS4NCg0KKiBJbXB1dGVkIG5hIHZhbHVlcyB3aXRoIG1lYW4gb2YgdmFyaWFibGUgZ3JvdXAgYnkgQ291bnRyeV9jb2RlIGZvciBQT1BfVE9UTCB2YXJpYWJsZS4gQmVjYXVzZSBpbXB1dGluZyB2YWx1ZXMgd2l0aCBvdGhlciB2YXJpYWJsZXMgaXMgbm90IGdvaW5nIHRvIGJlIGFjY3VyYXRlLg0KDQoqIE5lZ2xlY3RlZCBpbXB1dGF0aW9uIG9mIG90aGVyIGNvbHVtbiB3aGljaCBhcmUgbm90IGhhdmluZyBhIHN0YXRpc3RpY2FsIGltcGFjdCBhY2NvcmRpbmcgdG8gdGhlIHNjZW5hcmlvLg0KDQojIwlTY2FuIElJDQoNCmBgYHtyfQ0KDQojRmluZGluZyB0aGUgbG9jYXRpb25zIG9mIG91dGxpZXJzIGluIHRoZSBQT09SXzMyMGNfREFZIHZhcmlhYmxlDQpQT09SXzMyMGNfREFZX291dGxpZXJzIDwtIFBPVl9TVEFUU19JTVBVVEVfRklOQUwkUE9PUl8zMjBjX0RBWSU+JSBzY29yZXModHlwZSA9ICJ6IikNCg0Kd2hpY2goIGFicyhQT09SXzMyMGNfREFZX291dGxpZXJzKSA+MyApDQoNCmxlbmd0aCAod2hpY2goIGFicyhQT09SXzMyMGNfREFZX291dGxpZXJzKSA+MyApKQ0KDQojRmluZGluZyB0aGUgbG9jYXRpb25zIG9mIG91dGxpZXJzIGluIHRoZSBQT09SXzE5OWNfREFZIHZhcmlhYmxlDQpQT09SXzE5OWNfREFZX291dGxpZXJzIDwtIFBPVl9TVEFUU19JTVBVVEVfRklOQUwkUE9PUl8xOTljX0RBWSU+JSBzY29yZXModHlwZSA9ICJ6IikNCg0Kd2hpY2goIGFicyhQT09SXzE5OWNfREFZX291dGxpZXJzKSA+MyApDQoNCmxlbmd0aCAod2hpY2goIGFicyhQT09SXzE5OWNfREFZX291dGxpZXJzKSA+MyApKQ0KDQojRmluZGluZyB0aGUgbG9jYXRpb25zIG9mIG91dGxpZXJzIGluIHRoZSBQT09SXzU1MGNfREFZIHZhcmlhYmxlDQpQT09SXzU1MGNfREFZX291dGxpZXJzIDwtIFBPVl9TVEFUU19JTVBVVEVfRklOQUwkUE9PUl81NTBjX0RBWSU+JSBzY29yZXModHlwZSA9ICJ6IikNCg0Kd2hpY2goIGFicyhQT09SXzU1MGNfREFZX291dGxpZXJzKSA+MyApDQoNCmxlbmd0aCAod2hpY2goIGFicyhQT09SXzU1MGNfREFZX291dGxpZXJzKSA+MyApKQ0KDQojRmluZGluZyB0aGUgbG9jYXRpb25zIG9mIG91dGxpZXJzIGluIHRoZSBQT1BfVE9UTCB2YXJpYWJsZQ0KUE9QX1RPVExfb3V0bGllcnMgPC0gUE9WX1NUQVRTX0lNUFVURV9GSU5BTCRQT1BfVE9UTCU+JSBzY29yZXModHlwZSA9ICJ6IikNCg0Kd2hpY2goIGFicyhQT1BfVE9UTF9vdXRsaWVycykgPjMgKQ0KDQpsZW5ndGggKHdoaWNoKCBhYnMoUE9QX1RPVExfb3V0bGllcnMpID4zICkpDQoNCiNGaW5kaW5nIHRoZSBsb2NhdGlvbnMgb2Ygb3V0bGllcnMgaW4gdGhlIFBPUF9BQk9WRV81NTBjX0RBWSAgdmFyaWFibGUNClBPUF9BQk9WRV81NTBjX0RBWV9vdXRsaWVycyA8LSBQT1ZfU1RBVFNfSU1QVVRFX0ZJTkFMJFBPUF9BQk9WRV81NTBjX0RBWSU+JSBzY29yZXModHlwZSA9ICJ6IikNCg0Kd2hpY2goIGFicyhQT1BfQUJPVkVfNTUwY19EQVlfb3V0bGllcnMpID4zICkNCg0KbGVuZ3RoICh3aGljaCggYWJzKFBPUF9BQk9WRV81NTBjX0RBWV9vdXRsaWVycykgPjMgKSkNCg0KDQojSW1wdXRpbmcgT3V0bGllcnMgb2YgbnVtZXJpYyB2YXJpYWJsZXMgd2l0aCBtZWFuIGFzc3VtaW5nIHRoYXQgb3V0bGllcnMgYXJlIGEgcmVzdWx0IG9mIGRhdGEgZW50cnkvcHJvY2Vzc2luZyBlcnJvci4NClBPVl9TVEFUU19JTVBVVEVfRklOQUwkUE9PUl8zMjBjX0RBWSBbd2hpY2goYWJzKFBPT1JfMzIwY19EQVlfb3V0bGllcnMpID4zICldIDwtIG1lYW4oUE9WX1NUQVRTX0lNUFVURV9GSU5BTCRQT09SXzMyMGNfREFZLCBuYS5ybSA9IFRSVUUpDQoNClBPT1JfMzIwY19EQVlfb3V0bGllcnNfaW1wdXRlZCA8LSBQT1ZfU1RBVFNfSU1QVVRFX0ZJTkFMJFBPT1JfMzIwY19EQVklPiUgc2NvcmVzKHR5cGUgPSAieiIpDQoNCmxlbmd0aCAod2hpY2goIGFicyhQT09SXzMyMGNfREFZX291dGxpZXJzX2ltcHV0ZWQpID4zICkpDQoNCiNJbXB1dGluZyBPdXRsaWVycyBvZiBudW1lcmljIHZhcmlhYmxlcyB3aXRoIG1lYW4gYXNzdW1pbmcgdGhhdCBvdXRsaWVycyBhcmUgYSByZXN1bHQgb2YgZGF0YSBlbnRyeS9wcm9jZXNzaW5nIGVycm9yLg0KUE9WX1NUQVRTX0lNUFVURV9GSU5BTCRQT09SXzE5OWNfREFZIFt3aGljaChhYnMoUE9PUl8xOTljX0RBWV9vdXRsaWVycykgPjMgKV0gPC0gbWVhbihQT1ZfU1RBVFNfSU1QVVRFX0ZJTkFMJFBPT1JfMTk5Y19EQVksIG5hLnJtID0gVFJVRSkNCg0KUE9PUl8xOTljX0RBWV9vdXRsaWVyc19pbXB1dGVkIDwtIFBPVl9TVEFUU19JTVBVVEVfRklOQUwkUE9PUl8xOTljX0RBWSU+JSBzY29yZXModHlwZSA9ICJ6IikNCg0KbGVuZ3RoICh3aGljaCggYWJzKFBPT1JfMTk5Y19EQVlfb3V0bGllcnNfaW1wdXRlZCkgPjMgKSkNCg0KI0ltcHV0aW5nIE91dGxpZXJzIG9mIG51bWVyaWMgdmFyaWFibGVzIHdpdGggbWVhbiBhc3N1bWluZyB0aGF0IG91dGxpZXJzIGFyZSBhIHJlc3VsdCBvZiBkYXRhIGVudHJ5L3Byb2Nlc3NpbmcgZXJyb3IuDQpQT1ZfU1RBVFNfSU1QVVRFX0ZJTkFMJFBPT1JfNTUwY19EQVkgW3doaWNoKGFicyhQT09SXzU1MGNfREFZX291dGxpZXJzKSA+MyApXSA8LSBtZWFuKFBPVl9TVEFUU19JTVBVVEVfRklOQUwkUE9PUl81NTBjX0RBWSwgbmEucm0gPSBUUlVFKQ0KDQpQT09SXzU1MGNfREFZX291dGxpZXJzX2ltcHV0ZWQgPC0gUE9WX1NUQVRTX0lNUFVURV9GSU5BTCRQT09SXzU1MGNfREFZJT4lIHNjb3Jlcyh0eXBlID0gInoiKQ0KDQpsZW5ndGggKHdoaWNoKCBhYnMoUE9PUl81NTBjX0RBWV9vdXRsaWVyc19pbXB1dGVkKSA+MyApKQ0KDQojSW1wdXRpbmcgT3V0bGllcnMgb2YgbnVtZXJpYyB2YXJpYWJsZXMgd2l0aCBtZWFuIGFzc3VtaW5nIHRoYXQgb3V0bGllcnMgYXJlIGEgcmVzdWx0IG9mIGRhdGEgZW50cnkvcHJvY2Vzc2luZyBlcnJvci4NClBPVl9TVEFUU19JTVBVVEVfRklOQUwkUE9QX1RPVEwgW3doaWNoKGFicyhQT1BfVE9UTF9vdXRsaWVycykgPjMgKV0gPC0gbWVhbihQT1ZfU1RBVFNfSU1QVVRFX0ZJTkFMJFBPUF9UT1RMLCBuYS5ybSA9IFRSVUUpDQoNClBPUF9UT1RMX291dGxpZXJzX2ltcHV0ZWQgPC0gUE9WX1NUQVRTX0lNUFVURV9GSU5BTCRQT1BfVE9UTCU+JSBzY29yZXModHlwZSA9ICJ6IikNCg0KbGVuZ3RoICh3aGljaCggYWJzKFBPUF9UT1RMX291dGxpZXJzX2ltcHV0ZWQpID4zICkpDQoNCiNJbXB1dGluZyBPdXRsaWVycyBvZiBudW1lcmljIHZhcmlhYmxlcyB3aXRoIG1lYW4gYXNzdW1pbmcgdGhhdCBvdXRsaWVycyBhcmUgYSByZXN1bHQgb2YgZGF0YSBlbnRyeS9wcm9jZXNzaW5nIGVycm9yLg0KUE9WX1NUQVRTX0lNUFVURV9GSU5BTCRQT1BfQUJPVkVfNTUwY19EQVkgW3doaWNoKGFicyhQT1BfQUJPVkVfNTUwY19EQVlfb3V0bGllcnMpID4zICldIDwtIG1lYW4oUE9WX1NUQVRTX0lNUFVURV9GSU5BTCRQT1BfQUJPVkVfNTUwY19EQVksIG5hLnJtID0gVFJVRSkNCg0KUE9QX0FCT1ZFXzU1MGNfREFZX291dGxpZXJzX2ltcHV0ZWQgPC0gUE9WX1NUQVRTX0lNUFVURV9GSU5BTCRQT1BfQUJPVkVfNTUwY19EQVkgJT4lIHNjb3Jlcyh0eXBlID0gInoiKQ0KDQpsZW5ndGggKHdoaWNoKCBhYnMoUE9QX0FCT1ZFXzU1MGNfREFZX291dGxpZXJzX2ltcHV0ZWQpID4zICkpDQoNCg0KDQoNCg0KDQoNCg0KDQpgYGANCiogRm91bmQgdGhlIG91dGxpZXJzIG9mIGFsbCB0aGUgbnVtZXJpYyB2YXJpYWJsZXMgdXNpbmcgei1zY29yZXMgbWV0aG9kIGV4Y2VwdCAneWVhcicgY29sdW1uIHdoaWNoIGlzIG5vdCByZXF1cmlyZWQgICB0byBiZSBjb25zaWRlcmVkIGZvciBzZWFyY2hpbmcgb3V0bGllcnMuDQoNCiogVGhlcmUgd2VyZSBzb21lIG91bGllcnMgd2hpY2ggbWlnaHQgaGFkIGJlZW4gZHVlIHRvIGRhdGEgZW50ZXJpbmcuDQoNCiogaW1wdXRhdGlvbiB3YXMgdXNlZCB0byByZW1vdmUgdGhlIG91dGxpZXJzIG9mIGFsbCB0aGUgbnVtZXJpYyB2YXRpYWJsZXMgYnkgcmVzcGVjdGl2ZSBtZWFuIHZhbHVlcy4NCg0KKiBIb3dldmVyLCBmb3Igc29tZSByZWFzb24gb3V0bGllciBjb3VudCBnb3QgaW5jcmVhc2VkIGFmdGVyIHRoZSBpbXB1dGF0aW9uLiBJdCBtaWdodCBiZSBkdWUgdG8gdGhlIGZhY3QgdGhhdCBkYXRhICAgIGVudHJ5IGVycm9yIG1pZ2h0IGNvbnRhaW4gZmFyIGxhcmdlciB2YWx1ZXMgdGhhbiBleGlzdGluZyB2YWx1ZXMuDQoNCg0KIyMJVHJhbnNmb3JtIA0KDQoNCmBgYHtyfQ0KI0Rpc3BsYXlpbmcgaGlzdG9ncmFtIG9mICd0b3RhbCBwb3B1bGF0aW9uJyBjb3VsdW1uDQpoaXN0KFBPVl9TVEFUU19JTVBVVEVfRklOQUwkUE9QX1RPVEwsIG1haW4gPSAiSGlzdG9ncmFtIG9mIFBPT1JfMzIwY19EQVkiKQ0KDQojQXBwbGljYXRpb24gb2YgbG9nIHRyYW5zZm9ybWF0aW9uIGZvciAndG90YWwgcG9wdWxhdGlvbicgY291bHVtbg0KbG9nX3RvdGFsX3BvcHVsYXRpb24gPC0gbG9nMTAoUE9WX1NUQVRTX0lNUFVURV9GSU5BTCRQT1BfVE9UTCkNCg0KaGlzdChsb2dfdG90YWxfcG9wdWxhdGlvbikNCg0KDQoNCg0KDQpgYGANCg0KKiBIaXN0b2dyYW0gb2YgdGhlIHNlbGVjdGVkIHZhcmlhYmxlICdQT1BfVE9UTCcgd2FzIGRpc3BsYXllZCB0byB2aWV3IHRoZSBkaXN0cmlidXRpb24uDQoNCiogSXQgd2FzIG1vcmUgb2YgYSByaWdodCBza2V3ZWQgZGlzdHJpYnV0aW9uIGFuZCBsb2cgdHJhbnNmb3JtYXRpb24gd2FzIHVzZWQgdG8gcmVtb3ZlIHRoZSByaWdodCBza2V3bmVzcyBzaW5jZSBsb2cgdHJuYXNmb3JtYXRpb24gaXMgaWRlYWwgZm9yIHJlbW92aW5nIHJpZ2h0IHNrZXduZXNzLg0KDQoNCg0KDQoNCjxicj4NCjxicj4NCg==