IMPORTANT NOTE:

You must also publish your report to RPubs

Required packages

Load the required packages.

library(readr)
library(tidyr)
library(dplyr)
library(editrules)
library(MVN)

Executive Summary

As a HR analyst, I need to pre process this data before the analysis.

First, I merged the 3 datasets into one by a common variable (EmployID) after importing the datasets. This will be the dataset I use for the analysis later on.

Second, I converted some of the charater and numeric values to factors. This is based on my understanding of the datasets. After assessing the dataset, I decide to leave it as it is as it’s already in a tidy format.

Third, I created 2 new variables and added them to this dataset while preserving the existing dataset. This will become useful for my analysis later on.

Then, missing values are identified in each column in the dataset. I decide to delete rows with missing values because the number of missing values are relatively small. Also, inconsistencies and obvious errors are scanned under my “Rule” file I created.

Next, mvn function was used to detect outliers among the multivariate attributes. 154 outliers were found in total, which is relatively small compared to the size of the dataset. I decided to delete the outliers and analyse the final dataset.

Last but not least, variable “MonthlyIncome” was transformed into normal distribution using transformation techiniques.

Now the dataset is ready for analysis.

Data

The 3 datasets are from https://www.kaggle.com/vjchoudhary7/hr-analytics-case-study. Company XYZ has around 15% of attrition every year, which is not good for the company projects. Therefore, HR analysts want to analyse the datasets and understand what’s causing this level of attrition. By doing this, HR can make recommendations to the management regarding how to keep their employees. Three datasets were collected from around 4000 employees in the company at a given point of time.

general <- read_csv("general_data.csv")
head(general)
survey <- read_csv("employee_survey_data.csv")
head(survey)
feedback <- read_csv("manager_survey_data.csv")
head(feedback)
full1 <- full_join(general, survey, by = "EmployeeID")
full <- full_join(full1, feedback, by = "EmployeeID")
# All 3 datasets are joined
head(full)

Understand

The merged dataset “full” is used for analysis. It contains a mixture of data types including numerics, characters, factors etc. In this step, data type conversions are performed to convert some numeric and character variables to factors.

full$Attrition <- factor(full$Attrition, levels = c("Yes", "No"))
full$Education <- factor(full$Education, levels = c(1, 2, 3, 4, 5), labels = c("Below College", "College", "Bachelor", "Master", "Doctor"), ordered=TRUE)
full$Gender <- factor(full$Gender, levels = c("Male", "Female"))
full$StockOptionLevel <- factor(full$StockOptionLevel, levels = c(0, 1, 2, 3))
full$JobLevel <- factor(full$JobLevel, levels = c(1, 2, 3, 4, 5), ordered = TRUE)
full$MaritalStatus <- factor(full$MaritalStatus, levels = c("Married", "Single", "Divorced"))
full$Over18 <- factor(full$Over18, levels = c("Y", "N"))
full$EnvironmentSatisfaction <- factor(full$EnvironmentSatisfaction, levels = c(1, 2, 3, 4), labels = c("Low", "Medium", "High", "Very High"), ordered = TRUE)
full$JobSatisfaction <- factor(full$JobSatisfaction, levels = c(1, 2, 3, 4), labels = c("Low", "Medium", "High", "Very High"), ordered = TRUE)
full$WorkLifeBalance <- factor(full$WorkLifeBalance, levels = c(1, 2, 3, 4), labels = c("Bad", "Good", "Better", "Best"), ordered = TRUE)
full$JobInvolvement <- factor(full$JobInvolvement, levels = c(1, 2, 3, 4), labels = c("Low", "Medium", "High", "Very High"), ordered = TRUE) 
full$PerformanceRating <- factor(full$PerformanceRating, levels = c(1, 2, 3, 4), labels = c("Low", "Good", "Excellent", "Outstanding"), ordered = TRUE)

Tidy & Manipulate Data I

This data is in the tidy format. No need to reshape the data.

Tidy & Manipulate Data II

Assuming “YearsAtCompany” is consecutive years, a new variable “employ_age” is created to represent the age when employees started working for the company. Another variable “previous_workyears” is created to find out the years employees had worked before joining the company.

full2 <- mutate(full, employ_age = Age - YearsAtCompany, previous_workyears = TotalWorkingYears-YearsAtCompany)

Scan I

Total number of missing values are identified in each column in the dataset. As the amount of missing data is relatively small (less than 5% of the dataset), I decide to delete rows with missing values by using na.omit function. To scan for inconsistencies and obvious errors, I created a text under the name of “Rule.txt”. I’ve set some rules in this text and scanned the data using violatedEdits function. According to the output, nil inconsistencies found.

sum(is.na(full2))
[1] 120
colSums(is.na(full2))
                    Age               Attrition          BusinessTravel 
                      0                       0                       0 
             Department        DistanceFromHome               Education 
                      0                       0                       0 
         EducationField           EmployeeCount              EmployeeID 
                      0                       0                       0 
                 Gender                JobLevel                 JobRole 
                      0                       0                       0 
          MaritalStatus           MonthlyIncome      NumCompaniesWorked 
                      0                       0                      19 
                 Over18       PercentSalaryHike           StandardHours 
                      0                       0                       0 
       StockOptionLevel       TotalWorkingYears   TrainingTimesLastYear 
                      0                       9                       0 
         YearsAtCompany YearsSinceLastPromotion    YearsWithCurrManager 
                      0                       0                       0 
EnvironmentSatisfaction         JobSatisfaction         WorkLifeBalance 
                     25                      20                      38 
         JobInvolvement       PerformanceRating              employ_age 
                      0                       0                       0 
     previous_workyears 
                      9 
full3 <- na.omit(full2)

Rule <- editfile("Rule.txt", type = "all")
Rule

Data model:
dat2  : Attrition %in% c('No', 'Yes')
dat3  : Education %in% c('Bachelor', 'Below College', 'College', 'Doctor', 'Master')
dat4  : EnvironmentSatisfaction %in% c('High', 'Low', 'Medium', 'Very High')
dat5  : Gender %in% c('Female', 'Male')
dat6  : JobInvolvement %in% c('High', 'Low', 'Medium', 'Very High')
dat7  : JobLevel %in% c('1', '2', '3', '4', '5')
dat8  : JobSatisfaction %in% c('High', 'Low', 'Medium', 'Very High')
dat9  : MaritalStatus %in% c('Divorced', 'Married', 'Single')
dat10 : Over18 %in% c('N', 'Y')
dat11 : PerformanceRating %in% c('Excellent', 'Good', 'Low', 'Outstanding')
dat12 : StockOptionLevel %in% c('0', '1', '2', '3')
dat13 : WorkLifeBalance %in% c('Bad', 'Best', 'Better', 'Good') 

Edit set:
num1  : 0 < Age
num2  : 0 < MonthlyIncome
num3  : Age <= 110
num4  : TotalWorkingYears < Age
num5  : 0 <= NumCompaniesWorked
num6  : 0 < StandardHours
num7  : 0 <= TotalWorkingYears
num8  : 0 <= TrainingTimesLastYear
num9  : 0 <= YearsAtCompany
num10 : 0 <= YearsSinceLastPromotion
num11 : 0 <= YearsWithCurrManager
num12 : 0 < DistanceFromHome
num13 : 0 < EmployeeID
num14 : employ_age <= Age
num15 : previous_workyears <= TotalWorkingYears
num16 : YearsSinceLastPromotion <= YearsAtCompany
num17 : YearsWithCurrManager <= YearsAtCompany
cat18 : if( MaritalStatus == 'Married' ) Over18 != 'N'
mix19 : if( 18 < Age ) Over18 != 'N' 
violated <- violatedEdits(Rule, full3)
summary(violated)
NULL

Scan II

There are 15 numeric variables in this dataset. Multivariate Outlier Detection Methods can be used to detect outliers. After doing some simple ploting such as box plot to understand the variables, I decided to separate “EmployeeCount” and “Standard Hours” from my Multivariate Outlier Detection. This is because these two variables have only one value. (see the boxplot below)

The Mahalanobis distance is used to detect outliers for the multivariate setting. “Education” is used as a categorical variable to subset the data. According to the output, there are 19 outliers among employees with a master’s degree, 8 outliers among employees with below College degree, 9 outliers among employees with a College degree, 115 outliers among employees with a bachelor’s degree and 3 outliers among employees with a doctor’s degree.

Because this dataset is about the information of employees from XYZ company, the outliers are most likely due to data entry error. Plus, the outlier observations are relatively small in numbers. I decide to delete the outliers in this caes. The mvn() function can identify the locations of outliers and show the new data without outliers. I have made the new data without outliers shown in the output.

While these outliers are removed, it should be noted that theses could be relevant to the final analysis. As the purpose of this data is to identify reasons that employees are leaving the company, these outliers could be valueable. I would recommend these outliers be investigated further and readded to the dataset if they are deemed accurate.

boxplot(full3$MonthlyIncome ~ full3$Education, main="Monthly Income by Education", ylab = "Monthly Income", xlab = "Education")

boxplot(full3$EmployeeCount, main = "Employee Count")

boxplot(full3$StandardHours, main = "Standard Hours")


Master <- full3 %>% filter(Education == "Master") %>% select(DistanceFromHome, NumCompaniesWorked, PercentSalaryHike, TotalWorkingYears, TrainingTimesLastYear, YearsAtCompany, YearsWithCurrManager, Age, MonthlyIncome, YearsSinceLastPromotion, employ_age, previous_workyears, EmployeeID)
results1 <- mvn(data = Master, multivariateOutlierMethod = "quan", showOutliers = TRUE, showNewData = TRUE )

results1$multivariateOutliers
results1$newData

Bel_College <- full3 %>% filter(Education == "Below College") %>% select(DistanceFromHome, NumCompaniesWorked, PercentSalaryHike, TotalWorkingYears, TrainingTimesLastYear, YearsAtCompany, YearsWithCurrManager, Age, MonthlyIncome, YearsSinceLastPromotion, employ_age, previous_workyears, EmployeeID)
results2 <- mvn(data = Bel_College, multivariateOutlierMethod = "quan", showOutliers = TRUE, showNewData = TRUE )

results2$newData

College <- full3 %>% filter(Education == "College") %>% select(DistanceFromHome, NumCompaniesWorked, PercentSalaryHike, TotalWorkingYears, TrainingTimesLastYear, YearsAtCompany, YearsWithCurrManager, Age, MonthlyIncome, YearsSinceLastPromotion, employ_age, previous_workyears, EmployeeID)
results3 <- mvn(data = College, multivariateOutlierMethod = "quan", showOutliers = TRUE, showNewData = TRUE )

results3$newData

Bachelor <- full3 %>% filter(Education == "Bachelor") %>% select(DistanceFromHome, NumCompaniesWorked, PercentSalaryHike, TotalWorkingYears, TrainingTimesLastYear, YearsAtCompany, YearsWithCurrManager, Age, MonthlyIncome, YearsSinceLastPromotion, employ_age, previous_workyears, EmployeeID)
results4 <- mvn(data = Bachelor, multivariateOutlierMethod = "quan", showOutliers = TRUE, showNewData = TRUE )

results4$newData

Doctor <- full3 %>% filter(Education == "Doctor") %>% select(DistanceFromHome, NumCompaniesWorked, PercentSalaryHike, TotalWorkingYears, TrainingTimesLastYear, YearsAtCompany, YearsWithCurrManager, Age, MonthlyIncome, YearsSinceLastPromotion, employ_age, previous_workyears, EmployeeID)
results5 <- mvn(data = Doctor, multivariateOutlierMethod = "quan", showOutliers = TRUE, showNewData = TRUE )

results5$newData

Transform

I used the full4 dataset after dealing with missing values and outliers. The variable I choose to transform is “MonthlyIncome”. The histogram of monthly income is right-skewed according to the output. In order to decrease the skewness and convert the distribution into a normal distribution, 3 transformation techiniques are used.

From the results it is obvious to see that Logarithmic transformation (base 10) and natural logrithmic transformation work slightly better than the cube root transformation.

hist(full4$MonthlyIncome)

log_income <- log10(full4$MonthlyIncome)
hist(log_income)

ln_income <- log(full4$MonthlyIncome)
hist(ln_income)

cuberoot_income <- (full4$MonthlyIncome)^(1/3)
hist(cuberoot_income)



LS0tDQp0aXRsZTogIk1BVEgyMzQ5IFNlbWVzdGVyIDEsIDIwMTkiDQphdXRob3I6ICJXZWkgWmhhbmcgICBzMzc1OTYwNyINCnN1YnRpdGxlOiBBc3NpZ25tZW50IDMgLSBIUiBBbmFseXRpY3MgRGF0YSBQcmVwcm9jZXNzaW5nDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQNCi0tLQ0KDQoNCiMjIElNUE9SVEFOVCBOT1RFOg0KDQpZb3UgbXVzdCBhbHNvIHB1Ymxpc2ggeW91ciByZXBvcnQgdG8gUlB1YnMgDQoNCiMjIFJlcXVpcmVkIHBhY2thZ2VzIA0KTG9hZCB0aGUgcmVxdWlyZWQgcGFja2FnZXMuDQoNCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQ0KbGlicmFyeShyZWFkcikNCmxpYnJhcnkodGlkeXIpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShlZGl0cnVsZXMpDQpsaWJyYXJ5KE1WTikNCmBgYA0KDQoNCiMjIEV4ZWN1dGl2ZSBTdW1tYXJ5IA0KQXMgYSBIUiBhbmFseXN0LCBJIG5lZWQgdG8gcHJlIHByb2Nlc3MgdGhpcyBkYXRhIGJlZm9yZSB0aGUgYW5hbHlzaXMuDQoNCkZpcnN0LCBJIG1lcmdlZCB0aGUgMyBkYXRhc2V0cyBpbnRvIG9uZSBieSBhIGNvbW1vbiB2YXJpYWJsZSAoRW1wbG95SUQpIGFmdGVyIGltcG9ydGluZyB0aGUgZGF0YXNldHMuIFRoaXMgd2lsbCBiZSB0aGUgZGF0YXNldCBJIHVzZSBmb3IgdGhlIGFuYWx5c2lzIGxhdGVyIG9uLg0KDQpTZWNvbmQsIEkgY29udmVydGVkIHNvbWUgb2YgdGhlIGNoYXJhdGVyIGFuZCBudW1lcmljIHZhbHVlcyB0byBmYWN0b3JzLiBUaGlzIGlzIGJhc2VkIG9uIG15IHVuZGVyc3RhbmRpbmcgb2YgdGhlIGRhdGFzZXRzLg0KQWZ0ZXIgYXNzZXNzaW5nIHRoZSBkYXRhc2V0LCBJIGRlY2lkZSB0byBsZWF2ZSBpdCBhcyBpdCBpcyBhcyBpdCdzIGFscmVhZHkgaW4gYSB0aWR5IGZvcm1hdC4NCg0KVGhpcmQsIEkgY3JlYXRlZCAyIG5ldyB2YXJpYWJsZXMgYW5kIGFkZGVkIHRoZW0gdG8gdGhpcyBkYXRhc2V0IHdoaWxlIHByZXNlcnZpbmcgdGhlIGV4aXN0aW5nIGRhdGFzZXQuIFRoaXMgd2lsbCBiZWNvbWUgdXNlZnVsIGZvciBteSBhbmFseXNpcyBsYXRlciBvbi4NCg0KVGhlbiwgbWlzc2luZyB2YWx1ZXMgYXJlIGlkZW50aWZpZWQgaW4gZWFjaCBjb2x1bW4gaW4gdGhlIGRhdGFzZXQuIEkgZGVjaWRlIHRvIGRlbGV0ZSByb3dzIHdpdGggbWlzc2luZyB2YWx1ZXMgYmVjYXVzZSB0aGUgbnVtYmVyIG9mIG1pc3NpbmcgdmFsdWVzIGFyZSByZWxhdGl2ZWx5IHNtYWxsLiBBbHNvLCBpbmNvbnNpc3RlbmNpZXMgYW5kIG9idmlvdXMgZXJyb3JzIGFyZSBzY2FubmVkIHVuZGVyIG15ICJSdWxlIiBmaWxlIEkgY3JlYXRlZC4gDQoNCk5leHQsIG12biBmdW5jdGlvbiB3YXMgdXNlZCB0byBkZXRlY3Qgb3V0bGllcnMgYW1vbmcgdGhlIG11bHRpdmFyaWF0ZSBhdHRyaWJ1dGVzLiAxNTQgb3V0bGllcnMgd2VyZSBmb3VuZCBpbiB0b3RhbCwgd2hpY2ggaXMgcmVsYXRpdmVseSBzbWFsbCBjb21wYXJlZCB0byB0aGUgc2l6ZSBvZiB0aGUgZGF0YXNldC4gSSBkZWNpZGVkIHRvIGRlbGV0ZSB0aGUgb3V0bGllcnMgYW5kIGFuYWx5c2UgdGhlIGZpbmFsIGRhdGFzZXQuDQoNCkxhc3QgYnV0IG5vdCBsZWFzdCwgdmFyaWFibGUgIk1vbnRobHlJbmNvbWUiIHdhcyB0cmFuc2Zvcm1lZCBpbnRvIG5vcm1hbCBkaXN0cmlidXRpb24gdXNpbmcgdHJhbnNmb3JtYXRpb24gdGVjaGluaXF1ZXMuDQoNCk5vdyB0aGUgZGF0YXNldCBpcyByZWFkeSBmb3IgYW5hbHlzaXMuDQoNCiMjIERhdGEgDQpUaGUgMyBkYXRhc2V0cyBhcmUgZnJvbSBodHRwczovL3d3dy5rYWdnbGUuY29tL3ZqY2hvdWRoYXJ5Ny9oci1hbmFseXRpY3MtY2FzZS1zdHVkeS4NCkNvbXBhbnkgWFlaIGhhcyBhcm91bmQgMTUlIG9mIGF0dHJpdGlvbiBldmVyeSB5ZWFyLCB3aGljaCBpcyBub3QgZ29vZCBmb3IgdGhlIGNvbXBhbnkgcHJvamVjdHMuIFRoZXJlZm9yZSwgSFIgYW5hbHlzdHMgd2FudCB0byBhbmFseXNlIHRoZSBkYXRhc2V0cyBhbmQgdW5kZXJzdGFuZCB3aGF0J3MgY2F1c2luZyB0aGlzIGxldmVsIG9mIGF0dHJpdGlvbi4gQnkgZG9pbmcgdGhpcywgSFIgY2FuIG1ha2UgcmVjb21tZW5kYXRpb25zIHRvIHRoZSBtYW5hZ2VtZW50IHJlZ2FyZGluZyBob3cgdG8ga2VlcCB0aGVpciBlbXBsb3llZXMuDQpUaHJlZSBkYXRhc2V0cyB3ZXJlIGNvbGxlY3RlZCBmcm9tIGFyb3VuZCA0MDAwIGVtcGxveWVlcyBpbiB0aGUgY29tcGFueSBhdCBhIGdpdmVuIHBvaW50IG9mIHRpbWUuDQoNCiogR2VuZXJhbF9kYXRhIGNvbnRhaW5zIDI0IHZhcmlhYmxlcy4gVGhleSBhcmUgZ2VuZXJhbCBpbmZvcm1hdGlvbiBhYm91dCBlbXBsb3llZXMgaW5jbHVkaW5nIGFnZSwgYXR0cml0aW9uLCBkZXBhcnRtZW50LCBlZHVjYXRpb24sIGdlbmRlciwgbW9udGhseSBpbmNvbWUsIHN0YW5kYXJkIGhvdXJzLCB5ZWFycyBhdCBjb21wYW55LCBldGMuDQoNCiogRW1wbG95ZWVfc3VydmV5X2RhdGEgaGFzIHRoZSBpbmZvcm1hdGlvbiBhYm91dCBzdXJ2ZXkgcmVzdWx0cy4gSXQgb25seSBjb250YWlucyA0IHZhcmlhYmxlcyBpbmNsdWRpbmcgZW1wbG95ZWVJRCwgV29yayBFbnZpcm9ubWVudCBTYXRpc2ZhY3Rpb24gTGV2ZWwsIEpvYiBTYXRpc2ZhY3Rpb24gTGV2ZWwgYW5kIFdvcmsgbGlmZSBiYWxhbmNlIGxldmVsLg0KDQoqIE1hbmFnZXJfc3VydmV5X2RhdGEgY29udGFpbnMgZW1wbG95ZWVzJyBmZWVkYmFjayBhYm91dCB0aGVpciBtYW5hZ2Vycy4gT25seSAzIHZhcmlhYmxlcyBhcmUgaW4gdGhpcyBkYXRhc2V0LiBUaGV5IGFyZSBlbXBsb3llZUlELEpvYiBJbnZvbHZlbWVudCBMZXZlbCBhbmQgUGVyZm9ybWFuY2UgcmF0aW5nIGZvciBsYXN0IHllYXIuDQpBbGwgdGhyZWUgZGF0YXNldHMgc2hhcmUgYSBjb21tb24gdmFyaWFibGUgLSBlbXBsb3llZUlELiBUaGV5IGFyZSBtZXJnZWQgdG9nZXRoZXIgdXNpbmcgZnVsbF9qb2luIGZ1bmN0aW9uLg0KYGBge3IsIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0NCmdlbmVyYWwgPC0gcmVhZF9jc3YoImdlbmVyYWxfZGF0YS5jc3YiKQ0KaGVhZChnZW5lcmFsKQ0Kc3VydmV5IDwtIHJlYWRfY3N2KCJlbXBsb3llZV9zdXJ2ZXlfZGF0YS5jc3YiKQ0KaGVhZChzdXJ2ZXkpDQpmZWVkYmFjayA8LSByZWFkX2NzdigibWFuYWdlcl9zdXJ2ZXlfZGF0YS5jc3YiKQ0KaGVhZChmZWVkYmFjaykNCmZ1bGwxIDwtIGZ1bGxfam9pbihnZW5lcmFsLCBzdXJ2ZXksIGJ5ID0gIkVtcGxveWVlSUQiKQ0KZnVsbCA8LSBmdWxsX2pvaW4oZnVsbDEsIGZlZWRiYWNrLCBieSA9ICJFbXBsb3llZUlEIikNCiMgQWxsIDMgZGF0YXNldHMgYXJlIGpvaW5lZA0KaGVhZChmdWxsKQ0KYGBgDQoNCiMjIFVuZGVyc3RhbmQgDQpUaGUgbWVyZ2VkIGRhdGFzZXQgImZ1bGwiIGlzIHVzZWQgZm9yIGFuYWx5c2lzLiBJdCBjb250YWlucyBhIG1peHR1cmUgb2YgZGF0YSB0eXBlcyBpbmNsdWRpbmcgbnVtZXJpY3MsIGNoYXJhY3RlcnMsIGZhY3RvcnMgZXRjLg0KSW4gdGhpcyBzdGVwLCBkYXRhIHR5cGUgY29udmVyc2lvbnMgYXJlIHBlcmZvcm1lZCB0byBjb252ZXJ0IHNvbWUgbnVtZXJpYyBhbmQgY2hhcmFjdGVyIHZhcmlhYmxlcyB0byBmYWN0b3JzLiANCg0KYGBge3IsIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0NCmZ1bGwkQXR0cml0aW9uIDwtIGZhY3RvcihmdWxsJEF0dHJpdGlvbiwgbGV2ZWxzID0gYygiWWVzIiwgIk5vIikpDQpmdWxsJEVkdWNhdGlvbiA8LSBmYWN0b3IoZnVsbCRFZHVjYXRpb24sIGxldmVscyA9IGMoMSwgMiwgMywgNCwgNSksIGxhYmVscyA9IGMoIkJlbG93IENvbGxlZ2UiLCAiQ29sbGVnZSIsICJCYWNoZWxvciIsICJNYXN0ZXIiLCAiRG9jdG9yIiksIG9yZGVyZWQ9VFJVRSkNCmZ1bGwkR2VuZGVyIDwtIGZhY3RvcihmdWxsJEdlbmRlciwgbGV2ZWxzID0gYygiTWFsZSIsICJGZW1hbGUiKSkNCmZ1bGwkU3RvY2tPcHRpb25MZXZlbCA8LSBmYWN0b3IoZnVsbCRTdG9ja09wdGlvbkxldmVsLCBsZXZlbHMgPSBjKDAsIDEsIDIsIDMpKQ0KZnVsbCRKb2JMZXZlbCA8LSBmYWN0b3IoZnVsbCRKb2JMZXZlbCwgbGV2ZWxzID0gYygxLCAyLCAzLCA0LCA1KSwgb3JkZXJlZCA9IFRSVUUpDQpmdWxsJE1hcml0YWxTdGF0dXMgPC0gZmFjdG9yKGZ1bGwkTWFyaXRhbFN0YXR1cywgbGV2ZWxzID0gYygiTWFycmllZCIsICJTaW5nbGUiLCAiRGl2b3JjZWQiKSkNCmZ1bGwkT3ZlcjE4IDwtIGZhY3RvcihmdWxsJE92ZXIxOCwgbGV2ZWxzID0gYygiWSIsICJOIikpDQpmdWxsJEVudmlyb25tZW50U2F0aXNmYWN0aW9uIDwtIGZhY3RvcihmdWxsJEVudmlyb25tZW50U2F0aXNmYWN0aW9uLCBsZXZlbHMgPSBjKDEsIDIsIDMsIDQpLCBsYWJlbHMgPSBjKCJMb3ciLCAiTWVkaXVtIiwgIkhpZ2giLCAiVmVyeSBIaWdoIiksIG9yZGVyZWQgPSBUUlVFKQ0KZnVsbCRKb2JTYXRpc2ZhY3Rpb24gPC0gZmFjdG9yKGZ1bGwkSm9iU2F0aXNmYWN0aW9uLCBsZXZlbHMgPSBjKDEsIDIsIDMsIDQpLCBsYWJlbHMgPSBjKCJMb3ciLCAiTWVkaXVtIiwgIkhpZ2giLCAiVmVyeSBIaWdoIiksIG9yZGVyZWQgPSBUUlVFKQ0KZnVsbCRXb3JrTGlmZUJhbGFuY2UgPC0gZmFjdG9yKGZ1bGwkV29ya0xpZmVCYWxhbmNlLCBsZXZlbHMgPSBjKDEsIDIsIDMsIDQpLCBsYWJlbHMgPSBjKCJCYWQiLCAiR29vZCIsICJCZXR0ZXIiLCAiQmVzdCIpLCBvcmRlcmVkID0gVFJVRSkNCmZ1bGwkSm9iSW52b2x2ZW1lbnQgPC0gZmFjdG9yKGZ1bGwkSm9iSW52b2x2ZW1lbnQsIGxldmVscyA9IGMoMSwgMiwgMywgNCksIGxhYmVscyA9IGMoIkxvdyIsICJNZWRpdW0iLCAiSGlnaCIsICJWZXJ5IEhpZ2giKSwgb3JkZXJlZCA9IFRSVUUpIA0KZnVsbCRQZXJmb3JtYW5jZVJhdGluZyA8LSBmYWN0b3IoZnVsbCRQZXJmb3JtYW5jZVJhdGluZywgbGV2ZWxzID0gYygxLCAyLCAzLCA0KSwgbGFiZWxzID0gYygiTG93IiwgIkdvb2QiLCAiRXhjZWxsZW50IiwgIk91dHN0YW5kaW5nIiksIG9yZGVyZWQgPSBUUlVFKQ0KYGBgDQoNCg0KIyMJVGlkeSAmIE1hbmlwdWxhdGUgRGF0YSBJIA0KVGhpcyBkYXRhIGlzIGluIHRoZSB0aWR5IGZvcm1hdC4gTm8gbmVlZCB0byByZXNoYXBlIHRoZSBkYXRhLg0KDQpgYGB7ciwgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQ0KDQpgYGANCg0KIyMJVGlkeSAmIE1hbmlwdWxhdGUgRGF0YSBJSSANCkFzc3VtaW5nICJZZWFyc0F0Q29tcGFueSIgaXMgY29uc2VjdXRpdmUgeWVhcnMsIGEgbmV3IHZhcmlhYmxlICJlbXBsb3lfYWdlIiBpcyBjcmVhdGVkIHRvIHJlcHJlc2VudCB0aGUgYWdlIHdoZW4gZW1wbG95ZWVzIHN0YXJ0ZWQgd29ya2luZyBmb3IgdGhlIGNvbXBhbnkuIEFub3RoZXIgdmFyaWFibGUgInByZXZpb3VzX3dvcmt5ZWFycyIgaXMgY3JlYXRlZCB0byBmaW5kIG91dCB0aGUgeWVhcnMgZW1wbG95ZWVzIGhhZCB3b3JrZWQgYmVmb3JlIGpvaW5pbmcgdGhlIGNvbXBhbnkuDQoNCmBgYHtyLCBlY2hvPVRSVUUsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9DQpmdWxsMiA8LSBtdXRhdGUoZnVsbCwgZW1wbG95X2FnZSA9IEFnZSAtIFllYXJzQXRDb21wYW55LCBwcmV2aW91c193b3JreWVhcnMgPSBUb3RhbFdvcmtpbmdZZWFycy1ZZWFyc0F0Q29tcGFueSkNCmBgYA0KDQoNCiMjCVNjYW4gSSANClRvdGFsIG51bWJlciBvZiBtaXNzaW5nIHZhbHVlcyBhcmUgaWRlbnRpZmllZCBpbiBlYWNoIGNvbHVtbiBpbiB0aGUgZGF0YXNldC4gQXMgdGhlIGFtb3VudCBvZiBtaXNzaW5nIGRhdGEgaXMgcmVsYXRpdmVseSBzbWFsbCAobGVzcyB0aGFuIDUlIG9mIHRoZSBkYXRhc2V0KSwgSSBkZWNpZGUgdG8gZGVsZXRlIHJvd3Mgd2l0aCBtaXNzaW5nIHZhbHVlcyBieSB1c2luZyBuYS5vbWl0IGZ1bmN0aW9uLg0KVG8gc2NhbiBmb3IgaW5jb25zaXN0ZW5jaWVzIGFuZCBvYnZpb3VzIGVycm9ycywgSSBjcmVhdGVkIGEgdGV4dCB1bmRlciB0aGUgbmFtZSBvZiAiUnVsZS50eHQiLiBJJ3ZlIHNldCBzb21lIHJ1bGVzIGluIHRoaXMgdGV4dCBhbmQgc2Nhbm5lZCB0aGUgZGF0YSB1c2luZyB2aW9sYXRlZEVkaXRzIGZ1bmN0aW9uLg0KQWNjb3JkaW5nIHRvIHRoZSBvdXRwdXQsIG5pbCBpbmNvbnNpc3RlbmNpZXMgZm91bmQuDQoNCmBgYHtyLCBlY2hvPVRSVUUsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9DQpzdW0oaXMubmEoZnVsbDIpKQ0KY29sU3Vtcyhpcy5uYShmdWxsMikpDQpmdWxsMyA8LSBuYS5vbWl0KGZ1bGwyKQ0KDQpSdWxlIDwtIGVkaXRmaWxlKCJSdWxlLnR4dCIsIHR5cGUgPSAiYWxsIikNClJ1bGUNCnZpb2xhdGVkIDwtIHZpb2xhdGVkRWRpdHMoUnVsZSwgZnVsbDMpDQpzdW1tYXJ5KHZpb2xhdGVkKQ0KYGBgDQoNCg0KIyMJU2NhbiBJSQ0KVGhlcmUgYXJlIDE1IG51bWVyaWMgdmFyaWFibGVzIGluIHRoaXMgZGF0YXNldC4gTXVsdGl2YXJpYXRlIE91dGxpZXIgRGV0ZWN0aW9uIE1ldGhvZHMgY2FuIGJlIHVzZWQgdG8gZGV0ZWN0IG91dGxpZXJzLiBBZnRlciBkb2luZyBzb21lIHNpbXBsZSBwbG90aW5nIHN1Y2ggYXMgYm94IHBsb3QgdG8gdW5kZXJzdGFuZCB0aGUgdmFyaWFibGVzLCBJIGRlY2lkZWQgdG8gc2VwYXJhdGUgIkVtcGxveWVlQ291bnQiIGFuZCAiU3RhbmRhcmQgSG91cnMiIGZyb20gbXkgTXVsdGl2YXJpYXRlIE91dGxpZXIgRGV0ZWN0aW9uLiBUaGlzIGlzIGJlY2F1c2UgdGhlc2UgdHdvIHZhcmlhYmxlcyBoYXZlIG9ubHkgb25lIHZhbHVlLiAoc2VlIHRoZSBib3hwbG90IGJlbG93KQ0KDQpUaGUgTWFoYWxhbm9iaXMgZGlzdGFuY2UgaXMgdXNlZCB0byBkZXRlY3Qgb3V0bGllcnMgZm9yIHRoZSBtdWx0aXZhcmlhdGUgc2V0dGluZy4gIkVkdWNhdGlvbiIgaXMgdXNlZCBhcyBhIGNhdGVnb3JpY2FsIHZhcmlhYmxlIHRvIHN1YnNldCB0aGUgZGF0YS4gQWNjb3JkaW5nIHRvIHRoZSBvdXRwdXQsIHRoZXJlIGFyZSAxOSBvdXRsaWVycyBhbW9uZyBlbXBsb3llZXMgd2l0aCBhIG1hc3RlcidzIGRlZ3JlZSwgOCBvdXRsaWVycyBhbW9uZyBlbXBsb3llZXMgd2l0aCBiZWxvdyBDb2xsZWdlIGRlZ3JlZSwgOSBvdXRsaWVycyBhbW9uZyBlbXBsb3llZXMgd2l0aCBhIENvbGxlZ2UgZGVncmVlLCAxMTUgb3V0bGllcnMgYW1vbmcgZW1wbG95ZWVzIHdpdGggYSBiYWNoZWxvcidzIGRlZ3JlZSBhbmQgMyBvdXRsaWVycyBhbW9uZyBlbXBsb3llZXMgd2l0aCBhIGRvY3RvcidzIGRlZ3JlZS4NCg0KQmVjYXVzZSB0aGlzIGRhdGFzZXQgaXMgYWJvdXQgdGhlIGluZm9ybWF0aW9uIG9mIGVtcGxveWVlcyBmcm9tIFhZWiBjb21wYW55LCB0aGUgb3V0bGllcnMgYXJlIG1vc3QgbGlrZWx5IGR1ZSB0byBkYXRhIGVudHJ5IGVycm9yLiBQbHVzLCB0aGUgb3V0bGllciBvYnNlcnZhdGlvbnMgYXJlIHJlbGF0aXZlbHkgc21hbGwgaW4gbnVtYmVycy4gSSBkZWNpZGUgdG8gZGVsZXRlIHRoZSBvdXRsaWVycyBpbiB0aGlzIGNhZXMuIFRoZSBtdm4oKSBmdW5jdGlvbiBjYW4gaWRlbnRpZnkgdGhlIGxvY2F0aW9ucyBvZiBvdXRsaWVycyBhbmQgc2hvdyB0aGUgbmV3IGRhdGEgd2l0aG91dCBvdXRsaWVycy4NCkkgaGF2ZSBtYWRlIHRoZSBuZXcgZGF0YSB3aXRob3V0IG91dGxpZXJzIHNob3duIGluIHRoZSBvdXRwdXQuDQoNCldoaWxlIHRoZXNlIG91dGxpZXJzIGFyZSByZW1vdmVkLCBpdCBzaG91bGQgYmUgbm90ZWQgdGhhdCB0aGVzZXMgY291bGQgYmUgcmVsZXZhbnQgdG8gdGhlIGZpbmFsIGFuYWx5c2lzLiBBcyB0aGUgcHVycG9zZSBvZiB0aGlzIGRhdGEgaXMgdG8gaWRlbnRpZnkgcmVhc29ucyB0aGF0IGVtcGxveWVlcyBhcmUgbGVhdmluZyB0aGUgY29tcGFueSwgdGhlc2Ugb3V0bGllcnMgY291bGQgYmUgdmFsdWVhYmxlLiBJIHdvdWxkIHJlY29tbWVuZCB0aGVzZSBvdXRsaWVycyBiZSBpbnZlc3RpZ2F0ZWQgZnVydGhlciBhbmQgcmVhZGRlZCB0byB0aGUgZGF0YXNldCBpZiB0aGV5IGFyZSBkZWVtZWQgYWNjdXJhdGUuDQoNCmBgYHtyLCBlY2hvPVRSVUUsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9DQpib3hwbG90KGZ1bGwzJE1vbnRobHlJbmNvbWUgfiBmdWxsMyRFZHVjYXRpb24sIG1haW49Ik1vbnRobHkgSW5jb21lIGJ5IEVkdWNhdGlvbiIsIHlsYWIgPSAiTW9udGhseSBJbmNvbWUiLCB4bGFiID0gIkVkdWNhdGlvbiIpDQpib3hwbG90KGZ1bGwzJEVtcGxveWVlQ291bnQsIG1haW4gPSAiRW1wbG95ZWUgQ291bnQiKQ0KYm94cGxvdChmdWxsMyRTdGFuZGFyZEhvdXJzLCBtYWluID0gIlN0YW5kYXJkIEhvdXJzIikNCg0KTWFzdGVyIDwtIGZ1bGwzICU+JSBmaWx0ZXIoRWR1Y2F0aW9uID09ICJNYXN0ZXIiKSAlPiUgc2VsZWN0KERpc3RhbmNlRnJvbUhvbWUsIE51bUNvbXBhbmllc1dvcmtlZCwgUGVyY2VudFNhbGFyeUhpa2UsIFRvdGFsV29ya2luZ1llYXJzLCBUcmFpbmluZ1RpbWVzTGFzdFllYXIsIFllYXJzQXRDb21wYW55LCBZZWFyc1dpdGhDdXJyTWFuYWdlciwgQWdlLCBNb250aGx5SW5jb21lLCBZZWFyc1NpbmNlTGFzdFByb21vdGlvbiwgZW1wbG95X2FnZSwgcHJldmlvdXNfd29ya3llYXJzLCBFbXBsb3llZUlEKQ0KcmVzdWx0czEgPC0gbXZuKGRhdGEgPSBNYXN0ZXIsIG11bHRpdmFyaWF0ZU91dGxpZXJNZXRob2QgPSAicXVhbiIsIHNob3dPdXRsaWVycyA9IFRSVUUsIHNob3dOZXdEYXRhID0gVFJVRSApDQpyZXN1bHRzMSRtdWx0aXZhcmlhdGVPdXRsaWVycw0KcmVzdWx0czEkbmV3RGF0YQ0KDQpCZWxfQ29sbGVnZSA8LSBmdWxsMyAlPiUgZmlsdGVyKEVkdWNhdGlvbiA9PSAiQmVsb3cgQ29sbGVnZSIpICU+JSBzZWxlY3QoRGlzdGFuY2VGcm9tSG9tZSwgTnVtQ29tcGFuaWVzV29ya2VkLCBQZXJjZW50U2FsYXJ5SGlrZSwgVG90YWxXb3JraW5nWWVhcnMsIFRyYWluaW5nVGltZXNMYXN0WWVhciwgWWVhcnNBdENvbXBhbnksIFllYXJzV2l0aEN1cnJNYW5hZ2VyLCBBZ2UsIE1vbnRobHlJbmNvbWUsIFllYXJzU2luY2VMYXN0UHJvbW90aW9uLCBlbXBsb3lfYWdlLCBwcmV2aW91c193b3JreWVhcnMsIEVtcGxveWVlSUQpDQpyZXN1bHRzMiA8LSBtdm4oZGF0YSA9IEJlbF9Db2xsZWdlLCBtdWx0aXZhcmlhdGVPdXRsaWVyTWV0aG9kID0gInF1YW4iLCBzaG93T3V0bGllcnMgPSBUUlVFLCBzaG93TmV3RGF0YSA9IFRSVUUgKQ0KcmVzdWx0czIkbmV3RGF0YQ0KDQpDb2xsZWdlIDwtIGZ1bGwzICU+JSBmaWx0ZXIoRWR1Y2F0aW9uID09ICJDb2xsZWdlIikgJT4lIHNlbGVjdChEaXN0YW5jZUZyb21Ib21lLCBOdW1Db21wYW5pZXNXb3JrZWQsIFBlcmNlbnRTYWxhcnlIaWtlLCBUb3RhbFdvcmtpbmdZZWFycywgVHJhaW5pbmdUaW1lc0xhc3RZZWFyLCBZZWFyc0F0Q29tcGFueSwgWWVhcnNXaXRoQ3Vyck1hbmFnZXIsIEFnZSwgTW9udGhseUluY29tZSwgWWVhcnNTaW5jZUxhc3RQcm9tb3Rpb24sIGVtcGxveV9hZ2UsIHByZXZpb3VzX3dvcmt5ZWFycywgRW1wbG95ZWVJRCkNCnJlc3VsdHMzIDwtIG12bihkYXRhID0gQ29sbGVnZSwgbXVsdGl2YXJpYXRlT3V0bGllck1ldGhvZCA9ICJxdWFuIiwgc2hvd091dGxpZXJzID0gVFJVRSwgc2hvd05ld0RhdGEgPSBUUlVFICkNCnJlc3VsdHMzJG5ld0RhdGENCg0KQmFjaGVsb3IgPC0gZnVsbDMgJT4lIGZpbHRlcihFZHVjYXRpb24gPT0gIkJhY2hlbG9yIikgJT4lIHNlbGVjdChEaXN0YW5jZUZyb21Ib21lLCBOdW1Db21wYW5pZXNXb3JrZWQsIFBlcmNlbnRTYWxhcnlIaWtlLCBUb3RhbFdvcmtpbmdZZWFycywgVHJhaW5pbmdUaW1lc0xhc3RZZWFyLCBZZWFyc0F0Q29tcGFueSwgWWVhcnNXaXRoQ3Vyck1hbmFnZXIsIEFnZSwgTW9udGhseUluY29tZSwgWWVhcnNTaW5jZUxhc3RQcm9tb3Rpb24sIGVtcGxveV9hZ2UsIHByZXZpb3VzX3dvcmt5ZWFycywgRW1wbG95ZWVJRCkNCnJlc3VsdHM0IDwtIG12bihkYXRhID0gQmFjaGVsb3IsIG11bHRpdmFyaWF0ZU91dGxpZXJNZXRob2QgPSAicXVhbiIsIHNob3dPdXRsaWVycyA9IFRSVUUsIHNob3dOZXdEYXRhID0gVFJVRSApDQpyZXN1bHRzNCRuZXdEYXRhDQoNCkRvY3RvciA8LSBmdWxsMyAlPiUgZmlsdGVyKEVkdWNhdGlvbiA9PSAiRG9jdG9yIikgJT4lIHNlbGVjdChEaXN0YW5jZUZyb21Ib21lLCBOdW1Db21wYW5pZXNXb3JrZWQsIFBlcmNlbnRTYWxhcnlIaWtlLCBUb3RhbFdvcmtpbmdZZWFycywgVHJhaW5pbmdUaW1lc0xhc3RZZWFyLCBZZWFyc0F0Q29tcGFueSwgWWVhcnNXaXRoQ3Vyck1hbmFnZXIsIEFnZSwgTW9udGhseUluY29tZSwgWWVhcnNTaW5jZUxhc3RQcm9tb3Rpb24sIGVtcGxveV9hZ2UsIHByZXZpb3VzX3dvcmt5ZWFycywgRW1wbG95ZWVJRCkNCnJlc3VsdHM1IDwtIG12bihkYXRhID0gRG9jdG9yLCBtdWx0aXZhcmlhdGVPdXRsaWVyTWV0aG9kID0gInF1YW4iLCBzaG93T3V0bGllcnMgPSBUUlVFLCBzaG93TmV3RGF0YSA9IFRSVUUgKQ0KcmVzdWx0czUkbmV3RGF0YQ0KDQpmdWxsNCA8LSBiaW5kX3Jvd3MocmVzdWx0czEkbmV3RGF0YSwgcmVzdWx0czIkbmV3RGF0YSwgcmVzdWx0czMkbmV3RGF0YSwgcmVzdWx0czQkbmV3RGF0YSwgcmVzdWx0czUkbmV3RGF0YSkNCmBgYA0KDQoNCiMjCVRyYW5zZm9ybSANCkkgdXNlZCB0aGUgZnVsbDQgZGF0YXNldCBhZnRlciBkZWFsaW5nIHdpdGggbWlzc2luZyB2YWx1ZXMgYW5kIG91dGxpZXJzLiBUaGUgdmFyaWFibGUgSSBjaG9vc2UgdG8gdHJhbnNmb3JtIGlzICJNb250aGx5SW5jb21lIi4gVGhlIGhpc3RvZ3JhbSBvZiBtb250aGx5IGluY29tZSBpcyByaWdodC1za2V3ZWQgYWNjb3JkaW5nIHRvIHRoZSBvdXRwdXQuIEluIG9yZGVyIHRvIGRlY3JlYXNlIHRoZSBza2V3bmVzcyBhbmQgY29udmVydCB0aGUgZGlzdHJpYnV0aW9uIGludG8gYSBub3JtYWwgZGlzdHJpYnV0aW9uLCAzIHRyYW5zZm9ybWF0aW9uIHRlY2hpbmlxdWVzIGFyZSB1c2VkLg0KDQoqIExvZ2FyaXRobWljIHRyYW5zZm9ybWF0aW9uIChiYXNlIDEwKSANCg0KKiBOYXR1cmFsIGxvZ2FyaXRobWljIHRyYW5zZm9ybWF0aW9uDQoNCiogQ3ViZSByb290IHRyYW5zZm9ybWF0aW9uDQoNCkZyb20gdGhlIHJlc3VsdHMgaXQgaXMgb2J2aW91cyB0byBzZWUgdGhhdCBMb2dhcml0aG1pYyB0cmFuc2Zvcm1hdGlvbiAoYmFzZSAxMCkgYW5kIG5hdHVyYWwgbG9ncml0aG1pYyB0cmFuc2Zvcm1hdGlvbiB3b3JrIHNsaWdodGx5IGJldHRlciB0aGFuIHRoZSBjdWJlIHJvb3QgdHJhbnNmb3JtYXRpb24uDQpgYGB7ciwgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQ0KaGlzdChmdWxsNCRNb250aGx5SW5jb21lKQ0KbG9nX2luY29tZSA8LSBsb2cxMChmdWxsNCRNb250aGx5SW5jb21lKQ0KaGlzdChsb2dfaW5jb21lKQ0KbG5faW5jb21lIDwtIGxvZyhmdWxsNCRNb250aGx5SW5jb21lKQ0KaGlzdChsbl9pbmNvbWUpDQpjdWJlcm9vdF9pbmNvbWUgPC0gKGZ1bGw0JE1vbnRobHlJbmNvbWUpXigxLzMpDQpoaXN0KGN1YmVyb290X2luY29tZSkNCmBgYA0KDQoNCg0KPGJyPg0KPGJyPg0K