Required packages
library(readr) # Useful for reading data
library(car) # Useful to qqplot
library(dplyr) # Useful for data maipulation
library(tidyr) # Useful for tidying data
library(Hmisc) # Useful for utility operation
library(ggplot2) # Useful for creating plots
library(outliers) # Useful to handle outliers
library(knitr) # Useful for creating nice tables
library(lubridate) # Useful for date/time
library(forecast) # Userful for analysing time series data
library(naniar) # Userful for Handling data
rm(list=ls()) # Cleaning environment
# getwd()
setwd("D:/Studies/MS/Sem1/Data Preprocessing (MATH2349)/Assignment/Assignment3") # Set userdirectory
Executive Summary
Aim of the assignment is, collecting two different datasets, merging, understanding and analysiing dataset, perform data processing concepts like correct datatype conversion. Further checking tidy principles on dataset and coverting data into tidy form using various method if it doesn’t satisfy. Next is to scan and handle missing values and outliers, And exclude or replace with appropriate values if it is necessary to do which helps to nearly accurate analysis. After applying tidy principle and handling inapproriate values, check for normality of data and applying any trasnformation to convert into normalized form. These task will help us to prepare data for any statisitcal analysis or reporting.
Data
We are using demographic and population health data(around 3141 US counties) to perform data preprocessing task.But we will focus on county data to perform task.
Datasource: https://data.world/data-society/health-status-indicators https://catalog.data.gov/dataset/community-health-status-indicators-chsi-to-combat-obesity-heart-disease-and-cancer#sec-dates
Dataset1: Demographic contains county/population count by age group and total Dataset2: Vulnerable population data by various category like unemployed, depression, Drug user for particular time span(2001-2003, 1999-2003, 1994-2003)
Both dataset has State_FIPS_Code, Country_FIPS_Code which helps to identify state and county developed by National Bureau of Standards. Some measure column contains any of these values -9999, -2222, -222, -2 which represent NA/Not available/ No details. All measures are calculated based on average.
dataset1 <- read.csv("demographic.csv",stringsAsFactors = FALSE)
dataset2 <- read.csv("vulnerablepopulation.csv",stringsAsFactors = FALSE)
Understand
Dataset1 (Dempographic Population by Age) :
- Snapshot and data strcture of demographic data
# Data Structure of dataset1
dataset_1 <- dataset1 %>% select(-c("CHSI_State_Name", "Strata_ID_Number"))
head(dataset_1)
str(dataset_1)
'data.frame': 3141 obs. of 8 variables:
$ State_FIPS_Code : int 1 1 1 1 1 1 1 1 1 1 ...
$ County_FIPS_Code: int 1 3 5 7 9 11 13 15 17 19 ...
$ CHSI_County_Name: chr "Autauga" "Baldwin" "Barbour" "Bibb" ...
$ CHSI_State_Abbr : chr "AL" "AL" "AL" "AL" ...
$ Age_19_Under : num 26.9 23.5 24.3 24.6 24.5 24.7 25.6 24.1 24.8 21.9 ...
$ Age_19_64 : num 62.3 60.3 62.5 63.3 62.1 63.2 58.5 61.6 59.5 61.4 ...
$ Age_65_84 : num 9.8 14.5 11.6 10.9 12.1 10 13.6 12.7 13.5 15.2 ...
$ Age_85_and_Over : num 0.9 1.8 1.6 1.2 1.3 2.2 2.4 1.5 2.2 1.4 ...
- Total Observations: 3141, TOtal Variables: 8
- State_FIPS_Code : int(2) State Code
- County_FIPS_Code : int(2) County Code
- CHSI_County_Name : chr Country Name
- CHSI_State_Abbr : chr County Abbreviation
- Age_19_Under : num population count with age < 19
- Age_19_64 : num population count with age >= 19 and <=64
- Age_65_84 : num population count with age >= 65 and <=84
- Age_85_and_Over : num population count with age >= 85
Dataset2 (Vulnerable Population) :
- Snapshot and data structure of demographic data
# Data Structure of dataset1
dataset_2 <- dataset2 %>% select(-c("CHSI_State_Name", "Strata_ID_Number"))
head(dataset_2)
str(dataset_2)
'data.frame': 3141 obs. of 10 variables:
$ State_FIPS_Code : int 1 1 1 1 1 1 1 1 1 1 ...
$ County_FIPS_Code : int 1 3 5 7 9 11 13 15 17 19 ...
$ CHSI_County_Name : chr "Autauga" "Baldwin" "Barbour" "Bibb" ...
$ CHSI_State_Abbr : chr "AL" "AL" "AL" "AL" ...
$ No_HS_Diploma : int 6690 20254 6729 5355 11181 2848 4363 19546 8718 6398 ...
$ Unemployed : int 774 2533 569 358 819 327 537 2182 849 464 ...
$ Sev_Work_Disabled: int 1727 4933 1302 900 2217 448 976 5722 1470 1154 ...
$ Major_Depression : int 2680 9354 1618 1218 3164 626 1164 6400 2005 1436 ...
$ Recent_Drug_Use : int 2394 7753 1403 1034 2675 565 1029 5545 1647 1140 ...
$ EH_Time_Span : chr "1999-2003" "2001-2003" "1999-2003" "1994-2003" ...
- Total Observations: 3141, TOtal Variables: 10
- State_FIPS_Code : int(2) State Code
- County_FIPS_Code : int(2) County Code
- CHSI_County_Name : chr Country Name
- CHSI_State_Abbr : chr County Abbreviation
- No_HS_Diploma : int No high school diploma
- Unemployed : int unemployed count
- Sev_Work_Disabled : int severely work disabled
- Major_Depression : int major depression
- Recent_Drug_Use : int recent drug users(last month)
- EH_Time_Span : chr time span
Joining datasets
- Merging both dataset by State_FIPS_Code, County_FIPS_Code and create combine dataset
- Snapshot of new dataset
# Joining dataset1 with dataset2 based in State_FIPS_Code
dataset <- left_join(dataset1, dataset2,by=c("State_FIPS_Code","County_FIPS_Code"))
head(dataset)
- data strcture of new dataset
str(dataset)
'data.frame': 3141 obs. of 20 variables:
$ State_FIPS_Code : int 1 1 1 1 1 1 1 1 1 1 ...
$ County_FIPS_Code : int 1 3 5 7 9 11 13 15 17 19 ...
$ CHSI_County_Name.x: chr "Autauga" "Baldwin" "Barbour" "Bibb" ...
$ CHSI_State_Name.x : chr "Alabama" "Alabama" "Alabama" "Alabama" ...
$ CHSI_State_Abbr.x : chr "AL" "AL" "AL" "AL" ...
$ Strata_ID_Number.x: int 29 16 51 42 28 75 76 6 50 64 ...
$ Age_19_Under : num 26.9 23.5 24.3 24.6 24.5 24.7 25.6 24.1 24.8 21.9 ...
$ Age_19_64 : num 62.3 60.3 62.5 63.3 62.1 63.2 58.5 61.6 59.5 61.4 ...
$ Age_65_84 : num 9.8 14.5 11.6 10.9 12.1 10 13.6 12.7 13.5 15.2 ...
$ Age_85_and_Over : num 0.9 1.8 1.6 1.2 1.3 2.2 2.4 1.5 2.2 1.4 ...
$ CHSI_County_Name.y: chr "Autauga" "Baldwin" "Barbour" "Bibb" ...
$ CHSI_State_Name.y : chr "Alabama" "Alabama" "Alabama" "Alabama" ...
$ CHSI_State_Abbr.y : chr "AL" "AL" "AL" "AL" ...
$ Strata_ID_Number.y: int 29 16 51 42 28 75 76 6 50 64 ...
$ No_HS_Diploma : int 6690 20254 6729 5355 11181 2848 4363 19546 8718 6398 ...
$ Unemployed : int 774 2533 569 358 819 327 537 2182 849 464 ...
$ Sev_Work_Disabled : int 1727 4933 1302 900 2217 448 976 5722 1470 1154 ...
$ Major_Depression : int 2680 9354 1618 1218 3164 626 1164 6400 2005 1436 ...
$ Recent_Drug_Use : int 2394 7753 1403 1034 2675 565 1029 5545 1647 1140 ...
$ EH_Time_Span : chr "1999-2003" "2001-2003" "1999-2003" "1994-2003" ...
- Variable suffix with .x belongs to dataset1 and with .y belongs to dataset2.
Tidy & Manipulate Data I
- Current dataset contains Age category as variables which we need to reshape.
- Also Vulnerable Category as variables which also needs to be reshape
# reshape age category
dataset_t1 <- dataset %>% gather(c("Age_19_Under","Age_19_64","Age_65_84","Age_85_and_Over"),key="Age",value="Age_Population")
# reshape Vulnerable Category
dataset_t2 <- dataset_t1 %>% gather(c("No_HS_Diploma","Unemployed","Sev_Work_Disabled","Major_Depression","Recent_Drug_Use"),key="Vulnerable_Category", value="Vulnerable_Population")
str(dataset_t2)
'data.frame': 62820 obs. of 15 variables:
$ State_FIPS_Code : int 1 1 1 1 1 1 1 1 1 1 ...
$ County_FIPS_Code : int 1 3 5 7 9 11 13 15 17 19 ...
$ CHSI_County_Name.x : chr "Autauga" "Baldwin" "Barbour" "Bibb" ...
$ CHSI_State_Name.x : chr "Alabama" "Alabama" "Alabama" "Alabama" ...
$ CHSI_State_Abbr.x : chr "AL" "AL" "AL" "AL" ...
$ Strata_ID_Number.x : int 29 16 51 42 28 75 76 6 50 64 ...
$ CHSI_County_Name.y : chr "Autauga" "Baldwin" "Barbour" "Bibb" ...
$ CHSI_State_Name.y : chr "Alabama" "Alabama" "Alabama" "Alabama" ...
$ CHSI_State_Abbr.y : chr "AL" "AL" "AL" "AL" ...
$ Strata_ID_Number.y : int 29 16 51 42 28 75 76 6 50 64 ...
$ EH_Time_Span : chr "1999-2003" "2001-2003" "1999-2003" "1994-2003" ...
$ Age : chr "Age_19_Under" "Age_19_Under" "Age_19_Under" "Age_19_Under" ...
$ Age_Population : num 26.9 23.5 24.3 24.6 24.5 24.7 25.6 24.1 24.8 21.9 ...
$ Vulnerable_Category : chr "No_HS_Diploma" "No_HS_Diploma" "No_HS_Diploma" "No_HS_Diploma" ...
$ Vulnerable_Population: int 6690 20254 6729 5355 11181 2848 4363 19546 8718 6398 ...
- Factoring Age and Vulnerable category
# Factoring Age and Vulnerable Category
Age_cat <- c("Age_19_Under","Age_19_64","Age_65_84","Age_85_and_Over")
Age_Cat_labels <- c("<19","19-64","65-84","85+")
vulnerable_cat <- c("No_HS_Diploma","Unemployed","Sev_Work_Disabled","Major_Depression","Recent_Drug_Use")
dataset_t2 <- mutate(dataset_t2
,Age_Cat=factor(Age,levels=Age_cat, labels=Age_Cat_labels,ordered = TRUE)
,Vulnerable_Cat=factor(Vulnerable_Category,levels=vulnerable_cat)
) %>% select(-c("Age","Vulnerable_Category"))
- Summary of Age Categorywise Population
summary(dataset_t2$Age_Population)
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.100 5.175 19.600 25.000 47.300 83.300
- Summary of Vulnerable Population
summary(dataset_t2$Vulnerable_Population)
Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
1 500 1298 5646 3881 1872316 148
Tidy & Manipulate Data II
- Dataset has EH_Time_Span which contains 2 years(separated by “-”) start and end year of reporting data. So, separate will create two new variable(“Start_Year”,“End_Year”).
dataset_t3 <- dataset_t2 %>% separate(EH_Time_Span, into=c("Start_Year","End_Year"),sep="-")
str(dataset_t3)
'data.frame': 62820 obs. of 16 variables:
$ State_FIPS_Code : int 1 1 1 1 1 1 1 1 1 1 ...
$ County_FIPS_Code : int 1 3 5 7 9 11 13 15 17 19 ...
$ CHSI_County_Name.x : chr "Autauga" "Baldwin" "Barbour" "Bibb" ...
$ CHSI_State_Name.x : chr "Alabama" "Alabama" "Alabama" "Alabama" ...
$ CHSI_State_Abbr.x : chr "AL" "AL" "AL" "AL" ...
$ Strata_ID_Number.x : int 29 16 51 42 28 75 76 6 50 64 ...
$ CHSI_County_Name.y : chr "Autauga" "Baldwin" "Barbour" "Bibb" ...
$ CHSI_State_Name.y : chr "Alabama" "Alabama" "Alabama" "Alabama" ...
$ CHSI_State_Abbr.y : chr "AL" "AL" "AL" "AL" ...
$ Strata_ID_Number.y : int 29 16 51 42 28 75 76 6 50 64 ...
$ Start_Year : chr "1999" "2001" "1999" "1994" ...
$ End_Year : chr "2003" "2003" "2003" "2003" ...
$ Age_Population : num 26.9 23.5 24.3 24.6 24.5 24.7 25.6 24.1 24.8 21.9 ...
$ Vulnerable_Population: int 6690 20254 6729 5355 11181 2848 4363 19546 8718 6398 ...
$ Age_Cat : Ord.factor w/ 4 levels "<19"<"19-64"<..: 1 1 1 1 1 1 1 1 1 1 ...
$ Vulnerable_Cat : Factor w/ 5 levels "No_HS_Diploma",..: 1 1 1 1 1 1 1 1 1 1 ...
Scan I
- Data contains values like -9999, -2222, -222, -2 which represent NA/Not available/ No details.
- First step, find missing values by variables
# Finding Missing values
colSums(is.na(dataset_t3))
State_FIPS_Code County_FIPS_Code CHSI_County_Name.x CHSI_State_Name.x
0 0 0 0
CHSI_State_Abbr.x Strata_ID_Number.x CHSI_County_Name.y CHSI_State_Name.y
0 0 0 0
CHSI_State_Abbr.y Strata_ID_Number.y Start_Year End_Year
0 0 0 0
Age_Population Vulnerable_Population Age_Cat Vulnerable_Cat
0 148 0 0
# total missing values
sum(is.na(dataset_t3))
[1] 148
- Missing data is very less compare to total no of observation, so we can omit them. And Other reason is, If we replace the with mean or median of individual variable, it can lead to wrong result.
# Omitting missing values
dataset_t4 <- na.omit(dataset_t3)
sum(is.na(dataset_t4))
[1] 0
- Check for special values Inf,-Inf, NaN in Age_Population
# Total NaN values in Age_Population
sum(is.nan(dataset_t4$Age_Population))
[1] 0
# Total Inf/-Inf values in Age_Population
sum(is.infinite(dataset_t4$Age_Population))
[1] 0
- Check for special values Inf,-Inf, NaN in Vulnerable_Population
# Total NaN values in Vulnerable_Population
sum(is.nan(dataset_t4$Vulnerable_Population))
[1] 0
# Total Inf/-Inf values in Vulnerable_Population
sum(is.infinite(dataset_t4$Vulnerable_Population))
[1] 0
Scan II
- Scanning outliers for Age_Population and Vulnerable_Population using z-score>3 and boxplot method.
# Z-Score for Age Population
z.scores_age <- dataset_t4$Age_Population %>% scores(type="z")
length (which( abs(z.scores_age) >3 ))
[1] 0
- Age Population has no value which has z.score>3. It means Age population has no outliers.
# Boxplot for Age Population
boxplot(dataset_t4$Age_Population, main = "BoxPlot of Age Population", ylab = "Population")

- By studying z.score and boxplot, we can conculde Age_Population has no outliers.
# Z-Score for Vulnerable Population
z.scores_vul <- dataset_t4$Vulnerable_Population %>% scores(type="z")
length (which( abs(z.scores_vul) >3 ))
[1] 448
- Identify outliers by boxplot
# Boxplot for Vulnerable Population
boxplot(dataset_t4$Vulnerable_Population, main = "BoxPlot of Vulnerable Population", ylab = "Population")

- As per z.score and boxplot, Vulnerable Population has ~448 outlier. We can handle those using capping function and result will not have much affect.
# Capping function to replace the outlier value with 1st and 3rd Quantilies.
cap <- function(x){quantiles <- quantile(x,c(0.05,0.25,0.75,0.95))
x[x < quantiles[2] - 1.5*IQR(x)] <- quantiles[1]
x[x > quantiles[3] + 1.5*IQR(x)] <- quantiles[4]
x
}
# Replace Outlier values using capping function
dataset_t4$Vulnerable_Population[is.na(dataset_t4$Vulnerable_Population) != TRUE] <-
dataset_t4$Vulnerable_Population[is.na(dataset_t4$Vulnerable_Population) != TRUE] %>% cap()
# After capping boxplot
boxplot(dataset_t4$Vulnerable_Population, main = "BoxPlot of Vulnerable Population", ylab = "Population")

- After capping of outlier, z.score check for Vulnerable_Population and count Z.Score>3 is zero. it means Vulnerable_Population has no outliers.
# Z-Score for Vulnerable Population
z.scores_vul <- dataset_t4$Vulnerable_Population %>% scores(type="z")
length (which( abs(z.scores_vul) >3 ))
[1] 0
LS0tDQp0aXRsZTogIk1BVEgyMzQ5IFNlbWVzdGVyIDIsIDIwMTkiDQphdXRob3I6IFJhdmlrdW1hciBCYWxhciAoUzM3OTgwOTgpIHwgRHJhc2h0aSBNYW5peWEgKFMzNzQ4OTQ0KSB8IEFqYXlrdW1hciBLb3RoaXlhIChTMzc5MzY2MSkNCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdA0KICBodG1sX2RvY3VtZW50Og0KICAgIGRmX3ByaW50OiBwYWdlZA0Kc3VidGl0bGU6IEFzc2lnbm1lbnQgMw0KLS0tDQoNCiMjIFJlcXVpcmVkIHBhY2thZ2VzIA0KDQpgYGB7ciwgZWNobz1UUlVFLHdhcm5pbmc9RkFMU0UsbWVzc2FnZT1GQUxTRX0NCmxpYnJhcnkocmVhZHIpICMgVXNlZnVsIGZvciByZWFkaW5nIGRhdGENCmxpYnJhcnkoY2FyKSAjIFVzZWZ1bCB0byBxcXBsb3QNCmxpYnJhcnkoZHBseXIpICAjIFVzZWZ1bCBmb3IgZGF0YSBtYWlwdWxhdGlvbg0KbGlicmFyeSh0aWR5cikgIyBVc2VmdWwgZm9yIHRpZHlpbmcgZGF0YQ0KbGlicmFyeShIbWlzYykgIyBVc2VmdWwgZm9yIHV0aWxpdHkgb3BlcmF0aW9uDQpsaWJyYXJ5KGdncGxvdDIpICMgVXNlZnVsIGZvciBjcmVhdGluZyBwbG90cw0KbGlicmFyeShvdXRsaWVycykgIyBVc2VmdWwgdG8gaGFuZGxlIG91dGxpZXJzDQpsaWJyYXJ5KGtuaXRyKSAjIFVzZWZ1bCBmb3IgY3JlYXRpbmcgbmljZSB0YWJsZXMNCmxpYnJhcnkobHVicmlkYXRlKSAjIFVzZWZ1bCBmb3IgZGF0ZS90aW1lDQpsaWJyYXJ5KGZvcmVjYXN0KSAjIFVzZXJmdWwgZm9yIGFuYWx5c2luZyB0aW1lIHNlcmllcyBkYXRhDQpsaWJyYXJ5KG5hbmlhcikgIyBVc2VyZnVsIGZvciBIYW5kbGluZyBkYXRhIA0Kcm0obGlzdD1scygpKSAjIENsZWFuaW5nIGVudmlyb25tZW50DQojIGdldHdkKCkNCnNldHdkKCJEOi9TdHVkaWVzL01TL1NlbTEvRGF0YSBQcmVwcm9jZXNzaW5nIChNQVRIMjM0OSkvQXNzaWdubWVudC9Bc3NpZ25tZW50MyIpICMgU2V0IHVzZXJkaXJlY3RvcnkNCmBgYA0KDQoNCiMjIEV4ZWN1dGl2ZSBTdW1tYXJ5IA0KDQpBaW0gb2YgdGhlIGFzc2lnbm1lbnQgaXMsIGNvbGxlY3RpbmcgdHdvIGRpZmZlcmVudCBkYXRhc2V0cywgbWVyZ2luZywgdW5kZXJzdGFuZGluZyBhbmQgYW5hbHlzaWluZyBkYXRhc2V0LCBwZXJmb3JtIGRhdGEgcHJvY2Vzc2luZyBjb25jZXB0cyBsaWtlIGNvcnJlY3QgZGF0YXR5cGUgY29udmVyc2lvbi4gRnVydGhlciBjaGVja2luZyB0aWR5IHByaW5jaXBsZXMgb24gZGF0YXNldCBhbmQgY292ZXJ0aW5nIGRhdGEgaW50byB0aWR5IGZvcm0gdXNpbmcgdmFyaW91cyBtZXRob2QgaWYgaXQgZG9lc24ndCBzYXRpc2Z5LiBOZXh0IGlzIHRvIHNjYW4gYW5kIGhhbmRsZSBtaXNzaW5nIHZhbHVlcyBhbmQgb3V0bGllcnMsIEFuZCBleGNsdWRlIG9yIHJlcGxhY2Ugd2l0aCBhcHByb3ByaWF0ZSB2YWx1ZXMgaWYgaXQgaXMgbmVjZXNzYXJ5IHRvIGRvIHdoaWNoIGhlbHBzIHRvIG5lYXJseSBhY2N1cmF0ZSBhbmFseXNpcy4gQWZ0ZXIgYXBwbHlpbmcgdGlkeSBwcmluY2lwbGUgYW5kIGhhbmRsaW5nIGluYXBwcm9yaWF0ZSB2YWx1ZXMsIGNoZWNrIGZvciBub3JtYWxpdHkgb2YgZGF0YSBhbmQgYXBwbHlpbmcgYW55IHRyYXNuZm9ybWF0aW9uIHRvIGNvbnZlcnQgaW50byBub3JtYWxpemVkIGZvcm0uIFRoZXNlIHRhc2sgd2lsbCBoZWxwIHVzIHRvIHByZXBhcmUgZGF0YSBmb3IgYW55IHN0YXRpc2l0Y2FsIGFuYWx5c2lzIG9yIHJlcG9ydGluZy4NCg0KIyMgRGF0YSANCg0KV2UgYXJlIHVzaW5nIGRlbW9ncmFwaGljIGFuZCBwb3B1bGF0aW9uIGhlYWx0aCBkYXRhKGFyb3VuZCAzMTQxIFVTIGNvdW50aWVzKSB0byBwZXJmb3JtIGRhdGEgcHJlcHJvY2Vzc2luZyB0YXNrLkJ1dCB3ZSB3aWxsIGZvY3VzIG9uIGNvdW50eSBkYXRhIHRvIHBlcmZvcm0gdGFzay4NCg0KRGF0YXNvdXJjZTogaHR0cHM6Ly9kYXRhLndvcmxkL2RhdGEtc29jaWV0eS9oZWFsdGgtc3RhdHVzLWluZGljYXRvcnMNCmh0dHBzOi8vY2F0YWxvZy5kYXRhLmdvdi9kYXRhc2V0L2NvbW11bml0eS1oZWFsdGgtc3RhdHVzLWluZGljYXRvcnMtY2hzaS10by1jb21iYXQtb2Jlc2l0eS1oZWFydC1kaXNlYXNlLWFuZC1jYW5jZXIjc2VjLWRhdGVzDQoNCkRhdGFzZXQxOiBEZW1vZ3JhcGhpYyBjb250YWlucyBjb3VudHkvcG9wdWxhdGlvbiBjb3VudCBieSBhZ2UgZ3JvdXAgYW5kIHRvdGFsDQpEYXRhc2V0MjogVnVsbmVyYWJsZSBwb3B1bGF0aW9uIGRhdGEgYnkgdmFyaW91cyBjYXRlZ29yeSBsaWtlIHVuZW1wbG95ZWQsIGRlcHJlc3Npb24sIERydWcgdXNlciBmb3IgcGFydGljdWxhciB0aW1lIHNwYW4oMjAwMS0yMDAzLCAxOTk5LTIwMDMsIDE5OTQtMjAwMykNCg0KQm90aCBkYXRhc2V0IGhhcyBTdGF0ZV9GSVBTX0NvZGUsIENvdW50cnlfRklQU19Db2RlIHdoaWNoIGhlbHBzIHRvIGlkZW50aWZ5IHN0YXRlIGFuZCBjb3VudHkgZGV2ZWxvcGVkIGJ5IE5hdGlvbmFsIEJ1cmVhdSBvZiBTdGFuZGFyZHMuDQpTb21lIG1lYXN1cmUgY29sdW1uIGNvbnRhaW5zIGFueSBvZiB0aGVzZSB2YWx1ZXMgLTk5OTksIC0yMjIyLCAtMjIyLCAtMiB3aGljaCByZXByZXNlbnQgTkEvTm90IGF2YWlsYWJsZS8gTm8gZGV0YWlscy4gQWxsIG1lYXN1cmVzIGFyZSBjYWxjdWxhdGVkIGJhc2VkIG9uIGF2ZXJhZ2UuDQpgYGB7cn0NCmRhdGFzZXQxIDwtIHJlYWQuY3N2KCJkZW1vZ3JhcGhpYy5jc3YiLHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkNCmRhdGFzZXQyIDwtIHJlYWQuY3N2KCJ2dWxuZXJhYmxlcG9wdWxhdGlvbi5jc3YiLHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkNCmBgYA0KDQojIyBVbmRlcnN0YW5kIA0KDQojIyMgRGF0YXNldDEgKERlbXBvZ3JhcGhpYyBQb3B1bGF0aW9uIGJ5IEFnZSkgOg0KDQotIFNuYXBzaG90IGFuZCBkYXRhIHN0cmN0dXJlIG9mIGRlbW9ncmFwaGljIGRhdGENCmBgYHtyfQ0KIyBEYXRhIFN0cnVjdHVyZSBvZiBkYXRhc2V0MQ0KZGF0YXNldF8xIDwtIGRhdGFzZXQxICU+JSAgc2VsZWN0KC1jKCJDSFNJX1N0YXRlX05hbWUiLCAiU3RyYXRhX0lEX051bWJlciIpKQ0KaGVhZChkYXRhc2V0XzEpDQpgYGANCg0KYGBge3J9DQpzdHIoZGF0YXNldF8xKQ0KYGBgDQoNCi0gKlRvdGFsIE9ic2VydmF0aW9ucyo6IDMxNDEsIFRPdGFsIFZhcmlhYmxlczogOA0KLSAqU3RhdGVfRklQU19Db2RlKiAgIDogaW50KDIpIFN0YXRlIENvZGUNCi0gKkNvdW50eV9GSVBTX0NvZGUqICA6IGludCgyKSBDb3VudHkgQ29kZSANCi0gKkNIU0lfQ291bnR5X05hbWUqICA6IGNociBDb3VudHJ5IE5hbWUNCi0gKkNIU0lfU3RhdGVfQWJiciogICA6IGNociBDb3VudHkgQWJicmV2aWF0aW9uDQotICpBZ2VfMTlfVW5kZXIqICAgICAgOiBudW0gcG9wdWxhdGlvbiBjb3VudCB3aXRoIGFnZSA8IDE5DQotICpBZ2VfMTlfNjQqICAgICAgICAgOiBudW0gcG9wdWxhdGlvbiBjb3VudCB3aXRoIGFnZSA+PSAxOSBhbmQgPD02NA0KLSAqQWdlXzY1Xzg0KiAgICAgICAgIDogbnVtIHBvcHVsYXRpb24gY291bnQgd2l0aCBhZ2UgPj0gNjUgYW5kIDw9ODQgIA0KLSAqQWdlXzg1X2FuZF9PdmVyKiAgIDogbnVtIHBvcHVsYXRpb24gY291bnQgd2l0aCBhZ2UgPj0gODUNCg0KIyMjIERhdGFzZXQyIChWdWxuZXJhYmxlIFBvcHVsYXRpb24pIDoNCg0KLSBTbmFwc2hvdCBhbmQgZGF0YSBzdHJ1Y3R1cmUgb2YgZGVtb2dyYXBoaWMgZGF0YQ0KYGBge3J9DQojIERhdGEgU3RydWN0dXJlIG9mIGRhdGFzZXQxDQpkYXRhc2V0XzIgPC0gZGF0YXNldDIgJT4lICBzZWxlY3QoLWMoIkNIU0lfU3RhdGVfTmFtZSIsICJTdHJhdGFfSURfTnVtYmVyIikpDQpoZWFkKGRhdGFzZXRfMikNCmBgYA0KDQpgYGB7cn0NCnN0cihkYXRhc2V0XzIpDQpgYGANCg0KLSAqVG90YWwgT2JzZXJ2YXRpb25zKjogMzE0MSwgVE90YWwgVmFyaWFibGVzOiAxMA0KLSAqU3RhdGVfRklQU19Db2RlKiAgIDogaW50KDIpIFN0YXRlIENvZGUNCi0gKkNvdW50eV9GSVBTX0NvZGUqICA6IGludCgyKSBDb3VudHkgQ29kZSANCi0gKkNIU0lfQ291bnR5X05hbWUqICA6IGNociBDb3VudHJ5IE5hbWUNCi0gKkNIU0lfU3RhdGVfQWJiciogICA6IGNociBDb3VudHkgQWJicmV2aWF0aW9uDQotICpOb19IU19EaXBsb21hKiAgICAgOiBpbnQgTm8gaGlnaCBzY2hvb2wgZGlwbG9tYQ0KLSAqVW5lbXBsb3llZCogICAgICAgIDogaW50IHVuZW1wbG95ZWQgY291bnQNCi0gKlNldl9Xb3JrX0Rpc2FibGVkKiA6IGludCBzZXZlcmVseSB3b3JrIGRpc2FibGVkDQotICpNYWpvcl9EZXByZXNzaW9uKiAgOiBpbnQgbWFqb3IgZGVwcmVzc2lvbg0KLSAqUmVjZW50X0RydWdfVXNlKiAgIDogaW50IHJlY2VudCBkcnVnIHVzZXJzKGxhc3QgbW9udGgpDQotICpFSF9UaW1lX1NwYW4qICAgICAgOiBjaHIgdGltZSBzcGFuDQoNCiMjIyBKb2luaW5nIGRhdGFzZXRzDQoNCi0gTWVyZ2luZyBib3RoIGRhdGFzZXQgYnkgU3RhdGVfRklQU19Db2RlLCBDb3VudHlfRklQU19Db2RlIGFuZCBjcmVhdGUgY29tYmluZSBkYXRhc2V0DQotIFNuYXBzaG90IG9mIG5ldyBkYXRhc2V0DQpgYGB7cn0NCiMgSm9pbmluZyBkYXRhc2V0MSB3aXRoIGRhdGFzZXQyIGJhc2VkIGluIFN0YXRlX0ZJUFNfQ29kZQ0KZGF0YXNldCA8LSBsZWZ0X2pvaW4oZGF0YXNldDEsIGRhdGFzZXQyLGJ5PWMoIlN0YXRlX0ZJUFNfQ29kZSIsIkNvdW50eV9GSVBTX0NvZGUiKSkNCmhlYWQoZGF0YXNldCkNCmBgYA0KDQotIGRhdGEgc3RyY3R1cmUgb2YgbmV3IGRhdGFzZXQNCmBgYHtyfQ0Kc3RyKGRhdGFzZXQpDQpgYGANCg0KLSBWYXJpYWJsZSBzdWZmaXggd2l0aCAueCBiZWxvbmdzIHRvIGRhdGFzZXQxIGFuZCB3aXRoIC55IGJlbG9uZ3MgdG8gZGF0YXNldDIuDQoNCiMjCVRpZHkgJiBNYW5pcHVsYXRlIERhdGEgSSANCg0KLSBDdXJyZW50IGRhdGFzZXQgY29udGFpbnMgQWdlIGNhdGVnb3J5IGFzIHZhcmlhYmxlcyB3aGljaCB3ZSBuZWVkIHRvIHJlc2hhcGUuDQotIEFsc28gVnVsbmVyYWJsZSBDYXRlZ29yeSBhcyB2YXJpYWJsZXMgd2hpY2ggYWxzbyBuZWVkcyB0byBiZSByZXNoYXBlDQpgYGB7cn0NCiMgcmVzaGFwZSBhZ2UgY2F0ZWdvcnkNCmRhdGFzZXRfdDEgPC0gZGF0YXNldCAlPiUgZ2F0aGVyKGMoIkFnZV8xOV9VbmRlciIsIkFnZV8xOV82NCIsIkFnZV82NV84NCIsIkFnZV84NV9hbmRfT3ZlciIpLGtleT0iQWdlIix2YWx1ZT0iQWdlX1BvcHVsYXRpb24iKQ0KIyByZXNoYXBlIFZ1bG5lcmFibGUgQ2F0ZWdvcnkNCmRhdGFzZXRfdDIgPC0gZGF0YXNldF90MSAlPiUgIGdhdGhlcihjKCJOb19IU19EaXBsb21hIiwiVW5lbXBsb3llZCIsIlNldl9Xb3JrX0Rpc2FibGVkIiwiTWFqb3JfRGVwcmVzc2lvbiIsIlJlY2VudF9EcnVnX1VzZSIpLGtleT0iVnVsbmVyYWJsZV9DYXRlZ29yeSIsIHZhbHVlPSJWdWxuZXJhYmxlX1BvcHVsYXRpb24iKSANCnN0cihkYXRhc2V0X3QyKQ0KYGBgDQoNCi0gRmFjdG9yaW5nIEFnZSBhbmQgVnVsbmVyYWJsZSBjYXRlZ29yeSANCmBgYHtyfQ0KIyBGYWN0b3JpbmcgQWdlIGFuZCBWdWxuZXJhYmxlIENhdGVnb3J5DQpBZ2VfY2F0IDwtIGMoIkFnZV8xOV9VbmRlciIsIkFnZV8xOV82NCIsIkFnZV82NV84NCIsIkFnZV84NV9hbmRfT3ZlciIpDQpBZ2VfQ2F0X2xhYmVscyA8LSBjKCI8MTkiLCIxOS02NCIsIjY1LTg0IiwiODUrIikNCnZ1bG5lcmFibGVfY2F0IDwtIGMoIk5vX0hTX0RpcGxvbWEiLCJVbmVtcGxveWVkIiwiU2V2X1dvcmtfRGlzYWJsZWQiLCJNYWpvcl9EZXByZXNzaW9uIiwiUmVjZW50X0RydWdfVXNlIikNCmRhdGFzZXRfdDIgPC0gbXV0YXRlKGRhdGFzZXRfdDIgDQogICAgICAgICAgICAgICAgICAgICAgICAsQWdlX0NhdD1mYWN0b3IoQWdlLGxldmVscz1BZ2VfY2F0LCBsYWJlbHM9QWdlX0NhdF9sYWJlbHMsb3JkZXJlZCA9IFRSVUUpDQogICAgICAgICAgICAgICAgICAgICAgICAsVnVsbmVyYWJsZV9DYXQ9ZmFjdG9yKFZ1bG5lcmFibGVfQ2F0ZWdvcnksbGV2ZWxzPXZ1bG5lcmFibGVfY2F0KQ0KICAgICAgICAgICAgICAgICAgICAgICApICU+JSBzZWxlY3QoLWMoIkFnZSIsIlZ1bG5lcmFibGVfQ2F0ZWdvcnkiKSkNCnN0cihkYXRhc2V0X3QyKQ0KYGBgDQoNCi0gU3VtbWFyeSBvZiBBZ2UgQ2F0ZWdvcnl3aXNlIFBvcHVsYXRpb24NCmBgYHtyfQ0Kc3VtbWFyeShkYXRhc2V0X3QyJEFnZV9Qb3B1bGF0aW9uKQ0KYGBgDQoNCi0gU3VtbWFyeSBvZiBWdWxuZXJhYmxlIFBvcHVsYXRpb24NCmBgYHtyfQ0Kc3VtbWFyeShkYXRhc2V0X3QyJFZ1bG5lcmFibGVfUG9wdWxhdGlvbikNCmBgYA0KDQojIwlUaWR5ICYgTWFuaXB1bGF0ZSBEYXRhIElJIA0KDQotIERhdGFzZXQgaGFzIEVIX1RpbWVfU3BhbiB3aGljaCBjb250YWlucyAyIHllYXJzKHNlcGFyYXRlZCBieSAiLSIpIHN0YXJ0IGFuZCBlbmQgeWVhciBvZiByZXBvcnRpbmcgZGF0YS4gU28sIHNlcGFyYXRlIHdpbGwgY3JlYXRlIHR3byBuZXcgdmFyaWFibGUoIlN0YXJ0X1llYXIiLCJFbmRfWWVhciIpLg0KYGBge3J9DQpkYXRhc2V0X3QzIDwtIGRhdGFzZXRfdDIgJT4lIHNlcGFyYXRlKEVIX1RpbWVfU3BhbiwgaW50bz1jKCJTdGFydF9ZZWFyIiwiRW5kX1llYXIiKSxzZXA9Ii0iKQ0Kc3RyKGRhdGFzZXRfdDMpDQpgYGANCg0KIyMJU2NhbiBJIA0KDQotIERhdGEgY29udGFpbnMgdmFsdWVzIGxpa2UgLTk5OTksIC0yMjIyLCAtMjIyLCAtMiB3aGljaCByZXByZXNlbnQgTkEvTm90IGF2YWlsYWJsZS8gTm8gZGV0YWlscy4NCi0gRmlyc3Qgc3RlcCwgZmluZCBtaXNzaW5nIHZhbHVlcyBieSB2YXJpYWJsZXMNCmBgYHtyfQ0KIyBGaW5kaW5nIE1pc3NpbmcgdmFsdWVzDQpjb2xTdW1zKGlzLm5hKGRhdGFzZXRfdDMpKQ0KYGBgDQoNCmBgYHtyfQ0KIyB0b3RhbCBtaXNzaW5nIHZhbHVlcw0Kc3VtKGlzLm5hKGRhdGFzZXRfdDMpKQ0KYGBgDQoNCi0gTWlzc2luZyBkYXRhIGlzIHZlcnkgbGVzcyBjb21wYXJlIHRvIHRvdGFsIG5vIG9mIG9ic2VydmF0aW9uLCBzbyB3ZSBjYW4gb21pdCB0aGVtLiBBbmQgT3RoZXIgcmVhc29uIGlzLCBJZiB3ZSByZXBsYWNlIHRoZSB3aXRoIG1lYW4gb3IgbWVkaWFuIG9mIGluZGl2aWR1YWwgdmFyaWFibGUsIGl0IGNhbiBsZWFkIHRvIHdyb25nIHJlc3VsdC4NCmBgYHtyfQ0KIyBPbWl0dGluZyBtaXNzaW5nIHZhbHVlcw0KZGF0YXNldF90NCA8LSBuYS5vbWl0KGRhdGFzZXRfdDMpDQpzdW0oaXMubmEoZGF0YXNldF90NCkpDQpgYGANCg0KLSBDaGVjayBmb3Igc3BlY2lhbCB2YWx1ZXMgSW5mLC1JbmYsIE5hTiBpbiBBZ2VfUG9wdWxhdGlvbg0KYGBge3IsIGVjaG89VFJVRX0NCiMgVG90YWwgTmFOIHZhbHVlcyBpbiBBZ2VfUG9wdWxhdGlvbg0Kc3VtKGlzLm5hbihkYXRhc2V0X3Q0JEFnZV9Qb3B1bGF0aW9uKSkNCiMgVG90YWwgSW5mLy1JbmYgdmFsdWVzIGluIEFnZV9Qb3B1bGF0aW9uDQpzdW0oaXMuaW5maW5pdGUoZGF0YXNldF90NCRBZ2VfUG9wdWxhdGlvbikpDQpgYGANCg0KLSBDaGVjayBmb3Igc3BlY2lhbCB2YWx1ZXMgSW5mLC1JbmYsIE5hTiBpbiBWdWxuZXJhYmxlX1BvcHVsYXRpb24NCmBgYHtyLCBlY2hvPVRSVUV9DQojIFRvdGFsIE5hTiB2YWx1ZXMgaW4gVnVsbmVyYWJsZV9Qb3B1bGF0aW9uDQpzdW0oaXMubmFuKGRhdGFzZXRfdDQkVnVsbmVyYWJsZV9Qb3B1bGF0aW9uKSkNCiMgVG90YWwgSW5mLy1JbmYgdmFsdWVzIGluIFZ1bG5lcmFibGVfUG9wdWxhdGlvbg0Kc3VtKGlzLmluZmluaXRlKGRhdGFzZXRfdDQkVnVsbmVyYWJsZV9Qb3B1bGF0aW9uKSkNCmBgYA0KDQojIwlTY2FuIElJDQoNCi0gU2Nhbm5pbmcgb3V0bGllcnMgZm9yIEFnZV9Qb3B1bGF0aW9uIGFuZCBWdWxuZXJhYmxlX1BvcHVsYXRpb24gdXNpbmcgei1zY29yZT4zIGFuZCBib3hwbG90IG1ldGhvZC4NCmBgYHtyfQ0KIyBaLVNjb3JlIGZvciBBZ2UgUG9wdWxhdGlvbg0Kei5zY29yZXNfYWdlIDwtIGRhdGFzZXRfdDQkQWdlX1BvcHVsYXRpb24gJT4lIHNjb3Jlcyh0eXBlPSJ6IikNCmxlbmd0aCAod2hpY2goIGFicyh6LnNjb3Jlc19hZ2UpID4zICkpDQpgYGANCg0KLSBBZ2UgUG9wdWxhdGlvbiBoYXMgbm8gdmFsdWUgd2hpY2ggaGFzIHouc2NvcmU+My4gSXQgbWVhbnMgQWdlIHBvcHVsYXRpb24gaGFzIG5vIG91dGxpZXJzLg0KYGBge3J9DQojIEJveHBsb3QgZm9yIEFnZSBQb3B1bGF0aW9uDQpib3hwbG90KGRhdGFzZXRfdDQkQWdlX1BvcHVsYXRpb24sIG1haW4gPSAiQm94UGxvdCBvZiBBZ2UgUG9wdWxhdGlvbiIsIHlsYWIgPSAiUG9wdWxhdGlvbiIpDQpgYGANCg0KLSBCeSBzdHVkeWluZyB6LnNjb3JlIGFuZCBib3hwbG90LCB3ZSBjYW4gY29uY3VsZGUgQWdlX1BvcHVsYXRpb24gaGFzIG5vIG91dGxpZXJzLg0KYGBge3J9DQojIFotU2NvcmUgZm9yIFZ1bG5lcmFibGUgUG9wdWxhdGlvbg0Kei5zY29yZXNfdnVsIDwtIGRhdGFzZXRfdDQkVnVsbmVyYWJsZV9Qb3B1bGF0aW9uICU+JSBzY29yZXModHlwZT0ieiIpDQpsZW5ndGggKHdoaWNoKCBhYnMoei5zY29yZXNfdnVsKSA+MyApKQ0KYGBgDQoNCi0gSWRlbnRpZnkgb3V0bGllcnMgYnkgYm94cGxvdA0KYGBge3J9DQojIEJveHBsb3QgZm9yIFZ1bG5lcmFibGUgUG9wdWxhdGlvbg0KYm94cGxvdChkYXRhc2V0X3Q0JFZ1bG5lcmFibGVfUG9wdWxhdGlvbiwgbWFpbiA9ICJCb3hQbG90IG9mIFZ1bG5lcmFibGUgUG9wdWxhdGlvbiIsIHlsYWIgPSAiUG9wdWxhdGlvbiIpDQpgYGANCg0KLSBBcyBwZXIgei5zY29yZSBhbmQgYm94cGxvdCwgVnVsbmVyYWJsZSBQb3B1bGF0aW9uIGhhcyB+NDQ4IG91dGxpZXIuIFdlIGNhbiBoYW5kbGUgdGhvc2UgdXNpbmcgY2FwcGluZyBmdW5jdGlvbiBhbmQgcmVzdWx0IHdpbGwgbm90IGhhdmUgbXVjaCBhZmZlY3QuDQpgYGB7cn0NCiMgQ2FwcGluZyBmdW5jdGlvbiB0byByZXBsYWNlIHRoZSBvdXRsaWVyIHZhbHVlIHdpdGggMXN0IGFuZCAzcmQgUXVhbnRpbGllcy4NCmNhcCA8LSBmdW5jdGlvbih4KXtxdWFudGlsZXMgPC0gcXVhbnRpbGUoeCxjKDAuMDUsMC4yNSwwLjc1LDAuOTUpKQ0KICB4W3ggPCBxdWFudGlsZXNbMl0gLSAxLjUqSVFSKHgpXSA8LSBxdWFudGlsZXNbMV0NCiAgeFt4ID4gcXVhbnRpbGVzWzNdICsgMS41KklRUih4KV0gPC0gcXVhbnRpbGVzWzRdDQogIHgNCn0NCiMgUmVwbGFjZSBPdXRsaWVyIHZhbHVlcyB1c2luZyBjYXBwaW5nIGZ1bmN0aW9uDQpkYXRhc2V0X3Q0JFZ1bG5lcmFibGVfUG9wdWxhdGlvbltpcy5uYShkYXRhc2V0X3Q0JFZ1bG5lcmFibGVfUG9wdWxhdGlvbikgIT0gVFJVRV0gPC0NCmRhdGFzZXRfdDQkVnVsbmVyYWJsZV9Qb3B1bGF0aW9uW2lzLm5hKGRhdGFzZXRfdDQkVnVsbmVyYWJsZV9Qb3B1bGF0aW9uKSAhPSBUUlVFXSAlPiUgY2FwKCkNCiMgQWZ0ZXIgY2FwcGluZyBib3hwbG90DQpib3hwbG90KGRhdGFzZXRfdDQkVnVsbmVyYWJsZV9Qb3B1bGF0aW9uLCBtYWluID0gIkJveFBsb3Qgb2YgVnVsbmVyYWJsZSBQb3B1bGF0aW9uIiwgeWxhYiA9ICJQb3B1bGF0aW9uIikNCmBgYA0KDQotIEFmdGVyIGNhcHBpbmcgb2Ygb3V0bGllciwgei5zY29yZSBjaGVjayBmb3IgVnVsbmVyYWJsZV9Qb3B1bGF0aW9uIGFuZCBjb3VudCBaLlNjb3JlPjMgaXMgemVyby4gaXQgbWVhbnMgVnVsbmVyYWJsZV9Qb3B1bGF0aW9uIGhhcyBubyBvdXRsaWVycy4NCmBgYHtyfQ0KIyBaLVNjb3JlIGZvciBWdWxuZXJhYmxlIFBvcHVsYXRpb24NCnouc2NvcmVzX3Z1bCA8LSBkYXRhc2V0X3Q0JFZ1bG5lcmFibGVfUG9wdWxhdGlvbiAlPiUgc2NvcmVzKHR5cGU9InoiKQ0KbGVuZ3RoICh3aGljaCggYWJzKHouc2NvcmVzX3Z1bCkgPjMgKSkNCmBgYA0KDQojIwlUcmFuc2Zvcm0gDQoNCi0gVHJhbnNmb3JtaW5nIEFnZSBQb3B1bGF0aW9uIGRhdGENCmBgYHtyfQ0KaGlzdChkYXRhc2V0X3Q0JEFnZV9Qb3B1bGF0aW9uLCBtYWluPSJIaXN0b2dyYW0gZm9yIEFnZSBQb3B1bGF0aW9uIiwgeGxhYj0iTWVhbiBvZiBBZ2UgcG9wdWxhdGlvbiIpDQphYmxpbmUodiA9IG1lYW4oZGF0YXNldF90NCRBZ2VfUG9wdWxhdGlvbiksIGNvbD0icmVkIiwgbHdkPTMsIGx0eT0yKQ0KYGBgDQoNCi0gSGlzdG9ncmFtIG9mIEFnZSBQb3B1bGF0aW9uIHNob3csIGl0IGlzIG5vdCBub3JtYWxpemUgZGF0YS4gQnV0IEFnZSBncm91cCBpcyBtdWx0aSB2YXJpYXRlIHZhcmlhYmxlIHNvIGl0IGNhbid0IGJlIGNvbnZlcnRlZCBpbnRvIG5vcm1hbGl6ZSBkYXRhLiBFdmVuIGlmIHdlIGFwcGx5IG5vcm1hbGl6ZSB0ZWNobmlxdWUsIHJlc3VsdCBtaWdodCBiZSBhcyBiZWxvdy4NCmBgYHtyfQ0KIyBBcHBseSBib3hjb3ggd2l0aCBsYW1kYSBBdXRvIHRvIG5vcm1hbGl6ZSB0aGUgZGF0YQ0KZGF0YXNldF90NCRBZ2VfUG9wdWxhdGlvbl9Cb3ggPC0gQm94Q294KGRhdGFzZXRfdDQkQWdlX1BvcHVsYXRpb24sIGxhbWJkYSA9ICJhdXRvIikNCmRhdGFzZXRfdDQkQWdlX1BvcHVsYXRpb25fQm94PC1zY2FsZShkYXRhc2V0X3Q0JEFnZV9Qb3B1bGF0aW9uX0JveCxjZW50ZXI9VFJVRSxzY2FsZT1UUlVFKQ0KaGlzdChkYXRhc2V0X3Q0JEFnZV9Qb3B1bGF0aW9uX0JveCwgbWFpbj0iQWZ0ZXIgdHJhbnNmb3JtYXRpb24sIEhpc3RvZ3JhbSBmb3IgQWdlIFBvcHVsYXRpb24iLCB4bGFiPSJNZWFuIG9mIEFnZSBwb3B1bGF0aW9uIikNCmFibGluZSh2ID0gbWVhbihkYXRhc2V0X3Q0JEFnZV9Qb3B1bGF0aW9uX0JveCksIGNvbD0icmVkIiwgbHdkPTMsIGx0eT0yKQ0KYGBgDQoNCi0gVHJhbnNmb3JtaW5nIFZ1bG5lcmFibGUgUG9wdWxhdGlvbiBkYXRhDQpgYGB7cn0NCmhpc3QoZGF0YXNldF90NCRWdWxuZXJhYmxlX1BvcHVsYXRpb24sIG1haW49Ikhpc3RvZ3JhbSBmb3IgVnVsbmVyYWJsZSBQb3B1bGF0aW9uIiwgeGxhYj0iTWVhbiBvZiBWdWxuZXJhYmxlIHBvcHVsYXRpb24iKQ0KYWJsaW5lKHYgPSBtZWFuKGRhdGFzZXRfdDQkVnVsbmVyYWJsZV9Qb3B1bGF0aW9uKSwgY29sPSJyZWQiLCBsd2Q9MywgbHR5PTIpDQpgYGANCg0KLSBIaXN0b2dyYW0gb2YgVnVsbmVyYWJsZSBQb3B1bGF0aW9uIHNob3csIGl0IGlzIG5vdCBub3JtYWxpemUgZGF0YS4gV2UgY2FuIGNvbnZlcnQgaXQgYnkgYXBwbHlpbmcgYm94Y294IG1ldGhvZCB3aXRoIG9uZSBvdXRsaWVyIHdoaWNoIHdlIGNhbiBpZ25vcmUuDQpgYGB7cn0NCiMgQXBwbHkgYm94Y294IHdpdGggbGFtZGEgQXV0byB0byBub3JtYWxpemUgdGhlIGRhdGENCmRhdGFzZXRfdDQkVnVsbmVyYWJsZV9Qb3B1bGF0aW9uX0JveF8xIDwtIEJveENveChkYXRhc2V0X3Q0JFZ1bG5lcmFibGVfUG9wdWxhdGlvbiwgbGFtYmRhID0gImF1dG8iKQ0KZGF0YXNldF90NCRWdWxuZXJhYmxlX1BvcHVsYXRpb25fQm94IDwtIEJveENveChkYXRhc2V0X3Q0JFZ1bG5lcmFibGVfUG9wdWxhdGlvbl9Cb3hfMSwgbGFtYmRhID0gImF1dG8iKQ0KaGlzdChkYXRhc2V0X3Q0JFZ1bG5lcmFibGVfUG9wdWxhdGlvbl9Cb3gsIG1haW49IkFmdGVyIHRyYW5zZm9ybWF0aW9uLCBIaXN0b2dyYW0gZm9yIFZ1bG5lcmFibGUgUG9wdWxhdGlvbiIsIHhsYWI9Ik1lYW4gb2YgVnVsbmVyYWJsZSBwb3B1bGF0aW9uIikNCmFibGluZSh2ID0gbWVhbihkYXRhc2V0X3Q0JFZ1bG5lcmFibGVfUG9wdWxhdGlvbl9Cb3gpLCBjb2w9InJlZCIsIGx3ZD0zLCBsdHk9MikNCmBgYA0KDQoNCjxicj4NCjxicj4NCg==