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.


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

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 = col_double(),
  `Home Team Goals` = col_double(),
  `Away Team Goals` = col_double(),
  Attendance = col_double(),
  `Half-time Home Goals` = col_double(),
  `Half-time Away Goals` = col_double(),
  RoundID = col_double(),
  MatchID = col_double()
)
See spec(...) for full column specifications.
WorldCupPlayers <- read_csv(file = "WorldCupPlayers.csv")
Parsed with column specification:
cols(
  RoundID = col_double(),
  MatchID = col_double(),
  `Team Initials` = col_character(),
  `Coach Name` = col_character(),
  `Line-up` = col_character(),
  `Shirt Number` = col_double(),
  `Player Name` = col_character(),
  Position = col_character(),
  Event = col_character()
)
#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.

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:

  1. Each variable must have its own column.
  2. Each unique observation has its own row.
  3. 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.

Steps performed:

Step 1: Using the mutate() function we are creating the new variables Home_Sec_half, Away_Sec_half by subtracting total team goals with the half time goals and Total_Goals by adding both the Home and Away team goals together.

Step 2: head() function is used to display the dataset after creation of the new variables.

#Creating a new variable with second half time score by using the mutate function
Worldcup <- mutate(Worldcup,
                   Home_Sec_Half = Worldcup$`Home Team Goals` - Worldcup$`Half-time Home Goals`,
                   Away_Sec_Half= Worldcup$`Away Team Goals`- Worldcup$`Half-time Away Goals`,
                   Total_Goals = Worldcup$`Home Team Goals` + Worldcup$`Away Team Goals`)
#remove spaces from column names
names(Worldcup) <- make.names(names(Worldcup), unique=TRUE)
#Displaying the dataset 
head(Worldcup)

Scan - I

#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:

  1. Square-root transformation for Attendance (to reduce right-skewness and applying to the zero values in the histogram)

  2. 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==