Required packages
The below packages have been used for this analysis:
library(readr)
library(dplyr)
library(tidyr)
library(knitr)
library(hms)
library(outliers)
library(ggplot2)
library(forecast)
Executive Summary
- Data preprocessing is a process and the collection of operations needed to prepare all forms of untidy data for statistical analysis.
- There are 5 major tasks for data preprocessing framework, namely: Get, Understand, Tidy & Manipulate, Scan and Transform.
- We load the necessary packages into R required for our processes.
- The data sets which was downloaded from an open source website is loaded into R using read_csv() as they were in csv format. The head of the datasets are also displayed.
- Next, we join the 2 datasets using inner_join() function with the common variable being “Country” and display the head of the newly joined data frame.
- We check the structure of the data frame to get an overview of the data structures and variable types. We make necessary data type conversions on the data set.
- While observing the dataset, we realised that the way country coordinates were represented was incorrect and tidying was necessary to join the latitude and longitude values into a single column. We use unite() function to join the columns giving country coordinates in the correct format.
- A new column is mutated from Dystopia Residual column to get the Residual value of each column (Residual Value = Dystopia Residual -1.85).
- Once we are done with the tidying and manipulating the dataset, we scan for missing values. It is seen that there are no missing values hence we move on to scan for outliers.
- We use boxplot method to scan for outliers on numeric variables and use capping function to cap those values to 5th and 95th percentile values of the data.
- Lastly, data transformation is performed on 3 variables having non-normal distribution. The transformations were done using Square root method and BoxCox method inorder to reduce the skewness and transforming the data into a normal distribution.
Data
The first dataset used is “World Happiness Report” from the year 2015. This report is a landmark survey of the state of global happiness of various countries across the globe. The data consist of a happiness score which is calculated on a scale of 0 to 10 where 10 is the happiest and the respective countries ranking amongst others. Switzerland is currently ranking 1st with a happiness score of 7.59. The different columns are described as below:
- Country – Name of the country
- Region – Region the country belongs to
- Happiness Rank – Country’s rank based on happiness score
- Happiness Score – Score measured by asking sampled people how they would rate their happiness.
- Standard Error – The standard error of the happiness score
- Economy (GDP per Capita) – The extent to which GDP contributes to the calculation of happiness score
- Family - The extent to which Family contributes to the calculation of happiness score
- Health (Life Expectancy) - The extent to which health contributes to the calculation of happiness score
- Freedom - The extent to which Freedom contributes to the calculation of happiness score
- Trust (Government Corruption) - The extent to which perception of corruption contributes to the calculation of happiness score
- Generosity – The extent to which Generosity contributes to the calculation of happiness score
- Dystopia Residual – It is the sum of the dystopia happiness score (1.85) ie score of a hypothetical country having rank lower than the lowest ranking country in the report, plus the residual value of each country which is a number left over from the normalization of the variables which cannot be explained).
By adding up all these factors including the Dystopia Residual, we get the happiness score for each country.
The second dataset used here is “Countries Longitude and Latitude” which gives the latitude and longitude details of each country which is used as a reference in several cases. The latitude and longitude of each country is given in Simple Decimal Standard in separate columns. The columns in the dataset can be explained as below:
- (Blank column title) – Gives the serial number of countries
- Latitude – Gives the respective country’s latitudinal coordinates
- Longitude – Gives the respective country’s longitudinal coordinates
- Country – Name of the country
The datasets used were obtained from an open source website: “World Happiness Report” (https://www.kaggle.com/unsdsn/world-happiness) and “Countries Longitude and Latitude” (https://www.kaggle.com/folaraz/world-countries-and-continents-details?select=Countries+Longitude+and+Latitude.csv).
- First the working directory was set to my preference using setwd() function.
- The two datasets which were in csv format was read into R using read_csv(). The happiness report dataset is stored in data frame “Happy” and countries coordinates in “Country” (column with no title consisting of serial number was excluded as it possessed no relevance to our analysis).
- The head of datasets is displayed using head().The two data sets have Country names as the common variable.
- Although there were different approaches in joining the 2 datasets, I went ahead using inner_join(). The reason being that dataset “Happy” consisted of 158 observations and after using inner join, the resultant dataset had 147 observations meaning that 11 countries in the first dataset did not have coordinates details from the second dataset. As this is only a small percentage of the dataset, I decided to omit those observation, hence using inner join to join the datasets.
#Setting the working directory
setwd("C:/Users/User/Desktop/RMIT class/Data Wrangling/Assignment 2")
getwd()
[1] "C:/Users/User/Desktop/RMIT class/Data Wrangling/Assignment 2"
#Reading the datasets
Happy <- read_csv("2015.csv")
Parsed with column specification:
cols(
Country = [31mcol_character()[39m,
Region = [31mcol_character()[39m,
`Happiness Rank` = [32mcol_double()[39m,
`Happiness Score` = [32mcol_double()[39m,
`Standard Error` = [32mcol_double()[39m,
`Economy (GDP per Capita)` = [32mcol_double()[39m,
Family = [32mcol_double()[39m,
`Health (Life Expectancy)` = [32mcol_double()[39m,
Freedom = [32mcol_double()[39m,
`Trust (Government Corruption)` = [32mcol_double()[39m,
Generosity = [32mcol_double()[39m,
`Dystopia Residual` = [32mcol_double()[39m
)
head(Happy)
Country1 <- read_csv("Countries.csv")
Missing column names filled in: 'X1' [1]Parsed with column specification:
cols(
X1 = [32mcol_double()[39m,
latitude = [32mcol_double()[39m,
longitude = [32mcol_double()[39m,
Country = [31mcol_character()[39m
)
Country <- Country1[,c(2,3,4)]
head(Country)
#Joining the 2 datasets
join <- inner_join(Happy, Country, by = "Country")
head(join)
NA
Understand
- The data types of the data frame were checked using str() function.
- The variable ‘Region’ was factorised and levels were set with region names. Also the variable ‘Rank’ was converted to ordered factor.
- The structure of the data frame was checked after the data type conversions and all the variables were in the correct format.
str(join)
Classes ‘spec_tbl_df’, ‘tbl_df’, ‘tbl’ and 'data.frame': 147 obs. of 14 variables:
$ Country : chr "Switzerland" "Iceland" "Denmark" "Norway" ...
$ Region : chr "Western Europe" "Western Europe" "Western Europe" "Western Europe" ...
$ Happiness Rank : num 1 2 3 4 5 6 7 8 9 10 ...
$ Happiness Score : num 7.59 7.56 7.53 7.52 7.43 ...
$ Standard Error : num 0.0341 0.0488 0.0333 0.0388 0.0355 ...
$ Economy (GDP per Capita) : num 1.4 1.3 1.33 1.46 1.33 ...
$ Family : num 1.35 1.4 1.36 1.33 1.32 ...
$ Health (Life Expectancy) : num 0.941 0.948 0.875 0.885 0.906 ...
$ Freedom : num 0.666 0.629 0.649 0.67 0.633 ...
$ Trust (Government Corruption): num 0.42 0.141 0.484 0.365 0.33 ...
$ Generosity : num 0.297 0.436 0.341 0.347 0.458 ...
$ Dystopia Residual : num 2.52 2.7 2.49 2.47 2.45 ...
$ latitude : num 46.8 65 56.3 60.5 56.1 ...
$ longitude : num 8.23 -19.02 9.5 8.47 -106.35 ...
#Data type conversion
join$Region <- factor(join$Region, order = FALSE, levels = c("Australia and New Zealand", "Central and Eastern Europe", "Eastern Asia", "Latin America and Caribbean", "Middle East and Northern Africa", "North America", "Southeastern Asia", "Southern Asia", "Sub-Saharan Africa", "Western Europe"))
join$`Happiness Rank` <- factor(join$`Happiness Rank`, order = TRUE, levels = c(1:158))
str(join)
Classes ‘spec_tbl_df’, ‘tbl_df’, ‘tbl’ and 'data.frame': 147 obs. of 14 variables:
$ Country : chr "Switzerland" "Iceland" "Denmark" "Norway" ...
$ Region : Factor w/ 10 levels "Australia and New Zealand",..: 10 10 10 10 6 10 10 10 1 1 ...
$ Happiness Rank : Ord.factor w/ 158 levels "1"<"2"<"3"<"4"<..: 1 2 3 4 5 6 7 8 9 10 ...
$ Happiness Score : num 7.59 7.56 7.53 7.52 7.43 ...
$ Standard Error : num 0.0341 0.0488 0.0333 0.0388 0.0355 ...
$ Economy (GDP per Capita) : num 1.4 1.3 1.33 1.46 1.33 ...
$ Family : num 1.35 1.4 1.36 1.33 1.32 ...
$ Health (Life Expectancy) : num 0.941 0.948 0.875 0.885 0.906 ...
$ Freedom : num 0.666 0.629 0.649 0.67 0.633 ...
$ Trust (Government Corruption): num 0.42 0.141 0.484 0.365 0.33 ...
$ Generosity : num 0.297 0.436 0.341 0.347 0.458 ...
$ Dystopia Residual : num 2.52 2.7 2.49 2.47 2.45 ...
$ latitude : num 46.8 65 56.3 60.5 56.1 ...
$ longitude : num 8.23 -19.02 9.5 8.47 -106.35 ...
Tidy & Manipulate Data I
- In my data frame (join) the columns latitude and longitude are untidy.
- Location coordinates are represented in various formats. For example Decimal coordinates (20.5936832, 78.962883), Decimal Degrees Coordinates (20.5937° N 78.9629° E) and DMS (Degrees, Minutes and Seconds) Coordinates (20°35’37.3’’ N 78°57’46.4’’ E). When you look up any location on google maps, the address of that location would also give the coordinates written in Decimal Degrees format.
- Location coordinates by default is written with latitude and longitude together and not seperately. This link shows the formats in which location coordinates are written (https://www.wikihow.com/Write-Latitude-and-Longitude#:~:text=Start%20with%20your%20line%20of,or%20West%20as%20the%20direction.)
- If the coordinates are written in the correct format, it would be helpful while exporting the data into a GPS software for plotting them on the map.
- In my data frame, the latitude and longitude is written seperately. Therefore, I unite those two columns using unite() with sep=“,”.
- The new column created by uniting the latitude and longitude is called “Country Coordinates”.
- The columns have been re-arranged to place the new column next to the country names.
join1 <- join %>% unite(`Country Coordinates`, latitude, longitude, sep = ", ")
join2 <- join1[, c(1,13,2:12)]
Tidy & Manipulate Data II
- In our dataset, Dystopia Residual is measured as the sum of Dystopia happiness score and residual value of each country.
- The Dystopia happiness score as mentioned earlier is a constant value (1.85) which is same for all countries.
- Inorder to check the residual value of each counry, we use mutate() function to create a new variable by preserving the existing variable.
- We name the new variable “Residual Value” and it is calculated as the difference between Dystopia residual of each country and 1.85.
- The residual value is directly proportional to the dystopia residual meaning as the residual value increases, dystopia residual increases.
join3 <- join2 %>% mutate(`Residual Value` = (join$`Dystopia Residual` - 1.85))
Scan I
- We check for NA values in our dataset by incorporating is.na() within colSums() so as to get the sum of NA values in each column.
- By running the code, we see that there are no NA values in our dataset.
- The next step is to check for special values (-Inf, Inf, Nan).
- Inorder to check all numeric columns for the presence of special values, we create a function called “special”. The function checks all obervations whether if its a special value and the result is in logical form (TRUE if special values are detected, FALSE if not a special value).
- To get the sum of special values in each column, we use sapply() function as given below. This would give the count of special values in each column of our dataset.
#Checking for NA values
colSums(is.na(join3))
Country Country Coordinates Region
0 0 0
Happiness Rank Happiness Score Standard Error
0 0 0
Economy (GDP per Capita) Family Health (Life Expectancy)
0 0 0
Freedom Trust (Government Corruption) Generosity
0 0 0
Dystopia Residual Residual Value
0 0
#Checking for special values
special <- function(x){
if (is.numeric(x)) (is.infinite(x) | is.nan(x))}
#Sum of special values in each column
sapply(join3, function(x){if (is.numeric(x)) sum(special(x))})
$Country
NULL
$`Country Coordinates`
NULL
$Region
NULL
$`Happiness Rank`
NULL
$`Happiness Score`
[1] 0
$`Standard Error`
[1] 0
$`Economy (GDP per Capita)`
[1] 0
$Family
[1] 0
$`Health (Life Expectancy)`
[1] 0
$Freedom
[1] 0
$`Trust (Government Corruption)`
[1] 0
$Generosity
[1] 0
$`Dystopia Residual`
[1] 0
$`Residual Value`
[1] 0
Scan II
- Once we have scanned for NA values and special values, the next step is to handle the outliers present in our dataset.
- We use boxplot method to detect the univariate variables (as our variables are not dependent on each other)
- By observing the boxplots, we see that 6 out of 10 variables are having outliers. We use capping method to handle the outliers.
- Capping process involves in replacing the outliers with the nearest neighbouring values that are not outliers. As an example, for outliers that lie outside the outlier fence we cap it by replacing those values outside the lower limit with the value of 5 th percentile and for those that lie above the upper limit with the 95 th percentile value.
- We check the boxplots once again after applying capping function on the variables having outliers.
- While observing the boxplots, we see that the ‘Standard Error’ and ‘Trust (Government Corruption)’ are showing the presence of outliers on the boxplot. They are actually the outlier fence value which acts as a barrier between outliers and non outlier values. In order to confirm this, we use length() on the boxplots of those two variables.
#Boxplots before handling outliers
box_1 <- boxplot(join3$`Happiness Score`, join3$Family, join3$`Dystopia Residual`,
join3$`Residual Value`, join3$`Economy (GDP per Capita)`,
names = c("Happiness", "Family", "Dys Residual", "Res Value", "Economy"),
main = "Boxplot to detect outliers")

box_2 <- boxplot(join3$`Health (Life Expectancy)`, join3$Freedom, join3$`Standard Error`,
join3$`Trust (Government Corruption)`, join3$Generosity,
names = c("Health", "Freedom", "Standard Err", "Trust", "Generosity"),
main = "Boxplot to detect outliers")

#Capping function
cap <- function(x){
quantiles <- quantile(x, c(.05, .25, .75, .95) )
x[ x < quantiles[2] - 1.5*IQR(x)] <- quantiles[1]
x[ x > quantiles[3] + 1.5*IQR(x)] <- quantiles[4]
x}
join3$`Standard Error` <- join3$`Standard Error` %>% cap()
join3$Family <- join3$Family %>% cap()
join3$`Trust (Government Corruption)` <- join3$`Trust (Government Corruption)` %>% cap()
join3$Generosity <- join3$Generosity %>% cap()
join3$`Dystopia Residual` <- join3$`Dystopia Residual` %>% cap()
join3$`Residual Value` <- join3$`Residual Value` %>% cap()
#Boxplots after removing outliers
box_3 <- boxplot(join3$`Happiness Score`, join3$Family, join3$`Dystopia Residual`,
join3$`Residual Value`, join3$`Economy (GDP per Capita)`,
names = c("Happiness", "Family", "Dys Residual", "Res Value", "Economy"),
main = "Boxplot to detect outliers")

box_4<- boxplot(join3$`Health (Life Expectancy)`, join3$Freedom, join3$`Standard Error`,
join3$`Trust (Government Corruption)`, join3$Generosity,
names = c("Health", "Freedom", "Standard Err", "Trust", "Generosity"),
main = "Boxplot to detect outliers")

boxplot(join3$`Standard Error`, main = "Boxplot of Standard Error")$out
[1] 0.078768 0.078768 0.078768 0.078768 0.078768 0.078768 0.078768 0.078768 0.078768 0.078768 0.078768
[12] 0.078768

boxplot(join3$`Trust (Government Corruption)`, main = "Boxplot of Trust")$out
[1] 0.405353 0.405353 0.405353 0.405353 0.405353 0.405353 0.405353 0.405353 0.405353 0.405353 0.405353
[12] 0.405353 0.405353

References
Kaggle.com. 2019. World Happiness Report. [online] Available at: https://www.kaggle.com/unsdsn/world-happiness
Kaggle.com. 2017. World Countries And Continents Details. [online] Available at: https://www.kaggle.com/folaraz/world-countries-and-continents-details?select=Countries+Longitude+and+Latitude.csv
Dolgun, Anil 2010, ‘Data Wrangling’, lecture notes, MATH2349, RMIT University, http://rare-phoenix-161610.appspot.com/secured/index.html
Baglin, James 2010, ‘Applied Analytics’, lecture notes, MATH1324, RMIT University, https://astral-theory-157510.appspot.com/secured/index.html
wikiHow. 2020. How To Write Latitude And Longitude. [online] Available at: https://www.wikihow.com/Write-Latitude-and-Longitude#:~:text=Start%20with%20your%20line%20of,or%20West%20as%20the%20direction.
LS0tDQp0aXRsZTogIk1BVEgyMzQ5IFNlbWVzdGVyIDEsIDIwMjAiDQphdXRob3I6ICJTaWRkaGFydGggSmFjb2IgKFMzODI1MjM0KSINCnN1YnRpdGxlOiBBc3NpZ25tZW50IDINCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdA0KLS0tDQoNCg0KDQojIyBSZXF1aXJlZCBwYWNrYWdlcyANCg0KVGhlIGJlbG93IHBhY2thZ2VzIGhhdmUgYmVlbiB1c2VkIGZvciB0aGlzIGFuYWx5c2lzOg0KYGBge3J9DQpsaWJyYXJ5KHJlYWRyKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkodGlkeXIpDQpsaWJyYXJ5KGtuaXRyKQ0KbGlicmFyeShobXMpDQpsaWJyYXJ5KG91dGxpZXJzKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShmb3JlY2FzdCkNCg0KYGBgDQo8YnI+DQo8YnI+DQoNCiMjIEV4ZWN1dGl2ZSBTdW1tYXJ5IA0KDQoqIERhdGEgcHJlcHJvY2Vzc2luZyBpcyBhIHByb2Nlc3MgYW5kIHRoZSBjb2xsZWN0aW9uIG9mIG9wZXJhdGlvbnMgbmVlZGVkIHRvIHByZXBhcmUgYWxsIGZvcm1zIG9mIHVudGlkeSBkYXRhIGZvciBzdGF0aXN0aWNhbCBhbmFseXNpcy4gDQoqIFRoZXJlIGFyZSA1IG1ham9yIHRhc2tzIGZvciBkYXRhIHByZXByb2Nlc3NpbmcgZnJhbWV3b3JrLCBuYW1lbHk6IEdldCwgVW5kZXJzdGFuZCwgVGlkeSAmIE1hbmlwdWxhdGUsIFNjYW4gYW5kIFRyYW5zZm9ybS4NCiogV2UgbG9hZCB0aGUgbmVjZXNzYXJ5IHBhY2thZ2VzIGludG8gUiByZXF1aXJlZCBmb3Igb3VyIHByb2Nlc3Nlcy4NCiogVGhlIGRhdGEgc2V0cyB3aGljaCB3YXMgZG93bmxvYWRlZCBmcm9tIGFuIG9wZW4gc291cmNlIHdlYnNpdGUgaXMgbG9hZGVkIGludG8gUiB1c2luZyByZWFkX2NzdigpIGFzIHRoZXkgd2VyZSBpbiBjc3YgZm9ybWF0LiBUaGUgaGVhZCBvZiB0aGUgZGF0YXNldHMgYXJlIGFsc28gZGlzcGxheWVkLg0KKiBOZXh0LCB3ZSBqb2luIHRoZSAyIGRhdGFzZXRzIHVzaW5nIGlubmVyX2pvaW4oKSBmdW5jdGlvbiB3aXRoIHRoZSBjb21tb24gdmFyaWFibGUgYmVpbmcg4oCcQ291bnRyeeKAnSBhbmQgZGlzcGxheSB0aGUgaGVhZCBvZiB0aGUgbmV3bHkgam9pbmVkIGRhdGEgZnJhbWUuDQoqIFdlIGNoZWNrIHRoZSBzdHJ1Y3R1cmUgb2YgdGhlIGRhdGEgZnJhbWUgdG8gZ2V0IGFuIG92ZXJ2aWV3IG9mIHRoZSBkYXRhIHN0cnVjdHVyZXMgYW5kIHZhcmlhYmxlIHR5cGVzLiBXZSBtYWtlIG5lY2Vzc2FyeSBkYXRhIHR5cGUgY29udmVyc2lvbnMgb24gdGhlIGRhdGEgc2V0Lg0KKiBXaGlsZSBvYnNlcnZpbmcgdGhlIGRhdGFzZXQsIHdlIHJlYWxpc2VkIHRoYXQgdGhlIHdheSBjb3VudHJ5IGNvb3JkaW5hdGVzIHdlcmUgcmVwcmVzZW50ZWQgd2FzIGluY29ycmVjdCBhbmQgdGlkeWluZyB3YXMgbmVjZXNzYXJ5IHRvIGpvaW4gdGhlIGxhdGl0dWRlIGFuZCBsb25naXR1ZGUgdmFsdWVzIGludG8gYSBzaW5nbGUgY29sdW1uLiBXZSB1c2UgdW5pdGUoKSBmdW5jdGlvbiB0byBqb2luIHRoZSBjb2x1bW5zIGdpdmluZyBjb3VudHJ5IGNvb3JkaW5hdGVzIGluIHRoZSBjb3JyZWN0IGZvcm1hdC4NCiogQSBuZXcgY29sdW1uIGlzIG11dGF0ZWQgZnJvbSBEeXN0b3BpYSBSZXNpZHVhbCBjb2x1bW4gdG8gZ2V0IHRoZSBSZXNpZHVhbCB2YWx1ZSBvZiBlYWNoIGNvbHVtbiAoUmVzaWR1YWwgVmFsdWUgPSBEeXN0b3BpYSBSZXNpZHVhbCAtMS44NSkuDQoqIE9uY2Ugd2UgYXJlIGRvbmUgd2l0aCB0aGUgdGlkeWluZyBhbmQgbWFuaXB1bGF0aW5nIHRoZSBkYXRhc2V0LCB3ZSBzY2FuIGZvciBtaXNzaW5nIHZhbHVlcy4gSXQgaXMgc2VlbiB0aGF0IHRoZXJlIGFyZSBubyBtaXNzaW5nIHZhbHVlcyBoZW5jZSB3ZSBtb3ZlIG9uIHRvIHNjYW4gZm9yIG91dGxpZXJzLg0KKiBXZSB1c2UgYm94cGxvdCBtZXRob2QgdG8gc2NhbiBmb3Igb3V0bGllcnMgb24gbnVtZXJpYyB2YXJpYWJsZXMgYW5kIHVzZSBjYXBwaW5nIGZ1bmN0aW9uIHRvIGNhcCB0aG9zZSB2YWx1ZXMgdG8gNXRoIGFuZCA5NXRoIHBlcmNlbnRpbGUgdmFsdWVzIG9mIHRoZSBkYXRhLiANCiogTGFzdGx5LCBkYXRhIHRyYW5zZm9ybWF0aW9uIGlzIHBlcmZvcm1lZCBvbiAzIHZhcmlhYmxlcyBoYXZpbmcgbm9uLW5vcm1hbCBkaXN0cmlidXRpb24uIFRoZSB0cmFuc2Zvcm1hdGlvbnMgd2VyZSBkb25lIHVzaW5nIFNxdWFyZSByb290IG1ldGhvZCBhbmQgQm94Q294IG1ldGhvZCBpbm9yZGVyIHRvIHJlZHVjZSB0aGUgc2tld25lc3MgYW5kIHRyYW5zZm9ybWluZyB0aGUgZGF0YSBpbnRvIGEgbm9ybWFsIGRpc3RyaWJ1dGlvbi4NCg0KPGJyPg0KPGJyPg0KDQojIyBEYXRhIA0KDQpUaGUgZmlyc3QgZGF0YXNldCB1c2VkIGlzIOKAnFdvcmxkIEhhcHBpbmVzcyBSZXBvcnTigJ0gZnJvbSB0aGUgeWVhciAyMDE1LiBUaGlzIHJlcG9ydCBpcyBhIGxhbmRtYXJrIHN1cnZleSBvZiB0aGUgc3RhdGUgb2YgZ2xvYmFsIGhhcHBpbmVzcyBvZiB2YXJpb3VzIGNvdW50cmllcyBhY3Jvc3MgdGhlIGdsb2JlLiBUaGUgZGF0YSBjb25zaXN0IG9mIGEgaGFwcGluZXNzIHNjb3JlIHdoaWNoIGlzIGNhbGN1bGF0ZWQgb24gYSBzY2FsZSBvZiAwIHRvIDEwIHdoZXJlIDEwIGlzIHRoZSBoYXBwaWVzdCBhbmQgdGhlIHJlc3BlY3RpdmUgY291bnRyaWVzIHJhbmtpbmcgYW1vbmdzdCBvdGhlcnMuIFN3aXR6ZXJsYW5kIGlzIGN1cnJlbnRseSByYW5raW5nIDFzdCB3aXRoIGEgaGFwcGluZXNzIHNjb3JlIG9mIDcuNTkuIFRoZSBkaWZmZXJlbnQgY29sdW1ucyBhcmUgZGVzY3JpYmVkIGFzIGJlbG93Og0KDQoxLglDb3VudHJ5IOKAkyBOYW1lIG9mIHRoZSBjb3VudHJ5DQoyLglSZWdpb24g4oCTIFJlZ2lvbiB0aGUgY291bnRyeSBiZWxvbmdzIHRvDQozLglIYXBwaW5lc3MgUmFuayDigJMgQ291bnRyeeKAmXMgcmFuayBiYXNlZCBvbiBoYXBwaW5lc3Mgc2NvcmUNCjQuCUhhcHBpbmVzcyBTY29yZSDigJMgU2NvcmUgbWVhc3VyZWQgYnkgYXNraW5nIHNhbXBsZWQgcGVvcGxlIGhvdyB0aGV5IHdvdWxkIHJhdGUgdGhlaXIgaGFwcGluZXNzLg0KNS4JU3RhbmRhcmQgRXJyb3Ig4oCTIFRoZSBzdGFuZGFyZCBlcnJvciBvZiB0aGUgaGFwcGluZXNzIHNjb3JlDQo2LglFY29ub215IChHRFAgcGVyIENhcGl0YSkg4oCTIFRoZSBleHRlbnQgdG8gd2hpY2ggR0RQIGNvbnRyaWJ1dGVzIHRvIHRoZSBjYWxjdWxhdGlvbiBvZiBoYXBwaW5lc3MgICAgIHNjb3JlDQo3LglGYW1pbHkgLSBUaGUgZXh0ZW50IHRvIHdoaWNoIEZhbWlseSBjb250cmlidXRlcyB0byB0aGUgY2FsY3VsYXRpb24gb2YgaGFwcGluZXNzIHNjb3JlDQo4LglIZWFsdGggKExpZmUgRXhwZWN0YW5jeSkgLSBUaGUgZXh0ZW50IHRvIHdoaWNoIGhlYWx0aCBjb250cmlidXRlcyB0byB0aGUgY2FsY3VsYXRpb24gb2YgICAgICAgICAgIGhhcHBpbmVzcyBzY29yZQ0KOS4JRnJlZWRvbSAtIFRoZSBleHRlbnQgdG8gd2hpY2ggRnJlZWRvbSBjb250cmlidXRlcyB0byB0aGUgY2FsY3VsYXRpb24gb2YgaGFwcGluZXNzIHNjb3JlDQoxMC4JVHJ1c3QgKEdvdmVybm1lbnQgQ29ycnVwdGlvbikgLSBUaGUgZXh0ZW50IHRvIHdoaWNoIHBlcmNlcHRpb24gb2YgY29ycnVwdGlvbiBjb250cmlidXRlcyB0byAgICAgICB0aGUgY2FsY3VsYXRpb24gb2YgaGFwcGluZXNzIHNjb3JlDQoxMS4JR2VuZXJvc2l0eSDigJMgVGhlIGV4dGVudCB0byB3aGljaCBHZW5lcm9zaXR5IGNvbnRyaWJ1dGVzIHRvIHRoZSBjYWxjdWxhdGlvbiBvZiBoYXBwaW5lc3Mgc2NvcmUNCjEyLglEeXN0b3BpYSBSZXNpZHVhbCDigJMgSXQgaXMgdGhlIHN1bSBvZiB0aGUgZHlzdG9waWEgaGFwcGluZXNzIHNjb3JlICgxLjg1KSBpZSBzY29yZSBvZiBhICAgICAgICAgICAgaHlwb3RoZXRpY2FsIGNvdW50cnkgaGF2aW5nIHJhbmsgbG93ZXIgdGhhbiB0aGUgbG93ZXN0IHJhbmtpbmcgY291bnRyeSBpbiB0aGUgcmVwb3J0LCBwbHVzIHRoZSAgICAgcmVzaWR1YWwgdmFsdWUgb2YgZWFjaCBjb3VudHJ5IHdoaWNoIGlzIGEgbnVtYmVyIGxlZnQgb3ZlciBmcm9tIHRoZSBub3JtYWxpemF0aW9uIG9mIHRoZSAgICAgICAgICB2YXJpYWJsZXMgd2hpY2ggY2Fubm90IGJlIGV4cGxhaW5lZCkuDQoNCkJ5IGFkZGluZyB1cCBhbGwgdGhlc2UgZmFjdG9ycyBpbmNsdWRpbmcgdGhlIER5c3RvcGlhIFJlc2lkdWFsLCB3ZSBnZXQgdGhlIGhhcHBpbmVzcyBzY29yZSBmb3IgZWFjaCBjb3VudHJ5LiANCjxicj4NClRoZSBzZWNvbmQgZGF0YXNldCB1c2VkIGhlcmUgaXMg4oCcQ291bnRyaWVzIExvbmdpdHVkZSBhbmQgTGF0aXR1ZGXigJ0gd2hpY2ggZ2l2ZXMgdGhlIGxhdGl0dWRlIGFuZCBsb25naXR1ZGUgZGV0YWlscyBvZiBlYWNoIGNvdW50cnkgd2hpY2ggaXMgdXNlZCBhcyBhIHJlZmVyZW5jZSBpbiBzZXZlcmFsIGNhc2VzLiBUaGUgbGF0aXR1ZGUgYW5kIGxvbmdpdHVkZSBvZiBlYWNoIGNvdW50cnkgaXMgZ2l2ZW4gaW4gU2ltcGxlIERlY2ltYWwgU3RhbmRhcmQgaW4gc2VwYXJhdGUgY29sdW1ucy4gVGhlIGNvbHVtbnMgaW4gdGhlIGRhdGFzZXQgY2FuIGJlIGV4cGxhaW5lZCBhcyBiZWxvdzoNCg0KMS4JKEJsYW5rIGNvbHVtbiB0aXRsZSkg4oCTIEdpdmVzIHRoZSBzZXJpYWwgbnVtYmVyIG9mIGNvdW50cmllcw0KMi4JTGF0aXR1ZGUg4oCTIEdpdmVzIHRoZSByZXNwZWN0aXZlIGNvdW50cnnigJlzIGxhdGl0dWRpbmFsIGNvb3JkaW5hdGVzDQozLglMb25naXR1ZGUg4oCTIEdpdmVzIHRoZSByZXNwZWN0aXZlIGNvdW50cnnigJlzIGxvbmdpdHVkaW5hbCBjb29yZGluYXRlcw0KNC4JQ291bnRyeSDigJMgTmFtZSBvZiB0aGUgY291bnRyeQ0KDQpUaGUgZGF0YXNldHMgdXNlZCB3ZXJlIG9idGFpbmVkIGZyb20gYW4gb3BlbiBzb3VyY2Ugd2Vic2l0ZTog4oCcV29ybGQgSGFwcGluZXNzIFJlcG9ydOKAnSAoaHR0cHM6Ly93d3cua2FnZ2xlLmNvbS91bnNkc24vd29ybGQtaGFwcGluZXNzKSBhbmQg4oCcQ291bnRyaWVzIExvbmdpdHVkZSBhbmQgTGF0aXR1ZGXigJ0gKGh0dHBzOi8vd3d3LmthZ2dsZS5jb20vZm9sYXJhei93b3JsZC1jb3VudHJpZXMtYW5kLWNvbnRpbmVudHMtZGV0YWlscz9zZWxlY3Q9Q291bnRyaWVzK0xvbmdpdHVkZSthbmQrTGF0aXR1ZGUuY3N2KS4NCg0KKiBGaXJzdCB0aGUgd29ya2luZyBkaXJlY3Rvcnkgd2FzIHNldCB0byBteSBwcmVmZXJlbmNlIHVzaW5nIHNldHdkKCkgZnVuY3Rpb24uDQoqIFRoZSB0d28gZGF0YXNldHMgd2hpY2ggd2VyZSBpbiBjc3YgZm9ybWF0IHdhcyByZWFkIGludG8gUiB1c2luZyByZWFkX2NzdigpLiBUaGUgaGFwcGluZXNzIHJlcG9ydCBkYXRhc2V0IGlzIHN0b3JlZCBpbiBkYXRhIGZyYW1lIOKAnEhhcHB54oCdIGFuZCBjb3VudHJpZXMgY29vcmRpbmF0ZXMgaW4g4oCcQ291bnRyeeKAnSAoY29sdW1uIHdpdGggbm8gdGl0bGUgY29uc2lzdGluZyBvZiBzZXJpYWwgbnVtYmVyIHdhcyBleGNsdWRlZCBhcyBpdCBwb3NzZXNzZWQgbm8gcmVsZXZhbmNlIHRvIG91ciBhbmFseXNpcykuDQoqIFRoZSBoZWFkIG9mIGRhdGFzZXRzIGlzIGRpc3BsYXllZCB1c2luZyBoZWFkKCkuVGhlIHR3byBkYXRhIHNldHMgaGF2ZSBDb3VudHJ5IG5hbWVzIGFzIHRoZSBjb21tb24gdmFyaWFibGUuDQoqIEFsdGhvdWdoIHRoZXJlIHdlcmUgZGlmZmVyZW50IGFwcHJvYWNoZXMgaW4gam9pbmluZyB0aGUgMiBkYXRhc2V0cywgSSB3ZW50IGFoZWFkIHVzaW5nIGlubmVyX2pvaW4oKS4gVGhlIHJlYXNvbiBiZWluZyB0aGF0IGRhdGFzZXQg4oCcSGFwcHnigJ0gY29uc2lzdGVkIG9mIDE1OCBvYnNlcnZhdGlvbnMgYW5kIGFmdGVyIHVzaW5nIGlubmVyIGpvaW4sIHRoZSByZXN1bHRhbnQgZGF0YXNldCBoYWQgMTQ3IG9ic2VydmF0aW9ucyBtZWFuaW5nIHRoYXQgMTEgY291bnRyaWVzIGluIHRoZSBmaXJzdCBkYXRhc2V0IGRpZCBub3QgaGF2ZSBjb29yZGluYXRlcyBkZXRhaWxzIGZyb20gdGhlIHNlY29uZCBkYXRhc2V0LiBBcyB0aGlzIGlzIG9ubHkgYSBzbWFsbCBwZXJjZW50YWdlIG9mIHRoZSBkYXRhc2V0LCBJIGRlY2lkZWQgdG8gb21pdCB0aG9zZSBvYnNlcnZhdGlvbiwgaGVuY2UgdXNpbmcgaW5uZXIgam9pbiB0byBqb2luIHRoZSBkYXRhc2V0cy4NCg0KYGBge3J9DQojU2V0dGluZyB0aGUgd29ya2luZyBkaXJlY3RvcnkNCnNldHdkKCJDOi9Vc2Vycy9Vc2VyL0Rlc2t0b3AvUk1JVCBjbGFzcy9EYXRhIFdyYW5nbGluZy9Bc3NpZ25tZW50IDIiKQ0KZ2V0d2QoKQ0KDQojUmVhZGluZyB0aGUgZGF0YXNldHMNCkhhcHB5IDwtIHJlYWRfY3N2KCIyMDE1LmNzdiIpDQpoZWFkKEhhcHB5KQ0KDQpDb3VudHJ5MSA8LSByZWFkX2NzdigiQ291bnRyaWVzLmNzdiIpDQpDb3VudHJ5IDwtIENvdW50cnkxWyxjKDIsMyw0KV0NCmhlYWQoQ291bnRyeSkNCg0KI0pvaW5pbmcgdGhlIDIgZGF0YXNldHMNCmpvaW4gPC0gaW5uZXJfam9pbihIYXBweSwgQ291bnRyeSwgYnkgPSAiQ291bnRyeSIpDQpoZWFkKGpvaW4pDQoNCmBgYA0KPGJyPg0KPGJyPg0KDQojIyBVbmRlcnN0YW5kIA0KDQoqIFRoZSBkYXRhIHR5cGVzIG9mIHRoZSBkYXRhIGZyYW1lIHdlcmUgY2hlY2tlZCB1c2luZyBzdHIoKSBmdW5jdGlvbi4gDQoqIFRoZSB2YXJpYWJsZSAnUmVnaW9uJyB3YXMgZmFjdG9yaXNlZCBhbmQgbGV2ZWxzIHdlcmUgc2V0IHdpdGggcmVnaW9uIG5hbWVzLiBBbHNvIHRoZSB2YXJpYWJsZSAnUmFuaycgd2FzIGNvbnZlcnRlZCB0byBvcmRlcmVkIGZhY3Rvci4NCiogVGhlIHN0cnVjdHVyZSBvZiB0aGUgZGF0YSBmcmFtZSB3YXMgY2hlY2tlZCBhZnRlciB0aGUgZGF0YSB0eXBlIGNvbnZlcnNpb25zIGFuZCBhbGwgdGhlIHZhcmlhYmxlcyB3ZXJlIGluIHRoZSBjb3JyZWN0IGZvcm1hdC4NCg0KYGBge3J9DQpzdHIoam9pbikNCg0KI0RhdGEgdHlwZSBjb252ZXJzaW9uDQoNCmpvaW4kUmVnaW9uIDwtIGZhY3Rvcihqb2luJFJlZ2lvbiwgb3JkZXIgPSBGQUxTRSwgbGV2ZWxzID0gYygiQXVzdHJhbGlhIGFuZCBOZXcgWmVhbGFuZCIsICJDZW50cmFsIGFuZCBFYXN0ZXJuIEV1cm9wZSIsICJFYXN0ZXJuIEFzaWEiLCAiTGF0aW4gQW1lcmljYSBhbmQgQ2FyaWJiZWFuIiwgIk1pZGRsZSBFYXN0IGFuZCBOb3J0aGVybiBBZnJpY2EiLCAiTm9ydGggQW1lcmljYSIsICJTb3V0aGVhc3Rlcm4gQXNpYSIsICJTb3V0aGVybiBBc2lhIiwgIlN1Yi1TYWhhcmFuIEFmcmljYSIsICJXZXN0ZXJuIEV1cm9wZSIpKQ0KDQpqb2luJGBIYXBwaW5lc3MgUmFua2AgPC0gZmFjdG9yKGpvaW4kYEhhcHBpbmVzcyBSYW5rYCwgb3JkZXIgPSBUUlVFLCBsZXZlbHMgPSBjKDE6MTU4KSkNCg0Kc3RyKGpvaW4pDQpgYGANCjxicj4NCjxicj4NCg0KIyMJVGlkeSAmIE1hbmlwdWxhdGUgRGF0YSBJIA0KDQoqIEluIG15IGRhdGEgZnJhbWUgKGpvaW4pIHRoZSBjb2x1bW5zIGxhdGl0dWRlIGFuZCBsb25naXR1ZGUgYXJlIHVudGlkeS4NCiogTG9jYXRpb24gY29vcmRpbmF0ZXMgYXJlIHJlcHJlc2VudGVkIGluIHZhcmlvdXMgZm9ybWF0cy4gRm9yIGV4YW1wbGUgRGVjaW1hbCBjb29yZGluYXRlcyAoMjAuNTkzNjgzMiwgNzguOTYyODgzKSwgRGVjaW1hbCBEZWdyZWVzIENvb3JkaW5hdGVzICgyMC41OTM3wrAgTiA3OC45NjI5wrAgRSkgYW5kIERNUyAoRGVncmVlcywgTWludXRlcyBhbmQgU2Vjb25kcykgQ29vcmRpbmF0ZXMgKDIwwrAzNSczNy4zJycgTiA3OMKwNTcnNDYuNCcnIEUpLiBXaGVuIHlvdSBsb29rIHVwIGFueSBsb2NhdGlvbiBvbiBnb29nbGUgbWFwcywgdGhlIGFkZHJlc3Mgb2YgdGhhdCBsb2NhdGlvbiB3b3VsZCBhbHNvIGdpdmUgdGhlIGNvb3JkaW5hdGVzIHdyaXR0ZW4gaW4gRGVjaW1hbCBEZWdyZWVzIGZvcm1hdC4gDQoqIExvY2F0aW9uIGNvb3JkaW5hdGVzIGJ5IGRlZmF1bHQgaXMgd3JpdHRlbiB3aXRoIGxhdGl0dWRlIGFuZCBsb25naXR1ZGUgdG9nZXRoZXIgYW5kIG5vdCBzZXBlcmF0ZWx5LiBUaGlzIGxpbmsgc2hvd3MgdGhlIGZvcm1hdHMgaW4gd2hpY2ggbG9jYXRpb24gY29vcmRpbmF0ZXMgYXJlIHdyaXR0ZW4gKGh0dHBzOi8vd3d3Lndpa2lob3cuY29tL1dyaXRlLUxhdGl0dWRlLWFuZC1Mb25naXR1ZGUjOn46dGV4dD1TdGFydCUyMHdpdGglMjB5b3VyJTIwbGluZSUyMG9mLG9yJTIwV2VzdCUyMGFzJTIwdGhlJTIwZGlyZWN0aW9uLikNCiogSWYgdGhlIGNvb3JkaW5hdGVzIGFyZSB3cml0dGVuIGluIHRoZSBjb3JyZWN0IGZvcm1hdCwgaXQgd291bGQgYmUgaGVscGZ1bCB3aGlsZSBleHBvcnRpbmcgdGhlIGRhdGEgaW50byBhIEdQUyBzb2Z0d2FyZSBmb3IgcGxvdHRpbmcgdGhlbSBvbiB0aGUgbWFwLg0KKiBJbiBteSBkYXRhIGZyYW1lLCB0aGUgbGF0aXR1ZGUgYW5kIGxvbmdpdHVkZSBpcyB3cml0dGVuIHNlcGVyYXRlbHkuIFRoZXJlZm9yZSwgSSB1bml0ZSB0aG9zZSB0d28gY29sdW1ucyB1c2luZyB1bml0ZSgpIHdpdGggc2VwPSIsICIuDQoqIFRoZSBuZXcgY29sdW1uIGNyZWF0ZWQgYnkgdW5pdGluZyB0aGUgbGF0aXR1ZGUgYW5kIGxvbmdpdHVkZSBpcyBjYWxsZWQgIkNvdW50cnkgQ29vcmRpbmF0ZXMiLg0KKiBUaGUgY29sdW1ucyBoYXZlIGJlZW4gcmUtYXJyYW5nZWQgdG8gcGxhY2UgdGhlIG5ldyBjb2x1bW4gbmV4dCB0byB0aGUgY291bnRyeSBuYW1lcy4NCg0KYGBge3J9DQpqb2luMSA8LSBqb2luICU+JSB1bml0ZShgQ291bnRyeSBDb29yZGluYXRlc2AsIGxhdGl0dWRlLCBsb25naXR1ZGUsIHNlcCA9ICIsICIpDQpqb2luMiA8LSBqb2luMVssIGMoMSwxMywyOjEyKV0NCg0KYGBgDQo8YnI+DQo8YnI+DQoNCiMjCVRpZHkgJiBNYW5pcHVsYXRlIERhdGEgSUkgDQoNCiogSW4gb3VyIGRhdGFzZXQsIER5c3RvcGlhIFJlc2lkdWFsIGlzIG1lYXN1cmVkIGFzIHRoZSBzdW0gb2YgRHlzdG9waWEgaGFwcGluZXNzIHNjb3JlIGFuZCByZXNpZHVhbCB2YWx1ZSBvZiBlYWNoIGNvdW50cnkuDQoqIFRoZSBEeXN0b3BpYSBoYXBwaW5lc3Mgc2NvcmUgYXMgbWVudGlvbmVkIGVhcmxpZXIgaXMgYSBjb25zdGFudCB2YWx1ZSAoMS44NSkgd2hpY2ggaXMgc2FtZSBmb3IgYWxsIGNvdW50cmllcy4NCiogSW5vcmRlciB0byBjaGVjayB0aGUgcmVzaWR1YWwgdmFsdWUgb2YgZWFjaCBjb3VucnksIHdlIHVzZSBtdXRhdGUoKSBmdW5jdGlvbiB0byBjcmVhdGUgYSBuZXcgdmFyaWFibGUgYnkgcHJlc2VydmluZyB0aGUgZXhpc3RpbmcgdmFyaWFibGUuIA0KKiBXZSBuYW1lIHRoZSBuZXcgdmFyaWFibGUgIlJlc2lkdWFsIFZhbHVlIiBhbmQgaXQgaXMgY2FsY3VsYXRlZCBhcyB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIER5c3RvcGlhIHJlc2lkdWFsIG9mIGVhY2ggY291bnRyeSBhbmQgMS44NS4NCiogVGhlIHJlc2lkdWFsIHZhbHVlIGlzIGRpcmVjdGx5IHByb3BvcnRpb25hbCB0byB0aGUgZHlzdG9waWEgcmVzaWR1YWwgbWVhbmluZyBhcyB0aGUgcmVzaWR1YWwgdmFsdWUgaW5jcmVhc2VzLCBkeXN0b3BpYSByZXNpZHVhbCBpbmNyZWFzZXMuDQoNCg0KYGBge3J9DQpqb2luMyA8LSBqb2luMiAlPiUgbXV0YXRlKGBSZXNpZHVhbCBWYWx1ZWAgPSAoam9pbiRgRHlzdG9waWEgUmVzaWR1YWxgIC0gMS44NSkpDQoNCmBgYA0KPGJyPg0KPGJyPg0KDQojIwlTY2FuIEkgDQoNCiogV2UgY2hlY2sgZm9yIE5BIHZhbHVlcyBpbiBvdXIgZGF0YXNldCBieSBpbmNvcnBvcmF0aW5nIGlzLm5hKCkgd2l0aGluIGNvbFN1bXMoKSBzbyBhcyB0byBnZXQgdGhlIHN1bSBvZiBOQSB2YWx1ZXMgaW4gZWFjaCBjb2x1bW4uIA0KKiBCeSBydW5uaW5nIHRoZSBjb2RlLCB3ZSBzZWUgdGhhdCB0aGVyZSBhcmUgbm8gTkEgdmFsdWVzIGluIG91ciBkYXRhc2V0Lg0KKiBUaGUgbmV4dCBzdGVwIGlzIHRvIGNoZWNrIGZvciBzcGVjaWFsIHZhbHVlcyAoLUluZiwgSW5mLCBOYW4pLg0KKiBJbm9yZGVyIHRvIGNoZWNrIGFsbCBudW1lcmljIGNvbHVtbnMgZm9yIHRoZSBwcmVzZW5jZSBvZiBzcGVjaWFsIHZhbHVlcywgd2UgY3JlYXRlIGEgZnVuY3Rpb24gY2FsbGVkICJzcGVjaWFsIi4gVGhlIGZ1bmN0aW9uIGNoZWNrcyBhbGwgb2JlcnZhdGlvbnMgd2hldGhlciBpZiBpdHMgYSBzcGVjaWFsIHZhbHVlIGFuZCB0aGUgcmVzdWx0IGlzIGluIGxvZ2ljYWwgZm9ybSAoVFJVRSBpZiBzcGVjaWFsIHZhbHVlcyBhcmUgZGV0ZWN0ZWQsIEZBTFNFIGlmIG5vdCBhIHNwZWNpYWwgdmFsdWUpLg0KKiBUbyBnZXQgdGhlIHN1bSBvZiBzcGVjaWFsIHZhbHVlcyBpbiBlYWNoIGNvbHVtbiwgd2UgdXNlIHNhcHBseSgpIGZ1bmN0aW9uIGFzIGdpdmVuIGJlbG93LiBUaGlzIHdvdWxkIGdpdmUgdGhlIGNvdW50IG9mIHNwZWNpYWwgdmFsdWVzIGluIGVhY2ggY29sdW1uIG9mIG91ciBkYXRhc2V0Lg0KDQoNCmBgYHtyfQ0KI0NoZWNraW5nIGZvciBOQSB2YWx1ZXMNCmNvbFN1bXMoaXMubmEoam9pbjMpKQ0KDQojQ2hlY2tpbmcgZm9yIHNwZWNpYWwgdmFsdWVzDQpzcGVjaWFsIDwtIGZ1bmN0aW9uKHgpew0KICBpZiAoaXMubnVtZXJpYyh4KSkgKGlzLmluZmluaXRlKHgpIHwgaXMubmFuKHgpKX0NCg0KI1N1bSBvZiBzcGVjaWFsIHZhbHVlcyBpbiBlYWNoIGNvbHVtbg0Kc2FwcGx5KGpvaW4zLCBmdW5jdGlvbih4KXtpZiAoaXMubnVtZXJpYyh4KSkgc3VtKHNwZWNpYWwoeCkpfSkNCg0KYGBgDQo8YnI+DQo8YnI+DQoNCiMjCVNjYW4gSUkNCg0KKiBPbmNlIHdlIGhhdmUgc2Nhbm5lZCBmb3IgTkEgdmFsdWVzIGFuZCBzcGVjaWFsIHZhbHVlcywgdGhlIG5leHQgc3RlcCBpcyB0byBoYW5kbGUgdGhlIG91dGxpZXJzIHByZXNlbnQgaW4gb3VyIGRhdGFzZXQuDQoqIFdlIHVzZSBib3hwbG90IG1ldGhvZCB0byBkZXRlY3QgdGhlIHVuaXZhcmlhdGUgdmFyaWFibGVzIChhcyBvdXIgdmFyaWFibGVzIGFyZSBub3QgZGVwZW5kZW50IG9uIGVhY2ggb3RoZXIpDQoqIEJ5IG9ic2VydmluZyB0aGUgYm94cGxvdHMsIHdlIHNlZSB0aGF0IDYgb3V0IG9mIDEwIHZhcmlhYmxlcyBhcmUgaGF2aW5nIG91dGxpZXJzLiBXZSB1c2UgY2FwcGluZyBtZXRob2QgdG8gaGFuZGxlIHRoZSBvdXRsaWVycy4NCiogQ2FwcGluZyBwcm9jZXNzIGludm9sdmVzIGluIHJlcGxhY2luZyB0aGUgb3V0bGllcnMgd2l0aCB0aGUgbmVhcmVzdCBuZWlnaGJvdXJpbmcgdmFsdWVzIHRoYXQgYXJlIG5vdCBvdXRsaWVycy4gQXMgYW4gZXhhbXBsZSwgZm9yIG91dGxpZXJzIHRoYXQgbGllIG91dHNpZGUgdGhlIG91dGxpZXIgZmVuY2Ugd2UgY2FwIGl0IGJ5IHJlcGxhY2luZyB0aG9zZSB2YWx1ZXMgb3V0c2lkZSB0aGUgbG93ZXIgbGltaXQgd2l0aCB0aGUgdmFsdWUgb2YgNSB0aCBwZXJjZW50aWxlIGFuZCBmb3IgdGhvc2UgdGhhdCBsaWUgYWJvdmUgdGhlIHVwcGVyIGxpbWl0IHdpdGggdGhlIDk1IHRoIHBlcmNlbnRpbGUgdmFsdWUuDQoqIFdlIGNoZWNrIHRoZSBib3hwbG90cyBvbmNlIGFnYWluIGFmdGVyIGFwcGx5aW5nIGNhcHBpbmcgZnVuY3Rpb24gb24gdGhlIHZhcmlhYmxlcyBoYXZpbmcgb3V0bGllcnMuIA0KKiBXaGlsZSBvYnNlcnZpbmcgdGhlIGJveHBsb3RzLCB3ZSBzZWUgdGhhdCB0aGUgJ1N0YW5kYXJkIEVycm9yJyBhbmQgJ1RydXN0IChHb3Zlcm5tZW50IENvcnJ1cHRpb24pJyBhcmUgc2hvd2luZyB0aGUgcHJlc2VuY2Ugb2Ygb3V0bGllcnMgb24gdGhlIGJveHBsb3QuIFRoZXkgYXJlIGFjdHVhbGx5IHRoZSBvdXRsaWVyIGZlbmNlIHZhbHVlIHdoaWNoIGFjdHMgYXMgYSBiYXJyaWVyIGJldHdlZW4gb3V0bGllcnMgYW5kIG5vbiBvdXRsaWVyIHZhbHVlcy4gSW4gb3JkZXIgdG8gY29uZmlybSB0aGlzLCB3ZSB1c2UgbGVuZ3RoKCkgb24gdGhlIGJveHBsb3RzIG9mIHRob3NlIHR3byB2YXJpYWJsZXMuIA0KDQpgYGB7cn0NCiNCb3hwbG90cyBiZWZvcmUgaGFuZGxpbmcgb3V0bGllcnMNCmJveF8xIDwtIGJveHBsb3Qoam9pbjMkYEhhcHBpbmVzcyBTY29yZWAsIGpvaW4zJEZhbWlseSwgam9pbjMkYER5c3RvcGlhIFJlc2lkdWFsYCwgDQogICAgICAgICAgICAgICAgIGpvaW4zJGBSZXNpZHVhbCBWYWx1ZWAsIGpvaW4zJGBFY29ub215IChHRFAgcGVyIENhcGl0YSlgLA0KICAgICAgICAgICAgICAgICBuYW1lcyA9IGMoIkhhcHBpbmVzcyIsICJGYW1pbHkiLCAiRHlzIFJlc2lkdWFsIiwgIlJlcyBWYWx1ZSIsICJFY29ub215IiksIA0KICAgICAgICAgICAgICAgICBtYWluID0gIkJveHBsb3QgdG8gZGV0ZWN0IG91dGxpZXJzIikNCg0KYm94XzIgPC0gYm94cGxvdChqb2luMyRgSGVhbHRoIChMaWZlIEV4cGVjdGFuY3kpYCwgam9pbjMkRnJlZWRvbSwgam9pbjMkYFN0YW5kYXJkIEVycm9yYCwgDQogICAgICAgICAgICAgICAgIGpvaW4zJGBUcnVzdCAoR292ZXJubWVudCBDb3JydXB0aW9uKWAsIGpvaW4zJEdlbmVyb3NpdHksDQogICAgICAgICAgICAgICAgIG5hbWVzID0gYygiSGVhbHRoIiwgIkZyZWVkb20iLCAiU3RhbmRhcmQgRXJyIiwgIlRydXN0IiwgIkdlbmVyb3NpdHkiKSwNCiAgICAgICAgICAgICAgICAgbWFpbiA9ICJCb3hwbG90IHRvIGRldGVjdCBvdXRsaWVycyIpDQoNCiNDYXBwaW5nIGZ1bmN0aW9uDQpjYXAgPC0gZnVuY3Rpb24oeCl7DQogIHF1YW50aWxlcyA8LSBxdWFudGlsZSh4LCBjKC4wNSwgLjI1LCAuNzUsIC45NSkgKQ0KICB4WyB4IDwgcXVhbnRpbGVzWzJdIC0gMS41KklRUih4KV0gPC0gcXVhbnRpbGVzWzFdDQogIHhbIHggPiBxdWFudGlsZXNbM10gKyAxLjUqSVFSKHgpXSA8LSBxdWFudGlsZXNbNF0NCiAgeH0NCg0Kam9pbjMkYFN0YW5kYXJkIEVycm9yYCA8LSBqb2luMyRgU3RhbmRhcmQgRXJyb3JgICU+JSBjYXAoKQ0Kam9pbjMkRmFtaWx5IDwtIGpvaW4zJEZhbWlseSAlPiUgY2FwKCkNCmpvaW4zJGBUcnVzdCAoR292ZXJubWVudCBDb3JydXB0aW9uKWAgPC0gam9pbjMkYFRydXN0IChHb3Zlcm5tZW50IENvcnJ1cHRpb24pYCAlPiUgY2FwKCkNCmpvaW4zJEdlbmVyb3NpdHkgPC0gam9pbjMkR2VuZXJvc2l0eSAlPiUgY2FwKCkNCmpvaW4zJGBEeXN0b3BpYSBSZXNpZHVhbGAgPC0gam9pbjMkYER5c3RvcGlhIFJlc2lkdWFsYCAlPiUgY2FwKCkNCmpvaW4zJGBSZXNpZHVhbCBWYWx1ZWAgPC0gam9pbjMkYFJlc2lkdWFsIFZhbHVlYCAlPiUgY2FwKCkNCg0KI0JveHBsb3RzIGFmdGVyIHJlbW92aW5nIG91dGxpZXJzDQpib3hfMyA8LSBib3hwbG90KGpvaW4zJGBIYXBwaW5lc3MgU2NvcmVgLCBqb2luMyRGYW1pbHksIGpvaW4zJGBEeXN0b3BpYSBSZXNpZHVhbGAsIA0KICAgICAgICAgICAgICAgICBqb2luMyRgUmVzaWR1YWwgVmFsdWVgLCBqb2luMyRgRWNvbm9teSAoR0RQIHBlciBDYXBpdGEpYCwNCiAgICAgICAgICAgICAgICAgbmFtZXMgPSBjKCJIYXBwaW5lc3MiLCAiRmFtaWx5IiwgIkR5cyBSZXNpZHVhbCIsICJSZXMgVmFsdWUiLCAiRWNvbm9teSIpLCANCiAgICAgICAgICAgICAgICAgbWFpbiA9ICJCb3hwbG90IHRvIGRldGVjdCBvdXRsaWVycyIpDQoNCmJveF80PC0gYm94cGxvdChqb2luMyRgSGVhbHRoIChMaWZlIEV4cGVjdGFuY3kpYCwgam9pbjMkRnJlZWRvbSwgam9pbjMkYFN0YW5kYXJkIEVycm9yYCwgDQogICAgICAgICAgICAgICAgam9pbjMkYFRydXN0IChHb3Zlcm5tZW50IENvcnJ1cHRpb24pYCwgam9pbjMkR2VuZXJvc2l0eSwNCiAgICAgICAgICAgICAgICBuYW1lcyA9IGMoIkhlYWx0aCIsICJGcmVlZG9tIiwgIlN0YW5kYXJkIEVyciIsICJUcnVzdCIsICJHZW5lcm9zaXR5IiksDQogICAgICAgICAgICAgICAgbWFpbiA9ICJCb3hwbG90IHRvIGRldGVjdCBvdXRsaWVycyIpDQoNCmJveHBsb3Qoam9pbjMkYFN0YW5kYXJkIEVycm9yYCwgbWFpbiA9ICJCb3hwbG90IG9mIFN0YW5kYXJkIEVycm9yIikkb3V0DQpib3hwbG90KGpvaW4zJGBUcnVzdCAoR292ZXJubWVudCBDb3JydXB0aW9uKWAsIG1haW4gPSAiQm94cGxvdCBvZiBUcnVzdCIpJG91dA0KDQpgYGANCjxicj4NCjxicj4NCg0KIyMJVHJhbnNmb3JtIA0KDQoqIFRoZSBsYXN0IHN0ZXAgaW4gb3VyIGFuYWx5c2lzIGlzIHRvIHBlcmZvcm0gdHJhbnNmb3JtYXRpb24uIFdlIHBlcmZvcm0gZGF0YSB0cmFuc2Zvcm1hdGlvbiB0byBzdGFuZGFyZGlzZSB0aGUgdmFyaWFibGVzLCB0byBtYWtlIHRoZSByZWxhdGlvbnNoaXBzIGxpbmVhciBhbmQgZXZlbiB0byBtYWtlIHRoZSBkaXN0cmlidXRpb24gbGVzcyBza2V3ZWQuDQoqIFdlIHBlcmZvcm0gZGF0YSB0cmFuc2Zvcm1hdGlvbiBvbiAzIHZhcmlhYmxlcyBoZXJlOiBSZXNpZHVhbCBWYWx1ZSwgRHlzdG9waWEgUmVzaWR1YWwgYW5kIFRydXN0IChHb3Zlcm5tZW50IENvcnJ1cHRpb24pLg0KKiBPbiBjaGVja2luZyB0aGUgaGlzdG9ncmFtIGZvciBUcnVzdCwgd2Ugc2VlIHRoYXQgaXQgaXMgc2tld2VkIHRvd2FyZHMgdGhlIHJpZ2h0LiBXZSB1c2Ugc3F1YXJlIHJvb3QgdHJhbnNmb3JtYXRpb24gbWV0aG9kIGFzIGl0IGNhbiByZWR1Y2UgcmlnaHQgc2tld25lc3MgYW5kIGNhbiBhbHNvIGJlIGFwcGxpZWQgdG8gemVybyB2YWx1ZXMuIFRoZSBmdW5jdGlvbiBzcXJ0KCkgd2lsbCBhcHBseSBzcXVhcmUgcm9vdCB0cmFuZm9ybWF0aW9uIGFuZCBhIGhpc3RvZ3JhbSBpcyBwbG90dGVkIHBvc3QgdHJhbnNmb3JtYXRpb24gd2hlcmUgdGhlIGRpc3RyaWJ1dGlvbiBpcyBzZWVuIGJlIG5vcm1hbC4NCiogTmV4dCB3ZSBjaGVjayB0aGUgaGlzdG9ncmFtIGZvciBSZXNpZHVhbCBWYWx1ZS4gQWx0aG91Z2ggdGhlIGRpc3RyaWJ1dGlvbiBzZWVtcyB0byBiZSBub3JtYWwsIGl0IGlzIG5vdCBwZXJmZWN0LiBCb3gtQ294IG1ldGhvZCBpcyBhIHR5cGUgb2YgcG93ZXIgdHJhbnNmb3JtYXRpb24gdXNlZCB0byB0cmFuc2Zvcm0gbm9uLW5vcm1hbCBkYXRhIGludG8gbm9ybWFsIGRpc3RyaWJ1dGlvbi4gV2UgdXNlIHRoZSBwYWNrYWdlICJmb3JlY2FzdCIgdG8gcGVyZm9ybSB0aGlzIGZ1bmN0aW9uLiBUbyBnZXQgdGhlIGJlc3QgQm94LUNveCB0cmFuc2Zvcm1hdGlvbiwgd2UgdXNlIEJveENveCgpIGZ1bmN0aW9uIHVzaW5nIGxhbWJkYT0iYXV0byIgYXJndW1lbnQuIFVwb24gcGxvdHRpbmcgdGhlIGhpc3RvZ3JhbSBhZnRlciB0aGUgdHJhbnNmb3JtYXRpb24sIHdlIHNlZSB0aGF0IHRoZSBkaXN0cmlidXRpb24gaXMgcGVyZmVjdGx5IG5vcm1hbC4NCiogV2UgY2hlY2sgdGhlIGhpc3RvZ3JhbSBmb3IgRHlzdG9waWEgUmVzaWR1YWwuIFRoaXMgYWxzbyBhcHBlYXJzIHRvIGJlIG5vcm1hbGx5IGRpc3RyaWJ1dGVkIGJ1dCBub3QgcGVyZmVjdGx5IHN5bW1ldHJpYy4gSGVyZSBhbHNvIHdlIHBlcmZvcm0gQm94Q294KCkgZnVuY3Rpb24gd2l0aCBsYWJtZGE9ImF1dG8iLiBUaGUgaGlzdG9ncmFtIGFmdGVyIHRyYW5zZm9ybWF0aW9uIGdpdmVzIGEgbXVjaCBiZXR0ZXIgc3ltbWV0cmljIGxvb2tpbmcgZGlzdHJpYnV0aW9uLg0KDQpgYGB7cn0NCiNUcnVzdCAoR292ZXJubWVudCBDb3JydXB0aW9uKQ0KaGlzdChqb2luMyRgVHJ1c3QgKEdvdmVybm1lbnQgQ29ycnVwdGlvbilgLCBtYWluID0gIkhpc3RvZ3JhbSBvZiBUcnVzdCIsDQogICAgIHhsYWIgPSAiVHJ1c3QgKEdvdmVybm1lbnQgQ29ycnVwdGlvbikiKQ0Kc3FydF90cnVzdDwtIHNxcnQoam9pbjMkYFRydXN0IChHb3Zlcm5tZW50IENvcnJ1cHRpb24pYCkNCmhpc3Qoc3FydF90cnVzdCwgbWFpbiA9ICJUcnVzdCAoYWZ0ZXIgVHJhbnNmb3JtYXRpb24pIiwNCiAgICAgeGxhYiA9ICJUcnVzdCAoR292ZXJubWVudCBDb3JydXB0aW9uKSIpDQoNCiMgUmVzaWR1YWwgdmFsdWUNCmhpc3Qoam9pbjMkYFJlc2lkdWFsIFZhbHVlYCwgbWFpbiA9ICJIaXN0b2dyYW0gb2YgUmVzaWR1YWwgVmFsdWUiLCANCiAgICAgeGxhYiA9ICJSZXNpZHVhbCBWYWx1ZSIpDQpib3hfcmVzaWR1YWwgPC0gQm94Q294KGpvaW4zJGBSZXNpZHVhbCBWYWx1ZWAsIGxhbWJkYSA9ICJhdXRvIikNCmhpc3QoYm94X3Jlc2lkdWFsLCBtYWluID0gIlJlc2lkdWFsIFZhbHVlIChhZnRlciBUcmFuc2Zvcm1hdGlvbikiLA0KICAgICB4bGFiID0gIlJlc2lkdWFsIFZhbHVlIikNCg0KIyBEeXN0b3BpYSByZXNpZHVhbA0KaGlzdChqb2luMyRgRHlzdG9waWEgUmVzaWR1YWxgLCBtYWluID0gIkhpc3RvZ3JhbSBvZiBEeXN0b3BpYSBSZXNpZHVhbCIsDQogICAgIHhsYWIgPSAiRHlzdG9waWEgUmVzaWR1YWwiKQ0KYm94X2R5c3RvcGlhIDwtIEJveENveChqb2luMyRgRHlzdG9waWEgUmVzaWR1YWxgLCBsYW1iZGEgPSAiYXV0byIpDQpoaXN0KGJveF9keXN0b3BpYSwgbWFpbiA9ICJEeXN0b3BpYSBSZXNpZHVhbCAoYWZ0ZXIgVHJhbnNmb3JtYXRpb24pIiwNCiAgICAgeGxhYiA9ICJEeXN0b3BpYSBSZXNpZHVhbCIpDQoNCmBgYA0KPGJyPg0KPGJyPg0KDQojIyBSZWZlcmVuY2VzDQoNCiogS2FnZ2xlLmNvbS4gMjAxOS4gV29ybGQgSGFwcGluZXNzIFJlcG9ydC4gW29ubGluZV0gQXZhaWxhYmxlIGF0OiA8aHR0cHM6Ly93d3cua2FnZ2xlLmNvbS91bnNkc24vd29ybGQtaGFwcGluZXNzPg0KDQoqIEthZ2dsZS5jb20uIDIwMTcuIFdvcmxkIENvdW50cmllcyBBbmQgQ29udGluZW50cyBEZXRhaWxzLiBbb25saW5lXSBBdmFpbGFibGUgYXQ6IDxodHRwczovL3d3dy5rYWdnbGUuY29tL2ZvbGFyYXovd29ybGQtY291bnRyaWVzLWFuZC1jb250aW5lbnRzLWRldGFpbHM/c2VsZWN0PUNvdW50cmllcytMb25naXR1ZGUrYW5kK0xhdGl0dWRlLmNzdj4NCg0KKiBEb2xndW4sIEFuaWwgMjAxMCwg4oCYRGF0YSBXcmFuZ2xpbmfigJksIGxlY3R1cmUgbm90ZXMsIE1BVEgyMzQ5LCBSTUlUIFVuaXZlcnNpdHksIDxodHRwOi8vcmFyZS1waG9lbml4LTE2MTYxMC5hcHBzcG90LmNvbS9zZWN1cmVkL2luZGV4Lmh0bWw+DQoNCiogQmFnbGluLCBKYW1lcyAyMDEwLCDigJhBcHBsaWVkIEFuYWx5dGljc+KAmSwgbGVjdHVyZSBub3RlcywgTUFUSDEzMjQsIFJNSVQgVW5pdmVyc2l0eSwgPGh0dHBzOi8vYXN0cmFsLXRoZW9yeS0xNTc1MTAuYXBwc3BvdC5jb20vc2VjdXJlZC9pbmRleC5odG1sPg0KDQoqIHdpa2lIb3cuIDIwMjAuIEhvdyBUbyBXcml0ZSBMYXRpdHVkZSBBbmQgTG9uZ2l0dWRlLiBbb25saW5lXSBBdmFpbGFibGUgYXQ6IDxodHRwczovL3d3dy53aWtpaG93LmNvbS9Xcml0ZS1MYXRpdHVkZS1hbmQtTG9uZ2l0dWRlIzp+OnRleHQ9U3RhcnQlMjB3aXRoJTIweW91ciUyMGxpbmUlMjBvZixvciUyMFdlc3QlMjBhcyUyMHRoZSUyMGRpcmVjdGlvbi4+DQoNCjxicj4NCjxicj4NCg==