Executive Summary
Data preprocessing is a procedure that takes a raw, incomplete/inconsistent and noisy set of data and transforms it into a tidy format of data. This tidy form of data is more understandable which will make our task of doing an analysis on the data set easier. By cleaning the data we will automatically get a more refined and appropriate result of our analysis.
Our data set includes the information on the FIFA Worldcup Matches from 1930 to 2014 as well as the players with the number of goals conceded by each team. Using this data in a tidy format we would like to predict/ analyze the trends of these matches. For example, how a team performs on its home ground versus an away ground (known as Home Field Advantage)? Can a team qualify for the next WorldCup match depending on their previous performances? Data analysis is also changing the recruitment process in football. The odds of a team to win a match are tremendously based on the Match Analysis.
Two datasets WorldCupMatches with 20 variables and WorldCupPlayers with 9 variables belonging to the period between 1930 to 2014 are considered for the investigation.These datasets will later be cleaned, subsetted and merged to aid the objective of our analysis.
Post merging, checks will be performed on the joined dataset for the verification of datatypes of each variable. This is followed by creating new variables for second half-time goals and total number of goals which will help us understand the trend better.
Missing values are thoroughly checked. All numerical variables are investigated against outliers. Resulting outliers are then excluded using filteration.
The variables for total number of goals and attendances are then observed through a histogram. To further normalize, the distribution transformation technique is applied.
Required packages
All the necessary packages are installed and loaded for the purpose of reproducing the report in here:
#install.packages(c("readr","magrittr","ggplot2","lubridate","Hmisc","dplyr",
#"car","knitr","outliers","kableExtra","rmarkdown","tidyr","forecast","editrules")
#install packages only if required
library(readr) # Useful for importing data
library(magrittr) # Useful for pipe %>% Operator etc
library(ggplot2) # Useful for data Visualisations & creating plots
library(lubridate) # Useful for date and time manipulation
library(Hmisc) # Useful for value imputation
library(dplyr) # Useful for data manipulation
library(car) # Useful for creating Anova tables
library(knitr) # Useful for creating nice tables
library(outliers) # Useful for outlier detection
library(kableExtra) # Useful to build complex tables
library(rmarkdown) # Useful to create neat records of analysis
library(tidyr) # Useful for tidy up the dataset
library(forecast) # Useful for displaying and analysing univariate time series
library(editrules) # Useful for detecting obvious errors
Data
The dataset for this study is acquired from the website https://www.kaggle.com which is an online platform for users to download, upload/publish datasets, explore and much more. This open dataset has Creative Commons Licence and can be found on : https://www.kaggle.com/abecklas/fifa-world-cup
The dataset WorldCupMatches contains 20 variables with 852 rows of records. The highlighted variables have been considered important and these variables are then subsetted for a more apt analysis with respect to our objective. The rest of the variables are not necessary for our analysis at this point. Variables like Year are not needed as this data can be derived from the variable Datetime. The resulting subset WorldCupMatches_1 now has 11 variables.
- Year: The year in which the match was played
- Datetime: The date on which the match was played along with a 24 hour format time
- Stage: The stage at which the match was played
- Stadium: Stadium name where the match was held
- City: The city name where the match was played
- Home Team Name: Home team country name
- Home Team Goals: Total goals scored by the home team by the end of the match
- Away Team Goals: Total goals scored by the away team by the end of the match
- Away Team Name: Away team country name
- Win conditions: Special win condition (if any)
- Attendance: Total crowd present at the stadium
- Half-time Home Goals: Goals scored by the home team until half time
- Half-time Away Goals: Goals scored by the away team until half time
- Referee: Name of the first referee
- Assistant 1: Name of the first assistant referee (linesman)
- Assistant 2: Name of the second assistant referee (linesman)
- RoundID: Unique ID of the round
- MatchID: Unique ID of the match
- Home Team Initials: Home team country’s three letter initials
- Away Team Initials: Away team country’s three letter initials
The dataset WorldCupPlayers contains 9 variables with 37784 rows of records.The highlighted variables have been considered important and these variables are then subsetted for a more apt analysis with respect to our objective. The rest of the vraiables are not necessary for our analysis at this point. The resulting subset WorldCupPlayers_1 now has 4 variables.
- RoundID: Unique ID of the round
- MatchID: Unique ID of the match
- Team Initials: Player’s team initials
- Coach Name: Name and country of the team coach
- Line-up: S=Line-up, N=Substitute
- Shirt Number: Shirt number if available
- Player Name: Name of the player
- Position: C= Captain, GK= Goalkeeper
- Event: G=Goal, OG=Own Goal, Y=Yellow Card, R=Red Card, SY = Red Card by second yellow, P=Penalty, MP=Missed Penalty, I = Substitution In, O=Substitute Out
Steps performed:
Step 1: Using the readr function read_csv() both the .csv files: WorldcupMatches & Worldcupplayers are read/imported and appropriate variables are assigned for further use.
Step 2: Datasets are checked for any duplicate rows and only distinct rows are kept. After removal of duplicates, the dimensions of both the datasets are checked.
Step 3: Subsets of both the datasets are created using the subset() function to include the above highlighted variables, in the dataframes WorldCupMatches_1 and WorldCupPlayers_1 respectively. The head() function displays the first 6 rows of the subsets.
Step 4: The inner_join() function is used to merge both the datasets using the common attribute as MatchID. The Inner Join selects all rows from both participating tables as long as there is a match between the columns. Our final dataset is now ready for further analysis with 14 columns and 37048 rows.
#set working directory
setwd("~/preprocessing_3")
#Reading/Importing the two datasets from .csv files
WorldCupMatches <- read_csv(file = "WorldCupMatches.csv")
Parsed with column specification:
cols(
.default = col_character(),
Year = [32mcol_double()[39m,
`Home Team Goals` = [32mcol_double()[39m,
`Away Team Goals` = [32mcol_double()[39m,
Attendance = [32mcol_double()[39m,
`Half-time Home Goals` = [32mcol_double()[39m,
`Half-time Away Goals` = [32mcol_double()[39m,
RoundID = [32mcol_double()[39m,
MatchID = [32mcol_double()[39m
)
See spec(...) for full column specifications.
WorldCupPlayers <- read_csv(file = "WorldCupPlayers.csv")
Parsed with column specification:
cols(
RoundID = [32mcol_double()[39m,
MatchID = [32mcol_double()[39m,
`Team Initials` = [31mcol_character()[39m,
`Coach Name` = [31mcol_character()[39m,
`Line-up` = [31mcol_character()[39m,
`Shirt Number` = [32mcol_double()[39m,
`Player Name` = [31mcol_character()[39m,
Position = [31mcol_character()[39m,
Event = [31mcol_character()[39m
)
#check for duplicates and subset only distinct rows
WorldCupMatches_distinct <- WorldCupMatches %>% distinct()
WorldCupMatches_distinct
WorldCupPlayers_distinct <- WorldCupPlayers %>% distinct()
WorldCupPlayers_distinct
#check dimensions
dim(WorldCupMatches_distinct)
[1] 837 20
dim(WorldCupPlayers_distinct)
[1] 37048 9
#Subsetting the Worldcup Matches dataset
WorldCupMatches_1 <- subset( WorldCupMatches_distinct[,c(2,5:9,11,12,13,17,18)] )
#Viewing the subset
head(WorldCupMatches_1)
#Subsetting the Worldcup Players dataset
WorldCupPlayers_1 <- subset( WorldCupPlayers_distinct[,c(2,4,5,7)] )
#Viewing the subset
head(WorldCupPlayers_1)
#Left joining the datasets of Match and players details by the common attribute "MatchID"
Worldcup <- WorldCupMatches_1 %>% inner_join(WorldCupPlayers_1, by = "MatchID")
#Viewing the final dataset
dim(Worldcup)
[1] 37048 14
head(Worldcup)
Understand Data
In this section, we will be investigating the structure of the dataset. Datatype of each variable is observed.
While investigating the datatypes, it was found that the variable Datetime is a Character variable. Moreover the Date and time information is stored in a single variable.
Also, the variable Line-up is a character with only two categories ‘S=Line-up’ and ‘N=Substitute’.
Steps performed:
Step 1: The structure of the dataset Worldcup is displayed using the str() function.
Step 2: To confirm the structure of the variable Datetime the typeof() function is used. We first use the tidyr function Separate() to split the ‘Date’ and ‘Time’ as 2 different columns. We then format the separated columns. The as.date() function is used to format the date into a ‘DateMonthYear’ form and the parse_hm() function is used to format the time into ‘HH:MM:SS’ form. The structure of both are then displayed to verify the formating.
Step 3: The datatype of the variable Line-up is checked using the function typeof().
Step 4: To convert the character datatype to a factor data type, we use the function factor() with two levels ‘N’ and ‘S’ for ‘Substitute’ andd ‘Line-up’. The function is.factor() will confirm this conversion.
Step 5: The first 6 rows of the formatted dataset is displayed using head() function.
#Checking the structure of the dataset
str(Worldcup)
Classes ‘tbl_df’, ‘tbl’ and 'data.frame': 37048 obs. of 14 variables:
$ Datetime : chr "13 Jul 1930 - 15:00" "13 Jul 1930 - 15:00" "13 Jul 1930 - 15:00" "13 Jul 1930 - 15:00" ...
$ City : chr "Montevideo" "Montevideo" "Montevideo" "Montevideo" ...
$ Home Team Name : chr "France" "France" "France" "France" ...
$ Home Team Goals : num 4 4 4 4 4 4 4 4 4 4 ...
$ Away Team Goals : num 1 1 1 1 1 1 1 1 1 1 ...
$ Away Team Name : chr "Mexico" "Mexico" "Mexico" "Mexico" ...
$ Attendance : num 4444 4444 4444 4444 4444 ...
$ Half-time Home Goals: num 3 3 3 3 3 3 3 3 3 3 ...
$ Half-time Away Goals: num 0 0 0 0 0 0 0 0 0 0 ...
$ RoundID : num 201 201 201 201 201 201 201 201 201 201 ...
$ MatchID : num 1096 1096 1096 1096 1096 ...
$ Coach Name : chr "CAUDRON Raoul (FRA)" "LUQUE Juan (MEX)" "CAUDRON Raoul (FRA)" "LUQUE Juan (MEX)" ...
$ Line-up : chr "S" "S" "S" "S" ...
$ Player Name : chr "Alex THEPOT" "Oscar BONFIGLIO" "Marcel LANGILLER" "Juan CARRENO" ...
#Checking the data type of Column `Datetime`
typeof(Worldcup$Datetime)
[1] "character"
#Split column 'Datetime' into `Date` and `Time` columns
Worldcup <- Worldcup %>% tidyr::separate(Datetime,into = c("Date", "Time"),sep =" - ")
#Formating column `Date` into Day/Month/Year format of date
Worldcup$Date <- as.Date(Worldcup$Date, format = "%d %b %Y")
#Confirming the structure of the 'Date' Column
str(Worldcup$Date)
Date[1:37048], format: "1930-07-13" "1930-07-13" "1930-07-13" "1930-07-13" "1930-07-13" "1930-07-13" "1930-07-13" ...
#Formating column `Time` into Hour/Month/Sec format of time
Worldcup$Time <- hms::parse_hm(Worldcup$Time)
#Confirming the structure of `Time` Column
str(Worldcup$Time)
'hms' num [1:37048] 15:00:00 15:00:00 15:00:00 15:00:00 ...
- attr(*, "units")= chr "secs"
#Checking the data type of Column `Line-up`
typeof(Worldcup$`Line-up`)
[1] "character"
#Converting variables from character to factor data type
Worldcup$`Line-up`<-factor(Worldcup$`Line-up`,
levels=c("N","S"),
labels=c("Substitute","Line-up"))
#To confirm the conversion
is.factor(Worldcup$`Line-up`)
[1] TRUE
#Displaying the formated dataset
head(Worldcup)
Tidy & Manipulate Data - I
In this section, the dataset is checked against following characteristics established by Hadley Wickham and Grolemund:
- Each variable must have its own column.
- Each unique observation has its own row.
- All values have its own cell.
Post examination, it can be observed that the dataset is readily available in a tidy format. Thus it can be inferred that the dataset has all the above mentioned characteristics and therefore, it needs no further tidying.
#Displaying the Tidy dataset
head(Worldcup)
Tidy & Manipulate Data - II
In this section, we use the dplyr function mutate() to create 3 new variables that will help us analyse the dataset further. The new variables are Home_Sec_half, Away_Sec_half and Total_Goals. Using the Second half goals we can find out if the goals are scored in the last half of the time remaining in the game. It is commonly noticed that most of the goals are indeed scored within the last few minutes. The total number of goals will help us look at their total score after each match.
Scan - I
To pre-process the data effectively, dataset is checked for missing values,inconsistencies and obvious errors. Through the baseR functions colSums() and is.na(), we find that there are only 184 missing values in Attendance variable.
Since this is only approximately 0.6% of the size of the variable data set, we can exclude these values while performing mathematical operations (e.g., calculating mean & standard deviation for plotting normal curve) in order to not bias the analysis. This can be done by using the na.rm=TRUE argument inside the mathematical functions.
To check for special values : -Inf, Inf and NaN, we define a function inside sapply() and calculate the total number of special values for each column. We find that there are no such values in the dataset.
To check for obvious inconsistencies/errors, we define a set of rules using editset() funcion from editrules package. The dataset Worldcup is checked against these rules using the violatedEdits() function and its summary is checked. We find that there are no obvious errors in the dataset.
#check for missing values
colSums(is.na(Worldcup))
Date Time City Home.Team.Name
0 0 0 0
Home.Team.Goals Away.Team.Goals Away.Team.Name Attendance
0 0 0 46
Half.time.Home.Goals Half.time.Away.Goals RoundID MatchID
0 0 0 0
Coach.Name Line.up Player.Name Home_Sec_Half
0 0 0 0
Away_Sec_Half Total_Goals
0 0
percent_na <- (sum(is.na(Worldcup$Attendance)) / length(Worldcup$Attendance))*100
percent_na
[1] 0.1241632
#check for special values : infinite (-inf or +inf) and NaN
sapply(Worldcup, function(x) sum(if (is.numeric(x)) (is.infinite(x) | is.nan(x) )))
Date Time City Home.Team.Name
0 0 0 0
Home.Team.Goals Away.Team.Goals Away.Team.Name Attendance
0 0 0 0
Half.time.Home.Goals Half.time.Away.Goals RoundID MatchID
0 0 0 0
Coach.Name Line.up Player.Name Home_Sec_Half
0 0 0 0
Away_Sec_Half Total_Goals
0 0
#check for obvious errors
Rules <- editset(c("Home.Team.Goals >=0",
"Away.Team.Goals >= 0",
"Attendance >=0",
"Half.time.Home.Goals >=0",
"Half.time.Away.Goals >=0",
"Half.time.Home.Goals <= Home.Team.Goals",
"Half.time.Away.Goals <= Away.Team.Goals",
"Line.up %in% c('Line-up','Substitute')"))
Rules
Data model:
dat1 : Line.up %in% c('Line-up', 'Substitute')
Edit set:
num1 : 0 <= Home.Team.Goals
num2 : 0 <= Away.Team.Goals
num3 : 0 <= Attendance
num4 : 0 <= Half.time.Home.Goals
num5 : 0 <= Half.time.Away.Goals
num6 : Half.time.Home.Goals <= Home.Team.Goals
num7 : Half.time.Away.Goals <= Away.Team.Goals
violated <- violatedEdits(Rules, Worldcup)
summary(violated)
No violations detected, 46 checks evaluated to NA
NULL
Scan - II
In this section we will examine all the numeric variables in the dataset for outliers using the Univariate Outlier Detection Method of boxplot. We will then filter out the outliers from the dataset to obtain a more focussed and clean dataset.
Steps performed:
Step 1: The Boxplot of the following numeric variables have been plotted respectively with their Y-axis labels and then stored into appropriate variables for the ease of further steps. - Home Team Goals - Away Team Goals - Half-Time Home Goals - Half-Time Away Goals - Second Half Home Goals - Second Half Away Goals
Step 2: The out() function along with the variable names we assigned in the last step gives us the outliers of each numeric variable.
Step 3: The baseR function min() of the outliers will give us the minima of the outliers of each of the numeric variable which we can use in the next step to filter them out.
Step 4: We are now using the dplyr function filter() to filter out the meaningful and important data from the outliers. The above mentioned 6 variables are filtered using the Minima values from Step 3 and assigned back to the main dataset Worldcup.
Step 5: Boxplots of the clarified variables are displayed without any outliers.
#Drawing a boxplot for the numeric variables to check for outliers
#Outliers of the numeric variables are assigned to a new variable
#Finding the minimum value of the outliers of each variable
BP_1 <- boxplot(Worldcup$Home.Team.Goals,main="Boxplot of Home Team Goals",ylab ="Home team score",col = "palegreen")

min(BP_1$out)
[1] 7
BP_2 <- boxplot(Worldcup$Away.Team.Goals,main="Boxplot of Away Team Goals",ylab ="Away team score", col = "palegreen")

min(BP_2$out)
[1] 7
BP_3 <-boxplot(Worldcup$Half.time.Home.Goals,main="Boxplot of Home Team Half time Goals",ylab ="Home team score",col = "palegreen")

min(BP_3$out)
[1] 3
BP_4 <-boxplot(Worldcup$Half.time.Away.Goals,main="Boxplot of Away Team Half time Goals",ylab ="Away team score",col = "palegreen")

min(BP_4$out)
[1] 3
BP_5 <-boxplot(Worldcup$Home_Sec_Half,main="Boxplot of Home Team Second half Goals",ylab ="Second half score",col = "palegreen")

min(BP_5$out)
[1] 6
BP_6 <-boxplot(Worldcup$Away_Sec_Half,main="Boxplot of Away Team Second half Goals",ylab ="Second half score",col = "palegreen")

min(BP_6$out)
[1] 3
#Filtering the variables using the minimum values of the outliers
Worldcup %<>% filter(Home.Team.Goals< min(BP_1$out) )
Worldcup %<>% filter(Away.Team.Goals< min(BP_2$out) )
Worldcup %<>% filter(Half.time.Home.Goals< min(BP_3$out) )
Worldcup %<>% filter(Half.time.Away.Goals< min(BP_4$out) )
Worldcup %<>% filter(Home_Sec_Half< min(BP_5$out) )
Worldcup %<>% filter(Away_Sec_Half< min(BP_6$out) )
#Displaying the Boxplots of the filtered variables
boxplot(Worldcup$Home.Team.Goals,main="Boxplot of Home Team Goals",ylab ="Home team score",col = "Skyblue")

boxplot(Worldcup$Away.Team.Goals,main="Boxplot of Away Team Goals",ylab ="Away team score", col = "Skyblue")

boxplot(Worldcup$Half.time.Home.Goals,main="Boxplot of Home Team Half time Goals",ylab ="Home team score",col = "Skyblue")

boxplot(Worldcup$Half.time.Away.Goals,main="Boxplot of Away Team Half time Goals",ylab ="Away team score",col = "Skyblue")

boxplot(Worldcup$Home_Sec_Half,main="Boxplot of Home Team Second-half Goals",ylab ="Second-half score",col = "Skyblue")

boxplot(Worldcup$Away_Sec_Half,main="Boxplot of Away Team Second-half Goals",ylab ="Second-half score",col = "Skyblue")

Transform
Data transformation is applied on the variables : Attendance and Total goals.
Steps performed:
Step 1: We check the distribution of both the variables using the hist() function. We can see in both the graphs that the data is skewed to the right.
Step 2: We overlay a normal distribution curve over the histograms in order to visualise the trends better. It is to be noted that since Attendance variable contains 184 NA values [as discussed previously], we use na.rm=TRUE argument to exclude them.
Step 3: To decrease the skewness and normalize the data we aaply:
Square-root transformation for Attendance (to reduce right-skewness and applying to the zero values in the histogram)
Box-Cox transformation for Total_Goals with lambda = "auto"
Step 4: Checking the transformed data, we observe that the right-skewness has significantly reduced and it has converted to a normal distribution.
#Visualising Attendances
AttendanceHist <- hist(Worldcup$Attendance,
col="firebrick",
main ="Histogram of Attendances",
xlab ="Attendances",
prob=TRUE) #or freq=FALSE
curve(dnorm(x, mean=mean(Worldcup$Attendance, na.rm=TRUE),sd=sd(Worldcup$Attendance, na.rm=TRUE) ),
add=TRUE, col="green", lwd=2 )
legend("topright",legend=c("Normal Curve"),fill=c("green"),
text.font = 4,cex = 0.8,border= F, box.lty = 3)

#Visualising Total No. of Goals
TotalGoalHist <- hist(Worldcup$Total_Goals,
col="yellowgreen",
main ="Histogram of Goals",
xlab="Total No. of Goals",
prob=TRUE )
curve(dnorm(x, mean=mean(Worldcup$Total_Goals),sd=sd(Worldcup$Total_Goals) ),
add=TRUE, col="red", lwd=2 )
legend("topright",legend=c("Normal Curve"),fill=c("red"),
text.font = 4,cex = 0.8,border= F, box.lty = 3)

# Normalising using square-root transformation
Norm1 <- sqrt(Worldcup$Attendance)
# Normalising using Box-Cox transformation
Norm2 <- BoxCox(Worldcup$Total_Goals, lambda = "auto")
#Displaying the normalised histograms
hist(Norm1, col="firebrick", main="Normalised Histogram of Attendances",
xlab="Attendances", prob=TRUE)
curve(dnorm(x, mean=mean(Norm1, na.rm=T),sd=sd(Norm1, na.rm=T)),
add=TRUE, col="green", lwd=2 )
legend("topright",legend=c("Normal Curve"),fill=c("green"),
text.font = 4,cex = 0.8,border= F, box.lty = 3)

hist(Norm2, col="yellowgreen", main="Normalised Histogram of Goals", xlab="Total No. of Goals",
prob=TRUE)
curve(dnorm(x, mean=mean(Norm2),sd=sd(Norm2) ),
add=TRUE, col="red", lwd=2 )
legend("topright",legend=c("Normal Curve"),fill=c("red"),
text.font = 4,cex = 0.8,border= F, box.lty = 3)

Conclusion
With respect to the results above and the tasks performed throughout the report, the dataset on FIFA Worldcup Matches from 1930 to 2014 (excluding 1942 & 1946) is now tidy and ready for further match analysis. With these normalised data , we can further explore questions on how a team performs on its home ground , whether a team can qualify for the next worldcup match depending on previous perfomances and future match strategies using methods like linear regression analysis, anova, correlation and hypothesis testing.
LS0tDQp0aXRsZTogIk1BVEgyMzQ5IFNlbWVzdGVyIDIsIDIwMTkiDQphdXRob3I6ICJBbndlc2hhIER1dHRhIChzMzc5MDg4NiksU2hhcmFkYSBLdXBwYXNhZCAoUzM3OTkwMjIpLCBUYXBvam95IERlIChzMzc5MDg5OSkiDQpzdWJ0aXRsZTogQXNzaWdubWVudCAzDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQNCi0tLQ0KKioqDQojIyBFeGVjdXRpdmUgU3VtbWFyeSANCg0KRGF0YSBwcmVwcm9jZXNzaW5nIGlzIGEgcHJvY2VkdXJlIHRoYXQgdGFrZXMgYSByYXcsIGluY29tcGxldGUvaW5jb25zaXN0ZW50IGFuZCBub2lzeSBzZXQgb2YgZGF0YSBhbmQgdHJhbnNmb3JtcyBpdCBpbnRvIGEgdGlkeSBmb3JtYXQgb2YgZGF0YS4gVGhpcyB0aWR5IGZvcm0gb2YgZGF0YSBpcyBtb3JlIHVuZGVyc3RhbmRhYmxlIHdoaWNoIHdpbGwgbWFrZSBvdXIgdGFzayBvZiBkb2luZyBhbiBhbmFseXNpcyBvbiB0aGUgZGF0YSBzZXQgZWFzaWVyLiBCeSBjbGVhbmluZyB0aGUgZGF0YSB3ZSB3aWxsIGF1dG9tYXRpY2FsbHkgZ2V0IGEgbW9yZSByZWZpbmVkIGFuZCBhcHByb3ByaWF0ZSByZXN1bHQgb2Ygb3VyIGFuYWx5c2lzLg0KDQpPdXIgZGF0YSBzZXQgaW5jbHVkZXMgdGhlIGluZm9ybWF0aW9uIG9uIHRoZSBGSUZBIFdvcmxkY3VwIE1hdGNoZXMgZnJvbSAxOTMwIHRvIDIwMTQgYXMgd2VsbCBhcyB0aGUgcGxheWVycyB3aXRoIHRoZSBudW1iZXIgb2YgZ29hbHMgY29uY2VkZWQgYnkgZWFjaCB0ZWFtLiBVc2luZyB0aGlzIGRhdGEgaW4gYSB0aWR5IGZvcm1hdCB3ZSB3b3VsZCBsaWtlIHRvIHByZWRpY3QvIGFuYWx5emUgdGhlIHRyZW5kcyBvZiB0aGVzZSBtYXRjaGVzLiBGb3IgZXhhbXBsZSwgaG93IGEgdGVhbSBwZXJmb3JtcyBvbiBpdHMgaG9tZSBncm91bmQgdmVyc3VzIGFuIGF3YXkgZ3JvdW5kIChrbm93biBhcyBIb21lIEZpZWxkIEFkdmFudGFnZSk/IENhbiBhIHRlYW0gcXVhbGlmeSBmb3IgdGhlIG5leHQgV29ybGRDdXAgbWF0Y2ggZGVwZW5kaW5nIG9uIHRoZWlyIHByZXZpb3VzIHBlcmZvcm1hbmNlcz8gRGF0YSBhbmFseXNpcyBpcyBhbHNvIGNoYW5naW5nIHRoZSByZWNydWl0bWVudCBwcm9jZXNzIGluIGZvb3RiYWxsLiBUaGUgb2RkcyBvZiBhIHRlYW0gdG8gd2luIGEgbWF0Y2ggYXJlIHRyZW1lbmRvdXNseSBiYXNlZCBvbiB0aGUgTWF0Y2ggQW5hbHlzaXMuDQoNCiogVHdvIGRhdGFzZXRzIGBXb3JsZEN1cE1hdGNoZXNgIHdpdGggKioyMCoqIHZhcmlhYmxlcyBhbmQgYFdvcmxkQ3VwUGxheWVyc2Agd2l0aCAqKjkqKiB2YXJpYWJsZXMgYmVsb25naW5nIHRvIHRoZSBwZXJpb2QgYmV0d2VlbiAxOTMwIHRvIDIwMTQgYXJlIGNvbnNpZGVyZWQgZm9yIHRoZSBpbnZlc3RpZ2F0aW9uLlRoZXNlIGRhdGFzZXRzIHdpbGwgbGF0ZXIgYmUgY2xlYW5lZCwgc3Vic2V0dGVkIGFuZCBtZXJnZWQgdG8gYWlkIHRoZSBvYmplY3RpdmUgb2Ygb3VyIGFuYWx5c2lzLg0KDQoqIFBvc3QgbWVyZ2luZywgY2hlY2tzIHdpbGwgYmUgcGVyZm9ybWVkIG9uIHRoZSBqb2luZWQgZGF0YXNldCBmb3IgdGhlIHZlcmlmaWNhdGlvbiBvZiBkYXRhdHlwZXMgb2YgZWFjaCB2YXJpYWJsZS4gVGhpcyBpcyBmb2xsb3dlZCBieSBjcmVhdGluZyBuZXcgdmFyaWFibGVzIGZvciBzZWNvbmQgaGFsZi10aW1lIGdvYWxzIGFuZCB0b3RhbCBudW1iZXIgb2YgZ29hbHMgd2hpY2ggd2lsbCBoZWxwIHVzIHVuZGVyc3RhbmQgdGhlIHRyZW5kIGJldHRlci4NCg0KKiBNaXNzaW5nIHZhbHVlcyBhcmUgdGhvcm91Z2hseSBjaGVja2VkLiBBbGwgbnVtZXJpY2FsIHZhcmlhYmxlcyBhcmUgaW52ZXN0aWdhdGVkIGFnYWluc3Qgb3V0bGllcnMuIFJlc3VsdGluZyBvdXRsaWVycyBhcmUgdGhlbiBleGNsdWRlZCB1c2luZyBmaWx0ZXJhdGlvbi4NCg0KKiBUaGUgdmFyaWFibGVzIGZvciB0b3RhbCBudW1iZXIgb2YgZ29hbHMgYW5kIGF0dGVuZGFuY2VzIGFyZSB0aGVuIG9ic2VydmVkIHRocm91Z2ggYSBoaXN0b2dyYW0uIFRvIGZ1cnRoZXIgbm9ybWFsaXplLCB0aGUgZGlzdHJpYnV0aW9uIHRyYW5zZm9ybWF0aW9uIHRlY2huaXF1ZSBpcyBhcHBsaWVkLg0KDQoqKioNCg0KIyMgUmVxdWlyZWQgcGFja2FnZXMgDQoNCkFsbCB0aGUgbmVjZXNzYXJ5IHBhY2thZ2VzIGFyZSBpbnN0YWxsZWQgYW5kIGxvYWRlZCBmb3IgdGhlIHB1cnBvc2Ugb2YgcmVwcm9kdWNpbmcgdGhlIHJlcG9ydCBpbiBoZXJlOg0KDQpgYGB7cn0NCiNpbnN0YWxsLnBhY2thZ2VzKGMoInJlYWRyIiwibWFncml0dHIiLCJnZ3Bsb3QyIiwibHVicmlkYXRlIiwiSG1pc2MiLCJkcGx5ciIsDQojImNhciIsImtuaXRyIiwib3V0bGllcnMiLCJrYWJsZUV4dHJhIiwicm1hcmtkb3duIiwidGlkeXIiLCJmb3JlY2FzdCIsImVkaXRydWxlcyIpDQojaW5zdGFsbCBwYWNrYWdlcyBvbmx5IGlmIHJlcXVpcmVkDQpsaWJyYXJ5KHJlYWRyKSAjIFVzZWZ1bCBmb3IgaW1wb3J0aW5nIGRhdGEgDQpsaWJyYXJ5KG1hZ3JpdHRyKSAjIFVzZWZ1bCBmb3IgcGlwZSAlPiUgT3BlcmF0b3IgZXRjIA0KbGlicmFyeShnZ3Bsb3QyKSAjIFVzZWZ1bCBmb3IgZGF0YSBWaXN1YWxpc2F0aW9ucyAmIGNyZWF0aW5nIHBsb3RzIA0KbGlicmFyeShsdWJyaWRhdGUpICMgVXNlZnVsIGZvciBkYXRlIGFuZCB0aW1lIG1hbmlwdWxhdGlvbiANCmxpYnJhcnkoSG1pc2MpICMgVXNlZnVsIGZvciB2YWx1ZSBpbXB1dGF0aW9uIA0KbGlicmFyeShkcGx5cikgIyBVc2VmdWwgZm9yIGRhdGEgbWFuaXB1bGF0aW9uIA0KbGlicmFyeShjYXIpICMgVXNlZnVsIGZvciBjcmVhdGluZyBBbm92YSB0YWJsZXMgDQpsaWJyYXJ5KGtuaXRyKSAjIFVzZWZ1bCBmb3IgY3JlYXRpbmcgbmljZSB0YWJsZXMgDQpsaWJyYXJ5KG91dGxpZXJzKSAjIFVzZWZ1bCBmb3Igb3V0bGllciBkZXRlY3Rpb24gDQpsaWJyYXJ5KGthYmxlRXh0cmEpICMgVXNlZnVsIHRvIGJ1aWxkIGNvbXBsZXggdGFibGVzIA0KbGlicmFyeShybWFya2Rvd24pICMgVXNlZnVsIHRvIGNyZWF0ZSBuZWF0IHJlY29yZHMgb2YgYW5hbHlzaXMgDQpsaWJyYXJ5KHRpZHlyKSAjIFVzZWZ1bCBmb3IgdGlkeSB1cCB0aGUgZGF0YXNldA0KbGlicmFyeShmb3JlY2FzdCkgIyBVc2VmdWwgZm9yIGRpc3BsYXlpbmcgYW5kIGFuYWx5c2luZyB1bml2YXJpYXRlIHRpbWUgc2VyaWVzDQpsaWJyYXJ5KGVkaXRydWxlcykgIyBVc2VmdWwgZm9yIGRldGVjdGluZyBvYnZpb3VzIGVycm9ycyANCmBgYA0KDQoqKioNCiMjIERhdGEgDQoNCiogVGhlIGRhdGFzZXQgZm9yIHRoaXMgc3R1ZHkgaXMgYWNxdWlyZWQgZnJvbSB0aGUgd2Vic2l0ZSBodHRwczovL3d3dy5rYWdnbGUuY29tIHdoaWNoIGlzIGFuIG9ubGluZSBwbGF0Zm9ybSBmb3IgdXNlcnMgdG8gZG93bmxvYWQsIHVwbG9hZC9wdWJsaXNoIGRhdGFzZXRzLCBleHBsb3JlIGFuZCBtdWNoIG1vcmUuIFRoaXMgb3BlbiBkYXRhc2V0IGhhcyBDcmVhdGl2ZSBDb21tb25zIExpY2VuY2UgYW5kIGNhbiBiZSBmb3VuZCBvbiA6IGh0dHBzOi8vd3d3LmthZ2dsZS5jb20vYWJlY2tsYXMvZmlmYS13b3JsZC1jdXAgDQoNCiogVGhlIGRhdGFzZXQgYFdvcmxkQ3VwTWF0Y2hlc2AgY29udGFpbnMgKioyMCoqIHZhcmlhYmxlcyB3aXRoICoqODUyKiogcm93cyBvZiByZWNvcmRzLiBUaGUgPG1hcms+aGlnaGxpZ2h0ZWQ8L21hcms+IHZhcmlhYmxlcyBoYXZlIGJlZW4gY29uc2lkZXJlZCBpbXBvcnRhbnQgYW5kIHRoZXNlIHZhcmlhYmxlcyBhcmUgdGhlbiBzdWJzZXR0ZWQgZm9yIGEgbW9yZSBhcHQgYW5hbHlzaXMgd2l0aCByZXNwZWN0IHRvIG91ciBvYmplY3RpdmUuIFRoZSByZXN0IG9mIHRoZSB2YXJpYWJsZXMgYXJlIG5vdCBuZWNlc3NhcnkgZm9yIG91ciBhbmFseXNpcyBhdCB0aGlzIHBvaW50LiBWYXJpYWJsZXMgbGlrZSBgWWVhcmAgYXJlIG5vdCBuZWVkZWQgYXMgdGhpcyBkYXRhIGNhbiBiZSBkZXJpdmVkIGZyb20gdGhlIHZhcmlhYmxlIGBEYXRldGltZWAuIFRoZSByZXN1bHRpbmcgc3Vic2V0IGBXb3JsZEN1cE1hdGNoZXNfMWAgbm93IGhhcyAxMSB2YXJpYWJsZXMuDQoNCiAgLSAqKlllYXIqKjogVGhlIHllYXIgaW4gd2hpY2ggdGhlIG1hdGNoIHdhcyBwbGF5ZWQNCiAgLSA8bWFyaz4qKkRhdGV0aW1lKio6IFRoZSBkYXRlIG9uIHdoaWNoIHRoZSBtYXRjaCB3YXMgcGxheWVkIGFsb25nIHdpdGggYSAyNCBob3VyIGZvcm1hdCB0aW1lPC9tYXJrPg0KICAtICoqU3RhZ2UqKjogVGhlIHN0YWdlIGF0IHdoaWNoIHRoZSBtYXRjaCB3YXMgcGxheWVkDQogIC0gKipTdGFkaXVtKio6IFN0YWRpdW0gbmFtZSB3aGVyZSB0aGUgbWF0Y2ggd2FzIGhlbGQNCiAgLSA8bWFyaz4qKkNpdHkqKjogVGhlIGNpdHkgbmFtZSB3aGVyZSB0aGUgbWF0Y2ggd2FzIHBsYXllZA0KICAtIDxtYXJrPioqSG9tZSBUZWFtIE5hbWUqKjogSG9tZSB0ZWFtIGNvdW50cnkgbmFtZTwvbWFyaz4NCiAgLSA8bWFyaz4qKkhvbWUgVGVhbSBHb2FscyoqOiBUb3RhbCBnb2FscyBzY29yZWQgYnkgdGhlIGhvbWUgdGVhbSBieSB0aGUgZW5kIG9mIHRoZSBtYXRjaDwvbWFyaz4NCiAgLSA8bWFyaz4qKkF3YXkgVGVhbSBHb2FscyoqOiBUb3RhbCBnb2FscyBzY29yZWQgYnkgdGhlIGF3YXkgdGVhbSBieSB0aGUgZW5kIG9mIHRoZSBtYXRjaDwvbWFyaz4NCiAgLSA8bWFyaz4qKkF3YXkgVGVhbSBOYW1lKio6IEF3YXkgdGVhbSBjb3VudHJ5IG5hbWU8L21hcms+DQogIC0gKipXaW4gY29uZGl0aW9ucyoqOiBTcGVjaWFsIHdpbiBjb25kaXRpb24gKGlmIGFueSkNCiAgLSA8bWFyaz4qKkF0dGVuZGFuY2UqKjogVG90YWwgY3Jvd2QgcHJlc2VudCBhdCB0aGUgc3RhZGl1bQ0KICAtIDxtYXJrPioqSGFsZi10aW1lIEhvbWUgR29hbHMqKjogR29hbHMgc2NvcmVkIGJ5IHRoZSBob21lIHRlYW0gdW50aWwgaGFsZiB0aW1lPC9tYXJrPg0KICAtIDxtYXJrPioqSGFsZi10aW1lIEF3YXkgR29hbHMqKjogR29hbHMgc2NvcmVkIGJ5IHRoZSBhd2F5IHRlYW0gdW50aWwgaGFsZiB0aW1lPC9tYXJrPg0KICAtICoqUmVmZXJlZSoqOiBOYW1lIG9mIHRoZSBmaXJzdCByZWZlcmVlDQogIC0gKipBc3Npc3RhbnQgMSoqOiBOYW1lIG9mIHRoZSBmaXJzdCBhc3Npc3RhbnQgcmVmZXJlZSAobGluZXNtYW4pDQogIC0gKipBc3Npc3RhbnQgMioqOiBOYW1lIG9mIHRoZSBzZWNvbmQgYXNzaXN0YW50IHJlZmVyZWUgKGxpbmVzbWFuKQ0KICAtIDxtYXJrPioqUm91bmRJRCoqOiBVbmlxdWUgSUQgb2YgdGhlIHJvdW5kPC9tYXJrPg0KICAtIDxtYXJrPioqTWF0Y2hJRCoqOiBVbmlxdWUgSUQgb2YgdGhlIG1hdGNoPC9tYXJrPg0KICAtICoqSG9tZSBUZWFtIEluaXRpYWxzKio6IEhvbWUgdGVhbSBjb3VudHJ5J3MgdGhyZWUgbGV0dGVyIGluaXRpYWxzDQogIC0gKipBd2F5IFRlYW0gSW5pdGlhbHMqKjogQXdheSB0ZWFtIGNvdW50cnkncyB0aHJlZSBsZXR0ZXIgaW5pdGlhbHMNCiAgDQoNCiogVGhlIGRhdGFzZXQgYFdvcmxkQ3VwUGxheWVyc2AgY29udGFpbnMgKio5KiogdmFyaWFibGVzIHdpdGggKiozNzc4NCoqIHJvd3Mgb2YgcmVjb3Jkcy5UaGUgPG1hcmsgPmhpZ2hsaWdodGVkPC9tYXJrPiB2YXJpYWJsZXMgaGF2ZSBiZWVuIGNvbnNpZGVyZWQgaW1wb3J0YW50IGFuZCB0aGVzZSB2YXJpYWJsZXMgYXJlIHRoZW4gc3Vic2V0dGVkIGZvciBhIG1vcmUgYXB0IGFuYWx5c2lzIHdpdGggcmVzcGVjdCB0byBvdXIgb2JqZWN0aXZlLiBUaGUgcmVzdCBvZiB0aGUgdnJhaWFibGVzIGFyZSBub3QgbmVjZXNzYXJ5IGZvciBvdXIgYW5hbHlzaXMgYXQgdGhpcyBwb2ludC4gVGhlIHJlc3VsdGluZyBzdWJzZXQgYFdvcmxkQ3VwUGxheWVyc18xYCBub3cgaGFzIDQgdmFyaWFibGVzLg0KDQogIC0gKipSb3VuZElEKio6IFVuaXF1ZSBJRCBvZiB0aGUgcm91bmQNCiAgLSA8bWFyaz4qKk1hdGNoSUQqKjogVW5pcXVlIElEIG9mIHRoZSBtYXRjaDwvbWFyaz4NCiAgLSAqKlRlYW0gSW5pdGlhbHMqKjogUGxheWVyJ3MgdGVhbSBpbml0aWFscw0KICAtIDxtYXJrPioqQ29hY2ggTmFtZSoqOiBOYW1lIGFuZCBjb3VudHJ5IG9mIHRoZSB0ZWFtIGNvYWNoPC9tYXJrPg0KICAtIDxtYXJrPioqTGluZS11cCoqOiBTPUxpbmUtdXAsIE49U3Vic3RpdHV0ZTwvbWFyaz4NCiAgLSAqKlNoaXJ0IE51bWJlcioqOiBTaGlydCBudW1iZXIgaWYgYXZhaWxhYmxlDQogIC0gPG1hcms+KipQbGF5ZXIgTmFtZSoqOiBOYW1lIG9mIHRoZSBwbGF5ZXI8L21hcms+DQogIC0gKipQb3NpdGlvbioqOiBDPSBDYXB0YWluLCBHSz0gR29hbGtlZXBlcg0KICAtICoqRXZlbnQqKjogRz1Hb2FsLCBPRz1Pd24gR29hbCwgWT1ZZWxsb3cgQ2FyZCwgUj1SZWQgQ2FyZCwgU1kgPSBSZWQgQ2FyZCBieSBzZWNvbmQgeWVsbG93LCBQPVBlbmFsdHksIE1QPU1pc3NlZCBQZW5hbHR5LCBJID0gU3Vic3RpdHV0aW9uIEluLCBPPVN1YnN0aXR1dGUgT3V0DQoNCiMjIyMgU3RlcHMgcGVyZm9ybWVkOiANCg0KKipTdGVwIDEqKjogVXNpbmcgdGhlIHJlYWRyIGZ1bmN0aW9uIGByZWFkX2NzdigpYCBib3RoIHRoZSAuY3N2IGZpbGVzOiBgV29ybGRjdXBNYXRjaGVzYCAmIGBXb3JsZGN1cHBsYXllcnNgIGFyZSByZWFkL2ltcG9ydGVkIGFuZCBhcHByb3ByaWF0ZSB2YXJpYWJsZXMgYXJlIGFzc2lnbmVkIGZvciBmdXJ0aGVyIHVzZS4NCg0KKipTdGVwIDIqKjogRGF0YXNldHMgYXJlIGNoZWNrZWQgZm9yIGFueSBkdXBsaWNhdGUgcm93cyBhbmQgb25seSBkaXN0aW5jdCByb3dzIGFyZSBrZXB0LiBBZnRlciByZW1vdmFsIG9mIGR1cGxpY2F0ZXMsIHRoZSBkaW1lbnNpb25zIG9mIGJvdGggdGhlIGRhdGFzZXRzIGFyZSBjaGVja2VkLg0KICANCioqU3RlcCAzKio6IFN1YnNldHMgb2YgYm90aCB0aGUgZGF0YXNldHMgYXJlIGNyZWF0ZWQgdXNpbmcgdGhlIGBzdWJzZXQoKWAgZnVuY3Rpb24gdG8gaW5jbHVkZSB0aGUgYWJvdmUgaGlnaGxpZ2h0ZWQgdmFyaWFibGVzLCBpbiB0aGUgZGF0YWZyYW1lcyBgV29ybGRDdXBNYXRjaGVzXzFgIGFuZCBgV29ybGRDdXBQbGF5ZXJzXzFgIHJlc3BlY3RpdmVseS4gVGhlIGBoZWFkKClgIGZ1bmN0aW9uIGRpc3BsYXlzIHRoZSBmaXJzdCA2IHJvd3Mgb2YgdGhlIHN1YnNldHMuDQogIA0KKipTdGVwIDQqKjogVGhlIGBpbm5lcl9qb2luKClgIGZ1bmN0aW9uIGlzIHVzZWQgdG8gbWVyZ2UgYm90aCB0aGUgZGF0YXNldHMgdXNpbmcgdGhlIGNvbW1vbiBhdHRyaWJ1dGUgYXMgYE1hdGNoSURgLiBUaGUgSW5uZXIgSm9pbiBzZWxlY3RzIGFsbCByb3dzIGZyb20gYm90aCBwYXJ0aWNpcGF0aW5nIHRhYmxlcyBhcyBsb25nIGFzIHRoZXJlIGlzIGEgbWF0Y2ggYmV0d2VlbiB0aGUgY29sdW1ucy4gT3VyIGZpbmFsIGRhdGFzZXQgaXMgbm93IHJlYWR5IGZvciBmdXJ0aGVyIGFuYWx5c2lzIHdpdGggKioxNCoqIGNvbHVtbnMgYW5kICoqMzcwNDgqKiByb3dzLg0KDQpgYGB7cn0NCg0KI3NldCB3b3JraW5nIGRpcmVjdG9yeQ0Kc2V0d2QoIn4vcHJlcHJvY2Vzc2luZ18zIikNCiAgICAgIA0KI1JlYWRpbmcvSW1wb3J0aW5nIHRoZSB0d28gZGF0YXNldHMgZnJvbSAuY3N2IGZpbGVzDQpXb3JsZEN1cE1hdGNoZXMgPC0gcmVhZF9jc3YoZmlsZSA9ICJXb3JsZEN1cE1hdGNoZXMuY3N2IikNCg0KV29ybGRDdXBQbGF5ZXJzIDwtIHJlYWRfY3N2KGZpbGUgPSAiV29ybGRDdXBQbGF5ZXJzLmNzdiIpDQoNCiNjaGVjayBmb3IgZHVwbGljYXRlcyBhbmQgc3Vic2V0IG9ubHkgZGlzdGluY3Qgcm93cw0KV29ybGRDdXBNYXRjaGVzX2Rpc3RpbmN0IDwtIFdvcmxkQ3VwTWF0Y2hlcyAlPiUgIGRpc3RpbmN0KCkNCldvcmxkQ3VwTWF0Y2hlc19kaXN0aW5jdA0KDQpXb3JsZEN1cFBsYXllcnNfZGlzdGluY3QgPC0gV29ybGRDdXBQbGF5ZXJzICU+JSBkaXN0aW5jdCgpDQpXb3JsZEN1cFBsYXllcnNfZGlzdGluY3QNCg0KI2NoZWNrIGRpbWVuc2lvbnMNCmRpbShXb3JsZEN1cE1hdGNoZXNfZGlzdGluY3QpDQpkaW0oV29ybGRDdXBQbGF5ZXJzX2Rpc3RpbmN0KQ0KDQojU3Vic2V0dGluZyB0aGUgV29ybGRjdXAgTWF0Y2hlcyBkYXRhc2V0IA0KV29ybGRDdXBNYXRjaGVzXzEgPC0gc3Vic2V0KCBXb3JsZEN1cE1hdGNoZXNfZGlzdGluY3RbLGMoMiw1OjksMTEsMTIsMTMsMTcsMTgpXSApDQojVmlld2luZyB0aGUgc3Vic2V0DQpoZWFkKFdvcmxkQ3VwTWF0Y2hlc18xKQ0KDQojU3Vic2V0dGluZyB0aGUgV29ybGRjdXAgUGxheWVycyBkYXRhc2V0DQpXb3JsZEN1cFBsYXllcnNfMSA8LSBzdWJzZXQoIFdvcmxkQ3VwUGxheWVyc19kaXN0aW5jdFssYygyLDQsNSw3KV0gKQ0KDQojVmlld2luZyB0aGUgc3Vic2V0DQpoZWFkKFdvcmxkQ3VwUGxheWVyc18xKQ0KDQojTGVmdCBqb2luaW5nIHRoZSBkYXRhc2V0cyBvZiBNYXRjaCBhbmQgcGxheWVycyBkZXRhaWxzIGJ5IHRoZSBjb21tb24gYXR0cmlidXRlICJNYXRjaElEIg0KV29ybGRjdXAgPC0gV29ybGRDdXBNYXRjaGVzXzEgJT4lIGlubmVyX2pvaW4oV29ybGRDdXBQbGF5ZXJzXzEsIGJ5ID0gIk1hdGNoSUQiKQ0KDQojVmlld2luZyB0aGUgZmluYWwgZGF0YXNldA0KZGltKFdvcmxkY3VwKQ0KaGVhZChXb3JsZGN1cCkNCmBgYA0KKioqDQojIyBVbmRlcnN0YW5kIERhdGENCg0KSW4gdGhpcyBzZWN0aW9uLCB3ZSB3aWxsIGJlIGludmVzdGlnYXRpbmcgdGhlIHN0cnVjdHVyZSBvZiB0aGUgZGF0YXNldC4gRGF0YXR5cGUgb2YgZWFjaCB2YXJpYWJsZSBpcyBvYnNlcnZlZC4gDQoNCiogV2hpbGUgaW52ZXN0aWdhdGluZyB0aGUgZGF0YXR5cGVzLCBpdCB3YXMgZm91bmQgdGhhdCB0aGUgdmFyaWFibGUgYERhdGV0aW1lYCBpcyBhIENoYXJhY3RlciB2YXJpYWJsZS4gTW9yZW92ZXIgdGhlIERhdGUgYW5kIHRpbWUgaW5mb3JtYXRpb24gaXMgc3RvcmVkIGluIGEgc2luZ2xlIHZhcmlhYmxlLg0KDQoqIEFsc28sIHRoZSB2YXJpYWJsZSBgTGluZS11cGAgaXMgYSBjaGFyYWN0ZXIgd2l0aCBvbmx5IHR3byBjYXRlZ29yaWVzICdTPUxpbmUtdXAnIGFuZCAnTj1TdWJzdGl0dXRlJy4NCg0KIyMjIyBTdGVwcyBwZXJmb3JtZWQ6DQoNCioqU3RlcCAxKio6IFRoZSBzdHJ1Y3R1cmUgb2YgdGhlIGRhdGFzZXQgYFdvcmxkY3VwYCBpcyBkaXNwbGF5ZWQgdXNpbmcgdGhlIGBzdHIoKWAgZnVuY3Rpb24uDQoNCioqU3RlcCAyKio6IFRvIGNvbmZpcm0gdGhlIHN0cnVjdHVyZSBvZiB0aGUgdmFyaWFibGUgYERhdGV0aW1lYCB0aGUgYHR5cGVvZigpYCBmdW5jdGlvbiBpcyB1c2VkLiBXZSBmaXJzdCB1c2UgdGhlIHRpZHlyIGZ1bmN0aW9uIGBTZXBhcmF0ZSgpYCB0byBzcGxpdCB0aGUgJ0RhdGUnIGFuZCAnVGltZScgYXMgMiBkaWZmZXJlbnQgY29sdW1ucy4gV2UgdGhlbiBmb3JtYXQgdGhlIHNlcGFyYXRlZCBjb2x1bW5zLiBUaGUgYGFzLmRhdGUoKWAgZnVuY3Rpb24gaXMgdXNlZCB0byBmb3JtYXQgdGhlIGRhdGUgaW50byBhICdEYXRlTW9udGhZZWFyJyBmb3JtIGFuZCB0aGUgYHBhcnNlX2htKClgIGZ1bmN0aW9uIGlzIHVzZWQgdG8gZm9ybWF0IHRoZSB0aW1lIGludG8gJ0hIOk1NOlNTJyBmb3JtLiBUaGUgc3RydWN0dXJlIG9mIGJvdGggYXJlIHRoZW4gZGlzcGxheWVkIHRvIHZlcmlmeSB0aGUgZm9ybWF0aW5nLg0KDQoqKlN0ZXAgMyoqOiBUaGUgZGF0YXR5cGUgb2YgdGhlIHZhcmlhYmxlIGBMaW5lLXVwYCBpcyBjaGVja2VkIHVzaW5nIHRoZSBmdW5jdGlvbiBgdHlwZW9mKClgLg0KDQoqKlN0ZXAgNCoqOiBUbyBjb252ZXJ0IHRoZSBjaGFyYWN0ZXIgZGF0YXR5cGUgdG8gYSBmYWN0b3IgZGF0YSB0eXBlLCB3ZSB1c2UgdGhlIGZ1bmN0aW9uIGBmYWN0b3IoKWAgd2l0aCB0d28gbGV2ZWxzICdOJyBhbmQgJ1MnIGZvciAnU3Vic3RpdHV0ZScgYW5kZCAnTGluZS11cCcuIFRoZSBmdW5jdGlvbiBgaXMuZmFjdG9yKClgIHdpbGwgY29uZmlybSB0aGlzIGNvbnZlcnNpb24uDQoNCioqU3RlcCA1Kio6IFRoZSBmaXJzdCA2IHJvd3Mgb2YgdGhlIGZvcm1hdHRlZCBkYXRhc2V0IGlzIGRpc3BsYXllZCB1c2luZyBgaGVhZCgpYCBmdW5jdGlvbi4NCg0KYGBge3J9DQojQ2hlY2tpbmcgdGhlIHN0cnVjdHVyZSBvZiB0aGUgZGF0YXNldA0Kc3RyKFdvcmxkY3VwKQ0KDQojQ2hlY2tpbmcgdGhlIGRhdGEgdHlwZSBvZiBDb2x1bW4gYERhdGV0aW1lYA0KdHlwZW9mKFdvcmxkY3VwJERhdGV0aW1lKQ0KDQojU3BsaXQgY29sdW1uICdEYXRldGltZScgaW50byBgRGF0ZWAgYW5kIGBUaW1lYCBjb2x1bW5zIA0KV29ybGRjdXAgPC0gV29ybGRjdXAgJT4lIHRpZHlyOjpzZXBhcmF0ZShEYXRldGltZSxpbnRvID0gYygiRGF0ZSIsICJUaW1lIiksc2VwID0iIC0gIikNCg0KI0Zvcm1hdGluZyBjb2x1bW4gYERhdGVgIGludG8gRGF5L01vbnRoL1llYXIgZm9ybWF0IG9mIGRhdGUNCldvcmxkY3VwJERhdGUgPC0gYXMuRGF0ZShXb3JsZGN1cCREYXRlLCBmb3JtYXQgPSAiJWQgJWIgJVkiKQ0KI0NvbmZpcm1pbmcgdGhlIHN0cnVjdHVyZSBvZiB0aGUgJ0RhdGUnIENvbHVtbg0Kc3RyKFdvcmxkY3VwJERhdGUpDQoNCiNGb3JtYXRpbmcgY29sdW1uIGBUaW1lYCBpbnRvIEhvdXIvTW9udGgvU2VjIGZvcm1hdCBvZiB0aW1lDQpXb3JsZGN1cCRUaW1lIDwtIGhtczo6cGFyc2VfaG0oV29ybGRjdXAkVGltZSkNCg0KI0NvbmZpcm1pbmcgdGhlIHN0cnVjdHVyZSBvZiBgVGltZWAgQ29sdW1uDQpzdHIoV29ybGRjdXAkVGltZSkNCg0KI0NoZWNraW5nIHRoZSBkYXRhIHR5cGUgb2YgQ29sdW1uIGBMaW5lLXVwYCANCnR5cGVvZihXb3JsZGN1cCRgTGluZS11cGApDQoNCiNDb252ZXJ0aW5nIHZhcmlhYmxlcyBmcm9tIGNoYXJhY3RlciB0byBmYWN0b3IgZGF0YSB0eXBlDQpXb3JsZGN1cCRgTGluZS11cGA8LWZhY3RvcihXb3JsZGN1cCRgTGluZS11cGAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHM9YygiTiIsIlMiKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHM9YygiU3Vic3RpdHV0ZSIsIkxpbmUtdXAiKSkNCg0KI1RvIGNvbmZpcm0gdGhlIGNvbnZlcnNpb24NCmlzLmZhY3RvcihXb3JsZGN1cCRgTGluZS11cGApDQoNCiNEaXNwbGF5aW5nIHRoZSBmb3JtYXRlZCBkYXRhc2V0IA0KaGVhZChXb3JsZGN1cCkNCmBgYA0KDQoqKioNCiMjCVRpZHkgJiBNYW5pcHVsYXRlIERhdGEgLSBJIA0KDQpJbiB0aGlzIHNlY3Rpb24sIHRoZSBkYXRhc2V0IGlzIGNoZWNrZWQgYWdhaW5zdCBmb2xsb3dpbmcgY2hhcmFjdGVyaXN0aWNzIGVzdGFibGlzaGVkIGJ5IEhhZGxleSBXaWNraGFtIGFuZCBHcm9sZW11bmQ6DQoNCjEuIEVhY2ggdmFyaWFibGUgbXVzdCBoYXZlIGl0cyBvd24gY29sdW1uLiANCjIuIEVhY2ggdW5pcXVlIG9ic2VydmF0aW9uIGhhcyBpdHMgb3duIHJvdy4gDQozLiBBbGwgdmFsdWVzIGhhdmUgaXRzIG93biBjZWxsLiANCg0KUG9zdCBleGFtaW5hdGlvbiwgaXQgY2FuIGJlIG9ic2VydmVkIHRoYXQgdGhlIGRhdGFzZXQgaXMgcmVhZGlseSBhdmFpbGFibGUgaW4gYSB0aWR5IGZvcm1hdC4gVGh1cyBpdCBjYW4gYmUgaW5mZXJyZWQgdGhhdCB0aGUgZGF0YXNldCBoYXMgYWxsIHRoZSBhYm92ZSBtZW50aW9uZWQgY2hhcmFjdGVyaXN0aWNzIGFuZCB0aGVyZWZvcmUsIGl0IG5lZWRzIG5vIGZ1cnRoZXIgdGlkeWluZy4NCg0KYGBge3J9DQoNCiNEaXNwbGF5aW5nIHRoZSBUaWR5IGRhdGFzZXQgDQpoZWFkKFdvcmxkY3VwKQ0KYGBgDQoqKioNCiMjCVRpZHkgJiBNYW5pcHVsYXRlIERhdGEgLSBJSSANCg0KSW4gdGhpcyBzZWN0aW9uLCB3ZSB1c2UgdGhlIGRwbHlyIGZ1bmN0aW9uIGBtdXRhdGUoKWAgdG8gY3JlYXRlIDMgbmV3IHZhcmlhYmxlcyB0aGF0IHdpbGwgaGVscCB1cyBhbmFseXNlIHRoZSBkYXRhc2V0IGZ1cnRoZXIuIFRoZSBuZXcgdmFyaWFibGVzIGFyZSBgSG9tZV9TZWNfaGFsZmAsIGBBd2F5X1NlY19oYWxmYCBhbmQgYFRvdGFsX0dvYWxzYC4gVXNpbmcgdGhlIFNlY29uZCBoYWxmIGdvYWxzIHdlIGNhbiBmaW5kIG91dCBpZiB0aGUgZ29hbHMgYXJlIHNjb3JlZCBpbiB0aGUgbGFzdCBoYWxmIG9mIHRoZSB0aW1lIHJlbWFpbmluZyBpbiB0aGUgZ2FtZS4gSXQgaXMgY29tbW9ubHkgbm90aWNlZCB0aGF0IG1vc3Qgb2YgdGhlIGdvYWxzIGFyZSBpbmRlZWQgc2NvcmVkIHdpdGhpbiB0aGUgbGFzdCBmZXcgbWludXRlcy4gVGhlIHRvdGFsIG51bWJlciBvZiBnb2FscyB3aWxsIGhlbHAgdXMgbG9vayBhdCB0aGVpciB0b3RhbCBzY29yZSBhZnRlciBlYWNoIG1hdGNoLg0KDQojIyMjIFN0ZXBzIHBlcmZvcm1lZDogDQoqKlN0ZXAgMSoqOiBVc2luZyB0aGUgYG11dGF0ZSgpYCBmdW5jdGlvbiB3ZSBhcmUgY3JlYXRpbmcgdGhlIG5ldyB2YXJpYWJsZXMgYEhvbWVfU2VjX2hhbGZgLCBgQXdheV9TZWNfaGFsZmAgYnkgc3VidHJhY3RpbmcgdG90YWwgdGVhbSBnb2FscyB3aXRoIHRoZSBoYWxmIHRpbWUgZ29hbHMgYW5kIGBUb3RhbF9Hb2Fsc2AgYnkgYWRkaW5nIGJvdGggdGhlIEhvbWUgYW5kIEF3YXkgdGVhbSBnb2FscyB0b2dldGhlci4NCg0KKipTdGVwIDIqKjogYGhlYWQoKWAgZnVuY3Rpb24gaXMgdXNlZCB0byBkaXNwbGF5IHRoZSBkYXRhc2V0IGFmdGVyIGNyZWF0aW9uIG9mIHRoZSBuZXcgdmFyaWFibGVzLg0KDQpgYGB7cn0NCiNDcmVhdGluZyBhIG5ldyB2YXJpYWJsZSB3aXRoIHNlY29uZCBoYWxmIHRpbWUgc2NvcmUgYnkgdXNpbmcgdGhlIG11dGF0ZSBmdW5jdGlvbg0KV29ybGRjdXAgPC0gbXV0YXRlKFdvcmxkY3VwLA0KICAgICAgICAgICAgICAgICAgIEhvbWVfU2VjX0hhbGYgPSBXb3JsZGN1cCRgSG9tZSBUZWFtIEdvYWxzYCAtIFdvcmxkY3VwJGBIYWxmLXRpbWUgSG9tZSBHb2Fsc2AsDQogICAgICAgICAgICAgICAgICAgQXdheV9TZWNfSGFsZj0gV29ybGRjdXAkYEF3YXkgVGVhbSBHb2Fsc2AtIFdvcmxkY3VwJGBIYWxmLXRpbWUgQXdheSBHb2Fsc2AsDQogICAgICAgICAgICAgICAgICAgVG90YWxfR29hbHMgPSBXb3JsZGN1cCRgSG9tZSBUZWFtIEdvYWxzYCArIFdvcmxkY3VwJGBBd2F5IFRlYW0gR29hbHNgKQ0KDQojcmVtb3ZlIHNwYWNlcyBmcm9tIGNvbHVtbiBuYW1lcw0KbmFtZXMoV29ybGRjdXApIDwtIG1ha2UubmFtZXMobmFtZXMoV29ybGRjdXApLCB1bmlxdWU9VFJVRSkNCg0KI0Rpc3BsYXlpbmcgdGhlIGRhdGFzZXQgDQpoZWFkKFdvcmxkY3VwKQ0KDQoNCmBgYA0KKioqDQojIwlTY2FuIC0gSSANCg0KKiBUbyBwcmUtcHJvY2VzcyB0aGUgZGF0YSBlZmZlY3RpdmVseSwgZGF0YXNldCBpcyBjaGVja2VkIGZvciBtaXNzaW5nIHZhbHVlcyxpbmNvbnNpc3RlbmNpZXMgYW5kIG9idmlvdXMgZXJyb3JzLiBUaHJvdWdoIHRoZSBiYXNlUiBmdW5jdGlvbnMgYGNvbFN1bXMoKWAgYW5kIGBpcy5uYSgpYCwgd2UgZmluZCB0aGF0IHRoZXJlIGFyZSBvbmx5ICoqMTg0KiogbWlzc2luZyB2YWx1ZXMgaW4gYEF0dGVuZGFuY2VgIHZhcmlhYmxlLiANCg0KKiBTaW5jZSB0aGlzIGlzIG9ubHkgYXBwcm94aW1hdGVseSAqKjAuNiUqKiBvZiB0aGUgc2l6ZSBvZiB0aGUgdmFyaWFibGUgZGF0YSBzZXQsIHdlIGNhbiBleGNsdWRlIHRoZXNlIHZhbHVlcyB3aGlsZSBwZXJmb3JtaW5nIG1hdGhlbWF0aWNhbCBvcGVyYXRpb25zIChlLmcuLCBjYWxjdWxhdGluZyBtZWFuICYgc3RhbmRhcmQgZGV2aWF0aW9uIGZvciBwbG90dGluZyBub3JtYWwgY3VydmUpIGluIG9yZGVyIHRvIG5vdCBiaWFzIHRoZSBhbmFseXNpcy4gVGhpcyBjYW4gYmUgZG9uZSBieSB1c2luZyB0aGUgYG5hLnJtPVRSVUVgIGFyZ3VtZW50IGluc2lkZSB0aGUgbWF0aGVtYXRpY2FsIGZ1bmN0aW9ucy4NCg0KKiBUbyBjaGVjayBmb3Igc3BlY2lhbCB2YWx1ZXMgOiBgLUluZmAsIGBJbmZgIGFuZCBgTmFOYCwgd2UgZGVmaW5lIGEgZnVuY3Rpb24gaW5zaWRlIGBzYXBwbHkoKWAgYW5kIGNhbGN1bGF0ZSB0aGUgdG90YWwgbnVtYmVyIG9mIHNwZWNpYWwgdmFsdWVzIGZvciBlYWNoIGNvbHVtbi4gV2UgZmluZCB0aGF0IHRoZXJlIGFyZSBubyBzdWNoIHZhbHVlcyBpbiB0aGUgZGF0YXNldC4NCg0KKiBUbyBjaGVjayBmb3Igb2J2aW91cyBpbmNvbnNpc3RlbmNpZXMvZXJyb3JzLCB3ZSBkZWZpbmUgYSBzZXQgb2YgcnVsZXMgdXNpbmcgYGVkaXRzZXQoKWAgZnVuY2lvbiBmcm9tIGBlZGl0cnVsZXNgIHBhY2thZ2UuIFRoZSBkYXRhc2V0IGBXb3JsZGN1cGAgaXMgY2hlY2tlZCBhZ2FpbnN0IHRoZXNlIHJ1bGVzIHVzaW5nIHRoZSBgdmlvbGF0ZWRFZGl0cygpYCBmdW5jdGlvbiBhbmQgaXRzIHN1bW1hcnkgaXMgY2hlY2tlZC4gV2UgZmluZCB0aGF0IHRoZXJlIGFyZSBubyBvYnZpb3VzIGVycm9ycyBpbiB0aGUgZGF0YXNldC4NCg0KYGBge3J9DQojY2hlY2sgZm9yIG1pc3NpbmcgdmFsdWVzDQpjb2xTdW1zKGlzLm5hKFdvcmxkY3VwKSkNCg0KcGVyY2VudF9uYSA8LSAoc3VtKGlzLm5hKFdvcmxkY3VwJEF0dGVuZGFuY2UpKSAvIGxlbmd0aChXb3JsZGN1cCRBdHRlbmRhbmNlKSkqMTAwDQpwZXJjZW50X25hDQoNCiNjaGVjayBmb3Igc3BlY2lhbCB2YWx1ZXMgOiBpbmZpbml0ZSAoLWluZiBvciAraW5mKSBhbmQgTmFODQpzYXBwbHkoV29ybGRjdXAsIGZ1bmN0aW9uKHgpIHN1bShpZiAoaXMubnVtZXJpYyh4KSkgKGlzLmluZmluaXRlKHgpIHwgaXMubmFuKHgpICkpKQ0KDQojY2hlY2sgZm9yIG9idmlvdXMgZXJyb3JzDQpSdWxlcyA8LSBlZGl0c2V0KGMoIkhvbWUuVGVhbS5Hb2FscyA+PTAiLA0KICAgICAgICAgICAgICAgICAgICJBd2F5LlRlYW0uR29hbHMgPj0gMCIsIA0KICAgICAgICAgICAgICAgICAgICJBdHRlbmRhbmNlID49MCIsDQogICAgICAgICAgICAgICAgICAgIkhhbGYudGltZS5Ib21lLkdvYWxzID49MCIsDQogICAgICAgICAgICAgICAgICAgIkhhbGYudGltZS5Bd2F5LkdvYWxzID49MCIsDQogICAgICAgICAgICAgICAgICAgIkhhbGYudGltZS5Ib21lLkdvYWxzIDw9IEhvbWUuVGVhbS5Hb2FscyIsDQogICAgICAgICAgICAgICAgICAgIkhhbGYudGltZS5Bd2F5LkdvYWxzIDw9IEF3YXkuVGVhbS5Hb2FscyIsDQogICAgICAgICAgICAgICAgICAgIkxpbmUudXAgJWluJSBjKCdMaW5lLXVwJywnU3Vic3RpdHV0ZScpIikpDQpSdWxlcw0KDQp2aW9sYXRlZCA8LSB2aW9sYXRlZEVkaXRzKFJ1bGVzLCBXb3JsZGN1cCkNCnN1bW1hcnkodmlvbGF0ZWQpDQpgYGANCioqKg0KIyMJU2NhbiAtIElJDQoNCkluIHRoaXMgc2VjdGlvbiB3ZSB3aWxsIGV4YW1pbmUgYWxsIHRoZSBudW1lcmljIHZhcmlhYmxlcyBpbiB0aGUgZGF0YXNldCBmb3Igb3V0bGllcnMgdXNpbmcgdGhlIFVuaXZhcmlhdGUgT3V0bGllciBEZXRlY3Rpb24gTWV0aG9kIG9mIGJveHBsb3QuIFdlIHdpbGwgdGhlbiBmaWx0ZXIgb3V0IHRoZSBvdXRsaWVycyBmcm9tIHRoZSBkYXRhc2V0IHRvIG9idGFpbiBhIG1vcmUgZm9jdXNzZWQgYW5kIGNsZWFuIGRhdGFzZXQuDQoNCiMjIyMgU3RlcHMgcGVyZm9ybWVkOiANCg0KKipTdGVwIDEqKjogVGhlIEJveHBsb3Qgb2YgdGhlIGZvbGxvd2luZyBudW1lcmljIHZhcmlhYmxlcyBoYXZlIGJlZW4gcGxvdHRlZCByZXNwZWN0aXZlbHkgd2l0aCB0aGVpciBZLWF4aXMgbGFiZWxzIGFuZCB0aGVuIHN0b3JlZCBpbnRvIGFwcHJvcHJpYXRlIHZhcmlhYmxlcyBmb3IgdGhlIGVhc2Ugb2YgZnVydGhlciBzdGVwcy4NCiAgLSBIb21lIFRlYW0gR29hbHMNCiAgLSBBd2F5IFRlYW0gR29hbHMNCiAgLSBIYWxmLVRpbWUgSG9tZSBHb2Fscw0KICAtIEhhbGYtVGltZSBBd2F5IEdvYWxzDQogIC0gU2Vjb25kIEhhbGYgSG9tZSBHb2Fscw0KICAtIFNlY29uZCBIYWxmIEF3YXkgR29hbHMNCg0KKipTdGVwIDIqKjogVGhlIGBvdXQoKWAgZnVuY3Rpb24gYWxvbmcgd2l0aCB0aGUgdmFyaWFibGUgbmFtZXMgd2UgYXNzaWduZWQgaW4gdGhlIGxhc3Qgc3RlcCBnaXZlcyB1cyB0aGUgb3V0bGllcnMgb2YgZWFjaCBudW1lcmljIHZhcmlhYmxlLiANCg0KKipTdGVwIDMqKjogVGhlIGJhc2VSIGZ1bmN0aW9uIGBtaW4oKWAgb2YgdGhlIG91dGxpZXJzIHdpbGwgZ2l2ZSB1cyB0aGUgbWluaW1hIG9mIHRoZSBvdXRsaWVycyBvZiBlYWNoIG9mIHRoZSBudW1lcmljIHZhcmlhYmxlIHdoaWNoIHdlIGNhbiB1c2UgaW4gdGhlIG5leHQgc3RlcCB0byBmaWx0ZXIgdGhlbSBvdXQuDQoNCioqU3RlcCA0Kio6IFdlIGFyZSBub3cgdXNpbmcgdGhlIGRwbHlyIGZ1bmN0aW9uIGBmaWx0ZXIoKWAgdG8gZmlsdGVyIG91dCB0aGUgbWVhbmluZ2Z1bCBhbmQgaW1wb3J0YW50IGRhdGEgZnJvbSB0aGUgb3V0bGllcnMuIFRoZSBhYm92ZSBtZW50aW9uZWQgNiB2YXJpYWJsZXMgYXJlIGZpbHRlcmVkIHVzaW5nIHRoZSBNaW5pbWEgdmFsdWVzIGZyb20gU3RlcCAzIGFuZCBhc3NpZ25lZCBiYWNrIHRvIHRoZSBtYWluIGRhdGFzZXQgYFdvcmxkY3VwYC4NCg0KKipTdGVwIDUqKjogQm94cGxvdHMgb2YgdGhlIGNsYXJpZmllZCB2YXJpYWJsZXMgYXJlIGRpc3BsYXllZCB3aXRob3V0IGFueSBvdXRsaWVycy4gDQoNCmBgYHtyfQ0KI0RyYXdpbmcgYSBib3hwbG90IGZvciB0aGUgbnVtZXJpYyB2YXJpYWJsZXMgdG8gY2hlY2sgZm9yIG91dGxpZXJzDQojT3V0bGllcnMgb2YgdGhlIG51bWVyaWMgdmFyaWFibGVzIGFyZSBhc3NpZ25lZCB0byBhIG5ldyB2YXJpYWJsZQ0KI0ZpbmRpbmcgdGhlIG1pbmltdW0gdmFsdWUgb2YgdGhlIG91dGxpZXJzIG9mIGVhY2ggdmFyaWFibGUNCg0KQlBfMSA8LSBib3hwbG90KFdvcmxkY3VwJEhvbWUuVGVhbS5Hb2FscyxtYWluPSJCb3hwbG90IG9mIEhvbWUgVGVhbSBHb2FscyIseWxhYiA9IkhvbWUgdGVhbSBzY29yZSIsY29sID0gInBhbGVncmVlbiIpDQptaW4oQlBfMSRvdXQpDQoNCkJQXzIgPC0gYm94cGxvdChXb3JsZGN1cCRBd2F5LlRlYW0uR29hbHMsbWFpbj0iQm94cGxvdCBvZiBBd2F5IFRlYW0gR29hbHMiLHlsYWIgPSJBd2F5IHRlYW0gc2NvcmUiLCBjb2wgPSAicGFsZWdyZWVuIikNCm1pbihCUF8yJG91dCkNCg0KQlBfMyA8LWJveHBsb3QoV29ybGRjdXAkSGFsZi50aW1lLkhvbWUuR29hbHMsbWFpbj0iQm94cGxvdCBvZiBIb21lIFRlYW0gSGFsZiB0aW1lIEdvYWxzIix5bGFiID0iSG9tZSB0ZWFtIHNjb3JlIixjb2wgPSAicGFsZWdyZWVuIikNCm1pbihCUF8zJG91dCkNCg0KQlBfNCA8LWJveHBsb3QoV29ybGRjdXAkSGFsZi50aW1lLkF3YXkuR29hbHMsbWFpbj0iQm94cGxvdCBvZiBBd2F5IFRlYW0gSGFsZiB0aW1lIEdvYWxzIix5bGFiID0iQXdheSB0ZWFtIHNjb3JlIixjb2wgPSAicGFsZWdyZWVuIikNCm1pbihCUF80JG91dCkNCg0KQlBfNSA8LWJveHBsb3QoV29ybGRjdXAkSG9tZV9TZWNfSGFsZixtYWluPSJCb3hwbG90IG9mIEhvbWUgVGVhbSBTZWNvbmQgaGFsZiBHb2FscyIseWxhYiA9IlNlY29uZCBoYWxmIHNjb3JlIixjb2wgPSAicGFsZWdyZWVuIikNCm1pbihCUF81JG91dCkNCg0KQlBfNiA8LWJveHBsb3QoV29ybGRjdXAkQXdheV9TZWNfSGFsZixtYWluPSJCb3hwbG90IG9mIEF3YXkgVGVhbSBTZWNvbmQgaGFsZiBHb2FscyIseWxhYiA9IlNlY29uZCBoYWxmIHNjb3JlIixjb2wgPSAicGFsZWdyZWVuIikNCm1pbihCUF82JG91dCkNCg0KI0ZpbHRlcmluZyB0aGUgdmFyaWFibGVzIHVzaW5nIHRoZSBtaW5pbXVtIHZhbHVlcyBvZiB0aGUgb3V0bGllcnMgDQpXb3JsZGN1cCAlPD4lIGZpbHRlcihIb21lLlRlYW0uR29hbHM8IG1pbihCUF8xJG91dCkgKQ0KV29ybGRjdXAgJTw+JSBmaWx0ZXIoQXdheS5UZWFtLkdvYWxzPCBtaW4oQlBfMiRvdXQpICkgDQpXb3JsZGN1cCAlPD4lIGZpbHRlcihIYWxmLnRpbWUuSG9tZS5Hb2FsczwgbWluKEJQXzMkb3V0KSApDQpXb3JsZGN1cCAlPD4lIGZpbHRlcihIYWxmLnRpbWUuQXdheS5Hb2FsczwgbWluKEJQXzQkb3V0KSApIA0KV29ybGRjdXAgJTw+JSBmaWx0ZXIoSG9tZV9TZWNfSGFsZjwgbWluKEJQXzUkb3V0KSApIA0KV29ybGRjdXAgJTw+JSBmaWx0ZXIoQXdheV9TZWNfSGFsZjwgbWluKEJQXzYkb3V0KSApIA0KDQojRGlzcGxheWluZyB0aGUgQm94cGxvdHMgb2YgdGhlIGZpbHRlcmVkIHZhcmlhYmxlcw0KYm94cGxvdChXb3JsZGN1cCRIb21lLlRlYW0uR29hbHMsbWFpbj0iQm94cGxvdCBvZiBIb21lIFRlYW0gR29hbHMiLHlsYWIgPSJIb21lIHRlYW0gc2NvcmUiLGNvbCA9ICJTa3libHVlIikNCmJveHBsb3QoV29ybGRjdXAkQXdheS5UZWFtLkdvYWxzLG1haW49IkJveHBsb3Qgb2YgQXdheSBUZWFtIEdvYWxzIix5bGFiID0iQXdheSB0ZWFtIHNjb3JlIiwgY29sID0gIlNreWJsdWUiKQ0KYm94cGxvdChXb3JsZGN1cCRIYWxmLnRpbWUuSG9tZS5Hb2FscyxtYWluPSJCb3hwbG90IG9mIEhvbWUgVGVhbSBIYWxmIHRpbWUgR29hbHMiLHlsYWIgPSJIb21lIHRlYW0gc2NvcmUiLGNvbCA9ICJTa3libHVlIikNCmJveHBsb3QoV29ybGRjdXAkSGFsZi50aW1lLkF3YXkuR29hbHMsbWFpbj0iQm94cGxvdCBvZiBBd2F5IFRlYW0gSGFsZiB0aW1lIEdvYWxzIix5bGFiID0iQXdheSB0ZWFtIHNjb3JlIixjb2wgPSAiU2t5Ymx1ZSIpDQpib3hwbG90KFdvcmxkY3VwJEhvbWVfU2VjX0hhbGYsbWFpbj0iQm94cGxvdCBvZiBIb21lIFRlYW0gU2Vjb25kLWhhbGYgR29hbHMiLHlsYWIgPSJTZWNvbmQtaGFsZiBzY29yZSIsY29sID0gIlNreWJsdWUiKQ0KYm94cGxvdChXb3JsZGN1cCRBd2F5X1NlY19IYWxmLG1haW49IkJveHBsb3Qgb2YgQXdheSBUZWFtIFNlY29uZC1oYWxmIEdvYWxzIix5bGFiID0iU2Vjb25kLWhhbGYgc2NvcmUiLGNvbCA9ICJTa3libHVlIikNCg0KYGBgDQoqKioNCiMjCVRyYW5zZm9ybSANCg0KRGF0YSB0cmFuc2Zvcm1hdGlvbiBpcyBhcHBsaWVkIG9uIHRoZSB2YXJpYWJsZXMgOiBgQXR0ZW5kYW5jZWAgYW5kIGBUb3RhbCBnb2Fsc2AuDQoNCiMjIyMgU3RlcHMgcGVyZm9ybWVkOiANCioqU3RlcCAxKio6IFdlIGNoZWNrIHRoZSBkaXN0cmlidXRpb24gb2YgYm90aCB0aGUgdmFyaWFibGVzIHVzaW5nIHRoZSBgaGlzdCgpYCBmdW5jdGlvbi4gV2UgY2FuIHNlZSBpbiBib3RoIHRoZSBncmFwaHMgdGhhdCB0aGUgZGF0YSBpcyBza2V3ZWQgdG8gdGhlIHJpZ2h0Lg0KDQoqKlN0ZXAgMioqOiBXZSBvdmVybGF5IGEgbm9ybWFsIGRpc3RyaWJ1dGlvbiBjdXJ2ZSBvdmVyIHRoZSBoaXN0b2dyYW1zIGluIG9yZGVyIHRvIHZpc3VhbGlzZSB0aGUgdHJlbmRzIGJldHRlci4gSXQgaXMgdG8gYmUgbm90ZWQgdGhhdCBzaW5jZSBgQXR0ZW5kYW5jZWAgdmFyaWFibGUgY29udGFpbnMgKioxODQqKiBgTkFgIHZhbHVlcyBbYXMgZGlzY3Vzc2VkIHByZXZpb3VzbHldLCB3ZSB1c2UgYG5hLnJtPVRSVUVgIGFyZ3VtZW50IHRvIGV4Y2x1ZGUgdGhlbS4NCg0KKipTdGVwIDMqKjogVG8gZGVjcmVhc2UgdGhlIHNrZXduZXNzIGFuZCBub3JtYWxpemUgdGhlIGRhdGEgd2UgYWFwbHk6DQoNCjEuIFNxdWFyZS1yb290IHRyYW5zZm9ybWF0aW9uIGZvciBgQXR0ZW5kYW5jZWAgDQoodG8gcmVkdWNlIHJpZ2h0LXNrZXduZXNzIGFuZCBhcHBseWluZyB0byB0aGUgemVybyB2YWx1ZXMgaW4gdGhlIGhpc3RvZ3JhbSkNCg0KMi4gQm94LUNveCB0cmFuc2Zvcm1hdGlvbiBmb3IgYFRvdGFsX0dvYWxzYCB3aXRoIGBsYW1iZGEgPSAiYXV0byJgDQoNCioqU3RlcCA0Kio6IENoZWNraW5nIHRoZSB0cmFuc2Zvcm1lZCBkYXRhLCB3ZSBvYnNlcnZlIHRoYXQgdGhlIHJpZ2h0LXNrZXduZXNzIGhhcyBzaWduaWZpY2FudGx5IHJlZHVjZWQgYW5kIGl0IGhhcyBjb252ZXJ0ZWQgdG8gYSBub3JtYWwgZGlzdHJpYnV0aW9uLg0KDQoNCmBgYHtyfQ0KI1Zpc3VhbGlzaW5nIEF0dGVuZGFuY2VzDQpBdHRlbmRhbmNlSGlzdCA8LSBoaXN0KFdvcmxkY3VwJEF0dGVuZGFuY2UsDQogICAgICAgICAgICAgICAgICAgICAgIGNvbD0iZmlyZWJyaWNrIiwNCiAgICAgICAgICAgICAgICAgICAgICAgbWFpbiA9Ikhpc3RvZ3JhbSBvZiBBdHRlbmRhbmNlcyIsDQogICAgICAgICAgICAgICAgICAgICAgIHhsYWIgPSJBdHRlbmRhbmNlcyIsDQogICAgICAgICAgICAgICAgICAgICAgIHByb2I9VFJVRSkgI29yIGZyZXE9RkFMU0UNCmN1cnZlKGRub3JtKHgsIG1lYW49bWVhbihXb3JsZGN1cCRBdHRlbmRhbmNlLCBuYS5ybT1UUlVFKSxzZD1zZChXb3JsZGN1cCRBdHRlbmRhbmNlLCBuYS5ybT1UUlVFKSApLA0KICAgICAgYWRkPVRSVUUsIGNvbD0iZ3JlZW4iLCBsd2Q9MiApDQpsZWdlbmQoInRvcHJpZ2h0IixsZWdlbmQ9YygiTm9ybWFsIEN1cnZlIiksZmlsbD1jKCJncmVlbiIpLA0KICAgICAgIHRleHQuZm9udCA9IDQsY2V4ID0gMC44LGJvcmRlcj0gRiwgYm94Lmx0eSA9IDMpDQoNCg0KI1Zpc3VhbGlzaW5nIFRvdGFsIE5vLiBvZiBHb2Fscw0KVG90YWxHb2FsSGlzdCA8LSBoaXN0KFdvcmxkY3VwJFRvdGFsX0dvYWxzLA0KICAgICAgICAgICAgICAgICAgICAgIGNvbD0ieWVsbG93Z3JlZW4iLA0KICAgICAgICAgICAgICAgICAgICAgIG1haW4gPSJIaXN0b2dyYW0gb2YgR29hbHMiLA0KICAgICAgICAgICAgICAgICAgICAgIHhsYWI9IlRvdGFsIE5vLiBvZiBHb2FscyIsDQogICAgICAgICAgICAgICAgICAgICAgcHJvYj1UUlVFICkNCg0KY3VydmUoZG5vcm0oeCwgbWVhbj1tZWFuKFdvcmxkY3VwJFRvdGFsX0dvYWxzKSxzZD1zZChXb3JsZGN1cCRUb3RhbF9Hb2FscykgKSwNCiAgICAgIGFkZD1UUlVFLCBjb2w9InJlZCIsIGx3ZD0yICkNCmxlZ2VuZCgidG9wcmlnaHQiLGxlZ2VuZD1jKCJOb3JtYWwgQ3VydmUiKSxmaWxsPWMoInJlZCIpLA0KICAgICAgIHRleHQuZm9udCA9IDQsY2V4ID0gMC44LGJvcmRlcj0gRiwgYm94Lmx0eSA9IDMpDQoNCiMgTm9ybWFsaXNpbmcgdXNpbmcgc3F1YXJlLXJvb3QgdHJhbnNmb3JtYXRpb24NCk5vcm0xIDwtIHNxcnQoV29ybGRjdXAkQXR0ZW5kYW5jZSkNCg0KIyBOb3JtYWxpc2luZyB1c2luZyBCb3gtQ294IHRyYW5zZm9ybWF0aW9uDQpOb3JtMiA8LSBCb3hDb3goV29ybGRjdXAkVG90YWxfR29hbHMsIGxhbWJkYSA9ICJhdXRvIikNCg0KI0Rpc3BsYXlpbmcgdGhlIG5vcm1hbGlzZWQgaGlzdG9ncmFtcyANCg0KaGlzdChOb3JtMSwgY29sPSJmaXJlYnJpY2siLCBtYWluPSJOb3JtYWxpc2VkIEhpc3RvZ3JhbSBvZiBBdHRlbmRhbmNlcyIsIA0KICAgICB4bGFiPSJBdHRlbmRhbmNlcyIsIHByb2I9VFJVRSkNCmN1cnZlKGRub3JtKHgsIG1lYW49bWVhbihOb3JtMSwgbmEucm09VCksc2Q9c2QoTm9ybTEsIG5hLnJtPVQpKSwNCiAgICAgIGFkZD1UUlVFLCBjb2w9ImdyZWVuIiwgbHdkPTIgKQ0KbGVnZW5kKCJ0b3ByaWdodCIsbGVnZW5kPWMoIk5vcm1hbCBDdXJ2ZSIpLGZpbGw9YygiZ3JlZW4iKSwNCiAgICAgICB0ZXh0LmZvbnQgPSA0LGNleCA9IDAuOCxib3JkZXI9IEYsIGJveC5sdHkgPSAzKQ0KDQoNCmhpc3QoTm9ybTIsIGNvbD0ieWVsbG93Z3JlZW4iLCBtYWluPSJOb3JtYWxpc2VkIEhpc3RvZ3JhbSBvZiBHb2FscyIsIHhsYWI9IlRvdGFsIE5vLiBvZiBHb2FscyIsDQogICAgIHByb2I9VFJVRSkNCmN1cnZlKGRub3JtKHgsIG1lYW49bWVhbihOb3JtMiksc2Q9c2QoTm9ybTIpICksDQogICAgICBhZGQ9VFJVRSwgY29sPSJyZWQiLCBsd2Q9MiApDQpsZWdlbmQoInRvcHJpZ2h0IixsZWdlbmQ9YygiTm9ybWFsIEN1cnZlIiksZmlsbD1jKCJyZWQiKSwNCiAgICAgICB0ZXh0LmZvbnQgPSA0LGNleCA9IDAuOCxib3JkZXI9IEYsIGJveC5sdHkgPSAzKQ0KYGBgDQoqKioNCg0KIyMgQ29uY2x1c2lvbg0KDQpXaXRoIHJlc3BlY3QgdG8gdGhlIHJlc3VsdHMgYWJvdmUgYW5kIHRoZSB0YXNrcyBwZXJmb3JtZWQgdGhyb3VnaG91dCB0aGUgcmVwb3J0LCB0aGUgZGF0YXNldCBvbiBGSUZBIFdvcmxkY3VwIE1hdGNoZXMgZnJvbSAxOTMwIHRvIDIwMTQgKGV4Y2x1ZGluZyAxOTQyICYgMTk0NikgaXMgbm93IHRpZHkgYW5kIHJlYWR5IGZvciBmdXJ0aGVyIG1hdGNoIGFuYWx5c2lzLiBXaXRoIHRoZXNlIG5vcm1hbGlzZWQgZGF0YSAsIHdlIGNhbiBmdXJ0aGVyIGV4cGxvcmUgcXVlc3Rpb25zIG9uIGhvdyBhIHRlYW0gcGVyZm9ybXMgb24gaXRzIGhvbWUgZ3JvdW5kICwgd2hldGhlciBhIHRlYW0gY2FuIHF1YWxpZnkgZm9yIHRoZSBuZXh0IHdvcmxkY3VwIG1hdGNoIGRlcGVuZGluZyBvbiBwcmV2aW91cyBwZXJmb21hbmNlcyBhbmQgZnV0dXJlIG1hdGNoIHN0cmF0ZWdpZXMgdXNpbmcgbWV0aG9kcyBsaWtlIGxpbmVhciByZWdyZXNzaW9uIGFuYWx5c2lzLCBhbm92YSwgY29ycmVsYXRpb24gYW5kIGh5cG90aGVzaXMgdGVzdGluZy4NCjxicj4NCjxicj4NCg==