1 Preamble

We start by loading the necessary packages and the .RData for these examples.

knitr::opts_chunk$set(warning = FALSE, message = FALSE) 

# Load the data, make sure the directory is the correct one
load('Count_datasets.RData')

# for running the different type of regressions
library(MASS)
library(pscl)
library(glmmTMB)
library(lme4)

# for fitting distributions to your data
library(fitdistrplus)
library(gamlss)

# for calculating marginal means from model outputs
library(emmeans)

# for checking model diagnostics
library(DHARMa)
library(vcd)

# for displaying the results neatly
library(sjPlot)
library(ggplot2)
library(knitr)

# for changing databases between long and wide
library(reshape2)

2 Count data

So you captured some data and it looks like these example datasets below.

These three datasets are from gambling experiments, where participants were given some money and allowed to place as many bets as they wanted in a simulated roulette or football fixtures. They did not have to bet if they did not want to, and could simply keep the money given to them. Those that decided to bet were paid the outcomes of their bets.

# plot the histogram for example 1
ggplot(example_data_1, aes(x = bet_count)) + geom_histogram(fill="lightblue", color="black", binwidth=1) + theme_bw() + ylab('N participants') + ggtitle('Number of bets placed in roulette experiment 1')

# plot the histogram for example 2
ggplot(example_data_2, aes(x = bet_count)) + geom_histogram(fill="lightblue", color="black", binwidth=1) + theme_bw() + ylab('N participants') + ggtitle('Number of bets placed in roulette experiment 2')

# plot the histogram for example 3
ggplot(example_data_3, aes(x = bet_count)) + geom_histogram(fill="lightblue", color="black", binwidth=1) + theme_bw() + ylab('N participants') + ggtitle('Number of bets placed in football fixtures')

This is all count data. Count data can be characterized as:

  • Discrete (integers) data: 0, 1, 2, 3, …
  • They count something, such as people, instances, events, actions, successes, or occurrences.
  • Cannot be smaller than zero (lower bound = 0) but do not have an upper limit.

There are many distributions used for count data, such as Binomial, Poisson, and Negative Binomial.

2.1 Binomial

For example, there is the binomial distribution, and its special case, Bernoulli, which is used for binary logistic regression. The general binomial distribution has two parameters, N the number of trials, and p the probability of success at each trial. The mean is N \(\times\) p.

Below you can see some plots of different binomial distributions. They don’t seem too similar to our datasets from above.

Ns <- c(10, 20, 30) # set some example for Ns
probs <- c(0.1, 0.25, 0.5) # set some examples for probabilities
Nprobs <- expand.grid(N=Ns, Prob=probs) # creates a matrix of all the Ns x all the probabilities
# extract the point density of the binomial distribution with the Ns and Ps from above
sim_binomial <- do.call(rbind, apply(Nprobs, 1, function(np) data.frame(N=np[[1]], Prob=np[[2]], Count=1:25, Frequency=dbinom(1:25,size=np[1], prob=np[2]))))
sim_binomial$N <- factor(sim_binomial$N) # transform into factor for the legend
sim_binomial$Prob <- factor(sim_binomial$Prob) # transform into factor for the legend

# plot the histograms
ggplot(sim_binomial, aes(x=Count+0.5, y = Frequency, color=N, group=N, fill=N)) + theme_bw() + scale_x_continuous(expand = c(0, 0)) + scale_y_continuous(expand = c(0, 0, 0.1, 0)) + stat_smooth(formula = y ~ x, geom = 'area', method = 'loess', span = 1/5, alpha = 1/2, linewidth=1) + facet_wrap(~Prob, labeller = label_both) + xlab("Count")

2.2 Poisson

The Poisson distribution is probably the most famous of the count distributions. It only has one parameter, lambda, which is the expected mean (and also its standard deviation). It is often used to model the number of people in a queue at any point in time, number of people who attend a class, or the number of daily customers that visit a store.

Below you can see some plots of different Poisson distributions. These are more similar to our datasets, in particular with small lambdas, but with only one parameter they are not very flexible.

lambdas <- c(3, 6, 12) # set some example for lambdas
# extract the point density of the poisson distribution with the lambdas from above
sim_poisson <- do.call(rbind, lapply(lambdas, function(l) data.frame(Lambda=l, Count=1:25, Frequency=dpois(1:25,l))))
sim_poisson$Lambda <- factor(sim_poisson$Lambda)

# plot the histograms
ggplot(sim_poisson, aes(x=Count+0.5, y = Frequency, color=Lambda, group=Lambda, fill=Lambda)) + theme_bw() + scale_x_continuous(expand = c(0, 0)) + scale_y_continuous(expand = c(0, 0, 0.1, 0)) + stat_smooth(formula = y ~ x, geom = 'area', method = 'loess', span = 1/3, alpha = 1/3, linewidth=1) + xlab("Count")

2.3 Negative Binomial

Another one is the Negative Binomial distribution, which is more flexible than Poisson with two parameters, mu, which is the expected mean, and size which is a measure of dispersion (similar to deviation).

Below you can see some plots of different Negative Binomial distributions. These are the most similar to our distributions.

Negative Binomial is probably the most useful distribution for psychological experimental data.

mus <- c(3, 6, 12) # set some example for mus
sizes <- c(3, 6, 12) # set some example for sizes
musizes = expand.grid(mu=mus, size=sizes) # creates a matrix of all the mus x all the sizes
# extract the point density of the negative binomial distribution with the mus and sizes from above
sim_nbinom <- do.call(rbind, apply(musizes, 1, function(ms) data.frame(Mu=ms[[1]], Size=ms[[2]], Count=1:30, Frequency=dnbinom(1:30,size=ms[[2]], mu=ms[[1]]))))
sim_nbinom$Mu <- factor(sim_nbinom$Mu) # transform into factor for the legend
sim_nbinom$Size <- factor(sim_nbinom$Size) # transform into factor for the legend

# plot the histograms
ggplot(sim_nbinom, aes(x=Count+0.5, y = Frequency, color=Mu, group=Mu, fill=Mu)) + theme_bw() + scale_x_continuous(expand = c(0, 0)) + scale_y_continuous(expand = c(0, 0, 0.1, 0)) + stat_smooth(formula = y ~ x, geom = 'area', method = 'loess', span = 1/3, alpha = 1/3, linewidth=1) + facet_wrap(~Size, labeller = label_both) + xlab("Count")

3 Example: Stock trading

Let’s look at one particular example: trade_data. This study was about stock trading behaviour, and participants were allowed to trade stocks (buy or sell) as they wanted in a simulated trading task.

Crucially, the data counts how many trades participants made during the task. Participants did not need to trade at all, so the minimum number of trades was zero. There was no upper limit, participants could make as many trades as they wanted.

# show histogram of trade_count
ggplot(trade_data, aes(x = trade_count)) + geom_histogram(fill="lightblue", color="black", binwidth=2) + theme_bw() + ggtitle("Number of trades made (buy + sell combined)")

We can see the range and quantiles of trades in the table below.

# quantiles of trades made
kable(quantile(trade_data$trade_count), col.names = c("quantile", "trade count"))
quantile trade count
0% 0
25% 5
50% 10
75% 18
100% 68

R has a handy function that checks how well a distribution fits against a set distribution, fitdist from package fitdistrplus. It estimates the best fit parameters that most closely fits the data. In the case of a normal distribution, it estimates the mean and standard deviation (sd).

Let’s see how well this fits a normal distribution. Ideally the data should be as close as possible to the theoretical best fit distribution (shown in red). As expected, it’s a very poor fit. We have excess observations in both ends of the distribution.1

# compare trade_count data with a normal distribution
# 'Summary' shows the best fitting mean and sd of a normal distribution against this data, and the loglikelihood measures the deviance between the actual data and the best fitting distribution.
summary(fitdist(trade_data$trade_count, dist="norm"))
## Fitting of the distribution ' norm ' by maximum likelihood 
## Parameters : 
##      estimate Std. Error
## mean 12.82886  0.4506794
## sd   11.00248  0.3186784
## Loglikelihood:  -2274.967   AIC:  4553.935   BIC:  4562.715 
## Correlation matrix:
##              mean           sd
## mean 1.000000e+00 6.857721e-07
## sd   6.857721e-07 1.000000e+00
# 'Plot' is used to create the plot shown below.
plot(fitdist(trade_data$trade_count, dist="norm"))

3.1 Transformations

Because the data seems skewed, you might be tempted to analyse its log. However, because this is discrete count data, transforming it will not fix the problem. If you log the data, it will be closer to, but still not exactly, a normal distribution. Transformations tend to work better with continuous data.

There’s an additional problem that count data often contains zeros, and the log of zero is undefined. So if we want to try the log of this data, we need to add +1 (or any other positive amount) before calculating the log.

# log trade_count + 1
ggplot(trade_data, aes(x = log(trade_count+1))) + geom_histogram(fill="lightblue", color="black", binwidth=1) + theme_bw()

The log of the trade count data + 1 is closer to a normal distribution, but still problematic - there is still an excess of observations at the lower end, and missing observations below zero.

This is because normal distributions assume that the data is unbounded (with no lower or upper limits) but in this case the raw data has a lower bound of zero, as you cannot have any counts below zero.

Another problem is that Normal distributions assume that the data is continuous, and in this case the raw data is discrete (only integers are allowed: 1, 2, 3, etc).

The fit of the normal distribution here is not too bad, but it would much worse when there are extreme (very large) values - very high counts - which can be very common in count data. Normal distributions struggle with large values, which will skew analysis and interpretation. Count distribution will handle extreme outliers much better.

# compare log of trade_count + 1 data with a normal distribution
plot(fitdist(log(trade_data$trade_count+1), dist="norm"))

Adding +1 is also completely subjective and arbitrary, we could arguably add +0.1 or +10. The results are quite different and it also changes how well the new log data fits a theoretical normal distribution, and will impact the results and interpretation of any analysis we might run on this data. And no matter what we do, we always have a problem with the excess observations at the lower end.

Unless you are pre-registering how much you will add before the log (and have a good theoretical reason for this), this transformation can easily lead to p-hacking.

# testing other logs
plot(fitdist(log(trade_data$trade_count+.1), dist="norm"))

plot(fitdist(log(trade_data$trade_count+10), dist="norm"))

Therefore we should never log count data when it’s this close to zero.2

3.2 Ordinary linear regression applied to count data

We could try to analyse this trade count data (the raw data, not the log) anyway with a ordinary (general)3 linear regression to see what we would get.

The ordinary linear regression model will fit the count data. But is it correct?

Here we have two predictors: the experimental condition exp_cond (there were two: low-risk and high-risk, with a zero sum contrast -1 and +1) and participant’s age. Age has been centered as age_c.

In this case, all predictors are significant.

contrasts(trade_data$exp_cond) <- contr.sum(2) # zero-sum contrast code
trade_data$age_c <- scale(trade_data$age, scale=F) # centre the age

trade.m1.lm <- lm(trade_count ~ exp_cond * age_c, data=trade_data) # run the linear regression
tab_model(trade.m1.lm, show.se = TRUE, collapse.se = TRUE, show.loglik = TRUE) # show the result in a nice table
  trade count
Predictors Estimates CI p
(Intercept) 12.77
(0.45)
11.90 – 13.64 <0.001
exp cond [1] 1.53
(0.45)
0.66 – 2.41 0.001
age c -0.08
(0.04)
-0.15 – -0.01 0.025
exp cond [1] × age c -0.07
(0.04)
-0.14 – -0.00 0.046
Observations 596
R2 / R2 adjusted 0.034 / 0.029
log-Likelihood -2264.606


What kind of predictions does this model make? We can create and average 100 random simulated datasets from the model, using the fitted parameters plus random noise.

First of all, some of these predictions are negative, which is simply not possible with count data. Participants could not make fewer than zero (negative) trades. This is a common problem with bounded data or censored data. Normal distributions (which we assume for an ordinary linear regression) are not bounded, and will try to predict negative data.

And it definitely does not match the shape of the original dataset. This clearly shows how an ordinary linear regression predicts normally distributed data.

pred.samples <- 1:100 # how many predictions do we want?
# simulate the predictions
pred.count <- data.frame(data='Prediction (Ordinary Linear Regression)', trade_count=as.vector(do.call(rbind, sapply(pred.samples, function(x){simulate(trade.m1.lm)}))))
# bind the predictions with the raw data in a single data frame for plotting
pred.count <- rbind(pred.count, data.frame(data='Original Data', trade_count=trade_data$trade_count))

# show a histogram of the simulated outcomes next to the original raw data
ggplot(data=pred.count, aes(trade_count, group=data)) + geom_histogram(aes(y=after_stat(density)), fill="lightblue", color = "black", binwidth=2) + theme_bw() + xlab("Trade Count") + ylab("% of participants") + facet_wrap(~data)

3.2.1 Assumptions

There are packages in R that are very useful for checking the assumptions of a model and how well a model fits a dataset, such as DHARMa (which we use here) and performance. A good-fitting model should not have anything red highlighted on this output.

The output shows how this ordinary linear model has several deviations from the assumptions.

On the left we see that the residuals deviate from a normal distribution (the black dots should be on the red line).

On the right we see that the residuals are not evenly spread across the predictions, therefore there is heteroskedasticity (the solid lines - which show the 25%, 50%, and 75% quartiles - should be horizontal, showing that the dispersion of residuals is flat across the levels of predictions).

# the way DHARMa works is that first we simulate the residuals, and save it
sim.m1.lm <- simulateResiduals(fittedModel = trade.m1.lm)
# then we plot it
plot(sim.m1.lm, title="Residual diagnostics for linear model")

3.2.2 Transformation (log)

If you used the log of the data, it would fit better, and all the predictors are still significant. However, there are still some red deviations highlighted.

The amount that you add to the log (+1 or +.01) would impact the results, coefficients, effect sizes, and inferences. If you add 0.1 instead of 1, the interaction is no longer significant.

Log data also would never return a trade count of zero (it would tend to zero but never be zero), which is not ideal as we clearly have zero counts in the dataset.

trade.m1.lm.log01 <- lm(log(trade_count+.1) ~ exp_cond * age_c, data=trade_data) # run the log regression
trade.m1.lm.log1 <- lm(log(trade_count+1) ~ exp_cond * age_c, data=trade_data) # run the log regression
trade.m1.lm.log10 <- lm(log(trade_count+10) ~ exp_cond * age_c, data=trade_data) # run the log regression

tab_model(trade.m1.lm.log01, trade.m1.lm.log1, trade.m1.lm.log10, show.se = TRUE, collapse.se = TRUE, show.loglik = TRUE)
  log(trade count+0.1) log(trade count+1) log(trade count+10)
Predictors Estimates CI p Estimates CI p Estimates CI p
(Intercept) 2.07
(0.05)
1.97 – 2.17 <0.001 2.28
(0.04)
2.21 – 2.36 <0.001 3.03
(0.02)
2.99 – 3.06 <0.001
exp cond [1] 0.13
(0.05)
0.03 – 0.23 0.012 0.11
(0.04)
0.04 – 0.18 0.003 0.06
(0.02)
0.02 – 0.09 0.001
age c -0.01
(0.00)
-0.02 – -0.00 0.023 -0.01
(0.00)
-0.01 – -0.00 0.029 -0.00
(0.00)
-0.01 – -0.00 0.032
exp cond [1] × age c -0.01
(0.00)
-0.02 – 0.00 0.052 -0.01
(0.00)
-0.01 – -0.00 0.039 -0.00
(0.00)
-0.01 – -0.00 0.038
Observations 596 596 596
R2 / R2 adjusted 0.025 / 0.020 0.029 / 0.024 0.033 / 0.028
log-Likelihood -971.297 -770.473 -336.274
sim.m1.lm.log01 <- simulateResiduals(fittedModel = trade.m1.lm.log01) # runs the DHARMa output
plot(sim.m1.lm.log01, title="Residual diagnostics for linear model")

There are better ways to analyse this data.

3.3 Count distributions: Poisson and Negative Binomial

We can check if this data fits a count distribution, in accordance with the underlying counting nature of the data - counting the number of trades made.

We can try to fit our data to a Poisson distribution. However it does not fit the data particularly well - the predictions (in red) peak later than the actual data peaks. The red lines do not match the black lines and dots.

The mean of this Poisson distribution is also the same mean as the normal distribution previously fitted to this data. However, the Poisson distribution does not predict negative outcomes (as the normal distribution did).

# to fit against a poisson distribution use 'pois'
summary(fitdist(trade_data$trade_count, dist="pois"))
## Fitting of the distribution ' pois ' by maximum likelihood 
## Parameters : 
##        estimate Std. Error
## lambda 12.82886  0.1467138
## Loglikelihood:  -3744.46   AIC:  7490.919   BIC:  7495.31
plot(fitdist(trade_data$trade_count, dist="pois"))

We can try to fit a Negative Binomial distribution. This is a much better fit, even though it does not capture the peaks as well, it is the best fit so far.

In most experimental conditions, Negative Binomial tends to fit data better, because it is more flexible (with two parameters instead of Poisson’s single parameter).

Once again, the mean of the distribution is the same. But because the negatibe binomial has the extra size (dispersion) parameters, it fits the data better.

# to fit against a negative binomial distribution use 'nbinom'
summary(fitdist(trade_data$trade_count, dist="nbinom"))
## Fitting of the distribution ' nbinom ' by maximum likelihood 
## Parameters : 
##      estimate Std. Error
## size  1.49464 0.09590306
## mu   12.82693 0.45404943
## Loglikelihood:  -2120.557   AIC:  4245.115   BIC:  4253.895 
## Correlation matrix:
##              size           mu
## size 1.0000000000 0.0002446197
## mu   0.0002446197 1.0000000000
plot(fitdist(trade_data$trade_count, dist="nbinom"))

In addition to visual comparison of the outputs from this function, you could also compare the Log-Likelihoods of the outputs from the function - the closer to zero the better. The Log-Likelihood of the negative binomial distribution is much closer to zero.4

Another tool for identifying your type of distribution is this Ord plot named after J.K. Ord. This function is implemented in the package vcd. An upward trending line shows a negative binomial distribution. For more information on interpreting this plot, there are examples in the original paper by Ord (1967).

Ord_plot(trade_data$trade_count)

3.4 Negative Binomial model

There are several packages and functions in R for fitting count data models.

For Poisson, the most common is probably the glm function from the base package, which you might have used in the past to fit logistic regressions. You simply need to specify family = 'poisson'.

For Negative Binomial, you can use the function glm.nb from the package MASS.

As our data more closely resembles a negative binomial, we will fit a negative binomial generalized linear regression to our data, with the same predictors.

Exponentiating the coefficients in count regressions (\(e^\beta\)) gives us Incident Rate Ratios which has a similar interpretation to Odds Ratios (ORs).

  • IRRs above 1 indicate positive correlation - higher incidences when the predictor goes up;
  • IRRs below 1 indicate negative correlation - higher incidences when the predictor goes down.

IRRs are multiplicative (not additive) again similar to ORs.

With a negative binomial regression, the interaction is no longer significant.

trade.m2.nb <- glm.nb(trade_count ~ exp_cond * age_c, data=trade_data) # run the negative binomial regression
tab_model(trade.m2.nb, show.se = TRUE, collapse.se = TRUE, show.loglik = TRUE) # show the results in a nice table
  trade count
Predictors Incidence Rate Ratios CI p
(Intercept) 12.62
(0.44)
11.79 – 13.53 <0.001
exp cond [1] 1.12
(0.04)
1.05 – 1.20 0.001
age c 0.99
(0.00)
0.99 – 1.00 0.034
exp cond [1] × age c 0.99
(0.00)
0.99 – 1.00 0.065
Observations 596
R2 Nagelkerke 0.047
log-Likelihood -2110.859


Again we simulate a series of 100 predicted datasets from our model and compare it to the original dataset. It’s a much better fit - there are no predictions below 0. The shape of the distribution of predictions closely matches the original data distribution.

pred.samples <- 1:100 # how many predictions do we want?
# simulate the predictions
pred.count <- data.frame(data='Prediction (Neg Bin)', trade_count=as.vector(do.call(rbind, sapply(pred.samples, function(x){simulate(trade.m2.nb)}))))
# bind the predictions with the raw data in a single data frame for plotting
pred.count <- rbind(pred.count, data.frame(data='Original Data', trade_count=trade_data$trade_count))

# show a histogram of the simulated outcomes next to the original raw data
ggplot(data=pred.count, aes(trade_count, group=data)) + geom_histogram(aes(y=after_stat(density)), fill="lightblue", color = "black", binwidth=2) + theme_bw() + xlab("Trade Count") + ylab("% of participants") + facet_wrap(~data) + coord_cartesian(xlim=c(0,100))

If we look at the two models side-by-side, we see that we would make different inferences from each model with regards to the interactions. The coefficients are not directly comparable. But we can ask R to give us some estimated marginal means.

tab_model(trade.m1.lm, trade.m2.nb, show.se = TRUE, collapse.se = TRUE, show.loglik = TRUE, dv.labels=c("general linear model", "negative binomial model")) # show the two models side by side
  general linear model negative binomial model
Predictors Estimates CI p Incidence Rate Ratios CI p
(Intercept) 12.77
(0.45)
11.90 – 13.64 <0.001 12.62
(0.44)
11.79 – 13.53 <0.001
exp cond [1] 1.53
(0.45)
0.66 – 2.41 0.001 1.12
(0.04)
1.05 – 1.20 0.001
age c -0.08
(0.04)
-0.15 – -0.01 0.025 0.99
(0.00)
0.99 – 1.00 0.034
exp cond [1] × age c -0.07
(0.04)
-0.14 – -0.00 0.046 0.99
(0.00)
0.99 – 1.00 0.065
Observations 596 596
R2 / R2 adjusted 0.034 / 0.029 0.047
log-Likelihood -2264.606 -2110.859


We can compare the predictions that the model makes of the means of each experimental condition using the functions emmeans and emmip.

They are close in this case - general linear regressions are very good at finding the means of distribution - the differences in this case are in the standard errors. This translates into differences in the effect sizes and interpretation of the results.

If there were extreme (very large) values, as we will see later, the results would not have been so similar.

# get the means for each experimental condition, for each model
emmeans.lm <- summary(emmeans(trade.m1.lm, ~exp_cond, type="r"))
emmeans.lm$model <- "lm" # name the models for the plot below
emmeans.nb <- summary(emmeans(trade.m2.nb, ~exp_cond, type="r"))
emmeans.nb$model <- "nb"
colnames(emmeans.nb) <- colnames(emmeans.lm) # we need the column names to be the same for the rbind below

# plot the results
ggplot(rbind(emmeans.lm, emmeans.nb), aes(x=exp_cond, y=emmean, ymin=lower.CL, ymax=upper.CL, group=model, color=model, fill=model)) + geom_bar(stat="identity", width=0.5, position=position_dodge(width=0.55), color="black") + geom_errorbar(width=0.2, position=position_dodge(width=0.55), color="black") + theme_bw() + xlab("experimental condition") + ylab("estimated marginal means")

# get the slopes of age for each experimental condition, for each model
emmip.lm <- emmip(trade.m1.lm, ~age_c|exp_cond, at=list(age_c=c(-20:40)), CIs=T, plotit = FALSE)
emmip.lm$model <- "lm"
emmip.nb <- emmip(trade.m2.nb, ~age_c|exp_cond, at=list(age_c=c(-20:40)), CIs=T, type="r", plotit = FALSE)
emmip.nb$model <- "nb"
colnames(emmip.nb) <- colnames(emmip.lm)

ggplot(rbind(emmip.lm, emmip.nb), aes(x=age_c, y=yvar, ymin=LCL, ymax=UCL, group=model, color=model, fill=model)) + geom_line(position=position_dodge(width=1), linewidth=1) + geom_ribbon(position=position_dodge(width=1), alpha=0.2) + theme_bw() + xlab("centered Age in years") + ylab("estimated marginal means") + facet_wrap(~exp_cond)

We can look at the diagnostics again, and we see no deviations with the negative binomial model (nothing is highlighted in red, apart from a couple of potential outliers).

sim.m2.nb <- simulateResiduals(fittedModel = trade.m2.nb)
plot(sim.m2.nb, title="Residual diagnostics for negative binomial model")

4 Repeated-measures (Mixed effects) Example

In this new example, the data (sampling_data) comes from a decision-making study.

Before making a financially-consequential decision that affected their bonus, participants could freely sample from the available options without any costs or consequences, to determine which option they preferred.

This is again count data, as it counts the number of samples taken by each participant before each choice. They could sample for as long as they liked. Each participant made 18 choices, and they could sample a different number of times for each choice.

# plot the histogram of sample count
ggplot(sampling_data, aes(x = sample_count)) + geom_histogram(fill="lightblue", color="black", binwidth=1) + theme_bw() + ggtitle("Number of samples taken before each choice")

It’s not immediately obvious, but the peak of the data (and also the lowest count of samples) is not zero, but instead, it is 2. That is because participants were required to sample at least twice for each choice they made, although there was no upper bound.

With count data it’s always best to have the data starting from zero if there was some structural characteristic that limited the minimum count. We create a new variable, excess sample count, which counts how many samples participants took in excess of the minimum. This was calculated by subtracting 2 from each sample count.

Now the data starts from zero, which will help it more closely match a count distribution.

# subtract two to create excess sample count
sampling_data$excess_sample_count <- sampling_data$sample_count - 2
# plot the new excess sample count
ggplot(sampling_data, aes(x = excess_sample_count)) + geom_histogram(fill="lightblue", color="black", binwidth=1) + theme_bw() + ggtitle("Number of excess samples taken before each choice")

Again this data is clearly not normally distributed, but instead it seems to follow a negative binomial distribution.

# compare trade_count data with a normal distribution
summary(fitdist(sampling_data$excess_sample_count,dist="norm"))
## Fitting of the distribution ' norm ' by maximum likelihood 
## Parameters : 
##      estimate Std. Error
## mean 5.620428  0.1414394
## sd   8.750989  0.1000129
## Loglikelihood:  -13735.27   AIC:  27474.53   BIC:  27487.03 
## Correlation matrix:
##              mean           sd
## mean 1.000000e+00 1.875147e-06
## sd   1.875147e-06 1.000000e+00
plot(fitdist(sampling_data$excess_sample_count,dist="norm"))

# compare trade_count data with a negative binomial distribution
summary(fitdist(sampling_data$excess_sample_count,dist="nbinom"))
## Fitting of the distribution ' nbinom ' by maximum likelihood 
## Parameters : 
##       estimate Std. Error
## size 0.3879878 0.01075305
## mu   5.6212534 0.15083192
## Loglikelihood:  -10123.22   AIC:  20250.45   BIC:  20262.95 
## Correlation matrix:
##               size            mu
## size  1.0000000000 -0.0001418066
## mu   -0.0001418066  1.0000000000
plot(fitdist(sampling_data$excess_sample_count,dist="nbinom"))

Because this was within-subjects (repeated measures) with each participant making 18 choices (and each choice associated with its own sampling) we need to fit a mixed effects model to this data.

The best package for this is glmmTMB which is flexible enough to allow for several different distributions, including negative binomial.5

There were two experimental conditions expcond, and we also add two continuous predictors: age and choice number (both centered). Choice number was the sequential choice they made, from 1 to 18, to see if sampling behaviour changed as they made more choices and the task progressed. There is a random intercept for each participant. The random effects are specified using the same format as the more traditional lmer or glmer functions.

We compare the outputs with an ordinary linear regression. The conclusions would have been different. We can see that one of the predictors (experimental condition) is significant with the negative binomial regression, but not with the ordinary linear regression.

contrasts(sampling_data$expcond) <- contr.sum(2) # zero-sum contrast code
sampling_data$choice_number_c <- scale(sampling_data$choice_number, scale=FALSE) # centre the choice number
sampling_data$age_c <- scale(sampling_data$age, scale=FALSE) # centre the age

# run an ordinary mixed effects linear regression
sample.m1.lm <- lmer(excess_sample_count ~ expcond + age_c + choice_number_c + (1|pptid), data=sampling_data)

# run a negative binomial mixed effects regression
sample.m2.nb <- glmmTMB(excess_sample_count ~ expcond + age_c + choice_number_c + (1|pptid), data=sampling_data, family="nbinom1")

tab_model(sample.m1.lm, sample.m2.nb, show.se = TRUE, collapse.se = TRUE, show.loglik = TRUE, show.icc=FALSE, show.re.var = FALSE, dv.labels=c("ordinary mixed-effects linear model", "negative binomial mixed-effects model"))
  ordinary mixed-effects linear model negative binomial mixed-effects model
Predictors Estimates CI p Incidence Rate Ratios CI p
(Intercept) 5.64
(0.44)
4.78 – 6.51 <0.001 1.78
(0.19)
1.44 – 2.20 <0.001
expcond1 -0.12
(0.07)
-0.25 – 0.01 0.068 0.98
(0.01)
0.96 – 1.00 0.049
age c -0.04
(0.04)
-0.11 – 0.03 0.322 0.99
(0.01)
0.98 – 1.01 0.515
choice number c -0.10
(0.01)
-0.13 – -0.08 <0.001 0.98
(0.00)
0.98 – 0.99 <0.001
N 316 pptid 316 pptid
Observations 3792 3792
Marginal R2 / Conditional R2 0.006 / 0.780 0.003 / 0.849
log-Likelihood -11354.371 -7675.980

When we compare the model outputs, we see a large difference in the estimate of the sample count. This is because the ordinary linear model is heavily influenced by some very large sample counts, which push the average up.

Because of these extreme values, the estimated marginal means from count models can be significantly different from the actual means of the underlying data, and will be closer to the median, which was 2. In comparison, the estimated marginal means from general linear models will be closer to the mean, which was 5.62.

# emmeans is used to create estimated marginal means for each experimental condition
# we calculate these for each model
emmeans.lm <- summary(emmeans(sample.m1.lm, ~expcond, type="r", lmer.df = "asymp"))
emmeans.lm$model <- "lm"
emmeans.nb <- summary(emmeans(sample.m2.nb, ~expcond, type="r", lmer.df = "asymp"))
emmeans.nb$model <- "nb"
colnames(emmeans.nb) <- colnames(emmeans.lm)

ggplot(rbind(emmeans.lm, emmeans.nb), aes(x=expcond, y=emmean, ymin=asymp.LCL, ymax=asymp.UCL, group=model, color=model, fill=model)) + geom_bar(stat="identity", width=0.5, position=position_dodge(width=0.55), color="black") + geom_errorbar(width=0.2, position=position_dodge(width=0.55), color="black") + theme_bw() + xlab("experimental condition") + ylab("estimated marginal means")

# emmip is used to create regression lines for each predictor
emmip.lm <- emmip(sample.m1.lm, ~age_c, at=list(age_c=c(-20:40)), CIs=T, plotit = FALSE, lmer.df = "asymp")
emmip.lm$model <- "lm"
emmip.nb <- emmip(sample.m2.nb, ~age_c, at=list(age_c=c(-20:40)), CIs=T, type="r", plotit = FALSE, lmer.df = "asymp")
emmip.nb$model <- "nb"

ggplot(rbind(emmip.lm, emmip.nb), aes(x=age_c, y=yvar, ymin=LCL, ymax=UCL, group=model, color=model, fill=model)) + geom_line(position=position_dodge(width=1), linewidth=1) + geom_ribbon(position=position_dodge(width=1), alpha=0.2) + theme_bw() + xlab("Centered age in years") + ylab("estimated marginal means")

emmip.lm <- emmip(sample.m1.lm, ~choice_number_c, at=list(choice_number_c=c(-10:10)), CIs=T, plotit = FALSE, lmer.df = "asymp")
emmip.lm$model <- "lm"
emmip.nb <- emmip(sample.m2.nb, ~choice_number_c, at=list(choice_number_c=c(-10:10)), CIs=T, type="r", plotit = FALSE, lmer.df = "asymp")
emmip.nb$model <- "nb"

ggplot(rbind(emmip.lm, emmip.nb), aes(x=choice_number_c, y=yvar, ymin=LCL, ymax=UCL, group=model, color=model, fill=model)) + geom_line(position=position_dodge(width=1), linewidth=1) + geom_ribbon(position=position_dodge(width=1), alpha=0.2) + theme_bw() + xlab("Centered Choice number") + ylab("estimated marginal means") 

5 Zero inflation

In this final example, the data (zi_data) comes from a study on gambling.6

Participants were given £3 and allowed to place as many bets as they wanted across four simulated slot machines. We measured how often participants switched between the different machines. They did not have to switch at all, and could switch as many times as they wanted. We also measured the participants’ PGSI, which is the problem gambling severity index: higher scores in the PGSI correlate with more disordered gambling and more harm from gambling.

What we have here is what we call zero-inflated data, or data which has more zero observations than we would expect. These are the participants that did not switch between slot machines and placed all their bets with a single machine.

Zero-inflation count data often has two peaks, one at zero, then a dip in responses, and another peak later on, as we see here. If there is a single peak, there might not be zero inflation at all.

# plot the histogram of number of switches
ggplot(zi_data, aes(x = switch_count)) + geom_histogram(fill="lightblue", color="black", binwidth=1) + theme_bw() + ggtitle("Number of switches between the different slot machines")

In fact, 30% of participants did not switch at all.

When we compare this data to a normal distribution and to a negative binomial distribution, we can see the excess zero responses. The negative binomial distribution fits the data slightly better but it’s still quite different, because of the excess zeros.

# compare number of switches data with a normal distribution
summary(fitdist(zi_data$switch_count,dist="norm"))
## Fitting of the distribution ' norm ' by maximum likelihood 
## Parameters : 
##      estimate Std. Error
## mean 2.768229  0.1576408
## sd   3.089116  0.1114688
## Loglikelihood:  -977.9802   AIC:  1959.96   BIC:  1967.862 
## Correlation matrix:
##               mean            sd
## mean  1.000000e+00 -2.197479e-08
## sd   -2.197479e-08  1.000000e+00
plot(fitdist(zi_data$switch_count,dist="norm"))

# compare number of switches data with a negative binomial distribution
fit_output_nb <- fitdist(zi_data$switch_count,dist="nbinom")
summary(fit_output_nb)
## Fitting of the distribution ' nbinom ' by maximum likelihood 
## Parameters : 
##       estimate Std. Error
## size 0.9847249  0.1127879
## mu   2.7679689  0.1657271
## Loglikelihood:  -837.2336   AIC:  1678.467   BIC:  1686.368 
## Correlation matrix:
##              size           mu
## size 1.0000000000 0.0001327684
## mu   0.0001327684 1.0000000000
plot(fit_output_nb)

What we have here is a count distribution with a relatively high mean – if you ignore the excessive zeros.

The fit function in R tries to compensate by fitting a distribution with a smaller mean (mu) and larger variance (size) to account for the excessive zeros. However, it’s not a very good fit. Accounting for the excessive zeros and allowing for a larger mean would fit this data better.

5.1 Testing for zero-inflation

We can test if the data has zero inflation in a few different ways.

5.1.1 Fit a zero-inflated distribution

First, we can try the fitdist function by specifying the ZINBI distribution from package gamlss.

However, this is always clunky and likes to throw many warnings and errors. But in this case, the AIC is smaller and it seems to fit better than a regular negative binomial distribution. You can also see how it now estimated a higher mean (mu).

Nu is the probability of responding with a zero, or the zero-inflation parameter, which was 0.208. The zero-inflated distribution assumes that there were around 21% excessive zeros.

# To run a ZINBI fit you need to specify starting points for the different parameters, as well as bounds for the parameters. Sometimes you need to try different starting parameters until it fits.
fit_output_zinbi <- fitdist(zi_data$switch_count, dist="ZINBI", start=list(mu=1, sigma=1, nu=0.1), upper=c(Inf, Inf, 1), lower=c(0,0,0), discrete=TRUE)
summary(fit_output_zinbi)
## Fitting of the distribution ' ZINBI ' by maximum likelihood 
## Parameters : 
##        estimate Std. Error
## mu    3.4937468 0.04500344
## sigma 0.4335047 0.08676652
## nu    0.2076853 0.05081428
## Loglikelihood:  -828.697   AIC:  1663.394   BIC:  1675.246 
## Correlation matrix:
##               mu      sigma         nu
## mu     1.0000000 -0.5240354  0.2766188
## sigma -0.5240354  1.0000000 -0.2524488
## nu     0.2766188 -0.2524488  1.0000000
plot(fit_output_zinbi)

We can also plot a typical negative binomial distribution (without zero inflation) over our data to see how the zero inflation works. The plot below shows that without the zero inflation part, the negative binomial fits the second peak of the data, with a higher average (mu) and smaller variance (sigma).

The negative binomial predicts around 10% are zeros, but the actual observed zero count was 30%. The difference in zeros is accounted by the 21% excessive zeros.

# we compare the zero inflated data to a negative binomial model, without zero inflation, but with the mu and sigma from the zero inflation model.
# this shows us the zero inflation component
plotdist(zi_data$switch_count, dist="NBI", para=list(sigma=coef(fit_output_zinbi)['sigma'], mu=coef(fit_output_zinbi)['mu']), discrete=T)

5.1.2 Run a negative binomial and test for zero inflation

Alternatively, we can run a negative binomial regression (without zero inflation) using the same glm.nb function as before, and use the testZeroInflation function from DHARMa.

Significant values above 1 mean that there is zero inflation. Ideally the red line in the plot below should be in the middle of the data distribution. Because the data is to the left of the red line, and the statistic is significant, this test confirms that there is zero inflation.7

zi.m2.nb <- glm.nb(switch_count ~ pgsi, data=zi_data) # run a standard negative binomial regression
testZeroInflation(zi.m2.nb) # test for zero inflation

## 
##  DHARMa zero-inflation test via comparison to expected zeros with
##  simulation under H0 = fitted model
## 
## data:  simulationOutput
## ratioObsSim = 1.1837, p-value = 0.024
## alternative hypothesis: two.sided

5.1.3 Compare a negative binomial model with a zero-inflated model

Finally, another way to test is to run a Zero-Inflation Negative Binomial (ZINB) \(-\) or ZIP for Zero-Inflated Poisson \(-\) regression and compare the two models. There are several packages that allow for zero-inflated models, but one of the simplest is the function zeroinfl from package pscl.8

Zero-inflation models assume that two different and separate processes generate the data.

  • Zero inflation: This process is responsible for excessive zero responses (in other words, is the response going to be an excessive zero, more than expected?). This is commonly estimated using a binary logistic regression. In this example, did the participant decide to switch between slot machines at all?

  • Conditional regression: Then, assuming that the model does not classify this response as an excessive zero, a conditional negative binomial regression is run (conditional that the result is not an excessive zero) to determine the actual response, in this case, the number of switches between different machines.

It is important to note that the conditional regression part of the model can also return a count of zero, and the zero-inflation part is only responsible for excessive zeros (or inflation of zeros). If you believe this cannot be the case, then Hurdle models do not allow for zeros in the conditional part, only the zero-inflation part generates zeros. You can think of this as a hurdle that the participant has to pass (e.g., is the response a zero or not).

NB: The packages that allow for zero-inflation often require the zero-inflation part of the model to be specified separately. In the case of the zeroinfl function, if you don’t specify anything, the function assumes that the zero inflation part of the model is the same as the conditional part of the mode, ie, it has the same predictors. This is the simplest and easiest approach.9

We can then compare the two models to see if the zero-inflated model fits the data better. The model with the lowest AIC is considered to fit the data better, and that’s the case here for the ZINB model in comparison to the NB model (without zero-inflation).10

# run a zero-inflation negative binomial regression
zi.m3.zinb <- zeroinfl(switch_count ~ pgsi, data=zi_data, dist="negbin")
# compare the AICs of the two models. Lower AIC is better.
AIC(zi.m2.nb, zi.m3.zinb)
##            df      AIC
## zi.m2.nb    3 1638.248
## zi.m3.zinb  5 1565.833

The output from zero-inflation models follow the two components described above: The conditional and the zero-inflation components.

  • The zero-inflation component outputs Odds-Ratios (OR) based on a binary logistic regression (1 = excess zero). It measures the likelihood of a participant responding with an excessive zero response or not. Note that this part of the model is positively correlated with zero responses, therefore negatively correlated with actual bets placed Higher ORs here indicate a higher propensity to respond with a zero (and therefore a lower number of switches overall).

    • In this example, the PGSI predictor was significant, with an OR below 1. Participants with higher PGSI scores were less likely to not switch at all.


  • The conditional component Incidence Rate Ratios (IRRs) based on a negative binomial regression. IRRs above 1 indicate a higher switch count and vice-versa.

    • In this example, PGSI scores significantly influenced how often a participant switched between slot machines. The OR was significantly above 1, which means that with higher PGSI scores, participants switched more times.
tab_model(zi.m3.zinb) # nice table of the results of the ZINB model
  switch count
Predictors Incidence Rate Ratios CI p
Count Model
(Intercept) 3.17 2.73 – 3.69 <0.001
pgsi 1.07 1.02 – 1.11 0.004
Zero-Inflated Model
(Intercept) 1.09 0.75 – 1.58 0.641
pgsi 0.31 0.22 – 0.45 <0.001
Observations 384
R2 / R2 adjusted 0.525 / 0.523

We can compare model predictions to see how the zero-inflated model predicts many more zeros and more closely matches the original dataset. We see that the zero-inflated model fits the data better.

pred.samples <- 1:100 # how many predictions do we want?
# simulate the predictions for negative binomial
pred.count.nb <- data.frame(data='Prediction (Negative Binomial)', switch_count=as.vector(do.call(rbind, sapply(pred.samples, function(x){simulate(zi.m2.nb)}))))
# simulate the predictions for the ZINB model
probabilities <- predict(zi.m3.zinb, type="prob") # get the probabilities
pred.count.zi <- data.frame(data='Prediction (Zero-Inflated Neg Bin)', switch_count=as.vector(sapply(pred.samples, function(s){apply(probabilities, 1, function(p) sample(1:ncol(probabilities), 1, prob=p))})))

# bind the predictions with the raw data in a single data frame for plotting
pred.count <- rbind(pred.count.nb, pred.count.zi, data.frame(data='Original Data', switch_count=zi_data$switch_count))

# show a histogram of the simulated outcomes next to the original raw data
ggplot(data=pred.count, aes(switch_count, group=data)) + geom_histogram(aes(y=after_stat(density)), fill="lightblue", color="black", binwidth=1) + theme_bw() + xlab("Switch Count") + ylab("% of participants") + facet_wrap(~data) + coord_cartesian(xlim=c(0,20))

When interpreting the results of a zero-inflated model, you need to consider both components separately, the zero-inflated and the conditional components. Not only the coefficients, predictors, and their significance, but also their estimated marginal means.

You can do that by setting the component of the regression that you want to evaluate at each time, often using component=... or mode=.... These can be identified using zero or zi for zero-inflation and count or cond for the conditional component. Each function requires slightly different parameters, so check the help.

If you don’t set the component for your analyses, often you will only be shown the conditional component by default. Be careful as this is only part of the picture.

# emmip is used to calculate the slope of the predictors against the DV
# one is needed for each component of the zero-inflation regression.
emout.zero <- emmip(zi.m3.zinb, ~pgsi, at=list(pgsi=0:20), type="r", CIs=T, mode="zero", plotit=FALSE) 
emout.zero$component <- "zero inflation"
emout.cond <- emmip(zi.m3.zinb, ~pgsi, at=list(pgsi=0:20), type="r", CIs=T, mode="count", plotit=FALSE)
emout.cond$component <- "conditional"

ggplot(rbind(emout.zero, emout.cond), aes(x=pgsi, y=yvar, ymin=LCL, ymax=UCL)) + geom_line(linewidth=1) + geom_ribbon(alpha=0.2) + theme_bw() + xlab("pgsi") + ylab("Count of Switches / Probability of Excess Zero") + facet_wrap(~component, scales="free")

6 Postscript: Don’t like to use R?

If you don’t like using R, Jamovi has an implementation for Poisson and Negative Binomial regressions including mixed effects, while JASP supports only Poisson distributions (also mixed effects). Neither seems to support zero inflation at the moment.

7 Further reading

There are several useful online guides on count data analysis. Here are some that I have found:

Regression Models for Count Data in R using the pscl package

UCLA’s Advanced Research Computing Statistical Methods and Data Analytics page on Negative Binomial Regression … and Zero Inflated Negative Binomial Regression

The Poisson distribution: From basic probability theory to regression models

Count data and GLMs: choosing among Poisson, negative binomial, and zero-inflated models

GLM with zero-inflated data

Zero-inflated Models Using DHARMa and glmmTMB

8 Other (similar) types of data

Sometimes your data might look like Count data, but it really ain’t.

If your data is ranked, for example, a Likert scale, then you should use an ordinal regression. R has several implementations for ordinal logistic regressions, such as the function polr from the package MASS or the much more flexible clm from the package ordinal. If you need a mixed-effects ordinal logistic regression (for within-subjects repeated-measures data) then there is the function clmm also from ordinal.

If your data has both lower and upper bounds, then it could potentially be percentage or proportional data, and you should consider a beta regression. You can use the function betareg from the same-named package. Note that beta regressions do not accept zeros or ones, so either you need to convert your data to remove the zeros and ones (e.g., see Smithson & Verkuilen, 2006; Verkuilen & Smithson, 2012) or you can use zero-one-inflated-beta regression (ZOIB).


  1. A note on statistical diagnostic tests for distributions and regressions: most tests of normality (Shapiro-Wilk, Kolmogorov-Smirnoff) and tests of diagnostics (Levene’s, White’s) are generally not recommended. This is because with large datasets they are over-sensitive and will highlight even tiny deviations (common in real-life due to noise) as significant. With small datasets they are under-powered to identify real deviations. Visual inspection is often best. For more read: https://towardsdatascience.com/stop-testing-for-normality-dba96bb73f90.↩︎

  2. If the count data is far away enough from zero, then it would be more acceptable to either treat it as a normal distribution (if it looks like one), or try to log the data if it is skewed.↩︎

  3. Note that a general linear regression is the ordinary type which assumes that the data follows a normal distribution, while a generalized linear regression is a more flexible version that accepts different types of distributions, such as binomial (for logistic regression), or poisson and negative binomial (and many others).↩︎

  4. You might have come across Log-Likelihood (LL) before in logistic regressions, often called deviance, and it is the base to calculate pseudo-\(R^2\) measures such as Nagelkerke. LL is great for comparing across model fits, but be careful as certain approaches use different ways to calculate LL which means they might not be directly comparable sometimes.↩︎

  5. Don’t ask me why there are three different negative binomial families in glmmTMB (nbinom1, nbinom2, and nbinom12). The difference between them seem minimal - and mostly philosophical about how to define variance. I’m not sure which one is the “best” or why. I often just use the first one and if it throws me an error I try one of the other ones. Also annoyingly, note that different functions call the distributions different names, such as nbinom vs negbinom and poisson vs pois. Always check the help for each function.↩︎

  6. This data is adapted from a real study, but the actual data has been tweaked slightly to simplify this example. In the actual dataset there was no correlation between switch count and PGSI.↩︎

  7. Significant values below 1 with data significantly to the right of the red line would mean a lack of zeros.↩︎

  8. If your data is repeated measures and you need a mixed effects model, the same function glmmTMB from before allows for zero inflation, all you need to do is specify the zero-inflation component using zi=..., although it can be very slow to fit.↩︎

  9. Some other functions require you to specify a zero-inflated component using some sort of syntax such as zi = .... Please read the help for the specific function you are using. Even for zeroinfl you can specify separate models for each part, with separate predictors, if you believe that the processes generating outcomes from each component are different.↩︎

  10. Again be careful when comparing AIC as not all are comparable, but in this case they are. Also if you want to evaluate if the difference in AIC between the two models is significant or not, there are ways of calculating this, but I did not include it here.↩︎

LS0tCnRpdGxlOiAiRGVhbGluZyB3aXRoIENvdW50IERhdGEiCnN1YnRpdGxlOiAiTkcgRGF0YSBDbHViIgphdXRob3I6ICJMZW9uYXJkbyBDb2hlbiIKZGF0ZTogIjA0IE5vdmVtYmVyIDIwMjQiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OiAKICAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgICBrZWVwX21kOiBmYWxzZQogICAgZW1iZWQtcmVzb3VyY2VzOiB0cnVlCmVkaXRvcl9vcHRpb25zOgogIGNodW5rX291dHB1dF90eXBlOiBjb25zb2xlCi0tLQoKPHN0eWxlPgpkaXYuYmx1ZSB7YmFja2dyb3VuZC1jb2xvcjojZTZmMGZmOyBib3JkZXItcmFkaXVzOiA1cHg7IHBhZGRpbmc6IDIwcHg7IG1hcmdpbjogMjBweH0KPC9zdHlsZT4KCiMgUHJlYW1ibGUKCldlIHN0YXJ0IGJ5IGxvYWRpbmcgdGhlIG5lY2Vzc2FyeSBwYWNrYWdlcyBhbmQgdGhlIC5SRGF0YSBmb3IgdGhlc2UgZXhhbXBsZXMuCgpgYGB7ciBwYWNrYWdlcywgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0V9Cgprbml0cjo6b3B0c19jaHVuayRzZXQod2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UpIAoKIyBMb2FkIHRoZSBkYXRhLCBtYWtlIHN1cmUgdGhlIGRpcmVjdG9yeSBpcyB0aGUgY29ycmVjdCBvbmUKbG9hZCgnQ291bnRfZGF0YXNldHMuUkRhdGEnKQoKIyBmb3IgcnVubmluZyB0aGUgZGlmZmVyZW50IHR5cGUgb2YgcmVncmVzc2lvbnMKbGlicmFyeShNQVNTKQpsaWJyYXJ5KHBzY2wpCmxpYnJhcnkoZ2xtbVRNQikKbGlicmFyeShsbWU0KQoKIyBmb3IgZml0dGluZyBkaXN0cmlidXRpb25zIHRvIHlvdXIgZGF0YQpsaWJyYXJ5KGZpdGRpc3RycGx1cykKbGlicmFyeShnYW1sc3MpCgojIGZvciBjYWxjdWxhdGluZyBtYXJnaW5hbCBtZWFucyBmcm9tIG1vZGVsIG91dHB1dHMKbGlicmFyeShlbW1lYW5zKQoKIyBmb3IgY2hlY2tpbmcgbW9kZWwgZGlhZ25vc3RpY3MKbGlicmFyeShESEFSTWEpCmxpYnJhcnkodmNkKQoKIyBmb3IgZGlzcGxheWluZyB0aGUgcmVzdWx0cyBuZWF0bHkKbGlicmFyeShzalBsb3QpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShrbml0cikKCiMgZm9yIGNoYW5naW5nIGRhdGFiYXNlcyBiZXR3ZWVuIGxvbmcgYW5kIHdpZGUKbGlicmFyeShyZXNoYXBlMikKYGBgCgojIENvdW50IGRhdGEKCjxkaXYgY2xhc3MgPSAiYmx1ZSI+CgpTbyB5b3UgY2FwdHVyZWQgc29tZSBkYXRhIGFuZCBpdCBsb29rcyBsaWtlIHRoZXNlIGV4YW1wbGUgZGF0YXNldHMgYmVsb3cuIAoKPC9kaXY+CgpUaGVzZSB0aHJlZSBkYXRhc2V0cyBhcmUgZnJvbSBnYW1ibGluZyBleHBlcmltZW50cywgd2hlcmUgcGFydGljaXBhbnRzIHdlcmUgZ2l2ZW4gc29tZSBtb25leSBhbmQgYWxsb3dlZCB0byBwbGFjZSBhcyBtYW55IGJldHMgYXMgdGhleSB3YW50ZWQgaW4gYSBzaW11bGF0ZWQgcm91bGV0dGUgb3IgZm9vdGJhbGwgZml4dHVyZXMuIFRoZXkgZGlkIG5vdCBoYXZlIHRvIGJldCBpZiB0aGV5IGRpZCBub3Qgd2FudCB0bywgYW5kIGNvdWxkIHNpbXBseSBrZWVwIHRoZSBtb25leSBnaXZlbiB0byB0aGVtLiBUaG9zZSB0aGF0IGRlY2lkZWQgdG8gYmV0IHdlcmUgcGFpZCB0aGUgb3V0Y29tZXMgb2YgdGhlaXIgYmV0cy4KCmBgYHtyIGxvYWQgZXhhbXBsZXN9CiMgcGxvdCB0aGUgaGlzdG9ncmFtIGZvciBleGFtcGxlIDEKZ2dwbG90KGV4YW1wbGVfZGF0YV8xLCBhZXMoeCA9IGJldF9jb3VudCkpICsgZ2VvbV9oaXN0b2dyYW0oZmlsbD0ibGlnaHRibHVlIiwgY29sb3I9ImJsYWNrIiwgYmlud2lkdGg9MSkgKyB0aGVtZV9idygpICsgeWxhYignTiBwYXJ0aWNpcGFudHMnKSArIGdndGl0bGUoJ051bWJlciBvZiBiZXRzIHBsYWNlZCBpbiByb3VsZXR0ZSBleHBlcmltZW50IDEnKQoKIyBwbG90IHRoZSBoaXN0b2dyYW0gZm9yIGV4YW1wbGUgMgpnZ3Bsb3QoZXhhbXBsZV9kYXRhXzIsIGFlcyh4ID0gYmV0X2NvdW50KSkgKyBnZW9tX2hpc3RvZ3JhbShmaWxsPSJsaWdodGJsdWUiLCBjb2xvcj0iYmxhY2siLCBiaW53aWR0aD0xKSArIHRoZW1lX2J3KCkgKyB5bGFiKCdOIHBhcnRpY2lwYW50cycpICsgZ2d0aXRsZSgnTnVtYmVyIG9mIGJldHMgcGxhY2VkIGluIHJvdWxldHRlIGV4cGVyaW1lbnQgMicpCgojIHBsb3QgdGhlIGhpc3RvZ3JhbSBmb3IgZXhhbXBsZSAzCmdncGxvdChleGFtcGxlX2RhdGFfMywgYWVzKHggPSBiZXRfY291bnQpKSArIGdlb21faGlzdG9ncmFtKGZpbGw9ImxpZ2h0Ymx1ZSIsIGNvbG9yPSJibGFjayIsIGJpbndpZHRoPTEpICsgdGhlbWVfYncoKSArIHlsYWIoJ04gcGFydGljaXBhbnRzJykgKyBnZ3RpdGxlKCdOdW1iZXIgb2YgYmV0cyBwbGFjZWQgaW4gZm9vdGJhbGwgZml4dHVyZXMnKQpgYGAKCjxkaXYgY2xhc3MgPSAiYmx1ZSI+CgpUaGlzIGlzIGFsbCAqKmNvdW50IGRhdGEqKi4gQ291bnQgZGF0YSBjYW4gYmUgY2hhcmFjdGVyaXplZCBhczoKCi0gRGlzY3JldGUgKGludGVnZXJzKSBkYXRhOiAwLCAxLCAyLCAzLCAuLi4KLSBUaGV5IGNvdW50IHNvbWV0aGluZywgc3VjaCBhcyBwZW9wbGUsIGluc3RhbmNlcywgZXZlbnRzLCBhY3Rpb25zLCBzdWNjZXNzZXMsIG9yIG9jY3VycmVuY2VzLgotIENhbm5vdCBiZSBzbWFsbGVyIHRoYW4gemVybyAobG93ZXIgYm91bmQgPSAwKSBidXQgZG8gbm90IGhhdmUgYW4gdXBwZXIgbGltaXQuCgo8L2Rpdj4KClRoZXJlIGFyZSBtYW55IGRpc3RyaWJ1dGlvbnMgdXNlZCBmb3IgY291bnQgZGF0YSwgc3VjaCBhcyBCaW5vbWlhbCwgUG9pc3NvbiwgYW5kIE5lZ2F0aXZlIEJpbm9taWFsLgoKIyMgQmlub21pYWwKCkZvciBleGFtcGxlLCB0aGVyZSBpcyB0aGUgYmlub21pYWwgZGlzdHJpYnV0aW9uLCBhbmQgaXRzIHNwZWNpYWwgY2FzZSwgQmVybm91bGxpLCB3aGljaCBpcyB1c2VkIGZvciBiaW5hcnkgbG9naXN0aWMgcmVncmVzc2lvbi4gVGhlIGdlbmVyYWwgYmlub21pYWwgZGlzdHJpYnV0aW9uIGhhcyB0d28gcGFyYW1ldGVycywgKk4qIHRoZSBudW1iZXIgb2YgdHJpYWxzLCBhbmQgKnAqIHRoZSBwcm9iYWJpbGl0eSBvZiBzdWNjZXNzIGF0IGVhY2ggdHJpYWwuIFRoZSBtZWFuIGlzICpOICRcdGltZXMkIHAqLgoKQmVsb3cgeW91IGNhbiBzZWUgc29tZSBwbG90cyBvZiBkaWZmZXJlbnQgYmlub21pYWwgZGlzdHJpYnV0aW9ucy4gVGhleSBkb24ndCBzZWVtIHRvbyBzaW1pbGFyIHRvIG91ciBkYXRhc2V0cyBmcm9tIGFib3ZlLgoKYGBge3IgdHlwaWNhbCBiaW5vbWlhbH0KTnMgPC0gYygxMCwgMjAsIDMwKSAjIHNldCBzb21lIGV4YW1wbGUgZm9yIE5zCnByb2JzIDwtIGMoMC4xLCAwLjI1LCAwLjUpICMgc2V0IHNvbWUgZXhhbXBsZXMgZm9yIHByb2JhYmlsaXRpZXMKTnByb2JzIDwtIGV4cGFuZC5ncmlkKE49TnMsIFByb2I9cHJvYnMpICMgY3JlYXRlcyBhIG1hdHJpeCBvZiBhbGwgdGhlIE5zIHggYWxsIHRoZSBwcm9iYWJpbGl0aWVzCiMgZXh0cmFjdCB0aGUgcG9pbnQgZGVuc2l0eSBvZiB0aGUgYmlub21pYWwgZGlzdHJpYnV0aW9uIHdpdGggdGhlIE5zIGFuZCBQcyBmcm9tIGFib3ZlCnNpbV9iaW5vbWlhbCA8LSBkby5jYWxsKHJiaW5kLCBhcHBseShOcHJvYnMsIDEsIGZ1bmN0aW9uKG5wKSBkYXRhLmZyYW1lKE49bnBbWzFdXSwgUHJvYj1ucFtbMl1dLCBDb3VudD0xOjI1LCBGcmVxdWVuY3k9ZGJpbm9tKDE6MjUsc2l6ZT1ucFsxXSwgcHJvYj1ucFsyXSkpKSkKc2ltX2Jpbm9taWFsJE4gPC0gZmFjdG9yKHNpbV9iaW5vbWlhbCROKSAjIHRyYW5zZm9ybSBpbnRvIGZhY3RvciBmb3IgdGhlIGxlZ2VuZApzaW1fYmlub21pYWwkUHJvYiA8LSBmYWN0b3Ioc2ltX2Jpbm9taWFsJFByb2IpICMgdHJhbnNmb3JtIGludG8gZmFjdG9yIGZvciB0aGUgbGVnZW5kCgojIHBsb3QgdGhlIGhpc3RvZ3JhbXMKZ2dwbG90KHNpbV9iaW5vbWlhbCwgYWVzKHg9Q291bnQrMC41LCB5ID0gRnJlcXVlbmN5LCBjb2xvcj1OLCBncm91cD1OLCBmaWxsPU4pKSArIHRoZW1lX2J3KCkgKyBzY2FsZV94X2NvbnRpbnVvdXMoZXhwYW5kID0gYygwLCAwKSkgKyBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gYygwLCAwLCAwLjEsIDApKSArIHN0YXRfc21vb3RoKGZvcm11bGEgPSB5IH4geCwgZ2VvbSA9ICdhcmVhJywgbWV0aG9kID0gJ2xvZXNzJywgc3BhbiA9IDEvNSwgYWxwaGEgPSAxLzIsIGxpbmV3aWR0aD0xKSArIGZhY2V0X3dyYXAoflByb2IsIGxhYmVsbGVyID0gbGFiZWxfYm90aCkgKyB4bGFiKCJDb3VudCIpCmBgYAoKIyMgUG9pc3NvbgoKVGhlICoqUG9pc3NvbioqIGRpc3RyaWJ1dGlvbiBpcyBwcm9iYWJseSB0aGUgbW9zdCBmYW1vdXMgb2YgdGhlIGNvdW50IGRpc3RyaWJ1dGlvbnMuIEl0IG9ubHkgaGFzIG9uZSBwYXJhbWV0ZXIsICpsYW1iZGEqLCB3aGljaCBpcyB0aGUgZXhwZWN0ZWQgbWVhbiAoYW5kIGFsc28gaXRzIHN0YW5kYXJkIGRldmlhdGlvbikuIEl0IGlzIG9mdGVuIHVzZWQgdG8gbW9kZWwgdGhlIG51bWJlciBvZiBwZW9wbGUgaW4gYSBxdWV1ZSBhdCBhbnkgcG9pbnQgaW4gdGltZSwgbnVtYmVyIG9mIHBlb3BsZSB3aG8gYXR0ZW5kIGEgY2xhc3MsIG9yIHRoZSBudW1iZXIgb2YgZGFpbHkgY3VzdG9tZXJzIHRoYXQgdmlzaXQgYSBzdG9yZS4KCkJlbG93IHlvdSBjYW4gc2VlIHNvbWUgcGxvdHMgb2YgZGlmZmVyZW50IFBvaXNzb24gZGlzdHJpYnV0aW9ucy4gVGhlc2UgYXJlIG1vcmUgc2ltaWxhciB0byBvdXIgZGF0YXNldHMsIGluIHBhcnRpY3VsYXIgd2l0aCBzbWFsbCBsYW1iZGFzLCBidXQgd2l0aCBvbmx5IG9uZSBwYXJhbWV0ZXIgdGhleSBhcmUgbm90IHZlcnkgZmxleGlibGUuCgpgYGB7ciB0eXBpY2FsIFBvaXNzb259IApsYW1iZGFzIDwtIGMoMywgNiwgMTIpICMgc2V0IHNvbWUgZXhhbXBsZSBmb3IgbGFtYmRhcwojIGV4dHJhY3QgdGhlIHBvaW50IGRlbnNpdHkgb2YgdGhlIHBvaXNzb24gZGlzdHJpYnV0aW9uIHdpdGggdGhlIGxhbWJkYXMgZnJvbSBhYm92ZQpzaW1fcG9pc3NvbiA8LSBkby5jYWxsKHJiaW5kLCBsYXBwbHkobGFtYmRhcywgZnVuY3Rpb24obCkgZGF0YS5mcmFtZShMYW1iZGE9bCwgQ291bnQ9MToyNSwgRnJlcXVlbmN5PWRwb2lzKDE6MjUsbCkpKSkKc2ltX3BvaXNzb24kTGFtYmRhIDwtIGZhY3RvcihzaW1fcG9pc3NvbiRMYW1iZGEpCgojIHBsb3QgdGhlIGhpc3RvZ3JhbXMKZ2dwbG90KHNpbV9wb2lzc29uLCBhZXMoeD1Db3VudCswLjUsIHkgPSBGcmVxdWVuY3ksIGNvbG9yPUxhbWJkYSwgZ3JvdXA9TGFtYmRhLCBmaWxsPUxhbWJkYSkpICsgdGhlbWVfYncoKSArIHNjYWxlX3hfY29udGludW91cyhleHBhbmQgPSBjKDAsIDApKSArIHNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBjKDAsIDAsIDAuMSwgMCkpICsgc3RhdF9zbW9vdGgoZm9ybXVsYSA9IHkgfiB4LCBnZW9tID0gJ2FyZWEnLCBtZXRob2QgPSAnbG9lc3MnLCBzcGFuID0gMS8zLCBhbHBoYSA9IDEvMywgbGluZXdpZHRoPTEpICsgeGxhYigiQ291bnQiKQpgYGAKCiMjIE5lZ2F0aXZlIEJpbm9taWFsCgpBbm90aGVyIG9uZSBpcyB0aGUgKipOZWdhdGl2ZSBCaW5vbWlhbCoqIGRpc3RyaWJ1dGlvbiwgd2hpY2ggaXMgbW9yZSBmbGV4aWJsZSB0aGFuIFBvaXNzb24gd2l0aCB0d28gcGFyYW1ldGVycywgKm11Kiwgd2hpY2ggaXMgdGhlIGV4cGVjdGVkIG1lYW4sIGFuZCAqc2l6ZSogd2hpY2ggaXMgYSBtZWFzdXJlIG9mIGRpc3BlcnNpb24gKHNpbWlsYXIgdG8gZGV2aWF0aW9uKS4KCkJlbG93IHlvdSBjYW4gc2VlIHNvbWUgcGxvdHMgb2YgZGlmZmVyZW50IE5lZ2F0aXZlIEJpbm9taWFsIGRpc3RyaWJ1dGlvbnMuIFRoZXNlIGFyZSB0aGUgbW9zdCBzaW1pbGFyIHRvIG91ciBkaXN0cmlidXRpb25zLgoKPGRpdiBjbGFzcyA9ICJibHVlIj4KCk5lZ2F0aXZlIEJpbm9taWFsIGlzIHByb2JhYmx5IHRoZSBtb3N0IHVzZWZ1bCBkaXN0cmlidXRpb24gZm9yIHBzeWNob2xvZ2ljYWwgZXhwZXJpbWVudGFsIGRhdGEuCgo8L2Rpdj4KCmBgYHtyIHR5cGljYWwgbmVnYXRpdmUgYmlub21pYWx9Cm11cyA8LSBjKDMsIDYsIDEyKSAjIHNldCBzb21lIGV4YW1wbGUgZm9yIG11cwpzaXplcyA8LSBjKDMsIDYsIDEyKSAjIHNldCBzb21lIGV4YW1wbGUgZm9yIHNpemVzCm11c2l6ZXMgPSBleHBhbmQuZ3JpZChtdT1tdXMsIHNpemU9c2l6ZXMpICMgY3JlYXRlcyBhIG1hdHJpeCBvZiBhbGwgdGhlIG11cyB4IGFsbCB0aGUgc2l6ZXMKIyBleHRyYWN0IHRoZSBwb2ludCBkZW5zaXR5IG9mIHRoZSBuZWdhdGl2ZSBiaW5vbWlhbCBkaXN0cmlidXRpb24gd2l0aCB0aGUgbXVzIGFuZCBzaXplcyBmcm9tIGFib3ZlCnNpbV9uYmlub20gPC0gZG8uY2FsbChyYmluZCwgYXBwbHkobXVzaXplcywgMSwgZnVuY3Rpb24obXMpIGRhdGEuZnJhbWUoTXU9bXNbWzFdXSwgU2l6ZT1tc1tbMl1dLCBDb3VudD0xOjMwLCBGcmVxdWVuY3k9ZG5iaW5vbSgxOjMwLHNpemU9bXNbWzJdXSwgbXU9bXNbWzFdXSkpKSkKc2ltX25iaW5vbSRNdSA8LSBmYWN0b3Ioc2ltX25iaW5vbSRNdSkgIyB0cmFuc2Zvcm0gaW50byBmYWN0b3IgZm9yIHRoZSBsZWdlbmQKc2ltX25iaW5vbSRTaXplIDwtIGZhY3RvcihzaW1fbmJpbm9tJFNpemUpICMgdHJhbnNmb3JtIGludG8gZmFjdG9yIGZvciB0aGUgbGVnZW5kCgojIHBsb3QgdGhlIGhpc3RvZ3JhbXMKZ2dwbG90KHNpbV9uYmlub20sIGFlcyh4PUNvdW50KzAuNSwgeSA9IEZyZXF1ZW5jeSwgY29sb3I9TXUsIGdyb3VwPU11LCBmaWxsPU11KSkgKyB0aGVtZV9idygpICsgc2NhbGVfeF9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwgMCkpICsgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwgMCwgMC4xLCAwKSkgKyBzdGF0X3Ntb290aChmb3JtdWxhID0geSB+IHgsIGdlb20gPSAnYXJlYScsIG1ldGhvZCA9ICdsb2VzcycsIHNwYW4gPSAxLzMsIGFscGhhID0gMS8zLCBsaW5ld2lkdGg9MSkgKyBmYWNldF93cmFwKH5TaXplLCBsYWJlbGxlciA9IGxhYmVsX2JvdGgpICsgeGxhYigiQ291bnQiKQpgYGAKCiMgRXhhbXBsZTogU3RvY2sgdHJhZGluZwoKTGV0J3MgbG9vayBhdCBvbmUgcGFydGljdWxhciBleGFtcGxlOiBgdHJhZGVfZGF0YWAuIFRoaXMgc3R1ZHkgd2FzIGFib3V0IHN0b2NrIHRyYWRpbmcgYmVoYXZpb3VyLCBhbmQgcGFydGljaXBhbnRzIHdlcmUgYWxsb3dlZCB0byB0cmFkZSBzdG9ja3MgKGJ1eSBvciBzZWxsKSBhcyB0aGV5IHdhbnRlZCBpbiBhIHNpbXVsYXRlZCB0cmFkaW5nIHRhc2suCgo8ZGl2IGNsYXNzID0gImJsdWUiPgoKQ3J1Y2lhbGx5LCB0aGUgZGF0YSAqKmNvdW50cyoqIGhvdyBtYW55IHRyYWRlcyBwYXJ0aWNpcGFudHMgbWFkZSBkdXJpbmcgdGhlIHRhc2suIFBhcnRpY2lwYW50cyBkaWQgbm90ICoqbmVlZCoqIHRvIHRyYWRlIGF0IGFsbCwgc28gdGhlIG1pbmltdW0gbnVtYmVyIG9mIHRyYWRlcyB3YXMgemVyby4gVGhlcmUgd2FzICoqbm8gdXBwZXIgbGltaXQqKiwgcGFydGljaXBhbnRzIGNvdWxkIG1ha2UgYXMgbWFueSB0cmFkZXMgYXMgdGhleSB3YW50ZWQuCgo8L2Rpdj4KCmBgYHtyIHRyYWRlIGRhdGEgaGlzdG9ncmFtfQojIHNob3cgaGlzdG9ncmFtIG9mIHRyYWRlX2NvdW50CmdncGxvdCh0cmFkZV9kYXRhLCBhZXMoeCA9IHRyYWRlX2NvdW50KSkgKyBnZW9tX2hpc3RvZ3JhbShmaWxsPSJsaWdodGJsdWUiLCBjb2xvcj0iYmxhY2siLCBiaW53aWR0aD0yKSArIHRoZW1lX2J3KCkgKyBnZ3RpdGxlKCJOdW1iZXIgb2YgdHJhZGVzIG1hZGUgKGJ1eSArIHNlbGwgY29tYmluZWQpIikKYGBgCgpXZSBjYW4gc2VlIHRoZSByYW5nZSBhbmQgcXVhbnRpbGVzIG9mIHRyYWRlcyBpbiB0aGUgdGFibGUgYmVsb3cuCgpgYGB7ciB0cmFkZSByYW5nZX0KIyBxdWFudGlsZXMgb2YgdHJhZGVzIG1hZGUKa2FibGUocXVhbnRpbGUodHJhZGVfZGF0YSR0cmFkZV9jb3VudCksIGNvbC5uYW1lcyA9IGMoInF1YW50aWxlIiwgInRyYWRlIGNvdW50IikpCmBgYAoKUiBoYXMgYSBoYW5keSBmdW5jdGlvbiB0aGF0IGNoZWNrcyBob3cgd2VsbCBhIGRpc3RyaWJ1dGlvbiBmaXRzIGFnYWluc3QgYSBzZXQgZGlzdHJpYnV0aW9uLCBgZml0ZGlzdGAgZnJvbSBwYWNrYWdlIGBmaXRkaXN0cnBsdXNgLiBJdCBlc3RpbWF0ZXMgdGhlIGJlc3QgZml0IHBhcmFtZXRlcnMgdGhhdCBtb3N0IGNsb3NlbHkgZml0cyB0aGUgZGF0YS4gSW4gdGhlIGNhc2Ugb2YgYSBub3JtYWwgZGlzdHJpYnV0aW9uLCBpdCBlc3RpbWF0ZXMgdGhlIG1lYW4gYW5kIHN0YW5kYXJkIGRldmlhdGlvbiAoc2QpLiAKCkxldCdzIHNlZSBob3cgd2VsbCB0aGlzIGZpdHMgYSBub3JtYWwgZGlzdHJpYnV0aW9uLiBJZGVhbGx5IHRoZSBkYXRhIHNob3VsZCBiZSAqKmFzIGNsb3NlIGFzIHBvc3NpYmxlKiogdG8gdGhlIHRoZW9yZXRpY2FsIGJlc3QgZml0IGRpc3RyaWJ1dGlvbiAoc2hvd24gaW4gcmVkKS4gQXMgZXhwZWN0ZWQsIGl0J3MgYSAqKnZlcnkgcG9vciBmaXQqKi4gV2UgaGF2ZSBleGNlc3Mgb2JzZXJ2YXRpb25zIGluIGJvdGggZW5kcyBvZiB0aGUgZGlzdHJpYnV0aW9uLl5bQSBub3RlIG9uIHN0YXRpc3RpY2FsIGRpYWdub3N0aWMgdGVzdHMgZm9yIGRpc3RyaWJ1dGlvbnMgYW5kIHJlZ3Jlc3Npb25zOiBtb3N0IHRlc3RzIG9mIG5vcm1hbGl0eSAoU2hhcGlyby1XaWxrLCBLb2xtb2dvcm92LVNtaXJub2ZmKSBhbmQgdGVzdHMgb2YgZGlhZ25vc3RpY3MgKExldmVuZSdzLCBXaGl0ZSdzKSBhcmUgZ2VuZXJhbGx5IG5vdCByZWNvbW1lbmRlZC4gVGhpcyBpcyBiZWNhdXNlIHdpdGggbGFyZ2UgZGF0YXNldHMgdGhleSBhcmUgb3Zlci1zZW5zaXRpdmUgYW5kIHdpbGwgaGlnaGxpZ2h0IGV2ZW4gdGlueSBkZXZpYXRpb25zIChjb21tb24gaW4gcmVhbC1saWZlIGR1ZSB0byBub2lzZSkgYXMgc2lnbmlmaWNhbnQuIFdpdGggc21hbGwgZGF0YXNldHMgdGhleSBhcmUgdW5kZXItcG93ZXJlZCB0byBpZGVudGlmeSByZWFsIGRldmlhdGlvbnMuIFZpc3VhbCBpbnNwZWN0aW9uIGlzIG9mdGVuIGJlc3QuIEZvciBtb3JlIHJlYWQ6IGh0dHBzOi8vdG93YXJkc2RhdGFzY2llbmNlLmNvbS9zdG9wLXRlc3RpbmctZm9yLW5vcm1hbGl0eS1kYmE5NmJiNzNmOTAuXQoKYGBge3IgdHJhZGUgZml0ZGlzdHIgbm9ybWFsLCBjbGFzcy5zb3VyY2UgPSAnZm9sZC1zaG93J30KIyBjb21wYXJlIHRyYWRlX2NvdW50IGRhdGEgd2l0aCBhIG5vcm1hbCBkaXN0cmlidXRpb24KIyAnU3VtbWFyeScgc2hvd3MgdGhlIGJlc3QgZml0dGluZyBtZWFuIGFuZCBzZCBvZiBhIG5vcm1hbCBkaXN0cmlidXRpb24gYWdhaW5zdCB0aGlzIGRhdGEsIGFuZCB0aGUgbG9nbGlrZWxpaG9vZCBtZWFzdXJlcyB0aGUgZGV2aWFuY2UgYmV0d2VlbiB0aGUgYWN0dWFsIGRhdGEgYW5kIHRoZSBiZXN0IGZpdHRpbmcgZGlzdHJpYnV0aW9uLgpzdW1tYXJ5KGZpdGRpc3QodHJhZGVfZGF0YSR0cmFkZV9jb3VudCwgZGlzdD0ibm9ybSIpKQojICdQbG90JyBpcyB1c2VkIHRvIGNyZWF0ZSB0aGUgcGxvdCBzaG93biBiZWxvdy4KcGxvdChmaXRkaXN0KHRyYWRlX2RhdGEkdHJhZGVfY291bnQsIGRpc3Q9Im5vcm0iKSkKYGBgCgojIyBUcmFuc2Zvcm1hdGlvbnMKCkJlY2F1c2UgdGhlIGRhdGEgc2VlbXMgc2tld2VkLCB5b3UgbWlnaHQgYmUgdGVtcHRlZCB0byBhbmFseXNlIGl0cyBsb2cuIEhvd2V2ZXIsIGJlY2F1c2UgdGhpcyBpcyBkaXNjcmV0ZSBjb3VudCBkYXRhLCB0cmFuc2Zvcm1pbmcgaXQgd2lsbCBub3QgZml4IHRoZSBwcm9ibGVtLiBJZiB5b3UgbG9nIHRoZSBkYXRhLCBpdCB3aWxsIGJlIGNsb3NlciB0bywgYnV0IHN0aWxsIG5vdCBleGFjdGx5LCBhIG5vcm1hbCBkaXN0cmlidXRpb24uIFRyYW5zZm9ybWF0aW9ucyB0ZW5kIHRvIHdvcmsgYmV0dGVyIHdpdGggY29udGludW91cyBkYXRhLgoKPGRpdiBjbGFzcyA9ICJibHVlIj4KClRoZXJlJ3MgYW4gYWRkaXRpb25hbCBwcm9ibGVtIHRoYXQgY291bnQgZGF0YSBvZnRlbiBjb250YWlucyAqKnplcm9zKiosIGFuZCB0aGUgbG9nIG9mIHplcm8gaXMgdW5kZWZpbmVkLiBTbyBpZiB3ZSB3YW50IHRvIHRyeSB0aGUgbG9nIG9mIHRoaXMgZGF0YSwgd2UgbmVlZCB0byBhZGQgKzEgKG9yIGFueSBvdGhlciBwb3NpdGl2ZSBhbW91bnQpIGJlZm9yZSBjYWxjdWxhdGluZyB0aGUgbG9nLiAKCjwvZGl2PgoKYGBge3IgdHJhZGUgbG9nZ2VkIGRhdGEsIGNsYXNzLnNvdXJjZSA9ICdmb2xkLXNob3cnfQojIGxvZyB0cmFkZV9jb3VudCArIDEKZ2dwbG90KHRyYWRlX2RhdGEsIGFlcyh4ID0gbG9nKHRyYWRlX2NvdW50KzEpKSkgKyBnZW9tX2hpc3RvZ3JhbShmaWxsPSJsaWdodGJsdWUiLCBjb2xvcj0iYmxhY2siLCBiaW53aWR0aD0xKSArIHRoZW1lX2J3KCkKYGBgCgpUaGUgbG9nIG9mIHRoZSB0cmFkZSBjb3VudCBkYXRhICsgMSBpcyBjbG9zZXIgdG8gYSBub3JtYWwgZGlzdHJpYnV0aW9uLCBidXQgc3RpbGwgcHJvYmxlbWF0aWMgLSB0aGVyZSBpcyBzdGlsbCBhbiBleGNlc3Mgb2Ygb2JzZXJ2YXRpb25zIGF0IHRoZSBsb3dlciBlbmQsIGFuZCBtaXNzaW5nIG9ic2VydmF0aW9ucyBiZWxvdyB6ZXJvLiAKCjxkaXYgY2xhc3MgPSAiYmx1ZSI+CgpUaGlzIGlzIGJlY2F1c2Ugbm9ybWFsIGRpc3RyaWJ1dGlvbnMgYXNzdW1lIHRoYXQgdGhlIGRhdGEgaXMgdW5ib3VuZGVkICh3aXRoIG5vIGxvd2VyIG9yIHVwcGVyIGxpbWl0cykgYnV0IGluIHRoaXMgY2FzZSB0aGUgcmF3IGRhdGEgaGFzIGEgbG93ZXIgYm91bmQgb2YgemVybywgYXMgeW91IGNhbm5vdCBoYXZlIGFueSAqKmNvdW50cyBiZWxvdyB6ZXJvKiouCgpBbm90aGVyIHByb2JsZW0gaXMgdGhhdCBOb3JtYWwgZGlzdHJpYnV0aW9ucyBhc3N1bWUgdGhhdCB0aGUgZGF0YSBpcyBjb250aW51b3VzLCBhbmQgaW4gdGhpcyBjYXNlIHRoZSByYXcgZGF0YSBpcyAqKmRpc2NyZXRlKiogKG9ubHkgaW50ZWdlcnMgYXJlIGFsbG93ZWQ6IDEsIDIsIDMsIGV0YykuIAoKVGhlIGZpdCBvZiB0aGUgbm9ybWFsIGRpc3RyaWJ1dGlvbiBoZXJlIGlzIG5vdCAqdG9vKiBiYWQsIGJ1dCBpdCB3b3VsZCBtdWNoIHdvcnNlIHdoZW4gdGhlcmUgYXJlICoqZXh0cmVtZSAodmVyeSBsYXJnZSkgdmFsdWVzKiogLSB2ZXJ5IGhpZ2ggY291bnRzIC0gd2hpY2ggY2FuIGJlIHZlcnkgY29tbW9uIGluIGNvdW50IGRhdGEuIE5vcm1hbCBkaXN0cmlidXRpb25zIHN0cnVnZ2xlIHdpdGggbGFyZ2UgdmFsdWVzLCB3aGljaCB3aWxsIHNrZXcgYW5hbHlzaXMgYW5kIGludGVycHJldGF0aW9uLiBDb3VudCBkaXN0cmlidXRpb24gd2lsbCBoYW5kbGUgZXh0cmVtZSBvdXRsaWVycyBtdWNoIGJldHRlci4KCjwvZGl2PgoKYGBge3IgdHJhZGUgZml0ZGlzdHIgbG9nIG5vcm1hbCwgY2xhc3Muc291cmNlID0gJ2ZvbGQtc2hvdyd9CiMgY29tcGFyZSBsb2cgb2YgdHJhZGVfY291bnQgKyAxIGRhdGEgd2l0aCBhIG5vcm1hbCBkaXN0cmlidXRpb24KcGxvdChmaXRkaXN0KGxvZyh0cmFkZV9kYXRhJHRyYWRlX2NvdW50KzEpLCBkaXN0PSJub3JtIikpCmBgYAoKQWRkaW5nICsxIGlzIGFsc28gY29tcGxldGVseSBzdWJqZWN0aXZlIGFuZCAqKmFyYml0cmFyeSoqLCB3ZSBjb3VsZCBhcmd1YWJseSBhZGQgKzAuMSBvciArMTAuIFRoZSByZXN1bHRzIGFyZSBxdWl0ZSBkaWZmZXJlbnQgYW5kIGl0IGFsc28gY2hhbmdlcyBob3cgd2VsbCB0aGUgbmV3IGxvZyBkYXRhIGZpdHMgYSB0aGVvcmV0aWNhbCBub3JtYWwgZGlzdHJpYnV0aW9uLCBhbmQgd2lsbCBpbXBhY3QgdGhlIHJlc3VsdHMgYW5kIGludGVycHJldGF0aW9uIG9mIGFueSBhbmFseXNpcyB3ZSBtaWdodCBydW4gb24gdGhpcyBkYXRhLiBBbmQgbm8gbWF0dGVyIHdoYXQgd2UgZG8sIHdlIGFsd2F5cyBoYXZlIGEgcHJvYmxlbSB3aXRoIHRoZSBleGNlc3Mgb2JzZXJ2YXRpb25zIGF0IHRoZSBsb3dlciBlbmQuCgo8ZGl2IGNsYXNzID0gImJsdWUiPgoKVW5sZXNzIHlvdSBhcmUgcHJlLXJlZ2lzdGVyaW5nIGhvdyBtdWNoIHlvdSB3aWxsIGFkZCBiZWZvcmUgdGhlIGxvZyAoYW5kIGhhdmUgYSBnb29kIHRoZW9yZXRpY2FsIHJlYXNvbiBmb3IgdGhpcyksIHRoaXMgdHJhbnNmb3JtYXRpb24gY2FuIGVhc2lseSBsZWFkIHRvIHAtaGFja2luZy4KCjwvZGl2PgoKYGBge3IgdHJhZGUgZml0ZGlzdHIgbG9nIG5vcm1hbCBwbHVzLCBjbGFzcy5zb3VyY2UgPSAnZm9sZC1zaG93J30KIyB0ZXN0aW5nIG90aGVyIGxvZ3MKcGxvdChmaXRkaXN0KGxvZyh0cmFkZV9kYXRhJHRyYWRlX2NvdW50Ky4xKSwgZGlzdD0ibm9ybSIpKQpwbG90KGZpdGRpc3QobG9nKHRyYWRlX2RhdGEkdHJhZGVfY291bnQrMTApLCBkaXN0PSJub3JtIikpCmBgYAoKVGhlcmVmb3JlIHdlIHNob3VsZCBuZXZlciBsb2cgY291bnQgZGF0YSB3aGVuIGl0J3MgdGhpcyBjbG9zZSB0byB6ZXJvLl5bSWYgdGhlIGNvdW50IGRhdGEgaXMgZmFyIGF3YXkgZW5vdWdoIGZyb20gemVybywgdGhlbiBpdCB3b3VsZCBiZSBtb3JlIGFjY2VwdGFibGUgdG8gZWl0aGVyIHRyZWF0IGl0IGFzIGEgbm9ybWFsIGRpc3RyaWJ1dGlvbiAoaWYgaXQgbG9va3MgbGlrZSBvbmUpLCBvciB0cnkgdG8gbG9nIHRoZSBkYXRhIGlmIGl0IGlzIHNrZXdlZC5dCgojIyBPcmRpbmFyeSBsaW5lYXIgcmVncmVzc2lvbiBhcHBsaWVkIHRvIGNvdW50IGRhdGEKCldlIGNvdWxkIHRyeSB0byBhbmFseXNlIHRoaXMgdHJhZGUgY291bnQgZGF0YSAoKnRoZSByYXcgZGF0YSwgbm90IHRoZSBsb2cqKSBhbnl3YXkgd2l0aCBhIG9yZGluYXJ5IChnZW5lcmFsKV5bTm90ZSB0aGF0IGEgKmdlbmVyYWwqIGxpbmVhciByZWdyZXNzaW9uIGlzIHRoZSBvcmRpbmFyeSB0eXBlIHdoaWNoIGFzc3VtZXMgdGhhdCB0aGUgZGF0YSBmb2xsb3dzIGEgbm9ybWFsIGRpc3RyaWJ1dGlvbiwgd2hpbGUgYSAqZ2VuZXJhbGl6ZWQqIGxpbmVhciByZWdyZXNzaW9uIGlzIGEgbW9yZSBmbGV4aWJsZSB2ZXJzaW9uIHRoYXQgYWNjZXB0cyBkaWZmZXJlbnQgdHlwZXMgb2YgZGlzdHJpYnV0aW9ucywgc3VjaCBhcyBiaW5vbWlhbCAoZm9yIGxvZ2lzdGljIHJlZ3Jlc3Npb24pLCBvciBwb2lzc29uIGFuZCBuZWdhdGl2ZSBiaW5vbWlhbCAoYW5kIG1hbnkgb3RoZXJzKS5dIGxpbmVhciByZWdyZXNzaW9uIHRvIHNlZSB3aGF0IHdlIHdvdWxkIGdldC4gCgo8ZGl2IGNsYXNzID0gImJsdWUiPgoKVGhlIG9yZGluYXJ5IGxpbmVhciByZWdyZXNzaW9uIG1vZGVsIHdpbGwgZml0IHRoZSBjb3VudCBkYXRhLiBCdXQgaXMgaXQgY29ycmVjdD8KCjwvZGl2PgoKSGVyZSB3ZSBoYXZlIHR3byBwcmVkaWN0b3JzOiB0aGUgKmV4cGVyaW1lbnRhbCBjb25kaXRpb24qIGBleHBfY29uZGAgKHRoZXJlIHdlcmUgdHdvOiAqbG93LXJpc2sqIGFuZCAqaGlnaC1yaXNrKiwgd2l0aCBhIHplcm8gc3VtIGNvbnRyYXN0IC0xIGFuZCArMSkgYW5kICpwYXJ0aWNpcGFudCdzIGFnZSouIEFnZSBoYXMgYmVlbiBjZW50ZXJlZCBhcyBgYWdlX2NgLgoKSW4gdGhpcyBjYXNlLCBhbGwgcHJlZGljdG9ycyBhcmUgc2lnbmlmaWNhbnQuCgpgYGB7ciB0cmFkZSBub3JtYWwgbW9kZWwsIGNsYXNzLnNvdXJjZSA9ICdmb2xkLXNob3cnfQpjb250cmFzdHModHJhZGVfZGF0YSRleHBfY29uZCkgPC0gY29udHIuc3VtKDIpICMgemVyby1zdW0gY29udHJhc3QgY29kZQp0cmFkZV9kYXRhJGFnZV9jIDwtIHNjYWxlKHRyYWRlX2RhdGEkYWdlLCBzY2FsZT1GKSAjIGNlbnRyZSB0aGUgYWdlCgp0cmFkZS5tMS5sbSA8LSBsbSh0cmFkZV9jb3VudCB+IGV4cF9jb25kICogYWdlX2MsIGRhdGE9dHJhZGVfZGF0YSkgIyBydW4gdGhlIGxpbmVhciByZWdyZXNzaW9uCnRhYl9tb2RlbCh0cmFkZS5tMS5sbSwgc2hvdy5zZSA9IFRSVUUsIGNvbGxhcHNlLnNlID0gVFJVRSwgc2hvdy5sb2dsaWsgPSBUUlVFKSAjIHNob3cgdGhlIHJlc3VsdCBpbiBhIG5pY2UgdGFibGUKYGBgCgo8YnI+CgpXaGF0IGtpbmQgb2YgcHJlZGljdGlvbnMgZG9lcyB0aGlzIG1vZGVsIG1ha2U/IFdlIGNhbiBjcmVhdGUgYW5kIGF2ZXJhZ2UgMTAwIHJhbmRvbSBzaW11bGF0ZWQgZGF0YXNldHMgZnJvbSB0aGUgbW9kZWwsIHVzaW5nIHRoZSBmaXR0ZWQgcGFyYW1ldGVycyBwbHVzIHJhbmRvbSBub2lzZS4gCgo8ZGl2IGNsYXNzID0gImJsdWUiPgoKRmlyc3Qgb2YgYWxsLCBzb21lIG9mIHRoZXNlIHByZWRpY3Rpb25zIGFyZSBuZWdhdGl2ZSwgd2hpY2ggaXMgc2ltcGx5IG5vdCBwb3NzaWJsZSB3aXRoIGNvdW50IGRhdGEuIFBhcnRpY2lwYW50cyBjb3VsZCBub3QgbWFrZSBmZXdlciB0aGFuIHplcm8gKG5lZ2F0aXZlKSB0cmFkZXMuIFRoaXMgaXMgYSBjb21tb24gcHJvYmxlbSB3aXRoICoqYm91bmRlZCBkYXRhKiogb3IgKipjZW5zb3JlZCBkYXRhKiouIE5vcm1hbCBkaXN0cmlidXRpb25zICh3aGljaCB3ZSBhc3N1bWUgZm9yIGFuIG9yZGluYXJ5IGxpbmVhciByZWdyZXNzaW9uKSBhcmUgbm90IGJvdW5kZWQsIGFuZCB3aWxsIHRyeSB0byBwcmVkaWN0IG5lZ2F0aXZlIGRhdGEuCgpBbmQgaXQgZGVmaW5pdGVseSBkb2VzIG5vdCBtYXRjaCB0aGUgc2hhcGUgb2YgdGhlIG9yaWdpbmFsIGRhdGFzZXQuIFRoaXMgY2xlYXJseSBzaG93cyBob3cgYW4gKipvcmRpbmFyeSBsaW5lYXIgcmVncmVzc2lvbiBwcmVkaWN0cyBub3JtYWxseSBkaXN0cmlidXRlZCBkYXRhKiouCgo8L2Rpdj4KCmBgYHtyIHRyYWRlIG5vcm1hbCBwcmVkaWN0aW9uc30KcHJlZC5zYW1wbGVzIDwtIDE6MTAwICMgaG93IG1hbnkgcHJlZGljdGlvbnMgZG8gd2Ugd2FudD8KIyBzaW11bGF0ZSB0aGUgcHJlZGljdGlvbnMKcHJlZC5jb3VudCA8LSBkYXRhLmZyYW1lKGRhdGE9J1ByZWRpY3Rpb24gKE9yZGluYXJ5IExpbmVhciBSZWdyZXNzaW9uKScsIHRyYWRlX2NvdW50PWFzLnZlY3Rvcihkby5jYWxsKHJiaW5kLCBzYXBwbHkocHJlZC5zYW1wbGVzLCBmdW5jdGlvbih4KXtzaW11bGF0ZSh0cmFkZS5tMS5sbSl9KSkpKQojIGJpbmQgdGhlIHByZWRpY3Rpb25zIHdpdGggdGhlIHJhdyBkYXRhIGluIGEgc2luZ2xlIGRhdGEgZnJhbWUgZm9yIHBsb3R0aW5nCnByZWQuY291bnQgPC0gcmJpbmQocHJlZC5jb3VudCwgZGF0YS5mcmFtZShkYXRhPSdPcmlnaW5hbCBEYXRhJywgdHJhZGVfY291bnQ9dHJhZGVfZGF0YSR0cmFkZV9jb3VudCkpCgojIHNob3cgYSBoaXN0b2dyYW0gb2YgdGhlIHNpbXVsYXRlZCBvdXRjb21lcyBuZXh0IHRvIHRoZSBvcmlnaW5hbCByYXcgZGF0YQpnZ3Bsb3QoZGF0YT1wcmVkLmNvdW50LCBhZXModHJhZGVfY291bnQsIGdyb3VwPWRhdGEpKSArIGdlb21faGlzdG9ncmFtKGFlcyh5PWFmdGVyX3N0YXQoZGVuc2l0eSkpLCBmaWxsPSJsaWdodGJsdWUiLCBjb2xvciA9ICJibGFjayIsIGJpbndpZHRoPTIpICsgdGhlbWVfYncoKSArIHhsYWIoIlRyYWRlIENvdW50IikgKyB5bGFiKCIlIG9mIHBhcnRpY2lwYW50cyIpICsgZmFjZXRfd3JhcCh+ZGF0YSkKYGBgCgojIyMgQXNzdW1wdGlvbnMKClRoZXJlIGFyZSBwYWNrYWdlcyBpbiBSIHRoYXQgYXJlIHZlcnkgdXNlZnVsIGZvciBjaGVja2luZyB0aGUgKiphc3N1bXB0aW9ucyoqIG9mIGEgbW9kZWwgYW5kIGhvdyB3ZWxsIGEgbW9kZWwgZml0cyBhIGRhdGFzZXQsIHN1Y2ggYXMgYERIQVJNYWAgKHdoaWNoIHdlIHVzZSBoZXJlKSBhbmQgYHBlcmZvcm1hbmNlYC4gQSBnb29kLWZpdHRpbmcgbW9kZWwgc2hvdWxkIG5vdCBoYXZlIGFueXRoaW5nIHJlZCBoaWdobGlnaHRlZCBvbiB0aGlzIG91dHB1dC4gCgpUaGUgb3V0cHV0IHNob3dzIGhvdyB0aGlzIG9yZGluYXJ5IGxpbmVhciBtb2RlbCBoYXMgKipzZXZlcmFsIGRldmlhdGlvbnMgZnJvbSB0aGUgYXNzdW1wdGlvbnMqKi4gCgpPbiB0aGUgbGVmdCB3ZSBzZWUgdGhhdCB0aGUgcmVzaWR1YWxzIGRldmlhdGUgZnJvbSBhIG5vcm1hbCBkaXN0cmlidXRpb24gKHRoZSBibGFjayBkb3RzIHNob3VsZCBiZSBvbiB0aGUgcmVkIGxpbmUpLiAKCk9uIHRoZSByaWdodCB3ZSBzZWUgdGhhdCB0aGUgcmVzaWR1YWxzIGFyZSBub3QgZXZlbmx5IHNwcmVhZCBhY3Jvc3MgdGhlIHByZWRpY3Rpb25zLCB0aGVyZWZvcmUgdGhlcmUgaXMgaGV0ZXJvc2tlZGFzdGljaXR5ICh0aGUgc29saWQgbGluZXMgLSB3aGljaCBzaG93IHRoZSAyNSUsIDUwJSwgYW5kIDc1JSBxdWFydGlsZXMgLSBzaG91bGQgYmUgaG9yaXpvbnRhbCwgc2hvd2luZyB0aGF0IHRoZSBkaXNwZXJzaW9uIG9mIHJlc2lkdWFscyBpcyBmbGF0IGFjcm9zcyB0aGUgbGV2ZWxzIG9mIHByZWRpY3Rpb25zKS4KCmBgYHtyIG5vcm1hbCBESEFSTWEsIGNsYXNzLnNvdXJjZSA9ICdmb2xkLXNob3cnfQojIHRoZSB3YXkgREhBUk1hIHdvcmtzIGlzIHRoYXQgZmlyc3Qgd2Ugc2ltdWxhdGUgdGhlIHJlc2lkdWFscywgYW5kIHNhdmUgaXQKc2ltLm0xLmxtIDwtIHNpbXVsYXRlUmVzaWR1YWxzKGZpdHRlZE1vZGVsID0gdHJhZGUubTEubG0pCiMgdGhlbiB3ZSBwbG90IGl0CnBsb3Qoc2ltLm0xLmxtLCB0aXRsZT0iUmVzaWR1YWwgZGlhZ25vc3RpY3MgZm9yIGxpbmVhciBtb2RlbCIpCmBgYAoKIyMjIFRyYW5zZm9ybWF0aW9uIChsb2cpCgpJZiB5b3UgdXNlZCB0aGUgbG9nIG9mIHRoZSBkYXRhLCBpdCB3b3VsZCBmaXQgYmV0dGVyLCBhbmQgYWxsIHRoZSBwcmVkaWN0b3JzIGFyZSBzdGlsbCBzaWduaWZpY2FudC4gSG93ZXZlciwgdGhlcmUgYXJlIHN0aWxsIHNvbWUgcmVkIGRldmlhdGlvbnMgaGlnaGxpZ2h0ZWQuIAoKPGRpdiBjbGFzcyA9ICJibHVlIj4KClRoZSBhbW91bnQgdGhhdCB5b3UgYWRkIHRvIHRoZSBsb2cgKCsxIG9yICsuMDEpIHdvdWxkIGltcGFjdCB0aGUgcmVzdWx0cywgY29lZmZpY2llbnRzLCBlZmZlY3Qgc2l6ZXMsIGFuZCBpbmZlcmVuY2VzLiBJZiB5b3UgYWRkIDAuMSBpbnN0ZWFkIG9mIDEsIHRoZSBpbnRlcmFjdGlvbiBpcyBubyBsb25nZXIgc2lnbmlmaWNhbnQuIAoKPC9kaXY+CgpMb2cgZGF0YSBhbHNvIHdvdWxkIG5ldmVyIHJldHVybiBhIHRyYWRlIGNvdW50IG9mIHplcm8gKGl0IHdvdWxkIHRlbmQgdG8gemVybyBidXQgbmV2ZXIgYmUgemVybyksIHdoaWNoIGlzIG5vdCBpZGVhbCBhcyB3ZSBjbGVhcmx5IGhhdmUgemVybyBjb3VudHMgaW4gdGhlIGRhdGFzZXQuIAoKYGBge3IgdHJhZGUgbG9nIG5vcm1hbCBtb2RlbH0KdHJhZGUubTEubG0ubG9nMDEgPC0gbG0obG9nKHRyYWRlX2NvdW50Ky4xKSB+IGV4cF9jb25kICogYWdlX2MsIGRhdGE9dHJhZGVfZGF0YSkgIyBydW4gdGhlIGxvZyByZWdyZXNzaW9uCnRyYWRlLm0xLmxtLmxvZzEgPC0gbG0obG9nKHRyYWRlX2NvdW50KzEpIH4gZXhwX2NvbmQgKiBhZ2VfYywgZGF0YT10cmFkZV9kYXRhKSAjIHJ1biB0aGUgbG9nIHJlZ3Jlc3Npb24KdHJhZGUubTEubG0ubG9nMTAgPC0gbG0obG9nKHRyYWRlX2NvdW50KzEwKSB+IGV4cF9jb25kICogYWdlX2MsIGRhdGE9dHJhZGVfZGF0YSkgIyBydW4gdGhlIGxvZyByZWdyZXNzaW9uCgp0YWJfbW9kZWwodHJhZGUubTEubG0ubG9nMDEsIHRyYWRlLm0xLmxtLmxvZzEsIHRyYWRlLm0xLmxtLmxvZzEwLCBzaG93LnNlID0gVFJVRSwgY29sbGFwc2Uuc2UgPSBUUlVFLCBzaG93LmxvZ2xpayA9IFRSVUUpCgpzaW0ubTEubG0ubG9nMDEgPC0gc2ltdWxhdGVSZXNpZHVhbHMoZml0dGVkTW9kZWwgPSB0cmFkZS5tMS5sbS5sb2cwMSkgIyBydW5zIHRoZSBESEFSTWEgb3V0cHV0CnBsb3Qoc2ltLm0xLmxtLmxvZzAxLCB0aXRsZT0iUmVzaWR1YWwgZGlhZ25vc3RpY3MgZm9yIGxpbmVhciBtb2RlbCIpCmBgYAoKVGhlcmUgYXJlIGJldHRlciB3YXlzIHRvIGFuYWx5c2UgdGhpcyBkYXRhLiAKCiMjIENvdW50IGRpc3RyaWJ1dGlvbnM6IFBvaXNzb24gYW5kIE5lZ2F0aXZlIEJpbm9taWFsCgo8ZGl2IGNsYXNzID0gImJsdWUiPgoKV2UgY2FuIGNoZWNrIGlmIHRoaXMgZGF0YSBmaXRzIGEgY291bnQgZGlzdHJpYnV0aW9uLCBpbiBhY2NvcmRhbmNlIHdpdGggdGhlIHVuZGVybHlpbmcgKmNvdW50aW5nIG5hdHVyZSBvZiB0aGUgZGF0YSogLSBjb3VudGluZyB0aGUgbnVtYmVyIG9mIHRyYWRlcyBtYWRlLiAKCjwvZGl2PgoKV2UgY2FuIHRyeSB0byBmaXQgb3VyIGRhdGEgdG8gYSAqUG9pc3NvbiogZGlzdHJpYnV0aW9uLiBIb3dldmVyIGl0ICpkb2VzIG5vdCBmaXQgdGhlIGRhdGEgcGFydGljdWxhcmx5IHdlbGwqIC0gdGhlIHByZWRpY3Rpb25zIChpbiByZWQpIHBlYWsgbGF0ZXIgdGhhbiB0aGUgYWN0dWFsIGRhdGEgcGVha3MuIFRoZSByZWQgbGluZXMgZG8gbm90IG1hdGNoIHRoZSBibGFjayBsaW5lcyBhbmQgZG90cy4KClRoZSBtZWFuIG9mIHRoaXMgUG9pc3NvbiBkaXN0cmlidXRpb24gaXMgYWxzbyB0aGUgc2FtZSBtZWFuIGFzIHRoZSBub3JtYWwgZGlzdHJpYnV0aW9uIHByZXZpb3VzbHkgZml0dGVkIHRvIHRoaXMgZGF0YS4gSG93ZXZlciwgdGhlIFBvaXNzb24gZGlzdHJpYnV0aW9uICpkb2VzIG5vdCBwcmVkaWN0IG5lZ2F0aXZlIG91dGNvbWVzKiAoYXMgdGhlIG5vcm1hbCBkaXN0cmlidXRpb24gZGlkKS4KCmBgYHtyIHRyYWRlIGZpdGRpc3RyIHBvaXNzb24sIGNsYXNzLnNvdXJjZSA9ICdmb2xkLXNob3cnfQojIHRvIGZpdCBhZ2FpbnN0IGEgcG9pc3NvbiBkaXN0cmlidXRpb24gdXNlICdwb2lzJwpzdW1tYXJ5KGZpdGRpc3QodHJhZGVfZGF0YSR0cmFkZV9jb3VudCwgZGlzdD0icG9pcyIpKQpwbG90KGZpdGRpc3QodHJhZGVfZGF0YSR0cmFkZV9jb3VudCwgZGlzdD0icG9pcyIpKQpgYGAKCldlIGNhbiB0cnkgdG8gZml0IGEgKk5lZ2F0aXZlIEJpbm9taWFsKiBkaXN0cmlidXRpb24uIFRoaXMgaXMgYSAqbXVjaCBiZXR0ZXIgZml0KiwgZXZlbiB0aG91Z2ggaXQgZG9lcyBub3QgY2FwdHVyZSB0aGUgcGVha3MgYXMgd2VsbCwgaXQgaXMgdGhlIGJlc3QgZml0IHNvIGZhci4gCgo8ZGl2IGNsYXNzID0gImJsdWUiPgoKSW4gbW9zdCBleHBlcmltZW50YWwgY29uZGl0aW9ucywgTmVnYXRpdmUgQmlub21pYWwgdGVuZHMgdG8gZml0IGRhdGEgYmV0dGVyLCBiZWNhdXNlIGl0IGlzIG1vcmUgZmxleGlibGUgKHdpdGggdHdvIHBhcmFtZXRlcnMgaW5zdGVhZCBvZiBQb2lzc29uJ3Mgc2luZ2xlIHBhcmFtZXRlcikuCgo8L2Rpdj4KCk9uY2UgYWdhaW4sIHRoZSBtZWFuIG9mIHRoZSBkaXN0cmlidXRpb24gaXMgdGhlIHNhbWUuIEJ1dCBiZWNhdXNlIHRoZSBuZWdhdGliZSBiaW5vbWlhbCBoYXMgdGhlIGV4dHJhICpzaXplKiAoZGlzcGVyc2lvbikgcGFyYW1ldGVycywgaXQgZml0cyB0aGUgZGF0YSBiZXR0ZXIuCgpgYGB7ciB0cmFkZSBmaXRkaXN0ciBuYmlub20sIGNsYXNzLnNvdXJjZSA9ICdmb2xkLXNob3cnfQojIHRvIGZpdCBhZ2FpbnN0IGEgbmVnYXRpdmUgYmlub21pYWwgZGlzdHJpYnV0aW9uIHVzZSAnbmJpbm9tJwpzdW1tYXJ5KGZpdGRpc3QodHJhZGVfZGF0YSR0cmFkZV9jb3VudCwgZGlzdD0ibmJpbm9tIikpCnBsb3QoZml0ZGlzdCh0cmFkZV9kYXRhJHRyYWRlX2NvdW50LCBkaXN0PSJuYmlub20iKSkKYGBgCgpJbiBhZGRpdGlvbiB0byB2aXN1YWwgY29tcGFyaXNvbiBvZiB0aGUgb3V0cHV0cyBmcm9tIHRoaXMgZnVuY3Rpb24sIHlvdSBjb3VsZCBhbHNvIGNvbXBhcmUgdGhlIExvZy1MaWtlbGlob29kcyBvZiB0aGUgb3V0cHV0cyBmcm9tIHRoZSBmdW5jdGlvbiAtIHRoZSBjbG9zZXIgdG8gemVybyB0aGUgYmV0dGVyLiBUaGUgTG9nLUxpa2VsaWhvb2Qgb2YgdGhlIG5lZ2F0aXZlIGJpbm9taWFsIGRpc3RyaWJ1dGlvbiBpcyBtdWNoIGNsb3NlciB0byB6ZXJvLl5bWW91IG1pZ2h0IGhhdmUgY29tZSBhY3Jvc3MgTG9nLUxpa2VsaWhvb2QgKExMKSBiZWZvcmUgaW4gbG9naXN0aWMgcmVncmVzc2lvbnMsIG9mdGVuIGNhbGxlZCAqZGV2aWFuY2UqLCBhbmQgaXQgaXMgdGhlIGJhc2UgdG8gY2FsY3VsYXRlIHBzZXVkby0kUl4yJCBtZWFzdXJlcyBzdWNoIGFzIE5hZ2Vsa2Vya2UuIExMIGlzIGdyZWF0IGZvciBjb21wYXJpbmcgYWNyb3NzIG1vZGVsIGZpdHMsIGJ1dCBiZSBjYXJlZnVsIGFzIGNlcnRhaW4gYXBwcm9hY2hlcyB1c2UgZGlmZmVyZW50IHdheXMgdG8gY2FsY3VsYXRlIExMIHdoaWNoIG1lYW5zIHRoZXkgbWlnaHQgbm90IGJlIGRpcmVjdGx5IGNvbXBhcmFibGUgc29tZXRpbWVzLl0KCkFub3RoZXIgdG9vbCBmb3IgaWRlbnRpZnlpbmcgeW91ciB0eXBlIG9mIGRpc3RyaWJ1dGlvbiBpcyB0aGlzICoqT3JkIHBsb3QqKiBuYW1lZCBhZnRlciBKLksuIE9yZC4gVGhpcyBmdW5jdGlvbiBpcyBpbXBsZW1lbnRlZCBpbiB0aGUgcGFja2FnZSBgdmNkYC4gQW4gdXB3YXJkIHRyZW5kaW5nIGxpbmUgc2hvd3MgYSBuZWdhdGl2ZSBiaW5vbWlhbCBkaXN0cmlidXRpb24uIEZvciBtb3JlIGluZm9ybWF0aW9uIG9uIGludGVycHJldGluZyB0aGlzIHBsb3QsIHRoZXJlIGFyZSBleGFtcGxlcyBpbiB0aGUgb3JpZ2luYWwgcGFwZXIgYnkgW09yZCAoMTk2NyldKGh0dHBzOi8vd3d3LmpzdG9yLm9yZy9zdGFibGUvMjM0MzQwMz9zZXE9MikuCgpgYGB7ciB0cmFkZSBvcmQgcGxvdH0KT3JkX3Bsb3QodHJhZGVfZGF0YSR0cmFkZV9jb3VudCkKYGBgCgojIyBOZWdhdGl2ZSBCaW5vbWlhbCBtb2RlbAoKVGhlcmUgYXJlIHNldmVyYWwgcGFja2FnZXMgYW5kIGZ1bmN0aW9ucyBpbiBSIGZvciBmaXR0aW5nIGNvdW50IGRhdGEgbW9kZWxzLiAKCkZvciBQb2lzc29uLCB0aGUgbW9zdCBjb21tb24gaXMgcHJvYmFibHkgdGhlIGBnbG1gIGZ1bmN0aW9uIGZyb20gdGhlIGJhc2UgcGFja2FnZSwgd2hpY2ggeW91IG1pZ2h0IGhhdmUgdXNlZCBpbiB0aGUgcGFzdCB0byBmaXQgbG9naXN0aWMgcmVncmVzc2lvbnMuIFlvdSBzaW1wbHkgbmVlZCB0byBzcGVjaWZ5IGBmYW1pbHkgPSAncG9pc3NvbidgLgoKRm9yIE5lZ2F0aXZlIEJpbm9taWFsLCB5b3UgY2FuIHVzZSB0aGUgZnVuY3Rpb24gYGdsbS5uYmAgZnJvbSB0aGUgcGFja2FnZSBgTUFTU2AuCgpBcyBvdXIgZGF0YSBtb3JlIGNsb3NlbHkgcmVzZW1ibGVzIGEgbmVnYXRpdmUgYmlub21pYWwsIHdlIHdpbGwgZml0IGEgbmVnYXRpdmUgYmlub21pYWwgZ2VuZXJhbGl6ZWQgbGluZWFyIHJlZ3Jlc3Npb24gdG8gb3VyIGRhdGEsIHdpdGggdGhlIHNhbWUgcHJlZGljdG9ycy4KCjxkaXYgY2xhc3MgPSAiYmx1ZSI+CgpFeHBvbmVudGlhdGluZyB0aGUgY29lZmZpY2llbnRzIGluIGNvdW50IHJlZ3Jlc3Npb25zICgkZV5cYmV0YSQpIGdpdmVzIHVzICoqSW5jaWRlbnQgUmF0ZSBSYXRpb3MqKiB3aGljaCBoYXMgYSBzaW1pbGFyIGludGVycHJldGF0aW9uIHRvIE9kZHMgUmF0aW9zIChPUnMpLiAKCi0gSVJScyBhYm92ZSAxIGluZGljYXRlIHBvc2l0aXZlIGNvcnJlbGF0aW9uIC0gaGlnaGVyIGluY2lkZW5jZXMgd2hlbiB0aGUgcHJlZGljdG9yIGdvZXMgdXA7Ci0gSVJScyBiZWxvdyAxIGluZGljYXRlIG5lZ2F0aXZlIGNvcnJlbGF0aW9uIC0gaGlnaGVyIGluY2lkZW5jZXMgd2hlbiB0aGUgcHJlZGljdG9yIGdvZXMgZG93bi4KCklSUnMgYXJlIG11bHRpcGxpY2F0aXZlIChub3QgYWRkaXRpdmUpIGFnYWluIHNpbWlsYXIgdG8gT1JzLgoKPC9kaXY+CgpXaXRoIGEgbmVnYXRpdmUgYmlub21pYWwgcmVncmVzc2lvbiwgdGhlICoqaW50ZXJhY3Rpb24gaXMgbm8gbG9uZ2VyIHNpZ25pZmljYW50KiouCgpgYGB7ciB0cmFkZSBuYiBtb2RlbCwgY2xhc3Muc291cmNlID0gJ2ZvbGQtc2hvdyd9CnRyYWRlLm0yLm5iIDwtIGdsbS5uYih0cmFkZV9jb3VudCB+IGV4cF9jb25kICogYWdlX2MsIGRhdGE9dHJhZGVfZGF0YSkgIyBydW4gdGhlIG5lZ2F0aXZlIGJpbm9taWFsIHJlZ3Jlc3Npb24KdGFiX21vZGVsKHRyYWRlLm0yLm5iLCBzaG93LnNlID0gVFJVRSwgY29sbGFwc2Uuc2UgPSBUUlVFLCBzaG93LmxvZ2xpayA9IFRSVUUpICMgc2hvdyB0aGUgcmVzdWx0cyBpbiBhIG5pY2UgdGFibGUKYGBgCgo8YnI+CgpBZ2FpbiB3ZSBzaW11bGF0ZSBhIHNlcmllcyBvZiAxMDAgcHJlZGljdGVkIGRhdGFzZXRzIGZyb20gb3VyIG1vZGVsIGFuZCBjb21wYXJlIGl0IHRvIHRoZSBvcmlnaW5hbCBkYXRhc2V0LiBJdCdzIGEgKiptdWNoIGJldHRlciBmaXQqKiAtIHRoZXJlIGFyZSBubyBwcmVkaWN0aW9ucyBiZWxvdyAwLiBUaGUgc2hhcGUgb2YgdGhlIGRpc3RyaWJ1dGlvbiBvZiBwcmVkaWN0aW9ucyAqKmNsb3NlbHkgbWF0Y2hlcyB0aGUgb3JpZ2luYWwgZGF0YSoqIGRpc3RyaWJ1dGlvbi4KCmBgYHtyIHRyYWRlIG5iIHByZWRpY3Rpb25zfQpwcmVkLnNhbXBsZXMgPC0gMToxMDAgIyBob3cgbWFueSBwcmVkaWN0aW9ucyBkbyB3ZSB3YW50PwojIHNpbXVsYXRlIHRoZSBwcmVkaWN0aW9ucwpwcmVkLmNvdW50IDwtIGRhdGEuZnJhbWUoZGF0YT0nUHJlZGljdGlvbiAoTmVnIEJpbiknLCB0cmFkZV9jb3VudD1hcy52ZWN0b3IoZG8uY2FsbChyYmluZCwgc2FwcGx5KHByZWQuc2FtcGxlcywgZnVuY3Rpb24oeCl7c2ltdWxhdGUodHJhZGUubTIubmIpfSkpKSkKIyBiaW5kIHRoZSBwcmVkaWN0aW9ucyB3aXRoIHRoZSByYXcgZGF0YSBpbiBhIHNpbmdsZSBkYXRhIGZyYW1lIGZvciBwbG90dGluZwpwcmVkLmNvdW50IDwtIHJiaW5kKHByZWQuY291bnQsIGRhdGEuZnJhbWUoZGF0YT0nT3JpZ2luYWwgRGF0YScsIHRyYWRlX2NvdW50PXRyYWRlX2RhdGEkdHJhZGVfY291bnQpKQoKIyBzaG93IGEgaGlzdG9ncmFtIG9mIHRoZSBzaW11bGF0ZWQgb3V0Y29tZXMgbmV4dCB0byB0aGUgb3JpZ2luYWwgcmF3IGRhdGEKZ2dwbG90KGRhdGE9cHJlZC5jb3VudCwgYWVzKHRyYWRlX2NvdW50LCBncm91cD1kYXRhKSkgKyBnZW9tX2hpc3RvZ3JhbShhZXMoeT1hZnRlcl9zdGF0KGRlbnNpdHkpKSwgZmlsbD0ibGlnaHRibHVlIiwgY29sb3IgPSAiYmxhY2siLCBiaW53aWR0aD0yKSArIHRoZW1lX2J3KCkgKyB4bGFiKCJUcmFkZSBDb3VudCIpICsgeWxhYigiJSBvZiBwYXJ0aWNpcGFudHMiKSArIGZhY2V0X3dyYXAofmRhdGEpICsgY29vcmRfY2FydGVzaWFuKHhsaW09YygwLDEwMCkpCmBgYAoKSWYgd2UgbG9vayBhdCB0aGUgdHdvIG1vZGVscyBzaWRlLWJ5LXNpZGUsIHdlIHNlZSB0aGF0IHdlIHdvdWxkIG1ha2UgZGlmZmVyZW50IGluZmVyZW5jZXMgZnJvbSBlYWNoIG1vZGVsIHdpdGggcmVnYXJkcyB0byB0aGUgaW50ZXJhY3Rpb25zLiBUaGUgY29lZmZpY2llbnRzIGFyZSBub3QgZGlyZWN0bHkgY29tcGFyYWJsZS4gQnV0IHdlIGNhbiBhc2sgUiB0byBnaXZlIHVzIHNvbWUgZXN0aW1hdGVkIG1hcmdpbmFsIG1lYW5zLgoKYGBge3IgdHJhZGUgbmIgbW9kZWwgY29tcGFyaXNvbn0KdGFiX21vZGVsKHRyYWRlLm0xLmxtLCB0cmFkZS5tMi5uYiwgc2hvdy5zZSA9IFRSVUUsIGNvbGxhcHNlLnNlID0gVFJVRSwgc2hvdy5sb2dsaWsgPSBUUlVFLCBkdi5sYWJlbHM9YygiZ2VuZXJhbCBsaW5lYXIgbW9kZWwiLCAibmVnYXRpdmUgYmlub21pYWwgbW9kZWwiKSkgIyBzaG93IHRoZSB0d28gbW9kZWxzIHNpZGUgYnkgc2lkZQpgYGAKCjxicj4KCldlIGNhbiBjb21wYXJlIHRoZSBwcmVkaWN0aW9ucyB0aGF0IHRoZSBtb2RlbCBtYWtlcyBvZiB0aGUgbWVhbnMgb2YgZWFjaCBleHBlcmltZW50YWwgY29uZGl0aW9uIHVzaW5nIHRoZSBmdW5jdGlvbnMgYGVtbWVhbnNgIGFuZCBgZW1taXBgLiAKCjxkaXYgY2xhc3MgPSAiYmx1ZSI+CgpUaGV5IGFyZSBjbG9zZSBpbiB0aGlzIGNhc2UgLSBnZW5lcmFsIGxpbmVhciByZWdyZXNzaW9ucyBhcmUgdmVyeSBnb29kIGF0IGZpbmRpbmcgdGhlIG1lYW5zIG9mIGRpc3RyaWJ1dGlvbiAtIHRoZSBkaWZmZXJlbmNlcyBpbiB0aGlzIGNhc2UgYXJlIGluIHRoZSAqKnN0YW5kYXJkIGVycm9ycyoqLiBUaGlzIHRyYW5zbGF0ZXMgaW50byBkaWZmZXJlbmNlcyBpbiB0aGUgKmVmZmVjdCBzaXplcyogYW5kICppbnRlcnByZXRhdGlvbiBvZiB0aGUgcmVzdWx0cyouCgpJZiB0aGVyZSB3ZXJlIGV4dHJlbWUgKHZlcnkgbGFyZ2UpIHZhbHVlcywgYXMgd2Ugd2lsbCBzZWUgbGF0ZXIsIHRoZSByZXN1bHRzIHdvdWxkIG5vdCBoYXZlIGJlZW4gc28gc2ltaWxhci4KCjwvZGl2PgoKYGBge3IgdHJhZGUgZW1tZWFuc30KIyBnZXQgdGhlIG1lYW5zIGZvciBlYWNoIGV4cGVyaW1lbnRhbCBjb25kaXRpb24sIGZvciBlYWNoIG1vZGVsCmVtbWVhbnMubG0gPC0gc3VtbWFyeShlbW1lYW5zKHRyYWRlLm0xLmxtLCB+ZXhwX2NvbmQsIHR5cGU9InIiKSkKZW1tZWFucy5sbSRtb2RlbCA8LSAibG0iICMgbmFtZSB0aGUgbW9kZWxzIGZvciB0aGUgcGxvdCBiZWxvdwplbW1lYW5zLm5iIDwtIHN1bW1hcnkoZW1tZWFucyh0cmFkZS5tMi5uYiwgfmV4cF9jb25kLCB0eXBlPSJyIikpCmVtbWVhbnMubmIkbW9kZWwgPC0gIm5iIgpjb2xuYW1lcyhlbW1lYW5zLm5iKSA8LSBjb2xuYW1lcyhlbW1lYW5zLmxtKSAjIHdlIG5lZWQgdGhlIGNvbHVtbiBuYW1lcyB0byBiZSB0aGUgc2FtZSBmb3IgdGhlIHJiaW5kIGJlbG93CgojIHBsb3QgdGhlIHJlc3VsdHMKZ2dwbG90KHJiaW5kKGVtbWVhbnMubG0sIGVtbWVhbnMubmIpLCBhZXMoeD1leHBfY29uZCwgeT1lbW1lYW4sIHltaW49bG93ZXIuQ0wsIHltYXg9dXBwZXIuQ0wsIGdyb3VwPW1vZGVsLCBjb2xvcj1tb2RlbCwgZmlsbD1tb2RlbCkpICsgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiLCB3aWR0aD0wLjUsIHBvc2l0aW9uPXBvc2l0aW9uX2RvZGdlKHdpZHRoPTAuNTUpLCBjb2xvcj0iYmxhY2siKSArIGdlb21fZXJyb3JiYXIod2lkdGg9MC4yLCBwb3NpdGlvbj1wb3NpdGlvbl9kb2RnZSh3aWR0aD0wLjU1KSwgY29sb3I9ImJsYWNrIikgKyB0aGVtZV9idygpICsgeGxhYigiZXhwZXJpbWVudGFsIGNvbmRpdGlvbiIpICsgeWxhYigiZXN0aW1hdGVkIG1hcmdpbmFsIG1lYW5zIikKCiMgZ2V0IHRoZSBzbG9wZXMgb2YgYWdlIGZvciBlYWNoIGV4cGVyaW1lbnRhbCBjb25kaXRpb24sIGZvciBlYWNoIG1vZGVsCmVtbWlwLmxtIDwtIGVtbWlwKHRyYWRlLm0xLmxtLCB+YWdlX2N8ZXhwX2NvbmQsIGF0PWxpc3QoYWdlX2M9YygtMjA6NDApKSwgQ0lzPVQsIHBsb3RpdCA9IEZBTFNFKQplbW1pcC5sbSRtb2RlbCA8LSAibG0iCmVtbWlwLm5iIDwtIGVtbWlwKHRyYWRlLm0yLm5iLCB+YWdlX2N8ZXhwX2NvbmQsIGF0PWxpc3QoYWdlX2M9YygtMjA6NDApKSwgQ0lzPVQsIHR5cGU9InIiLCBwbG90aXQgPSBGQUxTRSkKZW1taXAubmIkbW9kZWwgPC0gIm5iIgpjb2xuYW1lcyhlbW1pcC5uYikgPC0gY29sbmFtZXMoZW1taXAubG0pCgpnZ3Bsb3QocmJpbmQoZW1taXAubG0sIGVtbWlwLm5iKSwgYWVzKHg9YWdlX2MsIHk9eXZhciwgeW1pbj1MQ0wsIHltYXg9VUNMLCBncm91cD1tb2RlbCwgY29sb3I9bW9kZWwsIGZpbGw9bW9kZWwpKSArIGdlb21fbGluZShwb3NpdGlvbj1wb3NpdGlvbl9kb2RnZSh3aWR0aD0xKSwgbGluZXdpZHRoPTEpICsgZ2VvbV9yaWJib24ocG9zaXRpb249cG9zaXRpb25fZG9kZ2Uod2lkdGg9MSksIGFscGhhPTAuMikgKyB0aGVtZV9idygpICsgeGxhYigiY2VudGVyZWQgQWdlIGluIHllYXJzIikgKyB5bGFiKCJlc3RpbWF0ZWQgbWFyZ2luYWwgbWVhbnMiKSArIGZhY2V0X3dyYXAofmV4cF9jb25kKQoKYGBgCgpXZSBjYW4gbG9vayBhdCB0aGUgZGlhZ25vc3RpY3MgYWdhaW4sIGFuZCB3ZSBzZWUgKipubyBkZXZpYXRpb25zKiogd2l0aCB0aGUgbmVnYXRpdmUgYmlub21pYWwgbW9kZWwgKG5vdGhpbmcgaXMgaGlnaGxpZ2h0ZWQgaW4gcmVkLCBhcGFydCBmcm9tIGEgY291cGxlIG9mIHBvdGVudGlhbCBvdXRsaWVycykuCgpgYGB7ciBuYiBESEFSTWEsIGNsYXNzLnNvdXJjZSA9ICdmb2xkLXNob3cnfQpzaW0ubTIubmIgPC0gc2ltdWxhdGVSZXNpZHVhbHMoZml0dGVkTW9kZWwgPSB0cmFkZS5tMi5uYikKcGxvdChzaW0ubTIubmIsIHRpdGxlPSJSZXNpZHVhbCBkaWFnbm9zdGljcyBmb3IgbmVnYXRpdmUgYmlub21pYWwgbW9kZWwiKQpgYGAKCiMgUmVwZWF0ZWQtbWVhc3VyZXMgKE1peGVkIGVmZmVjdHMpIEV4YW1wbGUgCgpJbiB0aGlzIG5ldyBleGFtcGxlLCB0aGUgZGF0YSAoYHNhbXBsaW5nX2RhdGFgKSBjb21lcyBmcm9tIGEgZGVjaXNpb24tbWFraW5nIHN0dWR5LiAKCkJlZm9yZSBtYWtpbmcgYSBmaW5hbmNpYWxseS1jb25zZXF1ZW50aWFsIGRlY2lzaW9uIHRoYXQgYWZmZWN0ZWQgdGhlaXIgYm9udXMsIHBhcnRpY2lwYW50cyBjb3VsZCBmcmVlbHkgc2FtcGxlIGZyb20gdGhlIGF2YWlsYWJsZSBvcHRpb25zIHdpdGhvdXQgYW55IGNvc3RzIG9yIGNvbnNlcXVlbmNlcywgdG8gZGV0ZXJtaW5lIHdoaWNoIG9wdGlvbiB0aGV5IHByZWZlcnJlZC4gCgpUaGlzIGlzIGFnYWluIGNvdW50IGRhdGEsIGFzIGl0IGNvdW50cyB0aGUgbnVtYmVyIG9mIHNhbXBsZXMgdGFrZW4gYnkgZWFjaCBwYXJ0aWNpcGFudCBiZWZvcmUgZWFjaCBjaG9pY2UuIFRoZXkgY291bGQgc2FtcGxlIGZvciBhcyBsb25nIGFzIHRoZXkgbGlrZWQuIEVhY2ggcGFydGljaXBhbnQgbWFkZSAxOCBjaG9pY2VzLCBhbmQgdGhleSBjb3VsZCBzYW1wbGUgYSBkaWZmZXJlbnQgbnVtYmVyIG9mIHRpbWVzIGZvciBlYWNoIGNob2ljZS4KCmBgYHtyIHNhbXBsaW5nIGRhdGEgaGlzdG9ncmFtfQojIHBsb3QgdGhlIGhpc3RvZ3JhbSBvZiBzYW1wbGUgY291bnQKZ2dwbG90KHNhbXBsaW5nX2RhdGEsIGFlcyh4ID0gc2FtcGxlX2NvdW50KSkgKyBnZW9tX2hpc3RvZ3JhbShmaWxsPSJsaWdodGJsdWUiLCBjb2xvcj0iYmxhY2siLCBiaW53aWR0aD0xKSArIHRoZW1lX2J3KCkgKyBnZ3RpdGxlKCJOdW1iZXIgb2Ygc2FtcGxlcyB0YWtlbiBiZWZvcmUgZWFjaCBjaG9pY2UiKQpgYGAKCkl0J3Mgbm90IGltbWVkaWF0ZWx5IG9idmlvdXMsIGJ1dCB0aGUgcGVhayBvZiB0aGUgZGF0YSAoYW5kIGFsc28gdGhlIGxvd2VzdCBjb3VudCBvZiBzYW1wbGVzKSBpcyBub3QgemVybywgYnV0IGluc3RlYWQsIGl0IGlzIDIuIFRoYXQgaXMgYmVjYXVzZSBwYXJ0aWNpcGFudHMgd2VyZSByZXF1aXJlZCB0byBzYW1wbGUgYXQgbGVhc3QgdHdpY2UgZm9yIGVhY2ggY2hvaWNlIHRoZXkgbWFkZSwgYWx0aG91Z2ggdGhlcmUgd2FzIG5vIHVwcGVyIGJvdW5kLgoKPGRpdiBjbGFzcyA9ICJibHVlIj4KCldpdGggY291bnQgZGF0YSBpdCdzIGFsd2F5cyBiZXN0IHRvIGhhdmUgdGhlIGRhdGEgc3RhcnRpbmcgZnJvbSB6ZXJvIGlmIHRoZXJlIHdhcyBzb21lIHN0cnVjdHVyYWwgY2hhcmFjdGVyaXN0aWMgdGhhdCBsaW1pdGVkIHRoZSBtaW5pbXVtIGNvdW50LiBXZSBjcmVhdGUgYSBuZXcgdmFyaWFibGUsICpleGNlc3Mgc2FtcGxlIGNvdW50Kiwgd2hpY2ggY291bnRzIGhvdyBtYW55IHNhbXBsZXMgcGFydGljaXBhbnRzIHRvb2sgKmluIGV4Y2Vzcyogb2YgdGhlIG1pbmltdW0uIFRoaXMgd2FzIGNhbGN1bGF0ZWQgYnkgc3VidHJhY3RpbmcgMiBmcm9tIGVhY2ggc2FtcGxlIGNvdW50LgoKPC9kaXY+CgpOb3cgdGhlIGRhdGEgc3RhcnRzIGZyb20gemVybywgd2hpY2ggd2lsbCBoZWxwIGl0IG1vcmUgY2xvc2VseSBtYXRjaCBhIGNvdW50IGRpc3RyaWJ1dGlvbi4KCmBgYHtyIHNhbXBsZSBleGNlc3Mgc2FtcGxlIGNvdW50LCBjbGFzcy5zb3VyY2UgPSAnZm9sZC1zaG93J30KIyBzdWJ0cmFjdCB0d28gdG8gY3JlYXRlIGV4Y2VzcyBzYW1wbGUgY291bnQKc2FtcGxpbmdfZGF0YSRleGNlc3Nfc2FtcGxlX2NvdW50IDwtIHNhbXBsaW5nX2RhdGEkc2FtcGxlX2NvdW50IC0gMgojIHBsb3QgdGhlIG5ldyBleGNlc3Mgc2FtcGxlIGNvdW50CmdncGxvdChzYW1wbGluZ19kYXRhLCBhZXMoeCA9IGV4Y2Vzc19zYW1wbGVfY291bnQpKSArIGdlb21faGlzdG9ncmFtKGZpbGw9ImxpZ2h0Ymx1ZSIsIGNvbG9yPSJibGFjayIsIGJpbndpZHRoPTEpICsgdGhlbWVfYncoKSArIGdndGl0bGUoIk51bWJlciBvZiBleGNlc3Mgc2FtcGxlcyB0YWtlbiBiZWZvcmUgZWFjaCBjaG9pY2UiKQpgYGAgCgpBZ2FpbiB0aGlzIGRhdGEgaXMgY2xlYXJseSBub3Qgbm9ybWFsbHkgZGlzdHJpYnV0ZWQsIGJ1dCBpbnN0ZWFkIGl0IHNlZW1zIHRvIGZvbGxvdyBhIG5lZ2F0aXZlIGJpbm9taWFsIGRpc3RyaWJ1dGlvbi4KCmBgYHtyIHNhbXBsZSBmaXRkaXN0fQojIGNvbXBhcmUgdHJhZGVfY291bnQgZGF0YSB3aXRoIGEgbm9ybWFsIGRpc3RyaWJ1dGlvbgpzdW1tYXJ5KGZpdGRpc3Qoc2FtcGxpbmdfZGF0YSRleGNlc3Nfc2FtcGxlX2NvdW50LGRpc3Q9Im5vcm0iKSkKcGxvdChmaXRkaXN0KHNhbXBsaW5nX2RhdGEkZXhjZXNzX3NhbXBsZV9jb3VudCxkaXN0PSJub3JtIikpCgojIGNvbXBhcmUgdHJhZGVfY291bnQgZGF0YSB3aXRoIGEgbmVnYXRpdmUgYmlub21pYWwgZGlzdHJpYnV0aW9uCnN1bW1hcnkoZml0ZGlzdChzYW1wbGluZ19kYXRhJGV4Y2Vzc19zYW1wbGVfY291bnQsZGlzdD0ibmJpbm9tIikpCnBsb3QoZml0ZGlzdChzYW1wbGluZ19kYXRhJGV4Y2Vzc19zYW1wbGVfY291bnQsZGlzdD0ibmJpbm9tIikpCmBgYAoKQmVjYXVzZSB0aGlzIHdhcyAqd2l0aGluLXN1YmplY3RzIChyZXBlYXRlZCBtZWFzdXJlcykqIHdpdGggZWFjaCBwYXJ0aWNpcGFudCBtYWtpbmcgMTggY2hvaWNlcyAoYW5kIGVhY2ggY2hvaWNlIGFzc29jaWF0ZWQgd2l0aCBpdHMgb3duIHNhbXBsaW5nKSB3ZSBuZWVkIHRvIGZpdCBhICptaXhlZCBlZmZlY3RzIG1vZGVsKiB0byB0aGlzIGRhdGEuIAoKVGhlIGJlc3QgcGFja2FnZSBmb3IgdGhpcyBpcyBgZ2xtbVRNQmAgd2hpY2ggaXMgZmxleGlibGUgZW5vdWdoIHRvIGFsbG93IGZvciBzZXZlcmFsIGRpZmZlcmVudCBkaXN0cmlidXRpb25zLCBpbmNsdWRpbmcgbmVnYXRpdmUgYmlub21pYWwuXltEb24ndCBhc2sgbWUgd2h5IHRoZXJlIGFyZSB0aHJlZSBkaWZmZXJlbnQgbmVnYXRpdmUgYmlub21pYWwgZmFtaWxpZXMgaW4gZ2xtbVRNQiAoYG5iaW5vbTFgLCBgbmJpbm9tMmAsIGFuZCBgbmJpbm9tMTJgKS4gVGhlIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGVtIHNlZW0gbWluaW1hbCAtIGFuZCBtb3N0bHkgcGhpbG9zb3BoaWNhbCBhYm91dCBob3cgdG8gZGVmaW5lIHZhcmlhbmNlLiBJJ20gbm90IHN1cmUgd2hpY2ggb25lIGlzIHRoZSAiYmVzdCIgb3Igd2h5LiBJIG9mdGVuIGp1c3QgdXNlIHRoZSBmaXJzdCBvbmUgYW5kIGlmIGl0IHRocm93cyBtZSBhbiBlcnJvciBJIHRyeSBvbmUgb2YgdGhlIG90aGVyIG9uZXMuIEFsc28gYW5ub3lpbmdseSwgbm90ZSB0aGF0IGRpZmZlcmVudCBmdW5jdGlvbnMgY2FsbCB0aGUgZGlzdHJpYnV0aW9ucyBkaWZmZXJlbnQgbmFtZXMsIHN1Y2ggYXMgYG5iaW5vbWAgdnMgYG5lZ2Jpbm9tYCBhbmQgYHBvaXNzb25gIHZzIGBwb2lzYC4gQWx3YXlzIGNoZWNrIHRoZSBoZWxwIGZvciBlYWNoIGZ1bmN0aW9uLl0gCgpUaGVyZSB3ZXJlICp0d28gZXhwZXJpbWVudGFsIGNvbmRpdGlvbnMqIGBleHBjb25kYCwgYW5kIHdlIGFsc28gYWRkIHR3byBjb250aW51b3VzIHByZWRpY3RvcnM6ICphZ2UqIGFuZCAqY2hvaWNlIG51bWJlciogKGJvdGggY2VudGVyZWQpLiBDaG9pY2UgbnVtYmVyIHdhcyB0aGUgc2VxdWVudGlhbCBjaG9pY2UgdGhleSBtYWRlLCBmcm9tIDEgdG8gMTgsIHRvIHNlZSBpZiBzYW1wbGluZyBiZWhhdmlvdXIgY2hhbmdlZCBhcyB0aGV5IG1hZGUgbW9yZSBjaG9pY2VzIGFuZCB0aGUgdGFzayBwcm9ncmVzc2VkLiBUaGVyZSBpcyBhIHJhbmRvbSBpbnRlcmNlcHQgZm9yIGVhY2ggcGFydGljaXBhbnQuIFRoZSByYW5kb20gZWZmZWN0cyBhcmUgc3BlY2lmaWVkIHVzaW5nIHRoZSBzYW1lIGZvcm1hdCBhcyB0aGUgbW9yZSB0cmFkaXRpb25hbCBgbG1lcmAgb3IgYGdsbWVyYCBmdW5jdGlvbnMuCgo8ZGl2IGNsYXNzID0gImJsdWUiPgoKV2UgY29tcGFyZSB0aGUgb3V0cHV0cyB3aXRoIGFuIG9yZGluYXJ5IGxpbmVhciByZWdyZXNzaW9uLiBUaGUgY29uY2x1c2lvbnMgd291bGQgaGF2ZSBiZWVuIGRpZmZlcmVudC4gV2UgY2FuIHNlZSB0aGF0IG9uZSBvZiB0aGUgcHJlZGljdG9ycyAoZXhwZXJpbWVudGFsIGNvbmRpdGlvbikgaXMgc2lnbmlmaWNhbnQgd2l0aCB0aGUgbmVnYXRpdmUgYmlub21pYWwgcmVncmVzc2lvbiwgYnV0IG5vdCB3aXRoIHRoZSBvcmRpbmFyeSBsaW5lYXIgcmVncmVzc2lvbi4KCjwvZGl2PgoKYGBge3Igc2FtcGxlIGdsbW1UTUIsIGNsYXNzLnNvdXJjZSA9ICdmb2xkLXNob3cnfQpjb250cmFzdHMoc2FtcGxpbmdfZGF0YSRleHBjb25kKSA8LSBjb250ci5zdW0oMikgIyB6ZXJvLXN1bSBjb250cmFzdCBjb2RlCnNhbXBsaW5nX2RhdGEkY2hvaWNlX251bWJlcl9jIDwtIHNjYWxlKHNhbXBsaW5nX2RhdGEkY2hvaWNlX251bWJlciwgc2NhbGU9RkFMU0UpICMgY2VudHJlIHRoZSBjaG9pY2UgbnVtYmVyCnNhbXBsaW5nX2RhdGEkYWdlX2MgPC0gc2NhbGUoc2FtcGxpbmdfZGF0YSRhZ2UsIHNjYWxlPUZBTFNFKSAjIGNlbnRyZSB0aGUgYWdlCgojIHJ1biBhbiBvcmRpbmFyeSBtaXhlZCBlZmZlY3RzIGxpbmVhciByZWdyZXNzaW9uCnNhbXBsZS5tMS5sbSA8LSBsbWVyKGV4Y2Vzc19zYW1wbGVfY291bnQgfiBleHBjb25kICsgYWdlX2MgKyBjaG9pY2VfbnVtYmVyX2MgKyAoMXxwcHRpZCksIGRhdGE9c2FtcGxpbmdfZGF0YSkKCiMgcnVuIGEgbmVnYXRpdmUgYmlub21pYWwgbWl4ZWQgZWZmZWN0cyByZWdyZXNzaW9uCnNhbXBsZS5tMi5uYiA8LSBnbG1tVE1CKGV4Y2Vzc19zYW1wbGVfY291bnQgfiBleHBjb25kICsgYWdlX2MgKyBjaG9pY2VfbnVtYmVyX2MgKyAoMXxwcHRpZCksIGRhdGE9c2FtcGxpbmdfZGF0YSwgZmFtaWx5PSJuYmlub20xIikKCnRhYl9tb2RlbChzYW1wbGUubTEubG0sIHNhbXBsZS5tMi5uYiwgc2hvdy5zZSA9IFRSVUUsIGNvbGxhcHNlLnNlID0gVFJVRSwgc2hvdy5sb2dsaWsgPSBUUlVFLCBzaG93LmljYz1GQUxTRSwgc2hvdy5yZS52YXIgPSBGQUxTRSwgZHYubGFiZWxzPWMoIm9yZGluYXJ5IG1peGVkLWVmZmVjdHMgbGluZWFyIG1vZGVsIiwgIm5lZ2F0aXZlIGJpbm9taWFsIG1peGVkLWVmZmVjdHMgbW9kZWwiKSkKYGBgCgpXaGVuIHdlIGNvbXBhcmUgdGhlIG1vZGVsIG91dHB1dHMsIHdlIHNlZSBhICoqbGFyZ2UgZGlmZmVyZW5jZSBpbiB0aGUgZXN0aW1hdGUgb2YgdGhlIHNhbXBsZSBjb3VudCoqLiBUaGlzIGlzIGJlY2F1c2UgdGhlIG9yZGluYXJ5IGxpbmVhciBtb2RlbCBpcyBoZWF2aWx5IGluZmx1ZW5jZWQgYnkgc29tZSAqKnZlcnkgbGFyZ2Ugc2FtcGxlIGNvdW50cyoqLCB3aGljaCBwdXNoIHRoZSBhdmVyYWdlIHVwLgoKPGRpdiBjbGFzcyA9ICJibHVlIj4KCkJlY2F1c2Ugb2YgdGhlc2UgZXh0cmVtZSB2YWx1ZXMsIHRoZSBlc3RpbWF0ZWQgbWFyZ2luYWwgbWVhbnMgZnJvbSBjb3VudCBtb2RlbHMgY2FuIGJlIHNpZ25pZmljYW50bHkgZGlmZmVyZW50IGZyb20gdGhlIGFjdHVhbCBtZWFucyBvZiB0aGUgdW5kZXJseWluZyBkYXRhLCBhbmQgd2lsbCBiZSBjbG9zZXIgdG8gdGhlIG1lZGlhbiwgd2hpY2ggd2FzIGByIG1lZGlhbihzYW1wbGluZ19kYXRhJGV4Y2Vzc19zYW1wbGVfY291bnQpYC4gSW4gY29tcGFyaXNvbiwgdGhlIGVzdGltYXRlZCBtYXJnaW5hbCBtZWFucyBmcm9tIGdlbmVyYWwgbGluZWFyIG1vZGVscyB3aWxsIGJlIGNsb3NlciB0byB0aGUgbWVhbiwgd2hpY2ggd2FzIGByIHJvdW5kKG1lYW4oc2FtcGxpbmdfZGF0YSRleGNlc3Nfc2FtcGxlX2NvdW50KSwyKWAuCgo8L2Rpdj4KCmBgYHtyIHNhbXBsZSBlbW1lYW5zfQojIGVtbWVhbnMgaXMgdXNlZCB0byBjcmVhdGUgZXN0aW1hdGVkIG1hcmdpbmFsIG1lYW5zIGZvciBlYWNoIGV4cGVyaW1lbnRhbCBjb25kaXRpb24KIyB3ZSBjYWxjdWxhdGUgdGhlc2UgZm9yIGVhY2ggbW9kZWwKZW1tZWFucy5sbSA8LSBzdW1tYXJ5KGVtbWVhbnMoc2FtcGxlLm0xLmxtLCB+ZXhwY29uZCwgdHlwZT0iciIsIGxtZXIuZGYgPSAiYXN5bXAiKSkKZW1tZWFucy5sbSRtb2RlbCA8LSAibG0iCmVtbWVhbnMubmIgPC0gc3VtbWFyeShlbW1lYW5zKHNhbXBsZS5tMi5uYiwgfmV4cGNvbmQsIHR5cGU9InIiLCBsbWVyLmRmID0gImFzeW1wIikpCmVtbWVhbnMubmIkbW9kZWwgPC0gIm5iIgpjb2xuYW1lcyhlbW1lYW5zLm5iKSA8LSBjb2xuYW1lcyhlbW1lYW5zLmxtKQoKZ2dwbG90KHJiaW5kKGVtbWVhbnMubG0sIGVtbWVhbnMubmIpLCBhZXMoeD1leHBjb25kLCB5PWVtbWVhbiwgeW1pbj1hc3ltcC5MQ0wsIHltYXg9YXN5bXAuVUNMLCBncm91cD1tb2RlbCwgY29sb3I9bW9kZWwsIGZpbGw9bW9kZWwpKSArIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5Iiwgd2lkdGg9MC41LCBwb3NpdGlvbj1wb3NpdGlvbl9kb2RnZSh3aWR0aD0wLjU1KSwgY29sb3I9ImJsYWNrIikgKyBnZW9tX2Vycm9yYmFyKHdpZHRoPTAuMiwgcG9zaXRpb249cG9zaXRpb25fZG9kZ2Uod2lkdGg9MC41NSksIGNvbG9yPSJibGFjayIpICsgdGhlbWVfYncoKSArIHhsYWIoImV4cGVyaW1lbnRhbCBjb25kaXRpb24iKSArIHlsYWIoImVzdGltYXRlZCBtYXJnaW5hbCBtZWFucyIpCgojIGVtbWlwIGlzIHVzZWQgdG8gY3JlYXRlIHJlZ3Jlc3Npb24gbGluZXMgZm9yIGVhY2ggcHJlZGljdG9yCmVtbWlwLmxtIDwtIGVtbWlwKHNhbXBsZS5tMS5sbSwgfmFnZV9jLCBhdD1saXN0KGFnZV9jPWMoLTIwOjQwKSksIENJcz1ULCBwbG90aXQgPSBGQUxTRSwgbG1lci5kZiA9ICJhc3ltcCIpCmVtbWlwLmxtJG1vZGVsIDwtICJsbSIKZW1taXAubmIgPC0gZW1taXAoc2FtcGxlLm0yLm5iLCB+YWdlX2MsIGF0PWxpc3QoYWdlX2M9YygtMjA6NDApKSwgQ0lzPVQsIHR5cGU9InIiLCBwbG90aXQgPSBGQUxTRSwgbG1lci5kZiA9ICJhc3ltcCIpCmVtbWlwLm5iJG1vZGVsIDwtICJuYiIKCmdncGxvdChyYmluZChlbW1pcC5sbSwgZW1taXAubmIpLCBhZXMoeD1hZ2VfYywgeT15dmFyLCB5bWluPUxDTCwgeW1heD1VQ0wsIGdyb3VwPW1vZGVsLCBjb2xvcj1tb2RlbCwgZmlsbD1tb2RlbCkpICsgZ2VvbV9saW5lKHBvc2l0aW9uPXBvc2l0aW9uX2RvZGdlKHdpZHRoPTEpLCBsaW5ld2lkdGg9MSkgKyBnZW9tX3JpYmJvbihwb3NpdGlvbj1wb3NpdGlvbl9kb2RnZSh3aWR0aD0xKSwgYWxwaGE9MC4yKSArIHRoZW1lX2J3KCkgKyB4bGFiKCJDZW50ZXJlZCBhZ2UgaW4geWVhcnMiKSArIHlsYWIoImVzdGltYXRlZCBtYXJnaW5hbCBtZWFucyIpCgplbW1pcC5sbSA8LSBlbW1pcChzYW1wbGUubTEubG0sIH5jaG9pY2VfbnVtYmVyX2MsIGF0PWxpc3QoY2hvaWNlX251bWJlcl9jPWMoLTEwOjEwKSksIENJcz1ULCBwbG90aXQgPSBGQUxTRSwgbG1lci5kZiA9ICJhc3ltcCIpCmVtbWlwLmxtJG1vZGVsIDwtICJsbSIKZW1taXAubmIgPC0gZW1taXAoc2FtcGxlLm0yLm5iLCB+Y2hvaWNlX251bWJlcl9jLCBhdD1saXN0KGNob2ljZV9udW1iZXJfYz1jKC0xMDoxMCkpLCBDSXM9VCwgdHlwZT0iciIsIHBsb3RpdCA9IEZBTFNFLCBsbWVyLmRmID0gImFzeW1wIikKZW1taXAubmIkbW9kZWwgPC0gIm5iIgoKZ2dwbG90KHJiaW5kKGVtbWlwLmxtLCBlbW1pcC5uYiksIGFlcyh4PWNob2ljZV9udW1iZXJfYywgeT15dmFyLCB5bWluPUxDTCwgeW1heD1VQ0wsIGdyb3VwPW1vZGVsLCBjb2xvcj1tb2RlbCwgZmlsbD1tb2RlbCkpICsgZ2VvbV9saW5lKHBvc2l0aW9uPXBvc2l0aW9uX2RvZGdlKHdpZHRoPTEpLCBsaW5ld2lkdGg9MSkgKyBnZW9tX3JpYmJvbihwb3NpdGlvbj1wb3NpdGlvbl9kb2RnZSh3aWR0aD0xKSwgYWxwaGE9MC4yKSArIHRoZW1lX2J3KCkgKyB4bGFiKCJDZW50ZXJlZCBDaG9pY2UgbnVtYmVyIikgKyB5bGFiKCJlc3RpbWF0ZWQgbWFyZ2luYWwgbWVhbnMiKSAKCmBgYAoKIyBaZXJvIGluZmxhdGlvbiAKCkluIHRoaXMgZmluYWwgZXhhbXBsZSwgdGhlIGRhdGEgKGB6aV9kYXRhYCkgY29tZXMgZnJvbSBhIHN0dWR5IG9uIGdhbWJsaW5nLl5bVGhpcyBkYXRhIGlzIGFkYXB0ZWQgZnJvbSBhIHJlYWwgc3R1ZHksIGJ1dCB0aGUgYWN0dWFsIGRhdGEgaGFzIGJlZW4gdHdlYWtlZCBzbGlnaHRseSB0byBzaW1wbGlmeSB0aGlzIGV4YW1wbGUuIEluIHRoZSBhY3R1YWwgZGF0YXNldCB0aGVyZSB3YXMgbm8gY29ycmVsYXRpb24gYmV0d2VlbiBzd2l0Y2ggY291bnQgYW5kIFBHU0kuXSAKClBhcnRpY2lwYW50cyB3ZXJlIGdpdmVuIMKjMyBhbmQgYWxsb3dlZCB0byBwbGFjZSBhcyBtYW55IGJldHMgYXMgdGhleSB3YW50ZWQgYWNyb3NzIGZvdXIgc2ltdWxhdGVkIHNsb3QgbWFjaGluZXMuIFdlIG1lYXN1cmVkIGhvdyBvZnRlbiBwYXJ0aWNpcGFudHMgc3dpdGNoZWQgYmV0d2VlbiB0aGUgZGlmZmVyZW50IG1hY2hpbmVzLiBUaGV5IGRpZCBub3QgaGF2ZSB0byBzd2l0Y2ggYXQgYWxsLCBhbmQgY291bGQgc3dpdGNoIGFzIG1hbnkgdGltZXMgYXMgdGhleSB3YW50ZWQuIFdlIGFsc28gbWVhc3VyZWQgdGhlIHBhcnRpY2lwYW50cycgUEdTSSwgd2hpY2ggaXMgdGhlIHByb2JsZW0gZ2FtYmxpbmcgc2V2ZXJpdHkgaW5kZXg6IGhpZ2hlciBzY29yZXMgaW4gdGhlIFBHU0kgY29ycmVsYXRlIHdpdGggbW9yZSBkaXNvcmRlcmVkIGdhbWJsaW5nIGFuZCBtb3JlIGhhcm0gZnJvbSBnYW1ibGluZy4KCjxkaXYgY2xhc3MgPSAiYmx1ZSI+CgpXaGF0IHdlIGhhdmUgaGVyZSBpcyB3aGF0IHdlIGNhbGwgKip6ZXJvLWluZmxhdGVkKiogZGF0YSwgb3IgZGF0YSB3aGljaCBoYXMgbW9yZSB6ZXJvIG9ic2VydmF0aW9ucyB0aGFuIHdlIHdvdWxkIGV4cGVjdC4gVGhlc2UgYXJlIHRoZSBwYXJ0aWNpcGFudHMgdGhhdCBkaWQgbm90IHN3aXRjaCBiZXR3ZWVuIHNsb3QgbWFjaGluZXMgYW5kIHBsYWNlZCBhbGwgdGhlaXIgYmV0cyB3aXRoIGEgc2luZ2xlIG1hY2hpbmUuIAoKWmVyby1pbmZsYXRpb24gY291bnQgZGF0YSBvZnRlbiBoYXMgdHdvIHBlYWtzLCBvbmUgYXQgemVybywgdGhlbiBhIGRpcCBpbiByZXNwb25zZXMsIGFuZCBhbm90aGVyIHBlYWsgbGF0ZXIgb24sIGFzIHdlIHNlZSBoZXJlLiBJZiB0aGVyZSBpcyBhIHNpbmdsZSBwZWFrLCB0aGVyZSBtaWdodCBub3QgYmUgemVybyBpbmZsYXRpb24gYXQgYWxsLgoKPC9kaXY+CgpgYGB7ciB6aSBkYXRhIGhpc3RvZ3JhbX0KIyBwbG90IHRoZSBoaXN0b2dyYW0gb2YgbnVtYmVyIG9mIHN3aXRjaGVzCmdncGxvdCh6aV9kYXRhLCBhZXMoeCA9IHN3aXRjaF9jb3VudCkpICsgZ2VvbV9oaXN0b2dyYW0oZmlsbD0ibGlnaHRibHVlIiwgY29sb3I9ImJsYWNrIiwgYmlud2lkdGg9MSkgKyB0aGVtZV9idygpICsgZ2d0aXRsZSgiTnVtYmVyIG9mIHN3aXRjaGVzIGJldHdlZW4gdGhlIGRpZmZlcmVudCBzbG90IG1hY2hpbmVzIikKYGBgCgpJbiBmYWN0LCBgciByb3VuZChucm93KHN1YnNldCh6aV9kYXRhLCBzd2l0Y2hfY291bnQ9PTApKS9ucm93KHppX2RhdGEpLDIpKjEwMGAlIG9mIHBhcnRpY2lwYW50cyBkaWQgbm90IHN3aXRjaCBhdCBhbGwuCgpXaGVuIHdlIGNvbXBhcmUgdGhpcyBkYXRhIHRvIGEgbm9ybWFsIGRpc3RyaWJ1dGlvbiBhbmQgdG8gYSBuZWdhdGl2ZSBiaW5vbWlhbCBkaXN0cmlidXRpb24sIHdlIGNhbiBzZWUgdGhlIGV4Y2VzcyB6ZXJvIHJlc3BvbnNlcy4gVGhlIG5lZ2F0aXZlIGJpbm9taWFsIGRpc3RyaWJ1dGlvbiBmaXRzIHRoZSBkYXRhIHNsaWdodGx5IGJldHRlciBidXQgaXQncyBzdGlsbCBxdWl0ZSBkaWZmZXJlbnQsIGJlY2F1c2Ugb2YgdGhlIGV4Y2VzcyB6ZXJvcy4gCgpgYGB7ciB6aSBmaXRkaXN0fQojIGNvbXBhcmUgbnVtYmVyIG9mIHN3aXRjaGVzIGRhdGEgd2l0aCBhIG5vcm1hbCBkaXN0cmlidXRpb24Kc3VtbWFyeShmaXRkaXN0KHppX2RhdGEkc3dpdGNoX2NvdW50LGRpc3Q9Im5vcm0iKSkKcGxvdChmaXRkaXN0KHppX2RhdGEkc3dpdGNoX2NvdW50LGRpc3Q9Im5vcm0iKSkKCiMgY29tcGFyZSBudW1iZXIgb2Ygc3dpdGNoZXMgZGF0YSB3aXRoIGEgbmVnYXRpdmUgYmlub21pYWwgZGlzdHJpYnV0aW9uCmZpdF9vdXRwdXRfbmIgPC0gZml0ZGlzdCh6aV9kYXRhJHN3aXRjaF9jb3VudCxkaXN0PSJuYmlub20iKQpzdW1tYXJ5KGZpdF9vdXRwdXRfbmIpCnBsb3QoZml0X291dHB1dF9uYikKYGBgCgpXaGF0IHdlIGhhdmUgaGVyZSBpcyBhIGNvdW50IGRpc3RyaWJ1dGlvbiB3aXRoIGEgcmVsYXRpdmVseSBoaWdoIG1lYW4gLS0gaWYgeW91IGlnbm9yZSB0aGUgZXhjZXNzaXZlIHplcm9zLiAKClRoZSBmaXQgZnVuY3Rpb24gaW4gUiB0cmllcyB0byBjb21wZW5zYXRlIGJ5IGZpdHRpbmcgYSBkaXN0cmlidXRpb24gd2l0aCBhIHNtYWxsZXIgbWVhbiAobXUpIGFuZCBsYXJnZXIgdmFyaWFuY2UgKHNpemUpIHRvIGFjY291bnQgZm9yIHRoZSBleGNlc3NpdmUgemVyb3MuIEhvd2V2ZXIsIGl0J3Mgbm90IGEgdmVyeSBnb29kIGZpdC4gQWNjb3VudGluZyBmb3IgdGhlIGV4Y2Vzc2l2ZSB6ZXJvcyBhbmQgYWxsb3dpbmcgZm9yIGEgbGFyZ2VyIG1lYW4gd291bGQgZml0IHRoaXMgZGF0YSBiZXR0ZXIuCgojIyBUZXN0aW5nIGZvciB6ZXJvLWluZmxhdGlvbgoKPGRpdiBjbGFzcyA9ICJibHVlIj4KCldlIGNhbiB0ZXN0IGlmIHRoZSBkYXRhIGhhcyB6ZXJvIGluZmxhdGlvbiBpbiBhIGZldyBkaWZmZXJlbnQgd2F5cy4gCgo8L2Rpdj4KCiMjIyBGaXQgYSB6ZXJvLWluZmxhdGVkIGRpc3RyaWJ1dGlvbgoKRmlyc3QsIHdlIGNhbiB0cnkgdGhlIGBmaXRkaXN0YCBmdW5jdGlvbiBieSBzcGVjaWZ5aW5nIHRoZSBgWklOQklgIGRpc3RyaWJ1dGlvbiBmcm9tIHBhY2thZ2UgYGdhbWxzc2AuIAoKSG93ZXZlciwgdGhpcyBpcyBhbHdheXMgY2x1bmt5IGFuZCBsaWtlcyB0byB0aHJvdyBtYW55IHdhcm5pbmdzIGFuZCBlcnJvcnMuIEJ1dCBpbiB0aGlzIGNhc2UsIHRoZSBBSUMgaXMgc21hbGxlciBhbmQgaXQgc2VlbXMgdG8gZml0IGJldHRlciB0aGFuIGEgcmVndWxhciBuZWdhdGl2ZSBiaW5vbWlhbCBkaXN0cmlidXRpb24uIFlvdSBjYW4gYWxzbyBzZWUgaG93IGl0IG5vdyBlc3RpbWF0ZWQgYSBoaWdoZXIgbWVhbiAobXUpLiAKCipOdSogaXMgdGhlIHByb2JhYmlsaXR5IG9mIHJlc3BvbmRpbmcgd2l0aCBhIHplcm8sIG9yIHRoZSB6ZXJvLWluZmxhdGlvbiBwYXJhbWV0ZXIsIHdoaWNoIHdhcyAwLjIwOC4gVGhlIHplcm8taW5mbGF0ZWQgZGlzdHJpYnV0aW9uIGFzc3VtZXMgdGhhdCB0aGVyZSB3ZXJlIGFyb3VuZCAyMSUgZXhjZXNzaXZlIHplcm9zLgoKYGBge3IgemkgZml0ZGlzdCB6aW5iLCB3YXJuaW5nPUZBTFNFLCBjbGFzcy5zb3VyY2UgPSAnZm9sZC1zaG93J30KIyBUbyBydW4gYSBaSU5CSSBmaXQgeW91IG5lZWQgdG8gc3BlY2lmeSBzdGFydGluZyBwb2ludHMgZm9yIHRoZSBkaWZmZXJlbnQgcGFyYW1ldGVycywgYXMgd2VsbCBhcyBib3VuZHMgZm9yIHRoZSBwYXJhbWV0ZXJzLiBTb21ldGltZXMgeW91IG5lZWQgdG8gdHJ5IGRpZmZlcmVudCBzdGFydGluZyBwYXJhbWV0ZXJzIHVudGlsIGl0IGZpdHMuCmZpdF9vdXRwdXRfemluYmkgPC0gZml0ZGlzdCh6aV9kYXRhJHN3aXRjaF9jb3VudCwgZGlzdD0iWklOQkkiLCBzdGFydD1saXN0KG11PTEsIHNpZ21hPTEsIG51PTAuMSksIHVwcGVyPWMoSW5mLCBJbmYsIDEpLCBsb3dlcj1jKDAsMCwwKSwgZGlzY3JldGU9VFJVRSkKc3VtbWFyeShmaXRfb3V0cHV0X3ppbmJpKQpwbG90KGZpdF9vdXRwdXRfemluYmkpCmBgYAoKV2UgY2FuIGFsc28gcGxvdCBhIHR5cGljYWwgbmVnYXRpdmUgYmlub21pYWwgZGlzdHJpYnV0aW9uICh3aXRob3V0IHplcm8gaW5mbGF0aW9uKSBvdmVyIG91ciBkYXRhIHRvIHNlZSBob3cgdGhlIHplcm8gaW5mbGF0aW9uIHdvcmtzLiBUaGUgcGxvdCBiZWxvdyBzaG93cyB0aGF0IHdpdGhvdXQgdGhlIHplcm8gaW5mbGF0aW9uIHBhcnQsIHRoZSBuZWdhdGl2ZSBiaW5vbWlhbCBmaXRzIHRoZSBzZWNvbmQgcGVhayBvZiB0aGUgZGF0YSwgd2l0aCBhIGhpZ2hlciBhdmVyYWdlIChtdSkgYW5kIHNtYWxsZXIgdmFyaWFuY2UgKHNpZ21hKS4KClRoZSBuZWdhdGl2ZSBiaW5vbWlhbCBwcmVkaWN0cyBhcm91bmQgMTAlIGFyZSB6ZXJvcywgYnV0IHRoZSBhY3R1YWwgb2JzZXJ2ZWQgemVybyBjb3VudCB3YXMgMzAlLiBUaGUgZGlmZmVyZW5jZSBpbiB6ZXJvcyBpcyBhY2NvdW50ZWQgYnkgdGhlIDIxJSBleGNlc3NpdmUgemVyb3MuCgpgYGB7ciB6aSBwbG90ZGlzdCB6aW5iLCB3YXJuaW5nPUZBTFNFLCBjbGFzcy5zb3VyY2UgPSAnZm9sZC1zaG93J30KIyB3ZSBjb21wYXJlIHRoZSB6ZXJvIGluZmxhdGVkIGRhdGEgdG8gYSBuZWdhdGl2ZSBiaW5vbWlhbCBtb2RlbCwgd2l0aG91dCB6ZXJvIGluZmxhdGlvbiwgYnV0IHdpdGggdGhlIG11IGFuZCBzaWdtYSBmcm9tIHRoZSB6ZXJvIGluZmxhdGlvbiBtb2RlbC4KIyB0aGlzIHNob3dzIHVzIHRoZSB6ZXJvIGluZmxhdGlvbiBjb21wb25lbnQKcGxvdGRpc3QoemlfZGF0YSRzd2l0Y2hfY291bnQsIGRpc3Q9Ik5CSSIsIHBhcmE9bGlzdChzaWdtYT1jb2VmKGZpdF9vdXRwdXRfemluYmkpWydzaWdtYSddLCBtdT1jb2VmKGZpdF9vdXRwdXRfemluYmkpWydtdSddKSwgZGlzY3JldGU9VCkKYGBgCgojIyMgUnVuIGEgbmVnYXRpdmUgYmlub21pYWwgYW5kIHRlc3QgZm9yIHplcm8gaW5mbGF0aW9uCgpBbHRlcm5hdGl2ZWx5LCB3ZSBjYW4gcnVuIGEgbmVnYXRpdmUgYmlub21pYWwgcmVncmVzc2lvbiAod2l0aG91dCB6ZXJvIGluZmxhdGlvbikgdXNpbmcgdGhlIHNhbWUgYGdsbS5uYmAgZnVuY3Rpb24gYXMgYmVmb3JlLCBhbmQgdXNlIHRoZSBgdGVzdFplcm9JbmZsYXRpb25gIGZ1bmN0aW9uIGZyb20gYERIQVJNYWAuIAoKU2lnbmlmaWNhbnQgdmFsdWVzIGFib3ZlIDEgbWVhbiB0aGF0IHRoZXJlIGlzIHplcm8gaW5mbGF0aW9uLiBJZGVhbGx5IHRoZSByZWQgbGluZSBpbiB0aGUgcGxvdCBiZWxvdyBzaG91bGQgYmUgaW4gdGhlIG1pZGRsZSBvZiB0aGUgZGF0YSBkaXN0cmlidXRpb24uIEJlY2F1c2UgdGhlIGRhdGEgaXMgdG8gdGhlIGxlZnQgb2YgdGhlIHJlZCBsaW5lLCBhbmQgdGhlIHN0YXRpc3RpYyBpcyBzaWduaWZpY2FudCwgdGhpcyB0ZXN0IGNvbmZpcm1zIHRoYXQgdGhlcmUgaXMgemVybyBpbmZsYXRpb24uXltTaWduaWZpY2FudCB2YWx1ZXMgYmVsb3cgMSB3aXRoIGRhdGEgc2lnbmlmaWNhbnRseSB0byB0aGUgcmlnaHQgb2YgdGhlIHJlZCBsaW5lIHdvdWxkIG1lYW4gYSBsYWNrIG9mIHplcm9zLl0KCmBgYHtyIHplcm8gaW5mbGF0aW9uIHRlc3QsIGNsYXNzLnNvdXJjZSA9ICdmb2xkLXNob3cnfQp6aS5tMi5uYiA8LSBnbG0ubmIoc3dpdGNoX2NvdW50IH4gcGdzaSwgZGF0YT16aV9kYXRhKSAjIHJ1biBhIHN0YW5kYXJkIG5lZ2F0aXZlIGJpbm9taWFsIHJlZ3Jlc3Npb24KdGVzdFplcm9JbmZsYXRpb24oemkubTIubmIpICMgdGVzdCBmb3IgemVybyBpbmZsYXRpb24KYGBgCgojIyMgQ29tcGFyZSBhIG5lZ2F0aXZlIGJpbm9taWFsIG1vZGVsIHdpdGggYSB6ZXJvLWluZmxhdGVkIG1vZGVsCgpGaW5hbGx5LCBhbm90aGVyIHdheSB0byB0ZXN0IGlzIHRvIHJ1biBhICoqWmVyby1JbmZsYXRpb24gTmVnYXRpdmUgQmlub21pYWwqKiAoYFpJTkJgKSAkLSQgb3IgYFpJUGAgZm9yICoqWmVyby1JbmZsYXRlZCBQb2lzc29uKiogJC0kIHJlZ3Jlc3Npb24gYW5kIGNvbXBhcmUgdGhlIHR3byBtb2RlbHMuIFRoZXJlIGFyZSBzZXZlcmFsIHBhY2thZ2VzIHRoYXQgYWxsb3cgZm9yIHplcm8taW5mbGF0ZWQgbW9kZWxzLCBidXQgb25lIG9mIHRoZSBzaW1wbGVzdCBpcyB0aGUgZnVuY3Rpb24gYHplcm9pbmZsYCBmcm9tIHBhY2thZ2UgYHBzY2xgLl5bSWYgeW91ciBkYXRhIGlzIHJlcGVhdGVkIG1lYXN1cmVzIGFuZCB5b3UgbmVlZCBhIG1peGVkIGVmZmVjdHMgbW9kZWwsIHRoZSBzYW1lIGZ1bmN0aW9uIGBnbG1tVE1CYCBmcm9tIGJlZm9yZSBhbGxvd3MgZm9yIHplcm8gaW5mbGF0aW9uLCBhbGwgeW91IG5lZWQgdG8gZG8gaXMgc3BlY2lmeSB0aGUgemVyby1pbmZsYXRpb24gY29tcG9uZW50IHVzaW5nIGB6aT0uLi5gLCBhbHRob3VnaCBpdCBjYW4gYmUgdmVyeSBzbG93IHRvIGZpdC5dCgpaZXJvLWluZmxhdGlvbiBtb2RlbHMgYXNzdW1lIHRoYXQgdHdvIGRpZmZlcmVudCBhbmQgc2VwYXJhdGUgcHJvY2Vzc2VzIGdlbmVyYXRlIHRoZSBkYXRhLiAKCi0gKipaZXJvIGluZmxhdGlvbioqOiBUaGlzIHByb2Nlc3MgaXMgcmVzcG9uc2libGUgZm9yIGV4Y2Vzc2l2ZSB6ZXJvIHJlc3BvbnNlcyAoaW4gb3RoZXIgd29yZHMsIGlzIHRoZSByZXNwb25zZSBnb2luZyB0byBiZSBhbiBleGNlc3NpdmUgemVybywgbW9yZSB0aGFuIGV4cGVjdGVkPykuIFRoaXMgaXMgY29tbW9ubHkgZXN0aW1hdGVkIHVzaW5nIGEgYmluYXJ5IGxvZ2lzdGljIHJlZ3Jlc3Npb24uIEluIHRoaXMgZXhhbXBsZSwgZGlkIHRoZSBwYXJ0aWNpcGFudCBkZWNpZGUgdG8gc3dpdGNoIGJldHdlZW4gc2xvdCBtYWNoaW5lcyBhdCBhbGw/CgotICoqQ29uZGl0aW9uYWwgcmVncmVzc2lvbioqOiBUaGVuLCBhc3N1bWluZyB0aGF0IHRoZSBtb2RlbCBkb2VzIG5vdCBjbGFzc2lmeSB0aGlzIHJlc3BvbnNlIGFzIGFuIGV4Y2Vzc2l2ZSB6ZXJvLCBhIGNvbmRpdGlvbmFsIG5lZ2F0aXZlIGJpbm9taWFsIHJlZ3Jlc3Npb24gaXMgcnVuIChjb25kaXRpb25hbCB0aGF0IHRoZSByZXN1bHQgaXMgbm90IGFuIGV4Y2Vzc2l2ZSB6ZXJvKSB0byBkZXRlcm1pbmUgdGhlIGFjdHVhbCByZXNwb25zZSwgaW4gdGhpcyBjYXNlLCB0aGUgbnVtYmVyIG9mIHN3aXRjaGVzIGJldHdlZW4gZGlmZmVyZW50IG1hY2hpbmVzLiAKCkl0IGlzIGltcG9ydGFudCB0byBub3RlIHRoYXQgdGhlIGNvbmRpdGlvbmFsIHJlZ3Jlc3Npb24gcGFydCBvZiB0aGUgbW9kZWwgY2FuIGFsc28gcmV0dXJuIGEgY291bnQgb2YgemVybywgYW5kIHRoZSB6ZXJvLWluZmxhdGlvbiBwYXJ0IGlzIG9ubHkgcmVzcG9uc2libGUgZm9yIGV4Y2Vzc2l2ZSB6ZXJvcyAob3IgaW5mbGF0aW9uIG9mIHplcm9zKS4gSWYgeW91IGJlbGlldmUgdGhpcyBjYW5ub3QgYmUgdGhlIGNhc2UsIHRoZW4gKipIdXJkbGUqKiBtb2RlbHMgZG8gbm90IGFsbG93IGZvciB6ZXJvcyBpbiB0aGUgY29uZGl0aW9uYWwgcGFydCwgb25seSB0aGUgemVyby1pbmZsYXRpb24gcGFydCBnZW5lcmF0ZXMgemVyb3MuIFlvdSBjYW4gdGhpbmsgb2YgdGhpcyBhcyBhIGh1cmRsZSB0aGF0IHRoZSBwYXJ0aWNpcGFudCBoYXMgdG8gcGFzcyAoZS5nLiwgaXMgdGhlIHJlc3BvbnNlIGEgemVybyBvciBub3QpLgoKKipOQioqOiBUaGUgcGFja2FnZXMgdGhhdCBhbGxvdyBmb3IgemVyby1pbmZsYXRpb24gb2Z0ZW4gcmVxdWlyZSB0aGUgemVyby1pbmZsYXRpb24gcGFydCBvZiB0aGUgbW9kZWwgdG8gYmUgc3BlY2lmaWVkIHNlcGFyYXRlbHkuIEluIHRoZSBjYXNlIG9mIHRoZSBgemVyb2luZmxgIGZ1bmN0aW9uLCBpZiB5b3UgZG9uJ3Qgc3BlY2lmeSBhbnl0aGluZywgdGhlIGZ1bmN0aW9uIGFzc3VtZXMgdGhhdCB0aGUgemVybyBpbmZsYXRpb24gcGFydCBvZiB0aGUgbW9kZWwgaXMgdGhlIHNhbWUgYXMgdGhlIGNvbmRpdGlvbmFsIHBhcnQgb2YgdGhlIG1vZGUsIGllLCBpdCBoYXMgdGhlIHNhbWUgcHJlZGljdG9ycy4gVGhpcyBpcyB0aGUgc2ltcGxlc3QgYW5kIGVhc2llc3QgYXBwcm9hY2guXltTb21lIG90aGVyIGZ1bmN0aW9ucyAqKnJlcXVpcmUqKiB5b3UgdG8gc3BlY2lmeSBhIHplcm8taW5mbGF0ZWQgY29tcG9uZW50IHVzaW5nIHNvbWUgc29ydCBvZiBzeW50YXggc3VjaCBhcyBgemkgPSAuLi5gLiBQbGVhc2UgcmVhZCB0aGUgaGVscCBmb3IgdGhlIHNwZWNpZmljIGZ1bmN0aW9uIHlvdSBhcmUgdXNpbmcuIEV2ZW4gZm9yIGB6ZXJvaW5mbGAgeW91IGNhbiBzcGVjaWZ5IHNlcGFyYXRlIG1vZGVscyBmb3IgZWFjaCBwYXJ0LCB3aXRoIHNlcGFyYXRlIHByZWRpY3RvcnMsIGlmIHlvdSBiZWxpZXZlIHRoYXQgdGhlIHByb2Nlc3NlcyBnZW5lcmF0aW5nIG91dGNvbWVzIGZyb20gZWFjaCBjb21wb25lbnQgYXJlIGRpZmZlcmVudC5dCgo8ZGl2IGNsYXNzID0gImJsdWUiPgoKV2UgY2FuIHRoZW4gY29tcGFyZSB0aGUgdHdvIG1vZGVscyB0byBzZWUgaWYgdGhlIHplcm8taW5mbGF0ZWQgbW9kZWwgZml0cyB0aGUgZGF0YSBiZXR0ZXIuIFRoZSBtb2RlbCB3aXRoIHRoZSBsb3dlc3QgQUlDIGlzIGNvbnNpZGVyZWQgdG8gZml0IHRoZSBkYXRhIGJldHRlciwgYW5kIHRoYXQncyB0aGUgY2FzZSBoZXJlIGZvciB0aGUgWklOQiBtb2RlbCBpbiBjb21wYXJpc29uIHRvIHRoZSBOQiBtb2RlbCAod2l0aG91dCB6ZXJvLWluZmxhdGlvbikuXltBZ2FpbiBiZSBjYXJlZnVsIHdoZW4gY29tcGFyaW5nIEFJQyBhcyBub3QgYWxsIGFyZSBjb21wYXJhYmxlLCBidXQgaW4gdGhpcyBjYXNlIHRoZXkgYXJlLiBBbHNvIGlmIHlvdSB3YW50IHRvIGV2YWx1YXRlIGlmIHRoZSBkaWZmZXJlbmNlIGluIEFJQyBiZXR3ZWVuIHRoZSB0d28gbW9kZWxzIGlzIHNpZ25pZmljYW50IG9yIG5vdCwgdGhlcmUgYXJlIHdheXMgb2YgY2FsY3VsYXRpbmcgdGhpcywgYnV0IEkgZGlkIG5vdCBpbmNsdWRlIGl0IGhlcmUuXQoKPC9kaXY+CgpgYGB7ciB6aSB6aW5iIHJlZ3Jlc3Npb259CiMgcnVuIGEgemVyby1pbmZsYXRpb24gbmVnYXRpdmUgYmlub21pYWwgcmVncmVzc2lvbgp6aS5tMy56aW5iIDwtIHplcm9pbmZsKHN3aXRjaF9jb3VudCB+IHBnc2ksIGRhdGE9emlfZGF0YSwgZGlzdD0ibmVnYmluIikKIyBjb21wYXJlIHRoZSBBSUNzIG9mIHRoZSB0d28gbW9kZWxzLiBMb3dlciBBSUMgaXMgYmV0dGVyLgpBSUMoemkubTIubmIsIHppLm0zLnppbmIpCmBgYAoKVGhlIG91dHB1dCBmcm9tIHplcm8taW5mbGF0aW9uIG1vZGVscyBmb2xsb3cgdGhlIHR3byBjb21wb25lbnRzIGRlc2NyaWJlZCBhYm92ZTogVGhlICoqY29uZGl0aW9uYWwqKiBhbmQgdGhlICoqemVyby1pbmZsYXRpb24qKiBjb21wb25lbnRzLgoKLSBUaGUgemVyby1pbmZsYXRpb24gY29tcG9uZW50IG91dHB1dHMgT2Rkcy1SYXRpb3MgKE9SKSBiYXNlZCBvbiBhIGJpbmFyeSBsb2dpc3RpYyByZWdyZXNzaW9uICgxID0gZXhjZXNzIHplcm8pLiBJdCBtZWFzdXJlcyB0aGUgbGlrZWxpaG9vZCBvZiBhIHBhcnRpY2lwYW50IHJlc3BvbmRpbmcgd2l0aCBhbiBleGNlc3NpdmUgemVybyByZXNwb25zZSBvciBub3QuIE5vdGUgdGhhdCB0aGlzIHBhcnQgb2YgdGhlIG1vZGVsIGlzIHBvc2l0aXZlbHkgY29ycmVsYXRlZCB3aXRoIHplcm8gcmVzcG9uc2VzLCB0aGVyZWZvcmUgbmVnYXRpdmVseSBjb3JyZWxhdGVkIHdpdGggYWN0dWFsIGJldHMgcGxhY2VkIEhpZ2hlciBPUnMgaGVyZSBpbmRpY2F0ZSBhIGhpZ2hlciBwcm9wZW5zaXR5IHRvIHJlc3BvbmQgd2l0aCBhIHplcm8gKGFuZCB0aGVyZWZvcmUgYSBsb3dlciBudW1iZXIgb2Ygc3dpdGNoZXMgb3ZlcmFsbCkuCgogICAtIEluIHRoaXMgZXhhbXBsZSwgdGhlIFBHU0kgcHJlZGljdG9yIHdhcyBzaWduaWZpY2FudCwgd2l0aCBhbiBPUiBiZWxvdyAxLiBQYXJ0aWNpcGFudHMgd2l0aCBoaWdoZXIgUEdTSSBzY29yZXMgd2VyZSBsZXNzIGxpa2VseSB0byBub3Qgc3dpdGNoIGF0IGFsbC4gCgo8YnI+CgotIFRoZSBjb25kaXRpb25hbCBjb21wb25lbnQgSW5jaWRlbmNlIFJhdGUgUmF0aW9zIChJUlJzKSBiYXNlZCBvbiBhIG5lZ2F0aXZlIGJpbm9taWFsIHJlZ3Jlc3Npb24uIElSUnMgYWJvdmUgMSBpbmRpY2F0ZSBhIGhpZ2hlciBzd2l0Y2ggY291bnQgYW5kIHZpY2UtdmVyc2EuCgogICAtIEluIHRoaXMgZXhhbXBsZSwgUEdTSSBzY29yZXMgc2lnbmlmaWNhbnRseSBpbmZsdWVuY2VkIGhvdyBvZnRlbiBhIHBhcnRpY2lwYW50IHN3aXRjaGVkIGJldHdlZW4gc2xvdCBtYWNoaW5lcy4gVGhlIE9SIHdhcyBzaWduaWZpY2FudGx5IGFib3ZlIDEsIHdoaWNoIG1lYW5zIHRoYXQgd2l0aCBoaWdoZXIgUEdTSSBzY29yZXMsIHBhcnRpY2lwYW50cyBzd2l0Y2hlZCBtb3JlIHRpbWVzLgoKYGBge3IgemkgdGFiIG1vZGVsIGludGVycHJldGF0aW9ufQp0YWJfbW9kZWwoemkubTMuemluYikgIyBuaWNlIHRhYmxlIG9mIHRoZSByZXN1bHRzIG9mIHRoZSBaSU5CIG1vZGVsCmBgYAoKV2UgY2FuIGNvbXBhcmUgbW9kZWwgcHJlZGljdGlvbnMgdG8gc2VlIGhvdyB0aGUgemVyby1pbmZsYXRlZCBtb2RlbCBwcmVkaWN0cyBtYW55IG1vcmUgemVyb3MgYW5kIG1vcmUgY2xvc2VseSBtYXRjaGVzIHRoZSBvcmlnaW5hbCBkYXRhc2V0LiBXZSBzZWUgdGhhdCB0aGUgemVyby1pbmZsYXRlZCBtb2RlbCBmaXRzIHRoZSBkYXRhIGJldHRlci4KCmBgYHtyIHppIHByZWRpY3Rpb25zfQpwcmVkLnNhbXBsZXMgPC0gMToxMDAgIyBob3cgbWFueSBwcmVkaWN0aW9ucyBkbyB3ZSB3YW50PwojIHNpbXVsYXRlIHRoZSBwcmVkaWN0aW9ucyBmb3IgbmVnYXRpdmUgYmlub21pYWwKcHJlZC5jb3VudC5uYiA8LSBkYXRhLmZyYW1lKGRhdGE9J1ByZWRpY3Rpb24gKE5lZ2F0aXZlIEJpbm9taWFsKScsIHN3aXRjaF9jb3VudD1hcy52ZWN0b3IoZG8uY2FsbChyYmluZCwgc2FwcGx5KHByZWQuc2FtcGxlcywgZnVuY3Rpb24oeCl7c2ltdWxhdGUoemkubTIubmIpfSkpKSkKIyBzaW11bGF0ZSB0aGUgcHJlZGljdGlvbnMgZm9yIHRoZSBaSU5CIG1vZGVsCnByb2JhYmlsaXRpZXMgPC0gcHJlZGljdCh6aS5tMy56aW5iLCB0eXBlPSJwcm9iIikgIyBnZXQgdGhlIHByb2JhYmlsaXRpZXMKcHJlZC5jb3VudC56aSA8LSBkYXRhLmZyYW1lKGRhdGE9J1ByZWRpY3Rpb24gKFplcm8tSW5mbGF0ZWQgTmVnIEJpbiknLCBzd2l0Y2hfY291bnQ9YXMudmVjdG9yKHNhcHBseShwcmVkLnNhbXBsZXMsIGZ1bmN0aW9uKHMpe2FwcGx5KHByb2JhYmlsaXRpZXMsIDEsIGZ1bmN0aW9uKHApIHNhbXBsZSgxOm5jb2wocHJvYmFiaWxpdGllcyksIDEsIHByb2I9cCkpfSkpKQoKIyBiaW5kIHRoZSBwcmVkaWN0aW9ucyB3aXRoIHRoZSByYXcgZGF0YSBpbiBhIHNpbmdsZSBkYXRhIGZyYW1lIGZvciBwbG90dGluZwpwcmVkLmNvdW50IDwtIHJiaW5kKHByZWQuY291bnQubmIsIHByZWQuY291bnQuemksIGRhdGEuZnJhbWUoZGF0YT0nT3JpZ2luYWwgRGF0YScsIHN3aXRjaF9jb3VudD16aV9kYXRhJHN3aXRjaF9jb3VudCkpCgojIHNob3cgYSBoaXN0b2dyYW0gb2YgdGhlIHNpbXVsYXRlZCBvdXRjb21lcyBuZXh0IHRvIHRoZSBvcmlnaW5hbCByYXcgZGF0YQpnZ3Bsb3QoZGF0YT1wcmVkLmNvdW50LCBhZXMoc3dpdGNoX2NvdW50LCBncm91cD1kYXRhKSkgKyBnZW9tX2hpc3RvZ3JhbShhZXMoeT1hZnRlcl9zdGF0KGRlbnNpdHkpKSwgZmlsbD0ibGlnaHRibHVlIiwgY29sb3I9ImJsYWNrIiwgYmlud2lkdGg9MSkgKyB0aGVtZV9idygpICsgeGxhYigiU3dpdGNoIENvdW50IikgKyB5bGFiKCIlIG9mIHBhcnRpY2lwYW50cyIpICsgZmFjZXRfd3JhcCh+ZGF0YSkgKyBjb29yZF9jYXJ0ZXNpYW4oeGxpbT1jKDAsMjApKQpgYGAKCldoZW4gaW50ZXJwcmV0aW5nIHRoZSByZXN1bHRzIG9mIGEgemVyby1pbmZsYXRlZCBtb2RlbCwgeW91IG5lZWQgdG8gY29uc2lkZXIgYm90aCBjb21wb25lbnRzIHNlcGFyYXRlbHksIHRoZSB6ZXJvLWluZmxhdGVkIGFuZCB0aGUgY29uZGl0aW9uYWwgY29tcG9uZW50cy4gTm90IG9ubHkgdGhlIGNvZWZmaWNpZW50cywgcHJlZGljdG9ycywgYW5kIHRoZWlyIHNpZ25pZmljYW5jZSwgYnV0IGFsc28gdGhlaXIgZXN0aW1hdGVkIG1hcmdpbmFsIG1lYW5zLiAKCllvdSBjYW4gZG8gdGhhdCBieSBzZXR0aW5nIHRoZSBjb21wb25lbnQgb2YgdGhlIHJlZ3Jlc3Npb24gdGhhdCB5b3Ugd2FudCB0byBldmFsdWF0ZSBhdCBlYWNoIHRpbWUsIG9mdGVuIHVzaW5nIGBjb21wb25lbnQ9Li4uYCBvciBgbW9kZT0uLi5gLiBUaGVzZSBjYW4gYmUgaWRlbnRpZmllZCB1c2luZyBgemVyb2Agb3IgYHppYCBmb3IgemVyby1pbmZsYXRpb24gYW5kIGBjb3VudGAgb3IgYGNvbmRgIGZvciB0aGUgY29uZGl0aW9uYWwgY29tcG9uZW50LiBFYWNoIGZ1bmN0aW9uIHJlcXVpcmVzIHNsaWdodGx5IGRpZmZlcmVudCBwYXJhbWV0ZXJzLCBzbyBjaGVjayB0aGUgaGVscC4gCgo8ZGl2IGNsYXNzID0gImJsdWUiPgoKSWYgeW91IGRvbid0IHNldCB0aGUgY29tcG9uZW50IGZvciB5b3VyIGFuYWx5c2VzLCBvZnRlbiB5b3Ugd2lsbCBvbmx5IGJlIHNob3duIHRoZSBjb25kaXRpb25hbCBjb21wb25lbnQgYnkgZGVmYXVsdC4gQmUgY2FyZWZ1bCBhcyB0aGlzIGlzIG9ubHkgcGFydCBvZiB0aGUgcGljdHVyZS4KCjwvZGl2PgoKYGBge3IgemkgZW1tZWFuc30KIyBlbW1pcCBpcyB1c2VkIHRvIGNhbGN1bGF0ZSB0aGUgc2xvcGUgb2YgdGhlIHByZWRpY3RvcnMgYWdhaW5zdCB0aGUgRFYKIyBvbmUgaXMgbmVlZGVkIGZvciBlYWNoIGNvbXBvbmVudCBvZiB0aGUgemVyby1pbmZsYXRpb24gcmVncmVzc2lvbi4KZW1vdXQuemVybyA8LSBlbW1pcCh6aS5tMy56aW5iLCB+cGdzaSwgYXQ9bGlzdChwZ3NpPTA6MjApLCB0eXBlPSJyIiwgQ0lzPVQsIG1vZGU9Inplcm8iLCBwbG90aXQ9RkFMU0UpIAplbW91dC56ZXJvJGNvbXBvbmVudCA8LSAiemVybyBpbmZsYXRpb24iCmVtb3V0LmNvbmQgPC0gZW1taXAoemkubTMuemluYiwgfnBnc2ksIGF0PWxpc3QocGdzaT0wOjIwKSwgdHlwZT0iciIsIENJcz1ULCBtb2RlPSJjb3VudCIsIHBsb3RpdD1GQUxTRSkKZW1vdXQuY29uZCRjb21wb25lbnQgPC0gImNvbmRpdGlvbmFsIgoKZ2dwbG90KHJiaW5kKGVtb3V0Lnplcm8sIGVtb3V0LmNvbmQpLCBhZXMoeD1wZ3NpLCB5PXl2YXIsIHltaW49TENMLCB5bWF4PVVDTCkpICsgZ2VvbV9saW5lKGxpbmV3aWR0aD0xKSArIGdlb21fcmliYm9uKGFscGhhPTAuMikgKyB0aGVtZV9idygpICsgeGxhYigicGdzaSIpICsgeWxhYigiQ291bnQgb2YgU3dpdGNoZXMgLyBQcm9iYWJpbGl0eSBvZiBFeGNlc3MgWmVybyIpICsgZmFjZXRfd3JhcCh+Y29tcG9uZW50LCBzY2FsZXM9ImZyZWUiKQoKYGBgCgojIFBvc3RzY3JpcHQ6IERvbid0IGxpa2UgdG8gdXNlIFI/CgpJZiB5b3UgZG9uJ3QgbGlrZSB1c2luZyBSLCBKYW1vdmkgaGFzIGFuIGltcGxlbWVudGF0aW9uIGZvciBQb2lzc29uIGFuZCBOZWdhdGl2ZSBCaW5vbWlhbCByZWdyZXNzaW9ucyBpbmNsdWRpbmcgbWl4ZWQgZWZmZWN0cywgd2hpbGUgSkFTUCBzdXBwb3J0cyBvbmx5IFBvaXNzb24gZGlzdHJpYnV0aW9ucyAoYWxzbyBtaXhlZCBlZmZlY3RzKS4gTmVpdGhlciBzZWVtcyB0byBzdXBwb3J0IHplcm8gaW5mbGF0aW9uIGF0IHRoZSBtb21lbnQuCgojIEZ1cnRoZXIgcmVhZGluZwoKVGhlcmUgYXJlIHNldmVyYWwgdXNlZnVsIG9ubGluZSBndWlkZXMgb24gY291bnQgZGF0YSBhbmFseXNpcy4gSGVyZSBhcmUgc29tZSB0aGF0IEkgaGF2ZSBmb3VuZDoKCltSZWdyZXNzaW9uIE1vZGVscyBmb3IgQ291bnQgRGF0YSBpbiBSIHVzaW5nIHRoZSBwc2NsIHBhY2thZ2VdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9wc2NsL3ZpZ25ldHRlcy9jb3VudHJlZy5wZGYpCgpbVUNMQSdzIEFkdmFuY2VkIFJlc2VhcmNoIENvbXB1dGluZyBTdGF0aXN0aWNhbCBNZXRob2RzIGFuZCBEYXRhIEFuYWx5dGljcyBwYWdlIG9uIE5lZ2F0aXZlIEJpbm9taWFsIFJlZ3Jlc3Npb25dKGh0dHBzOi8vc3RhdHMub2FyYy51Y2xhLmVkdS9yL2RhZS9uZWdhdGl2ZS1iaW5vbWlhbC1yZWdyZXNzaW9uLykKWy4uLiBhbmQgWmVybyBJbmZsYXRlZCBOZWdhdGl2ZSBCaW5vbWlhbCBSZWdyZXNzaW9uXShodHRwczovL3N0YXRzLm9hcmMudWNsYS5lZHUvci9kYWUvemluYi8pCgpbVGhlIFBvaXNzb24gZGlzdHJpYnV0aW9uOiBGcm9tIGJhc2ljIHByb2JhYmlsaXR5IHRoZW9yeSB0byByZWdyZXNzaW9uIG1vZGVsc10oaHR0cHM6Ly93d3cuci1ibG9nZ2Vycy5jb20vMjAyMi8wNi90aGUtcG9pc3Nvbi1kaXN0cmlidXRpb24tZnJvbS1iYXNpYy1wcm9iYWJpbGl0eS10aGVvcnktdG8tcmVncmVzc2lvbi1tb2RlbHMvKQoKW0NvdW50IGRhdGEgYW5kIEdMTXM6IGNob29zaW5nIGFtb25nIFBvaXNzb24sIG5lZ2F0aXZlIGJpbm9taWFsLCBhbmQgemVyby1pbmZsYXRlZCBtb2RlbHNdKGh0dHBzOi8vd3d3LnItYmxvZ2dlcnMuY29tLzIwMTIvMDgvY291bnQtZGF0YS1hbmQtZ2xtcy1jaG9vc2luZy1hbW9uZy1wb2lzc29uLW5lZ2F0aXZlLWJpbm9taWFsLWFuZC16ZXJvLWluZmxhdGVkLW1vZGVscy8pCgpbR0xNIHdpdGggemVyby1pbmZsYXRlZCBkYXRhXShodHRwczovL2Z1a2FtaWxhYi5naXRodWIuaW8vQklPMjAyLzA0LUMtemVyby1kYXRhLmh0bWwpCgpbWmVyby1pbmZsYXRlZCBNb2RlbHMgVXNpbmcgREhBUk1hIGFuZCBnbG1tVE1CXShodHRwczovL2VkZGF0YXNjaWVuY2VlZXMuZ2l0aHViLmlvL3R1dG9yaWFsLXphY2hhcnlsaTEvKQoKIyBPdGhlciAoc2ltaWxhcikgdHlwZXMgb2YgZGF0YQoKU29tZXRpbWVzIHlvdXIgZGF0YSBtaWdodCBsb29rIGxpa2UgQ291bnQgZGF0YSwgYnV0IGl0IHJlYWxseSBhaW4ndC4KCklmIHlvdXIgZGF0YSBpcyAqcmFua2VkKiwgZm9yIGV4YW1wbGUsIGEgKkxpa2VydCBzY2FsZSosIHRoZW4geW91IHNob3VsZCB1c2UgYW4gKipvcmRpbmFsKiogcmVncmVzc2lvbi4gUiBoYXMgc2V2ZXJhbCBpbXBsZW1lbnRhdGlvbnMgZm9yIG9yZGluYWwgbG9naXN0aWMgcmVncmVzc2lvbnMsIHN1Y2ggYXMgdGhlIGZ1bmN0aW9uIGBwb2xyYCBmcm9tIHRoZSBwYWNrYWdlIGBNQVNTYCBvciB0aGUgbXVjaCBtb3JlIGZsZXhpYmxlIGBjbG1gIGZyb20gdGhlIHBhY2thZ2UgYG9yZGluYWxgLiBJZiB5b3UgbmVlZCBhIG1peGVkLWVmZmVjdHMgb3JkaW5hbCBsb2dpc3RpYyByZWdyZXNzaW9uIChmb3Igd2l0aGluLXN1YmplY3RzIHJlcGVhdGVkLW1lYXN1cmVzIGRhdGEpIHRoZW4gdGhlcmUgaXMgdGhlIGZ1bmN0aW9uIGBjbG1tYCBhbHNvIGZyb20gYG9yZGluYWxgLgoKSWYgeW91ciBkYXRhIGhhcyBib3RoICpsb3dlciBhbmQgdXBwZXIgYm91bmRzKiwgdGhlbiBpdCBjb3VsZCBwb3RlbnRpYWxseSBiZSAqcGVyY2VudGFnZSBvciBwcm9wb3J0aW9uYWwqIGRhdGEsIGFuZCB5b3Ugc2hvdWxkIGNvbnNpZGVyIGEgKipiZXRhKiogcmVncmVzc2lvbi4gWW91IGNhbiB1c2UgdGhlIGZ1bmN0aW9uIGBiZXRhcmVnYCBmcm9tIHRoZSBzYW1lLW5hbWVkIHBhY2thZ2UuIE5vdGUgdGhhdCBiZXRhIHJlZ3Jlc3Npb25zIGRvIG5vdCBhY2NlcHQgemVyb3Mgb3Igb25lcywgc28gZWl0aGVyIHlvdSBuZWVkIHRvIGNvbnZlcnQgeW91ciBkYXRhIHRvIHJlbW92ZSB0aGUgemVyb3MgYW5kIG9uZXMgKGUuZy4sIHNlZSBbU21pdGhzb24gJiBWZXJrdWlsZW4sIDIwMDZdKGh0dHBzOi8vZG9pLmFwYS5vcmcvZG9pLzEwLjEwMzcvMTA4Mi05ODlYLjExLjEuNTQpOyBbVmVya3VpbGVuICYgU21pdGhzb24sIDIwMTJdKGh0dHBzOi8vZG9pLm9yZy8xMC4zMTAyLzEwNzY5OTg2MTAzOTY4OTUpKSBvciB5b3UgY2FuIHVzZSB6ZXJvLW9uZS1pbmZsYXRlZC1iZXRhIHJlZ3Jlc3Npb24gKFpPSUIpLgoK