Last letters of names - Distributions of last letters of names of American babies. See Chapter 2 in Regression and Other Stories.
Plot data
namelength <- nchar(names)
lastletter <- substr(names, namelength, namelength)
firstletter <- substr(names, 1, 1)
discrete.histogram <- function (x, prob, prob2=NULL,
xlab="x", ylab="Probability", xaxs.label=NULL, yaxs.label=NULL, bar.width=NULL, ...){
if (length(x) != length(prob)) stop()
x.values <- sort (unique(x))
n.x.values <- length (x.values)
gaps <- x.values[2:n.x.values] - x.values[1:(n.x.values-1)]
if (is.null(bar.width)) bar.width <- min(gaps)*.2
par(mar=c(3,3,2,2), mgp=c(1.7,.3,0), tck=0)
plot(range(x)+c(-1,1)*bar.width, c(0,max(prob)),
xlab=xlab, ylab=ylab, xaxs="i", xaxt="n", yaxs="i",
yaxt=ifelse(is.null(yaxs.label),"s","n"), bty="l", type="n", ...)
if (is.null(xaxs.label)){
axis(1, x.values)
}
else {
n <- length(xaxs.label[[1]])
even <- (1:n)[(1:n)%%2==0]
odd <- (1:n)[(1:n)%%2==1]
axis(1, xaxs.label[[1]][even], xaxs.label[[2]][even], cex.axis=.9)
axis(1, xaxs.label[[1]][odd], xaxs.label[[2]][odd], cex.axis=.9)
}
if (!is.null(yaxs.label)){
axis(2, yaxs.label[[1]], yaxs.label[[2]], tck=-.02)
}
for (i in 1:length(x)){
polygon(x[i] + c(-1,-1,1,1)*bar.width/2, c(0,prob[i],prob[i],0),
border="gray10", col="gray10")
if (!is.null(prob2))
polygon(x[i] + c(-1,-1,1,1)*bar.width/10, c(0,prob2[i],prob2[i],0),
border="red", col="black")
}
}
for (year in c(1900,1950,2010)){
thisyear <- allnames[,paste("X",year,sep="")]
lastletter.by.sex <- array(NA, c(26,2))
firstletter.by.sex <- array(NA, c(26,2))
for (i in 1:26){
lastletter.by.sex[i,1] <- sum(thisyear[lastletter==letters[i] & girl])
lastletter.by.sex[i,2] <- sum(thisyear[lastletter==letters[i] & !girl])
firstletter.by.sex[i,1] <- sum(thisyear[firstletter==LETTERS[i] & girl])
firstletter.by.sex[i,2] <- sum(thisyear[firstletter==LETTERS[i] & !girl])
}
if (savefigs) pdf(root("Names/figs", paste("boys", year, ".pdf", sep="")),
height=3, width=4.5)
discrete.histogram(1:26, 100*(lastletter.by.sex[,2])/sum(lastletter.by.sex[,2]), xaxs.label=list(1:26,letters), yaxs.label=list(seq(0,30,10),seq(0,30,10)), xlab="", ylab="Percentage of boys born", main=paste("Last letter of boys' names in", year), cex.axis=.9, cex.main=.9, bar.width=.8)
for (y in c(10,20,30)) abline (y,0,col="gray",lwd=.5)
if (savefigs) dev.off()
if (savefigs) pdf(root("Names/figs", paste("girls", year, ".pdf", sep="")),
height=3, width=4.5)
discrete.histogram(1:26, 100*(lastletter.by.sex[,1])/sum(lastletter.by.sex[,1]), xaxs.label=list(1:26,letters), yaxs.label=list(seq(0,30,10),seq(0,30,10)), xlab="", ylab="Percentage of girls born", main=paste("Last letter of girls' names in", year), cex.main=.9)
if (savefigs) dev.off()
}
yrs <- 1880:2010
n.yrs <- length(yrs)
lastletterfreqs <- array(NA, c(n.yrs,26,2))
firstletterfreqs <- array(NA, c(n.yrs,26,2))
dimnames(lastletterfreqs) <- list(yrs, letters, c("girls","boys"))
dimnames(firstletterfreqs) <- list(yrs, letters, c("girls","boys"))
for (i in 1:n.yrs){
thisyear <- allnames[,paste("X",yrs[i],sep="")]
for (j in 1:26){
lastletterfreqs[i,j,1] <- sum(thisyear[lastletter==letters[j] & girl])
lastletterfreqs[i,j,2] <- sum(thisyear[lastletter==letters[j] & !girl])
firstletterfreqs[i,j,1] <- sum(thisyear[firstletter==LETTERS[j] & girl])
firstletterfreqs[i,j,2] <- sum(thisyear[firstletter==LETTERS[j] & !girl])
}
for (k in 1:2){
lastletterfreqs[i,,k] <- lastletterfreqs[i,,k]/sum(lastletterfreqs[i,,k])
firstletterfreqs[i,,k] <- firstletterfreqs[i,,k]/sum(firstletterfreqs[i,,k])
}
}
par(mar=c(2,2,1,1), mgp=c(1.7,.3,0), tck=-.01, oma=c(0,0,2,0), mfrow=c(2,3))
popular <- rev(order(lastletterfreqs[1,,2]))[1:6]
for (k in 1:length(popular)){
plot(range(yrs), c(0,50), type="n", xlab="", ylab="", bty="l", xaxt="n", yaxt="n", yaxs="i", xaxs="i")
axis(1, seq(1900,2000,50))
axis(2, seq(0,40,20), paste(seq(0,40,20), "%", sep=""))
mtext(paste(". . .", LETTERS[popular[k]]), side=3, line=-1, cex=.8)
for (j in 1:26){
maxfreq <- max(lastletterfreqs[,j,2])
best <- (1:n.yrs)[lastletterfreqs[,j,2]==maxfreq]
lines(yrs, 100*lastletterfreqs[,j,2], col=if (j==popular[k]) "black" else "darkgray", lwd=if (j==popular[k]) 1 else .5)
}
}
mtext("Last letters of boys' names", side=3, outer=TRUE, line=.5)
par(mar=c(2,3,2,1), mgp=c(1.7,.3,0), tck=-.01)
popular <- c(14,25,4)
width <- rep(.5,26)
type <- rep(1,26)
width[popular] <- c(2,3,3)
type[popular] <- c(1,3,2)
plot(range(yrs), c(0,41), type="n", xlab="", ylab="Percentage of all boys' names that year", bty="l", xaxt="n", yaxt="n", yaxs="i", xaxs="i")
axis(1, seq(1900,2000,50))
axis(2, seq(0,40,20), paste(seq(0,40,20), "%", sep=""))
for (j in 1:26){
maxfreq <- max(lastletterfreqs[,j,2])
best <- (1:n.yrs)[lastletterfreqs[,j,2]==maxfreq]
lines(yrs, 100*lastletterfreqs[,j,2], col="black", lwd=width[j], lty=type[j])
}
text(2000, 35, "N")
text(1935, 20, "D")
text(1975, 15, "Y")
mtext("Last letters of boys' names", side=3, line=.5)
par(mar=c(2,3,2,1), mgp=c(1.7,.3,0), tck=-.01)
popular <- c(14,25,4)
plot(range(yrs), c(0,41), type="n", xlab="", ylab="Percentage", bty="l", xaxt="n", yaxt="n", yaxs="i", xaxs="i")
axis(1, seq(1900,2000,50))
axis(2, seq(0,40,20), paste(seq(0,40,20), "%", sep=""))
for (j in 1:26){
maxfreq <- max(firstletterfreqs[,j,2])
best <- (1:n.yrs)[firstletterfreqs[,j,2]==maxfreq]
if (j %in% popular){
lines(yrs, 100*firstletterfreqs[,j,2], col="black", lwd=2)
}
else{
lines(yrs, 100*firstletterfreqs[,j,2], col="black", lwd=.5)
}
}
mtext("First letters of boys' names", side=3, line=.5)
Stuff for NYT column
dim(lastletterfreqs[,,2])
[1] 131 26
round(lastletterfreqs[yrs>2005,,2], 2) # 35% end in n
a b c d e f g h i j k l m n o p q r
2006 0.02 0.02 0.01 0.03 0.07 0 0 0.05 0.02 0 0.02 0.08 0.02 0.35 0.05 0 0 0.09
2007 0.02 0.02 0.01 0.02 0.07 0 0 0.05 0.02 0 0.02 0.08 0.02 0.36 0.05 0 0 0.09
2008 0.02 0.02 0.01 0.02 0.07 0 0 0.05 0.02 0 0.02 0.07 0.02 0.36 0.05 0 0 0.09
2009 0.02 0.02 0.01 0.02 0.07 0 0 0.05 0.02 0 0.02 0.07 0.02 0.36 0.04 0 0 0.09
2010 0.01 0.02 0.01 0.02 0.07 0 0 0.05 0.02 0 0.02 0.07 0.02 0.36 0.04 0 0 0.09
s t u v w x y z
2006 0.07 0.02 0 0 0.02 0.01 0.06 0
2007 0.07 0.02 0 0 0.02 0.01 0.06 0
2008 0.07 0.02 0 0 0.02 0.01 0.06 0
2009 0.07 0.02 0 0 0.02 0.01 0.06 0
2010 0.07 0.02 0 0 0.02 0.01 0.06 0
round(lastletterfreqs[yrs>2005,,1], 2) # 38% of all girls end in a
a b c d e f g h i j k l m n o p q r s t u v w x
2006 0.40 0 0 0 0.17 0 0 0.07 0.03 0 0 0.03 0 0.13 0 0 0 0.02 0.01 0.01 0 0 0 0
2007 0.39 0 0 0 0.17 0 0 0.07 0.03 0 0 0.03 0 0.14 0 0 0 0.02 0.01 0.01 0 0 0 0
2008 0.38 0 0 0 0.18 0 0 0.07 0.03 0 0 0.03 0 0.14 0 0 0 0.02 0.01 0.01 0 0 0 0
2009 0.38 0 0 0 0.18 0 0 0.08 0.03 0 0 0.03 0 0.14 0 0 0 0.02 0.01 0.01 0 0 0 0
2010 0.38 0 0 0 0.18 0 0 0.08 0.03 0 0 0.03 0 0.14 0 0 0 0.02 0.01 0.01 0 0 0 0
y z
2006 0.12 0
2007 0.12 0
2008 0.12 0
2009 0.12 0
2010 0.12 0
1950
round(lastletterfreqs[yrs==1950,,2], 2) # 14% end in n (tied with d, s, and y as most popular)
a b c d e f g h i j k l m n o p
0.00 0.00 0.00 0.15 0.09 0.00 0.00 0.04 0.00 0.00 0.03 0.10 0.04 0.14 0.01 0.01
q r s t u v w x y z
0.00 0.03 0.14 0.07 0.00 0.00 0.00 0.00 0.14 0.00
round(lastletterfreqs[yrs==1950,,1], 2) # 34% of all girls end in a
a b c d e f g h i j k l m n o p
0.34 0.00 0.00 0.00 0.22 0.00 0.00 0.05 0.01 0.00 0.00 0.04 0.00 0.15 0.00 0.00
q r s t u v w x y z
0.00 0.00 0.02 0.02 0.00 0.00 0.00 0.00 0.14 0.00
1900
round(lastletterfreqs[yrs==1900,,2], 2) # 14% end in n (tied with d, s, and y as most popular)
a b c d e f g h i j k l m n o p
0.01 0.00 0.00 0.10 0.15 0.00 0.00 0.04 0.00 0.00 0.04 0.06 0.07 0.14 0.01 0.00
q r s t u v w x y z
0.00 0.07 0.13 0.07 0.00 0.00 0.01 0.00 0.08 0.00
round(lastletterfreqs[yrs==1900,,1], 2) # 34% of all girls end in a
a b c d e f g h i j k l m n o p
0.30 0.00 0.00 0.01 0.36 0.00 0.00 0.05 0.00 0.00 0.00 0.05 0.00 0.06 0.00 0.00
q r s t u v w x y z
0.00 0.01 0.03 0.02 0.00 0.00 0.00 0.00 0.10 0.00
Most popular names in any given year
boy.names <- names[!girl]
girl.names <- names[girl]
for (year in c(1900,1950,2010)){
thisyear <- allnames[,paste("X",year,sep="")]
boy.totals <- thisyear[!girl]
boy.proportions <- boy.totals/sum(boy.totals)
index <- rev(order(boy.proportions))
popular.names <- boy.names[index]
popularity <- boy.proportions[index]
print(year)
print(popular.names[1:30])
round(popularity[1:30],3)
print(c(sum(popularity[1:10]), sum(popularity[1:20]), sum(popularity[1:30]), sum(popularity[1:50]), sum(popularity[1:100])))
}
[1] 1900
[1] "John" "William" "James" "George" "Charles" "Robert"
[7] "Joseph" "Frank" "Edward" "Henry" "Thomas" "Walter"
[13] "Harry" "Willie" "Arthur" "Albert" "Fred" "Clarence"
[19] "Paul" "Harold" "Roy" "Joe" "Raymond" "Richard"
[25] "Charlie" "Louis" "Jack" "Earl" "Carl" "Ernest"
[1] 0.3433763 0.4666782 0.5401941 0.6310008 0.7491101
[1] 1950
[1] "James" "Robert" "John" "Michael" "David" "William" "Richard"
[8] "Thomas" "Charles" "Gary" "Larry" "Ronald" "Joseph" "Donald"
[15] "Kenneth" "Steven" "Dennis" "Paul" "Stephen" "George" "Daniel"
[22] "Edward" "Mark" "Jerry" "Gregory" "Bruce" "Roger" "Douglas"
[29] "Frank" "Terry"
[1] 0.3384098 0.4769608 0.5572918 0.6489598 0.7768003
[1] 2010
[1] "Jacob" "Ethan" "Michael" "Jayden" "William"
[6] "Alexander" "Noah" "Daniel" "Aiden" "Anthony"
[11] "Joshua" "Mason" "Christopher" "Andrew" "David"
[16] "Matthew" "Logan" "Elijah" "James" "Joseph"
[21] "Gabriel" "Benjamin" "Ryan" "Samuel" "Jackson"
[26] "John" "Nathan" "Jonathan" "Christian" "Liam"
[1] 0.08965114 0.16407578 0.22514563 0.32102364 0.46921162
n_percentage <- 100*lastletterfreqs[,14,2]
topten_percentage <- array(NA, c(length(yrs), 2))
for (i in 1:length(yrs)){
thisyear <- allnames[,paste("X",yrs[i],sep="")]
boy.totals <- thisyear[!girl]
boy.proportions <- boy.totals/sum(boy.totals)
index <- rev(order(boy.proportions))
popular.names <- boy.names[index]
popularity <- boy.proportions[index]
topten_percentage[i,2] <- 100*sum(popularity[1:10])
girl.totals <- thisyear[girl]
girl.proportions <- girl.totals/sum(girl.totals)
index <- rev(order(girl.proportions))
popular.names <- girl.names[index]
popularity <- girl.proportions[index]
topten_percentage[i,1] <- 100*sum(popularity[1:10])
}
par(mar=c(4,2,1,0), mgp=c(1.3,.2,0), tck=-.02)
plot(yrs, n_percentage, type="l", xaxt="n", yaxt="n", xaxs="i", yaxs="i", ylim=c(0,45), bty="l", xlab="Year", ylab="", cex.lab=.8)
axis(1, c(1900,1950,2000), cex.axis=.8)
axis(2, c(0,20,40), c("0%","20%","40%"), cex.axis=.8)
mtext("Percentage of new boys' names each year ending in 'n'", cex=.8)
mtext("Source: Social Security Administration, courtesy of Laura Wattenberg", 1, 2.5, cex=.5, adj=0)
par(mar=c(4,2,1,0), mgp=c(1.3,.2,0), tck=-.02)
plot(yrs, topten_percentage[,2], type="l", xaxt="n", yaxt="n", xaxs="i", yaxs="i", ylim=c(0,45), bty="l", xlab="Year", ylab="", cex.lab=.8)
lines(yrs, topten_percentage[,1])
axis(1, c(1900,1950,2000), cex.axis=.8)
axis(2, c(0,20,40), c("0%","20%","40%"), cex.axis=.8)
text(1902, 35, "Boys", cex=.75, adj=0)
text(1911, 20, "Girls", cex=.75, adj=0)
mtext("Total popularity of top ten names each year, by sex", cex=.8)
mtext("Source: Social Security Administration, courtesy of Laura Wattenberg", 1, 2.5, cex=.5, adj=0)
LS0tCnRpdGxlOiAiUmVncmVzc2lvbiBhbmQgT3RoZXIgU3RvcmllczogTGFzdCBsZXR0ZXJzIG9mIG5hbWVzIgphdXRob3I6ICJBbmRyZXcgR2VsbWFuLCBKZW5uaWZlciBIaWxsLCBBa2kgVmVodGFyaSIKZGF0ZTogImByIGZvcm1hdChTeXMuRGF0ZSgpKWAiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdGhlbWU6IHJlYWRhYmxlCiAgICB0b2M6IHRydWUKICAgIHRvY19kZXB0aDogMgogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCi0tLQpMYXN0IGxldHRlcnMgb2YgbmFtZXMgLSBEaXN0cmlidXRpb25zIG9mIGxhc3QgbGV0dGVycyBvZiBuYW1lcyBvZgpBbWVyaWNhbiBiYWJpZXMuIFNlZSBDaGFwdGVyIDIgaW4gUmVncmVzc2lvbiBhbmQgT3RoZXIgU3Rvcmllcy4KCi0tLS0tLS0tLS0tLS0KCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KG1lc3NhZ2U9RkFMU0UsIGVycm9yPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBjb21tZW50PU5BKQojIHN3aXRjaCB0aGlzIHRvIFRSVUUgdG8gc2F2ZSBmaWd1cmVzIGluIHNlcGFyYXRlIGZpbGVzCnNhdmVmaWdzIDwtIEZBTFNFCmBgYAoKIyMjIyBMb2FkIHBhY2thZ2VzCgpgYGB7ciB9CmxpYnJhcnkoInJwcm9qcm9vdCIpCnJvb3Q8LWhhc19maWxlKCIuUk9TLUV4YW1wbGVzLXJvb3QiKSRtYWtlX2ZpeF9maWxlKCkKYGBgCgojIyMjIExvYWQgZGF0YQoKYGBge3IgfQphbGxuYW1lcyA8LSByZWFkLmNzdihyb290KCJOYW1lcy9kYXRhIiwiYWxsbmFtZXNfY2xlYW4uY3N2IikpCmdpcmwgPC0gYXMudmVjdG9yKGFsbG5hbWVzJHNleCk9PSJGIgpuYW1lcyA8LSBhcy52ZWN0b3IoYWxsbmFtZXMkbmFtZSkKYGBgCgojIyMjIFBsb3QgZGF0YQoKYGBge3IgfQpuYW1lbGVuZ3RoIDwtIG5jaGFyKG5hbWVzKQpsYXN0bGV0dGVyIDwtIHN1YnN0cihuYW1lcywgbmFtZWxlbmd0aCwgbmFtZWxlbmd0aCkKZmlyc3RsZXR0ZXIgPC0gc3Vic3RyKG5hbWVzLCAxLCAxKQoKZGlzY3JldGUuaGlzdG9ncmFtIDwtIGZ1bmN0aW9uICh4LCBwcm9iLCBwcm9iMj1OVUxMLAogICAgeGxhYj0ieCIsIHlsYWI9IlByb2JhYmlsaXR5IiwgeGF4cy5sYWJlbD1OVUxMLCB5YXhzLmxhYmVsPU5VTEwsIGJhci53aWR0aD1OVUxMLCAuLi4pewogIGlmIChsZW5ndGgoeCkgIT0gbGVuZ3RoKHByb2IpKSBzdG9wKCkKICB4LnZhbHVlcyA8LSBzb3J0ICh1bmlxdWUoeCkpCiAgbi54LnZhbHVlcyA8LSBsZW5ndGggKHgudmFsdWVzKQogIGdhcHMgPC0geC52YWx1ZXNbMjpuLngudmFsdWVzXSAtIHgudmFsdWVzWzE6KG4ueC52YWx1ZXMtMSldCiAgaWYgKGlzLm51bGwoYmFyLndpZHRoKSkgYmFyLndpZHRoIDwtIG1pbihnYXBzKSouMgogIHBhcihtYXI9YygzLDMsMiwyKSwgbWdwPWMoMS43LC4zLDApLCB0Y2s9MCkKICBwbG90KHJhbmdlKHgpK2MoLTEsMSkqYmFyLndpZHRoLCBjKDAsbWF4KHByb2IpKSwKICAgIHhsYWI9eGxhYiwgeWxhYj15bGFiLCB4YXhzPSJpIiwgeGF4dD0ibiIsICB5YXhzPSJpIiwKICAgIHlheHQ9aWZlbHNlKGlzLm51bGwoeWF4cy5sYWJlbCksInMiLCJuIiksIGJ0eT0ibCIsIHR5cGU9Im4iLCAuLi4pCiAgaWYgKGlzLm51bGwoeGF4cy5sYWJlbCkpewogICAgYXhpcygxLCB4LnZhbHVlcykKICB9CiAgZWxzZSB7CiAgICBuIDwtIGxlbmd0aCh4YXhzLmxhYmVsW1sxXV0pCiAgICBldmVuIDwtICgxOm4pWygxOm4pJSUyPT0wXQogICAgb2RkIDwtICgxOm4pWygxOm4pJSUyPT0xXQogICAgYXhpcygxLCB4YXhzLmxhYmVsW1sxXV1bZXZlbl0sIHhheHMubGFiZWxbWzJdXVtldmVuXSwgY2V4LmF4aXM9LjkpCiAgICBheGlzKDEsIHhheHMubGFiZWxbWzFdXVtvZGRdLCB4YXhzLmxhYmVsW1syXV1bb2RkXSwgY2V4LmF4aXM9LjkpCiAgfQogIGlmICghaXMubnVsbCh5YXhzLmxhYmVsKSl7CiAgICBheGlzKDIsIHlheHMubGFiZWxbWzFdXSwgeWF4cy5sYWJlbFtbMl1dLCB0Y2s9LS4wMikKICB9CiAgZm9yIChpIGluIDE6bGVuZ3RoKHgpKXsKICAgIHBvbHlnb24oeFtpXSArIGMoLTEsLTEsMSwxKSpiYXIud2lkdGgvMiwgYygwLHByb2JbaV0scHJvYltpXSwwKSwKICAgICAgYm9yZGVyPSJncmF5MTAiLCBjb2w9ImdyYXkxMCIpCiAgICBpZiAoIWlzLm51bGwocHJvYjIpKQogICAgICBwb2x5Z29uKHhbaV0gKyBjKC0xLC0xLDEsMSkqYmFyLndpZHRoLzEwLCBjKDAscHJvYjJbaV0scHJvYjJbaV0sMCksCiAgICAgICAgYm9yZGVyPSJyZWQiLCBjb2w9ImJsYWNrIikKICB9Cn0KCmZvciAoeWVhciBpbiBjKDE5MDAsMTk1MCwyMDEwKSl7CiAgdGhpc3llYXIgPC0gYWxsbmFtZXNbLHBhc3RlKCJYIix5ZWFyLHNlcD0iIildCiAgbGFzdGxldHRlci5ieS5zZXggPC0gYXJyYXkoTkEsIGMoMjYsMikpCiAgZmlyc3RsZXR0ZXIuYnkuc2V4IDwtIGFycmF5KE5BLCBjKDI2LDIpKQogIGZvciAoaSBpbiAxOjI2KXsKICAgIGxhc3RsZXR0ZXIuYnkuc2V4W2ksMV0gPC0gc3VtKHRoaXN5ZWFyW2xhc3RsZXR0ZXI9PWxldHRlcnNbaV0gJiBnaXJsXSkKICAgIGxhc3RsZXR0ZXIuYnkuc2V4W2ksMl0gPC0gc3VtKHRoaXN5ZWFyW2xhc3RsZXR0ZXI9PWxldHRlcnNbaV0gJiAhZ2lybF0pCiAgICBmaXJzdGxldHRlci5ieS5zZXhbaSwxXSA8LSBzdW0odGhpc3llYXJbZmlyc3RsZXR0ZXI9PUxFVFRFUlNbaV0gJiBnaXJsXSkKICAgIGZpcnN0bGV0dGVyLmJ5LnNleFtpLDJdIDwtIHN1bSh0aGlzeWVhcltmaXJzdGxldHRlcj09TEVUVEVSU1tpXSAmICFnaXJsXSkKICB9CiAgaWYgKHNhdmVmaWdzKSBwZGYocm9vdCgiTmFtZXMvZmlncyIsIHBhc3RlKCJib3lzIiwgeWVhciwgIi5wZGYiLCBzZXA9IiIpKSwKICAgICAgICAgICAgICAgICAgIGhlaWdodD0zLCB3aWR0aD00LjUpCiAgZGlzY3JldGUuaGlzdG9ncmFtKDE6MjYsIDEwMCoobGFzdGxldHRlci5ieS5zZXhbLDJdKS9zdW0obGFzdGxldHRlci5ieS5zZXhbLDJdKSwgeGF4cy5sYWJlbD1saXN0KDE6MjYsbGV0dGVycyksIHlheHMubGFiZWw9bGlzdChzZXEoMCwzMCwxMCksc2VxKDAsMzAsMTApKSwgeGxhYj0iIiwgeWxhYj0iUGVyY2VudGFnZSBvZiBib3lzIGJvcm4iLCBtYWluPXBhc3RlKCJMYXN0IGxldHRlciBvZiBib3lzJyBuYW1lcyBpbiIsIHllYXIpLCBjZXguYXhpcz0uOSwgY2V4Lm1haW49LjksIGJhci53aWR0aD0uOCkKICBmb3IgKHkgaW4gYygxMCwyMCwzMCkpIGFibGluZSAoeSwwLGNvbD0iZ3JheSIsbHdkPS41KQogIGlmIChzYXZlZmlncykgZGV2Lm9mZigpCiAgaWYgKHNhdmVmaWdzKSBwZGYocm9vdCgiTmFtZXMvZmlncyIsIHBhc3RlKCJnaXJscyIsIHllYXIsICIucGRmIiwgc2VwPSIiKSksCiAgICAgICAgICAgICAgICAgICBoZWlnaHQ9Mywgd2lkdGg9NC41KQogIGRpc2NyZXRlLmhpc3RvZ3JhbSgxOjI2LCAxMDAqKGxhc3RsZXR0ZXIuYnkuc2V4WywxXSkvc3VtKGxhc3RsZXR0ZXIuYnkuc2V4WywxXSksIHhheHMubGFiZWw9bGlzdCgxOjI2LGxldHRlcnMpLCB5YXhzLmxhYmVsPWxpc3Qoc2VxKDAsMzAsMTApLHNlcSgwLDMwLDEwKSksIHhsYWI9IiIsIHlsYWI9IlBlcmNlbnRhZ2Ugb2YgZ2lybHMgYm9ybiIsIG1haW49cGFzdGUoIkxhc3QgbGV0dGVyIG9mIGdpcmxzJyBuYW1lcyBpbiIsIHllYXIpLCBjZXgubWFpbj0uOSkKICBpZiAoc2F2ZWZpZ3MpIGRldi5vZmYoKQp9Cgp5cnMgPC0gMTg4MDoyMDEwCm4ueXJzIDwtIGxlbmd0aCh5cnMpCmxhc3RsZXR0ZXJmcmVxcyA8LSBhcnJheShOQSwgYyhuLnlycywyNiwyKSkKZmlyc3RsZXR0ZXJmcmVxcyA8LSBhcnJheShOQSwgYyhuLnlycywyNiwyKSkKZGltbmFtZXMobGFzdGxldHRlcmZyZXFzKSA8LSBsaXN0KHlycywgbGV0dGVycywgYygiZ2lybHMiLCJib3lzIikpCmRpbW5hbWVzKGZpcnN0bGV0dGVyZnJlcXMpIDwtIGxpc3QoeXJzLCBsZXR0ZXJzLCBjKCJnaXJscyIsImJveXMiKSkKZm9yIChpIGluIDE6bi55cnMpewogIHRoaXN5ZWFyIDwtIGFsbG5hbWVzWyxwYXN0ZSgiWCIseXJzW2ldLHNlcD0iIildCiAgZm9yIChqIGluIDE6MjYpewogICAgbGFzdGxldHRlcmZyZXFzW2ksaiwxXSA8LSBzdW0odGhpc3llYXJbbGFzdGxldHRlcj09bGV0dGVyc1tqXSAmIGdpcmxdKQogICAgbGFzdGxldHRlcmZyZXFzW2ksaiwyXSA8LSBzdW0odGhpc3llYXJbbGFzdGxldHRlcj09bGV0dGVyc1tqXSAmICFnaXJsXSkKICAgIGZpcnN0bGV0dGVyZnJlcXNbaSxqLDFdIDwtIHN1bSh0aGlzeWVhcltmaXJzdGxldHRlcj09TEVUVEVSU1tqXSAmIGdpcmxdKQogICAgZmlyc3RsZXR0ZXJmcmVxc1tpLGosMl0gPC0gc3VtKHRoaXN5ZWFyW2ZpcnN0bGV0dGVyPT1MRVRURVJTW2pdICYgIWdpcmxdKQogIH0KICBmb3IgKGsgaW4gMToyKXsKICAgIGxhc3RsZXR0ZXJmcmVxc1tpLCxrXSA8LSBsYXN0bGV0dGVyZnJlcXNbaSwsa10vc3VtKGxhc3RsZXR0ZXJmcmVxc1tpLCxrXSkKICAgIGZpcnN0bGV0dGVyZnJlcXNbaSwsa10gPC0gZmlyc3RsZXR0ZXJmcmVxc1tpLCxrXS9zdW0oZmlyc3RsZXR0ZXJmcmVxc1tpLCxrXSkKICB9Cn0KCmBgYApgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQppZiAoc2F2ZWZpZ3MpIHBkZihyb290KCJOYW1lcy9maWdzIiwgIm5hbWVzdGltZWJveXMucGRmIiksIGhlaWdodD0zLjUsIHdpZHRoPTYpCmBgYApgYGB7ciB9CnBhcihtYXI9YygyLDIsMSwxKSwgbWdwPWMoMS43LC4zLDApLCB0Y2s9LS4wMSwgb21hPWMoMCwwLDIsMCksIG1mcm93PWMoMiwzKSkKcG9wdWxhciA8LSByZXYob3JkZXIobGFzdGxldHRlcmZyZXFzWzEsLDJdKSlbMTo2XQpmb3IgKGsgaW4gMTpsZW5ndGgocG9wdWxhcikpewogIHBsb3QocmFuZ2UoeXJzKSwgYygwLDUwKSwgdHlwZT0ibiIsIHhsYWI9IiIsIHlsYWI9IiIsIGJ0eT0ibCIsIHhheHQ9Im4iLCB5YXh0PSJuIiwgeWF4cz0iaSIsIHhheHM9ImkiKQogIGF4aXMoMSwgc2VxKDE5MDAsMjAwMCw1MCkpCiAgYXhpcygyLCBzZXEoMCw0MCwyMCksIHBhc3RlKHNlcSgwLDQwLDIwKSwgIiUiLCBzZXA9IiIpKQogIG10ZXh0KHBhc3RlKCIuIC4gLiIsIExFVFRFUlNbcG9wdWxhcltrXV0pLCBzaWRlPTMsIGxpbmU9LTEsIGNleD0uOCkKICBmb3IgKGogaW4gMToyNil7CiAgICBtYXhmcmVxIDwtIG1heChsYXN0bGV0dGVyZnJlcXNbLGosMl0pCiAgICBiZXN0IDwtICgxOm4ueXJzKVtsYXN0bGV0dGVyZnJlcXNbLGosMl09PW1heGZyZXFdCiAgICBsaW5lcyh5cnMsIDEwMCpsYXN0bGV0dGVyZnJlcXNbLGosMl0sIGNvbD1pZiAoaj09cG9wdWxhcltrXSkgImJsYWNrIiBlbHNlICJkYXJrZ3JheSIsIGx3ZD1pZiAoaj09cG9wdWxhcltrXSkgMSBlbHNlIC41KQogIH0KfQptdGV4dCgiTGFzdCBsZXR0ZXJzIG9mIGJveXMnIG5hbWVzIiwgc2lkZT0zLCBvdXRlcj1UUlVFLCBsaW5lPS41KQpgYGAKYGBge3IgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KaWYgKHNhdmVmaWdzKSBkZXYub2ZmKCkKYGBgCmBgYHtyIH0KCmBgYApgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQppZiAoc2F2ZWZpZ3MpIHBkZihyb290KCJOYW1lcy9maWdzIiwgIm5hbWVzdGltZWJveXMyLnBkZiIpLCBoZWlnaHQ9NCwgd2lkdGg9NikKYGBgCmBgYHtyIH0KcGFyKG1hcj1jKDIsMywyLDEpLCBtZ3A9YygxLjcsLjMsMCksIHRjaz0tLjAxKQpwb3B1bGFyIDwtIGMoMTQsMjUsNCkKd2lkdGggPC0gcmVwKC41LDI2KQp0eXBlIDwtIHJlcCgxLDI2KQp3aWR0aFtwb3B1bGFyXSA8LSBjKDIsMywzKQp0eXBlW3BvcHVsYXJdIDwtIGMoMSwzLDIpCnBsb3QocmFuZ2UoeXJzKSwgYygwLDQxKSwgdHlwZT0ibiIsIHhsYWI9IiIsIHlsYWI9IlBlcmNlbnRhZ2Ugb2YgYWxsIGJveXMnIG5hbWVzIHRoYXQgeWVhciIsIGJ0eT0ibCIsIHhheHQ9Im4iLCB5YXh0PSJuIiwgeWF4cz0iaSIsIHhheHM9ImkiKQogIGF4aXMoMSwgc2VxKDE5MDAsMjAwMCw1MCkpCiAgYXhpcygyLCBzZXEoMCw0MCwyMCksIHBhc3RlKHNlcSgwLDQwLDIwKSwgIiUiLCBzZXA9IiIpKQogIGZvciAoaiBpbiAxOjI2KXsKICAgIG1heGZyZXEgPC0gbWF4KGxhc3RsZXR0ZXJmcmVxc1ssaiwyXSkKICAgIGJlc3QgPC0gKDE6bi55cnMpW2xhc3RsZXR0ZXJmcmVxc1ssaiwyXT09bWF4ZnJlcV0KICAgIGxpbmVzKHlycywgMTAwKmxhc3RsZXR0ZXJmcmVxc1ssaiwyXSwgY29sPSJibGFjayIsIGx3ZD13aWR0aFtqXSwgbHR5PXR5cGVbal0pCiAgfQp0ZXh0KDIwMDAsIDM1LCAiTiIpCnRleHQoMTkzNSwgMjAsICJEIikKdGV4dCgxOTc1LCAxNSwgIlkiKQptdGV4dCgiTGFzdCBsZXR0ZXJzIG9mIGJveXMnIG5hbWVzIiwgc2lkZT0zLCBsaW5lPS41KQpgYGAKYGBge3IgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KaWYgKHNhdmVmaWdzKSBkZXYub2ZmKCkKYGBgCmBgYHtyIH0KCmBgYApgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQppZiAoc2F2ZWZpZ3MpIHBkZihyb290KCJOYW1lcy9maWdzIiwgIm5hbWVzdGltZWJveXMzLnBkZiIpLCBoZWlnaHQ9NCwgd2lkdGg9NikKYGBgCmBgYHtyIH0KcGFyKG1hcj1jKDIsMywyLDEpLCBtZ3A9YygxLjcsLjMsMCksIHRjaz0tLjAxKQpwb3B1bGFyIDwtIGMoMTQsMjUsNCkKcGxvdChyYW5nZSh5cnMpLCBjKDAsNDEpLCB0eXBlPSJuIiwgeGxhYj0iIiwgeWxhYj0iUGVyY2VudGFnZSIsIGJ0eT0ibCIsIHhheHQ9Im4iLCB5YXh0PSJuIiwgeWF4cz0iaSIsIHhheHM9ImkiKQogIGF4aXMoMSwgc2VxKDE5MDAsMjAwMCw1MCkpCiAgYXhpcygyLCBzZXEoMCw0MCwyMCksIHBhc3RlKHNlcSgwLDQwLDIwKSwgIiUiLCBzZXA9IiIpKQogIGZvciAoaiBpbiAxOjI2KXsKICAgIG1heGZyZXEgPC0gbWF4KGZpcnN0bGV0dGVyZnJlcXNbLGosMl0pCiAgICBiZXN0IDwtICgxOm4ueXJzKVtmaXJzdGxldHRlcmZyZXFzWyxqLDJdPT1tYXhmcmVxXQogICAgaWYgKGogJWluJSBwb3B1bGFyKXsKICAgICAgbGluZXMoeXJzLCAxMDAqZmlyc3RsZXR0ZXJmcmVxc1ssaiwyXSwgY29sPSJibGFjayIsIGx3ZD0yKQogICAgfQogICAgZWxzZXsKICAgICAgbGluZXMoeXJzLCAxMDAqZmlyc3RsZXR0ZXJmcmVxc1ssaiwyXSwgY29sPSJibGFjayIsIGx3ZD0uNSkKICAgIH0KICB9Cm10ZXh0KCJGaXJzdCBsZXR0ZXJzIG9mIGJveXMnIG5hbWVzIiwgc2lkZT0zLCBsaW5lPS41KQpgYGAKYGBge3IgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KaWYgKHNhdmVmaWdzKSBkZXYub2ZmKCkKYGBgCmBgYHtyIH0KYGBgCgpTdHVmZiBmb3IgTllUIGNvbHVtbgoKYGBge3IgfQpkaW0obGFzdGxldHRlcmZyZXFzWywsMl0pCnJvdW5kKGxhc3RsZXR0ZXJmcmVxc1t5cnM+MjAwNSwsMl0sIDIpICAjIDM1JSBlbmQgaW4gbgpyb3VuZChsYXN0bGV0dGVyZnJlcXNbeXJzPjIwMDUsLDFdLCAyKSAgIyAzOCUgb2YgYWxsIGdpcmxzIGVuZCBpbiBhCmBgYAoKMTk1MAoKYGBge3IgfQpyb3VuZChsYXN0bGV0dGVyZnJlcXNbeXJzPT0xOTUwLCwyXSwgMikgICMgMTQlIGVuZCBpbiBuICh0aWVkIHdpdGggZCwgcywgYW5kIHkgYXMgbW9zdCBwb3B1bGFyKQpyb3VuZChsYXN0bGV0dGVyZnJlcXNbeXJzPT0xOTUwLCwxXSwgMikgICMgMzQlIG9mIGFsbCBnaXJscyBlbmQgaW4gYQpgYGAKCjE5MDAKCmBgYHtyIH0Kcm91bmQobGFzdGxldHRlcmZyZXFzW3lycz09MTkwMCwsMl0sIDIpICAjIDE0JSBlbmQgaW4gbiAodGllZCB3aXRoIGQsIHMsIGFuZCB5IGFzIG1vc3QgcG9wdWxhcikKcm91bmQobGFzdGxldHRlcmZyZXFzW3lycz09MTkwMCwsMV0sIDIpICAjIDM0JSBvZiBhbGwgZ2lybHMgZW5kIGluIGEKYGBgCgpNb3N0IHBvcHVsYXIgbmFtZXMgaW4gYW55IGdpdmVuIHllYXIKCmBgYHtyIH0KYm95Lm5hbWVzIDwtIG5hbWVzWyFnaXJsXQpnaXJsLm5hbWVzIDwtIG5hbWVzW2dpcmxdCmZvciAoeWVhciBpbiBjKDE5MDAsMTk1MCwyMDEwKSl7CiAgdGhpc3llYXIgPC0gYWxsbmFtZXNbLHBhc3RlKCJYIix5ZWFyLHNlcD0iIildCiAgYm95LnRvdGFscyA8LSB0aGlzeWVhclshZ2lybF0KICBib3kucHJvcG9ydGlvbnMgPC0gYm95LnRvdGFscy9zdW0oYm95LnRvdGFscykKICBpbmRleCA8LSByZXYob3JkZXIoYm95LnByb3BvcnRpb25zKSkKICBwb3B1bGFyLm5hbWVzIDwtIGJveS5uYW1lc1tpbmRleF0KICBwb3B1bGFyaXR5IDwtIGJveS5wcm9wb3J0aW9uc1tpbmRleF0KICBwcmludCh5ZWFyKQogIHByaW50KHBvcHVsYXIubmFtZXNbMTozMF0pCiAgcm91bmQocG9wdWxhcml0eVsxOjMwXSwzKQogIHByaW50KGMoc3VtKHBvcHVsYXJpdHlbMToxMF0pLCBzdW0ocG9wdWxhcml0eVsxOjIwXSksIHN1bShwb3B1bGFyaXR5WzE6MzBdKSwgc3VtKHBvcHVsYXJpdHlbMTo1MF0pLCBzdW0ocG9wdWxhcml0eVsxOjEwMF0pKSkKfQoKbl9wZXJjZW50YWdlIDwtIDEwMCpsYXN0bGV0dGVyZnJlcXNbLDE0LDJdCnRvcHRlbl9wZXJjZW50YWdlIDwtIGFycmF5KE5BLCBjKGxlbmd0aCh5cnMpLCAyKSkKZm9yIChpIGluIDE6bGVuZ3RoKHlycykpewogIHRoaXN5ZWFyIDwtIGFsbG5hbWVzWyxwYXN0ZSgiWCIseXJzW2ldLHNlcD0iIildCiAgYm95LnRvdGFscyA8LSB0aGlzeWVhclshZ2lybF0KICBib3kucHJvcG9ydGlvbnMgPC0gYm95LnRvdGFscy9zdW0oYm95LnRvdGFscykKICBpbmRleCA8LSByZXYob3JkZXIoYm95LnByb3BvcnRpb25zKSkKICBwb3B1bGFyLm5hbWVzIDwtIGJveS5uYW1lc1tpbmRleF0KICBwb3B1bGFyaXR5IDwtIGJveS5wcm9wb3J0aW9uc1tpbmRleF0KICB0b3B0ZW5fcGVyY2VudGFnZVtpLDJdIDwtIDEwMCpzdW0ocG9wdWxhcml0eVsxOjEwXSkKICBnaXJsLnRvdGFscyA8LSB0aGlzeWVhcltnaXJsXQogIGdpcmwucHJvcG9ydGlvbnMgPC0gZ2lybC50b3RhbHMvc3VtKGdpcmwudG90YWxzKQogIGluZGV4IDwtIHJldihvcmRlcihnaXJsLnByb3BvcnRpb25zKSkKICBwb3B1bGFyLm5hbWVzIDwtIGdpcmwubmFtZXNbaW5kZXhdCiAgcG9wdWxhcml0eSA8LSBnaXJsLnByb3BvcnRpb25zW2luZGV4XQogIHRvcHRlbl9wZXJjZW50YWdlW2ksMV0gPC0gMTAwKnN1bShwb3B1bGFyaXR5WzE6MTBdKQp9CgpgYGAKYGBge3IgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KaWYgKHNhdmVmaWdzKSBwZGYocm9vdCgiTmFtZXMvZmlncyIsICJuLnBkZiIpLCBoZWlnaHQ9Mywgd2lkdGg9NCkKYGBgCmBgYHtyIH0KcGFyKG1hcj1jKDQsMiwxLDApLCBtZ3A9YygxLjMsLjIsMCksIHRjaz0tLjAyKQpwbG90KHlycywgbl9wZXJjZW50YWdlLCB0eXBlPSJsIiwgeGF4dD0ibiIsIHlheHQ9Im4iLCB4YXhzPSJpIiwgeWF4cz0iaSIsIHlsaW09YygwLDQ1KSwgYnR5PSJsIiwgeGxhYj0iWWVhciIsIHlsYWI9IiIsIGNleC5sYWI9LjgpCmF4aXMoMSwgYygxOTAwLDE5NTAsMjAwMCksIGNleC5heGlzPS44KQpheGlzKDIsIGMoMCwyMCw0MCksIGMoIjAlIiwiMjAlIiwiNDAlIiksIGNleC5heGlzPS44KQptdGV4dCgiUGVyY2VudGFnZSBvZiBuZXcgYm95cycgbmFtZXMgZWFjaCB5ZWFyIGVuZGluZyBpbiAnbiciLCBjZXg9LjgpCm10ZXh0KCJTb3VyY2U6ICBTb2NpYWwgU2VjdXJpdHkgQWRtaW5pc3RyYXRpb24sIGNvdXJ0ZXN5IG9mIExhdXJhIFdhdHRlbmJlcmciLCAxLCAyLjUsIGNleD0uNSwgYWRqPTApCmBgYApgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQppZiAoc2F2ZWZpZ3MpIGRldi5vZmYoKQpgYGAKYGBge3IgfQoKYGBgCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CmlmIChzYXZlZmlncykgcGRmKHJvb3QoIk5hbWVzL2ZpZ3MiLCAidG9wdGVuLnBkZiIpLCBoZWlnaHQ9Mywgd2lkdGg9NCkKYGBgCmBgYHtyIH0KcGFyKG1hcj1jKDQsMiwxLDApLCBtZ3A9YygxLjMsLjIsMCksIHRjaz0tLjAyKQpwbG90KHlycywgdG9wdGVuX3BlcmNlbnRhZ2VbLDJdLCB0eXBlPSJsIiwgeGF4dD0ibiIsIHlheHQ9Im4iLCB4YXhzPSJpIiwgeWF4cz0iaSIsIHlsaW09YygwLDQ1KSwgYnR5PSJsIiwgeGxhYj0iWWVhciIsIHlsYWI9IiIsIGNleC5sYWI9LjgpCmxpbmVzKHlycywgdG9wdGVuX3BlcmNlbnRhZ2VbLDFdKQpheGlzKDEsIGMoMTkwMCwxOTUwLDIwMDApLCBjZXguYXhpcz0uOCkKYXhpcygyLCBjKDAsMjAsNDApLCBjKCIwJSIsIjIwJSIsIjQwJSIpLCBjZXguYXhpcz0uOCkKdGV4dCgxOTAyLCAzNSwgIkJveXMiLCBjZXg9Ljc1LCBhZGo9MCkKdGV4dCgxOTExLCAyMCwgIkdpcmxzIiwgY2V4PS43NSwgYWRqPTApCm10ZXh0KCJUb3RhbCBwb3B1bGFyaXR5IG9mIHRvcCB0ZW4gbmFtZXMgZWFjaCB5ZWFyLCBieSBzZXgiLCBjZXg9LjgpCm10ZXh0KCJTb3VyY2U6ICBTb2NpYWwgU2VjdXJpdHkgQWRtaW5pc3RyYXRpb24sIGNvdXJ0ZXN5IG9mIExhdXJhIFdhdHRlbmJlcmciLCAxLCAyLjUsIGNleD0uNSwgYWRqPTApCmBgYApgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQppZiAoc2F2ZWZpZ3MpIGRldi5vZmYgKCkKYGBgCmBgYHtyIH0KYGBgCgo=