Predicting presidential vote share from the economy. See Chapters 1, 7, 8, 9, and 22 in Regression and Other Stories.


Load packages

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

Load data

hibbs <- read.table(root("ElectionsEconomy/data","hibbs.dat"), header=TRUE)
head(hibbs)
  year growth  vote inc_party_candidate other_candidate
1 1952   2.40 44.60           Stevenson      Eisenhower
2 1956   2.89 57.76          Eisenhower       Stevenson
3 1960   0.85 49.91               Nixon         Kennedy
4 1964   4.21 61.34             Johnson       Goldwater
5 1968   3.02 49.60            Humphrey           Nixon
6 1972   3.62 61.79               Nixon        McGovern

Graphing the bread and peace model

n <- nrow(hibbs)
par(mar=c(0,0,1.2,0))
left <- -.3
right <- -.28
center <- -.07
f <- .17
plot(c(left-.31,center+.23), c(-3.3,n+3), type="n", bty="n", xaxt="n", yaxt="n", xlab="", ylab="", xaxs="i", yaxs="i")
mtext("Forecasting elections from the economy", 3, 0, cex=1.2)
with(hibbs, {
  for (i in 1:n){
    ii <- order(growth)[i]
    text(left-.3, i, paste (inc_party_candidate[ii], " vs. ", other_candidate[ii], " (", year[ii], ")", sep=""), adj=0, cex=.8)
    points(center+f*(vote[ii]-50)/10, i, pch=20)
    if (i>1){
      if (floor(growth[ii]) != floor(growth[order(growth)[i-1]])){
        lines(c(left-.3,center+.22), rep(i-.5,2), lwd=.5, col="darkgray")
      }
    }
  }
})
lines(center+f*c(-.65,1.3), rep(0,2), lwd=.5)
for (tick in seq(-.5,1,.5)){
  lines(center + f*rep(tick,2), c(0,-.2), lwd=.5)
  text(center + f*tick, -.5, paste(50+10*tick,"%",sep=""), cex=.8)
}
lines(rep(center,2), c(0,n+.5), lty=2, lwd=.5)
text(center+.05, n+1.5, "Incumbent party's share of the popular vote", cex=.8)
lines(c(center-.088,center+.19), rep(n+1,2), lwd=.5)
text(right, n+1.5, "Income growth", adj=.5, cex=.8)
lines(c(right-.05,right+.05), rep(n+1,2), lwd=.5)
text(right, 16.15, "more than 4%", cex=.8)
text(right, 14, "3% to 4%", cex=.8)
text(right, 10.5, "2% to 3%", cex=.8)
text(right, 7, "1% to 2%", cex=.8)
text(right, 3.5, "0% to 1%", cex=.8)
text(right, .85, "negative", cex=.8)
text(left-.3, -2.3, "Above matchups are all listed as incumbent party's candidate vs.\ other party's candidate.\nIncome growth is a weighted measure over the four years preceding the election.  Vote share excludes third parties.", adj=0, cex=.7)

par(mar=c(3,3,2,.1), mgp=c(1.7,.5,0), tck=-.01)
plot(c(-.7, 4.5), c(43,63), type="n", xlab="Avg recent growth in personal income", ylab="Incumbent party's vote share", xaxt="n", yaxt="n", mgp=c(2,.5,0), main="Forecasting the election from the economy      ", bty="l")
axis(1, 0:4, paste(0:4,"%",sep=""), mgp=c(2,.5,0))
axis(2, seq(45,60,5), paste(seq(45,60,5),"%",sep=""), mgp=c(2,.5,0))
with(hibbs, text(growth, vote, year, cex=.8))
abline(50, 0, lwd=.5, col="gray")

Linear regression

The option refresh = 0 supresses the default Stan sampling progress output. This is useful for small data with fast computation. For more complex models and bigger data, it can be useful to see the progress.

M1 <- stan_glm(vote ~ growth, data = hibbs, refresh = 0)

Print default summary of the fitted model

print(M1)
stan_glm
 family:       gaussian [identity]
 formula:      vote ~ growth
 observations: 16
 predictors:   2
------
            Median MAD_SD
(Intercept) 46.3    1.6  
growth       3.1    0.7  

Auxiliary parameter(s):
      Median MAD_SD
sigma 3.9    0.7   

------
* For help interpreting the printed output see ?print.stanreg
* For info on the priors used see ?prior_summary.stanreg

Print summary of the priors used

prior_summary(M1)
Priors for model 'M1' 
------
Intercept (after predictors centered)
  Specified prior:
    ~ normal(location = 52, scale = 2.5)
  Adjusted prior:
    ~ normal(location = 52, scale = 14)

Coefficients
  Specified prior:
    ~ normal(location = 0, scale = 2.5)
  Adjusted prior:
    ~ normal(location = 0, scale = 10)

Auxiliary (sigma)
  Specified prior:
    ~ exponential(rate = 1)
  Adjusted prior:
    ~ exponential(rate = 0.18)
------
See help('prior_summary.stanreg') for more details

Almost all models in Regression and Other Stories have very good sampling behavior. summary() function can be used to obtain the summary of the convergence diagnostics for MCMC sampling.

summary(M1)

Model Info:
 function:     stan_glm
 family:       gaussian [identity]
 formula:      vote ~ growth
 algorithm:    sampling
 sample:       4000 (posterior sample size)
 priors:       see help('prior_summary')
 observations: 16
 predictors:   2

Estimates:
              mean   sd   10%   50%   90%
(Intercept) 46.3    1.8 44.1  46.3  48.5 
growth       3.1    0.8  2.1   3.1   4.0 
sigma        4.0    0.8  3.1   3.9   5.1 

Fit Diagnostics:
           mean   sd   10%   50%   90%
mean_PPD 52.1    1.4 50.3  52.1  53.8 

The mean_ppd is the sample average posterior predictive distribution of the outcome variable (for details see help('summary.stanreg')).

MCMC diagnostics
              mcse Rhat n_eff
(Intercept)   0.0  1.0  2293 
growth        0.0  1.0  2459 
sigma         0.0  1.0  2153 
mean_PPD      0.0  1.0  2949 
log-posterior 0.0  1.0  1473 

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).

Posterior interval

round(posterior_interval(M1),1)
              5%  95%
(Intercept) 43.4 49.2
growth       1.8  4.3
sigma        2.9  5.5

Plot regression line

par(mar=c(3,3,2,.1), mgp=c(1.7,.5,0), tck=-.01)
plot(c(-.7, 4.5), c(43,63), type="n", xlab="Average recent growth in personal income", ylab="Incumbent party's vote share", xaxt="n", yaxt="n", mgp=c(2,.5,0), main="Data and linear fit", bty="l")
axis(1, 0:4, paste(0:4,"%",sep=""), mgp=c(2,.5,0))
axis(2, seq(45,60,5), paste(seq(45,60,5),"%",sep=""), mgp=c(2,.5,0))
with(hibbs, points(growth, vote, pch=20))
abline(50, 0, lwd=.5, col="gray")
abline(coef(M1), col="gray15")
text(2.7, 53.5, paste("y =", fround(coef(M1)[1],1), "+", fround(coef(M1)[2],1), "x"), adj=0, col="gray15")

Plot prediction given 2% growth

par(mar=c(3,3,3,1), mgp=c(1.7,.5,0), tck=-.01)
mu <- 52.3
sigma <- 3.9
curve (dnorm(x,mu,sigma), ylim=c(0,.103), from=35, to=70, bty="n",
  xaxt="n", yaxt="n", yaxs="i",
  xlab="Clinton share of the two-party vote", ylab="",
  main="Probability forecast of Hillary Clinton vote share in 2016,\nbased on 2% rate of economic growth", cex.main=.9)
x <- seq (50,65,.1)
polygon(c(min(x),x,max(x)), c(0,dnorm(x,mu,sigma),0),
  col="darkgray", border="black")
axis(1, seq(40,65,5), paste(seq(40,65,5),"%",sep=""))
text(50.7, .025, "Predicted\n72% chance\nof Clinton victory", adj=0)

Plot data and linear fit

par(mar=c(3,3,2,.1), mgp=c(1.7,.5,0), tck=-.01)
plot(c(-.7, 4.5), c(43,63), type="n", xlab="x", ylab="y", xaxt="n", yaxt="n", mgp=c(2,.5,0), main="Data and linear fit", bty="l", cex.lab=1.3, cex.main=1.3)
axis(1, 0:4, cex.axis=1.3)
axis(2, seq(45, 60, 5), cex.axis=1.3)
abline(coef(M1), col="gray15")
with(hibbs, points(growth, vote, pch=20))
text(2.7, 53.5, paste("y =", fround(coef(M1)[1],1), "+", fround(coef(M1)[2],1), "x"), adj=0, col="gray15", cex=1.3)

Plot data and range of possible linear fits

par(mar=c(3,3,2,.1), mgp=c(1.7,.5,0), tck=-.01)
plot(c(-.7, 4.5), c(43,63), type="n", xlab="x", ylab="y", xaxt="n", yaxt="n", mgp=c(2,.5,0), main="Data and range of possible linear fits", bty="l", cex.lab=1.3, cex.main=1.3)
axis(1, 0:4, cex.axis=1.3)
axis(2, seq(45, 60, 5), cex.axis=1.3)
sims <- as.matrix(M1)
n_sims <- nrow(sims)
for (s in sample(n_sims, 50))
  abline(sims[s,1], sims[s,2], col="gray50", lwd=0.5)
with(hibbs, points(growth, vote, pch=20))

Illustrate computations

Extract the simulations

sims <- as.matrix(M1)
a <- sims[,1]
b <- sims[,2]
sigma <- sims[,3]
n_sims <- nrow(sims)

Median and mean absolute deviation (MAD_SD)

Median <- apply(sims, 2, median)
MAD_SD <- apply(sims, 2, mad)
print(cbind(Median, MAD_SD))
               Median    MAD_SD
(Intercept) 46.276439 1.6307144
growth       3.052480 0.6947779
sigma        3.886283 0.7495022

Median and mean absolute deviation (MAD_SD) for a derived quantity a/b

a <- sims[,1]
b <- sims[,2]
z <- a/b
print(median(z))
[1] 15.14231
print(mad(z))
[1] 3.840035

Point prediction given 2% growth

new <- data.frame(growth=2.0)
y_point_pred <- predict(M1, newdata=new)

Alternative way to compute the point prediction

a_hat <- coef(M1)[1]
b_hat <- coef(M1)[2]
y_point_pred <- a_hat + b_hat*as.numeric(new)

Uncertainty in prediction given 2% growth

y_linpred <- posterior_linpred(M1, newdata=new)

Do same computation "manually"

a <- sims[,1]
b <- sims[,2]
y_linpred <- a + b*as.numeric(new)

Predictive uncertainty

y_pred <- posterior_predict(M1, newdata=new)

Predictive uncertainty manually

sigma <- sims[,3]
n_sims <- nrow(sims)
y_pred <- a + b*as.numeric(new) + rnorm(n_sims, 0, sigma)

Summarize predictions

Median <- median(y_pred)
MAD_SD <- mad(y_pred)
win_prob <- mean(y_pred > 50)
cat("Predicted Clinton percentage of 2-party vote: ", round(Median,1),
  ", with s.e. ", round(MAD_SD, 1), "\nPr (Clinton win) = ", round(win_prob, 2),
  sep="")
Predicted Clinton percentage of 2-party vote: 52.5, with s.e. 3.9
Pr (Clinton win) = 0.73

Summarize predictions graphically

hist(y_pred)

Predict for many new values

new_grid <- data.frame(growth=seq(-2.0, 4.0, 0.5))
y_point_pred_grid <- predict(M1, newdata=new_grid)
y_linpred_grid <- posterior_linpred(M1, newdata=new_grid)
y_pred_grid <- posterior_predict(M1, newdata=new_grid)

Plots

par(mfrow=c(1,2), mar=c(3,2,3,0), mgp=c(1.5,.5,0), tck=-.01)
hist(a, ylim=c(0,0.25*n_sims), xlab="a", ylab="", main="Posterior simulations of the intercept, a,\nand posterior median +/- 1 and 2 std err", cex.axis=.9, cex.lab=.9, yaxt="n", col="gray90")
abline(v=median(a), lwd=2)
arrows(median(a) - 1.483*median(abs(a - median(a))), 550, median(a) + 1.483*median(abs(a - median(a))), 550, length=.1, code=3, lwd=2)
arrows(median(a) - 2*1.483*median(abs(a - median(a))), 250, median(a) + 2*1.483*median(abs(a - median(a))), 250, length=.1, code=3, lwd=2)
hist(b, ylim=c(0,0.27*n_sims), xlab="b", ylab="", main="Posterior simulations of the slope, b,\nand posterior median +/- 1 and 2 std err", cex.axis=.9, cex.lab=.9, yaxt="n", col="gray90")
abline(v=median(b), lwd=2)
arrows(median(b) - 1.483*median(abs(b - median(b))), 550, median(b) + 1.483*median(abs(b - median(b))), 550, length=.1, code=3, lwd=2)
arrows(median(b) - 2*1.483*median(abs(b - median(b))), 250, median(b) + 2*1.483*median(abs(b - median(b))), 250, length=.1, code=3, lwd=2)

par(mar=c(3,3,2,.1), mgp=c(1.7,.5,0), tck=-.01)
plot(a, b, xlab="a", ylab="b", main="Posterior draws of the regression coefficients a, b          ", bty="l", pch=20, cex=.2)

ggplot version

ggplot(data.frame(a = sims[, 1], b = sims[, 2]), aes(a, b)) +
  geom_point(size = 1) +
  labs(title = "Posterior draws of the regression coefficients a, b")

More plotting

par(mar=c(3,3,2,.1), mgp=c(1.7,.5,0), tck=-.01)
plot(c(-.7, 4.5), c(43,63), type="n", xlab="Average recent growth in personal income", ylab="Incumbent party's vote share", xaxt="n", yaxt="n", mgp=c(2,.5,0), main="Data and 100 posterior draws of the line, y = a + bx           ", bty="l")
axis(1, 0:4, paste(0:4,"%",sep=""), mgp=c(2,.5,0))
axis(2, seq(45,60,5), paste(seq(45,60,5),"%",sep=""), mgp=c(2,.5,0))
for (i in 1:100){
  abline(a[i], b[i], lwd=.5)
}
abline(50, 0, lwd=.5, col="gray")
with(hibbs, {
  points(growth, vote, pch=20, cex=1.7, col="white")
  points(growth, vote, pch=20)
})

ggplot version

ggplot(hibbs, aes(x = growth, y = vote)) +
  geom_abline(
    intercept = sims[1:100, 1],
    slope = sims[1:100, 2],
    size = 0.1
  ) +
  geom_abline(
    intercept = mean(sims[, 1]),
    slope = mean(sims[, 2])
  ) +
  geom_point(color = "white", size = 3) +
  geom_point(color = "black", size = 2) +
  labs(
    x = "Avg recent growth in personal income",
    y ="Incumbent party's vote share",
    title = "Data and 100 posterior draws of the line, y = a + bx"
  ) +
  scale_x_continuous(
    limits = c(-.7, 4.5),
    breaks = 0:4,
    labels = paste(0:4, "%", sep = "")
  ) +
  scale_y_continuous(
    limits = c(43, 63),
    breaks = seq(45, 60, 5),
    labels = paste(seq(45, 60, 5), "%", sep = "")
  )

Add more uncertainty

x <- rnorm(n_sims, 2.0, 0.3)
y_hat <- a + b*x
y_pred <- rnorm(n_sims, y_hat, sigma)

Median <- median(y_pred)
MAD_SD <- 1.483*median(abs(y_pred - median(y_pred)))
win_prob <- mean(y_pred > 50)
cat("Predicted Clinton percentage of 2-party vote: ", round(Median, 1), ",
  with s.e. ", round(MAD_SD, 1), "\nPr (Clinton win) = ", round(win_prob, 2), sep="", "\n")
Predicted Clinton percentage of 2-party vote: 52.3,
  with s.e. 4.2
Pr (Clinton win) = 0.71

More plotting

par(mar=c(3,3,3,1), mgp=c(1.7,.5,0), tck=-.01)
hist(y_pred, breaks=seq(floor(min(y_pred)), ceiling(max(y_pred)),1), xlim=c(35,70), xaxt="n", yaxt="n", yaxs="i", bty="n",
  xlab="Clinton share of the two-party vote", ylab="",
  main="Bayesian simulations of Hillary Clinton vote share,\nbased on 2% rate of economic growth")
axis(1, seq(40,65,5), paste(seq(40,65,5),"%",sep=""))

ggplot version

qplot(y_pred, binwidth = 1) +
    labs(
    x ="Clinton share of the two-party vote",
    title = "Simulations of Hillary Clinton vote share,\nbased on 2% rate of economic growth"
  ) +
  theme(axis.line.y = element_blank())

Bayesian inference and prior information

Combining information from a forecast and a poll. Hypothetical forecast and data.

theta_hat_prior <- 0.524
se_prior <- 0.041

n <- 400
y <- 190
theta_hat_data <- y/n
se_data <- sqrt((y/n)*(1-y/n)/n)

theta_hat_bayes <-
  (theta_hat_prior / se_prior^2 + theta_hat_data / se_data^2) /
  (1 / se_prior^2 + 1 / se_data^2)

se_bayes <- sqrt(1/(1/se_prior^2 + 1/se_data^2))

Ramp up the data variance

se_data <- .075
print((theta_hat_prior/se_prior^2 + theta_hat_data/se_data^2)/(1/se_prior^2 + 1/se_data^2))
[1] 0.5127258

Comparison to lm()

M1a <- lm(vote ~ growth, data=hibbs)
print(M1a)

Call:
lm(formula = vote ~ growth, data = hibbs)

Coefficients:
(Intercept)       growth  
     46.248        3.061  
summary(M1a)

Call:
lm(formula = vote ~ growth, data = hibbs)

Residuals:
    Min      1Q  Median      3Q     Max 
-8.9929 -0.6674  0.2556  2.3225  5.3094 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  46.2476     1.6219  28.514 8.41e-14 ***
growth        3.0605     0.6963   4.396  0.00061 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 3.763 on 14 degrees of freedom
Multiple R-squared:  0.5798,    Adjusted R-squared:  0.5498 
F-statistic: 19.32 on 1 and 14 DF,  p-value: 0.00061
LS0tCnRpdGxlOiAiUmVncmVzc2lvbiBhbmQgT3RoZXIgU3RvcmllczogRWxlY3Rpb25zIEVjb25vbXkiCmF1dGhvcjogIkFuZHJldyBHZWxtYW4sIEplbm5pZmVyIEhpbGwsIEFraSBWZWh0YXJpIgpkYXRlOiAiYHIgZm9ybWF0KFN5cy5EYXRlKCkpYCIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICB0aGVtZTogcmVhZGFibGUKICAgIHRvYzogdHJ1ZQogICAgdG9jX2RlcHRoOiAyCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKLS0tClByZWRpY3RpbmcgcHJlc2lkZW50aWFsIHZvdGUgc2hhcmUgZnJvbSB0aGUgZWNvbm9teS4gU2VlIENoYXB0ZXJzCjEsIDcsIDgsIDksIGFuZCAyMiBpbiBSZWdyZXNzaW9uIGFuZCBPdGhlciBTdG9yaWVzLgoKLS0tLS0tLS0tLS0tLQoKCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQobWVzc2FnZT1GQUxTRSwgZXJyb3I9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGNvbW1lbnQ9TkEpCiMgc3dpdGNoIHRoaXMgdG8gVFJVRSB0byBzYXZlIGZpZ3VyZXMgaW4gc2VwYXJhdGUgZmlsZXMKc2F2ZWZpZ3MgPC0gRkFMU0UKYGBgCgojIyMjIExvYWQgcGFja2FnZXMKCmBgYHtyIH0KbGlicmFyeSgicnByb2pyb290IikKcm9vdDwtaGFzX2ZpbGUoIi5ST1MtRXhhbXBsZXMtcm9vdCIpJG1ha2VfZml4X2ZpbGUoKQpsaWJyYXJ5KCJyc3RhbmFybSIpCmxpYnJhcnkoImFybSIpCmxpYnJhcnkoImdncGxvdDIiKQpsaWJyYXJ5KCJiYXllc3Bsb3QiKQp0aGVtZV9zZXQoYmF5ZXNwbG90Ojp0aGVtZV9kZWZhdWx0KGJhc2VfZmFtaWx5ID0gInNhbnMiKSkKYGBgCgojIyMjIExvYWQgZGF0YQoKYGBge3IgfQpoaWJicyA8LSByZWFkLnRhYmxlKHJvb3QoIkVsZWN0aW9uc0Vjb25vbXkvZGF0YSIsImhpYmJzLmRhdCIpLCBoZWFkZXI9VFJVRSkKaGVhZChoaWJicykKYGBgCgojIyBHcmFwaGluZyB0aGUgYnJlYWQgYW5kIHBlYWNlIG1vZGVsCgpgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQppZiAoc2F2ZWZpZ3MpIHBkZihyb290KCJFbGVjdGlvbnNFY29ub215L2ZpZ3MiLCJoaWJic2RvdHMucGRmIiksIGhlaWdodD00LjUsIHdpZHRoPTcuNSwgY29sb3Jtb2RlbD0iZ3JheSIpCmBgYApgYGB7ciB9Cm4gPC0gbnJvdyhoaWJicykKcGFyKG1hcj1jKDAsMCwxLjIsMCkpCmxlZnQgPC0gLS4zCnJpZ2h0IDwtIC0uMjgKY2VudGVyIDwtIC0uMDcKZiA8LSAuMTcKcGxvdChjKGxlZnQtLjMxLGNlbnRlcisuMjMpLCBjKC0zLjMsbiszKSwgdHlwZT0ibiIsIGJ0eT0ibiIsIHhheHQ9Im4iLCB5YXh0PSJuIiwgeGxhYj0iIiwgeWxhYj0iIiwgeGF4cz0iaSIsIHlheHM9ImkiKQptdGV4dCgiRm9yZWNhc3RpbmcgZWxlY3Rpb25zIGZyb20gdGhlIGVjb25vbXkiLCAzLCAwLCBjZXg9MS4yKQp3aXRoKGhpYmJzLCB7CiAgZm9yIChpIGluIDE6bil7CiAgICBpaSA8LSBvcmRlcihncm93dGgpW2ldCiAgICB0ZXh0KGxlZnQtLjMsIGksIHBhc3RlIChpbmNfcGFydHlfY2FuZGlkYXRlW2lpXSwgIiB2cy4gIiwgb3RoZXJfY2FuZGlkYXRlW2lpXSwgIiAoIiwgeWVhcltpaV0sICIpIiwgc2VwPSIiKSwgYWRqPTAsIGNleD0uOCkKICAgIHBvaW50cyhjZW50ZXIrZioodm90ZVtpaV0tNTApLzEwLCBpLCBwY2g9MjApCiAgICBpZiAoaT4xKXsKICAgICAgaWYgKGZsb29yKGdyb3d0aFtpaV0pICE9IGZsb29yKGdyb3d0aFtvcmRlcihncm93dGgpW2ktMV1dKSl7CiAgICAgICAgbGluZXMoYyhsZWZ0LS4zLGNlbnRlcisuMjIpLCByZXAoaS0uNSwyKSwgbHdkPS41LCBjb2w9ImRhcmtncmF5IikKICAgICAgfQogICAgfQogIH0KfSkKbGluZXMoY2VudGVyK2YqYygtLjY1LDEuMyksIHJlcCgwLDIpLCBsd2Q9LjUpCmZvciAodGljayBpbiBzZXEoLS41LDEsLjUpKXsKICBsaW5lcyhjZW50ZXIgKyBmKnJlcCh0aWNrLDIpLCBjKDAsLS4yKSwgbHdkPS41KQogIHRleHQoY2VudGVyICsgZip0aWNrLCAtLjUsIHBhc3RlKDUwKzEwKnRpY2ssIiUiLHNlcD0iIiksIGNleD0uOCkKfQpsaW5lcyhyZXAoY2VudGVyLDIpLCBjKDAsbisuNSksIGx0eT0yLCBsd2Q9LjUpCnRleHQoY2VudGVyKy4wNSwgbisxLjUsICJJbmN1bWJlbnQgcGFydHkncyBzaGFyZSBvZiB0aGUgcG9wdWxhciB2b3RlIiwgY2V4PS44KQpsaW5lcyhjKGNlbnRlci0uMDg4LGNlbnRlcisuMTkpLCByZXAobisxLDIpLCBsd2Q9LjUpCnRleHQocmlnaHQsIG4rMS41LCAiSW5jb21lIGdyb3d0aCIsIGFkaj0uNSwgY2V4PS44KQpsaW5lcyhjKHJpZ2h0LS4wNSxyaWdodCsuMDUpLCByZXAobisxLDIpLCBsd2Q9LjUpCnRleHQocmlnaHQsIDE2LjE1LCAibW9yZSB0aGFuIDQlIiwgY2V4PS44KQp0ZXh0KHJpZ2h0LCAxNCwgIjMlIHRvIDQlIiwgY2V4PS44KQp0ZXh0KHJpZ2h0LCAxMC41LCAiMiUgdG8gMyUiLCBjZXg9LjgpCnRleHQocmlnaHQsIDcsICIxJSB0byAyJSIsIGNleD0uOCkKdGV4dChyaWdodCwgMy41LCAiMCUgdG8gMSUiLCBjZXg9LjgpCnRleHQocmlnaHQsIC44NSwgIm5lZ2F0aXZlIiwgY2V4PS44KQp0ZXh0KGxlZnQtLjMsIC0yLjMsICJBYm92ZSBtYXRjaHVwcyBhcmUgYWxsIGxpc3RlZCBhcyBpbmN1bWJlbnQgcGFydHkncyBjYW5kaWRhdGUgdnMuXCBvdGhlciBwYXJ0eSdzIGNhbmRpZGF0ZS5cbkluY29tZSBncm93dGggaXMgYSB3ZWlnaHRlZCBtZWFzdXJlIG92ZXIgdGhlIGZvdXIgeWVhcnMgcHJlY2VkaW5nIHRoZSBlbGVjdGlvbi4gIFZvdGUgc2hhcmUgZXhjbHVkZXMgdGhpcmQgcGFydGllcy4iLCBhZGo9MCwgY2V4PS43KQpgYGAKYGBge3IgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KaWYgKHNhdmVmaWdzKSBkZXYub2ZmKCkKCmBgYApgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQppZiAoc2F2ZWZpZ3MpIHBkZihyb290KCJFbGVjdGlvbnNFY29ub215L2ZpZ3MiLCJoaWJic3NjYXR0ZXIucGRmIiksIGhlaWdodD00LjUsIHdpZHRoPTUsIGNvbG9ybW9kZWw9ImdyYXkiKQpgYGAKYGBge3IgfQpwYXIobWFyPWMoMywzLDIsLjEpLCBtZ3A9YygxLjcsLjUsMCksIHRjaz0tLjAxKQpwbG90KGMoLS43LCA0LjUpLCBjKDQzLDYzKSwgdHlwZT0ibiIsIHhsYWI9IkF2ZyByZWNlbnQgZ3Jvd3RoIGluIHBlcnNvbmFsIGluY29tZSIsIHlsYWI9IkluY3VtYmVudCBwYXJ0eSdzIHZvdGUgc2hhcmUiLCB4YXh0PSJuIiwgeWF4dD0ibiIsIG1ncD1jKDIsLjUsMCksIG1haW49IkZvcmVjYXN0aW5nIHRoZSBlbGVjdGlvbiBmcm9tIHRoZSBlY29ub215ICAgICAgIiwgYnR5PSJsIikKYXhpcygxLCAwOjQsIHBhc3RlKDA6NCwiJSIsc2VwPSIiKSwgbWdwPWMoMiwuNSwwKSkKYXhpcygyLCBzZXEoNDUsNjAsNSksIHBhc3RlKHNlcSg0NSw2MCw1KSwiJSIsc2VwPSIiKSwgbWdwPWMoMiwuNSwwKSkKd2l0aChoaWJicywgdGV4dChncm93dGgsIHZvdGUsIHllYXIsIGNleD0uOCkpCmFibGluZSg1MCwgMCwgbHdkPS41LCBjb2w9ImdyYXkiKQpgYGAKYGBge3IgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KaWYgKHNhdmVmaWdzKSBkZXYub2ZmKCkKYGBgCgojIyBMaW5lYXIgcmVncmVzc2lvbgoKVGhlIG9wdGlvbiBgcmVmcmVzaCA9IDBgIHN1cHJlc3NlcyB0aGUgZGVmYXVsdCBTdGFuIHNhbXBsaW5nCnByb2dyZXNzIG91dHB1dC4gVGhpcyBpcyB1c2VmdWwgZm9yIHNtYWxsIGRhdGEgd2l0aCBmYXN0CmNvbXB1dGF0aW9uLiBGb3IgbW9yZSBjb21wbGV4IG1vZGVscyBhbmQgYmlnZ2VyIGRhdGEsIGl0IGNhbiBiZQp1c2VmdWwgdG8gc2VlIHRoZSBwcm9ncmVzcy4KCmBgYHtyIH0KTTEgPC0gc3Rhbl9nbG0odm90ZSB+IGdyb3d0aCwgZGF0YSA9IGhpYmJzLCByZWZyZXNoID0gMCkKYGBgCgpQcmludCBkZWZhdWx0IHN1bW1hcnkgb2YgdGhlIGZpdHRlZCBtb2RlbAoKYGBge3IgfQpwcmludChNMSkKYGBgCgpQcmludCBzdW1tYXJ5IG9mIHRoZSBwcmlvcnMgdXNlZAoKYGBge3IgfQpwcmlvcl9zdW1tYXJ5KE0xKQpgYGAKCkFsbW9zdCBhbGwgbW9kZWxzIGluIFJlZ3Jlc3Npb24gYW5kIE90aGVyIFN0b3JpZXMgaGF2ZSB2ZXJ5IGdvb2QKc2FtcGxpbmcgYmVoYXZpb3IuIGBzdW1tYXJ5KClgIGZ1bmN0aW9uIGNhbiBiZSB1c2VkIHRvIG9idGFpbiB0aGUKc3VtbWFyeSBvZiB0aGUgY29udmVyZ2VuY2UgZGlhZ25vc3RpY3MgZm9yIE1DTUMgc2FtcGxpbmcuCgpgYGB7ciB9CnN1bW1hcnkoTTEpCmBgYAoKIyMjIyBQb3N0ZXJpb3IgaW50ZXJ2YWwKCmBgYHtyIH0Kcm91bmQocG9zdGVyaW9yX2ludGVydmFsKE0xKSwxKQpgYGAKCiMjIyMgUGxvdCByZWdyZXNzaW9uIGxpbmUKCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CmlmIChzYXZlZmlncykgcGRmKHJvb3QoIkVsZWN0aW9uc0Vjb25vbXkvZmlncyIsImhpYmJzbGluZS5wZGYiKSwgaGVpZ2h0PTQuNSwgd2lkdGg9NSwgY29sb3Jtb2RlbD0iZ3JheSIpCmBgYApgYGB7ciB9CnBhcihtYXI9YygzLDMsMiwuMSksIG1ncD1jKDEuNywuNSwwKSwgdGNrPS0uMDEpCnBsb3QoYygtLjcsIDQuNSksIGMoNDMsNjMpLCB0eXBlPSJuIiwgeGxhYj0iQXZlcmFnZSByZWNlbnQgZ3Jvd3RoIGluIHBlcnNvbmFsIGluY29tZSIsIHlsYWI9IkluY3VtYmVudCBwYXJ0eSdzIHZvdGUgc2hhcmUiLCB4YXh0PSJuIiwgeWF4dD0ibiIsIG1ncD1jKDIsLjUsMCksIG1haW49IkRhdGEgYW5kIGxpbmVhciBmaXQiLCBidHk9ImwiKQpheGlzKDEsIDA6NCwgcGFzdGUoMDo0LCIlIixzZXA9IiIpLCBtZ3A9YygyLC41LDApKQpheGlzKDIsIHNlcSg0NSw2MCw1KSwgcGFzdGUoc2VxKDQ1LDYwLDUpLCIlIixzZXA9IiIpLCBtZ3A9YygyLC41LDApKQp3aXRoKGhpYmJzLCBwb2ludHMoZ3Jvd3RoLCB2b3RlLCBwY2g9MjApKQphYmxpbmUoNTAsIDAsIGx3ZD0uNSwgY29sPSJncmF5IikKYWJsaW5lKGNvZWYoTTEpLCBjb2w9ImdyYXkxNSIpCnRleHQoMi43LCA1My41LCBwYXN0ZSgieSA9IiwgZnJvdW5kKGNvZWYoTTEpWzFdLDEpLCAiKyIsIGZyb3VuZChjb2VmKE0xKVsyXSwxKSwgIngiKSwgYWRqPTAsIGNvbD0iZ3JheTE1IikKYGBgCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CmlmIChzYXZlZmlncykgZGV2Lm9mZigpCmBgYAoKIyMjIyBQbG90IHByZWRpY3Rpb24gZ2l2ZW4gMiUgZ3Jvd3RoCgpgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQppZiAoc2F2ZWZpZ3MpIHBkZihyb290KCJFbGVjdGlvbnNFY29ub215L2ZpZ3MiLCJoaWJic3ByZWRpY3QucGRmIiksIGhlaWdodD0zLjUsIHdpZHRoPTYuNSwgY29sb3Jtb2RlbD0iZ3JheSIpCmBgYApgYGB7ciB9CnBhcihtYXI9YygzLDMsMywxKSwgbWdwPWMoMS43LC41LDApLCB0Y2s9LS4wMSkKbXUgPC0gNTIuMwpzaWdtYSA8LSAzLjkKY3VydmUgKGRub3JtKHgsbXUsc2lnbWEpLCB5bGltPWMoMCwuMTAzKSwgZnJvbT0zNSwgdG89NzAsIGJ0eT0ibiIsCiAgeGF4dD0ibiIsIHlheHQ9Im4iLCB5YXhzPSJpIiwKICB4bGFiPSJDbGludG9uIHNoYXJlIG9mIHRoZSB0d28tcGFydHkgdm90ZSIsIHlsYWI9IiIsCiAgbWFpbj0iUHJvYmFiaWxpdHkgZm9yZWNhc3Qgb2YgSGlsbGFyeSBDbGludG9uIHZvdGUgc2hhcmUgaW4gMjAxNixcbmJhc2VkIG9uIDIlIHJhdGUgb2YgZWNvbm9taWMgZ3Jvd3RoIiwgY2V4Lm1haW49LjkpCnggPC0gc2VxICg1MCw2NSwuMSkKcG9seWdvbihjKG1pbih4KSx4LG1heCh4KSksIGMoMCxkbm9ybSh4LG11LHNpZ21hKSwwKSwKICBjb2w9ImRhcmtncmF5IiwgYm9yZGVyPSJibGFjayIpCmF4aXMoMSwgc2VxKDQwLDY1LDUpLCBwYXN0ZShzZXEoNDAsNjUsNSksIiUiLHNlcD0iIikpCnRleHQoNTAuNywgLjAyNSwgIlByZWRpY3RlZFxuNzIlIGNoYW5jZVxub2YgQ2xpbnRvbiB2aWN0b3J5IiwgYWRqPTApCmBgYApgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQppZiAoc2F2ZWZpZ3MpIGRldi5vZmYoKQpgYGAKCiMjIyMgUGxvdCBkYXRhIGFuZCBsaW5lYXIgZml0CgpgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQppZiAoc2F2ZWZpZ3MpIHBkZihyb290KCJFbGVjdGlvbnNFY29ub215L2ZpZ3MiLCJoaWJic2xpbmUyYS5wZGYiKSwgaGVpZ2h0PTQuNSwgd2lkdGg9NSwgY29sb3Jtb2RlbD0iZ3JheSIpCmBgYApgYGB7ciB9CnBhcihtYXI9YygzLDMsMiwuMSksIG1ncD1jKDEuNywuNSwwKSwgdGNrPS0uMDEpCnBsb3QoYygtLjcsIDQuNSksIGMoNDMsNjMpLCB0eXBlPSJuIiwgeGxhYj0ieCIsIHlsYWI9InkiLCB4YXh0PSJuIiwgeWF4dD0ibiIsIG1ncD1jKDIsLjUsMCksIG1haW49IkRhdGEgYW5kIGxpbmVhciBmaXQiLCBidHk9ImwiLCBjZXgubGFiPTEuMywgY2V4Lm1haW49MS4zKQpheGlzKDEsIDA6NCwgY2V4LmF4aXM9MS4zKQpheGlzKDIsIHNlcSg0NSwgNjAsIDUpLCBjZXguYXhpcz0xLjMpCmFibGluZShjb2VmKE0xKSwgY29sPSJncmF5MTUiKQp3aXRoKGhpYmJzLCBwb2ludHMoZ3Jvd3RoLCB2b3RlLCBwY2g9MjApKQp0ZXh0KDIuNywgNTMuNSwgcGFzdGUoInkgPSIsIGZyb3VuZChjb2VmKE0xKVsxXSwxKSwgIisiLCBmcm91bmQoY29lZihNMSlbMl0sMSksICJ4IiksIGFkaj0wLCBjb2w9ImdyYXkxNSIsIGNleD0xLjMpCmBgYApgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQppZiAoc2F2ZWZpZ3MpIGRldi5vZmYoKQpgYGAKCiMjIyMgUGxvdCBkYXRhIGFuZCByYW5nZSBvZiBwb3NzaWJsZSBsaW5lYXIgZml0cwoKYGBge3IgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KaWYgKHNhdmVmaWdzKSBwZGYocm9vdCgiRWxlY3Rpb25zRWNvbm9teS9maWdzIiwiaGliYnNsaW5lMmIucGRmIiksIGhlaWdodD00LjUsIHdpZHRoPTUsIGNvbG9ybW9kZWw9ImdyYXkiKQpgYGAKYGBge3IgfQpwYXIobWFyPWMoMywzLDIsLjEpLCBtZ3A9YygxLjcsLjUsMCksIHRjaz0tLjAxKQpwbG90KGMoLS43LCA0LjUpLCBjKDQzLDYzKSwgdHlwZT0ibiIsIHhsYWI9IngiLCB5bGFiPSJ5IiwgeGF4dD0ibiIsIHlheHQ9Im4iLCBtZ3A9YygyLC41LDApLCBtYWluPSJEYXRhIGFuZCByYW5nZSBvZiBwb3NzaWJsZSBsaW5lYXIgZml0cyIsIGJ0eT0ibCIsIGNleC5sYWI9MS4zLCBjZXgubWFpbj0xLjMpCmF4aXMoMSwgMDo0LCBjZXguYXhpcz0xLjMpCmF4aXMoMiwgc2VxKDQ1LCA2MCwgNSksIGNleC5heGlzPTEuMykKc2ltcyA8LSBhcy5tYXRyaXgoTTEpCm5fc2ltcyA8LSBucm93KHNpbXMpCmZvciAocyBpbiBzYW1wbGUobl9zaW1zLCA1MCkpCiAgYWJsaW5lKHNpbXNbcywxXSwgc2ltc1tzLDJdLCBjb2w9ImdyYXk1MCIsIGx3ZD0wLjUpCndpdGgoaGliYnMsIHBvaW50cyhncm93dGgsIHZvdGUsIHBjaD0yMCkpCmBgYApgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQppZiAoc2F2ZWZpZ3MpIGRldi5vZmYoKQpgYGAKCiMjIElsbHVzdHJhdGUgY29tcHV0YXRpb25zCiMjIyMgRXh0cmFjdCB0aGUgc2ltdWxhdGlvbnMKCmBgYHtyIH0Kc2ltcyA8LSBhcy5tYXRyaXgoTTEpCmEgPC0gc2ltc1ssMV0KYiA8LSBzaW1zWywyXQpzaWdtYSA8LSBzaW1zWywzXQpuX3NpbXMgPC0gbnJvdyhzaW1zKQpgYGAKCiMjIyMgTWVkaWFuIGFuZCBtZWFuIGFic29sdXRlIGRldmlhdGlvbiAoTUFEX1NEKQoKYGBge3IgfQpNZWRpYW4gPC0gYXBwbHkoc2ltcywgMiwgbWVkaWFuKQpNQURfU0QgPC0gYXBwbHkoc2ltcywgMiwgbWFkKQpwcmludChjYmluZChNZWRpYW4sIE1BRF9TRCkpCmBgYAoKIyMjIyBNZWRpYW4gYW5kIG1lYW4gYWJzb2x1dGUgZGV2aWF0aW9uIChNQURfU0QpIGZvciBhIGRlcml2ZWQgcXVhbnRpdHkgYS9iCgpgYGB7ciB9CmEgPC0gc2ltc1ssMV0KYiA8LSBzaW1zWywyXQp6IDwtIGEvYgpwcmludChtZWRpYW4oeikpCnByaW50KG1hZCh6KSkKYGBgCgojIyMjIFBvaW50IHByZWRpY3Rpb24gZ2l2ZW4gMiUgZ3Jvd3RoCgpgYGB7ciB9Cm5ldyA8LSBkYXRhLmZyYW1lKGdyb3d0aD0yLjApCnlfcG9pbnRfcHJlZCA8LSBwcmVkaWN0KE0xLCBuZXdkYXRhPW5ldykKYGBgCgojIyMjIEFsdGVybmF0aXZlIHdheSB0byBjb21wdXRlIHRoZSBwb2ludCBwcmVkaWN0aW9uCgpgYGB7ciB9CmFfaGF0IDwtIGNvZWYoTTEpWzFdCmJfaGF0IDwtIGNvZWYoTTEpWzJdCnlfcG9pbnRfcHJlZCA8LSBhX2hhdCArIGJfaGF0KmFzLm51bWVyaWMobmV3KQpgYGAKCiMjIyMgVW5jZXJ0YWludHkgaW4gcHJlZGljdGlvbiBnaXZlbiAyJSBncm93dGgKCmBgYHtyIH0KeV9saW5wcmVkIDwtIHBvc3Rlcmlvcl9saW5wcmVkKE0xLCBuZXdkYXRhPW5ldykKYGBgCgojIyMjIERvIHNhbWUgY29tcHV0YXRpb24gIm1hbnVhbGx5IgoKYGBge3IgfQphIDwtIHNpbXNbLDFdCmIgPC0gc2ltc1ssMl0KeV9saW5wcmVkIDwtIGEgKyBiKmFzLm51bWVyaWMobmV3KQpgYGAKCiMjIyMgUHJlZGljdGl2ZSB1bmNlcnRhaW50eQoKYGBge3IgfQp5X3ByZWQgPC0gcG9zdGVyaW9yX3ByZWRpY3QoTTEsIG5ld2RhdGE9bmV3KQpgYGAKCiMjIyMgUHJlZGljdGl2ZSB1bmNlcnRhaW50eSBtYW51YWxseQoKYGBge3IgfQpzaWdtYSA8LSBzaW1zWywzXQpuX3NpbXMgPC0gbnJvdyhzaW1zKQp5X3ByZWQgPC0gYSArIGIqYXMubnVtZXJpYyhuZXcpICsgcm5vcm0obl9zaW1zLCAwLCBzaWdtYSkKYGBgCgojIyMjIFN1bW1hcml6ZSBwcmVkaWN0aW9ucwoKYGBge3IgfQpNZWRpYW4gPC0gbWVkaWFuKHlfcHJlZCkKTUFEX1NEIDwtIG1hZCh5X3ByZWQpCndpbl9wcm9iIDwtIG1lYW4oeV9wcmVkID4gNTApCmNhdCgiUHJlZGljdGVkIENsaW50b24gcGVyY2VudGFnZSBvZiAyLXBhcnR5IHZvdGU6ICIsIHJvdW5kKE1lZGlhbiwxKSwKICAiLCB3aXRoIHMuZS4gIiwgcm91bmQoTUFEX1NELCAxKSwgIlxuUHIgKENsaW50b24gd2luKSA9ICIsIHJvdW5kKHdpbl9wcm9iLCAyKSwKICBzZXA9IiIpCmBgYAoKIyMjIyBTdW1tYXJpemUgcHJlZGljdGlvbnMgZ3JhcGhpY2FsbHkKCmBgYHtyIH0KaGlzdCh5X3ByZWQpCmBgYAoKIyMjIyBQcmVkaWN0IGZvciBtYW55IG5ldyB2YWx1ZXMKCmBgYHtyIH0KbmV3X2dyaWQgPC0gZGF0YS5mcmFtZShncm93dGg9c2VxKC0yLjAsIDQuMCwgMC41KSkKeV9wb2ludF9wcmVkX2dyaWQgPC0gcHJlZGljdChNMSwgbmV3ZGF0YT1uZXdfZ3JpZCkKeV9saW5wcmVkX2dyaWQgPC0gcG9zdGVyaW9yX2xpbnByZWQoTTEsIG5ld2RhdGE9bmV3X2dyaWQpCnlfcHJlZF9ncmlkIDwtIHBvc3Rlcmlvcl9wcmVkaWN0KE0xLCBuZXdkYXRhPW5ld19ncmlkKQpgYGAKCiMjIyMgUGxvdHMKCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CmlmIChzYXZlZmlncykgcGRmKHJvb3QoIkVsZWN0aW9uc0Vjb25vbXkvZmlncyIsImhpYmJzcHJlZGljdF9iYXllc18xLnBkZiIpLCBoZWlnaHQ9NCwgd2lkdGg9MTAsIGNvbG9ybW9kZWw9ImdyYXkiKQpgYGAKYGBge3IgfQpwYXIobWZyb3c9YygxLDIpLCBtYXI9YygzLDIsMywwKSwgbWdwPWMoMS41LC41LDApLCB0Y2s9LS4wMSkKaGlzdChhLCB5bGltPWMoMCwwLjI1Km5fc2ltcyksIHhsYWI9ImEiLCB5bGFiPSIiLCBtYWluPSJQb3N0ZXJpb3Igc2ltdWxhdGlvbnMgb2YgdGhlIGludGVyY2VwdCwgYSxcbmFuZCBwb3N0ZXJpb3IgbWVkaWFuICsvLSAxIGFuZCAyIHN0ZCBlcnIiLCBjZXguYXhpcz0uOSwgY2V4LmxhYj0uOSwgeWF4dD0ibiIsIGNvbD0iZ3JheTkwIikKYWJsaW5lKHY9bWVkaWFuKGEpLCBsd2Q9MikKYXJyb3dzKG1lZGlhbihhKSAtIDEuNDgzKm1lZGlhbihhYnMoYSAtIG1lZGlhbihhKSkpLCA1NTAsIG1lZGlhbihhKSArIDEuNDgzKm1lZGlhbihhYnMoYSAtIG1lZGlhbihhKSkpLCA1NTAsIGxlbmd0aD0uMSwgY29kZT0zLCBsd2Q9MikKYXJyb3dzKG1lZGlhbihhKSAtIDIqMS40ODMqbWVkaWFuKGFicyhhIC0gbWVkaWFuKGEpKSksIDI1MCwgbWVkaWFuKGEpICsgMioxLjQ4MyptZWRpYW4oYWJzKGEgLSBtZWRpYW4oYSkpKSwgMjUwLCBsZW5ndGg9LjEsIGNvZGU9MywgbHdkPTIpCmhpc3QoYiwgeWxpbT1jKDAsMC4yNypuX3NpbXMpLCB4bGFiPSJiIiwgeWxhYj0iIiwgbWFpbj0iUG9zdGVyaW9yIHNpbXVsYXRpb25zIG9mIHRoZSBzbG9wZSwgYixcbmFuZCBwb3N0ZXJpb3IgbWVkaWFuICsvLSAxIGFuZCAyIHN0ZCBlcnIiLCBjZXguYXhpcz0uOSwgY2V4LmxhYj0uOSwgeWF4dD0ibiIsIGNvbD0iZ3JheTkwIikKYWJsaW5lKHY9bWVkaWFuKGIpLCBsd2Q9MikKYXJyb3dzKG1lZGlhbihiKSAtIDEuNDgzKm1lZGlhbihhYnMoYiAtIG1lZGlhbihiKSkpLCA1NTAsIG1lZGlhbihiKSArIDEuNDgzKm1lZGlhbihhYnMoYiAtIG1lZGlhbihiKSkpLCA1NTAsIGxlbmd0aD0uMSwgY29kZT0zLCBsd2Q9MikKYXJyb3dzKG1lZGlhbihiKSAtIDIqMS40ODMqbWVkaWFuKGFicyhiIC0gbWVkaWFuKGIpKSksIDI1MCwgbWVkaWFuKGIpICsgMioxLjQ4MyptZWRpYW4oYWJzKGIgLSBtZWRpYW4oYikpKSwgMjUwLCBsZW5ndGg9LjEsIGNvZGU9MywgbHdkPTIpCmBgYApgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQppZiAoc2F2ZWZpZ3MpIGRldi5vZmYoKQoKYGBgCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CmlmIChzYXZlZmlncykgcGRmKHJvb3QoIkVsZWN0aW9uc0Vjb25vbXkvZmlncyIsImhpYmJzcHJlZGljdF9iYXllc18yYS5wZGYiKSwgaGVpZ2h0PTQuNSwgd2lkdGg9NSkKYGBgCmBgYHtyIH0KcGFyKG1hcj1jKDMsMywyLC4xKSwgbWdwPWMoMS43LC41LDApLCB0Y2s9LS4wMSkKcGxvdChhLCBiLCB4bGFiPSJhIiwgeWxhYj0iYiIsIG1haW49IlBvc3RlcmlvciBkcmF3cyBvZiB0aGUgcmVncmVzc2lvbiBjb2VmZmljaWVudHMgYSwgYiAgICAgICAgICAiLCBidHk9ImwiLCBwY2g9MjAsIGNleD0uMikKYGBgCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CmlmIChzYXZlZmlncykgZGV2Lm9mZigpCmBgYAoKIyMjIyBnZ3Bsb3QgdmVyc2lvbgoKYGBge3IgfQpnZ3Bsb3QoZGF0YS5mcmFtZShhID0gc2ltc1ssIDFdLCBiID0gc2ltc1ssIDJdKSwgYWVzKGEsIGIpKSArCiAgZ2VvbV9wb2ludChzaXplID0gMSkgKwogIGxhYnModGl0bGUgPSAiUG9zdGVyaW9yIGRyYXdzIG9mIHRoZSByZWdyZXNzaW9uIGNvZWZmaWNpZW50cyBhLCBiIikKYGBgCgojIyMjIE1vcmUgcGxvdHRpbmcKCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CmlmIChzYXZlZmlncykgcGRmKHJvb3QoIkVsZWN0aW9uc0Vjb25vbXkvZmlncyIsImhpYmJzcHJlZGljdF9iYXllc18yYi5wZGYiKSwgaGVpZ2h0PTQuNSwgd2lkdGg9NSwgY29sb3Jtb2RlbD0iZ3JheSIpCmBgYApgYGB7ciB9CnBhcihtYXI9YygzLDMsMiwuMSksIG1ncD1jKDEuNywuNSwwKSwgdGNrPS0uMDEpCnBsb3QoYygtLjcsIDQuNSksIGMoNDMsNjMpLCB0eXBlPSJuIiwgeGxhYj0iQXZlcmFnZSByZWNlbnQgZ3Jvd3RoIGluIHBlcnNvbmFsIGluY29tZSIsIHlsYWI9IkluY3VtYmVudCBwYXJ0eSdzIHZvdGUgc2hhcmUiLCB4YXh0PSJuIiwgeWF4dD0ibiIsIG1ncD1jKDIsLjUsMCksIG1haW49IkRhdGEgYW5kIDEwMCBwb3N0ZXJpb3IgZHJhd3Mgb2YgdGhlIGxpbmUsIHkgPSBhICsgYnggICAgICAgICAgICIsIGJ0eT0ibCIpCmF4aXMoMSwgMDo0LCBwYXN0ZSgwOjQsIiUiLHNlcD0iIiksIG1ncD1jKDIsLjUsMCkpCmF4aXMoMiwgc2VxKDQ1LDYwLDUpLCBwYXN0ZShzZXEoNDUsNjAsNSksIiUiLHNlcD0iIiksIG1ncD1jKDIsLjUsMCkpCmZvciAoaSBpbiAxOjEwMCl7CiAgYWJsaW5lKGFbaV0sIGJbaV0sIGx3ZD0uNSkKfQphYmxpbmUoNTAsIDAsIGx3ZD0uNSwgY29sPSJncmF5IikKd2l0aChoaWJicywgewogIHBvaW50cyhncm93dGgsIHZvdGUsIHBjaD0yMCwgY2V4PTEuNywgY29sPSJ3aGl0ZSIpCiAgcG9pbnRzKGdyb3d0aCwgdm90ZSwgcGNoPTIwKQp9KQpgYGAKYGBge3IgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KaWYgKHNhdmVmaWdzKSBkZXYub2ZmKCkKYGBgCgojIyMjIGdncGxvdCB2ZXJzaW9uCgpgYGB7ciB9CmdncGxvdChoaWJicywgYWVzKHggPSBncm93dGgsIHkgPSB2b3RlKSkgKwogIGdlb21fYWJsaW5lKAogICAgaW50ZXJjZXB0ID0gc2ltc1sxOjEwMCwgMV0sCiAgICBzbG9wZSA9IHNpbXNbMToxMDAsIDJdLAogICAgc2l6ZSA9IDAuMQogICkgKwogIGdlb21fYWJsaW5lKAogICAgaW50ZXJjZXB0ID0gbWVhbihzaW1zWywgMV0pLAogICAgc2xvcGUgPSBtZWFuKHNpbXNbLCAyXSkKICApICsKICBnZW9tX3BvaW50KGNvbG9yID0gIndoaXRlIiwgc2l6ZSA9IDMpICsKICBnZW9tX3BvaW50KGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDIpICsKICBsYWJzKAogICAgeCA9ICJBdmcgcmVjZW50IGdyb3d0aCBpbiBwZXJzb25hbCBpbmNvbWUiLAogICAgeSA9IkluY3VtYmVudCBwYXJ0eSdzIHZvdGUgc2hhcmUiLAogICAgdGl0bGUgPSAiRGF0YSBhbmQgMTAwIHBvc3RlcmlvciBkcmF3cyBvZiB0aGUgbGluZSwgeSA9IGEgKyBieCIKICApICsKICBzY2FsZV94X2NvbnRpbnVvdXMoCiAgICBsaW1pdHMgPSBjKC0uNywgNC41KSwKICAgIGJyZWFrcyA9IDA6NCwKICAgIGxhYmVscyA9IHBhc3RlKDA6NCwgIiUiLCBzZXAgPSAiIikKICApICsKICBzY2FsZV95X2NvbnRpbnVvdXMoCiAgICBsaW1pdHMgPSBjKDQzLCA2MyksCiAgICBicmVha3MgPSBzZXEoNDUsIDYwLCA1KSwKICAgIGxhYmVscyA9IHBhc3RlKHNlcSg0NSwgNjAsIDUpLCAiJSIsIHNlcCA9ICIiKQogICkKYGBgCgojIyMjIEFkZCBtb3JlIHVuY2VydGFpbnR5CgpgYGB7ciB9CnggPC0gcm5vcm0obl9zaW1zLCAyLjAsIDAuMykKeV9oYXQgPC0gYSArIGIqeAp5X3ByZWQgPC0gcm5vcm0obl9zaW1zLCB5X2hhdCwgc2lnbWEpCgpNZWRpYW4gPC0gbWVkaWFuKHlfcHJlZCkKTUFEX1NEIDwtIDEuNDgzKm1lZGlhbihhYnMoeV9wcmVkIC0gbWVkaWFuKHlfcHJlZCkpKQp3aW5fcHJvYiA8LSBtZWFuKHlfcHJlZCA+IDUwKQpjYXQoIlByZWRpY3RlZCBDbGludG9uIHBlcmNlbnRhZ2Ugb2YgMi1wYXJ0eSB2b3RlOiAiLCByb3VuZChNZWRpYW4sIDEpLCAiLAogIHdpdGggcy5lLiAiLCByb3VuZChNQURfU0QsIDEpLCAiXG5QciAoQ2xpbnRvbiB3aW4pID0gIiwgcm91bmQod2luX3Byb2IsIDIpLCBzZXA9IiIsICJcbiIpCmBgYAoKIyMjIyBNb3JlIHBsb3R0aW5nCgpgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQppZiAoc2F2ZWZpZ3MpIHBkZihyb290KCJFbGVjdGlvbnNFY29ub215L2ZpZ3MiLCJoaWJic3ByZWRpY3RfYmF5ZXNfMy5wZGYiKSwgaGVpZ2h0PTMuNSwgd2lkdGg9NikKYGBgCmBgYHtyIH0KcGFyKG1hcj1jKDMsMywzLDEpLCBtZ3A9YygxLjcsLjUsMCksIHRjaz0tLjAxKQpoaXN0KHlfcHJlZCwgYnJlYWtzPXNlcShmbG9vcihtaW4oeV9wcmVkKSksIGNlaWxpbmcobWF4KHlfcHJlZCkpLDEpLCB4bGltPWMoMzUsNzApLCB4YXh0PSJuIiwgeWF4dD0ibiIsIHlheHM9ImkiLCBidHk9Im4iLAogIHhsYWI9IkNsaW50b24gc2hhcmUgb2YgdGhlIHR3by1wYXJ0eSB2b3RlIiwgeWxhYj0iIiwKICBtYWluPSJCYXllc2lhbiBzaW11bGF0aW9ucyBvZiBIaWxsYXJ5IENsaW50b24gdm90ZSBzaGFyZSxcbmJhc2VkIG9uIDIlIHJhdGUgb2YgZWNvbm9taWMgZ3Jvd3RoIikKYXhpcygxLCBzZXEoNDAsNjUsNSksIHBhc3RlKHNlcSg0MCw2NSw1KSwiJSIsc2VwPSIiKSkKYGBgCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CmlmIChzYXZlZmlncykgZGV2Lm9mZigpCmBgYAoKIyMjIyBnZ3Bsb3QgdmVyc2lvbgoKYGBge3IgfQpxcGxvdCh5X3ByZWQsIGJpbndpZHRoID0gMSkgKwogICAgbGFicygKICAgIHggPSJDbGludG9uIHNoYXJlIG9mIHRoZSB0d28tcGFydHkgdm90ZSIsCiAgICB0aXRsZSA9ICJTaW11bGF0aW9ucyBvZiBIaWxsYXJ5IENsaW50b24gdm90ZSBzaGFyZSxcbmJhc2VkIG9uIDIlIHJhdGUgb2YgZWNvbm9taWMgZ3Jvd3RoIgogICkgKwogIHRoZW1lKGF4aXMubGluZS55ID0gZWxlbWVudF9ibGFuaygpKQpgYGAKCiMjIyMgQmF5ZXNpYW4gaW5mZXJlbmNlIGFuZCBwcmlvciBpbmZvcm1hdGlvbgoKQ29tYmluaW5nIGluZm9ybWF0aW9uIGZyb20gYSBmb3JlY2FzdCBhbmQgYSBwb2xsLgpIeXBvdGhldGljYWwgZm9yZWNhc3QgYW5kIGRhdGEuCgpgYGB7ciB9CnRoZXRhX2hhdF9wcmlvciA8LSAwLjUyNApzZV9wcmlvciA8LSAwLjA0MQoKbiA8LSA0MDAKeSA8LSAxOTAKdGhldGFfaGF0X2RhdGEgPC0geS9uCnNlX2RhdGEgPC0gc3FydCgoeS9uKSooMS15L24pL24pCgp0aGV0YV9oYXRfYmF5ZXMgPC0KICAodGhldGFfaGF0X3ByaW9yIC8gc2VfcHJpb3JeMiArIHRoZXRhX2hhdF9kYXRhIC8gc2VfZGF0YV4yKSAvCiAgKDEgLyBzZV9wcmlvcl4yICsgMSAvIHNlX2RhdGFeMikKCnNlX2JheWVzIDwtIHNxcnQoMS8oMS9zZV9wcmlvcl4yICsgMS9zZV9kYXRhXjIpKQpgYGAKCiMjIyMgUmFtcCB1cCB0aGUgZGF0YSB2YXJpYW5jZQoKYGBge3IgfQpzZV9kYXRhIDwtIC4wNzUKcHJpbnQoKHRoZXRhX2hhdF9wcmlvci9zZV9wcmlvcl4yICsgdGhldGFfaGF0X2RhdGEvc2VfZGF0YV4yKS8oMS9zZV9wcmlvcl4yICsgMS9zZV9kYXRhXjIpKQpgYGAKCiMjIENvbXBhcmlzb24gdG8gYGxtKClgCgpgYGB7ciB9Ck0xYSA8LSBsbSh2b3RlIH4gZ3Jvd3RoLCBkYXRhPWhpYmJzKQpwcmludChNMWEpCnN1bW1hcnkoTTFhKQpgYGAKCg==