Required packages

library(dplyr)
library(tidyr)
library(ggplot2)
library(MVN)
library(forecast)

Executive Summary

This report describes the pre-processing of two data sets: population projection (United Nations 2019) and suicide rate estimates (World Health Organization 2018).

Initially, unused columns and rows are filtered on both data sets. This report only uses the data for the year 2016 and data for other years is removed. The population data set is found to be untidy, so the original wide data format is converted into a long format.

There are a few countries that have slightly different names in the two data sets (e.g. “North Macedonia” vs. “Republic of North Macedonia”. So the name of these countries are manually updated to match so that the two data sets could be merged by country and gender. Then, the gender variable is converted from character into an unordered factor with two possible values: male and female.

A new variable for the estimate number of suicides per country per gender is calculated from the suicide rate and number of populations. The data frame is then checked for missing values where it is determined that no missing values are found.

The data frame has three numeric variables: suicide rate, population, and number of suicide estimates. The distribution of these three variables is visualised through box plots, where it is determined that all three have a right skewed distribution. Hence, the Tukey’s method is used to look for outliers. However, the determined outliers do not seem to come from data entry errors. They seem to come from countries having extreme values of suicide rate and population (e.g. China and India), so these outliers are not imputed and are kept.

Finally, a Box-Cox transformation is performed on the suicide rate variable to try to convert the distribution into a normal distribution.

Data

The first data set is the United Nation population projection by location and gender from the year 1950 to 2100 (United Nations 2019). The data set has several projection variants where each uses different assumptions on projecting future levels of fertility, mortality, and migration.

setwd("~/Dropbox/data-wrangling/assignment2")

population <- read.csv("WPP2019_TotalPopulationBySex.csv", stringsAsFactors = FALSE)
head(population)

These variables are:

# Subset the population data set.
populationFiltered <- population %>%
                      filter(Variant == "Medium") %>%
                      filter(Time == 2016) %>%
                      select(Location, PopMale, PopFemale)
head(populationFiltered)

The second data set is the World Health Organization suicide rate estimate (World Health Organization 2018). The data set contains information on the estimate of suicide rate on each country and gender. When reading the csv file, the first line is skipped because it contains the column header.

suicide <- read.csv("data.csv", stringsAsFactors = FALSE, skip=1)
head(suicide)

These variables are:

# Since we are only interested in the year 2016, remove columns of other years.
suicideFiltered <- suicide[ , !(names(suicide) %in% c("X2015", "X2010", "X2000"))]

# Change column X2016 to SuicideRate
names(suicideFiltered)[names(suicideFiltered) == "X2016"] <- "SuicideRate"

head(suicideFiltered)

Tidy & Manipulate Data I

The population data set is untidy because the variable gender does not have its own column and the variable population is spread over two columns.

So the wide data frame is converted into a long format with the gather() function by converting the “PopMale” and “PopFemale” columns into “Sex” and “Population”. Then the string “Pop” from “PopMale” and “PopFemale” is removed.

populationTidied <- populationFiltered %>%
                    gather(PopMale, PopFemale, key = "Sex", value = "Population") %>%
                    mutate(Sex = sub("Pop", "", Sex))
head(populationTidied)

Merge

As mentioned in the first section, the population data set contains locations other than countries while the suicide data set contains only countries. So the two data sets will be joined by countries and gender. But first, we will check which countries in the suicide data set that are not in the population data set.

anti_join(suicideFiltered, populationTidied, by = c("Country" = "Location")) %>% distinct(Country)

I looked at the population data set and it turns out these countries actually exist in the population data set under a different name. So let us manually change their names so that they match. Then we merge the two data sets by country and gender. Since all the countries in the suicide data set exists in the population data set, we can either use left join on suicide data set or inner join. We will use inner join.

# Rename countries that have different name on the two data sets.
populationTidied$Location[populationTidied$Location == "Dem. People's Republic of Korea"] <- "Democratic People's Republic of Korea"
populationTidied$Location[populationTidied$Location == "North Macedonia"] <- "Republic of North Macedonia"
populationTidied$Location[populationTidied$Location == "Micronesia (Fed. States of)"] <- "Micronesia (Federated States of)"
populationTidied$Location[populationTidied$Location == "United Kingdom"] <- "United Kingdom of Great Britain and Northern Ireland"

df <- suicideFiltered %>%
      inner_join(populationTidied, c("Country" = "Location", "Sex" = "Sex"))

Understand

The merged data set:

str(df)
'data.frame':   366 obs. of  4 variables:
 $ Country    : chr  "Afghanistan" "Afghanistan" "Albania" "Albania" ...
 $ Sex        : chr  "Male" "Female" "Male" "Female" ...
 $ SuicideRate: num  10.6 2.1 7 4.3 4.9 1.8 14 4.6 0 0.9 ...
 $ Population : num  18187 17196 1471 1416 20482 ...

As seen above, the gender variable has the character type, so we need to convert the gender variable into an unordered factor.

df$Sex <- factor(df$Sex, levels=c("Male","Female"), ordered = FALSE)

Now all the variables are in the correct type.

str(df)
'data.frame':   366 obs. of  4 variables:
 $ Country    : chr  "Afghanistan" "Afghanistan" "Albania" "Albania" ...
 $ Sex        : Factor w/ 2 levels "Male","Female": 1 2 1 2 1 2 1 2 1 2 ...
 $ SuicideRate: num  10.6 2.1 7 4.3 4.9 1.8 14 4.6 0 0.9 ...
 $ Population : num  18187 17196 1471 1416 20482 ...

Tidy & Manipulate Data II

A new variable for the estimate number of suicide in thousands for the year 2016 will be added. To do this, the population variable is multiplied with suicide rate and divided by 100,000 because the suicide rate is per 100,000 population. Then the result is rounded to 3 decimal places because the variable is in thousands.

df <- df %>% mutate(NumOfSuicide = round(Population * SuicideRate / 100000, 3))
head(df)

The new variable is called “NumOfSuicide”. For example, the estimate number of suicide for Afghanistan male in 2016 is 1,928 men. Please note that the number of suicide is not the actual number of suicide for the year 2016. The variables suicide rate and population are estimates from WHO and UN respectively. Hence, the resulting number of suicide is also an estimate.

Scan I

The is.na() function will be used to determine missing values. It returns TRUE if the value is NA. So we will use it with colSums() to count the number of NA on each column. Determine the number of missing values on each of the columns:

colSums(is.na(df))
     Country          Sex  SuicideRate   Population NumOfSuicide 
           0            0            0            0            0 

It seems that there is no missing values in the data frame.

Next, we will check whether the data is complete in terms of countries and gender. For example, there are no duplicates (e.g. multiple rows for Australia male) or missing rows. That is, the number of rows in the data frame should be equal to the number of unique countries multiplied by 2 (male and female). Which should also be equal to the number of unique country-gender combination.

# Number of unique countries.
df$Country %>% unique %>% length
[1] 183
# Number of unique gender.
df$Sex %>% unique %>% length
[1] 2
# Number of unique country-gender combination.
df %>% unite(CountrySex, Country, Sex) %>% .$CountrySex %>% unique %>% length
[1] 366
# Number of rows in the data frame.
nrow(df)
[1] 366

There are 183 different countries and 2 different levels for gender. 183*2 == 366. Looks like we have a complete data.

Scan II

We have three numeric variables: suicide rate, population, and number of suicide estimates. First, we will determine whether the distribution of these three variables are normal or not with box plots. We will create a long data frame by combining the three numeric variables into a single column so that we could easily plot a faceted box plot.

longDf <- df %>%
          gather(SuicideRate, Population, NumOfSuicide, key = "Var", value = "Value")

# Convert the Var column into a factor so that we can order the box plot.
longDf$Var <- factor(longDf$Var, levels=c("SuicideRate", "Population", "NumOfSuicide"))

# A better description of the variables for the box plots.
levels(longDf$Var) <- c("Suicide per 100,000", "Population in thousands", "Num of Suicide in thousands")

bp <- longDf %>%
      ggplot(aes(x=Var, y=Value)) + 
      geom_boxplot(aes(fill=Sex)) +
      facet_wrap( ~ Var, scales="free")
bp

Suicide rate has a right skewed distribution.

However, the box plots for population and number of suicide are not quite visible because there are few values that are substantially larger than the rest. So we will limit the y axis of the box plot by using the function coord_cartesian(). The function does not exclude data, it only limits the axis drawn. Thus, the median and quartiles will not be affected.

bpPop <- longDf %>%
         filter(Var=="Population in thousands") %>%
         ggplot(aes(x=Var, y=Value)) + 
         geom_boxplot(aes(fill=Sex)) +
         facet_wrap( ~ Var, scales="free") +
         coord_cartesian(ylim = c(0, 100000)) # Limit the y axis to 100,000
bpPop

bpNumOfSuicide <- longDf %>%
                  filter(Var=="Num of Suicide in thousands") %>%
                  ggplot(aes(x=Var, y=Value)) + 
                  geom_boxplot(aes(fill=Sex)) +
                  facet_wrap( ~ Var, scales="free") +
                  coord_cartesian(ylim = c(0, 10)) # Limit the y axis to 10
bpNumOfSuicide

It seems that population and number of suicide also have right skewed distribution. Using the z-score method to determine outliers will not be appropriate because it requires the values to have a normal distribution. So we will use the Tukey’s method.

According to the box plots above, all the outliers are located above the box plot, so we only need to calculate the maximum fence. Moreover, the box plots show that there are substantial difference between male and female suicide rate and number of suicide, so we will look for outliers by gender.

We need to calculate the maximum fence on each of the three numeric variables on each of the gender. The maximum fence is calculated by Q3 + 1.5 * IQR where Q3 is the 75th percentile and IQR is the inter-quartile range.

summary <- longDf %>%
           group_by(Var, Sex) %>%
           summarise(Max = quantile(Value, 0.75) + 1.5 * IQR(Value))
summary

The “Max” column represents the maximum fence where values greater than this will be considered outliers.

To find the suicide rate outliers, we have to filter the data frame by gender and look for suicide rate that is higher than 33.45 (for male) and 11.60 (for female).

Suicide rate outliers (the outliers are sorted by gender and suicide rate):

df %>%
  filter((Sex == "Male" & SuicideRate > summary$Max[1]) | (Sex == "Female" & SuicideRate > summary$Max[2])) %>%
  arrange(Sex, desc(SuicideRate))

The same step is also applicable in finding the population and number of suicide outliers. But please note that different max fence is used (e.g. summary$Max[3])).

Population outliers (the outliers are sorted by gender and population):

df %>%
  filter((Sex == "Male" & Population > summary$Max[3]) | (Sex == "Female" & Population > summary$Max[4])) %>%
  arrange(Sex, desc(Population))

Number of suicide outliers (the outliers are sorted by gender and number of suicide):

df %>%
  filter((Sex == "Male" & NumOfSuicide > summary$Max[5]) | (Sex == "Female" & NumOfSuicide > summary$Max[6])) %>%
  arrange(Sex, desc(NumOfSuicide))

I think these outliers are not a result of data entry errors, so it would not be good idea to remove them. That is, I think the numbers on these outliers seem to make sense. For example, China and India are the two most populated countries in the world. I also did some quick googling on the number of suicides on several of these countries that are considered outliers and the numbers seem to be accurate. Therefore, I decided not to impute these outliers and leave them as they are.

Transform

In this section, we will apply a transformation on the suicide rate variable to convert the distribution into a more symmetric / normal distribution. As seen on the box plot in one of the earlier section, the suicide rate variable have a right skewed distribution. There are several ways to reduce the skewness of a right skewed distribution, such as log transformation, square root transformation, reciprocal transformation, and Box-Cox transformation. I tried each of these possible methods with different parameters and determined that the Box-Cox transformation seems to work slightly better than the rest for the suicide rate variable. So in this section, we will do a Box-Cox transformation on the suicide rate variable.

We have determined in the previous section that the suicide rate distribution is affected by gender. So we will apply the Box-Cox transformation by gender. First, we will filter the suicide rate variable by gender. Then apply the Box-Cox transformation. Then plot the histogram to visualise the distribution.

maleDf <- df %>% filter(Sex=="Male")
boxCoxMaleSuicideRate <- BoxCox(maleDf$SuicideRate, lambda="auto")
hist(boxCoxMaleSuicideRate)

It seems that there is a value (around -25000) that is very far from the rest of the group. Let us see find the value of this “outlier” by sorting the transformed values and look at the first few values.

boxCoxMaleSuicideRate %>% sort %>% head
[1] -2.437681e+04 -2.231425e-01  7.419486e-01  9.163080e-01  9.163080e-01  1.029641e+00

Let us find the lambda value used:

boxCoxMaleSuicideRate
  [1]  2.360968e+00  1.945988e+00  1.589287e+00  2.639200e+00 -2.437681e+04  2.708201e+00  2.312645e+00  2.856638e+00
  [9]  2.862369e+00  1.458659e+00  1.029641e+00  2.066950e+00  1.704808e+00 -2.231425e-01  3.671501e+00  3.100289e+00
 [17]  2.292643e+00  3.118149e+00  2.624810e+00  2.827478e+00  2.360968e+00  2.907074e+00  2.272232e+00  1.824618e+00
 [25]  2.572748e+00  3.109259e+00  3.140035e+00  3.182420e+00  2.197324e+00  3.292349e+00  2.714846e+00  2.890543e+00
 [33]  2.839244e+00  2.772746e+00  2.066950e+00  2.442469e+00  2.868068e+00  2.632031e+00  2.549578e+00  3.465982e+00
 [41]  2.934033e+00  2.797442e+00  1.974161e+00  2.845075e+00  2.694776e+00  2.708201e+00  2.580353e+00  2.476664e+00
 [49]  2.884971e+00  2.370359e+00  1.974161e+00  3.211055e+00  3.443861e+00  3.109259e+00  3.242808e+00  3.234964e+00
 [57]  2.928699e+00  2.174849e+00  3.035142e+00  2.884971e+00  2.708201e+00  2.549578e+00  2.509728e+00  2.610210e+00
 [65]  2.760166e+00  1.808356e+00  7.419486e-01  1.481650e+00  2.541734e+00  2.186149e+00  3.841903e+00  2.907074e+00
 [73]  1.667764e+00  3.100289e+00  3.077507e+00  2.917945e+00  1.648714e+00  1.589287e+00  1.547612e+00  2.868068e+00
 [81]  2.104225e+00  2.128325e+00  1.163179e+00  3.020612e+00  1.547612e+00  3.691656e+00  2.272232e+00  3.254460e+00
 [89]  9.163080e-01  2.694776e+00  2.557361e+00  3.434229e+00  1.435127e+00  3.122565e+00  2.624810e+00  2.163419e+00
 [97]  3.861035e+00  2.708201e+00  2.351489e+00  2.617536e+00  2.163419e+00  1.280968e+00  2.602829e+00  2.332255e+00
[105]  2.493333e+00  2.525859e+00  2.104225e+00  2.785170e+00  3.148657e+00  2.533828e+00  9.163080e-01  2.639200e+00
[113]  1.840619e+00  2.965453e+00  2.433735e+00  2.557361e+00  2.850873e+00  2.955089e+00  2.442469e+00  2.862369e+00
[121]  2.610210e+00  1.568666e+00  1.098637e+00  2.028233e+00  2.322498e+00  2.509728e+00  2.028233e+00  1.648714e+00
[129]  3.174085e+00  2.660405e+00  1.987955e+00  3.388010e+00  3.182420e+00  2.272232e+00  2.632031e+00  3.877740e+00
[137]  2.827478e+00  2.541734e+00  1.361015e+00  2.163419e+00  1.435127e+00  1.526104e+00  3.010807e+00  2.850873e+00
[145]  2.708201e+00  2.901594e+00  2.407064e+00  2.912525e+00  3.109259e+00  2.140160e+00  2.442469e+00  3.077507e+00
[153]  2.116347e+00  2.230116e+00  3.148657e+00  2.674295e+00  3.586557e+00  2.760166e+00  2.760166e+00  1.335038e+00
[161]  1.609491e+00  3.063583e+00  2.197324e+00  3.122565e+00  1.648714e+00  3.086682e+00  1.481650e+00  2.424923e+00
[169]  2.398013e+00  3.054192e+00  3.541217e+00  1.252795e+00  2.476664e+00  2.660405e+00  3.049464e+00  3.288624e+00
[177]  2.332255e+00  2.091954e+00  1.887143e+00  2.379662e+00  2.595393e+00  2.862369e+00  3.370971e+00
attr(,"lambda")
[1] 4.102259e-05

The lambda used is 4.102259e-05.

Then we can find the original row of this “outlier” by finding the index of the value in the transformed variable.

maleDf[which(boxCoxMaleSuicideRate == min(boxCoxMaleSuicideRate)), ]

The original suicide rate is 0. Which makes sense because the Box-Cox formula is \(\frac{y^\lambda-1}{\lambda}\) and if \(y\) is 0 and \(\lambda\) is 4.102259e-05, then the result is \(\frac{-1}{4.102259e-05} = -24376.81\)

We will remove this value and draw the histogram again so that we can see a better picture of the distribution of the rest of the group.

hist(boxCoxMaleSuicideRate[boxCoxMaleSuicideRate != min(boxCoxMaleSuicideRate)], breaks=20)

The distribution is not exactly symmetric but is definitely more similar to a normal distribution than the original values.

Now we will apply Box-Cox transformation on the female suicide rate.

femaleDf <- df %>% filter(Sex=="Female")
boxCoxFemaleSuicideRate <- BoxCox(femaleDf$SuicideRate, lambda="auto")
hist(boxCoxFemaleSuicideRate)

Again, the distribution is not exactly symmetric but is definitely more similar to a normal distribution than the original values.

References



LS0tCnRpdGxlOiAiTUFUSDIzNDkgU2VtZXN0ZXIgMSwgMjAyMC4gMjAxNiBTdWljaWRlIFJhdGUgYnkgQ291bnRyeSBhbmQgR2VuZGVyLiIKYXV0aG9yOiAiU3RlcGhlbiBBbGV4YW5kcm8gVGphbmRyYSAzODU5NTExIgpzdWJ0aXRsZTogQXNzaWdubWVudCAyCm91dHB1dDoKICBodG1sX25vdGVib29rOiBkZWZhdWx0Ci0tLQoKCiMjIFJlcXVpcmVkIHBhY2thZ2VzIApgYGB7cn0KbGlicmFyeShkcGx5cikKbGlicmFyeSh0aWR5cikKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KE1WTikKbGlicmFyeShmb3JlY2FzdCkKYGBgCgoKIyMgRXhlY3V0aXZlIFN1bW1hcnkgCgpUaGlzIHJlcG9ydCBkZXNjcmliZXMgdGhlIHByZS1wcm9jZXNzaW5nIG9mIHR3byBkYXRhIHNldHM6IHBvcHVsYXRpb24gcHJvamVjdGlvbiAoVW5pdGVkIE5hdGlvbnMgMjAxOSkgYW5kIHN1aWNpZGUgcmF0ZSBlc3RpbWF0ZXMgKFdvcmxkIEhlYWx0aCBPcmdhbml6YXRpb24gMjAxOCkuCgpJbml0aWFsbHksIHVudXNlZCBjb2x1bW5zIGFuZCByb3dzIGFyZSBmaWx0ZXJlZCBvbiBib3RoIGRhdGEgc2V0cy4KVGhpcyByZXBvcnQgb25seSB1c2VzIHRoZSBkYXRhIGZvciB0aGUgeWVhciAyMDE2IGFuZCBkYXRhIGZvciBvdGhlciB5ZWFycyBpcyByZW1vdmVkLgpUaGUgcG9wdWxhdGlvbiBkYXRhIHNldCBpcyBmb3VuZCB0byBiZSB1bnRpZHksIHNvIHRoZSBvcmlnaW5hbCB3aWRlIGRhdGEgZm9ybWF0IGlzIGNvbnZlcnRlZCBpbnRvIGEgbG9uZyBmb3JtYXQuCgpUaGVyZSBhcmUgYSBmZXcgY291bnRyaWVzIHRoYXQgaGF2ZSBzbGlnaHRseSBkaWZmZXJlbnQgbmFtZXMgaW4gdGhlIHR3byBkYXRhIHNldHMgKGUuZy4gIk5vcnRoIE1hY2Vkb25pYSIgdnMuICJSZXB1YmxpYyBvZiBOb3J0aCBNYWNlZG9uaWEiLgpTbyB0aGUgbmFtZSBvZiB0aGVzZSBjb3VudHJpZXMgYXJlIG1hbnVhbGx5IHVwZGF0ZWQgdG8gbWF0Y2ggc28gdGhhdCB0aGUgdHdvIGRhdGEgc2V0cyBjb3VsZCBiZSBtZXJnZWQgYnkgY291bnRyeSBhbmQgZ2VuZGVyLgpUaGVuLCB0aGUgZ2VuZGVyIHZhcmlhYmxlIGlzIGNvbnZlcnRlZCBmcm9tIGNoYXJhY3RlciBpbnRvIGFuIHVub3JkZXJlZCBmYWN0b3Igd2l0aCB0d28gcG9zc2libGUgdmFsdWVzOiBtYWxlIGFuZCBmZW1hbGUuCgpBIG5ldyB2YXJpYWJsZSBmb3IgdGhlIGVzdGltYXRlIG51bWJlciBvZiBzdWljaWRlcyBwZXIgY291bnRyeSBwZXIgZ2VuZGVyIGlzIGNhbGN1bGF0ZWQgZnJvbSB0aGUgc3VpY2lkZSByYXRlIGFuZCBudW1iZXIgb2YgcG9wdWxhdGlvbnMuClRoZSBkYXRhIGZyYW1lIGlzIHRoZW4gY2hlY2tlZCBmb3IgbWlzc2luZyB2YWx1ZXMgd2hlcmUgaXQgaXMgZGV0ZXJtaW5lZCB0aGF0IG5vIG1pc3NpbmcgdmFsdWVzIGFyZSBmb3VuZC4KClRoZSBkYXRhIGZyYW1lIGhhcyB0aHJlZSBudW1lcmljIHZhcmlhYmxlczogc3VpY2lkZSByYXRlLCBwb3B1bGF0aW9uLCBhbmQgbnVtYmVyIG9mIHN1aWNpZGUgZXN0aW1hdGVzLgpUaGUgZGlzdHJpYnV0aW9uIG9mIHRoZXNlIHRocmVlIHZhcmlhYmxlcyBpcyB2aXN1YWxpc2VkIHRocm91Z2ggYm94IHBsb3RzLCB3aGVyZSBpdCBpcyBkZXRlcm1pbmVkIHRoYXQgYWxsIHRocmVlIGhhdmUgYSByaWdodCBza2V3ZWQgZGlzdHJpYnV0aW9uLgpIZW5jZSwgdGhlIFR1a2V54oCZcyBtZXRob2QgaXMgdXNlZCB0byBsb29rIGZvciBvdXRsaWVycy4KSG93ZXZlciwgdGhlIGRldGVybWluZWQgb3V0bGllcnMgZG8gbm90IHNlZW0gdG8gY29tZSBmcm9tIGRhdGEgZW50cnkgZXJyb3JzLgpUaGV5IHNlZW0gdG8gY29tZSBmcm9tIGNvdW50cmllcyBoYXZpbmcgZXh0cmVtZSB2YWx1ZXMgb2Ygc3VpY2lkZSByYXRlIGFuZCBwb3B1bGF0aW9uIChlLmcuIENoaW5hIGFuZCBJbmRpYSksIHNvIHRoZXNlIG91dGxpZXJzIGFyZSBub3QgaW1wdXRlZCBhbmQgYXJlIGtlcHQuCgpGaW5hbGx5LCBhIEJveC1Db3ggdHJhbnNmb3JtYXRpb24gaXMgcGVyZm9ybWVkIG9uIHRoZSBzdWljaWRlIHJhdGUgdmFyaWFibGUgdG8gdHJ5IHRvIGNvbnZlcnQgdGhlIGRpc3RyaWJ1dGlvbiBpbnRvIGEgbm9ybWFsIGRpc3RyaWJ1dGlvbi4KCiMjIERhdGEKVGhlIGZpcnN0IGRhdGEgc2V0IGlzIHRoZSBVbml0ZWQgTmF0aW9uIHBvcHVsYXRpb24gcHJvamVjdGlvbiBieSBsb2NhdGlvbiBhbmQgZ2VuZGVyIGZyb20gdGhlIHllYXIgMTk1MCB0byAyMTAwIChVbml0ZWQgTmF0aW9ucyAyMDE5KS4KVGhlIGRhdGEgc2V0IGhhcyBzZXZlcmFsIHByb2plY3Rpb24gdmFyaWFudHMgd2hlcmUgZWFjaCB1c2VzIGRpZmZlcmVudCBhc3N1bXB0aW9ucyBvbiBwcm9qZWN0aW5nIGZ1dHVyZSBsZXZlbHMgb2YgZmVydGlsaXR5LCBtb3J0YWxpdHksIGFuZCBtaWdyYXRpb24uCmBgYHtyfQpzZXR3ZCgifi9Ecm9wYm94L2RhdGEtd3JhbmdsaW5nL2Fzc2lnbm1lbnQyIikKCnBvcHVsYXRpb24gPC0gcmVhZC5jc3YoIldQUDIwMTlfVG90YWxQb3B1bGF0aW9uQnlTZXguY3N2Iiwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQpoZWFkKHBvcHVsYXRpb24pCmBgYAoKVGhlc2UgdmFyaWFibGVzIGFyZToKCiogTG9jSUQgLSBUaGUgbG9jYXRpb24gbnVtZXJpYyBpZGVudGlmaWVyLiBUaGlzIHZhcmlhYmxlIHdpbGwgYmUgZHJvcHBlZCBiZWNhdXNlIHRoZSBpZGVudGlmaWVyIGlzIG5vdCB1c2VmdWwgZm9yIG91ciBwdXJwb3NlLgoqIExvY2F0aW9uIC0gVGhlIGxvY2F0aW9uIG5hbWUuIFRoZSBsb2NhdGlvbiBjb3VsZCBiZSBhIGNvdW50cnksIHJlZ2lvbiwgb3Igc3VicmVnaW9uIChlLmcuIENlbnRyYWwgQXNpYSksIGJ1dCB0aGlzIHJlcG9ydCB3aWxsIG9ubHkgYmUgdXNpbmcgY291bnRyaWVzIGR1ZSB0byB0aGUgbmF0dXJlIG9mIHRoZSBzZWNvbmQgZGF0YSBzZXQuCiogVmFySUQgLSBUaGUgdmFyaWFudCBudW1lcmljIGlkZW50aWZpZXIuIFRoaXMgdmFyaWFibGUgd2lsbCBiZSBkcm9wcGVkIGJlY2F1c2UgdGhlIGlkZW50aWZpZXIgaXMgbm90IHVzZWZ1bCBmb3Igb3VyIHB1cnBvc2UuCiogVmFyaWFudCAtIFByb2plY3Rpb24gdmFyaWFudCBuYW1lLiBUaGlzIHJlcG9ydCB3aWxsIG9ubHkgYmUgdXNpbmcgdGhlIG1lZGl1bSBwcm9qZWN0aW9uIHZhcmlhbnQgYmVjYXVzZSBpdCBpcyB0aGUgbW9zdCB1c2VkIChVbml0ZWQgTmF0aW9ucyAyMDE5KS4KKiBUaW1lIC0gVGhlIHllYXIgZnJvbSAxOTUwIHRvIDIxMDAuIFRoaXMgcmVwb3J0IHdpbGwgb25seSBiZSB1c2luZyAyMDE2IGJlY2F1c2UgaXQgaXMgdGhlIG1vc3QgcmVjZW50IHllYXIgYXZhaWxhYmxlIGluIHRoZSBzZWNvbmQgZGF0YSBzZXQuCiogTWlkUGVyaW9kIC0gVGhlIG1pZCBwZXJpb2Qgb2YgdGhlIGRhdGEgKGUuZy4gMTk1MC41IGluZGljYXRlcyBKdWx5IDE5NTApLiBUaGlzIHZhcmlhYmxlIHdpbGwgYmUgZHJvcHBlZCBiZWNhdXNlIGl0IGlzIG5vdCB1c2VmdWwgZm9yIG91ciBwdXJwb3NlLgoqIFBvcE1hbGUgLSBUaGUgbWFsZSBwb3B1bGF0aW9uIGluIHRob3VzYW5kcy4KKiBQb3BGZW1hbGUgLSBUaGUgZmVtYWxlIHBvcHVsYXRpb24gaW4gdGhvdXNhbmRzLgoqIFBvcFRvdGFsIC0gVGhlIG1hbGUgYW5kIGZlbWFsZSBwb3B1bGF0aW9uIGluIHRob3VzYW5kcy4gVGhpcyB2YXJpYWJsZSB3aWxsIGJlIGRyb3BwZWQgc2luY2UgaXQgY2FuIGJlIGVhc2lseSBjYWxjdWxhdGVkIGZyb20gdGhlIG1hbGUgYW5kIGZlbWFsZSBpZiBuZWVkZWQuCiogUG9wRGVuc2l0eSAtIFBvcHVsYXRpb24gcGVyIHNxdWFyZSBraWxvbWV0cmUgaW4gdGhvdXNhbmRzLiBUaGlzIHZhcmlhYmxlIHdpbGwgYmUgZHJvcHBlZCBiZWNhdXNlIHRoaXMgcmVwb3J0IGRvZXMgbm90IGRpc2N1c3MgdGhlIGRlbnNpdHkuCmBgYHtyfQojIFN1YnNldCB0aGUgcG9wdWxhdGlvbiBkYXRhIHNldC4KcG9wdWxhdGlvbkZpbHRlcmVkIDwtIHBvcHVsYXRpb24gJT4lCiAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXIoVmFyaWFudCA9PSAiTWVkaXVtIikgJT4lCiAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXIoVGltZSA9PSAyMDE2KSAlPiUKICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdChMb2NhdGlvbiwgUG9wTWFsZSwgUG9wRmVtYWxlKQpoZWFkKHBvcHVsYXRpb25GaWx0ZXJlZCkKYGBgCgpUaGUgc2Vjb25kIGRhdGEgc2V0IGlzIHRoZSBXb3JsZCBIZWFsdGggT3JnYW5pemF0aW9uIHN1aWNpZGUgcmF0ZSBlc3RpbWF0ZSAoV29ybGQgSGVhbHRoIE9yZ2FuaXphdGlvbiAyMDE4KS4KVGhlIGRhdGEgc2V0IGNvbnRhaW5zIGluZm9ybWF0aW9uIG9uIHRoZSBlc3RpbWF0ZSBvZiBzdWljaWRlIHJhdGUgb24gZWFjaCBjb3VudHJ5IGFuZCBnZW5kZXIuCldoZW4gcmVhZGluZyB0aGUgY3N2IGZpbGUsIHRoZSBmaXJzdCBsaW5lIGlzIHNraXBwZWQgYmVjYXVzZSBpdCBjb250YWlucyB0aGUgY29sdW1uIGhlYWRlci4KYGBge3J9CnN1aWNpZGUgPC0gcmVhZC5jc3YoImRhdGEuY3N2Iiwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFLCBza2lwPTEpCmhlYWQoc3VpY2lkZSkKYGBgCgpUaGVzZSB2YXJpYWJsZXMgYXJlOgoKKiBDb3VudHJ5IC0gVGhlIGNvdW50cnkgbmFtZS4KKiBTZXggLSBUaGUgZ2VuZGVyLgoqIFgyMDE2LCBYMjAxNSwgWDIwMTAsIFgyMDAwIC0gVGhlIHN1aWNpZGUgcmF0ZSBlc3RpbWF0ZSBmb3IgdGhhdCB5ZWFyIHBlciAxMDAsMDAwIHBvcHVsYXRpb24uIFNpbmNlIHRoZSB5ZWFyIGF2YWlsYWJsZSBpcyBub3QgY29udGludW91cywgYW5hbHlzaW5nIHRoZSB0cmVuZCB3b3VsZCBub3QgbWFrZSBzbyBtdWNoIHNlbnNlLiBIZW5jZSwgdGhpcyByZXBvcnQgd2lsbCBvbmx5IHVzZSB0aGUgbW9zdCByZWNlbnQgeWVhciBhdmFpbGFibGUsIHRoYXQgaXMsIDIwMTYuCmBgYHtyfQojIFNpbmNlIHdlIGFyZSBvbmx5IGludGVyZXN0ZWQgaW4gdGhlIHllYXIgMjAxNiwgcmVtb3ZlIGNvbHVtbnMgb2Ygb3RoZXIgeWVhcnMuCnN1aWNpZGVGaWx0ZXJlZCA8LSBzdWljaWRlWyAsICEobmFtZXMoc3VpY2lkZSkgJWluJSBjKCJYMjAxNSIsICJYMjAxMCIsICJYMjAwMCIpKV0KCiMgQ2hhbmdlIGNvbHVtbiBYMjAxNiB0byBTdWljaWRlUmF0ZQpuYW1lcyhzdWljaWRlRmlsdGVyZWQpW25hbWVzKHN1aWNpZGVGaWx0ZXJlZCkgPT0gIlgyMDE2Il0gPC0gIlN1aWNpZGVSYXRlIgoKaGVhZChzdWljaWRlRmlsdGVyZWQpCmBgYAoKIyMJVGlkeSAmIE1hbmlwdWxhdGUgRGF0YSBJClRoZSBwb3B1bGF0aW9uIGRhdGEgc2V0IGlzIHVudGlkeSBiZWNhdXNlIHRoZSB2YXJpYWJsZSBnZW5kZXIgZG9lcyBub3QgaGF2ZSBpdHMgb3duIGNvbHVtbiBhbmQgdGhlIHZhcmlhYmxlIHBvcHVsYXRpb24gaXMgc3ByZWFkIG92ZXIgdHdvIGNvbHVtbnMuCgpTbyB0aGUgd2lkZSBkYXRhIGZyYW1lIGlzIGNvbnZlcnRlZCBpbnRvIGEgbG9uZyBmb3JtYXQgd2l0aCB0aGUgYGBnYXRoZXIoKWBgIGZ1bmN0aW9uIGJ5IGNvbnZlcnRpbmcgdGhlICJQb3BNYWxlIiBhbmQgIlBvcEZlbWFsZSIgY29sdW1ucyBpbnRvICJTZXgiIGFuZCAiUG9wdWxhdGlvbiIuClRoZW4gdGhlIHN0cmluZyAiUG9wIiBmcm9tICJQb3BNYWxlIiBhbmQgIlBvcEZlbWFsZSIgaXMgcmVtb3ZlZC4KYGBge3J9CnBvcHVsYXRpb25UaWRpZWQgPC0gcG9wdWxhdGlvbkZpbHRlcmVkICU+JQogICAgICAgICAgICAgICAgICAgIGdhdGhlcihQb3BNYWxlLCBQb3BGZW1hbGUsIGtleSA9ICJTZXgiLCB2YWx1ZSA9ICJQb3B1bGF0aW9uIikgJT4lCiAgICAgICAgICAgICAgICAgICAgbXV0YXRlKFNleCA9IHN1YigiUG9wIiwgIiIsIFNleCkpCmhlYWQocG9wdWxhdGlvblRpZGllZCkKYGBgCgojIyBNZXJnZQpBcyBtZW50aW9uZWQgaW4gdGhlIGZpcnN0IHNlY3Rpb24sIHRoZSBwb3B1bGF0aW9uIGRhdGEgc2V0IGNvbnRhaW5zIGxvY2F0aW9ucyBvdGhlciB0aGFuIGNvdW50cmllcyB3aGlsZSB0aGUgc3VpY2lkZSBkYXRhIHNldCBjb250YWlucyBvbmx5IGNvdW50cmllcy4KU28gdGhlIHR3byBkYXRhIHNldHMgd2lsbCBiZSBqb2luZWQgYnkgY291bnRyaWVzIGFuZCBnZW5kZXIuCkJ1dCBmaXJzdCwgd2Ugd2lsbCBjaGVjayB3aGljaCBjb3VudHJpZXMgaW4gdGhlIHN1aWNpZGUgZGF0YSBzZXQgdGhhdCBhcmUgbm90IGluIHRoZSBwb3B1bGF0aW9uIGRhdGEgc2V0LgpgYGB7cn0KYW50aV9qb2luKHN1aWNpZGVGaWx0ZXJlZCwgcG9wdWxhdGlvblRpZGllZCwgYnkgPSBjKCJDb3VudHJ5IiA9ICJMb2NhdGlvbiIpKSAlPiUgZGlzdGluY3QoQ291bnRyeSkKYGBgCgpJIGxvb2tlZCBhdCB0aGUgcG9wdWxhdGlvbiBkYXRhIHNldCBhbmQgaXQgdHVybnMgb3V0IHRoZXNlIGNvdW50cmllcyBhY3R1YWxseSBleGlzdCBpbiB0aGUgcG9wdWxhdGlvbiBkYXRhIHNldCB1bmRlciBhIGRpZmZlcmVudCBuYW1lLgpTbyBsZXQgdXMgbWFudWFsbHkgY2hhbmdlIHRoZWlyIG5hbWVzIHNvIHRoYXQgdGhleSBtYXRjaC4KVGhlbiB3ZSBtZXJnZSB0aGUgdHdvIGRhdGEgc2V0cyBieSBjb3VudHJ5IGFuZCBnZW5kZXIuClNpbmNlIGFsbCB0aGUgY291bnRyaWVzIGluIHRoZSBzdWljaWRlIGRhdGEgc2V0IGV4aXN0cyBpbiB0aGUgcG9wdWxhdGlvbiBkYXRhIHNldCwgd2UgY2FuIGVpdGhlciB1c2UgbGVmdCBqb2luIG9uIHN1aWNpZGUgZGF0YSBzZXQgb3IgaW5uZXIgam9pbi4KV2Ugd2lsbCB1c2UgaW5uZXIgam9pbi4KYGBge3J9CiMgUmVuYW1lIGNvdW50cmllcyB0aGF0IGhhdmUgZGlmZmVyZW50IG5hbWUgb24gdGhlIHR3byBkYXRhIHNldHMuCnBvcHVsYXRpb25UaWRpZWQkTG9jYXRpb25bcG9wdWxhdGlvblRpZGllZCRMb2NhdGlvbiA9PSAiRGVtLiBQZW9wbGUncyBSZXB1YmxpYyBvZiBLb3JlYSJdIDwtICJEZW1vY3JhdGljIFBlb3BsZSdzIFJlcHVibGljIG9mIEtvcmVhIgpwb3B1bGF0aW9uVGlkaWVkJExvY2F0aW9uW3BvcHVsYXRpb25UaWRpZWQkTG9jYXRpb24gPT0gIk5vcnRoIE1hY2Vkb25pYSJdIDwtICJSZXB1YmxpYyBvZiBOb3J0aCBNYWNlZG9uaWEiCnBvcHVsYXRpb25UaWRpZWQkTG9jYXRpb25bcG9wdWxhdGlvblRpZGllZCRMb2NhdGlvbiA9PSAiTWljcm9uZXNpYSAoRmVkLiBTdGF0ZXMgb2YpIl0gPC0gIk1pY3JvbmVzaWEgKEZlZGVyYXRlZCBTdGF0ZXMgb2YpIgpwb3B1bGF0aW9uVGlkaWVkJExvY2F0aW9uW3BvcHVsYXRpb25UaWRpZWQkTG9jYXRpb24gPT0gIlVuaXRlZCBLaW5nZG9tIl0gPC0gIlVuaXRlZCBLaW5nZG9tIG9mIEdyZWF0IEJyaXRhaW4gYW5kIE5vcnRoZXJuIElyZWxhbmQiCgpkZiA8LSBzdWljaWRlRmlsdGVyZWQgJT4lCiAgICAgIGlubmVyX2pvaW4ocG9wdWxhdGlvblRpZGllZCwgYygiQ291bnRyeSIgPSAiTG9jYXRpb24iLCAiU2V4IiA9ICJTZXgiKSkKYGBgCgojIyBVbmRlcnN0YW5kIApUaGUgbWVyZ2VkIGRhdGEgc2V0OgpgYGB7cn0Kc3RyKGRmKQpgYGAKCkFzIHNlZW4gYWJvdmUsIHRoZSBnZW5kZXIgdmFyaWFibGUgaGFzIHRoZSBjaGFyYWN0ZXIgdHlwZSwgc28gd2UgbmVlZCB0byBjb252ZXJ0IHRoZSBnZW5kZXIgdmFyaWFibGUgaW50byBhbiB1bm9yZGVyZWQgZmFjdG9yLgpgYGB7cn0KZGYkU2V4IDwtIGZhY3RvcihkZiRTZXgsIGxldmVscz1jKCJNYWxlIiwiRmVtYWxlIiksIG9yZGVyZWQgPSBGQUxTRSkKYGBgCgpOb3cgYWxsIHRoZSB2YXJpYWJsZXMgYXJlIGluIHRoZSBjb3JyZWN0IHR5cGUuCgoqIENvdW50cnkgLSBUaGUgY291bnRyeSBuYW1lLgoqIFNleCAtIFRoZSBnZW5kZXIuIEFuIHVub3JkZXJlZCBmYWN0b3IgdmFyaWFibGUgd2l0aCAyIHBvc3NpYmxlIHZhbHVlczogbWFsZSBvciBmZW1hbGUuCiogU3VpY2lkZVJhdGUgLSBUaGUgMjAxNiBlc3RpbWF0ZSBzdWljaWRlIHJhdGUgcGVyIDEwMCwwMDAgcG9wdWxhdGlvbi4KKiBQb3B1bGF0aW9uIC1UaGUgMjAxNiBlc3RpbWF0ZSBwb3B1bGF0aW9uIGluIHRob3VzYW5kcy4KYGBge3J9CnN0cihkZikKYGBgCgojIwlUaWR5ICYgTWFuaXB1bGF0ZSBEYXRhIElJIAoKQSBuZXcgdmFyaWFibGUgZm9yIHRoZSBlc3RpbWF0ZSBudW1iZXIgb2Ygc3VpY2lkZSBpbiB0aG91c2FuZHMgZm9yIHRoZSB5ZWFyIDIwMTYgd2lsbCBiZSBhZGRlZC4KVG8gZG8gdGhpcywgdGhlIHBvcHVsYXRpb24gdmFyaWFibGUgaXMgbXVsdGlwbGllZCB3aXRoIHN1aWNpZGUgcmF0ZSBhbmQgZGl2aWRlZCBieSAxMDAsMDAwIGJlY2F1c2UgdGhlIHN1aWNpZGUgcmF0ZSBpcyBwZXIgMTAwLDAwMCBwb3B1bGF0aW9uLgpUaGVuIHRoZSByZXN1bHQgaXMgcm91bmRlZCB0byAzIGRlY2ltYWwgcGxhY2VzIGJlY2F1c2UgdGhlIHZhcmlhYmxlIGlzIGluIHRob3VzYW5kcy4KYGBge3J9CmRmIDwtIGRmICU+JSBtdXRhdGUoTnVtT2ZTdWljaWRlID0gcm91bmQoUG9wdWxhdGlvbiAqIFN1aWNpZGVSYXRlIC8gMTAwMDAwLCAzKSkKaGVhZChkZikKYGBgCgpUaGUgbmV3IHZhcmlhYmxlIGlzIGNhbGxlZCAiTnVtT2ZTdWljaWRlIi4KRm9yIGV4YW1wbGUsIHRoZSBlc3RpbWF0ZSBudW1iZXIgb2Ygc3VpY2lkZSBmb3IgQWZnaGFuaXN0YW4gbWFsZSBpbiAyMDE2IGlzIDEsOTI4IG1lbi4KUGxlYXNlIG5vdGUgdGhhdCB0aGUgbnVtYmVyIG9mIHN1aWNpZGUgaXMgbm90IHRoZSBhY3R1YWwgbnVtYmVyIG9mIHN1aWNpZGUgZm9yIHRoZSB5ZWFyIDIwMTYuClRoZSB2YXJpYWJsZXMgc3VpY2lkZSByYXRlIGFuZCBwb3B1bGF0aW9uIGFyZSBlc3RpbWF0ZXMgZnJvbSBXSE8gYW5kIFVOIHJlc3BlY3RpdmVseS4KSGVuY2UsIHRoZSByZXN1bHRpbmcgbnVtYmVyIG9mIHN1aWNpZGUgaXMgYWxzbyBhbiBlc3RpbWF0ZS4KCiMjCVNjYW4gSSAKClRoZSBgYGlzLm5hKClgYCBmdW5jdGlvbiB3aWxsIGJlIHVzZWQgdG8gZGV0ZXJtaW5lIG1pc3NpbmcgdmFsdWVzLgpJdCByZXR1cm5zIGBgVFJVRWBgIGlmIHRoZSB2YWx1ZSBpcyBgYE5BYGAuClNvIHdlIHdpbGwgdXNlIGl0IHdpdGggYGBjb2xTdW1zKClgYCB0byBjb3VudCB0aGUgbnVtYmVyIG9mIGBgTkFgYCBvbiBlYWNoIGNvbHVtbi4KRGV0ZXJtaW5lIHRoZSBudW1iZXIgb2YgbWlzc2luZyB2YWx1ZXMgb24gZWFjaCBvZiB0aGUgY29sdW1uczoKYGBge3J9CmNvbFN1bXMoaXMubmEoZGYpKQpgYGAKSXQgc2VlbXMgdGhhdCB0aGVyZSBpcyBubyBtaXNzaW5nIHZhbHVlcyBpbiB0aGUgZGF0YSBmcmFtZS4KCk5leHQsIHdlIHdpbGwgY2hlY2sgd2hldGhlciB0aGUgZGF0YSBpcyBjb21wbGV0ZSBpbiB0ZXJtcyBvZiBjb3VudHJpZXMgYW5kIGdlbmRlci4KRm9yIGV4YW1wbGUsIHRoZXJlIGFyZSBubyBkdXBsaWNhdGVzIChlLmcuIG11bHRpcGxlIHJvd3MgZm9yIEF1c3RyYWxpYSBtYWxlKSBvciBtaXNzaW5nIHJvd3MuClRoYXQgaXMsIHRoZSBudW1iZXIgb2Ygcm93cyBpbiB0aGUgZGF0YSBmcmFtZSBzaG91bGQgYmUgZXF1YWwgdG8gdGhlIG51bWJlciBvZiB1bmlxdWUgY291bnRyaWVzIG11bHRpcGxpZWQgYnkgMiAobWFsZSBhbmQgZmVtYWxlKS4KV2hpY2ggc2hvdWxkIGFsc28gYmUgZXF1YWwgdG8gdGhlIG51bWJlciBvZiB1bmlxdWUgY291bnRyeS1nZW5kZXIgY29tYmluYXRpb24uCmBgYHtyfQojIE51bWJlciBvZiB1bmlxdWUgY291bnRyaWVzLgpkZiRDb3VudHJ5ICU+JSB1bmlxdWUgJT4lIGxlbmd0aAoKIyBOdW1iZXIgb2YgdW5pcXVlIGdlbmRlci4KZGYkU2V4ICU+JSB1bmlxdWUgJT4lIGxlbmd0aAoKIyBOdW1iZXIgb2YgdW5pcXVlIGNvdW50cnktZ2VuZGVyIGNvbWJpbmF0aW9uLgpkZiAlPiUgdW5pdGUoQ291bnRyeVNleCwgQ291bnRyeSwgU2V4KSAlPiUgLiRDb3VudHJ5U2V4ICU+JSB1bmlxdWUgJT4lIGxlbmd0aAoKIyBOdW1iZXIgb2Ygcm93cyBpbiB0aGUgZGF0YSBmcmFtZS4KbnJvdyhkZikKYGBgClRoZXJlIGFyZSAxODMgZGlmZmVyZW50IGNvdW50cmllcyBhbmQgMiBkaWZmZXJlbnQgbGV2ZWxzIGZvciBnZW5kZXIuCmBgMTgzKjIgPT0gMzY2YGAuCkxvb2tzIGxpa2Ugd2UgaGF2ZSBhIGNvbXBsZXRlIGRhdGEuCgojIwlTY2FuIElJCgpXZSBoYXZlIHRocmVlIG51bWVyaWMgdmFyaWFibGVzOiBzdWljaWRlIHJhdGUsIHBvcHVsYXRpb24sIGFuZCBudW1iZXIgb2Ygc3VpY2lkZSBlc3RpbWF0ZXMuCkZpcnN0LCB3ZSB3aWxsIGRldGVybWluZSB3aGV0aGVyIHRoZSBkaXN0cmlidXRpb24gb2YgdGhlc2UgdGhyZWUgdmFyaWFibGVzIGFyZSBub3JtYWwgb3Igbm90IHdpdGggYm94IHBsb3RzLgpXZSB3aWxsIGNyZWF0ZSBhIGxvbmcgZGF0YSBmcmFtZSBieSBjb21iaW5pbmcgdGhlIHRocmVlIG51bWVyaWMgdmFyaWFibGVzIGludG8gYSBzaW5nbGUgY29sdW1uIHNvIHRoYXQgd2UgY291bGQgZWFzaWx5IHBsb3QgYSBmYWNldGVkIGJveCBwbG90LgpgYGB7cn0KbG9uZ0RmIDwtIGRmICU+JQogICAgICAgICAgZ2F0aGVyKFN1aWNpZGVSYXRlLCBQb3B1bGF0aW9uLCBOdW1PZlN1aWNpZGUsIGtleSA9ICJWYXIiLCB2YWx1ZSA9ICJWYWx1ZSIpCgojIENvbnZlcnQgdGhlIFZhciBjb2x1bW4gaW50byBhIGZhY3RvciBzbyB0aGF0IHdlIGNhbiBvcmRlciB0aGUgYm94IHBsb3QuCmxvbmdEZiRWYXIgPC0gZmFjdG9yKGxvbmdEZiRWYXIsIGxldmVscz1jKCJTdWljaWRlUmF0ZSIsICJQb3B1bGF0aW9uIiwgIk51bU9mU3VpY2lkZSIpKQoKIyBBIGJldHRlciBkZXNjcmlwdGlvbiBvZiB0aGUgdmFyaWFibGVzIGZvciB0aGUgYm94IHBsb3RzLgpsZXZlbHMobG9uZ0RmJFZhcikgPC0gYygiU3VpY2lkZSBwZXIgMTAwLDAwMCIsICJQb3B1bGF0aW9uIGluIHRob3VzYW5kcyIsICJOdW0gb2YgU3VpY2lkZSBpbiB0aG91c2FuZHMiKQoKYnAgPC0gbG9uZ0RmICU+JQogICAgICBnZ3Bsb3QoYWVzKHg9VmFyLCB5PVZhbHVlKSkgKyAKICAgICAgZ2VvbV9ib3hwbG90KGFlcyhmaWxsPVNleCkpICsKICAgICAgZmFjZXRfd3JhcCggfiBWYXIsIHNjYWxlcz0iZnJlZSIpCmJwCmBgYAoKU3VpY2lkZSByYXRlIGhhcyBhIHJpZ2h0IHNrZXdlZCBkaXN0cmlidXRpb24uCgpIb3dldmVyLCB0aGUgYm94IHBsb3RzIGZvciBwb3B1bGF0aW9uIGFuZCBudW1iZXIgb2Ygc3VpY2lkZSBhcmUgbm90IHF1aXRlIHZpc2libGUgYmVjYXVzZSB0aGVyZSBhcmUgZmV3IHZhbHVlcyB0aGF0IGFyZSBzdWJzdGFudGlhbGx5IGxhcmdlciB0aGFuIHRoZSByZXN0LgpTbyB3ZSB3aWxsIGxpbWl0IHRoZSB5IGF4aXMgb2YgdGhlIGJveCBwbG90IGJ5IHVzaW5nIHRoZSBmdW5jdGlvbiBgYGNvb3JkX2NhcnRlc2lhbigpYGAuClRoZSBmdW5jdGlvbiBkb2VzIG5vdCBleGNsdWRlIGRhdGEsIGl0IG9ubHkgbGltaXRzIHRoZSBheGlzIGRyYXduLgpUaHVzLCB0aGUgbWVkaWFuIGFuZCBxdWFydGlsZXMgd2lsbCBub3QgYmUgYWZmZWN0ZWQuCmBgYHtyfQpicFBvcCA8LSBsb25nRGYgJT4lCiAgICAgICAgIGZpbHRlcihWYXI9PSJQb3B1bGF0aW9uIGluIHRob3VzYW5kcyIpICU+JQogICAgICAgICBnZ3Bsb3QoYWVzKHg9VmFyLCB5PVZhbHVlKSkgKyAKICAgICAgICAgZ2VvbV9ib3hwbG90KGFlcyhmaWxsPVNleCkpICsKICAgICAgICAgZmFjZXRfd3JhcCggfiBWYXIsIHNjYWxlcz0iZnJlZSIpICsKICAgICAgICAgY29vcmRfY2FydGVzaWFuKHlsaW0gPSBjKDAsIDEwMDAwMCkpICMgTGltaXQgdGhlIHkgYXhpcyB0byAxMDAsMDAwCmJwUG9wCmBgYAoKYGBge3J9CmJwTnVtT2ZTdWljaWRlIDwtIGxvbmdEZiAlPiUKICAgICAgICAgICAgICAgICAgZmlsdGVyKFZhcj09Ik51bSBvZiBTdWljaWRlIGluIHRob3VzYW5kcyIpICU+JQogICAgICAgICAgICAgICAgICBnZ3Bsb3QoYWVzKHg9VmFyLCB5PVZhbHVlKSkgKyAKICAgICAgICAgICAgICAgICAgZ2VvbV9ib3hwbG90KGFlcyhmaWxsPVNleCkpICsKICAgICAgICAgICAgICAgICAgZmFjZXRfd3JhcCggfiBWYXIsIHNjYWxlcz0iZnJlZSIpICsKICAgICAgICAgICAgICAgICAgY29vcmRfY2FydGVzaWFuKHlsaW0gPSBjKDAsIDEwKSkgIyBMaW1pdCB0aGUgeSBheGlzIHRvIDEwCmJwTnVtT2ZTdWljaWRlCmBgYAoKSXQgc2VlbXMgdGhhdCBwb3B1bGF0aW9uIGFuZCBudW1iZXIgb2Ygc3VpY2lkZSBhbHNvIGhhdmUgcmlnaHQgc2tld2VkIGRpc3RyaWJ1dGlvbi4KVXNpbmcgdGhlIHotc2NvcmUgbWV0aG9kIHRvIGRldGVybWluZSBvdXRsaWVycyB3aWxsIG5vdCBiZSBhcHByb3ByaWF0ZSBiZWNhdXNlIGl0IHJlcXVpcmVzIHRoZSB2YWx1ZXMgdG8gaGF2ZSBhIG5vcm1hbCBkaXN0cmlidXRpb24uClNvIHdlIHdpbGwgdXNlIHRoZSBUdWtleeKAmXMgbWV0aG9kLgoKQWNjb3JkaW5nIHRvIHRoZSBib3ggcGxvdHMgYWJvdmUsIGFsbCB0aGUgb3V0bGllcnMgYXJlIGxvY2F0ZWQgYWJvdmUgdGhlIGJveCBwbG90LCBzbyB3ZSBvbmx5IG5lZWQgdG8gY2FsY3VsYXRlIHRoZSBtYXhpbXVtIGZlbmNlLgpNb3Jlb3ZlciwgdGhlIGJveCBwbG90cyBzaG93IHRoYXQgdGhlcmUgYXJlIHN1YnN0YW50aWFsIGRpZmZlcmVuY2UgYmV0d2VlbiBtYWxlIGFuZCBmZW1hbGUgc3VpY2lkZSByYXRlIGFuZCBudW1iZXIgb2Ygc3VpY2lkZSwgc28gd2Ugd2lsbCBsb29rIGZvciBvdXRsaWVycyBieSBnZW5kZXIuCgpXZSBuZWVkIHRvIGNhbGN1bGF0ZSB0aGUgbWF4aW11bSBmZW5jZSBvbiBlYWNoIG9mIHRoZSB0aHJlZSBudW1lcmljIHZhcmlhYmxlcyBvbiBlYWNoIG9mIHRoZSBnZW5kZXIuClRoZSBtYXhpbXVtIGZlbmNlIGlzIGNhbGN1bGF0ZWQgYnkgYGBRMyArIDEuNSAqIElRUmBgIHdoZXJlIGBgUTNgYCBpcyB0aGUgNzVedGheIHBlcmNlbnRpbGUgYW5kIGBgSVFSYGAgaXMgdGhlIGludGVyLXF1YXJ0aWxlIHJhbmdlLgpgYGB7cn0Kc3VtbWFyeSA8LSBsb25nRGYgJT4lCiAgICAgICAgICAgZ3JvdXBfYnkoVmFyLCBTZXgpICU+JQogICAgICAgICAgIHN1bW1hcmlzZShNYXggPSBxdWFudGlsZShWYWx1ZSwgMC43NSkgKyAxLjUgKiBJUVIoVmFsdWUpKQpzdW1tYXJ5CmBgYAoKVGhlICJNYXgiIGNvbHVtbiByZXByZXNlbnRzIHRoZSBtYXhpbXVtIGZlbmNlIHdoZXJlIHZhbHVlcyBncmVhdGVyIHRoYW4gdGhpcyB3aWxsIGJlIGNvbnNpZGVyZWQgb3V0bGllcnMuCgpUbyBmaW5kIHRoZSBzdWljaWRlIHJhdGUgb3V0bGllcnMsIHdlIGhhdmUgdG8gZmlsdGVyIHRoZSBkYXRhIGZyYW1lIGJ5IGdlbmRlciBhbmQgbG9vayBmb3Igc3VpY2lkZSByYXRlIHRoYXQgaXMgaGlnaGVyIHRoYW4gMzMuNDUgKGZvciBtYWxlKSBhbmQgMTEuNjAgKGZvciBmZW1hbGUpLgoKU3VpY2lkZSByYXRlIG91dGxpZXJzICh0aGUgb3V0bGllcnMgYXJlIHNvcnRlZCBieSBnZW5kZXIgYW5kIHN1aWNpZGUgcmF0ZSk6CmBgYHtyfQpkZiAlPiUKICBmaWx0ZXIoKFNleCA9PSAiTWFsZSIgJiBTdWljaWRlUmF0ZSA+IHN1bW1hcnkkTWF4WzFdKSB8IChTZXggPT0gIkZlbWFsZSIgJiBTdWljaWRlUmF0ZSA+IHN1bW1hcnkkTWF4WzJdKSkgJT4lCiAgYXJyYW5nZShTZXgsIGRlc2MoU3VpY2lkZVJhdGUpKQpgYGAKClRoZSBzYW1lIHN0ZXAgaXMgYWxzbyBhcHBsaWNhYmxlIGluIGZpbmRpbmcgdGhlIHBvcHVsYXRpb24gYW5kIG51bWJlciBvZiBzdWljaWRlIG91dGxpZXJzLgpCdXQgcGxlYXNlIG5vdGUgdGhhdCBkaWZmZXJlbnQgbWF4IGZlbmNlIGlzIHVzZWQgKGUuZy4gYGBzdW1tYXJ5JE1heFszXSlgYCkuCgpQb3B1bGF0aW9uIG91dGxpZXJzICh0aGUgb3V0bGllcnMgYXJlIHNvcnRlZCBieSBnZW5kZXIgYW5kIHBvcHVsYXRpb24pOgpgYGB7cn0KZGYgJT4lCiAgZmlsdGVyKChTZXggPT0gIk1hbGUiICYgUG9wdWxhdGlvbiA+IHN1bW1hcnkkTWF4WzNdKSB8IChTZXggPT0gIkZlbWFsZSIgJiBQb3B1bGF0aW9uID4gc3VtbWFyeSRNYXhbNF0pKSAlPiUKICBhcnJhbmdlKFNleCwgZGVzYyhQb3B1bGF0aW9uKSkKYGBgCgpOdW1iZXIgb2Ygc3VpY2lkZSBvdXRsaWVycyAodGhlIG91dGxpZXJzIGFyZSBzb3J0ZWQgYnkgZ2VuZGVyIGFuZCBudW1iZXIgb2Ygc3VpY2lkZSk6CmBgYHtyfQpkZiAlPiUKICBmaWx0ZXIoKFNleCA9PSAiTWFsZSIgJiBOdW1PZlN1aWNpZGUgPiBzdW1tYXJ5JE1heFs1XSkgfCAoU2V4ID09ICJGZW1hbGUiICYgTnVtT2ZTdWljaWRlID4gc3VtbWFyeSRNYXhbNl0pKSAlPiUKICBhcnJhbmdlKFNleCwgZGVzYyhOdW1PZlN1aWNpZGUpKQpgYGAKCkkgdGhpbmsgdGhlc2Ugb3V0bGllcnMgYXJlIG5vdCBhIHJlc3VsdCBvZiBkYXRhIGVudHJ5IGVycm9ycywgc28gaXQgd291bGQgbm90IGJlIGdvb2QgaWRlYSB0byByZW1vdmUgdGhlbS4KVGhhdCBpcywgSSB0aGluayB0aGUgbnVtYmVycyBvbiB0aGVzZSBvdXRsaWVycyBzZWVtIHRvIG1ha2Ugc2Vuc2UuCkZvciBleGFtcGxlLCBDaGluYSBhbmQgSW5kaWEgYXJlIHRoZSB0d28gbW9zdCBwb3B1bGF0ZWQgY291bnRyaWVzIGluIHRoZSB3b3JsZC4KSSBhbHNvIGRpZCBzb21lIHF1aWNrIGdvb2dsaW5nIG9uIHRoZSBudW1iZXIgb2Ygc3VpY2lkZXMgb24gc2V2ZXJhbCBvZiB0aGVzZSBjb3VudHJpZXMgdGhhdCBhcmUgY29uc2lkZXJlZCBvdXRsaWVycyBhbmQgdGhlIG51bWJlcnMgc2VlbSB0byBiZSBhY2N1cmF0ZS4KVGhlcmVmb3JlLCBJIGRlY2lkZWQgbm90IHRvIGltcHV0ZSB0aGVzZSBvdXRsaWVycyBhbmQgbGVhdmUgdGhlbSBhcyB0aGV5IGFyZS4KCiMjCVRyYW5zZm9ybSAKCkluIHRoaXMgc2VjdGlvbiwgd2Ugd2lsbCBhcHBseSBhIHRyYW5zZm9ybWF0aW9uIG9uIHRoZSBzdWljaWRlIHJhdGUgdmFyaWFibGUgdG8gY29udmVydCB0aGUgZGlzdHJpYnV0aW9uIGludG8gYSBtb3JlIHN5bW1ldHJpYyAvIG5vcm1hbCBkaXN0cmlidXRpb24uCkFzIHNlZW4gb24gdGhlIGJveCBwbG90IGluIG9uZSBvZiB0aGUgZWFybGllciBzZWN0aW9uLCB0aGUgc3VpY2lkZSByYXRlIHZhcmlhYmxlIGhhdmUgYSByaWdodCBza2V3ZWQgZGlzdHJpYnV0aW9uLgpUaGVyZSBhcmUgc2V2ZXJhbCB3YXlzIHRvIHJlZHVjZSB0aGUgc2tld25lc3Mgb2YgYSByaWdodCBza2V3ZWQgZGlzdHJpYnV0aW9uLCBzdWNoIGFzIGxvZyB0cmFuc2Zvcm1hdGlvbiwgc3F1YXJlIHJvb3QgdHJhbnNmb3JtYXRpb24sIHJlY2lwcm9jYWwgdHJhbnNmb3JtYXRpb24sIGFuZCBCb3gtQ294IHRyYW5zZm9ybWF0aW9uLgpJIHRyaWVkIGVhY2ggb2YgdGhlc2UgcG9zc2libGUgbWV0aG9kcyB3aXRoIGRpZmZlcmVudCBwYXJhbWV0ZXJzIGFuZCBkZXRlcm1pbmVkIHRoYXQgdGhlIEJveC1Db3ggdHJhbnNmb3JtYXRpb24gc2VlbXMgdG8gd29yayBzbGlnaHRseSBiZXR0ZXIgdGhhbiB0aGUgcmVzdCBmb3IgdGhlIHN1aWNpZGUgcmF0ZSB2YXJpYWJsZS4KU28gaW4gdGhpcyBzZWN0aW9uLCB3ZSB3aWxsIGRvIGEgQm94LUNveCB0cmFuc2Zvcm1hdGlvbiBvbiB0aGUgc3VpY2lkZSByYXRlIHZhcmlhYmxlLgoKV2UgaGF2ZSBkZXRlcm1pbmVkIGluIHRoZSBwcmV2aW91cyBzZWN0aW9uIHRoYXQgdGhlIHN1aWNpZGUgcmF0ZSBkaXN0cmlidXRpb24gaXMgYWZmZWN0ZWQgYnkgZ2VuZGVyLgpTbyB3ZSB3aWxsIGFwcGx5IHRoZSBCb3gtQ294IHRyYW5zZm9ybWF0aW9uIGJ5IGdlbmRlci4KRmlyc3QsIHdlIHdpbGwgZmlsdGVyIHRoZSBzdWljaWRlIHJhdGUgdmFyaWFibGUgYnkgZ2VuZGVyLgpUaGVuIGFwcGx5IHRoZSBCb3gtQ294IHRyYW5zZm9ybWF0aW9uLgpUaGVuIHBsb3QgdGhlIGhpc3RvZ3JhbSB0byB2aXN1YWxpc2UgdGhlIGRpc3RyaWJ1dGlvbi4KYGBge3J9Cm1hbGVEZiA8LSBkZiAlPiUgZmlsdGVyKFNleD09Ik1hbGUiKQpib3hDb3hNYWxlU3VpY2lkZVJhdGUgPC0gQm94Q294KG1hbGVEZiRTdWljaWRlUmF0ZSwgbGFtYmRhPSJhdXRvIikKaGlzdChib3hDb3hNYWxlU3VpY2lkZVJhdGUpCmBgYAoKSXQgc2VlbXMgdGhhdCB0aGVyZSBpcyBhIHZhbHVlIChhcm91bmQgLTI1MDAwKSB0aGF0IGlzIHZlcnkgZmFyIGZyb20gdGhlIHJlc3Qgb2YgdGhlIGdyb3VwLgpMZXQgdXMgc2VlIGZpbmQgdGhlIHZhbHVlIG9mIHRoaXMgIm91dGxpZXIiIGJ5IHNvcnRpbmcgdGhlIHRyYW5zZm9ybWVkIHZhbHVlcyBhbmQgbG9vayBhdCB0aGUgZmlyc3QgZmV3IHZhbHVlcy4KYGBge3J9CmJveENveE1hbGVTdWljaWRlUmF0ZSAlPiUgc29ydCAlPiUgaGVhZApgYGAKCkxldCB1cyBmaW5kIHRoZSBsYW1iZGEgdmFsdWUgdXNlZDoKYGBge3J9CmJveENveE1hbGVTdWljaWRlUmF0ZQpgYGAKClRoZSBsYW1iZGEgdXNlZCBpcyA0LjEwMjI1OWUtMDUuCgpUaGVuIHdlIGNhbiBmaW5kIHRoZSBvcmlnaW5hbCByb3cgb2YgdGhpcyAib3V0bGllciIgYnkgZmluZGluZyB0aGUgaW5kZXggb2YgdGhlIHZhbHVlIGluIHRoZSB0cmFuc2Zvcm1lZCB2YXJpYWJsZS4KYGBge3J9Cm1hbGVEZlt3aGljaChib3hDb3hNYWxlU3VpY2lkZVJhdGUgPT0gbWluKGJveENveE1hbGVTdWljaWRlUmF0ZSkpLCBdCmBgYApUaGUgb3JpZ2luYWwgc3VpY2lkZSByYXRlIGlzIDAuIFdoaWNoIG1ha2VzIHNlbnNlIGJlY2F1c2UgdGhlIEJveC1Db3ggZm9ybXVsYSBpcyAkXGZyYWN7eV5cbGFtYmRhLTF9e1xsYW1iZGF9JCBhbmQgaWYgJHkkIGlzIDAgYW5kICRcbGFtYmRhJCBpcyA0LjEwMjI1OWUtMDUsIHRoZW4gdGhlIHJlc3VsdCBpcyAkXGZyYWN7LTF9ezQuMTAyMjU5ZS0wNX0gPSAtMjQzNzYuODEkCgpXZSB3aWxsIHJlbW92ZSB0aGlzIHZhbHVlIGFuZCBkcmF3IHRoZSBoaXN0b2dyYW0gYWdhaW4gc28gdGhhdCB3ZSBjYW4gc2VlIGEgYmV0dGVyIHBpY3R1cmUgb2YgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgcmVzdCBvZiB0aGUgZ3JvdXAuCmBgYHtyfQpoaXN0KGJveENveE1hbGVTdWljaWRlUmF0ZVtib3hDb3hNYWxlU3VpY2lkZVJhdGUgIT0gbWluKGJveENveE1hbGVTdWljaWRlUmF0ZSldLCBicmVha3M9MjApCmBgYAoKVGhlIGRpc3RyaWJ1dGlvbiBpcyBub3QgZXhhY3RseSBzeW1tZXRyaWMgYnV0IGlzIGRlZmluaXRlbHkgbW9yZSBzaW1pbGFyIHRvIGEgbm9ybWFsIGRpc3RyaWJ1dGlvbiB0aGFuIHRoZSBvcmlnaW5hbCB2YWx1ZXMuCgpOb3cgd2Ugd2lsbCBhcHBseSBCb3gtQ294IHRyYW5zZm9ybWF0aW9uIG9uIHRoZSBmZW1hbGUgc3VpY2lkZSByYXRlLgpgYGB7cn0KZmVtYWxlRGYgPC0gZGYgJT4lIGZpbHRlcihTZXg9PSJGZW1hbGUiKQpib3hDb3hGZW1hbGVTdWljaWRlUmF0ZSA8LSBCb3hDb3goZmVtYWxlRGYkU3VpY2lkZVJhdGUsIGxhbWJkYT0iYXV0byIpCmhpc3QoYm94Q294RmVtYWxlU3VpY2lkZVJhdGUpCmBgYAoKQWdhaW4sIHRoZSBkaXN0cmlidXRpb24gaXMgbm90IGV4YWN0bHkgc3ltbWV0cmljIGJ1dCBpcyBkZWZpbml0ZWx5IG1vcmUgc2ltaWxhciB0byBhIG5vcm1hbCBkaXN0cmlidXRpb24gdGhhbiB0aGUgb3JpZ2luYWwgdmFsdWVzLgoKIyMgUmVmZXJlbmNlcwoKKiBVbml0ZWQgTmF0aW9ucyAyMDE5LCBXb3JsZCBQb3B1bGF0aW9uIFByb3NwZWN0cywgVW5pdGVkIE5hdGlvbnMsIHZpZXdlZCAxIEp1bmUgMjAyMCwgXDw8aHR0cHM6Ly9wb3B1bGF0aW9uLnVuLm9yZy93cHAvRG93bmxvYWQvU3RhbmRhcmQvQ1NWLz5cPi4KKiBXb3JsZCBIZWFsdGggT3JnYW5pemF0aW9uIDIwMTgsIFN1aWNpZGUgcmF0ZSBlc3RpbWF0ZXMsIGFnZS1zdGFuZGFyZGl6ZWQsIFdvcmxkIEhlYWx0aCBPcmdhbml6YXRpb24sIHZpZXdlZCAxIEp1bmUgMjAyMCwgXDw8aHR0cHM6Ly9hcHBzLndoby5pbnQvZ2hvL2RhdGEvbm9kZS5tYWluLk1IU1VJQ0lERUFTRFI/bGFuZz1lbj5cPi4KCjxicj4KPGJyPgoK