Required packages

library(readr)
library(dplyr)
library(editrules)
library(MVN)

Executive Summary

The two tables in the Exploring 120 Years of Olympic History dataset are loaded in to R, stored as respective dataframe objects, and briefly explored by displaying the head() of each dataframe. The individual tables are combined with a join on a shared value, creating a single dataframe that contains all features in the Olympic History dataset. A sample of the joined dataframe is taken and data types of each variable are explored, using the class() function to explore typed and str() function to display the size of the dataset and a sample of each variable. These exploration processes highlight incorrectly assigned data types that need to be converted. After these transformations are applied, the structure of the data is explored using the Tidy Data Principles to determine if it is tidy. A new variable is created by multiplying existing ones with the mutate() function. Missing values, inconsistencies, & errors are explored with the use of the is.na() function (for the whole dataset with sum() and individual columns with colSums()). A user-defined function locates special values, and a rule set to identify obvious errors is applied to the data. Once located, the problematic values are handled for both numeric and categorical features; either by string replacements or imputations with statistics from similar observations. Any outliers in numerical features are explored and dealt with using capping for univariate outliers and mvn()s Mahalanobis distances for multivariate outliers. Methods of dealing with outliers are determined by calculating the percentage of outliers. After exploring their distributions, mathematical operations are used to transform numeric variables to make them more closely resemble normality. The resulting dataset is tidy, complete, and suitable to use for statistical analysis or machine learning applications.

Data

The 120 Years of Olympic History: Athletes and Results has been uploaded to Kaggle.com and falls under the Creative Commons License. The dataset consists of two tables; athlete_events.csv and noc_regions.csv. The athlete events dataset contains information about Olympic Athletes and events that they have competed in, including biological data (Age, Sex, Height, Weight etc.) and event data (Year, Season, City, Sport etc.). The NOC (National Organizing Committee) regions dataset possesses information about the countries that compete in the Olympics, including the country name and any notes about said country.

The Athletes dataset contains 15 variables:

The NOC Regions dataset contains 3 variables:

Load Data

Each dataset is individually loaded in to R using the read_csv() function from the readr library. read_csv() is used instead of base R’s read.csv() since it it faster at processing larger datasets. The athlete_events.csv dataset is assigned to an object named athletes. After which, the head() function displays the first 5 rows of the newly created data.frame to ensure that everything has been imported correctly.

athletes <- read_csv("Data/Olympics/athlete_events.csv", col_types = cols())
head(athletes, 5)

The same process used to load athlete_events.csv is applied to noc_regions.csv, with this dataset being assigned to an object called regions.

regions <- read_csv("Data/Olympics/noc_regions.csv", col_types = cols())
head(regions, 5)

Merge

The two datasets share a common variable: NOC. A left_join is used to join regions to athletes by NOC value. This preserves all of the athlete’s information within athletes and appends the corresponding region information from regions, applying an NA value where no matching information is available. These merged datasets are assigned to a new object called olympics. Once again calling the head() function shows the first 5 rows of the newly created data.frame to confirm that the two datasets have been merged correctly.

olympics <- left_join(athletes, regions, by = "NOC")
head(olympics, 5)

After the join is performed, due to the size of the dataset a random sample of 500 rows will be partitioned for analysis to limit processing time using the sample_n() function.

olympics_sample <- sample_n(olympics, 5000)

Understand

The function class() can be called to determine the data type stored within the variables in the olympics dataset. To prevent the need to call class() once for each individual variable, the sapply function administers it to each column automatically.

sapply(olympics_sample, class)
         ID        Name         Sex         Age      Height      Weight        Team         NOC       Games        Year      Season        City       Sport 
  "numeric" "character" "character"   "numeric"   "numeric"   "numeric" "character" "character" "character"   "numeric" "character" "character" "character" 
      Event       Medal      region       notes 
"character" "character" "character" "character" 

There are multiple data types within the olympics dataset, however the read_csv() function has assigned them all as either character or numeric. While this is correct in some cases (Name, Height, Weight, etc.) some have been mis-labelled. Specifically, Medals should be an ordered factor, Sex should be a factor and requires additional re-labelling of levels, and multiple other features require factor conversions.

Base R’s str() function displays the structure of the olympics dataset, including the attribute name, data type, and sample of values for each column. str() also returns the number of variables and observations in the dataset.

str(olympics_sample)
Classes ‘spec_tbl_df’, ‘tbl_df’, ‘tbl’ and 'data.frame':    5000 obs. of  17 variables:
 $ ID    : num  94906 67498 62645 80790 113748 ...
 $ Name  : chr  "Paulo Braune Pimenta" "Pierre Le Coq" "Primo Kopa" "Aubrey Maphosa Modiba" ...
 $ Sex   : chr  "M" "M" "M" "M" ...
 $ Age   : num  23 27 21 21 33 22 33 29 39 27 ...
 $ Height: num  169 186 NA 172 180 174 177 NA 183 178 ...
 $ Weight: num  65 72 NA 64 102 63 72 NA NA 68 ...
 $ Team  : chr  "Brazil" "France" "Slovenia" "South Africa" ...
 $ NOC   : chr  "BRA" "FRA" "SLO" "RSA" ...
 $ Games : chr  "1984 Summer" "2016 Summer" "1992 Winter" "2016 Summer" ...
 $ Year  : num  1984 2016 1992 2016 2014 ...
 $ Season: chr  "Summer" "Summer" "Winter" "Summer" ...
 $ City  : chr  "Los Angeles" "Rio de Janeiro" "Albertville" "Rio de Janeiro" ...
 $ Sport : chr  "Shooting" "Sailing" "Ski Jumping" "Football" ...
 $ Event : chr  "Shooting Men's Air Rifle, 10 metres" "Sailing Men's Windsurfer" "Ski Jumping Men's Large Hill, Team" "Football Men's Football" ...
 $ Medal : chr  NA "Bronze" NA NA ...
 $ region: chr  "Brazil" "France" "Slovenia" "South Africa" ...
 $ notes : chr  NA NA NA NA ...

The output of str() confirms that some variables have been mis-assigned data types. Additionally, the shape of the dataset is shown to be 5,000 observations of 17 variables. This further confirms that the two datasets have been correctly joined since athletes contained 15 variables, regions was made up of 3, and they each shared a common attribute to make up 17 total variables. Displaying a sample of the values within each variable provides some insight to the contents of the dataset. Most are to be expected, (Age is a 2-digit integer, Team is a Country name as a character string, etc.), however str() helps to explain the difference between Sport and Event: An Event looks to be the subset of a Sport, i.e. the Sport Basketball encompasses basketball-related events including Basketball Men's Basketball.

Conversions

Using the output of str() as a guide, conversions can be applied to the variables with mis-assigned data types. Firstly, the variables that should be un-ordered factors are each added to a character vector called factor_names. Using the lapply() function, each variable in the factor_names list is converted to a factor, without any changes to level names or ordering. Secondly, the Sex variable is of type Chr when it should be a factor. Using the factor() function, the levels M and F are re-named to Male and Female respectively and Sex is converted to a factor.

unique(olympics_sample$Year)
 [1] 1984 2016 1992 2014 2004 1998 1976 1924 2012 1996 1980 1956 2000 1948 1988 1920 1936 1952 1960 2008 1964 1928 1972 1912 1932 1968 2002 1994 2010 1908 2006
[32] 1906 1904 1900 1896

While Year contains 4-digit integers, these values skip every 2 to 4 years due to the amount of time between Olympic games. For this reason, Year will be converted to a factor.

unique(olympics_sample$Medal)
[1] NA       "Bronze" "Gold"   "Silver"

Finally, the Medal variable can be ordered by the podium positions: Gold > Silver > Bronze > NA. This is again performed with the factor() function, this time by also specifying the order of the levels and setting the ordered argument to T (or TRUE).

# Character to factor (no orderering or labelling)
factor_names <- c("Team", "NOC", "Games", "Season", "City", "Sport", "Event", "region")
olympics_sample[, factor_names] <- lapply(olympics_sample[, factor_names], factor)

# Character to factor (labelled)
olympics_sample$Sex <- factor(olympics_sample$Sex, levels = c("M", "F"), labels = c("Male", "Female"), ordered = F)

# Character to factor (ordered)
olympics_sample$Medal <- factor(olympics_sample$Medal, levels = c(NA, "Bronze", "Silver", "Gold"), ordered = T)

# number to factor
olympics_sample$Year <- factor(olympics_sample$Year)

str(olympics_sample)
Classes ‘spec_tbl_df’, ‘tbl_df’, ‘tbl’ and 'data.frame':    5000 obs. of  17 variables:
 $ ID    : num  94906 67498 62645 80790 113748 ...
 $ Name  : chr  "Paulo Braune Pimenta" "Pierre Le Coq" "Primo Kopa" "Aubrey Maphosa Modiba" ...
 $ Sex   : Factor w/ 2 levels "Male","Female": 1 1 1 1 1 2 1 1 1 1 ...
 $ Age   : num  23 27 21 21 33 22 33 29 39 27 ...
 $ Height: num  169 186 NA 172 180 174 177 NA 183 178 ...
 $ Weight: num  65 72 NA 64 102 63 72 NA NA 68 ...
 $ Team  : Factor w/ 279 levels "Afghanistan",..: 34 94 224 225 15 204 204 15 257 94 ...
 $ NOC   : Factor w/ 185 levels "AFG","AHO","ALG",..: 23 57 148 138 12 139 139 12 174 57 ...
 $ Games : Factor w/ 51 levels "1896 Summer",..: 34 51 39 51 50 45 42 30 8 34 ...
 $ Year  : Factor w/ 35 levels "1896","1900",..: 21 35 23 35 34 29 26 19 8 21 ...
 $ Season: Factor w/ 2 levels "Summer","Winter": 1 1 2 1 2 1 2 1 1 1 ...
 $ City  : Factor w/ 42 levels "Albertville",..: 19 28 1 28 35 4 25 22 27 19 ...
 $ Sport : Factor w/ 59 levels "Aeronautics",..: 41 40 44 22 11 5 15 48 41 5 ...
 $ Event : Factor w/ 564 levels "Aeronautics Mixed Aeronautics",..: 369 349 429 253 124 78 160 463 404 47 ...
 $ Medal : Ord.factor w/ 3 levels "Bronze"<"Silver"<..: NA 1 NA NA NA NA NA NA 3 NA ...
 $ region: Factor w/ 170 levels "Afghanistan",..: 21 52 139 140 10 127 127 10 162 52 ...
 $ notes : chr  NA NA NA NA ...

Showing the structure of the newly-converted dataset shows that the factors originally mistakenly assigned as chr have been correctly changed to Factor, and additionally, the levels within Sex have been re-labelled as Male and Female from M and F respectively, and finally Medal is now an ordered factor corresponding to the podium placements.

Tidy & Manipulate Data I

Hadley Wickham and Grolemund’s Tidy Data principles state that in tidy data:

  1. Each variable must have its own column.
  2. Each observation must have its own row.
  3. Each value must have its own cell.

Investigating each of these principles in the context of the olympics dataset will determine whether or not any conversions need to be applied to make the data tidy.

Firstly, each variable in the olympics dataset has its own column. The columns are always associated with a single variable, and any variable can simply be accessed with the standard R codes. For example: olympics$Name will return Athlete’s names and nothing else. olympics$Age will only return their ages, etc. Secondly, each observation in the olympics dataset has its own row; as discussed in the Data description, each row in the data frame corresponds to an athlete and the olympics that they competed in. Finally, each value in the olympics dataset has its own cell. At first glance there appears to be a breach of this principle in the variable Games as it contains a Year and Season (both of which are separate variables), however the description of the dataset on Kaggle.com states that “Winter and Summer Games were held in the same year up until 1992. After that, they staggered them such that Winter Games occur on a four year cycle starting with 1994, then Summer in 1996, then Winter in 1998, and so on.”, so for this purpose the Games variable is important as its own attribute.

From examining each of the tidy data principles, it can be concluded that the olympics dataset is tidy and reshaping is not required.

Tidy & Manipulate Data II

Since the olympics dataset contains multiple biological attributes, it is possible to calculate each athlete’s Body Mass Index (or BMI). Calculating BMI as a body-structure indicator can provide helpful insight to the different sizes of athletes between Sports, Age, Gender, etc.

BMI is calculated as (\(BMI = \frac{kg}{m^2}\)) or BMI = Weight (in kg) / Height (in m)^2. This calculation is performed within the mutate function of the dplyr package. A new variable BMI is created by dividing the existing variables Weight by Height by Height (to incorporate height^2) and multiplying the result by 10000 to factor in the incorrect measurement type for Height (Height is recorded in cm in the dataset, BMI calculations require meters). A sample of the resulting dataset shows the calculated BMIs for certain athletes.

# BMI = kg/m2 
olympics_mutate <- olympics_sample %>% mutate(
  BMI = round((Weight / Height / Height) * 10000, 2) 
)

olympics_mutate[c(1:5), ] %>% select(Name, Sex, Age, Height, Weight, BMI)

Scan I

To explore (and potentially deal with) inconsistencies and missing values, first their presence or absence needs to be determined. Starting with NA values; base R’s sum() and is.na() functions are applied to the olympics_mutate dataframe to determine a total count of all NA instances.

Missing Values

# Total missing values
sum(is.na(olympics_mutate))
[1] 12726

There are a total of 12,726 missing values in the olympics_mutate dataframe, which is too many to blindly remove or ignore as it would potentially distort the underlying message of the data. Calling the colSums() function on the dataframe will display which columns contain these NA instances.

# Missing values by column
colSums(is.na(olympics_mutate))
    ID   Name    Sex    Age Height Weight   Team    NOC  Games   Year Season   City  Sport  Event  Medal region  notes    BMI 
     0      0      0    165   1090   1133      0      0      0      0      0      0      0      0   4253     14   4909   1162 

The NA values are spread amongst the variables Age, Height, Weight, Medal, region, notes, and the mutated feature BMI. The numeric variables can be imputed but first the categorical variables will be addressed.

First, the nature of the Medal variable suggests that a missing value indicates that the athlete did not win a medal for that particular Event, Year, and Games combination. These NAs will be replaced by a new factor level called None. To accomplish this, the existing levels of Medals are assigned to a character vector medal_levels, the length of the vector is increased by 1 and the empty value is replaced by the string "None". Similar to the operations performed in 5. Understand, the Medal variable is converted to an ordered factor using the new medal_levels. Using the is.na() function, the NAs are replaced with the new factor level None. This process preserves the order of the Medal variable so that Gold > Silver > Bronze > None.

olympics_complete <- olympics_mutate

medal_levels <- levels(olympics_complete$Medal)
medal_levels[length(medal_levels) + 1] <- "None"

olympics_complete$Medal <- factor(olympics_complete$Medal, levels = medal_levels, order = T)
olympics_complete$Medal[is.na(olympics_complete$Medal)] <- "None"

Merging athletes and regions to form the initial olympics dataframe involved performing a left_join() which is prone to introducing NAs since each row in the left dataframe (athletes) is kept and a lack of corresponding rows in the right table (regions) results in an NA. The region and notes features contain numerous NAs, some of which are a result of the join and the remaining existed in the original table. A quick investigation of the rows whose region is NA shows the following:

head(olympics_complete[is.na(olympics_complete$region), "Team"], 5)

The data description states that the region variable is the country or region assosciated with the corresponding NOC code. In the above sample, all of the Teams contain the same value; Singapore.

unique(olympics_complete[is.na(olympics_complete$region), "Team"])
which("Singapore" %in% olympics_complete$region)              
integer(0)

Out of the 14 NA instances in regions, there is only 1 unique Team value, and it appears that Singapore never shows up in the region column despite showing up in Team. By variable definitions, Team and region aren’t always the same, however due to the small number of NAs, the Team value will be copied to region for these problematic rows via an ifelse() statement. As for notes, a similar approach to the solution for Medal will be applied, minus the factor ordering which will replace the NA values with "None".

# notes
notes_levels <- levels(olympics_complete$notes)
notes_levels[length(notes_levels) + 1] <- "None"

olympics_complete$notes <- factor(olympics_complete$notes, levels = medal_levels)
olympics_complete$notes[is.na(olympics_complete$notes)] <- "None"

# region
olympics_complete$region <- ifelse(is.na(olympics_complete$region), olympics_complete$Team, olympics_complete$region) 

The remaining variables that contain NA instances are Age, Height, Weight, and BMI. Being numeric variables, incorrectly changing or imputing these NAs can potentially distort the data and result in invalid conclusions. Starting with Age, summary statistics will be explored grouped by Sport to see if there is a difference in Age between different sports. This exploration will be conducted by using various functions from the dplyr package.

olympics_complete %>% 
  group_by(Sport) %>%  
  na.omit() %>% 
  summarize(mean=mean(Age), median = median(Age), min = min(Age), max = max(Age), SD = sd(Age)) %>% 
  sample_n(5)

The mean, median, and variance in Age appears to differ between sports which makes logical sense– an Olympic Gymnast or Swimmer would likely be younger than a Weightlifter or Shooter. The median Age of each Sport will be imputed to the missing Age variables.

olympics_complete <- olympics_complete %>%
 group_by(Sport) %>%
 mutate(
   Age = ifelse(
   is.na(Age),
   median(Age, na.rm = TRUE),
   Age)
 )

A similar approach will be applied to Height and Weight, this time using the median of the combination of Age and Sport. The logic here is that an 11-year-old Gymnast would likely weigh less and be shorter than a 40-year-old Gymnast, and be significantly smaller than a 40-year-old Weightlifter.

olympics_complete <- olympics_complete %>%
 na.omit() %>% 
 group_by(Sport, Age) %>%
 mutate(
   Height = ifelse(
   is.na(Height),
   median(Height, na.rm = TRUE),
   Height)
 )

olympics_complete <- olympics_complete %>%
 na.omit() %>% 
 group_by(Sport, Age) %>%
 mutate(
   Weight = ifelse(
   is.na(Weight),
   median(Weight, na.rm = TRUE),
   Weight)
 )

BMI is the final variable that contains NAs, which is a result of performing calculations on variables with missing values, Height and Weight. Since these NAs have been imputed, the now complete columns can be used to once again calculate BMI.

olympics_complete <- olympics_complete %>%
 mutate(
   BMI = ifelse(
   is.na(BMI),
   round((Weight / Height / Height) * 10000, 2),
   BMI)
 )

colSums(is.na(olympics_complete))
    ID   Name    Sex    Age Height Weight   Team    NOC  Games   Year Season   City  Sport  Event  Medal region  notes    BMI 
     0      0      0      0      0      0      0      0      0      0      0      0      0      0      0      0      0      0 

The result of the multiple imputations and renaming procedures is a new data.frame olympics_complete which contains some sort of value for every observation in the whole dataset.

Special Values

NA values aren’t the only problematic observations in an exploratory data analysis. To explore any potential Special Values, (Inf, -Inf, NaN), a custom function is_special is defined, which utilises base R’s functions is.infinite(), is.nan(), and is.na(). Applying this custom function to the olympics_complete data.frame will determine the number of these special values present.

is_special <- function(x){
 if (is.numeric(x)) (is.infinite(x) | is.nan(x) | is.na(x))
}
sum(is_special(olympics_complete))
[1] 0

The is_special() function has returned 0, indicating that there are no special values in the olympics_complete data.frame that require further investigation.

Inconsistencies and Errors

Another type of error to check for is inconsistencies and errors, or data values that are nonsensical in context. For example, negative values for Age, Height, or Weight, or exceedingly large values for those same variables (such as Age being over 200). Using the editset function from the editrules package, a set of rules is defined to identify any of these obvious errors. The rule set checks that:

  • Age is between 0 and 100,
  • Height is between 0 and 250,
  • Weight is between 0 and 300,
  • Season is one of either Summer or Winter,
  • and Medal is one of either Gold, Silver, Bronze or None

Some of the numerical boundaries are extreme, (i.e. an Olympic athlete being 0 or 100 years old is very unlikely), however the purpose of these functions is to locate inconsistencies and errors. Outliers will be explored later in this study. Some variables have too many factor levels to check for (Team and Event for example) so this ruleset is limited to numerical variables, and categorical variables that contain less than 5 unique levels.

(Rule1 <- editset(c("Age >= 0", "Age <= 100", "Height >= 0", 
                    "Height <= 250", "Weight >= 0", "Weight <= 300",
                    "Season %in% c('Summer', 'Winter')",
                    "Medal %in% c('Gold', 'Silver', 'Bronze', 'None')")))

Data model:
dat1 : Medal %in% c('Bronze', 'Gold', 'None', 'Silver')
dat2 : Season %in% c('Summer', 'Winter') 

Edit set:
num1 : 0 <= Age
num2 : Age <= 100
num3 : 0 <= Height
num4 : Height <= 250
num5 : 0 <= Weight
num6 : Weight <= 300 
sum(violatedEdits(Rule1, olympics_complete))
[1] 0

Using a combination of base R’s sum() and editrulesviolatedEdits shows that there are no instances in the olympics_complete dataset that violate the defined rules.

Scan II

Outliers

Missing or inconsistent data is one common issue in exploratory data analysis; another is outliers which can be present as a result of some form of error. A quick method of detecting the presence of outliers is to display boxplots for each numeric variable.

Boxplots show that there are outliers present in all of the numeric features (as represented by the dots) according to Tukey’s method of outlier detection (\(\pm1.5 × IQR\)). Shapes of the box plots give a general idea of each variable’s univariate distribution. Height is the only variable that appears to resemble normality, which suggests that the z-score approach can be used to detect outliers for these features. However since the combination of features Height, Weight, and BMI are not likely to be independent, multivariate outlier detection techniques will be used. Age will be assessed for univariate outliers.

Outliers in Age were highlighted in the initial boxplots. By saving a boxplot() of the Age variable to an object and accessing the $out attribute of the stored boxplot, the percentage of outliers according to Tukey’s method can be counted.

boxplot_age <- boxplot(olympics_complete$Age)

round(length(boxplot_age$out) / nrow(olympics_complete) * 100, 2)
[1] 2.55

Since only 2.55% of instances in Age are outliers, they can likely be removed without distorting the underlying data. As highlighted in the histogram, Age is not normally distributed so z-scores shouldn’t be used. Instead, capping (or Windsoring) will be implemented by invoking a user-defined function cap that replaces outliers with their nearest neighbour.

cap <- function(x){
    quantiles <- quantile( x, c(.05, 0.25, 0.75, .95 ) )
    x[ x < quantiles[2] - 1.5*IQR(x) ] <- quantiles[1]
    x[ x > quantiles[3] + 1.5*IQR(x) ] <- quantiles[4]
    x
}

olympics_capped <- olympics_complete
olympics_capped$Age <- cap(olympics_capped$Age)

The remaining numeric variables Height and Weight will be investigated using multivariate outlier detection methods, given that it is likely that a person’s height will impact their weight.

plot(olympics_capped$Height~olympics_capped$Weight, main = "Height vs Weight", xlab = "Weight", ylab = "Height")

Logically, a taller person would likely weigh more than a shorter person, which is supported by the scatterplot of Height vs Weight. There is a strong, positive correlation between the two variables with some likely outliers spread toward the outside of the plot space. Using the mvn() function from the MVN package can detect outliers using Mahalanobis distances.

mvn_results <- mvn(data = olympics_capped[, c("Height", "Weight")], multivariateOutlierMethod = "quan", showOutliers = TRUE)

Using the quan method in mvn() uses the chi-square distribution critical value approach to detect multivariate outliers. Values to the right of the red Quantile line in the plot are determined to be outliers from this method. As displayed on the visualisation, there are n=200 multivariate Height and Weight outliers.

mvn_results$multivariateOutliers
round(nrow(mvn_results$multivariateOutliers) / nrow(olympics_capped) * 100, 2)
[1] 5.21

5.21% of values are multivariate outliers amongst the Height and Weight variables, so it is likely safe to remove them without distorting the underlying data. This is accomplished by directly subsetting the problematic rows from the olympics_capped dataframe.

olympics_clean <- olympics_capped[ -c(1:nrow(mvn_results$multivariateOutliers)), ]
dim(olympics_clean)
[1] 3637   18

The remaining numeric variable with potential outliers is BMI, however this is a special case since it is calculated from two existing variables Height and Weight. Since the outliers for these variables have been addressed, the resulting BMI values aren’t likely to be caused by any form of user input error. Since BMI is a product of variables that have been screened for outliers, they will remain as-is.

Transform

The distributions of numeric features was briefly covered in Scan II - Outliers. Historgrams will provide more accurate insight.

As initially suspected, Height appears normally distributed and will require no transformations. Post-outlier removal, Age also appears to approximately resemble normality, leaving Weight and BMI that are both left skewed, with BMI containing a very narrow peak. Mathematical operations for Weight and BMI will be explored in attempt to make them more closely resemble the normal distribution.

sqrt_weight <- sqrt(olympics_clean$Weight)
hist(sqrt_weight, main = "Histogram of sqrt(Weight)")

The square root operation sqrt() has transformed the Weight feature to more closely resemble normality.

bmi_recip <- 1/(olympics_clean$BMI)

hist(bmi_recip)

Applying the reciprocal function to BMI shows that the shape is closer to normal.

After converting the previously explored variables using sqrt() for Weight and the reciprocal of BMI, each of the numeric features now approximately resembles normality which is an assumption and sometimes a requirement for many statistical tests.

After investigating the Exploring 120 Years of Olympic History dataset, converting the necessary data types, handling outliers and missing values, and transforming numeric features to normal, the data is now in a clean & complete form that is suitable for statistical analysis and potentially machine learning applications.



LS0tDQp0aXRsZTogIk1BVEgyMzQ5IFNlbWVzdGVyIDEsIDIwMTkgLSBBc3NpZ25tZW50IDMiDQphdXRob3I6ICJDYWl0bGluIExvbnNraSwgczM2Nzk4NTAiDQpzdWJ0aXRsZTogRXhwbG9yaW5nIDEyMCBZZWFycyBvZiBPbHltcGljIEhpc3RvcnkNCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdA0KLS0tDQoNCiMjIFJlcXVpcmVkIHBhY2thZ2VzIA0KDQpgYGB7ciBpbmNsdWRlPUZBTFNFfQ0Kb3B0aW9ucyh3YXJuPS0xKQ0Kc2V0LnNlZWQoMTIzKQ0KYGBgDQoNCmBgYHtyfQ0KbGlicmFyeShyZWFkcikNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KGVkaXRydWxlcykNCmxpYnJhcnkoTVZOKQ0KYGBgDQoNCg0KIyMgRXhlY3V0aXZlIFN1bW1hcnkgDQoNClRoZSB0d28gdGFibGVzIGluIHRoZSBfRXhwbG9yaW5nIDEyMCBZZWFycyBvZiBPbHltcGljIEhpc3RvcnlfIGRhdGFzZXQgYXJlIGxvYWRlZCBpbiB0byBSLCBzdG9yZWQgYXMgcmVzcGVjdGl2ZSBkYXRhZnJhbWUgb2JqZWN0cywgYW5kIGJyaWVmbHkgZXhwbG9yZWQgYnkgZGlzcGxheWluZyB0aGUgYGhlYWQoKWAgb2YgZWFjaCBkYXRhZnJhbWUuIFRoZSBpbmRpdmlkdWFsIHRhYmxlcyBhcmUgY29tYmluZWQgd2l0aCBhIGpvaW4gb24gYSBzaGFyZWQgdmFsdWUsIGNyZWF0aW5nIGEgc2luZ2xlIGRhdGFmcmFtZSB0aGF0IGNvbnRhaW5zIGFsbCBmZWF0dXJlcyBpbiB0aGUgT2x5bXBpYyBIaXN0b3J5IGRhdGFzZXQuIEEgc2FtcGxlIG9mIHRoZSBqb2luZWQgZGF0YWZyYW1lIGlzIHRha2VuIGFuZCBkYXRhIHR5cGVzIG9mIGVhY2ggdmFyaWFibGUgYXJlIGV4cGxvcmVkLCB1c2luZyB0aGUgYGNsYXNzKClgIGZ1bmN0aW9uIHRvIGV4cGxvcmUgdHlwZWQgYW5kIGBzdHIoKWAgZnVuY3Rpb24gdG8gZGlzcGxheSB0aGUgc2l6ZSBvZiB0aGUgZGF0YXNldCBhbmQgYSBzYW1wbGUgb2YgZWFjaCB2YXJpYWJsZS4gVGhlc2UgZXhwbG9yYXRpb24gcHJvY2Vzc2VzIGhpZ2hsaWdodCBpbmNvcnJlY3RseSBhc3NpZ25lZCBkYXRhIHR5cGVzIHRoYXQgbmVlZCB0byBiZSBjb252ZXJ0ZWQuIEFmdGVyIHRoZXNlIHRyYW5zZm9ybWF0aW9ucyBhcmUgYXBwbGllZCwgdGhlIHN0cnVjdHVyZSBvZiB0aGUgZGF0YSBpcyBleHBsb3JlZCB1c2luZyB0aGUgX1RpZHkgRGF0YSBQcmluY2lwbGVzXyB0byBkZXRlcm1pbmUgaWYgaXQgaXMgdGlkeS4gQSBuZXcgdmFyaWFibGUgaXMgY3JlYXRlZCBieSBtdWx0aXBseWluZyBleGlzdGluZyBvbmVzIHdpdGggdGhlIGBtdXRhdGUoKWAgZnVuY3Rpb24uIE1pc3NpbmcgdmFsdWVzLCBpbmNvbnNpc3RlbmNpZXMsICYgZXJyb3JzIGFyZSBleHBsb3JlZCB3aXRoIHRoZSB1c2Ugb2YgdGhlIGBpcy5uYSgpYCBmdW5jdGlvbiAoZm9yIHRoZSB3aG9sZSBkYXRhc2V0IHdpdGggYHN1bSgpYCBhbmQgaW5kaXZpZHVhbCBjb2x1bW5zIHdpdGggYGNvbFN1bXMoKWApLiBBIHVzZXItZGVmaW5lZCBmdW5jdGlvbiBsb2NhdGVzIHNwZWNpYWwgdmFsdWVzLCBhbmQgYSBydWxlIHNldCB0byBpZGVudGlmeSBvYnZpb3VzIGVycm9ycyBpcyBhcHBsaWVkIHRvIHRoZSBkYXRhLiBPbmNlIGxvY2F0ZWQsIHRoZSBwcm9ibGVtYXRpYyB2YWx1ZXMgYXJlIGhhbmRsZWQgZm9yIGJvdGggbnVtZXJpYyBhbmQgY2F0ZWdvcmljYWwgZmVhdHVyZXM7IGVpdGhlciBieSBzdHJpbmcgcmVwbGFjZW1lbnRzIG9yIGltcHV0YXRpb25zIHdpdGggc3RhdGlzdGljcyBmcm9tIHNpbWlsYXIgb2JzZXJ2YXRpb25zLiBBbnkgb3V0bGllcnMgaW4gbnVtZXJpY2FsIGZlYXR1cmVzIGFyZSBleHBsb3JlZCBhbmQgZGVhbHQgd2l0aCB1c2luZyBjYXBwaW5nIGZvciB1bml2YXJpYXRlIG91dGxpZXJzIGFuZCBgbXZuKClgcyBNYWhhbGFub2JpcyBkaXN0YW5jZXMgZm9yIG11bHRpdmFyaWF0ZSBvdXRsaWVycy4gTWV0aG9kcyBvZiBkZWFsaW5nIHdpdGggb3V0bGllcnMgYXJlIGRldGVybWluZWQgYnkgY2FsY3VsYXRpbmcgdGhlIHBlcmNlbnRhZ2Ugb2Ygb3V0bGllcnMuIEFmdGVyIGV4cGxvcmluZyB0aGVpciBkaXN0cmlidXRpb25zLCBtYXRoZW1hdGljYWwgb3BlcmF0aW9ucyBhcmUgdXNlZCB0byB0cmFuc2Zvcm0gbnVtZXJpYyB2YXJpYWJsZXMgdG8gbWFrZSB0aGVtIG1vcmUgY2xvc2VseSByZXNlbWJsZSBub3JtYWxpdHkuIFRoZSByZXN1bHRpbmcgZGF0YXNldCBpcyB0aWR5LCBjb21wbGV0ZSwgYW5kIHN1aXRhYmxlIHRvIHVzZSBmb3Igc3RhdGlzdGljYWwgYW5hbHlzaXMgb3IgbWFjaGluZSBsZWFybmluZyBhcHBsaWNhdGlvbnMuDQoNCg0KIyMgRGF0YSANCg0KVGhlIFsxMjAgWWVhcnMgb2YgT2x5bXBpYyBIaXN0b3J5OiBBdGhsZXRlcyBhbmQgUmVzdWx0c10oaHR0cHM6Ly93d3cua2FnZ2xlLmNvbS9oZWVzb28zNy8xMjAteWVhcnMtb2Ytb2x5bXBpYy1oaXN0b3J5LWF0aGxldGVzLWFuZC1yZXN1bHRzKSBoYXMgYmVlbiB1cGxvYWRlZCB0byBbS2FnZ2xlLmNvbV0oaHR0cHM6Ly93d3cua2FnZ2xlLmNvbS8pIGFuZCBmYWxscyB1bmRlciB0aGUgQ3JlYXRpdmUgQ29tbW9ucyBMaWNlbnNlLiBUaGUgZGF0YXNldCBjb25zaXN0cyBvZiB0d28gdGFibGVzOyBgYXRobGV0ZV9ldmVudHMuY3N2YCBhbmQgYG5vY19yZWdpb25zLmNzdmAuIFRoZSBhdGhsZXRlIGV2ZW50cyBkYXRhc2V0IGNvbnRhaW5zIGluZm9ybWF0aW9uIGFib3V0IE9seW1waWMgQXRobGV0ZXMgYW5kIGV2ZW50cyB0aGF0IHRoZXkgaGF2ZSBjb21wZXRlZCBpbiwgaW5jbHVkaW5nIGJpb2xvZ2ljYWwgZGF0YSAoQWdlLCBTZXgsIEhlaWdodCwgV2VpZ2h0IGV0Yy4pIGFuZCBldmVudCBkYXRhIChZZWFyLCBTZWFzb24sIENpdHksIFNwb3J0IGV0Yy4pLiBUaGUgTk9DIChOYXRpb25hbCBPcmdhbml6aW5nIENvbW1pdHRlZSkgcmVnaW9ucyBkYXRhc2V0IHBvc3Nlc3NlcyBpbmZvcm1hdGlvbiBhYm91dCB0aGUgY291bnRyaWVzIHRoYXQgY29tcGV0ZSBpbiB0aGUgT2x5bXBpY3MsIGluY2x1ZGluZyB0aGUgY291bnRyeSBuYW1lIGFuZCBhbnkgbm90ZXMgYWJvdXQgc2FpZCBjb3VudHJ5LiANCg0KVGhlIEF0aGxldGVzIGRhdGFzZXQgY29udGFpbnMgMTUgdmFyaWFibGVzOg0KDQoqIF9fSURfXzogQSBudW1iZXIgdXNlZCBhcyBhIHVuaXF1ZSBpZGVudGlmaWVyIGZvciBlYWNoIGF0aGxldGUNCiogX19OYW1lX186IFRoZSBhdGhsZXRlJ3MgbmFtZShzKSBpbiB0aGUgZm9ybSBvZiBgRmlyc3QgTWlkZGxlIExhc3RgIHdoZXJlIGF2YWlsYWJsZQ0KKiBfX1NleF9fOiBUaGUgYXRobGV0ZSdzIGdlbmRlcjsgb25lIG9mIGBNYCBvciBgRmANCiogX19BZ2VfXzogVGhlIGF0aGxldGUncyBhZ2UgaW4geWVhcnMNCiogX19IZWlnaHRfXzogVGhlIGF0aGxldGUncyBoZWlnaHQgaW4gY2VudGltZXRlcnMgKGNtKQ0KKiBfX1dlaWdodF9fOiBUaGUgYXRobGV0ZSdzIHdlaWdodCBpbiBraWxvZ3JhbXMgKGtnKQ0KKiBfX1RlYW1fXzogVGhlIG5hbWUgb2YgdGhlIHRlYW0gdGhhdCB0aGUgYXRobGV0ZSBjb21wZXRlZCBmb3INCiogX19OT0NfXzogVGhlIE5hdGlvbmFsIE9yZ2FuaXppbmcgQ29tbWl0dGVlJ3MgMy1sZXR0ZXIgY29kZQ0KKiBfX0dhbWVzX186IFRoZSB5ZWFyIGFuZCBzZWFzb24gb2YgdGhlIE9seW1waWNzIHRoZSBhdGhsZXRlIGNvbXBldGVkIGluIGluIHRoZSBmb3JtYXQgYFlZWVkgU2Vhc29uYA0KKiBfX1llYXJfXzogVGhlIHllYXIgb2YgdGhlIE9seW1waWNzIHRoYXQgdGhlIGF0aGxldGUgY29tcGV0ZWQgaW4NCiogX19TZWFzb25fXzogVGhlIHNlYXNvbiBvZiB0aGUgT2x5bXBpY3MgdGhhdCB0aGUgYXRobGV0ZSBjb21wZXRlZCBpbg0KKiBfX0NpdHlfXzogVGhlIGNpdHkgdGhhdCBob3N0ZWQgdGhlIE9seW1waWNzIHRoYXQgdGhlIGF0aGxldGUgY29tcGV0ZWQgaW4NCiogX19TcG9ydF9fOiBUaGUgc3BvcnQgdGhhdCB0aGUgYXRobGV0ZSBjb21wZXRlZCBpbg0KKiBfX0V2ZW50X186IFRoZSBldmVudCB0aGF0IHRoZSBhdGhsZXRlIGNvbXBldGVkIGluDQoqIF9fTWVkYWxfXzogVGhlIG1lZGFsIHdvbiBieSB0aGUgYXRobGV0ZTsgb25lIG9mIGBHb2xkYCwgYFNpbHZlcmAsIG9yIGBCcm9uemVgLiBgTkFgIGlmIG5vIG1lZGFsIHdhcyB3b24uDQoNClRoZSBOT0MgUmVnaW9ucyBkYXRhc2V0IGNvbnRhaW5zIDMgdmFyaWFibGVzOiANCg0KKiBfX05PQ19fOiBUaGUgTmF0aW9uYWwgT3JnYW5pemluZyBDb21taXR0ZWXigJlzIDMtbGV0dGVyIGNvZGUNCiogX19yZWdpb25fXzogVGhlIG5hbWUgb2YgdGhlIGNvdW50cnkvcmVnaW9uIGFzc29jaWF0ZWQgd2l0aCB0aGUgTk9DIGNvZGUNCiogX19ub3Rlc19fOiBBbnkgZXh0cmEvbWlzY2VsbGFuZW91cyBpbmZvcm1hdGlvbiBhYm91dCB0aGUgTk9DIHJlZ2lvbiANCg0KIyMjIExvYWQgRGF0YQ0KDQpFYWNoIGRhdGFzZXQgaXMgaW5kaXZpZHVhbGx5IGxvYWRlZCBpbiB0byBSIHVzaW5nIHRoZSBgcmVhZF9jc3YoKWAgZnVuY3Rpb24gZnJvbSB0aGUgYHJlYWRyYCBsaWJyYXJ5LiBgcmVhZF9jc3YoKWAgaXMgdXNlZCBpbnN0ZWFkIG9mIGJhc2UgUidzIGByZWFkLmNzdigpYCBzaW5jZSBpdCBpdCBmYXN0ZXIgYXQgcHJvY2Vzc2luZyBsYXJnZXIgZGF0YXNldHMuIFRoZSBgYXRobGV0ZV9ldmVudHMuY3N2YCBkYXRhc2V0IGlzIGFzc2lnbmVkIHRvIGFuIG9iamVjdCBuYW1lZCBgYXRobGV0ZXNgLiBBZnRlciB3aGljaCwgdGhlIGBoZWFkKClgIGZ1bmN0aW9uIGRpc3BsYXlzIHRoZSBmaXJzdCA1IHJvd3Mgb2YgdGhlIG5ld2x5IGNyZWF0ZWQgYGRhdGEuZnJhbWVgIHRvIGVuc3VyZSB0aGF0IGV2ZXJ5dGhpbmcgaGFzIGJlZW4gaW1wb3J0ZWQgY29ycmVjdGx5Lg0KYGBge3J9DQphdGhsZXRlcyA8LSByZWFkX2NzdigiRGF0YS9PbHltcGljcy9hdGhsZXRlX2V2ZW50cy5jc3YiLCBjb2xfdHlwZXMgPSBjb2xzKCkpDQpoZWFkKGF0aGxldGVzLCA1KQ0KYGBgDQoNClRoZSBzYW1lIHByb2Nlc3MgdXNlZCB0byBsb2FkIGBhdGhsZXRlX2V2ZW50cy5jc3ZgIGlzIGFwcGxpZWQgdG8gYG5vY19yZWdpb25zLmNzdmAsIHdpdGggdGhpcyBkYXRhc2V0IGJlaW5nIGFzc2lnbmVkIHRvIGFuIG9iamVjdCBjYWxsZWQgYHJlZ2lvbnNgLg0KDQpgYGB7cn0NCnJlZ2lvbnMgPC0gcmVhZF9jc3YoIkRhdGEvT2x5bXBpY3Mvbm9jX3JlZ2lvbnMuY3N2IiwgY29sX3R5cGVzID0gY29scygpKQ0KaGVhZChyZWdpb25zLCA1KQ0KYGBgDQoNCiMjIyBNZXJnZQ0KDQpUaGUgdHdvIGRhdGFzZXRzIHNoYXJlIGEgY29tbW9uIHZhcmlhYmxlOiBOT0MuIEEgYGxlZnRfam9pbmAgaXMgdXNlZCB0byBqb2luIGByZWdpb25zYCB0byBgYXRobGV0ZXNgIGJ5IGBOT0NgIHZhbHVlLiBUaGlzIHByZXNlcnZlcyBhbGwgb2YgdGhlIGF0aGxldGUncyBpbmZvcm1hdGlvbiB3aXRoaW4gYGF0aGxldGVzYCBhbmQgYXBwZW5kcyB0aGUgY29ycmVzcG9uZGluZyByZWdpb24gaW5mb3JtYXRpb24gZnJvbSBgcmVnaW9uc2AsIGFwcGx5aW5nIGFuIGBOQWAgdmFsdWUgd2hlcmUgbm8gbWF0Y2hpbmcgaW5mb3JtYXRpb24gaXMgYXZhaWxhYmxlLiBUaGVzZSBtZXJnZWQgZGF0YXNldHMgYXJlIGFzc2lnbmVkIHRvIGEgbmV3IG9iamVjdCBjYWxsZWQgYG9seW1waWNzYC4gT25jZSBhZ2FpbiBjYWxsaW5nIHRoZSBgaGVhZCgpYCBmdW5jdGlvbiBzaG93cyB0aGUgZmlyc3QgNSByb3dzIG9mIHRoZSBuZXdseSBjcmVhdGVkIGBkYXRhLmZyYW1lYCB0byBjb25maXJtIHRoYXQgdGhlIHR3byBkYXRhc2V0cyBoYXZlIGJlZW4gbWVyZ2VkIGNvcnJlY3RseS4NCg0KYGBge3J9DQpvbHltcGljcyA8LSBsZWZ0X2pvaW4oYXRobGV0ZXMsIHJlZ2lvbnMsIGJ5ID0gIk5PQyIpDQpoZWFkKG9seW1waWNzLCA1KQ0KYGBgDQoNCkFmdGVyIHRoZSBqb2luIGlzIHBlcmZvcm1lZCwgZHVlIHRvIHRoZSBzaXplIG9mIHRoZSBkYXRhc2V0IGEgcmFuZG9tIHNhbXBsZSBvZiA1MDAgcm93cyB3aWxsIGJlIHBhcnRpdGlvbmVkIGZvciBhbmFseXNpcyB0byBsaW1pdCBwcm9jZXNzaW5nIHRpbWUgdXNpbmcgdGhlIGBzYW1wbGVfbigpYCBmdW5jdGlvbi4NCg0KYGBge3J9DQpvbHltcGljc19zYW1wbGUgPC0gc2FtcGxlX24ob2x5bXBpY3MsIDUwMDApDQpgYGANCg0KIyMgVW5kZXJzdGFuZCANCg0KVGhlIGZ1bmN0aW9uIGBjbGFzcygpYCBjYW4gYmUgY2FsbGVkIHRvIGRldGVybWluZSB0aGUgZGF0YSB0eXBlIHN0b3JlZCB3aXRoaW4gdGhlIHZhcmlhYmxlcyBpbiB0aGUgYG9seW1waWNzYCBkYXRhc2V0LiBUbyBwcmV2ZW50IHRoZSBuZWVkIHRvIGNhbGwgYGNsYXNzKClgIG9uY2UgZm9yIGVhY2ggaW5kaXZpZHVhbCB2YXJpYWJsZSwgdGhlIGBzYXBwbHlgIGZ1bmN0aW9uIGFkbWluaXN0ZXJzIGl0IHRvIGVhY2ggY29sdW1uIGF1dG9tYXRpY2FsbHkuDQoNCmBgYHtyfQ0Kc2FwcGx5KG9seW1waWNzX3NhbXBsZSwgY2xhc3MpDQpgYGANClRoZXJlIGFyZSBtdWx0aXBsZSBkYXRhIHR5cGVzIHdpdGhpbiB0aGUgYG9seW1waWNzYCBkYXRhc2V0LCBob3dldmVyIHRoZSBgcmVhZF9jc3YoKWAgZnVuY3Rpb24gaGFzIGFzc2lnbmVkIHRoZW0gYWxsIGFzIGVpdGhlciBgY2hhcmFjdGVyYCBvciBgbnVtZXJpY2AuIFdoaWxlIHRoaXMgaXMgY29ycmVjdCBpbiBzb21lIGNhc2VzIChgTmFtZWAsIGBIZWlnaHRgLCBgV2VpZ2h0YCwgZXRjLikgc29tZSBoYXZlIGJlZW4gbWlzLWxhYmVsbGVkLiBTcGVjaWZpY2FsbHksIGBNZWRhbHNgIHNob3VsZCBiZSBhbiBvcmRlcmVkIGZhY3RvciwgYFNleGAgc2hvdWxkIGJlIGEgZmFjdG9yIGFuZCByZXF1aXJlcyBhZGRpdGlvbmFsIHJlLWxhYmVsbGluZyBvZiBsZXZlbHMsIGFuZCBtdWx0aXBsZSBvdGhlciBmZWF0dXJlcyByZXF1aXJlIGZhY3RvciBjb252ZXJzaW9ucy4gPGJyPjxicj4NCg0KQmFzZSBSJ3MgYHN0cigpYCBmdW5jdGlvbiBkaXNwbGF5cyB0aGUgc3RydWN0dXJlIG9mIHRoZSBgb2x5bXBpY3NgIGRhdGFzZXQsIGluY2x1ZGluZyB0aGUgYXR0cmlidXRlIG5hbWUsIGRhdGEgdHlwZSwgYW5kIHNhbXBsZSBvZiB2YWx1ZXMgZm9yIGVhY2ggY29sdW1uLiBgc3RyKClgIGFsc28gcmV0dXJucyB0aGUgbnVtYmVyIG9mIHZhcmlhYmxlcyBhbmQgb2JzZXJ2YXRpb25zIGluIHRoZSBkYXRhc2V0Lg0KYGBge3J9DQpzdHIob2x5bXBpY3Nfc2FtcGxlKQ0KYGBgDQpUaGUgb3V0cHV0IG9mIGBzdHIoKWAgY29uZmlybXMgdGhhdCBzb21lIHZhcmlhYmxlcyBoYXZlIGJlZW4gbWlzLWFzc2lnbmVkIGRhdGEgdHlwZXMuIEFkZGl0aW9uYWxseSwgdGhlIHNoYXBlIG9mIHRoZSBkYXRhc2V0IGlzIHNob3duIHRvIGJlIDUsMDAwIG9ic2VydmF0aW9ucyBvZiAxNyB2YXJpYWJsZXMuIFRoaXMgZnVydGhlciBjb25maXJtcyB0aGF0IHRoZSB0d28gZGF0YXNldHMgaGF2ZSBiZWVuIGNvcnJlY3RseSBqb2luZWQgc2luY2UgYGF0aGxldGVzYCBjb250YWluZWQgMTUgdmFyaWFibGVzLCBgcmVnaW9uc2Agd2FzIG1hZGUgdXAgb2YgMywgYW5kIHRoZXkgZWFjaCBzaGFyZWQgYSBjb21tb24gYXR0cmlidXRlIHRvIG1ha2UgdXAgMTcgdG90YWwgdmFyaWFibGVzLiBEaXNwbGF5aW5nIGEgc2FtcGxlIG9mIHRoZSB2YWx1ZXMgd2l0aGluIGVhY2ggdmFyaWFibGUgcHJvdmlkZXMgc29tZSBpbnNpZ2h0IHRvIHRoZSBjb250ZW50cyBvZiB0aGUgZGF0YXNldC4gTW9zdCBhcmUgdG8gYmUgZXhwZWN0ZWQsIChgQWdlYCBpcyBhIDItZGlnaXQgaW50ZWdlciwgYFRlYW1gIGlzIGEgQ291bnRyeSBuYW1lIGFzIGEgY2hhcmFjdGVyIHN0cmluZywgZXRjLiksIGhvd2V2ZXIgYHN0cigpYCBoZWxwcyB0byBleHBsYWluIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gYFNwb3J0YCBhbmQgYEV2ZW50YDogQW4gYEV2ZW50YCBsb29rcyB0byBiZSB0aGUgc3Vic2V0IG9mIGEgYFNwb3J0YCwgaS5lLiB0aGUgYFNwb3J0YCBgQmFza2V0YmFsbGAgZW5jb21wYXNzZXMgYmFza2V0YmFsbC1yZWxhdGVkIGV2ZW50cyBpbmNsdWRpbmcgYEJhc2tldGJhbGwgTWVuJ3MgQmFza2V0YmFsbGAuIA0KDQojIyMgQ29udmVyc2lvbnMNClVzaW5nIHRoZSBvdXRwdXQgb2YgYHN0cigpYCBhcyBhIGd1aWRlLCBjb252ZXJzaW9ucyBjYW4gYmUgYXBwbGllZCB0byB0aGUgdmFyaWFibGVzIHdpdGggbWlzLWFzc2lnbmVkIGRhdGEgdHlwZXMuIEZpcnN0bHksIHRoZSB2YXJpYWJsZXMgdGhhdCBzaG91bGQgYmUgdW4tb3JkZXJlZCBmYWN0b3JzIGFyZSBlYWNoIGFkZGVkIHRvIGEgY2hhcmFjdGVyIHZlY3RvciBjYWxsZWQgYGZhY3Rvcl9uYW1lc2AuIFVzaW5nIHRoZSBgbGFwcGx5KClgIGZ1bmN0aW9uLCBlYWNoIHZhcmlhYmxlIGluIHRoZSBgZmFjdG9yX25hbWVzYCBsaXN0IGlzIGNvbnZlcnRlZCB0byBhIGZhY3Rvciwgd2l0aG91dCBhbnkgY2hhbmdlcyB0byBsZXZlbCBuYW1lcyBvciBvcmRlcmluZy4gU2Vjb25kbHksIHRoZSBgU2V4YCB2YXJpYWJsZSBpcyBvZiB0eXBlIGBDaHJgIHdoZW4gaXQgc2hvdWxkIGJlIGEgZmFjdG9yLiBVc2luZyB0aGUgYGZhY3RvcigpYCBmdW5jdGlvbiwgdGhlIGxldmVscyBgTWAgYW5kIGBGYCBhcmUgcmUtbmFtZWQgdG8gYE1hbGVgIGFuZCBgRmVtYWxlYCByZXNwZWN0aXZlbHkgYW5kIGBTZXhgIGlzIGNvbnZlcnRlZCB0byBhIGZhY3Rvci4gDQoNCmBgYHtyfQ0KdW5pcXVlKG9seW1waWNzX3NhbXBsZSRZZWFyKQ0KYGBgDQoNCldoaWxlIGBZZWFyYCBjb250YWlucyA0LWRpZ2l0IGludGVnZXJzLCB0aGVzZSB2YWx1ZXMgc2tpcCBldmVyeSAyIHRvIDQgeWVhcnMgZHVlIHRvIHRoZSBhbW91bnQgb2YgdGltZSBiZXR3ZWVuIE9seW1waWMgZ2FtZXMuIEZvciB0aGlzIHJlYXNvbiwgYFllYXJgIHdpbGwgYmUgY29udmVydGVkIHRvIGEgZmFjdG9yLg0KDQpgYGB7cn0NCnVuaXF1ZShvbHltcGljc19zYW1wbGUkTWVkYWwpDQpgYGANCg0KRmluYWxseSwgdGhlIGBNZWRhbGAgdmFyaWFibGUgY2FuIGJlIG9yZGVyZWQgYnkgdGhlIHBvZGl1bSBwb3NpdGlvbnM6IGBHb2xkYCA+IGBTaWx2ZXJgID4gYEJyb256ZWAgPiBgTkFgLiBUaGlzIGlzIGFnYWluIHBlcmZvcm1lZCB3aXRoIHRoZSBgZmFjdG9yKClgIGZ1bmN0aW9uLCB0aGlzIHRpbWUgYnkgYWxzbyBzcGVjaWZ5aW5nIHRoZSBvcmRlciBvZiB0aGUgbGV2ZWxzIGFuZCBzZXR0aW5nIHRoZSBgb3JkZXJlZGAgYXJndW1lbnQgdG8gYFRgIChvciBgVFJVRWApLg0KDQpgYGB7cn0NCiMgQ2hhcmFjdGVyIHRvIGZhY3RvciAobm8gb3JkZXJlcmluZyBvciBsYWJlbGxpbmcpDQpmYWN0b3JfbmFtZXMgPC0gYygiVGVhbSIsICJOT0MiLCAiR2FtZXMiLCAiU2Vhc29uIiwgIkNpdHkiLCAiU3BvcnQiLCAiRXZlbnQiLCAicmVnaW9uIikNCm9seW1waWNzX3NhbXBsZVssIGZhY3Rvcl9uYW1lc10gPC0gbGFwcGx5KG9seW1waWNzX3NhbXBsZVssIGZhY3Rvcl9uYW1lc10sIGZhY3RvcikNCg0KIyBDaGFyYWN0ZXIgdG8gZmFjdG9yIChsYWJlbGxlZCkNCm9seW1waWNzX3NhbXBsZSRTZXggPC0gZmFjdG9yKG9seW1waWNzX3NhbXBsZSRTZXgsIGxldmVscyA9IGMoIk0iLCAiRiIpLCBsYWJlbHMgPSBjKCJNYWxlIiwgIkZlbWFsZSIpLCBvcmRlcmVkID0gRikNCg0KIyBDaGFyYWN0ZXIgdG8gZmFjdG9yIChvcmRlcmVkKQ0Kb2x5bXBpY3Nfc2FtcGxlJE1lZGFsIDwtIGZhY3RvcihvbHltcGljc19zYW1wbGUkTWVkYWwsIGxldmVscyA9IGMoTkEsICJCcm9uemUiLCAiU2lsdmVyIiwgIkdvbGQiKSwgb3JkZXJlZCA9IFQpDQoNCiMgbnVtYmVyIHRvIGZhY3Rvcg0Kb2x5bXBpY3Nfc2FtcGxlJFllYXIgPC0gZmFjdG9yKG9seW1waWNzX3NhbXBsZSRZZWFyKQ0KDQpzdHIob2x5bXBpY3Nfc2FtcGxlKQ0KYGBgDQpTaG93aW5nIHRoZSBzdHJ1Y3R1cmUgb2YgdGhlIG5ld2x5LWNvbnZlcnRlZCBkYXRhc2V0IHNob3dzIHRoYXQgdGhlIGZhY3RvcnMgb3JpZ2luYWxseSBtaXN0YWtlbmx5IGFzc2lnbmVkIGFzIGBjaHJgIGhhdmUgYmVlbiBjb3JyZWN0bHkgY2hhbmdlZCB0byBgRmFjdG9yYCwgYW5kIGFkZGl0aW9uYWxseSwgdGhlIGxldmVscyB3aXRoaW4gYFNleGAgaGF2ZSBiZWVuIHJlLWxhYmVsbGVkIGFzIGBNYWxlYCBhbmQgYEZlbWFsZWAgZnJvbSBgTWAgYW5kIGBGYCByZXNwZWN0aXZlbHksIGFuZCBmaW5hbGx5IGBNZWRhbGAgaXMgbm93IGFuIG9yZGVyZWQgZmFjdG9yIGNvcnJlc3BvbmRpbmcgdG8gdGhlIHBvZGl1bSBwbGFjZW1lbnRzLg0KDQojIwlUaWR5ICYgTWFuaXB1bGF0ZSBEYXRhIEkgDQoNCkhhZGxleSBXaWNraGFtIGFuZCBHcm9sZW11bmQncyBUaWR5IERhdGEgcHJpbmNpcGxlcyBzdGF0ZSB0aGF0IGluIHRpZHkgZGF0YToNCg0KMS4gRWFjaCB2YXJpYWJsZSBtdXN0IGhhdmUgaXRzIG93biBjb2x1bW4uDQoyLiBFYWNoIG9ic2VydmF0aW9uIG11c3QgaGF2ZSBpdHMgb3duIHJvdy4NCjMuIEVhY2ggdmFsdWUgbXVzdCBoYXZlIGl0cyBvd24gY2VsbC4NCg0KSW52ZXN0aWdhdGluZyBlYWNoIG9mIHRoZXNlIHByaW5jaXBsZXMgaW4gdGhlIGNvbnRleHQgb2YgdGhlIGBvbHltcGljc2AgZGF0YXNldCB3aWxsIGRldGVybWluZSB3aGV0aGVyIG9yIG5vdCBhbnkgY29udmVyc2lvbnMgbmVlZCB0byBiZSBhcHBsaWVkIHRvIG1ha2UgdGhlIGRhdGEgdGlkeS4gPGJyPjxicj4NCg0KRmlyc3RseSwgZWFjaCB2YXJpYWJsZSBpbiB0aGUgYG9seW1waWNzYCBkYXRhc2V0IGhhcyBpdHMgb3duIGNvbHVtbi4gVGhlIGNvbHVtbnMgYXJlIGFsd2F5cyBhc3NvY2lhdGVkIHdpdGggYSBzaW5nbGUgdmFyaWFibGUsIGFuZCBhbnkgdmFyaWFibGUgY2FuIHNpbXBseSBiZSBhY2Nlc3NlZCB3aXRoIHRoZSBzdGFuZGFyZCBSIGNvZGVzLiBGb3IgZXhhbXBsZTogYG9seW1waWNzJE5hbWVgIHdpbGwgcmV0dXJuIEF0aGxldGUncyBuYW1lcyBhbmQgbm90aGluZyBlbHNlLiBgb2x5bXBpY3MkQWdlYCB3aWxsIG9ubHkgcmV0dXJuIHRoZWlyIGFnZXMsIGV0Yy4gU2Vjb25kbHksIGVhY2ggb2JzZXJ2YXRpb24gaW4gdGhlIGBvbHltcGljc2AgZGF0YXNldCBoYXMgaXRzIG93biByb3c7IGFzIGRpc2N1c3NlZCBpbiB0aGUgRGF0YSBkZXNjcmlwdGlvbiwgZWFjaCByb3cgaW4gdGhlIGRhdGEgZnJhbWUgY29ycmVzcG9uZHMgdG8gYW4gYXRobGV0ZSBhbmQgdGhlIG9seW1waWNzIHRoYXQgdGhleSBjb21wZXRlZCBpbi4gRmluYWxseSwgZWFjaCB2YWx1ZSBpbiB0aGUgYG9seW1waWNzYCBkYXRhc2V0IGhhcyBpdHMgb3duIGNlbGwuIEF0IGZpcnN0IGdsYW5jZSB0aGVyZSBhcHBlYXJzIHRvIGJlIGEgYnJlYWNoIG9mIHRoaXMgcHJpbmNpcGxlIGluIHRoZSB2YXJpYWJsZSBgR2FtZXNgIGFzIGl0IGNvbnRhaW5zIGEgYFllYXJgIGFuZCBgU2Vhc29uYCAoYm90aCBvZiB3aGljaCBhcmUgc2VwYXJhdGUgdmFyaWFibGVzKSwgaG93ZXZlciB0aGUgZGVzY3JpcHRpb24gb2YgdGhlIGRhdGFzZXQgb24gS2FnZ2xlLmNvbSBzdGF0ZXMgdGhhdCAqIldpbnRlciBhbmQgU3VtbWVyIEdhbWVzIHdlcmUgaGVsZCBpbiB0aGUgc2FtZSB5ZWFyIHVwIHVudGlsIDE5OTIuIEFmdGVyIHRoYXQsIHRoZXkgc3RhZ2dlcmVkIHRoZW0gc3VjaCB0aGF0IFdpbnRlciBHYW1lcyBvY2N1ciBvbiBhIGZvdXIgeWVhciBjeWNsZSBzdGFydGluZyB3aXRoIDE5OTQsIHRoZW4gU3VtbWVyIGluIDE5OTYsIHRoZW4gV2ludGVyIGluIDE5OTgsIGFuZCBzbyBvbi4iKiwgc28gZm9yIHRoaXMgcHVycG9zZSB0aGUgYEdhbWVzYCB2YXJpYWJsZSBpcyBpbXBvcnRhbnQgYXMgaXRzIG93biBhdHRyaWJ1dGUuIDxicj48YnI+DQoNCkZyb20gZXhhbWluaW5nIGVhY2ggb2YgdGhlIHRpZHkgZGF0YSBwcmluY2lwbGVzLCBpdCBjYW4gYmUgY29uY2x1ZGVkIHRoYXQgdGhlIGBvbHltcGljc2AgZGF0YXNldCBpcyB0aWR5IGFuZCByZXNoYXBpbmcgaXMgbm90IHJlcXVpcmVkLg0KDQojIwlUaWR5ICYgTWFuaXB1bGF0ZSBEYXRhIElJIA0KDQpTaW5jZSB0aGUgYG9seW1waWNzYCBkYXRhc2V0IGNvbnRhaW5zIG11bHRpcGxlIGJpb2xvZ2ljYWwgYXR0cmlidXRlcywgaXQgaXMgcG9zc2libGUgdG8gY2FsY3VsYXRlIGVhY2ggYXRobGV0ZSdzIEJvZHkgTWFzcyBJbmRleCAob3IgQk1JKS4gQ2FsY3VsYXRpbmcgQk1JIGFzIGEgYm9keS1zdHJ1Y3R1cmUgaW5kaWNhdG9yIGNhbiBwcm92aWRlIGhlbHBmdWwgaW5zaWdodCB0byB0aGUgZGlmZmVyZW50IHNpemVzIG9mIGF0aGxldGVzIGJldHdlZW4gYFNwb3J0c2AsIGBBZ2VgLCBgR2VuZGVyYCwgZXRjLiA8YnI+PGJyPiANCg0KQk1JIGlzIGNhbGN1bGF0ZWQgYXMgKCRCTUkgPSBcZnJhY3trZ317bV4yfSQpIG9yIEJNSSA9IFdlaWdodCAoaW4ga2cpIC8gSGVpZ2h0IChpbiBtKV4yLiBUaGlzIGNhbGN1bGF0aW9uIGlzIHBlcmZvcm1lZCB3aXRoaW4gdGhlIGBtdXRhdGVgIGZ1bmN0aW9uIG9mIHRoZSBgZHBseXJgIHBhY2thZ2UuIEEgbmV3IHZhcmlhYmxlIGBCTUlgIGlzIGNyZWF0ZWQgYnkgZGl2aWRpbmcgdGhlIGV4aXN0aW5nIHZhcmlhYmxlcyBgV2VpZ2h0YCBieSBgSGVpZ2h0YCBieSBgSGVpZ2h0YCAodG8gaW5jb3Jwb3JhdGUgaGVpZ2h0XjIpIGFuZCBtdWx0aXBseWluZyB0aGUgcmVzdWx0IGJ5IDEwMDAwIHRvIGZhY3RvciBpbiB0aGUgaW5jb3JyZWN0IG1lYXN1cmVtZW50IHR5cGUgZm9yIGBIZWlnaHRgIChgSGVpZ2h0YCBpcyByZWNvcmRlZCBpbiBjbSBpbiB0aGUgZGF0YXNldCwgQk1JIGNhbGN1bGF0aW9ucyByZXF1aXJlIG1ldGVycykuIEEgc2FtcGxlIG9mIHRoZSByZXN1bHRpbmcgZGF0YXNldCBzaG93cyB0aGUgY2FsY3VsYXRlZCBgQk1JYHMgZm9yIGNlcnRhaW4gYXRobGV0ZXMuDQoNCmBgYHtyfQ0KIyBCTUkgPSBrZy9tMiANCm9seW1waWNzX211dGF0ZSA8LSBvbHltcGljc19zYW1wbGUgJT4lIG11dGF0ZSgNCiAgQk1JID0gcm91bmQoKFdlaWdodCAvIEhlaWdodCAvIEhlaWdodCkgKiAxMDAwMCwgMikgDQopDQoNCm9seW1waWNzX211dGF0ZVtjKDE6NSksIF0gJT4lIHNlbGVjdChOYW1lLCBTZXgsIEFnZSwgSGVpZ2h0LCBXZWlnaHQsIEJNSSkNCmBgYA0KDQoNCiMjCVNjYW4gSSANCg0KVG8gZXhwbG9yZSAoYW5kIHBvdGVudGlhbGx5IGRlYWwgd2l0aCkgaW5jb25zaXN0ZW5jaWVzIGFuZCBtaXNzaW5nIHZhbHVlcywgZmlyc3QgdGhlaXIgcHJlc2VuY2Ugb3IgYWJzZW5jZSBuZWVkcyB0byBiZSBkZXRlcm1pbmVkLiBTdGFydGluZyB3aXRoIGBOQWAgdmFsdWVzOyBiYXNlIFIncyBgc3VtKClgIGFuZCBgaXMubmEoKWAgZnVuY3Rpb25zIGFyZSBhcHBsaWVkIHRvIHRoZSBgb2x5bXBpY3NfbXV0YXRlYCBkYXRhZnJhbWUgdG8gZGV0ZXJtaW5lIGEgdG90YWwgY291bnQgb2YgYWxsIGBOQWAgaW5zdGFuY2VzLg0KDQojIyMgTWlzc2luZyBWYWx1ZXMNCg0KYGBge3J9DQojIFRvdGFsIG1pc3NpbmcgdmFsdWVzDQpzdW0oaXMubmEob2x5bXBpY3NfbXV0YXRlKSkNCmBgYA0KDQpUaGVyZSBhcmUgYSB0b3RhbCBvZiAxMiw3MjYgbWlzc2luZyB2YWx1ZXMgaW4gdGhlIGBvbHltcGljc19tdXRhdGVgIGRhdGFmcmFtZSwgd2hpY2ggaXMgdG9vIG1hbnkgdG8gYmxpbmRseSByZW1vdmUgb3IgaWdub3JlIGFzIGl0IHdvdWxkIHBvdGVudGlhbGx5IGRpc3RvcnQgdGhlIHVuZGVybHlpbmcgbWVzc2FnZSBvZiB0aGUgZGF0YS4gQ2FsbGluZyB0aGUgYGNvbFN1bXMoKWAgZnVuY3Rpb24gb24gdGhlIGRhdGFmcmFtZSB3aWxsIGRpc3BsYXkgd2hpY2ggY29sdW1ucyBjb250YWluIHRoZXNlIGBOQWAgaW5zdGFuY2VzLg0KDQpgYGB7cn0NCiMgTWlzc2luZyB2YWx1ZXMgYnkgY29sdW1uDQpjb2xTdW1zKGlzLm5hKG9seW1waWNzX211dGF0ZSkpDQpgYGANCg0KVGhlIGBOQWAgdmFsdWVzIGFyZSBzcHJlYWQgYW1vbmdzdCB0aGUgdmFyaWFibGVzIGBBZ2VgLCBgSGVpZ2h0YCwgYFdlaWdodGAsIGBNZWRhbGAsIGByZWdpb25gLCBgbm90ZXNgLCBhbmQgdGhlIG11dGF0ZWQgZmVhdHVyZSBgQk1JYC4gVGhlIG51bWVyaWMgdmFyaWFibGVzIGNhbiBiZSBpbXB1dGVkIGJ1dCBmaXJzdCB0aGUgY2F0ZWdvcmljYWwgdmFyaWFibGVzIHdpbGwgYmUgYWRkcmVzc2VkLiA8YnI+PGJyPg0KDQpGaXJzdCwgdGhlIG5hdHVyZSBvZiB0aGUgYE1lZGFsYCB2YXJpYWJsZSBzdWdnZXN0cyB0aGF0IGEgbWlzc2luZyB2YWx1ZSBpbmRpY2F0ZXMgdGhhdCB0aGUgYXRobGV0ZSBkaWQgbm90IHdpbiBhIG1lZGFsIGZvciB0aGF0IHBhcnRpY3VsYXIgYEV2ZW50YCwgYFllYXJgLCBhbmQgYEdhbWVzYCBjb21iaW5hdGlvbi4gVGhlc2UgYE5BYHMgd2lsbCBiZSByZXBsYWNlZCBieSBhIG5ldyBmYWN0b3IgbGV2ZWwgY2FsbGVkIGBOb25lYC4gVG8gYWNjb21wbGlzaCB0aGlzLCB0aGUgZXhpc3RpbmcgYGxldmVsc2Agb2YgYE1lZGFsc2AgYXJlIGFzc2lnbmVkIHRvIGEgY2hhcmFjdGVyIHZlY3RvciBgbWVkYWxfbGV2ZWxzYCwgdGhlIGBsZW5ndGhgIG9mIHRoZSB2ZWN0b3IgaXMgaW5jcmVhc2VkIGJ5IDEgYW5kIHRoZSBlbXB0eSB2YWx1ZSBpcyByZXBsYWNlZCBieSB0aGUgc3RyaW5nIGAiTm9uZSJgLiBTaW1pbGFyIHRvIHRoZSBvcGVyYXRpb25zIHBlcmZvcm1lZCBpbiBfXzUuIFVuZGVyc3RhbmRfXywgdGhlIGBNZWRhbGAgdmFyaWFibGUgaXMgY29udmVydGVkIHRvIGFuIG9yZGVyZWQgZmFjdG9yIHVzaW5nIHRoZSBuZXcgYG1lZGFsX2xldmVsc2AuIFVzaW5nIHRoZSBgaXMubmEoKWAgZnVuY3Rpb24sIHRoZSBgTkFgcyBhcmUgcmVwbGFjZWQgd2l0aCB0aGUgbmV3IGZhY3RvciBsZXZlbCBgTm9uZWAuIFRoaXMgcHJvY2VzcyBwcmVzZXJ2ZXMgdGhlIG9yZGVyIG9mIHRoZSBgTWVkYWxgIHZhcmlhYmxlIHNvIHRoYXQgR29sZCA+IFNpbHZlciA+IEJyb256ZSA+IE5vbmUuDQoNCmBgYHtyfQ0Kb2x5bXBpY3NfY29tcGxldGUgPC0gb2x5bXBpY3NfbXV0YXRlDQoNCm1lZGFsX2xldmVscyA8LSBsZXZlbHMob2x5bXBpY3NfY29tcGxldGUkTWVkYWwpDQptZWRhbF9sZXZlbHNbbGVuZ3RoKG1lZGFsX2xldmVscykgKyAxXSA8LSAiTm9uZSINCg0Kb2x5bXBpY3NfY29tcGxldGUkTWVkYWwgPC0gZmFjdG9yKG9seW1waWNzX2NvbXBsZXRlJE1lZGFsLCBsZXZlbHMgPSBtZWRhbF9sZXZlbHMsIG9yZGVyID0gVCkNCm9seW1waWNzX2NvbXBsZXRlJE1lZGFsW2lzLm5hKG9seW1waWNzX2NvbXBsZXRlJE1lZGFsKV0gPC0gIk5vbmUiDQpgYGANCg0KTWVyZ2luZyBgYXRobGV0ZXNgIGFuZCBgcmVnaW9uc2AgdG8gZm9ybSB0aGUgaW5pdGlhbCBgb2x5bXBpY3NgIGRhdGFmcmFtZSBpbnZvbHZlZCBwZXJmb3JtaW5nIGEgYGxlZnRfam9pbigpYCB3aGljaCBpcyBwcm9uZSB0byBpbnRyb2R1Y2luZyBgTkFgcyBzaW5jZSBlYWNoIHJvdyBpbiB0aGUgbGVmdCBkYXRhZnJhbWUgKGBhdGhsZXRlc2ApIGlzIGtlcHQgYW5kIGEgbGFjayBvZiBjb3JyZXNwb25kaW5nIHJvd3MgaW4gdGhlIHJpZ2h0IHRhYmxlIChgcmVnaW9uc2ApIHJlc3VsdHMgaW4gYW4gYE5BYC4gVGhlIGByZWdpb25gIGFuZCBgbm90ZXNgIGZlYXR1cmVzIGNvbnRhaW4gbnVtZXJvdXMgYE5BYHMsIHNvbWUgb2Ygd2hpY2ggYXJlIGEgcmVzdWx0IG9mIHRoZSBqb2luIGFuZCB0aGUgcmVtYWluaW5nIGV4aXN0ZWQgaW4gdGhlIG9yaWdpbmFsIHRhYmxlLiBBIHF1aWNrIGludmVzdGlnYXRpb24gb2YgdGhlIHJvd3Mgd2hvc2UgYHJlZ2lvbmAgaXMgYE5BYCBzaG93cyB0aGUgZm9sbG93aW5nOg0KDQpgYGB7cn0NCmhlYWQob2x5bXBpY3NfY29tcGxldGVbaXMubmEob2x5bXBpY3NfY29tcGxldGUkcmVnaW9uKSwgIlRlYW0iXSwgNSkNCmBgYA0KDQpUaGUgZGF0YSBkZXNjcmlwdGlvbiBzdGF0ZXMgdGhhdCB0aGUgYHJlZ2lvbmAgdmFyaWFibGUgaXMgdGhlIGNvdW50cnkgb3IgcmVnaW9uIGFzc29zY2lhdGVkIHdpdGggdGhlIGNvcnJlc3BvbmRpbmcgYE5PQ2AgY29kZS4gSW4gdGhlIGFib3ZlIHNhbXBsZSwgYWxsIG9mIHRoZSBgVGVhbWBzIGNvbnRhaW4gdGhlIHNhbWUgdmFsdWU7IGBTaW5nYXBvcmVgLiANCg0KYGBge3J9DQp1bmlxdWUob2x5bXBpY3NfY29tcGxldGVbaXMubmEob2x5bXBpY3NfY29tcGxldGUkcmVnaW9uKSwgIlRlYW0iXSkNCmBgYA0KDQoNCmBgYHtyfQ0Kd2hpY2goIlNpbmdhcG9yZSIgJWluJSBvbHltcGljc19jb21wbGV0ZSRyZWdpb24pICAgICAgICAgICAgICANCmBgYA0KT3V0IG9mIHRoZSAxNCBgTkFgIGluc3RhbmNlcyBpbiBgcmVnaW9uc2AsIHRoZXJlIGlzIG9ubHkgMSB1bmlxdWUgYFRlYW1gIHZhbHVlLCBhbmQgaXQgYXBwZWFycyB0aGF0IGBTaW5nYXBvcmVgIG5ldmVyIHNob3dzIHVwIGluIHRoZSBgcmVnaW9uYCBjb2x1bW4gZGVzcGl0ZSBzaG93aW5nIHVwIGluIGBUZWFtYC4gQnkgdmFyaWFibGUgZGVmaW5pdGlvbnMsIGBUZWFtYCBhbmQgYHJlZ2lvbmAgYXJlbid0IGFsd2F5cyB0aGUgc2FtZSwgaG93ZXZlciBkdWUgdG8gdGhlIHNtYWxsIG51bWJlciBvZiBgTkFgcywgdGhlIGBUZWFtYCB2YWx1ZSB3aWxsIGJlIGNvcGllZCB0byBgcmVnaW9uYCBmb3IgdGhlc2UgcHJvYmxlbWF0aWMgcm93cyB2aWEgYW4gYGlmZWxzZSgpYCBzdGF0ZW1lbnQuIEFzIGZvciBgbm90ZXNgLCBhIHNpbWlsYXIgYXBwcm9hY2ggdG8gdGhlIHNvbHV0aW9uIGZvciBgTWVkYWxgIHdpbGwgYmUgYXBwbGllZCwgbWludXMgdGhlIGZhY3RvciBvcmRlcmluZyB3aGljaCB3aWxsIHJlcGxhY2UgdGhlIGBOQWAgdmFsdWVzIHdpdGggYCJOb25lImAuDQoNCg0KYGBge3J9DQojIG5vdGVzDQpub3Rlc19sZXZlbHMgPC0gbGV2ZWxzKG9seW1waWNzX2NvbXBsZXRlJG5vdGVzKQ0Kbm90ZXNfbGV2ZWxzW2xlbmd0aChub3Rlc19sZXZlbHMpICsgMV0gPC0gIk5vbmUiDQoNCm9seW1waWNzX2NvbXBsZXRlJG5vdGVzIDwtIGZhY3RvcihvbHltcGljc19jb21wbGV0ZSRub3RlcywgbGV2ZWxzID0gbWVkYWxfbGV2ZWxzKQ0Kb2x5bXBpY3NfY29tcGxldGUkbm90ZXNbaXMubmEob2x5bXBpY3NfY29tcGxldGUkbm90ZXMpXSA8LSAiTm9uZSINCg0KIyByZWdpb24NCm9seW1waWNzX2NvbXBsZXRlJHJlZ2lvbiA8LSBpZmVsc2UoaXMubmEob2x5bXBpY3NfY29tcGxldGUkcmVnaW9uKSwgb2x5bXBpY3NfY29tcGxldGUkVGVhbSwgb2x5bXBpY3NfY29tcGxldGUkcmVnaW9uKSANCmBgYA0KDQpUaGUgcmVtYWluaW5nIHZhcmlhYmxlcyB0aGF0IGNvbnRhaW4gYE5BYCBpbnN0YW5jZXMgYXJlIGBBZ2VgLCBgSGVpZ2h0YCwgYFdlaWdodGAsIGFuZCBgQk1JYC4gQmVpbmcgbnVtZXJpYyB2YXJpYWJsZXMsIGluY29ycmVjdGx5IGNoYW5naW5nIG9yIGltcHV0aW5nIHRoZXNlIGBOQWBzIGNhbiBwb3RlbnRpYWxseSBkaXN0b3J0IHRoZSBkYXRhIGFuZCByZXN1bHQgaW4gaW52YWxpZCBjb25jbHVzaW9ucy4gU3RhcnRpbmcgd2l0aCBgQWdlYCwgc3VtbWFyeSBzdGF0aXN0aWNzIHdpbGwgYmUgZXhwbG9yZWQgZ3JvdXBlZCBieSBgU3BvcnRgIHRvIHNlZSBpZiB0aGVyZSBpcyBhIGRpZmZlcmVuY2UgaW4gYEFnZWAgYmV0d2VlbiBkaWZmZXJlbnQgc3BvcnRzLiBUaGlzIGV4cGxvcmF0aW9uIHdpbGwgYmUgY29uZHVjdGVkIGJ5IHVzaW5nIHZhcmlvdXMgZnVuY3Rpb25zIGZyb20gdGhlIGBkcGx5cmAgcGFja2FnZS4NCg0KYGBge3J9DQpvbHltcGljc19jb21wbGV0ZSAlPiUgDQogIGdyb3VwX2J5KFNwb3J0KSAlPiUgIA0KICBuYS5vbWl0KCkgJT4lIA0KICBzdW1tYXJpemUobWVhbj1tZWFuKEFnZSksIG1lZGlhbiA9IG1lZGlhbihBZ2UpLCBtaW4gPSBtaW4oQWdlKSwgbWF4ID0gbWF4KEFnZSksIFNEID0gc2QoQWdlKSkgJT4lIA0KICBzYW1wbGVfbig1KQ0KYGBgDQpUaGUgYG1lYW5gLCBgbWVkaWFuYCwgYW5kIHZhcmlhbmNlIGluIGBBZ2VgIGFwcGVhcnMgdG8gZGlmZmVyIGJldHdlZW4gc3BvcnRzIHdoaWNoIG1ha2VzIGxvZ2ljYWwgc2Vuc2UtLSBhbiBPbHltcGljIEd5bW5hc3Qgb3IgU3dpbW1lciB3b3VsZCBsaWtlbHkgYmUgeW91bmdlciB0aGFuIGEgV2VpZ2h0bGlmdGVyIG9yIFNob290ZXIuIFRoZSBgbWVkaWFuYCBgQWdlYCBvZiBlYWNoIGBTcG9ydGAgd2lsbCBiZSBpbXB1dGVkIHRvIHRoZSBtaXNzaW5nIGBBZ2VgIHZhcmlhYmxlcy4NCg0KYGBge3J9DQpvbHltcGljc19jb21wbGV0ZSA8LSBvbHltcGljc19jb21wbGV0ZSAlPiUNCiBncm91cF9ieShTcG9ydCkgJT4lDQogbXV0YXRlKA0KICAgQWdlID0gaWZlbHNlKA0KICAgaXMubmEoQWdlKSwNCiAgIG1lZGlhbihBZ2UsIG5hLnJtID0gVFJVRSksDQogICBBZ2UpDQogKQ0KYGBgDQoNCkEgc2ltaWxhciBhcHByb2FjaCB3aWxsIGJlIGFwcGxpZWQgdG8gYEhlaWdodGAgYW5kIGBXZWlnaHRgLCB0aGlzIHRpbWUgdXNpbmcgdGhlIG1lZGlhbiBvZiB0aGUgY29tYmluYXRpb24gb2YgYEFnZWAgYW5kIGBTcG9ydGAuIFRoZSBsb2dpYyBoZXJlIGlzIHRoYXQgYW4gMTEteWVhci1vbGQgR3ltbmFzdCB3b3VsZCBsaWtlbHkgd2VpZ2ggbGVzcyBhbmQgYmUgc2hvcnRlciB0aGFuIGEgNDAteWVhci1vbGQgR3ltbmFzdCwgYW5kIGJlIHNpZ25pZmljYW50bHkgc21hbGxlciB0aGFuIGEgNDAteWVhci1vbGQgV2VpZ2h0bGlmdGVyLg0KDQpgYGB7cn0NCm9seW1waWNzX2NvbXBsZXRlIDwtIG9seW1waWNzX2NvbXBsZXRlICU+JQ0KIG5hLm9taXQoKSAlPiUgDQogZ3JvdXBfYnkoU3BvcnQsIEFnZSkgJT4lDQogbXV0YXRlKA0KICAgSGVpZ2h0ID0gaWZlbHNlKA0KICAgaXMubmEoSGVpZ2h0KSwNCiAgIG1lZGlhbihIZWlnaHQsIG5hLnJtID0gVFJVRSksDQogICBIZWlnaHQpDQogKQ0KDQpvbHltcGljc19jb21wbGV0ZSA8LSBvbHltcGljc19jb21wbGV0ZSAlPiUNCiBuYS5vbWl0KCkgJT4lIA0KIGdyb3VwX2J5KFNwb3J0LCBBZ2UpICU+JQ0KIG11dGF0ZSgNCiAgIFdlaWdodCA9IGlmZWxzZSgNCiAgIGlzLm5hKFdlaWdodCksDQogICBtZWRpYW4oV2VpZ2h0LCBuYS5ybSA9IFRSVUUpLA0KICAgV2VpZ2h0KQ0KICkNCg0KYGBgDQoNCmBCTUlgIGlzIHRoZSBmaW5hbCB2YXJpYWJsZSB0aGF0IGNvbnRhaW5zIGBOQWBzLCB3aGljaCBpcyBhIHJlc3VsdCBvZiBwZXJmb3JtaW5nIGNhbGN1bGF0aW9ucyBvbiB2YXJpYWJsZXMgd2l0aCBtaXNzaW5nIHZhbHVlcywgYEhlaWdodGAgYW5kIGBXZWlnaHRgLiBTaW5jZSB0aGVzZSBgTkFgcyBoYXZlIGJlZW4gaW1wdXRlZCwgdGhlIG5vdyBjb21wbGV0ZSBjb2x1bW5zIGNhbiBiZSB1c2VkIHRvIG9uY2UgYWdhaW4gY2FsY3VsYXRlIGBCTUlgLg0KDQpgYGB7cn0NCm9seW1waWNzX2NvbXBsZXRlIDwtIG9seW1waWNzX2NvbXBsZXRlICU+JQ0KIG11dGF0ZSgNCiAgIEJNSSA9IGlmZWxzZSgNCiAgIGlzLm5hKEJNSSksDQogICByb3VuZCgoV2VpZ2h0IC8gSGVpZ2h0IC8gSGVpZ2h0KSAqIDEwMDAwLCAyKSwNCiAgIEJNSSkNCiApDQoNCmNvbFN1bXMoaXMubmEob2x5bXBpY3NfY29tcGxldGUpKQ0KYGBgDQoNClRoZSByZXN1bHQgb2YgdGhlIG11bHRpcGxlIGltcHV0YXRpb25zIGFuZCByZW5hbWluZyBwcm9jZWR1cmVzIGlzIGEgbmV3IGBkYXRhLmZyYW1lYCBgb2x5bXBpY3NfY29tcGxldGVgIHdoaWNoIGNvbnRhaW5zIHNvbWUgc29ydCBvZiB2YWx1ZSBmb3IgZXZlcnkgb2JzZXJ2YXRpb24gaW4gdGhlIHdob2xlIGRhdGFzZXQuDQoNCiMjIyBTcGVjaWFsIFZhbHVlcw0KDQpgTkFgIHZhbHVlcyBhcmVuJ3QgdGhlIG9ubHkgcHJvYmxlbWF0aWMgb2JzZXJ2YXRpb25zIGluIGFuIGV4cGxvcmF0b3J5IGRhdGEgYW5hbHlzaXMuIFRvIGV4cGxvcmUgYW55IHBvdGVudGlhbCBTcGVjaWFsIFZhbHVlcywgKGBJbmZgLCBgLUluZmAsIGBOYU5gKSwgYSBjdXN0b20gZnVuY3Rpb24gYGlzX3NwZWNpYWxgIGlzIGRlZmluZWQsIHdoaWNoIHV0aWxpc2VzIGJhc2UgUidzIGZ1bmN0aW9ucyBgaXMuaW5maW5pdGUoKWAsIGBpcy5uYW4oKWAsIGFuZCBgaXMubmEoKWAuIEFwcGx5aW5nIHRoaXMgY3VzdG9tIGZ1bmN0aW9uIHRvIHRoZSBgb2x5bXBpY3NfY29tcGxldGVgIGBkYXRhLmZyYW1lYCB3aWxsIGRldGVybWluZSB0aGUgbnVtYmVyIG9mIHRoZXNlIHNwZWNpYWwgdmFsdWVzIHByZXNlbnQuDQoNCmBgYHtyfQ0KaXNfc3BlY2lhbCA8LSBmdW5jdGlvbih4KXsNCiBpZiAoaXMubnVtZXJpYyh4KSkgKGlzLmluZmluaXRlKHgpIHwgaXMubmFuKHgpIHwgaXMubmEoeCkpDQp9DQpzdW0oaXNfc3BlY2lhbChvbHltcGljc19jb21wbGV0ZSkpDQpgYGANCg0KVGhlIGBpc19zcGVjaWFsKClgIGZ1bmN0aW9uIGhhcyByZXR1cm5lZCAwLCBpbmRpY2F0aW5nIHRoYXQgdGhlcmUgYXJlIG5vIHNwZWNpYWwgdmFsdWVzIGluIHRoZSBgb2x5bXBpY3NfY29tcGxldGVgIGBkYXRhLmZyYW1lYCB0aGF0IHJlcXVpcmUgZnVydGhlciBpbnZlc3RpZ2F0aW9uLiANCg0KIyMjIEluY29uc2lzdGVuY2llcyBhbmQgRXJyb3JzDQoNCkFub3RoZXIgdHlwZSBvZiBlcnJvciB0byBjaGVjayBmb3IgaXMgaW5jb25zaXN0ZW5jaWVzIGFuZCBlcnJvcnMsIG9yIGRhdGEgdmFsdWVzIHRoYXQgYXJlIG5vbnNlbnNpY2FsICBpbiBjb250ZXh0LiBGb3IgZXhhbXBsZSwgbmVnYXRpdmUgdmFsdWVzIGZvciBgQWdlYCwgYEhlaWdodGAsIG9yIGBXZWlnaHRgLCBvciBleGNlZWRpbmdseSBsYXJnZSB2YWx1ZXMgZm9yIHRob3NlIHNhbWUgdmFyaWFibGVzIChzdWNoIGFzIGBBZ2VgIGJlaW5nIG92ZXIgMjAwKS4gVXNpbmcgdGhlIGBlZGl0c2V0YCBmdW5jdGlvbiBmcm9tIHRoZSBgZWRpdHJ1bGVzYCBwYWNrYWdlLCBhIHNldCBvZiBydWxlcyBpcyBkZWZpbmVkIHRvIGlkZW50aWZ5IGFueSBvZiB0aGVzZSBvYnZpb3VzIGVycm9ycy4gVGhlIHJ1bGUgc2V0IGNoZWNrcyB0aGF0OiANCg0KKiBgQWdlYCBpcyBiZXR3ZWVuIGAwYCBhbmQgYDEwMGAsIA0KKiBgSGVpZ2h0YCBpcyBiZXR3ZWVuIGAwYCBhbmQgYDI1MGAsIA0KKiBgV2VpZ2h0YCBpcyBiZXR3ZWVuIGAwYCBhbmQgYDMwMGAsIA0KKiBgU2Vhc29uYCBpcyBvbmUgb2YgZWl0aGVyIGBTdW1tZXJgIG9yIGBXaW50ZXJgLA0KKiBhbmQgYE1lZGFsYCBpcyBvbmUgb2YgZWl0aGVyIGBHb2xkYCwgYFNpbHZlcmAsIGBCcm9uemVgIG9yIGBOb25lYA0KDQpTb21lIG9mIHRoZSBudW1lcmljYWwgYm91bmRhcmllcyBhcmUgZXh0cmVtZSwgKGkuZS4gYW4gT2x5bXBpYyBhdGhsZXRlIGJlaW5nIDAgb3IgMTAwIHllYXJzIG9sZCBpcyB2ZXJ5IHVubGlrZWx5KSwgaG93ZXZlciB0aGUgcHVycG9zZSBvZiB0aGVzZSBmdW5jdGlvbnMgaXMgdG8gbG9jYXRlIGluY29uc2lzdGVuY2llcyBhbmQgZXJyb3JzLiBPdXRsaWVycyB3aWxsIGJlIGV4cGxvcmVkIGxhdGVyIGluIHRoaXMgc3R1ZHkuIFNvbWUgdmFyaWFibGVzIGhhdmUgdG9vIG1hbnkgZmFjdG9yIGxldmVscyB0byBjaGVjayBmb3IgKGBUZWFtYCBhbmQgYEV2ZW50YCBmb3IgZXhhbXBsZSkgc28gdGhpcyBydWxlc2V0IGlzIGxpbWl0ZWQgdG8gbnVtZXJpY2FsIHZhcmlhYmxlcywgYW5kIGNhdGVnb3JpY2FsIHZhcmlhYmxlcyB0aGF0IGNvbnRhaW4gbGVzcyB0aGFuIDUgdW5pcXVlIGxldmVscy4NCmBgYHtyfQ0KKFJ1bGUxIDwtIGVkaXRzZXQoYygiQWdlID49IDAiLCAiQWdlIDw9IDEwMCIsICJIZWlnaHQgPj0gMCIsIA0KICAgICAgICAgICAgICAgICAgICAiSGVpZ2h0IDw9IDI1MCIsICJXZWlnaHQgPj0gMCIsICJXZWlnaHQgPD0gMzAwIiwNCiAgICAgICAgICAgICAgICAgICAgIlNlYXNvbiAlaW4lIGMoJ1N1bW1lcicsICdXaW50ZXInKSIsDQogICAgICAgICAgICAgICAgICAgICJNZWRhbCAlaW4lIGMoJ0dvbGQnLCAnU2lsdmVyJywgJ0Jyb256ZScsICdOb25lJykiKSkpDQpzdW0odmlvbGF0ZWRFZGl0cyhSdWxlMSwgb2x5bXBpY3NfY29tcGxldGUpKQ0KYGBgDQoNClVzaW5nIGEgY29tYmluYXRpb24gb2YgYmFzZSBSJ3MgYHN1bSgpYCBhbmQgYGVkaXRydWxlc2AnIGB2aW9sYXRlZEVkaXRzYCBzaG93cyB0aGF0IHRoZXJlIGFyZSBubyBpbnN0YW5jZXMgaW4gdGhlIGBvbHltcGljc19jb21wbGV0ZWAgZGF0YXNldCB0aGF0IHZpb2xhdGUgdGhlIGRlZmluZWQgcnVsZXMuDQoNCiMjCVNjYW4gSUkNCg0KIyMjIE91dGxpZXJzDQoNCk1pc3Npbmcgb3IgaW5jb25zaXN0ZW50IGRhdGEgaXMgb25lIGNvbW1vbiBpc3N1ZSBpbiBleHBsb3JhdG9yeSBkYXRhIGFuYWx5c2lzOyBhbm90aGVyIGlzIG91dGxpZXJzIHdoaWNoIGNhbiBiZSBwcmVzZW50IGFzIGEgcmVzdWx0IG9mIHNvbWUgZm9ybSBvZiBlcnJvci4gQSBxdWljayBtZXRob2Qgb2YgZGV0ZWN0aW5nIHRoZSBwcmVzZW5jZSBvZiBvdXRsaWVycyBpcyB0byBkaXNwbGF5IGJveHBsb3RzIGZvciBlYWNoIG51bWVyaWMgdmFyaWFibGUuDQoNCmBgYHtyLGVjaG89RkFMU0UsIG91dC53aWR0aD0nLjQ5XFxsaW5ld2lkdGgnLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9MyxmaWcuc2hvdz0naG9sZCcsZmlnLmFsaWduPSdjZW50ZXInfQ0KbnVtZXJpY192YXJpYWJsZXMgPC0gYygiQWdlIiwgIkhlaWdodCIsICJXZWlnaHQiLCAiQk1JIikNCnBhcihtZnJvdyA9IGMoMSw0KSkNCmZvciAoaSBpbiBudW1lcmljX3ZhcmlhYmxlcykgew0KICBib3hwbG90KG9seW1waWNzX2NvbXBsZXRlWywgaV0sDQogICAgICAgICAgbWFpbiA9IHBhc3RlKCJCb3hwbG90IG9mIiwgaSkpDQp9DQpgYGANCkJveHBsb3RzIHNob3cgdGhhdCB0aGVyZSBhcmUgb3V0bGllcnMgcHJlc2VudCBpbiBhbGwgb2YgdGhlIG51bWVyaWMgZmVhdHVyZXMgKGFzIHJlcHJlc2VudGVkIGJ5IHRoZSBkb3RzKSBhY2NvcmRpbmcgdG8gVHVrZXnigJlzIG1ldGhvZCBvZiBvdXRsaWVyIGRldGVjdGlvbiAoJFxwbTEuNSDDlyBJUVIkKS4gU2hhcGVzIG9mIHRoZSBib3ggcGxvdHMgZ2l2ZSBhIGdlbmVyYWwgaWRlYSBvZiBlYWNoIHZhcmlhYmxlJ3MgdW5pdmFyaWF0ZSBkaXN0cmlidXRpb24uIGBIZWlnaHRgIGlzIHRoZSBvbmx5IHZhcmlhYmxlIHRoYXQgYXBwZWFycyB0byByZXNlbWJsZSBub3JtYWxpdHksIHdoaWNoIHN1Z2dlc3RzIHRoYXQgdGhlIHotc2NvcmUgYXBwcm9hY2ggY2FuIGJlIHVzZWQgdG8gZGV0ZWN0IG91dGxpZXJzIGZvciB0aGVzZSBmZWF0dXJlcy4gSG93ZXZlciBzaW5jZSB0aGUgY29tYmluYXRpb24gb2YgZmVhdHVyZXMgYEhlaWdodGAsIGBXZWlnaHRgLCBhbmQgYEJNSWAgYXJlIG5vdCBsaWtlbHkgdG8gYmUgaW5kZXBlbmRlbnQsIG11bHRpdmFyaWF0ZSBvdXRsaWVyIGRldGVjdGlvbiB0ZWNobmlxdWVzIHdpbGwgYmUgdXNlZC4gYEFnZWAgd2lsbCBiZSBhc3Nlc3NlZCBmb3IgdW5pdmFyaWF0ZSBvdXRsaWVycy4gPGJyPjxicj4NCg0KT3V0bGllcnMgaW4gQWdlIHdlcmUgaGlnaGxpZ2h0ZWQgaW4gdGhlIGluaXRpYWwgYm94cGxvdHMuIEJ5IHNhdmluZyBhIGBib3hwbG90KClgIG9mIHRoZSBgQWdlYCB2YXJpYWJsZSB0byBhbiBvYmplY3QgYW5kIGFjY2Vzc2luZyB0aGUgYCRvdXRgIGF0dHJpYnV0ZSBvZiB0aGUgc3RvcmVkIGJveHBsb3QsIHRoZSBwZXJjZW50YWdlIG9mIG91dGxpZXJzIGFjY29yZGluZyB0byBUdWtleSdzIG1ldGhvZCBjYW4gYmUgY291bnRlZC4NCg0KYGBge3IgZmlnLnNob3c9J2hpZGUnfQ0KYm94cGxvdF9hZ2UgPC0gYm94cGxvdChvbHltcGljc19jb21wbGV0ZSRBZ2UpDQpgYGANCmBgYHtyfQ0Kcm91bmQobGVuZ3RoKGJveHBsb3RfYWdlJG91dCkgLyBucm93KG9seW1waWNzX2NvbXBsZXRlKSAqIDEwMCwgMikNCmBgYA0KDQpTaW5jZSBvbmx5IDIuNTUlIG9mIGluc3RhbmNlcyBpbiBgQWdlYCBhcmUgb3V0bGllcnMsIHRoZXkgY2FuIGxpa2VseSBiZSByZW1vdmVkIHdpdGhvdXQgZGlzdG9ydGluZyB0aGUgdW5kZXJseWluZyBkYXRhLiBBcyBoaWdobGlnaHRlZCBpbiB0aGUgaGlzdG9ncmFtLCBgQWdlYCBpcyBub3Qgbm9ybWFsbHkgZGlzdHJpYnV0ZWQgc28gei1zY29yZXMgc2hvdWxkbid0IGJlIHVzZWQuIEluc3RlYWQsIGNhcHBpbmcgKG9yIFdpbmRzb3JpbmcpIHdpbGwgYmUgaW1wbGVtZW50ZWQgYnkgaW52b2tpbmcgYSB1c2VyLWRlZmluZWQgZnVuY3Rpb24gYGNhcGAgdGhhdCByZXBsYWNlcyBvdXRsaWVycyB3aXRoIHRoZWlyIG5lYXJlc3QgbmVpZ2hib3VyLg0KDQpgYGB7cn0NCmNhcCA8LSBmdW5jdGlvbih4KXsNCiAgICBxdWFudGlsZXMgPC0gcXVhbnRpbGUoIHgsIGMoLjA1LCAwLjI1LCAwLjc1LCAuOTUgKSApDQogICAgeFsgeCA8IHF1YW50aWxlc1syXSAtIDEuNSpJUVIoeCkgXSA8LSBxdWFudGlsZXNbMV0NCiAgICB4WyB4ID4gcXVhbnRpbGVzWzNdICsgMS41KklRUih4KSBdIDwtIHF1YW50aWxlc1s0XQ0KICAgIHgNCn0NCg0Kb2x5bXBpY3NfY2FwcGVkIDwtIG9seW1waWNzX2NvbXBsZXRlDQpvbHltcGljc19jYXBwZWQkQWdlIDwtIGNhcChvbHltcGljc19jYXBwZWQkQWdlKQ0KYGBgDQoNClRoZSByZW1haW5pbmcgbnVtZXJpYyB2YXJpYWJsZXMgYEhlaWdodGAgYW5kIGBXZWlnaHRgIHdpbGwgYmUgaW52ZXN0aWdhdGVkIHVzaW5nIG11bHRpdmFyaWF0ZSBvdXRsaWVyIGRldGVjdGlvbiBtZXRob2RzLCBnaXZlbiB0aGF0IGl0IGlzIGxpa2VseSB0aGF0IGEgcGVyc29uJ3MgaGVpZ2h0IHdpbGwgaW1wYWN0IHRoZWlyIHdlaWdodC4NCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCnBsb3Qob2x5bXBpY3NfY2FwcGVkJEhlaWdodH5vbHltcGljc19jYXBwZWQkV2VpZ2h0LCBtYWluID0gIkhlaWdodCB2cyBXZWlnaHQiLCB4bGFiID0gIldlaWdodCIsIHlsYWIgPSAiSGVpZ2h0IikNCmBgYA0KTG9naWNhbGx5LCBhIHRhbGxlciBwZXJzb24gd291bGQgbGlrZWx5IHdlaWdoIG1vcmUgdGhhbiBhIHNob3J0ZXIgcGVyc29uLCB3aGljaCBpcyBzdXBwb3J0ZWQgYnkgdGhlIHNjYXR0ZXJwbG90IG9mIGBIZWlnaHRgIHZzIGBXZWlnaHRgLiBUaGVyZSBpcyBhIHN0cm9uZywgcG9zaXRpdmUgY29ycmVsYXRpb24gYmV0d2VlbiB0aGUgdHdvIHZhcmlhYmxlcyB3aXRoIHNvbWUgbGlrZWx5IG91dGxpZXJzIHNwcmVhZCB0b3dhcmQgdGhlIG91dHNpZGUgb2YgdGhlIHBsb3Qgc3BhY2UuIFVzaW5nIHRoZSBgbXZuKClgIGZ1bmN0aW9uIGZyb20gdGhlIGBNVk5gIHBhY2thZ2UgY2FuIGRldGVjdCBvdXRsaWVycyB1c2luZyBNYWhhbGFub2JpcyBkaXN0YW5jZXMuIA0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KbXZuX3Jlc3VsdHMgPC0gbXZuKGRhdGEgPSBvbHltcGljc19jYXBwZWRbLCBjKCJIZWlnaHQiLCAiV2VpZ2h0IildLCBtdWx0aXZhcmlhdGVPdXRsaWVyTWV0aG9kID0gInF1YW4iLCBzaG93T3V0bGllcnMgPSBUUlVFKQ0KYGBgDQoNClVzaW5nIHRoZSBgcXVhbmAgbWV0aG9kIGluIGBtdm4oKWAgdXNlcyB0aGUgY2hpLXNxdWFyZSBkaXN0cmlidXRpb24gY3JpdGljYWwgdmFsdWUgYXBwcm9hY2ggdG8gZGV0ZWN0IG11bHRpdmFyaWF0ZSBvdXRsaWVycy4gVmFsdWVzIHRvIHRoZSByaWdodCBvZiB0aGUgcmVkIF9RdWFudGlsZV8gbGluZSBpbiB0aGUgcGxvdCBhcmUgZGV0ZXJtaW5lZCB0byBiZSBvdXRsaWVycyBmcm9tIHRoaXMgbWV0aG9kLiBBcyBkaXNwbGF5ZWQgb24gdGhlIHZpc3VhbGlzYXRpb24sIHRoZXJlIGFyZSBgbj0yMDBgIG11bHRpdmFyaWF0ZSBgSGVpZ2h0YCBhbmQgYFdlaWdodGAgb3V0bGllcnMuDQoNCmBgYHtyfQ0KbXZuX3Jlc3VsdHMkbXVsdGl2YXJpYXRlT3V0bGllcnMNCmBgYA0KYGBge3J9DQpyb3VuZChucm93KG12bl9yZXN1bHRzJG11bHRpdmFyaWF0ZU91dGxpZXJzKSAvIG5yb3cob2x5bXBpY3NfY2FwcGVkKSAqIDEwMCwgMikNCmBgYA0KNS4yMSUgb2YgdmFsdWVzIGFyZSBtdWx0aXZhcmlhdGUgb3V0bGllcnMgYW1vbmdzdCB0aGUgYEhlaWdodGAgYW5kIGBXZWlnaHRgIHZhcmlhYmxlcywgc28gaXQgaXMgbGlrZWx5IHNhZmUgdG8gcmVtb3ZlIHRoZW0gd2l0aG91dCBkaXN0b3J0aW5nIHRoZSB1bmRlcmx5aW5nIGRhdGEuIFRoaXMgaXMgYWNjb21wbGlzaGVkIGJ5IGRpcmVjdGx5IHN1YnNldHRpbmcgdGhlIHByb2JsZW1hdGljIHJvd3MgZnJvbSB0aGUgYG9seW1waWNzX2NhcHBlZGAgZGF0YWZyYW1lLg0KDQpgYGB7cn0NCm9seW1waWNzX2NsZWFuIDwtIG9seW1waWNzX2NhcHBlZFsgLWMoMTpucm93KG12bl9yZXN1bHRzJG11bHRpdmFyaWF0ZU91dGxpZXJzKSksIF0NCmRpbShvbHltcGljc19jbGVhbikNCmBgYA0KDQpUaGUgcmVtYWluaW5nIG51bWVyaWMgdmFyaWFibGUgd2l0aCBwb3RlbnRpYWwgb3V0bGllcnMgaXMgYEJNSWAsIGhvd2V2ZXIgdGhpcyBpcyBhIHNwZWNpYWwgY2FzZSBzaW5jZSBpdCBpcyBjYWxjdWxhdGVkIGZyb20gdHdvIGV4aXN0aW5nIHZhcmlhYmxlcyBgSGVpZ2h0YCBhbmQgYFdlaWdodGAuIFNpbmNlIHRoZSBvdXRsaWVycyBmb3IgdGhlc2UgdmFyaWFibGVzIGhhdmUgYmVlbiBhZGRyZXNzZWQsIHRoZSByZXN1bHRpbmcgYEJNSWAgdmFsdWVzIGFyZW4ndCBsaWtlbHkgdG8gYmUgY2F1c2VkIGJ5IGFueSBmb3JtIG9mIHVzZXIgaW5wdXQgZXJyb3IuIFNpbmNlIGBCTUlgIGlzIGEgcHJvZHVjdCBvZiB2YXJpYWJsZXMgdGhhdCBoYXZlIGJlZW4gc2NyZWVuZWQgZm9yIG91dGxpZXJzLCB0aGV5IHdpbGwgcmVtYWluIGFzLWlzLg0KDQojIwlUcmFuc2Zvcm0gDQoNClRoZSBkaXN0cmlidXRpb25zIG9mIG51bWVyaWMgZmVhdHVyZXMgd2FzIGJyaWVmbHkgY292ZXJlZCBpbiBfX1NjYW4gSUkgLSBPdXRsaWVyc19fLiBIaXN0b3JncmFtcyB3aWxsIHByb3ZpZGUgbW9yZSBhY2N1cmF0ZSBpbnNpZ2h0Lg0KDQpgYGB7cixlY2hvPUZBTFNFLCBvdXQud2lkdGg9Jy40OVxcbGluZXdpZHRoJywgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTMsZmlnLnNob3c9J2hvbGQnLGZpZy5hbGlnbj0nY2VudGVyJ30NCnBhcihtZnJvdyA9IGMoMSw1KSkNCmZvciAoaSBpbiBudW1lcmljX3ZhcmlhYmxlcykgew0KICAgeCA8LSBhcy5udW1lcmljKHVubGlzdChvbHltcGljc19jbGVhblssIGldKSkNCiAgIGhpc3QoeCwgbWFpbiA9IHBhc3RlKCJIaXN0b2dyYW0gb2YiLCBpKSwgeGxhYiA9IHBhc3RlKGkpLCB5bGFiID0gIkNvdW50IikNCn0NCmBgYA0KDQpBcyBpbml0aWFsbHkgc3VzcGVjdGVkLCBgSGVpZ2h0YCBhcHBlYXJzIG5vcm1hbGx5IGRpc3RyaWJ1dGVkIGFuZCB3aWxsIHJlcXVpcmUgbm8gdHJhbnNmb3JtYXRpb25zLiBQb3N0LW91dGxpZXIgcmVtb3ZhbCwgYEFnZWAgYWxzbyBhcHBlYXJzIHRvIGFwcHJveGltYXRlbHkgcmVzZW1ibGUgbm9ybWFsaXR5LCBsZWF2aW5nIGBXZWlnaHRgIGFuZCBgQk1JYCB0aGF0IGFyZSBib3RoIGxlZnQgc2tld2VkLCB3aXRoIGBCTUlgIGNvbnRhaW5pbmcgYSB2ZXJ5IG5hcnJvdyBwZWFrLiBNYXRoZW1hdGljYWwgb3BlcmF0aW9ucyBmb3IgYFdlaWdodGAgYW5kIGBCTUlgIHdpbGwgYmUgZXhwbG9yZWQgaW4gYXR0ZW1wdCB0byBtYWtlIHRoZW0gbW9yZSBjbG9zZWx5IHJlc2VtYmxlIHRoZSBub3JtYWwgZGlzdHJpYnV0aW9uLg0KDQpgYGB7cn0NCnNxcnRfd2VpZ2h0IDwtIHNxcnQob2x5bXBpY3NfY2xlYW4kV2VpZ2h0KQ0KaGlzdChzcXJ0X3dlaWdodCwgbWFpbiA9ICJIaXN0b2dyYW0gb2Ygc3FydChXZWlnaHQpIikNCmBgYA0KVGhlIHNxdWFyZSByb290IG9wZXJhdGlvbiBgc3FydCgpYCBoYXMgdHJhbnNmb3JtZWQgdGhlIGBXZWlnaHRgIGZlYXR1cmUgdG8gbW9yZSBjbG9zZWx5IHJlc2VtYmxlIG5vcm1hbGl0eS4gDQoNCmBgYHtyfQ0KYm1pX3JlY2lwIDwtIDEvKG9seW1waWNzX2NsZWFuJEJNSSkNCg0KaGlzdChibWlfcmVjaXApDQpgYGANCkFwcGx5aW5nIHRoZSByZWNpcHJvY2FsIGZ1bmN0aW9uIHRvIGBCTUlgIHNob3dzIHRoYXQgdGhlIHNoYXBlIGlzIGNsb3NlciB0byBub3JtYWwuDQoNCmBgYHtyLGVjaG89RkFMU0UsIG91dC53aWR0aD0nLjQ5XFxsaW5ld2lkdGgnLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9MyxmaWcuc2hvdz0naG9sZCcsZmlnLmFsaWduPSdjZW50ZXInfQ0Kb2x5bXBpY3NfZmluYWwgPC0gb2x5bXBpY3NfY2xlYW4NCm9seW1waWNzX2ZpbmFsJFdlaWdodCA8LSBzcXJ0KG9seW1waWNzX2ZpbmFsJFdlaWdodCkNCm9seW1waWNzX2ZpbmFsJEJNSSA8LSAxLyhvbHltcGljc19maW5hbCRCTUkpDQoNCnBhcihtZnJvdyA9IGMoMSw1KSkNCmZvciAoaSBpbiBudW1lcmljX3ZhcmlhYmxlcykgew0KICAgeCA8LSBhcy5udW1lcmljKHVubGlzdChvbHltcGljc19maW5hbFssIGldKSkNCiAgIGhpc3QoeCwgbWFpbiA9IHBhc3RlKCJIaXN0b2dyYW0gb2YiLCBpKSwgeGxhYiA9IHBhc3RlKGkpLCB5bGFiID0gIkNvdW50IikNCn0NCmBgYA0KQWZ0ZXIgY29udmVydGluZyB0aGUgcHJldmlvdXNseSBleHBsb3JlZCB2YXJpYWJsZXMgdXNpbmcgYHNxcnQoKWAgZm9yIGBXZWlnaHRgIGFuZCB0aGUgcmVjaXByb2NhbCBvZiBgQk1JYCwgZWFjaCBvZiB0aGUgbnVtZXJpYyBmZWF0dXJlcyBub3cgYXBwcm94aW1hdGVseSByZXNlbWJsZXMgbm9ybWFsaXR5IHdoaWNoIGlzIGFuIGFzc3VtcHRpb24gYW5kIHNvbWV0aW1lcyBhIHJlcXVpcmVtZW50IGZvciBtYW55IHN0YXRpc3RpY2FsIHRlc3RzLg0KDQpBZnRlciBpbnZlc3RpZ2F0aW5nIHRoZSBFeHBsb3JpbmcgMTIwIFllYXJzIG9mIE9seW1waWMgSGlzdG9yeSBkYXRhc2V0LCBjb252ZXJ0aW5nIHRoZSBuZWNlc3NhcnkgZGF0YSB0eXBlcywgaGFuZGxpbmcgb3V0bGllcnMgYW5kIG1pc3NpbmcgdmFsdWVzLCBhbmQgdHJhbnNmb3JtaW5nIG51bWVyaWMgZmVhdHVyZXMgdG8gbm9ybWFsLCB0aGUgZGF0YSBpcyBub3cgaW4gYSBjbGVhbiAmIGNvbXBsZXRlIGZvcm0gdGhhdCBpcyBzdWl0YWJsZSBmb3Igc3RhdGlzdGljYWwgYW5hbHlzaXMgYW5kIHBvdGVudGlhbGx5IG1hY2hpbmUgbGVhcm5pbmcgYXBwbGljYXRpb25zLiANCg0KPGJyPg0KPGJyPg0K