Required packages


Attaching package: ‘dplyr’

The following objects are masked from ‘package:stats’:

    filter, lag

The following objects are masked from ‘package:base’:

    intersect, setdiff, setequal, union


Attaching package: ‘lubridate’

The following object is masked from ‘package:base’:

    date

Loading required package: lattice
Loading required package: survival
Loading required package: Formula
Loading required package: ggplot2
Registered S3 methods overwritten by 'htmltools':
  method               from         
  print.html           tools:rstudio
  print.shiny.tag      tools:rstudio
  print.shiny.tag.list tools:rstudio
Registered S3 method overwritten by 'htmlwidgets':
  method           from         
  print.htmlwidget tools:rstudio
Registered S3 method overwritten by 'data.table':
  method           from
  print.data.table     

Attaching package: ‘Hmisc’

The following objects are masked from ‘package:dplyr’:

    src, summarize

The following objects are masked from ‘package:base’:

    format.pval, units


********************************************************
Note: As of version 1.0.0, cowplot does not change the
  default ggplot2 theme anymore. To recover the previous
  behavior, execute:
  theme_set(theme_cowplot())
********************************************************


Attaching package: ‘cowplot’

The following object is masked from ‘package:lubridate’:

    stamp

Registered S3 method overwritten by 'xts':
  method     from
  as.zoo.xts zoo 
Registered S3 method overwritten by 'quantmod':
  method            from
  as.zoo.data.frame zoo 
Registered S3 methods overwritten by 'forecast':
  method             from    
  fitted.fracdiff    fracdiff
  residuals.fracdiff fracdiff

Executive Summary

Two socio-economic datasets, Suicide Rates Overview 1985 to 2016 and World Bank Data (1960 to 2016) were retrieved from Kaggle as text files.

The Life Expectancy table from the World Bank Data was tidied by converting the table from wide to long format and then left joined to the suicide rates data.

The resulting data’s structure is then inspected. Variables with incorrect data types were cast to the appropriate data types and the data was subset.

The data is then checked to see if the data is tidy. From previously tidying the data before joining, the data is determined to be tidy.

A new variable suicide_pop_prop is created from mutating the variables suicides_no and population by dividing the two values to create the new variable.

The dataset were checked for NaN, Inf and NA values.

Missing values were identified in the variables hdi_per_year and lifeexpectancy. Missing values in the variables were imputed and mutated to form new variables such that an analyst may refer to the raw data where appropriate.

Outliers for each numeric column are winsorised to the 5th and 95th percentile and stored in a new data frame. There are large number of outliers for each variable and so the variables are not directly replaced, since imputation or removal may impact the results of the analysis.

The lifeexpectancy variable is transformed using the Box-Cox transformation to remove the effect of skewness.

Data

The World Bank Data (1960 to 2016) was sourced from Kaggle (https://www.kaggle.com/gemartin/world-bank-data-1960-to-2016) which contains three datasets:

The Suicide Rates Overview 1985 to 2016 data was also sourced from Kaggle (https://www.kaggle.com/russellyates88/suicide-rates-overview-1985-to-2016) compares socio-economic information with suicide rates by year and country and is composed of one file.

First we import and preview the data. Since they are both csv/text format we call the appropriate function for these file formats.

suiciderates <- read_csv("suiciderates.csv")
Parsed with column specification:
cols(
  country = col_character(),
  year = col_double(),
  sex = col_character(),
  age = col_character(),
  suicides_no = col_double(),
  population = col_double(),
  `suicides/100k pop` = col_double(),
  `country-year` = col_character(),
  `HDI for year` = col_double(),
  `gdp_for_year ($)` = col_number(),
  `gdp_per_capita ($)` = col_double(),
  generation = col_character()
)
lifeexpectancy <- read_csv("life_expectancy.csv")
Parsed with column specification:
cols(
  .default = col_double(),
  `Country Name` = col_character(),
  `Country Code` = col_character(),
  `Indicator Name` = col_character(),
  `Indicator Code` = col_character()
)
See spec(...) for full column specifications.
suiciderates %>% head()
lifeexpectancy %>% head()

We see that the life expectancy table is untidy because the year values are variables rather than observations.

We clean the life expactancy dataset first by converting it to long format to tidy the data and to make it easier to join the two datasets, since year is a common factor between the two.

lifeexpectancy %>% head()
lifeexpectancy <- lifeexpectancy %>% gather(c(5:length(lifeexpectancy)), key = "year", value = "lifeexpectancy")

lifeexpectancy %>% head()

We create a new variable to join on so the data can be matched appropriately. We then join the two datasets using a left join on the suicide rates dataset.

#Mutate new variable, country-year
lifeexpectancy <- lifeexpectancy %>% mutate(`country-year` = paste0(`Country Name`, year))

#Left join lifeexpectancy on suiciderates
suicide_life <- suiciderates %>% left_join(lifeexpectancy, by = "country-year")

#Working dataset
suicide_life %>% head()

Understand

First we check the structure of our dataset.

str(suicide_life)
Classes ‘spec_tbl_df’, ‘tbl_df’, ‘tbl’ and 'data.frame':    27820 obs. of  18 variables:
 $ country           : chr  "Albania" "Albania" "Albania" "Albania" ...
 $ year.x            : num  1987 1987 1987 1987 1987 ...
 $ sex               : chr  "male" "male" "female" "male" ...
 $ age               : chr  "15-24 years" "35-54 years" "15-24 years" "75+ years" ...
 $ suicides_no       : num  21 16 14 1 9 1 6 4 1 0 ...
 $ population        : num  312900 308000 289700 21800 274300 ...
 $ suicides/100k pop : num  6.71 5.19 4.83 4.59 3.28 2.81 2.15 1.56 0.73 0 ...
 $ country-year      : chr  "Albania1987" "Albania1987" "Albania1987" "Albania1987" ...
 $ HDI for year      : num  NA NA NA NA NA NA NA NA NA NA ...
 $ gdp_for_year ($)  : num  2.16e+09 2.16e+09 2.16e+09 2.16e+09 2.16e+09 ...
 $ gdp_per_capita ($): num  796 796 796 796 796 796 796 796 796 796 ...
 $ generation        : chr  "Generation X" "Silent" "Generation X" "G.I. Generation" ...
 $ Country Name      : chr  "Albania" "Albania" "Albania" "Albania" ...
 $ Country Code      : chr  "ALB" "ALB" "ALB" "ALB" ...
 $ Indicator Name    : chr  "Life expectancy at birth, total (years)" "Life expectancy at birth, total (years)" "Life expectancy at birth, total (years)" "Life expectancy at birth, total (years)" ...
 $ Indicator Code    : chr  "SP.DYN.LE00.IN" "SP.DYN.LE00.IN" "SP.DYN.LE00.IN" "SP.DYN.LE00.IN" ...
 $ year.y            : chr  "1987" "1987" "1987" "1987" ...
 $ lifeexpectancy    : num  71.8 71.8 71.8 71.8 71.8 ...

We see that we have some variables that are not the data type they should be, so we convert them to the appropriate data types.

#Rename columns
colnames(suicide_life)[colnames(suicide_life) == "year.x"] <- "year"
colnames(suicide_life)[colnames(suicide_life) == "HDI for year"] <- "hdi_for_year"
colnames(suicide_life)[colnames(suicide_life) == "suicides/100k pop"] <- "suicides_per_100k_pop"

#Factor sex
suicide_life$sex <- suicide_life$sex %>% factor(labels = unique(suicide_life$sex))

#Factor age
suicide_life$age <- suicide_life$age %>% factor(levels = c("5-14 years", "15-24 years", "25-34 years", "35-54 years", "55-74 years", "75+ years"), labels = unique(suicide_life$age), ordered = TRUE)

#Factor generation
suicide_life$generation <- 
    suicide_life$generation %>% factor(labels = unique(suicide_life$generation))

#Convert year to integer
suicide_life$year <- as.integer(suicide_life$year)

#Subset the data
suicide_life <- suicide_life %>% select(-c("year.y", "Country Name", "Country Code", "Indicator Name", "Indicator Code", "gdp_for_year ($)", "gdp_per_capita ($)"))

#Check the structure again
str(suicide_life)
Classes ‘spec_tbl_df’, ‘tbl_df’, ‘tbl’ and 'data.frame':    27820 obs. of  11 variables:
 $ country              : chr  "Albania" "Albania" "Albania" "Albania" ...
 $ year                 : int  1987 1987 1987 1987 1987 1987 1987 1987 1987 1987 ...
 $ sex                  : Factor w/ 2 levels "male","female": 2 2 1 2 2 1 1 1 2 1 ...
 $ age                  : Ord.factor w/ 6 levels "15-24 years"<..: 2 4 2 6 3 6 4 3 5 1 ...
 $ suicides_no          : num  21 16 14 1 9 1 6 4 1 0 ...
 $ population           : num  312900 308000 289700 21800 274300 ...
 $ suicides_per_100k_pop: num  6.71 5.19 4.83 4.59 3.28 2.81 2.15 1.56 0.73 0 ...
 $ country-year         : chr  "Albania1987" "Albania1987" "Albania1987" "Albania1987" ...
 $ hdi_for_year         : num  NA NA NA NA NA NA NA NA NA NA ...
 $ generation           : Factor w/ 6 levels "Generation X",..: 3 6 3 2 1 2 6 1 2 3 ...
 $ lifeexpectancy       : num  71.8 71.8 71.8 71.8 71.8 ...

Tidy & Manipulate Data I

Previously to join the datasets, the datasets were tidied in order to join better, so the current working dataset is clean.

suicide_life %>% head()

The data is tidy since each variable has its own column (after being tidied in a previous step), each observation has its own row and each value has its own cell.

Tidy & Manipulate Data II

We will create a new variable suicide_pop_prop by mutating to existing variables. To create the proportion variable we mutate by dividing the suicides_no variable by the population variable.

suicide_life <- suicide_life %>% mutate(suicide_pop_prop = suicides_no/population) 

Scan I

Let’s check which variables have missing values. We also check for any Inf or NaN values.

#NA check
colSums(is.na(suicide_life))
              country                  year 
                    0                     0 
                  sex                   age 
                    0                     0 
          suicides_no            population 
                    0                     0 
suicides_per_100k_pop          country-year 
                    0                     0 
         hdi_for_year            generation 
                19456                     0 
       lifeexpectancy      suicide_pop_prop 
                 1980                     0 
#NaN check
suicide_life %>% sapply(function (x) sum(is.nan(x)))
              country                  year 
                    0                     0 
                  sex                   age 
                    0                     0 
          suicides_no            population 
                    0                     0 
suicides_per_100k_pop          country-year 
                    0                     0 
         hdi_for_year            generation 
                    0                     0 
       lifeexpectancy      suicide_pop_prop 
                    0                     0 
#Inf check
suicide_life %>% sapply(function (x) sum(is.infinite(x)))
              country                  year 
                    0                     0 
                  sex                   age 
                    0                     0 
          suicides_no            population 
                    0                     0 
suicides_per_100k_pop          country-year 
                    0                     0 
         hdi_for_year            generation 
                    0                     0 
       lifeexpectancy      suicide_pop_prop 
                    0                     0 

We see that the lifeexpectancy variable has 1980 missing values and the hdi_for_year variable has 19456 values missing.

#Check number of observations
dim(suicide_life)
[1] 27820    12
#Is missing data more than 5%?
sum(is.na(suicide_life$hdi_for_year))/dim(suicide_life)[1]
[1] 0.699353
sum(is.na(suicide_life$lifeexpectancy))/dim(suicide_life)[1]
[1] 0.07117182

Since both variables have more than 5% of the observations that are missing, excluding the data could bias the analysis.

We check the distribution of the variables with outliers to determine the best method of imputation to handle these.

ggplot(data = suicide_life, aes(x = hdi_for_year)) + geom_histogram()

ggplot(data = suicide_life, aes(x = lifeexpectancy)) + geom_histogram()

Both values are negatively skewed, so we will use the median to impute the missing values and mutate them into a new variable.

By retaining the original data an analyst may refer back to the original, since the hdi_for_year column may be heavily biased by the sheer volume of imputed values (19456 of 27820 total observations, more than 69% of the values).

suicide_life$hdiforyearfilter <- impute(suicide_life$hdi_for_year, fun = median)
suicide_life$lifeexpectancyfilter <- impute(suicide_life$lifeexpectancy, fun = median)

colSums(is.na(suicide_life))
              country                  year 
                    0                     0 
                  sex                   age 
                    0                     0 
          suicides_no            population 
                    0                     0 
suicides_per_100k_pop          country-year 
                    0                     0 
         hdi_for_year            generation 
                19456                     0 
       lifeexpectancy      suicide_pop_prop 
                 1980                     0 
     hdiforyearfilter  lifeexpectancyfilter 
                    0                     0 

The mutated variables hdiforyearfilter and lifeexpectancyfilter are free from missing values.

Scan II

To find outliers we plot boxplots for each numeric value.

str(suicide_life)
Classes ‘spec_tbl_df’, ‘tbl_df’, ‘tbl’ and 'data.frame':    27820 obs. of  14 variables:
 $ country              : chr  "Albania" "Albania" "Albania" "Albania" ...
 $ year                 : int  1987 1987 1987 1987 1987 1987 1987 1987 1987 1987 ...
 $ sex                  : Factor w/ 2 levels "male","female": 2 2 1 2 2 1 1 1 2 1 ...
 $ age                  : Ord.factor w/ 6 levels "15-24 years"<..: 2 4 2 6 3 6 4 3 5 1 ...
 $ suicides_no          : num  21 16 14 1 9 1 6 4 1 0 ...
 $ population           : num  312900 308000 289700 21800 274300 ...
 $ suicides_per_100k_pop: num  6.71 5.19 4.83 4.59 3.28 2.81 2.15 1.56 0.73 0 ...
 $ country-year         : chr  "Albania1987" "Albania1987" "Albania1987" "Albania1987" ...
 $ hdi_for_year         : num  NA NA NA NA NA NA NA NA NA NA ...
 $ generation           : Factor w/ 6 levels "Generation X",..: 3 6 3 2 1 2 6 1 2 3 ...
 $ lifeexpectancy       : num  71.8 71.8 71.8 71.8 71.8 ...
 $ suicide_pop_prop     : num  6.71e-05 5.19e-05 4.83e-05 4.59e-05 3.28e-05 ...
 $ hdiforyearfilter     : 'impute' num  0.779 0.779 0.779 0.779 0.779 0.779 0.779 0.779 0.779 0.779 ...
  ..- attr(*, "imputed")= int  1 2 3 4 5 6 7 8 9 10 ...
 $ lifeexpectancyfilter : 'impute' num  71.8 71.8 71.8 71.8 71.8 ...
  ..- attr(*, "imputed")= int  2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 ...
p1 <- ggplot(data = suicide_life, aes(x = factor(0), y = suicides_no)) + geom_boxplot()
p2 <- ggplot(data = suicide_life, aes(x = factor(0), y = population)) + geom_boxplot()
p3 <- ggplot(data = suicide_life, aes(x = factor(0), y = suicides_per_100k_pop)) + geom_boxplot()
p4 <- ggplot(data = suicide_life, aes(x = factor(0), y = hdiforyearfilter)) + geom_boxplot()
p5 <- ggplot(data = suicide_life, aes(x = factor(0), y = lifeexpectancyfilter)) + geom_boxplot()
p6 <- ggplot(data = suicide_life, aes(x = factor(0), y = suicide_pop_prop)) + geom_boxplot()
plot_grid(p1, p2, p3, p4, p5, p6)
Don't know how to automatically pick scale for object of type impute. Defaulting to continuous.
Don't know how to automatically pick scale for object of type impute. Defaulting to continuous.

All numeric values in the dataset appear to have some outliers.

For many of the variables, it appears (visually) that for some variables, there is a large amount of outliers. For this reason, the cleaned variables are stored in a new dataframe to allow an analyst to refer to the original if needed.

Numeric values are subset into new dataframe and the outliers windsorised to the 5th and 95th percentiles.

#original data
summary(suicide_life)

 19456 values imputed to 0.779 


 1980 values imputed to 74.678 

   country               year          sex       
 Length:27820       Min.   :1985   male  :13910  
 Class :character   1st Qu.:1995   female:13910  
 Mode  :character   Median :2002                 
                    Mean   :2001                 
                    3rd Qu.:2008                 
                    Max.   :2016                 
                                                 
          age        suicides_no     
 15-24 years:4610   Min.   :    0.0  
 35-54 years:4642   1st Qu.:    3.0  
 75+ years  :4642   Median :   25.0  
 25-34 years:4642   Mean   :  242.6  
 55-74 years:4642   3rd Qu.:  131.0  
 5-14 years :4642   Max.   :22338.0  
                                     
   population       suicides_per_100k_pop
 Min.   :     278   Min.   :  0.00       
 1st Qu.:   97498   1st Qu.:  0.92       
 Median :  430150   Median :  5.99       
 Mean   : 1844794   Mean   : 12.82       
 3rd Qu.: 1486143   3rd Qu.: 16.62       
 Max.   :43805214   Max.   :224.97       
                                         
 country-year        hdi_for_year  
 Length:27820       Min.   :0.483  
 Class :character   1st Qu.:0.713  
 Mode  :character   Median :0.779  
                    Mean   :0.777  
                    3rd Qu.:0.855  
                    Max.   :0.944  
                    NA's   :19456  
           generation   lifeexpectancy 
 Generation X   :4990   Min.   :52.57  
 Silent         :2744   1st Qu.:70.90  
 G.I. Generation:6408   Median :74.68  
 Boomers        :1470   Mean   :74.14  
 Millenials     :5844   3rd Qu.:77.88  
 Generation Z   :6364   Max.   :83.79  
                        NA's   :1980   
 suicide_pop_prop    hdiforyearfilter
 Min.   :0.000e+00   Min.   :0.4830  
 1st Qu.:9.187e-06   1st Qu.:0.7790  
 Median :5.991e-05   Median :0.7790  
 Mean   :1.282e-04   Mean   :0.7783  
 3rd Qu.:1.662e-04   3rd Qu.:0.7790  
 Max.   :2.250e-03   Max.   :0.9440  
                                     
 lifeexpectancyfilter
 Min.   :52.57       
 1st Qu.:71.22       
 Median :74.68       
 Mean   :74.18       
 3rd Qu.:77.61       
 Max.   :83.79       
                     
capped <- suicide_life %>% select(c("suicides_no", "population", "suicides_per_100k_pop", "hdiforyearfilter", "lifeexpectancyfilter", "suicide_pop_prop"))
capped$suicides_no <- suicide_life$suicides_no %>% cap()
capped$population <- suicide_life$population %>% cap()
capped$suicides_per_100k_pop <- suicide_life$suicides_per_100k_pop %>% cap()
capped$hdiforyearfilter <- suicide_life$hdiforyearfilter %>% cap()
capped$lifeexpectancyfilter <- capped$lifeexpectancyfilter %>% cap()
capped$suicide_pop_prop <- suicide_life$suicide_pop_prop %>% cap()

#windsorised data
summary(capped)

 19456 values imputed to 0.779 


 1980 values imputed to 74.678 

  suicides_no       population     
 Min.   :   0.0   Min.   :    278  
 1st Qu.:   3.0   1st Qu.:  97498  
 Median :  25.0   Median : 430150  
 Mean   : 189.1   Mean   :1850021  
 3rd Qu.: 131.0   3rd Qu.:1486143  
 Max.   :1050.0   Max.   :8850240  
 suicides_per_100k_pop hdiforyearfilter
 Min.   : 0.00         Min.   :0.6750  
 1st Qu.: 0.92         1st Qu.:0.7790  
 Median : 5.99         Median :0.7790  
 Mean   :11.64         Mean   :0.7789  
 3rd Qu.:16.62         3rd Qu.:0.7790  
 Max.   :50.53         Max.   :0.8820  
 lifeexpectancyfilter suicide_pop_prop   
 Min.   :61.73        Min.   :0.000e+00  
 1st Qu.:71.22        1st Qu.:9.187e-06  
 Median :74.68        Median :5.991e-05  
 Mean   :74.27        Mean   :1.164e-04  
 3rd Qu.:77.61        3rd Qu.:1.662e-04  
 Max.   :83.79        Max.   :5.053e-04  

Transform

Looking at the distribution of lifeexpectancyfilter_capped variable, we see that it is slightly left skewed.

ggplot(capped, aes(x = lifeexpectancyfilter)) + geom_histogram()

We will transform the data to minimise the effect of skewness in analysis by applying a Box-Cox transformation on the variable.

capped$lifeexpectancyfilter_boxcox <- BoxCox(capped$lifeexpectancyfilter, lambda = "auto")
NA/Inf replaced by maximum positive valueNA/Inf replaced by maximum positive valueNA/Inf replaced by maximum positive valueNA/Inf replaced by maximum positive valueNA/Inf replaced by maximum positive valueNA/Inf replaced by maximum positive valueNA/Inf replaced by maximum positive valueNA/Inf replaced by maximum positive valueNA/Inf replaced by maximum positive valueNA/Inf replaced by maximum positive valueNA/Inf replaced by maximum positive valueNA/Inf replaced by maximum positive valueNA/Inf replaced by maximum positive valueNA/Inf replaced by maximum positive valueNA/Inf replaced by maximum positive valueNA/Inf replaced by maximum positive valueNA/Inf replaced by maximum positive valueNA/Inf replaced by maximum positive valueNA/Inf replaced by maximum positive valueNA/Inf replaced by maximum positive valueNA/Inf replaced by maximum positive valueNA/Inf replaced by maximum positive value
ggplot(data = capped, aes(x = lifeexpectancyfilter_boxcox)) + geom_histogram()

The resulting distribution is less skewed and ready for analysis.



LS0tCnRpdGxlOiAiTUFUSDIzNDkgU2VtZXN0ZXIgMiwgMjAxOSIKYXV0aG9yOiAiQW5kcmV3IENoZW4gKHMzNDg4MTk1KSwgWW9ubiBBcHJpbCAoczM3MjcyMTApIgpzdWJ0aXRsZTogQXNzaWdubWVudCAzCm91dHB1dDoKICBodG1sX25vdGVib29rOiBkZWZhdWx0Ci0tLQoKIyMgUmVxdWlyZWQgcGFja2FnZXMgCgpgYGB7ciBlY2hvPUZBTFNFfQpsaWJyYXJ5KHJlYWRyKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHRpZHlyKQpsaWJyYXJ5KGx1YnJpZGF0ZSkKbGlicmFyeShzdHJpbmdyKQpsaWJyYXJ5KEhtaXNjKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoY293cGxvdCkKbGlicmFyeShmb3JlY2FzdCkKCmNhcCA8LSBmdW5jdGlvbih4KSB7CiAgICBxdWFudGlsZXMgPC0gcXVhbnRpbGUoIHgsIGMoLjA1LCAwLjI1LCAwLjc1LCAuOTUgKSApCiAgICB4WyB4IDwgcXVhbnRpbGVzWzJdIC0gMS41KklRUih4KSBdIDwtIHF1YW50aWxlc1sxXQogICAgeFsgeCA+IHF1YW50aWxlc1szXSArIDEuNSpJUVIoeCkgXSA8LSBxdWFudGlsZXNbNF0KICAgIHgKfQoKb3V0bGllcmxpbWl0IDwtIGZ1bmN0aW9uKHgpIHsKICAgICAgICBxdWFudGlsZXMgPC0gcXVhbnRpbGUoeCwgYyguMDUsIDAuMjUsIDAuNzUsIC45NSkpCiAgICAgICAgbG93ZXJfbGltaXQgPC0gcXVhbnRpbGVzWzJdIC0gMS41KklRUih4KQogICAgICAgIHVwcGVyX2xpbWl0IDwtIHF1YW50aWxlc1szXSArIDEuNSpJUVIoeCkKICAgICAgICAKICAgICAgICByZXR1cm4obGlzdCgibG93ZXIiID0gbG93ZXJfbGltaXQsICJ1cHBlciIgPSB1cHBlcl9saW1pdCkpCn0KYGBgCgoKIyMgRXhlY3V0aXZlIFN1bW1hcnkgCgpUd28gc29jaW8tZWNvbm9taWMgZGF0YXNldHMsIFN1aWNpZGUgUmF0ZXMgT3ZlcnZpZXcgMTk4NSB0byAyMDE2IGFuZCBXb3JsZCBCYW5rIERhdGEgKDE5NjAgdG8gMjAxNikgd2VyZSByZXRyaWV2ZWQgZnJvbSBLYWdnbGUgYXMgdGV4dCBmaWxlcy4KClRoZSBMaWZlIEV4cGVjdGFuY3kgdGFibGUgZnJvbSB0aGUgV29ybGQgQmFuayBEYXRhIHdhcyB0aWRpZWQgYnkgY29udmVydGluZyB0aGUgdGFibGUgZnJvbSB3aWRlIHRvIGxvbmcgZm9ybWF0IGFuZCB0aGVuIGxlZnQgam9pbmVkIHRvIHRoZSBzdWljaWRlIHJhdGVzIGRhdGEuCgpUaGUgcmVzdWx0aW5nIGRhdGEncyBzdHJ1Y3R1cmUgaXMgdGhlbiBpbnNwZWN0ZWQuIFZhcmlhYmxlcyB3aXRoIGluY29ycmVjdCBkYXRhIHR5cGVzIHdlcmUgY2FzdCB0byB0aGUgYXBwcm9wcmlhdGUgZGF0YSB0eXBlcyBhbmQgdGhlIGRhdGEgd2FzIHN1YnNldC4KClRoZSBkYXRhIGlzIHRoZW4gY2hlY2tlZCB0byBzZWUgaWYgdGhlIGRhdGEgaXMgdGlkeS4gRnJvbSBwcmV2aW91c2x5IHRpZHlpbmcgdGhlIGRhdGEgYmVmb3JlIGpvaW5pbmcsIHRoZSBkYXRhIGlzIGRldGVybWluZWQgdG8gYmUgdGlkeS4KCkEgbmV3IHZhcmlhYmxlIGBzdWljaWRlX3BvcF9wcm9wYCBpcyBjcmVhdGVkIGZyb20gbXV0YXRpbmcgdGhlIHZhcmlhYmxlcyBgc3VpY2lkZXNfbm9gIGFuZCBgcG9wdWxhdGlvbmAgYnkgZGl2aWRpbmcgdGhlIHR3byB2YWx1ZXMgdG8gY3JlYXRlIHRoZSBuZXcgdmFyaWFibGUuCgpUaGUgZGF0YXNldCB3ZXJlIGNoZWNrZWQgZm9yIGBOYU5gLCBgSW5mYCBhbmQgYE5BYCB2YWx1ZXMuCgpNaXNzaW5nIHZhbHVlcyB3ZXJlIGlkZW50aWZpZWQgaW4gdGhlIHZhcmlhYmxlcyBgaGRpX3Blcl95ZWFyYCBhbmQgYGxpZmVleHBlY3RhbmN5YC4gTWlzc2luZyB2YWx1ZXMgaW4gdGhlIHZhcmlhYmxlcyB3ZXJlIGltcHV0ZWQgYW5kIG11dGF0ZWQgdG8gZm9ybSBuZXcgdmFyaWFibGVzIHN1Y2ggdGhhdCBhbiBhbmFseXN0IG1heSByZWZlciB0byB0aGUgcmF3IGRhdGEgd2hlcmUgYXBwcm9wcmlhdGUuIAoKT3V0bGllcnMgZm9yIGVhY2ggbnVtZXJpYyBjb2x1bW4gYXJlIHdpbnNvcmlzZWQgdG8gdGhlIDV0aCBhbmQgOTV0aCBwZXJjZW50aWxlIGFuZCBzdG9yZWQgaW4gYSBuZXcgZGF0YSBmcmFtZS4gVGhlcmUgYXJlIGxhcmdlIG51bWJlciBvZiBvdXRsaWVycyBmb3IgZWFjaCB2YXJpYWJsZSBhbmQgc28gdGhlIHZhcmlhYmxlcyBhcmUgbm90IGRpcmVjdGx5IHJlcGxhY2VkLCBzaW5jZSBpbXB1dGF0aW9uIG9yIHJlbW92YWwgbWF5IGltcGFjdCB0aGUgcmVzdWx0cyBvZiB0aGUgYW5hbHlzaXMuCgpUaGUgYGxpZmVleHBlY3RhbmN5YCB2YXJpYWJsZSBpcyB0cmFuc2Zvcm1lZCB1c2luZyB0aGUgQm94LUNveCB0cmFuc2Zvcm1hdGlvbiB0byByZW1vdmUgdGhlIGVmZmVjdCBvZiBza2V3bmVzcy4KCiMjIERhdGEgCgpUaGUgV29ybGQgQmFuayBEYXRhICgxOTYwIHRvIDIwMTYpIHdhcyBzb3VyY2VkIGZyb20gS2FnZ2xlIChodHRwczovL3d3dy5rYWdnbGUuY29tL2dlbWFydGluL3dvcmxkLWJhbmstZGF0YS0xOTYwLXRvLTIwMTYpIHdoaWNoIGNvbnRhaW5zIHRocmVlIGRhdGFzZXRzOgoKKiBMaWZlIEV4cGVjdGFuY3kgYXQgQmlydGg6IG51bWJlciBvZiB5ZWFycyBhIG5ld2Jvcm4gd291bGQgaWYgdGhlIHBhdHRlcm5zIG9mIG1vcnRhbGl0eSBhdCB0aGUgdGltZSBvZiBiaXJ0aCByZW1haW4gdGhlIHNhbWUgdGhyb3VnaG91dCBoaXMgbGlmZS4KCiogRmVydGlsaXR5IHJhdGU6IG51bWJlciBvZiBjaGlsZHJlbiBhIHdvbWFuIHNob3VsZCBnaXZlIGJpcnRoIHRvIGR1cmluZyBoZXIgY2hpbGRiZWFyaW5nIHllYXJzLgoKKiBDb3VudHJ5IHBvcHVsYXRpb246IGEgdG90YWwgbnVtYmVyIG9mIHJlc2lkZW50cyByZWdhcmRsZXNzIG9mIGxlZ2FsIHN0YXR1cyBvciBjaXRpemVuc2hpcCAobWlkeWVhciBlc3RpbWF0ZXMpCgpUaGUgU3VpY2lkZSBSYXRlcyBPdmVydmlldyAxOTg1IHRvIDIwMTYgZGF0YSB3YXMgYWxzbyBzb3VyY2VkIGZyb20gS2FnZ2xlIChodHRwczovL3d3dy5rYWdnbGUuY29tL3J1c3NlbGx5YXRlczg4L3N1aWNpZGUtcmF0ZXMtb3ZlcnZpZXctMTk4NS10by0yMDE2KSBjb21wYXJlcyBzb2Npby1lY29ub21pYyBpbmZvcm1hdGlvbiB3aXRoIHN1aWNpZGUgcmF0ZXMgYnkgeWVhciBhbmQgY291bnRyeSBhbmQgaXMgY29tcG9zZWQgb2Ygb25lIGZpbGUuCgpGaXJzdCB3ZSBpbXBvcnQgYW5kIHByZXZpZXcgdGhlIGRhdGEuIFNpbmNlIHRoZXkgYXJlIGJvdGggY3N2L3RleHQgZm9ybWF0IHdlIGNhbGwgdGhlIGFwcHJvcHJpYXRlIGZ1bmN0aW9uIGZvciB0aGVzZSBmaWxlIGZvcm1hdHMuCgpgYGB7cn0Kc3VpY2lkZXJhdGVzIDwtIHJlYWRfY3N2KCJzdWljaWRlcmF0ZXMuY3N2IikKbGlmZWV4cGVjdGFuY3kgPC0gcmVhZF9jc3YoImxpZmVfZXhwZWN0YW5jeS5jc3YiKQoKc3VpY2lkZXJhdGVzICU+JSBoZWFkKCkKbGlmZWV4cGVjdGFuY3kgJT4lIGhlYWQoKQpgYGAKCldlIHNlZSB0aGF0IHRoZSBsaWZlIGV4cGVjdGFuY3kgdGFibGUgaXMgdW50aWR5IGJlY2F1c2UgdGhlIHllYXIgdmFsdWVzIGFyZSB2YXJpYWJsZXMgcmF0aGVyIHRoYW4gb2JzZXJ2YXRpb25zLgoKV2UgY2xlYW4gdGhlIGxpZmUgZXhwYWN0YW5jeSBkYXRhc2V0IGZpcnN0IGJ5IGNvbnZlcnRpbmcgaXQgdG8gbG9uZyBmb3JtYXQgdG8gdGlkeSB0aGUgZGF0YSBhbmQgdG8gbWFrZSBpdCBlYXNpZXIgdG8gam9pbiB0aGUgdHdvIGRhdGFzZXRzLCBzaW5jZSB5ZWFyIGlzIGEgY29tbW9uIGZhY3RvciBiZXR3ZWVuIHRoZSB0d28uCgpgYGB7cn0KbGlmZWV4cGVjdGFuY3kgJT4lIGhlYWQoKQpsaWZlZXhwZWN0YW5jeSA8LSBsaWZlZXhwZWN0YW5jeSAlPiUgZ2F0aGVyKGMoNTpsZW5ndGgobGlmZWV4cGVjdGFuY3kpKSwga2V5ID0gInllYXIiLCB2YWx1ZSA9ICJsaWZlZXhwZWN0YW5jeSIpCgpsaWZlZXhwZWN0YW5jeSAlPiUgaGVhZCgpCmBgYAoKV2UgY3JlYXRlIGEgbmV3IHZhcmlhYmxlIHRvIGpvaW4gb24gc28gdGhlIGRhdGEgY2FuIGJlIG1hdGNoZWQgYXBwcm9wcmlhdGVseS4gV2UgdGhlbiBqb2luIHRoZSB0d28gZGF0YXNldHMgdXNpbmcgYSBsZWZ0IGpvaW4gb24gdGhlIHN1aWNpZGUgcmF0ZXMgZGF0YXNldC4KCmBgYHtyfQojTXV0YXRlIG5ldyB2YXJpYWJsZSwgY291bnRyeS15ZWFyCmxpZmVleHBlY3RhbmN5IDwtIGxpZmVleHBlY3RhbmN5ICU+JSBtdXRhdGUoYGNvdW50cnkteWVhcmAgPSBwYXN0ZTAoYENvdW50cnkgTmFtZWAsIHllYXIpKQoKI0xlZnQgam9pbiBsaWZlZXhwZWN0YW5jeSBvbiBzdWljaWRlcmF0ZXMKc3VpY2lkZV9saWZlIDwtIHN1aWNpZGVyYXRlcyAlPiUgbGVmdF9qb2luKGxpZmVleHBlY3RhbmN5LCBieSA9ICJjb3VudHJ5LXllYXIiKQoKI1dvcmtpbmcgZGF0YXNldApzdWljaWRlX2xpZmUgJT4lIGhlYWQoKQpgYGAKCiMjIFVuZGVyc3RhbmQgCgpGaXJzdCB3ZSBjaGVjayB0aGUgc3RydWN0dXJlIG9mIG91ciBkYXRhc2V0LgoKYGBge3J9CnN0cihzdWljaWRlX2xpZmUpCmBgYAoKV2Ugc2VlIHRoYXQgd2UgaGF2ZSBzb21lIHZhcmlhYmxlcyB0aGF0IGFyZSBub3QgdGhlIGRhdGEgdHlwZSB0aGV5IHNob3VsZCBiZSwgc28gd2UgY29udmVydCB0aGVtIHRvIHRoZSBhcHByb3ByaWF0ZSBkYXRhIHR5cGVzLgoKYGBge3J9CiNSZW5hbWUgY29sdW1ucwpjb2xuYW1lcyhzdWljaWRlX2xpZmUpW2NvbG5hbWVzKHN1aWNpZGVfbGlmZSkgPT0gInllYXIueCJdIDwtICJ5ZWFyIgpjb2xuYW1lcyhzdWljaWRlX2xpZmUpW2NvbG5hbWVzKHN1aWNpZGVfbGlmZSkgPT0gIkhESSBmb3IgeWVhciJdIDwtICJoZGlfZm9yX3llYXIiCmNvbG5hbWVzKHN1aWNpZGVfbGlmZSlbY29sbmFtZXMoc3VpY2lkZV9saWZlKSA9PSAic3VpY2lkZXMvMTAwayBwb3AiXSA8LSAic3VpY2lkZXNfcGVyXzEwMGtfcG9wIgoKI0ZhY3RvciBzZXgKc3VpY2lkZV9saWZlJHNleCA8LSBzdWljaWRlX2xpZmUkc2V4ICU+JSBmYWN0b3IobGFiZWxzID0gdW5pcXVlKHN1aWNpZGVfbGlmZSRzZXgpKQoKI0ZhY3RvciBhZ2UKc3VpY2lkZV9saWZlJGFnZSA8LSBzdWljaWRlX2xpZmUkYWdlICU+JSBmYWN0b3IobGV2ZWxzID0gYygiNS0xNCB5ZWFycyIsICIxNS0yNCB5ZWFycyIsICIyNS0zNCB5ZWFycyIsICIzNS01NCB5ZWFycyIsICI1NS03NCB5ZWFycyIsICI3NSsgeWVhcnMiKSwgbGFiZWxzID0gdW5pcXVlKHN1aWNpZGVfbGlmZSRhZ2UpLCBvcmRlcmVkID0gVFJVRSkKCiNGYWN0b3IgZ2VuZXJhdGlvbgpzdWljaWRlX2xpZmUkZ2VuZXJhdGlvbiA8LSAKICAgIHN1aWNpZGVfbGlmZSRnZW5lcmF0aW9uICU+JSBmYWN0b3IobGFiZWxzID0gdW5pcXVlKHN1aWNpZGVfbGlmZSRnZW5lcmF0aW9uKSkKCiNDb252ZXJ0IHllYXIgdG8gaW50ZWdlcgpzdWljaWRlX2xpZmUkeWVhciA8LSBhcy5pbnRlZ2VyKHN1aWNpZGVfbGlmZSR5ZWFyKQoKI1N1YnNldCB0aGUgZGF0YQpzdWljaWRlX2xpZmUgPC0gc3VpY2lkZV9saWZlICU+JSBzZWxlY3QoLWMoInllYXIueSIsICJDb3VudHJ5IE5hbWUiLCAiQ291bnRyeSBDb2RlIiwgIkluZGljYXRvciBOYW1lIiwgIkluZGljYXRvciBDb2RlIiwgImdkcF9mb3JfeWVhciAoJCkiLCAiZ2RwX3Blcl9jYXBpdGEgKCQpIikpCgojQ2hlY2sgdGhlIHN0cnVjdHVyZSBhZ2FpbgpzdHIoc3VpY2lkZV9saWZlKQpgYGAKCgojIwlUaWR5ICYgTWFuaXB1bGF0ZSBEYXRhIEkgCgpQcmV2aW91c2x5IHRvIGpvaW4gdGhlIGRhdGFzZXRzLCB0aGUgZGF0YXNldHMgd2VyZSB0aWRpZWQgaW4gb3JkZXIgdG8gam9pbiBiZXR0ZXIsIHNvIHRoZSBjdXJyZW50IHdvcmtpbmcgZGF0YXNldCBpcyBjbGVhbi4KCgpgYGB7cn0Kc3VpY2lkZV9saWZlICU+JSBoZWFkKCkKYGBgCgpUaGUgZGF0YSBpcyB0aWR5IHNpbmNlIGVhY2ggdmFyaWFibGUgaGFzIGl0cyBvd24gY29sdW1uIChhZnRlciBiZWluZyB0aWRpZWQgaW4gYSBwcmV2aW91cyBzdGVwKSwgZWFjaCBvYnNlcnZhdGlvbiBoYXMgaXRzIG93biByb3cgYW5kIGVhY2ggdmFsdWUgaGFzIGl0cyBvd24gY2VsbC4KCiMjCVRpZHkgJiBNYW5pcHVsYXRlIERhdGEgSUkgCgpXZSB3aWxsIGNyZWF0ZSBhIG5ldyB2YXJpYWJsZSBgc3VpY2lkZV9wb3BfcHJvcGAgYnkgbXV0YXRpbmcgdG8gZXhpc3RpbmcgdmFyaWFibGVzLiBUbyBjcmVhdGUgdGhlIHByb3BvcnRpb24gdmFyaWFibGUgd2UgbXV0YXRlIGJ5IGRpdmlkaW5nIHRoZSBgc3VpY2lkZXNfbm9gIHZhcmlhYmxlIGJ5IHRoZSBgcG9wdWxhdGlvbmAgdmFyaWFibGUuCmBgYHtyfQpzdWljaWRlX2xpZmUgPC0gc3VpY2lkZV9saWZlICU+JSBtdXRhdGUoc3VpY2lkZV9wb3BfcHJvcCA9IHN1aWNpZGVzX25vL3BvcHVsYXRpb24pIApgYGAKCgojIwlTY2FuIEkgCgpMZXQncyBjaGVjayB3aGljaCB2YXJpYWJsZXMgaGF2ZSBtaXNzaW5nIHZhbHVlcy4gV2UgYWxzbyBjaGVjayBmb3IgYW55IGBJbmZgIG9yIGBOYU5gIHZhbHVlcy4KCmBgYHtyfQojTkEgY2hlY2sKY29sU3Vtcyhpcy5uYShzdWljaWRlX2xpZmUpKQojTmFOIGNoZWNrCnN1aWNpZGVfbGlmZSAlPiUgc2FwcGx5KGZ1bmN0aW9uICh4KSBzdW0oaXMubmFuKHgpKSkKI0luZiBjaGVjawpzdWljaWRlX2xpZmUgJT4lIHNhcHBseShmdW5jdGlvbiAoeCkgc3VtKGlzLmluZmluaXRlKHgpKSkKYGBgCgpXZSBzZWUgdGhhdCB0aGUgYGxpZmVleHBlY3RhbmN5YCB2YXJpYWJsZSBoYXMgMTk4MCBtaXNzaW5nIHZhbHVlcyBhbmQgdGhlIGBoZGlfZm9yX3llYXJgIHZhcmlhYmxlIGhhcyAxOTQ1NiB2YWx1ZXMgbWlzc2luZy4KCmBgYHtyfQojQ2hlY2sgbnVtYmVyIG9mIG9ic2VydmF0aW9ucwpkaW0oc3VpY2lkZV9saWZlKQoKI0lzIG1pc3NpbmcgZGF0YSBtb3JlIHRoYW4gNSU/CnN1bShpcy5uYShzdWljaWRlX2xpZmUkaGRpX2Zvcl95ZWFyKSkvZGltKHN1aWNpZGVfbGlmZSlbMV0KCnN1bShpcy5uYShzdWljaWRlX2xpZmUkbGlmZWV4cGVjdGFuY3kpKS9kaW0oc3VpY2lkZV9saWZlKVsxXQpgYGAKU2luY2UgYm90aCB2YXJpYWJsZXMgaGF2ZSBtb3JlIHRoYW4gNSUgb2YgdGhlIG9ic2VydmF0aW9ucyB0aGF0IGFyZSBtaXNzaW5nLCBleGNsdWRpbmcgdGhlIGRhdGEgY291bGQgYmlhcyB0aGUgYW5hbHlzaXMuCgpXZSBjaGVjayB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZSB2YXJpYWJsZXMgd2l0aCBvdXRsaWVycyB0byBkZXRlcm1pbmUgdGhlIGJlc3QgbWV0aG9kIG9mIGltcHV0YXRpb24gdG8gaGFuZGxlIHRoZXNlLgoKYGBge3J9CmdncGxvdChkYXRhID0gc3VpY2lkZV9saWZlLCBhZXMoeCA9IGhkaV9mb3JfeWVhcikpICsgZ2VvbV9oaXN0b2dyYW0oKQpnZ3Bsb3QoZGF0YSA9IHN1aWNpZGVfbGlmZSwgYWVzKHggPSBsaWZlZXhwZWN0YW5jeSkpICsgZ2VvbV9oaXN0b2dyYW0oKQpgYGAKQm90aCB2YWx1ZXMgYXJlIG5lZ2F0aXZlbHkgc2tld2VkLCBzbyB3ZSB3aWxsIHVzZSB0aGUgbWVkaWFuIHRvIGltcHV0ZSB0aGUgbWlzc2luZyB2YWx1ZXMgYW5kIG11dGF0ZSB0aGVtIGludG8gYSBuZXcgdmFyaWFibGUuIAoKQnkgcmV0YWluaW5nIHRoZSBvcmlnaW5hbCBkYXRhIGFuIGFuYWx5c3QgbWF5IHJlZmVyIGJhY2sgdG8gdGhlIG9yaWdpbmFsLCBzaW5jZSB0aGUgYGhkaV9mb3JfeWVhcmAgY29sdW1uIG1heSBiZSBoZWF2aWx5IGJpYXNlZCBieSB0aGUgc2hlZXIgdm9sdW1lIG9mIGltcHV0ZWQgdmFsdWVzICgxOTQ1NiBvZiAyNzgyMCB0b3RhbCBvYnNlcnZhdGlvbnMsIG1vcmUgdGhhbiA2OSUgb2YgdGhlIHZhbHVlcykuCgpgYGB7cn0Kc3VpY2lkZV9saWZlJGhkaWZvcnllYXJmaWx0ZXIgPC0gaW1wdXRlKHN1aWNpZGVfbGlmZSRoZGlfZm9yX3llYXIsIGZ1biA9IG1lZGlhbikKc3VpY2lkZV9saWZlJGxpZmVleHBlY3RhbmN5ZmlsdGVyIDwtIGltcHV0ZShzdWljaWRlX2xpZmUkbGlmZWV4cGVjdGFuY3ksIGZ1biA9IG1lZGlhbikKCmNvbFN1bXMoaXMubmEoc3VpY2lkZV9saWZlKSkKYGBgCgpUaGUgbXV0YXRlZCB2YXJpYWJsZXMgYGhkaWZvcnllYXJmaWx0ZXJgIGFuZCBgbGlmZWV4cGVjdGFuY3lmaWx0ZXJgIGFyZSBmcmVlIGZyb20gbWlzc2luZyB2YWx1ZXMuCgojIwlTY2FuIElJCgpUbyBmaW5kIG91dGxpZXJzIHdlIHBsb3QgYm94cGxvdHMgZm9yIGVhY2ggbnVtZXJpYyB2YWx1ZS4KYGBge3J9CnN0cihzdWljaWRlX2xpZmUpCgpwMSA8LSBnZ3Bsb3QoZGF0YSA9IHN1aWNpZGVfbGlmZSwgYWVzKHggPSBmYWN0b3IoMCksIHkgPSBzdWljaWRlc19ubykpICsgZ2VvbV9ib3hwbG90KCkKcDIgPC0gZ2dwbG90KGRhdGEgPSBzdWljaWRlX2xpZmUsIGFlcyh4ID0gZmFjdG9yKDApLCB5ID0gcG9wdWxhdGlvbikpICsgZ2VvbV9ib3hwbG90KCkKcDMgPC0gZ2dwbG90KGRhdGEgPSBzdWljaWRlX2xpZmUsIGFlcyh4ID0gZmFjdG9yKDApLCB5ID0gc3VpY2lkZXNfcGVyXzEwMGtfcG9wKSkgKyBnZW9tX2JveHBsb3QoKQpwNCA8LSBnZ3Bsb3QoZGF0YSA9IHN1aWNpZGVfbGlmZSwgYWVzKHggPSBmYWN0b3IoMCksIHkgPSBoZGlmb3J5ZWFyZmlsdGVyKSkgKyBnZW9tX2JveHBsb3QoKQpwNSA8LSBnZ3Bsb3QoZGF0YSA9IHN1aWNpZGVfbGlmZSwgYWVzKHggPSBmYWN0b3IoMCksIHkgPSBsaWZlZXhwZWN0YW5jeWZpbHRlcikpICsgZ2VvbV9ib3hwbG90KCkKcDYgPC0gZ2dwbG90KGRhdGEgPSBzdWljaWRlX2xpZmUsIGFlcyh4ID0gZmFjdG9yKDApLCB5ID0gc3VpY2lkZV9wb3BfcHJvcCkpICsgZ2VvbV9ib3hwbG90KCkKcGxvdF9ncmlkKHAxLCBwMiwgcDMsIHA0LCBwNSwgcDYpCmBgYApBbGwgbnVtZXJpYyB2YWx1ZXMgaW4gdGhlIGRhdGFzZXQgYXBwZWFyIHRvIGhhdmUgc29tZSBvdXRsaWVycy4KCkZvciBtYW55IG9mIHRoZSB2YXJpYWJsZXMsIGl0IGFwcGVhcnMgKHZpc3VhbGx5KSB0aGF0IGZvciBzb21lIHZhcmlhYmxlcywgdGhlcmUgaXMgYSBsYXJnZSBhbW91bnQgb2Ygb3V0bGllcnMuIEZvciB0aGlzIHJlYXNvbiwgdGhlIGNsZWFuZWQgdmFyaWFibGVzIGFyZSBzdG9yZWQgaW4gYSBuZXcgZGF0YWZyYW1lIHRvIGFsbG93IGFuIGFuYWx5c3QgdG8gcmVmZXIgdG8gdGhlIG9yaWdpbmFsIGlmIG5lZWRlZC4KCk51bWVyaWMgdmFsdWVzIGFyZSBzdWJzZXQgaW50byBuZXcgZGF0YWZyYW1lIGFuZCB0aGUgb3V0bGllcnMgd2luZHNvcmlzZWQgdG8gdGhlIDV0aCBhbmQgOTV0aCBwZXJjZW50aWxlcy4KCmBgYHtyfQojb3JpZ2luYWwgZGF0YQpzdW1tYXJ5KHN1aWNpZGVfbGlmZSkKCmNhcHBlZCA8LSBzdWljaWRlX2xpZmUgJT4lIHNlbGVjdChjKCJzdWljaWRlc19ubyIsICJwb3B1bGF0aW9uIiwgInN1aWNpZGVzX3Blcl8xMDBrX3BvcCIsICJoZGlmb3J5ZWFyZmlsdGVyIiwgImxpZmVleHBlY3RhbmN5ZmlsdGVyIiwgInN1aWNpZGVfcG9wX3Byb3AiKSkKY2FwcGVkJHN1aWNpZGVzX25vIDwtIHN1aWNpZGVfbGlmZSRzdWljaWRlc19ubyAlPiUgY2FwKCkKY2FwcGVkJHBvcHVsYXRpb24gPC0gc3VpY2lkZV9saWZlJHBvcHVsYXRpb24gJT4lIGNhcCgpCmNhcHBlZCRzdWljaWRlc19wZXJfMTAwa19wb3AgPC0gc3VpY2lkZV9saWZlJHN1aWNpZGVzX3Blcl8xMDBrX3BvcCAlPiUgY2FwKCkKY2FwcGVkJGhkaWZvcnllYXJmaWx0ZXIgPC0gc3VpY2lkZV9saWZlJGhkaWZvcnllYXJmaWx0ZXIgJT4lIGNhcCgpCmNhcHBlZCRsaWZlZXhwZWN0YW5jeWZpbHRlciA8LSBjYXBwZWQkbGlmZWV4cGVjdGFuY3lmaWx0ZXIgJT4lIGNhcCgpCmNhcHBlZCRzdWljaWRlX3BvcF9wcm9wIDwtIHN1aWNpZGVfbGlmZSRzdWljaWRlX3BvcF9wcm9wICU+JSBjYXAoKQoKI3dpbmRzb3Jpc2VkIGRhdGEKc3VtbWFyeShjYXBwZWQpCmBgYAoKIyMJVHJhbnNmb3JtIAoKTG9va2luZyBhdCB0aGUgZGlzdHJpYnV0aW9uIG9mIGBsaWZlZXhwZWN0YW5jeWZpbHRlcl9jYXBwZWRgIHZhcmlhYmxlLCB3ZSBzZWUgdGhhdCBpdCBpcyBzbGlnaHRseSBsZWZ0IHNrZXdlZC4KCmBgYHtyfQpnZ3Bsb3QoY2FwcGVkLCBhZXMoeCA9IGxpZmVleHBlY3RhbmN5ZmlsdGVyKSkgKyBnZW9tX2hpc3RvZ3JhbSgpCmBgYAoKV2Ugd2lsbCB0cmFuc2Zvcm0gdGhlIGRhdGEgdG8gbWluaW1pc2UgdGhlIGVmZmVjdCBvZiBza2V3bmVzcyBpbiBhbmFseXNpcyBieSBhcHBseWluZyBhIEJveC1Db3ggdHJhbnNmb3JtYXRpb24gb24gdGhlIHZhcmlhYmxlLgoKYGBge3J9CmNhcHBlZCRsaWZlZXhwZWN0YW5jeWZpbHRlcl9ib3hjb3ggPC0gQm94Q294KGNhcHBlZCRsaWZlZXhwZWN0YW5jeWZpbHRlciwgbGFtYmRhID0gImF1dG8iKQoKZ2dwbG90KGRhdGEgPSBjYXBwZWQsIGFlcyh4ID0gbGlmZWV4cGVjdGFuY3lmaWx0ZXJfYm94Y294KSkgKyBnZW9tX2hpc3RvZ3JhbSgpCmBgYAoKVGhlIHJlc3VsdGluZyBkaXN0cmlidXRpb24gaXMgbGVzcyBza2V3ZWQgYW5kIHJlYWR5IGZvciBhbmFseXNpcy4KCjxicj4KPGJyPgo=