Models for regression coefficients. See Chapter 12 in Regression and Other Stories.


Load packages

library("rprojroot")
root<-has_file(".ROS-Examples-root")$make_fix_file()
library("rstanarm")
library("rstantools")
library("loo")
library("ggplot2")
library("bayesplot")
theme_set(bayesplot::theme_default(base_family = "sans"))

Set random seed for reproducibility

SEED <- 2132

Here we consider regression with more than handful of predictors. We demonstrate the usefulness of standardization of predictors and models for regression coefficients.

We demonstrate with data from Portuguese students and their final period math grade. We predict the third period math grade given student's school, student's sex, student's age, student's home address type, family size, parent's cohabitation status, mother's education, father's education, home to school travel time, weekly study tie, number of past class failures, extra educational support, extra paid classes within the course subject, extra-curricular activities, attended nursery school, wants to take higher education, Internet access at home, with a romantic relationship, quality of family relationships, free time after school, going out with friends, workday alcohol consumption, weekend alcohol consumption, current health status, and number of school absences.

Load data

# Use the merged data with students having both math and Portuguese language grades
data <- read.csv(root("Student/data","student-merged.csv"))
head(data)
  G1mat G2mat G3mat G1por G2por G3por school sex age address famsize Pstatus
1     7    10    10    13    13    13      0   0  15       0       0       1
2     8     6     5    13    11    11      0   0  15       0       0       1
3    14    13    13    14    13    12      0   0  15       0       0       1
4    10     9     8    10    11    10      0   0  15       0       0       1
5    10    10    10    13    13    13      0   0  15       0       0       1
6    12    12    11    11    12    12      0   0  15       0       0       1
  Medu Fedu traveltime studytime failures schoolsup famsup paid activities
1    1    1          2         4        1         1      1    1          1
2    1    1          1         2        2         1      1    0          0
3    2    2          1         1        0         1      1    1          1
4    2    4          1         3        0         1      1    1          1
5    3    3          2         3        2         0      1    1          1
6    3    4          1         3        0         1      1    1          1
  nursery higher internet romantic famrel freetime goout Dalc Walc health
1       1      1        1        0      3        1     2    1    1      1
2       0      1        1        1      3        3     4    2    4      5
3       1      1        0        0      4        3     1    1    1      2
4       1      1        1        0      4        3     2    1    1      5
5       1      1        1        1      4        2     1    2    3      3
6       1      1        1        0      4        3     2    1    1      5
  absences
1        2
2        2
3        8
4        2
5        8
6        2
grades <- c("G1mat","G2mat","G3mat","G1por","G2por","G3por")
predictors <- c("school","sex","age","address","famsize","Pstatus","Medu","Fedu","traveltime","studytime","failures","schoolsup","famsup","paid","activities", "nursery", "higher", "internet", "romantic","famrel","freetime","goout","Dalc","Walc","health","absences")
p <- length(predictors)

Data for predicting the final math grade

Pick columns to make models for third period mathematics grade G3mat and select only students with non-zero math grade (use G3por to make a model for third grade Portuguese language grade).

data_G3mat <- subset(data, subset=G3mat>0, select=c("G3mat",predictors))
n <- nrow(data_G3mat)

Default weak prior on original coefficients

Fit a regression model with default weak priors

Dot (.) in the formula means all other columns execpt what is already on the left of ~.

fit0 <- stan_glm(G3mat ~ ., data = data_G3mat, seed = SEED, refresh=0)

Plot posterior marginals of coefficients

p0 <- mcmc_areas(as.matrix(fit0), pars=vars(-'(Intercept)',-sigma),
                 prob_outer=0.95, area_method = "scaled height") +
  xlim(c(-3.2,2.4))
p0 <- p0 + scale_y_discrete(limits = rev(levels(p0$data$parameter)))
p0

The above figure shows that without standardization of predictors, it looks like there is a different amount of uncertainty on the relevance of the predictors. For example, it looks like absences has really small relevance and high certainty.

Standardize all predictors for easier comparison of relevances as discussed in Section 12.1.

datastd_G3mat <- data_G3mat
datastd_G3mat[,predictors] <-scale(data_G3mat[,predictors])

Default weak prior on coefficients

Fit a regression model with default weak priors

fit1 <- stan_glm(G3mat ~ ., data = datastd_G3mat, seed = SEED, refresh=0)

Plot posterior marginals of coefficients

p1 <- mcmc_areas(as.matrix(fit1), pars=vars(-'(Intercept)',-sigma),
                 prob_outer=0.95, area_method = "scaled height") +
  xlim(c(-1.2,0.8))
p1 <- p1 + scale_y_discrete(limits = rev(levels(p1$data$parameter)))
p1

The above figure shows that after all predictors have been standardized to have equal standard deviation, the uncertainties on the relevances are similar. For example, it is now easier to see that absences has relatively high relevance compared to other predictors in the model.

Compare Bayesian \(R^2\) and LOO \(R^2\)

round(median(bayes_R2(fit1)), 2)
[1] 0.31
round(median(loo_R2(fit1)), 2)
[1] 0.16

Compute LOO log score

(loo1 <- loo(fit1))

Computed from 4000 by 343 log-likelihood matrix

         Estimate   SE
elpd_loo   -864.7 12.6
p_loo        26.5  2.0
looic      1729.4 25.1
------
Monte Carlo SE of elpd_loo is 0.1.

All Pareto k estimates are good (k < 0.5).
See help('pareto-k-diagnostic') for details.

Medians of Bayesian \(R^2\) and LOO \(R^2\) are quite different, and p_loo is approximately 26, which indicates that the model is fitting to all predictors.

If the predictors have been standardized to have standard deviation 1 and we give the regression coefficients independent normal priors with mean 0 and standard deviation 2.5, this implies that the prior standard deviation of the modeled predictive means is \(2.5 \sqrt{26} = 12.7\). The default prior for \(\sigma\) is an exponential distribution, scaled to have mean equal to data standard deviation which in this case is approximately 3.3 which is much less than 12.7. We can simulate from these prior distributions and examine what is the corresponding prior distribution for explained variance \(R^2\).

Bayesian \(R^2\) distribution

ggplot() + geom_histogram(aes(x=bayes_R2(fit1)), breaks=seq(0,1,length.out=100)) +
  xlim(c(0,1)) +
  scale_y_continuous(breaks=NULL) +
  labs(x="Bayesian R^2", y="")

Prior predictive checking by looking at the prior on Bayesian \(R^2\)

ppR2<-numeric()
for (i in 1:4000) {
  sigma2 <- rexp(1,rate=0.3)^2;
  muvar <- var(as.matrix(datastd_G3mat[,2:27]) %*% rnorm(26)*2.5)
  ppR2[i] <- muvar/(muvar+sigma2)
}
ggplot()+geom_histogram(aes(x=ppR2), breaks=seq(0,1,length.out=50)) +
  xlim(c(0,1)) +
  scale_y_continuous(breaks=NULL) +
  labs(x="Prior predictive Bayesian R^2",y="")

pp1 <- mcmc_hist(data.frame(Prior=ppR2,Posterior=bayes_R2(fit1)),
                 breaks=seq(0,1,length.out=100),
                 facet_args = list(nrow = 2)) +
  facet_text(size = 13) +
  scale_x_continuous(limits = c(0,1), expand = c(0, 0),
                     labels = c("0","0.25","0.5","0.75","1")) +
  theme(axis.line.y = element_blank()) +
  xlab("Bayesian R^2")
pp1

The above figure shows that with the default prior on regression coefficients and \(\sigma\), the implied prior distribution for \(R^2\) is strongly favoring larger values and thus is favoring overfitted models. The priors often considered as weakly informative for regression coefficients turn out to be in multiple predictor case highly informative for the explained variance.

Weakly informative prior scaled with the number of covariates

Prior predictive checking by looking at the prior on Bayesian \(R^2\)

ppR2<-numeric()
for (i in 1:4000) {
  sigma2 <- 0.7*rexp(1, rate=1/sd(datastd_G3mat$G3mat))^2
  muvar <- var(as.matrix(datastd_G3mat[,2:27]) %*% rnorm(26, sd=sd(datastd_G3mat$G3mat)/sqrt(26)*sqrt(0.3)))
  ppR2[i] <- muvar/(muvar+sigma2)
}
ggplot()+geom_histogram(aes(x=ppR2), breaks=seq(0,1,length.out=50)) +
  xlim(c(0,1)) +
  scale_y_continuous(breaks=NULL) +
  labs(x="Prior predictive Bayesian R^2",y="")

Fit a regression model with a weakly informative prior scaled with the number of covariates

fit2 <- stan_glm(G3mat ~ ., data = datastd_G3mat, seed = SEED,
                 prior=normal(scale=sd(datastd_G3mat$G3mat)/sqrt(26)*sqrt(0.3),
                              autoscale=FALSE),
                 refresh=0)

When we compare Bayesian \(R^2\) and LOO \(R^2\), we see the difference is much smaller and LOO \(R^2\) has improved slightly.

Compare Bayesian \(R^2\) and LOO \(R^2\)

round(median(loo_R2(fit2)), 2)
[1] 0.19
round(median(bayes_R2(fit2)), 2)
[1] 0.26

Bayesian \(R^2\) distribution

ggplot()+geom_histogram(aes(x=bayes_R2(fit2)), breaks=seq(0,1,length.out=100)) +
  xlim(c(0,1)) +
  scale_y_continuous(breaks=NULL) +
  labs(x="Bayesian R^2",y="")

pp2 <- mcmc_hist(data.frame(Prior=ppR2,Posterior=bayes_R2(fit2)),
                 breaks=seq(0,1,length.out=100),
                 facet_args = list(nrow = 2)) +
  facet_text(size = 13) +
  scale_x_continuous(limits = c(0,1), expand = c(0, 0),
                     labels = c("0","0.25","0.5","0.75","1")) +
  theme(axis.line.y = element_blank()) +
  xlab("Bayesian R^2")
pp2

Comparison of the LOO log score reveals that the new model has better leave-one-out prediction.

Compute LOO log score

(loo2 <- loo(fit2))

Computed from 4000 by 343 log-likelihood matrix

         Estimate   SE
elpd_loo   -859.9 12.3
p_loo        21.1  1.6
looic      1719.9 24.7
------
Monte Carlo SE of elpd_loo is 0.1.

Pareto k diagnostic values:
                         Count Pct.    Min. n_eff
(-Inf, 0.5]   (good)     342   99.7%   2071      
 (0.5, 0.7]   (ok)         1    0.3%   2304      
   (0.7, 1]   (bad)        0    0.0%   <NA>      
   (1, Inf)   (very bad)   0    0.0%   <NA>      

All Pareto k estimates are ok (k < 0.7).
See help('pareto-k-diagnostic') for details.

Compare models

loo_compare(loo1,loo2)
     elpd_diff se_diff
fit2  0.0       0.0   
fit1 -4.8       1.8   

Plot posterior marginals of coefficients

p2 <- mcmc_areas(as.matrix(fit2), pars=vars(-'(Intercept)',-sigma),
                 prob_outer=0.95, area_method = "scaled height") +
  xlim(c(-1.2,0.8))
p2 <- p2 + scale_y_discrete(limits = rev(levels(p2$data$parameter)))
p2

The above figure shows the posterior distributions of coefficients, which are slightly more concentrated than for the previous model.

Weakly informative prior assuming only some covariates are relevant

We next use regularized horseshoe pruior, assuming that the expected number of relevant predictors is near \(p_0=6\) and the prior scale for the relevant predictors is chosen as in the previous model but using \(p_0\) for scaling. We can then simulate from this prior and examine the corresponding prior for \(R^2\)

p0 <- 6
slab_scale <- sd(datastd_G3mat$G3mat)/sqrt(p0)*sqrt(0.3)
#
ppR2<-numeric()
for (i in 1:4000) {
  sigma2 <- 0.7*rexp(1,rate=1/sd(datastd_G3mat$G3mat))^2;
  global_scale <- p0 / (p - p0) * sqrt(sigma2) / sqrt(n)
  z <- rnorm(p)
  lambda <- rcauchy(p)
  tau <- rcauchy(1, scale = global_scale)
  caux <- 1/rgamma(1, shape=0.5, rate=0.5)
  c <-  slab_scale * sqrt(caux)
  lambda_tilde <- sqrt(c^2 * lambda^2 / (c^2 + tau^2*lambda^2))
  beta <- rnorm(p) * lambda_tilde * tau
  muvar <- var(as.matrix(datastd_G3mat[,2:27]) %*% beta)
  ppR2[i] <- muvar/(muvar+sigma2)
}
ggplot()+geom_histogram(aes(x=ppR2), breaks=seq(0,1,length.out=50)) +
  xlim(c(0,1)) +
  scale_y_continuous(breaks=NULL) +
  labs(x="Prior predictive Bayesian R^2",y="")

The above figure shows that the regularized horseshoe prior with sensible parameters implies a more cautious prior on explained variance \(R^2\) than is implicitly assumed by the default wide prior. The horseshoe prior favors simpler models, but is quite flat around most \(R^2\) values.

Fit a regression model with regularized horseshoe prior

p0 <- 6
slab_scale <- sd(datastd_G3mat$G3mat)/sqrt(p0)*sqrt(0.3)
# global scale without sigma, as the scaling by sigma happens in stan_glm
global_scale <- p0 / (p - p0) / sqrt(n)
fit3 <- stan_glm(G3mat ~ ., data = datastd_G3mat, seed = SEED,
                 prior=hs(global_scale=global_scale, slab_scale=slab_scale),
                 refresh=0)

Compare Bayesian \(R^2\) and LOO \(R^2\)

round(median(loo_R2(fit3)), 2)
[1] 0.19
round(median(bayes_R2(fit3)), 2)
[1] 0.23

When we compare models using LOO log score, the new model is better than the default prior model, but there is no difference compared the model with normal prior scaled with the predictors. It is common that the data do not have strong information about how many predictors are relevant and then different types of priors can produce similar predictive accuracies.

Compute LOO log score

(loo3 <- loo(fit3))

Computed from 4000 by 343 log-likelihood matrix

         Estimate   SE
elpd_loo   -860.2 12.3
p_loo        19.0  1.4
looic      1720.3 24.6
------
Monte Carlo SE of elpd_loo is 0.1.

Pareto k diagnostic values:
                         Count Pct.    Min. n_eff
(-Inf, 0.5]   (good)     342   99.7%   1554      
 (0.5, 0.7]   (ok)         1    0.3%   2212      
   (0.7, 1]   (bad)        0    0.0%   <NA>      
   (1, Inf)   (very bad)   0    0.0%   <NA>      

All Pareto k estimates are ok (k < 0.7).
See help('pareto-k-diagnostic') for details.

Compare models

loo_compare(loo1,loo3)
     elpd_diff se_diff
fit3  0.0       0.0   
fit1 -4.5       3.0   
loo_compare(loo2,loo3)
     elpd_diff se_diff
fit2  0.0       0.0   
fit3 -0.2       1.6   

Bayesian \(R^2\) distribution

ggplot()+geom_histogram(aes(x=bayes_R2(fit3)), breaks=seq(0,1,length.out=100)) +
  xlim(c(0,1)) +
  scale_y_continuous(breaks=NULL) +
  labs(x="Bayesian R^2",y="")

pp3 <- mcmc_hist(data.frame(Prior=ppR2,Posterior=bayes_R2(fit3)),
                 breaks=seq(0,1,length.out=100),
                 facet_args = list(nrow = 2)) +
  facet_text(size = 13) +
  scale_x_continuous(limits = c(0,1), expand = c(0, 0),
                     labels = c("0","0.25","0.5","0.75","1")) +
  theme(axis.line.y = element_blank()) +
  xlab("Bayesian R^2")
pp3

Plot posterior marginals of coefficients

p3 <- mcmc_areas(as.matrix(fit3), pars=vars(-'(Intercept)',-sigma),
                 prob_outer=0.95, area_method = "scaled height") +
  xlim(c(-1.2,0.8))
p3 <- p3 + scale_y_discrete(limits = rev(levels(p3$data$parameter)))
p3

The above figure shows that the regularized horseshoe prior has the benefit of shrinking the posterior for many regression coefficients more tightly towards 0, making it easier to see the most relevant predictors. Failures, school support, going out, and the number of absences appear to be the most relevant predictors.

Subset of covariates

Fit a regression model with subset of covariates and default weak prior on coefficients

fit4 <- stan_glm(G3mat ~ failures + schoolsup + goout + absences,
                 data = datastd_G3mat, seed = SEED, refresh=0)

When we compare Bayesian \(R^2\) and LOO \(R^2\), we see the difference is small and there is less overfit than when using all predictors with wide prior. LOO \(R^2\) is just slightly smaller than for models with all predictors and better priors. The prediction performance can not improved much by adding more predictors. Note that by observing more students it might be possible to learn regression coefficients for other predictors with sufficient small uncertainty so that predictions for new students could be improved.

Compare Bayesian \(R^2\) and LOO \(R^2\)

round(median(loo_R2(fit4)), 2)
[1] 0.17
round(median(bayes_R2(fit4)), 2)
[1] 0.2

Bayesian \(R^2\) distribution

ggplot()+geom_histogram(aes(x=bayes_R2(fit4)), breaks=seq(0,1,length.out=100)) +
  xlim(c(0,1)) +
  scale_y_continuous(breaks=NULL) +
  labs(x="Bayesian R^2",y="")

Compute LOO log score

(loo4 <- loo(fit4))

Computed from 4000 by 343 log-likelihood matrix

         Estimate   SE
elpd_loo   -863.1 12.4
p_loo         5.2  0.6
looic      1726.2 24.9
------
Monte Carlo SE of elpd_loo is 0.0.

All Pareto k estimates are good (k < 0.5).
See help('pareto-k-diagnostic') for details.

Compare models

loo_compare(loo3,loo4)
     elpd_diff se_diff
fit3  0.0       0.0   
fit4 -2.9       4.2   
loo_compare(loo2,loo4)
     elpd_diff se_diff
fit2  0.0       0.0   
fit4 -3.2       5.5   
loo_compare(loo1,loo4)
     elpd_diff se_diff
fit4  0.0       0.0   
fit1 -1.6       6.3   

Plot posterior marginals of coefficients

p4 <- mcmc_areas(as.matrix(fit4), pars=vars(-'(Intercept)',-sigma),
                 prob_outer=0.99, area_method = "scaled height") +
  xlim(c(-1.3,0.1))
p4 <- p4 + scale_y_discrete(limits = rev(levels(p4$data$parameter)))
p4

LS0tCnRpdGxlOiAiUmVncmVzc2lvbiBhbmQgT3RoZXIgU3RvcmllczogU3R1ZGVudCIKYXV0aG9yOiAiQW5kcmV3IEdlbG1hbiwgSmVubmlmZXIgSGlsbCwgQWtpIFZlaHRhcmkiCmRhdGU6ICJgciBmb3JtYXQoU3lzLkRhdGUoKSlgIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIHRoZW1lOiByZWFkYWJsZQogICAgdG9jOiB0cnVlCiAgICB0b2NfZGVwdGg6IDIKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQotLS0KTW9kZWxzIGZvciByZWdyZXNzaW9uIGNvZWZmaWNpZW50cy4gU2VlIENoYXB0ZXIgMTIgaW4KUmVncmVzc2lvbiBhbmQgT3RoZXIgU3Rvcmllcy4KCi0tLS0tLS0tLS0tLS0KCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KG1lc3NhZ2U9RkFMU0UsIGVycm9yPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBjb21tZW50PU5BKQojIHN3aXRjaCB0aGlzIHRvIFRSVUUgdG8gc2F2ZSBmaWd1cmVzIGluIHNlcGFyYXRlIGZpbGVzCnNhdmVmaWdzIDwtIEZBTFNFCmBgYAoKIyMjIyBMb2FkIHBhY2thZ2VzCgpgYGB7ciB9CmxpYnJhcnkoInJwcm9qcm9vdCIpCnJvb3Q8LWhhc19maWxlKCIuUk9TLUV4YW1wbGVzLXJvb3QiKSRtYWtlX2ZpeF9maWxlKCkKbGlicmFyeSgicnN0YW5hcm0iKQpsaWJyYXJ5KCJyc3RhbnRvb2xzIikKbGlicmFyeSgibG9vIikKbGlicmFyeSgiZ2dwbG90MiIpCmxpYnJhcnkoImJheWVzcGxvdCIpCnRoZW1lX3NldChiYXllc3Bsb3Q6OnRoZW1lX2RlZmF1bHQoYmFzZV9mYW1pbHkgPSAic2FucyIpKQpgYGAKClNldCByYW5kb20gc2VlZCBmb3IgcmVwcm9kdWNpYmlsaXR5CgpgYGB7ciB9ClNFRUQgPC0gMjEzMgoKYGBgCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CiMgZ3JheXNjYWxlIGZpZ3VyZXMgZm9yIHRoZSBib29rCmlmIChzYXZlZmlncykgY29sb3Jfc2NoZW1lX3NldChzY2hlbWUgPSAiZ3JheSIpCmBgYAoKSGVyZSB3ZSBjb25zaWRlciByZWdyZXNzaW9uIHdpdGggbW9yZSB0aGFuIGhhbmRmdWwgb2YKcHJlZGljdG9ycy4gV2UgZGVtb25zdHJhdGUgdGhlIHVzZWZ1bG5lc3Mgb2Ygc3RhbmRhcmRpemF0aW9uIG9mCnByZWRpY3RvcnMgYW5kIG1vZGVscyBmb3IgcmVncmVzc2lvbiBjb2VmZmljaWVudHMuCgpXZSBkZW1vbnN0cmF0ZSB3aXRoIGRhdGEgZnJvbSBQb3J0dWd1ZXNlIHN0dWRlbnRzIGFuZCB0aGVpciBmaW5hbApwZXJpb2QgbWF0aCBncmFkZS4gIFdlIHByZWRpY3QgdGhlIHRoaXJkIHBlcmlvZCBtYXRoIGdyYWRlIGdpdmVuCnN0dWRlbnQncyBzY2hvb2wsIHN0dWRlbnQncyBzZXgsIHN0dWRlbnQncyBhZ2UsIHN0dWRlbnQncyBob21lCmFkZHJlc3MgdHlwZSwgZmFtaWx5IHNpemUsIHBhcmVudCdzIGNvaGFiaXRhdGlvbiBzdGF0dXMsIG1vdGhlcidzCmVkdWNhdGlvbiwgZmF0aGVyJ3MgZWR1Y2F0aW9uLCBob21lIHRvIHNjaG9vbCB0cmF2ZWwgdGltZSwgd2Vla2x5CnN0dWR5IHRpZSwgbnVtYmVyIG9mIHBhc3QgY2xhc3MgZmFpbHVyZXMsIGV4dHJhIGVkdWNhdGlvbmFsCnN1cHBvcnQsIGV4dHJhIHBhaWQgY2xhc3NlcyB3aXRoaW4gdGhlIGNvdXJzZSBzdWJqZWN0LApleHRyYS1jdXJyaWN1bGFyIGFjdGl2aXRpZXMsIGF0dGVuZGVkIG51cnNlcnkgc2Nob29sLCB3YW50cyB0byB0YWtlCmhpZ2hlciBlZHVjYXRpb24sIEludGVybmV0IGFjY2VzcyBhdCBob21lLCB3aXRoIGEgcm9tYW50aWMKcmVsYXRpb25zaGlwLCBxdWFsaXR5IG9mIGZhbWlseSByZWxhdGlvbnNoaXBzLCBmcmVlIHRpbWUgYWZ0ZXIKc2Nob29sLCBnb2luZyBvdXQgd2l0aCBmcmllbmRzLCB3b3JrZGF5IGFsY29ob2wgY29uc3VtcHRpb24sCndlZWtlbmQgYWxjb2hvbCBjb25zdW1wdGlvbiwgY3VycmVudCBoZWFsdGggc3RhdHVzLCBhbmQgbnVtYmVyIG9mCnNjaG9vbCBhYnNlbmNlcy4KCiMjIyMgTG9hZCBkYXRhCgpgYGB7ciB9CiMgVXNlIHRoZSBtZXJnZWQgZGF0YSB3aXRoIHN0dWRlbnRzIGhhdmluZyBib3RoIG1hdGggYW5kIFBvcnR1Z3Vlc2UgbGFuZ3VhZ2UgZ3JhZGVzCmRhdGEgPC0gcmVhZC5jc3Yocm9vdCgiU3R1ZGVudC9kYXRhIiwic3R1ZGVudC1tZXJnZWQuY3N2IikpCmhlYWQoZGF0YSkKZ3JhZGVzIDwtIGMoIkcxbWF0IiwiRzJtYXQiLCJHM21hdCIsIkcxcG9yIiwiRzJwb3IiLCJHM3BvciIpCnByZWRpY3RvcnMgPC0gYygic2Nob29sIiwic2V4IiwiYWdlIiwiYWRkcmVzcyIsImZhbXNpemUiLCJQc3RhdHVzIiwiTWVkdSIsIkZlZHUiLCJ0cmF2ZWx0aW1lIiwic3R1ZHl0aW1lIiwiZmFpbHVyZXMiLCJzY2hvb2xzdXAiLCJmYW1zdXAiLCJwYWlkIiwiYWN0aXZpdGllcyIsICJudXJzZXJ5IiwgImhpZ2hlciIsICJpbnRlcm5ldCIsICJyb21hbnRpYyIsImZhbXJlbCIsImZyZWV0aW1lIiwiZ29vdXQiLCJEYWxjIiwiV2FsYyIsImhlYWx0aCIsImFic2VuY2VzIikKcCA8LSBsZW5ndGgocHJlZGljdG9ycykKYGBgCgojIyMjIERhdGEgZm9yIHByZWRpY3RpbmcgdGhlIGZpbmFsIG1hdGggZ3JhZGUKClBpY2sgY29sdW1ucyB0byBtYWtlIG1vZGVscyBmb3IgdGhpcmQgcGVyaW9kIG1hdGhlbWF0aWNzIGdyYWRlIEczbWF0IGFuZApzZWxlY3Qgb25seSBzdHVkZW50cyB3aXRoIG5vbi16ZXJvIG1hdGggZ3JhZGUKKHVzZSBHM3BvciB0byBtYWtlIGEgbW9kZWwgZm9yIHRoaXJkIGdyYWRlIFBvcnR1Z3Vlc2UgbGFuZ3VhZ2UgZ3JhZGUpLgoKYGBge3IgfQpkYXRhX0czbWF0IDwtIHN1YnNldChkYXRhLCBzdWJzZXQ9RzNtYXQ+MCwgc2VsZWN0PWMoIkczbWF0IixwcmVkaWN0b3JzKSkKbiA8LSBucm93KGRhdGFfRzNtYXQpCmBgYAoKIyMgRGVmYXVsdCB3ZWFrIHByaW9yIG9uIG9yaWdpbmFsIGNvZWZmaWNpZW50cwoKIyMjIyBGaXQgYSByZWdyZXNzaW9uIG1vZGVsIHdpdGggZGVmYXVsdCB3ZWFrIHByaW9ycwoKRG90ICguKSBpbiB0aGUgZm9ybXVsYSBtZWFucyBhbGwgb3RoZXIgY29sdW1ucyBleGVjcHQgd2hhdCBpcwphbHJlYWR5IG9uIHRoZSBsZWZ0IG9mIH4uCgpgYGB7ciB9CmZpdDAgPC0gc3Rhbl9nbG0oRzNtYXQgfiAuLCBkYXRhID0gZGF0YV9HM21hdCwgc2VlZCA9IFNFRUQsIHJlZnJlc2g9MCkKYGBgCgojIyMjIFBsb3QgcG9zdGVyaW9yIG1hcmdpbmFscyBvZiBjb2VmZmljaWVudHMKCmBgYHtyIH0KcDAgPC0gbWNtY19hcmVhcyhhcy5tYXRyaXgoZml0MCksIHBhcnM9dmFycygtJyhJbnRlcmNlcHQpJywtc2lnbWEpLAogICAgICAgICAgICAgICAgIHByb2Jfb3V0ZXI9MC45NSwgYXJlYV9tZXRob2QgPSAic2NhbGVkIGhlaWdodCIpICsKICB4bGltKGMoLTMuMiwyLjQpKQpwMCA8LSBwMCArIHNjYWxlX3lfZGlzY3JldGUobGltaXRzID0gcmV2KGxldmVscyhwMCRkYXRhJHBhcmFtZXRlcikpKQpwMApgYGAKYGBge3IgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KaWYgKHNhdmVmaWdzKQogIGdnc2F2ZShyb290KCJTdHVkZW50L2ZpZ3MiLCJzdHVkZW50X2ZpdDBfbWNtY19hcmVhcy5wZGYiKSwgcDAsIGhlaWdodD01LCB3aWR0aD01LCBjb2xvcm1vZGVsPSJncmF5IikKYGBgCgpUaGUgYWJvdmUgZmlndXJlIHNob3dzIHRoYXQgd2l0aG91dCBzdGFuZGFyZGl6YXRpb24gb2YgcHJlZGljdG9ycywKaXQgbG9va3MgbGlrZSB0aGVyZSBpcyBhIGRpZmZlcmVudCBhbW91bnQgb2YgdW5jZXJ0YWludHkgb24gdGhlCnJlbGV2YW5jZSBvZiB0aGUgcHJlZGljdG9ycy4gRm9yIGV4YW1wbGUsIGl0IGxvb2tzIGxpa2UgYGFic2VuY2VzYApoYXMgcmVhbGx5IHNtYWxsIHJlbGV2YW5jZSBhbmQgaGlnaCBjZXJ0YWludHkuCgpTdGFuZGFyZGl6ZSBhbGwgcHJlZGljdG9ycyBmb3IgZWFzaWVyIGNvbXBhcmlzb24gb2YgcmVsZXZhbmNlcyBhcwpkaXNjdXNzZWQgaW4gU2VjdGlvbiAxMi4xLgoKYGBge3IgfQpkYXRhc3RkX0czbWF0IDwtIGRhdGFfRzNtYXQKZGF0YXN0ZF9HM21hdFsscHJlZGljdG9yc10gPC1zY2FsZShkYXRhX0czbWF0WyxwcmVkaWN0b3JzXSkKYGBgCgojIyBEZWZhdWx0IHdlYWsgcHJpb3Igb24gY29lZmZpY2llbnRzCgojIyMjIEZpdCBhIHJlZ3Jlc3Npb24gbW9kZWwgd2l0aCBkZWZhdWx0IHdlYWsgcHJpb3JzCgpgYGB7ciB9CmZpdDEgPC0gc3Rhbl9nbG0oRzNtYXQgfiAuLCBkYXRhID0gZGF0YXN0ZF9HM21hdCwgc2VlZCA9IFNFRUQsIHJlZnJlc2g9MCkKYGBgCgojIyMjIFBsb3QgcG9zdGVyaW9yIG1hcmdpbmFscyBvZiBjb2VmZmljaWVudHMKCmBgYHtyIH0KcDEgPC0gbWNtY19hcmVhcyhhcy5tYXRyaXgoZml0MSksIHBhcnM9dmFycygtJyhJbnRlcmNlcHQpJywtc2lnbWEpLAogICAgICAgICAgICAgICAgIHByb2Jfb3V0ZXI9MC45NSwgYXJlYV9tZXRob2QgPSAic2NhbGVkIGhlaWdodCIpICsKICB4bGltKGMoLTEuMiwwLjgpKQpwMSA8LSBwMSArIHNjYWxlX3lfZGlzY3JldGUobGltaXRzID0gcmV2KGxldmVscyhwMSRkYXRhJHBhcmFtZXRlcikpKQpwMQpgYGAKYGBge3IgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KaWYgKHNhdmVmaWdzKQogIGdnc2F2ZShyb290KCJTdHVkZW50L2ZpZ3MiLCJzdHVkZW50X2ZpdDFfbWNtY19hcmVhcy5wZGYiKSwgcDEsIGhlaWdodD01LCB3aWR0aD01LCBjb2xvcm1vZGVsPSJncmF5IikKYGBgCgpUaGUgYWJvdmUgZmlndXJlIHNob3dzIHRoYXQgYWZ0ZXIgYWxsIHByZWRpY3RvcnMgaGF2ZSBiZWVuCnN0YW5kYXJkaXplZCB0byBoYXZlIGVxdWFsIHN0YW5kYXJkIGRldmlhdGlvbiwgdGhlIHVuY2VydGFpbnRpZXMgb24KdGhlIHJlbGV2YW5jZXMgYXJlIHNpbWlsYXIuIEZvciBleGFtcGxlLCBpdCBpcyBub3cgZWFzaWVyIHRvIHNlZQp0aGF0IGBhYnNlbmNlc2AgaGFzIHJlbGF0aXZlbHkgaGlnaCByZWxldmFuY2UgY29tcGFyZWQgdG8gb3RoZXIKcHJlZGljdG9ycyBpbiB0aGUgbW9kZWwuCgojIyMjIENvbXBhcmUgQmF5ZXNpYW4gJFJeMiQgYW5kIExPTyAkUl4yJAoKYGBge3IgfQpyb3VuZChtZWRpYW4oYmF5ZXNfUjIoZml0MSkpLCAyKQpyb3VuZChtZWRpYW4obG9vX1IyKGZpdDEpKSwgMikKYGBgCgojIyMjIENvbXB1dGUgTE9PIGxvZyBzY29yZQoKYGBge3IgfQoobG9vMSA8LSBsb28oZml0MSkpCmBgYAoKTWVkaWFucyBvZiBCYXllc2lhbiAkUl4yJCBhbmQgTE9PICRSXjIkIGFyZSBxdWl0ZSBkaWZmZXJlbnQsIGFuZCBwX2xvbwppcyBhcHByb3hpbWF0ZWx5IDI2LCB3aGljaCBpbmRpY2F0ZXMgdGhhdCB0aGUgbW9kZWwgaXMgZml0dGluZyB0bwphbGwgcHJlZGljdG9ycy4gCgpJZiB0aGUgcHJlZGljdG9ycyBoYXZlIGJlZW4gc3RhbmRhcmRpemVkIHRvIGhhdmUgc3RhbmRhcmQgZGV2aWF0aW9uCjEgYW5kIHdlIGdpdmUgdGhlIHJlZ3Jlc3Npb24gY29lZmZpY2llbnRzIGluZGVwZW5kZW50IG5vcm1hbCBwcmlvcnMKd2l0aCBtZWFuIDAgYW5kIHN0YW5kYXJkIGRldmlhdGlvbiAyLjUsIHRoaXMgaW1wbGllcyB0aGF0IHRoZSBwcmlvcgpzdGFuZGFyZCBkZXZpYXRpb24gb2YgdGhlIG1vZGVsZWQgcHJlZGljdGl2ZSBtZWFucyBpcyAkMi41ClxzcXJ0ezI2fSA9IDEyLjckLiBUaGUgZGVmYXVsdCBwcmlvciBmb3IgJFxzaWdtYSQgaXMgYW4gZXhwb25lbnRpYWwKZGlzdHJpYnV0aW9uLCBzY2FsZWQgdG8gaGF2ZSBtZWFuIGVxdWFsIHRvIGRhdGEgc3RhbmRhcmQgZGV2aWF0aW9uCndoaWNoIGluIHRoaXMgY2FzZSBpcyBhcHByb3hpbWF0ZWx5IDMuMyB3aGljaCBpcyBtdWNoIGxlc3MgdGhhbgoxMi43LiBXZSBjYW4gc2ltdWxhdGUgZnJvbSB0aGVzZSBwcmlvciBkaXN0cmlidXRpb25zIGFuZCBleGFtaW5lCndoYXQgaXMgdGhlIGNvcnJlc3BvbmRpbmcgcHJpb3IgZGlzdHJpYnV0aW9uIGZvciBleHBsYWluZWQgdmFyaWFuY2UKJFJeMiQuCgojIyMjIEJheWVzaWFuICRSXjIkIGRpc3RyaWJ1dGlvbgoKYGBge3IgfQpnZ3Bsb3QoKSArIGdlb21faGlzdG9ncmFtKGFlcyh4PWJheWVzX1IyKGZpdDEpKSwgYnJlYWtzPXNlcSgwLDEsbGVuZ3RoLm91dD0xMDApKSArCiAgeGxpbShjKDAsMSkpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzPU5VTEwpICsKICBsYWJzKHg9IkJheWVzaWFuIFJeMiIsIHk9IiIpCmBgYAoKIyMjIyBQcmlvciBwcmVkaWN0aXZlIGNoZWNraW5nIGJ5IGxvb2tpbmcgYXQgdGhlIHByaW9yIG9uIEJheWVzaWFuICRSXjIkPC9icj4KCmBgYHtyIH0KcHBSMjwtbnVtZXJpYygpCmZvciAoaSBpbiAxOjQwMDApIHsKICBzaWdtYTIgPC0gcmV4cCgxLHJhdGU9MC4zKV4yOwogIG11dmFyIDwtIHZhcihhcy5tYXRyaXgoZGF0YXN0ZF9HM21hdFssMjoyN10pICUqJSBybm9ybSgyNikqMi41KQogIHBwUjJbaV0gPC0gbXV2YXIvKG11dmFyK3NpZ21hMikKfQpnZ3Bsb3QoKStnZW9tX2hpc3RvZ3JhbShhZXMoeD1wcFIyKSwgYnJlYWtzPXNlcSgwLDEsbGVuZ3RoLm91dD01MCkpICsKICB4bGltKGMoMCwxKSkgKwogIHNjYWxlX3lfY29udGludW91cyhicmVha3M9TlVMTCkgKwogIGxhYnMoeD0iUHJpb3IgcHJlZGljdGl2ZSBCYXllc2lhbiBSXjIiLHk9IiIpCgpwcDEgPC0gbWNtY19oaXN0KGRhdGEuZnJhbWUoUHJpb3I9cHBSMixQb3N0ZXJpb3I9YmF5ZXNfUjIoZml0MSkpLAogICAgICAgICAgICAgICAgIGJyZWFrcz1zZXEoMCwxLGxlbmd0aC5vdXQ9MTAwKSwKICAgICAgICAgICAgICAgICBmYWNldF9hcmdzID0gbGlzdChucm93ID0gMikpICsKICBmYWNldF90ZXh0KHNpemUgPSAxMykgKwogIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKDAsMSksIGV4cGFuZCA9IGMoMCwgMCksCiAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIjAiLCIwLjI1IiwiMC41IiwiMC43NSIsIjEiKSkgKwogIHRoZW1lKGF4aXMubGluZS55ID0gZWxlbWVudF9ibGFuaygpKSArCiAgeGxhYigiQmF5ZXNpYW4gUl4yIikKcHAxCmBgYApgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQppZiAoc2F2ZWZpZ3MpCiAgZ2dzYXZlKHJvb3QoIlN0dWRlbnQvZmlncyIsInN0dWRlbnRfZml0MV9SMi5wZGYiKSwgcHAxLCBoZWlnaHQ9Mywgd2lkdGg9MywgY29sb3Jtb2RlbD0iZ3JheSIpCmBgYAoKVGhlIGFib3ZlIGZpZ3VyZSBzaG93cyB0aGF0IHdpdGggdGhlIGRlZmF1bHQgcHJpb3Igb24gcmVncmVzc2lvbgpjb2VmZmljaWVudHMgYW5kICRcc2lnbWEkLCB0aGUgaW1wbGllZCBwcmlvciBkaXN0cmlidXRpb24gZm9yICRSXjIkCmlzIHN0cm9uZ2x5IGZhdm9yaW5nIGxhcmdlciB2YWx1ZXMgYW5kIHRodXMgaXMgZmF2b3Jpbmcgb3ZlcmZpdHRlZAptb2RlbHMuIFRoZSBwcmlvcnMgb2Z0ZW4gY29uc2lkZXJlZCBhcyB3ZWFrbHkgaW5mb3JtYXRpdmUgZm9yCnJlZ3Jlc3Npb24gY29lZmZpY2llbnRzIHR1cm4gb3V0IHRvIGJlIGluIG11bHRpcGxlIHByZWRpY3RvciBjYXNlCmhpZ2hseSBpbmZvcm1hdGl2ZSBmb3IgdGhlIGV4cGxhaW5lZCB2YXJpYW5jZS4KCiMjIFdlYWtseSBpbmZvcm1hdGl2ZSBwcmlvciBzY2FsZWQgd2l0aCB0aGUgbnVtYmVyIG9mIGNvdmFyaWF0ZXMKCiMjIyMgUHJpb3IgcHJlZGljdGl2ZSBjaGVja2luZyBieSBsb29raW5nIGF0IHRoZSBwcmlvciBvbiBCYXllc2lhbiAkUl4yJAoKYGBge3IgfQpwcFIyPC1udW1lcmljKCkKZm9yIChpIGluIDE6NDAwMCkgewogIHNpZ21hMiA8LSAwLjcqcmV4cCgxLCByYXRlPTEvc2QoZGF0YXN0ZF9HM21hdCRHM21hdCkpXjIKICBtdXZhciA8LSB2YXIoYXMubWF0cml4KGRhdGFzdGRfRzNtYXRbLDI6MjddKSAlKiUgcm5vcm0oMjYsIHNkPXNkKGRhdGFzdGRfRzNtYXQkRzNtYXQpL3NxcnQoMjYpKnNxcnQoMC4zKSkpCiAgcHBSMltpXSA8LSBtdXZhci8obXV2YXIrc2lnbWEyKQp9CmdncGxvdCgpK2dlb21faGlzdG9ncmFtKGFlcyh4PXBwUjIpLCBicmVha3M9c2VxKDAsMSxsZW5ndGgub3V0PTUwKSkgKwogIHhsaW0oYygwLDEpKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcz1OVUxMKSArCiAgbGFicyh4PSJQcmlvciBwcmVkaWN0aXZlIEJheWVzaWFuIFJeMiIseT0iIikKYGBgCgojIyMjIEZpdCBhIHJlZ3Jlc3Npb24gbW9kZWwgd2l0aCBhIHdlYWtseSBpbmZvcm1hdGl2ZSBwcmlvciBzY2FsZWQgd2l0aCB0aGUgbnVtYmVyIG9mIGNvdmFyaWF0ZXMKCmBgYHtyIH0KZml0MiA8LSBzdGFuX2dsbShHM21hdCB+IC4sIGRhdGEgPSBkYXRhc3RkX0czbWF0LCBzZWVkID0gU0VFRCwKICAgICAgICAgICAgICAgICBwcmlvcj1ub3JtYWwoc2NhbGU9c2QoZGF0YXN0ZF9HM21hdCRHM21hdCkvc3FydCgyNikqc3FydCgwLjMpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhdXRvc2NhbGU9RkFMU0UpLAogICAgICAgICAgICAgICAgIHJlZnJlc2g9MCkKYGBgCgpXaGVuIHdlIGNvbXBhcmUgQmF5ZXNpYW4gJFJeMiQgYW5kIExPTyAkUl4yJCwgd2Ugc2VlIHRoZSBkaWZmZXJlbmNlCmlzIG11Y2ggc21hbGxlciBhbmQgTE9PICRSXjIkIGhhcyBpbXByb3ZlZCBzbGlnaHRseS4KCiMjIyMgQ29tcGFyZSBCYXllc2lhbiAkUl4yJCBhbmQgTE9PICRSXjIkCgpgYGB7ciB9CnJvdW5kKG1lZGlhbihsb29fUjIoZml0MikpLCAyKQpyb3VuZChtZWRpYW4oYmF5ZXNfUjIoZml0MikpLCAyKQpgYGAKCiMjIyMgQmF5ZXNpYW4gJFJeMiQgZGlzdHJpYnV0aW9uCgpgYGB7ciB9CmdncGxvdCgpK2dlb21faGlzdG9ncmFtKGFlcyh4PWJheWVzX1IyKGZpdDIpKSwgYnJlYWtzPXNlcSgwLDEsbGVuZ3RoLm91dD0xMDApKSArCiAgeGxpbShjKDAsMSkpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzPU5VTEwpICsKICBsYWJzKHg9IkJheWVzaWFuIFJeMiIseT0iIikKCnBwMiA8LSBtY21jX2hpc3QoZGF0YS5mcmFtZShQcmlvcj1wcFIyLFBvc3Rlcmlvcj1iYXllc19SMihmaXQyKSksCiAgICAgICAgICAgICAgICAgYnJlYWtzPXNlcSgwLDEsbGVuZ3RoLm91dD0xMDApLAogICAgICAgICAgICAgICAgIGZhY2V0X2FyZ3MgPSBsaXN0KG5yb3cgPSAyKSkgKwogIGZhY2V0X3RleHQoc2l6ZSA9IDEzKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cyA9IGMoMCwxKSwgZXhwYW5kID0gYygwLCAwKSwKICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygiMCIsIjAuMjUiLCIwLjUiLCIwLjc1IiwiMSIpKSArCiAgdGhlbWUoYXhpcy5saW5lLnkgPSBlbGVtZW50X2JsYW5rKCkpICsKICB4bGFiKCJCYXllc2lhbiBSXjIiKQpwcDIKYGBgCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CmlmIChzYXZlZmlncykKICBnZ3NhdmUocm9vdCgiU3R1ZGVudC9maWdzIiwic3R1ZGVudF9maXQyX1IyLnBkZiIpLCBwcDIsIGhlaWdodD0zLCB3aWR0aD0zLCBjb2xvcm1vZGVsPSJncmF5IikKYGBgCgpDb21wYXJpc29uIG9mIHRoZSBMT08gbG9nIHNjb3JlIHJldmVhbHMgdGhhdCB0aGUgbmV3IG1vZGVsIGhhcwpiZXR0ZXIgbGVhdmUtb25lLW91dCBwcmVkaWN0aW9uLgoKIyMjIyBDb21wdXRlIExPTyBsb2cgc2NvcmUKCmBgYHtyIH0KKGxvbzIgPC0gbG9vKGZpdDIpKQpgYGAKCiMjIyMgQ29tcGFyZSBtb2RlbHMKCmBgYHtyIH0KbG9vX2NvbXBhcmUobG9vMSxsb28yKQpgYGAKCiMjIyMgUGxvdCBwb3N0ZXJpb3IgbWFyZ2luYWxzIG9mIGNvZWZmaWNpZW50cwoKYGBge3IgfQpwMiA8LSBtY21jX2FyZWFzKGFzLm1hdHJpeChmaXQyKSwgcGFycz12YXJzKC0nKEludGVyY2VwdCknLC1zaWdtYSksCiAgICAgICAgICAgICAgICAgcHJvYl9vdXRlcj0wLjk1LCBhcmVhX21ldGhvZCA9ICJzY2FsZWQgaGVpZ2h0IikgKwogIHhsaW0oYygtMS4yLDAuOCkpCnAyIDwtIHAyICsgc2NhbGVfeV9kaXNjcmV0ZShsaW1pdHMgPSByZXYobGV2ZWxzKHAyJGRhdGEkcGFyYW1ldGVyKSkpCnAyCmBgYApgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQppZiAoc2F2ZWZpZ3MpCiAgZ2dzYXZlKHJvb3QoIlN0dWRlbnQvZmlncyIsInN0dWRlbnRfZml0Ml9tY21jX2FyZWFzLnBkZiIpLCBwMiwgaGVpZ2h0PTUsIHdpZHRoPTUsIGNvbG9ybW9kZWw9ImdyYXkiKQpgYGAKClRoZSBhYm92ZSBmaWd1cmUgc2hvd3MgdGhlIHBvc3RlcmlvciBkaXN0cmlidXRpb25zIG9mIGNvZWZmaWNpZW50cywKd2hpY2ggYXJlIHNsaWdodGx5IG1vcmUgY29uY2VudHJhdGVkIHRoYW4gZm9yIHRoZSBwcmV2aW91cyBtb2RlbC4KCiMjIFdlYWtseSBpbmZvcm1hdGl2ZSBwcmlvciBhc3N1bWluZyBvbmx5IHNvbWUgY292YXJpYXRlcyBhcmUgcmVsZXZhbnQKCldlIG5leHQgdXNlIHJlZ3VsYXJpemVkIGhvcnNlc2hvZSBwcnVpb3IsIGFzc3VtaW5nIHRoYXQgdGhlCmV4cGVjdGVkIG51bWJlciBvZiByZWxldmFudCBwcmVkaWN0b3JzIGlzIG5lYXIgJHBfMD02JCBhbmQgdGhlCnByaW9yIHNjYWxlIGZvciB0aGUgcmVsZXZhbnQgcHJlZGljdG9ycyBpcyBjaG9zZW4gYXMgaW4gdGhlCnByZXZpb3VzIG1vZGVsIGJ1dCB1c2luZyAkcF8wJCBmb3Igc2NhbGluZy4gV2UgY2FuIHRoZW4gc2ltdWxhdGUKZnJvbSB0aGlzIHByaW9yIGFuZCBleGFtaW5lIHRoZSBjb3JyZXNwb25kaW5nIHByaW9yIGZvciAkUl4yJAoKYGBge3IgfQpwMCA8LSA2CnNsYWJfc2NhbGUgPC0gc2QoZGF0YXN0ZF9HM21hdCRHM21hdCkvc3FydChwMCkqc3FydCgwLjMpCiMKcHBSMjwtbnVtZXJpYygpCmZvciAoaSBpbiAxOjQwMDApIHsKICBzaWdtYTIgPC0gMC43KnJleHAoMSxyYXRlPTEvc2QoZGF0YXN0ZF9HM21hdCRHM21hdCkpXjI7CiAgZ2xvYmFsX3NjYWxlIDwtIHAwIC8gKHAgLSBwMCkgKiBzcXJ0KHNpZ21hMikgLyBzcXJ0KG4pCiAgeiA8LSBybm9ybShwKQogIGxhbWJkYSA8LSByY2F1Y2h5KHApCiAgdGF1IDwtIHJjYXVjaHkoMSwgc2NhbGUgPSBnbG9iYWxfc2NhbGUpCiAgY2F1eCA8LSAxL3JnYW1tYSgxLCBzaGFwZT0wLjUsIHJhdGU9MC41KQogIGMgPC0gIHNsYWJfc2NhbGUgKiBzcXJ0KGNhdXgpCiAgbGFtYmRhX3RpbGRlIDwtIHNxcnQoY14yICogbGFtYmRhXjIgLyAoY14yICsgdGF1XjIqbGFtYmRhXjIpKQogIGJldGEgPC0gcm5vcm0ocCkgKiBsYW1iZGFfdGlsZGUgKiB0YXUKICBtdXZhciA8LSB2YXIoYXMubWF0cml4KGRhdGFzdGRfRzNtYXRbLDI6MjddKSAlKiUgYmV0YSkKICBwcFIyW2ldIDwtIG11dmFyLyhtdXZhcitzaWdtYTIpCn0KZ2dwbG90KCkrZ2VvbV9oaXN0b2dyYW0oYWVzKHg9cHBSMiksIGJyZWFrcz1zZXEoMCwxLGxlbmd0aC5vdXQ9NTApKSArCiAgeGxpbShjKDAsMSkpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzPU5VTEwpICsKICBsYWJzKHg9IlByaW9yIHByZWRpY3RpdmUgQmF5ZXNpYW4gUl4yIix5PSIiKQpgYGAKClRoZSBhYm92ZSBmaWd1cmUgc2hvd3MgdGhhdCB0aGUgcmVndWxhcml6ZWQgaG9yc2VzaG9lIHByaW9yIHdpdGgKc2Vuc2libGUgcGFyYW1ldGVycyBpbXBsaWVzIGEgbW9yZSBjYXV0aW91cyBwcmlvciBvbiBleHBsYWluZWQKdmFyaWFuY2UgJFJeMiQgdGhhbiBpcyBpbXBsaWNpdGx5IGFzc3VtZWQgYnkgdGhlIGRlZmF1bHQgd2lkZQpwcmlvci4gVGhlIGhvcnNlc2hvZSBwcmlvciBmYXZvcnMgc2ltcGxlciBtb2RlbHMsIGJ1dCBpcyBxdWl0ZSBmbGF0CmFyb3VuZCBtb3N0ICRSXjIkIHZhbHVlcy4KCiMjIyMgRml0IGEgcmVncmVzc2lvbiBtb2RlbCB3aXRoIHJlZ3VsYXJpemVkIGhvcnNlc2hvZSBwcmlvcjwvYnI+CgpgYGB7ciB9CnAwIDwtIDYKc2xhYl9zY2FsZSA8LSBzZChkYXRhc3RkX0czbWF0JEczbWF0KS9zcXJ0KHAwKSpzcXJ0KDAuMykKIyBnbG9iYWwgc2NhbGUgd2l0aG91dCBzaWdtYSwgYXMgdGhlIHNjYWxpbmcgYnkgc2lnbWEgaGFwcGVucyBpbiBzdGFuX2dsbQpnbG9iYWxfc2NhbGUgPC0gcDAgLyAocCAtIHAwKSAvIHNxcnQobikKZml0MyA8LSBzdGFuX2dsbShHM21hdCB+IC4sIGRhdGEgPSBkYXRhc3RkX0czbWF0LCBzZWVkID0gU0VFRCwKICAgICAgICAgICAgICAgICBwcmlvcj1ocyhnbG9iYWxfc2NhbGU9Z2xvYmFsX3NjYWxlLCBzbGFiX3NjYWxlPXNsYWJfc2NhbGUpLAogICAgICAgICAgICAgICAgIHJlZnJlc2g9MCkKYGBgCgojIyMjIENvbXBhcmUgQmF5ZXNpYW4gJFJeMiQgYW5kIExPTyAkUl4yJAoKYGBge3IgfQpyb3VuZChtZWRpYW4obG9vX1IyKGZpdDMpKSwgMikKcm91bmQobWVkaWFuKGJheWVzX1IyKGZpdDMpKSwgMikKYGBgCgpXaGVuIHdlIGNvbXBhcmUgbW9kZWxzIHVzaW5nIExPTyBsb2cgc2NvcmUsIHRoZSBuZXcgbW9kZWwgaXMgYmV0dGVyCnRoYW4gdGhlIGRlZmF1bHQgcHJpb3IgbW9kZWwsIGJ1dCB0aGVyZSBpcyBubyBkaWZmZXJlbmNlIGNvbXBhcmVkCnRoZSBtb2RlbCB3aXRoIG5vcm1hbCBwcmlvciBzY2FsZWQgd2l0aCB0aGUgcHJlZGljdG9ycy4gSXQgaXMKY29tbW9uIHRoYXQgdGhlIGRhdGEgZG8gbm90IGhhdmUgc3Ryb25nIGluZm9ybWF0aW9uIGFib3V0IGhvdyBtYW55CnByZWRpY3RvcnMgYXJlIHJlbGV2YW50IGFuZCB0aGVuIGRpZmZlcmVudCB0eXBlcyBvZiBwcmlvcnMgY2FuCnByb2R1Y2Ugc2ltaWxhciBwcmVkaWN0aXZlIGFjY3VyYWNpZXMuCgojIyMjIENvbXB1dGUgTE9PIGxvZyBzY29yZQoKYGBge3IgfQoobG9vMyA8LSBsb28oZml0MykpCmBgYAoKIyMjIyBDb21wYXJlIG1vZGVscwoKYGBge3IgfQpsb29fY29tcGFyZShsb28xLGxvbzMpCmxvb19jb21wYXJlKGxvbzIsbG9vMykKYGBgCgojIyMjIEJheWVzaWFuICRSXjIkIGRpc3RyaWJ1dGlvbgoKYGBge3IgfQpnZ3Bsb3QoKStnZW9tX2hpc3RvZ3JhbShhZXMoeD1iYXllc19SMihmaXQzKSksIGJyZWFrcz1zZXEoMCwxLGxlbmd0aC5vdXQ9MTAwKSkgKwogIHhsaW0oYygwLDEpKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcz1OVUxMKSArCiAgbGFicyh4PSJCYXllc2lhbiBSXjIiLHk9IiIpCgpwcDMgPC0gbWNtY19oaXN0KGRhdGEuZnJhbWUoUHJpb3I9cHBSMixQb3N0ZXJpb3I9YmF5ZXNfUjIoZml0MykpLAogICAgICAgICAgICAgICAgIGJyZWFrcz1zZXEoMCwxLGxlbmd0aC5vdXQ9MTAwKSwKICAgICAgICAgICAgICAgICBmYWNldF9hcmdzID0gbGlzdChucm93ID0gMikpICsKICBmYWNldF90ZXh0KHNpemUgPSAxMykgKwogIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKDAsMSksIGV4cGFuZCA9IGMoMCwgMCksCiAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIjAiLCIwLjI1IiwiMC41IiwiMC43NSIsIjEiKSkgKwogIHRoZW1lKGF4aXMubGluZS55ID0gZWxlbWVudF9ibGFuaygpKSArCiAgeGxhYigiQmF5ZXNpYW4gUl4yIikKcHAzCmBgYApgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQppZiAoc2F2ZWZpZ3MpCiAgZ2dzYXZlKHJvb3QoIlN0dWRlbnQvZmlncyIsInN0dWRlbnRfZml0M19SMi5wZGYiKSwgcHAzLCBoZWlnaHQ9Mywgd2lkdGg9MywgY29sb3Jtb2RlbD0iZ3JheSIpCmBgYAoKIyMjIyBQbG90IHBvc3RlcmlvciBtYXJnaW5hbHMgb2YgY29lZmZpY2llbnRzCgpgYGB7ciB9CnAzIDwtIG1jbWNfYXJlYXMoYXMubWF0cml4KGZpdDMpLCBwYXJzPXZhcnMoLScoSW50ZXJjZXB0KScsLXNpZ21hKSwKICAgICAgICAgICAgICAgICBwcm9iX291dGVyPTAuOTUsIGFyZWFfbWV0aG9kID0gInNjYWxlZCBoZWlnaHQiKSArCiAgeGxpbShjKC0xLjIsMC44KSkKcDMgPC0gcDMgKyBzY2FsZV95X2Rpc2NyZXRlKGxpbWl0cyA9IHJldihsZXZlbHMocDMkZGF0YSRwYXJhbWV0ZXIpKSkKcDMKYGBgCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CmlmIChzYXZlZmlncykKICBnZ3NhdmUocm9vdCgiU3R1ZGVudC9maWdzIiwic3R1ZGVudF9maXQzX21jbWNfYXJlYXMucGRmIiksIHAzLCBoZWlnaHQ9NSwgd2lkdGg9NSwgY29sb3Jtb2RlbD0iZ3JheSIpCmBgYAoKVGhlIGFib3ZlIGZpZ3VyZSBzaG93cyB0aGF0IHRoZSByZWd1bGFyaXplZCBob3JzZXNob2UgcHJpb3IgaGFzIHRoZQpiZW5lZml0IG9mIHNocmlua2luZyB0aGUgcG9zdGVyaW9yIGZvciBtYW55IHJlZ3Jlc3Npb24gY29lZmZpY2llbnRzCm1vcmUgdGlnaHRseSB0b3dhcmRzIDAsIG1ha2luZyBpdCBlYXNpZXIgdG8gc2VlIHRoZSBtb3N0IHJlbGV2YW50CnByZWRpY3RvcnMuIEZhaWx1cmVzLCBzY2hvb2wgc3VwcG9ydCwgZ29pbmcgb3V0LCBhbmQgdGhlIG51bWJlciBvZgphYnNlbmNlcyBhcHBlYXIgdG8gYmUgdGhlIG1vc3QgcmVsZXZhbnQgcHJlZGljdG9ycy4KCiMjIFN1YnNldCBvZiBjb3ZhcmlhdGVzCgpGaXQgYSByZWdyZXNzaW9uIG1vZGVsIHdpdGggc3Vic2V0IG9mIGNvdmFyaWF0ZXMgYW5kIGRlZmF1bHQgd2VhawpwcmlvciBvbiBjb2VmZmljaWVudHMKCmBgYHtyIH0KZml0NCA8LSBzdGFuX2dsbShHM21hdCB+IGZhaWx1cmVzICsgc2Nob29sc3VwICsgZ29vdXQgKyBhYnNlbmNlcywKICAgICAgICAgICAgICAgICBkYXRhID0gZGF0YXN0ZF9HM21hdCwgc2VlZCA9IFNFRUQsIHJlZnJlc2g9MCkKYGBgCgpXaGVuIHdlIGNvbXBhcmUgQmF5ZXNpYW4gJFJeMiQgYW5kIExPTyAkUl4yJCwgd2Ugc2VlIHRoZSBkaWZmZXJlbmNlCmlzIHNtYWxsIGFuZCB0aGVyZSBpcyBsZXNzIG92ZXJmaXQgdGhhbiB3aGVuIHVzaW5nIGFsbCBwcmVkaWN0b3JzCndpdGggd2lkZSBwcmlvci4gTE9PICRSXjIkIGlzIGp1c3Qgc2xpZ2h0bHkgc21hbGxlciB0aGFuIGZvciBtb2RlbHMKd2l0aCBhbGwgcHJlZGljdG9ycyBhbmQgYmV0dGVyIHByaW9ycy4gVGhlIHByZWRpY3Rpb24gcGVyZm9ybWFuY2UKY2FuIG5vdCBpbXByb3ZlZCBtdWNoIGJ5IGFkZGluZyBtb3JlIHByZWRpY3RvcnMuIE5vdGUgdGhhdCBieQpvYnNlcnZpbmcgbW9yZSBzdHVkZW50cyBpdCBtaWdodCBiZSBwb3NzaWJsZSB0byBsZWFybiByZWdyZXNzaW9uCmNvZWZmaWNpZW50cyBmb3Igb3RoZXIgcHJlZGljdG9ycyB3aXRoIHN1ZmZpY2llbnQgc21hbGwgdW5jZXJ0YWludHkKc28gdGhhdCBwcmVkaWN0aW9ucyBmb3IgbmV3IHN0dWRlbnRzIGNvdWxkIGJlIGltcHJvdmVkLgoKIyMjIyBDb21wYXJlIEJheWVzaWFuICRSXjIkIGFuZCBMT08gJFJeMiQKCmBgYHtyIH0Kcm91bmQobWVkaWFuKGxvb19SMihmaXQ0KSksIDIpCnJvdW5kKG1lZGlhbihiYXllc19SMihmaXQ0KSksIDIpCmBgYAoKIyMjIyBCYXllc2lhbiAkUl4yJCBkaXN0cmlidXRpb24KCmBgYHtyIH0KZ2dwbG90KCkrZ2VvbV9oaXN0b2dyYW0oYWVzKHg9YmF5ZXNfUjIoZml0NCkpLCBicmVha3M9c2VxKDAsMSxsZW5ndGgub3V0PTEwMCkpICsKICB4bGltKGMoMCwxKSkgKwogIHNjYWxlX3lfY29udGludW91cyhicmVha3M9TlVMTCkgKwogIGxhYnMoeD0iQmF5ZXNpYW4gUl4yIix5PSIiKQpgYGAKCiMjIyMgQ29tcHV0ZSBMT08gbG9nIHNjb3JlCgpgYGB7ciB9Cihsb280IDwtIGxvbyhmaXQ0KSkKYGBgCgojIyMjIENvbXBhcmUgbW9kZWxzCgpgYGB7ciB9Cmxvb19jb21wYXJlKGxvbzMsbG9vNCkKbG9vX2NvbXBhcmUobG9vMixsb280KQpsb29fY29tcGFyZShsb28xLGxvbzQpCmBgYAoKIyMjIyBQbG90IHBvc3RlcmlvciBtYXJnaW5hbHMgb2YgY29lZmZpY2llbnRzCgpgYGB7ciB9CnA0IDwtIG1jbWNfYXJlYXMoYXMubWF0cml4KGZpdDQpLCBwYXJzPXZhcnMoLScoSW50ZXJjZXB0KScsLXNpZ21hKSwKICAgICAgICAgICAgICAgICBwcm9iX291dGVyPTAuOTksIGFyZWFfbWV0aG9kID0gInNjYWxlZCBoZWlnaHQiKSArCiAgeGxpbShjKC0xLjMsMC4xKSkKcDQgPC0gcDQgKyBzY2FsZV95X2Rpc2NyZXRlKGxpbWl0cyA9IHJldihsZXZlbHMocDQkZGF0YSRwYXJhbWV0ZXIpKSkKcDQKYGBgCgo=