Last letters of names - Distributions of last letters 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)

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=