Nonlinear models (loess, spline, GP, and BART) and political attitudes as a function of age. See Chapter 22 in Regression and Other Stories.
Load packages
library("rprojroot")
root<-has_file(".ROS-Examples-root")$make_fix_file()
library("rstanarm")
# Enable parallel computation in stan_gamm4 used for splines and GPs
options(mc.cores = parallel::detectCores())
library("dbarts")
Define common plot functions
gay_plot <- function(fit=NULL, question=NULL, title=NULL, savefigs=FALSE) {
if (savefigs) pdf(root("Gay/figs",paste("gay", index, ".pdf", sep="")), height=4.5, width=6)
par(mar=c(3,3,1,1), mgp=c(1.7, .5, 0), tck=-.01)
plot(gay_sum[[j]]$age, gay_sum[[j]]$y/gay_sum[[j]]$n, ylim=c(0,.65), yaxs="i", xlab="Age", ylab=question, yaxt="n", bty="l", type="n", main=title)
axis(2, seq(0,1,.2), c("0","20%","40%","60%","80%","100%"))
symbols(gay_sum[[j]]$age, gay_sum[[j]]$y/gay_sum[[j]]$n, circles=sqrt(gay_sum[[j]]$n), inches=.1, add=TRUE, fg=if (is.null(fit)) "black" else "gray80", bg=if (is.null(fit)) "gray70" else "gray90")
if (!is.null(fit)) {
n_sims <- nrow(fit)
for (i in sample(n_sims, 20)){
lines(gay_sum[[j]]$age, fit[i,], lwd=.5, col="gray50")
}
lines(gay_sum[[j]]$age, colMeans(fit), lwd=2)
}
if (savefigs) dev.off()
index <<- index + 1
}
gay_plot_1 <- function(fit=NULL, question=NULL, title=NULL, k_bottom) {
age <- gay_sum[[j]]$age
y <- gay_sum[[j]]$y
n <- gay_sum[[j]]$n
plot(age, y/n, ylim=c(0,.65), yaxs="i", xlab= if (k==k_bottom) "Age" else "", ylab="", xaxt= if (k<k_bottom) "n", yaxt="n", bty="l", type="n", cex.lab=1.2, cex.axis=1.2)
axis(1, seq(20,80,20), rep("",4), cex.axis=1.2)
axis(2, seq(0,1,.1), rep("",11))
axis(2, seq(0,1,.2), c("0","20%","40%","60%","80%","100%"), cex.axis=1.2)
symbols(age, y/n, circles=sqrt(n), inches=.1, add=TRUE, fg="gray80", bg="gray90")
n_sims <- nrow(fit)
for (i in sample(n_sims, 20)){
lines(age, fit[i,], lwd=.5, col="gray50")
}
lines(age, colMeans(fit), lwd=2)
}
gay_plot_2 <- function(fit=NULL, question=NULL, title=NULL, m=NULL) {
ok <- gay_sum_2[[j]]$male == m
age <- gay_sum_2[[j]]$age[ok]
y <- gay_sum_2[[j]]$y[ok]
n <- gay_sum_2[[j]]$n[ok]
plot(age, y/n, ylim=c(0,.65), yaxs="i", xlab= if (k==4) "Age" else "", ylab="", xaxt= if (k<4) "n", yaxt="n", bty="l", type="n", cex.lab=1.4, cex.axis=1.4, main=if (m==0) "Women" else "Men")
axis(1, seq(20,80,20), rep("",4), cex.axis=1.4)
axis(2, seq(0,1,.1), rep("",11))
axis(2, seq(0,1,.2), c("0","20%","40%","60%","80%","100%"), cex.axis=1.4)
symbols(age, y/n, circles=sqrt(n), inches=.1, add=TRUE, fg="gray80", bg="gray90")
n_sims <- nrow(fit)
for (i in sample(n_sims, 20)){
lines(age, fit[i,ok], lwd=.5, col="gray50")
}
lines(age, colMeans(fit[,ok]), lwd=2)
}
Two different questions
question <- c("Support for same-sex marriage", "Do you know any gay people?")
variable <- c("gayFavorStateMarriage", "gayKnowSomeone")
index <- 0
Loop over two different questions
Prepare variables to store results
gay <- as.list(rep(NA, 2))
gay_sum <- as.list(rep(NA, 2))
gay_sum_2 <- as.list(rep(NA, 2))
gay_loess <- as.list(rep(NA, 2))
gay_spline <- as.list(rep(NA, 2))
gay_GP <- as.list(rep(NA, 2))
gay_bart <- as.list(rep(NA, 2))
gay_spline_2 <- as.list(rep(NA, 2))
for (j in 1:2){
# Prepare the data
gay[[j]] <- read.csv(root("Gay/data","naes04.csv"))
gay[[j]] <- gay[[j]][!is.na(gay[[j]][,"age"]) & !is.na(gay[[j]][,variable[j]]),]
gay[[j]]$age[gay[[j]]$age>90] <- 91
y <- as.character(gay[[j]][,variable[j]])
gay[[j]]$y <- ifelse(y=="Yes", 1, ifelse(y=="No", 0, NA))
gender <- as.character(gay[[j]][,"gender"])
gay[[j]]$male <- ifelse(gender=="Female", 0, ifelse(gender=="Male", 1, NA))
uniq_age <- sort(unique(gay[[j]]$age))
n_age <- length(uniq_age)
tab <- table(gay[[j]]$age, gay[[j]]$y)
y_sum <- as.vector(tab[,2])
n_sum <- as.vector(tab[,1] + tab[,2])
gay_sum[[j]] <- data.frame(n=n_sum, y=y_sum, age=uniq_age)
gay_sum_2[[j]] <- data.frame(array(NA, c(n_age*2, 4), dimnames=list(NULL, c("n", "y", "age", "male"))))
for (i1 in 1:n_age){
for (i2 in 0:1){
ok <- gay[[j]]$age==uniq_age[i1] & gay[[j]]$male==i2
n_sum_2 <- sum(ok)
y_sum_2 <- sum(gay[[j]]$y[ok])
age_sum_2 <- uniq_age[i1]
male_sum_2 <- i2
gay_sum_2[[j]][n_age*i2 + i1,] <- c(n_sum_2, y_sum_2, age_sum_2, male_sum_2)
}
}
# Make the plots
gay_plot(question=question[j], title="Raw data from a national survey", savefigs = savefigs)
# LOESS
gay_loess[[j]] <- loess(y ~ age, data=gay[[j]])
gay_loess_fit <- predict(gay_loess[[j]], data.frame(age=gay_sum[[j]]$age))
gay_loess_fit <- matrix(gay_loess_fit, nrow=100, ncol=length(gay_loess_fit), byrow=TRUE)
gay_plot(gay_loess_fit, question=question[j], title="Loess fit", savefigs = savefigs)
# Splines
gay_spline[[j]] <- stan_gamm4(I(y/n) ~ s(age), data=gay_sum[[j]], adapt_delta=0.99)
gay_spline_fit <- posterior_linpred(gay_spline[[j]], newdata=data.frame(age=gay_sum[[j]]$age))
gay_plot(gay_spline_fit, question=question[j], title="Spline fit and uncertainty", savefigs = savefigs)
# GP represented with splines
gay_GP[[j]] <- stan_gamm4(I(y/n) ~ age + s(age, bs="gp"), data=gay_sum[[j]], prior_smooth=student_t(df=4, scale=100), adapt_delta=0.99)
gay_GP_fit <- posterior_linpred(gay_GP[[j]], newdata=data.frame(age=gay_sum[[j]]$age))
gay_plot(gay_GP_fit, question=question[j], title="Gaussian process fit and uncertainty", savefigs = savefigs)
# BART
output <- capture.output(
gay_bart[[j]] <- bart(gay[[j]]$age, gay[[j]]$y, matrix(uniq_age), ntree = 20))
gay_bart_fit <- pnorm(gay_bart[[j]]$yhat.test)
gay_plot(gay_bart_fit, question=question[j], title="Bart fit and uncertainty", savefigs = savefigs)
# Another spline
gay_spline_2[[j]] <- stan_gamm4(I(y/n) ~ s(age, male), data=gay_sum_2[[j]])
}








2:
New graphs
par(mar=c(3,2,1,1), mgp=c(1.7, .5, 0), tck=-.01)
par(mfrow=c(1,2))
for (j in 1:2){
plot(gay_sum[[j]]$age, gay_sum[[j]]$y/gay_sum[[j]]$n, ylim=c(0,.69), yaxs="i", xlab="Age", ylab="", yaxt="n", bty="l", type="n", main=question[[j]])
axis(2, seq(0,1,.1), rep("",11))
axis(2, seq(0,1,.2), c("0","20%","40%","60%","80%","100%"))
symbols(gay_sum[[j]]$age, gay_sum[[j]]$y/gay_sum[[j]]$n, circles=sqrt(gay_sum[[j]]$n), inches=.1, add=TRUE, fg="black", bg="gray70")
}

par(mar=c(3,2,1,1), mgp=c(1.7, .5, 0), tck=-.01)
par(mfcol=c(4,2), oma=c(0,0,2.5,0))
for (j in 1:2){
k=1
gay_loess_fit <- predict(gay_loess[[j]], data.frame(age=gay_sum[[j]]$age))
gay_loess_fit <- matrix(gay_loess_fit, nrow=100, ncol=length(gay_loess_fit), byrow=TRUE)
gay_plot_1(gay_loess_fit, question=question[j], title="Loess fit", k_bottom=4)
k=2
gay_spline_fit <- posterior_linpred(gay_spline[[j]], newdata=data.frame(age=gay_sum[[j]]$age))
gay_plot_1(gay_spline_fit, question=question[j], title="Spline fit and uncertainty", k_bottom=4)
k=3
gay_GP_fit <- posterior_linpred(gay_GP[[j]], newdata=data.frame(age=gay_sum[[j]]$age))
gay_plot_1(gay_GP_fit, question="Support for same-sex marriage", title="Gaussian process fit and uncertainty", k_bottom=4)
k=4
gay_bart_fit <- pnorm(gay_bart[[j]]$yhat.test)
gay_plot_1(gay_bart_fit, question=question[j], title="Bart fit and uncertainty", k_bottom=4)
}
mtext(paste(question[[1]], question[[2]], sep=" "), side=3, line=1, outer=TRUE)

par(mar=c(3,2,1,1), mgp=c(1.7, .5, 0), tck=-.01)
par(mfcol=c(2,2))
for (j in 1:2){
gay_spline_2_fit <- posterior_linpred(gay_spline_2[[j]], newdata=data.frame(age=gay_sum_2[[j]]$age, male=gay_sum_2[[j]]$male))
for (m in 0:1){
gay_plot_2(gay_spline_2_fit, question=question[j], title="2-dimensional spline fit", m=m)
}
}

par(mar=c(3,2,1,1), mgp=c(1.7, .5, 0), tck=-.01)
par(mfcol=c(2,2), oma=c(0,0,2.5,0))
for (j in 1:2){
k=1
gay_loess_fit <- predict(gay_loess[[j]], data.frame(age=gay_sum[[j]]$age))
gay_loess_fit <- matrix(gay_loess_fit, nrow=100, ncol=length(gay_loess_fit), byrow=TRUE)
gay_plot_1(gay_loess_fit, question=question[j], title="Loess fit", k_bottom=2)
k=2
gay_spline_fit <- posterior_linpred(gay_spline[[j]], newdata=data.frame(age=gay_sum[[j]]$age))
gay_plot_1(gay_spline_fit, question=question[j], title="Spline fit and uncertainty", k_bottom=2)
k=3
}
mtext(paste(question[[1]], question[[2]], sep=" "), side=3, line=1, outer=TRUE)

LS0tCnRpdGxlOiAiUmVncmVzc2lvbiBhbmQgT3RoZXIgU3RvcmllczogR2F5IgphdXRob3I6ICJBbmRyZXcgR2VsbWFuLCBBa2kgVmVodGFyaSIKZGF0ZTogImByIGZvcm1hdChTeXMuRGF0ZSgpKWAiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdGhlbWU6IHJlYWRhYmxlCiAgICB0b2M6IHRydWUKICAgIHRvY19kZXB0aDogMgogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCi0tLQpOb25saW5lYXIgbW9kZWxzIChsb2Vzcywgc3BsaW5lLCBHUCwgYW5kIEJBUlQpIGFuZCBwb2xpdGljYWwKYXR0aXR1ZGVzIGFzIGEgZnVuY3Rpb24gb2YgYWdlLiBTZWUgQ2hhcHRlciAyMiBpbiBSZWdyZXNzaW9uIGFuZApPdGhlciBTdG9yaWVzLgoKLS0tLS0tLS0tLS0tLQoKCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQobWVzc2FnZT1GQUxTRSwgZXJyb3I9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGNvbW1lbnQ9TkEpCiMgc3dpdGNoIHRoaXMgdG8gVFJVRSB0byBzYXZlIGZpZ3VyZXMgaW4gc2VwYXJhdGUgZmlsZXMKc2F2ZWZpZ3MgPC0gRkFMU0UKYGBgCgojIyMjIExvYWQgcGFja2FnZXMKCmBgYHtyfQpsaWJyYXJ5KCJycHJvanJvb3QiKQpyb290PC1oYXNfZmlsZSgiLlJPUy1FeGFtcGxlcy1yb290IikkbWFrZV9maXhfZmlsZSgpCmxpYnJhcnkoInJzdGFuYXJtIikKIyBFbmFibGUgcGFyYWxsZWwgY29tcHV0YXRpb24gaW4gc3Rhbl9nYW1tNCB1c2VkIGZvciBzcGxpbmVzIGFuZCBHUHMKb3B0aW9ucyhtYy5jb3JlcyA9IHBhcmFsbGVsOjpkZXRlY3RDb3JlcygpKQpsaWJyYXJ5KCJkYmFydHMiKQpgYGAKCiMjIyMgRGVmaW5lIGNvbW1vbiBwbG90IGZ1bmN0aW9ucwoKYGBge3J9CmdheV9wbG90IDwtIGZ1bmN0aW9uKGZpdD1OVUxMLCBxdWVzdGlvbj1OVUxMLCB0aXRsZT1OVUxMLCBzYXZlZmlncz1GQUxTRSkgewogIGlmIChzYXZlZmlncykgcGRmKHJvb3QoIkdheS9maWdzIixwYXN0ZSgiZ2F5IiwgaW5kZXgsICIucGRmIiwgc2VwPSIiKSksIGhlaWdodD00LjUsIHdpZHRoPTYpCiAgcGFyKG1hcj1jKDMsMywxLDEpLCBtZ3A9YygxLjcsIC41LCAwKSwgdGNrPS0uMDEpCiAgcGxvdChnYXlfc3VtW1tqXV0kYWdlLCBnYXlfc3VtW1tqXV0keS9nYXlfc3VtW1tqXV0kbiwgeWxpbT1jKDAsLjY1KSwgeWF4cz0iaSIsIHhsYWI9IkFnZSIsIHlsYWI9cXVlc3Rpb24sIHlheHQ9Im4iLCBidHk9ImwiLCB0eXBlPSJuIiwgbWFpbj10aXRsZSkKICBheGlzKDIsIHNlcSgwLDEsLjIpLCBjKCIwIiwiMjAlIiwiNDAlIiwiNjAlIiwiODAlIiwiMTAwJSIpKQogIHN5bWJvbHMoZ2F5X3N1bVtbal1dJGFnZSwgZ2F5X3N1bVtbal1dJHkvZ2F5X3N1bVtbal1dJG4sIGNpcmNsZXM9c3FydChnYXlfc3VtW1tqXV0kbiksIGluY2hlcz0uMSwgYWRkPVRSVUUsIGZnPWlmIChpcy5udWxsKGZpdCkpICJibGFjayIgZWxzZSAiZ3JheTgwIiwgYmc9aWYgKGlzLm51bGwoZml0KSkgImdyYXk3MCIgZWxzZSAiZ3JheTkwIikKICBpZiAoIWlzLm51bGwoZml0KSkgewogICAgbl9zaW1zIDwtIG5yb3coZml0KQogICAgZm9yIChpIGluIHNhbXBsZShuX3NpbXMsIDIwKSl7CiAgICAgIGxpbmVzKGdheV9zdW1bW2pdXSRhZ2UsIGZpdFtpLF0sIGx3ZD0uNSwgY29sPSJncmF5NTAiKQogICAgfQogICAgbGluZXMoZ2F5X3N1bVtbal1dJGFnZSwgY29sTWVhbnMoZml0KSwgbHdkPTIpCiAgfQogIGlmIChzYXZlZmlncykgZGV2Lm9mZigpCiAgaW5kZXggPDwtIGluZGV4ICsgMQp9CgpnYXlfcGxvdF8xIDwtIGZ1bmN0aW9uKGZpdD1OVUxMLCBxdWVzdGlvbj1OVUxMLCB0aXRsZT1OVUxMLCBrX2JvdHRvbSkgewogIGFnZSA8LSBnYXlfc3VtW1tqXV0kYWdlCiAgeSA8LSBnYXlfc3VtW1tqXV0keQogIG4gPC0gZ2F5X3N1bVtbal1dJG4KICBwbG90KGFnZSwgeS9uLCB5bGltPWMoMCwuNjUpLCB5YXhzPSJpIiwgeGxhYj0gaWYgKGs9PWtfYm90dG9tKSAiQWdlIiBlbHNlICIiLCB5bGFiPSIiLCB4YXh0PSBpZiAoazxrX2JvdHRvbSkgIm4iLCB5YXh0PSJuIiwgYnR5PSJsIiwgdHlwZT0ibiIsIGNleC5sYWI9MS4yLCBjZXguYXhpcz0xLjIpCiAgYXhpcygxLCBzZXEoMjAsODAsMjApLCByZXAoIiIsNCksIGNleC5heGlzPTEuMikKICBheGlzKDIsIHNlcSgwLDEsLjEpLCByZXAoIiIsMTEpKQogIGF4aXMoMiwgc2VxKDAsMSwuMiksIGMoIjAiLCIyMCUiLCI0MCUiLCI2MCUiLCI4MCUiLCIxMDAlIiksIGNleC5heGlzPTEuMikKICBzeW1ib2xzKGFnZSwgeS9uLCBjaXJjbGVzPXNxcnQobiksIGluY2hlcz0uMSwgYWRkPVRSVUUsIGZnPSJncmF5ODAiLCBiZz0iZ3JheTkwIikKICBuX3NpbXMgPC0gbnJvdyhmaXQpCiAgZm9yIChpIGluIHNhbXBsZShuX3NpbXMsIDIwKSl7CiAgICBsaW5lcyhhZ2UsIGZpdFtpLF0sIGx3ZD0uNSwgY29sPSJncmF5NTAiKQogIH0KICBsaW5lcyhhZ2UsIGNvbE1lYW5zKGZpdCksIGx3ZD0yKQp9CgpnYXlfcGxvdF8yIDwtIGZ1bmN0aW9uKGZpdD1OVUxMLCBxdWVzdGlvbj1OVUxMLCB0aXRsZT1OVUxMLCBtPU5VTEwpIHsKICBvayA8LSBnYXlfc3VtXzJbW2pdXSRtYWxlID09IG0KICBhZ2UgPC0gZ2F5X3N1bV8yW1tqXV0kYWdlW29rXQogIHkgPC0gZ2F5X3N1bV8yW1tqXV0keVtva10KICBuIDwtIGdheV9zdW1fMltbal1dJG5bb2tdCiAgcGxvdChhZ2UsIHkvbiwgeWxpbT1jKDAsLjY1KSwgeWF4cz0iaSIsIHhsYWI9IGlmIChrPT00KSAiQWdlIiBlbHNlICIiLCB5bGFiPSIiLCB4YXh0PSBpZiAoazw0KSAibiIsIHlheHQ9Im4iLCBidHk9ImwiLCB0eXBlPSJuIiwgY2V4LmxhYj0xLjQsIGNleC5heGlzPTEuNCwgbWFpbj1pZiAobT09MCkgIldvbWVuIiBlbHNlICJNZW4iKQogIGF4aXMoMSwgc2VxKDIwLDgwLDIwKSwgcmVwKCIiLDQpLCBjZXguYXhpcz0xLjQpCiAgYXhpcygyLCBzZXEoMCwxLC4xKSwgcmVwKCIiLDExKSkKICBheGlzKDIsIHNlcSgwLDEsLjIpLCBjKCIwIiwiMjAlIiwiNDAlIiwiNjAlIiwiODAlIiwiMTAwJSIpLCBjZXguYXhpcz0xLjQpCiAgc3ltYm9scyhhZ2UsIHkvbiwgY2lyY2xlcz1zcXJ0KG4pLCBpbmNoZXM9LjEsIGFkZD1UUlVFLCBmZz0iZ3JheTgwIiwgYmc9ImdyYXk5MCIpCiAgbl9zaW1zIDwtIG5yb3coZml0KQogIGZvciAoaSBpbiBzYW1wbGUobl9zaW1zLCAyMCkpewogICAgbGluZXMoYWdlLCBmaXRbaSxva10sIGx3ZD0uNSwgY29sPSJncmF5NTAiKQogIH0KICBsaW5lcyhhZ2UsIGNvbE1lYW5zKGZpdFssb2tdKSwgbHdkPTIpCn0KYGBgCgojIyMjIFR3byBkaWZmZXJlbnQgcXVlc3Rpb25zCgpgYGB7cn0KcXVlc3Rpb24gPC0gYygiU3VwcG9ydCBmb3Igc2FtZS1zZXggbWFycmlhZ2UiLCAiRG8geW91IGtub3cgYW55IGdheSBwZW9wbGU/IikKdmFyaWFibGUgPC0gYygiZ2F5RmF2b3JTdGF0ZU1hcnJpYWdlIiwgImdheUtub3dTb21lb25lIikKaW5kZXggPC0gMApgYGAKCiMjIyMgTG9vcCBvdmVyIHR3byBkaWZmZXJlbnQgcXVlc3Rpb25zClByZXBhcmUgdmFyaWFibGVzIHRvIHN0b3JlIHJlc3VsdHMKCmBgYHtyfQpnYXkgPC0gYXMubGlzdChyZXAoTkEsIDIpKQpnYXlfc3VtIDwtIGFzLmxpc3QocmVwKE5BLCAyKSkKZ2F5X3N1bV8yIDwtIGFzLmxpc3QocmVwKE5BLCAyKSkKZ2F5X2xvZXNzIDwtIGFzLmxpc3QocmVwKE5BLCAyKSkKZ2F5X3NwbGluZSA8LSBhcy5saXN0KHJlcChOQSwgMikpCmdheV9HUCA8LSBhcy5saXN0KHJlcChOQSwgMikpCmdheV9iYXJ0IDwtIGFzLmxpc3QocmVwKE5BLCAyKSkKZ2F5X3NwbGluZV8yIDwtIGFzLmxpc3QocmVwKE5BLCAyKSkKZm9yIChqIGluIDE6Mil7CiAgCiAgIyBQcmVwYXJlIHRoZSBkYXRhCiAgZ2F5W1tqXV0gPC0gcmVhZC5jc3Yocm9vdCgiR2F5L2RhdGEiLCJuYWVzMDQuY3N2IikpCiAgZ2F5W1tqXV0gPC0gZ2F5W1tqXV1bIWlzLm5hKGdheVtbal1dWywiYWdlIl0pICYgIWlzLm5hKGdheVtbal1dWyx2YXJpYWJsZVtqXV0pLF0KICBnYXlbW2pdXSRhZ2VbZ2F5W1tqXV0kYWdlPjkwXSA8LSA5MQogIHkgPC0gYXMuY2hhcmFjdGVyKGdheVtbal1dWyx2YXJpYWJsZVtqXV0pCiAgZ2F5W1tqXV0keSA8LSBpZmVsc2UoeT09IlllcyIsIDEsIGlmZWxzZSh5PT0iTm8iLCAwLCBOQSkpCiAgZ2VuZGVyIDwtIGFzLmNoYXJhY3RlcihnYXlbW2pdXVssImdlbmRlciJdKQogIGdheVtbal1dJG1hbGUgPC0gaWZlbHNlKGdlbmRlcj09IkZlbWFsZSIsIDAsIGlmZWxzZShnZW5kZXI9PSJNYWxlIiwgMSwgTkEpKQogIAogIHVuaXFfYWdlIDwtIHNvcnQodW5pcXVlKGdheVtbal1dJGFnZSkpCiAgbl9hZ2UgPC0gbGVuZ3RoKHVuaXFfYWdlKQogIHRhYiA8LSB0YWJsZShnYXlbW2pdXSRhZ2UsIGdheVtbal1dJHkpCiAgeV9zdW0gPC0gYXMudmVjdG9yKHRhYlssMl0pCiAgbl9zdW0gPC0gYXMudmVjdG9yKHRhYlssMV0gKyB0YWJbLDJdKQogIGdheV9zdW1bW2pdXSA8LSBkYXRhLmZyYW1lKG49bl9zdW0sIHk9eV9zdW0sIGFnZT11bmlxX2FnZSkKICBnYXlfc3VtXzJbW2pdXSA8LSBkYXRhLmZyYW1lKGFycmF5KE5BLCBjKG5fYWdlKjIsIDQpLCBkaW1uYW1lcz1saXN0KE5VTEwsIGMoIm4iLCAieSIsICJhZ2UiLCAibWFsZSIpKSkpCiAgZm9yIChpMSBpbiAxOm5fYWdlKXsKICAgIGZvciAoaTIgaW4gMDoxKXsKICAgICAgb2sgPC0gZ2F5W1tqXV0kYWdlPT11bmlxX2FnZVtpMV0gJiBnYXlbW2pdXSRtYWxlPT1pMgogICAgICBuX3N1bV8yIDwtIHN1bShvaykKICAgICAgeV9zdW1fMiA8LSBzdW0oZ2F5W1tqXV0keVtva10pCiAgICAgIGFnZV9zdW1fMiA8LSB1bmlxX2FnZVtpMV0KICAgICAgbWFsZV9zdW1fMiA8LSBpMgogICAgICBnYXlfc3VtXzJbW2pdXVtuX2FnZSppMiArIGkxLF0gPC0gYyhuX3N1bV8yLCB5X3N1bV8yLCBhZ2Vfc3VtXzIsIG1hbGVfc3VtXzIpCiAgICB9CiAgfQogIAogICMgTWFrZSB0aGUgcGxvdHMKICBnYXlfcGxvdChxdWVzdGlvbj1xdWVzdGlvbltqXSwgdGl0bGU9IlJhdyBkYXRhIGZyb20gYSBuYXRpb25hbCBzdXJ2ZXkiLCBzYXZlZmlncyA9IHNhdmVmaWdzKQoKICAjIExPRVNTCiAgZ2F5X2xvZXNzW1tqXV0gPC0gbG9lc3MoeSB+IGFnZSwgZGF0YT1nYXlbW2pdXSkKICBnYXlfbG9lc3NfZml0IDwtIHByZWRpY3QoZ2F5X2xvZXNzW1tqXV0sIGRhdGEuZnJhbWUoYWdlPWdheV9zdW1bW2pdXSRhZ2UpKQogIGdheV9sb2Vzc19maXQgPC0gbWF0cml4KGdheV9sb2Vzc19maXQsIG5yb3c9MTAwLCBuY29sPWxlbmd0aChnYXlfbG9lc3NfZml0KSwgYnlyb3c9VFJVRSkKICBnYXlfcGxvdChnYXlfbG9lc3NfZml0LCBxdWVzdGlvbj1xdWVzdGlvbltqXSwgdGl0bGU9IkxvZXNzIGZpdCIsIHNhdmVmaWdzID0gc2F2ZWZpZ3MpCgogICMgU3BsaW5lcwogIGdheV9zcGxpbmVbW2pdXSA8LSBzdGFuX2dhbW00KEkoeS9uKSB+IHMoYWdlKSwgZGF0YT1nYXlfc3VtW1tqXV0sIGFkYXB0X2RlbHRhPTAuOTkpCiAgZ2F5X3NwbGluZV9maXQgPC0gcG9zdGVyaW9yX2xpbnByZWQoZ2F5X3NwbGluZVtbal1dLCBuZXdkYXRhPWRhdGEuZnJhbWUoYWdlPWdheV9zdW1bW2pdXSRhZ2UpKQogIGdheV9wbG90KGdheV9zcGxpbmVfZml0LCBxdWVzdGlvbj1xdWVzdGlvbltqXSwgdGl0bGU9IlNwbGluZSBmaXQgYW5kIHVuY2VydGFpbnR5Iiwgc2F2ZWZpZ3MgPSBzYXZlZmlncykKCiAgIyBHUCByZXByZXNlbnRlZCB3aXRoIHNwbGluZXMKICBnYXlfR1BbW2pdXSA8LSBzdGFuX2dhbW00KEkoeS9uKSB+IGFnZSArIHMoYWdlLCBicz0iZ3AiKSwgZGF0YT1nYXlfc3VtW1tqXV0sIHByaW9yX3Ntb290aD1zdHVkZW50X3QoZGY9NCwgc2NhbGU9MTAwKSwgYWRhcHRfZGVsdGE9MC45OSkKICBnYXlfR1BfZml0IDwtIHBvc3Rlcmlvcl9saW5wcmVkKGdheV9HUFtbal1dLCBuZXdkYXRhPWRhdGEuZnJhbWUoYWdlPWdheV9zdW1bW2pdXSRhZ2UpKQogIGdheV9wbG90KGdheV9HUF9maXQsIHF1ZXN0aW9uPXF1ZXN0aW9uW2pdLCB0aXRsZT0iR2F1c3NpYW4gcHJvY2VzcyBmaXQgYW5kIHVuY2VydGFpbnR5Iiwgc2F2ZWZpZ3MgPSBzYXZlZmlncykKICAjIEJBUlQKICBvdXRwdXQgPC0gY2FwdHVyZS5vdXRwdXQoCiAgICBnYXlfYmFydFtbal1dIDwtIGJhcnQoZ2F5W1tqXV0kYWdlLCBnYXlbW2pdXSR5LCBtYXRyaXgodW5pcV9hZ2UpLCBudHJlZSA9IDIwKSkKICBnYXlfYmFydF9maXQgPC0gcG5vcm0oZ2F5X2JhcnRbW2pdXSR5aGF0LnRlc3QpCiAgZ2F5X3Bsb3QoZ2F5X2JhcnRfZml0LCBxdWVzdGlvbj1xdWVzdGlvbltqXSwgdGl0bGU9IkJhcnQgZml0IGFuZCB1bmNlcnRhaW50eSIsIHNhdmVmaWdzID0gc2F2ZWZpZ3MpCgogICMgQW5vdGhlciBzcGxpbmUKICBnYXlfc3BsaW5lXzJbW2pdXSA8LSBzdGFuX2dhbW00KEkoeS9uKSB+IHMoYWdlLCBtYWxlKSwgZGF0YT1nYXlfc3VtXzJbW2pdXSkKfQpgYGAKCiMjIyMgTmV3IGdyYXBocwoKYGBge3IgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KaWYgKHNhdmVmaWdzKSBwZGYocm9vdCgiR2F5L2ZpZ3MiLCJnYXkxMC5wZGYiKSwgaGVpZ2h0PTQsIHdpZHRoPTEwKQpgYGAKYGBge3J9CnBhcihtYXI9YygzLDIsMSwxKSwgbWdwPWMoMS43LCAuNSwgMCksIHRjaz0tLjAxKQpwYXIobWZyb3c9YygxLDIpKQpmb3IgKGogaW4gMToyKXsKICBwbG90KGdheV9zdW1bW2pdXSRhZ2UsIGdheV9zdW1bW2pdXSR5L2dheV9zdW1bW2pdXSRuLCB5bGltPWMoMCwuNjkpLCB5YXhzPSJpIiwgeGxhYj0iQWdlIiwgeWxhYj0iIiwgeWF4dD0ibiIsIGJ0eT0ibCIsIHR5cGU9Im4iLCBtYWluPXF1ZXN0aW9uW1tqXV0pCiAgYXhpcygyLCBzZXEoMCwxLC4xKSwgcmVwKCIiLDExKSkKICBheGlzKDIsIHNlcSgwLDEsLjIpLCBjKCIwIiwiMjAlIiwiNDAlIiwiNjAlIiwiODAlIiwiMTAwJSIpKQogIHN5bWJvbHMoZ2F5X3N1bVtbal1dJGFnZSwgZ2F5X3N1bVtbal1dJHkvZ2F5X3N1bVtbal1dJG4sIGNpcmNsZXM9c3FydChnYXlfc3VtW1tqXV0kbiksIGluY2hlcz0uMSwgYWRkPVRSVUUsIGZnPSJibGFjayIsIGJnPSJncmF5NzAiKQp9CmBgYApgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQppZiAoc2F2ZWZpZ3MpIGRldi5vZmYoKQoKYGBgCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CmlmIChzYXZlZmlncykgcGRmKHJvb3QoIkdheS9maWdzIiwiZ2F5MTEucGRmIiksIGhlaWdodD0xMCwgd2lkdGg9NykKYGBgCmBgYHtyfQpwYXIobWFyPWMoMywyLDEsMSksIG1ncD1jKDEuNywgLjUsIDApLCB0Y2s9LS4wMSkKcGFyKG1mY29sPWMoNCwyKSwgb21hPWMoMCwwLDIuNSwwKSkKZm9yIChqIGluIDE6Mil7CiAgaz0xCiAgZ2F5X2xvZXNzX2ZpdCA8LSBwcmVkaWN0KGdheV9sb2Vzc1tbal1dLCBkYXRhLmZyYW1lKGFnZT1nYXlfc3VtW1tqXV0kYWdlKSkKICBnYXlfbG9lc3NfZml0IDwtIG1hdHJpeChnYXlfbG9lc3NfZml0LCBucm93PTEwMCwgbmNvbD1sZW5ndGgoZ2F5X2xvZXNzX2ZpdCksIGJ5cm93PVRSVUUpCiAgZ2F5X3Bsb3RfMShnYXlfbG9lc3NfZml0LCBxdWVzdGlvbj1xdWVzdGlvbltqXSwgdGl0bGU9IkxvZXNzIGZpdCIsIGtfYm90dG9tPTQpCiAgaz0yCiAgZ2F5X3NwbGluZV9maXQgPC0gcG9zdGVyaW9yX2xpbnByZWQoZ2F5X3NwbGluZVtbal1dLCBuZXdkYXRhPWRhdGEuZnJhbWUoYWdlPWdheV9zdW1bW2pdXSRhZ2UpKQogIGdheV9wbG90XzEoZ2F5X3NwbGluZV9maXQsIHF1ZXN0aW9uPXF1ZXN0aW9uW2pdLCB0aXRsZT0iU3BsaW5lIGZpdCBhbmQgdW5jZXJ0YWludHkiLCBrX2JvdHRvbT00KQogIGs9MwogIGdheV9HUF9maXQgPC0gcG9zdGVyaW9yX2xpbnByZWQoZ2F5X0dQW1tqXV0sIG5ld2RhdGE9ZGF0YS5mcmFtZShhZ2U9Z2F5X3N1bVtbal1dJGFnZSkpCiAgZ2F5X3Bsb3RfMShnYXlfR1BfZml0LCBxdWVzdGlvbj0iU3VwcG9ydCBmb3Igc2FtZS1zZXggbWFycmlhZ2UiLCB0aXRsZT0iR2F1c3NpYW4gcHJvY2VzcyBmaXQgYW5kIHVuY2VydGFpbnR5Iiwga19ib3R0b209NCkKICBrPTQKICBnYXlfYmFydF9maXQgPC0gcG5vcm0oZ2F5X2JhcnRbW2pdXSR5aGF0LnRlc3QpCiAgZ2F5X3Bsb3RfMShnYXlfYmFydF9maXQsIHF1ZXN0aW9uPXF1ZXN0aW9uW2pdLCB0aXRsZT0iQmFydCBmaXQgYW5kIHVuY2VydGFpbnR5Iiwga19ib3R0b209NCkKfQptdGV4dChwYXN0ZShxdWVzdGlvbltbMV1dLCBxdWVzdGlvbltbMl1dLCBzZXA9IiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiKSwgc2lkZT0zLCBsaW5lPTEsIG91dGVyPVRSVUUpCmBgYApgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQppZiAoc2F2ZWZpZ3MpIGRldi5vZmYoKQoKCmBgYApgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQppZiAoc2F2ZWZpZ3MpIHBkZihyb290KCJHYXkvZmlncyIsImdheTEyLnBkZiIpLCBoZWlnaHQ9OCwgd2lkdGg9MTApCmBgYApgYGB7cn0KcGFyKG1hcj1jKDMsMiwxLDEpLCBtZ3A9YygxLjcsIC41LCAwKSwgdGNrPS0uMDEpCnBhcihtZmNvbD1jKDIsMikpCmZvciAoaiBpbiAxOjIpewogIGdheV9zcGxpbmVfMl9maXQgPC0gcG9zdGVyaW9yX2xpbnByZWQoZ2F5X3NwbGluZV8yW1tqXV0sIG5ld2RhdGE9ZGF0YS5mcmFtZShhZ2U9Z2F5X3N1bV8yW1tqXV0kYWdlLCBtYWxlPWdheV9zdW1fMltbal1dJG1hbGUpKQogIGZvciAobSBpbiAwOjEpeyAgICAgICAgICAgICAgICAgICAgCiAgICBnYXlfcGxvdF8yKGdheV9zcGxpbmVfMl9maXQsIHF1ZXN0aW9uPXF1ZXN0aW9uW2pdLCB0aXRsZT0iMi1kaW1lbnNpb25hbCBzcGxpbmUgZml0IiwgbT1tKQogIH0KfQpgYGAKYGBge3IgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KaWYgKHNhdmVmaWdzKSBkZXYub2ZmKCkKCgpgYGAKYGBge3IgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KaWYgKHNhdmVmaWdzKSBwZGYocm9vdCgiR2F5L2ZpZ3MiLCJnYXkxMy5wZGYiKSwgaGVpZ2h0PTUuNSwgd2lkdGg9NykKYGBgCmBgYHtyfQpwYXIobWFyPWMoMywyLDEsMSksIG1ncD1jKDEuNywgLjUsIDApLCB0Y2s9LS4wMSkKcGFyKG1mY29sPWMoMiwyKSwgb21hPWMoMCwwLDIuNSwwKSkKZm9yIChqIGluIDE6Mil7CiAgaz0xCiAgZ2F5X2xvZXNzX2ZpdCA8LSBwcmVkaWN0KGdheV9sb2Vzc1tbal1dLCBkYXRhLmZyYW1lKGFnZT1nYXlfc3VtW1tqXV0kYWdlKSkKICBnYXlfbG9lc3NfZml0IDwtIG1hdHJpeChnYXlfbG9lc3NfZml0LCBucm93PTEwMCwgbmNvbD1sZW5ndGgoZ2F5X2xvZXNzX2ZpdCksIGJ5cm93PVRSVUUpCiAgZ2F5X3Bsb3RfMShnYXlfbG9lc3NfZml0LCBxdWVzdGlvbj1xdWVzdGlvbltqXSwgdGl0bGU9IkxvZXNzIGZpdCIsIGtfYm90dG9tPTIpCiAgaz0yCiAgZ2F5X3NwbGluZV9maXQgPC0gcG9zdGVyaW9yX2xpbnByZWQoZ2F5X3NwbGluZVtbal1dLCBuZXdkYXRhPWRhdGEuZnJhbWUoYWdlPWdheV9zdW1bW2pdXSRhZ2UpKQogIGdheV9wbG90XzEoZ2F5X3NwbGluZV9maXQsIHF1ZXN0aW9uPXF1ZXN0aW9uW2pdLCB0aXRsZT0iU3BsaW5lIGZpdCBhbmQgdW5jZXJ0YWludHkiLCBrX2JvdHRvbT0yKQogIGs9Mwp9Cm10ZXh0KHBhc3RlKHF1ZXN0aW9uW1sxXV0sIHF1ZXN0aW9uW1syXV0sIHNlcD0iICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIpLCBzaWRlPTMsIGxpbmU9MSwgb3V0ZXI9VFJVRSkKYGBgCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CmlmIChzYXZlZmlncykgZGV2Lm9mZigpCmBgYAoK