In his notebook we are going to try and get an intuition for the stepping stone approach (Xie et al., 2011) to compute the marginal likelihood of a model.

library(matrixStats)

Our question

We are going to study the letters that Darwin wrote during a 10 year period 1. We have the number of letters he wrote per week, and we would like to estimate the rate with which he wrote those letters.

The data

Here is the data:

n_letters <- c(13,17,14,10,13,16,14,14,11,14,9,14,13,10,11,12,17,15,23,16,13,14,12,16,15,19,14,11,15,14,17,20,27,21,12,6,18,10,8,12,11,14,14,15,16,17,12,13,17,10,16,20,15,12,24,10,17,14,14,13,15,12,14,17,9,16,27,10,16,9,15,11,16,11,17,17,11,20,23,12,18,26,12,17,16,12,11,16,21,19,20,19,15,15,7,14,12,12,15,10,19,19,17,15,17,20,17,17,11,11,17,16,15,12,16,17,14,18,15,15,10,17,12,16,22,20,23,16,11,19,16,22,13,14,11,10,19,16,19,15,16,16,16,19,14,19,14,10,7,11,12,9,13,18,23,13,16,11,15,13,15,19,16,21,14,14,14,12,15,16,18,15,15,13,15,19,11,17,13,9,13,16,16,13,13,13,18,21,14,14,13,8,21,20,15,20,10,16,18,14,21,15,20,15,15,9,16,16,11,19,12,31,19,18,13,11,14,14,11,10,11,12,13,17,14,14,25,17,18,16,18,11,23,23,14,14,11,17,11,12,18,15,15,13,16,18,15,16,14,11,11,17,18,14,20,17,16,13,12,14,14,18,16,21,16,15,18,19,22,16,15,10,12,11,9,18,11,13,5,13,16,14,12,23,19,14,17,11,17,18,17,16,11,22,18,20,11,12,9,8,12,12,17,16,12,20,19,15,9,14,11,13,13,23,16,10,17,14,20,11,20,15,20,15,19,19,16,15,14,18,24,18,12,16,10,18,16,19,13,13,13,22,13,15,17,17,20,18,18,12,13,14,17,15,17,20,18,22,23,13,16,15,18,10,19,12,14,19,14,19,23,19,22,8,14,20,22,14,13,13,9,11,13,14,16,16,16,14,16,24,11,10,14,14,14,18,15,11,12,15,16,13,8,13,21,19,17,18,10,14,14,16,14,11,14,12,15,9,17,17,21,11,8,22,18,18,23,20,14,10,15,10,18,12,10,18,17,18,18,9,15,18,15,15,17,15,18,16,9,16,15,10,18,18,19,8,18,17,10,11,17,12,14,16,14,10,10,20,16,11,15,14,15,21,15,21,16,17,18,12,14,10,13,17,12,14,26,23,20,12,21,17,11,12,12,11,15,19,15,12,15,12,21,13,19,17,18,17,11,22,17,13,11,16,16,10,17,14,17,18)

The model

We are going to use a Bayesian model to estimate the rate of letter writing per week. We are going to assume that the letters are Poisson-distributed, and we put a uniform prior on the parameter lambda of the distribution. We don’t know much about Darwin, so we are going to use a wide Uniform distribution: as far as we know, he may have written between 0 and 1000 letters per week, so we are going to use those numbers as boundaries for our prior uniform distribution on \(\lambda\).

log_prior <- function (lambda) {
  return (dunif(x=lambda, min=0, max=1000, log=TRUE))
}

log_likelihood <- function (data, lambda, power=1) {
  return (sum(dpois(x=data, lambda=lambda, log=TRUE))*power)
}

likelihood <- function (data, lambda, power=1) {
  return (prod(dpois(x=data, lambda=lambda, log=FALSE))^power)
}

unnormalized_log_posterior <- function (data, lambda, power=1) {
  log_prior_value <- log_prior(lambda=lambda)
  log_likelihood_value <- log_likelihood(data=data, lambda=lambda, power=power) 
  return(log_prior_value + log_likelihood_value)
}
plotDistributions <- function (power) {
x_axis <- seq(1:1000)#/1000
min_max= quantile(x=c(sapply(X=x_axis, FUN=log_likelihood, data=n_letters, power=1.0), sapply(X=x_axis, FUN=log_prior), sapply(X=x_axis, FUN=unnormalized_log_posterior, data=n_letters, power=1.0)), probs=c(0,1))
plot(x=x_axis, y=sapply(X=x_axis, FUN=log_likelihood, data=n_letters, power=power), t="l", lwd=4, col="red", ylim=c(min_max[1], min_max[2]), ylab="Value", xlab="lambda value", cex=2)
lines(x=x_axis, y=sapply(X=x_axis, FUN=log_prior), lwd=2, col="black")
lines(x=x_axis, y=sapply(X=x_axis, FUN=unnormalized_log_posterior, data=n_letters, power=power), lwd=2, col="orange")
legend("bottomleft", legend=c("log-likelihood", "log-prior", "log-posterior"), col=c("red", "black", "orange"), lwd=2)
}

plotDistributions(power=1.0)

Bayes theorem and the marginal likelihood

A little reminder on how we compute the posterior probability: \[ P(\lambda|D) = \frac{P(D|\lambda)\times P(\lambda)}{P(D)} \] \(P(D)\) is the marginal likelihood, and we want to compute it: \[ P(D) = \int_{0}^{1000} P(D|\lambda)\times P(\lambda)~d\lambda \] The problem is quite easy here, because we have a single parameter to integrate over, \(\lambda\). So we can do it brute force, by trying many values for \(\lambda\).

lambda_sample <- seq(1:100000)/100
unnormalized_marginal_likelihood <- logSumExp(sapply(X=lambda_sample, FUN=log_likelihood, data=n_letters)) - log(length(lambda_sample)) 
print(unnormalized_marginal_likelihood)
[1] -1432.766

Now we know the marginal likelihood of our model.

But let’s see how we could compute it using other approaches: the Harmonic mean, and stepping stone sampling.

Harmonic mean

MCMC implementation

To compute the harmonic mean, we need an MCMC algorithm to sample from the posterior. We use the Metropolis algorithm, i.e. our move is symmetric so we don’t need the Hastings correction.

# Function to propose a new parameter value, randomly drawn between 0 and 1000
propose_new_lambda_value <-function() {
    return (runif(min=0, max=1000, n=1))
}
# Function to run Metropolis MCMC inference
MetropolisMCMC <- function (data, number_iterations, burnin_proportion=0.5, thining_proportion=0.1, likelihood_power=1.0) {
    current_parameter_value <- propose_new_lambda_value()
    record_parameter <- c()
    record_parameter <- c(record_parameter,current_parameter_value)
    #print(paste("Initial parameter value for the MCMC: ", current_parameter_value, sep=""))
    current_log_posterior <- unnormalized_log_posterior(data = data, lambda = current_parameter_value, power=likelihood_power)
    #print(paste("Initial probability of the model: ", current_log_posterior, sep=""))
    record_log_posterior <- c()
    record_log_posterior <- c(record_log_posterior, current_log_posterior)
    # We also record the likelihood:
    current_log_likelihood <- log_likelihood(data = data, lambda = current_parameter_value, power=likelihood_power)
    record_log_likelihood <- c()
    record_log_likelihood <- c(record_log_likelihood, current_log_likelihood)
    for (i in 1:number_iterations) {
        acceptance_threshold <- runif(min=0, max=1, n=1)
        proposed_parameter_value <- propose_new_lambda_value()
        proposed_log_posterior = unnormalized_log_posterior(data = data, lambda = proposed_parameter_value, power=likelihood_power)
        # print(paste0(proposed_log_posterior, " vs ", current_log_posterior))
        if (exp(proposed_log_posterior - current_log_posterior) > acceptance_threshold) {
            current_parameter_value <- proposed_parameter_value
            current_log_posterior <- proposed_log_posterior
            current_log_likelihood <- log_likelihood(data = data, lambda = current_parameter_value, power=likelihood_power)
        }
        record_parameter <- c(record_parameter, current_parameter_value)
        record_log_posterior <-c(record_log_posterior, current_log_posterior)
        record_log_likelihood <- c(record_log_likelihood, current_log_likelihood)
    }
    to_discard_as_burnin <- floor(burnin_proportion * number_iterations)
    num_samples_left <- number_iterations - to_discard_as_burnin
    between_samples <- floor(num_samples_left / (num_samples_left * thining_proportion))
    record_parameter <- record_parameter[(to_discard_as_burnin+1):number_iterations][seq(from=1, to=num_samples_left, by=between_samples)]
    record_log_posterior <- record_log_posterior[(to_discard_as_burnin+1):number_iterations][seq(from=1, to=num_samples_left, by=between_samples)]
    record_log_likelihood <- record_log_likelihood[(to_discard_as_burnin+1):number_iterations][seq(from=1, to=num_samples_left, by=between_samples)]
    return (list(record_parameter, record_log_posterior, record_log_likelihood))
}
mcmc_sample <- MetropolisMCMC(data=n_letters, number_iterations=10000, likelihood_power=1.0, burnin_proportion=0.5, thining_proportion=0.01)

Once we have a sample from the posterior, we can compute the harmonic mean of our sample.

harmonic_mean <- function(mcmc_sample) {
  loglks <- mcmc_sample[[3]]
  min_loglk <- min(loglks)
  scaled_loglks <- loglks - min_loglk + 1
  number_of_samples <- length(loglks)
  marginal_lk <- log(number_of_samples) - log( sum(exp(-scaled_loglks))) + min_loglk - 1
  return(marginal_lk)
}
harmonic_mean(mcmc_sample)
[1] -1425.26

The marginal likelihood as computed by the Harmonic Mean approach appears to be a bit overestimated, which makes sense: it is estimated from a sample of the posterior distribution, and so probably does not give enough weight to the far tail of the prior distribution, which it has most certainly too rarely visited.

We can illustrate this point by sampling a small number of points from the posterior and seeing where they fall. We will do the same from the prior and see where they fall.

subset_posterior <- sample(mcmc_sample[[1]], size=10)
subset_prior <- runif(n=10, min = 0, max=1000)
x_axis <- seq(1:1000)#/1000
min_max= quantile(x=c(sapply(X=x_axis, FUN=log_likelihood, data=n_letters, power=1.0), sapply(X=x_axis, FUN=log_prior), sapply(X=x_axis, FUN=unnormalized_log_posterior, data=n_letters, power=1.0)), probs=c(0,1))
plot(x=x_axis, y=sapply(X=x_axis, FUN=log_likelihood, data=n_letters, power=1.0), t="l", lwd=4, col="red", ylim=c(min_max[1], min_max[2]), ylab="Value", xlab="lambda value", cex=2)
lines(x=x_axis, y=sapply(X=x_axis, FUN=log_prior), lwd=2, col="black")
lines(x=x_axis, y=sapply(X=x_axis, FUN=unnormalized_log_posterior, data=n_letters, power=1.0), lwd=2, col="orange")
legend("bottomleft", legend=c("log-likelihood", "log-prior", "log-posterior"), col=c("red", "black", "orange"), lwd=2)
points(x=subset_posterior, y=rep(mean(min_max), length(subset_posterior)), col="orange", pch=20, cex=2)
points(x=subset_prior, y=rep(-100, length(subset_prior)), col="black", pch=20, cex=2)

We see that the little sample from the prior distribution, in black, fails to sample the area of high likelihood: if we were to try to estimate the marginal likelihood based on this sample, we would not get a good estimate, it would be too low. Similarly, the little sample from the posterior distribution in orange is completely concentrated at the area of high probability. If we were to try to estimate the marginal likelihood based on this sample, we would not get a good estimate, it would be too high. This is what we observed with the harmonic mean approach. The whole point of stepping stone sampling is to find a sweet spot between sampling from the prior and missing the area of high probability, and sampling from the posterior and missing the area of low probability, to sample both areas correctly.

Stepping stone sampling

To do stepping stone sampling, we need to run MCMC many times, with varying powers on the log-likelihood function. First, let’s see the effect of different powers on the log-likelihood:

par(mfrow=c(5,1))
plotDistributions(1.0)
plotDistributions(0.5)
plotDistributions(0.25)
plotDistributions(0.125)
plotDistributions(0.06275)

When the power is 1, we have the posterior probability. As the power approaches 0, the log-likelihood curve flattens. When the power is 0.0, we have the prior distribution.

We can look at what small samples would look like with these different powers.

posterior <- sample( MetropolisMCMC(data=n_letters, number_iterations=10000, likelihood_power=1.0, burnin_proportion=0.5, thining_proportion=0.01)[[1]], size=10)
post_05 <- sample( MetropolisMCMC(data=n_letters, number_iterations=10000, likelihood_power=0.5, burnin_proportion=0.5, thining_proportion=0.01)[[1]], size=10)
post_025 <- sample( MetropolisMCMC(data=n_letters, number_iterations=10000, likelihood_power=0.005, burnin_proportion=0.5, thining_proportion=0.01)[[1]], size=10)
post_0125 <- sample( MetropolisMCMC(data=n_letters, number_iterations=10000, likelihood_power=0.00005, burnin_proportion=0.5, thining_proportion=0.01)[[1]], size=10)
post_00625 <- sample( MetropolisMCMC(data=n_letters, number_iterations=10000, likelihood_power=0.0000005, burnin_proportion=0.5, thining_proportion=0.01)[[1]], size=10)
x_axis <- seq(1:1000)#/1000
min_max= quantile(x=c(sapply(X=x_axis, FUN=log_likelihood, data=n_letters, power=1.0), sapply(X=x_axis, FUN=log_prior), sapply(X=x_axis, FUN=unnormalized_log_posterior, data=n_letters, power=1.0)), probs=c(0,1))
plot(x=x_axis, y=sapply(X=x_axis, FUN=log_likelihood, data=n_letters, power=1.0), t="l", lwd=4, col="red", ylim=c(min_max[1], min_max[2]), ylab="Value", xlab="lambda value", cex=2)
lines(x=x_axis, y=sapply(X=x_axis, FUN=log_prior), lwd=2, col="black")
lines(x=x_axis, y=sapply(X=x_axis, FUN=unnormalized_log_posterior, data=n_letters, power=1.0), lwd=2, col="orange")
legend("bottomleft", legend=c("log-likelihood", "log-prior", "log-posterior"), col=c("red", "black", "orange"), lwd=2)
points(x=posterior, y=rep(mean(min_max), length(posterior)), col="orange", pch=20, cex=2)
points(x=post_05, y=rep(mean(min_max)-0.2*mean(min_max), length(posterior)), col="orange", pch=20, cex=2)
points(x=post_025, y=rep(mean(min_max)-0.4*mean(min_max), length(posterior)), col="orange", pch=20, cex=2)
points(x=post_0125, y=rep(mean(min_max)-0.6*mean(min_max), length(posterior)), col="orange", pch=20, cex=2)
points(x=post_00625, y=rep(mean(min_max)-0.8*mean(min_max), length(posterior)), col="orange", pch=20, cex=2)

A function for performing stepping stone sampling

We want to perform stepping stone sampling, which involves performing MCMC sampling of a posterior in which the likelihood has been put to a power \(\beta \leq 1.0\). For each of these “stones”, by summing over the samples of the MCMC, we get a normalizing constant \(c_\beta\). When \(\beta=1.0\), we sum the posterior values of the sampled parameters. When \(\beta=0.0\), we sum the prior values of the sampled parameters.

\[c_\beta = \sum samples(P(D|\lambda)^\beta \times P(\lambda))\]

Then we’ll compute the following product: \[\prod_{k=1}^{number~of~stones}\frac{c_{\beta_k}}{c_{\beta_{k-1}}}\] Why is that? Let’s imagine we have 3 stones only: one for the prior where the likelihood has power \(\beta=0\), one where the likelihood has power \(\beta=0.5\), and one where the likelihood has power \(\beta=1.0\). The product can be unrolled as:

\[\prod_{k=1}^{number~of~stones}\frac{c_{\beta_k}}{c_{\beta_{k-1}}} = \frac{c_{0.5}}{c_{0}} \times \frac{c_{1.0}}{c_{0.5}} = \frac{c_{1.0}}{c_{0}}\]

The beauty here is that \(c_0\) is obtained under the prior, and is an estimate of the integral under the prior. Given that the prior is a probability distribution, its integral is \(1.0\), so what we end up having is an estimate of \(c_{1.0}\), i.e. an estimate of the integral under the posterior, i.e. the marginal likelihood.

Why don’t we compute directly \(c_{1.0}\) once and stop there? If we do that we obtain the harmonic mean estimator, which tends to provide a biased estimate of the marginal likelihood, biased towards areas of high likelihood. By contrast, each ratio \(\frac{c_{\beta_k}}{c_{\beta_{k-1}}}\) is estimated pretty accurately, which in the end results in a better estimate of the marginal likelihood.

steppingStoneSampling <- function (vector_powers, data, number_iterations, burnin_proportion=0.5, thining_proportion=0.1) {
  number_of_stones = length(vector_powers)
  number_of_samples_per_stone = floor(number_iterations - floor(burnin_proportion*number_iterations)) * thining_proportion
  sampled_values <-matrix(rep(number_of_stones*number_of_samples_per_stone), nrow=number_of_stones, ncol=number_of_samples_per_stone, byrow=TRUE)
  for (i in 1:number_of_stones) {
    sampled_values[i,] <- MetropolisMCMC(data, number_iterations, burnin_proportion=0.5, thining_proportion=0.1, likelihood_power=vector_powers[i])[[2]]
  }
  return(sampled_values)
}


compute_marginal_lk_from_SS_samples <- function (ss_samples) {
  number_of_stones = dim(ss_samples)[1]
  number_of_samples = dim(ss_samples)[2]
  c_values <- rep(0, number_of_stones)
  for (i in 1:number_of_stones) {
    c_values[i] <- logSumExp(ss_samples[i,])
  }
  logratios <- rep(0, number_of_stones-1)
  for (i in 1:(number_of_stones-1)) {
     logratios[i] <- c_values[i+1]-c_values[i]
  }
  return(sum(logratios))
}

Defining the powers for stepping stone estimation of the marginal likelihood.

We use a distribution \(beta(0.3, 1)\).

build_vector_powers <- function (alpha=0.3, beta=1.0, number_of_stones=10) {
  vector_powers <- rep(0.0, 10)
  for (i in 1:number_of_stones) {
    vector_powers[i]<-(i/number_of_stones)^(beta/alpha)
  }
  return(vector_powers)
}

Now we run the analysis.

vector_powers <- build_vector_powers ()

SS_samples <- steppingStoneSampling(vector_powers=vector_powers, data=n_letters, number_iterations=1000, burnin_proportion=0.5, thining_proportion=0.1)
compute_marginal_lk_from_SS_samples(SS_samples)
[1] -1424.372

If we use more iterations per MCMC, do we get better estimates?

SS_samples <- steppingStoneSampling(vector_powers=vector_powers, data=n_letters, number_iterations=10000, burnin_proportion=0.5, thining_proportion=0.1)

compute_marginal_lk_from_SS_samples(SS_samples)
[1] -1424.388

Not clear! What if we use more stones?

vector_powers <- build_vector_powers (number_of_stones = 100)


SS_samples <- steppingStoneSampling(vector_powers=vector_powers, data=n_letters, number_iterations=1000, burnin_proportion=0.5, thining_proportion=0.1)

compute_marginal_lk_from_SS_samples(SS_samples)
[1] -1447.075

Not clear again!

What if we use both more stones and more iterations per stone?

vector_powers <- build_vector_powers (number_of_stones = 100)


SS_samples <- steppingStoneSampling(vector_powers=vector_powers, data=n_letters, number_iterations=10000, burnin_proportion=0.5, thining_proportion=0.1)

compute_marginal_lk_from_SS_samples(SS_samples)
[1] -1425.275

Conclusion

In this very simple case, stepping stone sampling does not perform better than the Harmonic mean for computing the marginal likelihood. A more complex example, where the prior is wider, may be necessary to start seeing the benefits of the more sophisticated method. Alternatively, some refinements of the computations of the stepping stone marginal likelihood, notably to improve the numerical stability of the algorithm, have not been implemented, so it is possible that they damage the performance of the algorithm.


  1. In reality, this is a made-up dataset, where I drew 520 independent values from a Poisson distribution of parameter 15.5, using “nletters <- rpois(n=520, lambda=15.5)”↩︎

LS0tCnRpdGxlOiAiU3RlcHBpbmcgc3RvbmUgdG8gY29tcHV0ZSB0aGUgbWFyZ2luYWwgbGlrZWxpaG9vZCIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKKkluIGhpcyBub3RlYm9vayB3ZSBhcmUgZ29pbmcgdG8gdHJ5IGFuZCBnZXQgYW4gaW50dWl0aW9uIGZvciB0aGUgc3RlcHBpbmcgc3RvbmUgYXBwcm9hY2ggKFhpZSBldCBhbC4sIDIwMTEpIHRvIGNvbXB1dGUgdGhlIG1hcmdpbmFsIGxpa2VsaWhvb2Qgb2YgYSBtb2RlbC4qCgpgYGB7cn0KbGlicmFyeShtYXRyaXhTdGF0cykKYGBgCgoKIyBPdXIgcXVlc3Rpb24KV2UgYXJlIGdvaW5nIHRvIHN0dWR5IHRoZSBsZXR0ZXJzIHRoYXQgRGFyd2luIHdyb3RlIGR1cmluZyBhIDEwIHllYXIgcGVyaW9kIF5bSW4gcmVhbGl0eSwgdGhpcyBpcyBhIG1hZGUtdXAgZGF0YXNldCwgd2hlcmUgSSBkcmV3IDUyMCBpbmRlcGVuZGVudCB2YWx1ZXMgZnJvbSBhIFBvaXNzb24gZGlzdHJpYnV0aW9uIG9mIHBhcmFtZXRlciAxNS41LCB1c2luZyAibmxldHRlcnMgPC0gcnBvaXMobj01MjAsIGxhbWJkYT0xNS41KSJdLiBXZSBoYXZlIHRoZSBudW1iZXIgb2YgbGV0dGVycyBoZSB3cm90ZSBwZXIgd2VlaywgYW5kIHdlIHdvdWxkIGxpa2UgdG8gZXN0aW1hdGUgdGhlIHJhdGUgd2l0aCB3aGljaCBoZSB3cm90ZSB0aG9zZSBsZXR0ZXJzLgoKIyBUaGUgZGF0YQpIZXJlIGlzIHRoZSBkYXRhOgpgYGB7cn0Kbl9sZXR0ZXJzIDwtIGMoMTMsMTcsMTQsMTAsMTMsMTYsMTQsMTQsMTEsMTQsOSwxNCwxMywxMCwxMSwxMiwxNywxNSwyMywxNiwxMywxNCwxMiwxNiwxNSwxOSwxNCwxMSwxNSwxNCwxNywyMCwyNywyMSwxMiw2LDE4LDEwLDgsMTIsMTEsMTQsMTQsMTUsMTYsMTcsMTIsMTMsMTcsMTAsMTYsMjAsMTUsMTIsMjQsMTAsMTcsMTQsMTQsMTMsMTUsMTIsMTQsMTcsOSwxNiwyNywxMCwxNiw5LDE1LDExLDE2LDExLDE3LDE3LDExLDIwLDIzLDEyLDE4LDI2LDEyLDE3LDE2LDEyLDExLDE2LDIxLDE5LDIwLDE5LDE1LDE1LDcsMTQsMTIsMTIsMTUsMTAsMTksMTksMTcsMTUsMTcsMjAsMTcsMTcsMTEsMTEsMTcsMTYsMTUsMTIsMTYsMTcsMTQsMTgsMTUsMTUsMTAsMTcsMTIsMTYsMjIsMjAsMjMsMTYsMTEsMTksMTYsMjIsMTMsMTQsMTEsMTAsMTksMTYsMTksMTUsMTYsMTYsMTYsMTksMTQsMTksMTQsMTAsNywxMSwxMiw5LDEzLDE4LDIzLDEzLDE2LDExLDE1LDEzLDE1LDE5LDE2LDIxLDE0LDE0LDE0LDEyLDE1LDE2LDE4LDE1LDE1LDEzLDE1LDE5LDExLDE3LDEzLDksMTMsMTYsMTYsMTMsMTMsMTMsMTgsMjEsMTQsMTQsMTMsOCwyMSwyMCwxNSwyMCwxMCwxNiwxOCwxNCwyMSwxNSwyMCwxNSwxNSw5LDE2LDE2LDExLDE5LDEyLDMxLDE5LDE4LDEzLDExLDE0LDE0LDExLDEwLDExLDEyLDEzLDE3LDE0LDE0LDI1LDE3LDE4LDE2LDE4LDExLDIzLDIzLDE0LDE0LDExLDE3LDExLDEyLDE4LDE1LDE1LDEzLDE2LDE4LDE1LDE2LDE0LDExLDExLDE3LDE4LDE0LDIwLDE3LDE2LDEzLDEyLDE0LDE0LDE4LDE2LDIxLDE2LDE1LDE4LDE5LDIyLDE2LDE1LDEwLDEyLDExLDksMTgsMTEsMTMsNSwxMywxNiwxNCwxMiwyMywxOSwxNCwxNywxMSwxNywxOCwxNywxNiwxMSwyMiwxOCwyMCwxMSwxMiw5LDgsMTIsMTIsMTcsMTYsMTIsMjAsMTksMTUsOSwxNCwxMSwxMywxMywyMywxNiwxMCwxNywxNCwyMCwxMSwyMCwxNSwyMCwxNSwxOSwxOSwxNiwxNSwxNCwxOCwyNCwxOCwxMiwxNiwxMCwxOCwxNiwxOSwxMywxMywxMywyMiwxMywxNSwxNywxNywyMCwxOCwxOCwxMiwxMywxNCwxNywxNSwxNywyMCwxOCwyMiwyMywxMywxNiwxNSwxOCwxMCwxOSwxMiwxNCwxOSwxNCwxOSwyMywxOSwyMiw4LDE0LDIwLDIyLDE0LDEzLDEzLDksMTEsMTMsMTQsMTYsMTYsMTYsMTQsMTYsMjQsMTEsMTAsMTQsMTQsMTQsMTgsMTUsMTEsMTIsMTUsMTYsMTMsOCwxMywyMSwxOSwxNywxOCwxMCwxNCwxNCwxNiwxNCwxMSwxNCwxMiwxNSw5LDE3LDE3LDIxLDExLDgsMjIsMTgsMTgsMjMsMjAsMTQsMTAsMTUsMTAsMTgsMTIsMTAsMTgsMTcsMTgsMTgsOSwxNSwxOCwxNSwxNSwxNywxNSwxOCwxNiw5LDE2LDE1LDEwLDE4LDE4LDE5LDgsMTgsMTcsMTAsMTEsMTcsMTIsMTQsMTYsMTQsMTAsMTAsMjAsMTYsMTEsMTUsMTQsMTUsMjEsMTUsMjEsMTYsMTcsMTgsMTIsMTQsMTAsMTMsMTcsMTIsMTQsMjYsMjMsMjAsMTIsMjEsMTcsMTEsMTIsMTIsMTEsMTUsMTksMTUsMTIsMTUsMTIsMjEsMTMsMTksMTcsMTgsMTcsMTEsMjIsMTcsMTMsMTEsMTYsMTYsMTAsMTcsMTQsMTcsMTgpCgpgYGAKCgojIFRoZSBtb2RlbApXZSBhcmUgZ29pbmcgdG8gdXNlIGEgQmF5ZXNpYW4gbW9kZWwgdG8gZXN0aW1hdGUgdGhlIHJhdGUgb2YgbGV0dGVyIHdyaXRpbmcgcGVyIHdlZWsuCldlIGFyZSBnb2luZyB0byBhc3N1bWUgdGhhdCB0aGUgbGV0dGVycyBhcmUgUG9pc3Nvbi1kaXN0cmlidXRlZCwgYW5kIHdlIHB1dCBhIHVuaWZvcm0gcHJpb3Igb24gdGhlIHBhcmFtZXRlciBsYW1iZGEgb2YgdGhlIGRpc3RyaWJ1dGlvbi4KV2UgZG9uJ3Qga25vdyBtdWNoIGFib3V0IERhcndpbiwgc28gd2UgYXJlIGdvaW5nIHRvIHVzZSBhIHdpZGUgVW5pZm9ybSBkaXN0cmlidXRpb246IGFzIGZhciBhcyB3ZSBrbm93LCBoZSBtYXkgaGF2ZSB3cml0dGVuIGJldHdlZW4gMCBhbmQgMTAwMCBsZXR0ZXJzIHBlciB3ZWVrLCBzbyB3ZSBhcmUgZ29pbmcgdG8gdXNlIHRob3NlIG51bWJlcnMgYXMgYm91bmRhcmllcyBmb3Igb3VyIHByaW9yIHVuaWZvcm0gZGlzdHJpYnV0aW9uIG9uICRcbGFtYmRhJC4KCmBgYHtyfQpsb2dfcHJpb3IgPC0gZnVuY3Rpb24gKGxhbWJkYSkgewogIHJldHVybiAoZHVuaWYoeD1sYW1iZGEsIG1pbj0wLCBtYXg9MTAwMCwgbG9nPVRSVUUpKQp9Cgpsb2dfbGlrZWxpaG9vZCA8LSBmdW5jdGlvbiAoZGF0YSwgbGFtYmRhLCBwb3dlcj0xKSB7CiAgcmV0dXJuIChzdW0oZHBvaXMoeD1kYXRhLCBsYW1iZGE9bGFtYmRhLCBsb2c9VFJVRSkpKnBvd2VyKQp9CgpsaWtlbGlob29kIDwtIGZ1bmN0aW9uIChkYXRhLCBsYW1iZGEsIHBvd2VyPTEpIHsKICByZXR1cm4gKHByb2QoZHBvaXMoeD1kYXRhLCBsYW1iZGE9bGFtYmRhLCBsb2c9RkFMU0UpKV5wb3dlcikKfQoKdW5ub3JtYWxpemVkX2xvZ19wb3N0ZXJpb3IgPC0gZnVuY3Rpb24gKGRhdGEsIGxhbWJkYSwgcG93ZXI9MSkgewogIGxvZ19wcmlvcl92YWx1ZSA8LSBsb2dfcHJpb3IobGFtYmRhPWxhbWJkYSkKICBsb2dfbGlrZWxpaG9vZF92YWx1ZSA8LSBsb2dfbGlrZWxpaG9vZChkYXRhPWRhdGEsIGxhbWJkYT1sYW1iZGEsIHBvd2VyPXBvd2VyKSAKICByZXR1cm4obG9nX3ByaW9yX3ZhbHVlICsgbG9nX2xpa2VsaWhvb2RfdmFsdWUpCn0KYGBgCgoKYGBge3J9CnBsb3REaXN0cmlidXRpb25zIDwtIGZ1bmN0aW9uIChwb3dlcikgewp4X2F4aXMgPC0gc2VxKDE6MTAwMCkjLzEwMDAKbWluX21heD0gcXVhbnRpbGUoeD1jKHNhcHBseShYPXhfYXhpcywgRlVOPWxvZ19saWtlbGlob29kLCBkYXRhPW5fbGV0dGVycywgcG93ZXI9MS4wKSwgc2FwcGx5KFg9eF9heGlzLCBGVU49bG9nX3ByaW9yKSwgc2FwcGx5KFg9eF9heGlzLCBGVU49dW5ub3JtYWxpemVkX2xvZ19wb3N0ZXJpb3IsIGRhdGE9bl9sZXR0ZXJzLCBwb3dlcj0xLjApKSwgcHJvYnM9YygwLDEpKQpwbG90KHg9eF9heGlzLCB5PXNhcHBseShYPXhfYXhpcywgRlVOPWxvZ19saWtlbGlob29kLCBkYXRhPW5fbGV0dGVycywgcG93ZXI9cG93ZXIpLCB0PSJsIiwgbHdkPTQsIGNvbD0icmVkIiwgeWxpbT1jKG1pbl9tYXhbMV0sIG1pbl9tYXhbMl0pLCB5bGFiPSJWYWx1ZSIsIHhsYWI9ImxhbWJkYSB2YWx1ZSIsIGNleD0yKQpsaW5lcyh4PXhfYXhpcywgeT1zYXBwbHkoWD14X2F4aXMsIEZVTj1sb2dfcHJpb3IpLCBsd2Q9MiwgY29sPSJibGFjayIpCmxpbmVzKHg9eF9heGlzLCB5PXNhcHBseShYPXhfYXhpcywgRlVOPXVubm9ybWFsaXplZF9sb2dfcG9zdGVyaW9yLCBkYXRhPW5fbGV0dGVycywgcG93ZXI9cG93ZXIpLCBsd2Q9MiwgY29sPSJvcmFuZ2UiKQpsZWdlbmQoImJvdHRvbWxlZnQiLCBsZWdlbmQ9YygibG9nLWxpa2VsaWhvb2QiLCAibG9nLXByaW9yIiwgImxvZy1wb3N0ZXJpb3IiKSwgY29sPWMoInJlZCIsICJibGFjayIsICJvcmFuZ2UiKSwgbHdkPTIpCn0KCnBsb3REaXN0cmlidXRpb25zKHBvd2VyPTEuMCkKYGBgCgoKIyBCYXllcyB0aGVvcmVtIGFuZCB0aGUgbWFyZ2luYWwgbGlrZWxpaG9vZApBIGxpdHRsZSByZW1pbmRlciBvbiBob3cgd2UgY29tcHV0ZSB0aGUgcG9zdGVyaW9yIHByb2JhYmlsaXR5OgokJCBQKFxsYW1iZGF8RCkgPSBcZnJhY3tQKER8XGxhbWJkYSlcdGltZXMgUChcbGFtYmRhKX17UChEKX0gJCQKJFAoRCkkIGlzIHRoZSBtYXJnaW5hbCBsaWtlbGlob29kLCBhbmQgd2Ugd2FudCB0byBjb21wdXRlIGl0OgokJCBQKEQpID0gXGludF97MH1eezEwMDB9IFAoRHxcbGFtYmRhKVx0aW1lcyBQKFxsYW1iZGEpfmRcbGFtYmRhICQkClRoZSBwcm9ibGVtIGlzIHF1aXRlIGVhc3kgaGVyZSwgYmVjYXVzZSB3ZSBoYXZlIGEgc2luZ2xlIHBhcmFtZXRlciB0byBpbnRlZ3JhdGUgb3ZlciwgJFxsYW1iZGEkLiBTbyB3ZSBjYW4gZG8gaXQgYnJ1dGUgZm9yY2UsIGJ5IHRyeWluZyBtYW55IHZhbHVlcyBmb3IgJFxsYW1iZGEkLgoKYGBge3J9CmxhbWJkYV9zYW1wbGUgPC0gc2VxKDE6MTAwMDAwKS8xMDAKdW5ub3JtYWxpemVkX21hcmdpbmFsX2xpa2VsaWhvb2QgPC0gbG9nU3VtRXhwKHNhcHBseShYPWxhbWJkYV9zYW1wbGUsIEZVTj1sb2dfbGlrZWxpaG9vZCwgZGF0YT1uX2xldHRlcnMpKSAtIGxvZyhsZW5ndGgobGFtYmRhX3NhbXBsZSkpIApwcmludCh1bm5vcm1hbGl6ZWRfbWFyZ2luYWxfbGlrZWxpaG9vZCkKYGBgCk5vdyB3ZSBrbm93IHRoZSBtYXJnaW5hbCBsaWtlbGlob29kIG9mIG91ciBtb2RlbC4KCkJ1dCBsZXQncyBzZWUgaG93IHdlIGNvdWxkIGNvbXB1dGUgaXQgdXNpbmcgb3RoZXIgYXBwcm9hY2hlczogdGhlIEhhcm1vbmljIG1lYW4sIGFuZCBzdGVwcGluZyBzdG9uZSBzYW1wbGluZy4KCiMgSGFybW9uaWMgbWVhbgojIyBNQ01DIGltcGxlbWVudGF0aW9uClRvIGNvbXB1dGUgdGhlIGhhcm1vbmljIG1lYW4sIHdlIG5lZWQgYW4gTUNNQyBhbGdvcml0aG0gdG8gc2FtcGxlIGZyb20gdGhlIHBvc3Rlcmlvci4KV2UgdXNlIHRoZSBNZXRyb3BvbGlzIGFsZ29yaXRobSwgaS5lLiBvdXIgbW92ZSBpcyBzeW1tZXRyaWMgc28gd2UgZG9uJ3QgbmVlZCB0aGUgSGFzdGluZ3MgY29ycmVjdGlvbi4KYGBge3J9CiMgRnVuY3Rpb24gdG8gcHJvcG9zZSBhIG5ldyBwYXJhbWV0ZXIgdmFsdWUsIHJhbmRvbWx5IGRyYXduIGJldHdlZW4gMCBhbmQgMTAwMApwcm9wb3NlX25ld19sYW1iZGFfdmFsdWUgPC1mdW5jdGlvbigpIHsKICAgIHJldHVybiAocnVuaWYobWluPTAsIG1heD0xMDAwLCBuPTEpKQp9CiMgRnVuY3Rpb24gdG8gcnVuIE1ldHJvcG9saXMgTUNNQyBpbmZlcmVuY2UKTWV0cm9wb2xpc01DTUMgPC0gZnVuY3Rpb24gKGRhdGEsIG51bWJlcl9pdGVyYXRpb25zLCBidXJuaW5fcHJvcG9ydGlvbj0wLjUsIHRoaW5pbmdfcHJvcG9ydGlvbj0wLjEsIGxpa2VsaWhvb2RfcG93ZXI9MS4wKSB7CiAgICBjdXJyZW50X3BhcmFtZXRlcl92YWx1ZSA8LSBwcm9wb3NlX25ld19sYW1iZGFfdmFsdWUoKQogICAgcmVjb3JkX3BhcmFtZXRlciA8LSBjKCkKICAgIHJlY29yZF9wYXJhbWV0ZXIgPC0gYyhyZWNvcmRfcGFyYW1ldGVyLGN1cnJlbnRfcGFyYW1ldGVyX3ZhbHVlKQogICAgI3ByaW50KHBhc3RlKCJJbml0aWFsIHBhcmFtZXRlciB2YWx1ZSBmb3IgdGhlIE1DTUM6ICIsIGN1cnJlbnRfcGFyYW1ldGVyX3ZhbHVlLCBzZXA9IiIpKQogICAgY3VycmVudF9sb2dfcG9zdGVyaW9yIDwtIHVubm9ybWFsaXplZF9sb2dfcG9zdGVyaW9yKGRhdGEgPSBkYXRhLCBsYW1iZGEgPSBjdXJyZW50X3BhcmFtZXRlcl92YWx1ZSwgcG93ZXI9bGlrZWxpaG9vZF9wb3dlcikKICAgICNwcmludChwYXN0ZSgiSW5pdGlhbCBwcm9iYWJpbGl0eSBvZiB0aGUgbW9kZWw6ICIsIGN1cnJlbnRfbG9nX3Bvc3Rlcmlvciwgc2VwPSIiKSkKICAgIHJlY29yZF9sb2dfcG9zdGVyaW9yIDwtIGMoKQogICAgcmVjb3JkX2xvZ19wb3N0ZXJpb3IgPC0gYyhyZWNvcmRfbG9nX3Bvc3RlcmlvciwgY3VycmVudF9sb2dfcG9zdGVyaW9yKQogICAgIyBXZSBhbHNvIHJlY29yZCB0aGUgbGlrZWxpaG9vZDoKICAgIGN1cnJlbnRfbG9nX2xpa2VsaWhvb2QgPC0gbG9nX2xpa2VsaWhvb2QoZGF0YSA9IGRhdGEsIGxhbWJkYSA9IGN1cnJlbnRfcGFyYW1ldGVyX3ZhbHVlLCBwb3dlcj1saWtlbGlob29kX3Bvd2VyKQogICAgcmVjb3JkX2xvZ19saWtlbGlob29kIDwtIGMoKQogICAgcmVjb3JkX2xvZ19saWtlbGlob29kIDwtIGMocmVjb3JkX2xvZ19saWtlbGlob29kLCBjdXJyZW50X2xvZ19saWtlbGlob29kKQogICAgZm9yIChpIGluIDE6bnVtYmVyX2l0ZXJhdGlvbnMpIHsKICAgICAgICBhY2NlcHRhbmNlX3RocmVzaG9sZCA8LSBydW5pZihtaW49MCwgbWF4PTEsIG49MSkKICAgICAgICBwcm9wb3NlZF9wYXJhbWV0ZXJfdmFsdWUgPC0gcHJvcG9zZV9uZXdfbGFtYmRhX3ZhbHVlKCkKICAgICAgICBwcm9wb3NlZF9sb2dfcG9zdGVyaW9yID0gdW5ub3JtYWxpemVkX2xvZ19wb3N0ZXJpb3IoZGF0YSA9IGRhdGEsIGxhbWJkYSA9IHByb3Bvc2VkX3BhcmFtZXRlcl92YWx1ZSwgcG93ZXI9bGlrZWxpaG9vZF9wb3dlcikKICAgICAgICAjIHByaW50KHBhc3RlMChwcm9wb3NlZF9sb2dfcG9zdGVyaW9yLCAiIHZzICIsIGN1cnJlbnRfbG9nX3Bvc3RlcmlvcikpCiAgICAgICAgaWYgKGV4cChwcm9wb3NlZF9sb2dfcG9zdGVyaW9yIC0gY3VycmVudF9sb2dfcG9zdGVyaW9yKSA+IGFjY2VwdGFuY2VfdGhyZXNob2xkKSB7CiAgICAgICAgICAgIGN1cnJlbnRfcGFyYW1ldGVyX3ZhbHVlIDwtIHByb3Bvc2VkX3BhcmFtZXRlcl92YWx1ZQogICAgICAgICAgICBjdXJyZW50X2xvZ19wb3N0ZXJpb3IgPC0gcHJvcG9zZWRfbG9nX3Bvc3RlcmlvcgogICAgICAgICAgICBjdXJyZW50X2xvZ19saWtlbGlob29kIDwtIGxvZ19saWtlbGlob29kKGRhdGEgPSBkYXRhLCBsYW1iZGEgPSBjdXJyZW50X3BhcmFtZXRlcl92YWx1ZSwgcG93ZXI9bGlrZWxpaG9vZF9wb3dlcikKICAgICAgICB9CiAgICAgICAgcmVjb3JkX3BhcmFtZXRlciA8LSBjKHJlY29yZF9wYXJhbWV0ZXIsIGN1cnJlbnRfcGFyYW1ldGVyX3ZhbHVlKQogICAgICAgIHJlY29yZF9sb2dfcG9zdGVyaW9yIDwtYyhyZWNvcmRfbG9nX3Bvc3RlcmlvciwgY3VycmVudF9sb2dfcG9zdGVyaW9yKQogICAgICAgIHJlY29yZF9sb2dfbGlrZWxpaG9vZCA8LSBjKHJlY29yZF9sb2dfbGlrZWxpaG9vZCwgY3VycmVudF9sb2dfbGlrZWxpaG9vZCkKICAgIH0KICAgIHRvX2Rpc2NhcmRfYXNfYnVybmluIDwtIGZsb29yKGJ1cm5pbl9wcm9wb3J0aW9uICogbnVtYmVyX2l0ZXJhdGlvbnMpCiAgICBudW1fc2FtcGxlc19sZWZ0IDwtIG51bWJlcl9pdGVyYXRpb25zIC0gdG9fZGlzY2FyZF9hc19idXJuaW4KICAgIGJldHdlZW5fc2FtcGxlcyA8LSBmbG9vcihudW1fc2FtcGxlc19sZWZ0IC8gKG51bV9zYW1wbGVzX2xlZnQgKiB0aGluaW5nX3Byb3BvcnRpb24pKQogICAgcmVjb3JkX3BhcmFtZXRlciA8LSByZWNvcmRfcGFyYW1ldGVyWyh0b19kaXNjYXJkX2FzX2J1cm5pbisxKTpudW1iZXJfaXRlcmF0aW9uc11bc2VxKGZyb209MSwgdG89bnVtX3NhbXBsZXNfbGVmdCwgYnk9YmV0d2Vlbl9zYW1wbGVzKV0KICAgIHJlY29yZF9sb2dfcG9zdGVyaW9yIDwtIHJlY29yZF9sb2dfcG9zdGVyaW9yWyh0b19kaXNjYXJkX2FzX2J1cm5pbisxKTpudW1iZXJfaXRlcmF0aW9uc11bc2VxKGZyb209MSwgdG89bnVtX3NhbXBsZXNfbGVmdCwgYnk9YmV0d2Vlbl9zYW1wbGVzKV0KICAgIHJlY29yZF9sb2dfbGlrZWxpaG9vZCA8LSByZWNvcmRfbG9nX2xpa2VsaWhvb2RbKHRvX2Rpc2NhcmRfYXNfYnVybmluKzEpOm51bWJlcl9pdGVyYXRpb25zXVtzZXEoZnJvbT0xLCB0bz1udW1fc2FtcGxlc19sZWZ0LCBieT1iZXR3ZWVuX3NhbXBsZXMpXQogICAgcmV0dXJuIChsaXN0KHJlY29yZF9wYXJhbWV0ZXIsIHJlY29yZF9sb2dfcG9zdGVyaW9yLCByZWNvcmRfbG9nX2xpa2VsaWhvb2QpKQp9CmBgYAoKCmBgYHtyfQptY21jX3NhbXBsZSA8LSBNZXRyb3BvbGlzTUNNQyhkYXRhPW5fbGV0dGVycywgbnVtYmVyX2l0ZXJhdGlvbnM9MTAwMDAsIGxpa2VsaWhvb2RfcG93ZXI9MS4wLCBidXJuaW5fcHJvcG9ydGlvbj0wLjUsIHRoaW5pbmdfcHJvcG9ydGlvbj0wLjAxKQpgYGAKCk9uY2Ugd2UgaGF2ZSBhIHNhbXBsZSBmcm9tIHRoZSBwb3N0ZXJpb3IsIHdlIGNhbiBjb21wdXRlIHRoZSBoYXJtb25pYyBtZWFuIG9mIG91ciBzYW1wbGUuCmBgYHtyfQpoYXJtb25pY19tZWFuIDwtIGZ1bmN0aW9uKG1jbWNfc2FtcGxlKSB7CiAgbG9nbGtzIDwtIG1jbWNfc2FtcGxlW1szXV0KICBtaW5fbG9nbGsgPC0gbWluKGxvZ2xrcykKICBzY2FsZWRfbG9nbGtzIDwtIGxvZ2xrcyAtIG1pbl9sb2dsayArIDEKICBudW1iZXJfb2Zfc2FtcGxlcyA8LSBsZW5ndGgobG9nbGtzKQogIG1hcmdpbmFsX2xrIDwtIGxvZyhudW1iZXJfb2Zfc2FtcGxlcykgLSBsb2coIHN1bShleHAoLXNjYWxlZF9sb2dsa3MpKSkgKyBtaW5fbG9nbGsgLSAxCiAgcmV0dXJuKG1hcmdpbmFsX2xrKQp9CmBgYAoKYGBge3J9Cmhhcm1vbmljX21lYW4obWNtY19zYW1wbGUpCmBgYApUaGUgbWFyZ2luYWwgbGlrZWxpaG9vZCBhcyBjb21wdXRlZCBieSB0aGUgSGFybW9uaWMgTWVhbiBhcHByb2FjaCBhcHBlYXJzIHRvIGJlIGEgYml0IG92ZXJlc3RpbWF0ZWQsIHdoaWNoIG1ha2VzIHNlbnNlOiBpdCBpcyBlc3RpbWF0ZWQgZnJvbSBhIHNhbXBsZSBvZiB0aGUgcG9zdGVyaW9yIGRpc3RyaWJ1dGlvbiwgYW5kIHNvIHByb2JhYmx5IGRvZXMgbm90IGdpdmUgZW5vdWdoIHdlaWdodCB0byB0aGUgZmFyIHRhaWwgb2YgdGhlIHByaW9yIGRpc3RyaWJ1dGlvbiwgd2hpY2ggaXQgaGFzIG1vc3QgY2VydGFpbmx5IHRvbyByYXJlbHkgdmlzaXRlZC4KCldlIGNhbiBpbGx1c3RyYXRlIHRoaXMgcG9pbnQgYnkgc2FtcGxpbmcgYSBzbWFsbCBudW1iZXIgb2YgcG9pbnRzIGZyb20gdGhlIHBvc3RlcmlvciBhbmQgc2VlaW5nIHdoZXJlIHRoZXkgZmFsbC4gV2Ugd2lsbCBkbyB0aGUgc2FtZSBmcm9tIHRoZSBwcmlvciBhbmQgc2VlIHdoZXJlIHRoZXkgZmFsbC4KCmBgYHtyfQpzdWJzZXRfcG9zdGVyaW9yIDwtIHNhbXBsZShtY21jX3NhbXBsZVtbMV1dLCBzaXplPTEwKQpzdWJzZXRfcHJpb3IgPC0gcnVuaWYobj0xMCwgbWluID0gMCwgbWF4PTEwMDApCnhfYXhpcyA8LSBzZXEoMToxMDAwKSMvMTAwMAptaW5fbWF4PSBxdWFudGlsZSh4PWMoc2FwcGx5KFg9eF9heGlzLCBGVU49bG9nX2xpa2VsaWhvb2QsIGRhdGE9bl9sZXR0ZXJzLCBwb3dlcj0xLjApLCBzYXBwbHkoWD14X2F4aXMsIEZVTj1sb2dfcHJpb3IpLCBzYXBwbHkoWD14X2F4aXMsIEZVTj11bm5vcm1hbGl6ZWRfbG9nX3Bvc3RlcmlvciwgZGF0YT1uX2xldHRlcnMsIHBvd2VyPTEuMCkpLCBwcm9icz1jKDAsMSkpCnBsb3QoeD14X2F4aXMsIHk9c2FwcGx5KFg9eF9heGlzLCBGVU49bG9nX2xpa2VsaWhvb2QsIGRhdGE9bl9sZXR0ZXJzLCBwb3dlcj0xLjApLCB0PSJsIiwgbHdkPTQsIGNvbD0icmVkIiwgeWxpbT1jKG1pbl9tYXhbMV0sIG1pbl9tYXhbMl0pLCB5bGFiPSJWYWx1ZSIsIHhsYWI9ImxhbWJkYSB2YWx1ZSIsIGNleD0yKQpsaW5lcyh4PXhfYXhpcywgeT1zYXBwbHkoWD14X2F4aXMsIEZVTj1sb2dfcHJpb3IpLCBsd2Q9MiwgY29sPSJibGFjayIpCmxpbmVzKHg9eF9heGlzLCB5PXNhcHBseShYPXhfYXhpcywgRlVOPXVubm9ybWFsaXplZF9sb2dfcG9zdGVyaW9yLCBkYXRhPW5fbGV0dGVycywgcG93ZXI9MS4wKSwgbHdkPTIsIGNvbD0ib3JhbmdlIikKbGVnZW5kKCJib3R0b21sZWZ0IiwgbGVnZW5kPWMoImxvZy1saWtlbGlob29kIiwgImxvZy1wcmlvciIsICJsb2ctcG9zdGVyaW9yIiksIGNvbD1jKCJyZWQiLCAiYmxhY2siLCAib3JhbmdlIiksIGx3ZD0yKQpwb2ludHMoeD1zdWJzZXRfcG9zdGVyaW9yLCB5PXJlcChtZWFuKG1pbl9tYXgpLCBsZW5ndGgoc3Vic2V0X3Bvc3RlcmlvcikpLCBjb2w9Im9yYW5nZSIsIHBjaD0yMCwgY2V4PTIpCnBvaW50cyh4PXN1YnNldF9wcmlvciwgeT1yZXAoLTEwMCwgbGVuZ3RoKHN1YnNldF9wcmlvcikpLCBjb2w9ImJsYWNrIiwgcGNoPTIwLCBjZXg9MikKCmBgYApXZSBzZWUgdGhhdCB0aGUgbGl0dGxlIHNhbXBsZSBmcm9tIHRoZSBwcmlvciBkaXN0cmlidXRpb24sIGluIGJsYWNrLCBmYWlscyB0byBzYW1wbGUgdGhlIGFyZWEgb2YgaGlnaCBsaWtlbGlob29kOiBpZiB3ZSB3ZXJlIHRvIHRyeSB0byBlc3RpbWF0ZSB0aGUgbWFyZ2luYWwgbGlrZWxpaG9vZCBiYXNlZCBvbiB0aGlzIHNhbXBsZSwgd2Ugd291bGQgbm90IGdldCBhIGdvb2QgZXN0aW1hdGUsIGl0IHdvdWxkIGJlIHRvbyBsb3cuClNpbWlsYXJseSwgdGhlIGxpdHRsZSBzYW1wbGUgZnJvbSB0aGUgcG9zdGVyaW9yIGRpc3RyaWJ1dGlvbiBpbiBvcmFuZ2UgaXMgY29tcGxldGVseSBjb25jZW50cmF0ZWQgYXQgdGhlIGFyZWEgb2YgaGlnaCBwcm9iYWJpbGl0eS4gSWYgd2Ugd2VyZSB0byB0cnkgdG8gZXN0aW1hdGUgdGhlIG1hcmdpbmFsIGxpa2VsaWhvb2QgYmFzZWQgb24gdGhpcyBzYW1wbGUsIHdlIHdvdWxkIG5vdCBnZXQgYSBnb29kIGVzdGltYXRlLCBpdCB3b3VsZCBiZSB0b28gaGlnaC4gVGhpcyBpcyB3aGF0IHdlIG9ic2VydmVkIHdpdGggdGhlIGhhcm1vbmljIG1lYW4gYXBwcm9hY2guClRoZSB3aG9sZSBwb2ludCBvZiBzdGVwcGluZyBzdG9uZSBzYW1wbGluZyBpcyB0byBmaW5kIGEgc3dlZXQgc3BvdCBiZXR3ZWVuIHNhbXBsaW5nIGZyb20gdGhlIHByaW9yIGFuZCBtaXNzaW5nIHRoZSBhcmVhIG9mIGhpZ2ggcHJvYmFiaWxpdHksIGFuZCBzYW1wbGluZyBmcm9tIHRoZSBwb3N0ZXJpb3IgYW5kIG1pc3NpbmcgdGhlIGFyZWEgb2YgbG93IHByb2JhYmlsaXR5LCB0byBzYW1wbGUgYm90aCBhcmVhcyBjb3JyZWN0bHkuCgojIFN0ZXBwaW5nIHN0b25lIHNhbXBsaW5nClRvIGRvIHN0ZXBwaW5nIHN0b25lIHNhbXBsaW5nLCB3ZSBuZWVkIHRvIHJ1biBNQ01DIG1hbnkgdGltZXMsIHdpdGggdmFyeWluZyBwb3dlcnMgb24gdGhlIGxvZy1saWtlbGlob29kIGZ1bmN0aW9uLiAKRmlyc3QsIGxldCdzIHNlZSB0aGUgZWZmZWN0IG9mIGRpZmZlcmVudCBwb3dlcnMgb24gdGhlIGxvZy1saWtlbGlob29kOgoKYGBge3IsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD0xMH0KcGFyKG1mcm93PWMoNSwxKSkKcGxvdERpc3RyaWJ1dGlvbnMoMS4wKQpwbG90RGlzdHJpYnV0aW9ucygwLjUpCnBsb3REaXN0cmlidXRpb25zKDAuMjUpCnBsb3REaXN0cmlidXRpb25zKDAuMTI1KQpwbG90RGlzdHJpYnV0aW9ucygwLjA2Mjc1KQpgYGAKCgpXaGVuIHRoZSBwb3dlciBpcyAxLCB3ZSBoYXZlIHRoZSBwb3N0ZXJpb3IgcHJvYmFiaWxpdHkuIEFzIHRoZSBwb3dlciBhcHByb2FjaGVzIDAsIHRoZSBsb2ctbGlrZWxpaG9vZCBjdXJ2ZSBmbGF0dGVucy4gV2hlbiB0aGUgcG93ZXIgaXMgMC4wLCB3ZSBoYXZlIHRoZSBwcmlvciBkaXN0cmlidXRpb24uCgpXZSBjYW4gbG9vayBhdCB3aGF0IHNtYWxsIHNhbXBsZXMgd291bGQgbG9vayBsaWtlIHdpdGggdGhlc2UgZGlmZmVyZW50IHBvd2Vycy4KCmBgYHtyfQpwb3N0ZXJpb3IgPC0gc2FtcGxlKCBNZXRyb3BvbGlzTUNNQyhkYXRhPW5fbGV0dGVycywgbnVtYmVyX2l0ZXJhdGlvbnM9MTAwMDAsIGxpa2VsaWhvb2RfcG93ZXI9MS4wLCBidXJuaW5fcHJvcG9ydGlvbj0wLjUsIHRoaW5pbmdfcHJvcG9ydGlvbj0wLjAxKVtbMV1dLCBzaXplPTEwKQpwb3N0XzA1IDwtIHNhbXBsZSggTWV0cm9wb2xpc01DTUMoZGF0YT1uX2xldHRlcnMsIG51bWJlcl9pdGVyYXRpb25zPTEwMDAwLCBsaWtlbGlob29kX3Bvd2VyPTAuNSwgYnVybmluX3Byb3BvcnRpb249MC41LCB0aGluaW5nX3Byb3BvcnRpb249MC4wMSlbWzFdXSwgc2l6ZT0xMCkKcG9zdF8wMjUgPC0gc2FtcGxlKCBNZXRyb3BvbGlzTUNNQyhkYXRhPW5fbGV0dGVycywgbnVtYmVyX2l0ZXJhdGlvbnM9MTAwMDAsIGxpa2VsaWhvb2RfcG93ZXI9MC4wMDUsIGJ1cm5pbl9wcm9wb3J0aW9uPTAuNSwgdGhpbmluZ19wcm9wb3J0aW9uPTAuMDEpW1sxXV0sIHNpemU9MTApCnBvc3RfMDEyNSA8LSBzYW1wbGUoIE1ldHJvcG9saXNNQ01DKGRhdGE9bl9sZXR0ZXJzLCBudW1iZXJfaXRlcmF0aW9ucz0xMDAwMCwgbGlrZWxpaG9vZF9wb3dlcj0wLjAwMDA1LCBidXJuaW5fcHJvcG9ydGlvbj0wLjUsIHRoaW5pbmdfcHJvcG9ydGlvbj0wLjAxKVtbMV1dLCBzaXplPTEwKQpwb3N0XzAwNjI1IDwtIHNhbXBsZSggTWV0cm9wb2xpc01DTUMoZGF0YT1uX2xldHRlcnMsIG51bWJlcl9pdGVyYXRpb25zPTEwMDAwLCBsaWtlbGlob29kX3Bvd2VyPTAuMDAwMDAwNSwgYnVybmluX3Byb3BvcnRpb249MC41LCB0aGluaW5nX3Byb3BvcnRpb249MC4wMSlbWzFdXSwgc2l6ZT0xMCkKCgpgYGAKCmBgYHtyfQp4X2F4aXMgPC0gc2VxKDE6MTAwMCkjLzEwMDAKbWluX21heD0gcXVhbnRpbGUoeD1jKHNhcHBseShYPXhfYXhpcywgRlVOPWxvZ19saWtlbGlob29kLCBkYXRhPW5fbGV0dGVycywgcG93ZXI9MS4wKSwgc2FwcGx5KFg9eF9heGlzLCBGVU49bG9nX3ByaW9yKSwgc2FwcGx5KFg9eF9heGlzLCBGVU49dW5ub3JtYWxpemVkX2xvZ19wb3N0ZXJpb3IsIGRhdGE9bl9sZXR0ZXJzLCBwb3dlcj0xLjApKSwgcHJvYnM9YygwLDEpKQpwbG90KHg9eF9heGlzLCB5PXNhcHBseShYPXhfYXhpcywgRlVOPWxvZ19saWtlbGlob29kLCBkYXRhPW5fbGV0dGVycywgcG93ZXI9MS4wKSwgdD0ibCIsIGx3ZD00LCBjb2w9InJlZCIsIHlsaW09YyhtaW5fbWF4WzFdLCBtaW5fbWF4WzJdKSwgeWxhYj0iVmFsdWUiLCB4bGFiPSJsYW1iZGEgdmFsdWUiLCBjZXg9MikKbGluZXMoeD14X2F4aXMsIHk9c2FwcGx5KFg9eF9heGlzLCBGVU49bG9nX3ByaW9yKSwgbHdkPTIsIGNvbD0iYmxhY2siKQpsaW5lcyh4PXhfYXhpcywgeT1zYXBwbHkoWD14X2F4aXMsIEZVTj11bm5vcm1hbGl6ZWRfbG9nX3Bvc3RlcmlvciwgZGF0YT1uX2xldHRlcnMsIHBvd2VyPTEuMCksIGx3ZD0yLCBjb2w9Im9yYW5nZSIpCmxlZ2VuZCgiYm90dG9tbGVmdCIsIGxlZ2VuZD1jKCJsb2ctbGlrZWxpaG9vZCIsICJsb2ctcHJpb3IiLCAibG9nLXBvc3RlcmlvciIpLCBjb2w9YygicmVkIiwgImJsYWNrIiwgIm9yYW5nZSIpLCBsd2Q9MikKcG9pbnRzKHg9cG9zdGVyaW9yLCB5PXJlcChtZWFuKG1pbl9tYXgpLCBsZW5ndGgocG9zdGVyaW9yKSksIGNvbD0ib3JhbmdlIiwgcGNoPTIwLCBjZXg9MikKcG9pbnRzKHg9cG9zdF8wNSwgeT1yZXAobWVhbihtaW5fbWF4KS0wLjIqbWVhbihtaW5fbWF4KSwgbGVuZ3RoKHBvc3RlcmlvcikpLCBjb2w9Im9yYW5nZSIsIHBjaD0yMCwgY2V4PTIpCnBvaW50cyh4PXBvc3RfMDI1LCB5PXJlcChtZWFuKG1pbl9tYXgpLTAuNCptZWFuKG1pbl9tYXgpLCBsZW5ndGgocG9zdGVyaW9yKSksIGNvbD0ib3JhbmdlIiwgcGNoPTIwLCBjZXg9MikKcG9pbnRzKHg9cG9zdF8wMTI1LCB5PXJlcChtZWFuKG1pbl9tYXgpLTAuNiptZWFuKG1pbl9tYXgpLCBsZW5ndGgocG9zdGVyaW9yKSksIGNvbD0ib3JhbmdlIiwgcGNoPTIwLCBjZXg9MikKcG9pbnRzKHg9cG9zdF8wMDYyNSwgeT1yZXAobWVhbihtaW5fbWF4KS0wLjgqbWVhbihtaW5fbWF4KSwgbGVuZ3RoKHBvc3RlcmlvcikpLCBjb2w9Im9yYW5nZSIsIHBjaD0yMCwgY2V4PTIpCgpgYGAKCgoKIyBBIGZ1bmN0aW9uIGZvciBwZXJmb3JtaW5nIHN0ZXBwaW5nIHN0b25lIHNhbXBsaW5nCldlIHdhbnQgdG8gcGVyZm9ybSBzdGVwcGluZyBzdG9uZSBzYW1wbGluZywgd2hpY2ggaW52b2x2ZXMgcGVyZm9ybWluZyBNQ01DIHNhbXBsaW5nIG9mIGEgcG9zdGVyaW9yIGluIHdoaWNoIHRoZSBsaWtlbGlob29kIGhhcyBiZWVuIHB1dCB0byBhIHBvd2VyICRcYmV0YSBcbGVxIDEuMCQuCkZvciBlYWNoIG9mIHRoZXNlICJzdG9uZXMiLCBieSBzdW1taW5nIG92ZXIgdGhlIHNhbXBsZXMgb2YgdGhlIE1DTUMsIHdlIGdldCBhIG5vcm1hbGl6aW5nIGNvbnN0YW50ICRjX1xiZXRhJC4KV2hlbiAkXGJldGE9MS4wJCwgd2Ugc3VtIHRoZSAqcG9zdGVyaW9yKiB2YWx1ZXMgb2YgdGhlIHNhbXBsZWQgcGFyYW1ldGVycy4gV2hlbiAkXGJldGE9MC4wJCwgd2Ugc3VtIHRoZSAqcHJpb3IqIHZhbHVlcyBvZiB0aGUgc2FtcGxlZCBwYXJhbWV0ZXJzLgoKJCRjX1xiZXRhID0gXHN1bSBzYW1wbGVzKFAoRHxcbGFtYmRhKV5cYmV0YSBcdGltZXMgUChcbGFtYmRhKSkkJAoKVGhlbiB3ZSdsbCBjb21wdXRlIHRoZSBmb2xsb3dpbmcgcHJvZHVjdDoKJCRccHJvZF97az0xfV57bnVtYmVyfm9mfnN0b25lc31cZnJhY3tjX3tcYmV0YV9rfX17Y197XGJldGFfe2stMX19fSQkCldoeSBpcyB0aGF0PyBMZXQncyBpbWFnaW5lIHdlIGhhdmUgMyBzdG9uZXMgb25seTogb25lIGZvciB0aGUgcHJpb3Igd2hlcmUgdGhlIGxpa2VsaWhvb2QgaGFzIHBvd2VyICRcYmV0YT0wJCwgb25lIHdoZXJlIHRoZSBsaWtlbGlob29kIGhhcyBwb3dlciAkXGJldGE9MC41JCwgYW5kIG9uZSB3aGVyZSB0aGUgbGlrZWxpaG9vZCBoYXMgcG93ZXIgJFxiZXRhPTEuMCQuIFRoZSBwcm9kdWN0IGNhbiBiZSB1bnJvbGxlZCBhczoKCgokJFxwcm9kX3trPTF9XntudW1iZXJ+b2Z+c3RvbmVzfVxmcmFje2Nfe1xiZXRhX2t9fXtjX3tcYmV0YV97ay0xfX19ID0gXGZyYWN7Y197MC41fX17Y197MH19IFx0aW1lcyBcZnJhY3tjX3sxLjB9fXtjX3swLjV9fSA9IFxmcmFje2NfezEuMH19e2NfezB9fSQkCgpUaGUgYmVhdXR5IGhlcmUgaXMgdGhhdCAkY18wJCBpcyBvYnRhaW5lZCB1bmRlciB0aGUgcHJpb3IsIGFuZCBpcyBhbiBlc3RpbWF0ZSBvZiB0aGUgaW50ZWdyYWwgdW5kZXIgdGhlIHByaW9yLiBHaXZlbiB0aGF0IHRoZSBwcmlvciBpcyBhIHByb2JhYmlsaXR5IGRpc3RyaWJ1dGlvbiwgaXRzIGludGVncmFsIGlzICQxLjAkLCBzbyB3aGF0IHdlIGVuZCB1cCBoYXZpbmcgaXMgYW4gZXN0aW1hdGUgb2YgJGNfezEuMH0kLCBpLmUuIGFuIGVzdGltYXRlIG9mIHRoZSBpbnRlZ3JhbCB1bmRlciB0aGUgcG9zdGVyaW9yLCBpLmUuIHRoZSBtYXJnaW5hbCBsaWtlbGlob29kLgoKV2h5IGRvbid0IHdlIGNvbXB1dGUgZGlyZWN0bHkgJGNfezEuMH0kIG9uY2UgYW5kIHN0b3AgdGhlcmU/IElmIHdlIGRvIHRoYXQgd2Ugb2J0YWluIHRoZSBoYXJtb25pYyBtZWFuIGVzdGltYXRvciwgd2hpY2ggdGVuZHMgdG8gcHJvdmlkZSBhIGJpYXNlZCBlc3RpbWF0ZSBvZiB0aGUgbWFyZ2luYWwgbGlrZWxpaG9vZCwgYmlhc2VkIHRvd2FyZHMgYXJlYXMgb2YgaGlnaCBsaWtlbGlob29kLiBCeSBjb250cmFzdCwgZWFjaCByYXRpbyAgJFxmcmFje2Nfe1xiZXRhX2t9fXtjX3tcYmV0YV97ay0xfX19JCBpcyBlc3RpbWF0ZWQgcHJldHR5IGFjY3VyYXRlbHksIHdoaWNoIGluIHRoZSBlbmQgcmVzdWx0cyBpbiBhIGJldHRlciBlc3RpbWF0ZSBvZiB0aGUgbWFyZ2luYWwgbGlrZWxpaG9vZC4gCgoKYGBge3J9CnN0ZXBwaW5nU3RvbmVTYW1wbGluZyA8LSBmdW5jdGlvbiAodmVjdG9yX3Bvd2VycywgZGF0YSwgbnVtYmVyX2l0ZXJhdGlvbnMsIGJ1cm5pbl9wcm9wb3J0aW9uPTAuNSwgdGhpbmluZ19wcm9wb3J0aW9uPTAuMSkgewogIG51bWJlcl9vZl9zdG9uZXMgPSBsZW5ndGgodmVjdG9yX3Bvd2VycykKICBudW1iZXJfb2Zfc2FtcGxlc19wZXJfc3RvbmUgPSBmbG9vcihudW1iZXJfaXRlcmF0aW9ucyAtIGZsb29yKGJ1cm5pbl9wcm9wb3J0aW9uKm51bWJlcl9pdGVyYXRpb25zKSkgKiB0aGluaW5nX3Byb3BvcnRpb24KICBzYW1wbGVkX3ZhbHVlcyA8LW1hdHJpeChyZXAobnVtYmVyX29mX3N0b25lcypudW1iZXJfb2Zfc2FtcGxlc19wZXJfc3RvbmUpLCBucm93PW51bWJlcl9vZl9zdG9uZXMsIG5jb2w9bnVtYmVyX29mX3NhbXBsZXNfcGVyX3N0b25lLCBieXJvdz1UUlVFKQogIGZvciAoaSBpbiAxOm51bWJlcl9vZl9zdG9uZXMpIHsKICAgIHNhbXBsZWRfdmFsdWVzW2ksXSA8LSBNZXRyb3BvbGlzTUNNQyhkYXRhLCBudW1iZXJfaXRlcmF0aW9ucywgYnVybmluX3Byb3BvcnRpb249MC41LCB0aGluaW5nX3Byb3BvcnRpb249MC4xLCBsaWtlbGlob29kX3Bvd2VyPXZlY3Rvcl9wb3dlcnNbaV0pW1syXV0KICB9CiAgcmV0dXJuKHNhbXBsZWRfdmFsdWVzKQp9CgoKY29tcHV0ZV9tYXJnaW5hbF9sa19mcm9tX1NTX3NhbXBsZXMgPC0gZnVuY3Rpb24gKHNzX3NhbXBsZXMpIHsKICBudW1iZXJfb2Zfc3RvbmVzID0gZGltKHNzX3NhbXBsZXMpWzFdCiAgbnVtYmVyX29mX3NhbXBsZXMgPSBkaW0oc3Nfc2FtcGxlcylbMl0KICBjX3ZhbHVlcyA8LSByZXAoMCwgbnVtYmVyX29mX3N0b25lcykKICBmb3IgKGkgaW4gMTpudW1iZXJfb2Zfc3RvbmVzKSB7CiAgICBjX3ZhbHVlc1tpXSA8LSBsb2dTdW1FeHAoc3Nfc2FtcGxlc1tpLF0pCiAgfQogIGxvZ3JhdGlvcyA8LSByZXAoMCwgbnVtYmVyX29mX3N0b25lcy0xKQogIGZvciAoaSBpbiAxOihudW1iZXJfb2Zfc3RvbmVzLTEpKSB7CiAgICAgbG9ncmF0aW9zW2ldIDwtIGNfdmFsdWVzW2krMV0tY192YWx1ZXNbaV0KICB9CiAgcmV0dXJuKHN1bShsb2dyYXRpb3MpKQp9CgpgYGAKCgoKIyBEZWZpbmluZyB0aGUgcG93ZXJzIGZvciBzdGVwcGluZyBzdG9uZSBlc3RpbWF0aW9uIG9mIHRoZSBtYXJnaW5hbCBsaWtlbGlob29kLgpXZSB1c2UgYSBkaXN0cmlidXRpb24gJGJldGEoMC4zLCAxKSQuCmBgYHtyfQpidWlsZF92ZWN0b3JfcG93ZXJzIDwtIGZ1bmN0aW9uIChhbHBoYT0wLjMsIGJldGE9MS4wLCBudW1iZXJfb2Zfc3RvbmVzPTEwKSB7CiAgdmVjdG9yX3Bvd2VycyA8LSByZXAoMC4wLCAxMCkKICBmb3IgKGkgaW4gMTpudW1iZXJfb2Zfc3RvbmVzKSB7CiAgICB2ZWN0b3JfcG93ZXJzW2ldPC0oaS9udW1iZXJfb2Zfc3RvbmVzKV4oYmV0YS9hbHBoYSkKICB9CiAgcmV0dXJuKHZlY3Rvcl9wb3dlcnMpCn0KYGBgCgoKTm93IHdlIHJ1biB0aGUgYW5hbHlzaXMuCmBgYHtyfQp2ZWN0b3JfcG93ZXJzIDwtIGJ1aWxkX3ZlY3Rvcl9wb3dlcnMgKCkKClNTX3NhbXBsZXMgPC0gc3RlcHBpbmdTdG9uZVNhbXBsaW5nKHZlY3Rvcl9wb3dlcnM9dmVjdG9yX3Bvd2VycywgZGF0YT1uX2xldHRlcnMsIG51bWJlcl9pdGVyYXRpb25zPTEwMDAsIGJ1cm5pbl9wcm9wb3J0aW9uPTAuNSwgdGhpbmluZ19wcm9wb3J0aW9uPTAuMSkKCmBgYAoKYGBge3J9CmNvbXB1dGVfbWFyZ2luYWxfbGtfZnJvbV9TU19zYW1wbGVzKFNTX3NhbXBsZXMpCmBgYApJZiB3ZSB1c2UgbW9yZSBpdGVyYXRpb25zIHBlciBNQ01DLCBkbyB3ZSBnZXQgYmV0dGVyIGVzdGltYXRlcz8KCmBgYHtyfQpTU19zYW1wbGVzIDwtIHN0ZXBwaW5nU3RvbmVTYW1wbGluZyh2ZWN0b3JfcG93ZXJzPXZlY3Rvcl9wb3dlcnMsIGRhdGE9bl9sZXR0ZXJzLCBudW1iZXJfaXRlcmF0aW9ucz0xMDAwMCwgYnVybmluX3Byb3BvcnRpb249MC41LCB0aGluaW5nX3Byb3BvcnRpb249MC4xKQoKY29tcHV0ZV9tYXJnaW5hbF9sa19mcm9tX1NTX3NhbXBsZXMoU1Nfc2FtcGxlcykKCmBgYApOb3QgY2xlYXIhIFdoYXQgaWYgd2UgdXNlIG1vcmUgc3RvbmVzPwoKYGBge3J9CnZlY3Rvcl9wb3dlcnMgPC0gYnVpbGRfdmVjdG9yX3Bvd2VycyAobnVtYmVyX29mX3N0b25lcyA9IDEwMCkKCgpTU19zYW1wbGVzIDwtIHN0ZXBwaW5nU3RvbmVTYW1wbGluZyh2ZWN0b3JfcG93ZXJzPXZlY3Rvcl9wb3dlcnMsIGRhdGE9bl9sZXR0ZXJzLCBudW1iZXJfaXRlcmF0aW9ucz0xMDAwLCBidXJuaW5fcHJvcG9ydGlvbj0wLjUsIHRoaW5pbmdfcHJvcG9ydGlvbj0wLjEpCgpjb21wdXRlX21hcmdpbmFsX2xrX2Zyb21fU1Nfc2FtcGxlcyhTU19zYW1wbGVzKQoKYGBgCk5vdCBjbGVhciBhZ2FpbiEKCldoYXQgaWYgd2UgdXNlIGJvdGggbW9yZSBzdG9uZXMgYW5kIG1vcmUgaXRlcmF0aW9ucyBwZXIgc3RvbmU/CgpgYGB7cn0KdmVjdG9yX3Bvd2VycyA8LSBidWlsZF92ZWN0b3JfcG93ZXJzIChudW1iZXJfb2Zfc3RvbmVzID0gMTAwKQoKClNTX3NhbXBsZXMgPC0gc3RlcHBpbmdTdG9uZVNhbXBsaW5nKHZlY3Rvcl9wb3dlcnM9dmVjdG9yX3Bvd2VycywgZGF0YT1uX2xldHRlcnMsIG51bWJlcl9pdGVyYXRpb25zPTEwMDAwLCBidXJuaW5fcHJvcG9ydGlvbj0wLjUsIHRoaW5pbmdfcHJvcG9ydGlvbj0wLjEpCgpjb21wdXRlX21hcmdpbmFsX2xrX2Zyb21fU1Nfc2FtcGxlcyhTU19zYW1wbGVzKQoKYGBgCgoKIyBDb25jbHVzaW9uCkluIHRoaXMgdmVyeSBzaW1wbGUgY2FzZSwgc3RlcHBpbmcgc3RvbmUgc2FtcGxpbmcgZG9lcyBub3QgcGVyZm9ybSBiZXR0ZXIgdGhhbiB0aGUgSGFybW9uaWMgbWVhbiBmb3IgY29tcHV0aW5nIHRoZSBtYXJnaW5hbCBsaWtlbGlob29kLiBBIG1vcmUgY29tcGxleCBleGFtcGxlLCB3aGVyZSB0aGUgcHJpb3IgaXMgd2lkZXIsIG1heSBiZSBuZWNlc3NhcnkgdG8gc3RhcnQgc2VlaW5nIHRoZSBiZW5lZml0cyBvZiB0aGUgbW9yZSBzb3BoaXN0aWNhdGVkIG1ldGhvZC4gQWx0ZXJuYXRpdmVseSwgc29tZSByZWZpbmVtZW50cyBvZiB0aGUgY29tcHV0YXRpb25zIG9mIHRoZSBzdGVwcGluZyBzdG9uZSBtYXJnaW5hbCBsaWtlbGlob29kLCBub3RhYmx5IHRvIGltcHJvdmUgdGhlIG51bWVyaWNhbCBzdGFiaWxpdHkgb2YgdGhlIGFsZ29yaXRobSwgaGF2ZSBub3QgYmVlbiBpbXBsZW1lbnRlZCwgc28gaXQgaXMgcG9zc2libGUgdGhhdCB0aGV5IGRhbWFnZSB0aGUgcGVyZm9ybWFuY2Ugb2YgdGhlIGFsZ29yaXRobS4K