Linear regression with multiple predictors. See Chapters 10, 11 and 12 in Regression and Other Stories.


Load packages

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

Load children's test scores data

kidiq <- read.csv(root("KidIQ/data","kidiq.csv"))
head(kidiq)
  kid_score mom_hs    mom_iq mom_work mom_age
1        65      1 121.11753        4      27
2        98      1  89.36188        4      25
3        85      1 115.44316        4      27
4        83      1  99.44964        3      25
5       115      1  92.74571        4      27
6        98      0 107.90184        1      18

A single predictor

A single binary predictor

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.

fit_1 <- stan_glm(kid_score ~ mom_hs, data=kidiq, refresh = 0)
print(fit_1)
stan_glm
 family:       gaussian [identity]
 formula:      kid_score ~ mom_hs
 observations: 434
 predictors:   2
------
            Median MAD_SD
(Intercept) 77.6    2.0  
mom_hs      11.8    2.3  

Auxiliary parameter(s):
      Median MAD_SD
sigma 19.9    0.7  

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

A single continuous predictor

fit_2 <- stan_glm(kid_score ~ mom_iq, data=kidiq, refresh = 0)
print(fit_2)
stan_glm
 family:       gaussian [identity]
 formula:      kid_score ~ mom_iq
 observations: 434
 predictors:   2
------
            Median MAD_SD
(Intercept) 25.6    5.9  
mom_iq       0.6    0.1  

Auxiliary parameter(s):
      Median MAD_SD
sigma 18.3    0.6  

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

Displaying a regression line as a function of one input variable

plot(kidiq$mom_iq, kidiq$kid_score, xlab="Mother IQ score", ylab="Child test score")
abline(coef(fit_2))

# or
#abline(coef(fit_2)[1], coef(fit_2)[2])
# or
#curve(cbind(1,x) %*% coef(fit_2), add=TRUE)

ggplot version

ggplot(kidiq, aes(mom_iq, kid_score)) +
  geom_point() +
  geom_abline(intercept = coef(fit_2)[1], slope = coef(fit_2)[2]) +
  labs(x = "Mother IQ score", y = "Child test score")

Two predictors

Linear regression

fit_3 <- stan_glm(kid_score ~ mom_hs + mom_iq, data=kidiq, refresh = 0)
print(fit_3)
stan_glm
 family:       gaussian [identity]
 formula:      kid_score ~ mom_hs + mom_iq
 observations: 434
 predictors:   3
------
            Median MAD_SD
(Intercept) 25.6    6.0  
mom_hs       5.9    2.2  
mom_iq       0.6    0.1  

Auxiliary parameter(s):
      Median MAD_SD
sigma 18.1    0.6  

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

Alternative display

summary(fit_3)

Model Info:
 function:     stan_glm
 family:       gaussian [identity]
 formula:      kid_score ~ mom_hs + mom_iq
 algorithm:    sampling
 sample:       4000 (posterior sample size)
 priors:       see help('prior_summary')
 observations: 434
 predictors:   3

Estimates:
              mean   sd   10%   50%   90%
(Intercept) 25.6    5.8 18.2  25.6  33.0 
mom_hs       5.9    2.3  3.0   5.9   8.9 
mom_iq       0.6    0.1  0.5   0.6   0.6 
sigma       18.2    0.6 17.4  18.1  19.0 

Fit Diagnostics:
           mean   sd   10%   50%   90%
mean_PPD 86.8    1.2 85.2  86.8  88.4 

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.1  1.0  5102 
mom_hs        0.0  1.0  4522 
mom_iq        0.0  1.0  4551 
sigma         0.0  1.0  4701 
mean_PPD      0.0  1.0  4268 
log-posterior 0.0  1.0  1749 

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

Graphical displays of data and fitted models

Two fitted regression lines -- model with no interaction

colors <- ifelse(kidiq$mom_hs==1, "black", "gray")
plot(kidiq$mom_iq, kidiq$kid_score,
  xlab="Mother IQ score", ylab="Child test score", col=colors, pch=20)
b_hat <- coef(fit_3)
abline(b_hat[1] + b_hat[2], b_hat[3], col="black")
abline(b_hat[1], b_hat[3], col="gray")

ggplot version

ggplot(kidiq, aes(mom_iq, kid_score)) +
  geom_point(aes(color = factor(mom_hs)), show.legend = FALSE) +
  geom_abline(
    intercept = c(coef(fit_3)[1], coef(fit_3)[1] + coef(fit_3)[2]),
    slope = coef(fit_3)[3],
    color = c("gray", "black")) +
  scale_color_manual(values = c("gray", "black")) +
  labs(x = "Mother IQ score", y = "Child test score")

Two fitted regression lines -- model with interaction

fit_4 <- stan_glm(kid_score ~ mom_hs + mom_iq + mom_hs:mom_iq, data=kidiq,
                  refresh = 0)
print(fit_4)
stan_glm
 family:       gaussian [identity]
 formula:      kid_score ~ mom_hs + mom_iq + mom_hs:mom_iq
 observations: 434
 predictors:   4
------
              Median MAD_SD
(Intercept)   -9.7   13.5  
mom_hs        49.2   15.2  
mom_iq         1.0    0.1  
mom_hs:mom_iq -0.5    0.2  

Auxiliary parameter(s):
      Median MAD_SD
sigma 18.0    0.6  

------
* For help interpreting the printed output see ?print.stanreg
* For info on the priors used see ?prior_summary.stanreg
colors <- ifelse(kidiq$mom_hs==1, "black", "gray")
plot(kidiq$mom_iq, kidiq$kid_score,
  xlab="Mother IQ score", ylab="Child test score", col=colors, pch=20)
b_hat <- coef(fit_4)
abline(b_hat[1] + b_hat[2], b_hat[3] + b_hat[4], col="black")
abline(b_hat[1], b_hat[3], col="gray")

ggplot version

ggplot(kidiq, aes(mom_iq, kid_score)) +
  geom_point(aes(color = factor(mom_hs)), show.legend = FALSE) +
  geom_abline(
    intercept = c(coef(fit_4)[1], sum(coef(fit_4)[1:2])),
    slope = c(coef(fit_4)[3], sum(coef(fit_4)[3:4])),
    color = c("gray", "black")) +
  scale_color_manual(values = c("gray", "black")) +
  labs(x = "Mother IQ score", y = "Child test score")

Displaying uncertainty in the fitted regression

A single continuous predictor

print(fit_2)
stan_glm
 family:       gaussian [identity]
 formula:      kid_score ~ mom_iq
 observations: 434
 predictors:   2
------
            Median MAD_SD
(Intercept) 25.6    5.9  
mom_iq       0.6    0.1  

Auxiliary parameter(s):
      Median MAD_SD
sigma 18.3    0.6  

------
* For help interpreting the printed output see ?print.stanreg
* For info on the priors used see ?prior_summary.stanreg
sims_2 <- as.matrix(fit_2)
n_sims_2 <- nrow(sims_2)
subset <- sample(n_sims_2, 10)
plot(kidiq$mom_iq, kidiq$kid_score,
     xlab="Mother IQ score", ylab="Child test score")
for (i in subset){
  abline(sims_2[i,1], sims_2[i,2], col="gray")
}
abline(coef(fit_2)[1], coef(fit_2)[2], col="black")

ggplot version

ggplot(kidiq, aes(mom_iq, kid_score)) +
  geom_point() +
  geom_abline(
    intercept = sims_2[subset, 1],
    slope = sims_2[subset, 2],
    color = "gray",
    size = 0.25) +
  geom_abline(
    intercept = coef(fit_2)[1],
    slope = coef(fit_2)[2],
    size = 0.75) +
  labs(x = "Mother IQ score", y = "Child test score")

Two predictors

sims_3 <- as.matrix(fit_3)
n_sims_3 <- nrow(sims_3)
par(mar=c(3,3,1,3), mgp=c(1.7, .5, 0), tck=-.01)
par(mfrow=c(1,2))
plot(kidiq$mom_iq, kidiq$kid_score, xlab="Mother IQ score", ylab="Child test score", bty="l", pch=20, xaxt="n", yaxt="n")
axis(1, seq(80, 140, 20))
axis(2, seq(20, 140, 40))
mom_hs_bar <- mean(kidiq$mom_hs)
subset <- sample(n_sims_3, 10)
for (i in subset){
  curve(cbind(1, mom_hs_bar, x) %*% sims_3[i,1:3], lwd=.5,
     col="gray", add=TRUE)
}
curve(cbind(1, mom_hs_bar, x) %*% coef(fit_3), col="black", add=TRUE)
jitt <- runif(nrow(kidiq), -.03, .03)
plot(kidiq$mom_hs + jitt, kidiq$kid_score, xlab="Mother completed high school", ylab="Child test score", bty="l", pch=20, xaxt="n", yaxt="n")
axis(1, c(0,1))
axis(2, seq(20, 140, 40))
mom_iq_bar <- mean(kidiq$mom_iq)
for (i in subset){
  curve(cbind(1, x, mom_iq_bar) %*% sims_3[i,1:3], lwd=.5,
     col="gray", add=TRUE)
}
curve(cbind(1, x, mom_iq_bar) %*% coef(fit_3), col="black", add=TRUE)

Center predictors to have zero mean

kidiq$c_mom_hs <- kidiq$mom_hs - mean(kidiq$mom_hs)
kidiq$c_mom_iq <- kidiq$mom_iq - mean(kidiq$mom_iq)
fit_4c <- stan_glm(kid_score ~ c_mom_hs + c_mom_iq + c_mom_hs:c_mom_iq,
                   data=kidiq, refresh = 0)
print(fit_4c)
stan_glm
 family:       gaussian [identity]
 formula:      kid_score ~ c_mom_hs + c_mom_iq + c_mom_hs:c_mom_iq
 observations: 434
 predictors:   4
------
                  Median MAD_SD
(Intercept)       87.6    0.9  
c_mom_hs           2.9    2.4  
c_mom_iq           0.6    0.1  
c_mom_hs:c_mom_iq -0.5    0.2  

Auxiliary parameter(s):
      Median MAD_SD
sigma 18.0    0.6  

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

Center predictors based on a reference point

kidiq$c2_mom_hs <- kidiq$mom_hs - 0.5
kidiq$c2_mom_iq <- kidiq$mom_iq - 100
fit_4c2 <- stan_glm(kid_score ~ c2_mom_hs + c2_mom_iq + c2_mom_hs:c2_mom_iq,
                    data=kidiq, refresh = 0)
print(fit_4c2)
stan_glm
 family:       gaussian [identity]
 formula:      kid_score ~ c2_mom_hs + c2_mom_iq + c2_mom_hs:c2_mom_iq
 observations: 434
 predictors:   4
------
                    Median MAD_SD
(Intercept)         86.8    1.2  
c2_mom_hs            2.9    2.4  
c2_mom_iq            0.7    0.1  
c2_mom_hs:c2_mom_iq -0.5    0.2  

Auxiliary parameter(s):
      Median MAD_SD
sigma 18.0    0.6  

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

Center and scale predictors to have zero mean and sd=1/2

kidiq$z_mom_hs <- (kidiq$mom_hs - mean(kidiq$mom_hs))/(2*sd(kidiq$mom_hs))
kidiq$z_mom_iq <- (kidiq$mom_iq - mean(kidiq$mom_iq))/(2*sd(kidiq$mom_iq))
fit_4z <- stan_glm(kid_score ~ z_mom_hs + z_mom_iq + z_mom_hs:z_mom_iq,
                   data=kidiq, refresh = 0)
print(fit_4z)
stan_glm
 family:       gaussian [identity]
 formula:      kid_score ~ z_mom_hs + z_mom_iq + z_mom_hs:z_mom_iq
 observations: 434
 predictors:   4
------
                  Median MAD_SD
(Intercept)        87.6    0.9 
z_mom_hs            2.4    1.9 
z_mom_iq           17.6    1.9 
z_mom_hs:z_mom_iq -11.8    3.9 

Auxiliary parameter(s):
      Median MAD_SD
sigma 18.0    0.6  

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

Predict using working status of mother

fit_5 <- stan_glm(kid_score ~ as.factor(mom_work), data=kidiq, refresh = 0)
print(fit_5)
stan_glm
 family:       gaussian [identity]
 formula:      kid_score ~ as.factor(mom_work)
 observations: 434
 predictors:   4
------
                     Median MAD_SD
(Intercept)          81.9    2.3  
as.factor(mom_work)2  4.0    3.1  
as.factor(mom_work)3 11.5    3.6  
as.factor(mom_work)4  5.3    2.8  

Auxiliary parameter(s):
      Median MAD_SD
sigma 20.3    0.7  

------
* For help interpreting the printed output see ?print.stanreg
* For info on the priors used see ?prior_summary.stanreg
LS0tCnRpdGxlOiAiUmVncmVzc2lvbiBhbmQgT3RoZXIgU3RvcmllczogS2lkSVEiCmF1dGhvcjogIkFuZHJldyBHZWxtYW4sIEplbm5pZmVyIEhpbGwsIEFraSBWZWh0YXJpIgpkYXRlOiAiYHIgZm9ybWF0KFN5cy5EYXRlKCkpYCIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICB0aGVtZTogcmVhZGFibGUKICAgIHRvYzogdHJ1ZQogICAgdG9jX2RlcHRoOiAyCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKLS0tCkxpbmVhciByZWdyZXNzaW9uIHdpdGggbXVsdGlwbGUgcHJlZGljdG9ycy4gU2VlIENoYXB0ZXJzIDEwLCAxMSBhbmQKMTIgaW4gUmVncmVzc2lvbiBhbmQgT3RoZXIgU3Rvcmllcy4KCi0tLS0tLS0tLS0tLS0KCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KG1lc3NhZ2U9RkFMU0UsIGVycm9yPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBjb21tZW50PU5BKQojIHN3aXRjaCB0aGlzIHRvIFRSVUUgdG8gc2F2ZSBmaWd1cmVzIGluIHNlcGFyYXRlIGZpbGVzCnNhdmVmaWdzIDwtIEZBTFNFCmBgYAoKIyMjIyBMb2FkIHBhY2thZ2VzCgpgYGB7ciB9CmxpYnJhcnkoInJwcm9qcm9vdCIpCnJvb3Q8LWhhc19maWxlKCIuUk9TLUV4YW1wbGVzLXJvb3QiKSRtYWtlX2ZpeF9maWxlKCkKbGlicmFyeSgicnN0YW5hcm0iKQpsaWJyYXJ5KCJnZ3Bsb3QyIikKbGlicmFyeSgiYmF5ZXNwbG90IikKdGhlbWVfc2V0KGJheWVzcGxvdDo6dGhlbWVfZGVmYXVsdChiYXNlX2ZhbWlseSA9ICJzYW5zIikpCmxpYnJhcnkoImZvcmVpZ24iKQpgYGAKCiMjIyMgTG9hZCBjaGlsZHJlbidzIHRlc3Qgc2NvcmVzIGRhdGEKCmBgYHtyIH0Ka2lkaXEgPC0gcmVhZC5jc3Yocm9vdCgiS2lkSVEvZGF0YSIsImtpZGlxLmNzdiIpKQpoZWFkKGtpZGlxKQpgYGAKCiMjIEEgc2luZ2xlIHByZWRpY3RvcgoKIyMjIyBBIHNpbmdsZSBiaW5hcnkgcHJlZGljdG9yIAoKVGhlIG9wdGlvbiBgcmVmcmVzaCA9IDBgIHN1cHJlc3NlcyB0aGUgZGVmYXVsdCBTdGFuIHNhbXBsaW5nCnByb2dyZXNzIG91dHB1dC4gVGhpcyBpcyB1c2VmdWwgZm9yIHNtYWxsIGRhdGEgd2l0aCBmYXN0CmNvbXB1dGF0aW9uLiBGb3IgbW9yZSBjb21wbGV4IG1vZGVscyBhbmQgYmlnZ2VyIGRhdGEsIGl0IGNhbiBiZQp1c2VmdWwgdG8gc2VlIHRoZSBwcm9ncmVzcy4KCmBgYHtyIH0KZml0XzEgPC0gc3Rhbl9nbG0oa2lkX3Njb3JlIH4gbW9tX2hzLCBkYXRhPWtpZGlxLCByZWZyZXNoID0gMCkKcHJpbnQoZml0XzEpCmBgYAoKIyMjIyBBIHNpbmdsZSBjb250aW51b3VzIHByZWRpY3RvciAKCmBgYHtyIH0KZml0XzIgPC0gc3Rhbl9nbG0oa2lkX3Njb3JlIH4gbW9tX2lxLCBkYXRhPWtpZGlxLCByZWZyZXNoID0gMCkKcHJpbnQoZml0XzIpCmBgYAoKIyMjIyBEaXNwbGF5aW5nIGEgcmVncmVzc2lvbiBsaW5lIGFzIGEgZnVuY3Rpb24gb2Ygb25lIGlucHV0IHZhcmlhYmxlCgpgYGB7ciB9CnBsb3Qoa2lkaXEkbW9tX2lxLCBraWRpcSRraWRfc2NvcmUsIHhsYWI9Ik1vdGhlciBJUSBzY29yZSIsIHlsYWI9IkNoaWxkIHRlc3Qgc2NvcmUiKQphYmxpbmUoY29lZihmaXRfMikpCiMgb3IKI2FibGluZShjb2VmKGZpdF8yKVsxXSwgY29lZihmaXRfMilbMl0pCiMgb3IKI2N1cnZlKGNiaW5kKDEseCkgJSolIGNvZWYoZml0XzIpLCBhZGQ9VFJVRSkKYGBgCgojIyMjIGdncGxvdCB2ZXJzaW9uCgpgYGB7ciB9CmdncGxvdChraWRpcSwgYWVzKG1vbV9pcSwga2lkX3Njb3JlKSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0ID0gY29lZihmaXRfMilbMV0sIHNsb3BlID0gY29lZihmaXRfMilbMl0pICsKICBsYWJzKHggPSAiTW90aGVyIElRIHNjb3JlIiwgeSA9ICJDaGlsZCB0ZXN0IHNjb3JlIikKYGBgCgojIyBUd28gcHJlZGljdG9ycwoKIyMjIyBMaW5lYXIgcmVncmVzc2lvbgoKYGBge3IgfQpmaXRfMyA8LSBzdGFuX2dsbShraWRfc2NvcmUgfiBtb21faHMgKyBtb21faXEsIGRhdGE9a2lkaXEsIHJlZnJlc2ggPSAwKQpwcmludChmaXRfMykKYGBgCgojIyMjIEFsdGVybmF0aXZlIGRpc3BsYXkKCmBgYHtyIH0Kc3VtbWFyeShmaXRfMykKYGBgCgojIyMgR3JhcGhpY2FsIGRpc3BsYXlzIG9mIGRhdGEgYW5kIGZpdHRlZCBtb2RlbHMKCiMjIyMgVHdvIGZpdHRlZCByZWdyZXNzaW9uIGxpbmVzIC0tIG1vZGVsIHdpdGggbm8gaW50ZXJhY3Rpb24KCmBgYHtyIH0KY29sb3JzIDwtIGlmZWxzZShraWRpcSRtb21faHM9PTEsICJibGFjayIsICJncmF5IikKcGxvdChraWRpcSRtb21faXEsIGtpZGlxJGtpZF9zY29yZSwKICB4bGFiPSJNb3RoZXIgSVEgc2NvcmUiLCB5bGFiPSJDaGlsZCB0ZXN0IHNjb3JlIiwgY29sPWNvbG9ycywgcGNoPTIwKQpiX2hhdCA8LSBjb2VmKGZpdF8zKQphYmxpbmUoYl9oYXRbMV0gKyBiX2hhdFsyXSwgYl9oYXRbM10sIGNvbD0iYmxhY2siKQphYmxpbmUoYl9oYXRbMV0sIGJfaGF0WzNdLCBjb2w9ImdyYXkiKQpgYGAKCiMjIyMgZ2dwbG90IHZlcnNpb24KCmBgYHtyIH0KZ2dwbG90KGtpZGlxLCBhZXMobW9tX2lxLCBraWRfc2NvcmUpKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBmYWN0b3IobW9tX2hzKSksIHNob3cubGVnZW5kID0gRkFMU0UpICsKICBnZW9tX2FibGluZSgKICAgIGludGVyY2VwdCA9IGMoY29lZihmaXRfMylbMV0sIGNvZWYoZml0XzMpWzFdICsgY29lZihmaXRfMylbMl0pLAogICAgc2xvcGUgPSBjb2VmKGZpdF8zKVszXSwKICAgIGNvbG9yID0gYygiZ3JheSIsICJibGFjayIpKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoImdyYXkiLCAiYmxhY2siKSkgKwogIGxhYnMoeCA9ICJNb3RoZXIgSVEgc2NvcmUiLCB5ID0gIkNoaWxkIHRlc3Qgc2NvcmUiKQpgYGAKCiMjIyMgVHdvIGZpdHRlZCByZWdyZXNzaW9uIGxpbmVzIC0tIG1vZGVsIHdpdGggaW50ZXJhY3Rpb24KCmBgYHtyIH0KZml0XzQgPC0gc3Rhbl9nbG0oa2lkX3Njb3JlIH4gbW9tX2hzICsgbW9tX2lxICsgbW9tX2hzOm1vbV9pcSwgZGF0YT1raWRpcSwKICAgICAgICAgICAgICAgICAgcmVmcmVzaCA9IDApCnByaW50KGZpdF80KQpjb2xvcnMgPC0gaWZlbHNlKGtpZGlxJG1vbV9ocz09MSwgImJsYWNrIiwgImdyYXkiKQpwbG90KGtpZGlxJG1vbV9pcSwga2lkaXEka2lkX3Njb3JlLAogIHhsYWI9Ik1vdGhlciBJUSBzY29yZSIsIHlsYWI9IkNoaWxkIHRlc3Qgc2NvcmUiLCBjb2w9Y29sb3JzLCBwY2g9MjApCmJfaGF0IDwtIGNvZWYoZml0XzQpCmFibGluZShiX2hhdFsxXSArIGJfaGF0WzJdLCBiX2hhdFszXSArIGJfaGF0WzRdLCBjb2w9ImJsYWNrIikKYWJsaW5lKGJfaGF0WzFdLCBiX2hhdFszXSwgY29sPSJncmF5IikKYGBgCgojIyMjIGdncGxvdCB2ZXJzaW9uCgpgYGB7ciB9CmdncGxvdChraWRpcSwgYWVzKG1vbV9pcSwga2lkX3Njb3JlKSkgKwogIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gZmFjdG9yKG1vbV9ocykpLCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgZ2VvbV9hYmxpbmUoCiAgICBpbnRlcmNlcHQgPSBjKGNvZWYoZml0XzQpWzFdLCBzdW0oY29lZihmaXRfNClbMToyXSkpLAogICAgc2xvcGUgPSBjKGNvZWYoZml0XzQpWzNdLCBzdW0oY29lZihmaXRfNClbMzo0XSkpLAogICAgY29sb3IgPSBjKCJncmF5IiwgImJsYWNrIikpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiZ3JheSIsICJibGFjayIpKSArCiAgbGFicyh4ID0gIk1vdGhlciBJUSBzY29yZSIsIHkgPSAiQ2hpbGQgdGVzdCBzY29yZSIpCmBgYAoKIyMgRGlzcGxheWluZyB1bmNlcnRhaW50eSBpbiB0aGUgZml0dGVkIHJlZ3Jlc3Npb24KCiMjIyMgQSBzaW5nbGUgY29udGludW91cyBwcmVkaWN0b3IgCgpgYGB7ciB9CnByaW50KGZpdF8yKQpzaW1zXzIgPC0gYXMubWF0cml4KGZpdF8yKQpuX3NpbXNfMiA8LSBucm93KHNpbXNfMikKc3Vic2V0IDwtIHNhbXBsZShuX3NpbXNfMiwgMTApCnBsb3Qoa2lkaXEkbW9tX2lxLCBraWRpcSRraWRfc2NvcmUsCiAgICAgeGxhYj0iTW90aGVyIElRIHNjb3JlIiwgeWxhYj0iQ2hpbGQgdGVzdCBzY29yZSIpCmZvciAoaSBpbiBzdWJzZXQpewogIGFibGluZShzaW1zXzJbaSwxXSwgc2ltc18yW2ksMl0sIGNvbD0iZ3JheSIpCn0KYWJsaW5lKGNvZWYoZml0XzIpWzFdLCBjb2VmKGZpdF8yKVsyXSwgY29sPSJibGFjayIpCmBgYAoKZ2dwbG90IHZlcnNpb24KCmBgYHtyIH0KZ2dwbG90KGtpZGlxLCBhZXMobW9tX2lxLCBraWRfc2NvcmUpKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX2FibGluZSgKICAgIGludGVyY2VwdCA9IHNpbXNfMltzdWJzZXQsIDFdLAogICAgc2xvcGUgPSBzaW1zXzJbc3Vic2V0LCAyXSwKICAgIGNvbG9yID0gImdyYXkiLAogICAgc2l6ZSA9IDAuMjUpICsKICBnZW9tX2FibGluZSgKICAgIGludGVyY2VwdCA9IGNvZWYoZml0XzIpWzFdLAogICAgc2xvcGUgPSBjb2VmKGZpdF8yKVsyXSwKICAgIHNpemUgPSAwLjc1KSArCiAgbGFicyh4ID0gIk1vdGhlciBJUSBzY29yZSIsIHkgPSAiQ2hpbGQgdGVzdCBzY29yZSIpCmBgYAoKIyMjIyBUd28gcHJlZGljdG9ycyAKCmBgYHtyIH0Kc2ltc18zIDwtIGFzLm1hdHJpeChmaXRfMykKbl9zaW1zXzMgPC0gbnJvdyhzaW1zXzMpCgpgYGAKYGBge3IgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KaWYgKHNhdmVmaWdzKSBwZGYocm9vdCgiS2lkSVEvZmlncyIsImtpZGlxLmJldGFzaW0yLnBkZiIpLCBoZWlnaHQ9My41LCB3aWR0aD05KQpgYGAKYGBge3IgfQpwYXIobWFyPWMoMywzLDEsMyksIG1ncD1jKDEuNywgLjUsIDApLCB0Y2s9LS4wMSkKcGFyKG1mcm93PWMoMSwyKSkKcGxvdChraWRpcSRtb21faXEsIGtpZGlxJGtpZF9zY29yZSwgeGxhYj0iTW90aGVyIElRIHNjb3JlIiwgeWxhYj0iQ2hpbGQgdGVzdCBzY29yZSIsIGJ0eT0ibCIsIHBjaD0yMCwgeGF4dD0ibiIsIHlheHQ9Im4iKQpheGlzKDEsIHNlcSg4MCwgMTQwLCAyMCkpCmF4aXMoMiwgc2VxKDIwLCAxNDAsIDQwKSkKbW9tX2hzX2JhciA8LSBtZWFuKGtpZGlxJG1vbV9ocykKc3Vic2V0IDwtIHNhbXBsZShuX3NpbXNfMywgMTApCmZvciAoaSBpbiBzdWJzZXQpewogIGN1cnZlKGNiaW5kKDEsIG1vbV9oc19iYXIsIHgpICUqJSBzaW1zXzNbaSwxOjNdLCBsd2Q9LjUsCiAgICAgY29sPSJncmF5IiwgYWRkPVRSVUUpCn0KY3VydmUoY2JpbmQoMSwgbW9tX2hzX2JhciwgeCkgJSolIGNvZWYoZml0XzMpLCBjb2w9ImJsYWNrIiwgYWRkPVRSVUUpCmppdHQgPC0gcnVuaWYobnJvdyhraWRpcSksIC0uMDMsIC4wMykKcGxvdChraWRpcSRtb21faHMgKyBqaXR0LCBraWRpcSRraWRfc2NvcmUsIHhsYWI9Ik1vdGhlciBjb21wbGV0ZWQgaGlnaCBzY2hvb2wiLCB5bGFiPSJDaGlsZCB0ZXN0IHNjb3JlIiwgYnR5PSJsIiwgcGNoPTIwLCB4YXh0PSJuIiwgeWF4dD0ibiIpCmF4aXMoMSwgYygwLDEpKQpheGlzKDIsIHNlcSgyMCwgMTQwLCA0MCkpCm1vbV9pcV9iYXIgPC0gbWVhbihraWRpcSRtb21faXEpCmZvciAoaSBpbiBzdWJzZXQpewogIGN1cnZlKGNiaW5kKDEsIHgsIG1vbV9pcV9iYXIpICUqJSBzaW1zXzNbaSwxOjNdLCBsd2Q9LjUsCiAgICAgY29sPSJncmF5IiwgYWRkPVRSVUUpCn0KY3VydmUoY2JpbmQoMSwgeCwgbW9tX2lxX2JhcikgJSolIGNvZWYoZml0XzMpLCBjb2w9ImJsYWNrIiwgYWRkPVRSVUUpCmBgYApgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQppZiAoc2F2ZWZpZ3MpIGRldi5vZmYoKQpgYGAKCiMjIyMgQ2VudGVyIHByZWRpY3RvcnMgdG8gaGF2ZSB6ZXJvIG1lYW4KCmBgYHtyIH0Ka2lkaXEkY19tb21faHMgPC0ga2lkaXEkbW9tX2hzIC0gbWVhbihraWRpcSRtb21faHMpCmtpZGlxJGNfbW9tX2lxIDwtIGtpZGlxJG1vbV9pcSAtIG1lYW4oa2lkaXEkbW9tX2lxKQpmaXRfNGMgPC0gc3Rhbl9nbG0oa2lkX3Njb3JlIH4gY19tb21faHMgKyBjX21vbV9pcSArIGNfbW9tX2hzOmNfbW9tX2lxLAogICAgICAgICAgICAgICAgICAgZGF0YT1raWRpcSwgcmVmcmVzaCA9IDApCnByaW50KGZpdF80YykKYGBgCgojIyMjIENlbnRlciBwcmVkaWN0b3JzIGJhc2VkIG9uIGEgcmVmZXJlbmNlIHBvaW50CgpgYGB7ciB9CmtpZGlxJGMyX21vbV9ocyA8LSBraWRpcSRtb21faHMgLSAwLjUKa2lkaXEkYzJfbW9tX2lxIDwtIGtpZGlxJG1vbV9pcSAtIDEwMApmaXRfNGMyIDwtIHN0YW5fZ2xtKGtpZF9zY29yZSB+IGMyX21vbV9ocyArIGMyX21vbV9pcSArIGMyX21vbV9oczpjMl9tb21faXEsCiAgICAgICAgICAgICAgICAgICAgZGF0YT1raWRpcSwgcmVmcmVzaCA9IDApCnByaW50KGZpdF80YzIpCmBgYAoKIyMjIyBDZW50ZXIgYW5kIHNjYWxlIHByZWRpY3RvcnMgdG8gaGF2ZSB6ZXJvIG1lYW4gYW5kIHNkPTEvMgoKYGBge3IgfQpraWRpcSR6X21vbV9ocyA8LSAoa2lkaXEkbW9tX2hzIC0gbWVhbihraWRpcSRtb21faHMpKS8oMipzZChraWRpcSRtb21faHMpKQpraWRpcSR6X21vbV9pcSA8LSAoa2lkaXEkbW9tX2lxIC0gbWVhbihraWRpcSRtb21faXEpKS8oMipzZChraWRpcSRtb21faXEpKQpmaXRfNHogPC0gc3Rhbl9nbG0oa2lkX3Njb3JlIH4gel9tb21faHMgKyB6X21vbV9pcSArIHpfbW9tX2hzOnpfbW9tX2lxLAogICAgICAgICAgICAgICAgICAgZGF0YT1raWRpcSwgcmVmcmVzaCA9IDApCnByaW50KGZpdF80eikKYGBgCgojIyMjIFByZWRpY3QgdXNpbmcgd29ya2luZyBzdGF0dXMgb2YgbW90aGVyCgpgYGB7ciB9CmZpdF81IDwtIHN0YW5fZ2xtKGtpZF9zY29yZSB+IGFzLmZhY3Rvcihtb21fd29yayksIGRhdGE9a2lkaXEsIHJlZnJlc2ggPSAwKQpwcmludChmaXRfNSkKYGBgCgo=