Required packages
Following are the packages below that I have used for this assignment.
# This is the R chunk for the required packages
library(readr)
library(dplyr)
library(outliers)
library(ggplot2)
library(tidyr)
library(validate)
library(lubridate)
library(knitr)
library(Hmisc)
Executive Summary
The main aim of the assignment is to preprocess a open source untidy dataset. I have downloaded the dataset from Kaggle. The data file contains two datasets named athlete_events.csv and noc_regions.csv. Then the datasets are imported using read_csv() function from the readr library. After importing the datasets I have subsetted both the datasets using select() function as some of the variables are not required for the further process. Later I have used left join to join both the datasets using common variable NOC. Then using the str() function I get the data types of the variables. Data type of some variables like Sex, Season, Medal should be factor but it is shown as character thats why I converted it using as.factor() function. Later I used head() function to check whether the dataset is tidy or not. After concluding that the dataset is tidy I created a new variable named Body Mass Index using Height, Weight variables and mutate() function. In the scan section I have scanned the dataset for missing values using colSums() and is.na() function. I have imputed the Null values in numeric variables using the mean() function. Null values in the categorical variables are imputed by creating a new category. Then I have scanned for outliers by plotting the numeric variables using boxplot(). I have calculated the percentage of outliers using score() and which() function. Outliers were capped using cap() function. The variables were plotted using boxplot() to check if there are outliers. After removing outliers using hist() the distribution of the variables was analysed. Age and Weight were transformed using log10 transformation as their graph was right skewed. Height was normally distributed thats why it is not transformed.
Data
The following dataset is historical dataset of olyampic games from 1896 to 2016. The dataset is downloade from kaggle. Link of the data source is given below-
https://www.kaggle.com/heesoo37/120-years-of-olympic-history-athletes-and-results
The dataset contains two tables named athlete_events.csv and noc_regions.csv. First table contains information about the athletes and the olyampic events that they have participated and second table contains information about the countries that participated in the olyampic games. The athlete_events.csv contains following variables- 1) ID- A unique identifier for each athlete 2) Name- Name of the athlete 3) Sex- Gender of athlete 4) Age Age of athlete 5) Height- Height of athlete in cm 6) Weight- Weight of an athlete in kgs 7) Team- Name of the Team 8) NOC- National olyampic committee 3-letter code 9) Games- Year and Season of the olyampic game 10) Year- Year of the olyampic 11) Season- Season of the olyampic 12) City- Name of the city that hosted the olyampics 13) Sport- Sport that the athlete participated 14) Event- Event of the sport in which the athlete participated 15) Medal- Medal won by the athlete.
The noc_regions.csv contains following variables- 1) NOC- National olyampic committee 3-letter code 2) region- Country name of the respective NOC 3) notes- Some information about the region.
Importing and Subsetting the Data
Here I have used the read_csv() function from the readr library to import both the datasets. athlete_events.csv dataset is assigned to object player_data and noc_regions.csv datadet is assigned to the object country_data. Games variable consist of year and season information combined and seperating the variable would give us the variables like Year and Season which already exist thats why I have excluded the Games variable using the select() function and store all the other variable in object Player_data. Similarly the note variable in the countries data is not useful for further analysis therefore I have selected only two variables NOC and region and stored it in an object country_data Now after subsetting both the datasets I have merged both the datasets using left_join based on the common key variable NOC. The merged datset is stored in the object new. After that using the head() function I have displayed the new dataset and checked if every column is included after joining both the datasets
#Importing the first dataset using read_csv
player_data <- read_csv("/Users/christangelfargose/120-years-of-olympic-history-athletes-and-results/athlete_events.csv")
Parsed with column specification:
cols(
ID = [32mcol_double()[39m,
Name = [31mcol_character()[39m,
Sex = [31mcol_character()[39m,
Age = [32mcol_double()[39m,
Height = [32mcol_double()[39m,
Weight = [32mcol_double()[39m,
Team = [31mcol_character()[39m,
NOC = [31mcol_character()[39m,
Games = [31mcol_character()[39m,
Year = [32mcol_double()[39m,
Season = [31mcol_character()[39m,
City = [31mcol_character()[39m,
Sport = [31mcol_character()[39m,
Event = [31mcol_character()[39m,
Medal = [31mcol_character()[39m
)
#Subsetting the dataset by removing the 9th column
player_data <- select(player_data,-9)
head(player_data)
#Importing country data using read_csv
country_data <- read_csv("/Users/christangelfargose/120-years-of-olympic-history-athletes-and-results/noc_regions.csv")
Parsed with column specification:
cols(
NOC = [31mcol_character()[39m,
region = [31mcol_character()[39m,
notes = [31mcol_character()[39m
)
#Subsetting the country_data
country_data <- country_data[,c(1,2)]
head(country_data)
#Joining both the datasets using left join
new <- left_join(player_data,country_data,by="NOC")
head(new)
NA
NA
Understand
The new dataset consist of total 15 variables and 271116 rows. The data type of variables can be found out using str() function
#Displaying the structure of dataset and checking data types
str(new)
tibble [271,116 × 15] (S3: tbl_df/tbl/data.frame)
$ ID : num [1:271116] 1 2 3 4 5 5 5 5 5 5 ...
$ Name : chr [1:271116] "A Dijiang" "A Lamusi" "Gunnar Nielsen Aaby" "Edgar Lindenau Aabye" ...
$ Sex : chr [1:271116] "M" "M" "M" "M" ...
$ Age : num [1:271116] 24 23 24 34 21 21 25 25 27 27 ...
$ Height: num [1:271116] 180 170 NA NA 185 185 185 185 185 185 ...
$ Weight: num [1:271116] 80 60 NA NA 82 82 82 82 82 82 ...
$ Team : chr [1:271116] "China" "China" "Denmark" "Denmark/Sweden" ...
$ NOC : chr [1:271116] "CHN" "CHN" "DEN" "DEN" ...
$ Year : num [1:271116] 1992 2012 1920 1900 1988 ...
$ Season: chr [1:271116] "Summer" "Summer" "Summer" "Summer" ...
$ City : chr [1:271116] "Barcelona" "London" "Antwerpen" "Paris" ...
$ Sport : chr [1:271116] "Basketball" "Judo" "Football" "Tug-Of-War" ...
$ Event : chr [1:271116] "Basketball Men's Basketball" "Judo Men's Extra-Lightweight" "Football Men's Football" "Tug-Of-War Men's Tug-Of-War" ...
$ Medal : chr [1:271116] NA NA NA "Gold" ...
$ region: chr [1:271116] "China" "China" "Denmark" "Denmark" ...
Conversion
As shown from the above output the data types of ID,Age,Height,Weight,Year is numeric which is correct. Also the data types of Name, Team, NOC,Event, Sport,City,region is character which is correct. Variables like Sex,Season,Medal should be factors but are shown as character thats why they should be changed to factors. I have used as.factor() function to change the data type of variables Sex, Season and Medal. I have also ordered the Medal variable in the order Gold, Silver and then Bronze. After converting the data type to factors I have used class() function to check the class of the variables and also used the str() function to check if the data types are changed.
#Converting Sex to factor and checking it using class
new$Sex <- as.factor(new$Sex)
class(new$Sex)
[1] "factor"
#Converting Season to factor and checking it using class
new$Season <- as.factor(new$Season)
class(new$Season)
[1] "factor"
#Converting Medal to factor and checking it using class
new$Medal <- as.factor(new$Medal)
new$Medal <- factor(new$Medal,levels = c("Gold","Silver","Bronze"),ordered = TRUE)
class(new$Medal)
[1] "ordered" "factor"
#Checking whether the data type is changed
str(new)
tibble [271,116 × 15] (S3: tbl_df/tbl/data.frame)
$ ID : num [1:271116] 1 2 3 4 5 5 5 5 5 5 ...
$ Name : chr [1:271116] "A Dijiang" "A Lamusi" "Gunnar Nielsen Aaby" "Edgar Lindenau Aabye" ...
$ Sex : Factor w/ 2 levels "F","M": 2 2 2 2 1 1 1 1 1 1 ...
$ Age : num [1:271116] 24 23 24 34 21 21 25 25 27 27 ...
$ Height: num [1:271116] 180 170 NA NA 185 185 185 185 185 185 ...
$ Weight: num [1:271116] 80 60 NA NA 82 82 82 82 82 82 ...
$ Team : chr [1:271116] "China" "China" "Denmark" "Denmark/Sweden" ...
$ NOC : chr [1:271116] "CHN" "CHN" "DEN" "DEN" ...
$ Year : num [1:271116] 1992 2012 1920 1900 1988 ...
$ Season: Factor w/ 2 levels "Summer","Winter": 1 1 1 1 2 2 2 2 2 2 ...
$ City : chr [1:271116] "Barcelona" "London" "Antwerpen" "Paris" ...
$ Sport : chr [1:271116] "Basketball" "Judo" "Football" "Tug-Of-War" ...
$ Event : chr [1:271116] "Basketball Men's Basketball" "Judo Men's Extra-Lightweight" "Football Men's Football" "Tug-Of-War Men's Tug-Of-War" ...
$ Medal : Ord.factor w/ 3 levels "Gold"<"Silver"<..: NA NA NA 1 NA NA NA NA NA NA ...
$ region: chr [1:271116] "China" "China" "Denmark" "Denmark" ...
Tidy & Manipulate Data I
We will check the dataset using head() function. In the dataset each variable have its own column and each observation is stored in a seperate row. Plus each value is stored in its own cell therfore the dataset is tidy and there is no necessity to reshape the dataset. The dataset can be used for further process.
# Using the head() function to check if the dataset is tidy or not
head(new)
Tidy & Manipulate Data II
Here we have created a new variable Body mass index using the mutate() function of the dplyr package. Body mass index was created using the variables Height and Weight. Body mass index is a measurement of persons weight with respect to their height.Following formula was used to create the variable-
Body Mass Index = Weight/(Height)^2
Here I have used 10000 because the height in the formula should be in meter but in the data we have it in cm so we need to convert it.
# Creating variable Body Mass Index using mutate() function
new <- mutate(new,Body_Mass_Index=round(new$Weight/(new$Height * new$Height) *10000))
head(new)
Scan I
First I have used colSums() function with is.na() to find the NULL values in each variables of the dataset.
# Displaying the count of Null values in each variable of dataset
colSums(is.na(new))
ID Name Sex Age
0 0 0 9474
Height Weight Team NOC
60171 62875 0 0
Year Season City Sport
0 0 0 0
Event Medal region Body_Mass_Index
0 231333 370 64263
According to the above output we can conclude that there are missing values in the Age, Height, Weight, Medal, region and Body_Mass_Index variables. Age, Height, Weight, Body_Mass_Index are numeric variables and Medal and region are categorical that is why they should be handled diffrently. The NULL value count of variables are high therfore they cannot be excluded as this might affect the analysis process therefore the null values must be imputed. I have imputed the numeric NULL values by using mean() function along with is.na()
#Imputing numeric variables with the mean of the particular column
new$Age[is.na(new$Age)] <- round(mean(new$Age, na.rm = TRUE))
new$Weight[is.na(new$Weight)] <- mean(new$Weight, na.rm = TRUE)
new$Height[is.na(new$Height)] <- round(mean(new$Height, na.rm = TRUE))
new$Body_Mass_Index[is.na(new$Body_Mass_Index)] <- round(mean(new$Body_Mass_Index, na.rm = TRUE))
head(new)
NA
NA
The categorical variables are imputed by forming a new category in the variable. Here I have formed a category “No Medal” in the variable Medal and replace all the null values with that category using is.na() function. The medal variable is factor so we need to convert it to character. Similarly new category named “Unknown” was formed in the variable region and all null values were imputed with this category using is.na() function
#Imputing categorical variable by creating a new category
new$Medal<- as.character(new$Medal)
new$Medal[is.na(new$Medal)] <- "No Medal"
new$Medal <- as.factor(new$Medal)
new$Medal <-factor(new$Medal,levels = c("Gold","Silver","Bronze","No Medal"),ordered = TRUE)
new$region[is.na(new$region)] <- "Unknown"
head(new)
NA
NA
Lastly all the numeric variables were scanned for missing values using sum() function along with is.infinite() function to check the special values in the dataset. Finally to check whether all the null values were removed I have used colSums() function along with is.na() function
#Scanning for special values in numeric variables of the dataset
sum(is.infinite(new$ID))
[1] 0
sum(is.infinite(new$Age))
[1] 0
sum(is.infinite(new$Height))
[1] 0
sum(is.infinite(new$Weight))
[1] 0
sum(is.infinite(new$Body_Mass_Index))
[1] 0
sum(is.infinite(new$Year))
[1] 0
colSums(is.na(new))
ID Name Sex Age
0 0 0 0
Height Weight Team NOC
0 0 0 0
Year Season City Sport
0 0 0 0
Event Medal region Body_Mass_Index
0 0 0 0
Scan II
An outlier is an observation which stand out from other observations. Outlier can be because of data entry error, Measurement error, Data processing error or many other reasons. To scan the outliers I have plotted the numeric variables using boxplot function from the ggplot2 library. I have not checked the numeric variables like ID and year for outliers because ID is unique and there cant be a outlier for ID. Similar is the case for year. Here I have used Turkeys method of outlier detection using boxplot.
# Ploting numeric variables using boxplot() to check the outliers
#Referred from week8 lecture slides and worksheet
new$Age %>% boxplot(main="Boxplot Age",ylab="Age",col = "green")

new$Height %>% boxplot(main="Boxplot Height",ylab="Height",col = "blue")

new$Weight %>% boxplot(main="Boxplot Weight",ylab="Weight",col = "red")

new$Body_Mass_Index %>% boxplot(main="Boxplot Body Mass Index",ylab="Body Mass Index",col = "orange")

From the above outputs we can see that there are outliers in each numeric variables that we have plotted using the boxplot.Now to check the number and percentage of outliers I have used the z-score value along with the score() and summary() function. Summary() function gives us the summary of the variable. By using lenght() function along with which() function and total number of rows I have calculated the percentage of outliers in a variable.The percentage of outliers value is stored in objects like age_outliers, height_outliers, weight_outliers, body_mass outliers
#Finding the percentage of outliers using scores, summary and which function
#Referred from week8 lecture slides and worksheet
z.age <- new$Age %>% scores(type = "z")
z.age %>% summary()
Min. 1st Qu. Median Mean 3rd Qu. Max.
-2.47913 -0.56873 -0.09112 0.00000 0.38648 11.37132
age_ouliers <- (length (which( abs(z.age) >3 )) / 271116) * 100
age_ouliers
[1] 1.625135
z.height <- new$Height %>% scores(type = "z")
z.height %>% summary()
Min. 1st Qu. Median Mean 3rd Qu. Max.
-5.20130 -0.56726 -0.02842 0.00000 0.51042 5.46776
height_ouliers <- (length (which( abs(z.height) >3 )) / 271116) * 100
height_ouliers
[1] 0.7513389
z.weight <- new$Weight %>% scores(type = "z")
z.weight %>% summary()
Min. 1st Qu. Median Mean 3rd Qu. Max.
-3.6345 -0.6125 0.0000 0.0000 0.3418 11.3957
weight_ouliers <- (length (which( abs(z.weight) >3 )) / 271116) * 100
weight_ouliers
[1] 1.144897
z.bodymass <- new$Body_Mass_Index %>% scores(type = "z")
z.bodymass %>% summary()
Min. 1st Qu. Median Mean 3rd Qu. Max.
-5.79814 -0.71697 0.06475 0.00000 0.45561 16.08999
bodymass_ouliers <- (length (which( abs(z.bodymass) >3 )) / 271116) * 100
bodymass_ouliers
[1] 1.451039
From the above data we can see that there are around 1.625% outliers in Age variable, 0.75% outliers in Height variable, 1.144% outliers in Weight and 1.45% outliers in Body Mass Index. I have used capping function along with the cap() method to replace the outliers in each variable with their nearest neighbour that are not outliers. Excluding the outliers might affect the analysis later and imputing it with mean or median will ultimately favour the count of one value thats why I choosed the cap method to impute the outliers.
#Capping the outliers using the cap function
#Referred from week8 lecture slides and worksheet
cap <- function(x){
quantiles <- quantile( x, c(.05, 0.25, 0.75, .95 ) )
x[ x < quantiles[2] - 1.5*IQR(x) ] <- quantiles[1]
x[ x > quantiles[3] + 1.5*IQR(x) ] <- quantiles[4]
x
}
new_olyampic <- new
new_olyampic$Age <- cap(new_olyampic$Age)
new_olyampic$Height <- cap(new_olyampic$Height)
new_olyampic$Weight <- cap(new_olyampic$Weight)
new_olyampic$Body_Mass_Index <- cap(new_olyampic$Body_Mass_Index)
head(new_olyampic)
NA
NA
After using the cap function I have plotted the variables using boxplots to check if there are outliers present in the Age, Height, Weight and Body Mass Index.
#Plotting the varaibles to check if the outliers are removed or not.
new_olyampic$Age %>% boxplot(main="Boxplot Age",ylab="Age",col = "green")

new_olyampic$Height %>% boxplot(main="Boxplot Height",ylab="Height",col = "blue")

new_olyampic$Weight %>% boxplot(main="Boxplot Weight",ylab="Weight",col = "red")

new_olyampic$Body_Mass_Index %>% boxplot(main="Boxplot Body Mass Index",ylab="Body Mass Index",col = "orange")

From the above output we can conclude that there are no outliers present in the dataset. The outliers have been capped successfully.
Transform
Transformation is required to change the scale of the variable or standardise the value of variable for better understanding. It can also be used to change the linearity of a realtionship. I have used the hist() function to plot histogram of the numeric variable. The Age variable was plotted using the hist() function here xlim and ylim are scale limits of the x and y axis
#Using hist() to check the distribution of age
#Referred from week9 lecture slides and worksheet
hist(new$Age, main = "Histogram of Age",
xlab = "Age",
ylim = c(0,120000), xlim = c(0,80))

NA
NA
From the above output it is clear that the graph for the Age variable is right skewed. Therefore I have used the log10 transformation to Age variable to obtain a normal distribution. I have transformed Age by applying log10() function to it and store it in an object named age_log. Later with the help of hist() function I have plotted the age variable
#Using log10 to transform age variable
#Referred from week9 lecture slides and worksheet
age_log <- log10(new$Age)
hist(age_log,main = "Age histogram after transformation",
xlab = "Age", ylim = c(0,80000))

NA
NA
From the output we can conclude that age is normally distributed after tranformation .
Now I have used hist() function to plot the Height variable.
#Using hist() to check the distribution of age
#Referred from week9 lecture slides and worksheet
hist(new$Height, main = "Histogram of Height",
xlab = "Height",
ylim = c(0, 120000),
xlim = c(130, 220))

From the output we can conclude that Height is normally distributed and therefore we dont need to tranform it.
Now I have used hist() function to plot the Weight variable.
#Using hist() to check the distribution of weight
#Referred from week9 lecture slides and worksheet
hist(new$Weight, main = "Histogram of Weight",
xlab = "Weight",
ylim = c(0, 120000),
xlim = c(20,150))

From the output we can conclude that graph for weight variable is right skewed. Therefore I have used the log10 transformation to Weight variable to obtain a normal distribution. I have transformed Weight by applying log10() function to it and store it in an object named weight_log.
#Using log10 to transform age variable
#Referred from week9 lecture slides and worksheet
weight_log <- log10(new$Weight)
hist(weight_log,main = "Weight histogram after transformation",xlab = "Weight",
ylim = c(0,120000))

From the output we can conclude that weight is normally distributed after tranformation.
After processing and investigating the historical dataset of olyampic games I have transformed the dataset by converting the variables to appropraite data types, handling outliers and missing values, transforming the variables. The dataset is now clean and can be used for further analysis.
LS0tCnRpdGxlOiAiTUFUSDIzNDkgU2VtZXN0ZXIgMSwgMjAyMCIKYXV0aG9yOiAiU3R1ZGVudCBuYW1lOiBDaHJpc3RhbmdlbCBGYXJnb3NlIGFuZCBTdHVkZW50IG51bWJlcjpzMzc5NDgwMCIKc3VidGl0bGU6IEFzc2lnbm1lbnQgMgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdAotLS0KCiMjIFJlcXVpcmVkIHBhY2thZ2VzIApGb2xsb3dpbmcgYXJlIHRoZSBwYWNrYWdlcyBiZWxvdyB0aGF0IEkgaGF2ZSB1c2VkIGZvciB0aGlzIGFzc2lnbm1lbnQuCgpgYGB7cn0KIyBCZWxvdyBwYWNrYWdlcyBhcmUgdXNlZApsaWJyYXJ5KHJlYWRyKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KG91dGxpZXJzKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkodmFsaWRhdGUpCmxpYnJhcnkobHVicmlkYXRlKQpsaWJyYXJ5KGtuaXRyKQpsaWJyYXJ5KEhtaXNjKQoKCmBgYAoKCiMjIEV4ZWN1dGl2ZSBTdW1tYXJ5IApUaGUgbWFpbiBhaW0gb2YgdGhlIGFzc2lnbm1lbnQgaXMgdG8gcHJlcHJvY2VzcyBhIG9wZW4gc291cmNlIHVudGlkeSBkYXRhc2V0LiBJIGhhdmUgZG93bmxvYWRlZCB0aGUgZGF0YXNldCBmcm9tIEthZ2dsZS4gVGhlIGRhdGEgZmlsZSBjb250YWlucyB0d28gZGF0YXNldHMgbmFtZWQgYXRobGV0ZV9ldmVudHMuY3N2IGFuZCBub2NfcmVnaW9ucy5jc3YuIFRoZW4gdGhlIGRhdGFzZXRzIGFyZSBpbXBvcnRlZCB1c2luZyByZWFkX2NzdigpIGZ1bmN0aW9uIGZyb20gdGhlIHJlYWRyIGxpYnJhcnkuCkFmdGVyIGltcG9ydGluZyB0aGUgZGF0YXNldHMgSSBoYXZlIHN1YnNldHRlZCBib3RoIHRoZSBkYXRhc2V0cyB1c2luZyBzZWxlY3QoKSBmdW5jdGlvbiBhcyBzb21lIG9mIHRoZSB2YXJpYWJsZXMgYXJlIG5vdCByZXF1aXJlZCBmb3IgdGhlIGZ1cnRoZXIgcHJvY2Vzcy4gTGF0ZXIgSSBoYXZlIHVzZWQgbGVmdCBqb2luIHRvIGpvaW4gYm90aCB0aGUgZGF0YXNldHMgdXNpbmcgY29tbW9uIHZhcmlhYmxlIE5PQy4KVGhlbiB1c2luZyB0aGUgc3RyKCkgZnVuY3Rpb24gSSBnZXQgdGhlIGRhdGEgdHlwZXMgb2YgdGhlIHZhcmlhYmxlcy4gRGF0YSB0eXBlIG9mIHNvbWUgdmFyaWFibGVzIGxpa2UgU2V4LCBTZWFzb24sIE1lZGFsIHNob3VsZCBiZSBmYWN0b3IgYnV0IGl0IGlzIHNob3duIGFzIGNoYXJhY3RlciB0aGF0cyB3aHkgSSBjb252ZXJ0ZWQgaXQgdXNpbmcgYXMuZmFjdG9yKCkgZnVuY3Rpb24uCkxhdGVyIEkgdXNlZCBoZWFkKCkgZnVuY3Rpb24gdG8gY2hlY2sgd2hldGhlciB0aGUgZGF0YXNldCBpcyB0aWR5IG9yIG5vdC4gQWZ0ZXIgY29uY2x1ZGluZyB0aGF0IHRoZSBkYXRhc2V0IGlzIHRpZHkgSSBjcmVhdGVkIGEgbmV3IHZhcmlhYmxlIG5hbWVkIEJvZHkgTWFzcyBJbmRleCB1c2luZyBIZWlnaHQsIFdlaWdodCB2YXJpYWJsZXMgYW5kIG11dGF0ZSgpIGZ1bmN0aW9uLgpJbiB0aGUgc2NhbiBzZWN0aW9uIEkgaGF2ZSBzY2FubmVkIHRoZSBkYXRhc2V0IGZvciBtaXNzaW5nIHZhbHVlcyB1c2luZyBjb2xTdW1zKCkgYW5kIGlzLm5hKCkgZnVuY3Rpb24uIEkgaGF2ZSBpbXB1dGVkIHRoZSBOdWxsIHZhbHVlcyBpbiBudW1lcmljIHZhcmlhYmxlcyB1c2luZyB0aGUgbWVhbigpIGZ1bmN0aW9uLiBOdWxsIHZhbHVlcyBpbiB0aGUgY2F0ZWdvcmljYWwgdmFyaWFibGVzIGFyZSBpbXB1dGVkIGJ5IGNyZWF0aW5nIGEgbmV3IGNhdGVnb3J5LgpUaGVuIEkgaGF2ZSBzY2FubmVkIGZvciBvdXRsaWVycyBieSBwbG90dGluZyB0aGUgbnVtZXJpYyB2YXJpYWJsZXMgdXNpbmcgYm94cGxvdCgpLiBJIGhhdmUgY2FsY3VsYXRlZCB0aGUgcGVyY2VudGFnZSBvZiBvdXRsaWVycyB1c2luZyBzY29yZSgpIGFuZCB3aGljaCgpIGZ1bmN0aW9uLiBPdXRsaWVycyB3ZXJlIGNhcHBlZCB1c2luZyBjYXAoKSBmdW5jdGlvbi4gVGhlIHZhcmlhYmxlcyB3ZXJlIHBsb3R0ZWQgdXNpbmcgYm94cGxvdCgpIHRvIGNoZWNrIGlmIHRoZXJlIGFyZSBvdXRsaWVycy4KQWZ0ZXIgcmVtb3Zpbmcgb3V0bGllcnMgdXNpbmcgaGlzdCgpIHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIHZhcmlhYmxlcyB3YXMgYW5hbHlzZWQuIEFnZSBhbmQgV2VpZ2h0IHdlcmUgdHJhbnNmb3JtZWQgdXNpbmcgbG9nMTAgdHJhbnNmb3JtYXRpb24gYXMgdGhlaXIgZ3JhcGggd2FzIHJpZ2h0IHNrZXdlZC4gSGVpZ2h0IHdhcyBub3JtYWxseSBkaXN0cmlidXRlZCB0aGF0cyB3aHkgaXQgaXMgbm90IHRyYW5zZm9ybWVkLgoKIyMgRGF0YSAKClRoZSBmb2xsb3dpbmcgZGF0YXNldCBpcyBoaXN0b3JpY2FsIGRhdGFzZXQgb2Ygb2x5YW1waWMgZ2FtZXMgZnJvbSAxODk2IHRvIDIwMTYuIFRoZSBkYXRhc2V0IGlzIGRvd25sb2FkZSBmcm9tIGthZ2dsZS4gTGluayBvZiB0aGUgZGF0YSBzb3VyY2UgaXMgZ2l2ZW4gYmVsb3ctCgpodHRwczovL3d3dy5rYWdnbGUuY29tL2hlZXNvbzM3LzEyMC15ZWFycy1vZi1vbHltcGljLWhpc3RvcnktYXRobGV0ZXMtYW5kLXJlc3VsdHMKClRoZSBkYXRhc2V0IGNvbnRhaW5zIHR3byB0YWJsZXMgbmFtZWQgYXRobGV0ZV9ldmVudHMuY3N2IGFuZCBub2NfcmVnaW9ucy5jc3YuIEZpcnN0IHRhYmxlIGNvbnRhaW5zIGluZm9ybWF0aW9uIGFib3V0IHRoZSBhdGhsZXRlcyBhbmQgdGhlIG9seWFtcGljIGV2ZW50cyB0aGF0IHRoZXkgaGF2ZSBwYXJ0aWNpcGF0ZWQgYW5kIHNlY29uZCB0YWJsZSBjb250YWlucyBpbmZvcm1hdGlvbiBhYm91dCB0aGUgY291bnRyaWVzIHRoYXQgcGFydGljaXBhdGVkIGluIHRoZSBvbHlhbXBpYyBnYW1lcy4gVGhlIGF0aGxldGVfZXZlbnRzLmNzdiBjb250YWlucyBmb2xsb3dpbmcgdmFyaWFibGVzLQoxKSBJRC0gQSB1bmlxdWUgaWRlbnRpZmllciBmb3IgZWFjaCBhdGhsZXRlCjIpIE5hbWUtIE5hbWUgb2YgdGhlIGF0aGxldGUKMykgU2V4LSBHZW5kZXIgb2YgYXRobGV0ZQo0KSBBZ2UgQWdlIG9mIGF0aGxldGUKNSkgSGVpZ2h0LSBIZWlnaHQgb2YgYXRobGV0ZSBpbiBjbQo2KSBXZWlnaHQtIFdlaWdodCBvZiBhbiBhdGhsZXRlIGluIGtncwo3KSBUZWFtLSBOYW1lIG9mIHRoZSBUZWFtCjgpIE5PQy0gTmF0aW9uYWwgb2x5YW1waWMgY29tbWl0dGVlIDMtbGV0dGVyIGNvZGUKOSkgR2FtZXMtIFllYXIgYW5kIFNlYXNvbiBvZiB0aGUgb2x5YW1waWMgZ2FtZQoxMCkgWWVhci0gWWVhciBvZiB0aGUgb2x5YW1waWMKMTEpIFNlYXNvbi0gU2Vhc29uIG9mIHRoZSBvbHlhbXBpYwoxMikgQ2l0eS0gTmFtZSBvZiB0aGUgY2l0eSB0aGF0IGhvc3RlZCB0aGUgb2x5YW1waWNzCjEzKSBTcG9ydC0gU3BvcnQgdGhhdCB0aGUgYXRobGV0ZSBwYXJ0aWNpcGF0ZWQKMTQpIEV2ZW50LSBFdmVudCBvZiB0aGUgc3BvcnQgaW4gd2hpY2ggdGhlIGF0aGxldGUgcGFydGljaXBhdGVkCjE1KSBNZWRhbC0gTWVkYWwgd29uIGJ5IHRoZSBhdGhsZXRlLgoKVGhlIG5vY19yZWdpb25zLmNzdiBjb250YWlucyBmb2xsb3dpbmcgdmFyaWFibGVzLQoxKSBOT0MtIE5hdGlvbmFsIG9seWFtcGljIGNvbW1pdHRlZSAzLWxldHRlciBjb2RlCjIpIHJlZ2lvbi0gQ291bnRyeSBuYW1lIG9mIHRoZSByZXNwZWN0aXZlIE5PQwozKSBub3Rlcy0gU29tZSBpbmZvcm1hdGlvbiBhYm91dCB0aGUgcmVnaW9uLgoKIyMgSW1wb3J0aW5nIGFuZCBTdWJzZXR0aW5nIHRoZSBEYXRhCgpIZXJlIEkgaGF2ZSB1c2VkIHRoZSByZWFkX2NzdigpIGZ1bmN0aW9uIGZyb20gdGhlIHJlYWRyIGxpYnJhcnkgdG8gaW1wb3J0IGJvdGggdGhlIGRhdGFzZXRzLiBhdGhsZXRlX2V2ZW50cy5jc3YgZGF0YXNldCBpcyBhc3NpZ25lZCB0byBvYmplY3QgcGxheWVyX2RhdGEgYW5kCm5vY19yZWdpb25zLmNzdiBkYXRhZGV0IGlzIGFzc2lnbmVkIHRvIHRoZSBvYmplY3QgY291bnRyeV9kYXRhLiAKR2FtZXMgdmFyaWFibGUgY29uc2lzdCBvZiB5ZWFyIGFuZCBzZWFzb24gaW5mb3JtYXRpb24gY29tYmluZWQgYW5kIHNlcGVyYXRpbmcgdGhlIHZhcmlhYmxlIHdvdWxkIGdpdmUgdXMgdGhlIHZhcmlhYmxlcyBsaWtlIFllYXIgYW5kIFNlYXNvbiB3aGljaCBhbHJlYWR5IGV4aXN0IHRoYXRzIHdoeSBJIGhhdmUgZXhjbHVkZWQgdGhlIEdhbWVzIHZhcmlhYmxlIHVzaW5nIHRoZSBzZWxlY3QoKSBmdW5jdGlvbiBhbmQgc3RvcmUgYWxsIHRoZSBvdGhlciB2YXJpYWJsZSBpbiBvYmplY3QgUGxheWVyX2RhdGEuIApTaW1pbGFybHkgdGhlIG5vdGUgdmFyaWFibGUgaW4gdGhlIGNvdW50cmllcyBkYXRhIGlzIG5vdCB1c2VmdWwgZm9yIGZ1cnRoZXIgYW5hbHlzaXMgdGhlcmVmb3JlIEkgaGF2ZSBzZWxlY3RlZCBvbmx5IHR3byB2YXJpYWJsZXMgTk9DIGFuZCByZWdpb24gYW5kIHN0b3JlZCBpdCBpbiBhbiBvYmplY3QgY291bnRyeV9kYXRhCk5vdyBhZnRlciBzdWJzZXR0aW5nIGJvdGggdGhlIGRhdGFzZXRzIEkgaGF2ZSBtZXJnZWQgYm90aCB0aGUgZGF0YXNldHMgdXNpbmcgbGVmdF9qb2luIGJhc2VkIG9uIHRoZSBjb21tb24ga2V5IHZhcmlhYmxlIE5PQy4gVGhlIG1lcmdlZCBkYXRzZXQgaXMgc3RvcmVkIGluIHRoZSBvYmplY3QgbmV3LiBBZnRlciB0aGF0IHVzaW5nIHRoZSBoZWFkKCkgZnVuY3Rpb24gSSBoYXZlIGRpc3BsYXllZCB0aGUgbmV3IGRhdGFzZXQgYW5kIGNoZWNrZWQgaWYgZXZlcnkgY29sdW1uIGlzIGluY2x1ZGVkIGFmdGVyIGpvaW5pbmcgYm90aCB0aGUgZGF0YXNldHMKCgpgYGB7cn0KI0ltcG9ydGluZyB0aGUgZmlyc3QgZGF0YXNldCB1c2luZyByZWFkX2NzdgpwbGF5ZXJfZGF0YSA8LSByZWFkX2NzdigiL1VzZXJzL2NocmlzdGFuZ2VsZmFyZ29zZS8xMjAteWVhcnMtb2Ytb2x5bXBpYy1oaXN0b3J5LWF0aGxldGVzLWFuZC1yZXN1bHRzL2F0aGxldGVfZXZlbnRzLmNzdiIpCgojU3Vic2V0dGluZyB0aGUgZGF0YXNldCBieSByZW1vdmluZyB0aGUgOXRoIGNvbHVtbgpwbGF5ZXJfZGF0YSA8LSBzZWxlY3QocGxheWVyX2RhdGEsLTkpCmhlYWQocGxheWVyX2RhdGEpCgojSW1wb3J0aW5nIGNvdW50cnkgZGF0YSB1c2luZyByZWFkX2Nzdgpjb3VudHJ5X2RhdGEgPC0gcmVhZF9jc3YoIi9Vc2Vycy9jaHJpc3RhbmdlbGZhcmdvc2UvMTIwLXllYXJzLW9mLW9seW1waWMtaGlzdG9yeS1hdGhsZXRlcy1hbmQtcmVzdWx0cy9ub2NfcmVnaW9ucy5jc3YiKQoKI1N1YnNldHRpbmcgdGhlIGNvdW50cnlfZGF0YQpjb3VudHJ5X2RhdGEgPC0gY291bnRyeV9kYXRhWyxjKDEsMildCmhlYWQoY291bnRyeV9kYXRhKQoKI0pvaW5pbmcgYm90aCB0aGUgZGF0YXNldHMgdXNpbmcgbGVmdCBqb2luCm5ldyA8LSBsZWZ0X2pvaW4ocGxheWVyX2RhdGEsY291bnRyeV9kYXRhLGJ5PSJOT0MiKQpoZWFkKG5ldykKCgpgYGAKCiMjIFVuZGVyc3RhbmQgCgpUaGUgbmV3IGRhdGFzZXQgY29uc2lzdCBvZiB0b3RhbCAxNSB2YXJpYWJsZXMgYW5kIDI3MTExNiByb3dzLiBUaGUgZGF0YSB0eXBlIG9mIHZhcmlhYmxlcyBjYW4gYmUgZm91bmQgb3V0IHVzaW5nIHN0cigpIGZ1bmN0aW9uCgoKYGBge3J9CgojRGlzcGxheWluZyB0aGUgc3RydWN0dXJlIG9mIGRhdGFzZXQgYW5kIGNoZWNraW5nIGRhdGEgdHlwZXMgCnN0cihuZXcpCmBgYAoKIyMgQ29udmVyc2lvbgoKQXMgc2hvd24gZnJvbSB0aGUgYWJvdmUgb3V0cHV0IHRoZSBkYXRhIHR5cGVzIG9mIElELEFnZSxIZWlnaHQsV2VpZ2h0LFllYXIgaXMgbnVtZXJpYyB3aGljaCBpcyBjb3JyZWN0LiBBbHNvIHRoZSBkYXRhIHR5cGVzIG9mIE5hbWUsIFRlYW0sIE5PQyxFdmVudCwgU3BvcnQsQ2l0eSxyZWdpb24gaXMgY2hhcmFjdGVyIHdoaWNoIGlzIGNvcnJlY3QuIFZhcmlhYmxlcyBsaWtlIFNleCxTZWFzb24sTWVkYWwgc2hvdWxkIGJlIGZhY3RvcnMgYnV0IGFyZSBzaG93biBhcyBjaGFyYWN0ZXIgdGhhdHMgd2h5IHRoZXkgc2hvdWxkIGJlIGNoYW5nZWQgdG8gZmFjdG9ycy4gSSBoYXZlIHVzZWQgYXMuZmFjdG9yKCkgZnVuY3Rpb24gdG8gY2hhbmdlIHRoZSBkYXRhIHR5cGUgb2YgdmFyaWFibGVzIFNleCwgU2Vhc29uIGFuZCBNZWRhbC4gSSBoYXZlIGFsc28gb3JkZXJlZCB0aGUgTWVkYWwgdmFyaWFibGUgaW4gdGhlIG9yZGVyIEdvbGQsIFNpbHZlciBhbmQgdGhlbiBCcm9uemUuIEFmdGVyIGNvbnZlcnRpbmcgdGhlIGRhdGEgdHlwZSB0byBmYWN0b3JzIEkgaGF2ZSB1c2VkIGNsYXNzKCkgZnVuY3Rpb24gdG8gY2hlY2sgdGhlIGNsYXNzIG9mIHRoZSB2YXJpYWJsZXMgYW5kIGFsc28gdXNlZCB0aGUgc3RyKCkgZnVuY3Rpb24gdG8gY2hlY2sgaWYgdGhlIGRhdGEgdHlwZXMgYXJlIGNoYW5nZWQuCgpgYGB7cn0KI0NvbnZlcnRpbmcgU2V4IHRvIGZhY3RvciBhbmQgY2hlY2tpbmcgaXQgdXNpbmcgY2xhc3MKbmV3JFNleCA8LSBhcy5mYWN0b3IobmV3JFNleCkKY2xhc3MobmV3JFNleCkKCiNDb252ZXJ0aW5nIFNlYXNvbiB0byBmYWN0b3IgYW5kIGNoZWNraW5nIGl0IHVzaW5nIGNsYXNzCm5ldyRTZWFzb24gPC0gYXMuZmFjdG9yKG5ldyRTZWFzb24pCmNsYXNzKG5ldyRTZWFzb24pCgojQ29udmVydGluZyBNZWRhbCB0byBmYWN0b3IgYW5kIGNoZWNraW5nIGl0IHVzaW5nIGNsYXNzCm5ldyRNZWRhbCA8LSBhcy5mYWN0b3IobmV3JE1lZGFsKQpuZXckTWVkYWwgPC0gZmFjdG9yKG5ldyRNZWRhbCxsZXZlbHMgPSBjKCJHb2xkIiwiU2lsdmVyIiwiQnJvbnplIiksb3JkZXJlZCA9IFRSVUUpCmNsYXNzKG5ldyRNZWRhbCkKCiNDaGVja2luZyB3aGV0aGVyIHRoZSBkYXRhIHR5cGUgaXMgY2hhbmdlZCAKc3RyKG5ldykKCmBgYAoKIyMJVGlkeSAmIE1hbmlwdWxhdGUgRGF0YSBJIAoKV2Ugd2lsbCBjaGVjayB0aGUgZGF0YXNldCB1c2luZyBoZWFkKCkgZnVuY3Rpb24uIEluIHRoZSBkYXRhc2V0IGVhY2ggdmFyaWFibGUgaGF2ZSBpdHMgb3duIGNvbHVtbiBhbmQgZWFjaCBvYnNlcnZhdGlvbiBpcyBzdG9yZWQgaW4gYSBzZXBlcmF0ZSByb3cuIFBsdXMgZWFjaCB2YWx1ZSBpcyBzdG9yZWQgaW4gaXRzIG93biBjZWxsIHRoZXJmb3JlIHRoZSBkYXRhc2V0IGlzIHRpZHkgYW5kIHRoZXJlIGlzIG5vIG5lY2Vzc2l0eSB0byByZXNoYXBlIHRoZSBkYXRhc2V0LiBUaGUgZGF0YXNldCBjYW4gYmUgdXNlZCBmb3IgZnVydGhlciBwcm9jZXNzLgoKCmBgYHtyfQojIFVzaW5nIHRoZSBoZWFkKCkgZnVuY3Rpb24gdG8gY2hlY2sgaWYgdGhlIGRhdGFzZXQgaXMgdGlkeSBvciBub3QgCmhlYWQobmV3KQpgYGAKCiMjCVRpZHkgJiBNYW5pcHVsYXRlIERhdGEgSUkgCgpIZXJlIHdlIGhhdmUgY3JlYXRlZCBhIG5ldyB2YXJpYWJsZSBCb2R5IG1hc3MgaW5kZXggdXNpbmcgdGhlIG11dGF0ZSgpIGZ1bmN0aW9uIG9mIHRoZSBkcGx5ciBwYWNrYWdlLiBCb2R5IG1hc3MgaW5kZXggd2FzIGNyZWF0ZWQgdXNpbmcgdGhlIHZhcmlhYmxlcyBIZWlnaHQgYW5kIFdlaWdodC4gQm9keSBtYXNzIGluZGV4IGlzIGEgbWVhc3VyZW1lbnQgb2YgcGVyc29ucyB3ZWlnaHQgd2l0aCByZXNwZWN0IHRvIHRoZWlyIGhlaWdodC5Gb2xsb3dpbmcgZm9ybXVsYSB3YXMgdXNlZCB0byBjcmVhdGUgdGhlIHZhcmlhYmxlLQoKQm9keSBNYXNzIEluZGV4ID0gV2VpZ2h0LyhIZWlnaHQpXjIKCkhlcmUgSSBoYXZlIHVzZWQgMTAwMDAgYmVjYXVzZSB0aGUgaGVpZ2h0IGluIHRoZSBmb3JtdWxhIHNob3VsZCBiZSBpbiBtZXRlciBidXQgaW4gdGhlIGRhdGEgd2UgaGF2ZSBpdCBpbiBjbSBzbyB3ZSBuZWVkIHRvIGNvbnZlcnQgaXQuCgoKYGBge3J9CiMgQ3JlYXRpbmcgdmFyaWFibGUgQm9keSBNYXNzIEluZGV4IHVzaW5nIG11dGF0ZSgpIGZ1bmN0aW9uCm5ldyA8LSAgbXV0YXRlKG5ldyxCb2R5X01hc3NfSW5kZXg9cm91bmQobmV3JFdlaWdodC8obmV3JEhlaWdodCAqIG5ldyRIZWlnaHQpICoxMDAwMCkpCmhlYWQobmV3KQpgYGAKCgojIwlTY2FuIEkgCgpGaXJzdCBJIGhhdmUgdXNlZCBjb2xTdW1zKCkgZnVuY3Rpb24gd2l0aCBpcy5uYSgpIHRvIGZpbmQgdGhlIE5VTEwgdmFsdWVzIGluIGVhY2ggdmFyaWFibGVzIG9mIHRoZSBkYXRhc2V0LgoKYGBge3J9CiMgRGlzcGxheWluZyB0aGUgY291bnQgb2YgTnVsbCB2YWx1ZXMgaW4gZWFjaCB2YXJpYWJsZSBvZiBkYXRhc2V0CmNvbFN1bXMoaXMubmEobmV3KSkKYGBgCgpBY2NvcmRpbmcgdG8gdGhlIGFib3ZlIG91dHB1dCB3ZSBjYW4gY29uY2x1ZGUgdGhhdCB0aGVyZSBhcmUgbWlzc2luZyB2YWx1ZXMgaW4gdGhlIEFnZSwgSGVpZ2h0LCBXZWlnaHQsIE1lZGFsLCByZWdpb24gYW5kIEJvZHlfTWFzc19JbmRleCB2YXJpYWJsZXMuIEFnZSwgSGVpZ2h0LCBXZWlnaHQsIEJvZHlfTWFzc19JbmRleCBhcmUgbnVtZXJpYyB2YXJpYWJsZXMgYW5kIE1lZGFsIGFuZCByZWdpb24gYXJlIGNhdGVnb3JpY2FsIHRoYXQgaXMgd2h5IHRoZXkgc2hvdWxkIGJlIGhhbmRsZWQgZGlmZnJlbnRseS4gVGhlIE5VTEwgdmFsdWUgY291bnQgb2YgdmFyaWFibGVzIGFyZSBoaWdoIHRoZXJmb3JlIHRoZXkgY2Fubm90IGJlIGV4Y2x1ZGVkIGFzIHRoaXMgbWlnaHQgYWZmZWN0IHRoZSBhbmFseXNpcyBwcm9jZXNzIHRoZXJlZm9yZSB0aGUgbnVsbCB2YWx1ZXMgbXVzdCBiZSBpbXB1dGVkLiBJIGhhdmUgaW1wdXRlZCB0aGUgbnVtZXJpYyBOVUxMIHZhbHVlcyBieSB1c2luZyBtZWFuKCkgZnVuY3Rpb24gYWxvbmcgd2l0aCBpcy5uYSgpCgpgYGB7cn0KI0ltcHV0aW5nIG51bWVyaWMgdmFyaWFibGVzIHdpdGggdGhlIG1lYW4gb2YgdGhlIHBhcnRpY3VsYXIgY29sdW1uCm5ldyRBZ2VbaXMubmEobmV3JEFnZSldIDwtIHJvdW5kKG1lYW4obmV3JEFnZSwgbmEucm0gPSBUUlVFKSkKCm5ldyRXZWlnaHRbaXMubmEobmV3JFdlaWdodCldIDwtIG1lYW4obmV3JFdlaWdodCwgbmEucm0gPSBUUlVFKQoKbmV3JEhlaWdodFtpcy5uYShuZXckSGVpZ2h0KV0gPC0gcm91bmQobWVhbihuZXckSGVpZ2h0LCBuYS5ybSA9IFRSVUUpKQoKbmV3JEJvZHlfTWFzc19JbmRleFtpcy5uYShuZXckQm9keV9NYXNzX0luZGV4KV0gPC0gcm91bmQobWVhbihuZXckQm9keV9NYXNzX0luZGV4LCBuYS5ybSA9IFRSVUUpKQoKaGVhZChuZXcpCgoKYGBgCgpUaGUgY2F0ZWdvcmljYWwgdmFyaWFibGVzIGFyZSBpbXB1dGVkIGJ5IGZvcm1pbmcgYSBuZXcgY2F0ZWdvcnkgaW4gdGhlIHZhcmlhYmxlLiBIZXJlIEkgaGF2ZSBmb3JtZWQgYSBjYXRlZ29yeSAiTm8gTWVkYWwiIGluIHRoZSB2YXJpYWJsZSBNZWRhbCBhbmQgcmVwbGFjZSBhbGwgdGhlIG51bGwgdmFsdWVzIHdpdGggdGhhdCBjYXRlZ29yeSB1c2luZyBpcy5uYSgpIGZ1bmN0aW9uLiBUaGUgbWVkYWwgdmFyaWFibGUgaXMgZmFjdG9yIHNvIHdlIG5lZWQgdG8gY29udmVydCBpdCB0byBjaGFyYWN0ZXIuIFNpbWlsYXJseSBuZXcgY2F0ZWdvcnkgbmFtZWQgIlVua25vd24iIHdhcyBmb3JtZWQgaW4gdGhlIHZhcmlhYmxlIHJlZ2lvbiBhbmQgYWxsIG51bGwgdmFsdWVzIHdlcmUgaW1wdXRlZCB3aXRoIHRoaXMgY2F0ZWdvcnkgdXNpbmcgaXMubmEoKSBmdW5jdGlvbgoKYGBge3J9CiNJbXB1dGluZyBjYXRlZ29yaWNhbCB2YXJpYWJsZSBieSBjcmVhdGluZyBhIG5ldyBjYXRlZ29yeQpuZXckTWVkYWw8LSBhcy5jaGFyYWN0ZXIobmV3JE1lZGFsKQpuZXckTWVkYWxbaXMubmEobmV3JE1lZGFsKV0gPC0gIk5vIE1lZGFsIgpuZXckTWVkYWwgPC0gYXMuZmFjdG9yKG5ldyRNZWRhbCkKbmV3JE1lZGFsIDwtZmFjdG9yKG5ldyRNZWRhbCxsZXZlbHMgPSBjKCJHb2xkIiwiU2lsdmVyIiwiQnJvbnplIiwiTm8gTWVkYWwiKSxvcmRlcmVkID0gVFJVRSkKCm5ldyRyZWdpb25baXMubmEobmV3JHJlZ2lvbildIDwtICJVbmtub3duIgoKaGVhZChuZXcpCgoKYGBgCgpMYXN0bHkgYWxsIHRoZSBudW1lcmljIHZhcmlhYmxlcyB3ZXJlIHNjYW5uZWQgZm9yIG1pc3NpbmcgdmFsdWVzIHVzaW5nIHN1bSgpIGZ1bmN0aW9uIGFsb25nIHdpdGggaXMuaW5maW5pdGUoKSBmdW5jdGlvbiB0byBjaGVjayB0aGUgc3BlY2lhbCB2YWx1ZXMgaW4gdGhlIGRhdGFzZXQuIEZpbmFsbHkgdG8gY2hlY2sgd2hldGhlciBhbGwgdGhlIG51bGwgdmFsdWVzIHdlcmUgcmVtb3ZlZCBJIGhhdmUgdXNlZCBjb2xTdW1zKCkgZnVuY3Rpb24gYWxvbmcgd2l0aCBpcy5uYSgpIGZ1bmN0aW9uCgpgYGB7cn0KI1NjYW5uaW5nIGZvciBzcGVjaWFsIHZhbHVlcyBpbiBudW1lcmljIHZhcmlhYmxlcyBvZiB0aGUgZGF0YXNldApzdW0oaXMuaW5maW5pdGUobmV3JElEKSkKCnN1bShpcy5pbmZpbml0ZShuZXckQWdlKSkKCnN1bShpcy5pbmZpbml0ZShuZXckSGVpZ2h0KSkKCnN1bShpcy5pbmZpbml0ZShuZXckV2VpZ2h0KSkKCnN1bShpcy5pbmZpbml0ZShuZXckQm9keV9NYXNzX0luZGV4KSkKCnN1bShpcy5pbmZpbml0ZShuZXckWWVhcikpCgpjb2xTdW1zKGlzLm5hKG5ldykpCgpgYGAKCiMjCVNjYW4gSUkKCkFuIG91dGxpZXIgaXMgYW4gb2JzZXJ2YXRpb24gd2hpY2ggc3RhbmQgb3V0IGZyb20gb3RoZXIgb2JzZXJ2YXRpb25zLiBPdXRsaWVyIGNhbiBiZSBiZWNhdXNlIG9mIGRhdGEgZW50cnkgZXJyb3IsIE1lYXN1cmVtZW50IGVycm9yLCBEYXRhIHByb2Nlc3NpbmcgZXJyb3Igb3IgbWFueSBvdGhlciByZWFzb25zLiBUbyBzY2FuIHRoZSBvdXRsaWVycyBJIGhhdmUgcGxvdHRlZCB0aGUgbnVtZXJpYyB2YXJpYWJsZXMgdXNpbmcgYm94cGxvdCBmdW5jdGlvbiBmcm9tIHRoZSBnZ3Bsb3QyIGxpYnJhcnkuIEkgaGF2ZSBub3QgY2hlY2tlZCB0aGUgbnVtZXJpYyB2YXJpYWJsZXMgbGlrZSBJRCBhbmQgeWVhciBmb3Igb3V0bGllcnMgYmVjYXVzZSBJRCBpcyB1bmlxdWUgYW5kIHRoZXJlIGNhbnQgYmUgYSBvdXRsaWVyIGZvciBJRC4gU2ltaWxhciBpcyB0aGUgY2FzZSBmb3IgeWVhci4gSGVyZSBJIGhhdmUgdXNlZCBUdXJrZXlzIG1ldGhvZCBvZiBvdXRsaWVyIGRldGVjdGlvbiB1c2luZyBib3hwbG90LgoKYGBge3J9CiMgUGxvdGluZyBudW1lcmljIHZhcmlhYmxlcyB1c2luZyBib3hwbG90KCkgdG8gY2hlY2sgdGhlIG91dGxpZXJzCiNSZWZlcnJlZCBmcm9tIHdlZWs4IGxlY3R1cmUgc2xpZGVzIGFuZCB3b3Jrc2hlZXQKbmV3JEFnZSAlPiUgYm94cGxvdChtYWluPSJCb3hwbG90IEFnZSIseWxhYj0iQWdlIixjb2wgPSAiZ3JlZW4iKQoKbmV3JEhlaWdodCAlPiUgYm94cGxvdChtYWluPSJCb3hwbG90IEhlaWdodCIseWxhYj0iSGVpZ2h0Iixjb2wgPSAiYmx1ZSIpCgpuZXckV2VpZ2h0ICU+JSBib3hwbG90KG1haW49IkJveHBsb3QgV2VpZ2h0Iix5bGFiPSJXZWlnaHQiLGNvbCA9ICJyZWQiKQoKbmV3JEJvZHlfTWFzc19JbmRleCAlPiUgYm94cGxvdChtYWluPSJCb3hwbG90IEJvZHkgTWFzcyBJbmRleCIseWxhYj0iQm9keSBNYXNzIEluZGV4Iixjb2wgPSAib3JhbmdlIikKCmBgYAoKRnJvbSB0aGUgYWJvdmUgb3V0cHV0cyB3ZSBjYW4gc2VlIHRoYXQgdGhlcmUgYXJlIG91dGxpZXJzIGluIGVhY2ggbnVtZXJpYyB2YXJpYWJsZXMgdGhhdCB3ZSBoYXZlIHBsb3R0ZWQgdXNpbmcgdGhlIGJveHBsb3QuTm93IHRvIGNoZWNrIHRoZSBudW1iZXIgYW5kIHBlcmNlbnRhZ2Ugb2Ygb3V0bGllcnMgSSBoYXZlIHVzZWQgdGhlIHotc2NvcmUgdmFsdWUgYWxvbmcgd2l0aCB0aGUgc2NvcmUoKSBhbmQgc3VtbWFyeSgpIGZ1bmN0aW9uLiBTdW1tYXJ5KCkgZnVuY3Rpb24gZ2l2ZXMgdXMgdGhlIHN1bW1hcnkgb2YgdGhlIHZhcmlhYmxlLiBCeSB1c2luZyBsZW5naHQoKSBmdW5jdGlvbiBhbG9uZyB3aXRoIHdoaWNoKCkgZnVuY3Rpb24gYW5kIHRvdGFsIG51bWJlciBvZiByb3dzIEkgaGF2ZSBjYWxjdWxhdGVkIHRoZSBwZXJjZW50YWdlIG9mIG91dGxpZXJzIGluIGEgdmFyaWFibGUuVGhlIHBlcmNlbnRhZ2Ugb2Ygb3V0bGllcnMgdmFsdWUgaXMgc3RvcmVkIGluIG9iamVjdHMgbGlrZSBhZ2Vfb3V0bGllcnMsIGhlaWdodF9vdXRsaWVycywgd2VpZ2h0X291dGxpZXJzLCBib2R5X21hc3Mgb3V0bGllcnMKCmBgYHtyfQojRmluZGluZyB0aGUgcGVyY2VudGFnZSBvZiBvdXRsaWVycyB1c2luZyBzY29yZXMsIHN1bW1hcnkgYW5kIHdoaWNoIGZ1bmN0aW9uIAojUmVmZXJyZWQgZnJvbSB3ZWVrOCBsZWN0dXJlIHNsaWRlcyBhbmQgd29ya3NoZWV0CnouYWdlIDwtIG5ldyRBZ2UgJT4lICBzY29yZXModHlwZSA9ICJ6IikKei5hZ2UgJT4lIHN1bW1hcnkoKQphZ2Vfb3VsaWVycyA8LSAobGVuZ3RoICh3aGljaCggYWJzKHouYWdlKSA+MyApKSAvIDI3MTExNikgKiAxMDAKYWdlX291bGllcnMKCnouaGVpZ2h0IDwtIG5ldyRIZWlnaHQgJT4lICBzY29yZXModHlwZSA9ICJ6IikKei5oZWlnaHQgJT4lIHN1bW1hcnkoKQpoZWlnaHRfb3VsaWVycyA8LSAobGVuZ3RoICh3aGljaCggYWJzKHouaGVpZ2h0KSA+MyApKSAvIDI3MTExNikgKiAxMDAKaGVpZ2h0X291bGllcnMKCnoud2VpZ2h0IDwtIG5ldyRXZWlnaHQgJT4lICBzY29yZXModHlwZSA9ICJ6IikKei53ZWlnaHQgJT4lIHN1bW1hcnkoKQp3ZWlnaHRfb3VsaWVycyA8LSAobGVuZ3RoICh3aGljaCggYWJzKHoud2VpZ2h0KSA+MyApKSAvIDI3MTExNikgKiAxMDAKd2VpZ2h0X291bGllcnMKCnouYm9keW1hc3MgPC0gbmV3JEJvZHlfTWFzc19JbmRleCAlPiUgIHNjb3Jlcyh0eXBlID0gInoiKQp6LmJvZHltYXNzICU+JSBzdW1tYXJ5KCkKYm9keW1hc3Nfb3VsaWVycyA8LSAobGVuZ3RoICh3aGljaCggYWJzKHouYm9keW1hc3MpID4zICkpIC8gMjcxMTE2KSAqIDEwMApib2R5bWFzc19vdWxpZXJzCgoKYGBgCgpGcm9tIHRoZSBhYm92ZSBkYXRhIHdlIGNhbiBzZWUgdGhhdCB0aGVyZSBhcmUgYXJvdW5kIDEuNjI1JSBvdXRsaWVycyBpbiBBZ2UgdmFyaWFibGUsIDAuNzUlIG91dGxpZXJzIGluIEhlaWdodCB2YXJpYWJsZSwgMS4xNDQlIG91dGxpZXJzIGluIFdlaWdodCBhbmQgMS40NSUgb3V0bGllcnMgaW4gQm9keSBNYXNzIEluZGV4LiBJIGhhdmUgdXNlZCBjYXBwaW5nIGZ1bmN0aW9uIGFsb25nIHdpdGggdGhlIGNhcCgpIG1ldGhvZCB0byByZXBsYWNlIHRoZSBvdXRsaWVycyBpbiBlYWNoIHZhcmlhYmxlIHdpdGggdGhlaXIgbmVhcmVzdCBuZWlnaGJvdXIgdGhhdCBhcmUgbm90IG91dGxpZXJzLiBFeGNsdWRpbmcgdGhlIG91dGxpZXJzIG1pZ2h0IGFmZmVjdCB0aGUgYW5hbHlzaXMgbGF0ZXIgYW5kIGltcHV0aW5nIGl0IHdpdGggbWVhbiBvciBtZWRpYW4gd2lsbCB1bHRpbWF0ZWx5IGZhdm91ciB0aGUgY291bnQgb2Ygb25lIHZhbHVlIHRoYXRzIHdoeSBJIGNob29zZWQgdGhlIGNhcCBtZXRob2QgdG8gaW1wdXRlIHRoZSBvdXRsaWVycy4KCmBgYHtyfQojQ2FwcGluZyB0aGUgb3V0bGllcnMgdXNpbmcgdGhlIGNhcCBmdW5jdGlvbgojUmVmZXJyZWQgZnJvbSB3ZWVrOCBsZWN0dXJlIHNsaWRlcyBhbmQgd29ya3NoZWV0CmNhcCA8LSBmdW5jdGlvbih4KXsKICBxdWFudGlsZXMgPC0gcXVhbnRpbGUoIHgsIGMoLjA1LCAwLjI1LCAwLjc1LCAuOTUgKSApCiAgeFsgeCA8IHF1YW50aWxlc1syXSAtIDEuNSpJUVIoeCkgXSA8LSBxdWFudGlsZXNbMV0KICB4WyB4ID4gcXVhbnRpbGVzWzNdICsgMS41KklRUih4KSBdIDwtIHF1YW50aWxlc1s0XQogIHgKfQoKbmV3X29seWFtcGljIDwtIG5ldwpuZXdfb2x5YW1waWMkQWdlIDwtIGNhcChuZXdfb2x5YW1waWMkQWdlKQpuZXdfb2x5YW1waWMkSGVpZ2h0IDwtIGNhcChuZXdfb2x5YW1waWMkSGVpZ2h0KQpuZXdfb2x5YW1waWMkV2VpZ2h0IDwtIGNhcChuZXdfb2x5YW1waWMkV2VpZ2h0KQpuZXdfb2x5YW1waWMkQm9keV9NYXNzX0luZGV4IDwtIGNhcChuZXdfb2x5YW1waWMkQm9keV9NYXNzX0luZGV4KQoKaGVhZChuZXdfb2x5YW1waWMpCgoKYGBgCgpBZnRlciB1c2luZyB0aGUgY2FwIGZ1bmN0aW9uIEkgaGF2ZSBwbG90dGVkIHRoZSB2YXJpYWJsZXMgdXNpbmcgYm94cGxvdHMgdG8gY2hlY2sgaWYgdGhlcmUgYXJlIG91dGxpZXJzIHByZXNlbnQgaW4gdGhlIEFnZSwgSGVpZ2h0LCBXZWlnaHQgYW5kIEJvZHkgTWFzcyBJbmRleC4KCmBgYHtyfQojUGxvdHRpbmcgdGhlIHZhcmFpYmxlcyB0byBjaGVjayBpZiB0aGUgb3V0bGllcnMgYXJlIHJlbW92ZWQgb3Igbm90LgpuZXdfb2x5YW1waWMkQWdlICU+JSBib3hwbG90KG1haW49IkJveHBsb3QgQWdlIix5bGFiPSJBZ2UiLGNvbCA9ICJncmVlbiIpCgpuZXdfb2x5YW1waWMkSGVpZ2h0ICU+JSBib3hwbG90KG1haW49IkJveHBsb3QgSGVpZ2h0Iix5bGFiPSJIZWlnaHQiLGNvbCA9ICJibHVlIikKCm5ld19vbHlhbXBpYyRXZWlnaHQgJT4lIGJveHBsb3QobWFpbj0iQm94cGxvdCBXZWlnaHQiLHlsYWI9IldlaWdodCIsY29sID0gInJlZCIpCgpuZXdfb2x5YW1waWMkQm9keV9NYXNzX0luZGV4ICU+JSBib3hwbG90KG1haW49IkJveHBsb3QgQm9keSBNYXNzIEluZGV4Iix5bGFiPSJCb2R5IE1hc3MgSW5kZXgiLGNvbCA9ICJvcmFuZ2UiKQpgYGAKCkZyb20gdGhlIGFib3ZlIG91dHB1dCB3ZSBjYW4gY29uY2x1ZGUgdGhhdCB0aGVyZSBhcmUgbm8gb3V0bGllcnMgcHJlc2VudCBpbiB0aGUgZGF0YXNldC4gVGhlIG91dGxpZXJzIGhhdmUgYmVlbiBjYXBwZWQgc3VjY2Vzc2Z1bGx5LgoKIyMJVHJhbnNmb3JtIAoKVHJhbnNmb3JtYXRpb24gaXMgcmVxdWlyZWQgdG8gY2hhbmdlIHRoZSBzY2FsZSBvZiB0aGUgdmFyaWFibGUgb3Igc3RhbmRhcmRpc2UgdGhlIHZhbHVlIG9mIHZhcmlhYmxlIGZvciBiZXR0ZXIgdW5kZXJzdGFuZGluZy4gSXQgY2FuIGFsc28gYmUgdXNlZCB0byBjaGFuZ2UgdGhlIGxpbmVhcml0eSBvZiBhIHJlYWx0aW9uc2hpcC4gSSBoYXZlIHVzZWQgdGhlIGhpc3QoKSBmdW5jdGlvbiB0byBwbG90IGhpc3RvZ3JhbSBvZiB0aGUgbnVtZXJpYyB2YXJpYWJsZS4gVGhlIEFnZSB2YXJpYWJsZSB3YXMgcGxvdHRlZCB1c2luZyB0aGUgaGlzdCgpIGZ1bmN0aW9uIGhlcmUgeGxpbSBhbmQgeWxpbSBhcmUgc2NhbGUgbGltaXRzIG9mIHRoZSB4IGFuZCB5IGF4aXMKCmBgYHtyfQojVXNpbmcgaGlzdCgpIHRvIGNoZWNrIHRoZSBkaXN0cmlidXRpb24gb2YgYWdlCiNSZWZlcnJlZCBmcm9tIHdlZWs5IGxlY3R1cmUgc2xpZGVzIGFuZCB3b3Jrc2hlZXQKaGlzdChuZXckQWdlLCBtYWluID0gIkhpc3RvZ3JhbSBvZiBBZ2UiLCAKICAgICB4bGFiID0gIkFnZSIsCiAgICAgeWxpbSA9IGMoMCwxMjAwMDApLCB4bGltID0gYygwLDgwKSkKCgpgYGAKCkZyb20gdGhlIGFib3ZlIG91dHB1dCBpdCBpcyBjbGVhciB0aGF0IHRoZSBncmFwaCBmb3IgdGhlIEFnZSB2YXJpYWJsZSBpcyByaWdodCBza2V3ZWQuIFRoZXJlZm9yZSBJIGhhdmUgdXNlZCB0aGUgbG9nMTAgdHJhbnNmb3JtYXRpb24gdG8gQWdlIHZhcmlhYmxlIHRvIG9idGFpbiBhIG5vcm1hbCBkaXN0cmlidXRpb24uIEkgaGF2ZSB0cmFuc2Zvcm1lZCBBZ2UgYnkgYXBwbHlpbmcgbG9nMTAoKSBmdW5jdGlvbiB0byBpdCBhbmQgc3RvcmUgaXQgaW4gYW4gb2JqZWN0IG5hbWVkIGFnZV9sb2cuIExhdGVyIHdpdGggdGhlIGhlbHAgb2YgaGlzdCgpIGZ1bmN0aW9uIEkgaGF2ZSBwbG90dGVkIHRoZSBhZ2UgdmFyaWFibGUKCmBgYHtyfQojVXNpbmcgbG9nMTAgdG8gdHJhbnNmb3JtIGFnZSB2YXJpYWJsZQojUmVmZXJyZWQgZnJvbSB3ZWVrOSBsZWN0dXJlIHNsaWRlcyBhbmQgd29ya3NoZWV0CmFnZV9sb2cgPC0gbG9nMTAobmV3JEFnZSkKaGlzdChhZ2VfbG9nLG1haW4gPSAiQWdlIGhpc3RvZ3JhbSBhZnRlciB0cmFuc2Zvcm1hdGlvbiIsCiAgICAgeGxhYiA9ICJBZ2UiLCB5bGltID0gYygwLDgwMDAwKSkKCgpgYGAKCkZyb20gdGhlIG91dHB1dCB3ZSBjYW4gY29uY2x1ZGUgdGhhdCBhZ2UgaXMgbm9ybWFsbHkgZGlzdHJpYnV0ZWQgYWZ0ZXIgdHJhbmZvcm1hdGlvbiAuCgpOb3cgSSBoYXZlIHVzZWQgaGlzdCgpIGZ1bmN0aW9uIHRvIHBsb3QgdGhlIEhlaWdodCB2YXJpYWJsZS4KCmBgYHtyfQojVXNpbmcgaGlzdCgpIHRvIGNoZWNrIHRoZSBkaXN0cmlidXRpb24gb2YgYWdlCiNSZWZlcnJlZCBmcm9tIHdlZWs5IGxlY3R1cmUgc2xpZGVzIGFuZCB3b3Jrc2hlZXQKaGlzdChuZXckSGVpZ2h0LCBtYWluID0gIkhpc3RvZ3JhbSBvZiBIZWlnaHQiLCAKICAgICB4bGFiID0gIkhlaWdodCIsCiAgICAgeWxpbSA9IGMoMCwgMTIwMDAwKSwKICAgICB4bGltID0gYygxMzAsIDIyMCkpCgpgYGAKCkZyb20gdGhlIG91dHB1dCB3ZSBjYW4gY29uY2x1ZGUgdGhhdCBIZWlnaHQgaXMgbm9ybWFsbHkgZGlzdHJpYnV0ZWQgYW5kIHRoZXJlZm9yZSB3ZSBkb250IG5lZWQgdG8gdHJhbmZvcm0gaXQuCgpOb3cgSSBoYXZlIHVzZWQgaGlzdCgpIGZ1bmN0aW9uIHRvIHBsb3QgdGhlIFdlaWdodCB2YXJpYWJsZS4KCmBgYHtyfQojVXNpbmcgaGlzdCgpIHRvIGNoZWNrIHRoZSBkaXN0cmlidXRpb24gb2Ygd2VpZ2h0CiNSZWZlcnJlZCBmcm9tIHdlZWs5IGxlY3R1cmUgc2xpZGVzIGFuZCB3b3Jrc2hlZXQKaGlzdChuZXckV2VpZ2h0LCBtYWluID0gIkhpc3RvZ3JhbSBvZiBXZWlnaHQiLCAKICAgICB4bGFiID0gIldlaWdodCIsCiAgICAgeWxpbSA9IGMoMCwgMTIwMDAwKSwKICAgICB4bGltID0gYygyMCwxNTApKQoKYGBgCgpGcm9tIHRoZSBvdXRwdXQgd2UgY2FuIGNvbmNsdWRlIHRoYXQgZ3JhcGggZm9yIHdlaWdodCB2YXJpYWJsZSBpcyByaWdodCBza2V3ZWQuClRoZXJlZm9yZSBJIGhhdmUgdXNlZCB0aGUgbG9nMTAgdHJhbnNmb3JtYXRpb24gdG8gV2VpZ2h0IHZhcmlhYmxlIHRvIG9idGFpbiBhIG5vcm1hbCBkaXN0cmlidXRpb24uIEkgaGF2ZSB0cmFuc2Zvcm1lZCBXZWlnaHQgYnkgYXBwbHlpbmcgbG9nMTAoKSBmdW5jdGlvbiB0byBpdCBhbmQgc3RvcmUgaXQgaW4gYW4gb2JqZWN0IG5hbWVkIHdlaWdodF9sb2cuCgpgYGB7cn0KI1VzaW5nIGxvZzEwIHRvIHRyYW5zZm9ybSBhZ2UgdmFyaWFibGUKI1JlZmVycmVkIGZyb20gd2VlazkgbGVjdHVyZSBzbGlkZXMgYW5kIHdvcmtzaGVldAp3ZWlnaHRfbG9nIDwtIGxvZzEwKG5ldyRXZWlnaHQpCmhpc3Qod2VpZ2h0X2xvZyxtYWluID0gIldlaWdodCBoaXN0b2dyYW0gYWZ0ZXIgdHJhbnNmb3JtYXRpb24iLHhsYWIgPSAiV2VpZ2h0IiwKICAgICB5bGltID0gYygwLDEyMDAwMCkpCmBgYAoKRnJvbSB0aGUgb3V0cHV0IHdlIGNhbiBjb25jbHVkZSB0aGF0IHdlaWdodCBpcyBub3JtYWxseSBkaXN0cmlidXRlZCBhZnRlciB0cmFuZm9ybWF0aW9uLgoKQWZ0ZXIgcHJvY2Vzc2luZyBhbmQgaW52ZXN0aWdhdGluZyB0aGUgaGlzdG9yaWNhbCBkYXRhc2V0IG9mIG9seWFtcGljIGdhbWVzIEkgaGF2ZSB0cmFuc2Zvcm1lZCB0aGUgZGF0YXNldCBieSBjb252ZXJ0aW5nIHRoZSB2YXJpYWJsZXMgdG8gYXBwcm9wcmFpdGUgZGF0YSB0eXBlcywgaGFuZGxpbmcgb3V0bGllcnMgYW5kIG1pc3NpbmcgdmFsdWVzLCB0cmFuc2Zvcm1pbmcgdGhlIHZhcmlhYmxlcy4gVGhlIGRhdGFzZXQgaXMgbm93IGNsZWFuIGFuZCBjYW4gYmUgdXNlZCBmb3IgZnVydGhlciBhbmFseXNpcy4KCgo8YnI+Cjxicj4K