Required packages
library("dplyr")
library("tidyr")
library("readr")
Executive Summary
The aim of this report is to preprocess the combined data of emission rates and surface temperature of countries around the world across couple of centuries which could be used to find (if any) correlation between the two variables.
In the Tidy & Manipulate sections, we first tidy the tables separately by using tidyr functions like gather, separate and drop unwanted columns (variables). We make sure both the tables are in the same format so that we can join them correctly. After merging the tables, we carry out any type conversions that may be required. We then manipulate the data using mutate function to add new variables.
In the SCAN sections, we scan for missing values after we are done tidying and manipulating the data. We handle the missing values with the appropriate methods, we impute the missing values with the mean values of the remaining data. Scanning for outliers is the next part of the preprocessing and we find out that we require to Transform the data to make it more appropriate for our evaluation of the correlation between the emission rates and the surface temperature. After carrying out the transformation we again scan for outliers and deal with them with suitable methods.
Completing all these steps makes the data ready for evaluation and we end the report by displaying a scatter plot showing the relation of the emission rates and surface temperatures.
Data
This source has repackaged the data from compilation put together by the Berkeley Earth, which is affiliated with Lawrence Berkeley National Laboratory. The Berkeley Earth Surface Temperature Study combines 1.6 billion temperature reports from 16 pre-existing archives. The raw data comes from the Berkeley Earth data page.
This source has multiple files that give temperature data for the world. We select the CSV file - GlobalLandTemperaturesByCountry.csv as our first data source.
The GlobalLandTemperaturesByCountry data set consists of the following variables:
dt [date]: Date (yyyy-mm-dd) when the temperture was recorded, starts from the year 1743
AverageTemperature [numeric]: Average Land Temperature in Celsius
AverageTemperatureUncertainty [numeric]: the 95% confidence interval around the average temperature
Country [character]: name of the country for which the temperature is recorded
This dataset contains emission data of Green House Gases (in tonnes) of different countries from year 1750 - 2017 taken from https://ourworldindata.org/co2-and-other-greenhouse-gas-emissions
Our second data source is the CSV file - emission data.csv
The emission data data set consists of the following variables:
Country [character]: name of the country for which the emission data is recorded
1751, 1752,..., 2018, 2019 [numeric]: the year that data was collected. Gives the emission data recorded in tonnes.
Reading the Data:
Importing the data file emission data.csv:
emissionData <- read_csv("emission data.csv")
The first 6 observations of the emission data data set are:
head(emissionData)
Importing the data file GlobalLandTemperaturesByCountry.csv:
temperatureData <- read_csv("GlobalLandTemperaturesByCountry.csv")
The first 6 observations of the GlobalLandTemperaturesByCountry data set are:
head(temperatureData)
NOTE: We do not merge the datasets in this section as the datasets need to be tidied before we can proceed to merge them. The data sets are merged in the Merging Datasets section after Tidy & Manipulate Data I.
Understand
1. Checking the dimensions and structure of the data frame emissionData:
dim(emissionData)
[1] 231 268
str(emissionData[,1:8])
Classes ‘tbl_df’, ‘tbl’ and 'data.frame': 231 obs. of 8 variables:
$ Country: chr "Afghanistan" "Africa" "Albania" "Algeria" ...
$ 1751 : num 0 0 0 0 0 0 0 0 0 0 ...
$ 1752 : num 0 0 0 0 0 0 0 0 0 0 ...
$ 1753 : num 0 0 0 0 0 0 0 0 0 0 ...
$ 1754 : num 0 0 0 0 0 0 0 0 0 0 ...
$ 1755 : num 0 0 0 0 0 0 0 0 0 0 ...
$ 1756 : num 0 0 0 0 0 0 0 0 0 0 ...
$ 1757 : num 0 0 0 0 0 0 0 0 0 0 ...
The columns 9-268 all give the emission reading for a year and are of numeric class. These aren’t shown above to improve readability.
2. Checking the attributes of emissionData:
- Checking the data type of the
Country variable:
class(emissionData$Country)
[1] "character"
typeof(emissionData$Country)
[1] "character"
The variable Country gives the name of the country. The variable is of type character. No type conversions are needed.
- Checking the data type of the
1751 variable:
class(emissionData$`1751`)
[1] "numeric"
typeof(emissionData$`1751`)
[1] "double"
The variable 1751 gives emission readings for the year 1751. The variable is of type double. The emission readings are continuous numeric values and cannot be factored.
The remaining variables 1752 - 2019 are all numeric and give the emission values. We do not individually check the type of all the variables due to large number of variables.
3. Checking the dimensions and structure of the data frame temperatureData:
dim(temperatureData)
[1] 577462 4
str(temperatureData, give.attr = FALSE)
Classes ‘spec_tbl_df’, ‘tbl_df’, ‘tbl’ and 'data.frame': 577462 obs. of 4 variables:
$ dt : Date, format: "1743-11-01" "1743-12-01" "1744-01-01" "1744-02-01" ...
$ AverageTemperature : num 4.38 NA NA NA NA ...
$ AverageTemperatureUncertainty: num 2.29 NA NA NA NA ...
$ Country : chr "Ã…land" "Ã…land" "Ã…land" "Ã…land" ...
4. Checking the attributes of temperatureData:
- Checking the data type of the
dt variable:
class(temperatureData$dt)
[1] "Date"
typeof(temperatureData$dt)
[1] "double"
The variable dt is of class Date. dt gives the date when the temperature was recorded. The Date type is correct and no type conversion is made.
- Checking the data type of the
AverageTemperature variable:
class(temperatureData$AverageTemperature)
[1] "numeric"
typeof(temperatureData$AverageTemperature)
[1] "double"
The variable AverageTemperature gives average temperature value. The variable is numeric. The temperature readings are continuous numeric values and cannot be factored.
- Checking the data type of the
AverageTemperatureUncertainty variable:
class(temperatureData$AverageTemperatureUncertainty)
[1] "numeric"
typeof(temperatureData$AverageTemperatureUncertainty)
[1] "double"
The variable AverageTemperatureUncertainty is numeric. AverageTemperatureUncertainty has continuous numeric values and cannot be factored. The type numeric is correct. No changes are made.
- Checking the data type of the
Country variable:
class(temperatureData$Country)
[1] "character"
typeof(temperatureData$Country)
[1] "character"
The variable Country gives the name of the country. The variable is of type character. No type conversions are needed.
NOTE: As you can see we do not have any variables that we can factor. We factor year after we have gathered it in the Merging Datasets section after we merge the two datasets. We also create a new variable and factor it in section Tidy & Manipulate Data II.
Tidy & Manipulate Data I
1. Gather emissionData:
The columns 2-268 give the emission reading measured for the years 1751-2017. The columns 2-268 contain year data in the column names, these column names are gathered to form a new variable year.
-c(Country) denotes that we gather all the columns except the Country column.
emissionDataTidy <- emissionData %>% gather(-c(Country),key = "year", value = "emission")
emissionDataTidy
Dimensions of emissionData after gather():
dim(emissionDataTidy)
[1] 61677 3
3. Drop month, day and AverageTemperatureUncertainty columns from temperatureData:
We only need the year column, so we drop the month and day columns. The column AverageTemperatureUncertainty gives the 95% confidence interval around the average temperature. As we do not need this data for our evaluation, we drop this column too.
temperatureDataTidy <- temperatureDataTidy %>% select(-month, -day, -AverageTemperatureUncertainty)
temperatureDataTidy
Dimensions of temperatureDataTidy:
dim(emissionDataTidy)
[1] 61677 3
4. Group temperatureData by year and Country:
The temperatureDataTidy data set contains multiple rows for each country each corresponding to the same year. This is beacuse temperatureData had entries for different months of the same year. Now since we dropeed the month and kept only the year, we see multiple rows for the same year. To tidy the data, the data is grouped by year & Country to obtain the new average temperature value AverageTemperature for each year for each country.
temperatureDataTidy <- temperatureDataTidy %>% group_by(year, Country) %>% summarise(AverageTemperature = mean(AverageTemperature, na.rm = TRUE))
temperatureDataTidy
Dimensions of temperatureDataTidy after grouping:
dim(emissionDataTidy)
[1] 61677 3
Merging Datasets
We merge the two datasets - emissionDataTidy and temperatureDataTidy. We use the pair of variables Country and year as the key to join the tables using inner_join().
emissionTemperatureJoined <- inner_join(emissionDataTidy, temperatureDataTidy, by = c("Country","year"))
emissionTemperatureJoined
Dimensions of emissionTemperatureJoined:
dim(emissionTemperatureJoined)
[1] 37437 4
Factoring year variable
Examining the number of unique values for year
cat("Number of unique values for `year`:", length(unique(emissionTemperatureJoined$year)),
"\nNumber of rows in the data frame `emissionTemperatureJoined`:", nrow(emissionTemperatureJoined))
Number of unique values for `year`: 263
Number of rows in the data frame `emissionTemperatureJoined`: 37437
The number of unique values for year is much smaller than the size of the data frame emissionTemperatureJoined. Therefore we proceed to factor year.
We factor the year variable and order it.
emissionTemperatureJoined$year <- factor(emissionTemperatureJoined$year, levels= unique(emissionTemperatureJoined$year), ordered = TRUE)
str(emissionTemperatureJoined$year)
Ord.factor w/ 263 levels "1751"<"1752"<..: 1 1 1 1 1 1 1 1 1 1 ...
Tidy & Manipulate Data II
In this section we create a new variable thanMeanEmission, this variable will tell us if the emission value is higher or lower than the Mean Emission of all the countries for the corresponding year.
1. Calculating the mean emission value meanEmissionByYear for each year:
meanEmissionByYearData <- emissionTemperatureJoined %>%
group_by(year) %>%
summarise(meanEmissionByYear = mean(emission, na.rm = TRUE))
meanEmissionByYearData
2. Add the meanEmissionByYear variable to emissionTemperatureJoined joining by year:
The new data frame is named joinedData.
joinedData <- left_join(emissionTemperatureJoined, meanEmissionByYearData,"year")
joinedData
3. Use meanEmissionByYear to create a new variable thanMeanEmission using mutate():
Code referred from: Link
joinedData <- mutate(joinedData, thanMeanEmission = ifelse(emission > meanEmissionByYear, "Higher", "Lower"))
joinedData
4. Factoring thanMeanEmission :
joinedData$thanMeanEmission <- factor(joinedData$thanMeanEmission,
levels = c("Lower", "Higher"),
ordered = TRUE)
str(joinedData$thanMeanEmission)
Ord.factor w/ 2 levels "Lower"<"Higher": 1 1 1 1 1 1 1 1 1 1 ...
5. Dropping meanEmissionByYear:
We can see that the column meanEmissionByYear has the same value for multiple rows. Having repeated values isn’t desirable, so we drop this column. We just added this column to create the column thanMeanEmission.
Now that meanEmissionByYear has served its purpose, we drop it.
joinedData <- joinedData %>% select(-meanEmissionByYear)
str(joinedData)
Classes ‘tbl_df’, ‘tbl’ and 'data.frame': 37437 obs. of 5 variables:
$ Country : chr "Albania" "Andorra" "Austria" "Belarus" ...
$ year : Ord.factor w/ 263 levels "1751"<"1752"<..: 1 1 1 1 1 1 1 1 1 1 ...
$ emission : num 0 0 0 0 0 0 0 0 0 0 ...
$ AverageTemperature: num 13.57 12.2 7.18 6.69 10.15 ...
$ thanMeanEmission : Ord.factor w/ 2 levels "Lower"<"Higher": 1 1 1 1 1 1 1 1 1 1 ...
If we still do however need the mean emission for a year, we can obtain it from the data frame meanEmissionByYearData. This data frame has just 263 rows compared to the 37437 rows the variable meanEmissionByYear had in joinedData.
str(meanEmissionByYearData)
Classes ‘tbl_df’, ‘tbl’ and 'data.frame': 263 obs. of 2 variables:
$ year : Ord.factor w/ 263 levels "1751"<"1752"<..: 1 2 3 4 5 6 7 8 9 10 ...
$ meanEmissionByYear: num 259737 519576 719459 959404 1199443 ...
Scan I
In this section we detect and deal with missing values.
1. Checking for Missing Values:
colSums(is.na(joinedData))
Country year emission AverageTemperature thanMeanEmission
0 0 0 1676 0
Only AverageTemperature column has missing values. We examine the missing values in the next step.
2. Examining the Missing Values:
joinedData %>% filter(is.na(joinedData$AverageTemperature))
sum(is.nan(joinedData$AverageTemperature))
[1] 1676
We can see that all the missing values are NaN, and they were generated as a result of calculation of mean_temp using mean() with na.rm = true. This resulted in an NaN value when all AverageTemperature values are NA for a particular country and year.
3. Calculating mean temperature for each Country to impute the missing values:
We choose to impute the missing values with the mean value of AverageTemperature. However, replacing the missing values with the mean of the all the Average temperatures isn’t wise. Instead, we impute the missing values with the mean Average temperatures of its corresponding country.
For example, the missing value of Average temperature for Bahamas for the year 1761 will be imputed with the mean temperatures of Bahamas for the rest of the years (1751 - 2019), ignoring any missing values, if any.
Here we calculate the mean temperature for each country through all the years (1751 - 2019) and not just one year.
countryMeanTemp <- joinedData %>%
group_by(Country) %>%
summarise(meanTempByCountry = mean(AverageTemperature, na.rm = TRUE))
countryMeanTemp
sum(is.na(countryMeanTemp))
[1] 0
The mean we calculated above doesn’t have any missing values. Now we can be sure that we do not impute any missing values with NA or NaN.
4. Combine countryMeanTemp with joinedData:
Before we impute the missing values, we need to combine the mean we calculated - countryMeanTemp with joinedData.
joinedData <- left_join(joinedData, countryMeanTemp, "Country")
joinedData
Checking the number of missing values before imputing them:
colSums(is.na(joinedData))
Country year emission AverageTemperature thanMeanEmission
0 0 0 1676 0
meanTempByCountry
0
5. Imputing missing values:
joinedData_imputed <- mutate(joinedData, AverageTemperature = ifelse(is.na(AverageTemperature),
meanTempByCountry,
AverageTemperature))
Checking the number of missing values after imputing them:
colSums(is.na(joinedData_imputed))
Country year emission AverageTemperature thanMeanEmission
0 0 0 0 0
meanTempByCountry
0
The output above shows us that all the missing values have been imputed successfully.
6. Dropping meanTempByCountry:
Just like meanEmissionByYear, meanTempByCountry also has same values for multiple rows. As having repeated values isn’t desirable, so we drop this column too. We just added this column to impute the missing values in AverageTemperature. Now that meanTempByCountry has served its purpose, we drop it.
joinedData_imputed <- joinedData_imputed %>% select(-meanTempByCountry)
str(joinedData_imputed)
Classes ‘tbl_df’, ‘tbl’ and 'data.frame': 37437 obs. of 5 variables:
$ Country : chr "Albania" "Andorra" "Austria" "Belarus" ...
$ year : Ord.factor w/ 263 levels "1751"<"1752"<..: 1 1 1 1 1 1 1 1 1 1 ...
$ emission : num 0 0 0 0 0 0 0 0 0 0 ...
$ AverageTemperature: num 13.57 12.2 7.18 6.69 10.15 ...
$ thanMeanEmission : Ord.factor w/ 2 levels "Lower"<"Higher": 1 1 1 1 1 1 1 1 1 1 ...
If we still do however need the mean tempereature for a country, we can obtain it from the data frame countryMeanTemp. This data frame has just 189 rows compared to the 37437 rows the variable meanTempByCountry had in joinedData_new_imputed.
str(countryMeanTemp)
Classes ‘tbl_df’, ‘tbl’ and 'data.frame': 189 obs. of 2 variables:
$ Country : chr "Afghanistan" "Africa" "Albania" "Algeria" ...
$ meanTempByCountry: num 14.1 24.1 12.6 22.9 11.2 ...
Scan II
Lets take a look at our dataset:
str(joinedData_imputed)
Classes ‘tbl_df’, ‘tbl’ and 'data.frame': 37437 obs. of 5 variables:
$ Country : chr "Albania" "Andorra" "Austria" "Belarus" ...
$ year : Ord.factor w/ 263 levels "1751"<"1752"<..: 1 1 1 1 1 1 1 1 1 1 ...
$ emission : num 0 0 0 0 0 0 0 0 0 0 ...
$ AverageTemperature: num 13.57 12.2 7.18 6.69 10.15 ...
$ thanMeanEmission : Ord.factor w/ 2 levels "Lower"<"Higher": 1 1 1 1 1 1 1 1 1 1 ...
We have only two numeric values, emission and AverageTemperature
Examine emission variable for outliers:
joinedData_imputed %>% summary()
Country year emission AverageTemperature thanMeanEmission
Length:37437 1883 : 189 Min. :0.000e+00 Min. :-22.62 Lower :34654
Class :character 1884 : 189 1st Qu.:0.000e+00 1st Qu.: 9.99 Higher: 2783
Mode :character 1885 : 189 Median :0.000e+00 Median : 21.23
1886 : 189 Mean :1.327e+09 Mean : 17.65
1887 : 189 3rd Qu.:3.582e+07 3rd Qu.: 25.61
1888 : 189 Max. :3.780e+11 Max. : 30.74
(Other):36303
cat("Number of outliers: ", length(boxplot(joinedData_imputed$emission)$out))
Number of outliers: 7697

We find a very odd boxplot, this is because most of the data contains 0 as the emission rate. We even get median = 0. From the boxplot we can see that there are a huge number of outliers, this is because in our data, a larger number of entries have the emission value as 0, which is skewing our data towards 0. The zeroes could be entered to represent missing values, or the actual value of zero emission. The source was unclear about the nature of 0 value in emission column. We consider the zeros to represent 0 emission for the purpose of this assignment.
Examining the percentage of zero entries in emission:
joinedData_imputed %>% filter(emission == 0) %>% nrow()
[1] 21609
joinedData_imputed %>% nrow()
[1] 37437
cat("Percentage of rows with zero emission:", 21609/37437 *100)
Percentage of rows with zero emission: 57.72097
After analyzing the data closely, we find that for the data before 1950s, most countries had 0 emission rate. Also in the later years there are few countries such as USA, India and China that have exponentially huge values of emissions.
Since the application of our dataset could be to find the correlation (if any) between the rise in temperature and the emission rate, by removing the outliers we find (extremely large emission values), we may lose valuable results/insights of the impacts of the large emission by these handful of countries on the rise of global temperature.
As the dataset gives emission values beginning from the year 1751, the reason for the zeros could be the unavailability of data for such early years. Instead of excluding or imputing all the 0 values in our dataset, we choose to filter the data and consider the data from the year 1955 onwards, as almost all countries have 0 emission rates until the 1950s.
Filtering joinedData_imputed after the year 1955
dataAfter1955<- joinedData_imputed %>% filter(year >= 1955)
dataAfter1955 %>% filter(emission == 0) %>% nrow()
[1] 531
dataAfter1955 %>% nrow()
[1] 11151
cat("Percentage of rows with zero emission:", 531/11151 *100)
Percentage of rows with zero emission: 4.761905
Here we can see that the percentage of zeros has reduced drastically in the data.
Checking for outliers in the filtered data:
cat("Number of outliers in `dataAfter1955`: ", length(boxplot(dataAfter1955$emission)$out))
Number of outliers in `dataAfter1955`: 1768

NOTE: We first tranform our data and then deal with the outliers in Scan III after Transform.
Scan III
2. Examining with outliers for emission
length(outliers[outliers != 0])
[1] 6
length(outliers[outliers == 0])
[1] 531
The majority of the outliers 531/537 * 100 = 98.8826816% have the value zero. Zero corresponds to 1 (e^0 = 1) in the non-transformed data. Hence, by removing the outliers, we are effectively removing the rows with 0 as their emission, which is what we were aiming for.
3. Removing with outliers from emission
dataAfter1955 <- dataAfter1955[-(which(dataAfter1955$emission %in% outliers)),]
boxplot(dataAfter1955$emission)

4. Examine AverageTemperature variable for outliers:
Now we look at the second numeric variable - AverageTemperature. We consider the dataAfter1955 data frame iteslf.
cat("Number of outliers: " , length(boxplot(dataAfter1955$AverageTemperature)$out))
Number of outliers: 118

length(dataAfter1955$AverageTemperature)
[1] 10614
cat("Percentage of outliers: ", 118/10614 * 100)
Percentage of outliers: 1.111739
5. Dealing with outliers in AverageTemperature:
We find that there are 118 outliers, which is roughly around 1.11%. These outliers correspond to countries that are exteremely cold places with temperatures that drop below -10°C. Therefore deleting them would not be safe way to handle these outliers. Hence we have opted for using the Capping method to bring these extreme values to the closest quantile.
Referred from: Module 6 Notes | Stackoverflow
Capping the outliers:
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
}
dataAfter1955$AverageTemperature <- dataAfter1955$AverageTemperature %>% cap()
Boxplot after capping the outliers:
boxplot(dataAfter1955$AverageTemperature)

Examine variables
We finally show the scatter plot of emisison and temperature. We apply exp() on the emission variable to get back the untransformed data to plot against Temperature.
dataAfter1955 %>% plot(exp(emission) ~ AverageTemperature, data = ., ylab="Emission", xlab="Temperature", main="Emission by Temperature")

LS0tCnRpdGxlOiAiTUFUSDIzNDkgU2VtZXN0ZXIgMiwgMjAxOSIKYXV0aG9yOgotIE1hYXogU2hhaWtoIC0gUzM3OTU2MDMKLSBWYWlzaG5hdmkgTmFyYXlhbmEgTmFpayAtIFMzNzk3NDQyCnN1YnRpdGxlOiBBc3NpZ25tZW50IDMKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQKLS0tCgojIyBSZXF1aXJlZCBwYWNrYWdlcyAKYGBge3IsIGVjaG8gPSBUUlVFLCB3YXJuaW5ncyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CmxpYnJhcnkoImRwbHlyIikKbGlicmFyeSgidGlkeXIiKQpsaWJyYXJ5KCJyZWFkciIpCmBgYAoKPGhyIHN0eWxlPSJib3JkZXI6IDAuNXB4IHNvbGlkIFNpbHZlcjsgIGFsaWduOmNlbnRlciI+CgojIyBFeGVjdXRpdmUgU3VtbWFyeSAKClRoZSBhaW0gb2YgdGhpcyByZXBvcnQgaXMgdG8gcHJlcHJvY2VzcyB0aGUgY29tYmluZWQgZGF0YSBvZiBlbWlzc2lvbiByYXRlcyBhbmQgc3VyZmFjZSB0ZW1wZXJhdHVyZSBvZiBjb3VudHJpZXMgYXJvdW5kIHRoZSB3b3JsZCBhY3Jvc3MgY291cGxlIG9mIGNlbnR1cmllcyB3aGljaCBjb3VsZCBiZSB1c2VkIHRvIGZpbmQgKGlmIGFueSkgY29ycmVsYXRpb24gYmV0d2VlbiB0aGUgdHdvIHZhcmlhYmxlcy4KCkluIHRoZSBgVGlkeSAmIE1hbmlwdWxhdGVgIHNlY3Rpb25zLCB3ZSBmaXJzdCB0aWR5IHRoZSB0YWJsZXMgc2VwYXJhdGVseSBieSB1c2luZyBgdGlkeXJgIGZ1bmN0aW9ucyBsaWtlIGdhdGhlciwgc2VwYXJhdGUgYW5kIGRyb3AgdW53YW50ZWQgY29sdW1ucyAodmFyaWFibGVzKS4gV2UgbWFrZSBzdXJlIGJvdGggdGhlIHRhYmxlcyBhcmUgaW4gdGhlIHNhbWUgZm9ybWF0IHNvIHRoYXQgd2UgY2FuIGpvaW4gdGhlbSBjb3JyZWN0bHkuIEFmdGVyIG1lcmdpbmcgdGhlIHRhYmxlcywgd2UgY2Fycnkgb3V0IGFueSB0eXBlIGNvbnZlcnNpb25zIHRoYXQgbWF5IGJlIHJlcXVpcmVkLiBXZSB0aGVuIG1hbmlwdWxhdGUgdGhlIGRhdGEgdXNpbmcgbXV0YXRlIGZ1bmN0aW9uIHRvIGFkZCBuZXcgdmFyaWFibGVzLgoKSW4gdGhlIGBTQ0FOYCBzZWN0aW9ucywgd2Ugc2NhbiBmb3IgbWlzc2luZyB2YWx1ZXMgYWZ0ZXIgd2UgYXJlIGRvbmUgdGlkeWluZyBhbmQgbWFuaXB1bGF0aW5nIHRoZSBkYXRhLiBXZSBoYW5kbGUgdGhlIG1pc3NpbmcgdmFsdWVzIHdpdGggdGhlIGFwcHJvcHJpYXRlIG1ldGhvZHMsIHdlIGltcHV0ZSB0aGUgbWlzc2luZyB2YWx1ZXMgd2l0aCB0aGUgbWVhbiB2YWx1ZXMgb2YgdGhlIHJlbWFpbmluZyBkYXRhLiBTY2FubmluZyBmb3Igb3V0bGllcnMgaXMgdGhlIG5leHQgcGFydCBvZiB0aGUgcHJlcHJvY2Vzc2luZyBhbmQgd2UgZmluZCBvdXQgdGhhdCB3ZSByZXF1aXJlIHRvIGBUcmFuc2Zvcm1gIHRoZSBkYXRhIHRvIG1ha2UgaXQgbW9yZSBhcHByb3ByaWF0ZSBmb3Igb3VyIGV2YWx1YXRpb24gb2YgdGhlIGNvcnJlbGF0aW9uIGJldHdlZW4gdGhlIGVtaXNzaW9uIHJhdGVzIGFuZCB0aGUgc3VyZmFjZSB0ZW1wZXJhdHVyZS4gQWZ0ZXIgY2Fycnlpbmcgb3V0IHRoZSB0cmFuc2Zvcm1hdGlvbiB3ZSBhZ2FpbiBzY2FuIGZvciBvdXRsaWVycyBhbmQgZGVhbCB3aXRoIHRoZW0gd2l0aCBzdWl0YWJsZSBtZXRob2RzLgoKQ29tcGxldGluZyBhbGwgdGhlc2Ugc3RlcHMgbWFrZXMgdGhlIGRhdGEgcmVhZHkgZm9yIGV2YWx1YXRpb24gYW5kIHdlIGVuZCB0aGUgcmVwb3J0IGJ5IGRpc3BsYXlpbmcgYSBzY2F0dGVyIHBsb3Qgc2hvd2luZyB0aGUgcmVsYXRpb24gb2YgdGhlIGVtaXNzaW9uIHJhdGVzIGFuZCBzdXJmYWNlIHRlbXBlcmF0dXJlcy4KCjxociBzdHlsZT0iYm9yZGVyOiAwLjVweCBzb2xpZCBTaWx2ZXI7ICBhbGlnbjpjZW50ZXIiPgoKIyMgRGF0YSAKIyMjIFNvdXJjZSAxOiBbQ2xpbWF0ZSBDaGFuZ2U6IEVhcnRoIFN1cmZhY2UgVGVtcGVyYXR1cmUgRGF0YV0oaHR0cHM6Ly93d3cua2FnZ2xlLmNvbS9iZXJrZWxleWVhcnRoL2NsaW1hdGUtY2hhbmdlLWVhcnRoLXN1cmZhY2UtdGVtcGVyYXR1cmUtZGF0YSkKClRoaXMgc291cmNlIGhhcyByZXBhY2thZ2VkIHRoZSBkYXRhIGZyb20gY29tcGlsYXRpb24gcHV0IHRvZ2V0aGVyIGJ5IHRoZSAqKltCZXJrZWxleSBFYXJ0aF0oaHR0cDovL2JlcmtlbGV5ZWFydGgub3JnL2Fib3V0LykqKiwgd2hpY2ggaXMgYWZmaWxpYXRlZCB3aXRoIExhd3JlbmNlIEJlcmtlbGV5IE5hdGlvbmFsIExhYm9yYXRvcnkuIFRoZSBCZXJrZWxleSBFYXJ0aCBTdXJmYWNlIFRlbXBlcmF0dXJlIFN0dWR5IGNvbWJpbmVzIDEuNiBiaWxsaW9uIHRlbXBlcmF0dXJlIHJlcG9ydHMgZnJvbSAxNiBwcmUtZXhpc3RpbmcgYXJjaGl2ZXMuClRoZSByYXcgZGF0YSBjb21lcyBmcm9tIHRoZSBbQmVya2VsZXkgRWFydGggZGF0YSBwYWdlXShodHRwOi8vYmVya2VsZXllYXJ0aC5vcmcvZGF0YS8pLgoKVGhpcyBzb3VyY2UgaGFzIG11bHRpcGxlIGZpbGVzIHRoYXQgZ2l2ZSB0ZW1wZXJhdHVyZSBkYXRhIGZvciB0aGUgd29ybGQuICoqV2Ugc2VsZWN0IHRoZSBDU1YgZmlsZSAtIGBHbG9iYWxMYW5kVGVtcGVyYXR1cmVzQnlDb3VudHJ5LmNzdmAgYXMgb3VyIGZpcnN0IGRhdGEgc291cmNlLioqCgpUaGUgYEdsb2JhbExhbmRUZW1wZXJhdHVyZXNCeUNvdW50cnlgIGRhdGEgc2V0IGNvbnNpc3RzIG9mIHRoZSBmb2xsb3dpbmcgdmFyaWFibGVzOgoKICAqIGBkdCBbZGF0ZV1gOiAqKkRhdGUqKiAoeXl5eS1tbS1kZCkgd2hlbiB0aGUgdGVtcGVydHVyZSB3YXMgcmVjb3JkZWQsIHN0YXJ0cyBmcm9tIHRoZSB5ZWFyIDE3NDMKICAKICAqIGBBdmVyYWdlVGVtcGVyYXR1cmUgW251bWVyaWNdYDogKipBdmVyYWdlIExhbmQgVGVtcGVyYXR1cmUqKiBpbiBDZWxzaXVzIAogIAogICogYEF2ZXJhZ2VUZW1wZXJhdHVyZVVuY2VydGFpbnR5IFtudW1lcmljXWA6IHRoZSAqKjk1JSBjb25maWRlbmNlIGludGVydmFsKiogYXJvdW5kIHRoZSBhdmVyYWdlIHRlbXBlcmF0dXJlCiAgCiAgKiBgQ291bnRyeSBbY2hhcmFjdGVyXWA6IG5hbWUgb2YgdGhlIGNvdW50cnkgZm9yIHdoaWNoIHRoZSB0ZW1wZXJhdHVyZSBpcyByZWNvcmRlZAoKIyMjIyMgRGF0YXNldCBEb3dubG9hZCBMaW5rOiAqKltHbG9iYWxMYW5kVGVtcGVyYXR1cmVzQnlDb3VudHJ5LmNzdiBdKGh0dHBzOi8vd3d3LmthZ2dsZS5jb20vYmVya2VsZXllYXJ0aC9jbGltYXRlLWNoYW5nZS1lYXJ0aC1zdXJmYWNlLXRlbXBlcmF0dXJlLWRhdGEvZG93bmxvYWQvZU0weENoaHNUQWJhNFZoTGVEMkslMkZ2ZXJzaW9ucyUyRmZncnVRck1GRVBCNlZxMTliQ2UyJTJGZmlsZXMlMkZHbG9iYWxMYW5kVGVtcGVyYXR1cmVzQnlDb3VudHJ5LmNzdj9kYXRhc2V0VmVyc2lvbk51bWJlcj0xKSoqCgoqKioKIAojIyMgU291cmNlIDI6IFtDTzIgYW5kIEdIRyBlbWlzc2lvbiBkYXRhXShodHRwczovL3d3dy5rYWdnbGUuY29tL3NyaWthbnRzYWh1L2NvMi1hbmQtZ2hnLWVtaXNzaW9uLWRhdGEpCgpUaGlzIGRhdGFzZXQgY29udGFpbnMgZW1pc3Npb24gZGF0YSBvZiBHcmVlbiBIb3VzZSBHYXNlcyAoaW4gdG9ubmVzKSBvZiBkaWZmZXJlbnQgY291bnRyaWVzIGZyb20geWVhciAxNzUwIC0gMjAxNyB0YWtlbiBmcm9tIGh0dHBzOi8vb3Vyd29ybGRpbmRhdGEub3JnL2NvMi1hbmQtb3RoZXItZ3JlZW5ob3VzZS1nYXMtZW1pc3Npb25zCgoKKipPdXIgc2Vjb25kIGRhdGEgc291cmNlIGlzIHRoZSBDU1YgZmlsZSAtIGBlbWlzc2lvbiBkYXRhLmNzdmAqKgoKVGhlIGBlbWlzc2lvbiBkYXRhYCBkYXRhIHNldCBjb25zaXN0cyBvZiB0aGUgZm9sbG93aW5nIHZhcmlhYmxlczoKCiAgKiBgQ291bnRyeSBbY2hhcmFjdGVyXWA6IG5hbWUgb2YgdGhlIGNvdW50cnkgZm9yIHdoaWNoIHRoZSBlbWlzc2lvbiBkYXRhIGlzIHJlY29yZGVkCgogICogYDE3NTEsIDE3NTIsLi4uLCAyMDE4LCAyMDE5IFtudW1lcmljXWA6IHRoZSAqKnllYXIqKiB0aGF0IGRhdGEgd2FzIGNvbGxlY3RlZC4gR2l2ZXMgdGhlIGVtaXNzaW9uIGRhdGEgcmVjb3JkZWQgaW4gdG9ubmVzLgoKIyMjIyMgRGF0YXNldCBEb3dubG9hZCBMaW5rOiAqKltlbWlzc2lvbiBkYXRhLmNzdl0oaHR0cHM6Ly93d3cua2FnZ2xlLmNvbS9zcmlrYW50c2FodS9jbzItYW5kLWdoZy1lbWlzc2lvbi1kYXRhL2Rvd25sb2FkL3NXcVlVb3lYR2ZyaldEeTZRM1lqJTJGdmVyc2lvbnMlMkZnaHlrRDV2RHJCbXAwZlQxNnJiWSUyRmZpbGVzJTJGZW1pc3Npb24lMjBkYXRhLmNzdj9kYXRhc2V0VmVyc2lvbk51bWJlcj0xKSoqCgoqKioKCiMjIyBSZWFkaW5nIHRoZSBEYXRhOgoKIyMjIyBJbXBvcnRpbmcgdGhlIGRhdGEgZmlsZSBgZW1pc3Npb24gZGF0YS5jc3ZgOgpgYGB7ciwgZWNobyA9IFRSVUUsIHdhcm5pbmdzID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KZW1pc3Npb25EYXRhIDwtIHJlYWRfY3N2KCJlbWlzc2lvbiBkYXRhLmNzdiIpCmBgYAo8YnI+CgpUaGUgZmlyc3QgNiBvYnNlcnZhdGlvbnMgb2YgdGhlIGBlbWlzc2lvbiBkYXRhYCBkYXRhIHNldCBhcmU6CmBgYHtyLCBlY2hvID0gVFJVRSwgd2FybmluZ3MgPSBGQUxTRX0KaGVhZChlbWlzc2lvbkRhdGEpCmBgYAoKPGJyPgoKIyMjIyBJbXBvcnRpbmcgdGhlIGRhdGEgZmlsZSBgR2xvYmFsTGFuZFRlbXBlcmF0dXJlc0J5Q291bnRyeS5jc3ZgOgpgYGB7ciwgZWNobyA9IFRSVUUsIHdhcm5pbmdzID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KdGVtcGVyYXR1cmVEYXRhIDwtIHJlYWRfY3N2KCJHbG9iYWxMYW5kVGVtcGVyYXR1cmVzQnlDb3VudHJ5LmNzdiIpCmBgYAoKPGJyPgoKVGhlIGZpcnN0IDYgb2JzZXJ2YXRpb25zIG9mIHRoZSBgR2xvYmFsTGFuZFRlbXBlcmF0dXJlc0J5Q291bnRyeWAgZGF0YSBzZXQgYXJlOgpgYGB7ciwgZWNobyA9IFRSVUUsIHdhcm5pbmdzID0gRkFMU0V9CmhlYWQodGVtcGVyYXR1cmVEYXRhKQpgYGAKPGJyPgoKIyMjIyBOT1RFOiBXZSBkbyBub3QgbWVyZ2UgdGhlIGRhdGFzZXRzIGluIHRoaXMgc2VjdGlvbiBhcyB0aGUgZGF0YXNldHMgbmVlZCB0byBiZSB0aWRpZWQgYmVmb3JlIHdlIGNhbiBwcm9jZWVkIHRvIG1lcmdlIHRoZW0uIFRoZSBkYXRhIHNldHMgYXJlIG1lcmdlZCBpbiB0aGUgYE1lcmdpbmcgRGF0YXNldHNgIHNlY3Rpb24gYWZ0ZXIgYFRpZHkgJiBNYW5pcHVsYXRlIERhdGEgSWAuCgo8aHIgc3R5bGU9ImJvcmRlcjogMC41cHggc29saWQgU2lsdmVyOyAgYWxpZ246Y2VudGVyIj4KCiMjIFVuZGVyc3RhbmQKCiMjIyAxLiBDaGVja2luZyB0aGUgZGltZW5zaW9ucyBhbmQgc3RydWN0dXJlIG9mIHRoZSBkYXRhIGZyYW1lIGBlbWlzc2lvbkRhdGFgOgpgYGB7ciwgZWNobyA9IFRSVUUsIHdhcm5pbmdzID0gRkFMU0V9CmRpbShlbWlzc2lvbkRhdGEpCnN0cihlbWlzc2lvbkRhdGFbLDE6OF0pCmBgYApUaGUgY29sdW1ucyBgOS0yNjhgIGFsbCBnaXZlIHRoZSBlbWlzc2lvbiByZWFkaW5nIGZvciBhIHllYXIgYW5kIGFyZSBvZiBgbnVtZXJpY2AgY2xhc3MuIFRoZXNlIGFyZW4ndCBzaG93biBhYm92ZSB0byBpbXByb3ZlIHJlYWRhYmlsaXR5LgoKKioqCgojIyMgMi4gQ2hlY2tpbmcgdGhlIGF0dHJpYnV0ZXMgb2YgYGVtaXNzaW9uRGF0YWA6CiogICoqQ2hlY2tpbmcgdGhlIGRhdGEgdHlwZSBvZiB0aGUgYENvdW50cnlgIHZhcmlhYmxlOioqCmBgYHtyLCBlY2hvID0gVFJVRSwgd2FybmluZ3MgPSBGQUxTRX0KY2xhc3MoZW1pc3Npb25EYXRhJENvdW50cnkpCnR5cGVvZihlbWlzc2lvbkRhdGEkQ291bnRyeSkKYGBgClRoZSB2YXJpYWJsZSBgQ291bnRyeWAgZ2l2ZXMgdGhlIG5hbWUgb2YgdGhlIGNvdW50cnkuIFRoZSB2YXJpYWJsZSBpcyBvZiB0eXBlIGBjaGFyYWN0ZXJgLiBObyB0eXBlIGNvbnZlcnNpb25zIGFyZSBuZWVkZWQuCgo8YnI+CgoqICAqKkNoZWNraW5nIHRoZSBkYXRhIHR5cGUgb2YgdGhlIGAxNzUxYCB2YXJpYWJsZToqKgpgYGB7ciwgZWNobyA9IFRSVUUsIHdhcm5pbmdzID0gRkFMU0V9CmNsYXNzKGVtaXNzaW9uRGF0YSRgMTc1MWApCnR5cGVvZihlbWlzc2lvbkRhdGEkYDE3NTFgKQpgYGAKVGhlIHZhcmlhYmxlIGAxNzUxYCBnaXZlcyBlbWlzc2lvbiByZWFkaW5ncyBmb3IgdGhlIHllYXIgMTc1MS4gVGhlIHZhcmlhYmxlIGlzIG9mIHR5cGUgYGRvdWJsZWAuIFRoZSBlbWlzc2lvbiByZWFkaW5ncyBhcmUgY29udGludW91cyBudW1lcmljIHZhbHVlcyBhbmQgY2Fubm90IGJlIGZhY3RvcmVkLgoKIyMjIyBUaGUgcmVtYWluaW5nIHZhcmlhYmxlcyBgMTc1MiAtIDIwMTlgIGFyZSBhbGwgbnVtZXJpYyBhbmQgZ2l2ZSB0aGUgZW1pc3Npb24gdmFsdWVzLiBXZSBkbyBub3QgaW5kaXZpZHVhbGx5IGNoZWNrIHRoZSB0eXBlIG9mIGFsbCB0aGUgdmFyaWFibGVzIGR1ZSB0byBsYXJnZSBudW1iZXIgb2YgdmFyaWFibGVzLiAKCioqKgoKIyMjIDMuIENoZWNraW5nIHRoZSBkaW1lbnNpb25zIGFuZCBzdHJ1Y3R1cmUgb2YgdGhlIGRhdGEgZnJhbWUgYHRlbXBlcmF0dXJlRGF0YWA6CmBgYHtyLCBlY2hvID0gVFJVRSwgd2FybmluZ3MgPSBGQUxTRX0KZGltKHRlbXBlcmF0dXJlRGF0YSkKc3RyKHRlbXBlcmF0dXJlRGF0YSwgZ2l2ZS5hdHRyID0gRkFMU0UpCmBgYAoKKioqCgojIyMgNC4gQ2hlY2tpbmcgdGhlIGF0dHJpYnV0ZXMgb2YgYHRlbXBlcmF0dXJlRGF0YWA6CiogICoqQ2hlY2tpbmcgdGhlIGRhdGEgdHlwZSBvZiB0aGUgYGR0YCB2YXJpYWJsZToqKgpgYGB7ciwgZWNobyA9IFRSVUUsIHdhcm5pbmdzID0gRkFMU0V9CmNsYXNzKHRlbXBlcmF0dXJlRGF0YSRkdCkKdHlwZW9mKHRlbXBlcmF0dXJlRGF0YSRkdCkKYGBgClRoZSB2YXJpYWJsZSBgZHRgIGlzIG9mIGNsYXNzIGBEYXRlYC4gYGR0YCBnaXZlcyB0aGUgZGF0ZSB3aGVuIHRoZSB0ZW1wZXJhdHVyZSB3YXMgcmVjb3JkZWQuIFRoZSBgRGF0ZWAgdHlwZSBpcyBjb3JyZWN0IGFuZCBubyB0eXBlIGNvbnZlcnNpb24gaXMgbWFkZS4KCjxicj4KCiogICoqQ2hlY2tpbmcgdGhlIGRhdGEgdHlwZSBvZiB0aGUgYEF2ZXJhZ2VUZW1wZXJhdHVyZWAgdmFyaWFibGU6KioKYGBge3IsIGVjaG8gPSBUUlVFLCB3YXJuaW5ncyA9IEZBTFNFfQpjbGFzcyh0ZW1wZXJhdHVyZURhdGEkQXZlcmFnZVRlbXBlcmF0dXJlKQp0eXBlb2YodGVtcGVyYXR1cmVEYXRhJEF2ZXJhZ2VUZW1wZXJhdHVyZSkKYGBgClRoZSB2YXJpYWJsZSBgQXZlcmFnZVRlbXBlcmF0dXJlYCBnaXZlcyBhdmVyYWdlIHRlbXBlcmF0dXJlIHZhbHVlLiBUaGUgdmFyaWFibGUgaXMgYG51bWVyaWNgLiBUaGUgdGVtcGVyYXR1cmUgcmVhZGluZ3MgYXJlIGNvbnRpbnVvdXMgbnVtZXJpYyB2YWx1ZXMgYW5kIGNhbm5vdCBiZSBmYWN0b3JlZC4gCgo8YnI+CgoqICAqKkNoZWNraW5nIHRoZSBkYXRhIHR5cGUgb2YgdGhlIGBBdmVyYWdlVGVtcGVyYXR1cmVVbmNlcnRhaW50eWAgdmFyaWFibGU6KioKYGBge3IsIGVjaG8gPSBUUlVFLCB3YXJuaW5ncyA9IEZBTFNFfQpjbGFzcyh0ZW1wZXJhdHVyZURhdGEkQXZlcmFnZVRlbXBlcmF0dXJlVW5jZXJ0YWludHkpCnR5cGVvZih0ZW1wZXJhdHVyZURhdGEkQXZlcmFnZVRlbXBlcmF0dXJlVW5jZXJ0YWludHkpCmBgYApUaGUgdmFyaWFibGUgYEF2ZXJhZ2VUZW1wZXJhdHVyZVVuY2VydGFpbnR5YCBpcyBgbnVtZXJpY2AuIGBBdmVyYWdlVGVtcGVyYXR1cmVVbmNlcnRhaW50eWAgaGFzIGNvbnRpbnVvdXMgbnVtZXJpYyB2YWx1ZXMgYW5kIGNhbm5vdCBiZSBmYWN0b3JlZC4gVGhlIHR5cGUgYG51bWVyaWNgIGlzIGNvcnJlY3QuIE5vIGNoYW5nZXMgYXJlIG1hZGUuIAoKPGJyPgoKKiAgKipDaGVja2luZyB0aGUgZGF0YSB0eXBlIG9mIHRoZSBgQ291bnRyeWAgdmFyaWFibGU6KioKYGBge3IsIGVjaG8gPSBUUlVFLCB3YXJuaW5ncyA9IEZBTFNFfQpjbGFzcyh0ZW1wZXJhdHVyZURhdGEkQ291bnRyeSkKdHlwZW9mKHRlbXBlcmF0dXJlRGF0YSRDb3VudHJ5KQpgYGAKVGhlIHZhcmlhYmxlIGBDb3VudHJ5YCBnaXZlcyB0aGUgbmFtZSBvZiB0aGUgY291bnRyeS4gVGhlIHZhcmlhYmxlIGlzIG9mIHR5cGUgYGNoYXJhY3RlcmAuIE5vIHR5cGUgY29udmVyc2lvbnMgYXJlIG5lZWRlZC4KCjxicj4KCiMjIyMgTk9URTogQXMgeW91IGNhbiBzZWUgd2UgZG8gbm90IGhhdmUgYW55IHZhcmlhYmxlcyB0aGF0IHdlIGNhbiBmYWN0b3IuIFdlIGZhY3RvciBgeWVhcmAgYWZ0ZXIgd2UgaGF2ZSBnYXRoZXJlZCBpdCBpbiB0aGUgYE1lcmdpbmcgRGF0YXNldHNgIHNlY3Rpb24gYWZ0ZXIgd2UgbWVyZ2UgdGhlIHR3byBkYXRhc2V0cy4gV2UgYWxzbyBjcmVhdGUgYSBuZXcgdmFyaWFibGUgYW5kIGZhY3RvciBpdCBpbiBzZWN0aW9uIGBUaWR5ICYgTWFuaXB1bGF0ZSBEYXRhIElJYC4gCjxociBzdHlsZT0iYm9yZGVyOiAwLjVweCBzb2xpZCBTaWx2ZXI7ICBhbGlnbjpjZW50ZXIiPgoKIyMJVGlkeSAmIE1hbmlwdWxhdGUgRGF0YSBJIAoKIyMjIDEuIEdhdGhlciBgZW1pc3Npb25EYXRhYDoKVGhlIGNvbHVtbnMgYDItMjY4YCBnaXZlIHRoZSBlbWlzc2lvbiByZWFkaW5nIG1lYXN1cmVkIGZvciB0aGUgeWVhcnMgYDE3NTEtMjAxN2AuIFRoZSBjb2x1bW5zIGAyLTI2OGAgY29udGFpbiB5ZWFyIGRhdGEgaW4gdGhlIGNvbHVtbiBuYW1lcywgdGhlc2UgY29sdW1uIG5hbWVzIGFyZSBnYXRoZXJlZCB0byBmb3JtIGEgbmV3IHZhcmlhYmxlIGB5ZWFyYC4KCmAtYyhDb3VudHJ5KWAgZGVub3RlcyB0aGF0IHdlIGdhdGhlciBhbGwgdGhlIGNvbHVtbnMgZXhjZXB0IHRoZSBgQ291bnRyeWAgY29sdW1uLgpgYGB7cn0KZW1pc3Npb25EYXRhVGlkeSA8LSBlbWlzc2lvbkRhdGEgJT4lIGdhdGhlcigtYyhDb3VudHJ5KSxrZXkgPSAieWVhciIsIHZhbHVlID0gImVtaXNzaW9uIikKZW1pc3Npb25EYXRhVGlkeQpgYGAKCjxicj4KCiMjIyMjIF9fRGltZW5zaW9ucyBvZiBgZW1pc3Npb25EYXRhYCBhZnRlciBgZ2F0aGVyKClgOl9fCmBgYHtyfQpkaW0oZW1pc3Npb25EYXRhVGlkeSkKYGBgCgoqKioKCiMjIyAyLiBTZXBhcmF0ZSBgZHRgIHRvIGV4dHJhY3QgYHllYXJgIGZyb20gdGhlIGRhdGUgaW4gYHRlbXBlcmF0dXJlRGF0YWA6CmBgYHtyfQp0ZW1wZXJhdHVyZURhdGFUaWR5IDwtIHRlbXBlcmF0dXJlRGF0YSAlPiUgc2VwYXJhdGUoZHQsIGludG8gPSBjKCJ5ZWFyIiwgIm1vbnRoIiwgImRheSIpLCBzZXAgPSAnLScpCnRlbXBlcmF0dXJlRGF0YVRpZHkKYGBgCgoqKioKCiMjIyAzLiBEcm9wIGBtb250aGAsIGBkYXlgIGFuZCBgQXZlcmFnZVRlbXBlcmF0dXJlVW5jZXJ0YWludHlgIGNvbHVtbnMgZnJvbSBgdGVtcGVyYXR1cmVEYXRhYDoKV2Ugb25seSBuZWVkIHRoZSBgeWVhcmAgY29sdW1uLCBzbyB3ZSBkcm9wIHRoZSBgbW9udGhgIGFuZCBgZGF5YCBjb2x1bW5zLiBUaGUgY29sdW1uIGBBdmVyYWdlVGVtcGVyYXR1cmVVbmNlcnRhaW50eWAgZ2l2ZXMgdGhlIDk1JSBjb25maWRlbmNlIGludGVydmFsIGFyb3VuZCB0aGUgYXZlcmFnZSB0ZW1wZXJhdHVyZS4gQXMgd2UgZG8gbm90IG5lZWQgdGhpcyBkYXRhIGZvciBvdXIgZXZhbHVhdGlvbiwgd2UgZHJvcCB0aGlzIGNvbHVtbiB0b28uCmBgYHtyfQp0ZW1wZXJhdHVyZURhdGFUaWR5IDwtIHRlbXBlcmF0dXJlRGF0YVRpZHkgJT4lIHNlbGVjdCgtbW9udGgsIC1kYXksIC1BdmVyYWdlVGVtcGVyYXR1cmVVbmNlcnRhaW50eSkKdGVtcGVyYXR1cmVEYXRhVGlkeQpgYGAKCjxicj4KCiMjIyMjIF9fRGltZW5zaW9ucyBvZiBgdGVtcGVyYXR1cmVEYXRhVGlkeWA6X18KYGBge3J9CmRpbShlbWlzc2lvbkRhdGFUaWR5KQpgYGAKCioqKgoKIyMjIDQuIEdyb3VwIGB0ZW1wZXJhdHVyZURhdGFgIGJ5IGB5ZWFyYCBhbmQgYENvdW50cnlgOgpUaGUgYHRlbXBlcmF0dXJlRGF0YVRpZHlgIGRhdGEgc2V0IGNvbnRhaW5zIG11bHRpcGxlIHJvd3MgZm9yIGVhY2ggY291bnRyeSBlYWNoIGNvcnJlc3BvbmRpbmcgdG8gdGhlIHNhbWUgeWVhci4gVGhpcyBpcyBiZWFjdXNlIGB0ZW1wZXJhdHVyZURhdGFgIGhhZCBlbnRyaWVzIGZvciBkaWZmZXJlbnQgbW9udGhzIG9mIHRoZSBzYW1lIHllYXIuIE5vdyBzaW5jZSB3ZSBkcm9wZWVkIHRoZSBtb250aCBhbmQga2VwdCBvbmx5IHRoZSB5ZWFyLCB3ZSBzZWUgbXVsdGlwbGUgcm93cyBmb3IgdGhlIHNhbWUgeWVhci4gVG8gdGlkeSB0aGUgZGF0YSwgdGhlIGRhdGEgaXMgZ3JvdXBlZCBieSBgeWVhcmAgJiBgQ291bnRyeWAgdG8gb2J0YWluIHRoZSBuZXcgYXZlcmFnZSB0ZW1wZXJhdHVyZSB2YWx1ZSBgQXZlcmFnZVRlbXBlcmF0dXJlYCBmb3IgZWFjaCB5ZWFyIGZvciBlYWNoIGNvdW50cnkuCmBgYHtyfQp0ZW1wZXJhdHVyZURhdGFUaWR5IDwtIHRlbXBlcmF0dXJlRGF0YVRpZHkgJT4lIGdyb3VwX2J5KHllYXIsIENvdW50cnkpICU+JSBzdW1tYXJpc2UoQXZlcmFnZVRlbXBlcmF0dXJlID0gbWVhbihBdmVyYWdlVGVtcGVyYXR1cmUsIG5hLnJtID0gVFJVRSkpCnRlbXBlcmF0dXJlRGF0YVRpZHkKYGBgCgo8YnI+CgojIyMjIyBfX0RpbWVuc2lvbnMgb2YgYHRlbXBlcmF0dXJlRGF0YVRpZHlgIGFmdGVyIGdyb3VwaW5nOl9fCmBgYHtyfQpkaW0oZW1pc3Npb25EYXRhVGlkeSkKYGBgCgo8aHIgc3R5bGU9ImJvcmRlcjogMC41cHggc29saWQgU2lsdmVyOyAgYWxpZ246Y2VudGVyIj4KCiMjIE1lcmdpbmcgRGF0YXNldHMKV2UgbWVyZ2UgdGhlIHR3byBkYXRhc2V0cyAtIGBlbWlzc2lvbkRhdGFUaWR5YCBhbmQgYHRlbXBlcmF0dXJlRGF0YVRpZHlgLiBXZSB1c2UgdGhlIHBhaXIgb2YgdmFyaWFibGVzIGBDb3VudHJ5YCBhbmQgYHllYXJgIGFzIHRoZSBrZXkgdG8gam9pbiB0aGUgdGFibGVzIHVzaW5nIGBpbm5lcl9qb2luKClgLgpgYGB7cn0KZW1pc3Npb25UZW1wZXJhdHVyZUpvaW5lZCA8LSBpbm5lcl9qb2luKGVtaXNzaW9uRGF0YVRpZHksIHRlbXBlcmF0dXJlRGF0YVRpZHksIGJ5ID0gYygiQ291bnRyeSIsInllYXIiKSkKZW1pc3Npb25UZW1wZXJhdHVyZUpvaW5lZApgYGAKCjxicj4KCiMjIyMjIF9fRGltZW5zaW9ucyBvZiBgZW1pc3Npb25UZW1wZXJhdHVyZUpvaW5lZGA6X18KYGBge3J9CmRpbShlbWlzc2lvblRlbXBlcmF0dXJlSm9pbmVkKQpgYGAKCioqKgoKIyMjIEZhY3RvcmluZyBgeWVhcmAgdmFyaWFibGUKCiMjIyMgRXhhbWluaW5nIHRoZSBudW1iZXIgb2YgdW5pcXVlIHZhbHVlcyBmb3IgYHllYXJgCmBgYHtyLCByZXN1bHRzPSdob2xkJ30KY2F0KCJOdW1iZXIgb2YgdW5pcXVlIHZhbHVlcyBmb3IgYHllYXJgOiIsIGxlbmd0aCh1bmlxdWUoZW1pc3Npb25UZW1wZXJhdHVyZUpvaW5lZCR5ZWFyKSksCiAgICAiXG5OdW1iZXIgb2Ygcm93cyBpbiB0aGUgZGF0YSBmcmFtZSBgZW1pc3Npb25UZW1wZXJhdHVyZUpvaW5lZGA6IiwgbnJvdyhlbWlzc2lvblRlbXBlcmF0dXJlSm9pbmVkKSkKCmBgYApUaGUgbnVtYmVyIG9mIHVuaXF1ZSB2YWx1ZXMgZm9yIGB5ZWFyYCBpcyBtdWNoIHNtYWxsZXIgdGhhbiB0aGUgc2l6ZSBvZiB0aGUgZGF0YSBmcmFtZSBgZW1pc3Npb25UZW1wZXJhdHVyZUpvaW5lZGAuIFRoZXJlZm9yZSB3ZSBwcm9jZWVkIHRvIGZhY3RvciBgeWVhcmAuCgo8YnI+CgojIyMjIFdlIGZhY3RvciB0aGUgYHllYXJgIHZhcmlhYmxlIGFuZCBvcmRlciBpdC4KYGBge3J9CmVtaXNzaW9uVGVtcGVyYXR1cmVKb2luZWQkeWVhciA8LSBmYWN0b3IoZW1pc3Npb25UZW1wZXJhdHVyZUpvaW5lZCR5ZWFyLCBsZXZlbHM9IHVuaXF1ZShlbWlzc2lvblRlbXBlcmF0dXJlSm9pbmVkJHllYXIpLCBvcmRlcmVkID0gVFJVRSkKc3RyKGVtaXNzaW9uVGVtcGVyYXR1cmVKb2luZWQkeWVhcikKYGBgCgo8aHIgc3R5bGU9ImJvcmRlcjogMC41cHggc29saWQgU2lsdmVyOyAgYWxpZ246Y2VudGVyIj4KCiMjCVRpZHkgJiBNYW5pcHVsYXRlIERhdGEgSUkgCgpJbiB0aGlzIHNlY3Rpb24gd2UgY3JlYXRlIGEgbmV3IHZhcmlhYmxlIGB0aGFuTWVhbkVtaXNzaW9uYCwgdGhpcyB2YXJpYWJsZSB3aWxsIHRlbGwgdXMgaWYgdGhlIGVtaXNzaW9uIHZhbHVlIGlzIGhpZ2hlciBvciBsb3dlciB0aGFuIHRoZSBNZWFuIEVtaXNzaW9uIG9mIGFsbCB0aGUgY291bnRyaWVzIGZvciB0aGUgY29ycmVzcG9uZGluZyB5ZWFyLgoKIyMjIDEuIENhbGN1bGF0aW5nIHRoZSBtZWFuIGVtaXNzaW9uIHZhbHVlIGBtZWFuRW1pc3Npb25CeVllYXJgIGZvciBlYWNoIGB5ZWFyYDoKYGBge3J9Cm1lYW5FbWlzc2lvbkJ5WWVhckRhdGEgPC0gZW1pc3Npb25UZW1wZXJhdHVyZUpvaW5lZCAlPiUgCiAgZ3JvdXBfYnkoeWVhcikgJT4lIAogIHN1bW1hcmlzZShtZWFuRW1pc3Npb25CeVllYXIgPSBtZWFuKGVtaXNzaW9uLCBuYS5ybSA9IFRSVUUpKQoKbWVhbkVtaXNzaW9uQnlZZWFyRGF0YQpgYGAKCioqKgoKIyMjIDIuIEFkZCB0aGUgYG1lYW5FbWlzc2lvbkJ5WWVhcmAgdmFyaWFibGUgdG8gYGVtaXNzaW9uVGVtcGVyYXR1cmVKb2luZWRgIGpvaW5pbmcgYnkgYHllYXJgOgpUaGUgbmV3IGRhdGEgZnJhbWUgaXMgbmFtZWQgYGpvaW5lZERhdGFgLgpgYGB7cn0Kam9pbmVkRGF0YSA8LSBsZWZ0X2pvaW4oZW1pc3Npb25UZW1wZXJhdHVyZUpvaW5lZCwgbWVhbkVtaXNzaW9uQnlZZWFyRGF0YSwieWVhciIpCmpvaW5lZERhdGEKYGBgCgoqKioKCiMjIyAzLiBVc2UgYG1lYW5FbWlzc2lvbkJ5WWVhcmAgdG8gY3JlYXRlIGEgbmV3IHZhcmlhYmxlIGB0aGFuTWVhbkVtaXNzaW9uYCB1c2luZyBgbXV0YXRlKClgOgoKQ29kZSByZWZlcnJlZCBmcm9tOiBbTGlua10oaHR0cHM6Ly9yc3R1ZGlvLXB1YnMtc3RhdGljLnMzLmFtYXpvbmF3cy5jb20vMTE2MzE3X2U2OTIyZTgxZTcyZTRlM2Y4Mzk5NTQ4NWNlNjg2YzE0Lmh0bWwjLzUKKQpgYGB7cn0Kam9pbmVkRGF0YSA8LSBtdXRhdGUoam9pbmVkRGF0YSwgdGhhbk1lYW5FbWlzc2lvbiA9IGlmZWxzZShlbWlzc2lvbiA+IG1lYW5FbWlzc2lvbkJ5WWVhciwgIkhpZ2hlciIsICJMb3dlciIpKQpqb2luZWREYXRhCmBgYAoKKioqCgojIyMgNC4gRmFjdG9yaW5nIGB0aGFuTWVhbkVtaXNzaW9uYCA6CmBgYHtyfQpqb2luZWREYXRhJHRoYW5NZWFuRW1pc3Npb24gPC0gZmFjdG9yKGpvaW5lZERhdGEkdGhhbk1lYW5FbWlzc2lvbiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygiTG93ZXIiLCAiSGlnaGVyIiksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9yZGVyZWQgPSBUUlVFKQpzdHIoam9pbmVkRGF0YSR0aGFuTWVhbkVtaXNzaW9uKQpgYGAKCioqKgoKIyMjIDUuIERyb3BwaW5nIGBtZWFuRW1pc3Npb25CeVllYXJgOgpXZSBjYW4gc2VlIHRoYXQgdGhlIGNvbHVtbiBgbWVhbkVtaXNzaW9uQnlZZWFyYCBoYXMgdGhlIHNhbWUgdmFsdWUgZm9yIG11bHRpcGxlIHJvd3MuIEhhdmluZyByZXBlYXRlZCB2YWx1ZXMgaXNuJ3QgZGVzaXJhYmxlLCBzbyB3ZSBkcm9wIHRoaXMgY29sdW1uLiBXZSBqdXN0IGFkZGVkIHRoaXMgY29sdW1uIHRvIGNyZWF0ZSB0aGUgY29sdW1uIGB0aGFuTWVhbkVtaXNzaW9uYC4gCgpOb3cgdGhhdCBgbWVhbkVtaXNzaW9uQnlZZWFyYCBoYXMgc2VydmVkIGl0cyBwdXJwb3NlLCB3ZSBkcm9wIGl0LgoKYGBge3J9CmpvaW5lZERhdGEgPC0gam9pbmVkRGF0YSAlPiUgc2VsZWN0KC1tZWFuRW1pc3Npb25CeVllYXIpCnN0cihqb2luZWREYXRhKQpgYGAKCklmIHdlIHN0aWxsIGRvIGhvd2V2ZXIgbmVlZCB0aGUgbWVhbiBlbWlzc2lvbiBmb3IgYSB5ZWFyLCB3ZSBjYW4gb2J0YWluIGl0IGZyb20gdGhlIGRhdGEgZnJhbWUgYG1lYW5FbWlzc2lvbkJ5WWVhckRhdGFgLiBUaGlzIGRhdGEgZnJhbWUgaGFzIGp1c3QgMjYzIHJvd3MgY29tcGFyZWQgdG8gdGhlIDM3NDM3IHJvd3MgdGhlIHZhcmlhYmxlIGBtZWFuRW1pc3Npb25CeVllYXJgIGhhZCBpbiBgam9pbmVkRGF0YWAuCgpgYGB7cn0Kc3RyKG1lYW5FbWlzc2lvbkJ5WWVhckRhdGEpCmBgYAoKPGhyIHN0eWxlPSJib3JkZXI6IDAuNXB4IHNvbGlkIFNpbHZlcjsgIGFsaWduOmNlbnRlciI+CgojIwlTY2FuIEkgCgojIyMjIEluIHRoaXMgc2VjdGlvbiB3ZSBkZXRlY3QgYW5kIGRlYWwgd2l0aCBtaXNzaW5nIHZhbHVlcy4KIyMjIDEuIENoZWNraW5nIGZvciBNaXNzaW5nIFZhbHVlczoKYGBge3J9CmNvbFN1bXMoaXMubmEoam9pbmVkRGF0YSkpCmBgYApPbmx5IGBBdmVyYWdlVGVtcGVyYXR1cmVgIGNvbHVtbiBoYXMgbWlzc2luZyB2YWx1ZXMuIFdlIGV4YW1pbmUgdGhlIG1pc3NpbmcgdmFsdWVzIGluIHRoZSBuZXh0IHN0ZXAuCgoqKioKCiMjIyAyLiBFeGFtaW5pbmcgdGhlIE1pc3NpbmcgVmFsdWVzOgoKYGBge3J9CmpvaW5lZERhdGEgJT4lIGZpbHRlcihpcy5uYShqb2luZWREYXRhJEF2ZXJhZ2VUZW1wZXJhdHVyZSkpCnN1bShpcy5uYW4oam9pbmVkRGF0YSRBdmVyYWdlVGVtcGVyYXR1cmUpKQpgYGAKV2UgY2FuIHNlZSB0aGF0IGFsbCB0aGUgbWlzc2luZyB2YWx1ZXMgYXJlIGBOYU5gLCBhbmQgdGhleSB3ZXJlIGdlbmVyYXRlZCBhcyBhIHJlc3VsdCBvZiBjYWxjdWxhdGlvbiBvZiBgbWVhbl90ZW1wYCB1c2luZyBgbWVhbigpYCB3aXRoIGBuYS5ybSA9IHRydWVgLiBUaGlzIHJlc3VsdGVkIGluIGFuIGBOYU5gIHZhbHVlIHdoZW4gYWxsIGBBdmVyYWdlVGVtcGVyYXR1cmVgIHZhbHVlcyBhcmUgYE5BYCBmb3IgYSBwYXJ0aWN1bGFyIGNvdW50cnkgYW5kIHllYXIuIAoKKioqCgojIyMgMy4gQ2FsY3VsYXRpbmcgbWVhbiB0ZW1wZXJhdHVyZSBmb3IgZWFjaCBgQ291bnRyeWAgdG8gaW1wdXRlIHRoZSBtaXNzaW5nIHZhbHVlczoKCldlIGNob29zZSB0byBpbXB1dGUgdGhlIG1pc3NpbmcgdmFsdWVzIHdpdGggdGhlIG1lYW4gdmFsdWUgb2YgYEF2ZXJhZ2VUZW1wZXJhdHVyZWAuIEhvd2V2ZXIsIHJlcGxhY2luZyB0aGUgbWlzc2luZyB2YWx1ZXMgd2l0aCB0aGUgbWVhbiBvZiB0aGUgYWxsIHRoZSBBdmVyYWdlIHRlbXBlcmF0dXJlcyBpc24ndCB3aXNlLiBJbnN0ZWFkLCB3ZSBpbXB1dGUgdGhlIG1pc3NpbmcgdmFsdWVzIHdpdGggdGhlIG1lYW4gQXZlcmFnZSB0ZW1wZXJhdHVyZXMgb2YgaXRzIGNvcnJlc3BvbmRpbmcgY291bnRyeS4gCgpGb3IgZXhhbXBsZSwgdGhlIG1pc3NpbmcgdmFsdWUgb2YgQXZlcmFnZSB0ZW1wZXJhdHVyZSBmb3IgYEJhaGFtYXNgIGZvciB0aGUgeWVhciBgMTc2MWAgd2lsbCBiZSBpbXB1dGVkIHdpdGggdGhlIG1lYW4gdGVtcGVyYXR1cmVzIG9mIGBCYWhhbWFzYCBmb3IgdGhlIHJlc3Qgb2YgdGhlIHllYXJzICgxNzUxIC0gMjAxOSksIGlnbm9yaW5nIGFueSBtaXNzaW5nIHZhbHVlcywgaWYgYW55LgoKPGJyPgoKIyMjIyMgSGVyZSB3ZSBjYWxjdWxhdGUgdGhlIG1lYW4gdGVtcGVyYXR1cmUgZm9yIGVhY2ggY291bnRyeSB0aHJvdWdoIGFsbCB0aGUgeWVhcnMgKDE3NTEgLSAyMDE5KSBhbmQgbm90IGp1c3Qgb25lIHllYXIuIApgYGB7cn0KY291bnRyeU1lYW5UZW1wIDwtIGpvaW5lZERhdGEgJT4lIAogIGdyb3VwX2J5KENvdW50cnkpICU+JSAKICBzdW1tYXJpc2UobWVhblRlbXBCeUNvdW50cnkgPSBtZWFuKEF2ZXJhZ2VUZW1wZXJhdHVyZSwgbmEucm0gPSBUUlVFKSkKY291bnRyeU1lYW5UZW1wCmBgYApgYGB7cn0Kc3VtKGlzLm5hKGNvdW50cnlNZWFuVGVtcCkpCmBgYApUaGUgbWVhbiB3ZSBjYWxjdWxhdGVkIGFib3ZlIGRvZXNuJ3QgaGF2ZSBhbnkgbWlzc2luZyB2YWx1ZXMuIE5vdyB3ZSBjYW4gYmUgc3VyZSB0aGF0IHdlIGRvIG5vdCBpbXB1dGUgYW55IG1pc3NpbmcgdmFsdWVzIHdpdGggYE5BYCBvciBgTmFOYC4KCioqKgoKIyMjIDQuIENvbWJpbmUgYGNvdW50cnlNZWFuVGVtcGAgd2l0aCBgam9pbmVkRGF0YWA6CkJlZm9yZSB3ZSBpbXB1dGUgdGhlIG1pc3NpbmcgdmFsdWVzLCB3ZSBuZWVkIHRvIGNvbWJpbmUgdGhlIG1lYW4gd2UgY2FsY3VsYXRlZCAtIGBjb3VudHJ5TWVhblRlbXBgIHdpdGggYGpvaW5lZERhdGFgLgpgYGB7cn0Kam9pbmVkRGF0YSA8LSBsZWZ0X2pvaW4oam9pbmVkRGF0YSwgY291bnRyeU1lYW5UZW1wLCAiQ291bnRyeSIpCmpvaW5lZERhdGEKYGBgCjxicj4KCiMjIyMjIENoZWNraW5nIHRoZSBudW1iZXIgb2YgbWlzc2luZyB2YWx1ZXMgYmVmb3JlIGltcHV0aW5nIHRoZW06CmBgYHtyfQpjb2xTdW1zKGlzLm5hKGpvaW5lZERhdGEpKQpgYGAKCgoqKioKCiMjIyA1LiBJbXB1dGluZyBtaXNzaW5nIHZhbHVlczoKYGBge3J9CmpvaW5lZERhdGFfaW1wdXRlZCA8LSBtdXRhdGUoam9pbmVkRGF0YSwgQXZlcmFnZVRlbXBlcmF0dXJlID0gaWZlbHNlKGlzLm5hKEF2ZXJhZ2VUZW1wZXJhdHVyZSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZWFuVGVtcEJ5Q291bnRyeSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEF2ZXJhZ2VUZW1wZXJhdHVyZSkpCmBgYAoKPGJyPgoKIyMjIyMgQ2hlY2tpbmcgdGhlIG51bWJlciBvZiBtaXNzaW5nIHZhbHVlcyBhZnRlciBpbXB1dGluZyB0aGVtOgpgYGB7cn0KY29sU3Vtcyhpcy5uYShqb2luZWREYXRhX2ltcHV0ZWQpKQpgYGAKVGhlIG91dHB1dCBhYm92ZSBzaG93cyB1cyB0aGF0IGFsbCB0aGUgbWlzc2luZyB2YWx1ZXMgaGF2ZSBiZWVuIGltcHV0ZWQgc3VjY2Vzc2Z1bGx5LgoKKioqCgojIyMgNi4gRHJvcHBpbmcgYG1lYW5UZW1wQnlDb3VudHJ5YDoKSnVzdCBsaWtlIGBtZWFuRW1pc3Npb25CeVllYXJgLCAqKmBtZWFuVGVtcEJ5Q291bnRyeWAqKiBhbHNvIGhhcyBzYW1lIHZhbHVlcyBmb3IgbXVsdGlwbGUgcm93cy4gQXMgaGF2aW5nIHJlcGVhdGVkIHZhbHVlcyBpc24ndCBkZXNpcmFibGUsIHNvIHdlIGRyb3AgdGhpcyBjb2x1bW4gdG9vLiBXZSBqdXN0IGFkZGVkIHRoaXMgY29sdW1uIHRvIGltcHV0ZSB0aGUgbWlzc2luZyB2YWx1ZXMgaW4gYEF2ZXJhZ2VUZW1wZXJhdHVyZWAuIE5vdyB0aGF0IGBtZWFuVGVtcEJ5Q291bnRyeWAgaGFzIHNlcnZlZCBpdHMgcHVycG9zZSwgd2UgZHJvcCBpdC4KCmBgYHtyfQpqb2luZWREYXRhX2ltcHV0ZWQgPC0gam9pbmVkRGF0YV9pbXB1dGVkICU+JSBzZWxlY3QoLW1lYW5UZW1wQnlDb3VudHJ5KQpzdHIoam9pbmVkRGF0YV9pbXB1dGVkKQpgYGAKCklmIHdlIHN0aWxsIGRvIGhvd2V2ZXIgbmVlZCB0aGUgbWVhbiB0ZW1wZXJlYXR1cmUgZm9yIGEgY291bnRyeSwgd2UgY2FuIG9idGFpbiBpdCBmcm9tIHRoZSBkYXRhIGZyYW1lIGBjb3VudHJ5TWVhblRlbXBgLiBUaGlzIGRhdGEgZnJhbWUgaGFzIGp1c3QgMTg5IHJvd3MgY29tcGFyZWQgdG8gdGhlIDM3NDM3IHJvd3MgdGhlIHZhcmlhYmxlIGBtZWFuVGVtcEJ5Q291bnRyeWAgaGFkIGluIGBqb2luZWREYXRhX25ld19pbXB1dGVkYC4KCmBgYHtyfQpzdHIoY291bnRyeU1lYW5UZW1wKQpgYGAKCjxociBzdHlsZT0iYm9yZGVyOiAwLjVweCBzb2xpZCBTaWx2ZXI7ICBhbGlnbjpjZW50ZXIiPgoKIyMJU2NhbiBJSQoKKipMZXRzIHRha2UgYSBsb29rIGF0IG91ciBkYXRhc2V0OioqCgpgYGB7cn0Kc3RyKGpvaW5lZERhdGFfaW1wdXRlZCkKYGBgCgoqKldlIGhhdmUgb25seSB0d28gbnVtZXJpYyB2YWx1ZXMsIGBlbWlzc2lvbmAgYW5kIGBBdmVyYWdlVGVtcGVyYXR1cmVgKiogCgoqKioKCiMjIyBFeGFtaW5lIGBlbWlzc2lvbmAgdmFyaWFibGUgZm9yIG91dGxpZXJzOgpgYGB7cn0Kam9pbmVkRGF0YV9pbXB1dGVkICU+JSBzdW1tYXJ5KCkKY2F0KCJOdW1iZXIgb2Ygb3V0bGllcnM6ICIsIGxlbmd0aChib3hwbG90KGpvaW5lZERhdGFfaW1wdXRlZCRlbWlzc2lvbikkb3V0KSkKYGBgCgpXZSBmaW5kIGEgdmVyeSBvZGQgYm94cGxvdCwgdGhpcyBpcyBiZWNhdXNlIG1vc3Qgb2YgdGhlIGRhdGEgY29udGFpbnMgMCBhcyB0aGUgZW1pc3Npb24gcmF0ZS4gV2UgZXZlbiBnZXQgbWVkaWFuID0gYDBgLgpGcm9tIHRoZSBib3hwbG90IHdlIGNhbiBzZWUgdGhhdCB0aGVyZSBhcmUgYSBodWdlIG51bWJlciBvZiBvdXRsaWVycywgdGhpcyBpcyBiZWNhdXNlIGluIG91ciBkYXRhLCBhIGxhcmdlciBudW1iZXIgb2YgZW50cmllcyBoYXZlIHRoZSBlbWlzc2lvbiB2YWx1ZSBhcyBgMGAsIHdoaWNoIGlzIHNrZXdpbmcgb3VyIGRhdGEgdG93YXJkcyAwLgpUaGUgemVyb2VzIGNvdWxkIGJlIGVudGVyZWQgdG8gcmVwcmVzZW50IG1pc3NpbmcgdmFsdWVzLCBvciB0aGUgYWN0dWFsIHZhbHVlIG9mIHplcm8gZW1pc3Npb24uIFRoZSBbc291cmNlXShodHRwczovL3d3dy5rYWdnbGUuY29tL3NyaWthbnRzYWh1L2NvMi1hbmQtZ2hnLWVtaXNzaW9uLWRhdGEpIHdhcyB1bmNsZWFyIGFib3V0IHRoZSBuYXR1cmUgb2YgYDBgIHZhbHVlIGluIGBlbWlzc2lvbmAgY29sdW1uLiBXZSBjb25zaWRlciB0aGUgemVyb3MgdG8gcmVwcmVzZW50IDAgZW1pc3Npb24gZm9yIHRoZSBwdXJwb3NlIG9mIHRoaXMgYXNzaWdubWVudC4KCjxicj4KCiMjIyMgRXhhbWluaW5nIHRoZSBwZXJjZW50YWdlIG9mIHplcm8gZW50cmllcyBpbiBgZW1pc3Npb25gOgpgYGB7cn0Kam9pbmVkRGF0YV9pbXB1dGVkICU+JSBmaWx0ZXIoZW1pc3Npb24gPT0gMCkgJT4lIG5yb3coKQpqb2luZWREYXRhX2ltcHV0ZWQgJT4lIG5yb3coKQpjYXQoIlBlcmNlbnRhZ2Ugb2Ygcm93cyB3aXRoIHplcm8gZW1pc3Npb246IiwgMjE2MDkvMzc0MzcgKjEwMCkKYGBgCgpBZnRlciBhbmFseXppbmcgdGhlIGRhdGEgY2xvc2VseSwgd2UgZmluZCB0aGF0IGZvciB0aGUgZGF0YSBiZWZvcmUgMTk1MHMsIG1vc3QgY291bnRyaWVzIGhhZCAwIGVtaXNzaW9uIHJhdGUuIEFsc28gaW4gdGhlIGxhdGVyIHllYXJzIHRoZXJlIGFyZSBmZXcgY291bnRyaWVzIHN1Y2ggYXMgVVNBLCBJbmRpYSBhbmQgQ2hpbmEgdGhhdCBoYXZlIGV4cG9uZW50aWFsbHkgaHVnZSB2YWx1ZXMgb2YgZW1pc3Npb25zLgoKU2luY2UgdGhlIGFwcGxpY2F0aW9uIG9mIG91ciBkYXRhc2V0IGNvdWxkIGJlIHRvIGZpbmQgdGhlIGNvcnJlbGF0aW9uIChpZiBhbnkpIGJldHdlZW4gdGhlIHJpc2UgaW4gdGVtcGVyYXR1cmUgYW5kIHRoZSBlbWlzc2lvbiByYXRlLCBieSByZW1vdmluZyB0aGUgb3V0bGllcnMgd2UgZmluZCAoZXh0cmVtZWx5IGxhcmdlIGVtaXNzaW9uIHZhbHVlcyksIHdlIG1heSBsb3NlIHZhbHVhYmxlIHJlc3VsdHMvaW5zaWdodHMgb2YgdGhlIGltcGFjdHMgb2YgdGhlIGxhcmdlIGVtaXNzaW9uIGJ5IHRoZXNlIGhhbmRmdWwgb2YgY291bnRyaWVzIG9uIHRoZSByaXNlIG9mIGdsb2JhbCB0ZW1wZXJhdHVyZS4KCkFzIHRoZSBkYXRhc2V0IGdpdmVzIGVtaXNzaW9uIHZhbHVlcyBiZWdpbm5pbmcgZnJvbSB0aGUgeWVhciAxNzUxLCB0aGUgcmVhc29uIGZvciB0aGUgemVyb3MgY291bGQgYmUgdGhlIHVuYXZhaWxhYmlsaXR5IG9mIGRhdGEgZm9yIHN1Y2ggZWFybHkgeWVhcnMuIEluc3RlYWQgb2YgZXhjbHVkaW5nIG9yIGltcHV0aW5nIGFsbCB0aGUgYDBgIHZhbHVlcyBpbiBvdXIgZGF0YXNldCwgd2UgY2hvb3NlIHRvIGZpbHRlciB0aGUgZGF0YSBhbmQgY29uc2lkZXIgdGhlIGRhdGEgZnJvbSB0aGUgeWVhciAxOTU1IG9ud2FyZHMsIGFzIGFsbW9zdCBhbGwgY291bnRyaWVzIGhhdmUgMCBlbWlzc2lvbiByYXRlcyB1bnRpbCB0aGUgMTk1MHMuCgo8YnI+CgojIyMjIEZpbHRlcmluZyBgam9pbmVkRGF0YV9pbXB1dGVkYCBhZnRlciB0aGUgeWVhciBgMTk1NWAKYGBge3J9CmRhdGFBZnRlcjE5NTU8LSBqb2luZWREYXRhX2ltcHV0ZWQgJT4lIGZpbHRlcih5ZWFyID49IDE5NTUpCmRhdGFBZnRlcjE5NTUgJT4lIGZpbHRlcihlbWlzc2lvbiA9PSAwKSAlPiUgbnJvdygpCmRhdGFBZnRlcjE5NTUgJT4lIG5yb3coKQpjYXQoIlBlcmNlbnRhZ2Ugb2Ygcm93cyB3aXRoIHplcm8gZW1pc3Npb246IiwgNTMxLzExMTUxICoxMDApCmBgYApIZXJlIHdlIGNhbiBzZWUgdGhhdCB0aGUgcGVyY2VudGFnZSBvZiB6ZXJvcyBoYXMgcmVkdWNlZCBkcmFzdGljYWxseSBpbiB0aGUgZGF0YS4KCjxicj4KCiMjIyMgQ2hlY2tpbmcgZm9yIG91dGxpZXJzIGluIHRoZSBmaWx0ZXJlZCBkYXRhOgpgYGB7cn0KY2F0KCJOdW1iZXIgb2Ygb3V0bGllcnMgaW4gYGRhdGFBZnRlcjE5NTVgOiAiLCBsZW5ndGgoYm94cGxvdChkYXRhQWZ0ZXIxOTU1JGVtaXNzaW9uKSRvdXQpKQpgYGAKCjxicj4KCiMjIyMgTk9URTogV2UgZmlyc3QgdHJhbmZvcm0gb3VyIGRhdGEgYW5kIHRoZW4gZGVhbCB3aXRoIHRoZSBvdXRsaWVycyBpbiBgU2NhbiBJSUlgIGFmdGVyIGBUcmFuc2Zvcm1gLgo8aHIgc3R5bGU9ImJvcmRlcjogMC41cHggc29saWQgU2lsdmVyOyAgYWxpZ246Y2VudGVyIj4KCiMjCVRyYW5zZm9ybSAKCioqSW4gdGhpcyBzZWN0aW9uLCB0byB0cnkgYW5kIGRlYWwgd2l0aCB0aGUgb3V0bGllcnMgd2UgdHJhbnNmb3JtIHRoZSBkYXRhOioqCmBgYHtyfQpkYXRhQWZ0ZXIxOTU1ICU+JSBzdW1tYXJ5KCkKYGBgCjxicj4KCiMjIyMgRXhhbWluZSB0aGUgaGlzdG9ncmFtIGZvciBgZW1pc3Npb25gOgpgYGB7cn0KaGlzdChkYXRhQWZ0ZXIxOTU1JGVtaXNzaW9uLCB4bGFiID0gIkVtaXNzaW9uIChpbiB0b25uZXMpIiwgbWFpbiA9ICJIaXN0b2dyYW0gZm9yIGBFbWlzc2lvbmAiKQpsZW5ndGgoZGF0YUFmdGVyMTk1NSRlbWlzc2lvbikKYGBgCkZyb20gdGhlIGhpc3RvZ3JhbSB3ZSBjYW4gb2JzZXJ2ZSB0aGF0IHRoZSBkYXRhIGlzIGBzdHJvbmdseSByaWdodCBza2V3ZWRgLiBXZSB0cmFuc2Zvcm0gdGhlIGBlbWlzc2lvbmAgdmFyaWFibGUgYnkgYXBwbHlpbmcgYSBgTG9nYXJpdGhtaWMgVHJhbnNmb3JtYXRpb25gLgoKQXMgd2Uga25vdywgTG9nIHRyYW5zZm9ybWF0aW9uIGNhbm5vdCBiZSBhcHBsaWVkIHRvIHplcm8gb3IgbmVnYXRpdmUgdmFsdWVzIGRpcmVjdGx5LiBJbiBvcmRlciB0byBhcHBseSB0aGUgTG9nIHRyYW5zZm9ybWF0aW9uIHRvIGBlbWlzc2lvbmAsIHdlIGFkZCB0aGUgbm9uLW5lZ2F0aXZlIGNvbnN0YW50IGAxYCB0byBhbGwgb2JzZXJ2YXRpb25zIGFuZCB0aGVuIGFwcGx5IHRoZSB0cmFuZm9ybWF0aW9uLiAKYGBge3J9Cmxlbmd0aChkYXRhQWZ0ZXIxOTU1JGVtaXNzaW9uKQpkYXRhQWZ0ZXIxOTU1JGVtaXNzaW9uIDwtIGRhdGFBZnRlcjE5NTUkZW1pc3Npb24gKyAxCmRhdGFBZnRlcjE5NTUkZW1pc3Npb24gPC0gbG9nKGRhdGFBZnRlcjE5NTUkZW1pc3Npb24pCmhpc3QoZGF0YUFmdGVyMTk1NSRlbWlzc2lvbiwgCiAgICAgeGxhYiA9ICJsb2coZW1pc3Npb24pIiwgCiAgICAgbWFpbiA9ICJIaXN0b2dyYW0gZm9yIGBFbWlzc2lvbmAgYWZ0ZXIgXG5Mb2cgVHJhbnNmb3JtYXRpb24iKQpgYGAKQWZ0ZXIgdGhlIHRyYW5zZm9ybWF0aW9uLCBvdXIgZGF0YSBpcyBtb3JlIG5vcm1hbGx5IGRpc3RydWJ1dGVkLgo8aHIgc3R5bGU9ImJvcmRlcjogMC41cHggc29saWQgU2lsdmVyOyAgYWxpZ246Y2VudGVyIj4KCiMjIFNjYW4gSUlJCgojIyMgMS4gRGVhbGluZyB3aXRoIG91dGxpZXJzIGZyb20gdGhlIHRyYW5zZm9ybWVkIGBlbWlzc2lvbmAgdmFyaWFibGUuCldlIGZpbmQgdGhhdCB0aGVyZSBhcmUgYDUzN2Agb3V0bGllcnMsIHdoaWNoIGlzIHJvdWdobHkgYXJvdW5kIGA0LjgxJWAuIFNpbmNlIHRoZSBvdXRsaWVycyBmb3JtIGxlc3MgdGhhbiA1JSBvZiB0aGUgdGhlIGRhdGEsIHdlIGNhbiBzYWZlbHkgZGVsZXRlIHRoZW0uIFdlIGV4YW1pbmUgdGhlIG91dGxpZXJzIGJlZm9yZSB3ZSBkZWxldGUgdGhlbS4KCmBgYHtyfQpvdXRsaWVycyA8LSBib3hwbG90KGRhdGFBZnRlcjE5NTUkZW1pc3Npb24pJG91dApjYXQoIk51bWJlciBvZiBvdXRsaWVyczogIiwgbGVuZ3RoKG91dGxpZXJzKSkKbGVuZ3RoKGRhdGFBZnRlcjE5NTUkZW1pc3Npb24pCmNhdCgiUGVyY2VudGFnZSBvZiBvdXRsaWVyczogIiwgNTM3LzExMTUxICogMTAwKQpgYGAKKioqCgojIyMgMi4gRXhhbWluaW5nIHdpdGggb3V0bGllcnMgZm9yIGBlbWlzc2lvbmAKYGBge3J9Cmxlbmd0aChvdXRsaWVyc1tvdXRsaWVycyAhPSAwXSkKbGVuZ3RoKG91dGxpZXJzW291dGxpZXJzID09IDBdKQpgYGAKVGhlIG1ham9yaXR5IG9mIHRoZSBvdXRsaWVycyA1MzEvNTM3ICogMTAwID0gYGByIDUzMS81MzcgKiAxMDBgYCUgaGF2ZSB0aGUgdmFsdWUgemVyby4gWmVybyBjb3JyZXNwb25kcyB0byAxIChlXjAgPSAxKSBpbiB0aGUgbm9uLXRyYW5zZm9ybWVkIGRhdGEuIEhlbmNlLCBieSByZW1vdmluZyB0aGUgb3V0bGllcnMsIHdlIGFyZSBlZmZlY3RpdmVseSByZW1vdmluZyB0aGUgcm93cyB3aXRoIGAwYCBhcyB0aGVpciBlbWlzc2lvbiwgd2hpY2ggaXMgd2hhdCB3ZSB3ZXJlIGFpbWluZyBmb3IuCgoqKioKCiMjIyAzLiBSZW1vdmluZyB3aXRoIG91dGxpZXJzIGZyb20gYGVtaXNzaW9uYApgYGB7cn0KZGF0YUFmdGVyMTk1NSA8LSBkYXRhQWZ0ZXIxOTU1Wy0od2hpY2goZGF0YUFmdGVyMTk1NSRlbWlzc2lvbiAlaW4lIG91dGxpZXJzKSksXQpib3hwbG90KGRhdGFBZnRlcjE5NTUkZW1pc3Npb24pCmBgYAoKKioqCgojIyMgNC4gRXhhbWluZSBgQXZlcmFnZVRlbXBlcmF0dXJlYCB2YXJpYWJsZSBmb3Igb3V0bGllcnM6Ck5vdyB3ZSBsb29rIGF0IHRoZSBzZWNvbmQgbnVtZXJpYyB2YXJpYWJsZSAtIGBBdmVyYWdlVGVtcGVyYXR1cmVgLiBXZSBjb25zaWRlciB0aGUgYGRhdGFBZnRlcjE5NTVgIGRhdGEgZnJhbWUgaXRlc2xmLgpgYGB7cn0KY2F0KCJOdW1iZXIgb2Ygb3V0bGllcnM6ICIgLCBsZW5ndGgoYm94cGxvdChkYXRhQWZ0ZXIxOTU1JEF2ZXJhZ2VUZW1wZXJhdHVyZSkkb3V0KSkKbGVuZ3RoKGRhdGFBZnRlcjE5NTUkQXZlcmFnZVRlbXBlcmF0dXJlKQoKY2F0KCJQZXJjZW50YWdlIG9mIG91dGxpZXJzOiAiLCAxMTgvMTA2MTQgKiAxMDApCmBgYAoKKioqCgojIyMgNS4gRGVhbGluZyB3aXRoIG91dGxpZXJzIGluIGBBdmVyYWdlVGVtcGVyYXR1cmVgOgoKV2UgZmluZCB0aGF0IHRoZXJlIGFyZSAxMTggb3V0bGllcnMsIHdoaWNoIGlzIHJvdWdobHkgYXJvdW5kIGAxLjExJWAuIFRoZXNlIG91dGxpZXJzIGNvcnJlc3BvbmQgdG8gY291bnRyaWVzIHRoYXQgYXJlIGV4dGVyZW1lbHkgY29sZCBwbGFjZXMgd2l0aCB0ZW1wZXJhdHVyZXMgdGhhdCBkcm9wIGJlbG93IC0xMMKwQy4gVGhlcmVmb3JlIGRlbGV0aW5nIHRoZW0gd291bGQgbm90IGJlIHNhZmUgd2F5IHRvIGhhbmRsZSB0aGVzZSBvdXRsaWVycy4gSGVuY2Ugd2UgaGF2ZSBvcHRlZCBmb3IgdXNpbmcgdGhlICoqQ2FwcGluZyBtZXRob2QqKiB0byBicmluZyB0aGVzZSBleHRyZW1lIHZhbHVlcyB0byB0aGUgY2xvc2VzdCBxdWFudGlsZS4gCgpSZWZlcnJlZCBmcm9tOiBbTW9kdWxlIDYgTm90ZXNdKGh0dHA6Ly9yYXJlLXBob2VuaXgtMTYxNjEwLmFwcHNwb3QuY29tL3NlY3VyZWQvTW9kdWxlXzA2Lmh0bWwjY2FwcGluZ18oYWthX3dpbnNvcmlzaW5nKSkgfCBbU3RhY2tvdmVyZmxvd10oaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvMTMzMzk2ODUvaG93LXRvLXJlcGxhY2Utb3V0bGllcnMtd2l0aC10aGUtNXRoLWFuZC05NXRoLXBlcmNlbnRpbGUtdmFsdWVzLWluLXI/dXRtX21lZGl1bT1vcmdhbmljJnV0bV9zb3VyY2U9Z29vZ2xlX3JpY2hfcWEmdXRtX2NhbXBhaWduPWdvb2dsZV9yaWNoX3FhKQoKIyMjIyBDYXBwaW5nIHRoZSBvdXRsaWVyczoKYGBge3J9CmNhcCA8LSBmdW5jdGlvbih4KXsKICAgIHF1YW50aWxlcyA8LSBxdWFudGlsZSggeCwgYyguMDUsIDAuMjUsIDAuNzUsIC45NSApICkKICAgIHhbIHggPCBxdWFudGlsZXNbMl0gLSAxLjUqSVFSKHgpIF0gPC0gcXVhbnRpbGVzWzFdCiAgICB4WyB4ID4gcXVhbnRpbGVzWzNdICsgMS41KklRUih4KSBdIDwtIHF1YW50aWxlc1s0XQogICAgeAp9CmRhdGFBZnRlcjE5NTUkQXZlcmFnZVRlbXBlcmF0dXJlIDwtIGRhdGFBZnRlcjE5NTUkQXZlcmFnZVRlbXBlcmF0dXJlICU+JSBjYXAoKQpgYGAKCjxicj4KCiMjIyMgQm94cGxvdCBhZnRlciBjYXBwaW5nIHRoZSBvdXRsaWVyczoKYGBge3J9CmJveHBsb3QoZGF0YUFmdGVyMTk1NSRBdmVyYWdlVGVtcGVyYXR1cmUpIApgYGAKCjxociBzdHlsZT0iYm9yZGVyOiAwLjVweCBzb2xpZCBTaWx2ZXI7ICBhbGlnbjpjZW50ZXIiPgoKIyMgRXhhbWluZSB2YXJpYWJsZXMgCgpXZSBmaW5hbGx5IHNob3cgdGhlIHNjYXR0ZXIgcGxvdCBvZiBlbWlzaXNvbiBhbmQgdGVtcGVyYXR1cmUuCldlIGFwcGx5IGBleHAoKWAgb24gdGhlIGBlbWlzc2lvbmAgdmFyaWFibGUgdG8gZ2V0IGJhY2sgdGhlIHVudHJhbnNmb3JtZWQgZGF0YSB0byBwbG90IGFnYWluc3QgVGVtcGVyYXR1cmUuCmBgYHtyfQpkYXRhQWZ0ZXIxOTU1ICU+JSBwbG90KGV4cChlbWlzc2lvbikgfiBBdmVyYWdlVGVtcGVyYXR1cmUsIGRhdGEgPSAuLCB5bGFiPSJFbWlzc2lvbiIsIHhsYWI9IlRlbXBlcmF0dXJlIiwgbWFpbj0iRW1pc3Npb24gYnkgVGVtcGVyYXR1cmUiKQpgYGAKPGJyPgo8YnI+Cg==