Names - Distributions of names of American babies. See Chapter 2 in Regression and Other Stories.
Load packages
library("rprojroot")
root<-has_file(".ROS-Examples-root")$make_fix_file()
Load data
allnames <- read.csv(root("Names/data","allnames_clean.csv"))
girl <- as.vector(allnames$sex)=="F"
names <- as.vector(allnames$name)
columns <- colnames(allnames)
range <- (1:length(columns))[columns=="X1931"]:(1:length(columns))[columns=="X2000"]
years <- 1931:2000
colRenorm <- function(a){
a / matrix(colSums(a), nrow=nrow(a), ncol=ncol(a), byrow=TRUE)
}
counts <- as.matrix(allnames[,range])
counts.norm <- colRenorm(counts)
totals <- rowMeans(counts.norm)
counts.adj <- ifelse (counts==0, 2, counts)
counts.adj.norm <- colRenorm(counts.adj)/colSums(counts.adj)
Compute stats
N <- nrow(allnames)
stats.labels <- c("avg.year", "avg.pop", "max.pop", "ratio", "year.of.max.pop",
"volatility", "slope.1931.1965", "slope.1966.2000","slope.1931.2000",
"slope.1981.2000", "pop.2000","avg.year.2")
stats <- array(NA, c(N,length(stats.labels)))
dimnames(stats) <- list(names, stats.labels)
for (i in 1:N){
avg.year <- sum(years*counts.norm[i,])/sum(counts.norm[i,])
avg.year.2 <- sum(years*as.numeric(counts[i,]))/sum(as.numeric(counts[i,]))
avg.pop <- mean(counts.norm[i,])
max.pop <- max(counts.norm[i,])
ratio <- max(counts.adj.norm[i,])/ min(counts.adj.norm[i,])
year.of.max.pop <- min(years[counts.norm[i,]==max.pop])
logcounts <- log(counts.adj.norm[i,])
volatility <- sd(logcounts)
M1 <- lm(logcounts ~ I(years/10), subset=years<=1965)
M2 <- lm(logcounts ~ I(years/10), subset=years>=1966)
M3 <- lm(logcounts ~ I(years/10))
M4 <- lm(logcounts ~ I(years/10), subset=years>=1981)
slope.1931.1965 <- coef(M1)[2]
slope.1966.2000 <- coef(M2)[2]
slope.1931.2000 <- coef(M3)[2]
slope.1981.2000 <- coef(M4)[2]
pop.2000 <- counts.norm[i,years==2000]
stats[i,] <- unlist(lapply(stats.labels, get))
}
Helper function
name.subset <- function(subset, n){
if (n==0){
n <- length(years)
probs.remaining <- counts.norm
sample.1.raw <- rep(NA, n)
for (i in 1:n){
sample.1.raw[i] <- sample(1:N, 1, prob=ifelse(subset,probs.remaining[,i],0))
probs.remaining[sample.1.raw[i],] <- 0
}
}
else {
sample.1.raw <- sample(1:N, n, prob=ifelse(subset,totals,0))
}
year.of.max.pop.1 <- stats[sample.1.raw,"year.of.max.pop"]
sample.1 <- sample.1.raw[order(year.of.max.pop.1)]
a <- stats[sample.1, c("year.of.max.pop","avg.year.2","max.pop","ratio","slope.1931.2000","slope.1981.2000","pop.2000")]
return(a)
}
Helper plot function
namesplot <- function(a){
labels <- c("Peak year", "Avg year", "Peak\npopularity", "Ratio max/min\npopularity", "Avg trend,\n1931-2000", "Avg trend,\n1981-2000", "Popularity\nin 2000")
digits <- rep(0, 5)
is.log <- c(FALSE, FALSE, TRUE, TRUE, FALSE, FALSE, TRUE)
lo <- c(1930, 1930, -5, 0, -2, -2, -5)
hi <- c(2000, 2000, -1, 4, 2, 2, -1)
J <- ncol(a)
n <- nrow(a)
plot(c(-.6,J), c(-n,3), xlab="", ylab="", xaxt="n", yaxt="n", type="n", bty="n")
for (i in seq(0,-n,-6)){
polygon(c(-.6,J,J,-.6), rep(c(i-.5, max(i-3,-n)-.5), c(2,2)), col="gray90", border=NA)
}
text(-.1, -(1:n), dimnames(a)[[1]], adj=1, cex=.7)
for (j in 1:J){
if (is.log[j]){
x <- log10(a[,j])
text(c(j-.8,j-.5,j-.2), c(1,1,1), 10^seq(lo[j],hi[j],length=3), cex=.7)
}
else {
x <- a[,j]
text(c(j-.8,j-.5,j-.2), c(1,1,1), seq(lo[j],hi[j],length=3), cex=.7)
}
lines(c(j-.8,j-.2), c(0,0))
segments(c(j-.8,j-.5,j-.2), c(0,0,0), c(j-.8,j-.5,j-.2), c(.2,.2,.2))
lines(c(j-1,j-1), c(0,-n), col="gray")
text(j-.5, 3, labels[j], cex=.7)
points(j-.8 + .6*(x-lo[j])/(hi[j]-lo[j]), -(1:n), pch=20, cex=.6)
}
}
Plot stats
girls50 <- name.subset(girl, 50)
boys50 <- name.subset(!girl, 50)
par(mar=c(1,2,1,1))
namesplot(girls50)
par(mar=c(1,2,1,1))
namesplot(boys50)
girls70 <- name.subset(girl, 0)
boys70 <- name.subset(!girl, 0)
par(mar=c(1,2,1,1))
namesplot(girls70)
par(mar=c(1,2,1,1))
namesplot(boys70)
Restrict to top 1000
top1000 <- array(NA, c(N,length(years)))
for (i in 1:length(years)){
top1000[girl,i] <- counts[girl,i] >= rev(sort(counts[girl,i]))[1000]
top1000[!girl,i] <- counts[!girl,i] >= rev(sort(counts[!girl,i]))[1000]
}
evertop1000 <- rowSums(top1000) > 0
girls50new <- name.subset(girl, 50)
boys50new <- name.subset(!girl, 50)
par(mar=c(1,2,1,1))
namesplot(girls50new)
par(mar=c(1,2,1,1))
namesplot(boys50new)
Add new column
avg.year.2 <- rep(NA, 50)
names50 <- row.names(girls50new)
for (i in 1:50){
ok <- (1:N)[names==names50[i]&girl]
avg.year.2[i] <- stats[ok,"avg.year.2"]
}
girls50new <- cbind(girls50new[,1], avg.year.2, girls50new[,2:6])
colnames(girls50new)[1] <- "year.of.max.pop"
par(mar=c(1,2,1,1))
namesplot(girls50new)
avg.year.2 <- rep(NA, 50)
names50 <- row.names(boys50new)
for (i in 1:50){
ok <- (1:N)[names==names50[i]&!girl]
avg.year.2[i] <- stats[ok,"avg.year.2"]
}
boys50new <- cbind(boys50new[,1], avg.year.2, boys50new[,2:6])
colnames(boys50new)[1] <- "year.of.max.pop"
par(mar=c(1,2,1,1))
namesplot(boys50new)
LS0tCnRpdGxlOiAiUmVncmVzc2lvbiBhbmQgT3RoZXIgU3RvcmllczogTmFtZXMiCmF1dGhvcjogIkFuZHJldyBHZWxtYW4sIEplbm5pZmVyIEhpbGwsIEFraSBWZWh0YXJpIgpkYXRlOiAiYHIgZm9ybWF0KFN5cy5EYXRlKCkpYCIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICB0aGVtZTogcmVhZGFibGUKICAgIHRvYzogdHJ1ZQogICAgdG9jX2RlcHRoOiAyCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKLS0tCk5hbWVzIC0gRGlzdHJpYnV0aW9ucyBvZiBuYW1lcyBvZiBBbWVyaWNhbiBiYWJpZXMuIFNlZSBDaGFwdGVyIDIgaW4KUmVncmVzc2lvbiBhbmQgT3RoZXIgU3Rvcmllcy4KCi0tLS0tLS0tLS0tLS0KCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KG1lc3NhZ2U9RkFMU0UsIGVycm9yPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBjb21tZW50PU5BKQojIHN3aXRjaCB0aGlzIHRvIFRSVUUgdG8gc2F2ZSBmaWd1cmVzIGluIHNlcGFyYXRlIGZpbGVzCnNhdmVmaWdzIDwtIEZBTFNFCmBgYAoKIyMjIyBMb2FkIHBhY2thZ2VzCgpgYGB7ciB9CmxpYnJhcnkoInJwcm9qcm9vdCIpCnJvb3Q8LWhhc19maWxlKCIuUk9TLUV4YW1wbGVzLXJvb3QiKSRtYWtlX2ZpeF9maWxlKCkKYGBgCgojIyMjIExvYWQgZGF0YQoKYGBge3IgfQphbGxuYW1lcyA8LSByZWFkLmNzdihyb290KCJOYW1lcy9kYXRhIiwiYWxsbmFtZXNfY2xlYW4uY3N2IikpCmdpcmwgPC0gYXMudmVjdG9yKGFsbG5hbWVzJHNleCk9PSJGIgpuYW1lcyA8LSBhcy52ZWN0b3IoYWxsbmFtZXMkbmFtZSkKY29sdW1ucyA8LSBjb2xuYW1lcyhhbGxuYW1lcykKcmFuZ2UgPC0gKDE6bGVuZ3RoKGNvbHVtbnMpKVtjb2x1bW5zPT0iWDE5MzEiXTooMTpsZW5ndGgoY29sdW1ucykpW2NvbHVtbnM9PSJYMjAwMCJdCnllYXJzIDwtIDE5MzE6MjAwMApjb2xSZW5vcm0gPC0gZnVuY3Rpb24oYSl7CiAgYSAvIG1hdHJpeChjb2xTdW1zKGEpLCBucm93PW5yb3coYSksIG5jb2w9bmNvbChhKSwgYnlyb3c9VFJVRSkKfQpjb3VudHMgPC0gYXMubWF0cml4KGFsbG5hbWVzWyxyYW5nZV0pCmNvdW50cy5ub3JtIDwtIGNvbFJlbm9ybShjb3VudHMpCnRvdGFscyA8LSByb3dNZWFucyhjb3VudHMubm9ybSkKY291bnRzLmFkaiA8LSBpZmVsc2UgKGNvdW50cz09MCwgMiwgY291bnRzKQpjb3VudHMuYWRqLm5vcm0gPC0gY29sUmVub3JtKGNvdW50cy5hZGopL2NvbFN1bXMoY291bnRzLmFkaikKYGBgCgojIyMjIENvbXB1dGUgc3RhdHMKCmBgYHtyIH0KTiA8LSBucm93KGFsbG5hbWVzKQpzdGF0cy5sYWJlbHMgPC0gYygiYXZnLnllYXIiLCAiYXZnLnBvcCIsICJtYXgucG9wIiwgInJhdGlvIiwgInllYXIub2YubWF4LnBvcCIsCiAgInZvbGF0aWxpdHkiLCAic2xvcGUuMTkzMS4xOTY1IiwgInNsb3BlLjE5NjYuMjAwMCIsInNsb3BlLjE5MzEuMjAwMCIsCiAgInNsb3BlLjE5ODEuMjAwMCIsICJwb3AuMjAwMCIsImF2Zy55ZWFyLjIiKQpzdGF0cyA8LSBhcnJheShOQSwgYyhOLGxlbmd0aChzdGF0cy5sYWJlbHMpKSkKZGltbmFtZXMoc3RhdHMpIDwtIGxpc3QobmFtZXMsIHN0YXRzLmxhYmVscykKZm9yIChpIGluIDE6Til7CiAgYXZnLnllYXIgPC0gc3VtKHllYXJzKmNvdW50cy5ub3JtW2ksXSkvc3VtKGNvdW50cy5ub3JtW2ksXSkKICBhdmcueWVhci4yIDwtIHN1bSh5ZWFycyphcy5udW1lcmljKGNvdW50c1tpLF0pKS9zdW0oYXMubnVtZXJpYyhjb3VudHNbaSxdKSkKICBhdmcucG9wIDwtIG1lYW4oY291bnRzLm5vcm1baSxdKQogIG1heC5wb3AgPC0gbWF4KGNvdW50cy5ub3JtW2ksXSkKICByYXRpbyA8LSBtYXgoY291bnRzLmFkai5ub3JtW2ksXSkvIG1pbihjb3VudHMuYWRqLm5vcm1baSxdKQogIHllYXIub2YubWF4LnBvcCA8LSBtaW4oeWVhcnNbY291bnRzLm5vcm1baSxdPT1tYXgucG9wXSkKICBsb2djb3VudHMgPC0gbG9nKGNvdW50cy5hZGoubm9ybVtpLF0pCiAgdm9sYXRpbGl0eSA8LSBzZChsb2djb3VudHMpCiAgTTEgPC0gbG0obG9nY291bnRzIH4gSSh5ZWFycy8xMCksIHN1YnNldD15ZWFyczw9MTk2NSkKICBNMiA8LSBsbShsb2djb3VudHMgfiBJKHllYXJzLzEwKSwgc3Vic2V0PXllYXJzPj0xOTY2KQogIE0zIDwtIGxtKGxvZ2NvdW50cyB+IEkoeWVhcnMvMTApKQogIE00IDwtIGxtKGxvZ2NvdW50cyB+IEkoeWVhcnMvMTApLCBzdWJzZXQ9eWVhcnM+PTE5ODEpCiAgc2xvcGUuMTkzMS4xOTY1IDwtIGNvZWYoTTEpWzJdCiAgc2xvcGUuMTk2Ni4yMDAwIDwtIGNvZWYoTTIpWzJdCiAgc2xvcGUuMTkzMS4yMDAwIDwtIGNvZWYoTTMpWzJdCiAgc2xvcGUuMTk4MS4yMDAwIDwtIGNvZWYoTTQpWzJdCiAgcG9wLjIwMDAgPC0gY291bnRzLm5vcm1baSx5ZWFycz09MjAwMF0KICBzdGF0c1tpLF0gPC0gdW5saXN0KGxhcHBseShzdGF0cy5sYWJlbHMsIGdldCkpCn0KYGBgCgpIZWxwZXIgZnVuY3Rpb24KCmBgYHtyIH0KbmFtZS5zdWJzZXQgPC0gZnVuY3Rpb24oc3Vic2V0LCBuKXsKICBpZiAobj09MCl7CiAgICBuIDwtIGxlbmd0aCh5ZWFycykKICAgIHByb2JzLnJlbWFpbmluZyA8LSBjb3VudHMubm9ybQogICAgc2FtcGxlLjEucmF3IDwtIHJlcChOQSwgbikKICAgIGZvciAoaSBpbiAxOm4pewogICAgICBzYW1wbGUuMS5yYXdbaV0gPC0gc2FtcGxlKDE6TiwgMSwgcHJvYj1pZmVsc2Uoc3Vic2V0LHByb2JzLnJlbWFpbmluZ1ssaV0sMCkpCiAgICAgIHByb2JzLnJlbWFpbmluZ1tzYW1wbGUuMS5yYXdbaV0sXSA8LSAwCiAgICB9CiAgfQogIGVsc2UgewogICAgc2FtcGxlLjEucmF3IDwtIHNhbXBsZSgxOk4sIG4sIHByb2I9aWZlbHNlKHN1YnNldCx0b3RhbHMsMCkpCiAgfQogIHllYXIub2YubWF4LnBvcC4xIDwtIHN0YXRzW3NhbXBsZS4xLnJhdywieWVhci5vZi5tYXgucG9wIl0KICBzYW1wbGUuMSA8LSBzYW1wbGUuMS5yYXdbb3JkZXIoeWVhci5vZi5tYXgucG9wLjEpXQogIGEgPC0gc3RhdHNbc2FtcGxlLjEsIGMoInllYXIub2YubWF4LnBvcCIsImF2Zy55ZWFyLjIiLCJtYXgucG9wIiwicmF0aW8iLCJzbG9wZS4xOTMxLjIwMDAiLCJzbG9wZS4xOTgxLjIwMDAiLCJwb3AuMjAwMCIpXQogIHJldHVybihhKQp9CmBgYAoKSGVscGVyIHBsb3QgZnVuY3Rpb24KCmBgYHtyIH0KbmFtZXNwbG90IDwtIGZ1bmN0aW9uKGEpewogIGxhYmVscyA8LSBjKCJQZWFrIHllYXIiLCAiQXZnIHllYXIiLCAiUGVha1xucG9wdWxhcml0eSIsICJSYXRpbyBtYXgvbWluXG5wb3B1bGFyaXR5IiwgIkF2ZyB0cmVuZCxcbjE5MzEtMjAwMCIsICJBdmcgdHJlbmQsXG4xOTgxLTIwMDAiLCAiUG9wdWxhcml0eVxuaW4gMjAwMCIpCiAgZGlnaXRzIDwtIHJlcCgwLCA1KQogIGlzLmxvZyA8LSBjKEZBTFNFLCBGQUxTRSwgVFJVRSwgVFJVRSwgRkFMU0UsIEZBTFNFLCBUUlVFKQogIGxvIDwtIGMoMTkzMCwgMTkzMCwgLTUsIDAsIC0yLCAtMiwgLTUpCiAgaGkgPC0gYygyMDAwLCAyMDAwLCAtMSwgNCwgMiwgMiwgLTEpCiAgSiA8LSBuY29sKGEpCiAgbiA8LSBucm93KGEpCiAgcGxvdChjKC0uNixKKSwgYygtbiwzKSwgeGxhYj0iIiwgeWxhYj0iIiwgeGF4dD0ibiIsIHlheHQ9Im4iLCB0eXBlPSJuIiwgYnR5PSJuIikKICBmb3IgKGkgaW4gc2VxKDAsLW4sLTYpKXsKICAgIHBvbHlnb24oYygtLjYsSixKLC0uNiksIHJlcChjKGktLjUsIG1heChpLTMsLW4pLS41KSwgYygyLDIpKSwgY29sPSJncmF5OTAiLCBib3JkZXI9TkEpCiAgfQogIHRleHQoLS4xLCAtKDE6biksIGRpbW5hbWVzKGEpW1sxXV0sIGFkaj0xLCBjZXg9LjcpCiAgZm9yIChqIGluIDE6Sil7CiAgICBpZiAoaXMubG9nW2pdKXsKICAgICAgeCA8LSBsb2cxMChhWyxqXSkKICAgICAgdGV4dChjKGotLjgsai0uNSxqLS4yKSwgYygxLDEsMSksIDEwXnNlcShsb1tqXSxoaVtqXSxsZW5ndGg9MyksIGNleD0uNykKICAgIH0KICAgIGVsc2UgewogICAgICB4IDwtIGFbLGpdCiAgICAgIHRleHQoYyhqLS44LGotLjUsai0uMiksIGMoMSwxLDEpLCBzZXEobG9bal0saGlbal0sbGVuZ3RoPTMpLCBjZXg9LjcpCiAgICB9CiAgICBsaW5lcyhjKGotLjgsai0uMiksIGMoMCwwKSkKICAgIHNlZ21lbnRzKGMoai0uOCxqLS41LGotLjIpLCBjKDAsMCwwKSwgYyhqLS44LGotLjUsai0uMiksIGMoLjIsLjIsLjIpKQogICAgbGluZXMoYyhqLTEsai0xKSwgYygwLC1uKSwgY29sPSJncmF5IikKICAgIAogICAgdGV4dChqLS41LCAzLCBsYWJlbHNbal0sIGNleD0uNykKICAgIHBvaW50cyhqLS44ICsgLjYqKHgtbG9bal0pLyhoaVtqXS1sb1tqXSksIC0oMTpuKSwgcGNoPTIwLCBjZXg9LjYpCiAgfQp9CmBgYAoKIyMjIyBQbG90IHN0YXRzCgpgYGB7ciB9CmdpcmxzNTAgPC0gbmFtZS5zdWJzZXQoZ2lybCwgNTApCmJveXM1MCA8LSBuYW1lLnN1YnNldCghZ2lybCwgNTApCmBgYApgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQppZiAoc2F2ZWZpZ3MpIHBkZihyb290KCJOYW1lcy9maWdzIiwiZ2lybHM1MC5wZGYiKSwgaGVpZ2h0PTgsIHdpZHRoPTgpCmBgYApgYGB7ciB9CnBhcihtYXI9YygxLDIsMSwxKSkKbmFtZXNwbG90KGdpcmxzNTApCmBgYApgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQppZiAoc2F2ZWZpZ3MpIGRldi5vZmYoKQppZiAoc2F2ZWZpZ3MpIHBkZihyb290KCJOYW1lcy9maWdzIiwiYm95czUwLnBkZiIpLCBoZWlnaHQ9OCwgd2lkdGg9OCkKYGBgCmBgYHtyIH0KcGFyKG1hcj1jKDEsMiwxLDEpKQpuYW1lc3Bsb3QoYm95czUwKQpgYGAKYGBge3IgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KaWYgKHNhdmVmaWdzKSBkZXYub2ZmKCkKYGBgCmBgYHtyIH0KZ2lybHM3MCA8LSBuYW1lLnN1YnNldChnaXJsLCAwKQpib3lzNzAgPC0gbmFtZS5zdWJzZXQoIWdpcmwsIDApCmBgYApgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQppZiAoc2F2ZWZpZ3MpIHBkZihyb290KCJOYW1lcy9maWdzIiwiZ2lybHM3MC5wZGYiKSwgaGVpZ2h0PTEwLCB3aWR0aD04KQpgYGAKYGBge3IgfQpwYXIobWFyPWMoMSwyLDEsMSkpCm5hbWVzcGxvdChnaXJsczcwKQpgYGAKYGBge3IgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KaWYgKHNhdmVmaWdzKSBkZXYub2ZmKCkKaWYgKHNhdmVmaWdzKSBwZGYocm9vdCgiTmFtZXMvZmlncyIsImJveXM3MC5wZGYiKSwgaGVpZ2h0PTEwLCB3aWR0aD04KQpgYGAKYGBge3IgfQpwYXIobWFyPWMoMSwyLDEsMSkpCm5hbWVzcGxvdChib3lzNzApCmBgYApgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQppZiAoc2F2ZWZpZ3MpIGRldi5vZmYoKQpgYGAKCiMjIyMgUmVzdHJpY3QgdG8gdG9wIDEwMDAKCmBgYHtyIH0KdG9wMTAwMCA8LSBhcnJheShOQSwgYyhOLGxlbmd0aCh5ZWFycykpKQpmb3IgKGkgaW4gMTpsZW5ndGgoeWVhcnMpKXsKICB0b3AxMDAwW2dpcmwsaV0gPC0gY291bnRzW2dpcmwsaV0gPj0gcmV2KHNvcnQoY291bnRzW2dpcmwsaV0pKVsxMDAwXQogIHRvcDEwMDBbIWdpcmwsaV0gPC0gY291bnRzWyFnaXJsLGldID49IHJldihzb3J0KGNvdW50c1shZ2lybCxpXSkpWzEwMDBdCn0KZXZlcnRvcDEwMDAgPC0gcm93U3Vtcyh0b3AxMDAwKSA+IDAKCmdpcmxzNTBuZXcgPC0gbmFtZS5zdWJzZXQoZ2lybCwgNTApCmJveXM1MG5ldyA8LSBuYW1lLnN1YnNldCghZ2lybCwgNTApCmBgYApgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQppZiAoc2F2ZWZpZ3MpIHBkZihyb290KCJOYW1lcy9maWdzIiwiZ2lybHM1MG5ldy5wZGYiKSwgaGVpZ2h0PTgsIHdpZHRoPTgpCmBgYApgYGB7ciB9CnBhcihtYXI9YygxLDIsMSwxKSkKbmFtZXNwbG90KGdpcmxzNTBuZXcpCmBgYApgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQppZiAoc2F2ZWZpZ3MpIGRldi5vZmYoKQppZiAoc2F2ZWZpZ3MpIHBkZihyb290KCJOYW1lcy9maWdzIiwiYm95czUwbmV3LnBkZiIpLCBoZWlnaHQ9OCwgd2lkdGg9OCkKYGBgCmBgYHtyIH0KcGFyKG1hcj1jKDEsMiwxLDEpKQpuYW1lc3Bsb3QoYm95czUwbmV3KQpgYGAKYGBge3IgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KaWYgKHNhdmVmaWdzKSBkZXYub2ZmKCkKYGBgCgpBZGQgbmV3IGNvbHVtbgoKYGBge3IgfQphdmcueWVhci4yIDwtIHJlcChOQSwgNTApCm5hbWVzNTAgPC0gcm93Lm5hbWVzKGdpcmxzNTBuZXcpCmZvciAoaSBpbiAxOjUwKXsKICBvayA8LSAoMTpOKVtuYW1lcz09bmFtZXM1MFtpXSZnaXJsXQogIGF2Zy55ZWFyLjJbaV0gPC0gc3RhdHNbb2ssImF2Zy55ZWFyLjIiXQp9CmdpcmxzNTBuZXcgPC0gY2JpbmQoZ2lybHM1MG5ld1ssMV0sIGF2Zy55ZWFyLjIsIGdpcmxzNTBuZXdbLDI6Nl0pCmNvbG5hbWVzKGdpcmxzNTBuZXcpWzFdIDwtICJ5ZWFyLm9mLm1heC5wb3AiCmBgYApgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQppZiAoc2F2ZWZpZ3MpIHBkZihyb290KCJOYW1lcy9maWdzIiwiZ2lybHM1MG5ldy5wZGYiKSwgaGVpZ2h0PTgsIHdpZHRoPTkpCmBgYApgYGB7ciB9CnBhcihtYXI9YygxLDIsMSwxKSkKbmFtZXNwbG90KGdpcmxzNTBuZXcpCmBgYApgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQppZiAoc2F2ZWZpZ3MpIGRldi5vZmYoKQpgYGAKCgoKYGBge3IgfQphdmcueWVhci4yIDwtIHJlcChOQSwgNTApCm5hbWVzNTAgPC0gcm93Lm5hbWVzKGJveXM1MG5ldykKZm9yIChpIGluIDE6NTApewogIG9rIDwtICgxOk4pW25hbWVzPT1uYW1lczUwW2ldJiFnaXJsXQogIGF2Zy55ZWFyLjJbaV0gPC0gc3RhdHNbb2ssImF2Zy55ZWFyLjIiXQp9CmJveXM1MG5ldyA8LSBjYmluZChib3lzNTBuZXdbLDFdLCBhdmcueWVhci4yLCBib3lzNTBuZXdbLDI6Nl0pCmNvbG5hbWVzKGJveXM1MG5ldylbMV0gPC0gInllYXIub2YubWF4LnBvcCIKYGBgCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CmlmIChzYXZlZmlncykgcGRmKHJvb3QoIk5hbWVzL2ZpZ3MiLCJib3lzNTBuZXcucGRmIiksIGhlaWdodD04LCB3aWR0aD05KQpgYGAKYGBge3IgfQpwYXIobWFyPWMoMSwyLDEsMSkpCm5hbWVzcGxvdChib3lzNTBuZXcpCmBgYApgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQppZiAoc2F2ZWZpZ3MpIGRldi5vZmYoKQpgYGAKCg==