Predict respondents' yearly earnings using survey data from 1990. See Chapters 6, 9 and 12 in Regression and Other Stories.
Normal linear regression
Predict earnings in dollars
fit_0 <- stan_glm(earn ~ height, data=earnings,
seed = SEED, refresh = 0)
print(fit_0)
stan_glm
family: gaussian [identity]
formula: earn ~ height
observations: 1816
predictors: 2
------
Median MAD_SD
(Intercept) -85061.4 8786.9
height 1594.5 131.0
Auxiliary parameter(s):
Median MAD_SD
sigma 21696.8 369.2
------
* For help interpreting the printed output see ?print.stanreg
* For info on the priors used see ?prior_summary.stanreg
Plot linear model draws
sims_0 <- as.matrix(fit_0)
n_sims <- nrow(sims_0)
keep <- earnings$earn <= 2e5
par(mar=c(3,3,2,0), mgp=c(1.7,.5,0), tck=-.01)
plot((earnings$height + height_jitter_add)[keep], earnings$earn[keep], xlab="height", ylab="earnings", pch=20, yaxt="n", col="gray10", bty="l", cex=.4)
mtext("Fitted linear model", 3, 1)
axis(2, seq(0, 2e5, 1e5), c("0", "100000", "200000"))
for (i in sample(n_sims, 10)){
curve(sims_0[i,1] + sims_0[i,2]*x, lwd=0.5, col="gray30", add=TRUE)
}
curve(coef(fit_0)[1] + coef(fit_0)[2]*x, add=TRUE)
Plot linear model draws with x-axis extended to 0
keep <- earnings$earn <= 2e5
par(mar=c(3,3,2,0), mgp=c(1.7,.5,0), tck=-.01)
plot((earnings$height + height_jitter_add)[keep], earnings$earn[keep], xlab="height", ylab="earnings", pch=20, yaxt="n", col="gray10", bty="l", cex=.4, xlim=c(0, max(earnings$height)), ylim=c(-1e5, 2.2e5))
mtext("x-axis extended to 0", 3, 1)
axis(2, seq(-1e5, 2e5, 1e5), c("-100000", "0", "100000", "200000"))
for (i in sample(n_sims, 10)){
curve(sims_0[i,1] + sims_0[i,2]*x, lwd=0.5, col="gray30", add=TRUE)
}
curve(coef(fit_0)[1] + coef(fit_0)[2]*x, add=TRUE)
Predict earnings in thousands dollars
By scaling the earnings, the model coefficients are scaled, but the results don't change otherwise.
earnings$earnk <- earnings$earn/1000
# (earnk is actually already included in the data frame `earnings` for
# convenience for running examples in different arts of the book)
fit_1 <- stan_glm(earnk ~ height, data = earnings,
seed = SEED, refresh = 0)
print(fit_1)
stan_glm
family: gaussian [identity]
formula: earnk ~ height
observations: 1816
predictors: 2
------
Median MAD_SD
(Intercept) -85.1 8.9
height 1.6 0.1
Auxiliary parameter(s):
Median MAD_SD
sigma 21.7 0.4
------
* For help interpreting the printed output see ?print.stanreg
* For info on the priors used see ?prior_summary.stanreg
for plotting scale back to dollar scale
coef1 <- coef(fit_1)*1000
Plot linear model, ggplot version
gg_earnings <- ggplot(subset(earnings, subset=earn<2e5), aes(x = jitter(height, amount=0.2), y = earn)) +
geom_point(alpha = 0.75) +
geom_hline(yintercept = 0, color = "darkgray") +
geom_abline(intercept = coef1[1], slope = coef1[2], size = 1) +
labs(x = "height", y = "earnings",
title = "Fitted linear model")
gg_earnings
Include male/female
fit_2 <- stan_glm(earnk ~ height + male, data = earnings,
seed = SEED, refresh = 0)
print(fit_2)
stan_glm
family: gaussian [identity]
formula: earnk ~ height + male
observations: 1816
predictors: 3
------
Median MAD_SD
(Intercept) -25.7 12.0
height 0.6 0.2
male 10.7 1.5
Auxiliary parameter(s):
Median MAD_SD
sigma 21.4 0.4
------
* For help interpreting the printed output see ?print.stanreg
* For info on the priors used see ?prior_summary.stanreg
for plotting scale back to dollar scale
coef2 <- coef(fit_2)*1000
Include male/female, ggplot version
ggplot(earnings, aes(height, earn)) +
geom_blank() +
geom_abline(
intercept = c(coef2[1], coef2[1] + coef2[3]),
slope = coef2[2],
color = c("red", "blue")
) +
coord_cartesian(
ylim = range(predict(fit_2)*1000),
xlim = range(earnings$height)
) +
annotate(
"text",
x = c(68, 68),
y = c(coef2[1] + coef2[2] * 65, coef2[1] + coef2[3] + coef2[2] * 65),
label = c("women:\ny = -11 000 + 450x", "men:\ny = -2 000 + 450x"),
color = c("red", "blue"),
size = 5, hjust = 0
) +
labs(
x = "height",
y = "predicted earnings",
title = "Fitted regression, displayed as\nseparate lines for men and women"
)
Include interaction
fit_3 <- stan_glm(earnk ~ height + male + height:male, data = earnings,
seed = SEED, refresh = 0)
print(fit_3)
stan_glm
family: gaussian [identity]
formula: earnk ~ height + male + height:male
observations: 1816
predictors: 4
------
Median MAD_SD
(Intercept) -9.3 15.2
height 0.4 0.2
male -29.3 24.3
height:male 0.6 0.4
Auxiliary parameter(s):
Median MAD_SD
sigma 21.4 0.3
------
* For help interpreting the printed output see ?print.stanreg
* For info on the priors used see ?prior_summary.stanreg
for plotting scale back to dollar scale
coef3 <- coef(fit_3)*1000
Include interaction, ggplot version
ggplot(subset(earnings, subset=earn>0), aes(height, earn)) +
geom_blank() +
geom_abline(
intercept = c(coef3[1], coef3[1] + coef3[3]),
slope = c(coef3[2], coef3[2] + coef3[4]),
color = c("red", "blue")
) +
coord_cartesian(
ylim = range(predict(fit_3)*1000),
xlim = range(earnings$height)
) +
annotate(
"text",
x = c(62, 68),
y = c(coef3[1] + coef3[2] * 80, coef3[1]+coef3[3] + (coef3[2]+coef3[4])*66),
label = c("women:\ny = -7 000 + 180x", "men:\ny = -22 000 + 740x"),
color = c("red", "blue"),
size = 5, hjust = 0
) +
labs(
x = "height",
y = "predicted earnings",
title = "Fitted regression with interactions,\nseparate lines for men and women"
)
Linear regression on log scale
Models on log scale
logmodel_1 <- stan_glm(log(earn) ~ height, data = earnings,
subset = earn>0,
seed = SEED, refresh = 0)
print(logmodel_1, digits=2)
stan_glm
family: gaussian [identity]
formula: log(earn) ~ height
observations: 1629
predictors: 2
------
Median MAD_SD
(Intercept) 5.90 0.37
height 0.06 0.01
Auxiliary parameter(s):
Median MAD_SD
sigma 0.88 0.01
------
* For help interpreting the printed output see ?print.stanreg
* For info on the priors used see ?prior_summary.stanreg
Model on log10 scale
log10model_1 <- stan_glm(log10(earn) ~ height, data = earnings,
subset = earn>0,
seed = SEED, refresh = 0)
print(log10model_1, digits=3)
stan_glm
family: gaussian [identity]
formula: log10(earn) ~ height
observations: 1629
predictors: 2
------
Median MAD_SD
(Intercept) 2.571 0.165
height 0.025 0.002
Auxiliary parameter(s):
Median MAD_SD
sigma 0.381 0.007
------
* For help interpreting the printed output see ?print.stanreg
* For info on the priors used see ?prior_summary.stanreg
Model on log scale with two predictors
logmodel_2 <- stan_glm(log(earn) ~ height + male, data = earnings,
subset = earn>0,
seed = SEED, refresh = 0)
print(logmodel_2, digits=2)
stan_glm
family: gaussian [identity]
formula: log(earn) ~ height + male
observations: 1629
predictors: 3
------
Median MAD_SD
(Intercept) 7.97 0.49
height 0.02 0.01
male 0.37 0.06
Auxiliary parameter(s):
Median MAD_SD
sigma 0.87 0.01
------
* For help interpreting the printed output see ?print.stanreg
* For info on the priors used see ?prior_summary.stanreg
Model on log scale for the target and one predictor
loglogmodel_2 <- stan_glm(log(earn) ~ log(height) + male, data = earnings,
subset = earn>0,
seed = SEED, refresh = 0)
print(loglogmodel_2, digits=2)
stan_glm
family: gaussian [identity]
formula: log(earn) ~ log(height) + male
observations: 1629
predictors: 3
------
Median MAD_SD
(Intercept) 2.83 2.20
log(height) 1.60 0.53
male 0.37 0.06
Auxiliary parameter(s):
Median MAD_SD
sigma 0.87 0.02
------
* For help interpreting the printed output see ?print.stanreg
* For info on the priors used see ?prior_summary.stanreg
Model on log scale with two predictors and interaction
logmodel_3 <- stan_glm(log(earn) ~ height + male + height:male, data = earnings,
subset = earn>0,
seed = SEED, refresh = 0)
print(logmodel_3, digits=2)
stan_glm
family: gaussian [identity]
formula: log(earn) ~ height + male + height:male
observations: 1629
predictors: 4
------
Median MAD_SD
(Intercept) 8.53 0.65
height 0.02 0.01
male -0.87 0.99
height:male 0.02 0.01
Auxiliary parameter(s):
Median MAD_SD
sigma 0.87 0.02
------
* For help interpreting the printed output see ?print.stanreg
* For info on the priors used see ?prior_summary.stanreg
Model on log scale with standardized interaction
earnings$z_height <- with(earnings, (height - mean(height))/sd(height))
logmodel_3a <- stan_glm(log(earn) ~ z_height + male + z_height:male,
data = earnings, subset = earn>0,
seed = SEED, refresh = 0)
print(logmodel_3a, digits=2)
stan_glm
family: gaussian [identity]
formula: log(earn) ~ z_height + male + z_height:male
observations: 1629
predictors: 4
------
Median MAD_SD
(Intercept) 9.54 0.04
z_height 0.06 0.04
male 0.35 0.06
z_height:male 0.08 0.06
Auxiliary parameter(s):
Median MAD_SD
sigma 0.87 0.02
------
* For help interpreting the printed output see ?print.stanreg
* For info on the priors used see ?prior_summary.stanreg
PLot log models
get posterior draws
sims <- as.matrix(logmodel_1)
n_sims <- nrow(sims)
Plot log model on log scale
keep <- earnings$earn > 0
par(mar=c(3,3,2,0), mgp=c(1.7,.5,0), tck=-.01)
plot((earnings$height + height_jitter_add)[keep], log(earnings$earn)[keep], xlab="height", ylab="log (earnings)", pch=20, yaxt="n", col="gray10", bty="l", cex=.4)
mtext("Log regression plotted on log scale", 3, 1)
axis(2, seq(6,12,2))
for (i in sample(n_sims, 10)){
curve(sims[i,1] + sims[i,2]*x, lwd=0.5, col="gray30", add=TRUE)
}
curve(coef(logmodel_1)[1] + coef(logmodel_1)[2]*x, add=TRUE)
Plot posterior draws of linear model on log scale, ggplot version
sims_display <- sample(n_sims, 10)
ggplot(subset(earnings, subset=earn>0), aes(height, log(earn))) +
geom_jitter(height = 0, width = 0.25) +
geom_abline(
intercept = sims[sims_display, 1],
slope = sims[sims_display, 2],
color = "darkgray"
) +
geom_abline(
intercept = coef(logmodel_1)[1],
slope = coef(logmodel_1)[2]
) +
labs(
x = "height",
y = "log(earnings)",
title = "Log regression, plotted on log scale"
)
Plot log model on linear scale
keep <- earnings$earn > 0 & earnings$earn <= 2e5
par(mar=c(3,3,2,0), mgp=c(1.7,.5,0), tck=-.01)
plot((earnings$height + height_jitter_add)[keep], earnings$earn[keep], xlab="height", ylab="earnings", pch=20, yaxt="n", col="gray10", bty="l", cex=.4)
mtext("Log regression plotted on original scale", 3, 1)
axis(2, seq(0, 2e5, 1e5), c("0", "100000", "200000"))
for (i in sample(n_sims, 10)){
curve(exp(sims[i,1] + sims[i,2]*x), lwd=0.5, col="gray30", add=TRUE)
}
curve(exp(coef(logmodel_1)[1] + coef(logmodel_1)[2]*x), add=TRUE)
Posterior predictive checking
Posterior predictive checking for model in linear scale
for fair comparison refit the linear scale model only for non.zero earnings
yrep_0 <- posterior_predict(fit_0)
n_sims <- nrow(yrep_0)
sims_display <- sample(n_sims, 100)
ppc_0 <- ppc_dens_overlay(earnings$earn, yrep_0[sims_display,]) +
theme(axis.line.y = element_blank())
Posterior predictive checking for model in log scale
yrep_log_1 <- posterior_predict(logmodel_1)
n_sims <- nrow(yrep_log_1)
sims_display <- sample(n_sims, 100)
ppc_log_1 <- ppc_dens_overlay(log(earnings$earn[earnings$earn>0]), yrep_log_1[sims_display,]) +
theme(axis.line.y = element_blank())
bpg <- bayesplot_grid(
ppc_0, ppc_log_1,
grid_args = list(ncol = 2),
titles = c("earn", "log(earn)"))
bpg
Posterior predictive checking for model in linear scale
fit_2b <- stan_glm(earn ~ height + male, data = earnings, subset=earn>0,
seed = SEED, refresh = 0)
yrep_2 <- posterior_predict(fit_2b)
n_sims <- nrow(yrep_2)
sims_display <- sample(n_sims, 100)
ppc_dens_overlay(earnings$earn[earnings$earn>0], yrep_2[sims_display,])
Posterior predictive checking for model in log scale
yrep_log_2 <- posterior_predict(logmodel_2)
n_sims <- nrow(yrep_log_2)
sims_display <- sample(n_sims, 100)
ppc_dens_overlay(log(earnings$earn[earnings$earn>0]), yrep_log_2[sims_display,])
Posterior predictive checking for model in log-log scale
yrep_loglog_2 <- posterior_predict(loglogmodel_2)
n_sims <- nrow(yrep_loglog_2)
sims_display <- sample(n_sims, 100)
ppc_dens_overlay(log(earnings$earn[earnings$earn>0]), yrep_loglog_2[sims_display,])
LS0tCnRpdGxlOiAiUmVncmVzc2lvbiBhbmQgT3RoZXIgU3RvcmllczogRWFybmluZ3MiCmF1dGhvcjogIkFuZHJldyBHZWxtYW4sIEplbm5pZmVyIEhpbGwsIEFraSBWZWh0YXJpIgpkYXRlOiAiYHIgZm9ybWF0KFN5cy5EYXRlKCkpYCIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICB0aGVtZTogcmVhZGFibGUKICAgIHRvYzogdHJ1ZQogICAgdG9jX2RlcHRoOiAyCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKLS0tClByZWRpY3QgcmVzcG9uZGVudHMnIHllYXJseSBlYXJuaW5ncyB1c2luZyBzdXJ2ZXkgZGF0YSBmcm9tCjE5OTAuIFNlZSBDaGFwdGVycyA2LCA5IGFuZCAxMiBpbiBSZWdyZXNzaW9uIGFuZCBPdGhlciBTdG9yaWVzLgoKLS0tLS0tLS0tLS0tLQoKCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQobWVzc2FnZT1GQUxTRSwgZXJyb3I9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGNvbW1lbnQ9TkEpCiMgc3dpdGNoIHRoaXMgdG8gVFJVRSB0byBzYXZlIGZpZ3VyZXMgaW4gc2VwYXJhdGUgZmlsZXMKc2F2ZWZpZ3MgPC0gRkFMU0UKYGBgCgojIyMjIExvYWQgcGFja2FnZXMKCmBgYHtyIH0KbGlicmFyeSgicnByb2pyb290IikKcm9vdDwtaGFzX2ZpbGUoIi5ST1MtRXhhbXBsZXMtcm9vdCIpJG1ha2VfZml4X2ZpbGUoKQpsaWJyYXJ5KCJyc3RhbmFybSIpCmxpYnJhcnkoImdncGxvdDIiKQpsaWJyYXJ5KCJiYXllc3Bsb3QiKQp0aGVtZV9zZXQoYmF5ZXNwbG90Ojp0aGVtZV9kZWZhdWx0KGJhc2VfZmFtaWx5ID0gInNhbnMiKSkKYGBgCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CiMgZ3JheXNjYWxlIGZpZ3VyZXMgZm9yIHRoZSBib29rCmlmIChzYXZlZmlncykgY29sb3Jfc2NoZW1lX3NldChzY2hlbWUgPSAiZ3JheSIpCmBgYAoKU2V0IHJhbmRvbSBzZWVkIGZvciByZXByb2R1Y2liaWxpdHkKCmBgYHtyIH0KU0VFRCA8LSA3NzgzCmBgYAoKIyMjIyBMb2FkIGRhdGEKCmBgYHtyIH0KZWFybmluZ3MgPC0gcmVhZC5jc3Yocm9vdCgiRWFybmluZ3MvZGF0YSIsImVhcm5pbmdzLmNzdiIpKQpoZWFkKGVhcm5pbmdzKQpuIDwtIG5yb3coZWFybmluZ3MpCmhlaWdodF9qaXR0ZXJfYWRkIDwtIHJ1bmlmKG4sIC0uMiwgLjIpCmBgYAoKIyMgTm9ybWFsIGxpbmVhciByZWdyZXNzaW9uCgojIyMjIFByZWRpY3QgZWFybmluZ3MgaW4gZG9sbGFycwoKYGBge3IgfQpmaXRfMCA8LSBzdGFuX2dsbShlYXJuIH4gaGVpZ2h0LCBkYXRhPWVhcm5pbmdzLAogICAgICAgICAgICAgICAgICBzZWVkID0gU0VFRCwgcmVmcmVzaCA9IDApCnByaW50KGZpdF8wKQpgYGAKCiMjIyMgUGxvdCBsaW5lYXIgbW9kZWwgZHJhd3MKCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CmlmIChzYXZlZmlncykgcGRmKHJvb3QoIkVhcm5pbmdzL2ZpZ3MiLCJoZWlnaHRzLnNpbXBsZTBhLnBkZiIpLCBoZWlnaHQ9Mywgd2lkdGg9NC4yLCBjb2xvcm1vZGVsPSJncmF5IikKYGBgCmBgYHtyIH0Kc2ltc18wIDwtIGFzLm1hdHJpeChmaXRfMCkKbl9zaW1zIDwtIG5yb3coc2ltc18wKQprZWVwIDwtIGVhcm5pbmdzJGVhcm4gPD0gMmU1CnBhcihtYXI9YygzLDMsMiwwKSwgbWdwPWMoMS43LC41LDApLCB0Y2s9LS4wMSkKcGxvdCgoZWFybmluZ3MkaGVpZ2h0ICsgaGVpZ2h0X2ppdHRlcl9hZGQpW2tlZXBdLCBlYXJuaW5ncyRlYXJuW2tlZXBdLCB4bGFiPSJoZWlnaHQiLCB5bGFiPSJlYXJuaW5ncyIsIHBjaD0yMCwgeWF4dD0ibiIsIGNvbD0iZ3JheTEwIiwgYnR5PSJsIiwgY2V4PS40KQptdGV4dCgiRml0dGVkIGxpbmVhciBtb2RlbCIsIDMsIDEpCmF4aXMoMiwgc2VxKDAsIDJlNSwgMWU1KSwgYygiMCIsICIxMDAwMDAiLCAiMjAwMDAwIikpCmZvciAoaSBpbiBzYW1wbGUobl9zaW1zLCAxMCkpewogY3VydmUoc2ltc18wW2ksMV0gKyBzaW1zXzBbaSwyXSp4LCBsd2Q9MC41LCBjb2w9ImdyYXkzMCIsIGFkZD1UUlVFKQp9CmN1cnZlKGNvZWYoZml0XzApWzFdICsgY29lZihmaXRfMClbMl0qeCwgYWRkPVRSVUUpCmBgYApgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQpkZXYub2ZmKCkKYGBgCgojIyMjIFBsb3QgbGluZWFyIG1vZGVsIGRyYXdzIHdpdGggeC1heGlzIGV4dGVuZGVkIHRvIDAKCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CmlmIChzYXZlZmlncykgcGRmKHJvb3QoIkVhcm5pbmdzL2ZpZ3MiLCJoZWlnaHRzLmludGVyY2VwdC5wZGYiKSwgaGVpZ2h0PTMsIHdpZHRoPTQuMiwgY29sb3Jtb2RlbD0iZ3JheSIpCmBgYApgYGB7ciB9CmtlZXAgPC0gZWFybmluZ3MkZWFybiA8PSAyZTUKcGFyKG1hcj1jKDMsMywyLDApLCBtZ3A9YygxLjcsLjUsMCksIHRjaz0tLjAxKQpwbG90KChlYXJuaW5ncyRoZWlnaHQgKyBoZWlnaHRfaml0dGVyX2FkZClba2VlcF0sIGVhcm5pbmdzJGVhcm5ba2VlcF0sIHhsYWI9ImhlaWdodCIsIHlsYWI9ImVhcm5pbmdzIiwgcGNoPTIwLCB5YXh0PSJuIiwgY29sPSJncmF5MTAiLCBidHk9ImwiLCBjZXg9LjQsIHhsaW09YygwLCBtYXgoZWFybmluZ3MkaGVpZ2h0KSksIHlsaW09YygtMWU1LCAyLjJlNSkpCm10ZXh0KCJ4LWF4aXMgZXh0ZW5kZWQgdG8gMCIsIDMsIDEpCmF4aXMoMiwgc2VxKC0xZTUsIDJlNSwgMWU1KSwgYygiLTEwMDAwMCIsICIwIiwgIjEwMDAwMCIsICIyMDAwMDAiKSkKZm9yIChpIGluIHNhbXBsZShuX3NpbXMsIDEwKSl7CiBjdXJ2ZShzaW1zXzBbaSwxXSArIHNpbXNfMFtpLDJdKngsIGx3ZD0wLjUsIGNvbD0iZ3JheTMwIiwgYWRkPVRSVUUpCn0KY3VydmUoY29lZihmaXRfMClbMV0gKyBjb2VmKGZpdF8wKVsyXSp4LCBhZGQ9VFJVRSkKYGBgCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CmlmIChzYXZlZmlncykgZGV2Lm9mZigpCmBgYAoKIyMjIyBQcmVkaWN0IGVhcm5pbmdzIGluIHRob3VzYW5kcyBkb2xsYXJzCgpCeSBzY2FsaW5nIHRoZSBlYXJuaW5ncywgdGhlIG1vZGVsIGNvZWZmaWNpZW50cyBhcmUgc2NhbGVkLCBidXQgdGhlCnJlc3VsdHMgZG9uJ3QgY2hhbmdlIG90aGVyd2lzZS4KCmBgYHtyIH0KZWFybmluZ3MkZWFybmsgPC0gZWFybmluZ3MkZWFybi8xMDAwCiMgKGVhcm5rIGlzIGFjdHVhbGx5IGFscmVhZHkgaW5jbHVkZWQgaW4gdGhlIGRhdGEgZnJhbWUgYGVhcm5pbmdzYCBmb3IKIyBjb252ZW5pZW5jZSBmb3IgcnVubmluZyBleGFtcGxlcyBpbiBkaWZmZXJlbnQgYXJ0cyBvZiB0aGUgYm9vaykKZml0XzEgPC0gc3Rhbl9nbG0oZWFybmsgfiBoZWlnaHQsIGRhdGEgPSBlYXJuaW5ncywKICAgICAgICAgICAgICAgICAgc2VlZCA9IFNFRUQsIHJlZnJlc2ggPSAwKQpwcmludChmaXRfMSkKYGBgCgpmb3IgcGxvdHRpbmcgc2NhbGUgYmFjayB0byBkb2xsYXIgc2NhbGUKCmBgYHtyIH0KY29lZjEgPC0gY29lZihmaXRfMSkqMTAwMApgYGAKCiMjIyMgUGxvdCBsaW5lYXIgbW9kZWwsIGdncGxvdCB2ZXJzaW9uCgpgYGB7ciB9CmdnX2Vhcm5pbmdzIDwtIGdncGxvdChzdWJzZXQoZWFybmluZ3MsIHN1YnNldD1lYXJuPDJlNSksIGFlcyh4ID0gaml0dGVyKGhlaWdodCwgYW1vdW50PTAuMiksIHkgPSBlYXJuKSkgKwogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjc1KSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCwgY29sb3IgPSAiZGFya2dyYXkiKSArCiAgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0ID0gY29lZjFbMV0sIHNsb3BlID0gY29lZjFbMl0sIHNpemUgPSAxKSArCiAgbGFicyh4ID0gImhlaWdodCIsIHkgPSAiZWFybmluZ3MiLAogICAgICAgdGl0bGUgPSAiRml0dGVkIGxpbmVhciBtb2RlbCIpCmdnX2Vhcm5pbmdzCmBgYAoKIyMjIyBQbG90IGV4dHJhcG9sYXRpb24sIGdncGxvdCB2ZXJzaW9uCm1vZGlmeWluZyB0aGUgZ2dfZWFybmluZ3Mgb2JqZWN0IHdlIGFscmVhZHkgY3JlYXRlZAoKYGBge3IgfQpnZ19lYXJuaW5ncyArCiAgeWxpbSgtNzAwMDAsIDIwMDAwMCkgKwogIHhsaW0oMCwgODApICsKICBsYWJzKHRpdGxlID0gIkV4dHJhcG9sYXRpb24iKQpgYGAKCiMjIyMgSW5jbHVkZSBtYWxlL2ZlbWFsZQoKYGBge3IgfQpmaXRfMiA8LSBzdGFuX2dsbShlYXJuayB+IGhlaWdodCArIG1hbGUsIGRhdGEgPSBlYXJuaW5ncywKICAgICAgICAgICAgICAgICAgc2VlZCA9IFNFRUQsIHJlZnJlc2ggPSAwKQpwcmludChmaXRfMikKYGBgCgpmb3IgcGxvdHRpbmcgc2NhbGUgYmFjayB0byBkb2xsYXIgc2NhbGUKCmBgYHtyIH0KY29lZjIgPC0gY29lZihmaXRfMikqMTAwMApgYGAKCiMjIyMgSW5jbHVkZSBtYWxlL2ZlbWFsZSwgZ2dwbG90IHZlcnNpb24KCmBgYHtyIH0KZ2dwbG90KGVhcm5pbmdzLCBhZXMoaGVpZ2h0LCBlYXJuKSkgKwogIGdlb21fYmxhbmsoKSArCiAgZ2VvbV9hYmxpbmUoCiAgICBpbnRlcmNlcHQgPSBjKGNvZWYyWzFdLCBjb2VmMlsxXSArIGNvZWYyWzNdKSwKICAgIHNsb3BlID0gY29lZjJbMl0sCiAgICBjb2xvciA9IGMoInJlZCIsICJibHVlIikKICApICsKICBjb29yZF9jYXJ0ZXNpYW4oCiAgICB5bGltID0gcmFuZ2UocHJlZGljdChmaXRfMikqMTAwMCksCiAgICB4bGltID0gcmFuZ2UoZWFybmluZ3MkaGVpZ2h0KQogICkgKwogIGFubm90YXRlKAogICAgInRleHQiLAogICAgeCA9IGMoNjgsIDY4KSwKICAgIHkgPSBjKGNvZWYyWzFdICsgY29lZjJbMl0gKiA2NSwgY29lZjJbMV0gKyBjb2VmMlszXSArIGNvZWYyWzJdICogNjUpLAogICAgbGFiZWwgPSBjKCJ3b21lbjpcbnkgPSAtMTEgMDAwICsgNDUweCIsICJtZW46XG55ID0gLTIgMDAwICsgNDUweCIpLAogICAgY29sb3IgPSBjKCJyZWQiLCAiYmx1ZSIpLAogICAgc2l6ZSA9IDUsIGhqdXN0ID0gMAogICkgKwogIGxhYnMoCiAgICB4ID0gImhlaWdodCIsCiAgICB5ID0gInByZWRpY3RlZCBlYXJuaW5ncyIsCiAgICB0aXRsZSA9ICJGaXR0ZWQgcmVncmVzc2lvbiwgZGlzcGxheWVkIGFzXG5zZXBhcmF0ZSBsaW5lcyBmb3IgbWVuIGFuZCB3b21lbiIKICApCmBgYAoKIyMjIyBJbmNsdWRlIGludGVyYWN0aW9uCgpgYGB7ciB9CmZpdF8zIDwtIHN0YW5fZ2xtKGVhcm5rIH4gaGVpZ2h0ICsgbWFsZSArIGhlaWdodDptYWxlLCBkYXRhID0gZWFybmluZ3MsCiAgICAgICAgICAgICAgICAgIHNlZWQgPSBTRUVELCByZWZyZXNoID0gMCkKcHJpbnQoZml0XzMpCmBgYAoKZm9yIHBsb3R0aW5nIHNjYWxlIGJhY2sgdG8gZG9sbGFyIHNjYWxlCgpgYGB7ciB9CmNvZWYzIDwtIGNvZWYoZml0XzMpKjEwMDAKYGBgCgojIyMjIEluY2x1ZGUgaW50ZXJhY3Rpb24sIGdncGxvdCB2ZXJzaW9uCgpgYGB7ciB9CmdncGxvdChzdWJzZXQoZWFybmluZ3MsIHN1YnNldD1lYXJuPjApLCBhZXMoaGVpZ2h0LCBlYXJuKSkgKwogIGdlb21fYmxhbmsoKSArCiAgZ2VvbV9hYmxpbmUoCiAgICBpbnRlcmNlcHQgPSBjKGNvZWYzWzFdLCBjb2VmM1sxXSArIGNvZWYzWzNdKSwKICAgIHNsb3BlID0gYyhjb2VmM1syXSwgY29lZjNbMl0gKyBjb2VmM1s0XSksCiAgICBjb2xvciA9IGMoInJlZCIsICJibHVlIikKICApICsKICBjb29yZF9jYXJ0ZXNpYW4oCiAgICB5bGltID0gcmFuZ2UocHJlZGljdChmaXRfMykqMTAwMCksCiAgICB4bGltID0gcmFuZ2UoZWFybmluZ3MkaGVpZ2h0KQogICkgKwogIGFubm90YXRlKAogICAgInRleHQiLAogICAgeCA9IGMoNjIsIDY4KSwKICAgIHkgPSBjKGNvZWYzWzFdICsgY29lZjNbMl0gKiA4MCwgY29lZjNbMV0rY29lZjNbM10gKyAoY29lZjNbMl0rY29lZjNbNF0pKjY2KSwKICAgIGxhYmVsID0gYygid29tZW46XG55ID0gLTcgMDAwICsgMTgweCIsICJtZW46XG55ID0gLTIyIDAwMCArIDc0MHgiKSwKICAgIGNvbG9yID0gYygicmVkIiwgImJsdWUiKSwKICAgIHNpemUgPSA1LCBoanVzdCA9IDAKICApICsKICBsYWJzKAogICAgeCA9ICJoZWlnaHQiLAogICAgeSA9ICJwcmVkaWN0ZWQgZWFybmluZ3MiLAogICAgdGl0bGUgPSAiRml0dGVkIHJlZ3Jlc3Npb24gd2l0aCBpbnRlcmFjdGlvbnMsXG5zZXBhcmF0ZSBsaW5lcyBmb3IgbWVuIGFuZCB3b21lbiIKICApCmBgYAoKIyMgTGluZWFyIHJlZ3Jlc3Npb24gb24gbG9nIHNjYWxlCiMjIyMgTW9kZWxzIG9uIGxvZyBzY2FsZQoKYGBge3IgfQpsb2dtb2RlbF8xIDwtIHN0YW5fZ2xtKGxvZyhlYXJuKSB+IGhlaWdodCwgZGF0YSA9IGVhcm5pbmdzLAogICAgICAgICAgICAgICAgICAgICAgIHN1YnNldCA9IGVhcm4+MCwKICAgICAgICAgICAgICAgICAgICAgICBzZWVkID0gU0VFRCwgcmVmcmVzaCA9IDApCnByaW50KGxvZ21vZGVsXzEsIGRpZ2l0cz0yKQpgYGAKCiMjIyMgTW9kZWwgb24gbG9nMTAgc2NhbGUKCmBgYHtyIH0KbG9nMTBtb2RlbF8xIDwtIHN0YW5fZ2xtKGxvZzEwKGVhcm4pIH4gaGVpZ2h0LCBkYXRhID0gZWFybmluZ3MsCiAgICAgICAgICAgICAgICAgICAgICAgICBzdWJzZXQgPSBlYXJuPjAsCiAgICAgICAgICAgICAgICAgICAgICAgICBzZWVkID0gU0VFRCwgcmVmcmVzaCA9IDApCnByaW50KGxvZzEwbW9kZWxfMSwgZGlnaXRzPTMpCmBgYAoKIyMjIyBNb2RlbCBvbiBsb2cgc2NhbGUgd2l0aCB0d28gcHJlZGljdG9ycwoKYGBge3IgfQpsb2dtb2RlbF8yIDwtIHN0YW5fZ2xtKGxvZyhlYXJuKSB+IGhlaWdodCArIG1hbGUsIGRhdGEgPSBlYXJuaW5ncywKICAgICAgICAgICAgICAgICAgICAgICBzdWJzZXQgPSBlYXJuPjAsCiAgICAgICAgICAgICAgICAgICAgICAgc2VlZCA9IFNFRUQsIHJlZnJlc2ggPSAwKQpwcmludChsb2dtb2RlbF8yLCBkaWdpdHM9MikKYGBgCgojIyMjIE1vZGVsIG9uIGxvZyBzY2FsZSBmb3IgdGhlIHRhcmdldCBhbmQgb25lIHByZWRpY3RvcgoKYGBge3IgfQpsb2dsb2dtb2RlbF8yIDwtIHN0YW5fZ2xtKGxvZyhlYXJuKSB+IGxvZyhoZWlnaHQpICsgbWFsZSwgZGF0YSA9IGVhcm5pbmdzLAogICAgICAgICAgICAgICAgICAgICAgICAgIHN1YnNldCA9IGVhcm4+MCwKICAgICAgICAgICAgICAgICAgICAgICAgICBzZWVkID0gU0VFRCwgcmVmcmVzaCA9IDApCnByaW50KGxvZ2xvZ21vZGVsXzIsIGRpZ2l0cz0yKQpgYGAKCiMjIyMgTW9kZWwgb24gbG9nIHNjYWxlIHdpdGggdHdvIHByZWRpY3RvcnMgYW5kIGludGVyYWN0aW9uCgpgYGB7ciB9CmxvZ21vZGVsXzMgPC0gc3Rhbl9nbG0obG9nKGVhcm4pIH4gaGVpZ2h0ICsgbWFsZSArIGhlaWdodDptYWxlLCBkYXRhID0gZWFybmluZ3MsCiAgICAgICAgICAgICAgICAgICAgICAgc3Vic2V0ID0gZWFybj4wLAogICAgICAgICAgICAgICAgICAgICAgIHNlZWQgPSBTRUVELCByZWZyZXNoID0gMCkKcHJpbnQobG9nbW9kZWxfMywgZGlnaXRzPTIpCmBgYAoKIyMjIyBNb2RlbCBvbiBsb2cgc2NhbGUgd2l0aCBzdGFuZGFyZGl6ZWQgaW50ZXJhY3Rpb24KCmBgYHtyIH0KZWFybmluZ3Mkel9oZWlnaHQgPC0gd2l0aChlYXJuaW5ncywgKGhlaWdodCAtIG1lYW4oaGVpZ2h0KSkvc2QoaGVpZ2h0KSkKbG9nbW9kZWxfM2EgPC0gc3Rhbl9nbG0obG9nKGVhcm4pIH4gel9oZWlnaHQgKyBtYWxlICsgel9oZWlnaHQ6bWFsZSwKICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IGVhcm5pbmdzLCBzdWJzZXQgPSBlYXJuPjAsCiAgICAgICAgICAgICAgICAgICAgICAgIHNlZWQgPSBTRUVELCByZWZyZXNoID0gMCkKcHJpbnQobG9nbW9kZWxfM2EsIGRpZ2l0cz0yKQpgYGAKCiMjIyMgUExvdCBsb2cgbW9kZWxzCmdldCBwb3N0ZXJpb3IgZHJhd3MKCmBgYHtyIH0Kc2ltcyA8LSBhcy5tYXRyaXgobG9nbW9kZWxfMSkKbl9zaW1zIDwtIG5yb3coc2ltcykKYGBgCgojIyMjIFBsb3QgbG9nIG1vZGVsIG9uIGxvZyBzY2FsZQoKYGBge3IgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KaWYgKHNhdmVmaWdzKSBwZGYocm9vdCgiRWFybmluZ3MvZmlncyIsImhlaWdodHMubG9nMWEucGRmIiksIGhlaWdodD0zLCB3aWR0aD00LjIsIGNvbG9ybW9kZWw9ImdyYXkiKQpgYGAKYGBge3IgfQprZWVwIDwtIGVhcm5pbmdzJGVhcm4gPiAwCnBhcihtYXI9YygzLDMsMiwwKSwgbWdwPWMoMS43LC41LDApLCB0Y2s9LS4wMSkKcGxvdCgoZWFybmluZ3MkaGVpZ2h0ICsgaGVpZ2h0X2ppdHRlcl9hZGQpW2tlZXBdLCBsb2coZWFybmluZ3MkZWFybilba2VlcF0sIHhsYWI9ImhlaWdodCIsIHlsYWI9ImxvZyAoZWFybmluZ3MpIiwgcGNoPTIwLCB5YXh0PSJuIiwgY29sPSJncmF5MTAiLCBidHk9ImwiLCBjZXg9LjQpCm10ZXh0KCJMb2cgcmVncmVzc2lvbiBwbG90dGVkIG9uIGxvZyBzY2FsZSIsIDMsIDEpCmF4aXMoMiwgc2VxKDYsMTIsMikpCmZvciAoaSBpbiBzYW1wbGUobl9zaW1zLCAxMCkpewogY3VydmUoc2ltc1tpLDFdICsgc2ltc1tpLDJdKngsIGx3ZD0wLjUsIGNvbD0iZ3JheTMwIiwgYWRkPVRSVUUpCn0KY3VydmUoY29lZihsb2dtb2RlbF8xKVsxXSArIGNvZWYobG9nbW9kZWxfMSlbMl0qeCwgYWRkPVRSVUUpCmBgYApgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQppZiAoc2F2ZWZpZ3MpIGRldi5vZmYoKQpgYGAKCiMjIyMgUGxvdCBwb3N0ZXJpb3IgZHJhd3Mgb2YgbGluZWFyIG1vZGVsIG9uIGxvZyBzY2FsZSwgZ2dwbG90IHZlcnNpb24KCmBgYHtyIH0Kc2ltc19kaXNwbGF5IDwtIHNhbXBsZShuX3NpbXMsIDEwKQpnZ3Bsb3Qoc3Vic2V0KGVhcm5pbmdzLCBzdWJzZXQ9ZWFybj4wKSwgYWVzKGhlaWdodCwgbG9nKGVhcm4pKSkgKwogIGdlb21faml0dGVyKGhlaWdodCA9IDAsIHdpZHRoID0gMC4yNSkgKwogIGdlb21fYWJsaW5lKAogICAgaW50ZXJjZXB0ID0gc2ltc1tzaW1zX2Rpc3BsYXksIDFdLAogICAgc2xvcGUgPSBzaW1zW3NpbXNfZGlzcGxheSwgMl0sCiAgICBjb2xvciA9ICJkYXJrZ3JheSIKICApICsKICBnZW9tX2FibGluZSgKICAgIGludGVyY2VwdCA9IGNvZWYobG9nbW9kZWxfMSlbMV0sCiAgICBzbG9wZSA9IGNvZWYobG9nbW9kZWxfMSlbMl0KICApICsKICBsYWJzKAogICAgeCA9ICJoZWlnaHQiLAogICAgeSA9ICJsb2coZWFybmluZ3MpIiwKICAgIHRpdGxlID0gIkxvZyByZWdyZXNzaW9uLCBwbG90dGVkIG9uIGxvZyBzY2FsZSIKICApCmBgYAoKIyMjIyBQbG90IGxvZyBtb2RlbCBvbiBsaW5lYXIgc2NhbGUKCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CmlmIChzYXZlZmlncykgcGRmKHJvb3QoIkVhcm5pbmdzL2ZpZ3MiLCJoZWlnaHRzLmxvZzFiLnBkZiIpLCBoZWlnaHQ9Mywgd2lkdGg9NC4yLCBjb2xvcm1vZGVsPSJncmF5IikKYGBgCmBgYHtyIH0Ka2VlcCA8LSBlYXJuaW5ncyRlYXJuID4gMCAmIGVhcm5pbmdzJGVhcm4gPD0gMmU1CnBhcihtYXI9YygzLDMsMiwwKSwgbWdwPWMoMS43LC41LDApLCB0Y2s9LS4wMSkKcGxvdCgoZWFybmluZ3MkaGVpZ2h0ICsgaGVpZ2h0X2ppdHRlcl9hZGQpW2tlZXBdLCBlYXJuaW5ncyRlYXJuW2tlZXBdLCB4bGFiPSJoZWlnaHQiLCB5bGFiPSJlYXJuaW5ncyIsIHBjaD0yMCwgeWF4dD0ibiIsIGNvbD0iZ3JheTEwIiwgYnR5PSJsIiwgY2V4PS40KQptdGV4dCgiTG9nIHJlZ3Jlc3Npb24gcGxvdHRlZCBvbiBvcmlnaW5hbCBzY2FsZSIsIDMsIDEpCmF4aXMoMiwgc2VxKDAsIDJlNSwgMWU1KSwgYygiMCIsICIxMDAwMDAiLCAiMjAwMDAwIikpCmZvciAoaSBpbiBzYW1wbGUobl9zaW1zLCAxMCkpewogY3VydmUoZXhwKHNpbXNbaSwxXSArIHNpbXNbaSwyXSp4KSwgbHdkPTAuNSwgY29sPSJncmF5MzAiLCBhZGQ9VFJVRSkKfQpjdXJ2ZShleHAoY29lZihsb2dtb2RlbF8xKVsxXSArIGNvZWYobG9nbW9kZWxfMSlbMl0qeCksIGFkZD1UUlVFKQpgYGAKYGBge3IgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KaWYgKHNhdmVmaWdzKSBkZXYub2ZmKCkKYGBgCgojIyBQb3N0ZXJpb3IgcHJlZGljdGl2ZSBjaGVja2luZwojIyMjIFBvc3RlcmlvciBwcmVkaWN0aXZlIGNoZWNraW5nIGZvciBtb2RlbCBpbiBsaW5lYXIgc2NhbGU8YnI+CmZvciBmYWlyIGNvbXBhcmlzb24gcmVmaXQgdGhlIGxpbmVhciBzY2FsZSBtb2RlbCBvbmx5IGZvciBub24uemVybyBlYXJuaW5ncwoKYGBge3IgfQp5cmVwXzAgPC0gcG9zdGVyaW9yX3ByZWRpY3QoZml0XzApCm5fc2ltcyA8LSBucm93KHlyZXBfMCkKc2ltc19kaXNwbGF5IDwtIHNhbXBsZShuX3NpbXMsIDEwMCkKcHBjXzAgPC0gcHBjX2RlbnNfb3ZlcmxheShlYXJuaW5ncyRlYXJuLCB5cmVwXzBbc2ltc19kaXNwbGF5LF0pICsKICB0aGVtZShheGlzLmxpbmUueSA9IGVsZW1lbnRfYmxhbmsoKSkKYGBgCgojIyMjIFBvc3RlcmlvciBwcmVkaWN0aXZlIGNoZWNraW5nIGZvciBtb2RlbCBpbiBsb2cgc2NhbGUKCmBgYHtyIH0KeXJlcF9sb2dfMSA8LSBwb3N0ZXJpb3JfcHJlZGljdChsb2dtb2RlbF8xKQpuX3NpbXMgPC0gbnJvdyh5cmVwX2xvZ18xKQpzaW1zX2Rpc3BsYXkgPC0gc2FtcGxlKG5fc2ltcywgMTAwKQpwcGNfbG9nXzEgPC0gcHBjX2RlbnNfb3ZlcmxheShsb2coZWFybmluZ3MkZWFybltlYXJuaW5ncyRlYXJuPjBdKSwgeXJlcF9sb2dfMVtzaW1zX2Rpc3BsYXksXSkgKwogICAgdGhlbWUoYXhpcy5saW5lLnkgPSBlbGVtZW50X2JsYW5rKCkpCmJwZyA8LSBiYXllc3Bsb3RfZ3JpZCgKICBwcGNfMCwgcHBjX2xvZ18xLAogIGdyaWRfYXJncyA9IGxpc3QobmNvbCA9IDIpLAogIHRpdGxlcyA9IGMoImVhcm4iLCAibG9nKGVhcm4pIikpCmJwZwpgYGAKYGBge3IgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KZ2dzYXZlKHJvb3QoIkVhcm5pbmdzL2ZpZ3MiLCJlYXJuaW5nc19wcGMucGRmIiksIGJwZywgaGVpZ2h0PTMsIHdpZHRoPTksIGNvbG9ybW9kZWw9ImdyYXkiKQpgYGAKCiMjIyMgUG9zdGVyaW9yIHByZWRpY3RpdmUgY2hlY2tpbmcgZm9yIG1vZGVsIGluIGxpbmVhciBzY2FsZQoKYGBge3IgfQpmaXRfMmIgPC0gc3Rhbl9nbG0oZWFybiB+IGhlaWdodCArIG1hbGUsIGRhdGEgPSBlYXJuaW5ncywgc3Vic2V0PWVhcm4+MCwKICAgICAgICAgICAgICAgICAgIHNlZWQgPSBTRUVELCByZWZyZXNoID0gMCkKeXJlcF8yIDwtIHBvc3Rlcmlvcl9wcmVkaWN0KGZpdF8yYikKbl9zaW1zIDwtIG5yb3coeXJlcF8yKQpzaW1zX2Rpc3BsYXkgPC0gc2FtcGxlKG5fc2ltcywgMTAwKQpwcGNfZGVuc19vdmVybGF5KGVhcm5pbmdzJGVhcm5bZWFybmluZ3MkZWFybj4wXSwgeXJlcF8yW3NpbXNfZGlzcGxheSxdKQpgYGAKCiMjIyMgUG9zdGVyaW9yIHByZWRpY3RpdmUgY2hlY2tpbmcgZm9yIG1vZGVsIGluIGxvZyBzY2FsZQoKYGBge3IgfQp5cmVwX2xvZ18yIDwtIHBvc3Rlcmlvcl9wcmVkaWN0KGxvZ21vZGVsXzIpCm5fc2ltcyA8LSBucm93KHlyZXBfbG9nXzIpCnNpbXNfZGlzcGxheSA8LSBzYW1wbGUobl9zaW1zLCAxMDApCnBwY19kZW5zX292ZXJsYXkobG9nKGVhcm5pbmdzJGVhcm5bZWFybmluZ3MkZWFybj4wXSksIHlyZXBfbG9nXzJbc2ltc19kaXNwbGF5LF0pCmBgYAoKIyMjIyBQb3N0ZXJpb3IgcHJlZGljdGl2ZSBjaGVja2luZyBmb3IgbW9kZWwgaW4gbG9nLWxvZyBzY2FsZQoKYGBge3IgfQp5cmVwX2xvZ2xvZ18yIDwtIHBvc3Rlcmlvcl9wcmVkaWN0KGxvZ2xvZ21vZGVsXzIpCm5fc2ltcyA8LSBucm93KHlyZXBfbG9nbG9nXzIpCnNpbXNfZGlzcGxheSA8LSBzYW1wbGUobl9zaW1zLCAxMDApCnBwY19kZW5zX292ZXJsYXkobG9nKGVhcm5pbmdzJGVhcm5bZWFybmluZ3MkZWFybj4wXSksIHlyZXBfbG9nbG9nXzJbc2ltc19kaXNwbGF5LF0pCmBgYAoK