Recently a paper was published in the journal Nature titled “Evidence for a limit to human lifespan” which received wide publicity, by nature itself, the New York Times, the Atlantic, the Guardian, and the BBC to name but a few. However some of the methods in this paper have been criticised:

This @nature paper should be renamed ‘Evidence for a limit to Peer-Review’

(ht @StuartBuck1)https://t.co/OsHzPEZScV

— Amitabh Chandra (@amitabhchandra2) October 7, 2016

So, Nature paper on ‘human lifespan limit’ makes this inference from those TWO points at the very end!? Total toss. https://t.co/p1L8FnH9Cw pic.twitter.com/v7P9YMrJxM

— Stuart Ritchie (@StuartJRitchie) October 7, 2016

In this analysis I look at figure 2 specifically which argues that the maximum age of death has plateaued.

I downloaded the data from the International Database on Longevity at the Max Planck Institute for Demographic Research. The terms of the data access do not permit third party sharing so the raw data is not uploaded to GitHub but you can download it yourself if you want to rerun the following analyses.

First I load the data into R, tidy up some of the columns, and subset to the same individuals used the in paper. (Not sure why they didn’t just use all 668 rather than just 534). Here is the breakdown by country:

library(data.table)
library(ggplot2)
library(extRemes) # Generalised Extreme Value distribution functions
library(scales) # to define double log transform
# load in data
data.files <- list.files(path = "raw-data")
stopifnot(length(data.files) > 0)
age.data <- data.table()
i = 0
for (file in data.files){
  i <- i + 1
  temp <- fread(paste0("raw-data/", file))
  
  age.data <- rbind(age.data, temp)
}
# correct column names
setnames(age.data, make.names(colnames(age.data)))
# extract year of death only
age.data$death_year <- as.numeric(gsub("[0-9]+/[0-9]+/", "", age.data$Date.of.death))
age.data$age_inyears <- age.data$Age.days. / 365.25
full.age.data <- age.data
# subset to only people with year of death in certian countries as used by the paper
age.data <- age.data[Country.of.death %in% c("FRA","GBR","JPN","USA")]
# check the same number of people as in the paper
age.data[, .N, by = Country.of.death]

Now let’s recreate figure 2A.

Raw Data

# define function for getting 2nd highest value, 3rd highest etc.
maxN <- function(x, N = 2){
  len <- length(x)
  if(N > len){
    warning('N greater than length(x)')
    return(as.numeric(NA))
  }
  sort(x, partial = len - N + 1)[len - N + 1]
}
# calculate mean, Xth maximum etc summary values for each year of death
summarised <- age.data[,.(mean_age = mean(age_inyears),
                    n = .N,
                    sem = sd(age_inyears) / .N,
                    maximum = max(age_inyears),
                    second_maximum = maxN(age_inyears, 2),
                    third_maximum = maxN(age_inyears, 3),
                    fourth_maximum = maxN(age_inyears, 4),
                    fifth_maximum = maxN(age_inyears, 5),
                    sixth_maximum = maxN(age_inyears, 6)),
                 by = death_year]
# Repoduce Figure 2A
MRAD <- ggplot(summarised, aes(x=death_year, y=maximum)) +
  geom_point() +
  xlab("Year of Death") +
  ylab("Maximum Reported Age at Death")
MRAD

With regression lines

MRAD +
  geom_smooth(method = "lm", data = summarised[death_year > 1994]) +
  geom_smooth(method = "lm", data = summarised[death_year <= 1994])

The authors of the paper fitted two separate regression lines to this data arguing that after 1995 there was a change in the trend (a seemingly arbitrary choice of breakpoint - the choice of a broken vs linear trend has been analysed elsewhere).

You can see from the confidence intervals on the regression lines that the gradient for the second segment is actually consistent with being the same as the first segment. In the paper the authors calculate a p-value of 0.27 for the gradient of the second segment (null hypothesis = 0) and conclude “no further increases were observed”. They apply the same reasoning in a reply to a post-publication review on publons “The latter is not significant, so we conclude that the MRAD is essentially flat”. However, you can not accept the null hypothesis based on p > 0.05, you can only reject a null hypothesis. In this case a p-value of greater than 0.05 suggests that there is not enough data to conclude that the gradient is different from 0 (perhaps the null hypothesis should really by that the gradient is the same as the first segment, although the p-value is still non significant). The 95% confidence interval for the second segment gradient is −0.83 to +0.20 which includes the point estimate of the first segment gradient of 0.15 (using non rounded age values here).

# calculate 95% confidence intervals on the second segment gradient
confint(lm(maximum ~ death_year, summarised[death_year > 1994]), "death_year")
                2.5 %    97.5 %
death_year -0.8276784 0.1956178
cat("First segment point estimate: \n"); coef(lm(maximum ~ death_year, summarised[death_year <= 1994]))['death_year']
First segment point estimate: 
death_year 
 0.1533292 
cat("P-value when H0 = 0.1533: \n"); summary(lm(maximum ~ death_year, summarised[death_year > 1994], offset = 0.1533292 * death_year))$coefficients["death_year", "Pr(>|t|)", drop = FALSE]
P-value when H0 = 0.1533: 
             Pr(>|t|)
death_year 0.06819191

However this analysis is quite sensitive to the choice of breakpoint. In the above mentioned review response the authors re-analysed the data and found that a breakpoint of 1999 was a better fit. Although the package used (“segmented”) fits a continuous piecewise regression, I will continue using the method above to illustrate a different choice of date anyway. Replotting the regression lines using this breakpoint shows the confidence intervals more clearly supporting the downward trend and the p-value (with H0 = 1st segment gradient) is now significant at the 0.05 threshold. The upper 95% confidence interval is now −0.2 suggesting a downward trend (rather than a plateau).

MRAD +
  geom_smooth(method = "lm", data = summarised[death_year <= 1998]) +
  geom_smooth(method = "lm", data = summarised[death_year > 1998])

  
first.segment <- lm(maximum ~ death_year, summarised[death_year <= 1998])
second.segment <- lm(maximum ~ death_year, summarised[death_year > 1998])
# calculate 95% confidence intervals on the second segment gradient
cat("First segment gradient point estimate & confidence intervals: \n"); coef(first.segment)['death_year']; confint(first.segment, "death_year")
First segment gradient point estimate & confidence intervals: 
death_year 
 0.1926284 
                2.5 %   97.5 %
death_year 0.09503971 0.290217
cat("Second segment gradient point estimate & confidence intervals: \n"); coef(second.segment)['death_year']; confint(second.segment, "death_year")
Second segment gradient point estimate & confidence intervals: 
death_year 
-0.6874939 
               2.5 %     97.5 %
death_year -1.164361 -0.2106265
cat("P-value for second segment when H0 = 0.193: \n"); summary(lm(maximum ~ death_year, summarised[death_year > 1998], offset= 0.1926284 * death_year))$coefficients["death_year", "Pr(>|t|)", drop = FALSE]
P-value for second segment when H0 = 0.193: 
              Pr(>|t|)
death_year 0.004034243

Higher order maximums (2nd, 3rd etc)

The authors note that due to the fact that each of these data points is just a single individual the apparent plateau they observe could be due to random fluctuation. To strengthen their argument they looked at the 2nd highest reported age at death, 3rd highest etc and claimed that these series showed the same pattern. However the data points were only plotted for the 1st MRAD and only cubic smoothing splines for the remaining. Fitting a cubic spline could be misleading / overfitting and each series should probably be processed in the same manner as figure 2A if one is to conclude that they show the same pattern. Below I plot each series individually so the actual data is visible. The cubic splines show downward trends towards the end although with increasing uncertainty and linearity. Similarly with the linear regressions the gradient of the second segments are lower than the first segments although with increasing consistency between the two (note variable y-axis).

Raw Data

# Figure 2B
summary_melt <- melt(summarised, id.vars = c("death_year","mean_age","n","sem"))
summary_melt <- summary_melt[!is.na(value)]
# Recreate figure 2A for each maximum type
higher_orders <- ggplot(summary_melt, aes(death_year, value)) +
  facet_wrap(~ variable, ncol = 2, scales = "free") +
  geom_point() +
  xlab("Year of Death") +
  ylab("Age at Death")
higher_orders

With Cubic Spline

higher_orders +
  geom_smooth(method = "lm", formula = y ~ splines::bs(x, degree = 3), size = 0.5)

With Regression Lines

higher_orders +
  geom_smooth(method = "lm", data = summary_melt[death_year > 1994], size = 0.5) +
  geom_smooth(method = "lm", data = summary_melt[death_year <= 1994], size = 0.5)

Mean age of death

In another alternate approach the authors looked at all individuals in the dataset to calculate mean age of death and concluded that the annual average age of supercentenarians had not increased since 1968 (the start of the dataset). I recreate their plot below but with the addition of error bars representing the standard error of the mean for each point in order to visualise the uncertainty in the values.

# Figure 2C
ggplot(summarised, aes(x = death_year, y = mean_age)) +
  geom_errorbar(aes(ymin = mean_age-sem, ymax = mean_age+sem), width = 0.1) +
  geom_point() +
  xlab("Year of Death") +
  ylab("Mean Age at Death") +
  geom_smooth(method = "lm", formula = y ~ splines::bs(x, degree = 3), se=FALSE)

You can see that for the earlier points there are no error bars, this is because there is only a single data point for those years. It is therefore quite misleading to give each mean equal weighting by fitting a cubic spline to point estimates of the means alone.

A perhaps fairer approach is to recreate the graphs but using the whole dataset (note the dataset does not include anyone who died younger than 110). In this form the uncertainty in the first and last few years is much clearer, and the dip pattern fitted above is much less convincing. I would argue that a linear regression fits the data just as well and this gives an increase of ~ 0.04 years per year.

Cubic Spline

all_data <- ggplot(age.data, aes(x = death_year, y = age_inyears)) +
  geom_point(alpha = 0.3) +
  xlab("Year of Death") +
  ylab("Age at Death")
#plot(age.data$death_year, age.data$age_inyears)
#lines(smooth.spline(age.data$death_year, age.data$age_inyears), col="red")
all_data +
  geom_smooth(method = "lm", formula = y ~ splines::bs(x, degree = 3))

Linear Regression

all_data + 
    geom_smooth(method = "lm", colour="red") + 
    annotate("text", x = 1975, y = 121,
             label = paste("gradient =",format(coef(lm(age_inyears ~ death_year, age.data))[2], digits = 2)))

Sample Sizes

In the study the authors analysed maximum reported age of death (MRAD) over different years but the data for each year was from a different combination of countries and hence the sample size varies. One therefore might expect that the MRAD could change solely due to variation in the sample size (we are more likely to see high maximums when there is more data). Here I investigate the effect of using different sample sizes on the MRAD.

To get an equation for the distribution of age at death we can fit a generalised extreme value distribution to data from the UK Office of National Statistics (which fits much better than a normal distribution).

# Take data from UK Office of National Statistics life tables to fit distribution
# http://www.ons.gov.uk/generator?uri=/peoplepopulationandcommunity/birthsdeathsandmarriages/deaths/articles/mortalityinenglandandwales/2012-12-17/74c4dc50&format=csv
# And http://www.ons.gov.uk/generator?uri=/peoplepopulationandcommunity/birthsdeathsandmarriages/deaths/articles/mortalityinenglandandwales/2012-12-17/4b665795&format=csv
# load data
life_expectancy <- fread("chart7628369219653552718.csv") # females
life_expectancy_male <- fread("chart182252582428823712.csv") # males
# convert to numeric
life_expectancy$`2010` <- as.numeric(life_expectancy$`2010`) + as.numeric(life_expectancy_male$`2010`)
life_expectancy$Age <- as.numeric(life_expectancy$Age)
# convert histogram values to sample data
deaths <- numeric()
for (Age2 in life_expectancy$Age){
  deaths <- c(deaths, rep(as.numeric(Age2), life_expectancy[Age == Age2]$`2010`))
}
# fit to a generalised extreme value distribution
fitted_distribution <- fevd(-deaths)
fitted_parameters <- fitted_distribution$results$par
plot(fitted_distribution, type = "density", xlab = "Negative Age at death", main = "")

plot(fitted_distribution, type = "qq2")

params <- fitted_distribution$results$par
print(fitted_distribution$results$par)
   location       scale       shape 
-86.6589101   9.8271019   0.0372343 

We will also need to estimate the sample size (number of deaths) for each year in each country. For this I multiplied the world bank crude death rate by population size. We can then see how the total sample size varies over time in the original papers analysis.

# Calculate sample size as number of deaths for each year-country, by multiplying crude death rate by populatoin size
# from http://api.worldbank.org/v2/en/indicator/SP.DYN.CDRT.IN?downloadformat=csv
death.rate <- fread("API_SP.DYN.CDRT.IN_DS2_en_csv_v2.csv")
death.rate <- death.rate[`Country Code` %in% c("GBR","FRA","JPN","USA"),]
death.rate$`Indicator Name` <- NULL
death.rate$`Indicator Code` <- NULL
death.rate$`Country Name` <- NULL
death.rate <- melt(death.rate, id.vars = c("Country Code"), variable = "year", value = "death_rate")
setnames(death.rate,"Country Code","country")
death.rate$year <- as.numeric(as.character(death.rate$year))
# from http://api.worldbank.org/v2/en/indicator/SP.POP.TOTL?downloadformat=csv
population <- fread("API_SP.POP.TOTL_DS2_en_csv_v2.csv")
population <- population[`Country Code` %in% c("GBR","FRA","JPN","USA"),]
population$`Indicator Name` <- NULL
population$`Indicator Code` <- NULL
population$`Country Name` <- NULL
population <- melt(population, id.vars = c("Country Code"), variable = "year", value = "population")
setnames(population, "Country Code", "country")
population$year <- as.numeric(as.character(population$year))
setkey(population, "year", "country")
setkey(death.rate, "year", "country")
deaths <- population[death.rate]
deaths <- deaths[(country=="GBR" & year >= 1968 & year <= 2006) | 
             (country=="FRA" & year >= 1987 & year <= 2003) |
             (country=="USA" & year >= 1980 & year <= 2003) |
             (country=="JPN" & year >= 1996 & year <= 2007)]
deaths$population <- as.numeric(deaths$population)
deaths$death_rate <- as.numeric(deaths$death_rate)
deaths$deaths <- round((deaths$death_rate / 1000) * deaths$population)
deaths.summary <- deaths[, .(deaths = sum(deaths)), by = year]
qplot(deaths.summary$year, deaths.summary$deaths,
      geom = "point",
      xlab = "Year",
      ylab = "Total deaths")

The trend is similar to the regression lines they fit and so any bias from sample size would result in an overestimate in their favour for the gradient of both of the regression lines. However the effect of sample size on MRAD is probably not linear - maybe the population sizes used are large enough that the MRAD is effectively independent. With the sample size and an equation for the distribution of age at death we can now calculate the probability distribution of MRAD (more formally the nth order statistic) for different sample sizes. First let us look at the distributions of MRAD for the estimated minimum and maximum sample size used in the study.

# define k=1 (minimum) order statistic of minimum extream value distribution
order_pdf_k1_evd <- function(age, sample.size){
  sample.size *
    (1-pevd(age, loc = params['location'], scale = params['scale'], shape = params['shape']))^(sample.size - 1) * 
    devd(age, loc = params['location'], scale = params['scale'], shape = params['shape'])
}
max.sample.size = max(deaths.summary$deaths)
min.sample.size = min(deaths.summary$deaths)
# compare the probability density distributions for the minimum and maximum sample sizes used
age = seq(-109, -115, -0.02)
max.min.comparison <- data.table(
  density = c(order_pdf_k1_evd(age, sample.size = max.sample.size),
              order_pdf_k1_evd(age, sample.size = min.sample.size)),
  maximum.age = -c(age, age),
  sample.size = as.factor(rep(c(max.sample.size, min.sample.size), each = length(age))))
# find modes of both distributions
mode.min = -optimize(order_pdf_k1_evd,
                     interval = c(-100, -120),
                     sample.size = min.sample.size,
                     maximum = TRUE)$maximum
mode.max = -optimize(order_pdf_k1_evd,
                     interval = c(-100, -120),
                     sample.size = max.sample.size,
                     maximum = TRUE)$maximum
ggplot(max.min.comparison, aes(x = maximum.age, y = density, group = sample.size, colour = sample.size) ) +
  geom_line() +
  geom_vline(xintercept = mode.max, colour="turquoise", linetype = "dashed") +
  geom_vline(xintercept = mode.min, colour="red", linetype = "dashed") +
  annotate("text", label = paste(signif(mode.max - mode.min, digits = 3), "years"), x = 114, y = 0.6) +
  xlab("Maximum Reported Age at Death") + ylab("Probability Density")

This shows we might expect a difference of over a year in the MRAD due to the change in sample size alone (dashed lines indicate mode). We can also look at how the modal MRAD changes over many different sample sizes.

# plot most likely maximum observed value as a function of sample size
# Linear on log scale sampling
sample.sizes = matrix(unique(round(exp(runif(300, 0, 16)))))
modal_MRAD <- apply(sample.sizes, 1,function(x) optimize(order_pdf_k1_evd, interval = c(-90, -120), sample.size = x, maximum = TRUE)$maximum)
MRAD_samplesize <- data.table(sample.size = sample.sizes[, 1], modal.maximum = - modal_MRAD)[order(-sample.size)]
ggplot(MRAD_samplesize, aes(x = sample.size, y = modal.maximum)) +
  geom_line() +
  ylim(108, 113) + 
  geom_vline(xintercept = min.sample.size, linetype = "dashed") +
  geom_vline(xintercept = max.sample.size, linetype = "dashed") +
  xlab("Sample Size") + ylab("Modal Maximum Age")

The modal MRAD increases sharply at first and then starts to plateau once the sample size increases to millions of deaths. The dashed lines indicate the estimated minimum and maximum sample sizes used in the study. A double log distribution fits this curve well for reasonable sample sizes (>20).

# plot on double log x scale (straight line)
# define transformation
double_log_trans <- function(base = exp(1)){
trans <- function(x) log((log(x, base)), base)
inv <- function(x) (base^(base^x))
trans_new("double_log", trans, inv, log_breaks(base = base), domain = c(1e-100, Inf))
}
# plot
ggplot(MRAD_samplesize[sample.size > 20], aes(x = sample.size, y = modal.maximum)) +
  geom_point() +
  scale_x_continuous(trans = "double_log", breaks = 10^(2:7)) +
  xlab("Sample Size (double log scale)") +
  ylab("Modal maximum age") +
  geom_smooth(method = "lm", size = 0.5)

We can also plot the difference from the mean MRAD for each year in the study based on changing sample size alone.

sample.sizes.actual = matrix(deaths.summary$deaths)
MRAD_expected <- apply(sample.sizes.actual, 1, function(x) optimize(order_pdf_k1_evd, interval = c(-90, -120), sample.size = x, maximum = TRUE)$maximum)
MRAD_expected_by_samplesize <- data.table(sample.size = sample.sizes.actual[, 1], modal.maximum = - MRAD_expected)
MRAD_expected_by_samplesize$death_year <- deaths.summary$year
# to adjust points?
# MRAD_expected_by_samplesize$demeaned <- as.numeric(scale(MRAD_expected_by_samplesize$modal.maximum, scale = FALSE))
# setkey(MRAD_expected_by_samplesize, death_year)
# setkey(summarised, death_year)
# summarised <- MRAD_expected_by_samplesize[summarised]
# summarised$adjusted_maximum <- summarised$maximum - summarised$demeaned
qplot(MRAD_expected_by_samplesize$death_year,
      scale(MRAD_expected_by_samplesize$modal.maximum, scale = FALSE),
      xlab = "Year",
      ylab = "Mean-Centered MRAD")

Hence the sample sizes used would probably have a noticeable although small effect on the MRAD and a correction would slightly weaken the authors conclusions by reducing the gradient of both regression lines. Even though the effect is moderate it would have been nice to see an analysis of this type reported in the study.

Whether or not there is a genuine plateau rather than a temporary fluctuation would be clearer if there was more than 7-10 years of data beyond the breakpoint, given it is now a decade on perhaps there is new data available, for example from the USA Death Master File.

LS0tCnRpdGxlOiAiUmVhbmFseXNpcyBvZiB0aGUgZXZpZGVuY2UgZm9yIGEgbGltaXQgdG8gaHVtYW4gbGlmZXNwYW4iCmF1dGhvcjogIkRhbmllbCBXZWxscyIKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6IAogIGh0bWxfbm90ZWJvb2s6IAogICAgY29kZV9mb2xkaW5nOiBoaWRlCiAgICB0aGVtZTogY29zbW8KLS0tCgo8c3R5bGU+CiAgLm1haW4tY29udGFpbmVyIHsKICAgIG1heC13aWR0aDogNzAwcHggIWltcG9ydGFudDsKICB9Cjwvc3R5bGU+Cgo8c2NyaXB0IHNyYz0iaHR0cDovL3BsYXRmb3JtLnR3aXR0ZXIuY29tL3dpZGdldHMuanMiIGNoYXJzZXQ9InV0Zi04Ij48L3NjcmlwdD4KClJlY2VudGx5IGEgcGFwZXIgd2FzIHB1Ymxpc2hlZCBpbiB0aGUgam91cm5hbCBOYXR1cmUgdGl0bGVkICJbRXZpZGVuY2UgZm9yIGEgbGltaXQgdG8gaHVtYW4gbGlmZXNwYW5dKGh0dHA6Ly93d3cubmF0dXJlLmNvbS9uYXR1cmUvam91cm5hbC92YW9wL25jdXJyZW50L2Z1bGwvbmF0dXJlMTk3OTMuaHRtbCkiIHdoaWNoIHJlY2VpdmVkIHdpZGUgcHVibGljaXR5LCBieSBbbmF0dXJlIGl0c2VsZl0oaHR0cDovL3d3dy5uYXR1cmUuY29tL25ld3MvaHVtYW4tYWdlLWxpbWl0LWNsYWltLXNwYXJrcy1kZWJhdGUtMS4yMDc1MCksIHRoZSBbTmV3IFlvcmsgVGltZXNdKGh0dHA6Ly93d3cubnl0aW1lcy5jb20vMjAxNi8xMC8wNi9zY2llbmNlL21heGltdW0tbGlmZS1zcGFuLXN0dWR5Lmh0bWwpLCB0aGUgW0F0bGFudGljXShodHRwOi8vd3d3LnRoZWF0bGFudGljLmNvbS9zY2llbmNlL2FyY2hpdmUvMjAxNi8xMC9odW1hbnMtd29udC1ldmVyLWxpdmUtZmFyLWJleW9uZC0xMTUteWVhcnMvNTAyOTY3LyksIFt0aGUgR3VhcmRpYW5dKGh0dHBzOi8vd3d3LnRoZWd1YXJkaWFuLmNvbS9zY2llbmNlLzIwMTYvb2N0LzA1L2h1bWFuLWxpZmVzcGFuLWhhcy1oaXQtaXRzLW5hdHVyYWwtbGltaXQtcmVzZWFyY2gtc3VnZ2VzdHMpLCBhbmQgW3RoZSBCQkNdKGh0dHA6Ly93d3cuYmJjLmNvLnVrL25ld3MvaGVhbHRoLTM3NTUyMTE2KSB0byBuYW1lIGJ1dCBhIGZldy4gSG93ZXZlciBzb21lIG9mIHRoZSBtZXRob2RzIGluIHRoaXMgcGFwZXIgaGF2ZSBiZWVuIGNyaXRpY2lzZWQ6Cgo8YmxvY2txdW90ZSBjbGFzcz0idHdpdHRlci10d2VldCIgZGF0YS1sYW5nPSJlbiI+PHAgbGFuZz0iZW4iIGRpcj0ibHRyIj5UaGlzIDxhIGhyZWY9Imh0dHBzOi8vdHdpdHRlci5jb20vbmF0dXJlIj5AbmF0dXJlPC9hPiBwYXBlciBzaG91bGQgYmUgcmVuYW1lZCDigJhFdmlkZW5jZSBmb3IgYSBsaW1pdCB0byBQZWVyLVJldmlld+KAmSA8YnI+PGJyPihodCA8YSBocmVmPSJodHRwczovL3R3aXR0ZXIuY29tL1N0dWFydEJ1Y2sxIj5AU3R1YXJ0QnVjazE8L2E+KTxhIGhyZWY9Imh0dHBzOi8vdC5jby9Pc0h6UEVaU2NWIj5odHRwczovL3QuY28vT3NIelBFWlNjVjwvYT48L3A+Jm1kYXNoOyBBbWl0YWJoIENoYW5kcmEgKEBhbWl0YWJoY2hhbmRyYTIpIDxhIGhyZWY9Imh0dHBzOi8vdHdpdHRlci5jb20vYW1pdGFiaGNoYW5kcmEyL3N0YXR1cy83ODQzNzMzODY4MjY5NDQ1MTIiPk9jdG9iZXIgNywgMjAxNjwvYT48L2Jsb2NrcXVvdGU+IAoKPGJsb2NrcXVvdGUgY2xhc3M9InR3aXR0ZXItdHdlZXQiIGRhdGEtY2FyZHM9ImhpZGRlbiIgZGF0YS1sYW5nPSJlbiI+PHAgbGFuZz0iZW4iIGRpcj0ibHRyIj5TbywgTmF0dXJlIHBhcGVyIG9uICYjMzk7aHVtYW4gbGlmZXNwYW4gbGltaXQmIzM5OyBtYWtlcyB0aGlzIGluZmVyZW5jZSBmcm9tIHRob3NlIFRXTyBwb2ludHMgYXQgdGhlIHZlcnkgZW5kIT8gVG90YWwgdG9zcy4gPGEgaHJlZj0iaHR0cHM6Ly90LmNvL3AxTDhGbkg5Q3ciPmh0dHBzOi8vdC5jby9wMUw4Rm5IOUN3PC9hPiA8YSBocmVmPSJodHRwczovL3QuY28vdjdQOVlNckp4TSI+cGljLnR3aXR0ZXIuY29tL3Y3UDlZTXJKeE08L2E+PC9wPiZtZGFzaDsgU3R1YXJ0IFJpdGNoaWUgKEBTdHVhcnRKUml0Y2hpZSkgPGEgaHJlZj0iaHR0cHM6Ly90d2l0dGVyLmNvbS9TdHVhcnRKUml0Y2hpZS9zdGF0dXMvNzg0MzE5NDE1MDM4ODQ0OTI5Ij5PY3RvYmVyIDcsIDIwMTY8L2E+PC9ibG9ja3F1b3RlPgoKSW4gdGhpcyBhbmFseXNpcyBJIGxvb2sgYXQgZmlndXJlIDIgc3BlY2lmaWNhbGx5IHdoaWNoIGFyZ3VlcyB0aGF0IHRoZSBtYXhpbXVtIGFnZSBvZiBkZWF0aCBoYXMgcGxhdGVhdWVkLgoKSSBkb3dubG9hZGVkIHRoZSBkYXRhIGZyb20gdGhlIFtJbnRlcm5hdGlvbmFsIERhdGFiYXNlIG9uIExvbmdldml0eQphdCB0aGUgTWF4IFBsYW5jayBJbnN0aXR1dGUgZm9yIERlbW9ncmFwaGljIFJlc2VhcmNoXShodHRwOi8vd3d3LnN1cGVyY2VudGVuYXJpYW5zLm9yZykuIFRoZSB0ZXJtcyBvZiB0aGUgZGF0YSBhY2Nlc3MgZG8gbm90IHBlcm1pdCB0aGlyZCBwYXJ0eSBzaGFyaW5nIHNvIHRoZSByYXcgZGF0YSBpcyBub3QgdXBsb2FkZWQgdG8gR2l0SHViIGJ1dCB5b3UgY2FuIGRvd25sb2FkIGl0IHlvdXJzZWxmIGlmIHlvdSB3YW50IHRvIHJlcnVuIHRoZSBmb2xsb3dpbmcgYW5hbHlzZXMuCgpGaXJzdCBJIGxvYWQgdGhlIGRhdGEgaW50byBSLCB0aWR5IHVwIHNvbWUgb2YgdGhlIGNvbHVtbnMsIGFuZCBzdWJzZXQgdG8gdGhlIHNhbWUgaW5kaXZpZHVhbHMgdXNlZCB0aGUgaW4gcGFwZXIuIChOb3Qgc3VyZSB3aHkgdGhleSBkaWRuJ3QganVzdCB1c2UgYWxsIDY2OCByYXRoZXIgdGhhbiBqdXN0IDUzNCkuIEhlcmUgaXMgdGhlIGJyZWFrZG93biBieSBjb3VudHJ5OgpgYGB7ciB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KbGlicmFyeShkYXRhLnRhYmxlKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZXh0UmVtZXMpICMgR2VuZXJhbGlzZWQgRXh0cmVtZSBWYWx1ZSBkaXN0cmlidXRpb24gZnVuY3Rpb25zCmxpYnJhcnkoc2NhbGVzKSAjIHRvIGRlZmluZSBkb3VibGUgbG9nIHRyYW5zZm9ybQoKIyBsb2FkIGluIGRhdGEKZGF0YS5maWxlcyA8LSBsaXN0LmZpbGVzKHBhdGggPSAicmF3LWRhdGEiKQpzdG9waWZub3QobGVuZ3RoKGRhdGEuZmlsZXMpID4gMCkKCmFnZS5kYXRhIDwtIGRhdGEudGFibGUoKQppID0gMAoKZm9yIChmaWxlIGluIGRhdGEuZmlsZXMpewogIGkgPC0gaSArIDEKICB0ZW1wIDwtIGZyZWFkKHBhc3RlMCgicmF3LWRhdGEvIiwgZmlsZSkpCiAgCiAgYWdlLmRhdGEgPC0gcmJpbmQoYWdlLmRhdGEsIHRlbXApCn0KCiMgY29ycmVjdCBjb2x1bW4gbmFtZXMKc2V0bmFtZXMoYWdlLmRhdGEsIG1ha2UubmFtZXMoY29sbmFtZXMoYWdlLmRhdGEpKSkKCiMgZXh0cmFjdCB5ZWFyIG9mIGRlYXRoIG9ubHkKYWdlLmRhdGEkZGVhdGhfeWVhciA8LSBhcy5udW1lcmljKGdzdWIoIlswLTldKy9bMC05XSsvIiwgIiIsIGFnZS5kYXRhJERhdGUub2YuZGVhdGgpKQphZ2UuZGF0YSRhZ2VfaW55ZWFycyA8LSBhZ2UuZGF0YSRBZ2UuZGF5cy4gLyAzNjUuMjUKCmZ1bGwuYWdlLmRhdGEgPC0gYWdlLmRhdGEKCiMgc3Vic2V0IHRvIG9ubHkgcGVvcGxlIHdpdGggeWVhciBvZiBkZWF0aCBpbiBjZXJ0aWFuIGNvdW50cmllcyBhcyB1c2VkIGJ5IHRoZSBwYXBlcgphZ2UuZGF0YSA8LSBhZ2UuZGF0YVtDb3VudHJ5Lm9mLmRlYXRoICVpbiUgYygiRlJBIiwiR0JSIiwiSlBOIiwiVVNBIildCgojIGNoZWNrIHRoZSBzYW1lIG51bWJlciBvZiBwZW9wbGUgYXMgaW4gdGhlIHBhcGVyCmFnZS5kYXRhWywgLk4sIGJ5ID0gQ291bnRyeS5vZi5kZWF0aF0KYGBgCgoKTm93IGxldCdzIHJlY3JlYXRlIGZpZ3VyZSAyQS4KCiMjIHsudGFic2V0fQojIyMgUmF3IERhdGEKCmBgYHtyIHdhcm5pbmc9RkFMU0V9CiMgZGVmaW5lIGZ1bmN0aW9uIGZvciBnZXR0aW5nIDJuZCBoaWdoZXN0IHZhbHVlLCAzcmQgaGlnaGVzdCBldGMuCm1heE4gPC0gZnVuY3Rpb24oeCwgTiA9IDIpewogIGxlbiA8LSBsZW5ndGgoeCkKICBpZihOID4gbGVuKXsKICAgIHdhcm5pbmcoJ04gZ3JlYXRlciB0aGFuIGxlbmd0aCh4KScpCiAgICByZXR1cm4oYXMubnVtZXJpYyhOQSkpCiAgfQogIHNvcnQoeCwgcGFydGlhbCA9IGxlbiAtIE4gKyAxKVtsZW4gLSBOICsgMV0KfQoKIyBjYWxjdWxhdGUgbWVhbiwgWHRoIG1heGltdW0gZXRjIHN1bW1hcnkgdmFsdWVzIGZvciBlYWNoIHllYXIgb2YgZGVhdGgKc3VtbWFyaXNlZCA8LSBhZ2UuZGF0YVssLihtZWFuX2FnZSA9IG1lYW4oYWdlX2lueWVhcnMpLAogICAgICAgICAgICAgICAgICAgIG4gPSAuTiwKICAgICAgICAgICAgICAgICAgICBzZW0gPSBzZChhZ2VfaW55ZWFycykgLyAuTiwKICAgICAgICAgICAgICAgICAgICBtYXhpbXVtID0gbWF4KGFnZV9pbnllYXJzKSwKICAgICAgICAgICAgICAgICAgICBzZWNvbmRfbWF4aW11bSA9IG1heE4oYWdlX2lueWVhcnMsIDIpLAogICAgICAgICAgICAgICAgICAgIHRoaXJkX21heGltdW0gPSBtYXhOKGFnZV9pbnllYXJzLCAzKSwKICAgICAgICAgICAgICAgICAgICBmb3VydGhfbWF4aW11bSA9IG1heE4oYWdlX2lueWVhcnMsIDQpLAogICAgICAgICAgICAgICAgICAgIGZpZnRoX21heGltdW0gPSBtYXhOKGFnZV9pbnllYXJzLCA1KSwKICAgICAgICAgICAgICAgICAgICBzaXh0aF9tYXhpbXVtID0gbWF4TihhZ2VfaW55ZWFycywgNikpLAogICAgICAgICAgICAgICAgIGJ5ID0gZGVhdGhfeWVhcl0KCiMgUmVwb2R1Y2UgRmlndXJlIDJBCk1SQUQgPC0gZ2dwbG90KHN1bW1hcmlzZWQsIGFlcyh4PWRlYXRoX3llYXIsIHk9bWF4aW11bSkpICsKICBnZW9tX3BvaW50KCkgKwogIHhsYWIoIlllYXIgb2YgRGVhdGgiKSArCiAgeWxhYigiTWF4aW11bSBSZXBvcnRlZCBBZ2UgYXQgRGVhdGgiKQoKTVJBRApgYGAKCiMjIyBXaXRoIHJlZ3Jlc3Npb24gbGluZXMKYGBge3J9Ck1SQUQgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIGRhdGEgPSBzdW1tYXJpc2VkW2RlYXRoX3llYXIgPiAxOTk0XSkgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIGRhdGEgPSBzdW1tYXJpc2VkW2RlYXRoX3llYXIgPD0gMTk5NF0pCmBgYAoKIyMgey50YWJzZXR9CgpUaGUgYXV0aG9ycyBvZiB0aGUgcGFwZXIgZml0dGVkIHR3byBzZXBhcmF0ZSByZWdyZXNzaW9uIGxpbmVzIHRvIHRoaXMgZGF0YSBhcmd1aW5nIHRoYXQgYWZ0ZXIgMTk5NSB0aGVyZSB3YXMgYSBjaGFuZ2UgaW4gdGhlIHRyZW5kIChhIHNlZW1pbmdseSBhcmJpdHJhcnkgY2hvaWNlIG9mIGJyZWFrcG9pbnQgLSB0aGUgY2hvaWNlIG9mIGEgYnJva2VuIHZzIGxpbmVhciB0cmVuZCBoYXMgYmVlbiBhbmFseXNlZCBbZWxzZXdoZXJlXShodHRwczovL2dpdGh1Yi5jb20vcGhpbGlwcGJlcmVucy9saWZlc3BhbikpLgoKWW91IGNhbiBzZWUgZnJvbSB0aGUgY29uZmlkZW5jZSBpbnRlcnZhbHMgb24gdGhlIHJlZ3Jlc3Npb24gbGluZXMgdGhhdCB0aGUgZ3JhZGllbnQgZm9yIHRoZSBzZWNvbmQgc2VnbWVudCBpcyBhY3R1YWxseSBjb25zaXN0ZW50IHdpdGggYmVpbmcgdGhlIHNhbWUgYXMgdGhlIGZpcnN0IHNlZ21lbnQuIEluIHRoZSBwYXBlciB0aGUgYXV0aG9ycyBjYWxjdWxhdGUgYSBwLXZhbHVlIG9mIDAuMjcgZm9yIHRoZSBncmFkaWVudCBvZiB0aGUgc2Vjb25kIHNlZ21lbnQgKG51bGwgaHlwb3RoZXNpcyA9IDApIGFuZCBjb25jbHVkZSAibm8gZnVydGhlciBpbmNyZWFzZXMgd2VyZSBvYnNlcnZlZCIuIFRoZXkgYXBwbHkgdGhlIHNhbWUgcmVhc29uaW5nIGluIGEgcmVwbHkgdG8gYSBbcG9zdC1wdWJsaWNhdGlvbiByZXZpZXcgb24gcHVibG9uc10oaHR0cHM6Ly9wdWJsb25zLmNvbS9yZXZpZXcvNDgwNTE3LyNjMTk2KSAiVGhlIGxhdHRlciBpcyBub3Qgc2lnbmlmaWNhbnQsIHNvIHdlIGNvbmNsdWRlIHRoYXQgdGhlIE1SQUQgaXMgZXNzZW50aWFsbHkgZmxhdCIuIEhvd2V2ZXIsIHlvdSBjYW4gbm90IGFjY2VwdCB0aGUgbnVsbCBoeXBvdGhlc2lzIGJhc2VkIG9uIHAgPiAwLjA1LCB5b3UgY2FuIG9ubHkgcmVqZWN0IGEgbnVsbCBoeXBvdGhlc2lzLiBJbiB0aGlzIGNhc2UgYSBwLXZhbHVlIG9mIGdyZWF0ZXIgdGhhbiAwLjA1IHN1Z2dlc3RzIHRoYXQgdGhlcmUgaXMgbm90IGVub3VnaCBkYXRhIHRvIGNvbmNsdWRlIHRoYXQgdGhlIGdyYWRpZW50IGlzIGRpZmZlcmVudCBmcm9tIDAgKHBlcmhhcHMgdGhlIG51bGwgaHlwb3RoZXNpcyBzaG91bGQgcmVhbGx5IGJ5IHRoYXQgdGhlIGdyYWRpZW50IGlzIHRoZSBzYW1lIGFzIHRoZSBmaXJzdCBzZWdtZW50LCBhbHRob3VnaCB0aGUgcC12YWx1ZSBpcyBzdGlsbCBub24gc2lnbmlmaWNhbnQpLiBUaGUgOTUlIGNvbmZpZGVuY2UgaW50ZXJ2YWwgZm9yIHRoZSBzZWNvbmQgc2VnbWVudCBncmFkaWVudCBpcyDiiJIwLjgzIHRvICswLjIwIHdoaWNoIGluY2x1ZGVzIHRoZSBwb2ludCBlc3RpbWF0ZSBvZiB0aGUgZmlyc3Qgc2VnbWVudCBncmFkaWVudCBvZiAwLjE1ICh1c2luZyBub24gcm91bmRlZCBhZ2UgdmFsdWVzIGhlcmUpLgoKYGBge3J9CiMgY2FsY3VsYXRlIDk1JSBjb25maWRlbmNlIGludGVydmFscyBvbiB0aGUgc2Vjb25kIHNlZ21lbnQgZ3JhZGllbnQKY29uZmludChsbShtYXhpbXVtIH4gZGVhdGhfeWVhciwgc3VtbWFyaXNlZFtkZWF0aF95ZWFyID4gMTk5NF0pLCAiZGVhdGhfeWVhciIpCgpjYXQoIkZpcnN0IHNlZ21lbnQgcG9pbnQgZXN0aW1hdGU6IFxuIik7IGNvZWYobG0obWF4aW11bSB+IGRlYXRoX3llYXIsIHN1bW1hcmlzZWRbZGVhdGhfeWVhciA8PSAxOTk0XSkpWydkZWF0aF95ZWFyJ10KCmNhdCgiUC12YWx1ZSB3aGVuIEgwID0gMC4xNTMzOiBcbiIpOyBzdW1tYXJ5KGxtKG1heGltdW0gfiBkZWF0aF95ZWFyLCBzdW1tYXJpc2VkW2RlYXRoX3llYXIgPiAxOTk0XSwgb2Zmc2V0ID0gMC4xNTMzMjkyICogZGVhdGhfeWVhcikpJGNvZWZmaWNpZW50c1siZGVhdGhfeWVhciIsICJQcig+fHR8KSIsIGRyb3AgPSBGQUxTRV0KYGBgCgpIb3dldmVyIHRoaXMgYW5hbHlzaXMgaXMgcXVpdGUgc2Vuc2l0aXZlIHRvIHRoZSBjaG9pY2Ugb2YgYnJlYWtwb2ludC4gSW4gdGhlIGFib3ZlIG1lbnRpb25lZCByZXZpZXcgcmVzcG9uc2UgdGhlIGF1dGhvcnMgcmUtYW5hbHlzZWQgdGhlIGRhdGEgYW5kIGZvdW5kIHRoYXQgYSBicmVha3BvaW50IG9mIDE5OTkgd2FzIGEgYmV0dGVyIGZpdC4gQWx0aG91Z2ggdGhlIHBhY2thZ2UgdXNlZCAoInNlZ21lbnRlZCIpIGZpdHMgYSBjb250aW51b3VzIHBpZWNld2lzZSByZWdyZXNzaW9uLCBJIHdpbGwgY29udGludWUgdXNpbmcgdGhlIG1ldGhvZCBhYm92ZSB0byBpbGx1c3RyYXRlIGEgZGlmZmVyZW50IGNob2ljZSBvZiBkYXRlIGFueXdheS4gUmVwbG90dGluZyB0aGUgcmVncmVzc2lvbiBsaW5lcyB1c2luZyB0aGlzIGJyZWFrcG9pbnQgc2hvd3MgdGhlIGNvbmZpZGVuY2UgaW50ZXJ2YWxzIG1vcmUgY2xlYXJseSBzdXBwb3J0aW5nIHRoZSBkb3dud2FyZCB0cmVuZCBhbmQgdGhlIHAtdmFsdWUgKHdpdGggSDAgPSAxc3Qgc2VnbWVudCBncmFkaWVudCkgaXMgbm93IHNpZ25pZmljYW50IGF0IHRoZSAwLjA1IHRocmVzaG9sZC4gVGhlIHVwcGVyIDk1JSBjb25maWRlbmNlIGludGVydmFsIGlzIG5vdyDiiJIwLjIgc3VnZ2VzdGluZyBhIGRvd253YXJkIHRyZW5kIChyYXRoZXIgdGhhbiBhIHBsYXRlYXUpLgoKYGBge3J9Ck1SQUQgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIGRhdGEgPSBzdW1tYXJpc2VkW2RlYXRoX3llYXIgPD0gMTk5OF0pICsKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBkYXRhID0gc3VtbWFyaXNlZFtkZWF0aF95ZWFyID4gMTk5OF0pCiAgCgpmaXJzdC5zZWdtZW50IDwtIGxtKG1heGltdW0gfiBkZWF0aF95ZWFyLCBzdW1tYXJpc2VkW2RlYXRoX3llYXIgPD0gMTk5OF0pCnNlY29uZC5zZWdtZW50IDwtIGxtKG1heGltdW0gfiBkZWF0aF95ZWFyLCBzdW1tYXJpc2VkW2RlYXRoX3llYXIgPiAxOTk4XSkKCiMgY2FsY3VsYXRlIDk1JSBjb25maWRlbmNlIGludGVydmFscyBvbiB0aGUgc2Vjb25kIHNlZ21lbnQgZ3JhZGllbnQKY2F0KCJGaXJzdCBzZWdtZW50IGdyYWRpZW50IHBvaW50IGVzdGltYXRlICYgY29uZmlkZW5jZSBpbnRlcnZhbHM6IFxuIik7IGNvZWYoZmlyc3Quc2VnbWVudClbJ2RlYXRoX3llYXInXTsgY29uZmludChmaXJzdC5zZWdtZW50LCAiZGVhdGhfeWVhciIpCgpjYXQoIlNlY29uZCBzZWdtZW50IGdyYWRpZW50IHBvaW50IGVzdGltYXRlICYgY29uZmlkZW5jZSBpbnRlcnZhbHM6IFxuIik7IGNvZWYoc2Vjb25kLnNlZ21lbnQpWydkZWF0aF95ZWFyJ107IGNvbmZpbnQoc2Vjb25kLnNlZ21lbnQsICJkZWF0aF95ZWFyIikKCmNhdCgiUC12YWx1ZSBmb3Igc2Vjb25kIHNlZ21lbnQgd2hlbiBIMCA9IDAuMTkzOiBcbiIpOyBzdW1tYXJ5KGxtKG1heGltdW0gfiBkZWF0aF95ZWFyLCBzdW1tYXJpc2VkW2RlYXRoX3llYXIgPiAxOTk4XSwgb2Zmc2V0PSAwLjE5MjYyODQgKiBkZWF0aF95ZWFyKSkkY29lZmZpY2llbnRzWyJkZWF0aF95ZWFyIiwgIlByKD58dHwpIiwgZHJvcCA9IEZBTFNFXQpgYGAKCiMgSGlnaGVyIG9yZGVyIG1heGltdW1zICgybmQsIDNyZCBldGMpClRoZSBhdXRob3JzIG5vdGUgdGhhdCBkdWUgdG8gdGhlIGZhY3QgdGhhdCBlYWNoIG9mIHRoZXNlIGRhdGEgcG9pbnRzIGlzIGp1c3QgYSBzaW5nbGUgaW5kaXZpZHVhbCB0aGUgYXBwYXJlbnQgcGxhdGVhdSB0aGV5IG9ic2VydmUgY291bGQgYmUgZHVlIHRvIHJhbmRvbSBmbHVjdHVhdGlvbi4gVG8gc3RyZW5ndGhlbiB0aGVpciBhcmd1bWVudCB0aGV5IGxvb2tlZCBhdCB0aGUgMm5kIGhpZ2hlc3QgcmVwb3J0ZWQgYWdlIGF0IGRlYXRoLCAzcmQgaGlnaGVzdCBldGMgYW5kIGNsYWltZWQgdGhhdCB0aGVzZSBzZXJpZXMgc2hvd2VkIHRoZSBzYW1lIHBhdHRlcm4uIEhvd2V2ZXIgdGhlIGRhdGEgcG9pbnRzIHdlcmUgb25seSBwbG90dGVkIGZvciB0aGUgMXN0IE1SQUQgYW5kIG9ubHkgY3ViaWMgc21vb3RoaW5nIHNwbGluZXMgZm9yIHRoZSByZW1haW5pbmcuIEZpdHRpbmcgYSBjdWJpYyBzcGxpbmUgY291bGQgYmUgbWlzbGVhZGluZyAvIG92ZXJmaXR0aW5nIGFuZCBlYWNoIHNlcmllcyBzaG91bGQgcHJvYmFibHkgYmUgcHJvY2Vzc2VkIGluIHRoZSBzYW1lIG1hbm5lciBhcyBmaWd1cmUgMkEgaWYgb25lIGlzIHRvIGNvbmNsdWRlIHRoYXQgdGhleSBzaG93IHRoZSBzYW1lIHBhdHRlcm4uIEJlbG93IEkgcGxvdCBlYWNoIHNlcmllcyBpbmRpdmlkdWFsbHkgc28gdGhlIGFjdHVhbCBkYXRhIGlzIHZpc2libGUuIFRoZSBjdWJpYyBzcGxpbmVzIHNob3cgZG93bndhcmQgdHJlbmRzIHRvd2FyZHMgdGhlIGVuZCBhbHRob3VnaCB3aXRoIGluY3JlYXNpbmcgdW5jZXJ0YWludHkgYW5kIGxpbmVhcml0eS4gU2ltaWxhcmx5IHdpdGggdGhlIGxpbmVhciByZWdyZXNzaW9ucyB0aGUgZ3JhZGllbnQgb2YgdGhlIHNlY29uZCBzZWdtZW50cyBhcmUgbG93ZXIgdGhhbiB0aGUgZmlyc3Qgc2VnbWVudHMgYWx0aG91Z2ggd2l0aCBpbmNyZWFzaW5nIGNvbnNpc3RlbmN5IGJldHdlZW4gdGhlIHR3byAobm90ZSB2YXJpYWJsZSB5LWF4aXMpLgoKIyMgey50YWJzZXR9CiMjIyBSYXcgRGF0YQpgYGB7ciB3YXJuaW5nPUZBTFNFLCBmaWcuaGVpZ2h0PTcsIGZpZy53aWR0aD03fQojIEZpZ3VyZSAyQgpzdW1tYXJ5X21lbHQgPC0gbWVsdChzdW1tYXJpc2VkLCBpZC52YXJzID0gYygiZGVhdGhfeWVhciIsIm1lYW5fYWdlIiwibiIsInNlbSIpKQpzdW1tYXJ5X21lbHQgPC0gc3VtbWFyeV9tZWx0WyFpcy5uYSh2YWx1ZSldCgojIFJlY3JlYXRlIGZpZ3VyZSAyQSBmb3IgZWFjaCBtYXhpbXVtIHR5cGUKaGlnaGVyX29yZGVycyA8LSBnZ3Bsb3Qoc3VtbWFyeV9tZWx0LCBhZXMoZGVhdGhfeWVhciwgdmFsdWUpKSArCiAgZmFjZXRfd3JhcCh+IHZhcmlhYmxlLCBuY29sID0gMiwgc2NhbGVzID0gImZyZWUiKSArCiAgZ2VvbV9wb2ludCgpICsKICB4bGFiKCJZZWFyIG9mIERlYXRoIikgKwogIHlsYWIoIkFnZSBhdCBEZWF0aCIpCgpoaWdoZXJfb3JkZXJzCmBgYAoKIyMjIFdpdGggQ3ViaWMgU3BsaW5lCmBgYHtyIHdhcm5pbmc9RkFMU0UsIGZpZy5oZWlnaHQ9NywgZmlnLndpZHRoPTd9CmhpZ2hlcl9vcmRlcnMgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIGZvcm11bGEgPSB5IH4gc3BsaW5lczo6YnMoeCwgZGVncmVlID0gMyksIHNpemUgPSAwLjUpCmBgYAoKIyMjIFdpdGggUmVncmVzc2lvbiBMaW5lcwpgYGB7ciB3YXJuaW5nPUZBTFNFLCBmaWcuaGVpZ2h0PTcsIGZpZy53aWR0aD03fQpoaWdoZXJfb3JkZXJzICsKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBkYXRhID0gc3VtbWFyeV9tZWx0W2RlYXRoX3llYXIgPiAxOTk0XSwgc2l6ZSA9IDAuNSkgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIGRhdGEgPSBzdW1tYXJ5X21lbHRbZGVhdGhfeWVhciA8PSAxOTk0XSwgc2l6ZSA9IDAuNSkKYGBgCgoKIyBNZWFuIGFnZSBvZiBkZWF0aAoKSW4gYW5vdGhlciBhbHRlcm5hdGUgYXBwcm9hY2ggdGhlIGF1dGhvcnMgbG9va2VkIGF0IGFsbCBpbmRpdmlkdWFscyBpbiB0aGUgZGF0YXNldCB0byBjYWxjdWxhdGUgbWVhbiBhZ2Ugb2YgZGVhdGggYW5kIGNvbmNsdWRlZCB0aGF0IHRoZSBhbm51YWwgYXZlcmFnZSBhZ2Ugb2Ygc3VwZXJjZW50ZW5hcmlhbnMgaGFkIG5vdCBpbmNyZWFzZWQgc2luY2UgMTk2OCAodGhlIHN0YXJ0IG9mIHRoZSBkYXRhc2V0KS4gSSByZWNyZWF0ZSB0aGVpciBwbG90IGJlbG93IGJ1dCB3aXRoIHRoZSBhZGRpdGlvbiBvZiBlcnJvciBiYXJzIHJlcHJlc2VudGluZyB0aGUgc3RhbmRhcmQgZXJyb3Igb2YgdGhlIG1lYW4gZm9yIGVhY2ggcG9pbnQgaW4gb3JkZXIgdG8gdmlzdWFsaXNlIHRoZSB1bmNlcnRhaW50eSBpbiB0aGUgdmFsdWVzLgoKYGBge3Igd2FybmluZz1GQUxTRX0KIyBGaWd1cmUgMkMKZ2dwbG90KHN1bW1hcmlzZWQsIGFlcyh4ID0gZGVhdGhfeWVhciwgeSA9IG1lYW5fYWdlKSkgKwogIGdlb21fZXJyb3JiYXIoYWVzKHltaW4gPSBtZWFuX2FnZS1zZW0sIHltYXggPSBtZWFuX2FnZStzZW0pLCB3aWR0aCA9IDAuMSkgKwogIGdlb21fcG9pbnQoKSArCiAgeGxhYigiWWVhciBvZiBEZWF0aCIpICsKICB5bGFiKCJNZWFuIEFnZSBhdCBEZWF0aCIpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBmb3JtdWxhID0geSB+IHNwbGluZXM6OmJzKHgsIGRlZ3JlZSA9IDMpLCBzZT1GQUxTRSkKYGBgCgpZb3UgY2FuIHNlZSB0aGF0IGZvciB0aGUgZWFybGllciBwb2ludHMgdGhlcmUgYXJlIG5vIGVycm9yIGJhcnMsIHRoaXMgaXMgYmVjYXVzZSB0aGVyZSBpcyBvbmx5IGEgc2luZ2xlIGRhdGEgcG9pbnQgZm9yIHRob3NlIHllYXJzLiBJdCBpcyB0aGVyZWZvcmUgcXVpdGUgbWlzbGVhZGluZyB0byBnaXZlIGVhY2ggbWVhbiBlcXVhbCB3ZWlnaHRpbmcgYnkgZml0dGluZyBhIGN1YmljIHNwbGluZSB0byBwb2ludCBlc3RpbWF0ZXMgb2YgdGhlIG1lYW5zIGFsb25lLgoKQSBwZXJoYXBzIGZhaXJlciBhcHByb2FjaCBpcyB0byByZWNyZWF0ZSB0aGUgZ3JhcGhzIGJ1dCB1c2luZyB0aGUgd2hvbGUgZGF0YXNldCAobm90ZSB0aGUgZGF0YXNldCBkb2VzIG5vdCBpbmNsdWRlIGFueW9uZSB3aG8gZGllZCB5b3VuZ2VyIHRoYW4gMTEwKS4gSW4gdGhpcyBmb3JtIHRoZSB1bmNlcnRhaW50eSBpbiB0aGUgZmlyc3QgYW5kIGxhc3QgZmV3IHllYXJzIGlzIG11Y2ggY2xlYXJlciwgYW5kIHRoZSBkaXAgcGF0dGVybiBmaXR0ZWQgYWJvdmUgaXMgbXVjaCBsZXNzIGNvbnZpbmNpbmcuIEkgd291bGQgYXJndWUgdGhhdCBhIGxpbmVhciByZWdyZXNzaW9uIGZpdHMgdGhlIGRhdGEganVzdCBhcyB3ZWxsIGFuZCB0aGlzIGdpdmVzIGFuIGluY3JlYXNlIG9mIH4gMC4wNCB5ZWFycyBwZXIgeWVhci4KCiMjICB7LnRhYnNldH0KIyMjIEN1YmljIFNwbGluZQpgYGB7cn0KYWxsX2RhdGEgPC0gZ2dwbG90KGFnZS5kYXRhLCBhZXMoeCA9IGRlYXRoX3llYXIsIHkgPSBhZ2VfaW55ZWFycykpICsKICBnZW9tX3BvaW50KGFscGhhID0gMC4zKSArCiAgeGxhYigiWWVhciBvZiBEZWF0aCIpICsKICB5bGFiKCJBZ2UgYXQgRGVhdGgiKQoKI3Bsb3QoYWdlLmRhdGEkZGVhdGhfeWVhciwgYWdlLmRhdGEkYWdlX2lueWVhcnMpCiNsaW5lcyhzbW9vdGguc3BsaW5lKGFnZS5kYXRhJGRlYXRoX3llYXIsIGFnZS5kYXRhJGFnZV9pbnllYXJzKSwgY29sPSJyZWQiKQoKYWxsX2RhdGEgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIGZvcm11bGEgPSB5IH4gc3BsaW5lczo6YnMoeCwgZGVncmVlID0gMykpCmBgYAoKIyMjIExpbmVhciBSZWdyZXNzaW9uCmBgYHtyfQphbGxfZGF0YSArIAogICAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgY29sb3VyPSJyZWQiKSArIAogICAgYW5ub3RhdGUoInRleHQiLCB4ID0gMTk3NSwgeSA9IDEyMSwKICAgICAgICAgICAgIGxhYmVsID0gcGFzdGUoImdyYWRpZW50ID0iLGZvcm1hdChjb2VmKGxtKGFnZV9pbnllYXJzIH4gZGVhdGhfeWVhciwgYWdlLmRhdGEpKVsyXSwgZGlnaXRzID0gMikpKQoKYGBgCgojIFNhbXBsZSBTaXplcwpJbiB0aGUgc3R1ZHkgdGhlIGF1dGhvcnMgYW5hbHlzZWQgbWF4aW11bSByZXBvcnRlZCBhZ2Ugb2YgZGVhdGggKE1SQUQpIG92ZXIgZGlmZmVyZW50IHllYXJzIGJ1dCB0aGUgZGF0YSBmb3IgZWFjaCB5ZWFyIHdhcyBmcm9tIGEgZGlmZmVyZW50IGNvbWJpbmF0aW9uIG9mIGNvdW50cmllcyBhbmQgaGVuY2UgdGhlIHNhbXBsZSBzaXplIHZhcmllcy4gT25lIHRoZXJlZm9yZSBtaWdodCBleHBlY3QgdGhhdCB0aGUgTVJBRCBjb3VsZCBjaGFuZ2Ugc29sZWx5IGR1ZSB0byB2YXJpYXRpb24gaW4gdGhlIHNhbXBsZSBzaXplICh3ZSBhcmUgbW9yZSBsaWtlbHkgdG8gc2VlIGhpZ2ggbWF4aW11bXMgd2hlbiB0aGVyZSBpcyBtb3JlIGRhdGEpLiBIZXJlIEkgaW52ZXN0aWdhdGUgdGhlIGVmZmVjdCBvZiB1c2luZyBkaWZmZXJlbnQgc2FtcGxlIHNpemVzIG9uIHRoZSBNUkFELgoKVG8gZ2V0IGFuIGVxdWF0aW9uIGZvciB0aGUgZGlzdHJpYnV0aW9uIG9mIGFnZSBhdCBkZWF0aCB3ZSBjYW4gZml0IGEgW2dlbmVyYWxpc2VkIGV4dHJlbWUgdmFsdWUgZGlzdHJpYnV0aW9uXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9HZW5lcmFsaXplZF9leHRyZW1lX3ZhbHVlX2Rpc3RyaWJ1dGlvbikgdG8gZGF0YSBmcm9tIHRoZSBVSyBbT2ZmaWNlIG9mIE5hdGlvbmFsIFN0YXRpc3RpY3NdKGh0dHA6Ly93d3cub25zLmdvdi51ay9wZW9wbGVwb3B1bGF0aW9uYW5kY29tbXVuaXR5L2JpcnRoc2RlYXRoc2FuZG1hcnJpYWdlcy9kZWF0aHMvYXJ0aWNsZXMvbW9ydGFsaXR5aW5lbmdsYW5kYW5kd2FsZXMvMjAxMi0xMi0xNykgKHdoaWNoIGZpdHMgbXVjaCBiZXR0ZXIgdGhhbiBhIG5vcm1hbCBkaXN0cmlidXRpb24pLgpgYGB7cn0KIyBUYWtlIGRhdGEgZnJvbSBVSyBPZmZpY2Ugb2YgTmF0aW9uYWwgU3RhdGlzdGljcyBsaWZlIHRhYmxlcyB0byBmaXQgZGlzdHJpYnV0aW9uCiMgaHR0cDovL3d3dy5vbnMuZ292LnVrL2dlbmVyYXRvcj91cmk9L3Blb3BsZXBvcHVsYXRpb25hbmRjb21tdW5pdHkvYmlydGhzZGVhdGhzYW5kbWFycmlhZ2VzL2RlYXRocy9hcnRpY2xlcy9tb3J0YWxpdHlpbmVuZ2xhbmRhbmR3YWxlcy8yMDEyLTEyLTE3Lzc0YzRkYzUwJmZvcm1hdD1jc3YKCiMgQW5kIGh0dHA6Ly93d3cub25zLmdvdi51ay9nZW5lcmF0b3I/dXJpPS9wZW9wbGVwb3B1bGF0aW9uYW5kY29tbXVuaXR5L2JpcnRoc2RlYXRoc2FuZG1hcnJpYWdlcy9kZWF0aHMvYXJ0aWNsZXMvbW9ydGFsaXR5aW5lbmdsYW5kYW5kd2FsZXMvMjAxMi0xMi0xNy80YjY2NTc5NSZmb3JtYXQ9Y3N2CgojIGxvYWQgZGF0YQpsaWZlX2V4cGVjdGFuY3kgPC0gZnJlYWQoImNoYXJ0NzYyODM2OTIxOTY1MzU1MjcxOC5jc3YiKSAjIGZlbWFsZXMKbGlmZV9leHBlY3RhbmN5X21hbGUgPC0gZnJlYWQoImNoYXJ0MTgyMjUyNTgyNDI4ODIzNzEyLmNzdiIpICMgbWFsZXMKCiMgY29udmVydCB0byBudW1lcmljCmxpZmVfZXhwZWN0YW5jeSRgMjAxMGAgPC0gYXMubnVtZXJpYyhsaWZlX2V4cGVjdGFuY3kkYDIwMTBgKSArIGFzLm51bWVyaWMobGlmZV9leHBlY3RhbmN5X21hbGUkYDIwMTBgKQpsaWZlX2V4cGVjdGFuY3kkQWdlIDwtIGFzLm51bWVyaWMobGlmZV9leHBlY3RhbmN5JEFnZSkKCiMgY29udmVydCBoaXN0b2dyYW0gdmFsdWVzIHRvIHNhbXBsZSBkYXRhCmRlYXRocyA8LSBudW1lcmljKCkKZm9yIChBZ2UyIGluIGxpZmVfZXhwZWN0YW5jeSRBZ2UpewogIGRlYXRocyA8LSBjKGRlYXRocywgcmVwKGFzLm51bWVyaWMoQWdlMiksIGxpZmVfZXhwZWN0YW5jeVtBZ2UgPT0gQWdlMl0kYDIwMTBgKSkKfQoKIyBmaXQgdG8gYSBnZW5lcmFsaXNlZCBleHRyZW1lIHZhbHVlIGRpc3RyaWJ1dGlvbgpmaXR0ZWRfZGlzdHJpYnV0aW9uIDwtIGZldmQoLWRlYXRocykKZml0dGVkX3BhcmFtZXRlcnMgPC0gZml0dGVkX2Rpc3RyaWJ1dGlvbiRyZXN1bHRzJHBhcgoKCnBsb3QoZml0dGVkX2Rpc3RyaWJ1dGlvbiwgdHlwZSA9ICJkZW5zaXR5IiwgeGxhYiA9ICJOZWdhdGl2ZSBBZ2UgYXQgZGVhdGgiLCBtYWluID0gIiIpCnBsb3QoZml0dGVkX2Rpc3RyaWJ1dGlvbiwgdHlwZSA9ICJxcTIiKQoKcGFyYW1zIDwtIGZpdHRlZF9kaXN0cmlidXRpb24kcmVzdWx0cyRwYXIKcHJpbnQoZml0dGVkX2Rpc3RyaWJ1dGlvbiRyZXN1bHRzJHBhcikKYGBgCgpXZSB3aWxsIGFsc28gbmVlZCB0byBlc3RpbWF0ZSB0aGUgc2FtcGxlIHNpemUgKG51bWJlciBvZiBkZWF0aHMpIGZvciBlYWNoIHllYXIgaW4gZWFjaCBjb3VudHJ5LiBGb3IgdGhpcyBJIG11bHRpcGxpZWQgdGhlIHdvcmxkIGJhbmsgY3J1ZGUgZGVhdGggcmF0ZSBieSBwb3B1bGF0aW9uIHNpemUuIFdlIGNhbiB0aGVuIHNlZSBob3cgdGhlIHRvdGFsIHNhbXBsZSBzaXplIHZhcmllcyBvdmVyIHRpbWUgaW4gdGhlIG9yaWdpbmFsIHBhcGVycyBhbmFseXNpcy4KCmBgYHtyIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQoKIyBDYWxjdWxhdGUgc2FtcGxlIHNpemUgYXMgbnVtYmVyIG9mIGRlYXRocyBmb3IgZWFjaCB5ZWFyLWNvdW50cnksIGJ5IG11bHRpcGx5aW5nIGNydWRlIGRlYXRoIHJhdGUgYnkgcG9wdWxhdG9pbiBzaXplCgojIGZyb20gaHR0cDovL2FwaS53b3JsZGJhbmsub3JnL3YyL2VuL2luZGljYXRvci9TUC5EWU4uQ0RSVC5JTj9kb3dubG9hZGZvcm1hdD1jc3YKZGVhdGgucmF0ZSA8LSBmcmVhZCgiQVBJX1NQLkRZTi5DRFJULklOX0RTMl9lbl9jc3ZfdjIuY3N2IikKZGVhdGgucmF0ZSA8LSBkZWF0aC5yYXRlW2BDb3VudHJ5IENvZGVgICVpbiUgYygiR0JSIiwiRlJBIiwiSlBOIiwiVVNBIiksXQpkZWF0aC5yYXRlJGBJbmRpY2F0b3IgTmFtZWAgPC0gTlVMTApkZWF0aC5yYXRlJGBJbmRpY2F0b3IgQ29kZWAgPC0gTlVMTApkZWF0aC5yYXRlJGBDb3VudHJ5IE5hbWVgIDwtIE5VTEwKCmRlYXRoLnJhdGUgPC0gbWVsdChkZWF0aC5yYXRlLCBpZC52YXJzID0gYygiQ291bnRyeSBDb2RlIiksIHZhcmlhYmxlID0gInllYXIiLCB2YWx1ZSA9ICJkZWF0aF9yYXRlIikKc2V0bmFtZXMoZGVhdGgucmF0ZSwiQ291bnRyeSBDb2RlIiwiY291bnRyeSIpCmRlYXRoLnJhdGUkeWVhciA8LSBhcy5udW1lcmljKGFzLmNoYXJhY3RlcihkZWF0aC5yYXRlJHllYXIpKQoKIyBmcm9tIGh0dHA6Ly9hcGkud29ybGRiYW5rLm9yZy92Mi9lbi9pbmRpY2F0b3IvU1AuUE9QLlRPVEw/ZG93bmxvYWRmb3JtYXQ9Y3N2CnBvcHVsYXRpb24gPC0gZnJlYWQoIkFQSV9TUC5QT1AuVE9UTF9EUzJfZW5fY3N2X3YyLmNzdiIpCnBvcHVsYXRpb24gPC0gcG9wdWxhdGlvbltgQ291bnRyeSBDb2RlYCAlaW4lIGMoIkdCUiIsIkZSQSIsIkpQTiIsIlVTQSIpLF0KcG9wdWxhdGlvbiRgSW5kaWNhdG9yIE5hbWVgIDwtIE5VTEwKcG9wdWxhdGlvbiRgSW5kaWNhdG9yIENvZGVgIDwtIE5VTEwKcG9wdWxhdGlvbiRgQ291bnRyeSBOYW1lYCA8LSBOVUxMCgpwb3B1bGF0aW9uIDwtIG1lbHQocG9wdWxhdGlvbiwgaWQudmFycyA9IGMoIkNvdW50cnkgQ29kZSIpLCB2YXJpYWJsZSA9ICJ5ZWFyIiwgdmFsdWUgPSAicG9wdWxhdGlvbiIpCnNldG5hbWVzKHBvcHVsYXRpb24sICJDb3VudHJ5IENvZGUiLCAiY291bnRyeSIpCnBvcHVsYXRpb24keWVhciA8LSBhcy5udW1lcmljKGFzLmNoYXJhY3Rlcihwb3B1bGF0aW9uJHllYXIpKQoKc2V0a2V5KHBvcHVsYXRpb24sICJ5ZWFyIiwgImNvdW50cnkiKQpzZXRrZXkoZGVhdGgucmF0ZSwgInllYXIiLCAiY291bnRyeSIpCmRlYXRocyA8LSBwb3B1bGF0aW9uW2RlYXRoLnJhdGVdCgpkZWF0aHMgPC0gZGVhdGhzWyhjb3VudHJ5PT0iR0JSIiAmIHllYXIgPj0gMTk2OCAmIHllYXIgPD0gMjAwNikgfCAKICAgICAgICAgICAgIChjb3VudHJ5PT0iRlJBIiAmIHllYXIgPj0gMTk4NyAmIHllYXIgPD0gMjAwMykgfAogICAgICAgICAgICAgKGNvdW50cnk9PSJVU0EiICYgeWVhciA+PSAxOTgwICYgeWVhciA8PSAyMDAzKSB8CiAgICAgICAgICAgICAoY291bnRyeT09IkpQTiIgJiB5ZWFyID49IDE5OTYgJiB5ZWFyIDw9IDIwMDcpXQpkZWF0aHMkcG9wdWxhdGlvbiA8LSBhcy5udW1lcmljKGRlYXRocyRwb3B1bGF0aW9uKQpkZWF0aHMkZGVhdGhfcmF0ZSA8LSBhcy5udW1lcmljKGRlYXRocyRkZWF0aF9yYXRlKQpkZWF0aHMkZGVhdGhzIDwtIHJvdW5kKChkZWF0aHMkZGVhdGhfcmF0ZSAvIDEwMDApICogZGVhdGhzJHBvcHVsYXRpb24pCmRlYXRocy5zdW1tYXJ5IDwtIGRlYXRoc1ssIC4oZGVhdGhzID0gc3VtKGRlYXRocykpLCBieSA9IHllYXJdCgpxcGxvdChkZWF0aHMuc3VtbWFyeSR5ZWFyLCBkZWF0aHMuc3VtbWFyeSRkZWF0aHMsCiAgICAgIGdlb20gPSAicG9pbnQiLAogICAgICB4bGFiID0gIlllYXIiLAogICAgICB5bGFiID0gIlRvdGFsIGRlYXRocyIpCmBgYAoKVGhlIHRyZW5kIGlzIHNpbWlsYXIgdG8gdGhlIHJlZ3Jlc3Npb24gbGluZXMgdGhleSBmaXQgYW5kIHNvIGFueSBiaWFzIGZyb20gc2FtcGxlIHNpemUgd291bGQgcmVzdWx0IGluIGFuIG92ZXJlc3RpbWF0ZSBpbiB0aGVpciBmYXZvdXIgZm9yIHRoZSBncmFkaWVudCBvZiBib3RoIG9mIHRoZSByZWdyZXNzaW9uIGxpbmVzLiBIb3dldmVyIHRoZSBlZmZlY3Qgb2Ygc2FtcGxlIHNpemUgb24gTVJBRCBpcyBwcm9iYWJseSBub3QgbGluZWFyIC0gbWF5YmUgdGhlIHBvcHVsYXRpb24gc2l6ZXMgdXNlZCBhcmUgbGFyZ2UgZW5vdWdoIHRoYXQgdGhlIE1SQUQgaXMgZWZmZWN0aXZlbHkgaW5kZXBlbmRlbnQuIFdpdGggdGhlIHNhbXBsZSBzaXplIGFuZCBhbiBlcXVhdGlvbiBmb3IgdGhlIGRpc3RyaWJ1dGlvbiBvZiBhZ2UgYXQgZGVhdGggd2UgY2FuIG5vdyBjYWxjdWxhdGUgdGhlIHByb2JhYmlsaXR5IGRpc3RyaWJ1dGlvbiBvZiBNUkFEIChtb3JlIGZvcm1hbGx5IHRoZSBudGggW29yZGVyIHN0YXRpc3RpY10oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvT3JkZXJfc3RhdGlzdGljKSkgZm9yIGRpZmZlcmVudCBzYW1wbGUgc2l6ZXMuIEZpcnN0IGxldCB1cyBsb29rIGF0IHRoZSBkaXN0cmlidXRpb25zIG9mIE1SQUQgZm9yIHRoZSBlc3RpbWF0ZWQgbWluaW11bSBhbmQgbWF4aW11bSBzYW1wbGUgc2l6ZSB1c2VkIGluIHRoZSBzdHVkeS4gCgpgYGB7cn0KCiMgZGVmaW5lIGs9MSAobWluaW11bSkgb3JkZXIgc3RhdGlzdGljIG9mIG1pbmltdW0gZXh0cmVhbSB2YWx1ZSBkaXN0cmlidXRpb24Kb3JkZXJfcGRmX2sxX2V2ZCA8LSBmdW5jdGlvbihhZ2UsIHNhbXBsZS5zaXplKXsKICBzYW1wbGUuc2l6ZSAqCiAgICAoMS1wZXZkKGFnZSwgbG9jID0gcGFyYW1zWydsb2NhdGlvbiddLCBzY2FsZSA9IHBhcmFtc1snc2NhbGUnXSwgc2hhcGUgPSBwYXJhbXNbJ3NoYXBlJ10pKV4oc2FtcGxlLnNpemUgLSAxKSAqIAogICAgZGV2ZChhZ2UsIGxvYyA9IHBhcmFtc1snbG9jYXRpb24nXSwgc2NhbGUgPSBwYXJhbXNbJ3NjYWxlJ10sIHNoYXBlID0gcGFyYW1zWydzaGFwZSddKQp9CgptYXguc2FtcGxlLnNpemUgPSBtYXgoZGVhdGhzLnN1bW1hcnkkZGVhdGhzKQptaW4uc2FtcGxlLnNpemUgPSBtaW4oZGVhdGhzLnN1bW1hcnkkZGVhdGhzKQoKIyBjb21wYXJlIHRoZSBwcm9iYWJpbGl0eSBkZW5zaXR5IGRpc3RyaWJ1dGlvbnMgZm9yIHRoZSBtaW5pbXVtIGFuZCBtYXhpbXVtIHNhbXBsZSBzaXplcyB1c2VkCmFnZSA9IHNlcSgtMTA5LCAtMTE1LCAtMC4wMikKCm1heC5taW4uY29tcGFyaXNvbiA8LSBkYXRhLnRhYmxlKAogIGRlbnNpdHkgPSBjKG9yZGVyX3BkZl9rMV9ldmQoYWdlLCBzYW1wbGUuc2l6ZSA9IG1heC5zYW1wbGUuc2l6ZSksCiAgICAgICAgICAgICAgb3JkZXJfcGRmX2sxX2V2ZChhZ2UsIHNhbXBsZS5zaXplID0gbWluLnNhbXBsZS5zaXplKSksCiAgbWF4aW11bS5hZ2UgPSAtYyhhZ2UsIGFnZSksCiAgc2FtcGxlLnNpemUgPSBhcy5mYWN0b3IocmVwKGMobWF4LnNhbXBsZS5zaXplLCBtaW4uc2FtcGxlLnNpemUpLCBlYWNoID0gbGVuZ3RoKGFnZSkpKSkKCiMgZmluZCBtb2RlcyBvZiBib3RoIGRpc3RyaWJ1dGlvbnMKbW9kZS5taW4gPSAtb3B0aW1pemUob3JkZXJfcGRmX2sxX2V2ZCwKICAgICAgICAgICAgICAgICAgICAgaW50ZXJ2YWwgPSBjKC0xMDAsIC0xMjApLAogICAgICAgICAgICAgICAgICAgICBzYW1wbGUuc2l6ZSA9IG1pbi5zYW1wbGUuc2l6ZSwKICAgICAgICAgICAgICAgICAgICAgbWF4aW11bSA9IFRSVUUpJG1heGltdW0KCm1vZGUubWF4ID0gLW9wdGltaXplKG9yZGVyX3BkZl9rMV9ldmQsCiAgICAgICAgICAgICAgICAgICAgIGludGVydmFsID0gYygtMTAwLCAtMTIwKSwKICAgICAgICAgICAgICAgICAgICAgc2FtcGxlLnNpemUgPSBtYXguc2FtcGxlLnNpemUsCiAgICAgICAgICAgICAgICAgICAgIG1heGltdW0gPSBUUlVFKSRtYXhpbXVtCgpnZ3Bsb3QobWF4Lm1pbi5jb21wYXJpc29uLCBhZXMoeCA9IG1heGltdW0uYWdlLCB5ID0gZGVuc2l0eSwgZ3JvdXAgPSBzYW1wbGUuc2l6ZSwgY29sb3VyID0gc2FtcGxlLnNpemUpICkgKwogIGdlb21fbGluZSgpICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBtb2RlLm1heCwgY29sb3VyPSJ0dXJxdW9pc2UiLCBsaW5ldHlwZSA9ICJkYXNoZWQiKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gbW9kZS5taW4sIGNvbG91cj0icmVkIiwgbGluZXR5cGUgPSAiZGFzaGVkIikgKwogIGFubm90YXRlKCJ0ZXh0IiwgbGFiZWwgPSBwYXN0ZShzaWduaWYobW9kZS5tYXggLSBtb2RlLm1pbiwgZGlnaXRzID0gMyksICJ5ZWFycyIpLCB4ID0gMTE0LCB5ID0gMC42KSArCiAgeGxhYigiTWF4aW11bSBSZXBvcnRlZCBBZ2UgYXQgRGVhdGgiKSArIHlsYWIoIlByb2JhYmlsaXR5IERlbnNpdHkiKQpgYGAKClRoaXMgc2hvd3Mgd2UgbWlnaHQgZXhwZWN0IGEgZGlmZmVyZW5jZSBvZiBvdmVyIGEgeWVhciBpbiB0aGUgTVJBRCBkdWUgdG8gdGhlIGNoYW5nZSBpbiBzYW1wbGUgc2l6ZSBhbG9uZSAoZGFzaGVkIGxpbmVzIGluZGljYXRlIG1vZGUpLiBXZSBjYW4gYWxzbyBsb29rIGF0IGhvdyB0aGUgbW9kYWwgTVJBRCBjaGFuZ2VzIG92ZXIgbWFueSBkaWZmZXJlbnQgc2FtcGxlIHNpemVzLgoKYGBge3J9CiMgcGxvdCBtb3N0IGxpa2VseSBtYXhpbXVtIG9ic2VydmVkIHZhbHVlIGFzIGEgZnVuY3Rpb24gb2Ygc2FtcGxlIHNpemUKCiMgTGluZWFyIG9uIGxvZyBzY2FsZSBzYW1wbGluZwpzYW1wbGUuc2l6ZXMgPSBtYXRyaXgodW5pcXVlKHJvdW5kKGV4cChydW5pZigzMDAsIDAsIDE2KSkpKSkKCm1vZGFsX01SQUQgPC0gYXBwbHkoc2FtcGxlLnNpemVzLCAxLGZ1bmN0aW9uKHgpIG9wdGltaXplKG9yZGVyX3BkZl9rMV9ldmQsIGludGVydmFsID0gYygtOTAsIC0xMjApLCBzYW1wbGUuc2l6ZSA9IHgsIG1heGltdW0gPSBUUlVFKSRtYXhpbXVtKQoKTVJBRF9zYW1wbGVzaXplIDwtIGRhdGEudGFibGUoc2FtcGxlLnNpemUgPSBzYW1wbGUuc2l6ZXNbLCAxXSwgbW9kYWwubWF4aW11bSA9IC0gbW9kYWxfTVJBRClbb3JkZXIoLXNhbXBsZS5zaXplKV0KCmdncGxvdChNUkFEX3NhbXBsZXNpemUsIGFlcyh4ID0gc2FtcGxlLnNpemUsIHkgPSBtb2RhbC5tYXhpbXVtKSkgKwogIGdlb21fbGluZSgpICsKICB5bGltKDEwOCwgMTEzKSArIAogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IG1pbi5zYW1wbGUuc2l6ZSwgbGluZXR5cGUgPSAiZGFzaGVkIikgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IG1heC5zYW1wbGUuc2l6ZSwgbGluZXR5cGUgPSAiZGFzaGVkIikgKwogIHhsYWIoIlNhbXBsZSBTaXplIikgKyB5bGFiKCJNb2RhbCBNYXhpbXVtIEFnZSIpCmBgYAoKVGhlIG1vZGFsIE1SQUQgaW5jcmVhc2VzIHNoYXJwbHkgYXQgZmlyc3QgYW5kIHRoZW4gc3RhcnRzIHRvIHBsYXRlYXUgb25jZSB0aGUgc2FtcGxlIHNpemUgaW5jcmVhc2VzIHRvIG1pbGxpb25zIG9mIGRlYXRocy4gVGhlIGRhc2hlZCBsaW5lcyBpbmRpY2F0ZSB0aGUgZXN0aW1hdGVkIG1pbmltdW0gYW5kIG1heGltdW0gc2FtcGxlIHNpemVzIHVzZWQgaW4gdGhlIHN0dWR5LiBBIGRvdWJsZSBsb2cgZGlzdHJpYnV0aW9uIGZpdHMgdGhpcyBjdXJ2ZSB3ZWxsIGZvciByZWFzb25hYmxlIHNhbXBsZSBzaXplcyAoPjIwKS4KCmBgYHtyfQojIHBsb3Qgb24gZG91YmxlIGxvZyB4IHNjYWxlIChzdHJhaWdodCBsaW5lKQoKIyBkZWZpbmUgdHJhbnNmb3JtYXRpb24KZG91YmxlX2xvZ190cmFucyA8LSBmdW5jdGlvbihiYXNlID0gZXhwKDEpKXsKdHJhbnMgPC0gZnVuY3Rpb24oeCkgbG9nKChsb2coeCwgYmFzZSkpLCBiYXNlKQppbnYgPC0gZnVuY3Rpb24oeCkgKGJhc2VeKGJhc2VeeCkpCnRyYW5zX25ldygiZG91YmxlX2xvZyIsIHRyYW5zLCBpbnYsIGxvZ19icmVha3MoYmFzZSA9IGJhc2UpLCBkb21haW4gPSBjKDFlLTEwMCwgSW5mKSkKfQoKIyBwbG90CmdncGxvdChNUkFEX3NhbXBsZXNpemVbc2FtcGxlLnNpemUgPiAyMF0sIGFlcyh4ID0gc2FtcGxlLnNpemUsIHkgPSBtb2RhbC5tYXhpbXVtKSkgKwogIGdlb21fcG9pbnQoKSArCiAgc2NhbGVfeF9jb250aW51b3VzKHRyYW5zID0gImRvdWJsZV9sb2ciLCBicmVha3MgPSAxMF4oMjo3KSkgKwogIHhsYWIoIlNhbXBsZSBTaXplIChkb3VibGUgbG9nIHNjYWxlKSIpICsKICB5bGFiKCJNb2RhbCBtYXhpbXVtIGFnZSIpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzaXplID0gMC41KQpgYGAKCldlIGNhbiBhbHNvIHBsb3QgdGhlIGRpZmZlcmVuY2UgZnJvbSB0aGUgbWVhbiBNUkFEIGZvciBlYWNoIHllYXIgaW4gdGhlIHN0dWR5IGJhc2VkIG9uIGNoYW5naW5nIHNhbXBsZSBzaXplIGFsb25lLgoKYGBge3J9CgpzYW1wbGUuc2l6ZXMuYWN0dWFsID0gbWF0cml4KGRlYXRocy5zdW1tYXJ5JGRlYXRocykKCk1SQURfZXhwZWN0ZWQgPC0gYXBwbHkoc2FtcGxlLnNpemVzLmFjdHVhbCwgMSwgZnVuY3Rpb24oeCkgb3B0aW1pemUob3JkZXJfcGRmX2sxX2V2ZCwgaW50ZXJ2YWwgPSBjKC05MCwgLTEyMCksIHNhbXBsZS5zaXplID0geCwgbWF4aW11bSA9IFRSVUUpJG1heGltdW0pCgpNUkFEX2V4cGVjdGVkX2J5X3NhbXBsZXNpemUgPC0gZGF0YS50YWJsZShzYW1wbGUuc2l6ZSA9IHNhbXBsZS5zaXplcy5hY3R1YWxbLCAxXSwgbW9kYWwubWF4aW11bSA9IC0gTVJBRF9leHBlY3RlZCkKTVJBRF9leHBlY3RlZF9ieV9zYW1wbGVzaXplJGRlYXRoX3llYXIgPC0gZGVhdGhzLnN1bW1hcnkkeWVhcgoKIyB0byBhZGp1c3QgcG9pbnRzPwojIE1SQURfZXhwZWN0ZWRfYnlfc2FtcGxlc2l6ZSRkZW1lYW5lZCA8LSBhcy5udW1lcmljKHNjYWxlKE1SQURfZXhwZWN0ZWRfYnlfc2FtcGxlc2l6ZSRtb2RhbC5tYXhpbXVtLCBzY2FsZSA9IEZBTFNFKSkKIyBzZXRrZXkoTVJBRF9leHBlY3RlZF9ieV9zYW1wbGVzaXplLCBkZWF0aF95ZWFyKQojIHNldGtleShzdW1tYXJpc2VkLCBkZWF0aF95ZWFyKQojIHN1bW1hcmlzZWQgPC0gTVJBRF9leHBlY3RlZF9ieV9zYW1wbGVzaXplW3N1bW1hcmlzZWRdCiMgc3VtbWFyaXNlZCRhZGp1c3RlZF9tYXhpbXVtIDwtIHN1bW1hcmlzZWQkbWF4aW11bSAtIHN1bW1hcmlzZWQkZGVtZWFuZWQKCnFwbG90KE1SQURfZXhwZWN0ZWRfYnlfc2FtcGxlc2l6ZSRkZWF0aF95ZWFyLAogICAgICBzY2FsZShNUkFEX2V4cGVjdGVkX2J5X3NhbXBsZXNpemUkbW9kYWwubWF4aW11bSwgc2NhbGUgPSBGQUxTRSksCiAgICAgIHhsYWIgPSAiWWVhciIsCiAgICAgIHlsYWIgPSAiTWVhbi1DZW50ZXJlZCBNUkFEIikKYGBgCgpIZW5jZSB0aGUgc2FtcGxlIHNpemVzIHVzZWQgd291bGQgcHJvYmFibHkgaGF2ZSBhIG5vdGljZWFibGUgYWx0aG91Z2ggc21hbGwgZWZmZWN0IG9uIHRoZSBNUkFEIGFuZCBhIGNvcnJlY3Rpb24gd291bGQgc2xpZ2h0bHkgd2Vha2VuIHRoZSBhdXRob3JzIGNvbmNsdXNpb25zIGJ5IHJlZHVjaW5nIHRoZSBncmFkaWVudCBvZiBib3RoIHJlZ3Jlc3Npb24gbGluZXMuIEV2ZW4gdGhvdWdoIHRoZSBlZmZlY3QgaXMgbW9kZXJhdGUgaXQgd291bGQgaGF2ZSBiZWVuIG5pY2UgdG8gc2VlIGFuIGFuYWx5c2lzIG9mIHRoaXMgdHlwZSByZXBvcnRlZCBpbiB0aGUgc3R1ZHkuCgpXaGV0aGVyIG9yIG5vdCB0aGVyZSBpcyBhIGdlbnVpbmUgcGxhdGVhdSByYXRoZXIgdGhhbiBhIHRlbXBvcmFyeSBmbHVjdHVhdGlvbiB3b3VsZCBiZSBjbGVhcmVyIGlmIHRoZXJlIHdhcyBtb3JlIHRoYW4gNy0xMCB5ZWFycyBvZiBkYXRhIGJleW9uZCB0aGUgYnJlYWtwb2ludCwgZ2l2ZW4gaXQgaXMgbm93IGEgZGVjYWRlIG9uIHBlcmhhcHMgdGhlcmUgaXMgbmV3IGRhdGEgYXZhaWxhYmxlLCBmb3IgZXhhbXBsZSBmcm9tIHRoZSBbVVNBIERlYXRoIE1hc3RlciBGaWxlXShodHRwczovL2NsYXNzaWMubnRpcy5nb3YvcHJvZHVjdHMvc3NhLWRtZi8pLgo=