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)
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.
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")
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")
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")
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"))
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.
# 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"))
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.
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) 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)
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")
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.
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)
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")
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.
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")
Zero inflation
In this final example, the data (zi_data
) comes from a
study on gambling.
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.
Testing for
zero-inflation
We can test if the data has zero inflation in a few different
ways.
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)
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.
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
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
.
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.
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).
# 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.
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")
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.
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).
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