Required packages
Below packages are installed to enhance and add features to the existing base R functionalities
library(readr)
library(dplyr)
library(tidyr)
library(Hmisc)
library(outliers)
library(forecast)
Executive Summary
Aim:
The aim of the below preprocessing steps is to create a clean, understandable dataset from the raw data available.
Dataset:
Two datasets, one containing each country’s economic status and the other containing the details of their population’s employment ratio were taken, analyzed, cleaned and preprocessed.
Procedure:
- The two raw datasets were subsetted, renamed and joined together to form a single dataset with only the relevant information needed for analysis
- Depending on the types of variables, the necessary type conversions were applied
- The dataset was then tidied into long format and type conversions applied wherever necessary
- The tidied dataset was then checked for missing values and necessary techniques were applied to handle them
- A new variable was computed from the existing values to calculate the growth of each country in 4 years. This step involved converting the long format to wide format(for ease of calculation), calculating the growth and converting back to long format, with necessary data type conversions along the way
- The numeric variables were then checked for outliers and analysed
- The distribution of data was visualized and necessary transformations applied to render a normlly distributed data
Data
The dataset contains the details of Employment to population ratio of each country. The ages considered for this analysis is in the range of 15-24 years, which is generally considered as the youth population. The second dataset that is considered for the analysis is the metadata that contains all the information of each country, such as Income group, currency, latest population, industrial and trade data, accounting concept, etc. These datasets has been taken from World Banks’s open source data(https://data.worldbank.org).
#Importing dataset
rawDataset <- read_csv("Employment_stats.csv")
metadata <- read_csv("Country_Metadata.csv")
head(rawDataset)
head(metadata)
For the purpose of preparing the data for analysis, the two datasets are merged as one with only the required variables. This is done step by step as below:
Since the metadata contains all the information about each country, we are going to select only the variables we need(Country code/Code and Income Group) and store it in ‘IncomeGroup’ data frame
Since the country code is the variable we are to use for joining the metadata with the rawDataset, we need to ensure that te variable names match. We thus rename the variables of the ‘IncomeGroup’ dataset
#Selecting only the variables needed
IncomeGroup <- metadata %>% select(Code, `Income Group`)
#Renaming 'Code' as 'Country_Code' for merging
IncomeGroup <- rename(IncomeGroup,Country_Code=Code,Income_group='Income Group')
head(IncomeGroup)
- The rawDataset contains some unwanted information (Series Code and Series Name) that are repetitive and insignificant for our analysis. So the rawDataset is subsetted with only the variables needed, and renamed appropriately for ease of understanding
#subsetting the dataset to remove the unwanted variables 'SeriesCode' and 'seriesName'
dataset_subset <- rawDataset %>% select(-c('Series Code','Series Name'))
dataset_subset <- rename(dataset_subset, Country_Name='Country Name', Country_Code='Country Code')
names(dataset_subset)[3] <- 2014
names(dataset_subset)[4] <- 2015
names(dataset_subset)[5] <- 2016
names(dataset_subset)[6] <- 2017
head(dataset_subset)
- The two datasets are now merged using left_join() function, by the common Country_Code variable
#Joining 'dataset_subset' and 'IncomeGroup'
dataset_join <- left_join(dataset_subset, IncomeGroup, by="Country_Code" )
head(dataset_join)
NA
The variables in the dataset after joining are:
colnames(dataset_join)
[1] "Country_Name" "Country_Code" "2014" "2015"
[5] "2016" "2017" "Income_group"
Country_Name: Names of all countries, arranged alphabetically
Country_Code: A unique code for each country, used for merging the two datasets
2014-2017: Employment to population ratio of each country through the years 2014 - 2017
Income_group: Economy status of each country
Understand
Using str() function, the structure of the joined dataset(dataset_join) can be seen as:
str(dataset_join)
Classes ‘spec_tbl_df’, ‘tbl_df’, ‘tbl’ and 'data.frame': 268 obs. of 7 variables:
$ Country_Name: chr "Afghanistan" "Albania" "Algeria" "American Samoa" ...
$ Country_Code: chr "AFG" "ALB" "DZA" "ASM" ...
$ 2014 : num NA 17.7 19.4 NA NA ...
$ 2015 : num NA 18.9 17.7 NA NA ...
$ 2016 : num NA NA 18.3 NA NA ...
$ 2017 : num 32.4 21.6 19.5 NA NA ...
$ Income_group: chr "Low income" "Upper middle income" "Upper middle income" "Upper middle income" ...
From the structure, it can be seen that the data types of ‘Country_Name’, ‘Country_Code’ and ‘Income_group’ is character and the rest are numeric. But since the ‘Income_Group’ variable contains categorical data, it is converted to a factored variable using the factor() function and levels defined.
#Converting data type of income group from chr to factor
dataset_join$Income_group <- factor(dataset_join$Income_group, levels = c("Low income", "Lower middle income", "Upper middle income", "High income"), ordered = TRUE)
levels(dataset_join$Income_group)
[1] "Low income" "Lower middle income" "Upper middle income"
[4] "High income"
str(dataset_join)
Classes ‘spec_tbl_df’, ‘tbl_df’, ‘tbl’ and 'data.frame': 268 obs. of 7 variables:
$ Country_Name: chr "Afghanistan" "Albania" "Algeria" "American Samoa" ...
$ Country_Code: chr "AFG" "ALB" "DZA" "ASM" ...
$ 2014 : num NA 17.7 19.4 NA NA ...
$ 2015 : num NA 18.9 17.7 NA NA ...
$ 2016 : num NA NA 18.3 NA NA ...
$ 2017 : num 32.4 21.6 19.5 NA NA ...
$ Income_group: Ord.factor w/ 4 levels "Low income"<"Lower middle income"<..: 1 3 3 3 4 2 4 NA 3 3 ...
Tidy & Manipulate Data I
In the ‘dataset_join’ data frame, the column names 2014-2017 are values of year and not variable names. This calls for a transformation of the wide format to long format. gather() function is thus used to create the ‘year’ and ‘percentage’ variables. The arguments ‘key’ and ‘value’ correspond to the name of the variable whose values form the column names and the name of the variable whose values are spread over the cells.
Since the ‘Year’ variable is of character data type, it is converted into an integer using the as.integer() function.
#wide to long format - tidying data
dataset_join <- dataset_join %>% gather(key = "year", value= "percentage", 3:6 )
#converting data type of 'year' variable from chr to int
dataset_join$year <- as.integer(dataset_join$year)
head(dataset_join)
NA
Scan I
With the tidied dataset in long format, it is now checked for missing values in all variables. colSums() gives the total number of values missing in each variable.
#checking NA values for all variables
colSums(is.na(dataset_join))
Country_Name Country_Code Income_group year percentage
20 20 204 0 574
From above, it can be seen that there are 20 observartions with missing ‘Country_Name’ and ‘Country_Code’. Since the data relevent to our analysis becomes invalid without the ‘Country_Name’ or ‘Country_Code’, it needs to be excluded. But before proceeding, to make sure excluding these missing values do not create an impact on the dataset, the % of missing values is calculated for ‘Country_Name’ and ‘Country_Code’ variables. The total number of values in each of the variables can be obtained with length() function as:
#finding length to calculate the % of missing values in country
length(dataset_join$Country_Name)
[1] 1072
length(dataset_join$Country_Code)
[1] 1072
% of missing values = (No. of missing values / Total number of values) * 100 = (20/1072) * 100 = 1.86%
Since the percentage of missing values in country name and code is less than 5%, we can now omit the observations with missing values.
dataset_join <- dataset_join[-which(is.na(dataset_join$Country_Name)),]
colSums(is.na(dataset_join))
Country_Name Country_Code Income_group year percentage
0 0 184 0 554
From above, it can be seen that the number of missing values is now 0 for the variables ‘Country_Name’ and ‘Country_Code’.
The variable ‘Income_group’ has 184 missing values and is a categorical variable. ‘getmode’ is a custom function to find the most common categorical value to be replaced in place of missing values.
#function to find the most common category
getmode <- function(v) {
uniqv <- unique(v)
uniqv[which.max(tabulate(match(v, uniqv)))]
}
income_mode <- getmode(dataset_join$Income_group)
#Replacing the missing values with the most common category
dataset_join$Income_group[which(is.na(dataset_join$Income_group))] <- income_mode
colSums(is.na(dataset_join))
Country_Name Country_Code Income_group year percentage
0 0 0 0 554
head(dataset_join)
Now, the ‘Percentage’ variable has 554 missing values. Since the dataset is in long format, for each country code, we have 4 values, corresponding to the years 2014, 2015, 2016 and 2017. In order to impute the missing percentage values with their means, we group the dataset by country code and get the mean for each country and impute them in the missing values.
dataset_impute <- dataset_join %>% group_by(Country_Code) %>% mutate(percentage = ifelse(is.na(percentage), mean(percentage, na.rm = TRUE), percentage))
colSums(is.na(dataset_impute))
Country_Name Country_Code Income_group year percentage
0 0 0 0 392
There are now 392 missing values in the percentage variable, which are actually special values ‘NaN’, caused because of unavailble means.
sum(is.nan(dataset_impute$percentage))
[1] 392
This means that these countries don’t have any percentage values recorded through the years 2014-2017. Hence there is no mean value to impute the missing values. Since these values are unavailable in the dataset, we omit them.
dataset_impute2 <- na.omit(dataset_impute)
colSums(is.na(dataset_impute2))
Country_Name Country_Code Income_group year percentage
0 0 0 0 0
Tidy & Manipulate Data II
To find the growth in employment to population ratio between the years 2014 and 2017, we convert the dataset from long format to wide format for ease of calculation and compute the growth(values of 2017 - 2014).
#converting the year variable into wide format in order to calculate growth
wideFormat <- spread(dataset_impute2, key = year, value = percentage)
wideFormat <- wideFormat %>% mutate(growth=(`2017`-`2014`))
head(wideFormat)
Upon calculating the growth, the dataset is tidied back up into ‘longFormat’ and year variable’s datatype is converted from character to integer.
#Converting back to long format
longFormat <- wideFormat %>% gather(key = "year", value= "percentage", 4:7 )
longFormat$year <- as.integer(longFormat$year)
head(longFormat)
NA
Scan II
From above, it can be seen that ‘growth’, ‘year’ and ‘percentage’ are the only numeric variables that needs to be scanned for outliers. Since the variable ‘year’ was created as part of the tidy step, it only has four values, 2014-2017. It therefore doesn’t require any checks for outliers. Proceeding to check for outliers in the variables ‘growth’ and ‘year’ using the z-score method from outliers package.
#checking for outliers in 'growth' variable
z.scoreGrowth <- longFormat$growth %>% scores(type = "z")
length(which( abs(z.scoreGrowth) >3 ))
[1] 4
#checking for outliers in 'percentage' variable
z.scorePercentage <- longFormat$percentage %>% scores(type = "z")
length(which( abs(z.scorePercentage) >3 ))
[1] 1
It can be seen that there are 4 outliers in ‘growth’ variable and 1 outlier in ‘percentage’ variable. These outlier values belong to a country’s growth and percentage of employment to population ratio respectively. Since these are observations of a country’s performance, they cannot be omitted, deleted, imputed or capped and are treated as valid data and not outliers. In order to reduce the variation caused by these values in the ‘percentage’ variable, we apply the below explained transformations.
LS0tDQp0aXRsZTogIk1BVEgyMzQ5IFNlbWVzdGVyIDIsIDIwMTkiDQphdXRob3I6ICJTeWVkYSBIYWZzYSBNdWplZWIgKHMzNzk2Njk3KSwgDQogICAgICAgICBWYWlzaG5hYXZlZSBSYWphcmFtYW4gKHMzODEzNTQ0KSwgDQogICAgICAgICBWaWRhdmFsdXJ1IFNhaSBIYW5lZXNoYSAoczM3OTkzNjUpIg0Kc3VidGl0bGU6IEFzc2lnbm1lbnQgMw0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOiBkZWZhdWx0DQotLS0NCg0KIyMgUmVxdWlyZWQgcGFja2FnZXMgDQoNCkJlbG93IHBhY2thZ2VzIGFyZSBpbnN0YWxsZWQgdG8gZW5oYW5jZSBhbmQgYWRkIGZlYXR1cmVzIHRvIHRoZSBleGlzdGluZyBiYXNlIFIgZnVuY3Rpb25hbGl0aWVzDQoNCmBgYHtyfQ0KbGlicmFyeShyZWFkcikNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KHRpZHlyKQ0KbGlicmFyeShIbWlzYykNCmxpYnJhcnkob3V0bGllcnMpDQpsaWJyYXJ5KGZvcmVjYXN0KQ0KYGBgDQoNCg0KIyMgRXhlY3V0aXZlIFN1bW1hcnkgDQoNCiMjIyMgQWltOg0KVGhlIGFpbSBvZiB0aGUgYmVsb3cgcHJlcHJvY2Vzc2luZyBzdGVwcyBpcyB0byBjcmVhdGUgYSBjbGVhbiwgdW5kZXJzdGFuZGFibGUgZGF0YXNldCBmcm9tIHRoZSByYXcgZGF0YSBhdmFpbGFibGUuDQoNCiMjIyMgRGF0YXNldDoNClR3byBkYXRhc2V0cywgb25lIGNvbnRhaW5pbmcgZWFjaCBjb3VudHJ5J3MgZWNvbm9taWMgc3RhdHVzIGFuZCB0aGUgb3RoZXIgY29udGFpbmluZyB0aGUgZGV0YWlscyBvZiB0aGVpciBwb3B1bGF0aW9uJ3MgZW1wbG95bWVudCByYXRpbyB3ZXJlIHRha2VuLCBhbmFseXplZCwgY2xlYW5lZCBhbmQgcHJlcHJvY2Vzc2VkLiANCg0KIyMjIyBQcm9jZWR1cmU6DQoqIFRoZSB0d28gcmF3IGRhdGFzZXRzIHdlcmUgc3Vic2V0dGVkLCByZW5hbWVkIGFuZCBqb2luZWQgdG9nZXRoZXIgdG8gZm9ybSBhIHNpbmdsZSBkYXRhc2V0IHdpdGggb25seSB0aGUgcmVsZXZhbnQgaW5mb3JtYXRpb24gbmVlZGVkIGZvciBhbmFseXNpcw0KKiBEZXBlbmRpbmcgb24gdGhlIHR5cGVzIG9mIHZhcmlhYmxlcywgdGhlIG5lY2Vzc2FyeSB0eXBlIGNvbnZlcnNpb25zIHdlcmUgYXBwbGllZA0KKiBUaGUgZGF0YXNldCB3YXMgdGhlbiB0aWRpZWQgaW50byBsb25nIGZvcm1hdCBhbmQgdHlwZSBjb252ZXJzaW9ucyBhcHBsaWVkIHdoZXJldmVyIG5lY2Vzc2FyeQ0KKiBUaGUgdGlkaWVkIGRhdGFzZXQgd2FzIHRoZW4gY2hlY2tlZCBmb3IgbWlzc2luZyB2YWx1ZXMgYW5kIG5lY2Vzc2FyeSB0ZWNobmlxdWVzIHdlcmUgYXBwbGllZCB0byBoYW5kbGUgdGhlbQ0KKiBBIG5ldyB2YXJpYWJsZSB3YXMgY29tcHV0ZWQgZnJvbSB0aGUgZXhpc3RpbmcgdmFsdWVzIHRvIGNhbGN1bGF0ZSB0aGUgZ3Jvd3RoIG9mIGVhY2ggY291bnRyeSBpbiA0IHllYXJzLiBUaGlzIHN0ZXAgaW52b2x2ZWQgY29udmVydGluZyB0aGUgbG9uZyBmb3JtYXQgdG8gd2lkZSBmb3JtYXQoZm9yIGVhc2Ugb2YgY2FsY3VsYXRpb24pLCBjYWxjdWxhdGluZyB0aGUgZ3Jvd3RoIGFuZCBjb252ZXJ0aW5nIGJhY2sgdG8gbG9uZyBmb3JtYXQsIHdpdGggbmVjZXNzYXJ5IGRhdGEgdHlwZSBjb252ZXJzaW9ucyBhbG9uZyB0aGUgd2F5DQoqIFRoZSBudW1lcmljIHZhcmlhYmxlcyB3ZXJlIHRoZW4gY2hlY2tlZCBmb3Igb3V0bGllcnMgYW5kIGFuYWx5c2VkDQoqIFRoZSBkaXN0cmlidXRpb24gb2YgZGF0YSB3YXMgdmlzdWFsaXplZCBhbmQgbmVjZXNzYXJ5IHRyYW5zZm9ybWF0aW9ucyBhcHBsaWVkIHRvIHJlbmRlciBhIG5vcm1sbHkgZGlzdHJpYnV0ZWQgZGF0YQ0KDQoNCiMjIERhdGENCg0KVGhlIGRhdGFzZXQgY29udGFpbnMgdGhlIGRldGFpbHMgb2YgRW1wbG95bWVudCB0byBwb3B1bGF0aW9uIHJhdGlvIG9mIGVhY2ggY291bnRyeS4gVGhlIGFnZXMgY29uc2lkZXJlZCBmb3IgdGhpcyBhbmFseXNpcyBpcyBpbiB0aGUgcmFuZ2Ugb2YgMTUtMjQgeWVhcnMsIHdoaWNoIGlzIGdlbmVyYWxseSBjb25zaWRlcmVkIGFzIHRoZSB5b3V0aCBwb3B1bGF0aW9uLiBUaGUgc2Vjb25kIGRhdGFzZXQgdGhhdCBpcyBjb25zaWRlcmVkIGZvciB0aGUgYW5hbHlzaXMgaXMgdGhlIG1ldGFkYXRhIHRoYXQgY29udGFpbnMgYWxsIHRoZSBpbmZvcm1hdGlvbiBvZiBlYWNoIGNvdW50cnksIHN1Y2ggYXMgSW5jb21lIGdyb3VwLCBjdXJyZW5jeSwgbGF0ZXN0IHBvcHVsYXRpb24sIGluZHVzdHJpYWwgYW5kIHRyYWRlIGRhdGEsIGFjY291bnRpbmcgY29uY2VwdCwgZXRjLiBUaGVzZSBkYXRhc2V0cyBoYXMgYmVlbiB0YWtlbiBmcm9tIFdvcmxkIEJhbmtzJ3Mgb3BlbiBzb3VyY2UgZGF0YShodHRwczovL2RhdGEud29ybGRiYW5rLm9yZykuDQoNCmBgYHtyfQ0KI0ltcG9ydGluZyBkYXRhc2V0DQpyYXdEYXRhc2V0IDwtIHJlYWRfY3N2KCJFbXBsb3ltZW50X3N0YXRzLmNzdiIpDQptZXRhZGF0YSA8LSByZWFkX2NzdigiQ291bnRyeV9NZXRhZGF0YS5jc3YiKQ0KYGBgDQpgYGB7cn0NCmhlYWQocmF3RGF0YXNldCkNCmhlYWQobWV0YWRhdGEpDQpgYGANCg0KRm9yIHRoZSBwdXJwb3NlIG9mIHByZXBhcmluZyB0aGUgZGF0YSBmb3IgYW5hbHlzaXMsIHRoZSB0d28gZGF0YXNldHMgYXJlIG1lcmdlZCBhcyBvbmUgd2l0aCBvbmx5IHRoZSByZXF1aXJlZCB2YXJpYWJsZXMuIFRoaXMgaXMgZG9uZSBzdGVwIGJ5IHN0ZXAgYXMgYmVsb3c6DQoNCjEuIFNpbmNlIHRoZSBtZXRhZGF0YSBjb250YWlucyBhbGwgdGhlIGluZm9ybWF0aW9uIGFib3V0IGVhY2ggY291bnRyeSwgd2UgYXJlIGdvaW5nIHRvIHNlbGVjdCBvbmx5IHRoZSB2YXJpYWJsZXMgd2UgbmVlZChDb3VudHJ5IGNvZGUvQ29kZSBhbmQgSW5jb21lIEdyb3VwKSBhbmQgc3RvcmUgaXQgaW4gJ0luY29tZUdyb3VwJyBkYXRhIGZyYW1lDQoNCjIuIFNpbmNlIHRoZSBjb3VudHJ5IGNvZGUgaXMgdGhlIHZhcmlhYmxlIHdlIGFyZSB0byB1c2UgZm9yIGpvaW5pbmcgdGhlIG1ldGFkYXRhIHdpdGggdGhlIHJhd0RhdGFzZXQsIHdlIG5lZWQgdG8gZW5zdXJlIHRoYXQgdGUgdmFyaWFibGUgbmFtZXMgbWF0Y2guIFdlIHRodXMgcmVuYW1lIHRoZSB2YXJpYWJsZXMgb2YgdGhlICdJbmNvbWVHcm91cCcgZGF0YXNldA0KDQpgYGB7cn0NCiNTZWxlY3Rpbmcgb25seSB0aGUgdmFyaWFibGVzIG5lZWRlZA0KSW5jb21lR3JvdXAgPC0gbWV0YWRhdGEgJT4lIHNlbGVjdChDb2RlLCBgSW5jb21lIEdyb3VwYCkNCg0KI1JlbmFtaW5nICdDb2RlJyBhcyAnQ291bnRyeV9Db2RlJyBmb3IgbWVyZ2luZw0KSW5jb21lR3JvdXAgPC0gcmVuYW1lKEluY29tZUdyb3VwLENvdW50cnlfQ29kZT1Db2RlLEluY29tZV9ncm91cD0nSW5jb21lIEdyb3VwJykNCg0KaGVhZChJbmNvbWVHcm91cCkNCmBgYA0KDQozLiBUaGUgcmF3RGF0YXNldCBjb250YWlucyBzb21lIHVud2FudGVkIGluZm9ybWF0aW9uIChTZXJpZXMgQ29kZSBhbmQgU2VyaWVzIE5hbWUpIHRoYXQgYXJlIHJlcGV0aXRpdmUgYW5kIGluc2lnbmlmaWNhbnQgZm9yIG91ciBhbmFseXNpcy4gU28gdGhlIHJhd0RhdGFzZXQgaXMgc3Vic2V0dGVkIHdpdGggb25seSB0aGUgdmFyaWFibGVzIG5lZWRlZCwgYW5kIHJlbmFtZWQgYXBwcm9wcmlhdGVseSBmb3IgZWFzZSBvZiB1bmRlcnN0YW5kaW5nDQpgYGB7cn0NCiNzdWJzZXR0aW5nIHRoZSBkYXRhc2V0IHRvIHJlbW92ZSB0aGUgdW53YW50ZWQgdmFyaWFibGVzICdTZXJpZXNDb2RlJyBhbmQgJ3Nlcmllc05hbWUnDQpkYXRhc2V0X3N1YnNldCA8LSByYXdEYXRhc2V0ICU+JSBzZWxlY3QoLWMoJ1NlcmllcyBDb2RlJywnU2VyaWVzIE5hbWUnKSkNCg0KZGF0YXNldF9zdWJzZXQgPC0gcmVuYW1lKGRhdGFzZXRfc3Vic2V0LCBDb3VudHJ5X05hbWU9J0NvdW50cnkgTmFtZScsIENvdW50cnlfQ29kZT0nQ291bnRyeSBDb2RlJykNCm5hbWVzKGRhdGFzZXRfc3Vic2V0KVszXSA8LSAyMDE0DQpuYW1lcyhkYXRhc2V0X3N1YnNldClbNF0gPC0gMjAxNQ0KbmFtZXMoZGF0YXNldF9zdWJzZXQpWzVdIDwtIDIwMTYNCm5hbWVzKGRhdGFzZXRfc3Vic2V0KVs2XSA8LSAyMDE3DQpoZWFkKGRhdGFzZXRfc3Vic2V0KQ0KYGBgDQoNCjQuIFRoZSB0d28gZGF0YXNldHMgYXJlIG5vdyBtZXJnZWQgdXNpbmcgbGVmdF9qb2luKCkgZnVuY3Rpb24sIGJ5IHRoZSBjb21tb24gQ291bnRyeV9Db2RlIHZhcmlhYmxlDQpgYGB7cn0NCiNKb2luaW5nICdkYXRhc2V0X3N1YnNldCcgYW5kICdJbmNvbWVHcm91cCcNCmRhdGFzZXRfam9pbiA8LSBsZWZ0X2pvaW4oZGF0YXNldF9zdWJzZXQsIEluY29tZUdyb3VwLCBieT0iQ291bnRyeV9Db2RlIiApDQpoZWFkKGRhdGFzZXRfam9pbikNCg0KYGBgDQoNClRoZSB2YXJpYWJsZXMgaW4gdGhlIGRhdGFzZXQgYWZ0ZXIgam9pbmluZyBhcmU6DQoNCmBgYHtyfQ0KY29sbmFtZXMoZGF0YXNldF9qb2luKQ0KYGBgDQpDb3VudHJ5X05hbWU6IE5hbWVzIG9mIGFsbCBjb3VudHJpZXMsIGFycmFuZ2VkIGFscGhhYmV0aWNhbGx5DQoNCkNvdW50cnlfQ29kZTogQSB1bmlxdWUgY29kZSBmb3IgZWFjaCBjb3VudHJ5LCB1c2VkIGZvciBtZXJnaW5nIHRoZSB0d28gZGF0YXNldHMNCg0KMjAxNC0yMDE3OiBFbXBsb3ltZW50IHRvIHBvcHVsYXRpb24gcmF0aW8gb2YgZWFjaCBjb3VudHJ5IHRocm91Z2ggdGhlIHllYXJzIDIwMTQgLSAyMDE3DQoNCkluY29tZV9ncm91cDogRWNvbm9teSBzdGF0dXMgb2YgZWFjaCBjb3VudHJ5DQoNCg0KIyMgVW5kZXJzdGFuZCANCg0KVXNpbmcgc3RyKCkgZnVuY3Rpb24sIHRoZSBzdHJ1Y3R1cmUgb2YgdGhlIGpvaW5lZCBkYXRhc2V0KGRhdGFzZXRfam9pbikgY2FuIGJlIHNlZW4gYXM6DQpgYGB7cn0NCnN0cihkYXRhc2V0X2pvaW4pDQpgYGANCg0KRnJvbSB0aGUgc3RydWN0dXJlLCBpdCBjYW4gYmUgc2VlbiB0aGF0IHRoZSBkYXRhIHR5cGVzIG9mICdDb3VudHJ5X05hbWUnLCAnQ291bnRyeV9Db2RlJyBhbmQgJ0luY29tZV9ncm91cCcgaXMgY2hhcmFjdGVyIGFuZCB0aGUgcmVzdCBhcmUgbnVtZXJpYy4gQnV0IHNpbmNlIHRoZSAnSW5jb21lX0dyb3VwJyB2YXJpYWJsZSBjb250YWlucyBjYXRlZ29yaWNhbCBkYXRhLCBpdCBpcyBjb252ZXJ0ZWQgdG8gYSBmYWN0b3JlZCB2YXJpYWJsZSB1c2luZyB0aGUgZmFjdG9yKCkgZnVuY3Rpb24gYW5kIGxldmVscyBkZWZpbmVkLg0KDQpgYGB7cn0NCiNDb252ZXJ0aW5nIGRhdGEgdHlwZSBvZiBpbmNvbWUgZ3JvdXAgZnJvbSBjaHIgdG8gZmFjdG9yDQpkYXRhc2V0X2pvaW4kSW5jb21lX2dyb3VwIDwtIGZhY3RvcihkYXRhc2V0X2pvaW4kSW5jb21lX2dyb3VwLCBsZXZlbHMgPSBjKCJMb3cgaW5jb21lIiwgIkxvd2VyIG1pZGRsZSBpbmNvbWUiLCAiVXBwZXIgbWlkZGxlIGluY29tZSIsICJIaWdoIGluY29tZSIpLCBvcmRlcmVkID0gVFJVRSkNCg0KbGV2ZWxzKGRhdGFzZXRfam9pbiRJbmNvbWVfZ3JvdXApDQpzdHIoZGF0YXNldF9qb2luKQ0KYGBgDQoNCg0KIyMJVGlkeSAmIE1hbmlwdWxhdGUgRGF0YSBJIA0KDQpJbiB0aGUgJ2RhdGFzZXRfam9pbicgZGF0YSBmcmFtZSwgdGhlIGNvbHVtbiBuYW1lcyAyMDE0LTIwMTcgYXJlIHZhbHVlcyBvZiB5ZWFyIGFuZCBub3QgdmFyaWFibGUgbmFtZXMuIFRoaXMgY2FsbHMgZm9yIGEgdHJhbnNmb3JtYXRpb24gb2YgdGhlIHdpZGUgZm9ybWF0IHRvIGxvbmcgZm9ybWF0LiBnYXRoZXIoKSBmdW5jdGlvbiBpcyB0aHVzIHVzZWQgdG8gY3JlYXRlIHRoZSAneWVhcicgYW5kICdwZXJjZW50YWdlJyB2YXJpYWJsZXMuIFRoZSBhcmd1bWVudHMgJ2tleScgYW5kICd2YWx1ZScgY29ycmVzcG9uZCB0byB0aGUgbmFtZSBvZiB0aGUgdmFyaWFibGUgd2hvc2UgdmFsdWVzIGZvcm0gdGhlIGNvbHVtbiBuYW1lcyBhbmQgdGhlIG5hbWUgb2YgdGhlIHZhcmlhYmxlIHdob3NlIHZhbHVlcyBhcmUgc3ByZWFkIG92ZXIgdGhlIGNlbGxzLg0KDQpTaW5jZSB0aGUgJ1llYXInIHZhcmlhYmxlIGlzIG9mIGNoYXJhY3RlciBkYXRhIHR5cGUsIGl0IGlzIGNvbnZlcnRlZCBpbnRvIGFuIGludGVnZXIgdXNpbmcgdGhlIGFzLmludGVnZXIoKSBmdW5jdGlvbi4NCg0KYGBge3J9DQojd2lkZSB0byBsb25nIGZvcm1hdCAtIHRpZHlpbmcgZGF0YQ0KZGF0YXNldF9qb2luIDwtIGRhdGFzZXRfam9pbiAlPiUgZ2F0aGVyKGtleSA9ICJ5ZWFyIiwgdmFsdWU9ICJwZXJjZW50YWdlIiwgMzo2ICkNCg0KI2NvbnZlcnRpbmcgZGF0YSB0eXBlIG9mICd5ZWFyJyB2YXJpYWJsZSBmcm9tIGNociB0byBpbnQNCmRhdGFzZXRfam9pbiR5ZWFyIDwtIGFzLmludGVnZXIoZGF0YXNldF9qb2luJHllYXIpDQpoZWFkKGRhdGFzZXRfam9pbikNCg0KYGBgDQoNCg0KIyMJU2NhbiBJIA0KDQpXaXRoIHRoZSB0aWRpZWQgZGF0YXNldCBpbiBsb25nIGZvcm1hdCwgaXQgaXMgbm93IGNoZWNrZWQgZm9yIG1pc3NpbmcgdmFsdWVzIGluIGFsbCB2YXJpYWJsZXMuIGNvbFN1bXMoKSBnaXZlcyB0aGUgdG90YWwgbnVtYmVyIG9mIHZhbHVlcyBtaXNzaW5nIGluIGVhY2ggdmFyaWFibGUuDQoNCmBgYHtyfQ0KI2NoZWNraW5nIE5BIHZhbHVlcyBmb3IgYWxsIHZhcmlhYmxlcw0KY29sU3Vtcyhpcy5uYShkYXRhc2V0X2pvaW4pKQ0KYGBgDQoNCkZyb20gYWJvdmUsIGl0IGNhbiBiZSBzZWVuIHRoYXQgdGhlcmUgYXJlIDIwIG9ic2VydmFydGlvbnMgd2l0aCBtaXNzaW5nICdDb3VudHJ5X05hbWUnIGFuZCAnQ291bnRyeV9Db2RlJy4gU2luY2UgdGhlIGRhdGEgcmVsZXZlbnQgdG8gb3VyIGFuYWx5c2lzIGJlY29tZXMgaW52YWxpZCB3aXRob3V0IHRoZSAnQ291bnRyeV9OYW1lJyBvciAnQ291bnRyeV9Db2RlJywgaXQgbmVlZHMgdG8gYmUgZXhjbHVkZWQuIEJ1dCBiZWZvcmUgcHJvY2VlZGluZywgdG8gbWFrZSBzdXJlIGV4Y2x1ZGluZyB0aGVzZSBtaXNzaW5nIHZhbHVlcyBkbyBub3QgY3JlYXRlIGFuIGltcGFjdCBvbiB0aGUgZGF0YXNldCwgdGhlICUgb2YgbWlzc2luZyB2YWx1ZXMgaXMgY2FsY3VsYXRlZCBmb3IgJ0NvdW50cnlfTmFtZScgYW5kICdDb3VudHJ5X0NvZGUnIHZhcmlhYmxlcy4gVGhlIHRvdGFsIG51bWJlciBvZiB2YWx1ZXMgaW4gZWFjaCBvZiB0aGUgdmFyaWFibGVzIGNhbiBiZSBvYnRhaW5lZCB3aXRoIGxlbmd0aCgpIGZ1bmN0aW9uIGFzOg0KYGBge3J9DQojZmluZGluZyBsZW5ndGggdG8gY2FsY3VsYXRlIHRoZSAlIG9mIG1pc3NpbmcgdmFsdWVzIGluIGNvdW50cnkNCmxlbmd0aChkYXRhc2V0X2pvaW4kQ291bnRyeV9OYW1lKQ0KbGVuZ3RoKGRhdGFzZXRfam9pbiRDb3VudHJ5X0NvZGUpDQpgYGANCiUgb2YgbWlzc2luZyB2YWx1ZXMgPSAoTm8uIG9mIG1pc3NpbmcgdmFsdWVzIC8gVG90YWwgbnVtYmVyIG9mIHZhbHVlcykgKiAxMDAgDQo9ICgyMC8xMDcyKSAqIDEwMCA9IDEuODYlDQoNClNpbmNlIHRoZSBwZXJjZW50YWdlIG9mIG1pc3NpbmcgdmFsdWVzIGluIGNvdW50cnkgbmFtZSBhbmQgY29kZSBpcyBsZXNzIHRoYW4gNSUsIHdlIGNhbiBub3cgb21pdCB0aGUgb2JzZXJ2YXRpb25zIHdpdGggbWlzc2luZyB2YWx1ZXMuDQoNCmBgYHtyfQ0KZGF0YXNldF9qb2luIDwtIGRhdGFzZXRfam9pblstd2hpY2goaXMubmEoZGF0YXNldF9qb2luJENvdW50cnlfTmFtZSkpLF0NCmNvbFN1bXMoaXMubmEoZGF0YXNldF9qb2luKSkNCmBgYA0KRnJvbSBhYm92ZSwgaXQgY2FuIGJlIHNlZW4gdGhhdCB0aGUgbnVtYmVyIG9mIG1pc3NpbmcgdmFsdWVzIGlzIG5vdyAwIGZvciB0aGUgdmFyaWFibGVzICdDb3VudHJ5X05hbWUnIGFuZCAnQ291bnRyeV9Db2RlJy4gDQoNClRoZSB2YXJpYWJsZSAnSW5jb21lX2dyb3VwJyBoYXMgMTg0IG1pc3NpbmcgdmFsdWVzIGFuZCBpcyBhIGNhdGVnb3JpY2FsIHZhcmlhYmxlLiAnZ2V0bW9kZScgaXMgYSBjdXN0b20gZnVuY3Rpb24gdG8gZmluZCB0aGUgbW9zdCBjb21tb24gY2F0ZWdvcmljYWwgdmFsdWUgdG8gYmUgcmVwbGFjZWQgaW4gcGxhY2Ugb2YgbWlzc2luZyB2YWx1ZXMuIA0KYGBge3J9DQojZnVuY3Rpb24gdG8gZmluZCB0aGUgbW9zdCBjb21tb24gY2F0ZWdvcnkNCmdldG1vZGUgPC0gZnVuY3Rpb24odikgew0KICAgdW5pcXYgPC0gdW5pcXVlKHYpDQogICB1bmlxdlt3aGljaC5tYXgodGFidWxhdGUobWF0Y2godiwgdW5pcXYpKSldDQp9DQppbmNvbWVfbW9kZSA8LSBnZXRtb2RlKGRhdGFzZXRfam9pbiRJbmNvbWVfZ3JvdXApDQoNCiNSZXBsYWNpbmcgdGhlIG1pc3NpbmcgdmFsdWVzIHdpdGggdGhlIG1vc3QgY29tbW9uIGNhdGVnb3J5DQpkYXRhc2V0X2pvaW4kSW5jb21lX2dyb3VwW3doaWNoKGlzLm5hKGRhdGFzZXRfam9pbiRJbmNvbWVfZ3JvdXApKV0gPC0gaW5jb21lX21vZGUNCmNvbFN1bXMoaXMubmEoZGF0YXNldF9qb2luKSkNCmhlYWQoZGF0YXNldF9qb2luKQ0KYGBgDQoNCk5vdywgdGhlICdQZXJjZW50YWdlJyB2YXJpYWJsZSBoYXMgNTU0IG1pc3NpbmcgdmFsdWVzLiBTaW5jZSB0aGUgZGF0YXNldCBpcyBpbiBsb25nIGZvcm1hdCwgZm9yIGVhY2ggY291bnRyeSBjb2RlLCB3ZSBoYXZlIDQgdmFsdWVzLCBjb3JyZXNwb25kaW5nIHRvIHRoZSB5ZWFycyAyMDE0LCAyMDE1LCAyMDE2IGFuZCAyMDE3LiBJbiBvcmRlciB0byBpbXB1dGUgdGhlIG1pc3NpbmcgcGVyY2VudGFnZSB2YWx1ZXMgd2l0aCB0aGVpciBtZWFucywgd2UgZ3JvdXAgdGhlIGRhdGFzZXQgYnkgY291bnRyeSBjb2RlIGFuZCBnZXQgdGhlIG1lYW4gZm9yIGVhY2ggY291bnRyeSBhbmQgaW1wdXRlIHRoZW0gaW4gdGhlIG1pc3NpbmcgdmFsdWVzLg0KDQpgYGB7cn0NCmRhdGFzZXRfaW1wdXRlIDwtIGRhdGFzZXRfam9pbiAlPiUgZ3JvdXBfYnkoQ291bnRyeV9Db2RlKSAlPiUgIG11dGF0ZShwZXJjZW50YWdlID0gaWZlbHNlKGlzLm5hKHBlcmNlbnRhZ2UpLCBtZWFuKHBlcmNlbnRhZ2UsIG5hLnJtID0gVFJVRSksIHBlcmNlbnRhZ2UpKQ0KDQpjb2xTdW1zKGlzLm5hKGRhdGFzZXRfaW1wdXRlKSkNCmBgYA0KDQpUaGVyZSBhcmUgbm93IDM5MiBtaXNzaW5nIHZhbHVlcyBpbiB0aGUgcGVyY2VudGFnZSB2YXJpYWJsZSwgd2hpY2ggYXJlIGFjdHVhbGx5IHNwZWNpYWwgdmFsdWVzICdOYU4nLCBjYXVzZWQgYmVjYXVzZSBvZiB1bmF2YWlsYmxlIG1lYW5zLg0KDQpgYGB7cn0NCnN1bShpcy5uYW4oZGF0YXNldF9pbXB1dGUkcGVyY2VudGFnZSkpDQpgYGANCg0KVGhpcyBtZWFucyB0aGF0IHRoZXNlICBjb3VudHJpZXMgZG9uJ3QgaGF2ZSBhbnkgcGVyY2VudGFnZSB2YWx1ZXMgcmVjb3JkZWQgdGhyb3VnaCB0aGUgeWVhcnMgMjAxNC0yMDE3LiBIZW5jZSB0aGVyZSBpcyBubyBtZWFuIHZhbHVlIHRvIGltcHV0ZSB0aGUgbWlzc2luZyB2YWx1ZXMuIFNpbmNlIHRoZXNlIHZhbHVlcyBhcmUgdW5hdmFpbGFibGUgaW4gdGhlIGRhdGFzZXQsIHdlIG9taXQgdGhlbS4NCg0KYGBge3J9DQpkYXRhc2V0X2ltcHV0ZTIgPC0gbmEub21pdChkYXRhc2V0X2ltcHV0ZSkNCmNvbFN1bXMoaXMubmEoZGF0YXNldF9pbXB1dGUyKSkNCmBgYA0KDQoNCiMjCVRpZHkgJiBNYW5pcHVsYXRlIERhdGEgSUkgDQoNClRvIGZpbmQgdGhlIGdyb3d0aCBpbiBlbXBsb3ltZW50IHRvIHBvcHVsYXRpb24gcmF0aW8gYmV0d2VlbiB0aGUgeWVhcnMgMjAxNCBhbmQgMjAxNywgd2UgY29udmVydCB0aGUgZGF0YXNldCBmcm9tIGxvbmcgZm9ybWF0IHRvIHdpZGUgZm9ybWF0IGZvciBlYXNlIG9mIGNhbGN1bGF0aW9uIGFuZCBjb21wdXRlIHRoZSBncm93dGgodmFsdWVzIG9mIDIwMTcgLSAyMDE0KS4NCg0KYGBge3J9DQojY29udmVydGluZyB0aGUgeWVhciB2YXJpYWJsZSBpbnRvIHdpZGUgZm9ybWF0IGluIG9yZGVyIHRvIGNhbGN1bGF0ZSBncm93dGgNCndpZGVGb3JtYXQgPC0gc3ByZWFkKGRhdGFzZXRfaW1wdXRlMiwga2V5ID0geWVhciwgdmFsdWUgPSBwZXJjZW50YWdlKQ0Kd2lkZUZvcm1hdCA8LSB3aWRlRm9ybWF0ICU+JSBtdXRhdGUoZ3Jvd3RoPShgMjAxN2AtYDIwMTRgKSkNCmhlYWQod2lkZUZvcm1hdCkNCmBgYA0KDQpVcG9uIGNhbGN1bGF0aW5nIHRoZSBncm93dGgsIHRoZSBkYXRhc2V0IGlzIHRpZGllZCBiYWNrIHVwIGludG8gJ2xvbmdGb3JtYXQnIGFuZCB5ZWFyIHZhcmlhYmxlJ3MgZGF0YXR5cGUgaXMgY29udmVydGVkIGZyb20gY2hhcmFjdGVyIHRvIGludGVnZXIuDQoNCmBgYHtyfQ0KI0NvbnZlcnRpbmcgYmFjayB0byBsb25nIGZvcm1hdA0KbG9uZ0Zvcm1hdCA8LSB3aWRlRm9ybWF0ICU+JSBnYXRoZXIoa2V5ID0gInllYXIiLCB2YWx1ZT0gInBlcmNlbnRhZ2UiLCA0OjcgKQ0KbG9uZ0Zvcm1hdCR5ZWFyIDwtIGFzLmludGVnZXIobG9uZ0Zvcm1hdCR5ZWFyKQ0KaGVhZChsb25nRm9ybWF0KQ0KDQpgYGANCg0KDQojIwlTY2FuIElJDQoNCkZyb20gYWJvdmUsIGl0IGNhbiBiZSBzZWVuIHRoYXQgJ2dyb3d0aCcsICd5ZWFyJyBhbmQgJ3BlcmNlbnRhZ2UnIGFyZSB0aGUgb25seSBudW1lcmljIHZhcmlhYmxlcyB0aGF0IG5lZWRzIHRvIGJlIHNjYW5uZWQgZm9yIG91dGxpZXJzLiBTaW5jZSB0aGUgdmFyaWFibGUgJ3llYXInIHdhcyBjcmVhdGVkIGFzIHBhcnQgb2YgdGhlIHRpZHkgc3RlcCwgaXQgb25seSBoYXMgZm91ciB2YWx1ZXMsIDIwMTQtMjAxNy4gSXQgdGhlcmVmb3JlIGRvZXNuJ3QgcmVxdWlyZSBhbnkgY2hlY2tzIGZvciBvdXRsaWVycy4gUHJvY2VlZGluZyB0byBjaGVjayBmb3Igb3V0bGllcnMgaW4gIHRoZSB2YXJpYWJsZXMgJ2dyb3d0aCcgYW5kICd5ZWFyJyB1c2luZyB0aGUgei1zY29yZSBtZXRob2QgZnJvbSBvdXRsaWVycyBwYWNrYWdlLiANCg0KYGBge3J9DQojY2hlY2tpbmcgZm9yIG91dGxpZXJzIGluICdncm93dGgnIHZhcmlhYmxlDQp6LnNjb3JlR3Jvd3RoIDwtIGxvbmdGb3JtYXQkZ3Jvd3RoICU+JSAgc2NvcmVzKHR5cGUgPSAieiIpDQpsZW5ndGgod2hpY2goIGFicyh6LnNjb3JlR3Jvd3RoKSA+MyApKQ0KDQojY2hlY2tpbmcgZm9yIG91dGxpZXJzIGluICdwZXJjZW50YWdlJyB2YXJpYWJsZQ0Kei5zY29yZVBlcmNlbnRhZ2UgPC0gbG9uZ0Zvcm1hdCRwZXJjZW50YWdlICU+JSAgc2NvcmVzKHR5cGUgPSAieiIpDQpsZW5ndGgod2hpY2goIGFicyh6LnNjb3JlUGVyY2VudGFnZSkgPjMgKSkNCmBgYA0KSXQgY2FuIGJlIHNlZW4gdGhhdCB0aGVyZSBhcmUgNCBvdXRsaWVycyBpbiAnZ3Jvd3RoJyB2YXJpYWJsZSBhbmQgMSBvdXRsaWVyIGluICdwZXJjZW50YWdlJyB2YXJpYWJsZS4gVGhlc2Ugb3V0bGllciB2YWx1ZXMgYmVsb25nIHRvIGEgY291bnRyeSdzIGdyb3d0aCBhbmQgcGVyY2VudGFnZSBvZiBlbXBsb3ltZW50IHRvIHBvcHVsYXRpb24gcmF0aW8gcmVzcGVjdGl2ZWx5LiBTaW5jZSB0aGVzZSBhcmUgb2JzZXJ2YXRpb25zIG9mIGEgY291bnRyeSdzIHBlcmZvcm1hbmNlLCB0aGV5IGNhbm5vdCBiZSBvbWl0dGVkLCBkZWxldGVkLCBpbXB1dGVkIG9yIGNhcHBlZCBhbmQgYXJlIHRyZWF0ZWQgYXMgdmFsaWQgZGF0YSBhbmQgbm90IG91dGxpZXJzLiBJbiBvcmRlciB0byByZWR1Y2UgdGhlIHZhcmlhdGlvbiBjYXVzZWQgYnkgdGhlc2UgdmFsdWVzIGluIHRoZSAncGVyY2VudGFnZScgdmFyaWFibGUsIHdlIGFwcGx5IHRoZSBiZWxvdyBleHBsYWluZWQgdHJhbnNmb3JtYXRpb25zLiAgICAgDQoNCg0KIyMJVHJhbnNmb3JtIA0KDQpUaGUgaGlzdG9ncmFtIG9mICdwZXJjZW50YWdlJyB2YXJpYWJsZSBpcyBwbG90dGVkIHRvIGFuYWx5c2UgdGhlIGRpc3RyaWJ1dGlvbi4NCg0KYGBge3J9DQpoaXN0KGxvbmdGb3JtYXQkcGVyY2VudGFnZSkNCmBgYA0KDQpJdCBjYW4gYmUgc2VlbiB0aGF0IHRoZSBkaXN0cmlidXRpb24gaXMgbm90IG5vcm1hbC4gU2luY2UgaXQgaXMgcmlnaHQgc2tld2VkLCB3ZSBjb25zaWRlciB0YWtpbmcgc3F1YXJlIHJvb3QodXNpbmcgc3FydCgpIGZ1bmN0aW9uKSBhbmQgbG9nYXJpdGhtKHVzaW5nIGxvZzEwKCkgZnVuY3Rpb24pIHRvIHNlZSB3aGljaCB0cmFuc2Zvcm1hdGlvbiB3b3JrcyBiZXN0Lg0KDQpgYGB7cn0NCnBlcmNlbnRhZ2VfbG9nIDwtIGxvZzEwKGxvbmdGb3JtYXQkcGVyY2VudGFnZSkNCmhpc3QocGVyY2VudGFnZV9sb2cpDQoNCnBlcmNlbnRhZ2Vfc3FydCA8LSBzcXJ0KGxvbmdGb3JtYXQkcGVyY2VudGFnZSkNCmhpc3QocGVyY2VudGFnZV9zcXJ0KQ0KYGBgDQpGcm9tIHRoZSBhYm92ZSBoaXN0b2dyYW1zLCB3ZSBjYW4gc2VlIHRoYXQgdGhlIHNxdWFyZSByb290IHRyYW5zZm9ybWF0aW9uIGhhcyByZWR1Y2VkIHRoZSBza2V3bmVzcyBhbmQgaGFzIGltcHJvdmVkIHRoZSBzeW1tZXRyeSBvZiB0aGUgJ3BlcmNlbnRhZ2UnIGRpc3RyaWJ1dGlvbi4NCg0KPGJyPg0KPGJyPg0KDQo=