Predicting presidential vote share from the economy. See Chapters 1, 7, 8, 9, and 22 in Regression and Other Stories.
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
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())
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
LS0tCnRpdGxlOiAiUmVncmVzc2lvbiBhbmQgT3RoZXIgU3RvcmllczogRWxlY3Rpb25zIEVjb25vbXkiCmF1dGhvcjogIkFuZHJldyBHZWxtYW4sIEplbm5pZmVyIEhpbGwsIEFraSBWZWh0YXJpIgpkYXRlOiAiYHIgZm9ybWF0KFN5cy5EYXRlKCkpYCIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICB0aGVtZTogcmVhZGFibGUKICAgIHRvYzogdHJ1ZQogICAgdG9jX2RlcHRoOiAyCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKLS0tClByZWRpY3RpbmcgcHJlc2lkZW50aWFsIHZvdGUgc2hhcmUgZnJvbSB0aGUgZWNvbm9teS4gU2VlIENoYXB0ZXJzCjEsIDcsIDgsIDksIGFuZCAyMiBpbiBSZWdyZXNzaW9uIGFuZCBPdGhlciBTdG9yaWVzLgoKLS0tLS0tLS0tLS0tLQoKCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQobWVzc2FnZT1GQUxTRSwgZXJyb3I9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGNvbW1lbnQ9TkEpCiMgc3dpdGNoIHRoaXMgdG8gVFJVRSB0byBzYXZlIGZpZ3VyZXMgaW4gc2VwYXJhdGUgZmlsZXMKc2F2ZWZpZ3MgPC0gRkFMU0UKYGBgCgojIyMjIExvYWQgcGFja2FnZXMKCmBgYHtyIH0KbGlicmFyeSgicnByb2pyb290IikKcm9vdDwtaGFzX2ZpbGUoIi5ST1MtRXhhbXBsZXMtcm9vdCIpJG1ha2VfZml4X2ZpbGUoKQpsaWJyYXJ5KCJyc3RhbmFybSIpCmxpYnJhcnkoImFybSIpCmxpYnJhcnkoImdncGxvdDIiKQpsaWJyYXJ5KCJiYXllc3Bsb3QiKQp0aGVtZV9zZXQoYmF5ZXNwbG90Ojp0aGVtZV9kZWZhdWx0KGJhc2VfZmFtaWx5ID0gInNhbnMiKSkKYGBgCgojIyMjIExvYWQgZGF0YQoKYGBge3IgfQpoaWJicyA8LSByZWFkLnRhYmxlKHJvb3QoIkVsZWN0aW9uc0Vjb25vbXkvZGF0YSIsImhpYmJzLmRhdCIpLCBoZWFkZXI9VFJVRSkKaGVhZChoaWJicykKYGBgCgojIyBHcmFwaGluZyB0aGUgYnJlYWQgYW5kIHBlYWNlIG1vZGVsCgpgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQppZiAoc2F2ZWZpZ3MpIHBkZihyb290KCJFbGVjdGlvbnNFY29ub215L2ZpZ3MiLCJoaWJic2RvdHMucGRmIiksIGhlaWdodD00LjUsIHdpZHRoPTcuNSwgY29sb3Jtb2RlbD0iZ3JheSIpCmBgYApgYGB7ciB9Cm4gPC0gbnJvdyhoaWJicykKcGFyKG1hcj1jKDAsMCwxLjIsMCkpCmxlZnQgPC0gLS4zCnJpZ2h0IDwtIC0uMjgKY2VudGVyIDwtIC0uMDcKZiA8LSAuMTcKcGxvdChjKGxlZnQtLjMxLGNlbnRlcisuMjMpLCBjKC0zLjMsbiszKSwgdHlwZT0ibiIsIGJ0eT0ibiIsIHhheHQ9Im4iLCB5YXh0PSJuIiwgeGxhYj0iIiwgeWxhYj0iIiwgeGF4cz0iaSIsIHlheHM9ImkiKQptdGV4dCgiRm9yZWNhc3RpbmcgZWxlY3Rpb25zIGZyb20gdGhlIGVjb25vbXkiLCAzLCAwLCBjZXg9MS4yKQp3aXRoKGhpYmJzLCB7CiAgZm9yIChpIGluIDE6bil7CiAgICBpaSA8LSBvcmRlcihncm93dGgpW2ldCiAgICB0ZXh0KGxlZnQtLjMsIGksIHBhc3RlIChpbmNfcGFydHlfY2FuZGlkYXRlW2lpXSwgIiB2cy4gIiwgb3RoZXJfY2FuZGlkYXRlW2lpXSwgIiAoIiwgeWVhcltpaV0sICIpIiwgc2VwPSIiKSwgYWRqPTAsIGNleD0uOCkKICAgIHBvaW50cyhjZW50ZXIrZioodm90ZVtpaV0tNTApLzEwLCBpLCBwY2g9MjApCiAgICBpZiAoaT4xKXsKICAgICAgaWYgKGZsb29yKGdyb3d0aFtpaV0pICE9IGZsb29yKGdyb3d0aFtvcmRlcihncm93dGgpW2ktMV1dKSl7CiAgICAgICAgbGluZXMoYyhsZWZ0LS4zLGNlbnRlcisuMjIpLCByZXAoaS0uNSwyKSwgbHdkPS41LCBjb2w9ImRhcmtncmF5IikKICAgICAgfQogICAgfQogIH0KfSkKbGluZXMoY2VudGVyK2YqYygtLjY1LDEuMyksIHJlcCgwLDIpLCBsd2Q9LjUpCmZvciAodGljayBpbiBzZXEoLS41LDEsLjUpKXsKICBsaW5lcyhjZW50ZXIgKyBmKnJlcCh0aWNrLDIpLCBjKDAsLS4yKSwgbHdkPS41KQogIHRleHQoY2VudGVyICsgZip0aWNrLCAtLjUsIHBhc3RlKDUwKzEwKnRpY2ssIiUiLHNlcD0iIiksIGNleD0uOCkKfQpsaW5lcyhyZXAoY2VudGVyLDIpLCBjKDAsbisuNSksIGx0eT0yLCBsd2Q9LjUpCnRleHQoY2VudGVyKy4wNSwgbisxLjUsICJJbmN1bWJlbnQgcGFydHkncyBzaGFyZSBvZiB0aGUgcG9wdWxhciB2b3RlIiwgY2V4PS44KQpsaW5lcyhjKGNlbnRlci0uMDg4LGNlbnRlcisuMTkpLCByZXAobisxLDIpLCBsd2Q9LjUpCnRleHQocmlnaHQsIG4rMS41LCAiSW5jb21lIGdyb3d0aCIsIGFkaj0uNSwgY2V4PS44KQpsaW5lcyhjKHJpZ2h0LS4wNSxyaWdodCsuMDUpLCByZXAobisxLDIpLCBsd2Q9LjUpCnRleHQocmlnaHQsIDE2LjE1LCAibW9yZSB0aGFuIDQlIiwgY2V4PS44KQp0ZXh0KHJpZ2h0LCAxNCwgIjMlIHRvIDQlIiwgY2V4PS44KQp0ZXh0KHJpZ2h0LCAxMC41LCAiMiUgdG8gMyUiLCBjZXg9LjgpCnRleHQocmlnaHQsIDcsICIxJSB0byAyJSIsIGNleD0uOCkKdGV4dChyaWdodCwgMy41LCAiMCUgdG8gMSUiLCBjZXg9LjgpCnRleHQocmlnaHQsIC44NSwgIm5lZ2F0aXZlIiwgY2V4PS44KQp0ZXh0KGxlZnQtLjMsIC0yLjMsICJBYm92ZSBtYXRjaHVwcyBhcmUgYWxsIGxpc3RlZCBhcyBpbmN1bWJlbnQgcGFydHkncyBjYW5kaWRhdGUgdnMuXCBvdGhlciBwYXJ0eSdzIGNhbmRpZGF0ZS5cbkluY29tZSBncm93dGggaXMgYSB3ZWlnaHRlZCBtZWFzdXJlIG92ZXIgdGhlIGZvdXIgeWVhcnMgcHJlY2VkaW5nIHRoZSBlbGVjdGlvbi4gIFZvdGUgc2hhcmUgZXhjbHVkZXMgdGhpcmQgcGFydGllcy4iLCBhZGo9MCwgY2V4PS43KQpgYGAKYGBge3IgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KaWYgKHNhdmVmaWdzKSBkZXYub2ZmKCkKCmBgYApgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQppZiAoc2F2ZWZpZ3MpIHBkZihyb290KCJFbGVjdGlvbnNFY29ub215L2ZpZ3MiLCJoaWJic3NjYXR0ZXIucGRmIiksIGhlaWdodD00LjUsIHdpZHRoPTUsIGNvbG9ybW9kZWw9ImdyYXkiKQpgYGAKYGBge3IgfQpwYXIobWFyPWMoMywzLDIsLjEpLCBtZ3A9YygxLjcsLjUsMCksIHRjaz0tLjAxKQpwbG90KGMoLS43LCA0LjUpLCBjKDQzLDYzKSwgdHlwZT0ibiIsIHhsYWI9IkF2ZyByZWNlbnQgZ3Jvd3RoIGluIHBlcnNvbmFsIGluY29tZSIsIHlsYWI9IkluY3VtYmVudCBwYXJ0eSdzIHZvdGUgc2hhcmUiLCB4YXh0PSJuIiwgeWF4dD0ibiIsIG1ncD1jKDIsLjUsMCksIG1haW49IkZvcmVjYXN0aW5nIHRoZSBlbGVjdGlvbiBmcm9tIHRoZSBlY29ub215ICAgICAgIiwgYnR5PSJsIikKYXhpcygxLCAwOjQsIHBhc3RlKDA6NCwiJSIsc2VwPSIiKSwgbWdwPWMoMiwuNSwwKSkKYXhpcygyLCBzZXEoNDUsNjAsNSksIHBhc3RlKHNlcSg0NSw2MCw1KSwiJSIsc2VwPSIiKSwgbWdwPWMoMiwuNSwwKSkKd2l0aChoaWJicywgdGV4dChncm93dGgsIHZvdGUsIHllYXIsIGNleD0uOCkpCmFibGluZSg1MCwgMCwgbHdkPS41LCBjb2w9ImdyYXkiKQpgYGAKYGBge3IgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KaWYgKHNhdmVmaWdzKSBkZXYub2ZmKCkKYGBgCgojIyBMaW5lYXIgcmVncmVzc2lvbgoKVGhlIG9wdGlvbiBgcmVmcmVzaCA9IDBgIHN1cHJlc3NlcyB0aGUgZGVmYXVsdCBTdGFuIHNhbXBsaW5nCnByb2dyZXNzIG91dHB1dC4gVGhpcyBpcyB1c2VmdWwgZm9yIHNtYWxsIGRhdGEgd2l0aCBmYXN0CmNvbXB1dGF0aW9uLiBGb3IgbW9yZSBjb21wbGV4IG1vZGVscyBhbmQgYmlnZ2VyIGRhdGEsIGl0IGNhbiBiZQp1c2VmdWwgdG8gc2VlIHRoZSBwcm9ncmVzcy4KCmBgYHtyIH0KTTEgPC0gc3Rhbl9nbG0odm90ZSB+IGdyb3d0aCwgZGF0YSA9IGhpYmJzLCByZWZyZXNoID0gMCkKYGBgCgpQcmludCBkZWZhdWx0IHN1bW1hcnkgb2YgdGhlIGZpdHRlZCBtb2RlbAoKYGBge3IgfQpwcmludChNMSkKYGBgCgpQcmludCBzdW1tYXJ5IG9mIHRoZSBwcmlvcnMgdXNlZAoKYGBge3IgfQpwcmlvcl9zdW1tYXJ5KE0xKQpgYGAKCkFsbW9zdCBhbGwgbW9kZWxzIGluIFJlZ3Jlc3Npb24gYW5kIE90aGVyIFN0b3JpZXMgaGF2ZSB2ZXJ5IGdvb2QKc2FtcGxpbmcgYmVoYXZpb3IuIGBzdW1tYXJ5KClgIGZ1bmN0aW9uIGNhbiBiZSB1c2VkIHRvIG9idGFpbiB0aGUKc3VtbWFyeSBvZiB0aGUgY29udmVyZ2VuY2UgZGlhZ25vc3RpY3MgZm9yIE1DTUMgc2FtcGxpbmcuCgpgYGB7ciB9CnN1bW1hcnkoTTEpCmBgYAoKIyMjIyBQb3N0ZXJpb3IgaW50ZXJ2YWwKCmBgYHtyIH0Kcm91bmQocG9zdGVyaW9yX2ludGVydmFsKE0xKSwxKQpgYGAKCiMjIyMgUGxvdCByZWdyZXNzaW9uIGxpbmUKCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CmlmIChzYXZlZmlncykgcGRmKHJvb3QoIkVsZWN0aW9uc0Vjb25vbXkvZmlncyIsImhpYmJzbGluZS5wZGYiKSwgaGVpZ2h0PTQuNSwgd2lkdGg9NSwgY29sb3Jtb2RlbD0iZ3JheSIpCmBgYApgYGB7ciB9CnBhcihtYXI9YygzLDMsMiwuMSksIG1ncD1jKDEuNywuNSwwKSwgdGNrPS0uMDEpCnBsb3QoYygtLjcsIDQuNSksIGMoNDMsNjMpLCB0eXBlPSJuIiwgeGxhYj0iQXZlcmFnZSByZWNlbnQgZ3Jvd3RoIGluIHBlcnNvbmFsIGluY29tZSIsIHlsYWI9IkluY3VtYmVudCBwYXJ0eSdzIHZvdGUgc2hhcmUiLCB4YXh0PSJuIiwgeWF4dD0ibiIsIG1ncD1jKDIsLjUsMCksIG1haW49IkRhdGEgYW5kIGxpbmVhciBmaXQiLCBidHk9ImwiKQpheGlzKDEsIDA6NCwgcGFzdGUoMDo0LCIlIixzZXA9IiIpLCBtZ3A9YygyLC41LDApKQpheGlzKDIsIHNlcSg0NSw2MCw1KSwgcGFzdGUoc2VxKDQ1LDYwLDUpLCIlIixzZXA9IiIpLCBtZ3A9YygyLC41LDApKQp3aXRoKGhpYmJzLCBwb2ludHMoZ3Jvd3RoLCB2b3RlLCBwY2g9MjApKQphYmxpbmUoNTAsIDAsIGx3ZD0uNSwgY29sPSJncmF5IikKYWJsaW5lKGNvZWYoTTEpLCBjb2w9ImdyYXkxNSIpCnRleHQoMi43LCA1My41LCBwYXN0ZSgieSA9IiwgZnJvdW5kKGNvZWYoTTEpWzFdLDEpLCAiKyIsIGZyb3VuZChjb2VmKE0xKVsyXSwxKSwgIngiKSwgYWRqPTAsIGNvbD0iZ3JheTE1IikKYGBgCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CmlmIChzYXZlZmlncykgZGV2Lm9mZigpCmBgYAoKIyMjIyBQbG90IHByZWRpY3Rpb24gZ2l2ZW4gMiUgZ3Jvd3RoCgpgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQppZiAoc2F2ZWZpZ3MpIHBkZihyb290KCJFbGVjdGlvbnNFY29ub215L2ZpZ3MiLCJoaWJic3ByZWRpY3QucGRmIiksIGhlaWdodD0zLjUsIHdpZHRoPTYuNSwgY29sb3Jtb2RlbD0iZ3JheSIpCmBgYApgYGB7ciB9CnBhcihtYXI9YygzLDMsMywxKSwgbWdwPWMoMS43LC41LDApLCB0Y2s9LS4wMSkKbXUgPC0gNTIuMwpzaWdtYSA8LSAzLjkKY3VydmUgKGRub3JtKHgsbXUsc2lnbWEpLCB5bGltPWMoMCwuMTAzKSwgZnJvbT0zNSwgdG89NzAsIGJ0eT0ibiIsCiAgeGF4dD0ibiIsIHlheHQ9Im4iLCB5YXhzPSJpIiwKICB4bGFiPSJDbGludG9uIHNoYXJlIG9mIHRoZSB0d28tcGFydHkgdm90ZSIsIHlsYWI9IiIsCiAgbWFpbj0iUHJvYmFiaWxpdHkgZm9yZWNhc3Qgb2YgSGlsbGFyeSBDbGludG9uIHZvdGUgc2hhcmUgaW4gMjAxNixcbmJhc2VkIG9uIDIlIHJhdGUgb2YgZWNvbm9taWMgZ3Jvd3RoIiwgY2V4Lm1haW49LjkpCnggPC0gc2VxICg1MCw2NSwuMSkKcG9seWdvbihjKG1pbih4KSx4LG1heCh4KSksIGMoMCxkbm9ybSh4LG11LHNpZ21hKSwwKSwKICBjb2w9ImRhcmtncmF5IiwgYm9yZGVyPSJibGFjayIpCmF4aXMoMSwgc2VxKDQwLDY1LDUpLCBwYXN0ZShzZXEoNDAsNjUsNSksIiUiLHNlcD0iIikpCnRleHQoNTAuNywgLjAyNSwgIlByZWRpY3RlZFxuNzIlIGNoYW5jZVxub2YgQ2xpbnRvbiB2aWN0b3J5IiwgYWRqPTApCmBgYApgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQppZiAoc2F2ZWZpZ3MpIGRldi5vZmYoKQpgYGAKCiMjIyMgUGxvdCBkYXRhIGFuZCBsaW5lYXIgZml0CgpgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQppZiAoc2F2ZWZpZ3MpIHBkZihyb290KCJFbGVjdGlvbnNFY29ub215L2ZpZ3MiLCJoaWJic2xpbmUyYS5wZGYiKSwgaGVpZ2h0PTQuNSwgd2lkdGg9NSwgY29sb3Jtb2RlbD0iZ3JheSIpCmBgYApgYGB7ciB9CnBhcihtYXI9YygzLDMsMiwuMSksIG1ncD1jKDEuNywuNSwwKSwgdGNrPS0uMDEpCnBsb3QoYygtLjcsIDQuNSksIGMoNDMsNjMpLCB0eXBlPSJuIiwgeGxhYj0ieCIsIHlsYWI9InkiLCB4YXh0PSJuIiwgeWF4dD0ibiIsIG1ncD1jKDIsLjUsMCksIG1haW49IkRhdGEgYW5kIGxpbmVhciBmaXQiLCBidHk9ImwiLCBjZXgubGFiPTEuMywgY2V4Lm1haW49MS4zKQpheGlzKDEsIDA6NCwgY2V4LmF4aXM9MS4zKQpheGlzKDIsIHNlcSg0NSwgNjAsIDUpLCBjZXguYXhpcz0xLjMpCmFibGluZShjb2VmKE0xKSwgY29sPSJncmF5MTUiKQp3aXRoKGhpYmJzLCBwb2ludHMoZ3Jvd3RoLCB2b3RlLCBwY2g9MjApKQp0ZXh0KDIuNywgNTMuNSwgcGFzdGUoInkgPSIsIGZyb3VuZChjb2VmKE0xKVsxXSwxKSwgIisiLCBmcm91bmQoY29lZihNMSlbMl0sMSksICJ4IiksIGFkaj0wLCBjb2w9ImdyYXkxNSIsIGNleD0xLjMpCmBgYApgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQppZiAoc2F2ZWZpZ3MpIGRldi5vZmYoKQpgYGAKCiMjIyMgUGxvdCBkYXRhIGFuZCByYW5nZSBvZiBwb3NzaWJsZSBsaW5lYXIgZml0cwoKYGBge3IgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KaWYgKHNhdmVmaWdzKSBwZGYocm9vdCgiRWxlY3Rpb25zRWNvbm9teS9maWdzIiwiaGliYnNsaW5lMmIucGRmIiksIGhlaWdodD00LjUsIHdpZHRoPTUsIGNvbG9ybW9kZWw9ImdyYXkiKQpgYGAKYGBge3IgfQpwYXIobWFyPWMoMywzLDIsLjEpLCBtZ3A9YygxLjcsLjUsMCksIHRjaz0tLjAxKQpwbG90KGMoLS43LCA0LjUpLCBjKDQzLDYzKSwgdHlwZT0ibiIsIHhsYWI9IngiLCB5bGFiPSJ5IiwgeGF4dD0ibiIsIHlheHQ9Im4iLCBtZ3A9YygyLC41LDApLCBtYWluPSJEYXRhIGFuZCByYW5nZSBvZiBwb3NzaWJsZSBsaW5lYXIgZml0cyIsIGJ0eT0ibCIsIGNleC5sYWI9MS4zLCBjZXgubWFpbj0xLjMpCmF4aXMoMSwgMDo0LCBjZXguYXhpcz0xLjMpCmF4aXMoMiwgc2VxKDQ1LCA2MCwgNSksIGNleC5heGlzPTEuMykKc2ltcyA8LSBhcy5tYXRyaXgoTTEpCm5fc2ltcyA8LSBucm93KHNpbXMpCmZvciAocyBpbiBzYW1wbGUobl9zaW1zLCA1MCkpCiAgYWJsaW5lKHNpbXNbcywxXSwgc2ltc1tzLDJdLCBjb2w9ImdyYXk1MCIsIGx3ZD0wLjUpCndpdGgoaGliYnMsIHBvaW50cyhncm93dGgsIHZvdGUsIHBjaD0yMCkpCmBgYApgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQppZiAoc2F2ZWZpZ3MpIGRldi5vZmYoKQpgYGAKCiMjIElsbHVzdHJhdGUgY29tcHV0YXRpb25zCiMjIyMgRXh0cmFjdCB0aGUgc2ltdWxhdGlvbnMKCmBgYHtyIH0Kc2ltcyA8LSBhcy5tYXRyaXgoTTEpCmEgPC0gc2ltc1ssMV0KYiA8LSBzaW1zWywyXQpzaWdtYSA8LSBzaW1zWywzXQpuX3NpbXMgPC0gbnJvdyhzaW1zKQpgYGAKCiMjIyMgTWVkaWFuIGFuZCBtZWFuIGFic29sdXRlIGRldmlhdGlvbiAoTUFEX1NEKQoKYGBge3IgfQpNZWRpYW4gPC0gYXBwbHkoc2ltcywgMiwgbWVkaWFuKQpNQURfU0QgPC0gYXBwbHkoc2ltcywgMiwgbWFkKQpwcmludChjYmluZChNZWRpYW4sIE1BRF9TRCkpCmBgYAoKIyMjIyBNZWRpYW4gYW5kIG1lYW4gYWJzb2x1dGUgZGV2aWF0aW9uIChNQURfU0QpIGZvciBhIGRlcml2ZWQgcXVhbnRpdHkgYS9iCgpgYGB7ciB9CmEgPC0gc2ltc1ssMV0KYiA8LSBzaW1zWywyXQp6IDwtIGEvYgpwcmludChtZWRpYW4oeikpCnByaW50KG1hZCh6KSkKYGBgCgojIyMjIFBvaW50IHByZWRpY3Rpb24gZ2l2ZW4gMiUgZ3Jvd3RoCgpgYGB7ciB9Cm5ldyA8LSBkYXRhLmZyYW1lKGdyb3d0aD0yLjApCnlfcG9pbnRfcHJlZCA8LSBwcmVkaWN0KE0xLCBuZXdkYXRhPW5ldykKYGBgCgojIyMjIEFsdGVybmF0aXZlIHdheSB0byBjb21wdXRlIHRoZSBwb2ludCBwcmVkaWN0aW9uCgpgYGB7ciB9CmFfaGF0IDwtIGNvZWYoTTEpWzFdCmJfaGF0IDwtIGNvZWYoTTEpWzJdCnlfcG9pbnRfcHJlZCA8LSBhX2hhdCArIGJfaGF0KmFzLm51bWVyaWMobmV3KQpgYGAKCiMjIyMgVW5jZXJ0YWludHkgaW4gcHJlZGljdGlvbiBnaXZlbiAyJSBncm93dGgKCmBgYHtyIH0KeV9saW5wcmVkIDwtIHBvc3Rlcmlvcl9saW5wcmVkKE0xLCBuZXdkYXRhPW5ldykKYGBgCgojIyMjIERvIHNhbWUgY29tcHV0YXRpb24gIm1hbnVhbGx5IgoKYGBge3IgfQphIDwtIHNpbXNbLDFdCmIgPC0gc2ltc1ssMl0KeV9saW5wcmVkIDwtIGEgKyBiKmFzLm51bWVyaWMobmV3KQpgYGAKCiMjIyMgUHJlZGljdGl2ZSB1bmNlcnRhaW50eQoKYGBge3IgfQp5X3ByZWQgPC0gcG9zdGVyaW9yX3ByZWRpY3QoTTEsIG5ld2RhdGE9bmV3KQpgYGAKCiMjIyMgUHJlZGljdGl2ZSB1bmNlcnRhaW50eSBtYW51YWxseQoKYGBge3IgfQpzaWdtYSA8LSBzaW1zWywzXQpuX3NpbXMgPC0gbnJvdyhzaW1zKQp5X3ByZWQgPC0gYSArIGIqYXMubnVtZXJpYyhuZXcpICsgcm5vcm0obl9zaW1zLCAwLCBzaWdtYSkKYGBgCgojIyMjIFN1bW1hcml6ZSBwcmVkaWN0aW9ucwoKYGBge3IgfQpNZWRpYW4gPC0gbWVkaWFuKHlfcHJlZCkKTUFEX1NEIDwtIG1hZCh5X3ByZWQpCndpbl9wcm9iIDwtIG1lYW4oeV9wcmVkID4gNTApCmNhdCgiUHJlZGljdGVkIENsaW50b24gcGVyY2VudGFnZSBvZiAyLXBhcnR5IHZvdGU6ICIsIHJvdW5kKE1lZGlhbiwxKSwKICAiLCB3aXRoIHMuZS4gIiwgcm91bmQoTUFEX1NELCAxKSwgIlxuUHIgKENsaW50b24gd2luKSA9ICIsIHJvdW5kKHdpbl9wcm9iLCAyKSwKICBzZXA9IiIpCmBgYAoKIyMjIyBTdW1tYXJpemUgcHJlZGljdGlvbnMgZ3JhcGhpY2FsbHkKCmBgYHtyIH0KaGlzdCh5X3ByZWQpCmBgYAoKIyMjIyBQcmVkaWN0IGZvciBtYW55IG5ldyB2YWx1ZXMKCmBgYHtyIH0KbmV3X2dyaWQgPC0gZGF0YS5mcmFtZShncm93dGg9c2VxKC0yLjAsIDQuMCwgMC41KSkKeV9wb2ludF9wcmVkX2dyaWQgPC0gcHJlZGljdChNMSwgbmV3ZGF0YT1uZXdfZ3JpZCkKeV9saW5wcmVkX2dyaWQgPC0gcG9zdGVyaW9yX2xpbnByZWQoTTEsIG5ld2RhdGE9bmV3X2dyaWQpCnlfcHJlZF9ncmlkIDwtIHBvc3Rlcmlvcl9wcmVkaWN0KE0xLCBuZXdkYXRhPW5ld19ncmlkKQpgYGAKCiMjIyMgUGxvdHMKCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CmlmIChzYXZlZmlncykgcGRmKHJvb3QoIkVsZWN0aW9uc0Vjb25vbXkvZmlncyIsImhpYmJzcHJlZGljdF9iYXllc18xLnBkZiIpLCBoZWlnaHQ9NCwgd2lkdGg9MTAsIGNvbG9ybW9kZWw9ImdyYXkiKQpgYGAKYGBge3IgfQpwYXIobWZyb3c9YygxLDIpLCBtYXI9YygzLDIsMywwKSwgbWdwPWMoMS41LC41LDApLCB0Y2s9LS4wMSkKaGlzdChhLCB5bGltPWMoMCwwLjI1Km5fc2ltcyksIHhsYWI9ImEiLCB5bGFiPSIiLCBtYWluPSJQb3N0ZXJpb3Igc2ltdWxhdGlvbnMgb2YgdGhlIGludGVyY2VwdCwgYSxcbmFuZCBwb3N0ZXJpb3IgbWVkaWFuICsvLSAxIGFuZCAyIHN0ZCBlcnIiLCBjZXguYXhpcz0uOSwgY2V4LmxhYj0uOSwgeWF4dD0ibiIsIGNvbD0iZ3JheTkwIikKYWJsaW5lKHY9bWVkaWFuKGEpLCBsd2Q9MikKYXJyb3dzKG1lZGlhbihhKSAtIDEuNDgzKm1lZGlhbihhYnMoYSAtIG1lZGlhbihhKSkpLCA1NTAsIG1lZGlhbihhKSArIDEuNDgzKm1lZGlhbihhYnMoYSAtIG1lZGlhbihhKSkpLCA1NTAsIGxlbmd0aD0uMSwgY29kZT0zLCBsd2Q9MikKYXJyb3dzKG1lZGlhbihhKSAtIDIqMS40ODMqbWVkaWFuKGFicyhhIC0gbWVkaWFuKGEpKSksIDI1MCwgbWVkaWFuKGEpICsgMioxLjQ4MyptZWRpYW4oYWJzKGEgLSBtZWRpYW4oYSkpKSwgMjUwLCBsZW5ndGg9LjEsIGNvZGU9MywgbHdkPTIpCmhpc3QoYiwgeWxpbT1jKDAsMC4yNypuX3NpbXMpLCB4bGFiPSJiIiwgeWxhYj0iIiwgbWFpbj0iUG9zdGVyaW9yIHNpbXVsYXRpb25zIG9mIHRoZSBzbG9wZSwgYixcbmFuZCBwb3N0ZXJpb3IgbWVkaWFuICsvLSAxIGFuZCAyIHN0ZCBlcnIiLCBjZXguYXhpcz0uOSwgY2V4LmxhYj0uOSwgeWF4dD0ibiIsIGNvbD0iZ3JheTkwIikKYWJsaW5lKHY9bWVkaWFuKGIpLCBsd2Q9MikKYXJyb3dzKG1lZGlhbihiKSAtIDEuNDgzKm1lZGlhbihhYnMoYiAtIG1lZGlhbihiKSkpLCA1NTAsIG1lZGlhbihiKSArIDEuNDgzKm1lZGlhbihhYnMoYiAtIG1lZGlhbihiKSkpLCA1NTAsIGxlbmd0aD0uMSwgY29kZT0zLCBsd2Q9MikKYXJyb3dzKG1lZGlhbihiKSAtIDIqMS40ODMqbWVkaWFuKGFicyhiIC0gbWVkaWFuKGIpKSksIDI1MCwgbWVkaWFuKGIpICsgMioxLjQ4MyptZWRpYW4oYWJzKGIgLSBtZWRpYW4oYikpKSwgMjUwLCBsZW5ndGg9LjEsIGNvZGU9MywgbHdkPTIpCmBgYApgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQppZiAoc2F2ZWZpZ3MpIGRldi5vZmYoKQoKYGBgCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CmlmIChzYXZlZmlncykgcGRmKHJvb3QoIkVsZWN0aW9uc0Vjb25vbXkvZmlncyIsImhpYmJzcHJlZGljdF9iYXllc18yYS5wZGYiKSwgaGVpZ2h0PTQuNSwgd2lkdGg9NSkKYGBgCmBgYHtyIH0KcGFyKG1hcj1jKDMsMywyLC4xKSwgbWdwPWMoMS43LC41LDApLCB0Y2s9LS4wMSkKcGxvdChhLCBiLCB4bGFiPSJhIiwgeWxhYj0iYiIsIG1haW49IlBvc3RlcmlvciBkcmF3cyBvZiB0aGUgcmVncmVzc2lvbiBjb2VmZmljaWVudHMgYSwgYiAgICAgICAgICAiLCBidHk9ImwiLCBwY2g9MjAsIGNleD0uMikKYGBgCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CmlmIChzYXZlZmlncykgZGV2Lm9mZigpCmBgYAoKIyMjIyBnZ3Bsb3QgdmVyc2lvbgoKYGBge3IgfQpnZ3Bsb3QoZGF0YS5mcmFtZShhID0gc2ltc1ssIDFdLCBiID0gc2ltc1ssIDJdKSwgYWVzKGEsIGIpKSArCiAgZ2VvbV9wb2ludChzaXplID0gMSkgKwogIGxhYnModGl0bGUgPSAiUG9zdGVyaW9yIGRyYXdzIG9mIHRoZSByZWdyZXNzaW9uIGNvZWZmaWNpZW50cyBhLCBiIikKYGBgCgojIyMjIE1vcmUgcGxvdHRpbmcKCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CmlmIChzYXZlZmlncykgcGRmKHJvb3QoIkVsZWN0aW9uc0Vjb25vbXkvZmlncyIsImhpYmJzcHJlZGljdF9iYXllc18yYi5wZGYiKSwgaGVpZ2h0PTQuNSwgd2lkdGg9NSwgY29sb3Jtb2RlbD0iZ3JheSIpCmBgYApgYGB7ciB9CnBhcihtYXI9YygzLDMsMiwuMSksIG1ncD1jKDEuNywuNSwwKSwgdGNrPS0uMDEpCnBsb3QoYygtLjcsIDQuNSksIGMoNDMsNjMpLCB0eXBlPSJuIiwgeGxhYj0iQXZlcmFnZSByZWNlbnQgZ3Jvd3RoIGluIHBlcnNvbmFsIGluY29tZSIsIHlsYWI9IkluY3VtYmVudCBwYXJ0eSdzIHZvdGUgc2hhcmUiLCB4YXh0PSJuIiwgeWF4dD0ibiIsIG1ncD1jKDIsLjUsMCksIG1haW49IkRhdGEgYW5kIDEwMCBwb3N0ZXJpb3IgZHJhd3Mgb2YgdGhlIGxpbmUsIHkgPSBhICsgYnggICAgICAgICAgICIsIGJ0eT0ibCIpCmF4aXMoMSwgMDo0LCBwYXN0ZSgwOjQsIiUiLHNlcD0iIiksIG1ncD1jKDIsLjUsMCkpCmF4aXMoMiwgc2VxKDQ1LDYwLDUpLCBwYXN0ZShzZXEoNDUsNjAsNSksIiUiLHNlcD0iIiksIG1ncD1jKDIsLjUsMCkpCmZvciAoaSBpbiAxOjEwMCl7CiAgYWJsaW5lKGFbaV0sIGJbaV0sIGx3ZD0uNSkKfQphYmxpbmUoNTAsIDAsIGx3ZD0uNSwgY29sPSJncmF5IikKd2l0aChoaWJicywgewogIHBvaW50cyhncm93dGgsIHZvdGUsIHBjaD0yMCwgY2V4PTEuNywgY29sPSJ3aGl0ZSIpCiAgcG9pbnRzKGdyb3d0aCwgdm90ZSwgcGNoPTIwKQp9KQpgYGAKYGBge3IgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KaWYgKHNhdmVmaWdzKSBkZXYub2ZmKCkKYGBgCgojIyMjIGdncGxvdCB2ZXJzaW9uCgpgYGB7ciB9CmdncGxvdChoaWJicywgYWVzKHggPSBncm93dGgsIHkgPSB2b3RlKSkgKwogIGdlb21fYWJsaW5lKAogICAgaW50ZXJjZXB0ID0gc2ltc1sxOjEwMCwgMV0sCiAgICBzbG9wZSA9IHNpbXNbMToxMDAsIDJdLAogICAgc2l6ZSA9IDAuMQogICkgKwogIGdlb21fYWJsaW5lKAogICAgaW50ZXJjZXB0ID0gbWVhbihzaW1zWywgMV0pLAogICAgc2xvcGUgPSBtZWFuKHNpbXNbLCAyXSkKICApICsKICBnZW9tX3BvaW50KGNvbG9yID0gIndoaXRlIiwgc2l6ZSA9IDMpICsKICBnZW9tX3BvaW50KGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDIpICsKICBsYWJzKAogICAgeCA9ICJBdmcgcmVjZW50IGdyb3d0aCBpbiBwZXJzb25hbCBpbmNvbWUiLAogICAgeSA9IkluY3VtYmVudCBwYXJ0eSdzIHZvdGUgc2hhcmUiLAogICAgdGl0bGUgPSAiRGF0YSBhbmQgMTAwIHBvc3RlcmlvciBkcmF3cyBvZiB0aGUgbGluZSwgeSA9IGEgKyBieCIKICApICsKICBzY2FsZV94X2NvbnRpbnVvdXMoCiAgICBsaW1pdHMgPSBjKC0uNywgNC41KSwKICAgIGJyZWFrcyA9IDA6NCwKICAgIGxhYmVscyA9IHBhc3RlKDA6NCwgIiUiLCBzZXAgPSAiIikKICApICsKICBzY2FsZV95X2NvbnRpbnVvdXMoCiAgICBsaW1pdHMgPSBjKDQzLCA2MyksCiAgICBicmVha3MgPSBzZXEoNDUsIDYwLCA1KSwKICAgIGxhYmVscyA9IHBhc3RlKHNlcSg0NSwgNjAsIDUpLCAiJSIsIHNlcCA9ICIiKQogICkKYGBgCgojIyMjIEFkZCBtb3JlIHVuY2VydGFpbnR5CgpgYGB7ciB9CnggPC0gcm5vcm0obl9zaW1zLCAyLjAsIDAuMykKeV9oYXQgPC0gYSArIGIqeAp5X3ByZWQgPC0gcm5vcm0obl9zaW1zLCB5X2hhdCwgc2lnbWEpCgpNZWRpYW4gPC0gbWVkaWFuKHlfcHJlZCkKTUFEX1NEIDwtIDEuNDgzKm1lZGlhbihhYnMoeV9wcmVkIC0gbWVkaWFuKHlfcHJlZCkpKQp3aW5fcHJvYiA8LSBtZWFuKHlfcHJlZCA+IDUwKQpjYXQoIlByZWRpY3RlZCBDbGludG9uIHBlcmNlbnRhZ2Ugb2YgMi1wYXJ0eSB2b3RlOiAiLCByb3VuZChNZWRpYW4sIDEpLCAiLAogIHdpdGggcy5lLiAiLCByb3VuZChNQURfU0QsIDEpLCAiXG5QciAoQ2xpbnRvbiB3aW4pID0gIiwgcm91bmQod2luX3Byb2IsIDIpLCBzZXA9IiIsICJcbiIpCmBgYAoKIyMjIyBNb3JlIHBsb3R0aW5nCgpgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQppZiAoc2F2ZWZpZ3MpIHBkZihyb290KCJFbGVjdGlvbnNFY29ub215L2ZpZ3MiLCJoaWJic3ByZWRpY3RfYmF5ZXNfMy5wZGYiKSwgaGVpZ2h0PTMuNSwgd2lkdGg9NikKYGBgCmBgYHtyIH0KcGFyKG1hcj1jKDMsMywzLDEpLCBtZ3A9YygxLjcsLjUsMCksIHRjaz0tLjAxKQpoaXN0KHlfcHJlZCwgYnJlYWtzPXNlcShmbG9vcihtaW4oeV9wcmVkKSksIGNlaWxpbmcobWF4KHlfcHJlZCkpLDEpLCB4bGltPWMoMzUsNzApLCB4YXh0PSJuIiwgeWF4dD0ibiIsIHlheHM9ImkiLCBidHk9Im4iLAogIHhsYWI9IkNsaW50b24gc2hhcmUgb2YgdGhlIHR3by1wYXJ0eSB2b3RlIiwgeWxhYj0iIiwKICBtYWluPSJCYXllc2lhbiBzaW11bGF0aW9ucyBvZiBIaWxsYXJ5IENsaW50b24gdm90ZSBzaGFyZSxcbmJhc2VkIG9uIDIlIHJhdGUgb2YgZWNvbm9taWMgZ3Jvd3RoIikKYXhpcygxLCBzZXEoNDAsNjUsNSksIHBhc3RlKHNlcSg0MCw2NSw1KSwiJSIsc2VwPSIiKSkKYGBgCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CmlmIChzYXZlZmlncykgZGV2Lm9mZigpCmBgYAoKIyMjIyBnZ3Bsb3QgdmVyc2lvbgoKYGBge3IgfQpxcGxvdCh5X3ByZWQsIGJpbndpZHRoID0gMSkgKwogICAgbGFicygKICAgIHggPSJDbGludG9uIHNoYXJlIG9mIHRoZSB0d28tcGFydHkgdm90ZSIsCiAgICB0aXRsZSA9ICJTaW11bGF0aW9ucyBvZiBIaWxsYXJ5IENsaW50b24gdm90ZSBzaGFyZSxcbmJhc2VkIG9uIDIlIHJhdGUgb2YgZWNvbm9taWMgZ3Jvd3RoIgogICkgKwogIHRoZW1lKGF4aXMubGluZS55ID0gZWxlbWVudF9ibGFuaygpKQpgYGAKCiMjIyMgQmF5ZXNpYW4gaW5mZXJlbmNlIGFuZCBwcmlvciBpbmZvcm1hdGlvbgoKQ29tYmluaW5nIGluZm9ybWF0aW9uIGZyb20gYSBmb3JlY2FzdCBhbmQgYSBwb2xsLgpIeXBvdGhldGljYWwgZm9yZWNhc3QgYW5kIGRhdGEuCgpgYGB7ciB9CnRoZXRhX2hhdF9wcmlvciA8LSAwLjUyNApzZV9wcmlvciA8LSAwLjA0MQoKbiA8LSA0MDAKeSA8LSAxOTAKdGhldGFfaGF0X2RhdGEgPC0geS9uCnNlX2RhdGEgPC0gc3FydCgoeS9uKSooMS15L24pL24pCgp0aGV0YV9oYXRfYmF5ZXMgPC0KICAodGhldGFfaGF0X3ByaW9yIC8gc2VfcHJpb3JeMiArIHRoZXRhX2hhdF9kYXRhIC8gc2VfZGF0YV4yKSAvCiAgKDEgLyBzZV9wcmlvcl4yICsgMSAvIHNlX2RhdGFeMikKCnNlX2JheWVzIDwtIHNxcnQoMS8oMS9zZV9wcmlvcl4yICsgMS9zZV9kYXRhXjIpKQpgYGAKCiMjIyMgUmFtcCB1cCB0aGUgZGF0YSB2YXJpYW5jZQoKYGBge3IgfQpzZV9kYXRhIDwtIC4wNzUKcHJpbnQoKHRoZXRhX2hhdF9wcmlvci9zZV9wcmlvcl4yICsgdGhldGFfaGF0X2RhdGEvc2VfZGF0YV4yKS8oMS9zZV9wcmlvcl4yICsgMS9zZV9kYXRhXjIpKQpgYGAKCiMjIENvbXBhcmlzb24gdG8gYGxtKClgCgpgYGB7ciB9Ck0xYSA8LSBsbSh2b3RlIH4gZ3Jvd3RoLCBkYXRhPWhpYmJzKQpwcmludChNMWEpCnN1bW1hcnkoTTFhKQpgYGAKCg==