About


Sys.setenv(LANG = "en")
#library("rstudioapi") #to grab local position of the script
#setwd(dirname(rstudioapi::getActiveDocumentContext()$path))
knitr::opts_knit$set(root.dir = '.')

#library("rvest") # to handle html stuff

library(lubridate) # to handle dates

library(ggplot2) # for plotting
library(cowplot) # for plotting
library(RColorBrewer) # for choosing colors

custompalette <- brewer.pal(n=8, name = 'Dark2')

library(knitr) # for tables
library(kableExtra) # for tables

library(lubridate) # for dates

library(plyr) # ddply, to summarize number of words by author

load('BSails_worksData.RData')

This is a document detailing analysis of Black Sails Ao3 tag data, collected on the 11 Aug 2020. I haven’t figured out a way to get my scrapper to log in into Ao3 (yet? rvest seems to have some trouble with page redirects), so results here are based on the works visible without authentication, which likely filters out preferentially explicit/problemantic works from the selection.


plot_bar <- function (data, columnX, legendPosition) {
    ggplot(data, aes_string(x = columnX)) + 
    geom_bar(alpha=1)+
    theme_half_open() +
    background_grid() +
    theme(legend.title=element_blank(),
          axis.title.x = element_blank(),
          axis.text.x = element_text(angle = 90, vjust = 1, hjust=1))+
    labs(y="Number of works")
}

plot_bar_color <- function (data, columnX, colColor, legendPosition) {
    ggplot(data, aes_string(x = columnX, fill=colColor)) + 
    geom_bar(alpha=0.7)+
    scale_fill_manual(values = custompalette) +
    theme_half_open() +
    background_grid() +
    theme(legend.title=element_blank(),
          axis.title.x = element_blank(),
          axis.text.x = element_text(angle = 90, vjust = 1, hjust=1))+
    labs(y="Number of works")
}

plot_col <- function (data, columnX, columnY, legendPosition) {
    ggplot(data, aes_string(x = columnX, y = columnY)) + 
    geom_col(alpha=1)+
    theme_half_open() +
    background_grid() +
    theme(legend.title=element_blank(),
          axis.title.x = element_blank(),
          axis.text.x = element_text(angle = 90, vjust = 1, hjust=1))+
    labs(y=gsub('\\.', ' ', columnY))
  
}

plot_col_color <- function (data, columnX, columnY, colColor, legendPosition) {
    ggplot(data, aes_string(x = columnX, y = columnY, fill=colColor)) + 
    geom_col(alpha=0.7)+
    scale_fill_manual(values = custompalette) +
    theme_half_open() +
    background_grid() +
    theme(legend.title=element_blank(),
          axis.title.x = element_blank(),
          axis.text.x = element_text(angle = 90, vjust = 1, hjust=1))+
    labs(y=gsub('\\.', ' ', columnY))
  
}

plot_percentiles <- function (data, columnX, columnY, legendPosition) {
    ggplot(data, aes_string(x = columnX, y = columnY)) + 
    geom_point(alpha=0.3)+
    scale_y_log10(breaks = 10^c(0:15))+
    scale_x_continuous(breaks = c(0, 25, 50, 75, 100))+ #scale_x_continuous(breaks = c(0:10)*10)+
    theme_half_open() +
    background_grid() +
    theme(legend.title=element_blank())+
    labs(x=gsub('\\.', ' ', columnX))
}
#title <- lapply(worksData, function(x) {x$Title})
author <- lapply(worksData, function(x) {x$Author})
fandom <- lapply(worksData, function(x) {x$Fandom})
rating <- lapply(worksData, function(x) {x$Rating})
warnings <- lapply(worksData, function(x) {x$Warnings})
category <- lapply(worksData, function(x) {x$Category})
WIP <- lapply(worksData, function(x) {x$WIP})
date <-lapply(worksData, function(x) {x$Date})
relationships <-lapply(worksData, function(x) {x$Relationships})
character <-lapply(worksData, function(x) {x$Character})
freeform <-lapply(worksData, function(x) {x$Freeform})
language <-lapply(worksData, function(x) {x$Language})
words <-lapply(worksData, function(x) {x$Words})
words[is.na(words)] <- 0
kudos <-lapply(worksData, function(x) {x$Kudos})
kudos[is.na(kudos)] <- 0
comments <-lapply(worksData, function(x) {x$Comments})
comments[is.na(comments)] <- 0
bookmarks<-lapply(worksData, function(x) {x$Bookmarks})
bookmarks[is.na(bookmarks)] <- 0
hits <-lapply(worksData, function(x) {x$Hits})
hits[is.na(hits)] <- 0

stats <- data.frame(Words = unlist(words, recursive = FALSE),
                    Comments= as.numeric(as.character(comments)),
                    Kudos = as.numeric(as.character(kudos)),
                    Bookmarks = as.numeric(as.character(bookmarks)),
                    Hits = as.numeric(as.character(hits)),
                    WIP = unlist(WIP, recursive = FALSE),
                    Rating = unlist(rating, recursive = FALSE),
                    Date = do.call("c", date))

stats$Rating <- factor(stats$Rating, levels = c("Not Rated", "General Audiences", "Teen And Up Audiences", "Mature", "Explicit"))

total <- 1000
percentile <- c(1:total)
percentileData <- data.frame(Works.Percentile = 100*(total - percentile)/total,
                             Words = unlist(lapply(percentile/total, quantile, x = unlist(words) )) + 1,
                             Hits = unlist(lapply(percentile/total, quantile, x = unlist(hits) )) + 1,
                             Kudos = unlist(lapply(percentile/total, quantile, x = unlist(kudos) )) + 1,
                             Comments = unlist(lapply(percentile/total, quantile, x = unlist(comments) )) + 1,
                             Bookmarks = unlist(lapply(percentile/total, quantile, x = unlist(bookmarks) )) + 1 )

rm(rating, kudos, comments, bookmarks, hits)

Timeline

Solid vertical lines on the graph indicate initial air dates, and dashed indicate final air dates, according to Wiki article.

We see a peak of activity after each season which builds up to a major peak after season 4. A minor bump in May 2020 could be attributed to coronavirus locdowns.

There are 3 works published before season 1 premier, one on 22 June 2013, which is probably an artifact of work import, the other two are dated 22 of January are tagged with “Anne Bonny/”Calico" Jack Rackham " and could be due to trailer hype and/or historical pirates fandom.


#data$Timestamp <- parse_date_time2(as.character(data$Timestamp), orders = "%d/%m/%Y %H:%M:%S")
#data$day <- as.Date(data$Timestamp)

seasonsStart <- c("2014-01-25", "2015-01-24", "2016-01-23", "2017-01-29")
seasonsStart <- as.Date(seasonsStart)
seasonsEnd <- c("2014-03-15", "2015-03-28", "2016-03-26", "2017-04-02")
seasonsEnd <- as.Date(seasonsEnd)

plotDatesDensityTotal <- ggplot(stats, aes(x = Date)) + 
                    geom_density()+
                    geom_vline(xintercept=as.numeric(seasonsStart))+
                    geom_vline(xintercept=as.numeric(seasonsEnd), linetype ="longdash")+
                    scale_x_date(date_breaks="3 months")+
                    theme_half_open() +
                    background_grid() +
                    theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1),
                          legend.position = 'right')
plotDatesDensityTotal


rm(plotDatesDensityTotal)

If we plot Complete Works and Works in Progress separately, we still observe an overall peak structure in complete works, but, interestingly, Works in Progress follow the basic structure but don’t fluctuate much with new seasons.


plotDatesDensity <- ggplot(stats, aes(x = Date, col=WIP)) + 
                    geom_density(alpha = 0.1)+
                    geom_vline(xintercept=seasonsStart)+
                    geom_vline(xintercept=seasonsEnd, linetype ="longdash")+
                    scale_x_date(date_breaks="3 months")+
                    theme_half_open() +
                    background_grid() +
                    theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1),
                          legend.position = 'right')
plotDatesDensity


rm(plotDatesDensity)

Engagement percentiles

Small plotting cheat: all the numbers on the Y axis are increased by 1 to include the case of 0 into the plot (otherwise excluded because of log scale).

wordsPercentiles <- plot_percentiles(percentileData, 'Works.Percentile', 'Words', 'right')
hitsPercentiles <- plot_percentiles(percentileData, 'Works.Percentile', 'Hits', 'right')
kudosPercentiles <- plot_percentiles(percentileData, 'Works.Percentile', 'Kudos', 'right')
commentsPercentiles <- plot_percentiles(percentileData, 'Works.Percentile', 'Comments', 'right')
bookmarksPercentiles <- plot_percentiles(percentileData, 'Works.Percentile', 'Bookmarks', 'right')

plot_grid(wordsPercentiles + theme(legend.position="none"),
          hitsPercentiles + theme(legend.position="none"),
          kudosPercentiles + theme(legend.position="none"),
          commentsPercentiles + theme(legend.position="none"),
          bookmarksPercentiles + theme(legend.position="none") )


rm(total, percentile, percentileData, wordsPercentiles, hitsPercentiles, kudosPercentiles, commentsPercentiles, bookmarksPercentiles)

Complete Work vs Work in Progress distributions


statsWIP <- stats
statsWIP$Divisor <- unlist(lapply(statsWIP$WIP, function(x) summary(statsWIP$WIP)[names(summary(statsWIP$WIP)) == x]))
statsWIP$Words.per.Work <- statsWIP$Words/statsWIP$Divisor
statsWIP$Hits.per.Work <- statsWIP$Hits/statsWIP$Divisor
statsWIP$Kudos.per.Work <- statsWIP$Kudos/statsWIP$Divisor
statsWIP$Comments.per.Work <- statsWIP$Comments/statsWIP$Divisor
statsWIP$Bookmarks.per.Work <- statsWIP$Bookmarks/statsWIP$Divisor

barWorksWIP <- plot_bar(statsWIP, 'WIP', 'right')
barWordsWIP <- plot_col(statsWIP, 'WIP', 'Words.per.Work', 'right')
barHitsWIP <- plot_col(statsWIP, 'WIP', 'Hits.per.Work', 'right')
barKudosWIP <- plot_col(statsWIP, 'WIP', 'Kudos.per.Work', 'right')
barCommentsWIP <- plot_col(statsWIP, 'WIP', 'Comments.per.Work', 'right')
barBookmarksWIP <- plot_col(statsWIP, 'WIP', 'Bookmarks.per.Work', 'right')

# plot_grid(plot_grid( barWorksWIP + theme(legend.position="none"),
#                      barWordsWIP + theme(legend.position="none"),
#                      barHitsWIP + theme(legend.position="none"),
#                      barKudosWIP + theme(legend.position="none"),
#                      barCommentsWIP + theme(legend.position="none"),
#                      barBookmarksWIP + theme(legend.position="none"),
#                      align = 'hv'),
#           get_legend(barWorksWIP + theme(legend.title=element_blank())),
#           rel_widths = c(4,1),
#           align = 'hv')
plot_grid( barWorksWIP + theme(legend.position="none"),
           barWordsWIP + theme(legend.position="none"),
           barHitsWIP + theme(legend.position="none"),
           barKudosWIP + theme(legend.position="none"),
           barCommentsWIP + theme(legend.position="none"),
           barBookmarksWIP + theme(legend.position="none"),
           align = 'hv')


rm(statsWIP, barWorksWIP, barWordsWIP, barHitsWIP, barKudosWIP, barCommentsWIP, barBookmarksWIP)

Rating distributions


statsRating <- stats
statsRating$Divisor <- unlist(lapply(statsRating$Rating, function(x) summary(statsRating$Rating)[names(summary(statsRating$Rating)) == x]))
statsRating$Words.per.Work <- statsRating$Words/statsRating$Divisor
statsRating$Hits.per.Work <- statsRating$Hits/statsRating$Divisor
statsRating$Kudos.per.Work <- statsRating$Kudos/statsRating$Divisor
statsRating$Comments.per.Work <- statsRating$Comments/statsRating$Divisor
statsRating$Bookmarks.per.Work <- statsRating$Bookmarks/statsRating$Divisor

barWorksRating <- plot_bar(statsRating, 'Rating', 'right')
barWordsRating <- plot_col(statsRating, 'Rating', 'Words.per.Work', 'right')
barHitsRating <- plot_col(statsRating, 'Rating', 'Hits.per.Work', 'right')
barKudosRating <- plot_col(statsRating, 'Rating', 'Kudos.per.Work', 'right')
barCommentsRating <- plot_col(statsRating, 'Rating', 'Comments.per.Work', 'right')
barBookmarksRating <- plot_col(statsRating, 'Rating', 'Bookmarks.per.Work', 'right')

plot_grid( barWorksRating + theme(legend.position="none"),
           barWordsRating + theme(legend.position="none"),
           barHitsRating + theme(legend.position="none"),
           barKudosRating + theme(legend.position="none"),
           barCommentsRating + theme(legend.position="none"),
           barBookmarksRating + theme(legend.position="none"),
           align = 'hv')


rm(statsRating, barWorksRating, barWordsRating, barHitsRating, barKudosRating, barCommentsRating, barBookmarksRating)

Categories

There are 3467 works tagged with a single category, and 860 tagged with 2 or more (up until all 6).

‘M/M’ is the most popular category, and it dwarfs all the others.

Multiple category fics strongly contribute towards ‘M/M’ count, then to ‘F/M’, ‘Gen’, and ‘F/F’, and only marginally to ‘Multi’ and ‘Other’.


singleCategorySummary <- summary(as.factor(unlist(category[unlist(lapply(category, function(x) length(x))) == 1])))
singleCategorySummary <- data.frame(Category = names(singleCategorySummary),
                                    Number.of.Works = singleCategorySummary)
singleCategorySummary$Split <- "Single category"

multipleCategorySummary <- data.frame(Category = c('Gen', 'F/F', 'F/M', 'M/M', 'Multi', 'Other', 'No category'),
                              Number.of.Works = c(sum(grepl('Gen',category)),
                                                  sum(grepl('F/F',category)),
                                                  sum(grepl('F/M',category)),
                                                  sum(grepl('M/M',category)),
                                                  sum(grepl('Multi',category)),
                                                  sum(grepl('Other',category)),
                                                  sum(grepl('No category',category))) )
multipleCategorySummary$Split <- "All works"

categorySummary <- rbind(singleCategorySummary, multipleCategorySummary)
categorySummary$Category <- factor(categorySummary$Category, levels = c('Gen', 'F/F', 'F/M', 'M/M', 'Multi', 'Other', 'No category'))
categorySummary$Split <- factor(categorySummary$Split, levels = c("Single category", "All works"))

plotCategories <- ggplot(categorySummary, aes(x = Category, y = Number.of.Works)) + 
                  geom_col(alpha=1)+
                  theme_half_open() +
                  background_grid() +
                  facet_wrap(.~Split) +
                  theme(legend.title=element_blank(),
                        axis.title.x = element_blank(),
                        axis.text.x = element_text(angle = 90, vjust = 1, hjust=1))+
                  labs(y="Number of Works")
plotCategories


rm(singleCategorySummary, multipleCategorySummary, categorySummary, plotCategories)

Engagement by a single category

For simplicity I’m only looking at works tagged with a single category here.

“Other” seems to have most words, despite being a tiny category, and collects quite a bit of Hits, Kudos, Comments and Bookmarks. It’s possible that a number of those works are collections of stories for many fandoms, which amplifies the engagement numbers.

‘M/M’ and ‘F/F’ works collect the most hits, but ‘F/F’ gets significantly fewer kudos, comments and bookmarks.


statsCategory <- stats[unlist(lapply(category, function(x) length(x))) == 1,]
statsCategory$Category <- as.factor(unlist(category[unlist(lapply(category, function(x) length(x))) == 1]))
statsCategory$Category <- factor(statsCategory$Category, levels = c('Gen', 'F/F', 'F/M', 'M/M', 'Multi', 'Other', 'No category'))
statsCategory$Divisor <- unlist(lapply(statsCategory$Category, function(x) summary(statsCategory$Category)[names(summary(statsCategory$Category)) == x]))
statsCategory$Words.per.Work <- statsCategory$Words/statsCategory$Divisor
statsCategory$Hits.per.Work <- statsCategory$Hits/statsCategory$Divisor
statsCategory$Kudos.per.Work <- statsCategory$Kudos/statsCategory$Divisor
statsCategory$Comments.per.Work <- statsCategory$Comments/statsCategory$Divisor
statsCategory$Bookmarks.per.Work <- statsCategory$Bookmarks/statsCategory$Divisor
statsCategory$Works.Percent <- 1/statsCategory$Divisor

barWorksCategory <- plot_bar_color(statsCategory, 'Category', 'Rating', 'right')
barWordsCategory <- plot_col_color(statsCategory, 'Category', 'Words.per.Work', 'Rating', 'right')
barHitsCategory <- plot_col_color(statsCategory, 'Category', 'Hits.per.Work', 'Rating', 'right')
barKudosCategory <- plot_col_color(statsCategory, 'Category', 'Kudos.per.Work', 'Rating', 'right')
barCommentsCategory <- plot_col_color(statsCategory, 'Category', 'Comments.per.Work', 'Rating', 'right')
barBookmarksCategory <- plot_col_color(statsCategory, 'Category', 'Bookmarks.per.Work','Rating', 'right')

plot_grid(plot_grid( barWorksCategory + theme(legend.position="none"),
           barWordsCategory + theme(legend.position="none"),
           barHitsCategory + theme(legend.position="none"),
           barKudosCategory + theme(legend.position="none"),
           barCommentsCategory + theme(legend.position="none"),
           barBookmarksCategory + theme(legend.position="none"),
           align = 'hv'),
          get_legend(barWorksCategory + theme(legend.title=element_blank())),
          rel_widths = c(4,1))

Ratings percentages by a single category

In absolute numbers “M/M” is the most popular category, and in relative numbers it gets the most explicit works.


plotWorksCategoryNormalized <- plot_col_color(statsCategory, 'Rating', 'Works.Percent', 'Rating', 'none')+
                               scale_y_continuous(labels=scales::percent)+
                               facet_wrap(.~Category)
plotWorksCategoryNormalized


rm(barWorksCategory, barWordsCategory, barHitsCategory, barKudosCategory, barCommentsCategory, barBookmarksCategory, plotWorksCategoryNormalized)

Single Category through time

Season 1 had a significant “F/F” bump, likely to “Eleanor Guthrie/Max” relationship. In season 2 we see a dip, but seasons 3 and 4 slowly build up to a strong peak, likely due to “Anne Bonny/Max” endgame. Season 1 is almonst non-existent in terms of “M/M” category, which may be attributed to early series build up being described as “straightbaiting”. Throughout seasons 2-4 it however builds up to a strong peak, with complex “Captain Flint/John Silver” developments and “Captain Flint/Thomas Hamilton” finale.


plotDatesRatingDensity <- ggplot(statsCategory, aes(x = Date, col=Category)) + 
                    geom_density(alpha = 0.1)+
                    geom_vline(xintercept=seasonsStart)+
                    geom_vline(xintercept=seasonsEnd, linetype ="longdash")+
                    scale_x_date(date_breaks="3 months")+
                    scale_color_manual(values = custompalette) +
                    theme_half_open() +
                    background_grid() +
                    theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1),
                          legend.position = 'right')
plotDatesRatingDensity


rm(plotDatesRatingDensity)

Ship tags through time

Season 3 kickstarted the growth of popularity of “Captain Flint/John Silver”, which continued through season 4 and after, however at around March 2019 alternative tag “Captain Flint | James McGraw/John Silver” becomes more popular. Season 4 sees the rapid growth of “Captain Flint/Thomas Hamilton”, which similarly switches to alternative tag “Captain Flint/Thomas Hamilton” in March 2019. The shift in tagging most likely happened due to the efforts of Ao3 tag wranglers.


plotRelationships <- ggplot() +
    geom_density(data = relationshipsStats[relationshipsStats$relationship1 > 0,], mapping=aes(x = Date), colour=custompalette[1])+
    geom_density(data = relationshipsStats[relationshipsStats$relationship2 > 0,], mapping=aes(x = Date), colour=custompalette[2])+
    geom_density(data = relationshipsStats[relationshipsStats$relationship3 > 0,], mapping=aes(x = Date), colour=custompalette[3])+
    geom_density(data = relationshipsStats[relationshipsStats$relationship4 > 0,], mapping=aes(x = Date), colour=custompalette[4])+
    geom_density(data = relationshipsStats[relationshipsStats$relationship5 > 0,], mapping=aes(x = Date), colour=custompalette[5])+
    geom_density(data = relationshipsStats[relationshipsStats$relationship6 > 0,], mapping=aes(x = Date), colour=custompalette[6])+
    geom_density(data = relationshipsStats[relationshipsStats$relationship7 > 0,], mapping=aes(x = Date), colour=custompalette[7])+
    geom_density(data = relationshipsStats[relationshipsStats$relationship8 > 0,], mapping=aes(x = Date), colour=custompalette[8])+
    geom_vline(xintercept=seasonsStart)+
    geom_vline(xintercept=seasonsEnd, linetype ="longdash")+
    scale_x_date(date_breaks="3 months")+
    scale_color_manual(values = custompalette) +
    theme_half_open() +
    background_grid() +
    theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1))

mylegend <- get_legend(plotLegendRelationships)

plot_grid(plotRelationships, mylegend,
          rel_widths = c(2,1), nrow=1)

#plotRelationships

#rm(seasons, plotDatesRatingDensity)

Archive Warnings

Majority of works are tagged with “No Archive Warnings Apply”, followed by a sizable fraction of “Creator Chose Not To Use Archive Warnings”. It seems to be a common matter of confusion between the usage of those two warnings, so it’s possible that a lot of “Creator Chose Not To Use Archive Warnings” are mistagged “No Archive Warnings Apply”.


multipleWarningSummary <- data.frame(Warning = c("No Archive Warnings Apply",
                                                  "Graphic Depictions Of Violence",
                                                  "Major Character Death",
                                                  "Rape/Non-Con",
                                                  "Underage",
                                                  "Creator Chose Not To Use Archive Warnings"),
                              Number.of.Works = c(sum(grepl("No Archive Warnings Apply",warnings)),
                                                  sum(grepl("Graphic Depictions Of Violence",warnings)),
                                                  sum(grepl("Major Character Death",warnings)),
                                                  sum(grepl("Rape/Non-Con",warnings)),
                                                  sum(grepl("Underage",warnings)),
                                                  sum(grepl("Creator Chose Not To Use Archive Warnings",warnings))) )

multipleWarningSummary$Warning <- factor(multipleWarningSummary$Warning, levels = c("No Archive Warnings Apply",
                                                                                    "Graphic Depictions Of Violence",
                                                                                    "Major Character Death",
                                                                                    "Rape/Non-Con",
                                                                                    "Underage",
                                                                                    "Creator Chose Not To Use Archive Warnings"))

plotWarnings <- plot_col(multipleWarningSummary, 'Warning', 'Number.of.Works', 'right')
plotWarnings


rm(multipleWarningSummary, plotWarnings)

Multiple Fandoms

Number of works tagged with more than 1 fandom is 181, but the number of works explicitly tagged as ‘crossover’ is just 42.

Authors by Works

Top 30 of most prolific authors in the tag by the number of stories as of data collection date:

topList <- 30

AuthorTable <- data.frame('Author' = names(summary(as.factor(unlist(author)))[1:topList]),
                          'Number of Stories' = summary(as.factor(unlist(author)))[1:topList])
row.names(AuthorTable) <- c()

kable(AuthorTable,
      col.names = c('Author', 'Number of Stories'))

Author Number of Stories
Magnetism_bind 200
Melis_Ash 118
OnlyOneWoman 98
iwtv 94
ElDiablito_SF 91
PrimalScream 90
VarjoRuusu 79
queerpyrate 61
WeeBeastie 61
DreamingPagan 49
mapped 48
meridian_rose (meridianrose) 48
agdhani 46
shirogiku 44
vowelinthug 40
lacecat 39
NahaFlowers 34
medusine 33
orphan_account 33
Sweety_Mutant 32
depugnare 31
Elisexyz 28
Arzani 25
Diana924 25
Palebluedot 23
khazadspoon 22
Lazurit 22
fosfomifira 21
jauneclair 21
NovaCaelum 21


rm(AuthorTable)

Top place is occupied by orphan_account, which is an artifact of archive’ works orphaning function.

Authors by Words

Only 69 works have more than one author. In cases where works had more than one author, I assumed that each of them contributed an equal amounts of words.

Top 30 of most prolific authors in the tag by the number of words written as of data collection date:


wordsByAuthor <- c()

for (i in 1:length(words)){
  if (length(author[[i]]) > 1) {
    wordsByAuthor <- c(wordsByAuthor, rep(words[[i]]/length(author[[5]]), length(author[[i]]) ) )
  } else {
    wordsByAuthor <- c(wordsByAuthor, words[[i]])
  }
}

AuthorWordsTable <- data.frame('Author' = as.factor(unlist(author)),
                               'Words' = wordsByAuthor)

AuthorWordsSummary <- ddply(AuthorWordsTable, .(Author), 
                            summarize, 
                            Total.Words = sum(Words))
AuthorWordsSummary <- AuthorWordsSummary[order(AuthorWordsSummary$Total.Words, decreasing = TRUE),]
row.names(AuthorWordsSummary) <- c()

topList <- 30

kable(AuthorWordsSummary[1:topList,],
      col.names = c('Author', 'Total Words'))

Author Total Words
qqueenofhades 890452
LexyRomanova 856531
OnlyOneWoman 848757
Magnetism_bind 670789
iwtv 652764
lacecat 551184
PrimalScream 540335
DreamingPagan 461150
ElDiablito_SF 444795
vowelinthug 421236
queerpyrate 322172
Wind_Ryder 281640
brasspetal 266743
orphan_account 260828
VarjoRuusu 255679
WeeBeastie 225000
agdhani 203114
BehindBrokenWindows 200105
benchofindigo 194519
medusine 190964
sweetsunray 190186
Myheadisclear 182883
Thatswherethelightgetsin 182380
x_art 182019
Tarasque 181685
mapped 176418
seren_ccd 166441
StarRose 165105
Rising_Phoenix 161101
sebastianL (felix_atticus) 157171


rm(wordsByAuthor, i, AuthorWordsTable, AuthorWordsSummary)

Interestingly, orphan_account made it to the top by the number of words written as well.

Characters

Top 30 of the most popular characters:

topList <- 30
CharacterTable<- data.frame('Character' = names(summary(as.factor(unlist(character)))[1:topList]),
                          'Number of Stories' = summary(as.factor(unlist(character)))[1:topList])
row.names(CharacterTable) <- c()

kable(CharacterTable,
      col.names = c('Character', 'Number of Stories'))

Character Number of Stories
John Silver 2013
Captain Flint (Black Sails) 1919
Thomas Hamilton 1218
Billy Bones 776
Charles Vane 650
Miranda Barlow 633
Eleanor Guthrie 586
Anne Bonny 581
Max (Black Sails) 551
“Calico” Jack Rackham 524
Captain Flint 469
Captain Flint | James McGraw 444
Madi (Black Sails) 360
Hal Gates 188
Abigail Ashe 182
Woodes Rogers 154
James Flint 140
James McGraw 126
Ben Gunn 108
Blackbeard | Edward Teach 97
Idelle (Black Sails) 81
Peter Ashe 74
Original Characters 71
Muldoon (Black Sails) 66
Edward “Ned” Low 60
Dufresne (Black Sails) 55
Joji (Black Sails) 55
Dr. Howell (Black Sails) 49
Original Female Character(s) 49
Mr. Scott (Black Sails) 47


rm(CharacterTable)

Relationships

Top 30 of the most popular relationships:

I don’t have access to Ao3’s system of synonymous tags, so by virtue of text processing some relationship tags here are repeated.

Overwhelmingly, “Korra/Asami Sato”/“Korrasami”/“Korra/Asami” is the most popular relationship in LOK, contributing to popularity of “F/F” category. They are followed by “Korra/Mako (Avatar)”, and “Bolin/Opal (Avatar)”.

topList <- 30
RelationshipsTable<- data.frame('Relationship' = names(summary(as.factor(unlist(relationships)))[1:topList]),
                          'Number of Stories' = summary(as.factor(unlist(relationships)))[1:topList])
row.names(RelationshipsTable) <- c()

kable(RelationshipsTable,
      col.names = c('Relationship', 'Number of Stories'))

Relationship Number of Stories
Captain Flint/John Silver 1378
Captain Flint/Thomas Hamilton 811
Captain Flint | James McGraw/John Silver 229
Madi/John Silver 229
Captain Flint | James McGraw/Thomas Hamilton 218
Miranda Barlow/Captain Flint/Thomas Hamilton 214
Anne Bonny/“Calico” Jack Rackham 190
Anne Bonny/Max 183
Miranda Barlow/Captain Flint 169
Captain Flint/Thomas Hamilton/John Silver 155
Eleanor Guthrie/Max 146
Eleanor Guthrie/Charles Vane 135
Billy Bones/Captain Flint 121
Miranda Barlow/Thomas Hamilton 109
Thomas Hamilton/John Silver 80
James McGraw/Thomas Hamilton 73
Abigail Ashe/Billy Bones 69
Billy Bones/Ben Gunn 66
Eleanor Guthrie/Woodes Rogers 65
Miranda Barlow/Captain Flint | James McGraw/Thomas Hamilton 65
Anne Bonny/“Calico” Jack Rackham/Max 63
Captain Flint/Charles Vane 62
Miranda Barlow & Captain Flint & Thomas Hamilton 59
“Calico” Jack Rackham/Charles Vane 56
Billy Bones/Charles Vane 55
Captain Flint/Madi/John Silver 52
Captain Flint & John Silver 50
Captain Flint | James McGraw/Thomas Hamilton/John Silver 41
Billy Bones/John Silver 39
Miranda Barlow/Captain Flint | James McGraw 37


rm(RelationshipsTable)

Freeform tags

Top 30 of the most popular freeform tags:

topList <- 30
FreeformTable<- data.frame('Freeform' = names(summary(as.factor(unlist(freeform)))[1:topList]),
                          'Number of Stories' = summary(as.factor(unlist(freeform)))[1:topList])
row.names(FreeformTable) <- c()

kable(FreeformTable,
      col.names = c('Freeform Tag', 'Number of Stories'))

Freeform Tag Number of Stories
Angst 492
Fluff 400
Alternate Universe - Modern Setting 392
Post-Canon 230
Established Relationship 221
Alternate Universe - Canon Divergence 209
Anal Sex 199
Hurt/Comfort 199
Plot What Plot/Porn Without Plot 169
Canon-Typical Violence 158
Canon Compliant 155
Polyamory 141
Romance 140
Oral Sex 136
Angst with a Happy Ending 125
Blow Jobs 118
Drabble 117
Smut 112
First Time 111
Explicit Sexual Content 105
Tumblr Prompt 105
Character Study 104
Canon Disabled Character 103
Developing Relationship 101
First Kiss 100
Rimming 100
Pirates 99
Pining 96
Slow Burn 90
Love 87


rm(FreeformTable)

Languages

Unsurprisingly, most works are written in English. Apologies for U+. kable package for whatever reason murders unicode characters. The two languages in question are Russian (Русский) and Chinese (中文).

#topList <- 30

languagesList <- summary(as.factor(unlist(language)))

LanguageTable <- data.frame('Language' = names(languagesList),
                            'Number of Stories' = languagesList )
LanguageTable <- LanguageTable[order(LanguageTable$Number.of.Stories, decreasing=TRUE),]
row.names(LanguageTable) <- c()

kable(LanguageTable,
      col.names = c('Language', 'Number of Stories'))

Language Number of Stories
English 3963
<U+0420><U+0443><U+0441><U+0441><U+043A><U+0438><U+0439> 226
Italiano 47
<U+4E2D><U+6587> 38
Español 22
Deutsch 13
Français 12
Polski 4
Ceština 1
Português brasileiro 1


#languagesList

#rm(LanguageTable)
LS0tDQp0aXRsZTogIkFvMyBkYXRhIGFuYWx5c2lzIGZvciBCbGFjayBTYWlscyINCmF1dGhvcjogImRhcnRoYWxpbmUiDQpkYXRlOiAiMTEgQXVnIDIwMjAiDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6DQogICAgY29kZV9mb2xkaW5nOiAiaGlkZSINCiAgICB0b2M6IHRydWUNCi0tLQ0KDQojIEFib3V0DQoNCmBgYHtyIHNldHVwLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmc9RkFMU0V9DQoNClN5cy5zZXRlbnYoTEFORyA9ICJlbiIpDQojbGlicmFyeSgicnN0dWRpb2FwaSIpICN0byBncmFiIGxvY2FsIHBvc2l0aW9uIG9mIHRoZSBzY3JpcHQNCiNzZXR3ZChkaXJuYW1lKHJzdHVkaW9hcGk6OmdldEFjdGl2ZURvY3VtZW50Q29udGV4dCgpJHBhdGgpKQ0Ka25pdHI6Om9wdHNfa25pdCRzZXQocm9vdC5kaXIgPSAnLicpDQoNCiNsaWJyYXJ5KCJydmVzdCIpICMgdG8gaGFuZGxlIGh0bWwgc3R1ZmYNCg0KbGlicmFyeShsdWJyaWRhdGUpICMgdG8gaGFuZGxlIGRhdGVzDQoNCmxpYnJhcnkoZ2dwbG90MikgIyBmb3IgcGxvdHRpbmcNCmxpYnJhcnkoY293cGxvdCkgIyBmb3IgcGxvdHRpbmcNCmxpYnJhcnkoUkNvbG9yQnJld2VyKSAjIGZvciBjaG9vc2luZyBjb2xvcnMNCg0KY3VzdG9tcGFsZXR0ZSA8LSBicmV3ZXIucGFsKG49OCwgbmFtZSA9ICdEYXJrMicpDQoNCmxpYnJhcnkoa25pdHIpICMgZm9yIHRhYmxlcw0KbGlicmFyeShrYWJsZUV4dHJhKSAjIGZvciB0YWJsZXMNCg0KbGlicmFyeShsdWJyaWRhdGUpICMgZm9yIGRhdGVzDQoNCmxpYnJhcnkocGx5cikgIyBkZHBseSwgdG8gc3VtbWFyaXplIG51bWJlciBvZiB3b3JkcyBieSBhdXRob3INCg0KbG9hZCgnQlNhaWxzX3dvcmtzRGF0YS5SRGF0YScpDQoNCmBgYA0KDQpUaGlzIGlzIGEgZG9jdW1lbnQgZGV0YWlsaW5nIGFuYWx5c2lzIG9mIFtgciB0YWdWYWx1ZWAgQW8zIHRhZ10oaHR0cHM6Ly9hcmNoaXZlb2ZvdXJvd24ub3JnL3RhZ3MvQmxhY2slMjBTYWlscy93b3JrcykgZGF0YSwgY29sbGVjdGVkIG9uIHRoZSAxMSBBdWcgMjAyMC4gSSBoYXZlbid0IGZpZ3VyZWQgb3V0IGEgd2F5IHRvIGdldCBteSBzY3JhcHBlciB0byBsb2cgaW4gaW50byBBbzMgKHlldD8gcnZlc3Qgc2VlbXMgdG8gaGF2ZSBzb21lIHRyb3VibGUgd2l0aCBwYWdlIHJlZGlyZWN0cyksIHNvIHJlc3VsdHMgaGVyZSBhcmUgYmFzZWQgb24gdGhlIHdvcmtzIHZpc2libGUgd2l0aG91dCBhdXRoZW50aWNhdGlvbiwgd2hpY2ggbGlrZWx5IGZpbHRlcnMgb3V0IHByZWZlcmVudGlhbGx5IGV4cGxpY2l0L3Byb2JsZW1hbnRpYyB3b3JrcyBmcm9tIHRoZSBzZWxlY3Rpb24uDQoNCmBgYHtyIHBsb3R0aW5nRnVuY3Rpb25zLCBjb2xsYXBzZT1UUlVFLCB3YXJuaW5nPUZBTFNFfQ0KDQpwbG90X2JhciA8LSBmdW5jdGlvbiAoZGF0YSwgY29sdW1uWCwgbGVnZW5kUG9zaXRpb24pIHsNCiAgICBnZ3Bsb3QoZGF0YSwgYWVzX3N0cmluZyh4ID0gY29sdW1uWCkpICsgDQogICAgZ2VvbV9iYXIoYWxwaGE9MSkrDQogICAgdGhlbWVfaGFsZl9vcGVuKCkgKw0KICAgIGJhY2tncm91bmRfZ3JpZCgpICsNCiAgICB0aGVtZShsZWdlbmQudGl0bGU9ZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDEsIGhqdXN0PTEpKSsNCiAgICBsYWJzKHk9Ik51bWJlciBvZiB3b3JrcyIpDQp9DQoNCnBsb3RfYmFyX2NvbG9yIDwtIGZ1bmN0aW9uIChkYXRhLCBjb2x1bW5YLCBjb2xDb2xvciwgbGVnZW5kUG9zaXRpb24pIHsNCiAgICBnZ3Bsb3QoZGF0YSwgYWVzX3N0cmluZyh4ID0gY29sdW1uWCwgZmlsbD1jb2xDb2xvcikpICsgDQogICAgZ2VvbV9iYXIoYWxwaGE9MC43KSsNCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjdXN0b21wYWxldHRlKSArDQogICAgdGhlbWVfaGFsZl9vcGVuKCkgKw0KICAgIGJhY2tncm91bmRfZ3JpZCgpICsNCiAgICB0aGVtZShsZWdlbmQudGl0bGU9ZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDEsIGhqdXN0PTEpKSsNCiAgICBsYWJzKHk9Ik51bWJlciBvZiB3b3JrcyIpDQp9DQoNCnBsb3RfY29sIDwtIGZ1bmN0aW9uIChkYXRhLCBjb2x1bW5YLCBjb2x1bW5ZLCBsZWdlbmRQb3NpdGlvbikgew0KICAgIGdncGxvdChkYXRhLCBhZXNfc3RyaW5nKHggPSBjb2x1bW5YLCB5ID0gY29sdW1uWSkpICsgDQogICAgZ2VvbV9jb2woYWxwaGE9MSkrDQogICAgdGhlbWVfaGFsZl9vcGVuKCkgKw0KICAgIGJhY2tncm91bmRfZ3JpZCgpICsNCiAgICB0aGVtZShsZWdlbmQudGl0bGU9ZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDEsIGhqdXN0PTEpKSsNCiAgICBsYWJzKHk9Z3N1YignXFwuJywgJyAnLCBjb2x1bW5ZKSkNCiAgDQp9DQoNCnBsb3RfY29sX2NvbG9yIDwtIGZ1bmN0aW9uIChkYXRhLCBjb2x1bW5YLCBjb2x1bW5ZLCBjb2xDb2xvciwgbGVnZW5kUG9zaXRpb24pIHsNCiAgICBnZ3Bsb3QoZGF0YSwgYWVzX3N0cmluZyh4ID0gY29sdW1uWCwgeSA9IGNvbHVtblksIGZpbGw9Y29sQ29sb3IpKSArIA0KICAgIGdlb21fY29sKGFscGhhPTAuNykrDQogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gY3VzdG9tcGFsZXR0ZSkgKw0KICAgIHRoZW1lX2hhbGZfb3BlbigpICsNCiAgICBiYWNrZ3JvdW5kX2dyaWQoKSArDQogICAgdGhlbWUobGVnZW5kLnRpdGxlPWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgdmp1c3QgPSAxLCBoanVzdD0xKSkrDQogICAgbGFicyh5PWdzdWIoJ1xcLicsICcgJywgY29sdW1uWSkpDQogIA0KfQ0KDQpwbG90X3BlcmNlbnRpbGVzIDwtIGZ1bmN0aW9uIChkYXRhLCBjb2x1bW5YLCBjb2x1bW5ZLCBsZWdlbmRQb3NpdGlvbikgew0KICAgIGdncGxvdChkYXRhLCBhZXNfc3RyaW5nKHggPSBjb2x1bW5YLCB5ID0gY29sdW1uWSkpICsgDQogICAgZ2VvbV9wb2ludChhbHBoYT0wLjMpKw0KICAgIHNjYWxlX3lfbG9nMTAoYnJlYWtzID0gMTBeYygwOjE1KSkrDQogICAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IGMoMCwgMjUsIDUwLCA3NSwgMTAwKSkrICNzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gYygwOjEwKSoxMCkrDQogICAgdGhlbWVfaGFsZl9vcGVuKCkgKw0KICAgIGJhY2tncm91bmRfZ3JpZCgpICsNCiAgICB0aGVtZShsZWdlbmQudGl0bGU9ZWxlbWVudF9ibGFuaygpKSsNCiAgICBsYWJzKHg9Z3N1YignXFwuJywgJyAnLCBjb2x1bW5YKSkNCn0NCg0KYGBgDQoNCmBgYHtyIGZsYXR0ZW5pbmdEYXRhLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojdGl0bGUgPC0gbGFwcGx5KHdvcmtzRGF0YSwgZnVuY3Rpb24oeCkge3gkVGl0bGV9KQ0KYXV0aG9yIDwtIGxhcHBseSh3b3Jrc0RhdGEsIGZ1bmN0aW9uKHgpIHt4JEF1dGhvcn0pDQpmYW5kb20gPC0gbGFwcGx5KHdvcmtzRGF0YSwgZnVuY3Rpb24oeCkge3gkRmFuZG9tfSkNCnJhdGluZyA8LSBsYXBwbHkod29ya3NEYXRhLCBmdW5jdGlvbih4KSB7eCRSYXRpbmd9KQ0Kd2FybmluZ3MgPC0gbGFwcGx5KHdvcmtzRGF0YSwgZnVuY3Rpb24oeCkge3gkV2FybmluZ3N9KQ0KY2F0ZWdvcnkgPC0gbGFwcGx5KHdvcmtzRGF0YSwgZnVuY3Rpb24oeCkge3gkQ2F0ZWdvcnl9KQ0KV0lQIDwtIGxhcHBseSh3b3Jrc0RhdGEsIGZ1bmN0aW9uKHgpIHt4JFdJUH0pDQpkYXRlIDwtbGFwcGx5KHdvcmtzRGF0YSwgZnVuY3Rpb24oeCkge3gkRGF0ZX0pDQpyZWxhdGlvbnNoaXBzIDwtbGFwcGx5KHdvcmtzRGF0YSwgZnVuY3Rpb24oeCkge3gkUmVsYXRpb25zaGlwc30pDQpjaGFyYWN0ZXIgPC1sYXBwbHkod29ya3NEYXRhLCBmdW5jdGlvbih4KSB7eCRDaGFyYWN0ZXJ9KQ0KZnJlZWZvcm0gPC1sYXBwbHkod29ya3NEYXRhLCBmdW5jdGlvbih4KSB7eCRGcmVlZm9ybX0pDQpsYW5ndWFnZSA8LWxhcHBseSh3b3Jrc0RhdGEsIGZ1bmN0aW9uKHgpIHt4JExhbmd1YWdlfSkNCndvcmRzIDwtbGFwcGx5KHdvcmtzRGF0YSwgZnVuY3Rpb24oeCkge3gkV29yZHN9KQ0Kd29yZHNbaXMubmEod29yZHMpXSA8LSAwDQprdWRvcyA8LWxhcHBseSh3b3Jrc0RhdGEsIGZ1bmN0aW9uKHgpIHt4JEt1ZG9zfSkNCmt1ZG9zW2lzLm5hKGt1ZG9zKV0gPC0gMA0KY29tbWVudHMgPC1sYXBwbHkod29ya3NEYXRhLCBmdW5jdGlvbih4KSB7eCRDb21tZW50c30pDQpjb21tZW50c1tpcy5uYShjb21tZW50cyldIDwtIDANCmJvb2ttYXJrczwtbGFwcGx5KHdvcmtzRGF0YSwgZnVuY3Rpb24oeCkge3gkQm9va21hcmtzfSkNCmJvb2ttYXJrc1tpcy5uYShib29rbWFya3MpXSA8LSAwDQpoaXRzIDwtbGFwcGx5KHdvcmtzRGF0YSwgZnVuY3Rpb24oeCkge3gkSGl0c30pDQpoaXRzW2lzLm5hKGhpdHMpXSA8LSAwDQoNCnN0YXRzIDwtIGRhdGEuZnJhbWUoV29yZHMgPSB1bmxpc3Qod29yZHMsIHJlY3Vyc2l2ZSA9IEZBTFNFKSwNCiAgICAgICAgICAgICAgICAgICAgQ29tbWVudHM9IGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKGNvbW1lbnRzKSksDQogICAgICAgICAgICAgICAgICAgIEt1ZG9zID0gYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoa3Vkb3MpKSwNCiAgICAgICAgICAgICAgICAgICAgQm9va21hcmtzID0gYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoYm9va21hcmtzKSksDQogICAgICAgICAgICAgICAgICAgIEhpdHMgPSBhcy5udW1lcmljKGFzLmNoYXJhY3RlcihoaXRzKSksDQogICAgICAgICAgICAgICAgICAgIFdJUCA9IHVubGlzdChXSVAsIHJlY3Vyc2l2ZSA9IEZBTFNFKSwNCiAgICAgICAgICAgICAgICAgICAgUmF0aW5nID0gdW5saXN0KHJhdGluZywgcmVjdXJzaXZlID0gRkFMU0UpLA0KICAgICAgICAgICAgICAgICAgICBEYXRlID0gZG8uY2FsbCgiYyIsIGRhdGUpKQ0KDQpzdGF0cyRSYXRpbmcgPC0gZmFjdG9yKHN0YXRzJFJhdGluZywgbGV2ZWxzID0gYygiTm90IFJhdGVkIiwgIkdlbmVyYWwgQXVkaWVuY2VzIiwgIlRlZW4gQW5kIFVwIEF1ZGllbmNlcyIsICJNYXR1cmUiLCAiRXhwbGljaXQiKSkNCg0KdG90YWwgPC0gMTAwMA0KcGVyY2VudGlsZSA8LSBjKDE6dG90YWwpDQpwZXJjZW50aWxlRGF0YSA8LSBkYXRhLmZyYW1lKFdvcmtzLlBlcmNlbnRpbGUgPSAxMDAqKHRvdGFsIC0gcGVyY2VudGlsZSkvdG90YWwsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIFdvcmRzID0gdW5saXN0KGxhcHBseShwZXJjZW50aWxlL3RvdGFsLCBxdWFudGlsZSwgeCA9IHVubGlzdCh3b3JkcykgKSkgKyAxLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBIaXRzID0gdW5saXN0KGxhcHBseShwZXJjZW50aWxlL3RvdGFsLCBxdWFudGlsZSwgeCA9IHVubGlzdChoaXRzKSApKSArIDEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIEt1ZG9zID0gdW5saXN0KGxhcHBseShwZXJjZW50aWxlL3RvdGFsLCBxdWFudGlsZSwgeCA9IHVubGlzdChrdWRvcykgKSkgKyAxLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBDb21tZW50cyA9IHVubGlzdChsYXBwbHkocGVyY2VudGlsZS90b3RhbCwgcXVhbnRpbGUsIHggPSB1bmxpc3QoY29tbWVudHMpICkpICsgMSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQm9va21hcmtzID0gdW5saXN0KGxhcHBseShwZXJjZW50aWxlL3RvdGFsLCBxdWFudGlsZSwgeCA9IHVubGlzdChib29rbWFya3MpICkpICsgMSApDQoNCnJtKHJhdGluZywga3Vkb3MsIGNvbW1lbnRzLCBib29rbWFya3MsIGhpdHMpDQoNCmBgYA0KDQojIFRpbWVsaW5lDQoNClNvbGlkIHZlcnRpY2FsIGxpbmVzIG9uIHRoZSBncmFwaCBpbmRpY2F0ZSBpbml0aWFsIGFpciBkYXRlcywgYW5kIGRhc2hlZCBpbmRpY2F0ZSBmaW5hbCBhaXIgZGF0ZXMsIGFjY29yZGluZyB0byBbV2lraSBhcnRpY2xlXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9CbGFja19TYWlsc18oVFZfc2VyaWVzKSNQbG90KS4NCg0KV2Ugc2VlIGEgcGVhayBvZiBhY3Rpdml0eSBhZnRlciBlYWNoIHNlYXNvbiB3aGljaCBidWlsZHMgdXAgdG8gYSBtYWpvciBwZWFrIGFmdGVyIHNlYXNvbiA0LiBBIG1pbm9yIGJ1bXAgaW4gTWF5IDIwMjAgY291bGQgYmUgYXR0cmlidXRlZCB0byBjb3JvbmF2aXJ1cyBsb2Nkb3ducy4NCg0KVGhlcmUgYXJlIDMgd29ya3MgcHVibGlzaGVkIGJlZm9yZSBzZWFzb24gMSBwcmVtaWVyLCBvbmUgb24gMjIgSnVuZSAyMDEzLCB3aGljaCBpcyBwcm9iYWJseSBhbiBhcnRpZmFjdCBvZiB3b3JrIGltcG9ydCwgdGhlIG90aGVyIHR3byBhcmUgZGF0ZWQgMjIgb2YgSmFudWFyeSBhcmUgdGFnZ2VkIHdpdGggIkFubmUgQm9ubnkvIkNhbGljbyIgSmFjayBSYWNraGFtICIgYW5kIGNvdWxkIGJlIGR1ZSB0byB0cmFpbGVyIGh5cGUgYW5kL29yIGhpc3RvcmljYWwgcGlyYXRlcyBmYW5kb20uDQoNCmBgYHtyIHRpbWVsaW5lVG90YWwsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTZ9DQoNCiNkYXRhJFRpbWVzdGFtcCA8LSBwYXJzZV9kYXRlX3RpbWUyKGFzLmNoYXJhY3RlcihkYXRhJFRpbWVzdGFtcCksIG9yZGVycyA9ICIlZC8lbS8lWSAlSDolTTolUyIpDQojZGF0YSRkYXkgPC0gYXMuRGF0ZShkYXRhJFRpbWVzdGFtcCkNCg0Kc2Vhc29uc1N0YXJ0IDwtIGMoIjIwMTQtMDEtMjUiLCAiMjAxNS0wMS0yNCIsICIyMDE2LTAxLTIzIiwgIjIwMTctMDEtMjkiKQ0Kc2Vhc29uc1N0YXJ0IDwtIGFzLkRhdGUoc2Vhc29uc1N0YXJ0KQ0Kc2Vhc29uc0VuZCA8LSBjKCIyMDE0LTAzLTE1IiwgIjIwMTUtMDMtMjgiLCAiMjAxNi0wMy0yNiIsICIyMDE3LTA0LTAyIikNCnNlYXNvbnNFbmQgPC0gYXMuRGF0ZShzZWFzb25zRW5kKQ0KDQpwbG90RGF0ZXNEZW5zaXR5VG90YWwgPC0gZ2dwbG90KHN0YXRzLCBhZXMoeCA9IERhdGUpKSArIA0KICAgICAgICAgICAgICAgICAgICBnZW9tX2RlbnNpdHkoKSsNCiAgICAgICAgICAgICAgICAgICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0PWFzLm51bWVyaWMoc2Vhc29uc1N0YXJ0KSkrDQogICAgICAgICAgICAgICAgICAgIGdlb21fdmxpbmUoeGludGVyY2VwdD1hcy5udW1lcmljKHNlYXNvbnNFbmQpLCBsaW5ldHlwZSA9ImxvbmdkYXNoIikrDQogICAgICAgICAgICAgICAgICAgIHNjYWxlX3hfZGF0ZShkYXRlX2JyZWFrcz0iMyBtb250aHMiKSsNCiAgICAgICAgICAgICAgICAgICAgdGhlbWVfaGFsZl9vcGVuKCkgKw0KICAgICAgICAgICAgICAgICAgICBiYWNrZ3JvdW5kX2dyaWQoKSArDQogICAgICAgICAgICAgICAgICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHZqdXN0ID0gMC41LCBoanVzdD0xKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gJ3JpZ2h0JykNCnBsb3REYXRlc0RlbnNpdHlUb3RhbA0KDQpybShwbG90RGF0ZXNEZW5zaXR5VG90YWwpDQpgYGANCg0KSWYgd2UgcGxvdCBDb21wbGV0ZSBXb3JrcyBhbmQgV29ya3MgaW4gUHJvZ3Jlc3Mgc2VwYXJhdGVseSwgd2Ugc3RpbGwgb2JzZXJ2ZSBhbiBvdmVyYWxsIHBlYWsgc3RydWN0dXJlIGluIGNvbXBsZXRlIHdvcmtzLCBidXQsIGludGVyZXN0aW5nbHksIFdvcmtzIGluIFByb2dyZXNzIGZvbGxvdyB0aGUgYmFzaWMgc3RydWN0dXJlIGJ1dCBkb24ndCBmbHVjdHVhdGUgbXVjaCB3aXRoIG5ldyBzZWFzb25zLg0KDQpgYGB7ciB0aW1lbGluZVdJUCwgbWVzc2FnZSA9IEZBTFNFLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9Nn0NCg0KcGxvdERhdGVzRGVuc2l0eSA8LSBnZ3Bsb3Qoc3RhdHMsIGFlcyh4ID0gRGF0ZSwgY29sPVdJUCkpICsgDQogICAgICAgICAgICAgICAgICAgIGdlb21fZGVuc2l0eShhbHBoYSA9IDAuMSkrDQogICAgICAgICAgICAgICAgICAgIGdlb21fdmxpbmUoeGludGVyY2VwdD1zZWFzb25zU3RhcnQpKw0KICAgICAgICAgICAgICAgICAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQ9c2Vhc29uc0VuZCwgbGluZXR5cGUgPSJsb25nZGFzaCIpKw0KICAgICAgICAgICAgICAgICAgICBzY2FsZV94X2RhdGUoZGF0ZV9icmVha3M9IjMgbW9udGhzIikrDQogICAgICAgICAgICAgICAgICAgIHRoZW1lX2hhbGZfb3BlbigpICsNCiAgICAgICAgICAgICAgICAgICAgYmFja2dyb3VuZF9ncmlkKCkgKw0KICAgICAgICAgICAgICAgICAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDAuNSwgaGp1c3Q9MSksDQogICAgICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICdyaWdodCcpDQpwbG90RGF0ZXNEZW5zaXR5DQoNCnJtKHBsb3REYXRlc0RlbnNpdHkpDQoNCmBgYA0KDQoNCiMgRW5nYWdlbWVudCBwZXJjZW50aWxlcw0KDQpTbWFsbCBwbG90dGluZyBjaGVhdDogYWxsIHRoZSBudW1iZXJzIG9uIHRoZSBZIGF4aXMgYXJlIGluY3JlYXNlZCBieSAxIHRvIGluY2x1ZGUgdGhlIGNhc2Ugb2YgMCBpbnRvIHRoZSBwbG90IChvdGhlcndpc2UgZXhjbHVkZWQgYmVjYXVzZSBvZiBsb2cgc2NhbGUpLg0KDQoqIEFib3V0IDc1JSBvZiB3b3JrcyBoYXZlIG1vcmUgdGhhbiBhIDEwMDAgd29yZHMsIGJ1dCBvbmx5IGFib3V0IDEwJSBoYXZlIG1vcmUgdGhhbiAxMDAwMCB3b3Jkcy4NCiogT25seSBhYm91dCAyNSUgb2Ygd29ya3MgaGF2ZSBvdmVyIGEgMTAwMCBoaXRzLg0KKiBPbmx5IGFib3V0IDI1JSBvZiB3b3JrcyBoYXZlIG1vcmUgdGhhbiBhIDEwMCBrdWRvcy4NCiogT25seSBhYm91dCA1MCUgb2Ygd29ya3MgZ2V0IG1vcmUgdGhhbiAxMCBjb21tZW50cy4NCiogQXBwcm94aW1hdGVseSA1JSBvZiB3b3JrcyBoYXZlIG5vIGNvbW1lbnRzICh0YWlsIGVuZCkuDQoqIE9ubHkgYXBwcm94aW1hdGVseSAzNSUgb2Ygd29ya3MgZ2V0IG1vcmUgdGhhbiAxMCBib29rbWFya3MuDQoqIEFwcHJveGltYXRlbHkgMTUlIG9mIHdvcmtzIGhhdmUgbm8gYm9va21hcmtzICh0YWlsIGVuZCkuDQoNCmBgYHtyIHBlcmNlbnRpbGVzLCBtZXNzYWdlID0gRkFMU0V9DQp3b3Jkc1BlcmNlbnRpbGVzIDwtIHBsb3RfcGVyY2VudGlsZXMocGVyY2VudGlsZURhdGEsICdXb3Jrcy5QZXJjZW50aWxlJywgJ1dvcmRzJywgJ3JpZ2h0JykNCmhpdHNQZXJjZW50aWxlcyA8LSBwbG90X3BlcmNlbnRpbGVzKHBlcmNlbnRpbGVEYXRhLCAnV29ya3MuUGVyY2VudGlsZScsICdIaXRzJywgJ3JpZ2h0JykNCmt1ZG9zUGVyY2VudGlsZXMgPC0gcGxvdF9wZXJjZW50aWxlcyhwZXJjZW50aWxlRGF0YSwgJ1dvcmtzLlBlcmNlbnRpbGUnLCAnS3Vkb3MnLCAncmlnaHQnKQ0KY29tbWVudHNQZXJjZW50aWxlcyA8LSBwbG90X3BlcmNlbnRpbGVzKHBlcmNlbnRpbGVEYXRhLCAnV29ya3MuUGVyY2VudGlsZScsICdDb21tZW50cycsICdyaWdodCcpDQpib29rbWFya3NQZXJjZW50aWxlcyA8LSBwbG90X3BlcmNlbnRpbGVzKHBlcmNlbnRpbGVEYXRhLCAnV29ya3MuUGVyY2VudGlsZScsICdCb29rbWFya3MnLCAncmlnaHQnKQ0KDQpwbG90X2dyaWQod29yZHNQZXJjZW50aWxlcyArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpLA0KICAgICAgICAgIGhpdHNQZXJjZW50aWxlcyArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpLA0KICAgICAgICAgIGt1ZG9zUGVyY2VudGlsZXMgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSwNCiAgICAgICAgICBjb21tZW50c1BlcmNlbnRpbGVzICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiksDQogICAgICAgICAgYm9va21hcmtzUGVyY2VudGlsZXMgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSApDQoNCnJtKHRvdGFsLCBwZXJjZW50aWxlLCBwZXJjZW50aWxlRGF0YSwgd29yZHNQZXJjZW50aWxlcywgaGl0c1BlcmNlbnRpbGVzLCBrdWRvc1BlcmNlbnRpbGVzLCBjb21tZW50c1BlcmNlbnRpbGVzLCBib29rbWFya3NQZXJjZW50aWxlcykNCg0KYGBgDQoNCiMgQ29tcGxldGUgV29yayB2cyBXb3JrIGluIFByb2dyZXNzIGRpc3RyaWJ1dGlvbnMNCg0KKiBNYWpvcml0eSBvZiB3b3JrcyBhcmUgQ29tcGxldGUNCiogV29ya3MgaW4gUHJvZ3Jlc3MgYXJlIG1vcmUgdGhhbiAzIHRpbWVzIGxvbmdlciB0aGFuIENvbXBsZXRlIG9uZXMuDQoqIEJvdGggQ29tcGxldGUgV29ya3MgYW5kIFdvcmtzIGluIFByb2dyZXNzIGdldCBhcHByb3hpbWF0ZWx5IHRoZSBzYW1lIGFtb3VudHMgb2YgaGl0cy4NCiogQ29tcGxldGUgV29ya3MgZ2V0IGFib3V0IDIwJSBtb3JlIGt1ZG9zLg0KKiBXb3JrcyBpbiBQcm9ncmVzcyBnZXQgYXBwcm94aW1hdGVseSAyIHRpbWVzIGFzIG1hbnkgY29tbWVudHMgYXMgQ29tcGxldGUgb25lcyAoaG93ZXZlciwgYWdhaW4sIHRoZXJlJ3Mgbm8gd2F5IHRvIGZpbHRlciBvdXQgYXV0aG9yJ3MgY29tbWVudHMgaW4gdGhlIHNlYXJjaCBzZWxlY3Rpb24pLg0KKiBDb21wbGV0ZSBXb3JrcyBnZXQgc2xpZ2h0bHkgbW9yZSBib29rbWFya3MgdGhhbiBXb3JrcyBpbiBQcm9ncmVzcy4NCg0KYGBge3IgdG90YWxXb3Jrc1dJUCwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nPUZBTFNFLCBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD02fQ0KDQpzdGF0c1dJUCA8LSBzdGF0cw0Kc3RhdHNXSVAkRGl2aXNvciA8LSB1bmxpc3QobGFwcGx5KHN0YXRzV0lQJFdJUCwgZnVuY3Rpb24oeCkgc3VtbWFyeShzdGF0c1dJUCRXSVApW25hbWVzKHN1bW1hcnkoc3RhdHNXSVAkV0lQKSkgPT0geF0pKQ0Kc3RhdHNXSVAkV29yZHMucGVyLldvcmsgPC0gc3RhdHNXSVAkV29yZHMvc3RhdHNXSVAkRGl2aXNvcg0Kc3RhdHNXSVAkSGl0cy5wZXIuV29yayA8LSBzdGF0c1dJUCRIaXRzL3N0YXRzV0lQJERpdmlzb3INCnN0YXRzV0lQJEt1ZG9zLnBlci5Xb3JrIDwtIHN0YXRzV0lQJEt1ZG9zL3N0YXRzV0lQJERpdmlzb3INCnN0YXRzV0lQJENvbW1lbnRzLnBlci5Xb3JrIDwtIHN0YXRzV0lQJENvbW1lbnRzL3N0YXRzV0lQJERpdmlzb3INCnN0YXRzV0lQJEJvb2ttYXJrcy5wZXIuV29yayA8LSBzdGF0c1dJUCRCb29rbWFya3Mvc3RhdHNXSVAkRGl2aXNvcg0KDQpiYXJXb3Jrc1dJUCA8LSBwbG90X2JhcihzdGF0c1dJUCwgJ1dJUCcsICdyaWdodCcpDQpiYXJXb3Jkc1dJUCA8LSBwbG90X2NvbChzdGF0c1dJUCwgJ1dJUCcsICdXb3Jkcy5wZXIuV29yaycsICdyaWdodCcpDQpiYXJIaXRzV0lQIDwtIHBsb3RfY29sKHN0YXRzV0lQLCAnV0lQJywgJ0hpdHMucGVyLldvcmsnLCAncmlnaHQnKQ0KYmFyS3Vkb3NXSVAgPC0gcGxvdF9jb2woc3RhdHNXSVAsICdXSVAnLCAnS3Vkb3MucGVyLldvcmsnLCAncmlnaHQnKQ0KYmFyQ29tbWVudHNXSVAgPC0gcGxvdF9jb2woc3RhdHNXSVAsICdXSVAnLCAnQ29tbWVudHMucGVyLldvcmsnLCAncmlnaHQnKQ0KYmFyQm9va21hcmtzV0lQIDwtIHBsb3RfY29sKHN0YXRzV0lQLCAnV0lQJywgJ0Jvb2ttYXJrcy5wZXIuV29yaycsICdyaWdodCcpDQoNCiMgcGxvdF9ncmlkKHBsb3RfZ3JpZCggYmFyV29ya3NXSVAgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSwNCiMgICAgICAgICAgICAgICAgICAgICAgYmFyV29yZHNXSVAgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSwNCiMgICAgICAgICAgICAgICAgICAgICAgYmFySGl0c1dJUCArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpLA0KIyAgICAgICAgICAgICAgICAgICAgICBiYXJLdWRvc1dJUCArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpLA0KIyAgICAgICAgICAgICAgICAgICAgICBiYXJDb21tZW50c1dJUCArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpLA0KIyAgICAgICAgICAgICAgICAgICAgICBiYXJCb29rbWFya3NXSVAgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSwNCiMgICAgICAgICAgICAgICAgICAgICAgYWxpZ24gPSAnaHYnKSwNCiMgICAgICAgICAgIGdldF9sZWdlbmQoYmFyV29ya3NXSVAgKyB0aGVtZShsZWdlbmQudGl0bGU9ZWxlbWVudF9ibGFuaygpKSksDQojICAgICAgICAgICByZWxfd2lkdGhzID0gYyg0LDEpLA0KIyAgICAgICAgICAgYWxpZ24gPSAnaHYnKQ0KcGxvdF9ncmlkKCBiYXJXb3Jrc1dJUCArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpLA0KICAgICAgICAgICBiYXJXb3Jkc1dJUCArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpLA0KICAgICAgICAgICBiYXJIaXRzV0lQICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiksDQogICAgICAgICAgIGJhckt1ZG9zV0lQICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiksDQogICAgICAgICAgIGJhckNvbW1lbnRzV0lQICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiksDQogICAgICAgICAgIGJhckJvb2ttYXJrc1dJUCArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpLA0KICAgICAgICAgICBhbGlnbiA9ICdodicpDQoNCnJtKHN0YXRzV0lQLCBiYXJXb3Jrc1dJUCwgYmFyV29yZHNXSVAsIGJhckhpdHNXSVAsIGJhckt1ZG9zV0lQLCBiYXJDb21tZW50c1dJUCwgYmFyQm9va21hcmtzV0lQKQ0KYGBgDQoNCiMgUmF0aW5nIGRpc3RyaWJ1dGlvbnMNCg0KKiBNb3N0IHdvcmtzIGFyZSByYXRlZCBUIGFuZCBFLCBidXQgRyBhbmQgTSByYXRlZCB3b3JrcyBhcmUgbm90IGZhciBiZWhpbmQuDQoqIFdvcmtzIHJhdGVkIEcgYXJlIG9uIGF2ZXJhZ2UgdGhlIHNob3J0ZXN0ICh+MTUwMCB3b3JkcyksIGZvbGxvd2VkIGJ5IFQgKH40MDAwIHdvcmRzKSwgTm90IHJhdGVkICh+OTAwMCB3b3JkcyksIEUgKH4gOTAwMCB3b3JkcyksIGFuZCBNICh+MTEwMDAgd29yZHMpLiBUaGUgdHJlbmQgb2YgTSByYXRlZCB3b3JrcyBiZWluZyB0aGUgbG9uZ2VzdCB3ZSBvYnNlcnZlZCBpbiBvdGhlciBmYW5kb21zIGhvbGRzIGhlcmUgYXMgd2VsbC4NCiogRyByYXRlZCB3b3JrcyBnZXQgZmV3ZXN0IGhpdHMgKH41MDApLCBmb2xsb3dlZCBieSBOb3QgUmF0ZWQgKH43MDApLiBPdGhlcndpc2UsIHRoZSBudW1iZXIgb2YgaGl0cyByaXNlcyB3aXRoIHRoZSByYXRpbmcuIEUgcmF0ZWQgd29ya3MgYXJlIG1vc3QgcG9wdWxhciAofjE2MDApLg0KKiBOdW1iZXIgb2Yga3Vkb3MsIGNvbW1lbnRzIGFuZCBib29rbWFya3Mgc2VlbSB0byBiZSBicm9hZGx5IHByb3BvcnRpb25hbCB0byB0aGUgbnVtYmVyIG9mIGhpdHMuDQoNCg0KYGBge3IgdG90YWxXb3Jrc1JhdGluZywgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nPUZBTFNFLCBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD04fQ0KDQpzdGF0c1JhdGluZyA8LSBzdGF0cw0Kc3RhdHNSYXRpbmckRGl2aXNvciA8LSB1bmxpc3QobGFwcGx5KHN0YXRzUmF0aW5nJFJhdGluZywgZnVuY3Rpb24oeCkgc3VtbWFyeShzdGF0c1JhdGluZyRSYXRpbmcpW25hbWVzKHN1bW1hcnkoc3RhdHNSYXRpbmckUmF0aW5nKSkgPT0geF0pKQ0Kc3RhdHNSYXRpbmckV29yZHMucGVyLldvcmsgPC0gc3RhdHNSYXRpbmckV29yZHMvc3RhdHNSYXRpbmckRGl2aXNvcg0Kc3RhdHNSYXRpbmckSGl0cy5wZXIuV29yayA8LSBzdGF0c1JhdGluZyRIaXRzL3N0YXRzUmF0aW5nJERpdmlzb3INCnN0YXRzUmF0aW5nJEt1ZG9zLnBlci5Xb3JrIDwtIHN0YXRzUmF0aW5nJEt1ZG9zL3N0YXRzUmF0aW5nJERpdmlzb3INCnN0YXRzUmF0aW5nJENvbW1lbnRzLnBlci5Xb3JrIDwtIHN0YXRzUmF0aW5nJENvbW1lbnRzL3N0YXRzUmF0aW5nJERpdmlzb3INCnN0YXRzUmF0aW5nJEJvb2ttYXJrcy5wZXIuV29yayA8LSBzdGF0c1JhdGluZyRCb29rbWFya3Mvc3RhdHNSYXRpbmckRGl2aXNvcg0KDQpiYXJXb3Jrc1JhdGluZyA8LSBwbG90X2JhcihzdGF0c1JhdGluZywgJ1JhdGluZycsICdyaWdodCcpDQpiYXJXb3Jkc1JhdGluZyA8LSBwbG90X2NvbChzdGF0c1JhdGluZywgJ1JhdGluZycsICdXb3Jkcy5wZXIuV29yaycsICdyaWdodCcpDQpiYXJIaXRzUmF0aW5nIDwtIHBsb3RfY29sKHN0YXRzUmF0aW5nLCAnUmF0aW5nJywgJ0hpdHMucGVyLldvcmsnLCAncmlnaHQnKQ0KYmFyS3Vkb3NSYXRpbmcgPC0gcGxvdF9jb2woc3RhdHNSYXRpbmcsICdSYXRpbmcnLCAnS3Vkb3MucGVyLldvcmsnLCAncmlnaHQnKQ0KYmFyQ29tbWVudHNSYXRpbmcgPC0gcGxvdF9jb2woc3RhdHNSYXRpbmcsICdSYXRpbmcnLCAnQ29tbWVudHMucGVyLldvcmsnLCAncmlnaHQnKQ0KYmFyQm9va21hcmtzUmF0aW5nIDwtIHBsb3RfY29sKHN0YXRzUmF0aW5nLCAnUmF0aW5nJywgJ0Jvb2ttYXJrcy5wZXIuV29yaycsICdyaWdodCcpDQoNCnBsb3RfZ3JpZCggYmFyV29ya3NSYXRpbmcgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSwNCiAgICAgICAgICAgYmFyV29yZHNSYXRpbmcgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSwNCiAgICAgICAgICAgYmFySGl0c1JhdGluZyArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpLA0KICAgICAgICAgICBiYXJLdWRvc1JhdGluZyArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpLA0KICAgICAgICAgICBiYXJDb21tZW50c1JhdGluZyArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpLA0KICAgICAgICAgICBiYXJCb29rbWFya3NSYXRpbmcgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSwNCiAgICAgICAgICAgYWxpZ24gPSAnaHYnKQ0KDQpybShzdGF0c1JhdGluZywgYmFyV29ya3NSYXRpbmcsIGJhcldvcmRzUmF0aW5nLCBiYXJIaXRzUmF0aW5nLCBiYXJLdWRvc1JhdGluZywgYmFyQ29tbWVudHNSYXRpbmcsIGJhckJvb2ttYXJrc1JhdGluZykNCmBgYA0KDQojIENhdGVnb3JpZXMNCg0KVGhlcmUgYXJlIGByIGxlbmd0aChjYXRlZ29yeVt1bmxpc3QobGFwcGx5KGNhdGVnb3J5LCBmdW5jdGlvbih4KSBsZW5ndGgoeCkpKSA9PSAxXSlgIHdvcmtzIHRhZ2dlZCB3aXRoIGEgc2luZ2xlIGNhdGVnb3J5LCBhbmQgYHIgbGVuZ3RoKGNhdGVnb3J5W3VubGlzdChsYXBwbHkoY2F0ZWdvcnksIGZ1bmN0aW9uKHgpIGxlbmd0aCh4KSkpID4gMV0pYCB0YWdnZWQgd2l0aCAyIG9yIG1vcmUgKHVwIHVudGlsIGFsbCA2KS4NCg0KJ00vTScgaXMgdGhlIG1vc3QgcG9wdWxhciBjYXRlZ29yeSwgYW5kIGl0IGR3YXJmcyBhbGwgdGhlIG90aGVycy4NCg0KTXVsdGlwbGUgY2F0ZWdvcnkgZmljcyBzdHJvbmdseSBjb250cmlidXRlIHRvd2FyZHMgJ00vTScgY291bnQsIHRoZW4gdG8gJ0YvTScsICdHZW4nLCBhbmQgJ0YvRicsIGFuZCBvbmx5IG1hcmdpbmFsbHkgdG8gJ011bHRpJyBhbmQgJ090aGVyJy4NCg0KYGBge3IgY2F0ZWdvcmllc0JhcnMsIG1lc3NhZ2UgPSBGQUxTRX0NCg0Kc2luZ2xlQ2F0ZWdvcnlTdW1tYXJ5IDwtIHN1bW1hcnkoYXMuZmFjdG9yKHVubGlzdChjYXRlZ29yeVt1bmxpc3QobGFwcGx5KGNhdGVnb3J5LCBmdW5jdGlvbih4KSBsZW5ndGgoeCkpKSA9PSAxXSkpKQ0Kc2luZ2xlQ2F0ZWdvcnlTdW1tYXJ5IDwtIGRhdGEuZnJhbWUoQ2F0ZWdvcnkgPSBuYW1lcyhzaW5nbGVDYXRlZ29yeVN1bW1hcnkpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTnVtYmVyLm9mLldvcmtzID0gc2luZ2xlQ2F0ZWdvcnlTdW1tYXJ5KQ0Kc2luZ2xlQ2F0ZWdvcnlTdW1tYXJ5JFNwbGl0IDwtICJTaW5nbGUgY2F0ZWdvcnkiDQoNCm11bHRpcGxlQ2F0ZWdvcnlTdW1tYXJ5IDwtIGRhdGEuZnJhbWUoQ2F0ZWdvcnkgPSBjKCdHZW4nLCAnRi9GJywgJ0YvTScsICdNL00nLCAnTXVsdGknLCAnT3RoZXInLCAnTm8gY2F0ZWdvcnknKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE51bWJlci5vZi5Xb3JrcyA9IGMoc3VtKGdyZXBsKCdHZW4nLGNhdGVnb3J5KSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1bShncmVwbCgnRi9GJyxjYXRlZ29yeSkpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdW0oZ3JlcGwoJ0YvTScsY2F0ZWdvcnkpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VtKGdyZXBsKCdNL00nLGNhdGVnb3J5KSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1bShncmVwbCgnTXVsdGknLGNhdGVnb3J5KSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1bShncmVwbCgnT3RoZXInLGNhdGVnb3J5KSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1bShncmVwbCgnTm8gY2F0ZWdvcnknLGNhdGVnb3J5KSkpICkNCm11bHRpcGxlQ2F0ZWdvcnlTdW1tYXJ5JFNwbGl0IDwtICJBbGwgd29ya3MiDQoNCmNhdGVnb3J5U3VtbWFyeSA8LSByYmluZChzaW5nbGVDYXRlZ29yeVN1bW1hcnksIG11bHRpcGxlQ2F0ZWdvcnlTdW1tYXJ5KQ0KY2F0ZWdvcnlTdW1tYXJ5JENhdGVnb3J5IDwtIGZhY3RvcihjYXRlZ29yeVN1bW1hcnkkQ2F0ZWdvcnksIGxldmVscyA9IGMoJ0dlbicsICdGL0YnLCAnRi9NJywgJ00vTScsICdNdWx0aScsICdPdGhlcicsICdObyBjYXRlZ29yeScpKQ0KY2F0ZWdvcnlTdW1tYXJ5JFNwbGl0IDwtIGZhY3RvcihjYXRlZ29yeVN1bW1hcnkkU3BsaXQsIGxldmVscyA9IGMoIlNpbmdsZSBjYXRlZ29yeSIsICJBbGwgd29ya3MiKSkNCg0KcGxvdENhdGVnb3JpZXMgPC0gZ2dwbG90KGNhdGVnb3J5U3VtbWFyeSwgYWVzKHggPSBDYXRlZ29yeSwgeSA9IE51bWJlci5vZi5Xb3JrcykpICsgDQogICAgICAgICAgICAgICAgICBnZW9tX2NvbChhbHBoYT0xKSsNCiAgICAgICAgICAgICAgICAgIHRoZW1lX2hhbGZfb3BlbigpICsNCiAgICAgICAgICAgICAgICAgIGJhY2tncm91bmRfZ3JpZCgpICsNCiAgICAgICAgICAgICAgICAgIGZhY2V0X3dyYXAoLn5TcGxpdCkgKw0KICAgICAgICAgICAgICAgICAgdGhlbWUobGVnZW5kLnRpdGxlPWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgICAgICAgICAgICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgICAgICAgICAgICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHZqdXN0ID0gMSwgaGp1c3Q9MSkpKw0KICAgICAgICAgICAgICAgICAgbGFicyh5PSJOdW1iZXIgb2YgV29ya3MiKQ0KcGxvdENhdGVnb3JpZXMNCg0Kcm0oc2luZ2xlQ2F0ZWdvcnlTdW1tYXJ5LCBtdWx0aXBsZUNhdGVnb3J5U3VtbWFyeSwgY2F0ZWdvcnlTdW1tYXJ5LCBwbG90Q2F0ZWdvcmllcykNCg0KYGBgDQoNCiMgRW5nYWdlbWVudCBieSBhIHNpbmdsZSBjYXRlZ29yeQ0KDQpGb3Igc2ltcGxpY2l0eSBJJ20gb25seSBsb29raW5nIGF0IHdvcmtzIHRhZ2dlZCB3aXRoIGEgc2luZ2xlIGNhdGVnb3J5IGhlcmUuDQoNCiJPdGhlciIgc2VlbXMgdG8gaGF2ZSBtb3N0IHdvcmRzLCBkZXNwaXRlIGJlaW5nIGEgdGlueSBjYXRlZ29yeSwgYW5kIGNvbGxlY3RzIHF1aXRlIGEgYml0IG9mIEhpdHMsIEt1ZG9zLCBDb21tZW50cyBhbmQgQm9va21hcmtzLiBJdCdzIHBvc3NpYmxlIHRoYXQgYSBudW1iZXIgb2YgdGhvc2Ugd29ya3MgYXJlIGNvbGxlY3Rpb25zIG9mIHN0b3JpZXMgZm9yIG1hbnkgZmFuZG9tcywgd2hpY2ggYW1wbGlmaWVzIHRoZSBlbmdhZ2VtZW50IG51bWJlcnMuDQoNCidNL00nIGFuZCAnRi9GJyB3b3JrcyBjb2xsZWN0IHRoZSBtb3N0IGhpdHMsIGJ1dCAnRi9GJyBnZXRzIHNpZ25pZmljYW50bHkgZmV3ZXIga3Vkb3MsIGNvbW1lbnRzIGFuZCBib29rbWFya3MuDQoNCmBgYHtyIGNhdGVnb3JpZXNTaW5nbGVFbmdhZ2VtZW50LCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRSwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTZ9DQoNCnN0YXRzQ2F0ZWdvcnkgPC0gc3RhdHNbdW5saXN0KGxhcHBseShjYXRlZ29yeSwgZnVuY3Rpb24oeCkgbGVuZ3RoKHgpKSkgPT0gMSxdDQpzdGF0c0NhdGVnb3J5JENhdGVnb3J5IDwtIGFzLmZhY3Rvcih1bmxpc3QoY2F0ZWdvcnlbdW5saXN0KGxhcHBseShjYXRlZ29yeSwgZnVuY3Rpb24oeCkgbGVuZ3RoKHgpKSkgPT0gMV0pKQ0Kc3RhdHNDYXRlZ29yeSRDYXRlZ29yeSA8LSBmYWN0b3Ioc3RhdHNDYXRlZ29yeSRDYXRlZ29yeSwgbGV2ZWxzID0gYygnR2VuJywgJ0YvRicsICdGL00nLCAnTS9NJywgJ011bHRpJywgJ090aGVyJywgJ05vIGNhdGVnb3J5JykpDQpzdGF0c0NhdGVnb3J5JERpdmlzb3IgPC0gdW5saXN0KGxhcHBseShzdGF0c0NhdGVnb3J5JENhdGVnb3J5LCBmdW5jdGlvbih4KSBzdW1tYXJ5KHN0YXRzQ2F0ZWdvcnkkQ2F0ZWdvcnkpW25hbWVzKHN1bW1hcnkoc3RhdHNDYXRlZ29yeSRDYXRlZ29yeSkpID09IHhdKSkNCnN0YXRzQ2F0ZWdvcnkkV29yZHMucGVyLldvcmsgPC0gc3RhdHNDYXRlZ29yeSRXb3Jkcy9zdGF0c0NhdGVnb3J5JERpdmlzb3INCnN0YXRzQ2F0ZWdvcnkkSGl0cy5wZXIuV29yayA8LSBzdGF0c0NhdGVnb3J5JEhpdHMvc3RhdHNDYXRlZ29yeSREaXZpc29yDQpzdGF0c0NhdGVnb3J5JEt1ZG9zLnBlci5Xb3JrIDwtIHN0YXRzQ2F0ZWdvcnkkS3Vkb3Mvc3RhdHNDYXRlZ29yeSREaXZpc29yDQpzdGF0c0NhdGVnb3J5JENvbW1lbnRzLnBlci5Xb3JrIDwtIHN0YXRzQ2F0ZWdvcnkkQ29tbWVudHMvc3RhdHNDYXRlZ29yeSREaXZpc29yDQpzdGF0c0NhdGVnb3J5JEJvb2ttYXJrcy5wZXIuV29yayA8LSBzdGF0c0NhdGVnb3J5JEJvb2ttYXJrcy9zdGF0c0NhdGVnb3J5JERpdmlzb3INCnN0YXRzQ2F0ZWdvcnkkV29ya3MuUGVyY2VudCA8LSAxL3N0YXRzQ2F0ZWdvcnkkRGl2aXNvcg0KDQpiYXJXb3Jrc0NhdGVnb3J5IDwtIHBsb3RfYmFyX2NvbG9yKHN0YXRzQ2F0ZWdvcnksICdDYXRlZ29yeScsICdSYXRpbmcnLCAncmlnaHQnKQ0KYmFyV29yZHNDYXRlZ29yeSA8LSBwbG90X2NvbF9jb2xvcihzdGF0c0NhdGVnb3J5LCAnQ2F0ZWdvcnknLCAnV29yZHMucGVyLldvcmsnLCAnUmF0aW5nJywgJ3JpZ2h0JykNCmJhckhpdHNDYXRlZ29yeSA8LSBwbG90X2NvbF9jb2xvcihzdGF0c0NhdGVnb3J5LCAnQ2F0ZWdvcnknLCAnSGl0cy5wZXIuV29yaycsICdSYXRpbmcnLCAncmlnaHQnKQ0KYmFyS3Vkb3NDYXRlZ29yeSA8LSBwbG90X2NvbF9jb2xvcihzdGF0c0NhdGVnb3J5LCAnQ2F0ZWdvcnknLCAnS3Vkb3MucGVyLldvcmsnLCAnUmF0aW5nJywgJ3JpZ2h0JykNCmJhckNvbW1lbnRzQ2F0ZWdvcnkgPC0gcGxvdF9jb2xfY29sb3Ioc3RhdHNDYXRlZ29yeSwgJ0NhdGVnb3J5JywgJ0NvbW1lbnRzLnBlci5Xb3JrJywgJ1JhdGluZycsICdyaWdodCcpDQpiYXJCb29rbWFya3NDYXRlZ29yeSA8LSBwbG90X2NvbF9jb2xvcihzdGF0c0NhdGVnb3J5LCAnQ2F0ZWdvcnknLCAnQm9va21hcmtzLnBlci5Xb3JrJywnUmF0aW5nJywgJ3JpZ2h0JykNCg0KcGxvdF9ncmlkKHBsb3RfZ3JpZCggYmFyV29ya3NDYXRlZ29yeSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpLA0KICAgICAgICAgICBiYXJXb3Jkc0NhdGVnb3J5ICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiksDQogICAgICAgICAgIGJhckhpdHNDYXRlZ29yeSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpLA0KICAgICAgICAgICBiYXJLdWRvc0NhdGVnb3J5ICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiksDQogICAgICAgICAgIGJhckNvbW1lbnRzQ2F0ZWdvcnkgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSwNCiAgICAgICAgICAgYmFyQm9va21hcmtzQ2F0ZWdvcnkgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSwNCiAgICAgICAgICAgYWxpZ24gPSAnaHYnKSwNCiAgICAgICAgICBnZXRfbGVnZW5kKGJhcldvcmtzQ2F0ZWdvcnkgKyB0aGVtZShsZWdlbmQudGl0bGU9ZWxlbWVudF9ibGFuaygpKSksDQogICAgICAgICAgcmVsX3dpZHRocyA9IGMoNCwxKSkNCg0KYGBgDQoNCiMgUmF0aW5ncyBwZXJjZW50YWdlcyBieSBhIHNpbmdsZSBjYXRlZ29yeQ0KDQpJbiBhYnNvbHV0ZSBudW1iZXJzICJNL00iIGlzIHRoZSBtb3N0IHBvcHVsYXIgY2F0ZWdvcnksIGFuZCBpbiByZWxhdGl2ZSBudW1iZXJzIGl0IGdldHMgdGhlIG1vc3QgZXhwbGljaXQgd29ya3MuDQoNCmBgYHtyIGNhdGVnb3JpZXNTaW5nbGVFbmdhZ2VtZW50UGVyY2VudCwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0UsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD02fQ0KDQpwbG90V29ya3NDYXRlZ29yeU5vcm1hbGl6ZWQgPC0gcGxvdF9jb2xfY29sb3Ioc3RhdHNDYXRlZ29yeSwgJ1JhdGluZycsICdXb3Jrcy5QZXJjZW50JywgJ1JhdGluZycsICdub25lJykrDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscz1zY2FsZXM6OnBlcmNlbnQpKw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZhY2V0X3dyYXAoLn5DYXRlZ29yeSkNCnBsb3RXb3Jrc0NhdGVnb3J5Tm9ybWFsaXplZA0KDQpybShiYXJXb3Jrc0NhdGVnb3J5LCBiYXJXb3Jkc0NhdGVnb3J5LCBiYXJIaXRzQ2F0ZWdvcnksIGJhckt1ZG9zQ2F0ZWdvcnksIGJhckNvbW1lbnRzQ2F0ZWdvcnksIGJhckJvb2ttYXJrc0NhdGVnb3J5LCBwbG90V29ya3NDYXRlZ29yeU5vcm1hbGl6ZWQpDQoNCmBgYA0KDQojIFNpbmdsZSBDYXRlZ29yeSB0aHJvdWdoIHRpbWUNCg0KU2Vhc29uIDEgaGFkIGEgc2lnbmlmaWNhbnQgIkYvRiIgYnVtcCwgbGlrZWx5IHRvICJFbGVhbm9yIEd1dGhyaWUvTWF4IiByZWxhdGlvbnNoaXAuIEluIHNlYXNvbiAyIHdlIHNlZSBhIGRpcCwgYnV0IHNlYXNvbnMgMyBhbmQgNCBzbG93bHkgYnVpbGQgdXAgdG8gYSBzdHJvbmcgcGVhaywgbGlrZWx5IGR1ZSB0byAiQW5uZSBCb25ueS9NYXgiIGVuZGdhbWUuIFNlYXNvbiAxIGlzIGFsbW9uc3Qgbm9uLWV4aXN0ZW50IGluIHRlcm1zIG9mICJNL00iIGNhdGVnb3J5LCB3aGljaCBtYXkgYmUgYXR0cmlidXRlZCB0byBlYXJseSBzZXJpZXMgYnVpbGQgdXAgYmVpbmcgZGVzY3JpYmVkIGFzICJzdHJhaWdodGJhaXRpbmciLiBUaHJvdWdob3V0IHNlYXNvbnMgMi00IGl0IGhvd2V2ZXIgYnVpbGRzIHVwIHRvIGEgc3Ryb25nIHBlYWssIHdpdGggY29tcGxleCAiQ2FwdGFpbiBGbGludC9Kb2huIFNpbHZlciIgZGV2ZWxvcG1lbnRzIGFuZCAiQ2FwdGFpbiBGbGludC9UaG9tYXMgSGFtaWx0b24iIGZpbmFsZS4NCg0KYGBge3Igc2luZ2xlUmF0aW5nVGltZSwgbWVzc2FnZSA9IEZBTFNFLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9Nn0NCg0KcGxvdERhdGVzUmF0aW5nRGVuc2l0eSA8LSBnZ3Bsb3Qoc3RhdHNDYXRlZ29yeSwgYWVzKHggPSBEYXRlLCBjb2w9Q2F0ZWdvcnkpKSArIA0KICAgICAgICAgICAgICAgICAgICBnZW9tX2RlbnNpdHkoYWxwaGEgPSAwLjEpKw0KICAgICAgICAgICAgICAgICAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQ9c2Vhc29uc1N0YXJ0KSsNCiAgICAgICAgICAgICAgICAgICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0PXNlYXNvbnNFbmQsIGxpbmV0eXBlID0ibG9uZ2Rhc2giKSsNCiAgICAgICAgICAgICAgICAgICAgc2NhbGVfeF9kYXRlKGRhdGVfYnJlYWtzPSIzIG1vbnRocyIpKw0KICAgICAgICAgICAgICAgICAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gY3VzdG9tcGFsZXR0ZSkgKw0KICAgICAgICAgICAgICAgICAgICB0aGVtZV9oYWxmX29wZW4oKSArDQogICAgICAgICAgICAgICAgICAgIGJhY2tncm91bmRfZ3JpZCgpICsNCiAgICAgICAgICAgICAgICAgICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgdmp1c3QgPSAwLjUsIGhqdXN0PTEpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAncmlnaHQnKQ0KcGxvdERhdGVzUmF0aW5nRGVuc2l0eQ0KDQpybShwbG90RGF0ZXNSYXRpbmdEZW5zaXR5KQ0KYGBgDQoNCiMgTW9zdCBwb3B1bGFyIHNoaXAgdGFncw0KDQpUd28gbW9zdCBwb3B1bGFyIHNoaXBzIGluIEJsYWNrIFNhaWxzIGFyZSAiQ2FwdGFpbiBGbGludC9Kb2huIFNpbHZlciIgKCJDYXB0YWluIEZsaW50IHwgSmFtZXMgTWNHcmF3L0pvaG4gU2lsdmVyIikgYW5kICJDYXB0YWluIEZsaW50L1Rob21hcyBIYW1pbHRvbiIgKCJDYXB0YWluIEZsaW50IHwgSmFtZXMgTWNHcmF3L1Rob21hcyBIYW1pbHRvbiIpLiANCg0KYGBge3Igc2hpcHNIaXN0b2dyYW0sIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9Nn0NCg0KdG9wTGlzdCA8LSA4DQp0b3BSZWxhdGlvbnNoaXBzVGFibGU8LSBkYXRhLmZyYW1lKCdSZWxhdGlvbnNoaXAnID0gbmFtZXMoc3VtbWFyeShhcy5mYWN0b3IodW5saXN0KHJlbGF0aW9uc2hpcHMpKSlbMTp0b3BMaXN0XSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICdOdW1iZXIgb2YgU3RvcmllcycgPSBzdW1tYXJ5KGFzLmZhY3Rvcih1bmxpc3QocmVsYXRpb25zaGlwcykpKVsxOnRvcExpc3RdKQ0Kcm93Lm5hbWVzKHRvcFJlbGF0aW9uc2hpcHNUYWJsZSkgPC0gYygpDQp0b3BSZWxhdGlvbnNoaXBzVGFibGUgPC0gdG9wUmVsYXRpb25zaGlwc1RhYmxlW29yZGVyKHRvcFJlbGF0aW9uc2hpcHNUYWJsZSROdW1iZXIub2YuU3RvcmllcywgZGVjcmVhc2luZyA9IFRSVUUpLF0NCnRvcFJlbGF0aW9uc2hpcHNUYWJsZSRSZWxhdGlvbnNoaXAgPC0gZmFjdG9yKGFzLmNoYXJhY3Rlcih0b3BSZWxhdGlvbnNoaXBzVGFibGUkUmVsYXRpb25zaGlwKSwgbGV2ZWxzPWFzLmNoYXJhY3Rlcih0b3BSZWxhdGlvbnNoaXBzVGFibGUkUmVsYXRpb25zaGlwKSkNCg0KcmVsYXRpb25zaGlwc1N0YXRzIDwtIGRhdGEuZnJhbWUoRGF0ZSA9IHN0YXRzJERhdGUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWxhdGlvbnNoaXAxID0gcmVwKDAsIGxlbmd0aChzdGF0cyREYXRlKSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWxhdGlvbnNoaXAyID0gcmVwKDAsIGxlbmd0aChzdGF0cyREYXRlKSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWxhdGlvbnNoaXAzID0gcmVwKDAsIGxlbmd0aChzdGF0cyREYXRlKSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWxhdGlvbnNoaXA0ID0gcmVwKDAsIGxlbmd0aChzdGF0cyREYXRlKSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWxhdGlvbnNoaXA1ID0gcmVwKDAsIGxlbmd0aChzdGF0cyREYXRlKSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWxhdGlvbnNoaXA2ID0gcmVwKDAsIGxlbmd0aChzdGF0cyREYXRlKSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWxhdGlvbnNoaXA3ID0gcmVwKDAsIGxlbmd0aChzdGF0cyREYXRlKSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWxhdGlvbnNoaXA4ID0gcmVwKDAsIGxlbmd0aChzdGF0cyREYXRlKSkpDQpmb3IgKGkgaW4gMTp0b3BMaXN0KXsNCiAgbWF0Y2hpbmdWZWN0b3IgPC0gbGFwcGx5KHJlbGF0aW9uc2hpcHMsIG1hdGNoLCB0YWJsZT1hcy5jaGFyYWN0ZXIodG9wUmVsYXRpb25zaGlwc1RhYmxlJFJlbGF0aW9uc2hpcFtpXSkpDQogIG1hdGNoaW5nVmVjdG9yIDwtIHVubGlzdChsYXBwbHkobWF0Y2hpbmdWZWN0b3IsIHN1bSwgbmEucm09VFJVRSkpDQogIHJlbGF0aW9uc2hpcHNTdGF0c1tpKzFdIDwtIG1hdGNoaW5nVmVjdG9yDQp9DQoNCiNjb2xuYW1lcyhyZWxhdGlvbnNoaXBzU3RhdHMpWzI6OV0gPC0gZ3N1YignLycsICdcXC8nLCB0b3BSZWxhdGlvbnNoaXBzVGFibGUkUmVsYXRpb25zaGlwKQ0KDQpwbG90TGVnZW5kUmVsYXRpb25zaGlwcyA8LSBnZ3Bsb3QodG9wUmVsYXRpb25zaGlwc1RhYmxlLCBhZXMoeD1SZWxhdGlvbnNoaXAsIHk9TnVtYmVyLm9mLlN0b3JpZXMsIGZpbGw9UmVsYXRpb25zaGlwKSkrDQogIGdlb21fY29sKGFscGhhPTAuNykrDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGN1c3RvbXBhbGV0dGUpKw0KICB0aGVtZV9oYWxmX29wZW4oKSArDQogIGJhY2tncm91bmRfZ3JpZCgpICsNCiAgbGFicyh4PSIiLHk9J051bWJlciBvZiBTdG9yaWVzJykrDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHZqdXN0ID0gMC41LCBoanVzdD0xKSkNCnBsb3RMZWdlbmRSZWxhdGlvbnNoaXBzK3RoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICdub25lJykNCmBgYA0KDQojIFNoaXAgdGFncyB0aHJvdWdoIHRpbWUNCg0KU2Vhc29uIDMga2lja3N0YXJ0ZWQgdGhlIGdyb3d0aCBvZiBwb3B1bGFyaXR5IG9mICJDYXB0YWluIEZsaW50L0pvaG4gU2lsdmVyIiwgd2hpY2ggY29udGludWVkIHRocm91Z2ggc2Vhc29uIDQgYW5kIGFmdGVyLCBob3dldmVyIGF0IGFyb3VuZCBNYXJjaCAyMDE5IGFsdGVybmF0aXZlIHRhZyAiQ2FwdGFpbiBGbGludCB8IEphbWVzIE1jR3Jhdy9Kb2huIFNpbHZlciIgYmVjb21lcyBtb3JlIHBvcHVsYXIuIFNlYXNvbiA0IHNlZXMgdGhlIHJhcGlkIGdyb3d0aCBvZiAiQ2FwdGFpbiBGbGludC9UaG9tYXMgSGFtaWx0b24iLCB3aGljaCBzaW1pbGFybHkgc3dpdGNoZXMgdG8gYWx0ZXJuYXRpdmUgdGFnICJDYXB0YWluIEZsaW50L1Rob21hcyBIYW1pbHRvbiIgaW4gTWFyY2ggMjAxOS4gVGhlIHNoaWZ0IGluIHRhZ2dpbmcgbW9zdCBsaWtlbHkgaGFwcGVuZWQgZHVlIHRvIHRoZSBlZmZvcnRzIG9mIEFvMyB0YWcgd3JhbmdsZXJzLg0KDQpgYGB7ciBzaGlwVGFnc1RpbWUsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTZ9DQoNCnBsb3RSZWxhdGlvbnNoaXBzIDwtIGdncGxvdCgpICsNCiAgICBnZW9tX2RlbnNpdHkoZGF0YSA9IHJlbGF0aW9uc2hpcHNTdGF0c1tyZWxhdGlvbnNoaXBzU3RhdHMkcmVsYXRpb25zaGlwMSA+IDAsXSwgbWFwcGluZz1hZXMoeCA9IERhdGUpLCBjb2xvdXI9Y3VzdG9tcGFsZXR0ZVsxXSkrDQogICAgZ2VvbV9kZW5zaXR5KGRhdGEgPSByZWxhdGlvbnNoaXBzU3RhdHNbcmVsYXRpb25zaGlwc1N0YXRzJHJlbGF0aW9uc2hpcDIgPiAwLF0sIG1hcHBpbmc9YWVzKHggPSBEYXRlKSwgY29sb3VyPWN1c3RvbXBhbGV0dGVbMl0pKw0KICAgIGdlb21fZGVuc2l0eShkYXRhID0gcmVsYXRpb25zaGlwc1N0YXRzW3JlbGF0aW9uc2hpcHNTdGF0cyRyZWxhdGlvbnNoaXAzID4gMCxdLCBtYXBwaW5nPWFlcyh4ID0gRGF0ZSksIGNvbG91cj1jdXN0b21wYWxldHRlWzNdKSsNCiAgICBnZW9tX2RlbnNpdHkoZGF0YSA9IHJlbGF0aW9uc2hpcHNTdGF0c1tyZWxhdGlvbnNoaXBzU3RhdHMkcmVsYXRpb25zaGlwNCA+IDAsXSwgbWFwcGluZz1hZXMoeCA9IERhdGUpLCBjb2xvdXI9Y3VzdG9tcGFsZXR0ZVs0XSkrDQogICAgZ2VvbV9kZW5zaXR5KGRhdGEgPSByZWxhdGlvbnNoaXBzU3RhdHNbcmVsYXRpb25zaGlwc1N0YXRzJHJlbGF0aW9uc2hpcDUgPiAwLF0sIG1hcHBpbmc9YWVzKHggPSBEYXRlKSwgY29sb3VyPWN1c3RvbXBhbGV0dGVbNV0pKw0KICAgIGdlb21fZGVuc2l0eShkYXRhID0gcmVsYXRpb25zaGlwc1N0YXRzW3JlbGF0aW9uc2hpcHNTdGF0cyRyZWxhdGlvbnNoaXA2ID4gMCxdLCBtYXBwaW5nPWFlcyh4ID0gRGF0ZSksIGNvbG91cj1jdXN0b21wYWxldHRlWzZdKSsNCiAgICBnZW9tX2RlbnNpdHkoZGF0YSA9IHJlbGF0aW9uc2hpcHNTdGF0c1tyZWxhdGlvbnNoaXBzU3RhdHMkcmVsYXRpb25zaGlwNyA+IDAsXSwgbWFwcGluZz1hZXMoeCA9IERhdGUpLCBjb2xvdXI9Y3VzdG9tcGFsZXR0ZVs3XSkrDQogICAgZ2VvbV9kZW5zaXR5KGRhdGEgPSByZWxhdGlvbnNoaXBzU3RhdHNbcmVsYXRpb25zaGlwc1N0YXRzJHJlbGF0aW9uc2hpcDggPiAwLF0sIG1hcHBpbmc9YWVzKHggPSBEYXRlKSwgY29sb3VyPWN1c3RvbXBhbGV0dGVbOF0pKw0KICAgIGdlb21fdmxpbmUoeGludGVyY2VwdD1zZWFzb25zU3RhcnQpKw0KICAgIGdlb21fdmxpbmUoeGludGVyY2VwdD1zZWFzb25zRW5kLCBsaW5ldHlwZSA9ImxvbmdkYXNoIikrDQogICAgc2NhbGVfeF9kYXRlKGRhdGVfYnJlYWtzPSIzIG1vbnRocyIpKw0KICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjdXN0b21wYWxldHRlKSArDQogICAgdGhlbWVfaGFsZl9vcGVuKCkgKw0KICAgIGJhY2tncm91bmRfZ3JpZCgpICsNCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDAuNSwgaGp1c3Q9MSkpDQoNCm15bGVnZW5kIDwtIGdldF9sZWdlbmQocGxvdExlZ2VuZFJlbGF0aW9uc2hpcHMpDQoNCnBsb3RfZ3JpZChwbG90UmVsYXRpb25zaGlwcywgbXlsZWdlbmQsDQogICAgICAgICAgcmVsX3dpZHRocyA9IGMoMiwxKSwgbnJvdz0xKQ0KI3Bsb3RSZWxhdGlvbnNoaXBzDQoNCiNybShzZWFzb25zLCBwbG90RGF0ZXNSYXRpbmdEZW5zaXR5KQ0KYGBgDQoNCiMgQXJjaGl2ZSBXYXJuaW5ncw0KDQpNYWpvcml0eSBvZiB3b3JrcyBhcmUgdGFnZ2VkIHdpdGggIk5vIEFyY2hpdmUgV2FybmluZ3MgQXBwbHkiLCBmb2xsb3dlZCBieSBhIHNpemFibGUgZnJhY3Rpb24gb2YgIkNyZWF0b3IgQ2hvc2UgTm90IFRvIFVzZSBBcmNoaXZlIFdhcm5pbmdzIi4gSXQgc2VlbXMgdG8gYmUgYSBjb21tb24gbWF0dGVyIG9mIGNvbmZ1c2lvbiBiZXR3ZWVuIHRoZSB1c2FnZSBvZiB0aG9zZSB0d28gd2FybmluZ3MsIHNvIGl0J3MgcG9zc2libGUgdGhhdCBhIGxvdCBvZiAiQ3JlYXRvciBDaG9zZSBOb3QgVG8gVXNlIEFyY2hpdmUgV2FybmluZ3MiIGFyZSBtaXN0YWdnZWQgIk5vIEFyY2hpdmUgV2FybmluZ3MgQXBwbHkiLg0KDQpgYGB7ciB3YXJuaW5nQmFycywgbWVzc2FnZSA9IEZBTFNFLCBmaWcud2lkdGg9NiwgZmlnLmhlaWdodD02fQ0KDQptdWx0aXBsZVdhcm5pbmdTdW1tYXJ5IDwtIGRhdGEuZnJhbWUoV2FybmluZyA9IGMoIk5vIEFyY2hpdmUgV2FybmluZ3MgQXBwbHkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiR3JhcGhpYyBEZXBpY3Rpb25zIE9mIFZpb2xlbmNlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk1ham9yIENoYXJhY3RlciBEZWF0aCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJSYXBlL05vbi1Db24iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVW5kZXJhZ2UiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ3JlYXRvciBDaG9zZSBOb3QgVG8gVXNlIEFyY2hpdmUgV2FybmluZ3MiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE51bWJlci5vZi5Xb3JrcyA9IGMoc3VtKGdyZXBsKCJObyBBcmNoaXZlIFdhcm5pbmdzIEFwcGx5Iix3YXJuaW5ncykpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdW0oZ3JlcGwoIkdyYXBoaWMgRGVwaWN0aW9ucyBPZiBWaW9sZW5jZSIsd2FybmluZ3MpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VtKGdyZXBsKCJNYWpvciBDaGFyYWN0ZXIgRGVhdGgiLHdhcm5pbmdzKSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1bShncmVwbCgiUmFwZS9Ob24tQ29uIix3YXJuaW5ncykpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdW0oZ3JlcGwoIlVuZGVyYWdlIix3YXJuaW5ncykpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdW0oZ3JlcGwoIkNyZWF0b3IgQ2hvc2UgTm90IFRvIFVzZSBBcmNoaXZlIFdhcm5pbmdzIix3YXJuaW5ncykpKSApDQoNCm11bHRpcGxlV2FybmluZ1N1bW1hcnkkV2FybmluZyA8LSBmYWN0b3IobXVsdGlwbGVXYXJuaW5nU3VtbWFyeSRXYXJuaW5nLCBsZXZlbHMgPSBjKCJObyBBcmNoaXZlIFdhcm5pbmdzIEFwcGx5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJHcmFwaGljIERlcGljdGlvbnMgT2YgVmlvbGVuY2UiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk1ham9yIENoYXJhY3RlciBEZWF0aCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUmFwZS9Ob24tQ29uIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJVbmRlcmFnZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ3JlYXRvciBDaG9zZSBOb3QgVG8gVXNlIEFyY2hpdmUgV2FybmluZ3MiKSkNCg0KcGxvdFdhcm5pbmdzIDwtIHBsb3RfY29sKG11bHRpcGxlV2FybmluZ1N1bW1hcnksICdXYXJuaW5nJywgJ051bWJlci5vZi5Xb3JrcycsICdyaWdodCcpDQpwbG90V2FybmluZ3MNCg0Kcm0obXVsdGlwbGVXYXJuaW5nU3VtbWFyeSwgcGxvdFdhcm5pbmdzKQ0KDQpgYGANCg0KIyBNdWx0aXBsZSBGYW5kb21zDQoNCk51bWJlciBvZiB3b3JrcyB0YWdnZWQgd2l0aCBtb3JlIHRoYW4gMSBmYW5kb20gaXMgYHIgbGVuZ3RoKGZhbmRvbVt1bmxpc3QobGFwcGx5KGZhbmRvbSwgbGVuZ3RoKSkgPiAxXSlgLCBidXQgdGhlIG51bWJlciBvZiB3b3JrcyBleHBsaWNpdGx5IHRhZ2dlZCBhcyAnY3Jvc3NvdmVyJyBpcyBqdXN0IGByIGxlbmd0aChmYW5kb21bZ3JlcCgnY3Jvc3NvdmVyJyxmcmVlZm9ybSwgaWdub3JlLmNhc2U9VFJVRSldKWAuDQoNCiMgQXV0aG9ycyBieSBXb3Jrcw0KDQpUb3AgMzAgb2YgbW9zdCBwcm9saWZpYyBhdXRob3JzIGluIHRoZSB0YWcgYnkgdGhlIG51bWJlciBvZiBzdG9yaWVzIGFzIG9mIGRhdGEgY29sbGVjdGlvbiBkYXRlOg0KDQpgYGB7ciBhdXRob3JzV29ya3MsIG1lc3NhZ2UgPSBGQUxTRX0NCnRvcExpc3QgPC0gMzANCg0KQXV0aG9yVGFibGUgPC0gZGF0YS5mcmFtZSgnQXV0aG9yJyA9IG5hbWVzKHN1bW1hcnkoYXMuZmFjdG9yKHVubGlzdChhdXRob3IpKSlbMTp0b3BMaXN0XSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICdOdW1iZXIgb2YgU3RvcmllcycgPSBzdW1tYXJ5KGFzLmZhY3Rvcih1bmxpc3QoYXV0aG9yKSkpWzE6dG9wTGlzdF0pDQpyb3cubmFtZXMoQXV0aG9yVGFibGUpIDwtIGMoKQ0KDQprYWJsZShBdXRob3JUYWJsZSwNCiAgICAgIGNvbC5uYW1lcyA9IGMoJ0F1dGhvcicsICdOdW1iZXIgb2YgU3RvcmllcycpKQ0KDQpybShBdXRob3JUYWJsZSkNCmBgYA0KDQpUb3AgcGxhY2UgaXMgb2NjdXBpZWQgYnkgb3JwaGFuX2FjY291bnQsIHdoaWNoIGlzIGFuIGFydGlmYWN0IG9mIGFyY2hpdmUnIHdvcmtzIG9ycGhhbmluZyBmdW5jdGlvbi4NCg0KIyBBdXRob3JzIGJ5IFdvcmRzDQoNCk9ubHkgYHIgc3VtKHVubGlzdChsYXBwbHkoYXV0aG9yLCBsZW5ndGgpKT4xKWAgd29ya3MgaGF2ZSBtb3JlIHRoYW4gb25lIGF1dGhvci4gSW4gY2FzZXMgd2hlcmUgd29ya3MgaGFkIG1vcmUgdGhhbiBvbmUgYXV0aG9yLCBJIGFzc3VtZWQgdGhhdCBlYWNoIG9mIHRoZW0gY29udHJpYnV0ZWQgYW4gZXF1YWwgYW1vdW50cyBvZiB3b3Jkcy4NCg0KVG9wIDMwIG9mIG1vc3QgcHJvbGlmaWMgYXV0aG9ycyBpbiB0aGUgdGFnIGJ5IHRoZSBudW1iZXIgb2Ygd29yZHMgd3JpdHRlbiBhcyBvZiBkYXRhIGNvbGxlY3Rpb24gZGF0ZToNCg0KYGBge3IgYXV0aG9yc1dvcmRzLCBtZXNzYWdlID0gRkFMU0V9DQoNCndvcmRzQnlBdXRob3IgPC0gYygpDQoNCmZvciAoaSBpbiAxOmxlbmd0aCh3b3Jkcykpew0KICBpZiAobGVuZ3RoKGF1dGhvcltbaV1dKSA+IDEpIHsNCiAgICB3b3Jkc0J5QXV0aG9yIDwtIGMod29yZHNCeUF1dGhvciwgcmVwKHdvcmRzW1tpXV0vbGVuZ3RoKGF1dGhvcltbNV1dKSwgbGVuZ3RoKGF1dGhvcltbaV1dKSApICkNCiAgfSBlbHNlIHsNCiAgICB3b3Jkc0J5QXV0aG9yIDwtIGMod29yZHNCeUF1dGhvciwgd29yZHNbW2ldXSkNCiAgfQ0KfQ0KDQpBdXRob3JXb3Jkc1RhYmxlIDwtIGRhdGEuZnJhbWUoJ0F1dGhvcicgPSBhcy5mYWN0b3IodW5saXN0KGF1dGhvcikpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdXb3JkcycgPSB3b3Jkc0J5QXV0aG9yKQ0KDQpBdXRob3JXb3Jkc1N1bW1hcnkgPC0gZGRwbHkoQXV0aG9yV29yZHNUYWJsZSwgLihBdXRob3IpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdW1tYXJpemUsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRvdGFsLldvcmRzID0gc3VtKFdvcmRzKSkNCkF1dGhvcldvcmRzU3VtbWFyeSA8LSBBdXRob3JXb3Jkc1N1bW1hcnlbb3JkZXIoQXV0aG9yV29yZHNTdW1tYXJ5JFRvdGFsLldvcmRzLCBkZWNyZWFzaW5nID0gVFJVRSksXQ0Kcm93Lm5hbWVzKEF1dGhvcldvcmRzU3VtbWFyeSkgPC0gYygpDQoNCnRvcExpc3QgPC0gMzANCg0Ka2FibGUoQXV0aG9yV29yZHNTdW1tYXJ5WzE6dG9wTGlzdCxdLA0KICAgICAgY29sLm5hbWVzID0gYygnQXV0aG9yJywgJ1RvdGFsIFdvcmRzJykpDQoNCnJtKHdvcmRzQnlBdXRob3IsIGksIEF1dGhvcldvcmRzVGFibGUsIEF1dGhvcldvcmRzU3VtbWFyeSkNCmBgYA0KDQpJbnRlcmVzdGluZ2x5LCBvcnBoYW5fYWNjb3VudCBtYWRlIGl0IHRvIHRoZSB0b3AgYnkgdGhlIG51bWJlciBvZiB3b3JkcyB3cml0dGVuIGFzIHdlbGwuDQoNCiMgQ2hhcmFjdGVycw0KDQpUb3AgMzAgb2YgdGhlIG1vc3QgcG9wdWxhciBjaGFyYWN0ZXJzOg0KDQpgYGB7ciBjaGFyYWN0ZXJzLCBtZXNzYWdlID0gRkFMU0V9DQp0b3BMaXN0IDwtIDMwDQpDaGFyYWN0ZXJUYWJsZTwtIGRhdGEuZnJhbWUoJ0NoYXJhY3RlcicgPSBuYW1lcyhzdW1tYXJ5KGFzLmZhY3Rvcih1bmxpc3QoY2hhcmFjdGVyKSkpWzE6dG9wTGlzdF0pLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAnTnVtYmVyIG9mIFN0b3JpZXMnID0gc3VtbWFyeShhcy5mYWN0b3IodW5saXN0KGNoYXJhY3RlcikpKVsxOnRvcExpc3RdKQ0Kcm93Lm5hbWVzKENoYXJhY3RlclRhYmxlKSA8LSBjKCkNCg0Ka2FibGUoQ2hhcmFjdGVyVGFibGUsDQogICAgICBjb2wubmFtZXMgPSBjKCdDaGFyYWN0ZXInLCAnTnVtYmVyIG9mIFN0b3JpZXMnKSkNCg0Kcm0oQ2hhcmFjdGVyVGFibGUpDQpgYGANCg0KIyBSZWxhdGlvbnNoaXBzDQoNClRvcCAzMCBvZiB0aGUgbW9zdCBwb3B1bGFyIHJlbGF0aW9uc2hpcHM6DQoNCkkgZG9uJ3QgaGF2ZSBhY2Nlc3MgdG8gQW8zJ3Mgc3lzdGVtIG9mIHN5bm9ueW1vdXMgdGFncywgc28gYnkgdmlydHVlIG9mIHRleHQgcHJvY2Vzc2luZyBzb21lIHJlbGF0aW9uc2hpcCB0YWdzIGhlcmUgYXJlIHJlcGVhdGVkLg0KDQpPdmVyd2hlbG1pbmdseSwgIktvcnJhL0FzYW1pIFNhdG8iLyJLb3JyYXNhbWkiLyJLb3JyYS9Bc2FtaSIgaXMgdGhlIG1vc3QgcG9wdWxhciByZWxhdGlvbnNoaXAgaW4gTE9LLCBjb250cmlidXRpbmcgdG8gcG9wdWxhcml0eSBvZiAiRi9GIiBjYXRlZ29yeS4gVGhleSBhcmUgZm9sbG93ZWQgYnkgIktvcnJhL01ha28gKEF2YXRhcikiLCBhbmQgIkJvbGluL09wYWwgKEF2YXRhcikiLg0KDQpgYGB7ciByZWxhdGlvbnNoaXBzLCBtZXNzYWdlID0gRkFMU0V9DQp0b3BMaXN0IDwtIDMwDQpSZWxhdGlvbnNoaXBzVGFibGU8LSBkYXRhLmZyYW1lKCdSZWxhdGlvbnNoaXAnID0gbmFtZXMoc3VtbWFyeShhcy5mYWN0b3IodW5saXN0KHJlbGF0aW9uc2hpcHMpKSlbMTp0b3BMaXN0XSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICdOdW1iZXIgb2YgU3RvcmllcycgPSBzdW1tYXJ5KGFzLmZhY3Rvcih1bmxpc3QocmVsYXRpb25zaGlwcykpKVsxOnRvcExpc3RdKQ0Kcm93Lm5hbWVzKFJlbGF0aW9uc2hpcHNUYWJsZSkgPC0gYygpDQoNCmthYmxlKFJlbGF0aW9uc2hpcHNUYWJsZSwNCiAgICAgIGNvbC5uYW1lcyA9IGMoJ1JlbGF0aW9uc2hpcCcsICdOdW1iZXIgb2YgU3RvcmllcycpKQ0KDQpybShSZWxhdGlvbnNoaXBzVGFibGUpDQpgYGANCg0KIyBGcmVlZm9ybSB0YWdzDQoNClRvcCAzMCBvZiB0aGUgbW9zdCBwb3B1bGFyIGZyZWVmb3JtIHRhZ3M6DQoNCmBgYHtyIGZyZWVmb3JtLCBtZXNzYWdlID0gRkFMU0V9DQp0b3BMaXN0IDwtIDMwDQpGcmVlZm9ybVRhYmxlPC0gZGF0YS5mcmFtZSgnRnJlZWZvcm0nID0gbmFtZXMoc3VtbWFyeShhcy5mYWN0b3IodW5saXN0KGZyZWVmb3JtKSkpWzE6dG9wTGlzdF0pLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAnTnVtYmVyIG9mIFN0b3JpZXMnID0gc3VtbWFyeShhcy5mYWN0b3IodW5saXN0KGZyZWVmb3JtKSkpWzE6dG9wTGlzdF0pDQpyb3cubmFtZXMoRnJlZWZvcm1UYWJsZSkgPC0gYygpDQoNCmthYmxlKEZyZWVmb3JtVGFibGUsDQogICAgICBjb2wubmFtZXMgPSBjKCdGcmVlZm9ybSBUYWcnLCAnTnVtYmVyIG9mIFN0b3JpZXMnKSkNCg0Kcm0oRnJlZWZvcm1UYWJsZSkNCmBgYA0KDQojIExhbmd1YWdlcw0KDQpVbnN1cnByaXNpbmdseSwgbW9zdCB3b3JrcyBhcmUgd3JpdHRlbiBpbiBFbmdsaXNoLiBBcG9sb2dpZXMgZm9yIFUrLiBrYWJsZSBwYWNrYWdlIGZvciB3aGF0ZXZlciByZWFzb24gbXVyZGVycyB1bmljb2RlIGNoYXJhY3RlcnMuIFRoZSB0d28gbGFuZ3VhZ2VzIGluIHF1ZXN0aW9uIGFyZSBSdXNzaWFuICjQoNGD0YHRgdC60LjQuSkgYW5kIENoaW5lc2UgKOS4reaWhykuDQoNCmBgYHtyIGxhbmd1YWdlcywgbWVzc2FnZSA9IEZBTFNFfQ0KI3RvcExpc3QgPC0gMzANCg0KbGFuZ3VhZ2VzTGlzdCA8LSBzdW1tYXJ5KGFzLmZhY3Rvcih1bmxpc3QobGFuZ3VhZ2UpKSkNCg0KTGFuZ3VhZ2VUYWJsZSA8LSBkYXRhLmZyYW1lKCdMYW5ndWFnZScgPSBuYW1lcyhsYW5ndWFnZXNMaXN0KSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAnTnVtYmVyIG9mIFN0b3JpZXMnID0gbGFuZ3VhZ2VzTGlzdCApDQpMYW5ndWFnZVRhYmxlIDwtIExhbmd1YWdlVGFibGVbb3JkZXIoTGFuZ3VhZ2VUYWJsZSROdW1iZXIub2YuU3RvcmllcywgZGVjcmVhc2luZz1UUlVFKSxdDQpyb3cubmFtZXMoTGFuZ3VhZ2VUYWJsZSkgPC0gYygpDQoNCmthYmxlKExhbmd1YWdlVGFibGUsDQogICAgICBjb2wubmFtZXMgPSBjKCdMYW5ndWFnZScsICdOdW1iZXIgb2YgU3RvcmllcycpKQ0KDQojbGFuZ3VhZ2VzTGlzdA0KDQojcm0oTGFuZ3VhZ2VUYWJsZSkNCmBgYA0KDQojIE90aGVyIGxpbmtzDQoNCltBbzMgZGF0YSBhbmFseXNpcyBmb3IgVGhlIERyYWdvbiBQcmluY2UgKENhcnRvb24pXShodHRwczovL2RhcnRoYWxpbmUuZ2l0aHViLmlvL0FvM1NlYXJjaEFuYWx5c2lzL2ZhbmRvbXMvVERQL1REUF9wcm9jZXNzaW5nX25vdGVib29rLm5iLmh0bWwpDQoNCltBbzMgZGF0YSBhbmFseXNpcyBmb3IgQXZhdGFyOiBMZWdlbmQgb2YgS29ycmFdKGh0dHBzOi8vZGFydGhhbGluZS5naXRodWIuaW8vQW8zU2VhcmNoQW5hbHlzaXMvZmFuZG9tcy9MT0svTE9LX3Byb2Nlc3Npbmdfbm90ZWJvb2submIuaHRtbCkNCg0KW0FvMyBkYXRhIGFuYWx5c2lzIGZvciBBdmF0YXI6IFRoZSBMYXN0IEFpcmJlbmRlcl0oaHR0cHM6Ly9kYXJ0aGFsaW5lLmdpdGh1Yi5pby9BbzNTZWFyY2hBbmFseXNpcy9mYW5kb21zL0FUTEEvQVRMQV9wcm9jZXNzaW5nX25vdGVib29rLm5iLmh0bWwpDQoNCkFvMyBkYXRhIGFuYWx5c2lzIGZvciBCbGFjayBTYWlscw0KDQpJZiB5b3UgZW5qb3llZCBteSBhbmFseXNpcywgcGxlYXNlLCBjb25zaWRlciBbYnV5aW5nIG1lIGEgY29mZmVlXShodHRwczovL2tvLWZpLmNvbS9EMUQ4UklHNSkgb3Igc29tZSBvdGhlciBiZXZlcmFnZS4=