1 Basics

Bayesians treat a model parameter \(\theta\) as random variable while frequentists treat them as unknown constant. So instead of solving for an unknown constant, Bayesian inference is solving for an unknown distribution. To estimate the model parameters the Bayes’ Law is used:

\[ \begin{equation} \label{eq:bayeslaw} P(\theta | y, X) = \frac{P(y|\theta,X) \cdot P(\theta)}{P(y)} \propto P(y|\theta,X) \cdot P(\theta). \end{equation} \]

In plain words:

  • \(X\): The data we observed. In machine learning it is the training features.
  • \(y\): The outcome we observed. In machine learning it is the training labels.
  • \(P(\theta|y,X)\): What do we think about the possible value of our model parameters AFTER seeing the data?
  • \(P(y|\theta,X)\): What is the likelihood of seeing the current outcome conditioned on the data we have and a specific parameter value?
  • \(P(\theta)\): What do we think about the possible values of our model parameters BEFORE observing any actual data? This is our belief or prior about the model parameters.
  • \(P(y)\): What is the unconditional probability of the outcome? That is, the overall probability after taking into consideration all parameter possibilities.

Posterior Density: \(P(\theta|y,X)\)

In Baysian modeling, the objective is to solve for \(P(\theta|y,X)\), the posterior density of parameter. The point estimate is the expected value of posterior density. The variance estimate is also readily available from the posterior distribution.

Based on equation \(\eqref{eq:bayeslaw}\), to solve for \(P(\theta|y,X)\) analytically, we need to calculate the data likelihood \(P(y|\theta,X)\), the prior \(P(\theta)\), and the marginial likelihood of outcome \(P(y)\). The difficulty is that there is rarely a tractable closed-form solution due to the presence of the denominator term \(P(y)\). To overcome this problem, Markov chain Monte Carlo method is used to numerically solve the posterior density by directly generating random draws of parameters without calculating the denominator term.

Data Likelihood: \(P(y|\theta,X)\)

As long as the model is fully specified, and the data is identically and independently distributed, the data likelihood is simply the product of all individal observation likelihood:

\[ \begin{equation} P(y|\theta) = \prod_i^n P(y_i|\theta, X_i). \end{equation} \]

The individual likelihood \(P(y_i|\theta, X_i)\) can be output of a logistic regression or a deep neural nets or virtually any kind of probablistic model.

Prior: \(P(\theta)\)

How do we solve for a prior for a given parameter? Well indeed we can’t solve for it. We simply make assumption about it.

The introduction of model prior make Bayesian modeling more flexible when there is extra information that can be utilized. When there is no particular useful information a priori, one can still choose to use a so-called non-informative prior to express objectivity.

Marginal Likelihood: \(P(y)\)

The denominator of the Bayes’ Law is called the marginal probability of data. By the law of total probabiity we have:

\[ \begin{equation} \label{eq:marginal_lik} P(y) = \int P(y|\theta)P(\theta)d\theta. \end{equation} \]

The integral is usually analytically intractable, even for a moderately parameterized model. By using MCMC method we can avoid calculation of this term all together when solving for the Bayesian estimator. But there are other scenarios we may still need to calculate this term. Notable examples are model comparison and model averaging. In such case other Monte Carlo numerical methods (such as Bridge Sampling) are also developed to provide approximation to the solution.

In a nutshell, in frequentist statistics, we solve for a \(\theta\) that maximizes the likelihood of data. That is the well-known maximum likelihood estimator. In Baysian statistics, however, we solve for a posterior distribution of \(\theta\), where the expected value calculated as a weighted combination of likelihood and parameter prior serves as the model point estimate.

1.1 Frequentist Hypothesis Testing

To get started, we first consider a very simple task of statistical inference: we’d like to test our belief on the mean of a population based on samples we observed. The technique is called one-sample hypothesis testing. We will recap for a frequentist point of view before we dive into a Bayesian world.

1.1.1 One-Sample Mean Test

Here we assume the (unknown) population is a Beta distribution:1

beta_a <- 2
beta_b <- 10
simple_beta <- function(x) dbeta(x, beta_a, beta_b)
curve(simple_beta, from=0, to=1, ylab="Density",
main=sprintf("(Unknown) Population Beta(%s, %s)", beta_a, beta_b))

And we generate a random sample datset of size 1000 from this population as our observed dataset.

set.seed(777)
X1 <- rbeta(1000, beta_a, beta_b)
hist(X1, main=sprintf("(Known) Distribution of Sample from X ~ Beta(%s, %s)", beta_a, beta_b))

The population mean of a \(\mbox{Beta}(\alpha, \beta)\) is \(\frac{\alpha}{\alpha + \beta}\). So in this case it is:

(true_mean <- beta_a / (beta_a + beta_b))
[1] 0.1666667

In the traditional frequentist point of view, thanks to the Law of Large Number and the Central Limit Theorem, we know that sample mean is a very good estimator for the unknown population mean despite the shape of the true distribution:

mean(X1)
[1] 0.1696835

Let’s further assume we have some grounds that suggest (which is actually not true) the population mean should be:

h0_mean <-  0.175

For example if we are at a manufaturing production line, we setup the machine such that the manual tolds us the outcome of our product spec will have a mean of 0.175 but with a right-tailing bias (hence distributed as a Beta) by design if nothing went wrong.

The hypotesis testing hence can be written as:

\[H_0: \mu = 0.175, \\ H_1: \mu \neq 0.175.\]

How does a frequentist statistician verify this hypothesis? How to make a scientific judgement about whether our production line is functioning as expected? To examine this sort of question, a frequentist relies on the following quantitative reasoning:

Provided that the null hypothesis is actually true, how likely will I observe a set of random sampling dataset more extreme than the current one? If the likelihood is very low, then probably I shouldn’t trust the null hypothesis.

In order to measure how extreme our sample data is, we need to first construct the sampling distribution of sample mean, conditioned on our null hypothesis. The Central Limit Theorem (CLT hereafter) suggests that, under fair conditions (especially when the second moment of the population is finite), the sampling distribution of sample mean \(\bar{X}\) with sample size \(N\) is asymptotically Normal:

\[ \begin{equation} \label{eq:clt} \bar{X} \overset{d}\sim \mbox{Normal}(\mu, \frac{\sigma}{\sqrt{N}}), \end{equation} \]

where \(\mu\) and \(\sigma\) is the population mean and standard deviation, respectively.

So we have a very strong scientific ground (CLT) that states the limiting distribution of the sampling distribution of sample mean is Normal. Let’s plot it given the null hypothesis is true:

plot_sampling_dist <- function(X, true_mean, true_sd=NULL) {
  sample_mean <- mean(X)
  se <-
    if ( is.null(true_sd) ) {
      # If true population sd is unknown (practically always the case),
      # we can plug in the sample sd instead as an approximation.
      sample_sd <- sd(X)
      sample_sd / sqrt(length(X))
    } else {
      true_sd / sqrt(length(X))
    }
  x <- seq(true_mean - 3*se, true_mean + 3*se, length=1000)
  density <- dnorm(x, mean=true_mean, sd=se)
  plot(x, density, type="l", yaxs="i",
       main=bquote(bar(X) ~ "Sampling Distribution | Null Hypothesis" ~ H[0]),
       xlab=bquote(bar(X)))
  abline(v=true_mean, lty=2)
  text(true_mean, mean(density), pos=4, bquote("H[0] mean = " ~ .(true_mean)))
  abline(v=sample_mean, col="red")
  text(sample_mean, mean(density) / 2, pos=4, col="red",
       sprintf("Observed sample mean = %s", round(sample_mean, 4)))
  polygon(c(min(x), x[x <= sample_mean], sample_mean),
          c(0, density[which(x <= sample_mean)], 0),
          col=rgb(1, 0, 0, 0.5), border=NA)
}

# Calculate the population sd of a Beta(a, b).
sd_beta <- function(a, b) {
  sqrt((a*b) / ((a + b)^2*(a + b + 1)))
}
true_sd <- sd_beta(beta_a, beta_b)

plot_sampling_dist(X1, true_mean=h0_mean, true_sd=true_sd)

The curve is the probability density of sample mean. It means that it calculates the likelihood of observing a specific sample mean given a random dataset from our population.

In plain words, if we can draw an infinite number of random sample datasets with size 1000 (which we certainly can’t in the real world), and calculating the sample mean for all of them, the distribution of these resulting sample means will behave exactly like the curve we just plotted, provided that the null hypothesis is true. This is an established mathematical fact under a very loose assumption: as long as the random sample is truely i.i.d. (identically and independently distributed) and the population has a finite variance.

The shaded area size is the probability of observing a sample mean more extreme than the current one we have, provided that the null hypothesis is true. Indeed it is the famous (and also notorious) p-value of the test.2

The idea of rejecting the null hypothesis due to a low p-value hence has a clear rationale: If the null hypothesis is true, it is so unlikely that we will observe a even more extreme sample dataset than the current one at hand. So maybe the null hypothesis is just not correct in the first place?

In the example we use a so-called z-test for simplicity while in practice usually a t-test is preferred. That is, instead of using the Normal distribution directly for the sampling distribution, using the Student’s t distribution which is also asymptotically Normal but can conservatively account for small sample distortion and unknown population variance.

A t-test can be done by a one-liner in R:

t.test(X1, mu=h0_mean)

    One Sample t-test

data:  X1
t = -1.6228, df = 999, p-value = 0.1049
alternative hypothesis: true mean is not equal to 0.175
95 percent confidence interval:
 0.1632547 0.1761123
sample estimates:
mean of x
0.1696835 

We can check that the shaded area size in our z-test plot should be approximately the same as the t-test result since we have a fairly large sample size (plus that the population distribution is simple):

# Integral over -Inf ~ sample mean at the sampling distribution pdf.
pnorm(mean(X1), mean=h0_mean, sd=sd(X1)/sqrt(length(X1)))*2  # Doubling since this is a two-sided test.
[1] 0.1046301

1.1.2 Confidence Interval

Confidence interval is another common statistic used to describe the test. Based on a sample dataset, it calculates the range of values that cannot be rejected under a certain level of significance.

We can calculate the 95% interval based on the z-test limiting sampling distribution:

# qnorm is the quantile function for a Normal cdf.
z_lb <- qnorm(.025, mean=mean(X1), sd=sd(X1)/sqrt(length(X1)))
z_hb <- qnorm(.975, mean=mean(X1), sd=sd(X1)/sqrt(length(X1)))
c(z_lb, z_hb)
[1] 0.1632625 0.1761045

Or under t-test:

# qt is the quantile function for a standard students' t cdf.
t_lb <- qt(.025, df=length(X1) - 1)*sd(X1)/sqrt(length(X1)) + mean(X1)
t_hb <- qt(.975, df=length(X1) - 1)*sd(X1)/sqrt(length(X1)) + mean(X1)
c(t_lb, t_hb)  # Check if it is consistent with the result of a t.test.
[1] 0.1632547 0.1761123

The interpretation of the frequentist confidence interval is not straightforward and has been mis-understood quite often. A 95% confidence interval means that the interval has 95% chance to contain the true parameter value. It does NOT mean that the true parameter value has a 95% chance to be within the interval. The confidence level is describing the interval but not the population parameter.

In the frequentist world any population parameter is a constant, so there is no probabilistic description on it. Given any confidence interval the parameter will be either inside or outside the range. It is a deterministic but unknown event. The probabilistic description exists only for a random variable. Sample mean is a random variable. Confidence interval is a random variable. So a 95% confidence interval is a probabilistic description on the interval but not on the parameter.

Put it differently, if we were to repeat the sampling experiment many many times, then approximately 95% of the experiments we will have the resulting confidence interval containing the true parameter. For a given sample dataset, the confidence interval of the sample mean merely picks up all the values that cannot be rejected assuming the sample mean is the true mean at the given \(\alpha\)% where \(1 - \alpha\) is called the level of confidence.

When confidence interval is used in a hypothesis test, we simply check if the interval covers the null value. If a 95% interval covers the null value, it is equivalently to say that we are not able to reject the null at 5% significance level.

To illustrate visually, we plot the sampling distribution of sample mean under the true mean (in black), along with an extreme case of a sample mean whose 95% C.I. barely contains the true mean (in blue and red).

plot_ci <- function(X, true_mean, compare_mean=NULL, true_sd=NULL,
                    annotate_sample_mean=TRUE, xlim_adj=c(3, 6), ...) {
  sample_mean <- mean(X)  # Not used.
  se <-
    if ( is.null(true_sd) ) {
      # If true population sd is unknown (practically always the case),
      # we can plug in the sample sd instead as an approximation.
      sample_sd <- sd(X)
      sample_sd / sqrt(length(X))
    } else {
      true_sd / sqrt(length(X))
    }
  err <- qnorm(.975, mean=0, sd=se)
  if ( is.null(compare_mean) ) {
    compare_mean <- true_mean + err
  }
  z_hb <- compare_mean + err
  z_lb <- compare_mean - err
  x <- seq(true_mean - xlim_adj[1]*se, true_mean + xlim_adj[2]*se, length=1000)
  density_true <- dnorm(x, mean=true_mean, sd=se)
  density_compare <- dnorm(x, mean=compare_mean, sd=se)
  plot(x, density_true, type="l", yaxs="i", ylab="Density", ...)
  lines(x, density_compare, type="l", col="blue")
  abline(v=compare_mean, col="blue", lty=2)
  abline(v=c(z_lb, z_hb), col="red", lty=2)
  if ( annotate_sample_mean ) {
    text(compare_mean, mean(density_compare) / 2, pos=4, col="blue", lty=2,
         sprintf("Extreme Sample Mean = %s", round(sample_mean, 4)))
  }
  arrows(x0=z_lb, y0=diff(range(density_true))/2,
         x1=z_hb, y1=diff(range(density_true))/2,
         code=3, lty=2, col="red")
  text(compare_mean, diff(range(density_true))/2,
       "95% Confidence Interval", pos=1, col="red")

  # Two-tailed p-value area under the truth. (Plot one side only.)
  area_col <- rgb(1, 0, 0, 0.5)
  if ( compare_mean >= true_mean ) {
    polygon(c(compare_mean, x[x >= compare_mean], max(x)),
            c(0, density_true[which(x >= compare_mean)], 0),
            col=area_col, border=NA)
  } else {
    polygon(c(min(x), x[x <= compare_mean], compare_mean),
            c(0, density_true[which(x <= compare_mean)], 0),
            col=area_col, border=NA)
  }
}

plot_ci(X1, true_mean=true_mean,
        main=bquote(bar(X) ~ "Sampling Distribution under True Mean"),
        sub="Extreme C.I. to Contain True Mean",
        xlab=bquote(bar(X)))

The red area corresponds to the two-tailed 95% significance, with a size of exactly 0.025. (We only colorize the right side.) It is also the p-value in this case since we purposely make the sample mean just as extreme to be on the edge of 95% significance level. Any resulting sample mean value falling into the area will have a C.I. not able to cover the true mean. The sampling distribution based on the extreme sample mean (in blue) is simply a shift of the true sampling distribution to the right.

To reiterate the fact that C.I. is a random variable, let’s repeat the sampling experiment for 9 times and plot all the results:

set.seed(777)

par(mfrow=c(3, 3), oma=c(3, 3, 0, 0), mar=c(3, 3, 2, 2))
for ( i in 1:9 ) {
  X2 <- rbeta(1000, beta_a, beta_b)
  plot_ci(X2, true_mean=true_mean, compare_mean=mean(X2),
          annotate_sample_mean=FALSE, xlim_adj=c(4, 4))
}
mtext(text="Confidence Interval as Random Variable", side=1, line=0, outer=TRUE)

As one can see, out of our 9 independent experiments there is one case where we wrongly rejected the true model. (The so-called Type-I Error.)

The key take-away about frequentist confidence interval:

  • It is a sampling statistic; hence a random variable.
  • There is no distributional information inside the interval; it’s either the interval does contain or does not contain the true parameter. (Which we will never know since the true parameter is an unknown constant.)
  • The larger the sample size the narrower the interval, since it is derived from a sampling distribution which by LLN will converge in probability to the true parameter value.

1.1.3 Generalization

Though in this section we only cover a very simple one-sample hypothesis testing under a univariate context, the general idea behind the scene for a full-fledged statistical model is about the same.

For example, in order to make inference about a coefficient \(\beta\) in a regression model \(y = X\beta + \epsilon\), a frequentist first tries to develop the asymptotic property of the sampling distribution of \(\beta\), then apply the hypothesis testing with a null of \(\beta = 0\) to test if the linear association between the target \(y\) and the variable \(X\) is significantly present. It turns out that CLT also applies to linear model coefficients. The rationale is that these coefficients are indeed a kind of weighted-average where the weights are determined by the covariance.

1.1.4 Critiques

This section describes the critiques well documented in Rouder et al. (2009).

One crucial limitation of the frequentist approach is that it does not allow us to state evidience for but only against the null hypothesis. What does this exactly mean? Let’s examine the behavior of p-value under two different scenarios: when the null model is false and when the null model is true.

When Null is False

As sample size grows, the z-statistic (or t-statistic) grows unboundedly, resulting in p-value approching zero.

Let’s assume the null model is:

# Derive sampling distribution under the null.
null_mean <- 0.25
null_sd <- 1
null_model_pdf <- function(x, n) dnorm(x, null_mean, null_sd / sqrt(n))

\(\mbox{Normal(0.25, 1)},\)

and the true model is:

# Derive sampling distribution under the truth.
real_mean <- 0
real_sd <- 1
real_model_pdf <- function(x, n) dnorm(x, real_mean, real_sd / sqrt(n))

\(\mbox{Normal(0, 1)}.\)

Now we plot the sampling distribution of both models with random samples of varying sample size:

# Make partial functions for ease of using curve() for plotting.
null_pdf <- function(n) function(x) null_model_pdf(x, n=n)
real_pdf <- function(n) function(x) real_model_pdf(x, n=n)

plot_sampling_dist_2 <- function(x, null_pdf, true_pdf, null_mean, true_mean) {
  sample_mean <- mean(x)
  se <- sd(x) / sqrt(length(x))
  p1 <- null_pdf(length(x))
  xrange <- c(sample_mean - 6*se, sample_mean + 6*se)
  xticks <- seq(xrange[1], xrange[2], length.out=500)
  curve(p1, from=xrange[1], to=xrange[2], yaxs="i",
        main=sprintf("Sampling Dist. at N = %s", length(x)))
  p2 <- true_pdf(length(x))
  curve(p2, from=xrange[1], to=xrange[2], add=TRUE, col="red", lty=2)
  abline(v=true_mean, lty=2, col="red")
  abline(v=sample_mean, col="red")
  # Colorize p-value.
  area_col <- rgb(1, 0, 0, 0.5)
  if ( sample_mean <= null_mean ) {
    polygon(c(min(x), xticks[xticks <= sample_mean], sample_mean),
            c(0, p1(xticks)[which(xticks <= sample_mean)], 0),
            col=area_col, border=NA)
  } else {
    polygon(c(sample_mean, xticks[xticks > sample_mean], max(x)),
            c(0, p1(xticks)[which(xticks > sample_mean)], 0),
            col=area_col, border=NA)
  }
}

set.seed(777)
par(mfrow=c(2, 2), oma=c(3, 3, 0, 0), mar=c(3, 3, 2, 2))
for ( n in c(10, 30, 100, 300) ) {
  x <- rnorm(n, real_mean, real_sd)
  plot_sampling_dist_2(x, null_pdf, real_pdf, null_mean, real_mean)
}
mtext(text="Vanishing P-Value Given Null is False", side=1, line=0, outer=TRUE)

We use red-dot line as the true sampling distrubition while the solid-black line as sampling distribution under the null. The solid-red line is our simulated sample mean at different sample size.

As one can see, as sample size increases, p-value will converge to 0 since the null distribution will concentrate more and more on the wrong mean, making it less and less possible for the sample mean to fall into the range; hence making our sample data extremely unlikely, which in turn leads to rejection of the null.

This statistical property is a desired one as long as the null is actually false. It suggests that the evidence against the null can be strengthen if we collect a larger random sample. In other words, the test is statistically consistent thanks to its convergence in large sample.

When Null is True

What if the null model is actually true? That is, our null is the same as the true model: \(\mbox{Normal(0, 1)}\). We again plot the simulation of p-value under different sample sizes:

par(mfrow=c(2, 2), oma=c(3, 3, 0, 0), mar=c(3, 3, 2, 2))
for ( n in c(10, 30, 100, 300) ) {
  x <- rnorm(n, real_mean, real_sd)
  plot_sampling_dist_2(x, real_pdf, real_pdf, real_mean, real_mean)
}
mtext(text="Vanishing P-Value Given Null is True", side=1, line=0, outer=TRUE)

As one may already realize, in this case p-value is invariant in sample size. No matter how large our random sample is, there is no way to strengthen the evidence for the null. This is indeed why in the frequentist statistical wordings we never accept the null, but we either reject or cannot reject the null.

To sum up, the test is statistically consistent when the null is false. The error rate of not rejecting the null converge to 0 as sample size grows infinitely. The test is, however, inconsistent when the null is true. There is always a \(\alpha\)% of error to reject the correct null model regardless of the sample size, may it be a 5% or a 10% at the choice of conventional practice. Such property leads to potential overstatement of evidence against the null.

1.2 A Binomial Problem

Before we take a full Baysian approach to our first example, let’s consider another example with analytical solution available.

Consider a coin toss game, with \(N\) number of tosses and we are counting the number of heads as \(y\). The number of heads follows a Binomial distribution with two parameters:

\[ Y \sim \mbox{Binomial}(N, p). \]

We are interested in guessing the (unknown) true proportion of heads (denoted as \(p\)) out of \(N\) tosses.

Let’s simulate a set of \(N\) trials and count the heads:

set.seed(777)
N <- 10
p <- .5
(y <- rbinom(1, N, p))
[1] 6

By observing the fact that out of 10 trials we got 6 heads, we’d like to test the following two-sided hypothesis:

\[ \begin{aligned} H_0 &: \mbox{The coin is fair with p = .5}, \\ H_1 &: \mbox{The coin is biased}. \end{aligned} \]

This is also called a proportion test in the literature.

1.2.1 Frequentist Proportion Test

We can quickly explore the frequentist solution for this task first.

We calculate the sample proportion as \(\hat{p}\) and again by CLT we have the sample proportion to distribute asymptotically Normal:3

\[ \begin{equation} \hat{p} \overset{d}\sim \mbox{Normal}(p, \sqrt{\frac{p(1-p)}{N}}). \end{equation} \]

Calculation of the p-value is then very straightforward:

# Calculate the area under the PDF of the sampling distribution,
# provided that the null hypothesis is true.
prop_p <-
  if ( y/N > p ) {
    (1 - pnorm(y/N, p, sqrt(p*(1-p)/N)))*2
  } else {
    pnorm(y/N, p, sqrt(p*(1-p)/N))*2
  }
prop_p
[1] 0.5270893

We can verify the result via using R’s built-in prop.test:

# We purposely turn off the continuity correction factor for comparison.
# But the correction can be sizable if sample size is small since
# when sample size is small the Normal approximation is not very good.
# Another way is to do the exact Binomial test: see ?binom.test for details.
prop.test(y, N, p, correct=FALSE)

    1-sample proportions test without continuity correction

data:  y out of N, null probability p
X-squared = 0.4, df = 1, p-value = 0.5271
alternative hypothesis: true p is not equal to 0.5
95 percent confidence interval:
 0.3126738 0.8318197
sample estimates:
  p
0.6 

Based on the result, we have no enough evidence to reject the null hypothesis at the conventional significance level (5%).

1.2.2 Analytical Bayesian

Now move onto a Baysian approach.

Here we focus on our alternative hypothesis which suggests the coin is not fair. To do Bayesian one must lay out one’s exact prior on being “not fair.” Let’s first consider a discrete set of possible \(\theta\)

(theta <- seq(0, 1, length.out=5))
[1] 0.00 0.25 0.50 0.75 1.00

with equal probability

(theta_prior <- rep(1, length(theta)) / length(theta))  # Density function for theta.
[1] 0.2 0.2 0.2 0.2 0.2

That is, we believe a priori that there are in total 5 possible theta values listed above for the model parameter \(p\). Every value is equally possible. (Obviosuly, the coin is only fair if \(\theta = 0.5\) with probability 1.)

The probability mass function of a binomial distribution can be written as:

\[ P(y) = C^N_y \cdot p^y(1-p)^{N-y}. \]

Here \(p\) is our model parameter and it follows our discrete prior \(\theta\).

In order to derive the complete posterior \(P(\theta|y)\) (using the Bayes’ Law from equation \(\eqref{eq:bayeslaw}\)) for our alternative model, we need to calculate \(P(y)\) the marginal probability of data at the denominator:

\[ P(y) = \sum_nP(y|\theta_n)P(\theta_n). \]

Note that the above is just a discrete version of equation \(\eqref{eq:marginal_lik}\).

Here comes the results:

# P(y|theta) for all theta. The data likelihood.
p_y_theta <- choose(N, y)*theta^y*(1 - theta)^(N-y)

# P(y). Marginal probability of y. This is based on the law of total probability.
p_y <- sum(theta_prior * p_y_theta)

# P(theta|y). Derived by the Bayes' Law.
p_theta_y <- (p_y_theta * theta_prior) / p_y

data.frame(theta, theta_prior, p_y_theta, p_theta_y)

The table is a full analytical solution for our Bayesian model under the alternative hypothesis given the observed data and our discrete prior.

The table reveals how our naive prior is shaped by the data observed. For example, originally we think there is a 20.0% chance of \(\theta = 0\) meaning that the coin is completely biased toward the tail. But the data tells us that out of 10 tosses we got 6 heads. So indeed there is no chance that \(\theta = 0\)! Same logic applies to the prior of \(\theta = 1\). Their posteriors both become zero based on evidence brought by data.

Another subtle fact is that, there is no way for us to support a value that is not covered by the prior. Since our prior only support 5 possible values, our posterior can only adapt to these 5 as well. This is exactly why we’d like to replace the discrete prior with a continous one, unless we have a very strong prior that dictates the discrete case instead.

Among all the possible parameter values, the one with the highest posterior is called the maximum a posteriori (MAP) estimate:

theta[which.max(p_theta_y)]
[1] 0.5

Usually the Bayesian point estimate of a parameter is instead the expected value of the posterior. In this case our point estimate with the discrete prior will be:

# Check our posterior follows the law of prob.
stopifnot(sum(p_theta_y) == 1)

# Bayesian point estimate under the alternative hypothesis.
sum(p_theta_y * theta)
[1] 0.5883315

which is interestingly quite close to the sample mean 0.6.

Indeed, if our prior is a standard Uniform, MAP estimate is the same as the MLE estimate.

To see this clearly, let’s assume a discrete prior but with much more possible values and plot the prior along with the posterior:

plot_update <- function(theta, p_theta) {
  p_y_theta <- choose(N, y)*theta^y*(1 - theta)^(N-y)
  p_y <- sum(p_theta * p_y_theta)
  p_theta_y <- (p_y_theta * p_theta) / p_y

  plot(x=theta, y=p_theta_y, type="l", col="red",
       main=sprintf("Bayesian Update on Binomial(%s, %s)", N, p),
       xlab=expression(theta), ylab="Mass (Probability)")
  lines(x=theta, y=p_theta, col="blue")
  abline(v=y/N, lty=2)
  text(y/N, p_theta[1]*.8, pos=4, sprintf("Observed p = %s", round(y/N, 2)))
  legend("topleft", legend=c("Posterior", "Prior"),
         col=c("red", "blue"), lty=1)
}

many_theta <- seq(0, 1, length.out=1000)
p_theta <- rep(1, length(many_theta)) / length(many_theta)
plot_update(many_theta, p_theta)

The posterior will have its maximum exactly at the sample mean.

Uniform distribution in this case can be regarded as a general noninformative or flat prior. Under such prior our test becomes:

\[ \begin{aligned} H_0 &: Y \sim \mbox{Binomial}(N=10, p=0.5), \\ H_1 &: Y \sim \mbox{Binomial}(N=10, p=\theta); \mbox{ } \theta \sim \mbox{Uniform}(0,1). \end{aligned} \]

That is, we believe a priori the coin can be of any kind of bias with equal possibility.

Our marginal probability of the observed data given the alternative hypothesis becomes:4

\[ \begin{aligned} P(y|H_1) &= \int_0^1C_6^{10}\theta^6(1 - \theta)^{4}d\theta \\ &= C_6^{10} \times \frac{6!\cdot4!}{(10+1)!} \\ &= 0.09\overline{09}. \end{aligned} \]

Instead of solving for the analytical solution which could cause a headache, here we take the chance to demo a numerical solution using simulation. We will generate random draws of \(\theta\) from its prior (a standard Uniform) and calculate the resulting probability provided that our observed data \(y\) is 6.

set.seed(777)
(p_y_h1a <- mean(dbinom(y, N, runif(1e6))))
[1] 0.09089042

As one can see, for a large repetitions, the numerical solution is very close to the analytical one. This is indeed the concept of Monte Carlo method, which we will cover later in this tutorial.5

1.2.3 Generalization to Logistic Regression

How is this simple Binomial problem even relevant to our real world modeling?

Indeed the problem can be re-formulated as a logistic regression with only a constant term as the predictor variable. Each trial in a Binmoial is just a Bernoulli process. Consider each Bernoulli as our outcome observation without any other explanatory variable, a \(\mbox{Binomial}(N, p)\) means we have \(N\) records of observation. This is a binary classification problem modeled by logistic regression. In R we can use the built-in glm function to estimate the model:

# Create raw data. Binom(N, p) is simply N records of Bernoulli(p).
yl <- numeric(N)
yl[1:y] <- 1
d <- data.frame(y=yl)

# Constant-only logit model.
coef(glm(y ~ 1, data=d, family=binomial))
(Intercept)
  0.4054651 

The MLE estimator of the coefficient on constant term is the log odds ratio of the observed data. To confirm this:

odds <- (y/N) / (1 - y/N)
log(odds)
[1] 0.4054651

And we can of course solve for the \(p\) from odds ratio:

odds / (1 + odds)
[1] 0.6

which is just the sample mean.

1.3 Bayes Factor

In the previous section we examine a Bayesian model under the alternative model. But we didn’t come to any conclusion about the comparsion between the alternative and the null model. How do Bayesians look at the hypothesis testing task?

First of all, Bayesians depict belief as a distribution rather than a constant. So the hypothesis itself can become probability distribution. Again by Bayes’ Law:

\[ \begin{aligned} P(H_0 | y) = \frac{P(y|H_0) \cdot P(H_0)}{P(y)}, \\ P(H_1 | y) = \frac{P(y|H_1) \cdot P(H_1)}{P(y)}. \end{aligned} \]

In some cases we may also have \(P(H_0) + P(H_1) = 1\), which is not necessary in a more general setup.

Based on the above formula, we can calculate the posterior odds of two competing beliefs (or models) as:

\[ \begin{equation} \underbrace{\frac{P(H_0|y)}{P(H_1|y)}}_\text{posterior odds} = \underbrace{\frac{P(y|H_0)}{P(y|H_1)}}_\text{Bayes factor} \cdot \underbrace{\frac{P(H_0)}{P(H_1)}}_\text{prior odds}. \end{equation} \]

Bayes factor here is defined as the ratio of posterior odds to prior odds for two competing models. It can be viewed as an update of model preference from prior to posterior given the information provided by the data. One can regard Bayes factor as the counterpart of the frequentist p-value in their philosophy about hypothesis testing. The higher the factor, the more the prior belief shifts toward the posterior, or equivalently the larger the update. Bayes factor is a measure of change in relative belief after observing the data.

Usually it is natural to set the prior odds \(\frac{P(H_0)}{P(H_1)} = 1\) to express neutrality before observing the data as long as no strong prior preference presents. Calculating the Bayes factor then involves determination of the marginal likelihood conditioned on model: \(P(y|H_0)\) and \(P(y|H_1)\).

To be specific,

\[ \begin{equation} \label{eq:marginal_lik_h0} P(y|H_0) = \int P(y|\theta, H_0)P(\theta|H_0)d\theta = E_{prior}\big[ P(y|\theta, H_0) \big], \end{equation} \]

where \(\theta\) is the parameter under hypothesis \(H_0\). This is exactly the marginal probability of data under \(H_0\), we are essentially rewriting equation \(\eqref{eq:marginal_lik}\) to be conditioned on the given hypothesis.

Readers should not confuse \(P(y|H_0)\) with \(P(y|\theta)\). The latter is the data likelihood given a specific model parameter (implicitly following a model hypothesis), while the former is the data likelihood given the model hypothesis, or the marginal likelihood with consideration of all possible parameter values under that hypothesis.

Back to our binomial example. We can now calculate the Bayes factor \(\frac{P(y|H_0)}{P(y|H_1)}\) analytically.

Marginal probability under the null \(P(y|H_0)\) is

(p_y_h0 <- choose(N, y)*p^y*(1 - p)^(N-y) )  # Or equivalently: dbinom(y, N, p)
[1] 0.2050781

For marginal probability under the alternative \(P(y|H_1)\), with the discrete prior it is:

(p_y_h1d <- sum(theta_prior * (choose(N, y)*theta^y*(1 - theta)^(N-y))))
[1] 0.07345963

or with a continuous prior it is:

(p_y_h1c <- choose(N, y) * factorial(y)*factorial(N-y)/factorial(N + 1))
[1] 0.09090909

The Bayes factor is hence:

p_y_h0 / p_y_h1d
[1] 2.791712

for the discrete prior or

p_y_h0 / p_y_h1c
[1] 2.255859

for a Uniform prior.

The larger the Bayes factor the more supporting evidence for the numerator model against the denominator model.

The package BayesFactor (Morey and Rouder 2018) in R implements a variety of different hypothesis testing tools based on the usage of Bayes factor.

library(BayesFactor)
Loading required package: coda
Loading required package: Matrix
************
Welcome to BayesFactor 0.9.12-4.2. If you have questions, please contact Richard Morey (richarddmorey@gmail.com).

Type BFManual() to open the manual.
************

There is a function proportionBF for a proportion test at our disposal:

BayesFactor::proportionBF(y, N, p=.5)
Bayes factor analysis
--------------
[1] Alt., p0=0.5, r=0.5 : 0.6908793 ±0%

Against denominator:
  Null, p = 0.5
---
Bayes factor type: BFproportion, logistic

One may find that the BF value is a bit off from our calculation. This is because BayesFactor::proportionBF uses a different prior on the default alternative model. Based on the document (?proportionBF) we realize that the default prior is indeed a logistic distribution, while ours is simply a standard Uniform.

Numerical Bayesian on Marginal Likelihood

In this simple example we are able to calculate the marginal probability and hence the Bayes factor analytically. In a more general setup, however, analytical solution may well not exist or may not be tractable. In such case usually we need to resort to numerical solutions. In the literature there are quite some different approaches proposed to overcome the issue. Out of all those proposals, a Monte Carlo method called bridge sampling becomes the most promising one and is now the go-to method for numerical solution for estimation of marginal likelihood.

1.4 The JZS Prior for Mean Test

It’s time to revisit our very first example in this tutorial.

Remember that in the example we have the following test for population mean:

\[H_0: \mu = 0.175, \\ H_1: \mu \neq 0.175.\]

given a sample dataset randomgly drawn from a (unknown) Beta distribution.

In order to proceed with a Bayesian approach, we need to explicitly setup our prior. We will introduce the well-known Jeffreys-Zellner-Siow (JZS) prior for Baysian hypothesis testing on population mean.

There are indeed different ways of doing Bayesian on a hypothesis testing task. Research in this field is still active and evolving. In this tutorial we will only walk-through the classical one with JZS prior as a baseline approach.

To re-formulate the one-sample test, one may rewrite it as something like:

\[ \begin{align} H_0 &: \mu = 0, \\ H_1 &: \mu \sim \mbox{Normal}(0, \sigma_{\mu}^2), \end{align} \]

along with another prior on the population standard deviation \(\sigma\).

Here a noninformative prior (hence the most objective) on \(\sigma_{\mu}^2\) will technically mean to set \(\sigma_{\mu}^2 = \infty\). This is because the larger the variance the more the uncertainty about the distribution. Intuitively, the larger the variance the more the prior will be shifted toward the posterior by data. But mathematically setting it to infinity will result in unbounded support for the null model as sample size grows, which in turn makes the test useless. The fact is called the Jeffreys-Lindley paradox in the literature. To work-around this, it is proposed to test the standardized effect size defined as:

\[ \delta \equiv \frac{\mu}{\sigma}. \]

The test then becomes:

\[ \begin{align} H_0 &: \delta = 0, \\ H_1 &: \delta \sim \mbox{Normal}(0, \sigma_{\delta}^2). \end{align} \]

With a inverse-chi-squared prior on \(\sigma_{\delta}^2\) it is effectively saying:6

\[ \begin{align} H_0 &: \delta = 0, \\ H_1 &: \delta \sim r \cdot \mbox{Cauchy}, \end{align} \]

where \(r\) is a scale factor to control the expected size of the effect a priori.

JZS further assumes the prior on \(\sigma\) to be noninformative as well: \(P(\sigma^2) \propto \frac{1}{\sigma^2}\). (See Jeffreys’ prior for more technical details.)

BayesFactor implements exactly the JZS prior for one-sample testing:

BayesFactor::ttestBF(X1, mu=h0_mean, rscale=1)  # rscale = 1 for standard Cauchy.
Bayes factor analysis
--------------
[1] Alt., r=1 : 0.09376657 ±0%

Against denominator:
  Null, mu = 0.175
---
Bayes factor type: BFoneSample, JZS
# Or equivalently:
# BayesFactor::ttestBF(X1 - h0_mean, mu=0, rscale=1)

BayesFactor::ttestBF by default reports the Bayes factor for the alternative against the null. That is, the numerator model is the alternative and the denominator the null.

Or we take the reciproal of the test:

bftest_01 <- 1 / BayesFactor::ttestBF(X1, mu=h0_mean, rscale=1)
exp(bftest_01@bayesFactor$bf)  # Take expotential since the raw number is in log.
[1] 10.66478

JZS is not the only prior proposed in the literature but surely is a classical one. It also comes with a clear advantage back in the old time: it has a closed-form (though tedious) solution. In reality, however, closed-form solution does not exist for most of the probablistic models, even a moderately parameterized one. So the marginal likelihood can not be calculated analytically. In such case we need to resort to numerical method. Indeed, even for the closed-form integral in this problem, we need to use an approximation method called Gaussian quadrature to solve for it.

Other than calculating the marginal probability, we can also use BayesFactor to generate posterior parameter samples using Markov chain Monte Carlo (MCMC) method:

bftest_mcmc_samples <- BayesFactor::ttestBF(X1, mu=h0_mean, rscale=1,
                                            posterior=TRUE, iterations=1000,
                                            progress=FALSE)
head(bftest_mcmc_samples)
Markov Chain Monte Carlo (MCMC) output:
Start = 1
End = 7
Thinning interval = 1
            mu       sig2    delta        g
[1,] 0.1700247 0.01058286 1.652762 1.396052
[2,] 0.1700231 0.01055446 1.654970 1.256829
[3,] 0.1643468 0.01074145 1.585733 4.604765
[4,] 0.1674783 0.01172067 1.546971 1.379949
[5,] 0.1670325 0.01086682 1.602322 5.825714
[6,] 0.1725047 0.01009811 1.716646 5.396630
[7,] 0.1673158 0.01005497 1.668578 2.207440
# Even more, to plot sample distribution and trace:
# plot(bftest_mcmc_samples[,1:2])

As MCMC has become the dominant approach to solve for a Bayesian model, we will deep-dive into it in the next section.

2 Markov Chain Monte Carlo

In most applications the posterior density can not be derived analytically, we need to resort to simulation method to numerically approximate the solution. This is where MCMC comes in handy.

The goal of MCMC is to generate random samples without knowing the exact probability distribution function of the target distribution–This is the Markov chain part. Then we can calculate our estimator of interest based on the random samples–this is the Monte Carlo part.

The idea is that if we can construct a Markov chain with transition probabilities that converges to equilibrium probabilities that match the target distribution, we can effectively generate random samples from the equilibrium probabilities by starting at any state with initial samples discarded. There are multiple methods for constructing Markov chain with equilibrium, such as Metropolis sampling, Gibbs sampling, Hamiltonian method, …, etc.

MCMC is useful since in Bayesian modeling, usually the model distribution functions are NOT analytically tractable. (No closed-form solution.) Generating random samples from a Normal distribution is easy since its analytical form is clearly determined. A moderately configured Bayesian probabilistic model, on the other hand, can easily become intractable without the intention to make it really complicated.

2.1 Monte Carlo for Central Limit Theorem

First we illustrate Monte Carlo by examining CLT using computer-driven random drawing simulation rather than direct mathematic derivation.

Again that CLT suggests the limiting distribution of sample mean \(\bar{X}\) with sample size \(N\) is Normal as in equation \(\eqref{eq:clt}\).

Let’s use Monte Carlo simulation to verify if this is true. For simplicity we assume the population is a standard Uniform distribution \(X \sim \mbox{Uniform}(0,1)\). However, remember that in reality virtually any population distribution (with finite variance) should work.

The process is as the followings:

  1. Draw sample data of a size \(N\)
  2. Calculate the sample mean from the above sample data
  3. Repeat step 1 and 2 for many times (say, 100 times of repetition)

And we plot the distribution of all the sample means, with the blue line depicting the theoretical limiting sampling distribution that we’d like to approximate. We also do this for different values of sample sizes. The results are plotted all together as below.

plot_mc_sample_mean_clt <- function(N, sample_func=runif, n_rep=100, seed=777, ...) {
  set.seed(seed)
  # Generate random draws from Uniform(0,1) and calculate the sample mean.
  # Do this for `n_rep` times.
  x_bar <- numeric(n_rep)
  for ( r in 1:n_rep ) {
    x_bar[r] <- mean(sample_func(N))
  }
  # Plot.
  title <-bquote("Sampling Distribution of " ~ bar(X) ~ " at N = " ~ .(N))
  x <- seq(min(x_bar), max(x_bar), length=100)
  y <- dnorm(x, .5, sqrt(1/12)/sqrt(N))  # Based on true mean and sd of a standard Uniform.
  hist(x_bar, probability=TRUE, main=title, xlab=NULL, ylim=c(0, max(y)*1.1), ...)
  lines(x, y, col="blue", lwd=2)
}

par(mfrow=c(2, 2))
for ( n in c(50, 100, 500, 1000) ) plot_mc_sample_mean_clt(N=n)
mtext(text="Monte Carlo Simulation with 100 Repetitions", side=1, line=-2, outer=TRUE)

Obviously the approximation by Monte Carlo is quite good at various values of sample size \(N\) with even a small number of repetition in this simple case. If we enlarge the simulation repetition number, the approximation can be of course better:

par(mfrow=c(2, 2))
for ( n in c(50, 100, 500, 1000) ) plot_mc_sample_mean_clt(N=n, n_rep=1000)
mtext(text="Monte Carlo Simulation with 1000 Repetitions", side=1, line=-2, outer=TRUE)

Usually a Monte Carlo exercise involves in number of repetition at thousands at least.

To reiterate the importance of random sampling, let’s do the same simulation but this time with a biased sampler. We create a sequentially correlated sampler from a standard Uniform.

non_random_sampler <- function(n) {
  # A random process drawing from Uniform that is auto-related.
  get_y_t1 <- function(y_t0) {
    sample(c(runif(1, 0, y_t0), runif(1, y_t0, 1)), size=1, prob=c(.7, .3))
  }
  y_t0 <- runif(1)
  y <- numeric(n)
  y[1] <- y_t0
  for ( i in 2:n ) {
    y[i] <- get_y_t1(y_t0)
    y_t0 <- y[i]
  }
  y
}

plot_mc_sample_mean_clt(N=50, sample_func=non_random_sampler, n_rep=1000,
                        sub="Monte Carlo for Biased Samples")

As one can see, the resulting MC samples can no longer approximate the theoretical limiting distribution of the sample mean.

2.2 Monte Carlo for the Law of Large Number

Continue from the above example, we can also use Monte Carlo to examine the Law of Large Number (LLN hereafter). LLN suggests that the sample mean will converge to population mean in probability. That is,

\[ (\bar{x} - \mu) \overset{p}\rightarrow 0. \]

By ploting the overlapping density at a variety of sample sizes we can clearly see that the sampling distribution of sample mean error is converging around 0. So the intuitive conclusion: The larger the sample, the less the variance of our guessing.

plot_mc_sample_mean_lln <- function(N_list, n_rep=500) {
  require(lattice, quietly=TRUE)

  mc_mean <- function(N, n_rep) {
    x_bar <- numeric(n_rep)
    for ( r in 1:n_rep ) {
      x_bar[r] <- mean(runif(N))
    }
    x_bar
  }

  res <- matrix(NA_real_, nrow=n_rep, ncol=length(N_list))
  for ( i in 1:length(N_list)) {
    res[,i] <- mc_mean(N_list[i], n_rep) - .5
  }
  res <- as.data.frame(res)
  colnames(res) <- paste0("N = ", N_list)
  res <- stack(res)
  title <-bquote("Sampling Distribution of " ~ bar(X) - mu)
  lattice::densityplot(~ values, group=ind, data=res, auto.key=TRUE,
                       main=title, xlab="Sample Mean Error")
}

plot_mc_sample_mean_lln(c(50, 100, 500, 1000, 10000))

2.3 Monte Carlo for Confidence Interval

Previously to illustrate the idea that confidence interval is a random variable, we conduct 9 independent experiments and visualize the results. As a software engineer one should probably think: Why stop at only 9 experiments?

We can extend the exercise as a Monte Carlo one by generating as many experiments as we want. We can then summarize the overall results to examine whether the 95% confidence interval actually contains the true parameter approximately 95% of the time.

run_ci_exp <- function(X, true_mean) {
  xbar <- mean(X)
  err <- qnorm(.975, mean=0, sd=sd(X)/sqrt(length(X)))
  z_hb <- xbar + err
  z_lb <- xbar - err
  true_mean >= z_lb & true_mean <= z_hb
}

set.seed(777)
nr <- 1000
mc_ci_result <- rep(NA, nr)
for ( r in 1:nr ) {
  mc_ci_result[r] <- run_ci_exp(rbeta(1000, beta_a, beta_b), true_mean)
}

mean(mc_ci_result)
[1] 0.952

The result suggests that out of 1000 independent experiments there are 952 experiments we have the 95% C.I. containing the true mean. The Monte Carlo method has confirmed our interpretation of C.I..

2.4 Rejection Sampling

The previous example works perfectly fine since we have absolutely no problem in generating random samples from a well-known distribution which is the Standard Uniform distribution. In cases where the target distribution is not tractable, we need to rely on Markov chain Monte Carlo method. The sampling here is termed rejection sampling since it involves in a Markov process where the next sample is based on the previous one and subject to a rate of rejection.

Here is the general process of rejection sampling in one round:

  1. [Initialization] Start with an initial sample
  2. [Sample Proposal] Propose a new sample based on the previous one with a random noise
  3. [Acceptance Test] Accept or reject the new sample stochastically based on likelihood ratio of the new sample to the previous one
    • If the proposed sample is accepted, the next proposed sample will be based on it
    • It the proposed sample is rejected, the previous example is counted again as a valid sample, and a new sample is proposed based on it

Based on various ways of implementing the step 2 and step 3, different algorithms have been developed. The process will run as many rounds as possible to get the required sample size.

The random noise in step 2 is usually drawn from a Normal distribution centered at zero, referred to as the proposal distribution. A Normal proposal distribution with zero mean will make the sample generating process effectively a random walk stochastic process.

Step 3 is indeed the key component of the process, referred to as the acceptance test. Without this step, there is no chance that a random walk can approximate a given distribution.

2.4.1 Metropolis-Hastings Sampling

The Metropolis-Hastings algorithm is a classical method in MCMC family. It is probably one of the mostly quoted MCMC methods. Many other modern methods are based on top of it instead of a re-invention. In the acceptance test of Metropolis sampler, the proposed sample will be accepted if its posterior likelihood is larger than the previous sample. If the likelihood is lower, the proposed sample will still have a chance to be accepted, with the probability matching the likelihood ratio of the proposed sample to the previously accepted sample.

To illustrate, if the proposed sample has a likelihood value of 1 and that of the previous sample is 5, then the proposed sample will have a \(1/5 = 20\%\) chance to be still accpeted.

Let’s implement a Metropolis sampler for a target distribution which is Normal. This is for us to easily verify the (otherwise usually unknown) true population and the resulting samples from our sampler.

We can implement its very basic concept in just a few lines of code:

calc_lik_norm <- function(x, mean=10, sd=5) {
  # In actual application,
  # this function should calculate the posterior density of the parameter x given the data.
  # Here we just assume that is a N(mean,sd) for educational purpose.
  dnorm(x, mean, sd)
}

metro_sample <- function(likelihood, init_val, niter, seed=777) {
  # The function is not optimized at all. It is for illustration purpose only.
  set.seed(seed)

  current_val <- init_val
  current_val_lik <- likelihood(current_val)
  accepted_samples <- init_val
  n_accepted <- 0

  # Iteration.
  # No obvious way to vectorize this operation since it is serially dependent.
  for ( i in 1:niter ) {
    proposed_val <- current_val + rnorm(1, 0, 1)  # The proposal distribution is N(0,1).
    proposed_val_lik <- likelihood(proposed_val)
    accept_prob <- proposed_val_lik / current_val_lik
    if ( accept_prob > runif(1) ) {
      current_val <- proposed_val
      current_val_lik <- proposed_val_lik
      n_accepted <- n_accepted + 1
    }
    # Not that if the proposed sample is rejected,
    # the current sample is duplicated.
    accepted_samples <- c(accepted_samples, current_val)
  }

  list(accept_rate=n_accepted / niter, sample=accepted_samples)
}

We can plot the trace of the sampling process: The accepted sample values along the iteration.

init_val_1 <- 9
mcmc_res <- metro_sample(calc_lik_norm, init_val_1, niter=5000)

plot(mcmc_res$sample, type="l",
     main="Metropolis Sampling Trace",
     xlab="Iteration", ylab="MCMC Sample Value")

In this toy example, the acceptance rate of our sampler should be very high:

mcmc_res$accept_rate
[1] 0.943

We can also plot the distribution of resulting samples along with the true population density (in blue line) for comparison:

plot_mc_sample <- function(x, init_val, ...) {
  hist(x, prob=TRUE, xaxs="i", yaxs="i", ...)
  density <- curve(calc_lik_norm, from=min(x), to=max(x),
                   col="blue", add=TRUE)
  abline(v=init_val, col="red")
  text(init_val, mean(density$y)*.5, pos=2, "Initial Value", col="red")
}

plot_mc_sample(mcmc_res$sample, init_val_1)

On Initival Value

If the parameter value initializes at a reasonable location, we should see the trace plot exhibits stationarity over iterations. Any obvious non-stationarity in the beginning of the trace plot suggest a “burn-in” may be necessary–dropping samples generated in the early rounds.

For example, let’s purposely start the value at a far off location and plot the trace again:

init_val_2 <- 50
mcmc_res_2 <- metro_sample(calc_lik_norm, init_val_2, niter=5000)

plot(mcmc_res_2$sample, type="l",
     main="Metropolis Sampling Trace",
     xlab="Iteration", ylab="MCMC Sample Value")

plot_mc_sample(mcmc_res_2$sample, init_val_2)

We see the resulting distribution of samples has a fat right tail due to an inappropriate initial value, and the trace plot is obviously not stationary at the beginning rounds of iteration. The sampler still makes its way to the target distribution, but early draws should be removed or we will end up with an incorrect approximation if the number of sample size is not large enough to make the initial draws negligible.

We can examine the stability of the resulting distribution on different number of iterations with the far off initial value:

niter_1 <- 500L

par(mfrow=c(3, 3), oma=c(3, 3, 0, 0), mar=c(3, 3, 2, 2))
for ( i in 1:9 ) {
  s1 <- metro_sample(calc_lik_norm, init_val_2, niter=niter_1)$sample
  plot_mc_sample(s1, init_val_2, main=sprintf("%s Iterations@%s", niter_1, i))
}
{
mtext(text="Metropolis Sampling Exercises 1", side=1, line=0, outer=TRUE)
mtext(text="Density", side=2, line=0, outer=TRUE)
}

niter_2 <- 10000L

par(mfrow=c(3, 3), oma=c(3, 3, 0, 0), mar=c(3, 3, 2, 2))
for ( i in 1:9 ) {
  s2 <- metro_sample(calc_lik_norm, init_val_2, niter=niter_2)$sample
  plot_mc_sample(s2, init_val_2, main=sprintf("%s Iterations@%s", niter_2, i))
}
{
mtext(text="Metropolis Sampling Exercises 2", side=1, line=0, outer=TRUE)
mtext(text="Density", side=2, line=0, outer=TRUE)
}

For niter at 500 and 10000 we repeat the entire sampling process 9 times independently. As one can easily see, we need a relatively large number of repetitions in order to get a stable result of the sampler, especially when the initial value is not appropriate.

In practice there are several (non-exclusive) ways to handle the initial value:

  • Use MLE estimate (whenever available) as the initial value
  • Use multiple Markov chains separately with different initial values and do diagnostic check among results from different chains
  • Use burn-in: Remove early samples based on stationarity check

On Proposal Distribution

Of course not only the initial value but also the proposal distribution will affect the effectiveness of the sampling process. The size of the permutation is a hyperparameter of Metropolis sampler. Size too small will need more time to converge and is riskier to be trapped at local maximum density. Size too big will result in too low the acceptance rate since lots of the proposed value may not be even within the range of the target distribution. The sampling efficiency hence will be lower.

Metropolis Sampler for Other Distributions

For experimental purpose, let’s also simulate MCMC sampling for some other well-known distributions.

Beta Distribution
calc_lik_beta <- function(x, a=2, b=5) {
  dbeta(x, a, b)
}

mcmc_res_beta <- metro_sample(calc_lik_beta, 0, niter=5000)

par(mfrow=c(1, 2))
hist(mcmc_res_beta$sample, main="Metropolis Samples", xlab="X ~ Beta(2, 5)",
     sub="Does it look like a Beta distribution?")
plot(mcmc_res_beta$sample, type="l",
     main="Trace",
     xlab="Iteration", ylab="MCMC Sample Value")

x <- mcmc_res_beta$sample
hist(x, prob=TRUE, xaxs="i", yaxs="i")
density <- curve(calc_lik_beta, from=min(x), to=max(x), col="blue", add=TRUE)
abline(v=0, col="red")

Uniform Distribution
calc_lik_unif <- function(x, a=0, b=1) {
  dunif(x, min=a, max=b)
}

mcmc_res_3 <- metro_sample(calc_lik_unif, .01, niter=5000)

par(mfrow=c(1, 2))
hist(mcmc_res_3$sample, main="Metropolis Samples", xlab="X ~ Uniform(0, 1)",
     sub="Does it look like a Uniform distribution?")
plot(mcmc_res_3$sample, type="l",
     main="Trace",
     xlab="Iteration", ylab="MCMC Sample Value")

x <- mcmc_res_3$sample
hist(x, prob=TRUE, xaxs="i", yaxs="i")
density <- curve(calc_lik_unif, from=min(x), to=max(x), col="blue", add=TRUE)
abline(v=0, col="red")

Key Take-Away

The key take-away is that as long as we can calculate the posterior density, even without knowing the target distribution analytically, it is still possible to generate random sample out of it. Then we can apply Monte Carlo estimation on the resulting samples for model inference.

Practically speaking, thanks to MCMC, in the Bayes’ Law (equation \(\eqref{eq:bayeslaw}\)) we don’t need to calculate the denominator term (marginal likelihood of outcome) in order to generate random sample of our model parameters from its posterior distribution. We only need to calculate the numerator term.

Of course in the toy example here there is no such thing as posterior density per se, because we are not calculating the density conditioned on data observed. (We don’t observe any data at all!) In the latter section we will have a more realistic walk-through of how Metropolis sampling is applied under a full-fledged probabilistic model.

2.4.2 Gibbs Sampling

Gibbs sampling is yet another very popular MCMC method. It is designed particularly for joint distribution. That is, to draw random samples from multiple parameters with high correlation. The general idea is to draw samples from one parameter distribution conditional on the other parameters.

The hello-world example of a Gibbs sampler will be to draw random samples from a (supposedly complicated) target distribution \(f(x, y)\) by using only \(f(x|y)\) and \(f(y|x)\). Here \(x\) and \(y\) can be considered two parameters jointly determining the density function \(f(\cdot)\).

Readers interested in more details can refer to Resnik and Hardisty (2010) as a gentle introductory write-up with naive Bayes document classification as a detailed working example.

Notably, Gibbs sampling can also be used along with Metropolis sampling, referred to as “Metropolis within Gibbs.”

2.4.3 Hamiltonian Monte Carlo

Both Metropolis and Gibbs sampling are subject to inefficiency when it comes to complicated target distribution in high dimension.

Hamiltonian Monte Carlo (HMC) is another MCMC method based on top of Metropolis-Hastings algorithm. The difference is that it improves the way the proposed sample is generated, such that the search is more efficient and hence overall simulation time can be considerably reduced. The general idea of the Metropolis framework still holds. But instead of proposing one sample at a time purely at random, HMC proposes a series of new samples as a path to explore the probability space. And such path is guided by the gradient of the target density function. But gradient alone will not do the trick since gradient will guide the path directly to the mode of the density where the density is the largest but the volume is too small to fairly calculate the integral. To effectively explore the probability space we need to traverse within area with both moderate density and volume (area called the typical set). To accomplish this in HMC auxiliary momentum parameters are introduced such that the gradient together with the momentum will allow the path to have just enough “energy” to traverse the typical set without being pull to the density mode.

The detailed math involves differntial geometry and is way beyond the scope of our discussion. One can refer to Betancourt (2017) for a more in-depth but yet soft introduction to HMC.

3 Bridge Sampling

Bridge sampling is yet another Monte Carlo numerical method, designed to estimate marginal likelihood for a Bayesian model. Different from MCMC aiming at estimation for the posterior, the goal of bridge sampling is to estimate particularly for the denominator term in the Bayes’ Law given a model. Although the denominator is not neccessary at all when using MCMC to simulate samples drawn from the posterior density, there are other cases where we may want to solve for this term. The most common scenario is model comparison, with one obvious example being the calculation of Bayes factor. (Remember that Bayes factor is the ratio of marginal probability between two models.)

The package bridgesampling (Gronau and Singmann 2018) in R implements bridge sampling and is compatible with many other Bayesian libraries (notably rstan, among others).

library(bridgesampling)

Let’s use our binomial example previously discussed to illustrate the inner working of bridge sampling. A Binomial model with Uniform prior on the probability parameter is sometimes refered to as the Beta-Binomial model. (Apparently due to the fact that the marginal probability follows a form of Beta function.)

Formally,

\[ \begin{aligned} Y &\sim \mbox{Binomial}(N, \theta), \\ \theta &\sim \mbox{Uniform}(0, 1). \end{aligned} \]

An interesting fact about this model we didn’t mention previously is that the posterior is actually a Beta distribution

\[ \theta|y \sim \mbox{Beta}(y+1, N-y+1), \]

where \(y\) is the actual number of positive outcome out of \(N\) trials.

In the previous section we’ve tried using a naive Monte Carlo method to approximate the marginal likelihood, by taking advantage of the fact from \(\eqref{eq:marginal_lik_h0}\). That is, we simply generate random draws from prior and calculate the mean of the resulting conditional data likelihood as our approximation. It went well due to the simplicity of this particular problem. In general, however, the approximation may not be good for such a naive approach. Bridge sampling is then the solution for more complicated model setup.

The following write-up mainly follows Gronau et al. (2017).

The idea of bridge sampling is to introduce a proposal distribution \(g(\theta)\) along with a bridge function \(h(\theta)\) such that

\[ \begin{aligned} P(y) &= \int P(y|\theta)P(\theta)d\theta \\ &= \frac{\int P(y|\theta)P(\theta)h(\theta)g(\theta)d\theta}{\int P(y|\theta)P(\theta)h(\theta)g(\theta)d\theta} \cdot \frac{1}{\frac{1}{P(y)}} \\ &= \frac{\int P(y|\theta)P(\theta)h(\theta)g(\theta)d\theta}{\int \frac{P(y|\theta)P(\theta)}{P(y)}h(\theta)g(\theta)d\theta} \\ &= \frac{\int P(y|\theta)P(\theta)h(\theta)g(\theta)d\theta}{\int P(\theta|y)h(\theta)g(\theta)d\theta} \\ &= \frac{E_{g(\theta)}\big[P(y|\theta)P(\theta)h(\theta)\big]}{E_{post}\big[h(\theta)g(\theta)\big]} \end{aligned}. \]

In the above equation the denominator can be approximated by random parameter draws from the posterior and the numerator can be approximated by random parameter draws from the proposal distribution. We already know that we can use MCMC to generate random samples from the posterior; hence no problem in approximating the denominator term. The remaining question is how do we obtain a suitable proposal distribution and bridge function such that we can also approximate the numerator term.

We will skip the detailed mathematical derivation but in general we want a proposal distribution to resemble the posterior distribution as much as possible and with sufficient overlap in their support. A good candidate is a Normal distribution with its first two moments matching those of the posterior. But for a more complicated posterior (especially in very high dimension) other candidates may be more appropriate.

The choice of the bridge function is way beyond the scope of this tutorial. Roughly speaking it will be a function of sample sizes from both posterior and proposal sampling, and depend on data likelihood and marginal likelihood. Since it depends on marginal likelihood which is the very value we want to solve for, the actual bridge sampling will invovle in an iterative process where in each step the bridge function will be updated by the newest estimate of the marginal likelihood under convergence.

Back to our beta-binomial example, let’s do the bridge sampling using the package bridgesampling:

# Generate posterior samples.
# For a real-world problem we need to use MCMC.
# Here we simply take the mathematical advantage of knowing the posterior is indeed a Beta.
set.seed(777)
post_samples <- as.matrix(rbeta(5000, y+1, N-y+1))
colnames(post_samples) <- "theta"

# Define function to calculate the log unnormalized posterior density.
# We can use this function with a Metroplis sampler to derive the random posterior samples as well.
lup_betabinom <- function(theta, data) {
  log(choose(10, data)*theta^data*(1-theta)^(10-data))
}

mlh <- bridge_sampler(post_samples, log_posterior=lup_betabinom, data=y,
                      lb=setNames(-Inf, "theta"), ub=setNames(Inf, "theta"), silent=TRUE)
exp(mlh$logml)
[1] 0.0910414

The result is very close to the analytical solution derived in our previous section.

4 Bayesian Logistic Regression using R

The package mcmc (Geyer and Johnson 2019) implements seriously the Metropolis-Hastings algorithm. It also provieds a good example of a Bayesian logistic regression model. In this section we will take a close look at its introductory example. (Run vignette("demo", "mcmc") for the original write-up.)

4.1 Model Setup

The logistic regression model can be written as:

\[ P(y = 1) = \frac{1}{1+e^{-(X\beta)}}. \]

Simply put, a linear model \(\nu = X\beta + \epsilon\) is transformed into a probability space \(\mathbb{R} \in [0,1]\) via a logistic function \(\delta(t) = \frac{1}{1 + e^{-t}}\).

In order to do Bayesian analysis on this model, we need two more things:

  1. Our prior on all the regression coefficients \(\beta\), aka model parameters
  2. Function to calculate the posterior density of the parameters \(P(\beta|y)\), so that we can do MCMC sampling for the model parameters

Remember again (and again!) from Bayes’ Law \(\eqref{eq:bayeslaw}\), in order to calculate the posterior density, we need the data likelihood \(P(y|\beta)\) AND a prior on \(\beta\) that can help us specify \(P(\beta)\). What we are going to calculate is usually called the unnormalized density \(P(y|\beta) \cdot P(\beta)\), since the denominator won’t affect our MCMC solution to the system (and also in practice we will hardly know \(P(y)\) the true population distribution).

Let’s examine the toy example in mcmc package:

library(mcmc)
data(logit)  # This is a built-in dataset in `mcmc`.
head(logit)

And the result of a traditional frequentist approach:

fitted_model <- glm(y ~ x1 + x2 + x3 + x4, data=logit, family=binomial, x=TRUE)
summary(fitted_model)

Call:
glm(formula = y ~ x1 + x2 + x3 + x4, family = binomial, data = logit,
    x = TRUE)

Deviance Residuals:
    Min       1Q   Median       3Q      Max
-1.7461  -0.6907   0.1540   0.7041   2.1943

Coefficients:
            Estimate Std. Error z value Pr(>|z|)
(Intercept)   0.6328     0.3007   2.104  0.03536 *
x1            0.7390     0.3616   2.043  0.04100 *
x2            1.1137     0.3627   3.071  0.00213 **
x3            0.4781     0.3538   1.351  0.17663
x4            0.6944     0.3989   1.741  0.08172 .
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 137.628  on 99  degrees of freedom
Residual deviance:  87.668  on 95  degrees of freedom
AIC: 97.668

Number of Fisher Scoring iterations: 6

The parameter point estimate is solved by Newton’s method for a maximum likelihood estimator (MLE) implemented in R’s bulit-in glm function.7 The inference on these point estimates (based on the estimated variance of the point estimate and its sampling distribution property) is a frequentist’s point of view.

4.2 Constructing the Posterior

Now move on to the Bayesian approach of exactly the same model. To use mcmc::metrop the Metropolis sampling function we first need to implement our own function for calculating the unnormalized posterior density given the data and parameters.

In practice, in order to maintain numerical stability, we always use log-likelihood instead of the likelihood in raw scale. So instead of calculating

\[ \underbrace{P(\beta|y)}_\text{posterior density} \propto \underbrace{P(y|\beta)}_\text{data likelihood} \times \underbrace{P(\beta)}_\text{prior density}, \]

we end up with

\[ \underbrace{\ln P(\beta|y)}_\text{posterior log-likelihood} \propto \underbrace{\ln P(y|\beta)}_\text{data log-likelihood} + \underbrace{\ln P(\beta)}_\text{prior log-likelihood}. \]

Hence the posterior will be the sum of all log-likelihood of each observed data point and the log-likelihood of our parameter prior.

The data likelihood is very straightforward since the model is already expressed in probability:

\[ \begin{aligned} \mbox{data log-likelihood} \equiv \ln P(y = 1) &= - \ln (1 + e^{-X\beta}) \\ &= X\beta - \ln (1 + e^{X\beta}). \end{aligned} \]

The case of \(P(y = 0)\) can be derived similarly:8

\[ \begin{aligned} \mbox{data log-likelihood} \equiv \ln P(y = 0) &= \ln \big[1 - P(y=1)\big] \\ &= - X\beta - \ln (1 + e^{-X\beta}) \\ &= - \ln (1 + e^{X\beta}) . \end{aligned} \]

The prior density is purely determined by our assumption about the prior itself. For example, the probabilidy density function of a Normal distribution \(x \sim N(\mu, \sigma)\) prior is:

\[ f(x) = \frac{1}{\sqrt{2\pi\sigma^2}}e^{-\frac{(x - \mu)^2}{2\sigma^2}}. \]

Then the natural log of the pdf will be:

\[ \ln f(x) = \underbrace{-\frac{1}{2}\ln(2\pi\sigma^2)}_\text{a constant} - \frac{(x - \mu)^2}{2\sigma^2}, \]

where the first term is a constant that can be safely ignored in the actual computation.

Now let’s assume our prior on \(\beta\) is just \(\beta \sim N(0, 1)\) for all coefficients independently. A numerically stable likelihood function can be implemented as the following code chunk.

get_log_unnorm_post_fun <- function(X, y) function(beta, mu_beta=0, sigma_beta=2) {
  # Return a function that calculate the numerically stable log-likelihood given Xy.
  # To do so, make sure that v < 0 for all exp(v) in the calculation.
  v <- as.numeric(X %*% beta)
  # Sum of data log-likelihood for all observed data, conditional on beta.
  logp <- ifelse(v < 0, v - log1p(exp(v)), - log1p(exp(-v)))
  logq <- ifelse(v < 0, - log1p(exp(v)), - v - log1p(exp(-v)))
  logl <- sum(logp[y == 1]) + sum(logq[y == 0])
  # Assume beta ~ N(mu_beta, sigma_beta),
  # calculate only the data-dependent part of the log likelihood.
  logl - sum((beta - mu_beta)^2) / (2*sigma_beta^2)
}

lup_fun <- get_log_unnorm_post_fun(fitted_model$x, fitted_model$y)

4.3 Metropolis-Hastings Sampling using mcmc

Once we have the posterior density function, we can plug-and-play the sampling API to get the MCMC samples.

set.seed(777)

# Do Metropolis sampling with initial parameter values set to the MLE estimates.
beta_mle <- as.numeric(coefficients(fitted_model))
mcmc_result <- mcmc::metrop(lup_fun, beta_mle, nbatch=1000)

str(mcmc_result)
List of 14
 $ accept      : num 0.025
 $ batch       : num [1:1000, 1:5] 0.633 0.633 0.633 0.633 0.633 ...
 $ initial     : num [1:5] 0.633 0.739 1.114 0.478 0.694
 $ final       : num [1:5] 0.987 0.486 1.06 1.104 1.268
 $ accept.batch: num [1:1000] 0 0 0 0 0 0 0 0 0 0 ...
 $ initial.seed: int [1:626] 403 624 -139128651 -1634954766 -1410586421 -755369584 -1654052783 -1936562722 1715853511 1253552732 ...
 $ final.seed  : int [1:626] 403 378 493546101 -66585069 -648867965 1681197907 1228103382 -2070035409 2008945713 -403356099 ...
 $ time        : 'proc_time' Named num [1:5] 0.015 0.047 0.057 0 0
  ..- attr(*, "names")= chr [1:5] "user.self" "sys.self" "elapsed" "user.child" ...
 $ lud         :function (beta, mu_beta = 0, sigma_beta = 2)
  ..- attr(*, "srcref")= 'srcref' int [1:8] 1 43 12 1 43 1 1 12
  .. ..- attr(*, "srcfile")=Classes 'srcfilecopy', 'srcfile' <environment: 0xd87cda0>
 $ nbatch      : num 1000
 $ blen        : num 1
 $ nspac       : num 1
 $ scale       : num 1
 $ debug       : logi FALSE
 - attr(*, "class")= chr [1:2] "mcmc" "metropolis"

The returned object contains all the information useful for latter analysis, including acceptance rate, an indicator vector of which sample is accepted, the final estimates, processing time, etc.

Also the returned object can be re-used to run further sampling chain:

mcmc_result2 <- mcmc::metrop(mcmc_result)

# Check that the sampling chain is consecutive.
all(mcmc_result$final == mcmc_result2$initial)
[1] TRUE

We can use the argument scale to adjust the proposal distribution, that is, the step size of the random walk in the sampling process:

mcmc_result <- mcmc::metrop(mcmc_result, scale=.5, nbatch=10000)
mcmc_result$accept
[1] 0.159

In general the step size and the acceptance rate is negatively correlated: Bigger steps give lower acceptance rates.

We can plot the trace for all parameters:

plot(ts(mcmc_result$batch, names=sprintf("\U03B2%s", 1:length(beta_mle) - 1)),
     main="Trace of Metropolis Sampling for Logistic Model Coefficients",
     xlab="Iteration")

Or the auto-correlation plot among all parameters:

acf(mcmc_result$batch)

Since the sampling process is a Markov chain, serial correlation is expected.

4.4 MCMC Estimation and Standard Error

Be aware that the full Metropolis algorithm implemented in mcmc is a mini-batched process. The returned batch vector is indeed a vector of (mini-)batch means. By default mcmc::metrop use a batch length (blen) equal to 1, so there is no mini-batch at all. The total number of MC iterations hence is nbatch times blen.

# This time we return both the batch mean of raw value and the squared value
# in order to calculate variance of the estimate.
mcmc_result_mb <- mcmc::metrop(mcmc_result, scale=.5, nbatch=500, blen=500,
                               outfun=function(z) c(z, z^2))

Now to get our Monte Carlo point estimates about the model parameters:

# The grand mean: The mean of all the batch means.
batch_means <- colMeans(mcmc_result_mb$batch)
point_est <- batch_means[1:5]
point_est
[1] 0.6601806 0.7958475 1.1728578 0.4945127 0.7350583

and also the variance of the estimates

# Based on the fact that Var(X) = E(X^2) - E(X)^2.
var_est <- batch_means[6:10] - batch_means[1:5]^2
var_est
[1] 0.09135117 0.13547493 0.13550139 0.12753755 0.16434547

Monte Carlo itself is a random process that can introduce its own statistical noise. Such noise purely due to Monte Carlo is termed the Monte Carlo standard error (MCSE).

4.4.1 MCSE of Point Estimate

This is analogous to the sampling distribution of \(\bar{X}\) based on samples of \(X\), which by CLT has a standard error of \(\frac{\sigma}{\sqrt{N}}\).

Now we have a finite sample set of \(N\) MCMC batch means for \(\beta\), denoted as \(\beta_i, i \in \{1,2,...,N\}\). Our grand mean (the final point estimator) \(\bar{\beta} = \frac{1}{N}\sum_i^N\beta_i\), again by CLT, will have a standard error of \(\frac{\sigma}{\sqrt{N}}\) as well. To calculate the error the population standard deviation \(\sigma\) is estimated by the sample standard deviation:

mcse_mean <- apply(mcmc_result_mb$batch[,1:5], 2, sd) / sqrt(mcmc_result_mb$nbatch)
mcse_mean
[1] 0.002552742 0.003541831 0.003576170 0.003337310 0.004157431

MCSE is a good metric for us to determine our scale of MCMC. That is, how long we’d like to run the simulation. A general rule of thumb is to reach MCSE < 1%.

4.4.2 MCSE of Variance Estimate

We not only estimate the point of parameter, but also the variance if it. And since the estimation is done by finite MCMC, so again there will be additional errors solely attributable to the adoption of MCMC.

The calculation of MCSE of parameter variance is trickier. We need to use the delta method:

calc_var_mcse <- function(u, v, n) {
  # u: batch mean of first moment
  # v: batch mean of second moment
  # ubar: grand mean of batch mean u
  # vbar: grand mean of batch mean v
  ubar <- colMeans(u)
  vbar <- colMeans(v)
  deltau <- sweep(u, 2, ubar)
  deltav <- sweep(v, 2, vbar)
  foo <- sweep(deltau, 2, ubar, "*")
  sqrt(colMeans((deltav - 2 * foo)^2) / n)
}

mcse_var <- calc_var_mcse(u=mcmc_result_mb$batch[,1:5],
                          v=mcmc_result_mb$batch[,6:10],
                          n=mcmc_result_mb$nbatch)
mcse_var
[1] 0.001019018 0.001588670 0.001722342 0.001513095 0.002000193

To sum up our Bayesian modeling results:

baysian_result <- rbind(point_est, mcse_mean, var_est, mcse_var)
colnames(baysian_result) <- sprintf("\U03B2%s", 1:length(beta_mle) - 1)
baysian_result
                   β0          β1          β2          β3          β4
point_est 0.660180610 0.795847505 1.172857848 0.494512673 0.735058330
mcse_mean 0.002552742 0.003541831 0.003576170 0.003337310 0.004157431
var_est   0.091351171 0.135474933 0.135501393 0.127537550 0.164345468
mcse_var  0.001019018 0.001588670 0.001722342 0.001513095 0.002000193

4.5 Credible Interval

Credible interval is the Bayesian counterpart of the confidence interval defined in frequentist statistics. Unlike the easily-misunderstood confidence interval, credible interval is rather intuitive. A 95% credible interval is simply the range of 2.5% percentile and 97.5% percentile on the posterior distribution of a parameter.

That is, unlike frequentist confidence interval is a probablistic description about the interval itself, Bayesian credible interval is a probablistic description about the parameter of interest. The astonishing difference results from the very different philosophy about model parameter: Frequentists see parameters as unknown constants while Bayeisans see parameters as random variables.

For our estimated logistic model, the 95% credible interval for all parameters can be obtained as the followings.

beta_ci <- apply(mcmc_result_mb$batch[,1:5], 2, quantile, c(.025, .975))
colnames(beta_ci) <- sprintf("\U03B2%s", 1:length(beta_mle) - 1)
beta_ci
             β0        β1       β2        β3        β4
2.5%  0.5428566 0.6346733 1.007609 0.3553723 0.5631411
97.5% 0.7553094 0.9468908 1.328708 0.6527447 0.9159900

Easy? The calculation here has a problem though. Since the returning posterior samples are batch means of mini batches, the interval is not describing the original distribution but instead the sampling distribution of its mean. To construct the credible interval for the posterior we need to do sampling without mini-batching:

# Here we also do a burn-in for the first 10000 rounds.
mcmc_result_no_batch <- mcmc::metrop(lup_fun, beta_mle, scale=.5, nbatch=10000, blen=1)
mcmc_result_no_batch <- mcmc::metrop(mcmc_result_no_batch, scale=.5, nbatch=10000, blen=1)

# Now the credible interval is describing the posterior itself.
beta_ci2 <- apply(mcmc_result_no_batch$batch, 2, quantile, c(.025, .975))
colnames(beta_ci2) <- sprintf("\U03B2%s", 1:length(beta_mle) - 1)
beta_ci2
             β0        β1        β2         β3          β4
2.5%  0.1269002 0.1349256 0.4788433 -0.1484875 -0.09181974
97.5% 1.3100233 1.4560243 1.9507149  1.2735590  1.59211540

The Bayesian credible interval seems to agree with the frequentist confidence interval on that the last 2 coefficients are not statistically significant, since the interval encompass 0.

5 Bayesian Modeling with Stan

Stan is a probabilistic programming language (based on C++) that is widely adopted in practical bayesian modeling. Before its presence, there are two other popular alternatives: BUGS and JAGS. We will focus on using Stan since it is relatively new with a much more advacned MCMC algorithm and showing increasing influence in the community and also is better documented and smoothly integrated with a variety of other eco-systems for modern developers.

rstan(Stan Development Team 2018) is the Stan offical R API to interface directly with Stan model code using R. Notably there are a lot more higher-level packages based on top of rstan to make life even easier, avoiding the need to write native Stan model code all together. In this tutorial however we will purposely ignore them and use only rstan alone since we’d like to understand the very basic concept of how Stan work. It turns out that this is also a very good opportunity to tidy up our thought on what we are exactly modeling in a Baysian framework, without being abstracted away too far due to higher-level implementations.

To work with Stan, we write Stan model code in a separate .stan text file or as a string variable in R. We then compile the code using rstan API which will give us the Stan object to interact with directly within a R session.

library(rstan)
Loading required package: ggplot2
Loading required package: StanHeaders
rstan (Version 2.18.2, GitRev: 2e1f913d3ca3)
For execution on a local, multicore CPU with excess RAM we recommend calling
options(mc.cores = parallel::detectCores()).
To avoid recompilation of unchanged Stan programs, we recommend calling
rstan_options(auto_write = TRUE)

Attaching package: 'rstan'
The following object is masked from 'package:coda':

    traceplot
rstan_options(auto_write=TRUE)
if ( Sys.info()["sysname"] == "Windows" )
  Sys.setenv(LOCAL_CPPFLAGS="-march=native")

5.1 Probabilistic Simulation

A Stan model code contains several mandatory and optional blocks. Code block for data, parameters, and model are mandatory, even if they contain nothing.

Though it is very easy to run probabilistic simulation using R alone, here we illustrate how to do it using Stan with its optional function code block. The following Stan model code implements a function to generate random draws from a student’s t distribution based on a linear model \(y = X\beta + \epsilon\) (i.e., the residuals follow the student’s t distribution):

functions {
  vector st_rng(matrix X, vector beta, real nu, real sigma) {
    vector[rows(X)] y;
    for (n in 1:rows(X))
      y[n] = student_t_rng(nu, X[n] * beta, sigma);
    return y;
  }
}
data {
}
parameters {
}
model {
}

By running rstan::stan_model we can convert the native Stan model code into R stanmodel object.9

Some notes on the above Stan model code:

  • Variables are strongly typed, with declaration syntax <type> name.
  • Operator * for two vector variables is a dot product (while in R it is an element-wise product).
  • Loops are fast since it is C++.

After compilation, functions defined in the function block can be exposed to R environment as R functions:

# We store the compiled stan model in variable `stan_dgp`.
# This is done directly via Rmd API.
# For details: https://bookdown.org/yihui/rmarkdown/language-engines.html#stan
expose_stan_functions(stan_dgp)

# Verify that the Stan function has been exposed as a R function.
exists("st_rng")

Now we can use the function directly in R:

y_sim <- st_rng(nu=10, X=matrix(rnorm(100*3), 100, 3), sigma=5, beta=rnorm(3))
hist(y_sim)

Just for completeness, the same logic can of course be easily implemented in R:

X <- matrix(rnorm(100*3), 100, 3)
beta <- rnorm(3)
nu <- 10
sigma <- 5
y_sim <- as.vector(X%*%beta) + rt(nrow(X), nu)*sigma

5.2 One-Sample Mean Test Revisited

Let’s implement the one-sample test using Stan.

First we re-write the test with JZS prior based on our very first example:

\[ \begin{aligned} \begin{cases} \delta &= \frac{\mu}{\sigma}, \\ P(\sigma^2) &\propto \frac{1}{\sigma^2}, \\ H_0 &: \delta = 0, \\ H_1 &: \delta \sim r \cdot \mbox{Cauchy}. \end{cases} \end{aligned} \]

For notational simplicity, we shift our original random samples by the null value of 0.175, such that our null model has \(\mu = 0\).

Y <- X1 - h0_mean

In order to generate MCMC samples, our prior must be fully specified. Now we will also assume \(Y \sim \mbox{Normal}(\mu, \sigma)\). We already know that this is not true since we setup our sandbox problem such that \(Y \sim \mbox{Beta}(2, 10)\). In reality, we will never get to know the true population anyway. In Bayesian framework, we have the flexibility to dictate our prior on that. With no additional information we usually resort to a Normal distribution as a baseline in many cases. So here the Normal it is.10

The null model above can be defined in Stan as:

data {
  int n;
  vector[n] y;
}
parameters {
  real<lower=0> sigma2;
}
transformed parameters {
  real sigma = sqrt(sigma2);
}
model {
  target += log(1/sigma2);
  target += normal_lpdf(y | 0, sigma);
}
generated quantities {
  vector[n] log_lik;
  for (i in 1:n) {
    log_lik[i] = normal_lpdf(y[i] | 0, sigma);
  }
}

And the alternative model:

data {
  int n;
  vector[n] y;
  real r;
}
parameters {
  real delta;
  real<lower=0> sigma2;
}
transformed parameters {
  real sigma = sqrt(sigma2);
  real mu = delta*sigma;
}
model {
  target += cauchy_lpdf(delta | 0, r);
  target += log(1/sigma2);
  target += normal_lpdf(y | mu, sigma);
}
generated quantities {
  vector[n] log_lik;
  for (i in 1:n) {
    log_lik[i] = normal_lpdf(y[i] | mu, sigma);
  }
}

Some more tricks in writing Stan model code is summarized below:

  • parameters block
    • To define model parameters subject to MCMC; for model hyperparameter use data block to pass through.
    • To bound variable value, add <lower=> or <higher=> suffix in the type declaration.
  • transformed parameters block
    • is useful for derived parameters and in some cases can benefit from performance speedup.
  • model block
    • To define a distribution purely for sampling purpose, use the operator ~.
    • To define a distribution used for calculating likelihood, use target += *_lpdf() notation.
    • For parameters that are not specified, t`hey are assumed to follow a Uniform prior.
  • generated quantities block
    • Can be used to calculate output. It will appear in the resulting stanfit object as additional parameters. Common output will be the data likelihood or the simulated model outcome (Bayesain predicates).

In the model block when we write down something like y ~ normal(0, 1) in Stan model code it is indeed a vectorized instruction to calculate the sum of all log-likelihood from the specified distribution. If we instead write target += normal_lpdf(y | 0, 1) the only difference is that the former drops all constant terms (not used for sampling purpose) and the latter keeps them well.

By default the MCMC method used in Stan is an enhanced version of Hamiltonian Monte Carlo, called the No-U-Turn sampler (Hoffman and Gelman 2014). We can perform the sampling by just one function call using the compiled Stan model:

# We store the compiled stan model in ttest_h0 and ttest_h1, respectively.
# This is done directly via Rmd API.
# For details: https://bookdown.org/yihui/rmarkdown/language-engines.html#stan
stan_h0 <- rstan::sampling(ttest_h0, data=list(y=Y, n=length(Y)),
                           iter=10000, chains=4, cores=1, seed=777,
                           refresh=0)
stan_h1 <- rstan::sampling(ttest_h1, data=list(y=Y, n=length(Y), r=1),
                           iter=10000, chains=4, cores=1, seed=777,
                           refresh=0)

Several things to note about the rstan::sampling API:

  • Variables defined in the data block must be passed through a named list.
  • Multiple chains can be set, with support for parallellization among CPU cores.
  • Use refresh = 0 to suppress the overwhelming progress logs whenver appropriate.

We can do a lof of post processing on the resulting stanfit object.

To extract the data likelihood we defined in generated quantities block:

# If the sampler contains multiple chains, they are stacked together.
log_lik_h0 <- as.matrix(stan_h0, pars="log_lik")
str(log_lik_h0)
 num [1:20000, 1:1000] 1.14 1.13 1.11 1.12 1.12 ...
 - attr(*, "dimnames")=List of 2
  ..$ iterations: NULL
  ..$ parameters: chr [1:1000] "log_lik[1]" "log_lik[2]" "log_lik[3]" "log_lik[4]" ...

To print the summary of Bayesian estimation for parameters:

print(stan_h1, pars=c("delta", "sigma2", "lp__"))
Inference for Stan model: 02ba847215c406acd193f1b4287f8dd0.
4 chains, each with iter=10000; warmup=5000; thin=1;
post-warmup draws per chain=5000, total post-warmup draws=20000.

         mean se_mean   sd   2.5%    25%    50%    75%  97.5% n_eff Rhat
delta   -0.05    0.00 0.03  -0.11  -0.07  -0.05  -0.03   0.01 19253    1
sigma2   0.01    0.00 0.00   0.01   0.01   0.01   0.01   0.01 17042    1
lp__   846.63    0.01 1.01 843.91 846.23 846.94 847.35 847.62  8971    1

Samples were drawn using NUTS(diag_e) at Thu Nov 21 00:11:59 2019.
For each parameter, n_eff is a crude measure of effective sample size,
and Rhat is the potential scale reduction factor on split chains (at
convergence, Rhat=1).

To extract parameter MCMC samples:

h1_params <- rstan::extract(stan_h1, par="log_lik", include=FALSE)
names(h1_params)
[1] "delta"  "sigma2" "sigma"  "mu"     "lp__"  
delta

sigma2

sigma

mu

lp__
mean(h1_params$delta)  # Check if the reported mean is derived from the sample.
[1] -0.05147927

For all applicable methods for a stanfit object one can refer to ?`stanfit.

We can now use bride sampling to estimate the marginal likelihood of our models. Package bridgesampling directly support rstan so it is embarrassingly easy to perform the estimation:

h0_marginal_lik <- bridgesampling::bridge_sampler(stan_h0, silent=TRUE)
Warning: effective sample size cannot be calculated, has been replaced by
number of samples.
h1_marginal_lik <- bridgesampling::bridge_sampler(stan_h1, silent=TRUE)

# Calculate the Bayes factor.
bf(h1_marginal_lik, h0_marginal_lik)
Estimated Bayes factor in favor of h1_marginal_lik over h0_marginal_lik: 0.09377

We can check if the result is aligned with the one calculated from package BayesFactor:

BayesFactor::ttestBF(Y, mu=0, r=1)
Bayes factor analysis
--------------
[1] Alt., r=1 : 0.09376657 ±0%

Against denominator:
  Null, mu = 0
---
Bayes factor type: BFoneSample, JZS

5.3 Bayesian Logistic Regression

Let’s repeat the exercise we previously did with the R package mcmc, but now use rstan.

We rewrite the simple model here:

\[ \begin{aligned} P(y = 1) &= \frac{1}{1+e^{-(X\beta)}}, \\ \beta &\sim \mbox{Normal}(0, 1). \end{aligned} \]

Convert it into Stan model code:

data {
  int n;
  matrix[n, 5] X;
  int y[n];
}
parameters {
  vector[5] beta;
}
model {
  beta ~ normal(0, 1);
  y ~ bernoulli_logit(X*beta);
}

Notes on the above Stan model code:

  • For integer vector of length n, use int y[n] instead of vector[n] y since vector only applies to reals.
  • Use bernoulli_logit directly for logistic function.

Now do MCMC and print the modeling result:

fitted_blogit <- rstan::sampling(
  logit_model, data=list(X=fitted_model$x, y=fitted_model$y, n=length(fitted_model$y)),
  iter=10000, chains=4, cores=1, seed=777, refresh=0)

fitted_blogit
Inference for Stan model: aef518638c6dd98a2b4a46cb44267dc3.
4 chains, each with iter=10000; warmup=5000; thin=1;
post-warmup draws per chain=5000, total post-warmup draws=20000.

          mean se_mean   sd   2.5%    25%    50%    75%  97.5% n_eff Rhat
beta[1]   0.57    0.00 0.28   0.04   0.38   0.57   0.75   1.13 23768    1
beta[2]   0.75    0.00 0.34   0.11   0.52   0.74   0.97   1.44 23544    1
beta[3]   1.05    0.00 0.32   0.45   0.83   1.04   1.27   1.72 22195    1
beta[4]   0.46    0.00 0.33  -0.17   0.23   0.45   0.67   1.12 21599    1
beta[5]   0.66    0.00 0.36  -0.04   0.41   0.65   0.90   1.39 23290    1
lp__    -47.66    0.02 1.61 -51.66 -48.50 -47.33 -46.48 -45.53  9487    1

Samples were drawn using NUTS(diag_e) at Thu Nov 21 00:13:15 2019.
For each parameter, n_eff is a crude measure of effective sample size,
and Rhat is the potential scale reduction factor on split chains (at
convergence, Rhat=1).

One can compare the result with that of mcmc in our previous section. They should show qualitatively similar result even though the underlying sampler is different.

5.4 Bayesian Model Diagnositics

The Stan offical comes with a very handy GUI tool for investigation with Stan model fit: shinystan (Gabry 2018).

For the model we just fitted, we can simply run:

shinystan::launch_shinystan(fitted_blogit)

and be prepared to get your mind blown. :)

5.5 Bayesian Linear Regression

For completeness, let’s also do an exercise on a linear model with Stan:

\[ y = X \beta + \epsilon. \]

The linear model is NOT a probabilistic model until we make assumption about its model residual \(\epsilon\). For example, we can assume the residual to follow

\[ \epsilon \sim \mbox{Student's t}(\nu, 0, \sigma), \]

which gives us

\[ y \sim \mbox{Student's t}(\nu, X\beta, \sigma), \]

where \(\nu\) is the degrees of freedom of the student’s t distribution. Now the linear model has turned into a probabilistic model. Our model parameters in this case will be \(\theta = (\beta, \sigma)\). We need to setup our priors on them. To explore more possibility with Stan let’s have a prior such that the first \(\beta\) coefficient is nonnegatively normally distributed:

\[ \beta_{1} \sim\mbox{Normal}_{+}(\mu_{1}, \sigma_{1}), \]

while the rest of them are just Normal:

\[ \beta_{p} \sim \mbox{Normal}(\mu_{p}, \sigma_{p}), \forall p \in \{2, ..., P\}. \]

It’s quite common to use a truncated Cauchy prior on variance. (Another popular choice is inverse-chi-squared.)

\[ \sigma \sim\mbox{Cauchy}_{+}(\mu_{\sigma}, \gamma_{\sigma}). \]

Here \(\mu_{\sigma}\) and \(\gamma_{\sigma}\) are constants that parameterize the prior.

Now create some fake data for this modeling exercise.

set.seed(777)

# Number of data.
N <- 1000
# Number of features.
P <- 5
# Observed data with a constant term.
X <- cbind(1, matrix(rnorm(N*(P-1)), N, P-1))
colnames(X) <- sprintf("\U03B2%s", 1:P)
# Model parameters.
nu <- 5
sigma <- 5
beta <- rnorm(P)
beta[1] <- abs(beta[1])
names(beta) <- colnames(X)
# Simulated outcome.
outcome <- as.vector(X%*%beta) + rt(nrow(X), nu)*sigma
Xy <- cbind(outcome, X)

head(Xy)
        outcome β1         β2         β3         β4         β5
[1,] 11.4719023  1  0.4897862 -0.3735181  1.2813161  0.3247914
[2,]  0.7531383  1 -0.3985414 -0.3063354  1.1306368 -1.7611395
[3,] 12.0184417  1  0.5108363  0.3116682  1.6559974 -2.5140418
[4,]  3.6428597  1 -0.3988121 -1.1868928 -0.8896346  1.6366530
[5,] -1.5388515  1  1.6386861  0.0242684  0.1000113  0.2672694
[6,]  4.6826551  1  0.6212740  0.1038579  1.1602683 -0.3118227
# True model coefficients.
beta
         β1          β2          β3          β4          β5
 0.70398028 -0.90875342  0.08847479  0.71938375 -1.50103097 

First we report the regression result from the frequentist approach (not constrained by the prior):

lm_fit_freq <- lm(outcome ~ . - 1, data=as.data.frame(Xy))
summary(lm_fit_freq)

Call:
lm(formula = outcome ~ . - 1, data = as.data.frame(Xy))

Residuals:
    Min      1Q  Median      3Q     Max
-37.737  -3.581  -0.023   3.692  26.395

Coefficients:
   Estimate Std. Error t value Pr(>|t|)
β1   1.1031     0.2070   5.329 1.22e-07 ***
β2  -0.3896     0.2074  -1.879   0.0606 .
β3   0.2026     0.2127   0.953   0.3410
β4   0.8543     0.2105   4.058 5.34e-05 ***
β5  -1.6474     0.2007  -8.206 7.02e-16 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 6.528 on 995 degrees of freedom
Multiple R-squared:  0.1014,    Adjusted R-squared:  0.09691
F-statistic: 22.46 on 5 and 995 DF,  p-value: < 2.2e-16

The the Stan code for Bayesian approach:

data {
  int N;
  int P;
  matrix[N, P] X;
  vector[N] y;
}
parameters {
  real<lower = 0> beta_1;
  vector[P-1] beta_2;
  real<lower=0> sigma;
  real<lower=0> nu;
}
transformed parameters {
  vector[P] beta;
  beta = append_row(rep_vector(beta_1, 1), beta_2);
}
model {
  target += normal_lpdf(beta | 0, 5);
  target += cauchy_lpdf(sigma | 0, 2.5);
  target += cauchy_lpdf(nu | 7, 5);
  target += student_t_lpdf(y | nu, X*beta, sigma);
}
generated quantities {
  vector[N] y_sim;
  for (i in 1:N) {
    y_sim[i] = student_t_rng(nu, X[i,]*beta, sigma);
  }
}

Again some additional notes on Stan model coding:

  • To concatenate two vector use append_row. Scalar cannot be directly concatenated to vector so we need to convert it into a vector first by using rep_vector().
  • This time we use generated quantities to also calculate Bayesian model predicates. They are random draws from the predicted distribution of our model.

Note that we hardcode our prior parameter for all \(\beta\)s for simplicity. We can instead pass them into Stan model by using data block.

blm_fitted <- rstan::sampling(blm, data=list(N=N, P=P, X=X, y=outcome),
                              iter=10000, chains=4, cores=1, seed=777,
                              refresh=0)

print(blm_fitted, pars=c("beta", "sigma", "nu"))
Inference for Stan model: 69019223136a9ef3b50508880dd8a704.
4 chains, each with iter=10000; warmup=5000; thin=1;
post-warmup draws per chain=5000, total post-warmup draws=20000.

         mean se_mean   sd  2.5%   25%   50%   75% 97.5% n_eff Rhat
beta[1]  1.14    0.00 0.19  0.77  1.01  1.14  1.26  1.51 27627    1
beta[2] -0.49    0.00 0.19 -0.85 -0.61 -0.49 -0.36 -0.12 35426    1
beta[3]  0.32    0.00 0.20 -0.07  0.18  0.32  0.45  0.70 32579    1
beta[4]  0.66    0.00 0.19  0.30  0.54  0.66  0.79  1.03 29871    1
beta[5] -1.57    0.00 0.18 -1.92 -1.69 -1.57 -1.45 -1.22 32081    1
sigma    5.09    0.00 0.20  4.71  4.96  5.09  5.22  5.49 20310    1
nu       5.15    0.01 0.86  3.79  4.54  5.05  5.64  7.10 19791    1

Samples were drawn using NUTS(diag_e) at Thu Nov 21 00:14:40 2019.
For each parameter, n_eff is a crude measure of effective sample size,
and Rhat is the potential scale reduction factor on split chains (at
convergence, Rhat=1).

6 References

Betancourt, Michael. 2017. “A Conceptual Introduction to Hamiltonian Monte Carlo.” arXiv Preprint arXiv:1701.02434.

Gabry, Jonah. 2018. Shinystan: Interactive Visual and Numerical Diagnostics and Posterior Analysis for Bayesian Models. https://CRAN.R-project.org/package=shinystan.

Geyer, Charles J., and Leif T. Johnson. 2019. Mcmc: Markov Chain Monte Carlo. https://CRAN.R-project.org/package=mcmc.

Gronau, Quentin F, Alexandra Sarafoglou, Dora Matzke, Alexander Ly, Udo Boehm, Maarten Marsman, David S Leslie, Jonathan J Forster, Eric-Jan Wagenmakers, and Helen Steingroever. 2017. “A Tutorial on Bridge Sampling.” Journal of Mathematical Psychology 81. Elsevier: 80–97.

Gronau, Quentin F., and Henrik Singmann. 2018. Bridgesampling: Bridge Sampling for Marginal Likelihoods and Bayes Factors. https://CRAN.R-project.org/package=bridgesampling.

Hoffman, Matthew D, and Andrew Gelman. 2014. “The No-U-Turn Sampler: Adaptively Setting Path Lengths in Hamiltonian Monte Carlo.” Journal of Machine Learning Research 15 (1): 1593–1623.

Morey, Richard D., and Jeffrey N. Rouder. 2018. BayesFactor: Computation of Bayes Factors for Common Designs. https://CRAN.R-project.org/package=BayesFactor.

Resnik, Philip, and Eric Hardisty. 2010. “Gibbs Sampling for the Uninitiated.” Maryland Univ College Park Inst for Advanced Computer Studies.

Rouder, Jeffrey N, Paul L Speckman, Dongchu Sun, Richard D Morey, and Geoffrey Iverson. 2009. “Bayesian T Tests for Accepting and Rejecting the Null Hypothesis.” Psychonomic Bulletin & Review 16 (2). Springer: 225–37.

Stan Development Team. 2018. RStan: The R Interface to Stan. http://mc-stan.org/.

Van Ravenzwaaij, Don, Pete Cassey, and Scott D Brown. 2018. “A Simple Introduction to Markov Chain Monte–Carlo Sampling.” Psychonomic Bulletin & Review 25 (1). Springer: 143–54.


  1. What population distribution we choose here is not important. We choose a distribution just in order to generate some random samples to serve as fake data for our exercise.

  2. Under the context of a two-tailed test the actual p-value will be 2 times the shaded area size, since the distribution is symmetric and we are considering \(Pr(x > |z|) = Pr(x > z) + Pr(x < -z)\).

  3. Note that \(\hat{p}\) is indeed a discrete random variable but we are using a continous distribution for approximation. The approximation is of course better for larger \(N\).

  4. This is a special integral function called the Beta function.

  5. For this particular example, another maybe even smarter way to approximate the solution is to use a discrete Uniform that takes many many possible values. Then we can just calculate the discrete version of the integral, as in the first part of the section, which will also give very good approximations.

  6. The inverse-chi-squared is commonly used as a choice of prior for unknown variance in Bayesian literature.

  7. For machine learning application usually gradient descent (a 1st-order optimizer) is used instead of Newton’s method (a 2nd-order optimizer) due to computational complexity in large scale application. This is, however, less of an issue in the field of Econometrics. And since R is designed for statistics in a pre-Big Data era, the most concerned field of application will be traditional social science where observational data is usually very limited in volume.

  8. We are taking advantage of the fact that \(\frac{1}{1+e^{-v}} = \frac{e^v}{1 + e^v}\) to express the log likelihood differently in favor of our numerical computation stability.

  9. In this tutorial we use Rmd to directly compile the Stan code. For scripting usage, one should use either stan_model(file="model.stan") for stan_model(model_code=model_str) to compile and return the model object accordingly.

  10. As a comparison, in the frequentist approach in this case we don’t need to further assume the population distribution thanks to the CLT. We only need to loosely assume that the variance is finite for the population, such that CLT applies.

LS0tCnRpdGxlOiAiQmF5ZXNpYW4gTW9kZWxpbmcgRXhwbGFpbmVkIgpzdWJ0aXRsZTogIndpdGggRXhhbXBsZXMgVXNpbmcgUiBhbmQgU3RhbiIKYXV0aG9yOgotIG5hbWU6IEt5bGUgQ2h1bmcKICBhZmZpbGlhdGlvbjogCmRhdGU6ICJgciBmb3JtYXQoU3lzLnRpbWUoKSwgJyVkICVCICVZJylgIExhc3QgVXBkYXRlZCAoMTQgSnVuIDIwMTkgRmlyc3QgVXBsb2FkZWQpIgpvdXRwdXQ6IAogIGh0bWxfbm90ZWJvb2s6CiAgICBudW1iZXJfc2VjdGlvbnM6IHllcwogICAgdGhlbWU6IHBhcGVyCiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICB0b2NfZGVwdGg6IDMKICAgIGluY2x1ZGVzOgogICAgICBpbl9oZWFkZXI6IC90bXAvbWV0YV9oZWFkZXIuaHRtbAogIGNvZGVfZG93bmxvYWQ6IHRydWUKYmlibGlvZ3JhcGh5OiBiYXllc2lhbl9tb2RlbGluZ19leHBsYWluZWQuYmliCm5vY2l0ZTogfCAKICBAdmFuMjAxOHNpbXBsZQphYnN0cmFjdDogfAogIFRoaXMgaXMgYSBwcmFjdGljYWwgdHV0b3JpYWwgb24gQmF5ZXNpYW4gaW5mZXJlbmNlIGZvciByZWFkZXJzIHdobyBhcmUgYWxyZWFkeSBrbm93bGVkZ2VkIHdpdGggYmFzaWMgc3RhdGlzdGljcy4gVGhlIHR1dG9yaWFsIGlzIHByYWN0aWNhbCBpbiBhIHNlbnNlIHRoYXQgaXQgZm9jdXNlcyBtb3JlIG9uIGltcGxlbWVudGF0aW9uIGFuZCByZWFzb25pbmcgdGhhbiBvbiBtYXRoZW1hdGljYWwgYXJ0aWZhY3RzLiBXZSB3aWxsIHVzZSBSIGFuZCBhbHNvIFN0YW4sIGEgcHJvYmFiaWxpc3RpYyBwcm9ncmFtbWluZyBsYW5ndWFnZSwgdG8gaW50cm9kdWNlIGhvdyBCYXllc2lhbiBtb2RlbGluZyB3b3Jrcy4KICAKICBUaGUgdHV0b3JpYWwgaW5jbHVkZXMgdGhlIGZvbGxvd2luZyBzZWN0aW9uczogMS4pIGEgYmFzaWMgcmVjYXAgb2YgZnJlcXVlbnRpc3QgYXBwcm9hY2ggYW5kIGNyaXRpcXVlczsgMi4pIHRveSBleGFtcGxlcyB1c2luZyBNYXJrb3YgQ2hhaW4gTW9udGUgQ2FybG8gbWV0aG9kOyAzLikgQmF5ZXNpYW4gbW9kZWxpbmcgdXNpbmcgdGhlIGBtY21jYCBSIHBhY2thZ2UgOyA0LikgQmF5ZXNpYW4gbW9kZWxpbmcgdXNpbmcgU3RhbiBhbG9uZyB3aXRoIFIuCi0tLQoKPHNjcmlwdCB0eXBlPSJ0ZXh0L3gtbWF0aGpheC1jb25maWciPgpNYXRoSmF4Lkh1Yi5Db25maWcoewogIFRlWDogeyBlcXVhdGlvbk51bWJlcnM6IHsgYXV0b051bWJlcjogIkFNUyIgfSB9Cn0pOwo8L3NjcmlwdD4KCmBgYHtyIG1ldGEsIGluY2x1ZGU9RkFMU0V9Cm1ldGFfaGVhZGVyX2ZpbGUgPC0gZmlsZSgiL3RtcC9tZXRhX2hlYWRlci5odG1sIikKbWV0YSA8LSBjKAogICc8bWV0YSBuYW1lPSJhdXRob3IiIGNvbnRlbnQ9Ikt5bGUgQ2h1bmciPicsCiAgJzxtZXRhIHByb3BlcnR5PSJvZzp0aXRsZSIgY29udGVudD0iQmF5ZXNpYW4gTW9kZWxpbmcgRXhwbGFpbmVkIj4nLAogICc8bWV0YSBwcm9wZXJ0eT0ib2c6dHlwZSIgY29udGVudD0iYXJ0aWNsZSI+JywKICAnPG1ldGEgcHJvcGVydHk9Im9nOnVybCIgY29udGVudD0iaHR0cHM6Ly9ldmVyZGFyay5naXRodWIuaW8vazkvYmF5ZXNpYW4vYmF5ZXNpYW5fbW9kZWxpbmdfZXhwbGFpbmVkLm5iLmh0bWwiPicsCiAgJzxtZXRhIHByb3BlcnR5PSJvZzppbWFnZSIgY29udGVudD0iaHR0cHM6Ly9ldmVyZGFyay5naXRodWIuaW8vazkvYXNzZXRzL2F2YXRhci5qcGciPicsCiAgJzxtZXRhIHByb3BlcnR5PSJvZzpkZXNjcmlwdGlvbiIgY29udGVudD0iQSBkYXRhIHNjaWVuY2Ugbm90ZWJvb2sgYWJvdXQgQmF5ZXNpYW4gbW9kZWxpbmcgd2l0aCBleGFtcGxlcyB1c2luZyBSIGFuZCBTdGFuLiI+JwopCmNvbnRlbnRzIDwtIG1ldGEKCiMgQWRkIEdpdGh1YiBjb3JuZXIuCmdpdGh1Yl9jb3JuZXJfc3ZnIDwtICIuLi9hc3NldHMvZ2l0aHViX2Nvcm5lci5odG1sIgpnaXRodWJfY29ybmVyX2NvbmYgPC0gbGlzdChnaXRodWJfbGluaz0iaHR0cHM6Ly9naXRodWIuY29tL2V2ZXJkYXJrL2s5L3RyZWUvbWFzdGVyL2JheWVzaWFuIikKY29udGVudHMgPC0gYyhjb250ZW50cywgc3RyaW5ncjo6c3RyX2ludGVycChyZWFkTGluZXMoZ2l0aHViX2Nvcm5lcl9zdmcpLCBnaXRodWJfY29ybmVyX2NvbmYpKQp3cml0ZUxpbmVzKGNvbnRlbnRzLCBtZXRhX2hlYWRlcl9maWxlKQoKY2xvc2UobWV0YV9oZWFkZXJfZmlsZSkKYGBgCiAgCiMgQmFzaWNzCgpCYXllc2lhbnMgdHJlYXQgYSBtb2RlbCBwYXJhbWV0ZXIgJFx0aGV0YSQgYXMgWypyYW5kb20gdmFyaWFibGUqXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9SYW5kb21fdmFyaWFibGUpIHdoaWxlIGZyZXF1ZW50aXN0cyB0cmVhdCB0aGVtIGFzIHVua25vd24gKmNvbnN0YW50Ki4KU28gaW5zdGVhZCBvZiBzb2x2aW5nIGZvciBhbiB1bmtub3duIGNvbnN0YW50LCBCYXllc2lhbiBpbmZlcmVuY2UgaXMgc29sdmluZyBmb3IgYW4gdW5rbm93biBkaXN0cmlidXRpb24uClRvIGVzdGltYXRlIHRoZSBtb2RlbCBwYXJhbWV0ZXJzIHRoZSAqQmF5ZXMnIExhdyogaXMgdXNlZDoKCiQkClxiZWdpbntlcXVhdGlvbn0gXGxhYmVse2VxOmJheWVzbGF3fQpQKFx0aGV0YSB8IHksIFgpID0gXGZyYWN7UCh5fFx0aGV0YSxYKSBcY2RvdCBQKFx0aGV0YSl9e1AoeSl9IFxwcm9wdG8gUCh5fFx0aGV0YSxYKSBcY2RvdCBQKFx0aGV0YSkuClxlbmR7ZXF1YXRpb259CiQkCgpJbiBwbGFpbiB3b3JkczoKCisgJFgkOiBUaGUgZGF0YSB3ZSBvYnNlcnZlZC4gSW4gbWFjaGluZSBsZWFybmluZyBpdCBpcyB0aGUgdHJhaW5pbmcgZmVhdHVyZXMuCisgJHkkOiBUaGUgb3V0Y29tZSB3ZSBvYnNlcnZlZC4gSW4gbWFjaGluZSBsZWFybmluZyBpdCBpcyB0aGUgdHJhaW5pbmcgbGFiZWxzLgorICRQKFx0aGV0YXx5LFgpJDogV2hhdCBkbyB3ZSB0aGluayBhYm91dCB0aGUgcG9zc2libGUgdmFsdWUgb2Ygb3VyIG1vZGVsIHBhcmFtZXRlcnMgQUZURVIgc2VlaW5nIHRoZSBkYXRhPworICRQKHl8XHRoZXRhLFgpJDogV2hhdCBpcyB0aGUgbGlrZWxpaG9vZCBvZiBzZWVpbmcgdGhlIGN1cnJlbnQgb3V0Y29tZSBjb25kaXRpb25lZCBvbiB0aGUgZGF0YSB3ZSBoYXZlIGFuZCBhIHNwZWNpZmljIHBhcmFtZXRlciB2YWx1ZT8KKyAkUChcdGhldGEpJDogV2hhdCBkbyB3ZSB0aGluayBhYm91dCB0aGUgcG9zc2libGUgdmFsdWVzIG9mIG91ciBtb2RlbCBwYXJhbWV0ZXJzIEJFRk9SRSBvYnNlcnZpbmcgYW55IGFjdHVhbCBkYXRhPyBUaGlzIGlzIG91ciAqYmVsaWVmKiBvciAqcHJpb3IqIGFib3V0IHRoZSBtb2RlbCBwYXJhbWV0ZXJzLgorICRQKHkpJDogV2hhdCBpcyB0aGUgdW5jb25kaXRpb25hbCBwcm9iYWJpbGl0eSBvZiB0aGUgb3V0Y29tZT8gVGhhdCBpcywgdGhlIG92ZXJhbGwgcHJvYmFiaWxpdHkgYWZ0ZXIgdGFraW5nIGludG8gY29uc2lkZXJhdGlvbiBhbGwgcGFyYW1ldGVyIHBvc3NpYmlsaXRpZXMuCgoqKlBvc3RlcmlvciBEZW5zaXR5OiAkUChcdGhldGF8eSxYKSQqKgoKSW4gQmF5c2lhbiBtb2RlbGluZywKdGhlIG9iamVjdGl2ZSBpcyB0byBzb2x2ZSBmb3IgJFAoXHRoZXRhfHksWCkkLAp0aGUgcG9zdGVyaW9yIGRlbnNpdHkgb2YgcGFyYW1ldGVyLgpUaGUgcG9pbnQgZXN0aW1hdGUgaXMgdGhlIGV4cGVjdGVkIHZhbHVlIG9mIHBvc3RlcmlvciBkZW5zaXR5LgpUaGUgdmFyaWFuY2UgZXN0aW1hdGUgaXMgYWxzbyByZWFkaWx5IGF2YWlsYWJsZSBmcm9tIHRoZSBwb3N0ZXJpb3IgZGlzdHJpYnV0aW9uLgoKQmFzZWQgb24gZXF1YXRpb24gJFxlcXJlZntlcTpiYXllc2xhd30kLAp0byBzb2x2ZSBmb3IgJFAoXHRoZXRhfHksWCkkIGFuYWx5dGljYWxseSwKd2UgbmVlZCB0byBjYWxjdWxhdGUgdGhlIGRhdGEgbGlrZWxpaG9vZCAkUCh5fFx0aGV0YSxYKSQsCnRoZSBwcmlvciAkUChcdGhldGEpJCwKYW5kIHRoZSBtYXJnaW5pYWwgbGlrZWxpaG9vZCBvZiBvdXRjb21lICRQKHkpJC4KVGhlIGRpZmZpY3VsdHkgaXMgdGhhdCB0aGVyZSBpcyByYXJlbHkgYSB0cmFjdGFibGUgY2xvc2VkLWZvcm0gc29sdXRpb24gZHVlIHRvIHRoZSBwcmVzZW5jZSBvZiB0aGUgZGVub21pbmF0b3IgdGVybSAkUCh5KSQuClRvIG92ZXJjb21lIHRoaXMgcHJvYmxlbSwKKk1hcmtvdiBjaGFpbiBNb250ZSBDYXJsbyogbWV0aG9kIGlzIHVzZWQgdG8gbnVtZXJpY2FsbHkgc29sdmUgdGhlIHBvc3RlcmlvciBkZW5zaXR5IGJ5IGRpcmVjdGx5IGdlbmVyYXRpbmcgcmFuZG9tIGRyYXdzIG9mIHBhcmFtZXRlcnMgd2l0aG91dCBjYWxjdWxhdGluZyB0aGUgZGVub21pbmF0b3IgdGVybS4KCioqRGF0YSBMaWtlbGlob29kOiAkUCh5fFx0aGV0YSxYKSQqKgoKQXMgbG9uZyBhcyB0aGUgbW9kZWwgaXMgZnVsbHkgc3BlY2lmaWVkLAphbmQgdGhlIGRhdGEgaXMgW2lkZW50aWNhbGx5IGFuZCBpbmRlcGVuZGVudGx5IGRpc3RyaWJ1dGVkXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9JbmRlcGVuZGVudF9hbmRfaWRlbnRpY2FsbHlfZGlzdHJpYnV0ZWRfcmFuZG9tX3ZhcmlhYmxlcyksCnRoZSBkYXRhIGxpa2VsaWhvb2QgaXMgc2ltcGx5IHRoZSBwcm9kdWN0IG9mIGFsbCBpbmRpdmlkYWwgb2JzZXJ2YXRpb24gbGlrZWxpaG9vZDoKCiQkClxiZWdpbntlcXVhdGlvbn0KUCh5fFx0aGV0YSkgPSBccHJvZF9pXm4gUCh5X2l8XHRoZXRhLCBYX2kpLgpcZW5ke2VxdWF0aW9ufQokJAoKVGhlIGluZGl2aWR1YWwgbGlrZWxpaG9vZCAkUCh5X2l8XHRoZXRhLCBYX2kpJCBjYW4gYmUgb3V0cHV0IG9mIGEgbG9naXN0aWMgcmVncmVzc2lvbiBvciBhIGRlZXAgbmV1cmFsIG5ldHMgb3IgdmlydHVhbGx5IGFueSBraW5kIG9mIHByb2JhYmxpc3RpYyBtb2RlbC4KCioqUHJpb3I6ICRQKFx0aGV0YSkkKioKCkhvdyBkbyB3ZSBzb2x2ZSBmb3IgYSBwcmlvciBmb3IgYSBnaXZlbiBwYXJhbWV0ZXI/CldlbGwgaW5kZWVkIHdlIGNhbid0IHNvbHZlIGZvciBpdC4KV2Ugc2ltcGx5IG1ha2UgYXNzdW1wdGlvbiBhYm91dCBpdC4KClRoZSBpbnRyb2R1Y3Rpb24gb2YgbW9kZWwgcHJpb3IgbWFrZSBCYXllc2lhbiBtb2RlbGluZyBtb3JlIGZsZXhpYmxlIHdoZW4gdGhlcmUgaXMgZXh0cmEgaW5mb3JtYXRpb24gdGhhdCBjYW4gYmUgdXRpbGl6ZWQuCldoZW4gdGhlcmUgaXMgbm8gcGFydGljdWxhciB1c2VmdWwgaW5mb3JtYXRpb24gYSBwcmlvcmksCm9uZSBjYW4gc3RpbGwgY2hvb3NlIHRvIHVzZSBhIHNvLWNhbGxlZCBub24taW5mb3JtYXRpdmUgcHJpb3IgdG8gZXhwcmVzcyBvYmplY3Rpdml0eS4KCioqTWFyZ2luYWwgTGlrZWxpaG9vZDogJFAoeSkkKioKClRoZSBkZW5vbWluYXRvciBvZiB0aGUgQmF5ZXMnIExhdyBpcyBjYWxsZWQgdGhlIFttYXJnaW5hbCBwcm9iYWJpbGl0eV0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvTWFyZ2luYWxfZGlzdHJpYnV0aW9uKSBvZiBkYXRhLgpCeSBbdGhlIGxhdyBvZiB0b3RhbCBwcm9iYWJpaXR5XShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9MYXdfb2ZfdG90YWxfcHJvYmFiaWxpdHkpIHdlIGhhdmU6CgokJApcYmVnaW57ZXF1YXRpb259IFxsYWJlbHtlcTptYXJnaW5hbF9saWt9ClAoeSkgPSBcaW50IFAoeXxcdGhldGEpUChcdGhldGEpZFx0aGV0YS4KXGVuZHtlcXVhdGlvbn0KJCQKClRoZSBpbnRlZ3JhbCBpcyB1c3VhbGx5IGFuYWx5dGljYWxseSBpbnRyYWN0YWJsZSwKZXZlbiBmb3IgYSBtb2RlcmF0ZWx5IHBhcmFtZXRlcml6ZWQgbW9kZWwuCkJ5IHVzaW5nIE1DTUMgbWV0aG9kIHdlIGNhbiBhdm9pZCBjYWxjdWxhdGlvbiBvZiB0aGlzIHRlcm0gYWxsIHRvZ2V0aGVyIHdoZW4gc29sdmluZyBmb3IgdGhlIEJheWVzaWFuIGVzdGltYXRvci4KQnV0IHRoZXJlIGFyZSBvdGhlciBzY2VuYXJpb3Mgd2UgbWF5IHN0aWxsIG5lZWQgdG8gY2FsY3VsYXRlIHRoaXMgdGVybS4KTm90YWJsZSBleGFtcGxlcyBhcmUgbW9kZWwgY29tcGFyaXNvbiBhbmQgbW9kZWwgYXZlcmFnaW5nLgpJbiBzdWNoIGNhc2Ugb3RoZXIgTW9udGUgQ2FybG8gbnVtZXJpY2FsIG1ldGhvZHMgKHN1Y2ggYXMgQnJpZGdlIFNhbXBsaW5nKSBhcmUgYWxzbyBkZXZlbG9wZWQgdG8gcHJvdmlkZSBhcHByb3hpbWF0aW9uIHRvIHRoZSBzb2x1dGlvbi4KCkluIGEgbnV0c2hlbGwsCmluIGZyZXF1ZW50aXN0IHN0YXRpc3RpY3MsCndlIHNvbHZlIGZvciBhICRcdGhldGEkIHRoYXQgbWF4aW1pemVzIHRoZSBsaWtlbGlob29kIG9mIGRhdGEuClRoYXQgaXMgdGhlIHdlbGwta25vd24gbWF4aW11bSBsaWtlbGlob29kIGVzdGltYXRvci4KSW4gQmF5c2lhbiBzdGF0aXN0aWNzLApob3dldmVyLAp3ZSBzb2x2ZSBmb3IgYSBwb3N0ZXJpb3IgZGlzdHJpYnV0aW9uIG9mICRcdGhldGEkLAp3aGVyZSB0aGUgZXhwZWN0ZWQgdmFsdWUgY2FsY3VsYXRlZCBhcyBhIHdlaWdodGVkIGNvbWJpbmF0aW9uIG9mIGxpa2VsaWhvb2QgYW5kIHBhcmFtZXRlciBwcmlvciBzZXJ2ZXMgYXMgdGhlIG1vZGVsIHBvaW50IGVzdGltYXRlLgoKIyMgRnJlcXVlbnRpc3QgSHlwb3RoZXNpcyBUZXN0aW5nCgpUbyBnZXQgc3RhcnRlZCwgCndlIGZpcnN0IGNvbnNpZGVyIGEgdmVyeSBzaW1wbGUgdGFzayBvZiBzdGF0aXN0aWNhbCBpbmZlcmVuY2U6CndlJ2QgbGlrZSB0byB0ZXN0IG91ciBiZWxpZWYgb24gdGhlIG1lYW4gb2YgYSBwb3B1bGF0aW9uIGJhc2VkIG9uIHNhbXBsZXMgd2Ugb2JzZXJ2ZWQuClRoZSB0ZWNobmlxdWUgaXMgY2FsbGVkIG9uZS1zYW1wbGUgaHlwb3RoZXNpcyB0ZXN0aW5nLgpXZSB3aWxsIHJlY2FwIGZvciBhIGZyZXF1ZW50aXN0IHBvaW50IG9mIHZpZXcgYmVmb3JlIHdlIGRpdmUgaW50byBhIEJheWVzaWFuIHdvcmxkLgoKIyMjIE9uZS1TYW1wbGUgTWVhbiBUZXN0CgpIZXJlIHdlIGFzc3VtZSB0aGUgKHVua25vd24pIHBvcHVsYXRpb24gaXMgYSBbQmV0YSBkaXN0cmlidXRpb25dKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0JldGFfZGlzdHJpYnV0aW9uKTpeW1doYXQgcG9wdWxhdGlvbiBkaXN0cmlidXRpb24gd2UgY2hvb3NlIGhlcmUgaXMgbm90IGltcG9ydGFudC4gV2UgY2hvb3NlIGEgZGlzdHJpYnV0aW9uIGp1c3QgaW4gb3JkZXIgdG8gZ2VuZXJhdGUgc29tZSByYW5kb20gc2FtcGxlcyB0byBzZXJ2ZSBhcyBmYWtlIGRhdGEgZm9yIG91ciBleGVyY2lzZS5dCgpgYGB7ciBzaW1wbF9kYXRhX2JldGFfcG9wfQpiZXRhX2EgPC0gMgpiZXRhX2IgPC0gMTAKc2ltcGxlX2JldGEgPC0gZnVuY3Rpb24oeCkgZGJldGEoeCwgYmV0YV9hLCBiZXRhX2IpCmN1cnZlKHNpbXBsZV9iZXRhLCBmcm9tPTAsIHRvPTEsIHlsYWI9IkRlbnNpdHkiLAptYWluPXNwcmludGYoIihVbmtub3duKSBQb3B1bGF0aW9uIEJldGEoJXMsICVzKSIsIGJldGFfYSwgYmV0YV9iKSkKYGBgCgpBbmQgd2UgZ2VuZXJhdGUgYSByYW5kb20gc2FtcGxlIGRhdHNldCBvZiBzaXplIDEwMDAgZnJvbSB0aGlzIHBvcHVsYXRpb24gYXMgb3VyIG9ic2VydmVkIGRhdGFzZXQuCgpgYGB7ciBzaW1wbGVfZGF0YX0Kc2V0LnNlZWQoNzc3KQpYMSA8LSByYmV0YSgxMDAwLCBiZXRhX2EsIGJldGFfYikKaGlzdChYMSwgbWFpbj1zcHJpbnRmKCIoS25vd24pIERpc3RyaWJ1dGlvbiBvZiBTYW1wbGUgZnJvbSBYIH4gQmV0YSglcywgJXMpIiwgYmV0YV9hLCBiZXRhX2IpKQpgYGAKClRoZSBwb3B1bGF0aW9uIG1lYW4gb2YgYSAkXG1ib3h7QmV0YX0oXGFscGhhLCBcYmV0YSkkIGlzICRcZnJhY3tcYWxwaGF9e1xhbHBoYSArIFxiZXRhfSQuClNvIGluIHRoaXMgY2FzZSBpdCBpczoKCmBgYHtyIHNpbXBsZV9kYXRhX2JldGFfbWVhbn0KKHRydWVfbWVhbiA8LSBiZXRhX2EgLyAoYmV0YV9hICsgYmV0YV9iKSkKYGBgCgpJbiB0aGUgdHJhZGl0aW9uYWwgZnJlcXVlbnRpc3QgcG9pbnQgb2YgdmlldywKdGhhbmtzIHRvIFsqdGhlIExhdyBvZiBMYXJnZSBOdW1iZXIqXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9MYXdfb2ZfbGFyZ2VfbnVtYmVycykgYW5kIFsqdGhlIENlbnRyYWwgTGltaXQgVGhlb3JlbSpdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0NlbnRyYWxfbGltaXRfdGhlb3JlbSksCndlIGtub3cgdGhhdCAqc2FtcGxlIG1lYW4qIGlzIGEgdmVyeSBnb29kIGVzdGltYXRvciBmb3IgdGhlIHVua25vd24gcG9wdWxhdGlvbiBtZWFuIGRlc3BpdGUgdGhlIHNoYXBlIG9mIHRoZSB0cnVlIGRpc3RyaWJ1dGlvbjoKCmBgYHtyIHNpbXBsZV9kYXRhX3NhbXBsZV9tZWFufQptZWFuKFgxKQpgYGAKCkxldCdzIGZ1cnRoZXIgYXNzdW1lIHdlIGhhdmUgc29tZSBncm91bmRzIHRoYXQgc3VnZ2VzdCAod2hpY2ggaXMgYWN0dWFsbHkgbm90IHRydWUpIHRoZSBwb3B1bGF0aW9uIG1lYW4gc2hvdWxkIGJlOgoKYGBge3Igc2ltcGxlX2RhdGFfbnVsbF9oeXBvfQpoMF9tZWFuIDwtICAwLjE3NQpgYGAKCkZvciBleGFtcGxlIGlmIHdlIGFyZSBhdCBhIG1hbnVmYXR1cmluZyBwcm9kdWN0aW9uIGxpbmUsCndlIHNldHVwIHRoZSBtYWNoaW5lIHN1Y2ggdGhhdCB0aGUgbWFudWFsIHRvbGRzIHVzIHRoZSBvdXRjb21lIG9mIG91ciBwcm9kdWN0IHNwZWMgd2lsbCBoYXZlIGEgbWVhbiBvZiBgciBoMF9tZWFuYCBidXQgd2l0aCBhIHJpZ2h0LXRhaWxpbmcgYmlhcyAoaGVuY2UgZGlzdHJpYnV0ZWQgYXMgYSBCZXRhKSBieSBkZXNpZ24gaWYgbm90aGluZyB3ZW50IHdyb25nLgoKVGhlIGh5cG90ZXNpcyB0ZXN0aW5nIGhlbmNlIGNhbiBiZSB3cml0dGVuIGFzOgoKYHIgc3ByaW50ZigiJCRIXzA6IFxcbXUgPSAlcywgXFxcXCBIXzE6IFxcbXUgXFxuZXEgJXMuJCQiLCBoMF9tZWFuLCBoMF9tZWFuKWAKCkhvdyBkb2VzIGEgZnJlcXVlbnRpc3Qgc3RhdGlzdGljaWFuIHZlcmlmeSB0aGlzIGh5cG90aGVzaXM/CkhvdyB0byBtYWtlIGEgc2NpZW50aWZpYyBqdWRnZW1lbnQgYWJvdXQgd2hldGhlciBvdXIgcHJvZHVjdGlvbiBsaW5lIGlzIGZ1bmN0aW9uaW5nIGFzIGV4cGVjdGVkPwpUbyBleGFtaW5lIHRoaXMgc29ydCBvZiBxdWVzdGlvbiwKYSBmcmVxdWVudGlzdCByZWxpZXMgb24gdGhlIGZvbGxvd2luZyBxdWFudGl0YXRpdmUgcmVhc29uaW5nOgoKPiBQcm92aWRlZCB0aGF0IHRoZSBudWxsIGh5cG90aGVzaXMgaXMgYWN0dWFsbHkgdHJ1ZSwgaG93IGxpa2VseSB3aWxsIEkgb2JzZXJ2ZSBhIHNldCBvZiByYW5kb20gc2FtcGxpbmcgZGF0YXNldCBtb3JlIGV4dHJlbWUgdGhhbiB0aGUgY3VycmVudCBvbmU/IElmIHRoZSBsaWtlbGlob29kIGlzIHZlcnkgbG93LCB0aGVuIHByb2JhYmx5IEkgc2hvdWxkbid0IHRydXN0IHRoZSBudWxsIGh5cG90aGVzaXMuCgpJbiBvcmRlciB0byBtZWFzdXJlICpob3cgZXh0cmVtZSogb3VyIHNhbXBsZSBkYXRhIGlzLAp3ZSBuZWVkIHRvIGZpcnN0IGNvbnN0cnVjdCB0aGUgWypzYW1wbGluZyBkaXN0cmlidXRpb24qXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9TYW1wbGluZ19kaXN0cmlidXRpb24pIG9mIHNhbXBsZSBtZWFuLApjb25kaXRpb25lZCBvbiBvdXIgbnVsbCBoeXBvdGhlc2lzLgpUaGUgQ2VudHJhbCBMaW1pdCBUaGVvcmVtIChDTFQgaGVyZWFmdGVyKSBzdWdnZXN0cyB0aGF0LAp1bmRlciBmYWlyIGNvbmRpdGlvbnMgKGVzcGVjaWFsbHkgd2hlbiB0aGUgc2Vjb25kIG1vbWVudCBvZiB0aGUgcG9wdWxhdGlvbiBpcyBmaW5pdGUpLAp0aGUgc2FtcGxpbmcgZGlzdHJpYnV0aW9uIG9mIHNhbXBsZSBtZWFuICRcYmFye1h9JCB3aXRoIHNhbXBsZSBzaXplICROJCBpcyBhc3ltcHRvdGljYWxseSBbTm9ybWFsXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9Ob3JtYWxfZGlzdHJpYnV0aW9uKToKCiQkClxiZWdpbntlcXVhdGlvbn0gXGxhYmVse2VxOmNsdH0KXGJhcntYfSBcb3ZlcnNldHtkfVxzaW0gXG1ib3h7Tm9ybWFsfShcbXUsIFxmcmFje1xzaWdtYX17XHNxcnR7Tn19KSwKXGVuZHtlcXVhdGlvbn0KJCQKCndoZXJlICRcbXUkIGFuZCAkXHNpZ21hJCBpcyB0aGUgcG9wdWxhdGlvbiBtZWFuIGFuZCBzdGFuZGFyZCBkZXZpYXRpb24sCnJlc3BlY3RpdmVseS4KClNvIHdlIGhhdmUgYSB2ZXJ5IHN0cm9uZyBzY2llbnRpZmljIGdyb3VuZCAoQ0xUKSB0aGF0IHN0YXRlcyB0aGUgW2xpbWl0aW5nIGRpc3RyaWJ1dGlvbl0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvQXN5bXB0b3RpY19kaXN0cmlidXRpb24pIG9mIHRoZSBzYW1wbGluZyBkaXN0cmlidXRpb24gb2Ygc2FtcGxlIG1lYW4gaXMgTm9ybWFsLgpMZXQncyBwbG90IGl0IGdpdmVuIHRoZSBudWxsIGh5cG90aGVzaXMgaXMgdHJ1ZToKCmBgYHtyIHNpbXBsZV9kYXRhX3Bsb3Rfc2FtcGxpbmdfZGlzdH0KcGxvdF9zYW1wbGluZ19kaXN0IDwtIGZ1bmN0aW9uKFgsIHRydWVfbWVhbiwgdHJ1ZV9zZD1OVUxMKSB7CiAgc2FtcGxlX21lYW4gPC0gbWVhbihYKQogIHNlIDwtIAogICAgaWYgKCBpcy5udWxsKHRydWVfc2QpICkgewogICAgICAjIElmIHRydWUgcG9wdWxhdGlvbiBzZCBpcyB1bmtub3duIChwcmFjdGljYWxseSBhbHdheXMgdGhlIGNhc2UpLAogICAgICAjIHdlIGNhbiBwbHVnIGluIHRoZSBzYW1wbGUgc2QgaW5zdGVhZCBhcyBhbiBhcHByb3hpbWF0aW9uLgogICAgICBzYW1wbGVfc2QgPC0gc2QoWCkKICAgICAgc2FtcGxlX3NkIC8gc3FydChsZW5ndGgoWCkpCiAgICB9IGVsc2UgewogICAgICB0cnVlX3NkIC8gc3FydChsZW5ndGgoWCkpCiAgICB9CiAgeCA8LSBzZXEodHJ1ZV9tZWFuIC0gMypzZSwgdHJ1ZV9tZWFuICsgMypzZSwgbGVuZ3RoPTEwMDApCiAgZGVuc2l0eSA8LSBkbm9ybSh4LCBtZWFuPXRydWVfbWVhbiwgc2Q9c2UpCiAgcGxvdCh4LCBkZW5zaXR5LCB0eXBlPSJsIiwgeWF4cz0iaSIsCiAgICAgICBtYWluPWJxdW90ZShiYXIoWCkgfiAiU2FtcGxpbmcgRGlzdHJpYnV0aW9uIHwgTnVsbCBIeXBvdGhlc2lzIiB+IEhbMF0pLAogICAgICAgeGxhYj1icXVvdGUoYmFyKFgpKSkKICBhYmxpbmUodj10cnVlX21lYW4sIGx0eT0yKQogIHRleHQodHJ1ZV9tZWFuLCBtZWFuKGRlbnNpdHkpLCBwb3M9NCwgYnF1b3RlKCJIWzBdIG1lYW4gPSAiIH4gLih0cnVlX21lYW4pKSkKICBhYmxpbmUodj1zYW1wbGVfbWVhbiwgY29sPSJyZWQiKQogIHRleHQoc2FtcGxlX21lYW4sIG1lYW4oZGVuc2l0eSkgLyAyLCBwb3M9NCwgY29sPSJyZWQiLAogICAgICAgc3ByaW50ZigiT2JzZXJ2ZWQgc2FtcGxlIG1lYW4gPSAlcyIsIHJvdW5kKHNhbXBsZV9tZWFuLCA0KSkpCiAgcG9seWdvbihjKG1pbih4KSwgeFt4IDw9IHNhbXBsZV9tZWFuXSwgc2FtcGxlX21lYW4pLAogICAgICAgICAgYygwLCBkZW5zaXR5W3doaWNoKHggPD0gc2FtcGxlX21lYW4pXSwgMCksCiAgICAgICAgICBjb2w9cmdiKDEsIDAsIDAsIDAuNSksIGJvcmRlcj1OQSkKfQoKIyBDYWxjdWxhdGUgdGhlIHBvcHVsYXRpb24gc2Qgb2YgYSBCZXRhKGEsIGIpLgpzZF9iZXRhIDwtIGZ1bmN0aW9uKGEsIGIpIHsKICBzcXJ0KChhKmIpIC8gKChhICsgYileMiooYSArIGIgKyAxKSkpCn0KdHJ1ZV9zZCA8LSBzZF9iZXRhKGJldGFfYSwgYmV0YV9iKQoKcGxvdF9zYW1wbGluZ19kaXN0KFgxLCB0cnVlX21lYW49aDBfbWVhbiwgdHJ1ZV9zZD10cnVlX3NkKQpgYGAKClRoZSBjdXJ2ZSBpcyB0aGUgcHJvYmFiaWxpdHkgZGVuc2l0eSBvZiBzYW1wbGUgbWVhbi4KSXQgbWVhbnMgdGhhdCBpdCBjYWxjdWxhdGVzIHRoZSBsaWtlbGlob29kIG9mIG9ic2VydmluZyBhIHNwZWNpZmljIHNhbXBsZSBtZWFuIGdpdmVuIGEgcmFuZG9tIGRhdGFzZXQgZnJvbSBvdXIgcG9wdWxhdGlvbi4KCkluIHBsYWluIHdvcmRzLAppZiB3ZSBjYW4gZHJhdyBhbiBpbmZpbml0ZSBudW1iZXIgb2YgcmFuZG9tIHNhbXBsZSBkYXRhc2V0cyB3aXRoIHNpemUgMTAwMCAod2hpY2ggd2UgY2VydGFpbmx5IGNhbid0IGluIHRoZSByZWFsIHdvcmxkKSwKYW5kIGNhbGN1bGF0aW5nIHRoZSBzYW1wbGUgbWVhbiBmb3IgYWxsIG9mIHRoZW0sCnRoZSBkaXN0cmlidXRpb24gb2YgdGhlc2UgcmVzdWx0aW5nIHNhbXBsZSBtZWFucyB3aWxsIGJlaGF2ZSBleGFjdGx5IGxpa2UgdGhlIGN1cnZlIHdlIGp1c3QgcGxvdHRlZCwKcHJvdmlkZWQgdGhhdCB0aGUgbnVsbCBoeXBvdGhlc2lzIGlzIHRydWUuClRoaXMgaXMgYW4gZXN0YWJsaXNoZWQgbWF0aGVtYXRpY2FsIGZhY3QgdW5kZXIgYSB2ZXJ5IGxvb3NlIGFzc3VtcHRpb246CmFzIGxvbmcgYXMgdGhlIHJhbmRvbSBzYW1wbGUgaXMgdHJ1ZWx5IGkuaS5kLiAoaWRlbnRpY2FsbHkgYW5kIGluZGVwZW5kZW50bHkgZGlzdHJpYnV0ZWQpIGFuZCB0aGUgcG9wdWxhdGlvbiBoYXMgYSBmaW5pdGUgdmFyaWFuY2UuCgpUaGUgc2hhZGVkIGFyZWEgc2l6ZSBpcyAqdGhlIHByb2JhYmlsaXR5IG9mIG9ic2VydmluZyBhIHNhbXBsZSBtZWFuIG1vcmUgZXh0cmVtZSB0aGFuIHRoZSBjdXJyZW50IG9uZSB3ZSBoYXZlLCBwcm92aWRlZCB0aGF0IHRoZSBudWxsIGh5cG90aGVzaXMgaXMgdHJ1ZSouCkluZGVlZCBpdCBpcyB0aGUgZmFtb3VzIChhbmQgYWxzbyBub3RvcmlvdXMpICpwLXZhbHVlKiBvZiB0aGUgdGVzdC5eW1VuZGVyIHRoZSBjb250ZXh0IG9mIGEgdHdvLXRhaWxlZCB0ZXN0IHRoZSBhY3R1YWwgcC12YWx1ZSB3aWxsIGJlIDIgdGltZXMgdGhlIHNoYWRlZCBhcmVhIHNpemUsCnNpbmNlIHRoZSBkaXN0cmlidXRpb24gaXMgc3ltbWV0cmljIGFuZCB3ZSBhcmUgY29uc2lkZXJpbmcgJFByKHggPiB8enwpID0gUHIoeCA+IHopICsgUHIoeCA8IC16KSQuXQoKVGhlIGlkZWEgb2YgcmVqZWN0aW5nIHRoZSBudWxsIGh5cG90aGVzaXMgZHVlIHRvIGEgbG93IHAtdmFsdWUgaGVuY2UgaGFzIGEgY2xlYXIgcmF0aW9uYWxlOgpJZiB0aGUgbnVsbCBoeXBvdGhlc2lzIGlzIHRydWUsIAppdCBpcyBzbyB1bmxpa2VseSB0aGF0IHdlIHdpbGwgb2JzZXJ2ZSBhIGV2ZW4gbW9yZSBleHRyZW1lIHNhbXBsZSBkYXRhc2V0IHRoYW4gdGhlIGN1cnJlbnQgb25lIGF0IGhhbmQuClNvIG1heWJlIHRoZSBudWxsIGh5cG90aGVzaXMgaXMganVzdCBub3QgY29ycmVjdCBpbiB0aGUgZmlyc3QgcGxhY2U/CgpJbiB0aGUgZXhhbXBsZSB3ZSB1c2UgYSBzby1jYWxsZWQgKnotdGVzdCogZm9yIHNpbXBsaWNpdHkgd2hpbGUgaW4gcHJhY3RpY2UgdXN1YWxseSBhICp0LXRlc3QqIGlzIHByZWZlcnJlZC4gClRoYXQgaXMsCmluc3RlYWQgb2YgdXNpbmcgdGhlIE5vcm1hbCBkaXN0cmlidXRpb24gZGlyZWN0bHkgZm9yIHRoZSBzYW1wbGluZyBkaXN0cmlidXRpb24sCnVzaW5nIHRoZSBbU3R1ZGVudCdzIHQgZGlzdHJpYnV0aW9uXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9TdHVkZW50JTI3c190LWRpc3RyaWJ1dGlvbikgd2hpY2ggaXMgYWxzbyBhc3ltcHRvdGljYWxseSBOb3JtYWwgYnV0IGNhbiBjb25zZXJ2YXRpdmVseSBhY2NvdW50IGZvciBzbWFsbCBzYW1wbGUgZGlzdG9ydGlvbiBhbmQgdW5rbm93biBwb3B1bGF0aW9uIHZhcmlhbmNlLgoKQSB0LXRlc3QgY2FuIGJlIGRvbmUgYnkgYSBvbmUtbGluZXIgaW4gUjoKCmBgYHtyIHNpbXBsZV9kYXRhX3R0ZXN0fQp0LnRlc3QoWDEsIG11PWgwX21lYW4pCmBgYAoKV2UgY2FuIGNoZWNrIHRoYXQgdGhlIHNoYWRlZCBhcmVhIHNpemUgaW4gb3VyIHotdGVzdCBwbG90IHNob3VsZCBiZSBhcHByb3hpbWF0ZWx5IHRoZSBzYW1lIGFzIHRoZSB0LXRlc3QgcmVzdWx0IHNpbmNlIHdlIGhhdmUgYSBmYWlybHkgbGFyZ2Ugc2FtcGxlIHNpemUgKHBsdXMgdGhhdCB0aGUgcG9wdWxhdGlvbiBkaXN0cmlidXRpb24gaXMgc2ltcGxlKToKCmBgYHtyIHNpbXBsZV9kYXRhX2NoZWNrX3p0ZXN0fQojIEludGVncmFsIG92ZXIgLUluZiB+IHNhbXBsZSBtZWFuIGF0IHRoZSBzYW1wbGluZyBkaXN0cmlidXRpb24gcGRmLgpwbm9ybShtZWFuKFgxKSwgbWVhbj1oMF9tZWFuLCBzZD1zZChYMSkvc3FydChsZW5ndGgoWDEpKSkqMiAgIyBEb3VibGluZyBzaW5jZSB0aGlzIGlzIGEgdHdvLXNpZGVkIHRlc3QuCmBgYAoKIyMjIENvbmZpZGVuY2UgSW50ZXJ2YWwKCkNvbmZpZGVuY2UgaW50ZXJ2YWwgaXMgYW5vdGhlciBjb21tb24gc3RhdGlzdGljIHVzZWQgdG8gZGVzY3JpYmUgdGhlIHRlc3QuCkJhc2VkIG9uIGEgc2FtcGxlIGRhdGFzZXQsCml0IGNhbGN1bGF0ZXMgdGhlIHJhbmdlIG9mIHZhbHVlcyB0aGF0IGNhbm5vdCBiZSByZWplY3RlZCB1bmRlciBhIGNlcnRhaW4gbGV2ZWwgb2Ygc2lnbmlmaWNhbmNlLgoKV2UgY2FuIGNhbGN1bGF0ZSB0aGUgOTUlIGludGVydmFsIGJhc2VkIG9uIHRoZSB6LXRlc3QgbGltaXRpbmcgc2FtcGxpbmcgZGlzdHJpYnV0aW9uOgoKYGBge3Igc2ltcGxlX2RhdGFfY2lfenRlc3R9CiMgcW5vcm0gaXMgdGhlIHF1YW50aWxlIGZ1bmN0aW9uIGZvciBhIE5vcm1hbCBjZGYuCnpfbGIgPC0gcW5vcm0oLjAyNSwgbWVhbj1tZWFuKFgxKSwgc2Q9c2QoWDEpL3NxcnQobGVuZ3RoKFgxKSkpCnpfaGIgPC0gcW5vcm0oLjk3NSwgbWVhbj1tZWFuKFgxKSwgc2Q9c2QoWDEpL3NxcnQobGVuZ3RoKFgxKSkpCmMoel9sYiwgel9oYikKYGBgCgpPciB1bmRlciB0LXRlc3Q6CgpgYGB7ciBzaW1wbGVfZGF0YV9jaV90dGVzdH0KIyBxdCBpcyB0aGUgcXVhbnRpbGUgZnVuY3Rpb24gZm9yIGEgc3RhbmRhcmQgc3R1ZGVudHMnIHQgY2RmLgp0X2xiIDwtIHF0KC4wMjUsIGRmPWxlbmd0aChYMSkgLSAxKSpzZChYMSkvc3FydChsZW5ndGgoWDEpKSArIG1lYW4oWDEpCnRfaGIgPC0gcXQoLjk3NSwgZGY9bGVuZ3RoKFgxKSAtIDEpKnNkKFgxKS9zcXJ0KGxlbmd0aChYMSkpICsgbWVhbihYMSkKYyh0X2xiLCB0X2hiKSAgIyBDaGVjayBpZiBpdCBpcyBjb25zaXN0ZW50IHdpdGggdGhlIHJlc3VsdCBvZiBhIHQudGVzdC4KYGBgCgpUaGUgaW50ZXJwcmV0YXRpb24gb2YgdGhlIGZyZXF1ZW50aXN0IGNvbmZpZGVuY2UgaW50ZXJ2YWwgaXMgbm90IHN0cmFpZ2h0Zm9yd2FyZCBhbmQgaGFzIGJlZW4gbWlzLXVuZGVyc3Rvb2QgcXVpdGUgb2Z0ZW4uCkEgOTUlIGNvbmZpZGVuY2UgaW50ZXJ2YWwgbWVhbnMgdGhhdCB0aGUgaW50ZXJ2YWwgaGFzIDk1JSBjaGFuY2UgdG8gY29udGFpbiB0aGUgdHJ1ZSBwYXJhbWV0ZXIgdmFsdWUuCkl0IGRvZXMgTk9UIG1lYW4gdGhhdCB0aGUgdHJ1ZSBwYXJhbWV0ZXIgdmFsdWUgaGFzIGEgOTUlIGNoYW5jZSB0byBiZSB3aXRoaW4gdGhlIGludGVydmFsLgpUaGUgY29uZmlkZW5jZSBsZXZlbCBpcyBkZXNjcmliaW5nIHRoZSBpbnRlcnZhbCBidXQgbm90IHRoZSBwb3B1bGF0aW9uIHBhcmFtZXRlci4KCkluIHRoZSBmcmVxdWVudGlzdCB3b3JsZCBhbnkgcG9wdWxhdGlvbiBwYXJhbWV0ZXIgaXMgYSBjb25zdGFudCwKc28gdGhlcmUgaXMgbm8gcHJvYmFiaWxpc3RpYyBkZXNjcmlwdGlvbiBvbiBpdC4KR2l2ZW4gYW55IGNvbmZpZGVuY2UgaW50ZXJ2YWwgdGhlIHBhcmFtZXRlciB3aWxsIGJlIGVpdGhlciBpbnNpZGUgb3Igb3V0c2lkZSB0aGUgcmFuZ2UuCkl0IGlzIGEgKmRldGVybWluaXN0aWMqIGJ1dCB1bmtub3duIGV2ZW50LgpUaGUgcHJvYmFiaWxpc3RpYyBkZXNjcmlwdGlvbiBleGlzdHMgb25seSBmb3IgYSByYW5kb20gdmFyaWFibGUuClNhbXBsZSBtZWFuIGlzIGEgcmFuZG9tIHZhcmlhYmxlLgoqQ29uZmlkZW5jZSBpbnRlcnZhbCBpcyBhIHJhbmRvbSB2YXJpYWJsZSouClNvIGEgOTUlIGNvbmZpZGVuY2UgaW50ZXJ2YWwgaXMgYSBwcm9iYWJpbGlzdGljIGRlc2NyaXB0aW9uIG9uIHRoZSBpbnRlcnZhbCBidXQgbm90IG9uIHRoZSBwYXJhbWV0ZXIuCgpQdXQgaXQgZGlmZmVyZW50bHksCmlmIHdlIHdlcmUgdG8gcmVwZWF0IHRoZSBzYW1wbGluZyBleHBlcmltZW50IG1hbnkgbWFueSB0aW1lcywKdGhlbiBhcHByb3hpbWF0ZWx5IDk1JSBvZiB0aGUgZXhwZXJpbWVudHMgd2Ugd2lsbCBoYXZlIHRoZSByZXN1bHRpbmcgY29uZmlkZW5jZSBpbnRlcnZhbCBjb250YWluaW5nIHRoZSB0cnVlIHBhcmFtZXRlci4KRm9yIGEgZ2l2ZW4gc2FtcGxlIGRhdGFzZXQsCnRoZSBjb25maWRlbmNlIGludGVydmFsIG9mIHRoZSBzYW1wbGUgbWVhbiBtZXJlbHkgcGlja3MgdXAgYWxsIHRoZSB2YWx1ZXMgdGhhdCBjYW5ub3QgYmUgcmVqZWN0ZWQgYXNzdW1pbmcgdGhlIHNhbXBsZSBtZWFuIGlzIHRoZSB0cnVlIG1lYW4gYXQgdGhlIGdpdmVuICRcYWxwaGEkJSB3aGVyZSAkMSAtIFxhbHBoYSQgaXMgY2FsbGVkIHRoZSBsZXZlbCBvZiBjb25maWRlbmNlLgoKV2hlbiBjb25maWRlbmNlIGludGVydmFsIGlzIHVzZWQgaW4gYSBoeXBvdGhlc2lzIHRlc3QsCndlIHNpbXBseSBjaGVjayBpZiB0aGUgaW50ZXJ2YWwgY292ZXJzIHRoZSBudWxsIHZhbHVlLgpJZiBhIDk1JSBpbnRlcnZhbCBjb3ZlcnMgdGhlIG51bGwgdmFsdWUsCml0IGlzIGVxdWl2YWxlbnRseSB0byBzYXkgdGhhdCB3ZSBhcmUgbm90IGFibGUgdG8gcmVqZWN0IHRoZSBudWxsIGF0IDUlIHNpZ25pZmljYW5jZSBsZXZlbC4KClRvIGlsbHVzdHJhdGUgdmlzdWFsbHksCndlIHBsb3QgdGhlIHNhbXBsaW5nIGRpc3RyaWJ1dGlvbiBvZiBzYW1wbGUgbWVhbiB1bmRlciB0aGUgdHJ1ZSBtZWFuIChpbiBibGFjayksCmFsb25nIHdpdGggYW4gZXh0cmVtZSBjYXNlIG9mIGEgc2FtcGxlIG1lYW4gd2hvc2UgOTUlIEMuSS4gYmFyZWx5IGNvbnRhaW5zIHRoZSB0cnVlIG1lYW4gKGluIGJsdWUgYW5kIHJlZCkuCgpgYGB7ciBzaW1wbGVfZGF0YV9wbG90X2NpfQpwbG90X2NpIDwtIGZ1bmN0aW9uKFgsIHRydWVfbWVhbiwgY29tcGFyZV9tZWFuPU5VTEwsIHRydWVfc2Q9TlVMTCwgCiAgICAgICAgICAgICAgICAgICAgYW5ub3RhdGVfc2FtcGxlX21lYW49VFJVRSwgeGxpbV9hZGo9YygzLCA2KSwgLi4uKSB7CiAgc2FtcGxlX21lYW4gPC0gbWVhbihYKSAgIyBOb3QgdXNlZC4KICBzZSA8LSAKICAgIGlmICggaXMubnVsbCh0cnVlX3NkKSApIHsKICAgICAgIyBJZiB0cnVlIHBvcHVsYXRpb24gc2QgaXMgdW5rbm93biAocHJhY3RpY2FsbHkgYWx3YXlzIHRoZSBjYXNlKSwKICAgICAgIyB3ZSBjYW4gcGx1ZyBpbiB0aGUgc2FtcGxlIHNkIGluc3RlYWQgYXMgYW4gYXBwcm94aW1hdGlvbi4KICAgICAgc2FtcGxlX3NkIDwtIHNkKFgpCiAgICAgIHNhbXBsZV9zZCAvIHNxcnQobGVuZ3RoKFgpKQogICAgfSBlbHNlIHsKICAgICAgdHJ1ZV9zZCAvIHNxcnQobGVuZ3RoKFgpKQogICAgfQogIGVyciA8LSBxbm9ybSguOTc1LCBtZWFuPTAsIHNkPXNlKQogIGlmICggaXMubnVsbChjb21wYXJlX21lYW4pICkgewogICAgY29tcGFyZV9tZWFuIDwtIHRydWVfbWVhbiArIGVycgogIH0KICB6X2hiIDwtIGNvbXBhcmVfbWVhbiArIGVycgogIHpfbGIgPC0gY29tcGFyZV9tZWFuIC0gZXJyCiAgeCA8LSBzZXEodHJ1ZV9tZWFuIC0geGxpbV9hZGpbMV0qc2UsIHRydWVfbWVhbiArIHhsaW1fYWRqWzJdKnNlLCBsZW5ndGg9MTAwMCkKICBkZW5zaXR5X3RydWUgPC0gZG5vcm0oeCwgbWVhbj10cnVlX21lYW4sIHNkPXNlKQogIGRlbnNpdHlfY29tcGFyZSA8LSBkbm9ybSh4LCBtZWFuPWNvbXBhcmVfbWVhbiwgc2Q9c2UpCiAgcGxvdCh4LCBkZW5zaXR5X3RydWUsIHR5cGU9ImwiLCB5YXhzPSJpIiwgeWxhYj0iRGVuc2l0eSIsIC4uLikKICBsaW5lcyh4LCBkZW5zaXR5X2NvbXBhcmUsIHR5cGU9ImwiLCBjb2w9ImJsdWUiKQogIGFibGluZSh2PWNvbXBhcmVfbWVhbiwgY29sPSJibHVlIiwgbHR5PTIpCiAgYWJsaW5lKHY9Yyh6X2xiLCB6X2hiKSwgY29sPSJyZWQiLCBsdHk9MikKICBpZiAoIGFubm90YXRlX3NhbXBsZV9tZWFuICkgewogICAgdGV4dChjb21wYXJlX21lYW4sIG1lYW4oZGVuc2l0eV9jb21wYXJlKSAvIDIsIHBvcz00LCBjb2w9ImJsdWUiLCBsdHk9MiwKICAgICAgICAgc3ByaW50ZigiRXh0cmVtZSBTYW1wbGUgTWVhbiA9ICVzIiwgcm91bmQoc2FtcGxlX21lYW4sIDQpKSkKICB9CiAgYXJyb3dzKHgwPXpfbGIsIHkwPWRpZmYocmFuZ2UoZGVuc2l0eV90cnVlKSkvMiwgCiAgICAgICAgIHgxPXpfaGIsIHkxPWRpZmYocmFuZ2UoZGVuc2l0eV90cnVlKSkvMiwKICAgICAgICAgY29kZT0zLCBsdHk9MiwgY29sPSJyZWQiKQogIHRleHQoY29tcGFyZV9tZWFuLCBkaWZmKHJhbmdlKGRlbnNpdHlfdHJ1ZSkpLzIsCiAgICAgICAiOTUlIENvbmZpZGVuY2UgSW50ZXJ2YWwiLCBwb3M9MSwgY29sPSJyZWQiKQogIAogICMgVHdvLXRhaWxlZCBwLXZhbHVlIGFyZWEgdW5kZXIgdGhlIHRydXRoLiAoUGxvdCBvbmUgc2lkZSBvbmx5LikKICBhcmVhX2NvbCA8LSByZ2IoMSwgMCwgMCwgMC41KQogIGlmICggY29tcGFyZV9tZWFuID49IHRydWVfbWVhbiApIHsgIAogICAgcG9seWdvbihjKGNvbXBhcmVfbWVhbiwgeFt4ID49IGNvbXBhcmVfbWVhbl0sIG1heCh4KSksCiAgICAgICAgICAgIGMoMCwgZGVuc2l0eV90cnVlW3doaWNoKHggPj0gY29tcGFyZV9tZWFuKV0sIDApLAogICAgICAgICAgICBjb2w9YXJlYV9jb2wsIGJvcmRlcj1OQSkKICB9IGVsc2UgewogICAgcG9seWdvbihjKG1pbih4KSwgeFt4IDw9IGNvbXBhcmVfbWVhbl0sIGNvbXBhcmVfbWVhbiksCiAgICAgICAgICAgIGMoMCwgZGVuc2l0eV90cnVlW3doaWNoKHggPD0gY29tcGFyZV9tZWFuKV0sIDApLAogICAgICAgICAgICBjb2w9YXJlYV9jb2wsIGJvcmRlcj1OQSkKICB9Cn0KCnBsb3RfY2koWDEsIHRydWVfbWVhbj10cnVlX21lYW4sCiAgICAgICAgbWFpbj1icXVvdGUoYmFyKFgpIH4gIlNhbXBsaW5nIERpc3RyaWJ1dGlvbiB1bmRlciBUcnVlIE1lYW4iKSwKICAgICAgICBzdWI9IkV4dHJlbWUgQy5JLiB0byBDb250YWluIFRydWUgTWVhbiIsCiAgICAgICAgeGxhYj1icXVvdGUoYmFyKFgpKSkKYGBgCgpUaGUgcmVkIGFyZWEgY29ycmVzcG9uZHMgdG8gdGhlIHR3by10YWlsZWQgOTUlIHNpZ25pZmljYW5jZSwKd2l0aCBhIHNpemUgb2YgZXhhY3RseSAwLjAyNS4KKFdlIG9ubHkgY29sb3JpemUgdGhlIHJpZ2h0IHNpZGUuKQpJdCBpcyBhbHNvIHRoZSBwLXZhbHVlIGluIHRoaXMgY2FzZSBzaW5jZSB3ZSBwdXJwb3NlbHkgbWFrZSB0aGUgc2FtcGxlIG1lYW4ganVzdCBhcyBleHRyZW1lIHRvIGJlIG9uIHRoZSBlZGdlIG9mIDk1JSBzaWduaWZpY2FuY2UgbGV2ZWwuCkFueSByZXN1bHRpbmcgc2FtcGxlIG1lYW4gdmFsdWUgZmFsbGluZyBpbnRvIHRoZSBhcmVhIHdpbGwgaGF2ZSBhIEMuSS4gbm90IGFibGUgdG8gY292ZXIgdGhlIHRydWUgbWVhbi4KVGhlIHNhbXBsaW5nIGRpc3RyaWJ1dGlvbiBiYXNlZCBvbiB0aGUgZXh0cmVtZSBzYW1wbGUgbWVhbiAoaW4gYmx1ZSkgaXMgc2ltcGx5IGEgc2hpZnQgb2YgdGhlIHRydWUgc2FtcGxpbmcgZGlzdHJpYnV0aW9uIHRvIHRoZSByaWdodC4KClRvIHJlaXRlcmF0ZSB0aGUgZmFjdCB0aGF0IEMuSS4gaXMgYSByYW5kb20gdmFyaWFibGUsCmxldCdzIHJlcGVhdCB0aGUgc2FtcGxpbmcgZXhwZXJpbWVudCBmb3IgOSB0aW1lcyBhbmQgcGxvdCBhbGwgdGhlIHJlc3VsdHM6CgpgYGB7ciBzaW1wbGVfZGF0YV9wbG90X21vcmVfY2ksIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTEwfQpzZXQuc2VlZCg3NzcpCgpwYXIobWZyb3c9YygzLCAzKSwgb21hPWMoMywgMywgMCwgMCksIG1hcj1jKDMsIDMsIDIsIDIpKQpmb3IgKCBpIGluIDE6OSApIHsKICBYMiA8LSByYmV0YSgxMDAwLCBiZXRhX2EsIGJldGFfYikKICBwbG90X2NpKFgyLCB0cnVlX21lYW49dHJ1ZV9tZWFuLCBjb21wYXJlX21lYW49bWVhbihYMiksCiAgICAgICAgICBhbm5vdGF0ZV9zYW1wbGVfbWVhbj1GQUxTRSwgeGxpbV9hZGo9Yyg0LCA0KSkKfQptdGV4dCh0ZXh0PSJDb25maWRlbmNlIEludGVydmFsIGFzIFJhbmRvbSBWYXJpYWJsZSIsIHNpZGU9MSwgbGluZT0wLCBvdXRlcj1UUlVFKQpgYGAKCkFzIG9uZSBjYW4gc2VlLApvdXQgb2Ygb3VyIDkgaW5kZXBlbmRlbnQgZXhwZXJpbWVudHMgdGhlcmUgaXMgb25lIGNhc2Ugd2hlcmUgd2Ugd3JvbmdseSByZWplY3RlZCB0aGUgdHJ1ZSBtb2RlbC4KKFRoZSBzby1jYWxsZWQgVHlwZS1JIEVycm9yLikKClRoZSBrZXkgdGFrZS1hd2F5IGFib3V0IGZyZXF1ZW50aXN0IGNvbmZpZGVuY2UgaW50ZXJ2YWw6CgorIEl0IGlzIGEgc2FtcGxpbmcgc3RhdGlzdGljOyBoZW5jZSBhIHJhbmRvbSB2YXJpYWJsZS4KKyBUaGVyZSBpcyBubyBkaXN0cmlidXRpb25hbCBpbmZvcm1hdGlvbiBpbnNpZGUgdGhlIGludGVydmFsOyBpdCdzIGVpdGhlciB0aGUgaW50ZXJ2YWwgZG9lcyBjb250YWluIG9yIGRvZXMgbm90IGNvbnRhaW4gdGhlIHRydWUgcGFyYW1ldGVyLiAoV2hpY2ggd2Ugd2lsbCBuZXZlciBrbm93IHNpbmNlIHRoZSB0cnVlIHBhcmFtZXRlciBpcyBhbiB1bmtub3duIGNvbnN0YW50LikKKyBUaGUgbGFyZ2VyIHRoZSBzYW1wbGUgc2l6ZSB0aGUgbmFycm93ZXIgdGhlIGludGVydmFsLCBzaW5jZSBpdCBpcyBkZXJpdmVkIGZyb20gYSBzYW1wbGluZyBkaXN0cmlidXRpb24gd2hpY2ggYnkgTExOIHdpbGwgY29udmVyZ2UgaW4gcHJvYmFiaWxpdHkgdG8gdGhlIHRydWUgcGFyYW1ldGVyIHZhbHVlLgoKIyMjIEdlbmVyYWxpemF0aW9uCgpUaG91Z2ggaW4gdGhpcyBzZWN0aW9uIHdlIG9ubHkgY292ZXIgYSB2ZXJ5IHNpbXBsZSBvbmUtc2FtcGxlIGh5cG90aGVzaXMgdGVzdGluZyB1bmRlciBhIHVuaXZhcmlhdGUgY29udGV4dCwKdGhlIGdlbmVyYWwgaWRlYSBiZWhpbmQgdGhlIHNjZW5lIGZvciBhIGZ1bGwtZmxlZGdlZCBzdGF0aXN0aWNhbCBtb2RlbCBpcyBhYm91dCB0aGUgc2FtZS4KCkZvciBleGFtcGxlLAppbiBvcmRlciB0byBtYWtlIGluZmVyZW5jZSBhYm91dCBhIGNvZWZmaWNpZW50ICRcYmV0YSQgaW4gYSByZWdyZXNzaW9uIG1vZGVsICR5ID0gWFxiZXRhICsgXGVwc2lsb24kLAphIGZyZXF1ZW50aXN0IGZpcnN0IHRyaWVzIHRvIGRldmVsb3AgdGhlIGFzeW1wdG90aWMgcHJvcGVydHkgb2YgdGhlIHNhbXBsaW5nIGRpc3RyaWJ1dGlvbiBvZiAkXGJldGEkLCAKdGhlbiBhcHBseSB0aGUgaHlwb3RoZXNpcyB0ZXN0aW5nIHdpdGggYSBudWxsIG9mICRcYmV0YSA9IDAkIHRvIHRlc3QgaWYgdGhlIGxpbmVhciBhc3NvY2lhdGlvbiBiZXR3ZWVuIHRoZSB0YXJnZXQgJHkkIGFuZCB0aGUgdmFyaWFibGUgJFgkIGlzIHNpZ25pZmljYW50bHkgcHJlc2VudC4KSXQgdHVybnMgb3V0IHRoYXQgQ0xUIGFsc28gYXBwbGllcyB0byBsaW5lYXIgbW9kZWwgY29lZmZpY2llbnRzLgpUaGUgcmF0aW9uYWxlIGlzIHRoYXQgdGhlc2UgY29lZmZpY2llbnRzIGFyZSBpbmRlZWQgYSBraW5kIG9mIHdlaWdodGVkLWF2ZXJhZ2Ugd2hlcmUgdGhlIHdlaWdodHMgYXJlIGRldGVybWluZWQgYnkgdGhlIGNvdmFyaWFuY2UuCgojIyMgQ3JpdGlxdWVzCgpUaGlzIHNlY3Rpb24gZGVzY3JpYmVzIHRoZSBjcml0aXF1ZXMgd2VsbCBkb2N1bWVudGVkIGluIEByb3VkZXIyMDA5YmF5ZXNpYW4uCgpPbmUgY3J1Y2lhbCBsaW1pdGF0aW9uIG9mIHRoZSBmcmVxdWVudGlzdCBhcHByb2FjaCBpcyB0aGF0IGl0IGRvZXMgbm90IGFsbG93IHVzIHRvIHN0YXRlIGV2aWRpZW5jZSBmb3IgYnV0IG9ubHkgYWdhaW5zdCB0aGUgbnVsbCBoeXBvdGhlc2lzLgpXaGF0IGRvZXMgdGhpcyBleGFjdGx5IG1lYW4/CkxldCdzIGV4YW1pbmUgdGhlIGJlaGF2aW9yIG9mIHAtdmFsdWUgdW5kZXIgdHdvIGRpZmZlcmVudCBzY2VuYXJpb3M6IHdoZW4gdGhlIG51bGwgbW9kZWwgaXMgZmFsc2UgYW5kIHdoZW4gdGhlIG51bGwgbW9kZWwgaXMgdHJ1ZS4KCiMjIyMgV2hlbiBOdWxsIGlzIEZhbHNlIHstfQoKQXMgc2FtcGxlIHNpemUgZ3Jvd3MsCnRoZSB6LXN0YXRpc3RpYyAob3IgdC1zdGF0aXN0aWMpIGdyb3dzIHVuYm91bmRlZGx5LApyZXN1bHRpbmcgaW4gcC12YWx1ZSBhcHByb2NoaW5nIHplcm8uCgpMZXQncyBhc3N1bWUgdGhlIG51bGwgbW9kZWwgaXM6CgpgYGB7ciBjcml0aXF1ZV9udWxsfQojIERlcml2ZSBzYW1wbGluZyBkaXN0cmlidXRpb24gdW5kZXIgdGhlIG51bGwuCm51bGxfbWVhbiA8LSAwLjI1Cm51bGxfc2QgPC0gMQpudWxsX21vZGVsX3BkZiA8LSBmdW5jdGlvbih4LCBuKSBkbm9ybSh4LCBudWxsX21lYW4sIG51bGxfc2QgLyBzcXJ0KG4pKQpgYGAKCmByIHNwcmludGYoIiRcXG1ib3h7Tm9ybWFsKCVzLCAlcyl9LCQiLCBudWxsX21lYW4sIG51bGxfc2QpYAoKYW5kIHRoZSB0cnVlIG1vZGVsIGlzOgoKYGBge3IgY3JpdGlxdWVfdHJ1ZX0KIyBEZXJpdmUgc2FtcGxpbmcgZGlzdHJpYnV0aW9uIHVuZGVyIHRoZSB0cnV0aC4KcmVhbF9tZWFuIDwtIDAKcmVhbF9zZCA8LSAxCnJlYWxfbW9kZWxfcGRmIDwtIGZ1bmN0aW9uKHgsIG4pIGRub3JtKHgsIHJlYWxfbWVhbiwgcmVhbF9zZCAvIHNxcnQobikpCmBgYAoKYHIgc3ByaW50ZigiJFxcbWJveHtOb3JtYWwoJXMsICVzKX0uJCIsIHJlYWxfbWVhbiwgcmVhbF9zZClgCgpOb3cgd2UgcGxvdCB0aGUgc2FtcGxpbmcgZGlzdHJpYnV0aW9uIG9mIGJvdGggbW9kZWxzIHdpdGggcmFuZG9tIHNhbXBsZXMgb2YgdmFyeWluZyBzYW1wbGUgc2l6ZToKCmBgYHtyIGNyaXRpcXVlX2ZhbHNlX251bGx9CiMgTWFrZSBwYXJ0aWFsIGZ1bmN0aW9ucyBmb3IgZWFzZSBvZiB1c2luZyBjdXJ2ZSgpIGZvciBwbG90dGluZy4KbnVsbF9wZGYgPC0gZnVuY3Rpb24obikgZnVuY3Rpb24oeCkgbnVsbF9tb2RlbF9wZGYoeCwgbj1uKQpyZWFsX3BkZiA8LSBmdW5jdGlvbihuKSBmdW5jdGlvbih4KSByZWFsX21vZGVsX3BkZih4LCBuPW4pCgpwbG90X3NhbXBsaW5nX2Rpc3RfMiA8LSBmdW5jdGlvbih4LCBudWxsX3BkZiwgdHJ1ZV9wZGYsIG51bGxfbWVhbiwgdHJ1ZV9tZWFuKSB7CiAgc2FtcGxlX21lYW4gPC0gbWVhbih4KQogIHNlIDwtIHNkKHgpIC8gc3FydChsZW5ndGgoeCkpCiAgcDEgPC0gbnVsbF9wZGYobGVuZ3RoKHgpKQogIHhyYW5nZSA8LSBjKHNhbXBsZV9tZWFuIC0gNipzZSwgc2FtcGxlX21lYW4gKyA2KnNlKQogIHh0aWNrcyA8LSBzZXEoeHJhbmdlWzFdLCB4cmFuZ2VbMl0sIGxlbmd0aC5vdXQ9NTAwKQogIGN1cnZlKHAxLCBmcm9tPXhyYW5nZVsxXSwgdG89eHJhbmdlWzJdLCB5YXhzPSJpIiwKICAgICAgICBtYWluPXNwcmludGYoIlNhbXBsaW5nIERpc3QuIGF0IE4gPSAlcyIsIGxlbmd0aCh4KSkpCiAgcDIgPC0gdHJ1ZV9wZGYobGVuZ3RoKHgpKQogIGN1cnZlKHAyLCBmcm9tPXhyYW5nZVsxXSwgdG89eHJhbmdlWzJdLCBhZGQ9VFJVRSwgY29sPSJyZWQiLCBsdHk9MikKICBhYmxpbmUodj10cnVlX21lYW4sIGx0eT0yLCBjb2w9InJlZCIpCiAgYWJsaW5lKHY9c2FtcGxlX21lYW4sIGNvbD0icmVkIikKICAjIENvbG9yaXplIHAtdmFsdWUuCiAgYXJlYV9jb2wgPC0gcmdiKDEsIDAsIDAsIDAuNSkKICBpZiAoIHNhbXBsZV9tZWFuIDw9IG51bGxfbWVhbiApIHsKICAgIHBvbHlnb24oYyhtaW4oeCksIHh0aWNrc1t4dGlja3MgPD0gc2FtcGxlX21lYW5dLCBzYW1wbGVfbWVhbiksCiAgICAgICAgICAgIGMoMCwgcDEoeHRpY2tzKVt3aGljaCh4dGlja3MgPD0gc2FtcGxlX21lYW4pXSwgMCksCiAgICAgICAgICAgIGNvbD1hcmVhX2NvbCwgYm9yZGVyPU5BKQogIH0gZWxzZSB7CiAgICBwb2x5Z29uKGMoc2FtcGxlX21lYW4sIHh0aWNrc1t4dGlja3MgPiBzYW1wbGVfbWVhbl0sIG1heCh4KSksCiAgICAgICAgICAgIGMoMCwgcDEoeHRpY2tzKVt3aGljaCh4dGlja3MgPiBzYW1wbGVfbWVhbildLCAwKSwKICAgICAgICAgICAgY29sPWFyZWFfY29sLCBib3JkZXI9TkEpCiAgfQp9CgpzZXQuc2VlZCg3NzcpCnBhcihtZnJvdz1jKDIsIDIpLCBvbWE9YygzLCAzLCAwLCAwKSwgbWFyPWMoMywgMywgMiwgMikpCmZvciAoIG4gaW4gYygxMCwgMzAsIDEwMCwgMzAwKSApIHsKICB4IDwtIHJub3JtKG4sIHJlYWxfbWVhbiwgcmVhbF9zZCkKICBwbG90X3NhbXBsaW5nX2Rpc3RfMih4LCBudWxsX3BkZiwgcmVhbF9wZGYsIG51bGxfbWVhbiwgcmVhbF9tZWFuKQp9Cm10ZXh0KHRleHQ9IlZhbmlzaGluZyBQLVZhbHVlIEdpdmVuIE51bGwgaXMgRmFsc2UiLCBzaWRlPTEsIGxpbmU9MCwgb3V0ZXI9VFJVRSkKYGBgCgpXZSB1c2UgcmVkLWRvdCBsaW5lIGFzIHRoZSB0cnVlIHNhbXBsaW5nIGRpc3RydWJpdGlvbiB3aGlsZSB0aGUgc29saWQtYmxhY2sgbGluZSBhcyBzYW1wbGluZyBkaXN0cmlidXRpb24gdW5kZXIgdGhlIG51bGwuClRoZSBzb2xpZC1yZWQgbGluZSBpcyBvdXIgc2ltdWxhdGVkIHNhbXBsZSBtZWFuIGF0IGRpZmZlcmVudCBzYW1wbGUgc2l6ZS4KCkFzIG9uZSBjYW4gc2VlLCBhcyBzYW1wbGUgc2l6ZSBpbmNyZWFzZXMsIHAtdmFsdWUgd2lsbCBjb252ZXJnZSB0byAwIHNpbmNlIHRoZSBudWxsIGRpc3RyaWJ1dGlvbiB3aWxsIGNvbmNlbnRyYXRlIG1vcmUgYW5kIG1vcmUgb24gdGhlIHdyb25nIG1lYW4sCm1ha2luZyBpdCBsZXNzIGFuZCBsZXNzIHBvc3NpYmxlIGZvciB0aGUgc2FtcGxlIG1lYW4gdG8gZmFsbCBpbnRvIHRoZSByYW5nZTsKaGVuY2UgbWFraW5nIG91ciBzYW1wbGUgZGF0YSBleHRyZW1lbHkgdW5saWtlbHksIHdoaWNoIGluIHR1cm4gbGVhZHMgdG8gcmVqZWN0aW9uIG9mIHRoZSBudWxsLgoKVGhpcyBzdGF0aXN0aWNhbCBwcm9wZXJ0eSBpcyBhIGRlc2lyZWQgb25lICphcyBsb25nIGFzIHRoZSBudWxsIGlzIGFjdHVhbGx5IGZhbHNlKi4KSXQgc3VnZ2VzdHMgdGhhdCB0aGUgZXZpZGVuY2UgYWdhaW5zdCB0aGUgbnVsbCBjYW4gYmUgc3RyZW5ndGhlbiBpZiB3ZSBjb2xsZWN0IGEgbGFyZ2VyIHJhbmRvbSBzYW1wbGUuCkluIG90aGVyIHdvcmRzLCB0aGUgdGVzdCBpcyAqc3RhdGlzdGljYWxseSBjb25zaXN0ZW50KiB0aGFua3MgdG8gaXRzIGNvbnZlcmdlbmNlIGluIGxhcmdlIHNhbXBsZS4KCiMjIyMgV2hlbiBOdWxsIGlzIFRydWUgey19CgpXaGF0IGlmIHRoZSBudWxsIG1vZGVsIGlzIGFjdHVhbGx5IHRydWU/ClRoYXQgaXMsIG91ciBudWxsIGlzIHRoZSBzYW1lIGFzIHRoZSB0cnVlIG1vZGVsOiBgciBzcHJpbnRmKCIkXFxtYm94e05vcm1hbCglcywgJXMpfSQiLCByZWFsX21lYW4sIHJlYWxfc2QpYC4KV2UgYWdhaW4gcGxvdCB0aGUgc2ltdWxhdGlvbiBvZiBwLXZhbHVlIHVuZGVyIGRpZmZlcmVudCBzYW1wbGUgc2l6ZXM6CgpgYGB7ciBjcml0aXF1ZV90cnVlX251bGx9CnBhcihtZnJvdz1jKDIsIDIpLCBvbWE9YygzLCAzLCAwLCAwKSwgbWFyPWMoMywgMywgMiwgMikpCmZvciAoIG4gaW4gYygxMCwgMzAsIDEwMCwgMzAwKSApIHsKICB4IDwtIHJub3JtKG4sIHJlYWxfbWVhbiwgcmVhbF9zZCkKICBwbG90X3NhbXBsaW5nX2Rpc3RfMih4LCByZWFsX3BkZiwgcmVhbF9wZGYsIHJlYWxfbWVhbiwgcmVhbF9tZWFuKQp9Cm10ZXh0KHRleHQ9IlZhbmlzaGluZyBQLVZhbHVlIEdpdmVuIE51bGwgaXMgVHJ1ZSIsIHNpZGU9MSwgbGluZT0wLCBvdXRlcj1UUlVFKQpgYGAKCkFzIG9uZSBtYXkgYWxyZWFkeSByZWFsaXplLAppbiB0aGlzIGNhc2UgKnAtdmFsdWUgaXMgaW52YXJpYW50IGluIHNhbXBsZSBzaXplKi4KTm8gbWF0dGVyIGhvdyBsYXJnZSBvdXIgcmFuZG9tIHNhbXBsZSBpcywgdGhlcmUgaXMgbm8gd2F5IHRvIHN0cmVuZ3RoZW4gdGhlIGV2aWRlbmNlICpmb3IqIHRoZSBudWxsLgpUaGlzIGlzIGluZGVlZCB3aHkgaW4gdGhlIGZyZXF1ZW50aXN0IHN0YXRpc3RpY2FsIHdvcmRpbmdzIHdlIG5ldmVyICphY2NlcHQqIHRoZSBudWxsLApidXQgd2UgZWl0aGVyIHJlamVjdCBvciAqY2Fubm90IHJlamVjdCogdGhlIG51bGwuCgpUbyBzdW0gdXAsCnRoZSB0ZXN0IGlzIHN0YXRpc3RpY2FsbHkgKmNvbnNpc3RlbnQqIHdoZW4gdGhlIG51bGwgaXMgZmFsc2UuClRoZSBlcnJvciByYXRlIG9mIG5vdCByZWplY3RpbmcgdGhlIG51bGwgY29udmVyZ2UgdG8gMCBhcyBzYW1wbGUgc2l6ZSBncm93cyBpbmZpbml0ZWx5LgpUaGUgdGVzdCBpcywgaG93ZXZlciwgKmluY29uc2lzdGVudCogd2hlbiB0aGUgbnVsbCBpcyB0cnVlLgpUaGVyZSBpcyBhbHdheXMgYSAkXGFscGhhJCUgb2YgZXJyb3IgdG8gcmVqZWN0IHRoZSBjb3JyZWN0IG51bGwgbW9kZWwgcmVnYXJkbGVzcyBvZiB0aGUgc2FtcGxlIHNpemUsCm1heSBpdCBiZSBhIDUlIG9yIGEgMTAlIGF0IHRoZSBjaG9pY2Ugb2YgY29udmVudGlvbmFsIHByYWN0aWNlLgpTdWNoIHByb3BlcnR5IGxlYWRzIHRvIHBvdGVudGlhbCBvdmVyc3RhdGVtZW50IG9mIGV2aWRlbmNlIGFnYWluc3QgdGhlIG51bGwuCgojIyBBIEJpbm9taWFsIFByb2JsZW0KCkJlZm9yZSB3ZSB0YWtlIGEgZnVsbCBCYXlzaWFuIGFwcHJvYWNoIHRvIG91ciBmaXJzdCBleGFtcGxlLApsZXQncyBjb25zaWRlciBhbm90aGVyIGV4YW1wbGUgd2l0aCBhbmFseXRpY2FsIHNvbHV0aW9uIGF2YWlsYWJsZS4KCkNvbnNpZGVyIGEgY29pbiB0b3NzIGdhbWUsIHdpdGggJE4kIG51bWJlciBvZiB0b3NzZXMgYW5kIHdlIGFyZSBjb3VudGluZyB0aGUgbnVtYmVyIG9mIGhlYWRzIGFzICR5JC4KVGhlIG51bWJlciBvZiBoZWFkcyBmb2xsb3dzIGEgW0Jpbm9taWFsIGRpc3RyaWJ1dGlvbl0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvQmlub21pYWxfZGlzdHJpYnV0aW9uKSB3aXRoIHR3byBwYXJhbWV0ZXJzOgoKJCQKWSBcc2ltIFxtYm94e0Jpbm9taWFsfShOLCBwKS4KJCQKCldlIGFyZSBpbnRlcmVzdGVkIGluIGd1ZXNzaW5nIHRoZSAodW5rbm93bikgdHJ1ZSBwcm9wb3J0aW9uIG9mIGhlYWRzIChkZW5vdGVkIGFzICRwJCkgb3V0IG9mICROJCB0b3NzZXMuCgpMZXQncyBzaW11bGF0ZSBhIHNldCBvZiAkTiQgdHJpYWxzIGFuZCBjb3VudCB0aGUgaGVhZHM6CgpgYGB7ciBjb2luX2RhdGF9CnNldC5zZWVkKDc3NykKTiA8LSAxMApwIDwtIC41Cih5IDwtIHJiaW5vbSgxLCBOLCBwKSkKYGBgCgpCeSBvYnNlcnZpbmcgdGhlIGZhY3QgdGhhdCBvdXQgb2YgYHIgTmAgdHJpYWxzIHdlIGdvdCBgciB5YCBoZWFkcywgCndlJ2QgbGlrZSB0byB0ZXN0IHRoZSBmb2xsb3dpbmcgdHdvLXNpZGVkIGh5cG90aGVzaXM6CgokJApcYmVnaW57YWxpZ25lZH0KSF8wICY6IFxtYm94e1RoZSBjb2luIGlzIGZhaXIgd2l0aCBwID0gLjV9LCBcXApIXzEgJjogXG1ib3h7VGhlIGNvaW4gaXMgYmlhc2VkfS4KXGVuZHthbGlnbmVkfQokJAoKVGhpcyBpcyBhbHNvIGNhbGxlZCBhIHByb3BvcnRpb24gdGVzdCBpbiB0aGUgbGl0ZXJhdHVyZS4KCiMjIyBGcmVxdWVudGlzdCBQcm9wb3J0aW9uIFRlc3QKCldlIGNhbiBxdWlja2x5IGV4cGxvcmUgdGhlIGZyZXF1ZW50aXN0IHNvbHV0aW9uIGZvciB0aGlzIHRhc2sgZmlyc3QuCgpXZSBjYWxjdWxhdGUgdGhlIHNhbXBsZSBwcm9wb3J0aW9uIGFzICRcaGF0e3B9JCBhbmQgYWdhaW4gYnkgQ0xUIHdlIGhhdmUgdGhlIHNhbXBsZSBwcm9wb3J0aW9uIHRvIGRpc3RyaWJ1dGUgYXN5bXB0b3RpY2FsbHkgTm9ybWFsOl5bTm90ZSB0aGF0ICRcaGF0e3B9JCBpcyBpbmRlZWQgYSBkaXNjcmV0ZSByYW5kb20gdmFyaWFibGUgYnV0IHdlIGFyZSB1c2luZyBhIGNvbnRpbm91cyBkaXN0cmlidXRpb24gZm9yIGFwcHJveGltYXRpb24uIFRoZSBhcHByb3hpbWF0aW9uIGlzIG9mIGNvdXJzZSBiZXR0ZXIgZm9yIGxhcmdlciAkTiQuXQoKJCQKXGJlZ2lue2VxdWF0aW9ufQpcaGF0e3B9IFxvdmVyc2V0e2R9XHNpbSBcbWJveHtOb3JtYWx9KHAsIFxzcXJ0e1xmcmFje3AoMS1wKX17Tn19KS4KXGVuZHtlcXVhdGlvbn0KJCQKCkNhbGN1bGF0aW9uIG9mIHRoZSBwLXZhbHVlIGlzIHRoZW4gdmVyeSBzdHJhaWdodGZvcndhcmQ6CgpgYGB7ciBjb2luX3B2YWx1ZX0KIyBDYWxjdWxhdGUgdGhlIGFyZWEgdW5kZXIgdGhlIFBERiBvZiB0aGUgc2FtcGxpbmcgZGlzdHJpYnV0aW9uLAojIHByb3ZpZGVkIHRoYXQgdGhlIG51bGwgaHlwb3RoZXNpcyBpcyB0cnVlLgpwcm9wX3AgPC0gCiAgaWYgKCB5L04gPiBwICkgewogICAgKDEgLSBwbm9ybSh5L04sIHAsIHNxcnQocCooMS1wKS9OKSkpKjIKICB9IGVsc2UgewogICAgcG5vcm0oeS9OLCBwLCBzcXJ0KHAqKDEtcCkvTikpKjIKICB9CnByb3BfcApgYGAKCldlIGNhbiB2ZXJpZnkgdGhlIHJlc3VsdCB2aWEgdXNpbmcgUidzIGJ1aWx0LWluIGBwcm9wLnRlc3RgOgoKYGBge3IgY29pbl9wcm9wX3Rlc3R9CiMgV2UgcHVycG9zZWx5IHR1cm4gb2ZmIHRoZSBjb250aW51aXR5IGNvcnJlY3Rpb24gZmFjdG9yIGZvciBjb21wYXJpc29uLgojIEJ1dCB0aGUgY29ycmVjdGlvbiBjYW4gYmUgc2l6YWJsZSBpZiBzYW1wbGUgc2l6ZSBpcyBzbWFsbCBzaW5jZSAKIyB3aGVuIHNhbXBsZSBzaXplIGlzIHNtYWxsIHRoZSBOb3JtYWwgYXBwcm94aW1hdGlvbiBpcyBub3QgdmVyeSBnb29kLgojIEFub3RoZXIgd2F5IGlzIHRvIGRvIHRoZSBleGFjdCBCaW5vbWlhbCB0ZXN0OiBzZWUgP2Jpbm9tLnRlc3QgZm9yIGRldGFpbHMuCnByb3AudGVzdCh5LCBOLCBwLCBjb3JyZWN0PUZBTFNFKQpgYGAKCmBgYHtyIGNvaW5fdGVzdF9jb25jbHVzaW9uLCBlY2hvPUZBTFNFLCByZXN1bHRzPSJhc2lzIn0KY2F0KCJCYXNlZCBvbiB0aGUgcmVzdWx0LCAgIikKaWYgKCBwcm9wX3AgPCAuMDUgKSB7CiAgY2F0KCJ3ZSBjYW4gcmVqZWN0IHRoZSBudWxsIGh5cG90aGVzaXMgYXQgdGhlIGNvbnZlbnRpb25hbCBzaWduaWZpY2FuY2UgbGV2ZWwgKDUlKS4iKQp9IGVsc2UgewogIGNhdCgid2UgaGF2ZSBubyBlbm91Z2ggZXZpZGVuY2UgdG8gcmVqZWN0IHRoZSBudWxsIGh5cG90aGVzaXMgYXQgdGhlIGNvbnZlbnRpb25hbCBzaWduaWZpY2FuY2UgbGV2ZWwgKDUlKS4iKQp9CmBgYAoKIyMjIEFuYWx5dGljYWwgQmF5ZXNpYW4KCk5vdyBtb3ZlIG9udG8gYSBCYXlzaWFuIGFwcHJvYWNoLgoKSGVyZSB3ZSBmb2N1cyBvbiBvdXIgYWx0ZXJuYXRpdmUgaHlwb3RoZXNpcyB3aGljaCBzdWdnZXN0cyB0aGUgY29pbiBpcyBub3QgZmFpci4KVG8gZG8gQmF5ZXNpYW4gb25lIG11c3QgbGF5IG91dCBvbmUncyBleGFjdCBwcmlvciBvbiBiZWluZyAibm90IGZhaXIuIgpMZXQncyBmaXJzdCBjb25zaWRlciBhIGRpc2NyZXRlIHNldCBvZiBwb3NzaWJsZSAkXHRoZXRhJAoKYGBge3IgY29pbl9kaXNjcmV0ZV90aGV0YX0KKHRoZXRhIDwtIHNlcSgwLCAxLCBsZW5ndGgub3V0PTUpKQpgYGAKCndpdGggZXF1YWwgcHJvYmFiaWxpdHkKCmBgYHtyIGNvaW5fZGlzY3JldGVfcHJpb3J9Cih0aGV0YV9wcmlvciA8LSByZXAoMSwgbGVuZ3RoKHRoZXRhKSkgLyBsZW5ndGgodGhldGEpKSAgIyBEZW5zaXR5IGZ1bmN0aW9uIGZvciB0aGV0YS4KYGBgCgpUaGF0IGlzLAp3ZSBiZWxpZXZlICphIHByaW9yaSogdGhhdCB0aGVyZSBhcmUgaW4gdG90YWwgYHIgbGVuZ3RoKHRoZXRhKWAgcG9zc2libGUgdGhldGEgdmFsdWVzIGxpc3RlZCBhYm92ZSBmb3IgdGhlIG1vZGVsIHBhcmFtZXRlciAkcCQuCkV2ZXJ5IHZhbHVlIGlzIGVxdWFsbHkgcG9zc2libGUuCihPYnZpb3N1bHksIHRoZSBjb2luIGlzIG9ubHkgZmFpciBpZiAkXHRoZXRhID0gMC41JCB3aXRoIHByb2JhYmlsaXR5IDEuKQoKVGhlIHByb2JhYmlsaXR5IG1hc3MgZnVuY3Rpb24gb2YgYSBiaW5vbWlhbCBkaXN0cmlidXRpb24gY2FuIGJlIHdyaXR0ZW4gYXM6CgokJApQKHkpID0gQ15OX3kgXGNkb3QgcF55KDEtcClee04teX0uCiQkCgpIZXJlICRwJCBpcyBvdXIgbW9kZWwgcGFyYW1ldGVyIGFuZCBpdCBmb2xsb3dzIG91ciBkaXNjcmV0ZSBwcmlvciAkXHRoZXRhJC4KCkluIG9yZGVyIHRvIGRlcml2ZSB0aGUgY29tcGxldGUgcG9zdGVyaW9yICRQKFx0aGV0YXx5KSQgKHVzaW5nIHRoZSBCYXllcycgTGF3IGZyb20gZXF1YXRpb24gJFxlcXJlZntlcTpiYXllc2xhd30kKSBmb3Igb3VyIGFsdGVybmF0aXZlIG1vZGVsLAp3ZSBuZWVkIHRvIGNhbGN1bGF0ZSAkUCh5KSQgdGhlIG1hcmdpbmFsIHByb2JhYmlsaXR5IG9mIGRhdGEgYXQgdGhlIGRlbm9taW5hdG9yOgoKJCQKUCh5KSA9IFxzdW1fblAoeXxcdGhldGFfbilQKFx0aGV0YV9uKS4KJCQKCk5vdGUgdGhhdCB0aGUgYWJvdmUgaXMganVzdCBhIGRpc2NyZXRlIHZlcnNpb24gb2YgZXF1YXRpb24gJFxlcXJlZntlcTptYXJnaW5hbF9saWt9JC4KCkhlcmUgY29tZXMgdGhlIHJlc3VsdHM6CgpgYGB7ciBjb2luX2Rpc2NyZXRlX3NvbHV0aW9ufQojIFAoeXx0aGV0YSkgZm9yIGFsbCB0aGV0YS4gVGhlIGRhdGEgbGlrZWxpaG9vZC4KcF95X3RoZXRhIDwtIGNob29zZShOLCB5KSp0aGV0YV55KigxIC0gdGhldGEpXihOLXkpCgojIFAoeSkuIE1hcmdpbmFsIHByb2JhYmlsaXR5IG9mIHkuIFRoaXMgaXMgYmFzZWQgb24gdGhlIGxhdyBvZiB0b3RhbCBwcm9iYWJpbGl0eS4gCnBfeSA8LSBzdW0odGhldGFfcHJpb3IgKiBwX3lfdGhldGEpCgojIFAodGhldGF8eSkuIERlcml2ZWQgYnkgdGhlIEJheWVzJyBMYXcuCnBfdGhldGFfeSA8LSAocF95X3RoZXRhICogdGhldGFfcHJpb3IpIC8gcF95CgpkYXRhLmZyYW1lKHRoZXRhLCB0aGV0YV9wcmlvciwgcF95X3RoZXRhLCBwX3RoZXRhX3kpCmBgYAoKVGhlIHRhYmxlIGlzIGEgZnVsbCBhbmFseXRpY2FsIHNvbHV0aW9uIGZvciBvdXIgQmF5ZXNpYW4gbW9kZWwgdW5kZXIgdGhlIGFsdGVybmF0aXZlIGh5cG90aGVzaXMgZ2l2ZW4gdGhlIG9ic2VydmVkIGRhdGEgYW5kIG91ciBkaXNjcmV0ZSBwcmlvci4KClRoZSB0YWJsZSByZXZlYWxzIGhvdyBvdXIgbmFpdmUgcHJpb3IgaXMgc2hhcGVkIGJ5IHRoZSBkYXRhIG9ic2VydmVkLgpGb3IgZXhhbXBsZSwKb3JpZ2luYWxseSB3ZSB0aGluayB0aGVyZSBpcyBhIGByIHNjYWxlczo6cGVyY2VudCh0aGV0YV9wcmlvclsxXSlgIGNoYW5jZSBvZiAkXHRoZXRhID0gMCQgbWVhbmluZyB0aGF0IHRoZSBjb2luIGlzIGNvbXBsZXRlbHkgYmlhc2VkIHRvd2FyZCB0aGUgdGFpbC4KQnV0IHRoZSBkYXRhIHRlbGxzIHVzIHRoYXQgb3V0IG9mIGByIE5gIHRvc3NlcyB3ZSBnb3QgYHIgeWAgaGVhZHMuClNvIGluZGVlZCB0aGVyZSBpcyBubyBjaGFuY2UgdGhhdCAkXHRoZXRhID0gMCQhClNhbWUgbG9naWMgYXBwbGllcyB0byB0aGUgcHJpb3Igb2YgJFx0aGV0YSA9IDEkLgpUaGVpciBwb3N0ZXJpb3JzIGJvdGggYmVjb21lIHplcm8gYmFzZWQgb24gZXZpZGVuY2UgYnJvdWdodCBieSBkYXRhLgoKQW5vdGhlciBzdWJ0bGUgZmFjdCBpcyB0aGF0LAp0aGVyZSBpcyBubyB3YXkgZm9yIHVzIHRvIHN1cHBvcnQgYSB2YWx1ZSB0aGF0IGlzIG5vdCBjb3ZlcmVkIGJ5IHRoZSBwcmlvci4KU2luY2Ugb3VyIHByaW9yIG9ubHkgc3VwcG9ydCBgciBsZW5ndGgodGhldGEpYCBwb3NzaWJsZSB2YWx1ZXMsCm91ciBwb3N0ZXJpb3IgY2FuIG9ubHkgYWRhcHQgdG8gdGhlc2UgYHIgbGVuZ3RoKHRoZXRhKWAgYXMgd2VsbC4KVGhpcyBpcyBleGFjdGx5IHdoeSB3ZSdkIGxpa2UgdG8gcmVwbGFjZSB0aGUgZGlzY3JldGUgcHJpb3Igd2l0aCBhIGNvbnRpbm91cyBvbmUsIHVubGVzcyB3ZSBoYXZlIGEgdmVyeSBzdHJvbmcgcHJpb3IgdGhhdCBkaWN0YXRlcyB0aGUgZGlzY3JldGUgY2FzZSBpbnN0ZWFkLgoKQW1vbmcgYWxsIHRoZSBwb3NzaWJsZSBwYXJhbWV0ZXIgdmFsdWVzLAp0aGUgb25lIHdpdGggdGhlIGhpZ2hlc3QgcG9zdGVyaW9yIGlzIGNhbGxlZCB0aGUgKm1heGltdW0gYSBwb3N0ZXJpb3JpKiAoTUFQKSBlc3RpbWF0ZToKCmBgYHtyIGNvaW5fbWFwX2VzdH0KdGhldGFbd2hpY2gubWF4KHBfdGhldGFfeSldCmBgYAoKVXN1YWxseSB0aGUgQmF5ZXNpYW4gcG9pbnQgZXN0aW1hdGUgb2YgYSBwYXJhbWV0ZXIgaXMgaW5zdGVhZCB0aGUgZXhwZWN0ZWQgdmFsdWUgb2YgdGhlIHBvc3Rlcmlvci4KSW4gdGhpcyBjYXNlIG91ciBwb2ludCBlc3RpbWF0ZSB3aXRoIHRoZSBkaXNjcmV0ZSBwcmlvciB3aWxsIGJlOgoKYGBge3IgY29pbl9kaXNjcmV0ZV9wb2ludF9lc3R9CiMgQ2hlY2sgb3VyIHBvc3RlcmlvciBmb2xsb3dzIHRoZSBsYXcgb2YgcHJvYi4Kc3RvcGlmbm90KHN1bShwX3RoZXRhX3kpID09IDEpCgojIEJheWVzaWFuIHBvaW50IGVzdGltYXRlIHVuZGVyIHRoZSBhbHRlcm5hdGl2ZSBoeXBvdGhlc2lzLgpzdW0ocF90aGV0YV95ICogdGhldGEpCmBgYAoKd2hpY2ggaXMgaW50ZXJlc3RpbmdseSBxdWl0ZSBjbG9zZSB0byB0aGUgc2FtcGxlIG1lYW4gYHIgeS9OYC4KCkluZGVlZCwKaWYgb3VyIHByaW9yIGlzIGEgc3RhbmRhcmQgVW5pZm9ybSwKTUFQIGVzdGltYXRlIGlzIHRoZSBzYW1lIGFzIHRoZSBNTEUgZXN0aW1hdGUuCgpUbyBzZWUgdGhpcyBjbGVhcmx5LApsZXQncyBhc3N1bWUgYSBkaXNjcmV0ZSBwcmlvciBidXQgd2l0aCBtdWNoIG1vcmUgcG9zc2libGUgdmFsdWVzIGFuZCBwbG90IHRoZSBwcmlvciBhbG9uZyB3aXRoIHRoZSBwb3N0ZXJpb3I6CgpgYGB7ciBjb2luX3VwZGF0ZV9wbG90fQpwbG90X3VwZGF0ZSA8LSBmdW5jdGlvbih0aGV0YSwgcF90aGV0YSkgewogIHBfeV90aGV0YSA8LSBjaG9vc2UoTiwgeSkqdGhldGFeeSooMSAtIHRoZXRhKV4oTi15KQogIHBfeSA8LSBzdW0ocF90aGV0YSAqIHBfeV90aGV0YSkKICBwX3RoZXRhX3kgPC0gKHBfeV90aGV0YSAqIHBfdGhldGEpIC8gcF95CiAgCiAgcGxvdCh4PXRoZXRhLCB5PXBfdGhldGFfeSwgdHlwZT0ibCIsIGNvbD0icmVkIiwKICAgICAgIG1haW49c3ByaW50ZigiQmF5ZXNpYW4gVXBkYXRlIG9uIEJpbm9taWFsKCVzLCAlcykiLCBOLCBwKSwKICAgICAgIHhsYWI9ZXhwcmVzc2lvbih0aGV0YSksIHlsYWI9Ik1hc3MgKFByb2JhYmlsaXR5KSIpCiAgbGluZXMoeD10aGV0YSwgeT1wX3RoZXRhLCBjb2w9ImJsdWUiKQogIGFibGluZSh2PXkvTiwgbHR5PTIpCiAgdGV4dCh5L04sIHBfdGhldGFbMV0qLjgsIHBvcz00LCBzcHJpbnRmKCJPYnNlcnZlZCBwID0gJXMiLCByb3VuZCh5L04sIDIpKSkKICBsZWdlbmQoInRvcGxlZnQiLCBsZWdlbmQ9YygiUG9zdGVyaW9yIiwgIlByaW9yIiksCiAgICAgICAgIGNvbD1jKCJyZWQiLCAiYmx1ZSIpLCBsdHk9MSkKfQoKbWFueV90aGV0YSA8LSBzZXEoMCwgMSwgbGVuZ3RoLm91dD0xMDAwKQpwX3RoZXRhIDwtIHJlcCgxLCBsZW5ndGgobWFueV90aGV0YSkpIC8gbGVuZ3RoKG1hbnlfdGhldGEpCnBsb3RfdXBkYXRlKG1hbnlfdGhldGEsIHBfdGhldGEpCmBgYAoKVGhlIHBvc3RlcmlvciB3aWxsIGhhdmUgaXRzIG1heGltdW0gZXhhY3RseSBhdCB0aGUgc2FtcGxlIG1lYW4uCgpVbmlmb3JtIGRpc3RyaWJ1dGlvbiBpbiB0aGlzIGNhc2UgY2FuIGJlIHJlZ2FyZGVkIGFzIGEgZ2VuZXJhbCAqbm9uaW5mb3JtYXRpdmUqIG9yICpmbGF0KiBwcmlvci4KVW5kZXIgc3VjaCBwcmlvciBvdXIgdGVzdCBiZWNvbWVzOgoKJCQKXGJlZ2lue2FsaWduZWR9CkhfMCAmOiBZIFxzaW0gXG1ib3h7Qmlub21pYWx9KE49MTAsIHA9MC41KSwgXFwKSF8xICY6IFkgXHNpbSBcbWJveHtCaW5vbWlhbH0oTj0xMCwgcD1cdGhldGEpOyBcbWJveHsgfSBcdGhldGEgXHNpbSBcbWJveHtVbmlmb3JtfSgwLDEpLgpcZW5ke2FsaWduZWR9CiQkCgpUaGF0IGlzLAp3ZSBiZWxpZXZlIGEgcHJpb3JpIHRoZSBjb2luIGNhbiBiZSBvZiBhbnkga2luZCBvZiBiaWFzIHdpdGggZXF1YWwgcG9zc2liaWxpdHkuCgpPdXIgbWFyZ2luYWwgcHJvYmFiaWxpdHkgb2YgdGhlIG9ic2VydmVkIGRhdGEgZ2l2ZW4gdGhlIGFsdGVybmF0aXZlIGh5cG90aGVzaXMgYmVjb21lczpeW1RoaXMgaXMgYSBzcGVjaWFsIGludGVncmFsIGZ1bmN0aW9uIGNhbGxlZCB0aGUgW0JldGEgZnVuY3Rpb25dKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0JldGFfZnVuY3Rpb24pLl0KCiQkClxiZWdpbnthbGlnbmVkfQpQKHl8SF8xKSAKJj0gXGludF8wXjFDXzZeezEwfVx0aGV0YV42KDEgLSBcdGhldGEpXns0fWRcdGhldGEgXFwKJj0gQ182XnsxMH0gXHRpbWVzIFxmcmFjezYhXGNkb3Q0IX17KDEwKzEpIX0gXFwKJj0gMC4wOVxvdmVybGluZXswOX0uClxlbmR7YWxpZ25lZH0KJCQKCkluc3RlYWQgb2Ygc29sdmluZyBmb3IgdGhlIGFuYWx5dGljYWwgc29sdXRpb24gd2hpY2ggY291bGQgY2F1c2UgYSBoZWFkYWNoZSwKaGVyZSB3ZSB0YWtlIHRoZSBjaGFuY2UgdG8gZGVtbyBhIG51bWVyaWNhbCBzb2x1dGlvbiB1c2luZyBzaW11bGF0aW9uLgpXZSB3aWxsIGdlbmVyYXRlIHJhbmRvbSBkcmF3cyBvZiAkXHRoZXRhJCBmcm9tIGl0cyBwcmlvciAoYSBzdGFuZGFyZCBVbmlmb3JtKSBhbmQgY2FsY3VsYXRlIHRoZSByZXN1bHRpbmcgcHJvYmFiaWxpdHkgcHJvdmlkZWQgdGhhdCBvdXIgb2JzZXJ2ZWQgZGF0YSAkeSQgaXMgYHIgeWAuCgpgYGB7ciBjb2luX2gxX3VuaWZvcm19CnNldC5zZWVkKDc3NykKKHBfeV9oMWEgPC0gbWVhbihkYmlub20oeSwgTiwgcnVuaWYoMWU2KSkpKQpgYGAKCkFzIG9uZSBjYW4gc2VlLCAKZm9yIGEgbGFyZ2UgcmVwZXRpdGlvbnMsCnRoZSBudW1lcmljYWwgc29sdXRpb24gaXMgdmVyeSBjbG9zZSB0byB0aGUgYW5hbHl0aWNhbCBvbmUuClRoaXMgaXMgaW5kZWVkIHRoZSBjb25jZXB0IG9mIE1vbnRlIENhcmxvIG1ldGhvZCwKd2hpY2ggd2Ugd2lsbCBjb3ZlciBsYXRlciBpbiB0aGlzIHR1dG9yaWFsLl5bRm9yIHRoaXMgcGFydGljdWxhciBleGFtcGxlLCBhbm90aGVyIG1heWJlIGV2ZW4gc21hcnRlciB3YXkgdG8gYXBwcm94aW1hdGUgdGhlIHNvbHV0aW9uIGlzIHRvIHVzZSBhIGRpc2NyZXRlIFVuaWZvcm0gdGhhdCB0YWtlcyBtYW55IG1hbnkgcG9zc2libGUgdmFsdWVzLiBUaGVuIHdlIGNhbiBqdXN0IGNhbGN1bGF0ZSB0aGUgZGlzY3JldGUgdmVyc2lvbiBvZiB0aGUgaW50ZWdyYWwsIGFzIGluIHRoZSBmaXJzdCBwYXJ0IG9mIHRoZSBzZWN0aW9uLCB3aGljaCB3aWxsIGFsc28gZ2l2ZSB2ZXJ5IGdvb2QgYXBwcm94aW1hdGlvbnMuXQoKIyMjIEdlbmVyYWxpemF0aW9uIHRvIExvZ2lzdGljIFJlZ3Jlc3Npb24KCkhvdyBpcyB0aGlzIHNpbXBsZSBCaW5vbWlhbCBwcm9ibGVtIGV2ZW4gcmVsZXZhbnQgdG8gb3VyIHJlYWwgd29ybGQgbW9kZWxpbmc/CgpJbmRlZWQgdGhlIHByb2JsZW0gY2FuIGJlIHJlLWZvcm11bGF0ZWQgYXMgYSBsb2dpc3RpYyByZWdyZXNzaW9uIHdpdGggb25seSBhIGNvbnN0YW50IHRlcm0gYXMgdGhlIHByZWRpY3RvciB2YXJpYWJsZS4KRWFjaCB0cmlhbCBpbiBhIEJpbm1vaWFsIGlzIGp1c3QgYSBbQmVybm91bGxpIHByb2Nlc3NdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0Jlcm5vdWxsaV9kaXN0cmlidXRpb24pLgpDb25zaWRlciBlYWNoIEJlcm5vdWxsaSBhcyBvdXIgb3V0Y29tZSBvYnNlcnZhdGlvbiB3aXRob3V0IGFueSBvdGhlciBleHBsYW5hdG9yeSB2YXJpYWJsZSwKYSAkXG1ib3h7Qmlub21pYWx9KE4sIHApJCBtZWFucyB3ZSBoYXZlICROJCByZWNvcmRzIG9mIG9ic2VydmF0aW9uLgpUaGlzIGlzIGEgYmluYXJ5IGNsYXNzaWZpY2F0aW9uIHByb2JsZW0gbW9kZWxlZCBieSBsb2dpc3RpYyByZWdyZXNzaW9uLgpJbiBSIHdlIGNhbiB1c2UgdGhlIGJ1aWx0LWluIGBnbG1gIGZ1bmN0aW9uIHRvIGVzdGltYXRlIHRoZSBtb2RlbDoKCmBgYHtyIGNvaW5fY29uc3RhbnRfbG9naXN0aWN9CiMgQ3JlYXRlIHJhdyBkYXRhLiBCaW5vbShOLCBwKSBpcyBzaW1wbHkgTiByZWNvcmRzIG9mIEJlcm5vdWxsaShwKS4KeWwgPC0gbnVtZXJpYyhOKQp5bFsxOnldIDwtIDEKZCA8LSBkYXRhLmZyYW1lKHk9eWwpCgojIENvbnN0YW50LW9ubHkgbG9naXQgbW9kZWwuCmNvZWYoZ2xtKHkgfiAxLCBkYXRhPWQsIGZhbWlseT1iaW5vbWlhbCkpCmBgYAoKVGhlIE1MRSBlc3RpbWF0b3Igb2YgdGhlIGNvZWZmaWNpZW50IG9uIGNvbnN0YW50IHRlcm0gaXMgdGhlICpsb2cgb2RkcyByYXRpbyogb2YgdGhlIG9ic2VydmVkIGRhdGEuClRvIGNvbmZpcm0gdGhpczoKCmBgYHtyIGNvaW5fbG9nX29kZHN9Cm9kZHMgPC0gKHkvTikgLyAoMSAtIHkvTikKbG9nKG9kZHMpCmBgYAoKQW5kIHdlIGNhbiBvZiBjb3Vyc2Ugc29sdmUgZm9yIHRoZSAkcCQgZnJvbSBvZGRzIHJhdGlvOgoKYGBge3IgY29pbl9sb2dpdF9tbGV9Cm9kZHMgLyAoMSArIG9kZHMpCmBgYAoKd2hpY2ggaXMganVzdCB0aGUgc2FtcGxlIG1lYW4uCgojIyBCYXllcyBGYWN0b3IKCkluIHRoZSBwcmV2aW91cyBzZWN0aW9uIHdlIGV4YW1pbmUgYSBCYXllc2lhbiBtb2RlbCB1bmRlciB0aGUgYWx0ZXJuYXRpdmUgbW9kZWwuCkJ1dCB3ZSBkaWRuJ3QgY29tZSB0byBhbnkgY29uY2x1c2lvbiBhYm91dCB0aGUgY29tcGFyc2lvbiBiZXR3ZWVuIHRoZSBhbHRlcm5hdGl2ZSBhbmQgdGhlIG51bGwgbW9kZWwuCkhvdyBkbyBCYXllc2lhbnMgbG9vayBhdCB0aGUgaHlwb3RoZXNpcyB0ZXN0aW5nIHRhc2s/CgpGaXJzdCBvZiBhbGwsIEJheWVzaWFucyBkZXBpY3QgYmVsaWVmIGFzIGEgZGlzdHJpYnV0aW9uIHJhdGhlciB0aGFuIGEgY29uc3RhbnQuClNvIHRoZSBoeXBvdGhlc2lzIGl0c2VsZiBjYW4gYmVjb21lIHByb2JhYmlsaXR5IGRpc3RyaWJ1dGlvbi4KQWdhaW4gYnkgQmF5ZXMnIExhdzoKCiQkClxiZWdpbnthbGlnbmVkfQpQKEhfMCB8IHkpID0gXGZyYWN7UCh5fEhfMCkgXGNkb3QgUChIXzApfXtQKHkpfSwgXFwKUChIXzEgfCB5KSA9IFxmcmFje1AoeXxIXzEpIFxjZG90IFAoSF8xKX17UCh5KX0uClxlbmR7YWxpZ25lZH0KJCQKCkluIHNvbWUgY2FzZXMgd2UgbWF5IGFsc28gaGF2ZSAkUChIXzApICsgUChIXzEpID0gMSQsCndoaWNoIGlzIG5vdCBuZWNlc3NhcnkgaW4gYSBtb3JlIGdlbmVyYWwgc2V0dXAuCgpCYXNlZCBvbiB0aGUgYWJvdmUgZm9ybXVsYSwKd2UgY2FuIGNhbGN1bGF0ZSB0aGUgKnBvc3RlcmlvciBvZGRzKiBvZiB0d28gY29tcGV0aW5nIGJlbGllZnMgKG9yIG1vZGVscykgYXM6CgokJApcYmVnaW57ZXF1YXRpb259Clx1bmRlcmJyYWNle1xmcmFje1AoSF8wfHkpfXtQKEhfMXx5KX19X1x0ZXh0e3Bvc3RlcmlvciBvZGRzfSA9IApcdW5kZXJicmFjZXtcZnJhY3tQKHl8SF8wKX17UCh5fEhfMSl9fV9cdGV4dHtCYXllcyBmYWN0b3J9IFxjZG90Clx1bmRlcmJyYWNle1xmcmFje1AoSF8wKX17UChIXzEpfX1fXHRleHR7cHJpb3Igb2Rkc30uClxlbmR7ZXF1YXRpb259CiQkCgpCYXllcyBmYWN0b3IgaGVyZSBpcyBkZWZpbmVkIGFzIHRoZSByYXRpbyBvZiBwb3N0ZXJpb3Igb2RkcyB0byBwcmlvciBvZGRzIGZvciB0d28gY29tcGV0aW5nIG1vZGVscy4KSXQgY2FuIGJlIHZpZXdlZCBhcyAqYW4gdXBkYXRlKiBvZiBtb2RlbCBwcmVmZXJlbmNlIGZyb20gcHJpb3IgdG8gcG9zdGVyaW9yIGdpdmVuIHRoZSBpbmZvcm1hdGlvbiBwcm92aWRlZCBieSB0aGUgZGF0YS4KT25lIGNhbiByZWdhcmQgQmF5ZXMgZmFjdG9yIGFzIHRoZSBjb3VudGVycGFydCBvZiB0aGUgZnJlcXVlbnRpc3QgcC12YWx1ZSBpbiB0aGVpciBwaGlsb3NvcGh5IGFib3V0IGh5cG90aGVzaXMgdGVzdGluZy4KVGhlIGhpZ2hlciB0aGUgZmFjdG9yLAp0aGUgbW9yZSB0aGUgcHJpb3IgYmVsaWVmIHNoaWZ0cyB0b3dhcmQgdGhlIHBvc3RlcmlvciwKb3IgZXF1aXZhbGVudGx5IHRoZSBsYXJnZXIgdGhlIHVwZGF0ZS4KQmF5ZXMgZmFjdG9yIGlzIGEgbWVhc3VyZSBvZiBjaGFuZ2UgaW4gcmVsYXRpdmUgYmVsaWVmIGFmdGVyIG9ic2VydmluZyB0aGUgZGF0YS4KClVzdWFsbHkgaXQgaXMgbmF0dXJhbCB0byBzZXQgdGhlIHByaW9yIG9kZHMgJFxmcmFje1AoSF8wKX17UChIXzEpfSA9IDEkIHRvIGV4cHJlc3MgbmV1dHJhbGl0eSBiZWZvcmUgb2JzZXJ2aW5nIHRoZSBkYXRhIGFzIGxvbmcgYXMgbm8gc3Ryb25nIHByaW9yIHByZWZlcmVuY2UgcHJlc2VudHMuCkNhbGN1bGF0aW5nIHRoZSBCYXllcyBmYWN0b3IgdGhlbiBpbnZvbHZlcyBkZXRlcm1pbmF0aW9uIG9mIHRoZSBtYXJnaW5hbCBsaWtlbGlob29kIGNvbmRpdGlvbmVkIG9uIG1vZGVsOiAkUCh5fEhfMCkkIGFuZCAkUCh5fEhfMSkkLgoKVG8gYmUgc3BlY2lmaWMsCgokJApcYmVnaW57ZXF1YXRpb259IFxsYWJlbHtlcTptYXJnaW5hbF9saWtfaDB9ClAoeXxIXzApCj0gXGludCBQKHl8XHRoZXRhLCBIXzApUChcdGhldGF8SF8wKWRcdGhldGEKPSBFX3twcmlvcn1cYmlnWyBQKHl8XHRoZXRhLCBIXzApIFxiaWddLCAKXGVuZHtlcXVhdGlvbn0KJCQKCndoZXJlICRcdGhldGEkIGlzIHRoZSBwYXJhbWV0ZXIgdW5kZXIgaHlwb3RoZXNpcyAkSF8wJC4KVGhpcyBpcyBleGFjdGx5IHRoZSBtYXJnaW5hbCBwcm9iYWJpbGl0eSBvZiBkYXRhIHVuZGVyICRIXzAkLAp3ZSBhcmUgZXNzZW50aWFsbHkgcmV3cml0aW5nIGVxdWF0aW9uICRcZXFyZWZ7ZXE6bWFyZ2luYWxfbGlrfSQgdG8gYmUgY29uZGl0aW9uZWQgb24gdGhlIGdpdmVuIGh5cG90aGVzaXMuCgpSZWFkZXJzIHNob3VsZCBub3QgY29uZnVzZSAkUCh5fEhfMCkkIHdpdGggJFAoeXxcdGhldGEpJC4KVGhlIGxhdHRlciBpcyB0aGUgZGF0YSBsaWtlbGlob29kIGdpdmVuIGEgc3BlY2lmaWMgbW9kZWwgcGFyYW1ldGVyIChpbXBsaWNpdGx5IGZvbGxvd2luZyBhIG1vZGVsIGh5cG90aGVzaXMpLAp3aGlsZSB0aGUgZm9ybWVyIGlzIHRoZSBkYXRhIGxpa2VsaWhvb2QgZ2l2ZW4gdGhlIG1vZGVsIGh5cG90aGVzaXMsCm9yIHRoZSBtYXJnaW5hbCBsaWtlbGlob29kIHdpdGggY29uc2lkZXJhdGlvbiBvZiBhbGwgcG9zc2libGUgcGFyYW1ldGVyIHZhbHVlcyB1bmRlciB0aGF0IGh5cG90aGVzaXMuCgpCYWNrIHRvIG91ciBiaW5vbWlhbCBleGFtcGxlLgpXZSBjYW4gbm93IGNhbGN1bGF0ZSB0aGUgQmF5ZXMgZmFjdG9yICRcZnJhY3tQKHl8SF8wKX17UCh5fEhfMSl9JCBhbmFseXRpY2FsbHkuCgpNYXJnaW5hbCBwcm9iYWJpbGl0eSB1bmRlciB0aGUgbnVsbCAkUCh5fEhfMCkkIGlzCgpgYGB7ciBjb2luX3BfeV9oMH0KKHBfeV9oMCA8LSBjaG9vc2UoTiwgeSkqcF55KigxIC0gcCleKE4teSkgKSAgIyBPciBlcXVpdmFsZW50bHk6IGRiaW5vbSh5LCBOLCBwKQpgYGAKCkZvciBtYXJnaW5hbCBwcm9iYWJpbGl0eSB1bmRlciB0aGUgYWx0ZXJuYXRpdmUgJFAoeXxIXzEpJCwKd2l0aCB0aGUgZGlzY3JldGUgcHJpb3IgaXQgaXM6CgpgYGB7ciBjb2luX3BfeV9oMV9kaXNjcmV0ZX0KKHBfeV9oMWQgPC0gc3VtKHRoZXRhX3ByaW9yICogKGNob29zZShOLCB5KSp0aGV0YV55KigxIC0gdGhldGEpXihOLXkpKSkpCmBgYAoKb3Igd2l0aCBhIGNvbnRpbnVvdXMgcHJpb3IgaXQgaXM6CgpgYGB7ciBjb2luX3BfeV9oMV91bmlmb3JtfQoocF95X2gxYyA8LSBjaG9vc2UoTiwgeSkgKiBmYWN0b3JpYWwoeSkqZmFjdG9yaWFsKE4teSkvZmFjdG9yaWFsKE4gKyAxKSkKYGBgCgpUaGUgQmF5ZXMgZmFjdG9yIGlzIGhlbmNlOgoKYGBge3IgY29pbl9iZl9kaXNjcmV0ZX0KcF95X2gwIC8gcF95X2gxZApgYGAKCmZvciB0aGUgZGlzY3JldGUgcHJpb3Igb3IKCmBgYHtyIGNvaW5fYmZfdW5pZm9ybX0KcF95X2gwIC8gcF95X2gxYwpgYGAKCmZvciBhIFVuaWZvcm0gcHJpb3IuCgpUaGUgbGFyZ2VyIHRoZSBCYXllcyBmYWN0b3IgdGhlIG1vcmUgc3VwcG9ydGluZyBldmlkZW5jZSBmb3IgdGhlIG51bWVyYXRvciBtb2RlbCBhZ2FpbnN0IHRoZSBkZW5vbWluYXRvciBtb2RlbC4KClRoZSBwYWNrYWdlIGBCYXllc0ZhY3RvcmAgW0BqZWZmZXJ5MjAxOGJmXSBpbiBSIGltcGxlbWVudHMgYSB2YXJpZXR5IG9mIGRpZmZlcmVudCBoeXBvdGhlc2lzIHRlc3RpbmcgdG9vbHMgYmFzZWQgb24gdGhlIHVzYWdlIG9mIEJheWVzIGZhY3Rvci4KCmBgYHtyIGNvaW5faW1wb3J0LCByZXN1bHRzPSJoaWRlIn0KbGlicmFyeShCYXllc0ZhY3RvcikKYGBgCgpUaGVyZSBpcyBhIGZ1bmN0aW9uIGBwcm9wb3J0aW9uQkZgIGZvciBhIHByb3BvcnRpb24gdGVzdCBhdCBvdXIgZGlzcG9zYWw6CgpgYGB7ciBjb2luX2JmX2xvZ2lzdGljX3ByaW9yfQpCYXllc0ZhY3Rvcjo6cHJvcG9ydGlvbkJGKHksIE4sIHA9LjUpCmBgYAoKT25lIG1heSBmaW5kIHRoYXQgdGhlIEJGIHZhbHVlIGlzIGEgYml0IG9mZiBmcm9tIG91ciBjYWxjdWxhdGlvbi4KVGhpcyBpcyBiZWNhdXNlIGBCYXllc0ZhY3Rvcjo6cHJvcG9ydGlvbkJGYCB1c2VzIGEgZGlmZmVyZW50IHByaW9yIG9uIHRoZSBkZWZhdWx0IGFsdGVybmF0aXZlIG1vZGVsLgpCYXNlZCBvbiB0aGUgZG9jdW1lbnQgKGA/cHJvcG9ydGlvbkJGYCkgd2UgcmVhbGl6ZSB0aGF0IHRoZSBkZWZhdWx0IHByaW9yIGlzIGluZGVlZCBhIGxvZ2lzdGljIGRpc3RyaWJ1dGlvbiwKd2hpbGUgb3VycyBpcyBzaW1wbHkgYSBzdGFuZGFyZCBVbmlmb3JtLgoKKipOdW1lcmljYWwgQmF5ZXNpYW4gb24gTWFyZ2luYWwgTGlrZWxpaG9vZCoqCgpJbiB0aGlzIHNpbXBsZSBleGFtcGxlIHdlIGFyZSBhYmxlIHRvIGNhbGN1bGF0ZSB0aGUgbWFyZ2luYWwgcHJvYmFiaWxpdHkgYW5kIGhlbmNlIHRoZSBCYXllcyBmYWN0b3IgYW5hbHl0aWNhbGx5LgpJbiBhIG1vcmUgZ2VuZXJhbCBzZXR1cCwKaG93ZXZlciwKYW5hbHl0aWNhbCBzb2x1dGlvbiBtYXkgd2VsbCBub3QgZXhpc3Qgb3IgbWF5IG5vdCBiZSB0cmFjdGFibGUuCkluIHN1Y2ggY2FzZSB1c3VhbGx5IHdlIG5lZWQgdG8gcmVzb3J0IHRvIG51bWVyaWNhbCBzb2x1dGlvbnMuCkluIHRoZSBsaXRlcmF0dXJlIHRoZXJlIGFyZSBxdWl0ZSBzb21lIGRpZmZlcmVudCBhcHByb2FjaGVzIHByb3Bvc2VkIHRvIG92ZXJjb21lIHRoZSBpc3N1ZS4KT3V0IG9mIGFsbCB0aG9zZSBwcm9wb3NhbHMsCmEgTW9udGUgQ2FybG8gbWV0aG9kIGNhbGxlZCAqYnJpZGdlIHNhbXBsaW5nKiBiZWNvbWVzIHRoZSBtb3N0IHByb21pc2luZyBvbmUgYW5kIGlzIG5vdyB0aGUgZ28tdG8gbWV0aG9kIGZvciBudW1lcmljYWwgc29sdXRpb24gZm9yIGVzdGltYXRpb24gb2YgbWFyZ2luYWwgbGlrZWxpaG9vZC4KCiMjIFRoZSBKWlMgUHJpb3IgZm9yIE1lYW4gVGVzdAoKSXQncyB0aW1lIHRvIHJldmlzaXQgb3VyIHZlcnkgZmlyc3QgZXhhbXBsZSBpbiB0aGlzIHR1dG9yaWFsLgoKUmVtZW1iZXIgdGhhdCBpbiB0aGUgZXhhbXBsZSB3ZSBoYXZlIHRoZSBmb2xsb3dpbmcgdGVzdCBmb3IgcG9wdWxhdGlvbiBtZWFuOgoKYHIgc3ByaW50ZigiJCRIXzA6IFxcbXUgPSAlcywgXFxcXCBIXzE6IFxcbXUgXFxuZXEgJXMuJCQiLCBoMF9tZWFuLCBoMF9tZWFuKWAKCmdpdmVuIGEgc2FtcGxlIGRhdGFzZXQgcmFuZG9tZ2x5IGRyYXduIGZyb20gYSAodW5rbm93bikgQmV0YSBkaXN0cmlidXRpb24uCgpJbiBvcmRlciB0byBwcm9jZWVkIHdpdGggYSBCYXllc2lhbiBhcHByb2FjaCwKd2UgbmVlZCB0byBleHBsaWNpdGx5IHNldHVwIG91ciBwcmlvci4KV2Ugd2lsbCBpbnRyb2R1Y2UgdGhlIHdlbGwta25vd24gSmVmZnJleXMtWmVsbG5lci1TaW93IChKWlMpIHByaW9yIGZvciBCYXlzaWFuIGh5cG90aGVzaXMgdGVzdGluZyBvbiBwb3B1bGF0aW9uIG1lYW4uCgpUaGVyZSBhcmUgaW5kZWVkIGRpZmZlcmVudCB3YXlzIG9mIGRvaW5nIEJheWVzaWFuIG9uIGEgaHlwb3RoZXNpcyB0ZXN0aW5nIHRhc2suClJlc2VhcmNoIGluIHRoaXMgZmllbGQgaXMgc3RpbGwgYWN0aXZlIGFuZCBldm9sdmluZy4KSW4gdGhpcyB0dXRvcmlhbCB3ZSB3aWxsIG9ubHkgd2Fsay10aHJvdWdoIHRoZSBjbGFzc2ljYWwgb25lIHdpdGggSlpTIHByaW9yIGFzIGEgYmFzZWxpbmUgYXBwcm9hY2guCgpUbyByZS1mb3JtdWxhdGUgdGhlIG9uZS1zYW1wbGUgdGVzdCwKb25lIG1heSByZXdyaXRlIGl0IGFzIHNvbWV0aGluZyBsaWtlOgoKJCQKXGJlZ2lue2FsaWdufQpIXzAgJjogXG11ID0gMCwgXFwKSF8xICY6IFxtdSBcc2ltIFxtYm94e05vcm1hbH0oMCwgXHNpZ21hX3tcbXV9XjIpLApcZW5ke2FsaWdufQokJAoKYWxvbmcgd2l0aCBhbm90aGVyIHByaW9yIG9uIHRoZSBwb3B1bGF0aW9uIHN0YW5kYXJkIGRldmlhdGlvbiAkXHNpZ21hJC4KCkhlcmUgYSBub25pbmZvcm1hdGl2ZSBwcmlvciAoaGVuY2UgdGhlIG1vc3Qgb2JqZWN0aXZlKSBvbiAkXHNpZ21hX3tcbXV9XjIkIHdpbGwgdGVjaG5pY2FsbHkgbWVhbiB0byBzZXQgJFxzaWdtYV97XG11fV4yID0gXGluZnR5JC4KVGhpcyBpcyBiZWNhdXNlIHRoZSBsYXJnZXIgdGhlIHZhcmlhbmNlIHRoZSBtb3JlIHRoZSB1bmNlcnRhaW50eSBhYm91dCB0aGUgZGlzdHJpYnV0aW9uLgpJbnR1aXRpdmVseSwKdGhlIGxhcmdlciB0aGUgdmFyaWFuY2UgdGhlIG1vcmUgdGhlIHByaW9yIHdpbGwgYmUgc2hpZnRlZCB0b3dhcmQgdGhlIHBvc3RlcmlvciBieSBkYXRhLgpCdXQgbWF0aGVtYXRpY2FsbHkgc2V0dGluZyBpdCB0byBpbmZpbml0eSB3aWxsIHJlc3VsdCBpbiB1bmJvdW5kZWQgc3VwcG9ydCBmb3IgdGhlIG51bGwgbW9kZWwgYXMgc2FtcGxlIHNpemUgZ3Jvd3MsCndoaWNoIGluIHR1cm4gbWFrZXMgdGhlIHRlc3QgdXNlbGVzcy4KVGhlIGZhY3QgaXMgY2FsbGVkIHRoZSBKZWZmcmV5cy1MaW5kbGV5IHBhcmFkb3ggaW4gdGhlIGxpdGVyYXR1cmUuIApUbyB3b3JrLWFyb3VuZCB0aGlzLAppdCBpcyBwcm9wb3NlZCB0byB0ZXN0IHRoZSAqc3RhbmRhcmRpemVkIGVmZmVjdCBzaXplKiBkZWZpbmVkIGFzOgoKJCQKXGRlbHRhIFxlcXVpdiBcZnJhY3tcbXV9e1xzaWdtYX0uCiQkCgpUaGUgdGVzdCB0aGVuIGJlY29tZXM6CgokJApcYmVnaW57YWxpZ259CkhfMCAmOiBcZGVsdGEgPSAwLCBcXApIXzEgJjogXGRlbHRhIFxzaW0gXG1ib3h7Tm9ybWFsfSgwLCBcc2lnbWFfe1xkZWx0YX1eMikuClxlbmR7YWxpZ259CiQkCgpXaXRoIGEgaW52ZXJzZS1jaGktc3F1YXJlZCBwcmlvciBvbiAkXHNpZ21hX3tcZGVsdGF9XjIkIGl0IGlzIGVmZmVjdGl2ZWx5IHNheWluZzpeW1RoZSBbaW52ZXJzZS1jaGktc3F1YXJlZF0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvSW52ZXJzZS1jaGktc3F1YXJlZF9kaXN0cmlidXRpb24pIGlzIGNvbW1vbmx5IHVzZWQgYXMgYSBjaG9pY2Ugb2YgcHJpb3IgZm9yIHVua25vd24gdmFyaWFuY2UgaW4gQmF5ZXNpYW4gbGl0ZXJhdHVyZS5dCgokJApcYmVnaW57YWxpZ259CkhfMCAmOiBcZGVsdGEgPSAwLCBcXApIXzEgJjogXGRlbHRhIFxzaW0gciBcY2RvdCBcbWJveHtDYXVjaHl9LApcZW5ke2FsaWdufQokJAoKd2hlcmUgJHIkIGlzIGEgc2NhbGUgZmFjdG9yIHRvIGNvbnRyb2wgdGhlIGV4cGVjdGVkIHNpemUgb2YgdGhlIGVmZmVjdCBhIHByaW9yaS4gCgpKWlMgZnVydGhlciBhc3N1bWVzIHRoZSBwcmlvciBvbiAkXHNpZ21hJCB0byBiZSBub25pbmZvcm1hdGl2ZSBhcyB3ZWxsOgokUChcc2lnbWFeMikgXHByb3B0byBcZnJhY3sxfXtcc2lnbWFeMn0kLiAoU2VlIFtKZWZmcmV5cycgcHJpb3JdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0plZmZyZXlzX3ByaW9yKSBmb3IgbW9yZSB0ZWNobmljYWwgZGV0YWlscy4pCgpgQmF5ZXNGYWN0b3JgIGltcGxlbWVudHMgZXhhY3RseSB0aGUgSlpTIHByaW9yIGZvciBvbmUtc2FtcGxlIHRlc3Rpbmc6CgpgYGB7ciBzaW1wbGVfZGF0YV90dGVzdF9iZjEwfQpCYXllc0ZhY3Rvcjo6dHRlc3RCRihYMSwgbXU9aDBfbWVhbiwgcnNjYWxlPTEpICAjIHJzY2FsZSA9IDEgZm9yIHN0YW5kYXJkIENhdWNoeS4KCiMgT3IgZXF1aXZhbGVudGx5OgojIEJheWVzRmFjdG9yOjp0dGVzdEJGKFgxIC0gaDBfbWVhbiwgbXU9MCwgcnNjYWxlPTEpCmBgYAoKYEJheWVzRmFjdG9yOjp0dGVzdEJGYCBieSBkZWZhdWx0IHJlcG9ydHMgdGhlIEJheWVzIGZhY3RvciBmb3IgdGhlIGFsdGVybmF0aXZlIGFnYWluc3QgdGhlIG51bGwuClRoYXQgaXMsIAp0aGUgbnVtZXJhdG9yIG1vZGVsIGlzIHRoZSBhbHRlcm5hdGl2ZSBhbmQgdGhlIGRlbm9taW5hdG9yIHRoZSBudWxsLgoKT3Igd2UgdGFrZSB0aGUgcmVjaXByb2FsIG9mIHRoZSB0ZXN0OgoKYGBge3Igc2ltcGxlX2RhdGFfdHRlc3RfYmYwMX0KYmZ0ZXN0XzAxIDwtIDEgLyBCYXllc0ZhY3Rvcjo6dHRlc3RCRihYMSwgbXU9aDBfbWVhbiwgcnNjYWxlPTEpCmV4cChiZnRlc3RfMDFAYmF5ZXNGYWN0b3IkYmYpICAjIFRha2UgZXhwb3RlbnRpYWwgc2luY2UgdGhlIHJhdyBudW1iZXIgaXMgaW4gbG9nLgpgYGAKCkpaUyBpcyBub3QgdGhlIG9ubHkgcHJpb3IgcHJvcG9zZWQgaW4gdGhlIGxpdGVyYXR1cmUgYnV0IHN1cmVseSBpcyBhIGNsYXNzaWNhbCBvbmUuCkl0IGFsc28gY29tZXMgd2l0aCBhIGNsZWFyIGFkdmFudGFnZSBiYWNrIGluIHRoZSBvbGQgdGltZToKaXQgaGFzIGEgY2xvc2VkLWZvcm0gKHRob3VnaCB0ZWRpb3VzKSBzb2x1dGlvbi4KSW4gcmVhbGl0eSwKaG93ZXZlciwKY2xvc2VkLWZvcm0gc29sdXRpb24gZG9lcyBub3QgZXhpc3QgZm9yIG1vc3Qgb2YgdGhlIHByb2JhYmxpc3RpYyBtb2RlbHMsIGV2ZW4gYSBtb2RlcmF0ZWx5IHBhcmFtZXRlcml6ZWQgb25lLgpTbyB0aGUgbWFyZ2luYWwgbGlrZWxpaG9vZCBjYW4gbm90IGJlIGNhbGN1bGF0ZWQgYW5hbHl0aWNhbGx5LgpJbiBzdWNoIGNhc2Ugd2UgbmVlZCB0byByZXNvcnQgdG8gbnVtZXJpY2FsIG1ldGhvZC4KSW5kZWVkLApldmVuIGZvciB0aGUgY2xvc2VkLWZvcm0gaW50ZWdyYWwgaW4gdGhpcyBwcm9ibGVtLAp3ZSBuZWVkIHRvIHVzZSBhbiBhcHByb3hpbWF0aW9uIG1ldGhvZCBjYWxsZWQgW0dhdXNzaWFuIHF1YWRyYXR1cmVdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0dhdXNzaWFuX3F1YWRyYXR1cmUpIHRvIHNvbHZlIGZvciBpdC4KCk90aGVyIHRoYW4gY2FsY3VsYXRpbmcgdGhlIG1hcmdpbmFsIHByb2JhYmlsaXR5LAp3ZSBjYW4gYWxzbyB1c2UgYEJheWVzRmFjdG9yYCB0byBnZW5lcmF0ZSBwb3N0ZXJpb3IgcGFyYW1ldGVyIHNhbXBsZXMgdXNpbmcgTWFya292IGNoYWluIE1vbnRlIENhcmxvIChNQ01DKSBtZXRob2Q6CgpgYGB7ciBzaW1wbGVfZGF0YV90dGVzdF9iZl9tY21jLCBtZXNzYWdlPUZBTFNFfQpiZnRlc3RfbWNtY19zYW1wbGVzIDwtIEJheWVzRmFjdG9yOjp0dGVzdEJGKFgxLCBtdT1oMF9tZWFuLCByc2NhbGU9MSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcG9zdGVyaW9yPVRSVUUsIGl0ZXJhdGlvbnM9MTAwMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcm9ncmVzcz1GQUxTRSkKaGVhZChiZnRlc3RfbWNtY19zYW1wbGVzKQoKIyBFdmVuIG1vcmUsIHRvIHBsb3Qgc2FtcGxlIGRpc3RyaWJ1dGlvbiBhbmQgdHJhY2U6CiMgcGxvdChiZnRlc3RfbWNtY19zYW1wbGVzWywxOjJdKQpgYGAKCkFzIE1DTUMgaGFzIGJlY29tZSB0aGUgZG9taW5hbnQgYXBwcm9hY2ggdG8gc29sdmUgZm9yIGEgQmF5ZXNpYW4gbW9kZWwsCndlIHdpbGwgZGVlcC1kaXZlIGludG8gaXQgaW4gdGhlIG5leHQgc2VjdGlvbi4KCiMgTWFya292IENoYWluIE1vbnRlIENhcmxvCgpJbiBtb3N0IGFwcGxpY2F0aW9ucyB0aGUgcG9zdGVyaW9yIGRlbnNpdHkgY2FuIG5vdCBiZSBkZXJpdmVkIGFuYWx5dGljYWxseSwKd2UgbmVlZCB0byByZXNvcnQgdG8gc2ltdWxhdGlvbiBtZXRob2QgdG8gbnVtZXJpY2FsbHkgYXBwcm94aW1hdGUgdGhlIHNvbHV0aW9uLgpUaGlzIGlzIHdoZXJlIE1DTUMgY29tZXMgaW4gaGFuZHkuCgpUaGUgZ29hbCBvZiBNQ01DIGlzIHRvIGdlbmVyYXRlIHJhbmRvbSBzYW1wbGVzIHdpdGhvdXQga25vd2luZyB0aGUgZXhhY3QgcHJvYmFiaWxpdHkgZGlzdHJpYnV0aW9uIGZ1bmN0aW9uIG9mIHRoZSB0YXJnZXQgZGlzdHJpYnV0aW9uLS1UaGlzIGlzIHRoZSBNYXJrb3YgY2hhaW4gcGFydC4KVGhlbiB3ZSBjYW4gY2FsY3VsYXRlIG91ciBlc3RpbWF0b3Igb2YgaW50ZXJlc3QgYmFzZWQgb24gdGhlIHJhbmRvbSBzYW1wbGVzLS10aGlzIGlzIHRoZSBNb250ZSBDYXJsbyBwYXJ0LgoKVGhlIGlkZWEgaXMgdGhhdCBpZiB3ZSBjYW4gY29uc3RydWN0IGEgW01hcmtvdiBjaGFpbl0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvTWFya292X2NoYWluKSB3aXRoIHRyYW5zaXRpb24gcHJvYmFiaWxpdGllcyB0aGF0IGNvbnZlcmdlcyB0byBlcXVpbGlicml1bSBwcm9iYWJpbGl0aWVzIHRoYXQgbWF0Y2ggdGhlIHRhcmdldCBkaXN0cmlidXRpb24sIAp3ZSBjYW4gZWZmZWN0aXZlbHkgZ2VuZXJhdGUgcmFuZG9tIHNhbXBsZXMgZnJvbSB0aGUgZXF1aWxpYnJpdW0gcHJvYmFiaWxpdGllcyBieSBzdGFydGluZyBhdCBhbnkgc3RhdGUgd2l0aCBpbml0aWFsIHNhbXBsZXMgZGlzY2FyZGVkLgpUaGVyZSBhcmUgbXVsdGlwbGUgbWV0aG9kcyBmb3IgY29uc3RydWN0aW5nIE1hcmtvdiBjaGFpbiB3aXRoIGVxdWlsaWJyaXVtLCBzdWNoIGFzIE1ldHJvcG9saXMgc2FtcGxpbmcsIEdpYmJzIHNhbXBsaW5nLCBIYW1pbHRvbmlhbiBtZXRob2QsIC4uLiwgZXRjLgoKTUNNQyBpcyB1c2VmdWwgc2luY2UgaW4gQmF5ZXNpYW4gbW9kZWxpbmcsIAp1c3VhbGx5IHRoZSBtb2RlbCBkaXN0cmlidXRpb24gZnVuY3Rpb25zIGFyZSBOT1QgYW5hbHl0aWNhbGx5IHRyYWN0YWJsZS4gCihObyBjbG9zZWQtZm9ybSBzb2x1dGlvbi4pCkdlbmVyYXRpbmcgcmFuZG9tIHNhbXBsZXMgZnJvbSBhIE5vcm1hbCBkaXN0cmlidXRpb24gaXMgZWFzeSBzaW5jZSBpdHMgYW5hbHl0aWNhbCBmb3JtIGlzIGNsZWFybHkgZGV0ZXJtaW5lZC4KQSBtb2RlcmF0ZWx5IGNvbmZpZ3VyZWQgQmF5ZXNpYW4gcHJvYmFiaWxpc3RpYyBtb2RlbCwKb24gdGhlIG90aGVyIGhhbmQsCmNhbiBlYXNpbHkgYmVjb21lIGludHJhY3RhYmxlIHdpdGhvdXQgdGhlIGludGVudGlvbiB0byBtYWtlIGl0IHJlYWxseSBjb21wbGljYXRlZC4KCiMjIE1vbnRlIENhcmxvIGZvciBDZW50cmFsIExpbWl0IFRoZW9yZW0KCkZpcnN0IHdlIGlsbHVzdHJhdGUgTW9udGUgQ2FybG8gYnkgZXhhbWluaW5nIENMVCB1c2luZyBjb21wdXRlci1kcml2ZW4gcmFuZG9tIGRyYXdpbmcgc2ltdWxhdGlvbiByYXRoZXIgdGhhbiBkaXJlY3QgbWF0aGVtYXRpYyBkZXJpdmF0aW9uLgoKQWdhaW4gdGhhdCBDTFQgc3VnZ2VzdHMgdGhlIGxpbWl0aW5nIGRpc3RyaWJ1dGlvbiBvZiBzYW1wbGUgbWVhbiAkXGJhcntYfSQgd2l0aCBzYW1wbGUgc2l6ZSAkTiQgaXMgTm9ybWFsIGFzIGluIGVxdWF0aW9uICRcZXFyZWZ7ZXE6Y2x0fSQuCgpMZXQncyB1c2UgTW9udGUgQ2FybG8gc2ltdWxhdGlvbiB0byB2ZXJpZnkgaWYgdGhpcyBpcyB0cnVlLgpGb3Igc2ltcGxpY2l0eSB3ZSBhc3N1bWUgdGhlIHBvcHVsYXRpb24gaXMgYSBzdGFuZGFyZCBVbmlmb3JtIGRpc3RyaWJ1dGlvbiAkWCBcc2ltIFxtYm94e1VuaWZvcm19KDAsMSkkLgpIb3dldmVyLCByZW1lbWJlciB0aGF0IGluIHJlYWxpdHkgdmlydHVhbGx5IGFueSBwb3B1bGF0aW9uIGRpc3RyaWJ1dGlvbiAod2l0aCBmaW5pdGUgdmFyaWFuY2UpIHNob3VsZCB3b3JrLgoKVGhlIHByb2Nlc3MgaXMgYXMgdGhlIGZvbGxvd2luZ3M6CgoxLiBEcmF3IHNhbXBsZSBkYXRhIG9mIGEgc2l6ZSAkTiQKMi4gQ2FsY3VsYXRlIHRoZSBzYW1wbGUgbWVhbiBmcm9tIHRoZSBhYm92ZSBzYW1wbGUgZGF0YSAKMy4gUmVwZWF0IHN0ZXAgMSBhbmQgMiBmb3IgbWFueSB0aW1lcyAoc2F5LCAxMDAgdGltZXMgb2YgcmVwZXRpdGlvbikKCkFuZCB3ZSBwbG90IHRoZSBkaXN0cmlidXRpb24gb2YgYWxsIHRoZSBzYW1wbGUgbWVhbnMsCndpdGggdGhlIGJsdWUgbGluZSBkZXBpY3RpbmcgdGhlIHRoZW9yZXRpY2FsIGxpbWl0aW5nIHNhbXBsaW5nIGRpc3RyaWJ1dGlvbiB0aGF0IHdlJ2QgbGlrZSB0byBhcHByb3hpbWF0ZS4KV2UgYWxzbyBkbyB0aGlzIGZvciBkaWZmZXJlbnQgdmFsdWVzIG9mIHNhbXBsZSBzaXplcy4KVGhlIHJlc3VsdHMgYXJlIHBsb3R0ZWQgYWxsIHRvZ2V0aGVyIGFzIGJlbG93LgoKYGBge3IgbWNfY2x0X3IxMDB9CnBsb3RfbWNfc2FtcGxlX21lYW5fY2x0IDwtIGZ1bmN0aW9uKE4sIHNhbXBsZV9mdW5jPXJ1bmlmLCBuX3JlcD0xMDAsIHNlZWQ9Nzc3LCAuLi4pIHsKICBzZXQuc2VlZChzZWVkKQogICMgR2VuZXJhdGUgcmFuZG9tIGRyYXdzIGZyb20gVW5pZm9ybSgwLDEpIGFuZCBjYWxjdWxhdGUgdGhlIHNhbXBsZSBtZWFuLgogICMgRG8gdGhpcyBmb3IgYG5fcmVwYCB0aW1lcy4KICB4X2JhciA8LSBudW1lcmljKG5fcmVwKQogIGZvciAoIHIgaW4gMTpuX3JlcCApIHsKICAgIHhfYmFyW3JdIDwtIG1lYW4oc2FtcGxlX2Z1bmMoTikpCiAgfQogICMgUGxvdC4gIAogIHRpdGxlIDwtYnF1b3RlKCJTYW1wbGluZyBEaXN0cmlidXRpb24gb2YgIiB+IGJhcihYKSB+ICIgYXQgTiA9ICIgfiAuKE4pKQogIHggPC0gc2VxKG1pbih4X2JhciksIG1heCh4X2JhciksIGxlbmd0aD0xMDApIAogIHkgPC0gZG5vcm0oeCwgLjUsIHNxcnQoMS8xMikvc3FydChOKSkgICMgQmFzZWQgb24gdHJ1ZSBtZWFuIGFuZCBzZCBvZiBhIHN0YW5kYXJkIFVuaWZvcm0uCiAgaGlzdCh4X2JhciwgcHJvYmFiaWxpdHk9VFJVRSwgbWFpbj10aXRsZSwgeGxhYj1OVUxMLCB5bGltPWMoMCwgbWF4KHkpKjEuMSksIC4uLikKICBsaW5lcyh4LCB5LCBjb2w9ImJsdWUiLCBsd2Q9MikKfQoKcGFyKG1mcm93PWMoMiwgMikpCmZvciAoIG4gaW4gYyg1MCwgMTAwLCA1MDAsIDEwMDApICkgcGxvdF9tY19zYW1wbGVfbWVhbl9jbHQoTj1uKQptdGV4dCh0ZXh0PSJNb250ZSBDYXJsbyBTaW11bGF0aW9uIHdpdGggMTAwIFJlcGV0aXRpb25zIiwgc2lkZT0xLCBsaW5lPS0yLCBvdXRlcj1UUlVFKQpgYGAKCk9idmlvdXNseSB0aGUgYXBwcm94aW1hdGlvbiBieSBNb250ZSBDYXJsbyBpcyBxdWl0ZSBnb29kIGF0IHZhcmlvdXMgdmFsdWVzIG9mIHNhbXBsZSBzaXplICROJCB3aXRoIGV2ZW4gYSBzbWFsbCBudW1iZXIgb2YgcmVwZXRpdGlvbiBpbiB0aGlzIHNpbXBsZSBjYXNlLgpJZiB3ZSBlbmxhcmdlIHRoZSBzaW11bGF0aW9uIHJlcGV0aXRpb24gbnVtYmVyLCB0aGUgYXBwcm94aW1hdGlvbiBjYW4gYmUgb2YgY291cnNlIGJldHRlcjoKCmBgYHtyIG1jX2NsdF9yMTAwMH0KcGFyKG1mcm93PWMoMiwgMikpCmZvciAoIG4gaW4gYyg1MCwgMTAwLCA1MDAsIDEwMDApICkgcGxvdF9tY19zYW1wbGVfbWVhbl9jbHQoTj1uLCBuX3JlcD0xMDAwKQptdGV4dCh0ZXh0PSJNb250ZSBDYXJsbyBTaW11bGF0aW9uIHdpdGggMTAwMCBSZXBldGl0aW9ucyIsIHNpZGU9MSwgbGluZT0tMiwgb3V0ZXI9VFJVRSkKYGBgCgpVc3VhbGx5IGEgTW9udGUgQ2FybG8gZXhlcmNpc2UgaW52b2x2ZXMgaW4gbnVtYmVyIG9mIHJlcGV0aXRpb24gYXQgdGhvdXNhbmRzIGF0IGxlYXN0LgoKVG8gcmVpdGVyYXRlIHRoZSBpbXBvcnRhbmNlIG9mIHJhbmRvbSBzYW1wbGluZywKbGV0J3MgZG8gdGhlIHNhbWUgc2ltdWxhdGlvbiBidXQgdGhpcyB0aW1lIHdpdGggYSBiaWFzZWQgc2FtcGxlci4KV2UgY3JlYXRlIGEgc2VxdWVudGlhbGx5IGNvcnJlbGF0ZWQgc2FtcGxlciBmcm9tIGEgc3RhbmRhcmQgVW5pZm9ybS4KCmBgYHtyIG1jX2NsdF9iaWFzZX0Kbm9uX3JhbmRvbV9zYW1wbGVyIDwtIGZ1bmN0aW9uKG4pIHsKICAjIEEgcmFuZG9tIHByb2Nlc3MgZHJhd2luZyBmcm9tIFVuaWZvcm0gdGhhdCBpcyBhdXRvLXJlbGF0ZWQuCiAgZ2V0X3lfdDEgPC0gZnVuY3Rpb24oeV90MCkgewogICAgc2FtcGxlKGMocnVuaWYoMSwgMCwgeV90MCksIHJ1bmlmKDEsIHlfdDAsIDEpKSwgc2l6ZT0xLCBwcm9iPWMoLjcsIC4zKSkKICB9CiAgeV90MCA8LSBydW5pZigxKQogIHkgPC0gbnVtZXJpYyhuKQogIHlbMV0gPC0geV90MAogIGZvciAoIGkgaW4gMjpuICkgewogICAgeVtpXSA8LSBnZXRfeV90MSh5X3QwKQogICAgeV90MCA8LSB5W2ldCiAgfQogIHkKfQoKcGxvdF9tY19zYW1wbGVfbWVhbl9jbHQoTj01MCwgc2FtcGxlX2Z1bmM9bm9uX3JhbmRvbV9zYW1wbGVyLCBuX3JlcD0xMDAwLAogICAgICAgICAgICAgICAgICAgICAgICBzdWI9Ik1vbnRlIENhcmxvIGZvciBCaWFzZWQgU2FtcGxlcyIpCmBgYAoKQXMgb25lIGNhbiBzZWUsCnRoZSByZXN1bHRpbmcgTUMgc2FtcGxlcyBjYW4gbm8gbG9uZ2VyIGFwcHJveGltYXRlIHRoZSB0aGVvcmV0aWNhbCBsaW1pdGluZyBkaXN0cmlidXRpb24gb2YgdGhlIHNhbXBsZSBtZWFuLgoKIyMgTW9udGUgQ2FybG8gZm9yIHRoZSBMYXcgb2YgTGFyZ2UgTnVtYmVyCgpDb250aW51ZSBmcm9tIHRoZSBhYm92ZSBleGFtcGxlLAp3ZSBjYW4gYWxzbyB1c2UgTW9udGUgQ2FybG8gdG8gZXhhbWluZSB0aGUgTGF3IG9mIExhcmdlIE51bWJlciAoTExOIGhlcmVhZnRlcikuCkxMTiBzdWdnZXN0cyB0aGF0IHRoZSBzYW1wbGUgbWVhbiB3aWxsIGNvbnZlcmdlIHRvIHBvcHVsYXRpb24gbWVhbiBpbiBwcm9iYWJpbGl0eS4KVGhhdCBpcywKCiQkCihcYmFye3h9IC0gXG11KSBcb3ZlcnNldHtwfVxyaWdodGFycm93IDAuCiQkCgpCeSBwbG90aW5nIHRoZSBvdmVybGFwcGluZyBkZW5zaXR5IGF0IGEgdmFyaWV0eSBvZiBzYW1wbGUgc2l6ZXMgd2UgY2FuIGNsZWFybHkgc2VlIHRoYXQgdGhlIHNhbXBsaW5nIGRpc3RyaWJ1dGlvbiBvZiBzYW1wbGUgbWVhbiBlcnJvciBpcyBjb252ZXJnaW5nIGFyb3VuZCAwLgpTbyB0aGUgaW50dWl0aXZlIGNvbmNsdXNpb246IFRoZSBsYXJnZXIgdGhlIHNhbXBsZSwgdGhlIGxlc3MgdGhlIHZhcmlhbmNlIG9mIG91ciBndWVzc2luZy4KCmBgYHtyIG1jX2xsbn0KcGxvdF9tY19zYW1wbGVfbWVhbl9sbG4gPC0gZnVuY3Rpb24oTl9saXN0LCBuX3JlcD01MDApIHsKICByZXF1aXJlKGxhdHRpY2UsIHF1aWV0bHk9VFJVRSkKICAKICBtY19tZWFuIDwtIGZ1bmN0aW9uKE4sIG5fcmVwKSB7CiAgICB4X2JhciA8LSBudW1lcmljKG5fcmVwKQogICAgZm9yICggciBpbiAxOm5fcmVwICkgewogICAgICB4X2JhcltyXSA8LSBtZWFuKHJ1bmlmKE4pKQogICAgfQogICAgeF9iYXIKICB9CiAgCiAgcmVzIDwtIG1hdHJpeChOQV9yZWFsXywgbnJvdz1uX3JlcCwgbmNvbD1sZW5ndGgoTl9saXN0KSkKICBmb3IgKCBpIGluIDE6bGVuZ3RoKE5fbGlzdCkpIHsKICAgIHJlc1ssaV0gPC0gbWNfbWVhbihOX2xpc3RbaV0sIG5fcmVwKSAtIC41CiAgfQogIHJlcyA8LSBhcy5kYXRhLmZyYW1lKHJlcykKICBjb2xuYW1lcyhyZXMpIDwtIHBhc3RlMCgiTiA9ICIsIE5fbGlzdCkKICByZXMgPC0gc3RhY2socmVzKQogIHRpdGxlIDwtYnF1b3RlKCJTYW1wbGluZyBEaXN0cmlidXRpb24gb2YgIiB+IGJhcihYKSAtIG11KQogIGxhdHRpY2U6OmRlbnNpdHlwbG90KH4gdmFsdWVzLCBncm91cD1pbmQsIGRhdGE9cmVzLCBhdXRvLmtleT1UUlVFLAogICAgICAgICAgICAgICAgICAgICAgIG1haW49dGl0bGUsIHhsYWI9IlNhbXBsZSBNZWFuIEVycm9yIikKfQoKcGxvdF9tY19zYW1wbGVfbWVhbl9sbG4oYyg1MCwgMTAwLCA1MDAsIDEwMDAsIDEwMDAwKSkKYGBgCgojIyBNb250ZSBDYXJsbyBmb3IgQ29uZmlkZW5jZSBJbnRlcnZhbAoKUHJldmlvdXNseSB0byBpbGx1c3RyYXRlIHRoZSBpZGVhIHRoYXQgY29uZmlkZW5jZSBpbnRlcnZhbCBpcyBhIHJhbmRvbSB2YXJpYWJsZSwKd2UgY29uZHVjdCA5IGluZGVwZW5kZW50IGV4cGVyaW1lbnRzIGFuZCB2aXN1YWxpemUgdGhlIHJlc3VsdHMuCkFzIGEgc29mdHdhcmUgZW5naW5lZXIgb25lIHNob3VsZCBwcm9iYWJseSB0aGluazoKV2h5IHN0b3AgYXQgb25seSA5IGV4cGVyaW1lbnRzPwoKV2UgY2FuIGV4dGVuZCB0aGUgZXhlcmNpc2UgYXMgYSBNb250ZSBDYXJsbyBvbmUgYnkgZ2VuZXJhdGluZyBhcyBtYW55IGV4cGVyaW1lbnRzIGFzIHdlIHdhbnQuCldlIGNhbiB0aGVuIHN1bW1hcml6ZSB0aGUgb3ZlcmFsbCByZXN1bHRzIHRvIGV4YW1pbmUgd2hldGhlciB0aGUgOTUlIGNvbmZpZGVuY2UgaW50ZXJ2YWwgYWN0dWFsbHkgY29udGFpbnMgdGhlIHRydWUgcGFyYW1ldGVyIGFwcHJveGltYXRlbHkgOTUlIG9mIHRoZSB0aW1lLgoKYGBge3IgbWNfY2l9CnJ1bl9jaV9leHAgPC0gZnVuY3Rpb24oWCwgdHJ1ZV9tZWFuKSB7CiAgeGJhciA8LSBtZWFuKFgpCiAgZXJyIDwtIHFub3JtKC45NzUsIG1lYW49MCwgc2Q9c2QoWCkvc3FydChsZW5ndGgoWCkpKQogIHpfaGIgPC0geGJhciArIGVycgogIHpfbGIgPC0geGJhciAtIGVycgogIHRydWVfbWVhbiA+PSB6X2xiICYgdHJ1ZV9tZWFuIDw9IHpfaGIKfQoKc2V0LnNlZWQoNzc3KQpuciA8LSAxMDAwCm1jX2NpX3Jlc3VsdCA8LSByZXAoTkEsIG5yKQpmb3IgKCByIGluIDE6bnIgKSB7CiAgbWNfY2lfcmVzdWx0W3JdIDwtIHJ1bl9jaV9leHAocmJldGEoMTAwMCwgYmV0YV9hLCBiZXRhX2IpLCB0cnVlX21lYW4pCn0KCm1lYW4obWNfY2lfcmVzdWx0KQpgYGAKClRoZSByZXN1bHQgc3VnZ2VzdHMgdGhhdCBvdXQgb2YgYHIgbnJgIGluZGVwZW5kZW50IGV4cGVyaW1lbnRzIHRoZXJlIGFyZSBgciBzdW0obWNfY2lfcmVzdWx0KWAgZXhwZXJpbWVudHMgd2UgaGF2ZSB0aGUgOTUlIEMuSS4gY29udGFpbmluZyB0aGUgdHJ1ZSBtZWFuLgpUaGUgTW9udGUgQ2FybG8gbWV0aG9kIGhhcyBjb25maXJtZWQgb3VyIGludGVycHJldGF0aW9uIG9mIEMuSS4uCgojIyBSZWplY3Rpb24gU2FtcGxpbmcKClRoZSBwcmV2aW91cyBleGFtcGxlIHdvcmtzIHBlcmZlY3RseSBmaW5lIHNpbmNlIHdlIGhhdmUgYWJzb2x1dGVseSBubyBwcm9ibGVtIGluIGdlbmVyYXRpbmcgcmFuZG9tIHNhbXBsZXMgZnJvbSBhIHdlbGwta25vd24gZGlzdHJpYnV0aW9uIHdoaWNoIGlzIHRoZSBTdGFuZGFyZCBVbmlmb3JtIGRpc3RyaWJ1dGlvbi4KSW4gY2FzZXMgd2hlcmUgdGhlIHRhcmdldCBkaXN0cmlidXRpb24gaXMgbm90IHRyYWN0YWJsZSwKd2UgbmVlZCB0byByZWx5IG9uIE1hcmtvdiBjaGFpbiBNb250ZSBDYXJsbyBtZXRob2QuClRoZSBzYW1wbGluZyBoZXJlIGlzIHRlcm1lZCByZWplY3Rpb24gc2FtcGxpbmcgc2luY2UgaXQgaW52b2x2ZXMgaW4gYSBNYXJrb3YgcHJvY2VzcyB3aGVyZSB0aGUgbmV4dCBzYW1wbGUgaXMgYmFzZWQgb24gdGhlIHByZXZpb3VzIG9uZSBhbmQgc3ViamVjdCB0byBhIHJhdGUgb2YgcmVqZWN0aW9uLgoKSGVyZSBpcyB0aGUgZ2VuZXJhbCBwcm9jZXNzIG9mIHJlamVjdGlvbiBzYW1wbGluZyBpbiBvbmUgcm91bmQ6CgoxLiAqKltJbml0aWFsaXphdGlvbl0qKiBTdGFydCB3aXRoIGFuIGluaXRpYWwgc2FtcGxlCjIuICoqW1NhbXBsZSBQcm9wb3NhbF0qKiBQcm9wb3NlIGEgbmV3IHNhbXBsZSBiYXNlZCBvbiB0aGUgcHJldmlvdXMgb25lIHdpdGggYSByYW5kb20gbm9pc2UKMy4gKipbQWNjZXB0YW5jZSBUZXN0XSoqIEFjY2VwdCBvciByZWplY3QgdGhlIG5ldyBzYW1wbGUgc3RvY2hhc3RpY2FsbHkgYmFzZWQgb24gbGlrZWxpaG9vZCByYXRpbyBvZiB0aGUgbmV3IHNhbXBsZSB0byB0aGUgcHJldmlvdXMgb25lCiAgICArIElmIHRoZSBwcm9wb3NlZCBzYW1wbGUgaXMgYWNjZXB0ZWQsIHRoZSBuZXh0IHByb3Bvc2VkIHNhbXBsZSB3aWxsIGJlIGJhc2VkIG9uIGl0CiAgICArIEl0IHRoZSBwcm9wb3NlZCBzYW1wbGUgaXMgcmVqZWN0ZWQsIHRoZSBwcmV2aW91cyBleGFtcGxlIGlzIGNvdW50ZWQgYWdhaW4gYXMgYSB2YWxpZCBzYW1wbGUsIGFuZCBhIG5ldyBzYW1wbGUgaXMgcHJvcG9zZWQgYmFzZWQgb24gaXQKCkJhc2VkIG9uIHZhcmlvdXMgd2F5cyBvZiBpbXBsZW1lbnRpbmcgdGhlIHN0ZXAgMiBhbmQgc3RlcCAzLApkaWZmZXJlbnQgYWxnb3JpdGhtcyBoYXZlIGJlZW4gZGV2ZWxvcGVkLgpUaGUgcHJvY2VzcyB3aWxsIHJ1biBhcyBtYW55IHJvdW5kcyBhcyBwb3NzaWJsZSB0byBnZXQgdGhlIHJlcXVpcmVkIHNhbXBsZSBzaXplLgoKVGhlIHJhbmRvbSBub2lzZSBpbiBzdGVwIDIgaXMgdXN1YWxseSBkcmF3biBmcm9tIGEgTm9ybWFsIGRpc3RyaWJ1dGlvbiBjZW50ZXJlZCBhdCB6ZXJvLApyZWZlcnJlZCB0byBhcyB0aGUgKnByb3Bvc2FsIGRpc3RyaWJ1dGlvbiouCkEgTm9ybWFsIHByb3Bvc2FsIGRpc3RyaWJ1dGlvbiB3aXRoIHplcm8gbWVhbiB3aWxsIG1ha2UgdGhlIHNhbXBsZSBnZW5lcmF0aW5nIHByb2Nlc3MgZWZmZWN0aXZlbHkgYSBbcmFuZG9tIHdhbGtdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL1JhbmRvbV93YWxrKSBzdG9jaGFzdGljIHByb2Nlc3MuCgpTdGVwIDMgaXMgaW5kZWVkIHRoZSBrZXkgY29tcG9uZW50IG9mIHRoZSBwcm9jZXNzLApyZWZlcnJlZCB0byBhcyB0aGUgKmFjY2VwdGFuY2UgdGVzdCouCldpdGhvdXQgdGhpcyBzdGVwLAp0aGVyZSBpcyBubyBjaGFuY2UgdGhhdCBhIHJhbmRvbSB3YWxrIGNhbiBhcHByb3hpbWF0ZSBhIGdpdmVuIGRpc3RyaWJ1dGlvbi4KCiMjIyBNZXRyb3BvbGlzLUhhc3RpbmdzIFNhbXBsaW5nCgpUaGUgTWV0cm9wb2xpcy1IYXN0aW5ncyBhbGdvcml0aG0gaXMgYSBjbGFzc2ljYWwgbWV0aG9kIGluIE1DTUMgZmFtaWx5LgpJdCBpcyBwcm9iYWJseSBvbmUgb2YgdGhlIG1vc3RseSBxdW90ZWQgTUNNQyBtZXRob2RzLgpNYW55IG90aGVyIG1vZGVybiBtZXRob2RzIGFyZSBiYXNlZCBvbiB0b3Agb2YgaXQgaW5zdGVhZCBvZiBhIHJlLWludmVudGlvbi4KSW4gdGhlIGFjY2VwdGFuY2UgdGVzdCBvZiBNZXRyb3BvbGlzIHNhbXBsZXIsCnRoZSBwcm9wb3NlZCBzYW1wbGUgd2lsbCBiZSBhY2NlcHRlZCBpZiBpdHMgcG9zdGVyaW9yIGxpa2VsaWhvb2QgaXMgbGFyZ2VyIHRoYW4gdGhlIHByZXZpb3VzIHNhbXBsZS4KSWYgdGhlIGxpa2VsaWhvb2QgaXMgbG93ZXIsCnRoZSBwcm9wb3NlZCBzYW1wbGUgd2lsbCBzdGlsbCBoYXZlIGEgY2hhbmNlIHRvIGJlIGFjY2VwdGVkLAp3aXRoIHRoZSBwcm9iYWJpbGl0eSBtYXRjaGluZyB0aGUgbGlrZWxpaG9vZCByYXRpbyBvZiB0aGUgcHJvcG9zZWQgc2FtcGxlIHRvIHRoZSBwcmV2aW91c2x5IGFjY2VwdGVkIHNhbXBsZS4KClRvIGlsbHVzdHJhdGUsCmlmIHRoZSBwcm9wb3NlZCBzYW1wbGUgaGFzIGEgbGlrZWxpaG9vZCB2YWx1ZSBvZiAxIGFuZCB0aGF0IG9mIHRoZSBwcmV2aW91cyBzYW1wbGUgaXMgNSwKdGhlbiB0aGUgcHJvcG9zZWQgc2FtcGxlIHdpbGwgaGF2ZSBhICQxLzUgPSAyMFwlJCBjaGFuY2UgdG8gYmUgc3RpbGwgYWNjcGV0ZWQuCgpMZXQncyBpbXBsZW1lbnQgYSBNZXRyb3BvbGlzIHNhbXBsZXIgZm9yIGEgdGFyZ2V0IGRpc3RyaWJ1dGlvbiB3aGljaCBpcyBOb3JtYWwuClRoaXMgaXMgZm9yIHVzIHRvIGVhc2lseSB2ZXJpZnkgdGhlIChvdGhlcndpc2UgdXN1YWxseSB1bmtub3duKSB0cnVlIHBvcHVsYXRpb24gYW5kIHRoZSByZXN1bHRpbmcgc2FtcGxlcyBmcm9tIG91ciBzYW1wbGVyLgoKV2UgY2FuIGltcGxlbWVudCBpdHMgdmVyeSBiYXNpYyBjb25jZXB0IGluIGp1c3QgYSBmZXcgbGluZXMgb2YgY29kZToKCmBgYHtyIG1ldHJvX3NhbXBsZXJ9CmNhbGNfbGlrX25vcm0gPC0gZnVuY3Rpb24oeCwgbWVhbj0xMCwgc2Q9NSkgewogICMgSW4gYWN0dWFsIGFwcGxpY2F0aW9uLAogICMgdGhpcyBmdW5jdGlvbiBzaG91bGQgY2FsY3VsYXRlIHRoZSBwb3N0ZXJpb3IgZGVuc2l0eSBvZiB0aGUgcGFyYW1ldGVyIHggZ2l2ZW4gdGhlIGRhdGEuCiAgIyBIZXJlIHdlIGp1c3QgYXNzdW1lIHRoYXQgaXMgYSBOKG1lYW4sc2QpIGZvciBlZHVjYXRpb25hbCBwdXJwb3NlLgogIGRub3JtKHgsIG1lYW4sIHNkKQp9CgptZXRyb19zYW1wbGUgPC0gZnVuY3Rpb24obGlrZWxpaG9vZCwgaW5pdF92YWwsIG5pdGVyLCBzZWVkPTc3NykgewogICMgVGhlIGZ1bmN0aW9uIGlzIG5vdCBvcHRpbWl6ZWQgYXQgYWxsLiBJdCBpcyBmb3IgaWxsdXN0cmF0aW9uIHB1cnBvc2Ugb25seS4KICBzZXQuc2VlZChzZWVkKQogIAogIGN1cnJlbnRfdmFsIDwtIGluaXRfdmFsCiAgY3VycmVudF92YWxfbGlrIDwtIGxpa2VsaWhvb2QoY3VycmVudF92YWwpCiAgYWNjZXB0ZWRfc2FtcGxlcyA8LSBpbml0X3ZhbAogIG5fYWNjZXB0ZWQgPC0gMAogIAogICMgSXRlcmF0aW9uLgogICMgTm8gb2J2aW91cyB3YXkgdG8gdmVjdG9yaXplIHRoaXMgb3BlcmF0aW9uIHNpbmNlIGl0IGlzIHNlcmlhbGx5IGRlcGVuZGVudC4KICBmb3IgKCBpIGluIDE6bml0ZXIgKSB7CiAgICBwcm9wb3NlZF92YWwgPC0gY3VycmVudF92YWwgKyBybm9ybSgxLCAwLCAxKSAgIyBUaGUgcHJvcG9zYWwgZGlzdHJpYnV0aW9uIGlzIE4oMCwxKS4KICAgIHByb3Bvc2VkX3ZhbF9saWsgPC0gbGlrZWxpaG9vZChwcm9wb3NlZF92YWwpCiAgICBhY2NlcHRfcHJvYiA8LSBwcm9wb3NlZF92YWxfbGlrIC8gY3VycmVudF92YWxfbGlrCiAgICBpZiAoIGFjY2VwdF9wcm9iID4gcnVuaWYoMSkgKSB7CiAgICAgIGN1cnJlbnRfdmFsIDwtIHByb3Bvc2VkX3ZhbAogICAgICBjdXJyZW50X3ZhbF9saWsgPC0gcHJvcG9zZWRfdmFsX2xpawogICAgICBuX2FjY2VwdGVkIDwtIG5fYWNjZXB0ZWQgKyAxCiAgICB9CiAgICAjIE5vdCB0aGF0IGlmIHRoZSBwcm9wb3NlZCBzYW1wbGUgaXMgcmVqZWN0ZWQsCiAgICAjIHRoZSBjdXJyZW50IHNhbXBsZSBpcyBkdXBsaWNhdGVkLgogICAgYWNjZXB0ZWRfc2FtcGxlcyA8LSBjKGFjY2VwdGVkX3NhbXBsZXMsIGN1cnJlbnRfdmFsKQogIH0KICAKICBsaXN0KGFjY2VwdF9yYXRlPW5fYWNjZXB0ZWQgLyBuaXRlciwgc2FtcGxlPWFjY2VwdGVkX3NhbXBsZXMpCn0KYGBgCgpXZSBjYW4gcGxvdCB0aGUgKnRyYWNlKiBvZiB0aGUgc2FtcGxpbmcgcHJvY2VzczoKVGhlIGFjY2VwdGVkIHNhbXBsZSB2YWx1ZXMgYWxvbmcgdGhlIGl0ZXJhdGlvbi4KCmBgYHtyIG1ldHJvX3Bsb3RfdHJhY2V9CmluaXRfdmFsXzEgPC0gOQptY21jX3JlcyA8LSBtZXRyb19zYW1wbGUoY2FsY19saWtfbm9ybSwgaW5pdF92YWxfMSwgbml0ZXI9NTAwMCkKCnBsb3QobWNtY19yZXMkc2FtcGxlLCB0eXBlPSJsIiwgCiAgICAgbWFpbj0iTWV0cm9wb2xpcyBTYW1wbGluZyBUcmFjZSIsCiAgICAgeGxhYj0iSXRlcmF0aW9uIiwgeWxhYj0iTUNNQyBTYW1wbGUgVmFsdWUiKQpgYGAKCkluIHRoaXMgdG95IGV4YW1wbGUsIHRoZSBhY2NlcHRhbmNlIHJhdGUgb2Ygb3VyIHNhbXBsZXIgc2hvdWxkIGJlIHZlcnkgaGlnaDoKCmBgYHtyIG1ldHJvX2FjY2VwdF9yYXRlfQptY21jX3JlcyRhY2NlcHRfcmF0ZQpgYGAKCldlIGNhbiBhbHNvIHBsb3QgdGhlIGRpc3RyaWJ1dGlvbiBvZiByZXN1bHRpbmcgc2FtcGxlcyBhbG9uZyB3aXRoIHRoZSB0cnVlIHBvcHVsYXRpb24gZGVuc2l0eSAoaW4gYmx1ZSBsaW5lKSBmb3IgY29tcGFyaXNvbjoKCmBgYHtyIG1ldHJvX3Bsb3RfaGlzdH0KcGxvdF9tY19zYW1wbGUgPC0gZnVuY3Rpb24oeCwgaW5pdF92YWwsIC4uLikgewogIGhpc3QoeCwgcHJvYj1UUlVFLCB4YXhzPSJpIiwgeWF4cz0iaSIsIC4uLikKICBkZW5zaXR5IDwtIGN1cnZlKGNhbGNfbGlrX25vcm0sIGZyb209bWluKHgpLCB0bz1tYXgoeCksIAogICAgICAgICAgICAgICAgICAgY29sPSJibHVlIiwgYWRkPVRSVUUpCiAgYWJsaW5lKHY9aW5pdF92YWwsIGNvbD0icmVkIikKICB0ZXh0KGluaXRfdmFsLCBtZWFuKGRlbnNpdHkkeSkqLjUsIHBvcz0yLCAiSW5pdGlhbCBWYWx1ZSIsIGNvbD0icmVkIikKfQoKcGxvdF9tY19zYW1wbGUobWNtY19yZXMkc2FtcGxlLCBpbml0X3ZhbF8xKQpgYGAKCiMjIyMgT24gSW5pdGl2YWwgVmFsdWUgey19CgpJZiB0aGUgcGFyYW1ldGVyIHZhbHVlIGluaXRpYWxpemVzIGF0IGEgcmVhc29uYWJsZSBsb2NhdGlvbiwKd2Ugc2hvdWxkIHNlZSB0aGUgdHJhY2UgcGxvdCBleGhpYml0cyBbc3RhdGlvbmFyaXR5XShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9TdGF0aW9uYXJ5X3Byb2Nlc3MpIG92ZXIgaXRlcmF0aW9ucy4KQW55IG9idmlvdXMgbm9uLXN0YXRpb25hcml0eSBpbiB0aGUgYmVnaW5uaW5nIG9mIHRoZSB0cmFjZSBwbG90IHN1Z2dlc3QgYSAiYnVybi1pbiIgbWF5IGJlIG5lY2Vzc2FyeS0tZHJvcHBpbmcgc2FtcGxlcyBnZW5lcmF0ZWQgaW4gdGhlIGVhcmx5IHJvdW5kcy4KCkZvciBleGFtcGxlLApsZXQncyBwdXJwb3NlbHkgc3RhcnQgdGhlIHZhbHVlIGF0IGEgZmFyIG9mZiBsb2NhdGlvbiBhbmQgcGxvdCB0aGUgdHJhY2UgYWdhaW46CgpgYGB7ciBtZXRyb19wbG90X3RyYWNlXzJ9CmluaXRfdmFsXzIgPC0gNTAKbWNtY19yZXNfMiA8LSBtZXRyb19zYW1wbGUoY2FsY19saWtfbm9ybSwgaW5pdF92YWxfMiwgbml0ZXI9NTAwMCkKCnBsb3QobWNtY19yZXNfMiRzYW1wbGUsIHR5cGU9ImwiLCAKICAgICBtYWluPSJNZXRyb3BvbGlzIFNhbXBsaW5nIFRyYWNlIiwKICAgICB4bGFiPSJJdGVyYXRpb24iLCB5bGFiPSJNQ01DIFNhbXBsZSBWYWx1ZSIpCmBgYAoKYGBge3IgbWV0cm9fcGxvdF9oaXN0XzJ9CnBsb3RfbWNfc2FtcGxlKG1jbWNfcmVzXzIkc2FtcGxlLCBpbml0X3ZhbF8yKQpgYGAKCldlIHNlZSB0aGUgcmVzdWx0aW5nIGRpc3RyaWJ1dGlvbiBvZiBzYW1wbGVzIGhhcyBhIGZhdCByaWdodCB0YWlsIGR1ZSB0byBhbiBpbmFwcHJvcHJpYXRlIGluaXRpYWwgdmFsdWUsIAphbmQgdGhlIHRyYWNlIHBsb3QgaXMgb2J2aW91c2x5IG5vdCBzdGF0aW9uYXJ5IGF0IHRoZSBiZWdpbm5pbmcgcm91bmRzIG9mIGl0ZXJhdGlvbi4KVGhlIHNhbXBsZXIgc3RpbGwgbWFrZXMgaXRzIHdheSB0byB0aGUgdGFyZ2V0IGRpc3RyaWJ1dGlvbiwKYnV0IGVhcmx5IGRyYXdzIHNob3VsZCBiZSByZW1vdmVkIG9yIHdlIHdpbGwgZW5kIHVwIHdpdGggYW4gaW5jb3JyZWN0IGFwcHJveGltYXRpb24gaWYgdGhlIG51bWJlciBvZiBzYW1wbGUgc2l6ZSBpcyBub3QgbGFyZ2UgZW5vdWdoIHRvIG1ha2UgdGhlIGluaXRpYWwgZHJhd3MgbmVnbGlnaWJsZS4KCldlIGNhbiBleGFtaW5lIHRoZSBzdGFiaWxpdHkgb2YgdGhlIHJlc3VsdGluZyBkaXN0cmlidXRpb24gb24gZGlmZmVyZW50IG51bWJlciBvZiBpdGVyYXRpb25zIHdpdGggdGhlIGZhciBvZmYgaW5pdGlhbCB2YWx1ZToKCmBgYHtyIG1ldHJvX2xlc3NfaXRlcl9oaXN0fQpuaXRlcl8xIDwtIDUwMEwKCnBhcihtZnJvdz1jKDMsIDMpLCBvbWE9YygzLCAzLCAwLCAwKSwgbWFyPWMoMywgMywgMiwgMikpCmZvciAoIGkgaW4gMTo5ICkgewogIHMxIDwtIG1ldHJvX3NhbXBsZShjYWxjX2xpa19ub3JtLCBpbml0X3ZhbF8yLCBuaXRlcj1uaXRlcl8xKSRzYW1wbGUKICBwbG90X21jX3NhbXBsZShzMSwgaW5pdF92YWxfMiwgbWFpbj1zcHJpbnRmKCIlcyBJdGVyYXRpb25zQCVzIiwgbml0ZXJfMSwgaSkpCn0KewptdGV4dCh0ZXh0PSJNZXRyb3BvbGlzIFNhbXBsaW5nIEV4ZXJjaXNlcyAxIiwgc2lkZT0xLCBsaW5lPTAsIG91dGVyPVRSVUUpCm10ZXh0KHRleHQ9IkRlbnNpdHkiLCBzaWRlPTIsIGxpbmU9MCwgb3V0ZXI9VFJVRSkKfQpgYGAKCmBgYHtyIG1ldHJvX21vcmVfaXRlcl9oaXN0fQpuaXRlcl8yIDwtIDEwMDAwTAoKcGFyKG1mcm93PWMoMywgMyksIG9tYT1jKDMsIDMsIDAsIDApLCBtYXI9YygzLCAzLCAyLCAyKSkKZm9yICggaSBpbiAxOjkgKSB7CiAgczIgPC0gbWV0cm9fc2FtcGxlKGNhbGNfbGlrX25vcm0sIGluaXRfdmFsXzIsIG5pdGVyPW5pdGVyXzIpJHNhbXBsZQogIHBsb3RfbWNfc2FtcGxlKHMyLCBpbml0X3ZhbF8yLCBtYWluPXNwcmludGYoIiVzIEl0ZXJhdGlvbnNAJXMiLCBuaXRlcl8yLCBpKSkKfQp7Cm10ZXh0KHRleHQ9Ik1ldHJvcG9saXMgU2FtcGxpbmcgRXhlcmNpc2VzIDIiLCBzaWRlPTEsIGxpbmU9MCwgb3V0ZXI9VFJVRSkKbXRleHQodGV4dD0iRGVuc2l0eSIsIHNpZGU9MiwgbGluZT0wLCBvdXRlcj1UUlVFKQp9CmBgYAoKRm9yIGBuaXRlcmAgYXQgYHIgbml0ZXJfMWAgYW5kIGByIG5pdGVyXzJgIHdlIHJlcGVhdCB0aGUgZW50aXJlIHNhbXBsaW5nIHByb2Nlc3MgOSB0aW1lcyBpbmRlcGVuZGVudGx5LgpBcyBvbmUgY2FuIGVhc2lseSBzZWUsCndlIG5lZWQgYSByZWxhdGl2ZWx5IGxhcmdlIG51bWJlciBvZiByZXBldGl0aW9ucyBpbiBvcmRlciB0byBnZXQgYSBzdGFibGUgcmVzdWx0IG9mIHRoZSBzYW1wbGVyLAplc3BlY2lhbGx5IHdoZW4gdGhlIGluaXRpYWwgdmFsdWUgaXMgbm90IGFwcHJvcHJpYXRlLgoKSW4gcHJhY3RpY2UgdGhlcmUgYXJlIHNldmVyYWwgKG5vbi1leGNsdXNpdmUpIHdheXMgdG8gaGFuZGxlIHRoZSBpbml0aWFsIHZhbHVlOgoKKyBVc2UgTUxFIGVzdGltYXRlICh3aGVuZXZlciBhdmFpbGFibGUpIGFzIHRoZSBpbml0aWFsIHZhbHVlCisgVXNlIG11bHRpcGxlIE1hcmtvdiBjaGFpbnMgc2VwYXJhdGVseSB3aXRoIGRpZmZlcmVudCBpbml0aWFsIHZhbHVlcyBhbmQgZG8gZGlhZ25vc3RpYyBjaGVjayBhbW9uZyByZXN1bHRzIGZyb20gZGlmZmVyZW50IGNoYWlucworIFVzZSBidXJuLWluOiBSZW1vdmUgZWFybHkgc2FtcGxlcyBiYXNlZCBvbiBzdGF0aW9uYXJpdHkgY2hlY2sKCiMjIyMgT24gUHJvcG9zYWwgRGlzdHJpYnV0aW9uIHstfQoKT2YgY291cnNlIG5vdCBvbmx5IHRoZSBpbml0aWFsIHZhbHVlIGJ1dCBhbHNvIHRoZSBwcm9wb3NhbCBkaXN0cmlidXRpb24gd2lsbCBhZmZlY3QgdGhlIGVmZmVjdGl2ZW5lc3Mgb2YgdGhlIHNhbXBsaW5nIHByb2Nlc3MuClRoZSBzaXplIG9mIHRoZSBwZXJtdXRhdGlvbiBpcyBhIGh5cGVycGFyYW1ldGVyIG9mIE1ldHJvcG9saXMgc2FtcGxlci4KU2l6ZSB0b28gc21hbGwgd2lsbCBuZWVkIG1vcmUgdGltZSB0byBjb252ZXJnZSBhbmQgaXMgcmlza2llciB0byBiZSB0cmFwcGVkIGF0IGxvY2FsIG1heGltdW0gZGVuc2l0eS4KU2l6ZSB0b28gYmlnIHdpbGwgcmVzdWx0IGluIHRvbyBsb3cgdGhlIGFjY2VwdGFuY2UgcmF0ZSBzaW5jZSBsb3RzIG9mIHRoZSBwcm9wb3NlZCB2YWx1ZSBtYXkgbm90IGJlIGV2ZW4gd2l0aGluIHRoZSByYW5nZSBvZiB0aGUgdGFyZ2V0IGRpc3RyaWJ1dGlvbi4KVGhlIHNhbXBsaW5nIGVmZmljaWVuY3kgaGVuY2Ugd2lsbCBiZSBsb3dlci4KCiMjIyMgTWV0cm9wb2xpcyBTYW1wbGVyIGZvciBPdGhlciBEaXN0cmlidXRpb25zIHstfQoKRm9yIGV4cGVyaW1lbnRhbCBwdXJwb3NlLApsZXQncyBhbHNvIHNpbXVsYXRlIE1DTUMgc2FtcGxpbmcgZm9yIHNvbWUgb3RoZXIgd2VsbC1rbm93biBkaXN0cmlidXRpb25zLgoKIyMjIyMgQmV0YSBEaXN0cmlidXRpb24gey19CgpgYGB7ciBtZXRyb19iZXRhLCBmaWcuaGVpZ2h0PTUsIGZpZy53aWR0aD0xMH0KY2FsY19saWtfYmV0YSA8LSBmdW5jdGlvbih4LCBhPTIsIGI9NSkgewogIGRiZXRhKHgsIGEsIGIpCn0KCm1jbWNfcmVzX2JldGEgPC0gbWV0cm9fc2FtcGxlKGNhbGNfbGlrX2JldGEsIDAsIG5pdGVyPTUwMDApCgpwYXIobWZyb3c9YygxLCAyKSkKaGlzdChtY21jX3Jlc19iZXRhJHNhbXBsZSwgbWFpbj0iTWV0cm9wb2xpcyBTYW1wbGVzIiwgeGxhYj0iWCB+IEJldGEoMiwgNSkiLAogICAgIHN1Yj0iRG9lcyBpdCBsb29rIGxpa2UgYSBCZXRhIGRpc3RyaWJ1dGlvbj8iKQpwbG90KG1jbWNfcmVzX2JldGEkc2FtcGxlLCB0eXBlPSJsIiwgCiAgICAgbWFpbj0iVHJhY2UiLAogICAgIHhsYWI9Ikl0ZXJhdGlvbiIsIHlsYWI9Ik1DTUMgU2FtcGxlIFZhbHVlIikKYGBgCgpgYGB7ciBtZXRyYV9iZXRhX3RyYWNlcGxvdH0KeCA8LSBtY21jX3Jlc19iZXRhJHNhbXBsZQpoaXN0KHgsIHByb2I9VFJVRSwgeGF4cz0iaSIsIHlheHM9ImkiKQpkZW5zaXR5IDwtIGN1cnZlKGNhbGNfbGlrX2JldGEsIGZyb209bWluKHgpLCB0bz1tYXgoeCksIGNvbD0iYmx1ZSIsIGFkZD1UUlVFKQphYmxpbmUodj0wLCBjb2w9InJlZCIpCmBgYAoKIyMjIyMgVW5pZm9ybSBEaXN0cmlidXRpb24gey19CgpgYGB7ciBtZXRyb191bmlmLCBmaWcuaGVpZ2h0PTUsIGZpZy53aWR0aD0xMH0KY2FsY19saWtfdW5pZiA8LSBmdW5jdGlvbih4LCBhPTAsIGI9MSkgewogIGR1bmlmKHgsIG1pbj1hLCBtYXg9YikKfQoKbWNtY19yZXNfMyA8LSBtZXRyb19zYW1wbGUoY2FsY19saWtfdW5pZiwgLjAxLCBuaXRlcj01MDAwKQoKcGFyKG1mcm93PWMoMSwgMikpCmhpc3QobWNtY19yZXNfMyRzYW1wbGUsIG1haW49Ik1ldHJvcG9saXMgU2FtcGxlcyIsIHhsYWI9IlggfiBVbmlmb3JtKDAsIDEpIiwKICAgICBzdWI9IkRvZXMgaXQgbG9vayBsaWtlIGEgVW5pZm9ybSBkaXN0cmlidXRpb24/IikKcGxvdChtY21jX3Jlc18zJHNhbXBsZSwgdHlwZT0ibCIsIAogICAgIG1haW49IlRyYWNlIiwKICAgICB4bGFiPSJJdGVyYXRpb24iLCB5bGFiPSJNQ01DIFNhbXBsZSBWYWx1ZSIpCmBgYAoKYGBge3IgbWV0cmFfdW5pZl90cmFjZXBsb3R9CnggPC0gbWNtY19yZXNfMyRzYW1wbGUKaGlzdCh4LCBwcm9iPVRSVUUsIHhheHM9ImkiLCB5YXhzPSJpIikKZGVuc2l0eSA8LSBjdXJ2ZShjYWxjX2xpa191bmlmLCBmcm9tPW1pbih4KSwgdG89bWF4KHgpLCBjb2w9ImJsdWUiLCBhZGQ9VFJVRSkKYWJsaW5lKHY9MCwgY29sPSJyZWQiKQpgYGAKCiMjIyMgS2V5IFRha2UtQXdheSB7LX0KClRoZSBrZXkgdGFrZS1hd2F5IGlzIHRoYXQgYXMgbG9uZyBhcyB3ZSBjYW4gY2FsY3VsYXRlIHRoZSBwb3N0ZXJpb3IgZGVuc2l0eSwgCmV2ZW4gd2l0aG91dCBrbm93aW5nIHRoZSB0YXJnZXQgZGlzdHJpYnV0aW9uIGFuYWx5dGljYWxseSwgCml0IGlzIHN0aWxsIHBvc3NpYmxlIHRvIGdlbmVyYXRlIHJhbmRvbSBzYW1wbGUgb3V0IG9mIGl0LgpUaGVuIHdlIGNhbiBhcHBseSBNb250ZSBDYXJsbyBlc3RpbWF0aW9uIG9uIHRoZSByZXN1bHRpbmcgc2FtcGxlcyBmb3IgbW9kZWwgaW5mZXJlbmNlLgoKUHJhY3RpY2FsbHkgc3BlYWtpbmcsCnRoYW5rcyB0byBNQ01DLAppbiB0aGUgQmF5ZXMnIExhdyAoZXF1YXRpb24gJFxlcXJlZntlcTpiYXllc2xhd30kKSB3ZSBkb24ndCBuZWVkIHRvIGNhbGN1bGF0ZSB0aGUgZGVub21pbmF0b3IgdGVybSAobWFyZ2luYWwgbGlrZWxpaG9vZCBvZiBvdXRjb21lKSBpbiBvcmRlciB0byBnZW5lcmF0ZSByYW5kb20gc2FtcGxlIG9mIG91ciBtb2RlbCBwYXJhbWV0ZXJzIGZyb20gaXRzIHBvc3RlcmlvciBkaXN0cmlidXRpb24uCldlIG9ubHkgbmVlZCB0byBjYWxjdWxhdGUgdGhlIG51bWVyYXRvciB0ZXJtLgoKT2YgY291cnNlIGluIHRoZSB0b3kgZXhhbXBsZSBoZXJlIHRoZXJlIGlzIG5vIHN1Y2ggdGhpbmcgYXMgcG9zdGVyaW9yIGRlbnNpdHkgcGVyIHNlLApiZWNhdXNlIHdlIGFyZSBub3QgY2FsY3VsYXRpbmcgdGhlIGRlbnNpdHkgY29uZGl0aW9uZWQgb24gZGF0YSBvYnNlcnZlZC4gCihXZSBkb24ndCBvYnNlcnZlIGFueSBkYXRhIGF0IGFsbCEpCkluIHRoZSBsYXR0ZXIgc2VjdGlvbiB3ZSB3aWxsIGhhdmUgYSBtb3JlIHJlYWxpc3RpYyB3YWxrLXRocm91Z2ggb2YgaG93IE1ldHJvcG9saXMgc2FtcGxpbmcgaXMgYXBwbGllZCB1bmRlciBhIGZ1bGwtZmxlZGdlZCBwcm9iYWJpbGlzdGljIG1vZGVsLgoKIyMjIEdpYmJzIFNhbXBsaW5nCgpHaWJicyBzYW1wbGluZyBpcyB5ZXQgYW5vdGhlciB2ZXJ5IHBvcHVsYXIgTUNNQyBtZXRob2QuCkl0IGlzIGRlc2lnbmVkIHBhcnRpY3VsYXJseSBmb3Igam9pbnQgZGlzdHJpYnV0aW9uLgpUaGF0IGlzLAp0byBkcmF3IHJhbmRvbSBzYW1wbGVzIGZyb20gbXVsdGlwbGUgcGFyYW1ldGVycyB3aXRoIGhpZ2ggY29ycmVsYXRpb24uClRoZSBnZW5lcmFsIGlkZWEgaXMgdG8gZHJhdyBzYW1wbGVzIGZyb20gb25lIHBhcmFtZXRlciBkaXN0cmlidXRpb24gY29uZGl0aW9uYWwgb24gdGhlIG90aGVyIHBhcmFtZXRlcnMuCgpUaGUgaGVsbG8td29ybGQgZXhhbXBsZSBvZiBhIEdpYmJzIHNhbXBsZXIgd2lsbCBiZSB0byBkcmF3IHJhbmRvbSBzYW1wbGVzIGZyb20gYSAoc3VwcG9zZWRseSBjb21wbGljYXRlZCkgdGFyZ2V0IGRpc3RyaWJ1dGlvbiAkZih4LCB5KSQgYnkgdXNpbmcgb25seSAkZih4fHkpJCBhbmQgJGYoeXx4KSQuCkhlcmUgJHgkIGFuZCAkeSQgY2FuIGJlIGNvbnNpZGVyZWQgdHdvIHBhcmFtZXRlcnMgam9pbnRseSBkZXRlcm1pbmluZyB0aGUgZGVuc2l0eSBmdW5jdGlvbiAkZihcY2RvdCkkLgoKUmVhZGVycyBpbnRlcmVzdGVkIGluIG1vcmUgZGV0YWlscyBjYW4gcmVmZXIgdG8gQHJlc25pazIwMTBnaWJicyBhcyBhIGdlbnRsZSBpbnRyb2R1Y3Rvcnkgd3JpdGUtdXAgd2l0aCBuYWl2ZSBCYXllcyBkb2N1bWVudCBjbGFzc2lmaWNhdGlvbiBhcyBhIGRldGFpbGVkIHdvcmtpbmcgZXhhbXBsZS4KCk5vdGFibHksCkdpYmJzIHNhbXBsaW5nIGNhbiBhbHNvIGJlIHVzZWQgYWxvbmcgd2l0aCBNZXRyb3BvbGlzIHNhbXBsaW5nLApyZWZlcnJlZCB0byBhcyAiTWV0cm9wb2xpcyB3aXRoaW4gR2liYnMuIgoKIyMjIEhhbWlsdG9uaWFuIE1vbnRlIENhcmxvCgpCb3RoIE1ldHJvcG9saXMgYW5kIEdpYmJzIHNhbXBsaW5nIGFyZSBzdWJqZWN0IHRvIGluZWZmaWNpZW5jeSB3aGVuIGl0IGNvbWVzIHRvIGNvbXBsaWNhdGVkIHRhcmdldCBkaXN0cmlidXRpb24gaW4gaGlnaCBkaW1lbnNpb24uCgpIYW1pbHRvbmlhbiBNb250ZSBDYXJsbyAoSE1DKSBpcyBhbm90aGVyIE1DTUMgbWV0aG9kIGJhc2VkIG9uIHRvcCBvZiBNZXRyb3BvbGlzLUhhc3RpbmdzIGFsZ29yaXRobS4gClRoZSBkaWZmZXJlbmNlIGlzIHRoYXQgaXQgaW1wcm92ZXMgdGhlIHdheSB0aGUgcHJvcG9zZWQgc2FtcGxlIGlzIGdlbmVyYXRlZCwKc3VjaCB0aGF0IHRoZSBzZWFyY2ggaXMgbW9yZSBlZmZpY2llbnQgYW5kIGhlbmNlIG92ZXJhbGwgc2ltdWxhdGlvbiB0aW1lIGNhbiBiZSBjb25zaWRlcmFibHkgcmVkdWNlZC4KVGhlIGdlbmVyYWwgaWRlYSBvZiB0aGUgTWV0cm9wb2xpcyBmcmFtZXdvcmsgc3RpbGwgaG9sZHMuCkJ1dCBpbnN0ZWFkIG9mIHByb3Bvc2luZyBvbmUgc2FtcGxlIGF0IGEgdGltZSBwdXJlbHkgYXQgcmFuZG9tLApITUMgcHJvcG9zZXMgYSBzZXJpZXMgb2YgbmV3IHNhbXBsZXMgYXMgYSBwYXRoIHRvIGV4cGxvcmUgdGhlIHByb2JhYmlsaXR5IHNwYWNlLgpBbmQgc3VjaCBwYXRoIGlzIGd1aWRlZCBieSB0aGUgZ3JhZGllbnQgb2YgdGhlIHRhcmdldCBkZW5zaXR5IGZ1bmN0aW9uLgpCdXQgZ3JhZGllbnQgYWxvbmUgd2lsbCBub3QgZG8gdGhlIHRyaWNrIHNpbmNlIGdyYWRpZW50IHdpbGwgZ3VpZGUgdGhlIHBhdGggZGlyZWN0bHkgdG8gdGhlIG1vZGUgb2YgdGhlIGRlbnNpdHkgd2hlcmUgdGhlIGRlbnNpdHkgaXMgdGhlIGxhcmdlc3QgYnV0IHRoZSB2b2x1bWUgaXMgdG9vIHNtYWxsIHRvIGZhaXJseSBjYWxjdWxhdGUgdGhlIGludGVncmFsLgpUbyBlZmZlY3RpdmVseSBleHBsb3JlIHRoZSBwcm9iYWJpbGl0eSBzcGFjZSB3ZSBuZWVkIHRvIHRyYXZlcnNlIHdpdGhpbiBhcmVhIHdpdGggYm90aCBtb2RlcmF0ZSBkZW5zaXR5IGFuZCB2b2x1bWUgKGFyZWEgY2FsbGVkIHRoZSBbKnR5cGljYWwgc2V0Kl0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvVHlwaWNhbF9zZXQpKS4KVG8gYWNjb21wbGlzaCB0aGlzIGluIEhNQyBhdXhpbGlhcnkgbW9tZW50dW0gcGFyYW1ldGVycyBhcmUgaW50cm9kdWNlZCBzdWNoIHRoYXQgdGhlIGdyYWRpZW50IHRvZ2V0aGVyIHdpdGggdGhlIG1vbWVudHVtIHdpbGwgYWxsb3cgdGhlIHBhdGggdG8gaGF2ZSBqdXN0IGVub3VnaCAiZW5lcmd5IiB0byB0cmF2ZXJzZSB0aGUgdHlwaWNhbCBzZXQgd2l0aG91dCBiZWluZyBwdWxsIHRvIHRoZSBkZW5zaXR5IG1vZGUuCgpUaGUgZGV0YWlsZWQgbWF0aCBpbnZvbHZlcyBkaWZmZXJudGlhbCBnZW9tZXRyeSBhbmQgaXMgd2F5IGJleW9uZCB0aGUgc2NvcGUgb2Ygb3VyIGRpc2N1c3Npb24uCk9uZSBjYW4gcmVmZXIgdG8gQGJldGFuY291cnQyMDE3Y29uY2VwdHVhbCBmb3IgYSBtb3JlIGluLWRlcHRoIGJ1dCB5ZXQgc29mdCBpbnRyb2R1Y3Rpb24gdG8gSE1DLgoKIyBCcmlkZ2UgU2FtcGxpbmcKCkJyaWRnZSBzYW1wbGluZyBpcyB5ZXQgYW5vdGhlciBNb250ZSBDYXJsbyBudW1lcmljYWwgbWV0aG9kLApkZXNpZ25lZCB0byBlc3RpbWF0ZSBtYXJnaW5hbCBsaWtlbGlob29kIGZvciBhIEJheWVzaWFuIG1vZGVsLgpEaWZmZXJlbnQgZnJvbSBNQ01DIGFpbWluZyBhdCBlc3RpbWF0aW9uIGZvciB0aGUgcG9zdGVyaW9yLAp0aGUgZ29hbCBvZiBicmlkZ2Ugc2FtcGxpbmcgaXMgdG8gZXN0aW1hdGUgcGFydGljdWxhcmx5IGZvciB0aGUgZGVub21pbmF0b3IgdGVybSBpbiB0aGUgQmF5ZXMnIExhdyBnaXZlbiBhIG1vZGVsLgpBbHRob3VnaCB0aGUgZGVub21pbmF0b3IgaXMgbm90IG5lY2Nlc3NhcnkgYXQgYWxsIHdoZW4gdXNpbmcgTUNNQyB0byBzaW11bGF0ZSBzYW1wbGVzIGRyYXduIGZyb20gdGhlIHBvc3RlcmlvciBkZW5zaXR5LAp0aGVyZSBhcmUgb3RoZXIgY2FzZXMgd2hlcmUgd2UgbWF5IHdhbnQgdG8gc29sdmUgZm9yIHRoaXMgdGVybS4KVGhlIG1vc3QgY29tbW9uIHNjZW5hcmlvIGlzIG1vZGVsIGNvbXBhcmlzb24sCndpdGggb25lIG9idmlvdXMgZXhhbXBsZSBiZWluZyB0aGUgY2FsY3VsYXRpb24gb2YgQmF5ZXMgZmFjdG9yLgooUmVtZW1iZXIgdGhhdCBCYXllcyBmYWN0b3IgaXMgdGhlIHJhdGlvIG9mIG1hcmdpbmFsIHByb2JhYmlsaXR5IGJldHdlZW4gdHdvIG1vZGVscy4pCgpUaGUgcGFja2FnZSBgYnJpZGdlc2FtcGxpbmdgIFtAYnJpZGdlMjAxOF0gaW4gUiBpbXBsZW1lbnRzIGJyaWRnZSBzYW1wbGluZyBhbmQgaXMgY29tcGF0aWJsZSB3aXRoIG1hbnkgb3RoZXIgQmF5ZXNpYW4gbGlicmFyaWVzIChub3RhYmx5IGByc3RhbmAsIGFtb25nIG90aGVycykuCgpgYGB7ciBicmlkZ2VfaW1wb3J0LCByZXN1bHRzPSJoaWRlIn0KbGlicmFyeShicmlkZ2VzYW1wbGluZykKYGBgCgpMZXQncyB1c2Ugb3VyIGJpbm9taWFsIGV4YW1wbGUgcHJldmlvdXNseSBkaXNjdXNzZWQgdG8gaWxsdXN0cmF0ZSB0aGUgaW5uZXIgd29ya2luZyBvZiBicmlkZ2Ugc2FtcGxpbmcuCkEgQmlub21pYWwgbW9kZWwgd2l0aCBVbmlmb3JtIHByaW9yIG9uIHRoZSBwcm9iYWJpbGl0eSBwYXJhbWV0ZXIgaXMgc29tZXRpbWVzIHJlZmVyZWQgdG8gYXMgdGhlIEJldGEtQmlub21pYWwgbW9kZWwuCihBcHBhcmVudGx5IGR1ZSB0byB0aGUgZmFjdCB0aGF0IHRoZSBtYXJnaW5hbCBwcm9iYWJpbGl0eSBmb2xsb3dzIGEgZm9ybSBvZiBCZXRhIGZ1bmN0aW9uLikKCkZvcm1hbGx5LAoKJCQKXGJlZ2lue2FsaWduZWR9ClkgJlxzaW0gXG1ib3h7Qmlub21pYWx9KE4sIFx0aGV0YSksIFxcClx0aGV0YSAmXHNpbSBcbWJveHtVbmlmb3JtfSgwLCAxKS4KXGVuZHthbGlnbmVkfQokJAoKQW4gaW50ZXJlc3RpbmcgZmFjdCBhYm91dCB0aGlzIG1vZGVsIHdlIGRpZG4ndCBtZW50aW9uIHByZXZpb3VzbHkgaXMgdGhhdCB0aGUgcG9zdGVyaW9yIGlzIGFjdHVhbGx5IGEgQmV0YSBkaXN0cmlidXRpb24KCiQkClx0aGV0YXx5IFxzaW0gXG1ib3h7QmV0YX0oeSsxLCBOLXkrMSksCiQkIAoKd2hlcmUgJHkkIGlzIHRoZSBhY3R1YWwgbnVtYmVyIG9mIHBvc2l0aXZlIG91dGNvbWUgb3V0IG9mICROJCB0cmlhbHMuCgpJbiB0aGUgcHJldmlvdXMgc2VjdGlvbiB3ZSd2ZSB0cmllZCB1c2luZyBhIG5haXZlIE1vbnRlIENhcmxvIG1ldGhvZCB0byBhcHByb3hpbWF0ZSB0aGUgbWFyZ2luYWwgbGlrZWxpaG9vZCwKYnkgdGFraW5nIGFkdmFudGFnZSBvZiB0aGUgZmFjdCBmcm9tICRcZXFyZWZ7ZXE6bWFyZ2luYWxfbGlrX2gwfSQuClRoYXQgaXMsCndlIHNpbXBseSBnZW5lcmF0ZSByYW5kb20gZHJhd3MgZnJvbSBwcmlvciBhbmQgY2FsY3VsYXRlIHRoZSBtZWFuIG9mIHRoZSByZXN1bHRpbmcgY29uZGl0aW9uYWwgZGF0YSBsaWtlbGlob29kIGFzIG91ciBhcHByb3hpbWF0aW9uLgpJdCB3ZW50IHdlbGwgZHVlIHRvIHRoZSBzaW1wbGljaXR5IG9mIHRoaXMgcGFydGljdWxhciBwcm9ibGVtLgpJbiBnZW5lcmFsLApob3dldmVyLAp0aGUgYXBwcm94aW1hdGlvbiBtYXkgbm90IGJlIGdvb2QgZm9yIHN1Y2ggYSBuYWl2ZSBhcHByb2FjaC4KQnJpZGdlIHNhbXBsaW5nIGlzIHRoZW4gdGhlIHNvbHV0aW9uIGZvciBtb3JlIGNvbXBsaWNhdGVkIG1vZGVsIHNldHVwLgoKVGhlIGZvbGxvd2luZyB3cml0ZS11cCBtYWlubHkgZm9sbG93cyBAZ3JvbmF1MjAxN3R1dG9yaWFsLgoKVGhlIGlkZWEgb2YgYnJpZGdlIHNhbXBsaW5nIGlzIHRvIGludHJvZHVjZSBhIHByb3Bvc2FsIGRpc3RyaWJ1dGlvbiAkZyhcdGhldGEpJCBhbG9uZyB3aXRoIGEgYnJpZGdlIGZ1bmN0aW9uICRoKFx0aGV0YSkkIHN1Y2ggdGhhdAoKJCQKXGJlZ2lue2FsaWduZWR9ClAoeSkKJj0gXGludCBQKHl8XHRoZXRhKVAoXHRoZXRhKWRcdGhldGEgXFwKJj0gXGZyYWN7XGludCBQKHl8XHRoZXRhKVAoXHRoZXRhKWgoXHRoZXRhKWcoXHRoZXRhKWRcdGhldGF9e1xpbnQgUCh5fFx0aGV0YSlQKFx0aGV0YSloKFx0aGV0YSlnKFx0aGV0YSlkXHRoZXRhfSBcY2RvdCBcZnJhY3sxfXtcZnJhY3sxfXtQKHkpfX0gXFwKJj0gXGZyYWN7XGludCBQKHl8XHRoZXRhKVAoXHRoZXRhKWgoXHRoZXRhKWcoXHRoZXRhKWRcdGhldGF9e1xpbnQgXGZyYWN7UCh5fFx0aGV0YSlQKFx0aGV0YSl9e1AoeSl9aChcdGhldGEpZyhcdGhldGEpZFx0aGV0YX0gXFwKJj0gXGZyYWN7XGludCBQKHl8XHRoZXRhKVAoXHRoZXRhKWgoXHRoZXRhKWcoXHRoZXRhKWRcdGhldGF9e1xpbnQgUChcdGhldGF8eSloKFx0aGV0YSlnKFx0aGV0YSlkXHRoZXRhfSBcXAomPSBcZnJhY3tFX3tnKFx0aGV0YSl9XGJpZ1tQKHl8XHRoZXRhKVAoXHRoZXRhKWgoXHRoZXRhKVxiaWddfXtFX3twb3N0fVxiaWdbaChcdGhldGEpZyhcdGhldGEpXGJpZ119ClxlbmR7YWxpZ25lZH0uCiQkCgpJbiB0aGUgYWJvdmUgZXF1YXRpb24gdGhlIGRlbm9taW5hdG9yIGNhbiBiZSBhcHByb3hpbWF0ZWQgYnkgcmFuZG9tIHBhcmFtZXRlciBkcmF3cyBmcm9tIHRoZSBwb3N0ZXJpb3IgYW5kIHRoZSBudW1lcmF0b3IgY2FuIGJlIGFwcHJveGltYXRlZCBieSByYW5kb20gcGFyYW1ldGVyIGRyYXdzIGZyb20gdGhlIHByb3Bvc2FsIGRpc3RyaWJ1dGlvbi4KV2UgYWxyZWFkeSBrbm93IHRoYXQgd2UgY2FuIHVzZSBNQ01DIHRvIGdlbmVyYXRlIHJhbmRvbSBzYW1wbGVzIGZyb20gdGhlIHBvc3RlcmlvcjsKaGVuY2Ugbm8gcHJvYmxlbSBpbiBhcHByb3hpbWF0aW5nIHRoZSBkZW5vbWluYXRvciB0ZXJtLgpUaGUgcmVtYWluaW5nIHF1ZXN0aW9uIGlzIGhvdyBkbyB3ZSBvYnRhaW4gYSBzdWl0YWJsZSBwcm9wb3NhbCBkaXN0cmlidXRpb24gYW5kIGJyaWRnZSBmdW5jdGlvbiBzdWNoIHRoYXQgd2UgY2FuIGFsc28gYXBwcm94aW1hdGUgdGhlIG51bWVyYXRvciB0ZXJtLgoKV2Ugd2lsbCBza2lwIHRoZSBkZXRhaWxlZCBtYXRoZW1hdGljYWwgZGVyaXZhdGlvbiBidXQgaW4gZ2VuZXJhbCB3ZSB3YW50IGEgcHJvcG9zYWwgZGlzdHJpYnV0aW9uIHRvIHJlc2VtYmxlIHRoZSBwb3N0ZXJpb3IgZGlzdHJpYnV0aW9uIGFzIG11Y2ggYXMgcG9zc2libGUgYW5kIHdpdGggc3VmZmljaWVudCBvdmVybGFwIGluIHRoZWlyIHN1cHBvcnQuCkEgZ29vZCBjYW5kaWRhdGUgaXMgYSBOb3JtYWwgZGlzdHJpYnV0aW9uIHdpdGggaXRzIGZpcnN0IHR3byBtb21lbnRzIG1hdGNoaW5nIHRob3NlIG9mIHRoZSBwb3N0ZXJpb3IuCkJ1dCBmb3IgYSBtb3JlIGNvbXBsaWNhdGVkIHBvc3RlcmlvciAoZXNwZWNpYWxseSBpbiB2ZXJ5IGhpZ2ggZGltZW5zaW9uKSBvdGhlciBjYW5kaWRhdGVzIG1heSBiZSBtb3JlIGFwcHJvcHJpYXRlLgoKVGhlIGNob2ljZSBvZiB0aGUgYnJpZGdlIGZ1bmN0aW9uIGlzIHdheSBiZXlvbmQgdGhlIHNjb3BlIG9mIHRoaXMgdHV0b3JpYWwuClJvdWdobHkgc3BlYWtpbmcgaXQgd2lsbCBiZSBhIGZ1bmN0aW9uIG9mIHNhbXBsZSBzaXplcyBmcm9tIGJvdGggcG9zdGVyaW9yIGFuZCBwcm9wb3NhbCBzYW1wbGluZywKYW5kIGRlcGVuZCBvbiBkYXRhIGxpa2VsaWhvb2QgYW5kIG1hcmdpbmFsIGxpa2VsaWhvb2QuClNpbmNlIGl0IGRlcGVuZHMgb24gbWFyZ2luYWwgbGlrZWxpaG9vZCB3aGljaCBpcyB0aGUgdmVyeSB2YWx1ZSB3ZSB3YW50IHRvIHNvbHZlIGZvciwKdGhlIGFjdHVhbCBicmlkZ2Ugc2FtcGxpbmcgd2lsbCBpbnZvdmxlIGluIGFuIGl0ZXJhdGl2ZSBwcm9jZXNzIHdoZXJlIGluIGVhY2ggc3RlcCB0aGUgYnJpZGdlIGZ1bmN0aW9uIHdpbGwgYmUgdXBkYXRlZCBieSB0aGUgbmV3ZXN0IGVzdGltYXRlIG9mIHRoZSBtYXJnaW5hbCBsaWtlbGlob29kIHVuZGVyIGNvbnZlcmdlbmNlLgoKQmFjayB0byBvdXIgYmV0YS1iaW5vbWlhbCBleGFtcGxlLApsZXQncyBkbyB0aGUgYnJpZGdlIHNhbXBsaW5nIHVzaW5nIHRoZSBwYWNrYWdlIGBicmlkZ2VzYW1wbGluZ2A6CgpgYGB7ciBicmlkZ2VfYmV0YV9iaW5vbX0KIyBHZW5lcmF0ZSBwb3N0ZXJpb3Igc2FtcGxlcy4KIyBGb3IgYSByZWFsLXdvcmxkIHByb2JsZW0gd2UgbmVlZCB0byB1c2UgTUNNQy4KIyBIZXJlIHdlIHNpbXBseSB0YWtlIHRoZSBtYXRoZW1hdGljYWwgYWR2YW50YWdlIG9mIGtub3dpbmcgdGhlIHBvc3RlcmlvciBpcyBpbmRlZWQgYSBCZXRhLgpzZXQuc2VlZCg3NzcpCnBvc3Rfc2FtcGxlcyA8LSBhcy5tYXRyaXgocmJldGEoNTAwMCwgeSsxLCBOLXkrMSkpCmNvbG5hbWVzKHBvc3Rfc2FtcGxlcykgPC0gInRoZXRhIgoKIyBEZWZpbmUgZnVuY3Rpb24gdG8gY2FsY3VsYXRlIHRoZSBsb2cgdW5ub3JtYWxpemVkIHBvc3RlcmlvciBkZW5zaXR5LgojIFdlIGNhbiB1c2UgdGhpcyBmdW5jdGlvbiB3aXRoIGEgTWV0cm9wbGlzIHNhbXBsZXIgdG8gZGVyaXZlIHRoZSByYW5kb20gcG9zdGVyaW9yIHNhbXBsZXMgYXMgd2VsbC4KbHVwX2JldGFiaW5vbSA8LSBmdW5jdGlvbih0aGV0YSwgZGF0YSkgewogIGxvZyhjaG9vc2UoMTAsIGRhdGEpKnRoZXRhXmRhdGEqKDEtdGhldGEpXigxMC1kYXRhKSkKfQoKbWxoIDwtIGJyaWRnZV9zYW1wbGVyKHBvc3Rfc2FtcGxlcywgbG9nX3Bvc3Rlcmlvcj1sdXBfYmV0YWJpbm9tLCBkYXRhPXksIAogICAgICAgICAgICAgICAgICAgICAgbGI9c2V0TmFtZXMoLUluZiwgInRoZXRhIiksIHViPXNldE5hbWVzKEluZiwgInRoZXRhIiksIHNpbGVudD1UUlVFKQpleHAobWxoJGxvZ21sKQpgYGAKClRoZSByZXN1bHQgaXMgdmVyeSBjbG9zZSB0byB0aGUgYW5hbHl0aWNhbCBzb2x1dGlvbiBkZXJpdmVkIGluIG91ciBwcmV2aW91cyBzZWN0aW9uLgoKIyBCYXllc2lhbiBMb2dpc3RpYyBSZWdyZXNzaW9uIHVzaW5nIFIKClRoZSBwYWNrYWdlIGBtY21jYCBbQGdleWVyMjAxOW1jbWNdIGltcGxlbWVudHMgc2VyaW91c2x5IHRoZSBNZXRyb3BvbGlzLUhhc3RpbmdzIGFsZ29yaXRobS4KSXQgYWxzbyBwcm92aWVkcyBhIGdvb2QgZXhhbXBsZSBvZiBhIEJheWVzaWFuIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWwuCkluIHRoaXMgc2VjdGlvbiB3ZSB3aWxsIHRha2UgYSBjbG9zZSBsb29rIGF0IGl0cyBpbnRyb2R1Y3RvcnkgZXhhbXBsZS4KKFJ1biBgdmlnbmV0dGUoImRlbW8iLCAibWNtYyIpYCBmb3IgdGhlIG9yaWdpbmFsIHdyaXRlLXVwLikKCiMjIE1vZGVsIFNldHVwCgpUaGUgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbCBjYW4gYmUgd3JpdHRlbiBhczoKCiQkClAoeSA9IDEpID0gXGZyYWN7MX17MStlXnstKFhcYmV0YSl9fS4KJCQKClNpbXBseSBwdXQsCmEgbGluZWFyIG1vZGVsICRcbnUgPSBYXGJldGEgKyBcZXBzaWxvbiQgaXMgdHJhbnNmb3JtZWQgaW50byBhIHByb2JhYmlsaXR5IHNwYWNlICRcbWF0aGJie1J9IFxpbiBbMCwxXSQgdmlhIGEgbG9naXN0aWMgZnVuY3Rpb24gJFxkZWx0YSh0KSA9IFxmcmFjezF9ezEgKyBlXnstdH19JC4KCkluIG9yZGVyIHRvIGRvIEJheWVzaWFuIGFuYWx5c2lzIG9uIHRoaXMgbW9kZWwsCndlIG5lZWQgdHdvIG1vcmUgdGhpbmdzOgoKMS4gT3VyIHByaW9yIG9uIGFsbCB0aGUgcmVncmVzc2lvbiBjb2VmZmljaWVudHMgJFxiZXRhJCwgYWthIG1vZGVsIHBhcmFtZXRlcnMKMi4gRnVuY3Rpb24gdG8gY2FsY3VsYXRlIHRoZSBwb3N0ZXJpb3IgZGVuc2l0eSBvZiB0aGUgcGFyYW1ldGVycyAkUChcYmV0YXx5KSQsIHNvIHRoYXQgd2UgY2FuIGRvIE1DTUMgc2FtcGxpbmcgZm9yIHRoZSBtb2RlbCBwYXJhbWV0ZXJzCgpSZW1lbWJlciBhZ2FpbiAoYW5kIGFnYWluISkgZnJvbSBCYXllcycgTGF3ICRcZXFyZWZ7ZXE6YmF5ZXNsYXd9JCwKaW4gb3JkZXIgdG8gY2FsY3VsYXRlIHRoZSBwb3N0ZXJpb3IgZGVuc2l0eSwKd2UgbmVlZCB0aGUgZGF0YSBsaWtlbGlob29kICRQKHl8XGJldGEpJCBBTkQgYSBwcmlvciBvbiAkXGJldGEkIHRoYXQgY2FuIGhlbHAgdXMgc3BlY2lmeSAkUChcYmV0YSkkLgpXaGF0IHdlIGFyZSBnb2luZyB0byBjYWxjdWxhdGUgaXMgdXN1YWxseSBjYWxsZWQgdGhlICp1bm5vcm1hbGl6ZWQqIGRlbnNpdHkgJFAoeXxcYmV0YSkgXGNkb3QgUChcYmV0YSkkLCBzaW5jZSB0aGUgZGVub21pbmF0b3Igd29uJ3QgYWZmZWN0IG91ciBNQ01DIHNvbHV0aW9uIHRvIHRoZSBzeXN0ZW0gKGFuZCBhbHNvIGluIHByYWN0aWNlIHdlIHdpbGwgaGFyZGx5IGtub3cgJFAoeSkkIHRoZSB0cnVlIHBvcHVsYXRpb24gZGlzdHJpYnV0aW9uKS4KCkxldCdzIGV4YW1pbmUgdGhlIHRveSBleGFtcGxlIGluIGBtY21jYCBwYWNrYWdlOgoKYGBge3IgbWNtY19pbXBvcnQsIHJlc3VsdHM9ImhpZGUifQpsaWJyYXJ5KG1jbWMpCmBgYAoKCmBgYHtyIG1jbWNfbG9naXRfZGF0YX0KZGF0YShsb2dpdCkgICMgVGhpcyBpcyBhIGJ1aWx0LWluIGRhdGFzZXQgaW4gYG1jbWNgLgpoZWFkKGxvZ2l0KQpgYGAKCkFuZCB0aGUgcmVzdWx0IG9mIGEgdHJhZGl0aW9uYWwgZnJlcXVlbnRpc3QgYXBwcm9hY2g6CgpgYGB7ciBtY21jX2xvZ2l0X2ZyZXF1ZW50aXN0fQpmaXR0ZWRfbW9kZWwgPC0gZ2xtKHkgfiB4MSArIHgyICsgeDMgKyB4NCwgZGF0YT1sb2dpdCwgZmFtaWx5PWJpbm9taWFsLCB4PVRSVUUpCnN1bW1hcnkoZml0dGVkX21vZGVsKQpgYGAKClRoZSBwYXJhbWV0ZXIgcG9pbnQgZXN0aW1hdGUgaXMgc29sdmVkIGJ5IFtOZXd0b24ncyBtZXRob2RdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL05ld3RvbiUyN3NfbWV0aG9kKSBmb3IgYSBtYXhpbXVtIGxpa2VsaWhvb2QgZXN0aW1hdG9yIChNTEUpIGltcGxlbWVudGVkIGluIFIncyBidWxpdC1pbiBgZ2xtYCBmdW5jdGlvbi5eW0ZvciBtYWNoaW5lIGxlYXJuaW5nIGFwcGxpY2F0aW9uIHVzdWFsbHkgZ3JhZGllbnQgZGVzY2VudCAoYSAxc3Qtb3JkZXIgb3B0aW1pemVyKSBpcyB1c2VkIGluc3RlYWQgb2YgTmV3dG9uJ3MgbWV0aG9kIChhIDJuZC1vcmRlciBvcHRpbWl6ZXIpIGR1ZSB0byBjb21wdXRhdGlvbmFsIGNvbXBsZXhpdHkgaW4gbGFyZ2Ugc2NhbGUgYXBwbGljYXRpb24uIFRoaXMgaXMsIGhvd2V2ZXIsIGxlc3Mgb2YgYW4gaXNzdWUgaW4gdGhlIGZpZWxkIG9mIEVjb25vbWV0cmljcy4gQW5kIHNpbmNlIFIgaXMgZGVzaWduZWQgZm9yIHN0YXRpc3RpY3MgaW4gYSBwcmUtQmlnIERhdGEgZXJhLCB0aGUgbW9zdCBjb25jZXJuZWQgZmllbGQgb2YgYXBwbGljYXRpb24gd2lsbCBiZSB0cmFkaXRpb25hbCBzb2NpYWwgc2NpZW5jZSB3aGVyZSBvYnNlcnZhdGlvbmFsIGRhdGEgaXMgdXN1YWxseSB2ZXJ5IGxpbWl0ZWQgaW4gdm9sdW1lLl0gVGhlIGluZmVyZW5jZSBvbiB0aGVzZSBwb2ludCBlc3RpbWF0ZXMgKGJhc2VkIG9uIHRoZSBlc3RpbWF0ZWQgdmFyaWFuY2Ugb2YgdGhlIHBvaW50IGVzdGltYXRlIGFuZCBpdHMgc2FtcGxpbmcgZGlzdHJpYnV0aW9uIHByb3BlcnR5KSBpcyBhIGZyZXF1ZW50aXN0J3MgcG9pbnQgb2Ygdmlldy4KCiMjIENvbnN0cnVjdGluZyB0aGUgUG9zdGVyaW9yCgpOb3cgbW92ZSBvbiB0byB0aGUgQmF5ZXNpYW4gYXBwcm9hY2ggb2YgZXhhY3RseSB0aGUgc2FtZSBtb2RlbC4KVG8gdXNlIGBtY21jOjptZXRyb3BgIHRoZSBNZXRyb3BvbGlzIHNhbXBsaW5nIGZ1bmN0aW9uIHdlIGZpcnN0IG5lZWQgdG8gaW1wbGVtZW50IG91ciBvd24gZnVuY3Rpb24gZm9yIGNhbGN1bGF0aW5nIHRoZSB1bm5vcm1hbGl6ZWQgcG9zdGVyaW9yIGRlbnNpdHkgZ2l2ZW4gdGhlIGRhdGEgYW5kIHBhcmFtZXRlcnMuCgpJbiBwcmFjdGljZSwKaW4gb3JkZXIgdG8gbWFpbnRhaW4gbnVtZXJpY2FsIHN0YWJpbGl0eSwKd2UgYWx3YXlzIHVzZSBbbG9nLWxpa2VsaWhvb2RdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0xvZ19wcm9iYWJpbGl0eSkgaW5zdGVhZCBvZiB0aGUgbGlrZWxpaG9vZCBpbiByYXcgc2NhbGUuClNvIGluc3RlYWQgb2YgY2FsY3VsYXRpbmcKCiQkClx1bmRlcmJyYWNle1AoXGJldGF8eSl9X1x0ZXh0e3Bvc3RlcmlvciBkZW5zaXR5fSBccHJvcHRvIApcdW5kZXJicmFjZXtQKHl8XGJldGEpfV9cdGV4dHtkYXRhIGxpa2VsaWhvb2R9IFx0aW1lcwpcdW5kZXJicmFjZXtQKFxiZXRhKX1fXHRleHR7cHJpb3IgZGVuc2l0eX0sCiQkCgp3ZSBlbmQgdXAgd2l0aAoKJCQKXHVuZGVyYnJhY2V7XGxuIFAoXGJldGF8eSl9X1x0ZXh0e3Bvc3RlcmlvciBsb2ctbGlrZWxpaG9vZH0gXHByb3B0byAKXHVuZGVyYnJhY2V7XGxuIFAoeXxcYmV0YSl9X1x0ZXh0e2RhdGEgbG9nLWxpa2VsaWhvb2R9ICsgClx1bmRlcmJyYWNle1xsbiBQKFxiZXRhKX1fXHRleHR7cHJpb3IgbG9nLWxpa2VsaWhvb2R9LgokJAoKSGVuY2UgdGhlIHBvc3RlcmlvciB3aWxsIGJlIHRoZSBzdW0gb2YgYWxsIGxvZy1saWtlbGlob29kIG9mIGVhY2ggb2JzZXJ2ZWQgZGF0YSBwb2ludCBhbmQgdGhlIGxvZy1saWtlbGlob29kIG9mIG91ciBwYXJhbWV0ZXIgcHJpb3IuCgpUaGUgZGF0YSBsaWtlbGlob29kIGlzIHZlcnkgc3RyYWlnaHRmb3J3YXJkIHNpbmNlIHRoZSBtb2RlbCBpcyBhbHJlYWR5IGV4cHJlc3NlZCBpbiBwcm9iYWJpbGl0eToKCiQkClxiZWdpbnthbGlnbmVkfQpcbWJveHtkYXRhIGxvZy1saWtlbGlob29kfSBcZXF1aXYgClxsbiBQKHkgPSAxKSAmPSAtIFxsbiAoMSArIGVeey1YXGJldGF9KSAgXFwKJj0gWFxiZXRhIC0gXGxuICgxICsgZV57WFxiZXRhfSkuClxlbmR7YWxpZ25lZH0KJCQKClRoZSBjYXNlIG9mICRQKHkgPSAwKSQgY2FuIGJlIGRlcml2ZWQgc2ltaWxhcmx5Ol5bV2UgYXJlIHRha2luZyBhZHZhbnRhZ2Ugb2YgdGhlIGZhY3QgdGhhdCAkXGZyYWN7MX17MStlXnstdn19ID0gXGZyYWN7ZV52fXsxICsgZV52fSQgdG8gZXhwcmVzcyB0aGUgbG9nIGxpa2VsaWhvb2QgZGlmZmVyZW50bHkgaW4gZmF2b3Igb2Ygb3VyIG51bWVyaWNhbCBjb21wdXRhdGlvbiBzdGFiaWxpdHkuXQoKJCQKXGJlZ2lue2FsaWduZWR9ClxtYm94e2RhdGEgbG9nLWxpa2VsaWhvb2R9IFxlcXVpdiBcbG4gUCh5ID0gMCkgCiY9IFxsbiBcYmlnWzEgLSBQKHk9MSlcYmlnXSBcXAomPSAtIFhcYmV0YSAtIFxsbiAoMSArIGVeey1YXGJldGF9KSAgXFwKJj0gLSBcbG4gKDEgKyBlXntYXGJldGF9KSAuClxlbmR7YWxpZ25lZH0KJCQKClRoZSBwcmlvciBkZW5zaXR5IGlzIHB1cmVseSBkZXRlcm1pbmVkIGJ5IG91ciBhc3N1bXB0aW9uIGFib3V0IHRoZSBwcmlvciBpdHNlbGYuCkZvciBleGFtcGxlLAp0aGUgcHJvYmFiaWxpZHkgZGVuc2l0eSBmdW5jdGlvbiBvZiBhIE5vcm1hbCBkaXN0cmlidXRpb24gJHggXHNpbSBOKFxtdSwgXHNpZ21hKSQgcHJpb3IgaXM6CgokJApmKHgpID0gXGZyYWN7MX17XHNxcnR7MlxwaVxzaWdtYV4yfX1lXnstXGZyYWN7KHggLSBcbXUpXjJ9ezJcc2lnbWFeMn19LgokJAoKVGhlbiB0aGUgbmF0dXJhbCBsb2cgb2YgdGhlIHBkZiB3aWxsIGJlOgoKJCQKXGxuIGYoeCkgPSAKXHVuZGVyYnJhY2V7LVxmcmFjezF9ezJ9XGxuKDJccGlcc2lnbWFeMil9X1x0ZXh0e2EgY29uc3RhbnR9IC0gClxmcmFjeyh4IC0gXG11KV4yfXsyXHNpZ21hXjJ9LAokJAoKd2hlcmUgdGhlIGZpcnN0IHRlcm0gaXMgYSBjb25zdGFudCB0aGF0IGNhbiBiZSBzYWZlbHkgaWdub3JlZCBpbiB0aGUgYWN0dWFsIGNvbXB1dGF0aW9uLgoKTm93IGxldCdzIGFzc3VtZSBvdXIgcHJpb3Igb24gJFxiZXRhJCBpcyBqdXN0ICRcYmV0YSBcc2ltIE4oMCwgMSkkIGZvciBhbGwgY29lZmZpY2llbnRzIGluZGVwZW5kZW50bHkuCkEgbnVtZXJpY2FsbHkgc3RhYmxlIGxpa2VsaWhvb2QgZnVuY3Rpb24gY2FuIGJlIGltcGxlbWVudGVkIGFzIHRoZSBmb2xsb3dpbmcgY29kZSBjaHVuay4KCmBgYHtyIG1jbWNfbG9naXRfcG9zdF9kZW5zaXR5fQpnZXRfbG9nX3Vubm9ybV9wb3N0X2Z1biA8LSBmdW5jdGlvbihYLCB5KSBmdW5jdGlvbihiZXRhLCBtdV9iZXRhPTAsIHNpZ21hX2JldGE9MikgewogICMgUmV0dXJuIGEgZnVuY3Rpb24gdGhhdCBjYWxjdWxhdGUgdGhlIG51bWVyaWNhbGx5IHN0YWJsZSBsb2ctbGlrZWxpaG9vZCBnaXZlbiBYeS4KICAjIFRvIGRvIHNvLCBtYWtlIHN1cmUgdGhhdCB2IDwgMCBmb3IgYWxsIGV4cCh2KSBpbiB0aGUgY2FsY3VsYXRpb24uCiAgdiA8LSBhcy5udW1lcmljKFggJSolIGJldGEpCiAgIyBTdW0gb2YgZGF0YSBsb2ctbGlrZWxpaG9vZCBmb3IgYWxsIG9ic2VydmVkIGRhdGEsIGNvbmRpdGlvbmFsIG9uIGJldGEuCiAgbG9ncCA8LSBpZmVsc2UodiA8IDAsIHYgLSBsb2cxcChleHAodikpLCAtIGxvZzFwKGV4cCgtdikpKQogIGxvZ3EgPC0gaWZlbHNlKHYgPCAwLCAtIGxvZzFwKGV4cCh2KSksIC0gdiAtIGxvZzFwKGV4cCgtdikpKQogIGxvZ2wgPC0gc3VtKGxvZ3BbeSA9PSAxXSkgKyBzdW0obG9ncVt5ID09IDBdKQogICMgQXNzdW1lIGJldGEgfiBOKG11X2JldGEsIHNpZ21hX2JldGEpLCAKICAjIGNhbGN1bGF0ZSBvbmx5IHRoZSBkYXRhLWRlcGVuZGVudCBwYXJ0IG9mIHRoZSBsb2cgbGlrZWxpaG9vZC4KICBsb2dsIC0gc3VtKChiZXRhIC0gbXVfYmV0YSleMikgLyAoMipzaWdtYV9iZXRhXjIpCn0KCmx1cF9mdW4gPC0gZ2V0X2xvZ191bm5vcm1fcG9zdF9mdW4oZml0dGVkX21vZGVsJHgsIGZpdHRlZF9tb2RlbCR5KQpgYGAKCiMjIE1ldHJvcG9saXMtSGFzdGluZ3MgU2FtcGxpbmcgdXNpbmcgYG1jbWNgCgpPbmNlIHdlIGhhdmUgdGhlIHBvc3RlcmlvciBkZW5zaXR5IGZ1bmN0aW9uLCB3ZSBjYW4gcGx1Zy1hbmQtcGxheSB0aGUgc2FtcGxpbmcgQVBJIHRvIGdldCB0aGUgTUNNQyBzYW1wbGVzLgoKYGBge3IgbWNtY19sb2dpdF9iYXllc30Kc2V0LnNlZWQoNzc3KQoKIyBEbyBNZXRyb3BvbGlzIHNhbXBsaW5nIHdpdGggaW5pdGlhbCBwYXJhbWV0ZXIgdmFsdWVzIHNldCB0byB0aGUgTUxFIGVzdGltYXRlcy4KYmV0YV9tbGUgPC0gYXMubnVtZXJpYyhjb2VmZmljaWVudHMoZml0dGVkX21vZGVsKSkKbWNtY19yZXN1bHQgPC0gbWNtYzo6bWV0cm9wKGx1cF9mdW4sIGJldGFfbWxlLCBuYmF0Y2g9MTAwMCkKCnN0cihtY21jX3Jlc3VsdCkKYGBgCgpUaGUgcmV0dXJuZWQgb2JqZWN0IGNvbnRhaW5zIGFsbCB0aGUgaW5mb3JtYXRpb24gdXNlZnVsIGZvciBsYXR0ZXIgYW5hbHlzaXMsIAppbmNsdWRpbmcgYWNjZXB0YW5jZSByYXRlLCAKYW4gaW5kaWNhdG9yIHZlY3RvciBvZiB3aGljaCBzYW1wbGUgaXMgYWNjZXB0ZWQsIAp0aGUgZmluYWwgZXN0aW1hdGVzLCAKcHJvY2Vzc2luZyB0aW1lLApldGMuCgpBbHNvIHRoZSByZXR1cm5lZCBvYmplY3QgY2FuIGJlIHJlLXVzZWQgdG8gcnVuIGZ1cnRoZXIgc2FtcGxpbmcgY2hhaW46CgpgYGB7ciBtY21jX2NoZWNrX3JlcnVufQptY21jX3Jlc3VsdDIgPC0gbWNtYzo6bWV0cm9wKG1jbWNfcmVzdWx0KQoKIyBDaGVjayB0aGF0IHRoZSBzYW1wbGluZyBjaGFpbiBpcyBjb25zZWN1dGl2ZS4KYWxsKG1jbWNfcmVzdWx0JGZpbmFsID09IG1jbWNfcmVzdWx0MiRpbml0aWFsKQpgYGAKCldlIGNhbiB1c2UgdGhlIGFyZ3VtZW50IGBzY2FsZWAgdG8gYWRqdXN0IHRoZSBwcm9wb3NhbCBkaXN0cmlidXRpb24sIHRoYXQgaXMsIHRoZSBzdGVwIHNpemUgb2YgdGhlIHJhbmRvbSB3YWxrIGluIHRoZSBzYW1wbGluZyBwcm9jZXNzOgoKYGBge3IgbWNtY19jaGFuZ2Vfc3RlcF9zaXplfQptY21jX3Jlc3VsdCA8LSBtY21jOjptZXRyb3AobWNtY19yZXN1bHQsIHNjYWxlPS41LCBuYmF0Y2g9MTAwMDApCm1jbWNfcmVzdWx0JGFjY2VwdApgYGAKCkluIGdlbmVyYWwgdGhlIHN0ZXAgc2l6ZSBhbmQgdGhlIGFjY2VwdGFuY2UgcmF0ZSBpcyBuZWdhdGl2ZWx5IGNvcnJlbGF0ZWQ6CkJpZ2dlciBzdGVwcyBnaXZlIGxvd2VyIGFjY2VwdGFuY2UgcmF0ZXMuCgpXZSBjYW4gcGxvdCB0aGUgdHJhY2UgZm9yIGFsbCBwYXJhbWV0ZXJzOgoKYGBge3IgbWNtY19iZXRhX3RyYWNlfQpwbG90KHRzKG1jbWNfcmVzdWx0JGJhdGNoLCBuYW1lcz1zcHJpbnRmKCJcVTAzQjIlcyIsIDE6bGVuZ3RoKGJldGFfbWxlKSAtIDEpKSwKICAgICBtYWluPSJUcmFjZSBvZiBNZXRyb3BvbGlzIFNhbXBsaW5nIGZvciBMb2dpc3RpYyBNb2RlbCBDb2VmZmljaWVudHMiLAogICAgIHhsYWI9Ikl0ZXJhdGlvbiIpCmBgYAoKT3IgdGhlIGF1dG8tY29ycmVsYXRpb24gcGxvdCBhbW9uZyBhbGwgcGFyYW1ldGVyczoKCmBgYHtyIG1jbWNfYmV0YV9hdXRvY29yLCBmaWcuaGVpZ2h0PTcuNSwgZmlnLndpZHRoPTcuNX0KYWNmKG1jbWNfcmVzdWx0JGJhdGNoKQpgYGAKClNpbmNlIHRoZSBzYW1wbGluZyBwcm9jZXNzIGlzIGEgTWFya292IGNoYWluLApzZXJpYWwgY29ycmVsYXRpb24gaXMgZXhwZWN0ZWQuCgojIyBNQ01DIEVzdGltYXRpb24gYW5kIFN0YW5kYXJkIEVycm9yCgpCZSBhd2FyZSB0aGF0IHRoZSBmdWxsIE1ldHJvcG9saXMgYWxnb3JpdGhtIGltcGxlbWVudGVkIGluIGBtY21jYCBpcyBhIG1pbmktYmF0Y2hlZCBwcm9jZXNzLgpUaGUgcmV0dXJuZWQgYGJhdGNoYCB2ZWN0b3IgaXMgaW5kZWVkIGEgdmVjdG9yIG9mIChtaW5pLSkqYmF0Y2ggbWVhbnMqLgpCeSBkZWZhdWx0IGBtY21jOjptZXRyb3BgIHVzZSBhIGJhdGNoIGxlbmd0aCAoYGJsZW5gKSBlcXVhbCB0byAxLCBzbyB0aGVyZSBpcyBubyBtaW5pLWJhdGNoIGF0IGFsbC4KVGhlIHRvdGFsIG51bWJlciBvZiBNQyBpdGVyYXRpb25zIGhlbmNlIGlzIGBuYmF0Y2hgIHRpbWVzIGBibGVuYC4KCmBgYHtyIG1jbWNfbWluaWJhdGNofQojIFRoaXMgdGltZSB3ZSByZXR1cm4gYm90aCB0aGUgYmF0Y2ggbWVhbiBvZiByYXcgdmFsdWUgYW5kIHRoZSBzcXVhcmVkIHZhbHVlCiMgaW4gb3JkZXIgdG8gY2FsY3VsYXRlIHZhcmlhbmNlIG9mIHRoZSBlc3RpbWF0ZS4KbWNtY19yZXN1bHRfbWIgPC0gbWNtYzo6bWV0cm9wKG1jbWNfcmVzdWx0LCBzY2FsZT0uNSwgbmJhdGNoPTUwMCwgYmxlbj01MDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvdXRmdW49ZnVuY3Rpb24oeikgYyh6LCB6XjIpKQpgYGAKCk5vdyB0byBnZXQgb3VyIE1vbnRlIENhcmxvIHBvaW50IGVzdGltYXRlcyBhYm91dCB0aGUgbW9kZWwgcGFyYW1ldGVyczoKCmBgYHtyIG1jbWNfZXN0aW1hdGlvbl9tZWFufQojIFRoZSBncmFuZCBtZWFuOiBUaGUgbWVhbiBvZiBhbGwgdGhlIGJhdGNoIG1lYW5zLgpiYXRjaF9tZWFucyA8LSBjb2xNZWFucyhtY21jX3Jlc3VsdF9tYiRiYXRjaCkKcG9pbnRfZXN0IDwtIGJhdGNoX21lYW5zWzE6NV0KcG9pbnRfZXN0CmBgYAoKYW5kIGFsc28gdGhlIHZhcmlhbmNlIG9mIHRoZSBlc3RpbWF0ZXMKCmBgYHtyIG1jbWNfZXN0aW1hdGlvbl92YXJpYW5jZX0KIyBCYXNlZCBvbiB0aGUgZmFjdCB0aGF0IFZhcihYKSA9IEUoWF4yKSAtIEUoWCleMi4KdmFyX2VzdCA8LSBiYXRjaF9tZWFuc1s2OjEwXSAtIGJhdGNoX21lYW5zWzE6NV1eMgp2YXJfZXN0CmBgYAoKTW9udGUgQ2FybG8gaXRzZWxmIGlzIGEgcmFuZG9tIHByb2Nlc3MgdGhhdCBjYW4gaW50cm9kdWNlIGl0cyBvd24gc3RhdGlzdGljYWwgbm9pc2UuClN1Y2ggbm9pc2UgcHVyZWx5IGR1ZSB0byBNb250ZSBDYXJsbyBpcyB0ZXJtZWQgdGhlIE1vbnRlIENhcmxvIHN0YW5kYXJkIGVycm9yIChNQ1NFKS4KCiMjIyBNQ1NFIG9mIFBvaW50IEVzdGltYXRlCgpUaGlzIGlzIGFuYWxvZ291cyB0byB0aGUgc2FtcGxpbmcgZGlzdHJpYnV0aW9uIG9mICRcYmFye1h9JCBiYXNlZCBvbiBzYW1wbGVzIG9mICRYJCwKd2hpY2ggYnkgQ0xUIGhhcyBhIHN0YW5kYXJkIGVycm9yIG9mICRcZnJhY3tcc2lnbWF9e1xzcXJ0e059fSQuCgpOb3cgd2UgaGF2ZSBhIGZpbml0ZSBzYW1wbGUgc2V0IG9mICROJCBNQ01DIGJhdGNoIG1lYW5zIGZvciAkXGJldGEkLCAKZGVub3RlZCBhcyAkXGJldGFfaSwgaSBcaW4gXHsxLDIsLi4uLE5cfSQuCk91ciBncmFuZCBtZWFuICh0aGUgZmluYWwgcG9pbnQgZXN0aW1hdG9yKSAkXGJhcntcYmV0YX0gPSBcZnJhY3sxfXtOfVxzdW1faV5OXGJldGFfaSQsCmFnYWluIGJ5IENMVCwKd2lsbCBoYXZlIGEgc3RhbmRhcmQgZXJyb3Igb2YgJFxmcmFje1xzaWdtYX17XHNxcnR7Tn19JCBhcyB3ZWxsLgpUbyBjYWxjdWxhdGUgdGhlIGVycm9yIHRoZSBwb3B1bGF0aW9uIHN0YW5kYXJkIGRldmlhdGlvbiAkXHNpZ21hJCBpcyBlc3RpbWF0ZWQgYnkgdGhlIHNhbXBsZSBzdGFuZGFyZCBkZXZpYXRpb246CgpgYGB7ciBtY21jX21jc2VfbWVhbn0KbWNzZV9tZWFuIDwtIGFwcGx5KG1jbWNfcmVzdWx0X21iJGJhdGNoWywxOjVdLCAyLCBzZCkgLyBzcXJ0KG1jbWNfcmVzdWx0X21iJG5iYXRjaCkKbWNzZV9tZWFuCmBgYAoKTUNTRSBpcyBhIGdvb2QgbWV0cmljIGZvciB1cyB0byBkZXRlcm1pbmUgb3VyIHNjYWxlIG9mIE1DTUMuClRoYXQgaXMsIGhvdyBsb25nIHdlJ2QgbGlrZSB0byBydW4gdGhlIHNpbXVsYXRpb24uCkEgZ2VuZXJhbCBydWxlIG9mIHRodW1iIGlzIHRvIHJlYWNoIE1DU0UgPCAxJS4KCiMjIyBNQ1NFIG9mIFZhcmlhbmNlIEVzdGltYXRlCgpXZSBub3Qgb25seSBlc3RpbWF0ZSB0aGUgcG9pbnQgb2YgcGFyYW1ldGVyLCBidXQgYWxzbyB0aGUgdmFyaWFuY2UgaWYgaXQuCkFuZCBzaW5jZSB0aGUgZXN0aW1hdGlvbiBpcyBkb25lIGJ5IGZpbml0ZSBNQ01DLApzbyBhZ2FpbiB0aGVyZSB3aWxsIGJlIGFkZGl0aW9uYWwgZXJyb3JzIHNvbGVseSBhdHRyaWJ1dGFibGUgdG8gdGhlIGFkb3B0aW9uIG9mIE1DTUMuCgpUaGUgY2FsY3VsYXRpb24gb2YgTUNTRSBvZiBwYXJhbWV0ZXIgdmFyaWFuY2UgaXMgdHJpY2tpZXIuCldlIG5lZWQgdG8gdXNlIHRoZSBbZGVsdGEgbWV0aG9kXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9EZWx0YV9tZXRob2QpOgoKYGBge3IgbWNtY19tY3NlX3Zhcn0KY2FsY192YXJfbWNzZSA8LSBmdW5jdGlvbih1LCB2LCBuKSB7CiAgIyB1OiBiYXRjaCBtZWFuIG9mIGZpcnN0IG1vbWVudAogICMgdjogYmF0Y2ggbWVhbiBvZiBzZWNvbmQgbW9tZW50CiAgIyB1YmFyOiBncmFuZCBtZWFuIG9mIGJhdGNoIG1lYW4gdSAKICAjIHZiYXI6IGdyYW5kIG1lYW4gb2YgYmF0Y2ggbWVhbiB2CiAgdWJhciA8LSBjb2xNZWFucyh1KQogIHZiYXIgPC0gY29sTWVhbnModikKICBkZWx0YXUgPC0gc3dlZXAodSwgMiwgdWJhcikKICBkZWx0YXYgPC0gc3dlZXAodiwgMiwgdmJhcikKICBmb28gPC0gc3dlZXAoZGVsdGF1LCAyLCB1YmFyLCAiKiIpCiAgc3FydChjb2xNZWFucygoZGVsdGF2IC0gMiAqIGZvbyleMikgLyBuKQp9CgptY3NlX3ZhciA8LSBjYWxjX3Zhcl9tY3NlKHU9bWNtY19yZXN1bHRfbWIkYmF0Y2hbLDE6NV0sIAogICAgICAgICAgICAgICAgICAgICAgICAgIHY9bWNtY19yZXN1bHRfbWIkYmF0Y2hbLDY6MTBdLAogICAgICAgICAgICAgICAgICAgICAgICAgIG49bWNtY19yZXN1bHRfbWIkbmJhdGNoKQptY3NlX3ZhcgpgYGAKClRvIHN1bSB1cCBvdXIgQmF5ZXNpYW4gbW9kZWxpbmcgcmVzdWx0czoKCmBgYHtyIG1jbWNfZmluYWx9CmJheXNpYW5fcmVzdWx0IDwtIHJiaW5kKHBvaW50X2VzdCwgbWNzZV9tZWFuLCB2YXJfZXN0LCBtY3NlX3ZhcikKY29sbmFtZXMoYmF5c2lhbl9yZXN1bHQpIDwtIHNwcmludGYoIlxVMDNCMiVzIiwgMTpsZW5ndGgoYmV0YV9tbGUpIC0gMSkKYmF5c2lhbl9yZXN1bHQKYGBgCgojIyBDcmVkaWJsZSBJbnRlcnZhbAoKQ3JlZGlibGUgaW50ZXJ2YWwgaXMgdGhlIEJheWVzaWFuIGNvdW50ZXJwYXJ0IG9mIHRoZSBjb25maWRlbmNlIGludGVydmFsIGRlZmluZWQgaW4gZnJlcXVlbnRpc3Qgc3RhdGlzdGljcy4KVW5saWtlIHRoZSBlYXNpbHktbWlzdW5kZXJzdG9vZCBjb25maWRlbmNlIGludGVydmFsLApjcmVkaWJsZSBpbnRlcnZhbCBpcyByYXRoZXIgaW50dWl0aXZlLgpBIDk1JSBjcmVkaWJsZSBpbnRlcnZhbCBpcyBzaW1wbHkgdGhlIHJhbmdlIG9mIDIuNSUgcGVyY2VudGlsZSBhbmQgOTcuNSUgcGVyY2VudGlsZSBvbiB0aGUgcG9zdGVyaW9yIGRpc3RyaWJ1dGlvbiBvZiBhIHBhcmFtZXRlci4KClRoYXQgaXMsCnVubGlrZSBmcmVxdWVudGlzdCBjb25maWRlbmNlIGludGVydmFsIGlzIGEgcHJvYmFibGlzdGljIGRlc2NyaXB0aW9uIGFib3V0IHRoZSBpbnRlcnZhbCBpdHNlbGYsCkJheWVzaWFuIGNyZWRpYmxlIGludGVydmFsIGlzIGEgcHJvYmFibGlzdGljIGRlc2NyaXB0aW9uIGFib3V0IHRoZSBwYXJhbWV0ZXIgb2YgaW50ZXJlc3QuClRoZSBhc3RvbmlzaGluZyBkaWZmZXJlbmNlIHJlc3VsdHMgZnJvbSB0aGUgdmVyeSBkaWZmZXJlbnQgcGhpbG9zb3BoeSBhYm91dCBtb2RlbCBwYXJhbWV0ZXI6CkZyZXF1ZW50aXN0cyBzZWUgcGFyYW1ldGVycyBhcyB1bmtub3duIGNvbnN0YW50cyB3aGlsZSBCYXllaXNhbnMgc2VlIHBhcmFtZXRlcnMgYXMgcmFuZG9tIHZhcmlhYmxlcy4KCkZvciBvdXIgZXN0aW1hdGVkIGxvZ2lzdGljIG1vZGVsLAp0aGUgOTUlIGNyZWRpYmxlIGludGVydmFsIGZvciBhbGwgcGFyYW1ldGVycyBjYW4gYmUgb2J0YWluZWQgYXMgdGhlIGZvbGxvd2luZ3MuCgpgYGB7ciBtY21jX2NpfQpiZXRhX2NpIDwtIGFwcGx5KG1jbWNfcmVzdWx0X21iJGJhdGNoWywxOjVdLCAyLCBxdWFudGlsZSwgYyguMDI1LCAuOTc1KSkKY29sbmFtZXMoYmV0YV9jaSkgPC0gc3ByaW50ZigiXFUwM0IyJXMiLCAxOmxlbmd0aChiZXRhX21sZSkgLSAxKQpiZXRhX2NpCmBgYAoKRWFzeT8KVGhlIGNhbGN1bGF0aW9uIGhlcmUgaGFzIGEgcHJvYmxlbSB0aG91Z2guClNpbmNlIHRoZSByZXR1cm5pbmcgcG9zdGVyaW9yIHNhbXBsZXMgYXJlIGJhdGNoIG1lYW5zIG9mIG1pbmkgYmF0Y2hlcywKdGhlIGludGVydmFsIGlzIG5vdCBkZXNjcmliaW5nIHRoZSBvcmlnaW5hbCBkaXN0cmlidXRpb24gYnV0IGluc3RlYWQgdGhlIHNhbXBsaW5nIGRpc3RyaWJ1dGlvbiBvZiBpdHMgbWVhbi4KVG8gY29uc3RydWN0IHRoZSBjcmVkaWJsZSBpbnRlcnZhbCBmb3IgdGhlIHBvc3RlcmlvciB3ZSBuZWVkIHRvIGRvIHNhbXBsaW5nIHdpdGhvdXQgbWluaS1iYXRjaGluZzoKCmBgYHtyIG1jbWNfY2lfbm9fbWluaWJhdGNofQojIEhlcmUgd2UgYWxzbyBkbyBhIGJ1cm4taW4gZm9yIHRoZSBmaXJzdCAxMDAwMCByb3VuZHMuCm1jbWNfcmVzdWx0X25vX2JhdGNoIDwtIG1jbWM6Om1ldHJvcChsdXBfZnVuLCBiZXRhX21sZSwgc2NhbGU9LjUsIG5iYXRjaD0xMDAwMCwgYmxlbj0xKQptY21jX3Jlc3VsdF9ub19iYXRjaCA8LSBtY21jOjptZXRyb3AobWNtY19yZXN1bHRfbm9fYmF0Y2gsIHNjYWxlPS41LCBuYmF0Y2g9MTAwMDAsIGJsZW49MSkKCiMgTm93IHRoZSBjcmVkaWJsZSBpbnRlcnZhbCBpcyBkZXNjcmliaW5nIHRoZSBwb3N0ZXJpb3IgaXRzZWxmLgpiZXRhX2NpMiA8LSBhcHBseShtY21jX3Jlc3VsdF9ub19iYXRjaCRiYXRjaCwgMiwgcXVhbnRpbGUsIGMoLjAyNSwgLjk3NSkpCmNvbG5hbWVzKGJldGFfY2kyKSA8LSBzcHJpbnRmKCJcVTAzQjIlcyIsIDE6bGVuZ3RoKGJldGFfbWxlKSAtIDEpCmJldGFfY2kyCmBgYAoKVGhlIEJheWVzaWFuIGNyZWRpYmxlIGludGVydmFsIHNlZW1zIHRvIGFncmVlIHdpdGggdGhlIGZyZXF1ZW50aXN0IGNvbmZpZGVuY2UgaW50ZXJ2YWwgb24gdGhhdCB0aGUgbGFzdCAyIGNvZWZmaWNpZW50cyBhcmUgbm90IHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQsCnNpbmNlIHRoZSBpbnRlcnZhbCBlbmNvbXBhc3MgMC4KCiMgQmF5ZXNpYW4gTW9kZWxpbmcgd2l0aCBTdGFuCgpbU3Rhbl0oaHR0cHM6Ly9tYy1zdGFuLm9yZy8pIGlzIGEgcHJvYmFiaWxpc3RpYyBwcm9ncmFtbWluZyBsYW5ndWFnZSAoYmFzZWQgb24gQysrKSB0aGF0IGlzIHdpZGVseSBhZG9wdGVkIGluIHByYWN0aWNhbCBiYXllc2lhbiBtb2RlbGluZy4KQmVmb3JlIGl0cyBwcmVzZW5jZSwKdGhlcmUgYXJlIHR3byBvdGhlciBwb3B1bGFyIGFsdGVybmF0aXZlczoKW0JVR1NdKGh0dHBzOi8vd3d3Lm1yYy1ic3UuY2FtLmFjLnVrL3NvZnR3YXJlL2J1Z3MvKSBhbmQgW0pBR1NdKGh0dHA6Ly9tY21jLWphZ3Muc291cmNlZm9yZ2UubmV0LykuCldlIHdpbGwgZm9jdXMgb24gdXNpbmcgU3RhbiBzaW5jZSBpdCBpcyByZWxhdGl2ZWx5IG5ldyB3aXRoIGEgbXVjaCBtb3JlIGFkdmFjbmVkIE1DTUMgYWxnb3JpdGhtIGFuZCBzaG93aW5nIGluY3JlYXNpbmcgaW5mbHVlbmNlIGluIHRoZSBjb21tdW5pdHkgYW5kIGFsc28gaXMgYmV0dGVyIGRvY3VtZW50ZWQgYW5kIHNtb290aGx5IGludGVncmF0ZWQgd2l0aCBhIHZhcmlldHkgb2Ygb3RoZXIgZWNvLXN5c3RlbXMgZm9yIG1vZGVybiBkZXZlbG9wZXJzLgoKW2Byc3RhbmBdKGh0dHBzOi8vZ2l0aHViLmNvbS9zdGFuLWRldi9yc3RhbilbQHN0YW4yMDE4XSBpcyB0aGUgU3RhbiBvZmZpY2FsIFIgQVBJIHRvIGludGVyZmFjZSBkaXJlY3RseSB3aXRoIFN0YW4gbW9kZWwgY29kZSB1c2luZyBSLgpOb3RhYmx5IHRoZXJlIGFyZSBhIGxvdCBtb3JlIGhpZ2hlci1sZXZlbCBwYWNrYWdlcyBiYXNlZCBvbiB0b3Agb2YgYHJzdGFuYCB0byBtYWtlIGxpZmUgZXZlbiBlYXNpZXIsCmF2b2lkaW5nIHRoZSBuZWVkIHRvIHdyaXRlIG5hdGl2ZSBTdGFuIG1vZGVsIGNvZGUgYWxsIHRvZ2V0aGVyLgpJbiB0aGlzIHR1dG9yaWFsIGhvd2V2ZXIgd2Ugd2lsbCBwdXJwb3NlbHkgaWdub3JlIHRoZW0gYW5kIHVzZSBvbmx5IGByc3RhbmAgYWxvbmUgc2luY2Ugd2UnZCBsaWtlIHRvIHVuZGVyc3RhbmQgdGhlIHZlcnkgYmFzaWMgY29uY2VwdCBvZiBob3cgU3RhbiB3b3JrLgpJdCB0dXJucyBvdXQgdGhhdCB0aGlzIGlzIGFsc28gYSB2ZXJ5IGdvb2Qgb3Bwb3J0dW5pdHkgdG8gdGlkeSB1cCBvdXIgdGhvdWdodCBvbiB3aGF0IHdlIGFyZSBleGFjdGx5IG1vZGVsaW5nIGluIGEgQmF5c2lhbiBmcmFtZXdvcmssCndpdGhvdXQgYmVpbmcgYWJzdHJhY3RlZCBhd2F5IHRvbyBmYXIgZHVlIHRvIGhpZ2hlci1sZXZlbCBpbXBsZW1lbnRhdGlvbnMuCgpUbyB3b3JrIHdpdGggU3Rhbiwgd2Ugd3JpdGUgU3RhbiBtb2RlbCBjb2RlIGluIGEgc2VwYXJhdGUgYC5zdGFuYCB0ZXh0IGZpbGUgb3IgYXMgYSBzdHJpbmcgdmFyaWFibGUgaW4gUi4KV2UgdGhlbiBjb21waWxlIHRoZSBjb2RlIHVzaW5nIGByc3RhbmAgQVBJIHdoaWNoIHdpbGwgZ2l2ZSB1cyB0aGUgU3RhbiBvYmplY3QgdG8gaW50ZXJhY3Qgd2l0aCBkaXJlY3RseSB3aXRoaW4gYSBSIHNlc3Npb24uCgpgYGB7ciBzdGFuX2ltcG9ydCwgcmVzdWx0cz0iaGlkZSJ9CmxpYnJhcnkocnN0YW4pCgpyc3Rhbl9vcHRpb25zKGF1dG9fd3JpdGU9VFJVRSkKaWYgKCBTeXMuaW5mbygpWyJzeXNuYW1lIl0gPT0gIldpbmRvd3MiICkKICBTeXMuc2V0ZW52KExPQ0FMX0NQUEZMQUdTPSItbWFyY2g9bmF0aXZlIikKYGBgCgojIyBQcm9iYWJpbGlzdGljIFNpbXVsYXRpb24KCkEgU3RhbiBtb2RlbCBjb2RlIGNvbnRhaW5zIHNldmVyYWwgbWFuZGF0b3J5IGFuZCBvcHRpb25hbCBibG9ja3MuCkNvZGUgYmxvY2sgZm9yIGBkYXRhYCwgYHBhcmFtZXRlcnNgLCBhbmQgYG1vZGVsYCBhcmUgbWFuZGF0b3J5LCBldmVuIGlmIHRoZXkgY29udGFpbiBub3RoaW5nLgoKVGhvdWdoIGl0IGlzIHZlcnkgZWFzeSB0byBydW4gcHJvYmFiaWxpc3RpYyBzaW11bGF0aW9uIHVzaW5nIFIgYWxvbmUsIApoZXJlIHdlIGlsbHVzdHJhdGUgaG93IHRvIGRvIGl0IHVzaW5nIFN0YW4gd2l0aCBpdHMgb3B0aW9uYWwgYGZ1bmN0aW9uYCBjb2RlIGJsb2NrLgpUaGUgZm9sbG93aW5nIFN0YW4gbW9kZWwgY29kZSBpbXBsZW1lbnRzIGEgZnVuY3Rpb24gdG8gZ2VuZXJhdGUgcmFuZG9tIGRyYXdzIGZyb20gYSBzdHVkZW50J3MgdCBkaXN0cmlidXRpb24gYmFzZWQgb24gYSBsaW5lYXIgbW9kZWwgJHkgPSBYXGJldGEgKyBcZXBzaWxvbiQgKGkuZS4sIHRoZSByZXNpZHVhbHMgZm9sbG93IHRoZSBzdHVkZW50J3MgdCBkaXN0cmlidXRpb24pOgoKYGBge3N0YW4gc3Rhbl9kZ3AsIG91dHB1dC52YXI9InN0YW5fZGdwIn0KZnVuY3Rpb25zIHsKICB2ZWN0b3Igc3Rfcm5nKG1hdHJpeCBYLCB2ZWN0b3IgYmV0YSwgcmVhbCBudSwgcmVhbCBzaWdtYSkgewogICAgdmVjdG9yW3Jvd3MoWCldIHk7CiAgICBmb3IgKG4gaW4gMTpyb3dzKFgpKQogICAgICB5W25dID0gc3R1ZGVudF90X3JuZyhudSwgWFtuXSAqIGJldGEsIHNpZ21hKTsKICAgIHJldHVybiB5OwogIH0KfQpkYXRhIHsKfQpwYXJhbWV0ZXJzIHsKfQptb2RlbCB7Cn0KYGBgCgpCeSBydW5uaW5nIGByc3Rhbjo6c3Rhbl9tb2RlbGAgd2UgY2FuIGNvbnZlcnQgdGhlIG5hdGl2ZSBTdGFuIG1vZGVsIGNvZGUgaW50byBSIGBzdGFubW9kZWxgIG9iamVjdC5eW0luIHRoaXMgdHV0b3JpYWwgd2UgdXNlIFJtZCB0byBkaXJlY3RseSBjb21waWxlIHRoZSBTdGFuIGNvZGUuIEZvciBzY3JpcHRpbmcgdXNhZ2UsIG9uZSBzaG91bGQgdXNlIGVpdGhlciBgc3Rhbl9tb2RlbChmaWxlPSJtb2RlbC5zdGFuIilgIGZvciBgc3Rhbl9tb2RlbChtb2RlbF9jb2RlPW1vZGVsX3N0cilgIHRvIGNvbXBpbGUgYW5kIHJldHVybiB0aGUgbW9kZWwgb2JqZWN0IGFjY29yZGluZ2x5Ll0KClNvbWUgbm90ZXMgb24gdGhlIGFib3ZlIFN0YW4gbW9kZWwgY29kZToKCisgVmFyaWFibGVzIGFyZSBzdHJvbmdseSB0eXBlZCwgd2l0aCBkZWNsYXJhdGlvbiBzeW50YXggYDx0eXBlPiBuYW1lYC4KKyBPcGVyYXRvciBgKmAgZm9yIHR3byBgdmVjdG9yYCB2YXJpYWJsZXMgaXMgYSBkb3QgcHJvZHVjdCAod2hpbGUgaW4gUiBpdCBpcyBhbiBlbGVtZW50LXdpc2UgcHJvZHVjdCkuCisgTG9vcHMgYXJlIGZhc3Qgc2luY2UgaXQgaXMgQysrLgoKQWZ0ZXIgY29tcGlsYXRpb24sCmZ1bmN0aW9ucyBkZWZpbmVkIGluIHRoZSBgZnVuY3Rpb25gIGJsb2NrIGNhbiBiZSBleHBvc2VkIHRvIFIgZW52aXJvbm1lbnQgYXMgUiBmdW5jdGlvbnM6CgpgYGB7ciBzdGFuX2V4cG9zZV9zdGFuX2Z1bmMsIHJlc3VsdHM9ImhpZGUifQojIFdlIHN0b3JlIHRoZSBjb21waWxlZCBzdGFuIG1vZGVsIGluIHZhcmlhYmxlIGBzdGFuX2RncGAuCiMgVGhpcyBpcyBkb25lIGRpcmVjdGx5IHZpYSBSbWQgQVBJLgojIEZvciBkZXRhaWxzOiBodHRwczovL2Jvb2tkb3duLm9yZy95aWh1aS9ybWFya2Rvd24vbGFuZ3VhZ2UtZW5naW5lcy5odG1sI3N0YW4KZXhwb3NlX3N0YW5fZnVuY3Rpb25zKHN0YW5fZGdwKQoKIyBWZXJpZnkgdGhhdCB0aGUgU3RhbiBmdW5jdGlvbiBoYXMgYmVlbiBleHBvc2VkIGFzIGEgUiBmdW5jdGlvbi4KZXhpc3RzKCJzdF9ybmciKQpgYGAKCk5vdyB3ZSBjYW4gdXNlIHRoZSBmdW5jdGlvbiBkaXJlY3RseSBpbiBSOgoKYGBge3Igc3Rhbl91c2Vfc3Rhbl9mdW5jfQp5X3NpbSA8LSBzdF9ybmcobnU9MTAsIFg9bWF0cml4KHJub3JtKDEwMCozKSwgMTAwLCAzKSwgc2lnbWE9NSwgYmV0YT1ybm9ybSgzKSkKaGlzdCh5X3NpbSkKYGBgCgpKdXN0IGZvciBjb21wbGV0ZW5lc3MsCnRoZSBzYW1lIGxvZ2ljIGNhbiBvZiBjb3Vyc2UgYmUgZWFzaWx5IGltcGxlbWVudGVkIGluIFI6CgpgYGB7ciBzdGFuX3Byb2Jfc2ltX3JfZXF1aXZhbGVudCwgZXZhbD1GQUxTRX0KWCA8LSBtYXRyaXgocm5vcm0oMTAwKjMpLCAxMDAsIDMpCmJldGEgPC0gcm5vcm0oMykKbnUgPC0gMTAKc2lnbWEgPC0gNQp5X3NpbSA8LSBhcy52ZWN0b3IoWCUqJWJldGEpICsgcnQobnJvdyhYKSwgbnUpKnNpZ21hCmBgYAoKIyMgT25lLVNhbXBsZSBNZWFuIFRlc3QgUmV2aXNpdGVkCgpMZXQncyBpbXBsZW1lbnQgdGhlIG9uZS1zYW1wbGUgdGVzdCB1c2luZyBTdGFuLgoKRmlyc3Qgd2UgcmUtd3JpdGUgdGhlIHRlc3Qgd2l0aCBKWlMgcHJpb3IgYmFzZWQgb24gb3VyIHZlcnkgZmlyc3QgZXhhbXBsZToKCiQkClxiZWdpbnthbGlnbmVkfQpcYmVnaW57Y2FzZXN9ClxkZWx0YSAmPSBcZnJhY3tcbXV9e1xzaWdtYX0sIFxcClAoXHNpZ21hXjIpICZccHJvcHRvIFxmcmFjezF9e1xzaWdtYV4yfSwgXFwKSF8wICY6IFxkZWx0YSA9IDAsIFxcCkhfMSAmOiBcZGVsdGEgXHNpbSByIFxjZG90IFxtYm94e0NhdWNoeX0uClxlbmR7Y2FzZXN9ClxlbmR7YWxpZ25lZH0KJCQKCkZvciBub3RhdGlvbmFsIHNpbXBsaWNpdHksCndlIHNoaWZ0IG91ciBvcmlnaW5hbCByYW5kb20gc2FtcGxlcyBieSB0aGUgbnVsbCB2YWx1ZSBvZiBgciBoMF9tZWFuYCwKc3VjaCB0aGF0IG91ciBudWxsIG1vZGVsIGhhcyAkXG11ID0gMCQuCgpgYGB7ciBzdGFuX3NpbXBsZV9kYXRhX3N0ZH0KWSA8LSBYMSAtIGgwX21lYW4KYGBgCgpJbiBvcmRlciB0byBnZW5lcmF0ZSBNQ01DIHNhbXBsZXMsCm91ciBwcmlvciBtdXN0IGJlIGZ1bGx5IHNwZWNpZmllZC4KTm93IHdlIHdpbGwgYWxzbyBhc3N1bWUgJFkgXHNpbSBcbWJveHtOb3JtYWx9KFxtdSwgXHNpZ21hKSQuCldlIGFscmVhZHkga25vdyB0aGF0IHRoaXMgaXMgbm90IHRydWUgc2luY2Ugd2Ugc2V0dXAgb3VyIHNhbmRib3ggcHJvYmxlbSBzdWNoIHRoYXQgJFkgXHNpbSBcbWJveHtCZXRhfSgyLCAxMCkkLgpJbiByZWFsaXR5LAp3ZSB3aWxsIG5ldmVyIGdldCB0byBrbm93IHRoZSB0cnVlIHBvcHVsYXRpb24gYW55d2F5LgpJbiBCYXllc2lhbiBmcmFtZXdvcmssCndlIGhhdmUgdGhlIGZsZXhpYmlsaXR5IHRvIGRpY3RhdGUgb3VyIHByaW9yIG9uIHRoYXQuCldpdGggbm8gYWRkaXRpb25hbCBpbmZvcm1hdGlvbiB3ZSB1c3VhbGx5IHJlc29ydCB0byBhIE5vcm1hbCBkaXN0cmlidXRpb24gYXMgYSBiYXNlbGluZSBpbiBtYW55IGNhc2VzLgpTbyBoZXJlIHRoZSBOb3JtYWwgaXQgaXMuXltBcyBhIGNvbXBhcmlzb24sIGluIHRoZSBmcmVxdWVudGlzdCBhcHByb2FjaCBpbiB0aGlzIGNhc2Ugd2UgZG9uJ3QgbmVlZCB0byBmdXJ0aGVyIGFzc3VtZSB0aGUgcG9wdWxhdGlvbiBkaXN0cmlidXRpb24gdGhhbmtzIHRvIHRoZSBDTFQuIFdlIG9ubHkgbmVlZCB0byBsb29zZWx5IGFzc3VtZSB0aGF0IHRoZSB2YXJpYW5jZSBpcyBmaW5pdGUgZm9yIHRoZSBwb3B1bGF0aW9uLCBzdWNoIHRoYXQgQ0xUIGFwcGxpZXMuXQoKVGhlIG51bGwgbW9kZWwgYWJvdmUgY2FuIGJlIGRlZmluZWQgaW4gU3RhbiBhczoKCmBgYHtzdGFuIG9uZV9zYW1wbGVfdHRlc3RfaDAsIG91dHB1dC52YXI9InR0ZXN0X2gwIn0KZGF0YSB7CiAgaW50IG47CiAgdmVjdG9yW25dIHk7Cn0KcGFyYW1ldGVycyB7CiAgcmVhbDxsb3dlcj0wPiBzaWdtYTI7Cn0KdHJhbnNmb3JtZWQgcGFyYW1ldGVycyB7CiAgcmVhbCBzaWdtYSA9IHNxcnQoc2lnbWEyKTsKfQptb2RlbCB7CiAgdGFyZ2V0ICs9IGxvZygxL3NpZ21hMik7CiAgdGFyZ2V0ICs9IG5vcm1hbF9scGRmKHkgfCAwLCBzaWdtYSk7Cn0KZ2VuZXJhdGVkIHF1YW50aXRpZXMgewogIHZlY3RvcltuXSBsb2dfbGlrOwogIGZvciAoaSBpbiAxOm4pIHsKICAgIGxvZ19saWtbaV0gPSBub3JtYWxfbHBkZih5W2ldIHwgMCwgc2lnbWEpOwogIH0KfQpgYGAKCkFuZCB0aGUgYWx0ZXJuYXRpdmUgbW9kZWw6CgpgYGB7c3RhbiBvbmVfc2FtcGxlX3R0ZXN0X2gxLCBvdXRwdXQudmFyPSJ0dGVzdF9oMSJ9CmRhdGEgewogIGludCBuOwogIHZlY3RvcltuXSB5OwogIHJlYWwgcjsKfQpwYXJhbWV0ZXJzIHsKICByZWFsIGRlbHRhOwogIHJlYWw8bG93ZXI9MD4gc2lnbWEyOwp9CnRyYW5zZm9ybWVkIHBhcmFtZXRlcnMgewogIHJlYWwgc2lnbWEgPSBzcXJ0KHNpZ21hMik7CiAgcmVhbCBtdSA9IGRlbHRhKnNpZ21hOwp9Cm1vZGVsIHsKICB0YXJnZXQgKz0gY2F1Y2h5X2xwZGYoZGVsdGEgfCAwLCByKTsKICB0YXJnZXQgKz0gbG9nKDEvc2lnbWEyKTsKICB0YXJnZXQgKz0gbm9ybWFsX2xwZGYoeSB8IG11LCBzaWdtYSk7Cn0KZ2VuZXJhdGVkIHF1YW50aXRpZXMgewogIHZlY3RvcltuXSBsb2dfbGlrOwogIGZvciAoaSBpbiAxOm4pIHsKICAgIGxvZ19saWtbaV0gPSBub3JtYWxfbHBkZih5W2ldIHwgbXUsIHNpZ21hKTsKICB9Cn0KYGBgCgpTb21lIG1vcmUgdHJpY2tzIGluIHdyaXRpbmcgU3RhbiBtb2RlbCBjb2RlIGlzIHN1bW1hcml6ZWQgYmVsb3c6CgorIGBwYXJhbWV0ZXJzYCBibG9jawogICsgVG8gZGVmaW5lIG1vZGVsIHBhcmFtZXRlcnMgc3ViamVjdCB0byBNQ01DOyBmb3IgbW9kZWwgaHlwZXJwYXJhbWV0ZXIgdXNlIGBkYXRhYCBibG9jayB0byBwYXNzIHRocm91Z2guCiAgKyBUbyBib3VuZCB2YXJpYWJsZSB2YWx1ZSwgYWRkIGA8bG93ZXI9PmAgb3IgYDxoaWdoZXI9PmAgc3VmZml4IGluIHRoZSB0eXBlIGRlY2xhcmF0aW9uLgorIGB0cmFuc2Zvcm1lZCBwYXJhbWV0ZXJzYCBibG9jawogICsgaXMgdXNlZnVsIGZvciBkZXJpdmVkIHBhcmFtZXRlcnMgYW5kIGluIHNvbWUgY2FzZXMgY2FuIGJlbmVmaXQgZnJvbSBwZXJmb3JtYW5jZSBzcGVlZHVwLgorIGBtb2RlbGAgYmxvY2sKICArIFRvIGRlZmluZSBhIGRpc3RyaWJ1dGlvbiBwdXJlbHkgZm9yIHNhbXBsaW5nIHB1cnBvc2UsIHVzZSB0aGUgb3BlcmF0b3IgYH5gLgogICsgVG8gZGVmaW5lIGEgZGlzdHJpYnV0aW9uIHVzZWQgZm9yIGNhbGN1bGF0aW5nIGxpa2VsaWhvb2QsIHVzZSBgdGFyZ2V0ICs9ICpfbHBkZigpYCBub3RhdGlvbi4KICArIEZvciBwYXJhbWV0ZXJzIHRoYXQgYXJlIG5vdCBzcGVjaWZpZWQsIHRgaGV5IGFyZSBhc3N1bWVkIHRvIGZvbGxvdyBhIFVuaWZvcm0gcHJpb3IuCisgYGdlbmVyYXRlZCBxdWFudGl0aWVzYCBibG9jawogICsgQ2FuIGJlIHVzZWQgdG8gY2FsY3VsYXRlIG91dHB1dC4gSXQgd2lsbCBhcHBlYXIgaW4gdGhlIHJlc3VsdGluZyBgc3RhbmZpdGAgb2JqZWN0IGFzIGFkZGl0aW9uYWwgcGFyYW1ldGVycy4gQ29tbW9uIG91dHB1dCB3aWxsIGJlIHRoZSBkYXRhIGxpa2VsaWhvb2Qgb3IgdGhlIHNpbXVsYXRlZCBtb2RlbCBvdXRjb21lIChCYXllc2FpbiBwcmVkaWNhdGVzKS4KCkluIHRoZSBgbW9kZWxgIGJsb2NrIHdoZW4gd2Ugd3JpdGUgZG93biBzb21ldGhpbmcgbGlrZSBgeSB+IG5vcm1hbCgwLCAxKWAgaW4gU3RhbiBtb2RlbCBjb2RlIGl0IGlzIGluZGVlZCBhIHZlY3Rvcml6ZWQgaW5zdHJ1Y3Rpb24gdG8gY2FsY3VsYXRlIHRoZSBzdW0gb2YgYWxsIGxvZy1saWtlbGlob29kIGZyb20gdGhlIHNwZWNpZmllZCBkaXN0cmlidXRpb24uCklmIHdlIGluc3RlYWQgd3JpdGUgYHRhcmdldCArPSBub3JtYWxfbHBkZih5IHwgMCwgMSlgIHRoZSBvbmx5IGRpZmZlcmVuY2UgaXMgdGhhdCB0aGUgZm9ybWVyIGRyb3BzIGFsbCBjb25zdGFudCB0ZXJtcyAobm90IHVzZWQgZm9yIHNhbXBsaW5nIHB1cnBvc2UpIGFuZCB0aGUgbGF0dGVyIGtlZXBzIHRoZW0gd2VsbC4KCkJ5IGRlZmF1bHQgdGhlIE1DTUMgbWV0aG9kIHVzZWQgaW4gU3RhbiBpcyBhbiBlbmhhbmNlZCB2ZXJzaW9uIG9mIEhhbWlsdG9uaWFuIE1vbnRlIENhcmxvLApjYWxsZWQgdGhlIE5vLVUtVHVybiBzYW1wbGVyIFtAaG9mZm1hbjIwMTRub10uCldlIGNhbiBwZXJmb3JtIHRoZSBzYW1wbGluZyBieSBqdXN0IG9uZSBmdW5jdGlvbiBjYWxsIHVzaW5nIHRoZSBjb21waWxlZCBTdGFuIG1vZGVsOgoKYGBge3Igc3Rhbl9vbmVfc2FtcGxlX3Rlc3Rfc2FtcGxpbmd9CiMgV2Ugc3RvcmUgdGhlIGNvbXBpbGVkIHN0YW4gbW9kZWwgaW4gdHRlc3RfaDAgYW5kIHR0ZXN0X2gxLCByZXNwZWN0aXZlbHkuCiMgVGhpcyBpcyBkb25lIGRpcmVjdGx5IHZpYSBSbWQgQVBJLgojIEZvciBkZXRhaWxzOiBodHRwczovL2Jvb2tkb3duLm9yZy95aWh1aS9ybWFya2Rvd24vbGFuZ3VhZ2UtZW5naW5lcy5odG1sI3N0YW4Kc3Rhbl9oMCA8LSByc3Rhbjo6c2FtcGxpbmcodHRlc3RfaDAsIGRhdGE9bGlzdCh5PVksIG49bGVuZ3RoKFkpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgaXRlcj0xMDAwMCwgY2hhaW5zPTQsIGNvcmVzPTEsIHNlZWQ9Nzc3LAogICAgICAgICAgICAgICAgICAgICAgICAgICByZWZyZXNoPTApCnN0YW5faDEgPC0gcnN0YW46OnNhbXBsaW5nKHR0ZXN0X2gxLCBkYXRhPWxpc3QoeT1ZLCBuPWxlbmd0aChZKSwgcj0xKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgaXRlcj0xMDAwMCwgY2hhaW5zPTQsIGNvcmVzPTEsIHNlZWQ9Nzc3LAogICAgICAgICAgICAgICAgICAgICAgICAgICByZWZyZXNoPTApCmBgYAogClNldmVyYWwgdGhpbmdzIHRvIG5vdGUgYWJvdXQgdGhlIGByc3Rhbjo6c2FtcGxpbmdgIEFQSToKCisgVmFyaWFibGVzIGRlZmluZWQgaW4gdGhlIGBkYXRhYCBibG9jayBtdXN0IGJlIHBhc3NlZCB0aHJvdWdoIGEgYG5hbWVkIGxpc3RgLgorIE11bHRpcGxlIGNoYWlucyBjYW4gYmUgc2V0LCB3aXRoIHN1cHBvcnQgZm9yIHBhcmFsbGVsbGl6YXRpb24gYW1vbmcgQ1BVIGNvcmVzLgorIFVzZSBgcmVmcmVzaCA9IDBgIHRvIHN1cHByZXNzIHRoZSBvdmVyd2hlbG1pbmcgcHJvZ3Jlc3MgbG9ncyB3aGVudmVyIGFwcHJvcHJpYXRlLgoKV2UgY2FuIGRvIGEgbG9mIG9mIHBvc3QgcHJvY2Vzc2luZyBvbiB0aGUgcmVzdWx0aW5nIGBzdGFuZml0YCBvYmplY3QuCgpUbyBleHRyYWN0IHRoZSBkYXRhIGxpa2VsaWhvb2Qgd2UgZGVmaW5lZCBpbiBgZ2VuZXJhdGVkIHF1YW50aXRpZXNgIGJsb2NrOgoKYGBge3Igc3Rhbl9leHRyYWN0X2xvZ19saWt9CiMgSWYgdGhlIHNhbXBsZXIgY29udGFpbnMgbXVsdGlwbGUgY2hhaW5zLCB0aGV5IGFyZSBzdGFja2VkIHRvZ2V0aGVyLgpsb2dfbGlrX2gwIDwtIGFzLm1hdHJpeChzdGFuX2gwLCBwYXJzPSJsb2dfbGlrIikKc3RyKGxvZ19saWtfaDApCmBgYAoKVG8gcHJpbnQgdGhlIHN1bW1hcnkgb2YgQmF5ZXNpYW4gZXN0aW1hdGlvbiBmb3IgcGFyYW1ldGVyczoKCmBgYHtyIHN0YW5fcHJpbnRfaDFfcGFyfQpwcmludChzdGFuX2gxLCBwYXJzPWMoImRlbHRhIiwgInNpZ21hMiIsICJscF9fIikpCmBgYAoKVG8gZXh0cmFjdCBwYXJhbWV0ZXIgTUNNQyBzYW1wbGVzOgoKYGBge3Igc3Rhbl9leHRyYWN0X3Bhcn0KaDFfcGFyYW1zIDwtIHJzdGFuOjpleHRyYWN0KHN0YW5faDEsIHBhcj0ibG9nX2xpayIsIGluY2x1ZGU9RkFMU0UpCm5hbWVzKGgxX3BhcmFtcykKbWVhbihoMV9wYXJhbXMkZGVsdGEpICAjIENoZWNrIGlmIHRoZSByZXBvcnRlZCBtZWFuIGlzIGRlcml2ZWQgZnJvbSB0aGUgc2FtcGxlLgpgYGAKCkZvciBhbGwgYXBwbGljYWJsZSBtZXRob2RzIGZvciBhIGBzdGFuZml0YCBvYmplY3Qgb25lIGNhbiByZWZlciB0byBgYD9gc3RhbmZpdGBgLgoKV2UgY2FuIG5vdyB1c2UgYnJpZGUgc2FtcGxpbmcgdG8gZXN0aW1hdGUgdGhlIG1hcmdpbmFsIGxpa2VsaWhvb2Qgb2Ygb3VyIG1vZGVscy4KUGFja2FnZSBgYnJpZGdlc2FtcGxpbmdgIGRpcmVjdGx5IHN1cHBvcnQgYHJzdGFuYCBzbyBpdCBpcyBlbWJhcnJhc3NpbmdseSBlYXN5IHRvIHBlcmZvcm0gdGhlIGVzdGltYXRpb246CgpgYGB7ciBzdGFuX2JyaWRnZV9zYW1wbGluZ30KaDBfbWFyZ2luYWxfbGlrIDwtIGJyaWRnZXNhbXBsaW5nOjpicmlkZ2Vfc2FtcGxlcihzdGFuX2gwLCBzaWxlbnQ9VFJVRSkKaDFfbWFyZ2luYWxfbGlrIDwtIGJyaWRnZXNhbXBsaW5nOjpicmlkZ2Vfc2FtcGxlcihzdGFuX2gxLCBzaWxlbnQ9VFJVRSkKCiMgQ2FsY3VsYXRlIHRoZSBCYXllcyBmYWN0b3IuCmJmKGgxX21hcmdpbmFsX2xpaywgaDBfbWFyZ2luYWxfbGlrKQpgYGAKCldlIGNhbiBjaGVjayBpZiB0aGUgcmVzdWx0IGlzIGFsaWduZWQgd2l0aCB0aGUgb25lIGNhbGN1bGF0ZWQgZnJvbSBwYWNrYWdlIGBCYXllc0ZhY3RvcmA6CgpgYGB7ciBzdGFuX2NoZWNrX2JmfQpCYXllc0ZhY3Rvcjo6dHRlc3RCRihZLCBtdT0wLCByPTEpCmBgYAoKIyMgQmF5ZXNpYW4gTG9naXN0aWMgUmVncmVzc2lvbgoKTGV0J3MgcmVwZWF0IHRoZSBleGVyY2lzZSB3ZSBwcmV2aW91c2x5IGRpZCB3aXRoIHRoZSBSIHBhY2thZ2UgYG1jbWNgLApidXQgbm93IHVzZSBgcnN0YW5gLgoKV2UgcmV3cml0ZSB0aGUgc2ltcGxlIG1vZGVsIGhlcmU6CgokJApcYmVnaW57YWxpZ25lZH0KUCh5ID0gMSkgJj0gXGZyYWN7MX17MStlXnstKFhcYmV0YSl9fSwgXFwKXGJldGEgJlxzaW0gXG1ib3h7Tm9ybWFsfSgwLCAxKS4KXGVuZHthbGlnbmVkfQokJAoKQ29udmVydCBpdCBpbnRvIFN0YW4gbW9kZWwgY29kZToKCmBgYHtzdGFuIHN0YW5fbG9naXRfbW9kZWwsIG91dHB1dC52YXI9ImxvZ2l0X21vZGVsIn0KZGF0YSB7CiAgaW50IG47CiAgbWF0cml4W24sIDVdIFg7CiAgaW50IHlbbl07Cn0KcGFyYW1ldGVycyB7CiAgdmVjdG9yWzVdIGJldGE7Cn0KbW9kZWwgewogIGJldGEgfiBub3JtYWwoMCwgMSk7CiAgeSB+IGJlcm5vdWxsaV9sb2dpdChYKmJldGEpOwp9CmBgYAoKTm90ZXMgb24gdGhlIGFib3ZlIFN0YW4gbW9kZWwgY29kZToKCisgRm9yIGludGVnZXIgdmVjdG9yIG9mIGxlbmd0aCBuLCB1c2UgYGludCB5W25dYCBpbnN0ZWFkIG9mIGB2ZWN0b3Jbbl0geWAgc2luY2UgYHZlY3RvcmAgb25seSBhcHBsaWVzIHRvIHJlYWxzLgorIFVzZSBgYmVybm91bGxpX2xvZ2l0YCBkaXJlY3RseSBmb3IgbG9naXN0aWMgZnVuY3Rpb24uCgpOb3cgZG8gTUNNQyBhbmQgcHJpbnQgdGhlIG1vZGVsaW5nIHJlc3VsdDoKCmBgYHtyIHN0YW5fbG9naXN0aWNfZml0fQpmaXR0ZWRfYmxvZ2l0IDwtIHJzdGFuOjpzYW1wbGluZygKICBsb2dpdF9tb2RlbCwgZGF0YT1saXN0KFg9Zml0dGVkX21vZGVsJHgsIHk9Zml0dGVkX21vZGVsJHksIG49bGVuZ3RoKGZpdHRlZF9tb2RlbCR5KSksCiAgaXRlcj0xMDAwMCwgY2hhaW5zPTQsIGNvcmVzPTEsIHNlZWQ9Nzc3LCByZWZyZXNoPTApCgpmaXR0ZWRfYmxvZ2l0CmBgYAoKT25lIGNhbiBjb21wYXJlIHRoZSByZXN1bHQgd2l0aCB0aGF0IG9mIGBtY21jYCBpbiBvdXIgcHJldmlvdXMgc2VjdGlvbi4KVGhleSBzaG91bGQgc2hvdyBxdWFsaXRhdGl2ZWx5IHNpbWlsYXIgcmVzdWx0IGV2ZW4gdGhvdWdoIHRoZSB1bmRlcmx5aW5nIHNhbXBsZXIgaXMgZGlmZmVyZW50LgoKIyMgQmF5ZXNpYW4gTW9kZWwgRGlhZ25vc2l0aWNzCgpUaGUgU3RhbiBvZmZpY2FsIGNvbWVzIHdpdGggYSB2ZXJ5IGhhbmR5IEdVSSB0b29sIGZvciBpbnZlc3RpZ2F0aW9uIHdpdGggU3RhbiBtb2RlbCBmaXQ6CmBzaGlueXN0YW5gIFtAc2hpbnlzdGFuMjAxOF0uCgpGb3IgdGhlIG1vZGVsIHdlIGp1c3QgZml0dGVkLCB3ZSBjYW4gc2ltcGx5IHJ1bjoKCmBgYHtyIHN0YW5fc2hpbnksIGV2YWw9RkFMU0V9CnNoaW55c3Rhbjo6bGF1bmNoX3NoaW55c3RhbihmaXR0ZWRfYmxvZ2l0KQpgYGAKCmFuZCBiZSBwcmVwYXJlZCB0byBnZXQgeW91ciBtaW5kIGJsb3duLiA6KQoKIyMgQmF5ZXNpYW4gTGluZWFyIFJlZ3Jlc3Npb24KCkZvciBjb21wbGV0ZW5lc3MsCmxldCdzIGFsc28gZG8gYW4gZXhlcmNpc2Ugb24gYSBsaW5lYXIgbW9kZWwgd2l0aCBTdGFuOgoKJCQKeSA9IFggXGJldGEgKyBcZXBzaWxvbi4KJCQKClRoZSBsaW5lYXIgbW9kZWwgaXMgTk9UIGEgcHJvYmFiaWxpc3RpYyBtb2RlbCB1bnRpbCB3ZSBtYWtlIGFzc3VtcHRpb24gYWJvdXQgaXRzIG1vZGVsIHJlc2lkdWFsICRcZXBzaWxvbiQuCkZvciBleGFtcGxlLAp3ZSBjYW4gYXNzdW1lIHRoZSByZXNpZHVhbCB0byBmb2xsb3cKCiQkClxlcHNpbG9uIFxzaW0gXG1ib3h7U3R1ZGVudCdzIHR9KFxudSwgMCwgXHNpZ21hKSwKJCQKCndoaWNoIGdpdmVzIHVzCgokJAp5IFxzaW0gXG1ib3h7U3R1ZGVudCdzIHR9KFxudSwgWFxiZXRhLCBcc2lnbWEpLAokJAoKd2hlcmUgJFxudSQgaXMgdGhlIGRlZ3JlZXMgb2YgZnJlZWRvbSBvZiB0aGUgc3R1ZGVudCdzIHQgZGlzdHJpYnV0aW9uLiAKTm93IHRoZSBsaW5lYXIgbW9kZWwgaGFzIHR1cm5lZCBpbnRvIGEgcHJvYmFiaWxpc3RpYyBtb2RlbC4KT3VyIG1vZGVsIHBhcmFtZXRlcnMgaW4gdGhpcyBjYXNlIHdpbGwgYmUgJFx0aGV0YSA9IChcYmV0YSwgXHNpZ21hKSQuCldlIG5lZWQgdG8gc2V0dXAgb3VyIHByaW9ycyBvbiB0aGVtLgpUbyBleHBsb3JlIG1vcmUgcG9zc2liaWxpdHkgd2l0aCBTdGFuIGxldCdzIGhhdmUgYSBwcmlvciBzdWNoIHRoYXQgdGhlIGZpcnN0ICRcYmV0YSQgY29lZmZpY2llbnQgaXMgbm9ubmVnYXRpdmVseSBub3JtYWxseSBkaXN0cmlidXRlZDoKCiQkClxiZXRhX3sxfSBcc2ltXG1ib3h7Tm9ybWFsfV97K30oXG11X3sxfSwgXHNpZ21hX3sxfSksCiQkCgp3aGlsZSB0aGUgcmVzdCBvZiB0aGVtIGFyZSBqdXN0IE5vcm1hbDoKCiQkClxiZXRhX3twfSBcc2ltIFxtYm94e05vcm1hbH0oXG11X3twfSwgXHNpZ21hX3twfSksIFxmb3JhbGwgcCBcaW4gXHsyLCAuLi4sIFBcfS4KJCQKCkl0J3MgcXVpdGUgY29tbW9uIHRvIHVzZSBhIHRydW5jYXRlZCBDYXVjaHkgcHJpb3Igb24gdmFyaWFuY2UuCihBbm90aGVyIHBvcHVsYXIgY2hvaWNlIGlzIGludmVyc2UtY2hpLXNxdWFyZWQuKQoKJCQKXHNpZ21hIFxzaW1cbWJveHtDYXVjaHl9X3srfShcbXVfe1xzaWdtYX0sIFxnYW1tYV97XHNpZ21hfSkuCiQkCgpIZXJlICRcbXVfe1xzaWdtYX0kIGFuZCAkXGdhbW1hX3tcc2lnbWF9JCBhcmUgY29uc3RhbnRzIHRoYXQgcGFyYW1ldGVyaXplIHRoZSBwcmlvci4KCk5vdyBjcmVhdGUgc29tZSBmYWtlIGRhdGEgZm9yIHRoaXMgbW9kZWxpbmcgZXhlcmNpc2UuCgpgYGB7ciBzdGFuX2xtX2Zha2VfZGF0YX0Kc2V0LnNlZWQoNzc3KQoKIyBOdW1iZXIgb2YgZGF0YS4KTiA8LSAxMDAwCiMgTnVtYmVyIG9mIGZlYXR1cmVzLgpQIDwtIDUKIyBPYnNlcnZlZCBkYXRhIHdpdGggYSBjb25zdGFudCB0ZXJtLgpYIDwtIGNiaW5kKDEsIG1hdHJpeChybm9ybShOKihQLTEpKSwgTiwgUC0xKSkKY29sbmFtZXMoWCkgPC0gc3ByaW50ZigiXFUwM0IyJXMiLCAxOlApCiMgTW9kZWwgcGFyYW1ldGVycy4KbnUgPC0gNQpzaWdtYSA8LSA1CmJldGEgPC0gcm5vcm0oUCkKYmV0YVsxXSA8LSBhYnMoYmV0YVsxXSkKbmFtZXMoYmV0YSkgPC0gY29sbmFtZXMoWCkKIyBTaW11bGF0ZWQgb3V0Y29tZS4Kb3V0Y29tZSA8LSBhcy52ZWN0b3IoWCUqJWJldGEpICsgcnQobnJvdyhYKSwgbnUpKnNpZ21hClh5IDwtIGNiaW5kKG91dGNvbWUsIFgpCgpoZWFkKFh5KQpgYGAKCmBgYHtyIHN0YW5fbG1fZmFrZV9jb2VmfQojIFRydWUgbW9kZWwgY29lZmZpY2llbnRzLgpiZXRhCmBgYAoKRmlyc3Qgd2UgcmVwb3J0IHRoZSByZWdyZXNzaW9uIHJlc3VsdCBmcm9tIHRoZSBmcmVxdWVudGlzdCBhcHByb2FjaCAobm90IGNvbnN0cmFpbmVkIGJ5IHRoZSBwcmlvcik6CgpgYGB7ciBzdGFuX2xtX2ZyZXFfZml0fQpsbV9maXRfZnJlcSA8LSBsbShvdXRjb21lIH4gLiAtIDEsIGRhdGE9YXMuZGF0YS5mcmFtZShYeSkpCnN1bW1hcnkobG1fZml0X2ZyZXEpCmBgYAoKVGhlIHRoZSBTdGFuIGNvZGUgZm9yIEJheWVzaWFuIGFwcHJvYWNoOiAKCmBgYHtzdGFuIHN0YW5fbG0sIG91dHB1dC52YXI9ImJsbSJ9CmRhdGEgewogIGludCBOOwogIGludCBQOwogIG1hdHJpeFtOLCBQXSBYOwogIHZlY3RvcltOXSB5Owp9CnBhcmFtZXRlcnMgewogIHJlYWw8bG93ZXIgPSAwPiBiZXRhXzE7CiAgdmVjdG9yW1AtMV0gYmV0YV8yOwogIHJlYWw8bG93ZXI9MD4gc2lnbWE7CiAgcmVhbDxsb3dlcj0wPiBudTsKfQp0cmFuc2Zvcm1lZCBwYXJhbWV0ZXJzIHsKICB2ZWN0b3JbUF0gYmV0YTsKICBiZXRhID0gYXBwZW5kX3JvdyhyZXBfdmVjdG9yKGJldGFfMSwgMSksIGJldGFfMik7Cn0KbW9kZWwgewogIHRhcmdldCArPSBub3JtYWxfbHBkZihiZXRhIHwgMCwgNSk7CiAgdGFyZ2V0ICs9IGNhdWNoeV9scGRmKHNpZ21hIHwgMCwgMi41KTsKICB0YXJnZXQgKz0gY2F1Y2h5X2xwZGYobnUgfCA3LCA1KTsKICB0YXJnZXQgKz0gc3R1ZGVudF90X2xwZGYoeSB8IG51LCBYKmJldGEsIHNpZ21hKTsKfQpnZW5lcmF0ZWQgcXVhbnRpdGllcyB7CiAgdmVjdG9yW05dIHlfc2ltOwogIGZvciAoaSBpbiAxOk4pIHsKICAgIHlfc2ltW2ldID0gc3R1ZGVudF90X3JuZyhudSwgWFtpLF0qYmV0YSwgc2lnbWEpOwogIH0KfQpgYGAKCkFnYWluIHNvbWUgYWRkaXRpb25hbCBub3RlcyBvbiBTdGFuIG1vZGVsIGNvZGluZzoKCisgVG8gY29uY2F0ZW5hdGUgdHdvIGB2ZWN0b3JgIHVzZSBgYXBwZW5kX3Jvd2AuIFNjYWxhciBjYW5ub3QgYmUgZGlyZWN0bHkgY29uY2F0ZW5hdGVkIHRvIGB2ZWN0b3JgIHNvIHdlIG5lZWQgdG8gY29udmVydCBpdCBpbnRvIGEgYHZlY3RvcmAgZmlyc3QgYnkgdXNpbmcgYHJlcF92ZWN0b3IoKWAuCisgVGhpcyB0aW1lIHdlIHVzZSBgZ2VuZXJhdGVkIHF1YW50aXRpZXNgIHRvIGFsc28gY2FsY3VsYXRlIEJheWVzaWFuIG1vZGVsIHByZWRpY2F0ZXMuIFRoZXkgYXJlIHJhbmRvbSBkcmF3cyBmcm9tIHRoZSBwcmVkaWN0ZWQgZGlzdHJpYnV0aW9uIG9mIG91ciBtb2RlbC4KCk5vdGUgdGhhdCB3ZSBoYXJkY29kZSBvdXIgcHJpb3IgcGFyYW1ldGVyIGZvciBhbGwgJFxiZXRhJHMgZm9yIHNpbXBsaWNpdHkuCldlIGNhbiBpbnN0ZWFkIHBhc3MgdGhlbSBpbnRvIFN0YW4gbW9kZWwgYnkgdXNpbmcgYGRhdGFgIGJsb2NrLgoKYGBge3Igc3Rhbl9sbV9iYXllc19maXR9CmJsbV9maXR0ZWQgPC0gcnN0YW46OnNhbXBsaW5nKGJsbSwgZGF0YT1saXN0KE49TiwgUD1QLCBYPVgsIHk9b3V0Y29tZSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGl0ZXI9MTAwMDAsIGNoYWlucz00LCBjb3Jlcz0xLCBzZWVkPTc3NywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVmcmVzaD0wKQoKcHJpbnQoYmxtX2ZpdHRlZCwgcGFycz1jKCJiZXRhIiwgInNpZ21hIiwgIm51IikpCmBgYAoKIyBSZWZlcmVuY2VzCg==