It won’t come as a surprise to anyone who knows me that I have to try a Bayesian approach to variable selection. It also turns out that there’s an easy way to do it, since “horseshoe priors” are built into rstanarm. What’s a horeshoe prior you ask? Well you can see a few of the details in the next section, or you can just skip to the section where we use it in rstanarm if you find the math more confusing than helpful.

What is a horsehoe prior?

If you really want to know and understand horseshoe priors, you’ll need to read a paper by Juho Pironen and Aki Vehtari in the Electronic Journal of Statistics,1 but here’s a brief outline.

The likelihood in standard linear regression model looks like this

\[ \eqalign{ y_i &\sim& \mbox{N}(\mu_i, \sigma^2) \\ \mu_i &=& \beta_0 + \sum_j \beta_jx_{ij} } \]

To complete the Bayesian model, we need to specify priors for \(\sigma^2\) and the \(\beta\)s. The default choices in rstanarm are:2

\[ \eqalign{ \sigma &\sim& \mbox{Exp}(1) \\ \beta_0 &\sim& \mbox{N}(0, 10) \\ \beta_i &\sim& \mbox{N}(0, 2.5) \quad \mbox{for } i > 0 } \]

These are the choices we used here. The basic horsehoe prior affects only the last of these. Specifically,

\[ \eqalign{ \beta_i &\sim& \mbox{N}(0, \tau^2\lambda_i^2) \\ \lambda_i &\sim& \mbox{Cauchy}^+(0, 1) \quad , } \]

where \(\mbox{Cauchy}^+\) refers to a half-Cauchy distribution on the positive real line. A Cauchy distribution has very fat tails, so the tendency for any \(\beta_i\) is to be either close to zero, because \(\lambda_i\) is small, or well away from zero, because \(\lambda_i\) is large. \(\tau\) sets the total amount of influence the covariates have on the response, rather like \(\lambda\) in the Lasso.

As Pironen and Vehtari explain, however, there hasn’t been consensus on how to set or estimate \(\tau\). They introduce a new version of the horseshoe prior, the regularized horseshoe prior that looks like this

\[ \eqalign{ \beta_i &\sim& \mbox{N}(0, \tau^2\tilde\lambda_i^2) \\ \tilde\lambda_i &=& \frac{c^2\lambda_i^2}{c^2 + \tau^2\lambda_i^2} \\ \lambda_i &\sim& \mbox{Cauchy}^+(0, 1) \quad . } \]

Notice that if \(\tau^2\lambda_i^2 \ll c^2\), meaning that \(\beta_i\) is close to 0, then we have something very close to the original horseshoe prior. If on the other hand, \(\tau^2\lambda_i^2 \gg c^2\) then the prior on \(\beta_i\) is close to \(\mbox{N}(0, c^2)\). Now we just3 need a prior on \(c\). Read Pironen and Vehtari if you want all of the gory details. We’ll just use hs() as a prior in our model, setting the global_scale parameter according to their advice.

Setting up the data

Once again we have to start by generating the data.

library(tidyverse)
library(reshape2)
library(ggplot2)
library(cowplot)
library(mvtnorm)
library(corrplot)

rm(list = ls())
## intetcept
##
beta0 <- 1.0
## regression coefficients
##
beta <- c(1.0, -1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
## pattern of correlation matrix, all non-zero entries are set to saem
## correlation, covariance matrix caldulated from individual variances and a 
## single association parameter governing the non-zero correlation coefficients
##
## Note: Not just any pattern will work here. The correlation matrix and
## covariance matrix generated from this pattern must be positive definite.
## If you change this pattern, you may get an error when you try to generate
## data with a non-zero association parameter.
##
Rho <- matrix(nrow = 9, ncol = , byrow = TRUE, 
              data = c(1,0,1,0,1,0,1,0,1,
                       0,1,0,1,0,1,0,1,0,
                       1,0,1,0,1,0,1,0,1,
                       0,1,0,1,0,1,0,1,0,
                       1,0,1,0,1,0,1,0,1,
                       0,1,0,1,0,1,0,1,0,
                       1,0,1,0,1,0,1,0,1,
                       0,1,0,1,0,1,0,1,0,
                       1,0,1,0,1,0,1,0,1
                       ))
## vector of standard deviations for covariates
##
sigma <- rep(1, 9)

## construct a covariance matrix from the pattern, standard deviations, and
## one parameter in [-1,1] that governs the magnitude of non-zero correlation
## coefficients
##
## Rho - the pattern of associations
## sigma - the vector of standard deviations
## rho - the association parameter
##
construct_Sigma <- function(Rho, sigma, rho) {
  ## get the correlation matris
  ##
  Rho <- Rho*rho
  for (i in 1:ncol(Rho)) {
    Rho[i,i] <- 1.0
  }
  ## notice the use of matrix multiplication
  ##
  Sigma <- diag(sigma) %*% Rho %*% diag(sigma)
  return(Sigma)
}

## set the random number seed manually so that every run of the code will 
## produce the same numbers
##
set.seed(1234)

n_samp <- 100
cov_str <- rmvnorm(n_samp,
                   mean = rep(0, nrow(Rho)),
                   sigma = construct_Sigma(Rho, sigma, 0.8))

resid <- rep(2.0, n_samp)

y_str <- rnorm(nrow(cov_str), mean = beta0 + cov_str %*% beta, sd = resid)
dat_1 <- data.frame(y_str, cov_str, rep("Strong", length(y_str)))

cov_str <- rmvnorm(n_samp,
                   mean = rep(0, nrow(Rho)),
                   sigma = construct_Sigma(Rho, sigma, 0.8))
y_str <- rnorm(nrow(cov_str), mean = beta0 + cov_str %*% beta, sd = resid)
dat_2 <- data.frame(y_str, cov_str, rep("Strong", length(y_str)))

column_names <- c("y", paste("x", seq(1, length(beta)), sep = ""), "Scenario")
colnames(dat_1) <- column_names
colnames(dat_2) <- column_names

## saving results in scale allows me to use them later for prediction with
## new data
##
scale_1 <- lapply(dat_1[, 1:10], scale)
scale_2 <- lapply(dat_2[, 1:10], scale)

## when assigning the same scaling to a data frame, the scaling attributes
## are lost
##
dat_1[, 1:10] <- lapply(dat_1[, 1:10], scale)
dat_2[, 1:10] <- lapply(dat_2[, 1:10], scale)

Trying the horseshoe prior

Now that we have the data, let’s try the horshoe prior.

library(rstanarm)

options(mc.cores = parallel::detectCores())

set.seed(1234)

## n is the number of observations
## D is the number of covariates
## p0 is the expected number of important covariates
##
n <- nrow(dat_1)
D <- ncol(dat_1[,2:10])
p0 <- 3
tau0 <- p0/(D - p0) * 1/sqrt(n)
prior_coeff <- hs(global_scale = tau0, slab_scale = 1)

fit_1 <- stan_glm(y ~ x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9,
                  data = dat_1,
                  prior = prior_coeff,
                  refresh = 0,
                  adapt_delta = 0.999)
## Note: I use the same prior scale here because both data sets have the same number of observations
## and the same expected number of important covariates
##
fit_2 <- stan_glm(y ~ x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9,
                  data = dat_2,
                  prior = prior_coeff,
                  refresh = 0,
                  adapt_delta = 0.999)

predict_1 <- posterior_predict(fit_1)
predict_2 <- posterior_predict(fit_2)

for.plot <- data.frame(Observed = dat_1$y, Predicted = apply(predict_1, 2, mean))
p <- ggplot(for.plot, aes(x = Observed, y = Predicted)) +
  geom_point() +
  geom_abline(slope = 1, intercept = 0) +
  geom_smooth(method = "lm") +
  ggtitle("Data set 1")
print(p)


for.plot <- data.frame(Observed = dat_2$y, Predicted = apply(predict_2, 2, mean))
p <- ggplot(for.plot, aes(x = Observed, y = Predicted)) +
  geom_point() +
  geom_abline(slope = 1, intercept = 0) +
  geom_smooth(method = "lm") +
  ggtitle("Data set 2")
print(p)


cat("Results from data set 1\n")
Results from data set 1
summary(fit_1, digits = 3)

Model Info:

 function:     stan_glm
 family:       gaussian [identity]
 formula:      y ~ x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9
 algorithm:    sampling
 priors:       see help('prior_summary')
 sample:       4000 (posterior sample size)
 observations: 100
 predictors:   10

Estimates:
                mean     sd       2.5%     25%      50%      75%      97.5% 
(Intercept)      0.002    0.081   -0.159   -0.052    0.003    0.054    0.166
x1               0.202    0.153   -0.022    0.067    0.197    0.312    0.508
x2              -0.051    0.086   -0.267   -0.092   -0.023    0.002    0.073
x3               0.303    0.159    0.000    0.194    0.314    0.417    0.595
x4              -0.039    0.078   -0.234   -0.073   -0.017    0.003    0.086
x5               0.017    0.077   -0.131   -0.015    0.004    0.043    0.204
x6              -0.043    0.081   -0.248   -0.078   -0.018    0.003    0.083
x7              -0.006    0.075   -0.187   -0.027   -0.001    0.022    0.148
x8              -0.027    0.079   -0.223   -0.058   -0.009    0.008    0.119
x9               0.117    0.155   -0.066    0.002    0.060    0.202    0.514
sigma            0.808    0.060    0.700    0.766    0.804    0.845    0.937
mean_PPD         0.000    0.113   -0.229   -0.075    0.001    0.075    0.219
log-posterior -177.435    4.780 -187.876 -180.399 -177.095 -174.074 -169.109

Diagnostics:
              mcse  Rhat  n_eff
(Intercept)   0.001 1.000 5339 
x1            0.003 1.000 2125 
x2            0.002 1.000 2634 
x3            0.003 1.000 2334 
x4            0.001 1.000 3196 
x5            0.001 1.000 4265 
x6            0.001 1.000 2960 
x7            0.001 0.999 4140 
x8            0.001 1.000 3831 
x9            0.004 1.001 1807 
sigma         0.001 1.000 3766 
mean_PPD      0.002 1.000 4337 
log-posterior 0.137 1.001 1211 

For each parameter, mcse is Monte Carlo standard error, 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).
cat("\n\nResults from data set 2\n")


Results from data set 2
summary(fit_2, digits = 3)

Model Info:

 function:     stan_glm
 family:       gaussian [identity]
 formula:      y ~ x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9
 algorithm:    sampling
 priors:       see help('prior_summary')
 sample:       4000 (posterior sample size)
 observations: 100
 predictors:   10

Estimates:
                mean     sd       2.5%     25%      50%      75%      97.5% 
(Intercept)      0.000    0.074   -0.145   -0.050    0.001    0.050    0.147
x1               0.118    0.120   -0.037    0.016    0.091    0.198    0.392
x2              -0.172    0.164   -0.511   -0.298   -0.142   -0.022    0.046
x3               0.467    0.135    0.186    0.386    0.474    0.560    0.709
x4               0.005    0.078   -0.163   -0.025    0.001    0.033    0.180
x5               0.018    0.073   -0.119   -0.014    0.005    0.045    0.197
x6              -0.259    0.172   -0.570   -0.390   -0.272   -0.116    0.019
x7               0.009    0.070   -0.131   -0.018    0.002    0.035    0.171
x8               0.010    0.076   -0.139   -0.019    0.002    0.037    0.192
x9              -0.011    0.072   -0.190   -0.035   -0.002    0.019    0.131
sigma            0.740    0.056    0.641    0.700    0.735    0.777    0.860
mean_PPD         0.000    0.108   -0.211   -0.073   -0.001    0.072    0.214
log-posterior -169.089    4.782 -179.092 -172.021 -168.731 -165.702 -160.714

Diagnostics:
              mcse  Rhat  n_eff
(Intercept)   0.001 1.000 4646 
x1            0.002 1.000 2371 
x2            0.004 1.002 1576 
x3            0.002 0.999 3194 
x4            0.001 0.999 4414 
x5            0.001 1.000 4767 
x6            0.004 1.002 1531 
x7            0.001 1.000 4473 
x8            0.001 0.999 4565 
x9            0.001 1.000 4384 
sigma         0.001 0.999 3974 
mean_PPD      0.002 1.000 4471 
log-posterior 0.141 1.002 1158 

For each parameter, mcse is Monte Carlo standard error, 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).

There are several things I like about using regularized horeshoe priors in rstanarm rather than the Lasso.

  1. I get an assessment of how reliable estimates of the regression coefficients are in addition to a point estimate of what they are. In this case you can see that the 95% credible intervals overlap 0 for every coefficient in analysis of the first data set, while only the credible intervals for x3 don’t overlap 0 in analysis of the second data set. Notice, however, that some of the intervals are very close to not overlapping 0, i.e., the intervals for x1, x3, and x9 in the analysis of data set 1 and the intervals for x1, x2, and x6 in the analysis of data set 2.

  2. I can also plot the estimates and their uncertainty very easily. Plotting the estimates and their uncertainty makes is much easier to pick out the covariates that seem to have an association with the response variable.4 Note: The outer intervals in these plots correspond to 90% credible intervals, not 95% credible intervals. You could change that by specifying prob_outer = 0.95 in the call to plot().

p <- plot(fit_1) + ggtitle("Estimates from data set 1")
print(p)

p <- plot(fit_2) + ggtitle("Estimates from data set 2")
print(p)

  1. I can use the regularized horsehoe prior in a generalized mixed model with stan_glmer(). There is a package to fit the Lasso to a generalized mixed model with lmer(), but I haven’t tried it, and it isn’t built into lmer().

How stable are the predictions?

If you look back at earlier installments in this series, you’ll see that the point estimates we got here aren’t too different from what we’ve seen before, e.g., 0.185 for x1 in data set 1 here vs. 0.239 in data set 1 from the Lasso. Using this Bayesian approach, however, we can see that even though x3 isn’t “significant” in the analysis of the first data set and is in the second,5 we don’t have good evidence that the estimates are different, because the posterior distributions are broadly overlapping as evidenced by the broadly overlapping credible intervals. In other words, if we pay appropriate attention to the uncertainty of our estimates, they’re pretty stable (at least across the two sample data sets we’ve been exploring).

What about out of sample predictions?

new_data <- data.frame(x1 = 4.0, x2 = 4.0, x3 = 4.0,
                       x4 = 4.0, x5 = 4.0, x6 = 4.0,
                       x7 = 4.0, x8 = 4.0, x9 = 4.0)


## re-scale the new data by subtracting mean and dividing by standard deviation
##
new_data_1 <- (new_data - lapply(scale_1[2:10], attr, "scaled:center")) /
               lapply(scale_1[2:10], attr, "scaled:scale")
new_data_2 <- (new_data - lapply(scale_2[2:10], attr, "scaled:center")) /
               lapply(scale_2[2:10], attr, "scaled:scale")
  
predict_1 <- posterior_predict(fit_1, new_data_1)
predict_2 <- posterior_predict(fit_2, new_data_2)

summarize_posterior <- function(x, credible = 0.95, digits = 3) {
  lo_p <- (1.0 - credible)/2.0
  hi_p <- credible + lo_p
  ci <- quantile(x, c(lo_p, hi_p))
  cat(round(mean(x), 3), " (", round(ci[1], 3), ",", round(ci[2], 3), ")\n", sep = "")
}

cat("Data set 1\n")
Data set 1
summarize_posterior(predict_1)
2.019 (0.12,3.976)
cat("  True answer: ", beta0 + as.matrix(new_data_1) %*% beta, "\n", 
    sep = "")
  True answer: 5.583683
cat("\nData set 2\n")

Data set 2
summarize_posterior(predict_2)
0.963 (-0.733,2.68)
cat("  True answer: ", beta0 + as.matrix(new_data_2) %*% beta, "\n", 
    sep = "")
  True answer: 5.711435

Those are pretty close to the Lasso predictions, but we have the advantage that they include an indication of how reliable the estimates are, and you can see that we don’t have good evidence that the predictions are different from one another even though the point estimates look fairly different. That’s the good news. The bad news is that neither of the prediction intervals include the true value.(Again, the true values differ because the scaling differs between the data sets.) If you’re getting the message that out of sample extrapolation is tricky, you’re getting the right message. Just imagine how much trickier it would be if the true relationship were non-linear rather than linear.

How do you pick the global scale

If you’re still paying attention (a) I congratulate you and (b) I owe you an explanation of how I set prior_coeff in the call to stan_glm(). The explanation is simple. I followed the advice in the help page for “Prior distributions and options” in the rstanarm package, i.e., help("priors"). I set it to “the ratio of the expected number of non-zero coefficients divided by the square root of the number of observations.” In fact, I cheated even a little more than that. I simply modified the code at https://mc-stan.org/projpred/articles/quickstart.html.6 I haven’t played around with other values, but I encourage you to try some. For example, instead of 3 in the code above try 1 and 8 to see how different your results are.

mtcars$mpg10 <- mtcars$mpg/10
fit <- stan_glm(mpg10 ~ wt + cyl + am, data = mtcars, QR = FALSE, refresh = 0)
prior_summary(fit)
Priors for model 'fit' 
------
Intercept (after predictors centered)
 ~ normal(location = 0, scale = 10)
     **adjusted scale = 6.03

Coefficients
 ~ normal(location = [0,0,0], scale = [2.5,2.5,2.5])
     **adjusted scale = [1.54,0.84,1.51]

Auxiliary (sigma)
 ~ exponential(rate = 1)
     **adjusted scale = 0.60 (adjusted rate = 1/adjusted scale)
------
See help('prior_summary.stanreg') for more details

  1. Pironen, J., and A. Vehtari. 2017. Sparsity information and regularization in the horseshoe and other shrinkage priors. Electronic Journal of Statistics 11(2):5018-5051. doi: 10.1214/17-EJS1337SI

  2. Note: You can check this easily using prior_summary(). See above (in HTML) or here (in an R notebook) to see how, using the builtin mtcars data set as an example.

  3. If you’ve been following along this far, you probably don’t regard this last step as qualifying for the word “just”, but it is the last step - really.

  4. If you’re uncomfortable with Bayesian inference, it’s worth noting that the covariates identified in the two analyses are pretty similar. In fact, I singled out the same covariates as important in the analysis of data set 2 here as the Lasso identified, and all of the covariates I identified here in the analysis of data set 1 were also identified in analysis using the Lasso (the Lasso identified three more).

  5. In the sense that the 95% credible interval for x3 overlaps 0 in analysis of the first data set and doesn’t in the second.

  6. If you visit https://mc-stan.org/projpred/articles/quickstart.html, you’ll see that it describes a package called prodprej. We’ll explore that in the next installment.

LS0tCnRpdGxlOiAiQSBCYXllc2lhbiBhcHByb2FjaCB0byB2YXJpYWJsZSBzZWxlY3Rpb24gdXNpbmcgaG9yc2VzaG9lIHByaW9ycyIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKSXQgd29uJ3QgY29tZSBhcyBhIHN1cnByaXNlIHRvIGFueW9uZSB3aG8ga25vd3MgbWUgdGhhdCBJIGhhdmUgdG8gdHJ5IGEgQmF5ZXNpYW4gYXBwcm9hY2ggdG8gdmFyaWFibGUgc2VsZWN0aW9uLiBJdCBhbHNvIHR1cm5zIG91dCB0aGF0IHRoZXJlJ3MgYW4gZWFzeSB3YXkgdG8gZG8gaXQsIHNpbmNlICJob3JzZXNob2UgcHJpb3JzIiBhcmUgYnVpbHQgaW50byBgcnN0YW5hcm1gLiBXaGF0J3MgYSBob3Jlc2hvZSBwcmlvciB5b3UgYXNrPyBXZWxsIHlvdSBjYW4gc2VlIGEgZmV3IG9mIHRoZSBkZXRhaWxzIGluIHRoZSBuZXh0IHNlY3Rpb24sIG9yIHlvdSBjYW4ganVzdCBza2lwIHRvIHRoZSBzZWN0aW9uIHdoZXJlIHdlIHVzZSBpdCBpbiBgcnN0YW5hcm1gIGlmIHlvdSBmaW5kIHRoZSBtYXRoIG1vcmUgY29uZnVzaW5nIHRoYW4gaGVscGZ1bC4KCiMjIFdoYXQgaXMgYSBob3JzZWhvZSBwcmlvcj8KCklmIHlvdSByZWFsbHkgd2FudCB0byBrbm93IGFuZCB1bmRlcnN0YW5kIGhvcnNlc2hvZSBwcmlvcnMsIHlvdSdsbCBuZWVkIHRvIHJlYWQgYSBwYXBlciBieSBKdWhvIFBpcm9uZW4gYW5kIEFraSBWZWh0YXJpIGluIHRoZSBfRWxlY3Ryb25pYyBKb3VybmFsIG9mIFN0YXRpc3RpY3NfLFteMV0gYnV0IGhlcmUncyBhIGJyaWVmIG91dGxpbmUuCgpUaGUgbGlrZWxpaG9vZCBpbiBzdGFuZGFyZCBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbCBsb29rcyBsaWtlIHRoaXMKCiQkClxlcWFsaWduewp5X2kgJlxzaW0mIFxtYm94e059KFxtdV9pLCBcc2lnbWFeMikgXFwKXG11X2kgJj0mIFxiZXRhXzAgKyBcc3VtX2ogXGJldGFfanhfe2lqfSAgCn0KJCQKClRvIGNvbXBsZXRlIHRoZSBCYXllc2lhbiBtb2RlbCwgd2UgbmVlZCB0byBzcGVjaWZ5IHByaW9ycyBmb3IgJFxzaWdtYV4yJCBhbmQgdGhlICRcYmV0YSRzLiBUaGUgZGVmYXVsdCBjaG9pY2VzIGluIGByc3RhbmFybWAgYXJlOlteMl0KCiQkClxlcWFsaWduewpcc2lnbWEgJlxzaW0mIFxtYm94e0V4cH0oMSkgXFwKXGJldGFfMCAmXHNpbSYgXG1ib3h7Tn0oMCwgMTApIFxcClxiZXRhX2kgJlxzaW0mIFxtYm94e059KDAsIDIuNSkgXHF1YWQgXG1ib3h7Zm9yIH0gaSA+IDAKfQokJAoKVGhlc2UgYXJlIHRoZSBjaG9pY2VzIHdlIHVzZWQgW2hlcmVdKGh0dHA6Ly9kYXJ3aW4uZWViLnVjb25uLmVkdS9wYWdlcy92YXJpYWJsZS1zZWxlY3Rpb24vcmVkdWNpbmctdGhlLW51bWJlci1vZi1jb3ZhcmlhdGVzLm5iLmh0bWwpLiBUaGUgYmFzaWMgaG9yc2Vob2UgcHJpb3IgYWZmZWN0cyBvbmx5IHRoZSBsYXN0IG9mIHRoZXNlLiBTcGVjaWZpY2FsbHksCgokJApcZXFhbGlnbnsKXGJldGFfaSAmXHNpbSYgXG1ib3h7Tn0oMCwgXHRhdV4yXGxhbWJkYV9pXjIpIFxcClxsYW1iZGFfaSAmXHNpbSYgXG1ib3h7Q2F1Y2h5fV4rKDAsIDEpIFxxdWFkICwKfQokJAoKd2hlcmUgJFxtYm94e0NhdWNoeX1eKyQgcmVmZXJzIHRvIGEgaGFsZi1DYXVjaHkgZGlzdHJpYnV0aW9uIG9uIHRoZSBwb3NpdGl2ZSByZWFsIGxpbmUuIEEgQ2F1Y2h5IGRpc3RyaWJ1dGlvbiBoYXMgdmVyeSBmYXQgdGFpbHMsIHNvIHRoZSB0ZW5kZW5jeSBmb3IgYW55ICRcYmV0YV9pJCBpcyB0byBiZSBlaXRoZXIgY2xvc2UgdG8gemVybywgYmVjYXVzZSAkXGxhbWJkYV9pJCBpcyBzbWFsbCwgb3Igd2VsbCBhd2F5IGZyb20gemVybywgYmVjYXVzZSAkXGxhbWJkYV9pJCBpcyBsYXJnZS4gJFx0YXUkIHNldHMgdGhlIHRvdGFsIGFtb3VudCBvZiBpbmZsdWVuY2UgdGhlIGNvdmFyaWF0ZXMgaGF2ZSBvbiB0aGUgcmVzcG9uc2UsIHJhdGhlciBsaWtlICRcbGFtYmRhJCBpbiB0aGUgW0xhc3NvXShodHRwOi8vZGFyd2luLmVlYi51Y29ubi5lZHUvcGFnZXMvdmFyaWFibGUtc2VsZWN0aW9uL3VzaW5nLXRoZS1sYXNzby5uYi5odG1sKS4KCkFzIFBpcm9uZW4gYW5kIFZlaHRhcmkgZXhwbGFpbiwgaG93ZXZlciwgdGhlcmUgaGFzbid0IGJlZW4gY29uc2Vuc3VzIG9uIGhvdyB0byBzZXQgb3IgZXN0aW1hdGUgJFx0YXUkLiBUaGV5IGludHJvZHVjZSBhIG5ldyB2ZXJzaW9uIG9mIHRoZSBob3JzZXNob2UgcHJpb3IsIHRoZSBfcmVndWxhcml6ZWQgaG9yc2VzaG9lIHByaW9yXyB0aGF0IGxvb2tzIGxpa2UgdGhpcwoKJCQKXGVxYWxpZ257ClxiZXRhX2kgJlxzaW0mIFxtYm94e059KDAsIFx0YXVeMlx0aWxkZVxsYW1iZGFfaV4yKSBcXApcdGlsZGVcbGFtYmRhX2kgJj0mIFxmcmFje2NeMlxsYW1iZGFfaV4yfXtjXjIgKyBcdGF1XjJcbGFtYmRhX2leMn0gXFwKXGxhbWJkYV9pICZcc2ltJiBcbWJveHtDYXVjaHl9XisoMCwgMSkgXHF1YWQgLgp9CiQkCgpOb3RpY2UgdGhhdCBpZiAkXHRhdV4yXGxhbWJkYV9pXjIgXGxsIGNeMiQsIG1lYW5pbmcgdGhhdCAkXGJldGFfaSQgaXMgY2xvc2UgdG8gMCwgdGhlbiB3ZSBoYXZlIHNvbWV0aGluZyB2ZXJ5IGNsb3NlIHRvIHRoZSBvcmlnaW5hbCBob3JzZXNob2UgcHJpb3IuIElmIG9uIHRoZSBvdGhlciBoYW5kLCAkXHRhdV4yXGxhbWJkYV9pXjIgXGdnIGNeMiQgdGhlbiB0aGUgcHJpb3Igb24gJFxiZXRhX2kkIGlzIGNsb3NlIHRvICRcbWJveHtOfSgwLCBjXjIpJC4gTm93IHdlIGp1c3RbXjNdIG5lZWQgYSBwcmlvciBvbiAkYyQuIFJlYWQgUGlyb25lbiBhbmQgVmVodGFyaSBpZiB5b3Ugd2FudCBhbGwgb2YgdGhlIGdvcnkgZGV0YWlscy4gV2UnbGwganVzdCB1c2UgYGhzKClgIGFzIGEgcHJpb3IgaW4gb3VyIG1vZGVsLCBzZXR0aW5nIHRoZSBgZ2xvYmFsX3NjYWxlYCBwYXJhbWV0ZXIgYWNjb3JkaW5nIHRvIHRoZWlyIGFkdmljZS4KCiMjIFNldHRpbmcgdXAgdGhlIGRhdGEKCk9uY2UgYWdhaW4gd2UgaGF2ZSB0byBzdGFydCBieSBnZW5lcmF0aW5nIHRoZSBkYXRhLgpgYGB7ciBzZXR1cCwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHJlc2hhcGUyKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoY293cGxvdCkKbGlicmFyeShtdnRub3JtKQpsaWJyYXJ5KGNvcnJwbG90KQoKcm0obGlzdCA9IGxzKCkpCmBgYAoKYGBge3J9CiMjIGludGV0Y2VwdAojIwpiZXRhMCA8LSAxLjAKIyMgcmVncmVzc2lvbiBjb2VmZmljaWVudHMKIyMKYmV0YSA8LSBjKDEuMCwgLTEuMCwgMS4wLCAwLjAsIDAuMCwgMC4wLCAwLjAsIDAuMCwgMC4wKQojIyBwYXR0ZXJuIG9mIGNvcnJlbGF0aW9uIG1hdHJpeCwgYWxsIG5vbi16ZXJvIGVudHJpZXMgYXJlIHNldCB0byBzYWVtCiMjIGNvcnJlbGF0aW9uLCBjb3ZhcmlhbmNlIG1hdHJpeCBjYWxkdWxhdGVkIGZyb20gaW5kaXZpZHVhbCB2YXJpYW5jZXMgYW5kIGEgCiMjIHNpbmdsZSBhc3NvY2lhdGlvbiBwYXJhbWV0ZXIgZ292ZXJuaW5nIHRoZSBub24temVybyBjb3JyZWxhdGlvbiBjb2VmZmljaWVudHMKIyMKIyMgTm90ZTogTm90IGp1c3QgYW55IHBhdHRlcm4gd2lsbCB3b3JrIGhlcmUuIFRoZSBjb3JyZWxhdGlvbiBtYXRyaXggYW5kCiMjIGNvdmFyaWFuY2UgbWF0cml4IGdlbmVyYXRlZCBmcm9tIHRoaXMgcGF0dGVybiBtdXN0IGJlIHBvc2l0aXZlIGRlZmluaXRlLgojIyBJZiB5b3UgY2hhbmdlIHRoaXMgcGF0dGVybiwgeW91IG1heSBnZXQgYW4gZXJyb3Igd2hlbiB5b3UgdHJ5IHRvIGdlbmVyYXRlCiMjIGRhdGEgd2l0aCBhIG5vbi16ZXJvIGFzc29jaWF0aW9uIHBhcmFtZXRlci4KIyMKUmhvIDwtIG1hdHJpeChucm93ID0gOSwgbmNvbCA9ICwgYnlyb3cgPSBUUlVFLCAKICAgICAgICAgICAgICBkYXRhID0gYygxLDAsMSwwLDEsMCwxLDAsMSwKICAgICAgICAgICAgICAgICAgICAgICAwLDEsMCwxLDAsMSwwLDEsMCwKICAgICAgICAgICAgICAgICAgICAgICAxLDAsMSwwLDEsMCwxLDAsMSwKICAgICAgICAgICAgICAgICAgICAgICAwLDEsMCwxLDAsMSwwLDEsMCwKICAgICAgICAgICAgICAgICAgICAgICAxLDAsMSwwLDEsMCwxLDAsMSwKICAgICAgICAgICAgICAgICAgICAgICAwLDEsMCwxLDAsMSwwLDEsMCwKICAgICAgICAgICAgICAgICAgICAgICAxLDAsMSwwLDEsMCwxLDAsMSwKICAgICAgICAgICAgICAgICAgICAgICAwLDEsMCwxLDAsMSwwLDEsMCwKICAgICAgICAgICAgICAgICAgICAgICAxLDAsMSwwLDEsMCwxLDAsMQogICAgICAgICAgICAgICAgICAgICAgICkpCiMjIHZlY3RvciBvZiBzdGFuZGFyZCBkZXZpYXRpb25zIGZvciBjb3ZhcmlhdGVzCiMjCnNpZ21hIDwtIHJlcCgxLCA5KQoKIyMgY29uc3RydWN0IGEgY292YXJpYW5jZSBtYXRyaXggZnJvbSB0aGUgcGF0dGVybiwgc3RhbmRhcmQgZGV2aWF0aW9ucywgYW5kCiMjIG9uZSBwYXJhbWV0ZXIgaW4gWy0xLDFdIHRoYXQgZ292ZXJucyB0aGUgbWFnbml0dWRlIG9mIG5vbi16ZXJvIGNvcnJlbGF0aW9uCiMjIGNvZWZmaWNpZW50cwojIwojIyBSaG8gLSB0aGUgcGF0dGVybiBvZiBhc3NvY2lhdGlvbnMKIyMgc2lnbWEgLSB0aGUgdmVjdG9yIG9mIHN0YW5kYXJkIGRldmlhdGlvbnMKIyMgcmhvIC0gdGhlIGFzc29jaWF0aW9uIHBhcmFtZXRlcgojIwpjb25zdHJ1Y3RfU2lnbWEgPC0gZnVuY3Rpb24oUmhvLCBzaWdtYSwgcmhvKSB7CiAgIyMgZ2V0IHRoZSBjb3JyZWxhdGlvbiBtYXRyaXMKICAjIwogIFJobyA8LSBSaG8qcmhvCiAgZm9yIChpIGluIDE6bmNvbChSaG8pKSB7CiAgICBSaG9baSxpXSA8LSAxLjAKICB9CiAgIyMgbm90aWNlIHRoZSB1c2Ugb2YgbWF0cml4IG11bHRpcGxpY2F0aW9uCiAgIyMKICBTaWdtYSA8LSBkaWFnKHNpZ21hKSAlKiUgUmhvICUqJSBkaWFnKHNpZ21hKQogIHJldHVybihTaWdtYSkKfQoKIyMgc2V0IHRoZSByYW5kb20gbnVtYmVyIHNlZWQgbWFudWFsbHkgc28gdGhhdCBldmVyeSBydW4gb2YgdGhlIGNvZGUgd2lsbCAKIyMgcHJvZHVjZSB0aGUgc2FtZSBudW1iZXJzCiMjCnNldC5zZWVkKDEyMzQpCgpuX3NhbXAgPC0gMTAwCmNvdl9zdHIgPC0gcm12bm9ybShuX3NhbXAsCiAgICAgICAgICAgICAgICAgICBtZWFuID0gcmVwKDAsIG5yb3coUmhvKSksCiAgICAgICAgICAgICAgICAgICBzaWdtYSA9IGNvbnN0cnVjdF9TaWdtYShSaG8sIHNpZ21hLCAwLjgpKQoKcmVzaWQgPC0gcmVwKDIuMCwgbl9zYW1wKQoKeV9zdHIgPC0gcm5vcm0obnJvdyhjb3Zfc3RyKSwgbWVhbiA9IGJldGEwICsgY292X3N0ciAlKiUgYmV0YSwgc2QgPSByZXNpZCkKZGF0XzEgPC0gZGF0YS5mcmFtZSh5X3N0ciwgY292X3N0ciwgcmVwKCJTdHJvbmciLCBsZW5ndGgoeV9zdHIpKSkKCmNvdl9zdHIgPC0gcm12bm9ybShuX3NhbXAsCiAgICAgICAgICAgICAgICAgICBtZWFuID0gcmVwKDAsIG5yb3coUmhvKSksCiAgICAgICAgICAgICAgICAgICBzaWdtYSA9IGNvbnN0cnVjdF9TaWdtYShSaG8sIHNpZ21hLCAwLjgpKQp5X3N0ciA8LSBybm9ybShucm93KGNvdl9zdHIpLCBtZWFuID0gYmV0YTAgKyBjb3Zfc3RyICUqJSBiZXRhLCBzZCA9IHJlc2lkKQpkYXRfMiA8LSBkYXRhLmZyYW1lKHlfc3RyLCBjb3Zfc3RyLCByZXAoIlN0cm9uZyIsIGxlbmd0aCh5X3N0cikpKQoKY29sdW1uX25hbWVzIDwtIGMoInkiLCBwYXN0ZSgieCIsIHNlcSgxLCBsZW5ndGgoYmV0YSkpLCBzZXAgPSAiIiksICJTY2VuYXJpbyIpCmNvbG5hbWVzKGRhdF8xKSA8LSBjb2x1bW5fbmFtZXMKY29sbmFtZXMoZGF0XzIpIDwtIGNvbHVtbl9uYW1lcwoKIyMgc2F2aW5nIHJlc3VsdHMgaW4gc2NhbGUgYWxsb3dzIG1lIHRvIHVzZSB0aGVtIGxhdGVyIGZvciBwcmVkaWN0aW9uIHdpdGgKIyMgbmV3IGRhdGEKIyMKc2NhbGVfMSA8LSBsYXBwbHkoZGF0XzFbLCAxOjEwXSwgc2NhbGUpCnNjYWxlXzIgPC0gbGFwcGx5KGRhdF8yWywgMToxMF0sIHNjYWxlKQoKIyMgd2hlbiBhc3NpZ25pbmcgdGhlIHNhbWUgc2NhbGluZyB0byBhIGRhdGEgZnJhbWUsIHRoZSBzY2FsaW5nIGF0dHJpYnV0ZXMKIyMgYXJlIGxvc3QKIyMKZGF0XzFbLCAxOjEwXSA8LSBsYXBwbHkoZGF0XzFbLCAxOjEwXSwgc2NhbGUpCmRhdF8yWywgMToxMF0gPC0gbGFwcGx5KGRhdF8yWywgMToxMF0sIHNjYWxlKQpgYGAKCiMjIFRyeWluZyB0aGUgaG9yc2VzaG9lIHByaW9yCgpOb3cgdGhhdCB3ZSBoYXZlIHRoZSBkYXRhLCBsZXQncyB0cnkgdGhlIGhvcnNob2UgcHJpb3IuCgpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CmxpYnJhcnkocnN0YW5hcm0pCgpvcHRpb25zKG1jLmNvcmVzID0gcGFyYWxsZWw6OmRldGVjdENvcmVzKCkpCgpzZXQuc2VlZCgxMjM0KQoKIyMgbiBpcyB0aGUgbnVtYmVyIG9mIG9ic2VydmF0aW9ucwojIyBEIGlzIHRoZSBudW1iZXIgb2YgY292YXJpYXRlcwojIyBwMCBpcyB0aGUgZXhwZWN0ZWQgbnVtYmVyIG9mIGltcG9ydGFudCBjb3ZhcmlhdGVzCiMjCm4gPC0gbnJvdyhkYXRfMSkKRCA8LSBuY29sKGRhdF8xWywyOjEwXSkKcDAgPC0gMwp0YXUwIDwtIHAwLyhEIC0gcDApICogMS9zcXJ0KG4pCnByaW9yX2NvZWZmIDwtIGhzKGdsb2JhbF9zY2FsZSA9IHRhdTAsIHNsYWJfc2NhbGUgPSAxKQoKZml0XzEgPC0gc3Rhbl9nbG0oeSB+IHgxICsgeDIgKyB4MyArIHg0ICsgeDUgKyB4NiArIHg3ICsgeDggKyB4OSwKICAgICAgICAgICAgICAgICAgZGF0YSA9IGRhdF8xLAogICAgICAgICAgICAgICAgICBwcmlvciA9IHByaW9yX2NvZWZmLAogICAgICAgICAgICAgICAgICByZWZyZXNoID0gMCwKICAgICAgICAgICAgICAgICAgYWRhcHRfZGVsdGEgPSAwLjk5OSkKIyMgTm90ZTogSSB1c2UgdGhlIHNhbWUgcHJpb3Igc2NhbGUgaGVyZSBiZWNhdXNlIGJvdGggZGF0YSBzZXRzIGhhdmUgdGhlIHNhbWUgbnVtYmVyIG9mIG9ic2VydmF0aW9ucwojIyBhbmQgdGhlIHNhbWUgZXhwZWN0ZWQgbnVtYmVyIG9mIGltcG9ydGFudCBjb3ZhcmlhdGVzCiMjCmZpdF8yIDwtIHN0YW5fZ2xtKHkgfiB4MSArIHgyICsgeDMgKyB4NCArIHg1ICsgeDYgKyB4NyArIHg4ICsgeDksCiAgICAgICAgICAgICAgICAgIGRhdGEgPSBkYXRfMiwKICAgICAgICAgICAgICAgICAgcHJpb3IgPSBwcmlvcl9jb2VmZiwKICAgICAgICAgICAgICAgICAgcmVmcmVzaCA9IDAsCiAgICAgICAgICAgICAgICAgIGFkYXB0X2RlbHRhID0gMC45OTkpCgpwcmVkaWN0XzEgPC0gcG9zdGVyaW9yX3ByZWRpY3QoZml0XzEpCnByZWRpY3RfMiA8LSBwb3N0ZXJpb3JfcHJlZGljdChmaXRfMikKCmZvci5wbG90IDwtIGRhdGEuZnJhbWUoT2JzZXJ2ZWQgPSBkYXRfMSR5LCBQcmVkaWN0ZWQgPSBhcHBseShwcmVkaWN0XzEsIDIsIG1lYW4pKQpwIDwtIGdncGxvdChmb3IucGxvdCwgYWVzKHggPSBPYnNlcnZlZCwgeSA9IFByZWRpY3RlZCkpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21fYWJsaW5lKHNsb3BlID0gMSwgaW50ZXJjZXB0ID0gMCkgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIpICsKICBnZ3RpdGxlKCJEYXRhIHNldCAxIikKcHJpbnQocCkKCmZvci5wbG90IDwtIGRhdGEuZnJhbWUoT2JzZXJ2ZWQgPSBkYXRfMiR5LCBQcmVkaWN0ZWQgPSBhcHBseShwcmVkaWN0XzIsIDIsIG1lYW4pKQpwIDwtIGdncGxvdChmb3IucGxvdCwgYWVzKHggPSBPYnNlcnZlZCwgeSA9IFByZWRpY3RlZCkpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21fYWJsaW5lKHNsb3BlID0gMSwgaW50ZXJjZXB0ID0gMCkgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIpICsKICBnZ3RpdGxlKCJEYXRhIHNldCAyIikKcHJpbnQocCkKCmNhdCgiUmVzdWx0cyBmcm9tIGRhdGEgc2V0IDFcbiIpCnN1bW1hcnkoZml0XzEsIGRpZ2l0cyA9IDMpCgpjYXQoIlxuXG5SZXN1bHRzIGZyb20gZGF0YSBzZXQgMlxuIikKc3VtbWFyeShmaXRfMiwgZGlnaXRzID0gMykKYGBgCgpUaGVyZSBhcmUgc2V2ZXJhbCB0aGluZ3MgSSBsaWtlIGFib3V0IHVzaW5nIHJlZ3VsYXJpemVkIGhvcmVzaG9lIHByaW9ycyBpbiBgcnN0YW5hcm1gIHJhdGhlciB0aGFuIHRoZSBMYXNzby4gIAoKMS4gSSBnZXQgYW4gYXNzZXNzbWVudCBvZiBob3cgcmVsaWFibGUgZXN0aW1hdGVzIG9mIHRoZSByZWdyZXNzaW9uIGNvZWZmaWNpZW50cyBhcmUgaW4gYWRkaXRpb24gdG8gYSBwb2ludCBlc3RpbWF0ZSBvZiB3aGF0IHRoZXkgYXJlLiBJbiB0aGlzIGNhc2UgeW91IGNhbiBzZWUgdGhhdCB0aGUgOTUlIGNyZWRpYmxlIGludGVydmFscyBvdmVybGFwIDAgZm9yIGV2ZXJ5IGNvZWZmaWNpZW50IGluIGFuYWx5c2lzIG9mIHRoZSBmaXJzdCBkYXRhIHNldCwgd2hpbGUgb25seSB0aGUgY3JlZGlibGUgaW50ZXJ2YWxzIGZvciBgeDNgIGRvbid0IG92ZXJsYXAgMCBpbiBhbmFseXNpcyBvZiB0aGUgc2Vjb25kIGRhdGEgc2V0LiBOb3RpY2UsIGhvd2V2ZXIsIHRoYXQgc29tZSBvZiB0aGUgaW50ZXJ2YWxzIGFyZSB2ZXJ5IGNsb3NlIHRvIG5vdCBvdmVybGFwcGluZyAwLCBpLmUuLCB0aGUgaW50ZXJ2YWxzIGZvciBgeDFgLCBgeDNgLCBhbmQgYHg5YCBpbiB0aGUgYW5hbHlzaXMgb2YgZGF0YSBzZXQgMSBhbmQgdGhlIGludGVydmFscyBmb3IgYHgxYCwgYHgyYCwgYW5kIGB4NmAgaW4gdGhlIGFuYWx5c2lzIG9mIGRhdGEgc2V0IDIuCgoyLiBJIGNhbiBhbHNvIHBsb3QgdGhlIGVzdGltYXRlcyBhbmQgdGhlaXIgdW5jZXJ0YWludHkgdmVyeSBlYXNpbHkuIFBsb3R0aW5nIHRoZSBlc3RpbWF0ZXMgYW5kIHRoZWlyIHVuY2VydGFpbnR5IG1ha2VzIGlzIG11Y2ggZWFzaWVyIHRvIHBpY2sgb3V0IHRoZSBjb3ZhcmlhdGVzIHRoYXQgc2VlbSB0byBoYXZlIGFuIGFzc29jaWF0aW9uIHdpdGggdGhlIHJlc3BvbnNlIHZhcmlhYmxlLlteNF0gTm90ZTogVGhlIG91dGVyIGludGVydmFscyBpbiB0aGVzZSBwbG90cyBjb3JyZXNwb25kIHRvIDkwJSBjcmVkaWJsZSBpbnRlcnZhbHMsIG5vdCA5NSUgY3JlZGlibGUgaW50ZXJ2YWxzLiBZb3UgY291bGQgY2hhbmdlIHRoYXQgYnkgc3BlY2lmeWluZyBgcHJvYl9vdXRlciA9IDAuOTVgIGluIHRoZSBjYWxsIHRvIGBwbG90KClgLgoKYGBge3J9CnAgPC0gcGxvdChmaXRfMSkgKyBnZ3RpdGxlKCJFc3RpbWF0ZXMgZnJvbSBkYXRhIHNldCAxIikKcHJpbnQocCkKcCA8LSBwbG90KGZpdF8yKSArIGdndGl0bGUoIkVzdGltYXRlcyBmcm9tIGRhdGEgc2V0IDIiKQpwcmludChwKQpgYGAKCjMuIEkgY2FuIHVzZSB0aGUgcmVndWxhcml6ZWQgaG9yc2Vob2UgcHJpb3IgaW4gYSBnZW5lcmFsaXplZCBtaXhlZCBtb2RlbCB3aXRoIGBzdGFuX2dsbWVyKClgLiBUaGVyZSBpcyBhIHBhY2thZ2UgdG8gZml0IHRoZSBMYXNzbyB0byBhIGdlbmVyYWxpemVkIG1peGVkIG1vZGVsIHdpdGggYGxtZXIoKWAsIGJ1dCBJIGhhdmVuJ3QgdHJpZWQgaXQsIGFuZCBpdCBpc24ndCBidWlsdCBpbnRvIGBsbWVyKClgLgoKIyMjIEhvdyBzdGFibGUgYXJlIHRoZSBwcmVkaWN0aW9ucz8KCklmIHlvdSBsb29rIGJhY2sgYXQgZWFybGllciBpbnN0YWxsbWVudHMgaW4gdGhpcyBzZXJpZXMsIHlvdSdsbCBzZWUgdGhhdCB0aGUgcG9pbnQgZXN0aW1hdGVzIHdlIGdvdCBoZXJlIGFyZW4ndCB0b28gZGlmZmVyZW50IGZyb20gd2hhdCB3ZSd2ZSBzZWVuIGJlZm9yZSwgZS5nLiwgMC4xODUgZm9yIGB4MWAgaW4gZGF0YSBzZXQgMSBoZXJlIF92c18uIDAuMjM5IGluIGRhdGEgc2V0IDEgZnJvbSB0aGUgTGFzc28uIFVzaW5nIHRoaXMgQmF5ZXNpYW4gYXBwcm9hY2gsIGhvd2V2ZXIsIHdlIGNhbiBzZWUgdGhhdCBldmVuIHRob3VnaCBgeDNgIGlzbid0ICJzaWduaWZpY2FudCIgaW4gdGhlIGFuYWx5c2lzIG9mIHRoZSBmaXJzdCBkYXRhIHNldCBhbmQgaXMgaW4gdGhlIHNlY29uZCxbXjVdIHdlIGRvbid0IGhhdmUgZ29vZCBldmlkZW5jZSB0aGF0IHRoZSBlc3RpbWF0ZXMgYXJlIGRpZmZlcmVudCwgYmVjYXVzZSB0aGUgcG9zdGVyaW9yIGRpc3RyaWJ1dGlvbnMgYXJlIGJyb2FkbHkgb3ZlcmxhcHBpbmcgYXMgZXZpZGVuY2VkIGJ5IHRoZSBicm9hZGx5IG92ZXJsYXBwaW5nIGNyZWRpYmxlIGludGVydmFscy4gSW4gb3RoZXIgd29yZHMsIGlmIHdlIHBheSBhcHByb3ByaWF0ZSBhdHRlbnRpb24gdG8gdGhlIHVuY2VydGFpbnR5IG9mIG91ciBlc3RpbWF0ZXMsIHRoZXkncmUgcHJldHR5IHN0YWJsZSAoYXQgbGVhc3QgYWNyb3NzIHRoZSB0d28gc2FtcGxlIGRhdGEgc2V0cyB3ZSd2ZSBiZWVuIGV4cGxvcmluZykuCgpXaGF0IGFib3V0IG91dCBvZiBzYW1wbGUgcHJlZGljdGlvbnM/CgpgYGB7cn0KbmV3X2RhdGEgPC0gZGF0YS5mcmFtZSh4MSA9IDQuMCwgeDIgPSA0LjAsIHgzID0gNC4wLAogICAgICAgICAgICAgICAgICAgICAgIHg0ID0gNC4wLCB4NSA9IDQuMCwgeDYgPSA0LjAsCiAgICAgICAgICAgICAgICAgICAgICAgeDcgPSA0LjAsIHg4ID0gNC4wLCB4OSA9IDQuMCkKCgojIyByZS1zY2FsZSB0aGUgbmV3IGRhdGEgYnkgc3VidHJhY3RpbmcgbWVhbiBhbmQgZGl2aWRpbmcgYnkgc3RhbmRhcmQgZGV2aWF0aW9uCiMjCm5ld19kYXRhXzEgPC0gKG5ld19kYXRhIC0gbGFwcGx5KHNjYWxlXzFbMjoxMF0sIGF0dHIsICJzY2FsZWQ6Y2VudGVyIikpIC8KICAgICAgICAgICAgICAgbGFwcGx5KHNjYWxlXzFbMjoxMF0sIGF0dHIsICJzY2FsZWQ6c2NhbGUiKQpuZXdfZGF0YV8yIDwtIChuZXdfZGF0YSAtIGxhcHBseShzY2FsZV8yWzI6MTBdLCBhdHRyLCAic2NhbGVkOmNlbnRlciIpKSAvCiAgICAgICAgICAgICAgIGxhcHBseShzY2FsZV8yWzI6MTBdLCBhdHRyLCAic2NhbGVkOnNjYWxlIikKICAKcHJlZGljdF8xIDwtIHBvc3Rlcmlvcl9wcmVkaWN0KGZpdF8xLCBuZXdfZGF0YV8xKQpwcmVkaWN0XzIgPC0gcG9zdGVyaW9yX3ByZWRpY3QoZml0XzIsIG5ld19kYXRhXzIpCgpzdW1tYXJpemVfcG9zdGVyaW9yIDwtIGZ1bmN0aW9uKHgsIGNyZWRpYmxlID0gMC45NSwgZGlnaXRzID0gMykgewogIGxvX3AgPC0gKDEuMCAtIGNyZWRpYmxlKS8yLjAKICBoaV9wIDwtIGNyZWRpYmxlICsgbG9fcAogIGNpIDwtIHF1YW50aWxlKHgsIGMobG9fcCwgaGlfcCkpCiAgY2F0KHJvdW5kKG1lYW4oeCksIDMpLCAiICgiLCByb3VuZChjaVsxXSwgMyksICIsIiwgcm91bmQoY2lbMl0sIDMpLCAiKVxuIiwgc2VwID0gIiIpCn0KCmNhdCgiRGF0YSBzZXQgMVxuIikKc3VtbWFyaXplX3Bvc3RlcmlvcihwcmVkaWN0XzEpCmNhdCgiICBUcnVlIGFuc3dlcjogIiwgYmV0YTAgKyBhcy5tYXRyaXgobmV3X2RhdGFfMSkgJSolIGJldGEsICJcbiIsIAogICAgc2VwID0gIiIpCmNhdCgiXG5EYXRhIHNldCAyXG4iKQpzdW1tYXJpemVfcG9zdGVyaW9yKHByZWRpY3RfMikKY2F0KCIgIFRydWUgYW5zd2VyOiAiLCBiZXRhMCArIGFzLm1hdHJpeChuZXdfZGF0YV8yKSAlKiUgYmV0YSwgIlxuIiwgCiAgICBzZXAgPSAiIikKYGBgCgpUaG9zZSBhcmUgcHJldHR5IGNsb3NlIHRvIHRoZSBMYXNzbyBwcmVkaWN0aW9ucywgYnV0IHdlIGhhdmUgdGhlIGFkdmFudGFnZSB0aGF0IHRoZXkgaW5jbHVkZSBhbiBpbmRpY2F0aW9uIG9mIGhvdyByZWxpYWJsZSB0aGUgZXN0aW1hdGVzIGFyZSwgYW5kIHlvdSBjYW4gc2VlIHRoYXQgd2UgZG9uJ3QgaGF2ZSBnb29kIGV2aWRlbmNlIHRoYXQgdGhlIHByZWRpY3Rpb25zIGFyZSBkaWZmZXJlbnQgZnJvbSBvbmUgYW5vdGhlciBldmVuIHRob3VnaCB0aGUgcG9pbnQgZXN0aW1hdGVzIGxvb2sgZmFpcmx5IGRpZmZlcmVudC4gVGhhdCdzIHRoZSBnb29kIG5ld3MuIFRoZSBiYWQgbmV3cyBpcyB0aGF0IG5laXRoZXIgb2YgdGhlIHByZWRpY3Rpb24gaW50ZXJ2YWxzIGluY2x1ZGUgdGhlIHRydWUgdmFsdWUuKEFnYWluLCB0aGUgdHJ1ZSB2YWx1ZXMgZGlmZmVyIGJlY2F1c2UgdGhlIHNjYWxpbmcgZGlmZmVycyBiZXR3ZWVuIHRoZSBkYXRhIHNldHMuKSBJZiB5b3UncmUgZ2V0dGluZyB0aGUgbWVzc2FnZSB0aGF0IG91dCBvZiBzYW1wbGUgZXh0cmFwb2xhdGlvbiBpcyB0cmlja3ksIHlvdSdyZSBnZXR0aW5nIHRoZSByaWdodCBtZXNzYWdlLiBKdXN0IGltYWdpbmUgaG93IG11Y2ggdHJpY2tpZXIgaXQgd291bGQgYmUgaWYgdGhlIHRydWUgcmVsYXRpb25zaGlwIHdlcmUgbm9uLWxpbmVhciByYXRoZXIgdGhhbiBsaW5lYXIuCgojIyBIb3cgZG8geW91IHBpY2sgdGhlIGdsb2JhbCBzY2FsZQoKSWYgeW91J3JlIHN0aWxsIHBheWluZyBhdHRlbnRpb24gKGEpIEkgY29uZ3JhdHVsYXRlIHlvdSBhbmQgKGIpIEkgb3dlIHlvdSBhbiBleHBsYW5hdGlvbiBvZiBob3cgSSBzZXQgYHByaW9yX2NvZWZmYCBpbiB0aGUgY2FsbCB0byBgc3Rhbl9nbG0oKWAuIFRoZSBleHBsYW5hdGlvbiBpcyBzaW1wbGUuIEkgZm9sbG93ZWQgdGhlIGFkdmljZSBpbiB0aGUgaGVscCBwYWdlIGZvciAiUHJpb3IgZGlzdHJpYnV0aW9ucyBhbmQgb3B0aW9ucyIgaW4gdGhlIGByc3RhbmFybWAgcGFja2FnZSwgaS5lLiwgYGhlbHAoInByaW9ycyIpYC4gSSBzZXQgaXQgdG8gInRoZSByYXRpbyBvZiB0aGUgZXhwZWN0ZWQgbnVtYmVyIG9mIG5vbi16ZXJvIGNvZWZmaWNpZW50cyBkaXZpZGVkIGJ5IHRoZSBzcXVhcmUgcm9vdCBvZiB0aGUgbnVtYmVyIG9mIG9ic2VydmF0aW9ucy4iIEluIGZhY3QsIEkgY2hlYXRlZCBldmVuIGEgbGl0dGxlIG1vcmUgdGhhbiB0aGF0LiBJIHNpbXBseSBtb2RpZmllZCB0aGUgY29kZSBhdCBbaHR0cHM6Ly9tYy1zdGFuLm9yZy9wcm9qcHJlZC9hcnRpY2xlcy9xdWlja3N0YXJ0Lmh0bWxdKGh0dHBzOi8vbWMtc3Rhbi5vcmcvcHJvanByZWQvYXJ0aWNsZXMvcXVpY2tzdGFydC5odG1sKS5bXjZdIEkgaGF2ZW4ndCBwbGF5ZWQgYXJvdW5kIHdpdGggb3RoZXIgdmFsdWVzLCBidXQgSSBlbmNvdXJhZ2UgeW91IHRvIHRyeSBzb21lLiBGb3IgZXhhbXBsZSwgaW5zdGVhZCBvZiAzIGluIHRoZSBjb2RlIGFib3ZlIHRyeSAxIGFuZCA4IHRvIHNlZSBob3cgZGlmZmVyZW50IHlvdXIgcmVzdWx0cyBhcmUuIAoKW14xXTogUGlyb25lbiwgSi4sIGFuZCBBLiBWZWh0YXJpLiAyMDE3LiBTcGFyc2l0eSBpbmZvcm1hdGlvbiBhbmQgcmVndWxhcml6YXRpb24gaW4gdGhlIGhvcnNlc2hvZSBhbmQgb3RoZXIgc2hyaW5rYWdlIHByaW9ycy4gX0VsZWN0cm9uaWMgSm91cm5hbCBvZiBTdGF0aXN0aWNzXyAxMSgyKTo1MDE4LTUwNTEuIGRvaTogWzEwLjEyMTQvMTctRUpTMTMzN1NJXShodHRwczovL2RvaS5vcmcvMTAuMTIxNC8xNy1FSlMxMzM3U0kpCgpbXjJdOiBOb3RlOiBZb3UgY2FuIGNoZWNrIHRoaXMgZWFzaWx5IHVzaW5nIGBwcmlvcl9zdW1tYXJ5KClgLiBTZWUgYWJvdmUgKGluIEhUTUwpIG9yIGhlcmUgKGluIGFuIFIgbm90ZWJvb2spIHRvIHNlZSBob3csIHVzaW5nIHRoZSBidWlsdGluIGBtdGNhcnNgIGRhdGEgc2V0IGFzIGFuIGV4YW1wbGUuCgpgYGB7cn0KbXRjYXJzJG1wZzEwIDwtIG10Y2FycyRtcGcvMTAKZml0IDwtIHN0YW5fZ2xtKG1wZzEwIH4gd3QgKyBjeWwgKyBhbSwgZGF0YSA9IG10Y2FycywgUVIgPSBGQUxTRSwgcmVmcmVzaCA9IDApCnByaW9yX3N1bW1hcnkoZml0KQpgYGAKClteM106IElmIHlvdSd2ZSBiZWVuIGZvbGxvd2luZyBhbG9uZyB0aGlzIGZhciwgeW91IHByb2JhYmx5IGRvbid0IHJlZ2FyZCB0aGlzIGxhc3Qgc3RlcCBhcyBxdWFsaWZ5aW5nIGZvciB0aGUgd29yZCAianVzdCIsIGJ1dCBpdCBpcyB0aGUgbGFzdCBzdGVwIC0gcmVhbGx5LgoKW140XTogSWYgeW91J3JlIHVuY29tZm9ydGFibGUgd2l0aCBCYXllc2lhbiBpbmZlcmVuY2UsIGl0J3Mgd29ydGggbm90aW5nIHRoYXQgdGhlIGNvdmFyaWF0ZXMgaWRlbnRpZmllZCBpbiB0aGUgdHdvIGFuYWx5c2VzIGFyZSBwcmV0dHkgc2ltaWxhci4gSW4gZmFjdCwgSSBzaW5nbGVkIG91dCB0aGUgc2FtZSBjb3ZhcmlhdGVzIGFzIGltcG9ydGFudCBpbiB0aGUgYW5hbHlzaXMgb2YgZGF0YSBzZXQgMiBoZXJlIGFzIHRoZSBMYXNzbyBpZGVudGlmaWVkLCBhbmQgYWxsIG9mIHRoZSBjb3ZhcmlhdGVzIEkgaWRlbnRpZmllZCBoZXJlIGluIHRoZSBhbmFseXNpcyBvZiBkYXRhIHNldCAxIHdlcmUgYWxzbyBpZGVudGlmaWVkIGluIGFuYWx5c2lzIHVzaW5nIHRoZSBMYXNzbyAodGhlIExhc3NvIGlkZW50aWZpZWQgdGhyZWUgbW9yZSkuCgpbXjVdOiBJbiB0aGUgc2Vuc2UgdGhhdCB0aGUgOTUlIGNyZWRpYmxlIGludGVydmFsIGZvciBgeDNgIG92ZXJsYXBzIDAgaW4gYW5hbHlzaXMgb2YgdGhlIGZpcnN0IGRhdGEgc2V0IGFuZCBkb2Vzbid0IGluIHRoZSBzZWNvbmQuCgpbXjZdOiBJZiB5b3UgdmlzaXQgW2h0dHBzOi8vbWMtc3Rhbi5vcmcvcHJvanByZWQvYXJ0aWNsZXMvcXVpY2tzdGFydC5odG1sXShodHRwczovL21jLXN0YW4ub3JnL3Byb2pwcmVkL2FydGljbGVzL3F1aWNrc3RhcnQuaHRtbCksIHlvdSdsbCBzZWUgdGhhdCBpdCBkZXNjcmliZXMgYSBwYWNrYWdlIGNhbGxlZCBgcHJvZHByZWpgLiBXZSdsbCBleHBsb3JlIHRoYXQgaW4gdGhlIG5leHQgaW5zdGFsbG1lbnQu