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).
- About 75% of works have more than a 1000 words, but only about 10% have more than 10000 words.
- Only about 25% of works have over a 1000 hits.
- Only about 25% of works have more than a 100 kudos.
- Only about 50% of works get more than 10 comments.
- Approximately 5% of works have no comments (tail end).
- Only approximately 35% of works get more than 10 bookmarks.
- Approximately 15% of works have no bookmarks (tail end).
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
- Majority of works are Complete
- Works in Progress are more than 3 times longer than Complete ones.
- Both Complete Works and Works in Progress get approximately the same amounts of hits.
- Complete Works get about 20% more kudos.
- Works in Progress get approximately 2 times as many comments as Complete ones (however, again, there’s no way to filter out author’s comments in the search selection).
- Complete Works get slightly more bookmarks than Works in Progress.
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
- Most works are rated T and E, but G and M rated works are not far behind.
- Works rated G are on average the shortest (~1500 words), followed by T (~4000 words), Not rated (~9000 words), E (~ 9000 words), and M (~11000 words). The trend of M rated works being the longest we observed in other fandoms holds here as well.
- G rated works get fewest hits (~500), followed by Not Rated (~700). Otherwise, the number of hits rises with the rating. E rated works are most popular (~1600).
- Number of kudos, comments and bookmarks seem to be broadly proportional to the number of hits.
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)
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=