Required packages

library(readxl)
library(tidyr)
library(dplyr)
library(outliers)
library(infotheo)

Executive Summary

The objective of this report was to make a tidy dataset of countries with two indicators:

Meanwhile, since income and educational attainment may be correlated, I added gross national income (GNI) and classified countries into different income levels. To my own interest, the working hour was also added to complement this classification. I adopted a combination of income level and the length of working hours to group countries, such as “High income & Short hours” and “Low income & Long hours”. To accomplish this, I obtained multiple datasets with the variables needed and joined them into a tidy one. Necessary scanning steps were taken to deal with missing values and to identify the outliers. For the classification, I created new variables of each country’s average GNI and working hours over three years and used the binning method to group the countries.

Data

Member countries of OECD (The Organisation for Economic Co-operation and Development) were investigated. I looked at three education levels for both employment rate and educational attainment: below upper secondary, upper secondary, and tertiary. The measures cover the latest three years available: 2014 to 2016. Five datasets were generated for further process. I introduced the main variables in each dataset below:

setwd("~/Desktop/DP Assignment 3")
GNI<-read.csv("GNI.csv",stringsAsFactors = FALSE)
GNI[1:3, ]
hours<-read.csv("hours worked.csv",stringsAsFactors = FALSE)
hours[1:3, ]
employment<-read.csv("employment.csv",stringsAsFactors = FALSE)
employment[1:3, ]
education<-read.csv("EducationLevel.csv",stringsAsFactors = FALSE)
education[1:3, ]
code<-read_excel("code.xls",skip=1)
code[1:3, ]
  1. I opened a new folder called “DP Assignment 3” on Desktop and saved the five datasets into it. This folder was set as the working directory using “setwd()”.
  2. CSV files were imported using Base R functions. I set “stringsAsFactors = FALSE” to read the variables in their original forms.
  3. The EXCEL file was imported using “read_excel()”.
  4. In order to save space for my report, I displayed only the first three rows of each dataset.

Understand

str(GNI)
'data.frame':   137 obs. of  8 variables:
 $ LOCATION  : chr  "AUS" "AUS" "AUS" "AUT" ...
 $ INDICATOR : chr  "GNI" "GNI" "GNI" "GNI" ...
 $ SUBJECT   : chr  "TOT" "TOT" "TOT" "TOT" ...
 $ MEASURE   : chr  "USD_CAP" "USD_CAP" "USD_CAP" "USD_CAP" ...
 $ FREQUENCY : chr  "A" "A" "A" "A" ...
 $ TIME      : int  2014 2015 2016 2014 2015 2016 2014 2015 2016 2014 ...
 $ Value     : num  45953 45710 46885 48866 49594 ...
 $ Flag.Codes: chr  "" "" "" "" ...
str(hours)
'data.frame':   112 obs. of  8 variables:
 $ LOCATION  : chr  "AUS" "AUS" "AUS" "AUT" ...
 $ INDICATOR : chr  "HRWKD" "HRWKD" "HRWKD" "HRWKD" ...
 $ SUBJECT   : chr  "TOT" "TOT" "TOT" "TOT" ...
 $ MEASURE   : chr  "HR_WKD" "HR_WKD" "HR_WKD" "HR_WKD" ...
 $ FREQUENCY : chr  "A" "A" "A" "A" ...
 $ TIME      : int  2014 2015 2016 2014 2015 2016 2014 2015 2014 2015 ...
 $ Value     : int  1681 1682 1669 1628 1608 1601 1556 1551 1704 1707 ...
 $ Flag.Codes: logi  NA NA NA NA NA NA ...
str(employment)
'data.frame':   363 obs. of  8 variables:
 $ LOCATION  : chr  "NZL" "NZL" "NZL" "GBR" ...
 $ INDICATOR : chr  "EMPEDU" "EMPEDU" "EMPEDU" "EMPEDU" ...
 $ SUBJECT   : Factor w/ 3 levels "Below_upper_secondary",..: NA NA NA NA NA NA NA NA NA NA ...
 $ MEASURE   : chr  "PC_25_64" "PC_25_64" "PC_25_64" "PC_25_64" ...
 $ FREQUENCY : chr  "A" "A" "A" "A" ...
 $ TIME      : int  2014 2015 2016 2014 2015 2016 2014 2015 2016 2014 ...
 $ Value     : num  80.3 81.3 82.1 84.6 85.4 ...
 $ Flag.Codes: logi  NA NA NA NA NA NA ...
str(education)
'data.frame':   363 obs. of  8 variables:
 $ LOCATION  : chr  "AUS" "AUS" "AUS" "AUS" ...
 $ INDICATOR : chr  "EDUADULT" "EDUADULT" "EDUADULT" "EDUADULT" ...
 $ SUBJECT   : Factor w/ 3 levels "Below_upper_secondary",..: NA NA NA NA NA NA NA NA NA NA ...
 $ MEASURE   : chr  "PC_25_64" "PC_25_64" "PC_25_64" "PC_25_64" ...
 $ FREQUENCY : chr  "A" "A" "A" "A" ...
 $ TIME      : int  2014 2015 2016 2014 2015 2016 2014 2015 2016 2014 ...
 $ Value     : num  22.9 21 20.1 41.9 42.9 ...
 $ Flag.Codes: logi  NA NA NA NA NA NA ...
str(code)
Classes ‘tbl_df’, ‘tbl’ and 'data.frame':   239 obs. of  2 variables:
 $ Code Value: chr  "AFG" "ALA" "ALB" "DZA" ...
 $ Definition: chr  "Afghanistan" "Aland Islands" "Albania" "Algeria" ...
employment<-employment %>% mutate(SUBJECT=factor(SUBJECT,levels=c("BUPPSRY","UPPSRY_NTRY","TRY"),labels =c("Below_upper_secondary","Upper_secondary","Tertiary")))
education<-education %>% mutate(SUBJECT=factor(SUBJECT,levels=c("BUPPSRY","UPPSRY","TRY"),labels=c("Below_upper_secondary","Upper_secondary","Tertiary")))

I inspected the structure of the data frame and the type of variables using “str()”. There were character, numeric and logical values. Since there was no factors in my datasets, I converted the character variables “Subject” to factors and labelled them using “mutate()” and “factor()”. “Subject” includes three education levels that are suitable for a convertion to factors.

Tidy & Manipulate Data I

GNInew<-GNI %>% select(-INDICATOR,-SUBJECT,-MEASURE,-FREQUENCY,-Flag.Codes) %>% unite(Location_Year,LOCATION,TIME)
colnames(GNInew)[2]<-"GNI"
hoursnew<-hours%>%select(-INDICATOR,-SUBJECT,-MEASURE,-FREQUENCY,-Flag.Codes) %>% unite(Location_Year,LOCATION,TIME)
colnames(hoursnew)[2]<-"HRWKD"
employmentnew<-employment %>% select(-INDICATOR,-MEASURE,-FREQUENCY,-Flag.Codes) %>% unite(Location_Year_Subject, LOCATION, TIME,SUBJECT)
colnames(employmentnew)[2]<-"EMP"
educationnew<-education %>% select(-INDICATOR,-MEASURE,-FREQUENCY,-Flag.Codes) %>% unite(Location_Year_Subject,LOCATION,TIME,SUBJECT)
colnames(educationnew)[2]<-"EDU"
df1<-full_join(educationnew,employmentnew,by="Location_Year_Subject")
df1<-df1 %>% separate(1,into=c("Location","Year","Education_level"),extra = "merge",fill="left") %>% unite(Location_Year, Location, Year)
df1[1:3, ]#an example of df1
df2<-right_join(GNInew,hoursnew,by="Location_Year")
df2[1:3, ]#an example of df2
df<-left_join(df2,df1,by="Location_Year")
df<-separate(df,1,into=c("Country_code","Year"))
colnames(code)[1]<-"Country_code"
df<-right_join(code,df,by="Country_code")
df

I joined five datasets into one tidy dataset named “df”. To accomplish this, I took the below steps:

  1. I deleted redundant variables to keep only the variables I need using “select()” for each dataset.

  2. For GNI and hours, I combined two variables “Location” and “Time” using “unite()” and set it as a unique variable to match for joining purpose. I renamed “Value” to the corresponding indicators “GNI” and “HRWKD”.

  3. For employment and education, I combined “Location”, “Time” and “Subject” using “unite()” and set it as a unique variable to match for joining purpose. I renamed “Value” to the corresponding indicators “EMP” and “EDU”.

  4. I joined employmentnew and educationnew by “Location_Year_Subject” into a new dataset df1 using “full_join()” to keep all the values from both datasets. Then I separated the variable “Location_Year_Subject” into two variables “Location_Year” and “Education_level” for further joining purpose.

  5. I joined matching rows from GNInew to hoursnew by “Location_Year” into a new dataset df2 using “right_join()” so that the variale “HRWKD” was placed to the right and all values from hoursnew were kept because hoursnew only contained countries that are OECD member countries.

  6. I joined matching rows from df1 to df2 by “Location_Year” into the final combined dataset df using “left_join()” to keep all values of df2. Then I separated the variable “Location_Year” into “Country_code” and “Year”. At last, I joined matching rows from code to df by “Country_code” using “right_join()” so that I could have a new column “Definition” to check the full name of each country code.

Tidy & Manipulate Data II

freq<-data.frame(table(df$Country_code,df$Year))
freq<-spread(freq,key = Var2, value = Freq)
freq[1:3, ]#an example of freq
which(freq$`2014`==0)
integer(0)
which(freq$`2015`==0)
integer(0)
which(freq$`2016`==0)
[1] 3 5
freq$Var1[3:5]
[1] BEL CAN CHE
38 Levels: AUS AUT BEL CAN CHE CHL CRI CZE DEU DNK ESP EST FIN FRA GBR GRC HUN IRL ISL ISR ITA JPN KOR LTU LUX ... USA
dffreq1<-df[!(df$`Country_code`=="BEL"), ]
dfnew<-dffreq1[!(dffreq1$`Country_code`=="CHE"), ]
dfnew<-dfnew %>% group_by(`Country_code`) %>% mutate(Mean_GNI=mean(unique(GNI)),Mean_hours=mean(unique(HRWKD)))
dfnew

After joining the datasets, I assumed all countries have both GNI and working hours values available for three years, 2014 to 2016. I checked my assumption using “table()” to see the frequency of a country under each year. I converted the table to a dataframe for easy checking. Normally, each country should appear three times (for three education levels in a year) or at least once under each year. I found that two countries “BEL” and “CHE” do not match with this criteria. They have no rows for year 2016. It was because that these two countries have no “HRKWD” values in year 2016 or they have no values for both “GNI” and “HRKWD” in year 2016. If they have “HRKWD” values in year 2016, their 2016 rows should apprear because in the above joining process I kept all rows of the working hours dataset.

To classify countries based on a combination of their income level and working hours, I need to calculate both the average GNI and working hours over three years 2014-2016 for each country. Therefore I excluded these two countries in my report. Then I created two new columns of mean values using “group_by()” and “mutate()”. I added “unique()” in this function to ensure the value of each year only appears once in the calculation to get the correct mean value.

Scan I

colSums(is.na(dfnew))
   Country_code      Definition            Year             GNI           HRWKD Education_level             EDU 
              0               3               0               0               0               7               7 
            EMP        Mean_GNI      Mean_hours 
              7               0               0 
which(is.na(dfnew$Definition))
[1] 275 276 277
head(dfnew[275:277, ])
dfnew1<-dfnew[!(dfnew$`Country_code`=="OECD"), ]
colSums((is.na(dfnew1)))
   Country_code      Definition            Year             GNI           HRWKD Education_level             EDU 
              0               0               0               0               0               4               4 
            EMP        Mean_GNI      Mean_hours 
              4               0               0 
which(is.na(dfnew1$`Education_level`))==which(is.na(dfnew1$EDU))
[1] TRUE TRUE TRUE TRUE
which(is.na(dfnew1$`Education_level`))==which(is.na(dfnew1$EMP))
[1] TRUE TRUE TRUE TRUE
which(is.na(dfnew1$`Education_level`))
[1] 106 236 240 265
dfnew1[c(106,236,240,265), ]
#CHL
r1<-dfnew1[237:239, ]
dfnew2 <- rbind(dfnew1[1:236,],r1,r1,dfnew1[-(1:236),])
c1<-c("2014",21903.17218,1990)
dfnew2[237:239,3:5]<-rbind(c1,c1,c1)
c2<-c("2016",22355.9843,1974)
dfnew2[243:245,3:5]<-rbind(c2,c2,c2)
dfnew3<-dfnew2[-c(246,236), ]
dfnew3 %>% filter(Country_code=="CHL")
#IRL and RUS
r3<-dfnew3[106, ]
dfnew4<-rbind(dfnew3[1:106, ],r3,r3,dfnew3[-(1:106), ])
r4<-dfnew4[271, ]
dfnew5<-rbind(dfnew4[1:271, ],r4,r4,dfnew4[-(1:271), ])
c3<-c("Below_upper_secondary","Tertiary","Upper_secondary")
dfnew5[106:108,6]<-c3
dfnew5[271:273,6]<-c3
dfnew6<-dfnew5%>%group_by(`Country_code`,`Education_level`)%>% mutate(EDU=ifelse(is.na(EDU),mean(EDU,na.rm=TRUE),EDU)) %>% mutate(EMP=ifelse(is.na(EMP),mean(EMP,na.rm=TRUE),EMP))
dfnew6 %>% filter(Country_code=="IRL")
dfnew6 %>% filter(Country_code=="RUS")

Missing Value: I checked the number of missing values in each column. The three missing values in variable “Definition” are “OECD”, which represents an average value for all OECD countries. I deleted these three rows because I want to study each country separately. For all the other missing values in “Education_level”,“EMP” and “EDU”, I checked that they were in the same rows. Three countries included missing values: Ireland, Chile and Russia. Chile only had employment rate and educational attainment available in year 2015. I copied its “EMP” and “EDU” values for each education level of year 2015 to year 2014 and 2016. For Ireland and Russia, they had employment rate and educational attainment availble in year 2014 and 2015. I imputed the average values of employment rate and educational attainment for each education level in year 2014 and year 2015 to year 2016.

dfnew6$GNI<-as.numeric(dfnew6$GNI)
dfnew6$HRWKD<-as.integer(dfnew6$HRWKD)
dfnew6$Education_level<-as.factor(dfnew6$Education_level)
m<-cbind(dfnew6$GNI,dfnew6$HRWKD,dfnew6$EDU,dfnew6$EMP,dfnew6$Mean_GNI,dfnew6$Mean_hours) #with a reason
sum(is.nan(m))
[1] 0
sum(is.infinite(m)) 
[1] 0

Special Value: I checked the type of variables again and found that “GNI”,“HRWKD” and “Education_level” have become characters. It was probably due to the above “unite()”, “separate()” and “rbind()” process that forced all variables into one type. “Education_level” was converted to factors. “GNI” and “HRWKD” were converted to numeric values for further checking on special values. Functions “is.nan()” and “is.infinite()” only work for vectorial input. Therefore I combined all numeric values into one matrix. It turned out that there were no special values.

sum(dfnew6$GNI<0)
[1] 0
sum(dfnew6$HRWKD<0)
[1] 0
sum(dfnew6$EDU<=0 |dfnew6$EDU>=100)
[1] 0
sum(dfnew6$EMP<=0 |dfnew6$EMP>=100)
[1] 0
sum(dfnew6$Mean_GNI<0)
[1] 0
sum(dfnew6$Mean_hours<0)
[1] 0
checkEDU100<-dfnew6 %>% group_by(`Country_code`,Year) %>% mutate(sumEDU=sum(EDU))
which(round(checkEDU100$sumEDU,digits = 0)!=100)
[1] 118 119 120
dfnew6[118:120,]

Inconsistency:

Scan II

z.scores<-dfnew6$GNI %>%scores(type="z")
z.scores %>% summary()
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
-1.8250 -0.7893 -0.1215  0.0000  0.7900  2.4540 
which(abs(z.scores)>3)
integer(0)
z.scores<-dfnew6$HRWKD %>%scores(type="z")
z.scores %>% summary()
    Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
-1.89600 -0.57890 -0.03397  0.00000  0.57730  2.33100 
which(abs(z.scores)>3)
integer(0)
z.scores<-dfnew6$Mean_GNI %>%scores(type="z")
z.scores %>% summary()
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
-1.7920 -0.7724 -0.1372  0.0000  0.8405  2.3340 
which(abs(z.scores)>3)
integer(0)
z.scores<-dfnew6$Mean_hours %>%scores(type="z")
z.scores %>% summary()
    Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
-1.88700 -0.56860 -0.04827  0.00000  0.53370  2.30200 
which(abs(z.scores)>3)
integer(0)
length(boxplot(dfnew6$EDU ~ dfnew6$Education_level, main="Educational attainment by Education level", ylab = "Education level", xlab = "Educational attainment")$out)
[1] 9

length(boxplot(dfnew6$EMP ~ dfnew6$Education_level, main="Employment rate by Education level", ylab = "Education level", xlab = "Employment rate")$out)
[1] 7

#identify the 9 outliers in educational attainment above the upper fence for "Below_upper_secondary" level
outlierEDU<-dfnew6 %>% filter(Education_level=="Below_upper_secondary") %>% select(-EMP)
outlierEDU<-outlierEDU[order(outlierEDU$EDU,decreasing=TRUE), ]
outlierEDU[1:9, ]
#identify the 1 outliers in employment rate above the upper fence for "Tertiary" level
outlierEMP1<-dfnew6 %>% filter(Education_level=="Tertiary") %>% select(-EDU)
outlierEMP1up<-outlierEMP1[order(outlierEMP1$EMP,decreasing=TRUE), ]
outlierEMP1up[1, ]
#identify the 3 outliers in employment rate below the lower fence for "Tertiary" level
outlierEMP1low<-outlierEMP1[order(outlierEMP1$EMP), ]
outlierEMP1low[1:3, ]
#identify the 3 outlier in employment rate below the lower fence for "Upper_secondary" level
outlierEMP2<-dfnew6 %>% filter(Education_level=="Upper_secondary") %>% select(-EDU)
outlierEMP2<-outlierEMP2[order(outlierEMP2$EMP), ]
outlierEMP2[1:3, ]

Transform

bin_GNI<-discretize(dfnew6$Mean_GNI, disc = "equalwidth",nbins=3)
dfnew7<-bind_cols(dfnew6,bin_GNI)
bin_hours<-discretize(dfnew7$Mean_hours,disc="equalwidth", nbins=3)
dfnew8<-bind_cols(dfnew7,bin_hours)
dfnew8<- dfnew8 %>% mutate(X=factor(X,levels=c(1,2,3),labels =c("Low_income","Middle_income","High_income"))) %>% mutate(X1=factor(X1,levels=c(1,2,3),labels=c("Short_hours","Medium_hours","Long_hours"))) %>% unite(Class,X,X1,sep = "&")
dfnew8
Country_class<-dfnew8 %>% ungroup() %>% distinct(Country_code,Class)
Country_class[order(Country_class$Class), ]



LS0tCnRpdGxlOiAiTUFUSDIzNDkgU2VtZXN0ZXIgMSwgMjAxOCIKYXV0aG9yOiAiWWlmYW4gRkVORyBzMzcwNDQ3NiIKc3VidGl0bGU6IEFzc2lnbm1lbnQgMwpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdAogIGh0bWxfZG9jdW1lbnQ6CiAgICBkZl9wcmludDogcGFnZWQKLS0tCgojIyBSZXF1aXJlZCBwYWNrYWdlcyAKCmBgYHtyfQpsaWJyYXJ5KHJlYWR4bCkKbGlicmFyeSh0aWR5cikKbGlicmFyeShkcGx5cikKbGlicmFyeShvdXRsaWVycykKbGlicmFyeShpbmZvdGhlbykKYGBgCgojIyBFeGVjdXRpdmUgU3VtbWFyeSAKClRoZSBvYmplY3RpdmUgb2YgdGhpcyByZXBvcnQgd2FzIHRvIG1ha2UgYSB0aWR5IGRhdGFzZXQgb2YgY291bnRyaWVzIHdpdGggdHdvIGluZGljYXRvcnM6IAoKKiBFbXBsb3ltZW50IHJhdGVzIGJ5IGVkdWNhdGlvbiBsZXZlbHM6IHRoZSBwZXJjZW50YWdlIG9mIGVtcGxveWVkIDI1LTY0IHllYXItb2xkcyBieSBhbGwgMjUtNjQgeWVhci1vbGRzIHdpdGggdGhlIHNhbWUgZWR1Y2F0aW9uIGxldmVsCiogRWR1Y2F0aW9uYWwgYXR0YWlubWVudDogdGhlIGhpZ2hlc3QgbGV2ZWwgb2YgZWR1Y2F0aW9uIGFjaGlldmVkIGFzIGEgcGVyY2VudGFnZSB0byB0aGUgdG90YWwgcG9wdWxhdGlvbiBvZiAyNS02NCB5ZWFyLW9sZHMKCk1lYW53aGlsZSwgc2luY2UgaW5jb21lIGFuZCBlZHVjYXRpb25hbCBhdHRhaW5tZW50IG1heSBiZSBjb3JyZWxhdGVkLCBJIGFkZGVkIGdyb3NzIG5hdGlvbmFsIGluY29tZSAoR05JKSBhbmQgY2xhc3NpZmllZCBjb3VudHJpZXMgaW50byBkaWZmZXJlbnQgaW5jb21lIGxldmVscy4gVG8gbXkgb3duIGludGVyZXN0LCB0aGUgd29ya2luZyBob3VyIHdhcyBhbHNvIGFkZGVkIHRvIGNvbXBsZW1lbnQgdGhpcyBjbGFzc2lmaWNhdGlvbi4gSSBhZG9wdGVkIGEgY29tYmluYXRpb24gb2YgaW5jb21lIGxldmVsIGFuZCB0aGUgbGVuZ3RoIG9mIHdvcmtpbmcgaG91cnMgdG8gZ3JvdXAgY291bnRyaWVzLCBzdWNoIGFzICJIaWdoIGluY29tZSAmIFNob3J0IGhvdXJzIiBhbmQgIkxvdyBpbmNvbWUgJiBMb25nIGhvdXJzIi4gVG8gYWNjb21wbGlzaCB0aGlzLCBJIG9idGFpbmVkIG11bHRpcGxlIGRhdGFzZXRzIHdpdGggdGhlIHZhcmlhYmxlcyBuZWVkZWQgYW5kIGpvaW5lZCB0aGVtIGludG8gYSB0aWR5IG9uZS4gTmVjZXNzYXJ5IHNjYW5uaW5nIHN0ZXBzIHdlcmUgdGFrZW4gdG8gZGVhbCB3aXRoIG1pc3NpbmcgdmFsdWVzIGFuZCB0byBpZGVudGlmeSB0aGUgb3V0bGllcnMuIEZvciB0aGUgY2xhc3NpZmljYXRpb24sIEkgY3JlYXRlZCBuZXcgdmFyaWFibGVzIG9mIGVhY2ggY291bnRyeSdzIGF2ZXJhZ2UgR05JIGFuZCB3b3JraW5nIGhvdXJzIG92ZXIgdGhyZWUgeWVhcnMgYW5kIHVzZWQgdGhlIGJpbm5pbmcgbWV0aG9kIHRvIGdyb3VwIHRoZSBjb3VudHJpZXMuIAoKIyMgRGF0YQoKTWVtYmVyIGNvdW50cmllcyBvZiBPRUNEIChUaGUgT3JnYW5pc2F0aW9uIGZvciBFY29ub21pYyBDby1vcGVyYXRpb24gYW5kIERldmVsb3BtZW50KSB3ZXJlIGludmVzdGlnYXRlZC4gSSBsb29rZWQgYXQgdGhyZWUgZWR1Y2F0aW9uIGxldmVscyBmb3IgYm90aCBlbXBsb3ltZW50IHJhdGUgYW5kIGVkdWNhdGlvbmFsIGF0dGFpbm1lbnQ6IGJlbG93IHVwcGVyIHNlY29uZGFyeSwgdXBwZXIgc2Vjb25kYXJ5LCBhbmQgdGVydGlhcnkuIFRoZSBtZWFzdXJlcyBjb3ZlciB0aGUgbGF0ZXN0IHRocmVlIHllYXJzIGF2YWlsYWJsZTogMjAxNCB0byAyMDE2LiBGaXZlIGRhdGFzZXRzIHdlcmUgZ2VuZXJhdGVkIGZvciBmdXJ0aGVyIHByb2Nlc3MuIEkgaW50cm9kdWNlZCB0aGUgbWFpbiB2YXJpYWJsZXMgaW4gZWFjaCBkYXRhc2V0IGJlbG93OgoKKiBHTkk6IExvY2F0aW9uKGNvdW50cnkgY29kZSksIFRpbWUoeWVhciksIFZhbHVlKGdyb3NzIG5hdGlvbmFsIGluY29tZTogVVMgZG9sbGFycyBwZXIgY2FwaXRhKTsgaXQgaXMgYSBDU1YgZmlsZSBkb3dubG9hZGVkIGZyb206IDxzcGFuIHN0eWxlPSJjb2xvcjpibHVlIj4qZGF0YS5vZWNkLm9yZy9uYXRpbmNvbWUvZ3Jvc3MtbmF0aW9uYWwtaW5jb21lLmh0bSo8L3NwYW4+CiogaG91cnM6IExvY2F0aW9uKGNvdW50cnkgY29kZSksIFRpbWUoeWVhciksIFZhbHVlKGF2ZXJhZ2UgYW5udWFsIHdvcmtpbmcgaG91cnMgcGVyIHdvcmtlcik7IGl0IGlzIGEgQ1NWIGZpbGUgZG93bmxvYWRlZCBmcm9tOiA8c3BhbiBzdHlsZT0iY29sb3I6Ymx1ZSI+KmRhdGEub2VjZC5vcmcvZW1wL2hvdXJzLXdvcmtlZC5odG0qPC9zcGFuPgoqIGVtcGxveW1lbnQ6IExvY2F0aW9uKGNvdW50cnkgY29kZSksIFN1YmplY3QoZWR1Y2F0aW9uIGxldmVsKSwgVGltZSh5ZWFyKSxWYWx1ZShlbXBsb3ltZW50IHJhdGUpOyBpdCBpcyBhIENTViBmaWxlIGRvd25sb2FkZWQgZnJvbTogPHNwYW4gc3R5bGU9ImNvbG9yOmJsdWUiPipkYXRhLm9lY2Qub3JnL2VtcC9lbXBsb3ltZW50LWJ5LWVkdWNhdGlvbi1sZXZlbC5odG0qPC9zcGFuPgoqIGVkdWNhdGlvbjogTG9jYXRpb24oY291bnRyeSBjb2RlKSwgU3ViamVjdChlZHVjYXRpb24gbGV2ZWwpLCBUaW1lKHllYXIpLFZhbHVlKGVkdWNhdGlvbmFsIGF0dGFpbm1lbnQpOyBpdCBpcyBhIENTViBmaWxlIGRvd25sb2FkZWQgZnJvbTogPHNwYW4gc3R5bGU9ImNvbG9yOmJsdWUiPipkYXRhLm9lY2Qub3JnL2VkdWF0dC9hZHVsdC1lZHVjYXRpb24tbGV2ZWwuaHRtKjwvc3Bhbj4KKiBjb2RlOiBDb2RlIHZhbHVlKGNvdW50cnkgY29kZSksIERlZmluaXRpb24oY291bnRyeSdzIGZ1bGwgbmFtZSk7IGl0IGlzIGFuIEVYQ0VMIGZpbGUgZG93bmxvYWRlZCBmcm9tOiA8c3BhbiBzdHlsZT0iY29sb3I6Ymx1ZSI+Knd3dy5kbmIuY29tL2NvbnRlbnQvZGFtL2VuZ2xpc2gvZG5iLXNvbHV0aW9ucy9zYWxlcy1hbmQtbWFya2V0aW5nL2lzb18zZGlnaXRfYWxwaGFfY291bnRyeV9jb2Rlcy54bHMqPC9zcGFuPgoKYGBge3J9CnNldHdkKCJ+L0Rlc2t0b3AvRFAgQXNzaWdubWVudCAzIikKR05JPC1yZWFkLmNzdigiR05JLmNzdiIsc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQpHTklbMTozLCBdCmhvdXJzPC1yZWFkLmNzdigiaG91cnMgd29ya2VkLmNzdiIsc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQpob3Vyc1sxOjMsIF0KZW1wbG95bWVudDwtcmVhZC5jc3YoImVtcGxveW1lbnQuY3N2IixzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCmVtcGxveW1lbnRbMTozLCBdCmVkdWNhdGlvbjwtcmVhZC5jc3YoIkVkdWNhdGlvbkxldmVsLmNzdiIsc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQplZHVjYXRpb25bMTozLCBdCmNvZGU8LXJlYWRfZXhjZWwoImNvZGUueGxzIixza2lwPTEpCmNvZGVbMTozLCBdCmBgYAoxLiBJIG9wZW5lZCBhIG5ldyBmb2xkZXIgY2FsbGVkICJEUCBBc3NpZ25tZW50IDMiIG9uIERlc2t0b3AgYW5kIHNhdmVkIHRoZSBmaXZlIGRhdGFzZXRzIGludG8gaXQuIFRoaXMgZm9sZGVyIHdhcyBzZXQgYXMgdGhlIHdvcmtpbmcgZGlyZWN0b3J5IHVzaW5nICJzZXR3ZCgpIi4KMi4gQ1NWIGZpbGVzIHdlcmUgaW1wb3J0ZWQgdXNpbmcgQmFzZSBSIGZ1bmN0aW9ucy4gSSBzZXQgInN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSIgdG8gcmVhZCB0aGUgdmFyaWFibGVzIGluIHRoZWlyIG9yaWdpbmFsIGZvcm1zLgozLiBUaGUgRVhDRUwgZmlsZSB3YXMgaW1wb3J0ZWQgdXNpbmcgInJlYWRfZXhjZWwoKSIuCjQuIEluIG9yZGVyIHRvIHNhdmUgc3BhY2UgZm9yIG15IHJlcG9ydCwgSSBkaXNwbGF5ZWQgb25seSB0aGUgZmlyc3QgdGhyZWUgcm93cyBvZiBlYWNoIGRhdGFzZXQuCgojIyBVbmRlcnN0YW5kIAoKYGBge3J9CnN0cihHTkkpCnN0cihob3VycykKc3RyKGVtcGxveW1lbnQpCnN0cihlZHVjYXRpb24pCnN0cihjb2RlKQplbXBsb3ltZW50PC1lbXBsb3ltZW50ICU+JSBtdXRhdGUoU1VCSkVDVD1mYWN0b3IoU1VCSkVDVCxsZXZlbHM9YygiQlVQUFNSWSIsIlVQUFNSWV9OVFJZIiwiVFJZIiksbGFiZWxzID1jKCJCZWxvd191cHBlcl9zZWNvbmRhcnkiLCJVcHBlcl9zZWNvbmRhcnkiLCJUZXJ0aWFyeSIpKSkKZWR1Y2F0aW9uPC1lZHVjYXRpb24gJT4lIG11dGF0ZShTVUJKRUNUPWZhY3RvcihTVUJKRUNULGxldmVscz1jKCJCVVBQU1JZIiwiVVBQU1JZIiwiVFJZIiksbGFiZWxzPWMoIkJlbG93X3VwcGVyX3NlY29uZGFyeSIsIlVwcGVyX3NlY29uZGFyeSIsIlRlcnRpYXJ5IikpKQpgYGAKSSBpbnNwZWN0ZWQgdGhlIHN0cnVjdHVyZSBvZiB0aGUgZGF0YSBmcmFtZSBhbmQgdGhlIHR5cGUgb2YgdmFyaWFibGVzIHVzaW5nICJzdHIoKSIuIFRoZXJlIHdlcmUgY2hhcmFjdGVyLCBudW1lcmljIGFuZCBsb2dpY2FsIHZhbHVlcy4gU2luY2UgdGhlcmUgd2FzIG5vIGZhY3RvcnMgaW4gbXkgZGF0YXNldHMsIEkgY29udmVydGVkIHRoZSBjaGFyYWN0ZXIgdmFyaWFibGVzICJTdWJqZWN0IiB0byBmYWN0b3JzIGFuZCBsYWJlbGxlZCB0aGVtIHVzaW5nICJtdXRhdGUoKSIgYW5kICJmYWN0b3IoKSIuICJTdWJqZWN0IiBpbmNsdWRlcyB0aHJlZSBlZHVjYXRpb24gbGV2ZWxzIHRoYXQgYXJlIHN1aXRhYmxlIGZvciBhIGNvbnZlcnRpb24gdG8gZmFjdG9ycy4KCiMjCVRpZHkgJiBNYW5pcHVsYXRlIERhdGEgSSAKCmBgYHtyfQpHTkluZXc8LUdOSSAlPiUgc2VsZWN0KC1JTkRJQ0FUT1IsLVNVQkpFQ1QsLU1FQVNVUkUsLUZSRVFVRU5DWSwtRmxhZy5Db2RlcykgJT4lIHVuaXRlKExvY2F0aW9uX1llYXIsTE9DQVRJT04sVElNRSkKY29sbmFtZXMoR05JbmV3KVsyXTwtIkdOSSIKCmhvdXJzbmV3PC1ob3VycyU+JXNlbGVjdCgtSU5ESUNBVE9SLC1TVUJKRUNULC1NRUFTVVJFLC1GUkVRVUVOQ1ksLUZsYWcuQ29kZXMpICU+JSB1bml0ZShMb2NhdGlvbl9ZZWFyLExPQ0FUSU9OLFRJTUUpCmNvbG5hbWVzKGhvdXJzbmV3KVsyXTwtIkhSV0tEIgoKZW1wbG95bWVudG5ldzwtZW1wbG95bWVudCAlPiUgc2VsZWN0KC1JTkRJQ0FUT1IsLU1FQVNVUkUsLUZSRVFVRU5DWSwtRmxhZy5Db2RlcykgJT4lIHVuaXRlKExvY2F0aW9uX1llYXJfU3ViamVjdCwgTE9DQVRJT04sIFRJTUUsU1VCSkVDVCkKY29sbmFtZXMoZW1wbG95bWVudG5ldylbMl08LSJFTVAiCgplZHVjYXRpb25uZXc8LWVkdWNhdGlvbiAlPiUgc2VsZWN0KC1JTkRJQ0FUT1IsLU1FQVNVUkUsLUZSRVFVRU5DWSwtRmxhZy5Db2RlcykgJT4lIHVuaXRlKExvY2F0aW9uX1llYXJfU3ViamVjdCxMT0NBVElPTixUSU1FLFNVQkpFQ1QpCmNvbG5hbWVzKGVkdWNhdGlvbm5ldylbMl08LSJFRFUiCgpkZjE8LWZ1bGxfam9pbihlZHVjYXRpb25uZXcsZW1wbG95bWVudG5ldyxieT0iTG9jYXRpb25fWWVhcl9TdWJqZWN0IikKZGYxPC1kZjEgJT4lIHNlcGFyYXRlKDEsaW50bz1jKCJMb2NhdGlvbiIsIlllYXIiLCJFZHVjYXRpb25fbGV2ZWwiKSxleHRyYSA9ICJtZXJnZSIsZmlsbD0ibGVmdCIpICU+JSB1bml0ZShMb2NhdGlvbl9ZZWFyLCBMb2NhdGlvbiwgWWVhcikKZGYxWzE6MywgXSNhbiBleGFtcGxlIG9mIGRmMQoKZGYyPC1yaWdodF9qb2luKEdOSW5ldyxob3Vyc25ldyxieT0iTG9jYXRpb25fWWVhciIpCmRmMlsxOjMsIF0jYW4gZXhhbXBsZSBvZiBkZjIKCmRmPC1sZWZ0X2pvaW4oZGYyLGRmMSxieT0iTG9jYXRpb25fWWVhciIpCmRmPC1zZXBhcmF0ZShkZiwxLGludG89YygiQ291bnRyeV9jb2RlIiwiWWVhciIpKQpjb2xuYW1lcyhjb2RlKVsxXTwtIkNvdW50cnlfY29kZSIKZGY8LXJpZ2h0X2pvaW4oY29kZSxkZixieT0iQ291bnRyeV9jb2RlIikKZGYKYGBgCkkgam9pbmVkIGZpdmUgZGF0YXNldHMgaW50byBvbmUgdGlkeSBkYXRhc2V0IG5hbWVkICJkZiIuIFRvIGFjY29tcGxpc2ggdGhpcywgSSB0b29rIHRoZSBiZWxvdyBzdGVwczoKCjEuIEkgZGVsZXRlZCByZWR1bmRhbnQgdmFyaWFibGVzIHRvIGtlZXAgb25seSB0aGUgdmFyaWFibGVzIEkgbmVlZCB1c2luZyAic2VsZWN0KCkiIGZvciBlYWNoIGRhdGFzZXQuIAoKMi4gRm9yIEdOSSBhbmQgaG91cnMsIEkgY29tYmluZWQgdHdvIHZhcmlhYmxlcyAiTG9jYXRpb24iIGFuZCAiVGltZSIgdXNpbmcgInVuaXRlKCkiIGFuZCBzZXQgaXQgYXMgYSB1bmlxdWUgdmFyaWFibGUgdG8gbWF0Y2ggZm9yIGpvaW5pbmcgcHVycG9zZS4gSSByZW5hbWVkICJWYWx1ZSIgdG8gdGhlIGNvcnJlc3BvbmRpbmcgaW5kaWNhdG9ycyAiR05JIiBhbmQgIkhSV0tEIi4KCjMuIEZvciBlbXBsb3ltZW50IGFuZCBlZHVjYXRpb24sIEkgY29tYmluZWQgIkxvY2F0aW9uIiwgIlRpbWUiIGFuZCAiU3ViamVjdCIgdXNpbmcgInVuaXRlKCkiIGFuZCBzZXQgaXQgYXMgYSB1bmlxdWUgdmFyaWFibGUgdG8gbWF0Y2ggZm9yIGpvaW5pbmcgcHVycG9zZS4gSSByZW5hbWVkICJWYWx1ZSIgdG8gdGhlIGNvcnJlc3BvbmRpbmcgaW5kaWNhdG9ycyAiRU1QIiBhbmQgIkVEVSIuCgozLiBJIGpvaW5lZCBlbXBsb3ltZW50bmV3IGFuZCBlZHVjYXRpb25uZXcgYnkgIkxvY2F0aW9uX1llYXJfU3ViamVjdCIgaW50byBhIG5ldyBkYXRhc2V0IGRmMSB1c2luZyAiZnVsbF9qb2luKCkiIHRvIGtlZXAgYWxsIHRoZSB2YWx1ZXMgZnJvbSBib3RoIGRhdGFzZXRzLiBUaGVuIEkgc2VwYXJhdGVkIHRoZSB2YXJpYWJsZSAiTG9jYXRpb25fWWVhcl9TdWJqZWN0IiBpbnRvIHR3byB2YXJpYWJsZXMgIkxvY2F0aW9uX1llYXIiIGFuZCAiRWR1Y2F0aW9uX2xldmVsIiBmb3IgZnVydGhlciBqb2luaW5nIHB1cnBvc2UuIAoKNC4gSSBqb2luZWQgbWF0Y2hpbmcgcm93cyBmcm9tIEdOSW5ldyB0byBob3Vyc25ldyBieSAiTG9jYXRpb25fWWVhciIgaW50byBhIG5ldyBkYXRhc2V0IGRmMiB1c2luZyAicmlnaHRfam9pbigpIiBzbyB0aGF0IHRoZSB2YXJpYWxlICJIUldLRCIgd2FzIHBsYWNlZCB0byB0aGUgcmlnaHQgYW5kIGFsbCB2YWx1ZXMgZnJvbSBob3Vyc25ldyB3ZXJlIGtlcHQgYmVjYXVzZSBob3Vyc25ldyBvbmx5IGNvbnRhaW5lZCBjb3VudHJpZXMgdGhhdCBhcmUgT0VDRCBtZW1iZXIgY291bnRyaWVzLgoKNS4gSSBqb2luZWQgbWF0Y2hpbmcgcm93cyBmcm9tIGRmMSB0byBkZjIgYnkgIkxvY2F0aW9uX1llYXIiIGludG8gdGhlIGZpbmFsIGNvbWJpbmVkIGRhdGFzZXQgZGYgdXNpbmcgImxlZnRfam9pbigpIiB0byBrZWVwIGFsbCB2YWx1ZXMgb2YgZGYyLiBUaGVuIEkgc2VwYXJhdGVkIHRoZSB2YXJpYWJsZSAiTG9jYXRpb25fWWVhciIgaW50byAiQ291bnRyeV9jb2RlIiBhbmQgIlllYXIiLiBBdCBsYXN0LCBJIGpvaW5lZCBtYXRjaGluZyByb3dzIGZyb20gY29kZSB0byBkZiBieSAiQ291bnRyeV9jb2RlIiB1c2luZyAicmlnaHRfam9pbigpIiBzbyB0aGF0IEkgY291bGQgaGF2ZSBhIG5ldyBjb2x1bW4gIkRlZmluaXRpb24iIHRvIGNoZWNrIHRoZSBmdWxsIG5hbWUgb2YgZWFjaCBjb3VudHJ5IGNvZGUuCgojIwlUaWR5ICYgTWFuaXB1bGF0ZSBEYXRhIElJIAoKYGBge3J9CmZyZXE8LWRhdGEuZnJhbWUodGFibGUoZGYkQ291bnRyeV9jb2RlLGRmJFllYXIpKQpmcmVxPC1zcHJlYWQoZnJlcSxrZXkgPSBWYXIyLCB2YWx1ZSA9IEZyZXEpCmZyZXFbMTozLCBdI2FuIGV4YW1wbGUgb2YgZnJlcQp3aGljaChmcmVxJGAyMDE0YD09MCkKd2hpY2goZnJlcSRgMjAxNWA9PTApCndoaWNoKGZyZXEkYDIwMTZgPT0wKQpmcmVxJFZhcjFbMzo1XQpkZmZyZXExPC1kZlshKGRmJGBDb3VudHJ5X2NvZGVgPT0iQkVMIiksIF0KZGZuZXc8LWRmZnJlcTFbIShkZmZyZXExJGBDb3VudHJ5X2NvZGVgPT0iQ0hFIiksIF0KZGZuZXc8LWRmbmV3ICU+JSBncm91cF9ieShgQ291bnRyeV9jb2RlYCkgJT4lIG11dGF0ZShNZWFuX0dOST1tZWFuKHVuaXF1ZShHTkkpKSxNZWFuX2hvdXJzPW1lYW4odW5pcXVlKEhSV0tEKSkpCmRmbmV3CmBgYApBZnRlciBqb2luaW5nIHRoZSBkYXRhc2V0cywgSSBhc3N1bWVkIGFsbCBjb3VudHJpZXMgaGF2ZSBib3RoIEdOSSBhbmQgd29ya2luZyBob3VycyB2YWx1ZXMgYXZhaWxhYmxlIGZvciB0aHJlZSB5ZWFycywgMjAxNCB0byAyMDE2LiBJIGNoZWNrZWQgbXkgYXNzdW1wdGlvbiB1c2luZyAidGFibGUoKSIgdG8gc2VlIHRoZSBmcmVxdWVuY3kgb2YgYSBjb3VudHJ5IHVuZGVyIGVhY2ggeWVhci4gSSBjb252ZXJ0ZWQgdGhlIHRhYmxlIHRvIGEgZGF0YWZyYW1lIGZvciBlYXN5IGNoZWNraW5nLiBOb3JtYWxseSwgZWFjaCBjb3VudHJ5IHNob3VsZCBhcHBlYXIgdGhyZWUgdGltZXMgKGZvciB0aHJlZSBlZHVjYXRpb24gbGV2ZWxzIGluIGEgeWVhcikgb3IgYXQgbGVhc3Qgb25jZSB1bmRlciBlYWNoIHllYXIuIEkgZm91bmQgdGhhdCB0d28gY291bnRyaWVzICJCRUwiIGFuZCAiQ0hFIiBkbyBub3QgbWF0Y2ggd2l0aCB0aGlzIGNyaXRlcmlhLiBUaGV5IGhhdmUgbm8gcm93cyBmb3IgeWVhciAyMDE2LiBJdCB3YXMgYmVjYXVzZSB0aGF0IHRoZXNlIHR3byBjb3VudHJpZXMgaGF2ZSBubyAiSFJLV0QiIHZhbHVlcyBpbiB5ZWFyIDIwMTYgb3IgdGhleSBoYXZlIG5vIHZhbHVlcyBmb3IgYm90aCAiR05JIiBhbmQgIkhSS1dEIiBpbiB5ZWFyIDIwMTYuIElmIHRoZXkgaGF2ZSAiSFJLV0QiIHZhbHVlcyBpbiB5ZWFyIDIwMTYsIHRoZWlyIDIwMTYgcm93cyBzaG91bGQgYXBwcmVhciBiZWNhdXNlIGluIHRoZSBhYm92ZSBqb2luaW5nIHByb2Nlc3MgSSBrZXB0IGFsbCByb3dzIG9mIHRoZSB3b3JraW5nIGhvdXJzIGRhdGFzZXQuIAoKVG8gY2xhc3NpZnkgY291bnRyaWVzIGJhc2VkIG9uIGEgY29tYmluYXRpb24gb2YgdGhlaXIgaW5jb21lIGxldmVsIGFuZCB3b3JraW5nIGhvdXJzLCBJIG5lZWQgdG8gY2FsY3VsYXRlIGJvdGggdGhlIGF2ZXJhZ2UgR05JIGFuZCB3b3JraW5nIGhvdXJzIG92ZXIgdGhyZWUgeWVhcnMgMjAxNC0yMDE2IGZvciBlYWNoIGNvdW50cnkuIFRoZXJlZm9yZSBJIGV4Y2x1ZGVkIHRoZXNlIHR3byBjb3VudHJpZXMgaW4gbXkgcmVwb3J0LiBUaGVuIEkgY3JlYXRlZCB0d28gbmV3IGNvbHVtbnMgb2YgbWVhbiB2YWx1ZXMgdXNpbmcgImdyb3VwX2J5KCkiIGFuZCAibXV0YXRlKCkiLiBJIGFkZGVkICJ1bmlxdWUoKSIgaW4gdGhpcyBmdW5jdGlvbiB0byBlbnN1cmUgdGhlIHZhbHVlIG9mIGVhY2ggeWVhciBvbmx5IGFwcGVhcnMgb25jZSBpbiB0aGUgY2FsY3VsYXRpb24gdG8gZ2V0IHRoZSBjb3JyZWN0IG1lYW4gdmFsdWUuIAoKIyMJU2NhbiBJIApgYGB7cn0KY29sU3Vtcyhpcy5uYShkZm5ldykpCndoaWNoKGlzLm5hKGRmbmV3JERlZmluaXRpb24pKQpoZWFkKGRmbmV3WzI3NToyNzcsIF0pCmRmbmV3MTwtZGZuZXdbIShkZm5ldyRgQ291bnRyeV9jb2RlYD09Ik9FQ0QiKSwgXQoKY29sU3VtcygoaXMubmEoZGZuZXcxKSkpCndoaWNoKGlzLm5hKGRmbmV3MSRgRWR1Y2F0aW9uX2xldmVsYCkpPT13aGljaChpcy5uYShkZm5ldzEkRURVKSkKd2hpY2goaXMubmEoZGZuZXcxJGBFZHVjYXRpb25fbGV2ZWxgKSk9PXdoaWNoKGlzLm5hKGRmbmV3MSRFTVApKQp3aGljaChpcy5uYShkZm5ldzEkYEVkdWNhdGlvbl9sZXZlbGApKQpkZm5ldzFbYygxMDYsMjM2LDI0MCwyNjUpLCBdCgojQ0hMCnIxPC1kZm5ldzFbMjM3OjIzOSwgXQpkZm5ldzIgPC0gcmJpbmQoZGZuZXcxWzE6MjM2LF0scjEscjEsZGZuZXcxWy0oMToyMzYpLF0pCmMxPC1jKCIyMDE0IiwyMTkwMy4xNzIxOCwxOTkwKQpkZm5ldzJbMjM3OjIzOSwzOjVdPC1yYmluZChjMSxjMSxjMSkKYzI8LWMoIjIwMTYiLDIyMzU1Ljk4NDMsMTk3NCkKZGZuZXcyWzI0MzoyNDUsMzo1XTwtcmJpbmQoYzIsYzIsYzIpCmRmbmV3MzwtZGZuZXcyWy1jKDI0NiwyMzYpLCBdCmRmbmV3MyAlPiUgZmlsdGVyKENvdW50cnlfY29kZT09IkNITCIpCgoKI0lSTCBhbmQgUlVTCnIzPC1kZm5ldzNbMTA2LCBdCmRmbmV3NDwtcmJpbmQoZGZuZXczWzE6MTA2LCBdLHIzLHIzLGRmbmV3M1stKDE6MTA2KSwgXSkKcjQ8LWRmbmV3NFsyNzEsIF0KZGZuZXc1PC1yYmluZChkZm5ldzRbMToyNzEsIF0scjQscjQsZGZuZXc0Wy0oMToyNzEpLCBdKQpjMzwtYygiQmVsb3dfdXBwZXJfc2Vjb25kYXJ5IiwiVGVydGlhcnkiLCJVcHBlcl9zZWNvbmRhcnkiKQpkZm5ldzVbMTA2OjEwOCw2XTwtYzMKZGZuZXc1WzI3MToyNzMsNl08LWMzCmRmbmV3NjwtZGZuZXc1JT4lZ3JvdXBfYnkoYENvdW50cnlfY29kZWAsYEVkdWNhdGlvbl9sZXZlbGApJT4lIG11dGF0ZShFRFU9aWZlbHNlKGlzLm5hKEVEVSksbWVhbihFRFUsbmEucm09VFJVRSksRURVKSkgJT4lIG11dGF0ZShFTVA9aWZlbHNlKGlzLm5hKEVNUCksbWVhbihFTVAsbmEucm09VFJVRSksRU1QKSkKZGZuZXc2ICU+JSBmaWx0ZXIoQ291bnRyeV9jb2RlPT0iSVJMIikKZGZuZXc2ICU+JSBmaWx0ZXIoQ291bnRyeV9jb2RlPT0iUlVTIikKYGBgCioqTWlzc2luZyBWYWx1ZToqKgpJIGNoZWNrZWQgdGhlIG51bWJlciBvZiBtaXNzaW5nIHZhbHVlcyBpbiBlYWNoIGNvbHVtbi4gVGhlIHRocmVlIG1pc3NpbmcgdmFsdWVzIGluIHZhcmlhYmxlICJEZWZpbml0aW9uIiBhcmUgIk9FQ0QiLCB3aGljaCByZXByZXNlbnRzIGFuIGF2ZXJhZ2UgdmFsdWUgZm9yIGFsbCBPRUNEIGNvdW50cmllcy4gSSBkZWxldGVkIHRoZXNlIHRocmVlIHJvd3MgYmVjYXVzZSBJIHdhbnQgdG8gc3R1ZHkgZWFjaCBjb3VudHJ5IHNlcGFyYXRlbHkuIEZvciBhbGwgdGhlIG90aGVyIG1pc3NpbmcgdmFsdWVzIGluICJFZHVjYXRpb25fbGV2ZWwiLCJFTVAiIGFuZCAiRURVIiwgSSBjaGVja2VkIHRoYXQgdGhleSB3ZXJlIGluIHRoZSBzYW1lIHJvd3MuIFRocmVlIGNvdW50cmllcyBpbmNsdWRlZCBtaXNzaW5nIHZhbHVlczogSXJlbGFuZCwgQ2hpbGUgYW5kIFJ1c3NpYS4gQ2hpbGUgb25seSBoYWQgZW1wbG95bWVudCByYXRlIGFuZCBlZHVjYXRpb25hbCBhdHRhaW5tZW50IGF2YWlsYWJsZSBpbiB5ZWFyIDIwMTUuIEkgY29waWVkIGl0cyAiRU1QIiBhbmQgIkVEVSIgdmFsdWVzIGZvciBlYWNoIGVkdWNhdGlvbiBsZXZlbCBvZiB5ZWFyIDIwMTUgdG8geWVhciAyMDE0IGFuZCAyMDE2LiBGb3IgSXJlbGFuZCBhbmQgUnVzc2lhLCB0aGV5IGhhZCBlbXBsb3ltZW50IHJhdGUgYW5kIGVkdWNhdGlvbmFsIGF0dGFpbm1lbnQgYXZhaWxibGUgaW4geWVhciAyMDE0IGFuZCAyMDE1LiBJIGltcHV0ZWQgdGhlIGF2ZXJhZ2UgdmFsdWVzIG9mIGVtcGxveW1lbnQgcmF0ZSBhbmQgZWR1Y2F0aW9uYWwgYXR0YWlubWVudCBmb3IgZWFjaCBlZHVjYXRpb24gbGV2ZWwgaW4geWVhciAyMDE0IGFuZCB5ZWFyIDIwMTUgdG8geWVhciAyMDE2LgoKYGBge3J9CmRmbmV3NiRHTkk8LWFzLm51bWVyaWMoZGZuZXc2JEdOSSkKZGZuZXc2JEhSV0tEPC1hcy5pbnRlZ2VyKGRmbmV3NiRIUldLRCkKZGZuZXc2JEVkdWNhdGlvbl9sZXZlbDwtYXMuZmFjdG9yKGRmbmV3NiRFZHVjYXRpb25fbGV2ZWwpCm08LWNiaW5kKGRmbmV3NiRHTkksZGZuZXc2JEhSV0tELGRmbmV3NiRFRFUsZGZuZXc2JEVNUCxkZm5ldzYkTWVhbl9HTkksZGZuZXc2JE1lYW5faG91cnMpICN3aXRoIGEgcmVhc29uCnN1bShpcy5uYW4obSkpCnN1bShpcy5pbmZpbml0ZShtKSkgCmBgYAoqKlNwZWNpYWwgVmFsdWU6KioKSSBjaGVja2VkIHRoZSB0eXBlIG9mIHZhcmlhYmxlcyBhZ2FpbiBhbmQgZm91bmQgdGhhdCAiR05JIiwiSFJXS0QiIGFuZCAiRWR1Y2F0aW9uX2xldmVsIiBoYXZlIGJlY29tZSBjaGFyYWN0ZXJzLiBJdCB3YXMgcHJvYmFibHkgZHVlIHRvIHRoZSBhYm92ZSAidW5pdGUoKSIsICJzZXBhcmF0ZSgpIiBhbmQgInJiaW5kKCkiIHByb2Nlc3MgdGhhdCBmb3JjZWQgYWxsIHZhcmlhYmxlcyBpbnRvIG9uZSB0eXBlLiAiRWR1Y2F0aW9uX2xldmVsIiB3YXMgY29udmVydGVkIHRvIGZhY3RvcnMuICJHTkkiIGFuZCAiSFJXS0QiIHdlcmUgY29udmVydGVkIHRvIG51bWVyaWMgdmFsdWVzIGZvciBmdXJ0aGVyIGNoZWNraW5nIG9uIHNwZWNpYWwgdmFsdWVzLiBGdW5jdGlvbnMgImlzLm5hbigpIiBhbmQgImlzLmluZmluaXRlKCkiIG9ubHkgd29yayBmb3IgdmVjdG9yaWFsIGlucHV0LiBUaGVyZWZvcmUgSSBjb21iaW5lZCBhbGwgbnVtZXJpYyB2YWx1ZXMgaW50byBvbmUgbWF0cml4LiBJdCB0dXJuZWQgb3V0IHRoYXQgdGhlcmUgd2VyZSBubyBzcGVjaWFsIHZhbHVlcy4KCmBgYHtyfQpzdW0oZGZuZXc2JEdOSTwwKQpzdW0oZGZuZXc2JEhSV0tEPDApCnN1bShkZm5ldzYkRURVPD0wIHxkZm5ldzYkRURVPj0xMDApCnN1bShkZm5ldzYkRU1QPD0wIHxkZm5ldzYkRU1QPj0xMDApCnN1bShkZm5ldzYkTWVhbl9HTkk8MCkKc3VtKGRmbmV3NiRNZWFuX2hvdXJzPDApCmNoZWNrRURVMTAwPC1kZm5ldzYgJT4lIGdyb3VwX2J5KGBDb3VudHJ5X2NvZGVgLFllYXIpICU+JSBtdXRhdGUoc3VtRURVPXN1bShFRFUpKQp3aGljaChyb3VuZChjaGVja0VEVTEwMCRzdW1FRFUsZGlnaXRzID0gMCkhPTEwMCkKZGZuZXc2WzExODoxMjAsXQpgYGAKKipJbmNvbnNpc3RlbmN5OioqCgoqIEFsbCB2YWx1ZXMgb2YgIkdOSSIsIkhSV0tEIiwiTWVhbl9HTkkiIGFuZCAiTWVhbl9ob3VycyIgc2hvdWxkIGJlIGdyZWF0ZXIgdGhhbiB6ZXJvLiBBZnRlciBjaGVja2luZywgdGhlIGRhdGFzZXQgb2JleXMgdGhpcyBydWxlLgoqIEFsbCB2YWx1ZXMgb2YgIkVEVSIgYW5kICJFTVAiIGFyZSBwZXJjZW50YWdlcywgdGhlcmVmb3JlIHRoZXkgc2hvdWxkIGJlIHdpdGhpbiAwLTEwMC4gQWZ0ZXIgY2hlY2tpbmcsIHRoZSBkYXRhc2V0IG9iZXlzIHRoaXMgcnVsZS4KKiBGb3IgZWFjaCB5ZWFyIGluIGV2ZXJ5IGNvdW50cnksIHRoZSBzdW0gb2YgIkVEVSIgb2YgdGhyZWUgZWR1Y2F0aW9uIGxldmVscyBzaG91bGQgYmUgMTAwLiBUaGUgZWR1Y2F0aW9uYWwgYXR0YWlubWVudCAoIkVEVSIpIHJlcHJlc2VudHMgdGhlIHBlcmNlbnRhZ2Ugb2YgdGhlIDI1LTY0IHllYXItb2xkcyB3aG8gYWNoaWV2ZWQgVGVydGlhcnkvVXBwZXIgc2Vjb25kYXJ5L0JlbG93IHVwcGVyIHNlY29uZGFyeSBhcyB0aGUgaGlnaGVzdCBlZHVjYXRpb24gbGV2ZWwuIFRoZXNlIHRocmVlIGVkdWNhdGlvbiBsZXZlbHMgY292ZXIgYWxsIHRoZSBzaXR1dGF0aW9ucy4gQWZ0ZXIgY2hlY2tpbmcsIEphcGFuIGRvZXMgbm90IGZ1bGZpbCB0aGUgcmVxdWlybWVudHMgYmVjYXVzZSBpdCBvbmx5IGhhcyB0aGUgdmFsdWVzIG9mICJFRFUiIGZvciBUZXJ0aWFyeSBsZXZlbC4gSSBkaWQgbm90IGFwcGx5IHRoZSBzYW1lIG1ldGhvZCB0byBjaGVjayAiRU1QIiBiZWNhdXNlIHRoZSBlbXBsb3ltZW50IHJhdGUgcmVwcmVzZW50cyB0aGUgcGVyY2VudGFnZSBvZiBlbXBsb3llZCAyNS02NCB5ZWFyLW9sZHMgYnkgYWxsIDI1LTY0IHllYXItb2xkcyB3aXRoIHRoZSBzYW1lIGVkdWNhdGlvbiBsZXZlbC4gVGhleSBjYW5ub3QgYmUgYWRkZWQgdXAgYmFzZWQgb24gZGlmZmVyZW50IHBvcHVsYXRpb24uCgojIwlTY2FuIElJCgpgYGB7cn0Kei5zY29yZXM8LWRmbmV3NiRHTkkgJT4lc2NvcmVzKHR5cGU9InoiKQp6LnNjb3JlcyAlPiUgc3VtbWFyeSgpCndoaWNoKGFicyh6LnNjb3Jlcyk+MykKei5zY29yZXM8LWRmbmV3NiRIUldLRCAlPiVzY29yZXModHlwZT0ieiIpCnouc2NvcmVzICU+JSBzdW1tYXJ5KCkKd2hpY2goYWJzKHouc2NvcmVzKT4zKQp6LnNjb3JlczwtZGZuZXc2JE1lYW5fR05JICU+JXNjb3Jlcyh0eXBlPSJ6IikKei5zY29yZXMgJT4lIHN1bW1hcnkoKQp3aGljaChhYnMoei5zY29yZXMpPjMpCnouc2NvcmVzPC1kZm5ldzYkTWVhbl9ob3VycyAlPiVzY29yZXModHlwZT0ieiIpCnouc2NvcmVzICU+JSBzdW1tYXJ5KCkKd2hpY2goYWJzKHouc2NvcmVzKT4zKQpsZW5ndGgoYm94cGxvdChkZm5ldzYkRURVIH4gZGZuZXc2JEVkdWNhdGlvbl9sZXZlbCwgbWFpbj0iRWR1Y2F0aW9uYWwgYXR0YWlubWVudCBieSBFZHVjYXRpb24gbGV2ZWwiLCB5bGFiID0gIkVkdWNhdGlvbiBsZXZlbCIsIHhsYWIgPSAiRWR1Y2F0aW9uYWwgYXR0YWlubWVudCIpJG91dCkKbGVuZ3RoKGJveHBsb3QoZGZuZXc2JEVNUCB+IGRmbmV3NiRFZHVjYXRpb25fbGV2ZWwsIG1haW49IkVtcGxveW1lbnQgcmF0ZSBieSBFZHVjYXRpb24gbGV2ZWwiLCB5bGFiID0gIkVkdWNhdGlvbiBsZXZlbCIsIHhsYWIgPSAiRW1wbG95bWVudCByYXRlIikkb3V0KQojaWRlbnRpZnkgdGhlIDkgb3V0bGllcnMgaW4gZWR1Y2F0aW9uYWwgYXR0YWlubWVudCBhYm92ZSB0aGUgdXBwZXIgZmVuY2UgZm9yICJCZWxvd191cHBlcl9zZWNvbmRhcnkiIGxldmVsCm91dGxpZXJFRFU8LWRmbmV3NiAlPiUgZmlsdGVyKEVkdWNhdGlvbl9sZXZlbD09IkJlbG93X3VwcGVyX3NlY29uZGFyeSIpICU+JSBzZWxlY3QoLUVNUCkKb3V0bGllckVEVTwtb3V0bGllckVEVVtvcmRlcihvdXRsaWVyRURVJEVEVSxkZWNyZWFzaW5nPVRSVUUpLCBdCm91dGxpZXJFRFVbMTo5LCBdCiNpZGVudGlmeSB0aGUgMSBvdXRsaWVycyBpbiBlbXBsb3ltZW50IHJhdGUgYWJvdmUgdGhlIHVwcGVyIGZlbmNlIGZvciAiVGVydGlhcnkiIGxldmVsCm91dGxpZXJFTVAxPC1kZm5ldzYgJT4lIGZpbHRlcihFZHVjYXRpb25fbGV2ZWw9PSJUZXJ0aWFyeSIpICU+JSBzZWxlY3QoLUVEVSkKb3V0bGllckVNUDF1cDwtb3V0bGllckVNUDFbb3JkZXIob3V0bGllckVNUDEkRU1QLGRlY3JlYXNpbmc9VFJVRSksIF0Kb3V0bGllckVNUDF1cFsxLCBdCiNpZGVudGlmeSB0aGUgMyBvdXRsaWVycyBpbiBlbXBsb3ltZW50IHJhdGUgYmVsb3cgdGhlIGxvd2VyIGZlbmNlIGZvciAiVGVydGlhcnkiIGxldmVsCm91dGxpZXJFTVAxbG93PC1vdXRsaWVyRU1QMVtvcmRlcihvdXRsaWVyRU1QMSRFTVApLCBdCm91dGxpZXJFTVAxbG93WzE6MywgXQojaWRlbnRpZnkgdGhlIDMgb3V0bGllciBpbiBlbXBsb3ltZW50IHJhdGUgYmVsb3cgdGhlIGxvd2VyIGZlbmNlIGZvciAiVXBwZXJfc2Vjb25kYXJ5IiBsZXZlbApvdXRsaWVyRU1QMjwtZGZuZXc2ICU+JSBmaWx0ZXIoRWR1Y2F0aW9uX2xldmVsPT0iVXBwZXJfc2Vjb25kYXJ5IikgJT4lIHNlbGVjdCgtRURVKQpvdXRsaWVyRU1QMjwtb3V0bGllckVNUDJbb3JkZXIob3V0bGllckVNUDIkRU1QKSwgXQpvdXRsaWVyRU1QMlsxOjMsIF0KYGBgCiogRm9yICJHTkkiLCJIUldLRCIsIk1lYW5fR05JIiBhbmQgIk1lYW5faG91cnMiLCBJIGFwcGxpZWQgei1zY29yZSBtZXRob2QgdG8gaWRlbnRpZnkgb3V0bGllcnMuIEFmdGVyIGNoZWNraW5nLCB0aGVyZSB3ZXJlIG5vIHN1Z2dlc3RlZCBvdXRsaWVycyBmb3IgdGhlbS4gSXQgbWF5IGltcGx5IHRoYXQgdGhlIHZhbHVlcyBvZiBncm9zcyBuYXRpb25hbCBpbmNvbWUgYW5kIHdvcmtpbmcgaG91cnMgZm9yIHRoZXNlIGNvdW50cmllcyByZW1haW4gaW4gYSByZWFzb25hYmxlIHJhbmdlLiBObyBjb3VudHJ5IHN0YW5kcyBvdXQgcmVnYXJkaW5nIGdyb3NzIG5hdGlvbmFsIGluY29tZSBvciB3b3JraW5nIGhvdXJzLgoqIEZvciB0aGUgZW1wbG95bWVudCByYXRlICgiRU1QIikgYW5kIGVkdWNhdGlvbmFsIGF0dGFpbm1lbnQgKCJFRFUiKSwgSSBhcHBsaWVkIGJpdmFyaWF0ZSBib3ggcGxvdHMgc28gdGhhdCBJIGNhbiBkZXRlY3Qgb3V0bGllcnMgYnkgZWR1Y2F0aW9uIGxldmVscy4gOSBvdXRsaWVycyB3ZXJlIGlkZW50aWZpZWQgaW4gdGhlIGVkdWNhdGlvbmFsIGF0dGFpbm1lbnQgZm9yICJCZWxvd191cHBlcl9zZWNvbmRhcnkiIGxldmVsLiA0IG91dGxpZXJzIHdlcmUgaWRlbnRpZmllZCBpbiB0aGUgZW1wbG95bWVudCByYXRlIGZvciAiVGVydGlhcnkiIGxldmVsLCB3aXRoIDEgYWJvdmUgdGhlIHVwcGVyIGZlbmNlIGFuZCAzIGJlbG93IHRoZSBsb3dlciBmZW5jZS4gMyBvdXRsaWVycyB3ZXJlIGlkZW50aWZpZWQgaW4gdGhlIGVtcGxveW1lbnQgcmF0ZSBmb3IgIlVwcGVyX3NlY29uZGFyeSIgbGV2ZWwuIEkgZGVjaWRlZCBub3QgdG8gZGVhbCB3aXRoIHRoZXNlIG91dGxpZXJzLiBJIGNvbXBhcmVkIHRoZSBkYXRhIG9uIGEgZ2xvYmFsIHNjYWxlLiBJdCBpcyByZWFzb25hYmxlIGZvciBkaWZmZXJlbnQgY291bnRyaWVzIHRvIGhhdmUgZGlmZmVyZW50IGNvbmRpdGlvbnMgb24gdGhlaXIgZWR1Y2F0aW9uIGFuZCBlbXBsb3ltZW50LiAKCiMjCVRyYW5zZm9ybSAKYGBge3J9CmJpbl9HTkk8LWRpc2NyZXRpemUoZGZuZXc2JE1lYW5fR05JLCBkaXNjID0gImVxdWFsd2lkdGgiLG5iaW5zPTMpCmRmbmV3NzwtYmluZF9jb2xzKGRmbmV3NixiaW5fR05JKQpiaW5faG91cnM8LWRpc2NyZXRpemUoZGZuZXc3JE1lYW5faG91cnMsZGlzYz0iZXF1YWx3aWR0aCIsIG5iaW5zPTMpCmRmbmV3ODwtYmluZF9jb2xzKGRmbmV3NyxiaW5faG91cnMpCgpkZm5ldzg8LSBkZm5ldzggJT4lIG11dGF0ZShYPWZhY3RvcihYLGxldmVscz1jKDEsMiwzKSxsYWJlbHMgPWMoIkxvd19pbmNvbWUiLCJNaWRkbGVfaW5jb21lIiwiSGlnaF9pbmNvbWUiKSkpICU+JSBtdXRhdGUoWDE9ZmFjdG9yKFgxLGxldmVscz1jKDEsMiwzKSxsYWJlbHM9YygiU2hvcnRfaG91cnMiLCJNZWRpdW1faG91cnMiLCJMb25nX2hvdXJzIikpKSAlPiUgdW5pdGUoQ2xhc3MsWCxYMSxzZXAgPSAiJiIpCmRmbmV3OApDb3VudHJ5X2NsYXNzPC1kZm5ldzggJT4lIHVuZ3JvdXAoKSAlPiUgZGlzdGluY3QoQ291bnRyeV9jb2RlLENsYXNzKQpDb3VudHJ5X2NsYXNzW29yZGVyKENvdW50cnlfY2xhc3MkQ2xhc3MpLCBdCmBgYAoqIEkgYXBwbGllZCBiaW5uaW5nIG1ldGhvZCB0byBkaXZpZGUgIk1lYW5fR05JIiBhbmQgIk1lYW5faG91cnMiIGZvciBzZXR0aW5nIHVwIHRoZSBzY2FsZSBvZiB0aGUgY2xhc3NpZmljYXRpb24uIFRoZSB2YXJpYWJsZXMgd2VyZSBkaXZpZGVkIGludG8gMyBpbnRlcnZhbHMgb2YgZXF1YWwgd2lkdGggdXNpbmcgImRpc2NyZXRpemUoKSIuIFRoZW4gSSBhZGRlZCB0aGUgYmluaW5nIGNvbHVtbnMgdG8gbXkgZGF0YXNldCB1c2luZyAiYmluZF9jb2xzKCkiLiBJIGNvbnZlcnRlZCB0aGUgYmluaW5pbmcgY29sdW1ucyB0byBmYWN0b3JzIGFuZCBsYWJlbGxlZCB0aGUgdmFsdWVzIHVzaW5nICJtdXRhdGUoKSIgYW5kICJmYWN0b3IoKSIuIFRoZW4gSSBjb21iaW5lZCB0aGUgdHdvIGJpbmluZyBjb2x1bW5zIGludG8gb25lIHZhcmlhYmxlICJDbGFzcyIuIAoqIEkgY3JlYXRlZCBhIG5ldyBkYXRhc2V0IENvdW50cnlfY2xhc3MgZm9yIGEgZWFzeSByZWZlcmVuY2Ugb24gdGhlIGNsYXNzaWZpY2F0aW9uLiBJIHVzZWQgInVuZ3JvdXAoKSIgdG8gZHJvcCBlYWNoIGNvdW50cnkncyBncm91cGluZyB3aXRoIHRoZSB0aHJlZSBlZHVjYXRpb24gbGV2ZWxzLiBUaGVuIEkgdXNlZCAiZGlzdGluY3QoKSIgdG8gcmVtb3ZlIGR1cGxpY2F0ZWQgcm93cyBiYXNlZCBvbiAiQ291bnRyeV9jb2RlIi4KCjxicj4KPGJyPgo=